<?php

if ((PHP_MAJOR_VERSION === 8 && PHP_MINOR_VERSION < 2) || PHP_MAJOR_VERSION <= 7) {
    die("MadelineProto requires at least PHP 8.2.".PHP_EOL);
}
if (PHP_INT_SIZE < 8) {
    die("A 64-bit build of PHP is required to run MadelineProto, PHP 8.2 is required.".PHP_EOL);
}

if (\extension_loaded("psr")) {
    die("Please uninstall the psr extension to use MadelineProto!");
}

if (\defined("MADELINE_PHAR")) {
    die("Please do not include madeline.phar twice, use require_once 'madeline.phar';!");
}

if (!\defined('MADELINE_ALLOW_COMPOSER') && \class_exists(\Composer\Autoload\ClassLoader::class)) {
    die('Composer autoloader detected: madeline.phar is incompatible with Composer, please install MadelineProto using composer: https://docs.madelineproto.xyz/docs/INSTALLATION.html#composer-from-existing-project');
}

\define('MADELINE_PHAR', __FILE__);

if (defined("MADELINE_REAL_ROOT")) {
    @chdir(MADELINE_REAL_ROOT);
} else {
    $backtrace = \debug_backtrace(0);
    if (\count($backtrace) === 0) {
        if (isset($GLOBALS["argv"]) && !empty($GLOBALS["argv"])) {
            $arguments = \array_slice($GLOBALS["argv"], 1);
        } elseif (isset($_GET["argv"]) && !empty($_GET["argv"])) {
            $arguments = $_GET["argv"];
        } else {
            $arguments = [];
        }
        if (\count($arguments) >= 2) {
            \define(\MADELINE_WORKER_TYPE::class, \array_shift($arguments));
            \define(\MADELINE_WORKER_ARGS::class, $arguments);
        } else {
            die("MadelineProto loader: you must include this file in another PHP script, see https://docs.madelineproto.xyz for more info.".PHP_EOL);
        }
        \define("MADELINE_REAL_ROOT", __DIR__);
        @chdir(\MADELINE_REAL_ROOT);
    }
}

Phar::interceptFileFuncs();
Phar::mapPhar("madeline85-f751f650859e231ce1921721bbf78287df5c30f8.phar"); 
$result = require_once "phar://madeline85-f751f650859e231ce1921721bbf78287df5c30f8.phar/vendor/autoload.php"; 

if (\defined("MADELINE_WORKER_TYPE") && \constant("MADELINE_WORKER_TYPE") === "madeline-ipc") {
    require_once "phar://madeline85-f751f650859e231ce1921721bbf78287df5c30f8.phar/vendor/danog/madelineproto/src/Ipc/Runner/entry.php";
}

return $result;

__HALT_COMPILER(); ?>
 Ð G      8   madeline85-f751f650859e231ce1921721bbf78287df5c30f8.phar    ?   vendor/nikic/php-parser/lib/PhpParser/PrettyPrinterAbstract.php)! ’¿Ýj)! rŒÓH¤      7   vendor/nikic/php-parser/lib/PhpParser/PrettyPrinter.php£  ’¿Ýj£  E“¤      0   vendor/nikic/php-parser/lib/PhpParser/Parser.phpý  ’¿Ýjý  »“?�¤      ?   vendor/nikic/php-parser/lib/PhpParser/ErrorHandler/Throwing.phpp  ’¿Ýjp  
¬Ð¤      A   vendor/nikic/php-parser/lib/PhpParser/ErrorHandler/Collecting.phpe  ’¿Ýje  Ó0Þ¤      8   vendor/nikic/php-parser/lib/PhpParser/ParserAbstract.php€Ï  ’¿Ýj€Ï  ªy›¤      .   vendor/nikic/php-parser/lib/PhpParser/Node.php  ’¿Ýj  (èÕ¤      F   vendor/nikic/php-parser/lib/PhpParser/ConstExprEvaluationException.phpp   ’¿Ýjp   Ò€˜‡¤      7   vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php[(  ’¿Ýj[(  ž·��¤      /   vendor/nikic/php-parser/lib/PhpParser/Token.phpç  ’¿Ýjç  Œ×±¤      8   vendor/nikic/php-parser/lib/PhpParser/Node/Attribute.php4  ’¿Ýj4  à¢ˆ¤      8   vendor/nikic/php-parser/lib/PhpParser/Node/StaticVar.phpç  ’¿Ýjç  `Ía$¤      ?   vendor/nikic/php-parser/lib/PhpParser/Node/IntersectionType.php™  ’¿Ýj™  ù
°A¤      B   vendor/nikic/php-parser/lib/PhpParser/Node/Name/FullyQualified.php’  ’¿Ýj’  É’Û!¤      <   vendor/nikic/php-parser/lib/PhpParser/Node/Name/Relative.php�  ’¿Ýj�  lñN¤      ;   vendor/nikic/php-parser/lib/PhpParser/Node/FunctionLike.phpÛ  ’¿ÝjÛ  *Ó/¤      8   vendor/nikic/php-parser/lib/PhpParser/Node/ArrayItem.php¼  ’¿Ýj¼   f¤      2   vendor/nikic/php-parser/lib/PhpParser/Node/Arg.php  ’¿Ýj  '+³¤      :   vendor/nikic/php-parser/lib/PhpParser/Node/ComplexType.phpC  ’¿ÝjC  (dN»¤      3   vendor/nikic/php-parser/lib/PhpParser/Node/Name.phpß!  ’¿Ýjß!  ðb±¤      @   vendor/nikic/php-parser/lib/PhpParser/Node/VarLikeIdentifier.phpö  ’¿Ýjö  J¨W¤      7   vendor/nikic/php-parser/lib/PhpParser/Node/MatchArm.phpŒ  ’¿ÝjŒ  vS‚¤      ;   vendor/nikic/php-parser/lib/PhpParser/Node/PropertyHook.phpg  ’¿Ýjg  hé”¤      4   vendor/nikic/php-parser/lib/PhpParser/Node/Param.phpË  ’¿ÝjË  u^¶¤      3   vendor/nikic/php-parser/lib/PhpParser/Node/Expr.php…   ’¿Ýj…   }åº�¤      ;   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/PostDec.php  ’¿Ýj  _E´ê¤      =   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/UnaryPlus.php‰  ’¿Ýj‰  Þ&ƒ>¤      F   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/NullsafeMethodCall.phpU  ’¿ÝjU  V<��¤      >   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/StaticCall.phpI  ’¿ÝjI  ëõ‰¤      :   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Match_.php  ’¿Ýj  !'ê¤      G   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/StaticPropertyFetch.phpö  ’¿Ýjö  ÞF“¤      <   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp.phpÖ  ’¿ÝjÖ  R?A×¤      =   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/YieldFrom.phpš  ’¿Ýjš  †”¡…¤      :   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/PreDec.php|  ’¿Ýj|  õ¢vü¤      :   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Yield_.phpN  ’¿ÝjN  ñ‡5]¤      A   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ArrowFunction.phpø	  ’¿Ýjø	  –šXì¤      =   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ArrayItem.php1  ’¿Ýj1  u÷ã#¤      :   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Isset_.php‚  ’¿Ýj‚  ? (ð¤      >   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BitwiseNot.phpŒ  ’¿ÝjŒ  .—‡¤      >   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ConstFetch.php«  ’¿Ýj«  ²,’¤      C   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ClassConstFetch.phpÚ  ’¿ÝjÚ  Nç'&¤      ;   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Ternary.phpÇ  ’¿ÝjÇ  *�ñÓ¤      ;   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Closure.php  ’¿Ýj  AÓÎæ¤      =   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ShellExec.php  ’¿Ýj  Œ)ÿá¤      A   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/PropertyFetch.php±  ’¿Ýj±  Ø ;¤      >   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/MethodCall.php>  ’¿Ýj>  �Ù;ò¤      <   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Include_.php·  ’¿Ýj·  +´ñ6¤      9   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Eval_.php}  ’¿Ýj}  {×%¤      <   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/FuncCall.php
  ’¿Ýj
  Þþ¯{¤      B   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Equal.php.  ’¿Ýj.  Ò=Œ|¤      G   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/ShiftRight.php8  ’¿Ýj8  ‚hA9¤      G   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/LogicalAnd.php9  ’¿Ýj9  OWz¤      @   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Div.php)  ’¿Ýj)  Uùl{¤      @   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Mul.php)  ’¿Ýj)  +\Ð¤      @   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Mod.php)  ’¿Ýj)  ‡¢=¤      F   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BitwiseOr.php5  ’¿Ýj5  ó›Á¼¤      D   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Smaller.php1  ’¿Ýj1  Os‡°¤      C   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Concat.php/  ’¿Ýj/  çÞÞE¤      A   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Plus.php+  ’¿Ýj+  ”§#¤      G   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BitwiseXor.php7  ’¿Ýj7  —(šá¤      F   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/ShiftLeft.php6  ’¿Ýj6  -è—)¤      A   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Pipe.php,  ’¿Ýj,  ß“¤      F   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BooleanOr.php6  ’¿Ýj6  _¯çu¤      E   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Coalesce.php4  ’¿Ýj4  œ@ó•¤      F   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Spaceship.php7  ’¿Ýj7  •S;¤      G   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/LogicalXor.php9  ’¿Ýj9  �ä}Ã¤      G   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BitwiseAnd.php7  ’¿Ýj7  hNì˜¤      K   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/SmallerOrEqual.php@  ’¿Ýj@  aÖ¤      B   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Minus.php-  ’¿Ýj-  	�>$¤      D   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Greater.php1  ’¿Ýj1  Ÿ¤Ô¤      G   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BooleanAnd.php8  ’¿Ýj8  Ï;%¤      @   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Pow.php*  ’¿Ýj*  Õêò@¤      I   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/NotIdentical.php=  ’¿Ýj=  5^¤      K   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/GreaterOrEqual.php@  ’¿Ýj@  ³�â$¤      F   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Identical.php7  ’¿Ýj7  3òêí¤      F   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/LogicalOr.php6  ’¿Ýj6  �•Ð¤      E   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/NotEqual.php4  ’¿Ýj4  êè�Ö¤      :   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Array_.php?  ’¿Ýj?  hâš¤      9   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Error.phpõ  ’¿Ýjõ  Œj�U¤      :   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Throw_.phpœ  ’¿Ýjœ  Õj¸¤      ?   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Instanceof_.php\  ’¿Ýj\  “{ƒ)¤      :   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/PreInc.php|  ’¿Ýj|  ý4ø	¤      G   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/ShiftRight.phpë   ’¿Ýjë   â±jä¤      @   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Div.phpÝ   ’¿ÝjÝ   -64œ¤      @   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Mul.phpÝ   ’¿ÝjÝ   ÷ßÙ¤      @   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Mod.phpÝ   ’¿ÝjÝ   ÈÃ)q¤      F   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/BitwiseOr.phpé   ’¿Ýjé   RYqÞ¤      C   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Concat.phpã   ’¿Ýjã   NsQé¤      A   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Plus.phpß   ’¿Ýjß   œ³)Þ¤      G   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/BitwiseXor.phpë   ’¿Ýjë   @sÃ¤      F   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/ShiftLeft.phpé   ’¿Ýjé   ¼³•\¤      E   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Coalesce.phpç   ’¿Ýjç   ¹Òîè¤      G   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/BitwiseAnd.phpë   ’¿Ýjë   &áh;¤      B   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Minus.phpá   ’¿Ýjá   ¢£¹Å¤      @   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Pow.phpÝ   ’¿ÝjÝ   Á!¶‡¤      A   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ArrayDimFetch.php6  ’¿Ýj6  Ú=¬R¤      :   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Assign.php  ’¿Ýj  ›ŠW0¤      A   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ErrorSuppress.php–  ’¿Ýj–  ›n»¤      ;   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/PostInc.php  ’¿Ýj  Â�Ñ«¤      ?   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Double.php‘  ’¿Ýj‘  ckÞ“¤      =   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Int_.phpX  ’¿ÝjX  @[¤      >   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Void_.phpÐ   ’¿ÝjÐ   ½ŠI¤      ?   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Array_.phpÒ   ’¿ÝjÒ   ”[òg¤      @   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Object_.phpÔ   ’¿ÝjÔ   ü Ì¤      >   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Bool_.php\  ’¿Ýj\  nJ¤      @   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/String_.phpb  ’¿Ýjb  Dƒ©¤      ?   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Unset_.phpÒ   ’¿ÝjÒ   8óª¤      I   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/NullsafePropertyFetch.phpË  ’¿ÝjË  wõ¤      >   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BooleanNot.phpŒ  ’¿ÝjŒ  &IÃÕ¤      :   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Print_.php€  ’¿Ýj€  ÷zrb¤      9   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Exit_.phpö  ’¿Ýjö  à=hf¤      <   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp.phpG  ’¿ÝjG  “¾D ¤      =   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignRef.php7  ’¿Ýj7  ú¯�–¤      >   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ClosureUse.php5  ’¿Ýj5  ü1ŠË¤      <   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/CallLike.phpö
  ’¿Ýjö
  Í‘—|¤      :   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Empty_.php€  ’¿Ýj€  i*¨¤      :   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Clone_.php}  ’¿Ýj}  �‘z¤      >   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/UnaryMinus.phpŒ  ’¿ÝjŒ  R®ï¢¤      8   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/New_.phpŽ  ’¿ÝjŽ  ²Äa¤      <   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Variable.php}  ’¿Ýj}  &8r¤      8   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast.php7  ’¿Ýj7  sPG¤      9   vendor/nikic/php-parser/lib/PhpParser/Node/Expr/List_.phpo  ’¿Ýjo  1bÜ¤      5   vendor/nikic/php-parser/lib/PhpParser/Node/Const_.phpË  ’¿ÝjË  ÐÆÆ¤      9   vendor/nikic/php-parser/lib/PhpParser/Node/Identifier.phpE  ’¿ÝjE  <í#¤      8   vendor/nikic/php-parser/lib/PhpParser/Node/UnionType.php¥  ’¿Ýj¥  Ö–
E¤      :   vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/Int_.phpÐ	  ’¿ÝjÐ	  {mf=¤      H   vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/EncapsedStringPart.php  ’¿Ýj  wÄ^¤      H   vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/InterpolatedString.php¥  ’¿Ýj¥  šÇ¢P¤      =   vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/DNumber.php  ’¿Ýj  ‚V×¤      J   vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Function_.phpD  ’¿ÝjD  ^R”À¤      I   vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Property.phpC  ’¿ÝjC  °PÉ¤      D   vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Dir.php4  ’¿Ýj4  ’M+/¤      E   vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/File.php7  ’¿Ýj7  ¥Ÿç¤      G   vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Method.php=  ’¿Ýj=  O˜Ö¼¤      G   vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Class_.php;  ’¿Ýj;  ;@N¤      G   vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Trait_.php;  ’¿Ýj;  ïeÿ«¤      E   vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Line.php7  ’¿Ýj7  ›_^¤      K   vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Namespace_.phpG  ’¿ÝjG  Áƒå�¤      =   vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/LNumber.php  ’¿Ýj  Iù0Ì¤      @   vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst.phpZ  ’¿ÝjZ  Ô¸eG¤      <   vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/Float_.phpû  ’¿Ýjû  Îú¤Ï¤      =   vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/String_.php  ’¿Ýj  –v:ç¤      >   vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/Encapsed.phpA  ’¿ÝjA  ¬]¤      =   vendor/nikic/php-parser/lib/PhpParser/Node/ArgPlaceholder.php  ’¿Ýj  8ûGê¤      B   vendor/nikic/php-parser/lib/PhpParser/Node/VariadicPlaceholder.php�  ’¿Ýj�  wEF|¤      ;   vendor/nikic/php-parser/lib/PhpParser/Node/NullableType.php§  ’¿Ýj§  Ii
¤      9   vendor/nikic/php-parser/lib/PhpParser/Node/ClosureUse.phpÍ  ’¿ÝjÍ  —«²l¤      6   vendor/nikic/php-parser/lib/PhpParser/Node/UseItem.php�  ’¿Ýj�  ”Q+Í¤      :   vendor/nikic/php-parser/lib/PhpParser/Node/DeclareItem.phpà  ’¿Ýjà  î¶ŒX¤      ;   vendor/nikic/php-parser/lib/PhpParser/Node/PropertyItem.php0  ’¿Ýj0  ¸½ø„¤      5   vendor/nikic/php-parser/lib/PhpParser/Node/Scalar.phpb   ’¿Ýjb   kQ�K¤      =   vendor/nikic/php-parser/lib/PhpParser/Node/AttributeGroup.php‡  ’¿Ýj‡  QøÕ/¤      ;   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Return_.php§  ’¿Ýj§  q…�¤      9   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Else_.php–  ’¿Ýj–  ²Æ™�¤      9   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Enum_.php*  ’¿Ýj*  ÍÍ¤      8   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Use_.php¨  ’¿Ýj¨   ü¤      =   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/StaticVar.php1  ’¿Ýj1  _|49¤      B   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/DeclareDeclare.phpM  ’¿ÝjM  ÖÇÖ¤      <   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Foreach_.php®  ’¿Ýj®  Wõ—¤      :   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Catch_.phpZ  ’¿ÝjZ  }í]¤      ?   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/ClassMethod.phpl  ’¿Ýjl  §›Â¤      =   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Function_.phpp
  ’¿Ýjp
  �¬ÄQ¤      9   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Goto_.phpõ  ’¿Ýjõ  ¶þoJ¤      7   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Do_.php3  ’¿Ýj3  œ9t¤      :   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/While_.php6  ’¿Ýj6  · >‹¤      9   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Block.php†  ’¿Ýj†  ƒ;77¤      >   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/InlineHTML.php“  ’¿Ýj“   o�„¤      :   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Break_.phpº  ’¿Ýjº  Þ¬>A¤      8   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/For_.php›  ’¿Ýj›  ö;6}¤      7   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/If_.phpp  ’¿Ýjp  _Ý¾¤      D   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/PropertyProperty.phpS  ’¿ÝjS  Æ"E¤      <   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Declare_.php�  ’¿Ýj�  gºMþ¤      <   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Property.phpb  ’¿Ýjb  †Š¹¤      @   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/HaltCompiler.php   ’¿Ýj   	¨5‘¤      ;   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Switch_.php(  ’¿Ýj(  _aÅ¤      ;   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/ElseIf_.php:  ’¿Ýj:  ÔÁ¬r¤      ;   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Static_.phpÑ  ’¿ÝjÑ  §?¤      :   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/UseUse.php5  ’¿Ýj5  ¨3ì²¤      :   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Const_.phpÇ  ’¿ÝjÇ  fu—=¤      9   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Case_.phpe  ’¿Ýje  ZcíÄ¤      F   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/TraitUseAdaptation.php%  ’¿Ýj%  À)‹¤      <   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/GroupUse.php%  ’¿Ýj%  �Ý$|¤      9   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Echo_.php“  ’¿Ýj“  	ì¬¤      L   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/TraitUseAdaptation/Alias.php  ’¿Ýj  ]T·S¤      Q   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/TraitUseAdaptation/Precedence.php  ’¿Ýj  !Ò°¤      >   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Interface_.php-  ’¿Ýj-  )M•)¤      9   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Label.phpÏ  ’¿ÝjÏ  BÇ¬¦¤      =   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Continue_.phpÉ  ’¿ÝjÉ  DåA¤      ;   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Global_.php¦  ’¿Ýj¦  ” ©X¤      <   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/EnumCase.phpŽ  ’¿ÝjŽ  c½wÀ¤      :   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Class_.php�  ’¿Ýj�  ˆ\î¤      7   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Nop.php'  ’¿Ýj'  �¯ãë¤      >   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/ClassConst.phpT  ’¿ÝjT  UÞœƒ¤      :   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Trait_.php/  ’¿Ýj/  Øÿ|º¤      <   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Finally_.phpž  ’¿Ýjž  ¯w�û¤      :   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Unset_.phpž  ’¿Ýjž  _^W¤      >   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Expression.phpÖ  ’¿ÝjÖ  èk¨j¤      <   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/TryCatch.php  ’¿Ýj  ²Ý¯M¤      <   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/TraitUse.phpy  ’¿Ýjy  &X§¤      >   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Namespace_.php½  ’¿Ýj½  Ëq*"¤      =   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/ClassLike.phpä  ’¿Ýjä  þ—‡<¤      3   vendor/nikic/php-parser/lib/PhpParser/Node/Stmt.php…   ’¿Ýj…   ªYm"¤      E   vendor/nikic/php-parser/lib/PhpParser/Node/InterpolatedStringPart.phpR  ’¿ÝjR  3ýà¤      @   vendor/nikic/php-parser/lib/PhpParser/PrettyPrinter/Standard.phpdÔ  ’¿ÝjdÔ  Z‘¤      4   vendor/nikic/php-parser/lib/PhpParser/NodeFinder.php/
  ’¿Ýj/
  c"J\¤      /   vendor/nikic/php-parser/lib/PhpParser/Lexer.php  ’¿Ýj  /2¤      /   vendor/nikic/php-parser/lib/PhpParser/Error.phpX  ’¿ÝjX  �›Ví¤      5   vendor/nikic/php-parser/lib/PhpParser/Parser/Php8.php¹þ ’¿Ýj¹þ .°ø¤      5   vendor/nikic/php-parser/lib/PhpParser/Parser/Php7.php‰ø ’¿Ýj‰ø ú�æ¤      1   vendor/nikic/php-parser/lib/PhpParser/Builder.phpÊ   ’¿ÝjÊ   6µíI¤      <   vendor/nikic/php-parser/lib/PhpParser/ConstExprEvaluator.php%  ’¿Ýj%  H(Ã�¤      >   vendor/nikic/php-parser/lib/PhpParser/compatibility_tokens.phpÓ	  ’¿ÝjÓ	  4Ï«¤      7   vendor/nikic/php-parser/lib/PhpParser/ParserFactory.php¨  ’¿Ýj¨  Ì}Æ�¤      =   vendor/nikic/php-parser/lib/PhpParser/NodeVisitorAbstract.php¿  ’¿Ýj¿  ° [×¤      5   vendor/nikic/php-parser/lib/PhpParser/JsonDecoder.phpÀ  ’¿ÝjÀ  ¨¹åø¤      @   vendor/nikic/php-parser/lib/PhpParser/NodeTraverserInterface.phpV  ’¿ÝjV  .ÍžÛ¤      4   vendor/nikic/php-parser/lib/PhpParser/NodeDumper.phpõ(  ’¿Ýjõ(  <Ÿ¨ö¤      6   vendor/nikic/php-parser/lib/PhpParser/NodeAbstract.phpú  ’¿Ýjú  Ù/…w¤      1   vendor/nikic/php-parser/lib/PhpParser/Comment.phpû  ’¿Ýjû  @€X¤      8   vendor/nikic/php-parser/lib/PhpParser/BuilderHelpers.phpê&  ’¿Ýjê&  Ý%ã¤      9   vendor/nikic/php-parser/lib/PhpParser/Internal/Differ.phpå  ’¿Ýjå  Ýv`¤      L   vendor/nikic/php-parser/lib/PhpParser/Internal/PrintableNewAnonClassNode.php•
  ’¿Ýj•
  'R»¤      @   vendor/nikic/php-parser/lib/PhpParser/Internal/TokenPolyfill.php %  ’¿Ýj %  ¡Î&U¤      >   vendor/nikic/php-parser/lib/PhpParser/Internal/TokenStream.php$  ’¿Ýj$  ëßèx¤      ;   vendor/nikic/php-parser/lib/PhpParser/Internal/DiffElem.phpû  ’¿Ýjû  @z+Ã¤      3   vendor/nikic/php-parser/lib/PhpParser/Modifiers.phpÛ
  ’¿ÝjÛ
  Y7D«¤      5   vendor/nikic/php-parser/lib/PhpParser/NameContext.phpE'  ’¿ÝjE'   INx¤      M   vendor/nikic/php-parser/lib/PhpParser/NodeVisitor/ParentConnectingVisitor.php=  ’¿Ýj=  tù§;¤      D   vendor/nikic/php-parser/lib/PhpParser/NodeVisitor/FindingVisitor.phpw  ’¿Ýjw  ­Iº¤      I   vendor/nikic/php-parser/lib/PhpParser/NodeVisitor/FirstFindingVisitor.phpã  ’¿Ýjã   Ðµ*¤      B   vendor/nikic/php-parser/lib/PhpParser/NodeVisitor/NameResolver.php‚(  ’¿Ýj‚(  H›¿L¤      D   vendor/nikic/php-parser/lib/PhpParser/NodeVisitor/CloningVisitor.phpö  ’¿Ýjö  ñ'p¤      K   vendor/nikic/php-parser/lib/PhpParser/NodeVisitor/NodeConnectingVisitor.php	  ’¿Ýj	  	T	û¤      N   vendor/nikic/php-parser/lib/PhpParser/NodeVisitor/CommentAnnotatingVisitor.phpÙ
  ’¿ÝjÙ
  ã¶Ï¤      4   vendor/nikic/php-parser/lib/PhpParser/PhpVersion.phpé  ’¿Ýjé  NW[1¤      6   vendor/nikic/php-parser/lib/PhpParser/ErrorHandler.php,  ’¿Ýj,  ­s…¤      8   vendor/nikic/php-parser/lib/PhpParser/BuilderFactory.php5)  ’¿Ýj5)  ¤þù�¤      7   vendor/nikic/php-parser/lib/PhpParser/Builder/Enum_.phpº  ’¿Ýjº  Ú£÷´¤      6   vendor/nikic/php-parser/lib/PhpParser/Builder/Use_.phpý  ’¿Ýjý  ÆXWy¤      ;   vendor/nikic/php-parser/lib/PhpParser/Builder/Function_.php›  ’¿Ýj›  åº¡¤      >   vendor/nikic/php-parser/lib/PhpParser/Builder/FunctionLike.php  ’¿Ýj  ,V¤      :   vendor/nikic/php-parser/lib/PhpParser/Builder/Property.phps  ’¿Ýjs  ¾½}þ¤      7   vendor/nikic/php-parser/lib/PhpParser/Builder/Param.phpr  ’¿Ýjr  1üÁÆ¤      =   vendor/nikic/php-parser/lib/PhpParser/Builder/Declaration.phpí  ’¿Ýjí  oþ ‡¤      D   vendor/nikic/php-parser/lib/PhpParser/Builder/TraitUseAdaptation.phpª  ’¿Ýjª  µ$ƒÐ¤      <   vendor/nikic/php-parser/lib/PhpParser/Builder/Interface_.phpD
  ’¿ÝjD
  q×Ô´¤      :   vendor/nikic/php-parser/lib/PhpParser/Builder/EnumCase.phpÔ  ’¿ÝjÔ  ƒ÷Å¤      8   vendor/nikic/php-parser/lib/PhpParser/Builder/Method.php¬  ’¿Ýj¬  ¸ššR¤      8   vendor/nikic/php-parser/lib/PhpParser/Builder/Class_.phpK  ’¿ÝjK  Ìj)¤      <   vendor/nikic/php-parser/lib/PhpParser/Builder/ClassConst.php#  ’¿Ýj#  ^æO)¤      8   vendor/nikic/php-parser/lib/PhpParser/Builder/Trait_.php4	  ’¿Ýj4	  	8¹¤      :   vendor/nikic/php-parser/lib/PhpParser/Builder/TraitUse.phpv  ’¿Ýjv  ^Ò¤¤      <   vendor/nikic/php-parser/lib/PhpParser/Builder/Namespace_.php1  ’¿Ýj1  ~uŒ~¤      5   vendor/nikic/php-parser/lib/PhpParser/Comment/Doc.phpg   ’¿Ýjg   [Ä Z¤      5   vendor/nikic/php-parser/lib/PhpParser/NodeVisitor.phpS  ’¿ÝjS  ÔÁ¤      9   vendor/nikic/php-parser/lib/PhpParser/Lexer/Emulative.php¤!  ’¿Ýj¤!  ¿²ˆì¤      K   vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/TokenEmulator.php3  ’¿Ýj3  \Q/è¤      N   vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/VoidCastEmulator.php,  ’¿Ýj,  ¡ì«H¤      O   vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/EnumTokenEmulator.php½  ’¿Ýj½  Myúø¤      O   vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/AttributeEmulator.phpÁ  ’¿ÝjÁ  È©3¤      M   vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/KeywordEmulator.php†  ’¿Ýj†  U•¡j¤      M   vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/FnTokenEmulator.phpÊ  ’¿ÝjÊ  93 ­¤      M   vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ReverseEmulator.phpõ  ’¿Ýjõ  ^HKß¤      S   vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/PropertyTokenEmulator.phpµ  ’¿Ýjµ  YmÝ¤      P   vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/MatchTokenEmulator.php¦  ’¿Ýj¦  ¡RÌ¤      R   vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/PipeOperatorEmulator.php¤  ’¿Ýj¤  ¾L+N¤      S   vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ExplicitOctalEmulator.php!  ’¿Ýj!  :ÁŠ@¤      S   vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ReadonlyTokenEmulator.php‰  ’¿Ýj‰  …	3ß¤      _   vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/AsymmetricVisibilityTokenEmulator.php  ’¿Ýj  /,§¤      S   vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/NullsafeTokenEmulator.phpê  ’¿Ýjê  7E5f¤      [   vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ReadonlyFunctionTokenEmulator.phpÐ  ’¿ÝjÐ  )Ë†‹¤      %   vendor/nikic/php-parser/composer.json1  ’¿Ýj1  \Mº¤      :   vendor/monolog/monolog/src/Monolog/ResettableInterface.phpÎ  ’¿ÝjÎ  ;•ì¤      :   vendor/monolog/monolog/src/Monolog/Handler/MailHandler.phpÅ  ’¿ÝjÅ  >Fº¤      8   vendor/monolog/monolog/src/Monolog/Handler/Curl/Util.phpM  ’¿ÝjM  ‚ R¤      >   vendor/monolog/monolog/src/Monolog/Handler/MandrillHandler.phpì	  ’¿Ýjì	  NµÒ@¤      C   vendor/monolog/monolog/src/Monolog/Handler/DeduplicationHandler.php  ’¿Ýj  »ãÊR¤      >   vendor/monolog/monolog/src/Monolog/Handler/ElasticaHandler.phpÊ  ’¿ÝjÊ  éZ¡Â¤      ?   vendor/monolog/monolog/src/Monolog/Handler/SyslogUdpHandler.php°  ’¿Ýj°  žm�¤      @   vendor/monolog/monolog/src/Monolog/Handler/LogEntriesHandler.phps  ’¿Ýjs  –wy6¤      H   vendor/monolog/monolog/src/Monolog/Handler/WebRequestRecognizerTrait.phpø  ’¿Ýjø  Ê_"¤      B   vendor/monolog/monolog/src/Monolog/Handler/SyslogUdp/UdpSocket.php]
  ’¿Ýj]
  ¯MOŠ¤      :   vendor/monolog/monolog/src/Monolog/Handler/NullHandler.php3  ’¿Ýj3  gbb4¤      :   vendor/monolog/monolog/src/Monolog/Handler/GelfHandler.php®  ’¿Ýj®  ”‡…“¤      <   vendor/monolog/monolog/src/Monolog/Handler/BufferHandler.php4  ’¿Ýj4  (W±¤      ;   vendor/monolog/monolog/src/Monolog/Handler/IFTTTHandler.phpÝ  ’¿ÝjÝ  ‚GÉ¤      <   vendor/monolog/monolog/src/Monolog/Handler/StreamHandler.php2)  ’¿Ýj2)  “Hºô¤      ;   vendor/monolog/monolog/src/Monolog/Handler/RedisHandler.php®
  ’¿Ýj®
  Ä™—¤      F   vendor/monolog/monolog/src/Monolog/Handler/FormattableHandlerTrait.phpô  ’¿Ýjô  I Ê\¤      D   vendor/monolog/monolog/src/Monolog/Handler/FingersCrossedHandler.phpù  ’¿Ýjù  Ä!Ï¤      9   vendor/monolog/monolog/src/Monolog/Handler/PsrHandler.php¦	  ’¿Ýj¦	  ‡´D¤      H   vendor/monolog/monolog/src/Monolog/Handler/MissingExtensionException.phpÙ  ’¿ÝjÙ  ÆÒÞ¤      C   vendor/monolog/monolog/src/Monolog/Handler/FallbackGroupHandler.php¸  ’¿Ýj¸  êçû¤      <   vendor/monolog/monolog/src/Monolog/Handler/SyslogHandler.php—  ’¿Ýj—  ÉnmÊ¤      >   vendor/monolog/monolog/src/Monolog/Handler/PushoverHandler.php°  ’¿Ýj°  ÆÈ;q¤      ?   vendor/monolog/monolog/src/Monolog/Handler/FleepHookHandler.phpÅ  ’¿ÝjÅ  Öò»Å¤      A   vendor/monolog/monolog/src/Monolog/Handler/TelegramBotHandler.phpU;  ’¿ÝjU;  Ÿ{·¤      @   vendor/monolog/monolog/src/Monolog/Handler/InsightOpsHandler.php(  ’¿Ýj(  uÜÜ¤      :   vendor/monolog/monolog/src/Monolog/Handler/NoopHandler.phpŒ  ’¿ÝjŒ  
óä¤      @   vendor/monolog/monolog/src/Monolog/Handler/FrankenPhpHandler.php¨  ’¿Ýj¨  =ç™¤      <   vendor/monolog/monolog/src/Monolog/Handler/LogglyHandler.php  ’¿Ýj  ªw~Í¤      B   vendor/monolog/monolog/src/Monolog/Handler/NativeMailerHandler.phpP  ’¿ÝjP  å=i¤      9   vendor/monolog/monolog/src/Monolog/Handler/SqsHandler.phpÖ  ’¿ÝjÖ  4d_¤      >   vendor/monolog/monolog/src/Monolog/Handler/SamplingHandler.phpM  ’¿ÝjM  VB¤      =   vendor/monolog/monolog/src/Monolog/Handler/CouchDBHandler.php2
  ’¿Ýj2
  J©Î^¤      J   vendor/monolog/monolog/src/Monolog/Handler/FormattableHandlerInterface.phpõ  ’¿Ýjõ  æñ:i¤      =   vendor/monolog/monolog/src/Monolog/Handler/MongoDBHandler.php¨	  ’¿Ýj¨	  üA´\¤      A   vendor/monolog/monolog/src/Monolog/Handler/ZendMonitorHandler.php#  ’¿Ýj#  X7a¤      >   vendor/monolog/monolog/src/Monolog/Handler/ErrorLogHandler.php_
  ’¿Ýj_
  ™ˆ—¤      :   vendor/monolog/monolog/src/Monolog/Handler/TestHandler.phpì  ’¿Ýjì  �°VK¤      :   vendor/monolog/monolog/src/Monolog/Handler/AmqpHandler.phpÎ  ’¿ÝjÎ  Z-ç¤      >   vendor/monolog/monolog/src/Monolog/Handler/FlowdockHandler.php²  ’¿Ýj²  ±BÙO¤      E   vendor/monolog/monolog/src/Monolog/Handler/DoctrineCouchDBHandler.php~  ’¿Ýj~  €¤ÇŽ¤      D   vendor/monolog/monolog/src/Monolog/Handler/BrowserConsoleHandler.php!%  ’¿Ýj!%  B'.¤      <   vendor/monolog/monolog/src/Monolog/Handler/FilterHandler.php³  ’¿Ýj³  GüÊ#¤      ?   vendor/monolog/monolog/src/Monolog/Handler/ChromePHPHandler.php-  ’¿Ýj-  2<êI¤      F   vendor/monolog/monolog/src/Monolog/Handler/ProcessableHandlerTrait.phpR  ’¿ÝjR  '`z¤      B   vendor/monolog/monolog/src/Monolog/Handler/SlackWebhookHandler.phpT  ’¿ÝjT  @KÅ×¤      C   vendor/monolog/monolog/src/Monolog/Handler/ElasticsearchHandler.php’  ’¿Ýj’  {{a¤      ;   vendor/monolog/monolog/src/Monolog/Handler/SlackHandler.phpø  ’¿Ýjø  0”ÄM¤      Y   vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php‰  ’¿Ýj‰  æjžO¤      Z   vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ErrorLevelActivationStrategy.php;  ’¿Ýj;  \ •Ö¤      \   vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ChannelLevelActivationStrategy.php†	  ’¿Ýj†	  ])?¤      J   vendor/monolog/monolog/src/Monolog/Handler/ProcessableHandlerInterface.php¡  ’¿Ýj¡  (µ¥¤      B   vendor/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php¹2  ’¿Ýj¹2  og?¤      <   vendor/monolog/monolog/src/Monolog/Handler/SocketHandler.php�/  ’¿Ýj�/  ‚@ÕÝ¤      @   vendor/monolog/monolog/src/Monolog/Handler/LogMonsterHandler.php¢  ’¿Ýj¢  wó§%¤      @   vendor/monolog/monolog/src/Monolog/Handler/PHPConsoleHandler.php
0  ’¿Ýj
0  uçj¤      6   vendor/monolog/monolog/src/Monolog/Handler/Handler.php÷  ’¿Ýj÷  ÃÝaÏ¤      @   vendor/monolog/monolog/src/Monolog/Handler/Slack/SlackRecord.php¤)  ’¿Ýj¤)  Ç2õ¤      >   vendor/monolog/monolog/src/Monolog/Handler/NewRelicHandler.phpÍ  ’¿ÝjÍ  ˆ:¤      :   vendor/monolog/monolog/src/Monolog/Handler/CubeHandler.php<  ’¿Ýj<  êéç¤      >   vendor/monolog/monolog/src/Monolog/Handler/DynamoDbHandler.phpŽ  ’¿ÝjŽ  Ahò¤      C   vendor/monolog/monolog/src/Monolog/Handler/SymfonyMailerHandler.phpÉ  ’¿ÝjÉ  Î|„*¤      =   vendor/monolog/monolog/src/Monolog/Handler/FirePHPHandler.php!  ’¿Ýj!  æ¢Ùµ¤      >   vendor/monolog/monolog/src/Monolog/Handler/LogmaticHandler.phpJ
  ’¿ÝjJ
  µëì¤      >   vendor/monolog/monolog/src/Monolog/Handler/AbstractHandler.phpr
  ’¿Ýjr
  † dP¤      =   vendor/monolog/monolog/src/Monolog/Handler/ProcessHandler.phpl  ’¿Ýjl  /µâZ¤      =   vendor/monolog/monolog/src/Monolog/Handler/RollbarHandler.phpì  ’¿Ýjì  m•CQ¤      ?   vendor/monolog/monolog/src/Monolog/Handler/HandlerInterface.phpÛ
  ’¿ÝjÛ
  ”ifw¤      ;   vendor/monolog/monolog/src/Monolog/Handler/GroupHandler.phpÆ  ’¿ÝjÆ  «À˜¼¤      F   vendor/monolog/monolog/src/Monolog/Handler/WhatFailureGroupHandler.phpw  ’¿Ýjw  ·E¤      =   vendor/monolog/monolog/src/Monolog/Handler/HandlerWrapper.php  ’¿Ýj  Üx–ì¤      D   vendor/monolog/monolog/src/Monolog/Handler/AbstractSyslogHandler.php™  ’¿Ýj™  z(F„¤      A   vendor/monolog/monolog/src/Monolog/Handler/RedisPubSubHandler.php“  ’¿Ýj“  -”Bx¤      >   vendor/monolog/monolog/src/Monolog/Handler/OverflowHandler.phpØ  ’¿ÝjØ  Uá<¤      >   vendor/monolog/monolog/src/Monolog/Handler/SendGridHandler.php  ’¿Ýj  ¼r3í¤      H   vendor/monolog/monolog/src/Monolog/Handler/AbstractProcessingHandler.php×  ’¿Ýj×  á!<y¤      ,   vendor/monolog/monolog/src/Monolog/Level.phpÔ  ’¿ÝjÔ  NAU¬¤      C   vendor/monolog/monolog/src/Monolog/Attribute/AsMonologProcessor.php¤  ’¿Ýj¤  oŽ¾I¤      C   vendor/monolog/monolog/src/Monolog/Attribute/WithMonologChannel.phpb  ’¿Ýjb  ÖVf€¤      ,   vendor/monolog/monolog/src/Monolog/Utils.php>.  ’¿Ýj>.  ÷£…¤      4   vendor/monolog/monolog/src/Monolog/Test/TestCase.php  ’¿Ýj  ‚Ï$¤      ;   vendor/monolog/monolog/src/Monolog/Test/MonologTestCase.php„  ’¿Ýj„  #H;6¤      B   vendor/monolog/monolog/src/Monolog/Formatter/LogstashFormatter.phph  ’¿Ýjh  Š¼8¤      D   vendor/monolog/monolog/src/Monolog/Formatter/NormalizerFormatter.php*0  ’¿Ýj*0  eJ>|¤      B   vendor/monolog/monolog/src/Monolog/Formatter/WildfireFormatter.phpÑ  ’¿ÝjÑ  ª>©P¤      A   vendor/monolog/monolog/src/Monolog/Formatter/FluentdFormatter.php  ’¿Ýj  2oEï¤      K   vendor/monolog/monolog/src/Monolog/Formatter/WrappingFormatterInterface.php  ’¿Ýj  ˜Ý?:¤      B   vendor/monolog/monolog/src/Monolog/Formatter/LogmaticFormatter.php%  ’¿Ýj%  Ëµ²e¤      C   vendor/monolog/monolog/src/Monolog/Formatter/RedactingFormatter.phpÇ8  ’¿ÝjÇ8  'Y¤¤      @   vendor/monolog/monolog/src/Monolog/Formatter/ScalarFormatter.phpq  ’¿Ýjq  ‰ ÷Ú¤      @   vendor/monolog/monolog/src/Monolog/Formatter/LogglyFormatter.php   ’¿Ýj   éøæÌ¤      C   vendor/monolog/monolog/src/Monolog/Formatter/FormatterInterface.phpf  ’¿Ýjf  Žä?¤      E   vendor/monolog/monolog/src/Monolog/Formatter/GelfMessageFormatter.phpÜ  ’¿ÝjÜ  ì¨ã¢¤      >   vendor/monolog/monolog/src/Monolog/Formatter/HtmlFormatter.phpÅ  ’¿ÝjÅ  ÙU¶H¤      L   vendor/monolog/monolog/src/Monolog/Formatter/GoogleCloudLoggingFormatter.php›  ’¿Ýj›  wS˜™¤      B   vendor/monolog/monolog/src/Monolog/Formatter/ElasticaFormatter.phps  ’¿Ýjs  Q8ñ<¤      >   vendor/monolog/monolog/src/Monolog/Formatter/JsonFormatter.phpÚ  ’¿ÝjÚ  /È¤      >   vendor/monolog/monolog/src/Monolog/Formatter/LineFormatter.php£&  ’¿Ýj£&  _b¤      @   vendor/monolog/monolog/src/Monolog/Formatter/SyslogFormatter.phpe  ’¿Ýje  P“¹"¤      C   vendor/monolog/monolog/src/Monolog/Formatter/ChromePHPFormatter.phpÚ  ’¿ÝjÚ  ¯w¤      A   vendor/monolog/monolog/src/Monolog/Formatter/MongoDBFormatter.php6  ’¿Ýj6  pÕz¤      G   vendor/monolog/monolog/src/Monolog/Formatter/ElasticsearchFormatter.php/  ’¿Ýj/  Œ~F¤      B   vendor/monolog/monolog/src/Monolog/Formatter/FlowdockFormatter.phpé	  ’¿Ýjé	  ^9ºE¤      4   vendor/monolog/monolog/src/Monolog/SignalHandler.phpB  ’¿ÝjB  ŒÃë ¤      /   vendor/monolog/monolog/src/Monolog/Registry.php¶  ’¿Ýj¶  ¥z@¤      0   vendor/monolog/monolog/src/Monolog/LogRecord.php„  ’¿Ýj„  óQ…�¤      H   vendor/monolog/monolog/src/Monolog/JsonSerializableDateTimeImmutable.phpå  ’¿Ýjå  _‹~C¤      E   vendor/monolog/monolog/src/Monolog/Processor/MemoryUsageProcessor.phpM  ’¿ÝjM  jVŒ‘¤      G   vendor/monolog/monolog/src/Monolog/Processor/IntrospectionProcessor.phpÒ  ’¿ÝjÒ  ¦Èº¤      C   vendor/monolog/monolog/src/Monolog/Processor/MercurialProcessor.php0  ’¿Ýj0  ŒJ¹8¤      @   vendor/monolog/monolog/src/Monolog/Processor/MemoryProcessor.php  ’¿Ýj  ]¦�`¤      E   vendor/monolog/monolog/src/Monolog/Processor/LoadAverageProcessor.php_  ’¿Ýj_  ?Ô>¤      B   vendor/monolog/monolog/src/Monolog/Processor/HostnameProcessor.phpà  ’¿Ýjà  Ùá¤      H   vendor/monolog/monolog/src/Monolog/Processor/ClosureContextProcessor.phpè  ’¿Ýjè  †‹ù¤      =   vendor/monolog/monolog/src/Monolog/Processor/UidProcessor.phpÕ  ’¿ÝjÕ  ÈšzM¤      =   vendor/monolog/monolog/src/Monolog/Processor/GitProcessor.phpÀ  ’¿ÝjÀ  ×J™î¤      =   vendor/monolog/monolog/src/Monolog/Processor/WebProcessor.php�  ’¿Ýj�  ß;s¦¤      C   vendor/monolog/monolog/src/Monolog/Processor/ProcessIdProcessor.php{  ’¿Ýj{  ­‰YS¤      G   vendor/monolog/monolog/src/Monolog/Processor/PsrLogMessageProcessor.php  ’¿Ýj  WŠk^¤      =   vendor/monolog/monolog/src/Monolog/Processor/TagProcessor.php°  ’¿Ýj°   ”ÁF¤      I   vendor/monolog/monolog/src/Monolog/Processor/MemoryPeakUsageProcessor.php`  ’¿Ýj`  l.;k¤      C   vendor/monolog/monolog/src/Monolog/Processor/ProcessorInterface.php@  ’¿Ýj@  Q–¸¤      -   vendor/monolog/monolog/src/Monolog/Logger.phplc  ’¿Ýjlc  uòS>¤      3   vendor/monolog/monolog/src/Monolog/ErrorHandler.php3  ’¿Ýj3  7õÀ¤      8   vendor/monolog/monolog/src/Monolog/DateTimeImmutable.phpG  ’¿ÝjG  ó†Vs¤      $   vendor/monolog/monolog/composer.json-  ’¿Ýj-  J…¾¤      7   vendor/paragonie/constant_time_encoding/src/RFC4648.php…  ’¿Ýj…  9 Õ¤      3   vendor/paragonie/constant_time_encoding/src/Hex.php‹  ’¿Ýj‹  …Âò¶¤      =   vendor/paragonie/constant_time_encoding/src/Base64UrlSafe.phpË  ’¿ÝjË  °ç‡¤      @   vendor/paragonie/constant_time_encoding/src/EncoderInterface.php9  ’¿Ýj9  É£ù•¤      6   vendor/paragonie/constant_time_encoding/src/Binary.phpÕ	  ’¿ÝjÕ	  @)¤      9   vendor/paragonie/constant_time_encoding/src/Base32Hex.php¹  ’¿Ýj¹  7à[³¤      >   vendor/paragonie/constant_time_encoding/src/Base64DotSlash.php  ’¿Ýj  òÆçõ¤      8   vendor/paragonie/constant_time_encoding/src/Encoding.php  ’¿Ýj  ý`�¤      E   vendor/paragonie/constant_time_encoding/src/Base64DotSlashOrdered.php  ’¿Ýj  ,4¢h¤      6   vendor/paragonie/constant_time_encoding/src/Base32.php9F  ’¿Ýj9F  µ¤      6   vendor/paragonie/constant_time_encoding/src/Base64.php]0  ’¿Ýj]0  |÷¤      5   vendor/paragonie/constant_time_encoding/composer.json  ’¿Ýj  4ïjÕ¤      ,   vendor/daverandom/libdns/examples/AQuery.phpÛ  ’¿ÝjÛ  ¶ïÆX¤      .   vendor/daverandom/libdns/examples/autoload.phpÿ  ’¿Ýjÿ  ¤¼ù•¤      .   vendor/daverandom/libdns/examples/SOAQuery.php`
  ’¿Ýj`
  eZ{¤      5   vendor/daverandom/libdns/tools/autoload_generator.phpÝ	  ’¿ÝjÝ	  Ì˜�Z¤      9   vendor/daverandom/libdns/src/Records/RecordCollection.php   ’¿Ýj   ÁÊ²¤¤      .   vendor/daverandom/libdns/src/Records/RData.php  ’¿Ýj  ÖF¤      1   vendor/daverandom/libdns/src/Records/Resource.php¥  ’¿Ýj¥  V~â¤      9   vendor/daverandom/libdns/src/Records/ResourceQClasses.php  ’¿Ýj  ûó€¤      7   vendor/daverandom/libdns/src/Records/ResourceQTypes.php½  ’¿Ýj½  \^,¤      8   vendor/daverandom/libdns/src/Records/QuestionFactory.phpE  ’¿ÝjE  PòÓ­¤      @   vendor/daverandom/libdns/src/Records/RecordCollectionFactory.php±  ’¿Ýj±  M)¤      ?   vendor/daverandom/libdns/src/Records/ResourceBuilderFactory.phpä  ’¿Ýjä  Ð¬;¤      1   vendor/daverandom/libdns/src/Records/Question.phpÅ  ’¿ÝjÅ  ƒê]ž¤      5   vendor/daverandom/libdns/src/Records/RDataFactory.phpn  ’¿Ýjn  �.j¤      8   vendor/daverandom/libdns/src/Records/ResourceFactory.phpŸ  ’¿ÝjŸ  pï7Í¤      :   vendor/daverandom/libdns/src/Records/Types/IPv6Address.phpÎ  ’¿ÝjÎ  ¸pù	¤      5   vendor/daverandom/libdns/src/Records/Types/BitMap.php¦  ’¿Ýj¦  æ™Çr¤      3   vendor/daverandom/libdns/src/Records/Types/Char.phpÐ  ’¿ÝjÐ  >9^¤      3   vendor/daverandom/libdns/src/Records/Types/Long.php:  ’¿Ýj:  \²xç¤      :   vendor/daverandom/libdns/src/Records/Types/IPv4Address.php�	  ’¿Ýj�	  ÙIlâ¤      >   vendor/daverandom/libdns/src/Records/Types/CharacterString.phpZ  ’¿ÝjZ  ÜüJ3¤      3   vendor/daverandom/libdns/src/Records/Types/Type.phpÂ  ’¿ÝjÂ  L§c¤      9   vendor/daverandom/libdns/src/Records/Types/DomainName.phpU  ’¿ÝjU  ¬4ŸX¤      :   vendor/daverandom/libdns/src/Records/Types/TypeBuilder.phpU  ’¿ÝjU  Œ§t¤      :   vendor/daverandom/libdns/src/Records/Types/TypeFactory.php˜
  ’¿Ýj˜
  áv¤      4   vendor/daverandom/libdns/src/Records/Types/Short.phpë  ’¿Ýjë  šð£¤      4   vendor/daverandom/libdns/src/Records/Types/Types.phpÕ  ’¿ÝjÕ  ´žËP¤      7   vendor/daverandom/libdns/src/Records/Types/Anything.php]  ’¿Ýj]  Ÿ2Öì¤      4   vendor/daverandom/libdns/src/Records/RecordTypes.php—  ’¿Ýj—  è„0¤      5   vendor/daverandom/libdns/src/Records/RDataBuilder.phpt  ’¿Ýjt  €|º¤      6   vendor/daverandom/libdns/src/Records/ResourceTypes.php  ’¿Ýj  z|Í[¤      O   vendor/daverandom/libdns/src/Records/TypeDefinitions/FieldDefinitionFactory.phpG  ’¿ÝjG  GBj×¤      N   vendor/daverandom/libdns/src/Records/TypeDefinitions/TypeDefinitionFactory.phpa  ’¿Ýja  :_×¤      N   vendor/daverandom/libdns/src/Records/TypeDefinitions/TypeDefinitionManager.php;)  ’¿Ýj;)  ç1]>¤      H   vendor/daverandom/libdns/src/Records/TypeDefinitions/FieldDefinition.php  ’¿Ýj  <‘ª¤      G   vendor/daverandom/libdns/src/Records/TypeDefinitions/TypeDefinition.php€  ’¿Ýj€  ã^XÞ¤      U   vendor/daverandom/libdns/src/Records/TypeDefinitions/TypeDefinitionManagerFactory.php”  ’¿Ýj”  p[¡è¤      /   vendor/daverandom/libdns/src/Records/Record.php!	  ’¿Ýj!	  €æó ¤      8   vendor/daverandom/libdns/src/Records/ResourceBuilder.php¤  ’¿Ýj¤  t%Y¤      8   vendor/daverandom/libdns/src/Records/ResourceClasses.phpÈ  ’¿ÝjÈ  v¿¤      ?   vendor/daverandom/libdns/src/Encoder/EncodingContextFactory.php  ’¿Ýj  n¡‹Ï¤      7   vendor/daverandom/libdns/src/Encoder/EncoderFactory.php  ’¿Ýj  ˜ô Î¤      0   vendor/daverandom/libdns/src/Encoder/Encoder.php“+  ’¿Ýj“+  ë„qH¤      8   vendor/daverandom/libdns/src/Encoder/EncodingContext.php´  ’¿Ýj´  ÇÜ)X¤      8   vendor/daverandom/libdns/src/Messages/MessageFactory.php†  ’¿Ýj†  Iù!®¤      >   vendor/daverandom/libdns/src/Messages/MessageResponseCodes.php   ’¿Ýj   b7'¤      6   vendor/daverandom/libdns/src/Messages/MessageTypes.phpš  ’¿Ýjš  T+»¤      1   vendor/daverandom/libdns/src/Messages/Message.php{  ’¿Ýj{  –^UA¤      8   vendor/daverandom/libdns/src/Messages/MessageOpCodes.php²  ’¿Ýj²  `iBù¤      6   vendor/daverandom/libdns/src/Packets/PacketFactory.php÷  ’¿Ýj÷  óiŸ@¤      /   vendor/daverandom/libdns/src/Packets/Packet.php  ’¿Ýj  ¿�­N¤      6   vendor/daverandom/libdns/src/Packets/LabelRegistry.phpA  ’¿ÝjA  Ûuìf¤      0   vendor/daverandom/libdns/src/Decoder/Decoder.phpI  ’¿ÝjI  -“Uµ¤      ?   vendor/daverandom/libdns/src/Decoder/DecodingContextFactory.php³  ’¿Ýj³  Y)æ¤      8   vendor/daverandom/libdns/src/Decoder/DecodingContext.php  ’¿Ýj  $W:A¤      7   vendor/daverandom/libdns/src/Decoder/DecoderFactory.phpb  ’¿Ýjb  mœ¤      ,   vendor/daverandom/libdns/src/Enumeration.phpÓ  ’¿ÝjÓ  ©‘{¤      *   vendor/daverandom/libdns/src/functions.php  ’¿Ýj  M€¨ñ¤      &   vendor/daverandom/libdns/composer.jsonþ  ’¿Ýjþ  6pè¤      5   vendor/bacon/bacon-qr-code/src/Encoder/MatrixUtil.phpêA  ’¿ÝjêA  =@Æ3¤      1   vendor/bacon/bacon-qr-code/src/Encoder/QrCode.phpL	  ’¿ÝjL	  ï|#¤      4   vendor/bacon/bacon-qr-code/src/Encoder/BlockPair.php�  ’¿Ýj�  è^•ø¤      2   vendor/bacon/bacon-qr-code/src/Encoder/Encoder.phpDW  ’¿ÝjDW  P!ò¤      3   vendor/bacon/bacon-qr-code/src/Encoder/MaskUtil.phpW  ’¿ÝjW  :êR¤      5   vendor/bacon/bacon-qr-code/src/Encoder/ByteMatrix.php  ’¿Ýj  F¾öf¤      A   vendor/bacon/bacon-qr-code/src/Exception/OutOfBoundsException.php¥   ’¿Ýj¥   y&¤      ?   vendor/bacon/bacon-qr-code/src/Exception/ExceptionInterface.php‡   ’¿Ýj‡   "ós¬¤      E   vendor/bacon/bacon-qr-code/src/Exception/UnexpectedValueException.php­   ’¿Ýj­   �„¹¤      E   vendor/bacon/bacon-qr-code/src/Exception/InvalidArgumentException.php­   ’¿Ýj­   6¡²½¤      <   vendor/bacon/bacon-qr-code/src/Exception/WriterException.phpœ   ’¿Ýjœ   ê¹-¤      =   vendor/bacon/bacon-qr-code/src/Exception/RuntimeException.php�   ’¿Ýj�   3/f!¤      3   vendor/bacon/bacon-qr-code/src/Common/BitMatrix.php  ’¿Ýj  œWï¤      >   vendor/bacon/bacon-qr-code/src/Common/ErrorCorrectionLevel.phpý  ’¿Ýjý  ¦pœ§¤      ;   vendor/bacon/bacon-qr-code/src/Common/FormatInformation.phpZ  ’¿ÝjZ  @Mža¤      9   vendor/bacon/bacon-qr-code/src/Common/CharacterSetEci.php·  ’¿Ýj·  TÒMï¤      1   vendor/bacon/bacon-qr-code/src/Common/Version.phpƒU  ’¿ÝjƒU  G~|÷¤      2   vendor/bacon/bacon-qr-code/src/Common/BitUtils.phpŠ  ’¿ÝjŠ  ×‰ÃÊ¤      .   vendor/bacon/bacon-qr-code/src/Common/Mode.php†  ’¿Ýj†  ¬¿ÚK¤      2   vendor/bacon/bacon-qr-code/src/Common/EcBlocks.phpó  ’¿Ýjó  !3¹ó¤      2   vendor/bacon/bacon-qr-code/src/Common/BitArray.phpC"  ’¿ÝjC"  �â+Ö¤      1   vendor/bacon/bacon-qr-code/src/Common/EcBlock.phpò  ’¿Ýjò  @šª¤      :   vendor/bacon/bacon-qr-code/src/Common/ReedSolomonCodec.phpß9  ’¿Ýjß9  G­ÅC¤      9   vendor/bacon/bacon-qr-code/src/Renderer/GDLibRenderer.phpÃ  ’¿ÝjÃ  QÝ'€¤      9   vendor/bacon/bacon-qr-code/src/Renderer/ImageRenderer.php+  ’¿Ýj+  YïNÅ¤      >   vendor/bacon/bacon-qr-code/src/Renderer/RendererStyle/Fill.php¬  ’¿Ýj¬  m¯¡÷¤      F   vendor/bacon/bacon-qr-code/src/Renderer/RendererStyle/GradientType.php  ’¿Ýj  #[‹I¤      A   vendor/bacon/bacon-qr-code/src/Renderer/RendererStyle/EyeFill.phpç  ’¿Ýjç  íÀx¤      B   vendor/bacon/bacon-qr-code/src/Renderer/RendererStyle/Gradient.phpy  ’¿Ýjy  µfY:¤      G   vendor/bacon/bacon-qr-code/src/Renderer/RendererStyle/RendererStyle.phpÃ  ’¿ÝjÃ  ²5Æ«¤      A   vendor/bacon/bacon-qr-code/src/Renderer/Image/SvgImageBackEnd.php2  ’¿Ýj2  "˜¬Í¤      F   vendor/bacon/bacon-qr-code/src/Renderer/Image/TransformationMatrix.phpÿ  ’¿Ýjÿ  ·üü°¤      E   vendor/bacon/bacon-qr-code/src/Renderer/Image/ImagickImageBackEnd.php°(  ’¿Ýj°(  Ë_PÉ¤      A   vendor/bacon/bacon-qr-code/src/Renderer/Image/EpsImageBackEnd.php›.  ’¿Ýj›.  ‡j�R¤      G   vendor/bacon/bacon-qr-code/src/Renderer/Image/ImageBackEndInterface.phpÔ	  ’¿ÝjÔ	  g^q¤      =   vendor/bacon/bacon-qr-code/src/Renderer/RendererInterface.php¹   ’¿Ýj¹   5Ðö¤      @   vendor/bacon/bacon-qr-code/src/Renderer/Color/ColorInterface.phpm  ’¿Ýjm  ¶ÏÛ¤      6   vendor/bacon/bacon-qr-code/src/Renderer/Color/Cmyk.phpÞ  ’¿ÝjÞ  °‚Y�¤      6   vendor/bacon/bacon-qr-code/src/Renderer/Color/Gray.php¼  ’¿Ýj¼  &¯–Ë¤      7   vendor/bacon/bacon-qr-code/src/Renderer/Color/Alpha.php   ’¿Ýj   4ñ¯/¤      5   vendor/bacon/bacon-qr-code/src/Renderer/Color/Rgb.php)  ’¿Ýj)  kòì¤      5   vendor/bacon/bacon-qr-code/src/Renderer/Path/Path.php�
  ’¿Ýj�
  iòæÈ¤      C   vendor/bacon/bacon-qr-code/src/Renderer/Path/OperationInterface.phpP  ’¿ÝjP  ä/o€¤      <   vendor/bacon/bacon-qr-code/src/Renderer/Path/EllipticArc.phpš  ’¿Ýjš  ã…2¤      6   vendor/bacon/bacon-qr-code/src/Renderer/Path/Curve.php  ’¿Ýj  O0|¤      6   vendor/bacon/bacon-qr-code/src/Renderer/Path/Close.phpe  ’¿Ýje  Ê`�[¤      5   vendor/bacon/bacon-qr-code/src/Renderer/Path/Move.phpt  ’¿Ýjt  ¤      5   vendor/bacon/bacon-qr-code/src/Renderer/Path/Line.phpt  ’¿Ýjt  »á·¶¤      =   vendor/bacon/bacon-qr-code/src/Renderer/PlainTextRenderer.phpH  ’¿ÝjH  4L4¤      <   vendor/bacon/bacon-qr-code/src/Renderer/Eye/CompositeEye.php:  ’¿Ýj:  ÊßLµ¤      <   vendor/bacon/bacon-qr-code/src/Renderer/Eye/EyeInterface.phpT  ’¿ÝjT  ²#Ú¤      9   vendor/bacon/bacon-qr-code/src/Renderer/Eye/ModuleEye.phpY  ’¿ÝjY  _éP¤      ?   vendor/bacon/bacon-qr-code/src/Renderer/Eye/SimpleCircleEye.php¹  ’¿Ýj¹   �W•¤      9   vendor/bacon/bacon-qr-code/src/Renderer/Eye/PointyEye.php¼  ’¿Ýj¼  �³‹¤      9   vendor/bacon/bacon-qr-code/src/Renderer/Eye/SquareEye.php  ’¿Ýj  WÛë)¤      B   vendor/bacon/bacon-qr-code/src/Renderer/Module/ModuleInterface.phpë  ’¿Ýjë  7A<y¤      =   vendor/bacon/bacon-qr-code/src/Renderer/Module/DotsModule.phpÙ  ’¿ÝjÙ   e±˜¤      ?   vendor/bacon/bacon-qr-code/src/Renderer/Module/SquareModule.php  ’¿Ýj  sÅÄ1¤      B   vendor/bacon/bacon-qr-code/src/Renderer/Module/RoundnessModule.phpI  ’¿ÝjI  ŽŠÒ¤      D   vendor/bacon/bacon-qr-code/src/Renderer/Module/EdgeIterator/Edge.phpP  ’¿ÝjP  Ô°�G¤      L   vendor/bacon/bacon-qr-code/src/Renderer/Module/EdgeIterator/EdgeIterator.php  ’¿Ýj  83F<¤      )   vendor/bacon/bacon-qr-code/src/Writer.phpÇ  ’¿ÝjÇ  )ròœ¤      (   vendor/bacon/bacon-qr-code/composer.json
  ’¿Ýj
  PîS¤      $   vendor/danog/ipc/examples/client.php	  ’¿Ýj	  5#ïþ¤      $   vendor/danog/ipc/examples/server.php‚  ’¿Ýj‚  3ðLw¤      '   vendor/danog/ipc/.php-cs-fixer.dist.phpß   ’¿Ýjß   èùÂ‰¤      +   vendor/danog/ipc/lib/PendingAcceptError.phpã  ’¿Ýjã  t¿Àª¤      -   vendor/danog/ipc/lib/Sync/ChannelCloseReq.phpY   ’¿ÝjY   Ñ–{Š¤      2   vendor/danog/ipc/lib/Sync/SynchronizationError.phpg   ’¿Ýjg   Ï›~¤      (   vendor/danog/ipc/lib/Sync/PanicError.phph  ’¿Ýjh  ì…¤      %   vendor/danog/ipc/lib/Sync/Channel.php‘  ’¿Ýj‘  ãžSß¤      .   vendor/danog/ipc/lib/Sync/ChannelledSocket.php¶	  ’¿Ýj¶	  �ç¡¤      4   vendor/danog/ipc/lib/Sync/SerializationException.phpm   ’¿Ýjm   †Ú�C¤      ,   vendor/danog/ipc/lib/Sync/ChannelInitAck.phpX   ’¿ÝjX   Í½ž%¤      .   vendor/danog/ipc/lib/Sync/ChannelException.phpg   ’¿Ýjg   ‹Î¼¤      +   vendor/danog/ipc/lib/Sync/ChannelParser.phpº	  ’¿Ýjº	  Ô*’5¤      +   vendor/danog/ipc/lib/IpcServerException.php¡  ’¿Ýj¡  üíÅ	¤      "   vendor/danog/ipc/lib/IpcServer.phpO.  ’¿ÝjO.  rØæf¤      "   vendor/danog/ipc/lib/functions.phpÌ  ’¿ÝjÌ  ¾d€r¤      !   vendor/danog/ipc/test/IpcTest.phpU  ’¿ÝjU  å%I[¤      )   vendor/danog/ipc/test/Fixtures/server.php¶  ’¿Ýj¶  å7Îo¤      -   vendor/danog/ipc/test/Fixtures/echoServer.php?  ’¿Ýj?  •-£Â¤         vendor/danog/ipc/composer.jsonr  ’¿Ýjr  yâ@8¤      ,   vendor/danog/php-rtc-mixin/src/DataClass.phpo  ’¿Ýjo  Jî&h¤      9   vendor/danog/php-rtc-mixin/src/SharedLibraryInterface.phpx  ’¿Ýjx  ÖÏú¤      4   vendor/danog/php-rtc-mixin/src/SerializableState.php5  ’¿Ýj5  sÙÌä¤      (   vendor/danog/php-rtc-mixin/composer.json¨  ’¿Ýj¨  ìü¤      )   vendor/danog/php-rtc-mdns/src/Factory.phpY  ’¿ÝjY  ¡
æ¤      3   vendor/danog/php-rtc-mdns/src/MulticastExecutor.phpH  ’¿ÝjH  õ¢™¤      '   vendor/danog/php-rtc-mdns/composer.jsonå  ’¿Ýjå  o{C¾¤      2   vendor/danog/dns-over-https/.php-cs-fixer.dist.phpß   ’¿Ýjß   èùÂ‰¤      0   vendor/danog/dns-over-https/lib/DoHException.php‘   ’¿Ýj‘   ˜Å€ü¤      -   vendor/danog/dns-over-https/lib/DoHConfig.phpV
  ’¿ÝjV
  #Ÿmx¤      5   vendor/danog/dns-over-https/lib/DoHNameserverType.php’   ’¿Ýj’   ~pÿû¤      :   vendor/danog/dns-over-https/lib/Rfc8484StubDoHResolver.phpdG  ’¿ÝjdG  ;&¤      1   vendor/danog/dns-over-https/lib/DoHNameserver.php�  ’¿Ýj�  *Ò Á¤      )   vendor/danog/dns-over-https/composer.json  ’¿Ýj  7ü)¤      F   vendor/danog/madelineproto/examples/plugins/Danogentili/PingPlugin.phpE	  ’¿ÝjE	  ×8î;¤      H   vendor/danog/madelineproto/examples/plugins/Danogentili/OnlinePlugin.php¤  ’¿Ýj¤  XòN®¤      +   vendor/danog/madelineproto/examples/bot.php2  ’¿Ýj2  õ‚d!í      2   vendor/danog/madelineproto/examples/PluginBase.php™  ’¿Ýj™  nì¹¤      3   vendor/danog/madelineproto/examples/combinedBot.phpC  ’¿ÝjC  Ñ™Oí      4   vendor/danog/madelineproto/examples/libtgvoipbot.php  ’¿Ýj  Œ?'õ¤      2   vendor/danog/madelineproto/examples/secret_bot.phpX  ’¿ÝjX  TÒÉí      8   vendor/danog/madelineproto/examples/tgstories_dl_bot.php(  ’¿Ýj(  µãwã¤      1   vendor/danog/madelineproto/examples/simpleBot.phpþ  ’¿Ýjþ  °s	3¤      1   vendor/danog/madelineproto/.php-cs-fixer.dist.php]  ’¿Ýj]  ~Ülž¤      -   vendor/danog/madelineproto/ton/toncustom.json  ’¿Ýj  ?Ð¤      .   vendor/danog/madelineproto/ton/lite-client.phpr  ’¿Ýjr  öt}¶¤      @   vendor/danog/madelineproto/ton/ton-lite-client-test1.config.jsonõ  ’¿Ýjõ  t¢í¤      1   vendor/danog/madelineproto/src/RecordingEvent.phpl  ’¿Ýjl  õïÕS¤      3   vendor/danog/madelineproto/src/GarbageCollector.php¾3  ’¿Ýj¾3  @2Íä¤      -   vendor/danog/madelineproto/src/Connection.php€i  ’¿Ýj€i  c+"ß¤      F   vendor/danog/madelineproto/src/GroupCall/Rtmp/RtmpRefusedException.php  ’¿Ýj  wÎéƒ¤      6   vendor/danog/madelineproto/src/GroupCall/Rtmp/Amf0.php  ’¿Ýj  á}}[¤      ?   vendor/danog/madelineproto/src/GroupCall/Rtmp/Amf0EcmaArray.php  ’¿Ýj  Þ'Ï¤      9   vendor/danog/madelineproto/src/GroupCall/Rtmp/FlvTags.phpP!  ’¿ÝjP!  Øš_¤      <   vendor/danog/madelineproto/src/GroupCall/Rtmp/RtmpClient.php/P  ’¿Ýj/P  {‰Å¤      ?   vendor/danog/madelineproto/src/GroupCall/Rtmp/RtmpPublisher.php	W  ’¿Ýj	W  rgH+¤      ;   vendor/danog/madelineproto/src/GroupCall/StreamReceiver.php}  ’¿Ýj}  'œXÄ¤      @   vendor/danog/madelineproto/src/GroupCall/GroupCallController.phpÆã  ’¿ÝjÆã  ¥Í(¤      4   vendor/danog/madelineproto/src/GroupCall/Handler.phpRÛ  ’¿ÝjRÛ  ™SJá¤      7   vendor/danog/madelineproto/src/GroupCall/OggDemuxer.phpâ  ’¿Ýjâ  QL¤      +   vendor/danog/madelineproto/src/polyfill.phpy  ’¿Ýjy  iÃ²¤      6   vendor/danog/madelineproto/src/Namespace/Chatlists.php´[  ’¿Ýj´[  nO�l¤      3   vendor/danog/madelineproto/src/Namespace/Wallet.phpçb ’¿Ýjçb ½„0%¤      5   vendor/danog/madelineproto/src/Namespace/Stickers.php}  ’¿Ýj}  ¶~àë¤      6   vendor/danog/madelineproto/src/Namespace/Blacklist.phph  ’¿Ýjh  7Çï¤      5   vendor/danog/madelineproto/src/Namespace/Langpack.php^   ’¿Ýj^   Z\ºQ¤      4   vendor/danog/madelineproto/src/Namespace/Smsjobs.php$  ’¿Ýj$  Vå#¤      1   vendor/danog/madelineproto/src/Namespace/Auth.phpÇÕ  ’¿ÝjÇÕ  Ô‚P…¤      5   vendor/danog/madelineproto/src/Namespace/Payments.php¿ ’¿Ýj¿ ×�¤      2   vendor/danog/madelineproto/src/Namespace/Stats.php5Û ’¿Ýj5Û œš‘E¤      8   vendor/danog/madelineproto/src/Namespace/AbstractAPI.php_
  ’¿Ýj_
  z¹åÇ¤      3   vendor/danog/madelineproto/src/Namespace/Upload.phpj
  ’¿Ýjj
  tHèn¤      5   vendor/danog/madelineproto/src/Namespace/Messages.php¯õ2 ’¿Ýj¯õ2 áOL¢¤      6   vendor/danog/madelineproto/src/Namespace/Ephemeral.php2¢ ’¿Ýj2¢ ñ^{¤      4   vendor/danog/madelineproto/src/Namespace/Premium.php!  ’¿Ýj!  °tW¤      1   vendor/danog/madelineproto/src/Namespace/Help.phpZê  ’¿ÝjZê  láÁŽ¤      4   vendor/danog/madelineproto/src/Namespace/Account.phpNÌ ’¿ÝjNÌ Ü'¾¤      4   vendor/danog/madelineproto/src/Namespace/Stories.phpEý ’¿ÝjEý 3>'”¤      6   vendor/danog/madelineproto/src/Namespace/Aicompose.phpgt  ’¿Ýjgt  ßô†¤      4   vendor/danog/madelineproto/src/Namespace/Folders.php  ’¿Ýj  Û•ùš¤      2   vendor/danog/madelineproto/src/Namespace/Phone.php6ö  ’¿Ýj6ö  ²ç€¤      5   vendor/danog/madelineproto/src/Namespace/Channels.phps ’¿Ýjs *xa¤      2   vendor/danog/madelineproto/src/Namespace/Users.phpˆ8  ’¿Ýjˆ8  ÀdÔr¤      8   vendor/danog/madelineproto/src/Namespace/Communities.phpÖ,  ’¿ÝjÖ,  è.¤      3   vendor/danog/madelineproto/src/Namespace/Photos.php³5  ’¿Ýj³5  ª]8›¤      4   vendor/danog/madelineproto/src/Namespace/Updates.php:  ’¿Ýj:  ,(è'¤      6   vendor/danog/madelineproto/src/Namespace/Toncenter.phpÚ	  ’¿ÝjÚ	  h	ß£¤      1   vendor/danog/madelineproto/src/Namespace/Bots.phpJ ’¿ÝjJ þJ�5¤      5   vendor/danog/madelineproto/src/Namespace/Fragment.php)  ’¿Ýj)  `|¤      5   vendor/danog/madelineproto/src/Namespace/Contacts.phpkº  ’¿Ýjkº  ½àpd¤      4   vendor/danog/madelineproto/src/UpdateHandlerType.phpz  ’¿Ýjz  $t´o¤      +   vendor/danog/madelineproto/src/Shutdown.phpˆ  ’¿Ýjˆ  1*J~¤      -   vendor/danog/madelineproto/src/CallStream.phpS  ’¿ÝjS  0Þv¶¤      7   vendor/danog/madelineproto/src/PeerNotInDbException.phpî  ’¿Ýjî  »û¤      0   vendor/danog/madelineproto/src/Serialization.php¬a  ’¿Ýj¬a  Ý-7¤      ;   vendor/danog/madelineproto/src/Ipc/Runner/ProcessRunner.phpD$  ’¿ÝjD$  ­îù�¤      7   vendor/danog/madelineproto/src/Ipc/Runner/WebRunner.php
  ’¿Ýj
  i½%‡¤      <   vendor/danog/madelineproto/src/Ipc/Runner/RunnerAbstract.php  ’¿Ýj  úÄúu¤      3   vendor/danog/madelineproto/src/Ipc/Runner/entry.phpq  ’¿Ýjq  ;›y¤      2   vendor/danog/madelineproto/src/Ipc/ExitFailure.php·  ’¿Ýj·  5¯¶¤      E   vendor/danog/madelineproto/src/Ipc/Wrapper/SeekableWritableStream.phpU  ’¿ÝjU  Ju¼¤      D   vendor/danog/madelineproto/src/Ipc/Wrapper/CancellationReference.php‰  ’¿Ýj‰  ±pL(¤      >   vendor/danog/madelineproto/src/Ipc/Wrapper/WrapMethodTrait.php{  ’¿Ýj{  þG{1¤      =   vendor/danog/madelineproto/src/Ipc/Wrapper/ReadableStream.phpŒ	  ’¿ÝjŒ	  Îu·¤      ;   vendor/danog/madelineproto/src/Ipc/Wrapper/FileCallback.php_  ’¿Ýj_  Õ‰‡¤      =   vendor/danog/madelineproto/src/Ipc/Wrapper/WritableStream.php÷  ’¿Ýj÷  6Agü¤      ;   vendor/danog/madelineproto/src/Ipc/Wrapper/Cancellation.phpÏ  ’¿ÝjÏ  }-—Ö¤      E   vendor/danog/madelineproto/src/Ipc/Wrapper/SeekableReadableStream.phpU  ’¿ÝjU  ¯+G+¤      @   vendor/danog/madelineproto/src/Ipc/Wrapper/CancellationInner.php   ’¿Ýj   =ÿÊí¤      <   vendor/danog/madelineproto/src/Ipc/Wrapper/SeekableTrait.php  ’¿Ýj  �Ô”¬¤      B   vendor/danog/madelineproto/src/Ipc/Wrapper/WrappedCancellation.php$  ’¿Ýj$  Ö>v¤      2   vendor/danog/madelineproto/src/Ipc/Wrapper/Obj.php  ’¿Ýj  GÒL3¤      <   vendor/danog/madelineproto/src/Ipc/Wrapper/ClosableTrait.phpi  ’¿Ýji  -iÌ¤      5   vendor/danog/madelineproto/src/Ipc/ClientAbstract.phpÿ2  ’¿Ýjÿ2  (ˆ�¤      -   vendor/danog/madelineproto/src/Ipc/Client.php;d  ’¿Ýj;d  C‰B|¤      .   vendor/danog/madelineproto/src/Ipc/Wrapper.phpÅ)  ’¿ÝjÅ)  Ïy›—¤      5   vendor/danog/madelineproto/src/Ipc/ServerCallback.phpl  ’¿Ýjl  ÿ{Y¤      5   vendor/danog/madelineproto/src/Ipc/AbstractServer.phpÆ1  ’¿ÝjÆ1  ;ýÏG¤      =   vendor/danog/madelineproto/src/Ipc/AlreadyServedException.phpÜ  ’¿ÝjÜ  Å`(/¤      8   vendor/danog/madelineproto/src/Ipc/EventHandlerProxy.phpÙ  ’¿ÝjÙ  ìÀa¤      1   vendor/danog/madelineproto/src/Ipc/IpcCapable.phpl	  ’¿Ýjl	  ·Ñ¥º¤      -   vendor/danog/madelineproto/src/Ipc/Server.phpT  ’¿ÝjT  hCT¤      /   vendor/danog/madelineproto/src/Ipc/IpcState.phpÏ  ’¿ÝjÏ  ·�ª¤      8   vendor/danog/madelineproto/src/UploadResumeException.phpå  ’¿Ýjå  �_:R¤      3   vendor/danog/madelineproto/src/SettingsAbstract.phpH  ’¿ÝjH  [‰[¤      7   vendor/danog/madelineproto/src/MyTelegramOrgWrapper.php¹-  ’¿Ýj¹-  ·«X&¤      -   vendor/danog/madelineproto/src/DoHWrapper.phpi  ’¿Ýji  †Ô¹+¤      :   vendor/danog/madelineproto/src/ResumableDownloadStream.php–!  ’¿Ýj–!  iTKG¤      G   vendor/danog/madelineproto/src/RPCError/ChatGuestSendForbiddenError.php±  ’¿Ýj±  Eþ¿³¤      :   vendor/danog/madelineproto/src/RPCError/FloodWaitError.phpÎ  ’¿ÝjÎ  äÈAï¤      @   vendor/danog/madelineproto/src/RPCError/ScheduleTooMuchError.php«  ’¿Ýj«  �
>‘¤      I   vendor/danog/madelineproto/src/RPCError/BusinessPeerUsageMissingError.phpÄ  ’¿ÝjÄ  »$²L¤      D   vendor/danog/madelineproto/src/RPCError/ScheduleDateTooLateError.phpÕ  ’¿ÝjÕ  :Äó2¤      H   vendor/danog/madelineproto/src/RPCError/ChatSendVideosForbiddenError.php¿  ’¿Ýj¿  Øßc)¤      E   vendor/danog/madelineproto/src/RPCError/UserBotToBotDisabledError.phpS  ’¿ÝjS  ŠŽ@0¤      G   vendor/danog/madelineproto/src/RPCError/FromMessageBotDisabledError.phpÎ  ’¿ÝjÎ  ´h§Ý¤      D   vendor/danog/madelineproto/src/RPCError/UsernameNotOccupiedError.php·  ’¿Ýj·  ì’—‚¤      B   vendor/danog/madelineproto/src/RPCError/TodoItemDuplicateError.php÷  ’¿Ýj÷  €ðtç¤      C   vendor/danog/madelineproto/src/RPCError/PaymentUnsupportedError.php‘  ’¿Ýj‘  ú/Ñ¤      ?   vendor/danog/madelineproto/src/RPCError/ChannelPrivateError.php²  ’¿Ýj²  |¨o¤      A   vendor/danog/madelineproto/src/RPCError/BotGamesDisabledError.php£  ’¿Ýj£  +çëí¤      J   vendor/danog/madelineproto/src/RPCError/QuickRepliesBotNotAllowedError.phpI  ’¿ÝjI  ^»8¤      A   vendor/danog/madelineproto/src/RPCError/FloodPremiumWaitError.phpÝ  ’¿ÝjÝ  \ÖÒÜ¤      H   vendor/danog/madelineproto/src/RPCError/ChatSendAudiosForbiddenError.phpÏ  ’¿ÝjÏ  �< a¤      ?   vendor/danog/madelineproto/src/RPCError/ChatRestrictedError.phpÐ  ’¿ÝjÐ  V³-š¤      D   vendor/danog/madelineproto/src/RPCError/BotPaymentsDisabledError.phpñ  ’¿Ýjñ  (!²8¤      G   vendor/danog/madelineproto/src/RPCError/ChatSendPlainForbiddenError.phpâ  ’¿Ýjâ  dêÌË¤      D   vendor/danog/madelineproto/src/RPCError/UserBannedInChannelError.phpå  ’¿Ýjå  ûŠf¤      >   vendor/danog/madelineproto/src/RPCError/UserIsBlockedError.php•  ’¿Ýj•  ¬V�š¤      @   vendor/danog/madelineproto/src/RPCError/WebpageNotFoundError.phpá  ’¿Ýjá  x<X¤      G   vendor/danog/madelineproto/src/RPCError/ChatSendMediaForbiddenError.phpº  ’¿Ýjº  Ð‡ý¤      ?   vendor/danog/madelineproto/src/RPCError/YouBlockedUserError.phpˆ  ’¿Ýjˆ  VüÅ¤      =   vendor/danog/madelineproto/src/RPCError/MsgIdInvalidError.phpŽ  ’¿ÝjŽ  œb/*¤      =   vendor/danog/madelineproto/src/RPCError/TopicDeletedError.php•  ’¿Ýj•  m	Æ9¤      O   vendor/danog/madelineproto/src/RPCError/BroadcastPublicVotersForbiddenError.phpä  ’¿Ýjä  £u0¤      J   vendor/danog/madelineproto/src/RPCError/EncryptionAlreadyAcceptedError.php·  ’¿Ýj·  ±D¤      <   vendor/danog/madelineproto/src/RPCError/TopicClosedError.phpÍ  ’¿ÝjÍ  ÂA¤¤      J   vendor/danog/madelineproto/src/RPCError/SubscriptionExportMissingError.php]  ’¿Ýj]  g(.¤      >   vendor/danog/madelineproto/src/RPCError/PeerIdInvalidError.php™  ’¿Ýj™  å?²¤      H   vendor/danog/madelineproto/src/RPCError/ChatSendVoicesForbiddenError.phpÓ  ’¿ÝjÓ  7]�/¤      D   vendor/danog/madelineproto/src/RPCError/RequestTokenInvalidError.php{  ’¿Ýj{  =°Ã¤      F   vendor/danog/madelineproto/src/RPCError/ChatSendDocsForbiddenError.php¿  ’¿Ýj¿  îµŒÃ¤      F   vendor/danog/madelineproto/src/RPCError/ScheduleStatusPrivateError.phpI  ’¿ÝjI  «h¤      A   vendor/danog/madelineproto/src/RPCError/FileTokenInvalidError.phpˆ  ’¿Ýjˆ  £ùôØ¤      F   vendor/danog/madelineproto/src/RPCError/ChatSendPollForbiddenError.php·  ’¿Ýj·  môJí¤      G   vendor/danog/madelineproto/src/RPCError/PrivacyPremiumRequiredError.phpV  ’¿ÝjV  VŠ:¤      D   vendor/danog/madelineproto/src/RPCError/StoriesNeverCreatedError.php¾  ’¿Ýj¾  ×5
Ÿ¤      D   vendor/danog/madelineproto/src/RPCError/QuickRepliesTooMuchError.phpž  ’¿Ýjž  Üwmð¤      C   vendor/danog/madelineproto/src/RPCError/EncryptionDeclinedError.php¡  ’¿Ýj¡  jž0)¤      E   vendor/danog/madelineproto/src/RPCError/ReplyMessagesTooMuchError.phpÑ  ’¿ÝjÑ  R]V¤      G   vendor/danog/madelineproto/src/RPCError/VoiceMessagesForbiddenError.phpý  ’¿Ýjý  ÙQBÎ¤      E   vendor/danog/madelineproto/src/RPCError/FileReferenceExpiredError.phpn  ’¿Ýjn  qŒhÒ¤      G   vendor/danog/madelineproto/src/RPCError/ChatForwardsRestrictedError.php×  ’¿Ýj×  {ˆ4w¤      E   vendor/danog/madelineproto/src/RPCError/AllowPaymentRequiredError.php<  ’¿Ýj<  ø˜�Í¤      D   vendor/danog/madelineproto/src/RPCError/PollOptionDuplicateError.php«  ’¿Ýj«  {Å¶Å¤      D   vendor/danog/madelineproto/src/RPCError/CallAlreadyAcceptedError.php§  ’¿Ýj§  QÒf¡¤      A   vendor/danog/madelineproto/src/RPCError/TodoItemsTooMuchError.php±  ’¿Ýj±  6Ó¤      :   vendor/danog/madelineproto/src/RPCError/RateLimitError.phpd
  ’¿Ýjd
  88‹¤      E   vendor/danog/madelineproto/src/RPCError/InputUserDeactivatedError.php¬  ’¿Ýj¬  +¡‘%¤      C   vendor/danog/madelineproto/src/RPCError/ChatWriteForbiddenError.php£  ’¿Ýj£  CùÃ¤      M   vendor/danog/madelineproto/src/RPCError/ChatSendRoundvideosForbiddenError.phpÚ  ’¿ÝjÚ  óÏð@¤      >   vendor/danog/madelineproto/src/RPCError/BalanceTooLowError.php[  ’¿Ýj[  8ËL!¤      J   vendor/danog/madelineproto/src/RPCError/ChatSendStickersForbiddenError.phpÉ  ’¿ÝjÉ  ˆŸùì¤      L   vendor/danog/madelineproto/src/RPCError/ButtonUserPrivacyRestrictedError.php  ’¿Ýj  ®Áwä¤      F   vendor/danog/madelineproto/src/RPCError/ScheduleBotNotAllowedError.php®  ’¿Ýj®  ˆ=$x¤      J   vendor/danog/madelineproto/src/RPCError/EncryptionAlreadyDeclinedError.phpÇ  ’¿ÝjÇ  Rƒ•+¤      <   vendor/danog/madelineproto/src/RPCError/DcIdInvalidError.php�  ’¿Ýj�  ”] Ý¤      J   vendor/danog/madelineproto/src/RPCError/QuizCorrectAnswersTooMuchError.php1  ’¿Ýj1  2Z¤      B   vendor/danog/madelineproto/src/RPCError/ChatAdminRequiredError.php¿  ’¿Ýj¿  „ÏÑº¤      B   vendor/danog/madelineproto/src/RPCError/WebpageCurlFailedError.php¿  ’¿Ýj¿  0…ô8¤      :   vendor/danog/madelineproto/src/RPCError/UserIsBotError.phpœ  ’¿Ýjœ  ¥v¬¤      H   vendor/danog/madelineproto/src/RPCError/ChatSendPhotosForbiddenError.php¿  ’¿Ýj¿  !–sí¤      C   vendor/danog/madelineproto/src/RPCError/ImageProcessFailedError.php¦  ’¿Ýj¦  ×é*@¤      8   vendor/danog/madelineproto/src/RPCError/TimeoutError.phpŒ  ’¿ÝjŒ  °öVƒ¤      G   vendor/danog/madelineproto/src/RPCError/PremiumAccountRequiredError.phpÞ  ’¿ÝjÞ  Ø©e¤      D   vendor/danog/madelineproto/src/RPCError/PasswordHashInvalidError.php·  ’¿Ýj·  •¤„#¤      ?   vendor/danog/madelineproto/src/RPCError/ChannelInvalidError.php›  ’¿Ýj›  |É•¤      F   vendor/danog/madelineproto/src/RPCError/SessionPasswordNeededError.phpÁ  ’¿ÝjÁ  �<“ö¤      @   vendor/danog/madelineproto/src/RPCError/UsernameInvalidError.php¤  ’¿Ýj¤  àyL¤      L   vendor/danog/madelineproto/src/RPCError/ChannelMonoforumUnsupportedError.php9  ’¿Ýj9  Sî¦¯¤      E   vendor/danog/madelineproto/src/RPCError/PinnedDialogsTooMuchError.phpŸ  ’¿ÝjŸ  ¯þt¤      >   vendor/danog/madelineproto/src/RPCError/ChatForbiddenError.php¶  ’¿Ýj¶  òHuM¤      D   vendor/danog/madelineproto/src/RPCError/CallAlreadyDeclinedError.php§  ’¿Ýj§  ÃG¤      F   vendor/danog/madelineproto/src/RPCError/ChatSendGifsForbiddenError.phpµ  ’¿Ýjµ  ‰`Òõ¤      M   vendor/danog/madelineproto/src/RPCError/BusinessConnectionNotAllowedError.php|	  ’¿Ýj|	  ,È¤      C   vendor/danog/madelineproto/src/SecretChats/SecretChatController.php+€  ’¿Ýj+€  s­AQ¤      9   vendor/danog/madelineproto/src/SecretChats/SecretChat.php¢  ’¿Ýj¢  a„P]¤      =   vendor/danog/madelineproto/src/SecretChats/AuthKeyHandler.php(  ’¿Ýj(  x=t¤      9   vendor/danog/madelineproto/src/SecretChats/RekeyState.phpe  ’¿Ýje  ä~?Í¤      1   vendor/danog/madelineproto/src/TransportError.php  ’¿Ýj  ÊÛFs¤      4   vendor/danog/madelineproto/src/SecurityException.phpk  ’¿Ýjk  ÏÚWœ¤      /   vendor/danog/madelineproto/src/WebUI/Action.phpæ  ’¿Ýjæ  ÇØ¥¤      \   vendor/danog/madelineproto/src/WebUI/templates/assets/tweb/tgs/TwoFactorSetupMonkeyIdle.jsonß ’¿Ýjß (“¤      \   vendor/danog/madelineproto/src/WebUI/templates/assets/tweb/tgs/TwoFactorSetupMonkeyPeek.jsonÛq ’¿ÝjÛq 6åÆ¤      `   vendor/danog/madelineproto/src/WebUI/templates/assets/tweb/tgs/TwoFactorSetupMonkeyTracking.jsong: ’¿Ýjg: üÀã¤      .   vendor/danog/madelineproto/src/WebUI/Field.php©  ’¿Ýj©  ”×‰¤      7   vendor/danog/madelineproto/src/WebUI/TemplateEngine.php·  ’¿Ýj·  ö½ä¸¤      .   vendor/danog/madelineproto/src/WebUI/WebUI.php[-  ’¿Ýj[-  ^U²¤      1   vendor/danog/madelineproto/src/WebUI/SafeHtml.phpp	  ’¿Ýjp	  ^xßé¤      3   vendor/danog/madelineproto/src/WebUI/AlertLevel.php‘  ’¿Ýj‘  òsM¤      -   vendor/danog/madelineproto/src/WebUI/Page.phpŽ  ’¿ÝjŽ  Qqþ]¤      .   vendor/danog/madelineproto/src/WebUI/Alert.php¸  ’¿Ýj¸  à?¤      2   vendor/danog/madelineproto/src/WebUI/FieldType.php|  ’¿Ýj|  éŒu¤      -   vendor/danog/madelineproto/src/WebUI/Step.phpÂ  ’¿ÝjÂ  ¼õ¶¤      1   vendor/danog/madelineproto/src/WebUI/Template.phpF   ’¿ÝjF   Þ."�¤      +   vendor/danog/madelineproto/src/Settings.phpa3  ’¿Ýja3  ¤¶MØ¤      6   vendor/danog/madelineproto/src/polyfill_phpseclib3.phpt  ’¿Ýjt  VµóÁ¤      G   vendor/danog/madelineproto/src/danog/MadelineProto/Ipc/Runner/entry.phpq  ’¿Ýjq  ;›y¤      2   vendor/danog/madelineproto/src/Wrappers/Button.phpø  ’¿Ýjø  ®K¨§¤      9   vendor/danog/madelineproto/src/Wrappers/DialogHandler.phpÒ%  ’¿ÝjÒ%  cè7¢¤      0   vendor/danog/madelineproto/src/Wrappers/Loop.phpT  ’¿ÝjT  Œ¶ô¤      /   vendor/danog/madelineproto/src/Wrappers/Ads.phpÖ
  ’¿ÝjÖ
  Íª7î¤      1   vendor/danog/madelineproto/src/Wrappers/Start.php>  ’¿Ýj>  ž_¤      1   vendor/danog/madelineproto/src/Wrappers/Login.php:?  ’¿Ýj:?  ]mjí¤      2   vendor/danog/madelineproto/src/Wrappers/Events.php‡  ’¿Ýj‡  Å‰—Q¤      <   vendor/danog/madelineproto/src/MTProtoTools/UpdatesState.php¨  ’¿Ýj¨  újè¤      ;   vendor/danog/madelineproto/src/MTProtoTools/CallHandler.php
  ’¿Ýj
  Y¹uA¤      @   vendor/danog/madelineproto/src/MTProtoTools/TransferProgress.phpx	  ’¿Ýjx	  ›ùõ<¤      @   vendor/danog/madelineproto/src/MTProtoTools/FilesAbstraction.php-© ’¿Ýj-© Fï$e¤      <   vendor/danog/madelineproto/src/MTProtoTools/PeerDatabase.phpe  ’¿Ýje  ²~ü>¤      >   vendor/danog/madelineproto/src/MTProtoTools/ResumableCalls.phpz5  ’¿Ýjz5  æ/¿E¤      A   vendor/danog/madelineproto/src/MTProtoTools/ReferenceDatabase.phpòO  ’¿ÝjòO  ðÎé¬¤      :   vendor/danog/madelineproto/src/MTProtoTools/FileServer.php'  ’¿Ýj'  §ˆþ¤      9   vendor/danog/madelineproto/src/MTProtoTools/Crypt/IGE.phpa  ’¿Ýja  fdí¤      @   vendor/danog/madelineproto/src/MTProtoTools/Crypt/IGEOpenssl.php)
  ’¿Ýj)
  Ã>°¤      B   vendor/danog/madelineproto/src/MTProtoTools/Crypt/IGEPhpseclib.phpŽ	  ’¿ÝjŽ	  †ü}¤      <   vendor/danog/madelineproto/src/MTProtoTools/Crypt/AesCtr.php  ’¿Ýj  „múü¤      5   vendor/danog/madelineproto/src/MTProtoTools/Crypt.php¯$  ’¿Ýj¯$  •N!Ø¤      8   vendor/danog/madelineproto/src/MTProtoTools/DialogId.php¹   ’¿Ýj¹   j„³ì¤      ;   vendor/danog/madelineproto/src/MTProtoTools/MinDatabase.phpñ$  ’¿Ýjñ$  Bƒo¤      D   vendor/danog/madelineproto/src/MTProtoTools/CombinedUpdatesState.phpÞ
  ’¿ÝjÞ
  þd0“¤      H   vendor/danog/madelineproto/src/MTProtoTools/FileRef/FileRefTraversal.php&C  ’¿Ýj&C  ´´­»¤      B   vendor/danog/madelineproto/src/MTProtoTools/FileRef/FileRefMap.phpÑ?  ’¿ÝjÑ?  xÀ¤      J   vendor/danog/madelineproto/src/MTProtoTools/FileRef/FileRefEnvironment.php_  ’¿Ýj_  +žÖ¤      I   vendor/danog/madelineproto/src/MTProtoTools/FileRef/FileRefMethodCall.php  ’¿Ýj  «Ãæö¤      E   vendor/danog/madelineproto/src/MTProtoTools/FileRef/FileRefMarker.phpr  ’¿Ýjr  â±ðÉ¤      :   vendor/danog/madelineproto/src/MTProtoTools/FilesLogic.phpYk  ’¿ÝjYk  jùV=¤      ;   vendor/danog/madelineproto/src/MTProtoTools/PeerHandler.phpÀ¨  ’¿ÝjÀ¨  Ç©E•¤      B   vendor/danog/madelineproto/src/MTProtoTools/PasswordCalculator.php’*  ’¿Ýj’*  h’y¤      >   vendor/danog/madelineproto/src/MTProtoTools/AuthKeyHandler.phpª  ’¿Ýjª  -o‚¬¤      <   vendor/danog/madelineproto/src/MTProtoTools/ResponseInfo.php(  ’¿Ýj(  Í½£p¤      =   vendor/danog/madelineproto/src/MTProtoTools/UpdateHandler.php ’¿Ýj ¦rQ¤      5   vendor/danog/madelineproto/src/MTProtoTools/Files.phpÝ6 ’¿ÝjÝ6 ú×�¤      -   vendor/danog/madelineproto/src/AsyncTools.phpy1  ’¿Ýjy1  5nÄì¤      0   vendor/danog/madelineproto/src/WrappedFuture.php  ’¿Ýj  Õš)`¤      /   vendor/danog/madelineproto/src/SessionPaths.phpÊ0  ’¿ÝjÊ0  r>»¤      (   vendor/danog/madelineproto/src/empty.wavN-  ’¿ÝjN-  è«+Ú¤      8   vendor/danog/madelineproto/src/TL_file_ref_map_schema.tl¿  ’¿Ýj¿  ÏuÖµ¤      1   vendor/danog/madelineproto/src/LocalDirectory.phpà  ’¿Ýjà  Š'W2¤      +   vendor/danog/madelineproto/src/TL_secret.tlË  ’¿ÝjË  TÃb ¤      =   vendor/danog/madelineproto/src/SecretPeerNotInDbException.phpð  ’¿Ýjð  _›Á¤      5   vendor/danog/madelineproto/src/PluginEventHandler.phpí  ’¿Ýjí  Üsªz¤      ,   vendor/danog/madelineproto/src/LocalFile.php½  ’¿Ýj½  �=k"¤      &   vendor/danog/madelineproto/src/Mp4.phpS‡  ’¿ÝjS‡  LR4¤      3   vendor/danog/madelineproto/src/MediaDestination.phpÑ  ’¿ÝjÑ  ^>�*¤      8   vendor/danog/madelineproto/src/FileCallbackInterface.phpã  ’¿Ýjã  îr§P¤      1   vendor/danog/madelineproto/src/Db/CachedArray.php  ’¿Ýj  œTÎí¤      1   vendor/danog/madelineproto/src/Db/MemoryArray.phpZ  ’¿ÝjZ  ?M:I¤      /   vendor/danog/madelineproto/src/TL_mtproto_v1.tld  ’¿Ýjd  è(þÐ¤      .   vendor/danog/madelineproto/src/AbstractAPI.phpñ  ’¿Ýjñ  �Ã{é¤      ,   vendor/danog/madelineproto/src/OggWriter.php8  ’¿Ýj8  ™Ö¤      /   vendor/danog/madelineproto/src/DoHConnector.php   ’¿Ýj   -®£á¤      6   vendor/danog/madelineproto/src/Tgcalls/H264Framing.phpÊ  ’¿ÝjÊ   ?µ¤      @   vendor/danog/madelineproto/src/Tgcalls/SctpSignalingObserver.phpë  ’¿Ýjë  ôp±z¤      9   vendor/danog/madelineproto/src/Tgcalls/RtcDebugLogger.phph  ’¿Ýjh  Ù®B¤      B   vendor/danog/madelineproto/src/Tgcalls/ScreencastCodecObserver.phpÚ  ’¿ÝjÚ  V:Nð¤      3   vendor/danog/madelineproto/src/Tgcalls/GroupSdp.phpþs  ’¿Ýjþs  #	¼Ù¤      A   vendor/danog/madelineproto/src/Tgcalls/SignalingSctpTransport.phpÎ  ’¿ÝjÎ  8%¤      @   vendor/danog/madelineproto/src/Tgcalls/CallStreamsDispatcher.phpü
  ’¿Ýjü
  Qc�T¤      :   vendor/danog/madelineproto/src/Tgcalls/GroupMediaTrait.php"o  ’¿Ýj"o  �¯@¤      0   vendor/danog/madelineproto/src/Tgcalls/V2Sdp.php§h  ’¿Ýj§h  %ø2{¤      B   vendor/danog/madelineproto/src/Tgcalls/CallControllerInterface.php¹  ’¿Ýj¹  n™ý¤      <   vendor/danog/madelineproto/src/Tgcalls/OpusPlaybackTrack.phpË8  ’¿ÝjË8  aâ~”¤      7   vendor/danog/madelineproto/src/Tgcalls/CallRecorder.php—¬  ’¿Ýj—¬  œ£'¤      :   vendor/danog/madelineproto/src/Tgcalls/GroupConnection.php%š  ’¿Ýj%š  (â¨ß¤      5   vendor/danog/madelineproto/src/Tgcalls/Controller.phpèG ’¿ÝjèG Á·øA¤      4   vendor/danog/madelineproto/src/Tgcalls/BitReader.php×  ’¿Ýj×  ýëLŠ¤      8   vendor/danog/madelineproto/src/Tgcalls/HevcBitstream.phpÅ'  ’¿ÝjÅ'  Ë-ÔÂ¤      @   vendor/danog/madelineproto/src/Tgcalls/PrivateCallController.phpŸ�  ’¿ÝjŸ�  
%ºª¤      >   vendor/danog/madelineproto/src/Tgcalls/EncryptedConnection.php2H  ’¿Ýj2H   ,�b¤      ;   vendor/danog/madelineproto/src/Tgcalls/SignalingIceRole.php¨  ’¿Ýj¨  ýèõ|¤      C   vendor/danog/madelineproto/src/Tgcalls/SignalingServiceObserver.phps  ’¿Ýjs  ÄÀî3¤      <   vendor/danog/madelineproto/src/Tgcalls/RecordingObserver.phpD  ’¿ÝjD  _íDÌ¤      7   vendor/danog/madelineproto/src/Tgcalls/OpusRecorder.php¾  ’¿Ýj¾  ¥êÇ¤      ;   vendor/danog/madelineproto/src/Tgcalls/LegacyController.phpøD  ’¿ÝjøD  h›Ž{¤      8   vendor/danog/madelineproto/src/Tgcalls/IncomingMedia.phpJP  ’¿ÝjJP  ºO¢\¤      =   vendor/danog/madelineproto/src/Tgcalls/VideoPlaybackTrack.phpj4  ’¿Ýjj4  n%Â.¤      =   vendor/danog/madelineproto/src/Tgcalls/VideoCodecObserver.phpD	  ’¿ÝjD	  ¯oÿé¤      7   vendor/danog/madelineproto/src/Tgcalls/TgcallsTools.phpÎ
  ’¿ÝjÎ
  zÌ{¤¤      7   vendor/danog/madelineproto/src/Tgcalls/Av1Bitstream.phpn+  ’¿Ýjn+  !Õ€g¤      ?   vendor/danog/madelineproto/src/Tgcalls/GroupConnectionOwner.phpû  ’¿Ýjû  =…5„¤      @   vendor/danog/madelineproto/src/Tgcalls/IncomingMediaObserver.phpÆ  ’¿ÝjÆ  ¾LÔå¤      ;   vendor/danog/madelineproto/src/Tgcalls/E2E/FrameCryptor.phpö/  ’¿Ýjö/  ·>p¤      @   vendor/danog/madelineproto/src/Tgcalls/E2E/FrameCryptorState.php¬  ’¿Ýj¬  ðƒ«r¤      <   vendor/danog/madelineproto/src/Tgcalls/E2E/e2e_conference.tl›  ’¿Ýj›  5ÊD¤      9   vendor/danog/madelineproto/src/Tgcalls/E2E/BlockCodec.php6  ’¿Ýj6  ¬RÆô¤      ;   vendor/danog/madelineproto/src/Tgcalls/E2E/Verification.phpn  ’¿Ýjn  ¦„é¤      =   vendor/danog/madelineproto/src/Tgcalls/E2E/E2EKeyProvider.php´  ’¿Ýj´  ~e"Ò¤      5   vendor/danog/madelineproto/src/Tgcalls/E2E/Crypto.phpMR  ’¿ÝjMR  «ÇP¤      >   vendor/danog/madelineproto/src/Tgcalls/E2E/ConferenceChain.phpãD  ’¿ÝjãD  -ÔKñ¤      9   vendor/danog/madelineproto/src/Tgcalls/E2E/CallPacket.phpW  ’¿ÝjW  |Ð°ô¤      =   vendor/danog/madelineproto/src/Tgcalls/E2E/ConferenceCall.phpQû  ’¿ÝjQû  .üš�¤      &   vendor/danog/madelineproto/src/Ogg.php(]  ’¿Ýj(]  ä£a„¤      0   vendor/danog/madelineproto/src/file_ref_map.jsonQS ’¿ÝjQS tÙÀõ¤      /   vendor/danog/madelineproto/src/EventHandler.phpÊV  ’¿ÝjÊV  œ¤      @   vendor/danog/madelineproto/src/VoIP/SignalingProtocolVersion.phpF  ’¿ÝjF  ý°Á­¤      1   vendor/danog/madelineproto/src/VoIP/VoIPState.phpÐ  ’¿ÝjÐ  @áÜ ¤      1   vendor/danog/madelineproto/src/VoIP/CallState.php?  ’¿Ýj?  1Òló¤      6   vendor/danog/madelineproto/src/VoIP/MessageHandler.php%/  ’¿Ýj%/  Æ>ã™¤      0   vendor/danog/madelineproto/src/VoIP/Endpoint.phpOT  ’¿ÝjOT  òE¡¤      6   vendor/danog/madelineproto/src/VoIP/AuthKeyHandler.phpÄB  ’¿ÝjÄB  KÀÚ ¤      5   vendor/danog/madelineproto/src/VoIP/DiscardReason.phpº  ’¿Ýjº  Îy7¤      2   vendor/danog/madelineproto/src/VoIP/MediaState.php
  ’¿Ýj
  Eoÿ¤      <   vendor/danog/madelineproto/src/MTProto/SpecialMethodType.php}  ’¿Ýj}  /­åV¤      9   vendor/danog/madelineproto/src/MTProto/MTProtoMessage.phpÙ  ’¿ÝjÙ  G=°¤      4   vendor/danog/madelineproto/src/MTProto/Container.phpW  ’¿ÝjW  ÀôÈØ¤      5   vendor/danog/madelineproto/src/MTProto/LoginState.phpü	  ’¿Ýjü	  B,xß¤      2   vendor/danog/madelineproto/src/MTProto/AuthKey.php  ’¿Ýj  ‹àÚ¤      A   vendor/danog/madelineproto/src/MTProto/MTProtoOutgoingMessage.php2D  ’¿Ýj2D  .¦—"¤      5   vendor/danog/madelineproto/src/MTProto/LinkedList.php}
  ’¿Ýj}
  ×n=¤      6   vendor/danog/madelineproto/src/MTProto/TempAuthKey.phpc  ’¿Ýjc  ŽÓn^¤      6   vendor/danog/madelineproto/src/MTProto/PermAuthKey.phpà	  ’¿Ýjà	  i®÷9¤      5   vendor/danog/madelineproto/src/MTProto/NewAuthKey.phpÇ  ’¿ÝjÇ  É¤¤      :   vendor/danog/madelineproto/src/MTProto/ConnectionState.php  ’¿Ýj  ¸Ì0Ã¤      A   vendor/danog/madelineproto/src/MTProto/MTProtoIncomingMessage.phpÀ  ’¿ÝjÀ  ²ÕÜD¤      1   vendor/danog/madelineproto/src/MatroskaWriter.phpQM  ’¿ÝjQM  `M¤      4   vendor/danog/madelineproto/src/ResponseException.phpª  ’¿Ýjª  ÝH<¤      3   vendor/danog/madelineproto/src/StreamDuplicator.phpg  ’¿Ýjg  � ©â¤      2   vendor/danog/madelineproto/src/ResumableStream.php¸  ’¿Ýj¸  Okö¤      4   vendor/danog/madelineproto/src/EventHandlerIssue.php
  ’¿Ýj
  P,6K¤      *   vendor/danog/madelineproto/src/MTProto.phpD ’¿ÝjD mÅ]¤      ,   vendor/danog/madelineproto/src/StreamEof.phpW  ’¿ÝjW  ü¸Ì}¤      4   vendor/danog/madelineproto/src/RPCErrorException.php&Ï ’¿Ýj&Ï ¨~ø	¤      &   vendor/danog/madelineproto/src/RSA.php  ’¿Ýj  r$r0¤      5   vendor/danog/madelineproto/src/SimpleEventHandler.php·  ’¿Ýj·  íažŠ¤      6   vendor/danog/madelineproto/src/Broadcast/Broadcast.phpâ  ’¿Ýjâ  ÿ½À¬¤      3   vendor/danog/madelineproto/src/Broadcast/Action.phpÏ  ’¿ÝjÏ  €¹ä¤      ;   vendor/danog/madelineproto/src/Broadcast/StatusInternal.phpÐ  ’¿ÝjÐ  b`%®¤      A   vendor/danog/madelineproto/src/Broadcast/Action/ActionForward.php  ’¿Ýj  XÓùR¤      >   vendor/danog/madelineproto/src/Broadcast/Action/ActionSend.php  ’¿Ýj  !£9"¤      :   vendor/danog/madelineproto/src/Broadcast/InternalState.php%  ’¿Ýj%  °3¤      3   vendor/danog/madelineproto/src/Broadcast/Filter.php   ’¿Ýj   »cÊS¤      3   vendor/danog/madelineproto/src/Broadcast/Status.phpÚ  ’¿ÝjÚ  3 1™¤      H   vendor/danog/madelineproto/src/Broadcast/BroadcastCancelledException.phpÚ  ’¿ÝjÚ  ‰Þ7Á¤      5   vendor/danog/madelineproto/src/Broadcast/Progress.phpA	  ’¿ÝjA	  ÆI«Ö¤      (   vendor/danog/madelineproto/src/Tools.phpšŽ  ’¿ÝjšŽ  ‹kÝ�¤      /   vendor/danog/madelineproto/src/PTSException.php5  ’¿Ýj5  9UŸø¤      ,   vendor/danog/madelineproto/src/ParseMode.phpx  ’¿Ýjx   —¶º¤      -   vendor/danog/madelineproto/src/Conversion.phpòJ  ’¿ÝjòJ  c}ÿ†¤      =   vendor/danog/madelineproto/src/RestoresDeclaredProperties.php/  ’¿Ýj/  Þ¼‹1¤      .   vendor/danog/madelineproto/src/InternalDoc.phpC� ’¿ÝjC� ”NÂ¤      &   vendor/danog/madelineproto/src/v3.jsonï ’¿Ýjï ÂBÎÙ¤      /   vendor/danog/madelineproto/src/FileRedirect.phpU  ’¿ÝjU  Å�Y8¤      3   vendor/danog/madelineproto/src/VoIPServerConfig.php
  ’¿Ýj
  	­. ¤      /   vendor/danog/madelineproto/src/FileCallback.phpÐ  ’¿ÝjÐ  Rj!�¤      >   vendor/danog/madelineproto/src/NothingInTheSocketException.phpX  ’¿ÝjX  7Ö…¤      ,   vendor/danog/madelineproto/src/Exception.phpº  ’¿Ýjº  ¦ÎnX¤      /   vendor/danog/madelineproto/src/LoggerGetter.php‚  ’¿Ýj‚  )AÁ£¤      :   vendor/danog/madelineproto/src/CallNotAllowedException.php;  ’¿Ýj;  g�HW¤      2   vendor/danog/madelineproto/src/TL_telegram_v232.tl2} ’¿Ýj2} Å‘J¼¤      7   vendor/danog/madelineproto/src/DataCenterConnection.phpµ]  ’¿Ýjµ]  ŒµÑ�¤      /   vendor/danog/madelineproto/src/BotApiFileId.phpŒ  ’¿ÝjŒ  Ÿq,%¤      ,   vendor/danog/madelineproto/src/PsrLogger.phps  ’¿Ýjs  ØëI¤      1   vendor/danog/madelineproto/src/LegacyMigrator.php&  ’¿Ýj&  yŽÈ¤      &   vendor/danog/madelineproto/src/API.phpNJ  ’¿ÝjNJ  eu¤      /   vendor/danog/madelineproto/src/TL/TLMethods.phpw  ’¿Ýjw  º§t¤      4   vendor/danog/madelineproto/src/TL/SecretTLParser.phpº ’¿Ýjº p&yâ¤      <   vendor/danog/madelineproto/src/TL/Conversion/BotAPIFiles.php
-  ’¿Ýj
-  µ^šX¤      7   vendor/danog/madelineproto/src/TL/Conversion/BotAPI.phpàm  ’¿Ýjàm  )›“—¤      3   vendor/danog/madelineproto/src/TL/Conversion/TD.php1!  ’¿Ýj1!  Ó
àð¤      :   vendor/danog/madelineproto/src/TL/Conversion/Exception.php(  ’¿Ýj(  r-"¤      :   vendor/danog/madelineproto/src/TL/Conversion/Extension.php"  ’¿Ýj"  ê$jœ¤      (   vendor/danog/madelineproto/src/TL/TL.phpVÇ  ’¿ÝjVÇ  š‘�7¤      1   vendor/danog/madelineproto/src/TL/TLInterface.php"  ’¿Ýj"  /£úÜ¤      .   vendor/danog/madelineproto/src/TL/TLParser.php• ’¿Ýj• #JÈ¯¤      4   vendor/danog/madelineproto/src/TL/TLConstructors.phpÑ  ’¿ÝjÑ  ¾;å¡¤      2   vendor/danog/madelineproto/src/TL/Types/Button.php'  ’¿Ýj'  7\P¦¤      1   vendor/danog/madelineproto/src/TL/Types/Bytes.php!  ’¿Ýj!  Róß¤      7   vendor/danog/madelineproto/src/TL/Types/LoginQrCode.php×  ’¿Ýj×  Pis¤      /   vendor/danog/madelineproto/src/TL/Exception.phpö  ’¿Ýjö  Í5�£¤      .   vendor/danog/madelineproto/src/TL/TLParams.php'  ’¿Ýj'  ö¦êÄ¤      5   vendor/danog/madelineproto/src/TL/PrettyException.phpç  ’¿Ýjç  sÌw’¤      0   vendor/danog/madelineproto/src/TL/TLCallback.php  ’¿Ýj  ×š¤      4   vendor/danog/madelineproto/src/Loop/InternalLoop.phpi  ’¿Ýji  ¯ýô·¤      D   vendor/danog/madelineproto/src/Loop/Generic/PeriodicLoopInternal.phpÛ  ’¿ÝjÛ  ×’Rx¤      0   vendor/danog/madelineproto/src/Loop/VoIPLoop.phph	  ’¿Ýjh	  Ç<8Ò¤      2   vendor/danog/madelineproto/src/Loop/LoggerLoop.php  ’¿Ýj  ç²×t¤      9   vendor/danog/madelineproto/src/Loop/Update/UpdateLoop.php±0  ’¿Ýj±0  ®x§¤      7   vendor/danog/madelineproto/src/Loop/Update/FeedLoop.php[-  ’¿Ýj[-  `¢žý¤      6   vendor/danog/madelineproto/src/Loop/Update/SeqLoop.php'  ’¿Ýj'  w8Ý¤      >   vendor/danog/madelineproto/src/Loop/Connection/CleanupLoop.php  ’¿Ýj  nÇ�¤      <   vendor/danog/madelineproto/src/Loop/Connection/WriteLoop.phpS  ’¿ÝjS  í£ ž¤      ;   vendor/danog/madelineproto/src/Loop/Connection/ReadLoop.phpA@  ’¿ÝjA@  q¶
8¤      9   vendor/danog/madelineproto/src/Loop/Connection/Common.php·  ’¿Ýj·  '¿QÔ¤      ;   vendor/danog/madelineproto/src/Loop/Connection/PingLoop.php©  ’¿Ýj©  ÛÈt˜¤      /   vendor/danog/madelineproto/src/Loop/APILoop.phpj  ’¿Ýjj  CÆt+¤      ;   vendor/danog/madelineproto/src/Loop/VoIP/StreamSnapshot.phpN
  ’¿ÝjN
  ["wL¤      3   vendor/danog/madelineproto/src/Loop/VoIP/DjLoop.phpóð  ’¿Ýjóð  ú˜OÌ¤      =   vendor/danog/madelineproto/src/Loop/Secret/SecretFeedLoop.phpv  ’¿Ýjv  /û `¤      '   vendor/danog/madelineproto/src/Lang.phpNø	 ’¿ÝjNø	 iÏæn¤      2   vendor/danog/madelineproto/src/RecordingFormat.phpž  ’¿Ýjž  ¶GÙ˜¤      -   vendor/danog/madelineproto/src/DataCenter.phpgK  ’¿ÝjgK  ÿúS¤      )   vendor/danog/madelineproto/src/Snitch.php�	  ’¿Ýj�	  ´´ŒŠ¤      ,   vendor/danog/madelineproto/src/RemoteUrl.php¼  ’¿Ýj¼  ¯¬<ž¤      /   vendor/danog/madelineproto/src/TextEntities.php™	  ’¿Ýj™	  f=O¤      (   vendor/danog/madelineproto/src/Magic.php¹S  ’¿Ýj¹S  s<¾¤      3   vendor/danog/madelineproto/src/Settings/Metrics.php)  ’¿Ýj)  ÚXÞa¤      6   vendor/danog/madelineproto/src/Settings/Connection.phpF  ’¿ÝjF  0AD¤      9   vendor/danog/madelineproto/src/Settings/Serialization.phpV  ’¿ÝjV  3Qp¤      5   vendor/danog/madelineproto/src/Settings/Templates.phpì  ’¿Ýjì  7Z´|¤      3   vendor/danog/madelineproto/src/Settings/AppInfo.phpâ   ’¿Ýjâ   EWç¤      7   vendor/danog/madelineproto/src/Settings/SecretChats.phpj  ’¿Ýjj  ˜õËæ¤      0   vendor/danog/madelineproto/src/Settings/Auth.phpw  ’¿Ýjw  - Â ¤      /   vendor/danog/madelineproto/src/Settings/RPC.php±  ’¿Ýj±  UX¤      /   vendor/danog/madelineproto/src/Settings/Ipc.phpŒ  ’¿ÝjŒ  =DÆ¤      @   vendor/danog/madelineproto/src/Settings/Database/SqlAbstract.php   ’¿Ýj   Zæá¤      K   vendor/danog/madelineproto/src/Settings/Database/DriverDatabaseAbstract.php  ’¿Ýj  ·£ó¤      =   vendor/danog/madelineproto/src/Settings/Database/Postgres.php0	  ’¿Ýj0	   ¢KÌ¤      :   vendor/danog/madelineproto/src/Settings/Database/Redis.php‰
  ’¿Ýj‰
  :U¿¤      ;   vendor/danog/madelineproto/src/Settings/Database/Memory.phpx  ’¿Ýjx  ¶h¤      :   vendor/danog/madelineproto/src/Settings/Database/Mysql.phpe  ’¿Ýje  qßxÊ¤      C   vendor/danog/madelineproto/src/Settings/Database/SerializerType.phpS  ’¿ÝjS  ÔÐ¿¬¤      0   vendor/danog/madelineproto/src/Settings/VoIP.php(	  ’¿Ýj(	  Úq}ÿ¤      4   vendor/danog/madelineproto/src/Settings/TLSchema.phpƒ  ’¿Ýjƒ  /ú¢¤      <   vendor/danog/madelineproto/src/Settings/DatabaseAbstract.php°  ’¿Ýj°  Î�‘e¤      /   vendor/danog/madelineproto/src/Settings/Pwr.php…  ’¿Ýj…  /"ê‰¤      0   vendor/danog/madelineproto/src/Settings/Peer.php©  ’¿Ýj©  kM½w¤      2   vendor/danog/madelineproto/src/Settings/Logger.php  ’¿Ýj  gÁp`¤      1   vendor/danog/madelineproto/src/Settings/Files.php)  ’¿Ýj)  ÑX¤      +   vendor/danog/madelineproto/src/Matroska.php�G  ’¿Ýj�G  	æ¤      1   vendor/danog/madelineproto/src/SettingsGetter.phpi  ’¿Ýji  õ·Ð]¤      @   vendor/danog/madelineproto/src/ReferencedTimeoutCancellation.phpÌ
  ’¿ÝjÌ
  Y¤      <   vendor/danog/madelineproto/src/Stream/RawStreamInterface.phpõ  ’¿Ýjõ  “cÚ¤      9   vendor/danog/madelineproto/src/Stream/StreamInterface.phpc  ’¿Ýjc  L Oo¤      @   vendor/danog/madelineproto/src/Stream/MTProtoBufferInterface.php¾  ’¿Ýj¾  [Ô[X¤      9   vendor/danog/madelineproto/src/Stream/BufferInterface.php®  ’¿Ýj®  é4`=¤      >   vendor/danog/madelineproto/src/Stream/ProxyStreamInterface.php>  ’¿Ýj>  ÷bk¤      >   vendor/danog/madelineproto/src/Stream/WriteBufferInterface.php3  ’¿Ýj3  Ýhä¤      9   vendor/danog/madelineproto/src/Stream/ContextIterator.php€  ’¿Ýj€  ˜ÚÂÌ¤      A   vendor/danog/madelineproto/src/Stream/BufferedStreamInterface.php°  ’¿Ýj°  Y&É¤      =   vendor/danog/madelineproto/src/Stream/ReadBufferInterface.phps  ’¿Ýjs  |­‘¾¤      S   vendor/danog/madelineproto/src/Stream/MTProtoTransport/IntermediatePaddedStream.php  ’¿Ýj  Ì¦¤      F   vendor/danog/madelineproto/src/Stream/MTProtoTransport/HttpsStream.php«  ’¿Ýj«   øø›¤      M   vendor/danog/madelineproto/src/Stream/MTProtoTransport/IntermediateStream.php„  ’¿Ýj„  ¤ßî¸¤      E   vendor/danog/madelineproto/src/Stream/MTProtoTransport/HttpStream.php#  ’¿Ýj#  ðï"¤      H   vendor/danog/madelineproto/src/Stream/MTProtoTransport/FakeTlsStream.phpP9  ’¿ÝjP9  ‘jºø¤      I   vendor/danog/madelineproto/src/Stream/MTProtoTransport/AbridgedStream.phpO  ’¿ÝjO  0t<¤      E   vendor/danog/madelineproto/src/Stream/MTProtoTransport/FullStream.php  ’¿Ýj  ê°àò¤      K   vendor/danog/madelineproto/src/Stream/MTProtoTransport/ObfuscatedStream.php�  ’¿Ýj�  6Fñ
¤      ;   vendor/danog/madelineproto/src/Stream/ConnectionContext.php"  ’¿Ýj"  á â¤      E   vendor/danog/madelineproto/src/Stream/Common/HashedBufferedStream.phpÀ"  ’¿ÝjÀ"  í­¬¤      C   vendor/danog/madelineproto/src/Stream/Common/FileBufferedStream.php¦  ’¿Ýj¦  öjVY¤      H   vendor/danog/madelineproto/src/Stream/Common/SimpleBufferedRawStream.php’
  ’¿Ýj’
  ±¤      :   vendor/danog/madelineproto/src/Stream/Common/CtrStream.phpó  ’¿Ýjó  Dnû¤      B   vendor/danog/madelineproto/src/Stream/Common/BufferedRawStream.php^  ’¿Ýj^  Ô‘:½¤      B   vendor/danog/madelineproto/src/Stream/Common/UdpBufferedStream.php	  ’¿Ýj	  ©»ÄS¤      :   vendor/danog/madelineproto/src/Stream/Proxy/SocksProxy.php³  ’¿Ýj³  �éPl¤      9   vendor/danog/madelineproto/src/Stream/Proxy/HttpProxy.phpÎ  ’¿ÝjÎ  Y¸Ê¤      A   vendor/danog/madelineproto/src/Stream/RawProxyStreamInterface.php  ’¿Ýj  t¤6›¤      =   vendor/danog/madelineproto/src/Stream/Transport/WssStream.phpm  ’¿Ýjm  �bæ¤      <   vendor/danog/madelineproto/src/Stream/Transport/WsStream.php5  ’¿Ýj5  BÄ«.¤      A   vendor/danog/madelineproto/src/Stream/Transport/DefaultStream.phpÚ  ’¿ÝjÚ  |0W¤      A   vendor/danog/madelineproto/src/Stream/Transport/PremadeStream.php€  ’¿Ýj€  ¨$VT¤      F   vendor/danog/madelineproto/src/Stream/BufferedProxyStreamInterface.php  ’¿Ýj  qM4¤      B   vendor/danog/madelineproto/src/Stream/ADNLTransport/ADNLStream.php&  ’¿Ýj&  Äñ—ë¤      1   vendor/danog/madelineproto/src/Reactive/Actor.php9  ’¿Ýj9  Ú ,¤      ?   vendor/danog/madelineproto/src/Reactive/EphemeralSubscriber.php2  ’¿Ýj2  ©¹{2¤      7   vendor/danog/madelineproto/src/Reactive/AsyncWaiter.phpX  ’¿ÝjX  ‰Ã~P¤      C   vendor/danog/madelineproto/src/Reactive/SimpleSubscriberAdaptor.phpM  ’¿ÝjM  �èk¤      6   vendor/danog/madelineproto/src/Reactive/Subscriber.phpw  ’¿Ýjw  ·È·Ö¤      5   vendor/danog/madelineproto/src/Reactive/Publisher.phpî  ’¿Ýjî  ½€Áë¤      <   vendor/danog/madelineproto/src/Reactive/SimpleSubscriber.phpì  ’¿Ýjì  g—¤      :   vendor/danog/madelineproto/src/Reactive/BaseSubscriber.php?  ’¿Ýj?  èÊ%*¤      4   vendor/danog/madelineproto/src/ApiWrappers/Start.php7@  ’¿Ýj7@  ;¿øÂ¤      )   vendor/danog/madelineproto/src/Logger.phpA  ’¿ÝjA  €s¤      +   vendor/danog/madelineproto/src/StrTools.php  ’¿Ýj  Ôw¤D¤      3   vendor/danog/madelineproto/src/ContextConnector.php%
  ’¿Ýj%
  Ï©ƒf¤      =   vendor/danog/madelineproto/src/MTProtoSession/CallHandler.php¨'  ’¿Ýj¨'  Ø)Äò¤      >   vendor/danog/madelineproto/src/MTProtoSession/MsgIdHandler.phpÞ  ’¿ÝjÞ  T+�¾¤      9   vendor/danog/madelineproto/src/MTProtoSession/Session.php¯   ’¿Ýj¯   XÖA¤      :   vendor/danog/madelineproto/src/MTProtoSession/Reliable.phpn  ’¿Ýjn  %üá9¤      >   vendor/danog/madelineproto/src/MTProtoSession/SeqNoHandler.phpb	  ’¿Ýjb	  Ë<¤      @   vendor/danog/madelineproto/src/MTProtoSession/AuthKeyHandler.php÷`  ’¿Ýj÷`  nÏ7Õ¤      A   vendor/danog/madelineproto/src/MTProtoSession/ResponseHandler.php¾U  ’¿Ýj¾U  Ç4ý¤      /   vendor/danog/madelineproto/src/file_ref_map.datlú  ’¿Ýjlú  ãÐû¯¤      0   vendor/danog/madelineproto/src/SettingsEmpty.php?  ’¿Ýj?  ™º`8¤      :   vendor/danog/madelineproto/src/TL_file_ref_map_schema.json§J  ’¿Ýj§J  ¾~zl¤      O   vendor/danog/madelineproto/src/EventHandler/Calls/ConferenceCallParticipant.phpü  ’¿Ýjü  y¨þ¤      A   vendor/danog/madelineproto/src/EventHandler/Calls/PrivateCall.php�8  ’¿Ýj�8  ÷ÃÆy¤      G   vendor/danog/madelineproto/src/EventHandler/Calls/AbstractGroupCall.php¦U  ’¿Ýj¦U  $˜Ì¤      N   vendor/danog/madelineproto/src/EventHandler/Calls/AbstractGroupCallMessage.phpW
  ’¿ÝjW
  ³e{¤      J   vendor/danog/madelineproto/src/EventHandler/Calls/MultiCallParticipant.phpì  ’¿Ýjì  w’Ý¤      ?   vendor/danog/madelineproto/src/EventHandler/Calls/GroupCall.php*  ’¿Ýj*  	ì,¤      J   vendor/danog/madelineproto/src/EventHandler/Calls/GroupCallParticipant.phpü  ’¿Ýjü  JL>€¤      A   vendor/danog/madelineproto/src/EventHandler/Calls/CallStreams.php™  ’¿Ýj™  ™7 1¤      D   vendor/danog/madelineproto/src/EventHandler/Calls/GroupCallDonor.php•  ’¿Ýj•  w��ï¤      ?   vendor/danog/madelineproto/src/EventHandler/Calls/LiveStory.php:  ’¿Ýj:  v“¤      J   vendor/danog/madelineproto/src/EventHandler/Calls/LiveStoryParticipant.php×  ’¿Ýj×  
¬êg¤      R   vendor/danog/madelineproto/src/EventHandler/Calls/AbstractGroupCallParticipant.php   ’¿Ýj   ìoç¤      D   vendor/danog/madelineproto/src/EventHandler/Calls/GroupCallStars.php†  ’¿Ýj†  Zœ¤      K   vendor/danog/madelineproto/src/EventHandler/Calls/ConferenceCallMessage.phpx  ’¿Ýjx  ËÞò·¤      N   vendor/danog/madelineproto/src/EventHandler/Calls/GroupCallMessagesDeleted.php  ’¿Ýj  !)Æ¤      K   vendor/danog/madelineproto/src/EventHandler/Calls/GroupCallParticipants.phpå
  ’¿Ýjå
  æ]Y¤      F   vendor/danog/madelineproto/src/EventHandler/Calls/LiveStoryMessage.php•
  ’¿Ýj•
  R—û¤      D   vendor/danog/madelineproto/src/EventHandler/Calls/GroupCallState.phpJ  ’¿ÝjJ  ^n.À¤      F   vendor/danog/madelineproto/src/EventHandler/Calls/MultiCallMessage.phpT  ’¿ÝjT  v2´Õ¤      D   vendor/danog/madelineproto/src/EventHandler/Calls/ConferenceCall.phpaR  ’¿ÝjaR  ”,€Š¤      F   vendor/danog/madelineproto/src/EventHandler/Calls/CallDenialReason.phpÃ  ’¿ÝjÃ  d¸Ê¤      F   vendor/danog/madelineproto/src/EventHandler/Calls/GroupCallMessage.phpw  ’¿Ýjw  Îàó†¤      A   vendor/danog/madelineproto/src/EventHandler/Query/ButtonQuery.phpž	  ’¿Ýjž	  °úÐJ¤      A   vendor/danog/madelineproto/src/EventHandler/Query/InlineTrait.phpÇ  ’¿ÝjÇ  ¦—CÖ¤      ?   vendor/danog/madelineproto/src/EventHandler/Query/ChatTrait.phpÏ  ’¿ÝjÏ  ÛÜ=N¤      G   vendor/danog/madelineproto/src/EventHandler/Query/InlineButtonQuery.php–  ’¿Ýj–  Ø)»j¤      E   vendor/danog/madelineproto/src/EventHandler/Query/ChatButtonQuery.php‡  ’¿Ýj‡  Ð¹T•¤      ?   vendor/danog/madelineproto/src/EventHandler/Query/GameQuery.phpƒ  ’¿Ýjƒ  Z9Mà¤      E   vendor/danog/madelineproto/src/EventHandler/Query/InlineGameQuery.phpž  ’¿Ýjž  .r!�¤      C   vendor/danog/madelineproto/src/EventHandler/Query/ChatGameQuery.php�  ’¿Ýj�  F¢{¤      6   vendor/danog/madelineproto/src/EventHandler/Action.php  ’¿Ýj  €³Ôñ¤      =   vendor/danog/madelineproto/src/EventHandler/ForwardedInfo.phpd  ’¿Ýjd  ‘Hmî¤      D   vendor/danog/madelineproto/src/EventHandler/Message/CommentReply.php³  ’¿Ýj³  ¿Îz¤      E   vendor/danog/madelineproto/src/EventHandler/Message/SecretMessage.php•  ’¿Ýj•  ßÂ‘Y¤      [   vendor/danog/madelineproto/src/EventHandler/Message/Service/DialogMemberJoinedByRequest.php  ’¿Ýj  qSMú¤      V   vendor/danog/madelineproto/src/EventHandler/Message/Service/DialogSetChatWallPaper.phpŸ  ’¿ÝjŸ  ’RGQ¤      T   vendor/danog/madelineproto/src/EventHandler/Message/Service/DialogHistoryCleared.phpb  ’¿Ýjb  EÁŸ˜¤      O   vendor/danog/madelineproto/src/EventHandler/Message/Service/DialogGameScore.phpë  ’¿Ýjë  ‚ö©j¤      R   vendor/danog/madelineproto/src/EventHandler/Message/Service/DialogSetChatTheme.php¨  ’¿Ýj¨  �xó^¤      M   vendor/danog/madelineproto/src/EventHandler/Message/Service/DialogCreated.php)  ’¿Ýj)  æåX¤      U   vendor/danog/madelineproto/src/EventHandler/Message/Service/DialogNoForwardToggle.phpž  ’¿Ýjž  í0t¤      R   vendor/danog/madelineproto/src/EventHandler/Message/Service/DialogReadMessages.phpØ  ’¿ÝjØ  9¼ð`¤      V   vendor/danog/madelineproto/src/EventHandler/Message/Service/DialogNoForwardRequest.php  ’¿Ýj  ÀÖè8¤      R   vendor/danog/madelineproto/src/EventHandler/Message/Service/DialogTitleChanged.phpÅ  ’¿ÝjÅ  þ2›	¤      T   vendor/danog/madelineproto/src/EventHandler/Message/Service/DialogDeleteMessages.phpÆ  ’¿ÝjÆ  É ´R¤      Y   vendor/danog/madelineproto/src/EventHandler/Message/Service/DialogSuggestProfilePhoto.php©  ’¿Ýj©  •‚Aâ¤      M   vendor/danog/madelineproto/src/EventHandler/Message/Service/DialogWebView.php¶  ’¿Ýj¶  �_.9¤      Y   vendor/danog/madelineproto/src/EventHandler/Message/Service/DialogGroupCall/GroupCall.phpS  ’¿ÝjS  è?Ú?¤      b   vendor/danog/madelineproto/src/EventHandler/Message/Service/DialogGroupCall/GroupCallScheduled.phpç  ’¿Ýjç  1ØÑB¤      `   vendor/danog/madelineproto/src/EventHandler/Message/Service/DialogGroupCall/GroupCallInvited.phpÙ  ’¿ÝjÙ  ž	\¤      Y   vendor/danog/madelineproto/src/EventHandler/Message/Service/DialogGeoProximityReached.php  ’¿Ýj  ~ÿù¤      L   vendor/danog/madelineproto/src/EventHandler/Message/Service/DialogSetTTL.php/  ’¿Ýj/  %1@
¤      S   vendor/danog/madelineproto/src/EventHandler/Message/Service/DialogMessagePinned.phpz  ’¿Ýjz  ÑŽ\>¤      S   vendor/danog/madelineproto/src/EventHandler/Message/Service/DialogPeerRequested.phpL  ’¿ÝjL  3sça¤      Q   vendor/danog/madelineproto/src/EventHandler/Message/Service/DialogPaymentSent.php@  ’¿Ýj@  %®|º¤      P   vendor/danog/madelineproto/src/EventHandler/Message/Service/DialogBotAllowed.php–	  ’¿Ýj–	  ß˜ª¤      S   vendor/danog/madelineproto/src/EventHandler/Message/Service/DialogChatMigrateTo.php  ’¿Ýj  ‰ƒ¦ã¤      O   vendor/danog/madelineproto/src/EventHandler/Message/Service/DialogGiftStars.phpj  ’¿Ýjj  q÷x¤      O   vendor/danog/madelineproto/src/EventHandler/Message/Service/DialogPhoneCall.php  ’¿Ýj  ,yöå¤      U   vendor/danog/madelineproto/src/EventHandler/Message/Service/DialogScreenshotTaken.php  ’¿Ýj  ÿo9Ä¤      Q   vendor/danog/madelineproto/src/EventHandler/Message/Service/DialogGiftPremium.phpw	  ’¿Ýjw	  oÓÄ$¤      T   vendor/danog/madelineproto/src/EventHandler/Message/Service/DialogConferenceCall.phpÒ
  ’¿ÝjÒ
  ŽË]¤      X   vendor/danog/madelineproto/src/EventHandler/Message/Service/DialogChannelMigrateFrom.phpd  ’¿Ýjd  ÌuÞê¤      N   vendor/danog/madelineproto/src/EventHandler/Message/Service/DialogStarGift.php  ’¿Ýj  ˆBÕ)¤      Q   vendor/danog/madelineproto/src/EventHandler/Message/Service/DialogTopicEdited.phpW	  ’¿ÝjW	  ¢üÉ¤      V   vendor/danog/madelineproto/src/EventHandler/Message/Service/DialogChatJoinedByLink.phpç  ’¿Ýjç  ;Nå9¤      R   vendor/danog/madelineproto/src/EventHandler/Message/Service/DialogPhotoChanged.php  ’¿Ýj  9ø7¼¤      S   vendor/danog/madelineproto/src/EventHandler/Message/Service/DialogMembersJoined.phpõ  ’¿Ýjõ  ` 6]¤      P   vendor/danog/madelineproto/src/EventHandler/Message/Service/DialogMemberLeft.phpÏ  ’¿ÝjÏ  %¨gs¤      R   vendor/danog/madelineproto/src/EventHandler/Message/Service/DialogTopicCreated.phpk  ’¿Ýjk  £úRi¤      S   vendor/danog/madelineproto/src/EventHandler/Message/Service/DialogContactSignUp.phpm  ’¿Ýjm  ‡xç~¤      O   vendor/danog/madelineproto/src/EventHandler/Message/Service/DialogGroupCall.php�  ’¿Ýj�  þ1_'¤      T   vendor/danog/madelineproto/src/EventHandler/Message/Service/DialogChannelCreated.phpË  ’¿ÝjË  kxŒ<¤      S   vendor/danog/madelineproto/src/EventHandler/Message/Service/DialogPaymentSentMe.php  ’¿Ýj  $JiÜ¤      D   vendor/danog/madelineproto/src/EventHandler/Message/ReportReason.php×  ’¿Ýj×  K¢ ¤      G   vendor/danog/madelineproto/src/EventHandler/Message/Entities/Italic.php^  ’¿Ýj^  p+»J¤      K   vendor/danog/madelineproto/src/EventHandler/Message/Entities/Blockquote.phpƒ  ’¿Ýjƒ  ŠÒ$†¤      L   vendor/danog/madelineproto/src/EventHandler/Message/Entities/CustomEmoji.phpv  ’¿Ýjv  xË_¤      D   vendor/danog/madelineproto/src/EventHandler/Message/Entities/Pre.phpÚ  ’¿ÝjÚ  òš.¤      I   vendor/danog/madelineproto/src/EventHandler/Message/Entities/BankCard.phpc  ’¿Ýjc  Áº|¤      E   vendor/danog/madelineproto/src/EventHandler/Message/Entities/Bold.phpV  ’¿ÝjV  Ãsâ&¤      H   vendor/danog/madelineproto/src/EventHandler/Message/Entities/Spoiler.php_  ’¿Ýj_  q²T¤      N   vendor/danog/madelineproto/src/EventHandler/Message/Entities/MessageEntity.php0  ’¿Ýj0  lÃà>¤      H   vendor/danog/madelineproto/src/EventHandler/Message/Entities/Unknown.phpË  ’¿ÝjË  Ž©Úý¤      H   vendor/danog/madelineproto/src/EventHandler/Message/Entities/Mention.phpd  ’¿Ýjd  ®+ß¤      H   vendor/danog/madelineproto/src/EventHandler/Message/Entities/TextUrl.phpD  ’¿ÝjD  yI!|¤      Q   vendor/danog/madelineproto/src/EventHandler/Message/Entities/InputMentionName.php¹  ’¿Ýj¹  Ô¤      F   vendor/danog/madelineproto/src/EventHandler/Message/Entities/Email.phpd  ’¿Ýjd  ù×1’¤      L   vendor/danog/madelineproto/src/EventHandler/Message/Entities/MentionName.php|  ’¿Ýj|  ‘�Å¤      F   vendor/danog/madelineproto/src/EventHandler/Message/Entities/Phone.phpe  ’¿Ýje  Œ/YÖ¤      J   vendor/danog/madelineproto/src/EventHandler/Message/Entities/Underline.phpk  ’¿Ýjk  ³Á[¤      N   vendor/danog/madelineproto/src/EventHandler/Message/Entities/FormattedDate.phpÈ  ’¿ÝjÈ  Pâ	~¤      H   vendor/danog/madelineproto/src/EventHandler/Message/Entities/Hashtag.phpQ  ’¿ÝjQ  åöo�¤      E   vendor/danog/madelineproto/src/EventHandler/Message/Entities/Code.phpX  ’¿ÝjX  EE3¤      H   vendor/danog/madelineproto/src/EventHandler/Message/Entities/Cashtag.php`  ’¿Ýj`  cw¤      K   vendor/danog/madelineproto/src/EventHandler/Message/Entities/BotCommand.phpn  ’¿Ýjn  y,uƒ¤      D   vendor/danog/madelineproto/src/EventHandler/Message/Entities/Url.phpˆ  ’¿Ýjˆ  ¢(Ñì¤      G   vendor/danog/madelineproto/src/EventHandler/Message/Entities/Strike.phpl  ’¿Ýjl  Šn¤      Q   vendor/danog/madelineproto/src/EventHandler/Message/Entities/TextWithEntities.phpÌ
  ’¿ÝjÌ
  ¨}ò›¤      F   vendor/danog/madelineproto/src/EventHandler/Message/ChannelMessage.php“  ’¿Ýj“  ÝÌ ‰¤      F   vendor/danog/madelineproto/src/EventHandler/Message/ServiceMessage.php™  ’¿Ýj™  B”|¹¤      F   vendor/danog/madelineproto/src/EventHandler/Message/PrivateMessage.php±  ’¿Ýj±  ž,¤      D   vendor/danog/madelineproto/src/EventHandler/Message/GroupMessage.php3H  ’¿Ýj3H  –¼r¤      6   vendor/danog/madelineproto/src/EventHandler/Delete.phpØ  ’¿ÝjØ  PéŸå¤      K   vendor/danog/madelineproto/src/EventHandler/Wallpaper/WallpaperSettings.php*  ’¿Ýj*  .dÄ¤      G   vendor/danog/madelineproto/src/EventHandler/Keyboard/InlineKeyboard.php  ’¿Ýj  _anŽ¤      F   vendor/danog/madelineproto/src/EventHandler/Keyboard/ReplyKeyboard.php|  ’¿Ýj|  säZÞ¤      ?   vendor/danog/madelineproto/src/EventHandler/User/BotStopped.phpà  ’¿Ýjà  &—‚¤      G   vendor/danog/madelineproto/src/EventHandler/User/Status/EmptyStatus.php«  ’¿Ýj«  º¹ÉÀ¤      B   vendor/danog/madelineproto/src/EventHandler/User/Status/Online.php  ’¿Ýj  L3å¤      A   vendor/danog/madelineproto/src/EventHandler/User/Status/Emoji.phpø  ’¿Ýjø  w&¤      D   vendor/danog/madelineproto/src/EventHandler/User/Status/LastWeek.phpª  ’¿Ýjª  }o˜ ¤      C   vendor/danog/madelineproto/src/EventHandler/User/Status/Offline.php  ’¿Ýj  xÊ™ä¤      D   vendor/danog/madelineproto/src/EventHandler/User/Status/Recently.php©  ’¿Ýj©  ®§—¤      E   vendor/danog/madelineproto/src/EventHandler/User/Status/LastMonth.php¬  ’¿Ýj¬  “–né¤      :   vendor/danog/madelineproto/src/EventHandler/User/Phone.phpL  ’¿ÝjL  ü‡)¤      <   vendor/danog/madelineproto/src/EventHandler/User/Blocked.php   ’¿Ýj   ‰ì ½¤      ;   vendor/danog/madelineproto/src/EventHandler/User/Status.phpÐ	  ’¿ÝjÐ	  Kó÷'¤      A   vendor/danog/madelineproto/src/EventHandler/User/UsernameInfo.phpM  ’¿ÝjM  3·l¤      =   vendor/danog/madelineproto/src/EventHandler/User/Username.phpâ  ’¿Ýjâ  …<ß¤      ?   vendor/danog/madelineproto/src/EventHandler/AbstractMessage.phpí´  ’¿Ýjí´  õÑA¤      A   vendor/danog/madelineproto/src/EventHandler/Payments/StarGift.php"	  ’¿Ýj"	  pý‹±¤      @   vendor/danog/madelineproto/src/EventHandler/Payments/Payment.php|  ’¿Ýj|  +Êbä¤      M   vendor/danog/madelineproto/src/EventHandler/Payments/PaymentRequestedInfo.phpÜ  ’¿ÝjÜ  5rI,¤      D   vendor/danog/madelineproto/src/EventHandler/Payments/PostAddress.php“  ’¿Ýj“  l~f¤      F   vendor/danog/madelineproto/src/EventHandler/Payments/PaymentCharge.phpú  ’¿Ýjú  ;$¤      G   vendor/danog/madelineproto/src/EventHandler/SimpleFilter/HasSticker.phpD  ’¿ÝjD  àŠAJ¤      F   vendor/danog/madelineproto/src/EventHandler/SimpleFilter/FromAdmin.phpQ  ’¿ÝjQ  Ç›—·¤      M   vendor/danog/madelineproto/src/EventHandler/SimpleFilter/HasDocumentPhoto.phpQ  ’¿ÝjQ  4w‰¤      J   vendor/danog/madelineproto/src/EventHandler/SimpleFilter/IsReplyToSelf.phpi  ’¿Ýji  ¤6«p¤      D   vendor/danog/madelineproto/src/EventHandler/SimpleFilter/IsReply.php^  ’¿Ýj^  7t'¤      H   vendor/danog/madelineproto/src/EventHandler/SimpleFilter/HasQuizPoll.php†  ’¿Ýj†  “¸^ì¤      G   vendor/danog/madelineproto/src/EventHandler/SimpleFilter/HasNoMedia.phpR  ’¿ÝjR  ÒD	
¤      E   vendor/danog/madelineproto/src/EventHandler/SimpleFilter/HasAudio.phpH  ’¿ÝjH  š'‹¤      E   vendor/danog/madelineproto/src/EventHandler/SimpleFilter/Incoming.phpL  ’¿ÝjL  Fß6¤      B   vendor/danog/madelineproto/src/EventHandler/SimpleFilter/Ended.phpC  ’¿ÝjC  j„­¤      L   vendor/danog/madelineproto/src/EventHandler/SimpleFilter/HasLiveLocation.phpf  ’¿Ýjf  °b"¤      D   vendor/danog/madelineproto/src/EventHandler/SimpleFilter/Running.phpO  ’¿ÝjO  öÃ9L¤      H   vendor/danog/madelineproto/src/EventHandler/SimpleFilter/IsForwarded.phpO  ’¿ÝjO  ˆò�¤      E   vendor/danog/madelineproto/src/EventHandler/SimpleFilter/HasTopic.phpx  ’¿Ýjx  sØF¤      J   vendor/danog/madelineproto/src/EventHandler/SimpleFilter/HasSinglePoll.phpŠ  ’¿ÝjŠ  ý´¼¤      E   vendor/danog/madelineproto/src/EventHandler/SimpleFilter/HasVenue.phpW  ’¿ÝjW  ÌC|O¤      E   vendor/danog/madelineproto/src/EventHandler/SimpleFilter/HasPhoto.php@  ’¿Ýj@  ?ß¤      E   vendor/danog/madelineproto/src/EventHandler/SimpleFilter/HasVoice.phpH  ’¿ÝjH  º!¤      E   vendor/danog/madelineproto/src/EventHandler/SimpleFilter/HasMedia.phpH  ’¿ÝjH  ¥
ó¤      D   vendor/danog/madelineproto/src/EventHandler/SimpleFilter/HasPoll.php}  ’¿Ýj}  k@t:¤      H   vendor/danog/madelineproto/src/EventHandler/SimpleFilter/HasDocument.phpF  ’¿ÝjF  œïO|¤      E   vendor/danog/madelineproto/src/EventHandler/SimpleFilter/HasVideo.php@  ’¿Ýj@   ®-×¤      H   vendor/danog/madelineproto/src/EventHandler/SimpleFilter/IsNotEdited.php}  ’¿Ýj}  ÚEr¤      E   vendor/danog/madelineproto/src/EventHandler/SimpleFilter/IsEdited.phpw  ’¿Ýjw  îdí¤      P   vendor/danog/madelineproto/src/EventHandler/SimpleFilter/FromAdminOrOutgoing.phpp  ’¿Ýjp  Íú‹¤      E   vendor/danog/madelineproto/src/EventHandler/SimpleFilter/Outgoing.phpL  ’¿ÝjL  #»¤      J   vendor/danog/madelineproto/src/EventHandler/SimpleFilter/HasRoundVideo.phpK  ’¿ÝjK  §ä<†¤      C   vendor/danog/madelineproto/src/EventHandler/SimpleFilter/HasGif.php<  ’¿Ýj<  wëw¤      L   vendor/danog/madelineproto/src/EventHandler/SimpleFilter/HasMultiplePoll.phpŽ  ’¿ÝjŽ  þè®#¤      H   vendor/danog/madelineproto/src/EventHandler/SimpleFilter/HasLocation.php‡  ’¿Ýj‡  gp×¤      7   vendor/danog/madelineproto/src/EventHandler/Command.php‚  ’¿Ýj‚  G¾÷L¤      J   vendor/danog/madelineproto/src/EventHandler/Channel/ChannelParticipant.phpn  ’¿Ýjn  ó~¤      G   vendor/danog/madelineproto/src/EventHandler/Channel/MessageForwards.phpú  ’¿Ýjú  :€d¤      E   vendor/danog/madelineproto/src/EventHandler/Channel/UpdateChannel.php   ’¿Ýj   ÆÓì^¤      K   vendor/danog/madelineproto/src/EventHandler/Channel/MessageViewsChanged.phpö  ’¿Ýjö  ÚfÊC¤      E   vendor/danog/madelineproto/src/EventHandler/Delete/DeleteMessages.php   ’¿Ýj   ©ÅŸ¤      N   vendor/danog/madelineproto/src/EventHandler/Delete/DeleteScheduledMessages.phpZ  ’¿ÝjZ  d>C¤      L   vendor/danog/madelineproto/src/EventHandler/Delete/DeleteChannelMessages.php7  ’¿Ýj7  	I>s¤      E   vendor/danog/madelineproto/src/EventHandler/GroupOrChannelMessage.php  ’¿Ýj  AJH¤      ?   vendor/danog/madelineproto/src/EventHandler/Attributes/Cron.php%  ’¿Ýj%  à±Ñ¤      B   vendor/danog/madelineproto/src/EventHandler/Attributes/Handler.phpŠ  ’¿ÝjŠ  a%'¤      =   vendor/danog/madelineproto/src/EventHandler/SimpleFilters.php¸  ’¿Ýj¸  ÉîÅþ¤      9   vendor/danog/madelineproto/src/EventHandler/Wallpaper.php3  ’¿Ýj3  “>ž¤      L   vendor/danog/madelineproto/src/EventHandler/Pinned/PinnedChannelMessages.phpÎ  ’¿ÝjÎ  =¾j¤      J   vendor/danog/madelineproto/src/EventHandler/Pinned/PinnedGroupMessages.phpÔ  ’¿ÝjÔ  ˜VYW¤      L   vendor/danog/madelineproto/src/EventHandler/Pinned/PinnedPrivateMessages.php–  ’¿Ýj–  YÔ»Õ¤      8   vendor/danog/madelineproto/src/EventHandler/Keyboard.php–  ’¿Ýj–  ¿Ï¤      A   vendor/danog/madelineproto/src/EventHandler/Participant/Admin.phpò	  ’¿Ýjò	  „þÂ~¤      B   vendor/danog/madelineproto/src/EventHandler/Participant/Banned.php  ’¿Ýj  ÝŽ¢¤      C   vendor/danog/madelineproto/src/EventHandler/Participant/Creator.php,  ’¿Ýj,  Êã_Q¤      B   vendor/danog/madelineproto/src/EventHandler/Participant/MySelf.php(  ’¿Ýj(  X¯wP¤      B   vendor/danog/madelineproto/src/EventHandler/Participant/Rights.phpÏ  ’¿ÝjÏ  ˜¤      H   vendor/danog/madelineproto/src/EventHandler/Participant/Rights/Admin.phpª  ’¿Ýjª  ÛÄ´¤      I   vendor/danog/madelineproto/src/EventHandler/Participant/Rights/Banned.php·  ’¿Ýj·  …Q&“¤      @   vendor/danog/madelineproto/src/EventHandler/Participant/Left.phpƒ  ’¿Ýjƒ  DKR�¤      B   vendor/danog/madelineproto/src/EventHandler/Participant/Member.phpí  ’¿Ýjí  ½–æ¤      B   vendor/danog/madelineproto/src/EventHandler/Action/RecordRound.php©  ’¿Ýj©  °ãg¤      D   vendor/danog/madelineproto/src/EventHandler/Action/ChooseSticker.php�  ’¿Ýj�  ¢�F¤      =   vendor/danog/madelineproto/src/EventHandler/Action/Cancel.phpà  ’¿Ýjà  »nÖb¤      B   vendor/danog/madelineproto/src/EventHandler/Action/RecordAudio.php¢  ’¿Ýj¢  _
L¤      B   vendor/danog/madelineproto/src/EventHandler/Action/GeoLocation.php¦  ’¿Ýj¦  Ì0¸î¤      B   vendor/danog/madelineproto/src/EventHandler/Action/UploadPhoto.php  ’¿Ýj  ãv1ô¤      ?   vendor/danog/madelineproto/src/EventHandler/Action/EmojiTap.php#	  ’¿Ýj#	  qÐ¹�¤      @   vendor/danog/madelineproto/src/EventHandler/Action/EmojiSeen.php„  ’¿Ýj„  ïî¨¸¤      B   vendor/danog/madelineproto/src/EventHandler/Action/RecordVideo.phpš  ’¿Ýjš  ¶k"Ø¤      B   vendor/danog/madelineproto/src/EventHandler/Action/UploadRound.php  ’¿Ýj  ÌÞ@¤      D   vendor/danog/madelineproto/src/EventHandler/Action/HistoryImport.php  ’¿Ýj  qÃ¤      H   vendor/danog/madelineproto/src/EventHandler/Action/GroupCallSpeaking.php³  ’¿Ýj³  Ï¦ì_¤      B   vendor/danog/madelineproto/src/EventHandler/Action/UploadAudio.php  ’¿Ýj  v²¹X¤      E   vendor/danog/madelineproto/src/EventHandler/Action/UploadDocument.php  ’¿Ýj  ¬6K¤      =   vendor/danog/madelineproto/src/EventHandler/Action/Typing.phpŠ  ’¿ÝjŠ  ç?Òñ¤      ?   vendor/danog/madelineproto/src/EventHandler/Action/GamePlay.php”  ’¿Ýj”  {Zx¤      B   vendor/danog/madelineproto/src/EventHandler/Action/UploadVideo.php  ’¿Ýj  CÜ¤      D   vendor/danog/madelineproto/src/EventHandler/Action/ChooseContact.php§  ’¿Ýj§  ‡Lë¤      6   vendor/danog/madelineproto/src/EventHandler/Update.php¥  ’¿Ýj¥  á93i¤      ;   vendor/danog/madelineproto/src/EventHandler/CommandType.phpÖ  ’¿ÝjÖ  „rzÍ¤      ;   vendor/danog/madelineproto/src/EventHandler/Participant.php%
  ’¿Ýj%
  w[½Â¤      @   vendor/danog/madelineproto/src/EventHandler/AbstractLocation.php7  ’¿Ýj7  ƒI{g¤      7   vendor/danog/madelineproto/src/EventHandler/Privacy.php  ’¿Ýj  —r­¤      D   vendor/danog/madelineproto/src/EventHandler/Plugin/RestartPlugin.php/  ’¿Ýj/  5Š¤      C   vendor/danog/madelineproto/src/EventHandler/Story/StoryReaction.phpÇ  ’¿ÝjÇ  ñT¤      B   vendor/danog/madelineproto/src/EventHandler/Story/StoryDeleted.php©  ’¿Ýj©  �ÛUa¤      ;   vendor/danog/madelineproto/src/EventHandler/Story/Story.php1  ’¿Ýj1  uîï­¤      B   vendor/danog/madelineproto/src/EventHandler/Filter/FilterEnded.phpÝ  ’¿ÝjÝ  ªÈT¤      F   vendor/danog/madelineproto/src/EventHandler/Filter/FilterForwarded.phpŸ  ’¿ÝjŸ  ö1¸H¤      B   vendor/danog/madelineproto/src/EventHandler/Filter/FilterMedia.php”  ’¿Ýj”  D—zÄ¤      I   vendor/danog/madelineproto/src/EventHandler/Filter/FilterCommentReply.php›  ’¿Ýj›  ºÁÌI¤      A   vendor/danog/madelineproto/src/EventHandler/Filter/FilterPoll.phpË  ’¿ÝjË  æª¤¤      D   vendor/danog/madelineproto/src/EventHandler/Filter/FilterService.php—  ’¿Ýj—  ›‘¶\¤      G   vendor/danog/madelineproto/src/EventHandler/Filter/FilterTextStarts.phpÿ  ’¿Ýjÿ  ´X·?¤      E   vendor/danog/madelineproto/src/EventHandler/Filter/FilterOutgoing.phpè  ’¿Ýjè  ©ü+b¤      P   vendor/danog/madelineproto/src/EventHandler/Filter/AbstractFilterFromSenders.php$  ’¿Ýj$  ~‹ª8¤      D   vendor/danog/madelineproto/src/EventHandler/Filter/FilterFromBot.php8  ’¿Ýj8  x×g‡¤      C   vendor/danog/madelineproto/src/EventHandler/Filter/FilterSender.php·  ’¿Ýj·  ´ªâ¤      G   vendor/danog/madelineproto/src/EventHandler/Filter/FilterFromSender.php»  ’¿Ýj»  =,=c¤      E   vendor/danog/madelineproto/src/EventHandler/Filter/FilterLocation.phpÙ  ’¿ÝjÙ  ê›|¤      S   vendor/danog/madelineproto/src/EventHandler/Filter/FilterCommandCaseInsensitive.php  ’¿Ýj  dÌ ¤      G   vendor/danog/madelineproto/src/EventHandler/Filter/FilterBotCommand.phpØ  ’¿ÝjØ  -á¤      P   vendor/danog/madelineproto/src/EventHandler/Filter/FilterTextCaseInsensitive.php2  ’¿Ýj2  )d^¤      J   vendor/danog/madelineproto/src/EventHandler/Filter/FilterRegexMatchAll.phpé  ’¿Ýjé  sAd¤      B   vendor/danog/madelineproto/src/EventHandler/Filter/FilterGroup.php�  ’¿Ýj�  z³’A¤      I   vendor/danog/madelineproto/src/EventHandler/Filter/FilterTextContains.phpõ  ’¿Ýjõ  5>ðY¤      A   vendor/danog/madelineproto/src/EventHandler/Filter/FilterText.phpœ  ’¿Ýjœ  ¨ÏÛJ¤      D   vendor/danog/madelineproto/src/EventHandler/Filter/FilterNoMedia.php¦  ’¿Ýj¦  –KŽ¤      D   vendor/danog/madelineproto/src/EventHandler/Filter/FilterRunning.phpã  ’¿Ýjã  �UªÀ¤      E   vendor/danog/madelineproto/src/EventHandler/Filter/FilterIncoming.phpé  ’¿Ýjé  Å 7O¤      D   vendor/danog/madelineproto/src/EventHandler/Filter/FilterPrivate.phpœ  ’¿Ýjœ  *Þ&è¤      O   vendor/danog/madelineproto/src/EventHandler/Filter/AbstractFilterFromSender.phpÍ  ’¿ÝjÍ  -|ú¤      E   vendor/danog/madelineproto/src/EventHandler/Filter/FilterAllowAll.php(  ’¿Ýj(  /G†r¤      A   vendor/danog/madelineproto/src/EventHandler/Filter/FilterPeer.phpu  ’¿Ýju  *ú·¤      L   vendor/danog/madelineproto/src/EventHandler/Filter/Poll/FilterSinglePoll.phpT  ’¿ÝjT  Ù>¬¤      N   vendor/danog/madelineproto/src/EventHandler/Filter/Poll/FilterMultiplePoll.php\  ’¿Ýj\  b¡¤      J   vendor/danog/madelineproto/src/EventHandler/Filter/Poll/FilterQuizPoll.phpL  ’¿ÝjL  ÷ø-ž¤      =   vendor/danog/madelineproto/src/EventHandler/Filter/Filter.php!  ’¿Ýj!  ÚÈPì¤      F   vendor/danog/madelineproto/src/EventHandler/Filter/FilterNotEdited.php¦  ’¿Ýj¦  ·WØ	¤      B   vendor/danog/madelineproto/src/EventHandler/Filter/FilterReply.php®  ’¿Ýj®  Äš=¤      D   vendor/danog/madelineproto/src/EventHandler/Filter/FilterCommand.php9
  ’¿Ýj9
  Ý®;r¤      C   vendor/danog/madelineproto/src/EventHandler/Filter/FilterEdited.php   ’¿Ýj   %¨¤      T   vendor/danog/madelineproto/src/EventHandler/Filter/FilterTextEndsCaseInsensitive.phpŒ  ’¿ÝjŒ  Ô~l®¤      J   vendor/danog/madelineproto/src/EventHandler/Filter/FilterForwardedFrom.php   ’¿Ýj   S "ã¤      H   vendor/danog/madelineproto/src/EventHandler/Filter/FilterReplyToSelf.phpˆ  ’¿Ýjˆ  ONÇÛ¤      C   vendor/danog/madelineproto/src/EventHandler/Filter/FilterSecret.php˜  ’¿Ýj˜  2ô½¤      L   vendor/danog/madelineproto/src/EventHandler/Filter/FilterButtonQueryData.php�  ’¿Ýj�  ƒz¹¤      E   vendor/danog/madelineproto/src/EventHandler/Filter/FilterTextEnds.phpö  ’¿Ýjö  ü„+Ï¤      K   vendor/danog/madelineproto/src/EventHandler/Filter/Combinator/FiltersOr.php
  ’¿Ýj
  Hbñ8¤      K   vendor/danog/madelineproto/src/EventHandler/Filter/Combinator/FilterNot.php"  ’¿Ýj"  \UpW¤      L   vendor/danog/madelineproto/src/EventHandler/Filter/Combinator/FiltersAnd.php
  ’¿Ýj
  VQðR¤      K   vendor/danog/madelineproto/src/EventHandler/Filter/Location/FilterVenue.php#  ’¿Ýj#  4[éá¤      R   vendor/danog/madelineproto/src/EventHandler/Filter/Location/FilterLiveLocation.php@  ’¿Ýj@  ‰Pž¤      B   vendor/danog/madelineproto/src/EventHandler/Filter/FilterRegex.phpñ  ’¿Ýjñ  Ãæ€¤      H   vendor/danog/madelineproto/src/EventHandler/Filter/Media/FilterVoice.php	  ’¿Ýj	  uKnü¤      J   vendor/danog/madelineproto/src/EventHandler/Filter/Media/FilterSticker.php  ’¿Ýj  À1Óî¤      H   vendor/danog/madelineproto/src/EventHandler/Filter/Media/FilterVideo.php  ’¿Ýj  ï'®)¤      H   vendor/danog/madelineproto/src/EventHandler/Filter/Media/FilterPhoto.php  ’¿Ýj  Ó‹Ïv¤      F   vendor/danog/madelineproto/src/EventHandler/Filter/Media/FilterGif.phpù  ’¿Ýjù  Ìlç¤      P   vendor/danog/madelineproto/src/EventHandler/Filter/Media/FilterDocumentPhoto.php0  ’¿Ýj0  $èãi¤      M   vendor/danog/madelineproto/src/EventHandler/Filter/Media/FilterRoundVideo.php  ’¿Ýj  iÂš¤      H   vendor/danog/madelineproto/src/EventHandler/Filter/Media/FilterAudio.php  ’¿Ýj  § Ø…¤      K   vendor/danog/madelineproto/src/EventHandler/Filter/Media/FilterDocument.php  ’¿Ýj  m€ª¤      H   vendor/danog/madelineproto/src/EventHandler/Filter/FilterFromSenders.phpÆ  ’¿ÝjÆ  Ùï¤      D   vendor/danog/madelineproto/src/EventHandler/Filter/FilterSenders.phpÂ  ’¿ÝjÂ  hCwa¤      D   vendor/danog/madelineproto/src/EventHandler/Filter/FilterTopicId.phpY  ’¿ÝjY  –%¾Y¤      X   vendor/danog/madelineproto/src/EventHandler/Filter/FilterTextContainsCaseInsensitive.phpŠ  ’¿ÝjŠ  Áeø¤      B   vendor/danog/madelineproto/src/EventHandler/Filter/FilterTopic.php…  ’¿Ýj…  <Gr¤      D   vendor/danog/madelineproto/src/EventHandler/Filter/FilterMessage.phpw  ’¿Ýjw  iÒæ¤      D   vendor/danog/madelineproto/src/EventHandler/Filter/FilterChannel.php—  ’¿Ýj—  ²¬"ÿ¤      V   vendor/danog/madelineproto/src/EventHandler/Filter/FilterTextStartsCaseInsensitive.php•  ’¿Ýj•  Ìé[Ù¤      F   vendor/danog/madelineproto/src/EventHandler/Filter/FilterFromAdmin.php³  ’¿Ýj³  Ã.¡¤      ?   vendor/danog/madelineproto/src/EventHandler/Poll/SinglePoll.php3  ’¿Ýj3  •°}¤      ?   vendor/danog/madelineproto/src/EventHandler/Poll/PollAnswer.phpp  ’¿Ýjp  y·w¤      A   vendor/danog/madelineproto/src/EventHandler/Poll/MultiplePoll.php>  ’¿Ýj>  Ý3¤      =   vendor/danog/madelineproto/src/EventHandler/Poll/QuizPoll.php  ’¿Ýj  ßÐ—¤      <   vendor/danog/madelineproto/src/EventHandler/AbstractPoll.phpR  ’¿ÝjR  1$7&¤      5   vendor/danog/madelineproto/src/EventHandler/Media.phpA*  ’¿ÝjA*  •F@¤      9   vendor/danog/madelineproto/src/EventHandler/MultiCall.php^  ’¿Ýj^  l­ªã¤      6   vendor/danog/madelineproto/src/EventHandler/Pinned.phpÿ  ’¿Ýjÿ  Ê²:¤      C   vendor/danog/madelineproto/src/EventHandler/InlineQueryPeerType.php7  ’¿Ýj7  ¾2v¤      X   vendor/danog/madelineproto/src/EventHandler/ChatInviteRequester/BotChatInviteRequest.php 	  ’¿Ýj 	  “wíã¤      W   vendor/danog/madelineproto/src/EventHandler/ChatInviteRequester/PendingJoinRequests.phpu  ’¿Ýju  S�”I¤      =   vendor/danog/madelineproto/src/EventHandler/CallbackQuery.phpx
  ’¿Ýjx
  Kà Ý¤      :   vendor/danog/madelineproto/src/EventHandler/ChatInvite.phpš  ’¿Ýjš  ª¢.�¤      6   vendor/danog/madelineproto/src/EventHandler/Typing.phpŒ  ’¿ÝjŒ  ‚·H¤      G   vendor/danog/madelineproto/src/EventHandler/Location/StaticLocation.php#  ’¿Ýj#  £}Ø�¤      >   vendor/danog/madelineproto/src/EventHandler/Location/Venue.phpÚ  ’¿ÝjÚ  ÿÈË¤      E   vendor/danog/madelineproto/src/EventHandler/Location/LiveLocation.phpº  ’¿Ýjº  ±7SÛ¤      C   vendor/danog/madelineproto/src/EventHandler/ChatInviteRequester.php  ’¿Ýj  R#pw¤      ;   vendor/danog/madelineproto/src/EventHandler/BotCommands.phpl  ’¿Ýjl  ±:À?¤      7   vendor/danog/madelineproto/src/EventHandler/Message.phpÉX  ’¿ÝjÉX  ;¾NÎ¤      ?   vendor/danog/madelineproto/src/EventHandler/Topic/IconColor.phpù  ’¿Ýjù  ×õ³c¤      E   vendor/danog/madelineproto/src/EventHandler/Media/AbstractSticker.phpé  ’¿Ýjé  ¤ai
¤      B   vendor/danog/madelineproto/src/EventHandler/Media/VideoSticker.php  ’¿Ýj  *±¨À¤      ;   vendor/danog/madelineproto/src/EventHandler/Media/Photo.phpS  ’¿ÝjS  _Ž/Ø¤      @   vendor/danog/madelineproto/src/EventHandler/Media/MediaStory.php¬  ’¿Ýj¬  q³Š'¤      A   vendor/danog/madelineproto/src/EventHandler/Media/CustomEmoji.phpû  ’¿Ýjû  Ó²ž�¤      @   vendor/danog/madelineproto/src/EventHandler/Media/RoundVideo.phpH  ’¿ÝjH  ?R}¤      A   vendor/danog/madelineproto/src/EventHandler/Media/MaskSticker.php
  ’¿Ýj
  ­è¤      E   vendor/danog/madelineproto/src/EventHandler/Media/InlineThumbnail.php¯  ’¿Ýj¯  *¤      ;   vendor/danog/madelineproto/src/EventHandler/Media/Voice.php5  ’¿Ýj5  Á¦“J¤      >   vendor/danog/madelineproto/src/EventHandler/Media/Document.phpO  ’¿ÝjO  aØ(¤      D   vendor/danog/madelineproto/src/EventHandler/Media/VideoThumbnail.phpä  ’¿Ýjä  ÞÙú¤      C   vendor/danog/madelineproto/src/EventHandler/Media/AbstractVideo.phps  ’¿Ýjs  ð¨ùw¤      C   vendor/danog/madelineproto/src/EventHandler/Media/StaticSticker.php  ’¿Ýj  Ý§Ô¤      B   vendor/danog/madelineproto/src/EventHandler/Media/MaskPosition.php(  ’¿Ýj(  çyWy¤      C   vendor/danog/madelineproto/src/EventHandler/Media/DocumentPhoto.php¦  ’¿Ýj¦  €Ty¤      ;   vendor/danog/madelineproto/src/EventHandler/Media/Video.phpØ  ’¿ÝjØ  Ì+P¤      G   vendor/danog/madelineproto/src/EventHandler/Media/AbstractThumbnail.phpC  ’¿ÝjC  ‘±üW¤      9   vendor/danog/madelineproto/src/EventHandler/Media/Gif.phpm	  ’¿Ýjm	  
GI–¤      >   vendor/danog/madelineproto/src/EventHandler/Media/GeoPoint.php  ’¿Ýj  ‰'ÝO¤      =   vendor/danog/madelineproto/src/EventHandler/Media/Sticker.phpi  ’¿Ýji  q^dä¤      E   vendor/danog/madelineproto/src/EventHandler/Media/AnimatedSticker.php  ’¿Ýj  =ß©H¤      ?   vendor/danog/madelineproto/src/EventHandler/Media/Thumbnail.php  ’¿Ýj  ¾”¨¤      C   vendor/danog/madelineproto/src/EventHandler/Media/AbstractAudio.php  ’¿Ýj  YðT|¤      ;   vendor/danog/madelineproto/src/EventHandler/Media/Audio.phpg  ’¿Ýjg  ¤Ý¤      F   vendor/danog/madelineproto/src/EventHandler/AbstractPrivateMessage.phpá  ’¿Ýjá  ÙUz–¤      6   vendor/danog/madelineproto/src/EventHandler/BotApp.phpn  ’¿Ýjn  &kP¤      O   vendor/danog/madelineproto/src/EventHandler/ChatInvite/ChatInvitePublicJoin.php·  ’¿Ýj·  bf«¤      M   vendor/danog/madelineproto/src/EventHandler/ChatInvite/ChatInviteExported.php†  ’¿Ýj†  û�Õœ¤      =   vendor/danog/madelineproto/src/EventHandler/AbstractStory.phpZ  ’¿ÝjZ  Ü,w¤      A   vendor/danog/madelineproto/src/EventHandler/Typing/UserTyping.phpu  ’¿Ýju  OoPÊ¤      E   vendor/danog/madelineproto/src/EventHandler/Typing/ChatUserTyping.phpË  ’¿ÝjË  ¸�UR¤      K   vendor/danog/madelineproto/src/EventHandler/Typing/SupergroupUserTyping.phpÂ  ’¿ÝjÂ  Š¢*×¤      G   vendor/danog/madelineproto/src/EventHandler/Typing/SecretUserTyping.phpW  ’¿ÝjW  Í’¨I¤      ;   vendor/danog/madelineproto/src/EventHandler/InlineQuery.php`
  ’¿Ýj`
  Yu$”¤      4   vendor/danog/madelineproto/src/EventHandler/Call.php~3  ’¿Ýj~3  ¶*¤      `   vendor/danog/madelineproto/src/EventHandler/Privacy/RuleDestination/DisallowChatParticipants.phpÌ  ’¿ÝjÌ  B@Î¤      X   vendor/danog/madelineproto/src/EventHandler/Privacy/RuleDestination/DisallowContacts.phpÇ  ’¿ÝjÇ  5…¨Ã¤      ]   vendor/danog/madelineproto/src/EventHandler/Privacy/RuleDestination/AllowChatParticipants.phpÅ  ’¿ÝjÅ  ²¼†?¤      Y   vendor/danog/madelineproto/src/EventHandler/Privacy/RuleDestination/AllowCloseFriends.php¥  ’¿Ýj¥  °D ¤      S   vendor/danog/madelineproto/src/EventHandler/Privacy/RuleDestination/DisallowAll.php¾  ’¿Ýj¾  ši¤j¤      R   vendor/danog/madelineproto/src/EventHandler/Privacy/RuleDestination/AllowUsers.phpª  ’¿Ýjª  Cñc@¤      U   vendor/danog/madelineproto/src/EventHandler/Privacy/RuleDestination/DisallowUsers.php±  ’¿Ýj±  T“°£¤      U   vendor/danog/madelineproto/src/EventHandler/Privacy/RuleDestination/AllowContacts.phpÀ  ’¿ÝjÀ  Ú{¤      P   vendor/danog/madelineproto/src/EventHandler/Privacy/RuleDestination/AllowAll.php¸  ’¿Ýj¸  #siå¤      <   vendor/danog/madelineproto/src/EventHandler/Privacy/Rule.php
  ’¿Ýj
  ç¦‚/¤      G   vendor/danog/madelineproto/src/EventHandler/Privacy/RuleDestination.php  ’¿Ýj  ÞôÉ`¤      -   vendor/danog/madelineproto/src/APIWrapper.phpñ  ’¿Ýjñ  ¾ÚäL¤      -   vendor/danog/madelineproto/src/LightState.php#  ’¿Ýj#  T<Æ:¤      (   vendor/danog/madelineproto/composer.jsonT  ’¿ÝjT  G"v¤      1   vendor/danog/php-rtc-rtcp/src/RtcpRtpfbPacket.php£  ’¿Ýj£  Ìë"¤      5   vendor/danog/php-rtc-rtcp/src/RtcpPacketInterface.phpÖ  ’¿ÝjÖ  ÃƒÓý¤      .   vendor/danog/php-rtc-rtcp/src/RtcpSrPacket.phpI  ’¿ÝjI  âáˆ¤      0   vendor/danog/php-rtc-rtcp/src/RtcpSdesPacket.phpI  ’¿ÝjI  š�zï¤      /   vendor/danog/php-rtc-rtcp/src/RtcpByePacket.phpî  ’¿Ýjî  
gz|¤      /   vendor/danog/php-rtc-rtcp/src/RtcpConstants.php  ’¿Ýj  óYê)¤      ,   vendor/danog/php-rtc-rtcp/src/RtcpPacket.php\  ’¿Ýj\  ž+b¤      ?   vendor/danog/php-rtc-rtcp/src/Exception/RtcpPacketException.phpƒ  ’¿Ýjƒ  (´™¤      B   vendor/danog/php-rtc-rtcp/src/Exception/RtcpExceptionInterface.php   ’¿Ýj   ”•BX¤      0   vendor/danog/php-rtc-rtcp/src/RtcpSourceInfo.php§  ’¿Ýj§  Ú¦ê¤      .   vendor/danog/php-rtc-rtcp/src/RtcpRrPacket.phpô  ’¿Ýjô  {6ÆÜ¤      2   vendor/danog/php-rtc-rtcp/src/RtcpReceiverInfo.phpó  ’¿Ýjó  ˜ó=÷¤      -   vendor/danog/php-rtc-rtcp/src/RtcpUtility.php4  ’¿Ýj4  ¯µÁ¤      0   vendor/danog/php-rtc-rtcp/src/RtcpSenderInfo.phpÊ  ’¿ÝjÊ  AÎµ|¤      0   vendor/danog/php-rtc-rtcp/src/RtcpPsfbPacket.phpˆ  ’¿Ýjˆ  «U:¢¤      '   vendor/danog/php-rtc-rtcp/composer.json„  ’¿Ýj„  ç7Ã¤      0   vendor/danog/php-rtc-ice/src/RTCIceCandidate.phpæ8  ’¿Ýjæ8  ph² ¤      4   vendor/danog/php-rtc-ice/src/RTCIceCandidatePair.phpò)  ’¿Ýjò)  F]É&¤      6   vendor/danog/php-rtc-ice/src/RTCIceServerInterface.phpö  ’¿Ýjö  i­9}¤      &   vendor/danog/php-rtc-ice/src/Utils.php
  ’¿Ýj
  ì‰¤      1   vendor/danog/php-rtc-ice/src/RTCIceParameters.phpê  ’¿Ýjê  ïïË¤      H   vendor/danog/php-rtc-ice/src/Listener/IceGathererStateChangeListener.php  ’¿Ýj  ,š$�¤      E   vendor/danog/php-rtc-ice/src/Listener/IceConnectionClosedListener.phpé   ’¿Ýjé   ‡3	¤      I   vendor/danog/php-rtc-ice/src/Listener/IceTransportStateChangeListener.php  ’¿Ýj  …~É§¤      H   vendor/danog/php-rtc-ice/src/Listener/IceTransportDisconnectListener.php4  ’¿Ýj4  pRx„¤      B   vendor/danog/php-rtc-ice/src/Listener/IceTransportDataListener.phps  ’¿Ýjs  ù*‘¤      C   vendor/danog/php-rtc-ice/src/Listener/IceConnectionDataListener.phpé  ’¿Ýjé  «^†î¤      9   vendor/danog/php-rtc-ice/src/RTCIceTransportInterface.php¸  ’¿Ýj¸  [™W¤      8   vendor/danog/php-rtc-ice/src/RTCIceGathererInterface.phpk  ’¿Ýjk  )ÙÇ¹¤      E   vendor/danog/php-rtc-ice/src/RTCIceProtocolConfigurationInterface.php  ’¿Ýj  æ¥qî¤      2   vendor/danog/php-rtc-ice/src/IceProtocolParser.phpb  ’¿Ýjb  øêŸŠ¤      .   vendor/danog/php-rtc-ice/src/RTCICESetting.phpÏ  ’¿ÝjÏ  AVŸ
¤      /   vendor/danog/php-rtc-ice/src/RTCIceGatherer.phpH  ’¿ÝjH  Ö¾s¤      *   vendor/danog/php-rtc-ice/src/Trait/DNS.php  ’¿Ýj  %0Ù¤      5   vendor/danog/php-rtc-ice/src/Trait/NetworkAdapter.phpD  ’¿ÝjD  z«Mð¤      +   vendor/danog/php-rtc-ice/src/Trait/Mdns.phpM  ’¿ÝjM  Ž¸f_¤      0   vendor/danog/php-rtc-ice/src/RTCIceTransport.phpœ0  ’¿Ýjœ0  6 �¤      -   vendor/danog/php-rtc-ice/src/RTCIceServer.phpê  ’¿Ýjê  Î°ó‚¤      >   vendor/danog/php-rtc-ice/src/Enum/RTCIceCandidatePairStats.php'  ’¿Ýj'  ÅÕ`¤      3   vendor/danog/php-rtc-ice/src/Enum/CandidateType.php  ’¿Ýj  Xr×¤      -   vendor/danog/php-rtc-ice/src/Enum/IceRole.php_  ’¿Ýj_  Ï¶ÜÝ¤      4   vendor/danog/php-rtc-ice/src/Enum/CheckListState.phpà  ’¿Ýjà  M¯ ^¤      7   vendor/danog/php-rtc-ice/src/Enum/IceTransportState.phpÆ  ’¿ÝjÆ  ¤SxJ¤      9   vendor/danog/php-rtc-ice/src/Enum/TransportPolicyType.php8  ’¿Ýj8  ø©ôß¤      7   vendor/danog/php-rtc-ice/src/Enum/IceGatheringState.php…  ’¿Ýj…  šˆ¥¤      3   vendor/danog/php-rtc-ice/src/Enum/TransportType.phpd  ’¿Ýjd  ¯C¤      :   vendor/danog/php-rtc-ice/src/RTCIceConnectionInterface.php¼  ’¿Ýj¼  ŽlH¤      1   vendor/danog/php-rtc-ice/src/RTCIceConnection.php�– ’¿Ýj�– ªQ—<¤      <   vendor/danog/php-rtc-ice/src/RTCIceProtocolConfiguration.php«  ’¿Ýj«  �'¯²¤      &   vendor/danog/php-rtc-ice/composer.json  ’¿Ýj  nnM
¤      .   vendor/danog/php-rtc-sctp/src/SctpConstant.php¦  ’¿Ýj¦  E¹Ä7¤      /   vendor/danog/php-rtc-sctp/src/InboundStream.php  ’¿Ýj  ¹é÷¤      >   vendor/danog/php-rtc-sctp/src/Listener/DataChannelListener.phpU  ’¿ÝjU  ½Ì#Ì¤      5   vendor/danog/php-rtc-sctp/src/Chunk/BaseInitChunk.phpü  ’¿Ýjü  C£žþ¤      5   vendor/danog/php-rtc-sctp/src/Chunk/ReconfigChunk.phpá  ’¿Ýjá  ÍÎP¤¤      6   vendor/danog/php-rtc-sctp/src/Chunk/CookieAckChunk.phpÝ  ’¿ÝjÝ  lÞu¤      6   vendor/danog/php-rtc-sctp/src/Chunk/HeartbeatChunk.phpÚ  ’¿ÝjÚ  ¤i5Ù¤      7   vendor/danog/php-rtc-sctp/src/Chunk/BaseParamsChunk.phpº  ’¿Ýjº  ¶@U?¤      2   vendor/danog/php-rtc-sctp/src/Chunk/ErrorChunk.phpÒ  ’¿ÝjÒ  4ü	4¤      7   vendor/danog/php-rtc-sctp/src/Chunk/ForwardTsnChunk.phpé  ’¿Ýjé  M¥T‹¤      6   vendor/danog/php-rtc-sctp/src/Chunk/ChunkInterface.php¹  ’¿Ýj¹  Ò+V¤      2   vendor/danog/php-rtc-sctp/src/Chunk/AbortChunk.phpÒ  ’¿ÝjÒ  Ï Þ¤      1   vendor/danog/php-rtc-sctp/src/Chunk/DataChunk.phpƒ  ’¿Ýjƒ  òca¤      9   vendor/danog/php-rtc-sctp/src/Chunk/HeartbeatAckChunk.phpì  ’¿Ýjì  Òw›v¤      6   vendor/danog/php-rtc-sctp/src/Chunk/AttributeChunk.php5  ’¿Ýj5  ö4_¤      =   vendor/danog/php-rtc-sctp/src/Chunk/ShutdownCompleteChunk.phpà  ’¿Ýjà  
ô&Ï¤      4   vendor/danog/php-rtc-sctp/src/Chunk/InitAckChunk.phpà  ’¿Ýjà  ¥EYæ¤      5   vendor/danog/php-rtc-sctp/src/Chunk/ShutdownChunk.php±  ’¿Ýj±  Ðöa¤      1   vendor/danog/php-rtc-sctp/src/Chunk/InitChunk.phpÏ  ’¿ÝjÏ  o‘6¿¤      -   vendor/danog/php-rtc-sctp/src/Chunk/Chunk.phpù  ’¿Ýjù  ñj£;¤      8   vendor/danog/php-rtc-sctp/src/Chunk/ShutdownAckChunk.phpà  ’¿Ýjà  üG¤      1   vendor/danog/php-rtc-sctp/src/Chunk/SackChunk.phpÑ  ’¿ÝjÑ   ÿ¤      7   vendor/danog/php-rtc-sctp/src/Chunk/CookieEchoChunk.phpÔ  ’¿ÝjÔ  ”ŒÀÁ¤      ,   vendor/danog/php-rtc-sctp/src/SctpPacket.php~  ’¿Ýj~  é(%‚¤      9   vendor/danog/php-rtc-sctp/src/Exception/SctpException.php·  ’¿Ýj·  »|­¤      B   vendor/danog/php-rtc-sctp/src/Exception/SctpExceptionInterface.phpº  ’¿Ýjº  %Ûð¦¤      -   vendor/danog/php-rtc-sctp/src/SctpUtility.php(#  ’¿Ýj(#  ’teÕ¤      3   vendor/danog/php-rtc-sctp/src/Trait/DataChannel.phpøa  ’¿Ýjøa  þœ¥Ç¤      8   vendor/danog/php-rtc-sctp/src/SignalingSinkInterface.php÷  ’¿Ýj÷  ZMò¤      +   vendor/danog/php-rtc-sctp/src/SctpTimer.phpR  ’¿ÝjR  %ae’¤      2   vendor/danog/php-rtc-sctp/src/RTCSctpTransport.phpxõ  ’¿Ýjxõ  i‹m¤      >   vendor/danog/php-rtc-sctp/src/Param/StreamAddOutgoingParam.php  ’¿Ýj  ›�gç¤      @   vendor/danog/php-rtc-sctp/src/Param/StreamResetResponseParam.php
  ’¿Ýj
  FVÿÐ¤      @   vendor/danog/php-rtc-sctp/src/Param/StreamResetOutgoingParam.php¢  ’¿Ýj¢  ég‚¤      <   vendor/danog/php-rtc-sctp/src/Param/StreamParamInterface.phpë  ’¿Ýjë  h‘�¤      ?   vendor/danog/php-rtc-sctp/src/RTCSctpDtlsTransportInterface.php%  ’¿Ýj%  ë„øì¤      ,   vendor/danog/php-rtc-sctp/src/Enum/State.phpK  ’¿ÝjK  Oä�°¤      '   vendor/danog/php-rtc-sctp/composer.json1  ’¿Ýj1  &Á§¤      %   vendor/danog/php-rtc-srtp/src/Aes.phpS  ’¿ÝjS  ¾¢¤      9   vendor/danog/php-rtc-srtp/src/Exception/SrtpException.php†  ’¿Ýj†  žsªR¤      A   vendor/danog/php-rtc-srtp/src/Exception/SrtpValidateException.php®	  ’¿Ýj®	  #A’¤      B   vendor/danog/php-rtc-srtp/src/Exception/SrtpExceptionInterface.php   ’¿Ýj   €oŽ¤      )   vendor/danog/php-rtc-srtp/src/Session.php;…  ’¿Ýj;…  ¾ÓëO¤      (   vendor/danog/php-rtc-srtp/src/Policy.phpO  ’¿ÝjO  ƒÛ¤\¤      2   vendor/danog/php-rtc-srtp/src/Enum/SrtpProfile.php«  ’¿Ýj«  Ù1ÍZ¤      /   vendor/danog/php-rtc-srtp/src/Enum/SsrcType.php|  ’¿Ýj|  ä¤      '   vendor/danog/php-rtc-srtp/composer.jsonÉ  ’¿ÝjÉ  ©e¤      /   vendor/danog/async-orm/examples/1-automatic.php!  ’¿Ýj!  äþû¤      ,   vendor/danog/async-orm/examples/2-manual.php˜  ’¿Ýj˜  ˜mŒÉ¤      -   vendor/danog/async-orm/.php-cs-fixer.dist.php.  ’¿Ýj.  ¯^}¤      '   vendor/danog/async-orm/src/DbObject.php„	  ’¿Ýj„	  êMƒ˜¤      0   vendor/danog/async-orm/src/Serializer/Native.phpà  ’¿Ýjà  óò×™¤      .   vendor/danog/async-orm/src/Serializer/Json.php�  ’¿Ýj�  XÔÀŒ¤      2   vendor/danog/async-orm/src/Serializer/Igbinary.phpö  ’¿Ýjö  öËäY¤      '   vendor/danog/async-orm/src/Settings.phpD  ’¿ÝjD  Ž×Ô¤      /   vendor/danog/async-orm/src/DbAutoProperties.phpg  ’¿Ýjg  ug�Ö¤      )   vendor/danog/async-orm/src/Serializer.phpA  ’¿ÝjA  a"è¤      .   vendor/danog/async-orm/src/Driver/SqlArray.php±  ’¿Ýj±  (cÈþ¤      1   vendor/danog/async-orm/src/Driver/DriverArray.phpƒ  ’¿Ýjƒ  Î(e„¤      1   vendor/danog/async-orm/src/Driver/MemoryArray.phpÙ  ’¿ÝjÙ  á·Ÿ¤      (   vendor/danog/async-orm/src/ValueType.php”  ’¿Ýj”  ‚g—‚¤      &   vendor/danog/async-orm/src/DbArray.phpó  ’¿Ýjó  2sg�¤      >   vendor/danog/async-orm/src/Internal/Serializer/Passthrough.phpG  ’¿ÝjG  Š=‡Æ¤      =   vendor/danog/async-orm/src/Internal/Serializer/BoolString.phpŸ  ’¿ÝjŸ  ÄR³¥¤      B   vendor/danog/async-orm/src/Internal/Serializer/ByteaSerializer.php—  ’¿Ýj—  šé³¤      >   vendor/danog/async-orm/src/Internal/Serializer/FloatString.phpâ  ’¿Ýjâ  <rrÜ¤      <   vendor/danog/async-orm/src/Internal/Serializer/IntString.phpÞ  ’¿ÝjÞ  H0M¤      :   vendor/danog/async-orm/src/Internal/Serializer/BoolInt.phpÛ  ’¿ÝjÛ  õ7É¤      A   vendor/danog/async-orm/src/Internal/Containers/CacheContainer.php|  ’¿Ýj|  (yR>¤      B   vendor/danog/async-orm/src/Internal/Containers/ObjectContainer.php%  ’¿Ýj%  PË46¤      B   vendor/danog/async-orm/src/Internal/Containers/ObjectReference.php¶  ’¿Ýj¶  �v`(¤      :   vendor/danog/async-orm/src/Internal/Driver/CachedArray.php¢  ’¿Ýj¢  �i¸[¤      <   vendor/danog/async-orm/src/Internal/Driver/PostgresArray.phpÄ  ’¿ÝjÄ  ·WßÆ¤      :   vendor/danog/async-orm/src/Internal/Driver/ObjectArray.php/  ’¿Ýj/  -Uª§¤      9   vendor/danog/async-orm/src/Internal/Driver/RedisArray.phpR  ’¿ÝjR  5ñ7¤      9   vendor/danog/async-orm/src/Internal/Driver/MysqlArray.php#%  ’¿Ýj#%  Á¸¤      9   vendor/danog/async-orm/src/Annotations/OrmMappedArray.phpÊ  ’¿ÝjÊ  s²™ç¤      &   vendor/danog/async-orm/src/KeyType.php—  ’¿Ýj—  lR&ä¤      6   vendor/danog/async-orm/src/Settings/DriverSettings.php¿  ’¿Ýj¿  ûÂ¤      3   vendor/danog/async-orm/src/Settings/SqlSettings.phpÏ	  ’¿ÝjÏ	  ÀÐA·¤      8   vendor/danog/async-orm/src/Settings/PostgresSettings.php  ’¿Ýj  8ÜÒ>¤      6   vendor/danog/async-orm/src/Settings/MemorySettings.phpÊ  ’¿ÝjÊ  =è¤      5   vendor/danog/async-orm/src/Settings/RedisSettings.phpØ  ’¿ÝjØ  Ék<¤      5   vendor/danog/async-orm/src/Settings/MysqlSettings.php
  ’¿Ýj
  É=¤      -   vendor/danog/async-orm/src/DbArrayBuilder.php  ’¿Ýj  ®ÔÜ¤      $   vendor/danog/async-orm/composer.json�  ’¿Ýj�  ËÏO¤      3   vendor/danog/php-rtc-turn/src/TurnTcpConnection.php¥  ’¿Ýj¥  ( ¤      9   vendor/danog/php-rtc-turn/src/TurnConnectionInterface.phpú  ’¿Ýjú  NX�µ¤      <   vendor/danog/php-rtc-turn/src/TurnConfigurationInterface.phpx  ’¿Ýjx  ëXN*¤      3   vendor/danog/php-rtc-turn/src/TurnUdpConnection.phpÀ  ’¿ÝjÀ  Åtôi¤      6   vendor/danog/php-rtc-turn/src/Trait/TurnConnection.php,a  ’¿Ýj,a  qZ‰¤      /   vendor/danog/php-rtc-turn/src/TCPConnection.php]  ’¿Ýj]  ¤—¡¤      /   vendor/danog/php-rtc-turn/src/TurnInterface.phpW  ’¿ÝjW  ÚæR¤      &   vendor/danog/php-rtc-turn/src/Turn.phpM   ’¿ÝjM   ¼•Ãn¤      '   vendor/danog/php-rtc-turn/composer.json•  ’¿Ýj•  îiÙ¤      -   vendor/danog/tg-dialog-id/examples/1-type.phpv	  ’¿Ýjv	  †üY¤      0   vendor/danog/tg-dialog-id/.php-cs-fixer.dist.phpÑ  ’¿ÝjÑ   Jðo¤      /   vendor/danog/tg-dialog-id/test/DialogIdTest.phpÄ  ’¿ÝjÄ  ²Dhk¤      *   vendor/danog/tg-dialog-id/src/DialogId.php¹!  ’¿Ýj¹!  �Ì+ˆ¤      '   vendor/danog/tg-dialog-id/composer.jsonˆ  ’¿Ýjˆ  âÓOÒ¤      1   vendor/danog/php-rtc-av/ffmpeg/include/avfilter.h•N  ’¿Ýj•N  1‰z•¤      0   vendor/danog/php-rtc-av/ffmpeg/include/avcodec.hx¶  ’¿Ýjx¶  a^.Ô¤      1   vendor/danog/php-rtc-av/ffmpeg/include/avformat.hzm  ’¿Ýjzm  J¢’Ð¤      0   vendor/danog/php-rtc-av/ffmpeg/include/swscale.h½  ’¿Ýj½  å×¼¤      5   vendor/danog/php-rtc-av/scripts/ffmpeg-downloader.php  ’¿Ýj  �Út¤      3   vendor/danog/php-rtc-av/src/TransCoderInterface.phpþ  ’¿Ýjþ  ±À
¤      4   vendor/danog/php-rtc-av/src/Audio/AudioResampler.php  ’¿Ýj  ºLÐ�¤      2   vendor/danog/php-rtc-av/src/Audio/AudioChannel.php  ’¿Ýj  m®£¡¤      1   vendor/danog/php-rtc-av/src/Audio/AudioLayout.php^  ’¿Ýj^  Â¢¤      (   vendor/danog/php-rtc-av/src/AVFilter.php  ’¿Ýj  :ÊÙ¤      '   vendor/danog/php-rtc-av/src/AVCodec.php  ’¿Ýj  Ê@•ª¤      '   vendor/danog/php-rtc-av/src/SWScale.php\  ’¿Ýj\  ¢¥º·¤      :   vendor/danog/php-rtc-av/src/Exception/AvCodecException.php{  ’¿Ýj{  Ìo–ä¤      (   vendor/danog/php-rtc-av/src/AVFormat.phpJ  ’¿ÝjJ  ¤×Â¤      4   vendor/danog/php-rtc-av/src/Data/BufferInterface.phpv  ’¿Ýjv  �¼@¤      /   vendor/danog/php-rtc-av/src/Data/VideoPlane.php¿  ’¿Ýj¿  …³ t¤      +   vendor/danog/php-rtc-av/src/Data/Buffer.phph  ’¿Ýjh  æxl ¤      +   vendor/danog/php-rtc-av/src/Data/Packet.php·  ’¿Ýj·  Õ‚Ÿ¤      /   vendor/danog/php-rtc-av/src/Data/AudioPlane.phpc  ’¿Ýjc  ‘!¤      ,   vendor/danog/php-rtc-av/src/Filter/Graph.phpŒ   ’¿ÝjŒ   ÕM4Ì¤      -   vendor/danog/php-rtc-av/src/Filter/Filter.phpX  ’¿ÝjX  æf¤      4   vendor/danog/php-rtc-av/src/Filter/FilterContext.php.  ’¿Ýj.  Gÿ¬c¤      *   vendor/danog/php-rtc-av/src/TransCoder.phpi#  ’¿Ýji#  ‘9Åƒ¤      )   vendor/danog/php-rtc-av/src/Util/Util.phpÎ  ’¿ÝjÎ  ÔA¤      4   vendor/danog/php-rtc-av/src/Context/AudioContext.php  ’¿Ýj  ëméÒ¤      8   vendor/danog/php-rtc-av/src/Context/ContextInterface.phpŒ  ’¿ÝjŒ  X•¤      4   vendor/danog/php-rtc-av/src/Context/VideoContext.php»  ’¿Ýj»  ]`<¤      2   vendor/danog/php-rtc-av/src/Context/Dictionary.phpý  ’¿Ýjý  Û¨b¤      /   vendor/danog/php-rtc-av/src/Context/Context.php*4  ’¿Ýj*4  [Ö1¦¤      0   vendor/danog/php-rtc-av/src/Enum/PictureType.phpG  ’¿ÝjG  	Á‘¤      /   vendor/danog/php-rtc-av/src/Enum/Colorspace.phpÝ  ’¿ÝjÝ  Ø¡š¤      2   vendor/danog/php-rtc-av/src/Enum/Interpolation.php  ’¿Ýj  WÀd}¤      /   vendor/danog/php-rtc-av/src/Enum/ColorRange.php•  ’¿Ýj•  Z-Áä¤      4   vendor/danog/php-rtc-av/src/libav/include/avfilter.h•N  ’¿Ýj•N  1‰z•¤      3   vendor/danog/php-rtc-av/src/libav/include/avcodec.hÍ¶  ’¿ÝjÍ¶  Ïï”Š¤      4   vendor/danog/php-rtc-av/src/libav/include/avformat.hzm  ’¿Ýjzm  J¢’Ð¤      3   vendor/danog/php-rtc-av/src/libav/include/swscale.h½  ’¿Ýj½  å×¼¤      :   vendor/danog/php-rtc-av/src/Frame/VideoFrameReformater.phpM!  ’¿ÝjM!   ‚Œ°¤      0   vendor/danog/php-rtc-av/src/Frame/VideoFrame.php“(  ’¿Ýj“(  Ø5£$¤      +   vendor/danog/php-rtc-av/src/Frame/Frame.phpv  ’¿Ýjv  ý[-'¤      5   vendor/danog/php-rtc-av/src/Frame/OpaqueContainer.phpÈ
  ’¿ÝjÈ
  WÄ¤      0   vendor/danog/php-rtc-av/src/Frame/AudioFrame.php‹  ’¿Ýj‹  é¿r˜¤      4   vendor/danog/php-rtc-av/src/Frame/FrameInterface.php¯  ’¿Ýj¯  f!ã¬¤      .   vendor/danog/php-rtc-av/src/LibraryVersion.php)  ’¿Ýj)  rbp1¤      %   vendor/danog/php-rtc-av/src/Codec.php„'  ’¿Ýj„'  ÃÔx’¤      2   vendor/danog/php-rtc-av/src/Format/AudioFormat.phpÈ  ’¿ÝjÈ  þ*Ù’¤      2   vendor/danog/php-rtc-av/src/Format/VideoFormat.phpí  ’¿Ýjí  ü�¢"¤      ;   vendor/danog/php-rtc-av/src/Format/VideoFormatComponent.phpÀ  ’¿ÝjÀ  >ð
¤      %   vendor/danog/php-rtc-av/composer.json	  ’¿Ýj	  „àºù¤      +   vendor/danog/loop/examples/PeriodicLoop.phpÂ  ’¿ÝjÂ  Mº¤      #   vendor/danog/loop/examples/Loop.php’  ’¿Ýj’  wQáÂ¤      *   vendor/danog/loop/examples/GenericLoop.phpÓ  ’¿ÝjÓ  èþ�¶¤      (   vendor/danog/loop/.php-cs-fixer.dist.phpÑ  ’¿ÝjÑ  �G·Û¤      &   vendor/danog/loop/lib/PeriodicLoop.phpO  ’¿ÝjO  PÉø«¤         vendor/danog/loop/lib/Loop.phpà  ’¿Ýjà  ÷§tp¤      %   vendor/danog/loop/lib/GenericLoop.phpL  ’¿ÝjL  îz¸¤      #   vendor/danog/loop/test/Fixtures.phpã  ’¿Ýjã  ãJ…¤      0   vendor/danog/loop/test/Traits/BasicException.php  ’¿Ýj  Xdp¤      )   vendor/danog/loop/test/Traits/Logging.php$  ’¿Ýj$  *Õ?B¤      .   vendor/danog/loop/test/Traits/LoggingPause.php3  ’¿Ýj3  ¥«Ž­¤      '   vendor/danog/loop/test/Traits/Basic.php†  ’¿Ýj†  æÐ ¤      &   vendor/danog/loop/test/GenericTest.phpå/  ’¿Ýjå/  „½\¨¤      #   vendor/danog/loop/test/LoopTest.php«  ’¿Ýj«  3ÑLs¤      '   vendor/danog/loop/test/PeriodicTest.php
  ’¿Ýj
  ”êŠ¤      7   vendor/danog/loop/test/Interfaces/IntervalInterface.php%  ’¿Ýj%  ÷a´¤      ;   vendor/danog/loop/test/Interfaces/LoggingPauseInterface.php\  ’¿Ýj\  “s¨C¤      6   vendor/danog/loop/test/Interfaces/LoggingInterface.php(  ’¿Ýj(  ˆI,œ¤      4   vendor/danog/loop/test/Interfaces/BasicInterface.phpK  ’¿ÝjK  ùMÓ�¤         vendor/danog/loop/composer.jsonƒ  ’¿Ýjƒ  ç3š@¤      ?   vendor/danog/php-rtc-exception/src/InvalidArgumentException.php”  ’¿Ýj”  L�=ä¤      >   vendor/danog/php-rtc-exception/src/NotImplementedException.php„  ’¿Ýj„  ÊÑk¤      ?   vendor/danog/php-rtc-exception/src/WebrtcExceptionInterface.php_  ’¿Ýj_  D¼ýz¤      7   vendor/danog/php-rtc-exception/src/RuntimeException.php„  ’¿Ýj„  APËö¤      0   vendor/danog/php-rtc-exception/src/Exception.php  ’¿Ýj  ü%1á¤      ,   vendor/danog/php-rtc-exception/composer.json¶  ’¿Ýj¶  tYªL¤      /   vendor/danog/libdns-json/.php-cs-fixer.dist.php.  ’¿Ýj.  Hªb—¤      3   vendor/danog/libdns-json/lib/JsonDecoderFactory.phpç  ’¿Ýjç  A¶çâ¤      -   vendor/danog/libdns-json/lib/QueryEncoder.phpü  ’¿Ýjü  Ÿ¤w¤      ,   vendor/danog/libdns-json/lib/JsonDecoder.phpO.  ’¿ÝjO.  UøAÿ¤      4   vendor/danog/libdns-json/lib/QueryEncoderFactory.php  ’¿Ýj  •¢\¤      8   vendor/danog/libdns-json/test/JsonDecoderFactoryTest.php^  ’¿Ýj^  q†@�¤      9   vendor/danog/libdns-json/test/QueryEncoderFactoryTest.phpd  ’¿Ýjd  Lµ[L¤      2   vendor/danog/libdns-json/test/QueryEncoderTest.php?  ’¿Ýj?  eó¤      1   vendor/danog/libdns-json/test/JsonDecoderTest.phps  ’¿Ýjs  }ûÍ¤      &   vendor/danog/libdns-json/composer.jsonW  ’¿ÝjW  ¢Ÿö”¤      (   vendor/danog/php-rtc-sdp/src/H264Sdp.phpû  ’¿Ýjû  •U ¦¤      1   vendor/danog/php-rtc-sdp/src/MediaDescription.phpçI  ’¿ÝjçI  £€¦¤      B   vendor/danog/php-rtc-sdp/src/SctpParameter/RTCSctpCapabilities.php�  ’¿Ýj�  ËÓ%¤      3   vendor/danog/php-rtc-sdp/src/SessionDescription.php.]  ’¿Ýj.]  ÿ{Z¤      @   vendor/danog/php-rtc-sdp/src/DtlsParameter/RTCDtlsParameters.phpg  ’¿Ýjg  y¸d!¤      A   vendor/danog/php-rtc-sdp/src/DtlsParameter/RTCDtlsFingerprint.phpQ  ’¿ÝjQ  ›Ûƒ%¤      +   vendor/danog/php-rtc-sdp/src/SDPUtility.php¹  ’¿Ýj¹  ¼­!¤      1   vendor/danog/php-rtc-sdp/src/GroupDescription.php_  ’¿Ýj_  !®Õ¤      6   vendor/danog/php-rtc-sdp/src/RTCSessionDescription.php$
  ’¿Ýj$
  SÁù@¤      +   vendor/danog/php-rtc-sdp/src/BitPattern.php	  ’¿Ýj	  MÔ¤      1   vendor/danog/php-rtc-sdp/src/Enum/H264Profile.php  ’¿Ýj  iÍÓp¤      /   vendor/danog/php-rtc-sdp/src/Enum/H264Level.phpÉ  ’¿ÝjÉ  mÿh¤      3   vendor/danog/php-rtc-sdp/src/Enum/SDPDirections.php  ’¿Ýj  öº ¤      .   vendor/danog/php-rtc-sdp/src/Enum/DtlsRole.php�  ’¿Ýj�  t¿Ù²¤      -   vendor/danog/php-rtc-sdp/src/Enum/SDPType.php{  ’¿Ýj{  -¾¤      0   vendor/danog/php-rtc-sdp/src/SsrcDescription.phpN  ’¿ÝjN  ?1x¾¤      &   vendor/danog/php-rtc-sdp/composer.jsonõ  ’¿Ýjõ  a*m¤      1   vendor/danog/better-prometheus/examples/1-all.php«  ’¿Ýj«  ­uY‚¤      5   vendor/danog/better-prometheus/.php-cs-fixer.dist.php  ’¿Ýj  ’c|¤      6   vendor/danog/better-prometheus/lib/BetterHistogram.php  ’¿Ýj  �ü¤      4   vendor/danog/better-prometheus/lib/BetterSummary.php„  ’¿Ýj„  Ïhn¤      6   vendor/danog/better-prometheus/lib/BetterCollector.php¡  ’¿Ýj¡  äpªÙ¤      >   vendor/danog/better-prometheus/lib/BetterCollectorRegistry.phpë)  ’¿Ýjë)  û7ÔÏ¤      4   vendor/danog/better-prometheus/lib/BetterCounter.phpÏ  ’¿ÝjÏ  äÃÐ¤¤      2   vendor/danog/better-prometheus/lib/BetterGauge.php!
  ’¿Ýj!
  TM¿ã¤      ,   vendor/danog/better-prometheus/composer.jsonð  ’¿Ýjð  )õG|¤      0   vendor/danog/php-rtc-codecs/src/CodecUtility.php‚	  ’¿Ýj‚	  g�¤      >   vendor/danog/php-rtc-codecs/src/PayloadDescriptorInterface.php©  ’¿Ýj©  ;×†¤      9   vendor/danog/php-rtc-codecs/src/Audio/PCM/PCMaDecoder.php›  ’¿Ýj›  s�œ¤      8   vendor/danog/php-rtc-codecs/src/Audio/PCM/PCMEncoder.phpÞ  ’¿ÝjÞ  ì;©·¤      8   vendor/danog/php-rtc-codecs/src/Audio/PCM/PCMDecoder.phpÊ  ’¿ÝjÊ  Nö�¿¤      9   vendor/danog/php-rtc-codecs/src/Audio/PCM/PCMuDecoder.php  ’¿Ýj  ]£a¤      9   vendor/danog/php-rtc-codecs/src/Audio/PCM/PCMuEncoder.php>  ’¿Ýj>  `ÏX&¤      9   vendor/danog/php-rtc-codecs/src/Audio/PCM/PCMaEncoder.php  ’¿Ýj  ¿N�w¤      :   vendor/danog/php-rtc-codecs/src/Audio/Opus/OpusDecoder.phpf  ’¿Ýjf  !HM�¤      :   vendor/danog/php-rtc-codecs/src/Audio/Opus/OpusEncoder.php4  ’¿Ýj4  ÉtÌý¤      8   vendor/danog/php-rtc-codecs/src/JitterFrameInterface.php•  ’¿Ýj•  !î]¤      +   vendor/danog/php-rtc-codecs/src/Encoder.php	  ’¿Ýj	  ÀzŽL¤      4   vendor/danog/php-rtc-codecs/src/EncoderInterface.phpX  ’¿ÝjX  Õ ¤      1   vendor/danog/php-rtc-codecs/src/EncodedPacket.phpa  ’¿Ýja  	˜_K¤      :   vendor/danog/php-rtc-codecs/src/Video/X264/H264Encoder.phpˆ3  ’¿Ýjˆ3  ²FŒ�¤      D   vendor/danog/php-rtc-codecs/src/Video/X264/H264PayloadDescriptor.php$  ’¿Ýj$  bé�b¤      :   vendor/danog/php-rtc-codecs/src/Video/X264/H264Decoder.php'
  ’¿Ýj'
  Dñ¼¤      B   vendor/danog/php-rtc-codecs/src/Video/Vp8/Vp8PayloadDescriptor.phpP(  ’¿ÝjP(  inZ¤      8   vendor/danog/php-rtc-codecs/src/Video/Vp8/Vp8Encoder.phpj!  ’¿Ýjj!  éÎë¤      8   vendor/danog/php-rtc-codecs/src/Video/Vp8/Vp8Decoder.phpO	  ’¿ÝjO	  ~�AI¤      8   vendor/danog/php-rtc-codecs/src/Video/Vp9/Vp9Encoder.php  ’¿Ýj  í‡¨á¤      B   vendor/danog/php-rtc-codecs/src/Video/Vp9/Vp9PayloadDescriptor.phpm>  ’¿Ýjm>  mŽŽ™¤      8   vendor/danog/php-rtc-codecs/src/Video/Vp9/Vp9Decoder.phpV	  ’¿ÝjV	  jÜµø¤      B   vendor/danog/php-rtc-codecs/src/Video/Av1/Av1PayloadDescriptor.php»  ’¿Ýj»  ‚¯½¤      8   vendor/danog/php-rtc-codecs/src/Video/Av1/Av1Encoder.phpÎ"  ’¿ÝjÎ"  oBe¤      :   vendor/danog/php-rtc-codecs/src/Video/H265/H265Encoder.phpB  ’¿ÝjB  ¦V7Ø¤      D   vendor/danog/php-rtc-codecs/src/Video/H265/H265PayloadDescriptor.php(  ’¿Ýj(  \éœ¤      4   vendor/danog/php-rtc-codecs/src/DecoderInterface.php€  ’¿Ýj€  ÿd5ò¤      )   vendor/danog/php-rtc-codecs/src/Codec.phpÞD  ’¿ÝjÞD  ¾¤      )   vendor/danog/php-rtc-codecs/composer.jsoní  ’¿Ýjí  àÍ?a¤      3   vendor/danog/tg-file-decoder/.php-cs-fixer.dist.php  ’¿Ýj  ƒÍ£¤      1   vendor/danog/tg-file-decoder/src/UniqueFileId.php  ’¿Ýj  n;ïô¤      /   vendor/danog/tg-file-decoder/src/FileIdType.phpì  ’¿Ýjì  ¶¼„h¤      *   vendor/danog/tg-file-decoder/src/Tools.php÷  ’¿Ýj÷  s½ x¤      M   vendor/danog/tg-file-decoder/src/PhotoSizeSource/PhotoSizeSourceThumbnail.phpÃ  ’¿ÝjÃ  õôqS¤      O   vendor/danog/tg-file-decoder/src/PhotoSizeSource/PhotoSizeSourceDialogPhoto.php²  ’¿Ýj²  �ê:¤      J   vendor/danog/tg-file-decoder/src/PhotoSizeSource/PhotoSizeSourceLegacy.phpê  ’¿Ýjê  ÐI�¤      T   vendor/danog/tg-file-decoder/src/PhotoSizeSource/PhotoSizeSourceDialogPhotoSmall.php?  ’¿Ýj?  ¬O×¤      ^   vendor/danog/tg-file-decoder/src/PhotoSizeSource/PhotoSizeSourceStickersetThumbnailVersion.php  ’¿Ýj  
…|{¤      W   vendor/danog/tg-file-decoder/src/PhotoSizeSource/PhotoSizeSourceStickersetThumbnail.phpª  ’¿Ýjª  Eƒü;¤      R   vendor/danog/tg-file-decoder/src/PhotoSizeSource/PhotoSizeSourceDialogPhotoBig.php>  ’¿Ýj>  "ÙQ•¤      4   vendor/danog/tg-file-decoder/src/PhotoSizeSource.phpE  ’¿ÝjE  P^„¤      5   vendor/danog/tg-file-decoder/src/UniqueFileIdType.phpß  ’¿Ýjß  ŽqVk¤      +   vendor/danog/tg-file-decoder/src/FileId.php3  ’¿Ýj3  ÃÈ¨ï¤      8   vendor/danog/tg-file-decoder/src/PhotoSizeSourceType.php‘  ’¿Ýj‘  �¿‰¤      *   vendor/danog/tg-file-decoder/composer.jsonü  ’¿Ýjü  ]ƒÐ¤      1   vendor/danog/telegram-entities/examples/1-all.phpË
  ’¿ÝjË
  êäöR¤      5   vendor/danog/telegram-entities/.php-cs-fixer.dist.php.  ’¿Ýj.  ¯^}¤      2   vendor/danog/telegram-entities/src/EntityTools.phpm  ’¿Ýjm  B>R¤      /   vendor/danog/telegram-entities/src/Entities.php¶f  ’¿Ýj¶f  cæ�Ê¤      ,   vendor/danog/telegram-entities/composer.json»  ’¿Ýj»  ¸ëà)¤      V   vendor/danog/php-rtc-datachannel/src/Listener/DataChannelBufferedAmountLowListener.php£  ’¿Ýj£  �Ím�¤      I   vendor/danog/php-rtc-datachannel/src/Listener/DataChannelOpenListener.phpn  ’¿Ýjn  ^°Ï]¤      L   vendor/danog/php-rtc-datachannel/src/Listener/DataChannelMessageListener.php–  ’¿Ýj–  n†Å9¤      J   vendor/danog/php-rtc-datachannel/src/Listener/DataChannelCloseListener.phps  ’¿Ýjs  ×ËÛ‘¤      A   vendor/danog/php-rtc-datachannel/src/RTCDataChannelParameters.phpò  ’¿Ýjò  ‡èô4¤      7   vendor/danog/php-rtc-datachannel/src/RTCDataChannel.phpeE  ’¿ÝjeE  »ÂæÉ¤      @   vendor/danog/php-rtc-datachannel/src/RTCDataChannelInterface.phpM  ’¿ÝjM  š/¤      3   vendor/danog/php-rtc-datachannel/src/Enum/State.php�  ’¿Ýj�  X%“w¤      B   vendor/danog/php-rtc-datachannel/src/RTCSctpTransportInterface.phpÜ  ’¿ÝjÜ  Æ�û¤      .   vendor/danog/php-rtc-datachannel/composer.jsonì  ’¿Ýjì  ê�ØÀ¤      >   vendor/danog/php-rtc-webrtc/src/RTCPeerConnectionInterface.php1  ’¿Ýj1  `àð¤      5   vendor/danog/php-rtc-webrtc/src/RTCPeerConnection.php%¥ ’¿Ýj%¥ ur#Q¤      N   vendor/danog/php-rtc-webrtc/src/Listener/PeerConnectionDataChannelListener.php<  ’¿Ýj<  C�R¤      H   vendor/danog/php-rtc-webrtc/src/Listener/PeerConnectionTrackListener.php+  ’¿Ýj+  ØÊô�¤      W   vendor/danog/php-rtc-webrtc/src/Listener/PeerConnectionSignalingStateChangeListener.php  ’¿Ýj  iÖƒN¤      Z   vendor/danog/php-rtc-webrtc/src/Listener/PeerConnectionIceGatheringStateChangeListener.php  ’¿Ýj  j†ùu¤      X   vendor/danog/php-rtc-webrtc/src/Listener/PeerConnectionConnectionStateChangeListener.php  ’¿Ýj  ·áÃ¤      [   vendor/danog/php-rtc-webrtc/src/Listener/PeerConnectionIceConnectionStateChangeListener.php  ’¿Ýj  Øèp¤      4   vendor/danog/php-rtc-webrtc/src/RTCConfiguration.php  ’¿Ýj  m$¤      =   vendor/danog/php-rtc-webrtc/src/RTCConfigurationInterface.php.  ’¿Ýj.  }Ååé¤      7   vendor/danog/php-rtc-webrtc/src/Enum/SignalingState.phpê  ’¿Ýjê  ˆØìÊ¤      ;   vendor/danog/php-rtc-webrtc/src/Enum/IceConnectionState.php³  ’¿Ýj³  yÇ¤      8   vendor/danog/php-rtc-webrtc/src/Enum/ConnectionState.php²  ’¿Ýj²  @T ¤      )   vendor/danog/php-rtc-webrtc/composer.json  ’¿Ýj  ‰f:¤      C   vendor/danog/php-rtc-rtp/src/MediaStreamTrack/RemoteStreamTrack.phpÖ  ’¿ÝjÖ  ×zåQ¤      B   vendor/danog/php-rtc-rtp/src/MediaStreamTrack/MediaStreamTrack.php¢  ’¿Ýj¢  "[dŠ¤      F   vendor/danog/php-rtc-rtp/src/MediaStreamTrack/MediaStreamTrackSink.php1  ’¿Ýj1  mì�¤      ?   vendor/danog/php-rtc-rtp/src/MediaStreamTrack/RTCTrackEvent.phpÙ  ’¿ÝjÙ  ÞE4¤      3   vendor/danog/php-rtc-rtp/src/RtpComponentLogger.php/  ’¿Ýj/  ràø©¤      :   vendor/danog/php-rtc-rtp/src/Sender/RtpSenderInterface.phpÛ  ’¿ÝjÛ  ×¦H¤      4   vendor/danog/php-rtc-rtp/src/Sender/RTCRtpSender.php£‘  ’¿Ýj£‘  dÇ£l¤      7   vendor/danog/php-rtc-rtp/src/Sender/RTCEncodedFrame.php’  ’¿Ýj’  G†Ao¤      G   vendor/danog/php-rtc-rtp/src/Listener/MediaStreamTrackEndedListener.phpi  ’¿Ýji  -Ç¹T¤      D   vendor/danog/php-rtc-rtp/src/HeaderExtension/HeaderExtensionsMap.php¿   ’¿Ýj¿   ëÆÚ¤      A   vendor/danog/php-rtc-rtp/src/HeaderExtension/HeaderExtensions.php  ’¿Ýj  ÀÅ‰)¤      D   vendor/danog/php-rtc-rtp/src/Exception/MediaStreamTrackException.phpˆ  ’¿Ýjˆ  Bû®-¤      @   vendor/danog/php-rtc-rtp/src/Exception/RtpExceptionInterface.phpž  ’¿Ýjž  '»>¤      =   vendor/danog/php-rtc-rtp/src/Exception/RtpSenderException.php�  ’¿Ýj�  'a$·¤      =   vendor/danog/php-rtc-rtp/src/Exception/RtpPacketException.php�  ’¿Ýj�  n¶å´¤      9   vendor/danog/php-rtc-rtp/src/TransportSequenceNumbers.phpÊ  ’¿ÝjÊ  8K@T¤      =   vendor/danog/php-rtc-rtp/src/Crypto/FrameCryptorInterface.php8  ’¿Ýj8  äÐ²6¤      -   vendor/danog/php-rtc-rtp/src/RtpConstants.phpA  ’¿ÝjA  Ø£Y0¤      2   vendor/danog/php-rtc-rtp/src/RTCRtpTransceiver.php9  ’¿Ýj9  ê	—¤      *   vendor/danog/php-rtc-rtp/src/RtpPacket.phpD5  ’¿ÝjD5  ™øÓ§¤      >   vendor/danog/php-rtc-rtp/src/Receiver/RtpReceiverInterface.phpG  ’¿ÝjG  ¡®ê¤      6   vendor/danog/php-rtc-rtp/src/Receiver/DecoderQueue.phpI  ’¿ÝjI  Œ%�¤      :   vendor/danog/php-rtc-rtp/src/Receiver/StreamStatistics.php;  ’¿Ýj;  ¸�þ•¤      9   vendor/danog/php-rtc-rtp/src/Receiver/TimestampMapper.php“  ’¿Ýj“  ¯MV9¤      @   vendor/danog/php-rtc-rtp/src/Receiver/Rate/InterArrivalDelta.phpU  ’¿ÝjU  *sã¤      9   vendor/danog/php-rtc-rtp/src/Receiver/Rate/RateBucket.phps  ’¿Ýjs  °}Í¤      ?   vendor/danog/php-rtc-rtp/src/Receiver/Rate/OveruseEstimator.php  ’¿Ýj  ö.(¤      >   vendor/danog/php-rtc-rtp/src/Receiver/Rate/OveruseDetector.php´  ’¿Ýj´  ¶2Á¤      E   vendor/danog/php-rtc-rtp/src/Receiver/Rate/RemoteBitrateEstimator.php•  ’¿Ýj•  Lõ¡\¤      :   vendor/danog/php-rtc-rtp/src/Receiver/Rate/RateCounter.phpÌ  ’¿ÝjÌ  óKú„¤      ;   vendor/danog/php-rtc-rtp/src/Receiver/Rate/InterArrival.phpR  ’¿ÝjR  t~äç¤      =   vendor/danog/php-rtc-rtp/src/Receiver/Rate/TimestampGroup.phpñ  ’¿Ýjñ  
Ñ¹'¤      >   vendor/danog/php-rtc-rtp/src/Receiver/Rate/AimdRateControl.php$.  ’¿Ýj$.  §L¤      7   vendor/danog/php-rtc-rtp/src/Receiver/NackGenerator.phpŠ  ’¿ÝjŠ  <C‚æ¤      8   vendor/danog/php-rtc-rtp/src/Receiver/RTCRtpReceiver.phpçw  ’¿Ýjçw  —Vy¤      4   vendor/danog/php-rtc-rtp/src/Enum/BandwidthUsage.phpŠ  ’¿ÝjŠ  Q‰†¤      /   vendor/danog/php-rtc-rtp/src/Enum/MediaKind.php�  ’¿Ýj�   ?Y€¤      6   vendor/danog/php-rtc-rtp/src/Enum/RateControlState.php‡  ’¿Ýj‡  Ç=ö¤¤      *   vendor/danog/php-rtc-rtp/src/RtpRouter.phph  ’¿Ýjh  ¢fñˆ¤      +   vendor/danog/php-rtc-rtp/src/RtpUtility.php€+  ’¿Ýj€+  qý’u¤      =   vendor/danog/php-rtc-rtp/src/RTCRTPDtlsTransportInterface.phpw  ’¿Ýjw  z�¤      3   vendor/danog/php-rtc-rtp/src/Jitter/JitterFrame.php-  ’¿Ýj-  ê¥7¤      4   vendor/danog/php-rtc-rtp/src/Jitter/JitterBuffer.php'  ’¿Ýj'  Íyã†¤      &   vendor/danog/php-rtc-rtp/composer.json¶  ’¿Ýj¶  ¾Ó|¤      *   vendor/danog/php-rtc-dtls/src/DTLS/Prf.php  ’¿Ýj  m=´¤      -   vendor/danog/php-rtc-dtls/src/DTLS/Reader.phpA  ’¿ÝjA  ²~¤      2   vendor/danog/php-rtc-dtls/src/DTLS/RecordLayer.phpX%  ’¿ÝjX%  ùWDò¤      3   vendor/danog/php-rtc-dtls/src/DTLS/EcPrivateKey.phpÀ  ’¿ÝjÀ  ªâÀÌ¤      .   vendor/danog/php-rtc-dtls/src/DTLS/TLS/TLS.phpy+  ’¿Ýjy+  öÿW¤      4   vendor/danog/php-rtc-dtls/src/DTLS/TLS/Handshake.phpØ-  ’¿ÝjØ-  ]Ù¬¤      7   vendor/danog/php-rtc-dtls/src/DTLS/RTCDtlsTransport.phpÜh  ’¿ÝjÜh  €,{¤      >   vendor/danog/php-rtc-dtls/src/DTLS/Exception/DTLSException.php‚  ’¿Ýj‚  v§_ö¤      G   vendor/danog/php-rtc-dtls/src/DTLS/Exception/DTLSExceptionInterface.php¥  ’¿Ýj¥   î5]¤      H   vendor/danog/php-rtc-dtls/src/DTLS/Exception/RTCCertificateException.phpŒ  ’¿ÝjŒ  ‰Œ;¼¤      =   vendor/danog/php-rtc-dtls/src/DTLS/Exception/TLSException.php�  ’¿Ýj�  õº¤      C   vendor/danog/php-rtc-dtls/src/DTLS/Exception/HandshakeException.php‡  ’¿Ýj‡  ½Œª|¤      -   vendor/danog/php-rtc-dtls/src/DTLS/Engine.phpÔ}  ’¿ÝjÔ}  Š([Ö¤      0   vendor/danog/php-rtc-dtls/src/DTLS/Handshake.phpS  ’¿ÝjS  ÁÂò¤      -   vendor/danog/php-rtc-dtls/src/DTLS/Crypto.phpŽ  ’¿ÝjŽ  ¶v¤      +   vendor/danog/php-rtc-dtls/src/DTLS/Srtp.php`"  ’¿Ýj`"  Ä�¤      5   vendor/danog/php-rtc-dtls/src/DTLS/RTCCertificate.phpµ  ’¿Ýjµ  fM8!¤      =   vendor/danog/php-rtc-dtls/src/DTLS/Enum/SSLHandshakeState.phpt  ’¿Ýjt  ¹ŒÆ­¤      K   vendor/danog/php-rtc-dtls/src/Listener/DtlsTransportStateChangeListener.php�  ’¿Ýj�  uÇÆi¤      8   vendor/danog/php-rtc-dtls/src/Exception/SSLException.php`  ’¿Ýj`  ¬‹Òf¤      <   vendor/danog/php-rtc-dtls/src/Exception/SysCallException.phpd  ’¿Ýjd  ”Ýp¤      <   vendor/danog/php-rtc-dtls/src/Exception/OpenSSLException.phpx  ’¿Ýjx  SF_x¤      ?   vendor/danog/php-rtc-dtls/src/Exception/ZeroReturnException.phpg  ’¿Ýjg  ¾þ)¢¤      )   vendor/danog/php-rtc-dtls/src/SSL/BIO.php¥
  ’¿Ýj¥
  gär¤      2   vendor/danog/php-rtc-dtls/src/SSL/BIOInterface.php÷  ’¿Ýj÷  $“d:¤      )   vendor/danog/php-rtc-dtls/src/SSL/SSL.php7  ’¿Ýj7  1^¹¤      -   vendor/danog/php-rtc-dtls/src/SSL/Context.php‡	  ’¿Ýj‡	  ¥™Ä»¤      2   vendor/danog/php-rtc-dtls/src/SSL/SSLInterface.phpS  ’¿ÝjS  ¿Äo´¤      5   vendor/danog/php-rtc-dtls/src/Enum/PrivateKeyType.php+  ’¿Ýj+  žÅ÷¤      4   vendor/danog/php-rtc-dtls/src/Enum/ContextMethod.phpi  ’¿Ýji  $0åÝ¤      2   vendor/danog/php-rtc-dtls/src/Enum/ECCurveName.phpX  ’¿ÝjX  È¹Qª¤      -   vendor/danog/php-rtc-dtls/src/Enum/Verify.phpÍ  ’¿ÝjÍ  lËº}¤      0   vendor/danog/php-rtc-dtls/src/Enum/BioMethod.phpø  ’¿Ýjø  �Ÿ	¸¤      '   vendor/danog/php-rtc-dtls/composer.jsonW  ’¿ÝjW  ÎýÝ*¤      A   vendor/danog/php-rtc-stun/src/Message/MessageAttributeEncoder.php-6  ’¿Ýj-6  œác1¤      :   vendor/danog/php-rtc-stun/src/Message/MessageInterface.php   ’¿Ýj   Ç«¤      :   vendor/danog/php-rtc-stun/src/Message/MessageIntegrity.php.  ’¿Ýj.  ‰k«h¤      1   vendor/danog/php-rtc-stun/src/Message/Message.php*(  ’¿Ýj*(  KÏ=w¤      D   vendor/danog/php-rtc-stun/src/Message/MessageAttributeCollection.phpß  ’¿Ýjß  8}à§¤      '   vendor/danog/php-rtc-stun/src/Utils.php£  ’¿Ýj£  Îl ‚¤      7   vendor/danog/php-rtc-stun/src/BaseProtocolInterface.php�  ’¿Ýj�  Œlñ‹¤      3   vendor/danog/php-rtc-stun/src/ReceiverInterface.php  ’¿Ýj  ¼ÕèŸ¤      I   vendor/danog/php-rtc-stun/src/Exception/TransactionExceptionInterface.php§  ’¿Ýj§  °Xªh¤      @   vendor/danog/php-rtc-stun/src/Exception/TransactionException.phpž  ’¿Ýjž  w–â´¤      G   vendor/danog/php-rtc-stun/src/Exception/TransactionTimeoutException.phpF  ’¿ÝjF  ½‰Nè¤      F   vendor/danog/php-rtc-stun/src/Exception/TransactionFailedException.php²  ’¿Ýj²  ¬bÝ¶¤      -   vendor/danog/php-rtc-stun/src/Transaction.php  ’¿Ýj  Öh�¤¤      7   vendor/danog/php-rtc-stun/src/IceCandidateInterface.php­  ’¿Ýj­  Ò:Ç–¤      /   vendor/danog/php-rtc-stun/src/Trait/Request.php  ’¿Ýj  pP€¿¤      =   vendor/danog/php-rtc-stun/src/Trait/CandidateSetterGetter.php`  ’¿Ýj`  ,´�¤      @   vendor/danog/php-rtc-stun/src/IceConnectionProtocolInterface.php¡  ’¿Ýj¡  á»e¤      2   vendor/danog/php-rtc-stun/src/RequestInterface.phpc  ’¿Ýjc  �Öçƒ¤      .   vendor/danog/php-rtc-stun/src/BaseProtocol.phpá  ’¿Ýjá  e :ì¤      4   vendor/danog/php-rtc-stun/src/Enum/MessageMethod.php§  ’¿Ýj§  ”Õq¤      3   vendor/danog/php-rtc-stun/src/Enum/MessageClass.php<  ’¿Ýj<  |�ú…¤      7   vendor/danog/php-rtc-stun/src/Enum/MessageAttribute.phpÃ
  ’¿ÝjÃ
  !�ë¶¤      /   vendor/danog/php-rtc-stun/src/StunInterface.phpk  ’¿Ýjk  é°ËX¤      *   vendor/danog/php-rtc-stun/src/Datagram.phpõ2  ’¿Ýjõ2  "ž<Ÿ¤      &   vendor/danog/php-rtc-stun/src/Stun.php�   ’¿Ýj�   Í u_¤      6   vendor/danog/php-rtc-stun/src/TransactionInterface.phpÃ  ’¿ÝjÃ  ©„£I¤      '   vendor/danog/php-rtc-stun/composer.json¦  ’¿Ýj¦  …�âØ¤      4   vendor/danog/php-rtc-ntp/src/NetworkTimeProtocol.phpÄ  ’¿ÝjÄ  •ôH ¤      &   vendor/danog/php-rtc-ntp/composer.jsonµ  ’¿Ýjµ  Nz;¤      @   vendor/danog/php-rtc-rtp-parameter/src/RTCRtpCodecParameters.php±  ’¿Ýj±  ~¹½ó¤      A   vendor/danog/php-rtc-rtp-parameter/src/RTCRtpCodingParameters.php4  ’¿Ýj4  Ìz*K¤      =   vendor/danog/php-rtc-rtp-parameter/src/RTCRtpCapabilities.phpÒ  ’¿ÝjÒ   Ig÷¤      @   vendor/danog/php-rtc-rtp-parameter/src/RTCRtpCodecCapability.phpÀ  ’¿ÝjÀ  ‚+H¨¤      B   vendor/danog/php-rtc-rtp-parameter/src/RTCRtpReceiveParameters.php²  ’¿Ýj²  $“¤      F   vendor/danog/php-rtc-rtp-parameter/src/RTCRtpSynchronizationSource.php  ’¿Ýj  bÇrN¤      ?   vendor/danog/php-rtc-rtp-parameter/src/RTCRtpSendParameters.php×  ’¿Ýj×  \6ýd¤      C   vendor/danog/php-rtc-rtp-parameter/src/RTCRtpContributingSource.php²  ’¿Ýj²  h@–¤      C   vendor/danog/php-rtc-rtp-parameter/src/RTCRtpEncodingParameters.php£  ’¿Ýj£  ¦­¤      :   vendor/danog/php-rtc-rtp-parameter/src/RTCRtcpFeedback.phpk  ’¿Ýjk  :ÀË.¤      ;   vendor/danog/php-rtc-rtp-parameter/src/RTCRtpParameters.php  ’¿Ýj  !>2Ê¤      >   vendor/danog/php-rtc-rtp-parameter/src/RTCRtpRtxParameters.php
  ’¿Ýj
  ©FËz¤      <   vendor/danog/php-rtc-rtp-parameter/src/RTCRtcpParameters.phpî  ’¿Ýjî  ²XJ¤      C   vendor/danog/php-rtc-rtp-parameter/src/RTCRtpDecodingParameters.php£  ’¿Ýj£  YˆFÔ¤      J   vendor/danog/php-rtc-rtp-parameter/src/RTCRtpHeaderExtensionParameters.phpz  ’¿Ýjz  1'¤      J   vendor/danog/php-rtc-rtp-parameter/src/RTCRtpHeaderExtensionCapability.php  ’¿Ýj  ¶��¤      0   vendor/danog/php-rtc-rtp-parameter/composer.json  ’¿Ýj  3½B¤      +   vendor/danog/php-rtc-stats/src/RTCStats.php@  ’¿Ýj@  Éww¤      <   vendor/danog/php-rtc-stats/src/RTCOutboundRtpStreamStats.php#  ’¿Ýj#  �éÚ¤      1   vendor/danog/php-rtc-stats/src/RTCStatsReport.phpš  ’¿Ýjš  t”ÿ,¤      4   vendor/danog/php-rtc-stats/src/RTCRtpStreamStats.phpb  ’¿Ýjb  ýS_^¤      B   vendor/danog/php-rtc-stats/src/RTCRemoteOutboundRtpStreamStats.php­  ’¿Ýj­  >Aè¤      8   vendor/danog/php-rtc-stats/src/RTCSentRtpStreamStats.phpi  ’¿Ýji  ‡î„#¤      A   vendor/danog/php-rtc-stats/src/RTCRemoteInboundRtpStreamStats.php   ’¿Ýj   ú¯ÿ‡¤      4   vendor/danog/php-rtc-stats/src/RTCTransportStats.phpŸ  ’¿ÝjŸ  ?¾6»¤      0   vendor/danog/php-rtc-stats/src/enum/StatType.phpá  ’¿Ýjá  h®Âï¤      0   vendor/danog/php-rtc-stats/src/enum/TLSState.php©  ’¿Ýj©  �®ù¤      ;   vendor/danog/php-rtc-stats/src/RTCInboundRtpStreamStats.phpï  ’¿Ýjï  4Âk¤      <   vendor/danog/php-rtc-stats/src/RTCReceivedRtpStreamStats.php”  ’¿Ýj”  -×o­¤      (   vendor/danog/php-rtc-stats/composer.json  ’¿Ýj  Àp¤      +   vendor/danog/primemodule/lib/danog/prime.pyÜ-  ’¿ÝjÜ-  ò™B¤      /   vendor/danog/primemodule/lib/danog/alt_prime.py±  ’¿Ýj±  _3î{¤      2   vendor/danog/primemodule/lib/danog/PrimeModule.phpA>  ’¿ÝjA>  ÊÀS¤      &   vendor/danog/primemodule/composer.json>  ’¿Ýj>  TÛe¤      3   vendor/league/uri-interfaces/StringCoercionMode.php8  ’¿Ýj8  ÈEŒ ¤      +   vendor/league/uri-interfaces/Occurrence.php[  ’¿Ýj[  Nå¤      +   vendor/league/uri-interfaces/HostFormat.php^  ’¿Ýj^  ˆµ87¤      1   vendor/league/uri-interfaces/FeatureDetection.phpK  ’¿ÝjK  [‹a¤      1   vendor/league/uri-interfaces/QueryComposeMode.php§  ’¿Ýj§  �Š€¤      /   vendor/league/uri-interfaces/IPv6/Converter.php€  ’¿Ýj€  »]ý/¤      )   vendor/league/uri-interfaces/HostType.php…  ’¿Ýj…  é)"¤¤      2   vendor/league/uri-interfaces/UriComparisonMode.phpw  ’¿Ýjw  î‰÷k¤      *   vendor/league/uri-interfaces/UriString.phpªh  ’¿Ýjªh  A4ëð¤      7   vendor/league/uri-interfaces/KeyValuePair/Converter.phpq  ’¿Ýjq  e‘-¤      (   vendor/league/uri-interfaces/Encoder.php.E  ’¿Ýj.E  J?Š÷¤      ,   vendor/league/uri-interfaces/QueryString.phpD  ’¿ÝjD  �ëèC¤      +   vendor/league/uri-interfaces/Idna/Error.php(  ’¿Ýj(  ?›L`¤      ,   vendor/league/uri-interfaces/Idna/Result.php£  ’¿Ýj£  D|Šå¤      ,   vendor/league/uri-interfaces/Idna/Option.php‘  ’¿Ýj‘  ÕÆ1.¤      /   vendor/league/uri-interfaces/Idna/Converter.phpy  ’¿Ýjy  ù÷·š¤      >   vendor/league/uri-interfaces/Contracts/DomainHostInterface.phpT  ’¿ÝjT  1´å$¤      8   vendor/league/uri-interfaces/Contracts/PathInterface.php–  ’¿Ýj–  ­�c¤      <   vendor/league/uri-interfaces/Contracts/UserInfoInterface.php
  ’¿Ýj
  Í€Œ&¤      <   vendor/league/uri-interfaces/Contracts/FragmentDirective.php  ’¿Ýj  ¯Æ·¤      8   vendor/league/uri-interfaces/Contracts/Conditionable.phpÄ  ’¿ÝjÄ  ØO"_¤      :   vendor/league/uri-interfaces/Contracts/IpHostInterface.php»  ’¿Ýj»  E�V®¤      9   vendor/league/uri-interfaces/Contracts/QueryInterface.php4+  ’¿Ýj4+  ¶ï&¤      7   vendor/league/uri-interfaces/Contracts/UriInterface.php?0  ’¿Ýj?0  î§<u¤      8   vendor/league/uri-interfaces/Contracts/Transformable.php—  ’¿Ýj—  ‚»Á¤      A   vendor/league/uri-interfaces/Contracts/SegmentedPathInterface.php‡  ’¿Ýj‡  n“í`¤      7   vendor/league/uri-interfaces/Contracts/UriException.phpo  ’¿Ýjo  å�EF¤      <   vendor/league/uri-interfaces/Contracts/FragmentInterface.php/  ’¿Ýj/  2ÀÖ¤      4   vendor/league/uri-interfaces/Contracts/UriAccess.phpm  ’¿Ýjm  ¥d% ¤      8   vendor/league/uri-interfaces/Contracts/HostInterface.php  ’¿Ýj  p@’«¤      =   vendor/league/uri-interfaces/Contracts/AuthorityInterface.phpè  ’¿Ýjè  æÊ-²¤      @   vendor/league/uri-interfaces/Contracts/UriComponentInterface.php‹
  ’¿Ýj‹
  ˆ"×¤      <   vendor/league/uri-interfaces/Contracts/DataPathInterface.phpÇ
  ’¿ÝjÇ
  C±¡a¤      8   vendor/league/uri-interfaces/Contracts/PortInterface.phpÖ  ’¿ÝjÖ  Â³ìó¤      +   vendor/league/uri-interfaces/HostRecord.phpÁ7  ’¿ÝjÁ7  èã®¤      6   vendor/league/uri-interfaces/IPv4/NativeCalculator.phpõ  ’¿Ýjõ  F]È¤      6   vendor/league/uri-interfaces/IPv4/BCMathCalculator.phpÝ  ’¿ÝjÝ  –”c|¤      0   vendor/league/uri-interfaces/IPv4/Calculator.php8
  ’¿Ýj8
  ÓNSµ¤      3   vendor/league/uri-interfaces/IPv4/GMPCalculator.phpá  ’¿Ýjá  ùî¤      /   vendor/league/uri-interfaces/IPv4/Converter.php¾#  ’¿Ýj¾#  MA¤      2   vendor/league/uri-interfaces/UrnComparisonMode.php{  ’¿Ýj{  ¹‘"Œ¤      *   vendor/league/uri-interfaces/composer.jsonI  ’¿ÝjI  ¨7¤      7   vendor/league/uri-interfaces/Exceptions/SyntaxError.phpÈ  ’¿ÝjÈ  ƒn“l¤      <   vendor/league/uri-interfaces/Exceptions/ConversionFailed.phpÇ  ’¿ÝjÇ  U«Wt¤      =   vendor/league/uri-interfaces/Exceptions/OffsetOutOfBounds.phpc  ’¿Ýjc  dì¤      :   vendor/league/uri-interfaces/Exceptions/MissingFeature.php»  ’¿Ýj»  ÿÊb½¤      1   vendor/league/uri-interfaces/QueryExtractMode.phpü  ’¿Ýjü  «Yf¤         vendor/league/uri/UriScheme.php¦  ’¿Ýj¦  ðfId¤      '   vendor/league/uri/UrlPattern/Parser.php¬3  ’¿Ýj¬3  øÔÀ¤      .   vendor/league/uri/UrlPattern/ComponentName.php0  ’¿Ýj0  dÈ£^¤      0   vendor/league/uri/UrlPattern/ComponentResult.phpü  ’¿Ýjü  âÌÄº¤      %   vendor/league/uri/UrlPattern/Part.phpl  ’¿Ýjl  ghG¤      &   vendor/league/uri/UrlPattern/Token.phpì  ’¿Ýjì  "`«¤      )   vendor/league/uri/UrlPattern/PartType.phpO  ’¿ÝjO  
mâ�¤      )   vendor/league/uri/UrlPattern/Modifier.php¯  ’¿Ýj¯  :«£>¤      *   vendor/league/uri/UrlPattern/MatchMode.phpx  ’¿Ýjx  iÝqú¤      '   vendor/league/uri/UrlPattern/Result.php:  ’¿Ýj:  [¤<¤      &   vendor/league/uri/UrlPattern/State.phpr  ’¿Ýjr  tÛž¤      *   vendor/league/uri/UrlPattern/Component.phpÎ  ’¿ÝjÎ  SZö.¤      -   vendor/league/uri/UrlPattern/PathToRegexp.phpŠ?  ’¿ÝjŠ?  Þ-p7¤      *   vendor/league/uri/UrlPattern/TokenType.phpø  ’¿Ýjø  Ü@P¤      ,   vendor/league/uri/UrlPattern/PatternType.phpn  ’¿Ýjn  k{ªQ¤         vendor/league/uri/Http.phpN,  ’¿ÝjN,  ßàòá¤      !   vendor/league/uri/HttpFactory.php  ’¿Ýj  khM¤         vendor/league/uri/Builder.php‹(  ’¿Ýj‹(  SÛ”Ú¤          vendor/league/uri/UrlPattern.phpÈ  ’¿ÝjÈ  t [¦¤         vendor/league/uri/BaseUri.php'P  ’¿Ýj'P  öúÍ¤      #   vendor/league/uri/TypeConverter.php¹(  ’¿Ýj¹(  »§*¹¤      !   vendor/league/uri/UriResolver.php½  ’¿Ýj½  àQ:ù¤         vendor/league/uri/Urn.phpÔH  ’¿ÝjÔH  ä¥.Ó¤      !   vendor/league/uri/UriTemplate.php03  ’¿Ýj03  Þýn¤      '   vendor/league/uri/UrlPatternBuilder.phpq+  ’¿Ýjq+  ƒÄœ¤         vendor/league/uri/composer.jsonÜ	  ’¿ÝjÜ	  ƒ­þ¤          vendor/league/uri/SchemeType.php�  ’¿Ýj�  Ô¦SV¤         vendor/league/uri/Uri.php2ý  ’¿Ýj2ý  }Ê¤      0   vendor/league/uri/UriTemplate/ExtractedValue.php¬)  ’¿Ýj¬)  #ø¤      ;   vendor/league/uri/UriTemplate/VariableCanNotBeExtracted.phpÇ  ’¿ÝjÇ  ßMO¤      )   vendor/league/uri/UriTemplate/Literal.php  ’¿Ýj  È€�3¤      .   vendor/league/uri/UriTemplate/VarSpecifier.phpC
  ’¿ÝjC
  D{xÕ¤      :   vendor/league/uri/UriTemplate/TemplateCanNotBeExpanded.phpG  ’¿ÝjG  Â)j¤      7   vendor/league/uri/UriTemplate/ExtractionErrorReason.phpî  ’¿Ýjî  ÕÍ]¤      2   vendor/league/uri/UriTemplate/ExtractionResult.php'(  ’¿Ýj'(  GèØ¤      ,   vendor/league/uri/UriTemplate/Expression.phpÆ0  ’¿ÝjÆ0  :Šuœ¤      *   vendor/league/uri/UriTemplate/Template.php•L  ’¿Ýj•L  j¾+¼¤      *   vendor/league/uri/UriTemplate/Operator.phpÆ*  ’¿ÝjÆ*  ’1<w¤      -   vendor/league/uri/UriTemplate/VariableBag.php!  ’¿Ýj!  ÀÇå4¤         vendor/league/uri/UriInfo.phpQ  ’¿ÝjQ  Ì°¤      /   vendor/league/uri-components/IPv4Normalizer.phpâ  ’¿Ýjâ  ‹;(¤      )   vendor/league/uri-components/Modifier.php÷Ð  ’¿Ýj÷Ð  H}g¤      0   vendor/league/uri-components/Components/Path.phpQ  ’¿ÝjQ  ×Ó¿¤      0   vendor/league/uri-components/Components/Port.php  ’¿Ýj  
ÑÙK¤      >   vendor/league/uri-components/Components/FragmentDirectives.phpu-  ’¿Ýju-  X.b›¤      ;   vendor/league/uri-components/Components/URLSearchParams.phpK\  ’¿ÝjK\  nË¦¤      0   vendor/league/uri-components/Components/Host.php¯#  ’¿Ýj¯#  €8qù¤      <   vendor/league/uri-components/Components/HierarchicalPath.phpKM  ’¿ÝjKM  áî¤      2   vendor/league/uri-components/Components/Scheme.phpà  ’¿Ýjà  þÖ<Í¤      L   vendor/league/uri-components/Components/FragmentDirectives/TextDirective.phpp  ’¿Ýjp  ¨ÞWñ¤      N   vendor/league/uri-components/Components/FragmentDirectives/DirectiveString.php`  ’¿Ýj`  ?ÔPã¤      F   vendor/league/uri-components/Components/FragmentDirectives/Encoder.php=
  ’¿Ýj=
  ÔÏäý¤      O   vendor/league/uri-components/Components/FragmentDirectives/GenericDirective.phpÃ	  ’¿ÝjÃ	  ÒŠý»¤      4   vendor/league/uri-components/Components/UserInfo.phpª  ’¿Ýjª  Öø«ô¤      2   vendor/league/uri-components/Components/Domain.php:  ’¿Ýj:  ix ¤      1   vendor/league/uri-components/Components/Query.phpë”  ’¿Ýjë”  /Ì/æ¤      5   vendor/league/uri-components/Components/Component.php¨  ’¿Ýj¨  (!é¤      4   vendor/league/uri-components/Components/DataPath.php5  ’¿Ýj5  î-ïë¤      4   vendor/league/uri-components/Components/Fragment.phpÏ  ’¿ÝjÏ  OG´V¤      5   vendor/league/uri-components/Components/Authority.phpÉ&  ’¿ÝjÉ&  yíŠå¤      ,   vendor/league/uri-components/UriModifier.phpž(  ’¿Ýjž(  %Hý¸¤      *   vendor/league/uri-components/composer.jsonµ  ’¿Ýjµ  ·ç16¤      9   vendor/symfony/polyfill-php82/SensitiveParameterValue.php”  ’¿Ýj”  É�
õ¤      +   vendor/symfony/polyfill-php82/bootstrap.php  ’¿Ýj  O‘�É¤      6   vendor/symfony/polyfill-php82/Random/Engine/Secure.php¨  ’¿Ýj¨  vI¤      '   vendor/symfony/polyfill-php82/Php82.phpä1  ’¿Ýjä1  ¼ˆÀ¬¤      5   vendor/symfony/polyfill-php82/NoDynamicProperties.phpÛ  ’¿ÝjÛ  £ìm¤      +   vendor/symfony/polyfill-php82/composer.json”  ’¿Ýj”   ¨Ð*¤      D   vendor/symfony/polyfill-php82/Resources/stubs/SensitiveParameter.php¦  ’¿Ýj¦  Ðxû¤      I   vendor/symfony/polyfill-php82/Resources/stubs/SensitiveParameterValue.phpz  ’¿Ýjz  c”§}¤      D   vendor/symfony/polyfill-php82/Resources/stubs/Random/RandomError.php¥  ’¿Ýj¥  >xX¨¤      H   vendor/symfony/polyfill-php82/Resources/stubs/Random/RandomException.php­  ’¿Ýj­  Ïzl¤      F   vendor/symfony/polyfill-php82/Resources/stubs/Random/Engine/Secure.php±  ’¿Ýj±  òú¤      I   vendor/symfony/polyfill-php82/Resources/stubs/Random/CryptoSafeEngine.php\  ’¿Ýj\   £½¤      P   vendor/symfony/polyfill-php82/Resources/stubs/Random/BrokenRandomEngineError.phpd  ’¿Ýjd  …Lk~¤      ?   vendor/symfony/polyfill-php82/Resources/stubs/Random/Engine.phpo  ’¿Ýjo  •w<¤      H   vendor/symfony/polyfill-php82/Resources/stubs/AllowDynamicProperties.php¦  ’¿Ýj¦  5` Õ¤      +   vendor/symfony/polyfill-iconv/bootstrap.phpk  ’¿Ýjk  ±©#¤      -   vendor/symfony/polyfill-iconv/bootstrap80.php¾  ’¿Ýj¾  0ò¾¤      '   vendor/symfony/polyfill-iconv/Iconv.phplT  ’¿ÝjlT  8«Æ¤      +   vendor/symfony/polyfill-iconv/composer.jsonÆ  ’¿ÝjÆ  9†î¤¤      >   vendor/symfony/polyfill-iconv/Resources/charset/from.cp864.phpÃ  ’¿ÝjÃ  LŽtw¤      >   vendor/symfony/polyfill-iconv/Resources/charset/from.cp866.php÷  ’¿Ýj÷  ´£D;¤      >   vendor/symfony/polyfill-iconv/Resources/charset/from.cp874.phpR  ’¿ÝjR  ÿ¦i­¤      C   vendor/symfony/polyfill-iconv/Resources/charset/from.iso-8859-3.phpZ  ’¿ÝjZ  ÙÓc»¤      >   vendor/symfony/polyfill-iconv/Resources/charset/from.cp737.phpú  ’¿Ýjú  ¦Aÿ¤      =   vendor/symfony/polyfill-iconv/Resources/charset/from.big5.php£Ž ’¿Ýj£Ž î÷Žê¤      D   vendor/symfony/polyfill-iconv/Resources/charset/from.iso-8859-15.phpÄ  ’¿ÝjÄ  XQ±Ð¤      D   vendor/symfony/polyfill-iconv/Resources/charset/from.iso-8859-10.phpÄ  ’¿ÝjÄ  žR¤      >   vendor/symfony/polyfill-iconv/Resources/charset/from.cp855.phpá  ’¿Ýjá  Ëb|¤      >   vendor/symfony/polyfill-iconv/Resources/charset/from.cp437.php  ’¿Ýj  ÷•U(¤      >   vendor/symfony/polyfill-iconv/Resources/charset/from.cp861.php  ’¿Ýj  K ±W¤      >   vendor/symfony/polyfill-iconv/Resources/charset/from.cp860.php   ’¿Ýj   ´>Š³¤      C   vendor/symfony/polyfill-iconv/Resources/charset/from.iso-8859-5.phpÄ  ’¿ÝjÄ  êÒ&¤      ?   vendor/symfony/polyfill-iconv/Resources/charset/from.cp1006.php  ’¿Ýj  xBÉ¤      >   vendor/symfony/polyfill-iconv/Resources/charset/from.cp865.php  ’¿Ýj  ?”î*¤      ?   vendor/symfony/polyfill-iconv/Resources/charset/from.koi8-u.phpó  ’¿Ýjó  B4ñ¤      >   vendor/symfony/polyfill-iconv/Resources/charset/from.cp950.phpˆ ’¿Ýjˆ ª~V¤      ?   vendor/symfony/polyfill-iconv/Resources/charset/from.cp1026.phpÃ  ’¿ÝjÃ  #ðçÞ¤      >   vendor/symfony/polyfill-iconv/Resources/charset/from.cp424.phpŠ  ’¿ÝjŠ  §Ò÷¤      C   vendor/symfony/polyfill-iconv/Resources/charset/from.iso-8859-6.php   ’¿Ýj   \—™é¤      <   vendor/symfony/polyfill-iconv/Resources/charset/translit.phpî? ’¿Ýjî? ¬þÞD¤      C   vendor/symfony/polyfill-iconv/Resources/charset/from.iso-8859-8.phpª  ’¿Ýjª  Í©õÅ¤      D   vendor/symfony/polyfill-iconv/Resources/charset/from.iso-8859-11.php¢  ’¿Ýj¢  2«¤      E   vendor/symfony/polyfill-iconv/Resources/charset/from.windows-1258.phpN  ’¿ÝjN  ´Oß¤      >   vendor/symfony/polyfill-iconv/Resources/charset/from.cp775.phpç  ’¿Ýjç  çwgÜ¤      E   vendor/symfony/polyfill-iconv/Resources/charset/from.windows-1256.phpØ  ’¿ÝjØ  ´JT¤      E   vendor/symfony/polyfill-iconv/Resources/charset/from.windows-1255.php~  ’¿Ýj~  Úñð@¤      E   vendor/symfony/polyfill-iconv/Resources/charset/from.windows-1257.php   ’¿Ýj    ,ø¤      D   vendor/symfony/polyfill-iconv/Resources/charset/from.iso-8859-13.phpÇ  ’¿ÝjÇ  í94|¤      C   vendor/symfony/polyfill-iconv/Resources/charset/from.iso-8859-1.phpÃ  ’¿ÝjÃ  z;å7¤      >   vendor/symfony/polyfill-iconv/Resources/charset/from.cp863.php  ’¿Ýj  Å¬å¤      >   vendor/symfony/polyfill-iconv/Resources/charset/from.cp949.php°r ’¿Ýj°r 
³à‚¤      >   vendor/symfony/polyfill-iconv/Resources/charset/from.cp037.phpÃ  ’¿ÝjÃ  s1À¤      D   vendor/symfony/polyfill-iconv/Resources/charset/from.iso-8859-14.phpÙ  ’¿ÝjÙ  lÍ¶¤      E   vendor/symfony/polyfill-iconv/Resources/charset/from.windows-1254.phpk  ’¿Ýjk  ü ¤      C   vendor/symfony/polyfill-iconv/Resources/charset/from.iso-8859-4.phpÃ  ’¿ÝjÃ  jé Š¤      E   vendor/symfony/polyfill-iconv/Resources/charset/from.windows-1251.phpÆ  ’¿ÝjÆ  Z¼³Ô¤      >   vendor/symfony/polyfill-iconv/Resources/charset/from.cp857.php³  ’¿Ýj³  çr¢P¤      A   vendor/symfony/polyfill-iconv/Resources/charset/from.us-ascii.phpC  ’¿ÝjC  OP•g¤      D   vendor/symfony/polyfill-iconv/Resources/charset/from.iso-8859-16.phpÆ  ’¿ÝjÆ  1M–¤      >   vendor/symfony/polyfill-iconv/Resources/charset/from.cp862.php  ’¿Ýj  ¬¤§|¤      >   vendor/symfony/polyfill-iconv/Resources/charset/from.cp850.phpá  ’¿Ýjá  0›¸/¤      C   vendor/symfony/polyfill-iconv/Resources/charset/from.iso-8859-9.phpÃ  ’¿ÝjÃ  xÃzž¤      E   vendor/symfony/polyfill-iconv/Resources/charset/from.windows-1253.phpÖ  ’¿ÝjÖ   ¿CÐ¤      C   vendor/symfony/polyfill-iconv/Resources/charset/from.iso-8859-2.phpÃ  ’¿ÝjÃ  …–§¤      >   vendor/symfony/polyfill-iconv/Resources/charset/from.cp852.phpà  ’¿Ýjà  ³`i”¤      ?   vendor/symfony/polyfill-iconv/Resources/charset/from.koi8-r.phpû  ’¿Ýjû  	uÅˆ¤      C   vendor/symfony/polyfill-iconv/Resources/charset/from.iso-8859-7.phpl  ’¿Ýjl  dãlA¤      >   vendor/symfony/polyfill-iconv/Resources/charset/from.cp875.phpÀ  ’¿ÝjÀ  Æ*¤      E   vendor/symfony/polyfill-iconv/Resources/charset/from.windows-1250.php‰  ’¿Ýj‰  ¹–·¤      >   vendor/symfony/polyfill-iconv/Resources/charset/from.cp932.phpÏ ’¿ÝjÏ g¹CG¤      E   vendor/symfony/polyfill-iconv/Resources/charset/from.windows-1252.php‰  ’¿Ýj‰  È«ÃË¤      >   vendor/symfony/polyfill-iconv/Resources/charset/from.cp869.php\  ’¿Ýj\  ˜K«å¤      >   vendor/symfony/polyfill-iconv/Resources/charset/from.cp856.phpz  ’¿Ýjz  U…øµ¤      >   vendor/symfony/polyfill-iconv/Resources/charset/from.cp936.php;® ’¿Ýj;® áDŠH¤      >   vendor/symfony/polyfill-iconv/Resources/charset/from.cp500.phpÃ  ’¿ÝjÃ  0pIq¤      .   vendor/symfony/polyfill-mbstring/bootstrap.phpk  ’¿Ýjk  Ð�H@¤      0   vendor/symfony/polyfill-mbstring/bootstrap72.phpQ!  ’¿ÝjQ!  óÎ3^¤      0   vendor/symfony/polyfill-mbstring/bootstrap80.php9'  ’¿Ýj9'  “w*¤      .   vendor/symfony/polyfill-mbstring/composer.jsonò  ’¿Ýjò  „ÚvE¤      -   vendor/symfony/polyfill-mbstring/Mbstring.phpîé  ’¿Ýjîé  t’W¤      @   vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.phpÒf  ’¿ÝjÒf  èPõ¤      @   vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.phpÛ_  ’¿ÝjÛ_  ÕãÌd¤      B   vendor/symfony/polyfill-mbstring/Resources/unidata/caseFolding.phpa	  ’¿Ýja	  æ|â³¤      F   vendor/symfony/polyfill-mbstring/Resources/unidata/titleCaseRegexp.php9  ’¿Ýj9  >|zK¤      +   vendor/symfony/polyfill-php85/bootstrap.php@  ’¿Ýj@  ¤ÚÛ¤      -   vendor/symfony/polyfill-php85/bootstrap80.phpÍ  ’¿ÝjÍ  *á'Æ¤      '   vendor/symfony/polyfill-php85/Php85.phpP  ’¿ÝjP  2c#õ¤      +   vendor/symfony/polyfill-php85/composer.json”  ’¿Ýj”  ,ò—È¤      I   vendor/symfony/polyfill-php85/Resources/stubs/DelayedTargetValidation.php¨  ’¿Ýj¨  ÃžŒb¤      N   vendor/symfony/polyfill-php85/Resources/stubs/Filter/FilterFailedException.phpf  ’¿Ýjf  ´aù:¤      H   vendor/symfony/polyfill-php85/Resources/stubs/Filter/FilterException.php[  ’¿Ýj[  Ê¤¤      ;   vendor/symfony/polyfill-php85/Resources/stubs/NoDiscard.php  ’¿Ýj  Úáåz¤      '   vendor/symfony/polyfill-php86/Php86.php³  ’¿Ýj³  ê…VX¤      +   vendor/symfony/polyfill-php86/bootstrap.php²  ’¿Ýj²  rž¥ ¤      -   vendor/symfony/polyfill-php86/bootstrap80.php1  ’¿Ýj1  ßàû¤      +   vendor/symfony/polyfill-php86/composer.json”  ’¿Ýj”  ë¼Äì¤      ?   vendor/symfony/polyfill-php86/Resources/stubs/SortDirection.php  ’¿Ýj  )‡š	¤      -   vendor/symfony/polyfill-php84/bootstrap82.php  ’¿Ýj  YÅ a¤      +   vendor/symfony/polyfill-php84/bootstrap.phpÒ  ’¿ÝjÒ  •MKš¤      -   vendor/symfony/polyfill-php84/bootstrap72.phpØ  ’¿ÝjØ  7ù”ù¤      '   vendor/symfony/polyfill-php84/Php84.phpmR  ’¿ÝjmR  :ÕKÒ¤      +   vendor/symfony/polyfill-php84/composer.json”  ’¿Ýj”  ®5vb¤      D   vendor/symfony/polyfill-php84/Resources/stubs/ReflectionConstant.php®  ’¿Ýj®  ÜgÔ¤      >   vendor/symfony/polyfill-php84/Resources/stubs/RoundingMode.php\  ’¿Ýj\  <Hñ¤      <   vendor/symfony/polyfill-php84/Resources/stubs/Pdo/Sqlite.php 
  ’¿Ýj 
  ö¹@&¤      :   vendor/symfony/polyfill-php84/Resources/stubs/Pdo/Odbc.phpÄ  ’¿ÝjÄ  §�É}¤      >   vendor/symfony/polyfill-php84/Resources/stubs/Pdo/Firebird.phpR  ’¿ÝjR  Œ?f¤      ;   vendor/symfony/polyfill-php84/Resources/stubs/Pdo/Dblib.phpý  ’¿Ýjý  �œl8¤      ;   vendor/symfony/polyfill-php84/Resources/stubs/Pdo/Mysql.phpä!  ’¿Ýjä!  ½=Â¤      ;   vendor/symfony/polyfill-php84/Resources/stubs/Pdo/Pgsql.phpO  ’¿ÝjO  tÖïj¤      <   vendor/symfony/polyfill-php84/Resources/stubs/Deprecated.phpD  ’¿ÝjD  ÎÈÅ½¤      8   vendor/symfony/polyfill-php84/Resources/RoundingMode.php  ’¿Ýj  ‘£�ä¤      6   vendor/symfony/polyfill-php84/Resources/Deprecated.php¦  ’¿Ýj¦  ^Á6ó¤      '   vendor/symfony/polyfill-php83/Php83.phpÓ  ’¿ÝjÓ  F·Š ¤      +   vendor/symfony/polyfill-php83/bootstrap.php  ’¿Ýj  êM4�¤      -   vendor/symfony/polyfill-php83/bootstrap72.phpé  ’¿Ýjé  ´Xm_¤      -   vendor/symfony/polyfill-php83/bootstrap81.phpî  ’¿Ýjî  j²Ö¤      +   vendor/symfony/polyfill-php83/composer.json”  ’¿Ýj”  ¢o1€¤      V   vendor/symfony/polyfill-php83/Resources/stubs/DateMalformedIntervalStringException.php`  ’¿Ýj`  6€¿ø¤      N   vendor/symfony/polyfill-php83/Resources/stubs/DateInvalidTimeZoneException.phpX  ’¿ÝjX  Çìg¤      O   vendor/symfony/polyfill-php83/Resources/stubs/DateInvalidOperationException.phpY  ’¿ÝjY  �MÏß¤      B   vendor/symfony/polyfill-php83/Resources/stubs/SQLite3Exception.phpH  ’¿ÝjH  <Ü�Ž¤      A   vendor/symfony/polyfill-php83/Resources/stubs/DateObjectError.phpG  ’¿ÝjG  
.I¤      ?   vendor/symfony/polyfill-php83/Resources/stubs/DateException.phpE  ’¿ÝjE  ¼•¤      ;   vendor/symfony/polyfill-php83/Resources/stubs/DateError.php=  ’¿Ýj=  �ûË¤      @   vendor/symfony/polyfill-php83/Resources/stubs/DateRangeError.phpF  ’¿ÝjF  ÓL;¿¤      N   vendor/symfony/polyfill-php83/Resources/stubs/DateMalformedStringException.phpX  ’¿ÝjX  ýWÅß¤      T   vendor/symfony/polyfill-php83/Resources/stubs/DateMalformedPeriodStringException.php^  ’¿Ýj^  Køü¤      :   vendor/symfony/polyfill-php83/Resources/stubs/Override.php™  ’¿Ýj™  NsÀE¤      2   vendor/ramsey/uuid/src/Lazy/LazyUuidFromString.phpœ5  ’¿Ýjœ5  Æ¶åû¤      %   vendor/ramsey/uuid/src/FeatureSet.php:0  ’¿Ýj:0  ­‰DÌ¤      '   vendor/ramsey/uuid/src/Type/Integer.phpY  ’¿ÝjY  mG|Õ¤      /   vendor/ramsey/uuid/src/Type/NumberInterface.phpu  ’¿Ýju  Õo?e¤      -   vendor/ramsey/uuid/src/Type/TypeInterface.phpÉ  ’¿ÝjÉ  J3“Y¤      +   vendor/ramsey/uuid/src/Type/Hexadecimal.php™  ’¿Ýj™  øÄÃ¸¤      '   vendor/ramsey/uuid/src/Type/Decimal.phpñ  ’¿Ýjñ  ÁWpˆ¤      $   vendor/ramsey/uuid/src/Type/Time.phpd  ’¿Ýjd  £÷Æw¤      A   vendor/ramsey/uuid/src/Provider/Dce/SystemDceSecurityProvider.phpb  ’¿Ýjb  |!æ¤      @   vendor/ramsey/uuid/src/Provider/DceSecurityProviderInterface.phpæ  ’¿Ýjæ  íY‰¤      ;   vendor/ramsey/uuid/src/Provider/Node/StaticNodeProvider.phpÿ  ’¿Ýjÿ  ¬–÷Þ¤      ;   vendor/ramsey/uuid/src/Provider/Node/SystemNodeProvider.php—  ’¿Ýj—  M¼Ô¤      =   vendor/ramsey/uuid/src/Provider/Node/FallbackNodeProvider.php@  ’¿Ýj@  ›‹+/¤      ?   vendor/ramsey/uuid/src/Provider/Node/NodeProviderCollection.phpp  ’¿Ýjp  ÷?Ò•¤      ;   vendor/ramsey/uuid/src/Provider/Node/RandomNodeProvider.php9  ’¿Ýj9  ƒ¡	‹¤      :   vendor/ramsey/uuid/src/Provider/Time/FixedTimeProvider.php|  ’¿Ýj|  Øg	œ¤      ;   vendor/ramsey/uuid/src/Provider/Time/SystemTimeProvider.phpö  ’¿Ýjö  ƒv¤      9   vendor/ramsey/uuid/src/Provider/NodeProviderInterface.phpŒ  ’¿ÝjŒ  `¦¾¤      9   vendor/ramsey/uuid/src/Provider/TimeProviderInterface.php6  ’¿Ýj6  æfTã¤         vendor/ramsey/uuid/src/Uuid.phpU_  ’¿ÝjU_  ÞÙÑ¤      =   vendor/ramsey/uuid/src/Exception/BuilderNotFoundException.phpD  ’¿ÝjD  ÞveÃ¤      =   vendor/ramsey/uuid/src/Exception/InvalidArgumentException.php\  ’¿Ýj\  d{Ž}¤      ?   vendor/ramsey/uuid/src/Exception/InvalidUuidStringException.php·  ’¿Ýj·  µ}�z¤      :   vendor/ramsey/uuid/src/Exception/RandomSourceException.phpò  ’¿Ýjò  â¤¼¤      :   vendor/ramsey/uuid/src/Exception/InvalidBytesException.phpR  ’¿ÝjR  µoêu¤      2   vendor/ramsey/uuid/src/Exception/NodeException.phpS  ’¿ÝjS  . ä¤      6   vendor/ramsey/uuid/src/Exception/DateTimeException.phpT  ’¿ÝjT  ¼àð•¤      B   vendor/ramsey/uuid/src/Exception/UnsupportedOperationException.phpI  ’¿ÝjI  ï–>¤      ;   vendor/ramsey/uuid/src/Exception/UuidExceptionInterface.php¶  ’¿Ýj¶  Rêñ¤      ?   vendor/ramsey/uuid/src/Exception/UnableToBuildUuidException.phpB  ’¿ÝjB  H%å_¤      2   vendor/ramsey/uuid/src/Exception/NameException.phpV  ’¿ÝjV  X,¤      9   vendor/ramsey/uuid/src/Exception/DceSecurityException.php`  ’¿Ýj`  Ö£¤      8   vendor/ramsey/uuid/src/Exception/TimeSourceException.phpD  ’¿ÝjD  –=¤      5   vendor/ramsey/uuid/src/DeprecatedUuidMethodsTrait.php4  ’¿Ýj4  âÉÌþ¤      (   vendor/ramsey/uuid/src/UuidInterface.phpd  ’¿Ýjd  •Fd¤      ,   vendor/ramsey/uuid/src/Rfc4122/Validator.php7  ’¿Ýj7  kšýn¤      .   vendor/ramsey/uuid/src/Rfc4122/UuidBuilder.php  ’¿Ýj  ¤ÖíÙ¤      )   vendor/ramsey/uuid/src/Rfc4122/UuidV1.php÷  ’¿Ýj÷  €8PÜ¤      ,   vendor/ramsey/uuid/src/Rfc4122/TimeTrait.phpu  ’¿Ýju  Á¸¤      )   vendor/ramsey/uuid/src/Rfc4122/UuidV2.phpÒ  ’¿ÝjÒ  ’Ñ‡>¤      )   vendor/ramsey/uuid/src/Rfc4122/Fields.phpc  ’¿Ýjc  R~sC¤      )   vendor/ramsey/uuid/src/Rfc4122/UuidV3.php  ’¿Ýj  ä—7¤      )   vendor/ramsey/uuid/src/Rfc4122/UuidV7.phpø  ’¿Ýjø  æ�A¤      /   vendor/ramsey/uuid/src/Rfc4122/VersionTrait.php¸  ’¿Ýj¸  Ï“Ò¿¤      /   vendor/ramsey/uuid/src/Rfc4122/VariantTrait.phpý  ’¿Ýjý  	ž¤      +   vendor/ramsey/uuid/src/Rfc4122/MaxTrait.phpH  ’¿ÝjH  ¨h8÷¤      0   vendor/ramsey/uuid/src/Rfc4122/UuidInterface.phpƒ  ’¿Ýjƒ  š=�©¤      *   vendor/ramsey/uuid/src/Rfc4122/NilUuid.php„  ’¿Ýj„  ¥ O‹¤      +   vendor/ramsey/uuid/src/Rfc4122/NilTrait.php  ’¿Ýj  NÀ–¤      )   vendor/ramsey/uuid/src/Rfc4122/UuidV5.php  ’¿Ýj  ´­¦Ì¤      )   vendor/ramsey/uuid/src/Rfc4122/UuidV4.php¯  ’¿Ýj¯  1î*Î¤      )   vendor/ramsey/uuid/src/Rfc4122/UuidV8.phpó  ’¿Ýjó  HYA¤      2   vendor/ramsey/uuid/src/Rfc4122/FieldsInterface.phpÔ  ’¿ÝjÔ  Ý+¤      )   vendor/ramsey/uuid/src/Rfc4122/UuidV6.php   ’¿Ýj   —éè~¤      *   vendor/ramsey/uuid/src/Rfc4122/MaxUuid.php…  ’¿Ýj…  Zþº¤      9   vendor/ramsey/uuid/src/Fields/SerializableFieldsTrait.phpo  ’¿Ýjo  Áö}H¤      1   vendor/ramsey/uuid/src/Fields/FieldsInterface.phpñ  ’¿Ýjñ  »¹q¤      5   vendor/ramsey/uuid/src/Validator/GenericValidator.php  ’¿Ýj  9½{`¤      7   vendor/ramsey/uuid/src/Validator/ValidatorInterface.phpÛ  ’¿ÝjÛ  ¾2Ùe¤      &   vendor/ramsey/uuid/src/UuidFactory.phpæ;  ’¿Ýjæ;  ûXcÔ¤      ,   vendor/ramsey/uuid/src/Codec/StringCodec.php}  ’¿Ýj}  \š‚¤      /   vendor/ramsey/uuid/src/Codec/CodecInterface.phpÔ  ’¿ÝjÔ  Ù&*{¤      8   vendor/ramsey/uuid/src/Codec/TimestampFirstCombCodec.php  ’¿Ýj  >EÀ ¤      1   vendor/ramsey/uuid/src/Codec/OrderedTimeCodec.php&  ’¿Ýj&  Ð#í�¤      7   vendor/ramsey/uuid/src/Codec/TimestampLastCombCodec.php  ’¿Ýj  W!Ò˜¤      0   vendor/ramsey/uuid/src/Codec/GuidStringCodec.php8  ’¿Ýj8  Ó"ý¤      3   vendor/ramsey/uuid/src/Math/BrickMathCalculator.php˜  ’¿Ýj˜  v?Xq¤      ,   vendor/ramsey/uuid/src/Math/RoundingMode.php-  ’¿Ýj-  ŽÕ¤      3   vendor/ramsey/uuid/src/Math/CalculatorInterface.php.  ’¿Ýj.  öƒe¤      5   vendor/ramsey/uuid/src/Math/BrickMathRoundingMode.php¾  ’¿Ýj¾  ÇmË�¤      /   vendor/ramsey/uuid/src/UuidFactoryInterface.phpÓ  ’¿ÝjÓ  (3¤      9   vendor/ramsey/uuid/src/Generator/DceSecurityGenerator.phpç  ’¿Ýjç  Æ‰¤      :   vendor/ramsey/uuid/src/Generator/PeclUuidTimeGenerator.phpo  ’¿Ýjo  ûì€“¤      6   vendor/ramsey/uuid/src/Generator/UnixTimeGenerator.phpW  ’¿ÝjW  Ùµó¤      ;   vendor/ramsey/uuid/src/Generator/TimeGeneratorInterface.phpu  ’¿Ýju  !«¬Œ¤      :   vendor/ramsey/uuid/src/Generator/PeclUuidNameGenerator.php¼  ’¿Ýj¼  Nà©¤      9   vendor/ramsey/uuid/src/Generator/DefaultNameGenerator.phpW  ’¿ÝjW  ñøÆ¤      ;   vendor/ramsey/uuid/src/Generator/RandomGeneratorFactory.phpÁ  ’¿ÝjÁ  êÒÇó¤      9   vendor/ramsey/uuid/src/Generator/DefaultTimeGenerator.php•  ’¿Ýj•  Ag]¤      9   vendor/ramsey/uuid/src/Generator/TimeGeneratorFactory.phpY  ’¿ÝjY  óß¤      9   vendor/ramsey/uuid/src/Generator/NameGeneratorFactory.php·  ’¿Ýj·  ½ä„@¤      2   vendor/ramsey/uuid/src/Generator/CombGenerator.php¢  ’¿Ýj¢  Vð5Þ¤      <   vendor/ramsey/uuid/src/Generator/PeclUuidRandomGenerator.php0  ’¿Ýj0  $+’¤      B   vendor/ramsey/uuid/src/Generator/DceSecurityGeneratorInterface.php­  ’¿Ýj­  Ì´“€¤      ;   vendor/ramsey/uuid/src/Generator/NameGeneratorInterface.php  ’¿Ýj  š¤\Æ¤      =   vendor/ramsey/uuid/src/Generator/RandomGeneratorInterface.phpß  ’¿Ýjß  ÓˆR¤      5   vendor/ramsey/uuid/src/Generator/RandomLibAdapter.php  ’¿Ýj  d
i�¤      9   vendor/ramsey/uuid/src/Generator/RandomBytesGenerator.php7  ’¿Ýj7  ?%•	¤      '   vendor/ramsey/uuid/src/DegradedUuid.phpM  ’¿ÝjM  ¦Hùn¤      2   vendor/ramsey/uuid/src/DeprecatedUuidInterface.php�  ’¿Ýj�  ö ¬¤      &   vendor/ramsey/uuid/src/BinaryUtils.php  ’¿Ýj  G³hý¤      $   vendor/ramsey/uuid/src/functions.phpr  ’¿Ýjr  1žvè¤      &   vendor/ramsey/uuid/src/Guid/Fields.phpŠ  ’¿ÝjŠ  ÍSR¤      +   vendor/ramsey/uuid/src/Guid/GuidBuilder.phpù  ’¿Ýjù  JÇ5¤      $   vendor/ramsey/uuid/src/Guid/Guid.phpã  ’¿Ýjã  aã¤      2   vendor/ramsey/uuid/src/Nonstandard/UuidBuilder.php"	  ’¿Ýj"	  (‹áH¤      -   vendor/ramsey/uuid/src/Nonstandard/Fields.php6  ’¿Ýj6  ¹+ß"¤      +   vendor/ramsey/uuid/src/Nonstandard/Uuid.phpÓ  ’¿ÝjÓ  ¥‰\ ¤      -   vendor/ramsey/uuid/src/Nonstandard/UuidV6.php�  ’¿Ýj�  ×±M¤      7   vendor/ramsey/uuid/src/Builder/UuidBuilderInterface.phpø  ’¿Ýjø  	Næ¤      5   vendor/ramsey/uuid/src/Builder/DefaultUuidBuilder.php9  ’¿Ýj9  ¼§Ü¤      2   vendor/ramsey/uuid/src/Builder/FallbackBuilder.phpX  ’¿ÝjX  I^Øp¤      6   vendor/ramsey/uuid/src/Builder/DegradedUuidBuilder.php  ’¿Ýj  ´Ô¯¤      4   vendor/ramsey/uuid/src/Builder/BuilderCollection.php×	  ’¿Ýj×	  ¤Ñ¤      B   vendor/ramsey/uuid/src/Converter/Number/GenericNumberConverter.phpÔ  ’¿ÝjÔ  ­ÀÞÊ¤      >   vendor/ramsey/uuid/src/Converter/Number/BigNumberConverter.phpÕ  ’¿ÝjÕ   ?1Ì¤      C   vendor/ramsey/uuid/src/Converter/Number/DegradedNumberConverter.phpo  ’¿Ýjo  ‰6p¤      =   vendor/ramsey/uuid/src/Converter/NumberConverterInterface.phpÊ  ’¿ÝjÊ  ÿAhâ¤      >   vendor/ramsey/uuid/src/Converter/Time/GenericTimeConverter.php¼  ’¿Ýj¼  oìÙl¤      ;   vendor/ramsey/uuid/src/Converter/Time/UnixTimeConverter.phpù
  ’¿Ýjù
  é�Æx¤      ?   vendor/ramsey/uuid/src/Converter/Time/DegradedTimeConverter.phph  ’¿Ýjh  •mñ÷¤      :   vendor/ramsey/uuid/src/Converter/Time/PhpTimeConverter.php˜  ’¿Ýj˜  lN}Ç¤      @   vendor/ramsey/uuid/src/Converter/Time/BigNumberTimeConverter.php-  ’¿Ýj-  â”Ük¤      ;   vendor/ramsey/uuid/src/Converter/TimeConverterInterface.phpÙ  ’¿ÝjÙ  ±pïë¤          vendor/ramsey/uuid/composer.jsonÒ  ’¿ÝjÒ  ¦¡¤      +   vendor/ramsey/collection/src/Collection.php	  ’¿Ýj	  šzùá¤      3   vendor/ramsey/collection/src/AbstractCollection.php,  ’¿Ýj,  ¸ñ7$¤      .   vendor/ramsey/collection/src/AbstractArray.php  ’¿Ýj  Ô3ñ{¤      /   vendor/ramsey/collection/src/QueueInterface.phpÝ  ’¿ÝjÝ  :Â7¬¤      $   vendor/ramsey/collection/src/Set.php"  ’¿Ýj"  ’Kéß¤      :   vendor/ramsey/collection/src/DoubleEndedQueueInterface.phpy(  ’¿Ýjy(  µÎ4J¤      1   vendor/ramsey/collection/src/DoubleEndedQueue.phpÀ  ’¿ÝjÀ  Â[[¤      ?   vendor/ramsey/collection/src/Exception/OutOfBoundsException.phpj  ’¿Ýjj  I�¥û¤      C   vendor/ramsey/collection/src/Exception/InvalidArgumentException.phpf  ’¿Ýjf  ©X'N¤      A   vendor/ramsey/collection/src/Exception/NoSuchElementException.php7  ’¿Ýj7  Ž�>¤      H   vendor/ramsey/collection/src/Exception/UnsupportedOperationException.php?  ’¿Ýj?  ÛMï½¤      F   vendor/ramsey/collection/src/Exception/CollectionMismatchException.php@  ’¿Ýj@  ;fÏ2¤      B   vendor/ramsey/collection/src/Exception/InvalidPropertyOrMethod.php›  ’¿Ýj›  UÔ/¤      >   vendor/ramsey/collection/src/Exception/CollectionException.php¿  ’¿Ýj¿  N�DÒ¤      &   vendor/ramsey/collection/src/Queue.phpM  ’¿ÝjM  5¨äã¤      /   vendor/ramsey/collection/src/Tool/TypeTrait.php5  ’¿Ýj5  Ì¾fÒ¤      8   vendor/ramsey/collection/src/Tool/ValueToStringTrait.php	  ’¿Ýj	  –èi�¤      9   vendor/ramsey/collection/src/Tool/ValueExtractorTrait.php.  ’¿Ýj.  \	^ü¤      /   vendor/ramsey/collection/src/ArrayInterface.php%  ’¿Ýj%  }–®À¤      0   vendor/ramsey/collection/src/Map/AbstractMap.phpÆ  ’¿ÝjÆ  )7�Ý¤      6   vendor/ramsey/collection/src/Map/TypedMapInterface.php#  ’¿Ýj#  ž9¤      -   vendor/ramsey/collection/src/Map/TypedMap.phpø
  ’¿Ýjø
  AGÈ¤      8   vendor/ramsey/collection/src/Map/AssociativeArrayMap.php"  ’¿Ýj"  Ìý@i¤      1   vendor/ramsey/collection/src/Map/MapInterface.phpÓ  ’¿ÝjÓ  ”hµì¤      6   vendor/ramsey/collection/src/Map/NamedParameterMap.php:  ’¿Ýj:  )øª°¤      5   vendor/ramsey/collection/src/Map/AbstractTypedMap.php‡  ’¿Ýj‡  Éó¤      %   vendor/ramsey/collection/src/Sort.phpm  ’¿Ýjm  ÝÖ•¹¤      4   vendor/ramsey/collection/src/CollectionInterface.phpÀ#  ’¿ÝjÀ#  snbÞ¤      ,   vendor/ramsey/collection/src/AbstractSet.php—  ’¿Ýj—  !cŸ¤      -   vendor/ramsey/collection/src/GenericArray.php   ’¿Ýj   Ký™S¤      &   vendor/ramsey/collection/composer.json2  ’¿Ýj2  ¶WGö¤      1   vendor/kelunik/certificate/.php-cs-fixer.dist.php½   ’¿Ýj½   �.Á8¤      .   vendor/kelunik/certificate/src/Certificate.phpm  ’¿Ýjm  ]AB¤      >   vendor/kelunik/certificate/src/InvalidCertificateException.phpy   ’¿Ýjy   Ž¯¢¤      *   vendor/kelunik/certificate/src/Profile.phpw  ’¿Ýjw  pá¹¤      =   vendor/kelunik/certificate/src/FieldNotSupportedException.phpx   ’¿Ýjx   Ê/ƒ¤      (   vendor/kelunik/certificate/composer.json†  ’¿Ýj†  [^«¤      *   vendor/revolt/event-loop/src/EventLoop.php>  ’¿Ýj>  [` ¤      8   vendor/revolt/event-loop/src/EventLoop/DriverFactory.phpÝ  ’¿ÝjÝ  D0Æå¤      7   vendor/revolt/event-loop/src/EventLoop/CallbackType.php¼   ’¿Ýj¼   ¥nÛ/¤      1   vendor/revolt/event-loop/src/EventLoop/Driver.php(5  ’¿Ýj(5  ÊÖM¤      :   vendor/revolt/event-loop/src/EventLoop/Driver/EvDriver.php  ’¿Ýj  }37j¤      D   vendor/revolt/event-loop/src/EventLoop/Driver/StreamSelectDriver.phpJ/  ’¿ÝjJ/  6½.¤      ?   vendor/revolt/event-loop/src/EventLoop/Driver/TracingDriver.php¨  ’¿Ýj¨  £i^¤      =   vendor/revolt/event-loop/src/EventLoop/Driver/EventDriver.phpd  ’¿Ýjd  Ò&©¤      :   vendor/revolt/event-loop/src/EventLoop/Driver/UvDriver.phpG"  ’¿ÝjG"  ËÙl¤      ?   vendor/revolt/event-loop/src/EventLoop/InvalidCallbackError.php:  ’¿Ýj:  BBåE¤      J   vendor/revolt/event-loop/src/EventLoop/Internal/StreamReadableCallback.php–   ’¿Ýj–   0³ï¢¤      B   vendor/revolt/event-loop/src/EventLoop/Internal/DriverCallback.phpç  ’¿Ýjç  �´€¤      A   vendor/revolt/event-loop/src/EventLoop/Internal/TimerCallback.php‘  ’¿Ýj‘  ÝÉcÊ¤      D   vendor/revolt/event-loop/src/EventLoop/Internal/DriverSuspension.phpÂ  ’¿ÝjÂ  âÙ\o¤      J   vendor/revolt/event-loop/src/EventLoop/Internal/StreamWritableCallback.php–   ’¿Ýj–   ãÚ×¤      >   vendor/revolt/event-loop/src/EventLoop/Internal/TimerQueue.php  ’¿Ýj  ]UÕÔ¤      B   vendor/revolt/event-loop/src/EventLoop/Internal/SignalCallback.php<  ’¿Ýj<  ±ËÙÜ¤      B   vendor/revolt/event-loop/src/EventLoop/Internal/StreamCallback.phpp  ’¿Ýjp  ;-tW¤      A   vendor/revolt/event-loop/src/EventLoop/Internal/DeferCallback.php�   ’¿Ýj�   VUð¤      A   vendor/revolt/event-loop/src/EventLoop/Internal/ClosureHelper.php#  ’¿Ýj#  *Çêˆ¤      B   vendor/revolt/event-loop/src/EventLoop/Internal/AbstractDriver.phpüT  ’¿ÝjüT  Tç&u¤      5   vendor/revolt/event-loop/src/EventLoop/Suspension.php®  ’¿Ýj®  Íü£¡¤      F   vendor/revolt/event-loop/src/EventLoop/UnsupportedFeatureException.php9  ’¿Ýj9  G©åt¤      5   vendor/revolt/event-loop/src/EventLoop/FiberLocal.php  ’¿Ýj  Ë·H¤      <   vendor/revolt/event-loop/src/EventLoop/UncaughtThrowable.php]  ’¿Ýj]  åK_¤      &   vendor/revolt/event-loop/composer.jsoná  ’¿Ýjá  <UX¤      I   vendor/promphp/prometheus_client_php/src/Prometheus/CollectorRegistry.phpÌ-  ’¿ÝjÌ-  Ãy˜ß¤      H   vendor/promphp/prometheus_client_php/src/Prometheus/Storage/InMemory.php8  ’¿Ýj8  Š€×¤      F   vendor/promphp/prometheus_client_php/src/Prometheus/Storage/Predis.php7  ’¿Ýj7  Äe\—¤      C   vendor/promphp/prometheus_client_php/src/Prometheus/Storage/APC.phpO  ’¿ÝjO  ×ÁÚ:¤      a   vendor/promphp/prometheus_client_php/src/Prometheus/Storage/RedisClients/RedisClientException.php   ’¿Ýj   xJó¤      X   vendor/promphp/prometheus_client_php/src/Prometheus/Storage/RedisClients/RedisClient.php´  ’¿Ýj´   ´u´¤      S   vendor/promphp/prometheus_client_php/src/Prometheus/Storage/RedisClients/Predis.php]  ’¿Ýj]  ãC_¤      U   vendor/promphp/prometheus_client_php/src/Prometheus/Storage/RedisClients/PHPRedis.phpI  ’¿ÝjI  †~{ß¤      E   vendor/promphp/prometheus_client_php/src/Prometheus/Storage/Redis.php¤  ’¿Ýj¤  çîÑš¤      M   vendor/promphp/prometheus_client_php/src/Prometheus/Storage/AbstractRedis.php·M  ’¿Ýj·M  7¡Ø¡¤      C   vendor/promphp/prometheus_client_php/src/Prometheus/Storage/PDO.phpz`  ’¿Ýjz`    .Ù¤      G   vendor/promphp/prometheus_client_php/src/Prometheus/Storage/RedisNg.phpY  ’¿ÝjY  BÓ¤      G   vendor/promphp/prometheus_client_php/src/Prometheus/Storage/Adapter.php  ’¿Ýj  téÂ¤      E   vendor/promphp/prometheus_client_php/src/Prometheus/Storage/APCng.phpi€  ’¿Ýji€  ÝÝs»¤      I   vendor/promphp/prometheus_client_php/src/Prometheus/RegistryInterface.php6  ’¿Ýj6  ÓÚÉh¤      =   vendor/promphp/prometheus_client_php/src/Prometheus/Gauge.phpÙ  ’¿ÝjÙ  ¸é_ô¤      I   vendor/promphp/prometheus_client_php/src/Prometheus/RendererInterface.phpÌ   ’¿ÝjÌ   GÇõˆ¤      >   vendor/promphp/prometheus_client_php/src/Prometheus/Sample.php—  ’¿Ýj—  Ò—]Û¤      <   vendor/promphp/prometheus_client_php/src/Prometheus/Math.phpL  ’¿ÝjL  àæP"¤      H   vendor/promphp/prometheus_client_php/src/Prometheus/RenderTextFormat.phpM  ’¿ÝjM  Âš¯Ú¤      A   vendor/promphp/prometheus_client_php/src/Prometheus/Collector.php!  ’¿Ýj!  p//¤      Y   vendor/promphp/prometheus_client_php/src/Prometheus/Exception/MetricNotFoundException.phpË   ’¿ÝjË   ÉÉ}Œ¤      U   vendor/promphp/prometheus_client_php/src/Prometheus/Exception/MetricJsonException.phpE  ’¿ÝjE  $ïy¤      R   vendor/promphp/prometheus_client_php/src/Prometheus/Exception/StorageException.php¬   ’¿Ýj¬   W>f¤      ^   vendor/promphp/prometheus_client_php/src/Prometheus/Exception/MetricsRegistrationException.php½   ’¿Ýj½   Ü’¤      ?   vendor/promphp/prometheus_client_php/src/Prometheus/Counter.php~  ’¿Ýj~  ×cðs¤      ?   vendor/promphp/prometheus_client_php/src/Prometheus/Summary.php_  ’¿Ýj_  Çs.Q¤      K   vendor/promphp/prometheus_client_php/src/Prometheus/MetricFamilySamples.php  ’¿Ýj  T¯Ao¤      A   vendor/promphp/prometheus_client_php/src/Prometheus/Histogram.phpN  ’¿ÝjN  Ù!u©¤      2   vendor/promphp/prometheus_client_php/composer.json  ’¿Ýj  YlF¤      0   vendor/amphp/process/composer-require-check.jsonl  ’¿Ýjl  €~�£¤      -   vendor/amphp/process/src/ProcessException.phpf   ’¿Ýjf   Ô)H¤      3   vendor/amphp/process/src/Internal/ProcessRunner.phpD  ’¿ÝjD  ¾e¬6¤      4   vendor/amphp/process/src/Internal/ProcessStreams.php  ’¿Ýj  ?åÝM¤      =   vendor/amphp/process/src/Internal/Windows/HandshakeStatus.php€  ’¿Ýj€  ôwê¤      8   vendor/amphp/process/src/Internal/Windows/SignalCode.php’  ’¿Ýj’  Ä)çÂ¤      =   vendor/amphp/process/src/Internal/Windows/SocketConnector.phpl"  ’¿Ýjl"  Üds¤      ;   vendor/amphp/process/src/Internal/Windows/WindowsHandle.php•  ’¿Ýj•  l<Š°¤      ;   vendor/amphp/process/src/Internal/Windows/WindowsRunner.php¡  ’¿Ýj¡  FöÅ¤      @   vendor/amphp/process/src/Internal/Windows/HandshakeException.php  ’¿Ýj  9ÉÑ¤      4   vendor/amphp/process/src/Internal/ProcessContext.phpk  ’¿Ýjk  –¼öH¤      0   vendor/amphp/process/src/Internal/ProcHolder.phpÁ  ’¿ÝjÁ  LÁù¯¤      3   vendor/amphp/process/src/Internal/ProcessHandle.php×  ’¿Ýj×  Üü0¤      7   vendor/amphp/process/src/Internal/Posix/PosixHandle.phpê  ’¿Ýjê  S%Vu¤      7   vendor/amphp/process/src/Internal/Posix/PosixRunner.php¿  ’¿Ýj¿  oDó¤      3   vendor/amphp/process/src/Internal/ProcessStatus.phpž   ’¿Ýjž   T¼‹¤      $   vendor/amphp/process/src/Process.phpÎ  ’¿ÝjÎ  ñJ^Å¤      &   vendor/amphp/process/src/functions.php6  ’¿Ýj6  ð‰nË¤      "   vendor/amphp/process/composer.json’  ’¿Ýj’  w\¤}¤      3   vendor/amphp/process/bin/windows/ProcessWrapper.exe 4 ’¿Ýj 4 %P§¤      5   vendor/amphp/process/bin/windows/ProcessWrapper64.exe H ’¿Ýj H &Ó;‰¤      .   vendor/amphp/http/src/Http2/Http2Processor.php›  ’¿Ýj›  ÖZ…¤      4   vendor/amphp/http/src/Http2/Http2StreamException.php©  ’¿Ýj©  «¤      8   vendor/amphp/http/src/Http2/Http2ConnectionException.php  ’¿Ýj  ;mëN¤      +   vendor/amphp/http/src/Http2/Http2Parser.phpW  ’¿ÝjW  I Mø¤      .   vendor/amphp/http/src/Cookie/RequestCookie.phpi  ’¿Ýji  „¤¸¤      7   vendor/amphp/http/src/Cookie/InvalidCookieException.phpÚ   ’¿ÝjÚ   $:×¤      /   vendor/amphp/http/src/Cookie/ResponseCookie.phpn*  ’¿Ýjn*  œéÇ|¤      1   vendor/amphp/http/src/Cookie/CookieAttributes.php;"  ’¿Ýj;"  B‚ç¡¤      %   vendor/amphp/http/src/HttpMessage.phpÿ  ’¿Ýjÿ  )âïn¤      0   vendor/amphp/http/src/InvalidHeaderException.phpb  ’¿Ýjb  —_´g¤      %   vendor/amphp/http/src/HttpRequest.php  ’¿Ýj  f0ê¤      ,   vendor/amphp/http/src/Internal/constants.phpq
  ’¿Ýjq
  !@ÿ“¤      &   vendor/amphp/http/src/HttpResponse.phpÝ  ’¿ÝjÝ  ÓÂùÉ¤      $   vendor/amphp/http/src/HttpStatus.php;  ’¿Ýj;  »Z•–¤      '   vendor/amphp/http/src/Http1/Rfc7230.phpÂ  ’¿ÝjÂ  0«÷Ý¤      #   vendor/amphp/http/src/functions.php  ’¿Ýj  œÒÊ¯¤         vendor/amphp/http/composer.json­  ’¿Ýj­  ¥ £'¤      /   vendor/amphp/http-server/.php-cs-fixer.dist.phpß   ’¿Ýjß   +Ød¤      4   vendor/amphp/http-server/composer-require-check.json  ’¿Ýj  lí­Ì¤      6   vendor/amphp/http-server/src/MissingAttributeError.phpÕ   ’¿ÝjÕ   2Ô.¥¤      /   vendor/amphp/http-server/src/RequestHandler.php˜   ’¿Ýj˜   º’__¤      3   vendor/amphp/http-server/src/HttpErrorException.php}  ’¿Ýj}  ¯ëEF¤      E   vendor/amphp/http-server/src/RequestHandler/ClosureRequestHandler.phpz  ’¿Ýjz  Ž¹0N¤      %   vendor/amphp/http-server/src/Push.php  ’¿Ýj  ºV¤      (   vendor/amphp/http-server/src/Request.php51  ’¿Ýj51  A÷6¼¤      )   vendor/amphp/http-server/src/Response.php'  ’¿Ýj'  †‹L¤      1   vendor/amphp/http-server/src/ExceptionHandler.phps  ’¿Ýjs  Ô¿¨[¤      )   vendor/amphp/http-server/src/Trailers.phpÓ  ’¿ÝjÓ  zM/¤      ;   vendor/amphp/http-server/src/Driver/SocketClientFactory.phpg  ’¿Ýjg  ¤Œ/¤      6   vendor/amphp/http-server/src/Driver/UpgradedSocket.phpÔ  ’¿ÝjÔ  ÊÇðp¤      3   vendor/amphp/http-server/src/Driver/Http2Driver.php›Ä  ’¿Ýj›Ä  ù®1#¤      @   vendor/amphp/http-server/src/Driver/DefaultHttpDriverFactory.php\	  ’¿Ýj\	  ƒ›x¤      .   vendor/amphp/http-server/src/Driver/Client.php‰  ’¿Ýj‰  ù‹Ó÷¤      9   vendor/amphp/http-server/src/Driver/HttpDriverFactory.php¯  ’¿Ýj¯  …�£¤      B   vendor/amphp/http-server/src/Driver/LoggingSocketClientFactory.php  ’¿Ýj  !uz¤      G   vendor/amphp/http-server/src/Driver/ConnectionLimitingClientFactory.phpO	  ’¿ÝjO	  q¢-¤      3   vendor/amphp/http-server/src/Driver/Http1Driver.phpA­  ’¿ÝjA­  (x'¤      M   vendor/amphp/http-server/src/Driver/ConnectionLimitingServerSocketFactory.phpI  ’¿ÝjI  mìi¤      E   vendor/amphp/http-server/src/Driver/Internal/StreamTimeoutTracker.phpO  ’¿ÝjO  ß‡`”¤      C   vendor/amphp/http-server/src/Driver/Internal/AbstractHttpDriver.phpe  ’¿Ýje  cËŒ(¤      =   vendor/amphp/http-server/src/Driver/Internal/TimeoutQueue.php+  ’¿Ýj+  G´`ÿ¤      G   vendor/amphp/http-server/src/Driver/Internal/HttpDriverErrorHandler.php“  ’¿Ýj“  DýÕÍ¤      <   vendor/amphp/http-server/src/Driver/Internal/Http2Stream.phpï  ’¿Ýjï  Oˆ�°¤      2   vendor/amphp/http-server/src/Driver/HttpDriver.phpM  ’¿ÝjM  o´¤      5   vendor/amphp/http-server/src/Driver/ClientFactory.php{  ’¿Ýj{  A=Y¤      F   vendor/amphp/http-server/src/Driver/ConnectionLimitingServerSocket.php�  ’¿Ýj�  ”h¤      4   vendor/amphp/http-server/src/Driver/SocketClient.php  ’¿Ýj  ûuó@¤      1   vendor/amphp/http-server/src/Driver/functions.php•   ’¿Ýj•   ºòÂ@¤      ,   vendor/amphp/http-server/src/RequestBody.phpÌ
  ’¿ÝjÌ
  ªà…z¤      4   vendor/amphp/http-server/src/DefaultErrorHandler.phpY  ’¿ÝjY  Fë.e¤      +   vendor/amphp/http-server/src/HttpServer.phpö  ’¿Ýjö  ãÕÏž¤      D   vendor/amphp/http-server/src/Middleware/AllowedMethodsMiddleware.php¡  ’¿Ýj¡  rðxV¤      B   vendor/amphp/http-server/src/Middleware/AccessLoggerMiddleware.phpL  ’¿ÝjL  êÐÖq¤      F   vendor/amphp/http-server/src/Middleware/ExceptionHandlerMiddleware.phpà  ’¿Ýjà  |<™ó¤      ?   vendor/amphp/http-server/src/Middleware/ForwardedHeaderType.php…  ’¿Ýj…  Ž«O¤      =   vendor/amphp/http-server/src/Middleware/ClosureMiddleware.phpù  ’¿Ýjù  $Töä¤      M   vendor/amphp/http-server/src/Middleware/Internal/MiddlewareRequestHandler.phpÁ  ’¿ÝjÁ  Z}=`¤      A   vendor/amphp/http-server/src/Middleware/CompressionMiddleware.php  ’¿Ýj  ù4za¤      ?   vendor/amphp/http-server/src/Middleware/ForwardedMiddleware.php6  ’¿Ýj6  œ…ï¤      5   vendor/amphp/http-server/src/Middleware/Forwarded.php  ’¿Ýj  lÇç¿¤      5   vendor/amphp/http-server/src/Middleware/functions.php  ’¿Ýj  ]bA¤      I   vendor/amphp/http-server/src/Middleware/ConcurrencyLimitingMiddleware.phpg  ’¿Ýjg  ¯ñ5¤      1   vendor/amphp/http-server/src/HttpServerStatus.php¢  ’¿Ýj¢  Y¡…a¤      8   vendor/amphp/http-server/src/DefaultExceptionHandler.phpÜ  ’¿ÝjÜ  £öå¤      +   vendor/amphp/http-server/src/Middleware.php_  ’¿Ýj_  š †¹¤      0   vendor/amphp/http-server/src/ClientException.phpT  ’¿ÝjT  s�«C¤      -   vendor/amphp/http-server/src/ErrorHandler.phpÜ  ’¿ÝjÜ  U$Òª¤      1   vendor/amphp/http-server/src/SocketHttpServer.phpÁ9  ’¿ÝjÁ9  ƒh Ö¤      *   vendor/amphp/http-server/src/functions.phpê   ’¿Ýjê   n; l¤      &   vendor/amphp/http-server/composer.json	  ’¿Ýj	  �z}Æ¤      -   vendor/amphp/http-server/resources/error.html�	  ’¿Ýj�	  ÎAÜ‘¤      =   vendor/amphp/http-server/resources/internal-server-error.htmlª  ’¿Ýjª  ´Hë®¤      .   vendor/amphp/redis/composer-require-check.json<  ’¿Ýj<  8²+°¤      &   vendor/amphp/redis/src/RedisConfig.phpY  ’¿ÝjY  B•@‘¤      +   vendor/amphp/redis/src/Sync/RedisParcel.php	  ’¿Ýj	  :5Â‚¤      *   vendor/amphp/redis/src/Sync/RedisMutex.php�   ’¿Ýj�   }xýù¤      3   vendor/amphp/redis/src/Sync/RedisMutexException.php�   ’¿Ýj�   öýò¤      1   vendor/amphp/redis/src/Sync/RedisMutexOptions.phpG  ’¿ÝjG  L˜?¤      ,   vendor/amphp/redis/src/Command/RedisList.php  ’¿Ýj  >ç”¤      9   vendor/amphp/redis/src/Command/Boundary/ScoreBoundary.php£  ’¿Ýj£  –Baª¤      7   vendor/amphp/redis/src/Command/Boundary/LexBoundary.php  ’¿Ýj  1õ{G¤      4   vendor/amphp/redis/src/Command/Option/SetOptions.phpÐ  ’¿ÝjÐ  fKÏÒ¤      6   vendor/amphp/redis/src/Command/Option/RangeOptions.php·  ’¿Ýj·  qÇU¤      5   vendor/amphp/redis/src/Command/Option/SortOptions.phpâ  ’¿Ýjâ  Ê%æq¤      3   vendor/amphp/redis/src/Command/RedisHyperLogLog.phpƒ  ’¿Ýjƒ  õ®ý¤      1   vendor/amphp/redis/src/Command/RedisSortedSet.phpB  ’¿ÝjB  7ëÊ�¤      +   vendor/amphp/redis/src/Command/RedisMap.php�  ’¿Ýj�  ÚjÞ¤      +   vendor/amphp/redis/src/Command/RedisSet.php¼  ’¿Ýj¼  ¸4q×¤      &   vendor/amphp/redis/src/RedisClient.phpK  ’¿ÝjK  Û‘y}¤      5   vendor/amphp/redis/src/Protocol/ProtocolException.php—   ’¿Ýj—   E`Y¤      2   vendor/amphp/redis/src/Protocol/QueryException.php”   ’¿Ýj”   ŸŸÛ�¤      .   vendor/amphp/redis/src/Protocol/RedisError.phpÒ  ’¿ÝjÒ  JWÜÝ¤      .   vendor/amphp/redis/src/Protocol/RedisValue.php«  ’¿Ýj«  ,‘Î}¤      .   vendor/amphp/redis/src/Protocol/RespParser.phpŒ  ’¿ÝjŒ  ä|œë¤      1   vendor/amphp/redis/src/Protocol/RedisResponse.php  ’¿Ýj  ‚•Z¤      /   vendor/amphp/redis/src/Connection/RedisLink.php  ’¿Ýj  ½½Áö¤      >   vendor/amphp/redis/src/Connection/RedisConnectionException.php    ’¿Ýj    rr£O¤      :   vendor/amphp/redis/src/Connection/SocketRedisConnector.phpg  ’¿Ýjg  Zõì÷¤      6   vendor/amphp/redis/src/Connection/DatabaseSelector.php<  ’¿Ýj<  r^Ù·¤      3   vendor/amphp/redis/src/Connection/Authenticator.phpW  ’¿ÝjW  ýoS¤      ;   vendor/amphp/redis/src/Connection/SocketRedisConnection.phpC  ’¿ÝjC  áO€²¤      5   vendor/amphp/redis/src/Connection/RedisConnection.php3  ’¿Ýj3  dæ¬t¤      4   vendor/amphp/redis/src/Connection/RedisConnector.php  ’¿Ýj  ?êå¤      ;   vendor/amphp/redis/src/Connection/ReconnectingRedisLink.phpß  ’¿Ýjß  Ñßè’¤      -   vendor/amphp/redis/src/Internal/functions.phpS  ’¿ÝjS  éÄ±¤      )   vendor/amphp/redis/src/RedisException.phpq   ’¿Ýjq   AYN™¤      %   vendor/amphp/redis/src/RedisCache.phpJ  ’¿ÝjJ  :¡åµ¤      *   vendor/amphp/redis/src/RedisSubscriber.php‚  ’¿Ýj‚  I�ñ¥¤      ,   vendor/amphp/redis/src/RedisSubscription.php^  ’¿Ýj^  ¢›2p¤      $   vendor/amphp/redis/src/functions.phpr  ’¿Ýjr  \Õ¶¤          vendor/amphp/redis/composer.json  ’¿Ýj  (~b¤      -   vendor/amphp/websocket/.php-cs-fixer.dist.php½   ’¿Ýj½   �.Á8¤      2   vendor/amphp/websocket/composer-require-check.jsonì  ’¿Ýjì  -xé-¤      0   vendor/amphp/websocket/src/ConstantRateLimit.phpð
  ’¿Ýjð
  ù³ž|¤      -   vendor/amphp/websocket/src/WebsocketCount.phpP  ’¿ÝjP  �" ¤      /   vendor/amphp/websocket/src/WebsocketMessage.php½  ’¿Ýj½  TåÐ¤      1   vendor/amphp/websocket/src/WebsocketCloseInfo.phpç  ’¿Ýjç  W
�¤      1   vendor/amphp/websocket/src/WebsocketCloseCode.php
  ’¿Ýj
  ‡¬–¤      ,   vendor/amphp/websocket/src/Rfc6455Client.php»+  ’¿Ýj»+  ”Üåš¤      8   vendor/amphp/websocket/src/Parser/WebsocketFrameType.php¼  ’¿Ýj¼  ö$Ê¤      C   vendor/amphp/websocket/src/Parser/WebsocketFrameCompilerFactory.phpH  ’¿ÝjH  7xâ"¤      5   vendor/amphp/websocket/src/Parser/WebsocketParser.php­  ’¿Ýj­  Wd‚�¤      <   vendor/amphp/websocket/src/Parser/WebsocketParserFactory.php`  ’¿Ýj`  æÏ2~¤      :   vendor/amphp/websocket/src/Parser/Rfc6455FrameCompiler.phpÓ  ’¿ÝjÓ  Û´¤      3   vendor/amphp/websocket/src/Parser/Rfc6455Parser.php%  ’¿Ýj%  Êk-Ì¤      A   vendor/amphp/websocket/src/Parser/Rfc6455FrameCompilerFactory.php&  ’¿Ýj&  %c 6¤      :   vendor/amphp/websocket/src/Parser/Rfc6455ParserFactory.phpx  ’¿Ýjx  ºhþ[¤      ;   vendor/amphp/websocket/src/Parser/WebsocketFrameHandler.php  ’¿Ýj  ë™Y¸¤      >   vendor/amphp/websocket/src/Parser/WebsocketParserException.php"  ’¿Ýj"  þ^�$¤      <   vendor/amphp/websocket/src/Parser/WebsocketFrameCompiler.phpç  ’¿Ýjç  tÌÐ¤      .   vendor/amphp/websocket/src/WebsocketClient.php¯  ’¿Ýj¯  dÀÚ¤      1   vendor/amphp/websocket/src/WebsocketTimestamp.phpç   ’¿Ýjç   c×1€¤      7   vendor/amphp/websocket/src/WebsocketClosedException.php�  ’¿Ýj�  ›…�¤      5   vendor/amphp/websocket/src/PeriodicHeartbeatQueue.phpB  ’¿ÝjB  *‹›¤      M   vendor/amphp/websocket/src/Compression/WebsocketCompressionContextFactory.phpÂ  ’¿ÝjÂ  Ið³¤      =   vendor/amphp/websocket/src/Compression/Rfc7692Compression.php�  ’¿Ýj�  SHyZ¤      D   vendor/amphp/websocket/src/Compression/Rfc7692CompressionFactory.phpá  ’¿Ýjá  ¨�†¤      F   vendor/amphp/websocket/src/Compression/WebsocketCompressionContext.php§  ’¿Ýj§  aå‡{¤      1   vendor/amphp/websocket/src/WebsocketException.php”   ’¿Ýj”   žë±¤      ?   vendor/amphp/websocket/src/Internal/WebsocketClientMetadata.phpæ  ’¿Ýjæ  <5_v¤      ;   vendor/amphp/websocket/src/Internal/Rfc6455FrameHandler.php-  ’¿Ýj-  Z]þ-¤      (   vendor/amphp/websocket/src/functions.phpv  ’¿Ýjv  Fgéª¤      1   vendor/amphp/websocket/src/WebsocketRateLimit.phpÆ  ’¿ÝjÆ  –û%\¤      6   vendor/amphp/websocket/src/WebsocketHeartbeatQueue.phpM  ’¿ÝjM  8Ø2·¤      $   vendor/amphp/websocket/composer.jsonì  ’¿Ýjì  <X�ó¤      ,   vendor/amphp/parallel/.php-cs-fixer.dist.phpÞ   ’¿ÝjÞ   È±ØX¤      1   vendor/amphp/parallel/composer-require-check.jsonx  ’¿Ýjx  Zqõk¤      -   vendor/amphp/parallel/src/Ipc/LocalIpcHub.php!
  ’¿Ýj!
  ˜E^¤      (   vendor/amphp/parallel/src/Ipc/IpcHub.php€  ’¿Ýj€  ?ýÂ¤      .   vendor/amphp/parallel/src/Ipc/SocketIpcHub.php„  ’¿Ýj„  5FàV¤      +   vendor/amphp/parallel/src/Ipc/functions.php  ’¿Ýj  [Ê¶’¤      3   vendor/amphp/parallel/src/Context/ThreadContext.php1  ’¿Ýj1  ïsÞ÷¤      ;   vendor/amphp/parallel/src/Context/DefaultContextFactory.phpñ  ’¿Ýjñ  ¤RæT¤      4   vendor/amphp/parallel/src/Context/ContextFactory.phpÿ  ’¿Ýjÿ  ±zi¤      1   vendor/amphp/parallel/src/Context/ForkContext.phpý  ’¿Ýjý  ^y°s¤      4   vendor/amphp/parallel/src/Context/ProcessContext.php?-  ’¿Ýj?-  ~¥Ž÷¤      :   vendor/amphp/parallel/src/Context/ThreadContextFactory.php   ’¿Ýj   )U¤      8   vendor/amphp/parallel/src/Context/ForkContextFactory.phpÂ  ’¿ÝjÂ  Î‘X¤      6   vendor/amphp/parallel/src/Context/ContextException.php•   ’¿Ýj•   ; 4™¤      7   vendor/amphp/parallel/src/Context/ContextPanicError.phpð   ’¿Ýjð   <ÎÐ¬¤      =   vendor/amphp/parallel/src/Context/Internal/ContextChannel.phpK  ’¿ÝjK  Â„Óü¤      :   vendor/amphp/parallel/src/Context/Internal/ExitSuccess.phpÝ  ’¿ÝjÝ  ê¾Ö¤      :   vendor/amphp/parallel/src/Context/Internal/ExitFailure.phpp  ’¿Ýjp  èÁ©J¤      :   vendor/amphp/parallel/src/Context/Internal/ParallelHub.phpã  ’¿Ýjã  â2§p¤      =   vendor/amphp/parallel/src/Context/Internal/process-runner.phps
  ’¿Ýjs
  ø"I¤      =   vendor/amphp/parallel/src/Context/Internal/ContextMessage.php&  ’¿Ýj&  �Î¤¤      >   vendor/amphp/parallel/src/Context/Internal/AbstractContext.php#  ’¿Ýj#  P€–î¤      9   vendor/amphp/parallel/src/Context/Internal/ExitResult.php¦  ’¿Ýj¦  aöåö¤      ?   vendor/amphp/parallel/src/Context/Internal/ContextException.phpÛ	  ’¿ÝjÛ	  µ±ƒT¤      8   vendor/amphp/parallel/src/Context/Internal/functions.phpF  ’¿ÝjF  À*ü¤      ;   vendor/amphp/parallel/src/Context/ProcessContextFactory.php+  ’¿Ýj+  *Õ3¤      -   vendor/amphp/parallel/src/Context/Context.phpÁ  ’¿ÝjÁ  Ù’]¤      1   vendor/amphp/parallel/src/Context/StatusError.phpf   ’¿Ýjf   WA¡^¤      /   vendor/amphp/parallel/src/Context/functions.php  ’¿Ýj  ¼Œ­~¤      4   vendor/amphp/parallel/src/Worker/WorkerException.phpm   ’¿Ýjm   ØÙÖ&¤      )   vendor/amphp/parallel/src/Worker/Task.php¶  ’¿Ýj¶  m‚"½¤      9   vendor/amphp/parallel/src/Worker/TaskFailureThrowable.phpe  ’¿Ýje  ˜Í&g¤      9   vendor/amphp/parallel/src/Worker/DelegatingWorkerPool.php-  ’¿Ýj-  €ÉŒ¤      9   vendor/amphp/parallel/src/Worker/TaskFailureException.phpß   ’¿Ýjß   -ñy¤      +   vendor/amphp/parallel/src/Worker/Worker.phpë  ’¿Ýjë  fþ¬¤      6   vendor/amphp/parallel/src/Worker/ContextWorkerPool.phpÄ&  ’¿ÝjÄ&  5IOh¤      2   vendor/amphp/parallel/src/Worker/WorkerFactory.phpg  ’¿Ýjg  *ìNñ¤      9   vendor/amphp/parallel/src/Worker/Internal/TaskFailure.php*  ’¿Ýj*  ïa¤      :   vendor/amphp/parallel/src/Worker/Internal/PooledWorker.phpå  ’¿Ýjå  ¹[3¤      8   vendor/amphp/parallel/src/Worker/Internal/TaskResult.php-  ’¿Ýj-  ›!Aº¤      ;   vendor/amphp/parallel/src/Worker/Internal/TaskCancelled.php  ’¿Ýj  ‹fš¤      ;   vendor/amphp/parallel/src/Worker/Internal/ContextWorker.php(  ’¿Ýj(  ÌiÊ÷¤      <   vendor/amphp/parallel/src/Worker/Internal/TaskSubmission.php­  ’¿Ýj­  “Ýµ¤      ?   vendor/amphp/parallel/src/Worker/Internal/TaskExceptionType.phpŒ  ’¿ÝjŒ  –"ñ9¤      8   vendor/amphp/parallel/src/Worker/Internal/JobMessage.phpi  ’¿Ýji  -?k*¤      9   vendor/amphp/parallel/src/Worker/Internal/task-runner.phpa  ’¿Ýja  S/¤      =   vendor/amphp/parallel/src/Worker/Internal/JobCancellation.phpŒ   ’¿ÝjŒ   Zš·š¤      7   vendor/amphp/parallel/src/Worker/Internal/JobPacket.phpS  ’¿ÝjS  Á÷s_¤      9   vendor/amphp/parallel/src/Worker/Internal/TaskSuccess.php,  ’¿Ýj,  KÂ¤      8   vendor/amphp/parallel/src/Worker/Internal/JobChannel.php_  ’¿Ýj_  í$��¤      5   vendor/amphp/parallel/src/Worker/TaskFailureError.php×   ’¿Ýj×   ¢‹¤      ;   vendor/amphp/parallel/src/Worker/TaskCancelledException.php%  ’¿Ýj%  (Ow,¤      /   vendor/amphp/parallel/src/Worker/WorkerPool.phpõ  ’¿Ýjõ  Ó‹¤      6   vendor/amphp/parallel/src/Worker/LimitedWorkerPool.php@  ’¿Ýj@  §' ‚¤      .   vendor/amphp/parallel/src/Worker/Execution.phpb  ’¿Ýjb  0ûç¤      9   vendor/amphp/parallel/src/Worker/ContextWorkerFactory.php«  ’¿Ýj«  /Õ
¤      .   vendor/amphp/parallel/src/Worker/functions.php„  ’¿Ýj„  ~4B¤      #   vendor/amphp/parallel/composer.jsono  ’¿Ýjo  Õ¨­í      9   vendor/amphp/websocket-client/composer-require-check.json   ’¿Ýj   ùfz¤      6   vendor/amphp/websocket-client/test-autobahn/runner.php2
  ’¿Ýj2
  LW‰¤      E   vendor/amphp/websocket-client/test-autobahn/config/fuzzingserver.json;  ’¿Ýj;  ,ei¤      ?   vendor/amphp/websocket-client/src/WebsocketConnectException.phpì  ’¿Ýjì  IxAÂ¤      >   vendor/amphp/websocket-client/src/Rfc6455ConnectionFactory.phpE  ’¿ÝjE  ÖÛw¤      9   vendor/amphp/websocket-client/src/WebsocketConnection.phpN  ’¿ÝjN  'K.	¤      8   vendor/amphp/websocket-client/src/WebsocketConnector.phpØ  ’¿ÝjØ  ›†ZU¤      7   vendor/amphp/websocket-client/src/Rfc6455Connection.php(  ’¿Ýj(  3=Õ¦¤      8   vendor/amphp/websocket-client/src/WebsocketHandshake.phpR  ’¿ÝjR  tÂ¤      @   vendor/amphp/websocket-client/src/WebsocketConnectionFactory.php×  ’¿Ýj×  ˜ñwH¤      6   vendor/amphp/websocket-client/src/Rfc6455Connector.php›  ’¿Ýj›  «ìu¤      /   vendor/amphp/websocket-client/src/functions.php%  ’¿Ýj%  ñÊá¤      +   vendor/amphp/websocket-client/composer.json,  ’¿Ýj,  ëGï¤      *   vendor/amphp/socket/.php-cs-fixer.dist.phpÞ   ’¿ÝjÞ   È±ØX¤      /   vendor/amphp/socket/composer-require-check.json  ’¿Ýj  ëÔO¤      7   vendor/amphp/socket/src/ResourceServerSocketFactory.phpÓ  ’¿ÝjÓ  ÿ”ïF¤      +   vendor/amphp/socket/src/SocketException.phpŽ   ’¿ÝjŽ   ,ù‡¼¤      (   vendor/amphp/socket/src/ServerSocket.php“  ’¿Ýj“  l¼B¤      (   vendor/amphp/socket/src/TlsException.phpì   ’¿Ýjì   �ù¤      '   vendor/amphp/socket/src/Certificate.php
  ’¿Ýj
  ŒU¤      +   vendor/amphp/socket/src/SocketConnector.php’  ’¿Ýj’  ªßÏ`¤      *   vendor/amphp/socket/src/ResourceSocket.php`  ’¿Ýj`  ŠáT­¤      .   vendor/amphp/socket/src/PendingAcceptError.phpæ  ’¿Ýjæ  «µdÊ¤      ,   vendor/amphp/socket/src/ClientTlsContext.php�8  ’¿Ýj�8  ·\�¤      /   vendor/amphp/socket/src/UnlimitedSocketPool.phpÉ  ’¿ÝjÉ  àî*È¤      +   vendor/amphp/socket/src/InternetAddress.phpÐ  ’¿ÝjÐ  &lÛ™¤      2   vendor/amphp/socket/src/InternetAddressVersion.phpu   ’¿Ýju   ¦Ä9•¤      &   vendor/amphp/socket/src/SocketPool.php>  ’¿Ýj>  s	¢¤      .   vendor/amphp/socket/src/DnsSocketConnector.php€  ’¿Ýj€  Pä¤      1   vendor/amphp/socket/src/StaticSocketConnector.php  ’¿Ýj  ÿ¶ZÆ¤      ,   vendor/amphp/socket/src/ConnectException.php¸   ’¿Ýj¸   }»n1¤      '   vendor/amphp/socket/src/BindContext.phpü  ’¿Ýjü  è2³ù¤      %   vendor/amphp/socket/src/UdpSocket.phpJ  ’¿ÝjJ  @MOî¤      0   vendor/amphp/socket/src/ResourceServerSocket.php·  ’¿Ýj·  Åœß
¤      0   vendor/amphp/socket/src/RetrySocketConnector.php`  ’¿Ýj`  £I¯¤      #   vendor/amphp/socket/src/TlsInfo.php”  ’¿Ýj”  æD.’¤      *   vendor/amphp/socket/src/ConnectContext.phpt  ’¿Ýjt  cíš¤      -   vendor/amphp/socket/src/ResourceUdpSocket.phph  ’¿Ýjh  O7E¤      ,   vendor/amphp/socket/src/ServerTlsContext.php¤?  ’¿Ýj¤?  °I ï¤      .   vendor/amphp/socket/src/Internal/functions.phpº  ’¿Ýjº  Oc€¤      3   vendor/amphp/socket/src/SocketAddress/functions.php3  ’¿Ýj3  \$º)¤      "   vendor/amphp/socket/src/Socket.php?  ’¿Ýj?  íf¤      )   vendor/amphp/socket/src/SocketAddress.phpÂ   ’¿ÝjÂ   oöè¤      '   vendor/amphp/socket/src/UnixAddress.phpÔ  ’¿ÝjÔ   N ¤      $   vendor/amphp/socket/src/TlsState.phpŸ   ’¿ÝjŸ   ù‚å¤      '   vendor/amphp/socket/src/CidrMatcher.php�  ’¿Ýj�  "žL�¤      /   vendor/amphp/socket/src/PendingReceiveError.phpì  ’¿Ýjì  ÍÆ¤      1   vendor/amphp/socket/src/Socks5SocketConnector.phpÊ  ’¿ÝjÊ  ¹»JÌ¤      %   vendor/amphp/socket/src/functions.phpJ  ’¿ÝjJ  cêdÚ¤      /   vendor/amphp/socket/src/ServerSocketFactory.phpó   ’¿Ýjó   l…¤      -   vendor/amphp/socket/src/SocketAddressType.phpt   ’¿Ýjt   ¯|9¤      !   vendor/amphp/socket/composer.jsonŸ  ’¿ÝjŸ  ív,Ê¤      ,   vendor/amphp/pipeline/.php-cs-fixer.dist.phpß   ’¿Ýjß   +Ød¤      1   vendor/amphp/pipeline/composer-require-check.json  ’¿Ýj  éäM]¤      &   vendor/amphp/pipeline/src/Pipeline.phpØ8  ’¿ÝjØ8  MkF€¤      /   vendor/amphp/pipeline/src/DisposedException.php§  ’¿Ýj§  ÿQ�ô¤      0   vendor/amphp/pipeline/src/ConcurrentIterator.php©  ’¿Ýj©  Á`•š¤      #   vendor/amphp/pipeline/src/Queue.phpç  ’¿Ýjç  ¬i$ì¤      @   vendor/amphp/pipeline/src/Internal/ConcurrentChainedIterator.phpT  ’¿ÝjT  ’}ëÈ¤      ?   vendor/amphp/pipeline/src/Internal/ConcurrentMergedIterator.phpþ  ’¿Ýjþ  hJET¤      <   vendor/amphp/pipeline/src/Internal/IntermediateOperation.phpë   ’¿Ýjë   M!7 ¤      @   vendor/amphp/pipeline/src/Internal/ConcurrentClosureIterator.phpÐ  ’¿ÝjÐ  /¾;E¤      >   vendor/amphp/pipeline/src/Internal/ConcurrentArrayIterator.php'	  ’¿Ýj'	  ³P±¤      1   vendor/amphp/pipeline/src/Internal/QueueState.phpM3  ’¿ÝjM3  Sý/¤      @   vendor/amphp/pipeline/src/Internal/ConcurrentFlatMapIterator.php÷  ’¿Ýj÷  cW6¤      4   vendor/amphp/pipeline/src/Internal/SortOperation.php¹  ’¿Ýj¹  ´ŽÙ¤      >   vendor/amphp/pipeline/src/Internal/ConcurrentQueueIterator.php(  ’¿Ýj(  ÃV6¤      7   vendor/amphp/pipeline/src/Internal/FlatMapOperation.php  ’¿Ýj  æ¡ëY¤      A   vendor/amphp/pipeline/src/Internal/ConcurrentIterableIterator.php.  ’¿Ýj.  ³ÿ€¤      /   vendor/amphp/pipeline/src/Internal/Sequence.php+  ’¿Ýj+  §àâ¤      #   vendor/amphp/pipeline/composer.json)  ’¿Ýj)  X¤      '   vendor/amphp/dns/.php-cs-fixer.dist.phpß   ’¿Ýjß   +Ød¤      ,   vendor/amphp/dns/composer-require-check.json„  ’¿Ýj„  €,?¤      4   vendor/amphp/dns/src/BlockingFallbackDnsResolver.php›  ’¿Ýj›  êL,¤      (   vendor/amphp/dns/src/DnsConfigLoader.php±   ’¿Ýj±   ‘Hq¤      $   vendor/amphp/dns/src/DnsResolver.php”  ’¿Ýj”  ƒVÓã¤      "   vendor/amphp/dns/src/DnsRecord.php¹  ’¿Ýj¹  —ïu¤      2   vendor/amphp/dns/src/MissingDnsRecordException.php™   ’¿Ýj™   {<›©¤      ,   vendor/amphp/dns/src/DnsTimeoutException.php“   ’¿Ýj“   rfb¤      %   vendor/amphp/dns/src/DnsException.php^   ’¿Ýj^   i/{¤      /   vendor/amphp/dns/src/Rfc1035StubDnsResolver.phpšN  ’¿ÝjšN  ùT	¤      "   vendor/amphp/dns/src/DnsConfig.php�  ’¿Ýj�  î€¤      ,   vendor/amphp/dns/src/UnixDnsConfigLoader.phpl  ’¿Ýjl  ;\mõ¤      +   vendor/amphp/dns/src/DnsConfigException.phpv  ’¿Ýjv  ƒ¥““¤      .   vendor/amphp/dns/src/StaticDnsConfigLoader.php¡  ’¿Ýj¡  ¡ºS¤      +   vendor/amphp/dns/src/Internal/TcpSocket.php±  ’¿Ýj±  ¬|ü™¤      +   vendor/amphp/dns/src/Internal/UdpSocket.phpò  ’¿Ýjò  ¾Ø<Z¤      (   vendor/amphp/dns/src/Internal/Socket.phpl&  ’¿Ýjl&  Ÿ×‰í      -   vendor/amphp/dns/src/InvalidNameException.php”   ’¿Ýj”   DM`*¤      /   vendor/amphp/dns/src/WindowsDnsConfigLoader.php7  ’¿Ýj7  ¹ò€’¤      "   vendor/amphp/dns/src/functions.phpã
  ’¿Ýjã
  ë
€¨¤      #   vendor/amphp/dns/src/HostLoader.php†	  ’¿Ýj†	  ª¥KM¤         vendor/amphp/dns/composer.jsonb  ’¿Ýjb  ¥Î¤      &   vendor/amphp/log/examples/file-log.phpÎ  ’¿ÝjÎ  2¶¹¤      )   vendor/amphp/log/examples/hello-world.phpt  ’¿Ýjt  Å^4˜¤      '   vendor/amphp/log/.php-cs-fixer.dist.phpÈ   ’¿ÝjÈ   »´óÚ¤      ,   vendor/amphp/log/composer-require-check.jsonª  ’¿Ýjª  ód÷¾¤      &   vendor/amphp/log/src/StreamHandler.php¨  ’¿Ýj¨  Ã8ý¤      )   vendor/amphp/log/src/ConsoleFormatter.phpé
  ’¿Ýjé
  ÊLÕm¤      "   vendor/amphp/log/src/functions.php<  ’¿Ýj<  ”³(~¤         vendor/amphp/log/composer.jsonâ  ’¿Ýjâ  ã÷¤      *   vendor/amphp/parser/.php-cs-fixer.dist.phpß   ’¿Ýjß   +Ød¤      "   vendor/amphp/parser/src/Parser.phpG  ’¿ÝjG  ÄŒ*F¤      1   vendor/amphp/parser/src/InvalidDelimiterError.php  ’¿Ýj  ¾4ÅW¤      !   vendor/amphp/parser/composer.json¹  ’¿Ýj¹  ò¥¼¤      .   vendor/amphp/sql-common/.php-cs-fixer.dist.php½   ’¿Ýj½   �.Á8¤      4   vendor/amphp/sql-common/test/SqlPooledResultTest.php�	  ’¿Ýj�	  ÏøÁ…¤      <   vendor/amphp/sql-common/test/SqlCommonConnectionPoolTest.phpY  ’¿ÝjY  _ì>Õ¤      6   vendor/amphp/sql-common/test/RetrySqlConnectorTest.phpu	  ’¿Ýju	  ÚB¢í¤      3   vendor/amphp/sql-common/test/Stub/StubSqlResult.php8  ’¿Ýj8  Û§‹c¤      9   vendor/amphp/sql-common/test/Stub/StubSqlPooledResult.phpO  ’¿ÝjO  ýD ¤      5   vendor/amphp/sql-common/test/SqlStatementPoolTest.php  ’¿Ýj  f°¤      /   vendor/amphp/sql-common/src/SqlPooledResult.php�  ’¿Ýj�  éHœ¤      0   vendor/amphp/sql-common/src/SqlStatementPool.phpæ  ’¿Ýjæ  ðŸo§¤      7   vendor/amphp/sql-common/src/SqlCommonConnectionPool.phpé2  ’¿Ýjé2  Ê^¢¸¤      2   vendor/amphp/sql-common/src/SqlPooledStatement.php†  ’¿Ýj†  �Ö99¤      4   vendor/amphp/sql-common/src/SqlNestedTransaction.php   ’¿Ýj   òöž¤      >   vendor/amphp/sql-common/src/SqlNestableTransactionExecutor.php�  ’¿Ýj�  | ^¤      1   vendor/amphp/sql-common/src/RetrySqlConnector.php  ’¿Ýj  QÄ³¤      4   vendor/amphp/sql-common/src/SqlPooledTransaction.phpÊ  ’¿ÝjÊ  ¹ƒ£z¤      0   vendor/amphp/sql-common/src/SqlCommandResult.phpc  ’¿Ýjc  ­½:¤      8   vendor/amphp/sql-common/src/SqlConnectionTransaction.php'#  ’¿Ýj'#  IÖÉ¸¤      %   vendor/amphp/sql-common/composer.jsonÝ  ’¿ÝjÝ  .“ß¤      =   vendor/amphp/http-client/src/EventListener/LogHttpArchive.php±.  ’¿Ýj±.  îª$©¤      6   vendor/amphp/http-client/src/MissingAttributeError.phpÕ   ’¿ÝjÕ   bÝ«â¤      0   vendor/amphp/http-client/src/SocketException.phpl   ’¿Ýjl   .6í      -   vendor/amphp/http-client/src/TlsException.phpq   ’¿Ýjq   áþKªí      %   vendor/amphp/http-client/src/Form.phpî  ’¿Ýjî  Ä¨[¤      (   vendor/amphp/http-client/src/Request.php¤A  ’¿Ýj¤A  +�‹¤      +   vendor/amphp/http-client/src/HttpClient.php  ’¿Ýj  ¾žÏ_¤      7   vendor/amphp/http-client/src/ApplicationInterceptor.php  ’¿Ýj  Å–&b¤      )   vendor/amphp/http-client/src/Response.php  ’¿Ýj  <x´Ä¤      )   vendor/amphp/http-client/src/Trailers.php  ’¿Ýj  ^Þžˆ¤      C   vendor/amphp/http-client/src/Connection/UnlimitedConnectionPool.phpG  ’¿ÝjG   4œ«¤      6   vendor/amphp/http-client/src/Connection/Connection.phpÌ  ’¿ÝjÌ  s"n¤      :   vendor/amphp/http-client/src/Connection/UpgradedSocket.php€  ’¿Ýj€  d#?“¤      ;   vendor/amphp/http-client/src/Connection/Http1Connection.php}e  ’¿Ýj}e  +$™ß¤      >   vendor/amphp/http-client/src/Connection/StreamLimitingPool.php¶
  ’¿Ýj¶
  ›VÌ–¤      6   vendor/amphp/http-client/src/Connection/HttpStream.phpŽ  ’¿ÝjŽ  ãç¤      =   vendor/amphp/http-client/src/Connection/ConnectionFactory.php¤  ’¿Ýj¤  _’”¤      ;   vendor/amphp/http-client/src/Connection/Http2Connection.phpi  ’¿Ýji  NµQä¤      D   vendor/amphp/http-client/src/Connection/DefaultConnectionFactory.php  ’¿Ýj  W,í¤      =   vendor/amphp/http-client/src/Connection/InterceptedStream.phpf	  ’¿Ýjf	  ™õww¤      @   vendor/amphp/http-client/src/Connection/Internal/Http1Parser.php6=  ’¿Ýj6=  È]7¤      M   vendor/amphp/http-client/src/Connection/Internal/Http2ConnectionProcessor.php~Ø  ’¿Ýj~Ø  üÒ^q¤      @   vendor/amphp/http-client/src/Connection/Internal/Http2Stream.phpì  ’¿Ýjì  Þ ¨)¤      F   vendor/amphp/http-client/src/Connection/Internal/RequestNormalizer.phph  ’¿Ýjh  €â¥¦¤      2   vendor/amphp/http-client/src/Connection/Stream.phpb  ’¿Ýjb  þôä›¤      :   vendor/amphp/http-client/src/Connection/ConnectionPool.php/  ’¿Ýj/  +.†¤      B   vendor/amphp/http-client/src/Connection/ConnectionLimitingPool.phpþ.  ’¿Ýjþ.  •Là¤      /   vendor/amphp/http-client/src/ParseException.php"  ’¿Ýj"  �çËk¤      3   vendor/amphp/http-client/src/DelegateHttpClient.php  ’¿Ýj  ÿ¹è¤      ,   vendor/amphp/http-client/src/HttpContent.phpß  ’¿Ýjß  ¶Æ{÷¤      1   vendor/amphp/http-client/src/TimeoutException.phps   ’¿Ýjs   ùTë‚í      2   vendor/amphp/http-client/src/HttpClientBuilder.php/  ’¿Ýj/  :ý2¤      0   vendor/amphp/http-client/src/BufferedContent.php  ’¿Ýj  ¹"u@¤      6   vendor/amphp/http-client/src/InterceptedHttpClient.php³  ’¿Ýj³  äÓý?¤      .   vendor/amphp/http-client/src/HttpException.phpg   ’¿Ýjg   ßm*í      3   vendor/amphp/http-client/src/NetworkInterceptor.php²  ’¿Ýj²  ¥KÒ¤      0   vendor/amphp/http-client/src/StreamedContent.phpU  ’¿ÝjU  óžb¤      /   vendor/amphp/http-client/src/Internal/Phase.phpO  ’¿ÝjO  Ùd]d¤      D   vendor/amphp/http-client/src/Internal/SizeLimitingReadableStream.php@  ’¿Ýj@  Â[£¤      7   vendor/amphp/http-client/src/Internal/HarAttributes.phpª  ’¿Ýjª  E¢Þ¤      6   vendor/amphp/http-client/src/Internal/EventInvoker.php].  ’¿Ýj].  îÈ·Ý¤      <   vendor/amphp/http-client/src/Internal/ResponseBodyStream.php  ’¿Ýj  ÇJó¤      3   vendor/amphp/http-client/src/Internal/FormField.php×  ’¿Ýj×  ¿[Ú¤      3   vendor/amphp/http-client/src/Internal/functions.php·  ’¿Ýj·  .¯=¤      F   vendor/amphp/http-client/src/Interceptor/TooManyRedirectsException.php  ’¿Ýj  á¤Ïí      8   vendor/amphp/http-client/src/Interceptor/MatchOrigin.php÷  ’¿Ýj÷  Mí=§¤      =   vendor/amphp/http-client/src/Interceptor/AddRequestHeader.phpú  ’¿Ýjú  Ÿ´¤      ?   vendor/amphp/http-client/src/Interceptor/DecompressResponse.phpÇ
  ’¿ÝjÇ
  ß8 ¤      E   vendor/amphp/http-client/src/Interceptor/SetResponseHeaderIfUnset.php›  ’¿Ýj›  �—Z
¤      A   vendor/amphp/http-client/src/Interceptor/RemoveResponseHeader.php•  ’¿Ýj•  #gÃf¤      ;   vendor/amphp/http-client/src/Interceptor/ResolveBaseUri.php0  ’¿Ýj0  ×ðµÀ¤      >   vendor/amphp/http-client/src/Interceptor/ForbidUriUserInfo.php©  ’¿Ýj©  'ËÇ-¤      ;   vendor/amphp/http-client/src/Interceptor/ModifyResponse.php9  ’¿Ýj9  Øë¤      D   vendor/amphp/http-client/src/Interceptor/SetRequestHeaderIfUnset.php“  ’¿Ýj“  úœ(¤      <   vendor/amphp/http-client/src/Interceptor/FollowRedirects.php9$  ’¿Ýj9$  	^œ¤      =   vendor/amphp/http-client/src/Interceptor/SetRequestHeader.phpE  ’¿ÝjE  å¤      :   vendor/amphp/http-client/src/Interceptor/ModifyRequest.phpˆ  ’¿Ýjˆ  ²ÇtÏ¤      >   vendor/amphp/http-client/src/Interceptor/SetResponseHeader.phpL  ’¿ÝjL  «"Zß¤      :   vendor/amphp/http-client/src/Interceptor/RetryRequests.phpC  ’¿ÝjC  ŸnŠo¤      >   vendor/amphp/http-client/src/Interceptor/AddResponseHeader.php  ’¿Ýj  L³W¤      @   vendor/amphp/http-client/src/Interceptor/RemoveRequestHeader.phpŽ  ’¿ÝjŽ  ó�Ã¤      >   vendor/amphp/http-client/src/Interceptor/SetRequestTimeout.php¨  ’¿Ýj¨  ¿Àâ¤      8   vendor/amphp/http-client/src/InvalidRequestException.phpÈ  ’¿ÝjÈ  œ Â¢¤      1   vendor/amphp/http-client/src/PooledHttpClient.phpA	  ’¿ÝjA	  89ùƒ¤      *   vendor/amphp/http-client/src/functions.php&  ’¿Ýj&  �Èo¤      .   vendor/amphp/http-client/src/EventListener.phpŠ  ’¿ÝjŠ  D¯¹¤      &   vendor/amphp/http-client/composer.jsonX	  ’¿ÝjX	  ¤/kÁ¤      3   vendor/amphp/http-client-cookies/examples/basic.php3  ’¿Ýj3  Ê¥‘¯¤      7   vendor/amphp/http-client-cookies/.php-cs-fixer.dist.phpÞ   ’¿ÝjÞ   È±ØX¤      <   vendor/amphp/http-client-cookies/composer-require-check.json;  ’¿Ýj;  t	—¤      ;   vendor/amphp/http-client-cookies/test/FileCookieJarTest.php  ’¿Ýj  zM Ê¤      ?   vendor/amphp/http-client-cookies/test/InMemoryCookieJarTest.phpÖ   ’¿ÝjÖ   PCU¤      4   vendor/amphp/http-client-cookies/test/CookieTest.phpJ  ’¿ÝjJ  ¥Äà+¤      :   vendor/amphp/http-client-cookies/test/ClientCookieTest.phpR  ’¿ÝjR  yF'f¤      7   vendor/amphp/http-client-cookies/test/CookieJarTest.phpó	  ’¿Ýjó	  ÄT9h¤      G   vendor/amphp/http-client-cookies/test/Internal/PublicSuffixListTest.phpì  ’¿Ýjì  À®ÁD¤      6   vendor/amphp/http-client-cookies/src/NullCookieJar.phpu  ’¿Ýju  SË±¤      7   vendor/amphp/http-client-cookies/src/LocalCookieJar.php=  ’¿Ýj=  íëOô¤      2   vendor/amphp/http-client-cookies/src/CookieJar.php\  ’¿Ýj\  ÜÀ]¤      B   vendor/amphp/http-client-cookies/src/Internal/PublicSuffixList.phpW  ’¿ÝjW  á¥~÷¤      6   vendor/amphp/http-client-cookies/src/FileCookieJar.phpß  ’¿Ýjß  7:q¤      :   vendor/amphp/http-client-cookies/src/CookieInterceptor.php‚  ’¿Ýj‚  œ;ø´¤      ;   vendor/amphp/http-client-cookies/res/public_suffix_list.dat^ ’¿Ýj^  ¥�ƒ¤      .   vendor/amphp/http-client-cookies/composer.json„  ’¿Ýj„  $¿›¤      -   vendor/amphp/file/composer-require-check.json  ’¿Ýj  B?`Ð¤      (   vendor/amphp/file/test/FileCacheTest.php  ’¿Ýj  |õ8(¤      /   vendor/amphp/file/test/FilesystemDriverTest.phpYH  ’¿ÝjYH  ÑR¤      )   vendor/amphp/file/test/FilesystemTest.php`  ’¿Ýj`  Í'Ñ¤      "   vendor/amphp/file/test/Fixture.phpè
  ’¿Ýjè
  =k²¤      8   vendor/amphp/file/test/Driver/UvFilesystemDriverTest.php  ’¿Ýj  -ƒÓ£¤      ,   vendor/amphp/file/test/Driver/UvFileTest.php�  ’¿Ýj�  Í™Dˆ¤      2   vendor/amphp/file/test/Driver/BlockingFileTest.php<  ’¿Ýj<  I8š¹¤      C   vendor/amphp/file/test/Driver/StatusCachingFilesystemDriverTest.php”  ’¿Ýj”  (ZL#¤      2   vendor/amphp/file/test/Driver/ParallelFileTest.php  ’¿Ýj  œÑù ¤      >   vendor/amphp/file/test/Driver/ParallelFilesystemDriverTest.phpa  ’¿Ýja  iA»¤      -   vendor/amphp/file/test/Driver/EioFileTest.phpÙ  ’¿ÝjÙ  <Å‡R¤      >   vendor/amphp/file/test/Driver/BlockingFilesystemDriverTest.php`  ’¿Ýj`  ®_*¤      7   vendor/amphp/file/test/Driver/StatusCachingFileTest.phpp  ’¿Ýjp  ¤’Œ¤      9   vendor/amphp/file/test/Driver/EioFilesystemDriverTest.phpó  ’¿Ýjó  !Öp4¤      -   vendor/amphp/file/test/KeyedFileMutexTest.php  ’¿Ýj  2~rs¤      #   vendor/amphp/file/test/FileTest.php–"  ’¿Ýj–"  ¤uÓj¤      (   vendor/amphp/file/test/FileMutexTest.phpE  ’¿ÝjE  ×<'Ø¤      (   vendor/amphp/file/test/AsyncFileTest.phpA
  ’¿ÝjA
  êÀ5¤      -   vendor/amphp/file/src/FilesystemException.phpö   ’¿Ýjö   ‘iN¤      #   vendor/amphp/file/src/FileMutex.php"  ’¿Ýj"  g�‚ö¤      3   vendor/amphp/file/src/Driver/UvFilesystemDriver.phpÒ?  ’¿ÝjÒ?  –mî§¤      (   vendor/amphp/file/src/Driver/EioFile.php¦  ’¿Ýj¦  3úø­¤      -   vendor/amphp/file/src/Driver/ParallelFile.phpg  ’¿Ýjg  cï½Ì¤      9   vendor/amphp/file/src/Driver/BlockingFilesystemDriver.phpÍ(  ’¿ÝjÍ(  úðˆ ¤      '   vendor/amphp/file/src/Driver/UvFile.phpa  ’¿Ýja  G¶-¤      9   vendor/amphp/file/src/Driver/ParallelFilesystemDriver.phpÒ  ’¿ÝjÒ  îž7Ø¤      -   vendor/amphp/file/src/Driver/BlockingFile.php°  ’¿Ýj°  4•à¤      >   vendor/amphp/file/src/Driver/StatusCachingFilesystemDriver.phpG  ’¿ÝjG  ÓÙO}¤      2   vendor/amphp/file/src/Driver/StatusCachingFile.php
  ’¿Ýj
  K|çÕ¤      4   vendor/amphp/file/src/Driver/EioFilesystemDriver.phpË<  ’¿ÝjË<  E›¹q¤          vendor/amphp/file/src/Whence.php7  ’¿Ýj7  Á‡†ã¤         vendor/amphp/file/src/File.phpÒ  ’¿ÝjÒ  ¬½Ãò¤      3   vendor/amphp/file/src/Internal/QueuedWritesFile.php>  ’¿Ýj>  Â©˜8¤      (   vendor/amphp/file/src/Internal/Cache.php¡  ’¿Ýj¡  #wDŸ¤      *   vendor/amphp/file/src/Internal/EioPoll.phpá  ’¿Ýjá  µØõ¤      )   vendor/amphp/file/src/Internal/UvPoll.phpa  ’¿Ýja  LÏf¡¤      -   vendor/amphp/file/src/Internal/FileWorker.phpF  ’¿ÝjF  J6ç¹¤      +   vendor/amphp/file/src/Internal/FileTask.php#  ’¿Ýj#  ?¹î¤      #   vendor/amphp/file/src/FileCache.php  ’¿Ýj  ÀkJ¤      /   vendor/amphp/file/src/PendingOperationError.phpX  ’¿ÝjX  ´|ü¤      *   vendor/amphp/file/src/FilesystemDriver.phpË  ’¿ÝjË  7.†=¤      #   vendor/amphp/file/src/functions.php¹  ’¿Ýj¹  jtÎ/¤      (   vendor/amphp/file/src/KeyedFileMutex.phpò  ’¿Ýjò  ¸
‘¤      $   vendor/amphp/file/src/Filesystem.phpñ"  ’¿Ýjñ"  Ñ—«	¤         vendor/amphp/file/composer.jsonÇ  ’¿ÝjÇ  éV¡Õ¤      %   vendor/amphp/hpack/examples/bench.php8  ’¿Ýj8  >¾qt¤      )   vendor/amphp/hpack/.php-cs-fixer.dist.phpß   ’¿Ýjß   +Ød¤      .   vendor/amphp/hpack/tools/php-fuzzer/decode.phpÁ   ’¿ÝjÁ   É2j¤      5   vendor/amphp/hpack/tools/php-fuzzer/decode-oracle.phpû  ’¿Ýjû  ¦³s;¤      4   vendor/amphp/hpack/tools/php-fuzzer/decode-crash.php†   ’¿Ýj†   $rS¤      %   vendor/amphp/hpack/tools/compress.php  ’¿Ýj  MIù¤          vendor/amphp/hpack/src/HPack.php‹  ’¿Ýj‹  -m‡¤      )   vendor/amphp/hpack/src/HPackException.phpg   ’¿Ýjg   #†YÄ¤      1   vendor/amphp/hpack/src/Internal/huffman-codes.phpZö  ’¿ÝjZö  Õõm'¤      2   vendor/amphp/hpack/src/Internal/huffman-lookup.php! ’¿Ýj! D±¤      0   vendor/amphp/hpack/src/Internal/HPackNghttp2.phpJ  ’¿ÝjJ  ÿâ¤      /   vendor/amphp/hpack/src/Internal/HPackNative.phpÊ\  ’¿ÝjÊ\  ×yå¤      +   vendor/amphp/hpack/src/Internal/amp-hpack.hŸ  ’¿ÝjŸ  ÆF=a¤          vendor/amphp/hpack/composer.jsonõ  ’¿Ýjõ  y”š¶¤      )   vendor/amphp/mysql/examples/1-connect.phpÙ  ’¿ÝjÙ  ™wë¤      -   vendor/amphp/mysql/examples/6-transaction.phpB  ’¿ÝjB  ÜÄ&¤      1   vendor/amphp/mysql/examples/support/bootstrap.php�  ’¿Ýj�  BóÙ¤      5   vendor/amphp/mysql/examples/support/generic-table.php,  ’¿Ýj,  ×k¤      ,   vendor/amphp/mysql/examples/4-multi-rows.phpÊ  ’¿ÝjÊ  hSîk¤      4   vendor/amphp/mysql/examples/3-generic-with-yield.phpù  ’¿Ýjù  Ç7%d¤      -   vendor/amphp/mysql/examples/5-multi-stmts.php  ’¿Ýj  Š¶°á¤      .   vendor/amphp/mysql/examples/2-simple-query.php?  ’¿Ýj?  G@€[¤      )   vendor/amphp/mysql/.php-cs-fixer.dist.php½   ’¿Ýj½   �.Á8¤          vendor/amphp/mysql/phpbench.json“   ’¿Ýj“   Å£Œ¤      ,   vendor/amphp/mysql/benchmarks/QueryBench.php—
  ’¿Ýj—
  Y¢8¤      /   vendor/amphp/mysql/benchmarks/AbstractBench.phpZ  ’¿ÝjZ  và_Á¤      2   vendor/amphp/mysql/.github/workflows/bootstrap.php©   ’¿Ýj©   o„¨¤      *   vendor/amphp/mysql/src/MysqlConnection.php�  ’¿Ýj�  sF´M¤      0   vendor/amphp/mysql/src/SocketMysqlConnection.php%  ’¿Ýj%  Eõá™¤      (   vendor/amphp/mysql/src/MysqlDataType.php¿:  ’¿Ýj¿:  êtv¤      )   vendor/amphp/mysql/src/MysqlStatement.phpF  ’¿ÝjF  ¼J4¤      +   vendor/amphp/mysql/src/MysqlTransaction.phpæ   ’¿Ýjæ   ÍSQ¤      .   vendor/amphp/mysql/src/MysqlConnectionPool.php�  ’¿Ýj�  ”´±ç¤      /   vendor/amphp/mysql/src/SocketMysqlConnector.php§  ’¿Ýj§  ·.­˜¤      (   vendor/amphp/mysql/src/MysqlExecutor.php«  ’¿Ýj«  7…Ô¤      0   vendor/amphp/mysql/src/MysqlColumnDefinition.phpq
  ’¿Ýjq
  QOÝ¤      &   vendor/amphp/mysql/src/MysqlResult.phpÛ  ’¿ÝjÛ  ïîZÐ¤      <   vendor/amphp/mysql/src/Internal/MysqlConnectionStatement.phpÑ  ’¿ÝjÑ  §êÑ¤      8   vendor/amphp/mysql/src/Internal/MysqlPooledStatement.php|  ’¿Ýj|  ÐÇZã¤      9   vendor/amphp/mysql/src/Internal/MysqlNestableExecutor.php“	  ’¿Ýj“	  ñÁòð¤      9   vendor/amphp/mysql/src/Internal/MysqlResultProxyState.php•   ’¿Ýj•   ÜõYð¤      6   vendor/amphp/mysql/src/Internal/MysqlCommandResult.phpo  ’¿Ýjo  h{Ô«¤      2   vendor/amphp/mysql/src/Internal/PublicKeyCache.php�  ’¿Ýj�  u¸È¤      <   vendor/amphp/mysql/src/Internal/MysqlTransactionDelegate.phpþ  ’¿Ýjþ  ¤ßŽ¤      :   vendor/amphp/mysql/src/Internal/MysqlNestedTransaction.php®  ’¿Ýj®  Ó<^¤      4   vendor/amphp/mysql/src/Internal/SessionStateType.php^  ’¿Ýj^  Æµî¤      5   vendor/amphp/mysql/src/Internal/MysqlPooledResult.php¹  ’¿Ýj¹  vAsw¤      B   vendor/amphp/mysql/src/Internal/MysqlStatementCreationDelegate.phpü  ’¿Ýjü  _À"ø¤      >   vendor/amphp/mysql/src/Internal/MysqlConnectionTransaction.phpÍ  ’¿ÝjÍ  ÔÙÖÖ¤      :   vendor/amphp/mysql/src/Internal/MysqlPooledTransaction.php¿  ’¿Ýj¿  UgÑ¤      ;   vendor/amphp/mysql/src/Internal/MysqlConnectionMetadata.php  ’¿Ýj  y.e¤      9   vendor/amphp/mysql/src/Internal/MysqlConnectionResult.php   ’¿Ýj   éd£M¤      5   vendor/amphp/mysql/src/Internal/MysqlEncodedValue.phpŽ
  ’¿ÝjŽ
  #zB+¤      3   vendor/amphp/mysql/src/Internal/ConnectionState.phpÝ   ’¿ÝjÝ   ¹|Ñÿ¤      7   vendor/amphp/mysql/src/Internal/ConnectionProcessor.php±á  ’¿Ýj±á  K³ê¤      6   vendor/amphp/mysql/src/Internal/MysqlStatementPool.phpV	  ’¿ÝjV	  !´Â•¤      4   vendor/amphp/mysql/src/Internal/MysqlResultProxy.php  ’¿Ýj  T°ÿ�¤      $   vendor/amphp/mysql/src/MysqlLink.phpv  ’¿Ýjv  l¨?ì¤      &   vendor/amphp/mysql/src/MysqlConfig.php}  ’¿Ýj}  ÀbYF¤      $   vendor/amphp/mysql/src/functions.phpS  ’¿ÝjS  ÿÄ?.¤          vendor/amphp/mysql/composer.json  ’¿Ýj  À¥±Y¤      (   vendor/amphp/sync/.php-cs-fixer.dist.phpÝ   ’¿ÝjÝ   èXF3¤      -   vendor/amphp/sync/composer-require-check.json¤  ’¿Ýj¤  îÝ˜¤      (   vendor/amphp/sync/src/StaticKeyMutex.php³  ’¿Ýj³  ŽÎÅ€¤          vendor/amphp/sync/src/Parcel.phpÐ  ’¿ÝjÐ  �Iß�¤      ,   vendor/amphp/sync/src/SharedMemoryParcel.php¡7  ’¿Ýj¡7  Â�2Ü¤      (   vendor/amphp/sync/src/SemaphoreMutex.php;  ’¿Ýj;  h©¬!¤      #   vendor/amphp/sync/src/Semaphore.phpƒ  ’¿Ýjƒ  ÛåNG¤      !   vendor/amphp/sync/src/Channel.php°  ’¿Ýj°  šRˆ?¤      $   vendor/amphp/sync/src/KeyedMutex.phpã  ’¿Ýjã  Ì�]¤      /   vendor/amphp/sync/src/RateLimitingSemaphore.php­  ’¿Ýj­  ‰Vx¥¤      ,   vendor/amphp/sync/src/StaticKeySemaphore.phpÃ  ’¿ÝjÃ  Ãg'¤         vendor/amphp/sync/src/Mutex.phpÍ  ’¿ÝjÍ  ŸfsŽ¤      '   vendor/amphp/sync/src/PriorityQueue.phpÄ  ’¿ÝjÄ  jq/�¤      (   vendor/amphp/sync/src/KeyedSemaphore.php0  ’¿Ýj0  ¥ˆè¤      (   vendor/amphp/sync/src/PosixSemaphore.phpM  ’¿ÝjM  *\ÐN¤         vendor/amphp/sync/src/Lock.php2  ’¿Ýj2  —ZQé¤      )   vendor/amphp/sync/src/LocalKeyedMutex.phpÜ  ’¿ÝjÜ  ¹¹Ž-¤      %   vendor/amphp/sync/src/LocalParcel.phpü  ’¿Ýjü  •C0¤      <   vendor/amphp/sync/src/Internal/ConcurrentIteratorChannel.phpl	  ’¿Ýjl	  àÊÆþ¤      0   vendor/amphp/sync/src/PrefixedKeyedSemaphore.phpç  ’¿Ýjç  YmsÓ¤      $   vendor/amphp/sync/src/LocalMutex.php¹  ’¿Ýj¹  âZÔ¤      *   vendor/amphp/sync/src/ChannelException.phpc   ’¿Ýjc   É{éx¤      !   vendor/amphp/sync/src/Barrier.phpr	  ’¿Ýjr	  éÍ&ß¤      -   vendor/amphp/sync/src/LocalKeyedSemaphore.phpÂ  ’¿ÝjÂ  Ór¦¤      )   vendor/amphp/sync/src/ParcelException.phpb   ’¿Ýjb   ð}¤      ,   vendor/amphp/sync/src/PrefixedKeyedMutex.phpÓ  ’¿ÝjÓ  92;„¤      (   vendor/amphp/sync/src/LocalSemaphore.phpf  ’¿Ýjf  Ôw°¤      #   vendor/amphp/sync/src/functions.php�  ’¿Ýj�  ÖÆº¤      '   vendor/amphp/sync/src/SyncException.php`   ’¿Ýj`   W8XM¤         vendor/amphp/sync/composer.jsonß  ’¿Ýjß  vâ�?í      /   vendor/amphp/byte-stream/.php-cs-fixer.dist.phpß   ’¿Ýjß   +Ød¤      4   vendor/amphp/byte-stream/composer-require-check.jsonÑ  ’¿ÝjÑ  ·'Ì¤      @   vendor/amphp/byte-stream/src/ReadableStreamIteratorAggregate.phpˆ  ’¿Ýjˆ  ¥[Z¤      7   vendor/amphp/byte-stream/src/WritableResourceStream.php<0  ’¿Ýj<0  xe”@¤      7   vendor/amphp/byte-stream/src/ReadableResourceStream.phpË&  ’¿ÝjË&  ´7'„¤      /   vendor/amphp/byte-stream/src/ReadableBuffer.phpð  ’¿Ýjð  âË;˜¤      7   vendor/amphp/byte-stream/src/ReadableIterableStream.php£  ’¿Ýj£  ˆkÁ»¤      %   vendor/amphp/byte-stream/src/Pipe.phpD  ’¿ÝjD  IÆK¬¤      0   vendor/amphp/byte-stream/src/ClosedException.phps   ’¿Ýjs   à÷n¤      (   vendor/amphp/byte-stream/src/Payload.php™  ’¿Ýj™  _%tR¤      .   vendor/amphp/byte-stream/src/StreamChannel.phpã  ’¿Ýjã  ‹ƒF%¤      /   vendor/amphp/byte-stream/src/ResourceStream.php¥  ’¿Ýj¥  ñ½(ü¤      D   vendor/amphp/byte-stream/src/Base64/Base64DecodingReadableStream.php˜  ’¿Ýj˜  úpÿµ¤      D   vendor/amphp/byte-stream/src/Base64/Base64EncodingReadableStream.phpF  ’¿ÝjF  ¿ôÜ7¤      D   vendor/amphp/byte-stream/src/Base64/Base64DecodingWritableStream.phpð  ’¿Ýjð  €(®£¤      D   vendor/amphp/byte-stream/src/Base64/Base64EncodingWritableStream.php>  ’¿Ýj>  ¾;ê¤      /   vendor/amphp/byte-stream/src/ReadableStream.phpò  ’¿Ýjò  %(ƒö¤      0   vendor/amphp/byte-stream/src/BufferException.phpO  ’¿ÝjO  J­‹q¤      4   vendor/amphp/byte-stream/src/ReadableStreamChain.phpÔ  ’¿ÝjÔ  ±öÜ„¤      /   vendor/amphp/byte-stream/src/BufferedReader.php!  ’¿Ýj!  ˆx+¤      /   vendor/amphp/byte-stream/src/WritableStream.php³  ’¿Ýj³   XHŠ¤      ,   vendor/amphp/byte-stream/src/AsyncWriter.php¡  ’¿Ýj¡  éu	¢¤      H   vendor/amphp/byte-stream/src/Compression/DecompressingWritableStream.php|  ’¿Ýj|  oEi¤      H   vendor/amphp/byte-stream/src/Compression/DecompressingReadableStream.php7  ’¿Ýj7  áH„æ¤      F   vendor/amphp/byte-stream/src/Compression/CompressingWritableStream.php˜  ’¿Ýj˜  ˆsÐ²¤      F   vendor/amphp/byte-stream/src/Compression/CompressingReadableStream.php“  ’¿Ýj“  3™-¤      7   vendor/amphp/byte-stream/src/Internal/ChannelParser.phpã  ’¿Ýjã  x`·é¤      3   vendor/amphp/byte-stream/src/Internal/functions.phpÐ  ’¿ÝjÐ  d{d¤      7   vendor/amphp/byte-stream/src/WritableIterableStream.php€  ’¿Ýj€  ôñ±¤      1   vendor/amphp/byte-stream/src/PendingReadError.phpä  ’¿Ýjä  §—áÒ¤      /   vendor/amphp/byte-stream/src/WritableBuffer.php×  ’¿Ýj×  Ä@æd¤      0   vendor/amphp/byte-stream/src/StreamException.phph   ’¿Ýjh   4‡‚¤      *   vendor/amphp/byte-stream/src/functions.php,  ’¿Ýj,  q×ë@¤      &   vendor/amphp/byte-stream/composer.jsonD  ’¿ÝjD  º“¤      1   vendor/amphp/serialization/.php-cs-fixer.dist.php½   ’¿Ýj½   �.Á8¤      1   vendor/amphp/serialization/src/JsonSerializer.phpÜ	  ’¿ÝjÜ	  Q^yþ¤      -   vendor/amphp/serialization/src/Serializer.php�  ’¿Ýj�  $7ß�¤      9   vendor/amphp/serialization/src/SerializationException.phpž   ’¿Ýjž   à#ïô¤      8   vendor/amphp/serialization/src/PassthroughSerializer.phpÐ  ’¿ÝjÐ  ƒq¤      3   vendor/amphp/serialization/src/NativeSerializer.php¶  ’¿Ýj¶  •Yz´¤      8   vendor/amphp/serialization/src/CompressingSerializer.php  ’¿Ýj  Dáš¤      ,   vendor/amphp/serialization/src/functions.phpÈ  ’¿ÝjÈ  ­ŽáV¤      (   vendor/amphp/serialization/composer.jsonþ  ’¿Ýjþ  ½†Ó]¤      .   vendor/amphp/amp/src/CompositeCancellation.php¥  ’¿Ýj¥  ®Tžß¤      ,   vendor/amphp/amp/src/ForbidSerialization.phpÇ  ’¿ÝjÇ  ßÿGd¤      -   vendor/amphp/amp/src/DeferredCancellation.php~  ’¿Ýj~  CÄ®?¤         vendor/amphp/amp/src/Future.php6  ’¿Ýj6  (oÍ‹¤      !   vendor/amphp/amp/src/Interval.phpË	  ’¿ÝjË	  Œ!¤      )   vendor/amphp/amp/src/NullCancellation.phpð  ’¿Ýjð  â „ú¤      +   vendor/amphp/amp/src/CancelledException.phpf  ’¿Ýjf  õÉ™¤      )   vendor/amphp/amp/src/TimeoutException.php¬  ’¿Ýj¬  ËïÅ¤      4   vendor/amphp/amp/src/Future/UnhandledFutureError.phpR  ’¿ÝjR  2¤      )   vendor/amphp/amp/src/Future/functions.phpÛ  ’¿ÝjÛ  Aîh¤      '   vendor/amphp/amp/src/DeferredFuture.phpî  ’¿Ýjî  	Ègh¤      (   vendor/amphp/amp/src/SignalException.php¸  ’¿Ýj¸  odóc¤      +   vendor/amphp/amp/src/CompositeException.php¶  ’¿Ýj¶  ¦xÕ¤      %   vendor/amphp/amp/src/Cancellation.phpY  ’¿ÝjY  ÅL²¤      +   vendor/amphp/amp/src/SignalCancellation.phpš	  ’¿Ýjš	  Ô%ð¤      -   vendor/amphp/amp/src/Internal/FutureState.phpI  ’¿ÝjI  ¿#µ¤      -   vendor/amphp/amp/src/Internal/Cancellable.php
  ’¿Ýj
  Ž�³¤      0   vendor/amphp/amp/src/Internal/FutureIterator.php‡  ’¿Ýj‡  ÕçÔ¤      5   vendor/amphp/amp/src/Internal/FutureIteratorQueue.php×  ’¿Ýj×  4¨„¤      5   vendor/amphp/amp/src/Internal/WrappedCancellation.phps  ’¿Ýjs  ð^Ê@¤      +   vendor/amphp/amp/src/Internal/functions.phpK  ’¿ÝjK  Šâ('¤      ,   vendor/amphp/amp/src/TimeoutCancellation.phpØ  ’¿ÝjØ  tQåZ¤      &   vendor/amphp/amp/src/ForbidCloning.php¾   ’¿Ýj¾   ííÙ‡¤      !   vendor/amphp/amp/src/Closable.phpl  ’¿Ýjl  â!¤      1   vendor/amphp/amp/src/CompositeLengthException.phpÐ   ’¿ÝjÐ   qT¸†¤      "   vendor/amphp/amp/src/functions.phpL  ’¿ÝjL  I“e%¤         vendor/amphp/amp/composer.json0  ’¿Ýj0  Ö@Z¤      )   vendor/amphp/cache/.php-cs-fixer.dist.phpÀ   ’¿ÝjÀ   <Æš-¤      .   vendor/amphp/cache/composer-require-check.jsonÒ  ’¿ÝjÒ  «Ý¤      &   vendor/amphp/cache/src/AtomicCache.phpù  ’¿Ýjù  Z ¾¤      $   vendor/amphp/cache/src/NullCache.phpo  ’¿Ýjo  µù}�¤      &   vendor/amphp/cache/src/StringCache.phpü  ’¿Ýjü  Þ~�¤      &   vendor/amphp/cache/src/PrefixCache.phpŸ  ’¿ÝjŸ  �ãæü¤          vendor/amphp/cache/src/Cache.php  ’¿Ýj  (=y¤      -   vendor/amphp/cache/src/StringCacheAdapter.phpt  ’¿Ýjt  FþJs¤      %   vendor/amphp/cache/src/LocalCache.php  ’¿Ýj  àž¹•¤      *   vendor/amphp/cache/src/SerializedCache.phpÒ  ’¿ÝjÒ  ØÐÐ¤      )   vendor/amphp/cache/src/CacheException.php�   ’¿Ýj�   ==Œß¤          vendor/amphp/cache/composer.jsonQ  ’¿ÝjQ  Ôìø¤      *   vendor/amphp/postgres/examples/5-bytea.phpÐ  ’¿ÝjÐ  {c¯�¤      1   vendor/amphp/postgres/examples/4-multi-listen.phpÕ  ’¿ÝjÕ  ´L)¤      +   vendor/amphp/postgres/examples/3-listen.phpÛ  ’¿ÝjÛ  �R�%¤      0   vendor/amphp/postgres/examples/2-transaction.php¾  ’¿Ýj¾  ‹–¹ø¤      *   vendor/amphp/postgres/examples/1-basic.php’  ’¿Ýj’  ÍR&;¤      ,   vendor/amphp/postgres/.php-cs-fixer.dist.phpÝ   ’¿ÝjÝ   èXF3¤      *   vendor/amphp/postgres/src/PqConnection.phpÁ  ’¿ÝjÁ  õ“I¤      ,   vendor/amphp/postgres/src/PostgresResult.phpö  ’¿Ýjö  ¹óg»¤      +   vendor/amphp/postgres/src/PostgresArray.phpH  ’¿ÝjH  ÜÎ¤      4   vendor/amphp/postgres/src/PostgresConnectionPool.phpû  ’¿Ýjû  
 ^n¤      .   vendor/amphp/postgres/src/PostgresExecutor.php  ’¿Ýj  wi¦^¤      0   vendor/amphp/postgres/src/PostgresConnection.phpŽ  ’¿ÝjŽ  +€«”¤      4   vendor/amphp/postgres/src/PostgresParseException.php^  ’¿Ýj^  J\JŸ¤      0   vendor/amphp/postgres/src/PostgresQueryError.phpd  ’¿Ýjd  ï7Ç¤      ,   vendor/amphp/postgres/src/PostgresConfig.phpÊ  ’¿ÝjÊ  8 '¤      6   vendor/amphp/postgres/src/DefaultPostgresConnector.php“  ’¿Ýj“  ~Þ¨¤      A   vendor/amphp/postgres/src/Internal/PostgresConnectionListener.php»  ’¿Ýj»  Á²¤      5   vendor/amphp/postgres/src/Internal/AbstractHandle.php;  ’¿Ýj;  Öóõ9¤      5   vendor/amphp/postgres/src/Internal/PgSqlResultSet.php   ’¿Ýj   %Éœ!¤      :   vendor/amphp/postgres/src/Internal/PgSqlResultIterator.phpH  ’¿ÝjH  E[e¤      ;   vendor/amphp/postgres/src/Internal/PostgresPooledResult.phpÊ  ’¿ÝjÊ  ¼-vˆ¤      @   vendor/amphp/postgres/src/Internal/PostgresNestedTransaction.php«  ’¿Ýj«  §w…¤      0   vendor/amphp/postgres/src/Internal/PgSqlType.phpè  ’¿Ýjè  Ãët¤      @   vendor/amphp/postgres/src/Internal/PostgresPooledTransaction.phpv  ’¿Ýjv  –<)¤      2   vendor/amphp/postgres/src/Internal/PgSqlHandle.phpN  ’¿ÝjN  x�+Î¤      >   vendor/amphp/postgres/src/Internal/PostgresPooledStatement.php5  ’¿Ýj5  ìU{Å¤      <   vendor/amphp/postgres/src/Internal/PostgresCommandResult.php)  ’¿Ýj)  '/u·¤      <   vendor/amphp/postgres/src/Internal/PostgresStatementPool.phpp  ’¿Ýjp  x,çZ¤      ?   vendor/amphp/postgres/src/Internal/PostgresHandleConnection.php¢  ’¿Ýj¢  —nÐ¤      <   vendor/amphp/postgres/src/Internal/PqUnbufferedResultSet.phpn
  ’¿Ýjn
  |E‘¤      B   vendor/amphp/postgres/src/Internal/PostgresTransactionDelegate.php\	  ’¿Ýj\	  ß²üj¤      2   vendor/amphp/postgres/src/Internal/ArrayParser.phpO  ’¿ÝjO  I€ëF¤      7   vendor/amphp/postgres/src/Internal/StatementStorage.phpÓ  ’¿ÝjÓ  ¾E»¤      5   vendor/amphp/postgres/src/Internal/PostgresHandle.php<  ’¿Ýj<  šþhï¤      D   vendor/amphp/postgres/src/Internal/PostgresConnectionTransaction.php]  ’¿Ýj]  žüº9¤      =   vendor/amphp/postgres/src/Internal/PostgresPooledListener.php_  ’¿Ýj_  ê:R!¤      B   vendor/amphp/postgres/src/Internal/PostgresConnectionStatement.php"  ’¿Ýj"  ½¼“¤      0   vendor/amphp/postgres/src/Internal/functions.php—  ’¿Ýj—  åïä4¤      /   vendor/amphp/postgres/src/Internal/PqHandle.phpxA  ’¿ÝjxA  À¿X–¤      :   vendor/amphp/postgres/src/Internal/PqBufferedResultSet.php–  ’¿Ýj–  LMQª¤      1   vendor/amphp/postgres/src/PostgresTransaction.phpl  ’¿Ýjl  Sš¤      *   vendor/amphp/postgres/src/PostgresLink.phpŽ  ’¿ÝjŽ  Ð÷r¤      2   vendor/amphp/postgres/src/PostgresNotification.php­  ’¿Ýj­  |Ø?Ã¤      '   vendor/amphp/postgres/src/functions.php¬  ’¿Ýj¬  ¯ ‹¤      +   vendor/amphp/postgres/src/PostgresByteA.phpü   ’¿Ýjü   »Â€ý¤      /   vendor/amphp/postgres/src/PostgresStatement.phpº   ’¿Ýjº   ` û¤      .   vendor/amphp/postgres/src/PostgresListener.phpÌ  ’¿ÝjÌ  ‚–õ“¤      -   vendor/amphp/postgres/src/PgSqlConnection.phpŸ  ’¿ÝjŸ   M¶¤      #   vendor/amphp/postgres/composer.jsonˆ  ’¿Ýjˆ  Ê6Ï¤      '   vendor/amphp/sql/.php-cs-fixer.dist.php½   ’¿Ýj½   �.Á8¤      +   vendor/amphp/sql/test/SqlQueryErrorTest.phpú  ’¿Ýjú  ¥¾¼¤      '   vendor/amphp/sql/test/SqlConfigTest.phpÞ  ’¿ÝjÞ  âtä¤          vendor/amphp/sql/src/SqlLink.phpè  ’¿Ýjè  KÝ'0¤      /   vendor/amphp/sql/src/SqlConnectionException.phpj   ’¿Ýjj   Ax’º¤      0   vendor/amphp/sql/src/SqlTransactionIsolation.phpe  ’¿Ýje  çJÍà¤      '   vendor/amphp/sql/src/SqlTransaction.php$  ’¿Ýj$  ›:°º¤      5   vendor/amphp/sql/src/SqlTransactionIsolationLevel.php5  ’¿Ýj5  ¤:\‘¤      &   vendor/amphp/sql/src/SqlQueryError.phpÉ  ’¿ÝjÉ  •Ä3ò¤      $   vendor/amphp/sql/src/SqlExecutor.php$  ’¿Ýj$  ùDèQ¤      ,   vendor/amphp/sql/src/SqlTransactionError.phpa   ’¿Ýja   RäÆó¤      -   vendor/amphp/sql/src/SqlTransientResource.php'  ’¿Ýj'  7J¤      %   vendor/amphp/sql/src/SqlStatement.phpv  ’¿Ýjv  Mv2*¤      "   vendor/amphp/sql/src/SqlConfig.php  ’¿Ýj  ‘`_L¤      &   vendor/amphp/sql/src/SqlConnection.phpƒ  ’¿Ýjƒ  PÀ¡¤      %   vendor/amphp/sql/src/SqlException.php^   ’¿Ýj^   |ÂtÜ¤      %   vendor/amphp/sql/src/SqlConnector.php;  ’¿Ýj;  Væ\¦¤      *   vendor/amphp/sql/src/SqlConnectionPool.phpÎ  ’¿ÝjÎ  }ûë¤      "   vendor/amphp/sql/src/SqlResult.php£  ’¿Ýj£  4˜ù«¤         vendor/amphp/sql/composer.jsonu  ’¿Ýju  ï"¢.¤      8   vendor/webmozart/assert/src/InvalidArgumentException.phpt  ’¿Ýjt  @’¾>¤      +   vendor/webmozart/assert/src/PsalmPlugin.phpÿ  ’¿Ýjÿ  �0Àÿ¤      &   vendor/webmozart/assert/src/Assert.php‚" ’¿Ýj‚" wØâ¤      )   vendor/webmozart/assert/src/HasAssert.phpû  ’¿Ýjû   ¿H¤      %   vendor/webmozart/assert/src/Mixin.php€2 ’¿Ýj€2 HÏ—¤      %   vendor/webmozart/assert/composer.jsonƒ  ’¿Ýjƒ  À|º¤      %   vendor/dasprid/enum/src/NullValue.php  ’¿Ýj  š¦œ¤      8   vendor/dasprid/enum/src/Exception/ExceptionInterface.phpˆ   ’¿Ýjˆ   |ù6¤      D   vendor/dasprid/enum/src/Exception/SerializeNotSupportedException.php´   ’¿Ýj´   ·ó†Ê¤      @   vendor/dasprid/enum/src/Exception/CloneNotSupportedException.php°   ’¿Ýj°   ÐÓÅÞ¤      F   vendor/dasprid/enum/src/Exception/UnserializeNotSupportedException.php¶   ’¿Ýj¶   ˜LÒA¤      :   vendor/dasprid/enum/src/Exception/ExpectationException.phpª   ’¿Ýjª   1úÇ¤      7   vendor/dasprid/enum/src/Exception/MismatchException.php§   ’¿Ýj§   NÍ�{¤      >   vendor/dasprid/enum/src/Exception/IllegalArgumentException.php®   ’¿Ýj®   ÜmúY¤      (   vendor/dasprid/enum/src/AbstractEnum.phpÃ  ’¿ÝjÃ  “à.¤      #   vendor/dasprid/enum/src/EnumMap.phpŸ,  ’¿ÝjŸ,   d±Í¤      !   vendor/dasprid/enum/composer.jsonï  ’¿Ýjï  3¸Ó‚¤      /   vendor/psr/log/src/InvalidArgumentException.php`   ’¿Ýj`    ˆX1¤      !   vendor/psr/log/src/NullLogger.phpƒ  ’¿Ýjƒ  (é/¤         vendor/psr/log/src/LogLevel.phpP  ’¿ÝjP  �òº¤      +   vendor/psr/log/src/LoggerAwareInterface.phpç   ’¿Ýjç    {ý¤      "   vendor/psr/log/src/LoggerTrait.phpÃ
  ’¿ÝjÃ
  WéP¤      '   vendor/psr/log/src/LoggerAwareTrait.php[  ’¿Ýj[  •ØÛ¤      &   vendor/psr/log/src/LoggerInterface.phpÒ
  ’¿ÝjÒ
  q¢•¤      %   vendor/psr/log/src/AbstractLogger.phpž  ’¿Ýjž  Û›©ñ¤         vendor/psr/log/composer.json+  ’¿Ýj+  …²¼«¤      <   vendor/psr/http-factory/src/UploadedFileFactoryInterface.phph  ’¿Ýjh  Bjã¬¤      6   vendor/psr/http-factory/src/StreamFactoryInterface.phpŠ  ’¿ÝjŠ  ˜yÛœ¤      3   vendor/psr/http-factory/src/UriFactoryInterface.phpE  ’¿ÝjE  «šDh¤      8   vendor/psr/http-factory/src/ResponseFactoryInterface.php"  ’¿Ýj"  X¨ï¤      7   vendor/psr/http-factory/src/RequestFactoryInterface.phpó  ’¿Ýjó  rTÔX¤      =   vendor/psr/http-factory/src/ServerRequestFactoryInterface.phpŸ  ’¿ÝjŸ  BH²A¤      %   vendor/psr/http-factory/composer.json  ’¿Ýj  èåO¤      1   vendor/psr/http-message/src/ResponseInterface.phpJ
  ’¿ÝjJ
  bY6¤      /   vendor/psr/http-message/src/StreamInterface.phpü  ’¿Ýjü  Ð¶Jý¤      0   vendor/psr/http-message/src/MessageInterface.php¾  ’¿Ýj¾  ?î ¤      ,   vendor/psr/http-message/src/UriInterface.php2  ’¿Ýj2  o³•¤      6   vendor/psr/http-message/src/ServerRequestInterface.php:(  ’¿Ýj:(  iP:^¤      0   vendor/psr/http-message/src/RequestInterface.php7  ’¿Ýj7  �_8ý¤      5   vendor/psr/http-message/src/UploadedFileInterface.phpŒ  ’¿ÝjŒ  ZþÝ¤      %   vendor/psr/http-message/composer.jsons  ’¿Ýjs  ×Lo$¤      (   vendor/phpseclib/phpseclib/composer.jsong  ’¿Ýjg  î÷ö¤      2   vendor/phpseclib/phpseclib/phpseclib/bootstrap.phpÿ  ’¿Ýjÿ  ­c(S¤      D   vendor/phpseclib/phpseclib/phpseclib/Crypt/DH/Formats/Keys/PKCS8.php  ’¿Ýj  |„×ˆ¤      D   vendor/phpseclib/phpseclib/phpseclib/Crypt/DH/Formats/Keys/PKCS1.php,  ’¿Ýj,  CŸ¤      ;   vendor/phpseclib/phpseclib/phpseclib/Crypt/DH/PublicKey.phpÁ  ’¿ÝjÁ  ŠÃ›
¤      <   vendor/phpseclib/phpseclib/phpseclib/Crypt/DH/Parameters.phpÕ  ’¿ÝjÕ  ”
ìm¤      <   vendor/phpseclib/phpseclib/phpseclib/Crypt/DH/PrivateKey.php‡  ’¿Ýj‡  Røß_¤      2   vendor/phpseclib/phpseclib/phpseclib/Crypt/RC4.phpå  ’¿Ýjå  –öÞ¶¤      6   vendor/phpseclib/phpseclib/phpseclib/Crypt/Salsa20.phpg6  ’¿Ýjg6  òd¶¤      >   vendor/phpseclib/phpseclib/phpseclib/Crypt/PublicKeyLoader.phpß  ’¿Ýjß  BKþá¤      2   vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA.phpß'  ’¿Ýjß'  ^W”¤      8   vendor/phpseclib/phpseclib/phpseclib/Crypt/TripleDES.phpè/  ’¿Ýjè/  ºÝ´®¤      6   vendor/phpseclib/phpseclib/phpseclib/Crypt/Twofish.php/�  ’¿Ýj/�  Ä¦Õ8¤      7   vendor/phpseclib/phpseclib/phpseclib/Crypt/ChaCha20.phpÑ»  ’¿ÝjÑ»  i!»¤      3   vendor/phpseclib/phpseclib/phpseclib/Crypt/Hash.phpò½  ’¿Ýjò½  �Î‘´¤      1   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC.phpC  ’¿ÝjC  ¿Ë”¤      E   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/Common.phpôJ  ’¿ÝjôJ  ^q¶Ó¤      O   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/MontgomeryPublic.php9  ’¿Ýj9  ôèÅ£¤      D   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/PKCS8.php8*  ’¿Ýj8*  Ux$	¤      F   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/OpenSSH.php—  ’¿Ýj—  âî¤°¤      B   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/XML.php¦C  ’¿Ýj¦C  ¡1C¼¤      P   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/MontgomeryPrivate.php&  ’¿Ýj&  ¸¢·ž¤      D   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/PKCS1.php¤  ’¿Ýj¤  BŒ„}¤      H   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/libsodium.phpÍ  ’¿ÝjÍ  ¦eè@¤      D   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/PuTTY.php/  ’¿Ýj/  Ó^rx¤      B   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/JWK.php	  ’¿Ýj	  u½«¾¤      H   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/IEEE.php-  ’¿Ýj-  é…¤      G   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/Raw.php.  ’¿Ýj.  TáXÜ¤      H   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/ASN1.php¯  ’¿Ýj¯  …ü#&¤      H   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/SSH2.phpÑ  ’¿ÝjÑ  ÷”Ïi¤      ;   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/PublicKey.phpŽ%  ’¿ÝjŽ%  €ö÷¤      C   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime192v3.phpV  ’¿ÝjV  �ÑN˜¤      H   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP224t1.phpš  ’¿Ýjš  §˜È¤      A   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistb233.php  ’¿Ýj  Û×Ã´¤      B   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp256r1.php´  ’¿Ýj´  ·0Û—¤      B   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect113r2.phpo  ’¿Ýjo  oÔ”¤      C   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime192v1.phpƒ  ’¿Ýjƒ  tjìy¤      H   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP320t1.php  ’¿Ýj  X"•Ê¤      C   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime192v2.phpV  ’¿ÝjV  ç©8`¤      A   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk283.php€  ’¿Ýj€  •õ‡F¤      B   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp256k1.php\  ’¿Ýj\  ºG›¤      H   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP320r1.phpù  ’¿Ýjù  ÷;3¤      B   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect571r1.php)  ’¿Ýj)  Z�›ñ¤      B   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp192r1.php±  ’¿Ýj±  ï‡ƒ~¤      A   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk163.php  ’¿Ýj  É¤      B   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect131r2.php‰  ’¿Ýj‰  ä†ä”¤      A   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp192.php  ’¿Ýj  õû„�¤      A   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistb409.php  ’¿Ýj  ^³“Æ¤      B   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect283k1.phpJ  ’¿ÝjJ  û¯0¤      H   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP256t1.phpÊ  ’¿ÝjÊ  HÅ<Ó¤      C   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime239v3.phpž  ’¿Ýjž  •¨ÓÁ¤      H   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP512r1.php²  ’¿Ýj²  f8Õ¿¤      >   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Ed448.php²  ’¿Ýj²  öÜÑì¤      B   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp112r2.php   ’¿Ýj   –Xæ!¤      B   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp160k1.php2  ’¿Ýj2  %f|¤      A   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk233.php  ’¿Ýj  ®B+¤      H   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP160t1.php  ’¿Ýj  ¯££¤      C   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime239v1.phpž  ’¿Ýjž  }l)¤      B   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp160r2.phpC  ’¿ÝjC  Šð×™¤      H   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP512t1.php¼  ’¿Ýj¼  ½”:¤      H   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP384r1.phpñ  ’¿Ýjñ  5~¤      A   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp256.php  ’¿Ýj  ¾Hk_¤      H   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP192r1.php`  ’¿Ýj`  œæ–)¤      B   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect283r1.phpJ  ’¿ÝjJ  1ÿú¤      B   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect571k1.php)  ’¿Ýj)  sß�£¤      A   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistt571.php  ’¿Ýj  ÍÈwE¤      B   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp112r1.phpÜ  ’¿ÝjÜ  e’ñ¡¤      B   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp224r1.php„  ’¿Ýj„  VI¥Õ¤      C   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Curve25519.php�	  ’¿Ýj�	  ®E¹h¤      B   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect239k1.php	  ’¿Ýj	  
‘¤      B   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect193r2.phpÔ  ’¿ÝjÔ  Ì
¤      C   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime239v2.phpž  ’¿Ýjž  ¶i¸¤      A   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp384.php  ’¿Ýj  vØ´¾¤      B   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect113r1.phpo  ’¿Ýjo  t… p¤      B   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp128r2.php  ’¿Ýj   îƒ[¤      B   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect233r1.php  ’¿Ýj  _QÁý¤      A   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp224.php  ’¿Ýj  Ç™l˜¤      B   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp160r1.php&  ’¿Ýj&  šÈu¤      B   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp384r1.phpp  ’¿Ýjp  ÆDLy¤      @   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Ed25519.phpù'  ’¿Ýjù'  ¹/mß¤      B   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect163k1.php±  ’¿Ýj±  #-ãB¤      C   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime256v1.phpƒ  ’¿Ýjƒ  ˜~|¤      B   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect409k1.php  ’¿Ýj  Ò©=D¤      B   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect409r1.php  ’¿Ýj  ÃÆ{±¤      A   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp521.php  ’¿Ýj  e/Ø¤      H   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP160r1.php0  ’¿Ýj0  nãð‹¤      B   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp521r1.phpþ  ’¿Ýjþ  ùmÁ4¤      H   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP384t1.phpü  ’¿Ýjü  /f‘4¤      H   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP256r1.phpÀ  ’¿ÝjÀ  mŠ´;¤      B   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect193r1.phpÔ  ’¿ÝjÔ  E+¤      B   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect163r1.php±  ’¿Ýj±  ´½\¤      B   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect131r1.php‰  ’¿Ýj‰  djD§¤      B   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect233k1.php  ’¿Ýj  H¥MN¤      B   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect163r2.php±  ’¿Ýj±  «lD¤      H   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP224r1.php�  ’¿Ýj�  %£¼¤      B   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp128r1.phpô  ’¿Ýjô  ž¹›ÿ¤      B   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp192k1.phpY  ’¿ÝjY  àî¬¸¤      H   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP192t1.phpj  ’¿Ýjj  ³ÆÇ~¤      B   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp224k1.php£  ’¿Ýj£  D=¢¤      A   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Curve448.php½
  ’¿Ýj½
  qa ¤      A   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk409.php  ’¿Ýj  �ÊY¤      A   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Base.php  ’¿Ýj  Œép¤      I   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/KoblitzPrime.php�&  ’¿Ýj�&  Š„Ï¤      G   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Montgomery.phpä  ’¿Ýjä  cqó¯¤      C   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Binary.php*$  ’¿Ýj*$  7�iZ¤      B   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Prime.phpÜN  ’¿ÝjÜN  ”!Þ¢¤      K   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/TwistedEdwards.php¼  ’¿Ýj¼  Mã±å¤      <   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Parameters.phpõ  ’¿Ýjõ  R�6¤      <   vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/PrivateKey.php?A  ’¿Ýj?A  ¼w°`¤      2   vendor/phpseclib/phpseclib/phpseclib/Crypt/RC2.phpìN  ’¿ÝjìN  b·4¤      7   vendor/phpseclib/phpseclib/phpseclib/Crypt/Blowfish.php½¤  ’¿Ýj½¤  f†¸¤      7   vendor/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.php¦  ’¿Ýj¦  ;ïjŸ¤      1   vendor/phpseclib/phpseclib/phpseclib/Crypt/DH.phpýR  ’¿ÝjýR  ªºö¤      B   vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/SymmetricKey.php{² ’¿Ýj{² 
ÜÙ³¤      H   vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Traits/Fingerprint.phpÍ  ’¿ÝjÍ  þ…1À¤      N   vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Traits/PasswordProtected.phpä  ’¿Ýjä  çÞ‰Ñ¤      T   vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Traits/ASN1AlgorithmIdentifier.phpz3  ’¿Ýjz3  3E™G¤      I   vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Traits/PKCS12Helper.phpÔ  ’¿ÝjÔ  óWe¤      H   vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PKCS8.phpN0  ’¿ÝjN0  æc_½¤      G   vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PKCS.phpx  ’¿Ýjx  ¥>o¼¤      J   vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/OpenSSH.php@  ’¿Ýj@  ‰÷Ï¤      H   vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PKCS1.phpJ  ’¿ÝjJ  æœâž¤      H   vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PuTTY.php�4  ’¿Ýj�4  oùZ¤      F   vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/JWK.phpÊ  ’¿ÝjÊ  ²ÑÉ¤      K   vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Signature/Raw.phpÈ  ’¿ÝjÈ  ½ ²¤      A   vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/BlockCipher.php…  ’¿Ýj…  õ§<7¤      ?   vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/PublicKey.phpª  ’¿Ýjª  bVÏ¤      C   vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/AsymmetricKey.php¥<  ’¿Ýj¥<  _=‹:¤      @   vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/PrivateKey.php0  ’¿Ýj0  ¨Ä«¤      B   vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/StreamCipher.php,  ’¿Ýj,  c¡¤      2   vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.phpb¦  ’¿Ýjb¦  uË©u¤      C   vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PSS.php  ’¿Ýj  Ðßl|¤      F   vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/MSBLOB.php»  ’¿Ýj»  �ˆ¤      E   vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PKCS8.php  ’¿Ýj  Tc"¾¤      C   vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/Raw.php+  ’¿Ýj+  ÍMS±¤      G   vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/OpenSSH.php  ’¿Ýj  ³DÃ¤      C   vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/XML.phpq  ’¿Ýjq  sj/®¤      E   vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PKCS1.phpt  ’¿Ýjt  þ‘»V¤      E   vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PuTTY.phpP  ’¿ÝjP  ×E¯ã¤      C   vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/JWK.php  ’¿Ýj  ôWÕ�¤      <   vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/PublicKey.phpE(  ’¿ÝjE(  //GÂ¤      =   vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/PrivateKey.php86  ’¿Ýj86  �$k;¤      2   vendor/phpseclib/phpseclib/phpseclib/Crypt/AES.php  ’¿Ýj  ùÇ(D¤      E   vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/PKCS8.phpZ  ’¿ÝjZ  ¡€øù¤      C   vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/Raw.php  ’¿Ýj  +~º'¤      G   vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/OpenSSH.php  ’¿Ýj  *dëU¤      C   vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/XML.phpX  ’¿ÝjX   ×ý¤      E   vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/PKCS1.phpÕ  ’¿ÝjÕ  JGö;¤      E   vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/PuTTY.php¦  ’¿Ýj¦  û4a€¤      H   vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Signature/Raw.php0  ’¿Ýj0  ^ÐÈ‹¤      I   vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Signature/ASN1.php~  ’¿Ýj~  íÐâ¤      I   vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Signature/SSH2.php¸  ’¿Ýj¸  â7ñ$¤      <   vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/PublicKey.phpç  ’¿Ýjç  ­ eÃ¤      =   vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Parameters.phpÝ  ’¿ÝjÝ  p<!‹¤      =   vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/PrivateKey.php¦  ’¿Ýj¦  )s�ï¤      2   vendor/phpseclib/phpseclib/phpseclib/Crypt/DES.phpï ’¿Ýjï ¾Ry’¤      N   vendor/phpseclib/phpseclib/phpseclib/Exception/ServiceUnavailableException.phpð  ’¿Ýjð  Â·¤      K   vendor/phpseclib/phpseclib/phpseclib/Exception/UnexpectedValueException.phpø  ’¿Ýjø  Üá¤      K   vendor/phpseclib/phpseclib/phpseclib/Exception/InvalidArgumentException.phpƒ   ’¿Ýjƒ   ‰&&"¤      P   vendor/phpseclib/phpseclib/phpseclib/Exception/SSHChannelExitSignalException.php\  ’¿Ýj\  KÙÕw¤      I   vendor/phpseclib/phpseclib/phpseclib/Exception/KeyConstraintException.phpá  ’¿Ýjá  ^`OF¤      G   vendor/phpseclib/phpseclib/phpseclib/Exception/NoKeyLoadedException.phpÚ  ’¿ÝjÚ  Qã¬¤      L   vendor/phpseclib/phpseclib/phpseclib/Exception/UnsupportedValueException.php™   ’¿Ýj™   çU_ê¤      L   vendor/phpseclib/phpseclib/phpseclib/Exception/UnsupportedCurveException.phpê  ’¿Ýjê  ¹ÓKÀ¤      ?   vendor/phpseclib/phpseclib/phpseclib/Exception/EOCException.phpÂ  ’¿ÝjÂ  EÈ@k¤      I   vendor/phpseclib/phpseclib/phpseclib/Exception/ResourceLimitException.phpá  ’¿Ýjá  ]Û_î¤      H   vendor/phpseclib/phpseclib/phpseclib/Exception/InvalidStateException.phpï  ’¿Ýjï  Ï$‹a¤      O   vendor/phpseclib/phpseclib/phpseclib/Exception/InvalidPacketLengthException.phpÌ   ’¿ÝjÌ   #á„æ¤      O   vendor/phpseclib/phpseclib/phpseclib/Exception/ExcessivelyDeepDataException.phpð  ’¿Ýjð  {�Ü¤      G   vendor/phpseclib/phpseclib/phpseclib/Exception/InvalidModeException.php×  ’¿Ýj×  '÷ï#¤      R   vendor/phpseclib/phpseclib/phpseclib/Exception/EncodedDataUnavailableException.phpû  ’¿Ýjû  ÿÁ{~¤      C   vendor/phpseclib/phpseclib/phpseclib/Exception/TimeoutException.phpÈ   ’¿ÝjÈ   Š&å=¤      P   vendor/phpseclib/phpseclib/phpseclib/Exception/UnexpectedSFTPPacketException.phpõ  ’¿Ýjõ  ì;C¤      Q   vendor/phpseclib/phpseclib/phpseclib/Exception/NoSupportedAlgorithmsException.phpù  ’¿Ýjù  ZæíI¤      P   vendor/phpseclib/phpseclib/phpseclib/Exception/UnsupportedAlgorithmException.phpö  ’¿Ýjö  q¨ßD¤      L   vendor/phpseclib/phpseclib/phpseclib/Exception/BadConfigurationException.phpû  ’¿Ýjû  “ U*¤      P   vendor/phpseclib/phpseclib/phpseclib/Exception/UnexpectedSSHMessageException.phpõ  ’¿Ýjõ  x?3|¤      I   vendor/phpseclib/phpseclib/phpseclib/Exception/BadMethodCallException.php�   ’¿Ýj�   ˆ<à“¤      I   vendor/phpseclib/phpseclib/phpseclib/Exception/BadDecryptionException.phpà  ’¿Ýjà  A÷ô<¤      O   vendor/phpseclib/phpseclib/phpseclib/Exception/CharacterConversionException.phpò  ’¿Ýjò  @áóƒ¤      B   vendor/phpseclib/phpseclib/phpseclib/Exception/LengthException.php~   ’¿Ýj~   
±è¤      F   vendor/phpseclib/phpseclib/phpseclib/Exception/FileSystemException.php×  ’¿Ýj×  ¤Ã¤      J   vendor/phpseclib/phpseclib/phpseclib/Exception/PasswordNeededException.phpä  ’¿Ýjä  69Ê¤      @   vendor/phpseclib/phpseclib/phpseclib/Exception/BaseException.php    ’¿Ýj    üÙ¤      L   vendor/phpseclib/phpseclib/phpseclib/Exception/ConnectionClosedException.phpé  ’¿Ýjé  «îO¤      A   vendor/phpseclib/phpseclib/phpseclib/Common/Functions/Strings.php=  ’¿Ýj=  Ì�ê¤      @   vendor/phpseclib/phpseclib/phpseclib/Common/Functions/Arrays.phpé  ’¿Ýjé  ý6â»¤      ?   vendor/phpseclib/phpseclib/phpseclib/Common/Functions/Files.php@  ’¿Ýj@  �‚­Û¤      D   vendor/phpseclib/phpseclib/phpseclib/Common/ConstantUtilityTrait.phpn  ’¿Ýjn  oûA¤      1   vendor/phpseclib/phpseclib/phpseclib/File/PFX.php}o  ’¿Ýj}o  {‚u¶¤      1   vendor/phpseclib/phpseclib/phpseclib/File/CRL.php€^  ’¿Ýj€^  øgªe¤      3   vendor/phpseclib/phpseclib/phpseclib/File/SPKAC.phpó%  ’¿Ýjó%  Y>n¨¤      ?   vendor/phpseclib/phpseclib/phpseclib/File/CMS/EncryptedData.phpt  ’¿Ýjt  $!È ¤      <   vendor/phpseclib/phpseclib/phpseclib/File/CMS/SignedData.phpµS  ’¿ÝjµS  y¦³¤      C   vendor/phpseclib/phpseclib/phpseclib/File/CMS/SignedData/Signer.php%e  ’¿Ýj%e  b¤      @   vendor/phpseclib/phpseclib/phpseclib/File/CMS/CompressedData.php°  ’¿Ýj°  @üé6¤      I   vendor/phpseclib/phpseclib/phpseclib/File/CMS/EnvelopedData/Recipient.php¤  ’¿Ýj¤  –ë�n¤      Q   vendor/phpseclib/phpseclib/phpseclib/File/CMS/EnvelopedData/KeyAgreeRecipient.phpg  ’¿Ýjg  yø"=¤      L   vendor/phpseclib/phpseclib/phpseclib/File/CMS/EnvelopedData/KEKRecipient.php“  ’¿Ýj“  (0’”¤      L   vendor/phpseclib/phpseclib/phpseclib/File/CMS/EnvelopedData/DerivableKey.phpÖ  ’¿ÝjÖ  ‚€XÈ¤      M   vendor/phpseclib/phpseclib/phpseclib/File/CMS/EnvelopedData/SearchableKey.php¬  ’¿Ýj¬  bpC~¤      Q   vendor/phpseclib/phpseclib/phpseclib/File/CMS/EnvelopedData/PasswordRecipient.phpO  ’¿ÝjO  Ãô“¤      Q   vendor/phpseclib/phpseclib/phpseclib/File/CMS/EnvelopedData/KeyTransRecipient.php#  ’¿Ýj#  É¤      ^   vendor/phpseclib/phpseclib/phpseclib/File/CMS/EnvelopedData/KeyAgreeRecipient/EncryptedKey.phpÞ"  ’¿ÝjÞ"  µËa¤      >   vendor/phpseclib/phpseclib/phpseclib/File/CMS/DigestedData.php»  ’¿Ýj»  :s7¤      >   vendor/phpseclib/phpseclib/phpseclib/File/Common/Traits/DN.phpùT  ’¿ÝjùT  x†Ëê¤      E   vendor/phpseclib/phpseclib/phpseclib/File/Common/Traits/Extension.phpé@  ’¿Ýjé@  õÀÏŽ¤      I   vendor/phpseclib/phpseclib/phpseclib/File/Common/Traits/KeyDerivation.php  ’¿Ýj  ”	U4¤      I   vendor/phpseclib/phpseclib/phpseclib/File/Common/Traits/ASN1Signature.php   ’¿Ýj   åjØi¤      =   vendor/phpseclib/phpseclib/phpseclib/File/Common/Signable.phpÐ  ’¿ÝjÐ  àžûf¤      2   vendor/phpseclib/phpseclib/phpseclib/File/ASN1.php4ø  ’¿Ýj4ø  L˜]¢¤      D   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralNames.phpb  ’¿Ýjb  ˜Äå§¤      E   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RecipientInfo.php�  ’¿Ýj�  ûHÍ¤      H   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ObjectDigestInfo.phpR  ’¿ÝjR  8\¤      I   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/LogotypeReference.php(  ’¿Ýj(  k,j¤      E   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EncryptedData.php  ’¿Ýj  –v(¤      N   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AuthorityKeyIdentifier.phpw  ’¿Ýjw   P«š¤      A   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SecretBag.php\  ’¿Ýj\  §€‚Ò¤      L   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EncryptedContentInfo.php}  ’¿Ýj}  –A¯î¤      W   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CompressionAlgorithmIdentifiers.php¨  ’¿Ýj¨  2X‰~¤      L   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificationRequest.php  ’¿Ýj  ë¤      C   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/QCStatement.phpö  ’¿Ýjö  ãî&Î¤      Z   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/MessageAuthenticationCodeAlgorithm.php  ’¿Ýj  `V›þ¤      K   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificatePolicies.php}  ’¿Ýj}  HîVz¤      A   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Attribute.php  ’¿Ýj  º<M¤      F   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OtherPrimeInfo.php9  ’¿Ýj9  ÔE8ñ¤      D   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/IssuerSerial.php›  ’¿Ýj›  ñ[p¤      C   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/TSAPolicyId.php  ’¿Ýj  nñ5¤      D   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBEParameter.phpë  ’¿Ýjë   Û¤      L   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectPublicKeyInfo.phpÆ  ’¿ÝjÆ  ÆìÎ*¤      M   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateKeyUsagePeriod.phpÎ  ’¿ÝjÎ  Dö¦¤      E   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EnvelopedData.php@  ’¿Ýj@  û‰�×¤      I   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/LogotypeAudioInfo.phpŠ  ’¿ÝjŠ  1»êl¤      ;   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PFX.phpH  ’¿ÝjH  ”rZ¿¤      H   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/netscape_comment.php  ’¿Ýj  \ôpõ¤      C   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ContentInfo.php  ’¿Ýj  ÕLr¤      O   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OrganizationalUnitNames.php´  ’¿Ýj´  õãÿ�¤      I   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/LogotypeImageType.php�  ’¿Ýj�  æh�æ¤      B   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SignedData.phpe  ’¿Ýje  UÙ;¼¤      H   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BasicConstraints.phpa  ’¿Ýja  $b€y¤      V   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BuiltInDomainDefinedAttributes.phpÉ  ’¿ÝjÉ  S&Á¤      M   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NumericUserIdentifier.php#  ’¿Ýj#  r¨C•¤      H   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EncryptedDataCMS.phpY  ’¿ÝjY  Ëïø¤      H   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/MaskGenAlgorithm.phpì  ’¿Ýjì  xäÏ¤      D   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBMAC1params.phpê  ’¿Ýjê  [µ.l¤      ?   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/MacData.phpL  ’¿ÝjL  ÐP†÷¤      G   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificateList.phpè  ’¿Ýjè  Nàn¤      J   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/netscape_cert_type.phpk  ’¿Ýjk  çB²¤      H   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CMSEncryptedData.php[  ’¿Ýj[  èÎ1¤      M   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/MQVuserKeyingMaterial.php;  ’¿Ýj;  n­Þ ¤      J   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/UnsignedAttributes.phpm  ’¿Ýjm  Ù\Ý¤      I   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AccessDescription.php½  ’¿Ýj½  Õ©yã¤      J   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificateChoices.phpD  ’¿ÝjD  ‘3Ô3¤      D   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RSAPublicKey.php­  ’¿Ýj­  ˜¤^¤      A   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Trinomial.phpø  ’¿Ýjø  Â|�î¤      @   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyUsage.php,  ’¿Ýj,  cn1²¤      P   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificationRequestInfo.phpÛ  ’¿ÝjÛ  …T˜¤      G   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RC2CBCParameter.php;  ’¿Ýj;  =\ž¤      C   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DHParameter.phpx  ’¿Ýjx  %Að¤      I   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyQualifierId.php  ’¿Ýj  ò©÷¸¤      F   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeValue.php  ’¿Ýj  ø¶»¤      I   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SMIMECapabilities.phpº  ’¿Ýjº  ‡Þy¤      E   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttCertIssuer.phpÜ  ’¿ÝjÜ  ¢9¬Ý¤      F   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/TBSCertificate.php|  ’¿Ýj|  :/å¤      C   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Certificate.phpâ  ’¿Ýjâ  ˆJ…¤      D   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ECPrivateKey.php¢  ’¿Ýj¢  V5W¤      Q   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OtherRevocationInfoFormat.phpã  ’¿Ýjã  W¤kÕ¤      K   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Iso4217CurrencyCode.phpÚ  ’¿ÝjÚ  çM¥,¤      U   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BuiltInDomainDefinedAttribute.phpæ  ’¿Ýjæ  ç·‚¤      N   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeCertificateV1.php  ’¿Ýj  Çò4H¤      B   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/UserNotice.phpC  ’¿ÝjC  »�¢Ò¤      ?   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SafeBag.php.  ’¿Ýj.  —ué¤      F   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificateSet.phpj  ’¿Ýjj  „8?¤      J   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SigningCertificate.phpñ  ’¿Ýjñ  Gò³¢¤      A   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ESSCertID.php¸  ’¿Ýj¸  Ù!¢N¤      D   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/QCStatements.php�  ’¿Ýj�  W•é¤      \   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ContentEncryptionAlgorithmIdentifier.php  ’¿Ýj  ™ˆ¼…¤      F   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/MessageImprint.php·  ’¿Ýj·  j	Î±¤      ?   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertBag.php&  ’¿Ýj&  óì–¤      >   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/V2Form.php…  ’¿Ýj…  Á^pø¤      F   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/QcEuLimitValue.phpG  ’¿ÝjG  M…Šñ¤      M   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyTransRecipientInfo.php(  ’¿Ýj(  �%Ž¤      E   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KEKIdentifier.phpB  ’¿ÝjB  emc%¤      C   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralName.php	  ’¿Ýj	  ”vQ¤      H   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/UniqueIdentifier.php  ’¿Ýj  ÿ�'¤      E   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/IssuerAltName.phpÜ  ’¿ÝjÜ  °Ð0¤      C   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DssSigValue.php—  ’¿Ýj—  ü´ƒø¤      F   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyMappings.php0  ’¿Ýj0  žTA¤      L   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RevocationInfoChoice.php'  ’¿Ýj'  ü0Mk¤      S   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SignedPublicKeyAndChallenge.php   ’¿Ýj   Þkfê¤      C   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PKCS9String.php¤  ’¿Ýj¤  GÌú¤      I   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DistributionPoint.phpL  ’¿ÝjL  9àq¤      H   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SignerIdentifier.php=  ’¿Ýj=  äJµ>¤      <   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Hash.phpî  ’¿Ýjî  ^‹wt¤      M   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyAgreeRecipientInfo.phpm  ’¿Ýjm  a_-“¤      D   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/LogotypeExtn.phpÈ  ’¿ÝjÈ  “ Ÿ+¤      =   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Curve.phpç  ’¿Ýjç  ™vb¤      O   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EncryptedPrivateKeyInfo.phpÇ  ’¿ÝjÇ  ù{ÜŽ¤      K   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ExtendedCertificate.phpY  ’¿ÝjY  ®š‡R¤      <   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Name.phpI  ’¿ÝjI  7Fæ²¤      G   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DirectoryString.php“  ’¿Ýj“  œþª‡¤      F   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OriginatorInfo.phpŽ  ’¿ÝjŽ  ùÈÝ¤      C   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CountryName.php¶  ’¿Ýj¶  ÝJC–¤      X   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyEncryptionAlgorithmIdentifier.php  ’¿Ýj  *‘\O¤      V   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CompressionAlgorithmIdentifier.php  ’¿Ýj  hðŒ-¤      H   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OrganizationName.php  ’¿Ýj  tdx¤      >   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Digest.phpô  ’¿Ýjô    U×¤      E   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyIdentifier.php	  ’¿Ýj	  m"Š¤      D   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/LogotypeData.php†  ’¿Ýj†  üº
&¤      G   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NoticeReference.phpD  ’¿ÝjD  âÄ¶¤      E   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/LogotypeImage.phpö  ’¿Ýjö  º¤      E   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PostalAddress.php…  ’¿Ýj…  �$^¤      I   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateDomainName.phpÅ  ’¿ÝjÅ  ¨{¸¤      G   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SMIMECapability.php’  ’¿Ýj’  ¯¯õð¤      B   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CMSVersion.phpE  ’¿ÝjE  ÔÌn¤      M   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PublicKeyAndChallenge.php¾  ’¿Ýj¾  ÃâO¤      H   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttCertVersionV1.php5  ’¿Ýj5  0œcß¤      I   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RSAES_OAEP_params.php   ’¿Ýj   y¨`‹¤      I   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ExtKeyUsageSyntax.phpr  ’¿Ýjr  ¦Izê¤      D   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/LogotypeInfo.php  ’¿Ýj  b€^ù¤      O   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectInfoAccessSyntax.php‰  ’¿Ýj‰  ÙÞ×Ö¤      O   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ExtendedCertificateInfo.phpê  ’¿Ýjê  £œÉ¤      L   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SigningCertificateV2.phpù  ’¿Ýjù  ÿìä}¤      F   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NetworkAddress.php  ’¿Ýj  ‹»o!¤      H   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EncryptedContent.php  ’¿Ýj  æS¤      P   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeCertificateInfo.php:  ’¿Ýj:  Õò«i¤      G   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralSubtrees.phpn  ’¿Ýjn  á¡–¤      J   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/UserKeyingMaterial.php  ’¿Ýj  ×a^¤      D   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBKDF2params.php=  ’¿Ýj=  }Û¡Ÿ¤      P   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AdministrationDomainName.phpÌ  ’¿ÝjÌ  õO¦z¤      Q   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OriginatorIdentifierOrKey.php  ’¿Ýj  S´•u¤      C   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DisplayText.php1  ’¿Ýj1  ¶¤n¤      E   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeType.php  ’¿Ýj  ]žF¤      E   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DSAPrivateKey.phpo  ’¿Ýjo  †°2¤      I   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OtherLogotypeInfo.phpO  ’¿ÝjO  é3A¤      D   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PersonalName.phph  ’¿Ýjh  £x\¤      E   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RSAPrivateKey.php¡  ’¿Ýj¡  &Ð„t¤      A   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PublicKey.phpû  ’¿Ýjû  ð:â,¤      H   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OneAsymmetricKey.php«  ’¿Ýj«  óüª¤      Q   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BuiltInStandardAttributes.php  ’¿Ýj  ,?{<¤      C   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Pentanomial.phpí  ’¿Ýjí  ùÚi¤      G   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NameConstraints.php¤  ’¿Ýj¤  ]§ñ¤      I   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificateIssuer.phpè  ’¿Ýjè  
©Ú)¤      ?   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ECPoint.php÷  ’¿Ýj÷  Œ÷sW¤      H   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ECCCMSSharedInfo.php  ’¿Ýj  €0¿¤¤      J   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RevokedCertificate.php  ’¿Ýj  ´/!C¤      F   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/TimeStampToken.php&  ’¿Ýj&  ^(·¤      C   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SignerInfos.phpW  ’¿ÝjW  N…D™¤      J   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ExtensionAttribute.phpð  ’¿Ýjð  /áj&¤      R   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DigestAlgorithmIdentifiers.php°  ’¿Ýj°  ©w#Ò¤      R   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeCertificateInfoV1.phpù  ’¿Ýjù  ¢™R¤      D   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/FieldElement.php  ’¿Ýj  þR¤      I   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/LogotypeImageInfo.phpš  ’¿Ýjš  íc
¢¤      C   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AnotherName.php/  ’¿Ýj/  �E¯¤      I   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RSASSA_PSS_params.php  ’¿Ýj  Å5>¤      E   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/HashAlgorithm.phpã  ’¿Ýjã  7ËÇÉ¤      H   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/UnauthAttributes.phpg  ’¿Ýjg  „@Î¤      D   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BaseDistance.php  ’¿Ýj  ¹ÃÅÐ¤      ?   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/TSTInfo.phpG  ’¿ÝjG  (��”¤      N   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/netscape_ca_policy_url.php"  ’¿Ýj"  Ü1âÓ¤      N   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RecipientEncryptedKeys.phpŠ  ’¿ÝjŠ  ],HÑ¤      N   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RecipientKeyIdentifier.phpX  ’¿ÝjX  Ìà„û¤      M   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeTypeAndValue.php¥  ’¿Ýj¥  g0LE¤      Q   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/MessageAuthenticationCode.php-  ’¿Ýj-  œ3¦Œ¤      O   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/LogotypeImageResolution.phpo  ’¿Ýjo  —ïúÝ¤      F   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CompressedData.phpï  ’¿Ýjï  ÎAø>¤      Q   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DigestAlgorithmIdentifier.phpò  ’¿Ýjò  ÿ@ç}¤      K   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/HoldInstructionCode.php   ’¿Ýj   «�²¤      M   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RevocationInfoChoices.php�  ’¿Ýj�  ©Ø*K¤      X   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyDerivationAlgorithmIdentifier.php  ’¿Ýj  &0ã¤      I   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyInformation.phpo  ’¿Ýjo  V«¤      G   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OtherPrimeInfos.php¦  ’¿Ýj¦  8|¶j¤      C   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ReasonFlags.php>  ’¿Ýj>  P£¤      F   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RecipientInfos.phpe  ’¿Ýje  ›Ã¤¤      K   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AlgorithmIdentifier.php-  ’¿Ýj-  ëÉEÍ¤      A   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ORAddress.php@  ’¿Ýj@  ãƒ~g¤      I   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OtherKeyAttribute.php   ’¿Ýj   ±=ó¤      A   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Extension.phpM  ’¿ÝjM  êÿ¶¤      M   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PasswordRecipientInfo.phpÃ  ’¿ÝjÃ  Ñ–aÌ¤      A   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DSAParams.phpÄ  ’¿ÝjÄ  [)ê˜¤      K   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OriginatorPublicKey.php¼  ’¿Ýj¼  n†áû¤      G   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/LogotypeDetails.php^  ’¿Ýj^  �=­ø¤      G   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/HashAlgAndValue.phpñ  ’¿Ýjñ  	�>®¤      M   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/IssuerAndSerialNumber.php®  ’¿Ýj®  ¾p±É¤      C   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/TBSCertList.phpç  ’¿Ýjç  ^ŒÖ•¤      C   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RDNSequence.php(  ’¿Ýj(  ZÚûN¤      B   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SignerInfo.phpž  ’¿Ýjž  æU¤¤      E   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/LogotypeAudio.phpö  ’¿Ýjö  ‰8ò¤      K   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ExtensionAttributes.php•  ’¿Ýj•  ÿ&|�¤      F   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttCertVersion.php3  ’¿Ýj3  �?ÅÝ¤      N   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CMSAlgorithmProtection.php*  ’¿Ýj*  _…˜Î¤      K   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PKCS8ShroudedKeyBag.phpù  ’¿Ýjù  à‹v¤      D   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertPolicyId.php  ’¿Ýj  |„òç¤      F   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ShakeOutputLen.php  ’¿Ýj  BÃO„¤      ?   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Prime_p.phpò  ’¿Ýjò  Í¤      D   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DigestedData.php  ’¿Ýj  [Àk‡¤      Q   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RelativeDistinguishedName.phpò  ’¿Ýjò  Ÿ`‹‘¤      H   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PSourceAlgorithm.phpì  ’¿Ýjì  ëRÄs¤      O   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificateSerialNumber.php"  ’¿Ýj"  r1rÿ¤      B   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Attributes.phpU  ’¿ÝjU  °kîñ¤      C   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ContentType.php  ’¿Ýj  *©y»¤      F   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectAltName.phpß  ’¿Ýjß  b½…×¤      E   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/MonetaryValue.phpl  ’¿Ýjl  >¥~û¤      Q   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AuthorityInfoAccessSyntax.php�  ’¿Ýj�  4 H­¤      M   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/UnprotectedAttributes.phpv  ’¿Ýjv  ”>Lf¤      I   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SpecifiedECDomain.php{  ’¿Ýj{  BºÙŠ¤      J   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/TerminalIdentifier.php  ’¿Ýj  ¤n¤      D   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyPurposeId.php  ’¿Ýj  ‰iRÙ¤      F   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SignatureValue.php  ’¿Ýj  8Nuê¤      F   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralSubtree.phpÒ  ’¿ÝjÒ  ®I¤      E   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EcdsaSigValue.php�  ’¿Ýj�  ¨=³Ô¤      F   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/InvalidityDate.php  ’¿Ýj  ´jI¤      M   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CRLDistributionPoints.phpƒ  ’¿Ýjƒ  ©ÐE¤      P   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/IssuingDistributionPoint.php…  ’¿Ýj…  ÀÆE¤      >   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Holder.phpÅ  ’¿ÝjÅ  ‹ÜË¤      E   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PublicKeyInfo.php  ’¿Ýj  «À6C¤      I   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AuthEnvelopedData.php&  ’¿Ýj&  ];¤      @   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Accuracy.phpá  ’¿Ýjá  64&9¤      D   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ECParameters.php•  ’¿Ýj•  P€ŽC¤      J   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Characteristic_two.phpH  ’¿ÝjH  c™Q¤      K   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyQualifierInfo.php»  ’¿Ýj»  ÔÑ‡¤      H   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KEKRecipientInfo.php  ’¿Ýj  E‹‰ƒ¤      B   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Extensions.phpî  ’¿Ýjî  f°ßt¤      B   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateKey.php   ’¿Ýj   ¨ê¯×¤      C   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ESSCertIDv2.php^  ’¿Ýj^  ÎÑ‚¤      D   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EDIPartyName.phpU  ’¿ÝjU  © b¼¤      O   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EncapsulatedContentInfo.phpL  ’¿ÝjL  óŸãë¤      D   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DSAPublicKey.php  ’¿Ýj  \·†¤      R   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectDirectoryAttributes.phpŠ  ’¿ÝjŠ  eÌV¤      F   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AuthAttributes.phpa  ’¿Ýja  	“¢n¤      J   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OtherRecipientInfo.php¿  ’¿Ýj¿  vÇC¤      N   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OtherCertificateFormat.phpÔ  ’¿ÝjÔ  úÿ¤      D   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SafeContents.php^  ’¿Ýj^  ­dÐ¤      S   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyAgreeRecipientIdentifier.phpR  ’¿ÝjR  çw¿j¤      N   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeCertificateV2.php  ’¿Ýj  Š�²	¤      D   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EncryptedKey.php  ’¿Ýj  ­Bìî¤      C   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBES2params.phpæ  ’¿Ýjæ  ¢ü‘€¤      @   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Validity.phps  ’¿Ýjs  ªåt„¤      A   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CRLNumber.phpø  ’¿Ýjø  À‹}^¤      B   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DigestInfo.phpâ  ’¿Ýjâ  “
ú¤      F   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateKeyInfo.phpË  ’¿ÝjË  –mÄð¤      ?   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/FieldID.phpå  ’¿Ýjå  ]7Ûp¤      T   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SignatureAlgorithmIdentifier.phpû  ’¿Ýjû  1%e	¤      M   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DistributionPointName.php¹  ’¿Ýj¹  ÍQžó¤      A   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CRLReason.phpƒ  ’¿Ýjƒ  ó%(y¤      K   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RecipientIdentifier.phpy  ’¿Ýjy  Ï¦¸¤      L   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectKeyIdentifier.php  ’¿Ýj  =IH¤      I   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AuthenticatedSafe.phpq  ’¿Ýjq  ‚çM�¤      H   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SignedAttributes.phpg  ’¿Ýjg  q©'¤      M   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttCertValidityPeriod.phpÞ  ’¿ÝjÞ  yÖžs¤      >   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CPSuri.phpò  ’¿Ýjò  &¤•Û¤      <   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Time.phpš  ’¿Ýjš  d‘öw¤      M   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RecipientEncryptedKey.php·  ’¿Ýj·  æ¶Â¤      >   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Constructed.php‚  ’¿Ýj‚  ¢Y¡¤      ;   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/OIDs/PFX.php  ’¿Ýj  ²T¸¤      :   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/OIDs/EC.phpz  ’¿Ýjz  án7“¤      =   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/OIDs/PKCS8.phpY	  ’¿ÝjY	  ½Ñ'¤      >   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/OIDs/Hashes.phpÜ  ’¿ÝjÜ  ‡ÍÁb¤      ;   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/OIDs/CMS.php   ’¿Ýj   Äªùë¤      <   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/OIDs/X509.phpÅ%  ’¿ÝjÅ%  ŠÛ2˜¤      >   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/OIDs/Curves.phpï  ’¿Ýjï  ˜­ð«¤      @   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Types/Integer.phpP  ’¿ÝjP  )}èz¤      H   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Types/GeneralizedTime.phpt  ’¿Ýjt  ó$B¤      F   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Types/TeletexString.phpw  ’¿Ýjw  _¸t´¤      H   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Types/UniversalString.phpD  ’¿ÝjD  ‰Ÿ$¤      B   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Types/IA5String.phpj  ’¿Ýjj  Gw%¤      C   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Types/BaseString.phpÕ  ’¿ÝjÕ  þ¬E¤      F   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Types/NumericString.php  ’¿Ýj  jY|¤      F   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Types/VisibleString.php7  ’¿Ýj7  ûÐ@¤      F   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Types/GeneralString.php-  ’¿Ýj-  oÁjÇ¤      ?   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Types/Common.phpæ  ’¿Ýjæ  tê’¤      @   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Types/Boolean.php  ’¿Ýj  0@5¤      @   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Types/UTCTime.php\  ’¿Ýj\  6çCX¤      D   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Types/OctetString.phpI  ’¿ÝjI  In©ë¤      B   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Types/BMPString.phpn  ’¿Ýjn  ”Lßâ¤      B   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Types/BitString.php¤
  ’¿Ýj¤
  Oö¤      F   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Types/GraphicString.phpñ  ’¿Ýjñ  Bá½¤      C   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Types/UTF8String.phpw  ’¿Ýjw  5×¡(¤      H   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Types/PrintableString.phpÌ  ’¿ÝjÌ  ‡pI¤      E   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Types/ExplicitNull.php¿  ’¿Ýj¿  ]®Äó¤      <   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Types/OID.php�  ’¿Ýj�  v‰MW¤      ?   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Types/Choice.php7  ’¿Ýj7  è iÐ¤      A   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Types/BaseType.phpì  ’¿Ýjì  4hw¤      G   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Types/VideotexString.phpO  ’¿ÝjO  X»†¤      :   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Element.phpl  ’¿Ýjl  U]¤      @   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/MalformedData.phpÒ  ’¿ÝjÒ  ‹/â¤      F   vendor/phpseclib/phpseclib/phpseclib/File/ASN1/ExcessivelyDeepData.phpæ  ’¿Ýjæ  3‹2!¤      1   vendor/phpseclib/phpseclib/phpseclib/File/CMS.php…  ’¿Ýj…  ®Ï ¤      1   vendor/phpseclib/phpseclib/phpseclib/File/CSR.php=l  ’¿Ýj=l  #âñ¤      2   vendor/phpseclib/phpseclib/phpseclib/File/X509.php%Ø  ’¿Ýj%Ø  QÁA`¤      2   vendor/phpseclib/phpseclib/phpseclib/File/ANSI.phpôK  ’¿ÝjôK  ¹€ù¤      9   vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent.phpž!  ’¿Ýjž!  4¤Žn¤      B   vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent/Identity.phpß"  ’¿Ýjß"  ¼ÐØç¤      K   vendor/phpseclib/phpseclib/phpseclib/System/SSH/Common/Traits/ReadBytes.php©  ’¿Ýj©  -Oå,¤      8   vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger.phpM  ’¿ÝjM  ‚¯¯l¤      9   vendor/phpseclib/phpseclib/phpseclib/Math/BinaryField.phph  ’¿Ýjh  s#xö¤      I   vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath64.php9  ’¿Ýj9  K—y¤      W   vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Barrett.php"-  ’¿Ýj"-  à•ü4¤      ^   vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/MontgomeryMult.phpK  ’¿ÝjK  ¶ê\¤      Z   vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/PowerOfTwo.php·  ’¿Ýj·  ifG¤      [   vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/EvalBarrett.phpx;  ’¿Ýjx;  ûØíd¤      Z   vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Montgomery.php5  ’¿Ýj5  ‹µt†¤      W   vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Classic.php  ’¿Ýj  —Ó³Ó¤      I   vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Base.phpw  ’¿Ýjw  ´B¢l¤      L   vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/OpenSSL.phpI  ’¿ÝjI  °,—_¤      O   vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Montgomery.phpÝ	  ’¿ÝjÝ	  [”%Î¤      R   vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/DefaultEngine.phpO  ’¿ÝjO  QB|¤      D   vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP.php{–  ’¿Ýj{–  ƒÃò¤      F   vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP64.phpX   ’¿ÝjX   >dfy¤      R   vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/GMP/DefaultEngine.php=  ’¿Ýj=  } ?	¤      H   vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/OpenSSL.php¸  ’¿Ýj¸  8_à"¤      G   vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/Engine.php”  ’¿Ýj”  µ#k­¤      G   vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath.phpÊ@  ’¿ÝjÊ@  ¦³Üò¤      Z   vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/Reductions/Barrett.phpò  ’¿Ýjò  IpIB¤      ^   vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/Reductions/EvalBarrett.php  ’¿Ýj  Z«W¤      L   vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/Base.php  ’¿Ýj  þ�Gv¤      O   vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/OpenSSL.phpL  ’¿ÝjL  €;ä¤      O   vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/BuiltIn.phpj  ’¿Ýjj  ÂB¤      U   vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/DefaultEngine.phpP  ’¿ÝjP  ÆÉ­F¤      D   vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/GMP.php";  ’¿Ýj";  aÀ¦¤      F   vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP32.php   ’¿Ýj   Iƒ3¤      @   vendor/phpseclib/phpseclib/phpseclib/Math/PrimeField/Integer.phpŸ*  ’¿ÝjŸ*  h”·¤      @   vendor/phpseclib/phpseclib/phpseclib/Math/Common/FiniteField.php“  ’¿Ýj“  þ×¤      H   vendor/phpseclib/phpseclib/phpseclib/Math/Common/FiniteField/Integer.phpñ  ’¿Ýjñ  ÙhÇ¤      8   vendor/phpseclib/phpseclib/phpseclib/Math/PrimeField.php6
  ’¿Ýj6
  Js¤      A   vendor/phpseclib/phpseclib/phpseclib/Math/BinaryField/Integer.php64  ’¿Ýj64  PÇ0¤      >   vendor/phpseclib/phpseclib/phpseclib/Net/SSH2/TerminalMode.php—   ’¿Ýj—   æÑÀ¤      =   vendor/phpseclib/phpseclib/phpseclib/Net/SSH2/MessageType.php×  ’¿Ýj×  þ´¤      B   vendor/phpseclib/phpseclib/phpseclib/Net/SSH2/MessageTypeExtra.phpi  ’¿Ýji  }vë�¤      B   vendor/phpseclib/phpseclib/phpseclib/Net/SSH2/DisconnectReason.phpC  ’¿ÝjC  è9+u¤      P   vendor/phpseclib/phpseclib/phpseclib/Net/SSH2/ChannelConnectionFailureReason.php2  ’¿Ýj2  E!¾T¤      0   vendor/phpseclib/phpseclib/phpseclib/Net/SCP.php�$  ’¿Ýj�$  Ÿpí±¤      1   vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.php¯ ’¿Ýj¯ G¶Ô¸¤      1   vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.phpzê ’¿Ýjzê 2¨=�¤      ;   vendor/phpseclib/phpseclib/phpseclib/Net/SFTP/Attribute.phpô  ’¿Ýjô  èæ>z¤      ;   vendor/phpseclib/phpseclib/phpseclib/Net/SFTP/OpenFlag5.phpà  ’¿Ýjà  ‹}�Ü¤      <   vendor/phpseclib/phpseclib/phpseclib/Net/SFTP/StatusCode.php=  ’¿Ýj=  º5èœ¤      :   vendor/phpseclib/phpseclib/phpseclib/Net/SFTP/OpenFlag.php”  ’¿Ýj”  j®lò¤      :   vendor/phpseclib/phpseclib/phpseclib/Net/SFTP/FileType.phpž  ’¿Ýjž  LÌl;¤      <   vendor/phpseclib/phpseclib/phpseclib/Net/SFTP/PacketType.phpì  ’¿Ýjì  ï´¯i¤      8   vendor/phpseclib/phpseclib/phpseclib/Net/SFTP/Stream.phpÀa  ’¿ÝjÀa  Heý¤         vendor/autoload.phpì  ’¿Ýjì  _Çà7¤      !   vendor/composer/autoload_real.phpˆ  ’¿Ýjˆ  TX¾,¤      %   vendor/composer/autoload_classmap.php^ ’¿Ýj^ G$Ýô¤      "   vendor/composer/platform_check.phpÿ  ’¿Ýjÿ  "2Ä—¤      #   vendor/composer/autoload_static.phpH¼ ’¿ÝjH¼ WêHƒ¤         vendor/composer/ClassLoader.phpú?  ’¿Ýjú?  2@u¤      %   vendor/composer/InstalledVersions.phpóC  ’¿ÝjóC  <nw¤      !   vendor/composer/autoload_psr4.phpÊ  ’¿ÝjÊ  ä¢Àã¤      "   vendor/composer/autoload_files.phpó  ’¿Ýjó  –ôÞ¤         vendor/composer/installed.php§|  ’¿Ýj§|  i)ˆ¤      '   vendor/composer/autoload_namespaces.phpÊ   ’¿ÝjÊ   j˜8V¤         vendor/composer/installed.json4‡ ’¿Ýj4‡ $¾%y¤      $   vendor/brick/math/src/BigInteger.php¥º  ’¿Ýj¥º  úP=¤      $   vendor/brick/math/src/BigDecimal.php¤—  ’¿Ýj¤—  Ñ3…—¤      <   vendor/brick/math/src/Exception/InvalidArgumentException.php�  ’¿Ýj�  xQgÎ¤      ;   vendor/brick/math/src/Exception/NegativeNumberException.php$  ’¿Ýj$  zæîª¤      >   vendor/brick/math/src/Exception/RoundingNecessaryException.php&  ’¿Ýj&  Þn—Ê¤      9   vendor/brick/math/src/Exception/NumberFormatException.php†  ’¿Ýj†  er¤¤      9   vendor/brick/math/src/Exception/RandomSourceException.php?  ’¿Ýj?  ¡¶¸k¤      1   vendor/brick/math/src/Exception/MathException.php³   ’¿Ýj³   ‚þ¤      <   vendor/brick/math/src/Exception/IntegerOverflowException.php–  ’¿Ýj–  ëÀê¤      ;   vendor/brick/math/src/Exception/DivisionByZeroException.php  ’¿Ýj  (2“¤      6   vendor/brick/math/src/Exception/NoInverseException.php’  ’¿Ýj’  ›\m¤      5   vendor/brick/math/src/Exception/PlatformException.php  ’¿Ýj  †U$¤      %   vendor/brick/math/src/BigRational.phpÉF  ’¿ÝjÉF  T¡D�¤      #   vendor/brick/math/src/BigNumber.phpz  ’¿Ýjz  Øö¼©¤      &   vendor/brick/math/src/NumberSyntax.php©  ’¿Ýj©  ëœÕ¤      &   vendor/brick/math/src/RoundingMode.phpú  ’¿Ýjú  ?ùÖ:¤      >   vendor/brick/math/src/Internal/Calculator/NativeCalculator.php¥7  ’¿Ýj¥7  J;ŸÃ¤      ;   vendor/brick/math/src/Internal/Calculator/GmpCalculator.phpT  ’¿ÝjT  vùÈ¤      >   vendor/brick/math/src/Internal/Calculator/BcMathCalculator.php  ’¿Ýj  9ÏNÌ¤      -   vendor/brick/math/src/Internal/Calculator.php\O  ’¿Ýj\O  –»ÿ­¤      '   vendor/brick/math/src/Internal/Safe.phpm  ’¿Ýjm  “Í…´¤      5   vendor/brick/math/src/Internal/CalculatorRegistry.php_  ’¿Ýj_  „¹W]¤      0   vendor/brick/math/src/Internal/DecimalHelper.php«  ’¿Ýj«  óŽT¤         vendor/brick/math/composer.json0  ’¿Ýj0  ¤Îòx¤         composer.jsonñ  ’¿Ýjñ  fÐò{¤      9   vendor/danog/madelineproto/src/WebUI/templates/page.phtml¶  ’¿Ýj¶  ŸÑÁ¤      ;   vendor/danog/madelineproto/src/WebUI/templates/layout.phtml_  ’¿Ýj_  šþ*v¤      >   vendor/danog/madelineproto/src/WebUI/templates/assets/mark.svgø   ’¿Ýjø   ŸÈS¤      B   vendor/danog/madelineproto/src/WebUI/templates/assets/madeline.cssÁ:  ’¿ÝjÁ:  h9�¤      A   vendor/danog/madelineproto/src/WebUI/templates/assets/tweb/app.jsÅ ’¿ÝjÅ lA-‚¤      B   vendor/danog/madelineproto/src/WebUI/templates/assets/tweb/app.css� ’¿Ýj� þ¯ó�¤      D   vendor/danog/madelineproto/src/WebUI/templates/partials/fields.phtml%  ’¿Ýj%  mÁçÈ¤      C   vendor/danog/madelineproto/src/WebUI/templates/partials/brand.phtmlî   ’¿Ýjî   ËÆ�­¤      D   vendor/danog/madelineproto/src/WebUI/templates/partials/alerts.phtml»  ’¿Ýj»  CæyF¤      E   vendor/danog/madelineproto/src/WebUI/templates/partials/actions.phtml	  ’¿Ýj	  xm}¤      C   vendor/danog/madelineproto/src/WebUI/templates/partials/steps.phtml’  ’¿Ýj’  â/˜¨¤      <?php declare(strict_types=1);

namespace PhpParser;

use PhpParser\Internal\DiffElem;
use PhpParser\Internal\Differ;
use PhpParser\Internal\PrintableNewAnonClassNode;
use PhpParser\Internal\TokenStream;
use PhpParser\Node\AttributeGroup;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\AssignOp;
use PhpParser\Node\Expr\BinaryOp;
use PhpParser\Node\Expr\Cast;
use PhpParser\Node\IntersectionType;
use PhpParser\Node\MatchArm;
use PhpParser\Node\Param;
use PhpParser\Node\PropertyHook;
use PhpParser\Node\Scalar;
use PhpParser\Node\Stmt;
use PhpParser\Node\UnionType;

abstract class PrettyPrinterAbstract implements PrettyPrinter {
    protected const FIXUP_PREC_LEFT = 0; // LHS operand affected by precedence
    protected const FIXUP_PREC_RIGHT = 1; // RHS operand affected by precedence
    protected const FIXUP_PREC_UNARY = 2; // Only operand affected by precedence
    protected const FIXUP_CALL_LHS = 3; // LHS of call
    protected const FIXUP_DEREF_LHS = 4; // LHS of dereferencing operation
    protected const FIXUP_STATIC_DEREF_LHS = 5; // LHS of static dereferencing operation
    protected const FIXUP_BRACED_NAME  = 6; // Name operand that may require bracing
    protected const FIXUP_VAR_BRACED_NAME = 7; // Name operand that may require ${} bracing
    protected const FIXUP_ENCAPSED = 8; // Encapsed string part
    protected const FIXUP_NEW = 9; // New/instanceof operand

    protected const MAX_PRECEDENCE = 1000;

    /** @var array<class-string, array{int, int, int}> */
    protected array $precedenceMap = [
        // [precedence, precedenceLHS, precedenceRHS]
        // Where the latter two are the precedences to use for the LHS and RHS of a binary operator,
        // where 1 is added to one of the sides depending on associativity. This information is not
        // used for unary operators and set to -1.
        Expr\Clone_::class             => [-10,   0,   1],
        BinaryOp\Pow::class            => [  0,   0,   1],
        Expr\BitwiseNot::class         => [ 10,  -1,  -1],
        Expr\UnaryPlus::class          => [ 10,  -1,  -1],
        Expr\UnaryMinus::class         => [ 10,  -1,  -1],
        Cast\Int_::class               => [ 10,  -1,  -1],
        Cast\Double::class             => [ 10,  -1,  -1],
        Cast\String_::class            => [ 10,  -1,  -1],
        Cast\Array_::class             => [ 10,  -1,  -1],
        Cast\Object_::class            => [ 10,  -1,  -1],
        Cast\Bool_::class              => [ 10,  -1,  -1],
        Cast\Unset_::class             => [ 10,  -1,  -1],
        Expr\ErrorSuppress::class      => [ 10,  -1,  -1],
        Expr\Instanceof_::class        => [ 20,  -1,  -1],
        Expr\BooleanNot::class         => [ 30,  -1,  -1],
        BinaryOp\Mul::class            => [ 40,  41,  40],
        BinaryOp\Div::class            => [ 40,  41,  40],
        BinaryOp\Mod::class            => [ 40,  41,  40],
        BinaryOp\Plus::class           => [ 50,  51,  50],
        BinaryOp\Minus::class          => [ 50,  51,  50],
        // FIXME: This precedence is incorrect for PHP 8.
        BinaryOp\Concat::class         => [ 50,  51,  50],
        BinaryOp\ShiftLeft::class      => [ 60,  61,  60],
        BinaryOp\ShiftRight::class     => [ 60,  61,  60],
        BinaryOp\Pipe::class           => [ 65,  66,  65],
        BinaryOp\Smaller::class        => [ 70,  70,  70],
        BinaryOp\SmallerOrEqual::class => [ 70,  70,  70],
        BinaryOp\Greater::class        => [ 70,  70,  70],
        BinaryOp\GreaterOrEqual::class => [ 70,  70,  70],
        BinaryOp\Equal::class          => [ 80,  80,  80],
        BinaryOp\NotEqual::class       => [ 80,  80,  80],
        BinaryOp\Identical::class      => [ 80,  80,  80],
        BinaryOp\NotIdentical::class   => [ 80,  80,  80],
        BinaryOp\Spaceship::class      => [ 80,  80,  80],
        BinaryOp\BitwiseAnd::class     => [ 90,  91,  90],
        BinaryOp\BitwiseXor::class     => [100, 101, 100],
        BinaryOp\BitwiseOr::class      => [110, 111, 110],
        BinaryOp\BooleanAnd::class     => [120, 121, 120],
        BinaryOp\BooleanOr::class      => [130, 131, 130],
        BinaryOp\Coalesce::class       => [140, 140, 141],
        Expr\Ternary::class            => [150, 150, 150],
        Expr\Assign::class             => [160,  -1,  -1],
        Expr\AssignRef::class          => [160,  -1,  -1],
        AssignOp\Plus::class           => [160,  -1,  -1],
        AssignOp\Minus::class          => [160,  -1,  -1],
        AssignOp\Mul::class            => [160,  -1,  -1],
        AssignOp\Div::class            => [160,  -1,  -1],
        AssignOp\Concat::class         => [160,  -1,  -1],
        AssignOp\Mod::class            => [160,  -1,  -1],
        AssignOp\BitwiseAnd::class     => [160,  -1,  -1],
        AssignOp\BitwiseOr::class      => [160,  -1,  -1],
        AssignOp\BitwiseXor::class     => [160,  -1,  -1],
        AssignOp\ShiftLeft::class      => [160,  -1,  -1],
        AssignOp\ShiftRight::class     => [160,  -1,  -1],
        AssignOp\Pow::class            => [160,  -1,  -1],
        AssignOp\Coalesce::class       => [160,  -1,  -1],
        Expr\YieldFrom::class          => [170,  -1,  -1],
        Expr\Yield_::class             => [175,  -1,  -1],
        Expr\Print_::class             => [180,  -1,  -1],
        BinaryOp\LogicalAnd::class     => [190, 191, 190],
        BinaryOp\LogicalXor::class     => [200, 201, 200],
        BinaryOp\LogicalOr::class      => [210, 211, 210],
        Expr\Include_::class           => [220,  -1,  -1],
        Expr\ArrowFunction::class      => [230,  -1,  -1],
        Expr\Throw_::class             => [240,  -1,  -1],
        Expr\Cast\Void_::class         => [250,  -1,  -1],
    ];

    /** @var int Current indentation level. */
    protected int $indentLevel;
    /** @var string String for single level of indentation */
    private string $indent;
    /** @var int Width in spaces to indent by. */
    private int $indentWidth;
    /** @var bool Whether to use tab indentation. */
    private bool $useTabs;
    /** @var int Width in spaces of one tab. */
    private int $tabWidth = 4;

    /** @var string Newline style. Does not include current indentation. */
    protected string $newline;
    /** @var string Newline including current indentation. */
    protected string $nl;
    /** @var string|null Token placed at end of doc string to ensure it is followed by a newline.
     *                   Null if flexible doc strings are used. */
    protected ?string $docStringEndToken;
    /** @var bool Whether semicolon namespaces can be used (i.e. no global namespace is used) */
    protected bool $canUseSemicolonNamespaces;
    /** @var bool Whether to use short array syntax if the node specifies no preference */
    protected bool $shortArraySyntax;
    /** @var PhpVersion PHP version to target */
    protected PhpVersion $phpVersion;

    /** @var TokenStream|null Original tokens for use in format-preserving pretty print */
    protected ?TokenStream $origTokens;
    /** @var Internal\Differ<Node> Differ for node lists */
    protected Differ $nodeListDiffer;
    /** @var array<string, bool> Map determining whether a certain character is a label character */
    protected array $labelCharMap;
    /**
     * @var array<string, array<string, int>> Map from token classes and subnode names to FIXUP_* constants.
     *                                        This is used during format-preserving prints to place additional parens/braces if necessary.
     */
    protected array $fixupMap;
    /**
     * @var array<string, array{left?: int|string, right?: int|string}> Map from "{$node->getType()}->{$subNode}"
     *                                                                  to ['left' => $l, 'right' => $r], where $l and $r specify the token type that needs to be stripped
     *                                                                  when removing this node.
     */
    protected array $removalMap;
    /**
     * @var array<string, array{int|string|null, bool, string|null, string|null}> Map from
     *                                                                            "{$node->getType()}->{$subNode}" to [$find, $beforeToken, $extraLeft, $extraRight].
     *                                                                            $find is an optional token after which the insertion occurs. $extraLeft/Right
     *                                                                            are optionally added before/after the main insertions.
     */
    protected array $insertionMap;
    /**
     * @var array<string, string> Map From "{$class}->{$subNode}" to string that should be inserted
     *                            between elements of this list subnode.
     */
    protected array $listInsertionMap;

    /**
     * @var array<string, array{int|string|null, string, string}>
     */
    protected array $emptyListInsertionMap;
    /** @var array<string, array{string, int, int}>
     *       Map from "{$class}->{$subNode}" to [$printFn, $skipToken, $findToken] where $printFn is the function to
     *       print the modifiers, $skipToken is the token to skip at the start and $findToken is the token before which
     *       the modifiers should be reprinted. */
    protected array $modifierChangeMap;

    /**
     * Creates a pretty printer instance using the given options.
     *
     * Supported options:
     *  * PhpVersion $phpVersion: The PHP version to target (default to PHP 7.4). This option
     *                            controls compatibility of the generated code with older PHP
     *                            versions in cases where a simple stylistic choice exists (e.g.
     *                            array() vs []). It is safe to pretty-print an AST for a newer
     *                            PHP version while specifying an older target (but the result will
     *                            of course not be compatible with the older version in that case).
     *  * string $newline:        The newline style to use. Should be "\n" (default) or "\r\n".
     *  * string $indent:         The indentation to use. Should either be all spaces or a single
     *                            tab. Defaults to four spaces ("    ").
     *  * bool $shortArraySyntax: Whether to use [] instead of array() as the default array
     *                            syntax, if the node does not specify a format. Defaults to whether
     *                            the phpVersion support short array syntax.
     *
     * @param array{
     *     phpVersion?: PhpVersion, newline?: string, indent?: string, shortArraySyntax?: bool
     * } $options Dictionary of formatting options
     */
    public function __construct(array $options = []) {
        $this->phpVersion = $options['phpVersion'] ?? PhpVersion::fromComponents(7, 4);

        $this->newline = $options['newline'] ?? "\n";
        if ($this->newline !== "\n" && $this->newline != "\r\n") {
            throw new \LogicException('Option "newline" must be one of "\n" or "\r\n"');
        }

        $this->shortArraySyntax =
            $options['shortArraySyntax'] ?? $this->phpVersion->supportsShortArraySyntax();
        $this->docStringEndToken =
            $this->phpVersion->supportsFlexibleHeredoc() ? null : '_DOC_STRING_END_' . mt_rand();

        $this->indent = $indent = $options['indent'] ?? '    ';
        if ($indent === "\t") {
            $this->useTabs = true;
            $this->indentWidth = $this->tabWidth;
        } elseif ($indent === \str_repeat(' ', \strlen($indent))) {
            $this->useTabs = false;
            $this->indentWidth = \strlen($indent);
        } else {
            throw new \LogicException('Option "indent" must either be all spaces or a single tab');
        }
    }

    /**
     * Reset pretty printing state.
     */
    protected function resetState(): void {
        $this->indentLevel = 0;
        $this->nl = $this->newline;
        $this->origTokens = null;
    }

    /**
     * Set indentation level
     *
     * @param int $level Level in number of spaces
     */
    protected function setIndentLevel(int $level): void {
        $this->indentLevel = $level;
        if ($this->useTabs) {
            $tabs = \intdiv($level, $this->tabWidth);
            $spaces = $level % $this->tabWidth;
            $this->nl = $this->newline . \str_repeat("\t", $tabs) . \str_repeat(' ', $spaces);
        } else {
            $this->nl = $this->newline . \str_repeat(' ', $level);
        }
    }

    /**
     * Increase indentation level.
     */
    protected function indent(): void {
        $this->indentLevel += $this->indentWidth;
        $this->nl .= $this->indent;
    }

    /**
     * Decrease indentation level.
     */
    protected function outdent(): void {
        assert($this->indentLevel >= $this->indentWidth);
        $this->setIndentLevel($this->indentLevel - $this->indentWidth);
    }

    /**
     * Pretty prints an array of statements.
     *
     * @param Node[] $stmts Array of statements
     *
     * @return string Pretty printed statements
     */
    public function prettyPrint(array $stmts): string {
        $this->resetState();
        $this->preprocessNodes($stmts);

        return ltrim($this->handleMagicTokens($this->pStmts($stmts, false)));
    }

    /**
     * Pretty prints an expression.
     *
     * @param Expr $node Expression node
     *
     * @return string Pretty printed node
     */
    public function prettyPrintExpr(Expr $node): string {
        $this->resetState();
        return $this->handleMagicTokens($this->p($node));
    }

    /**
     * Pretty prints a file of statements (includes the opening <?php tag if it is required).
     *
     * @param Node[] $stmts Array of statements
     *
     * @return string Pretty printed statements
     */
    public function prettyPrintFile(array $stmts): string {
        if (!$stmts) {
            return "<?php" . $this->newline . $this->newline;
        }

        $p = "<?php" . $this->newline . $this->newline . $this->prettyPrint($stmts);

        if ($stmts[0] instanceof Stmt\InlineHTML) {
            $p = preg_replace('/^<\?php\s+\?>\r?\n?/', '', $p);
        }
        if ($stmts[count($stmts) - 1] instanceof Stmt\InlineHTML) {
            $p = preg_replace('/<\?php$/', '', rtrim($p));
        }

        return $p;
    }

    /**
     * Preprocesses the top-level nodes to initialize pretty printer state.
     *
     * @param Node[] $nodes Array of nodes
     */
    protected function preprocessNodes(array $nodes): void {
        /* We can use semicolon-namespaces unless there is a global namespace declaration */
        $this->canUseSemicolonNamespaces = true;
        foreach ($nodes as $node) {
            if ($node instanceof Stmt\Namespace_ && null === $node->name) {
                $this->canUseSemicolonNamespaces = false;
                break;
            }
        }
    }

    /**
     * Handles (and removes) doc-string-end tokens.
     */
    protected function handleMagicTokens(string $str): string {
        if ($this->docStringEndToken !== null) {
            // Replace doc-string-end tokens with nothing or a newline
            $str = str_replace(
                $this->docStringEndToken . ';' . $this->newline,
                ';' . $this->newline,
                $str);
            $str = str_replace($this->docStringEndToken, $this->newline, $str);
        }

        return $str;
    }

    /**
     * Pretty prints an array of nodes (statements) and indents them optionally.
     *
     * @param Node[] $nodes Array of nodes
     * @param bool $indent Whether to indent the printed nodes
     *
     * @return string Pretty printed statements
     */
    protected function pStmts(array $nodes, bool $indent = true): string {
        if ($indent) {
            $this->indent();
        }

        $result = '';
        foreach ($nodes as $node) {
            $comments = $node->getComments();
            if ($comments) {
                $result .= $this->nl . $this->pComments($comments);
                if ($node instanceof Stmt\Nop) {
                    continue;
                }
            }

            $result .= $this->nl . $this->p($node);
        }

        if ($indent) {
            $this->outdent();
        }

        return $result;
    }

    /**
     * Pretty-print an infix operation while taking precedence into account.
     *
     * @param string $class Node class of operator
     * @param Node $leftNode Left-hand side node
     * @param string $operatorString String representation of the operator
     * @param Node $rightNode Right-hand side node
     * @param int $precedence Precedence of parent operator
     * @param int $lhsPrecedence Precedence for unary operator on LHS of binary operator
     *
     * @return string Pretty printed infix operation
     */
    protected function pInfixOp(
        string $class, Node $leftNode, string $operatorString, Node $rightNode,
        int $precedence, int $lhsPrecedence
    ): string {
        list($opPrecedence, $newPrecedenceLHS, $newPrecedenceRHS) = $this->precedenceMap[$class];
        $prefix = '';
        $suffix = '';
        if ($opPrecedence >= $precedence) {
            $prefix = '(';
            $suffix = ')';
            $lhsPrecedence = self::MAX_PRECEDENCE;
        }
        return $prefix . $this->p($leftNode, $newPrecedenceLHS, $newPrecedenceLHS)
            . $operatorString . $this->p($rightNode, $newPrecedenceRHS, $lhsPrecedence) . $suffix;
    }

    /**
     * Pretty-print a prefix operation while taking precedence into account.
     *
     * @param string $class Node class of operator
     * @param string $operatorString String representation of the operator
     * @param Node $node Node
     * @param int $precedence Precedence of parent operator
     * @param int $lhsPrecedence Precedence for unary operator on LHS of binary operator
     *
     * @return string Pretty printed prefix operation
     */
    protected function pPrefixOp(string $class, string $operatorString, Node $node, int $precedence, int $lhsPrecedence): string {
        $opPrecedence = $this->precedenceMap[$class][0];
        $prefix = '';
        $suffix = '';
        if ($opPrecedence >= $lhsPrecedence) {
            $prefix = '(';
            $suffix = ')';
            $lhsPrecedence = self::MAX_PRECEDENCE;
        }
        $printedArg = $this->p($node, $opPrecedence, $lhsPrecedence);
        if (($operatorString === '+' && $printedArg[0] === '+') ||
            ($operatorString === '-' && $printedArg[0] === '-')
        ) {
            // Avoid printing +(+$a) as ++$a and similar.
            $printedArg = '(' . $printedArg . ')';
        }
        return $prefix . $operatorString . $printedArg . $suffix;
    }

    /**
     * Pretty-print a postfix operation while taking precedence into account.
     *
     * @param string $class Node class of operator
     * @param string $operatorString String representation of the operator
     * @param Node $node Node
     * @param int $precedence Precedence of parent operator
     * @param int $lhsPrecedence Precedence for unary operator on LHS of binary operator
     *
     * @return string Pretty printed postfix operation
     */
    protected function pPostfixOp(string $class, Node $node, string $operatorString, int $precedence, int $lhsPrecedence): string {
        $opPrecedence = $this->precedenceMap[$class][0];
        $prefix = '';
        $suffix = '';
        if ($opPrecedence >= $precedence) {
            $prefix = '(';
            $suffix = ')';
            $lhsPrecedence = self::MAX_PRECEDENCE;
        }
        if ($opPrecedence < $lhsPrecedence) {
            $lhsPrecedence = $opPrecedence;
        }
        return $prefix . $this->p($node, $opPrecedence, $lhsPrecedence) . $operatorString . $suffix;
    }

    /**
     * Pretty prints an array of nodes and implodes the printed values.
     *
     * @param Node[] $nodes Array of Nodes to be printed
     * @param string $glue Character to implode with
     *
     * @return string Imploded pretty printed nodes> $pre
     */
    protected function pImplode(array $nodes, string $glue = ''): string {
        $pNodes = [];
        foreach ($nodes as $node) {
            if (null === $node) {
                $pNodes[] = '';
            } else {
                $pNodes[] = $this->p($node);
            }
        }

        return implode($glue, $pNodes);
    }

    /**
     * Pretty prints an array of nodes and implodes the printed values with commas.
     *
     * @param Node[] $nodes Array of Nodes to be printed
     *
     * @return string Comma separated pretty printed nodes
     */
    protected function pCommaSeparated(array $nodes): string {
        return $this->pImplode($nodes, ', ');
    }

    /**
     * Pretty prints a comma-separated list of nodes in multiline style, including comments.
     *
     * The result includes a leading newline and one level of indentation (same as pStmts).
     *
     * @param Node[] $nodes Array of Nodes to be printed
     * @param bool $trailingComma Whether to use a trailing comma
     *
     * @return string Comma separated pretty printed nodes in multiline style
     */
    protected function pCommaSeparatedMultiline(array $nodes, bool $trailingComma): string {
        $this->indent();

        $result = '';
        $lastIdx = count($nodes) - 1;
        foreach ($nodes as $idx => $node) {
            if ($node !== null) {
                $comments = $node->getComments();
                if ($comments) {
                    $result .= $this->nl . $this->pComments($comments);
                }

                $result .= $this->nl . $this->p($node);
            } else {
                $result .= $this->nl;
            }
            if ($trailingComma || $idx !== $lastIdx) {
                $result .= ',';
            }
        }

        $this->outdent();
        return $result;
    }

    /**
     * Prints reformatted text of the passed comments.
     *
     * @param Comment[] $comments List of comments
     *
     * @return string Reformatted text of comments
     */
    protected function pComments(array $comments): string {
        $formattedComments = [];

        foreach ($comments as $comment) {
            $formattedComments[] = str_replace("\n", $this->nl, $comment->getReformattedText());
        }

        return implode($this->nl, $formattedComments);
    }

    /**
     * Perform a format-preserving pretty print of an AST.
     *
     * The format preservation is best effort. For some changes to the AST the formatting will not
     * be preserved (at least not locally).
     *
     * In order to use this method a number of prerequisites must be satisfied:
     *  * The startTokenPos and endTokenPos attributes in the lexer must be enabled.
     *  * The CloningVisitor must be run on the AST prior to modification.
     *  * The original tokens must be provided, using the getTokens() method on the lexer.
     *
     * @param Node[] $stmts Modified AST with links to original AST
     * @param Node[] $origStmts Original AST with token offset information
     * @param Token[] $origTokens Tokens of the original code
     */
    public function printFormatPreserving(array $stmts, array $origStmts, array $origTokens): string {
        $this->initializeNodeListDiffer();
        $this->initializeLabelCharMap();
        $this->initializeFixupMap();
        $this->initializeRemovalMap();
        $this->initializeInsertionMap();
        $this->initializeListInsertionMap();
        $this->initializeEmptyListInsertionMap();
        $this->initializeModifierChangeMap();

        $this->resetState();
        $this->origTokens = new TokenStream($origTokens, $this->tabWidth);

        $this->preprocessNodes($stmts);

        $pos = 0;
        $result = $this->pArray($stmts, $origStmts, $pos, 0, 'File', 'stmts', null);
        if (null !== $result) {
            $result .= $this->origTokens->getTokenCode($pos, count($origTokens) - 1, 0);
        } else {
            // Fallback
            // TODO Add <?php properly
            $result = "<?php" . $this->newline . $this->pStmts($stmts, false);
        }

        return $this->handleMagicTokens($result);
    }

    protected function pFallback(Node $node, int $precedence, int $lhsPrecedence): string {
        return $this->{'p' . $node->getType()}($node, $precedence, $lhsPrecedence);
    }

    /**
     * Pretty prints a node.
     *
     * This method also handles formatting preservation for nodes.
     *
     * @param Node $node Node to be pretty printed
     * @param int $precedence Precedence of parent operator
     * @param int $lhsPrecedence Precedence for unary operator on LHS of binary operator
     * @param bool $parentFormatPreserved Whether parent node has preserved formatting
     *
     * @return string Pretty printed node
     */
    protected function p(
        Node $node, int $precedence = self::MAX_PRECEDENCE, int $lhsPrecedence = self::MAX_PRECEDENCE,
        bool $parentFormatPreserved = false
    ): string {
        // No orig tokens means this is a normal pretty print without preservation of formatting
        if (!$this->origTokens) {
            return $this->{'p' . $node->getType()}($node, $precedence, $lhsPrecedence);
        }

        /** @var Node|null $origNode */
        $origNode = $node->getAttribute('origNode');
        if (null === $origNode) {
            return $this->pFallback($node, $precedence, $lhsPrecedence);
        }

        $class = \get_class($node);
        \assert($class === \get_class($origNode));

        $startPos = $origNode->getStartTokenPos();
        $endPos = $origNode->getEndTokenPos();
        \assert($startPos >= 0 && $endPos >= 0);

        $fallbackNode = $node;
        if ($node instanceof Expr\New_ && $node->class instanceof Stmt\Class_) {
            // Normalize node structure of anonymous classes
            assert($origNode instanceof Expr\New_);
            $node = PrintableNewAnonClassNode::fromNewNode($node);
            $origNode = PrintableNewAnonClassNode::fromNewNode($origNode);
            $class = PrintableNewAnonClassNode::class;
        }

        // InlineHTML node does not contain closing and opening PHP tags. If the parent formatting
        // is not preserved, then we need to use the fallback code to make sure the tags are
        // printed.
        if ($node instanceof Stmt\InlineHTML && !$parentFormatPreserved) {
            return $this->pFallback($fallbackNode, $precedence, $lhsPrecedence);
        }

        $indentAdjustment = $this->indentLevel - $this->origTokens->getIndentationBefore($startPos);

        $type = $node->getType();
        $fixupInfo = $this->fixupMap[$class] ?? null;

        $result = '';
        $pos = $startPos;
        foreach ($node->getSubNodeNames() as $subNodeName) {
            $subNode = $node->$subNodeName;
            $origSubNode = $origNode->$subNodeName;

            if ((!$subNode instanceof Node && $subNode !== null)
                || (!$origSubNode instanceof Node && $origSubNode !== null)
            ) {
                if ($subNode === $origSubNode) {
                    // Unchanged, can reuse old code
                    continue;
                }

                if (is_array($subNode) && is_array($origSubNode)) {
                    // Array subnode changed, we might be able to reconstruct it
                    $listResult = $this->pArray(
                        $subNode, $origSubNode, $pos, $indentAdjustment, $class, $subNodeName,
                        $fixupInfo[$subNodeName] ?? null
                    );
                    if (null === $listResult) {
                        return $this->pFallback($fallbackNode, $precedence, $lhsPrecedence);
                    }

                    $result .= $listResult;
                    continue;
                }

                // Check if this is a modifier change
                $key = $class . '->' . $subNodeName;
                if (!isset($this->modifierChangeMap[$key])) {
                    return $this->pFallback($fallbackNode, $precedence, $lhsPrecedence);
                }

                [$printFn, $skipToken, $findToken] = $this->modifierChangeMap[$key];
                $skipWSPos = $this->origTokens->skipRight($pos, $skipToken);
                $result .= $this->origTokens->getTokenCode($pos, $skipWSPos, $indentAdjustment);
                $result .= $this->$printFn($subNode);
                $pos = $this->origTokens->findRight($skipWSPos, $findToken);
                continue;
            }

            $extraLeft = '';
            $extraRight = '';
            if ($origSubNode !== null) {
                $subStartPos = $origSubNode->getStartTokenPos();
                $subEndPos = $origSubNode->getEndTokenPos();
                \assert($subStartPos >= 0 && $subEndPos >= 0);
            } else {
                if ($subNode === null) {
                    // Both null, nothing to do
                    continue;
                }

                // A node has been inserted, check if we have insertion information for it
                $key = $type . '->' . $subNodeName;
                if (!isset($this->insertionMap[$key])) {
                    return $this->pFallback($fallbackNode, $precedence, $lhsPrecedence);
                }

                list($findToken, $beforeToken, $extraLeft, $extraRight) = $this->insertionMap[$key];
                if (null !== $findToken) {
                    $subStartPos = $this->origTokens->findRight($pos, $findToken)
                        + (int) !$beforeToken;
                } else {
                    $subStartPos = $pos;
                }

                if (null === $extraLeft && null !== $extraRight) {
                    // If inserting on the right only, skipping whitespace looks better
                    $subStartPos = $this->origTokens->skipRightWhitespace($subStartPos);
                }
                $subEndPos = $subStartPos - 1;
            }

            if (null === $subNode) {
                // A node has been removed, check if we have removal information for it
                $key = $type . '->' . $subNodeName;
                if (!isset($this->removalMap[$key])) {
                    return $this->pFallback($fallbackNode, $precedence, $lhsPrecedence);
                }

                // Adjust positions to account for additional tokens that must be skipped
                $removalInfo = $this->removalMap[$key];
                if (isset($removalInfo['left'])) {
                    $subStartPos = $this->origTokens->skipLeft($subStartPos - 1, $removalInfo['left']) + 1;
                }
                if (isset($removalInfo['right'])) {
                    $subEndPos = $this->origTokens->skipRight($subEndPos + 1, $removalInfo['right']) - 1;
                }
            }

            $result .= $this->origTokens->getTokenCode($pos, $subStartPos, $indentAdjustment);

            if (null !== $subNode) {
                $result .= $extraLeft;

                $origIndentLevel = $this->indentLevel;
                $this->setIndentLevel(max($this->origTokens->getIndentationBefore($subStartPos) + $indentAdjustment, 0));

                // If it's the same node that was previously in this position, it certainly doesn't
                // need fixup. It's important to check this here, because our fixup checks are more
                // conservative than strictly necessary.
                if (isset($fixupInfo[$subNodeName])
                    && $subNode->getAttribute('origNode') !== $origSubNode
                ) {
                    $fixup = $fixupInfo[$subNodeName];
                    $res = $this->pFixup($fixup, $subNode, $class, $subStartPos, $subEndPos);
                } else {
                    $res = $this->p($subNode, self::MAX_PRECEDENCE, self::MAX_PRECEDENCE, true);
                }

                $this->safeAppend($result, $res);
                $this->setIndentLevel($origIndentLevel);

                $result .= $extraRight;
            }

            $pos = $subEndPos + 1;
        }

        $result .= $this->origTokens->getTokenCode($pos, $endPos + 1, $indentAdjustment);
        return $result;
    }

    /**
     * Perform a format-preserving pretty print of an array.
     *
     * @param Node[] $nodes New nodes
     * @param Node[] $origNodes Original nodes
     * @param int $pos Current token position (updated by reference)
     * @param int $indentAdjustment Adjustment for indentation
     * @param string $parentNodeClass Class of the containing node.
     * @param string $subNodeName Name of array subnode.
     * @param null|int $fixup Fixup information for array item nodes
     *
     * @return null|string Result of pretty print or null if cannot preserve formatting
     */
    protected function pArray(
        array  $nodes, array $origNodes, int &$pos, int $indentAdjustment,
        string $parentNodeClass, string $subNodeName, ?int $fixup
    ): ?string {
        $diff = $this->nodeListDiffer->diffWithReplacements($origNodes, $nodes);

        $mapKey = $parentNodeClass . '->' . $subNodeName;
        $insertStr = $this->listInsertionMap[$mapKey] ?? null;
        $isStmtList = $subNodeName === 'stmts';

        $beforeFirstKeepOrReplace = true;
        $skipRemovedNode = false;
        $delayedAdd = [];
        $lastElemIndentLevel = $this->indentLevel;

        $insertNewline = false;
        if ($insertStr === "\n") {
            $insertStr = '';
            $insertNewline = true;
        }

        if ($isStmtList && \count($origNodes) === 1 && \count($nodes) !== 1) {
            $startPos = $origNodes[0]->getStartTokenPos();
            $endPos = $origNodes[0]->getEndTokenPos();
            \assert($startPos >= 0 && $endPos >= 0);
            if (!$this->origTokens->haveBraces($startPos, $endPos)) {
                // This was a single statement without braces, but either additional statements
                // have been added, or the single statement has been removed. This requires the
                // addition of braces. For now fall back.
                // TODO: Try to preserve formatting
                return null;
            }
        }

        $result = '';
        foreach ($diff as $i => $diffElem) {
            $diffType = $diffElem->type;
            /** @var Node|string|null $arrItem */
            $arrItem = $diffElem->new;
            /** @var Node|string|null $origArrItem */
            $origArrItem = $diffElem->old;

            if ($diffType === DiffElem::TYPE_KEEP || $diffType === DiffElem::TYPE_REPLACE) {
                $beforeFirstKeepOrReplace = false;

                if ($origArrItem === null || $arrItem === null) {
                    // We can only handle the case where both are null
                    if ($origArrItem === $arrItem) {
                        continue;
                    }
                    return null;
                }

                if (!$arrItem instanceof Node || !$origArrItem instanceof Node) {
                    // We can only deal with nodes. This can occur for Names, which use string arrays.
                    return null;
                }

                $itemStartPos = $origArrItem->getStartTokenPos();
                $itemEndPos = $origArrItem->getEndTokenPos();
                \assert($itemStartPos >= 0 && $itemEndPos >= 0 && $itemStartPos >= $pos);

                $origIndentLevel = $this->indentLevel;
                $lastElemIndentLevel = max($this->origTokens->getIndentationBefore($itemStartPos) + $indentAdjustment, 0);
                $this->setIndentLevel($lastElemIndentLevel);

                $comments = $arrItem->getComments();
                $origComments = $origArrItem->getComments();
                $commentStartPos = $origComments ? $origComments[0]->getStartTokenPos() : $itemStartPos;
                \assert($commentStartPos >= 0);

                if ($commentStartPos < $pos) {
                    // Comments may be assigned to multiple nodes if they start at the same position.
                    // Make sure we don't try to print them multiple times.
                    $commentStartPos = $itemStartPos;
                }

                if ($skipRemovedNode) {
                    if ($isStmtList && $this->origTokens->haveTagInRange($pos, $itemStartPos)) {
                        // We'd remove an opening/closing PHP tag.
                        // TODO: Preserve formatting.
                        $this->setIndentLevel($origIndentLevel);
                        return null;
                    }
                } else {
                    $result .= $this->origTokens->getTokenCode(
                        $pos, $commentStartPos, $indentAdjustment);
                }

                if (!empty($delayedAdd)) {
                    /** @var Node $delayedAddNode */
                    foreach ($delayedAdd as $delayedAddNode) {
                        if ($insertNewline) {
                            $delayedAddComments = $delayedAddNode->getComments();
                            if ($delayedAddComments) {
                                $result .= $this->pComments($delayedAddComments) . $this->nl;
                            }
                        }

                        $this->safeAppend($result, $this->p($delayedAddNode, self::MAX_PRECEDENCE, self::MAX_PRECEDENCE, true));

                        if ($insertNewline) {
                            $result .= $insertStr . $this->nl;
                        } else {
                            $result .= $insertStr;
                        }
                    }

                    $delayedAdd = [];
                }

                if ($comments !== $origComments) {
                    if ($comments) {
                        $result .= $this->pComments($comments) . $this->nl;
                    }
                } else {
                    $result .= $this->origTokens->getTokenCode(
                        $commentStartPos, $itemStartPos, $indentAdjustment);
                }

                // If we had to remove anything, we have done so now.
                $skipRemovedNode = false;
            } elseif ($diffType === DiffElem::TYPE_ADD) {
                if (null === $insertStr) {
                    // We don't have insertion information for this list type
                    return null;
                }

                if (!$arrItem instanceof Node) {
                    // We only support list insertion of nodes.
                    return null;
                }

                // We go multiline if the original code was multiline,
                // or if it's an array item with a comment above it.
                // Match always uses multiline formatting.
                if ($insertStr === ', ' &&
                    ($this->isMultiline($origNodes) || $arrItem->getComments() ||
                     $parentNodeClass === Expr\Match_::class)
                ) {
                    $insertStr = ',';
                    $insertNewline = true;
                }

                if ($beforeFirstKeepOrReplace) {
                    // Will be inserted at the next "replace" or "keep" element
                    $delayedAdd[] = $arrItem;
                    continue;
                }

                $itemStartPos = $pos;
                $itemEndPos = $pos - 1;

                $origIndentLevel = $this->indentLevel;
                $this->setIndentLevel($lastElemIndentLevel);

                if ($insertNewline) {
                    $result .= $insertStr . $this->nl;
                    $comments = $arrItem->getComments();
                    if ($comments) {
                        $result .= $this->pComments($comments) . $this->nl;
                    }
                } else {
                    $result .= $insertStr;
                }
            } elseif ($diffType === DiffElem::TYPE_REMOVE) {
                if (!$origArrItem instanceof Node) {
                    // We only support removal for nodes
                    return null;
                }

                $itemStartPos = $origArrItem->getStartTokenPos();
                $itemEndPos = $origArrItem->getEndTokenPos();
                \assert($itemStartPos >= 0 && $itemEndPos >= 0);

                // Consider comments part of the node.
                $origComments = $origArrItem->getComments();
                if ($origComments) {
                    $itemStartPos = $origComments[0]->getStartTokenPos();
                }

                if ($i === 0) {
                    // If we're removing from the start, keep the tokens before the node and drop those after it,
                    // instead of the other way around.
                    $result .= $this->origTokens->getTokenCode(
                        $pos, $itemStartPos, $indentAdjustment);
                    $skipRemovedNode = true;
                } else {
                    if ($isStmtList && $this->origTokens->haveTagInRange($pos, $itemStartPos)) {
                        // We'd remove an opening/closing PHP tag.
                        // TODO: Preserve formatting.
                        return null;
                    }
                }

                $pos = $itemEndPos + 1;
                continue;
            } else {
                throw new \Exception("Shouldn't happen");
            }

            if (null !== $fixup && $arrItem->getAttribute('origNode') !== $origArrItem) {
                $res = $this->pFixup($fixup, $arrItem, null, $itemStartPos, $itemEndPos);
            } else {
                $res = $this->p($arrItem, self::MAX_PRECEDENCE, self::MAX_PRECEDENCE, true);
            }
            $this->safeAppend($result, $res);

            $this->setIndentLevel($origIndentLevel);
            $pos = $itemEndPos + 1;
        }

        if ($skipRemovedNode) {
            // TODO: Support removing single node.
            return null;
        }

        if (!empty($delayedAdd)) {
            if (!isset($this->emptyListInsertionMap[$mapKey])) {
                return null;
            }

            list($findToken, $extraLeft, $extraRight) = $this->emptyListInsertionMap[$mapKey];
            if (null !== $findToken) {
                // For anon classes skip to the class keyword.
                $isAnonClassArgs = $mapKey === PrintableNewAnonClassNode::class . '->args';
                if ($isAnonClassArgs) {
                    $insertPos = $this->origTokens->findRight($pos, \T_CLASS) + 1;
                    $result .= $this->origTokens->getTokenCode($pos, $insertPos, $indentAdjustment);
                    $pos = $insertPos;
                }

                // If "new Foo" was used without arguments, we need to convert to "new Foo()".
                if (($mapKey === Expr\New_::class . '->args' || $isAnonClassArgs) &&
                    !$this->origTokens->haveTokenImmediatelyAfter($pos - 1, '(')
                ) {
                    $extraLeft = '(';
                    $extraRight = ')';
                } else {
                    $insertPos = $this->origTokens->findRight($pos, $findToken) + 1;
                    $result .= $this->origTokens->getTokenCode($pos, $insertPos, $indentAdjustment);
                    $pos = $insertPos;
                }
            }

            $first = true;
            $result .= $extraLeft;
            foreach ($delayedAdd as $delayedAddNode) {
                if (!$first) {
                    $result .= $insertStr;
                    if ($insertNewline) {
                        $result .= $this->nl;
                    }
                }
                $result .= $this->p($delayedAddNode, self::MAX_PRECEDENCE, self::MAX_PRECEDENCE, true);
                $first = false;
            }
            $result .= $extraRight === "\n" ? $this->nl : $extraRight;
        }

        return $result;
    }

    /**
     * Print node with fixups.
     *
     * Fixups here refer to the addition of extra parentheses, braces or other characters, that
     * are required to preserve program semantics in a certain context (e.g. to maintain precedence
     * or because only certain expressions are allowed in certain places).
     *
     * @param int $fixup Fixup type
     * @param Node $subNode Subnode to print
     * @param string|null $parentClass Class of parent node
     * @param int $subStartPos Original start pos of subnode
     * @param int $subEndPos Original end pos of subnode
     *
     * @return string Result of fixed-up print of subnode
     */
    protected function pFixup(int $fixup, Node $subNode, ?string $parentClass, int $subStartPos, int $subEndPos): string {
        switch ($fixup) {
            case self::FIXUP_PREC_LEFT:
                // We use a conservative approximation where lhsPrecedence == precedence.
                if (!$this->origTokens->haveParens($subStartPos, $subEndPos)) {
                    $precedence = $this->precedenceMap[$parentClass][1];
                    return $this->p($subNode, $precedence, $precedence);
                }
                break;
            case self::FIXUP_PREC_RIGHT:
                if (!$this->origTokens->haveParens($subStartPos, $subEndPos)) {
                    $precedence = $this->precedenceMap[$parentClass][2];
                    return $this->p($subNode, $precedence, $precedence);
                }
                break;
            case self::FIXUP_PREC_UNARY:
                if (!$this->origTokens->haveParens($subStartPos, $subEndPos)) {
                    $precedence = $this->precedenceMap[$parentClass][0];
                    return $this->p($subNode, $precedence, $precedence);
                }
                break;
            case self::FIXUP_CALL_LHS:
                if ($this->callLhsRequiresParens($subNode)
                    && !$this->origTokens->haveParens($subStartPos, $subEndPos)
                ) {
                    return '(' . $this->p($subNode) . ')';
                }
                break;
            case self::FIXUP_DEREF_LHS:
                if ($this->dereferenceLhsRequiresParens($subNode)
                    && !$this->origTokens->haveParens($subStartPos, $subEndPos)
                ) {
                    return '(' . $this->p($subNode) . ')';
                }
                break;
            case self::FIXUP_STATIC_DEREF_LHS:
                if ($this->staticDereferenceLhsRequiresParens($subNode)
                    && !$this->origTokens->haveParens($subStartPos, $subEndPos)
                ) {
                    return '(' . $this->p($subNode) . ')';
                }
                break;
            case self::FIXUP_NEW:
                if ($this->newOperandRequiresParens($subNode)
                    && !$this->origTokens->haveParens($subStartPos, $subEndPos)) {
                    return '(' . $this->p($subNode) . ')';
                }
                break;
            case self::FIXUP_BRACED_NAME:
            case self::FIXUP_VAR_BRACED_NAME:
                if ($subNode instanceof Expr
                    && !$this->origTokens->haveBraces($subStartPos, $subEndPos)
                ) {
                    return ($fixup === self::FIXUP_VAR_BRACED_NAME ? '$' : '')
                        . '{' . $this->p($subNode) . '}';
                }
                break;
            case self::FIXUP_ENCAPSED:
                if (!$subNode instanceof Node\InterpolatedStringPart
                    && !$this->origTokens->haveBraces($subStartPos, $subEndPos)
                ) {
                    return '{' . $this->p($subNode) . '}';
                }
                break;
            default:
                throw new \Exception('Cannot happen');
        }

        // Nothing special to do
        return $this->p($subNode);
    }

    /**
     * Appends to a string, ensuring whitespace between label characters.
     *
     * Example: "echo" and "$x" result in "echo$x", but "echo" and "x" result in "echo x".
     * Without safeAppend the result would be "echox", which does not preserve semantics.
     */
    protected function safeAppend(string &$str, string $append): void {
        if ($str === "") {
            $str = $append;
            return;
        }

        if ($append === "") {
            return;
        }

        if (!$this->labelCharMap[$append[0]]
                || !$this->labelCharMap[$str[\strlen($str) - 1]]) {
            $str .= $append;
        } else {
            $str .= " " . $append;
        }
    }

    /**
     * Determines whether the LHS of a call must be wrapped in parenthesis.
     *
     * @param Node $node LHS of a call
     *
     * @return bool Whether parentheses are required
     */
    protected function callLhsRequiresParens(Node $node): bool {
        if ($node instanceof Expr\New_) {
            return !$this->phpVersion->supportsNewDereferenceWithoutParentheses();
        }
        return !($node instanceof Node\Name
            || $node instanceof Expr\Variable
            || $node instanceof Expr\ArrayDimFetch
            || $node instanceof Expr\FuncCall
            || $node instanceof Expr\MethodCall
            || $node instanceof Expr\NullsafeMethodCall
            || $node instanceof Expr\StaticCall
            || $node instanceof Expr\Array_);
    }

    /**
     * Determines whether the LHS of an array/object operation must be wrapped in parentheses.
     *
     * @param Node $node LHS of dereferencing operation
     *
     * @return bool Whether parentheses are required
     */
    protected function dereferenceLhsRequiresParens(Node $node): bool {
        // A constant can occur on the LHS of an array/object deref, but not a static deref.
        return $this->staticDereferenceLhsRequiresParens($node)
            && !$node instanceof Expr\ConstFetch;
    }

    /**
     * Determines whether the LHS of a static operation must be wrapped in parentheses.
     *
     * @param Node $node LHS of dereferencing operation
     *
     * @return bool Whether parentheses are required
     */
    protected function staticDereferenceLhsRequiresParens(Node $node): bool {
        if ($node instanceof Expr\New_) {
            return !$this->phpVersion->supportsNewDereferenceWithoutParentheses();
        }
        return !($node instanceof Expr\Variable
            || $node instanceof Node\Name
            || $node instanceof Expr\ArrayDimFetch
            || $node instanceof Expr\PropertyFetch
            || $node instanceof Expr\NullsafePropertyFetch
            || $node instanceof Expr\StaticPropertyFetch
            || $node instanceof Expr\FuncCall
            || $node instanceof Expr\MethodCall
            || $node instanceof Expr\NullsafeMethodCall
            || $node instanceof Expr\StaticCall
            || $node instanceof Expr\Array_
            || $node instanceof Scalar\String_
            || $node instanceof Expr\ClassConstFetch);
    }

    /**
     * Determines whether an expression used in "new" or "instanceof" requires parentheses.
     *
     * @param Node $node New or instanceof operand
     *
     * @return bool Whether parentheses are required
     */
    protected function newOperandRequiresParens(Node $node): bool {
        while (true) {
            if ($node instanceof Node\Name || $node instanceof Expr\Variable) {
                return false;
            }
            if ($node instanceof Expr\ArrayDimFetch || $node instanceof Expr\PropertyFetch ||
                $node instanceof Expr\NullsafePropertyFetch
            ) {
                $node = $node->var;
                continue;
            }
            if ($node instanceof Expr\StaticPropertyFetch) {
                $node = $node->class;
                continue;
            }
            return true;
        }
    }

    /**
     * Print modifiers, including trailing whitespace.
     *
     * @param int $modifiers Modifier mask to print
     *
     * @return string Printed modifiers
     */
    protected function pModifiers(int $modifiers): string {
        return ($modifiers & Modifiers::FINAL ? 'final ' : '')
             . ($modifiers & Modifiers::ABSTRACT ? 'abstract ' : '')
             . ($modifiers & Modifiers::PUBLIC ? 'public ' : '')
             . ($modifiers & Modifiers::PROTECTED ? 'protected ' : '')
             . ($modifiers & Modifiers::PRIVATE ? 'private ' : '')
             . ($modifiers & Modifiers::PUBLIC_SET ? 'public(set) ' : '')
             . ($modifiers & Modifiers::PROTECTED_SET ? 'protected(set) ' : '')
             . ($modifiers & Modifiers::PRIVATE_SET ? 'private(set) ' : '')
             . ($modifiers & Modifiers::STATIC ? 'static ' : '')
             . ($modifiers & Modifiers::READONLY ? 'readonly ' : '');
    }

    protected function pStatic(bool $static): string {
        return $static ? 'static ' : '';
    }

    /**
     * Determine whether a list of nodes uses multiline formatting.
     *
     * @param (Node|null)[] $nodes Node list
     *
     * @return bool Whether multiline formatting is used
     */
    protected function isMultiline(array $nodes): bool {
        if (\count($nodes) < 2) {
            return false;
        }

        $pos = -1;
        foreach ($nodes as $node) {
            if (null === $node) {
                continue;
            }

            $endPos = $node->getEndTokenPos() + 1;
            if ($pos >= 0) {
                $text = $this->origTokens->getTokenCode($pos, $endPos, 0);
                if (false === strpos($text, "\n")) {
                    // We require that a newline is present between *every* item. If the formatting
                    // is inconsistent, with only some items having newlines, we don't consider it
                    // as multiline
                    return false;
                }
            }
            $pos = $endPos;
        }

        return true;
    }

    /**
     * Lazily initializes label char map.
     *
     * The label char map determines whether a certain character may occur in a label.
     */
    protected function initializeLabelCharMap(): void {
        if (isset($this->labelCharMap)) {
            return;
        }

        $this->labelCharMap = [];
        for ($i = 0; $i < 256; $i++) {
            $chr = chr($i);
            $this->labelCharMap[$chr] = (bool) preg_match('/^[a-zA-Z0-9_\x80-\xff]$/', $chr);
        }

        if ($this->phpVersion->allowsDelInIdentifiers()) {
            $this->labelCharMap["\x7f"] = true;
        }
    }

    /**
     * Lazily initializes node list differ.
     *
     * The node list differ is used to determine differences between two array subnodes.
     */
    protected function initializeNodeListDiffer(): void {
        if (isset($this->nodeListDiffer)) {
            return;
        }

        $this->nodeListDiffer = new Internal\Differ(function ($a, $b) {
            if ($a instanceof Node && $b instanceof Node) {
                return $a === $b->getAttribute('origNode');
            }
            // Can happen for array destructuring
            return $a === null && $b === null;
        });
    }

    /**
     * Lazily initializes fixup map.
     *
     * The fixup map is used to determine whether a certain subnode of a certain node may require
     * some kind of "fixup" operation, e.g. the addition of parenthesis or braces.
     */
    protected function initializeFixupMap(): void {
        if (isset($this->fixupMap)) {
            return;
        }

        $this->fixupMap = [
            Expr\Instanceof_::class => [
                'expr' => self::FIXUP_PREC_UNARY,
                'class' => self::FIXUP_NEW,
            ],
            Expr\Ternary::class => [
                'cond' => self::FIXUP_PREC_LEFT,
                'else' => self::FIXUP_PREC_RIGHT,
            ],
            Expr\Yield_::class => ['value' => self::FIXUP_PREC_UNARY],

            Expr\FuncCall::class => ['name' => self::FIXUP_CALL_LHS],
            Expr\StaticCall::class => ['class' => self::FIXUP_STATIC_DEREF_LHS],
            Expr\ArrayDimFetch::class => ['var' => self::FIXUP_DEREF_LHS],
            Expr\ClassConstFetch::class => [
                'class' => self::FIXUP_STATIC_DEREF_LHS,
                'name' => self::FIXUP_BRACED_NAME,
            ],
            Expr\New_::class => ['class' => self::FIXUP_NEW],
            Expr\MethodCall::class => [
                'var' => self::FIXUP_DEREF_LHS,
                'name' => self::FIXUP_BRACED_NAME,
            ],
            Expr\NullsafeMethodCall::class => [
                'var' => self::FIXUP_DEREF_LHS,
                'name' => self::FIXUP_BRACED_NAME,
            ],
            Expr\StaticPropertyFetch::class => [
                'class' => self::FIXUP_STATIC_DEREF_LHS,
                'name' => self::FIXUP_VAR_BRACED_NAME,
            ],
            Expr\PropertyFetch::class => [
                'var' => self::FIXUP_DEREF_LHS,
                'name' => self::FIXUP_BRACED_NAME,
            ],
            Expr\NullsafePropertyFetch::class => [
                'var' => self::FIXUP_DEREF_LHS,
                'name' => self::FIXUP_BRACED_NAME,
            ],
            Scalar\InterpolatedString::class => [
                'parts' => self::FIXUP_ENCAPSED,
            ],
        ];

        $binaryOps = [
            BinaryOp\Pow::class, BinaryOp\Mul::class, BinaryOp\Div::class, BinaryOp\Mod::class,
            BinaryOp\Plus::class, BinaryOp\Minus::class, BinaryOp\Concat::class,
            BinaryOp\ShiftLeft::class, BinaryOp\ShiftRight::class, BinaryOp\Smaller::class,
            BinaryOp\SmallerOrEqual::class, BinaryOp\Greater::class, BinaryOp\GreaterOrEqual::class,
            BinaryOp\Equal::class, BinaryOp\NotEqual::class, BinaryOp\Identical::class,
            BinaryOp\NotIdentical::class, BinaryOp\Spaceship::class, BinaryOp\BitwiseAnd::class,
            BinaryOp\BitwiseXor::class, BinaryOp\BitwiseOr::class, BinaryOp\BooleanAnd::class,
            BinaryOp\BooleanOr::class, BinaryOp\Coalesce::class, BinaryOp\LogicalAnd::class,
            BinaryOp\LogicalXor::class, BinaryOp\LogicalOr::class, BinaryOp\Pipe::class,
        ];
        foreach ($binaryOps as $binaryOp) {
            $this->fixupMap[$binaryOp] = [
                'left' => self::FIXUP_PREC_LEFT,
                'right' => self::FIXUP_PREC_RIGHT
            ];
        }

        $prefixOps = [
            Expr\Clone_::class, Expr\BitwiseNot::class, Expr\BooleanNot::class, Expr\UnaryPlus::class, Expr\UnaryMinus::class,
            Cast\Int_::class, Cast\Double::class, Cast\String_::class, Cast\Array_::class,
            Cast\Object_::class, Cast\Bool_::class, Cast\Unset_::class, Expr\ErrorSuppress::class,
            Expr\YieldFrom::class, Expr\Print_::class, Expr\Include_::class,
            Expr\Assign::class, Expr\AssignRef::class, AssignOp\Plus::class, AssignOp\Minus::class,
            AssignOp\Mul::class, AssignOp\Div::class, AssignOp\Concat::class, AssignOp\Mod::class,
            AssignOp\BitwiseAnd::class, AssignOp\BitwiseOr::class, AssignOp\BitwiseXor::class,
            AssignOp\ShiftLeft::class, AssignOp\ShiftRight::class, AssignOp\Pow::class, AssignOp\Coalesce::class,
            Expr\ArrowFunction::class, Expr\Throw_::class,
        ];
        foreach ($prefixOps as $prefixOp) {
            $this->fixupMap[$prefixOp] = ['expr' => self::FIXUP_PREC_UNARY];
        }
    }

    /**
     * Lazily initializes the removal map.
     *
     * The removal map is used to determine which additional tokens should be removed when a
     * certain node is replaced by null.
     */
    protected function initializeRemovalMap(): void {
        if (isset($this->removalMap)) {
            return;
        }

        $stripBoth = ['left' => \T_WHITESPACE, 'right' => \T_WHITESPACE];
        $stripLeft = ['left' => \T_WHITESPACE];
        $stripRight = ['right' => \T_WHITESPACE];
        $stripDoubleArrow = ['right' => \T_DOUBLE_ARROW];
        $stripColon = ['left' => ':'];
        $stripEquals = ['left' => '='];
        $this->removalMap = [
            'Expr_ArrayDimFetch->dim' => $stripBoth,
            'ArrayItem->key' => $stripDoubleArrow,
            'Expr_ArrowFunction->returnType' => $stripColon,
            'Expr_Closure->returnType' => $stripColon,
            'Expr_Exit->expr' => $stripBoth,
            'Expr_Ternary->if' => $stripBoth,
            'Expr_Yield->key' => $stripDoubleArrow,
            'Expr_Yield->value' => $stripBoth,
            'Param->type' => $stripRight,
            'Param->default' => $stripEquals,
            'Stmt_Break->num' => $stripBoth,
            'Stmt_Catch->var' => $stripLeft,
            'Stmt_ClassConst->type' => $stripRight,
            'Stmt_ClassMethod->returnType' => $stripColon,
            'Stmt_Class->extends' => ['left' => \T_EXTENDS],
            'Stmt_Enum->scalarType' => $stripColon,
            'Stmt_EnumCase->expr' => $stripEquals,
            'Expr_PrintableNewAnonClass->extends' => ['left' => \T_EXTENDS],
            'Stmt_Continue->num' => $stripBoth,
            'Stmt_Foreach->keyVar' => $stripDoubleArrow,
            'Stmt_Function->returnType' => $stripColon,
            'Stmt_If->else' => $stripLeft,
            'Stmt_Namespace->name' => $stripLeft,
            'Stmt_Property->type' => $stripRight,
            'PropertyItem->default' => $stripEquals,
            'Stmt_Return->expr' => $stripBoth,
            'Stmt_StaticVar->default' => $stripEquals,
            'Stmt_TraitUseAdaptation_Alias->newName' => $stripLeft,
            'Stmt_TryCatch->finally' => $stripLeft,
            // 'Stmt_Case->cond': Replace with "default"
            // 'Stmt_Class->name': Unclear what to do
            // 'Stmt_Declare->stmts': Not a plain node
            // 'Stmt_TraitUseAdaptation_Alias->newModifier': Not a plain node
        ];
    }

    protected function initializeInsertionMap(): void {
        if (isset($this->insertionMap)) {
            return;
        }

        // TODO: "yield" where both key and value are inserted doesn't work
        // [$find, $beforeToken, $extraLeft, $extraRight]
        $this->insertionMap = [
            'Expr_ArrayDimFetch->dim' => ['[', false, null, null],
            'ArrayItem->key' => [null, false, null, ' => '],
            'Expr_ArrowFunction->returnType' => [')', false, ': ', null],
            'Expr_Closure->returnType' => [')', false, ': ', null],
            'Expr_Ternary->if' => ['?', false, ' ', ' '],
            'Expr_Yield->key' => [\T_YIELD, false, null, ' => '],
            'Expr_Yield->value' => [\T_YIELD, false, ' ', null],
            'Param->type' => [null, false, null, ' '],
            'Param->default' => [null, false, ' = ', null],
            'Stmt_Break->num' => [\T_BREAK, false, ' ', null],
            'Stmt_Catch->var' => [null, false, ' ', null],
            'Stmt_ClassMethod->returnType' => [')', false, ': ', null],
            'Stmt_ClassConst->type' => [\T_CONST, false, ' ', null],
            'Stmt_Class->extends' => [null, false, ' extends ', null],
            'Stmt_Enum->scalarType' => [null, false, ' : ', null],
            'Stmt_EnumCase->expr' => [null, false, ' = ', null],
            'Expr_PrintableNewAnonClass->extends' => [null, false, ' extends ', null],
            'Stmt_Continue->num' => [\T_CONTINUE, false, ' ', null],
            'Stmt_Foreach->keyVar' => [\T_AS, false, null, ' => '],
            'Stmt_Function->returnType' => [')', false, ': ', null],
            'Stmt_If->else' => [null, false, ' ', null],
            'Stmt_Namespace->name' => [\T_NAMESPACE, false, ' ', null],
            'Stmt_Property->type' => [\T_VARIABLE, true, null, ' '],
            'PropertyItem->default' => [null, false, ' = ', null],
            'Stmt_Return->expr' => [\T_RETURN, false, ' ', null],
            'Stmt_StaticVar->default' => [null, false, ' = ', null],
            //'Stmt_TraitUseAdaptation_Alias->newName' => [T_AS, false, ' ', null], // TODO
            'Stmt_TryCatch->finally' => [null, false, ' ', null],

            // 'Expr_Exit->expr': Complicated due to optional ()
            // 'Stmt_Case->cond': Conversion from default to case
            // 'Stmt_Class->name': Unclear
            // 'Stmt_Declare->stmts': Not a proper node
            // 'Stmt_TraitUseAdaptation_Alias->newModifier': Not a proper node
        ];
    }

    protected function initializeListInsertionMap(): void {
        if (isset($this->listInsertionMap)) {
            return;
        }

        $this->listInsertionMap = [
            // special
            //'Expr_ShellExec->parts' => '', // TODO These need to be treated more carefully
            //'Scalar_InterpolatedString->parts' => '',
            Stmt\Catch_::class . '->types' => '|',
            UnionType::class . '->types' => '|',
            IntersectionType::class . '->types' => '&',
            Stmt\If_::class . '->elseifs' => ' ',
            Stmt\TryCatch::class . '->catches' => ' ',

            // comma-separated lists
            Expr\Array_::class . '->items' => ', ',
            Expr\ArrowFunction::class . '->params' => ', ',
            Expr\Closure::class . '->params' => ', ',
            Expr\Closure::class . '->uses' => ', ',
            Expr\FuncCall::class . '->args' => ', ',
            Expr\Isset_::class . '->vars' => ', ',
            Expr\List_::class . '->items' => ', ',
            Expr\MethodCall::class . '->args' => ', ',
            Expr\NullsafeMethodCall::class . '->args' => ', ',
            Expr\New_::class . '->args' => ', ',
            PrintableNewAnonClassNode::class . '->args' => ', ',
            Expr\StaticCall::class . '->args' => ', ',
            Stmt\ClassConst::class . '->consts' => ', ',
            Stmt\ClassMethod::class . '->params' => ', ',
            Stmt\Class_::class . '->implements' => ', ',
            Stmt\Enum_::class . '->implements' => ', ',
            PrintableNewAnonClassNode::class . '->implements' => ', ',
            Stmt\Const_::class . '->consts' => ', ',
            Stmt\Declare_::class . '->declares' => ', ',
            Stmt\Echo_::class . '->exprs' => ', ',
            Stmt\For_::class . '->init' => ', ',
            Stmt\For_::class . '->cond' => ', ',
            Stmt\For_::class . '->loop' => ', ',
            Stmt\Function_::class . '->params' => ', ',
            Stmt\Global_::class . '->vars' => ', ',
            Stmt\GroupUse::class . '->uses' => ', ',
            Stmt\Interface_::class . '->extends' => ', ',
            Expr\Match_::class . '->arms' => ', ',
            Stmt\Property::class . '->props' => ', ',
            Stmt\StaticVar::class . '->vars' => ', ',
            Stmt\TraitUse::class . '->traits' => ', ',
            Stmt\TraitUseAdaptation\Precedence::class . '->insteadof' => ', ',
            Stmt\Unset_::class .  '->vars' => ', ',
            Stmt\UseUse::class . '->uses' => ', ',
            MatchArm::class . '->conds' => ', ',
            AttributeGroup::class . '->attrs' => ', ',
            PropertyHook::class . '->params' => ', ',

            // statement lists
            Expr\Closure::class . '->stmts' => "\n",
            Stmt\Case_::class . '->stmts' => "\n",
            Stmt\Catch_::class . '->stmts' => "\n",
            Stmt\Class_::class . '->stmts' => "\n",
            Stmt\Enum_::class . '->stmts' => "\n",
            PrintableNewAnonClassNode::class . '->stmts' => "\n",
            Stmt\Interface_::class . '->stmts' => "\n",
            Stmt\Trait_::class . '->stmts' => "\n",
            Stmt\ClassMethod::class . '->stmts' => "\n",
            Stmt\Declare_::class . '->stmts' => "\n",
            Stmt\Do_::class . '->stmts' => "\n",
            Stmt\ElseIf_::class . '->stmts' => "\n",
            Stmt\Else_::class . '->stmts' => "\n",
            Stmt\Finally_::class . '->stmts' => "\n",
            Stmt\Foreach_::class . '->stmts' => "\n",
            Stmt\For_::class . '->stmts' => "\n",
            Stmt\Function_::class . '->stmts' => "\n",
            Stmt\If_::class . '->stmts' => "\n",
            Stmt\Namespace_::class . '->stmts' => "\n",
            Stmt\Block::class . '->stmts' => "\n",

            // Attribute groups
            Stmt\Class_::class . '->attrGroups' => "\n",
            Stmt\Enum_::class . '->attrGroups' => "\n",
            Stmt\EnumCase::class . '->attrGroups' => "\n",
            Stmt\Interface_::class . '->attrGroups' => "\n",
            Stmt\Trait_::class . '->attrGroups' => "\n",
            Stmt\Function_::class . '->attrGroups' => "\n",
            Stmt\ClassMethod::class . '->attrGroups' => "\n",
            Stmt\ClassConst::class . '->attrGroups' => "\n",
            Stmt\Property::class . '->attrGroups' => "\n",
            PrintableNewAnonClassNode::class . '->attrGroups' => ' ',
            Expr\Closure::class . '->attrGroups' => ' ',
            Expr\ArrowFunction::class . '->attrGroups' => ' ',
            Param::class . '->attrGroups' => ' ',
            PropertyHook::class . '->attrGroups' => ' ',

            Stmt\Switch_::class . '->cases' => "\n",
            Stmt\TraitUse::class . '->adaptations' => "\n",
            Stmt\TryCatch::class . '->stmts' => "\n",
            Stmt\While_::class . '->stmts' => "\n",
            PropertyHook::class . '->body' => "\n",
            Stmt\Property::class . '->hooks' => "\n",
            Param::class . '->hooks' => "\n",

            // dummy for top-level context
            'File->stmts' => "\n",
        ];
    }

    protected function initializeEmptyListInsertionMap(): void {
        if (isset($this->emptyListInsertionMap)) {
            return;
        }

        // TODO Insertion into empty statement lists.

        // [$find, $extraLeft, $extraRight]
        $this->emptyListInsertionMap = [
            Expr\ArrowFunction::class . '->params' => ['(', '', ''],
            Expr\Closure::class . '->uses' => [')', ' use (', ')'],
            Expr\Closure::class . '->params' => ['(', '', ''],
            Expr\FuncCall::class . '->args' => ['(', '', ''],
            Expr\MethodCall::class . '->args' => ['(', '', ''],
            Expr\NullsafeMethodCall::class . '->args' => ['(', '', ''],
            Expr\New_::class . '->args' => ['(', '', ''],
            PrintableNewAnonClassNode::class . '->args' => ['(', '', ''],
            PrintableNewAnonClassNode::class . '->implements' => [null, ' implements ', ''],
            Expr\StaticCall::class . '->args' => ['(', '', ''],
            Stmt\Class_::class . '->implements' => [null, ' implements ', ''],
            Stmt\Enum_::class . '->implements' => [null, ' implements ', ''],
            Stmt\ClassMethod::class . '->params' => ['(', '', ''],
            Stmt\Interface_::class . '->extends' => [null, ' extends ', ''],
            Stmt\Function_::class . '->params' => ['(', '', ''],
            Stmt\Interface_::class . '->attrGroups' => [null, '', "\n"],
            Stmt\Class_::class . '->attrGroups' => [null, '', "\n"],
            Stmt\Enum_::class . '->attrGroups' => [null, '', "\n"],
            Stmt\EnumCase::class . '->attrGroups' => [null, '', "\n"],
            Stmt\ClassConst::class . '->attrGroups' => [null, '', "\n"],
            Stmt\ClassMethod::class . '->attrGroups' => [null, '', "\n"],
            Stmt\Function_::class . '->attrGroups' => [null, '', "\n"],
            Stmt\Property::class . '->attrGroups' => [null, '', "\n"],
            Stmt\Trait_::class . '->attrGroups' => [null, '', "\n"],
            Expr\ArrowFunction::class . '->attrGroups' => [null, '', ' '],
            Expr\Closure::class . '->attrGroups' => [null, '', ' '],
            Stmt\Const_::class . '->attrGroups' => [null, '', "\n"],
            PrintableNewAnonClassNode::class . '->attrGroups' => [\T_NEW, ' ', ''],

            /* These cannot be empty to start with:
             * Expr_Isset->vars
             * Stmt_Catch->types
             * Stmt_Const->consts
             * Stmt_ClassConst->consts
             * Stmt_Declare->declares
             * Stmt_Echo->exprs
             * Stmt_Global->vars
             * Stmt_GroupUse->uses
             * Stmt_Property->props
             * Stmt_StaticVar->vars
             * Stmt_TraitUse->traits
             * Stmt_TraitUseAdaptation_Precedence->insteadof
             * Stmt_Unset->vars
             * Stmt_Use->uses
             * UnionType->types
             */

            /* TODO
             * Stmt_If->elseifs
             * Stmt_TryCatch->catches
             * Expr_Array->items
             * Expr_List->items
             * Stmt_For->init
             * Stmt_For->cond
             * Stmt_For->loop
             */
        ];
    }

    protected function initializeModifierChangeMap(): void {
        if (isset($this->modifierChangeMap)) {
            return;
        }

        $this->modifierChangeMap = [
            Stmt\ClassConst::class . '->flags' => ['pModifiers', \T_WHITESPACE, \T_CONST],
            Stmt\ClassMethod::class . '->flags' => ['pModifiers', \T_WHITESPACE, \T_FUNCTION],
            Stmt\Class_::class . '->flags' => ['pModifiers', \T_WHITESPACE, \T_CLASS],
            Stmt\Property::class . '->flags' => ['pModifiers', \T_WHITESPACE, \T_VARIABLE],
            PrintableNewAnonClassNode::class . '->flags' => ['pModifiers', \T_NEW, \T_CLASS],
            Param::class . '->flags' => ['pModifiers', \T_WHITESPACE, \T_VARIABLE],
            PropertyHook::class . '->flags' => ['pModifiers', \T_WHITESPACE, \T_STRING],
            Expr\Closure::class . '->static' => ['pStatic', \T_WHITESPACE, \T_FUNCTION],
            Expr\ArrowFunction::class . '->static' => ['pStatic', \T_WHITESPACE, \T_FN],
            //Stmt\TraitUseAdaptation\Alias::class . '->newModifier' => 0, // TODO
        ];

        // List of integer subnodes that are not modifiers:
        // Expr_Include->type
        // Stmt_GroupUse->type
        // Stmt_Use->type
        // UseItem->type
    }
}
<?php declare(strict_types=1);

namespace PhpParser;

use PhpParser\Node\Expr;

interface PrettyPrinter {
    /**
     * Pretty prints an array of statements.
     *
     * @param Node[] $stmts Array of statements
     *
     * @return string Pretty printed statements
     */
    public function prettyPrint(array $stmts): string;

    /**
     * Pretty prints an expression.
     *
     * @param Expr $node Expression node
     *
     * @return string Pretty printed node
     */
    public function prettyPrintExpr(Expr $node): string;

    /**
     * Pretty prints a file of statements (includes the opening <?php tag if it is required).
     *
     * @param Node[] $stmts Array of statements
     *
     * @return string Pretty printed statements
     */
    public function prettyPrintFile(array $stmts): string;

    /**
     * Perform a format-preserving pretty print of an AST.
     *
     * The format preservation is best effort. For some changes to the AST the formatting will not
     * be preserved (at least not locally).
     *
     * In order to use this method a number of prerequisites must be satisfied:
     *  * The startTokenPos and endTokenPos attributes in the lexer must be enabled.
     *  * The CloningVisitor must be run on the AST prior to modification.
     *  * The original tokens must be provided, using the getTokens() method on the lexer.
     *
     * @param Node[] $stmts Modified AST with links to original AST
     * @param Node[] $origStmts Original AST with token offset information
     * @param Token[] $origTokens Tokens of the original code
     */
    public function printFormatPreserving(array $stmts, array $origStmts, array $origTokens): string;
}
<?php declare(strict_types=1);

namespace PhpParser;

interface Parser {
    /**
     * Parses PHP code into a node tree.
     *
     * @param string $code The source code to parse
     * @param ErrorHandler|null $errorHandler Error handler to use for lexer/parser errors, defaults
     *                                        to ErrorHandler\Throwing.
     *
     * @return Node\Stmt[]|null Array of statements (or null non-throwing error handler is used and
     *                          the parser was unable to recover from an error).
     */
    public function parse(string $code, ?ErrorHandler $errorHandler = null): ?array;

    /**
     * Return tokens for the last parse.
     *
     * @return Token[]
     */
    public function getTokens(): array;
}
<?php declare(strict_types=1);

namespace PhpParser\ErrorHandler;

use PhpParser\Error;
use PhpParser\ErrorHandler;

/**
 * Error handler that handles all errors by throwing them.
 *
 * This is the default strategy used by all components.
 */
class Throwing implements ErrorHandler {
    public function handleError(Error $error): void {
        throw $error;
    }
}
<?php declare(strict_types=1);

namespace PhpParser\ErrorHandler;

use PhpParser\Error;
use PhpParser\ErrorHandler;

/**
 * Error handler that collects all errors into an array.
 *
 * This allows graceful handling of errors.
 */
class Collecting implements ErrorHandler {
    /** @var Error[] Collected errors */
    private array $errors = [];

    public function handleError(Error $error): void {
        $this->errors[] = $error;
    }

    /**
     * Get collected errors.
     *
     * @return Error[]
     */
    public function getErrors(): array {
        return $this->errors;
    }

    /**
     * Check whether there are any errors.
     */
    public function hasErrors(): bool {
        return !empty($this->errors);
    }

    /**
     * Reset/clear collected errors.
     */
    public function clearErrors(): void {
        $this->errors = [];
    }
}
<?php declare(strict_types=1);

namespace PhpParser;

/*
 * This parser is based on a skeleton written by Moriyoshi Koizumi, which in
 * turn is based on work by Masato Bito.
 */

use PhpParser\Node\Arg;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\Cast\Double;
use PhpParser\Node\Identifier;
use PhpParser\Node\InterpolatedStringPart;
use PhpParser\Node\Name;
use PhpParser\Node\Param;
use PhpParser\Node\PropertyHook;
use PhpParser\Node\Scalar\InterpolatedString;
use PhpParser\Node\Scalar\Int_;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassConst;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Const_;
use PhpParser\Node\Stmt\Else_;
use PhpParser\Node\Stmt\ElseIf_;
use PhpParser\Node\Stmt\Enum_;
use PhpParser\Node\Stmt\Interface_;
use PhpParser\Node\Stmt\Namespace_;
use PhpParser\Node\Stmt\Nop;
use PhpParser\Node\Stmt\Property;
use PhpParser\Node\Stmt\TryCatch;
use PhpParser\Node\UseItem;
use PhpParser\Node\VarLikeIdentifier;
use PhpParser\NodeVisitor\CommentAnnotatingVisitor;

abstract class ParserAbstract implements Parser {
    private const SYMBOL_NONE = -1;

    /** @var Lexer Lexer that is used when parsing */
    protected Lexer $lexer;
    /** @var PhpVersion PHP version to target on a best-effort basis */
    protected PhpVersion $phpVersion;

    /*
     * The following members will be filled with generated parsing data:
     */

    /** @var int Size of $tokenToSymbol map */
    protected int $tokenToSymbolMapSize;
    /** @var int Size of $action table */
    protected int $actionTableSize;
    /** @var int Size of $goto table */
    protected int $gotoTableSize;

    /** @var int Symbol number signifying an invalid token */
    protected int $invalidSymbol;
    /** @var int Symbol number of error recovery token */
    protected int $errorSymbol;
    /** @var int Action number signifying default action */
    protected int $defaultAction;
    /** @var int Rule number signifying that an unexpected token was encountered */
    protected int $unexpectedTokenRule;

    protected int $YY2TBLSTATE;
    /** @var int Number of non-leaf states */
    protected int $numNonLeafStates;

    /** @var int[] Map of PHP token IDs to internal symbols */
    protected array $phpTokenToSymbol;
    /** @var array<int, bool> Map of PHP token IDs to drop */
    protected array $dropTokens;
    /** @var int[] Map of external symbols (static::T_*) to internal symbols */
    protected array $tokenToSymbol;
    /** @var string[] Map of symbols to their names */
    protected array $symbolToName;
    /** @var array<int, string> Names of the production rules (only necessary for debugging) */
    protected array $productions;

    /** @var int[] Map of states to a displacement into the $action table. The corresponding action for this
     *             state/symbol pair is $action[$actionBase[$state] + $symbol]. If $actionBase[$state] is 0, the
     *             action is defaulted, i.e. $actionDefault[$state] should be used instead. */
    protected array $actionBase;
    /** @var int[] Table of actions. Indexed according to $actionBase comment. */
    protected array $action;
    /** @var int[] Table indexed analogously to $action. If $actionCheck[$actionBase[$state] + $symbol] != $symbol
     *             then the action is defaulted, i.e. $actionDefault[$state] should be used instead. */
    protected array $actionCheck;
    /** @var int[] Map of states to their default action */
    protected array $actionDefault;
    /** @var callable[] Semantic action callbacks */
    protected array $reduceCallbacks;

    /** @var int[] Map of non-terminals to a displacement into the $goto table. The corresponding goto state for this
     *             non-terminal/state pair is $goto[$gotoBase[$nonTerminal] + $state] (unless defaulted) */
    protected array $gotoBase;
    /** @var int[] Table of states to goto after reduction. Indexed according to $gotoBase comment. */
    protected array $goto;
    /** @var int[] Table indexed analogously to $goto. If $gotoCheck[$gotoBase[$nonTerminal] + $state] != $nonTerminal
     *             then the goto state is defaulted, i.e. $gotoDefault[$nonTerminal] should be used. */
    protected array $gotoCheck;
    /** @var int[] Map of non-terminals to the default state to goto after their reduction */
    protected array $gotoDefault;

    /** @var int[] Map of rules to the non-terminal on their left-hand side, i.e. the non-terminal to use for
     *             determining the state to goto after reduction. */
    protected array $ruleToNonTerminal;
    /** @var int[] Map of rules to the length of their right-hand side, which is the number of elements that have to
     *             be popped from the stack(s) on reduction. */
    protected array $ruleToLength;

    /*
     * The following members are part of the parser state:
     */

    /** @var mixed Temporary value containing the result of last semantic action (reduction) */
    protected $semValue;
    /** @var mixed[] Semantic value stack (contains values of tokens and semantic action results) */
    protected array $semStack;
    /** @var int[] Token start position stack */
    protected array $tokenStartStack;
    /** @var int[] Token end position stack */
    protected array $tokenEndStack;

    /** @var ErrorHandler Error handler */
    protected ErrorHandler $errorHandler;
    /** @var int Error state, used to avoid error floods */
    protected int $errorState;

    /** @var \SplObjectStorage<Array_, null>|null Array nodes created during parsing, for postprocessing of empty elements. */
    protected ?\SplObjectStorage $createdArrays;

    /** @var \SplObjectStorage<Expr\ArrowFunction, null>|null
     *       Arrow functions that are wrapped in parentheses, to enforce the pipe operator parentheses requirements.
     */
    protected ?\SplObjectStorage $parenthesizedArrowFunctions;

    /** @var Token[] Tokens for the current parse */
    protected array $tokens;
    /** @var int Current position in token array */
    protected int $tokenPos;

    /**
     * Initialize $reduceCallbacks map.
     */
    abstract protected function initReduceCallbacks(): void;

    /**
     * Creates a parser instance.
     *
     * Options:
     *  * phpVersion: ?PhpVersion,
     *
     * @param Lexer $lexer A lexer
     * @param PhpVersion $phpVersion PHP version to target, defaults to latest supported. This
     *                               option is best-effort: Even if specified, parsing will generally assume the latest
     *                               supported version and only adjust behavior in minor ways, for example by omitting
     *                               errors in older versions and interpreting type hints as a name or identifier depending
     *                               on version.
     */
    public function __construct(Lexer $lexer, ?PhpVersion $phpVersion = null) {
        $this->lexer = $lexer;
        $this->phpVersion = $phpVersion ?? PhpVersion::getNewestSupported();

        $this->initReduceCallbacks();
        $this->phpTokenToSymbol = $this->createTokenMap();
        $this->dropTokens = array_fill_keys(
            [\T_WHITESPACE, \T_OPEN_TAG, \T_COMMENT, \T_DOC_COMMENT, \T_BAD_CHARACTER], true
        );
    }

    /**
     * Parses PHP code into a node tree.
     *
     * If a non-throwing error handler is used, the parser will continue parsing after an error
     * occurred and attempt to build a partial AST.
     *
     * @param string $code The source code to parse
     * @param ErrorHandler|null $errorHandler Error handler to use for lexer/parser errors, defaults
     *                                        to ErrorHandler\Throwing.
     *
     * @return Node\Stmt[]|null Array of statements (or null non-throwing error handler is used and
     *                          the parser was unable to recover from an error).
     */
    public function parse(string $code, ?ErrorHandler $errorHandler = null): ?array {
        $this->errorHandler = $errorHandler ?: new ErrorHandler\Throwing();
        $this->createdArrays = new \SplObjectStorage();
        $this->parenthesizedArrowFunctions = new \SplObjectStorage();

        $this->tokens = $this->lexer->tokenize($code, $this->errorHandler);
        $result = $this->doParse();

        // Report errors for any empty elements used inside arrays. This is delayed until after the main parse,
        // because we don't know a priori whether a given array expression will be used in a destructuring context
        // or not.
        foreach ($this->createdArrays as $node) {
            foreach ($node->items as $item) {
                if ($item->value instanceof Expr\Error) {
                    $this->errorHandler->handleError(
                        new Error('Cannot use empty array elements in arrays', $item->getAttributes()));
                }
            }
        }

        // Clear out some of the interior state, so we don't hold onto unnecessary
        // memory between uses of the parser
        $this->tokenStartStack = [];
        $this->tokenEndStack = [];
        $this->semStack = [];
        $this->semValue = null;
        $this->createdArrays = null;
        $this->parenthesizedArrowFunctions = null;

        if ($result !== null) {
            $traverser = new NodeTraverser(new CommentAnnotatingVisitor($this->tokens));
            $traverser->traverse($result);
        }

        return $result;
    }

    public function getTokens(): array {
        return $this->tokens;
    }

    /** @return Stmt[]|null */
    protected function doParse(): ?array {
        // We start off with no lookahead-token
        $symbol = self::SYMBOL_NONE;
        $tokenValue = null;
        $this->tokenPos = -1;

        // Keep stack of start and end attributes
        $this->tokenStartStack = [];
        $this->tokenEndStack = [0];

        // Start off in the initial state and keep a stack of previous states
        $state = 0;
        $stateStack = [$state];

        // Semantic value stack (contains values of tokens and semantic action results)
        $this->semStack = [];

        // Current position in the stack(s)
        $stackPos = 0;

        $this->errorState = 0;

        for (;;) {
            //$this->traceNewState($state, $symbol);

            if ($this->actionBase[$state] === 0) {
                $rule = $this->actionDefault[$state];
            } else {
                if ($symbol === self::SYMBOL_NONE) {
                    do {
                        $token = $this->tokens[++$this->tokenPos];
                        $tokenId = $token->id;
                    } while (isset($this->dropTokens[$tokenId]));

                    // Map the lexer token id to the internally used symbols.
                    $tokenValue = $token->text;
                    if (!isset($this->phpTokenToSymbol[$tokenId])) {
                        throw new \RangeException(sprintf(
                            'The lexer returned an invalid token (id=%d, value=%s)',
                            $tokenId, $tokenValue
                        ));
                    }
                    $symbol = $this->phpTokenToSymbol[$tokenId];

                    //$this->traceRead($symbol);
                }

                $idx = $this->actionBase[$state] + $symbol;
                if ((($idx >= 0 && $idx < $this->actionTableSize && $this->actionCheck[$idx] === $symbol)
                     || ($state < $this->YY2TBLSTATE
                         && ($idx = $this->actionBase[$state + $this->numNonLeafStates] + $symbol) >= 0
                         && $idx < $this->actionTableSize && $this->actionCheck[$idx] === $symbol))
                    && ($action = $this->action[$idx]) !== $this->defaultAction) {
                    /*
                     * >= numNonLeafStates: shift and reduce
                     * > 0: shift
                     * = 0: accept
                     * < 0: reduce
                     * = -YYUNEXPECTED: error
                     */
                    if ($action > 0) {
                        /* shift */
                        //$this->traceShift($symbol);

                        ++$stackPos;
                        $stateStack[$stackPos] = $state = $action;
                        $this->semStack[$stackPos] = $tokenValue;
                        $this->tokenStartStack[$stackPos] = $this->tokenPos;
                        $this->tokenEndStack[$stackPos] = $this->tokenPos;
                        $symbol = self::SYMBOL_NONE;

                        if ($this->errorState) {
                            --$this->errorState;
                        }

                        if ($action < $this->numNonLeafStates) {
                            continue;
                        }

                        /* $yyn >= numNonLeafStates means shift-and-reduce */
                        $rule = $action - $this->numNonLeafStates;
                    } else {
                        $rule = -$action;
                    }
                } else {
                    $rule = $this->actionDefault[$state];
                }
            }

            for (;;) {
                if ($rule === 0) {
                    /* accept */
                    //$this->traceAccept();
                    return $this->semValue;
                }
                if ($rule !== $this->unexpectedTokenRule) {
                    /* reduce */
                    //$this->traceReduce($rule);

                    $ruleLength = $this->ruleToLength[$rule];
                    try {
                        $callback = $this->reduceCallbacks[$rule];
                        if ($callback !== null) {
                            $callback($this, $stackPos);
                        } elseif ($ruleLength > 0) {
                            $this->semValue = $this->semStack[$stackPos - $ruleLength + 1];
                        }
                    } catch (Error $e) {
                        if (-1 === $e->getStartLine()) {
                            $e->setStartLine($this->tokens[$this->tokenPos]->line);
                        }

                        $this->emitError($e);
                        // Can't recover from this type of error
                        return null;
                    }

                    /* Goto - shift nonterminal */
                    $lastTokenEnd = $this->tokenEndStack[$stackPos];
                    $stackPos -= $ruleLength;
                    $nonTerminal = $this->ruleToNonTerminal[$rule];
                    $idx = $this->gotoBase[$nonTerminal] + $stateStack[$stackPos];
                    if ($idx >= 0 && $idx < $this->gotoTableSize && $this->gotoCheck[$idx] === $nonTerminal) {
                        $state = $this->goto[$idx];
                    } else {
                        $state = $this->gotoDefault[$nonTerminal];
                    }

                    ++$stackPos;
                    $stateStack[$stackPos]     = $state;
                    $this->semStack[$stackPos] = $this->semValue;
                    $this->tokenEndStack[$stackPos] = $lastTokenEnd;
                    if ($ruleLength === 0) {
                        // Empty productions use the start attributes of the lookahead token.
                        $this->tokenStartStack[$stackPos] = $this->tokenPos;
                    }
                } else {
                    /* error */
                    switch ($this->errorState) {
                        case 0:
                            $msg = $this->getErrorMessage($symbol, $state);
                            $this->emitError(new Error($msg, $this->getAttributesForToken($this->tokenPos)));
                            // Break missing intentionally
                            // no break
                        case 1:
                        case 2:
                            $this->errorState = 3;

                            // Pop until error-expecting state uncovered
                            while (!(
                                (($idx = $this->actionBase[$state] + $this->errorSymbol) >= 0
                                    && $idx < $this->actionTableSize && $this->actionCheck[$idx] === $this->errorSymbol)
                                || ($state < $this->YY2TBLSTATE
                                    && ($idx = $this->actionBase[$state + $this->numNonLeafStates] + $this->errorSymbol) >= 0
                                    && $idx < $this->actionTableSize && $this->actionCheck[$idx] === $this->errorSymbol)
                            ) || ($action = $this->action[$idx]) === $this->defaultAction) { // Not totally sure about this
                                if ($stackPos <= 0) {
                                    // Could not recover from error
                                    return null;
                                }
                                $state = $stateStack[--$stackPos];
                                //$this->tracePop($state);
                            }

                            //$this->traceShift($this->errorSymbol);
                            ++$stackPos;
                            $stateStack[$stackPos] = $state = $action;

                            // We treat the error symbol as being empty, so we reset the end attributes
                            // to the end attributes of the last non-error symbol
                            $this->tokenStartStack[$stackPos] = $this->tokenPos;
                            $this->tokenEndStack[$stackPos] = $this->tokenEndStack[$stackPos - 1];
                            break;

                        case 3:
                            if ($symbol === 0) {
                                // Reached EOF without recovering from error
                                return null;
                            }

                            //$this->traceDiscard($symbol);
                            $symbol = self::SYMBOL_NONE;
                            break 2;
                    }
                }

                if ($state < $this->numNonLeafStates) {
                    break;
                }

                /* >= numNonLeafStates means shift-and-reduce */
                $rule = $state - $this->numNonLeafStates;
            }
        }
    }

    protected function emitError(Error $error): void {
        $this->errorHandler->handleError($error);
    }

    /**
     * Format error message including expected tokens.
     *
     * @param int $symbol Unexpected symbol
     * @param int $state State at time of error
     *
     * @return string Formatted error message
     */
    protected function getErrorMessage(int $symbol, int $state): string {
        $expectedString = '';
        if ($expected = $this->getExpectedTokens($state)) {
            $expectedString = ', expecting ' . implode(' or ', $expected);
        }

        return 'Syntax error, unexpected ' . $this->symbolToName[$symbol] . $expectedString;
    }

    /**
     * Get limited number of expected tokens in given state.
     *
     * @param int $state State
     *
     * @return string[] Expected tokens. If too many, an empty array is returned.
     */
    protected function getExpectedTokens(int $state): array {
        $expected = [];

        $base = $this->actionBase[$state];
        foreach ($this->symbolToName as $symbol => $name) {
            $idx = $base + $symbol;
            if ($idx >= 0 && $idx < $this->actionTableSize && $this->actionCheck[$idx] === $symbol
                || $state < $this->YY2TBLSTATE
                && ($idx = $this->actionBase[$state + $this->numNonLeafStates] + $symbol) >= 0
                && $idx < $this->actionTableSize && $this->actionCheck[$idx] === $symbol
            ) {
                if ($this->action[$idx] !== $this->unexpectedTokenRule
                    && $this->action[$idx] !== $this->defaultAction
                    && $symbol !== $this->errorSymbol
                ) {
                    if (count($expected) === 4) {
                        /* Too many expected tokens */
                        return [];
                    }

                    $expected[] = $name;
                }
            }
        }

        return $expected;
    }

    /**
     * Get attributes for a node with the given start and end token positions.
     *
     * @param int $tokenStartPos Token position the node starts at
     * @param int $tokenEndPos Token position the node ends at
     * @return array<string, mixed> Attributes
     */
    protected function getAttributes(int $tokenStartPos, int $tokenEndPos): array {
        $startToken = $this->tokens[$tokenStartPos];
        $afterEndToken = $this->tokens[$tokenEndPos + 1];
        return [
            'startLine' => $startToken->line,
            'startTokenPos' => $tokenStartPos,
            'startFilePos' => $startToken->pos,
            'endLine' => $afterEndToken->line,
            'endTokenPos' => $tokenEndPos,
            'endFilePos' => $afterEndToken->pos - 1,
        ];
    }

    /**
     * Get attributes for a single token at the given token position.
     *
     * @return array<string, mixed> Attributes
     */
    protected function getAttributesForToken(int $tokenPos): array {
        if ($tokenPos < \count($this->tokens) - 1) {
            return $this->getAttributes($tokenPos, $tokenPos);
        }

        // Get attributes for the sentinel token.
        $token = $this->tokens[$tokenPos];
        return [
            'startLine' => $token->line,
            'startTokenPos' => $tokenPos,
            'startFilePos' => $token->pos,
            'endLine' => $token->line,
            'endTokenPos' => $tokenPos,
            'endFilePos' => $token->pos,
        ];
    }

    /*
     * Tracing functions used for debugging the parser.
     */

    /*
    protected function traceNewState($state, $symbol): void {
        echo '% State ' . $state
            . ', Lookahead ' . ($symbol == self::SYMBOL_NONE ? '--none--' : $this->symbolToName[$symbol]) . "\n";
    }

    protected function traceRead($symbol): void {
        echo '% Reading ' . $this->symbolToName[$symbol] . "\n";
    }

    protected function traceShift($symbol): void {
        echo '% Shift ' . $this->symbolToName[$symbol] . "\n";
    }

    protected function traceAccept(): void {
        echo "% Accepted.\n";
    }

    protected function traceReduce($n): void {
        echo '% Reduce by (' . $n . ') ' . $this->productions[$n] . "\n";
    }

    protected function tracePop($state): void {
        echo '% Recovering, uncovered state ' . $state . "\n";
    }

    protected function traceDiscard($symbol): void {
        echo '% Discard ' . $this->symbolToName[$symbol] . "\n";
    }
    */

    /*
     * Helper functions invoked by semantic actions
     */

    /**
     * Moves statements of semicolon-style namespaces into $ns->stmts and checks various error conditions.
     *
     * @param Node\Stmt[] $stmts
     * @return Node\Stmt[]
     */
    protected function handleNamespaces(array $stmts): array {
        $hasErrored = false;
        $style = $this->getNamespacingStyle($stmts);
        if (null === $style) {
            // not namespaced, nothing to do
            return $stmts;
        }
        if ('brace' === $style) {
            // For braced namespaces we only have to check that there are no invalid statements between the namespaces
            $afterFirstNamespace = false;
            foreach ($stmts as $stmt) {
                if ($stmt instanceof Node\Stmt\Namespace_) {
                    $afterFirstNamespace = true;
                } elseif (!$stmt instanceof Node\Stmt\HaltCompiler
                        && !$stmt instanceof Node\Stmt\Nop
                        && $afterFirstNamespace && !$hasErrored) {
                    $this->emitError(new Error(
                        'No code may exist outside of namespace {}', $stmt->getAttributes()));
                    $hasErrored = true; // Avoid one error for every statement
                }
            }
            return $stmts;
        } else {
            // For semicolon namespaces we have to move the statements after a namespace declaration into ->stmts
            $resultStmts = [];
            $targetStmts = &$resultStmts;
            $lastNs = null;
            foreach ($stmts as $stmt) {
                if ($stmt instanceof Node\Stmt\Namespace_) {
                    if ($lastNs !== null) {
                        $this->fixupNamespaceAttributes($lastNs);
                    }
                    if ($stmt->stmts === null) {
                        $stmt->stmts = [];
                        $targetStmts = &$stmt->stmts;
                        $resultStmts[] = $stmt;
                    } else {
                        // This handles the invalid case of mixed style namespaces
                        $resultStmts[] = $stmt;
                        $targetStmts = &$resultStmts;
                    }
                    $lastNs = $stmt;
                } elseif ($stmt instanceof Node\Stmt\HaltCompiler) {
                    // __halt_compiler() is not moved into the namespace
                    $resultStmts[] = $stmt;
                } else {
                    $targetStmts[] = $stmt;
                }
            }
            if ($lastNs !== null) {
                $this->fixupNamespaceAttributes($lastNs);
            }
            return $resultStmts;
        }
    }

    private function fixupNamespaceAttributes(Node\Stmt\Namespace_ $stmt): void {
        // We moved the statements into the namespace node, as such the end of the namespace node
        // needs to be extended to the end of the statements.
        if (empty($stmt->stmts)) {
            return;
        }

        // We only move the builtin end attributes here. This is the best we can do with the
        // knowledge we have.
        $endAttributes = ['endLine', 'endFilePos', 'endTokenPos'];
        $lastStmt = $stmt->stmts[count($stmt->stmts) - 1];
        foreach ($endAttributes as $endAttribute) {
            if ($lastStmt->hasAttribute($endAttribute)) {
                $stmt->setAttribute($endAttribute, $lastStmt->getAttribute($endAttribute));
            }
        }
    }

    /** @return array<string, mixed> */
    private function getNamespaceErrorAttributes(Namespace_ $node): array {
        $attrs = $node->getAttributes();
        // Adjust end attributes to only cover the "namespace" keyword, not the whole namespace.
        if (isset($attrs['startLine'])) {
            $attrs['endLine'] = $attrs['startLine'];
        }
        if (isset($attrs['startTokenPos'])) {
            $attrs['endTokenPos'] = $attrs['startTokenPos'];
        }
        if (isset($attrs['startFilePos'])) {
            $attrs['endFilePos'] = $attrs['startFilePos'] + \strlen('namespace') - 1;
        }
        return $attrs;
    }

    /**
     * Determine namespacing style (semicolon or brace)
     *
     * @param Node[] $stmts Top-level statements.
     *
     * @return null|string One of "semicolon", "brace" or null (no namespaces)
     */
    private function getNamespacingStyle(array $stmts): ?string {
        $style = null;
        $hasNotAllowedStmts = false;
        foreach ($stmts as $i => $stmt) {
            if ($stmt instanceof Node\Stmt\Namespace_) {
                $currentStyle = null === $stmt->stmts ? 'semicolon' : 'brace';
                if (null === $style) {
                    $style = $currentStyle;
                    if ($hasNotAllowedStmts) {
                        $this->emitError(new Error(
                            'Namespace declaration statement has to be the very first statement in the script',
                            $this->getNamespaceErrorAttributes($stmt)
                        ));
                    }
                } elseif ($style !== $currentStyle) {
                    $this->emitError(new Error(
                        'Cannot mix bracketed namespace declarations with unbracketed namespace declarations',
                        $this->getNamespaceErrorAttributes($stmt)
                    ));
                    // Treat like semicolon style for namespace normalization
                    return 'semicolon';
                }
                continue;
            }

            /* declare(), __halt_compiler() and nops can be used before a namespace declaration */
            if ($stmt instanceof Node\Stmt\Declare_
                || $stmt instanceof Node\Stmt\HaltCompiler
                || $stmt instanceof Node\Stmt\Nop) {
                continue;
            }

            /* There may be a hashbang line at the very start of the file */
            if ($i === 0 && $stmt instanceof Node\Stmt\InlineHTML && preg_match('/\A#!.*\r?\n\z/', $stmt->value)) {
                continue;
            }

            /* Everything else if forbidden before namespace declarations */
            $hasNotAllowedStmts = true;
        }
        return $style;
    }

    /** @return Name|Identifier */
    protected function handleBuiltinTypes(Name $name) {
        if (!$name->isUnqualified()) {
            return $name;
        }

        $lowerName = $name->toLowerString();
        if (!$this->phpVersion->supportsBuiltinType($lowerName)) {
            return $name;
        }

        return new Node\Identifier($lowerName, $name->getAttributes());
    }

    /**
     * Get combined start and end attributes at a stack location
     *
     * @param int $stackPos Stack location
     *
     * @return array<string, mixed> Combined start and end attributes
     */
    protected function getAttributesAt(int $stackPos): array {
        return $this->getAttributes($this->tokenStartStack[$stackPos], $this->tokenEndStack[$stackPos]);
    }

    protected function getFloatCastKind(string $cast): int {
        $cast = strtolower($cast);
        if (strpos($cast, 'float') !== false) {
            return Double::KIND_FLOAT;
        }

        if (strpos($cast, 'real') !== false) {
            return Double::KIND_REAL;
        }

        return Double::KIND_DOUBLE;
    }

    protected function getIntCastKind(string $cast): int {
        $cast = strtolower($cast);
        if (strpos($cast, 'integer') !== false) {
            return Expr\Cast\Int_::KIND_INTEGER;
        }

        return Expr\Cast\Int_::KIND_INT;
    }

    protected function getBoolCastKind(string $cast): int {
        $cast = strtolower($cast);
        if (strpos($cast, 'boolean') !== false) {
            return Expr\Cast\Bool_::KIND_BOOLEAN;
        }

        return Expr\Cast\Bool_::KIND_BOOL;
    }

    protected function getStringCastKind(string $cast): int {
        $cast = strtolower($cast);
        if (strpos($cast, 'binary') !== false) {
            return Expr\Cast\String_::KIND_BINARY;
        }

        return Expr\Cast\String_::KIND_STRING;
    }

    /** @param array<string, mixed> $attributes */
    protected function parseLNumber(string $str, array $attributes, bool $allowInvalidOctal = false): Int_ {
        try {
            return Int_::fromString($str, $attributes, $allowInvalidOctal);
        } catch (Error $error) {
            $this->emitError($error);
            // Use dummy value
            return new Int_(0, $attributes);
        }
    }

    /**
     * Parse a T_NUM_STRING token into either an integer or string node.
     *
     * @param string $str Number string
     * @param array<string, mixed> $attributes Attributes
     *
     * @return Int_|String_ Integer or string node.
     */
    protected function parseNumString(string $str, array $attributes) {
        if (!preg_match('/^(?:0|-?[1-9][0-9]*)$/', $str)) {
            return new String_($str, $attributes);
        }

        $num = +$str;
        if (!is_int($num)) {
            return new String_($str, $attributes);
        }

        return new Int_($num, $attributes);
    }

    /** @param array<string, mixed> $attributes */
    protected function stripIndentation(
        string $string, int $indentLen, string $indentChar,
        bool $newlineAtStart, bool $newlineAtEnd, array $attributes
    ): string {
        if ($indentLen === 0) {
            return $string;
        }

        $start = $newlineAtStart ? '(?:(?<=\n)|\A)' : '(?<=\n)';
        $end = $newlineAtEnd ? '(?:(?=[\r\n])|\z)' : '(?=[\r\n])';
        $regex = '/' . $start . '([ \t]*)(' . $end . ')?/';
        return preg_replace_callback(
            $regex,
            function ($matches) use ($indentLen, $indentChar, $attributes) {
                $prefix = substr($matches[1], 0, $indentLen);
                if (false !== strpos($prefix, $indentChar === " " ? "\t" : " ")) {
                    $this->emitError(new Error(
                        'Invalid indentation - tabs and spaces cannot be mixed', $attributes
                    ));
                } elseif (strlen($prefix) < $indentLen && !isset($matches[2])) {
                    $this->emitError(new Error(
                        'Invalid body indentation level ' .
                        '(expecting an indentation level of at least ' . $indentLen . ')',
                        $attributes
                    ));
                }
                return substr($matches[0], strlen($prefix));
            },
            $string
        );
    }

    /**
     * @param string|(Expr|InterpolatedStringPart)[] $contents
     * @param array<string, mixed> $attributes
     * @param array<string, mixed> $endTokenAttributes
     */
    protected function parseDocString(
        string $startToken, $contents, string $endToken,
        array $attributes, array $endTokenAttributes, bool $parseUnicodeEscape
    ): Expr {
        $kind = strpos($startToken, "'") === false
            ? String_::KIND_HEREDOC : String_::KIND_NOWDOC;

        $regex = '/\A[bB]?<<<[ \t]*[\'"]?([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)[\'"]?(?:\r\n|\n|\r)\z/';
        $result = preg_match($regex, $startToken, $matches);
        assert($result === 1);
        $label = $matches[1];

        $result = preg_match('/\A[ \t]*/', $endToken, $matches);
        assert($result === 1);
        $indentation = $matches[0];

        $attributes['kind'] = $kind;
        $attributes['docLabel'] = $label;
        $attributes['docIndentation'] = $indentation;

        $indentHasSpaces = false !== strpos($indentation, " ");
        $indentHasTabs = false !== strpos($indentation, "\t");
        if ($indentHasSpaces && $indentHasTabs) {
            $this->emitError(new Error(
                'Invalid indentation - tabs and spaces cannot be mixed',
                $endTokenAttributes
            ));

            // Proceed processing as if this doc string is not indented
            $indentation = '';
        }

        $indentLen = \strlen($indentation);
        $indentChar = $indentHasSpaces ? " " : "\t";

        if (\is_string($contents)) {
            if ($contents === '') {
                $attributes['rawValue'] = $contents;
                return new String_('', $attributes);
            }

            $contents = $this->stripIndentation(
                $contents, $indentLen, $indentChar, true, true, $attributes
            );
            $contents = preg_replace('~(\r\n|\n|\r)\z~', '', $contents);
            $attributes['rawValue'] = $contents;

            if ($kind === String_::KIND_HEREDOC) {
                $contents = String_::parseEscapeSequences($contents, null, $parseUnicodeEscape);
            }

            return new String_($contents, $attributes);
        } else {
            assert(count($contents) > 0);
            if (!$contents[0] instanceof Node\InterpolatedStringPart) {
                // If there is no leading encapsed string part, pretend there is an empty one
                $this->stripIndentation(
                    '', $indentLen, $indentChar, true, false, $contents[0]->getAttributes()
                );
            }

            $newContents = [];
            foreach ($contents as $i => $part) {
                if ($part instanceof Node\InterpolatedStringPart) {
                    $isLast = $i === \count($contents) - 1;
                    $part->value = $this->stripIndentation(
                        $part->value, $indentLen, $indentChar,
                        $i === 0, $isLast, $part->getAttributes()
                    );
                    if ($isLast) {
                        $part->value = preg_replace('~(\r\n|\n|\r)\z~', '', $part->value);
                    }
                    $part->setAttribute('rawValue', $part->value);
                    $part->value = String_::parseEscapeSequences($part->value, null, $parseUnicodeEscape);
                    if ('' === $part->value) {
                        continue;
                    }
                }
                $newContents[] = $part;
            }
            return new InterpolatedString($newContents, $attributes);
        }
    }

    protected function createCommentFromToken(Token $token, int $tokenPos): Comment {
        assert($token->id === \T_COMMENT || $token->id == \T_DOC_COMMENT);
        return \T_DOC_COMMENT === $token->id
            ? new Comment\Doc($token->text, $token->line, $token->pos, $tokenPos,
                $token->getEndLine(), $token->getEndPos() - 1, $tokenPos)
            : new Comment($token->text, $token->line, $token->pos, $tokenPos,
                $token->getEndLine(), $token->getEndPos() - 1, $tokenPos);
    }

    /**
     * Get last comment before the given token position, if any
     */
    protected function getCommentBeforeToken(int $tokenPos): ?Comment {
        while (--$tokenPos >= 0) {
            $token = $this->tokens[$tokenPos];
            if (!isset($this->dropTokens[$token->id])) {
                break;
            }

            if ($token->id === \T_COMMENT || $token->id === \T_DOC_COMMENT) {
                return $this->createCommentFromToken($token, $tokenPos);
            }
        }
        return null;
    }

    /**
     * Create a zero-length nop to capture preceding comments, if any.
     */
    protected function maybeCreateZeroLengthNop(int $tokenPos): ?Nop {
        $comment = $this->getCommentBeforeToken($tokenPos);
        if ($comment === null) {
            return null;
        }

        $commentEndLine = $comment->getEndLine();
        $commentEndFilePos = $comment->getEndFilePos();
        $commentEndTokenPos = $comment->getEndTokenPos();
        $attributes = [
            'startLine' => $commentEndLine,
            'endLine' => $commentEndLine,
            'startFilePos' => $commentEndFilePos + 1,
            'endFilePos' => $commentEndFilePos,
            'startTokenPos' => $commentEndTokenPos + 1,
            'endTokenPos' => $commentEndTokenPos,
        ];
        return new Nop($attributes);
    }

    protected function maybeCreateNop(int $tokenStartPos, int $tokenEndPos): ?Nop {
        if ($this->getCommentBeforeToken($tokenStartPos) === null) {
            return null;
        }
        return new Nop($this->getAttributes($tokenStartPos, $tokenEndPos));
    }

    protected function handleHaltCompiler(): string {
        // Prevent the lexer from returning any further tokens.
        $nextToken = $this->tokens[$this->tokenPos + 1];
        $this->tokenPos = \count($this->tokens) - 2;

        // Return text after __halt_compiler.
        return $nextToken->id === \T_INLINE_HTML ? $nextToken->text : '';
    }

    protected function inlineHtmlHasLeadingNewline(int $stackPos): bool {
        $tokenPos = $this->tokenStartStack[$stackPos];
        $token = $this->tokens[$tokenPos];
        assert($token->id == \T_INLINE_HTML);
        if ($tokenPos > 0) {
            $prevToken = $this->tokens[$tokenPos - 1];
            assert($prevToken->id == \T_CLOSE_TAG);
            return false !== strpos($prevToken->text, "\n")
                || false !== strpos($prevToken->text, "\r");
        }
        return true;
    }

    /**
     * @return array<string, mixed>
     */
    protected function createEmptyElemAttributes(int $tokenPos): array {
        return $this->getAttributesForToken($tokenPos);
    }

    protected function fixupArrayDestructuring(Array_ $node): Expr\List_ {
        $this->createdArrays->offsetUnset($node);
        return new Expr\List_(array_map(function (Node\ArrayItem $item) {
            if ($item->value instanceof Expr\Error) {
                // We used Error as a placeholder for empty elements, which are legal for destructuring.
                return null;
            }
            if ($item->value instanceof Array_) {
                return new Node\ArrayItem(
                    $this->fixupArrayDestructuring($item->value),
                    $item->key, $item->byRef, $item->getAttributes());
            }
            return $item;
        }, $node->items), ['kind' => Expr\List_::KIND_ARRAY] + $node->getAttributes());
    }

    protected function postprocessList(Expr\List_ $node): void {
        foreach ($node->items as $i => $item) {
            if ($item->value instanceof Expr\Error) {
                // We used Error as a placeholder for empty elements, which are legal for destructuring.
                $node->items[$i] = null;
            }
        }
    }

    /** @param ElseIf_|Else_ $node */
    protected function fixupAlternativeElse($node): void {
        // Make sure a trailing nop statement carrying comments is part of the node.
        $numStmts = \count($node->stmts);
        if ($numStmts !== 0 && $node->stmts[$numStmts - 1] instanceof Nop) {
            $nopAttrs = $node->stmts[$numStmts - 1]->getAttributes();
            if (isset($nopAttrs['endLine'])) {
                $node->setAttribute('endLine', $nopAttrs['endLine']);
            }
            if (isset($nopAttrs['endFilePos'])) {
                $node->setAttribute('endFilePos', $nopAttrs['endFilePos']);
            }
            if (isset($nopAttrs['endTokenPos'])) {
                $node->setAttribute('endTokenPos', $nopAttrs['endTokenPos']);
            }
        }
    }

    protected function checkClassModifier(int $a, int $b, int $modifierPos): void {
        try {
            Modifiers::verifyClassModifier($a, $b);
        } catch (Error $error) {
            $error->setAttributes($this->getAttributesAt($modifierPos));
            $this->emitError($error);
        }
    }

    protected function checkModifier(int $a, int $b, int $modifierPos): void {
        // Jumping through some hoops here because verifyModifier() is also used elsewhere
        try {
            Modifiers::verifyModifier($a, $b);
        } catch (Error $error) {
            $error->setAttributes($this->getAttributesAt($modifierPos));
            $this->emitError($error);
        }
    }

    protected function checkParam(Param $node): void {
        if ($node->variadic && null !== $node->default) {
            $this->emitError(new Error(
                'Variadic parameter cannot have a default value',
                $node->default->getAttributes()
            ));
        }

        if ($node->type instanceof Identifier && $node->type->name === 'void') {
            $this->emitError(new Error(
                'void cannot be used as a parameter type',
                $node->type->getAttributes()
            ));
        }
    }

    protected function checkTryCatch(TryCatch $node): void {
        if (empty($node->catches) && null === $node->finally) {
            $this->emitError(new Error(
                'Cannot use try without catch or finally', $node->getAttributes()
            ));
        }
    }

    protected function checkNamespace(Namespace_ $node): void {
        if (null !== $node->stmts) {
            foreach ($node->stmts as $stmt) {
                if ($stmt instanceof Namespace_) {
                    $this->emitError(new Error(
                        'Namespace declarations cannot be nested', $stmt->getAttributes()
                    ));
                }
            }
        }
    }

    private function checkClassName(?Identifier $name, int $namePos): void {
        if (null !== $name && $name->isSpecialClassName()) {
            $this->emitError(new Error(
                sprintf('Cannot use \'%s\' as class name as it is reserved', $name),
                $this->getAttributesAt($namePos)
            ));
        }
    }

    /** @param Name[] $interfaces */
    private function checkImplementedInterfaces(array $interfaces): void {
        foreach ($interfaces as $interface) {
            if ($interface->isSpecialClassName()) {
                $this->emitError(new Error(
                    sprintf('Cannot use \'%s\' as interface name as it is reserved', $interface),
                    $interface->getAttributes()
                ));
            }
        }
    }

    protected function checkClass(Class_ $node, int $namePos): void {
        $this->checkClassName($node->name, $namePos);

        if ($node->extends && $node->extends->isSpecialClassName()) {
            $this->emitError(new Error(
                sprintf('Cannot use \'%s\' as class name as it is reserved', $node->extends),
                $node->extends->getAttributes()
            ));
        }

        $this->checkImplementedInterfaces($node->implements);
    }

    protected function checkInterface(Interface_ $node, int $namePos): void {
        $this->checkClassName($node->name, $namePos);
        $this->checkImplementedInterfaces($node->extends);
    }

    protected function checkEnum(Enum_ $node, int $namePos): void {
        $this->checkClassName($node->name, $namePos);
        $this->checkImplementedInterfaces($node->implements);
    }

    protected function checkClassMethod(ClassMethod $node, int $modifierPos): void {
        if ($node->flags & Modifiers::STATIC) {
            switch ($node->name->toLowerString()) {
                case '__construct':
                    $this->emitError(new Error(
                        sprintf('Constructor %s() cannot be static', $node->name),
                        $this->getAttributesAt($modifierPos)));
                    break;
                case '__destruct':
                    $this->emitError(new Error(
                        sprintf('Destructor %s() cannot be static', $node->name),
                        $this->getAttributesAt($modifierPos)));
                    break;
                case '__clone':
                    $this->emitError(new Error(
                        sprintf('Clone method %s() cannot be static', $node->name),
                        $this->getAttributesAt($modifierPos)));
                    break;
            }
        }

        if ($node->flags & Modifiers::READONLY) {
            $this->emitError(new Error(
                sprintf('Method %s() cannot be readonly', $node->name),
                $this->getAttributesAt($modifierPos)));
        }
    }

    protected function checkClassConst(ClassConst $node, int $modifierPos): void {
        foreach ([Modifiers::STATIC, Modifiers::ABSTRACT, Modifiers::READONLY] as $modifier) {
            if ($node->flags & $modifier) {
                $this->emitError(new Error(
                    "Cannot use '" . Modifiers::toString($modifier) . "' as constant modifier",
                    $this->getAttributesAt($modifierPos)));
            }
        }
    }

    protected function checkUseUse(UseItem $node, int $namePos): void {
        if ($node->alias && $node->alias->isSpecialClassName()) {
            $this->emitError(new Error(
                sprintf(
                    'Cannot use %s as %s because \'%2$s\' is a special class name',
                    $node->name, $node->alias
                ),
                $this->getAttributesAt($namePos)
            ));
        }
    }

    protected function checkPropertyHooksForMultiProperty(Property $property, int $hookPos): void {
        if (count($property->props) > 1) {
            $this->emitError(new Error(
                'Cannot use hooks when declaring multiple properties', $this->getAttributesAt($hookPos)));
        }
    }

    /** @param PropertyHook[] $hooks */
    protected function checkEmptyPropertyHookList(array $hooks, int $hookPos): void {
        if (empty($hooks)) {
            $this->emitError(new Error(
                'Property hook list cannot be empty', $this->getAttributesAt($hookPos)));
        }
    }

    protected function checkPropertyHook(PropertyHook $hook, ?int $paramListPos): void {
        $name = $hook->name->toLowerString();
        if ($name !== 'get' && $name !== 'set') {
            $this->emitError(new Error(
                'Unknown hook "' . $hook->name . '", expected "get" or "set"',
                $hook->name->getAttributes()));
        }
        if ($name === 'get' && $paramListPos !== null) {
            $this->emitError(new Error(
                'get hook must not have a parameter list', $this->getAttributesAt($paramListPos)));
        }
    }

    protected function checkPropertyHookModifiers(int $a, int $b, int $modifierPos): void {
        try {
            Modifiers::verifyModifier($a, $b);
        } catch (Error $error) {
            $error->setAttributes($this->getAttributesAt($modifierPos));
            $this->emitError($error);
        }

        if ($b != Modifiers::FINAL) {
            $this->emitError(new Error(
                'Cannot use the ' . Modifiers::toString($b) . ' modifier on a property hook',
                $this->getAttributesAt($modifierPos)));
        }
    }

    protected function checkConstantAttributes(Const_ $node): void {
        if ($node->attrGroups !== [] && count($node->consts) > 1) {
            $this->emitError(new Error(
                'Cannot use attributes on multiple constants at once', $node->getAttributes()));
        }
    }

    protected function checkPipeOperatorParentheses(Expr $node): void {
        if ($node instanceof Expr\ArrowFunction && !$this->parenthesizedArrowFunctions->offsetExists($node)) {
            $this->emitError(new Error(
                'Arrow functions on the right hand side of |> must be parenthesized', $node->getAttributes()));
        }
    }

    /**
     * @param Property|Param $node
     */
    protected function addPropertyNameToHooks(Node $node): void {
        if ($node instanceof Property) {
            $name = $node->props[0]->name->toString();
        } else {
            $name = $node->var->name;
        }
        foreach ($node->hooks as $hook) {
            $hook->setAttribute('propertyName', $name);
        }
    }

    /** @param array<Node\Arg|Node\VariadicPlaceholder|Node\ArgPlaceholder> $args */
    private function isSimpleExit(array $args): bool {
        if (\count($args) === 0) {
            return true;
        }
        if (\count($args) === 1) {
            $arg = $args[0];
            return $arg instanceof Arg && $arg->name === null &&
                   $arg->byRef === false && $arg->unpack === false;
        }
        return false;
    }

    /**
     * @param array<Node\Arg|Node\VariadicPlaceholder|Node\ArgPlaceholder> $args
     * @param array<string, mixed> $attrs
     */
    protected function createExitExpr(string $name, int $namePos, array $args, array $attrs): Expr {
        if ($this->isSimpleExit($args)) {
            // Create Exit node for backwards compatibility.
            $attrs['kind'] = strtolower($name) === 'exit' ? Expr\Exit_::KIND_EXIT : Expr\Exit_::KIND_DIE;
            return new Expr\Exit_(\count($args) === 1 ? $args[0]->value : null, $attrs);
        }
        return new Expr\FuncCall(new Name($name, $this->getAttributesAt($namePos)), $args, $attrs);
    }

    /**
     * Creates the token map.
     *
     * The token map maps the PHP internal token identifiers
     * to the identifiers used by the Parser. Additionally it
     * maps T_OPEN_TAG_WITH_ECHO to T_ECHO and T_CLOSE_TAG to ';'.
     *
     * @return array<int, int> The token map
     */
    protected function createTokenMap(): array {
        $tokenMap = [];

        // Single-char tokens use an identity mapping.
        for ($i = 0; $i < 256; ++$i) {
            $tokenMap[$i] = $i;
        }

        foreach ($this->symbolToName as $name) {
            if ($name[0] === 'T') {
                $tokenMap[\constant($name)] = constant(static::class . '::' . $name);
            }
        }

        // T_OPEN_TAG_WITH_ECHO with dropped T_OPEN_TAG results in T_ECHO
        $tokenMap[\T_OPEN_TAG_WITH_ECHO] = static::T_ECHO;
        // T_CLOSE_TAG is equivalent to ';'
        $tokenMap[\T_CLOSE_TAG] = ord(';');

        // We have created a map from PHP token IDs to external symbol IDs.
        // Now map them to the internal symbol ID.
        $fullTokenMap = [];
        foreach ($tokenMap as $phpToken => $extSymbol) {
            $intSymbol = $this->tokenToSymbol[$extSymbol];
            if ($intSymbol === $this->invalidSymbol) {
                continue;
            }
            $fullTokenMap[$phpToken] = $intSymbol;
        }

        return $fullTokenMap;
    }
}
<?php declare(strict_types=1);

namespace PhpParser;

interface Node {
    /**
     * Gets the type of the node.
     *
     * @psalm-return non-empty-string
     * @return string Type of the node
     */
    public function getType(): string;

    /**
     * Gets the names of the sub nodes.
     *
     * @return string[] Names of sub nodes
     */
    public function getSubNodeNames(): array;

    /**
     * Gets line the node started in (alias of getStartLine).
     *
     * @return int Start line (or -1 if not available)
     * @phpstan-return -1|positive-int
     *
     * @deprecated Use getStartLine() instead
     */
    public function getLine(): int;

    /**
     * Gets line the node started in.
     *
     * Requires the 'startLine' attribute to be enabled in the lexer (enabled by default).
     *
     * @return int Start line (or -1 if not available)
     * @phpstan-return -1|positive-int
     */
    public function getStartLine(): int;

    /**
     * Gets the line the node ended in.
     *
     * Requires the 'endLine' attribute to be enabled in the lexer (enabled by default).
     *
     * @return int End line (or -1 if not available)
     * @phpstan-return -1|positive-int
     */
    public function getEndLine(): int;

    /**
     * Gets the token offset of the first token that is part of this node.
     *
     * The offset is an index into the array returned by Lexer::getTokens().
     *
     * Requires the 'startTokenPos' attribute to be enabled in the lexer (DISABLED by default).
     *
     * @return int Token start position (or -1 if not available)
     */
    public function getStartTokenPos(): int;

    /**
     * Gets the token offset of the last token that is part of this node.
     *
     * The offset is an index into the array returned by Lexer::getTokens().
     *
     * Requires the 'endTokenPos' attribute to be enabled in the lexer (DISABLED by default).
     *
     * @return int Token end position (or -1 if not available)
     */
    public function getEndTokenPos(): int;

    /**
     * Gets the file offset of the first character that is part of this node.
     *
     * Requires the 'startFilePos' attribute to be enabled in the lexer (DISABLED by default).
     *
     * @return int File start position (or -1 if not available)
     */
    public function getStartFilePos(): int;

    /**
     * Gets the file offset of the last character that is part of this node.
     *
     * Requires the 'endFilePos' attribute to be enabled in the lexer (DISABLED by default).
     *
     * @return int File end position (or -1 if not available)
     */
    public function getEndFilePos(): int;

    /**
     * Gets all comments directly preceding this node.
     *
     * The comments are also available through the "comments" attribute.
     *
     * @return Comment[]
     */
    public function getComments(): array;

    /**
     * Gets the doc comment of the node.
     *
     * @return null|Comment\Doc Doc comment object or null
     */
    public function getDocComment(): ?Comment\Doc;

    /**
     * Sets the doc comment of the node.
     *
     * This will either replace an existing doc comment or add it to the comments array.
     *
     * @param Comment\Doc $docComment Doc comment to set
     */
    public function setDocComment(Comment\Doc $docComment): void;

    /**
     * Sets an attribute on a node.
     *
     * @param mixed $value
     */
    public function setAttribute(string $key, $value): void;

    /**
     * Returns whether an attribute exists.
     */
    public function hasAttribute(string $key): bool;

    /**
     * Returns the value of an attribute.
     *
     * @param mixed $default
     *
     * @return mixed
     */
    public function getAttribute(string $key, $default = null);

    /**
     * Returns all the attributes of this node.
     *
     * @return array<string, mixed>
     */
    public function getAttributes(): array;

    /**
     * Replaces all the attributes of this node.
     *
     * @param array<string, mixed> $attributes
     */
    public function setAttributes(array $attributes): void;
}
<?php declare(strict_types=1);

namespace PhpParser;

class ConstExprEvaluationException extends \Exception {
}
<?php declare(strict_types=1);

namespace PhpParser;

class NodeTraverser implements NodeTraverserInterface {
    /**
     * @deprecated Use NodeVisitor::DONT_TRAVERSE_CHILDREN instead.
     */
    public const DONT_TRAVERSE_CHILDREN = NodeVisitor::DONT_TRAVERSE_CHILDREN;

    /**
     * @deprecated Use NodeVisitor::STOP_TRAVERSAL instead.
     */
    public const STOP_TRAVERSAL = NodeVisitor::STOP_TRAVERSAL;

    /**
     * @deprecated Use NodeVisitor::REMOVE_NODE instead.
     */
    public const REMOVE_NODE = NodeVisitor::REMOVE_NODE;

    /**
     * @deprecated Use NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN instead.
     */
    public const DONT_TRAVERSE_CURRENT_AND_CHILDREN = NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN;

    /** @var list<NodeVisitor> Visitors */
    protected array $visitors = [];

    /** @var bool Whether traversal should be stopped */
    protected bool $stopTraversal;

    /**
     * Create a traverser with the given visitors.
     *
     * @param NodeVisitor ...$visitors Node visitors
     */
    public function __construct(NodeVisitor ...$visitors) {
        $this->visitors = $visitors;
    }

    /**
     * Adds a visitor.
     *
     * @param NodeVisitor $visitor Visitor to add
     */
    public function addVisitor(NodeVisitor $visitor): void {
        $this->visitors[] = $visitor;
    }

    /**
     * Removes an added visitor.
     */
    public function removeVisitor(NodeVisitor $visitor): void {
        $index = array_search($visitor, $this->visitors);
        if ($index !== false) {
            array_splice($this->visitors, $index, 1, []);
        }
    }

    /**
     * Traverses an array of nodes using the registered visitors.
     *
     * @param Node[] $nodes Array of nodes
     *
     * @return Node[] Traversed array of nodes
     */
    public function traverse(array $nodes): array {
        $this->stopTraversal = false;

        foreach ($this->visitors as $visitor) {
            if (null !== $return = $visitor->beforeTraverse($nodes)) {
                $nodes = $return;
            }
        }

        $nodes = $this->traverseArray($nodes);

        for ($i = \count($this->visitors) - 1; $i >= 0; --$i) {
            $visitor = $this->visitors[$i];
            if (null !== $return = $visitor->afterTraverse($nodes)) {
                $nodes = $return;
            }
        }

        return $nodes;
    }

    /**
     * Recursively traverse a node.
     *
     * @param Node $node Node to traverse.
     */
    protected function traverseNode(Node $node): void {
        foreach ($node->getSubNodeNames() as $name) {
            $subNode = $node->$name;

            if (\is_array($subNode)) {
                $node->$name = $this->traverseArray($subNode);
                if ($this->stopTraversal) {
                    break;
                }

                continue;
            }

            if (!$subNode instanceof Node) {
                continue;
            }

            $traverseChildren = true;
            $visitorIndex = -1;

            foreach ($this->visitors as $visitorIndex => $visitor) {
                $return = $visitor->enterNode($subNode);
                if (null !== $return) {
                    if ($return instanceof Node) {
                        $this->ensureReplacementReasonable($subNode, $return);
                        $subNode = $node->$name = $return;
                    } elseif (NodeVisitor::DONT_TRAVERSE_CHILDREN === $return) {
                        $traverseChildren = false;
                    } elseif (NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN === $return) {
                        $traverseChildren = false;
                        break;
                    } elseif (NodeVisitor::STOP_TRAVERSAL === $return) {
                        $this->stopTraversal = true;
                        break 2;
                    } elseif (NodeVisitor::REPLACE_WITH_NULL === $return) {
                        $node->$name = null;
                        continue 2;
                    } else {
                        throw new \LogicException(
                            'enterNode() returned invalid value of type ' . gettype($return)
                        );
                    }
                }
            }

            if ($traverseChildren) {
                $this->traverseNode($subNode);
                if ($this->stopTraversal) {
                    break;
                }
            }

            for (; $visitorIndex >= 0; --$visitorIndex) {
                $visitor = $this->visitors[$visitorIndex];
                $return = $visitor->leaveNode($subNode);

                if (null !== $return) {
                    if ($return instanceof Node) {
                        $this->ensureReplacementReasonable($subNode, $return);
                        $subNode = $node->$name = $return;
                    } elseif (NodeVisitor::STOP_TRAVERSAL === $return) {
                        $this->stopTraversal = true;
                        break 2;
                    } elseif (NodeVisitor::REPLACE_WITH_NULL === $return) {
                        $node->$name = null;
                        break;
                    } elseif (\is_array($return)) {
                        throw new \LogicException(
                            'leaveNode() may only return an array ' .
                            'if the parent structure is an array'
                        );
                    } else {
                        throw new \LogicException(
                            'leaveNode() returned invalid value of type ' . gettype($return)
                        );
                    }
                }
            }
        }
    }

    /**
     * Recursively traverse array (usually of nodes).
     *
     * @param Node[] $nodes Array to traverse
     *
     * @return Node[] Result of traversal (may be original array or changed one)
     */
    protected function traverseArray(array $nodes): array {
        $doNodes = [];

        foreach ($nodes as $i => $node) {
            if (!$node instanceof Node) {
                if (\is_array($node)) {
                    throw new \LogicException('Invalid node structure: Contains nested arrays');
                }
                continue;
            }

            $traverseChildren = true;
            $visitorIndex = -1;

            foreach ($this->visitors as $visitorIndex => $visitor) {
                $return = $visitor->enterNode($node);
                if (null !== $return) {
                    if ($return instanceof Node) {
                        $this->ensureReplacementReasonable($node, $return);
                        $nodes[$i] = $node = $return;
                    } elseif (\is_array($return)) {
                        $doNodes[] = [$i, $return];
                        continue 2;
                    } elseif (NodeVisitor::REMOVE_NODE === $return) {
                        $doNodes[] = [$i, []];
                        continue 2;
                    } elseif (NodeVisitor::DONT_TRAVERSE_CHILDREN === $return) {
                        $traverseChildren = false;
                    } elseif (NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN === $return) {
                        $traverseChildren = false;
                        break;
                    } elseif (NodeVisitor::STOP_TRAVERSAL === $return) {
                        $this->stopTraversal = true;
                        break 2;
                    } elseif (NodeVisitor::REPLACE_WITH_NULL === $return) {
                        throw new \LogicException(
                            'REPLACE_WITH_NULL can not be used if the parent structure is an array');
                    } else {
                        throw new \LogicException(
                            'enterNode() returned invalid value of type ' . gettype($return)
                        );
                    }
                }
            }

            if ($traverseChildren) {
                $this->traverseNode($node);
                if ($this->stopTraversal) {
                    break;
                }
            }

            for (; $visitorIndex >= 0; --$visitorIndex) {
                $visitor = $this->visitors[$visitorIndex];
                $return = $visitor->leaveNode($node);

                if (null !== $return) {
                    if ($return instanceof Node) {
                        $this->ensureReplacementReasonable($node, $return);
                        $nodes[$i] = $node = $return;
                    } elseif (\is_array($return)) {
                        $doNodes[] = [$i, $return];
                        break;
                    } elseif (NodeVisitor::REMOVE_NODE === $return) {
                        $doNodes[] = [$i, []];
                        break;
                    } elseif (NodeVisitor::STOP_TRAVERSAL === $return) {
                        $this->stopTraversal = true;
                        break 2;
                    } elseif (NodeVisitor::REPLACE_WITH_NULL === $return) {
                        throw new \LogicException(
                            'REPLACE_WITH_NULL can not be used if the parent structure is an array');
                    } else {
                        throw new \LogicException(
                            'leaveNode() returned invalid value of type ' . gettype($return)
                        );
                    }
                }
            }
        }

        if (!empty($doNodes)) {
            while (list($i, $replace) = array_pop($doNodes)) {
                array_splice($nodes, $i, 1, $replace);
            }
        }

        return $nodes;
    }

    private function ensureReplacementReasonable(Node $old, Node $new): void {
        if ($old instanceof Node\Stmt && $new instanceof Node\Expr) {
            throw new \LogicException(
                "Trying to replace statement ({$old->getType()}) " .
                "with expression ({$new->getType()}). Are you missing a " .
                "Stmt_Expression wrapper?"
            );
        }

        if ($old instanceof Node\Expr && $new instanceof Node\Stmt) {
            throw new \LogicException(
                "Trying to replace expression ({$old->getType()}) " .
                "with statement ({$new->getType()})"
            );
        }
    }
}
<?php declare(strict_types=1);

namespace PhpParser;

/**
 * A PHP token. On PHP 8.0 this extends from PhpToken.
 */
class Token extends Internal\TokenPolyfill {
    /** Get (exclusive) zero-based end position of the token. */
    public function getEndPos(): int {
        return $this->pos + \strlen($this->text);
    }

    /** Get 1-based end line number of the token. */
    public function getEndLine(): int {
        return $this->line + \substr_count($this->text, "\n");
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node;

use PhpParser\Node;
use PhpParser\NodeAbstract;

class Attribute extends NodeAbstract {
    /** @var Name Attribute name */
    public Name $name;

    /** @var list<Arg> Attribute arguments */
    public array $args;

    /**
     * @param Node\Name $name Attribute name
     * @param list<Arg> $args Attribute arguments
     * @param array<string, mixed> $attributes Additional node attributes
     */
    public function __construct(Name $name, array $args = [], array $attributes = []) {
        $this->attributes = $attributes;
        $this->name = $name;
        $this->args = $args;
    }

    public function getSubNodeNames(): array {
        return ['name', 'args'];
    }

    public function getType(): string {
        return 'Attribute';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node;

use PhpParser\Node;
use PhpParser\NodeAbstract;

class StaticVar extends NodeAbstract {
    /** @var Expr\Variable Variable */
    public Expr\Variable $var;
    /** @var null|Node\Expr Default value */
    public ?Expr $default;

    /**
     * Constructs a static variable node.
     *
     * @param Expr\Variable $var Name
     * @param null|Node\Expr $default Default value
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(
        Expr\Variable $var, ?Node\Expr $default = null, array $attributes = []
    ) {
        $this->attributes = $attributes;
        $this->var = $var;
        $this->default = $default;
    }

    public function getSubNodeNames(): array {
        return ['var', 'default'];
    }

    public function getType(): string {
        return 'StaticVar';
    }
}

// @deprecated compatibility alias
class_alias(StaticVar::class, Stmt\StaticVar::class);
<?php declare(strict_types=1);

namespace PhpParser\Node;

class IntersectionType extends ComplexType {
    /** @var (Identifier|Name)[] Types */
    public array $types;

    /**
     * Constructs an intersection type.
     *
     * @param (Identifier|Name)[] $types Types
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(array $types, array $attributes = []) {
        $this->attributes = $attributes;
        $this->types = $types;
    }

    public function getSubNodeNames(): array {
        return ['types'];
    }

    public function getType(): string {
        return 'IntersectionType';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Name;

class FullyQualified extends \PhpParser\Node\Name {
    /**
     * Checks whether the name is unqualified. (E.g. Name)
     *
     * @return bool Whether the name is unqualified
     */
    public function isUnqualified(): bool {
        return false;
    }

    /**
     * Checks whether the name is qualified. (E.g. Name\Name)
     *
     * @return bool Whether the name is qualified
     */
    public function isQualified(): bool {
        return false;
    }

    /**
     * Checks whether the name is fully qualified. (E.g. \Name)
     *
     * @return bool Whether the name is fully qualified
     */
    public function isFullyQualified(): bool {
        return true;
    }

    /**
     * Checks whether the name is explicitly relative to the current namespace. (E.g. namespace\Name)
     *
     * @return bool Whether the name is relative
     */
    public function isRelative(): bool {
        return false;
    }

    public function toCodeString(): string {
        return '\\' . $this->toString();
    }

    public function getType(): string {
        return 'Name_FullyQualified';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Name;

class Relative extends \PhpParser\Node\Name {
    /**
     * Checks whether the name is unqualified. (E.g. Name)
     *
     * @return bool Whether the name is unqualified
     */
    public function isUnqualified(): bool {
        return false;
    }

    /**
     * Checks whether the name is qualified. (E.g. Name\Name)
     *
     * @return bool Whether the name is qualified
     */
    public function isQualified(): bool {
        return false;
    }

    /**
     * Checks whether the name is fully qualified. (E.g. \Name)
     *
     * @return bool Whether the name is fully qualified
     */
    public function isFullyQualified(): bool {
        return false;
    }

    /**
     * Checks whether the name is explicitly relative to the current namespace. (E.g. namespace\Name)
     *
     * @return bool Whether the name is relative
     */
    public function isRelative(): bool {
        return true;
    }

    public function toCodeString(): string {
        return 'namespace\\' . $this->toString();
    }

    public function getType(): string {
        return 'Name_Relative';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node;

use PhpParser\Node;

interface FunctionLike extends Node {
    /**
     * Whether to return by reference
     */
    public function returnsByRef(): bool;

    /**
     * List of parameters
     *
     * @return Param[]
     */
    public function getParams(): array;

    /**
     * Get the declared return type or null
     *
     * @return null|Identifier|Name|ComplexType
     */
    public function getReturnType();

    /**
     * The function body
     *
     * @return Stmt[]|null
     */
    public function getStmts(): ?array;

    /**
     * Get PHP attribute groups.
     *
     * @return AttributeGroup[]
     */
    public function getAttrGroups(): array;
}
<?php declare(strict_types=1);

namespace PhpParser\Node;

use PhpParser\NodeAbstract;

class ArrayItem extends NodeAbstract {
    /** @var null|Expr Key */
    public ?Expr $key;
    /** @var Expr Value */
    public Expr $value;
    /** @var bool Whether to assign by reference */
    public bool $byRef;
    /** @var bool Whether to unpack the argument */
    public bool $unpack;

    /**
     * Constructs an array item node.
     *
     * @param Expr $value Value
     * @param null|Expr $key Key
     * @param bool $byRef Whether to assign by reference
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(Expr $value, ?Expr $key = null, bool $byRef = false, array $attributes = [], bool $unpack = false) {
        $this->attributes = $attributes;
        $this->key = $key;
        $this->value = $value;
        $this->byRef = $byRef;
        $this->unpack = $unpack;
    }

    public function getSubNodeNames(): array {
        return ['key', 'value', 'byRef', 'unpack'];
    }

    public function getType(): string {
        return 'ArrayItem';
    }
}

// @deprecated compatibility alias
class_alias(ArrayItem::class, Expr\ArrayItem::class);
<?php declare(strict_types=1);

namespace PhpParser\Node;

use PhpParser\NodeAbstract;

class Arg extends NodeAbstract {
    /** @var Identifier|null Parameter name (for named parameters) */
    public ?Identifier $name;
    /** @var Expr Value to pass */
    public Expr $value;
    /** @var bool Whether to pass by ref */
    public bool $byRef;
    /** @var bool Whether to unpack the argument */
    public bool $unpack;

    /**
     * Constructs a function call argument node.
     *
     * @param Expr $value Value to pass
     * @param bool $byRef Whether to pass by ref
     * @param bool $unpack Whether to unpack the argument
     * @param array<string, mixed> $attributes Additional attributes
     * @param Identifier|null $name Parameter name (for named parameters)
     */
    public function __construct(
        Expr $value, bool $byRef = false, bool $unpack = false, array $attributes = [],
        ?Identifier $name = null
    ) {
        $this->attributes = $attributes;
        $this->name = $name;
        $this->value = $value;
        $this->byRef = $byRef;
        $this->unpack = $unpack;
    }

    public function getSubNodeNames(): array {
        return ['name', 'value', 'byRef', 'unpack'];
    }

    public function getType(): string {
        return 'Arg';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node;

use PhpParser\NodeAbstract;

/**
 * This is a base class for complex types, including nullable types and union types.
 *
 * It does not provide any shared behavior and exists only for type-checking purposes.
 */
abstract class ComplexType extends NodeAbstract {
}
<?php declare(strict_types=1);

namespace PhpParser\Node;

use PhpParser\NodeAbstract;

class Name extends NodeAbstract {
    /**
     * @psalm-var non-empty-string
     * @var string Name as string
     */
    public string $name;

    /** @var array<string, bool> */
    private static array $specialClassNames = [
        'self'   => true,
        'parent' => true,
        'static' => true,
    ];

    /**
     * Constructs a name node.
     *
     * @param string|string[]|self $name Name as string, part array or Name instance (copy ctor)
     * @param array<string, mixed> $attributes Additional attributes
     */
    final public function __construct($name, array $attributes = []) {
        $this->attributes = $attributes;
        $this->name = self::prepareName($name);
    }

    public function getSubNodeNames(): array {
        return ['name'];
    }

    /**
     * Get parts of name (split by the namespace separator).
     *
     * @psalm-return non-empty-list<string>
     * @return string[] Parts of name
     */
    public function getParts(): array {
        return \explode('\\', $this->name);
    }

    /**
     * Gets the first part of the name, i.e. everything before the first namespace separator.
     *
     * @return string First part of the name
     */
    public function getFirst(): string {
        if (false !== $pos = \strpos($this->name, '\\')) {
            return \substr($this->name, 0, $pos);
        }
        return $this->name;
    }

    /**
     * Gets the last part of the name, i.e. everything after the last namespace separator.
     *
     * @return string Last part of the name
     */
    public function getLast(): string {
        if (false !== $pos = \strrpos($this->name, '\\')) {
            return \substr($this->name, $pos + 1);
        }
        return $this->name;
    }

    /**
     * Checks whether the name is unqualified. (E.g. Name)
     *
     * @return bool Whether the name is unqualified
     */
    public function isUnqualified(): bool {
        return false === \strpos($this->name, '\\');
    }

    /**
     * Checks whether the name is qualified. (E.g. Name\Name)
     *
     * @return bool Whether the name is qualified
     */
    public function isQualified(): bool {
        return false !== \strpos($this->name, '\\');
    }

    /**
     * Checks whether the name is fully qualified. (E.g. \Name)
     *
     * @return bool Whether the name is fully qualified
     */
    public function isFullyQualified(): bool {
        return false;
    }

    /**
     * Checks whether the name is explicitly relative to the current namespace. (E.g. namespace\Name)
     *
     * @return bool Whether the name is relative
     */
    public function isRelative(): bool {
        return false;
    }

    /**
     * Returns a string representation of the name itself, without taking the name type into
     * account (e.g., not including a leading backslash for fully qualified names).
     *
     * @psalm-return non-empty-string
     * @return string String representation
     */
    public function toString(): string {
        return $this->name;
    }

    /**
     * Returns a string representation of the name as it would occur in code (e.g., including
     * leading backslash for fully qualified names.
     *
     * @psalm-return non-empty-string
     * @return string String representation
     */
    public function toCodeString(): string {
        return $this->toString();
    }

    /**
     * Returns lowercased string representation of the name, without taking the name type into
     * account (e.g., no leading backslash for fully qualified names).
     *
     * @psalm-return non-empty-string&lowercase-string
     * @return string Lowercased string representation
     */
    public function toLowerString(): string {
        return strtolower($this->name);
    }

    /**
     * Checks whether the identifier is a special class name (self, parent or static).
     *
     * @return bool Whether identifier is a special class name
     */
    public function isSpecialClassName(): bool {
        return isset(self::$specialClassNames[strtolower($this->name)]);
    }

    /**
     * Returns a string representation of the name by imploding the namespace parts with the
     * namespace separator.
     *
     * @psalm-return non-empty-string
     * @return string String representation
     */
    public function __toString(): string {
        return $this->name;
    }

    /**
     * Gets a slice of a name (similar to array_slice).
     *
     * This method returns a new instance of the same type as the original and with the same
     * attributes.
     *
     * If the slice is empty, null is returned. The null value will be correctly handled in
     * concatenations using concat().
     *
     * Offset and length have the same meaning as in array_slice().
     *
     * @param int $offset Offset to start the slice at (may be negative)
     * @param int|null $length Length of the slice (may be negative)
     *
     * @return static|null Sliced name
     */
    public function slice(int $offset, ?int $length = null) {
        if ($offset === 1 && $length === null) {
            // Short-circuit the common case.
            if (false !== $pos = \strpos($this->name, '\\')) {
                return new static(\substr($this->name, $pos + 1));
            }
            return null;
        }

        $parts = \explode('\\', $this->name);
        $numParts = \count($parts);

        $realOffset = $offset < 0 ? $offset + $numParts : $offset;
        if ($realOffset < 0 || $realOffset > $numParts) {
            throw new \OutOfBoundsException(sprintf('Offset %d is out of bounds', $offset));
        }

        if (null === $length) {
            $realLength = $numParts - $realOffset;
        } else {
            $realLength = $length < 0 ? $length + $numParts - $realOffset : $length;
            if ($realLength < 0 || $realLength > $numParts - $realOffset) {
                throw new \OutOfBoundsException(sprintf('Length %d is out of bounds', $length));
            }
        }

        if ($realLength === 0) {
            // Empty slice is represented as null
            return null;
        }

        return new static(array_slice($parts, $realOffset, $realLength), $this->attributes);
    }

    /**
     * Concatenate two names, yielding a new Name instance.
     *
     * The type of the generated instance depends on which class this method is called on, for
     * example Name\FullyQualified::concat() will yield a Name\FullyQualified instance.
     *
     * If one of the arguments is null, a new instance of the other name will be returned. If both
     * arguments are null, null will be returned. As such, writing
     *     Name::concat($namespace, $shortName)
     * where $namespace is a Name node or null will work as expected.
     *
     * @param string|string[]|self|null $name1 The first name
     * @param string|string[]|self|null $name2 The second name
     * @param array<string, mixed> $attributes Attributes to assign to concatenated name
     *
     * @return static|null Concatenated name
     */
    public static function concat($name1, $name2, array $attributes = []) {
        if (null === $name1 && null === $name2) {
            return null;
        }
        if (null === $name1) {
            return new static($name2, $attributes);
        }
        if (null === $name2) {
            return new static($name1, $attributes);
        } else {
            return new static(
                self::prepareName($name1) . '\\' . self::prepareName($name2), $attributes
            );
        }
    }

    /**
     * Prepares a (string, array or Name node) name for use in name changing methods by converting
     * it to a string.
     *
     * @param string|string[]|self $name Name to prepare
     *
     * @psalm-return non-empty-string
     * @return string Prepared name
     */
    private static function prepareName($name): string {
        if (\is_string($name)) {
            if ('' === $name) {
                throw new \InvalidArgumentException('Name cannot be empty');
            }

            return $name;
        }
        if (\is_array($name)) {
            if (empty($name)) {
                throw new \InvalidArgumentException('Name cannot be empty');
            }

            return implode('\\', $name);
        }
        if ($name instanceof self) {
            return $name->name;
        }

        throw new \InvalidArgumentException(
            'Expected string, array of parts or Name instance'
        );
    }

    public function getType(): string {
        return 'Name';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node;

/**
 * Represents a name that is written in source code with a leading dollar,
 * but is not a proper variable. The leading dollar is not stored as part of the name.
 *
 * Examples: Names in property declarations are formatted as variables. Names in static property
 * lookups are also formatted as variables.
 */
class VarLikeIdentifier extends Identifier {
    public function getType(): string {
        return 'VarLikeIdentifier';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node;

use PhpParser\Node;
use PhpParser\NodeAbstract;

class MatchArm extends NodeAbstract {
    /** @var null|list<Node\Expr> */
    public ?array $conds;
    public Expr $body;

    /**
     * @param null|list<Node\Expr> $conds
     */
    public function __construct(?array $conds, Node\Expr $body, array $attributes = []) {
        $this->conds = $conds;
        $this->body = $body;
        $this->attributes = $attributes;
    }

    public function getSubNodeNames(): array {
        return ['conds', 'body'];
    }

    public function getType(): string {
        return 'MatchArm';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node;

use PhpParser\Modifiers;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Stmt\Expression;
use PhpParser\Node\Stmt\Return_;
use PhpParser\NodeAbstract;

class PropertyHook extends NodeAbstract implements FunctionLike {
    /** @var AttributeGroup[] PHP attribute groups */
    public array $attrGroups;
    /** @var int Modifiers */
    public int $flags;
    /** @var bool Whether hook returns by reference */
    public bool $byRef;
    /** @var Identifier Hook name */
    public Identifier $name;
    /** @var Param[] Parameters */
    public array $params;
    /** @var null|Expr|Stmt[] Hook body */
    public $body;

    /**
     * Constructs a property hook node.
     *
     * @param string|Identifier $name Hook name
     * @param null|Expr|Stmt[] $body Hook body
     * @param array{
     *     flags?: int,
     *     byRef?: bool,
     *     params?: Param[],
     *     attrGroups?: AttributeGroup[],
     * } $subNodes Array of the following optional subnodes:
     *             'flags       => 0      : Flags
     *             'byRef'      => false  : Whether hook returns by reference
     *             'params'     => array(): Parameters
     *             'attrGroups' => array(): PHP attribute groups
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct($name, $body, array $subNodes = [], array $attributes = []) {
        $this->attributes = $attributes;
        $this->name = \is_string($name) ? new Identifier($name) : $name;
        $this->body = $body;
        $this->flags = $subNodes['flags'] ?? 0;
        $this->byRef = $subNodes['byRef'] ?? false;
        $this->params = $subNodes['params'] ?? [];
        $this->attrGroups = $subNodes['attrGroups'] ?? [];
    }

    public function returnsByRef(): bool {
        return $this->byRef;
    }

    public function getParams(): array {
        return $this->params;
    }

    public function getReturnType() {
        return null;
    }

    /**
     * Whether the property hook is final.
     */
    public function isFinal(): bool {
        return (bool) ($this->flags & Modifiers::FINAL);
    }

    public function getStmts(): ?array {
        if ($this->body instanceof Expr) {
            $name = $this->name->toLowerString();
            if ($name === 'get') {
                return [new Return_($this->body)];
            }
            if ($name === 'set') {
                if (!$this->hasAttribute('propertyName')) {
                    throw new \LogicException(
                        'Can only use getStmts() on a "set" hook if the "propertyName" attribute is set');
                }

                $propName = $this->getAttribute('propertyName');
                $prop = new PropertyFetch(new Variable('this'), (string) $propName);
                return [new Expression(new Assign($prop, $this->body))];
            }
            throw new \LogicException('Unknown property hook "' . $name . '"');
        }
        return $this->body;
    }

    public function getAttrGroups(): array {
        return $this->attrGroups;
    }

    public function getType(): string {
        return 'PropertyHook';
    }

    public function getSubNodeNames(): array {
        return ['attrGroups', 'flags', 'byRef', 'name', 'params', 'body'];
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node;

use PhpParser\Modifiers;
use PhpParser\Node;
use PhpParser\NodeAbstract;

class Param extends NodeAbstract {
    /** @var null|Identifier|Name|ComplexType Type declaration */
    public ?Node $type;
    /** @var bool Whether parameter is passed by reference */
    public bool $byRef;
    /** @var bool Whether this is a variadic argument */
    public bool $variadic;
    /** @var Expr\Variable|Expr\Error Parameter variable */
    public Expr $var;
    /** @var null|Expr Default value */
    public ?Expr $default;
    /** @var int Optional visibility flags */
    public int $flags;
    /** @var AttributeGroup[] PHP attribute groups */
    public array $attrGroups;
    /** @var PropertyHook[] Property hooks for promoted properties */
    public array $hooks;

    /**
     * Constructs a parameter node.
     *
     * @param Expr\Variable|Expr\Error $var Parameter variable
     * @param null|Expr $default Default value
     * @param null|Identifier|Name|ComplexType $type Type declaration
     * @param bool $byRef Whether is passed by reference
     * @param bool $variadic Whether this is a variadic argument
     * @param array<string, mixed> $attributes Additional attributes
     * @param int $flags Optional visibility flags
     * @param list<AttributeGroup> $attrGroups PHP attribute groups
     * @param PropertyHook[] $hooks Property hooks for promoted properties
     */
    public function __construct(
        Expr $var, ?Expr $default = null, ?Node $type = null,
        bool $byRef = false, bool $variadic = false,
        array $attributes = [],
        int $flags = 0,
        array $attrGroups = [],
        array $hooks = []
    ) {
        $this->attributes = $attributes;
        $this->type = $type;
        $this->byRef = $byRef;
        $this->variadic = $variadic;
        $this->var = $var;
        $this->default = $default;
        $this->flags = $flags;
        $this->attrGroups = $attrGroups;
        $this->hooks = $hooks;
    }

    public function getSubNodeNames(): array {
        return ['attrGroups', 'flags', 'type', 'byRef', 'variadic', 'var', 'default', 'hooks'];
    }

    public function getType(): string {
        return 'Param';
    }

    /**
     * Whether this parameter uses constructor property promotion.
     */
    public function isPromoted(): bool {
        return $this->flags !== 0 || $this->hooks !== [];
    }

    public function isFinal(): bool {
        return (bool) ($this->flags & Modifiers::FINAL);
    }

    public function isPublic(): bool {
        $public = (bool) ($this->flags & Modifiers::PUBLIC);
        if ($public) {
            return true;
        }

        if (!$this->isPromoted()) {
            return false;
        }

        return ($this->flags & Modifiers::VISIBILITY_MASK) === 0;
    }

    public function isProtected(): bool {
        return (bool) ($this->flags & Modifiers::PROTECTED);
    }

    public function isPrivate(): bool {
        return (bool) ($this->flags & Modifiers::PRIVATE);
    }

    public function isReadonly(): bool {
        return (bool) ($this->flags & Modifiers::READONLY);
    }

    /**
     * Whether the promoted property has explicit public(set) visibility.
     */
    public function isPublicSet(): bool {
        return (bool) ($this->flags & Modifiers::PUBLIC_SET);
    }

    /**
     * Whether the promoted property has explicit protected(set) visibility.
     */
    public function isProtectedSet(): bool {
        return (bool) ($this->flags & Modifiers::PROTECTED_SET);
    }

    /**
     * Whether the promoted property has explicit private(set) visibility.
     */
    public function isPrivateSet(): bool {
        return (bool) ($this->flags & Modifiers::PRIVATE_SET);
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node;

use PhpParser\NodeAbstract;

abstract class Expr extends NodeAbstract {
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

class PostDec extends Expr {
    /** @var Expr Variable */
    public Expr $var;

    /**
     * Constructs a post decrement node.
     *
     * @param Expr $var Variable
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(Expr $var, array $attributes = []) {
        $this->attributes = $attributes;
        $this->var = $var;
    }

    public function getSubNodeNames(): array {
        return ['var'];
    }

    public function getType(): string {
        return 'Expr_PostDec';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

class UnaryPlus extends Expr {
    /** @var Expr Expression */
    public Expr $expr;

    /**
     * Constructs a unary plus node.
     *
     * @param Expr $expr Expression
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(Expr $expr, array $attributes = []) {
        $this->attributes = $attributes;
        $this->expr = $expr;
    }

    public function getSubNodeNames(): array {
        return ['expr'];
    }

    public function getType(): string {
        return 'Expr_UnaryPlus';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\ArgPlaceholder;
use PhpParser\Node\Expr;
use PhpParser\Node\Identifier;
use PhpParser\Node\VariadicPlaceholder;

class NullsafeMethodCall extends CallLike {
    /** @var Expr Variable holding object */
    public Expr $var;
    /** @var Identifier|Expr Method name */
    public Node $name;
    /** @var array<Arg|VariadicPlaceholder|ArgPlaceholder> Arguments */
    public array $args;

    /**
     * Constructs a nullsafe method call node.
     *
     * @param Expr $var Variable holding object
     * @param string|Identifier|Expr $name Method name
     * @param array<Arg|VariadicPlaceholder|ArgPlaceholder> $args Arguments
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(Expr $var, $name, array $args = [], array $attributes = []) {
        $this->attributes = $attributes;
        $this->var = $var;
        $this->name = \is_string($name) ? new Identifier($name) : $name;
        $this->args = $args;
    }

    public function getSubNodeNames(): array {
        return ['var', 'name', 'args'];
    }

    public function getType(): string {
        return 'Expr_NullsafeMethodCall';
    }

    public function getRawArgs(): array {
        return $this->args;
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\ArgPlaceholder;
use PhpParser\Node\Expr;
use PhpParser\Node\Identifier;
use PhpParser\Node\VariadicPlaceholder;

class StaticCall extends CallLike {
    /** @var Node\Name|Expr Class name */
    public Node $class;
    /** @var Identifier|Expr Method name */
    public Node $name;
    /** @var array<Arg|VariadicPlaceholder|ArgPlaceholder> Arguments */
    public array $args;

    /**
     * Constructs a static method call node.
     *
     * @param Node\Name|Expr $class Class name
     * @param string|Identifier|Expr $name Method name
     * @param array<Arg|VariadicPlaceholder|ArgPlaceholder> $args Arguments
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(Node $class, $name, array $args = [], array $attributes = []) {
        $this->attributes = $attributes;
        $this->class = $class;
        $this->name = \is_string($name) ? new Identifier($name) : $name;
        $this->args = $args;
    }

    public function getSubNodeNames(): array {
        return ['class', 'name', 'args'];
    }

    public function getType(): string {
        return 'Expr_StaticCall';
    }

    public function getRawArgs(): array {
        return $this->args;
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node;
use PhpParser\Node\MatchArm;

class Match_ extends Node\Expr {
    /** @var Node\Expr Condition */
    public Node\Expr $cond;
    /** @var MatchArm[] */
    public array $arms;

    /**
     * @param Node\Expr $cond Condition
     * @param MatchArm[] $arms
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(Node\Expr $cond, array $arms = [], array $attributes = []) {
        $this->attributes = $attributes;
        $this->cond = $cond;
        $this->arms = $arms;
    }

    public function getSubNodeNames(): array {
        return ['cond', 'arms'];
    }

    public function getType(): string {
        return 'Expr_Match';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Name;
use PhpParser\Node\VarLikeIdentifier;

class StaticPropertyFetch extends Expr {
    /** @var Name|Expr Class name */
    public Node $class;
    /** @var VarLikeIdentifier|Expr Property name */
    public Node $name;

    /**
     * Constructs a static property fetch node.
     *
     * @param Name|Expr $class Class name
     * @param string|VarLikeIdentifier|Expr $name Property name
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(Node $class, $name, array $attributes = []) {
        $this->attributes = $attributes;
        $this->class = $class;
        $this->name = \is_string($name) ? new VarLikeIdentifier($name) : $name;
    }

    public function getSubNodeNames(): array {
        return ['class', 'name'];
    }

    public function getType(): string {
        return 'Expr_StaticPropertyFetch';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

abstract class AssignOp extends Expr {
    /** @var Expr Variable */
    public Expr $var;
    /** @var Expr Expression */
    public Expr $expr;

    /**
     * Constructs a compound assignment operation node.
     *
     * @param Expr $var Variable
     * @param Expr $expr Expression
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(Expr $var, Expr $expr, array $attributes = []) {
        $this->attributes = $attributes;
        $this->var = $var;
        $this->expr = $expr;
    }

    public function getSubNodeNames(): array {
        return ['var', 'expr'];
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

class YieldFrom extends Expr {
    /** @var Expr Expression to yield from */
    public Expr $expr;

    /**
     * Constructs an "yield from" node.
     *
     * @param Expr $expr Expression
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(Expr $expr, array $attributes = []) {
        $this->attributes = $attributes;
        $this->expr = $expr;
    }

    public function getSubNodeNames(): array {
        return ['expr'];
    }

    public function getType(): string {
        return 'Expr_YieldFrom';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

class PreDec extends Expr {
    /** @var Expr Variable */
    public Expr $var;

    /**
     * Constructs a pre decrement node.
     *
     * @param Expr $var Variable
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(Expr $var, array $attributes = []) {
        $this->attributes = $attributes;
        $this->var = $var;
    }

    public function getSubNodeNames(): array {
        return ['var'];
    }

    public function getType(): string {
        return 'Expr_PreDec';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

class Yield_ extends Expr {
    /** @var null|Expr Key expression */
    public ?Expr $key;
    /** @var null|Expr Value expression */
    public ?Expr $value;

    /**
     * Constructs a yield expression node.
     *
     * @param null|Expr $value Value expression
     * @param null|Expr $key Key expression
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(?Expr $value = null, ?Expr $key = null, array $attributes = []) {
        $this->attributes = $attributes;
        $this->key = $key;
        $this->value = $value;
    }

    public function getSubNodeNames(): array {
        return ['key', 'value'];
    }

    public function getType(): string {
        return 'Expr_Yield';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\FunctionLike;

class ArrowFunction extends Expr implements FunctionLike {
    /** @var bool Whether the closure is static */
    public bool $static;

    /** @var bool Whether to return by reference */
    public bool $byRef;

    /** @var Node\Param[] */
    public array $params = [];

    /** @var null|Node\Identifier|Node\Name|Node\ComplexType */
    public ?Node $returnType;

    /** @var Expr Expression body */
    public Expr $expr;
    /** @var Node\AttributeGroup[] */
    public array $attrGroups;

    /**
     * @param array{
     *     expr: Expr,
     *     static?: bool,
     *     byRef?: bool,
     *     params?: Node\Param[],
     *     returnType?: null|Node\Identifier|Node\Name|Node\ComplexType,
     *     attrGroups?: Node\AttributeGroup[]
     * } $subNodes Array of the following subnodes:
     *             'expr'                  : Expression body
     *             'static'     => false   : Whether the closure is static
     *             'byRef'      => false   : Whether to return by reference
     *             'params'     => array() : Parameters
     *             'returnType' => null    : Return type
     *             'attrGroups' => array() : PHP attribute groups
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(array $subNodes, array $attributes = []) {
        $this->attributes = $attributes;
        $this->static = $subNodes['static'] ?? false;
        $this->byRef = $subNodes['byRef'] ?? false;
        $this->params = $subNodes['params'] ?? [];
        $this->returnType = $subNodes['returnType'] ?? null;
        $this->expr = $subNodes['expr'];
        $this->attrGroups = $subNodes['attrGroups'] ?? [];
    }

    public function getSubNodeNames(): array {
        return ['attrGroups', 'static', 'byRef', 'params', 'returnType', 'expr'];
    }

    public function returnsByRef(): bool {
        return $this->byRef;
    }

    public function getParams(): array {
        return $this->params;
    }

    public function getReturnType() {
        return $this->returnType;
    }

    public function getAttrGroups(): array {
        return $this->attrGroups;
    }

    /**
     * @return Node\Stmt\Return_[]
     */
    public function getStmts(): array {
        return [new Node\Stmt\Return_($this->expr)];
    }

    public function getType(): string {
        return 'Expr_ArrowFunction';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

require __DIR__ . '/../ArrayItem.php';

if (false) {
    /**
     * For classmap-authoritative support.
     *
     * @deprecated use \PhpParser\Node\ArrayItem instead.
     */
    class ArrayItem extends \PhpParser\Node\ArrayItem {
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

class Isset_ extends Expr {
    /** @var Expr[] Variables */
    public array $vars;

    /**
     * Constructs an array node.
     *
     * @param Expr[] $vars Variables
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(array $vars, array $attributes = []) {
        $this->attributes = $attributes;
        $this->vars = $vars;
    }

    public function getSubNodeNames(): array {
        return ['vars'];
    }

    public function getType(): string {
        return 'Expr_Isset';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

class BitwiseNot extends Expr {
    /** @var Expr Expression */
    public Expr $expr;

    /**
     * Constructs a bitwise not node.
     *
     * @param Expr $expr Expression
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(Expr $expr, array $attributes = []) {
        $this->attributes = $attributes;
        $this->expr = $expr;
    }

    public function getSubNodeNames(): array {
        return ['expr'];
    }

    public function getType(): string {
        return 'Expr_BitwiseNot';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;
use PhpParser\Node\Name;

class ConstFetch extends Expr {
    /** @var Name Constant name */
    public Name $name;

    /**
     * Constructs a const fetch node.
     *
     * @param Name $name Constant name
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(Name $name, array $attributes = []) {
        $this->attributes = $attributes;
        $this->name = $name;
    }

    public function getSubNodeNames(): array {
        return ['name'];
    }

    public function getType(): string {
        return 'Expr_ConstFetch';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;

class ClassConstFetch extends Expr {
    /** @var Name|Expr Class name */
    public Node $class;
    /** @var Identifier|Expr|Error Constant name */
    public Node $name;

    /**
     * Constructs a class const fetch node.
     *
     * @param Name|Expr $class Class name
     * @param string|Identifier|Expr|Error $name Constant name
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(Node $class, $name, array $attributes = []) {
        $this->attributes = $attributes;
        $this->class = $class;
        $this->name = \is_string($name) ? new Identifier($name) : $name;
    }

    public function getSubNodeNames(): array {
        return ['class', 'name'];
    }

    public function getType(): string {
        return 'Expr_ClassConstFetch';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

class Ternary extends Expr {
    /** @var Expr Condition */
    public Expr $cond;
    /** @var null|Expr Expression for true */
    public ?Expr $if;
    /** @var Expr Expression for false */
    public Expr $else;

    /**
     * Constructs a ternary operator node.
     *
     * @param Expr $cond Condition
     * @param null|Expr $if Expression for true
     * @param Expr $else Expression for false
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(Expr $cond, ?Expr $if, Expr $else, array $attributes = []) {
        $this->attributes = $attributes;
        $this->cond = $cond;
        $this->if = $if;
        $this->else = $else;
    }

    public function getSubNodeNames(): array {
        return ['cond', 'if', 'else'];
    }

    public function getType(): string {
        return 'Expr_Ternary';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node;
use PhpParser\Node\ClosureUse;
use PhpParser\Node\Expr;
use PhpParser\Node\FunctionLike;

class Closure extends Expr implements FunctionLike {
    /** @var bool Whether the closure is static */
    public bool $static;
    /** @var bool Whether to return by reference */
    public bool $byRef;
    /** @var Node\Param[] Parameters */
    public array $params;
    /** @var ClosureUse[] use()s */
    public array $uses;
    /** @var null|Node\Identifier|Node\Name|Node\ComplexType Return type */
    public ?Node $returnType;
    /** @var Node\Stmt[] Statements */
    public array $stmts;
    /** @var Node\AttributeGroup[] PHP attribute groups */
    public array $attrGroups;

    /**
     * Constructs a lambda function node.
     *
     * @param array{
     *     static?: bool,
     *     byRef?: bool,
     *     params?: Node\Param[],
     *     uses?: ClosureUse[],
     *     returnType?: null|Node\Identifier|Node\Name|Node\ComplexType,
     *     stmts?: Node\Stmt[],
     *     attrGroups?: Node\AttributeGroup[],
     * } $subNodes Array of the following optional subnodes:
     *             'static'     => false  : Whether the closure is static
     *             'byRef'      => false  : Whether to return by reference
     *             'params'     => array(): Parameters
     *             'uses'       => array(): use()s
     *             'returnType' => null   : Return type
     *             'stmts'      => array(): Statements
     *             'attrGroups' => array(): PHP attributes groups
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(array $subNodes = [], array $attributes = []) {
        $this->attributes = $attributes;
        $this->static = $subNodes['static'] ?? false;
        $this->byRef = $subNodes['byRef'] ?? false;
        $this->params = $subNodes['params'] ?? [];
        $this->uses = $subNodes['uses'] ?? [];
        $this->returnType = $subNodes['returnType'] ?? null;
        $this->stmts = $subNodes['stmts'] ?? [];
        $this->attrGroups = $subNodes['attrGroups'] ?? [];
    }

    public function getSubNodeNames(): array {
        return ['attrGroups', 'static', 'byRef', 'params', 'uses', 'returnType', 'stmts'];
    }

    public function returnsByRef(): bool {
        return $this->byRef;
    }

    public function getParams(): array {
        return $this->params;
    }

    public function getReturnType() {
        return $this->returnType;
    }

    /** @return Node\Stmt[] */
    public function getStmts(): array {
        return $this->stmts;
    }

    public function getAttrGroups(): array {
        return $this->attrGroups;
    }

    public function getType(): string {
        return 'Expr_Closure';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;
use PhpParser\Node\InterpolatedStringPart;

class ShellExec extends Expr {
    /** @var (Expr|InterpolatedStringPart)[] Interpolated string array */
    public array $parts;

    /**
     * Constructs a shell exec (backtick) node.
     *
     * @param (Expr|InterpolatedStringPart)[] $parts Interpolated string array
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(array $parts, array $attributes = []) {
        $this->attributes = $attributes;
        $this->parts = $parts;
    }

    public function getSubNodeNames(): array {
        return ['parts'];
    }

    public function getType(): string {
        return 'Expr_ShellExec';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Identifier;

class PropertyFetch extends Expr {
    /** @var Expr Variable holding object */
    public Expr $var;
    /** @var Identifier|Expr Property name */
    public Node $name;

    /**
     * Constructs a function call node.
     *
     * @param Expr $var Variable holding object
     * @param string|Identifier|Expr $name Property name
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(Expr $var, $name, array $attributes = []) {
        $this->attributes = $attributes;
        $this->var = $var;
        $this->name = \is_string($name) ? new Identifier($name) : $name;
    }

    public function getSubNodeNames(): array {
        return ['var', 'name'];
    }

    public function getType(): string {
        return 'Expr_PropertyFetch';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\ArgPlaceholder;
use PhpParser\Node\Expr;
use PhpParser\Node\Identifier;
use PhpParser\Node\VariadicPlaceholder;

class MethodCall extends CallLike {
    /** @var Expr Variable holding object */
    public Expr $var;
    /** @var Identifier|Expr Method name */
    public Node $name;
    /** @var array<Arg|VariadicPlaceholder|ArgPlaceholder> Arguments */
    public array $args;

    /**
     * Constructs a function call node.
     *
     * @param Expr $var Variable holding object
     * @param string|Identifier|Expr $name Method name
     * @param array<Arg|VariadicPlaceholder|ArgPlaceholder> $args Arguments
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(Expr $var, $name, array $args = [], array $attributes = []) {
        $this->attributes = $attributes;
        $this->var = $var;
        $this->name = \is_string($name) ? new Identifier($name) : $name;
        $this->args = $args;
    }

    public function getSubNodeNames(): array {
        return ['var', 'name', 'args'];
    }

    public function getType(): string {
        return 'Expr_MethodCall';
    }

    public function getRawArgs(): array {
        return $this->args;
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

class Include_ extends Expr {
    public const TYPE_INCLUDE      = 1;
    public const TYPE_INCLUDE_ONCE = 2;
    public const TYPE_REQUIRE      = 3;
    public const TYPE_REQUIRE_ONCE = 4;

    /** @var Expr Expression */
    public Expr $expr;
    /** @var int Type of include */
    public int $type;

    /**
     * Constructs an include node.
     *
     * @param Expr $expr Expression
     * @param int $type Type of include
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(Expr $expr, int $type, array $attributes = []) {
        $this->attributes = $attributes;
        $this->expr = $expr;
        $this->type = $type;
    }

    public function getSubNodeNames(): array {
        return ['expr', 'type'];
    }

    public function getType(): string {
        return 'Expr_Include';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

class Eval_ extends Expr {
    /** @var Expr Expression */
    public Expr $expr;

    /**
     * Constructs an eval() node.
     *
     * @param Expr $expr Expression
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(Expr $expr, array $attributes = []) {
        $this->attributes = $attributes;
        $this->expr = $expr;
    }

    public function getSubNodeNames(): array {
        return ['expr'];
    }

    public function getType(): string {
        return 'Expr_Eval';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node;
use PhpParser\Node\Expr;

class FuncCall extends CallLike {
    /** @var Node\Name|Expr Function name */
    public Node $name;
    /** @var array<Node\Arg|Node\VariadicPlaceholder|Node\ArgPlaceholder> Arguments */
    public array $args;

    /**
     * Constructs a function call node.
     *
     * @param Node\Name|Expr $name Function name
     * @param array<Node\Arg|Node\VariadicPlaceholder|Node\ArgPlaceholder> $args Arguments
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(Node $name, array $args = [], array $attributes = []) {
        $this->attributes = $attributes;
        $this->name = $name;
        $this->args = $args;
    }

    public function getSubNodeNames(): array {
        return ['name', 'args'];
    }

    public function getType(): string {
        return 'Expr_FuncCall';
    }

    public function getRawArgs(): array {
        return $this->args;
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class Equal extends BinaryOp {
    public function getOperatorSigil(): string {
        return '==';
    }

    public function getType(): string {
        return 'Expr_BinaryOp_Equal';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class ShiftRight extends BinaryOp {
    public function getOperatorSigil(): string {
        return '>>';
    }

    public function getType(): string {
        return 'Expr_BinaryOp_ShiftRight';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class LogicalAnd extends BinaryOp {
    public function getOperatorSigil(): string {
        return 'and';
    }

    public function getType(): string {
        return 'Expr_BinaryOp_LogicalAnd';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class Div extends BinaryOp {
    public function getOperatorSigil(): string {
        return '/';
    }

    public function getType(): string {
        return 'Expr_BinaryOp_Div';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class Mul extends BinaryOp {
    public function getOperatorSigil(): string {
        return '*';
    }

    public function getType(): string {
        return 'Expr_BinaryOp_Mul';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class Mod extends BinaryOp {
    public function getOperatorSigil(): string {
        return '%';
    }

    public function getType(): string {
        return 'Expr_BinaryOp_Mod';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class BitwiseOr extends BinaryOp {
    public function getOperatorSigil(): string {
        return '|';
    }

    public function getType(): string {
        return 'Expr_BinaryOp_BitwiseOr';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class Smaller extends BinaryOp {
    public function getOperatorSigil(): string {
        return '<';
    }

    public function getType(): string {
        return 'Expr_BinaryOp_Smaller';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class Concat extends BinaryOp {
    public function getOperatorSigil(): string {
        return '.';
    }

    public function getType(): string {
        return 'Expr_BinaryOp_Concat';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class Plus extends BinaryOp {
    public function getOperatorSigil(): string {
        return '+';
    }

    public function getType(): string {
        return 'Expr_BinaryOp_Plus';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class BitwiseXor extends BinaryOp {
    public function getOperatorSigil(): string {
        return '^';
    }

    public function getType(): string {
        return 'Expr_BinaryOp_BitwiseXor';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class ShiftLeft extends BinaryOp {
    public function getOperatorSigil(): string {
        return '<<';
    }

    public function getType(): string {
        return 'Expr_BinaryOp_ShiftLeft';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class Pipe extends BinaryOp {
    public function getOperatorSigil(): string {
        return '|>';
    }

    public function getType(): string {
        return 'Expr_BinaryOp_Pipe';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class BooleanOr extends BinaryOp {
    public function getOperatorSigil(): string {
        return '||';
    }

    public function getType(): string {
        return 'Expr_BinaryOp_BooleanOr';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class Coalesce extends BinaryOp {
    public function getOperatorSigil(): string {
        return '??';
    }

    public function getType(): string {
        return 'Expr_BinaryOp_Coalesce';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class Spaceship extends BinaryOp {
    public function getOperatorSigil(): string {
        return '<=>';
    }

    public function getType(): string {
        return 'Expr_BinaryOp_Spaceship';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class LogicalXor extends BinaryOp {
    public function getOperatorSigil(): string {
        return 'xor';
    }

    public function getType(): string {
        return 'Expr_BinaryOp_LogicalXor';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class BitwiseAnd extends BinaryOp {
    public function getOperatorSigil(): string {
        return '&';
    }

    public function getType(): string {
        return 'Expr_BinaryOp_BitwiseAnd';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class SmallerOrEqual extends BinaryOp {
    public function getOperatorSigil(): string {
        return '<=';
    }

    public function getType(): string {
        return 'Expr_BinaryOp_SmallerOrEqual';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class Minus extends BinaryOp {
    public function getOperatorSigil(): string {
        return '-';
    }

    public function getType(): string {
        return 'Expr_BinaryOp_Minus';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class Greater extends BinaryOp {
    public function getOperatorSigil(): string {
        return '>';
    }

    public function getType(): string {
        return 'Expr_BinaryOp_Greater';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class BooleanAnd extends BinaryOp {
    public function getOperatorSigil(): string {
        return '&&';
    }

    public function getType(): string {
        return 'Expr_BinaryOp_BooleanAnd';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class Pow extends BinaryOp {
    public function getOperatorSigil(): string {
        return '**';
    }

    public function getType(): string {
        return 'Expr_BinaryOp_Pow';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class NotIdentical extends BinaryOp {
    public function getOperatorSigil(): string {
        return '!==';
    }

    public function getType(): string {
        return 'Expr_BinaryOp_NotIdentical';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class GreaterOrEqual extends BinaryOp {
    public function getOperatorSigil(): string {
        return '>=';
    }

    public function getType(): string {
        return 'Expr_BinaryOp_GreaterOrEqual';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class Identical extends BinaryOp {
    public function getOperatorSigil(): string {
        return '===';
    }

    public function getType(): string {
        return 'Expr_BinaryOp_Identical';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class LogicalOr extends BinaryOp {
    public function getOperatorSigil(): string {
        return 'or';
    }

    public function getType(): string {
        return 'Expr_BinaryOp_LogicalOr';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\BinaryOp;

use PhpParser\Node\Expr\BinaryOp;

class NotEqual extends BinaryOp {
    public function getOperatorSigil(): string {
        return '!=';
    }

    public function getType(): string {
        return 'Expr_BinaryOp_NotEqual';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\ArrayItem;
use PhpParser\Node\Expr;

class Array_ extends Expr {
    // For use in "kind" attribute
    public const KIND_LONG = 1;  // array() syntax
    public const KIND_SHORT = 2; // [] syntax

    /** @var ArrayItem[] Items */
    public array $items;

    /**
     * Constructs an array node.
     *
     * @param ArrayItem[] $items Items of the array
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(array $items = [], array $attributes = []) {
        $this->attributes = $attributes;
        $this->items = $items;
    }

    public function getSubNodeNames(): array {
        return ['items'];
    }

    public function getType(): string {
        return 'Expr_Array';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

/**
 * Error node used during parsing with error recovery.
 *
 * An error node may be placed at a position where an expression is required, but an error occurred.
 * Error nodes will not be present if the parser is run in throwOnError mode (the default).
 */
class Error extends Expr {
    /**
     * Constructs an error node.
     *
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(array $attributes = []) {
        $this->attributes = $attributes;
    }

    public function getSubNodeNames(): array {
        return [];
    }

    public function getType(): string {
        return 'Expr_Error';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node;

class Throw_ extends Node\Expr {
    /** @var Node\Expr Expression */
    public Node\Expr $expr;

    /**
     * Constructs a throw expression node.
     *
     * @param Node\Expr $expr Expression
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(Node\Expr $expr, array $attributes = []) {
        $this->attributes = $attributes;
        $this->expr = $expr;
    }

    public function getSubNodeNames(): array {
        return ['expr'];
    }

    public function getType(): string {
        return 'Expr_Throw';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Name;

class Instanceof_ extends Expr {
    /** @var Expr Expression */
    public Expr $expr;
    /** @var Name|Expr Class name */
    public Node $class;

    /**
     * Constructs an instanceof check node.
     *
     * @param Expr $expr Expression
     * @param Name|Expr $class Class name
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(Expr $expr, Node $class, array $attributes = []) {
        $this->attributes = $attributes;
        $this->expr = $expr;
        $this->class = $class;
    }

    public function getSubNodeNames(): array {
        return ['expr', 'class'];
    }

    public function getType(): string {
        return 'Expr_Instanceof';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

class PreInc extends Expr {
    /** @var Expr Variable */
    public Expr $var;

    /**
     * Constructs a pre increment node.
     *
     * @param Expr $var Variable
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(Expr $var, array $attributes = []) {
        $this->attributes = $attributes;
        $this->var = $var;
    }

    public function getSubNodeNames(): array {
        return ['var'];
    }

    public function getType(): string {
        return 'Expr_PreInc';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\AssignOp;

use PhpParser\Node\Expr\AssignOp;

class ShiftRight extends AssignOp {
    public function getType(): string {
        return 'Expr_AssignOp_ShiftRight';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\AssignOp;

use PhpParser\Node\Expr\AssignOp;

class Div extends AssignOp {
    public function getType(): string {
        return 'Expr_AssignOp_Div';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\AssignOp;

use PhpParser\Node\Expr\AssignOp;

class Mul extends AssignOp {
    public function getType(): string {
        return 'Expr_AssignOp_Mul';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\AssignOp;

use PhpParser\Node\Expr\AssignOp;

class Mod extends AssignOp {
    public function getType(): string {
        return 'Expr_AssignOp_Mod';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\AssignOp;

use PhpParser\Node\Expr\AssignOp;

class BitwiseOr extends AssignOp {
    public function getType(): string {
        return 'Expr_AssignOp_BitwiseOr';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\AssignOp;

use PhpParser\Node\Expr\AssignOp;

class Concat extends AssignOp {
    public function getType(): string {
        return 'Expr_AssignOp_Concat';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\AssignOp;

use PhpParser\Node\Expr\AssignOp;

class Plus extends AssignOp {
    public function getType(): string {
        return 'Expr_AssignOp_Plus';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\AssignOp;

use PhpParser\Node\Expr\AssignOp;

class BitwiseXor extends AssignOp {
    public function getType(): string {
        return 'Expr_AssignOp_BitwiseXor';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\AssignOp;

use PhpParser\Node\Expr\AssignOp;

class ShiftLeft extends AssignOp {
    public function getType(): string {
        return 'Expr_AssignOp_ShiftLeft';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\AssignOp;

use PhpParser\Node\Expr\AssignOp;

class Coalesce extends AssignOp {
    public function getType(): string {
        return 'Expr_AssignOp_Coalesce';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\AssignOp;

use PhpParser\Node\Expr\AssignOp;

class BitwiseAnd extends AssignOp {
    public function getType(): string {
        return 'Expr_AssignOp_BitwiseAnd';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\AssignOp;

use PhpParser\Node\Expr\AssignOp;

class Minus extends AssignOp {
    public function getType(): string {
        return 'Expr_AssignOp_Minus';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\AssignOp;

use PhpParser\Node\Expr\AssignOp;

class Pow extends AssignOp {
    public function getType(): string {
        return 'Expr_AssignOp_Pow';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

class ArrayDimFetch extends Expr {
    /** @var Expr Variable */
    public Expr $var;
    /** @var null|Expr Array index / dim */
    public ?Expr $dim;

    /**
     * Constructs an array index fetch node.
     *
     * @param Expr $var Variable
     * @param null|Expr $dim Array index / dim
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(Expr $var, ?Expr $dim = null, array $attributes = []) {
        $this->attributes = $attributes;
        $this->var = $var;
        $this->dim = $dim;
    }

    public function getSubNodeNames(): array {
        return ['var', 'dim'];
    }

    public function getType(): string {
        return 'Expr_ArrayDimFetch';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

class Assign extends Expr {
    /** @var Expr Variable */
    public Expr $var;
    /** @var Expr Expression */
    public Expr $expr;

    /**
     * Constructs an assignment node.
     *
     * @param Expr $var Variable
     * @param Expr $expr Expression
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(Expr $var, Expr $expr, array $attributes = []) {
        $this->attributes = $attributes;
        $this->var = $var;
        $this->expr = $expr;
    }

    public function getSubNodeNames(): array {
        return ['var', 'expr'];
    }

    public function getType(): string {
        return 'Expr_Assign';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

class ErrorSuppress extends Expr {
    /** @var Expr Expression */
    public Expr $expr;

    /**
     * Constructs an error suppress node.
     *
     * @param Expr $expr Expression
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(Expr $expr, array $attributes = []) {
        $this->attributes = $attributes;
        $this->expr = $expr;
    }

    public function getSubNodeNames(): array {
        return ['expr'];
    }

    public function getType(): string {
        return 'Expr_ErrorSuppress';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

class PostInc extends Expr {
    /** @var Expr Variable */
    public Expr $var;

    /**
     * Constructs a post increment node.
     *
     * @param Expr $var Variable
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(Expr $var, array $attributes = []) {
        $this->attributes = $attributes;
        $this->var = $var;
    }

    public function getSubNodeNames(): array {
        return ['var'];
    }

    public function getType(): string {
        return 'Expr_PostInc';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\Cast;

use PhpParser\Node\Expr\Cast;

class Double extends Cast {
    // For use in "kind" attribute
    public const KIND_DOUBLE = 1; // "double" syntax
    public const KIND_FLOAT = 2;  // "float" syntax
    public const KIND_REAL = 3; // "real" syntax

    public function getType(): string {
        return 'Expr_Cast_Double';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\Cast;

use PhpParser\Node\Expr\Cast;

class Int_ extends Cast {
    // For use in "kind" attribute
    public const KIND_INT = 1; // "int" syntax
    public const KIND_INTEGER = 2; // "integer" syntax

    public function getType(): string {
        return 'Expr_Cast_Int';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\Cast;

use PhpParser\Node\Expr\Cast;

class Void_ extends Cast {
    public function getType(): string {
        return 'Expr_Cast_Void';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\Cast;

use PhpParser\Node\Expr\Cast;

class Array_ extends Cast {
    public function getType(): string {
        return 'Expr_Cast_Array';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\Cast;

use PhpParser\Node\Expr\Cast;

class Object_ extends Cast {
    public function getType(): string {
        return 'Expr_Cast_Object';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\Cast;

use PhpParser\Node\Expr\Cast;

class Bool_ extends Cast {
    // For use in "kind" attribute
    public const KIND_BOOL = 1; // "bool" syntax
    public const KIND_BOOLEAN = 2; // "boolean" syntax

    public function getType(): string {
        return 'Expr_Cast_Bool';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\Cast;

use PhpParser\Node\Expr\Cast;

class String_ extends Cast {
    // For use in "kind" attribute
    public const KIND_STRING = 1; // "string" syntax
    public const KIND_BINARY = 2; // "binary" syntax

    public function getType(): string {
        return 'Expr_Cast_String';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr\Cast;

use PhpParser\Node\Expr\Cast;

class Unset_ extends Cast {
    public function getType(): string {
        return 'Expr_Cast_Unset';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Identifier;

class NullsafePropertyFetch extends Expr {
    /** @var Expr Variable holding object */
    public Expr $var;
    /** @var Identifier|Expr Property name */
    public Node $name;

    /**
     * Constructs a nullsafe property fetch node.
     *
     * @param Expr $var Variable holding object
     * @param string|Identifier|Expr $name Property name
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(Expr $var, $name, array $attributes = []) {
        $this->attributes = $attributes;
        $this->var = $var;
        $this->name = \is_string($name) ? new Identifier($name) : $name;
    }

    public function getSubNodeNames(): array {
        return ['var', 'name'];
    }

    public function getType(): string {
        return 'Expr_NullsafePropertyFetch';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

class BooleanNot extends Expr {
    /** @var Expr Expression */
    public Expr $expr;

    /**
     * Constructs a boolean not node.
     *
     * @param Expr $expr Expression
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(Expr $expr, array $attributes = []) {
        $this->attributes = $attributes;
        $this->expr = $expr;
    }

    public function getSubNodeNames(): array {
        return ['expr'];
    }

    public function getType(): string {
        return 'Expr_BooleanNot';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

class Print_ extends Expr {
    /** @var Expr Expression */
    public Expr $expr;

    /**
     * Constructs an print() node.
     *
     * @param Expr $expr Expression
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(Expr $expr, array $attributes = []) {
        $this->attributes = $attributes;
        $this->expr = $expr;
    }

    public function getSubNodeNames(): array {
        return ['expr'];
    }

    public function getType(): string {
        return 'Expr_Print';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

class Exit_ extends Expr {
    /* For use in "kind" attribute */
    public const KIND_EXIT = 1;
    public const KIND_DIE = 2;

    /** @var null|Expr Expression */
    public ?Expr $expr;

    /**
     * Constructs an exit() node.
     *
     * @param null|Expr $expr Expression
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(?Expr $expr = null, array $attributes = []) {
        $this->attributes = $attributes;
        $this->expr = $expr;
    }

    public function getSubNodeNames(): array {
        return ['expr'];
    }

    public function getType(): string {
        return 'Expr_Exit';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

abstract class BinaryOp extends Expr {
    /** @var Expr The left hand side expression */
    public Expr $left;
    /** @var Expr The right hand side expression */
    public Expr $right;

    /**
     * Constructs a binary operator node.
     *
     * @param Expr $left The left hand side expression
     * @param Expr $right The right hand side expression
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(Expr $left, Expr $right, array $attributes = []) {
        $this->attributes = $attributes;
        $this->left = $left;
        $this->right = $right;
    }

    public function getSubNodeNames(): array {
        return ['left', 'right'];
    }

    /**
     * Get the operator sigil for this binary operation.
     *
     * In the case there are multiple possible sigils for an operator, this method does not
     * necessarily return the one used in the parsed code.
     */
    abstract public function getOperatorSigil(): string;
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

class AssignRef extends Expr {
    /** @var Expr Variable reference is assigned to */
    public Expr $var;
    /** @var Expr Variable which is referenced */
    public Expr $expr;

    /**
     * Constructs an assignment node.
     *
     * @param Expr $var Variable
     * @param Expr $expr Expression
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(Expr $var, Expr $expr, array $attributes = []) {
        $this->attributes = $attributes;
        $this->var = $var;
        $this->expr = $expr;
    }

    public function getSubNodeNames(): array {
        return ['var', 'expr'];
    }

    public function getType(): string {
        return 'Expr_AssignRef';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

require __DIR__ . '/../ClosureUse.php';

if (false) {
    /**
     * For classmap-authoritative support.
     *
     * @deprecated use \PhpParser\Node\ClosureUse instead.
     */
    class ClosureUse extends \PhpParser\Node\ClosureUse {
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Arg;
use PhpParser\Node\ArgPlaceholder;
use PhpParser\Node\Expr;
use PhpParser\Node\VariadicPlaceholder;

abstract class CallLike extends Expr {
    /**
     * Return raw arguments, which may be actual Args, VariadicPlaceholders for first-class
     * callables, or ArgPlaceholders for partial function application.
     *
     * @return array<Arg|VariadicPlaceholder|ArgPlaceholder>
     */
    abstract public function getRawArgs(): array;

    /**
     * Returns whether this call expression is actually a first class callable.
     */
    public function isFirstClassCallable(): bool {
        $rawArgs = $this->getRawArgs();
        return count($rawArgs) === 1 && current($rawArgs) instanceof VariadicPlaceholder;
    }

    /**
     * Returns whether this call expression is a partial function application, i.e. whether its
     * argument list contains one or more "?" placeholders or a "..." placeholder. First-class
     * callables are a special case of partial function application, so this also returns true
     * for them.
     */
    public function isPartialFunctionApplication(): bool {
        foreach ($this->getRawArgs() as $arg) {
            if ($arg instanceof VariadicPlaceholder || $arg instanceof ArgPlaceholder) {
                return true;
            }
        }
        return false;
    }

    /**
     * Assert that this is not a partial function application (which includes first-class
     * callables) and return only ordinary Args.
     *
     * @return Arg[]
     */
    public function getArgs(): array {
        assert(!$this->isPartialFunctionApplication());
        return $this->getRawArgs();
    }

    /**
     * Retrieves a specific argument from the raw arguments.
     *
     * Returns the named argument that matches the given `$name`, or the
     * positional (unnamed) argument that exists at the given `$position`.
     * Returns `null` if no match is found, or when a "..." placeholder is
     * encountered, as argument positions are no longer known past that point.
     * A "?" placeholder occupies its argument position, but is never returned,
     * as it is not an actual argument.
     */
    public function getArg(string $name, int $position): ?Arg {
        foreach ($this->getRawArgs() as $i => $arg) {
            if ($arg instanceof VariadicPlaceholder) {
                return null;
            }
            if ($arg instanceof ArgPlaceholder || $arg->unpack) {
                continue;
            }
            if (
                ($arg->name !== null && $arg->name->toString() === $name)
                || ($arg->name === null && $i === $position)
            ) {
                return $arg;
            }
        }
        return null;
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

class Empty_ extends Expr {
    /** @var Expr Expression */
    public Expr $expr;

    /**
     * Constructs an empty() node.
     *
     * @param Expr $expr Expression
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(Expr $expr, array $attributes = []) {
        $this->attributes = $attributes;
        $this->expr = $expr;
    }

    public function getSubNodeNames(): array {
        return ['expr'];
    }

    public function getType(): string {
        return 'Expr_Empty';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

class Clone_ extends Expr {
    /** @var Expr Expression */
    public Expr $expr;

    /**
     * Constructs a clone node.
     *
     * @param Expr $expr Expression
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(Expr $expr, array $attributes = []) {
        $this->attributes = $attributes;
        $this->expr = $expr;
    }

    public function getSubNodeNames(): array {
        return ['expr'];
    }

    public function getType(): string {
        return 'Expr_Clone';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

class UnaryMinus extends Expr {
    /** @var Expr Expression */
    public Expr $expr;

    /**
     * Constructs a unary minus node.
     *
     * @param Expr $expr Expression
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(Expr $expr, array $attributes = []) {
        $this->attributes = $attributes;
        $this->expr = $expr;
    }

    public function getSubNodeNames(): array {
        return ['expr'];
    }

    public function getType(): string {
        return 'Expr_UnaryMinus';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\ArgPlaceholder;
use PhpParser\Node\Expr;
use PhpParser\Node\VariadicPlaceholder;

class New_ extends CallLike {
    /** @var Node\Name|Expr|Node\Stmt\Class_ Class name */
    public Node $class;
    /** @var array<Arg|VariadicPlaceholder|ArgPlaceholder> Arguments */
    public array $args;

    /**
     * Constructs a function call node.
     *
     * @param Node\Name|Expr|Node\Stmt\Class_ $class Class name (or class node for anonymous classes)
     * @param array<Arg|VariadicPlaceholder|ArgPlaceholder> $args Arguments
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(Node $class, array $args = [], array $attributes = []) {
        $this->attributes = $attributes;
        $this->class = $class;
        $this->args = $args;
    }

    public function getSubNodeNames(): array {
        return ['class', 'args'];
    }

    public function getType(): string {
        return 'Expr_New';
    }

    public function getRawArgs(): array {
        return $this->args;
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

class Variable extends Expr {
    /** @var string|Expr Name */
    public $name;

    /**
     * Constructs a variable node.
     *
     * @param string|Expr $name Name
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct($name, array $attributes = []) {
        $this->attributes = $attributes;
        $this->name = $name;
    }

    public function getSubNodeNames(): array {
        return ['name'];
    }

    public function getType(): string {
        return 'Expr_Variable';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\Expr;

abstract class Cast extends Expr {
    /** @var Expr Expression */
    public Expr $expr;

    /**
     * Constructs a cast node.
     *
     * @param Expr $expr Expression
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(Expr $expr, array $attributes = []) {
        $this->attributes = $attributes;
        $this->expr = $expr;
    }

    public function getSubNodeNames(): array {
        return ['expr'];
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Expr;

use PhpParser\Node\ArrayItem;
use PhpParser\Node\Expr;

class List_ extends Expr {
    // For use in "kind" attribute
    public const KIND_LIST = 1; // list() syntax
    public const KIND_ARRAY = 2; // [] syntax

    /** @var (ArrayItem|null)[] List of items to assign to */
    public array $items;

    /**
     * Constructs a list() destructuring node.
     *
     * @param (ArrayItem|null)[] $items List of items to assign to
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(array $items, array $attributes = []) {
        $this->attributes = $attributes;
        $this->items = $items;
    }

    public function getSubNodeNames(): array {
        return ['items'];
    }

    public function getType(): string {
        return 'Expr_List';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node;

use PhpParser\NodeAbstract;

class Const_ extends NodeAbstract {
    /** @var Identifier Name */
    public Identifier $name;
    /** @var Expr Value */
    public Expr $value;

    /** @var Name|null Namespaced name (if using NameResolver) */
    public ?Name $namespacedName;

    /**
     * Constructs a const node for use in class const and const statements.
     *
     * @param string|Identifier $name Name
     * @param Expr $value Value
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct($name, Expr $value, array $attributes = []) {
        $this->attributes = $attributes;
        $this->name = \is_string($name) ? new Identifier($name) : $name;
        $this->value = $value;
    }

    public function getSubNodeNames(): array {
        return ['name', 'value'];
    }

    public function getType(): string {
        return 'Const';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node;

use PhpParser\NodeAbstract;

/**
 * Represents a non-namespaced name. Namespaced names are represented using Name nodes.
 */
class Identifier extends NodeAbstract {
    /**
     * @psalm-var non-empty-string
     * @var string Identifier as string
     */
    public string $name;

    /** @var array<string, bool> */
    private static array $specialClassNames = [
        'self'   => true,
        'parent' => true,
        'static' => true,
    ];

    /**
     * Constructs an identifier node.
     *
     * @param string $name Identifier as string
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(string $name, array $attributes = []) {
        if ($name === '') {
            throw new \InvalidArgumentException('Identifier name cannot be empty');
        }

        $this->attributes = $attributes;
        $this->name = $name;
    }

    public function getSubNodeNames(): array {
        return ['name'];
    }

    /**
     * Get identifier as string.
     *
     * @psalm-return non-empty-string
     * @return string Identifier as string.
     */
    public function toString(): string {
        return $this->name;
    }

    /**
     * Get lowercased identifier as string.
     *
     * @psalm-return non-empty-string&lowercase-string
     * @return string Lowercased identifier as string
     */
    public function toLowerString(): string {
        return strtolower($this->name);
    }

    /**
     * Checks whether the identifier is a special class name (self, parent or static).
     *
     * @return bool Whether identifier is a special class name
     */
    public function isSpecialClassName(): bool {
        return isset(self::$specialClassNames[strtolower($this->name)]);
    }

    /**
     * Get identifier as string.
     *
     * @psalm-return non-empty-string
     * @return string Identifier as string
     */
    public function __toString(): string {
        return $this->name;
    }

    public function getType(): string {
        return 'Identifier';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node;

class UnionType extends ComplexType {
    /** @var (Identifier|Name|IntersectionType)[] Types */
    public array $types;

    /**
     * Constructs a union type.
     *
     * @param (Identifier|Name|IntersectionType)[] $types Types
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(array $types, array $attributes = []) {
        $this->attributes = $attributes;
        $this->types = $types;
    }

    public function getSubNodeNames(): array {
        return ['types'];
    }

    public function getType(): string {
        return 'UnionType';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Scalar;

use PhpParser\Error;
use PhpParser\Node\Scalar;

class Int_ extends Scalar {
    /* For use in "kind" attribute */
    public const KIND_BIN = 2;
    public const KIND_OCT = 8;
    public const KIND_DEC = 10;
    public const KIND_HEX = 16;

    /** @var int Number value */
    public int $value;

    /**
     * Constructs an integer number scalar node.
     *
     * @param int $value Value of the number
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(int $value, array $attributes = []) {
        $this->attributes = $attributes;
        $this->value = $value;
    }

    public function getSubNodeNames(): array {
        return ['value'];
    }

    /**
     * Constructs an Int node from a string number literal.
     *
     * @param string $str String number literal (decimal, octal, hex or binary)
     * @param array<string, mixed> $attributes Additional attributes
     * @param bool $allowInvalidOctal Whether to allow invalid octal numbers (PHP 5)
     *
     * @return Int_ The constructed LNumber, including kind attribute
     */
    public static function fromString(string $str, array $attributes = [], bool $allowInvalidOctal = false): Int_ {
        $attributes['rawValue'] = $str;

        $str = str_replace('_', '', $str);

        if ('0' !== $str[0] || '0' === $str) {
            $attributes['kind'] = Int_::KIND_DEC;
            return new Int_((int) $str, $attributes);
        }

        if ('x' === $str[1] || 'X' === $str[1]) {
            $attributes['kind'] = Int_::KIND_HEX;
            return new Int_(hexdec($str), $attributes);
        }

        if ('b' === $str[1] || 'B' === $str[1]) {
            $attributes['kind'] = Int_::KIND_BIN;
            return new Int_(bindec($str), $attributes);
        }

        if (!$allowInvalidOctal && strpbrk($str, '89')) {
            throw new Error('Invalid numeric literal', $attributes);
        }

        // Strip optional explicit octal prefix.
        if ('o' === $str[1] || 'O' === $str[1]) {
            $str = substr($str, 2);
        }

        // use intval instead of octdec to get proper cutting behavior with malformed numbers
        $attributes['kind'] = Int_::KIND_OCT;
        return new Int_(intval($str, 8), $attributes);
    }

    public function getType(): string {
        return 'Scalar_Int';
    }
}

// @deprecated compatibility alias
class_alias(Int_::class, LNumber::class);
<?php declare(strict_types=1);

namespace PhpParser\Node\Scalar;

use PhpParser\Node\InterpolatedStringPart;

require __DIR__ . '/../InterpolatedStringPart.php';

if (false) {
    /**
     * For classmap-authoritative support.
     *
     * @deprecated use \PhpParser\Node\InterpolatedStringPart instead.
     */
    class EncapsedStringPart extends InterpolatedStringPart {
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Scalar;

use PhpParser\Node\Expr;
use PhpParser\Node\InterpolatedStringPart;
use PhpParser\Node\Scalar;

class InterpolatedString extends Scalar {
    /** @var (Expr|InterpolatedStringPart)[] list of string parts */
    public array $parts;

    /**
     * Constructs an interpolated string node.
     *
     * @param (Expr|InterpolatedStringPart)[] $parts Interpolated string parts
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(array $parts, array $attributes = []) {
        $this->attributes = $attributes;
        $this->parts = $parts;
    }

    public function getSubNodeNames(): array {
        return ['parts'];
    }

    public function getType(): string {
        return 'Scalar_InterpolatedString';
    }
}

// @deprecated compatibility alias
class_alias(InterpolatedString::class, Encapsed::class);
<?php declare(strict_types=1);

namespace PhpParser\Node\Scalar;

require __DIR__ . '/Float_.php';

if (false) {
    /**
     * For classmap-authoritative support.
     *
     * @deprecated use \PhpParser\Node\Scalar\Float_ instead.
     */
    class DNumber extends Float_ {
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Scalar\MagicConst;

use PhpParser\Node\Scalar\MagicConst;

class Function_ extends MagicConst {
    public function getName(): string {
        return '__FUNCTION__';
    }

    public function getType(): string {
        return 'Scalar_MagicConst_Function';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Scalar\MagicConst;

use PhpParser\Node\Scalar\MagicConst;

class Property extends MagicConst {
    public function getName(): string {
        return '__PROPERTY__';
    }

    public function getType(): string {
        return 'Scalar_MagicConst_Property';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Scalar\MagicConst;

use PhpParser\Node\Scalar\MagicConst;

class Dir extends MagicConst {
    public function getName(): string {
        return '__DIR__';
    }

    public function getType(): string {
        return 'Scalar_MagicConst_Dir';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Scalar\MagicConst;

use PhpParser\Node\Scalar\MagicConst;

class File extends MagicConst {
    public function getName(): string {
        return '__FILE__';
    }

    public function getType(): string {
        return 'Scalar_MagicConst_File';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Scalar\MagicConst;

use PhpParser\Node\Scalar\MagicConst;

class Method extends MagicConst {
    public function getName(): string {
        return '__METHOD__';
    }

    public function getType(): string {
        return 'Scalar_MagicConst_Method';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Scalar\MagicConst;

use PhpParser\Node\Scalar\MagicConst;

class Class_ extends MagicConst {
    public function getName(): string {
        return '__CLASS__';
    }

    public function getType(): string {
        return 'Scalar_MagicConst_Class';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Scalar\MagicConst;

use PhpParser\Node\Scalar\MagicConst;

class Trait_ extends MagicConst {
    public function getName(): string {
        return '__TRAIT__';
    }

    public function getType(): string {
        return 'Scalar_MagicConst_Trait';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Scalar\MagicConst;

use PhpParser\Node\Scalar\MagicConst;

class Line extends MagicConst {
    public function getName(): string {
        return '__LINE__';
    }

    public function getType(): string {
        return 'Scalar_MagicConst_Line';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Scalar\MagicConst;

use PhpParser\Node\Scalar\MagicConst;

class Namespace_ extends MagicConst {
    public function getName(): string {
        return '__NAMESPACE__';
    }

    public function getType(): string {
        return 'Scalar_MagicConst_Namespace';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Scalar;

require __DIR__ . '/Int_.php';

if (false) {
    /**
     * For classmap-authoritative support.
     *
     * @deprecated use \PhpParser\Node\Scalar\Int_ instead.
     */
    class LNumber extends Int_ {
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Scalar;

use PhpParser\Node\Scalar;

abstract class MagicConst extends Scalar {
    /**
     * Constructs a magic constant node.
     *
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(array $attributes = []) {
        $this->attributes = $attributes;
    }

    public function getSubNodeNames(): array {
        return [];
    }

    /**
     * Get name of magic constant.
     *
     * @return string Name of magic constant
     */
    abstract public function getName(): string;
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Scalar;

use PhpParser\Node\Scalar;

class Float_ extends Scalar {
    /** @var float Number value */
    public float $value;

    /**
     * Constructs a float number scalar node.
     *
     * @param float $value Value of the number
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(float $value, array $attributes = []) {
        $this->attributes = $attributes;
        $this->value = $value;
    }

    public function getSubNodeNames(): array {
        return ['value'];
    }

    /**
     * @param mixed[] $attributes
     */
    public static function fromString(string $str, array $attributes = []): Float_ {
        $attributes['rawValue'] = $str;
        $float = self::parse($str);

        return new Float_($float, $attributes);
    }

    /**
     * @internal
     *
     * Parses a DNUMBER token like PHP would.
     *
     * @param string $str A string number
     *
     * @return float The parsed number
     */
    public static function parse(string $str): float {
        $str = str_replace('_', '', $str);

        // Check whether this is one of the special integer notations.
        if ('0' === $str[0]) {
            // hex
            if ('x' === $str[1] || 'X' === $str[1]) {
                // PHP 8.6 warns when hexdec() produces a float.
                return @hexdec($str);
            }

            // bin
            if ('b' === $str[1] || 'B' === $str[1]) {
                // PHP 8.6 warns when bindec() produces a float.
                return @bindec($str);
            }

            // oct, but only if the string does not contain any of '.eE'.
            if (false === strpbrk($str, '.eE')) {
                // substr($str, 0, strcspn($str, '89')) cuts the string at the first invalid digit
                // (8 or 9) so that only the digits before that are used.
                // PHP 8.6 warns when octdec() produces a float.
                return @octdec(substr($str, 0, strcspn($str, '89')));
            }
        }

        // dec
        return (float) $str;
    }

    public function getType(): string {
        return 'Scalar_Float';
    }
}

// @deprecated compatibility alias
class_alias(Float_::class, DNumber::class);
<?php declare(strict_types=1);

namespace PhpParser\Node\Scalar;

use PhpParser\Error;
use PhpParser\Node\Scalar;

class String_ extends Scalar {
    /* For use in "kind" attribute */
    public const KIND_SINGLE_QUOTED = 1;
    public const KIND_DOUBLE_QUOTED = 2;
    public const KIND_HEREDOC = 3;
    public const KIND_NOWDOC = 4;

    /** @var string String value */
    public string $value;

    /** @var array<string, string> Escaped character to its decoded value */
    protected static array $replacements = [
        '\\' => '\\',
        '$'  =>  '$',
        'n'  => "\n",
        'r'  => "\r",
        't'  => "\t",
        'f'  => "\f",
        'v'  => "\v",
        'e'  => "\x1B",
    ];

    /**
     * Constructs a string scalar node.
     *
     * @param string $value Value of the string
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(string $value, array $attributes = []) {
        $this->attributes = $attributes;
        $this->value = $value;
    }

    public function getSubNodeNames(): array {
        return ['value'];
    }

    /**
     * @param array<string, mixed> $attributes
     * @param bool $parseUnicodeEscape Whether to parse PHP 7 \u escapes
     */
    public static function fromString(string $str, array $attributes = [], bool $parseUnicodeEscape = true): self {
        $attributes['kind'] = ($str[0] === "'" || ($str[1] === "'" && ($str[0] === 'b' || $str[0] === 'B')))
            ? Scalar\String_::KIND_SINGLE_QUOTED
            : Scalar\String_::KIND_DOUBLE_QUOTED;

        $attributes['rawValue'] = $str;

        $string = self::parse($str, $parseUnicodeEscape);

        return new self($string, $attributes);
    }

    /**
     * @internal
     *
     * Parses a string token.
     *
     * @param string $str String token content
     * @param bool $parseUnicodeEscape Whether to parse PHP 7 \u escapes
     *
     * @return string The parsed string
     */
    public static function parse(string $str, bool $parseUnicodeEscape = true): string {
        $bLength = 0;
        if ('b' === $str[0] || 'B' === $str[0]) {
            $bLength = 1;
        }

        if ('\'' === $str[$bLength]) {
            return str_replace(
                ['\\\\', '\\\''],
                ['\\', '\''],
                substr($str, $bLength + 1, -1)
            );
        } else {
            return self::parseEscapeSequences(
                substr($str, $bLength + 1, -1), '"', $parseUnicodeEscape
            );
        }
    }

    /**
     * @internal
     *
     * Parses escape sequences in strings (all string types apart from single quoted).
     *
     * @param string $str String without quotes
     * @param null|string $quote Quote type
     * @param bool $parseUnicodeEscape Whether to parse PHP 7 \u escapes
     *
     * @return string String with escape sequences parsed
     */
    public static function parseEscapeSequences(string $str, ?string $quote, bool $parseUnicodeEscape = true): string {
        if (null !== $quote) {
            $str = str_replace('\\' . $quote, $quote, $str);
        }

        $extra = '';
        if ($parseUnicodeEscape) {
            $extra = '|u\{([0-9a-fA-F]+)\}';
        }

        return preg_replace_callback(
            '~\\\\([\\\\$nrtfve]|[xX][0-9a-fA-F]{1,2}|[0-7]{1,3}' . $extra . ')~',
            function ($matches) {
                $str = $matches[1];

                if (isset(self::$replacements[$str])) {
                    return self::$replacements[$str];
                }
                if ('x' === $str[0] || 'X' === $str[0]) {
                    return chr(hexdec(substr($str, 1)));
                }
                if ('u' === $str[0]) {
                    // PHP 8.6 warns when hexdec() produces a float.
                    $dec = @hexdec($matches[2]);
                    // If it overflowed to float, treat as INT_MAX, it will throw an error anyway.
                    return self::codePointToUtf8(\is_int($dec) ? $dec : \PHP_INT_MAX);
                } else {
                    return chr(octdec($str) & 255);
                }
            },
            $str
        );
    }

    /**
     * Converts a Unicode code point to its UTF-8 encoded representation.
     *
     * @param int $num Code point
     *
     * @return string UTF-8 representation of code point
     */
    private static function codePointToUtf8(int $num): string {
        if ($num <= 0x7F) {
            return chr($num);
        }
        if ($num <= 0x7FF) {
            return chr(($num >> 6) + 0xC0) . chr(($num & 0x3F) + 0x80);
        }
        if ($num <= 0xFFFF) {
            return chr(($num >> 12) + 0xE0) . chr((($num >> 6) & 0x3F) + 0x80) . chr(($num & 0x3F) + 0x80);
        }
        if ($num <= 0x10FFFF) {
            return chr(($num >> 18) + 0xF0) . chr((($num >> 12) & 0x3F) + 0x80)
                 . chr((($num >> 6) & 0x3F) + 0x80) . chr(($num & 0x3F) + 0x80);
        }
        throw new Error('Invalid UTF-8 codepoint escape sequence: Codepoint too large');
    }

    public function getType(): string {
        return 'Scalar_String';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Scalar;

require __DIR__ . '/InterpolatedString.php';

if (false) {
    /**
     * For classmap-authoritative support.
     *
     * @deprecated use \PhpParser\Node\Scalar\InterpolatedString instead.
     */
    class Encapsed extends InterpolatedString {
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node;

use PhpParser\NodeAbstract;

/**
 * Represents the "?" argument placeholder of the partial function application syntax,
 * e.g. the "?" in "foo(?)". Like VariadicPlaceholder, it occurs in the argument list
 * of a call, in place of an ordinary Arg.
 */
class ArgPlaceholder extends NodeAbstract {
    /** @var Identifier|null Parameter name (for named placeholders) */
    public ?Identifier $name;

    /**
     * Create a "?" argument placeholder (partial function application syntax).
     *
     * @param Identifier|null $name Parameter name (for named placeholders)
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(?Identifier $name = null, array $attributes = []) {
        $this->attributes = $attributes;
        $this->name = $name;
    }

    public function getSubNodeNames(): array {
        return ['name'];
    }

    public function getType(): string {
        return 'ArgPlaceholder';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node;

use PhpParser\NodeAbstract;

/**
 * Represents the "..." in "foo(...)" of the first-class callable syntax.
 */
class VariadicPlaceholder extends NodeAbstract {
    /**
     * Create a variadic argument placeholder (first-class callable syntax).
     *
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(array $attributes = []) {
        $this->attributes = $attributes;
    }

    public function getType(): string {
        return 'VariadicPlaceholder';
    }

    public function getSubNodeNames(): array {
        return [];
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node;

use PhpParser\Node;

class NullableType extends ComplexType {
    /** @var Identifier|Name Type */
    public Node $type;

    /**
     * Constructs a nullable type (wrapping another type).
     *
     * @param Identifier|Name $type Type
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(Node $type, array $attributes = []) {
        $this->attributes = $attributes;
        $this->type = $type;
    }

    public function getSubNodeNames(): array {
        return ['type'];
    }

    public function getType(): string {
        return 'NullableType';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node;

use PhpParser\NodeAbstract;

class ClosureUse extends NodeAbstract {
    /** @var Expr\Variable Variable to use */
    public Expr\Variable $var;
    /** @var bool Whether to use by reference */
    public bool $byRef;

    /**
     * Constructs a closure use node.
     *
     * @param Expr\Variable $var Variable to use
     * @param bool $byRef Whether to use by reference
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(Expr\Variable $var, bool $byRef = false, array $attributes = []) {
        $this->attributes = $attributes;
        $this->var = $var;
        $this->byRef = $byRef;
    }

    public function getSubNodeNames(): array {
        return ['var', 'byRef'];
    }

    public function getType(): string {
        return 'ClosureUse';
    }
}

// @deprecated compatibility alias
class_alias(ClosureUse::class, Expr\ClosureUse::class);
<?php declare(strict_types=1);

namespace PhpParser\Node;

use PhpParser\Node;
use PhpParser\NodeAbstract;
use PhpParser\Node\Stmt\Use_;

class UseItem extends NodeAbstract {
    /**
     * @var Use_::TYPE_* One of the Stmt\Use_::TYPE_* constants. Will only differ from TYPE_UNKNOWN for mixed group uses
     */
    public int $type;
    /** @var Node\Name Namespace, class, function or constant to alias */
    public Name $name;
    /** @var Identifier|null Alias */
    public ?Identifier $alias;

    /**
     * Constructs an alias (use) item node.
     *
     * @param Node\Name $name Namespace/Class to alias
     * @param null|string|Identifier $alias Alias
     * @param Use_::TYPE_* $type Type of the use element (for mixed group use only)
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(Node\Name $name, $alias = null, int $type = Use_::TYPE_UNKNOWN, array $attributes = []) {
        $this->attributes = $attributes;
        $this->type = $type;
        $this->name = $name;
        $this->alias = \is_string($alias) ? new Identifier($alias) : $alias;
    }

    public function getSubNodeNames(): array {
        return ['type', 'name', 'alias'];
    }

    /**
     * Get alias. If not explicitly given this is the last component of the used name.
     */
    public function getAlias(): Identifier {
        if (null !== $this->alias) {
            return $this->alias;
        }

        return new Identifier($this->name->getLast());
    }

    public function getType(): string {
        return 'UseItem';
    }
}

// @deprecated compatibility alias
class_alias(UseItem::class, Stmt\UseUse::class);
<?php declare(strict_types=1);

namespace PhpParser\Node;

use PhpParser\Node;
use PhpParser\NodeAbstract;

class DeclareItem extends NodeAbstract {
    /** @var Node\Identifier Key */
    public Identifier $key;
    /** @var Node\Expr Value */
    public Expr $value;

    /**
     * Constructs a declare key=>value pair node.
     *
     * @param string|Node\Identifier $key Key
     * @param Node\Expr $value Value
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct($key, Node\Expr $value, array $attributes = []) {
        $this->attributes = $attributes;
        $this->key = \is_string($key) ? new Node\Identifier($key) : $key;
        $this->value = $value;
    }

    public function getSubNodeNames(): array {
        return ['key', 'value'];
    }

    public function getType(): string {
        return 'DeclareItem';
    }
}

// @deprecated compatibility alias
class_alias(DeclareItem::class, Stmt\DeclareDeclare::class);
<?php declare(strict_types=1);

namespace PhpParser\Node;

use PhpParser\Node;
use PhpParser\NodeAbstract;

class PropertyItem extends NodeAbstract {
    /** @var Node\VarLikeIdentifier Name */
    public VarLikeIdentifier $name;
    /** @var null|Node\Expr Default */
    public ?Expr $default;

    /**
     * Constructs a class property item node.
     *
     * @param string|Node\VarLikeIdentifier $name Name
     * @param null|Node\Expr $default Default value
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct($name, ?Node\Expr $default = null, array $attributes = []) {
        $this->attributes = $attributes;
        $this->name = \is_string($name) ? new Node\VarLikeIdentifier($name) : $name;
        $this->default = $default;
    }

    public function getSubNodeNames(): array {
        return ['name', 'default'];
    }

    public function getType(): string {
        return 'PropertyItem';
    }
}

// @deprecated compatibility alias
class_alias(PropertyItem::class, Stmt\PropertyProperty::class);
<?php declare(strict_types=1);

namespace PhpParser\Node;

abstract class Scalar extends Expr {
}
<?php declare(strict_types=1);

namespace PhpParser\Node;

use PhpParser\NodeAbstract;

class AttributeGroup extends NodeAbstract {
    /** @var Attribute[] Attributes */
    public array $attrs;

    /**
     * @param Attribute[] $attrs PHP attributes
     * @param array<string, mixed> $attributes Additional node attributes
     */
    public function __construct(array $attrs, array $attributes = []) {
        $this->attributes = $attributes;
        $this->attrs = $attrs;
    }

    public function getSubNodeNames(): array {
        return ['attrs'];
    }

    public function getType(): string {
        return 'AttributeGroup';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

class Return_ extends Node\Stmt {
    /** @var null|Node\Expr Expression */
    public ?Node\Expr $expr;

    /**
     * Constructs a return node.
     *
     * @param null|Node\Expr $expr Expression
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(?Node\Expr $expr = null, array $attributes = []) {
        $this->attributes = $attributes;
        $this->expr = $expr;
    }

    public function getSubNodeNames(): array {
        return ['expr'];
    }

    public function getType(): string {
        return 'Stmt_Return';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

class Else_ extends Node\Stmt {
    /** @var Node\Stmt[] Statements */
    public array $stmts;

    /**
     * Constructs an else node.
     *
     * @param Node\Stmt[] $stmts Statements
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(array $stmts = [], array $attributes = []) {
        $this->attributes = $attributes;
        $this->stmts = $stmts;
    }

    public function getSubNodeNames(): array {
        return ['stmts'];
    }

    public function getType(): string {
        return 'Stmt_Else';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

class Enum_ extends ClassLike {
    /** @var null|Node\Identifier Scalar Type */
    public ?Node $scalarType;
    /** @var Node\Name[] Names of implemented interfaces */
    public array $implements;

    /**
     * @param string|Node\Identifier|null $name Name
     * @param array{
     *     scalarType?: Node\Identifier|null,
     *     implements?: Node\Name[],
     *     stmts?: Node\Stmt[],
     *     attrGroups?: Node\AttributeGroup[],
     * } $subNodes Array of the following optional subnodes:
     *             'scalarType'  => null    : Scalar type
     *             'implements'  => array() : Names of implemented interfaces
     *             'stmts'       => array() : Statements
     *             'attrGroups'  => array() : PHP attribute groups
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct($name, array $subNodes = [], array $attributes = []) {
        $this->name = \is_string($name) ? new Node\Identifier($name) : $name;
        $this->scalarType = $subNodes['scalarType'] ?? null;
        $this->implements = $subNodes['implements'] ?? [];
        $this->stmts = $subNodes['stmts'] ?? [];
        $this->attrGroups = $subNodes['attrGroups'] ?? [];

        parent::__construct($attributes);
    }

    public function getSubNodeNames(): array {
        return ['attrGroups', 'name', 'scalarType', 'implements', 'stmts'];
    }

    public function getType(): string {
        return 'Stmt_Enum';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node\Stmt;
use PhpParser\Node\UseItem;

class Use_ extends Stmt {
    /**
     * Unknown type. Both Stmt\Use_ / Stmt\GroupUse and Stmt\UseUse have a $type property, one of them will always be
     * TYPE_UNKNOWN while the other has one of the three other possible types. For normal use statements the type on the
     * Stmt\UseUse is unknown. It's only the other way around for mixed group use declarations.
     */
    public const TYPE_UNKNOWN = 0;
    /** Class or namespace import */
    public const TYPE_NORMAL = 1;
    /** Function import */
    public const TYPE_FUNCTION = 2;
    /** Constant import */
    public const TYPE_CONSTANT = 3;

    /** @var self::TYPE_* Type of alias */
    public int $type;
    /** @var UseItem[] Aliases */
    public array $uses;

    /**
     * Constructs an alias (use) list node.
     *
     * @param UseItem[] $uses Aliases
     * @param Stmt\Use_::TYPE_* $type Type of alias
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(array $uses, int $type = self::TYPE_NORMAL, array $attributes = []) {
        $this->attributes = $attributes;
        $this->type = $type;
        $this->uses = $uses;
    }

    public function getSubNodeNames(): array {
        return ['type', 'uses'];
    }

    public function getType(): string {
        return 'Stmt_Use';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

require __DIR__ . '/../StaticVar.php';

if (false) {
    /**
     * For classmap-authoritative support.
     *
     * @deprecated use \PhpParser\Node\StaticVar instead.
     */
    class StaticVar extends \PhpParser\Node\StaticVar {
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node\DeclareItem;

require __DIR__ . '/../DeclareItem.php';

if (false) {
    /**
     * For classmap-authoritative support.
     *
     * @deprecated use \PhpParser\Node\DeclareItem instead.
     */
    class DeclareDeclare extends DeclareItem {
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

class Foreach_ extends Node\Stmt {
    /** @var Node\Expr Expression to iterate */
    public Node\Expr $expr;
    /** @var null|Node\Expr Variable to assign key to */
    public ?Node\Expr $keyVar;
    /** @var bool Whether to assign value by reference */
    public bool $byRef;
    /** @var Node\Expr Variable to assign value to */
    public Node\Expr $valueVar;
    /** @var Node\Stmt[] Statements */
    public array $stmts;

    /**
     * Constructs a foreach node.
     *
     * @param Node\Expr $expr Expression to iterate
     * @param Node\Expr $valueVar Variable to assign value to
     * @param array{
     *     keyVar?: Node\Expr|null,
     *     byRef?: bool,
     *     stmts?: Node\Stmt[],
     * } $subNodes Array of the following optional subnodes:
     *             'keyVar' => null   : Variable to assign key to
     *             'byRef'  => false  : Whether to assign value by reference
     *             'stmts'  => array(): Statements
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(Node\Expr $expr, Node\Expr $valueVar, array $subNodes = [], array $attributes = []) {
        $this->attributes = $attributes;
        $this->expr = $expr;
        $this->keyVar = $subNodes['keyVar'] ?? null;
        $this->byRef = $subNodes['byRef'] ?? false;
        $this->valueVar = $valueVar;
        $this->stmts = $subNodes['stmts'] ?? [];
    }

    public function getSubNodeNames(): array {
        return ['expr', 'keyVar', 'byRef', 'valueVar', 'stmts'];
    }

    public function getType(): string {
        return 'Stmt_Foreach';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;
use PhpParser\Node\Expr;

class Catch_ extends Node\Stmt {
    /** @var Node\Name[] Types of exceptions to catch */
    public array $types;
    /** @var Expr\Variable|null Variable for exception */
    public ?Expr\Variable $var;
    /** @var Node\Stmt[] Statements */
    public array $stmts;

    /**
     * Constructs a catch node.
     *
     * @param Node\Name[] $types Types of exceptions to catch
     * @param Expr\Variable|null $var Variable for exception
     * @param Node\Stmt[] $stmts Statements
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(
        array $types, ?Expr\Variable $var = null, array $stmts = [], array $attributes = []
    ) {
        $this->attributes = $attributes;
        $this->types = $types;
        $this->var = $var;
        $this->stmts = $stmts;
    }

    public function getSubNodeNames(): array {
        return ['types', 'var', 'stmts'];
    }

    public function getType(): string {
        return 'Stmt_Catch';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Modifiers;
use PhpParser\Node;
use PhpParser\Node\FunctionLike;

class ClassMethod extends Node\Stmt implements FunctionLike {
    /** @var int Flags */
    public int $flags;
    /** @var bool Whether to return by reference */
    public bool $byRef;
    /** @var Node\Identifier Name */
    public Node\Identifier $name;
    /** @var Node\Param[] Parameters */
    public array $params;
    /** @var null|Node\Identifier|Node\Name|Node\ComplexType Return type */
    public ?Node $returnType;
    /** @var Node\Stmt[]|null Statements */
    public ?array $stmts;
    /** @var Node\AttributeGroup[] PHP attribute groups */
    public array $attrGroups;

    /** @var array<string, bool> */
    private static array $magicNames = [
        '__construct'   => true,
        '__destruct'    => true,
        '__call'        => true,
        '__callstatic'  => true,
        '__get'         => true,
        '__set'         => true,
        '__isset'       => true,
        '__unset'       => true,
        '__sleep'       => true,
        '__wakeup'      => true,
        '__tostring'    => true,
        '__set_state'   => true,
        '__clone'       => true,
        '__invoke'      => true,
        '__debuginfo'   => true,
        '__serialize'   => true,
        '__unserialize' => true,
    ];

    /**
     * Constructs a class method node.
     *
     * @param string|Node\Identifier $name Name
     * @param array{
     *     flags?: int,
     *     byRef?: bool,
     *     params?: Node\Param[],
     *     returnType?: null|Node\Identifier|Node\Name|Node\ComplexType,
     *     stmts?: Node\Stmt[]|null,
     *     attrGroups?: Node\AttributeGroup[],
     * } $subNodes Array of the following optional subnodes:
     *             'flags       => 0              : Flags
     *             'byRef'      => false          : Whether to return by reference
     *             'params'     => array()        : Parameters
     *             'returnType' => null           : Return type
     *             'stmts'      => array()        : Statements
     *             'attrGroups' => array()        : PHP attribute groups
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct($name, array $subNodes = [], array $attributes = []) {
        $this->attributes = $attributes;
        $this->flags = $subNodes['flags'] ?? $subNodes['type'] ?? 0;
        $this->byRef = $subNodes['byRef'] ?? false;
        $this->name = \is_string($name) ? new Node\Identifier($name) : $name;
        $this->params = $subNodes['params'] ?? [];
        $this->returnType = $subNodes['returnType'] ?? null;
        $this->stmts = array_key_exists('stmts', $subNodes) ? $subNodes['stmts'] : [];
        $this->attrGroups = $subNodes['attrGroups'] ?? [];
    }

    public function getSubNodeNames(): array {
        return ['attrGroups', 'flags', 'byRef', 'name', 'params', 'returnType', 'stmts'];
    }

    public function returnsByRef(): bool {
        return $this->byRef;
    }

    public function getParams(): array {
        return $this->params;
    }

    public function getReturnType() {
        return $this->returnType;
    }

    public function getStmts(): ?array {
        return $this->stmts;
    }

    public function getAttrGroups(): array {
        return $this->attrGroups;
    }

    /**
     * Whether the method is explicitly or implicitly public.
     */
    public function isPublic(): bool {
        return ($this->flags & Modifiers::PUBLIC) !== 0
            || ($this->flags & Modifiers::VISIBILITY_MASK) === 0;
    }

    /**
     * Whether the method is protected.
     */
    public function isProtected(): bool {
        return (bool) ($this->flags & Modifiers::PROTECTED);
    }

    /**
     * Whether the method is private.
     */
    public function isPrivate(): bool {
        return (bool) ($this->flags & Modifiers::PRIVATE);
    }

    /**
     * Whether the method is abstract.
     */
    public function isAbstract(): bool {
        return (bool) ($this->flags & Modifiers::ABSTRACT);
    }

    /**
     * Whether the method is final.
     */
    public function isFinal(): bool {
        return (bool) ($this->flags & Modifiers::FINAL);
    }

    /**
     * Whether the method is static.
     */
    public function isStatic(): bool {
        return (bool) ($this->flags & Modifiers::STATIC);
    }

    /**
     * Whether the method is magic.
     */
    public function isMagic(): bool {
        return isset(self::$magicNames[$this->name->toLowerString()]);
    }

    public function getType(): string {
        return 'Stmt_ClassMethod';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;
use PhpParser\Node\FunctionLike;

class Function_ extends Node\Stmt implements FunctionLike {
    /** @var bool Whether function returns by reference */
    public bool $byRef;
    /** @var Node\Identifier Name */
    public Node\Identifier $name;
    /** @var Node\Param[] Parameters */
    public array $params;
    /** @var null|Node\Identifier|Node\Name|Node\ComplexType Return type */
    public ?Node $returnType;
    /** @var Node\Stmt[] Statements */
    public array $stmts;
    /** @var Node\AttributeGroup[] PHP attribute groups */
    public array $attrGroups;

    /** @var Node\Name|null Namespaced name (if using NameResolver) */
    public ?Node\Name $namespacedName;

    /**
     * Constructs a function node.
     *
     * @param string|Node\Identifier $name Name
     * @param array{
     *     byRef?: bool,
     *     params?: Node\Param[],
     *     returnType?: null|Node\Identifier|Node\Name|Node\ComplexType,
     *     stmts?: Node\Stmt[],
     *     attrGroups?: Node\AttributeGroup[],
     * } $subNodes Array of the following optional subnodes:
     *             'byRef'      => false  : Whether to return by reference
     *             'params'     => array(): Parameters
     *             'returnType' => null   : Return type
     *             'stmts'      => array(): Statements
     *             'attrGroups' => array(): PHP attribute groups
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct($name, array $subNodes = [], array $attributes = []) {
        $this->attributes = $attributes;
        $this->byRef = $subNodes['byRef'] ?? false;
        $this->name = \is_string($name) ? new Node\Identifier($name) : $name;
        $this->params = $subNodes['params'] ?? [];
        $this->returnType = $subNodes['returnType'] ?? null;
        $this->stmts = $subNodes['stmts'] ?? [];
        $this->attrGroups = $subNodes['attrGroups'] ?? [];
    }

    public function getSubNodeNames(): array {
        return ['attrGroups', 'byRef', 'name', 'params', 'returnType', 'stmts'];
    }

    public function returnsByRef(): bool {
        return $this->byRef;
    }

    public function getParams(): array {
        return $this->params;
    }

    public function getReturnType() {
        return $this->returnType;
    }

    public function getAttrGroups(): array {
        return $this->attrGroups;
    }

    /** @return Node\Stmt[] */
    public function getStmts(): array {
        return $this->stmts;
    }

    public function getType(): string {
        return 'Stmt_Function';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node\Identifier;
use PhpParser\Node\Stmt;

class Goto_ extends Stmt {
    /** @var Identifier Name of label to jump to */
    public Identifier $name;

    /**
     * Constructs a goto node.
     *
     * @param string|Identifier $name Name of label to jump to
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct($name, array $attributes = []) {
        $this->attributes = $attributes;
        $this->name = \is_string($name) ? new Identifier($name) : $name;
    }

    public function getSubNodeNames(): array {
        return ['name'];
    }

    public function getType(): string {
        return 'Stmt_Goto';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

class Do_ extends Node\Stmt {
    /** @var Node\Stmt[] Statements */
    public array $stmts;
    /** @var Node\Expr Condition */
    public Node\Expr $cond;

    /**
     * Constructs a do while node.
     *
     * @param Node\Expr $cond Condition
     * @param Node\Stmt[] $stmts Statements
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(Node\Expr $cond, array $stmts = [], array $attributes = []) {
        $this->attributes = $attributes;
        $this->cond = $cond;
        $this->stmts = $stmts;
    }

    public function getSubNodeNames(): array {
        return ['stmts', 'cond'];
    }

    public function getType(): string {
        return 'Stmt_Do';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

class While_ extends Node\Stmt {
    /** @var Node\Expr Condition */
    public Node\Expr $cond;
    /** @var Node\Stmt[] Statements */
    public array $stmts;

    /**
     * Constructs a while node.
     *
     * @param Node\Expr $cond Condition
     * @param Node\Stmt[] $stmts Statements
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(Node\Expr $cond, array $stmts = [], array $attributes = []) {
        $this->attributes = $attributes;
        $this->cond = $cond;
        $this->stmts = $stmts;
    }

    public function getSubNodeNames(): array {
        return ['cond', 'stmts'];
    }

    public function getType(): string {
        return 'Stmt_While';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node\Stmt;

class Block extends Stmt {
    /** @var Stmt[] Statements */
    public array $stmts;

    /**
     * A block of statements.
     *
     * @param Stmt[] $stmts Statements
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(array $stmts, array $attributes = []) {
        $this->attributes = $attributes;
        $this->stmts = $stmts;
    }

    public function getType(): string {
        return 'Stmt_Block';
    }

    public function getSubNodeNames(): array {
        return ['stmts'];
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node\Stmt;

class InlineHTML extends Stmt {
    /** @var string String */
    public string $value;

    /**
     * Constructs an inline HTML node.
     *
     * @param string $value String
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(string $value, array $attributes = []) {
        $this->attributes = $attributes;
        $this->value = $value;
    }

    public function getSubNodeNames(): array {
        return ['value'];
    }

    public function getType(): string {
        return 'Stmt_InlineHTML';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

class Break_ extends Node\Stmt {
    /** @var null|Node\Expr Number of loops to break */
    public ?Node\Expr $num;

    /**
     * Constructs a break node.
     *
     * @param null|Node\Expr $num Number of loops to break
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(?Node\Expr $num = null, array $attributes = []) {
        $this->attributes = $attributes;
        $this->num = $num;
    }

    public function getSubNodeNames(): array {
        return ['num'];
    }

    public function getType(): string {
        return 'Stmt_Break';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

class For_ extends Node\Stmt {
    /** @var Node\Expr[] Init expressions */
    public array $init;
    /** @var Node\Expr[] Loop conditions */
    public array $cond;
    /** @var Node\Expr[] Loop expressions */
    public array $loop;
    /** @var Node\Stmt[] Statements */
    public array $stmts;

    /**
     * Constructs a for loop node.
     *
     * @param array{
     *     init?: Node\Expr[],
     *     cond?: Node\Expr[],
     *     loop?: Node\Expr[],
     *     stmts?: Node\Stmt[],
     * } $subNodes Array of the following optional subnodes:
     *             'init'  => array(): Init expressions
     *             'cond'  => array(): Loop conditions
     *             'loop'  => array(): Loop expressions
     *             'stmts' => array(): Statements
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(array $subNodes = [], array $attributes = []) {
        $this->attributes = $attributes;
        $this->init = $subNodes['init'] ?? [];
        $this->cond = $subNodes['cond'] ?? [];
        $this->loop = $subNodes['loop'] ?? [];
        $this->stmts = $subNodes['stmts'] ?? [];
    }

    public function getSubNodeNames(): array {
        return ['init', 'cond', 'loop', 'stmts'];
    }

    public function getType(): string {
        return 'Stmt_For';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

class If_ extends Node\Stmt {
    /** @var Node\Expr Condition expression */
    public Node\Expr $cond;
    /** @var Node\Stmt[] Statements */
    public array $stmts;
    /** @var ElseIf_[] Elseif clauses */
    public array $elseifs;
    /** @var null|Else_ Else clause */
    public ?Else_ $else;

    /**
     * Constructs an if node.
     *
     * @param Node\Expr $cond Condition
     * @param array{
     *     stmts?: Node\Stmt[],
     *     elseifs?: ElseIf_[],
     *     else?: Else_|null,
     * } $subNodes Array of the following optional subnodes:
     *             'stmts'   => array(): Statements
     *             'elseifs' => array(): Elseif clauses
     *             'else'    => null   : Else clause
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(Node\Expr $cond, array $subNodes = [], array $attributes = []) {
        $this->attributes = $attributes;
        $this->cond = $cond;
        $this->stmts = $subNodes['stmts'] ?? [];
        $this->elseifs = $subNodes['elseifs'] ?? [];
        $this->else = $subNodes['else'] ?? null;
    }

    public function getSubNodeNames(): array {
        return ['cond', 'stmts', 'elseifs', 'else'];
    }

    public function getType(): string {
        return 'Stmt_If';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node\PropertyItem;

require __DIR__ . '/../PropertyItem.php';

if (false) {
    /**
     * For classmap-authoritative support.
     *
     * @deprecated use \PhpParser\Node\PropertyItem instead.
     */
    class PropertyProperty extends PropertyItem {
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;
use PhpParser\Node\DeclareItem;

class Declare_ extends Node\Stmt {
    /** @var DeclareItem[] List of declares */
    public array $declares;
    /** @var Node\Stmt[]|null Statements */
    public ?array $stmts;

    /**
     * Constructs a declare node.
     *
     * @param DeclareItem[] $declares List of declares
     * @param Node\Stmt[]|null $stmts Statements
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(array $declares, ?array $stmts = null, array $attributes = []) {
        $this->attributes = $attributes;
        $this->declares = $declares;
        $this->stmts = $stmts;
    }

    public function getSubNodeNames(): array {
        return ['declares', 'stmts'];
    }

    public function getType(): string {
        return 'Stmt_Declare';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Modifiers;
use PhpParser\Node;
use PhpParser\Node\ComplexType;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use PhpParser\Node\PropertyItem;

class Property extends Node\Stmt {
    /** @var int Modifiers */
    public int $flags;
    /** @var PropertyItem[] Properties */
    public array $props;
    /** @var null|Identifier|Name|ComplexType Type declaration */
    public ?Node $type;
    /** @var Node\AttributeGroup[] PHP attribute groups */
    public array $attrGroups;
    /** @var Node\PropertyHook[] Property hooks */
    public array $hooks;

    /**
     * Constructs a class property list node.
     *
     * @param int $flags Modifiers
     * @param PropertyItem[] $props Properties
     * @param array<string, mixed> $attributes Additional attributes
     * @param null|Identifier|Name|ComplexType $type Type declaration
     * @param Node\AttributeGroup[] $attrGroups PHP attribute groups
     * @param Node\PropertyHook[] $hooks Property hooks
     */
    public function __construct(int $flags, array $props, array $attributes = [], ?Node $type = null, array $attrGroups = [], array $hooks = []) {
        $this->attributes = $attributes;
        $this->flags = $flags;
        $this->props = $props;
        $this->type = $type;
        $this->attrGroups = $attrGroups;
        $this->hooks = $hooks;
    }

    public function getSubNodeNames(): array {
        return ['attrGroups', 'flags', 'type', 'props', 'hooks'];
    }

    /**
     * Whether the property is explicitly or implicitly public.
     */
    public function isPublic(): bool {
        return ($this->flags & Modifiers::PUBLIC) !== 0
            || ($this->flags & Modifiers::VISIBILITY_MASK) === 0;
    }

    /**
     * Whether the property is protected.
     */
    public function isProtected(): bool {
        return (bool) ($this->flags & Modifiers::PROTECTED);
    }

    /**
     * Whether the property is private.
     */
    public function isPrivate(): bool {
        return (bool) ($this->flags & Modifiers::PRIVATE);
    }

    /**
     * Whether the property is static.
     */
    public function isStatic(): bool {
        return (bool) ($this->flags & Modifiers::STATIC);
    }

    /**
     * Whether the property is readonly.
     */
    public function isReadonly(): bool {
        return (bool) ($this->flags & Modifiers::READONLY);
    }

    /**
     * Whether the property is abstract.
     */
    public function isAbstract(): bool {
        return (bool) ($this->flags & Modifiers::ABSTRACT);
    }

    /**
     * Whether the property is final.
     */
    public function isFinal(): bool {
        return (bool) ($this->flags & Modifiers::FINAL);
    }

    /**
     * Whether the property has explicit public(set) visibility.
     */
    public function isPublicSet(): bool {
        return (bool) ($this->flags & Modifiers::PUBLIC_SET);
    }

    /**
     * Whether the property has explicit protected(set) visibility.
     */
    public function isProtectedSet(): bool {
        return (bool) ($this->flags & Modifiers::PROTECTED_SET);
    }

    /**
     * Whether the property has explicit private(set) visibility.
     */
    public function isPrivateSet(): bool {
        return (bool) ($this->flags & Modifiers::PRIVATE_SET);
    }

    public function getType(): string {
        return 'Stmt_Property';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node\Stmt;

class HaltCompiler extends Stmt {
    /** @var string Remaining text after halt compiler statement. */
    public string $remaining;

    /**
     * Constructs a __halt_compiler node.
     *
     * @param string $remaining Remaining text after halt compiler statement.
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(string $remaining, array $attributes = []) {
        $this->attributes = $attributes;
        $this->remaining = $remaining;
    }

    public function getSubNodeNames(): array {
        return ['remaining'];
    }

    public function getType(): string {
        return 'Stmt_HaltCompiler';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

class Switch_ extends Node\Stmt {
    /** @var Node\Expr Condition */
    public Node\Expr $cond;
    /** @var Case_[] Case list */
    public array $cases;

    /**
     * Constructs a case node.
     *
     * @param Node\Expr $cond Condition
     * @param Case_[] $cases Case list
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(Node\Expr $cond, array $cases, array $attributes = []) {
        $this->attributes = $attributes;
        $this->cond = $cond;
        $this->cases = $cases;
    }

    public function getSubNodeNames(): array {
        return ['cond', 'cases'];
    }

    public function getType(): string {
        return 'Stmt_Switch';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

class ElseIf_ extends Node\Stmt {
    /** @var Node\Expr Condition */
    public Node\Expr $cond;
    /** @var Node\Stmt[] Statements */
    public array $stmts;

    /**
     * Constructs an elseif node.
     *
     * @param Node\Expr $cond Condition
     * @param Node\Stmt[] $stmts Statements
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(Node\Expr $cond, array $stmts = [], array $attributes = []) {
        $this->attributes = $attributes;
        $this->cond = $cond;
        $this->stmts = $stmts;
    }

    public function getSubNodeNames(): array {
        return ['cond', 'stmts'];
    }

    public function getType(): string {
        return 'Stmt_ElseIf';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node\StaticVar;
use PhpParser\Node\Stmt;

class Static_ extends Stmt {
    /** @var StaticVar[] Variable definitions */
    public array $vars;

    /**
     * Constructs a static variables list node.
     *
     * @param StaticVar[] $vars Variable definitions
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(array $vars, array $attributes = []) {
        $this->attributes = $attributes;
        $this->vars = $vars;
    }

    public function getSubNodeNames(): array {
        return ['vars'];
    }

    public function getType(): string {
        return 'Stmt_Static';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node\UseItem;

require __DIR__ . '/../UseItem.php';

if (false) {
    /**
     * For classmap-authoritative support.
     *
     * @deprecated use \PhpParser\Node\UseItem instead.
     */
    class UseUse extends UseItem {
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

class Const_ extends Node\Stmt {
    /** @var Node\Const_[] Constant declarations */
    public array $consts;
    /** @var Node\AttributeGroup[] PHP attribute groups */
    public array $attrGroups;

    /**
     * Constructs a const list node.
     *
     * @param Node\Const_[] $consts Constant declarations
     * @param array<string, mixed> $attributes Additional attributes
     * @param list<Node\AttributeGroup> $attrGroups PHP attribute groups
     */
    public function __construct(
        array $consts,
        array $attributes = [],
        array $attrGroups = []
    ) {
        $this->attributes = $attributes;
        $this->attrGroups = $attrGroups;
        $this->consts = $consts;
    }

    public function getSubNodeNames(): array {
        return ['attrGroups', 'consts'];
    }

    public function getType(): string {
        return 'Stmt_Const';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

class Case_ extends Node\Stmt {
    /** @var null|Node\Expr Condition (null for default) */
    public ?Node\Expr $cond;
    /** @var Node\Stmt[] Statements */
    public array $stmts;

    /**
     * Constructs a case node.
     *
     * @param null|Node\Expr $cond Condition (null for default)
     * @param Node\Stmt[] $stmts Statements
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(?Node\Expr $cond, array $stmts = [], array $attributes = []) {
        $this->attributes = $attributes;
        $this->cond = $cond;
        $this->stmts = $stmts;
    }

    public function getSubNodeNames(): array {
        return ['cond', 'stmts'];
    }

    public function getType(): string {
        return 'Stmt_Case';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

abstract class TraitUseAdaptation extends Node\Stmt {
    /** @var Node\Name|null Trait name */
    public ?Node\Name $trait;
    /** @var Node\Identifier Method name */
    public Node\Identifier $method;
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node\Name;
use PhpParser\Node\Stmt;
use PhpParser\Node\UseItem;

class GroupUse extends Stmt {
    /**
     * @var Use_::TYPE_* Type of group use
     */
    public int $type;
    /** @var Name Prefix for uses */
    public Name $prefix;
    /** @var UseItem[] Uses */
    public array $uses;

    /**
     * Constructs a group use node.
     *
     * @param Name $prefix Prefix for uses
     * @param UseItem[] $uses Uses
     * @param Use_::TYPE_* $type Type of group use
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(Name $prefix, array $uses, int $type = Use_::TYPE_NORMAL, array $attributes = []) {
        $this->attributes = $attributes;
        $this->type = $type;
        $this->prefix = $prefix;
        $this->uses = $uses;
    }

    public function getSubNodeNames(): array {
        return ['type', 'prefix', 'uses'];
    }

    public function getType(): string {
        return 'Stmt_GroupUse';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

class Echo_ extends Node\Stmt {
    /** @var Node\Expr[] Expressions */
    public array $exprs;

    /**
     * Constructs an echo node.
     *
     * @param Node\Expr[] $exprs Expressions
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(array $exprs, array $attributes = []) {
        $this->attributes = $attributes;
        $this->exprs = $exprs;
    }

    public function getSubNodeNames(): array {
        return ['exprs'];
    }

    public function getType(): string {
        return 'Stmt_Echo';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt\TraitUseAdaptation;

use PhpParser\Node;

class Alias extends Node\Stmt\TraitUseAdaptation {
    /** @var null|int New modifier */
    public ?int $newModifier;
    /** @var null|Node\Identifier New name */
    public ?Node\Identifier $newName;

    /**
     * Constructs a trait use precedence adaptation node.
     *
     * @param null|Node\Name $trait Trait name
     * @param string|Node\Identifier $method Method name
     * @param null|int $newModifier New modifier
     * @param null|string|Node\Identifier $newName New name
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(?Node\Name $trait, $method, ?int $newModifier, $newName, array $attributes = []) {
        $this->attributes = $attributes;
        $this->trait = $trait;
        $this->method = \is_string($method) ? new Node\Identifier($method) : $method;
        $this->newModifier = $newModifier;
        $this->newName = \is_string($newName) ? new Node\Identifier($newName) : $newName;
    }

    public function getSubNodeNames(): array {
        return ['trait', 'method', 'newModifier', 'newName'];
    }

    public function getType(): string {
        return 'Stmt_TraitUseAdaptation_Alias';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt\TraitUseAdaptation;

use PhpParser\Node;

class Precedence extends Node\Stmt\TraitUseAdaptation {
    /** @var Node\Name[] Overwritten traits */
    public array $insteadof;

    /**
     * Constructs a trait use precedence adaptation node.
     *
     * @param Node\Name $trait Trait name
     * @param string|Node\Identifier $method Method name
     * @param Node\Name[] $insteadof Overwritten traits
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(Node\Name $trait, $method, array $insteadof, array $attributes = []) {
        $this->attributes = $attributes;
        $this->trait = $trait;
        $this->method = \is_string($method) ? new Node\Identifier($method) : $method;
        $this->insteadof = $insteadof;
    }

    public function getSubNodeNames(): array {
        return ['trait', 'method', 'insteadof'];
    }

    public function getType(): string {
        return 'Stmt_TraitUseAdaptation_Precedence';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

class Interface_ extends ClassLike {
    /** @var Node\Name[] Extended interfaces */
    public array $extends;

    /**
     * Constructs a class node.
     *
     * @param string|Node\Identifier $name Name
     * @param array{
     *     extends?: Node\Name[],
     *     stmts?: Node\Stmt[],
     *     attrGroups?: Node\AttributeGroup[],
     * } $subNodes Array of the following optional subnodes:
     *             'extends'    => array(): Name of extended interfaces
     *             'stmts'      => array(): Statements
     *             'attrGroups' => array(): PHP attribute groups
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct($name, array $subNodes = [], array $attributes = []) {
        $this->attributes = $attributes;
        $this->name = \is_string($name) ? new Node\Identifier($name) : $name;
        $this->extends = $subNodes['extends'] ?? [];
        $this->stmts = $subNodes['stmts'] ?? [];
        $this->attrGroups = $subNodes['attrGroups'] ?? [];
    }

    public function getSubNodeNames(): array {
        return ['attrGroups', 'name', 'extends', 'stmts'];
    }

    public function getType(): string {
        return 'Stmt_Interface';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node\Identifier;
use PhpParser\Node\Stmt;

class Label extends Stmt {
    /** @var Identifier Name */
    public Identifier $name;

    /**
     * Constructs a label node.
     *
     * @param string|Identifier $name Name
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct($name, array $attributes = []) {
        $this->attributes = $attributes;
        $this->name = \is_string($name) ? new Identifier($name) : $name;
    }

    public function getSubNodeNames(): array {
        return ['name'];
    }

    public function getType(): string {
        return 'Stmt_Label';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

class Continue_ extends Node\Stmt {
    /** @var null|Node\Expr Number of loops to continue */
    public ?Node\Expr $num;

    /**
     * Constructs a continue node.
     *
     * @param null|Node\Expr $num Number of loops to continue
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(?Node\Expr $num = null, array $attributes = []) {
        $this->attributes = $attributes;
        $this->num = $num;
    }

    public function getSubNodeNames(): array {
        return ['num'];
    }

    public function getType(): string {
        return 'Stmt_Continue';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

class Global_ extends Node\Stmt {
    /** @var Node\Expr[] Variables */
    public array $vars;

    /**
     * Constructs a global variables list node.
     *
     * @param Node\Expr[] $vars Variables to unset
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(array $vars, array $attributes = []) {
        $this->attributes = $attributes;
        $this->vars = $vars;
    }

    public function getSubNodeNames(): array {
        return ['vars'];
    }

    public function getType(): string {
        return 'Stmt_Global';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;
use PhpParser\Node\AttributeGroup;

class EnumCase extends Node\Stmt {
    /** @var Node\Identifier Enum case name */
    public Node\Identifier $name;
    /** @var Node\Expr|null Enum case expression */
    public ?Node\Expr $expr;
    /** @var Node\AttributeGroup[] PHP attribute groups */
    public array $attrGroups;

    /**
     * @param string|Node\Identifier $name Enum case name
     * @param Node\Expr|null $expr Enum case expression
     * @param list<AttributeGroup> $attrGroups PHP attribute groups
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct($name, ?Node\Expr $expr = null, array $attrGroups = [], array $attributes = []) {
        parent::__construct($attributes);
        $this->name = \is_string($name) ? new Node\Identifier($name) : $name;
        $this->expr = $expr;
        $this->attrGroups = $attrGroups;
    }

    public function getSubNodeNames(): array {
        return ['attrGroups', 'name', 'expr'];
    }

    public function getType(): string {
        return 'Stmt_EnumCase';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Modifiers;
use PhpParser\Node;

class Class_ extends ClassLike {
    /** @deprecated Use Modifiers::PUBLIC instead */
    public const MODIFIER_PUBLIC    =  1;
    /** @deprecated Use Modifiers::PROTECTED instead */
    public const MODIFIER_PROTECTED =  2;
    /** @deprecated Use Modifiers::PRIVATE instead */
    public const MODIFIER_PRIVATE   =  4;
    /** @deprecated Use Modifiers::STATIC instead */
    public const MODIFIER_STATIC    =  8;
    /** @deprecated Use Modifiers::ABSTRACT instead */
    public const MODIFIER_ABSTRACT  = 16;
    /** @deprecated Use Modifiers::FINAL instead */
    public const MODIFIER_FINAL     = 32;
    /** @deprecated Use Modifiers::READONLY instead */
    public const MODIFIER_READONLY  = 64;

    /** @deprecated Use Modifiers::VISIBILITY_MASK instead */
    public const VISIBILITY_MODIFIER_MASK = 7; // 1 | 2 | 4

    /** @var int Modifiers */
    public int $flags;
    /** @var null|Node\Name Name of extended class */
    public ?Node\Name $extends;
    /** @var Node\Name[] Names of implemented interfaces */
    public array $implements;

    /**
     * Constructs a class node.
     *
     * @param string|Node\Identifier|null $name Name
     * @param array{
     *     flags?: int,
     *     extends?: Node\Name|null,
     *     implements?: Node\Name[],
     *     stmts?: Node\Stmt[],
     *     attrGroups?: Node\AttributeGroup[],
     * } $subNodes Array of the following optional subnodes:
     *             'flags'       => 0      : Flags
     *             'extends'     => null   : Name of extended class
     *             'implements'  => array(): Names of implemented interfaces
     *             'stmts'       => array(): Statements
     *             'attrGroups'  => array(): PHP attribute groups
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct($name, array $subNodes = [], array $attributes = []) {
        $this->attributes = $attributes;
        $this->flags = $subNodes['flags'] ?? $subNodes['type'] ?? 0;
        $this->name = \is_string($name) ? new Node\Identifier($name) : $name;
        $this->extends = $subNodes['extends'] ?? null;
        $this->implements = $subNodes['implements'] ?? [];
        $this->stmts = $subNodes['stmts'] ?? [];
        $this->attrGroups = $subNodes['attrGroups'] ?? [];
    }

    public function getSubNodeNames(): array {
        return ['attrGroups', 'flags', 'name', 'extends', 'implements', 'stmts'];
    }

    /**
     * Whether the class is explicitly abstract.
     */
    public function isAbstract(): bool {
        return (bool) ($this->flags & Modifiers::ABSTRACT);
    }

    /**
     * Whether the class is final.
     */
    public function isFinal(): bool {
        return (bool) ($this->flags & Modifiers::FINAL);
    }

    public function isReadonly(): bool {
        return (bool) ($this->flags & Modifiers::READONLY);
    }

    /**
     * Whether the class is anonymous.
     */
    public function isAnonymous(): bool {
        return null === $this->name;
    }

    public function getType(): string {
        return 'Stmt_Class';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

/** Nop/empty statement (;). */
class Nop extends Node\Stmt {
    public function getSubNodeNames(): array {
        return [];
    }

    public function getType(): string {
        return 'Stmt_Nop';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Modifiers;
use PhpParser\Node;

class ClassConst extends Node\Stmt {
    /** @var int Modifiers */
    public int $flags;
    /** @var Node\Const_[] Constant declarations */
    public array $consts;
    /** @var Node\AttributeGroup[] PHP attribute groups */
    public array $attrGroups;
    /** @var Node\Identifier|Node\Name|Node\ComplexType|null Type declaration */
    public ?Node $type;

    /**
     * Constructs a class const list node.
     *
     * @param Node\Const_[] $consts Constant declarations
     * @param int $flags Modifiers
     * @param array<string, mixed> $attributes Additional attributes
     * @param list<Node\AttributeGroup> $attrGroups PHP attribute groups
     * @param null|Node\Identifier|Node\Name|Node\ComplexType $type Type declaration
     */
    public function __construct(
        array $consts,
        int $flags = 0,
        array $attributes = [],
        array $attrGroups = [],
        ?Node $type = null
    ) {
        $this->attributes = $attributes;
        $this->flags = $flags;
        $this->consts = $consts;
        $this->attrGroups = $attrGroups;
        $this->type = $type;
    }

    public function getSubNodeNames(): array {
        return ['attrGroups', 'flags', 'type', 'consts'];
    }

    /**
     * Whether constant is explicitly or implicitly public.
     */
    public function isPublic(): bool {
        return ($this->flags & Modifiers::PUBLIC) !== 0
            || ($this->flags & Modifiers::VISIBILITY_MASK) === 0;
    }

    /**
     * Whether constant is protected.
     */
    public function isProtected(): bool {
        return (bool) ($this->flags & Modifiers::PROTECTED);
    }

    /**
     * Whether constant is private.
     */
    public function isPrivate(): bool {
        return (bool) ($this->flags & Modifiers::PRIVATE);
    }

    /**
     * Whether constant is final.
     */
    public function isFinal(): bool {
        return (bool) ($this->flags & Modifiers::FINAL);
    }

    public function getType(): string {
        return 'Stmt_ClassConst';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

class Trait_ extends ClassLike {
    /**
     * Constructs a trait node.
     *
     * @param string|Node\Identifier $name Name
     * @param array{
     *     stmts?: Node\Stmt[],
     *     attrGroups?: Node\AttributeGroup[],
     * } $subNodes Array of the following optional subnodes:
     *             'stmts'      => array(): Statements
     *             'attrGroups' => array(): PHP attribute groups
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct($name, array $subNodes = [], array $attributes = []) {
        $this->attributes = $attributes;
        $this->name = \is_string($name) ? new Node\Identifier($name) : $name;
        $this->stmts = $subNodes['stmts'] ?? [];
        $this->attrGroups = $subNodes['attrGroups'] ?? [];
    }

    public function getSubNodeNames(): array {
        return ['attrGroups', 'name', 'stmts'];
    }

    public function getType(): string {
        return 'Stmt_Trait';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

class Finally_ extends Node\Stmt {
    /** @var Node\Stmt[] Statements */
    public array $stmts;

    /**
     * Constructs a finally node.
     *
     * @param Node\Stmt[] $stmts Statements
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(array $stmts = [], array $attributes = []) {
        $this->attributes = $attributes;
        $this->stmts = $stmts;
    }

    public function getSubNodeNames(): array {
        return ['stmts'];
    }

    public function getType(): string {
        return 'Stmt_Finally';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

class Unset_ extends Node\Stmt {
    /** @var Node\Expr[] Variables to unset */
    public array $vars;

    /**
     * Constructs an unset node.
     *
     * @param Node\Expr[] $vars Variables to unset
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(array $vars, array $attributes = []) {
        $this->attributes = $attributes;
        $this->vars = $vars;
    }

    public function getSubNodeNames(): array {
        return ['vars'];
    }

    public function getType(): string {
        return 'Stmt_Unset';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

/**
 * Represents statements of type "expr;"
 */
class Expression extends Node\Stmt {
    /** @var Node\Expr Expression */
    public Node\Expr $expr;

    /**
     * Constructs an expression statement.
     *
     * @param Node\Expr $expr Expression
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(Node\Expr $expr, array $attributes = []) {
        $this->attributes = $attributes;
        $this->expr = $expr;
    }

    public function getSubNodeNames(): array {
        return ['expr'];
    }

    public function getType(): string {
        return 'Stmt_Expression';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

class TryCatch extends Node\Stmt {
    /** @var Node\Stmt[] Statements */
    public array $stmts;
    /** @var Catch_[] Catches */
    public array $catches;
    /** @var null|Finally_ Optional finally node */
    public ?Finally_ $finally;

    /**
     * Constructs a try catch node.
     *
     * @param Node\Stmt[] $stmts Statements
     * @param Catch_[] $catches Catches
     * @param null|Finally_ $finally Optional finally node
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(array $stmts, array $catches, ?Finally_ $finally = null, array $attributes = []) {
        $this->attributes = $attributes;
        $this->stmts = $stmts;
        $this->catches = $catches;
        $this->finally = $finally;
    }

    public function getSubNodeNames(): array {
        return ['stmts', 'catches', 'finally'];
    }

    public function getType(): string {
        return 'Stmt_TryCatch';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

class TraitUse extends Node\Stmt {
    /** @var Node\Name[] Traits */
    public array $traits;
    /** @var TraitUseAdaptation[] Adaptations */
    public array $adaptations;

    /**
     * Constructs a trait use node.
     *
     * @param Node\Name[] $traits Traits
     * @param TraitUseAdaptation[] $adaptations Adaptations
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(array $traits, array $adaptations = [], array $attributes = []) {
        $this->attributes = $attributes;
        $this->traits = $traits;
        $this->adaptations = $adaptations;
    }

    public function getSubNodeNames(): array {
        return ['traits', 'adaptations'];
    }

    public function getType(): string {
        return 'Stmt_TraitUse';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;

class Namespace_ extends Node\Stmt {
    /* For use in the "kind" attribute */
    public const KIND_SEMICOLON = 1;
    public const KIND_BRACED = 2;

    /** @var null|Node\Name Name */
    public ?Node\Name $name;
    /** @var Node\Stmt[] Statements */
    public $stmts;

    /**
     * Constructs a namespace node.
     *
     * @param null|Node\Name $name Name
     * @param null|Node\Stmt[] $stmts Statements
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(?Node\Name $name = null, ?array $stmts = [], array $attributes = []) {
        $this->attributes = $attributes;
        $this->name = $name;
        $this->stmts = $stmts;
    }

    public function getSubNodeNames(): array {
        return ['name', 'stmts'];
    }

    public function getType(): string {
        return 'Stmt_Namespace';
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node;
use PhpParser\Node\PropertyItem;

abstract class ClassLike extends Node\Stmt {
    /** @var Node\Identifier|null Name */
    public ?Node\Identifier $name;
    /** @var Node\Stmt[] Statements */
    public array $stmts;
    /** @var Node\AttributeGroup[] PHP attribute groups */
    public array $attrGroups;

    /** @var Node\Name|null Namespaced name (if using NameResolver) */
    public ?Node\Name $namespacedName;

    /**
     * @return list<TraitUse>
     */
    public function getTraitUses(): array {
        $traitUses = [];
        foreach ($this->stmts as $stmt) {
            if ($stmt instanceof TraitUse) {
                $traitUses[] = $stmt;
            }
        }
        return $traitUses;
    }

    /**
     * @return list<ClassConst>
     */
    public function getConstants(): array {
        $constants = [];
        foreach ($this->stmts as $stmt) {
            if ($stmt instanceof ClassConst) {
                $constants[] = $stmt;
            }
        }
        return $constants;
    }

    /**
     * @return list<Property>
     */
    public function getProperties(): array {
        $properties = [];
        foreach ($this->stmts as $stmt) {
            if ($stmt instanceof Property) {
                $properties[] = $stmt;
            }
        }
        return $properties;
    }

    /**
     * Gets property with the given name defined directly in this class/interface/trait.
     *
     * @param string $name Name of the property
     *
     * @return Property|null Property node or null if the property does not exist
     */
    public function getProperty(string $name): ?Property {
        foreach ($this->stmts as $stmt) {
            if ($stmt instanceof Property) {
                foreach ($stmt->props as $prop) {
                    if ($prop instanceof PropertyItem && $name === $prop->name->toString()) {
                        return $stmt;
                    }
                }
            }
        }
        return null;
    }

    /**
     * Gets all methods defined directly in this class/interface/trait
     *
     * @return list<ClassMethod>
     */
    public function getMethods(): array {
        $methods = [];
        foreach ($this->stmts as $stmt) {
            if ($stmt instanceof ClassMethod) {
                $methods[] = $stmt;
            }
        }
        return $methods;
    }

    /**
     * Gets method with the given name defined directly in this class/interface/trait.
     *
     * @param string $name Name of the method (compared case-insensitively)
     *
     * @return ClassMethod|null Method node or null if the method does not exist
     */
    public function getMethod(string $name): ?ClassMethod {
        $lowerName = strtolower($name);
        foreach ($this->stmts as $stmt) {
            if ($stmt instanceof ClassMethod && $lowerName === $stmt->name->toLowerString()) {
                return $stmt;
            }
        }
        return null;
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Node;

use PhpParser\NodeAbstract;

abstract class Stmt extends NodeAbstract {
}
<?php declare(strict_types=1);

namespace PhpParser\Node;

use PhpParser\NodeAbstract;

class InterpolatedStringPart extends NodeAbstract {
    /** @var string String value */
    public string $value;

    /**
     * Constructs a node representing a string part of an interpolated string.
     *
     * @param string $value String value
     * @param array<string, mixed> $attributes Additional attributes
     */
    public function __construct(string $value, array $attributes = []) {
        $this->attributes = $attributes;
        $this->value = $value;
    }

    public function getSubNodeNames(): array {
        return ['value'];
    }

    public function getType(): string {
        return 'InterpolatedStringPart';
    }
}

// @deprecated compatibility alias
class_alias(InterpolatedStringPart::class, Scalar\EncapsedStringPart::class);
<?php declare(strict_types=1);

namespace PhpParser\PrettyPrinter;

use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\AssignOp;
use PhpParser\Node\Expr\BinaryOp;
use PhpParser\Node\Expr\Cast;
use PhpParser\Node\Name;
use PhpParser\Node\Scalar;
use PhpParser\Node\Scalar\MagicConst;
use PhpParser\Node\Stmt;
use PhpParser\PrettyPrinterAbstract;

class Standard extends PrettyPrinterAbstract {
    // Special nodes

    protected function pParam(Node\Param $node): string {
        return $this->pAttrGroups($node->attrGroups, $this->phpVersion->supportsAttributes())
             . $this->pModifiers($node->flags)
             . ($node->type ? $this->p($node->type) . ' ' : '')
             . ($node->byRef ? '&' : '')
             . ($node->variadic ? '...' : '')
             . $this->p($node->var)
             . ($node->default ? ' = ' . $this->p($node->default) : '')
             . ($node->hooks ? ' {' . $this->pStmts($node->hooks) . $this->nl . '}' : '');
    }

    protected function pArg(Node\Arg $node): string {
        return ($node->name ? $node->name->toString() . ': ' : '')
             . ($node->byRef ? '&' : '') . ($node->unpack ? '...' : '')
             . $this->p($node->value);
    }

    protected function pVariadicPlaceholder(Node\VariadicPlaceholder $node): string {
        return '...';
    }

    protected function pArgPlaceholder(Node\ArgPlaceholder $node): string {
        return ($node->name ? $node->name->toString() . ': ' : '') . '?';
    }

    protected function pConst(Node\Const_ $node): string {
        return $node->name . ' = ' . $this->p($node->value);
    }

    protected function pNullableType(Node\NullableType $node): string {
        return '?' . $this->p($node->type);
    }

    protected function pUnionType(Node\UnionType $node): string {
        $types = [];
        foreach ($node->types as $typeNode) {
            if ($typeNode instanceof Node\IntersectionType) {
                $types[] = '('. $this->p($typeNode) . ')';
                continue;
            }
            $types[] = $this->p($typeNode);
        }
        return implode('|', $types);
    }

    protected function pIntersectionType(Node\IntersectionType $node): string {
        return $this->pImplode($node->types, '&');
    }

    protected function pIdentifier(Node\Identifier $node): string {
        return $node->name;
    }

    protected function pVarLikeIdentifier(Node\VarLikeIdentifier $node): string {
        return '$' . $node->name;
    }

    protected function pAttribute(Node\Attribute $node): string {
        return $this->p($node->name)
             . ($node->args ? '(' . $this->pCommaSeparated($node->args) . ')' : '');
    }

    protected function pAttributeGroup(Node\AttributeGroup $node): string {
        return '#[' . $this->pCommaSeparated($node->attrs) . ']';
    }

    // Names

    protected function pName(Name $node): string {
        return $node->name;
    }

    protected function pName_FullyQualified(Name\FullyQualified $node): string {
        return '\\' . $node->name;
    }

    protected function pName_Relative(Name\Relative $node): string {
        return 'namespace\\' . $node->name;
    }

    // Magic Constants

    protected function pScalar_MagicConst_Class(MagicConst\Class_ $node): string {
        return '__CLASS__';
    }

    protected function pScalar_MagicConst_Dir(MagicConst\Dir $node): string {
        return '__DIR__';
    }

    protected function pScalar_MagicConst_File(MagicConst\File $node): string {
        return '__FILE__';
    }

    protected function pScalar_MagicConst_Function(MagicConst\Function_ $node): string {
        return '__FUNCTION__';
    }

    protected function pScalar_MagicConst_Line(MagicConst\Line $node): string {
        return '__LINE__';
    }

    protected function pScalar_MagicConst_Method(MagicConst\Method $node): string {
        return '__METHOD__';
    }

    protected function pScalar_MagicConst_Namespace(MagicConst\Namespace_ $node): string {
        return '__NAMESPACE__';
    }

    protected function pScalar_MagicConst_Trait(MagicConst\Trait_ $node): string {
        return '__TRAIT__';
    }

    protected function pScalar_MagicConst_Property(MagicConst\Property $node): string {
        return '__PROPERTY__';
    }

    // Scalars

    private function indentString(string $str): string {
        return str_replace("\n", $this->nl, $str);
    }

    protected function pScalar_String(Scalar\String_ $node): string {
        $kind = $node->getAttribute('kind', Scalar\String_::KIND_SINGLE_QUOTED);
        switch ($kind) {
            case Scalar\String_::KIND_NOWDOC:
                $label = $node->getAttribute('docLabel');
                if ($label && !$this->containsEndLabel($node->value, $label)) {
                    $shouldIdent = $this->phpVersion->supportsFlexibleHeredoc();
                    $nl = $shouldIdent ? $this->nl : $this->newline;
                    if ($node->value === '') {
                        return "<<<'$label'$nl$label{$this->docStringEndToken}";
                    }

                    // Make sure trailing \r is not combined with following \n into CRLF.
                    if ($node->value[strlen($node->value) - 1] !== "\r") {
                        $value = $shouldIdent ? $this->indentString($node->value) : $node->value;
                        return "<<<'$label'$nl$value$nl$label{$this->docStringEndToken}";
                    }
                }
                /* break missing intentionally */
                // no break
            case Scalar\String_::KIND_SINGLE_QUOTED:
                return $this->pSingleQuotedString($node->value);
            case Scalar\String_::KIND_HEREDOC:
                $label = $node->getAttribute('docLabel');
                $escaped = $this->escapeString($node->value, null);
                if ($label && !$this->containsEndLabel($escaped, $label)) {
                    $nl = $this->phpVersion->supportsFlexibleHeredoc() ? $this->nl : $this->newline;
                    if ($escaped === '') {
                        return "<<<$label$nl$label{$this->docStringEndToken}";
                    }

                    return "<<<$label$nl$escaped$nl$label{$this->docStringEndToken}";
                }
                /* break missing intentionally */
                // no break
            case Scalar\String_::KIND_DOUBLE_QUOTED:
                return '"' . $this->escapeString($node->value, '"') . '"';
        }
        throw new \Exception('Invalid string kind');
    }

    protected function pScalar_InterpolatedString(Scalar\InterpolatedString $node): string {
        if ($node->getAttribute('kind') === Scalar\String_::KIND_HEREDOC) {
            $label = $node->getAttribute('docLabel');
            if ($label && !$this->encapsedContainsEndLabel($node->parts, $label)) {
                $nl = $this->phpVersion->supportsFlexibleHeredoc() ? $this->nl : $this->newline;
                if (count($node->parts) === 1
                    && $node->parts[0] instanceof Node\InterpolatedStringPart
                    && $node->parts[0]->value === ''
                ) {
                    return "<<<$label$nl$label{$this->docStringEndToken}";
                }

                return "<<<$label$nl" . $this->pEncapsList($node->parts, null)
                     . "$nl$label{$this->docStringEndToken}";
            }
        }
        return '"' . $this->pEncapsList($node->parts, '"') . '"';
    }

    protected function pScalar_Int(Scalar\Int_ $node): string {
        if ($node->getAttribute('shouldPrintRawValue') === true) {
            return $node->getAttribute('rawValue');
        }

        if ($node->value === -\PHP_INT_MAX - 1) {
            // PHP_INT_MIN cannot be represented as a literal,
            // because the sign is not part of the literal
            return '(-' . \PHP_INT_MAX . '-1)';
        }

        $kind = $node->getAttribute('kind', Scalar\Int_::KIND_DEC);

        if (Scalar\Int_::KIND_DEC === $kind) {
            return (string) $node->value;
        }

        if ($node->value < 0) {
            $sign = '-';
            $str = (string) -$node->value;
        } else {
            $sign = '';
            $str = (string) $node->value;
        }
        switch ($kind) {
            case Scalar\Int_::KIND_BIN:
                return $sign . '0b' . base_convert($str, 10, 2);
            case Scalar\Int_::KIND_OCT:
                return $sign . '0' . base_convert($str, 10, 8);
            case Scalar\Int_::KIND_HEX:
                return $sign . '0x' . base_convert($str, 10, 16);
        }
        throw new \Exception('Invalid number kind');
    }

    protected function pScalar_Float(Scalar\Float_ $node): string {
        if (!is_finite($node->value)) {
            if ($node->value === \INF) {
                return '1.0E+1000';
            }
            if ($node->value === -\INF) {
                return '-1.0E+1000';
            } else {
                return '\NAN';
            }
        }

        // Try to find a short full-precision representation
        $stringValue = sprintf('%.16G', $node->value);
        if ($node->value !== (float) $stringValue) {
            $stringValue = sprintf('%.17G', $node->value);
        }

        // %G is locale dependent and there exists no locale-independent alternative. We don't want
        // mess with switching locales here, so let's assume that a comma is the only non-standard
        // decimal separator we may encounter...
        $stringValue = str_replace(',', '.', $stringValue);

        // ensure that number is really printed as float
        return preg_match('/^-?[0-9]+$/', $stringValue) ? $stringValue . '.0' : $stringValue;
    }

    // Assignments

    protected function pExpr_Assign(Expr\Assign $node, int $precedence, int $lhsPrecedence): string {
        return $this->pPrefixOp(Expr\Assign::class, $this->p($node->var) . ' = ', $node->expr, $precedence, $lhsPrecedence);
    }

    protected function pExpr_AssignRef(Expr\AssignRef $node, int $precedence, int $lhsPrecedence): string {
        return $this->pPrefixOp(Expr\AssignRef::class, $this->p($node->var) . ' =& ', $node->expr, $precedence, $lhsPrecedence);
    }

    protected function pExpr_AssignOp_Plus(AssignOp\Plus $node, int $precedence, int $lhsPrecedence): string {
        return $this->pPrefixOp(AssignOp\Plus::class, $this->p($node->var) . ' += ', $node->expr, $precedence, $lhsPrecedence);
    }

    protected function pExpr_AssignOp_Minus(AssignOp\Minus $node, int $precedence, int $lhsPrecedence): string {
        return $this->pPrefixOp(AssignOp\Minus::class, $this->p($node->var) . ' -= ', $node->expr, $precedence, $lhsPrecedence);
    }

    protected function pExpr_AssignOp_Mul(AssignOp\Mul $node, int $precedence, int $lhsPrecedence): string {
        return $this->pPrefixOp(AssignOp\Mul::class, $this->p($node->var) . ' *= ', $node->expr, $precedence, $lhsPrecedence);
    }

    protected function pExpr_AssignOp_Div(AssignOp\Div $node, int $precedence, int $lhsPrecedence): string {
        return $this->pPrefixOp(AssignOp\Div::class, $this->p($node->var) . ' /= ', $node->expr, $precedence, $lhsPrecedence);
    }

    protected function pExpr_AssignOp_Concat(AssignOp\Concat $node, int $precedence, int $lhsPrecedence): string {
        return $this->pPrefixOp(AssignOp\Concat::class, $this->p($node->var) . ' .= ', $node->expr, $precedence, $lhsPrecedence);
    }

    protected function pExpr_AssignOp_Mod(AssignOp\Mod $node, int $precedence, int $lhsPrecedence): string {
        return $this->pPrefixOp(AssignOp\Mod::class, $this->p($node->var) . ' %= ', $node->expr, $precedence, $lhsPrecedence);
    }

    protected function pExpr_AssignOp_BitwiseAnd(AssignOp\BitwiseAnd $node, int $precedence, int $lhsPrecedence): string {
        return $this->pPrefixOp(AssignOp\BitwiseAnd::class, $this->p($node->var) . ' &= ', $node->expr, $precedence, $lhsPrecedence);
    }

    protected function pExpr_AssignOp_BitwiseOr(AssignOp\BitwiseOr $node, int $precedence, int $lhsPrecedence): string {
        return $this->pPrefixOp(AssignOp\BitwiseOr::class, $this->p($node->var) . ' |= ', $node->expr, $precedence, $lhsPrecedence);
    }

    protected function pExpr_AssignOp_BitwiseXor(AssignOp\BitwiseXor $node, int $precedence, int $lhsPrecedence): string {
        return $this->pPrefixOp(AssignOp\BitwiseXor::class, $this->p($node->var) . ' ^= ', $node->expr, $precedence, $lhsPrecedence);
    }

    protected function pExpr_AssignOp_ShiftLeft(AssignOp\ShiftLeft $node, int $precedence, int $lhsPrecedence): string {
        return $this->pPrefixOp(AssignOp\ShiftLeft::class, $this->p($node->var) . ' <<= ', $node->expr, $precedence, $lhsPrecedence);
    }

    protected function pExpr_AssignOp_ShiftRight(AssignOp\ShiftRight $node, int $precedence, int $lhsPrecedence): string {
        return $this->pPrefixOp(AssignOp\ShiftRight::class, $this->p($node->var) . ' >>= ', $node->expr, $precedence, $lhsPrecedence);
    }

    protected function pExpr_AssignOp_Pow(AssignOp\Pow $node, int $precedence, int $lhsPrecedence): string {
        return $this->pPrefixOp(AssignOp\Pow::class, $this->p($node->var) . ' **= ', $node->expr, $precedence, $lhsPrecedence);
    }

    protected function pExpr_AssignOp_Coalesce(AssignOp\Coalesce $node, int $precedence, int $lhsPrecedence): string {
        return $this->pPrefixOp(AssignOp\Coalesce::class, $this->p($node->var) . ' ??= ', $node->expr, $precedence, $lhsPrecedence);
    }

    // Binary expressions

    protected function pExpr_BinaryOp_Plus(BinaryOp\Plus $node, int $precedence, int $lhsPrecedence): string {
        return $this->pInfixOp(BinaryOp\Plus::class, $node->left, ' + ', $node->right, $precedence, $lhsPrecedence);
    }

    protected function pExpr_BinaryOp_Minus(BinaryOp\Minus $node, int $precedence, int $lhsPrecedence): string {
        return $this->pInfixOp(BinaryOp\Minus::class, $node->left, ' - ', $node->right, $precedence, $lhsPrecedence);
    }

    protected function pExpr_BinaryOp_Mul(BinaryOp\Mul $node, int $precedence, int $lhsPrecedence): string {
        return $this->pInfixOp(BinaryOp\Mul::class, $node->left, ' * ', $node->right, $precedence, $lhsPrecedence);
    }

    protected function pExpr_BinaryOp_Div(BinaryOp\Div $node, int $precedence, int $lhsPrecedence): string {
        return $this->pInfixOp(BinaryOp\Div::class, $node->left, ' / ', $node->right, $precedence, $lhsPrecedence);
    }

    protected function pExpr_BinaryOp_Concat(BinaryOp\Concat $node, int $precedence, int $lhsPrecedence): string {
        return $this->pInfixOp(BinaryOp\Concat::class, $node->left, ' . ', $node->right, $precedence, $lhsPrecedence);
    }

    protected function pExpr_BinaryOp_Mod(BinaryOp\Mod $node, int $precedence, int $lhsPrecedence): string {
        return $this->pInfixOp(BinaryOp\Mod::class, $node->left, ' % ', $node->right, $precedence, $lhsPrecedence);
    }

    protected function pExpr_BinaryOp_BooleanAnd(BinaryOp\BooleanAnd $node, int $precedence, int $lhsPrecedence): string {
        return $this->pInfixOp(BinaryOp\BooleanAnd::class, $node->left, ' && ', $node->right, $precedence, $lhsPrecedence);
    }

    protected function pExpr_BinaryOp_BooleanOr(BinaryOp\BooleanOr $node, int $precedence, int $lhsPrecedence): string {
        return $this->pInfixOp(BinaryOp\BooleanOr::class, $node->left, ' || ', $node->right, $precedence, $lhsPrecedence);
    }

    protected function pExpr_BinaryOp_BitwiseAnd(BinaryOp\BitwiseAnd $node, int $precedence, int $lhsPrecedence): string {
        return $this->pInfixOp(BinaryOp\BitwiseAnd::class, $node->left, ' & ', $node->right, $precedence, $lhsPrecedence);
    }

    protected function pExpr_BinaryOp_BitwiseOr(BinaryOp\BitwiseOr $node, int $precedence, int $lhsPrecedence): string {
        return $this->pInfixOp(BinaryOp\BitwiseOr::class, $node->left, ' | ', $node->right, $precedence, $lhsPrecedence);
    }

    protected function pExpr_BinaryOp_BitwiseXor(BinaryOp\BitwiseXor $node, int $precedence, int $lhsPrecedence): string {
        return $this->pInfixOp(BinaryOp\BitwiseXor::class, $node->left, ' ^ ', $node->right, $precedence, $lhsPrecedence);
    }

    protected function pExpr_BinaryOp_ShiftLeft(BinaryOp\ShiftLeft $node, int $precedence, int $lhsPrecedence): string {
        return $this->pInfixOp(BinaryOp\ShiftLeft::class, $node->left, ' << ', $node->right, $precedence, $lhsPrecedence);
    }

    protected function pExpr_BinaryOp_ShiftRight(BinaryOp\ShiftRight $node, int $precedence, int $lhsPrecedence): string {
        return $this->pInfixOp(BinaryOp\ShiftRight::class, $node->left, ' >> ', $node->right, $precedence, $lhsPrecedence);
    }

    protected function pExpr_BinaryOp_Pow(BinaryOp\Pow $node, int $precedence, int $lhsPrecedence): string {
        return $this->pInfixOp(BinaryOp\Pow::class, $node->left, ' ** ', $node->right, $precedence, $lhsPrecedence);
    }

    protected function pExpr_BinaryOp_LogicalAnd(BinaryOp\LogicalAnd $node, int $precedence, int $lhsPrecedence): string {
        return $this->pInfixOp(BinaryOp\LogicalAnd::class, $node->left, ' and ', $node->right, $precedence, $lhsPrecedence);
    }

    protected function pExpr_BinaryOp_LogicalOr(BinaryOp\LogicalOr $node, int $precedence, int $lhsPrecedence): string {
        return $this->pInfixOp(BinaryOp\LogicalOr::class, $node->left, ' or ', $node->right, $precedence, $lhsPrecedence);
    }

    protected function pExpr_BinaryOp_LogicalXor(BinaryOp\LogicalXor $node, int $precedence, int $lhsPrecedence): string {
        return $this->pInfixOp(BinaryOp\LogicalXor::class, $node->left, ' xor ', $node->right, $precedence, $lhsPrecedence);
    }

    protected function pExpr_BinaryOp_Equal(BinaryOp\Equal $node, int $precedence, int $lhsPrecedence): string {
        return $this->pInfixOp(BinaryOp\Equal::class, $node->left, ' == ', $node->right, $precedence, $lhsPrecedence);
    }

    protected function pExpr_BinaryOp_NotEqual(BinaryOp\NotEqual $node, int $precedence, int $lhsPrecedence): string {
        return $this->pInfixOp(BinaryOp\NotEqual::class, $node->left, ' != ', $node->right, $precedence, $lhsPrecedence);
    }

    protected function pExpr_BinaryOp_Identical(BinaryOp\Identical $node, int $precedence, int $lhsPrecedence): string {
        return $this->pInfixOp(BinaryOp\Identical::class, $node->left, ' === ', $node->right, $precedence, $lhsPrecedence);
    }

    protected function pExpr_BinaryOp_NotIdentical(BinaryOp\NotIdentical $node, int $precedence, int $lhsPrecedence): string {
        return $this->pInfixOp(BinaryOp\NotIdentical::class, $node->left, ' !== ', $node->right, $precedence, $lhsPrecedence);
    }

    protected function pExpr_BinaryOp_Spaceship(BinaryOp\Spaceship $node, int $precedence, int $lhsPrecedence): string {
        return $this->pInfixOp(BinaryOp\Spaceship::class, $node->left, ' <=> ', $node->right, $precedence, $lhsPrecedence);
    }

    protected function pExpr_BinaryOp_Greater(BinaryOp\Greater $node, int $precedence, int $lhsPrecedence): string {
        return $this->pInfixOp(BinaryOp\Greater::class, $node->left, ' > ', $node->right, $precedence, $lhsPrecedence);
    }

    protected function pExpr_BinaryOp_GreaterOrEqual(BinaryOp\GreaterOrEqual $node, int $precedence, int $lhsPrecedence): string {
        return $this->pInfixOp(BinaryOp\GreaterOrEqual::class, $node->left, ' >= ', $node->right, $precedence, $lhsPrecedence);
    }

    protected function pExpr_BinaryOp_Smaller(BinaryOp\Smaller $node, int $precedence, int $lhsPrecedence): string {
        return $this->pInfixOp(BinaryOp\Smaller::class, $node->left, ' < ', $node->right, $precedence, $lhsPrecedence);
    }

    protected function pExpr_BinaryOp_SmallerOrEqual(BinaryOp\SmallerOrEqual $node, int $precedence, int $lhsPrecedence): string {
        return $this->pInfixOp(BinaryOp\SmallerOrEqual::class, $node->left, ' <= ', $node->right, $precedence, $lhsPrecedence);
    }

    protected function pExpr_BinaryOp_Coalesce(BinaryOp\Coalesce $node, int $precedence, int $lhsPrecedence): string {
        return $this->pInfixOp(BinaryOp\Coalesce::class, $node->left, ' ?? ', $node->right, $precedence, $lhsPrecedence);
    }

    protected function pExpr_BinaryOp_Pipe(BinaryOp\Pipe $node, int $precedence, int $lhsPrecedence): string {
        if ($node->right instanceof Expr\ArrowFunction) {
            // Force parentheses around arrow functions.
            $lhsPrecedence = $this->precedenceMap[Expr\ArrowFunction::class][0];
        }
        return $this->pInfixOp(BinaryOp\Pipe::class, $node->left, ' |> ', $node->right, $precedence, $lhsPrecedence);
    }

    protected function pExpr_Instanceof(Expr\Instanceof_ $node, int $precedence, int $lhsPrecedence): string {
        return $this->pPostfixOp(
            Expr\Instanceof_::class, $node->expr,
            ' instanceof ' . $this->pNewOperand($node->class),
            $precedence, $lhsPrecedence);
    }

    // Unary expressions

    protected function pExpr_BooleanNot(Expr\BooleanNot $node, int $precedence, int $lhsPrecedence): string {
        return $this->pPrefixOp(Expr\BooleanNot::class, '!', $node->expr, $precedence, $lhsPrecedence);
    }

    protected function pExpr_BitwiseNot(Expr\BitwiseNot $node, int $precedence, int $lhsPrecedence): string {
        return $this->pPrefixOp(Expr\BitwiseNot::class, '~', $node->expr, $precedence, $lhsPrecedence);
    }

    protected function pExpr_UnaryMinus(Expr\UnaryMinus $node, int $precedence, int $lhsPrecedence): string {
        return $this->pPrefixOp(Expr\UnaryMinus::class, '-', $node->expr, $precedence, $lhsPrecedence);
    }

    protected function pExpr_UnaryPlus(Expr\UnaryPlus $node, int $precedence, int $lhsPrecedence): string {
        return $this->pPrefixOp(Expr\UnaryPlus::class, '+', $node->expr, $precedence, $lhsPrecedence);
    }

    protected function pExpr_PreInc(Expr\PreInc $node): string {
        return '++' . $this->p($node->var);
    }

    protected function pExpr_PreDec(Expr\PreDec $node): string {
        return '--' . $this->p($node->var);
    }

    protected function pExpr_PostInc(Expr\PostInc $node): string {
        return $this->p($node->var) . '++';
    }

    protected function pExpr_PostDec(Expr\PostDec $node): string {
        return $this->p($node->var) . '--';
    }

    protected function pExpr_ErrorSuppress(Expr\ErrorSuppress $node, int $precedence, int $lhsPrecedence): string {
        return $this->pPrefixOp(Expr\ErrorSuppress::class, '@', $node->expr, $precedence, $lhsPrecedence);
    }

    protected function pExpr_YieldFrom(Expr\YieldFrom $node, int $precedence, int $lhsPrecedence): string {
        return $this->pPrefixOp(Expr\YieldFrom::class, 'yield from ', $node->expr, $precedence, $lhsPrecedence);
    }

    protected function pExpr_Print(Expr\Print_ $node, int $precedence, int $lhsPrecedence): string {
        return $this->pPrefixOp(Expr\Print_::class, 'print ', $node->expr, $precedence, $lhsPrecedence);
    }

    // Casts

    protected function pExpr_Cast_Int(Cast\Int_ $node, int $precedence, int $lhsPrecedence): string {
        return $this->pPrefixOp(Cast\Int_::class, '(int) ', $node->expr, $precedence, $lhsPrecedence);
    }

    protected function pExpr_Cast_Double(Cast\Double $node, int $precedence, int $lhsPrecedence): string {
        $kind = $node->getAttribute('kind', Cast\Double::KIND_DOUBLE);
        if ($kind === Cast\Double::KIND_DOUBLE) {
            $cast = '(double)';
        } elseif ($kind === Cast\Double::KIND_FLOAT) {
            $cast = '(float)';
        } else {
            assert($kind === Cast\Double::KIND_REAL);
            $cast = '(real)';
        }
        return $this->pPrefixOp(Cast\Double::class, $cast . ' ', $node->expr, $precedence, $lhsPrecedence);
    }

    protected function pExpr_Cast_String(Cast\String_ $node, int $precedence, int $lhsPrecedence): string {
        return $this->pPrefixOp(Cast\String_::class, '(string) ', $node->expr, $precedence, $lhsPrecedence);
    }

    protected function pExpr_Cast_Array(Cast\Array_ $node, int $precedence, int $lhsPrecedence): string {
        return $this->pPrefixOp(Cast\Array_::class, '(array) ', $node->expr, $precedence, $lhsPrecedence);
    }

    protected function pExpr_Cast_Object(Cast\Object_ $node, int $precedence, int $lhsPrecedence): string {
        return $this->pPrefixOp(Cast\Object_::class, '(object) ', $node->expr, $precedence, $lhsPrecedence);
    }

    protected function pExpr_Cast_Bool(Cast\Bool_ $node, int $precedence, int $lhsPrecedence): string {
        return $this->pPrefixOp(Cast\Bool_::class, '(bool) ', $node->expr, $precedence, $lhsPrecedence);
    }

    protected function pExpr_Cast_Unset(Cast\Unset_ $node, int $precedence, int $lhsPrecedence): string {
        return $this->pPrefixOp(Cast\Unset_::class, '(unset) ', $node->expr, $precedence, $lhsPrecedence);
    }

    protected function pExpr_Cast_Void(Cast\Void_ $node, int $precedence, int $lhsPrecedence): string {
        return $this->pPrefixOp(Cast\Void_::class, '(void) ', $node->expr, $precedence, $lhsPrecedence);
    }

    // Function calls and similar constructs

    protected function pExpr_FuncCall(Expr\FuncCall $node): string {
        return $this->pCallLhs($node->name)
             . '(' . $this->pMaybeMultiline($node->args) . ')';
    }

    protected function pExpr_MethodCall(Expr\MethodCall $node): string {
        return $this->pDereferenceLhs($node->var) . '->' . $this->pObjectProperty($node->name)
             . '(' . $this->pMaybeMultiline($node->args) . ')';
    }

    protected function pExpr_NullsafeMethodCall(Expr\NullsafeMethodCall $node): string {
        return $this->pDereferenceLhs($node->var) . '?->' . $this->pObjectProperty($node->name)
            . '(' . $this->pMaybeMultiline($node->args) . ')';
    }

    protected function pExpr_StaticCall(Expr\StaticCall $node): string {
        return $this->pStaticDereferenceLhs($node->class) . '::'
             . ($node->name instanceof Expr
                ? ($node->name instanceof Expr\Variable
                   ? $this->p($node->name)
                   : '{' . $this->p($node->name) . '}')
                : $node->name)
             . '(' . $this->pMaybeMultiline($node->args) . ')';
    }

    protected function pExpr_Empty(Expr\Empty_ $node): string {
        return 'empty(' . $this->p($node->expr) . ')';
    }

    protected function pExpr_Isset(Expr\Isset_ $node): string {
        return 'isset(' . $this->pCommaSeparated($node->vars) . ')';
    }

    protected function pExpr_Eval(Expr\Eval_ $node): string {
        return 'eval(' . $this->p($node->expr) . ')';
    }

    protected function pExpr_Include(Expr\Include_ $node, int $precedence, int $lhsPrecedence): string {
        static $map = [
            Expr\Include_::TYPE_INCLUDE      => 'include',
            Expr\Include_::TYPE_INCLUDE_ONCE => 'include_once',
            Expr\Include_::TYPE_REQUIRE      => 'require',
            Expr\Include_::TYPE_REQUIRE_ONCE => 'require_once',
        ];

        return $this->pPrefixOp(Expr\Include_::class, $map[$node->type] . ' ', $node->expr, $precedence, $lhsPrecedence);
    }

    protected function pExpr_List(Expr\List_ $node): string {
        $syntax = $node->getAttribute('kind',
            $this->phpVersion->supportsShortArrayDestructuring() ? Expr\List_::KIND_ARRAY : Expr\List_::KIND_LIST);
        if ($syntax === Expr\List_::KIND_ARRAY) {
            return '[' . $this->pMaybeMultiline($node->items, true) . ']';
        } else {
            return 'list(' . $this->pMaybeMultiline($node->items, true) . ')';
        }
    }

    // Other

    protected function pExpr_Error(Expr\Error $node): string {
        throw new \LogicException('Cannot pretty-print AST with Error nodes');
    }

    protected function pExpr_Variable(Expr\Variable $node): string {
        if ($node->name instanceof Expr) {
            return '${' . $this->p($node->name) . '}';
        } else {
            return '$' . $node->name;
        }
    }

    protected function pExpr_Array(Expr\Array_ $node): string {
        $syntax = $node->getAttribute('kind',
            $this->shortArraySyntax ? Expr\Array_::KIND_SHORT : Expr\Array_::KIND_LONG);
        if ($syntax === Expr\Array_::KIND_SHORT) {
            return '[' . $this->pMaybeMultiline($node->items, true) . ']';
        } else {
            return 'array(' . $this->pMaybeMultiline($node->items, true) . ')';
        }
    }

    protected function pKey(?Node $node): string {
        if ($node === null) {
            return '';
        }

        // => is not really an operator and does not typically participate in precedence resolution.
        // However, there is an exception if yield expressions with keys are involved:
        // [yield $a => $b] is interpreted as [(yield $a => $b)], so we need to ensure that
        // [(yield $a) => $b] is printed with parentheses. We approximate this by lowering the LHS
        // precedence to that of yield (which will also print unnecessary parentheses for rare low
        // precedence unary operators like include).
        $yieldPrecedence = $this->precedenceMap[Expr\Yield_::class][0];
        return $this->p($node, self::MAX_PRECEDENCE, $yieldPrecedence) . ' => ';
    }

    protected function pArrayItem(Node\ArrayItem $node): string {
        return $this->pKey($node->key)
             . ($node->byRef ? '&' : '')
             . ($node->unpack ? '...' : '')
             . $this->p($node->value);
    }

    protected function pExpr_ArrayDimFetch(Expr\ArrayDimFetch $node): string {
        return $this->pDereferenceLhs($node->var)
             . '[' . (null !== $node->dim ? $this->p($node->dim) : '') . ']';
    }

    protected function pExpr_ConstFetch(Expr\ConstFetch $node): string {
        return $this->p($node->name);
    }

    protected function pExpr_ClassConstFetch(Expr\ClassConstFetch $node): string {
        return $this->pStaticDereferenceLhs($node->class) . '::' . $this->pObjectProperty($node->name);
    }

    protected function pExpr_PropertyFetch(Expr\PropertyFetch $node): string {
        return $this->pDereferenceLhs($node->var) . '->' . $this->pObjectProperty($node->name);
    }

    protected function pExpr_NullsafePropertyFetch(Expr\NullsafePropertyFetch $node): string {
        return $this->pDereferenceLhs($node->var) . '?->' . $this->pObjectProperty($node->name);
    }

    protected function pExpr_StaticPropertyFetch(Expr\StaticPropertyFetch $node): string {
        return $this->pStaticDereferenceLhs($node->class) . '::$' . $this->pObjectProperty($node->name);
    }

    protected function pExpr_ShellExec(Expr\ShellExec $node): string {
        return '`' . $this->pEncapsList($node->parts, '`') . '`';
    }

    protected function pExpr_Closure(Expr\Closure $node): string {
        return $this->pAttrGroups($node->attrGroups, true)
             . $this->pStatic($node->static)
             . 'function ' . ($node->byRef ? '&' : '')
             . '(' . $this->pParams($node->params) . ')'
             . (!empty($node->uses) ? ' use (' . $this->pCommaSeparated($node->uses) . ')' : '')
             . (null !== $node->returnType ? ': ' . $this->p($node->returnType) : '')
             . ' {' . $this->pStmts($node->stmts) . $this->nl . '}';
    }

    protected function pExpr_Match(Expr\Match_ $node): string {
        return 'match (' . $this->p($node->cond) . ') {'
            . $this->pCommaSeparatedMultiline($node->arms, true)
            . $this->nl
            . '}';
    }

    protected function pMatchArm(Node\MatchArm $node): string {
        $result = '';
        if ($node->conds) {
            for ($i = 0, $c = \count($node->conds); $i + 1 < $c; $i++) {
                $result .= $this->p($node->conds[$i]) . ', ';
            }
            $result .= $this->pKey($node->conds[$i]);
        } else {
            $result = 'default => ';
        }
        return $result . $this->p($node->body);
    }

    protected function pExpr_ArrowFunction(Expr\ArrowFunction $node, int $precedence, int $lhsPrecedence): string {
        return $this->pPrefixOp(
            Expr\ArrowFunction::class,
            $this->pAttrGroups($node->attrGroups, true)
            . $this->pStatic($node->static)
            . 'fn' . ($node->byRef ? '&' : '')
            . '(' . $this->pParams($node->params) . ')'
            . (null !== $node->returnType ? ': ' . $this->p($node->returnType) : '')
            . ' => ',
            $node->expr, $precedence, $lhsPrecedence);
    }

    protected function pClosureUse(Node\ClosureUse $node): string {
        return ($node->byRef ? '&' : '') . $this->p($node->var);
    }

    protected function pExpr_New(Expr\New_ $node): string {
        if ($node->class instanceof Stmt\Class_) {
            $args = $node->args ? '(' . $this->pMaybeMultiline($node->args) . ')' : '';
            return 'new ' . $this->pClassCommon($node->class, $args);
        }
        return 'new ' . $this->pNewOperand($node->class)
            . '(' . $this->pMaybeMultiline($node->args) . ')';
    }

    protected function pExpr_Clone(Expr\Clone_ $node, int $precedence, int $lhsPrecedence): string {
        return $this->pPrefixOp(Expr\Clone_::class, 'clone ', $node->expr, $precedence, $lhsPrecedence);
    }

    protected function pExpr_Ternary(Expr\Ternary $node, int $precedence, int $lhsPrecedence): string {
        // a bit of cheating: we treat the ternary as a binary op where the ?...: part is the operator.
        // this is okay because the part between ? and : never needs parentheses.
        return $this->pInfixOp(Expr\Ternary::class,
            $node->cond, ' ?' . (null !== $node->if ? ' ' . $this->p($node->if) . ' ' : '') . ': ', $node->else,
            $precedence, $lhsPrecedence
        );
    }

    protected function pExpr_Exit(Expr\Exit_ $node): string {
        $kind = $node->getAttribute('kind', Expr\Exit_::KIND_DIE);
        return ($kind === Expr\Exit_::KIND_EXIT ? 'exit' : 'die')
             . (null !== $node->expr ? '(' . $this->p($node->expr) . ')' : '');
    }

    protected function pExpr_Throw(Expr\Throw_ $node, int $precedence, int $lhsPrecedence): string {
        return $this->pPrefixOp(Expr\Throw_::class, 'throw ', $node->expr, $precedence, $lhsPrecedence);
    }

    protected function pExpr_Yield(Expr\Yield_ $node, int $precedence, int $lhsPrecedence): string {
        if ($node->value === null) {
            $opPrecedence = $this->precedenceMap[Expr\Yield_::class][0];
            return $opPrecedence >= $lhsPrecedence ? '(yield)' : 'yield';
        } else {
            if (!$this->phpVersion->supportsYieldWithoutParentheses()) {
                return '(yield ' . $this->pKey($node->key) . $this->p($node->value) . ')';
            }
            return $this->pPrefixOp(
                Expr\Yield_::class, 'yield ' . $this->pKey($node->key),
                $node->value, $precedence, $lhsPrecedence);
        }
    }

    // Declarations

    protected function pStmt_Namespace(Stmt\Namespace_ $node): string {
        if ($this->canUseSemicolonNamespaces) {
            return 'namespace ' . $this->p($node->name) . ';'
                 . $this->nl . $this->pStmts($node->stmts, false);
        } else {
            return 'namespace' . (null !== $node->name ? ' ' . $this->p($node->name) : '')
                 . ' {' . $this->pStmts($node->stmts) . $this->nl . '}';
        }
    }

    protected function pStmt_Use(Stmt\Use_ $node): string {
        return 'use ' . $this->pUseType($node->type)
             . $this->pCommaSeparated($node->uses) . ';';
    }

    protected function pStmt_GroupUse(Stmt\GroupUse $node): string {
        return 'use ' . $this->pUseType($node->type) . $this->pName($node->prefix)
             . '\{' . $this->pCommaSeparated($node->uses) . '};';
    }

    protected function pUseItem(Node\UseItem $node): string {
        return $this->pUseType($node->type) . $this->p($node->name)
             . (null !== $node->alias ? ' as ' . $node->alias : '');
    }

    protected function pUseType(int $type): string {
        return $type === Stmt\Use_::TYPE_FUNCTION ? 'function '
            : ($type === Stmt\Use_::TYPE_CONSTANT ? 'const ' : '');
    }

    protected function pStmt_Interface(Stmt\Interface_ $node): string {
        return $this->pAttrGroups($node->attrGroups)
             . 'interface ' . $node->name
             . (!empty($node->extends) ? ' extends ' . $this->pCommaSeparated($node->extends) : '')
             . $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}';
    }

    protected function pStmt_Enum(Stmt\Enum_ $node): string {
        return $this->pAttrGroups($node->attrGroups)
             . 'enum ' . $node->name
             . ($node->scalarType ? ' : ' . $this->p($node->scalarType) : '')
             . (!empty($node->implements) ? ' implements ' . $this->pCommaSeparated($node->implements) : '')
             . $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}';
    }

    protected function pStmt_Class(Stmt\Class_ $node): string {
        return $this->pClassCommon($node, ' ' . $node->name);
    }

    protected function pStmt_Trait(Stmt\Trait_ $node): string {
        return $this->pAttrGroups($node->attrGroups)
             . 'trait ' . $node->name
             . $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}';
    }

    protected function pStmt_EnumCase(Stmt\EnumCase $node): string {
        return $this->pAttrGroups($node->attrGroups)
             . 'case ' . $node->name
             . ($node->expr ? ' = ' . $this->p($node->expr) : '')
             . ';';
    }

    protected function pStmt_TraitUse(Stmt\TraitUse $node): string {
        return 'use ' . $this->pCommaSeparated($node->traits)
             . (empty($node->adaptations)
                ? ';'
                : ' {' . $this->pStmts($node->adaptations) . $this->nl . '}');
    }

    protected function pStmt_TraitUseAdaptation_Precedence(Stmt\TraitUseAdaptation\Precedence $node): string {
        return $this->p($node->trait) . '::' . $node->method
             . ' insteadof ' . $this->pCommaSeparated($node->insteadof) . ';';
    }

    protected function pStmt_TraitUseAdaptation_Alias(Stmt\TraitUseAdaptation\Alias $node): string {
        return (null !== $node->trait ? $this->p($node->trait) . '::' : '')
             . $node->method . ' as'
             . (null !== $node->newModifier ? ' ' . rtrim($this->pModifiers($node->newModifier), ' ') : '')
             . (null !== $node->newName ? ' ' . $node->newName : '')
             . ';';
    }

    protected function pStmt_Property(Stmt\Property $node): string {
        return $this->pAttrGroups($node->attrGroups)
            . (0 === $node->flags ? 'var ' : $this->pModifiers($node->flags))
            . ($node->type ? $this->p($node->type) . ' ' : '')
            . $this->pCommaSeparated($node->props)
            . ($node->hooks ? ' {' . $this->pStmts($node->hooks) . $this->nl . '}' : ';');
    }

    protected function pPropertyItem(Node\PropertyItem $node): string {
        return '$' . $node->name
             . (null !== $node->default ? ' = ' . $this->p($node->default) : '');
    }

    protected function pPropertyHook(Node\PropertyHook $node): string {
        return $this->pAttrGroups($node->attrGroups)
             . $this->pModifiers($node->flags)
             . ($node->byRef ? '&' : '') . $node->name
             . ($node->params ? '(' . $this->pParams($node->params) . ')' : '')
             . (\is_array($node->body) ? ' {' . $this->pStmts($node->body) . $this->nl . '}'
                : ($node->body !== null ? ' => ' . $this->p($node->body) : '') . ';');
    }

    protected function pStmt_ClassMethod(Stmt\ClassMethod $node): string {
        return $this->pAttrGroups($node->attrGroups)
             . $this->pModifiers($node->flags)
             . 'function ' . ($node->byRef ? '&' : '') . $node->name
             . '(' . $this->pParams($node->params) . ')'
             . (null !== $node->returnType ? ': ' . $this->p($node->returnType) : '')
             . (null !== $node->stmts
                ? $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}'
                : ';');
    }

    protected function pStmt_ClassConst(Stmt\ClassConst $node): string {
        return $this->pAttrGroups($node->attrGroups)
             . $this->pModifiers($node->flags)
             . 'const '
             . (null !== $node->type ? $this->p($node->type) . ' ' : '')
             . $this->pCommaSeparated($node->consts) . ';';
    }

    protected function pStmt_Function(Stmt\Function_ $node): string {
        return $this->pAttrGroups($node->attrGroups)
             . 'function ' . ($node->byRef ? '&' : '') . $node->name
             . '(' . $this->pParams($node->params) . ')'
             . (null !== $node->returnType ? ': ' . $this->p($node->returnType) : '')
             . $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}';
    }

    protected function pStmt_Const(Stmt\Const_ $node): string {
        return $this->pAttrGroups($node->attrGroups)
            . 'const '
            . $this->pCommaSeparated($node->consts) . ';';
    }

    protected function pStmt_Declare(Stmt\Declare_ $node): string {
        return 'declare (' . $this->pCommaSeparated($node->declares) . ')'
             . (null !== $node->stmts ? ' {' . $this->pStmts($node->stmts) . $this->nl . '}' : ';');
    }

    protected function pDeclareItem(Node\DeclareItem $node): string {
        return $node->key . '=' . $this->p($node->value);
    }

    // Control flow

    protected function pStmt_If(Stmt\If_ $node): string {
        return 'if (' . $this->p($node->cond) . ') {'
             . $this->pStmts($node->stmts) . $this->nl . '}'
             . ($node->elseifs ? ' ' . $this->pImplode($node->elseifs, ' ') : '')
             . (null !== $node->else ? ' ' . $this->p($node->else) : '');
    }

    protected function pStmt_ElseIf(Stmt\ElseIf_ $node): string {
        return 'elseif (' . $this->p($node->cond) . ') {'
             . $this->pStmts($node->stmts) . $this->nl . '}';
    }

    protected function pStmt_Else(Stmt\Else_ $node): string {
        if (\count($node->stmts) === 1 && $node->stmts[0] instanceof Stmt\If_) {
            // Print as "else if" rather than "else { if }"
            return 'else ' . $this->p($node->stmts[0]);
        }
        return 'else {' . $this->pStmts($node->stmts) . $this->nl . '}';
    }

    protected function pStmt_For(Stmt\For_ $node): string {
        return 'for ('
             . $this->pCommaSeparated($node->init) . ';' . (!empty($node->cond) ? ' ' : '')
             . $this->pCommaSeparated($node->cond) . ';' . (!empty($node->loop) ? ' ' : '')
             . $this->pCommaSeparated($node->loop)
             . ') {' . $this->pStmts($node->stmts) . $this->nl . '}';
    }

    protected function pStmt_Foreach(Stmt\Foreach_ $node): string {
        return 'foreach (' . $this->p($node->expr) . ' as '
             . (null !== $node->keyVar ? $this->p($node->keyVar) . ' => ' : '')
             . ($node->byRef ? '&' : '') . $this->p($node->valueVar) . ') {'
             . $this->pStmts($node->stmts) . $this->nl . '}';
    }

    protected function pStmt_While(Stmt\While_ $node): string {
        return 'while (' . $this->p($node->cond) . ') {'
             . $this->pStmts($node->stmts) . $this->nl . '}';
    }

    protected function pStmt_Do(Stmt\Do_ $node): string {
        return 'do {' . $this->pStmts($node->stmts) . $this->nl
             . '} while (' . $this->p($node->cond) . ');';
    }

    protected function pStmt_Switch(Stmt\Switch_ $node): string {
        return 'switch (' . $this->p($node->cond) . ') {'
             . $this->pStmts($node->cases) . $this->nl . '}';
    }

    protected function pStmt_Case(Stmt\Case_ $node): string {
        return (null !== $node->cond ? 'case ' . $this->p($node->cond) : 'default') . ':'
             . $this->pStmts($node->stmts);
    }

    protected function pStmt_TryCatch(Stmt\TryCatch $node): string {
        return 'try {' . $this->pStmts($node->stmts) . $this->nl . '}'
             . ($node->catches ? ' ' . $this->pImplode($node->catches, ' ') : '')
             . ($node->finally !== null ? ' ' . $this->p($node->finally) : '');
    }

    protected function pStmt_Catch(Stmt\Catch_ $node): string {
        return 'catch (' . $this->pImplode($node->types, '|')
             . ($node->var !== null ? ' ' . $this->p($node->var) : '')
             . ') {' . $this->pStmts($node->stmts) . $this->nl . '}';
    }

    protected function pStmt_Finally(Stmt\Finally_ $node): string {
        return 'finally {' . $this->pStmts($node->stmts) . $this->nl . '}';
    }

    protected function pStmt_Break(Stmt\Break_ $node): string {
        return 'break' . ($node->num !== null ? ' ' . $this->p($node->num) : '') . ';';
    }

    protected function pStmt_Continue(Stmt\Continue_ $node): string {
        return 'continue' . ($node->num !== null ? ' ' . $this->p($node->num) : '') . ';';
    }

    protected function pStmt_Return(Stmt\Return_ $node): string {
        return 'return' . (null !== $node->expr ? ' ' . $this->p($node->expr) : '') . ';';
    }

    protected function pStmt_Label(Stmt\Label $node): string {
        return $node->name . ':';
    }

    protected function pStmt_Goto(Stmt\Goto_ $node): string {
        return 'goto ' . $node->name . ';';
    }

    // Other

    protected function pStmt_Expression(Stmt\Expression $node): string {
        return $this->p($node->expr) . ';';
    }

    protected function pStmt_Echo(Stmt\Echo_ $node): string {
        return 'echo ' . $this->pCommaSeparated($node->exprs) . ';';
    }

    protected function pStmt_Static(Stmt\Static_ $node): string {
        return 'static ' . $this->pCommaSeparated($node->vars) . ';';
    }

    protected function pStmt_Global(Stmt\Global_ $node): string {
        return 'global ' . $this->pCommaSeparated($node->vars) . ';';
    }

    protected function pStaticVar(Node\StaticVar $node): string {
        return $this->p($node->var)
             . (null !== $node->default ? ' = ' . $this->p($node->default) : '');
    }

    protected function pStmt_Unset(Stmt\Unset_ $node): string {
        return 'unset(' . $this->pCommaSeparated($node->vars) . ');';
    }

    protected function pStmt_InlineHTML(Stmt\InlineHTML $node): string {
        $newline = $node->getAttribute('hasLeadingNewline', true) ? $this->newline : '';
        return '?>' . $newline . $node->value . '<?php ';
    }

    protected function pStmt_HaltCompiler(Stmt\HaltCompiler $node): string {
        return '__halt_compiler();' . $node->remaining;
    }

    protected function pStmt_Nop(Stmt\Nop $node): string {
        return '';
    }

    protected function pStmt_Block(Stmt\Block $node): string {
        return '{' . $this->pStmts($node->stmts) . $this->nl . '}';
    }

    // Helpers

    protected function pClassCommon(Stmt\Class_ $node, string $afterClassToken): string {
        return $this->pAttrGroups($node->attrGroups, $node->name === null)
            . $this->pModifiers($node->flags)
            . 'class' . $afterClassToken
            . (null !== $node->extends ? ' extends ' . $this->p($node->extends) : '')
            . (!empty($node->implements) ? ' implements ' . $this->pCommaSeparated($node->implements) : '')
            . $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}';
    }

    protected function pObjectProperty(Node $node): string {
        if ($node instanceof Expr) {
            return '{' . $this->p($node) . '}';
        } else {
            assert($node instanceof Node\Identifier);
            return $node->name;
        }
    }

    /** @param (Expr|Node\InterpolatedStringPart)[] $encapsList */
    protected function pEncapsList(array $encapsList, ?string $quote): string {
        $return = '';
        foreach ($encapsList as $element) {
            if ($element instanceof Node\InterpolatedStringPart) {
                $return .= $this->escapeString($element->value, $quote);
            } else {
                $return .= '{' . $this->p($element) . '}';
            }
        }

        return $return;
    }

    protected function pSingleQuotedString(string $string): string {
        // It is idiomatic to only escape backslashes when necessary, i.e. when followed by ', \ or
        // the end of the string ('Foo\Bar' instead of 'Foo\\Bar'). However, we also don't want to
        // produce an odd number of backslashes, so '\\\\a' should not get rendered as '\\\a', even
        // though that would be legal.
        $regex = '/\'|\\\\(?=[\'\\\\]|$)|(?<=\\\\)\\\\/';
        return '\'' . preg_replace($regex, '\\\\$0', $string) . '\'';
    }

    protected function escapeString(string $string, ?string $quote): string {
        if (null === $quote) {
            // For doc strings, don't escape newlines
            $escaped = addcslashes($string, "\t\f\v$\\");
            // But do escape isolated \r. Combined with the terminating newline, it might get
            // interpreted as \r\n and dropped from the string contents.
            $escaped = preg_replace('/\r(?!\n)/', '\\r', $escaped);
            if ($this->phpVersion->supportsFlexibleHeredoc()) {
                $escaped = $this->indentString($escaped);
            }
        } else {
            $escaped = addcslashes($string, "\n\r\t\f\v$" . $quote . "\\");
        }

        // Escape control characters and non-UTF-8 characters.
        // Regex based on https://stackoverflow.com/a/11709412/385378.
        $regex = '/(
              [\x00-\x08\x0E-\x1F] # Control characters
            | [\xC0-\xC1] # Invalid UTF-8 Bytes
            | [\xF5-\xFF] # Invalid UTF-8 Bytes
            | \xE0(?=[\x80-\x9F]) # Overlong encoding of prior code point
            | \xF0(?=[\x80-\x8F]) # Overlong encoding of prior code point
            | [\xC2-\xDF](?![\x80-\xBF]) # Invalid UTF-8 Sequence Start
            | [\xE0-\xEF](?![\x80-\xBF]{2}) # Invalid UTF-8 Sequence Start
            | [\xF0-\xF4](?![\x80-\xBF]{3}) # Invalid UTF-8 Sequence Start
            | (?<=[\x00-\x7F\xF5-\xFF])[\x80-\xBF] # Invalid UTF-8 Sequence Middle
            | (?<![\xC2-\xDF]|[\xE0-\xEF]|[\xE0-\xEF][\x80-\xBF]|[\xF0-\xF4]|[\xF0-\xF4][\x80-\xBF]|[\xF0-\xF4][\x80-\xBF]{2})[\x80-\xBF] # Overlong Sequence
            | (?<=[\xE0-\xEF])[\x80-\xBF](?![\x80-\xBF]) # Short 3 byte sequence
            | (?<=[\xF0-\xF4])[\x80-\xBF](?![\x80-\xBF]{2}) # Short 4 byte sequence
            | (?<=[\xF0-\xF4][\x80-\xBF])[\x80-\xBF](?![\x80-\xBF]) # Short 4 byte sequence (2)
        )/x';
        return preg_replace_callback($regex, function ($matches): string {
            assert(strlen($matches[0]) === 1);
            $hex = dechex(ord($matches[0]));
            return '\\x' . str_pad($hex, 2, '0', \STR_PAD_LEFT);
        }, $escaped);
    }

    protected function containsEndLabel(string $string, string $label, bool $atStart = true): bool {
        $start = $atStart ? '(?:^|[\r\n])[ \t]*' : '[\r\n][ \t]*';
        return false !== strpos($string, $label)
            && preg_match('/' . $start . $label . '(?:$|[^_A-Za-z0-9\x80-\xff])/', $string);
    }

    /** @param (Expr|Node\InterpolatedStringPart)[] $parts */
    protected function encapsedContainsEndLabel(array $parts, string $label): bool {
        foreach ($parts as $i => $part) {
            if ($part instanceof Node\InterpolatedStringPart
                && $this->containsEndLabel($this->escapeString($part->value, null), $label, $i === 0)
            ) {
                return true;
            }
        }
        return false;
    }

    protected function pDereferenceLhs(Node $node): string {
        if (!$this->dereferenceLhsRequiresParens($node)) {
            return $this->p($node);
        } else {
            return '(' . $this->p($node) . ')';
        }
    }

    protected function pStaticDereferenceLhs(Node $node): string {
        if (!$this->staticDereferenceLhsRequiresParens($node)) {
            return $this->p($node);
        } else {
            return '(' . $this->p($node) . ')';
        }
    }

    protected function pCallLhs(Node $node): string {
        if (!$this->callLhsRequiresParens($node)) {
            return $this->p($node);
        } else {
            return '(' . $this->p($node) . ')';
        }
    }

    protected function pNewOperand(Node $node): string {
        if (!$this->newOperandRequiresParens($node)) {
            return $this->p($node);
        } else {
            return '(' . $this->p($node) . ')';
        }
    }

    /**
     * @param Node[] $nodes
     */
    protected function hasNodeWithComments(array $nodes): bool {
        foreach ($nodes as $node) {
            if ($node && $node->getComments()) {
                return true;
            }
        }
        return false;
    }

    /** @param Node[] $nodes */
    protected function pMaybeMultiline(array $nodes, bool $trailingComma = false): string {
        if (!$this->hasNodeWithComments($nodes)) {
            return $this->pCommaSeparated($nodes);
        } else {
            return $this->pCommaSeparatedMultiline($nodes, $trailingComma) . $this->nl;
        }
    }

    /** @param Node\Param[] $params
     */
    private function hasParamWithAttributes(array $params): bool {
        foreach ($params as $param) {
            if ($param->attrGroups) {
                return true;
            }
        }
        return false;
    }

    /** @param Node\Param[] $params */
    protected function pParams(array $params): string {
        if ($this->hasNodeWithComments($params) ||
            ($this->hasParamWithAttributes($params) && !$this->phpVersion->supportsAttributes())
        ) {
            return $this->pCommaSeparatedMultiline($params, $this->phpVersion->supportsTrailingCommaInParamList()) . $this->nl;
        }
        return $this->pCommaSeparated($params);
    }

    /** @param Node\AttributeGroup[] $nodes */
    protected function pAttrGroups(array $nodes, bool $inline = false): string {
        $result = '';
        $sep = $inline ? ' ' : $this->nl;
        foreach ($nodes as $node) {
            $result .= $this->p($node) . $sep;
        }

        return $result;
    }
}
<?php declare(strict_types=1);

namespace PhpParser;

use PhpParser\NodeVisitor\FindingVisitor;
use PhpParser\NodeVisitor\FirstFindingVisitor;

class NodeFinder {
    /**
     * Find all nodes satisfying a filter callback.
     *
     * @param Node|Node[] $nodes Single node or array of nodes to search in
     * @param callable $filter Filter callback: function(Node $node) : bool
     *
     * @return Node[] Found nodes satisfying the filter callback
     */
    public function find($nodes, callable $filter): array {
        if ($nodes === []) {
            return [];
        }

        if (!is_array($nodes)) {
            $nodes = [$nodes];
        }

        $visitor = new FindingVisitor($filter);

        $traverser = new NodeTraverser($visitor);
        $traverser->traverse($nodes);

        return $visitor->getFoundNodes();
    }

    /**
     * Find all nodes that are instances of a certain class.

     * @template TNode as Node
     *
     * @param Node|Node[] $nodes Single node or array of nodes to search in
     * @param class-string<TNode> $class Class name
     *
     * @return TNode[] Found nodes (all instances of $class)
     */
    public function findInstanceOf($nodes, string $class): array {
        return $this->find($nodes, function ($node) use ($class) {
            return $node instanceof $class;
        });
    }

    /**
     * Find first node satisfying a filter callback.
     *
     * @param Node|Node[] $nodes Single node or array of nodes to search in
     * @param callable $filter Filter callback: function(Node $node) : bool
     *
     * @return null|Node Found node (or null if none found)
     */
    public function findFirst($nodes, callable $filter): ?Node {
        if ($nodes === []) {
            return null;
        }

        if (!is_array($nodes)) {
            $nodes = [$nodes];
        }

        $visitor = new FirstFindingVisitor($filter);

        $traverser = new NodeTraverser($visitor);
        $traverser->traverse($nodes);

        return $visitor->getFoundNode();
    }

    /**
     * Find first node that is an instance of a certain class.
     *
     * @template TNode as Node
     *
     * @param Node|Node[] $nodes Single node or array of nodes to search in
     * @param class-string<TNode> $class Class name
     *
     * @return null|TNode Found node, which is an instance of $class (or null if none found)
     */
    public function findFirstInstanceOf($nodes, string $class): ?Node {
        return $this->findFirst($nodes, function ($node) use ($class) {
            return $node instanceof $class;
        });
    }
}
<?php declare(strict_types=1);

namespace PhpParser;

require __DIR__ . '/compatibility_tokens.php';

class Lexer {
    /**
     * Tokenize the provided source code.
     *
     * The token array is in the same format as provided by the PhpToken::tokenize() method in
     * PHP 8.0. The tokens are instances of PhpParser\Token, to abstract over a polyfill
     * implementation in earlier PHP version.
     *
     * The token array is terminated by a sentinel token with token ID 0.
     * The token array does not discard any tokens (i.e. whitespace and comments are included).
     * The token position attributes are against this token array.
     *
     * @param string $code The source code to tokenize.
     * @param ErrorHandler|null $errorHandler Error handler to use for lexing errors. Defaults to
     *                                        ErrorHandler\Throwing.
     * @return Token[] Tokens
     */
    public function tokenize(string $code, ?ErrorHandler $errorHandler = null): array {
        if (null === $errorHandler) {
            $errorHandler = new ErrorHandler\Throwing();
        }

        $scream = ini_set('xdebug.scream', '0');

        $tokens = @Token::tokenize($code);
        $this->postprocessTokens($tokens, $errorHandler);

        if (false !== $scream) {
            ini_set('xdebug.scream', $scream);
        }

        return $tokens;
    }

    private function handleInvalidCharacter(Token $token, ErrorHandler $errorHandler): void {
        $chr = $token->text;
        if ($chr === "\0") {
            // PHP cuts error message after null byte, so need special case
            $errorMsg = 'Unexpected null byte';
        } else {
            $errorMsg = sprintf(
                'Unexpected character "%s" (ASCII %d)', $chr, ord($chr)
            );
        }

        $errorHandler->handleError(new Error($errorMsg, [
            'startLine' => $token->line,
            'endLine' => $token->line,
            'startFilePos' => $token->pos,
            'endFilePos' => $token->pos,
        ]));
    }

    private function isUnterminatedComment(Token $token): bool {
        return $token->is([\T_COMMENT, \T_DOC_COMMENT])
            && substr($token->text, 0, 2) === '/*'
            && substr($token->text, -2) !== '*/';
    }

    /**
     * @param list<Token> $tokens
     */
    protected function postprocessTokens(array &$tokens, ErrorHandler $errorHandler): void {
        // This function reports errors (bad characters and unterminated comments) in the token
        // array, and performs certain canonicalizations:
        //  * Use PHP 8.1 T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG and
        //    T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG tokens used to disambiguate intersection types.
        //  * Add a sentinel token with ID 0.

        $numTokens = \count($tokens);
        if ($numTokens === 0) {
            // Empty input edge case: Just add the sentinel token.
            $tokens[] = new Token(0, "\0", 1, 0);
            return;
        }

        for ($i = 0; $i < $numTokens; $i++) {
            $token = $tokens[$i];
            if ($token->id === \T_BAD_CHARACTER) {
                $this->handleInvalidCharacter($token, $errorHandler);
            }

            if ($token->id === \ord('&')) {
                $next = $i + 1;
                while (isset($tokens[$next]) && $tokens[$next]->id === \T_WHITESPACE) {
                    $next++;
                }
                $followedByVarOrVarArg = isset($tokens[$next]) &&
                    $tokens[$next]->is([\T_VARIABLE, \T_ELLIPSIS]);
                $token->id = $followedByVarOrVarArg
                    ? \T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG
                    : \T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG;
            }
        }

        // Check for unterminated comment
        $lastToken = $tokens[$numTokens - 1];
        if ($this->isUnterminatedComment($lastToken)) {
            $errorHandler->handleError(new Error('Unterminated comment', [
                'startLine' => $lastToken->line,
                'endLine' => $lastToken->getEndLine(),
                'startFilePos' => $lastToken->pos,
                'endFilePos' => $lastToken->getEndPos(),
            ]));
        }

        // Add sentinel token.
        $tokens[] = new Token(0, "\0", $lastToken->getEndLine(), $lastToken->getEndPos());
    }
}
<?php declare(strict_types=1);

namespace PhpParser;

class Error extends \RuntimeException {
    protected string $rawMessage;
    /** @var array<string, mixed> */
    protected array $attributes;

    /**
     * Creates an Exception signifying a parse error.
     *
     * @param string $message Error message
     * @param array<string, mixed> $attributes Attributes of node/token where error occurred
     */
    public function __construct(string $message, array $attributes = []) {
        $this->rawMessage = $message;
        $this->attributes = $attributes;
        $this->updateMessage();
    }

    /**
     * Gets the error message
     *
     * @return string Error message
     */
    public function getRawMessage(): string {
        return $this->rawMessage;
    }

    /**
     * Gets the line the error starts in.
     *
     * @return int Error start line
     * @phpstan-return -1|positive-int
     */
    public function getStartLine(): int {
        return $this->attributes['startLine'] ?? -1;
    }

    /**
     * Gets the line the error ends in.
     *
     * @return int Error end line
     * @phpstan-return -1|positive-int
     */
    public function getEndLine(): int {
        return $this->attributes['endLine'] ?? -1;
    }

    /**
     * Gets the attributes of the node/token the error occurred at.
     *
     * @return array<string, mixed>
     */
    public function getAttributes(): array {
        return $this->attributes;
    }

    /**
     * Sets the attributes of the node/token the error occurred at.
     *
     * @param array<string, mixed> $attributes
     */
    public function setAttributes(array $attributes): void {
        $this->attributes = $attributes;
        $this->updateMessage();
    }

    /**
     * Sets the line of the PHP file the error occurred in.
     *
     * @param string $message Error message
     */
    public function setRawMessage(string $message): void {
        $this->rawMessage = $message;
        $this->updateMessage();
    }

    /**
     * Sets the line the error starts in.
     *
     * @param int $line Error start line
     */
    public function setStartLine(int $line): void {
        $this->attributes['startLine'] = $line;
        $this->updateMessage();
    }

    /**
     * Returns whether the error has start and end column information.
     *
     * For column information enable the startFilePos and endFilePos in the lexer options.
     */
    public function hasColumnInfo(): bool {
        return isset($this->attributes['startFilePos'], $this->attributes['endFilePos']);
    }

    /**
     * Gets the start column (1-based) into the line where the error started.
     *
     * @param string $code Source code of the file
     */
    public function getStartColumn(string $code): int {
        if (!$this->hasColumnInfo()) {
            throw new \RuntimeException('Error does not have column information');
        }

        return $this->toColumn($code, $this->attributes['startFilePos']);
    }

    /**
     * Gets the end column (1-based) into the line where the error ended.
     *
     * @param string $code Source code of the file
     */
    public function getEndColumn(string $code): int {
        if (!$this->hasColumnInfo()) {
            throw new \RuntimeException('Error does not have column information');
        }

        return $this->toColumn($code, $this->attributes['endFilePos']);
    }

    /**
     * Formats message including line and column information.
     *
     * @param string $code Source code associated with the error, for calculation of the columns
     *
     * @return string Formatted message
     */
    public function getMessageWithColumnInfo(string $code): string {
        return sprintf(
            '%s from %d:%d to %d:%d', $this->getRawMessage(),
            $this->getStartLine(), $this->getStartColumn($code),
            $this->getEndLine(), $this->getEndColumn($code)
        );
    }

    /**
     * Converts a file offset into a column.
     *
     * @param string $code Source code that $pos indexes into
     * @param int $pos 0-based position in $code
     *
     * @return int 1-based column (relative to start of line)
     */
    private function toColumn(string $code, int $pos): int {
        if ($pos > strlen($code)) {
            throw new \RuntimeException('Invalid position information');
        }

        $lineStartPos = strrpos($code, "\n", $pos - strlen($code));
        if (false === $lineStartPos) {
            $lineStartPos = -1;
        }

        return $pos - $lineStartPos;
    }

    /**
     * Updates the exception message after a change to rawMessage or rawLine.
     */
    protected function updateMessage(): void {
        $this->message = $this->rawMessage;

        if (-1 === $this->getStartLine()) {
            $this->message .= ' on unknown line';
        } else {
            $this->message .= ' on line ' . $this->getStartLine();
        }
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Parser;

use PhpParser\Error;
use PhpParser\Modifiers;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Name;
use PhpParser\Node\Scalar;
use PhpParser\Node\Stmt;

/* This is an automatically GENERATED file, which should not be manually edited.
 * Instead edit one of the following:
 *  * the grammar file grammar/php.y
 *  * the skeleton file grammar/parser.template
 *  * the preprocessing script grammar/rebuildParsers.php
 */
class Php8 extends \PhpParser\ParserAbstract
{
    public const YYERRTOK = 256;
    public const T_VOID_CAST = 257;
    public const T_THROW = 258;
    public const T_INCLUDE = 259;
    public const T_INCLUDE_ONCE = 260;
    public const T_EVAL = 261;
    public const T_REQUIRE = 262;
    public const T_REQUIRE_ONCE = 263;
    public const T_LOGICAL_OR = 264;
    public const T_LOGICAL_XOR = 265;
    public const T_LOGICAL_AND = 266;
    public const T_PRINT = 267;
    public const T_YIELD = 268;
    public const T_DOUBLE_ARROW = 269;
    public const T_YIELD_FROM = 270;
    public const T_PLUS_EQUAL = 271;
    public const T_MINUS_EQUAL = 272;
    public const T_MUL_EQUAL = 273;
    public const T_DIV_EQUAL = 274;
    public const T_CONCAT_EQUAL = 275;
    public const T_MOD_EQUAL = 276;
    public const T_AND_EQUAL = 277;
    public const T_OR_EQUAL = 278;
    public const T_XOR_EQUAL = 279;
    public const T_SL_EQUAL = 280;
    public const T_SR_EQUAL = 281;
    public const T_POW_EQUAL = 282;
    public const T_COALESCE_EQUAL = 283;
    public const T_COALESCE = 284;
    public const T_BOOLEAN_OR = 285;
    public const T_BOOLEAN_AND = 286;
    public const T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG = 287;
    public const T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG = 288;
    public const T_IS_EQUAL = 289;
    public const T_IS_NOT_EQUAL = 290;
    public const T_IS_IDENTICAL = 291;
    public const T_IS_NOT_IDENTICAL = 292;
    public const T_SPACESHIP = 293;
    public const T_IS_SMALLER_OR_EQUAL = 294;
    public const T_IS_GREATER_OR_EQUAL = 295;
    public const T_PIPE = 296;
    public const T_SL = 297;
    public const T_SR = 298;
    public const T_INSTANCEOF = 299;
    public const T_INC = 300;
    public const T_DEC = 301;
    public const T_INT_CAST = 302;
    public const T_DOUBLE_CAST = 303;
    public const T_STRING_CAST = 304;
    public const T_ARRAY_CAST = 305;
    public const T_OBJECT_CAST = 306;
    public const T_BOOL_CAST = 307;
    public const T_UNSET_CAST = 308;
    public const T_POW = 309;
    public const T_NEW = 310;
    public const T_CLONE = 311;
    public const T_EXIT = 312;
    public const T_IF = 313;
    public const T_ELSEIF = 314;
    public const T_ELSE = 315;
    public const T_ENDIF = 316;
    public const T_LNUMBER = 317;
    public const T_DNUMBER = 318;
    public const T_STRING = 319;
    public const T_STRING_VARNAME = 320;
    public const T_VARIABLE = 321;
    public const T_NUM_STRING = 322;
    public const T_INLINE_HTML = 323;
    public const T_ENCAPSED_AND_WHITESPACE = 324;
    public const T_CONSTANT_ENCAPSED_STRING = 325;
    public const T_ECHO = 326;
    public const T_DO = 327;
    public const T_WHILE = 328;
    public const T_ENDWHILE = 329;
    public const T_FOR = 330;
    public const T_ENDFOR = 331;
    public const T_FOREACH = 332;
    public const T_ENDFOREACH = 333;
    public const T_DECLARE = 334;
    public const T_ENDDECLARE = 335;
    public const T_AS = 336;
    public const T_SWITCH = 337;
    public const T_MATCH = 338;
    public const T_ENDSWITCH = 339;
    public const T_CASE = 340;
    public const T_DEFAULT = 341;
    public const T_BREAK = 342;
    public const T_CONTINUE = 343;
    public const T_GOTO = 344;
    public const T_FUNCTION = 345;
    public const T_FN = 346;
    public const T_CONST = 347;
    public const T_RETURN = 348;
    public const T_TRY = 349;
    public const T_CATCH = 350;
    public const T_FINALLY = 351;
    public const T_USE = 352;
    public const T_INSTEADOF = 353;
    public const T_GLOBAL = 354;
    public const T_STATIC = 355;
    public const T_ABSTRACT = 356;
    public const T_FINAL = 357;
    public const T_PRIVATE = 358;
    public const T_PROTECTED = 359;
    public const T_PUBLIC = 360;
    public const T_READONLY = 361;
    public const T_PUBLIC_SET = 362;
    public const T_PROTECTED_SET = 363;
    public const T_PRIVATE_SET = 364;
    public const T_VAR = 365;
    public const T_UNSET = 366;
    public const T_ISSET = 367;
    public const T_EMPTY = 368;
    public const T_HALT_COMPILER = 369;
    public const T_CLASS = 370;
    public const T_TRAIT = 371;
    public const T_INTERFACE = 372;
    public const T_ENUM = 373;
    public const T_EXTENDS = 374;
    public const T_IMPLEMENTS = 375;
    public const T_OBJECT_OPERATOR = 376;
    public const T_NULLSAFE_OBJECT_OPERATOR = 377;
    public const T_LIST = 378;
    public const T_ARRAY = 379;
    public const T_CALLABLE = 380;
    public const T_CLASS_C = 381;
    public const T_TRAIT_C = 382;
    public const T_METHOD_C = 383;
    public const T_FUNC_C = 384;
    public const T_PROPERTY_C = 385;
    public const T_LINE = 386;
    public const T_FILE = 387;
    public const T_START_HEREDOC = 388;
    public const T_END_HEREDOC = 389;
    public const T_DOLLAR_OPEN_CURLY_BRACES = 390;
    public const T_CURLY_OPEN = 391;
    public const T_PAAMAYIM_NEKUDOTAYIM = 392;
    public const T_NAMESPACE = 393;
    public const T_NS_C = 394;
    public const T_DIR = 395;
    public const T_NS_SEPARATOR = 396;
    public const T_ELLIPSIS = 397;
    public const T_NAME_FULLY_QUALIFIED = 398;
    public const T_NAME_QUALIFIED = 399;
    public const T_NAME_RELATIVE = 400;
    public const T_ATTRIBUTE = 401;

    protected int $tokenToSymbolMapSize = 402;
    protected int $actionTableSize = 1644;
    protected int $gotoTableSize = 717;

    protected int $invalidSymbol = 174;
    protected int $errorSymbol = 1;
    protected int $defaultAction = -32766;
    protected int $unexpectedTokenRule = 32767;

    protected int $YY2TBLSTATE = 451;
    protected int $numNonLeafStates = 764;

    protected array $symbolToName = array(
        "EOF",
        "error",
        "T_VOID_CAST",
        "T_THROW",
        "T_INCLUDE",
        "T_INCLUDE_ONCE",
        "T_EVAL",
        "T_REQUIRE",
        "T_REQUIRE_ONCE",
        "','",
        "T_LOGICAL_OR",
        "T_LOGICAL_XOR",
        "T_LOGICAL_AND",
        "T_PRINT",
        "T_YIELD",
        "T_DOUBLE_ARROW",
        "T_YIELD_FROM",
        "'='",
        "T_PLUS_EQUAL",
        "T_MINUS_EQUAL",
        "T_MUL_EQUAL",
        "T_DIV_EQUAL",
        "T_CONCAT_EQUAL",
        "T_MOD_EQUAL",
        "T_AND_EQUAL",
        "T_OR_EQUAL",
        "T_XOR_EQUAL",
        "T_SL_EQUAL",
        "T_SR_EQUAL",
        "T_POW_EQUAL",
        "T_COALESCE_EQUAL",
        "'?'",
        "':'",
        "T_COALESCE",
        "T_BOOLEAN_OR",
        "T_BOOLEAN_AND",
        "'|'",
        "'^'",
        "T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG",
        "T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG",
        "T_IS_EQUAL",
        "T_IS_NOT_EQUAL",
        "T_IS_IDENTICAL",
        "T_IS_NOT_IDENTICAL",
        "T_SPACESHIP",
        "'<'",
        "T_IS_SMALLER_OR_EQUAL",
        "'>'",
        "T_IS_GREATER_OR_EQUAL",
        "T_PIPE",
        "'.'",
        "T_SL",
        "T_SR",
        "'+'",
        "'-'",
        "'*'",
        "'/'",
        "'%'",
        "'!'",
        "T_INSTANCEOF",
        "'~'",
        "T_INC",
        "T_DEC",
        "T_INT_CAST",
        "T_DOUBLE_CAST",
        "T_STRING_CAST",
        "T_ARRAY_CAST",
        "T_OBJECT_CAST",
        "T_BOOL_CAST",
        "T_UNSET_CAST",
        "'@'",
        "T_POW",
        "'['",
        "T_NEW",
        "T_CLONE",
        "T_EXIT",
        "T_IF",
        "T_ELSEIF",
        "T_ELSE",
        "T_ENDIF",
        "T_LNUMBER",
        "T_DNUMBER",
        "T_STRING",
        "T_STRING_VARNAME",
        "T_VARIABLE",
        "T_NUM_STRING",
        "T_INLINE_HTML",
        "T_ENCAPSED_AND_WHITESPACE",
        "T_CONSTANT_ENCAPSED_STRING",
        "T_ECHO",
        "T_DO",
        "T_WHILE",
        "T_ENDWHILE",
        "T_FOR",
        "T_ENDFOR",
        "T_FOREACH",
        "T_ENDFOREACH",
        "T_DECLARE",
        "T_ENDDECLARE",
        "T_AS",
        "T_SWITCH",
        "T_MATCH",
        "T_ENDSWITCH",
        "T_CASE",
        "T_DEFAULT",
        "T_BREAK",
        "T_CONTINUE",
        "T_GOTO",
        "T_FUNCTION",
        "T_FN",
        "T_CONST",
        "T_RETURN",
        "T_TRY",
        "T_CATCH",
        "T_FINALLY",
        "T_USE",
        "T_INSTEADOF",
        "T_GLOBAL",
        "T_STATIC",
        "T_ABSTRACT",
        "T_FINAL",
        "T_PRIVATE",
        "T_PROTECTED",
        "T_PUBLIC",
        "T_READONLY",
        "T_PUBLIC_SET",
        "T_PROTECTED_SET",
        "T_PRIVATE_SET",
        "T_VAR",
        "T_UNSET",
        "T_ISSET",
        "T_EMPTY",
        "T_HALT_COMPILER",
        "T_CLASS",
        "T_TRAIT",
        "T_INTERFACE",
        "T_ENUM",
        "T_EXTENDS",
        "T_IMPLEMENTS",
        "T_OBJECT_OPERATOR",
        "T_NULLSAFE_OBJECT_OPERATOR",
        "T_LIST",
        "T_ARRAY",
        "T_CALLABLE",
        "T_CLASS_C",
        "T_TRAIT_C",
        "T_METHOD_C",
        "T_FUNC_C",
        "T_PROPERTY_C",
        "T_LINE",
        "T_FILE",
        "T_START_HEREDOC",
        "T_END_HEREDOC",
        "T_DOLLAR_OPEN_CURLY_BRACES",
        "T_CURLY_OPEN",
        "T_PAAMAYIM_NEKUDOTAYIM",
        "T_NAMESPACE",
        "T_NS_C",
        "T_DIR",
        "T_NS_SEPARATOR",
        "T_ELLIPSIS",
        "T_NAME_FULLY_QUALIFIED",
        "T_NAME_QUALIFIED",
        "T_NAME_RELATIVE",
        "T_ATTRIBUTE",
        "';'",
        "']'",
        "'('",
        "')'",
        "'{'",
        "'}'",
        "'`'",
        "'\"'",
        "'$'"
    );

    protected array $tokenToSymbol = array(
            0,  174,  174,  174,  174,  174,  174,  174,  174,  174,
          174,  174,  174,  174,  174,  174,  174,  174,  174,  174,
          174,  174,  174,  174,  174,  174,  174,  174,  174,  174,
          174,  174,  174,   58,  172,  174,  173,   57,  174,  174,
          167,  168,   55,   53,    9,   54,   50,   56,  174,  174,
          174,  174,  174,  174,  174,  174,  174,  174,   32,  165,
           45,   17,   47,   31,   70,  174,  174,  174,  174,  174,
          174,  174,  174,  174,  174,  174,  174,  174,  174,  174,
          174,  174,  174,  174,  174,  174,  174,  174,  174,  174,
          174,   72,  174,  166,   37,  174,  171,  174,  174,  174,
          174,  174,  174,  174,  174,  174,  174,  174,  174,  174,
          174,  174,  174,  174,  174,  174,  174,  174,  174,  174,
          174,  174,  174,  169,   36,  170,   60,  174,  174,  174,
          174,  174,  174,  174,  174,  174,  174,  174,  174,  174,
          174,  174,  174,  174,  174,  174,  174,  174,  174,  174,
          174,  174,  174,  174,  174,  174,  174,  174,  174,  174,
          174,  174,  174,  174,  174,  174,  174,  174,  174,  174,
          174,  174,  174,  174,  174,  174,  174,  174,  174,  174,
          174,  174,  174,  174,  174,  174,  174,  174,  174,  174,
          174,  174,  174,  174,  174,  174,  174,  174,  174,  174,
          174,  174,  174,  174,  174,  174,  174,  174,  174,  174,
          174,  174,  174,  174,  174,  174,  174,  174,  174,  174,
          174,  174,  174,  174,  174,  174,  174,  174,  174,  174,
          174,  174,  174,  174,  174,  174,  174,  174,  174,  174,
          174,  174,  174,  174,  174,  174,  174,  174,  174,  174,
          174,  174,  174,  174,  174,  174,    1,    2,    3,    4,
            5,    6,    7,    8,   10,   11,   12,   13,   14,   15,
           16,   18,   19,   20,   21,   22,   23,   24,   25,   26,
           27,   28,   29,   30,   33,   34,   35,   38,   39,   40,
           41,   42,   43,   44,   46,   48,   49,   51,   52,   59,
           61,   62,   63,   64,   65,   66,   67,   68,   69,   71,
           73,   74,   75,   76,   77,   78,   79,   80,   81,   82,
           83,   84,   85,   86,   87,   88,   89,   90,   91,   92,
           93,   94,   95,   96,   97,   98,   99,  100,  101,  102,
          103,  104,  105,  106,  107,  108,  109,  110,  111,  112,
          113,  114,  115,  116,  117,  118,  119,  120,  121,  122,
          123,  124,  125,  126,  127,  128,  129,  130,  131,  132,
          133,  134,  135,  136,  137,  138,  139,  140,  141,  142,
          143,  144,  145,  146,  147,  148,  149,  150,  151,  152,
          153,  154,  155,  156,  157,  158,  159,  160,  161,  162,
          163,  164
    );

    protected array $action = array(
          133,  134,  135,  581,  136,  137, 1059,  776,  777,  778,
          138,   41,  394,  395,  454,  501,-32766,-32766,-32766,  971,
          970,  851,  439,    0,  987,  988,  627,  851, 1105,  989,
        -32766,-32766,-32766,  862,-32766,  770,  769,-32766,  983,-32766,
        -32766,-32766,-32766,-32766,-32766,-32766,-32767,-32767,-32767,-32767,
        -32767,-32766,   27,-32766,-32766,-32766,  779, 1156, 1157, 1158,
         1152, 1151, 1150, 1159, 1153, 1154, 1155, -338,  304,  969,
          270,   54,  397,  783,  784,  785,  786, 1145,  -85,  440,
          860,-32766,-32766, 1401,  299, 1107,  840,  787,  788,  789,
          790,  791,  792,  793,  794,  795,  796,  816,  582,  817,
          818,  819,  820,  808,  809,  352,  353,  811,  812,  797,
          798,  799,  801,  802,  803,  367,  843,  844,  845,  846,
          847,  583, 1299,  610,  244,  804,  805,  584,  585, -195,
          828,  826,  827,  839,  823,  824, 1299,    4,  586,  587,
          822,  588,  589,  590,  591,  -85,  592,  593, 1018,   39,
          253, -194,  502,  825,  594,  595,  853,  139,  436,  133,
          134,  135,  581,  136,  137, 1094,  776,  777,  778,  138,
           41,-32766,  436, 1056, 1051, 1050, 1049, 1055, 1052, 1053,
         1054,-32766,-32766,-32766,-32766,-32766,-32766, 1105,  245,  132,
        -32766,-32766,-32766,  165,  770,  769, 1068,  861,-32766,-32766,
        -32766, 1382,-32766,-32766,-32766,-32766,-32766,-32766,-32766,-32766,
        -32766,-32766,    5,-32766,-32766,  779,-32766,-32766,-32766,-32766,
          298,-32766,-32766,-32766,-32766,-32766, -338,-32766, 1326,  270,
           54,  397,  783,  784,  785,  786,  855,-32766,  440,-32766,
        -32766,-32766,-32766,  733,  577,  840,  787,  788,  789,  790,
          791,  792,  793,  794,  795,  796,  816,  582,  817,  818,
          819,  820,  808,  809,  352,  353,  811,  812,  797,  798,
          799,  801,  802,  803,  367,  843,  844,  845,  846,  847,
          583, 1019,  851, -584,  804,  805,  584,  585, -195,  828,
          826,  827,  839,  823,  824,  -50,  440,  586,  587,  822,
          588,  589,  590,  591, -582,  592,  593,  394,  395, 1338,
         -194,  860,  825,  594,  595,   29,  139,  439,  133,  134,
          135,  581,  136,  137, 1092,  776,  777,  778,  138,   41,
         1156, 1157, 1158, 1152, 1151, 1150, 1159, 1153, 1154, 1155,
            3, -275, 1367, 1172, 1386, 1299, 1105,  751,   38, 1091,
         -584, -584, 1385,  770,  769,-32767,-32767,-32767,-32767,  107,
          108,  109,  110,  304,  111,  112,  113,  114,  115,  145,
          282, -582, -582,  331,  779, 1301, 1300, 1302,  152,  293,
          299,  436,  116,  161, 1131,  396,  395, -582,  270,   54,
          397,  783,  784,  785,  786,  439,  144,  440,  140, -588,
          331,  108,  109,  110,  840,  787,  788,  789,  790,  791,
          792,  793,  794,  795,  796,  816,  582,  817,  818,  819,
          820,  808,  809,  352,  353,  811,  812,  797,  798,  799,
          801,  802,  803,  367,  843,  844,  845,  846,  847,  583,
         1299, -580,  148,  804,  805,  584,  585,  856,  828,  826,
          827,  839,  823,  824,  143, -581,  586,  587,  822,  588,
          589,  590,  591, -579,  592,  593, 1065,-32766,-32766,-32766,
        -32766,  825,  594,  595, -578,  139, 1297,  133,  134,  135,
          581,  136,  137, 1096,  776,  777,  778,  138,   41,-32766,
         1299, 1068, 1068,   52,-32766,-32766,-32766,  633, 1130,  322,
         1067,  888,  759,  889, 1299, 1105,  256, 1299, -580, -580,
           87,  315,  770,  769,  331,  289,  298,  967,  319, 1301,
         1300, 1302, -581, -581, -580, 1411,  436,  857, 1412,  507,
         -579, -579,  332,  779,-32766,  761, -587, 1065, -581,  328,
          436, -578, -578,  436,  770,  769, -579,  270,   54,  397,
          783,  784,  785,  786,  859,  348,  440, -578, -586, 1068,
          305,  306, 1068,  840,  787,  788,  789,  790,  791,  792,
          793,  794,  795,  796,  816,  582,  817,  818,  819,  820,
          808,  809,  352,  353,  811,  812,  797,  798,  799,  801,
          802,  803,  367,  843,  844,  845,  846,  847,  583,  487,
          488,  349,  804,  805,  584,  585,  153,  828,  826,  827,
          839,  823,  824,   53,  382,  586,  587,  822,  588,  589,
          590,  591,  155,  592,  593,  -78,   88,   89,   90,  388,
          825,  594,  595,  404,  139,  800,  771,  772,  773,  774,
          775,  156,  776,  777,  778,  813,  814,   40,  458,   91,
           92,   93,   94,   95,   96,   97,   98,   99,  100,  101,
          102,  103,  104,  105,  106,  107,  108,  109,  110,  111,
          112,  113,  114,  115, 1306,  282,  476,  477,  478, 1299,
          307,  308, -383,  459, -383,  746,  406,  116,   10,  460,
          888,  779,  889, -620,  461, -620,  387,  109,  110,  866,
        -32766,-32766,  676,   23,  157,  780,  781,  782,  783,  784,
          785,  786,  695,  696,  849,  436,  151,  422,  392,  393,
          159,  840,  787,  788,  789,  790,  791,  792,  793,  794,
          795,  796,  816,  838,  817,  818,  819,  820,  808,  809,
          810,  837,  811,  812,  797,  798,  799,  801,  802,  803,
          842,  843,  844,  845,  846,  847,  848,  860,-32766, 1065,
          804,  805,  806,  807,    2,  828,  826,  827,  839,  823,
          824,  398,  399,  815,  821,  822,  829,  830,  832,  831,
           36,  833,  834, 1068, 1068,  667,  668,  -58,  825,  836,
          835,   55,   56,   57,   58,  533,   59,   60,  -57,  130,
          770,  769,   61,   62,  131,   63,  -87,  298,  289,  141,
          142,  147,  160,  162,  163,-32766,  770,  769,  164, -309,
          -84,   35,  117,  118,  119,  120,  121,  122,  123,  124,
          125,  126,  127,  128,  129,  -78, -305,  -73,  -72,  -71,
           80,  -70,   64,   65, 1299,  -69,  331,   66,  -68,   67,
          250,  251,   68,   69,   70,   71,   72,   73,   74,   75,
         1299,   31,  275,   48,  456, 1206, 1208,  -67,  -66, 1332,
         1333,  535,  -65,  860,  -46,  -18, -578, 1330,   42,  280,
          436,  290,  747,  750,  945,  150,  294,  282,  946,  116,
          541,  287,  288,  291,  292,  337,  436,  391,  390,  295,
          300,  301,  149,  963,  705,  851,  720,  347,-32766,  677,
          860, 1163,  682, 1292, 1413,  698,  722,  683,  312,  545,
          546,  309,   79,  597,  984,  665, -544,  318,-32766, 1337,
          548,  549,   13, 1318, 1319, 1320, 1321, 1323, 1315, 1316,
          303,  699, 1327, -578, -578,  965, 1322, 1317, -278,  310,
         1301, 1300, 1302,  304,   -4,  946,   76,  603,  631, -578,
          326,  327,  331,  534, -154, -154, -154,  484,  512,  859,
            0,    0, 1339,  936,    0,    0,   46,   22,  536, -154,
          537, -154,  538, -154,  539, -154, -534,  540,    0,   11,
            0,    0,   49,   50,  462,  389,   30,    0,   51,  542,
          386,  304,    0,   44,  380,    0,  987,  988,    0,   45,
          946,  543,    0,    0,    0,    0,  544,    0,    0,  922,
          983, -110, -110, -110,  421,  756,    0,    0,    0,    0,
          534,  757,  879, -276,    0,  927, 1028, 1005, 1012, 1002,
          936, 1013,  925,   46,   22,  536, 1000,  537, 1134,  538,
         1137,  539,  948, 1138,  540, 1135,  731, -154, 1174,   49,
           50,  462,  389, 1136,  452,   51,  542, 1142, -614,  302,
          871,  380, 1354,  987,  988, 1371, 1404,  670,  543, -613,
         -612,  946, -588,  544, -587,  534,  547,  983, -110, -110,
         -110, -586, -585, -528,    1,  936,   32,   33,   46,   22,
          536,   43,  537,   47,  538,   77,  539,   81,   82,  540,
           21, -253, -253, -253,   49,   50,  462,  389,   83,  948,
           51,  542,   84,  731,   -4,   85,  380,   86,  987,  988,
          146,  154,  946,  543,  158,  248,  333,  368,  544,  369,
          370,  922,  983, -110, -110, -110,  371,  372,  373,  374,
          375,  376,  377,  378,  381,  453,  534,  576, 1306,  379,
          770,  769, -275,  736,   15,   16,  936,   17,   18,   46,
           22,  536,   20,  537,  948,  538,  362,  539,  731, -253,
          540,  420, -252, -252, -252,   49,   50,  462,  389,  -16,
          503,   51,  542,  504,   31,  276,  511,  380,  514,  987,
          988,  515,  516,  517,  543,  521,  860,  534,  522,  544,
         1330,  523,  922,  983, -110, -110, -110,  936,  530,  608,
           46,   22,  536,  946,  537,  715,  538, 1247,  539, 1328,
         1095,  540, 1093, 1074, 1287, 1070,   49,   50,  462, -280,
         -102,  860,   51,  542,   14,  948, 1292,   19,  380,  731,
         -252,   24,  322,  419,  737,  622,  946,  628,  656,  721,
          544, 1251, 1305,  548,  549, 1248, 1318, 1319, 1320, 1321,
         1323, 1315, 1316, 1383,  425,   37, -110, -110,  325, 1322,
         1317, -110,  383,  732,  735,  739,  740,  738,  742,   78,
         -110,  753,  743,  744,  327,  331,  948,  745,  534,-32766,
          731,  749,  761,  734,  762,  946,  923, 1408,  936, 1410,
          882,   46,   22,  536,  881,  537,  977,  538, 1020,  539,
         1409,  304,  540,  976,   80,  974,  975,   49,   50,  462,
          331,  534,  978,   51,  542, 1280,  741,  956,  946,  380,
          966,  936,  954, 1173,   46,   22,  536, 1169,  537, 1123,
          538,  544,  539, 1010, 1011,  540,  654, 1407, 1365, 1380,
           49,   50,  462, 1265,  331, 1331,   51,  542,    0,  748,
            0,  946,  380,    0,    0,    0,    0,    0,    0,    0,
          534,    0,    0,    0,  544,    0,    0,  948,    0,    0,
          936,  731,    0,   46,   22,  536,    0,  537,    0,  538,
         1306,  539,    0,    0,  540,    0,    0,    0,    0,   49,
           50,  462,    0,  534,    0,   51,  542,    0,    0,    0,
         1004,  380,    0,  936,  731,    0,   46,   22,  536,    0,
          537,    0,  538,  544,  539,    0,    0,  540,    0,    0,
            0,    0,   49,   50,  462,    0,  534,    0,   51,  542,
            0,    0,   31,  275,  380,    0,  936,    0,    0,   46,
           22,  536,    0,  537,  860,  538,  544,  539, 1330,  948,
          540,    0,    0,  731, 1129,   49,   50,  462,    0,    0,
            0,   51,  542,  860,   31,  276,    0,  380,    0,    0,
            0,  293,    0,    0,    0,    0,  860,    0,    0,  544,
         1330,    0,  948,    0, 1292,    0,  731,    0,    0,    0,
            0,    0,    0,    0,    0,    0,    0,    0, -110, -110,
            0,    0,  549, -110, 1318, 1319, 1320, 1321, 1323, 1315,
         1316,    0, -110,    0,    0,  948, 1292, 1322, 1317,  731,
            0,-32766,  709,    0,    0,    0,    0,   78,    0,    0,
            0,    0,  327,  331,  549,    0, 1318, 1319, 1320, 1321,
         1323, 1315, 1316,  304,    0,    0,   80,    0,    0, 1322,
         1317,    0,  331,    0,    0,    0, -110,    0,  710,   78,
            0, -110,    0, -110,  327,  331,    0,    0,  311,    0,
            0, -110, -110, -110, -110, -110, -110, -110, -110, -110,
         -110, -110,  711,  712,    0,    0,    0,    0,    0,    0,
            0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
            0, 1301, 1300, 1302,    0,    0,    0,  289,    0,    0,
            0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
            0,    0,    0, -357
    );

    protected array $actionCheck = array(
            3,    4,    5,    6,    7,    8,    1,   10,   11,   12,
           13,   14,  108,  109,  110,   32,   10,   11,   12,   74,
           75,   82,  118,    0,  119,  120,    1,   82,   31,  124,
           10,   11,   12,    1,   10,   38,   39,   31,  133,   33,
           34,   35,   36,   37,   38,   39,   40,   41,   42,   43,
           44,   31,  103,   33,   34,   35,   59,  118,  119,  120,
          121,  122,  123,  124,  125,  126,  127,    9,  164,  124,
           73,   74,   75,   76,   77,   78,   79,  128,   32,   82,
           84,   10,   11,   87,   31,   31,   89,   90,   91,   92,
           93,   94,   95,   96,   97,   98,   99,  100,  101,  102,
          103,  104,  105,  106,  107,  108,  109,  110,  111,  112,
          113,  114,  115,  116,  117,  118,  119,  120,  121,  122,
          123,  124,   82,   83,   99,  128,  129,  130,  131,    9,
          133,  134,  135,  136,  137,  138,   82,    9,  141,  142,
          143,  144,  145,  146,  147,   99,  149,  150,   32,  153,
          154,    9,  169,  156,  157,  158,   82,  160,  118,    3,
            4,    5,    6,    7,    8,  168,   10,   11,   12,   13,
           14,  118,  118,  120,  121,  122,  123,  124,  125,  126,
          127,   10,   11,   12,   10,   11,   12,   31,   15,   15,
           10,   11,   12,   15,   38,   39,  143,  165,   10,   11,
           12,    1,   31,  118,   33,   34,   35,   36,   37,   38,
           39,   31,    9,   33,   34,   59,   10,   11,   12,   31,
          167,   33,   34,   35,   36,   37,  168,  142,    1,   73,
           74,   75,   76,   77,   78,   79,  162,   31,   82,   33,
           34,   35,   36,  169,   87,   89,   90,   91,   92,   93,
           94,   95,   96,   97,   98,   99,  100,  101,  102,  103,
          104,  105,  106,  107,  108,  109,  110,  111,  112,  113,
          114,  115,  116,  117,  118,  119,  120,  121,  122,  123,
          124,  165,   82,   72,  128,  129,  130,  131,  168,  133,
          134,  135,  136,  137,  138,   32,   82,  141,  142,  143,
          144,  145,  146,  147,   72,  149,  150,  108,  109,  152,
          168,   84,  156,  157,  158,    9,  160,  118,    3,    4,
            5,    6,    7,    8,  168,   10,   11,   12,   13,   14,
          118,  119,  120,  121,  122,  123,  124,  125,  126,  127,
            9,  168,    1,  165,    1,   82,   31,  169,    9,    1,
          139,  140,    9,   38,   39,   45,   46,   47,   48,   49,
           50,   51,   52,  164,   53,   54,   55,   56,   57,  169,
           59,  139,  140,  173,   59,  161,  162,  163,   15,   31,
           31,  118,   71,   17,  170,  108,  109,  155,   73,   74,
           75,   76,   77,   78,   79,  118,  169,   82,    9,  167,
          173,   50,   51,   52,   89,   90,   91,   92,   93,   94,
           95,   96,   97,   98,   99,  100,  101,  102,  103,  104,
          105,  106,  107,  108,  109,  110,  111,  112,  113,  114,
          115,  116,  117,  118,  119,  120,  121,  122,  123,  124,
           82,   72,    9,  128,  129,  130,  131,   82,  133,  134,
          135,  136,  137,  138,   32,   72,  141,  142,  143,  144,
          145,  146,  147,   72,  149,  150,  118,  118,   10,   11,
           12,  156,  157,  158,   72,  160,  118,    3,    4,    5,
            6,    7,    8,  168,   10,   11,   12,   13,   14,   31,
           82,  143,  143,   72,   10,   11,   12,   54,  165,  168,
          142,  108,  169,  110,   82,   31,    9,   82,  139,  140,
          169,    9,   38,   39,  173,  167,  167,    1,    9,  161,
          162,  163,  139,  140,  155,   82,  118,  162,   85,  104,
          139,  140,   72,   59,  118,  169,  167,  118,  155,    9,
          118,  139,  140,  118,   38,   39,  155,   73,   74,   75,
           76,   77,   78,   79,  161,    9,   82,  155,  167,  143,
          139,  140,  143,   89,   90,   91,   92,   93,   94,   95,
           96,   97,   98,   99,  100,  101,  102,  103,  104,  105,
          106,  107,  108,  109,  110,  111,  112,  113,  114,  115,
          116,  117,  118,  119,  120,  121,  122,  123,  124,  139,
          140,    9,  128,  129,  130,  131,   15,  133,  134,  135,
          136,  137,  138,   72,    9,  141,  142,  143,  144,  145,
          146,  147,   15,  149,  150,   17,   10,   11,   12,    9,
          156,  157,  158,    9,  160,    3,    4,    5,    6,    7,
            8,   15,   10,   11,   12,   13,   14,   31,    9,   33,
           34,   35,   36,   37,   38,   39,   40,   41,   42,   43,
           44,   45,   46,   47,   48,   49,   50,   51,   52,   53,
           54,   55,   56,   57,    1,   59,  134,  135,  136,   82,
          139,  140,  108,    9,  110,  169,  108,   71,  110,    9,
          108,   59,  110,  166,    9,  168,  155,   51,   52,    9,
           53,   54,   77,   78,   15,   73,   74,   75,   76,   77,
           78,   79,   77,   78,   82,  118,  103,  104,  108,  109,
           15,   89,   90,   91,   92,   93,   94,   95,   96,   97,
           98,   99,  100,  101,  102,  103,  104,  105,  106,  107,
          108,  109,  110,  111,  112,  113,  114,  115,  116,  117,
          118,  119,  120,  121,  122,  123,  124,   84,  118,  118,
          128,  129,  130,  131,  167,  133,  134,  135,  136,  137,
          138,  108,  109,  141,  142,  143,  144,  145,  146,  147,
           15,  149,  150,  143,  143,  113,  114,   17,  156,  157,
          158,    2,    3,    4,    5,    6,    7,    8,   17,   17,
           38,   39,   13,   14,   17,   16,   32,  167,  167,   17,
           17,   17,   17,   17,   17,  142,   38,   39,   17,   36,
           32,   17,   18,   19,   20,   21,   22,   23,   24,   25,
           26,   27,   28,   29,   30,   32,   36,   32,   32,   32,
          167,   32,   53,   54,   82,   32,  173,   58,   32,   60,
           61,   62,   63,   64,   65,   66,   67,   68,   69,   70,
           82,   72,   73,   74,   75,   61,   62,   32,   32,   80,
           81,   82,   32,   84,   32,   32,   72,   88,   32,   32,
          118,   32,   32,   32,   32,   32,   38,   59,    1,   71,
          101,   36,   36,   36,   36,   36,  118,  108,  109,   38,
           38,   38,   72,   39,   79,   82,   82,  118,   87,   92,
           84,   84,   98,  124,   85,   96,   94,  102,  116,  130,
          131,  137,  160,   91,  133,  115,  155,  137,  142,  152,
          141,  142,   99,  144,  145,  146,  147,  148,  149,  150,
          151,  102,  166,  139,  140,  160,  157,  158,  168,  138,
          161,  162,  163,  164,    0,    1,  167,  159,  159,  155,
          171,  172,  173,   76,   77,   78,   79,   99,   99,  161,
           -1,   -1,  152,   86,   -1,   -1,   89,   90,   91,   92,
           93,   94,   95,   96,   97,   98,  155,  100,   -1,  155,
           -1,   -1,  105,  106,  107,  108,  155,   -1,  111,  112,
          155,  164,   -1,  165,  117,   -1,  119,  120,   -1,  165,
            1,  124,   -1,   -1,   -1,   -1,  129,   -1,   -1,  132,
          133,  134,  135,  136,  170,  165,   -1,   -1,   -1,   -1,
           76,  165,  165,  168,   -1,  165,  165,  165,  165,  165,
           86,  165,  165,   89,   90,   91,  165,   93,  165,   95,
          165,   97,  165,  165,  100,  165,  169,  170,  165,  105,
          106,  107,  108,  165,  110,  111,  112,  165,  167,  115,
          166,  117,  166,  119,  120,  166,  166,  166,  124,  167,
          167,    1,  167,  129,  167,   76,  132,  133,  134,  135,
          136,  167,  167,  167,  167,   86,  167,  167,   89,   90,
           91,  167,   93,  167,   95,  167,   97,  167,  167,  100,
          156,  102,  103,  104,  105,  106,  107,  108,  167,  165,
          111,  112,  167,  169,  170,  167,  117,  167,  119,  120,
          167,  167,    1,  124,  167,  167,  167,  167,  129,  167,
          167,  132,  133,  134,  135,  136,  167,  167,  167,  167,
          167,  167,  167,  167,  167,  167,   76,  167,    1,  167,
           38,   39,  168,   32,  168,  168,   86,  168,  168,   89,
           90,   91,  168,   93,  165,   95,  168,   97,  169,  170,
          100,  168,  102,  103,  104,  105,  106,  107,  108,   32,
          168,  111,  112,  168,   72,   73,  168,  117,  168,  119,
          120,  168,  168,  168,  124,  168,   84,   76,  168,  129,
           88,  168,  132,  133,  134,  135,  136,   86,  168,  168,
           89,   90,   91,    1,   93,  168,   95,  168,   97,  168,
          168,  100,  168,  168,  168,  168,  105,  106,  107,  168,
          168,   84,  111,  112,  168,  165,  124,  168,  117,  169,
          170,  168,  168,  168,   32,  168,    1,  168,  168,  168,
          129,  168,  168,  141,  142,  168,  144,  145,  146,  147,
          148,  149,  150,  168,  170,  169,  119,  120,  169,  157,
          158,  124,  169,  169,  169,  169,  169,   32,  169,  167,
          133,  170,  169,  169,  172,  173,  165,  169,   76,  142,
          169,  169,  169,  169,  169,    1,  170,  170,   86,  170,
          170,   89,   90,   91,  170,   93,  170,   95,  170,   97,
          170,  164,  100,  170,  167,  170,  170,  105,  106,  107,
          173,   76,  170,  111,  112,  170,   32,  170,    1,  117,
          170,   86,  170,  170,   89,   90,   91,  170,   93,  170,
           95,  129,   97,  170,  170,  100,  170,  170,  170,  170,
          105,  106,  107,  171,  173,  172,  111,  112,   -1,   32,
           -1,    1,  117,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
           76,   -1,   -1,   -1,  129,   -1,   -1,  165,   -1,   -1,
           86,  169,   -1,   89,   90,   91,   -1,   93,   -1,   95,
            1,   97,   -1,   -1,  100,   -1,   -1,   -1,   -1,  105,
          106,  107,   -1,   76,   -1,  111,  112,   -1,   -1,   -1,
          165,  117,   -1,   86,  169,   -1,   89,   90,   91,   -1,
           93,   -1,   95,  129,   97,   -1,   -1,  100,   -1,   -1,
           -1,   -1,  105,  106,  107,   -1,   76,   -1,  111,  112,
           -1,   -1,   72,   73,  117,   -1,   86,   -1,   -1,   89,
           90,   91,   -1,   93,   84,   95,  129,   97,   88,  165,
          100,   -1,   -1,  169,    1,  105,  106,  107,   -1,   -1,
           -1,  111,  112,   84,   72,   73,   -1,  117,   -1,   -1,
           -1,   31,   -1,   -1,   -1,   -1,   84,   -1,   -1,  129,
           88,   -1,  165,   -1,  124,   -1,  169,   -1,   -1,   -1,
           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  119,  120,
           -1,   -1,  142,  124,  144,  145,  146,  147,  148,  149,
          150,   -1,  133,   -1,   -1,  165,  124,  157,  158,  169,
           -1,  142,   82,   -1,   -1,   -1,   -1,  167,   -1,   -1,
           -1,   -1,  172,  173,  142,   -1,  144,  145,  146,  147,
          148,  149,  150,  164,   -1,   -1,  167,   -1,   -1,  157,
          158,   -1,  173,   -1,   -1,   -1,  103,   -1,  118,  167,
           -1,  108,   -1,  110,  172,  173,   -1,   -1,  115,   -1,
           -1,  118,  119,  120,  121,  122,  123,  124,  125,  126,
          127,  128,  142,  143,   -1,   -1,   -1,   -1,   -1,   -1,
           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
           -1,  161,  162,  163,   -1,   -1,   -1,  167,   -1,   -1,
           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
           -1,   -1,   -1,  170
    );

    protected array $actionBase = array(
            0,  156,   -3,  315,  474,  474,  887,  954, 1009, 1080,
         1460,  227,  214,  -61, 1222, 1304, 1304, 1337, 1304, 1131,
         1255,   74, 1370, 1370, 1370,  898,  824,  632,  632,  824,
          632,  762,  762,  762,  762,  778,  778,  425,  425,   40,
          422,  263,   54,  408,  408,  408,  408,  408,  597,  408,
          408,  408,  408,  408,  597,  408,  408,  408,  408,  408,
          408,  408,  408,  408,  408,  408,  408,  408,  408,  408,
          408,  408,  408,  408,  408,  408,  408,  408,  408,  408,
          408,  408,  408,  408,  408,  408,  408,  408,  408,  408,
          408,  408,  408,  408,  408,  408,  408,  408,  408,  408,
          408,  408,  408,  408,  408,  408,  408,  408,  408,  408,
          408,  408,  408,  408,  408,  408,  408,  408,  408,  408,
          408,  408,  408,  408,  408,  408,  408,  408,  408,  408,
          408,  408,  408,  408,  408,  408,  408,  408,  408,  408,
          408,  408,  408,  408,  408,  408,  408,  408,  408,  408,
          408,  408,  408,  408,  408,  408,  408,  408,  408,  408,
          408,  408,  408,  408,  408,  408,   32,  116,  331,   25,
         1133,  765, 1084, 1093, 1087, 1094, 1079, 1076, 1085, 1089,
         1097, 1187, 1188,  853, 1186, 1189, 1090,  911, 1083, 1091,
          893,  616,  616,  616,  616,  616,  616,  616,  616,  616,
          616,  616,  616,  616,  616,  616,  616,  616,  616,  616,
          616,  616,  616,  616,  616,  616,  616,  616,   24,  174,
           71,  484,  484,  484,  484,  484,  484,  484,  484,  484,
          484,  484,  484,  484,  484,  484,  484,  484,  484,  484,
          484,  458,  458,  458, 1122, 1122,  180,   20, 1412, 1380,
         1412, 1412, 1412, 1412, 1412, 1412, 1412, 1412,  206,  188,
          804,  171,  171,    6,    6,    6,    6,    6, 1473,   53,
         1157,  310,  310,  310,  310, 1399, 1399,  -51,  506,  574,
          348,  351,  673,  -96,  646,  349,  199,  641,  641,  419,
          419,  640,  640,  419,  419,  419,  311,  311,  416,  416,
          416,  416,  393,  157,  786,  200,  200,  200,  200,  786,
          786,  786,  786,  820, 1193,  786,  970,  981,  786,  786,
          232,  369,  391,  647,  647,  582,   -4,   -4,  582,  -95,
           -4,  341,  443,   85,  791,  277,  527,   85,  992,  383,
          402,  402,  541,  402,  402,  402,  808,  610,  808, 1072,
          821,  821,  842,  805,  902, 1134, 1105,  815, 1183,  839,
         1184, 1135,  178,  542,  211,  421,  460,  803, 1071, 1071,
         1071, 1071, 1071, 1071, 1071, 1071, 1071, 1071, 1071, 1071,
         1191,  506, 1072,  365, 1177, 1179, 1191, 1191, 1191,  506,
          506,  506,  506,  506,  506,  506,  506,  843,  506,  506,
          672,  365,  625,  635,  365,  859,  506,  831,   32,   32,
           32,   32,   32,   32,   32,   32,   32,   32,   32,  -17,
           32,   32,  116,  516,  516,   32,  333,    5,  516,  516,
          516,  366,  516,   32,   32,   32,  610,  828,  798,  663,
           46,  799,  142,  828,  828,  828,   58,  173,  120,  783,
          800,  578,  823,  823,  823,  827,  927,  927,  823,  826,
          823,  827,  823,  823,  927,  927,  837,  927,  389,  639,
          546,  620,  674,  927,  502,  823,  823,  823,  823,  790,
          927,  128,  203,  680,  823,  497,  433,  823,  823,  790,
          784,  849,  811,  927,  927,  927,  790,  605,  811,  811,
          811,  860,  866,  810,  847,  530,  509,  690,  339,  864,
          847,  847,  823,  624,  810,  847,  810,  847,  785,  847,
          847,  847,  810,  847,  826,  592,  847,  795,  685,  306,
          847,  823,   23,  938,  940,  774,  941,  934,  951,  988,
          955,  958, 1114,  926,  968,  936,  960,  990,  930,  929,
          852,  782,  787,  834,  771,  925,  818,  818,  818,  915,
          917,  818,  818,  818,  818,  818,  818,  818,  818,  782,
          776,  845,  832,  972,  792,  793, 1051,  777, 1136, 1192,
          970,  938,  958,  788,  936,  960,  930,  929,  840,  836,
          816,  835,  813,  809,  806,  807,  846,  963,  838,  794,
         1004,  973, 1121, 1109,  979,  980, 1013, 1059,  867, 1061,
         1137,  830, 1139, 1140,  904,  982, 1115,  818,  913,  901,
          906,  981,  924,  782,  909, 1062, 1064,  996, 1106, 1022,
         1025, 1113,  868,  829,  910, 1144,  983,  984,  985, 1116,
         1117,  844,  997,  780, 1028,  869,  854, 1030, 1033, 1034,
         1035, 1119, 1146, 1123,  912, 1124,  870,  817,  865,  814,
         1148,  363,  857,  858,  863,  987,  591,  969, 1128, 1104,
         1150, 1037, 1040, 1043, 1153, 1155,  964,  871,  999,  819,
         1000,  994,  872,  873,  607,  862, 1065,  855,  856,  861,
          626,  689, 1156, 1162, 1165,  967,  850,  825,  874,  876,
         1066,  848, 1067, 1167,  705,  877, 1170, 1057,  796,  797,
          608,  781,  770,  801,  822, 1132,  841,  802,  833,  986,
          797,  851,  881, 1172,  883,  885,  888, 1050,  890, 1008,
         1173,    0,    0,    0,    0,    0,    0,    0,    0,    0,
            0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
            0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
            0,    0,    0,    0,    0,  789,  789,  789,  789,  789,
          789,  789,  789,  789,  632,  632,  632,  632,  789,  789,
          789,  789,  789,  789,  789,  632,  789,  789,  789,  632,
          632,    0,    0,  632,    0,  789,  789,  789,  789,  789,
          789,  789,  789,  789,  789,  789,  789,  789,  789,  789,
          789,  789,  789,  789,  789,  789,  789,  789,  789,  789,
          789,  789,  789,  789,  789,  789,  789,  789,  789,  789,
          789,  789,  789,  789,  789,  789,  789,  789,  789,  789,
          789,  789,  789,  789,  789,  789,  789,  789,  789,  789,
          789,  789,  789,  789,  789,  789,  789,  789,  789,  789,
          789,  789,  789,  789,  789,  789,  789,  789,  789,  789,
          789,  789,  789,  789,  789,  789,  789,  789,  789,  789,
          789,  789,  789,  789,  789,  789,  789,  789,  789,  789,
          789,  789,  789,  789,  789,  789,  789,  789,  789,  789,
          789,  789,  789,  789,  789,  789,  789,  789,  789,  789,
          789,  789,  789,  789,  789,  789,  789,  789,  789,  789,
          789,  789,  789,  789,  789,  789,  789,  789,  789,  789,
          616,  616,  616,  616,  616,  616,  616,  616,  616,  616,
          616,  616,  616,  616,  616,  616,  616,  616,  616,  616,
          616,  616,  616,  616,  616,    0,    0,    0,    0,    0,
            0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
            0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
            0,    0,  616,  616,  616,  616,  616,  616,  616,  616,
          616,  616,  616,  616,  616,  616,  616,  616,  616,  616,
          616,  616,  616,  616,  616,  616,  616,  616,  358,  358,
          616,  616,  358,  358,  358,  358,  358,  358,  358,  358,
          358,  358,  616,  616,    0,  616,  616,  616,  616,  616,
          616,  616,  837,  358,  358,  311,  311,  311,  311,  358,
          358,  212,  212,  212,  358,  311,  358,  -95,  311,  358,
          -95,  358,  358,  358,  358,  358,  358,  358,  358,  358,
            0,    0,  358,  358,  358,  358,  365,   -4,  358,  826,
          826,  826,  826,  358,  358,  358,  358,   -4,   -4,  358,
          -55,  -55,  358,  358,    0,    0,    0,  311,  311,  365,
            0,    0,  365,    0,    0,  826,  826,  358,  -95,  837,
          343,  358,  178,    0,    0,    0,    0,    0,    0,    0,
          365,  826,  365,  506,   -4,   -4,  506,  506,  516,   32,
          343,  613,  613,  613,  613,   32,    0,    0,    0,    0,
            0,  610,  837,  837,  837,  837,  837,  837,  837,  837,
          837,  837,  837,  837,  826,    0,  837,    0,  837,  837,
          826,  826,  826,    0,    0,    0,    0,    0,    0,    0,
            0,  927,    0,    0,    0,    0,    0,    0,    0,  826,
            0,  927,    0,    0,    0,    0,    0,    0,    0,    0,
            0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
            0,  826,    0,    0,    0,    0,    0,    0,    0,    0,
            0,  818,  868,    0,    0,  868,    0,  818,  818,  818,
            0,    0,    0,  862,  848
    );

    protected array $actionDefault = array(
            3,32767,32767,32767,  102,  102,32767,32767,32767,32767,
        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
        32767,32767,32767,32767,32767,32767,32767,32767,32767,  100,
        32767,  632,  632,  632,  632,32767,32767,  257,  102,32767,
        32767,  503,32767,  417,  417,  417,32767,32767,32767,  576,
          576,  576,  576,  576,   17,32767,32767,32767,32767,32767,
        32767,32767,  503,32767,32767,32767,32767,32767,32767,32767,
        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
        32767,32767,32767,   36,    7,    8,   10,   11,   49,  340,
          100,32767,32767,32767,32767,32767,32767,32767,  102,32767,
        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
          404,  625,32767,32767,32767,32767,32767,32767,32767,32767,
        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
        32767,  497,  507,  485,  486,  488,  489,  416,  577,  631,
          344,  628,  339,  415,  146,  354,  342,  245,  261,  508,
          262,  509,  512,  513,  218,  401,  150,  151,  448,  504,
          450,  502,  506,  449,  422,  429,  430,  431,  432,  433,
          434,  435,  436,  437,  438,  439,  440,  441,  420,  421,
          505,  482,  481,  480,32767,32767,  446,  447,32767,32767,
        32767,32767,32767,32767,32767,32767,  102,32767,  451,  454,
          419,  452,  453,  470,  471,  468,  469,  472,32767,  323,
        32767,  473,  474,  475,  476,32767,32767,  382,  196,  380,
        32767,  477,32767,  111,  455,  323,  111,32767,32767,32767,
        32767,32767,32767,32767,32767,32767,  461,  462,32767,32767,
        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
        32767,32767,32767,32767,32767,  102,32767,32767,32767,  100,
          520,  570,  479,  456,  457,32767,  545,32767,  102,32767,
          547,32767,32767,32767,32767,32767,32767,32767,32767,  572,
          443,  445,  540,  626,  423,  629,32767,  533,  100,  196,
        32767,  546,  196,  196,32767,32767,32767,32767,32767,32767,
        32767,32767,32767,32767,  571,32767,  639,  533,  110,  110,
          110,  110,  110,  110,  110,  110,  110,  110,  110,  110,
        32767,  196,  110,32767,  110,  110,32767,32767,  100,  196,
          196,  196,  196,  196,  196,  196,  196,  548,  196,  196,
          191,32767,  271,  273,  102,  594,  196,  550,32767,32767,
        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
        32767,  404,32767,32767,32767,32767,  533,  466,  139,32767,
          535,  139,  578,  458,  459,  460,  578,  578,  578,  319,
          296,32767,32767,32767,32767,32767,  548,  548,  100,  100,
          100,  100,32767,32767,32767,32767,  111,  519,   99,   99,
           99,   99,   99,  103,  101,32767,32767,32767,32767,  226,
        32767,  101,  101,   99,32767,  101,  101,32767,32767,  226,
          228,  215,  230,32767,  598,  599,  226,  101,  230,  230,
          230,  250,  250,  522,  325,  101,   99,  101,  101,  198,
          325,  325,32767,  101,  522,  325,  522,  325,  200,  325,
          325,  325,  522,  325,32767,  101,  325,  217,   99,   99,
          325,32767,32767,32767,32767,  535,32767,32767,32767,32767,
        32767,32767,32767,  225,32767,32767,32767,32767,32767,32767,
        32767,32767,  565,32767,  583,  596,  464,  465,  467,  582,
          580,  490,  491,  492,  493,  494,  495,  496,  499,  627,
        32767,  539,32767,32767,32767,  353,32767,  637,32767,32767,
        32767,    9,   74,  528,   42,   43,   51,   57,  554,  555,
          556,  557,  551,  552,  558,  553,32767,32767,32767,32767,
        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
          638,32767,  578,32767,32767,32767,32767,  463,  560,  604,
        32767,32767,  579,  630,32767,32767,32767,32767,32767,32767,
        32767,32767,  139,32767,32767,32767,32767,32767,32767,32767,
        32767,32767,32767,  565,32767,  137,32767,32767,32767,32767,
        32767,32767,32767,32767,  561,32767,32767,32767,  578,32767,
        32767,32767,32767,  321,  318,32767,32767,32767,32767,32767,
        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
        32767,  578,32767,32767,32767,32767,32767,  298,32767,  315,
        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
        32767,32767,32767,32767,32767,32767,32767,32767,  400,  535,
          301,  303,  304,32767,32767,32767,32767,  376,32767,32767,
        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
        32767,  153,  153,    3,    3,  356,  153,  153,  153,  356,
          356,  153,  356,  356,  356,  153,  153,  153,  153,  153,
          153,  153,  283,  186,  265,  268,  250,  250,  153,  368,
          153,  402,  402,  411
    );

    protected array $goto = array(
          200,  168,  200,  200,  200, 1066, 1098,  716,  447,  681,
          641,  678,  442,  344,  340,  341,  343,  612,  446,  345,
          448,  658,  480,  725,  569,  569,  569,  569, 1242,  623,
          171,  171,  171,  171,  224,  201,  197,  197,  181,  183,
          219,  206,  197,  197,  197,  197,  197, 1192,  198,  198,
          198,  198,  198, 1192,  191,  192,  193,  194,  195,  196,
          221,  219,  222,  556,  557,  437,  558,  561,  562,  563,
          564,  565,  566,  567,  568,  172,  173,  174,  199,  175,
          176,  177,  169,  178,  179,  180,  182,  218,  220,  223,
          241,  246,  247,  258,  259,  261,  262,  263,  264,  265,
          266,  267,  271,  272,  273,  274,  281,  284,  296,  297,
          323,  324,  443,  444,  445,  617,  225,  226,  227,  228,
          229,  230,  231,  232,  233,  234,  235,  236,  237,  238,
          239,  240,  192,  193,  194,  195,  196,  221,  202,  203,
          204,  205,  242,  184,  185,  186,  207,  203,  187,  243,
          167,  208,  209,  188,  210,  211,  212,  189,  213,  214,
          170,  215,  216,  217,  190,  880,  286,  283,  286,  286,
          877, 1122,  254,  254,  254,  254,  254,  602,  358,  878,
          619,  755,  657,  659, 1003,  441,  679,  632,  858,  852,
          703,  706, 1038,  714,  723, 1034,  730,  919,  876,  919,
          919,  435,  252,  252,  252,  252,  249,  255, 1069, 1069,
          701,  980, 1397, 1397, 1061, 1077, 1078,  350,  935,  930,
          931,  944,  886,  932,  883,  933,  934,  884,  887, 1397,
          938,  891,  858,  912,  858,  890,  356, 1041, 1041,  873,
          351,  350,  873, 1128, 1124, 1125,  531,  643,  643, 1400,
         1400, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329,
         1329,  609,  618, 1141, 1066, 1298,  410,  413,  620,  624,
         1298, 1298, 1097, 1099, 1101, 1066,  728, 1298,  527,  719,
         1066, 1139, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066,
         1066,  873,  713, 1066, 1066, 1066, 1066,  674,  675, 1298,
          692,  693,  694,  854, 1298, 1298, 1298, 1298,  713,  475,
         1298,  713,  475, 1298, 1298, 1379, 1379, 1379, 1379, 1387,
          580,  573, 1072, 1071,  894,  366,  968,  968,  952, 1148,
         1176, 1149,  953,  486,  486,  366,  366,  363,  363,  363,
          363,  571,  906,  486,  498,  893,  571,  571,  366,  366,
         1024,  500,  366,  571, 1414,  457,  408,  316,  573,  580,
          604,  605,  317,  615,  621,  607,  637,  638,  519,  575,
          520,  680,  366,  366,   28,  473,  526,  995,  426,  724,
          997,  997,  997,  997, 1075, 1076,  473, 1348, 1348,  991,
          998, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348,
         1348, 1345, 1345,  330,  314, 1345, 1345, 1345, 1345, 1345,
         1345, 1345, 1345, 1345, 1345,  257,  257,  495, 1372, 1373,
         1374, 1375,  873,  907,  895, 1110, 1114, 1359,  466,  939,
          466,  940,  359,  360, 1188,  466,  466, 1006, 1090, 1370,
          651,  653,  655,  702, 1370, 1370,  559,  559,  686,  431,
          559, 1370,  559,  559,  559,  559,  559,  559,  559,  559,
          996,  346,  574,  599,  574,    8,  455,    9,  574,  636,
          599,  870,  411,  479, 1381, 1381, 1381, 1381,  613,  635,
          986,  575, 1160,  911,  417,  489,  616,  490,  491,  899,
          896, 1171, 1046, 1289,  904, 1111,  758, 1405, 1406, 1057,
         1366,  560,  560,  418,  496,  560,  560,  560,  560,  560,
          560,  560,  560,  560,  560, 1027,  908, 1008,  999, 1115,
          754,  385,  902,  572, 1036, 1031, 1162,    0,  466,  466,
          466,  466,  466,  466,  466,  466,  466,  466,  466,  466,
            0,  338,  466,    0,  466,  466, 1291,    0,    0, 1113,
            0, 1293,    0,    0,    0, 1368, 1368, 1113,  630,  644,
          647,  648,  649,  650,  671,  672,  673,  727,  729,    0,
          625,  626,  423,  424,  957, 1178,    0,  690,    0,  691,
            0,  428,  429,  430,    0,  704, 1043,    0,  432,    0,
            0,    0,  354,    0,  898,    0,  684, 1022,    0,  277,
            0,  329,  892,  449,    0,    0,  329,  329, 1294, 1295,
            0, 1281,    0,    0, 1288,    0,    0,  868,    0,  449,
            0,    0, 1274,  972, 1281, 1073, 1073, 1275, 1278,  973,
            0, 1279,  685, 1084, 1080, 1081,    0, 1296, 1356, 1357,
            0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
            0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
            0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
            0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
            0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
            0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
            0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
            0,    0,    0,    0,    0,  763,  763
    );

    protected array $gotoCheck = array(
           42,   42,   42,   42,   42,   73,  130,   73,   66,   66,
           56,   56,   66,   66,   66,   66,   66,   66,   66,   66,
           66,   66,  158,    9,  107,  107,  107,  107,  158,  107,
           42,   42,   42,   42,   42,   42,   42,   42,   42,   42,
           42,   42,   42,   42,   42,   42,   42,   42,   42,   42,
           42,   42,   42,   42,   42,   42,   42,   42,   42,   42,
           42,   42,   42,   42,   42,   42,   42,   42,   42,   42,
           42,   42,   42,   42,   42,   42,   42,   42,   42,   42,
           42,   42,   42,   42,   42,   42,   42,   42,   42,   42,
           42,   42,   42,   42,   42,   42,   42,   42,   42,   42,
           42,   42,   42,   42,   42,   42,   42,   42,   42,   42,
           42,   42,   42,   42,   42,   42,   42,   42,   42,   42,
           42,   42,   42,   42,   42,   42,   42,   42,   42,   42,
           42,   42,   42,   42,   42,   42,   42,   42,   42,   42,
           42,   42,   42,   42,   42,   42,   42,   42,   42,   42,
           42,   42,   42,   42,   42,   42,   42,   42,   42,   42,
           42,   42,   42,   42,   42,   15,   23,   23,   23,   23,
           26,   15,    5,    5,    5,    5,    5,   48,   97,   27,
          133,   48,   48,   48,   49,   13,   48,   13,   12,    6,
           48,   48,   48,   48,   48,   48,   48,   25,   25,   25,
           25,   43,    5,    5,    5,    5,    5,    5,   89,   89,
           89,   89,  190,  190,   89,   89,   89,  176,   15,   15,
           15,   15,   15,   15,   15,   15,   15,   15,   15,  190,
           15,   15,   12,   45,   12,   15,  187,  107,  107,   22,
          176,  176,   22,   15,   15,   15,   76,  108,  108,  190,
          190,  108,  108,  108,  108,  108,  108,  108,  108,  108,
          108,  180,    8,    8,   73,   73,   59,   59,   59,   59,
           73,   73,  129,  129,  129,   73,    8,   73,    8,    8,
           73,    8,   73,   73,   73,   73,   73,   73,   73,   73,
           73,   22,    7,   73,   73,   73,   73,   86,   86,   73,
           86,   86,   86,    7,   73,   73,   73,   73,    7,   83,
           73,    7,   83,   73,   73,    9,    9,    9,    9,  189,
           76,   76,  119,  119,   35,   14,    9,    9,   73,  148,
          148,  148,   73,  156,  156,   14,   14,   24,   24,   24,
           24,   19,   35,  156,   84,   35,   19,   19,   14,   14,
          103,   84,   14,   19,   14,   83,   62,   76,   76,   76,
           76,   76,   76,   76,   76,  104,   76,   76,  162,   14,
          162,   64,   14,   14,   76,   19,  162,   93,   93,   93,
           19,   19,   19,   19,  120,  120,   19,  178,  178,   19,
           19,  178,  178,  178,  178,  178,  178,  178,  178,  178,
          178,  179,  179,  177,  177,  179,  179,  179,  179,  179,
          179,  179,  179,  179,  179,    5,    5,  184,  184,  184,
          186,  186,   22,   16,   16,   16,   16,   14,   23,   65,
           23,   65,   97,   97,  157,   23,   23,   16,  115,  133,
           85,   85,   85,  117,  133,  133,  164,  164,  121,   14,
          164,  133,  164,  164,  164,  164,  164,  164,  164,  164,
           16,   29,    9,    9,    9,   46,  113,   46,    9,   80,
            9,   18,    9,    9,  133,  133,  133,  133,    2,    2,
           92,   14,   16,   16,   28,    9,    9,    9,    9,   39,
           37,  155,  110,  168,    9,  132,   99,    9,    9,  114,
          133,  181,  181,   31,  159,  181,  181,  181,  181,  181,
          181,  181,  181,  181,  181,   50,   41,   96,   50,  135,
           50,  140,    9,   50,   50,   50,  151,   -1,   23,   23,
           23,   23,   23,   23,   23,   23,   23,   23,   23,   23,
           -1,    9,   23,   -1,   23,   23,   14,   -1,   -1,  133,
           -1,   20,   -1,   -1,   -1,  133,  133,  133,   81,   81,
           81,   81,   81,   81,   81,   81,   81,   81,   81,   -1,
           17,   17,   82,   82,   17,   17,   -1,   82,   -1,   82,
           -1,   82,   82,   82,   -1,   82,   17,   -1,   82,   -1,
           -1,   -1,   82,   -1,   17,   -1,   17,   17,   -1,   24,
           -1,   24,   17,  118,   -1,   -1,   24,   24,   20,   20,
           -1,   20,   -1,   -1,   17,   -1,   -1,   20,   -1,  118,
           -1,   -1,   79,   79,   20,  118,  118,   79,   79,   79,
           -1,   79,  118,  118,  118,  118,   -1,   20,   20,   20,
           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
           -1,   -1,   -1,   -1,   -1,   24,   24
    );

    protected array $gotoBase = array(
            0,    0, -255,    0,    0,  171,  176,  282,  251,   10,
            0,    0, -114, -161,   22, -190,  -46,   89,  156,   71,
          144,    0,  -44,  160,  331,  191,  163,  172,  138,  159,
            0,   49,    0,    0,    0,  -59,    0,  142,    0,  161,
            0,   58,   -1,  175,    0,  204, -293,    0, -555,  164,
          501,    0,    0,    0,    0,    0,  -34,    0,    0,  216,
            0,    0,  310,    0,  126,  413, -236,    0,    0,    0,
            0,    0,    0,   -5,    0,    0,  -32,    0,    0,  199,
          152,  189,   62,   26, -145,  -58, -442,    0,    0,  -79,
            0,    0,  146,   67,    0,    0,   57, -324,    0,   74,
            0,    0,    0,  312,  318,    0,    0,   -7,    3,    0,
          110,    0,    0,  181,   68,  158,    0,  154,  334,   35,
           93,  150,    0,    0,    0,    0,    0,    0,    0,  269,
            4,    0,  107,  169,    0,   60,    0,    0,    0,    0,
         -241,    0,    0,    0,    0,    0,    0,    0,   52,    0,
            0,   65,    0,    0,    0,  129,  296,  185, -254,   47,
            0,    0, -146,    0,  202,    0,    0,    0,  112,    0,
            0,    0,    0,    0,    0,    0,  -86,   77,  143,  157,
          229,  257,    0,    0,  111,    0,   34,  203,    0,  285,
         -101,    0,    0
    );

    protected array $gotoDefault = array(
        -32768,  532,  765,    7,  766,  961,  841,  850,  596,  550,
          726,  355,  645,  438, 1364,  937, 1177,  614,  869, 1307,
         1313,  474,  872,  335,  752,  949,  920,  921,  414,  401,
          885,  412,  669,  646,  513,  905,  470,  897,  505,  900,
          469,  909,  166,  434,  529,  913,    6,  916,  578,  947,
         1001,  402,  924,  403,  697,  926,  598,  928,  929,  409,
          415,  416, 1182,  606,  642,  941,  260,  600,  942,  400,
          943,  951,  405,  407,  707,  485,  524,  518,  427, 1143,
          601,  629,  666,  463,  492,  640,  652,  639,  499,  450,
          433,  334,  985,  993,  506,  483, 1007,  357, 1015,  760,
         1190,  660,  508, 1023,  661, 1030, 1033,  551,  552,  497,
         1045,  269, 1048,  509, 1058,   26,  687, 1063, 1064,  688,
          662, 1086,  663,  689,  664, 1088,  482, 1191,  481, 1100,
         1109,  471, 1112, 1353,  472, 1116,  268, 1119,  285,  361,
          384,  451, 1126, 1127,   12, 1133,  717,  718,   25,  279,
          528, 1161,  708, 1167,  278, 1170,  468, 1189,  467, 1262,
         1264,  579,  510, 1282,  320, 1285,  700,  525, 1290,  464,
         1355,  465,  553,  493,  342,  554, 1398,  313,  364,  339,
          570,  321,  365,  555,  494, 1361, 1369,  336,   34, 1388,
         1399,  611,  634
    );

    protected array $ruleToNonTerminal = array(
            0,    1,    3,    3,    2,    5,    5,    6,    6,    6,
            6,    6,    6,    6,    6,    6,    6,    6,    6,    6,
            6,    6,    6,    6,    6,    6,    6,    6,    6,    6,
            6,    6,    6,    6,    6,    6,    6,    6,    6,    6,
            6,    6,    6,    6,    6,    6,    6,    6,    6,    6,
            6,    6,    6,    6,    6,    6,    6,    6,    6,    6,
            6,    6,    6,    6,    6,    6,    6,    6,    6,    6,
            6,    6,    6,    6,    6,    6,    6,    7,    7,    7,
            7,    7,    7,    7,    7,    8,    8,    9,   10,   11,
           11,   11,   12,   12,   13,   13,   14,   15,   15,   16,
           16,   17,   17,   18,   18,   21,   21,   22,   23,   23,
           24,   24,    4,    4,    4,    4,    4,    4,    4,    4,
            4,    4,    4,    4,   29,   29,   30,   30,   32,   34,
           34,   28,   36,   36,   33,   38,   38,   35,   35,   37,
           37,   39,   39,   31,   40,   40,   41,   43,   44,   44,
           45,   45,   46,   46,   48,   47,   47,   47,   47,   49,
           49,   49,   49,   49,   49,   49,   49,   49,   49,   49,
           49,   49,   49,   49,   49,   49,   49,   49,   49,   49,
           49,   49,   49,   25,   25,   50,   69,   69,   72,   72,
           71,   70,   70,   63,   75,   75,   76,   76,   77,   77,
           78,   78,   79,   79,   80,   80,   80,   80,   26,   26,
           27,   27,   27,   27,   27,   88,   88,   90,   90,   83,
           83,   91,   91,   92,   92,   92,   84,   84,   87,   87,
           85,   85,   93,   94,   94,   57,   57,   65,   65,   68,
           68,   68,   67,   95,   95,   96,   58,   58,   58,   58,
           97,   97,   98,   98,   99,   99,  100,  101,  101,  102,
          102,  103,  103,   55,   55,   51,   51,  105,   53,   53,
          106,   52,   52,   54,   54,   64,   64,   64,   64,   81,
           81,  109,  109,  111,  111,  112,  112,  112,  112,  112,
          112,  112,  112,  110,  110,  110,  115,  115,  115,  115,
           89,   89,  118,  118,  118,  119,  119,  116,  116,  120,
          120,  122,  122,  123,  123,  117,  124,  124,  121,  125,
          125,  125,  125,  113,  113,   82,   82,   82,   20,   20,
          127,  127,  127,  128,  128,  128,  126,  126,  130,  130,
          130,  130,  130,  130,  129,  129,   60,  131,  131,  132,
           61,  134,  134,  135,  135,  136,  136,   86,  137,  137,
          137,  137,  137,  137,  137,  137,  143,  143,  144,  144,
          145,  145,  145,  145,  145,  146,  147,  147,  142,  142,
          138,  138,  141,  141,  149,  149,  148,  148,  148,  148,
          148,  148,  148,  148,  148,  148,  139,  150,  150,  152,
          151,  151,  140,  140,  114,  114,  153,  153,  155,  155,
          155,  154,  154,   62,  104,  156,  156,   56,   56,   42,
           42,   42,   42,   42,   42,   42,   42,   42,   42,   42,
           42,   42,   42,   42,   42,   42,   42,   42,   42,   42,
           42,   42,   42,   42,   42,   42,   42,   42,   42,   42,
           42,   42,   42,   42,   42,   42,   42,   42,   42,   42,
           42,   42,   42,   42,   42,   42,   42,   42,   42,   42,
           42,   42,   42,   42,   42,   42,   42,   42,   42,   42,
           42,   42,   42,   42,   42,   42,   42,   42,   42,   42,
           42,   42,   42,   42,   42,   42,   42,   42,   42,   42,
           42,   42,   42,   42,   42,   42,   42,   42,   42,   42,
           42,   42,   42,   42,   42,   42,  163,  164,  164,  165,
          157,  157,  162,  162,  166,  167,  167,  168,  169,  170,
          170,  170,  170,   19,   19,   73,   73,   73,   73,  158,
          158,  158,  158,  172,  172,  161,  161,  161,  159,  159,
          178,  178,  178,  178,  178,  178,  178,  178,  178,  178,
          179,  179,  179,  108,  181,  181,  181,  181,  160,  160,
          160,  160,  160,  160,  160,  160,   59,   59,  175,  175,
          175,  175,  175,  182,  182,  171,  171,  171,  171,  183,
          183,  183,  183,  183,   74,   74,   66,   66,   66,   66,
          133,  133,  133,  133,  186,  185,  174,  174,  174,  174,
          174,  174,  173,  173,  173,  184,  184,  184,  184,  107,
          180,  188,  188,  187,  187,  189,  189,  189,  189,  189,
          189,  189,  189,  177,  177,  177,  177,  176,  191,  190,
          190,  190,  190,  190,  190,  190,  190,  192,  192,  192,
          192
    );

    protected array $ruleToLength = array(
            1,    1,    2,    0,    1,    1,    1,    1,    1,    1,
            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
            1,    1,    1,    1,    1,    1,    1,    1,    1,    0,
            1,    0,    1,    1,    2,    1,    3,    4,    1,    2,
            0,    1,    1,    1,    1,    4,    3,    5,    4,    3,
            4,    1,    3,    4,    1,    1,    8,    7,    2,    3,
            1,    2,    3,    1,    2,    3,    1,    1,    3,    1,
            3,    1,    2,    2,    3,    1,    3,    2,    3,    1,
            3,    3,    2,    0,    1,    1,    1,    1,    1,    3,
            7,   10,    5,    7,    9,    5,    3,    3,    3,    3,
            3,    3,    1,    2,    5,    7,    9,    6,    5,    6,
            3,    2,    1,    1,    1,    1,    0,    2,    1,    3,
            8,    0,    4,    2,    1,    3,    0,    1,    0,    1,
            0,    1,    3,    1,    1,    1,    1,    1,    8,    9,
            7,    8,    7,    6,    8,    0,    2,    0,    2,    1,
            2,    1,    2,    1,    1,    1,    0,    2,    0,    2,
            0,    2,    2,    1,    3,    1,    4,    1,    4,    1,
            1,    4,    2,    1,    3,    3,    3,    4,    4,    5,
            0,    2,    4,    3,    1,    1,    7,    0,    2,    1,
            3,    3,    4,    1,    4,    0,    2,    5,    0,    2,
            6,    0,    2,    0,    3,    1,    2,    1,    1,    2,
            0,    1,    3,    0,    2,    1,    1,    1,    1,    1,
            1,    1,    1,    7,    9,    6,    1,    2,    1,    1,
            1,    1,    1,    1,    1,    1,    3,    3,    3,    1,
            3,    3,    3,    3,    3,    1,    3,    3,    1,    1,
            2,    1,    1,    0,    1,    0,    2,    2,    2,    4,
            2,    4,    4,    3,    1,    3,    1,    3,    2,    2,
            1,    1,    3,    3,    1,    1,    2,    3,    1,    1,
            2,    3,    1,    1,    3,    2,    0,    1,    5,    7,
            5,    6,   10,    3,    5,    1,    1,    3,    0,    2,
            4,    5,    4,    4,    4,    3,    1,    1,    1,    1,
            1,    1,    0,    1,    1,    2,    1,    1,    1,    1,
            1,    1,    1,    1,    1,    1,    2,    1,    3,    1,
            1,    3,    0,    2,    0,    3,    5,    8,    1,    3,
            3,    0,    2,    2,    2,    3,    1,    0,    1,    1,
            3,    3,    3,    4,    4,    1,    1,    2,    2,    3,
            3,    3,    3,    3,    3,    3,    3,    3,    3,    3,
            3,    3,    2,    2,    2,    2,    3,    3,    3,    3,
            3,    3,    3,    3,    3,    3,    3,    3,    3,    3,
            3,    3,    3,    3,    2,    2,    2,    2,    3,    3,
            3,    3,    3,    3,    3,    3,    3,    3,    3,    3,
            5,    4,    3,    4,    4,    2,    2,    4,    2,    2,
            2,    2,    2,    2,    2,    2,    2,    2,    2,    2,
            1,    3,    2,    1,    2,    4,    2,    2,    8,    9,
            8,    9,    9,   10,    9,   10,    8,    3,    2,    2,
            1,    1,    0,    4,    2,    1,    3,    2,    1,    2,
            2,    2,    4,    1,    1,    1,    1,    1,    1,    1,
            1,    3,    1,    1,    1,    0,    1,    1,    0,    1,
            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
            3,    5,    3,    3,    4,    1,    1,    3,    1,    1,
            1,    1,    1,    3,    2,    3,    0,    1,    1,    3,
            1,    1,    1,    1,    1,    1,    3,    1,    1,    1,
            4,    1,    4,    4,    0,    1,    1,    1,    3,    3,
            1,    4,    2,    2,    1,    3,    1,    4,    3,    3,
            3,    3,    1,    3,    1,    1,    3,    1,    1,    4,
            1,    1,    1,    3,    1,    1,    2,    1,    3,    4,
            3,    2,    0,    2,    2,    1,    2,    1,    1,    1,
            4,    3,    3,    3,    3,    6,    3,    1,    1,    2,
            1
    );

    protected function initReduceCallbacks(): void {
        $this->reduceCallbacks = [
            0 => null,
            1 => static function ($self, $stackPos) {
                 $self->semValue = $self->handleNamespaces($self->semStack[$stackPos-(1-1)]);
            },
            2 => static function ($self, $stackPos) {
                 if ($self->semStack[$stackPos-(2-2)] !== null) { $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; } $self->semValue = $self->semStack[$stackPos-(2-1)];;
            },
            3 => static function ($self, $stackPos) {
                 $self->semValue = array();
            },
            4 => static function ($self, $stackPos) {
                 $nop = $self->maybeCreateZeroLengthNop($self->tokenPos);;
            if ($nop !== null) { $self->semStack[$stackPos-(1-1)][] = $nop; } $self->semValue = $self->semStack[$stackPos-(1-1)];
            },
            5 => null,
            6 => null,
            7 => null,
            8 => null,
            9 => null,
            10 => null,
            11 => null,
            12 => null,
            13 => null,
            14 => null,
            15 => null,
            16 => null,
            17 => null,
            18 => null,
            19 => null,
            20 => null,
            21 => null,
            22 => null,
            23 => null,
            24 => null,
            25 => null,
            26 => null,
            27 => null,
            28 => null,
            29 => null,
            30 => null,
            31 => null,
            32 => null,
            33 => null,
            34 => null,
            35 => null,
            36 => null,
            37 => null,
            38 => null,
            39 => null,
            40 => null,
            41 => null,
            42 => null,
            43 => null,
            44 => null,
            45 => null,
            46 => null,
            47 => null,
            48 => null,
            49 => null,
            50 => null,
            51 => null,
            52 => null,
            53 => null,
            54 => null,
            55 => null,
            56 => null,
            57 => null,
            58 => null,
            59 => null,
            60 => null,
            61 => null,
            62 => null,
            63 => null,
            64 => null,
            65 => null,
            66 => null,
            67 => null,
            68 => null,
            69 => null,
            70 => null,
            71 => null,
            72 => null,
            73 => null,
            74 => null,
            75 => null,
            76 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(1-1)]; if ($self->semValue === "<?=") $self->emitError(new Error('Cannot use "<?=" as an identifier', $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])));
            },
            77 => null,
            78 => null,
            79 => null,
            80 => null,
            81 => null,
            82 => null,
            83 => null,
            84 => null,
            85 => static function ($self, $stackPos) {
                 $self->semValue = new Node\Identifier($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            86 => static function ($self, $stackPos) {
                 $self->semValue = new Node\Identifier($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            87 => static function ($self, $stackPos) {
                 $self->semValue = new Node\Identifier($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            88 => static function ($self, $stackPos) {
                 $self->semValue = new Node\Identifier($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            89 => static function ($self, $stackPos) {
                 $self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            90 => static function ($self, $stackPos) {
                 $self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            91 => static function ($self, $stackPos) {
                 $self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            92 => static function ($self, $stackPos) {
                 $self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            93 => static function ($self, $stackPos) {
                 $self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            94 => null,
            95 => static function ($self, $stackPos) {
                 $self->semValue = new Name(substr($self->semStack[$stackPos-(1-1)], 1), $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            96 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Variable(substr($self->semStack[$stackPos-(1-1)], 1), $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            97 => static function ($self, $stackPos) {
                 /* nothing */
            },
            98 => static function ($self, $stackPos) {
                 /* nothing */
            },
            99 => static function ($self, $stackPos) {
                 /* nothing */
            },
            100 => static function ($self, $stackPos) {
                 $self->emitError(new Error('A trailing comma is not allowed here', $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])));
            },
            101 => null,
            102 => null,
            103 => static function ($self, $stackPos) {
                 $self->semValue = new Node\Attribute($self->semStack[$stackPos-(1-1)], [], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            104 => static function ($self, $stackPos) {
                 $self->semValue = new Node\Attribute($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            105 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)]);
            },
            106 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
            },
            107 => static function ($self, $stackPos) {
                 $self->semValue = new Node\AttributeGroup($self->semStack[$stackPos-(4-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            108 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)]);
            },
            109 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)];
            },
            110 => static function ($self, $stackPos) {
                 $self->semValue = [];
            },
            111 => null,
            112 => null,
            113 => null,
            114 => null,
            115 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\HaltCompiler($self->handleHaltCompiler(), $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            116 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Namespace_($self->semStack[$stackPos-(3-2)], null, $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            $self->semValue->setAttribute('kind', Stmt\Namespace_::KIND_SEMICOLON);
            $self->checkNamespace($self->semValue);
            },
            117 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Namespace_($self->semStack[$stackPos-(5-2)], $self->semStack[$stackPos-(5-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]));
            $self->semValue->setAttribute('kind', Stmt\Namespace_::KIND_BRACED);
            $self->checkNamespace($self->semValue);
            },
            118 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Namespace_(null, $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            $self->semValue->setAttribute('kind', Stmt\Namespace_::KIND_BRACED);
            $self->checkNamespace($self->semValue);
            },
            119 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Use_($self->semStack[$stackPos-(3-2)], Stmt\Use_::TYPE_NORMAL, $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            120 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Use_($self->semStack[$stackPos-(4-3)], $self->semStack[$stackPos-(4-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            121 => null,
            122 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Const_($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]), []);
            },
            123 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Const_($self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(4-1)]);
            $self->checkConstantAttributes($self->semValue);
            },
            124 => static function ($self, $stackPos) {
                 $self->semValue = Stmt\Use_::TYPE_FUNCTION;
            },
            125 => static function ($self, $stackPos) {
                 $self->semValue = Stmt\Use_::TYPE_CONSTANT;
            },
            126 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\GroupUse($self->semStack[$stackPos-(8-3)], $self->semStack[$stackPos-(8-6)], $self->semStack[$stackPos-(8-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos]));
            },
            127 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\GroupUse($self->semStack[$stackPos-(7-2)], $self->semStack[$stackPos-(7-5)], Stmt\Use_::TYPE_UNKNOWN, $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos]));
            },
            128 => null,
            129 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
            },
            130 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)]);
            },
            131 => null,
            132 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
            },
            133 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)]);
            },
            134 => null,
            135 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
            },
            136 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)]);
            },
            137 => static function ($self, $stackPos) {
                 $self->semValue = new Node\UseItem($self->semStack[$stackPos-(1-1)], null, Stmt\Use_::TYPE_UNKNOWN, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); $self->checkUseUse($self->semValue, $stackPos-(1-1));
            },
            138 => static function ($self, $stackPos) {
                 $self->semValue = new Node\UseItem($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], Stmt\Use_::TYPE_UNKNOWN, $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); $self->checkUseUse($self->semValue, $stackPos-(3-3));
            },
            139 => static function ($self, $stackPos) {
                 $self->semValue = new Node\UseItem($self->semStack[$stackPos-(1-1)], null, Stmt\Use_::TYPE_UNKNOWN, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); $self->checkUseUse($self->semValue, $stackPos-(1-1));
            },
            140 => static function ($self, $stackPos) {
                 $self->semValue = new Node\UseItem($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], Stmt\Use_::TYPE_UNKNOWN, $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); $self->checkUseUse($self->semValue, $stackPos-(3-3));
            },
            141 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(1-1)]; $self->semValue->type = Stmt\Use_::TYPE_NORMAL;
            },
            142 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(2-2)]; $self->semValue->type = $self->semStack[$stackPos-(2-1)];
            },
            143 => null,
            144 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
            },
            145 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)]);
            },
            146 => static function ($self, $stackPos) {
                 $self->semValue = new Node\Const_($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            147 => null,
            148 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
            },
            149 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)]);
            },
            150 => static function ($self, $stackPos) {
                 $self->semValue = new Node\Const_(new Node\Identifier($self->semStack[$stackPos-(3-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)],  $self->tokenEndStack[$stackPos-(3-1)])), $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            151 => static function ($self, $stackPos) {
                 $self->semValue = new Node\Const_(new Node\Identifier($self->semStack[$stackPos-(3-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)],  $self->tokenEndStack[$stackPos-(3-1)])), $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            152 => static function ($self, $stackPos) {
                 if ($self->semStack[$stackPos-(2-2)] !== null) { $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; } $self->semValue = $self->semStack[$stackPos-(2-1)];;
            },
            153 => static function ($self, $stackPos) {
                 $self->semValue = array();
            },
            154 => static function ($self, $stackPos) {
                 $nop = $self->maybeCreateZeroLengthNop($self->tokenPos);;
            if ($nop !== null) { $self->semStack[$stackPos-(1-1)][] = $nop; } $self->semValue = $self->semStack[$stackPos-(1-1)];
            },
            155 => null,
            156 => null,
            157 => null,
            158 => static function ($self, $stackPos) {
                 throw new Error('__HALT_COMPILER() can only be used from the outermost scope', $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            159 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Block($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            160 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\If_($self->semStack[$stackPos-(7-3)], ['stmts' => $self->semStack[$stackPos-(7-5)], 'elseifs' => $self->semStack[$stackPos-(7-6)], 'else' => $self->semStack[$stackPos-(7-7)]], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos]));
            },
            161 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\If_($self->semStack[$stackPos-(10-3)], ['stmts' => $self->semStack[$stackPos-(10-6)], 'elseifs' => $self->semStack[$stackPos-(10-7)], 'else' => $self->semStack[$stackPos-(10-8)]], $self->getAttributes($self->tokenStartStack[$stackPos-(10-1)], $self->tokenEndStack[$stackPos]));
            },
            162 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\While_($self->semStack[$stackPos-(5-3)], $self->semStack[$stackPos-(5-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]));
            },
            163 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Do_($self->semStack[$stackPos-(7-5)], $self->semStack[$stackPos-(7-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos]));
            },
            164 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\For_(['init' => $self->semStack[$stackPos-(9-3)], 'cond' => $self->semStack[$stackPos-(9-5)], 'loop' => $self->semStack[$stackPos-(9-7)], 'stmts' => $self->semStack[$stackPos-(9-9)]], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos]));
            },
            165 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Switch_($self->semStack[$stackPos-(5-3)], $self->semStack[$stackPos-(5-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]));
            },
            166 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Break_($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            167 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Continue_($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            168 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Return_($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            169 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Global_($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            170 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Static_($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            171 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Echo_($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            172 => static function ($self, $stackPos) {

        $self->semValue = new Stmt\InlineHTML($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
        $self->semValue->setAttribute('hasLeadingNewline', $self->inlineHtmlHasLeadingNewline($stackPos-(1-1)));

            },
            173 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Expression($self->semStack[$stackPos-(2-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            174 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Unset_($self->semStack[$stackPos-(5-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]));
            },
            175 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Foreach_($self->semStack[$stackPos-(7-3)], $self->semStack[$stackPos-(7-5)][0], ['keyVar' => null, 'byRef' => $self->semStack[$stackPos-(7-5)][1], 'stmts' => $self->semStack[$stackPos-(7-7)]], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos]));
            },
            176 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Foreach_($self->semStack[$stackPos-(9-3)], $self->semStack[$stackPos-(9-7)][0], ['keyVar' => $self->semStack[$stackPos-(9-5)], 'byRef' => $self->semStack[$stackPos-(9-7)][1], 'stmts' => $self->semStack[$stackPos-(9-9)]], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos]));
            },
            177 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Foreach_($self->semStack[$stackPos-(6-3)], new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos-(6-4)],  $self->tokenEndStack[$stackPos-(6-4)])), ['stmts' => $self->semStack[$stackPos-(6-6)]], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos]));
            },
            178 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Declare_($self->semStack[$stackPos-(5-3)], $self->semStack[$stackPos-(5-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]));
            },
            179 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\TryCatch($self->semStack[$stackPos-(6-3)], $self->semStack[$stackPos-(6-5)], $self->semStack[$stackPos-(6-6)], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos])); $self->checkTryCatch($self->semValue);
            },
            180 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Goto_($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            181 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Label($self->semStack[$stackPos-(2-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            182 => static function ($self, $stackPos) {
                 $self->semValue = null; /* means: no statement */
            },
            183 => null,
            184 => static function ($self, $stackPos) {
                 $self->semValue = $self->maybeCreateNop($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]);
            },
            185 => static function ($self, $stackPos) {
                 if ($self->semStack[$stackPos-(1-1)] instanceof Stmt\Block) { $self->semValue = $self->semStack[$stackPos-(1-1)]->stmts; } else if ($self->semStack[$stackPos-(1-1)] === null) { $self->semValue = []; } else { $self->semValue = [$self->semStack[$stackPos-(1-1)]]; };
            },
            186 => static function ($self, $stackPos) {
                 $self->semValue = array();
            },
            187 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)];
            },
            188 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)]);
            },
            189 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
            },
            190 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Catch_($self->semStack[$stackPos-(8-3)], $self->semStack[$stackPos-(8-4)], $self->semStack[$stackPos-(8-7)], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos]));
            },
            191 => static function ($self, $stackPos) {
                 $self->semValue = null;
            },
            192 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Finally_($self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            193 => null,
            194 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)]);
            },
            195 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
            },
            196 => static function ($self, $stackPos) {
                 $self->semValue = false;
            },
            197 => static function ($self, $stackPos) {
                 $self->semValue = true;
            },
            198 => static function ($self, $stackPos) {
                 $self->semValue = false;
            },
            199 => static function ($self, $stackPos) {
                 $self->semValue = true;
            },
            200 => static function ($self, $stackPos) {
                 $self->semValue = false;
            },
            201 => static function ($self, $stackPos) {
                 $self->semValue = true;
            },
            202 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(3-2)];
            },
            203 => static function ($self, $stackPos) {
                 $self->semValue = [];
            },
            204 => null,
            205 => static function ($self, $stackPos) {
                 $self->semValue = new Node\Identifier($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            206 => static function ($self, $stackPos) {
                 $self->semValue = new Node\Identifier($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            207 => static function ($self, $stackPos) {
                 $self->semValue = new Node\Identifier($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            208 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Function_($self->semStack[$stackPos-(8-3)], ['byRef' => $self->semStack[$stackPos-(8-2)], 'params' => $self->semStack[$stackPos-(8-5)], 'returnType' => $self->semStack[$stackPos-(8-7)], 'stmts' => $self->semStack[$stackPos-(8-8)], 'attrGroups' => []], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos]));
            },
            209 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Function_($self->semStack[$stackPos-(9-4)], ['byRef' => $self->semStack[$stackPos-(9-3)], 'params' => $self->semStack[$stackPos-(9-6)], 'returnType' => $self->semStack[$stackPos-(9-8)], 'stmts' => $self->semStack[$stackPos-(9-9)], 'attrGroups' => $self->semStack[$stackPos-(9-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos]));
            },
            210 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Class_($self->semStack[$stackPos-(7-2)], ['type' => $self->semStack[$stackPos-(7-1)], 'extends' => $self->semStack[$stackPos-(7-3)], 'implements' => $self->semStack[$stackPos-(7-4)], 'stmts' => $self->semStack[$stackPos-(7-6)], 'attrGroups' => []], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos]));
            $self->checkClass($self->semValue, $stackPos-(7-2));
            },
            211 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Class_($self->semStack[$stackPos-(8-3)], ['type' => $self->semStack[$stackPos-(8-2)], 'extends' => $self->semStack[$stackPos-(8-4)], 'implements' => $self->semStack[$stackPos-(8-5)], 'stmts' => $self->semStack[$stackPos-(8-7)], 'attrGroups' => $self->semStack[$stackPos-(8-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos]));
            $self->checkClass($self->semValue, $stackPos-(8-3));
            },
            212 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Interface_($self->semStack[$stackPos-(7-3)], ['extends' => $self->semStack[$stackPos-(7-4)], 'stmts' => $self->semStack[$stackPos-(7-6)], 'attrGroups' => $self->semStack[$stackPos-(7-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos]));
            $self->checkInterface($self->semValue, $stackPos-(7-3));
            },
            213 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Trait_($self->semStack[$stackPos-(6-3)], ['stmts' => $self->semStack[$stackPos-(6-5)], 'attrGroups' => $self->semStack[$stackPos-(6-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos]));
            },
            214 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Enum_($self->semStack[$stackPos-(8-3)], ['scalarType' => $self->semStack[$stackPos-(8-4)], 'implements' => $self->semStack[$stackPos-(8-5)], 'stmts' => $self->semStack[$stackPos-(8-7)], 'attrGroups' => $self->semStack[$stackPos-(8-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos]));
            $self->checkEnum($self->semValue, $stackPos-(8-3));
            },
            215 => static function ($self, $stackPos) {
                 $self->semValue = null;
            },
            216 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(2-2)];
            },
            217 => static function ($self, $stackPos) {
                 $self->semValue = null;
            },
            218 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(2-2)];
            },
            219 => static function ($self, $stackPos) {
                 $self->semValue = 0;
            },
            220 => null,
            221 => null,
            222 => static function ($self, $stackPos) {
                 $self->checkClassModifier($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)], $stackPos-(2-2)); $self->semValue = $self->semStack[$stackPos-(2-1)] | $self->semStack[$stackPos-(2-2)];
            },
            223 => static function ($self, $stackPos) {
                 $self->semValue = Modifiers::ABSTRACT;
            },
            224 => static function ($self, $stackPos) {
                 $self->semValue = Modifiers::FINAL;
            },
            225 => static function ($self, $stackPos) {
                 $self->semValue = Modifiers::READONLY;
            },
            226 => static function ($self, $stackPos) {
                 $self->semValue = null;
            },
            227 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(2-2)];
            },
            228 => static function ($self, $stackPos) {
                 $self->semValue = array();
            },
            229 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(2-2)];
            },
            230 => static function ($self, $stackPos) {
                 $self->semValue = array();
            },
            231 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(2-2)];
            },
            232 => null,
            233 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)]);
            },
            234 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
            },
            235 => null,
            236 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(4-2)];
            },
            237 => null,
            238 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(4-2)];
            },
            239 => static function ($self, $stackPos) {
                 if ($self->semStack[$stackPos-(1-1)] instanceof Stmt\Block) { $self->semValue = $self->semStack[$stackPos-(1-1)]->stmts; } else if ($self->semStack[$stackPos-(1-1)] === null) { $self->semValue = []; } else { $self->semValue = [$self->semStack[$stackPos-(1-1)]]; };
            },
            240 => static function ($self, $stackPos) {
                 $self->semValue = null;
            },
            241 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(4-2)];
            },
            242 => null,
            243 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)]);
            },
            244 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
            },
            245 => static function ($self, $stackPos) {
                 $self->semValue = new Node\DeclareItem($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            246 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(3-2)];
            },
            247 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(4-3)];
            },
            248 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(4-2)];
            },
            249 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(5-3)];
            },
            250 => static function ($self, $stackPos) {
                 $self->semValue = array();
            },
            251 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)];
            },
            252 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Case_($self->semStack[$stackPos-(4-2)], $self->semStack[$stackPos-(4-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            253 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Case_(null, $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            254 => null,
            255 => null,
            256 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Match_($self->semStack[$stackPos-(7-3)], $self->semStack[$stackPos-(7-6)], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos]));
            },
            257 => static function ($self, $stackPos) {
                 $self->semValue = [];
            },
            258 => null,
            259 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)]);
            },
            260 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
            },
            261 => static function ($self, $stackPos) {
                 $self->semValue = new Node\MatchArm($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            262 => static function ($self, $stackPos) {
                 $self->semValue = new Node\MatchArm(null, $self->semStack[$stackPos-(4-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            263 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(1-1)];
            },
            264 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(4-2)];
            },
            265 => static function ($self, $stackPos) {
                 $self->semValue = array();
            },
            266 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)];
            },
            267 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\ElseIf_($self->semStack[$stackPos-(5-3)], $self->semStack[$stackPos-(5-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]));
            },
            268 => static function ($self, $stackPos) {
                 $self->semValue = array();
            },
            269 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)];
            },
            270 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\ElseIf_($self->semStack[$stackPos-(6-3)], $self->semStack[$stackPos-(6-6)], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos])); $self->fixupAlternativeElse($self->semValue);
            },
            271 => static function ($self, $stackPos) {
                 $self->semValue = null;
            },
            272 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Else_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            273 => static function ($self, $stackPos) {
                 $self->semValue = null;
            },
            274 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Else_($self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); $self->fixupAlternativeElse($self->semValue);
            },
            275 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)], false);
            },
            276 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(2-2)], true);
            },
            277 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)], false);
            },
            278 => static function ($self, $stackPos) {
                 $self->semValue = array($self->fixupArrayDestructuring($self->semStack[$stackPos-(1-1)]), false);
            },
            279 => null,
            280 => static function ($self, $stackPos) {
                 $self->semValue = array();
            },
            281 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)]);
            },
            282 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
            },
            283 => static function ($self, $stackPos) {
                 $self->semValue = 0;
            },
            284 => static function ($self, $stackPos) {
                 $self->checkModifier($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)], $stackPos-(2-2)); $self->semValue = $self->semStack[$stackPos-(2-1)] | $self->semStack[$stackPos-(2-2)];
            },
            285 => static function ($self, $stackPos) {
                 $self->semValue = Modifiers::PUBLIC;
            },
            286 => static function ($self, $stackPos) {
                 $self->semValue = Modifiers::PROTECTED;
            },
            287 => static function ($self, $stackPos) {
                 $self->semValue = Modifiers::PRIVATE;
            },
            288 => static function ($self, $stackPos) {
                 $self->semValue = Modifiers::PUBLIC_SET;
            },
            289 => static function ($self, $stackPos) {
                 $self->semValue = Modifiers::PROTECTED_SET;
            },
            290 => static function ($self, $stackPos) {
                 $self->semValue = Modifiers::PRIVATE_SET;
            },
            291 => static function ($self, $stackPos) {
                 $self->semValue = Modifiers::READONLY;
            },
            292 => static function ($self, $stackPos) {
                 $self->semValue = Modifiers::FINAL;
            },
            293 => static function ($self, $stackPos) {
                 $self->semValue = new Node\Param($self->semStack[$stackPos-(7-6)], null, $self->semStack[$stackPos-(7-3)], $self->semStack[$stackPos-(7-4)], $self->semStack[$stackPos-(7-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(7-2)], $self->semStack[$stackPos-(7-1)], $self->semStack[$stackPos-(7-7)]);
            $self->checkParam($self->semValue);
            $self->addPropertyNameToHooks($self->semValue);
            },
            294 => static function ($self, $stackPos) {
                 $self->semValue = new Node\Param($self->semStack[$stackPos-(9-6)], $self->semStack[$stackPos-(9-8)], $self->semStack[$stackPos-(9-3)], $self->semStack[$stackPos-(9-4)], $self->semStack[$stackPos-(9-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(9-2)], $self->semStack[$stackPos-(9-1)], $self->semStack[$stackPos-(9-9)]);
            $self->checkParam($self->semValue);
            $self->addPropertyNameToHooks($self->semValue);
            },
            295 => static function ($self, $stackPos) {
                 $self->semValue = new Node\Param(new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos])), null, $self->semStack[$stackPos-(6-3)], $self->semStack[$stackPos-(6-4)], $self->semStack[$stackPos-(6-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(6-2)], $self->semStack[$stackPos-(6-1)]);
            },
            296 => null,
            297 => static function ($self, $stackPos) {
                 $self->semValue = new Node\NullableType($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            298 => static function ($self, $stackPos) {
                 $self->semValue = new Node\UnionType($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            299 => null,
            300 => null,
            301 => static function ($self, $stackPos) {
                 $self->semValue = new Node\Name('static', $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            302 => static function ($self, $stackPos) {
                 $self->semValue = $self->handleBuiltinTypes($self->semStack[$stackPos-(1-1)]);
            },
            303 => static function ($self, $stackPos) {
                 $self->semValue = new Node\Identifier('array', $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            304 => static function ($self, $stackPos) {
                 $self->semValue = new Node\Identifier('callable', $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            305 => null,
            306 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(3-2)];
            },
            307 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)]);
            },
            308 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
            },
            309 => null,
            310 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(3-2)];
            },
            311 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)]);
            },
            312 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
            },
            313 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)]);
            },
            314 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
            },
            315 => static function ($self, $stackPos) {
                 $self->semValue = new Node\IntersectionType($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            316 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)]);
            },
            317 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
            },
            318 => static function ($self, $stackPos) {
                 $self->semValue = new Node\IntersectionType($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            319 => null,
            320 => static function ($self, $stackPos) {
                 $self->semValue = new Node\NullableType($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            321 => static function ($self, $stackPos) {
                 $self->semValue = new Node\UnionType($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            322 => null,
            323 => static function ($self, $stackPos) {
                 $self->semValue = null;
            },
            324 => null,
            325 => static function ($self, $stackPos) {
                 $self->semValue = null;
            },
            326 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(2-2)];
            },
            327 => static function ($self, $stackPos) {
                 $self->semValue = null;
            },
            328 => static function ($self, $stackPos) {
                 $self->semValue = array();
            },
            329 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(4-2)];
            },
            330 => static function ($self, $stackPos) {
                 $self->semValue = array();
            },
            331 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(4-2)];
            },
            332 => static function ($self, $stackPos) {
                 $self->semValue = array(new Node\Arg($self->semStack[$stackPos-(4-2)], false, false, $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])));
            },
            333 => static function ($self, $stackPos) {
                 $self->semValue = array(new Node\Arg($self->semStack[$stackPos-(3-1)], false, false, $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)],  $self->tokenEndStack[$stackPos-(3-1)])), $self->semStack[$stackPos-(3-3)]);
            },
            334 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)]);
            },
            335 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
            },
            336 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)]);
            },
            337 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
            },
            338 => static function ($self, $stackPos) {
                 $self->semValue = new Node\Arg($self->semStack[$stackPos-(2-2)], true, false, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            339 => static function ($self, $stackPos) {
                 $self->semValue = new Node\Arg($self->semStack[$stackPos-(2-2)], false, true, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            340 => static function ($self, $stackPos) {
                 $self->semValue = new Node\VariadicPlaceholder($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            341 => static function ($self, $stackPos) {
                 $self->semValue = new Node\ArgPlaceholder(null, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            342 => static function ($self, $stackPos) {
                 $self->semValue = new Node\Arg($self->semStack[$stackPos-(3-3)], false, false, $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(3-1)]);
            },
            343 => static function ($self, $stackPos) {
                 $self->semValue = new Node\ArgPlaceholder($self->semStack[$stackPos-(3-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            344 => static function ($self, $stackPos) {
                 $self->semValue = new Node\Arg($self->semStack[$stackPos-(1-1)], false, false, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            345 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(1-1)];
            },
            346 => null,
            347 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
            },
            348 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)]);
            },
            349 => null,
            350 => null,
            351 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
            },
            352 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)]);
            },
            353 => static function ($self, $stackPos) {
                 $self->semValue = new Node\StaticVar($self->semStack[$stackPos-(1-1)], null, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            354 => static function ($self, $stackPos) {
                 $self->semValue = new Node\StaticVar($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            355 => static function ($self, $stackPos) {
                 if ($self->semStack[$stackPos-(2-2)] !== null) { $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)]; } else { $self->semValue = $self->semStack[$stackPos-(2-1)]; }
            },
            356 => static function ($self, $stackPos) {
                 $self->semValue = array();
            },
            357 => static function ($self, $stackPos) {
                 $nop = $self->maybeCreateZeroLengthNop($self->tokenPos);;
            if ($nop !== null) { $self->semStack[$stackPos-(1-1)][] = $nop; } $self->semValue = $self->semStack[$stackPos-(1-1)];
            },
            358 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Property($self->semStack[$stackPos-(5-2)], $self->semStack[$stackPos-(5-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(5-3)], $self->semStack[$stackPos-(5-1)]);
            },
            359 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Property($self->semStack[$stackPos-(7-2)], $self->semStack[$stackPos-(7-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(7-3)], $self->semStack[$stackPos-(7-1)], $self->semStack[$stackPos-(7-6)]);
            $self->checkPropertyHooksForMultiProperty($self->semValue, $stackPos-(7-5));
            $self->checkEmptyPropertyHookList($self->semStack[$stackPos-(7-6)], $stackPos-(7-5));
            $self->addPropertyNameToHooks($self->semValue);
            },
            360 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\ClassConst($self->semStack[$stackPos-(5-4)], $self->semStack[$stackPos-(5-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(5-1)]);
            $self->checkClassConst($self->semValue, $stackPos-(5-2));
            },
            361 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\ClassConst($self->semStack[$stackPos-(6-5)], $self->semStack[$stackPos-(6-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(6-1)], $self->semStack[$stackPos-(6-4)]);
            $self->checkClassConst($self->semValue, $stackPos-(6-2));
            },
            362 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\ClassMethod($self->semStack[$stackPos-(10-5)], ['type' => $self->semStack[$stackPos-(10-2)], 'byRef' => $self->semStack[$stackPos-(10-4)], 'params' => $self->semStack[$stackPos-(10-7)], 'returnType' => $self->semStack[$stackPos-(10-9)], 'stmts' => $self->semStack[$stackPos-(10-10)], 'attrGroups' => $self->semStack[$stackPos-(10-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(10-1)], $self->tokenEndStack[$stackPos]));
            $self->checkClassMethod($self->semValue, $stackPos-(10-2));
            },
            363 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\TraitUse($self->semStack[$stackPos-(3-2)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            364 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\EnumCase($self->semStack[$stackPos-(5-3)], $self->semStack[$stackPos-(5-4)], $self->semStack[$stackPos-(5-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]));
            },
            365 => static function ($self, $stackPos) {
                 $self->semValue = null; /* will be skipped */
            },
            366 => static function ($self, $stackPos) {
                 $self->semValue = array();
            },
            367 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(3-2)];
            },
            368 => static function ($self, $stackPos) {
                 $self->semValue = array();
            },
            369 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)];
            },
            370 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\TraitUseAdaptation\Precedence($self->semStack[$stackPos-(4-1)][0], $self->semStack[$stackPos-(4-1)][1], $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            371 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\TraitUseAdaptation\Alias($self->semStack[$stackPos-(5-1)][0], $self->semStack[$stackPos-(5-1)][1], $self->semStack[$stackPos-(5-3)], $self->semStack[$stackPos-(5-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]));
            },
            372 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\TraitUseAdaptation\Alias($self->semStack[$stackPos-(4-1)][0], $self->semStack[$stackPos-(4-1)][1], $self->semStack[$stackPos-(4-3)], null, $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            373 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\TraitUseAdaptation\Alias($self->semStack[$stackPos-(4-1)][0], $self->semStack[$stackPos-(4-1)][1], null, $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            374 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\TraitUseAdaptation\Alias($self->semStack[$stackPos-(4-1)][0], $self->semStack[$stackPos-(4-1)][1], null, $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            375 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)]);
            },
            376 => null,
            377 => static function ($self, $stackPos) {
                 $self->semValue = array(null, $self->semStack[$stackPos-(1-1)]);
            },
            378 => static function ($self, $stackPos) {
                 $self->semValue = null;
            },
            379 => null,
            380 => null,
            381 => static function ($self, $stackPos) {
                 $self->semValue = 0;
            },
            382 => static function ($self, $stackPos) {
                 $self->semValue = 0;
            },
            383 => null,
            384 => null,
            385 => static function ($self, $stackPos) {
                 $self->checkModifier($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)], $stackPos-(2-2)); $self->semValue = $self->semStack[$stackPos-(2-1)] | $self->semStack[$stackPos-(2-2)];
            },
            386 => static function ($self, $stackPos) {
                 $self->semValue = Modifiers::PUBLIC;
            },
            387 => static function ($self, $stackPos) {
                 $self->semValue = Modifiers::PROTECTED;
            },
            388 => static function ($self, $stackPos) {
                 $self->semValue = Modifiers::PRIVATE;
            },
            389 => static function ($self, $stackPos) {
                 $self->semValue = Modifiers::PUBLIC_SET;
            },
            390 => static function ($self, $stackPos) {
                 $self->semValue = Modifiers::PROTECTED_SET;
            },
            391 => static function ($self, $stackPos) {
                 $self->semValue = Modifiers::PRIVATE_SET;
            },
            392 => static function ($self, $stackPos) {
                 $self->semValue = Modifiers::STATIC;
            },
            393 => static function ($self, $stackPos) {
                 $self->semValue = Modifiers::ABSTRACT;
            },
            394 => static function ($self, $stackPos) {
                 $self->semValue = Modifiers::FINAL;
            },
            395 => static function ($self, $stackPos) {
                 $self->semValue = Modifiers::READONLY;
            },
            396 => null,
            397 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)]);
            },
            398 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
            },
            399 => static function ($self, $stackPos) {
                 $self->semValue = new Node\VarLikeIdentifier(substr($self->semStack[$stackPos-(1-1)], 1), $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            400 => static function ($self, $stackPos) {
                 $self->semValue = new Node\PropertyItem($self->semStack[$stackPos-(1-1)], null, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            401 => static function ($self, $stackPos) {
                 $self->semValue = new Node\PropertyItem($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            402 => static function ($self, $stackPos) {
                 $self->semValue = [];
            },
            403 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)];
            },
            404 => static function ($self, $stackPos) {
                 $self->semValue = [];
            },
            405 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(3-2)]; $self->checkEmptyPropertyHookList($self->semStack[$stackPos-(3-2)], $stackPos-(3-1));
            },
            406 => static function ($self, $stackPos) {
                 $self->semValue = new Node\PropertyHook($self->semStack[$stackPos-(5-4)], $self->semStack[$stackPos-(5-5)], ['flags' => $self->semStack[$stackPos-(5-2)], 'byRef' => $self->semStack[$stackPos-(5-3)], 'params' => [], 'attrGroups' => $self->semStack[$stackPos-(5-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]));
            $self->checkPropertyHook($self->semValue, null);
            },
            407 => static function ($self, $stackPos) {
                 $self->semValue = new Node\PropertyHook($self->semStack[$stackPos-(8-4)], $self->semStack[$stackPos-(8-8)], ['flags' => $self->semStack[$stackPos-(8-2)], 'byRef' => $self->semStack[$stackPos-(8-3)], 'params' => $self->semStack[$stackPos-(8-6)], 'attrGroups' => $self->semStack[$stackPos-(8-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos]));
            $self->checkPropertyHook($self->semValue, $stackPos-(8-5));
            },
            408 => static function ($self, $stackPos) {
                 $self->semValue = null;
            },
            409 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(3-2)];
            },
            410 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(3-2)];
            },
            411 => static function ($self, $stackPos) {
                 $self->semValue = 0;
            },
            412 => static function ($self, $stackPos) {
                 $self->checkPropertyHookModifiers($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)], $stackPos-(2-2)); $self->semValue = $self->semStack[$stackPos-(2-1)] | $self->semStack[$stackPos-(2-2)];
            },
            413 => null,
            414 => null,
            415 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
            },
            416 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)]);
            },
            417 => static function ($self, $stackPos) {
                 $self->semValue = array();
            },
            418 => null,
            419 => null,
            420 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Assign($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            421 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Assign($self->fixupArrayDestructuring($self->semStack[$stackPos-(3-1)]), $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            422 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Assign($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            423 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\AssignRef($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            424 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\AssignRef($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            if (!$self->phpVersion->allowsAssignNewByReference()) {
                $self->emitError(new Error('Cannot assign new by reference', $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])));
            }

            },
            425 => null,
            426 => null,
            427 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\FuncCall(new Node\Name($self->semStack[$stackPos-(2-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)],  $self->tokenEndStack[$stackPos-(2-1)])), $self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            428 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Clone_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            429 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\AssignOp\Plus($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            430 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\AssignOp\Minus($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            431 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\AssignOp\Mul($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            432 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\AssignOp\Div($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            433 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\AssignOp\Concat($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            434 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\AssignOp\Mod($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            435 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\AssignOp\BitwiseAnd($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            436 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\AssignOp\BitwiseOr($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            437 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\AssignOp\BitwiseXor($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            438 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\AssignOp\ShiftLeft($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            439 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\AssignOp\ShiftRight($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            440 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\AssignOp\Pow($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            441 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\AssignOp\Coalesce($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            442 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\PostInc($self->semStack[$stackPos-(2-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            443 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\PreInc($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            444 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\PostDec($self->semStack[$stackPos-(2-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            445 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\PreDec($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            446 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\BooleanOr($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            447 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\BooleanAnd($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            448 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\LogicalOr($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            449 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\LogicalAnd($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            450 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\LogicalXor($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            451 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\BitwiseOr($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            452 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\BitwiseAnd($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            453 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\BitwiseAnd($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            454 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\BitwiseXor($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            455 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\Concat($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            456 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\Plus($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            457 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\Minus($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            458 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\Mul($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            459 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\Div($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            460 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\Mod($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            461 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\ShiftLeft($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            462 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\ShiftRight($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            463 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\Pow($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            464 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\UnaryPlus($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            465 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\UnaryMinus($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            466 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BooleanNot($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            467 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BitwiseNot($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            468 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\Identical($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            469 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\NotIdentical($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            470 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\Equal($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            471 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\NotEqual($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            472 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\Spaceship($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            473 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\Smaller($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            474 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\SmallerOrEqual($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            475 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\Greater($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            476 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\GreaterOrEqual($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            477 => static function ($self, $stackPos) {

          $self->semValue = new Expr\BinaryOp\Pipe($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
          $self->checkPipeOperatorParentheses($self->semStack[$stackPos-(3-3)]);

            },
            478 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Instanceof_($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            479 => static function ($self, $stackPos) {

          $self->semValue = $self->semStack[$stackPos-(3-2)];
          if ($self->semValue instanceof Expr\ArrowFunction) {
              $self->parenthesizedArrowFunctions->offsetSet($self->semValue);
          }

            },
            480 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Ternary($self->semStack[$stackPos-(5-1)], $self->semStack[$stackPos-(5-3)], $self->semStack[$stackPos-(5-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]));
            },
            481 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Ternary($self->semStack[$stackPos-(4-1)], null, $self->semStack[$stackPos-(4-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            482 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\Coalesce($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            483 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Isset_($self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            484 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Empty_($self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            485 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Include_($self->semStack[$stackPos-(2-2)], Expr\Include_::TYPE_INCLUDE, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            486 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Include_($self->semStack[$stackPos-(2-2)], Expr\Include_::TYPE_INCLUDE_ONCE, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            487 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Eval_($self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            488 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Include_($self->semStack[$stackPos-(2-2)], Expr\Include_::TYPE_REQUIRE, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            489 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Include_($self->semStack[$stackPos-(2-2)], Expr\Include_::TYPE_REQUIRE_ONCE, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            490 => static function ($self, $stackPos) {
                 $attrs = $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]);
            $attrs['kind'] = $self->getIntCastKind($self->semStack[$stackPos-(2-1)]);
            $self->semValue = new Expr\Cast\Int_($self->semStack[$stackPos-(2-2)], $attrs);
            },
            491 => static function ($self, $stackPos) {
                 $attrs = $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]);
            $attrs['kind'] = $self->getFloatCastKind($self->semStack[$stackPos-(2-1)]);
            $self->semValue = new Expr\Cast\Double($self->semStack[$stackPos-(2-2)], $attrs);
            },
            492 => static function ($self, $stackPos) {
                 $attrs = $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]);
            $attrs['kind'] = $self->getStringCastKind($self->semStack[$stackPos-(2-1)]);
            $self->semValue = new Expr\Cast\String_($self->semStack[$stackPos-(2-2)], $attrs);
            },
            493 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Cast\Array_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            494 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Cast\Object_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            495 => static function ($self, $stackPos) {
                 $attrs = $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]);
            $attrs['kind'] = $self->getBoolCastKind($self->semStack[$stackPos-(2-1)]);
            $self->semValue = new Expr\Cast\Bool_($self->semStack[$stackPos-(2-2)], $attrs);
            },
            496 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Cast\Unset_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            497 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Cast\Void_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            498 => static function ($self, $stackPos) {
                 $self->semValue = $self->createExitExpr($self->semStack[$stackPos-(2-1)], $stackPos-(2-1), $self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            499 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\ErrorSuppress($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            500 => null,
            501 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\ShellExec($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            502 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Print_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            503 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Yield_(null, null, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            504 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Yield_($self->semStack[$stackPos-(2-2)], null, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            505 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Yield_($self->semStack[$stackPos-(4-4)], $self->semStack[$stackPos-(4-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            506 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\YieldFrom($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            507 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Throw_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            508 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\ArrowFunction(['static' => false, 'byRef' => $self->semStack[$stackPos-(8-2)], 'params' => $self->semStack[$stackPos-(8-4)], 'returnType' => $self->semStack[$stackPos-(8-6)], 'expr' => $self->semStack[$stackPos-(8-8)], 'attrGroups' => []], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos]));
            },
            509 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\ArrowFunction(['static' => true, 'byRef' => $self->semStack[$stackPos-(9-3)], 'params' => $self->semStack[$stackPos-(9-5)], 'returnType' => $self->semStack[$stackPos-(9-7)], 'expr' => $self->semStack[$stackPos-(9-9)], 'attrGroups' => []], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos]));
            },
            510 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Closure(['static' => false, 'byRef' => $self->semStack[$stackPos-(8-2)], 'params' => $self->semStack[$stackPos-(8-4)], 'uses' => $self->semStack[$stackPos-(8-6)], 'returnType' => $self->semStack[$stackPos-(8-7)], 'stmts' => $self->semStack[$stackPos-(8-8)], 'attrGroups' => []], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos]));
            },
            511 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Closure(['static' => true, 'byRef' => $self->semStack[$stackPos-(9-3)], 'params' => $self->semStack[$stackPos-(9-5)], 'uses' => $self->semStack[$stackPos-(9-7)], 'returnType' => $self->semStack[$stackPos-(9-8)], 'stmts' => $self->semStack[$stackPos-(9-9)], 'attrGroups' => []], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos]));
            },
            512 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\ArrowFunction(['static' => false, 'byRef' => $self->semStack[$stackPos-(9-3)], 'params' => $self->semStack[$stackPos-(9-5)], 'returnType' => $self->semStack[$stackPos-(9-7)], 'expr' => $self->semStack[$stackPos-(9-9)], 'attrGroups' => $self->semStack[$stackPos-(9-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos]));
            },
            513 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\ArrowFunction(['static' => true, 'byRef' => $self->semStack[$stackPos-(10-4)], 'params' => $self->semStack[$stackPos-(10-6)], 'returnType' => $self->semStack[$stackPos-(10-8)], 'expr' => $self->semStack[$stackPos-(10-10)], 'attrGroups' => $self->semStack[$stackPos-(10-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(10-1)], $self->tokenEndStack[$stackPos]));
            },
            514 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Closure(['static' => false, 'byRef' => $self->semStack[$stackPos-(9-3)], 'params' => $self->semStack[$stackPos-(9-5)], 'uses' => $self->semStack[$stackPos-(9-7)], 'returnType' => $self->semStack[$stackPos-(9-8)], 'stmts' => $self->semStack[$stackPos-(9-9)], 'attrGroups' => $self->semStack[$stackPos-(9-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos]));
            },
            515 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Closure(['static' => true, 'byRef' => $self->semStack[$stackPos-(10-4)], 'params' => $self->semStack[$stackPos-(10-6)], 'uses' => $self->semStack[$stackPos-(10-8)], 'returnType' => $self->semStack[$stackPos-(10-9)], 'stmts' => $self->semStack[$stackPos-(10-10)], 'attrGroups' => $self->semStack[$stackPos-(10-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(10-1)], $self->tokenEndStack[$stackPos]));
            },
            516 => static function ($self, $stackPos) {
                 $self->semValue = array(new Stmt\Class_(null, ['type' => $self->semStack[$stackPos-(8-2)], 'extends' => $self->semStack[$stackPos-(8-4)], 'implements' => $self->semStack[$stackPos-(8-5)], 'stmts' => $self->semStack[$stackPos-(8-7)], 'attrGroups' => $self->semStack[$stackPos-(8-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos])), $self->semStack[$stackPos-(8-3)]);
            $self->checkClass($self->semValue[0], -1);
            },
            517 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\New_($self->semStack[$stackPos-(3-2)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            518 => static function ($self, $stackPos) {
                 list($class, $ctorArgs) = $self->semStack[$stackPos-(2-2)]; $self->semValue = new Expr\New_($class, $ctorArgs, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            519 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\New_($self->semStack[$stackPos-(2-2)], [], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            520 => null,
            521 => null,
            522 => static function ($self, $stackPos) {
                 $self->semValue = array();
            },
            523 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(4-3)];
            },
            524 => null,
            525 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)]);
            },
            526 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
            },
            527 => static function ($self, $stackPos) {
                 $self->semValue = new Node\ClosureUse($self->semStack[$stackPos-(2-2)], $self->semStack[$stackPos-(2-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            528 => static function ($self, $stackPos) {
                 $self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            529 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\FuncCall($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            530 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\FuncCall($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            531 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\FuncCall($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            532 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\StaticCall($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-3)], $self->semStack[$stackPos-(4-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            533 => static function ($self, $stackPos) {
                 $self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            534 => null,
            535 => static function ($self, $stackPos) {
                 $self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            536 => static function ($self, $stackPos) {
                 $self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            537 => static function ($self, $stackPos) {
                 $self->semValue = new Name\FullyQualified(substr($self->semStack[$stackPos-(1-1)], 1), $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            538 => static function ($self, $stackPos) {
                 $self->semValue = new Name\Relative(substr($self->semStack[$stackPos-(1-1)], 10), $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            539 => null,
            540 => null,
            541 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(3-2)];
            },
            542 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); $self->errorState = 2;
            },
            543 => null,
            544 => null,
            545 => static function ($self, $stackPos) {
                 $self->semValue = array();
            },
            546 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)]); foreach ($self->semValue as $s) { if ($s instanceof Node\InterpolatedStringPart) { $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '`', $self->phpVersion->supportsUnicodeEscapes()); } };
            },
            547 => static function ($self, $stackPos) {
                 foreach ($self->semStack[$stackPos-(1-1)] as $s) { if ($s instanceof Node\InterpolatedStringPart) { $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '`', $self->phpVersion->supportsUnicodeEscapes()); } }; $self->semValue = $self->semStack[$stackPos-(1-1)];
            },
            548 => static function ($self, $stackPos) {
                 $self->semValue = array();
            },
            549 => null,
            550 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\ConstFetch($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            551 => static function ($self, $stackPos) {
                 $self->semValue = new Scalar\MagicConst\Line($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            552 => static function ($self, $stackPos) {
                 $self->semValue = new Scalar\MagicConst\File($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            553 => static function ($self, $stackPos) {
                 $self->semValue = new Scalar\MagicConst\Dir($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            554 => static function ($self, $stackPos) {
                 $self->semValue = new Scalar\MagicConst\Class_($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            555 => static function ($self, $stackPos) {
                 $self->semValue = new Scalar\MagicConst\Trait_($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            556 => static function ($self, $stackPos) {
                 $self->semValue = new Scalar\MagicConst\Method($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            557 => static function ($self, $stackPos) {
                 $self->semValue = new Scalar\MagicConst\Function_($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            558 => static function ($self, $stackPos) {
                 $self->semValue = new Scalar\MagicConst\Namespace_($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            559 => static function ($self, $stackPos) {
                 $self->semValue = new Scalar\MagicConst\Property($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            560 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\ClassConstFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            561 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\ClassConstFetch($self->semStack[$stackPos-(5-1)], $self->semStack[$stackPos-(5-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]));
            },
            562 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\ClassConstFetch($self->semStack[$stackPos-(3-1)], new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos-(3-3)],  $self->tokenEndStack[$stackPos-(3-3)])), $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); $self->errorState = 2;
            },
            563 => static function ($self, $stackPos) {
                 $attrs = $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]); $attrs['kind'] = Expr\Array_::KIND_SHORT;
            $self->semValue = new Expr\Array_($self->semStack[$stackPos-(3-2)], $attrs);
            },
            564 => static function ($self, $stackPos) {
                 $attrs = $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]); $attrs['kind'] = Expr\Array_::KIND_LONG;
            $self->semValue = new Expr\Array_($self->semStack[$stackPos-(4-3)], $attrs);
            $self->createdArrays->offsetSet($self->semValue);
            },
            565 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(1-1)]; $self->createdArrays->offsetSet($self->semValue);
            },
            566 => static function ($self, $stackPos) {
                 $self->semValue = Scalar\String_::fromString($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]), $self->phpVersion->supportsUnicodeEscapes());
            },
            567 => static function ($self, $stackPos) {
                 $attrs = $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]); $attrs['kind'] = Scalar\String_::KIND_DOUBLE_QUOTED;
            foreach ($self->semStack[$stackPos-(3-2)] as $s) { if ($s instanceof Node\InterpolatedStringPart) { $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '"', $self->phpVersion->supportsUnicodeEscapes()); } }; $self->semValue = new Scalar\InterpolatedString($self->semStack[$stackPos-(3-2)], $attrs);
            },
            568 => static function ($self, $stackPos) {
                 $self->semValue = $self->parseLNumber($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]), $self->phpVersion->allowsInvalidOctals());
            },
            569 => static function ($self, $stackPos) {
                 $self->semValue = Scalar\Float_::fromString($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            570 => null,
            571 => null,
            572 => null,
            573 => static function ($self, $stackPos) {
                 $self->semValue = $self->parseDocString($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-2)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]), $self->getAttributes($self->tokenStartStack[$stackPos-(3-3)],  $self->tokenEndStack[$stackPos-(3-3)]), true);
            },
            574 => static function ($self, $stackPos) {
                 $self->semValue = $self->parseDocString($self->semStack[$stackPos-(2-1)], '', $self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]), $self->getAttributes($self->tokenStartStack[$stackPos-(2-2)],  $self->tokenEndStack[$stackPos-(2-2)]), true);
            },
            575 => static function ($self, $stackPos) {
                 $self->semValue = $self->parseDocString($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-2)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]), $self->getAttributes($self->tokenStartStack[$stackPos-(3-3)],  $self->tokenEndStack[$stackPos-(3-3)]), true);
            },
            576 => static function ($self, $stackPos) {
                 $self->semValue = null;
            },
            577 => null,
            578 => null,
            579 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(3-2)];
            },
            580 => null,
            581 => null,
            582 => null,
            583 => null,
            584 => null,
            585 => null,
            586 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(3-2)];
            },
            587 => null,
            588 => null,
            589 => null,
            590 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\ArrayDimFetch($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            591 => null,
            592 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\MethodCall($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-3)], $self->semStack[$stackPos-(4-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            593 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\NullsafeMethodCall($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-3)], $self->semStack[$stackPos-(4-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            594 => static function ($self, $stackPos) {
                 $self->semValue = null;
            },
            595 => null,
            596 => null,
            597 => null,
            598 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\PropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            599 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\NullsafePropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            600 => null,
            601 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Variable($self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            602 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Variable($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            603 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Variable(new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])), $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); $self->errorState = 2;
            },
            604 => static function ($self, $stackPos) {
                 $var = $self->semStack[$stackPos-(1-1)]->name; $self->semValue = \is_string($var) ? new Node\VarLikeIdentifier($var, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])) : $var;
            },
            605 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\StaticPropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            606 => null,
            607 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\ArrayDimFetch($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            608 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\PropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            609 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\NullsafePropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            610 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\StaticPropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            611 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\StaticPropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            612 => null,
            613 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(3-2)];
            },
            614 => null,
            615 => null,
            616 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(3-2)];
            },
            617 => null,
            618 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); $self->errorState = 2;
            },
            619 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\List_($self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); $self->semValue->setAttribute('kind', Expr\List_::KIND_LIST);
            $self->postprocessList($self->semValue);
            },
            620 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(1-1)]; $end = count($self->semValue)-1; if ($self->semValue[$end]->value instanceof Expr\Error) array_pop($self->semValue);
            },
            621 => null,
            622 => static function ($self, $stackPos) {
                 /* do nothing -- prevent default action of $$=$self->semStack[$1]. See $551. */
            },
            623 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
            },
            624 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)]);
            },
            625 => static function ($self, $stackPos) {
                 $self->semValue = new Node\ArrayItem($self->semStack[$stackPos-(1-1)], null, false, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            626 => static function ($self, $stackPos) {
                 $self->semValue = new Node\ArrayItem($self->semStack[$stackPos-(2-2)], null, true, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            627 => static function ($self, $stackPos) {
                 $self->semValue = new Node\ArrayItem($self->semStack[$stackPos-(1-1)], null, false, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            628 => static function ($self, $stackPos) {
                 $self->semValue = new Node\ArrayItem($self->semStack[$stackPos-(3-3)], $self->semStack[$stackPos-(3-1)], false, $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            629 => static function ($self, $stackPos) {
                 $self->semValue = new Node\ArrayItem($self->semStack[$stackPos-(4-4)], $self->semStack[$stackPos-(4-1)], true, $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            630 => static function ($self, $stackPos) {
                 $self->semValue = new Node\ArrayItem($self->semStack[$stackPos-(3-3)], $self->semStack[$stackPos-(3-1)], false, $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            631 => static function ($self, $stackPos) {
                 $self->semValue = new Node\ArrayItem($self->semStack[$stackPos-(2-2)], null, false, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]), true);
            },
            632 => static function ($self, $stackPos) {
                 /* Create an Error node now to remember the position. We'll later either report an error,
             or convert this into a null element, depending on whether this is a creation or destructuring context. */
          $attrs = $self->createEmptyElemAttributes($self->tokenPos);
          $self->semValue = new Node\ArrayItem(new Expr\Error($attrs), null, false, $attrs);
            },
            633 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)];
            },
            634 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)];
            },
            635 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)]);
            },
            636 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)]);
            },
            637 => static function ($self, $stackPos) {
                 $attrs = $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]); $attrs['rawValue'] = $self->semStack[$stackPos-(1-1)]; $self->semValue = new Node\InterpolatedStringPart($self->semStack[$stackPos-(1-1)], $attrs);
            },
            638 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Variable($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            639 => null,
            640 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\ArrayDimFetch($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            641 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\PropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            642 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\NullsafePropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            643 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Variable($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            644 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Variable($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            645 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\ArrayDimFetch($self->semStack[$stackPos-(6-2)], $self->semStack[$stackPos-(6-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos]));
            },
            646 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(3-2)];
            },
            647 => static function ($self, $stackPos) {
                 $self->semValue = new Scalar\String_($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            648 => static function ($self, $stackPos) {
                 $self->semValue = $self->parseNumString($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            649 => static function ($self, $stackPos) {
                 $self->semValue = $self->parseNumString('-' . $self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            650 => null,
        ];
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Parser;

use PhpParser\Error;
use PhpParser\Modifiers;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Name;
use PhpParser\Node\Scalar;
use PhpParser\Node\Stmt;

/* This is an automatically GENERATED file, which should not be manually edited.
 * Instead edit one of the following:
 *  * the grammar file grammar/php.y
 *  * the skeleton file grammar/parser.template
 *  * the preprocessing script grammar/rebuildParsers.php
 */
class Php7 extends \PhpParser\ParserAbstract
{
    public const YYERRTOK = 256;
    public const T_VOID_CAST = 257;
    public const T_THROW = 258;
    public const T_INCLUDE = 259;
    public const T_INCLUDE_ONCE = 260;
    public const T_EVAL = 261;
    public const T_REQUIRE = 262;
    public const T_REQUIRE_ONCE = 263;
    public const T_LOGICAL_OR = 264;
    public const T_LOGICAL_XOR = 265;
    public const T_LOGICAL_AND = 266;
    public const T_PRINT = 267;
    public const T_YIELD = 268;
    public const T_DOUBLE_ARROW = 269;
    public const T_YIELD_FROM = 270;
    public const T_PLUS_EQUAL = 271;
    public const T_MINUS_EQUAL = 272;
    public const T_MUL_EQUAL = 273;
    public const T_DIV_EQUAL = 274;
    public const T_CONCAT_EQUAL = 275;
    public const T_MOD_EQUAL = 276;
    public const T_AND_EQUAL = 277;
    public const T_OR_EQUAL = 278;
    public const T_XOR_EQUAL = 279;
    public const T_SL_EQUAL = 280;
    public const T_SR_EQUAL = 281;
    public const T_POW_EQUAL = 282;
    public const T_COALESCE_EQUAL = 283;
    public const T_COALESCE = 284;
    public const T_BOOLEAN_OR = 285;
    public const T_BOOLEAN_AND = 286;
    public const T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG = 287;
    public const T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG = 288;
    public const T_IS_EQUAL = 289;
    public const T_IS_NOT_EQUAL = 290;
    public const T_IS_IDENTICAL = 291;
    public const T_IS_NOT_IDENTICAL = 292;
    public const T_SPACESHIP = 293;
    public const T_IS_SMALLER_OR_EQUAL = 294;
    public const T_IS_GREATER_OR_EQUAL = 295;
    public const T_SL = 296;
    public const T_SR = 297;
    public const T_INSTANCEOF = 298;
    public const T_INC = 299;
    public const T_DEC = 300;
    public const T_INT_CAST = 301;
    public const T_DOUBLE_CAST = 302;
    public const T_STRING_CAST = 303;
    public const T_ARRAY_CAST = 304;
    public const T_OBJECT_CAST = 305;
    public const T_BOOL_CAST = 306;
    public const T_UNSET_CAST = 307;
    public const T_POW = 308;
    public const T_NEW = 309;
    public const T_CLONE = 310;
    public const T_EXIT = 311;
    public const T_IF = 312;
    public const T_ELSEIF = 313;
    public const T_ELSE = 314;
    public const T_ENDIF = 315;
    public const T_LNUMBER = 316;
    public const T_DNUMBER = 317;
    public const T_STRING = 318;
    public const T_STRING_VARNAME = 319;
    public const T_VARIABLE = 320;
    public const T_NUM_STRING = 321;
    public const T_INLINE_HTML = 322;
    public const T_ENCAPSED_AND_WHITESPACE = 323;
    public const T_CONSTANT_ENCAPSED_STRING = 324;
    public const T_ECHO = 325;
    public const T_DO = 326;
    public const T_WHILE = 327;
    public const T_ENDWHILE = 328;
    public const T_FOR = 329;
    public const T_ENDFOR = 330;
    public const T_FOREACH = 331;
    public const T_ENDFOREACH = 332;
    public const T_DECLARE = 333;
    public const T_ENDDECLARE = 334;
    public const T_AS = 335;
    public const T_SWITCH = 336;
    public const T_MATCH = 337;
    public const T_ENDSWITCH = 338;
    public const T_CASE = 339;
    public const T_DEFAULT = 340;
    public const T_BREAK = 341;
    public const T_CONTINUE = 342;
    public const T_GOTO = 343;
    public const T_FUNCTION = 344;
    public const T_FN = 345;
    public const T_CONST = 346;
    public const T_RETURN = 347;
    public const T_TRY = 348;
    public const T_CATCH = 349;
    public const T_FINALLY = 350;
    public const T_USE = 351;
    public const T_INSTEADOF = 352;
    public const T_GLOBAL = 353;
    public const T_STATIC = 354;
    public const T_ABSTRACT = 355;
    public const T_FINAL = 356;
    public const T_PRIVATE = 357;
    public const T_PROTECTED = 358;
    public const T_PUBLIC = 359;
    public const T_READONLY = 360;
    public const T_PUBLIC_SET = 361;
    public const T_PROTECTED_SET = 362;
    public const T_PRIVATE_SET = 363;
    public const T_VAR = 364;
    public const T_UNSET = 365;
    public const T_ISSET = 366;
    public const T_EMPTY = 367;
    public const T_HALT_COMPILER = 368;
    public const T_CLASS = 369;
    public const T_TRAIT = 370;
    public const T_INTERFACE = 371;
    public const T_ENUM = 372;
    public const T_EXTENDS = 373;
    public const T_IMPLEMENTS = 374;
    public const T_OBJECT_OPERATOR = 375;
    public const T_NULLSAFE_OBJECT_OPERATOR = 376;
    public const T_LIST = 377;
    public const T_ARRAY = 378;
    public const T_CALLABLE = 379;
    public const T_CLASS_C = 380;
    public const T_TRAIT_C = 381;
    public const T_METHOD_C = 382;
    public const T_FUNC_C = 383;
    public const T_PROPERTY_C = 384;
    public const T_LINE = 385;
    public const T_FILE = 386;
    public const T_START_HEREDOC = 387;
    public const T_END_HEREDOC = 388;
    public const T_DOLLAR_OPEN_CURLY_BRACES = 389;
    public const T_CURLY_OPEN = 390;
    public const T_PAAMAYIM_NEKUDOTAYIM = 391;
    public const T_NAMESPACE = 392;
    public const T_NS_C = 393;
    public const T_DIR = 394;
    public const T_NS_SEPARATOR = 395;
    public const T_ELLIPSIS = 396;
    public const T_NAME_FULLY_QUALIFIED = 397;
    public const T_NAME_QUALIFIED = 398;
    public const T_NAME_RELATIVE = 399;
    public const T_ATTRIBUTE = 400;

    protected int $tokenToSymbolMapSize = 401;
    protected int $actionTableSize = 1620;
    protected int $gotoTableSize = 696;

    protected int $invalidSymbol = 173;
    protected int $errorSymbol = 1;
    protected int $defaultAction = -32766;
    protected int $unexpectedTokenRule = 32767;

    protected int $YY2TBLSTATE = 444;
    protected int $numNonLeafStates = 751;

    protected array $symbolToName = array(
        "EOF",
        "error",
        "T_VOID_CAST",
        "T_THROW",
        "T_INCLUDE",
        "T_INCLUDE_ONCE",
        "T_EVAL",
        "T_REQUIRE",
        "T_REQUIRE_ONCE",
        "','",
        "T_LOGICAL_OR",
        "T_LOGICAL_XOR",
        "T_LOGICAL_AND",
        "T_PRINT",
        "T_YIELD",
        "T_DOUBLE_ARROW",
        "T_YIELD_FROM",
        "'='",
        "T_PLUS_EQUAL",
        "T_MINUS_EQUAL",
        "T_MUL_EQUAL",
        "T_DIV_EQUAL",
        "T_CONCAT_EQUAL",
        "T_MOD_EQUAL",
        "T_AND_EQUAL",
        "T_OR_EQUAL",
        "T_XOR_EQUAL",
        "T_SL_EQUAL",
        "T_SR_EQUAL",
        "T_POW_EQUAL",
        "T_COALESCE_EQUAL",
        "'?'",
        "':'",
        "T_COALESCE",
        "T_BOOLEAN_OR",
        "T_BOOLEAN_AND",
        "'|'",
        "'^'",
        "T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG",
        "T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG",
        "T_IS_EQUAL",
        "T_IS_NOT_EQUAL",
        "T_IS_IDENTICAL",
        "T_IS_NOT_IDENTICAL",
        "T_SPACESHIP",
        "'<'",
        "T_IS_SMALLER_OR_EQUAL",
        "'>'",
        "T_IS_GREATER_OR_EQUAL",
        "T_SL",
        "T_SR",
        "'+'",
        "'-'",
        "'.'",
        "'*'",
        "'/'",
        "'%'",
        "'!'",
        "T_INSTANCEOF",
        "'~'",
        "T_INC",
        "T_DEC",
        "T_INT_CAST",
        "T_DOUBLE_CAST",
        "T_STRING_CAST",
        "T_ARRAY_CAST",
        "T_OBJECT_CAST",
        "T_BOOL_CAST",
        "T_UNSET_CAST",
        "'@'",
        "T_POW",
        "'['",
        "T_NEW",
        "T_CLONE",
        "T_EXIT",
        "T_IF",
        "T_ELSEIF",
        "T_ELSE",
        "T_ENDIF",
        "T_LNUMBER",
        "T_DNUMBER",
        "T_STRING",
        "T_STRING_VARNAME",
        "T_VARIABLE",
        "T_NUM_STRING",
        "T_INLINE_HTML",
        "T_ENCAPSED_AND_WHITESPACE",
        "T_CONSTANT_ENCAPSED_STRING",
        "T_ECHO",
        "T_DO",
        "T_WHILE",
        "T_ENDWHILE",
        "T_FOR",
        "T_ENDFOR",
        "T_FOREACH",
        "T_ENDFOREACH",
        "T_DECLARE",
        "T_ENDDECLARE",
        "T_AS",
        "T_SWITCH",
        "T_MATCH",
        "T_ENDSWITCH",
        "T_CASE",
        "T_DEFAULT",
        "T_BREAK",
        "T_CONTINUE",
        "T_GOTO",
        "T_FUNCTION",
        "T_FN",
        "T_CONST",
        "T_RETURN",
        "T_TRY",
        "T_CATCH",
        "T_FINALLY",
        "T_USE",
        "T_INSTEADOF",
        "T_GLOBAL",
        "T_STATIC",
        "T_ABSTRACT",
        "T_FINAL",
        "T_PRIVATE",
        "T_PROTECTED",
        "T_PUBLIC",
        "T_READONLY",
        "T_PUBLIC_SET",
        "T_PROTECTED_SET",
        "T_PRIVATE_SET",
        "T_VAR",
        "T_UNSET",
        "T_ISSET",
        "T_EMPTY",
        "T_HALT_COMPILER",
        "T_CLASS",
        "T_TRAIT",
        "T_INTERFACE",
        "T_ENUM",
        "T_EXTENDS",
        "T_IMPLEMENTS",
        "T_OBJECT_OPERATOR",
        "T_NULLSAFE_OBJECT_OPERATOR",
        "T_LIST",
        "T_ARRAY",
        "T_CALLABLE",
        "T_CLASS_C",
        "T_TRAIT_C",
        "T_METHOD_C",
        "T_FUNC_C",
        "T_PROPERTY_C",
        "T_LINE",
        "T_FILE",
        "T_START_HEREDOC",
        "T_END_HEREDOC",
        "T_DOLLAR_OPEN_CURLY_BRACES",
        "T_CURLY_OPEN",
        "T_PAAMAYIM_NEKUDOTAYIM",
        "T_NAMESPACE",
        "T_NS_C",
        "T_DIR",
        "T_NS_SEPARATOR",
        "T_ELLIPSIS",
        "T_NAME_FULLY_QUALIFIED",
        "T_NAME_QUALIFIED",
        "T_NAME_RELATIVE",
        "T_ATTRIBUTE",
        "';'",
        "']'",
        "'('",
        "')'",
        "'{'",
        "'}'",
        "'`'",
        "'\"'",
        "'$'"
    );

    protected array $tokenToSymbol = array(
            0,  173,  173,  173,  173,  173,  173,  173,  173,  173,
          173,  173,  173,  173,  173,  173,  173,  173,  173,  173,
          173,  173,  173,  173,  173,  173,  173,  173,  173,  173,
          173,  173,  173,   57,  171,  173,  172,   56,  173,  173,
          166,  167,   54,   51,    9,   52,   53,   55,  173,  173,
          173,  173,  173,  173,  173,  173,  173,  173,   32,  164,
           45,   17,   47,   31,   69,  173,  173,  173,  173,  173,
          173,  173,  173,  173,  173,  173,  173,  173,  173,  173,
          173,  173,  173,  173,  173,  173,  173,  173,  173,  173,
          173,   71,  173,  165,   37,  173,  170,  173,  173,  173,
          173,  173,  173,  173,  173,  173,  173,  173,  173,  173,
          173,  173,  173,  173,  173,  173,  173,  173,  173,  173,
          173,  173,  173,  168,   36,  169,   59,  173,  173,  173,
          173,  173,  173,  173,  173,  173,  173,  173,  173,  173,
          173,  173,  173,  173,  173,  173,  173,  173,  173,  173,
          173,  173,  173,  173,  173,  173,  173,  173,  173,  173,
          173,  173,  173,  173,  173,  173,  173,  173,  173,  173,
          173,  173,  173,  173,  173,  173,  173,  173,  173,  173,
          173,  173,  173,  173,  173,  173,  173,  173,  173,  173,
          173,  173,  173,  173,  173,  173,  173,  173,  173,  173,
          173,  173,  173,  173,  173,  173,  173,  173,  173,  173,
          173,  173,  173,  173,  173,  173,  173,  173,  173,  173,
          173,  173,  173,  173,  173,  173,  173,  173,  173,  173,
          173,  173,  173,  173,  173,  173,  173,  173,  173,  173,
          173,  173,  173,  173,  173,  173,  173,  173,  173,  173,
          173,  173,  173,  173,  173,  173,    1,    2,    3,    4,
            5,    6,    7,    8,   10,   11,   12,   13,   14,   15,
           16,   18,   19,   20,   21,   22,   23,   24,   25,   26,
           27,   28,   29,   30,   33,   34,   35,   38,   39,   40,
           41,   42,   43,   44,   46,   48,   49,   50,   58,   60,
           61,   62,   63,   64,   65,   66,   67,   68,   70,   72,
           73,   74,   75,   76,   77,   78,   79,   80,   81,   82,
           83,   84,   85,   86,   87,   88,   89,   90,   91,   92,
           93,   94,   95,   96,   97,   98,   99,  100,  101,  102,
          103,  104,  105,  106,  107,  108,  109,  110,  111,  112,
          113,  114,  115,  116,  117,  118,  119,  120,  121,  122,
          123,  124,  125,  126,  127,  128,  129,  130,  131,  132,
          133,  134,  135,  136,  137,  138,  139,  140,  141,  142,
          143,  144,  145,  146,  147,  148,  149,  150,  151,  152,
          153,  154,  155,  156,  157,  158,  159,  160,  161,  162,
          163
    );

    protected array $action = array(
          134,  135,  136,  574,  137,  138, 1046,  763,  764,  765,
          139,   41,  387,  388,  447,  336,-32766,-32766,-32766, -628,
          838,  494,  432,    0,  875, -625,  876, -628, 1092, 1352,
        -32766,-32766,-32766, -625, -338,  757,  756,-32766,    4,-32766,
        -32766,-32766,-32766,-32766,-32766,-32766,-32767,-32767,-32767,-32767,
        -32767,-32766, -195,-32766,-32766,  766, 1142, 1143, 1144, 1138,
         1137, 1136, 1145, 1139, 1140, 1141,  387,  388,  305,  271,
           54,  390,  770,  771,  772,  773,  432,  846,  433,-32766,
        -32766,-32766,  480,  481,  297,  827,  774,  775,  776,  777,
          778,  779,  780,  781,  782,  783,  803,  575,  804,  805,
          806,  807,  795,  796,  352,  353,  798,  799,  784,  785,
          786,  788,  789,  790,  363,  830,  831,  832,  833,  834,
          576, -194,  305,  849,  791,  792,  577,  578,    5,  815,
          813,  814,  826,  810,  811, 1310,    3,  579,  580,  809,
          581,  582,  583,  584, -581,  585,  586,  757,  756,  109,
          110,  111,  812,  587,  588,  626,  140,  495,  134,  135,
          136,  574,  137,  138, 1081,  763,  764,  765,  139,   41,
        -32766,   29, 1043, 1038, 1037, 1036, 1042, 1039, 1040, 1041,
        -32766,-32766,-32766, -628, 1397, -628, 1092, 1398,  243, -625,
         1283, -625, -338,  757,  756, 1055,   87,-32766,-32766,-32766,
          335,-32766,  954,-32766,-32766,-32766,-32766,-32766,-32766,-32766,
         -195, -581, -581,  766,-32766,-32766,-32766,  847,-32766,  296,
        -32766,-32766,-32766,-32766,-32766, 1368,  429,  271,   54,  390,
          770,  771,  772,  773,   38,-32766,  433,-32766,-32766,-32766,
        -32766, -581,  620,  827,  774,  775,  776,  777,  778,  779,
          780,  781,  782,  783,  803,  575,  804,  805,  806,  807,
          795,  796,  352,  353,  798,  799,  784,  785,  786,  788,
          789,  790,  363,  830,  831,  832,  833,  834,  576, -194,
         -579,  141,  791,  792,  577,  578,  848,  815,  813,  814,
          826,  810,  811,  433,  302,  579,  580,  809,  581,  582,
          583,  584,  145,  585,  586,  838,  335,  469,  470,  471,
          812,  587,  588,  840,  140,  843,  134,  135,  136,  574,
          137,  138, 1079,  763,  764,  765,  139,   41, 1142, 1143,
         1144, 1138, 1137, 1136, 1145, 1139, 1140, 1141,  149,  242,
         -275,-32766,-32766,-32766, 1092,   27, 1283, -579, -579, 1078,
          256,  757,  756,-32767,-32767,-32767,-32767,  107,  108,  109,
          110,  111,-32766, -579,-32766,-32766,-32766,  316,  500,  737,
         1131,  766, 1285, 1284, 1286, -585, 1372, -579, -382,  293,
         -382, 1117,  429, 1005, 1371,  271,   54,  390,  770,  771,
          772,  773,  146,  842,  433,  844,  335,  757,  756,   52,
          724,  827,  774,  775,  776,  777,  778,  779,  780,  781,
          782,  783,  803,  575,  804,  805,  806,  807,  795,  796,
          352,  353,  798,  799,  784,  785,  786,  788,  789,  790,
          363,  830,  831,  832,  833,  834,  576, -577,  320, -578,
          791,  792,  577,  578, -576,  815,  813,  814,  826,  810,
          811, 1094,  332,  579,  580,  809,  581,  582,  583,  584,
          -85,  585,  586,  958,  957, 1052,  306,  307,  812,  587,
          588,  838,  140,  570,  134,  135,  136,  574,  137,  138,
         1083,  763,  764,  765,  139,   41,  348,  112,  113,  114,
         1055,  281,-32766,-32766,-32766, 1283,  131,-32766,-32766,-32766,
          349, 1283, 1092,  115, -577, -577, -578, -578,-32766,  757,
          756, -576, -576,  956,  289, 1006,  974,  975,-32766, 1055,
         -577,  976, -578,-32766,-32766,-32766,  -85, -576,  132,  766,
          970,  429, -584, 1055, -577,  377, -578,  429, 1322, -583,
          381, -576,  397,  271,   54,  390,  770,  771,  772,  773,
         1116,  399,  433,   10,  749,  451,  875,  296,  876,  827,
          774,  775,  776,  777,  778,  779,  780,  781,  782,  783,
          803,  575,  804,  805,  806,  807,  795,  796,  352,  353,
          798,  799,  784,  785,  786,  788,  789,  790,  363,  830,
          831,  832,  833,  834,  576,  -50,  669,   23,  791,  792,
          577,  578,-32766,  815,  813,  814,  826,  810,  811,  452,
         -575,  579,  580,  809,  581,  582,  583,  584,  847,  585,
          586, 1387,   88,   89,   90,  144,  812,  587,  588,  453,
          140,  787,  758,  759,  760,  761,  762,  153,  763,  764,
          765,  800,  801,   40, 1283,   91,   92,   93,   94,   95,
           96,   97,   98,   99,  100,  101,  102,  103,  104,  105,
          106,  107,  108,  109,  110,  111,  112,  113,  114, 1052,
          281,-32766, 1283,  603, 1283,  389,  388, -575, -575, -619,
          429, -619,  115,  688,  689,  432,  766,   39,  253,  152,
          415,  385,  386, -575, 1055,-32766,  297, 1290,  660,  661,
          767,  768,  769,  770,  771,  772,  773, -575,  429,  836,
          429,  391,  392,   53,  454,  853,  827,  774,  775,  776,
          777,  778,  779,  780,  781,  782,  783,  803,  825,  804,
          805,  806,  807,  795,  796,  797,  824,  798,  799,  784,
          785,  786,  788,  789,  790,  829,  830,  831,  832,  833,
          834,  835,  154,  156,  157,  791,  792,  793,  794, 1283,
          815,  813,  814,  826,  810,  811,  158,  -78,  802,  808,
          809,  816,  817,  819,  818,  293,  820,  821,  160,  847,
          308,  309,-32766,  812,  823,  822,   55,   56,   57,   58,
          526,   59,   60,  -58,   36, 1281,  380,   61,   62,  -57,
           63,  -87,   31,  272,  129,  130,  142, 1055,  143,  148,
          133,  161,  162,  163,  847,  164,  757,  756, 1314, 1054,
          165, -309,  -84,  281, 1283,  702,  115,  698,  150,  -78,
          847,  296,  -73,  -72,  -71,   64,   65,-32766, 1285, 1284,
         1286,   66,  -70,   67,  250,  251,   68,   69,   70,   71,
           72,   73,   74,   75, 1276,   31,  272,   48,  449, 1283,
          429,  703,   80, 1316, 1317,  528,  -69,  847,  335, 1052,
          -68, 1314,  542,  -67, 1302, 1303, 1304, 1305, 1307, 1299,
         1300,  -66,  -65,  933,  534,  704,  705, 1306, 1301,  -46,
          -18,  384,  383,   42, 1055,  429,  280,   78,  290,  738,
          741,  347,  331,  335, 1285, 1284, 1286, 1276,  932,    2,
          289,  151,  294,  538,  539,  295, -305,  285,  289,  286,
          291,  292,  341,  838,  541,  542,  298, 1302, 1303, 1304,
         1305, 1307, 1299, 1300,  304,  299,  713,   79,  950, 1149,
         1306, 1301,  590, 1399, 1285, 1284, 1286,  305,   -4,  933,
           76,-32766,   13,  670,  330,  331,  335,  527, -154, -154,
         -154,  477,  715,  313,  971,  691,  310,  923,  675,  319,
           46,   22,  529, -154,  530, -154,  531, -154,  532, -154,
          311,  533, 1321, -541,  596,  505,   49,   50,  455,  382,
          676,  692,   51,  535,-32766, 1323,  952,  624,  375, -531,
          974,  975, -613,  305,  933,  536,  658,  846,    0,    0,
          537, 1311,    0,  909,  970, -110, -110, -110, -278,    0,
            0,    0,    0,  527,   44,    0,   11,   30,    0, 1290,
            0,    0,  379,  923,    0,    0,   46,   22,  529,   45,
          530,    0,  531,    0,  532,    0,  935,  533,    0,    0,
          722, -154,   49,   50,  455,  382,  858,  445,   51,  535,
          -16, 1338,  303, 1356,  375,  746,  974,  975,  747,  866,
          914,  536, 1015,  992,  999,  933,  537,  989,  527,  540,
          970, -110, -110, -110, 1000,  912,  987, 1120,  923, 1123,
         1124,   46,   22,  529, 1121,  530, 1122,  531, 1128,  532,
         1390,  663,  533,   21, -253, -253, -253,   49,   50,  455,
          382,  847,  935,   51,  535, -612,  722,   -4, -611,  375,
         -585,  974,  975, -584, -583, -582,  536, -525,    1,   32,
           33,  537,   43,   47,  909,  970, -110, -110, -110,   77,
           81,   82,   83,   84,   85,   86, -110, -110,  147,  527,
         1115, -110,  155,  159,  248,  337,  364,  365,  366,  923,
         -110,  367,   46,   22,  529,  368,  530,  935,  531,-32766,
          532,  722, -253,  533,  369, -252, -252, -252,   49,   50,
          455,  382,  370,  371,   51,  535,  372,  373,  374,  376,
          375,  305,  974,  975,   80,  757,  756,  536,  446,  569,
          335, -276,  537, -275, 1290,  909,  970, -110, -110, -110,
           35,  116,  117,  118,  119,  120,  121,  122,  123,  124,
          125,  126,  127,  128,   15,   16,   17,   18,   31,  273,
           20,  413,  496,  497,  504,  507,  508,  509,  935,  510,
          847,   37,  722, -252, 1314,  514,  515,  516,  524,  601,
          933, -110,  708, 1191, 1193, 1231, -110, 1312, -110, 1082,
         1080, 1061, 1271,  312, -575, 1057, -110, -110, -110, -110,
         -110, -110, -110, -110, -110, -110, -110, -280, -102,   14,
         1276,  727,   19,   24,  302,  412,  847,  615,  621,  649,
          714, 1235, 1289, 1232,  933, 1369,  414,  541,  542,  329,
         1302, 1303, 1304, 1305, 1307, 1299, 1300,  378,  723,  726,
          730,  731,  733, 1306, 1301,  734,  735, 1315, -357,  736,
          740, -110, -110,   78,  527,  728, -110,  933,  331,  335,
          725, -575, -575,  418,  923, -110,  743,   46,   22,  529,
          910,  530, 1394,  531,-32766,  532, 1396, -575,  533,  869,
          868,  964, 1007,   49,   50,  455, 1395,  963,  729,   51,
          535, -575, 1249,  961,  962,  375,  305,  965,  527,   80,
         1264,  933,  335,  943,  953,  335,  941,  537,  923,  997,
          998,   46,   22,  529,  647,  530, 1393,  531, 1350,  532,
         1339, 1357,  533, 1366,    0,    0,    0,   49,   50,  455,
            0,  527,  732,   51,  535,    0,    0,    0,  933,  375,
            0,  923,    0,  935,   46,   22,  529,  722,  530,    0,
          531,  537,  532,    0,    0,  533,    0,    0,    0,    0,
           49,   50,  455,    0,    0,    0,   51,  535,    0,  739,
            0,    0,  375,    0,    0,  527,    0,    0,    0,    0,
            0,    0,  933,    0,  537,  923,    0,  935,   46,   22,
          529,  722,  530,    0,  531,    0,  532,    0,    0,  533,
            0,    0,    0,    0,   49,   50,  455,    0,    0,    0,
           51,  535,  527,    0,    0,    0,  375,    0,    0,    0,
          991,    0,  923,    0,  722,   46,   22,  529,  537,  530,
            0,  531,    0,  532,    0,    0,  533,    0,   31,  273,
            0,   49,   50,  455,    0,    0,    0,   51,  535,    0,
          847,    0,    0,  375, 1314,    0,  527,    0,    0,    0,
            0,    0,    0,    0,  935,  537,  923,    0,  722,   46,
           22,  529,    0,  530,    0,  531,    0,  532,    0,    0,
          533,    0,    0,    0,    0,   49,   50,  455,    0,    0,
         1276,   51,  535,    0,    0,    0,    0,  375,    0,    0,
            0,  935,    0,    0,    0,  722,    0,    0,  542,  537,
         1302, 1303, 1304, 1305, 1307, 1299, 1300,    0,    0,    0,
            0,    0,    0, 1306, 1301,    0,    0,    0,    0,    0,
            0,    0,    0,   78,    0,    0,    0,    0,  331,  335,
            0,    0,    0,    0,    0,  935,    0,    0,    0,  722
    );

    protected array $actionCheck = array(
            3,    4,    5,    6,    7,    8,    1,   10,   11,   12,
           13,   14,  107,  108,  109,   71,   10,   11,   12,    1,
           81,   32,  117,    0,  107,    1,  109,    9,   31,    1,
           10,   11,   12,    9,    9,   38,   39,   31,    9,   33,
           34,   35,   36,   37,   38,   39,   40,   41,   42,   43,
           44,   31,    9,   33,   34,   58,  117,  118,  119,  120,
          121,  122,  123,  124,  125,  126,  107,  108,  163,   72,
           73,   74,   75,   76,   77,   78,  117,  160,   81,   10,
           11,   12,  138,  139,   31,   88,   89,   90,   91,   92,
           93,   94,   95,   96,   97,   98,   99,  100,  101,  102,
          103,  104,  105,  106,  107,  108,  109,  110,  111,  112,
          113,  114,  115,  116,  117,  118,  119,  120,  121,  122,
          123,    9,  163,    1,  127,  128,  129,  130,    9,  132,
          133,  134,  135,  136,  137,    1,    9,  140,  141,  142,
          143,  144,  145,  146,   71,  148,  149,   38,   39,   51,
           52,   53,  155,  156,  157,   52,  159,  168,    3,    4,
            5,    6,    7,    8,  167,   10,   11,   12,   13,   14,
          117,    9,  119,  120,  121,  122,  123,  124,  125,  126,
           10,   11,   12,  165,   81,  167,   31,   84,   15,  165,
           81,  167,  167,   38,   39,  142,  168,   10,   11,   12,
          172,   31,    1,   33,   34,   35,   36,   37,   38,   39,
          167,  138,  139,   58,   10,   11,   12,   83,   31,  166,
           33,   34,   35,   36,   37,    1,  117,   72,   73,   74,
           75,   76,   77,   78,    9,   31,   81,   33,   34,   35,
           36,  168,    1,   88,   89,   90,   91,   92,   93,   94,
           95,   96,   97,   98,   99,  100,  101,  102,  103,  104,
          105,  106,  107,  108,  109,  110,  111,  112,  113,  114,
          115,  116,  117,  118,  119,  120,  121,  122,  123,  167,
           71,    9,  127,  128,  129,  130,  164,  132,  133,  134,
          135,  136,  137,   81,  167,  140,  141,  142,  143,  144,
          145,  146,  168,  148,  149,   81,  172,  133,  134,  135,
          155,  156,  157,   81,  159,   81,    3,    4,    5,    6,
            7,    8,  167,   10,   11,   12,   13,   14,  117,  118,
          119,  120,  121,  122,  123,  124,  125,  126,    9,   98,
          167,   10,   11,   12,   31,  102,   81,  138,  139,    1,
            9,   38,   39,   45,   46,   47,   48,   49,   50,   51,
           52,   53,   31,  154,   33,   34,   35,    9,  103,  168,
          127,   58,  160,  161,  162,  166,    1,  168,  107,   31,
          109,  169,  117,   32,    9,   72,   73,   74,   75,   76,
           77,   78,  168,  161,   81,  161,  172,   38,   39,   71,
          168,   88,   89,   90,   91,   92,   93,   94,   95,   96,
           97,   98,   99,  100,  101,  102,  103,  104,  105,  106,
          107,  108,  109,  110,  111,  112,  113,  114,  115,  116,
          117,  118,  119,  120,  121,  122,  123,   71,    9,   71,
          127,  128,  129,  130,   71,  132,  133,  134,  135,  136,
          137,   31,    9,  140,  141,  142,  143,  144,  145,  146,
           32,  148,  149,   73,   74,  117,  138,  139,  155,  156,
          157,   81,  159,   86,    3,    4,    5,    6,    7,    8,
          167,   10,   11,   12,   13,   14,    9,   54,   55,   56,
          142,   58,   10,   11,  117,   81,  168,   10,   11,   12,
            9,   81,   31,   70,  138,  139,  138,  139,  117,   38,
           39,  138,  139,  123,  166,  164,  118,  119,   31,  142,
          154,  123,  154,   10,   11,   12,   98,  154,   15,   58,
          132,  117,  166,  142,  168,    9,  168,  117,  151,  166,
            9,  168,    9,   72,   73,   74,   75,   76,   77,   78,
          164,  107,   81,  109,  168,    9,  107,  166,  109,   88,
           89,   90,   91,   92,   93,   94,   95,   96,   97,   98,
           99,  100,  101,  102,  103,  104,  105,  106,  107,  108,
          109,  110,  111,  112,  113,  114,  115,  116,  117,  118,
          119,  120,  121,  122,  123,   32,   76,   77,  127,  128,
          129,  130,   10,  132,  133,  134,  135,  136,  137,    9,
           71,  140,  141,  142,  143,  144,  145,  146,   83,  148,
          149,   86,   10,   11,   12,   32,  155,  156,  157,    9,
          159,    3,    4,    5,    6,    7,    8,   15,   10,   11,
           12,   13,   14,   31,   81,   33,   34,   35,   36,   37,
           38,   39,   40,   41,   42,   43,   44,   45,   46,   47,
           48,   49,   50,   51,   52,   53,   54,   55,   56,  117,
           58,  117,   81,   82,   81,  107,  108,  138,  139,  165,
          117,  167,   70,   76,   77,  117,   58,  152,  153,  102,
          103,  107,  108,  154,  142,  141,   31,    1,  112,  113,
           72,   73,   74,   75,   76,   77,   78,  168,  117,   81,
          117,  107,  108,   71,    9,    9,   88,   89,   90,   91,
           92,   93,   94,   95,   96,   97,   98,   99,  100,  101,
          102,  103,  104,  105,  106,  107,  108,  109,  110,  111,
          112,  113,  114,  115,  116,  117,  118,  119,  120,  121,
          122,  123,   15,   15,   15,  127,  128,  129,  130,   81,
          132,  133,  134,  135,  136,  137,   15,   17,  140,  141,
          142,  143,  144,  145,  146,   31,  148,  149,   15,   83,
          138,  139,  117,  155,  156,  157,    2,    3,    4,    5,
            6,    7,    8,   17,   15,  117,  154,   13,   14,   17,
           16,   32,   71,   72,   17,   17,   17,  142,   17,   17,
          168,   17,   17,   17,   83,   17,   38,   39,   87,  141,
           17,   36,   32,   58,   81,   81,   70,   78,   71,   32,
           83,  166,   32,   32,   32,   51,   52,  141,  160,  161,
          162,   57,   32,   59,   60,   61,   62,   63,   64,   65,
           66,   67,   68,   69,  123,   71,   72,   73,   74,   81,
          117,  117,  166,   79,   80,   81,   32,   83,  172,  117,
           32,   87,  141,   32,  143,  144,  145,  146,  147,  148,
          149,   32,   32,    1,  100,  141,  142,  156,  157,   32,
           32,  107,  108,   32,  142,  117,   32,  166,   32,   32,
           32,  117,  171,  172,  160,  161,  162,  123,   32,  166,
          166,   32,   38,  129,  130,   38,   36,   36,  166,   36,
           36,   36,   36,   81,  140,  141,   38,  143,  144,  145,
          146,  147,  148,  149,  150,   38,   81,  159,   39,   83,
          156,  157,   90,   84,  160,  161,  162,  163,    0,    1,
          166,   86,   98,   91,  170,  171,  172,   75,   76,   77,
           78,   98,   93,  115,  132,   95,  136,   85,   97,  136,
           88,   89,   90,   91,   92,   93,   94,   95,   96,   97,
          137,   99,  151,  154,  158,   98,  104,  105,  106,  107,
          101,  101,  110,  111,  141,  151,  159,  158,  116,  154,
          118,  119,  166,  163,    1,  123,  114,  160,   -1,   -1,
          128,  165,   -1,  131,  132,  133,  134,  135,  167,   -1,
           -1,   -1,   -1,   75,  164,   -1,  154,  154,   -1,    1,
           -1,   -1,  154,   85,   -1,   -1,   88,   89,   90,  164,
           92,   -1,   94,   -1,   96,   -1,  164,   99,   -1,   -1,
          168,  169,  104,  105,  106,  107,  165,  109,  110,  111,
           32,  165,  114,  165,  116,  164,  118,  119,  164,  164,
          164,  123,  164,  164,  164,    1,  128,  164,   75,  131,
          132,  133,  134,  135,  164,  164,  164,  164,   85,  164,
          164,   88,   89,   90,  164,   92,  164,   94,  164,   96,
          165,  165,   99,  155,  101,  102,  103,  104,  105,  106,
          107,   83,  164,  110,  111,  166,  168,  169,  166,  116,
          166,  118,  119,  166,  166,  166,  123,  166,  166,  166,
          166,  128,  166,  166,  131,  132,  133,  134,  135,  166,
          166,  166,  166,  166,  166,  166,  118,  119,  166,   75,
            1,  123,  166,  166,  166,  166,  166,  166,  166,   85,
          132,  166,   88,   89,   90,  166,   92,  164,   94,  141,
           96,  168,  169,   99,  166,  101,  102,  103,  104,  105,
          106,  107,  166,  166,  110,  111,  166,  166,  166,  166,
          116,  163,  118,  119,  166,   38,   39,  123,  166,  166,
          172,  167,  128,  167,    1,  131,  132,  133,  134,  135,
           17,   18,   19,   20,   21,   22,   23,   24,   25,   26,
           27,   28,   29,   30,  167,  167,  167,  167,   71,   72,
          167,  167,  167,  167,  167,  167,  167,  167,  164,  167,
           83,  168,  168,  169,   87,  167,  167,  167,  167,  167,
            1,  102,  167,   60,   61,  167,  107,  167,  109,  167,
          167,  167,  167,  114,   71,  167,  117,  118,  119,  120,
          121,  122,  123,  124,  125,  126,  127,  167,  167,  167,
          123,   32,  167,  167,  167,  167,   83,  167,  167,  167,
          167,  167,  167,  167,    1,  167,  169,  140,  141,  168,
          143,  144,  145,  146,  147,  148,  149,  168,  168,  168,
          168,  168,  168,  156,  157,  168,  168,  171,  169,  168,
          168,  118,  119,  166,   75,   32,  123,    1,  171,  172,
          168,  138,  139,  169,   85,  132,  169,   88,   89,   90,
          169,   92,  169,   94,  141,   96,  169,  154,   99,  169,
          169,  169,  169,  104,  105,  106,  169,  169,   32,  110,
          111,  168,  170,  169,  169,  116,  163,  169,   75,  166,
          169,    1,  172,  169,  169,  172,  169,  128,   85,  169,
          169,   88,   89,   90,  169,   92,  169,   94,  169,   96,
          169,  169,   99,  169,   -1,   -1,   -1,  104,  105,  106,
           -1,   75,   32,  110,  111,   -1,   -1,   -1,    1,  116,
           -1,   85,   -1,  164,   88,   89,   90,  168,   92,   -1,
           94,  128,   96,   -1,   -1,   99,   -1,   -1,   -1,   -1,
          104,  105,  106,   -1,   -1,   -1,  110,  111,   -1,   32,
           -1,   -1,  116,   -1,   -1,   75,   -1,   -1,   -1,   -1,
           -1,   -1,    1,   -1,  128,   85,   -1,  164,   88,   89,
           90,  168,   92,   -1,   94,   -1,   96,   -1,   -1,   99,
           -1,   -1,   -1,   -1,  104,  105,  106,   -1,   -1,   -1,
          110,  111,   75,   -1,   -1,   -1,  116,   -1,   -1,   -1,
          164,   -1,   85,   -1,  168,   88,   89,   90,  128,   92,
           -1,   94,   -1,   96,   -1,   -1,   99,   -1,   71,   72,
           -1,  104,  105,  106,   -1,   -1,   -1,  110,  111,   -1,
           83,   -1,   -1,  116,   87,   -1,   75,   -1,   -1,   -1,
           -1,   -1,   -1,   -1,  164,  128,   85,   -1,  168,   88,
           89,   90,   -1,   92,   -1,   94,   -1,   96,   -1,   -1,
           99,   -1,   -1,   -1,   -1,  104,  105,  106,   -1,   -1,
          123,  110,  111,   -1,   -1,   -1,   -1,  116,   -1,   -1,
           -1,  164,   -1,   -1,   -1,  168,   -1,   -1,  141,  128,
          143,  144,  145,  146,  147,  148,  149,   -1,   -1,   -1,
           -1,   -1,   -1,  156,  157,   -1,   -1,   -1,   -1,   -1,
           -1,   -1,   -1,  166,   -1,   -1,   -1,   -1,  171,  172,
           -1,   -1,   -1,   -1,   -1,  164,   -1,   -1,   -1,  168
    );

    protected array $actionBase = array(
            0,  155,   -3,  313,  471,  471,  882,  948, 1003, 1074,
          744,  134,  212,  -61, 1293, 1370, 1370, 1407, 1370, 1249,
         1326,  232, 1451, 1451, 1451,  932,  855,  628,  628,  855,
          628,  778,  778,  778,  778,  109,  109,  265,  265,  591,
          593,  563,  420,  414,  414,  414,  414,  414,  743,  414,
          414,  414,  414,  414,  743,  414,  414,  414,  414,  414,
          414,  414,  414,  414,  414,  414,  414,  414,  414,  414,
          414,  414,  414,  414,  414,  414,  414,  414,  414,  414,
          414,  414,  414,  414,  414,  414,  414,  414,  414,  414,
          414,  414,  414,  414,  414,  414,  414,  414,  414,  414,
          414,  414,  414,  414,  414,  414,  414,  414,  414,  414,
          414,  414,  414,  414,  414,  414,  414,  414,  414,  414,
          414,  414,  414,  414,  414,  414,  414,  414,  414,  414,
          414,  414,  414,  414,  414,  414,  414,  414,  414,  414,
          414,  414,  414,  414,  414,  414,  414,  414,  414,  414,
          414,  414,  414,  414,  414,  414,  414,  414,  414,  414,
          414,  414,  414,  414,  414,  414,  122,  351,  127,  241,
          779, 1117, 1124, 1120, 1125, 1115, 1112, 1118, 1121, 1126,
         1217, 1219,  879, 1221, 1222, 1215, 1224, 1122,  936, 1116,
         1123,  612,  612,  612,  612,  612,  612,  612,  612,  612,
          612,  612,  612,  612,  612,  612,  612,  612,  612,  612,
          612,  612,  612,  612,  612,  612,  612,  612,  612,  592,
          513,  482,   69,   69,   69,   69,   69,   69,   69,   69,
           69,   69,   69,   69,   69,   69,   69,   69,   69,   69,
           69,   69, 1157, 1157,  487,  487,  487,   20, 1437,  731,
         1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437,  331,  204,
         1193,  187,  170,  170,    6,    6,    6,    6,    6, 1149,
           53, 1028, 1203, 1203,  308,  308,  308,  308,  243,  271,
          348,  696,  -95,  665,  -41,  752,  752,   98,   98,  552,
          552,  391,  391,  552,  552,  552,  377,  377,  377,  377,
          209,  366,  373,  -83,  387,  853,  224,  224,  224,  224,
          853,  853,  853,  853,  844, 1146,  853,  990, 1008,  853,
          853,  368,  539,  539,  642,  433,  433,  433,  539,  449,
          535,  535,  449,  398,  535,   28,  103,  554,  832,  568,
          514,  554,   73,  328,   24,   18,  847,  584,  847, 1111,
          865,  865,  857,  800,  934, 1162, 1128,  889, 1210,  890,
         1211,  174,  -56,  797, 1110, 1110, 1110, 1110, 1110, 1110,
         1110, 1110, 1110, 1110, 1110, 1200,  359, 1111,  234, 1200,
         1200, 1200,  359,  359,  359,  359,  359,  359,  359,  359,
          858,  359,  359,  586,  234,  520,  607,  234,  886,  359,
          845,  122,  122,  122,  122,  122,  122,  122,  122,  122,
          122,  122,  -11,  122,  122,  351,  201,  201,  122,  386,
            5,  201,  201,  201,  201,  122,  122,  122,  122,  584,
          765,  826,  604,  428,  839,  112,  765,  765,  765,   25,
          173,   43,  785,  880,  444,  842,  842,  842,  856,  962,
          962,  842,  747,  842,  856,  842,  842,  962,  962,  840,
          962,  272,  546,  477,  531,  600,  962,  358,  842,  842,
          842,  842,  833,  962,   29,  119,  620,  842,  341,  329,
          842,  842,  833,  830,  866,  843,  962,  962,  962,  833,
          526,  843,  843,  843,  901,  904,  892,  864,  443,  429,
          706,  225,  899,  864,  864,  842,  533,  892,  864,  892,
          864,  837,  864,  864,  864,  892,  864,  747,  491,  864,
          794,  795,  705,  162,  864,   23,  973,  974,  769,  975,
          966,  976, 1032,  977,  978, 1140,  961,  988,  967,  979,
         1033,  964,  963,  876,  787,  788,  872,  829,  959,  756,
          756,  756,  954,  957,  756,  756,  756,  756,  756,  756,
          756,  756,  787,  846,  878,  852,  991,  789,  791, 1082,
          831, 1171, 1192,  990,  973,  978,  790,  967,  979,  964,
          963,  850,  849,  838,  841,  834,  810,  801,  802,  861,
          982,  860,  792, 1063,  992, 1167, 1131,  995,  999, 1064,
         1088,  905, 1090, 1173,  757, 1177, 1180,  891, 1016, 1141,
          756,  952,  836,  896, 1008,  958,  787,  898, 1092, 1093,
         1057, 1073, 1065, 1066, 1139,  863,  859,  935, 1181, 1017,
         1020, 1021, 1142, 1143,  875, 1058,  851, 1067,  887, 1127,
         1068, 1069, 1070, 1072, 1144, 1182, 1147,  949, 1148,  906,
          862, 1034,  871, 1183,  622,  884,  885,  897, 1023,  737,
          989, 1151, 1164, 1187, 1078, 1079, 1080, 1188, 1194,  986,
          908, 1059,  870, 1060, 1036,  909,  910,  738,  888, 1094,
          881,  883,  877,  739,  751, 1195, 1198, 1201,  987,  867,
          749,  913,  920, 1095,  874, 1098, 1204,  763,  921, 1205,
         1085,  796,  798,  750,  782,  776,  803,  869, 1152,  873,
          848,  854, 1022,  798,  868,  922, 1207,  923,  925,  926,
         1081,  930,    0,    0,    0,    0,    0,    0,    0,    0,
            0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
            0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
            0,    0,  784,  784,  784,  784,  784,  784,  784,  784,
          784,  628,  628,  628,  628,  784,  784,  784,  784,  784,
          784,  784,  628,  784,  784,  784,  628,  628,    0,    0,
          628,    0,  784,  784,  784,  784,  784,  784,  784,  784,
          784,  784,  784,  784,  784,  784,  784,  784,  784,  784,
          784,  784,  784,  784,  784,  784,  784,  784,  784,  784,
          784,  784,  784,  784,  784,  784,  784,  784,  784,  784,
          784,  784,  784,  784,  784,  784,  784,  784,  784,  784,
          784,  784,  784,  784,  784,  784,  784,  784,  784,  784,
          784,  784,  784,  784,  784,  784,  784,  784,  784,  784,
          784,  784,  784,  784,  784,  784,  784,  784,  784,  784,
          784,  784,  784,  784,  784,  784,  784,  784,  784,  784,
          784,  784,  784,  784,  784,  784,  784,  784,  784,  784,
          784,  784,  784,  784,  784,  784,  784,  784,  784,  784,
          784,  784,  784,  784,  784,  784,  784,  784,  784,  784,
          784,  784,  784,  784,  784,  784,  784,  784,  784,  784,
          784,  784,  784,  784,  784,  784,  784,  612,  612,  612,
          612,  612,  612,  612,  612,  612,  612,  612,  612,  612,
          612,  612,  612,  612,  612,  612,  612,  612,  612,  612,
          612,  612,    0,    0,    0,    0,    0,    0,    0,    0,
            0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
            0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
          612,  612,  612,  612,  612,  612,  612,  612,  612,  612,
          612,  612,  612,  612,  612,  612,  612,  612,  612,  612,
          612,  612,  612,  678,  678,  612,  612,  612,  612,  678,
          678,  678,  678,  678,  678,  678,  678,  678,  678,  612,
          612,    0,  612,  612,  612,  612,  612,  612,  612,  612,
          840,  678,  678,  678,  678,  433,  433,  433,  433,  211,
          211,  678,  678,  398,  678,  398,  678,  678,  433,  433,
          678,  678,  678,  678,  678,  678,  678,  678,  678,  678,
          678,    0,    0,    0,  234,  535,  678,  747,  747,  747,
          747,  678,  678,  678,  678,  535,  535,  678,  390,  390,
          678,  678,    0,    0,    0,    0,    0,    0,    0,    0,
          234,    0,    0,  234,    0,    0,  747,  747,  678,  398,
          840,  375,  678,    0,    0,    0,    0,  234,  747,  234,
          359,  535,  535,  359,  359,  201,  122,  375,  587,  587,
          587,  587,    0,    0,  584,  840,  840,  840,  840,  840,
          840,  840,  840,  840,  840,  840,  747,    0,  840,    0,
          747,  747,  747,    0,    0,    0,    0,    0,    0,    0,
            0,  962,    0,    0,    0,    0,    0,    0,    0,  747,
            0,  962,    0,    0,    0,    0,    0,    0,    0,    0,
            0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
            0,  747,    0,    0,    0,    0,    0,    0,    0,    0,
            0,  756,  863,    0,    0,  863,    0,  756,  756,  756,
            0,    0,    0,  888,  874
    );

    protected array $actionDefault = array(
            3,32767,32767,32767,  102,  102,32767,32767,32767,32767,
        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
        32767,32767,32767,32767,32767,32767,32767,32767,32767,  100,
        32767,  631,  631,  631,  631,32767,32767,  257,  102,32767,
        32767,  500,32767,  415,  415,  415,32767,32767,32767,  573,
          573,  573,  573,  573,   17,32767,32767,32767,32767,32767,
        32767,32767,  500,32767,32767,32767,32767,32767,32767,32767,
        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
        32767,32767,32767,32767,   36,    7,    8,   10,   11,   49,
          340,  100,32767,32767,32767,32767,32767,32767,32767,  102,
        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
          624,32767,32767,32767,32767,32767,32767,32767,32767,32767,
        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
        32767,  403,  494,  504,  482,  483,  485,  486,  414,  574,
          630,  344,  627,  339,  413,  146,  354,  342,  245,  261,
          505,  262,  506,  509,  510,  218,  400,  150,  151,  446,
          501,  448,  499,  503,  447,  420,  427,  428,  429,  430,
          431,  432,  433,  434,  435,  436,  437,  438,  439,  418,
          419,  502,32767,32767,  479,  478,  477,  444,32767,32767,
        32767,32767,32767,32767,32767,32767,  102,32767,  445,  449,
          417,  452,  450,  451,  468,  469,  466,  467,  470,32767,
          323,32767,32767,32767,  471,  472,  473,  474,  381,  379,
        32767,32767,  111,  323,  111,32767,32767,  459,  460,32767,
        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
          517,  567,  476,32767,32767,32767,32767,32767,32767,32767,
        32767,32767,32767,32767,32767,32767,  102,32767,32767,32767,
          100,  569,  441,  443,  537,  454,  455,  453,  421,32767,
          542,32767,  102,32767,  544,32767,32767,32767,32767,32767,
        32767,32767,  568,32767,  575,  575,32767,  530,  100,  196,
        32767,  543,  196,  196,32767,32767,32767,32767,32767,32767,
        32767,32767,  638,  530,  110,  110,  110,  110,  110,  110,
          110,  110,  110,  110,  110,32767,  196,  110,32767,32767,
        32767,  100,  196,  196,  196,  196,  196,  196,  196,  196,
          545,  196,  196,  191,32767,  271,  273,  102,  592,  196,
          547,32767,32767,32767,32767,32767,32767,32767,32767,32767,
        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
        32767,32767,32767,32767,32767,32767,32767,32767,32767,  530,
          464,  139,32767,  532,  139,  575,  456,  457,  458,  575,
          575,  575,  319,  296,32767,32767,32767,32767,32767,  545,
          545,  100,  100,  100,  100,32767,32767,32767,32767,  111,
          516,   99,   99,   99,   99,   99,  103,  101,32767,32767,
        32767,32767,  226,32767,  101,  101,   99,32767,  101,  101,
        32767,32767,  226,  228,  215,  230,32767,  596,  597,  226,
          101,  230,  230,  230,  250,  250,  519,  325,  101,   99,
          101,  101,  198,  325,  325,32767,  101,  519,  325,  519,
          325,  200,  325,  325,  325,  519,  325,32767,  101,  325,
          217,  403,   99,   99,  325,32767,32767,32767,  532,32767,
        32767,32767,32767,32767,32767,32767,  225,32767,32767,32767,
        32767,32767,32767,32767,32767,  562,32767,  580,  594,  462,
          463,  465,  579,  577,  487,  488,  489,  490,  491,  492,
          493,  496,  626,32767,  536,32767,32767,32767,  353,32767,
          636,32767,32767,32767,    9,   74,  525,   42,   43,   51,
           57,  551,  552,  553,  554,  548,  549,  555,  550,32767,
        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
        32767,32767,32767,  637,32767,  575,32767,32767,32767,32767,
          461,  557,  602,32767,32767,  576,  629,32767,32767,32767,
        32767,32767,32767,32767,32767,  139,32767,32767,32767,32767,
        32767,32767,32767,32767,32767,32767,  562,32767,  137,32767,
        32767,32767,32767,32767,32767,32767,32767,  558,32767,32767,
        32767,  575,32767,32767,32767,32767,  321,  318,32767,32767,
        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
        32767,32767,32767,32767,  575,32767,32767,32767,32767,32767,
          298,32767,  315,32767,32767,32767,32767,32767,32767,32767,
        32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
        32767,  399,  532,  301,  303,  304,32767,32767,32767,32767,
          375,32767,32767,32767,32767,32767,32767,32767,32767,32767,
        32767,32767,  153,  153,    3,    3,  356,  153,  153,  153,
          356,  356,  153,  356,  356,  356,  153,  153,  153,  153,
          153,  153,  283,  186,  265,  268,  250,  250,  153,  367,
          153
    );

    protected array $goto = array(
          201,  168,  201,  201,  201, 1053,  926,  709,  927,  667,
          668,  595,  685,  686,  687,  745,  650,  652, 1085,  358,
          672,  634,  671,  718,  696,  699, 1025,  707,  716, 1021,
          170,  170,  170,  170,  225,  202,  198,  198,  180,  182,
          220,  207,  198,  198,  198,  198,  198, 1177,  199,  199,
          199,  199,  199, 1177,  192,  193,  194,  195,  196,  197,
          222,  220,  223,  549,  550,  430,  551,  554,  555,  556,
          557,  558,  559,  560,  561,  171,  172,  173,  200,  174,
          175,  176,  169,  177,  178,  179,  181,  219,  221,  224,
          244,  247,  258,  259,  261,  262,  263,  264,  265,  266,
          267,  268,  274,  275,  276,  277,  287,  288,  325,  326,
          327,  436,  437,  438,  610,  226,  227,  228,  229,  230,
          231,  232,  233,  234,  235,  236,  237,  238,  239,  240,
          183,  241,  184,  193,  194,  195,  196,  197,  222,  203,
          204,  205,  206,  245,  185,  186,  187,  208,  204,  188,
          246,  167,  209,  210,  189,  211,  212,  213,  190,  214,
          215,  191,  216,  217,  218,  284,  282,  284,  284,  867,
         1084, 1086, 1088,  612,  254,  254,  254,  254,  254,  440,
          674,  611, 1127, 1134, 1135,  435,  328,  322,  323,  344,
          605,  439,  345,  441,  651,  721,  491,  520,  712,  434,
         1125,  625,  845,  493,  252,  252,  252,  252,  249,  255,
          361,  361,  361,  361,  864,  922,  917,  918,  931,  873,
          919,  870,  920,  921,  871,  874,  362,  925,  878,  982,
          419,  717,  877,  442, 1059, 1058,  362,  362,  865, 1114,
         1109, 1110, 1111, 1258,  959,  845,  442,  845, 1259, 1262,
          960, 1263,  362,  362, 1060, 1060,  362,  839, 1400,  678,
         1071, 1067, 1068,  706,  428, 1053, 1282, 1282, 1282,  568,
          359,  360,  362,  362,  841, 1053, 1282,  990, 1053,  706,
         1053, 1053,  706,  350, 1053, 1053, 1053, 1053, 1053, 1053,
         1053, 1053, 1053, 1053, 1053,  906,  863,  906,  906,  899,
         1282,  403,  406,  613,  617, 1282, 1282, 1282, 1282,  351,
          350, 1282, 1062, 1063, 1282, 1282, 1365, 1365, 1365, 1365,
         1344,  573,  566,  473,  602, 1056, 1056,  955,  955,  694,
          967, 1226,  939, 1048, 1064, 1065,  940,  564,  564,  564,
         1014,  356,  521,  986,  860,  744,  860,  564,  565, 1023,
         1018,  317,  566,  573,  597,  598,  318,  608,  614, 1011,
          630,  631,  479,  479,    8,  512,    9,  513,   28,  334,
          315,  466,  479,  519,  568, 1373,  984,  984,  984,  984,
         1360, 1361,  466,  636,  636,  978,  985,  606,  628, 1313,
         1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1332,
         1332,  860,  468,  401,  468, 1332, 1332, 1332, 1332, 1332,
         1332, 1332, 1332, 1332, 1332,  257,  257,  623,  637,  640,
          641,  642,  643,  664,  665,  666,  720,  600,  459,  673,
          459,  459,  459, 1355, 1355, 1355,  552,  552, 1173, 1275,
         1077,  448,  552, 1355,  552,  552,  552,  552,  552,  552,
          552,  552,  695,  450,  857,  567,  592,  567,  488, 1358,
         1359,  567,  679,  592,  346,  404,  472,  629, 1367, 1367,
         1367, 1367,  886,  278,  973,  333,  333,  333,  482,  609,
          483,  484,  883,  562,  562,  562,  562,  891,  616,  410,
         1391, 1392, 1329, 1329, 1273,  881, 1033, 1351, 1329, 1329,
         1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329,  644,  646,
          648,  893, 1098,  748,  880,  889, 1277,  411,  489, 1102,
          895,  860,    0,  459,  459,  459,  459,  459,  459,  459,
          459,  459,  459,  459,  995, 1044,  459, 1100,  553,  553,
         1148, 1353, 1353, 1100,  553,  553,  553,  553,  553,  553,
          553,  553,  553,  553,  618,  619,  416,  417,  944, 1163,
            0,  683,    0,  684,    0,  421,  422,  423,    0,  697,
         1030,    0,  424, 1278, 1279,    0, 1265,  354,  885,    0,
          677, 1009,  855,    0,    0,    0,  879, 1383, 1383, 1265,
            0,  894,  882, 1097, 1101,    0,    0,    0, 1272,    0,
            0,    0, 1280, 1341, 1342,  993,    0, 1383,    0,    0,
            0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
            0,    0,    0, 1386, 1386,    0,    0,    0,  983,    0,
            0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
            0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
            0, 1146,  898,    0,    0,    0,    0,    0,    0,    0,
            0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
            0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
            0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
            0,    0,    0,    0, 1028, 1028
    );

    protected array $gotoCheck = array(
           42,   42,   42,   42,   42,   73,   65,   73,   65,   86,
           86,   48,   86,   86,   86,   48,   48,   48,  130,   97,
           48,   56,   56,    9,   48,   48,   48,   48,   48,   48,
           42,   42,   42,   42,   42,   42,   42,   42,   42,   42,
           42,   42,   42,   42,   42,   42,   42,   42,   42,   42,
           42,   42,   42,   42,   42,   42,   42,   42,   42,   42,
           42,   42,   42,   42,   42,   42,   42,   42,   42,   42,
           42,   42,   42,   42,   42,   42,   42,   42,   42,   42,
           42,   42,   42,   42,   42,   42,   42,   42,   42,   42,
           42,   42,   42,   42,   42,   42,   42,   42,   42,   42,
           42,   42,   42,   42,   42,   42,   42,   42,   42,   42,
           42,   42,   42,   42,   42,   42,   42,   42,   42,   42,
           42,   42,   42,   42,   42,   42,   42,   42,   42,   42,
           42,   42,   42,   42,   42,   42,   42,   42,   42,   42,
           42,   42,   42,   42,   42,   42,   42,   42,   42,   42,
           42,   42,   42,   42,   42,   42,   42,   42,   42,   42,
           42,   42,   42,   42,   42,   23,   23,   23,   23,   15,
          129,  129,  129,  133,    5,    5,    5,    5,    5,   66,
           66,    8,    8,  147,  147,   66,   66,   66,   66,   66,
           66,   66,   66,   66,   66,    8,   84,    8,    8,   13,
            8,   13,   12,   84,    5,    5,    5,    5,    5,    5,
           24,   24,   24,   24,   26,   15,   15,   15,   15,   15,
           15,   15,   15,   15,   15,   15,   14,   15,   15,   93,
           93,   93,   15,  118,  119,  119,   14,   14,   27,   15,
           15,   15,   15,   79,   79,   12,  118,   12,   79,   79,
           79,   79,   14,   14,  118,  118,   14,    6,   14,  118,
          118,  118,  118,    7,   43,   73,   73,   73,   73,   14,
           97,   97,   14,   14,    7,   73,   73,   49,   73,    7,
           73,   73,    7,  176,   73,   73,   73,   73,   73,   73,
           73,   73,   73,   73,   73,   25,   25,   25,   25,   45,
           73,   59,   59,   59,   59,   73,   73,   73,   73,  176,
          176,   73,  120,  120,   73,   73,    9,    9,    9,    9,
           14,   76,   76,  158,  180,   89,   89,    9,    9,   89,
           89,  158,   73,   89,   89,   89,   73,   19,   19,   19,
           50,  187,   14,   50,   22,   50,   22,   19,   50,   50,
           50,   76,   76,   76,   76,   76,   76,   76,   76,  103,
           76,   76,  156,  156,   46,  162,   46,  162,   76,  177,
          177,   19,  156,  162,   14,  189,   19,   19,   19,   19,
          186,  186,   19,  108,  108,   19,   19,    2,    2,  108,
          108,  108,  108,  108,  108,  108,  108,  108,  108,  178,
          178,   22,   83,   62,   83,  178,  178,  178,  178,  178,
          178,  178,  178,  178,  178,    5,    5,   81,   81,   81,
           81,   81,   81,   81,   81,   81,   81,  104,   23,   64,
           23,   23,   23,  133,  133,  133,  164,  164,  157,   14,
          115,  113,  164,  133,  164,  164,  164,  164,  164,  164,
          164,  164,  117,   83,   18,    9,    9,    9,  184,  184,
          184,    9,  121,    9,   29,    9,    9,   80,  133,  133,
          133,  133,   39,   24,   92,   24,   24,   24,    9,    9,
            9,    9,   37,  107,  107,  107,  107,    9,  107,   28,
            9,    9,  179,  179,  168,   35,  110,  133,  179,  179,
          179,  179,  179,  179,  179,  179,  179,  179,   85,   85,
           85,   35,  132,   99,   35,    9,   20,   31,  159,  135,
           41,   22,   -1,   23,   23,   23,   23,   23,   23,   23,
           23,   23,   23,   23,   96,  114,   23,  133,  181,  181,
          150,  133,  133,  133,  181,  181,  181,  181,  181,  181,
          181,  181,  181,  181,   17,   17,   82,   82,   17,   17,
           -1,   82,   -1,   82,   -1,   82,   82,   82,   -1,   82,
           17,   -1,   82,   20,   20,   -1,   20,   82,   17,   -1,
           17,   17,   20,   -1,   -1,   -1,   17,  190,  190,   20,
           -1,   16,   16,   16,   16,   -1,   -1,   -1,   17,   -1,
           -1,   -1,   20,   20,   20,   16,   -1,  190,   -1,   -1,
           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
           -1,   -1,   -1,  190,  190,   -1,   -1,   -1,   16,   -1,
           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
           -1,   16,   16,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
           -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
           -1,   -1,   -1,   -1,  107,  107
    );

    protected array $gotoBase = array(
            0,    0, -337,    0,    0,  173,  244,  253,  170,   10,
            0,    0, -101, -147,  -78, -186,  129,   80,  138,   66,
          116,    0,   62,  159,  204,  289,  207,  231,  143,  161,
            0,   70,    0,    0,    0,  117,    0,  134,    0,  140,
            0,   69,   -1,  238,    0,  270, -384,    0, -712,  257,
          326,    0,    0,    0,    0,    0,  -23,    0,    0,  251,
            0,    0,  357,    0,  186,  -10,  -63,    0,    0,    0,
            0,    0,    0,   -5,    0,    0,  -31,    0,    0, -173,
          149,   52,   53,  120, -286,   17, -721,    0,    0,   40,
            0,    0,  136,  -82,    0,    0,   81, -476,    0,   98,
            0,    0,    0,  321,  380,    0,    0,  452,  141,    0,
          119,    0,    0,  158,   14,  160,    0,  163,  -37,  -51,
           21,  166,    0,    0,    0,    0,    0,    0,    0,  167,
           16,    0,  131,  162,    0,   67,    0,    0,    0,    0,
            0,    0,    0,    0,    0,    0,    0,  -95,    0,    0,
           86,    0,    0,    0,    0,    0,  325,  189,   50,   68,
            0,    0, -142,    0,  194,    0,    0,    0,  118,    0,
            0,    0,    0,    0,    0,    0,  -21,   39,  157,  250,
          292,  296,    0,    0,  151,    0,    1,  308,    0,  341,
          273,    0,    0
    );

    protected array $gotoDefault = array(
        -32768,  525,  752,    7,  753,  948,  828,  837,  589,  543,
          719,  355,  638,  431, 1349,  924, 1162,  607,  856, 1291,
         1297,  467,  859,  339,  742,  936,  907,  908,  407,  394,
          872,  405,  662,  639,  506,  892,  463,  884,  498,  887,
          462,  896,  166,  427,  523,  900,    6,  903,  571,  934,
          988,  395,  911,  396,  690,  913,  591,  915,  916,  402,
          408,  409, 1167,  599,  635,  928,  260,  593,  929,  393,
          930,  938,  398,  400,  700,  478,  517,  511,  420, 1129,
          594,  622,  659,  456,  485,  633,  645,  632,  492,  443,
          425,  338,  972,  980,  499,  476,  994,  357, 1002,  750,
         1175,  653,  501, 1010,  654, 1017, 1020,  544,  545,  490,
         1032,  270, 1035,  502, 1045,   26,  680, 1050, 1051,  681,
          655, 1073,  656,  682,  657, 1075,  475, 1176,  474, 1087,
         1096,  464, 1099, 1337,  465, 1103,  269, 1106,  283,  426,
          444, 1112, 1113,   12, 1119,  710,  711,   25,  279,  522,
         1147,  701,-32768,-32768,-32768,-32768,  461, 1174,  460, 1246,
         1248,  572,  503, 1266,  300, 1269,  693,  518, 1274,  457,
         1340,  458,  546,  486,  324,  547, 1384,  314,  342,  321,
          563,  301,  343,  548,  487, 1346, 1354,  340,   34, 1374,
         1385,  604,  627
    );

    protected array $ruleToNonTerminal = array(
            0,    1,    3,    3,    2,    5,    5,    6,    6,    6,
            6,    6,    6,    6,    6,    6,    6,    6,    6,    6,
            6,    6,    6,    6,    6,    6,    6,    6,    6,    6,
            6,    6,    6,    6,    6,    6,    6,    6,    6,    6,
            6,    6,    6,    6,    6,    6,    6,    6,    6,    6,
            6,    6,    6,    6,    6,    6,    6,    6,    6,    6,
            6,    6,    6,    6,    6,    6,    6,    6,    6,    6,
            6,    6,    6,    6,    6,    6,    6,    7,    7,    7,
            7,    7,    7,    7,    7,    8,    8,    9,   10,   11,
           11,   11,   12,   12,   13,   13,   14,   15,   15,   16,
           16,   17,   17,   18,   18,   21,   21,   22,   23,   23,
           24,   24,    4,    4,    4,    4,    4,    4,    4,    4,
            4,    4,    4,    4,   29,   29,   30,   30,   32,   34,
           34,   28,   36,   36,   33,   38,   38,   35,   35,   37,
           37,   39,   39,   31,   40,   40,   41,   43,   44,   44,
           45,   45,   46,   46,   48,   47,   47,   47,   47,   49,
           49,   49,   49,   49,   49,   49,   49,   49,   49,   49,
           49,   49,   49,   49,   49,   49,   49,   49,   49,   49,
           49,   49,   49,   25,   25,   50,   69,   69,   72,   72,
           71,   70,   70,   63,   75,   75,   76,   76,   77,   77,
           78,   78,   79,   79,   80,   80,   80,   80,   26,   26,
           27,   27,   27,   27,   27,   88,   88,   90,   90,   83,
           83,   91,   91,   92,   92,   92,   84,   84,   87,   87,
           85,   85,   93,   94,   94,   57,   57,   65,   65,   68,
           68,   68,   67,   95,   95,   96,   58,   58,   58,   58,
           97,   97,   98,   98,   99,   99,  100,  101,  101,  102,
          102,  103,  103,   55,   55,   51,   51,  105,   53,   53,
          106,   52,   52,   54,   54,   64,   64,   64,   64,   81,
           81,  109,  109,  111,  111,  112,  112,  112,  112,  112,
          112,  112,  112,  110,  110,  110,  115,  115,  115,  115,
           89,   89,  118,  118,  118,  119,  119,  116,  116,  120,
          120,  122,  122,  123,  123,  117,  124,  124,  121,  125,
          125,  125,  125,  113,  113,   82,   82,   82,   20,   20,
          127,  127,  127,  128,  128,  128,  126,  126,  130,  130,
          130,  130,  130,  130,  129,  129,   60,  131,  131,  132,
           61,  134,  134,  135,  135,  136,  136,   86,  137,  137,
          137,  137,  137,  137,  137,  142,  142,  143,  143,  144,
          144,  144,  144,  144,  145,  146,  146,  141,  141,  138,
          138,  140,  140,  148,  148,  147,  147,  147,  147,  147,
          147,  147,  147,  147,  147,  139,  149,  149,  151,  150,
          150,  152,  152,  114,  153,  153,  155,  155,  155,  154,
          154,   62,  104,  156,  156,   56,   56,   42,   42,   42,
           42,   42,   42,   42,   42,   42,   42,   42,   42,   42,
           42,   42,   42,   42,   42,   42,   42,   42,   42,   42,
           42,   42,   42,   42,   42,   42,   42,   42,   42,   42,
           42,   42,   42,   42,   42,   42,   42,   42,   42,   42,
           42,   42,   42,   42,   42,   42,   42,   42,   42,   42,
           42,   42,   42,   42,   42,   42,   42,   42,   42,   42,
           42,   42,   42,   42,   42,   42,   42,   42,   42,   42,
           42,   42,   42,   42,   42,   42,   42,   42,   42,   42,
           42,   42,   42,   42,   42,   42,   42,   42,   42,   42,
           42,   42,   42,  163,  164,  164,  165,  157,  157,  162,
          162,  166,  167,  167,  168,  169,  170,  170,  170,  170,
           19,   19,   73,   73,   73,   73,  158,  158,  158,  158,
          172,  172,  161,  161,  161,  159,  159,  178,  178,  178,
          178,  178,  178,  178,  178,  178,  178,  179,  179,  179,
          108,  181,  181,  181,  181,  160,  160,  160,  160,  160,
          160,  160,  160,   59,   59,  175,  175,  175,  175,  175,
          182,  182,  171,  171,  171,  171,  183,  183,  183,  183,
          183,  183,   74,   74,   66,   66,   66,   66,  133,  133,
          133,  133,  186,  185,  174,  174,  174,  174,  174,  174,
          174,  173,  173,  173,  184,  184,  184,  184,  107,  180,
          188,  188,  187,  187,  189,  189,  189,  189,  189,  189,
          189,  189,  177,  177,  177,  177,  176,  191,  190,  190,
          190,  190,  190,  190,  190,  190,  192,  192,  192,  192
    );

    protected array $ruleToLength = array(
            1,    1,    2,    0,    1,    1,    1,    1,    1,    1,
            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
            1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
            1,    1,    1,    1,    1,    1,    1,    1,    1,    0,
            1,    0,    1,    1,    2,    1,    3,    4,    1,    2,
            0,    1,    1,    1,    1,    4,    3,    5,    4,    3,
            4,    1,    3,    4,    1,    1,    8,    7,    2,    3,
            1,    2,    3,    1,    2,    3,    1,    1,    3,    1,
            3,    1,    2,    2,    3,    1,    3,    2,    3,    1,
            3,    3,    2,    0,    1,    1,    1,    1,    1,    3,
            7,   10,    5,    7,    9,    5,    3,    3,    3,    3,
            3,    3,    1,    2,    5,    7,    9,    6,    5,    6,
            3,    2,    1,    1,    1,    1,    0,    2,    1,    3,
            8,    0,    4,    2,    1,    3,    0,    1,    0,    1,
            0,    1,    3,    1,    1,    1,    1,    1,    8,    9,
            7,    8,    7,    6,    8,    0,    2,    0,    2,    1,
            2,    1,    2,    1,    1,    1,    0,    2,    0,    2,
            0,    2,    2,    1,    3,    1,    4,    1,    4,    1,
            1,    4,    2,    1,    3,    3,    3,    4,    4,    5,
            0,    2,    4,    3,    1,    1,    7,    0,    2,    1,
            3,    3,    4,    1,    4,    0,    2,    5,    0,    2,
            6,    0,    2,    0,    3,    1,    2,    1,    1,    2,
            0,    1,    3,    0,    2,    1,    1,    1,    1,    1,
            1,    1,    1,    7,    9,    6,    1,    2,    1,    1,
            1,    1,    1,    1,    1,    1,    3,    3,    3,    1,
            3,    3,    3,    3,    3,    1,    3,    3,    1,    1,
            2,    1,    1,    0,    1,    0,    2,    2,    2,    4,
            2,    4,    4,    3,    1,    3,    1,    3,    2,    2,
            1,    1,    3,    3,    1,    1,    2,    3,    1,    1,
            2,    3,    1,    1,    3,    2,    0,    1,    5,    5,
            6,   10,    3,    5,    1,    1,    3,    0,    2,    4,
            5,    4,    4,    4,    3,    1,    1,    1,    1,    1,
            1,    0,    1,    1,    2,    1,    1,    1,    1,    1,
            1,    1,    1,    1,    1,    2,    1,    3,    1,    1,
            3,    0,    2,    0,    5,    8,    1,    3,    3,    0,
            2,    2,    2,    3,    1,    0,    1,    1,    3,    3,
            3,    4,    4,    1,    1,    2,    2,    3,    3,    3,
            3,    3,    3,    3,    3,    3,    3,    3,    3,    3,
            2,    2,    2,    2,    3,    3,    3,    3,    3,    3,
            3,    3,    3,    3,    3,    3,    3,    3,    3,    3,
            3,    3,    2,    2,    2,    2,    3,    3,    3,    3,
            3,    3,    3,    3,    3,    3,    3,    5,    4,    3,
            4,    4,    2,    2,    4,    2,    2,    2,    2,    2,
            2,    2,    2,    2,    2,    2,    2,    1,    3,    2,
            1,    2,    4,    2,    2,    8,    9,    8,    9,    9,
           10,    9,   10,    8,    3,    2,    2,    1,    1,    0,
            4,    2,    1,    3,    2,    1,    2,    2,    2,    4,
            1,    1,    1,    1,    1,    1,    1,    1,    3,    1,
            1,    1,    0,    1,    1,    0,    1,    1,    1,    1,
            1,    1,    1,    1,    1,    1,    1,    3,    5,    3,
            3,    4,    1,    1,    3,    1,    1,    1,    1,    1,
            3,    2,    3,    0,    1,    1,    3,    1,    1,    1,
            1,    1,    1,    3,    1,    1,    1,    4,    4,    1,
            4,    4,    0,    1,    1,    1,    3,    3,    1,    4,
            2,    2,    1,    3,    1,    4,    4,    3,    3,    3,
            3,    1,    3,    1,    1,    3,    1,    1,    4,    1,
            1,    1,    3,    1,    1,    2,    1,    3,    4,    3,
            2,    0,    2,    2,    1,    2,    1,    1,    1,    4,
            3,    3,    3,    3,    6,    3,    1,    1,    2,    1
    );

    protected function initReduceCallbacks(): void {
        $this->reduceCallbacks = [
            0 => null,
            1 => static function ($self, $stackPos) {
                 $self->semValue = $self->handleNamespaces($self->semStack[$stackPos-(1-1)]);
            },
            2 => static function ($self, $stackPos) {
                 if ($self->semStack[$stackPos-(2-2)] !== null) { $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; } $self->semValue = $self->semStack[$stackPos-(2-1)];;
            },
            3 => static function ($self, $stackPos) {
                 $self->semValue = array();
            },
            4 => static function ($self, $stackPos) {
                 $nop = $self->maybeCreateZeroLengthNop($self->tokenPos);;
            if ($nop !== null) { $self->semStack[$stackPos-(1-1)][] = $nop; } $self->semValue = $self->semStack[$stackPos-(1-1)];
            },
            5 => null,
            6 => null,
            7 => null,
            8 => null,
            9 => null,
            10 => null,
            11 => null,
            12 => null,
            13 => null,
            14 => null,
            15 => null,
            16 => null,
            17 => null,
            18 => null,
            19 => null,
            20 => null,
            21 => null,
            22 => null,
            23 => null,
            24 => null,
            25 => null,
            26 => null,
            27 => null,
            28 => null,
            29 => null,
            30 => null,
            31 => null,
            32 => null,
            33 => null,
            34 => null,
            35 => null,
            36 => null,
            37 => null,
            38 => null,
            39 => null,
            40 => null,
            41 => null,
            42 => null,
            43 => null,
            44 => null,
            45 => null,
            46 => null,
            47 => null,
            48 => null,
            49 => null,
            50 => null,
            51 => null,
            52 => null,
            53 => null,
            54 => null,
            55 => null,
            56 => null,
            57 => null,
            58 => null,
            59 => null,
            60 => null,
            61 => null,
            62 => null,
            63 => null,
            64 => null,
            65 => null,
            66 => null,
            67 => null,
            68 => null,
            69 => null,
            70 => null,
            71 => null,
            72 => null,
            73 => null,
            74 => null,
            75 => null,
            76 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(1-1)]; if ($self->semValue === "<?=") $self->emitError(new Error('Cannot use "<?=" as an identifier', $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])));
            },
            77 => null,
            78 => null,
            79 => null,
            80 => null,
            81 => null,
            82 => null,
            83 => null,
            84 => null,
            85 => static function ($self, $stackPos) {
                 $self->semValue = new Node\Identifier($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            86 => static function ($self, $stackPos) {
                 $self->semValue = new Node\Identifier($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            87 => static function ($self, $stackPos) {
                 $self->semValue = new Node\Identifier($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            88 => static function ($self, $stackPos) {
                 $self->semValue = new Node\Identifier($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            89 => static function ($self, $stackPos) {
                 $self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            90 => static function ($self, $stackPos) {
                 $self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            91 => static function ($self, $stackPos) {
                 $self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            92 => static function ($self, $stackPos) {
                 $self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            93 => static function ($self, $stackPos) {
                 $self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            94 => null,
            95 => static function ($self, $stackPos) {
                 $self->semValue = new Name(substr($self->semStack[$stackPos-(1-1)], 1), $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            96 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Variable(substr($self->semStack[$stackPos-(1-1)], 1), $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            97 => static function ($self, $stackPos) {
                 /* nothing */
            },
            98 => static function ($self, $stackPos) {
                 /* nothing */
            },
            99 => static function ($self, $stackPos) {
                 /* nothing */
            },
            100 => static function ($self, $stackPos) {
                 $self->emitError(new Error('A trailing comma is not allowed here', $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])));
            },
            101 => null,
            102 => null,
            103 => static function ($self, $stackPos) {
                 $self->semValue = new Node\Attribute($self->semStack[$stackPos-(1-1)], [], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            104 => static function ($self, $stackPos) {
                 $self->semValue = new Node\Attribute($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            105 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)]);
            },
            106 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
            },
            107 => static function ($self, $stackPos) {
                 $self->semValue = new Node\AttributeGroup($self->semStack[$stackPos-(4-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            108 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)]);
            },
            109 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)];
            },
            110 => static function ($self, $stackPos) {
                 $self->semValue = [];
            },
            111 => null,
            112 => null,
            113 => null,
            114 => null,
            115 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\HaltCompiler($self->handleHaltCompiler(), $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            116 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Namespace_($self->semStack[$stackPos-(3-2)], null, $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            $self->semValue->setAttribute('kind', Stmt\Namespace_::KIND_SEMICOLON);
            $self->checkNamespace($self->semValue);
            },
            117 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Namespace_($self->semStack[$stackPos-(5-2)], $self->semStack[$stackPos-(5-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]));
            $self->semValue->setAttribute('kind', Stmt\Namespace_::KIND_BRACED);
            $self->checkNamespace($self->semValue);
            },
            118 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Namespace_(null, $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            $self->semValue->setAttribute('kind', Stmt\Namespace_::KIND_BRACED);
            $self->checkNamespace($self->semValue);
            },
            119 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Use_($self->semStack[$stackPos-(3-2)], Stmt\Use_::TYPE_NORMAL, $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            120 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Use_($self->semStack[$stackPos-(4-3)], $self->semStack[$stackPos-(4-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            121 => null,
            122 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Const_($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]), []);
            },
            123 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Const_($self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(4-1)]);
            $self->checkConstantAttributes($self->semValue);
            },
            124 => static function ($self, $stackPos) {
                 $self->semValue = Stmt\Use_::TYPE_FUNCTION;
            },
            125 => static function ($self, $stackPos) {
                 $self->semValue = Stmt\Use_::TYPE_CONSTANT;
            },
            126 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\GroupUse($self->semStack[$stackPos-(8-3)], $self->semStack[$stackPos-(8-6)], $self->semStack[$stackPos-(8-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos]));
            },
            127 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\GroupUse($self->semStack[$stackPos-(7-2)], $self->semStack[$stackPos-(7-5)], Stmt\Use_::TYPE_UNKNOWN, $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos]));
            },
            128 => null,
            129 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
            },
            130 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)]);
            },
            131 => null,
            132 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
            },
            133 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)]);
            },
            134 => null,
            135 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
            },
            136 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)]);
            },
            137 => static function ($self, $stackPos) {
                 $self->semValue = new Node\UseItem($self->semStack[$stackPos-(1-1)], null, Stmt\Use_::TYPE_UNKNOWN, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); $self->checkUseUse($self->semValue, $stackPos-(1-1));
            },
            138 => static function ($self, $stackPos) {
                 $self->semValue = new Node\UseItem($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], Stmt\Use_::TYPE_UNKNOWN, $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); $self->checkUseUse($self->semValue, $stackPos-(3-3));
            },
            139 => static function ($self, $stackPos) {
                 $self->semValue = new Node\UseItem($self->semStack[$stackPos-(1-1)], null, Stmt\Use_::TYPE_UNKNOWN, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); $self->checkUseUse($self->semValue, $stackPos-(1-1));
            },
            140 => static function ($self, $stackPos) {
                 $self->semValue = new Node\UseItem($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], Stmt\Use_::TYPE_UNKNOWN, $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); $self->checkUseUse($self->semValue, $stackPos-(3-3));
            },
            141 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(1-1)]; $self->semValue->type = Stmt\Use_::TYPE_NORMAL;
            },
            142 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(2-2)]; $self->semValue->type = $self->semStack[$stackPos-(2-1)];
            },
            143 => null,
            144 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
            },
            145 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)]);
            },
            146 => static function ($self, $stackPos) {
                 $self->semValue = new Node\Const_($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            147 => null,
            148 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
            },
            149 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)]);
            },
            150 => static function ($self, $stackPos) {
                 $self->semValue = new Node\Const_(new Node\Identifier($self->semStack[$stackPos-(3-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)],  $self->tokenEndStack[$stackPos-(3-1)])), $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            151 => static function ($self, $stackPos) {
                 $self->semValue = new Node\Const_(new Node\Identifier($self->semStack[$stackPos-(3-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)],  $self->tokenEndStack[$stackPos-(3-1)])), $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            152 => static function ($self, $stackPos) {
                 if ($self->semStack[$stackPos-(2-2)] !== null) { $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; } $self->semValue = $self->semStack[$stackPos-(2-1)];;
            },
            153 => static function ($self, $stackPos) {
                 $self->semValue = array();
            },
            154 => static function ($self, $stackPos) {
                 $nop = $self->maybeCreateZeroLengthNop($self->tokenPos);;
            if ($nop !== null) { $self->semStack[$stackPos-(1-1)][] = $nop; } $self->semValue = $self->semStack[$stackPos-(1-1)];
            },
            155 => null,
            156 => null,
            157 => null,
            158 => static function ($self, $stackPos) {
                 throw new Error('__HALT_COMPILER() can only be used from the outermost scope', $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            159 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Block($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            160 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\If_($self->semStack[$stackPos-(7-3)], ['stmts' => $self->semStack[$stackPos-(7-5)], 'elseifs' => $self->semStack[$stackPos-(7-6)], 'else' => $self->semStack[$stackPos-(7-7)]], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos]));
            },
            161 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\If_($self->semStack[$stackPos-(10-3)], ['stmts' => $self->semStack[$stackPos-(10-6)], 'elseifs' => $self->semStack[$stackPos-(10-7)], 'else' => $self->semStack[$stackPos-(10-8)]], $self->getAttributes($self->tokenStartStack[$stackPos-(10-1)], $self->tokenEndStack[$stackPos]));
            },
            162 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\While_($self->semStack[$stackPos-(5-3)], $self->semStack[$stackPos-(5-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]));
            },
            163 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Do_($self->semStack[$stackPos-(7-5)], $self->semStack[$stackPos-(7-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos]));
            },
            164 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\For_(['init' => $self->semStack[$stackPos-(9-3)], 'cond' => $self->semStack[$stackPos-(9-5)], 'loop' => $self->semStack[$stackPos-(9-7)], 'stmts' => $self->semStack[$stackPos-(9-9)]], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos]));
            },
            165 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Switch_($self->semStack[$stackPos-(5-3)], $self->semStack[$stackPos-(5-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]));
            },
            166 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Break_($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            167 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Continue_($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            168 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Return_($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            169 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Global_($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            170 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Static_($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            171 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Echo_($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            172 => static function ($self, $stackPos) {

        $self->semValue = new Stmt\InlineHTML($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
        $self->semValue->setAttribute('hasLeadingNewline', $self->inlineHtmlHasLeadingNewline($stackPos-(1-1)));

            },
            173 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Expression($self->semStack[$stackPos-(2-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            174 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Unset_($self->semStack[$stackPos-(5-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]));
            },
            175 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Foreach_($self->semStack[$stackPos-(7-3)], $self->semStack[$stackPos-(7-5)][0], ['keyVar' => null, 'byRef' => $self->semStack[$stackPos-(7-5)][1], 'stmts' => $self->semStack[$stackPos-(7-7)]], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos]));
            },
            176 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Foreach_($self->semStack[$stackPos-(9-3)], $self->semStack[$stackPos-(9-7)][0], ['keyVar' => $self->semStack[$stackPos-(9-5)], 'byRef' => $self->semStack[$stackPos-(9-7)][1], 'stmts' => $self->semStack[$stackPos-(9-9)]], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos]));
            },
            177 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Foreach_($self->semStack[$stackPos-(6-3)], new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos-(6-4)],  $self->tokenEndStack[$stackPos-(6-4)])), ['stmts' => $self->semStack[$stackPos-(6-6)]], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos]));
            },
            178 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Declare_($self->semStack[$stackPos-(5-3)], $self->semStack[$stackPos-(5-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]));
            },
            179 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\TryCatch($self->semStack[$stackPos-(6-3)], $self->semStack[$stackPos-(6-5)], $self->semStack[$stackPos-(6-6)], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos])); $self->checkTryCatch($self->semValue);
            },
            180 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Goto_($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            181 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Label($self->semStack[$stackPos-(2-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            182 => static function ($self, $stackPos) {
                 $self->semValue = null; /* means: no statement */
            },
            183 => null,
            184 => static function ($self, $stackPos) {
                 $self->semValue = $self->maybeCreateNop($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]);
            },
            185 => static function ($self, $stackPos) {
                 if ($self->semStack[$stackPos-(1-1)] instanceof Stmt\Block) { $self->semValue = $self->semStack[$stackPos-(1-1)]->stmts; } else if ($self->semStack[$stackPos-(1-1)] === null) { $self->semValue = []; } else { $self->semValue = [$self->semStack[$stackPos-(1-1)]]; };
            },
            186 => static function ($self, $stackPos) {
                 $self->semValue = array();
            },
            187 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)];
            },
            188 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)]);
            },
            189 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
            },
            190 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Catch_($self->semStack[$stackPos-(8-3)], $self->semStack[$stackPos-(8-4)], $self->semStack[$stackPos-(8-7)], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos]));
            },
            191 => static function ($self, $stackPos) {
                 $self->semValue = null;
            },
            192 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Finally_($self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            193 => null,
            194 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)]);
            },
            195 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
            },
            196 => static function ($self, $stackPos) {
                 $self->semValue = false;
            },
            197 => static function ($self, $stackPos) {
                 $self->semValue = true;
            },
            198 => static function ($self, $stackPos) {
                 $self->semValue = false;
            },
            199 => static function ($self, $stackPos) {
                 $self->semValue = true;
            },
            200 => static function ($self, $stackPos) {
                 $self->semValue = false;
            },
            201 => static function ($self, $stackPos) {
                 $self->semValue = true;
            },
            202 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(3-2)];
            },
            203 => static function ($self, $stackPos) {
                 $self->semValue = [];
            },
            204 => null,
            205 => static function ($self, $stackPos) {
                 $self->semValue = new Node\Identifier($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            206 => static function ($self, $stackPos) {
                 $self->semValue = new Node\Identifier($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            207 => static function ($self, $stackPos) {
                 $self->semValue = new Node\Identifier($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            208 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Function_($self->semStack[$stackPos-(8-3)], ['byRef' => $self->semStack[$stackPos-(8-2)], 'params' => $self->semStack[$stackPos-(8-5)], 'returnType' => $self->semStack[$stackPos-(8-7)], 'stmts' => $self->semStack[$stackPos-(8-8)], 'attrGroups' => []], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos]));
            },
            209 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Function_($self->semStack[$stackPos-(9-4)], ['byRef' => $self->semStack[$stackPos-(9-3)], 'params' => $self->semStack[$stackPos-(9-6)], 'returnType' => $self->semStack[$stackPos-(9-8)], 'stmts' => $self->semStack[$stackPos-(9-9)], 'attrGroups' => $self->semStack[$stackPos-(9-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos]));
            },
            210 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Class_($self->semStack[$stackPos-(7-2)], ['type' => $self->semStack[$stackPos-(7-1)], 'extends' => $self->semStack[$stackPos-(7-3)], 'implements' => $self->semStack[$stackPos-(7-4)], 'stmts' => $self->semStack[$stackPos-(7-6)], 'attrGroups' => []], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos]));
            $self->checkClass($self->semValue, $stackPos-(7-2));
            },
            211 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Class_($self->semStack[$stackPos-(8-3)], ['type' => $self->semStack[$stackPos-(8-2)], 'extends' => $self->semStack[$stackPos-(8-4)], 'implements' => $self->semStack[$stackPos-(8-5)], 'stmts' => $self->semStack[$stackPos-(8-7)], 'attrGroups' => $self->semStack[$stackPos-(8-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos]));
            $self->checkClass($self->semValue, $stackPos-(8-3));
            },
            212 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Interface_($self->semStack[$stackPos-(7-3)], ['extends' => $self->semStack[$stackPos-(7-4)], 'stmts' => $self->semStack[$stackPos-(7-6)], 'attrGroups' => $self->semStack[$stackPos-(7-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos]));
            $self->checkInterface($self->semValue, $stackPos-(7-3));
            },
            213 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Trait_($self->semStack[$stackPos-(6-3)], ['stmts' => $self->semStack[$stackPos-(6-5)], 'attrGroups' => $self->semStack[$stackPos-(6-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos]));
            },
            214 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Enum_($self->semStack[$stackPos-(8-3)], ['scalarType' => $self->semStack[$stackPos-(8-4)], 'implements' => $self->semStack[$stackPos-(8-5)], 'stmts' => $self->semStack[$stackPos-(8-7)], 'attrGroups' => $self->semStack[$stackPos-(8-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos]));
            $self->checkEnum($self->semValue, $stackPos-(8-3));
            },
            215 => static function ($self, $stackPos) {
                 $self->semValue = null;
            },
            216 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(2-2)];
            },
            217 => static function ($self, $stackPos) {
                 $self->semValue = null;
            },
            218 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(2-2)];
            },
            219 => static function ($self, $stackPos) {
                 $self->semValue = 0;
            },
            220 => null,
            221 => null,
            222 => static function ($self, $stackPos) {
                 $self->checkClassModifier($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)], $stackPos-(2-2)); $self->semValue = $self->semStack[$stackPos-(2-1)] | $self->semStack[$stackPos-(2-2)];
            },
            223 => static function ($self, $stackPos) {
                 $self->semValue = Modifiers::ABSTRACT;
            },
            224 => static function ($self, $stackPos) {
                 $self->semValue = Modifiers::FINAL;
            },
            225 => static function ($self, $stackPos) {
                 $self->semValue = Modifiers::READONLY;
            },
            226 => static function ($self, $stackPos) {
                 $self->semValue = null;
            },
            227 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(2-2)];
            },
            228 => static function ($self, $stackPos) {
                 $self->semValue = array();
            },
            229 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(2-2)];
            },
            230 => static function ($self, $stackPos) {
                 $self->semValue = array();
            },
            231 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(2-2)];
            },
            232 => null,
            233 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)]);
            },
            234 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
            },
            235 => null,
            236 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(4-2)];
            },
            237 => null,
            238 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(4-2)];
            },
            239 => static function ($self, $stackPos) {
                 if ($self->semStack[$stackPos-(1-1)] instanceof Stmt\Block) { $self->semValue = $self->semStack[$stackPos-(1-1)]->stmts; } else if ($self->semStack[$stackPos-(1-1)] === null) { $self->semValue = []; } else { $self->semValue = [$self->semStack[$stackPos-(1-1)]]; };
            },
            240 => static function ($self, $stackPos) {
                 $self->semValue = null;
            },
            241 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(4-2)];
            },
            242 => null,
            243 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)]);
            },
            244 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
            },
            245 => static function ($self, $stackPos) {
                 $self->semValue = new Node\DeclareItem($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            246 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(3-2)];
            },
            247 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(4-3)];
            },
            248 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(4-2)];
            },
            249 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(5-3)];
            },
            250 => static function ($self, $stackPos) {
                 $self->semValue = array();
            },
            251 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)];
            },
            252 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Case_($self->semStack[$stackPos-(4-2)], $self->semStack[$stackPos-(4-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            253 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Case_(null, $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            254 => null,
            255 => null,
            256 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Match_($self->semStack[$stackPos-(7-3)], $self->semStack[$stackPos-(7-6)], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos]));
            },
            257 => static function ($self, $stackPos) {
                 $self->semValue = [];
            },
            258 => null,
            259 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)]);
            },
            260 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
            },
            261 => static function ($self, $stackPos) {
                 $self->semValue = new Node\MatchArm($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            262 => static function ($self, $stackPos) {
                 $self->semValue = new Node\MatchArm(null, $self->semStack[$stackPos-(4-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            263 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(1-1)];
            },
            264 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(4-2)];
            },
            265 => static function ($self, $stackPos) {
                 $self->semValue = array();
            },
            266 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)];
            },
            267 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\ElseIf_($self->semStack[$stackPos-(5-3)], $self->semStack[$stackPos-(5-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]));
            },
            268 => static function ($self, $stackPos) {
                 $self->semValue = array();
            },
            269 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)];
            },
            270 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\ElseIf_($self->semStack[$stackPos-(6-3)], $self->semStack[$stackPos-(6-6)], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos])); $self->fixupAlternativeElse($self->semValue);
            },
            271 => static function ($self, $stackPos) {
                 $self->semValue = null;
            },
            272 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Else_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            273 => static function ($self, $stackPos) {
                 $self->semValue = null;
            },
            274 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Else_($self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); $self->fixupAlternativeElse($self->semValue);
            },
            275 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)], false);
            },
            276 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(2-2)], true);
            },
            277 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)], false);
            },
            278 => static function ($self, $stackPos) {
                 $self->semValue = array($self->fixupArrayDestructuring($self->semStack[$stackPos-(1-1)]), false);
            },
            279 => null,
            280 => static function ($self, $stackPos) {
                 $self->semValue = array();
            },
            281 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)]);
            },
            282 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
            },
            283 => static function ($self, $stackPos) {
                 $self->semValue = 0;
            },
            284 => static function ($self, $stackPos) {
                 $self->checkModifier($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)], $stackPos-(2-2)); $self->semValue = $self->semStack[$stackPos-(2-1)] | $self->semStack[$stackPos-(2-2)];
            },
            285 => static function ($self, $stackPos) {
                 $self->semValue = Modifiers::PUBLIC;
            },
            286 => static function ($self, $stackPos) {
                 $self->semValue = Modifiers::PROTECTED;
            },
            287 => static function ($self, $stackPos) {
                 $self->semValue = Modifiers::PRIVATE;
            },
            288 => static function ($self, $stackPos) {
                 $self->semValue = Modifiers::PUBLIC_SET;
            },
            289 => static function ($self, $stackPos) {
                 $self->semValue = Modifiers::PROTECTED_SET;
            },
            290 => static function ($self, $stackPos) {
                 $self->semValue = Modifiers::PRIVATE_SET;
            },
            291 => static function ($self, $stackPos) {
                 $self->semValue = Modifiers::READONLY;
            },
            292 => static function ($self, $stackPos) {
                 $self->semValue = Modifiers::FINAL;
            },
            293 => static function ($self, $stackPos) {
                 $self->semValue = new Node\Param($self->semStack[$stackPos-(7-6)], null, $self->semStack[$stackPos-(7-3)], $self->semStack[$stackPos-(7-4)], $self->semStack[$stackPos-(7-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(7-2)], $self->semStack[$stackPos-(7-1)], $self->semStack[$stackPos-(7-7)]);
            $self->checkParam($self->semValue);
            $self->addPropertyNameToHooks($self->semValue);
            },
            294 => static function ($self, $stackPos) {
                 $self->semValue = new Node\Param($self->semStack[$stackPos-(9-6)], $self->semStack[$stackPos-(9-8)], $self->semStack[$stackPos-(9-3)], $self->semStack[$stackPos-(9-4)], $self->semStack[$stackPos-(9-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(9-2)], $self->semStack[$stackPos-(9-1)], $self->semStack[$stackPos-(9-9)]);
            $self->checkParam($self->semValue);
            $self->addPropertyNameToHooks($self->semValue);
            },
            295 => static function ($self, $stackPos) {
                 $self->semValue = new Node\Param(new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos])), null, $self->semStack[$stackPos-(6-3)], $self->semStack[$stackPos-(6-4)], $self->semStack[$stackPos-(6-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(6-2)], $self->semStack[$stackPos-(6-1)]);
            },
            296 => null,
            297 => static function ($self, $stackPos) {
                 $self->semValue = new Node\NullableType($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            298 => static function ($self, $stackPos) {
                 $self->semValue = new Node\UnionType($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            299 => null,
            300 => null,
            301 => static function ($self, $stackPos) {
                 $self->semValue = new Node\Name('static', $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            302 => static function ($self, $stackPos) {
                 $self->semValue = $self->handleBuiltinTypes($self->semStack[$stackPos-(1-1)]);
            },
            303 => static function ($self, $stackPos) {
                 $self->semValue = new Node\Identifier('array', $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            304 => static function ($self, $stackPos) {
                 $self->semValue = new Node\Identifier('callable', $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            305 => null,
            306 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(3-2)];
            },
            307 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)]);
            },
            308 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
            },
            309 => null,
            310 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(3-2)];
            },
            311 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)]);
            },
            312 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
            },
            313 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)]);
            },
            314 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
            },
            315 => static function ($self, $stackPos) {
                 $self->semValue = new Node\IntersectionType($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            316 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)]);
            },
            317 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
            },
            318 => static function ($self, $stackPos) {
                 $self->semValue = new Node\IntersectionType($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            319 => null,
            320 => static function ($self, $stackPos) {
                 $self->semValue = new Node\NullableType($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            321 => static function ($self, $stackPos) {
                 $self->semValue = new Node\UnionType($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            322 => null,
            323 => static function ($self, $stackPos) {
                 $self->semValue = null;
            },
            324 => null,
            325 => static function ($self, $stackPos) {
                 $self->semValue = null;
            },
            326 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(2-2)];
            },
            327 => static function ($self, $stackPos) {
                 $self->semValue = null;
            },
            328 => static function ($self, $stackPos) {
                 $self->semValue = array();
            },
            329 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(4-2)];
            },
            330 => static function ($self, $stackPos) {
                 $self->semValue = array();
            },
            331 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(4-2)];
            },
            332 => static function ($self, $stackPos) {
                 $self->semValue = array(new Node\Arg($self->semStack[$stackPos-(4-2)], false, false, $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])));
            },
            333 => static function ($self, $stackPos) {
                 $self->semValue = array(new Node\Arg($self->semStack[$stackPos-(3-1)], false, false, $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)],  $self->tokenEndStack[$stackPos-(3-1)])), $self->semStack[$stackPos-(3-3)]);
            },
            334 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)]);
            },
            335 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
            },
            336 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)]);
            },
            337 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
            },
            338 => static function ($self, $stackPos) {
                 $self->semValue = new Node\Arg($self->semStack[$stackPos-(2-2)], true, false, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            339 => static function ($self, $stackPos) {
                 $self->semValue = new Node\Arg($self->semStack[$stackPos-(2-2)], false, true, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            340 => static function ($self, $stackPos) {
                 $self->semValue = new Node\VariadicPlaceholder($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            341 => static function ($self, $stackPos) {
                 $self->semValue = new Node\ArgPlaceholder(null, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            342 => static function ($self, $stackPos) {
                 $self->semValue = new Node\Arg($self->semStack[$stackPos-(3-3)], false, false, $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(3-1)]);
            },
            343 => static function ($self, $stackPos) {
                 $self->semValue = new Node\ArgPlaceholder($self->semStack[$stackPos-(3-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            344 => static function ($self, $stackPos) {
                 $self->semValue = new Node\Arg($self->semStack[$stackPos-(1-1)], false, false, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            345 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(1-1)];
            },
            346 => null,
            347 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
            },
            348 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)]);
            },
            349 => null,
            350 => null,
            351 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
            },
            352 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)]);
            },
            353 => static function ($self, $stackPos) {
                 $self->semValue = new Node\StaticVar($self->semStack[$stackPos-(1-1)], null, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            354 => static function ($self, $stackPos) {
                 $self->semValue = new Node\StaticVar($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            355 => static function ($self, $stackPos) {
                 if ($self->semStack[$stackPos-(2-2)] !== null) { $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)]; } else { $self->semValue = $self->semStack[$stackPos-(2-1)]; }
            },
            356 => static function ($self, $stackPos) {
                 $self->semValue = array();
            },
            357 => static function ($self, $stackPos) {
                 $nop = $self->maybeCreateZeroLengthNop($self->tokenPos);;
            if ($nop !== null) { $self->semStack[$stackPos-(1-1)][] = $nop; } $self->semValue = $self->semStack[$stackPos-(1-1)];
            },
            358 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\Property($self->semStack[$stackPos-(5-2)], $self->semStack[$stackPos-(5-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(5-3)], $self->semStack[$stackPos-(5-1)]);
            },
            359 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\ClassConst($self->semStack[$stackPos-(5-4)], $self->semStack[$stackPos-(5-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(5-1)]);
            $self->checkClassConst($self->semValue, $stackPos-(5-2));
            },
            360 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\ClassConst($self->semStack[$stackPos-(6-5)], $self->semStack[$stackPos-(6-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(6-1)], $self->semStack[$stackPos-(6-4)]);
            $self->checkClassConst($self->semValue, $stackPos-(6-2));
            },
            361 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\ClassMethod($self->semStack[$stackPos-(10-5)], ['type' => $self->semStack[$stackPos-(10-2)], 'byRef' => $self->semStack[$stackPos-(10-4)], 'params' => $self->semStack[$stackPos-(10-7)], 'returnType' => $self->semStack[$stackPos-(10-9)], 'stmts' => $self->semStack[$stackPos-(10-10)], 'attrGroups' => $self->semStack[$stackPos-(10-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(10-1)], $self->tokenEndStack[$stackPos]));
            $self->checkClassMethod($self->semValue, $stackPos-(10-2));
            },
            362 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\TraitUse($self->semStack[$stackPos-(3-2)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            363 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\EnumCase($self->semStack[$stackPos-(5-3)], $self->semStack[$stackPos-(5-4)], $self->semStack[$stackPos-(5-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]));
            },
            364 => static function ($self, $stackPos) {
                 $self->semValue = null; /* will be skipped */
            },
            365 => static function ($self, $stackPos) {
                 $self->semValue = array();
            },
            366 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(3-2)];
            },
            367 => static function ($self, $stackPos) {
                 $self->semValue = array();
            },
            368 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)];
            },
            369 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\TraitUseAdaptation\Precedence($self->semStack[$stackPos-(4-1)][0], $self->semStack[$stackPos-(4-1)][1], $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            370 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\TraitUseAdaptation\Alias($self->semStack[$stackPos-(5-1)][0], $self->semStack[$stackPos-(5-1)][1], $self->semStack[$stackPos-(5-3)], $self->semStack[$stackPos-(5-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]));
            },
            371 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\TraitUseAdaptation\Alias($self->semStack[$stackPos-(4-1)][0], $self->semStack[$stackPos-(4-1)][1], $self->semStack[$stackPos-(4-3)], null, $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            372 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\TraitUseAdaptation\Alias($self->semStack[$stackPos-(4-1)][0], $self->semStack[$stackPos-(4-1)][1], null, $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            373 => static function ($self, $stackPos) {
                 $self->semValue = new Stmt\TraitUseAdaptation\Alias($self->semStack[$stackPos-(4-1)][0], $self->semStack[$stackPos-(4-1)][1], null, $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            374 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)]);
            },
            375 => null,
            376 => static function ($self, $stackPos) {
                 $self->semValue = array(null, $self->semStack[$stackPos-(1-1)]);
            },
            377 => static function ($self, $stackPos) {
                 $self->semValue = null;
            },
            378 => null,
            379 => null,
            380 => static function ($self, $stackPos) {
                 $self->semValue = 0;
            },
            381 => static function ($self, $stackPos) {
                 $self->semValue = 0;
            },
            382 => null,
            383 => null,
            384 => static function ($self, $stackPos) {
                 $self->checkModifier($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)], $stackPos-(2-2)); $self->semValue = $self->semStack[$stackPos-(2-1)] | $self->semStack[$stackPos-(2-2)];
            },
            385 => static function ($self, $stackPos) {
                 $self->semValue = Modifiers::PUBLIC;
            },
            386 => static function ($self, $stackPos) {
                 $self->semValue = Modifiers::PROTECTED;
            },
            387 => static function ($self, $stackPos) {
                 $self->semValue = Modifiers::PRIVATE;
            },
            388 => static function ($self, $stackPos) {
                 $self->semValue = Modifiers::PUBLIC_SET;
            },
            389 => static function ($self, $stackPos) {
                 $self->semValue = Modifiers::PROTECTED_SET;
            },
            390 => static function ($self, $stackPos) {
                 $self->semValue = Modifiers::PRIVATE_SET;
            },
            391 => static function ($self, $stackPos) {
                 $self->semValue = Modifiers::STATIC;
            },
            392 => static function ($self, $stackPos) {
                 $self->semValue = Modifiers::ABSTRACT;
            },
            393 => static function ($self, $stackPos) {
                 $self->semValue = Modifiers::FINAL;
            },
            394 => static function ($self, $stackPos) {
                 $self->semValue = Modifiers::READONLY;
            },
            395 => null,
            396 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)]);
            },
            397 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
            },
            398 => static function ($self, $stackPos) {
                 $self->semValue = new Node\VarLikeIdentifier(substr($self->semStack[$stackPos-(1-1)], 1), $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            399 => static function ($self, $stackPos) {
                 $self->semValue = new Node\PropertyItem($self->semStack[$stackPos-(1-1)], null, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            400 => static function ($self, $stackPos) {
                 $self->semValue = new Node\PropertyItem($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            401 => static function ($self, $stackPos) {
                 $self->semValue = [];
            },
            402 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)];
            },
            403 => static function ($self, $stackPos) {
                 $self->semValue = [];
            },
            404 => static function ($self, $stackPos) {
                 $self->semValue = new Node\PropertyHook($self->semStack[$stackPos-(5-4)], $self->semStack[$stackPos-(5-5)], ['flags' => $self->semStack[$stackPos-(5-2)], 'byRef' => $self->semStack[$stackPos-(5-3)], 'params' => [], 'attrGroups' => $self->semStack[$stackPos-(5-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]));
            $self->checkPropertyHook($self->semValue, null);
            },
            405 => static function ($self, $stackPos) {
                 $self->semValue = new Node\PropertyHook($self->semStack[$stackPos-(8-4)], $self->semStack[$stackPos-(8-8)], ['flags' => $self->semStack[$stackPos-(8-2)], 'byRef' => $self->semStack[$stackPos-(8-3)], 'params' => $self->semStack[$stackPos-(8-6)], 'attrGroups' => $self->semStack[$stackPos-(8-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos]));
            $self->checkPropertyHook($self->semValue, $stackPos-(8-5));
            },
            406 => static function ($self, $stackPos) {
                 $self->semValue = null;
            },
            407 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(3-2)];
            },
            408 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(3-2)];
            },
            409 => static function ($self, $stackPos) {
                 $self->semValue = 0;
            },
            410 => static function ($self, $stackPos) {
                 $self->checkPropertyHookModifiers($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)], $stackPos-(2-2)); $self->semValue = $self->semStack[$stackPos-(2-1)] | $self->semStack[$stackPos-(2-2)];
            },
            411 => null,
            412 => null,
            413 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
            },
            414 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)]);
            },
            415 => static function ($self, $stackPos) {
                 $self->semValue = array();
            },
            416 => null,
            417 => null,
            418 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Assign($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            419 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Assign($self->fixupArrayDestructuring($self->semStack[$stackPos-(3-1)]), $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            420 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Assign($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            421 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\AssignRef($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            422 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\AssignRef($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            if (!$self->phpVersion->allowsAssignNewByReference()) {
                $self->emitError(new Error('Cannot assign new by reference', $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])));
            }

            },
            423 => null,
            424 => null,
            425 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\FuncCall(new Node\Name($self->semStack[$stackPos-(2-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)],  $self->tokenEndStack[$stackPos-(2-1)])), $self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            426 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Clone_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            427 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\AssignOp\Plus($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            428 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\AssignOp\Minus($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            429 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\AssignOp\Mul($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            430 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\AssignOp\Div($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            431 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\AssignOp\Concat($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            432 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\AssignOp\Mod($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            433 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\AssignOp\BitwiseAnd($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            434 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\AssignOp\BitwiseOr($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            435 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\AssignOp\BitwiseXor($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            436 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\AssignOp\ShiftLeft($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            437 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\AssignOp\ShiftRight($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            438 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\AssignOp\Pow($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            439 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\AssignOp\Coalesce($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            440 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\PostInc($self->semStack[$stackPos-(2-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            441 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\PreInc($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            442 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\PostDec($self->semStack[$stackPos-(2-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            443 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\PreDec($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            444 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\BooleanOr($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            445 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\BooleanAnd($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            446 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\LogicalOr($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            447 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\LogicalAnd($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            448 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\LogicalXor($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            449 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\BitwiseOr($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            450 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\BitwiseAnd($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            451 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\BitwiseAnd($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            452 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\BitwiseXor($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            453 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\Concat($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            454 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\Plus($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            455 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\Minus($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            456 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\Mul($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            457 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\Div($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            458 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\Mod($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            459 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\ShiftLeft($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            460 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\ShiftRight($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            461 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\Pow($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            462 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\UnaryPlus($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            463 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\UnaryMinus($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            464 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BooleanNot($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            465 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BitwiseNot($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            466 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\Identical($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            467 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\NotIdentical($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            468 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\Equal($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            469 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\NotEqual($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            470 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\Spaceship($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            471 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\Smaller($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            472 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\SmallerOrEqual($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            473 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\Greater($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            474 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\GreaterOrEqual($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            475 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Instanceof_($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            476 => static function ($self, $stackPos) {

          $self->semValue = $self->semStack[$stackPos-(3-2)];
          if ($self->semValue instanceof Expr\ArrowFunction) {
              $self->parenthesizedArrowFunctions->offsetSet($self->semValue);
          }

            },
            477 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Ternary($self->semStack[$stackPos-(5-1)], $self->semStack[$stackPos-(5-3)], $self->semStack[$stackPos-(5-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]));
            },
            478 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Ternary($self->semStack[$stackPos-(4-1)], null, $self->semStack[$stackPos-(4-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            479 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\BinaryOp\Coalesce($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            480 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Isset_($self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            481 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Empty_($self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            482 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Include_($self->semStack[$stackPos-(2-2)], Expr\Include_::TYPE_INCLUDE, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            483 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Include_($self->semStack[$stackPos-(2-2)], Expr\Include_::TYPE_INCLUDE_ONCE, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            484 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Eval_($self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            485 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Include_($self->semStack[$stackPos-(2-2)], Expr\Include_::TYPE_REQUIRE, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            486 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Include_($self->semStack[$stackPos-(2-2)], Expr\Include_::TYPE_REQUIRE_ONCE, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            487 => static function ($self, $stackPos) {
                 $attrs = $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]);
            $attrs['kind'] = $self->getIntCastKind($self->semStack[$stackPos-(2-1)]);
            $self->semValue = new Expr\Cast\Int_($self->semStack[$stackPos-(2-2)], $attrs);
            },
            488 => static function ($self, $stackPos) {
                 $attrs = $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]);
            $attrs['kind'] = $self->getFloatCastKind($self->semStack[$stackPos-(2-1)]);
            $self->semValue = new Expr\Cast\Double($self->semStack[$stackPos-(2-2)], $attrs);
            },
            489 => static function ($self, $stackPos) {
                 $attrs = $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]);
            $attrs['kind'] = $self->getStringCastKind($self->semStack[$stackPos-(2-1)]);
            $self->semValue = new Expr\Cast\String_($self->semStack[$stackPos-(2-2)], $attrs);
            },
            490 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Cast\Array_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            491 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Cast\Object_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            492 => static function ($self, $stackPos) {
                 $attrs = $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]);
            $attrs['kind'] = $self->getBoolCastKind($self->semStack[$stackPos-(2-1)]);
            $self->semValue = new Expr\Cast\Bool_($self->semStack[$stackPos-(2-2)], $attrs);
            },
            493 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Cast\Unset_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            494 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Cast\Void_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            495 => static function ($self, $stackPos) {
                 $self->semValue = $self->createExitExpr($self->semStack[$stackPos-(2-1)], $stackPos-(2-1), $self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            496 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\ErrorSuppress($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            497 => null,
            498 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\ShellExec($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            499 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Print_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            500 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Yield_(null, null, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            501 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Yield_($self->semStack[$stackPos-(2-2)], null, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            502 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Yield_($self->semStack[$stackPos-(4-4)], $self->semStack[$stackPos-(4-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            503 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\YieldFrom($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            504 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Throw_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            505 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\ArrowFunction(['static' => false, 'byRef' => $self->semStack[$stackPos-(8-2)], 'params' => $self->semStack[$stackPos-(8-4)], 'returnType' => $self->semStack[$stackPos-(8-6)], 'expr' => $self->semStack[$stackPos-(8-8)], 'attrGroups' => []], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos]));
            },
            506 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\ArrowFunction(['static' => true, 'byRef' => $self->semStack[$stackPos-(9-3)], 'params' => $self->semStack[$stackPos-(9-5)], 'returnType' => $self->semStack[$stackPos-(9-7)], 'expr' => $self->semStack[$stackPos-(9-9)], 'attrGroups' => []], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos]));
            },
            507 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Closure(['static' => false, 'byRef' => $self->semStack[$stackPos-(8-2)], 'params' => $self->semStack[$stackPos-(8-4)], 'uses' => $self->semStack[$stackPos-(8-6)], 'returnType' => $self->semStack[$stackPos-(8-7)], 'stmts' => $self->semStack[$stackPos-(8-8)], 'attrGroups' => []], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos]));
            },
            508 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Closure(['static' => true, 'byRef' => $self->semStack[$stackPos-(9-3)], 'params' => $self->semStack[$stackPos-(9-5)], 'uses' => $self->semStack[$stackPos-(9-7)], 'returnType' => $self->semStack[$stackPos-(9-8)], 'stmts' => $self->semStack[$stackPos-(9-9)], 'attrGroups' => []], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos]));
            },
            509 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\ArrowFunction(['static' => false, 'byRef' => $self->semStack[$stackPos-(9-3)], 'params' => $self->semStack[$stackPos-(9-5)], 'returnType' => $self->semStack[$stackPos-(9-7)], 'expr' => $self->semStack[$stackPos-(9-9)], 'attrGroups' => $self->semStack[$stackPos-(9-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos]));
            },
            510 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\ArrowFunction(['static' => true, 'byRef' => $self->semStack[$stackPos-(10-4)], 'params' => $self->semStack[$stackPos-(10-6)], 'returnType' => $self->semStack[$stackPos-(10-8)], 'expr' => $self->semStack[$stackPos-(10-10)], 'attrGroups' => $self->semStack[$stackPos-(10-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(10-1)], $self->tokenEndStack[$stackPos]));
            },
            511 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Closure(['static' => false, 'byRef' => $self->semStack[$stackPos-(9-3)], 'params' => $self->semStack[$stackPos-(9-5)], 'uses' => $self->semStack[$stackPos-(9-7)], 'returnType' => $self->semStack[$stackPos-(9-8)], 'stmts' => $self->semStack[$stackPos-(9-9)], 'attrGroups' => $self->semStack[$stackPos-(9-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos]));
            },
            512 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Closure(['static' => true, 'byRef' => $self->semStack[$stackPos-(10-4)], 'params' => $self->semStack[$stackPos-(10-6)], 'uses' => $self->semStack[$stackPos-(10-8)], 'returnType' => $self->semStack[$stackPos-(10-9)], 'stmts' => $self->semStack[$stackPos-(10-10)], 'attrGroups' => $self->semStack[$stackPos-(10-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(10-1)], $self->tokenEndStack[$stackPos]));
            },
            513 => static function ($self, $stackPos) {
                 $self->semValue = array(new Stmt\Class_(null, ['type' => $self->semStack[$stackPos-(8-2)], 'extends' => $self->semStack[$stackPos-(8-4)], 'implements' => $self->semStack[$stackPos-(8-5)], 'stmts' => $self->semStack[$stackPos-(8-7)], 'attrGroups' => $self->semStack[$stackPos-(8-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos])), $self->semStack[$stackPos-(8-3)]);
            $self->checkClass($self->semValue[0], -1);
            },
            514 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\New_($self->semStack[$stackPos-(3-2)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            515 => static function ($self, $stackPos) {
                 list($class, $ctorArgs) = $self->semStack[$stackPos-(2-2)]; $self->semValue = new Expr\New_($class, $ctorArgs, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            516 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\New_($self->semStack[$stackPos-(2-2)], [], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            517 => null,
            518 => null,
            519 => static function ($self, $stackPos) {
                 $self->semValue = array();
            },
            520 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(4-3)];
            },
            521 => null,
            522 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)]);
            },
            523 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
            },
            524 => static function ($self, $stackPos) {
                 $self->semValue = new Node\ClosureUse($self->semStack[$stackPos-(2-2)], $self->semStack[$stackPos-(2-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            525 => static function ($self, $stackPos) {
                 $self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            526 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\FuncCall($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            527 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\FuncCall($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            528 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\FuncCall($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            529 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\StaticCall($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-3)], $self->semStack[$stackPos-(4-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            530 => static function ($self, $stackPos) {
                 $self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            531 => null,
            532 => static function ($self, $stackPos) {
                 $self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            533 => static function ($self, $stackPos) {
                 $self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            534 => static function ($self, $stackPos) {
                 $self->semValue = new Name\FullyQualified(substr($self->semStack[$stackPos-(1-1)], 1), $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            535 => static function ($self, $stackPos) {
                 $self->semValue = new Name\Relative(substr($self->semStack[$stackPos-(1-1)], 10), $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            536 => null,
            537 => null,
            538 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(3-2)];
            },
            539 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); $self->errorState = 2;
            },
            540 => null,
            541 => null,
            542 => static function ($self, $stackPos) {
                 $self->semValue = array();
            },
            543 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)]); foreach ($self->semValue as $s) { if ($s instanceof Node\InterpolatedStringPart) { $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '`', $self->phpVersion->supportsUnicodeEscapes()); } };
            },
            544 => static function ($self, $stackPos) {
                 foreach ($self->semStack[$stackPos-(1-1)] as $s) { if ($s instanceof Node\InterpolatedStringPart) { $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '`', $self->phpVersion->supportsUnicodeEscapes()); } }; $self->semValue = $self->semStack[$stackPos-(1-1)];
            },
            545 => static function ($self, $stackPos) {
                 $self->semValue = array();
            },
            546 => null,
            547 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\ConstFetch($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            548 => static function ($self, $stackPos) {
                 $self->semValue = new Scalar\MagicConst\Line($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            549 => static function ($self, $stackPos) {
                 $self->semValue = new Scalar\MagicConst\File($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            550 => static function ($self, $stackPos) {
                 $self->semValue = new Scalar\MagicConst\Dir($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            551 => static function ($self, $stackPos) {
                 $self->semValue = new Scalar\MagicConst\Class_($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            552 => static function ($self, $stackPos) {
                 $self->semValue = new Scalar\MagicConst\Trait_($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            553 => static function ($self, $stackPos) {
                 $self->semValue = new Scalar\MagicConst\Method($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            554 => static function ($self, $stackPos) {
                 $self->semValue = new Scalar\MagicConst\Function_($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            555 => static function ($self, $stackPos) {
                 $self->semValue = new Scalar\MagicConst\Namespace_($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            556 => static function ($self, $stackPos) {
                 $self->semValue = new Scalar\MagicConst\Property($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            557 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\ClassConstFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            558 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\ClassConstFetch($self->semStack[$stackPos-(5-1)], $self->semStack[$stackPos-(5-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]));
            },
            559 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\ClassConstFetch($self->semStack[$stackPos-(3-1)], new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos-(3-3)],  $self->tokenEndStack[$stackPos-(3-3)])), $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); $self->errorState = 2;
            },
            560 => static function ($self, $stackPos) {
                 $attrs = $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]); $attrs['kind'] = Expr\Array_::KIND_SHORT;
            $self->semValue = new Expr\Array_($self->semStack[$stackPos-(3-2)], $attrs);
            },
            561 => static function ($self, $stackPos) {
                 $attrs = $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]); $attrs['kind'] = Expr\Array_::KIND_LONG;
            $self->semValue = new Expr\Array_($self->semStack[$stackPos-(4-3)], $attrs);
            $self->createdArrays->offsetSet($self->semValue);
            },
            562 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(1-1)]; $self->createdArrays->offsetSet($self->semValue);
            },
            563 => static function ($self, $stackPos) {
                 $self->semValue = Scalar\String_::fromString($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]), $self->phpVersion->supportsUnicodeEscapes());
            },
            564 => static function ($self, $stackPos) {
                 $attrs = $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]); $attrs['kind'] = Scalar\String_::KIND_DOUBLE_QUOTED;
            foreach ($self->semStack[$stackPos-(3-2)] as $s) { if ($s instanceof Node\InterpolatedStringPart) { $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '"', $self->phpVersion->supportsUnicodeEscapes()); } }; $self->semValue = new Scalar\InterpolatedString($self->semStack[$stackPos-(3-2)], $attrs);
            },
            565 => static function ($self, $stackPos) {
                 $self->semValue = $self->parseLNumber($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]), $self->phpVersion->allowsInvalidOctals());
            },
            566 => static function ($self, $stackPos) {
                 $self->semValue = Scalar\Float_::fromString($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            567 => null,
            568 => null,
            569 => null,
            570 => static function ($self, $stackPos) {
                 $self->semValue = $self->parseDocString($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-2)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]), $self->getAttributes($self->tokenStartStack[$stackPos-(3-3)],  $self->tokenEndStack[$stackPos-(3-3)]), true);
            },
            571 => static function ($self, $stackPos) {
                 $self->semValue = $self->parseDocString($self->semStack[$stackPos-(2-1)], '', $self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]), $self->getAttributes($self->tokenStartStack[$stackPos-(2-2)],  $self->tokenEndStack[$stackPos-(2-2)]), true);
            },
            572 => static function ($self, $stackPos) {
                 $self->semValue = $self->parseDocString($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-2)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]), $self->getAttributes($self->tokenStartStack[$stackPos-(3-3)],  $self->tokenEndStack[$stackPos-(3-3)]), true);
            },
            573 => static function ($self, $stackPos) {
                 $self->semValue = null;
            },
            574 => null,
            575 => null,
            576 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(3-2)];
            },
            577 => null,
            578 => null,
            579 => null,
            580 => null,
            581 => null,
            582 => null,
            583 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(3-2)];
            },
            584 => null,
            585 => null,
            586 => null,
            587 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\ArrayDimFetch($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            588 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\ArrayDimFetch($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            589 => null,
            590 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\MethodCall($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-3)], $self->semStack[$stackPos-(4-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            591 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\NullsafeMethodCall($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-3)], $self->semStack[$stackPos-(4-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            592 => static function ($self, $stackPos) {
                 $self->semValue = null;
            },
            593 => null,
            594 => null,
            595 => null,
            596 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\PropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            597 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\NullsafePropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            598 => null,
            599 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Variable($self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            600 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Variable($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            601 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Variable(new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])), $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); $self->errorState = 2;
            },
            602 => static function ($self, $stackPos) {
                 $var = $self->semStack[$stackPos-(1-1)]->name; $self->semValue = \is_string($var) ? new Node\VarLikeIdentifier($var, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])) : $var;
            },
            603 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\StaticPropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            604 => null,
            605 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\ArrayDimFetch($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            606 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\ArrayDimFetch($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            607 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\PropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            608 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\NullsafePropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            609 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\StaticPropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            610 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\StaticPropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            611 => null,
            612 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(3-2)];
            },
            613 => null,
            614 => null,
            615 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(3-2)];
            },
            616 => null,
            617 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); $self->errorState = 2;
            },
            618 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\List_($self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); $self->semValue->setAttribute('kind', Expr\List_::KIND_LIST);
            $self->postprocessList($self->semValue);
            },
            619 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(1-1)]; $end = count($self->semValue)-1; if ($self->semValue[$end]->value instanceof Expr\Error) array_pop($self->semValue);
            },
            620 => null,
            621 => static function ($self, $stackPos) {
                 /* do nothing -- prevent default action of $$=$self->semStack[$1]. See $551. */
            },
            622 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
            },
            623 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)]);
            },
            624 => static function ($self, $stackPos) {
                 $self->semValue = new Node\ArrayItem($self->semStack[$stackPos-(1-1)], null, false, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            625 => static function ($self, $stackPos) {
                 $self->semValue = new Node\ArrayItem($self->semStack[$stackPos-(2-2)], null, true, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            626 => static function ($self, $stackPos) {
                 $self->semValue = new Node\ArrayItem($self->semStack[$stackPos-(1-1)], null, false, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            627 => static function ($self, $stackPos) {
                 $self->semValue = new Node\ArrayItem($self->semStack[$stackPos-(3-3)], $self->semStack[$stackPos-(3-1)], false, $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            628 => static function ($self, $stackPos) {
                 $self->semValue = new Node\ArrayItem($self->semStack[$stackPos-(4-4)], $self->semStack[$stackPos-(4-1)], true, $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            629 => static function ($self, $stackPos) {
                 $self->semValue = new Node\ArrayItem($self->semStack[$stackPos-(3-3)], $self->semStack[$stackPos-(3-1)], false, $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            630 => static function ($self, $stackPos) {
                 $self->semValue = new Node\ArrayItem($self->semStack[$stackPos-(2-2)], null, false, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]), true);
            },
            631 => static function ($self, $stackPos) {
                 /* Create an Error node now to remember the position. We'll later either report an error,
             or convert this into a null element, depending on whether this is a creation or destructuring context. */
          $attrs = $self->createEmptyElemAttributes($self->tokenPos);
          $self->semValue = new Node\ArrayItem(new Expr\Error($attrs), null, false, $attrs);
            },
            632 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)];
            },
            633 => static function ($self, $stackPos) {
                 $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)];
            },
            634 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(1-1)]);
            },
            635 => static function ($self, $stackPos) {
                 $self->semValue = array($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)]);
            },
            636 => static function ($self, $stackPos) {
                 $attrs = $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]); $attrs['rawValue'] = $self->semStack[$stackPos-(1-1)]; $self->semValue = new Node\InterpolatedStringPart($self->semStack[$stackPos-(1-1)], $attrs);
            },
            637 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Variable($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            638 => null,
            639 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\ArrayDimFetch($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
            },
            640 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\PropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            641 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\NullsafePropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            642 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Variable($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            643 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\Variable($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
            },
            644 => static function ($self, $stackPos) {
                 $self->semValue = new Expr\ArrayDimFetch($self->semStack[$stackPos-(6-2)], $self->semStack[$stackPos-(6-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos]));
            },
            645 => static function ($self, $stackPos) {
                 $self->semValue = $self->semStack[$stackPos-(3-2)];
            },
            646 => static function ($self, $stackPos) {
                 $self->semValue = new Scalar\String_($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            647 => static function ($self, $stackPos) {
                 $self->semValue = $self->parseNumString($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
            },
            648 => static function ($self, $stackPos) {
                 $self->semValue = $self->parseNumString('-' . $self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
            },
            649 => null,
        ];
    }
}
<?php declare(strict_types=1);

namespace PhpParser;

interface Builder {
    /**
     * Returns the built node.
     *
     * @return Node The built node
     */
    public function getNode(): Node;
}
<?php declare(strict_types=1);

namespace PhpParser;

use PhpParser\Node\Expr;
use PhpParser\Node\Scalar;

use function array_merge;

/**
 * Evaluates constant expressions.
 *
 * This evaluator is able to evaluate all constant expressions (as defined by PHP), which can be
 * evaluated without further context. If a subexpression is not of this type, a user-provided
 * fallback evaluator is invoked. To support all constant expressions that are also supported by
 * PHP (and not already handled by this class), the fallback evaluator must be able to handle the
 * following node types:
 *
 *  * All Scalar\MagicConst\* nodes.
 *  * Expr\ConstFetch nodes. Only null/false/true are already handled by this class.
 *  * Expr\ClassConstFetch nodes.
 *
 * The fallback evaluator should throw ConstExprEvaluationException for nodes it cannot evaluate.
 *
 * The evaluation is dependent on runtime configuration in two respects: Firstly, floating
 * point to string conversions are affected by the precision ini setting. Secondly, they are also
 * affected by the LC_NUMERIC locale.
 */
class ConstExprEvaluator {
    /** @var callable|null */
    private $fallbackEvaluator;

    /**
     * Create a constant expression evaluator.
     *
     * The provided fallback evaluator is invoked whenever a subexpression cannot be evaluated. See
     * class doc comment for more information.
     *
     * @param callable|null $fallbackEvaluator To call if subexpression cannot be evaluated
     */
    public function __construct(?callable $fallbackEvaluator = null) {
        $this->fallbackEvaluator = $fallbackEvaluator ?? function (Expr $expr) {
            throw new ConstExprEvaluationException(
                "Expression of type {$expr->getType()} cannot be evaluated"
            );
        };
    }

    /**
     * Silently evaluates a constant expression into a PHP value.
     *
     * Thrown Errors, warnings or notices will be converted into a ConstExprEvaluationException.
     * The original source of the exception is available through getPrevious().
     *
     * If some part of the expression cannot be evaluated, the fallback evaluator passed to the
     * constructor will be invoked. By default, if no fallback is provided, an exception of type
     * ConstExprEvaluationException is thrown.
     *
     * See class doc comment for caveats and limitations.
     *
     * @param Expr $expr Constant expression to evaluate
     * @return mixed Result of evaluation
     *
     * @throws ConstExprEvaluationException if the expression cannot be evaluated or an error occurred
     */
    public function evaluateSilently(Expr $expr) {
        set_error_handler(function ($num, $str, $file, $line) {
            throw new \ErrorException($str, 0, $num, $file, $line);
        });

        try {
            return $this->evaluate($expr);
        } catch (\Throwable $e) {
            if (!$e instanceof ConstExprEvaluationException) {
                $e = new ConstExprEvaluationException(
                    "An error occurred during constant expression evaluation", 0, $e);
            }
            throw $e;
        } finally {
            restore_error_handler();
        }
    }

    /**
     * Directly evaluates a constant expression into a PHP value.
     *
     * May generate Error exceptions, warnings or notices. Use evaluateSilently() to convert these
     * into a ConstExprEvaluationException.
     *
     * If some part of the expression cannot be evaluated, the fallback evaluator passed to the
     * constructor will be invoked. By default, if no fallback is provided, an exception of type
     * ConstExprEvaluationException is thrown.
     *
     * See class doc comment for caveats and limitations.
     *
     * @param Expr $expr Constant expression to evaluate
     * @return mixed Result of evaluation
     *
     * @throws ConstExprEvaluationException if the expression cannot be evaluated
     */
    public function evaluateDirectly(Expr $expr) {
        return $this->evaluate($expr);
    }

    /** @return mixed */
    private function evaluate(Expr $expr) {
        if ($expr instanceof Scalar\Int_
            || $expr instanceof Scalar\Float_
            || $expr instanceof Scalar\String_
        ) {
            return $expr->value;
        }

        if ($expr instanceof Expr\Array_) {
            return $this->evaluateArray($expr);
        }

        // Unary operators
        if ($expr instanceof Expr\UnaryPlus) {
            return +$this->evaluate($expr->expr);
        }
        if ($expr instanceof Expr\UnaryMinus) {
            return -$this->evaluate($expr->expr);
        }
        if ($expr instanceof Expr\BooleanNot) {
            return !$this->evaluate($expr->expr);
        }
        if ($expr instanceof Expr\BitwiseNot) {
            return ~$this->evaluate($expr->expr);
        }

        if ($expr instanceof Expr\BinaryOp) {
            return $this->evaluateBinaryOp($expr);
        }

        if ($expr instanceof Expr\Ternary) {
            return $this->evaluateTernary($expr);
        }

        if ($expr instanceof Expr\ArrayDimFetch && null !== $expr->dim) {
            return $this->evaluate($expr->var)[$this->evaluate($expr->dim)];
        }

        if ($expr instanceof Expr\ConstFetch) {
            return $this->evaluateConstFetch($expr);
        }

        return ($this->fallbackEvaluator)($expr);
    }

    private function evaluateArray(Expr\Array_ $expr): array {
        $array = [];
        foreach ($expr->items as $item) {
            if (null !== $item->key) {
                $array[$this->evaluate($item->key)] = $this->evaluate($item->value);
            } elseif ($item->unpack) {
                $array = array_merge($array, $this->evaluate($item->value));
            } else {
                $array[] = $this->evaluate($item->value);
            }
        }
        return $array;
    }

    /** @return mixed */
    private function evaluateTernary(Expr\Ternary $expr) {
        if (null === $expr->if) {
            return $this->evaluate($expr->cond) ?: $this->evaluate($expr->else);
        }

        return $this->evaluate($expr->cond)
            ? $this->evaluate($expr->if)
            : $this->evaluate($expr->else);
    }

    /** @return mixed */
    private function evaluateBinaryOp(Expr\BinaryOp $expr) {
        if ($expr instanceof Expr\BinaryOp\Coalesce
            && $expr->left instanceof Expr\ArrayDimFetch
        ) {
            // This needs to be special cased to respect BP_VAR_IS fetch semantics
            return $this->evaluate($expr->left->var)[$this->evaluate($expr->left->dim)]
                ?? $this->evaluate($expr->right);
        }

        // The evaluate() calls are repeated in each branch, because some of the operators are
        // short-circuiting and evaluating the RHS in advance may be illegal in that case
        $l = $expr->left;
        $r = $expr->right;
        switch ($expr->getOperatorSigil()) {
            case '&':   return $this->evaluate($l) &   $this->evaluate($r);
            case '|':   return $this->evaluate($l) |   $this->evaluate($r);
            case '^':   return $this->evaluate($l) ^   $this->evaluate($r);
            case '&&':  return $this->evaluate($l) &&  $this->evaluate($r);
            case '||':  return $this->evaluate($l) ||  $this->evaluate($r);
            case '??':  return $this->evaluate($l) ??  $this->evaluate($r);
            case '.':   return $this->evaluate($l) .   $this->evaluate($r);
            case '/':   return $this->evaluate($l) /   $this->evaluate($r);
            case '==':  return $this->evaluate($l) ==  $this->evaluate($r);
            case '>':   return $this->evaluate($l) >   $this->evaluate($r);
            case '>=':  return $this->evaluate($l) >=  $this->evaluate($r);
            case '===': return $this->evaluate($l) === $this->evaluate($r);
            case 'and': return $this->evaluate($l) and $this->evaluate($r);
            case 'or':  return $this->evaluate($l) or  $this->evaluate($r);
            case 'xor': return $this->evaluate($l) xor $this->evaluate($r);
            case '-':   return $this->evaluate($l) -   $this->evaluate($r);
            case '%':   return $this->evaluate($l) %   $this->evaluate($r);
            case '*':   return $this->evaluate($l) *   $this->evaluate($r);
            case '!=':  return $this->evaluate($l) !=  $this->evaluate($r);
            case '!==': return $this->evaluate($l) !== $this->evaluate($r);
            case '+':   return $this->evaluate($l) +   $this->evaluate($r);
            case '**':  return $this->evaluate($l) **  $this->evaluate($r);
            case '<<':  return $this->evaluate($l) <<  $this->evaluate($r);
            case '>>':  return $this->evaluate($l) >>  $this->evaluate($r);
            case '<':   return $this->evaluate($l) <   $this->evaluate($r);
            case '<=':  return $this->evaluate($l) <=  $this->evaluate($r);
            case '<=>': return $this->evaluate($l) <=> $this->evaluate($r);
            case '|>':  return ($this->fallbackEvaluator)($expr);
        }

        throw new \Exception('Should not happen');
    }

    /** @return mixed */
    private function evaluateConstFetch(Expr\ConstFetch $expr) {
        $name = $expr->name->toLowerString();
        switch ($name) {
            case 'null': return null;
            case 'false': return false;
            case 'true': return true;
        }

        return ($this->fallbackEvaluator)($expr);
    }
}
<?php declare(strict_types=1);

namespace PhpParser;

if (!\function_exists('PhpParser\defineCompatibilityTokens')) {
    function defineCompatibilityTokens(): void {
        $compatTokens = [
            // PHP 8.0
            'T_NAME_QUALIFIED',
            'T_NAME_FULLY_QUALIFIED',
            'T_NAME_RELATIVE',
            'T_MATCH',
            'T_NULLSAFE_OBJECT_OPERATOR',
            'T_ATTRIBUTE',
            // PHP 8.1
            'T_ENUM',
            'T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG',
            'T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG',
            'T_READONLY',
            // PHP 8.4
            'T_PROPERTY_C',
            'T_PUBLIC_SET',
            'T_PROTECTED_SET',
            'T_PRIVATE_SET',
            // PHP 8.5
            'T_PIPE',
            'T_VOID_CAST',
        ];

        // PHP-Parser might be used together with another library that also emulates some or all
        // of these tokens. Perform a sanity-check that all already defined tokens have been
        // assigned a unique ID.
        $usedTokenIds = [];
        foreach ($compatTokens as $token) {
            if (\defined($token)) {
                $tokenId = \constant($token);
                if (!\is_int($tokenId)) {
                    throw new \Error(sprintf(
                        'Token %s has ID of type %s, should be int. ' .
                        'You may be using a library with broken token emulation',
                        $token, \gettype($tokenId)
                    ));
                }
                $clashingToken = $usedTokenIds[$tokenId] ?? null;
                if ($clashingToken !== null) {
                    throw new \Error(sprintf(
                        'Token %s has same ID as token %s, ' .
                        'you may be using a library with broken token emulation',
                        $token, $clashingToken
                    ));
                }
                $usedTokenIds[$tokenId] = $token;
            }
        }

        // Now define any tokens that have not yet been emulated. Try to assign IDs from -1
        // downwards, but skip any IDs that may already be in use.
        $newTokenId = -1;
        foreach ($compatTokens as $token) {
            if (!\defined($token)) {
                while (isset($usedTokenIds[$newTokenId])) {
                    $newTokenId--;
                }
                \define($token, $newTokenId);
                $newTokenId--;
            }
        }
    }

    defineCompatibilityTokens();
}
<?php declare(strict_types=1);

namespace PhpParser;

use PhpParser\Parser\Php7;
use PhpParser\Parser\Php8;

class ParserFactory {
    /**
     * Create a parser targeting the given version on a best-effort basis. The parser will generally
     * accept code for the newest supported version, but will try to accommodate code that becomes
     * invalid in newer versions or changes in interpretation.
     */
    public function createForVersion(PhpVersion $version): Parser {
        if ($version->isHostVersion()) {
            $lexer = new Lexer();
        } else {
            $lexer = new Lexer\Emulative($version);
        }
        if ($version->id >= 80000) {
            return new Php8($lexer, $version);
        }
        return new Php7($lexer, $version);
    }

    /**
     * Create a parser targeting the newest version supported by this library. Code for older
     * versions will be accepted if there have been no relevant backwards-compatibility breaks in
     * PHP.
     */
    public function createForNewestSupportedVersion(): Parser {
        return $this->createForVersion(PhpVersion::getNewestSupported());
    }

    /**
     * Create a parser targeting the host PHP version, that is the PHP version we're currently
     * running on. This parser will not use any token emulation.
     */
    public function createForHostVersion(): Parser {
        return $this->createForVersion(PhpVersion::getHostVersion());
    }
}
<?php declare(strict_types=1);

namespace PhpParser;

/**
 * @codeCoverageIgnore
 */
abstract class NodeVisitorAbstract implements NodeVisitor {
    public function beforeTraverse(array $nodes) {
        return null;
    }

    public function enterNode(Node $node) {
        return null;
    }

    public function leaveNode(Node $node) {
        return null;
    }

    public function afterTraverse(array $nodes) {
        return null;
    }
}
<?php declare(strict_types=1);

namespace PhpParser;

class JsonDecoder {
    /** @var \ReflectionClass<Node>[] Node type to reflection class map */
    private array $reflectionClassCache;

    /** @return mixed */
    public function decode(string $json) {
        $value = json_decode($json, true);
        if (json_last_error()) {
            throw new \RuntimeException('JSON decoding error: ' . json_last_error_msg());
        }

        return $this->decodeRecursive($value);
    }

    /**
     * @param mixed $value
     * @return mixed
     */
    private function decodeRecursive($value) {
        if (\is_array($value)) {
            if (isset($value['nodeType'])) {
                if ($value['nodeType'] === 'Comment' || $value['nodeType'] === 'Comment_Doc') {
                    return $this->decodeComment($value);
                }
                return $this->decodeNode($value);
            }
            return $this->decodeArray($value);
        }
        return $value;
    }

    private function decodeArray(array $array): array {
        $decodedArray = [];
        foreach ($array as $key => $value) {
            $decodedArray[$key] = $this->decodeRecursive($value);
        }
        return $decodedArray;
    }

    private function decodeNode(array $value): Node {
        $nodeType = $value['nodeType'];
        if (!\is_string($nodeType)) {
            throw new \RuntimeException('Node type must be a string');
        }

        $reflectionClass = $this->reflectionClassFromNodeType($nodeType);
        $node = $reflectionClass->newInstanceWithoutConstructor();

        if (isset($value['attributes'])) {
            if (!\is_array($value['attributes'])) {
                throw new \RuntimeException('Attributes must be an array');
            }

            $node->setAttributes($this->decodeArray($value['attributes']));
        }

        foreach ($value as $name => $subNode) {
            if ($name === 'nodeType' || $name === 'attributes') {
                continue;
            }

            $node->$name = $this->decodeRecursive($subNode);
        }

        return $node;
    }

    private function decodeComment(array $value): Comment {
        $className = $value['nodeType'] === 'Comment' ? Comment::class : Comment\Doc::class;
        if (!isset($value['text'])) {
            throw new \RuntimeException('Comment must have text');
        }

        return new $className(
            $value['text'],
            $value['line'] ?? -1, $value['filePos'] ?? -1, $value['tokenPos'] ?? -1,
            $value['endLine'] ?? -1, $value['endFilePos'] ?? -1, $value['endTokenPos'] ?? -1
        );
    }

    /** @return \ReflectionClass<Node> */
    private function reflectionClassFromNodeType(string $nodeType): \ReflectionClass {
        if (!isset($this->reflectionClassCache[$nodeType])) {
            $className = $this->classNameFromNodeType($nodeType);
            $this->reflectionClassCache[$nodeType] = new \ReflectionClass($className);
        }
        return $this->reflectionClassCache[$nodeType];
    }

    /** @return class-string<Node> */
    private function classNameFromNodeType(string $nodeType): string {
        $className = 'PhpParser\\Node\\' . strtr($nodeType, '_', '\\');
        if (class_exists($className)) {
            return $className;
        }

        $className .= '_';
        if (class_exists($className)) {
            return $className;
        }

        throw new \RuntimeException("Unknown node type \"$nodeType\"");
    }
}
<?php declare(strict_types=1);

namespace PhpParser;

interface NodeTraverserInterface {
    /**
     * Adds a visitor.
     *
     * @param NodeVisitor $visitor Visitor to add
     */
    public function addVisitor(NodeVisitor $visitor): void;

    /**
     * Removes an added visitor.
     */
    public function removeVisitor(NodeVisitor $visitor): void;

    /**
     * Traverses an array of nodes using the registered visitors.
     *
     * @param Node[] $nodes Array of nodes
     *
     * @return Node[] Traversed array of nodes
     */
    public function traverse(array $nodes): array;
}
<?php declare(strict_types=1);

namespace PhpParser;

use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\Include_;
use PhpParser\Node\Expr\List_;
use PhpParser\Node\Scalar\Int_;
use PhpParser\Node\Scalar\InterpolatedString;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt\GroupUse;
use PhpParser\Node\Stmt\Use_;
use PhpParser\Node\UseItem;

class NodeDumper {
    private bool $dumpComments;
    private bool $dumpPositions;
    private bool $dumpOtherAttributes;
    private ?string $code;
    private string $res;
    private string $nl;

    private const IGNORE_ATTRIBUTES = [
        'comments' => true,
        'startLine' => true,
        'endLine' => true,
        'startFilePos' => true,
        'endFilePos' => true,
        'startTokenPos' => true,
        'endTokenPos' => true,
    ];

    /**
     * Constructs a NodeDumper.
     *
     * Supported options:
     *  * bool dumpComments: Whether comments should be dumped.
     *  * bool dumpPositions: Whether line/offset information should be dumped. To dump offset
     *                        information, the code needs to be passed to dump().
     *  * bool dumpOtherAttributes: Whether non-comment, non-position attributes should be dumped.
     *
     * @param array $options Options (see description)
     */
    public function __construct(array $options = []) {
        $this->dumpComments = !empty($options['dumpComments']);
        $this->dumpPositions = !empty($options['dumpPositions']);
        $this->dumpOtherAttributes = !empty($options['dumpOtherAttributes']);
    }

    /**
     * Dumps a node or array.
     *
     * @param array|Node $node Node or array to dump
     * @param string|null $code Code corresponding to dumped AST. This only needs to be passed if
     *                          the dumpPositions option is enabled and the dumping of node offsets
     *                          is desired.
     *
     * @return string Dumped value
     */
    public function dump($node, ?string $code = null): string {
        $this->code = $code;
        $this->res = '';
        $this->nl = "\n";
        $this->dumpRecursive($node, false);
        return $this->res;
    }

    /** @param mixed $node */
    protected function dumpRecursive($node, bool $indent = true): void {
        if ($indent) {
            $this->nl .= "    ";
        }
        if ($node instanceof Node) {
            $this->res .= $node->getType();
            if ($this->dumpPositions && null !== $p = $this->dumpPosition($node)) {
                $this->res .= $p;
            }
            $this->res .= '(';

            foreach ($node->getSubNodeNames() as $key) {
                $this->res .= "$this->nl    " . $key . ': ';

                $value = $node->$key;
                if (\is_int($value)) {
                    if ('flags' === $key || 'newModifier' === $key) {
                        $this->res .= $this->dumpFlags($value);
                        continue;
                    }
                    if ('type' === $key && $node instanceof Include_) {
                        $this->res .= $this->dumpIncludeType($value);
                        continue;
                    }
                    if ('type' === $key
                            && ($node instanceof Use_ || $node instanceof UseItem || $node instanceof GroupUse)) {
                        $this->res .= $this->dumpUseType($value);
                        continue;
                    }
                }
                $this->dumpRecursive($value);
            }

            if ($this->dumpComments && $comments = $node->getComments()) {
                $this->res .= "$this->nl    comments: ";
                $this->dumpRecursive($comments);
            }

            if ($this->dumpOtherAttributes) {
                foreach ($node->getAttributes() as $key => $value) {
                    if (isset(self::IGNORE_ATTRIBUTES[$key])) {
                        continue;
                    }

                    $this->res .= "$this->nl    $key: ";
                    if (\is_int($value)) {
                        if ('kind' === $key) {
                            if ($node instanceof Int_) {
                                $this->res .= $this->dumpIntKind($value);
                                continue;
                            }
                            if ($node instanceof String_ || $node instanceof InterpolatedString) {
                                $this->res .= $this->dumpStringKind($value);
                                continue;
                            }
                            if ($node instanceof Array_) {
                                $this->res .= $this->dumpArrayKind($value);
                                continue;
                            }
                            if ($node instanceof List_) {
                                $this->res .= $this->dumpListKind($value);
                                continue;
                            }
                        }
                    }
                    $this->dumpRecursive($value);
                }
            }
            $this->res .= "$this->nl)";
        } elseif (\is_array($node)) {
            $this->res .= 'array(';
            foreach ($node as $key => $value) {
                $this->res .= "$this->nl    " . $key . ': ';
                $this->dumpRecursive($value);
            }
            $this->res .= "$this->nl)";
        } elseif ($node instanceof Comment) {
            $this->res .= \str_replace("\n", $this->nl, $node->getReformattedText());
        } elseif (\is_string($node)) {
            $this->res .= \str_replace("\n", $this->nl, $node);
        } elseif (\is_int($node) || \is_float($node)) {
            $this->res .= $node;
        } elseif (null === $node) {
            $this->res .= 'null';
        } elseif (false === $node) {
            $this->res .= 'false';
        } elseif (true === $node) {
            $this->res .= 'true';
        } else {
            throw new \InvalidArgumentException('Can only dump nodes and arrays.');
        }
        if ($indent) {
            $this->nl = \substr($this->nl, 0, -4);
        }
    }

    protected function dumpFlags(int $flags): string {
        $strs = [];
        if ($flags & Modifiers::PUBLIC) {
            $strs[] = 'PUBLIC';
        }
        if ($flags & Modifiers::PROTECTED) {
            $strs[] = 'PROTECTED';
        }
        if ($flags & Modifiers::PRIVATE) {
            $strs[] = 'PRIVATE';
        }
        if ($flags & Modifiers::ABSTRACT) {
            $strs[] = 'ABSTRACT';
        }
        if ($flags & Modifiers::STATIC) {
            $strs[] = 'STATIC';
        }
        if ($flags & Modifiers::FINAL) {
            $strs[] = 'FINAL';
        }
        if ($flags & Modifiers::READONLY) {
            $strs[] = 'READONLY';
        }
        if ($flags & Modifiers::PUBLIC_SET) {
            $strs[] = 'PUBLIC_SET';
        }
        if ($flags & Modifiers::PROTECTED_SET) {
            $strs[] = 'PROTECTED_SET';
        }
        if ($flags & Modifiers::PRIVATE_SET) {
            $strs[] = 'PRIVATE_SET';
        }

        if ($strs) {
            return implode(' | ', $strs) . ' (' . $flags . ')';
        } else {
            return (string) $flags;
        }
    }

    /** @param array<int, string> $map */
    private function dumpEnum(int $value, array $map): string {
        if (!isset($map[$value])) {
            return (string) $value;
        }
        return $map[$value] . ' (' . $value . ')';
    }

    private function dumpIncludeType(int $type): string {
        return $this->dumpEnum($type, [
            Include_::TYPE_INCLUDE      => 'TYPE_INCLUDE',
            Include_::TYPE_INCLUDE_ONCE => 'TYPE_INCLUDE_ONCE',
            Include_::TYPE_REQUIRE      => 'TYPE_REQUIRE',
            Include_::TYPE_REQUIRE_ONCE => 'TYPE_REQUIRE_ONCE',
        ]);
    }

    private function dumpUseType(int $type): string {
        return $this->dumpEnum($type, [
            Use_::TYPE_UNKNOWN  => 'TYPE_UNKNOWN',
            Use_::TYPE_NORMAL   => 'TYPE_NORMAL',
            Use_::TYPE_FUNCTION => 'TYPE_FUNCTION',
            Use_::TYPE_CONSTANT => 'TYPE_CONSTANT',
        ]);
    }

    private function dumpIntKind(int $kind): string {
        return $this->dumpEnum($kind, [
            Int_::KIND_BIN => 'KIND_BIN',
            Int_::KIND_OCT => 'KIND_OCT',
            Int_::KIND_DEC => 'KIND_DEC',
            Int_::KIND_HEX => 'KIND_HEX',
        ]);
    }

    private function dumpStringKind(int $kind): string {
        return $this->dumpEnum($kind, [
            String_::KIND_SINGLE_QUOTED => 'KIND_SINGLE_QUOTED',
            String_::KIND_DOUBLE_QUOTED => 'KIND_DOUBLE_QUOTED',
            String_::KIND_HEREDOC => 'KIND_HEREDOC',
            String_::KIND_NOWDOC => 'KIND_NOWDOC',
        ]);
    }

    private function dumpArrayKind(int $kind): string {
        return $this->dumpEnum($kind, [
            Array_::KIND_LONG => 'KIND_LONG',
            Array_::KIND_SHORT => 'KIND_SHORT',
        ]);
    }

    private function dumpListKind(int $kind): string {
        return $this->dumpEnum($kind, [
            List_::KIND_LIST => 'KIND_LIST',
            List_::KIND_ARRAY => 'KIND_ARRAY',
        ]);
    }

    /**
     * Dump node position, if possible.
     *
     * @param Node $node Node for which to dump position
     *
     * @return string|null Dump of position, or null if position information not available
     */
    protected function dumpPosition(Node $node): ?string {
        if (!$node->hasAttribute('startLine') || !$node->hasAttribute('endLine')) {
            return null;
        }

        $start = $node->getStartLine();
        $end = $node->getEndLine();
        if ($node->hasAttribute('startFilePos') && $node->hasAttribute('endFilePos')
            && null !== $this->code
        ) {
            $start .= ':' . $this->toColumn($this->code, $node->getStartFilePos());
            $end .= ':' . $this->toColumn($this->code, $node->getEndFilePos());
        }
        return "[$start - $end]";
    }

    // Copied from Error class
    private function toColumn(string $code, int $pos): int {
        if ($pos > strlen($code)) {
            throw new \RuntimeException('Invalid position information');
        }

        $lineStartPos = strrpos($code, "\n", $pos - strlen($code));
        if (false === $lineStartPos) {
            $lineStartPos = -1;
        }

        return $pos - $lineStartPos;
    }
}
<?php declare(strict_types=1);

namespace PhpParser;

abstract class NodeAbstract implements Node, \JsonSerializable {
    /** @var array<string, mixed> Attributes */
    protected array $attributes;

    /**
     * Creates a Node.
     *
     * @param array<string, mixed> $attributes Array of attributes
     */
    public function __construct(array $attributes = []) {
        $this->attributes = $attributes;
    }

    /**
     * Gets line the node started in (alias of getStartLine).
     *
     * @return int Start line (or -1 if not available)
     * @phpstan-return -1|positive-int
     */
    public function getLine(): int {
        return $this->attributes['startLine'] ?? -1;
    }

    /**
     * Gets line the node started in.
     *
     * Requires the 'startLine' attribute to be enabled in the lexer (enabled by default).
     *
     * @return int Start line (or -1 if not available)
     * @phpstan-return -1|positive-int
     */
    public function getStartLine(): int {
        return $this->attributes['startLine'] ?? -1;
    }

    /**
     * Gets the line the node ended in.
     *
     * Requires the 'endLine' attribute to be enabled in the lexer (enabled by default).
     *
     * @return int End line (or -1 if not available)
     * @phpstan-return -1|positive-int
     */
    public function getEndLine(): int {
        return $this->attributes['endLine'] ?? -1;
    }

    /**
     * Gets the token offset of the first token that is part of this node.
     *
     * The offset is an index into the array returned by Lexer::getTokens().
     *
     * Requires the 'startTokenPos' attribute to be enabled in the lexer (DISABLED by default).
     *
     * @return int Token start position (or -1 if not available)
     */
    public function getStartTokenPos(): int {
        return $this->attributes['startTokenPos'] ?? -1;
    }

    /**
     * Gets the token offset of the last token that is part of this node.
     *
     * The offset is an index into the array returned by Lexer::getTokens().
     *
     * Requires the 'endTokenPos' attribute to be enabled in the lexer (DISABLED by default).
     *
     * @return int Token end position (or -1 if not available)
     */
    public function getEndTokenPos(): int {
        return $this->attributes['endTokenPos'] ?? -1;
    }

    /**
     * Gets the file offset of the first character that is part of this node.
     *
     * Requires the 'startFilePos' attribute to be enabled in the lexer (DISABLED by default).
     *
     * @return int File start position (or -1 if not available)
     */
    public function getStartFilePos(): int {
        return $this->attributes['startFilePos'] ?? -1;
    }

    /**
     * Gets the file offset of the last character that is part of this node.
     *
     * Requires the 'endFilePos' attribute to be enabled in the lexer (DISABLED by default).
     *
     * @return int File end position (or -1 if not available)
     */
    public function getEndFilePos(): int {
        return $this->attributes['endFilePos'] ?? -1;
    }

    /**
     * Gets all comments directly preceding this node.
     *
     * The comments are also available through the "comments" attribute.
     *
     * @return Comment[]
     */
    public function getComments(): array {
        return $this->attributes['comments'] ?? [];
    }

    /**
     * Gets the doc comment of the node.
     *
     * @return null|Comment\Doc Doc comment object or null
     */
    public function getDocComment(): ?Comment\Doc {
        $comments = $this->getComments();
        for ($i = count($comments) - 1; $i >= 0; $i--) {
            $comment = $comments[$i];
            if ($comment instanceof Comment\Doc) {
                return $comment;
            }
        }

        return null;
    }

    /**
     * Sets the doc comment of the node.
     *
     * This will either replace an existing doc comment or add it to the comments array.
     *
     * @param Comment\Doc $docComment Doc comment to set
     */
    public function setDocComment(Comment\Doc $docComment): void {
        $comments = $this->getComments();
        for ($i = count($comments) - 1; $i >= 0; $i--) {
            if ($comments[$i] instanceof Comment\Doc) {
                // Replace existing doc comment.
                $comments[$i] = $docComment;
                $this->setAttribute('comments', $comments);
                return;
            }
        }

        // Append new doc comment.
        $comments[] = $docComment;
        $this->setAttribute('comments', $comments);
    }

    public function setAttribute(string $key, $value): void {
        $this->attributes[$key] = $value;
    }

    public function hasAttribute(string $key): bool {
        return array_key_exists($key, $this->attributes);
    }

    public function getAttribute(string $key, $default = null) {
        if (array_key_exists($key, $this->attributes)) {
            return $this->attributes[$key];
        }

        return $default;
    }

    public function getAttributes(): array {
        return $this->attributes;
    }

    public function setAttributes(array $attributes): void {
        $this->attributes = $attributes;
    }

    /**
     * @return array<string, mixed>
     */
    public function jsonSerialize(): array {
        return ['nodeType' => $this->getType()] + get_object_vars($this);
    }
}
<?php declare(strict_types=1);

namespace PhpParser;

class Comment implements \JsonSerializable {
    protected string $text;
    protected int $startLine;
    protected int $startFilePos;
    protected int $startTokenPos;
    protected int $endLine;
    protected int $endFilePos;
    protected int $endTokenPos;

    /**
     * Constructs a comment node.
     *
     * @param string $text Comment text (including comment delimiters like /*)
     * @param int $startLine Line number the comment started on
     * @param int $startFilePos File offset the comment started on
     * @param int $startTokenPos Token offset the comment started on
     */
    public function __construct(
        string $text,
        int $startLine = -1, int $startFilePos = -1, int $startTokenPos = -1,
        int $endLine = -1, int $endFilePos = -1, int $endTokenPos = -1
    ) {
        $this->text = $text;
        $this->startLine = $startLine;
        $this->startFilePos = $startFilePos;
        $this->startTokenPos = $startTokenPos;
        $this->endLine = $endLine;
        $this->endFilePos = $endFilePos;
        $this->endTokenPos = $endTokenPos;
    }

    /**
     * Gets the comment text.
     *
     * @return string The comment text (including comment delimiters like /*)
     */
    public function getText(): string {
        return $this->text;
    }

    /**
     * Gets the line number the comment started on.
     *
     * @return int Line number (or -1 if not available)
     * @phpstan-return -1|positive-int
     */
    public function getStartLine(): int {
        return $this->startLine;
    }

    /**
     * Gets the file offset the comment started on.
     *
     * @return int File offset (or -1 if not available)
     */
    public function getStartFilePos(): int {
        return $this->startFilePos;
    }

    /**
     * Gets the token offset the comment started on.
     *
     * @return int Token offset (or -1 if not available)
     */
    public function getStartTokenPos(): int {
        return $this->startTokenPos;
    }

    /**
     * Gets the line number the comment ends on.
     *
     * @return int Line number (or -1 if not available)
     * @phpstan-return -1|positive-int
     */
    public function getEndLine(): int {
        return $this->endLine;
    }

    /**
     * Gets the file offset the comment ends on.
     *
     * @return int File offset (or -1 if not available)
     */
    public function getEndFilePos(): int {
        return $this->endFilePos;
    }

    /**
     * Gets the token offset the comment ends on.
     *
     * @return int Token offset (or -1 if not available)
     */
    public function getEndTokenPos(): int {
        return $this->endTokenPos;
    }

    /**
     * Gets the comment text.
     *
     * @return string The comment text (including comment delimiters like /*)
     */
    public function __toString(): string {
        return $this->text;
    }

    /**
     * Gets the reformatted comment text.
     *
     * "Reformatted" here means that we try to clean up the whitespace at the
     * starts of the lines. This is necessary because we receive the comments
     * without leading whitespace on the first line, but with leading whitespace
     * on all subsequent lines.
     *
     * Additionally, this normalizes CRLF newlines to LF newlines.
     */
    public function getReformattedText(): string {
        $text = str_replace("\r\n", "\n", $this->text);
        $newlinePos = strpos($text, "\n");
        if (false === $newlinePos) {
            // Single line comments don't need further processing
            return $text;
        }
        if (preg_match('(^.*(?:\n\s+\*.*)+$)', $text)) {
            // Multi line comment of the type
            //
            //     /*
            //      * Some text.
            //      * Some more text.
            //      */
            //
            // is handled by replacing the whitespace sequences before the * by a single space
            return preg_replace('(^\s+\*)m', ' *', $text);
        }
        if (preg_match('(^/\*\*?\s*\n)', $text) && preg_match('(\n(\s*)\*/$)', $text, $matches)) {
            // Multi line comment of the type
            //
            //    /*
            //        Some text.
            //        Some more text.
            //    */
            //
            // is handled by removing the whitespace sequence on the line before the closing
            // */ on all lines. So if the last line is "    */", then "    " is removed at the
            // start of all lines.
            return preg_replace('(^' . preg_quote($matches[1]) . ')m', '', $text);
        }
        if (preg_match('(^/\*\*?\s*(?!\s))', $text, $matches)) {
            // Multi line comment of the type
            //
            //     /* Some text.
            //        Some more text.
            //          Indented text.
            //        Even more text. */
            //
            // is handled by removing the difference between the shortest whitespace prefix on all
            // lines and the length of the "/* " opening sequence.
            $prefixLen = $this->getShortestWhitespacePrefixLen(substr($text, $newlinePos + 1));
            $removeLen = $prefixLen - strlen($matches[0]);
            return preg_replace('(^\s{' . $removeLen . '})m', '', $text);
        }

        // No idea how to format this comment, so simply return as is
        return $text;
    }

    /**
     * Get length of shortest whitespace prefix (at the start of a line).
     *
     * If there is a line with no prefix whitespace, 0 is a valid return value.
     *
     * @param string $str String to check
     * @return int Length in characters. Tabs count as single characters.
     */
    private function getShortestWhitespacePrefixLen(string $str): int {
        $lines = explode("\n", $str);
        $shortestPrefixLen = \PHP_INT_MAX;
        foreach ($lines as $line) {
            preg_match('(^\s*)', $line, $matches);
            $prefixLen = strlen($matches[0]);
            if ($prefixLen < $shortestPrefixLen) {
                $shortestPrefixLen = $prefixLen;
            }
        }
        return $shortestPrefixLen;
    }

    /**
     * @return array{nodeType:string, text:mixed, line:mixed, filePos:mixed}
     */
    public function jsonSerialize(): array {
        // Technically not a node, but we make it look like one anyway
        $type = $this instanceof Comment\Doc ? 'Comment_Doc' : 'Comment';
        return [
            'nodeType' => $type,
            'text' => $this->text,
            // TODO: Rename these to include "start".
            'line' => $this->startLine,
            'filePos' => $this->startFilePos,
            'tokenPos' => $this->startTokenPos,
            'endLine' => $this->endLine,
            'endFilePos' => $this->endFilePos,
            'endTokenPos' => $this->endTokenPos,
        ];
    }
}
<?php declare(strict_types=1);

namespace PhpParser;

use PhpParser\Node\ComplexType;
use PhpParser\Node\Expr;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\NullableType;
use PhpParser\Node\Scalar;
use PhpParser\Node\Stmt;

/**
 * This class defines helpers used in the implementation of builders. Don't use it directly.
 *
 * @internal
 */
final class BuilderHelpers {
    /**
     * Normalizes a node: Converts builder objects to nodes.
     *
     * @param Node|Builder $node The node to normalize
     *
     * @return Node The normalized node
     */
    public static function normalizeNode($node): Node {
        if ($node instanceof Builder) {
            return $node->getNode();
        }

        if ($node instanceof Node) {
            return $node;
        }

        throw new \LogicException('Expected node or builder object');
    }

    /**
     * Normalizes a node to a statement.
     *
     * Expressions are wrapped in a Stmt\Expression node.
     *
     * @param Node|Builder $node The node to normalize
     *
     * @return Stmt The normalized statement node
     */
    public static function normalizeStmt($node): Stmt {
        $node = self::normalizeNode($node);
        if ($node instanceof Stmt) {
            return $node;
        }

        if ($node instanceof Expr) {
            return new Stmt\Expression($node);
        }

        throw new \LogicException('Expected statement or expression node');
    }

    /**
     * Normalizes strings to Identifier.
     *
     * @param string|Identifier $name The identifier to normalize
     *
     * @return Identifier The normalized identifier
     */
    public static function normalizeIdentifier($name): Identifier {
        if ($name instanceof Identifier) {
            return $name;
        }

        if (\is_string($name)) {
            return new Identifier($name);
        }

        throw new \LogicException('Expected string or instance of Node\Identifier');
    }

    /**
     * Normalizes strings to Identifier, also allowing expressions.
     *
     * @param string|Identifier|Expr $name The identifier to normalize
     *
     * @return Identifier|Expr The normalized identifier or expression
     */
    public static function normalizeIdentifierOrExpr($name) {
        if ($name instanceof Identifier || $name instanceof Expr) {
            return $name;
        }

        if (\is_string($name)) {
            return new Identifier($name);
        }

        throw new \LogicException('Expected string or instance of Node\Identifier or Node\Expr');
    }

    /**
     * Normalizes a name: Converts string names to Name nodes.
     *
     * @param Name|string $name The name to normalize
     *
     * @return Name The normalized name
     */
    public static function normalizeName($name): Name {
        if ($name instanceof Name) {
            return $name;
        }

        if (is_string($name)) {
            if (!$name) {
                throw new \LogicException('Name cannot be empty');
            }

            if ($name[0] === '\\') {
                return new Name\FullyQualified(substr($name, 1));
            }

            if (0 === strpos($name, 'namespace\\')) {
                return new Name\Relative(substr($name, strlen('namespace\\')));
            }

            return new Name($name);
        }

        throw new \LogicException('Name must be a string or an instance of Node\Name');
    }

    /**
     * Normalizes a name: Converts string names to Name nodes, while also allowing expressions.
     *
     * @param Expr|Name|string $name The name to normalize
     *
     * @return Name|Expr The normalized name or expression
     */
    public static function normalizeNameOrExpr($name) {
        if ($name instanceof Expr) {
            return $name;
        }

        if (!is_string($name) && !($name instanceof Name)) {
            throw new \LogicException(
                'Name must be a string or an instance of Node\Name or Node\Expr'
            );
        }

        return self::normalizeName($name);
    }

    /**
     * Normalizes a type: Converts plain-text type names into proper AST representation.
     *
     * In particular, builtin types become Identifiers, custom types become Names and nullables
     * are wrapped in NullableType nodes.
     *
     * @param string|Name|Identifier|ComplexType $type The type to normalize
     *
     * @return Name|Identifier|ComplexType The normalized type
     */
    public static function normalizeType($type) {
        if (!is_string($type)) {
            if (
                !$type instanceof Name && !$type instanceof Identifier &&
                !$type instanceof ComplexType
            ) {
                throw new \LogicException(
                    'Type must be a string, or an instance of Name, Identifier or ComplexType'
                );
            }
            return $type;
        }

        $nullable = false;
        if (strlen($type) > 0 && $type[0] === '?') {
            $nullable = true;
            $type = substr($type, 1);
        }

        $builtinTypes = [
            'array',
            'callable',
            'bool',
            'int',
            'float',
            'string',
            'iterable',
            'void',
            'object',
            'null',
            'false',
            'mixed',
            'never',
            'true',
        ];

        $lowerType = strtolower($type);
        if (in_array($lowerType, $builtinTypes)) {
            $type = new Identifier($lowerType);
        } else {
            $type = self::normalizeName($type);
        }

        $notNullableTypes = [
            'void', 'mixed', 'never',
        ];
        if ($nullable && in_array((string) $type, $notNullableTypes)) {
            throw new \LogicException(sprintf('%s type cannot be nullable', $type));
        }

        return $nullable ? new NullableType($type) : $type;
    }

    /**
     * Normalizes a value: Converts nulls, booleans, integers,
     * floats, strings and arrays into their respective nodes
     *
     * @param Node\Expr|bool|null|int|float|string|array|\UnitEnum $value The value to normalize
     *
     * @return Expr The normalized value
     */
    public static function normalizeValue($value): Expr {
        if ($value instanceof Node\Expr) {
            return $value;
        }

        if (is_null($value)) {
            return new Expr\ConstFetch(
                new Name('null')
            );
        }

        if (is_bool($value)) {
            return new Expr\ConstFetch(
                new Name($value ? 'true' : 'false')
            );
        }

        if (is_int($value)) {
            return new Scalar\Int_($value);
        }

        if (is_float($value)) {
            return new Scalar\Float_($value);
        }

        if (is_string($value)) {
            return new Scalar\String_($value);
        }

        if (is_array($value)) {
            $items = [];
            $lastKey = -1;
            foreach ($value as $itemKey => $itemValue) {
                // for consecutive, numeric keys don't generate keys
                if (null !== $lastKey && ++$lastKey === $itemKey) {
                    $items[] = new Node\ArrayItem(
                        self::normalizeValue($itemValue)
                    );
                } else {
                    $lastKey = null;
                    $items[] = new Node\ArrayItem(
                        self::normalizeValue($itemValue),
                        self::normalizeValue($itemKey)
                    );
                }
            }

            return new Expr\Array_($items);
        }

        if ($value instanceof \UnitEnum) {
            return new Expr\ClassConstFetch(new FullyQualified(\get_class($value)), new Identifier($value->name));
        }

        throw new \LogicException('Invalid value');
    }

    /**
     * Normalizes a doc comment: Converts plain strings to PhpParser\Comment\Doc.
     *
     * @param Comment\Doc|string $docComment The doc comment to normalize
     *
     * @return Comment\Doc The normalized doc comment
     */
    public static function normalizeDocComment($docComment): Comment\Doc {
        if ($docComment instanceof Comment\Doc) {
            return $docComment;
        }

        if (is_string($docComment)) {
            return new Comment\Doc($docComment);
        }

        throw new \LogicException('Doc comment must be a string or an instance of PhpParser\Comment\Doc');
    }

    /**
     * Normalizes a attribute: Converts attribute to the Attribute Group if needed.
     *
     * @param Node\Attribute|Node\AttributeGroup $attribute
     *
     * @return Node\AttributeGroup The Attribute Group
     */
    public static function normalizeAttribute($attribute): Node\AttributeGroup {
        if ($attribute instanceof Node\AttributeGroup) {
            return $attribute;
        }

        if (!($attribute instanceof Node\Attribute)) {
            throw new \LogicException('Attribute must be an instance of PhpParser\Node\Attribute or PhpParser\Node\AttributeGroup');
        }

        return new Node\AttributeGroup([$attribute]);
    }

    /**
     * Adds a modifier and returns new modifier bitmask.
     *
     * @param int $modifiers Existing modifiers
     * @param int $modifier Modifier to set
     *
     * @return int New modifiers
     */
    public static function addModifier(int $modifiers, int $modifier): int {
        Modifiers::verifyModifier($modifiers, $modifier);
        return $modifiers | $modifier;
    }

    /**
     * Adds a modifier and returns new modifier bitmask.
     * @return int New modifiers
     */
    public static function addClassModifier(int $existingModifiers, int $modifierToSet): int {
        Modifiers::verifyClassModifier($existingModifiers, $modifierToSet);
        return $existingModifiers | $modifierToSet;
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Internal;

/**
 * Implements the Myers diff algorithm.
 *
 * Myers, Eugene W. "An O (ND) difference algorithm and its variations."
 * Algorithmica 1.1 (1986): 251-266.
 *
 * @template T
 * @internal
 */
class Differ {
    /** @var callable(T, T): bool */
    private $isEqual;

    /**
     * Create differ over the given equality relation.
     *
     * @param callable(T, T): bool $isEqual Equality relation
     */
    public function __construct(callable $isEqual) {
        $this->isEqual = $isEqual;
    }

    /**
     * Calculate diff (edit script) from $old to $new.
     *
     * @param T[] $old Original array
     * @param T[] $new New array
     *
     * @return DiffElem[] Diff (edit script)
     */
    public function diff(array $old, array $new): array {
        $old = \array_values($old);
        $new = \array_values($new);
        list($trace, $x, $y) = $this->calculateTrace($old, $new);
        return $this->extractDiff($trace, $x, $y, $old, $new);
    }

    /**
     * Calculate diff, including "replace" operations.
     *
     * If a sequence of remove operations is followed by the same number of add operations, these
     * will be coalesced into replace operations.
     *
     * @param T[] $old Original array
     * @param T[] $new New array
     *
     * @return DiffElem[] Diff (edit script), including replace operations
     */
    public function diffWithReplacements(array $old, array $new): array {
        return $this->coalesceReplacements($this->diff($old, $new));
    }

    /**
     * @param T[] $old
     * @param T[] $new
     * @return array{array<int, array<int, int>>, int, int}
     */
    private function calculateTrace(array $old, array $new): array {
        $n = \count($old);
        $m = \count($new);
        $max = $n + $m;
        $v = [1 => 0];
        $trace = [];
        for ($d = 0; $d <= $max; $d++) {
            $trace[] = $v;
            for ($k = -$d; $k <= $d; $k += 2) {
                if ($k === -$d || ($k !== $d && $v[$k - 1] < $v[$k + 1])) {
                    $x = $v[$k + 1];
                } else {
                    $x = $v[$k - 1] + 1;
                }

                $y = $x - $k;
                while ($x < $n && $y < $m && ($this->isEqual)($old[$x], $new[$y])) {
                    $x++;
                    $y++;
                }

                $v[$k] = $x;
                if ($x >= $n && $y >= $m) {
                    return [$trace, $x, $y];
                }
            }
        }
        throw new \Exception('Should not happen');
    }

    /**
     * @param array<int, array<int, int>> $trace
     * @param T[] $old
     * @param T[] $new
     * @return DiffElem[]
     */
    private function extractDiff(array $trace, int $x, int $y, array $old, array $new): array {
        $result = [];
        for ($d = \count($trace) - 1; $d >= 0; $d--) {
            $v = $trace[$d];
            $k = $x - $y;

            if ($k === -$d || ($k !== $d && $v[$k - 1] < $v[$k + 1])) {
                $prevK = $k + 1;
            } else {
                $prevK = $k - 1;
            }

            $prevX = $v[$prevK];
            $prevY = $prevX - $prevK;

            while ($x > $prevX && $y > $prevY) {
                $result[] = new DiffElem(DiffElem::TYPE_KEEP, $old[$x - 1], $new[$y - 1]);
                $x--;
                $y--;
            }

            if ($d === 0) {
                break;
            }

            while ($x > $prevX) {
                $result[] = new DiffElem(DiffElem::TYPE_REMOVE, $old[$x - 1], null);
                $x--;
            }

            while ($y > $prevY) {
                $result[] = new DiffElem(DiffElem::TYPE_ADD, null, $new[$y - 1]);
                $y--;
            }
        }
        return array_reverse($result);
    }

    /**
     * Coalesce equal-length sequences of remove+add into a replace operation.
     *
     * @param DiffElem[] $diff
     * @return DiffElem[]
     */
    private function coalesceReplacements(array $diff): array {
        $newDiff = [];
        $c = \count($diff);
        for ($i = 0; $i < $c; $i++) {
            $diffType = $diff[$i]->type;
            if ($diffType !== DiffElem::TYPE_REMOVE) {
                $newDiff[] = $diff[$i];
                continue;
            }

            $j = $i;
            while ($j < $c && $diff[$j]->type === DiffElem::TYPE_REMOVE) {
                $j++;
            }

            $k = $j;
            while ($k < $c && $diff[$k]->type === DiffElem::TYPE_ADD) {
                $k++;
            }

            if ($j - $i === $k - $j) {
                $len = $j - $i;
                for ($n = 0; $n < $len; $n++) {
                    $newDiff[] = new DiffElem(
                        DiffElem::TYPE_REPLACE, $diff[$i + $n]->old, $diff[$j + $n]->new
                    );
                }
            } else {
                for (; $i < $k; $i++) {
                    $newDiff[] = $diff[$i];
                }
            }
            $i = $k - 1;
        }
        return $newDiff;
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Internal;

use PhpParser\Node;
use PhpParser\Node\Expr;

/**
 * This node is used internally by the format-preserving pretty printer to print anonymous classes.
 *
 * The normal anonymous class structure violates assumptions about the order of token offsets.
 * Namely, the constructor arguments are part of the Expr\New_ node and follow the class node, even
 * though they are actually interleaved with them. This special node type is used temporarily to
 * restore a sane token offset order.
 *
 * @internal
 */
class PrintableNewAnonClassNode extends Expr {
    /** @var Node\AttributeGroup[] PHP attribute groups */
    public array $attrGroups;
    /** @var int Modifiers */
    public int $flags;
    /** @var (Node\Arg|Node\VariadicPlaceholder|Node\ArgPlaceholder)[] Arguments */
    public array $args;
    /** @var null|Node\Name Name of extended class */
    public ?Node\Name $extends;
    /** @var Node\Name[] Names of implemented interfaces */
    public array $implements;
    /** @var Node\Stmt[] Statements */
    public array $stmts;

    /**
     * @param Node\AttributeGroup[] $attrGroups PHP attribute groups
     * @param (Node\Arg|Node\VariadicPlaceholder|Node\ArgPlaceholder)[] $args Arguments
     * @param Node\Name|null $extends Name of extended class
     * @param Node\Name[] $implements Names of implemented interfaces
     * @param Node\Stmt[] $stmts Statements
     * @param array<string, mixed> $attributes Attributes
     */
    public function __construct(
        array $attrGroups, int $flags, array $args, ?Node\Name $extends, array $implements,
        array $stmts, array $attributes
    ) {
        parent::__construct($attributes);
        $this->attrGroups = $attrGroups;
        $this->flags = $flags;
        $this->args = $args;
        $this->extends = $extends;
        $this->implements = $implements;
        $this->stmts = $stmts;
    }

    public static function fromNewNode(Expr\New_ $newNode): self {
        $class = $newNode->class;
        assert($class instanceof Node\Stmt\Class_);
        // We don't assert that $class->name is null here, to allow consumers to assign unique names
        // to anonymous classes for their own purposes. We simplify ignore the name here.
        return new self(
            $class->attrGroups, $class->flags, $newNode->args, $class->extends, $class->implements,
            $class->stmts, $newNode->getAttributes()
        );
    }

    public function getType(): string {
        return 'Expr_PrintableNewAnonClass';
    }

    public function getSubNodeNames(): array {
        return ['attrGroups', 'flags', 'args', 'extends', 'implements', 'stmts'];
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Internal;

if (\PHP_VERSION_ID >= 80000) {
    class TokenPolyfill extends \PhpToken {
    }
    return;
}

/**
 * This is a polyfill for the PhpToken class introduced in PHP 8.0. We do not actually polyfill
 * PhpToken, because composer might end up picking a different polyfill implementation, which does
 * not meet our requirements.
 *
 * @internal
 */
class TokenPolyfill {
    /** @var int The ID of the token. Either a T_* constant of a character code < 256. */
    public int $id;
    /** @var string The textual content of the token. */
    public string $text;
    /** @var int The 1-based starting line of the token (or -1 if unknown). */
    public int $line;
    /** @var int The 0-based starting position of the token (or -1 if unknown). */
    public int $pos;

    /** @var array<int, bool> Tokens ignored by the PHP parser. */
    private const IGNORABLE_TOKENS = [
        \T_WHITESPACE => true,
        \T_COMMENT => true,
        \T_DOC_COMMENT => true,
        \T_OPEN_TAG => true,
    ];

    /** @var array<int, bool> Tokens that may be part of a T_NAME_* identifier. */
    private static array $identifierTokens;

    /**
     * Create a Token with the given ID and text, as well optional line and position information.
     */
    final public function __construct(int $id, string $text, int $line = -1, int $pos = -1) {
        $this->id = $id;
        $this->text = $text;
        $this->line = $line;
        $this->pos = $pos;
    }

    /**
     * Get the name of the token. For single-char tokens this will be the token character.
     * Otherwise it will be a T_* style name, or null if the token ID is unknown.
     */
    public function getTokenName(): ?string {
        if ($this->id < 256) {
            return \chr($this->id);
        }

        $name = token_name($this->id);
        return $name === 'UNKNOWN' ? null : $name;
    }

    /**
     * Check whether the token is of the given kind. The kind may be either an integer that matches
     * the token ID, a string that matches the token text, or an array of integers/strings. In the
     * latter case, the function returns true if any of the kinds in the array match.
     *
     * @param int|string|(int|string)[] $kind
     */
    public function is($kind): bool {
        if (\is_int($kind)) {
            return $this->id === $kind;
        }
        if (\is_string($kind)) {
            return $this->text === $kind;
        }
        if (\is_array($kind)) {
            foreach ($kind as $entry) {
                if (\is_int($entry)) {
                    if ($this->id === $entry) {
                        return true;
                    }
                } elseif (\is_string($entry)) {
                    if ($this->text === $entry) {
                        return true;
                    }
                } else {
                    throw new \TypeError(
                        'Argument #1 ($kind) must only have elements of type string|int, ' .
                        gettype($entry) . ' given');
                }
            }
            return false;
        }
        throw new \TypeError(
            'Argument #1 ($kind) must be of type string|int|array, ' .gettype($kind) . ' given');
    }

    /**
     * Check whether this token would be ignored by the PHP parser. Returns true for T_WHITESPACE,
     * T_COMMENT, T_DOC_COMMENT and T_OPEN_TAG, and false for everything else.
     */
    public function isIgnorable(): bool {
        return isset(self::IGNORABLE_TOKENS[$this->id]);
    }

    /**
     * Return the textual content of the token.
     */
    public function __toString(): string {
        return $this->text;
    }

    /**
     * Tokenize the given source code and return an array of tokens.
     *
     * This performs certain canonicalizations to match the PHP 8.0 token format:
     *  * Bad characters are represented using T_BAD_CHARACTER rather than omitted.
     *  * T_COMMENT does not include trailing newlines, instead the newline is part of a following
     *    T_WHITESPACE token.
     *  * Namespaced names are represented using T_NAME_* tokens.
     *
     * @return static[]
     */
    public static function tokenize(string $code, int $flags = 0): array {
        self::init();

        $tokens = [];
        $line = 1;
        $pos = 0;
        $origTokens = \token_get_all($code, $flags);

        $numTokens = \count($origTokens);
        for ($i = 0; $i < $numTokens; $i++) {
            $token = $origTokens[$i];
            if (\is_string($token)) {
                if (\strlen($token) === 2) {
                    // b" and B" are tokenized as single-char tokens, even though they aren't.
                    $tokens[] = new static(\ord('"'), $token, $line, $pos);
                    $pos += 2;
                } else {
                    $tokens[] = new static(\ord($token), $token, $line, $pos);
                    $pos++;
                }
            } else {
                $id = $token[0];
                $text = $token[1];

                // Emulate PHP 8.0 comment format, which does not include trailing whitespace anymore.
                if ($id === \T_COMMENT && \substr($text, 0, 2) !== '/*' &&
                    \preg_match('/(\r\n|\n|\r)$/D', $text, $matches)
                ) {
                    $trailingNewline = $matches[0];
                    $text = \substr($text, 0, -\strlen($trailingNewline));
                    $tokens[] = new static($id, $text, $line, $pos);
                    $pos += \strlen($text);

                    if ($i + 1 < $numTokens && $origTokens[$i + 1][0] === \T_WHITESPACE) {
                        // Move trailing newline into following T_WHITESPACE token, if it already exists.
                        $origTokens[$i + 1][1] = $trailingNewline . $origTokens[$i + 1][1];
                        $origTokens[$i + 1][2]--;
                    } else {
                        // Otherwise, we need to create a new T_WHITESPACE token.
                        $tokens[] = new static(\T_WHITESPACE, $trailingNewline, $line, $pos);
                        $line++;
                        $pos += \strlen($trailingNewline);
                    }
                    continue;
                }

                // Emulate PHP 8.0 T_NAME_* tokens, by combining sequences of T_NS_SEPARATOR and
                // T_STRING into a single token.
                if (($id === \T_NS_SEPARATOR || isset(self::$identifierTokens[$id]))) {
                    $newText = $text;
                    $lastWasSeparator = $id === \T_NS_SEPARATOR;
                    for ($j = $i + 1; $j < $numTokens; $j++) {
                        if ($lastWasSeparator) {
                            if (!isset(self::$identifierTokens[$origTokens[$j][0]])) {
                                break;
                            }
                            $lastWasSeparator = false;
                        } else {
                            if ($origTokens[$j][0] !== \T_NS_SEPARATOR) {
                                break;
                            }
                            $lastWasSeparator = true;
                        }
                        $newText .= $origTokens[$j][1];
                    }
                    if ($lastWasSeparator) {
                        // Trailing separator is not part of the name.
                        $j--;
                        $newText = \substr($newText, 0, -1);
                    }
                    if ($j > $i + 1) {
                        if ($id === \T_NS_SEPARATOR) {
                            $id = \T_NAME_FULLY_QUALIFIED;
                        } elseif ($id === \T_NAMESPACE) {
                            $id = \T_NAME_RELATIVE;
                        } else {
                            $id = \T_NAME_QUALIFIED;
                        }
                        $tokens[] = new static($id, $newText, $line, $pos);
                        $pos += \strlen($newText);
                        $i = $j - 1;
                        continue;
                    }
                }

                $tokens[] = new static($id, $text, $line, $pos);
                $line += \substr_count($text, "\n");
                $pos += \strlen($text);
            }
        }
        return $tokens;
    }

    /** Initialize private static state needed by tokenize(). */
    private static function init(): void {
        if (isset(self::$identifierTokens)) {
            return;
        }

        // Based on semi_reserved production.
        self::$identifierTokens = \array_fill_keys([
            \T_STRING,
            \T_STATIC, \T_ABSTRACT, \T_FINAL, \T_PRIVATE, \T_PROTECTED, \T_PUBLIC, \T_READONLY,
            \T_INCLUDE, \T_INCLUDE_ONCE, \T_EVAL, \T_REQUIRE, \T_REQUIRE_ONCE, \T_LOGICAL_OR, \T_LOGICAL_XOR, \T_LOGICAL_AND,
            \T_INSTANCEOF, \T_NEW, \T_CLONE, \T_EXIT, \T_IF, \T_ELSEIF, \T_ELSE, \T_ENDIF, \T_ECHO, \T_DO, \T_WHILE,
            \T_ENDWHILE, \T_FOR, \T_ENDFOR, \T_FOREACH, \T_ENDFOREACH, \T_DECLARE, \T_ENDDECLARE, \T_AS, \T_TRY, \T_CATCH,
            \T_FINALLY, \T_THROW, \T_USE, \T_INSTEADOF, \T_GLOBAL, \T_VAR, \T_UNSET, \T_ISSET, \T_EMPTY, \T_CONTINUE, \T_GOTO,
            \T_FUNCTION, \T_CONST, \T_RETURN, \T_PRINT, \T_YIELD, \T_LIST, \T_SWITCH, \T_ENDSWITCH, \T_CASE, \T_DEFAULT,
            \T_BREAK, \T_ARRAY, \T_CALLABLE, \T_EXTENDS, \T_IMPLEMENTS, \T_NAMESPACE, \T_TRAIT, \T_INTERFACE, \T_CLASS,
            \T_CLASS_C, \T_TRAIT_C, \T_FUNC_C, \T_METHOD_C, \T_LINE, \T_FILE, \T_DIR, \T_NS_C, \T_HALT_COMPILER, \T_FN,
            \T_MATCH,
        ], true);
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Internal;

use PhpParser\Token;

/**
 * Provides operations on token streams, for use by pretty printer.
 *
 * @internal
 */
class TokenStream {
    /** @var Token[] Tokens (in PhpToken::tokenize() format) */
    private array $tokens;
    /** @var int[] Map from position to indentation */
    private array $indentMap;

    /**
     * Create token stream instance.
     *
     * @param Token[] $tokens Tokens in PhpToken::tokenize() format
     */
    public function __construct(array $tokens, int $tabWidth) {
        $this->tokens = $tokens;
        $this->indentMap = $this->calcIndentMap($tabWidth);
    }

    /**
     * Whether the given position is immediately surrounded by parenthesis.
     *
     * @param int $startPos Start position
     * @param int $endPos End position
     */
    public function haveParens(int $startPos, int $endPos): bool {
        return $this->haveTokenImmediatelyBefore($startPos, '(')
            && $this->haveTokenImmediatelyAfter($endPos, ')');
    }

    /**
     * Whether the given position is immediately surrounded by braces.
     *
     * @param int $startPos Start position
     * @param int $endPos End position
     */
    public function haveBraces(int $startPos, int $endPos): bool {
        return ($this->haveTokenImmediatelyBefore($startPos, '{')
                || $this->haveTokenImmediatelyBefore($startPos, T_CURLY_OPEN))
            && $this->haveTokenImmediatelyAfter($endPos, '}');
    }

    /**
     * Check whether the position is directly preceded by a certain token type.
     *
     * During this check whitespace and comments are skipped.
     *
     * @param int $pos Position before which the token should occur
     * @param int|string $expectedTokenType Token to check for
     *
     * @return bool Whether the expected token was found
     */
    public function haveTokenImmediatelyBefore(int $pos, $expectedTokenType): bool {
        $tokens = $this->tokens;
        $pos--;
        for (; $pos >= 0; $pos--) {
            $token = $tokens[$pos];
            if ($token->is($expectedTokenType)) {
                return true;
            }
            if (!$token->isIgnorable()) {
                break;
            }
        }
        return false;
    }

    /**
     * Check whether the position is directly followed by a certain token type.
     *
     * During this check whitespace and comments are skipped.
     *
     * @param int $pos Position after which the token should occur
     * @param int|string $expectedTokenType Token to check for
     *
     * @return bool Whether the expected token was found
     */
    public function haveTokenImmediatelyAfter(int $pos, $expectedTokenType): bool {
        $tokens = $this->tokens;
        $pos++;
        for ($c = \count($tokens); $pos < $c; $pos++) {
            $token = $tokens[$pos];
            if ($token->is($expectedTokenType)) {
                return true;
            }
            if (!$token->isIgnorable()) {
                break;
            }
        }
        return false;
    }

    /** @param int|string|(int|string)[] $skipTokenType */
    public function skipLeft(int $pos, $skipTokenType): int {
        $tokens = $this->tokens;

        $pos = $this->skipLeftWhitespace($pos);
        if ($skipTokenType === \T_WHITESPACE) {
            return $pos;
        }

        if (!$tokens[$pos]->is($skipTokenType)) {
            // Shouldn't happen. The skip token MUST be there
            throw new \Exception('Encountered unexpected token');
        }
        $pos--;

        return $this->skipLeftWhitespace($pos);
    }

    /** @param int|string|(int|string)[] $skipTokenType */
    public function skipRight(int $pos, $skipTokenType): int {
        $tokens = $this->tokens;

        $pos = $this->skipRightWhitespace($pos);
        if ($skipTokenType === \T_WHITESPACE) {
            return $pos;
        }

        if (!$tokens[$pos]->is($skipTokenType)) {
            // Shouldn't happen. The skip token MUST be there
            throw new \Exception('Encountered unexpected token');
        }
        $pos++;

        return $this->skipRightWhitespace($pos);
    }

    /**
     * Return first non-whitespace token position smaller or equal to passed position.
     *
     * @param int $pos Token position
     * @return int Non-whitespace token position
     */
    public function skipLeftWhitespace(int $pos): int {
        $tokens = $this->tokens;
        for (; $pos >= 0; $pos--) {
            if (!$tokens[$pos]->isIgnorable()) {
                break;
            }
        }
        return $pos;
    }

    /**
     * Return first non-whitespace position greater or equal to passed position.
     *
     * @param int $pos Token position
     * @return int Non-whitespace token position
     */
    public function skipRightWhitespace(int $pos): int {
        $tokens = $this->tokens;
        for ($count = \count($tokens); $pos < $count; $pos++) {
            if (!$tokens[$pos]->isIgnorable()) {
                break;
            }
        }
        return $pos;
    }

    /** @param int|string|(int|string)[] $findTokenType */
    public function findRight(int $pos, $findTokenType): int {
        $tokens = $this->tokens;
        for ($count = \count($tokens); $pos < $count; $pos++) {
            if ($tokens[$pos]->is($findTokenType)) {
                return $pos;
            }
        }
        return -1;
    }

    /**
     * Whether the given position range contains a certain token type.
     *
     * @param int $startPos Starting position (inclusive)
     * @param int $endPos Ending position (exclusive)
     * @param int|string $tokenType Token type to look for
     * @return bool Whether the token occurs in the given range
     */
    public function haveTokenInRange(int $startPos, int $endPos, $tokenType): bool {
        $tokens = $this->tokens;
        for ($pos = $startPos; $pos < $endPos; $pos++) {
            if ($tokens[$pos]->is($tokenType)) {
                return true;
            }
        }
        return false;
    }

    public function haveTagInRange(int $startPos, int $endPos): bool {
        return $this->haveTokenInRange($startPos, $endPos, \T_OPEN_TAG)
            || $this->haveTokenInRange($startPos, $endPos, \T_CLOSE_TAG);
    }

    /**
     * Get indentation before token position.
     *
     * @param int $pos Token position
     *
     * @return int Indentation depth (in spaces)
     */
    public function getIndentationBefore(int $pos): int {
        return $this->indentMap[$pos];
    }

    /**
     * Get the code corresponding to a token offset range, optionally adjusted for indentation.
     *
     * @param int $from Token start position (inclusive)
     * @param int $to Token end position (exclusive)
     * @param int $indent By how much the code should be indented (can be negative as well)
     *
     * @return string Code corresponding to token range, adjusted for indentation
     */
    public function getTokenCode(int $from, int $to, int $indent): string {
        $tokens = $this->tokens;
        $result = '';
        for ($pos = $from; $pos < $to; $pos++) {
            $token = $tokens[$pos];
            $id = $token->id;
            $text = $token->text;
            if ($id === \T_CONSTANT_ENCAPSED_STRING || $id === \T_ENCAPSED_AND_WHITESPACE) {
                $result .= $text;
            } else {
                // TODO Handle non-space indentation
                if ($indent < 0) {
                    $result .= str_replace("\n" . str_repeat(" ", -$indent), "\n", $text);
                } elseif ($indent > 0) {
                    $result .= str_replace("\n", "\n" . str_repeat(" ", $indent), $text);
                } else {
                    $result .= $text;
                }
            }
        }
        return $result;
    }

    /**
     * Precalculate the indentation at every token position.
     *
     * @return int[] Token position to indentation map
     */
    private function calcIndentMap(int $tabWidth): array {
        $indentMap = [];
        $indent = 0;
        foreach ($this->tokens as $i => $token) {
            $indentMap[] = $indent;

            if ($token->id === \T_WHITESPACE) {
                $content = $token->text;
                $newlinePos = \strrpos($content, "\n");
                if (false !== $newlinePos) {
                    $indent = $this->getIndent(\substr($content, $newlinePos + 1), $tabWidth);
                } elseif ($i === 1 && $this->tokens[0]->id === \T_OPEN_TAG &&
                          $this->tokens[0]->text[\strlen($this->tokens[0]->text) - 1] === "\n") {
                    // Special case: Newline at the end of opening tag followed by whitespace.
                    $indent = $this->getIndent($content, $tabWidth);
                }
            }
        }

        // Add a sentinel for one past end of the file
        $indentMap[] = $indent;

        return $indentMap;
    }

    private function getIndent(string $ws, int $tabWidth): int {
        $spaces = \substr_count($ws, " ");
        $tabs = \substr_count($ws, "\t");
        assert(\strlen($ws) === $spaces + $tabs);
        return $spaces + $tabs * $tabWidth;
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Internal;

/**
 * @internal
 */
class DiffElem {
    public const TYPE_KEEP = 0;
    public const TYPE_REMOVE = 1;
    public const TYPE_ADD = 2;
    public const TYPE_REPLACE = 3;

    /** @var int One of the TYPE_* constants */
    public int $type;
    /** @var mixed Is null for add operations */
    public $old;
    /** @var mixed Is null for remove operations */
    public $new;

    /**
     * @param int $type One of the TYPE_* constants
     * @param mixed $old Is null for add operations
     * @param mixed $new Is null for remove operations
     */
    public function __construct(int $type, $old, $new) {
        $this->type = $type;
        $this->old = $old;
        $this->new = $new;
    }
}
<?php declare(strict_types=1);

namespace PhpParser;

/**
 * Modifiers used (as a bit mask) by various flags subnodes, for example on classes, functions,
 * properties and constants.
 */
final class Modifiers {
    public const PUBLIC    =  1;
    public const PROTECTED =  2;
    public const PRIVATE   =  4;
    public const STATIC    =  8;
    public const ABSTRACT  = 16;
    public const FINAL     = 32;
    public const READONLY  = 64;
    public const PUBLIC_SET = 128;
    public const PROTECTED_SET = 256;
    public const PRIVATE_SET = 512;

    public const VISIBILITY_MASK = self::PUBLIC | self::PROTECTED | self::PRIVATE;

    public const VISIBILITY_SET_MASK = self::PUBLIC_SET | self::PROTECTED_SET | self::PRIVATE_SET;

    private const TO_STRING_MAP = [
        self::PUBLIC  => 'public',
        self::PROTECTED => 'protected',
        self::PRIVATE => 'private',
        self::STATIC  => 'static',
        self::ABSTRACT => 'abstract',
        self::FINAL  => 'final',
        self::READONLY  => 'readonly',
        self::PUBLIC_SET => 'public(set)',
        self::PROTECTED_SET => 'protected(set)',
        self::PRIVATE_SET => 'private(set)',
    ];

    public static function toString(int $modifier): string {
        if (!isset(self::TO_STRING_MAP[$modifier])) {
            throw new \InvalidArgumentException("Unknown modifier $modifier");
        }
        return self::TO_STRING_MAP[$modifier];
    }

    private static function isValidModifier(int $modifier): bool {
        $isPow2 = ($modifier & ($modifier - 1)) == 0 && $modifier != 0;
        return $isPow2 && $modifier <= self::PRIVATE_SET;
    }

    /**
     * @internal
     */
    public static function verifyClassModifier(int $a, int $b): void {
        assert(self::isValidModifier($b));
        if (($a & $b) != 0) {
            throw new Error(
                'Multiple ' . self::toString($b) . ' modifiers are not allowed');
        }

        if ($a & 48 && $b & 48) {
            throw new Error('Cannot use the final modifier on an abstract class');
        }
    }

    /**
     * @internal
     */
    public static function verifyModifier(int $a, int $b): void {
        assert(self::isValidModifier($b));
        if (($a & Modifiers::VISIBILITY_MASK && $b & Modifiers::VISIBILITY_MASK) ||
            ($a & Modifiers::VISIBILITY_SET_MASK && $b & Modifiers::VISIBILITY_SET_MASK)
        ) {
            throw new Error('Multiple access type modifiers are not allowed');
        }

        if (($a & $b) != 0) {
            throw new Error(
                'Multiple ' . self::toString($b) . ' modifiers are not allowed');
        }

        if ($a & 48 && $b & 48) {
            throw new Error('Cannot use the final modifier on an abstract class member');
        }
    }
}
<?php declare(strict_types=1);

namespace PhpParser;

use PhpParser\Node\Name;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt;

class NameContext {
    /** @var null|Name Current namespace */
    protected ?Name $namespace;

    /** @var Name[][] Map of format [aliasType => [aliasName => originalName]] */
    protected array $aliases = [];

    /** @var Name[][] Same as $aliases but preserving original case */
    protected array $origAliases = [];

    /** @var ErrorHandler Error handler */
    protected ErrorHandler $errorHandler;

    /**
     * Create a name context.
     *
     * @param ErrorHandler $errorHandler Error handling used to report errors
     */
    public function __construct(ErrorHandler $errorHandler) {
        $this->errorHandler = $errorHandler;
    }

    /**
     * Start a new namespace.
     *
     * This also resets the alias table.
     *
     * @param Name|null $namespace Null is the global namespace
     */
    public function startNamespace(?Name $namespace = null): void {
        $this->namespace = $namespace;
        $this->origAliases = $this->aliases = [
            Stmt\Use_::TYPE_NORMAL   => [],
            Stmt\Use_::TYPE_FUNCTION => [],
            Stmt\Use_::TYPE_CONSTANT => [],
        ];
    }

    /**
     * Add an alias / import.
     *
     * @param Name $name Original name
     * @param string $aliasName Aliased name
     * @param Stmt\Use_::TYPE_* $type One of Stmt\Use_::TYPE_*
     * @param array<string, mixed> $errorAttrs Attributes to use to report an error
     */
    public function addAlias(Name $name, string $aliasName, int $type, array $errorAttrs = []): void {
        // Constant names are case sensitive, everything else case insensitive
        if ($type === Stmt\Use_::TYPE_CONSTANT) {
            $aliasLookupName = $aliasName;
        } else {
            $aliasLookupName = strtolower($aliasName);
        }

        if (isset($this->aliases[$type][$aliasLookupName])) {
            $typeStringMap = [
                Stmt\Use_::TYPE_NORMAL   => '',
                Stmt\Use_::TYPE_FUNCTION => 'function ',
                Stmt\Use_::TYPE_CONSTANT => 'const ',
            ];

            $this->errorHandler->handleError(new Error(
                sprintf(
                    'Cannot use %s%s as %s because the name is already in use',
                    $typeStringMap[$type], $name, $aliasName
                ),
                $errorAttrs
            ));
            return;
        }

        $this->aliases[$type][$aliasLookupName] = $name;
        $this->origAliases[$type][$aliasName] = $name;
    }

    /**
     * Get current namespace.
     *
     * @return null|Name Namespace (or null if global namespace)
     */
    public function getNamespace(): ?Name {
        return $this->namespace;
    }

    /**
     * Get resolved name.
     *
     * @param Name $name Name to resolve
     * @param Stmt\Use_::TYPE_* $type One of Stmt\Use_::TYPE_{FUNCTION|CONSTANT}
     *
     * @return null|Name Resolved name, or null if static resolution is not possible
     */
    public function getResolvedName(Name $name, int $type): ?Name {
        // don't resolve special class names
        if ($type === Stmt\Use_::TYPE_NORMAL && $name->isSpecialClassName()) {
            if (!$name->isUnqualified()) {
                $this->errorHandler->handleError(new Error(
                    sprintf("'\\%s' is an invalid class name", $name->toString()),
                    $name->getAttributes()
                ));
            }
            return $name;
        }

        // fully qualified names are already resolved
        if ($name->isFullyQualified()) {
            return $name;
        }

        // Try to resolve aliases
        if (null !== $resolvedName = $this->resolveAlias($name, $type)) {
            return $resolvedName;
        }

        if ($type !== Stmt\Use_::TYPE_NORMAL && $name->isUnqualified()) {
            if (null === $this->namespace) {
                // outside of a namespace unaliased unqualified is same as fully qualified
                return new FullyQualified($name, $name->getAttributes());
            }

            // Cannot resolve statically
            return null;
        }

        // if no alias exists prepend current namespace
        return FullyQualified::concat($this->namespace, $name, $name->getAttributes());
    }

    /**
     * Get resolved class name.
     *
     * @param Name $name Class ame to resolve
     *
     * @return Name Resolved name
     */
    public function getResolvedClassName(Name $name): Name {
        return $this->getResolvedName($name, Stmt\Use_::TYPE_NORMAL);
    }

    /**
     * Get possible ways of writing a fully qualified name (e.g., by making use of aliases).
     *
     * @param string $name Fully-qualified name (without leading namespace separator)
     * @param Stmt\Use_::TYPE_* $type One of Stmt\Use_::TYPE_*
     *
     * @return Name[] Possible representations of the name
     */
    public function getPossibleNames(string $name, int $type): array {
        $lcName = strtolower($name);

        if ($type === Stmt\Use_::TYPE_NORMAL) {
            // self, parent and static must always be unqualified
            if ($lcName === "self" || $lcName === "parent" || $lcName === "static") {
                return [new Name($name)];
            }
        }

        // Collect possible ways to write this name, starting with the fully-qualified name
        $possibleNames = [new FullyQualified($name)];

        if (null !== $nsRelativeName = $this->getNamespaceRelativeName($name, $lcName, $type)) {
            // Make sure there is no alias that makes the normally namespace-relative name
            // into something else
            if (null === $this->resolveAlias($nsRelativeName, $type)) {
                $possibleNames[] = $nsRelativeName;
            }
        }

        // Check for relevant namespace use statements
        foreach ($this->origAliases[Stmt\Use_::TYPE_NORMAL] as $alias => $orig) {
            $lcOrig = $orig->toLowerString();
            if (0 === strpos($lcName, $lcOrig . '\\')) {
                $possibleNames[] = new Name($alias . substr($name, strlen($lcOrig)));
            }
        }

        // Check for relevant type-specific use statements
        foreach ($this->origAliases[$type] as $alias => $orig) {
            if ($type === Stmt\Use_::TYPE_CONSTANT) {
                // Constants are complicated-sensitive
                $normalizedOrig = $this->normalizeConstName($orig->toString());
                if ($normalizedOrig === $this->normalizeConstName($name)) {
                    $possibleNames[] = new Name($alias);
                }
            } else {
                // Everything else is case-insensitive
                if ($orig->toLowerString() === $lcName) {
                    $possibleNames[] = new Name($alias);
                }
            }
        }

        return $possibleNames;
    }

    /**
     * Get shortest representation of this fully-qualified name.
     *
     * @param string $name Fully-qualified name (without leading namespace separator)
     * @param Stmt\Use_::TYPE_* $type One of Stmt\Use_::TYPE_*
     *
     * @return Name Shortest representation
     */
    public function getShortName(string $name, int $type): Name {
        $possibleNames = $this->getPossibleNames($name, $type);

        // Find shortest name
        $shortestName = null;
        $shortestLength = \INF;
        foreach ($possibleNames as $possibleName) {
            $length = strlen($possibleName->toCodeString());
            if ($length < $shortestLength) {
                $shortestName = $possibleName;
                $shortestLength = $length;
            }
        }

        return $shortestName;
    }

    private function resolveAlias(Name $name, int $type): ?FullyQualified {
        $firstPart = $name->getFirst();

        if ($name->isQualified()) {
            // resolve aliases for qualified names, always against class alias table
            $checkName = strtolower($firstPart);
            if (isset($this->aliases[Stmt\Use_::TYPE_NORMAL][$checkName])) {
                $alias = $this->aliases[Stmt\Use_::TYPE_NORMAL][$checkName];
                return FullyQualified::concat($alias, $name->slice(1), $name->getAttributes());
            }
        } elseif ($name->isUnqualified()) {
            // constant aliases are case-sensitive, function aliases case-insensitive
            $checkName = $type === Stmt\Use_::TYPE_CONSTANT ? $firstPart : strtolower($firstPart);
            if (isset($this->aliases[$type][$checkName])) {
                // resolve unqualified aliases
                return new FullyQualified($this->aliases[$type][$checkName], $name->getAttributes());
            }
        }

        // No applicable aliases
        return null;
    }

    private function getNamespaceRelativeName(string $name, string $lcName, int $type): ?Name {
        if (null === $this->namespace) {
            return new Name($name);
        }

        if ($type === Stmt\Use_::TYPE_CONSTANT) {
            // The constants true/false/null always resolve to the global symbols, even inside a
            // namespace, so they may be used without qualification
            if ($lcName === "true" || $lcName === "false" || $lcName === "null") {
                return new Name($name);
            }
        }

        $namespacePrefix = strtolower($this->namespace . '\\');
        if (0 === strpos($lcName, $namespacePrefix)) {
            return new Name(substr($name, strlen($namespacePrefix)));
        }

        return null;
    }

    private function normalizeConstName(string $name): string {
        $nsSep = strrpos($name, '\\');
        if (false === $nsSep) {
            return $name;
        }

        // Constants have case-insensitive namespace and case-sensitive short-name
        $ns = substr($name, 0, $nsSep);
        $shortName = substr($name, $nsSep + 1);
        return strtolower($ns) . '\\' . $shortName;
    }
}
<?php declare(strict_types=1);

namespace PhpParser\NodeVisitor;

use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;

use function array_pop;
use function count;

/**
 * Visitor that connects a child node to its parent node.
 *
 * With <code>$weakReferences=false</code> on the child node, the parent node can be accessed through
 * <code>$node->getAttribute('parent')</code>.
 *
 * With <code>$weakReferences=true</code> the attribute name is "weak_parent" instead.
 */
final class ParentConnectingVisitor extends NodeVisitorAbstract {
    /**
     * @var Node[]
     */
    private array $stack = [];

    private bool $weakReferences;

    public function __construct(bool $weakReferences = false) {
        $this->weakReferences = $weakReferences;
    }

    public function beforeTraverse(array $nodes) {
        $this->stack = [];
    }

    public function enterNode(Node $node) {
        if (!empty($this->stack)) {
            $parent = $this->stack[count($this->stack) - 1];
            if ($this->weakReferences) {
                $node->setAttribute('weak_parent', \WeakReference::create($parent));
            } else {
                $node->setAttribute('parent', $parent);
            }
        }

        $this->stack[] = $node;
    }

    public function leaveNode(Node $node) {
        array_pop($this->stack);
    }
}
<?php declare(strict_types=1);

namespace PhpParser\NodeVisitor;

use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;

/**
 * This visitor can be used to find and collect all nodes satisfying some criterion determined by
 * a filter callback.
 */
class FindingVisitor extends NodeVisitorAbstract {
    /** @var callable Filter callback */
    protected $filterCallback;
    /** @var list<Node> Found nodes */
    protected array $foundNodes;

    public function __construct(callable $filterCallback) {
        $this->filterCallback = $filterCallback;
    }

    /**
     * Get found nodes satisfying the filter callback.
     *
     * Nodes are returned in pre-order.
     *
     * @return list<Node> Found nodes
     */
    public function getFoundNodes(): array {
        return $this->foundNodes;
    }

    public function beforeTraverse(array $nodes): ?array {
        $this->foundNodes = [];

        return null;
    }

    public function enterNode(Node $node) {
        $filterCallback = $this->filterCallback;
        if ($filterCallback($node)) {
            $this->foundNodes[] = $node;
        }

        return null;
    }
}
<?php declare(strict_types=1);

namespace PhpParser\NodeVisitor;

use PhpParser\Node;
use PhpParser\NodeVisitor;
use PhpParser\NodeVisitorAbstract;

/**
 * This visitor can be used to find the first node satisfying some criterion determined by
 * a filter callback.
 */
class FirstFindingVisitor extends NodeVisitorAbstract {
    /** @var callable Filter callback */
    protected $filterCallback;
    /** @var null|Node Found node */
    protected ?Node $foundNode;

    public function __construct(callable $filterCallback) {
        $this->filterCallback = $filterCallback;
    }

    /**
     * Get found node satisfying the filter callback.
     *
     * Returns null if no node satisfies the filter callback.
     *
     * @return null|Node Found node (or null if not found)
     */
    public function getFoundNode(): ?Node {
        return $this->foundNode;
    }

    public function beforeTraverse(array $nodes): ?array {
        $this->foundNode = null;

        return null;
    }

    public function enterNode(Node $node) {
        $filterCallback = $this->filterCallback;
        if ($filterCallback($node)) {
            $this->foundNode = $node;
            return NodeVisitor::STOP_TRAVERSAL;
        }

        return null;
    }
}
<?php declare(strict_types=1);

namespace PhpParser\NodeVisitor;

use PhpParser\ErrorHandler;
use PhpParser\NameContext;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Name;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt;
use PhpParser\NodeVisitorAbstract;

class NameResolver extends NodeVisitorAbstract {
    /** @var NameContext Naming context */
    protected NameContext $nameContext;

    /** @var bool Whether to preserve original names */
    protected bool $preserveOriginalNames;

    /** @var bool Whether to replace resolved nodes in place, or to add resolvedNode attributes */
    protected bool $replaceNodes;

    /**
     * Constructs a name resolution visitor.
     *
     * Options:
     *  * preserveOriginalNames (default false): An "originalName" attribute will be added to
     *    all name nodes that underwent resolution.
     *  * replaceNodes (default true): Resolved names are replaced in-place. Otherwise, a
     *    resolvedName attribute is added. (Names that cannot be statically resolved receive a
     *    namespacedName attribute, as usual.)
     *
     * @param ErrorHandler|null $errorHandler Error handler
     * @param array{preserveOriginalNames?: bool, replaceNodes?: bool} $options Options
     */
    public function __construct(?ErrorHandler $errorHandler = null, array $options = []) {
        $this->nameContext = new NameContext($errorHandler ?? new ErrorHandler\Throwing());
        $this->preserveOriginalNames = $options['preserveOriginalNames'] ?? false;
        $this->replaceNodes = $options['replaceNodes'] ?? true;
    }

    /**
     * Get name resolution context.
     */
    public function getNameContext(): NameContext {
        return $this->nameContext;
    }

    public function beforeTraverse(array $nodes): ?array {
        $this->nameContext->startNamespace();
        return null;
    }

    public function enterNode(Node $node) {
        if ($node instanceof Stmt\Namespace_) {
            $this->nameContext->startNamespace($node->name);
        } elseif ($node instanceof Stmt\Use_) {
            foreach ($node->uses as $use) {
                $this->addAlias($use, $node->type, null);
            }
        } elseif ($node instanceof Stmt\GroupUse) {
            foreach ($node->uses as $use) {
                $this->addAlias($use, $node->type, $node->prefix);
            }
        } elseif ($node instanceof Stmt\Class_) {
            if (null !== $node->extends) {
                $node->extends = $this->resolveClassName($node->extends);
            }

            foreach ($node->implements as &$interface) {
                $interface = $this->resolveClassName($interface);
            }

            $this->resolveAttrGroups($node);
            if (null !== $node->name) {
                $this->addNamespacedName($node);
            } else {
                $node->namespacedName = null;
            }
        } elseif ($node instanceof Stmt\Interface_) {
            foreach ($node->extends as &$interface) {
                $interface = $this->resolveClassName($interface);
            }

            $this->resolveAttrGroups($node);
            $this->addNamespacedName($node);
        } elseif ($node instanceof Stmt\Enum_) {
            foreach ($node->implements as &$interface) {
                $interface = $this->resolveClassName($interface);
            }

            $this->resolveAttrGroups($node);
            $this->addNamespacedName($node);
        } elseif ($node instanceof Stmt\Trait_) {
            $this->resolveAttrGroups($node);
            $this->addNamespacedName($node);
        } elseif ($node instanceof Stmt\Function_) {
            $this->resolveSignature($node);
            $this->resolveAttrGroups($node);
            $this->addNamespacedName($node);
        } elseif ($node instanceof Stmt\ClassMethod
                  || $node instanceof Expr\Closure
                  || $node instanceof Expr\ArrowFunction
        ) {
            $this->resolveSignature($node);
            $this->resolveAttrGroups($node);
        } elseif ($node instanceof Stmt\Property) {
            if (null !== $node->type) {
                $node->type = $this->resolveType($node->type);
            }
            $this->resolveAttrGroups($node);
        } elseif ($node instanceof Node\PropertyHook) {
            foreach ($node->params as $param) {
                $param->type = $this->resolveType($param->type);
                $this->resolveAttrGroups($param);
            }
            $this->resolveAttrGroups($node);
        } elseif ($node instanceof Stmt\Const_) {
            foreach ($node->consts as $const) {
                $this->addNamespacedName($const);
            }
            $this->resolveAttrGroups($node);
        } elseif ($node instanceof Stmt\ClassConst) {
            if (null !== $node->type) {
                $node->type = $this->resolveType($node->type);
            }
            $this->resolveAttrGroups($node);
        } elseif ($node instanceof Stmt\EnumCase) {
            $this->resolveAttrGroups($node);
        } elseif ($node instanceof Expr\StaticCall
                  || $node instanceof Expr\StaticPropertyFetch
                  || $node instanceof Expr\ClassConstFetch
                  || $node instanceof Expr\New_
                  || $node instanceof Expr\Instanceof_
        ) {
            if ($node->class instanceof Name) {
                $node->class = $this->resolveClassName($node->class);
            }
        } elseif ($node instanceof Stmt\Catch_) {
            foreach ($node->types as &$type) {
                $type = $this->resolveClassName($type);
            }
        } elseif ($node instanceof Expr\FuncCall) {
            if ($node->name instanceof Name) {
                $node->name = $this->resolveName($node->name, Stmt\Use_::TYPE_FUNCTION);
            }
        } elseif ($node instanceof Expr\ConstFetch) {
            $node->name = $this->resolveName($node->name, Stmt\Use_::TYPE_CONSTANT);
        } elseif ($node instanceof Stmt\TraitUse) {
            foreach ($node->traits as &$trait) {
                $trait = $this->resolveClassName($trait);
            }

            foreach ($node->adaptations as $adaptation) {
                if (null !== $adaptation->trait) {
                    $adaptation->trait = $this->resolveClassName($adaptation->trait);
                }

                if ($adaptation instanceof Stmt\TraitUseAdaptation\Precedence) {
                    foreach ($adaptation->insteadof as &$insteadof) {
                        $insteadof = $this->resolveClassName($insteadof);
                    }
                }
            }
        }

        return null;
    }

    /** @param Stmt\Use_::TYPE_* $type */
    private function addAlias(Node\UseItem $use, int $type, ?Name $prefix = null): void {
        // Add prefix for group uses
        $name = $prefix ? Name::concat($prefix, $use->name) : $use->name;
        // Type is determined either by individual element or whole use declaration
        $type |= $use->type;

        $this->nameContext->addAlias(
            $name, (string) $use->getAlias(), $type, $use->getAttributes()
        );
    }

    /** @param Stmt\Function_|Stmt\ClassMethod|Expr\Closure|Expr\ArrowFunction $node */
    private function resolveSignature($node): void {
        foreach ($node->params as $param) {
            $param->type = $this->resolveType($param->type);
            $this->resolveAttrGroups($param);
        }
        $node->returnType = $this->resolveType($node->returnType);
    }

    /**
     * @template T of Node\Identifier|Name|Node\ComplexType|null
     * @param T $node
     * @return T
     */
    private function resolveType(?Node $node): ?Node {
        if ($node instanceof Name) {
            return $this->resolveClassName($node);
        }
        if ($node instanceof Node\NullableType) {
            $node->type = $this->resolveType($node->type);
            return $node;
        }
        if ($node instanceof Node\UnionType || $node instanceof Node\IntersectionType) {
            foreach ($node->types as &$type) {
                $type = $this->resolveType($type);
            }
            return $node;
        }
        return $node;
    }

    /**
     * Resolve name, according to name resolver options.
     *
     * @param Name $name Function or constant name to resolve
     * @param Stmt\Use_::TYPE_* $type One of Stmt\Use_::TYPE_*
     *
     * @return Name Resolved name, or original name with attribute
     */
    protected function resolveName(Name $name, int $type): Name {
        if (!$this->replaceNodes) {
            $resolvedName = $this->nameContext->getResolvedName($name, $type);
            if (null !== $resolvedName) {
                $name->setAttribute('resolvedName', $resolvedName);
            } else {
                $name->setAttribute('namespacedName', FullyQualified::concat(
                    $this->nameContext->getNamespace(), $name, $name->getAttributes()));
            }
            return $name;
        }

        if ($this->preserveOriginalNames) {
            // Save the original name
            $originalName = $name;
            $name = clone $originalName;
            $name->setAttribute('originalName', $originalName);
        }

        $resolvedName = $this->nameContext->getResolvedName($name, $type);
        if (null !== $resolvedName) {
            return $resolvedName;
        }

        // unqualified names inside a namespace cannot be resolved at compile-time
        // add the namespaced version of the name as an attribute
        $name->setAttribute('namespacedName', FullyQualified::concat(
            $this->nameContext->getNamespace(), $name, $name->getAttributes()));
        return $name;
    }

    protected function resolveClassName(Name $name): Name {
        return $this->resolveName($name, Stmt\Use_::TYPE_NORMAL);
    }

    protected function addNamespacedName(Node $node): void {
        $node->namespacedName = Name::concat(
            $this->nameContext->getNamespace(), (string) $node->name);
    }

    protected function resolveAttrGroups(Node $node): void {
        foreach ($node->attrGroups as $attrGroup) {
            foreach ($attrGroup->attrs as $attr) {
                $attr->name = $this->resolveClassName($attr->name);
            }
        }
    }
}
<?php declare(strict_types=1);

namespace PhpParser\NodeVisitor;

use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;

/**
 * Visitor cloning all nodes and linking to the original nodes using an attribute.
 *
 * This visitor is required to perform format-preserving pretty prints.
 */
class CloningVisitor extends NodeVisitorAbstract {
    public function enterNode(Node $origNode) {
        $node = clone $origNode;
        $node->setAttribute('origNode', $origNode);
        return $node;
    }
}
<?php declare(strict_types=1);

namespace PhpParser\NodeVisitor;

use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;

/**
 * Visitor that connects a child node to its parent node
 * as well as its sibling nodes.
 *
 * With <code>$weakReferences=false</code> on the child node, the parent node can be accessed through
 * <code>$node->getAttribute('parent')</code>, the previous
 * node can be accessed through <code>$node->getAttribute('previous')</code>,
 * and the next node can be accessed through <code>$node->getAttribute('next')</code>.
 *
 * With <code>$weakReferences=true</code> attribute names are prefixed by "weak_", e.g. "weak_parent".
 */
final class NodeConnectingVisitor extends NodeVisitorAbstract {
    /**
     * @var Node[]
     */
    private array $stack = [];

    /**
     * @var ?Node
     */
    private $previous;

    private bool $weakReferences;

    public function __construct(bool $weakReferences = false) {
        $this->weakReferences = $weakReferences;
    }

    public function beforeTraverse(array $nodes) {
        $this->stack    = [];
        $this->previous = null;
    }

    public function enterNode(Node $node) {
        if (!empty($this->stack)) {
            $parent = $this->stack[count($this->stack) - 1];
            if ($this->weakReferences) {
                $node->setAttribute('weak_parent', \WeakReference::create($parent));
            } else {
                $node->setAttribute('parent', $parent);
            }
        }

        if ($this->previous !== null) {
            if (
                $this->weakReferences
            ) {
                if ($this->previous->getAttribute('weak_parent') === $node->getAttribute('weak_parent')) {
                    $node->setAttribute('weak_previous', \WeakReference::create($this->previous));
                    $this->previous->setAttribute('weak_next', \WeakReference::create($node));
                }
            } elseif ($this->previous->getAttribute('parent') === $node->getAttribute('parent')) {
                $node->setAttribute('previous', $this->previous);
                $this->previous->setAttribute('next', $node);
            }
        }

        $this->stack[] = $node;
    }

    public function leaveNode(Node $node) {
        $this->previous = $node;

        array_pop($this->stack);
    }
}
<?php declare(strict_types=1);

namespace PhpParser\NodeVisitor;

use PhpParser\Comment;
use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;
use PhpParser\Token;

class CommentAnnotatingVisitor extends NodeVisitorAbstract {
    /** @var int Last seen token start position */
    private int $pos = 0;
    /** @var Token[] Token array */
    private array $tokens;
    /** @var list<int> Token positions of comments */
    private array $commentPositions = [];

    /**
     * Create a comment annotation visitor.
     *
     * @param Token[] $tokens Token array
     */
    public function __construct(array $tokens) {
        $this->tokens = $tokens;

        // Collect positions of comments. We use this to avoid traversing parts of the AST where
        // there are no comments.
        foreach ($tokens as $i => $token) {
            if ($token->id === \T_COMMENT || $token->id === \T_DOC_COMMENT) {
                $this->commentPositions[] = $i;
            }
        }
    }

    public function enterNode(Node $node) {
        $nextCommentPos = current($this->commentPositions);
        if ($nextCommentPos === false) {
            // No more comments.
            return self::STOP_TRAVERSAL;
        }

        $oldPos = $this->pos;
        $this->pos = $pos = $node->getStartTokenPos();
        if ($nextCommentPos > $oldPos && $nextCommentPos < $pos) {
            $comments = [];
            while (--$pos >= $oldPos) {
                $token = $this->tokens[$pos];
                if ($token->id === \T_DOC_COMMENT) {
                    $comments[] = new Comment\Doc(
                        $token->text, $token->line, $token->pos, $pos,
                        $token->getEndLine(), $token->getEndPos() - 1, $pos);
                    continue;
                }
                if ($token->id === \T_COMMENT) {
                    $comments[] = new Comment(
                        $token->text, $token->line, $token->pos, $pos,
                        $token->getEndLine(), $token->getEndPos() - 1, $pos);
                    continue;
                }
                if ($token->id !== \T_WHITESPACE) {
                    break;
                }
            }
            if (!empty($comments)) {
                $node->setAttribute('comments', array_reverse($comments));
            }

            do {
                $nextCommentPos = next($this->commentPositions);
            } while ($nextCommentPos !== false && $nextCommentPos < $this->pos);
        }

        $endPos = $node->getEndTokenPos();
        if ($nextCommentPos > $endPos) {
            // Skip children if there are no comments located inside this node.
            $this->pos = $endPos;
            return self::DONT_TRAVERSE_CHILDREN;
        }

        return null;
    }
}
<?php declare(strict_types=1);

namespace PhpParser;

/**
 * A PHP version, representing only the major and minor version components.
 */
class PhpVersion {
    /** @var int Version ID in PHP_VERSION_ID format */
    public int $id;

    /** @var int[] Minimum versions for builtin types */
    private const BUILTIN_TYPE_VERSIONS = [
        'array'    => 50100,
        'callable' => 50400,
        'bool'     => 70000,
        'int'      => 70000,
        'float'    => 70000,
        'string'   => 70000,
        'iterable' => 70100,
        'void'     => 70100,
        'object'   => 70200,
        'null'     => 80000,
        'false'    => 80000,
        'mixed'    => 80000,
        'never'    => 80100,
        'true'     => 80200,
    ];

    private function __construct(int $id) {
        $this->id = $id;
    }

    /**
     * Create a PhpVersion object from major and minor version components.
     */
    public static function fromComponents(int $major, int $minor): self {
        return new self($major * 10000 + $minor * 100);
    }

    /**
     * Get the newest PHP version supported by this library. Support for this version may be partial,
     * if it is still under development.
     */
    public static function getNewestSupported(): self {
        return self::fromComponents(8, 5);
    }

    /**
     * Get the host PHP version, that is the PHP version we're currently running on.
     */
    public static function getHostVersion(): self {
        return self::fromComponents(\PHP_MAJOR_VERSION, \PHP_MINOR_VERSION);
    }

    /**
     * Parse the version from a string like "8.1".
     */
    public static function fromString(string $version): self {
        if (!preg_match('/^(\d+)\.(\d+)/', $version, $matches)) {
            throw new \LogicException("Invalid PHP version \"$version\"");
        }
        return self::fromComponents((int) $matches[1], (int) $matches[2]);
    }

    /**
     * Check whether two versions are the same.
     */
    public function equals(PhpVersion $other): bool {
        return $this->id === $other->id;
    }

    /**
     * Check whether this version is greater than or equal to the argument.
     */
    public function newerOrEqual(PhpVersion $other): bool {
        return $this->id >= $other->id;
    }

    /**
     * Check whether this version is older than the argument.
     */
    public function older(PhpVersion $other): bool {
        return $this->id < $other->id;
    }

    /**
     * Check whether this is the host PHP version.
     */
    public function isHostVersion(): bool {
        return $this->equals(self::getHostVersion());
    }

    /**
     * Check whether this PHP version supports the given builtin type. Type name must be lowercase.
     */
    public function supportsBuiltinType(string $type): bool {
        $minVersion = self::BUILTIN_TYPE_VERSIONS[$type] ?? null;
        return $minVersion !== null && $this->id >= $minVersion;
    }

    /**
     * Whether this version supports [] array literals.
     */
    public function supportsShortArraySyntax(): bool {
        return $this->id >= 50400;
    }

    /**
     * Whether this version supports [] for destructuring.
     */
    public function supportsShortArrayDestructuring(): bool {
        return $this->id >= 70100;
    }

    /**
     * Whether this version supports flexible heredoc/nowdoc.
     */
    public function supportsFlexibleHeredoc(): bool {
        return $this->id >= 70300;
    }

    /**
     * Whether this version supports trailing commas in parameter lists.
     */
    public function supportsTrailingCommaInParamList(): bool {
        return $this->id >= 80000;
    }

    /**
     * Whether this version allows "$var =& new Obj".
     */
    public function allowsAssignNewByReference(): bool {
        return $this->id < 70000;
    }

    /**
     * Whether this version allows invalid octals like "08".
     */
    public function allowsInvalidOctals(): bool {
        return $this->id < 70000;
    }

    /**
     * Whether this version allows DEL (\x7f) to occur in identifiers.
     */
    public function allowsDelInIdentifiers(): bool {
        return $this->id < 70100;
    }

    /**
     * Whether this version supports yield in expression context without parentheses.
     */
    public function supportsYieldWithoutParentheses(): bool {
        return $this->id >= 70000;
    }

    /**
     * Whether this version supports unicode escape sequences in strings.
     */
    public function supportsUnicodeEscapes(): bool {
        return $this->id >= 70000;
    }

    /*
     * Whether this version supports attributes.
     */
    public function supportsAttributes(): bool {
        return $this->id >= 80000;
    }

    public function supportsNewDereferenceWithoutParentheses(): bool {
        return $this->id >= 80400;
    }
}
<?php declare(strict_types=1);

namespace PhpParser;

interface ErrorHandler {
    /**
     * Handle an error generated during lexing, parsing or some other operation.
     *
     * @param Error $error The error that needs to be handled
     */
    public function handleError(Error $error): void;
}
<?php declare(strict_types=1);

namespace PhpParser;

use PhpParser\Node\Arg;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\BinaryOp\Concat;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt\Use_;

class BuilderFactory {
    /**
     * Creates an attribute node.
     *
     * @param string|Name $name Name of the attribute
     * @param array $args Attribute named arguments
     */
    public function attribute($name, array $args = []): Node\Attribute {
        return new Node\Attribute(
            BuilderHelpers::normalizeName($name),
            $this->args($args)
        );
    }

    /**
     * Creates a namespace builder.
     *
     * @param null|string|Node\Name $name Name of the namespace
     *
     * @return Builder\Namespace_ The created namespace builder
     */
    public function namespace($name): Builder\Namespace_ {
        return new Builder\Namespace_($name);
    }

    /**
     * Creates a class builder.
     *
     * @param string $name Name of the class
     *
     * @return Builder\Class_ The created class builder
     */
    public function class(string $name): Builder\Class_ {
        return new Builder\Class_($name);
    }

    /**
     * Creates an interface builder.
     *
     * @param string $name Name of the interface
     *
     * @return Builder\Interface_ The created interface builder
     */
    public function interface(string $name): Builder\Interface_ {
        return new Builder\Interface_($name);
    }

    /**
     * Creates a trait builder.
     *
     * @param string $name Name of the trait
     *
     * @return Builder\Trait_ The created trait builder
     */
    public function trait(string $name): Builder\Trait_ {
        return new Builder\Trait_($name);
    }

    /**
     * Creates an enum builder.
     *
     * @param string $name Name of the enum
     *
     * @return Builder\Enum_ The created enum builder
     */
    public function enum(string $name): Builder\Enum_ {
        return new Builder\Enum_($name);
    }

    /**
     * Creates a trait use builder.
     *
     * @param Node\Name|string ...$traits Trait names
     *
     * @return Builder\TraitUse The created trait use builder
     */
    public function useTrait(...$traits): Builder\TraitUse {
        return new Builder\TraitUse(...$traits);
    }

    /**
     * Creates a trait use adaptation builder.
     *
     * @param Node\Name|string|null $trait Trait name
     * @param Node\Identifier|string $method Method name
     *
     * @return Builder\TraitUseAdaptation The created trait use adaptation builder
     */
    public function traitUseAdaptation($trait, $method = null): Builder\TraitUseAdaptation {
        if ($method === null) {
            $method = $trait;
            $trait = null;
        }

        return new Builder\TraitUseAdaptation($trait, $method);
    }

    /**
     * Creates a method builder.
     *
     * @param string $name Name of the method
     *
     * @return Builder\Method The created method builder
     */
    public function method(string $name): Builder\Method {
        return new Builder\Method($name);
    }

    /**
     * Creates a parameter builder.
     *
     * @param string $name Name of the parameter
     *
     * @return Builder\Param The created parameter builder
     */
    public function param(string $name): Builder\Param {
        return new Builder\Param($name);
    }

    /**
     * Creates a property builder.
     *
     * @param string $name Name of the property
     *
     * @return Builder\Property The created property builder
     */
    public function property(string $name): Builder\Property {
        return new Builder\Property($name);
    }

    /**
     * Creates a function builder.
     *
     * @param string $name Name of the function
     *
     * @return Builder\Function_ The created function builder
     */
    public function function(string $name): Builder\Function_ {
        return new Builder\Function_($name);
    }

    /**
     * Creates a namespace/class use builder.
     *
     * @param Node\Name|string $name Name of the entity (namespace or class) to alias
     *
     * @return Builder\Use_ The created use builder
     */
    public function use($name): Builder\Use_ {
        return new Builder\Use_($name, Use_::TYPE_NORMAL);
    }

    /**
     * Creates a function use builder.
     *
     * @param Node\Name|string $name Name of the function to alias
     *
     * @return Builder\Use_ The created use function builder
     */
    public function useFunction($name): Builder\Use_ {
        return new Builder\Use_($name, Use_::TYPE_FUNCTION);
    }

    /**
     * Creates a constant use builder.
     *
     * @param Node\Name|string $name Name of the const to alias
     *
     * @return Builder\Use_ The created use const builder
     */
    public function useConst($name): Builder\Use_ {
        return new Builder\Use_($name, Use_::TYPE_CONSTANT);
    }

    /**
     * Creates a class constant builder.
     *
     * @param string|Identifier $name Name
     * @param Node\Expr|bool|null|int|float|string|array $value Value
     *
     * @return Builder\ClassConst The created use const builder
     */
    public function classConst($name, $value): Builder\ClassConst {
        return new Builder\ClassConst($name, $value);
    }

    /**
     * Creates an enum case builder.
     *
     * @param string|Identifier $name Name
     *
     * @return Builder\EnumCase The created use const builder
     */
    public function enumCase($name): Builder\EnumCase {
        return new Builder\EnumCase($name);
    }

    /**
     * Creates node a for a literal value.
     *
     * @param Expr|bool|null|int|float|string|array|\UnitEnum $value $value
     */
    public function val($value): Expr {
        return BuilderHelpers::normalizeValue($value);
    }

    /**
     * Creates variable node.
     *
     * @param string|Expr $name Name
     */
    public function var($name): Expr\Variable {
        if (!\is_string($name) && !$name instanceof Expr) {
            throw new \LogicException('Variable name must be string or Expr');
        }

        return new Expr\Variable($name);
    }

    /**
     * Normalizes an argument list.
     *
     * Creates Arg nodes for all arguments and converts literal values to expressions.
     *
     * @param array $args List of arguments to normalize
     *
     * @return list<Arg>
     */
    public function args(array $args): array {
        $normalizedArgs = [];
        foreach ($args as $key => $arg) {
            if (!($arg instanceof Arg)) {
                $arg = new Arg(BuilderHelpers::normalizeValue($arg));
            }
            if (\is_string($key)) {
                $arg->name = BuilderHelpers::normalizeIdentifier($key);
            }
            $normalizedArgs[] = $arg;
        }
        return $normalizedArgs;
    }

    /**
     * Creates a function call node.
     *
     * @param string|Name|Expr $name Function name
     * @param array $args Function arguments
     */
    public function funcCall($name, array $args = []): Expr\FuncCall {
        return new Expr\FuncCall(
            BuilderHelpers::normalizeNameOrExpr($name),
            $this->args($args)
        );
    }

    /**
     * Creates a method call node.
     *
     * @param Expr $var Variable the method is called on
     * @param string|Identifier|Expr $name Method name
     * @param array $args Method arguments
     */
    public function methodCall(Expr $var, $name, array $args = []): Expr\MethodCall {
        return new Expr\MethodCall(
            $var,
            BuilderHelpers::normalizeIdentifierOrExpr($name),
            $this->args($args)
        );
    }

    /**
     * Creates a static method call node.
     *
     * @param string|Name|Expr $class Class name
     * @param string|Identifier|Expr $name Method name
     * @param array $args Method arguments
     */
    public function staticCall($class, $name, array $args = []): Expr\StaticCall {
        return new Expr\StaticCall(
            BuilderHelpers::normalizeNameOrExpr($class),
            BuilderHelpers::normalizeIdentifierOrExpr($name),
            $this->args($args)
        );
    }

    /**
     * Creates an object creation node.
     *
     * @param string|Name|Expr $class Class name
     * @param array $args Constructor arguments
     */
    public function new($class, array $args = []): Expr\New_ {
        return new Expr\New_(
            BuilderHelpers::normalizeNameOrExpr($class),
            $this->args($args)
        );
    }

    /**
     * Creates a constant fetch node.
     *
     * @param string|Name $name Constant name
     */
    public function constFetch($name): Expr\ConstFetch {
        return new Expr\ConstFetch(BuilderHelpers::normalizeName($name));
    }

    /**
     * Creates a property fetch node.
     *
     * @param Expr $var Variable holding object
     * @param string|Identifier|Expr $name Property name
     */
    public function propertyFetch(Expr $var, $name): Expr\PropertyFetch {
        return new Expr\PropertyFetch($var, BuilderHelpers::normalizeIdentifierOrExpr($name));
    }

    /**
     * Creates a class constant fetch node.
     *
     * @param string|Name|Expr $class Class name
     * @param string|Identifier|Expr $name Constant name
     */
    public function classConstFetch($class, $name): Expr\ClassConstFetch {
        return new Expr\ClassConstFetch(
            BuilderHelpers::normalizeNameOrExpr($class),
            BuilderHelpers::normalizeIdentifierOrExpr($name)
        );
    }

    /**
     * Creates nested Concat nodes from a list of expressions.
     *
     * @param Expr|string ...$exprs Expressions or literal strings
     */
    public function concat(...$exprs): Concat {
        $numExprs = count($exprs);
        if ($numExprs < 2) {
            throw new \LogicException('Expected at least two expressions');
        }

        $lastConcat = $this->normalizeStringExpr($exprs[0]);
        for ($i = 1; $i < $numExprs; $i++) {
            $lastConcat = new Concat($lastConcat, $this->normalizeStringExpr($exprs[$i]));
        }
        return $lastConcat;
    }

    /**
     * @param string|Expr $expr
     */
    private function normalizeStringExpr($expr): Expr {
        if ($expr instanceof Expr) {
            return $expr;
        }

        if (\is_string($expr)) {
            return new String_($expr);
        }

        throw new \LogicException('Expected string or Expr');
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Builder;

use PhpParser;
use PhpParser\BuilderHelpers;
use PhpParser\Node;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt;

class Enum_ extends Declaration {
    protected string $name;
    protected ?Identifier $scalarType = null;
    /** @var list<Name> */
    protected array $implements = [];
    /** @var list<Stmt\TraitUse> */
    protected array $uses = [];
    /** @var list<Stmt\EnumCase> */
    protected array $enumCases = [];
    /** @var list<Stmt\ClassConst> */
    protected array $constants = [];
    /** @var list<Stmt\ClassMethod> */
    protected array $methods = [];
    /** @var list<Node\AttributeGroup> */
    protected array $attributeGroups = [];

    /**
     * Creates an enum builder.
     *
     * @param string $name Name of the enum
     */
    public function __construct(string $name) {
        $this->name = $name;
    }

    /**
     * Sets the scalar type.
     *
     * @param string|Identifier $scalarType
     *
     * @return $this
     */
    public function setScalarType($scalarType) {
        $this->scalarType = BuilderHelpers::normalizeType($scalarType);

        return $this;
    }

    /**
     * Implements one or more interfaces.
     *
     * @param Name|string ...$interfaces Names of interfaces to implement
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function implement(...$interfaces) {
        foreach ($interfaces as $interface) {
            $this->implements[] = BuilderHelpers::normalizeName($interface);
        }

        return $this;
    }

    /**
     * Adds a statement.
     *
     * @param Stmt|PhpParser\Builder $stmt The statement to add
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function addStmt($stmt) {
        $stmt = BuilderHelpers::normalizeNode($stmt);

        if ($stmt instanceof Stmt\EnumCase) {
            $this->enumCases[] = $stmt;
        } elseif ($stmt instanceof Stmt\ClassMethod) {
            $this->methods[] = $stmt;
        } elseif ($stmt instanceof Stmt\TraitUse) {
            $this->uses[] = $stmt;
        } elseif ($stmt instanceof Stmt\ClassConst) {
            $this->constants[] = $stmt;
        } else {
            throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType()));
        }

        return $this;
    }

    /**
     * Adds an attribute group.
     *
     * @param Node\Attribute|Node\AttributeGroup $attribute
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function addAttribute($attribute) {
        $this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);

        return $this;
    }

    /**
     * Returns the built class node.
     *
     * @return Stmt\Enum_ The built enum node
     */
    public function getNode(): PhpParser\Node {
        return new Stmt\Enum_($this->name, [
            'scalarType' => $this->scalarType,
            'implements' => $this->implements,
            'stmts' => array_merge($this->uses, $this->enumCases, $this->constants, $this->methods),
            'attrGroups' => $this->attributeGroups,
        ], $this->attributes);
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Builder;

use PhpParser\Builder;
use PhpParser\BuilderHelpers;
use PhpParser\Node;
use PhpParser\Node\Stmt;

class Use_ implements Builder {
    protected Node\Name $name;
    /** @var Stmt\Use_::TYPE_* */
    protected int $type;
    protected ?string $alias = null;

    /**
     * Creates a name use (alias) builder.
     *
     * @param Node\Name|string $name Name of the entity (namespace, class, function, constant) to alias
     * @param Stmt\Use_::TYPE_* $type One of the Stmt\Use_::TYPE_* constants
     */
    public function __construct($name, int $type) {
        $this->name = BuilderHelpers::normalizeName($name);
        $this->type = $type;
    }

    /**
     * Sets alias for used name.
     *
     * @param string $alias Alias to use (last component of full name by default)
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function as(string $alias) {
        $this->alias = $alias;
        return $this;
    }

    /**
     * Returns the built node.
     *
     * @return Stmt\Use_ The built node
     */
    public function getNode(): Node {
        return new Stmt\Use_([
            new Node\UseItem($this->name, $this->alias)
        ], $this->type);
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Builder;

use PhpParser;
use PhpParser\BuilderHelpers;
use PhpParser\Node;
use PhpParser\Node\Stmt;

class Function_ extends FunctionLike {
    protected string $name;
    /** @var list<Stmt> */
    protected array $stmts = [];

    /** @var list<Node\AttributeGroup> */
    protected array $attributeGroups = [];

    /**
     * Creates a function builder.
     *
     * @param string $name Name of the function
     */
    public function __construct(string $name) {
        $this->name = $name;
    }

    /**
     * Adds a statement.
     *
     * @param Node|PhpParser\Builder $stmt The statement to add
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function addStmt($stmt) {
        $this->stmts[] = BuilderHelpers::normalizeStmt($stmt);

        return $this;
    }

    /**
     * Adds an attribute group.
     *
     * @param Node\Attribute|Node\AttributeGroup $attribute
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function addAttribute($attribute) {
        $this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);

        return $this;
    }

    /**
     * Returns the built function node.
     *
     * @return Stmt\Function_ The built function node
     */
    public function getNode(): Node {
        return new Stmt\Function_($this->name, [
            'byRef'      => $this->returnByRef,
            'params'     => $this->params,
            'returnType' => $this->returnType,
            'stmts'      => $this->stmts,
            'attrGroups' => $this->attributeGroups,
        ], $this->attributes);
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Builder;

use PhpParser\BuilderHelpers;
use PhpParser\Node;

abstract class FunctionLike extends Declaration {
    protected bool $returnByRef = false;
    /** @var Node\Param[] */
    protected array $params = [];

    /** @var Node\Identifier|Node\Name|Node\ComplexType|null */
    protected ?Node $returnType = null;

    /**
     * Make the function return by reference.
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function makeReturnByRef() {
        $this->returnByRef = true;

        return $this;
    }

    /**
     * Adds a parameter.
     *
     * @param Node\Param|Param $param The parameter to add
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function addParam($param) {
        $param = BuilderHelpers::normalizeNode($param);

        if (!$param instanceof Node\Param) {
            throw new \LogicException(sprintf('Expected parameter node, got "%s"', $param->getType()));
        }

        $this->params[] = $param;

        return $this;
    }

    /**
     * Adds multiple parameters.
     *
     * @param (Node\Param|Param)[] $params The parameters to add
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function addParams(array $params) {
        foreach ($params as $param) {
            $this->addParam($param);
        }

        return $this;
    }

    /**
     * Sets the return type for PHP 7.
     *
     * @param string|Node\Name|Node\Identifier|Node\ComplexType $type
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function setReturnType($type) {
        $this->returnType = BuilderHelpers::normalizeType($type);

        return $this;
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Builder;

use PhpParser;
use PhpParser\BuilderHelpers;
use PhpParser\Modifiers;
use PhpParser\Node;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt;
use PhpParser\Node\ComplexType;

class Property implements PhpParser\Builder {
    protected string $name;

    protected int $flags = 0;

    protected ?Node\Expr $default = null;
    /** @var array<string, mixed> */
    protected array $attributes = [];
    /** @var null|Identifier|Name|ComplexType */
    protected ?Node $type = null;
    /** @var list<Node\AttributeGroup> */
    protected array $attributeGroups = [];
    /** @var list<Node\PropertyHook> */
    protected array $hooks = [];

    /**
     * Creates a property builder.
     *
     * @param string $name Name of the property
     */
    public function __construct(string $name) {
        $this->name = $name;
    }

    /**
     * Makes the property public.
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function makePublic() {
        $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PUBLIC);

        return $this;
    }

    /**
     * Makes the property protected.
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function makeProtected() {
        $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PROTECTED);

        return $this;
    }

    /**
     * Makes the property private.
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function makePrivate() {
        $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PRIVATE);

        return $this;
    }

    /**
     * Makes the property static.
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function makeStatic() {
        $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::STATIC);

        return $this;
    }

    /**
     * Makes the property readonly.
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function makeReadonly() {
        $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::READONLY);

        return $this;
    }

    /**
     * Makes the property abstract. Requires at least one property hook to be specified as well.
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function makeAbstract() {
        $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::ABSTRACT);

        return $this;
    }

    /**
     * Makes the property final.
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function makeFinal() {
        $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::FINAL);

        return $this;
    }

    /**
     * Gives the property private(set) visibility.
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function makePrivateSet() {
        $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PRIVATE_SET);

        return $this;
    }

    /**
     * Gives the property protected(set) visibility.
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function makeProtectedSet() {
        $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PROTECTED_SET);

        return $this;
    }

    /**
     * Sets default value for the property.
     *
     * @param mixed $value Default value to use
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function setDefault($value) {
        $this->default = BuilderHelpers::normalizeValue($value);

        return $this;
    }

    /**
     * Sets doc comment for the property.
     *
     * @param PhpParser\Comment\Doc|string $docComment Doc comment to set
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function setDocComment($docComment) {
        $this->attributes = [
            'comments' => [BuilderHelpers::normalizeDocComment($docComment)]
        ];

        return $this;
    }

    /**
     * Sets the property type for PHP 7.4+.
     *
     * @param string|Name|Identifier|ComplexType $type
     *
     * @return $this
     */
    public function setType($type) {
        $this->type = BuilderHelpers::normalizeType($type);

        return $this;
    }

    /**
     * Adds an attribute group.
     *
     * @param Node\Attribute|Node\AttributeGroup $attribute
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function addAttribute($attribute) {
        $this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);

        return $this;
    }

    /**
     * Adds a property hook.
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function addHook(Node\PropertyHook $hook) {
        $this->hooks[] = $hook;

        return $this;
    }

    /**
     * Returns the built class node.
     *
     * @return Stmt\Property The built property node
     */
    public function getNode(): PhpParser\Node {
        if ($this->flags & Modifiers::ABSTRACT && !$this->hooks) {
            throw new PhpParser\Error('Only hooked properties may be declared abstract');
        }

        return new Stmt\Property(
            $this->flags !== 0 ? $this->flags : Modifiers::PUBLIC,
            [
                new Node\PropertyItem($this->name, $this->default)
            ],
            $this->attributes,
            $this->type,
            $this->attributeGroups,
            $this->hooks
        );
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Builder;

use PhpParser;
use PhpParser\BuilderHelpers;
use PhpParser\Modifiers;
use PhpParser\Node;

class Param implements PhpParser\Builder {
    protected string $name;
    protected ?Node\Expr $default = null;
    /** @var Node\Identifier|Node\Name|Node\ComplexType|null */
    protected ?Node $type = null;
    protected bool $byRef = false;
    protected int $flags = 0;
    protected bool $variadic = false;
    /** @var list<Node\AttributeGroup> */
    protected array $attributeGroups = [];

    /**
     * Creates a parameter builder.
     *
     * @param string $name Name of the parameter
     */
    public function __construct(string $name) {
        $this->name = $name;
    }

    /**
     * Sets default value for the parameter.
     *
     * @param mixed $value Default value to use
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function setDefault($value) {
        $this->default = BuilderHelpers::normalizeValue($value);

        return $this;
    }

    /**
     * Sets type for the parameter.
     *
     * @param string|Node\Name|Node\Identifier|Node\ComplexType $type Parameter type
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function setType($type) {
        $this->type = BuilderHelpers::normalizeType($type);
        if ($this->type == 'void') {
            throw new \LogicException('Parameter type cannot be void');
        }

        return $this;
    }

    /**
     * Make the parameter accept the value by reference.
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function makeByRef() {
        $this->byRef = true;

        return $this;
    }

    /**
     * Make the parameter variadic
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function makeVariadic() {
        $this->variadic = true;

        return $this;
    }

    /**
     * Makes the (promoted) parameter public.
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function makePublic() {
        $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PUBLIC);

        return $this;
    }

    /**
     * Makes the (promoted) parameter protected.
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function makeProtected() {
        $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PROTECTED);

        return $this;
    }

    /**
     * Makes the (promoted) parameter private.
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function makePrivate() {
        $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PRIVATE);

        return $this;
    }

    /**
     * Makes the (promoted) parameter readonly.
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function makeReadonly() {
        $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::READONLY);

        return $this;
    }

    /**
     * Gives the promoted property private(set) visibility.
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function makePrivateSet() {
        $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PRIVATE_SET);

        return $this;
    }

    /**
     * Gives the promoted property protected(set) visibility.
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function makeProtectedSet() {
        $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PROTECTED_SET);

        return $this;
    }

    /**
     * Adds an attribute group.
     *
     * @param Node\Attribute|Node\AttributeGroup $attribute
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function addAttribute($attribute) {
        $this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);

        return $this;
    }

    /**
     * Returns the built parameter node.
     *
     * @return Node\Param The built parameter node
     */
    public function getNode(): Node {
        return new Node\Param(
            new Node\Expr\Variable($this->name),
            $this->default, $this->type, $this->byRef, $this->variadic, [], $this->flags, $this->attributeGroups
        );
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Builder;

use PhpParser;
use PhpParser\BuilderHelpers;

abstract class Declaration implements PhpParser\Builder {
    /** @var array<string, mixed> */
    protected array $attributes = [];

    /**
     * Adds a statement.
     *
     * @param PhpParser\Node\Stmt|PhpParser\Builder $stmt The statement to add
     *
     * @return $this The builder instance (for fluid interface)
     */
    abstract public function addStmt($stmt);

    /**
     * Adds multiple statements.
     *
     * @param (PhpParser\Node\Stmt|PhpParser\Builder)[] $stmts The statements to add
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function addStmts(array $stmts) {
        foreach ($stmts as $stmt) {
            $this->addStmt($stmt);
        }

        return $this;
    }

    /**
     * Sets doc comment for the declaration.
     *
     * @param PhpParser\Comment\Doc|string $docComment Doc comment to set
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function setDocComment($docComment) {
        $this->attributes['comments'] = [
            BuilderHelpers::normalizeDocComment($docComment)
        ];

        return $this;
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Builder;

use PhpParser\Builder;
use PhpParser\BuilderHelpers;
use PhpParser\Modifiers;
use PhpParser\Node;
use PhpParser\Node\Stmt;

class TraitUseAdaptation implements Builder {
    private const TYPE_UNDEFINED  = 0;
    private const TYPE_ALIAS      = 1;
    private const TYPE_PRECEDENCE = 2;

    protected int $type;
    protected ?Node\Name $trait;
    protected Node\Identifier $method;
    protected ?int $modifier = null;
    protected ?Node\Identifier $alias = null;
    /** @var Node\Name[] */
    protected array $insteadof = [];

    /**
     * Creates a trait use adaptation builder.
     *
     * @param Node\Name|string|null $trait Name of adapted trait
     * @param Node\Identifier|string $method Name of adapted method
     */
    public function __construct($trait, $method) {
        $this->type = self::TYPE_UNDEFINED;

        $this->trait = is_null($trait) ? null : BuilderHelpers::normalizeName($trait);
        $this->method = BuilderHelpers::normalizeIdentifier($method);
    }

    /**
     * Sets alias of method.
     *
     * @param Node\Identifier|string $alias Alias for adapted method
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function as($alias) {
        if ($this->type === self::TYPE_UNDEFINED) {
            $this->type = self::TYPE_ALIAS;
        }

        if ($this->type !== self::TYPE_ALIAS) {
            throw new \LogicException('Cannot set alias for not alias adaptation buider');
        }

        $this->alias = BuilderHelpers::normalizeIdentifier($alias);
        return $this;
    }

    /**
     * Sets adapted method public.
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function makePublic() {
        $this->setModifier(Modifiers::PUBLIC);
        return $this;
    }

    /**
     * Sets adapted method protected.
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function makeProtected() {
        $this->setModifier(Modifiers::PROTECTED);
        return $this;
    }

    /**
     * Sets adapted method private.
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function makePrivate() {
        $this->setModifier(Modifiers::PRIVATE);
        return $this;
    }

    /**
     * Adds overwritten traits.
     *
     * @param Node\Name|string ...$traits Traits for overwrite
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function insteadof(...$traits) {
        if ($this->type === self::TYPE_UNDEFINED) {
            if (is_null($this->trait)) {
                throw new \LogicException('Precedence adaptation must have trait');
            }

            $this->type = self::TYPE_PRECEDENCE;
        }

        if ($this->type !== self::TYPE_PRECEDENCE) {
            throw new \LogicException('Cannot add overwritten traits for not precedence adaptation buider');
        }

        foreach ($traits as $trait) {
            $this->insteadof[] = BuilderHelpers::normalizeName($trait);
        }

        return $this;
    }

    protected function setModifier(int $modifier): void {
        if ($this->type === self::TYPE_UNDEFINED) {
            $this->type = self::TYPE_ALIAS;
        }

        if ($this->type !== self::TYPE_ALIAS) {
            throw new \LogicException('Cannot set access modifier for not alias adaptation buider');
        }

        if (is_null($this->modifier)) {
            $this->modifier = $modifier;
        } else {
            throw new \LogicException('Multiple access type modifiers are not allowed');
        }
    }

    /**
     * Returns the built node.
     *
     * @return Node The built node
     */
    public function getNode(): Node {
        switch ($this->type) {
            case self::TYPE_ALIAS:
                return new Stmt\TraitUseAdaptation\Alias($this->trait, $this->method, $this->modifier, $this->alias);
            case self::TYPE_PRECEDENCE:
                return new Stmt\TraitUseAdaptation\Precedence($this->trait, $this->method, $this->insteadof);
            default:
                throw new \LogicException('Type of adaptation is not defined');
        }
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Builder;

use PhpParser;
use PhpParser\BuilderHelpers;
use PhpParser\Node;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt;

class Interface_ extends Declaration {
    protected string $name;
    /** @var list<Name> */
    protected array $extends = [];
    /** @var list<Stmt\ClassConst> */
    protected array $constants = [];
    /** @var list<Stmt\ClassMethod> */
    protected array $methods = [];
    /** @var list<Node\AttributeGroup> */
    protected array $attributeGroups = [];

    /**
     * Creates an interface builder.
     *
     * @param string $name Name of the interface
     */
    public function __construct(string $name) {
        $this->name = $name;
    }

    /**
     * Extends one or more interfaces.
     *
     * @param Name|string ...$interfaces Names of interfaces to extend
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function extend(...$interfaces) {
        foreach ($interfaces as $interface) {
            $this->extends[] = BuilderHelpers::normalizeName($interface);
        }

        return $this;
    }

    /**
     * Adds a statement.
     *
     * @param Stmt|PhpParser\Builder $stmt The statement to add
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function addStmt($stmt) {
        $stmt = BuilderHelpers::normalizeNode($stmt);

        if ($stmt instanceof Stmt\ClassConst) {
            $this->constants[] = $stmt;
        } elseif ($stmt instanceof Stmt\ClassMethod) {
            // we erase all statements in the body of an interface method
            $stmt->stmts = null;
            $this->methods[] = $stmt;
        } else {
            throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType()));
        }

        return $this;
    }

    /**
     * Adds an attribute group.
     *
     * @param Node\Attribute|Node\AttributeGroup $attribute
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function addAttribute($attribute) {
        $this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);

        return $this;
    }

    /**
     * Returns the built interface node.
     *
     * @return Stmt\Interface_ The built interface node
     */
    public function getNode(): PhpParser\Node {
        return new Stmt\Interface_($this->name, [
            'extends' => $this->extends,
            'stmts' => array_merge($this->constants, $this->methods),
            'attrGroups' => $this->attributeGroups,
        ], $this->attributes);
    }
}
<?php

declare(strict_types=1);

namespace PhpParser\Builder;

use PhpParser;
use PhpParser\BuilderHelpers;
use PhpParser\Node;
use PhpParser\Node\Identifier;
use PhpParser\Node\Stmt;

class EnumCase implements PhpParser\Builder {
    /** @var Identifier|string */
    protected $name;
    protected ?Node\Expr $value = null;
    /** @var array<string, mixed> */
    protected array $attributes = [];

    /** @var list<Node\AttributeGroup> */
    protected array $attributeGroups = [];

    /**
     * Creates an enum case builder.
     *
     * @param string|Identifier $name Name
     */
    public function __construct($name) {
        $this->name = $name;
    }

    /**
     * Sets the value.
     *
     * @param Node\Expr|string|int $value
     *
     * @return $this
     */
    public function setValue($value) {
        $this->value = BuilderHelpers::normalizeValue($value);

        return $this;
    }

    /**
     * Sets doc comment for the constant.
     *
     * @param PhpParser\Comment\Doc|string $docComment Doc comment to set
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function setDocComment($docComment) {
        $this->attributes = [
            'comments' => [BuilderHelpers::normalizeDocComment($docComment)]
        ];

        return $this;
    }

    /**
     * Adds an attribute group.
     *
     * @param Node\Attribute|Node\AttributeGroup $attribute
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function addAttribute($attribute) {
        $this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);

        return $this;
    }

    /**
     * Returns the built enum case node.
     *
     * @return Stmt\EnumCase The built constant node
     */
    public function getNode(): PhpParser\Node {
        return new Stmt\EnumCase(
            $this->name,
            $this->value,
            $this->attributeGroups,
            $this->attributes
        );
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Builder;

use PhpParser;
use PhpParser\BuilderHelpers;
use PhpParser\Modifiers;
use PhpParser\Node;
use PhpParser\Node\Stmt;

class Method extends FunctionLike {
    protected string $name;

    protected int $flags = 0;

    /** @var list<Stmt>|null */
    protected ?array $stmts = [];

    /** @var list<Node\AttributeGroup> */
    protected array $attributeGroups = [];

    /**
     * Creates a method builder.
     *
     * @param string $name Name of the method
     */
    public function __construct(string $name) {
        $this->name = $name;
    }

    /**
     * Makes the method public.
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function makePublic() {
        $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PUBLIC);

        return $this;
    }

    /**
     * Makes the method protected.
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function makeProtected() {
        $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PROTECTED);

        return $this;
    }

    /**
     * Makes the method private.
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function makePrivate() {
        $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PRIVATE);

        return $this;
    }

    /**
     * Makes the method static.
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function makeStatic() {
        $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::STATIC);

        return $this;
    }

    /**
     * Makes the method abstract.
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function makeAbstract() {
        if (!empty($this->stmts)) {
            throw new \LogicException('Cannot make method with statements abstract');
        }

        $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::ABSTRACT);
        $this->stmts = null; // abstract methods don't have statements

        return $this;
    }

    /**
     * Makes the method final.
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function makeFinal() {
        $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::FINAL);

        return $this;
    }

    /**
     * Adds a statement.
     *
     * @param Node|PhpParser\Builder $stmt The statement to add
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function addStmt($stmt) {
        if (null === $this->stmts) {
            throw new \LogicException('Cannot add statements to an abstract method');
        }

        $this->stmts[] = BuilderHelpers::normalizeStmt($stmt);

        return $this;
    }

    /**
     * Adds an attribute group.
     *
     * @param Node\Attribute|Node\AttributeGroup $attribute
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function addAttribute($attribute) {
        $this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);

        return $this;
    }

    /**
     * Returns the built method node.
     *
     * @return Stmt\ClassMethod The built method node
     */
    public function getNode(): Node {
        return new Stmt\ClassMethod($this->name, [
            'flags'      => $this->flags,
            'byRef'      => $this->returnByRef,
            'params'     => $this->params,
            'returnType' => $this->returnType,
            'stmts'      => $this->stmts,
            'attrGroups' => $this->attributeGroups,
        ], $this->attributes);
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Builder;

use PhpParser;
use PhpParser\BuilderHelpers;
use PhpParser\Modifiers;
use PhpParser\Node;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt;

class Class_ extends Declaration {
    protected string $name;
    protected ?Name $extends = null;
    /** @var list<Name> */
    protected array $implements = [];
    protected int $flags = 0;
    /** @var list<Stmt\TraitUse> */
    protected array $uses = [];
    /** @var list<Stmt\ClassConst> */
    protected array $constants = [];
    /** @var list<Stmt\Property> */
    protected array $properties = [];
    /** @var list<Stmt\ClassMethod> */
    protected array $methods = [];
    /** @var list<Node\AttributeGroup> */
    protected array $attributeGroups = [];

    /**
     * Creates a class builder.
     *
     * @param string $name Name of the class
     */
    public function __construct(string $name) {
        $this->name = $name;
    }

    /**
     * Extends a class.
     *
     * @param Name|string $class Name of class to extend
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function extend($class) {
        $this->extends = BuilderHelpers::normalizeName($class);

        return $this;
    }

    /**
     * Implements one or more interfaces.
     *
     * @param Name|string ...$interfaces Names of interfaces to implement
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function implement(...$interfaces) {
        foreach ($interfaces as $interface) {
            $this->implements[] = BuilderHelpers::normalizeName($interface);
        }

        return $this;
    }

    /**
     * Makes the class abstract.
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function makeAbstract() {
        $this->flags = BuilderHelpers::addClassModifier($this->flags, Modifiers::ABSTRACT);

        return $this;
    }

    /**
     * Makes the class final.
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function makeFinal() {
        $this->flags = BuilderHelpers::addClassModifier($this->flags, Modifiers::FINAL);

        return $this;
    }

    /**
     * Makes the class readonly.
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function makeReadonly() {
        $this->flags = BuilderHelpers::addClassModifier($this->flags, Modifiers::READONLY);

        return $this;
    }

    /**
     * Adds a statement.
     *
     * @param Stmt|PhpParser\Builder $stmt The statement to add
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function addStmt($stmt) {
        $stmt = BuilderHelpers::normalizeNode($stmt);

        if ($stmt instanceof Stmt\Property) {
            $this->properties[] = $stmt;
        } elseif ($stmt instanceof Stmt\ClassMethod) {
            $this->methods[] = $stmt;
        } elseif ($stmt instanceof Stmt\TraitUse) {
            $this->uses[] = $stmt;
        } elseif ($stmt instanceof Stmt\ClassConst) {
            $this->constants[] = $stmt;
        } else {
            throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType()));
        }

        return $this;
    }

    /**
     * Adds an attribute group.
     *
     * @param Node\Attribute|Node\AttributeGroup $attribute
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function addAttribute($attribute) {
        $this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);

        return $this;
    }

    /**
     * Returns the built class node.
     *
     * @return Stmt\Class_ The built class node
     */
    public function getNode(): PhpParser\Node {
        return new Stmt\Class_($this->name, [
            'flags' => $this->flags,
            'extends' => $this->extends,
            'implements' => $this->implements,
            'stmts' => array_merge($this->uses, $this->constants, $this->properties, $this->methods),
            'attrGroups' => $this->attributeGroups,
        ], $this->attributes);
    }
}
<?php

declare(strict_types=1);

namespace PhpParser\Builder;

use PhpParser;
use PhpParser\BuilderHelpers;
use PhpParser\Modifiers;
use PhpParser\Node;
use PhpParser\Node\Const_;
use PhpParser\Node\Identifier;
use PhpParser\Node\Stmt;

class ClassConst implements PhpParser\Builder {
    protected int $flags = 0;
    /** @var array<string, mixed> */
    protected array $attributes = [];
    /** @var list<Const_> */
    protected array $constants = [];

    /** @var list<Node\AttributeGroup> */
    protected array $attributeGroups = [];
    /** @var Identifier|Node\Name|Node\ComplexType|null */
    protected ?Node $type = null;

    /**
     * Creates a class constant builder
     *
     * @param string|Identifier $name Name
     * @param Node\Expr|bool|null|int|float|string|array|\UnitEnum $value Value
     */
    public function __construct($name, $value) {
        $this->constants = [new Const_($name, BuilderHelpers::normalizeValue($value))];
    }

    /**
     * Add another constant to const group
     *
     * @param string|Identifier $name Name
     * @param Node\Expr|bool|null|int|float|string|array|\UnitEnum $value Value
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function addConst($name, $value) {
        $this->constants[] = new Const_($name, BuilderHelpers::normalizeValue($value));

        return $this;
    }

    /**
     * Makes the constant public.
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function makePublic() {
        $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PUBLIC);

        return $this;
    }

    /**
     * Makes the constant protected.
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function makeProtected() {
        $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PROTECTED);

        return $this;
    }

    /**
     * Makes the constant private.
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function makePrivate() {
        $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PRIVATE);

        return $this;
    }

    /**
     * Makes the constant final.
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function makeFinal() {
        $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::FINAL);

        return $this;
    }

    /**
     * Sets doc comment for the constant.
     *
     * @param PhpParser\Comment\Doc|string $docComment Doc comment to set
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function setDocComment($docComment) {
        $this->attributes = [
            'comments' => [BuilderHelpers::normalizeDocComment($docComment)]
        ];

        return $this;
    }

    /**
     * Adds an attribute group.
     *
     * @param Node\Attribute|Node\AttributeGroup $attribute
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function addAttribute($attribute) {
        $this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);

        return $this;
    }

    /**
     * Sets the constant type.
     *
     * @param string|Node\Name|Identifier|Node\ComplexType $type
     *
     * @return $this
     */
    public function setType($type) {
        $this->type = BuilderHelpers::normalizeType($type);

        return $this;
    }

    /**
     * Returns the built class node.
     *
     * @return Stmt\ClassConst The built constant node
     */
    public function getNode(): PhpParser\Node {
        return new Stmt\ClassConst(
            $this->constants,
            $this->flags,
            $this->attributes,
            $this->attributeGroups,
            $this->type
        );
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Builder;

use PhpParser;
use PhpParser\BuilderHelpers;
use PhpParser\Node;
use PhpParser\Node\Stmt;

class Trait_ extends Declaration {
    protected string $name;
    /** @var list<Stmt\TraitUse> */
    protected array $uses = [];
    /** @var list<Stmt\ClassConst> */
    protected array $constants = [];
    /** @var list<Stmt\Property> */
    protected array $properties = [];
    /** @var list<Stmt\ClassMethod> */
    protected array $methods = [];
    /** @var list<Node\AttributeGroup> */
    protected array $attributeGroups = [];

    /**
     * Creates an interface builder.
     *
     * @param string $name Name of the interface
     */
    public function __construct(string $name) {
        $this->name = $name;
    }

    /**
     * Adds a statement.
     *
     * @param Stmt|PhpParser\Builder $stmt The statement to add
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function addStmt($stmt) {
        $stmt = BuilderHelpers::normalizeNode($stmt);

        if ($stmt instanceof Stmt\Property) {
            $this->properties[] = $stmt;
        } elseif ($stmt instanceof Stmt\ClassMethod) {
            $this->methods[] = $stmt;
        } elseif ($stmt instanceof Stmt\TraitUse) {
            $this->uses[] = $stmt;
        } elseif ($stmt instanceof Stmt\ClassConst) {
            $this->constants[] = $stmt;
        } else {
            throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType()));
        }

        return $this;
    }

    /**
     * Adds an attribute group.
     *
     * @param Node\Attribute|Node\AttributeGroup $attribute
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function addAttribute($attribute) {
        $this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);

        return $this;
    }

    /**
     * Returns the built trait node.
     *
     * @return Stmt\Trait_ The built interface node
     */
    public function getNode(): PhpParser\Node {
        return new Stmt\Trait_(
            $this->name, [
                'stmts' => array_merge($this->uses, $this->constants, $this->properties, $this->methods),
                'attrGroups' => $this->attributeGroups,
            ], $this->attributes
        );
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Builder;

use PhpParser\Builder;
use PhpParser\BuilderHelpers;
use PhpParser\Node;
use PhpParser\Node\Stmt;

class TraitUse implements Builder {
    /** @var Node\Name[] */
    protected array $traits = [];
    /** @var Stmt\TraitUseAdaptation[] */
    protected array $adaptations = [];

    /**
     * Creates a trait use builder.
     *
     * @param Node\Name|string ...$traits Names of used traits
     */
    public function __construct(...$traits) {
        foreach ($traits as $trait) {
            $this->and($trait);
        }
    }

    /**
     * Adds used trait.
     *
     * @param Node\Name|string $trait Trait name
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function and($trait) {
        $this->traits[] = BuilderHelpers::normalizeName($trait);
        return $this;
    }

    /**
     * Adds trait adaptation.
     *
     * @param Stmt\TraitUseAdaptation|Builder\TraitUseAdaptation $adaptation Trait adaptation
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function with($adaptation) {
        $adaptation = BuilderHelpers::normalizeNode($adaptation);

        if (!$adaptation instanceof Stmt\TraitUseAdaptation) {
            throw new \LogicException('Adaptation must have type TraitUseAdaptation');
        }

        $this->adaptations[] = $adaptation;
        return $this;
    }

    /**
     * Returns the built node.
     *
     * @return Node The built node
     */
    public function getNode(): Node {
        return new Stmt\TraitUse($this->traits, $this->adaptations);
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Builder;

use PhpParser;
use PhpParser\BuilderHelpers;
use PhpParser\Node;
use PhpParser\Node\Stmt;

class Namespace_ extends Declaration {
    private ?Node\Name $name;
    /** @var Stmt[] */
    private array $stmts = [];

    /**
     * Creates a namespace builder.
     *
     * @param Node\Name|string|null $name Name of the namespace
     */
    public function __construct($name) {
        $this->name = null !== $name ? BuilderHelpers::normalizeName($name) : null;
    }

    /**
     * Adds a statement.
     *
     * @param Node|PhpParser\Builder $stmt The statement to add
     *
     * @return $this The builder instance (for fluid interface)
     */
    public function addStmt($stmt) {
        $this->stmts[] = BuilderHelpers::normalizeStmt($stmt);

        return $this;
    }

    /**
     * Returns the built node.
     *
     * @return Stmt\Namespace_ The built node
     */
    public function getNode(): Node {
        return new Stmt\Namespace_($this->name, $this->stmts, $this->attributes);
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Comment;

class Doc extends \PhpParser\Comment {
}
<?php declare(strict_types=1);

namespace PhpParser;

interface NodeVisitor {
    /**
     * If NodeVisitor::enterNode() returns DONT_TRAVERSE_CHILDREN, child nodes
     * of the current node will not be traversed for any visitors.
     *
     * For subsequent visitors enterNode() will still be called on the current
     * node and leaveNode() will also be invoked for the current node.
     */
    public const DONT_TRAVERSE_CHILDREN = 1;

    /**
     * If NodeVisitor::enterNode() or NodeVisitor::leaveNode() returns
     * STOP_TRAVERSAL, traversal is aborted.
     *
     * The afterTraverse() method will still be invoked.
     */
    public const STOP_TRAVERSAL = 2;

    /**
     * If NodeVisitor::leaveNode() returns REMOVE_NODE for a node that occurs
     * in an array, it will be removed from the array.
     *
     * For subsequent visitors leaveNode() will still be invoked for the
     * removed node.
     */
    public const REMOVE_NODE = 3;

    /**
     * If NodeVisitor::enterNode() returns DONT_TRAVERSE_CURRENT_AND_CHILDREN, child nodes
     * of the current node will not be traversed for any visitors.
     *
     * For subsequent visitors enterNode() will not be called as well.
     * leaveNode() will be invoked for visitors that has enterNode() method invoked.
     */
    public const DONT_TRAVERSE_CURRENT_AND_CHILDREN = 4;

    /**
     * If NodeVisitor::enterNode() or NodeVisitor::leaveNode() returns REPLACE_WITH_NULL,
     * the node will be replaced with null. This is not a legal return value if the node is part
     * of an array, rather than another node.
     */
    public const REPLACE_WITH_NULL = 5;

    /**
     * Called once before traversal.
     *
     * Return value semantics:
     *  * null:      $nodes stays as-is
     *  * otherwise: $nodes is set to the return value
     *
     * @param Node[] $nodes Array of nodes
     *
     * @return null|Node[] Array of nodes
     */
    public function beforeTraverse(array $nodes);

    /**
     * Called when entering a node.
     *
     * Return value semantics:
     *  * null
     *        => $node stays as-is
     *  * array (of Nodes)
     *        => The return value is merged into the parent array (at the position of the $node)
     *  * NodeVisitor::REMOVE_NODE
     *        => $node is removed from the parent array
     *  * NodeVisitor::REPLACE_WITH_NULL
     *        => $node is replaced with null
     *  * NodeVisitor::DONT_TRAVERSE_CHILDREN
     *        => Children of $node are not traversed. $node stays as-is
     *  * NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN
     *        => Further visitors for the current node are skipped, and its children are not
     *           traversed. $node stays as-is.
     *  * NodeVisitor::STOP_TRAVERSAL
     *        => Traversal is aborted. $node stays as-is
     *  * otherwise
     *        => $node is set to the return value
     *
     * @param Node $node Node
     *
     * @return null|int|Node|Node[] Replacement node (or special return value)
     */
    public function enterNode(Node $node);

    /**
     * Called when leaving a node.
     *
     * Return value semantics:
     *  * null
     *        => $node stays as-is
     *  * NodeVisitor::REMOVE_NODE
     *        => $node is removed from the parent array
     *  * NodeVisitor::REPLACE_WITH_NULL
     *        => $node is replaced with null
     *  * NodeVisitor::STOP_TRAVERSAL
     *        => Traversal is aborted. $node stays as-is
     *  * array (of Nodes)
     *        => The return value is merged into the parent array (at the position of the $node)
     *  * otherwise
     *        => $node is set to the return value
     *
     * @param Node $node Node
     *
     * @return null|int|Node|Node[] Replacement node (or special return value)
     */
    public function leaveNode(Node $node);

    /**
     * Called once after traversal.
     *
     * Return value semantics:
     *  * null:      $nodes stays as-is
     *  * otherwise: $nodes is set to the return value
     *
     * @param Node[] $nodes Array of nodes
     *
     * @return null|Node[] Array of nodes
     */
    public function afterTraverse(array $nodes);
}
<?php declare(strict_types=1);

namespace PhpParser\Lexer;

use PhpParser\Error;
use PhpParser\ErrorHandler;
use PhpParser\Lexer;
use PhpParser\Lexer\TokenEmulator\AsymmetricVisibilityTokenEmulator;
use PhpParser\Lexer\TokenEmulator\AttributeEmulator;
use PhpParser\Lexer\TokenEmulator\EnumTokenEmulator;
use PhpParser\Lexer\TokenEmulator\ExplicitOctalEmulator;
use PhpParser\Lexer\TokenEmulator\FnTokenEmulator;
use PhpParser\Lexer\TokenEmulator\MatchTokenEmulator;
use PhpParser\Lexer\TokenEmulator\NullsafeTokenEmulator;
use PhpParser\Lexer\TokenEmulator\PipeOperatorEmulator;
use PhpParser\Lexer\TokenEmulator\PropertyTokenEmulator;
use PhpParser\Lexer\TokenEmulator\ReadonlyFunctionTokenEmulator;
use PhpParser\Lexer\TokenEmulator\ReadonlyTokenEmulator;
use PhpParser\Lexer\TokenEmulator\ReverseEmulator;
use PhpParser\Lexer\TokenEmulator\TokenEmulator;
use PhpParser\Lexer\TokenEmulator\VoidCastEmulator;
use PhpParser\PhpVersion;
use PhpParser\Token;

class Emulative extends Lexer {
    /** @var array{int, string, string}[] Patches used to reverse changes introduced in the code */
    private array $patches = [];

    /** @var list<TokenEmulator> */
    private array $emulators = [];

    private PhpVersion $targetPhpVersion;

    private PhpVersion $hostPhpVersion;

    /**
     * @param PhpVersion|null $phpVersion PHP version to emulate. Defaults to newest supported.
     */
    public function __construct(?PhpVersion $phpVersion = null) {
        $this->targetPhpVersion = $phpVersion ?? PhpVersion::getNewestSupported();
        $this->hostPhpVersion = PhpVersion::getHostVersion();

        $emulators = [
            new FnTokenEmulator(),
            new MatchTokenEmulator(),
            new NullsafeTokenEmulator(),
            new AttributeEmulator(),
            new EnumTokenEmulator(),
            new ReadonlyTokenEmulator(),
            new ExplicitOctalEmulator(),
            new ReadonlyFunctionTokenEmulator(),
            new PropertyTokenEmulator(),
            new AsymmetricVisibilityTokenEmulator(),
            new PipeOperatorEmulator(),
            new VoidCastEmulator(),
        ];

        // Collect emulators that are relevant for the PHP version we're running
        // and the PHP version we're targeting for emulation.
        foreach ($emulators as $emulator) {
            $emulatorPhpVersion = $emulator->getPhpVersion();
            if ($this->isForwardEmulationNeeded($emulatorPhpVersion)) {
                $this->emulators[] = $emulator;
            } elseif ($this->isReverseEmulationNeeded($emulatorPhpVersion)) {
                $this->emulators[] = new ReverseEmulator($emulator);
            }
        }
    }

    public function tokenize(string $code, ?ErrorHandler $errorHandler = null): array {
        $emulators = array_filter($this->emulators, function ($emulator) use ($code) {
            return $emulator->isEmulationNeeded($code);
        });

        if (empty($emulators)) {
            // Nothing to emulate, yay
            return parent::tokenize($code, $errorHandler);
        }

        if ($errorHandler === null) {
            $errorHandler = new ErrorHandler\Throwing();
        }

        $this->patches = [];
        foreach ($emulators as $emulator) {
            $code = $emulator->preprocessCode($code, $this->patches);
        }

        $collector = new ErrorHandler\Collecting();
        $tokens = parent::tokenize($code, $collector);
        $this->sortPatches();
        $tokens = $this->fixupTokens($tokens);

        $errors = $collector->getErrors();
        if (!empty($errors)) {
            $this->fixupErrors($errors);
            foreach ($errors as $error) {
                $errorHandler->handleError($error);
            }
        }

        foreach ($emulators as $emulator) {
            $tokens = $emulator->emulate($code, $tokens);
        }

        return $tokens;
    }

    private function isForwardEmulationNeeded(PhpVersion $emulatorPhpVersion): bool {
        return $this->hostPhpVersion->older($emulatorPhpVersion)
            && $this->targetPhpVersion->newerOrEqual($emulatorPhpVersion);
    }

    private function isReverseEmulationNeeded(PhpVersion $emulatorPhpVersion): bool {
        return $this->hostPhpVersion->newerOrEqual($emulatorPhpVersion)
            && $this->targetPhpVersion->older($emulatorPhpVersion);
    }

    private function sortPatches(): void {
        // Patches may be contributed by different emulators.
        // Make sure they are sorted by increasing patch position.
        usort($this->patches, function ($p1, $p2) {
            return $p1[0] <=> $p2[0];
        });
    }

    /**
     * @param list<Token> $tokens
     * @return list<Token>
     */
    private function fixupTokens(array $tokens): array {
        if (\count($this->patches) === 0) {
            return $tokens;
        }

        // Load first patch
        $patchIdx = 0;
        list($patchPos, $patchType, $patchText) = $this->patches[$patchIdx];

        // We use a manual loop over the tokens, because we modify the array on the fly
        $posDelta = 0;
        $lineDelta = 0;
        for ($i = 0, $c = \count($tokens); $i < $c; $i++) {
            $token = $tokens[$i];
            $pos = $token->pos;
            $token->pos += $posDelta;
            $token->line += $lineDelta;
            $localPosDelta = 0;
            $len = \strlen($token->text);
            while ($patchPos >= $pos && $patchPos < $pos + $len) {
                $patchTextLen = \strlen($patchText);
                if ($patchType === 'remove') {
                    if ($patchPos === $pos && $patchTextLen === $len) {
                        // Remove token entirely
                        array_splice($tokens, $i, 1, []);
                        $i--;
                        $c--;
                    } else {
                        // Remove from token string
                        $token->text = substr_replace(
                            $token->text, '', $patchPos - $pos + $localPosDelta, $patchTextLen
                        );
                        $localPosDelta -= $patchTextLen;
                    }
                    $lineDelta -= \substr_count($patchText, "\n");
                } elseif ($patchType === 'add') {
                    // Insert into the token string
                    $token->text = substr_replace(
                        $token->text, $patchText, $patchPos - $pos + $localPosDelta, 0
                    );
                    $localPosDelta += $patchTextLen;
                    $lineDelta += \substr_count($patchText, "\n");
                } elseif ($patchType === 'replace') {
                    // Replace inside the token string
                    $token->text = substr_replace(
                        $token->text, $patchText, $patchPos - $pos + $localPosDelta, $patchTextLen
                    );
                } else {
                    assert(false);
                }

                // Fetch the next patch
                $patchIdx++;
                if ($patchIdx >= \count($this->patches)) {
                    // No more patches. However, we still need to adjust position.
                    $patchPos = \PHP_INT_MAX;
                    break;
                }

                list($patchPos, $patchType, $patchText) = $this->patches[$patchIdx];
            }

            $posDelta += $localPosDelta;
        }
        return $tokens;
    }

    /**
     * Fixup line and position information in errors.
     *
     * @param Error[] $errors
     */
    private function fixupErrors(array $errors): void {
        foreach ($errors as $error) {
            $attrs = $error->getAttributes();

            $posDelta = 0;
            $lineDelta = 0;
            foreach ($this->patches as $patch) {
                list($patchPos, $patchType, $patchText) = $patch;
                if ($patchPos >= $attrs['startFilePos']) {
                    // No longer relevant
                    break;
                }

                if ($patchType === 'add') {
                    $posDelta += strlen($patchText);
                    $lineDelta += substr_count($patchText, "\n");
                } elseif ($patchType === 'remove') {
                    $posDelta -= strlen($patchText);
                    $lineDelta -= substr_count($patchText, "\n");
                }
            }

            $attrs['startFilePos'] += $posDelta;
            $attrs['endFilePos'] += $posDelta;
            $attrs['startLine'] += $lineDelta;
            $attrs['endLine'] += $lineDelta;
            $error->setAttributes($attrs);
        }
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Lexer\TokenEmulator;

use PhpParser\PhpVersion;
use PhpParser\Token;

/** @internal */
abstract class TokenEmulator {
    abstract public function getPhpVersion(): PhpVersion;

    abstract public function isEmulationNeeded(string $code): bool;

    /**
     * @param Token[] $tokens Original tokens
     * @return Token[] Modified Tokens
     */
    abstract public function emulate(string $code, array $tokens): array;

    /**
     * @param Token[] $tokens Original tokens
     * @return Token[] Modified Tokens
     */
    abstract public function reverseEmulate(string $code, array $tokens): array;

    /** @param array{int, string, string}[] $patches */
    public function preprocessCode(string $code, array &$patches): string {
        return $code;
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Lexer\TokenEmulator;

use PhpParser\PhpVersion;
use PhpParser\Token;

class VoidCastEmulator extends TokenEmulator {
    public function getPhpVersion(): PhpVersion {
        return PhpVersion::fromComponents(8, 5);
    }

    public function isEmulationNeeded(string $code): bool {
        return (bool)\preg_match('/\([ \t]*void[ \t]*\)/i', $code);
    }

    public function emulate(string $code, array $tokens): array {
        for ($i = 0, $c = count($tokens); $i < $c; ++$i) {
            $token = $tokens[$i];
            if ($token->text !== '(') {
                continue;
            }

            $numTokens = 1;
            $text = '(';
            $j = $i + 1;
            if ($j < $c && $tokens[$j]->id === \T_WHITESPACE && preg_match('/\A[ \t]+\z/', $tokens[$j]->text)) {
                $text .= $tokens[$j]->text;
                $numTokens++;
                $j++;
            }

            if ($j >= $c || $tokens[$j]->id !== \T_STRING || \strtolower($tokens[$j]->text) !== 'void') {
                continue;
            }

            $text .= $tokens[$j]->text;
            $numTokens++;
            $k = $j + 1;
            if ($k < $c && $tokens[$k]->id === \T_WHITESPACE && preg_match('/\A[ \t]+\z/', $tokens[$k]->text)) {
                $text .= $tokens[$k]->text;
                $numTokens++;
                $k++;
            }

            if ($k >= $c || $tokens[$k]->text !== ')') {
                continue;
            }

            $text .= ')';
            $numTokens++;
            array_splice($tokens, $i, $numTokens, [
                new Token(\T_VOID_CAST, $text, $token->line, $token->pos),
            ]);
            $c -= $numTokens - 1;
        }
        return $tokens;
    }

    public function reverseEmulate(string $code, array $tokens): array {
        for ($i = 0, $c = count($tokens); $i < $c; ++$i) {
            $token = $tokens[$i];
            if ($token->id !== \T_VOID_CAST) {
                continue;
            }

            if (!preg_match('/^\(([ \t]*)(void)([ \t]*)\)$/i', $token->text, $match)) {
                throw new \LogicException('Unexpected T_VOID_CAST contents');
            }

            $newTokens = [];
            $pos = $token->pos;

            $newTokens[] = new Token(\ord('('), '(', $token->line, $pos);
            $pos++;

            if ($match[1] !== '') {
                $newTokens[] = new Token(\T_WHITESPACE, $match[1], $token->line, $pos);
                $pos += \strlen($match[1]);
            }

            $newTokens[] = new Token(\T_STRING, $match[2], $token->line, $pos);
            $pos += \strlen($match[2]);

            if ($match[3] !== '') {
                $newTokens[] = new Token(\T_WHITESPACE, $match[3], $token->line, $pos);
                $pos += \strlen($match[3]);
            }

            $newTokens[] = new Token(\ord(')'), ')', $token->line, $pos);

            array_splice($tokens, $i, 1, $newTokens);
            $i += \count($newTokens) - 1;
            $c += \count($newTokens) - 1;
        }
        return $tokens;
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Lexer\TokenEmulator;

use PhpParser\PhpVersion;

final class EnumTokenEmulator extends KeywordEmulator {
    public function getPhpVersion(): PhpVersion {
        return PhpVersion::fromComponents(8, 1);
    }

    public function getKeywordString(): string {
        return 'enum';
    }

    public function getKeywordToken(): int {
        return \T_ENUM;
    }

    protected function isKeywordContext(array $tokens, int $pos): bool {
        return parent::isKeywordContext($tokens, $pos)
            && isset($tokens[$pos + 2])
            && $tokens[$pos + 1]->id === \T_WHITESPACE
            && $tokens[$pos + 2]->id === \T_STRING;
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Lexer\TokenEmulator;

use PhpParser\PhpVersion;
use PhpParser\Token;

final class AttributeEmulator extends TokenEmulator {
    public function getPhpVersion(): PhpVersion {
        return PhpVersion::fromComponents(8, 0);
    }

    public function isEmulationNeeded(string $code): bool {
        return strpos($code, '#[') !== false;
    }

    public function emulate(string $code, array $tokens): array {
        // We need to manually iterate and manage a count because we'll change
        // the tokens array on the way.
        for ($i = 0, $c = count($tokens); $i < $c; ++$i) {
            $token = $tokens[$i];
            if ($token->text === '#' && isset($tokens[$i + 1]) && $tokens[$i + 1]->text === '[') {
                array_splice($tokens, $i, 2, [
                    new Token(\T_ATTRIBUTE, '#[', $token->line, $token->pos),
                ]);
                $c--;
                continue;
            }
        }

        return $tokens;
    }

    public function reverseEmulate(string $code, array $tokens): array {
        // TODO
        return $tokens;
    }

    public function preprocessCode(string $code, array &$patches): string {
        $pos = 0;
        while (false !== $pos = strpos($code, '#[', $pos)) {
            // Replace #[ with %[
            $code[$pos] = '%';
            $patches[] = [$pos, 'replace', '#'];
            $pos += 2;
        }
        return $code;
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Lexer\TokenEmulator;

use PhpParser\Token;

abstract class KeywordEmulator extends TokenEmulator {
    abstract public function getKeywordString(): string;
    abstract public function getKeywordToken(): int;

    public function isEmulationNeeded(string $code): bool {
        return strpos(strtolower($code), $this->getKeywordString()) !== false;
    }

    /** @param Token[] $tokens */
    protected function isKeywordContext(array $tokens, int $pos): bool {
        $prevToken = $this->getPreviousNonIgnorableToken($tokens, $pos);
        if ($prevToken === null) {
            return false;
        }
        return $prevToken->id !== \T_OBJECT_OPERATOR
            && $prevToken->id !== \T_NULLSAFE_OBJECT_OPERATOR;
    }

    public function emulate(string $code, array $tokens): array {
        $keywordString = $this->getKeywordString();
        foreach ($tokens as $i => $token) {
            if ($token->id === T_STRING && strtolower($token->text) === $keywordString
                    && $this->isKeywordContext($tokens, $i)) {
                $token->id = $this->getKeywordToken();
            }
        }

        return $tokens;
    }

    /** @param Token[] $tokens */
    private function getPreviousNonIgnorableToken(array $tokens, int $start): ?Token {
        for ($i = $start - 1; $i >= 0; --$i) {
            $token = $tokens[$i];
            if ($token->id === T_WHITESPACE || $token->id == T_COMMENT || $token->id === T_DOC_COMMENT) {
                continue;
            }

            return $token;
        }

        return null;
    }

    public function reverseEmulate(string $code, array $tokens): array {
        $keywordToken = $this->getKeywordToken();
        foreach ($tokens as $token) {
            if ($token->id === $keywordToken) {
                $token->id = \T_STRING;
            }
        }

        return $tokens;
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Lexer\TokenEmulator;

use PhpParser\PhpVersion;

// Retained for reverse emulation support only.
final class FnTokenEmulator extends KeywordEmulator {
    public function getPhpVersion(): PhpVersion {
        return PhpVersion::fromString('7.4');
    }

    public function getKeywordString(): string {
        return 'fn';
    }

    public function getKeywordToken(): int {
        return \T_FN;
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Lexer\TokenEmulator;

use PhpParser\PhpVersion;

/**
 * Reverses emulation direction of the inner emulator.
 */
final class ReverseEmulator extends TokenEmulator {
    /** @var TokenEmulator Inner emulator */
    private TokenEmulator $emulator;

    public function __construct(TokenEmulator $emulator) {
        $this->emulator = $emulator;
    }

    public function getPhpVersion(): PhpVersion {
        return $this->emulator->getPhpVersion();
    }

    public function isEmulationNeeded(string $code): bool {
        return $this->emulator->isEmulationNeeded($code);
    }

    public function emulate(string $code, array $tokens): array {
        return $this->emulator->reverseEmulate($code, $tokens);
    }

    public function reverseEmulate(string $code, array $tokens): array {
        return $this->emulator->emulate($code, $tokens);
    }

    public function preprocessCode(string $code, array &$patches): string {
        return $code;
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Lexer\TokenEmulator;

use PhpParser\PhpVersion;

final class PropertyTokenEmulator extends KeywordEmulator {
    public function getPhpVersion(): PhpVersion {
        return PhpVersion::fromComponents(8, 4);
    }

    public function getKeywordString(): string {
        return '__property__';
    }

    public function getKeywordToken(): int {
        return \T_PROPERTY_C;
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Lexer\TokenEmulator;

use PhpParser\PhpVersion;

final class MatchTokenEmulator extends KeywordEmulator {
    public function getPhpVersion(): PhpVersion {
        return PhpVersion::fromComponents(8, 0);
    }

    public function getKeywordString(): string {
        return 'match';
    }

    public function getKeywordToken(): int {
        return \T_MATCH;
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Lexer\TokenEmulator;

use PhpParser\Lexer\TokenEmulator\TokenEmulator;
use PhpParser\PhpVersion;
use PhpParser\Token;

class PipeOperatorEmulator extends TokenEmulator {
    public function getPhpVersion(): PhpVersion {
        return PhpVersion::fromComponents(8, 5);
    }

    public function isEmulationNeeded(string $code): bool {
        return \strpos($code, '|>') !== false;
    }

    public function emulate(string $code, array $tokens): array {
        for ($i = 0, $c = count($tokens); $i < $c; ++$i) {
            $token = $tokens[$i];
            if ($token->text === '|' && isset($tokens[$i + 1]) && $tokens[$i + 1]->text === '>') {
                array_splice($tokens, $i, 2, [
                    new Token(\T_PIPE, '|>', $token->line, $token->pos),
                ]);
                $c--;
            }
        }
        return $tokens;
    }

    public function reverseEmulate(string $code, array $tokens): array {
        for ($i = 0, $c = count($tokens); $i < $c; ++$i) {
            $token = $tokens[$i];
            if ($token->id === \T_PIPE) {
                array_splice($tokens, $i, 1, [
                    new Token(\ord('|'), '|', $token->line, $token->pos),
                    new Token(\ord('>'), '>', $token->line, $token->pos + 1),
                ]);
                $i++;
                $c++;
            }
        }
        return $tokens;
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Lexer\TokenEmulator;

use PhpParser\PhpVersion;
use PhpParser\Token;

class ExplicitOctalEmulator extends TokenEmulator {
    public function getPhpVersion(): PhpVersion {
        return PhpVersion::fromComponents(8, 1);
    }

    public function isEmulationNeeded(string $code): bool {
        return strpos($code, '0o') !== false || strpos($code, '0O') !== false;
    }

    public function emulate(string $code, array $tokens): array {
        for ($i = 0, $c = count($tokens); $i < $c; ++$i) {
            $token = $tokens[$i];
            if ($token->id == \T_LNUMBER && $token->text === '0' &&
                isset($tokens[$i + 1]) && $tokens[$i + 1]->id == \T_STRING &&
                preg_match('/[oO][0-7]+(?:_[0-7]+)*/', $tokens[$i + 1]->text)
            ) {
                $tokenKind = $this->resolveIntegerOrFloatToken($tokens[$i + 1]->text);
                array_splice($tokens, $i, 2, [
                    new Token($tokenKind, '0' . $tokens[$i + 1]->text, $token->line, $token->pos),
                ]);
                $c--;
            }
        }
        return $tokens;
    }

    private function resolveIntegerOrFloatToken(string $str): int {
        $str = substr($str, 1);
        $str = str_replace('_', '', $str);
        $num = octdec($str);
        return is_float($num) ? \T_DNUMBER : \T_LNUMBER;
    }

    public function reverseEmulate(string $code, array $tokens): array {
        // Explicit octals were not legal code previously, don't bother.
        return $tokens;
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Lexer\TokenEmulator;

use PhpParser\PhpVersion;

final class ReadonlyTokenEmulator extends KeywordEmulator {
    public function getPhpVersion(): PhpVersion {
        return PhpVersion::fromComponents(8, 1);
    }

    public function getKeywordString(): string {
        return 'readonly';
    }

    public function getKeywordToken(): int {
        return \T_READONLY;
    }

    protected function isKeywordContext(array $tokens, int $pos): bool {
        if (!parent::isKeywordContext($tokens, $pos)) {
            return false;
        }
        // Support "function readonly("
        return !(isset($tokens[$pos + 1]) &&
                 ($tokens[$pos + 1]->text === '(' ||
                  ($tokens[$pos + 1]->id === \T_WHITESPACE &&
                   isset($tokens[$pos + 2]) &&
                   $tokens[$pos + 2]->text === '(')));
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Lexer\TokenEmulator;

use PhpParser\PhpVersion;
use PhpParser\Token;

final class AsymmetricVisibilityTokenEmulator extends TokenEmulator {
    public function getPhpVersion(): PhpVersion {
        return PhpVersion::fromComponents(8, 4);
    }
    public function isEmulationNeeded(string $code): bool {
        $code = strtolower($code);
        return strpos($code, 'public(set)') !== false ||
            strpos($code, 'protected(set)') !== false ||
            strpos($code, 'private(set)') !== false;
    }

    public function emulate(string $code, array $tokens): array {
        $map = [
            \T_PUBLIC => \T_PUBLIC_SET,
            \T_PROTECTED => \T_PROTECTED_SET,
            \T_PRIVATE => \T_PRIVATE_SET,
        ];
        for ($i = 0, $c = count($tokens); $i < $c; ++$i) {
            $token = $tokens[$i];
            if (isset($map[$token->id]) && $i + 3 < $c && $tokens[$i + 1]->text === '(' &&
                $tokens[$i + 2]->id === \T_STRING && \strtolower($tokens[$i + 2]->text) === 'set' &&
                $tokens[$i + 3]->text === ')' &&
                $this->isKeywordContext($tokens, $i)
            ) {
                array_splice($tokens, $i, 4, [
                    new Token(
                        $map[$token->id], $token->text . '(' . $tokens[$i + 2]->text . ')',
                        $token->line, $token->pos),
                ]);
                $c -= 3;
            }
        }

        return $tokens;
    }

    public function reverseEmulate(string $code, array $tokens): array {
        $reverseMap = [
            \T_PUBLIC_SET => \T_PUBLIC,
            \T_PROTECTED_SET => \T_PROTECTED,
            \T_PRIVATE_SET => \T_PRIVATE,
        ];
        for ($i = 0, $c = count($tokens); $i < $c; ++$i) {
            $token = $tokens[$i];
            if (isset($reverseMap[$token->id]) &&
                \preg_match('/(public|protected|private)\((set)\)/i', $token->text, $matches)
            ) {
                [, $modifier, $set] = $matches;
                $modifierLen = \strlen($modifier);
                array_splice($tokens, $i, 1, [
                    new Token($reverseMap[$token->id], $modifier, $token->line, $token->pos),
                    new Token(\ord('('), '(', $token->line, $token->pos + $modifierLen),
                    new Token(\T_STRING, $set, $token->line, $token->pos + $modifierLen + 1),
                    new Token(\ord(')'), ')', $token->line, $token->pos + $modifierLen + 4),
                ]);
                $i += 3;
                $c += 3;
            }
        }

        return $tokens;
    }

    /** @param Token[] $tokens */
    protected function isKeywordContext(array $tokens, int $pos): bool {
        $prevToken = $this->getPreviousNonSpaceToken($tokens, $pos);
        if ($prevToken === null) {
            return false;
        }
        return $prevToken->id !== \T_OBJECT_OPERATOR
            && $prevToken->id !== \T_NULLSAFE_OBJECT_OPERATOR;
    }

    /** @param Token[] $tokens */
    private function getPreviousNonSpaceToken(array $tokens, int $start): ?Token {
        for ($i = $start - 1; $i >= 0; --$i) {
            if ($tokens[$i]->id === T_WHITESPACE) {
                continue;
            }

            return $tokens[$i];
        }

        return null;
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Lexer\TokenEmulator;

use PhpParser\PhpVersion;
use PhpParser\Token;

final class NullsafeTokenEmulator extends TokenEmulator {
    public function getPhpVersion(): PhpVersion {
        return PhpVersion::fromComponents(8, 0);
    }

    public function isEmulationNeeded(string $code): bool {
        return strpos($code, '?->') !== false;
    }

    public function emulate(string $code, array $tokens): array {
        // We need to manually iterate and manage a count because we'll change
        // the tokens array on the way
        for ($i = 0, $c = count($tokens); $i < $c; ++$i) {
            $token = $tokens[$i];
            if ($token->text === '?' && isset($tokens[$i + 1]) && $tokens[$i + 1]->id === \T_OBJECT_OPERATOR) {
                array_splice($tokens, $i, 2, [
                    new Token(\T_NULLSAFE_OBJECT_OPERATOR, '?->', $token->line, $token->pos),
                ]);
                $c--;
                continue;
            }

            // Handle ?-> inside encapsed string.
            if ($token->id === \T_ENCAPSED_AND_WHITESPACE && isset($tokens[$i - 1])
                && $tokens[$i - 1]->id === \T_VARIABLE
                && preg_match('/^\?->([a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*)/', $token->text, $matches)
            ) {
                $replacement = [
                    new Token(\T_NULLSAFE_OBJECT_OPERATOR, '?->', $token->line, $token->pos),
                    new Token(\T_STRING, $matches[1], $token->line, $token->pos + 3),
                ];
                $matchLen = \strlen($matches[0]);
                if ($matchLen !== \strlen($token->text)) {
                    $replacement[] = new Token(
                        \T_ENCAPSED_AND_WHITESPACE,
                        \substr($token->text, $matchLen),
                        $token->line, $token->pos + $matchLen
                    );
                }
                array_splice($tokens, $i, 1, $replacement);
                $c += \count($replacement) - 1;
                continue;
            }
        }

        return $tokens;
    }

    public function reverseEmulate(string $code, array $tokens): array {
        // ?-> was not valid code previously, don't bother.
        return $tokens;
    }
}
<?php declare(strict_types=1);

namespace PhpParser\Lexer\TokenEmulator;

use PhpParser\PhpVersion;

/*
 * In PHP 8.1, "readonly(" was special cased in the lexer in order to support functions with
 * name readonly. In PHP 8.2, this may conflict with readonly properties having a DNF type. For
 * this reason, PHP 8.2 instead treats this as T_READONLY and then handles it specially in the
 * parser. This emulator only exists to handle this special case, which is skipped by the
 * PHP 8.1 ReadonlyTokenEmulator.
 */
class ReadonlyFunctionTokenEmulator extends KeywordEmulator {
    public function getKeywordString(): string {
        return 'readonly';
    }

    public function getKeywordToken(): int {
        return \T_READONLY;
    }

    public function getPhpVersion(): PhpVersion {
        return PhpVersion::fromComponents(8, 2);
    }

    public function reverseEmulate(string $code, array $tokens): array {
        // Don't bother
        return $tokens;
    }
}
{
    "name": "nikic/php-parser",
    "type": "library",
    "description": "A PHP parser written in PHP",
    "keywords": [
        "php",
        "parser"
    ],
    "license": "BSD-3-Clause",
    "authors": [
        {
            "name": "Nikita Popov"
        }
    ],
    "require": {
        "php": ">=7.4",
        "ext-tokenizer": "*",
        "ext-json": "*"
    },
    "require-dev": {
        "phpunit/phpunit": "^9.0",
        "ircmaxell/php-yacc": "^0.0.7"
    },
    "extra": {
        "branch-alias": {
            "dev-master": "5.x-dev"
        }
    },
    "autoload": {
        "psr-4": {
            "PhpParser\\": "lib/PhpParser"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "PhpParser\\": "test/PhpParser/"
        }
    },
    "bin": [
        "bin/php-parse"
    ]
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog;

/**
 * Handler or Processor implementing this interface will be reset when Logger::reset() is called.
 *
 * Resetting ends a log cycle gets them back to their initial state.
 *
 * Resetting a Handler or a Processor means flushing/cleaning all buffers, resetting internal
 * state, and getting it back to a state in which it can receive log records again.
 *
 * This is useful in case you want to avoid logs leaking between two requests or jobs when you
 * have a long running process like a worker or an application server serving multiple requests
 * in one process.
 *
 * @author GrÃ©goire Pineau <lyrixx@lyrixx.info>
 */
interface ResettableInterface
{
    public function reset(): void;
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\Formatter\FormatterInterface;
use Monolog\Formatter\HtmlFormatter;
use Monolog\LogRecord;

/**
 * Base class for all mail handlers
 *
 * @author Gyula Sallai
 */
abstract class MailHandler extends AbstractProcessingHandler
{
    /**
     * @inheritDoc
     */
    public function handleBatch(array $records): void
    {
        $messages = [];

        foreach ($records as $record) {
            if ($record->level->isLowerThan($this->level)) {
                continue;
            }

            $message = $this->processRecord($record);
            $messages[] = $message;
        }

        if (\count($messages) > 0) {
            $this->send((string) $this->getFormatter()->formatBatch($messages), $messages);
        }
    }

    /**
     * Send a mail with the given content
     *
     * @param string $content formatted email body to be sent
     * @param array  $records the array of log records that formed this content
     *
     * @phpstan-param non-empty-array<LogRecord> $records
     */
    abstract protected function send(string $content, array $records): void;

    /**
     * @inheritDoc
     */
    protected function write(LogRecord $record): void
    {
        $this->send((string) $record->formatted, [$record]);
    }

    /**
     * @phpstan-param non-empty-array<LogRecord> $records
     */
    protected function getHighestRecord(array $records): LogRecord
    {
        $highestRecord = null;
        foreach ($records as $record) {
            if ($highestRecord === null || $record->level->isHigherThan($highestRecord->level)) {
                $highestRecord = $record;
            }
        }

        return $highestRecord;
    }

    protected function isHtmlBody(string $body): bool
    {
        return ($body[0] ?? null) === '<';
    }

    /**
     * Gets the default formatter.
     */
    protected function getDefaultFormatter(): FormatterInterface
    {
        return new HtmlFormatter();
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler\Curl;

use CurlHandle;

/**
 * This class is marked as internal and it is not under the BC promise of the package.
 *
 * @internal
 */
final class Util
{
    /** @var array<int> */
    private static array $retriableErrorCodes = [
        CURLE_COULDNT_RESOLVE_HOST,
        CURLE_COULDNT_CONNECT,
        CURLE_HTTP_NOT_FOUND,
        CURLE_READ_ERROR,
        CURLE_OPERATION_TIMEOUTED,
        CURLE_HTTP_POST_ERROR,
        CURLE_SSL_CONNECT_ERROR,
    ];

    /**
     * Executes a CURL request with optional retries and exception on failure
     *
     * @param  CurlHandle  $ch curl handler
     * @return bool|string @see curl_exec
     */
    public static function execute(CurlHandle $ch, int $retries = 5): bool|string
    {
        while ($retries > 0) {
            $retries--;
            $curlResponse = curl_exec($ch);
            if ($curlResponse === false) {
                $curlErrno = curl_errno($ch);

                if (false === \in_array($curlErrno, self::$retriableErrorCodes, true) || $retries === 0) {
                    $curlError = curl_error($ch);

                    throw new \RuntimeException(sprintf('Curl error (code %d): %s', $curlErrno, $curlError));
                }
                continue;
            }

            return $curlResponse;
        }
        return false;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\Level;
use Swift;
use Swift_Message;

/**
 * MandrillHandler uses cURL to send the emails to the Mandrill API
 *
 * @author Adam Nicholson <adamnicholson10@gmail.com>
 */
class MandrillHandler extends MailHandler
{
    protected Swift_Message $message;
    protected string $apiKey;

    /**
     * @phpstan-param (Swift_Message|callable(): Swift_Message) $message
     *
     * @param string                 $apiKey  A valid Mandrill API key
     * @param callable|Swift_Message $message An example message for real messages, only the body will be replaced
     *
     * @throws \InvalidArgumentException if not a Swift Message is set
     */
    public function __construct(string $apiKey, callable|Swift_Message $message, int|string|Level $level = Level::Error, bool $bubble = true)
    {
        parent::__construct($level, $bubble);

        if (!$message instanceof Swift_Message) {
            $message = $message();
        }
        if (!$message instanceof Swift_Message) {
            throw new \InvalidArgumentException('You must provide either a Swift_Message instance or a callable returning it');
        }
        $this->message = $message;
        $this->apiKey = $apiKey;
    }

    /**
     * @inheritDoc
     */
    protected function send(string $content, array $records): void
    {
        $mime = 'text/plain';
        if ($this->isHtmlBody($content)) {
            $mime = 'text/html';
        }

        $message = clone $this->message;
        $message->setBody($content, $mime);
        /** @phpstan-ignore-next-line */
        if (version_compare(Swift::VERSION, '6.0.0', '>=')) {
            $message->setDate(new \DateTimeImmutable());
        } else {
            /** @phpstan-ignore-next-line */
            $message->setDate(time());
        }

        $ch = curl_init();

        curl_setopt($ch, CURLOPT_URL, 'https://mandrillapp.com/api/1.0/messages/send-raw.json');
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
            'key' => $this->apiKey,
            'raw_message' => (string) $message,
            'async' => false,
        ]));

        Curl\Util::execute($ch);
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\Level;
use Monolog\Logger;
use Psr\Log\LogLevel;
use Monolog\LogRecord;

/**
 * Simple handler wrapper that deduplicates log records across multiple requests
 *
 * It also includes the BufferHandler functionality and will buffer
 * all messages until the end of the request or flush() is called.
 *
 * This works by storing all log records' messages above $deduplicationLevel
 * to the file specified by $deduplicationStore. When further logs come in at the end of the
 * request (or when flush() is called), all those above $deduplicationLevel are checked
 * against the existing stored logs. If they match and the timestamps in the stored log is
 * not older than $time seconds, the new log record is discarded. If no log record is new, the
 * whole data set is discarded.
 *
 * This is mainly useful in combination with Mail handlers or things like Slack or HipChat handlers
 * that send messages to people, to avoid spamming with the same message over and over in case of
 * a major component failure like a database server being down which makes all requests fail in the
 * same way.
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 */
class DeduplicationHandler extends BufferHandler
{
    protected string $deduplicationStore;

    protected Level $deduplicationLevel;

    protected int $time;
    protected bool $gc = false;

    /**
     * @param HandlerInterface             $handler            Handler.
     * @param string|null                  $deduplicationStore The file/path where the deduplication log should be kept
     * @param int|string|Level|LogLevel::* $deduplicationLevel The minimum logging level for log records to be looked at for deduplication purposes
     * @param int                          $time               The period (in seconds) during which duplicate entries should be suppressed after a given log is sent through
     * @param bool                         $bubble             Whether the messages that are handled can bubble up the stack or not
     *
     * @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $deduplicationLevel
     */
    public function __construct(HandlerInterface $handler, ?string $deduplicationStore = null, int|string|Level $deduplicationLevel = Level::Error, int $time = 60, bool $bubble = true)
    {
        parent::__construct($handler, 0, Level::Debug, $bubble, false);

        $this->deduplicationStore = $deduplicationStore === null ? sys_get_temp_dir() . '/monolog-dedup-' . substr(md5(__FILE__), 0, 20) .'.log' : $deduplicationStore;
        $this->deduplicationLevel = Logger::toMonologLevel($deduplicationLevel);
        $this->time = $time;
    }

    public function flush(): void
    {
        if ($this->bufferSize === 0) {
            return;
        }

        $store = null;

        if (file_exists($this->deduplicationStore)) {
            $store = file($this->deduplicationStore, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
        }

        $passthru = null;

        foreach ($this->buffer as $record) {
            if ($record->level->value >= $this->deduplicationLevel->value) {
                $passthru = $passthru === true || !\is_array($store) || !$this->isDuplicate($store, $record);
                if ($passthru) {
                    $line = $this->buildDeduplicationStoreEntry($record);
                    file_put_contents($this->deduplicationStore, $line . "\n", FILE_APPEND | LOCK_EX);
                    if (!\is_array($store)) {
                        $store = [];
                    }
                    $store[] = $line;
                }
            }
        }

        // default of null is valid as well as if no record matches duplicationLevel we just pass through
        if ($passthru === true || $passthru === null) {
            $this->handler->handleBatch($this->buffer);
        }

        $this->clear();

        if ($this->gc) {
            $this->collectLogs();
        }
    }

    /**
     * If there is a store entry older than e.g. a day, this method should set `$this->gc` to `true` to trigger garbage collection.
     * @param string[] $store The deduplication store
     */
    protected function isDuplicate(array $store, LogRecord $record): bool
    {
        $timestampValidity = $record->datetime->getTimestamp() - $this->time;
        $expectedMessage = preg_replace('{[\r\n].*}', '', $record->message);
        $yesterday = time() - 86400;

        for ($i = \count($store) - 1; $i >= 0; $i--) {
            $parts = explode(':', $store[$i], 3);

            if (\count($parts) < 3) {
                // Skip invalid/incomplete lines (e.g. partially written due to concurrent access)
                continue;
            }

            [$timestamp, $level, $message] = $parts;

            if ($level === $record->level->getName() && $message === $expectedMessage && $timestamp > $timestampValidity) {
                return true;
            }

            if ($timestamp < $yesterday) {
                $this->gc = true;
            }
        }

        return false;
    }

    /**
     * @return string The given record serialized as a single line of text
     */
    protected function buildDeduplicationStoreEntry(LogRecord $record): string
    {
        return $record->datetime->getTimestamp() . ':' . $record->level->getName() . ':' . preg_replace('{[\r\n].*}', '', $record->message);
    }

    private function collectLogs(): void
    {
        if (!file_exists($this->deduplicationStore)) {
            return;
        }

        $handle = fopen($this->deduplicationStore, 'rw+');

        if (false === $handle) {
            throw new \RuntimeException('Failed to open file for reading and writing: ' . $this->deduplicationStore);
        }

        if (false === flock($handle, LOCK_EX)) {
            fclose($handle);
            return;
        }
        $validLogs = [];

        $timestampValidity = time() - $this->time;

        while (!feof($handle)) {
            $log = fgets($handle);
            if (\is_string($log) && '' !== $log && substr($log, 0, 10) >= $timestampValidity) {
                $validLogs[] = $log;
            }
        }

        ftruncate($handle, 0);
        rewind($handle);
        foreach ($validLogs as $log) {
            fwrite($handle, $log);
        }

        flock($handle, LOCK_UN);
        fclose($handle);

        $this->gc = false;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Elastic\Transport\Exception\TransportException;
use Elastica\Document;
use Monolog\Formatter\FormatterInterface;
use Monolog\Formatter\ElasticaFormatter;
use Monolog\Level;
use Elastica\Client;
use Elastica\Exception\ExceptionInterface;
use Monolog\LogRecord;
use Monolog\Utils;

/**
 * Elastic Search handler
 *
 * Usage example:
 *
 *    $client = new \Elastica\Client();
 *    $options = array(
 *        'index' => 'elastic_index_name',
 *        'type' => 'elastic_doc_type', Types have been removed in Elastica 7
 *    );
 *    $handler = new ElasticaHandler($client, $options);
 *    $log = new Logger('application');
 *    $log->pushHandler($handler);
 *
 * @author Jelle Vink <jelle.vink@gmail.com>
 * @phpstan-type Options array{
 *     index: string,
 *     type: string,
 *     ignore_error: bool
 * }
 * @phpstan-type InputOptions array{
 *     index?: string,
 *     type?: string,
 *     ignore_error?: bool
 * }
 */
class ElasticaHandler extends AbstractProcessingHandler
{
    protected Client $client;

    /**
     * @var mixed[] Handler config options
     * @phpstan-var Options
     */
    protected array $options;

    /**
     * @param Client  $client  Elastica Client object
     * @param mixed[] $options Handler configuration
     *
     * @phpstan-param InputOptions $options
     */
    public function __construct(Client $client, array $options = [], int|string|Level $level = Level::Debug, bool $bubble = true)
    {
        parent::__construct($level, $bubble);
        $this->client = $client;
        $this->options = array_merge(
            [
                'index'          => 'monolog',      // Elastic index name
                'type'           => 'record',       // Elastic document type
                'ignore_error'   => false,          // Suppress Elastica exceptions
            ],
            $options
        );
    }

    /**
     * @inheritDoc
     */
    protected function write(LogRecord $record): void
    {
        $this->bulkSend([$record->formatted]);
    }

    /**
     * @inheritDoc
     */
    public function setFormatter(FormatterInterface $formatter): HandlerInterface
    {
        if (Utils::unwrapFormatter($formatter) instanceof ElasticaFormatter) {
            return parent::setFormatter($formatter);
        }

        throw new \InvalidArgumentException('ElasticaHandler is only compatible with ElasticaFormatter');
    }

    /**
     * @return mixed[]
     *
     * @phpstan-return Options
     */
    public function getOptions(): array
    {
        return $this->options;
    }

    /**
     * @inheritDoc
     */
    protected function getDefaultFormatter(): FormatterInterface
    {
        return new ElasticaFormatter($this->options['index'], $this->options['type']);
    }

    /**
     * @inheritDoc
     */
    public function handleBatch(array $records): void
    {
        $documents = $this->getFormatter()->formatBatch($records);
        $this->bulkSend($documents);
    }

    /**
     * Use Elasticsearch bulk API to send list of documents
     *
     * @param Document[] $documents
     *
     * @throws \RuntimeException
     */
    protected function bulkSend(array $documents): void
    {
        try {
            $this->client->addDocuments($documents);
        } catch (ExceptionInterface | TransportException $e) {
            if (!$this->options['ignore_error']) {
                throw new \RuntimeException("Error sending messages to Elasticsearch", 0, $e);
            }
        }
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use DateTimeInterface;
use Monolog\Handler\SyslogUdp\UdpSocket;
use Monolog\Level;
use Monolog\LogRecord;
use Monolog\Utils;

/**
 * A Handler for logging to a remote syslogd server.
 *
 * @author Jesper Skovgaard Nielsen <nulpunkt@gmail.com>
 * @author Dominik Kukacka <dominik.kukacka@gmail.com>
 */
class SyslogUdpHandler extends AbstractSyslogHandler
{
    const RFC3164 = 0;
    const RFC5424 = 1;
    const RFC5424e = 2;

    /** @var array<self::RFC*, string> */
    private array $dateFormats = [
        self::RFC3164 => 'M d H:i:s',
        self::RFC5424 => \DateTime::RFC3339,
        self::RFC5424e => \DateTime::RFC3339_EXTENDED,
    ];

    protected UdpSocket $socket;
    protected string $ident;
    /** @var self::RFC* */
    protected int $rfc;

    /**
     * @param  string                    $host     Either IP/hostname or a path to a unix socket (port must be 0 then)
     * @param  int                       $port     Port number, or 0 if $host is a unix socket
     * @param  string|int                $facility Either one of the names of the keys in $this->facilities, or a LOG_* facility constant
     * @param  bool                      $bubble   Whether the messages that are handled can bubble up the stack or not
     * @param  string                    $ident     Program name or tag for each log message.
     * @param  int                       $rfc       RFC to format the message for.
     * @param  ?int                      $maxLength Maximum size in bytes of a single UDP datagram. Defaults to the
     *                                              largest a UDP payload can theoretically be, which risks getting
     *                                              silently truncated or dropped by routers/firewalls that reject
     *                                              fragmented UDP packets; pass a value that fits within the path
     *                                              MTU (eg. 1024) to avoid that.
     * @throws MissingExtensionException when there is no socket extension
     *
     * @phpstan-param self::RFC* $rfc
     */
    public function __construct(string $host, int $port = 514, string|int $facility = LOG_USER, int|string|Level $level = Level::Debug, bool $bubble = true, string $ident = 'php', int $rfc = self::RFC5424, ?int $maxLength = null)
    {
        if (!\extension_loaded('sockets')) {
            throw new MissingExtensionException('The sockets extension is required to use the SyslogUdpHandler');
        }

        parent::__construct($facility, $level, $bubble);

        $this->ident = $ident;
        $this->rfc = $rfc;

        $this->socket = new UdpSocket($host, $port, $maxLength);
    }

    protected function write(LogRecord $record): void
    {
        $lines = $this->splitMessageIntoLines($record->formatted);

        $header = $this->makeCommonSyslogHeader($this->toSyslogPriority($record->level), $record->datetime);

        foreach ($lines as $line) {
            $this->socket->write($line, $header);
        }
    }

    public function close(): void
    {
        $this->socket->close();
    }

    /**
     * @param  string|string[] $message
     * @return string[]
     */
    private function splitMessageIntoLines($message): array
    {
        if (\is_array($message)) {
            $message = implode("\n", $message);
        }

        $lines = preg_split('/$\R?^/m', (string) $message, -1, PREG_SPLIT_NO_EMPTY);
        if (false === $lines) {
            $pcreErrorCode = preg_last_error();

            throw new \RuntimeException('Could not preg_split: ' . $pcreErrorCode . ' / ' . preg_last_error_msg());
        }

        return $lines;
    }

    /**
     * Make common syslog header (see rfc5424 or rfc3164)
     */
    protected function makeCommonSyslogHeader(int $severity, DateTimeInterface $datetime): string
    {
        $priority = $severity + $this->facility;

        $pid = getmypid();
        if (false === $pid) {
            $pid = '-';
        }

        $hostname = gethostname();
        if (false === $hostname) {
            $hostname = '-';
        }

        if ($this->rfc === self::RFC3164) {
            // see https://github.com/phpstan/phpstan/issues/5348
            // @phpstan-ignore-next-line
            $dateNew = $datetime->setTimezone(new \DateTimeZone('UTC'));
            $date = $dateNew->format($this->dateFormats[$this->rfc]);

            return "<$priority>" .
                $date . " " .
                $hostname . " " .
                $this->ident . "[" . $pid . "]: ";
        }

        $date = $datetime->format($this->dateFormats[$this->rfc]);

        return "<$priority>1 " .
            $date . " " .
            $hostname . " " .
            $this->ident . " " .
            $pid . " - - ";
    }

    /**
     * Inject your own socket, mainly used for testing
     *
     * @return $this
     */
    public function setSocket(UdpSocket $socket): self
    {
        $this->socket = $socket;

        return $this;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\Level;
use Monolog\LogRecord;

/**
 * @author Robert Kaufmann III <rok3@rok3.me>
 */
class LogEntriesHandler extends SocketHandler
{
    protected string $logToken;

    /**
     * @param string $token  Log token supplied by LogEntries
     * @param bool   $useSSL Whether or not SSL encryption should be used.
     * @param string $host   Custom hostname to send the data to if needed
     *
     * @throws MissingExtensionException If SSL encryption is set to true and OpenSSL is missing
     */
    public function __construct(
        string $token,
        bool $useSSL = true,
        $level = Level::Debug,
        bool $bubble = true,
        string $host = 'data.logentries.com',
        bool $persistent = false,
        float $timeout = 0.0,
        float $writingTimeout = 10.0,
        ?float $connectionTimeout = null,
        ?int $chunkSize = null
    ) {
        if ($useSSL && !\extension_loaded('openssl')) {
            throw new MissingExtensionException('The OpenSSL PHP plugin is required to use SSL encrypted connection for LogEntriesHandler');
        }

        $endpoint = $useSSL ? 'ssl://' . $host . ':443' : $host . ':80';
        parent::__construct(
            $endpoint,
            $level,
            $bubble,
            $persistent,
            $timeout,
            $writingTimeout,
            $connectionTimeout,
            $chunkSize
        );
        $this->logToken = $token;
    }

    /**
     * @inheritDoc
     */
    protected function generateDataStream(LogRecord $record): string
    {
        return $this->logToken . ' ' . $record->formatted;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

trait WebRequestRecognizerTrait
{
    /**
     * Checks if PHP's serving a web request
     */
    protected function isWebRequest(): bool
    {
        return 'cli' !== \PHP_SAPI && 'phpdbg' !== \PHP_SAPI;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler\SyslogUdp;

use Monolog\Utils;
use Socket;

class UdpSocket
{
    protected const DATAGRAM_MAX_LENGTH = 65023;

    protected string $ip;
    protected int $port;
    protected int $maxLength;
    protected ?Socket $socket = null;

    /**
     * @param ?int $maxLength Maximum size in bytes of a single UDP datagram sent to $ip:$port.
     *                        Defaults to the largest a UDP payload can theoretically be, but
     *                        messages that size get fragmented at the IP level, and many routers
     *                        and firewalls drop fragmented UDP packets, which silently truncates
     *                        or loses the log entry on the receiving end. Passing a value that
     *                        fits within the path MTU (eg. 1024) avoids that.
     */
    public function __construct(string $ip, int $port = 514, ?int $maxLength = null)
    {
        $this->ip = $ip;
        $this->port = $port;
        $this->maxLength = $maxLength ?? self::DATAGRAM_MAX_LENGTH;
    }

    public function write(string $line, string $header = ""): void
    {
        $this->send($this->assembleMessage($line, $header));
    }

    public function close(): void
    {
        if ($this->socket instanceof Socket) {
            socket_close($this->socket);
            $this->socket = null;
        }
    }

    protected function getSocket(): Socket
    {
        if (null !== $this->socket) {
            return $this->socket;
        }

        $domain = AF_INET;
        $protocol = SOL_UDP;
        // Check if we are using unix sockets.
        if ($this->port === 0) {
            $domain = AF_UNIX;
            $protocol = IPPROTO_IP;
        }

        $socket = socket_create($domain, SOCK_DGRAM, $protocol);
        if ($socket instanceof Socket) {
            return $this->socket = $socket;
        }

        throw new \RuntimeException('The UdpSocket to '.$this->ip.':'.$this->port.' could not be opened via socket_create');
    }

    protected function send(string $chunk): void
    {
        socket_sendto($this->getSocket(), $chunk, \strlen($chunk), $flags = 0, $this->ip, $this->port);
    }

    protected function assembleMessage(string $line, string $header): string
    {
        $chunkSize = $this->maxLength - \strlen($header);

        return $header . Utils::substr($line, 0, $chunkSize);
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\Level;
use Psr\Log\LogLevel;
use Monolog\Logger;
use Monolog\LogRecord;

/**
 * Blackhole
 *
 * Any record it can handle will be thrown away. This can be used
 * to put on top of an existing stack to override it temporarily.
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 */
class NullHandler extends Handler
{
    private Level $level;

    /**
     * @param string|int|Level $level The minimum logging level at which this handler will be triggered
     *
     * @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $level
     */
    public function __construct(string|int|Level $level = Level::Debug)
    {
        $this->level = Logger::toMonologLevel($level);
    }

    /**
     * @inheritDoc
     */
    public function isHandling(LogRecord $record): bool
    {
        return $record->level->value >= $this->level->value;
    }

    /**
     * @inheritDoc
     */
    public function handle(LogRecord $record): bool
    {
        return $record->level->value >= $this->level->value;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Gelf\PublisherInterface;
use Monolog\Level;
use Monolog\Formatter\GelfMessageFormatter;
use Monolog\Formatter\FormatterInterface;
use Monolog\LogRecord;

/**
 * Handler to send messages to a Graylog2 (http://www.graylog2.org) server
 *
 * @author Matt Lehner <mlehner@gmail.com>
 * @author Benjamin Zikarsky <benjamin@zikarsky.de>
 */
class GelfHandler extends AbstractProcessingHandler
{
    /**
     * @var PublisherInterface the publisher object that sends the message to the server
     */
    protected PublisherInterface $publisher;

    /**
     * @param PublisherInterface $publisher a gelf publisher object
     */
    public function __construct(PublisherInterface $publisher, int|string|Level $level = Level::Debug, bool $bubble = true)
    {
        parent::__construct($level, $bubble);

        $this->publisher = $publisher;
    }

    /**
     * @inheritDoc
     */
    protected function write(LogRecord $record): void
    {
        $this->publisher->publish($record->formatted);
    }

    /**
     * @inheritDoc
     */
    protected function getDefaultFormatter(): FormatterInterface
    {
        return new GelfMessageFormatter();
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\Level;
use Monolog\ResettableInterface;
use Monolog\Formatter\FormatterInterface;
use Monolog\LogRecord;

/**
 * Buffers all records until closing the handler and then pass them as batch.
 *
 * This is useful for a MailHandler to send only one mail per request instead of
 * sending one per log message.
 *
 * @author Christophe Coevoet <stof@notk.org>
 */
class BufferHandler extends AbstractHandler implements ProcessableHandlerInterface, FormattableHandlerInterface
{
    use ProcessableHandlerTrait;

    protected HandlerInterface $handler;

    protected int $bufferSize = 0;

    protected int $bufferLimit;

    protected bool $flushOnOverflow;

    /** @var LogRecord[] */
    protected array $buffer = [];

    protected bool $initialized = false;

    /**
     * @param HandlerInterface $handler         Handler.
     * @param int              $bufferLimit     How many entries should be buffered at most, beyond that the oldest items are removed from the buffer.
     * @param bool             $flushOnOverflow If true, the buffer is flushed when the max size has been reached, by default oldest entries are discarded
     */
    public function __construct(HandlerInterface $handler, int $bufferLimit = 0, int|string|Level $level = Level::Debug, bool $bubble = true, bool $flushOnOverflow = false)
    {
        parent::__construct($level, $bubble);
        $this->handler = $handler;
        $this->bufferLimit = $bufferLimit;
        $this->flushOnOverflow = $flushOnOverflow;
    }

    /**
     * @inheritDoc
     */
    public function handle(LogRecord $record): bool
    {
        if ($record->level->isLowerThan($this->level)) {
            return false;
        }

        if (!$this->initialized) {
            // __destructor() doesn't get called on Fatal errors
            register_shutdown_function([$this, 'close']);
            $this->initialized = true;
        }

        if ($this->bufferLimit > 0 && $this->bufferSize === $this->bufferLimit) {
            if ($this->flushOnOverflow) {
                $this->flush();
            } else {
                array_shift($this->buffer);
                $this->bufferSize--;
            }
        }

        if (\count($this->processors) > 0) {
            $record = $this->processRecord($record);
        }

        $this->buffer[] = $record;
        $this->bufferSize++;

        return false === $this->bubble;
    }

    public function flush(): void
    {
        if ($this->bufferSize === 0) {
            return;
        }

        $this->handler->handleBatch($this->buffer);
        $this->clear();
    }

    public function __destruct()
    {
        // suppress the parent behavior since we already have register_shutdown_function()
        // to call close(), and the reference contained there will prevent this from being
        // GC'd until the end of the request
    }

    /**
     * @inheritDoc
     */
    public function close(): void
    {
        $this->flush();

        $this->handler->close();
    }

    /**
     * Clears the buffer without flushing any messages down to the wrapped handler.
     */
    public function clear(): void
    {
        $this->bufferSize = 0;
        $this->buffer = [];
    }

    public function reset(): void
    {
        $this->flush();

        parent::reset();

        $this->resetProcessors();

        if ($this->handler instanceof ResettableInterface) {
            $this->handler->reset();
        }
    }

    /**
     * @inheritDoc
     */
    public function setFormatter(FormatterInterface $formatter): HandlerInterface
    {
        if ($this->handler instanceof FormattableHandlerInterface) {
            $this->handler->setFormatter($formatter);

            return $this;
        }

        throw new \UnexpectedValueException('The nested handler of type '.\get_class($this->handler).' does not support formatters.');
    }

    /**
     * @inheritDoc
     */
    public function getFormatter(): FormatterInterface
    {
        if ($this->handler instanceof FormattableHandlerInterface) {
            return $this->handler->getFormatter();
        }

        throw new \UnexpectedValueException('The nested handler of type '.\get_class($this->handler).' does not support formatters.');
    }

    public function setHandler(HandlerInterface $handler): void
    {
        $this->handler = $handler;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\Level;
use Monolog\Utils;
use Monolog\LogRecord;

/**
 * IFTTTHandler uses cURL to trigger IFTTT Maker actions
 *
 * Register a secret key and trigger/event name at https://ifttt.com/maker
 *
 * value1 will be the channel from monolog's Logger constructor,
 * value2 will be the level name (ERROR, WARNING, ..)
 * value3 will be the log record's message
 *
 * @author Nehal Patel <nehal@nehalpatel.me>
 */
class IFTTTHandler extends AbstractProcessingHandler
{
    private string $eventName;
    private string $secretKey;

    /**
     * @param string $eventName The name of the IFTTT Maker event that should be triggered
     * @param string $secretKey A valid IFTTT secret key
     *
     * @throws MissingExtensionException If the curl extension is missing
     */
    public function __construct(string $eventName, string $secretKey, int|string|Level $level = Level::Error, bool $bubble = true)
    {
        if (!\extension_loaded('curl')) {
            throw new MissingExtensionException('The curl extension is needed to use the IFTTTHandler');
        }

        $this->eventName = $eventName;
        $this->secretKey = $secretKey;

        parent::__construct($level, $bubble);
    }

    /**
     * @inheritDoc
     */
    public function write(LogRecord $record): void
    {
        $postData = [
            "value1" => $record->channel,
            "value2" => $record["level_name"],
            "value3" => $record->message,
        ];
        $postString = Utils::jsonEncode($postData);

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, "https://maker.ifttt.com/trigger/" . $this->eventName . "/with/key/" . $this->secretKey);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $postString);
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            "Content-Type: application/json",
        ]);

        Curl\Util::execute($ch);
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\Level;
use Monolog\Utils;
use Monolog\LogRecord;

/**
 * Stores to any stream resource
 *
 * Can be used to store into php://stderr, remote and local files, etc.
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 */
class StreamHandler extends AbstractProcessingHandler
{
    protected const MAX_CHUNK_SIZE = 2147483647;
    /** 10MB */
    protected const DEFAULT_CHUNK_SIZE = 10 * 1024 * 1024;
    protected int $streamChunkSize;
    /** @var resource|null */
    protected $stream;
    protected string|null $url = null;
    private string|null $errorMessage = null;
    protected int|null $filePermission;
    protected bool $useLocking;
    protected string $fileOpenMode;
    /** @var true|null */
    private bool|null $dirCreated = null;
    private bool $retrying = false;
    private int|null $inodeUrl = null;

    /**
     * @param resource|string $stream         If a missing path can't be created, an UnexpectedValueException will be thrown on first write
     * @param int|null        $filePermission Optional file permissions (default (0644) are only for owner read/write)
     * @param bool            $useLocking     Try to lock log file before doing any writes
     * @param string          $fileOpenMode   The fopen() mode used when opening a file, if $stream is a file path
     *
     * @throws \InvalidArgumentException If stream is not a resource or string
     */
    public function __construct($stream, int|string|Level $level = Level::Debug, bool $bubble = true, ?int $filePermission = null, bool $useLocking = false, string $fileOpenMode = 'a')
    {
        parent::__construct($level, $bubble);

        if (($phpMemoryLimit = Utils::expandIniShorthandBytes(\ini_get('memory_limit'))) !== false) {
            if ($phpMemoryLimit > 0) {
                // use max 10% of allowed memory for the chunk size, and at least 100KB
                $this->streamChunkSize = min(static::MAX_CHUNK_SIZE, max((int) ($phpMemoryLimit / 10), 100 * 1024));
            } else {
                // memory is unlimited, set to the default 10MB
                $this->streamChunkSize = static::DEFAULT_CHUNK_SIZE;
            }
        } else {
            // no memory limit information, set to the default 10MB
            $this->streamChunkSize = static::DEFAULT_CHUNK_SIZE;
        }

        if (\is_resource($stream)) {
            $this->stream = $stream;

            stream_set_chunk_size($this->stream, $this->streamChunkSize);
        } elseif (\is_string($stream)) {
            $this->url = Utils::canonicalizePath($stream);
        } else {
            throw new \InvalidArgumentException('A stream must either be a resource or a string.');
        }

        $this->fileOpenMode = $fileOpenMode;
        $this->filePermission = $filePermission;
        $this->useLocking = $useLocking;
    }

    /**
     * @inheritDoc
     */
    public function reset(): void
    {
        parent::reset();

        // auto-close on reset to make sure we periodically close the file in long running processes
        // as long as they correctly call reset() between jobs
        if ($this->url !== null && $this->url !== 'php://memory') {
            $this->close();
        }
    }

    /**
     * @inheritDoc
     */
    public function close(): void
    {
        if (null !== $this->url && \is_resource($this->stream)) {
            fclose($this->stream);
        }
        $this->stream = null;
        $this->dirCreated = null;
    }

    /**
     * Return the currently active stream if it is open
     *
     * @return resource|null
     */
    public function getStream()
    {
        return $this->stream;
    }

    /**
     * Return the stream URL if it was configured with a URL and not an active resource
     */
    public function getUrl(): ?string
    {
        return $this->url;
    }

    public function getStreamChunkSize(): int
    {
        return $this->streamChunkSize;
    }

    /**
     * @inheritDoc
     */
    protected function write(LogRecord $record): void
    {
        if ($this->hasUrlInodeWasChanged()) {
            $this->close();
            $this->write($record);

            return;
        }

        if (!\is_resource($this->stream)) {
            $url = $this->url;
            if (null === $url || '' === $url) {
                throw new \LogicException('Missing stream url, the stream can not be opened. This may be caused by a premature call to close().' . Utils::getRecordMessageForException($record));
            }
            $this->createDir($url);
            $this->errorMessage = null;
            // forwarding to $this->customErrorHandler using a closure to make the
            // private method accessible, see https://github.com/Seldaek/monolog/issues/1866
            set_error_handler(function (int $code, string $msg) {
                return $this->customErrorHandler($code, $msg);
            });

            try {
                $stream = fopen($url, $this->fileOpenMode);
                if ($this->filePermission !== null) {
                    @chmod($url, $this->filePermission);
                }
            } finally {
                restore_error_handler();
            }
            if (!\is_resource($stream)) {
                $this->stream = null;

                throw new \UnexpectedValueException(sprintf('The stream or file "%s" could not be opened in append mode: '.$this->errorMessage, $url) . Utils::getRecordMessageForException($record));
            }
            stream_set_chunk_size($stream, $this->streamChunkSize);
            $this->stream = $stream;
            $this->inodeUrl = $this->getInodeFromUrl();
        }

        $stream = $this->stream;
        if ($this->useLocking) {
            // ignoring errors here, there's not much we can do about them
            flock($stream, LOCK_EX);
        }

        $this->errorMessage = null;
        // forwarding to $this->customErrorHandler using a closure to make the
        // private method accessible, see https://github.com/Seldaek/monolog/issues/1866
        set_error_handler(function (int $code, string $msg) {
            return $this->customErrorHandler($code, $msg);
        });
        try {
            $this->streamWrite($stream, $record);
        } finally {
            restore_error_handler();
        }
        if ($this->errorMessage !== null) {
            $error = $this->errorMessage;
            // close the resource if possible to reopen it, and retry the failed write
            if (!$this->retrying && $this->url !== null && $this->url !== 'php://memory') {
                $this->retrying = true;
                $this->close();
                $this->write($record);

                return;
            }

            throw new \UnexpectedValueException('Writing to the log file failed: '.$error . Utils::getRecordMessageForException($record));
        }

        $this->retrying = false;
        if ($this->useLocking) {
            flock($stream, LOCK_UN);
        }
    }

    /**
     * Write to stream
     * @param resource $stream
     */
    protected function streamWrite($stream, LogRecord $record): void
    {
        $data = (string) $record->formatted;
        $length = \strlen($data);
        $written = 0;

        // fwrite() may write only part of the data on non-blocking streams, so loop until it is all written, see https://github.com/Seldaek/monolog/issues/2011
        while ($written < $length) {
            $result = fwrite($stream, substr($data, $written));
            if ($result === false) {
                // write failed, let the customErrorHandler/errorMessage logic in write() report and retry it
                break;
            }
            if ($result === 0) {
                // the non-blocking stream's buffer is full, wait until it can be written to again instead of busy-looping or dropping the rest of the record
                $write = [$stream];
                $read = $except = [];
                stream_select($read, $write, $except, null);
                continue;
            }
            $written += $result;
        }
    }

    /**
     * @return true
     */
    private function customErrorHandler(int $code, string $msg): bool
    {
        $this->errorMessage = preg_replace('{^(fopen|mkdir|fwrite)\(.*?\): }', '', $msg);

        return true;
    }

    private function getDirFromStream(string $stream): ?string
    {
        $pos = strpos($stream, '://');
        if ($pos === false) {
            return \dirname($stream);
        }

        if ('file://' === substr($stream, 0, 7)) {
            return \dirname(substr($stream, 7));
        }

        return null;
    }

    private function createDir(string $url): void
    {
        // Do not try to create dir if it has already been tried.
        if (true === $this->dirCreated) {
            return;
        }

        $dir = $this->getDirFromStream($url);
        if (null !== $dir && !is_dir($dir)) {
            $this->errorMessage = null;
            // forwarding to $this->customErrorHandler using a closure to make the
            // private method accessible, see https://github.com/Seldaek/monolog/issues/1866
            set_error_handler(function (int $code, string $msg) {
                return $this->customErrorHandler($code, $msg);
            });
            $status = mkdir($dir, 0777, true);
            restore_error_handler();
            if (false === $status && !is_dir($dir) && strpos((string) $this->errorMessage, 'File exists') === false) {
                throw new \UnexpectedValueException(sprintf('There is no existing directory at "%s" and it could not be created: '.$this->errorMessage, $dir));
            }
        }
        $this->dirCreated = true;
    }

    private function getInodeFromUrl(): ?int
    {
        if ($this->url === null || str_starts_with($this->url, 'php://')) {
            return null;
        }

        $inode = @fileinode($this->url);

        return $inode === false ? null : $inode;
    }

    private function hasUrlInodeWasChanged(): bool
    {
        if ($this->inodeUrl === null || $this->retrying || $this->inodeUrl === $this->getInodeFromUrl()) {
            return false;
        }

        $this->retrying = true;

        return true;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\Formatter\LineFormatter;
use Monolog\Formatter\FormatterInterface;
use Monolog\Level;
use Monolog\LogRecord;
use Predis\Client as Predis;
use Redis;

/**
 * Logs to a Redis key using rpush
 *
 * usage example:
 *
 *   $log = new Logger('application');
 *   $redis = new RedisHandler(new Predis\Client("tcp://localhost:6379"), "logs");
 *   $log->pushHandler($redis);
 *
 * @author Thomas Tourlourat <thomas@tourlourat.com>
 */
class RedisHandler extends AbstractProcessingHandler
{
    /** @var Predis<Predis>|Redis */
    private Predis|Redis $redisClient;
    private string $redisKey;
    protected int $capSize;

    /**
     * @param Predis<Predis>|Redis $redis   The redis instance
     * @param string               $key     The key name to push records to
     * @param int                  $capSize Number of entries to limit list size to, 0 = unlimited
     */
    public function __construct(Predis|Redis $redis, string $key, int|string|Level $level = Level::Debug, bool $bubble = true, int $capSize = 0)
    {
        $this->redisClient = $redis;
        $this->redisKey = $key;
        $this->capSize = $capSize;

        parent::__construct($level, $bubble);
    }

    /**
     * @inheritDoc
     */
    protected function write(LogRecord $record): void
    {
        if ($this->capSize > 0) {
            $this->writeCapped($record);
        } else {
            $this->redisClient->rpush($this->redisKey, $record->formatted);
        }
    }

    /**
     * Write and cap the collection
     * Writes the record to the redis list and caps its
     */
    protected function writeCapped(LogRecord $record): void
    {
        if ($this->redisClient instanceof Redis) {
            $mode = \defined('Redis::MULTI') ? Redis::MULTI : 1;
            $this->redisClient->multi($mode)
                ->rPush($this->redisKey, $record->formatted)
                ->ltrim($this->redisKey, -$this->capSize, -1)
                ->exec();
        } else {
            $redisKey = $this->redisKey;
            $capSize = $this->capSize;
            $this->redisClient->transaction(function ($tx) use ($record, $redisKey, $capSize) {
                $tx->rpush($redisKey, $record->formatted);
                $tx->ltrim($redisKey, -$capSize, -1);
            });
        }
    }

    /**
     * @inheritDoc
     */
    protected function getDefaultFormatter(): FormatterInterface
    {
        return new LineFormatter();
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\Formatter\FormatterInterface;
use Monolog\Formatter\LineFormatter;

/**
 * Helper trait for implementing FormattableInterface
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 */
trait FormattableHandlerTrait
{
    protected FormatterInterface|null $formatter = null;

    /**
     * @inheritDoc
     */
    public function setFormatter(FormatterInterface $formatter): HandlerInterface
    {
        $this->formatter = $formatter;

        return $this;
    }

    /**
     * @inheritDoc
     */
    public function getFormatter(): FormatterInterface
    {
        if (null === $this->formatter) {
            $this->formatter = $this->getDefaultFormatter();
        }

        return $this->formatter;
    }

    /**
     * Gets the default formatter.
     *
     * Overwrite this if the LineFormatter is not a good default for your handler.
     */
    protected function getDefaultFormatter(): FormatterInterface
    {
        return new LineFormatter();
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Closure;
use Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy;
use Monolog\Handler\FingersCrossed\ActivationStrategyInterface;
use Monolog\Level;
use Monolog\Logger;
use Monolog\ResettableInterface;
use Monolog\Formatter\FormatterInterface;
use Psr\Log\LogLevel;
use Monolog\LogRecord;

/**
 * Buffers all records until a certain level is reached
 *
 * The advantage of this approach is that you don't get any clutter in your log files.
 * Only requests which actually trigger an error (or whatever your actionLevel is) will be
 * in the logs, but they will contain all records, not only those above the level threshold.
 *
 * You can then have a passthruLevel as well which means that at the end of the request,
 * even if it did not get activated, it will still send through log records of e.g. at least a
 * warning level.
 *
 * You can find the various activation strategies in the
 * Monolog\Handler\FingersCrossed\ namespace.
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 */
class FingersCrossedHandler extends Handler implements ProcessableHandlerInterface, ResettableInterface, FormattableHandlerInterface
{
    use ProcessableHandlerTrait;

    /**
     * Handler or factory Closure($record, $this)
     *
     * @phpstan-var (Closure(LogRecord|null, HandlerInterface): HandlerInterface)|HandlerInterface
     */
    protected Closure|HandlerInterface $handler;

    protected ActivationStrategyInterface $activationStrategy;

    protected bool $buffering = true;

    protected int $bufferSize;

    /** @var LogRecord[] */
    protected array $buffer = [];

    protected bool $stopBuffering;

    protected Level|null $passthruLevel = null;

    protected bool $bubble;

    /**
     * @phpstan-param (Closure(LogRecord|null, HandlerInterface): HandlerInterface)|HandlerInterface $handler
     *
     * @param Closure|HandlerInterface          $handler            Handler or factory Closure($record|null, $fingersCrossedHandler).
     * @param int|string|Level|LogLevel::*|null $activationStrategy Strategy which determines when this handler takes action, or a level name/value at which the handler is activated
     * @param int                               $bufferSize         How many entries should be buffered at most, beyond that the oldest items are removed from the buffer.
     * @param bool                              $bubble             Whether the messages that are handled can bubble up the stack or not
     * @param bool                              $stopBuffering      Whether the handler should stop buffering after being triggered (default true)
     * @param int|string|Level|LogLevel::*|null $passthruLevel      Minimum level to always flush to handler on close, even if strategy not triggered
     *
     * @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::*|ActivationStrategyInterface|null $activationStrategy
     * @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::*|null $passthruLevel
     */
    public function __construct(Closure|HandlerInterface $handler, int|string|Level|ActivationStrategyInterface|null $activationStrategy = null, int $bufferSize = 0, bool $bubble = true, bool $stopBuffering = true, int|string|Level|null $passthruLevel = null)
    {
        if (null === $activationStrategy) {
            $activationStrategy = new ErrorLevelActivationStrategy(Level::Warning);
        }

        // convert simple int activationStrategy to an object
        if (!$activationStrategy instanceof ActivationStrategyInterface) {
            $activationStrategy = new ErrorLevelActivationStrategy($activationStrategy);
        }

        $this->handler = $handler;
        $this->activationStrategy = $activationStrategy;
        $this->bufferSize = $bufferSize;
        $this->bubble = $bubble;
        $this->stopBuffering = $stopBuffering;

        if ($passthruLevel !== null) {
            $this->passthruLevel = Logger::toMonologLevel($passthruLevel);
        }
    }

    /**
     * @inheritDoc
     */
    public function isHandling(LogRecord $record): bool
    {
        return true;
    }

    /**
     * Manually activate this logger regardless of the activation strategy
     */
    public function activate(): void
    {
        if ($this->stopBuffering) {
            $this->buffering = false;
        }

        $this->getHandler(end($this->buffer) ?: null)->handleBatch($this->buffer);
        $this->buffer = [];
    }

    /**
     * @inheritDoc
     */
    public function handle(LogRecord $record): bool
    {
        if (\count($this->processors) > 0) {
            $record = $this->processRecord($record);
        }

        if ($this->buffering) {
            $this->buffer[] = $record;
            if ($this->bufferSize > 0 && \count($this->buffer) > $this->bufferSize) {
                array_shift($this->buffer);
            }
            if ($this->activationStrategy->isHandlerActivated($record)) {
                $this->activate();
            }
        } else {
            $this->getHandler($record)->handle($record);
        }

        return false === $this->bubble;
    }

    /**
     * @inheritDoc
     */
    public function close(): void
    {
        $this->flushBuffer();

        $this->getHandler()->close();
    }

    public function reset(): void
    {
        $this->flushBuffer();

        $this->resetProcessors();

        if ($this->getHandler() instanceof ResettableInterface) {
            $this->getHandler()->reset();
        }
    }

    /**
     * Clears the buffer without flushing any messages down to the wrapped handler.
     *
     * It also resets the handler to its initial buffering state.
     */
    public function clear(): void
    {
        $this->buffer = [];
        $this->reset();
    }

    /**
     * Resets the state of the handler. Stops forwarding records to the wrapped handler.
     */
    private function flushBuffer(): void
    {
        if (null !== $this->passthruLevel) {
            $passthruLevel = $this->passthruLevel;
            $this->buffer = array_filter($this->buffer, static function ($record) use ($passthruLevel) {
                return $passthruLevel->includes($record->level);
            });
            if (\count($this->buffer) > 0) {
                $this->getHandler(end($this->buffer))->handleBatch($this->buffer);
            }
        }

        $this->buffer = [];
        $this->buffering = true;
    }

    /**
     * Return the nested handler
     *
     * If the handler was provided as a factory, this will trigger the handler's instantiation.
     */
    public function getHandler(LogRecord|null $record = null): HandlerInterface
    {
        if (!$this->handler instanceof HandlerInterface) {
            $handler = ($this->handler)($record, $this);
            if (!$handler instanceof HandlerInterface) {
                throw new \RuntimeException("The factory Closure should return a HandlerInterface");
            }
            $this->handler = $handler;
        }

        return $this->handler;
    }

    /**
     * @inheritDoc
     */
    public function setFormatter(FormatterInterface $formatter): HandlerInterface
    {
        $handler = $this->getHandler();
        if ($handler instanceof FormattableHandlerInterface) {
            $handler->setFormatter($formatter);

            return $this;
        }

        throw new \UnexpectedValueException('The nested handler of type '.\get_class($handler).' does not support formatters.');
    }

    /**
     * @inheritDoc
     */
    public function getFormatter(): FormatterInterface
    {
        $handler = $this->getHandler();
        if ($handler instanceof FormattableHandlerInterface) {
            return $handler->getFormatter();
        }

        throw new \UnexpectedValueException('The nested handler of type '.\get_class($handler).' does not support formatters.');
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\Level;
use Psr\Log\LoggerInterface;
use Monolog\Formatter\FormatterInterface;
use Monolog\LogRecord;

/**
 * Proxies log messages to an existing PSR-3 compliant logger.
 *
 * If a formatter is configured, the formatter's output MUST be a string and the
 * formatted message will be fed to the wrapped PSR logger instead of the original
 * log record's message.
 *
 * @author Michael Moussa <michael.moussa@gmail.com>
 */
class PsrHandler extends AbstractHandler implements FormattableHandlerInterface
{
    /**
     * PSR-3 compliant logger
     */
    protected LoggerInterface $logger;

    protected FormatterInterface|null $formatter = null;
    private bool $includeExtra;

    /**
     * @param LoggerInterface $logger The underlying PSR-3 compliant logger to which messages will be proxied
     */
    public function __construct(LoggerInterface $logger, int|string|Level $level = Level::Debug, bool $bubble = true, bool $includeExtra = false)
    {
        parent::__construct($level, $bubble);

        $this->logger = $logger;
        $this->includeExtra = $includeExtra;
    }

    /**
     * @inheritDoc
     */
    public function handle(LogRecord $record): bool
    {
        if (!$this->isHandling($record)) {
            return false;
        }

        $message = $this->formatter !== null
            ? (string) $this->formatter->format($record)
            : $record->message;

        $context = $this->includeExtra
            ? [...$record->extra, ...$record->context]
            : $record->context;

        $this->logger->log($record->level->toPsrLogLevel(), $message, $context);

        return false === $this->bubble;
    }

    /**
     * Sets the formatter.
     */
    public function setFormatter(FormatterInterface $formatter): HandlerInterface
    {
        $this->formatter = $formatter;

        return $this;
    }

    /**
     * Gets the formatter.
     */
    public function getFormatter(): FormatterInterface
    {
        if ($this->formatter === null) {
            throw new \LogicException('No formatter has been set and this handler does not have a default formatter');
        }

        return $this->formatter;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

/**
 * Exception can be thrown if an extension for a handler is missing
 *
 * @author Christian Bergau <cbergau86@gmail.com>
 */
class MissingExtensionException extends \Exception
{
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Throwable;
use Monolog\LogRecord;

/**
 * Forwards records to at most one handler
 *
 * If a handler fails, the exception is suppressed and the record is forwarded to the next handler.
 *
 * As soon as one handler handles a record successfully, the handling stops there.
 */
class FallbackGroupHandler extends GroupHandler
{
    /**
     * @inheritDoc
     */
    public function handle(LogRecord $record): bool
    {
        if (\count($this->processors) > 0) {
            $record = $this->processRecord($record);
        }
        foreach ($this->handlers as $handler) {
            try {
                $handler->handle(clone $record);
                break;
            } catch (Throwable $e) {
                // What throwable?
            }
        }

        return false === $this->bubble;
    }

    /**
     * @inheritDoc
     */
    public function handleBatch(array $records): void
    {
        if (\count($this->processors) > 0) {
            $processed = [];
            foreach ($records as $record) {
                $processed[] = $this->processRecord($record);
            }
            $records = $processed;
        }

        foreach ($this->handlers as $handler) {
            try {
                $handler->handleBatch(array_map(fn ($record) => clone $record, $records));
                break;
            } catch (Throwable $e) {
                // What throwable?
            }
        }
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\Level;
use Monolog\LogRecord;

/**
 * Logs to syslog service.
 *
 * usage example:
 *
 *   $log = new Logger('application');
 *   $syslog = new SyslogHandler('myfacility', 'local6');
 *   $formatter = new LineFormatter("%channel%.%level_name%: %message% %extra%");
 *   $syslog->setFormatter($formatter);
 *   $log->pushHandler($syslog);
 *
 * @author Sven Paulus <sven@karlsruhe.org>
 */
class SyslogHandler extends AbstractSyslogHandler
{
    protected string $ident;
    protected int $logopts;

    /**
     * @param string|int $facility Either one of the names of the keys in $this->facilities, or a LOG_* facility constant
     * @param int        $logopts  Option flags for the openlog() call, defaults to LOG_PID
     */
    public function __construct(string $ident, string|int $facility = LOG_USER, int|string|Level $level = Level::Debug, bool $bubble = true, int $logopts = LOG_PID)
    {
        parent::__construct($facility, $level, $bubble);

        $this->ident = $ident;
        $this->logopts = $logopts;
    }

    /**
     * @inheritDoc
     */
    public function close(): void
    {
        closelog();
    }

    /**
     * @inheritDoc
     */
    protected function write(LogRecord $record): void
    {
        openlog($this->ident, $this->logopts, $this->facility);
        syslog($this->toSyslogPriority($record->level), (string) $record->formatted);
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\Level;
use Monolog\Logger;
use Monolog\Utils;
use Psr\Log\LogLevel;
use Monolog\LogRecord;

/**
 * Sends notifications through the pushover api to mobile phones
 *
 * @author Sebastian GÃ¶ttschkes <sebastian.goettschkes@googlemail.com>
 * @see    https://www.pushover.net/api
 */
class PushoverHandler extends SocketHandler
{
    private string $token;

    /** @var array<int|string> */
    private array $users;

    private string $title;

    private string|int|null $user = null;

    private int $retry;

    private int $expire;

    private Level $highPriorityLevel;

    private Level $emergencyLevel;

    private bool $useFormattedMessage = false;

    /**
     * All parameters that can be sent to Pushover
     * @see https://pushover.net/api
     * @var array<string, bool>
     */
    private array $parameterNames = [
        'token' => true,
        'user' => true,
        'message' => true,
        'device' => true,
        'title' => true,
        'url' => true,
        'url_title' => true,
        'priority' => true,
        'timestamp' => true,
        'sound' => true,
        'retry' => true,
        'expire' => true,
        'callback' => true,
    ];

    /**
     * Sounds the api supports by default
     * @see https://pushover.net/api#sounds
     * @var string[]
     */
    private array $sounds = [
        'pushover', 'bike', 'bugle', 'cashregister', 'classical', 'cosmic', 'falling', 'gamelan', 'incoming',
        'intermission', 'magic', 'mechanical', 'pianobar', 'siren', 'spacealarm', 'tugboat', 'alien', 'climb',
        'persistent', 'echo', 'updown', 'none',
    ];

    /**
     * @param string       $token  Pushover api token
     * @param string|array $users  Pushover user id or array of ids the message will be sent to
     * @param string|null  $title  Title sent to the Pushover API
     * @param bool         $useSSL Whether to connect via SSL. Required when pushing messages to users that are not
     *                             the pushover.net app owner. OpenSSL is required for this option.
     * @param int          $retry  The retry parameter specifies how often (in seconds) the Pushover servers will
     *                             send the same notification to the user.
     * @param int          $expire The expire parameter specifies how many seconds your notification will continue
     *                             to be retried for (every retry seconds).
     *
     * @param int|string|Level|LogLevel::* $highPriorityLevel The minimum logging level at which this handler will start
     *                                                        sending "high priority" requests to the Pushover API
     * @param int|string|Level|LogLevel::* $emergencyLevel    The minimum logging level at which this handler will start
     *                                                        sending "emergency" requests to the Pushover API
     *
     *
     * @phpstan-param string|array<int|string>    $users
     * @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $highPriorityLevel
     * @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $emergencyLevel
     */
    public function __construct(
        string $token,
        $users,
        ?string $title = null,
        int|string|Level $level = Level::Critical,
        bool $bubble = true,
        bool $useSSL = true,
        int|string|Level $highPriorityLevel = Level::Critical,
        int|string|Level $emergencyLevel = Level::Emergency,
        int $retry = 30,
        int $expire = 25200,
        bool $persistent = false,
        float $timeout = 0.0,
        float $writingTimeout = 10.0,
        ?float $connectionTimeout = null,
        ?int $chunkSize = null
    ) {
        $connectionString = $useSSL ? 'ssl://api.pushover.net:443' : 'api.pushover.net:80';
        parent::__construct(
            $connectionString,
            $level,
            $bubble,
            $persistent,
            $timeout,
            $writingTimeout,
            $connectionTimeout,
            $chunkSize
        );

        $this->token = $token;
        $this->users = (array) $users;
        $this->title = $title ?? (string) gethostname();
        $this->highPriorityLevel = Logger::toMonologLevel($highPriorityLevel);
        $this->emergencyLevel = Logger::toMonologLevel($emergencyLevel);
        $this->retry = $retry;
        $this->expire = $expire;
    }

    protected function generateDataStream(LogRecord $record): string
    {
        $content = $this->buildContent($record);

        return $this->buildHeader($content) . $content;
    }

    private function buildContent(LogRecord $record): string
    {
        // Pushover has a limit of 512 characters on title and message combined.
        $maxMessageLength = 512 - \strlen($this->title);

        $message = ($this->useFormattedMessage) ? $record->formatted : $record->message;
        $message = Utils::substr($message, 0, $maxMessageLength);

        $timestamp = $record->datetime->getTimestamp();

        $dataArray = [
            'token' => $this->token,
            'user' => $this->user,
            'message' => $message,
            'title' => $this->title,
            'timestamp' => $timestamp,
        ];

        if ($record->level->value >= $this->emergencyLevel->value) {
            $dataArray['priority'] = 2;
            $dataArray['retry'] = $this->retry;
            $dataArray['expire'] = $this->expire;
        } elseif ($record->level->value >= $this->highPriorityLevel->value) {
            $dataArray['priority'] = 1;
        }

        // First determine the available parameters
        $context = array_intersect_key($record->context, $this->parameterNames);
        $extra = array_intersect_key($record->extra, $this->parameterNames);

        // Least important info should be merged with subsequent info
        $dataArray = array_merge($extra, $context, $dataArray);

        // Only pass sounds that are supported by the API
        if (isset($dataArray['sound']) && !\in_array($dataArray['sound'], $this->sounds, true)) {
            unset($dataArray['sound']);
        }

        return http_build_query($dataArray);
    }

    private function buildHeader(string $content): string
    {
        $header = "POST /1/messages.json HTTP/1.1\r\n";
        $header .= "Host: api.pushover.net\r\n";
        $header .= "Content-Type: application/x-www-form-urlencoded\r\n";
        $header .= "Content-Length: " . \strlen($content) . "\r\n";
        $header .= "\r\n";

        return $header;
    }

    protected function write(LogRecord $record): void
    {
        foreach ($this->users as $user) {
            $this->user = $user;

            parent::write($record);
            $this->closeSocket();
        }

        $this->user = null;
    }

    /**
     * @param  int|string|Level|LogLevel::* $level
     * @return $this
     *
     * @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $level
     */
    public function setHighPriorityLevel(int|string|Level $level): self
    {
        $this->highPriorityLevel = Logger::toMonologLevel($level);

        return $this;
    }

    /**
     * @param  int|string|Level|LogLevel::* $level
     * @return $this
     *
     * @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $level
     */
    public function setEmergencyLevel(int|string|Level $level): self
    {
        $this->emergencyLevel = Logger::toMonologLevel($level);

        return $this;
    }

    /**
     * Use the formatted message?
     *
     * @return $this
     */
    public function useFormattedMessage(bool $useFormattedMessage): self
    {
        $this->useFormattedMessage = $useFormattedMessage;

        return $this;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\Formatter\FormatterInterface;
use Monolog\Formatter\LineFormatter;
use Monolog\Level;
use Monolog\LogRecord;

/**
 * Sends logs to Fleep.io using Webhook integrations
 *
 * You'll need a Fleep.io account to use this handler.
 *
 * @see https://fleep.io/integrations/webhooks/ Fleep Webhooks Documentation
 * @author Ando Roots <ando@sqroot.eu>
 */
class FleepHookHandler extends SocketHandler
{
    protected const FLEEP_HOST = 'fleep.io';

    protected const FLEEP_HOOK_URI = '/hook/';

    /**
     * @var string Webhook token (specifies the conversation where logs are sent)
     */
    protected string $token;

    /**
     * Construct a new Fleep.io Handler.
     *
     * For instructions on how to create a new web hook in your conversations
     * see https://fleep.io/integrations/webhooks/
     *
     * @param  string                    $token Webhook token
     * @throws MissingExtensionException if OpenSSL is missing
     */
    public function __construct(
        string $token,
        $level = Level::Debug,
        bool $bubble = true,
        bool $persistent = false,
        float $timeout = 0.0,
        float $writingTimeout = 10.0,
        ?float $connectionTimeout = null,
        ?int $chunkSize = null
    ) {
        if (!\extension_loaded('openssl')) {
            throw new MissingExtensionException('The OpenSSL PHP extension is required to use the FleepHookHandler');
        }

        $this->token = $token;

        $connectionString = 'ssl://' . static::FLEEP_HOST . ':443';
        parent::__construct(
            $connectionString,
            $level,
            $bubble,
            $persistent,
            $timeout,
            $writingTimeout,
            $connectionTimeout,
            $chunkSize
        );
    }

    /**
     * Returns the default formatter to use with this handler
     *
     * Overloaded to remove empty context and extra arrays from the end of the log message.
     *
     * @return LineFormatter
     */
    protected function getDefaultFormatter(): FormatterInterface
    {
        return new LineFormatter(null, null, true, true);
    }

    /**
     * Handles a log record
     */
    public function write(LogRecord $record): void
    {
        parent::write($record);
        $this->closeSocket();
    }

    /**
     * @inheritDoc
     */
    protected function generateDataStream(LogRecord $record): string
    {
        $content = $this->buildContent($record);

        return $this->buildHeader($content) . $content;
    }

    /**
     * Builds the header of the API Call
     */
    private function buildHeader(string $content): string
    {
        $header = "POST " . static::FLEEP_HOOK_URI . $this->token . " HTTP/1.1\r\n";
        $header .= "Host: " . static::FLEEP_HOST . "\r\n";
        $header .= "Content-Type: application/x-www-form-urlencoded\r\n";
        $header .= "Content-Length: " . \strlen($content) . "\r\n";
        $header .= "\r\n";

        return $header;
    }

    /**
     * Builds the body of API call
     */
    private function buildContent(LogRecord $record): string
    {
        $dataArray = [
            'message' => $record->formatted,
        ];

        return http_build_query($dataArray);
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use RuntimeException;
use Monolog\Level;
use Monolog\Utils;
use Monolog\LogRecord;

/**
 * Handler sends logs to Telegram using Telegram Bot API.
 *
 * How to use:
 *  1) Create a Telegram bot with https://telegram.me/BotFather;
 *  2) Create a Telegram channel or a group where logs will be recorded;
 *  3) Add the created bot from step 1 to the created channel/group from step 2.
 *
 * In order to create an instance of TelegramBotHandler use
 *  1. The Telegram bot API key from step 1
 *  2. The channel name with the `@` prefix if you created a public channel (e.g. `@my_public_channel`),
 *     or the channel ID with the `-100` prefix if you created a private channel (e.g. `-1001234567890`),
 *     or the group ID from step 2 (e.g. `-1234567890`).
 *
 * @link https://core.telegram.org/bots/api
 *
 * @author Mazur Alexandr <alexandrmazur96@gmail.com>
 */
class TelegramBotHandler extends AbstractProcessingHandler
{
    private const BOT_API = 'https://api.telegram.org/bot';

    /**
     * The available values of parseMode according to the Telegram api documentation
     */
    private const AVAILABLE_PARSE_MODES = [
        'HTML',
        'MarkdownV2',
        'Markdown', // legacy mode without underline and strikethrough, use MarkdownV2 instead
    ];

    /**
     * The maximum number of characters allowed in a message according to the Telegram api documentation
     */
    private const MAX_MESSAGE_LENGTH = 4096;

    /**
     * Telegram bot access token provided by BotFather.
     * Create telegram bot with https://telegram.me/BotFather and use access token from it.
     */
    private string $apiKey;

    /**
     * Telegram channel name.
     * Since to start with '@' symbol as prefix.
     */
    private string $channel;

    /**
     * The kind of formatting that is used for the message.
     * See available options at https://core.telegram.org/bots/api#formatting-options
     * or in AVAILABLE_PARSE_MODES
     */
    private string|null $parseMode;

    /**
     * Disables link previews for links in the message.
     */
    private bool|null $disableWebPagePreview;

    /**
     * Sends the message silently. Users will receive a notification with no sound.
     */
    private bool|null $disableNotification;

    /**
     * True - split a message longer than MAX_MESSAGE_LENGTH into parts and send in multiple messages.
     * False - truncates a message that is too long.
     */
    private bool $splitLongMessages;

    /**
     * Adds 1-second delay between sending a split message (according to Telegram API to avoid 429 Too Many Requests).
     */
    private bool $delayBetweenMessages;

    /**
     * Telegram message thread id, unique identifier for the target message thread (topic) of the forum; for forum supergroups only
     * See how to get the `message_thread_id` https://stackoverflow.com/a/75178418
     */
    private int|null $topic;

    /**
     * @param  string                    $apiKey               Telegram bot access token provided by BotFather
     * @param  string                    $channel              Telegram channel name
     * @param  bool                      $splitLongMessages    Split a message longer than MAX_MESSAGE_LENGTH into parts and send in multiple messages
     * @param  bool                      $delayBetweenMessages Adds delay between sending a split message according to Telegram API
     * @param  int                       $topic                Telegram message thread id, unique identifier for the target message thread (topic) of the forum
     * @throws MissingExtensionException If the curl extension is missing
     */
    public function __construct(
        string $apiKey,
        string $channel,
        $level = Level::Debug,
        bool   $bubble = true,
        ?string $parseMode = null,
        ?bool   $disableWebPagePreview = null,
        ?bool   $disableNotification = null,
        bool   $splitLongMessages = false,
        bool   $delayBetweenMessages = false,
        ?int   $topic = null
    ) {
        if (!\extension_loaded('curl')) {
            throw new MissingExtensionException('The curl extension is needed to use the TelegramBotHandler');
        }

        parent::__construct($level, $bubble);

        $this->apiKey = $apiKey;
        $this->channel = $channel;
        $this->setParseMode($parseMode);
        $this->disableWebPagePreview($disableWebPagePreview);
        $this->disableNotification($disableNotification);
        $this->splitLongMessages($splitLongMessages);
        $this->delayBetweenMessages($delayBetweenMessages);
        $this->setTopic($topic);
    }

    /**
     * @return $this
     */
    public function setParseMode(string|null $parseMode = null): self
    {
        if ($parseMode !== null && !\in_array($parseMode, self::AVAILABLE_PARSE_MODES, true)) {
            throw new \InvalidArgumentException('Unknown parseMode, use one of these: ' . implode(', ', self::AVAILABLE_PARSE_MODES) . '.');
        }

        $this->parseMode = $parseMode;

        return $this;
    }

    /**
     * @return $this
     */
    public function disableWebPagePreview(bool|null $disableWebPagePreview = null): self
    {
        $this->disableWebPagePreview = $disableWebPagePreview;

        return $this;
    }

    /**
     * @return $this
     */
    public function disableNotification(bool|null $disableNotification = null): self
    {
        $this->disableNotification = $disableNotification;

        return $this;
    }

    /**
     * True - split a message longer than MAX_MESSAGE_LENGTH into parts and send in multiple messages.
     * False - truncates a message that is too long.
     *
     * @return $this
     */
    public function splitLongMessages(bool $splitLongMessages = false): self
    {
        $this->splitLongMessages = $splitLongMessages;

        return $this;
    }

    /**
     * Adds 1-second delay between sending a split message (according to Telegram API to avoid 429 Too Many Requests).
     *
     * @return $this
     */
    public function delayBetweenMessages(bool $delayBetweenMessages = false): self
    {
        $this->delayBetweenMessages = $delayBetweenMessages;

        return $this;
    }

    /**
     * @return $this
     */
    public function setTopic(?int $topic = null): self
    {
        $this->topic = $topic;

        return $this;
    }

    /**
     * @inheritDoc
     */
    public function handleBatch(array $records): void
    {
        $messages = [];

        foreach ($records as $record) {
            if (!$this->isHandling($record)) {
                continue;
            }

            if (\count($this->processors) > 0) {
                $record = $this->processRecord($record);
            }

            $messages[] = $record;
        }

        if (\count($messages) > 0) {
            $this->send((string) $this->getFormatter()->formatBatch($messages));
        }
    }

    /**
     * @inheritDoc
     */
    protected function write(LogRecord $record): void
    {
        $this->send($record->formatted);
    }

    /**
     * Send request to @link https://api.telegram.org/bot on SendMessage action.
     */
    protected function send(string $message): void
    {
        $messages = $this->handleMessageLength($message);

        foreach ($messages as $key => $msg) {
            if ($this->delayBetweenMessages && $key > 0) {
                sleep(1);
            }

            $this->sendCurl($msg);
        }
    }

    /**
     * Returns the Telegram Bot API base URL.
     * Override in a subclass to point to a self-hosted Bot API server.
     */
    protected function getBotApiUrl(): string
    {
        return self::BOT_API;
    }

    /**
     * Returns extra HTTP headers to send with every Telegram API request.
     * Override in a subclass to inject custom headers (e.g. auth tokens, tracing).
     *
     * @return string[]
     */
    protected function getCurlHeaders(): array
    {
        return [];
    }

    protected function sendCurl(string $message): void
    {
        if ('' === trim($message)) {
            return;
        }

        $ch = curl_init();
        $url = $this->getBotApiUrl() . $this->apiKey . '/SendMessage';
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
        $headers = $this->getCurlHeaders();
        if ($headers !== []) {
            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        }
        $params = [
            'text' => $message,
            'chat_id' => $this->channel,
            'parse_mode' => $this->parseMode,
            'disable_web_page_preview' => $this->disableWebPagePreview,
            'disable_notification' => $this->disableNotification,
        ];
        if ($this->topic !== null) {
            $params['message_thread_id'] = $this->topic;
        }
        curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params));

        $this->validateApiResponse(Curl\Util::execute($ch));
    }

    /**
     * @param  string|bool      $rawResult Raw response body from the Telegram API call
     * @throws RuntimeException When the response is missing, non-JSON, or signals failure
     */
    protected function validateApiResponse(string|bool $rawResult): void
    {
        if (!\is_string($rawResult)) {
            throw new RuntimeException('Telegram API error. Description: No response');
        }

        $result = json_decode($rawResult, true);

        if (!\is_array($result)) {
            throw new RuntimeException('Telegram API error. Description: Unexpected non-JSON response');
        }
        if (($result['ok'] ?? null) !== true) {
            throw new RuntimeException('Telegram API error. Description: ' . ($result['description'] ?? 'Unknown error'));
        }
    }

    /**
     * Handle a message that is too long: truncates or splits into several
     * @return string[]
     */
    private function handleMessageLength(string $message): array
    {
        $truncatedMarker = ' (â€¦truncated)';
        if (!$this->splitLongMessages && \strlen($message) > self::MAX_MESSAGE_LENGTH) {
            $maxLength = self::MAX_MESSAGE_LENGTH - \strlen($truncatedMarker);
            $truncated = Utils::substr($message, 0, $maxLength);

            if ($this->parseMode === 'HTML') {
                $truncated = $this->trimPartialHtmlTag($truncated);
                $closing = $this->closingHtmlTagsFor($this->updateOpenHtmlTags($truncated, []));

                while ($truncated !== '' && \strlen($truncated . $closing) > $maxLength) {
                    $truncated = $this->trimPartialHtmlTag(Utils::substr($truncated, 0, -1));
                    $closing = $this->closingHtmlTagsFor($this->updateOpenHtmlTags($truncated, []));
                }

                $truncated .= $closing;
            }

            return [$truncated . $truncatedMarker];
        }

        if ($this->parseMode === 'HTML' && \strlen($message) > self::MAX_MESSAGE_LENGTH) {
            return $this->splitHtmlMessage($message, self::MAX_MESSAGE_LENGTH);
        }

        return str_split($message, self::MAX_MESSAGE_LENGTH);
    }

    /**
     * Splits an HTML $message into chunks of at most $maxLength characters, closing any
     * tag left open at the end of a chunk and reopening it at the start of the next one,
     * so that neither a tag nor a start/end tag pair is ever cut in half.
     *
     * @return string[]
     */
    private function splitHtmlMessage(string $message, int $maxLength): array
    {
        $chunks = [];
        $openTags = [];
        $remaining = $message;

        while ($remaining !== '') {
            $prefix = $this->openingHtmlTagsFor($openTags);
            $slice = Utils::substr($remaining, 0, max(1, $maxLength - \strlen($prefix)));
            $isLast = \strlen($slice) >= \strlen($remaining);

            if (!$isLast) {
                $slice = $this->trimPartialHtmlTag($slice);
            }

            $sliceOpenTags = $this->updateOpenHtmlTags($slice, $openTags);
            $suffix = $isLast ? '' : $this->closingHtmlTagsFor($sliceOpenTags);

            while (!$isLast && $slice !== '' && \strlen($prefix . $slice . $suffix) > $maxLength) {
                $slice = $this->trimPartialHtmlTag(Utils::substr($slice, 0, -1));
                $sliceOpenTags = $this->updateOpenHtmlTags($slice, $openTags);
                $suffix = $this->closingHtmlTagsFor($sliceOpenTags);
            }

            $chunks[] = $prefix . $slice . $suffix;
            $remaining = Utils::substr($remaining, \strlen($slice));
            $openTags = $isLast ? [] : $sliceOpenTags;
        }

        return $chunks;
    }

    /**
     * Backs off before a `<` that has no matching `>` yet, so $html never ends mid-tag.
     */
    private function trimPartialHtmlTag(string $html): string
    {
        $lastLt = strrpos($html, '<');
        $lastGt = strrpos($html, '>');

        if ($lastLt !== false && ($lastGt === false || $lastLt > $lastGt)) {
            return Utils::substr($html, 0, $lastLt);
        }

        return $html;
    }

    /**
     * Scans $html for tags, applying them onto $openTags, so the result is the set of
     * tags still open after $html (tags opened before $html that $html did not close,
     * plus any $html opened itself and did not close again).
     *
     * @param  array<array{name: string, raw: string}> $openTags
     * @return array<array{name: string, raw: string}>
     */
    private function updateOpenHtmlTags(string $html, array $openTags): array
    {
        if (0 === preg_match_all('/<(\/?)([a-zA-Z][a-zA-Z0-9]*)\b[^>]*>/', $html, $matches, PREG_SET_ORDER)) {
            return $openTags;
        }

        foreach ($matches as $match) {
            $name = strtolower($match[2]);
            if ($match[1] === '/') {
                for ($i = \count($openTags) - 1; $i >= 0; $i--) {
                    if ($openTags[$i]['name'] === $name) {
                        array_splice($openTags, $i, 1);
                        break;
                    }
                }
            } else {
                $openTags[] = ['name' => $name, 'raw' => $match[0]];
            }
        }

        return $openTags;
    }

    /**
     * @param  array<array{name: string, raw: string}> $openTags
     */
    private function closingHtmlTagsFor(array $openTags): string
    {
        $closing = '';
        foreach (array_reverse($openTags) as $tag) {
            $closing .= '</' . $tag['name'] . '>';
        }

        return $closing;
    }

    /**
     * @param  array<array{name: string, raw: string}> $openTags
     */
    private function openingHtmlTagsFor(array $openTags): string
    {
        $opening = '';
        foreach ($openTags as $tag) {
            $opening .= $tag['raw'];
        }

        return $opening;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\Level;
use Monolog\LogRecord;

/**
 * Inspired on LogEntriesHandler.
 *
 * @author Robert Kaufmann III <rok3@rok3.me>
 * @author Gabriel Machado <gabriel.ms1@hotmail.com>
 */
class InsightOpsHandler extends SocketHandler
{
    protected string $logToken;

    /**
     * @param string $token  Log token supplied by InsightOps
     * @param string $region Region where InsightOps account is hosted. Could be 'us' or 'eu'.
     * @param bool   $useSSL Whether or not SSL encryption should be used
     *
     * @throws MissingExtensionException If SSL encryption is set to true and OpenSSL is missing
     */
    public function __construct(
        string $token,
        string $region = 'us',
        bool $useSSL = true,
        $level = Level::Debug,
        bool $bubble = true,
        bool $persistent = false,
        float $timeout = 0.0,
        float $writingTimeout = 10.0,
        ?float $connectionTimeout = null,
        ?int $chunkSize = null
    ) {
        if ($useSSL && !\extension_loaded('openssl')) {
            throw new MissingExtensionException('The OpenSSL PHP plugin is required to use SSL encrypted connection for InsightOpsHandler');
        }

        $endpoint = $useSSL
            ? 'ssl://' . $region . '.data.logs.insight.rapid7.com:443'
            : $region . '.data.logs.insight.rapid7.com:80';

        parent::__construct(
            $endpoint,
            $level,
            $bubble,
            $persistent,
            $timeout,
            $writingTimeout,
            $connectionTimeout,
            $chunkSize
        );
        $this->logToken = $token;
    }

    /**
     * @inheritDoc
     */
    protected function generateDataStream(LogRecord $record): string
    {
        return $this->logToken . ' ' . $record->formatted;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\LogRecord;

/**
 * No-op
 *
 * This handler handles anything, but does nothing, and does not stop bubbling to the rest of the stack.
 * This can be used for testing, or to disable a handler when overriding a configuration without
 * influencing the rest of the stack.
 *
 * @author Roel Harbers <roelharbers@gmail.com>
 */
class NoopHandler extends Handler
{
    /**
     * @inheritDoc
     */
    public function isHandling(LogRecord $record): bool
    {
        return true;
    }

    /**
     * @inheritDoc
     */
    public function handle(LogRecord $record): bool
    {
        return false;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\Formatter\FormatterInterface;
use Monolog\Formatter\NormalizerFormatter;
use Monolog\Level;
use Monolog\LogRecord;

/**
 * Logs records using FrankenPHP's frankenphp_log() function.
 *
 * This handler requires a NormalizerFormatter to function and expects an array in $record->formatted
 *
 * @see https://frankenphp.dev/docs/logging/#frankenphp_log
 *
 * @author KÃ©vin Dunglas <kevin@dunglas.dev>
 */
class FrankenPhpHandler extends AbstractProcessingHandler
{
    /**
     * @throws MissingExtensionException If frankenphp_log() is not available
     */
    public function __construct(int|string|Level $level = Level::Debug, bool $bubble = true)
    {
        if (!\function_exists('frankenphp_log')) {
            throw new MissingExtensionException(
                'You must run this handler under FrankenPHP, the frankenphp_log() function is not available'
            );
        }

        parent::__construct($level, $bubble);
    }

    /**
     * Translates Monolog log levels to FrankenPHP's slog-based levels.
     *
     * slog's docs state its gap of 4 between named levels matches OpenTelemetry's SeverityNumber
     * mapping, converted by subtracting 9. Applying that same subtraction to OpenTelemetry's own
     * Syslog severity mapping for the 4 extra RFC 5424 levels slog has no constant for keeps this
     * scale traceable to that spec instead of an arbitrary choice.
     *
     * FrankenPHP feeds these through zapslog, which buckets everything >= Error into "error", so
     * above Error only the level_name context key tells the Monolog levels apart.
     *
     * @see https://pkg.go.dev/log/slog#hdr-Levels
     * @see https://opentelemetry.io/docs/specs/otel/logs/data-model-appendix/
     */
    protected function toFrankenPhpLevel(Level $level): int
    {
        return match ($level) {
            Level::Debug => \FRANKENPHP_LOG_LEVEL_DEBUG,
            Level::Info => \FRANKENPHP_LOG_LEVEL_INFO,
            Level::Notice => 1,
            Level::Warning => \FRANKENPHP_LOG_LEVEL_WARN,
            Level::Error => \FRANKENPHP_LOG_LEVEL_ERROR,
            Level::Critical => 9,
            Level::Alert => 10,
            Level::Emergency => 12,
        };
    }

    /**
     * @inheritDoc
     */
    protected function write(LogRecord $record): void
    {
        $this->writeFrankenPhpLog($record->message, $this->toFrankenPhpLevel($record->level), $record->formatted);
    }

    /**
     * @param array<mixed> $context Displayed as structured attributes alongside the message
     */
    protected function writeFrankenPhpLog(string $message, int $level, array $context): void
    {
        // frankenphp_log() merges $context into the same JSON object it already fills with "msg",
        // "level" and "ts", so drop Monolog's duplicates of those three. level_name still carries
        // the Monolog level name, which slog's own severity cannot express.
        unset($context['message'], $context['level'], $context['datetime']);

        \frankenphp_log($message, $level, $context);
    }

    /**
     * @inheritDoc
     */
    public function getDefaultFormatter(): FormatterInterface
    {
        return new NormalizerFormatter();
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\Level;
use Monolog\Formatter\FormatterInterface;
use Monolog\Formatter\LogglyFormatter;
use CurlHandle;
use Monolog\LogRecord;

/**
 * Sends errors to Loggly.
 *
 * @author Przemek Sobstel <przemek@sobstel.org>
 * @author Adam Pancutt <adam@pancutt.com>
 * @author Gregory Barchard <gregory@barchard.net>
 */
class LogglyHandler extends AbstractProcessingHandler
{
    protected const HOST = 'logs-01.loggly.com';
    protected const ENDPOINT_SINGLE = 'inputs';
    protected const ENDPOINT_BATCH = 'bulk';

    /**
     * Caches the curl handlers for every given endpoint.
     *
     * @var CurlHandle[]
     */
    protected array $curlHandlers = [];

    protected string $token;

    /** @var string[] */
    protected array $tag = [];

    /**
     * @param string $token API token supplied by Loggly
     *
     * @throws MissingExtensionException If the curl extension is missing
     */
    public function __construct(string $token, int|string|Level $level = Level::Debug, bool $bubble = true)
    {
        if (!\extension_loaded('curl')) {
            throw new MissingExtensionException('The curl extension is needed to use the LogglyHandler');
        }

        $this->token = $token;

        parent::__construct($level, $bubble);
    }

    /**
     * Loads and returns the shared curl handler for the given endpoint.
     */
    protected function getCurlHandler(string $endpoint): CurlHandle
    {
        if (!\array_key_exists($endpoint, $this->curlHandlers)) {
            $this->curlHandlers[$endpoint] = $this->loadCurlHandle($endpoint);
        }

        return $this->curlHandlers[$endpoint];
    }

    /**
     * Starts a fresh curl session for the given endpoint and returns its handler.
     */
    private function loadCurlHandle(string $endpoint): CurlHandle
    {
        $url = sprintf("https://%s/%s/%s/", static::HOST, $endpoint, $this->token);

        $ch = curl_init();

        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        return $ch;
    }

    /**
     * @param  string[]|string $tag
     * @return $this
     */
    public function setTag(string|array $tag): self
    {
        if ('' === $tag || [] === $tag) {
            $this->tag = [];
        } else {
            $this->tag = \is_array($tag) ? $tag : [$tag];
        }

        return $this;
    }

    /**
     * @param  string[]|string $tag
     * @return $this
     */
    public function addTag(string|array $tag): self
    {
        if ('' !== $tag) {
            $tag = \is_array($tag) ? $tag : [$tag];
            $this->tag = array_unique(array_merge($this->tag, $tag));
        }

        return $this;
    }

    protected function write(LogRecord $record): void
    {
        $this->send($record->formatted, static::ENDPOINT_SINGLE);
    }

    public function handleBatch(array $records): void
    {
        $level = $this->level;

        $records = array_filter($records, function ($record) use ($level) {
            return ($record->level->value >= $level->value);
        });

        if (\count($records) > 0) {
            $this->send($this->getFormatter()->formatBatch($records), static::ENDPOINT_BATCH);
        }
    }

    protected function send(string $data, string $endpoint): void
    {
        $ch = $this->getCurlHandler($endpoint);

        $headers = ['Content-Type: application/json'];

        if (\count($this->tag) > 0) {
            $headers[] = 'X-LOGGLY-TAG: '.implode(',', $this->tag);
        }

        curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

        Curl\Util::execute($ch, 5);
    }

    protected function getDefaultFormatter(): FormatterInterface
    {
        return new LogglyFormatter();
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\Level;
use Monolog\Formatter\LineFormatter;

/**
 * NativeMailerHandler uses the mail() function to send the emails
 *
 * @author Christophe Coevoet <stof@notk.org>
 * @author Mark Garrett <mark@moderndeveloperllc.com>
 */
class NativeMailerHandler extends MailHandler
{
    /**
     * The email addresses to which the message will be sent
     * @var string[]
     */
    protected array $to;

    /**
     * The subject of the email
     */
    protected string $subject;

    /**
     * Optional headers for the message
     * @var string[]
     */
    protected array $headers = [];

    /**
     * Optional parameters for the message
     * @var string[]
     */
    protected array $parameters = [];

    /**
     * The wordwrap length for the message
     */
    protected int $maxColumnWidth;

    /**
     * The Content-type for the message
     */
    protected string|null $contentType = null;

    /**
     * The encoding for the message
     */
    protected string $encoding = 'utf-8';

    /**
     * @param string|string[] $to             The receiver of the mail
     * @param string          $subject        The subject of the mail
     * @param string          $from           The sender of the mail
     * @param int             $maxColumnWidth The maximum column width that the message lines will have
     */
    public function __construct(string|array $to, string $subject, string $from, int|string|Level $level = Level::Error, bool $bubble = true, int $maxColumnWidth = 70)
    {
        parent::__construct($level, $bubble);
        $this->to = (array) $to;
        $this->subject = $subject;
        $this->addHeader(sprintf('From: %s', $from));
        $this->maxColumnWidth = $maxColumnWidth;
    }

    /**
     * Add headers to the message
     *
     * @param  string|string[] $headers Custom added headers
     * @return $this
     */
    public function addHeader($headers): self
    {
        foreach ((array) $headers as $header) {
            if (strpos($header, "\n") !== false || strpos($header, "\r") !== false) {
                throw new \InvalidArgumentException('Headers can not contain newline characters for security reasons');
            }
            $this->headers[] = $header;
        }

        return $this;
    }

    /**
     * Add parameters to the message
     *
     * @param  string|string[] $parameters Custom added parameters
     * @return $this
     */
    public function addParameter($parameters): self
    {
        $this->parameters = array_merge($this->parameters, (array) $parameters);

        return $this;
    }

    /**
     * @inheritDoc
     */
    protected function send(string $content, array $records): void
    {
        $contentType = $this->getContentType() ?? ($this->isHtmlBody($content) ? 'text/html' : 'text/plain');

        if ($contentType !== 'text/html') {
            $content = wordwrap($content, $this->maxColumnWidth);
        }

        $headers = ltrim(implode("\r\n", $this->headers) . "\r\n", "\r\n");
        $headers .= 'Content-type: ' . $contentType . '; charset=' . $this->getEncoding() . "\r\n";
        if ($contentType === 'text/html' && false === strpos($headers, 'MIME-Version:')) {
            $headers .= 'MIME-Version: 1.0' . "\r\n";
        }

        $subjectFormatter = new LineFormatter($this->subject);
        $subject = $subjectFormatter->format($this->getHighestRecord($records));

        $parameters = implode(' ', $this->parameters);
        foreach ($this->to as $to) {
            $this->mail($to, $subject, $content, $headers, $parameters);
        }
    }

    public function getContentType(): ?string
    {
        return $this->contentType;
    }

    public function getEncoding(): string
    {
        return $this->encoding;
    }

    /**
     * @param  string $contentType The content type of the email - Defaults to text/plain. Use text/html for HTML messages.
     * @return $this
     */
    public function setContentType(string $contentType): self
    {
        if (strpos($contentType, "\n") !== false || strpos($contentType, "\r") !== false) {
            throw new \InvalidArgumentException('The content type can not contain newline characters to prevent email header injection');
        }

        $this->contentType = $contentType;

        return $this;
    }

    /**
     * @return $this
     */
    public function setEncoding(string $encoding): self
    {
        if (strpos($encoding, "\n") !== false || strpos($encoding, "\r") !== false) {
            throw new \InvalidArgumentException('The encoding can not contain newline characters to prevent email header injection');
        }

        $this->encoding = $encoding;

        return $this;
    }


    protected function mail(string $to, string $subject, string $content, string $headers, string $parameters): void
    {
        mail($to, $subject, $content, $headers, $parameters);
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Aws\Sqs\SqsClient;
use Monolog\Level;
use Monolog\Utils;
use Monolog\LogRecord;

/**
 * Writes to any sqs queue.
 *
 * @author Martijn van Calker <git@amvc.nl>
 */
class SqsHandler extends AbstractProcessingHandler
{
    /** 256 KB in bytes - maximum message size in SQS */
    protected const MAX_MESSAGE_SIZE = 262144;
    /** 100 KB in bytes - head message size for new error log */
    protected const HEAD_MESSAGE_SIZE = 102400;

    private SqsClient $client;
    private string $queueUrl;

    public function __construct(SqsClient $sqsClient, string $queueUrl, int|string|Level $level = Level::Debug, bool $bubble = true)
    {
        parent::__construct($level, $bubble);

        $this->client = $sqsClient;
        $this->queueUrl = $queueUrl;
    }

    /**
     * @inheritDoc
     */
    protected function write(LogRecord $record): void
    {
        if (!isset($record->formatted) || 'string' !== \gettype($record->formatted)) {
            throw new \InvalidArgumentException('SqsHandler accepts only formatted records as a string' . Utils::getRecordMessageForException($record));
        }

        $messageBody = $record->formatted;
        if (\strlen($messageBody) >= static::MAX_MESSAGE_SIZE) {
            $messageBody = Utils::substr($messageBody, 0, static::HEAD_MESSAGE_SIZE);
        }

        $this->client->sendMessage([
            'QueueUrl' => $this->queueUrl,
            'MessageBody' => $messageBody,
        ]);
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Closure;
use Monolog\Formatter\FormatterInterface;
use Monolog\LogRecord;

/**
 * Sampling handler
 *
 * A sampled event stream can be useful for logging high frequency events in
 * a production environment where you only need an idea of what is happening
 * and are not concerned with capturing every occurrence. Since the decision to
 * handle or not handle a particular event is determined randomly, the
 * resulting sampled log is not guaranteed to contain 1/N of the events that
 * occurred in the application, but based on the Law of large numbers, it will
 * tend to be close to this ratio with a large number of attempts.
 *
 * @author Bryan Davis <bd808@wikimedia.org>
 * @author Kunal Mehta <legoktm@gmail.com>
 */
class SamplingHandler extends AbstractHandler implements ProcessableHandlerInterface, FormattableHandlerInterface
{
    use ProcessableHandlerTrait;

    /**
     * Handler or factory Closure($record, $this)
     *
     * @phpstan-var (Closure(LogRecord|null, HandlerInterface): HandlerInterface)|HandlerInterface
     */
    protected Closure|HandlerInterface $handler;

    protected int $factor;

    /**
     * @phpstan-param (Closure(LogRecord|null, HandlerInterface): HandlerInterface)|HandlerInterface $handler
     *
     * @param Closure|HandlerInterface $handler Handler or factory Closure($record|null, $samplingHandler).
     * @param int                      $factor  Sample factor (e.g. 10 means every ~10th record is sampled)
     */
    public function __construct(Closure|HandlerInterface $handler, int $factor)
    {
        parent::__construct();
        $this->handler = $handler;
        $this->factor = $factor;
    }

    public function isHandling(LogRecord $record): bool
    {
        return $this->getHandler($record)->isHandling($record);
    }

    public function handle(LogRecord $record): bool
    {
        if ($this->isHandling($record) && mt_rand(1, $this->factor) === 1) {
            if (\count($this->processors) > 0) {
                $record = $this->processRecord($record);
            }

            $this->getHandler($record)->handle($record);
        }

        return false === $this->bubble;
    }

    /**
     * Return the nested handler
     *
     * If the handler was provided as a factory, this will trigger the handler's instantiation.
     */
    public function getHandler(LogRecord|null $record = null): HandlerInterface
    {
        if (!$this->handler instanceof HandlerInterface) {
            $handler = ($this->handler)($record, $this);
            if (!$handler instanceof HandlerInterface) {
                throw new \RuntimeException("The factory Closure should return a HandlerInterface");
            }
            $this->handler = $handler;
        }

        return $this->handler;
    }

    /**
     * @inheritDoc
     */
    public function setFormatter(FormatterInterface $formatter): HandlerInterface
    {
        $handler = $this->getHandler();
        if ($handler instanceof FormattableHandlerInterface) {
            $handler->setFormatter($formatter);

            return $this;
        }

        throw new \UnexpectedValueException('The nested handler of type '.\get_class($handler).' does not support formatters.');
    }

    /**
     * @inheritDoc
     */
    public function getFormatter(): FormatterInterface
    {
        $handler = $this->getHandler();
        if ($handler instanceof FormattableHandlerInterface) {
            return $handler->getFormatter();
        }

        throw new \UnexpectedValueException('The nested handler of type '.\get_class($handler).' does not support formatters.');
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\Formatter\FormatterInterface;
use Monolog\Formatter\JsonFormatter;
use Monolog\Level;
use Monolog\LogRecord;

/**
 * CouchDB handler
 *
 * @author Markus Bachmann <markus.bachmann@bachi.biz>
 * @phpstan-type Options array{
 *     host: string,
 *     port: int,
 *     dbname: string,
 *     username: string|null,
 *     password: string|null
 * }
 * @phpstan-type InputOptions array{
 *     host?: string,
 *     port?: int,
 *     dbname?: string,
 *     username?: string|null,
 *     password?: string|null
 * }
 */
class CouchDBHandler extends AbstractProcessingHandler
{
    /**
     * @var mixed[]
     * @phpstan-var Options
     */
    private array $options;

    /**
     * @param mixed[] $options
     *
     * @phpstan-param InputOptions $options
     */
    public function __construct(array $options = [], int|string|Level $level = Level::Debug, bool $bubble = true)
    {
        $this->options = array_merge([
            'host'     => 'localhost',
            'port'     => 5984,
            'dbname'   => 'logger',
            'username' => null,
            'password' => null,
        ], $options);

        parent::__construct($level, $bubble);
    }

    /**
     * @inheritDoc
     */
    protected function write(LogRecord $record): void
    {
        $basicAuth = null;
        if (null !== $this->options['username'] && null !== $this->options['password']) {
            $basicAuth = sprintf('%s:%s@', $this->options['username'], $this->options['password']);
        }

        $url = 'http://'.$basicAuth.$this->options['host'].':'.$this->options['port'].'/'.$this->options['dbname'];
        $context = stream_context_create([
            'http' => [
                'method'        => 'POST',
                'content'       => $record->formatted,
                'ignore_errors' => true,
                'max_redirects' => 0,
                'header'        => 'Content-type: application/json',
            ],
        ]);

        if (false === @file_get_contents($url, false, $context)) {
            throw new \RuntimeException(sprintf('Could not connect to %s', $url));
        }
    }

    /**
     * @inheritDoc
     */
    protected function getDefaultFormatter(): FormatterInterface
    {
        return new JsonFormatter(JsonFormatter::BATCH_MODE_JSON, false);
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\Formatter\FormatterInterface;

/**
 * Interface to describe loggers that have a formatter
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 */
interface FormattableHandlerInterface
{
    /**
     * Sets the formatter.
     *
     * @return HandlerInterface self
     */
    public function setFormatter(FormatterInterface $formatter): HandlerInterface;

    /**
     * Gets the formatter.
     */
    public function getFormatter(): FormatterInterface;
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use MongoDB\Client;
use MongoDB\Collection;
use MongoDB\Driver\BulkWrite;
use MongoDB\Driver\Manager;
use Monolog\Level;
use Monolog\Formatter\FormatterInterface;
use Monolog\Formatter\MongoDBFormatter;
use Monolog\LogRecord;

/**
 * Logs to a MongoDB database.
 *
 * Usage example:
 *
 *   $log = new \Monolog\Logger('application');
 *   $client = new \MongoDB\Client('mongodb://localhost:27017');
 *   $mongodb = new \Monolog\Handler\MongoDBHandler($client, 'logs', 'prod');
 *   $log->pushHandler($mongodb);
 *
 * The above examples uses the MongoDB PHP library's client class; however, the
 * MongoDB\Driver\Manager class from ext-mongodb is also supported.
 */
class MongoDBHandler extends AbstractProcessingHandler
{
    private Collection $collection;

    private Client|Manager $manager;

    private string|null $namespace = null;

    /**
     * Constructor.
     *
     * @param Client|Manager $mongodb    MongoDB library or driver client
     * @param string         $database   Database name
     * @param string         $collection Collection name
     */
    public function __construct(Client|Manager $mongodb, string $database, string $collection, int|string|Level $level = Level::Debug, bool $bubble = true)
    {
        if ($mongodb instanceof Client) {
            $this->collection = method_exists($mongodb, 'getCollection') ? $mongodb->getCollection($database, $collection) : $mongodb->selectCollection($database, $collection);
        } else {
            $this->manager = $mongodb;
            $this->namespace = $database . '.' . $collection;
        }

        parent::__construct($level, $bubble);
    }

    protected function write(LogRecord $record): void
    {
        if (isset($this->collection)) {
            $this->collection->insertOne($record->formatted);
        }

        if (isset($this->manager, $this->namespace)) {
            $bulk = new BulkWrite;
            $bulk->insert($record->formatted);
            $this->manager->executeBulkWrite($this->namespace, $bulk);
        }
    }

    /**
     * @inheritDoc
     */
    protected function getDefaultFormatter(): FormatterInterface
    {
        return new MongoDBFormatter;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\Formatter\FormatterInterface;
use Monolog\Formatter\NormalizerFormatter;
use Monolog\Level;
use Monolog\LogRecord;

/**
 * Handler sending logs to Zend Monitor
 *
 * @author  Christian Bergau <cbergau86@gmail.com>
 * @author  Jason Davis <happydude@jasondavis.net>
 */
class ZendMonitorHandler extends AbstractProcessingHandler
{
    /**
     * @throws MissingExtensionException
     */
    public function __construct(int|string|Level $level = Level::Debug, bool $bubble = true)
    {
        if (!\function_exists('zend_monitor_custom_event')) {
            throw new MissingExtensionException(
                'You must have Zend Server installed with Zend Monitor enabled in order to use this handler'
            );
        }

        parent::__construct($level, $bubble);
    }

    /**
     * Translates Monolog log levels to ZendMonitor levels.
     */
    protected function toZendMonitorLevel(Level $level): int
    {
        return match ($level) {
            Level::Debug     => \ZEND_MONITOR_EVENT_SEVERITY_INFO,
            Level::Info      => \ZEND_MONITOR_EVENT_SEVERITY_INFO,
            Level::Notice    => \ZEND_MONITOR_EVENT_SEVERITY_INFO,
            Level::Warning   => \ZEND_MONITOR_EVENT_SEVERITY_WARNING,
            Level::Error     => \ZEND_MONITOR_EVENT_SEVERITY_ERROR,
            Level::Critical  => \ZEND_MONITOR_EVENT_SEVERITY_ERROR,
            Level::Alert     => \ZEND_MONITOR_EVENT_SEVERITY_ERROR,
            Level::Emergency => \ZEND_MONITOR_EVENT_SEVERITY_ERROR,
        };
    }

    /**
     * @inheritDoc
     */
    protected function write(LogRecord $record): void
    {
        $this->writeZendMonitorCustomEvent(
            $record->level->getName(),
            $record->message,
            $record->formatted,
            $this->toZendMonitorLevel($record->level)
        );
    }

    /**
     * Write to Zend Monitor Events
     * @param string       $type      Text displayed in "Class Name (custom)" field
     * @param string       $message   Text displayed in "Error String"
     * @param array<mixed> $formatted Displayed in Custom Variables tab
     * @param int          $severity  Set the event severity level (-1,0,1)
     */
    protected function writeZendMonitorCustomEvent(string $type, string $message, array $formatted, int $severity): void
    {
        zend_monitor_custom_event($type, $message, $formatted, $severity);
    }

    /**
     * @inheritDoc
     */
    public function getDefaultFormatter(): FormatterInterface
    {
        return new NormalizerFormatter();
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\Formatter\LineFormatter;
use Monolog\Formatter\FormatterInterface;
use Monolog\Level;
use Monolog\LogRecord;

/**
 * Stores to PHP error_log() handler.
 *
 * @author Elan RuusamÃ¤e <glen@delfi.ee>
 */
class ErrorLogHandler extends AbstractProcessingHandler
{
    public const OPERATING_SYSTEM = 0;
    public const SAPI = 4;

    /** @var 0|4 */
    protected int $messageType;
    protected bool $expandNewlines;

    /**
     * @param 0|4 $messageType    Says where the error should go.
     * @param bool $expandNewlines If set to true, newlines in the message will be expanded to be take multiple log entries
     *
     * @throws \InvalidArgumentException If an unsupported message type is set
     */
    public function __construct(int $messageType = self::OPERATING_SYSTEM, int|string|Level $level = Level::Debug, bool $bubble = true, bool $expandNewlines = false)
    {
        parent::__construct($level, $bubble);

        if (false === \in_array($messageType, self::getAvailableTypes(), true)) {
            $message = sprintf('The given message type "%s" is not supported', print_r($messageType, true));

            throw new \InvalidArgumentException($message);
        }

        $this->messageType = $messageType;
        $this->expandNewlines = $expandNewlines;
    }

    /**
     * @return int[] With all available types
     */
    public static function getAvailableTypes(): array
    {
        return [
            self::OPERATING_SYSTEM,
            self::SAPI,
        ];
    }

    /**
     * @inheritDoc
     */
    protected function getDefaultFormatter(): FormatterInterface
    {
        return new LineFormatter('[%datetime%] %channel%.%level_name%: %message% %context% %extra%');
    }

    /**
     * @inheritDoc
     */
    protected function write(LogRecord $record): void
    {
        if (!$this->expandNewlines) {
            error_log((string) $record->formatted, $this->messageType);

            return;
        }

        $lines = preg_split('{[\r\n]+}', (string) $record->formatted);
        if ($lines === false) {
            $pcreErrorCode = preg_last_error();

            throw new \RuntimeException('Failed to preg_split formatted string: ' . $pcreErrorCode . ' / '. preg_last_error_msg());
        }
        foreach ($lines as $line) {
            error_log($line, $this->messageType);
        }
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\Level;
use Monolog\Logger;
use Psr\Log\LogLevel;
use Monolog\LogRecord;
use NoDiscard;

/**
 * Used for testing purposes.
 *
 * It records all records and gives you access to them for verification.
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 *
 * @method bool hasEmergency(array{message: string, context?: mixed[]}|string $recordAssertions)
 * @method bool hasAlert(array{message: string, context?: mixed[]}|string $recordAssertions)
 * @method bool hasCritical(array{message: string, context?: mixed[]}|string $recordAssertions)
 * @method bool hasError(array{message: string, context?: mixed[]}|string $recordAssertions)
 * @method bool hasWarning(array{message: string, context?: mixed[]}|string $recordAssertions)
 * @method bool hasNotice(array{message: string, context?: mixed[]}|string $recordAssertions)
 * @method bool hasInfo(array{message: string, context?: mixed[]}|string $recordAssertions)
 * @method bool hasDebug(array{message: string, context?: mixed[]}|string $recordAssertions)
 *
 * @method bool hasEmergencyRecords()
 * @method bool hasAlertRecords()
 * @method bool hasCriticalRecords()
 * @method bool hasErrorRecords()
 * @method bool hasWarningRecords()
 * @method bool hasNoticeRecords()
 * @method bool hasInfoRecords()
 * @method bool hasDebugRecords()
 *
 * @method bool hasEmergencyThatContains(string $message)
 * @method bool hasAlertThatContains(string $message)
 * @method bool hasCriticalThatContains(string $message)
 * @method bool hasErrorThatContains(string $message)
 * @method bool hasWarningThatContains(string $message)
 * @method bool hasNoticeThatContains(string $message)
 * @method bool hasInfoThatContains(string $message)
 * @method bool hasDebugThatContains(string $message)
 *
 * @method bool hasEmergencyThatMatches(string $regex)
 * @method bool hasAlertThatMatches(string $regex)
 * @method bool hasCriticalThatMatches(string $regex)
 * @method bool hasErrorThatMatches(string $regex)
 * @method bool hasWarningThatMatches(string $regex)
 * @method bool hasNoticeThatMatches(string $regex)
 * @method bool hasInfoThatMatches(string $regex)
 * @method bool hasDebugThatMatches(string $regex)
 *
 * @method bool hasEmergencyThatPasses(callable $predicate)
 * @method bool hasAlertThatPasses(callable $predicate)
 * @method bool hasCriticalThatPasses(callable $predicate)
 * @method bool hasErrorThatPasses(callable $predicate)
 * @method bool hasWarningThatPasses(callable $predicate)
 * @method bool hasNoticeThatPasses(callable $predicate)
 * @method bool hasInfoThatPasses(callable $predicate)
 * @method bool hasDebugThatPasses(callable $predicate)
 */
class TestHandler extends AbstractProcessingHandler
{
    /** @var LogRecord[] */
    protected array $records = [];
    /** @phpstan-var array<value-of<Level::VALUES>, LogRecord[]> */
    protected array $recordsByLevel = [];
    private bool $skipReset = false;

    /**
     * @return array<LogRecord>
     */
    #[NoDiscard]
    public function getRecords(): array
    {
        return $this->records;
    }

    public function clear(): void
    {
        $this->records = [];
        $this->recordsByLevel = [];
    }

    public function reset(): void
    {
        if (!$this->skipReset) {
            $this->clear();
        }
    }

    public function setSkipReset(bool $skipReset): void
    {
        $this->skipReset = $skipReset;
    }

    /**
     * @param int|string|Level|LogLevel::* $level Logging level value or name
     *
     * @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $level
     */
    #[NoDiscard]
    public function hasRecords(int|string|Level $level): bool
    {
        return isset($this->recordsByLevel[Logger::toMonologLevel($level)->value]);
    }

    /**
     * @param string|array $recordAssertions Either a message string or an array containing message and optionally context keys that will be checked against all records
     *
     * @phpstan-param array{message: string, context?: mixed[]}|string $recordAssertions
     */
    #[NoDiscard]
    public function hasRecord(string|array $recordAssertions, Level $level): bool
    {
        if (\is_string($recordAssertions)) {
            $recordAssertions = ['message' => $recordAssertions];
        }

        return $this->hasRecordThatPasses(function (LogRecord $rec) use ($recordAssertions) {
            if ($rec->message !== $recordAssertions['message']) {
                return false;
            }
            if (isset($recordAssertions['context']) && $rec->context !== $recordAssertions['context']) {
                return false;
            }

            return true;
        }, $level);
    }

    #[NoDiscard]
    public function hasRecordThatContains(string $message, Level $level): bool
    {
        return $this->hasRecordThatPasses(fn (LogRecord $rec) => str_contains($rec->message, $message), $level);
    }

    #[NoDiscard]
    public function hasRecordThatMatches(string $regex, Level $level): bool
    {
        return $this->hasRecordThatPasses(fn (LogRecord $rec) => preg_match($regex, $rec->message) > 0, $level);
    }

    /**
     * @phpstan-param callable(LogRecord, int): mixed $predicate
     */
    #[NoDiscard]
    public function hasRecordThatPasses(callable $predicate, Level $level): bool
    {
        $level = Logger::toMonologLevel($level);

        if (!isset($this->recordsByLevel[$level->value])) {
            return false;
        }

        foreach ($this->recordsByLevel[$level->value] as $i => $rec) {
            if ((bool) $predicate($rec, $i)) {
                return true;
            }
        }

        return false;
    }

    /**
     * @inheritDoc
     */
    protected function write(LogRecord $record): void
    {
        $this->recordsByLevel[$record->level->value][] = $record;
        $this->records[] = $record;
    }

    /**
     * @param mixed[] $args
     */
    #[NoDiscard]
    public function __call(string $method, array $args): bool
    {
        if ((bool) preg_match('/(.*)(Debug|Info|Notice|Warning|Error|Critical|Alert|Emergency)(.*)/', $method, $matches)) {
            $genericMethod = $matches[1] . ('Records' !== $matches[3] ? 'Record' : '') . $matches[3];
            $level = \constant(Level::class.'::' . $matches[2]);
            $callback = [$this, $genericMethod];
            if (\is_callable($callback)) {
                $args[] = $level;

                return \call_user_func_array($callback, $args);
            }
        }

        throw new \BadMethodCallException('Call to undefined method ' . \get_class($this) . '::' . $method . '()');
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Gelf\Message as GelfMessage;
use Monolog\Level;
use Monolog\Formatter\FormatterInterface;
use Monolog\Formatter\JsonFormatter;
use PhpAmqpLib\Message\AMQPMessage;
use PhpAmqpLib\Channel\AMQPChannel;
use AMQPExchange;
use Monolog\LogRecord;

class AmqpHandler extends AbstractProcessingHandler
{
    protected AMQPExchange|AMQPChannel $exchange;

    /** @var array<string, mixed> */
    private array $extraAttributes = [];

    protected string $exchangeName;

    /**
     * @param AMQPExchange|AMQPChannel $exchange     AMQPExchange (php AMQP ext) or PHP AMQP lib channel, ready for use
     * @param string|null              $exchangeName Optional exchange name, for AMQPChannel (PhpAmqpLib) only
     */
    public function __construct(AMQPExchange|AMQPChannel $exchange, ?string $exchangeName = null, int|string|Level $level = Level::Debug, bool $bubble = true)
    {
        if ($exchange instanceof AMQPChannel) {
            $this->exchangeName = (string) $exchangeName;
        } elseif ($exchangeName !== null) {
            @trigger_error('The $exchangeName parameter can only be passed when using PhpAmqpLib, if using an AMQPExchange instance configure it beforehand', E_USER_DEPRECATED);
        }
        $this->exchange = $exchange;

        parent::__construct($level, $bubble);
    }

    /**
     * @return array<string, mixed>
     */
    public function getExtraAttributes(): array
    {
        return $this->extraAttributes;
    }

    /**
     * Configure extra attributes to pass to the AMQPExchange (if you are using the amqp extension)
     *
     * @param  array<string, mixed> $extraAttributes One of content_type, content_encoding,
     *                                               message_id, user_id, app_id, delivery_mode,
     *                                               priority, timestamp, expiration, type
     *                                               or reply_to, headers.
     * @return $this
     */
    public function setExtraAttributes(array $extraAttributes): self
    {
        $this->extraAttributes = $extraAttributes;

        return $this;
    }

    /**
     * @inheritDoc
     */
    protected function write(LogRecord $record): void
    {
        $data = $record->formatted;
        $routingKey = $this->getRoutingKey($record);

        if($data instanceof GelfMessage) {
            $data = json_encode($data->toArray());
        }

        if ($this->exchange instanceof AMQPExchange) {
            $attributes = [
                'delivery_mode' => 2,
                'content_type'  => 'application/json',
            ];
            if (\count($this->extraAttributes) > 0) {
                $attributes = array_merge($attributes, $this->extraAttributes);
            }
            $this->exchange->publish(
                $data,
                $routingKey,
                0,
                $attributes
            );
        } else {
            $this->exchange->basic_publish(
                $this->createAmqpMessage($data),
                $this->exchangeName,
                $routingKey
            );
        }
    }

    /**
     * @inheritDoc
     */
    public function handleBatch(array $records): void
    {
        if ($this->exchange instanceof AMQPExchange) {
            parent::handleBatch($records);

            return;
        }

        foreach ($records as $record) {
            if (!$this->isHandling($record)) {
                continue;
            }

            $record = $this->processRecord($record);
            $data = $this->getFormatter()->format($record);

            if($data instanceof GelfMessage) {
                $data = json_encode($data->toArray());
            }

            $this->exchange->batch_basic_publish(
                $this->createAmqpMessage($data),
                $this->exchangeName,
                $this->getRoutingKey($record)
            );
        }

        $this->exchange->publish_batch();
    }

    /**
     * Gets the routing key for the AMQP exchange
     */
    protected function getRoutingKey(LogRecord $record): string
    {
        $routingKey = sprintf('%s.%s', $record->level->name, $record->channel);

        return strtolower($routingKey);
    }

    private function createAmqpMessage(string $data): AMQPMessage
    {
        $attributes = [
            'delivery_mode' => 2,
            'content_type' => 'application/json',
        ];
        if (\count($this->extraAttributes) > 0) {
            $attributes = array_merge($attributes, $this->extraAttributes);
        }

        return new AMQPMessage($data, $attributes);
    }

    /**
     * @inheritDoc
     */
    protected function getDefaultFormatter(): FormatterInterface
    {
        return new JsonFormatter(JsonFormatter::BATCH_MODE_JSON, false);
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\Level;
use Monolog\Utils;
use Monolog\Formatter\FlowdockFormatter;
use Monolog\Formatter\FormatterInterface;
use Monolog\LogRecord;

/**
 * Sends notifications through the Flowdock push API
 *
 * This must be configured with a FlowdockFormatter instance via setFormatter()
 *
 * Notes:
 * API token - Flowdock API token
 *
 * @author Dominik Liebler <liebler.dominik@gmail.com>
 * @see https://www.flowdock.com/api/push
 * @deprecated Since 2.9.0 and 3.3.0, Flowdock was shutdown we will thus drop this handler in Monolog 4
 */
class FlowdockHandler extends SocketHandler
{
    protected string $apiToken;

    /**
     * @throws MissingExtensionException if OpenSSL is missing
     */
    public function __construct(
        string $apiToken,
        $level = Level::Debug,
        bool $bubble = true,
        bool $persistent = false,
        float $timeout = 0.0,
        float $writingTimeout = 10.0,
        ?float $connectionTimeout = null,
        ?int $chunkSize = null
    ) {
        if (!\extension_loaded('openssl')) {
            throw new MissingExtensionException('The OpenSSL PHP extension is required to use the FlowdockHandler');
        }

        parent::__construct(
            'ssl://api.flowdock.com:443',
            $level,
            $bubble,
            $persistent,
            $timeout,
            $writingTimeout,
            $connectionTimeout,
            $chunkSize
        );
        $this->apiToken = $apiToken;
    }

    /**
     * @inheritDoc
     */
    public function setFormatter(FormatterInterface $formatter): HandlerInterface
    {
        if (!Utils::unwrapFormatter($formatter) instanceof FlowdockFormatter) {
            throw new \InvalidArgumentException('The FlowdockHandler requires an instance of Monolog\Formatter\FlowdockFormatter to function correctly');
        }

        return parent::setFormatter($formatter);
    }

    /**
     * Gets the default formatter.
     */
    protected function getDefaultFormatter(): FormatterInterface
    {
        throw new \InvalidArgumentException('The FlowdockHandler must be configured (via setFormatter) with an instance of Monolog\Formatter\FlowdockFormatter to function correctly');
    }

    /**
     * @inheritDoc
     */
    protected function write(LogRecord $record): void
    {
        parent::write($record);

        $this->closeSocket();
    }

    /**
     * @inheritDoc
     */
    protected function generateDataStream(LogRecord $record): string
    {
        $content = $this->buildContent($record);

        return $this->buildHeader($content) . $content;
    }

    /**
     * Builds the body of API call
     */
    private function buildContent(LogRecord $record): string
    {
        return Utils::jsonEncode($record->formatted);
    }

    /**
     * Builds the header of the API Call
     */
    private function buildHeader(string $content): string
    {
        $header = "POST /v1/messages/team_inbox/" . $this->apiToken . " HTTP/1.1\r\n";
        $header .= "Host: api.flowdock.com\r\n";
        $header .= "Content-Type: application/json\r\n";
        $header .= "Content-Length: " . \strlen($content) . "\r\n";
        $header .= "\r\n";

        return $header;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\Level;
use Monolog\Formatter\NormalizerFormatter;
use Monolog\Formatter\FormatterInterface;
use Doctrine\CouchDB\CouchDBClient;
use Monolog\LogRecord;

/**
 * CouchDB handler for Doctrine CouchDB ODM
 *
 * @author Markus Bachmann <markus.bachmann@bachi.biz>
 */
class DoctrineCouchDBHandler extends AbstractProcessingHandler
{
    private CouchDBClient $client;

    public function __construct(CouchDBClient $client, int|string|Level $level = Level::Debug, bool $bubble = true)
    {
        $this->client = $client;
        parent::__construct($level, $bubble);
    }

    /**
     * @inheritDoc
     */
    protected function write(LogRecord $record): void
    {
        $this->client->postDocument($record->formatted);
    }

    protected function getDefaultFormatter(): FormatterInterface
    {
        return new NormalizerFormatter;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\Formatter\FormatterInterface;
use Monolog\Formatter\LineFormatter;
use Monolog\Utils;
use Monolog\LogRecord;
use Monolog\Level;

use function headers_list;
use function stripos;

/**
 * Handler sending logs to browser's javascript console with no browser extension required
 *
 * @author Olivier Poitrey <rs@dailymotion.com>
 */
class BrowserConsoleHandler extends AbstractProcessingHandler
{
    protected static bool $initialized = false;

    /** @var LogRecord[] */
    protected static array $records = [];

    protected const FORMAT_HTML = 'html';
    protected const FORMAT_JS = 'js';
    protected const FORMAT_UNKNOWN = 'unknown';

    /**
     * @inheritDoc
     *
     * Formatted output may contain some formatting markers to be transferred to `console.log` using the %c format.
     *
     * Example of formatted string:
     *
     *     You can do [[blue text]]{color: blue} or [[green background]]{background-color: green; color: white}
     */
    protected function getDefaultFormatter(): FormatterInterface
    {
        return new LineFormatter('[[%channel%]]{macro: autolabel} [[%level_name%]]{font-weight: bold} %message%');
    }

    /**
     * @inheritDoc
     */
    protected function write(LogRecord $record): void
    {
        // Accumulate records
        static::$records[] = $record;

        // Register shutdown handler if not already done
        if (!static::$initialized) {
            static::$initialized = true;
            $this->registerShutdownFunction();
        }
    }

    /**
     * Convert records to javascript console commands and send it to the browser.
     * This method is automatically called on PHP shutdown if output is HTML or Javascript.
     */
    public static function send(): void
    {
        $format = static::getResponseFormat();
        if ($format === self::FORMAT_UNKNOWN) {
            return;
        }

        if (\count(static::$records) > 0) {
            if ($format === self::FORMAT_HTML) {
                static::writeOutput('<script>' . self::generateScript() . '</script>');
            } else { // js format
                static::writeOutput(self::generateScript());
            }
            static::resetStatic();
        }
    }

    public function close(): void
    {
        self::resetStatic();
    }

    public function reset(): void
    {
        parent::reset();

        self::resetStatic();
    }

    /**
     * Forget all logged records
     */
    public static function resetStatic(): void
    {
        static::$records = [];
    }

    /**
     * Wrapper for register_shutdown_function to allow overriding
     */
    protected function registerShutdownFunction(): void
    {
        if (PHP_SAPI !== 'cli') {
            register_shutdown_function(['Monolog\Handler\BrowserConsoleHandler', 'send']);
        }
    }

    /**
     * Wrapper for echo to allow overriding
     */
    protected static function writeOutput(string $str): void
    {
        echo $str;
    }

    /**
     * Checks the format of the response
     *
     * If Content-Type is set to application/javascript or text/javascript -> js
     * If Content-Type is set to text/html, or is unset -> html
     * If Content-Type is anything else -> unknown
     *
     * @return string One of 'js', 'html' or 'unknown'
     * @phpstan-return self::FORMAT_*
     */
    protected static function getResponseFormat(): string
    {
        // Check content type
        foreach (headers_list() as $header) {
            if (stripos($header, 'content-type:') === 0) {
                return static::getResponseFormatFromContentType($header);
            }
        }

        return self::FORMAT_HTML;
    }

    /**
     * @return string One of 'js', 'html' or 'unknown'
     * @phpstan-return self::FORMAT_*
     */
    protected static function getResponseFormatFromContentType(string $contentType): string
    {
        // This handler only works with HTML and javascript outputs
        // text/javascript is obsolete in favour of application/javascript, but still used
        if (stripos($contentType, 'application/javascript') !== false || stripos($contentType, 'text/javascript') !== false) {
            return self::FORMAT_JS;
        }

        if (stripos($contentType, 'text/html') !== false) {
            return self::FORMAT_HTML;
        }

        return self::FORMAT_UNKNOWN;
    }

    private static function generateScript(): string
    {
        $script = [];
        foreach (static::$records as $record) {
            $context = self::dump('Context', $record->context);
            $extra = self::dump('Extra', $record->extra);

            if (\count($context) === 0 && \count($extra) === 0) {
                $script[] = self::call_array(self::getConsoleMethodForLevel($record->level), self::handleStyles($record->formatted));
            } else {
                $script = array_merge(
                    $script,
                    [self::call_array('groupCollapsed', self::handleStyles($record->formatted))],
                    $context,
                    $extra,
                    [self::call('groupEnd')]
                );
            }
        }

        return "(function (c) {if (c && c.groupCollapsed) {\n" . implode("\n", $script) . "\n}})(console);";
    }

    private static function getConsoleMethodForLevel(Level $level): string
    {
        return match ($level) {
            Level::Debug => 'debug',
            Level::Info, Level::Notice => 'info',
            Level::Warning => 'warn',
            Level::Error, Level::Critical, Level::Alert, Level::Emergency => 'error',
        };
    }

    /**
     * @return string[]
     */
    private static function handleStyles(string $formatted): array
    {
        $args = [];
        $format = '%c' . $formatted;
        preg_match_all('/\[\[(.*?)\]\]\{([^}]*)\}/s', $format, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);

        foreach (array_reverse($matches) as $match) {
            $args[] = '"font-weight: normal"';
            $args[] = self::quote(self::handleCustomStyles($match[2][0], $match[1][0]));

            $pos = $match[0][1];
            $format = Utils::substr($format, 0, $pos) . '%c' . $match[1][0] . '%c' . Utils::substr($format, $pos + \strlen($match[0][0]));
        }

        $args[] = self::quote('font-weight: normal');
        $args[] = self::quote($format);

        return array_reverse($args);
    }

    private static function handleCustomStyles(string $style, string $string): string
    {
        static $colors = ['blue', 'green', 'red', 'magenta', 'orange', 'black', 'grey'];
        static $labels = [];

        $style = preg_replace_callback('/macro\s*:(.*?)(?:;|$)/', function (array $m) use ($string, &$colors, &$labels) {
            if (trim($m[1]) === 'autolabel') {
                // Format the string as a label with consistent auto assigned background color
                if (!isset($labels[$string])) {
                    $labels[$string] = $colors[\count($labels) % \count($colors)];
                }
                $color = $labels[$string];

                return "background-color: $color; color: white; border-radius: 3px; padding: 0 2px 0 2px";
            }

            return $m[1];
        }, $style);

        if (null === $style) {
            $pcreErrorCode = preg_last_error();

            throw new \RuntimeException('Failed to run preg_replace_callback: ' . $pcreErrorCode . ' / ' . preg_last_error_msg());
        }

        return $style;
    }

    /**
     * @param  mixed[] $dict
     * @return mixed[]
     */
    private static function dump(string $title, array $dict): array
    {
        $script = [];
        $dict = array_filter($dict, fn ($value) => $value !== null);
        if (\count($dict) === 0) {
            return $script;
        }
        $script[] = self::call('log', self::quote('%c%s'), self::quote('font-weight: bold'), self::quote($title));
        foreach ($dict as $key => $value) {
            $value = Utils::jsonEncode($value, Utils::DEFAULT_JSON_FLAGS | JSON_HEX_TAG);
            $script[] = self::call('log', self::quote('%s: %o'), self::quote((string) $key), $value);
        }

        return $script;
    }

    private static function quote(string $arg): string
    {
        // JSON_HEX_TAG keeps a literal < out of the output, so that a log message cannot break out
        // of the surrounding <script> element via </script> or <!--<script>. json_encode also
        // escapes \r and U+2028/U+2029, which would otherwise terminate the JS string literal.
        return Utils::jsonEncode($arg, Utils::DEFAULT_JSON_FLAGS | JSON_HEX_TAG);
    }

    /**
     * @param mixed $args
     */
    private static function call(...$args): string
    {
        $method = array_shift($args);
        if (!\is_string($method)) {
            throw new \UnexpectedValueException('Expected the first arg to be a string, got: '.var_export($method, true));
        }

        return self::call_array($method, $args);
    }

    /**
     * @param mixed[] $args
     */
    private static function call_array(string $method, array $args): string
    {
        return 'c.' . $method . '(' . implode(', ', $args) . ');';
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Closure;
use Monolog\Level;
use Monolog\Logger;
use Monolog\ResettableInterface;
use Monolog\Formatter\FormatterInterface;
use Psr\Log\LogLevel;
use Monolog\LogRecord;

/**
 * Simple handler wrapper that filters records based on a list of levels
 *
 * It can be configured with an exact list of levels to allow, or a min/max level.
 *
 * @author Hennadiy Verkh
 * @author Jordi Boggiano <j.boggiano@seld.be>
 */
class FilterHandler extends Handler implements ProcessableHandlerInterface, ResettableInterface, FormattableHandlerInterface
{
    use ProcessableHandlerTrait;

    /**
     * Handler or factory Closure($record, $this)
     *
     * @phpstan-var (Closure(LogRecord|null, HandlerInterface): HandlerInterface)|HandlerInterface
     */
    protected Closure|HandlerInterface $handler;

    /**
     * Minimum level for logs that are passed to handler
     *
     * @var bool[] Map of Level value => true
     * @phpstan-var array<value-of<Level::VALUES>, true>
     */
    protected array $acceptedLevels;

    /**
     * Whether the messages that are handled can bubble up the stack or not
     */
    protected bool $bubble;

    /**
     * @phpstan-param (Closure(LogRecord|null, HandlerInterface): HandlerInterface)|HandlerInterface $handler
     *
     * @param Closure|HandlerInterface                             $handler        Handler or factory Closure($record|null, $filterHandler).
     * @param int|string|Level|array<int|string|Level|LogLevel::*> $minLevelOrList A list of levels to accept or a minimum level if maxLevel is provided
     * @param int|string|Level|LogLevel::*                         $maxLevel       Maximum level to accept, only used if $minLevelOrList is not an array
     * @param bool                                                 $bubble         Whether the messages that are handled can bubble up the stack or not
     *
     * @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::*|array<value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::*> $minLevelOrList
     * @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $maxLevel
     */
    public function __construct(Closure|HandlerInterface $handler, int|string|Level|array $minLevelOrList = Level::Debug, int|string|Level $maxLevel = Level::Emergency, bool $bubble = true)
    {
        $this->handler  = $handler;
        $this->bubble   = $bubble;
        $this->setAcceptedLevels($minLevelOrList, $maxLevel);
    }

    /**
     * @phpstan-return list<Level> List of levels
     */
    public function getAcceptedLevels(): array
    {
        return array_map(fn (int $level) => Level::from($level), array_keys($this->acceptedLevels));
    }

    /**
     * @param  int|string|Level|LogLevel::*|array<int|string|Level|LogLevel::*> $minLevelOrList A list of levels to accept or a minimum level or level name if maxLevel is provided
     * @param  int|string|Level|LogLevel::*                                     $maxLevel       Maximum level or level name to accept, only used if $minLevelOrList is not an array
     * @return $this
     *
     * @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::*|array<value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::*> $minLevelOrList
     * @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $maxLevel
     */
    public function setAcceptedLevels(int|string|Level|array $minLevelOrList = Level::Debug, int|string|Level $maxLevel = Level::Emergency): self
    {
        if (\is_array($minLevelOrList)) {
            $acceptedLevels = array_map(Logger::toMonologLevel(...), $minLevelOrList);
        } else {
            $minLevelOrList = Logger::toMonologLevel($minLevelOrList);
            $maxLevel = Logger::toMonologLevel($maxLevel);
            $acceptedLevels = array_values(array_filter(Level::cases(), fn (Level $level) => $level->value >= $minLevelOrList->value && $level->value <= $maxLevel->value));
        }
        $this->acceptedLevels = [];
        foreach ($acceptedLevels as $level) {
            $this->acceptedLevels[$level->value] = true;
        }

        return $this;
    }

    /**
     * @inheritDoc
     */
    public function isHandling(LogRecord $record): bool
    {
        return isset($this->acceptedLevels[$record->level->value]);
    }

    /**
     * @inheritDoc
     */
    public function handle(LogRecord $record): bool
    {
        if (!$this->isHandling($record)) {
            return false;
        }

        if (\count($this->processors) > 0) {
            $record = $this->processRecord($record);
        }

        $this->getHandler($record)->handle($record);

        return false === $this->bubble;
    }

    /**
     * @inheritDoc
     */
    public function handleBatch(array $records): void
    {
        $filtered = [];
        foreach ($records as $record) {
            if ($this->isHandling($record)) {
                $filtered[] = $record;
            }
        }

        if (\count($filtered) > 0) {
            $this->getHandler($filtered[\count($filtered) - 1])->handleBatch($filtered);
        }
    }

    /**
     * Return the nested handler
     *
     * If the handler was provided as a factory, this will trigger the handler's instantiation.
     */
    public function getHandler(LogRecord|null $record = null): HandlerInterface
    {
        if (!$this->handler instanceof HandlerInterface) {
            $handler = ($this->handler)($record, $this);
            if (!$handler instanceof HandlerInterface) {
                throw new \RuntimeException("The factory Closure should return a HandlerInterface");
            }
            $this->handler = $handler;
        }

        return $this->handler;
    }

    /**
     * @inheritDoc
     */
    public function setFormatter(FormatterInterface $formatter): HandlerInterface
    {
        $handler = $this->getHandler();
        if ($handler instanceof FormattableHandlerInterface) {
            $handler->setFormatter($formatter);

            return $this;
        }

        throw new \UnexpectedValueException('The nested handler of type '.\get_class($handler).' does not support formatters.');
    }

    /**
     * @inheritDoc
     */
    public function getFormatter(): FormatterInterface
    {
        $handler = $this->getHandler();
        if ($handler instanceof FormattableHandlerInterface) {
            return $handler->getFormatter();
        }

        throw new \UnexpectedValueException('The nested handler of type '.\get_class($handler).' does not support formatters.');
    }

    public function reset(): void
    {
        $this->resetProcessors();

        if ($this->getHandler() instanceof ResettableInterface) {
            $this->getHandler()->reset();
        }
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\Formatter\ChromePHPFormatter;
use Monolog\Formatter\FormatterInterface;
use Monolog\Level;
use Monolog\Utils;
use Monolog\LogRecord;
use Monolog\JsonSerializableDateTimeImmutable;

/**
 * Handler sending logs to the ChromePHP extension (http://www.chromephp.com/)
 *
 * This also works out of the box with Firefox 43+
 *
 * @author Christophe Coevoet <stof@notk.org>
 */
class ChromePHPHandler extends AbstractProcessingHandler
{
    use WebRequestRecognizerTrait;

    /**
     * Version of the extension
     */
    protected const VERSION = '4.0';

    /**
     * Header name
     */
    protected const HEADER_NAME = 'X-ChromeLogger-Data';

    /**
     * Regular expression to detect supported browsers (matches any Chrome, or Firefox 43+)
     */
    protected const USER_AGENT_REGEX = '{\b(?:Chrome/\d+(?:\.\d+)*|HeadlessChrome|Firefox/(?:4[3-9]|[5-9]\d|\d{3,})(?:\.\d)*)\b}';

    protected static bool $initialized = false;

    /**
     * Tracks whether we sent too much data
     *
     * Chrome limits the headers to 4KB, so when we sent 3KB we stop sending
     */
    protected static bool $overflowed = false;

    /** @var mixed[] */
    protected static array $json = [
        'version' => self::VERSION,
        'columns' => ['label', 'log', 'backtrace', 'type'],
        'rows' => [],
    ];

    protected static bool $sendHeaders = true;

    public function __construct(int|string|Level $level = Level::Debug, bool $bubble = true)
    {
        parent::__construct($level, $bubble);
    }

    /**
     * @inheritDoc
     */
    public function handleBatch(array $records): void
    {
        if (!$this->isWebRequest()) {
            return;
        }

        $messages = [];

        foreach ($records as $record) {
            if ($record->level < $this->level) {
                continue;
            }

            $message = $this->processRecord($record);
            $messages[] = $message;
        }

        if (\count($messages) > 0) {
            $messages = $this->getFormatter()->formatBatch($messages);
            self::$json['rows'] = array_merge(self::$json['rows'], $messages);
            $this->send();
        }
    }

    /**
     * @inheritDoc
     */
    protected function getDefaultFormatter(): FormatterInterface
    {
        return new ChromePHPFormatter();
    }

    /**
     * Creates & sends header for a record
     *
     * @see sendHeader()
     * @see send()
     */
    protected function write(LogRecord $record): void
    {
        if (!$this->isWebRequest()) {
            return;
        }

        self::$json['rows'][] = $record->formatted;

        $this->send();
    }

    /**
     * Sends the log header
     *
     * @see sendHeader()
     */
    protected function send(): void
    {
        if (self::$overflowed || !self::$sendHeaders) {
            return;
        }

        if (!self::$initialized) {
            self::$initialized = true;

            self::$sendHeaders = $this->headersAccepted();
            if (!self::$sendHeaders) {
                return;
            }

            self::$json['request_uri'] = $_SERVER['REQUEST_URI'] ?? '';
        }

        $json = Utils::jsonEncode(self::$json, Utils::DEFAULT_JSON_FLAGS & ~JSON_UNESCAPED_UNICODE, true);
        $data = base64_encode($json);
        if (\strlen($data) > 3 * 1024) {
            self::$overflowed = true;

            $record = new LogRecord(
                message: 'Incomplete logs, chrome header size limit reached',
                level: Level::Warning,
                channel: 'monolog',
                datetime: new JsonSerializableDateTimeImmutable(true),
            );
            self::$json['rows'][\count(self::$json['rows']) - 1] = $this->getFormatter()->format($record);
            $json = Utils::jsonEncode(self::$json, Utils::DEFAULT_JSON_FLAGS & ~JSON_UNESCAPED_UNICODE, true);
            $data = base64_encode($json);
        }

        if (trim($data) !== '') {
            $this->sendHeader(static::HEADER_NAME, $data);
        }
    }

    /**
     * Send header string to the client
     */
    protected function sendHeader(string $header, string $content): void
    {
        if (!headers_sent() && self::$sendHeaders) {
            header(sprintf('%s: %s', $header, $content));
        }
    }

    /**
     * Verifies if the headers are accepted by the current user agent
     */
    protected function headersAccepted(): bool
    {
        if (!isset($_SERVER['HTTP_USER_AGENT'])) {
            return false;
        }

        return preg_match(static::USER_AGENT_REGEX, $_SERVER['HTTP_USER_AGENT']) === 1;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\ResettableInterface;
use Monolog\Processor\ProcessorInterface;
use Monolog\LogRecord;

/**
 * Helper trait for implementing ProcessableInterface
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 */
trait ProcessableHandlerTrait
{
    /**
     * @var callable[]
     * @phpstan-var array<(callable(LogRecord): LogRecord)|ProcessorInterface>
     */
    protected array $processors = [];

    /**
     * @inheritDoc
     */
    public function pushProcessor(callable $callback): HandlerInterface
    {
        array_unshift($this->processors, $callback);

        return $this;
    }

    /**
     * @inheritDoc
     */
    public function popProcessor(): callable
    {
        if (\count($this->processors) === 0) {
            throw new \LogicException('You tried to pop from an empty processor stack.');
        }

        return array_shift($this->processors);
    }

    protected function processRecord(LogRecord $record): LogRecord
    {
        foreach ($this->processors as $processor) {
            $record = $processor($record);
        }

        return $record;
    }

    protected function resetProcessors(): void
    {
        foreach ($this->processors as $processor) {
            if ($processor instanceof ResettableInterface) {
                $processor->reset();
            }
        }
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\Formatter\FormatterInterface;
use Monolog\Level;
use Monolog\Utils;
use Monolog\Handler\Slack\SlackRecord;
use Monolog\LogRecord;

/**
 * Sends notifications through Slack Webhooks
 *
 * @author Haralan Dobrev <hkdobrev@gmail.com>
 * @see    https://api.slack.com/incoming-webhooks
 */
class SlackWebhookHandler extends AbstractProcessingHandler
{
    /**
     * Slack Webhook token
     *
     * @var non-empty-string
     */
    private string $webhookUrl;

    /**
     * Instance of the SlackRecord util class preparing data for Slack API.
     */
    private SlackRecord $slackRecord;

    /**
     * @param non-empty-string $webhookUrl             Slack Webhook URL
     * @param string|null $channel                Slack channel (encoded ID or name)
     * @param string|null $username               Name of a bot
     * @param bool        $useAttachment          Whether the message should be added to Slack as attachment (plain text otherwise)
     * @param string|null $iconEmoji              The emoji name to use (or null)
     * @param bool        $useShortAttachment     Whether the the context/extra messages added to Slack as attachments are in a short style
     * @param bool        $includeContextAndExtra Whether the attachment should include context and extra data
     * @param string[]    $excludeFields          Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2']
     *
     * @throws MissingExtensionException If the curl extension is missing
     */
    public function __construct(
        string $webhookUrl,
        ?string $channel = null,
        ?string $username = null,
        bool $useAttachment = true,
        ?string $iconEmoji = null,
        bool $useShortAttachment = false,
        bool $includeContextAndExtra = false,
        $level = Level::Critical,
        bool $bubble = true,
        array $excludeFields = []
    ) {
        if (!\extension_loaded('curl')) {
            throw new MissingExtensionException('The curl extension is needed to use the SlackWebhookHandler');
        }

        parent::__construct($level, $bubble);

        $this->webhookUrl = $webhookUrl;

        $this->slackRecord = new SlackRecord(
            $channel,
            $username,
            $useAttachment,
            $iconEmoji,
            $useShortAttachment,
            $includeContextAndExtra,
            $excludeFields
        );
    }

    public function getSlackRecord(): SlackRecord
    {
        return $this->slackRecord;
    }

    public function getWebhookUrl(): string
    {
        return $this->webhookUrl;
    }

    /**
     * @inheritDoc
     */
    protected function write(LogRecord $record): void
    {
        $postData = $this->slackRecord->getSlackData($record);
        $postString = Utils::jsonEncode($postData);

        $ch = curl_init();
        $options = [
            CURLOPT_URL => $this->webhookUrl,
            CURLOPT_POST => true,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HTTPHEADER => ['Content-type: application/json'],
            CURLOPT_POSTFIELDS => $postString,
        ];

        curl_setopt_array($ch, $options);

        Curl\Util::execute($ch);
    }

    public function setFormatter(FormatterInterface $formatter): HandlerInterface
    {
        parent::setFormatter($formatter);
        $this->slackRecord->setFormatter($formatter);

        return $this;
    }

    public function getFormatter(): FormatterInterface
    {
        $formatter = parent::getFormatter();
        $this->slackRecord->setFormatter($formatter);

        return $formatter;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Elastic\Elasticsearch\Response\Elasticsearch;
use Throwable;
use RuntimeException;
use Monolog\Level;
use Monolog\Formatter\FormatterInterface;
use Monolog\Formatter\ElasticsearchFormatter;
use InvalidArgumentException;
use Elasticsearch\Common\Exceptions\RuntimeException as ElasticsearchRuntimeException;
use Elasticsearch\Client;
use Monolog\LogRecord;
use Monolog\Utils;
use Elastic\Elasticsearch\Exception\InvalidArgumentException as ElasticInvalidArgumentException;
use Elastic\Elasticsearch\Client as Client8;

/**
 * Elasticsearch handler
 *
 * @link https://www.elastic.co/guide/en/elasticsearch/client/php-api/current/index.html
 *
 * Simple usage example:
 *
 *    $client = \Elasticsearch\ClientBuilder::create()
 *        ->setHosts($hosts)
 *        ->build();
 *
 *    $options = array(
 *        'index' => 'elastic_index_name',
 *        'type'  => 'elastic_doc_type',
 *    );
 *    $handler = new ElasticsearchHandler($client, $options);
 *    $log = new Logger('application');
 *    $log->pushHandler($handler);
 *
 * @author Avtandil Kikabidze <akalongman@gmail.com>
 * @phpstan-type Options array{
 *     index: string,
 *     type: string,
 *     ignore_error: bool,
 *     op_type: 'index'|'create'
 * }
 * @phpstan-type InputOptions array{
 *     index?: string,
 *     type?: string,
 *     ignore_error?: bool,
 *     op_type?: 'index'|'create'
 * }
 */
class ElasticsearchHandler extends AbstractProcessingHandler
{
    protected Client|Client8 $client;

    /**
     * @var mixed[] Handler config options
     * @phpstan-var Options
     */
    protected array $options;

    /**
     * @var bool
     */
    private $needsType;

    /**
     * @param Client|Client8 $client  Elasticsearch Client object
     * @param mixed[]        $options Handler configuration
     *
     * @phpstan-param InputOptions $options
     */
    public function __construct(Client|Client8 $client, array $options = [], int|string|Level $level = Level::Debug, bool $bubble = true)
    {
        parent::__construct($level, $bubble);
        $this->client = $client;
        $this->options = array_merge(
            [
                'index'        => 'monolog', // Elastic index name
                'type'         => '_doc',    // Elastic document type
                'ignore_error' => false,     // Suppress Elasticsearch exceptions
                'op_type'      => 'index',   // Elastic op_type (index or create) (https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-index_.html#docs-index-api-op_type)
            ],
            $options
        );

        if ($client instanceof Client8 || $client::VERSION[0] === '7') {
            $this->needsType = false;
            // force the type to _doc for ES8/ES7
            $this->options['type'] = '_doc';
        } else {
            $this->needsType = true;
        }
    }

    /**
     * @inheritDoc
     */
    protected function write(LogRecord $record): void
    {
        $this->bulkSend([$record->formatted]);
    }

    /**
     * @inheritDoc
     */
    public function setFormatter(FormatterInterface $formatter): HandlerInterface
    {
        if (Utils::unwrapFormatter($formatter) instanceof ElasticsearchFormatter) {
            return parent::setFormatter($formatter);
        }

        throw new InvalidArgumentException('ElasticsearchHandler is only compatible with ElasticsearchFormatter');
    }

    /**
     * Getter options
     *
     * @return mixed[]
     *
     * @phpstan-return Options
     */
    public function getOptions(): array
    {
        return $this->options;
    }

    /**
     * @inheritDoc
     */
    protected function getDefaultFormatter(): FormatterInterface
    {
        return new ElasticsearchFormatter($this->options['index'], $this->options['type']);
    }

    /**
     * @inheritDoc
     */
    public function handleBatch(array $records): void
    {
        $documents = $this->getFormatter()->formatBatch($records);
        $this->bulkSend($documents);
    }

    /**
     * Use Elasticsearch bulk API to send list of documents
     *
     * @param  array<array<mixed>> $records Records + _index/_type keys
     * @throws \RuntimeException
     */
    protected function bulkSend(array $records): void
    {
        try {
            $params = [
                'body' => [],
            ];

            foreach ($records as $record) {
                $params['body'][] = [
                    $this->options['op_type'] => $this->needsType ? [
                        '_index' => $record['_index'],
                        '_type'  => $record['_type'],
                    ] : [
                        '_index' => $record['_index'],
                    ],
                ];
                unset($record['_index'], $record['_type']);

                $params['body'][] = $record;
            }

            /** @var Elasticsearch */
            $responses = $this->client->bulk($params);

            if ($responses['errors'] === true) {
                throw $this->createExceptionFromResponses($responses);
            }
        } catch (Throwable $e) {
            if (! $this->options['ignore_error']) {
                throw new RuntimeException('Error sending messages to Elasticsearch', 0, $e);
            }
        }
    }

    /**
     * Creates elasticsearch exception from responses array
     *
     * Only the first error is converted into an exception.
     *
     * @param mixed[]|Elasticsearch $responses returned by $this->client->bulk()
     */
    protected function createExceptionFromResponses($responses): Throwable
    {
        foreach ($responses['items'] ?? [] as $item) {
            if (isset($item['index']['error'])) {
                return $this->createExceptionFromError($item['index']['error']);
            }
        }

        if (class_exists(ElasticInvalidArgumentException::class)) {
            return new ElasticInvalidArgumentException('Elasticsearch failed to index one or more records.');
        }

        if (class_exists(ElasticsearchRuntimeException::class)) {
            return new ElasticsearchRuntimeException('Elasticsearch failed to index one or more records.');
        }

        throw new \LogicException('Unsupported elastic search client version');
    }

    /**
     * Creates elasticsearch exception from error array
     *
     * @param mixed[] $error
     */
    protected function createExceptionFromError(array $error): Throwable
    {
        $previous = isset($error['caused_by']) ? $this->createExceptionFromError($error['caused_by']) : null;

        if (class_exists(ElasticInvalidArgumentException::class)) {
            return new ElasticInvalidArgumentException($error['type'] . ': ' . $error['reason'], 0, $previous);
        }

        if (class_exists(ElasticsearchRuntimeException::class)) {
            return new ElasticsearchRuntimeException($error['type'].': '.$error['reason'], 0, $previous);
        }

        throw new \LogicException('Unsupported elastic search client version');
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\Formatter\FormatterInterface;
use Monolog\Level;
use Monolog\Utils;
use Monolog\Handler\Slack\SlackRecord;
use Monolog\LogRecord;

/**
 * Sends notifications through Slack API
 *
 * @author Greg Kedzierski <greg@gregkedzierski.com>
 * @see    https://api.slack.com/
 */
class SlackHandler extends SocketHandler
{
    /**
     * Slack API token
     */
    private string $token;

    /**
     * Instance of the SlackRecord util class preparing data for Slack API.
     */
    private SlackRecord $slackRecord;

    /**
     * @param  string                    $token                  Slack API token
     * @param  string                    $channel                Slack channel (encoded ID or name)
     * @param  string|null               $username               Name of a bot
     * @param  bool                      $useAttachment          Whether the message should be added to Slack as attachment (plain text otherwise)
     * @param  string|null               $iconEmoji              The emoji name to use (or null)
     * @param  bool                      $useShortAttachment     Whether the context/extra messages added to Slack as attachments are in a short style
     * @param  bool                      $includeContextAndExtra Whether the attachment should include context and extra data
     * @param  string[]                  $excludeFields          Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2']
     * @throws MissingExtensionException If no OpenSSL PHP extension configured
     */
    public function __construct(
        string $token,
        string $channel,
        ?string $username = null,
        bool $useAttachment = true,
        ?string $iconEmoji = null,
        $level = Level::Critical,
        bool $bubble = true,
        bool $useShortAttachment = false,
        bool $includeContextAndExtra = false,
        array $excludeFields = [],
        bool $persistent = false,
        float $timeout = 0.0,
        float $writingTimeout = 10.0,
        ?float $connectionTimeout = null,
        ?int $chunkSize = null
    ) {
        if (!\extension_loaded('openssl')) {
            throw new MissingExtensionException('The OpenSSL PHP extension is required to use the SlackHandler');
        }

        parent::__construct(
            'ssl://slack.com:443',
            $level,
            $bubble,
            $persistent,
            $timeout,
            $writingTimeout,
            $connectionTimeout,
            $chunkSize
        );

        $this->slackRecord = new SlackRecord(
            $channel,
            $username,
            $useAttachment,
            $iconEmoji,
            $useShortAttachment,
            $includeContextAndExtra,
            $excludeFields
        );

        $this->token = $token;
    }

    public function getSlackRecord(): SlackRecord
    {
        return $this->slackRecord;
    }

    public function getToken(): string
    {
        return $this->token;
    }

    /**
     * @inheritDoc
     */
    protected function generateDataStream(LogRecord $record): string
    {
        $content = $this->buildContent($record);

        return $this->buildHeader($content) . $content;
    }

    /**
     * Builds the body of API call
     */
    private function buildContent(LogRecord $record): string
    {
        $dataArray = $this->prepareContentData($record);

        return http_build_query($dataArray);
    }

    /**
     * @return string[]
     */
    protected function prepareContentData(LogRecord $record): array
    {
        $dataArray = $this->slackRecord->getSlackData($record);
        $dataArray['token'] = $this->token;

        if (isset($dataArray['attachments']) && \is_array($dataArray['attachments']) && \count($dataArray['attachments']) > 0) {
            $dataArray['attachments'] = Utils::jsonEncode($dataArray['attachments']);
        }

        return $dataArray;
    }

    /**
     * Builds the header of the API Call
     */
    private function buildHeader(string $content): string
    {
        $header = "POST /api/chat.postMessage HTTP/1.1\r\n";
        $header .= "Host: slack.com\r\n";
        $header .= "Content-Type: application/x-www-form-urlencoded\r\n";
        $header .= "Content-Length: " . \strlen($content) . "\r\n";
        $header .= "\r\n";

        return $header;
    }

    /**
     * @inheritDoc
     */
    protected function write(LogRecord $record): void
    {
        parent::write($record);
        $this->finalizeWrite();
    }

    /**
     * Finalizes the request by reading some bytes and then closing the socket
     *
     * If we do not read some but close the socket too early, slack sometimes
     * drops the request entirely.
     */
    protected function finalizeWrite(): void
    {
        $res = $this->getResource();
        if (\is_resource($res)) {
            @fread($res, 2048);
        }
        $this->closeSocket();
    }

    public function setFormatter(FormatterInterface $formatter): HandlerInterface
    {
        parent::setFormatter($formatter);
        $this->slackRecord->setFormatter($formatter);

        return $this;
    }

    public function getFormatter(): FormatterInterface
    {
        $formatter = parent::getFormatter();
        $this->slackRecord->setFormatter($formatter);

        return $formatter;
    }

    /**
     * Channel used by the bot when posting
     *
     * @return $this
     */
    public function setChannel(string $channel): self
    {
        $this->slackRecord->setChannel($channel);

        return $this;
    }

    /**
     * Username used by the bot when posting
     *
     * @return $this
     */
    public function setUsername(string $username): self
    {
        $this->slackRecord->setUsername($username);

        return $this;
    }

    /**
     * @return $this
     */
    public function useAttachment(bool $useAttachment): self
    {
        $this->slackRecord->useAttachment($useAttachment);

        return $this;
    }

    /**
     * @return $this
     */
    public function setIconEmoji(string $iconEmoji): self
    {
        $this->slackRecord->setUserIcon($iconEmoji);

        return $this;
    }

    /**
     * @return $this
     */
    public function useShortAttachment(bool $useShortAttachment): self
    {
        $this->slackRecord->useShortAttachment($useShortAttachment);

        return $this;
    }

    /**
     * @return $this
     */
    public function includeContextAndExtra(bool $includeContextAndExtra): self
    {
        $this->slackRecord->includeContextAndExtra($includeContextAndExtra);

        return $this;
    }

    /**
     * @param  string[] $excludeFields
     * @return $this
     */
    public function excludeFields(array $excludeFields): self
    {
        $this->slackRecord->excludeFields($excludeFields);

        return $this;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler\FingersCrossed;

use Monolog\LogRecord;

/**
 * Interface for activation strategies for the FingersCrossedHandler.
 *
 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
 */
interface ActivationStrategyInterface
{
    /**
     * Returns whether the given record activates the handler.
     */
    public function isHandlerActivated(LogRecord $record): bool;
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler\FingersCrossed;

use Monolog\Level;
use Monolog\LogRecord;
use Monolog\Logger;
use Psr\Log\LogLevel;

/**
 * Error level based activation strategy.
 *
 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
 */
class ErrorLevelActivationStrategy implements ActivationStrategyInterface
{
    private Level $actionLevel;

    /**
     * @param int|string|Level $actionLevel Level or name or value
     *
     * @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $actionLevel
     */
    public function __construct(int|string|Level $actionLevel)
    {
        $this->actionLevel = Logger::toMonologLevel($actionLevel);
    }

    public function isHandlerActivated(LogRecord $record): bool
    {
        return $record->level->value >= $this->actionLevel->value;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler\FingersCrossed;

use Monolog\Level;
use Monolog\Logger;
use Psr\Log\LogLevel;
use Monolog\LogRecord;

/**
 * Channel and Error level based monolog activation strategy. Allows to trigger activation
 * based on level per channel. e.g. trigger activation on level 'ERROR' by default, except
 * for records of the 'sql' channel; those should trigger activation on level 'WARN'.
 *
 * Example:
 *
 * <code>
 *   $activationStrategy = new ChannelLevelActivationStrategy(
 *       Level::Critical,
 *       array(
 *           'request' => Level::Alert,
 *           'sensitive' => Level::Error,
 *       )
 *   );
 *   $handler = new FingersCrossedHandler(new StreamHandler('php://stderr'), $activationStrategy);
 * </code>
 *
 * @author Mike Meessen <netmikey@gmail.com>
 */
class ChannelLevelActivationStrategy implements ActivationStrategyInterface
{
    private Level $defaultActionLevel;

    /**
     * @var array<string, Level>
     */
    private array $channelToActionLevel;

    /**
     * @param int|string|Level|LogLevel::*                $defaultActionLevel   The default action level to be used if the record's category doesn't match any
     * @param array<string, int|string|Level|LogLevel::*> $channelToActionLevel An array that maps channel names to action levels.
     *
     * @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $defaultActionLevel
     * @phpstan-param array<string, value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::*> $channelToActionLevel
     */
    public function __construct(int|string|Level $defaultActionLevel, array $channelToActionLevel = [])
    {
        $this->defaultActionLevel = Logger::toMonologLevel($defaultActionLevel);
        $this->channelToActionLevel = array_map(Logger::toMonologLevel(...), $channelToActionLevel);
    }

    public function isHandlerActivated(LogRecord $record): bool
    {
        if (isset($this->channelToActionLevel[$record->channel])) {
            return $record->level->value >= $this->channelToActionLevel[$record->channel]->value;
        }

        return $record->level->value >= $this->defaultActionLevel->value;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\Processor\ProcessorInterface;
use Monolog\LogRecord;

/**
 * Interface to describe loggers that have processors
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 */
interface ProcessableHandlerInterface
{
    /**
     * Adds a processor in the stack.
     *
     * @phpstan-param ProcessorInterface|(callable(LogRecord): LogRecord) $callback
     *
     * @param  ProcessorInterface|callable $callback
     * @return HandlerInterface            self
     */
    public function pushProcessor(callable $callback): HandlerInterface;

    /**
     * Removes the processor on top of the stack and returns it.
     *
     * @phpstan-return ProcessorInterface|(callable(LogRecord): LogRecord) $callback
     *
     * @throws \LogicException             In case the processor stack is empty
     * @return callable|ProcessorInterface
     */
    public function popProcessor(): callable;
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use DateTimeZone;
use InvalidArgumentException;
use Monolog\Level;
use Monolog\Utils;
use Monolog\LogRecord;

/**
 * Stores logs to files that are rotated every day and a limited number of files are kept.
 *
 * This rotation is only intended to be used as a workaround. Using logrotate to
 * handle the rotation is strongly encouraged when you can use it.
 *
 * @author Christophe Coevoet <stof@notk.org>
 * @author Jordi Boggiano <j.boggiano@seld.be>
 */
class RotatingFileHandler extends StreamHandler
{
    public const FILE_PER_HOUR = 'Y-m-d-H';
    public const FILE_PER_DAY = 'Y-m-d';
    public const FILE_PER_MONTH = 'Y-m';
    public const FILE_PER_YEAR = 'Y';

    protected string $filename;
    protected int $maxFiles;
    protected bool|null $mustRotate = null;
    protected \DateTimeImmutable $nextRotation;
    protected string $filenameFormat;
    protected string $dateFormat;
    protected DateTimeZone|null $timezone = null;

    /**
     * @param int      $maxFiles       The maximal amount of files to keep (0 means unlimited)
     * @param int|null $filePermission Optional file permissions (default (0644) are only for owner read/write)
     * @param bool     $useLocking     Try to lock log file before doing any writes
     */
    public function __construct(string $filename, int $maxFiles = 0, int|string|Level $level = Level::Debug, bool $bubble = true, ?int $filePermission = null, bool $useLocking = false, string $dateFormat = self::FILE_PER_DAY, string $filenameFormat  = '{filename}-{date}', DateTimeZone|null $timezone = null)
    {
        $this->filename = Utils::canonicalizePath($filename);
        $this->maxFiles = $maxFiles;
        $this->timezone = $timezone;
        $this->setFilenameFormat($filenameFormat, $dateFormat);
        $this->nextRotation = $this->getNextRotation();

        parent::__construct($this->getTimedFilename(), $level, $bubble, $filePermission, $useLocking);
    }

    /**
     * @inheritDoc
     */
    public function close(): void
    {
        parent::close();

        if (true === $this->mustRotate) {
            $this->rotate();
        }
    }

    /**
     * @inheritDoc
     */
    public function reset(): void
    {
        parent::reset();
    }

    /**
     * @return $this
     */
    public function setFilenameFormat(string $filenameFormat, string $dateFormat): self
    {
        $this->setDateFormat($dateFormat);
        if (substr_count($filenameFormat, '{date}') === 0) {
            throw new InvalidArgumentException(
                'Invalid filename format - format must contain at least `{date}`, because otherwise rotating is impossible.'
            );
        }
        $this->filenameFormat = $filenameFormat;
        $this->url = $this->getTimedFilename();
        $this->close();

        return $this;
    }

    /**
     * @inheritDoc
     */
    protected function write(LogRecord $record): void
    {
        // on the first record written, if the log is new, we rotate (once per day) after the log has been written so that the new file exists
        if (null === $this->mustRotate) {
            $this->mustRotate = null === $this->url || !file_exists($this->url);
        }

        // if the next rotation is expired, then we rotate immediately
        if ($this->nextRotation <= $record->datetime) {
            $this->mustRotate = true;
            $this->close(); // triggers rotation
        }

        parent::write($record);

        if (true === $this->mustRotate) {
            $this->close(); // triggers rotation
        }
    }

    /**
     * Rotates the files.
     */
    protected function rotate(): void
    {
        // update filename
        $this->url = $this->getTimedFilename();
        $this->nextRotation = $this->getNextRotation();

        $this->mustRotate = false;

        // skip GC of old logs if files are unlimited
        if (0 === $this->maxFiles) {
            return;
        }

        $logFiles = $this->findRotatedFiles();

        if ($this->maxFiles >= \count($logFiles)) {
            // no files to remove
            return;
        }

        // Sorting the files by name to remove the older ones
        usort($logFiles, function ($a, $b) {
            return strcmp($b, $a);
        });

        $basePath = self::normalizeDirectorySeparators(dirname($this->filename));

        foreach (\array_slice($logFiles, $this->maxFiles) as $file) {
            if (is_writable($file)) {
                // suppress errors here as unlink() might fail if two processes
                // are cleaning up/rotating at the same time
                set_error_handler(function (int $errno, string $errstr, string $errfile, int $errline): bool {
                    return true;
                });
                unlink($file);

                $dir = self::normalizeDirectorySeparators(dirname($file));
                while ($dir !== $basePath) {
                    $entries = scandir($dir);
                    if ($entries === false || \count(array_diff($entries, ['.', '..'])) > 0) {
                        break;
                    }

                    rmdir($dir);
                    $dir = self::normalizeDirectorySeparators(dirname($dir));
                }
                restore_error_handler();
            }
        }
    }

    protected function getTimedFilename(): string
    {
        $fileInfo = pathinfo($this->filename);
        $timedFilename = str_replace(
            ['{filename}', '{date}'],
            [$fileInfo['filename'], (new \DateTimeImmutable(timezone: $this->timezone))->format($this->dateFormat)],
            self::appendDirectorySeparator($fileInfo['dirname'] ?? '') . $this->filenameFormat
        );

        if (isset($fileInfo['extension'])) {
            $timedFilename .= '.'.$fileInfo['extension'];
        }

        return $timedFilename;
    }

    /**
     * Appends the trailing "/" to a pathinfo() dirname.
     *
     * pathinfo() reduces a wrapper URL with no subdirectory ("scheme://foo.log")
     * to a bare "scheme:", and PHP does not route the single-slash "scheme:/"
     * to the wrapper at all, so that case needs both slashes back. The pattern
     * deliberately requires two characters before the colon so a Windows drive
     * letter is left alone.
     */
    private static function appendDirectorySeparator(string $dirName): string
    {
        if (1 === preg_match('#^[a-zA-Z][a-zA-Z0-9+.-]+:$#', $dirName)) {
            return $dirName.'//';
        }

        return $dirName.'/';
    }

    /**
     * Rewrites Windows directory separators to "/".
     *
     * SPL joins directory entries with DIRECTORY_SEPARATOR, so on Windows the
     * iterator yields "C:\\logs\\foo.rot" while the pattern is built from a
     * pathinfo() dirname. Both sides have to be normalised, and only on Windows:
     * on POSIX a backslash is a legal filename character.
     */
    private static function normalizeDirectorySeparators(string $path): string
    {
        if ('\\' !== \DIRECTORY_SEPARATOR) {
            return $path;
        }

        return str_replace('\\', '/', $path);
    }

    /**
     * Finds the log files that previous rotations left behind.
     *
     * glob() cannot see through stream wrappers (Drupal's private:// for one),
     * so the pattern is translated to a regex and matched against a directory
     * walk instead. It still comes from getGlobPattern(), so overriding that
     * keeps changing which files the cleanup considers. Unlike glob(), symlinked
     * directories are not followed, which also rules out symlink loops.
     *
     * @return string[]
     */
    protected function findRotatedFiles(): array
    {
        $pattern = self::normalizeDirectorySeparators($this->getGlobPattern());

        // split off the literal directory prefix, so only the subtree the pattern
        // can actually reach has to be walked
        $slashPos = strrpos(substr($pattern, 0, strcspn($pattern, '*?[')), '/');
        $baseDir = false === $slashPos ? './' : substr($pattern, 0, $slashPos + 1);
        $relativePattern = false === $slashPos ? $pattern : substr($pattern, $slashPos + 1);

        $regex = '#^'.preg_quote($baseDir, '#').self::globToRegex($relativePattern).'$#';

        if (!is_dir($baseDir)) {
            return [];
        }

        try {
            // walking the subtree is only needed when the pattern itself nests
            $iterator = str_contains($relativePattern, '/')
                ? new \RecursiveIteratorIterator(
                    new \RecursiveDirectoryIterator($baseDir, \FilesystemIterator::SKIP_DOTS),
                    \RecursiveIteratorIterator::LEAVES_ONLY,
                    // an unreadable subdirectory skips that subtree instead of throwing
                    // all the way out of write()/close() and breaking logging
                    \RecursiveIteratorIterator::CATCH_GET_CHILD
                )
                : new \FilesystemIterator($baseDir, \FilesystemIterator::SKIP_DOTS);
        } catch (\UnexpectedValueException $e) {
            // is_dir() above can pass on a directory we are still not allowed to open
            return [];
        }

        $logFiles = [];
        foreach ($iterator as $path => $file) {
            if (!$file->isFile()) {
                continue;
            }

            $path = self::normalizeDirectorySeparators((string) $path);
            if (1 === preg_match($regex, $path)) {
                $logFiles[] = $path;
            }
        }

        return $logFiles;
    }

    /**
     * Translates a glob pattern into a regex matching the same paths.
     *
     * Wildcards stop at "/" like glob's do, so a pattern for one directory
     * cannot start matching nested files once the walk goes deeper.
     */
    private static function globToRegex(string $glob): string
    {
        $regex = '';

        for ($i = 0, $length = \strlen($glob); $i < $length; $i++) {
            $char = $glob[$i];

            if ('*' === $char) {
                $regex .= '[^/]*';
                continue;
            }

            if ('?' === $char) {
                $regex .= '[^/]';
                continue;
            }

            // a "]" directly after the opening bracket is literal, hence $i + 2
            if ('[' === $char && false !== ($end = strpos($glob, ']', $i + 2))) {
                $class = substr($glob, $i + 1, $end - $i - 1);
                $negate = '';
                if (str_starts_with($class, '!')) {
                    $negate = '^';
                    $class = substr($class, 1);
                }
                $regex .= '['.$negate.str_replace(['\\', ']', '^'], ['\\\\', '\\]', '\\^'], $class).']';
                $i = $end;
                continue;
            }

            $regex .= preg_quote($char, '#');
        }

        return $regex;
    }

    protected function getGlobPattern(): string
    {
        $fileInfo = pathinfo($this->filename);
        $glob = str_replace(
            ['{filename}', '{date}'],
            [$fileInfo['filename'], str_replace(
                ['Y', 'y', 'm', 'd', 'H'],
                ['[0-9][0-9][0-9][0-9]', '[0-9][0-9]', '[0-9][0-9]', '[0-9][0-9]', '[0-9][0-9]'],
                $this->dateFormat
            )],
            self::appendDirectorySeparator($fileInfo['dirname'] ?? '') . $this->filenameFormat
        );
        if (isset($fileInfo['extension'])) {
            $glob .= '.'.$fileInfo['extension'];
        }

        return $glob;
    }

    protected function setDateFormat(string $dateFormat): void
    {
        if (0 === preg_match('{^[Yy](([/_.-]?m)([/_.-]?d([/_.-]?H)?)?)?$}', $dateFormat)) {
            throw new InvalidArgumentException(
                'Invalid date format - format must be one of RotatingFileHandler::FILE_PER_HOUR ("Y-m-d-H"), '.
                'RotatingFileHandler::FILE_PER_DAY ("Y-m-d"), RotatingFileHandler::FILE_PER_MONTH ("Y-m") '.
                'or RotatingFileHandler::FILE_PER_YEAR ("Y"), or you can set one of the '.
                'date formats using slashes, underscores and/or dots instead of dashes.'
            );
        }

        $this->dateFormat = $dateFormat;
    }

    protected function getNextRotation(): \DateTimeImmutable
    {
        return match (str_replace(['/','_','.'], '-', $this->dateFormat)) {
            self::FILE_PER_MONTH => (new \DateTimeImmutable('first day of next month', $this->timezone))->setTime(0, 0, 0),
            self::FILE_PER_YEAR => (new \DateTimeImmutable('first day of January next year', $this->timezone))->setTime(0, 0, 0),
            default => (new \DateTimeImmutable('tomorrow', $this->timezone))->setTime(0, 0, 0),
        };
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\Level;
use Monolog\LogRecord;

/**
 * Stores to any socket - uses fsockopen() or pfsockopen().
 *
 * @author Pablo de Leon Belloc <pablolb@gmail.com>
 * @see    http://php.net/manual/en/function.fsockopen.php
 */
class SocketHandler extends AbstractProcessingHandler
{
    private string $connectionString;
    private float $connectionTimeout;
    /** @var resource|null */
    private $resource;
    private float $timeout;
    private float $writingTimeout;
    private int|null $lastSentBytes = null;
    private int|null $chunkSize;
    private bool $persistent;
    private int|null $errno = null;
    private string|null $errstr = null;
    private float|null $lastWritingAt = null;

    /**
     * @param string     $connectionString  Socket connection string
     * @param bool       $persistent        Flag to enable/disable persistent connections
     * @param float      $timeout           Socket timeout to wait until the request is being aborted
     * @param float      $writingTimeout    Socket timeout to wait until the request should've been sent/written
     * @param float|null $connectionTimeout Socket connect timeout to wait until the connection should've been
     *                                      established
     * @param int|null   $chunkSize         Sets the chunk size. Only has effect during connection in the writing cycle
     *
     * @throws \InvalidArgumentException If an invalid timeout value (less than 0) is passed.
     */
    public function __construct(
        string $connectionString,
        $level = Level::Debug,
        bool $bubble = true,
        bool $persistent = false,
        float $timeout = 0.0,
        float $writingTimeout = 10.0,
        ?float $connectionTimeout = null,
        ?int $chunkSize = null
    ) {
        parent::__construct($level, $bubble);
        $this->connectionString = $connectionString;

        if ($connectionTimeout !== null) {
            $this->validateTimeout($connectionTimeout);
        }

        $this->connectionTimeout = $connectionTimeout ?? (float) \ini_get('default_socket_timeout');
        $this->persistent = $persistent;
        $this->validateTimeout($timeout);
        $this->timeout = $timeout;
        $this->validateTimeout($writingTimeout);
        $this->writingTimeout = $writingTimeout;
        $this->chunkSize = $chunkSize;
    }

    /**
     * Connect (if necessary) and write to the socket
     *
     * @inheritDoc
     *
     * @throws \UnexpectedValueException
     * @throws \RuntimeException
     */
    protected function write(LogRecord $record): void
    {
        $this->connectIfNotConnected();
        $data = $this->generateDataStream($record);
        $this->writeToSocket($data);
    }

    /**
     * We will not close a PersistentSocket instance so it can be reused in other requests.
     */
    public function close(): void
    {
        if (!$this->isPersistent()) {
            $this->closeSocket();
        }
    }

    /**
     * Close socket, if open
     */
    public function closeSocket(): void
    {
        if (\is_resource($this->resource)) {
            fclose($this->resource);
            $this->resource = null;
        }
    }

    /**
     * Set socket connection to be persistent. It only has effect before the connection is initiated.
     *
     * @return $this
     */
    public function setPersistent(bool $persistent): self
    {
        $this->persistent = $persistent;

        return $this;
    }

    /**
     * Set connection timeout.  Only has effect before we connect.
     *
     * @see http://php.net/manual/en/function.fsockopen.php
     * @return $this
     */
    public function setConnectionTimeout(float $seconds): self
    {
        $this->validateTimeout($seconds);
        $this->connectionTimeout = $seconds;

        return $this;
    }

    /**
     * Set write timeout. Only has effect before we connect.
     *
     * @see http://php.net/manual/en/function.stream-set-timeout.php
     * @return $this
     */
    public function setTimeout(float $seconds): self
    {
        $this->validateTimeout($seconds);
        $this->timeout = $seconds;

        return $this;
    }

    /**
     * Set writing timeout. Only has effect during connection in the writing cycle.
     *
     * @param  float $seconds 0 for no timeout
     * @return $this
     */
    public function setWritingTimeout(float $seconds): self
    {
        $this->validateTimeout($seconds);
        $this->writingTimeout = $seconds;

        return $this;
    }

    /**
     * Set chunk size. Only has effect during connection in the writing cycle.
     *
     * @return $this
     */
    public function setChunkSize(int $bytes): self
    {
        $this->chunkSize = $bytes;

        return $this;
    }

    /**
     * Get current connection string
     */
    public function getConnectionString(): string
    {
        return $this->connectionString;
    }

    /**
     * Get persistent setting
     */
    public function isPersistent(): bool
    {
        return $this->persistent;
    }

    /**
     * Get current connection timeout setting
     */
    public function getConnectionTimeout(): float
    {
        return $this->connectionTimeout;
    }

    /**
     * Get current in-transfer timeout
     */
    public function getTimeout(): float
    {
        return $this->timeout;
    }

    /**
     * Get current local writing timeout
     */
    public function getWritingTimeout(): float
    {
        return $this->writingTimeout;
    }

    /**
     * Get current chunk size
     */
    public function getChunkSize(): ?int
    {
        return $this->chunkSize;
    }

    /**
     * Check to see if the socket is currently available.
     *
     * UDP might appear to be connected but might fail when writing.  See http://php.net/fsockopen for details.
     */
    public function isConnected(): bool
    {
        return \is_resource($this->resource)
            && !feof($this->resource);  // on TCP - other party can close connection.
    }

    /**
     * Wrapper to allow mocking
     *
     * @return resource|false
     */
    protected function pfsockopen()
    {
        return @pfsockopen($this->connectionString, -1, $this->errno, $this->errstr, $this->connectionTimeout);
    }

    /**
     * Wrapper to allow mocking
     *
     * @return resource|false
     */
    protected function fsockopen()
    {
        return @fsockopen($this->connectionString, -1, $this->errno, $this->errstr, $this->connectionTimeout);
    }

    /**
     * Wrapper to allow mocking
     *
     * @see http://php.net/manual/en/function.stream-set-timeout.php
     */
    protected function streamSetTimeout(): bool
    {
        $seconds = floor($this->timeout);
        $microseconds = round(($this->timeout - $seconds) * 1e6);

        if (!\is_resource($this->resource)) {
            throw new \LogicException('streamSetTimeout called but $this->resource is not a resource');
        }

        return stream_set_timeout($this->resource, (int) $seconds, (int) $microseconds);
    }

    /**
     * Wrapper to allow mocking
     *
     * @see http://php.net/manual/en/function.stream-set-chunk-size.php
     *
     * @return int|false
     */
    protected function streamSetChunkSize(): int|bool
    {
        if (!\is_resource($this->resource)) {
            throw new \LogicException('streamSetChunkSize called but $this->resource is not a resource');
        }

        if (null === $this->chunkSize) {
            throw new \LogicException('streamSetChunkSize called but $this->chunkSize is not set');
        }

        return stream_set_chunk_size($this->resource, $this->chunkSize);
    }

    /**
     * Wrapper to allow mocking
     *
     * @return int|false
     */
    protected function fwrite(string $data): int|bool
    {
        if (!\is_resource($this->resource)) {
            throw new \LogicException('fwrite called but $this->resource is not a resource');
        }

        return @fwrite($this->resource, $data);
    }

    /**
     * Wrapper to allow mocking
     *
     * @return mixed[]|bool
     */
    protected function streamGetMetadata(): array|bool
    {
        if (!\is_resource($this->resource)) {
            throw new \LogicException('streamGetMetadata called but $this->resource is not a resource');
        }

        return stream_get_meta_data($this->resource);
    }

    private function validateTimeout(float $value): void
    {
        if ($value < 0) {
            throw new \InvalidArgumentException("Timeout must be 0 or a positive float (got $value)");
        }
    }

    private function connectIfNotConnected(): void
    {
        if ($this->isConnected()) {
            return;
        }
        $this->connect();
    }

    protected function generateDataStream(LogRecord $record): string
    {
        return (string) $record->formatted;
    }

    /**
     * @return resource|null
     */
    protected function getResource()
    {
        return $this->resource;
    }

    private function connect(): void
    {
        $this->createSocketResource();
        $this->setSocketTimeout();
        $this->setStreamChunkSize();
    }

    private function createSocketResource(): void
    {
        if ($this->isPersistent()) {
            $resource = $this->pfsockopen();
        } else {
            $resource = $this->fsockopen();
        }
        if (\is_bool($resource)) {
            throw new \UnexpectedValueException("Failed connecting to $this->connectionString ($this->errno: $this->errstr)");
        }
        $this->resource = $resource;
    }

    private function setSocketTimeout(): void
    {
        if (!$this->streamSetTimeout()) {
            throw new \UnexpectedValueException("Failed setting timeout with stream_set_timeout()");
        }
    }

    private function setStreamChunkSize(): void
    {
        if (null !== $this->chunkSize && false === $this->streamSetChunkSize()) {
            throw new \UnexpectedValueException("Failed setting chunk size with stream_set_chunk_size()");
        }
    }

    private function writeToSocket(string $data): void
    {
        $length = \strlen($data);
        $sent = 0;
        $this->lastSentBytes = $sent;
        $this->lastWritingAt = microtime(true);
        while ($this->isConnected() && $sent < $length) {
            if (0 === $sent) {
                $chunk = $this->fwrite($data);
            } else {
                $chunk = $this->fwrite(substr($data, $sent));
            }
            if ($chunk === false) {
                throw new \RuntimeException("Could not write to socket");
            }
            $sent += $chunk;
            $socketInfo = $this->streamGetMetadata();
            if (\is_array($socketInfo) && (bool) $socketInfo['timed_out']) {
                throw new \RuntimeException("Write timed-out");
            }

            if ($this->writingIsTimedOut($sent)) {
                throw new \RuntimeException("Write timed-out, no data sent for `{$this->writingTimeout}` seconds, probably we got disconnected (sent $sent of $length)");
            }
        }
        if (!$this->isConnected() && $sent < $length) {
            throw new \RuntimeException("End-of-file reached, probably we got disconnected (sent $sent of $length)");
        }
    }

    private function writingIsTimedOut(int $sent): bool
    {
        // convert to ms
        if (0.0 === $this->writingTimeout) {
            return false;
        }

        if ($sent !== $this->lastSentBytes) {
            $this->lastWritingAt = microtime(true);
            $this->lastSentBytes = $sent;

            return false;
        } else {
            usleep(100);
        }

        if ((microtime(true) - (float) $this->lastWritingAt) >= $this->writingTimeout) {
            $this->closeSocket();

            return true;
        }

        return false;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Closure;
use Monolog\Level;
use Monolog\Logger;
use Monolog\ResettableInterface;
use Monolog\Formatter\FormatterInterface;
use Psr\Log\LogLevel;
use Monolog\LogRecord;

/**
 * Watches log records go by and expects to be fed at least $hunger of them before
 * the process ends. If it stays hungry until close() (and was not manually fed()),
 * the monster gets angry and emits a complaint record at $angerLevel to the nested
 * handler. Useful as a dead-man's switch for cron jobs / workers that should produce
 * a known amount of log activity before they finish, or to make sure that every code
 * path / request in the application at least logs some amount of records.
 *
 * Records carrying context are "cookies with chocolate chips" â€” when $wantsContextChips
 * is true, only records with non-empty context count toward feeding the monster.
 *
 * Thanks to Jonathan Wage for the idea https://x.com/seldaek/status/1491110384023277569
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 */
class LogMonsterHandler extends Handler implements ProcessableHandlerInterface, ResettableInterface, FormattableHandlerInterface
{
    use ProcessableHandlerTrait;

    /**
     * Handler or factory Closure($record, $this)
     *
     * @phpstan-var (Closure(LogRecord|null, HandlerInterface): HandlerInterface)|HandlerInterface
     */
    protected Closure|HandlerInterface $handler;

    protected int $hunger;

    protected Level $angerLevel;

    protected string $channel;

    protected bool $wantsContextChips;

    /** Number of records the monster has eaten so far */
    protected int $eaten = 0;

    /** Whether the monster has been manually fed and should stay quiet on close */
    protected bool $fed = false;

    /**
     * @phpstan-param (Closure(LogRecord|null, HandlerInterface): HandlerInterface)|HandlerInterface $handler
     *
     * @param Closure|HandlerInterface     $handler           Handler or factory Closure($record|null, $logMonsterHandler).
     * @param int                          $hunger            Number of log records the monster must eat before close() to stay satisfied.
     * @param int|string|Level|LogLevel::* $angerLevel        Level at which the monster logs its complaint when it goes hungry.
     * @param string                       $channel           Channel name stamped on the synthetic complaint record, note the record only gets sent to the given $handler, and will for example not go to the handlers configured for $channel in Symfony.
     * @param bool                         $wantsContextChips If true, only records carrying context count toward feeding the monster.
     *
     * @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $angerLevel
     */
    public function __construct(Closure|HandlerInterface $handler, int $hunger, int|string|Level $angerLevel = Level::Error, string $channel = 'log-monster', bool $wantsContextChips = false)
    {
        $this->handler = $handler;
        $this->hunger = $hunger;
        $this->channel = $channel;
        $this->wantsContextChips = $wantsContextChips;
        $this->angerLevel = Logger::toMonologLevel($angerLevel);
    }

    /**
     * @inheritDoc
     */
    public function isHandling(LogRecord $record): bool
    {
        return true;
    }

    /**
     * Manually feed the log monster so it does not complain on close (until reset() is called)
     */
    public function feed(): void
    {
        $this->fed = true;
    }

    /**
     * @inheritDoc
     */
    public function handle(LogRecord $record): bool
    {
        if (!$this->wantsContextChips || \count($record->context) > 0) {
            $this->eaten++;
        }

        // the monster only watches records go by, it never consumes them
        return false;
    }

    /**
     * @inheritDoc
     */
    public function close(): void
    {
        if (!$this->fed && $this->eaten < $this->hunger) {
            $record = new LogRecord(
                datetime: new \DateTimeImmutable('now'),
                channel: $this->channel,
                level: $this->angerLevel,
                message: 'Om nom nom... the log monster is hangry: it only ate '.$this->eaten.' of '.$this->hunger.' expected log records',
                context: ['eaten' => $this->eaten, 'hunger' => $this->hunger],
            );

            if (\count($this->processors) > 0) {
                $record = $this->processRecord($record);
            }

            $this->getHandler($record)->handle($record);
        }

        $this->getHandler()->close();
    }

    public function reset(): void
    {
        $this->fed = false;
        $this->eaten = 0;

        $this->resetProcessors();

        if ($this->getHandler() instanceof ResettableInterface) {
            $this->getHandler()->reset();
        }
    }

    /**
     * Return the nested handler
     *
     * If the handler was provided as a factory, this will trigger the handler's instantiation.
     */
    public function getHandler(?LogRecord $record = null): HandlerInterface
    {
        if (!$this->handler instanceof HandlerInterface) {
            $handler = ($this->handler)($record, $this);
            if (!$handler instanceof HandlerInterface) {
                throw new \RuntimeException("The factory Closure should return a HandlerInterface");
            }
            $this->handler = $handler;
        }

        return $this->handler;
    }

    /**
     * @inheritDoc
     */
    public function setFormatter(FormatterInterface $formatter): HandlerInterface
    {
        $handler = $this->getHandler();
        if ($handler instanceof FormattableHandlerInterface) {
            $handler->setFormatter($formatter);

            return $this;
        }

        throw new \UnexpectedValueException('The nested handler of type '.\get_class($handler).' does not support formatters.');
    }

    /**
     * @inheritDoc
     */
    public function getFormatter(): FormatterInterface
    {
        $handler = $this->getHandler();
        if ($handler instanceof FormattableHandlerInterface) {
            return $handler->getFormatter();
        }

        throw new \UnexpectedValueException('The nested handler of type '.\get_class($handler).' does not support formatters.');
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\Formatter\LineFormatter;
use Monolog\Formatter\FormatterInterface;
use Monolog\Level;
use Monolog\Utils;
use PhpConsole\Connector;
use PhpConsole\Handler as VendorPhpConsoleHandler;
use PhpConsole\Helper;
use Monolog\LogRecord;
use PhpConsole\Storage;

/**
 * Monolog handler for Google Chrome extension "PHP Console"
 *
 * Display PHP error/debug log messages in Google Chrome console and notification popups, executes PHP code remotely
 *
 * Usage:
 * 1. Install Google Chrome extension [now dead and removed from the chrome store]
 * 2. See overview https://github.com/barbushin/php-console#overview
 * 3. Install PHP Console library https://github.com/barbushin/php-console#installation
 * 4. Example (result will looks like http://i.hizliresim.com/vg3Pz4.png)
 *
 *      $logger = new \Monolog\Logger('all', array(new \Monolog\Handler\PHPConsoleHandler()));
 *      \Monolog\ErrorHandler::register($logger);
 *      echo $undefinedVar;
 *      $logger->debug('SELECT * FROM users', array('db', 'time' => 0.012));
 *      PC::debug($_SERVER); // PHP Console debugger for any type of vars
 *
 * @author Sergey Barbushin https://www.linkedin.com/in/barbushin
 * @phpstan-type Options array{
 *     enabled: bool,
 *     classesPartialsTraceIgnore: string[],
 *     debugTagsKeysInContext: array<int|string>,
 *     useOwnErrorsHandler: bool,
 *     useOwnExceptionsHandler: bool,
 *     sourcesBasePath: string|null,
 *     registerHelper: bool,
 *     serverEncoding: string|null,
 *     headersLimit: int|null,
 *     password: string|null,
 *     enableSslOnlyMode: bool,
 *     ipMasks: string[],
 *     enableEvalListener: bool,
 *     dumperDetectCallbacks: bool,
 *     dumperLevelLimit: int,
 *     dumperItemsCountLimit: int,
 *     dumperItemSizeLimit: int,
 *     dumperDumpSizeLimit: int,
 *     detectDumpTraceAndSource: bool,
 *     dataStorage: Storage|null
 * }
 * @phpstan-type InputOptions array{
 *     enabled?: bool,
 *     classesPartialsTraceIgnore?: string[],
 *     debugTagsKeysInContext?: array<int|string>,
 *     useOwnErrorsHandler?: bool,
 *     useOwnExceptionsHandler?: bool,
 *     sourcesBasePath?: string|null,
 *     registerHelper?: bool,
 *     serverEncoding?: string|null,
 *     headersLimit?: int|null,
 *     password?: string|null,
 *     enableSslOnlyMode?: bool,
 *     ipMasks?: string[],
 *     enableEvalListener?: bool,
 *     dumperDetectCallbacks?: bool,
 *     dumperLevelLimit?: int,
 *     dumperItemsCountLimit?: int,
 *     dumperItemSizeLimit?: int,
 *     dumperDumpSizeLimit?: int,
 *     detectDumpTraceAndSource?: bool,
 *     dataStorage?: Storage|null
 * }
 *
 * @deprecated Since 2.8.0 and 3.2.0, PHPConsole is abandoned and thus we will drop this handler in Monolog 4
 */
class PHPConsoleHandler extends AbstractProcessingHandler
{
    /**
     * @phpstan-var Options
     */
    private array $options = [
        'enabled' => true, // bool Is PHP Console server enabled
        'classesPartialsTraceIgnore' => ['Monolog\\'], // array Hide calls of classes started with...
        'debugTagsKeysInContext' => [0, 'tag'], // bool Is PHP Console server enabled
        'useOwnErrorsHandler' => false, // bool Enable errors handling
        'useOwnExceptionsHandler' => false, // bool Enable exceptions handling
        'sourcesBasePath' => null, // string Base path of all project sources to strip in errors source paths
        'registerHelper' => true, // bool Register PhpConsole\Helper that allows short debug calls like PC::debug($var, 'ta.g.s')
        'serverEncoding' => null, // string|null Server internal encoding
        'headersLimit' => null, // int|null Set headers size limit for your web-server
        'password' => null, // string|null Protect PHP Console connection by password
        'enableSslOnlyMode' => false, // bool Force connection by SSL for clients with PHP Console installed
        'ipMasks' => [], // array Set IP masks of clients that will be allowed to connect to PHP Console: array('192.168.*.*', '127.0.0.1')
        'enableEvalListener' => false, // bool Enable eval request to be handled by eval dispatcher(if enabled, 'password' option is also required)
        'dumperDetectCallbacks' => false, // bool Convert callback items in dumper vars to (callback SomeClass::someMethod) strings
        'dumperLevelLimit' => 5, // int Maximum dumped vars array or object nested dump level
        'dumperItemsCountLimit' => 100, // int Maximum dumped var same level array items or object properties number
        'dumperItemSizeLimit' => 5000, // int Maximum length of any string or dumped array item
        'dumperDumpSizeLimit' => 500000, // int Maximum approximate size of dumped vars result formatted in JSON
        'detectDumpTraceAndSource' => false, // bool Autodetect and append trace data to debug
        'dataStorage' => null, // \PhpConsole\Storage|null Fixes problem with custom $_SESSION handler (see https://github.com/barbushin/php-console#troubleshooting-with-_session-handler-overridden-in-some-frameworks)
    ];

    private Connector $connector;

    /**
     * @param  array<string, mixed> $options   See \Monolog\Handler\PHPConsoleHandler::$options for more details
     * @param  Connector|null       $connector Instance of \PhpConsole\Connector class (optional)
     * @throws \RuntimeException
     * @phpstan-param InputOptions $options
     */
    public function __construct(array $options = [], ?Connector $connector = null, int|string|Level $level = Level::Debug, bool $bubble = true)
    {
        if (!class_exists('PhpConsole\Connector')) {
            throw new \RuntimeException('PHP Console library not found. See https://github.com/barbushin/php-console#installation');
        }
        parent::__construct($level, $bubble);
        $this->options = $this->initOptions($options);
        $this->connector = $this->initConnector($connector);
    }

    /**
     * @param  array<string, mixed> $options
     * @return array<string, mixed>
     *
     * @phpstan-param InputOptions $options
     * @phpstan-return Options
     */
    private function initOptions(array $options): array
    {
        $wrongOptions = array_diff(array_keys($options), array_keys($this->options));
        if (\count($wrongOptions) > 0) {
            throw new \RuntimeException('Unknown options: ' . implode(', ', $wrongOptions));
        }

        return array_replace($this->options, $options);
    }

    private function initConnector(?Connector $connector = null): Connector
    {
        if (null === $connector) {
            if ($this->options['dataStorage'] instanceof Storage) {
                Connector::setPostponeStorage($this->options['dataStorage']);
            }
            $connector = Connector::getInstance();
        }

        if ($this->options['registerHelper'] && !Helper::isRegistered()) {
            Helper::register();
        }

        if ($this->options['enabled'] && $connector->isActiveClient()) {
            if ($this->options['useOwnErrorsHandler'] || $this->options['useOwnExceptionsHandler']) {
                $handler = VendorPhpConsoleHandler::getInstance();
                $handler->setHandleErrors($this->options['useOwnErrorsHandler']);
                $handler->setHandleExceptions($this->options['useOwnExceptionsHandler']);
                $handler->start();
            }
            if (null !== $this->options['sourcesBasePath']) {
                $connector->setSourcesBasePath($this->options['sourcesBasePath']);
            }
            if (null !== $this->options['serverEncoding']) {
                $connector->setServerEncoding($this->options['serverEncoding']);
            }
            if (null !== $this->options['password']) {
                $connector->setPassword($this->options['password']);
            }
            if ($this->options['enableSslOnlyMode']) {
                $connector->enableSslOnlyMode();
            }
            if (\count($this->options['ipMasks']) > 0) {
                $connector->setAllowedIpMasks($this->options['ipMasks']);
            }
            if (null !== $this->options['headersLimit'] && $this->options['headersLimit'] > 0) {
                $connector->setHeadersLimit($this->options['headersLimit']);
            }
            if ($this->options['detectDumpTraceAndSource']) {
                $connector->getDebugDispatcher()->detectTraceAndSource = true;
            }
            $dumper = $connector->getDumper();
            $dumper->levelLimit = $this->options['dumperLevelLimit'];
            $dumper->itemsCountLimit = $this->options['dumperItemsCountLimit'];
            $dumper->itemSizeLimit = $this->options['dumperItemSizeLimit'];
            $dumper->dumpSizeLimit = $this->options['dumperDumpSizeLimit'];
            $dumper->detectCallbacks = $this->options['dumperDetectCallbacks'];
            if ($this->options['enableEvalListener']) {
                $connector->startEvalRequestsListener();
            }
        }

        return $connector;
    }

    public function getConnector(): Connector
    {
        return $this->connector;
    }

    /**
     * @return array<string, mixed>
     */
    public function getOptions(): array
    {
        return $this->options;
    }

    public function handle(LogRecord $record): bool
    {
        if ($this->options['enabled'] && $this->connector->isActiveClient()) {
            return parent::handle($record);
        }

        return !$this->bubble;
    }

    /**
     * Writes the record down to the log of the implementing handler
     */
    protected function write(LogRecord $record): void
    {
        if ($record->level->isLowerThan(Level::Notice)) {
            $this->handleDebugRecord($record);
        } elseif (isset($record->context['exception']) && $record->context['exception'] instanceof \Throwable) {
            $this->handleExceptionRecord($record);
        } else {
            $this->handleErrorRecord($record);
        }
    }

    private function handleDebugRecord(LogRecord $record): void
    {
        [$tags, $filteredContext] = $this->getRecordTags($record);
        $message = $record->message;
        if (\count($filteredContext) > 0) {
            $message .= ' ' . Utils::jsonEncode($this->connector->getDumper()->dump(array_filter($filteredContext)), null, true);
        }
        $this->connector->getDebugDispatcher()->dispatchDebug($message, $tags, $this->options['classesPartialsTraceIgnore']);
    }

    private function handleExceptionRecord(LogRecord $record): void
    {
        $this->connector->getErrorsDispatcher()->dispatchException($record->context['exception']);
    }

    private function handleErrorRecord(LogRecord $record): void
    {
        $context = $record->context;

        $this->connector->getErrorsDispatcher()->dispatchError(
            $context['code'] ?? null,
            $context['message'] ?? $record->message,
            $context['file'] ?? null,
            $context['line'] ?? null,
            $this->options['classesPartialsTraceIgnore']
        );
    }

    /**
     * @return array{string, mixed[]}
     */
    private function getRecordTags(LogRecord $record): array
    {
        $tags = null;
        $filteredContext = [];
        if ($record->context !== []) {
            $filteredContext = $record->context;
            foreach ($this->options['debugTagsKeysInContext'] as $key) {
                if (isset($filteredContext[$key])) {
                    $tags = $filteredContext[$key];
                    if ($key === 0) {
                        array_shift($filteredContext);
                    } else {
                        unset($filteredContext[$key]);
                    }
                    break;
                }
            }
        }

        return [$tags ?? $record->level->toPsrLogLevel(), $filteredContext];
    }

    /**
     * @inheritDoc
     */
    protected function getDefaultFormatter(): FormatterInterface
    {
        return new LineFormatter('%message%');
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

/**
 * Base Handler class providing basic close() support as well as handleBatch
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 */
abstract class Handler implements HandlerInterface
{
    /**
     * @inheritDoc
     */
    public function handleBatch(array $records): void
    {
        foreach ($records as $record) {
            $this->handle($record);
        }
    }

    /**
     * @inheritDoc
     */
    public function close(): void
    {
    }

    public function __destruct()
    {
        try {
            $this->close();
        } catch (\Throwable $e) {
            // do nothing
        }
    }

    public function __serialize(): array
    {
        $this->close();

        return (array) $this;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler\Slack;

use Monolog\Level;
use Monolog\Utils;
use Monolog\Formatter\NormalizerFormatter;
use Monolog\Formatter\FormatterInterface;
use Monolog\LogRecord;

/**
 * Slack record utility helping to log to Slack webhooks or API.
 *
 * @author Greg Kedzierski <greg@gregkedzierski.com>
 * @author Haralan Dobrev <hkdobrev@gmail.com>
 * @see    https://api.slack.com/incoming-webhooks
 * @see    https://api.slack.com/docs/message-attachments
 */
class SlackRecord
{
    public const COLOR_DANGER = 'danger';

    public const COLOR_WARNING = 'warning';

    public const COLOR_GOOD = 'good';

    public const COLOR_DEFAULT = '#e3e4e6';

    /**
     * Slack channel (encoded ID or name)
     */
    private string|null $channel;

    /**
     * Name of a bot
     */
    private string|null $username;

    /**
     * User icon e.g. 'ghost', 'http://example.com/user.png'
     */
    private string|null $userIcon;

    /**
     * Whether the message should be added to Slack as attachment (plain text otherwise)
     */
    private bool $useAttachment;

    /**
     * Whether the the context/extra messages added to Slack as attachments are in a short style
     */
    private bool $useShortAttachment;

    /**
     * Whether the attachment should include context and extra data
     */
    private bool $includeContextAndExtra;

    /**
     * Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2']
     * @var string[]
     */
    private array $excludeFields;

    private FormatterInterface|null $formatter;

    private NormalizerFormatter $normalizerFormatter;

    /**
     * @param string[] $excludeFields
     */
    public function __construct(
        ?string $channel = null,
        ?string $username = null,
        bool $useAttachment = true,
        ?string $userIcon = null,
        bool $useShortAttachment = false,
        bool $includeContextAndExtra = false,
        array $excludeFields = [],
        FormatterInterface|null $formatter = null
    ) {
        $this
            ->setChannel($channel)
            ->setUsername($username)
            ->useAttachment($useAttachment)
            ->setUserIcon($userIcon)
            ->useShortAttachment($useShortAttachment)
            ->includeContextAndExtra($includeContextAndExtra)
            ->excludeFields($excludeFields)
            ->setFormatter($formatter);

        if ($this->includeContextAndExtra) {
            $this->normalizerFormatter = new NormalizerFormatter();
        }
    }

    /**
     * Returns required data in format that Slack
     * is expecting.
     *
     * @phpstan-return mixed[]
     */
    public function getSlackData(LogRecord $record): array
    {
        $dataArray = [];

        if ($this->username !== null) {
            $dataArray['username'] = $this->username;
        }

        if ($this->channel !== null) {
            $dataArray['channel'] = $this->channel;
        }

        if ($this->formatter !== null && !$this->useAttachment) {
            $message = $this->formatter->format($record);
        } else {
            $message = $record->message;
        }

        $recordData = $this->removeExcludedFields($record);

        if ($this->useAttachment) {
            $attachment = [
                'fallback'  => $message,
                'text'      => $message,
                'color'     => $this->getAttachmentColor($record->level),
                'fields'    => [],
                'mrkdwn_in' => ['fields'],
                'ts'        => $recordData['datetime']->getTimestamp(),
                'footer'      => $this->username,
                'footer_icon' => $this->userIcon,
            ];

            if ($this->useShortAttachment) {
                $attachment['title'] = $recordData['level_name'];
            } else {
                $attachment['title'] = 'Message';
                $attachment['fields'][] = $this->generateAttachmentField('Level', $recordData['level_name']);
            }

            if ($this->includeContextAndExtra) {
                foreach (['extra', 'context'] as $key) {
                    if (!isset($recordData[$key]) || \count($recordData[$key]) === 0) {
                        continue;
                    }

                    if ($this->useShortAttachment) {
                        $attachment['fields'][] = $this->generateAttachmentField(
                            $key,
                            $recordData[$key]
                        );
                    } else {
                        // Add all extra fields as individual fields in attachment
                        $attachment['fields'] = array_merge(
                            $attachment['fields'],
                            $this->generateAttachmentFields($recordData[$key])
                        );
                    }
                }
            }

            $dataArray['attachments'] = [$attachment];
        } else {
            $dataArray['text'] = $message;
        }

        if ($this->userIcon !== null) {
            if (false !== ($iconUrl = filter_var($this->userIcon, FILTER_VALIDATE_URL))) {
                $dataArray['icon_url'] = $iconUrl;
            } else {
                $dataArray['icon_emoji'] = ":{$this->userIcon}:";
            }
        }

        return $dataArray;
    }

    /**
     * Returns a Slack message attachment color associated with
     * provided level.
     */
    public function getAttachmentColor(Level $level): string
    {
        return match ($level) {
            Level::Error, Level::Critical, Level::Alert, Level::Emergency => static::COLOR_DANGER,
            Level::Warning => static::COLOR_WARNING,
            Level::Info, Level::Notice => static::COLOR_GOOD,
            Level::Debug => static::COLOR_DEFAULT
        };
    }

    /**
     * Stringifies an array of key/value pairs to be used in attachment fields
     *
     * @param mixed[] $fields
     */
    public function stringify(array $fields): string
    {
        /** @var array<array<mixed>|bool|float|int|string|null> $normalized */
        $normalized = $this->normalizerFormatter->normalizeValue($fields);

        $hasSecondDimension = \count(array_filter($normalized, 'is_array')) > 0;
        $hasOnlyNonNumericKeys = \count(array_filter(array_keys($normalized), 'is_numeric')) === 0;

        return $hasSecondDimension || $hasOnlyNonNumericKeys
            ? Utils::jsonEncode($normalized, JSON_PRETTY_PRINT|Utils::DEFAULT_JSON_FLAGS)
            : Utils::jsonEncode($normalized, Utils::DEFAULT_JSON_FLAGS);
    }

    /**
     * Channel used by the bot when posting
     *
     * @param  ?string $channel
     * @return $this
     */
    public function setChannel(?string $channel = null): self
    {
        $this->channel = $channel;

        return $this;
    }

    /**
     * Username used by the bot when posting
     *
     * @param  ?string $username
     * @return $this
     */
    public function setUsername(?string $username = null): self
    {
        $this->username = $username;

        return $this;
    }

    /**
     * @return $this
     */
    public function useAttachment(bool $useAttachment = true): self
    {
        $this->useAttachment = $useAttachment;

        return $this;
    }

    /**
     * @return $this
     */
    public function setUserIcon(?string $userIcon = null): self
    {
        $this->userIcon = $userIcon;

        if (\is_string($userIcon)) {
            $this->userIcon = trim($userIcon, ':');
        }

        return $this;
    }

    /**
     * @return $this
     */
    public function useShortAttachment(bool $useShortAttachment = false): self
    {
        $this->useShortAttachment = $useShortAttachment;

        return $this;
    }

    /**
     * @return $this
     */
    public function includeContextAndExtra(bool $includeContextAndExtra = false): self
    {
        $this->includeContextAndExtra = $includeContextAndExtra;

        if ($this->includeContextAndExtra) {
            $this->normalizerFormatter = new NormalizerFormatter();
        }

        return $this;
    }

    /**
     * @param  string[] $excludeFields
     * @return $this
     */
    public function excludeFields(array $excludeFields = []): self
    {
        $this->excludeFields = $excludeFields;

        return $this;
    }

    /**
     * @return $this
     */
    public function setFormatter(?FormatterInterface $formatter = null): self
    {
        $this->formatter = $formatter;

        return $this;
    }

    /**
     * Generates attachment field
     *
     * @param string|mixed[] $value
     *
     * @return array{title: string, value: string, short: false}
     */
    private function generateAttachmentField(string $title, $value): array
    {
        $value = \is_array($value)
            ? sprintf('```%s```', substr($this->stringify($value), 0, 1990))
            : $value;

        return [
            'title' => ucfirst($title),
            'value' => $value,
            'short' => false,
        ];
    }

    /**
     * Generates a collection of attachment fields from array
     *
     * @param mixed[] $data
     *
     * @return array<array{title: string, value: string, short: false}>
     */
    private function generateAttachmentFields(array $data): array
    {
        /** @var array<array<mixed>|string> $normalized */
        $normalized = $this->normalizerFormatter->normalizeValue($data);

        $fields = [];
        foreach ($normalized as $key => $value) {
            $fields[] = $this->generateAttachmentField((string) $key, $value);
        }

        return $fields;
    }

    /**
     * Get a copy of record with fields excluded according to $this->excludeFields
     *
     * @return mixed[]
     */
    private function removeExcludedFields(LogRecord $record): array
    {
        $recordData = $record->toArray();
        foreach ($this->excludeFields as $field) {
            $keys = explode('.', $field);
            $node = &$recordData;
            $lastKey = end($keys);
            foreach ($keys as $key) {
                if (!isset($node[$key])) {
                    break;
                }
                if ($lastKey === $key) {
                    unset($node[$key]);
                    break;
                }
                $node = &$node[$key];
            }
        }

        return $recordData;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\Level;
use Monolog\Utils;
use Monolog\Formatter\NormalizerFormatter;
use Monolog\Formatter\FormatterInterface;
use Monolog\LogRecord;

/**
 * Class to record a log on a NewRelic application.
 * Enabling New Relic High Security mode may prevent capture of useful information.
 *
 * This handler requires a NormalizerFormatter to function and expects an array in $record->formatted
 *
 * @see https://docs.newrelic.com/docs/agents/php-agent
 * @see https://docs.newrelic.com/docs/accounts-partnerships/accounts/security/high-security
 */
class NewRelicHandler extends AbstractProcessingHandler
{
    /**
     * @inheritDoc
     */
    public function __construct(
        int|string|Level $level = Level::Error,
        bool $bubble = true,

        /**
         * Name of the New Relic application that will receive logs from this handler.
         */
        protected string|null $appName = null,

        /**
         * Some context and extra data is passed into the handler as arrays of values. Do we send them as is
         * (useful if we are using the API), or explode them for display on the NewRelic RPM website?
         */
        protected bool $explodeArrays = false,

        /**
         * Name of the current transaction
         */
        protected string|null $transactionName = null
    ) {
        parent::__construct($level, $bubble);
    }

    /**
     * @inheritDoc
     */
    protected function write(LogRecord $record): void
    {
        if (!$this->isNewRelicEnabled()) {
            throw new MissingExtensionException('The newrelic PHP extension is required to use the NewRelicHandler');
        }

        if (null !== ($appName = $this->getAppName($record->context))) {
            $this->setNewRelicAppName($appName);
        }

        if (null !== ($transactionName = $this->getTransactionName($record->context))) {
            $this->setNewRelicTransactionName($transactionName);
            unset($record->formatted['context']['transaction_name']);
        }

        if (isset($record->context['exception']) && $record->context['exception'] instanceof \Throwable) {
            newrelic_notice_error($record->message, $record->context['exception']);
            unset($record->formatted['context']['exception']);
        } else {
            newrelic_notice_error($record->message);
        }

        if (isset($record->formatted['context']) && \is_array($record->formatted['context'])) {
            foreach ($record->formatted['context'] as $key => $parameter) {
                if (\is_array($parameter) && $this->explodeArrays) {
                    foreach ($parameter as $paramKey => $paramValue) {
                        $this->setNewRelicParameter('context_' . $key . '_' . $paramKey, $paramValue);
                    }
                } else {
                    $this->setNewRelicParameter('context_' . $key, $parameter);
                }
            }
        }

        if (isset($record->formatted['extra']) && \is_array($record->formatted['extra'])) {
            foreach ($record->formatted['extra'] as $key => $parameter) {
                if (\is_array($parameter) && $this->explodeArrays) {
                    foreach ($parameter as $paramKey => $paramValue) {
                        $this->setNewRelicParameter('extra_' . $key . '_' . $paramKey, $paramValue);
                    }
                } else {
                    $this->setNewRelicParameter('extra_' . $key, $parameter);
                }
            }
        }
    }

    /**
     * Checks whether the NewRelic extension is enabled in the system.
     */
    protected function isNewRelicEnabled(): bool
    {
        return \extension_loaded('newrelic');
    }

    /**
     * Returns the appname where this log should be sent. Each log can override the default appname, set in this
     * handler's constructor, by providing the appname in it's context.
     *
     * @param mixed[] $context
     */
    protected function getAppName(array $context): ?string
    {
        if (isset($context['appname'])) {
            return $context['appname'];
        }

        return $this->appName;
    }

    /**
     * Returns the name of the current transaction. Each log can override the default transaction name, set in this
     * handler's constructor, by providing the transaction_name in it's context
     *
     * @param mixed[] $context
     */
    protected function getTransactionName(array $context): ?string
    {
        if (isset($context['transaction_name'])) {
            return $context['transaction_name'];
        }

        return $this->transactionName;
    }

    /**
     * Sets the NewRelic application that should receive this log.
     */
    protected function setNewRelicAppName(string $appName): void
    {
        newrelic_set_appname($appName);
    }

    /**
     * Overwrites the name of the current transaction
     */
    protected function setNewRelicTransactionName(string $transactionName): void
    {
        newrelic_name_transaction($transactionName);
    }

    /**
     * @param mixed $value
     */
    protected function setNewRelicParameter(string $key, $value): void
    {
        if (null === $value || \is_scalar($value)) {
            newrelic_add_custom_parameter($key, $value);
        } else {
            newrelic_add_custom_parameter($key, Utils::jsonEncode($value, null, true));
        }
    }

    /**
     * @inheritDoc
     */
    protected function getDefaultFormatter(): FormatterInterface
    {
        return new NormalizerFormatter();
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\Level;
use Monolog\Utils;
use Monolog\LogRecord;

/**
 * Logs to Cube.
 *
 * @link https://github.com/square/cube/wiki
 * @author Wan Chen <kami@kamisama.me>
 * @deprecated Since 2.8.0 and 3.2.0, Cube appears abandoned and thus we will drop this handler in Monolog 4
 */
class CubeHandler extends AbstractProcessingHandler
{
    private ?\Socket $udpConnection = null;
    private ?\CurlHandle $httpConnection = null;
    private string $scheme;
    private string $host;
    private int $port;
    /** @var string[] */
    private array $acceptedSchemes = ['http', 'udp'];

    /**
     * Create a Cube handler
     *
     * @throws \UnexpectedValueException when given url is not a valid url.
     *                                   A valid url must consist of three parts : protocol://host:port
     *                                   Only valid protocols used by Cube are http and udp
     */
    public function __construct(string $url, int|string|Level $level = Level::Debug, bool $bubble = true)
    {
        $urlInfo = parse_url($url);

        if ($urlInfo === false || !isset($urlInfo['scheme'], $urlInfo['host'], $urlInfo['port'])) {
            throw new \UnexpectedValueException('URL "'.$url.'" is not valid');
        }

        if (!\in_array($urlInfo['scheme'], $this->acceptedSchemes, true)) {
            throw new \UnexpectedValueException(
                'Invalid protocol (' . $urlInfo['scheme']  . ').'
                . ' Valid options are ' . implode(', ', $this->acceptedSchemes)
            );
        }

        $this->scheme = $urlInfo['scheme'];
        $this->host = $urlInfo['host'];
        $this->port = $urlInfo['port'];

        parent::__construct($level, $bubble);
    }

    /**
     * Establish a connection to an UDP socket
     *
     * @throws \LogicException           when unable to connect to the socket
     * @throws MissingExtensionException when there is no socket extension
     */
    protected function connectUdp(): void
    {
        if (!\extension_loaded('sockets')) {
            throw new MissingExtensionException('The sockets extension is required to use udp URLs with the CubeHandler');
        }

        $udpConnection = socket_create(AF_INET, SOCK_DGRAM, 0);
        if (false === $udpConnection) {
            throw new \LogicException('Unable to create a socket');
        }

        $this->udpConnection = $udpConnection;
        if (!socket_connect($this->udpConnection, $this->host, $this->port)) {
            throw new \LogicException('Unable to connect to the socket at ' . $this->host . ':' . $this->port);
        }
    }

    /**
     * Establish a connection to an http server
     *
     * @throws \LogicException           when unable to connect to the socket
     * @throws MissingExtensionException when no curl extension
     */
    protected function connectHttp(): void
    {
        if (!\extension_loaded('curl')) {
            throw new MissingExtensionException('The curl extension is required to use http URLs with the CubeHandler');
        }

        $httpConnection = curl_init('http://'.$this->host.':'.$this->port.'/1.0/event/put');
        if (false === $httpConnection) {
            throw new \LogicException('Unable to connect to ' . $this->host . ':' . $this->port);
        }

        $this->httpConnection = $httpConnection;
        curl_setopt($this->httpConnection, CURLOPT_CUSTOMREQUEST, "POST");
        curl_setopt($this->httpConnection, CURLOPT_RETURNTRANSFER, true);
    }

    /**
     * @inheritDoc
     */
    protected function write(LogRecord $record): void
    {
        $date = $record->datetime;

        $data = ['time' => $date->format('Y-m-d\TH:i:s.uO')];
        $context = $record->context;

        if (isset($context['type'])) {
            $data['type'] = $context['type'];
            unset($context['type']);
        } else {
            $data['type'] = $record->channel;
        }

        $data['data'] = $context;
        $data['data']['level'] = $record->level;

        if ($this->scheme === 'http') {
            $this->writeHttp(Utils::jsonEncode($data));
        } else {
            $this->writeUdp(Utils::jsonEncode($data));
        }
    }

    private function writeUdp(string $data): void
    {
        if (null === $this->udpConnection) {
            $this->connectUdp();
        }

        if (null === $this->udpConnection) {
            throw new \LogicException('No UDP socket could be opened');
        }

        socket_send($this->udpConnection, $data, \strlen($data), 0);
    }

    private function writeHttp(string $data): void
    {
        if (null === $this->httpConnection) {
            $this->connectHttp();
        }

        if (null === $this->httpConnection) {
            throw new \LogicException('No connection could be established');
        }

        curl_setopt($this->httpConnection, CURLOPT_POSTFIELDS, '['.$data.']');
        curl_setopt($this->httpConnection, CURLOPT_HTTPHEADER, [
            'Content-Type: application/json',
            'Content-Length: ' . \strlen('['.$data.']'),
        ]);

        Curl\Util::execute($this->httpConnection, 5);
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Aws\Sdk;
use Aws\DynamoDb\DynamoDbClient;
use Monolog\Formatter\FormatterInterface;
use Aws\DynamoDb\Marshaler;
use Monolog\Formatter\ScalarFormatter;
use Monolog\Level;
use Monolog\LogRecord;

/**
 * Amazon DynamoDB handler (http://aws.amazon.com/dynamodb/)
 *
 * @link https://github.com/aws/aws-sdk-php/
 * @author Andrew Lawson <adlawson@gmail.com>
 */
class DynamoDbHandler extends AbstractProcessingHandler
{
    public const DATE_FORMAT = 'Y-m-d\TH:i:s.uO';

    protected DynamoDbClient $client;

    protected string $table;

    protected Marshaler $marshaler;

    public function __construct(DynamoDbClient $client, string $table, int|string|Level $level = Level::Debug, bool $bubble = true)
    {
        $this->marshaler = new Marshaler;

        $this->client = $client;
        $this->table = $table;

        parent::__construct($level, $bubble);
    }

    /**
     * @inheritDoc
     */
    protected function write(LogRecord $record): void
    {
        $filtered = $this->filterEmptyFields($record->formatted);
        $formatted = $this->marshaler->marshalItem($filtered);

        $this->client->putItem([
            'TableName' => $this->table,
            'Item' => $formatted,
        ]);
    }

    /**
     * @param  mixed[] $record
     * @return mixed[]
     */
    protected function filterEmptyFields(array $record): array
    {
        return array_filter($record, function ($value) {
            return [] !== $value;
        });
    }

    /**
     * @inheritDoc
     */
    protected function getDefaultFormatter(): FormatterInterface
    {
        return new ScalarFormatter(self::DATE_FORMAT);
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Closure;
use Monolog\Level;
use Monolog\LogRecord;
use Monolog\Utils;
use Monolog\Formatter\FormatterInterface;
use Monolog\Formatter\LineFormatter;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mailer\Transport\TransportInterface;
use Symfony\Component\Mime\Email;

/**
 * SymfonyMailerHandler uses Symfony's Mailer component to send the emails
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 */
class SymfonyMailerHandler extends MailHandler
{
    protected MailerInterface|TransportInterface $mailer;
    /** @var Email|Closure(string, LogRecord[]): Email */
    private Email|Closure $emailTemplate;

    /**
     * @phpstan-param Email|Closure(string, LogRecord[]): Email $email
     *
     * @param MailerInterface|TransportInterface $mailer The mailer to use
     * @param Closure|Email                      $email  An email template, the subject/body will be replaced
     */
    public function __construct($mailer, Email|Closure $email, int|string|Level $level = Level::Error, bool $bubble = true)
    {
        parent::__construct($level, $bubble);

        $this->mailer = $mailer;
        $this->emailTemplate = $email;
    }

    /**
     * {@inheritDoc}
     */
    protected function send(string $content, array $records): void
    {
        $this->mailer->send($this->buildMessage($content, $records));
    }

    /**
     * Gets the formatter for the Swift_Message subject.
     *
     * @param string|null $format The format of the subject
     */
    protected function getSubjectFormatter(?string $format): FormatterInterface
    {
        return new LineFormatter($format);
    }

    /**
     * Creates instance of Email to be sent
     *
     * @param string      $content formatted email body to be sent
     * @param LogRecord[] $records Log records that formed the content
     */
    protected function buildMessage(string $content, array $records): Email
    {
        $message = null;
        if ($this->emailTemplate instanceof Email) {
            $message = clone $this->emailTemplate;
        } elseif (\is_callable($this->emailTemplate)) {
            $message = ($this->emailTemplate)($content, $records);
        }

        if (!$message instanceof Email) {
            $record = reset($records);

            throw new \InvalidArgumentException('Could not resolve message as instance of Email or a callable returning it' . ($record instanceof LogRecord ? Utils::getRecordMessageForException($record) : ''));
        }

        if (\count($records) > 0) {
            $subjectFormatter = $this->getSubjectFormatter($message->getSubject());
            $message->subject($subjectFormatter->format($this->getHighestRecord($records)));
        }

        if ($this->isHtmlBody($content)) {
            if (null !== ($charset = $message->getHtmlCharset())) {
                $message->html($content, $charset);
            } else {
                $message->html($content);
            }
        } else {
            if (null !== ($charset = $message->getTextCharset())) {
                $message->text($content, $charset);
            } else {
                $message->text($content);
            }
        }

        return $message->date(new \DateTimeImmutable());
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\Formatter\WildfireFormatter;
use Monolog\Formatter\FormatterInterface;
use Monolog\LogRecord;

/**
 * Simple FirePHP Handler (http://www.firephp.org/), which uses the Wildfire protocol.
 *
 * @author Eric Clemmons (@ericclemmons) <eric@uxdriven.com>
 */
class FirePHPHandler extends AbstractProcessingHandler
{
    use WebRequestRecognizerTrait;

    /**
     * WildFire JSON header message format
     */
    protected const PROTOCOL_URI = 'http://meta.wildfirehq.org/Protocol/JsonStream/0.2';

    /**
     * FirePHP structure for parsing messages & their presentation
     */
    protected const STRUCTURE_URI = 'http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1';

    /**
     * Must reference a "known" plugin, otherwise headers won't display in FirePHP
     */
    protected const PLUGIN_URI = 'http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.3';

    /**
     * Header prefix for Wildfire to recognize & parse headers
     */
    protected const HEADER_PREFIX = 'X-Wf';

    /**
     * Whether or not Wildfire vendor-specific headers have been generated & sent yet
     */
    protected static bool $initialized = false;

    /**
     * Shared static message index between potentially multiple handlers
     */
    protected static int $messageIndex = 1;

    protected static bool $sendHeaders = true;

    /**
     * Base header creation function used by init headers & record headers
     *
     * @param array<int|string> $meta    Wildfire Plugin, Protocol & Structure Indexes
     * @param string            $message Log message
     *
     * @return array<string, string> Complete header string ready for the client as key and message as value
     *
     * @phpstan-return non-empty-array<string, string>
     */
    protected function createHeader(array $meta, string $message): array
    {
        $header = sprintf('%s-%s', static::HEADER_PREFIX, join('-', $meta));

        return [$header => $message];
    }

    /**
     * Creates message header from record
     *
     * @return array<string, string>
     *
     * @phpstan-return non-empty-array<string, string>
     *
     * @see createHeader()
     */
    protected function createRecordHeader(LogRecord $record): array
    {
        // Wildfire is extensible to support multiple protocols & plugins in a single request,
        // but we're not taking advantage of that (yet), so we're using "1" for simplicity's sake.
        return $this->createHeader(
            [1, 1, 1, self::$messageIndex++],
            $record->formatted
        );
    }

    /**
     * @inheritDoc
     */
    protected function getDefaultFormatter(): FormatterInterface
    {
        return new WildfireFormatter();
    }

    /**
     * Wildfire initialization headers to enable message parsing
     *
     * @see createHeader()
     * @see sendHeader()
     *
     * @return array<string, string>
     */
    protected function getInitHeaders(): array
    {
        // Initial payload consists of required headers for Wildfire
        return array_merge(
            $this->createHeader(['Protocol', 1], static::PROTOCOL_URI),
            $this->createHeader([1, 'Structure', 1], static::STRUCTURE_URI),
            $this->createHeader([1, 'Plugin', 1], static::PLUGIN_URI)
        );
    }

    /**
     * Send header string to the client
     */
    protected function sendHeader(string $header, string $content): void
    {
        if (!headers_sent() && self::$sendHeaders) {
            header(sprintf('%s: %s', $header, $content));
        }
    }

    /**
     * Creates & sends header for a record, ensuring init headers have been sent prior
     *
     * @see sendHeader()
     * @see sendInitHeaders()
     */
    protected function write(LogRecord $record): void
    {
        if (!self::$sendHeaders || !$this->isWebRequest()) {
            return;
        }

        // WildFire-specific headers must be sent prior to any messages
        if (!self::$initialized) {
            self::$initialized = true;

            self::$sendHeaders = $this->headersAccepted();
            if (!self::$sendHeaders) {
                return;
            }

            foreach ($this->getInitHeaders() as $header => $content) {
                $this->sendHeader($header, $content);
            }
        }

        $header = $this->createRecordHeader($record);
        if (trim(current($header)) !== '') {
            $this->sendHeader(key($header), current($header));
        }
    }

    /**
     * Verifies if the headers are accepted by the current user agent
     */
    protected function headersAccepted(): bool
    {
        if (isset($_SERVER['HTTP_USER_AGENT']) && 1 === preg_match('{\bFirePHP/\d+\.\d+\b}', $_SERVER['HTTP_USER_AGENT'])) {
            return true;
        }

        return isset($_SERVER['HTTP_X_FIREPHP_VERSION']);
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\Level;
use Monolog\Formatter\FormatterInterface;
use Monolog\Formatter\LogmaticFormatter;
use Monolog\LogRecord;

/**
 * @author Julien Breux <julien.breux@gmail.com>
 */
class LogmaticHandler extends SocketHandler
{
    private string $logToken;

    private string $hostname;

    private string $appName;

    /**
     * @param string $token    Log token supplied by Logmatic.
     * @param string $hostname Host name supplied by Logmatic.
     * @param string $appName  Application name supplied by Logmatic.
     * @param bool   $useSSL   Whether or not SSL encryption should be used.
     *
     * @throws MissingExtensionException If SSL encryption is set to true and OpenSSL is missing
     */
    public function __construct(
        string $token,
        string $hostname = '',
        string $appName = '',
        bool $useSSL = true,
        $level = Level::Debug,
        bool $bubble = true,
        bool $persistent = false,
        float $timeout = 0.0,
        float $writingTimeout = 10.0,
        ?float $connectionTimeout = null,
        ?int $chunkSize = null
    ) {
        if ($useSSL && !\extension_loaded('openssl')) {
            throw new MissingExtensionException('The OpenSSL PHP extension is required to use SSL encrypted connection for LogmaticHandler');
        }

        $endpoint = $useSSL ? 'ssl://api.logmatic.io:10515' : 'api.logmatic.io:10514';
        $endpoint .= '/v1/';

        parent::__construct(
            $endpoint,
            $level,
            $bubble,
            $persistent,
            $timeout,
            $writingTimeout,
            $connectionTimeout,
            $chunkSize
        );

        $this->logToken = $token;
        $this->hostname = $hostname;
        $this->appName  = $appName;
    }

    /**
     * @inheritDoc
     */
    protected function generateDataStream(LogRecord $record): string
    {
        return $this->logToken . ' ' . $record->formatted;
    }

    /**
     * @inheritDoc
     */
    protected function getDefaultFormatter(): FormatterInterface
    {
        $formatter = new LogmaticFormatter();

        if ($this->hostname !== '') {
            $formatter->setHostname($this->hostname);
        }
        if ($this->appName !== '') {
            $formatter->setAppName($this->appName);
        }

        return $formatter;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\Level;
use Monolog\Logger;
use Monolog\ResettableInterface;
use Psr\Log\LogLevel;
use Monolog\LogRecord;

/**
 * Base Handler class providing basic level/bubble support
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 */
abstract class AbstractHandler extends Handler implements ResettableInterface
{
    protected Level $level = Level::Debug;
    protected bool $bubble = true;

    /**
     * @param int|string|Level|LogLevel::* $level  The minimum logging level at which this handler will be triggered
     * @param bool                         $bubble Whether the messages that are handled can bubble up the stack or not
     *
     * @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $level
     */
    public function __construct(int|string|Level $level = Level::Debug, bool $bubble = true)
    {
        $this->setLevel($level);
        $this->bubble = $bubble;
    }

    /**
     * @inheritDoc
     */
    public function isHandling(LogRecord $record): bool
    {
        return $record->level->value >= $this->level->value;
    }

    /**
     * Sets minimum logging level at which this handler will be triggered.
     *
     * @param  Level|LogLevel::* $level Level or level name
     * @return $this
     *
     * @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $level
     */
    public function setLevel(int|string|Level $level): self
    {
        $this->level = Logger::toMonologLevel($level);

        return $this;
    }

    /**
     * Gets minimum logging level at which this handler will be triggered.
     */
    public function getLevel(): Level
    {
        return $this->level;
    }

    /**
     * Sets the bubbling behavior.
     *
     * @param  bool  $bubble true means that this handler allows bubbling.
     *                       false means that bubbling is not permitted.
     * @return $this
     */
    public function setBubble(bool $bubble): self
    {
        $this->bubble = $bubble;

        return $this;
    }

    /**
     * Gets the bubbling behavior.
     *
     * @return bool true means that this handler allows bubbling.
     *              false means that bubbling is not permitted.
     */
    public function getBubble(): bool
    {
        return $this->bubble;
    }

    /**
     * @inheritDoc
     */
    public function reset(): void
    {
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\Level;
use Monolog\LogRecord;

/**
 * Stores to STDIN of any process, specified by a command.
 *
 * Usage example:
 * <pre>
 * $log = new Logger('myLogger');
 * $log->pushHandler(new ProcessHandler('/usr/bin/php /var/www/monolog/someScript.php'));
 * </pre>
 *
 * @author Kolja Zuelsdorf <koljaz@web.de>
 */
class ProcessHandler extends AbstractProcessingHandler
{
    /**
     * Holds the process to receive data on its STDIN.
     *
     * @var resource|bool|null
     */
    private $process;

    private string $command;

    private ?string $cwd;

    /**
     * @var resource[]
     */
    private array $pipes = [];

    private float $timeout;

    /**
     * @var array<int, list<string>>
     */
    protected const DESCRIPTOR_SPEC = [
        0 => ['pipe', 'r'],  // STDIN is a pipe that the child will read from
        1 => ['pipe', 'w'],  // STDOUT is a pipe that the child will write to
        2 => ['pipe', 'w'],  // STDERR is a pipe to catch the any errors
    ];

    /**
     * @param  string                    $command Command for the process to start. Absolute paths are recommended,
     *                                            especially if you do not use the $cwd parameter.
     * @param  string|null               $cwd     "Current working directory" (CWD) for the process to be executed in.
     * @param  float                     $timeout The maximum timeout (in seconds) for the stream_select() function.
     * @throws \InvalidArgumentException
     */
    public function __construct(string $command, int|string|Level $level = Level::Debug, bool $bubble = true, ?string $cwd = null, float $timeout = 1.0)
    {
        if ($command === '') {
            throw new \InvalidArgumentException('The command argument must be a non-empty string.');
        }
        if ($cwd === '') {
            throw new \InvalidArgumentException('The optional CWD argument must be a non-empty string or null.');
        }

        parent::__construct($level, $bubble);

        $this->command = $command;
        $this->cwd = $cwd;
        $this->timeout = $timeout;
    }

    /**
     * Writes the record down to the log of the implementing handler
     *
     * @throws \UnexpectedValueException
     */
    protected function write(LogRecord $record): void
    {
        $this->ensureProcessIsStarted();

        $this->writeProcessInput($record->formatted);

        $errors = $this->readProcessErrors();
        if ($errors !== '') {
            throw new \UnexpectedValueException(sprintf('Errors while writing to process: %s', $errors));
        }
    }

    /**
     * Makes sure that the process is actually started, and if not, starts it,
     * assigns the stream pipes, and handles startup errors, if any.
     */
    private function ensureProcessIsStarted(): void
    {
        if (\is_resource($this->process) === false) {
            $this->startProcess();

            $this->handleStartupErrors();
        }
    }

    /**
     * Starts the actual process and sets all streams to non-blocking.
     */
    private function startProcess(): void
    {
        $this->process = proc_open($this->command, static::DESCRIPTOR_SPEC, $this->pipes, $this->cwd);

        foreach ($this->pipes as $pipe) {
            stream_set_blocking($pipe, false);
        }
    }

    /**
     * Selects the STDERR stream, handles upcoming startup errors, and throws an exception, if any.
     *
     * @throws \UnexpectedValueException
     */
    private function handleStartupErrors(): void
    {
        $selected = $this->selectErrorStream();
        if (false === $selected) {
            throw new \UnexpectedValueException('Something went wrong while selecting a stream.');
        }

        $errors = $this->readProcessErrors();

        if (\is_resource($this->process) === false || $errors !== '') {
            throw new \UnexpectedValueException(
                sprintf('The process "%s" could not be opened: ' . $errors, $this->command)
            );
        }
    }

    /**
     * Selects the STDERR stream.
     *
     * @return int|bool
     */
    protected function selectErrorStream()
    {
        $empty = [];
        $errorPipes = [$this->pipes[2]];

        $seconds = (int) $this->timeout;
        return stream_select($errorPipes, $empty, $empty, $seconds, (int) (($this->timeout - $seconds) * 1000000));
    }

    /**
     * Reads the errors of the process, if there are any.
     *
     * @codeCoverageIgnore
     * @return string Empty string if there are no errors.
     */
    protected function readProcessErrors(): string
    {
        return (string) stream_get_contents($this->pipes[2]);
    }

    /**
     * Writes to the input stream of the opened process.
     *
     * @codeCoverageIgnore
     */
    protected function writeProcessInput(string $string): void
    {
        fwrite($this->pipes[0], $string);
    }

    /**
     * @inheritDoc
     */
    public function close(): void
    {
        if (\is_resource($this->process)) {
            foreach ($this->pipes as $pipe) {
                fclose($pipe);
            }
            proc_close($this->process);
            $this->process = null;
        }
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\Level;
use Rollbar\RollbarLogger;
use Throwable;
use Monolog\LogRecord;

/**
 * Sends errors to Rollbar
 *
 * If the context data contains a `payload` key, that is used as an array
 * of payload options to RollbarLogger's log method.
 *
 * Rollbar's context info will contain the context + extra keys from the log record
 * merged, and then on top of that a few keys:
 *
 *  - level (rollbar level name)
 *  - monolog_level (monolog level name, raw level, as rollbar only has 5 but monolog 8)
 *  - channel
 *  - datetime (unix timestamp)
 *
 * @author Paul Statezny <paulstatezny@gmail.com>
 */
class RollbarHandler extends AbstractProcessingHandler
{
    protected RollbarLogger $rollbarLogger;

    /**
     * Records whether any log records have been added since the last flush of the rollbar notifier
     */
    private bool $hasRecords = false;

    protected bool $initialized = false;

    /**
     * @param RollbarLogger $rollbarLogger RollbarLogger object constructed with valid token
     */
    public function __construct(RollbarLogger $rollbarLogger, int|string|Level $level = Level::Error, bool $bubble = true)
    {
        $this->rollbarLogger = $rollbarLogger;

        parent::__construct($level, $bubble);
    }

    /**
     * Translates Monolog log levels to Rollbar levels.
     *
     * @return 'debug'|'info'|'warning'|'error'|'critical'
     */
    protected function toRollbarLevel(Level $level): string
    {
        return match ($level) {
            Level::Debug     => 'debug',
            Level::Info      => 'info',
            Level::Notice    => 'info',
            Level::Warning   => 'warning',
            Level::Error     => 'error',
            Level::Critical  => 'critical',
            Level::Alert     => 'critical',
            Level::Emergency => 'critical',
        };
    }

    /**
     * @inheritDoc
     */
    protected function write(LogRecord $record): void
    {
        if (!$this->initialized) {
            // __destructor() doesn't get called on Fatal errors
            register_shutdown_function([$this, 'close']);
            $this->initialized = true;
        }

        $context = $record->context;
        $context = array_merge($context, $record->extra, [
            'level' => $this->toRollbarLevel($record->level),
            'monolog_level' => $record->level->getName(),
            'channel' => $record->channel,
            'datetime' => $record->datetime->format('U'),
        ]);

        if (isset($context['exception']) && $context['exception'] instanceof Throwable) {
            $exception = $context['exception'];
            unset($context['exception']);
            $toLog = $exception;
        } else {
            $toLog = $record->message;
        }

        $this->rollbarLogger->log($context['level'], $toLog, $context);

        $this->hasRecords = true;
    }

    public function flush(): void
    {
        if ($this->hasRecords) {
            $this->rollbarLogger->flush();
            $this->hasRecords = false;
        }
    }

    /**
     * @inheritDoc
     */
    public function close(): void
    {
        $this->flush();
    }

    /**
     * @inheritDoc
     */
    public function reset(): void
    {
        $this->flush();

        parent::reset();
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\LogRecord;

/**
 * Interface that all Monolog Handlers must implement
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 */
interface HandlerInterface
{
    /**
     * Checks whether the given record will be handled by this handler.
     *
     * This is mostly done for performance reasons, to avoid calling processors for nothing.
     *
     * Handlers should still check the record levels within handle(), returning false in isHandling()
     * is no guarantee that handle() will not be called, and isHandling() might not be called
     * for a given record.
     *
     * @param LogRecord $record Partial log record having only a level initialized
     */
    public function isHandling(LogRecord $record): bool;

    /**
     * Handles a record.
     *
     * All records may be passed to this method, and the handler should discard
     * those that it does not want to handle.
     *
     * The return value of this function controls the bubbling process of the handler stack.
     * Unless the bubbling is interrupted (by returning true), the Logger class will keep on
     * calling further handlers in the stack with a given log record.
     *
     * @param  LogRecord $record The record to handle
     * @return bool      true means that this handler handled the record, and that bubbling is not permitted.
     *                   false means the record was either not processed or that this handler allows bubbling.
     */
    public function handle(LogRecord $record): bool;

    /**
     * Handles a set of records at once.
     *
     * @param array<LogRecord> $records The records to handle
     */
    public function handleBatch(array $records): void;

    /**
     * Closes the handler.
     *
     * Ends a log cycle and frees all resources used by the handler.
     *
     * Closing a Handler means flushing all buffers and freeing any open resources/handles.
     *
     * Implementations have to be idempotent (i.e. it should be possible to call close several times without breakage)
     * and ideally handlers should be able to reopen themselves on handle() after they have been closed.
     *
     * This is useful at the end of a request and will be called automatically when the object
     * is destroyed if you extend Monolog\Handler\Handler.
     *
     * If you are thinking of calling this method yourself, most likely you should be
     * calling ResettableInterface::reset instead. Have a look.
     */
    public function close(): void;
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\Formatter\FormatterInterface;
use Monolog\ResettableInterface;
use Monolog\LogRecord;

/**
 * Forwards records to multiple handlers
 *
 * @author Lenar LÃµhmus <lenar@city.ee>
 */
class GroupHandler extends Handler implements ProcessableHandlerInterface, ResettableInterface
{
    use ProcessableHandlerTrait;

    /** @var HandlerInterface[] */
    protected array $handlers;
    protected bool $bubble;

    /**
     * @param HandlerInterface[] $handlers Array of Handlers.
     * @param bool               $bubble   Whether the messages that are handled can bubble up the stack or not
     *
     * @throws \InvalidArgumentException if an unsupported handler is set
     */
    public function __construct(array $handlers, bool $bubble = true)
    {
        foreach ($handlers as $handler) {
            if (!$handler instanceof HandlerInterface) {
                throw new \InvalidArgumentException('The first argument of the GroupHandler must be an array of HandlerInterface instances.');
            }
        }

        $this->handlers = $handlers;
        $this->bubble = $bubble;
    }

    /**
     * @inheritDoc
     */
    public function isHandling(LogRecord $record): bool
    {
        foreach ($this->handlers as $handler) {
            if ($handler->isHandling($record)) {
                return true;
            }
        }

        return false;
    }

    /**
     * @inheritDoc
     */
    public function handle(LogRecord $record): bool
    {
        if (\count($this->processors) > 0) {
            $record = $this->processRecord($record);
        }

        foreach ($this->handlers as $handler) {
            $handler->handle(clone $record);
        }

        return false === $this->bubble;
    }

    /**
     * @inheritDoc
     */
    public function handleBatch(array $records): void
    {
        if (\count($this->processors) > 0) {
            $processed = [];
            foreach ($records as $record) {
                $processed[] = $this->processRecord($record);
            }
            $records = $processed;
        }

        foreach ($this->handlers as $handler) {
            $handler->handleBatch(array_map(fn ($record) => clone $record, $records));
        }
    }

    public function reset(): void
    {
        $this->resetProcessors();

        foreach ($this->handlers as $handler) {
            if ($handler instanceof ResettableInterface) {
                $handler->reset();
            }
        }
    }

    public function close(): void
    {
        parent::close();

        foreach ($this->handlers as $handler) {
            $handler->close();
        }
    }

    /**
     * @inheritDoc
     */
    public function setFormatter(FormatterInterface $formatter): HandlerInterface
    {
        foreach ($this->handlers as $handler) {
            if ($handler instanceof FormattableHandlerInterface) {
                $handler->setFormatter($formatter);
            }
        }

        return $this;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\LogRecord;
use Throwable;

/**
 * Forwards records to multiple handlers suppressing failures of each handler
 * and continuing through to give every handler a chance to succeed.
 *
 * @author Craig D'Amelio <craig@damelio.ca>
 */
class WhatFailureGroupHandler extends GroupHandler
{
    /**
     * @inheritDoc
     */
    public function handle(LogRecord $record): bool
    {
        if (\count($this->processors) > 0) {
            $record = $this->processRecord($record);
        }

        foreach ($this->handlers as $handler) {
            try {
                $handler->handle(clone $record);
            } catch (Throwable) {
                // What failure?
            }
        }

        return false === $this->bubble;
    }

    /**
     * @inheritDoc
     */
    public function handleBatch(array $records): void
    {
        if (\count($this->processors) > 0) {
            $processed = [];
            foreach ($records as $record) {
                $processed[] = $this->processRecord($record);
            }
            $records = $processed;
        }

        foreach ($this->handlers as $handler) {
            try {
                $handler->handleBatch(array_map(fn ($record) => clone $record, $records));
            } catch (Throwable) {
                // What failure?
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    public function close(): void
    {
        foreach ($this->handlers as $handler) {
            try {
                $handler->close();
            } catch (\Throwable $e) {
                // What failure?
            }
        }
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\ResettableInterface;
use Monolog\Formatter\FormatterInterface;
use Monolog\LogRecord;

/**
 * This simple wrapper class can be used to extend handlers functionality.
 *
 * Example: A custom filtering that can be applied to any handler.
 *
 * Inherit from this class and override handle() like this:
 *
 *   public function handle(LogRecord $record)
 *   {
 *        if ($record meets certain conditions) {
 *            return false;
 *        }
 *        return $this->handler->handle($record);
 *   }
 *
 * @author Alexey Karapetov <alexey@karapetov.com>
 */
class HandlerWrapper implements HandlerInterface, ProcessableHandlerInterface, FormattableHandlerInterface, ResettableInterface
{
    protected HandlerInterface $handler;

    public function __construct(HandlerInterface $handler)
    {
        $this->handler = $handler;
    }

    /**
     * @inheritDoc
     */
    public function isHandling(LogRecord $record): bool
    {
        return $this->handler->isHandling($record);
    }

    /**
     * @inheritDoc
     */
    public function handle(LogRecord $record): bool
    {
        return $this->handler->handle($record);
    }

    /**
     * @inheritDoc
     */
    public function handleBatch(array $records): void
    {
        $this->handler->handleBatch($records);
    }

    /**
     * @inheritDoc
     */
    public function close(): void
    {
        $this->handler->close();
    }

    /**
     * @inheritDoc
     */
    public function pushProcessor(callable $callback): HandlerInterface
    {
        if ($this->handler instanceof ProcessableHandlerInterface) {
            $this->handler->pushProcessor($callback);

            return $this;
        }

        throw new \LogicException('The wrapped handler does not implement ' . ProcessableHandlerInterface::class);
    }

    /**
     * @inheritDoc
     */
    public function popProcessor(): callable
    {
        if ($this->handler instanceof ProcessableHandlerInterface) {
            return $this->handler->popProcessor();
        }

        throw new \LogicException('The wrapped handler does not implement ' . ProcessableHandlerInterface::class);
    }

    /**
     * @inheritDoc
     */
    public function setFormatter(FormatterInterface $formatter): HandlerInterface
    {
        if ($this->handler instanceof FormattableHandlerInterface) {
            $this->handler->setFormatter($formatter);

            return $this;
        }

        throw new \LogicException('The wrapped handler does not implement ' . FormattableHandlerInterface::class);
    }

    /**
     * @inheritDoc
     */
    public function getFormatter(): FormatterInterface
    {
        if ($this->handler instanceof FormattableHandlerInterface) {
            return $this->handler->getFormatter();
        }

        throw new \LogicException('The wrapped handler does not implement ' . FormattableHandlerInterface::class);
    }

    public function reset(): void
    {
        if ($this->handler instanceof ResettableInterface) {
            $this->handler->reset();
        }
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\Level;
use Monolog\Formatter\FormatterInterface;
use Monolog\Formatter\LineFormatter;

/**
 * Common syslog functionality
 */
abstract class AbstractSyslogHandler extends AbstractProcessingHandler
{
    protected int $facility;

    /**
     * List of valid log facility names.
     * @var array<string, int>
     */
    protected array $facilities = [
        'auth'     => \LOG_AUTH,
        'authpriv' => \LOG_AUTHPRIV,
        'cron'     => \LOG_CRON,
        'daemon'   => \LOG_DAEMON,
        'kern'     => \LOG_KERN,
        'lpr'      => \LOG_LPR,
        'mail'     => \LOG_MAIL,
        'news'     => \LOG_NEWS,
        'syslog'   => \LOG_SYSLOG,
        'user'     => \LOG_USER,
        'uucp'     => \LOG_UUCP,
    ];

    /**
     * Translates Monolog log levels to syslog log priorities.
     */
    protected function toSyslogPriority(Level $level): int
    {
        return $level->toRFC5424Level();
    }

    /**
     * @param string|int $facility Either one of the names of the keys in $this->facilities, or a LOG_* facility constant
     */
    public function __construct(string|int $facility = \LOG_USER, int|string|Level $level = Level::Debug, bool $bubble = true)
    {
        parent::__construct($level, $bubble);

        if (!\defined('PHP_WINDOWS_VERSION_BUILD')) {
            $this->facilities['local0'] = \LOG_LOCAL0;
            $this->facilities['local1'] = \LOG_LOCAL1;
            $this->facilities['local2'] = \LOG_LOCAL2;
            $this->facilities['local3'] = \LOG_LOCAL3;
            $this->facilities['local4'] = \LOG_LOCAL4;
            $this->facilities['local5'] = \LOG_LOCAL5;
            $this->facilities['local6'] = \LOG_LOCAL6;
            $this->facilities['local7'] = \LOG_LOCAL7;
        } else {
            $this->facilities['local0'] = 128; // LOG_LOCAL0
            $this->facilities['local1'] = 136; // LOG_LOCAL1
            $this->facilities['local2'] = 144; // LOG_LOCAL2
            $this->facilities['local3'] = 152; // LOG_LOCAL3
            $this->facilities['local4'] = 160; // LOG_LOCAL4
            $this->facilities['local5'] = 168; // LOG_LOCAL5
            $this->facilities['local6'] = 176; // LOG_LOCAL6
            $this->facilities['local7'] = 184; // LOG_LOCAL7
        }

        // convert textual description of facility to syslog constant
        if (\is_string($facility) && \array_key_exists(strtolower($facility), $this->facilities)) {
            $facility = $this->facilities[strtolower($facility)];
        } elseif (!\in_array($facility, array_values($this->facilities), true)) {
            throw new \UnexpectedValueException('Unknown facility value "'.$facility.'" given');
        }

        $this->facility = $facility;
    }

    /**
     * @inheritDoc
     */
    protected function getDefaultFormatter(): FormatterInterface
    {
        return new LineFormatter('%channel%.%level_name%: %message% %context% %extra%');
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\Formatter\LineFormatter;
use Monolog\Formatter\FormatterInterface;
use Monolog\Level;
use Monolog\LogRecord;
use Predis\Client as Predis;
use Redis;

/**
 * Sends the message to a Redis Pub/Sub channel using PUBLISH
 *
 * usage example:
 *
 *   $log = new Logger('application');
 *   $redis = new RedisPubSubHandler(new Predis\Client("tcp://localhost:6379"), "logs", Level::Warning);
 *   $log->pushHandler($redis);
 *
 * @author GaÃ«tan FaugÃ¨re <gaetan@fauge.re>
 */
class RedisPubSubHandler extends AbstractProcessingHandler
{
    /** @var Predis<Predis>|Redis */
    private Predis|Redis $redisClient;
    private string $channelKey;

    /**
     * @param Predis<Predis>|Redis $redis The redis instance
     * @param string               $key   The channel key to publish records to
     */
    public function __construct(Predis|Redis $redis, string $key, int|string|Level $level = Level::Debug, bool $bubble = true)
    {
        $this->redisClient = $redis;
        $this->channelKey = $key;

        parent::__construct($level, $bubble);
    }

    /**
     * @inheritDoc
     */
    protected function write(LogRecord $record): void
    {
        $this->redisClient->publish($this->channelKey, $record->formatted);
    }

    /**
     * @inheritDoc
     */
    protected function getDefaultFormatter(): FormatterInterface
    {
        return new LineFormatter();
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\Level;
use Monolog\Formatter\FormatterInterface;
use Monolog\LogRecord;

/**
 * Handler to only pass log messages when a certain threshold of number of messages is reached.
 *
 * This can be useful in cases of processing a batch of data, but you're for example only interested
 * in case it fails catastrophically instead of a warning for 1 or 2 events. Worse things can happen, right?
 *
 * Usage example:
 *
 * ```
 *   $log = new Logger('application');
 *   $handler = new SomeHandler(...)
 *
 *   // Pass all warnings to the handler when more than 10 & all error messages when more then 5
 *   $overflow = new OverflowHandler($handler, [Level::Warning->value => 10, Level::Error->value => 5]);
 *
 *   $log->pushHandler($overflow);
 *```
 *
 * @author Kris Buist <krisbuist@gmail.com>
 */
class OverflowHandler extends AbstractHandler implements FormattableHandlerInterface
{
    private HandlerInterface $handler;

    /** @var array<int, int> */
    private array $thresholdMap = [];

    /**
     * Buffer of all messages passed to the handler before the threshold was reached
     *
     * @var mixed[][]
     */
    private array $buffer = [];

    /**
     * @param array<int, int> $thresholdMap Dictionary of log level value => threshold
     */
    public function __construct(
        HandlerInterface $handler,
        array $thresholdMap = [],
        $level = Level::Debug,
        bool $bubble = true
    ) {
        $this->handler = $handler;
        foreach ($thresholdMap as $thresholdLevel => $threshold) {
            $this->thresholdMap[$thresholdLevel] = $threshold;
        }
        parent::__construct($level, $bubble);
    }

    /**
     * Handles a record.
     *
     * All records may be passed to this method, and the handler should discard
     * those that it does not want to handle.
     *
     * The return value of this function controls the bubbling process of the handler stack.
     * Unless the bubbling is interrupted (by returning true), the Logger class will keep on
     * calling further handlers in the stack with a given log record.
     *
     * @inheritDoc
     */
    public function handle(LogRecord $record): bool
    {
        if ($record->level->isLowerThan($this->level)) {
            return false;
        }

        $level = $record->level->value;

        if (!isset($this->thresholdMap[$level])) {
            $this->thresholdMap[$level] = 0;
        }

        if ($this->thresholdMap[$level] > 0) {
            // The overflow threshold is not yet reached, so we're buffering the record and lowering the threshold by 1
            $this->thresholdMap[$level]--;
            $this->buffer[$level][] = $record;

            return false === $this->bubble;
        }

        if ($this->thresholdMap[$level] === 0) {
            // This current message is breaking the threshold. Flush the buffer and continue handling the current record
            foreach ($this->buffer[$level] ?? [] as $buffered) {
                $this->handler->handle($buffered);
            }
            $this->thresholdMap[$level]--;
            unset($this->buffer[$level]);
        }

        $this->handler->handle($record);

        return false === $this->bubble;
    }

    /**
     * @inheritDoc
     */
    public function setFormatter(FormatterInterface $formatter): HandlerInterface
    {
        if ($this->handler instanceof FormattableHandlerInterface) {
            $this->handler->setFormatter($formatter);

            return $this;
        }

        throw new \UnexpectedValueException('The nested handler of type '.\get_class($this->handler).' does not support formatters.');
    }

    /**
     * @inheritDoc
     */
    public function getFormatter(): FormatterInterface
    {
        if ($this->handler instanceof FormattableHandlerInterface) {
            return $this->handler->getFormatter();
        }

        throw new \UnexpectedValueException('The nested handler of type '.\get_class($this->handler).' does not support formatters.');
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\Level;
use Monolog\Utils;

/**
 * SendGridHandler uses the SendGrid API v3 function to send Log emails, more information in https://www.twilio.com/docs/sendgrid/for-developers/sending-email/api-getting-started
 *
 * @author Ricardo Fontanelli <ricardo.fontanelli@hotmail.com>
 */
class SendGridHandler extends MailHandler
{
    /**
     * The SendGrid API User
     * @deprecated this is not used anymore as of SendGrid API v3
     */
    protected string $apiUser;
    /**
     * The email addresses to which the message will be sent
     * @var string[]
     */
    protected array $to;

    /**
     * @param string|null $apiUser Unused user as of SendGrid API v3, you can pass null or any string
     * @param list<string>|string $to
     * @param non-empty-string $apiHost Allows you to use another endpoint (e.g. api.eu.sendgrid.com)
     * @throws MissingExtensionException If the curl extension is missing
     */
    public function __construct(
        string|null $apiUser,
        protected string $apiKey,
        protected string $from,
        array|string $to,
        protected string $subject,
        int|string|Level $level = Level::Error,
        bool $bubble = true,
        /** @var non-empty-string */
        private readonly string $apiHost = 'api.sendgrid.com',
    ) {
        if (!\extension_loaded('curl')) {
            throw new MissingExtensionException('The curl extension is needed to use the SendGridHandler');
        }

        $this->to = (array) $to;
        // @phpstan-ignore property.deprecated
        $this->apiUser = $apiUser ?? '';
        parent::__construct($level, $bubble);
    }

    protected function send(string $content, array $records): void
    {
        $body = [];
        $body['personalizations'] = [];
        $body['from']['email'] = $this->from;
        foreach ($this->to as $recipient) {
            $body['personalizations'][]['to'][]['email'] = $recipient;
        }
        $body['subject'] = $this->subject;

        if ($this->isHtmlBody($content)) {
            $body['content'][] = [
                'type' => 'text/html',
                'value' => $content,
            ];
        } else {
            $body['content'][] = [
                'type' => 'text/plain',
                'value' => $content,
            ];
        }
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'Content-Type: application/json',
            'Authorization: Bearer '.$this->apiKey,
        ]);
        curl_setopt($ch, CURLOPT_URL, 'https://'.$this->apiHost.'/v3/mail/send');
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, Utils::jsonEncode($body));

        Curl\Util::execute($ch, 2);
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Handler;

use Monolog\LogRecord;

/**
 * Base Handler class providing the Handler structure, including processors and formatters
 *
 * Classes extending it should (in most cases) only implement write($record)
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 * @author Christophe Coevoet <stof@notk.org>
 */
abstract class AbstractProcessingHandler extends AbstractHandler implements ProcessableHandlerInterface, FormattableHandlerInterface
{
    use ProcessableHandlerTrait;
    use FormattableHandlerTrait;

    /**
     * @inheritDoc
     */
    public function handle(LogRecord $record): bool
    {
        if (!$this->isHandling($record)) {
            return false;
        }

        if (\count($this->processors) > 0) {
            $record = $this->processRecord($record);
        }

        $record->formatted = $this->getFormatter()->format($record);

        $this->write($record);

        return !$this->getBubble();
    }

    /**
     * Writes the (already formatted) record down to the log of the implementing handler
     */
    abstract protected function write(LogRecord $record): void;

    public function reset(): void
    {
        parent::reset();

        $this->resetProcessors();
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog;

use Psr\Log\LogLevel;

/**
 * Represents the log levels
 *
 * Monolog supports the logging levels described by RFC 5424 {@see https://datatracker.ietf.org/doc/html/rfc5424}
 * but due to BC the severity values used internally are not 0-7.
 *
 * To get the level name/value out of a Level there are several options:
 *
 * - Use ->getName() to get the standard Monolog name which is full uppercased (e.g. "DEBUG")
 * - Use ->toPsrLogLevel() to get the standard PSR-3 name which is full lowercased (e.g. "debug")
 * - Use ->toRFC5424Level() to get the standard RFC 5424 value (e.g. 7 for debug, 0 for emergency)
 * - Use ->name to get the enum case's name which is capitalized (e.g. "Debug")
 *
 * To get the internal value for filtering, if the includes/isLowerThan/isHigherThan methods are
 * not enough, you can use ->value to get the enum case's integer value.
 */
enum Level: int
{
    /**
     * Detailed debug information
     */
    case Debug = 100;

    /**
     * Interesting events
     *
     * Examples: User logs in, SQL logs.
     */
    case Info = 200;

    /**
     * Uncommon events
     */
    case Notice = 250;

    /**
     * Exceptional occurrences that are not errors
     *
     * Examples: Use of deprecated APIs, poor use of an API,
     * undesirable things that are not necessarily wrong.
     */
    case Warning = 300;

    /**
     * Runtime errors
     */
    case Error = 400;

    /**
     * Critical conditions
     *
     * Example: Application component unavailable, unexpected exception.
     */
    case Critical = 500;

    /**
     * Action must be taken immediately
     *
     * Example: Entire website down, database unavailable, etc.
     * This should trigger the SMS alerts and wake you up.
     */
    case Alert = 550;

    /**
     * Urgent alert.
     */
    case Emergency = 600;

    /**
     * @param  value-of<self::NAMES>|LogLevel::*|'Debug'|'Info'|'Notice'|'Warning'|'Error'|'Critical'|'Alert'|'Emergency' $name
     * @return static
     */
    public static function fromName(string $name): self
    {
        return match (strtolower($name)) {
            'debug' => self::Debug,
            'info' => self::Info,
            'notice' => self::Notice,
            'warning' => self::Warning,
            'error' => self::Error,
            'critical' => self::Critical,
            'alert' => self::Alert,
            'emergency' => self::Emergency,
        };
    }

    /**
     * @param  value-of<self::VALUES> $value
     * @return static
     */
    public static function fromValue(int $value): self
    {
        return self::from($value);
    }

    /**
     * Returns true if the passed $level is higher or equal to $this
     */
    public function includes(Level $level): bool
    {
        return $this->value <= $level->value;
    }

    public function isHigherThan(Level $level): bool
    {
        return $this->value > $level->value;
    }

    public function isLowerThan(Level $level): bool
    {
        return $this->value < $level->value;
    }

    /**
     * Returns the monolog standardized all-capitals name of the level
     *
     * Use this instead of $level->name which returns the enum case name (e.g. Debug vs DEBUG if you use getName())
     *
     * @return value-of<self::NAMES>
     */
    public function getName(): string
    {
        return match ($this) {
            self::Debug => 'DEBUG',
            self::Info => 'INFO',
            self::Notice => 'NOTICE',
            self::Warning => 'WARNING',
            self::Error => 'ERROR',
            self::Critical => 'CRITICAL',
            self::Alert => 'ALERT',
            self::Emergency => 'EMERGENCY',
        };
    }

    /**
     * Returns the PSR-3 level matching this instance
     *
     * @phpstan-return \Psr\Log\LogLevel::*
     */
    public function toPsrLogLevel(): string
    {
        return match ($this) {
            self::Debug => LogLevel::DEBUG,
            self::Info => LogLevel::INFO,
            self::Notice => LogLevel::NOTICE,
            self::Warning => LogLevel::WARNING,
            self::Error => LogLevel::ERROR,
            self::Critical => LogLevel::CRITICAL,
            self::Alert => LogLevel::ALERT,
            self::Emergency => LogLevel::EMERGENCY,
        };
    }

    /**
     * Returns the RFC 5424 level matching this instance
     *
     * @phpstan-return int<0, 7>
     */
    public function toRFC5424Level(): int
    {
        return match ($this) {
            self::Debug => 7,
            self::Info => 6,
            self::Notice => 5,
            self::Warning => 4,
            self::Error => 3,
            self::Critical => 2,
            self::Alert => 1,
            self::Emergency => 0,
        };
    }

    public const VALUES = [
        100,
        200,
        250,
        300,
        400,
        500,
        550,
        600,
    ];

    public const NAMES = [
        'DEBUG',
        'INFO',
        'NOTICE',
        'WARNING',
        'ERROR',
        'CRITICAL',
        'ALERT',
        'EMERGENCY',
    ];
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Attribute;

/**
 * A reusable attribute to help configure a class or a method as a processor.
 *
 * Using it offers no guarantee: it needs to be leveraged by a Monolog third-party consumer.
 *
 * Using it with the Monolog library only has no effect at all: processors should still be turned into a callable if
 * needed and manually pushed to the loggers and to the processable handlers.
 */
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class AsMonologProcessor
{
    /**
     * @param string|null $channel  The logging channel the processor should be pushed to.
     * @param string|null $handler  The handler the processor should be pushed to.
     * @param string|null $method   The method that processes the records (if the attribute is used at the class level).
     * @param int|null    $priority The priority of the processor so the order can be determined.
     */
    public function __construct(
        public readonly ?string $channel = null,
        public readonly ?string $handler = null,
        public readonly ?string $method = null,
        public readonly ?int $priority = null
    ) {
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Attribute;

/**
 * A reusable attribute to help configure a class, or one of its constructor/method arguments,
 * as expecting a given logger channel.
 *
 * Using it offers no guarantee: it needs to be leveraged by a Monolog third-party consumer.
 *
 * Using it with the Monolog library only has no effect at all: wiring the logger instance into
 * other classes is not managed by Monolog.
 */
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_PARAMETER)]
final class WithMonologChannel
{
    public function __construct(
        public readonly string $channel
    ) {
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog;

use Monolog\Formatter\FormatterInterface;
use Monolog\Formatter\WrappingFormatterInterface;

final class Utils
{
    const DEFAULT_JSON_FLAGS = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRESERVE_ZERO_FRACTION | JSON_INVALID_UTF8_SUBSTITUTE | JSON_PARTIAL_OUTPUT_ON_ERROR;

    /** @var array<class-string, array<string, true>> */
    private static array $sensitiveParameterNames = [];

    public static function getClass(object $object): string
    {
        return self::getClassName(\get_class($object));
    }

    /**
     * Strips the declaration site PHP appends to anonymous class names after a NUL byte
     *
     * Takes the name rather than the object because stack trace frames only carry the string.
     *
     * @param class-string $class
     */
    public static function getClassName(string $class): string
    {
        if (false === ($pos = strpos($class, "@anonymous\0"))) {
            return $class;
        }

        if (false === ($parent = get_parent_class($class))) {
            return substr($class, 0, $pos + 10);
        }

        return $parent . '@anonymous';
    }

    /**
     * Returns the names of the constructor parameters of a class which are marked #[SensitiveParameter]
     *
     * The attribute can only target parameters, and PHP does not copy it over to the promoted
     * property, so the constructor signature is the only place it can be found. Non-promoted
     * parameters are included too, to be matched by name against the object's properties, as
     * assigning a sensitive parameter to a same-named property is just as common.
     *
     * Results are cached per class as the reflection cost is otherwise prohibitive.
     *
     * @param  class-string        $class
     * @return array<string, true> Set of parameter names, to be used with isset()
     */
    public static function getSensitiveParameterNames(string $class): array
    {
        if (isset(self::$sensitiveParameterNames[$class])) {
            return self::$sensitiveParameterNames[$class];
        }

        $names = [];
        try {
            $constructor = (new \ReflectionClass($class))->getConstructor();
            if (null !== $constructor) {
                foreach ($constructor->getParameters() as $parameter) {
                    // filtering by name and not IS_INSTANCEOF on purpose, so this keeps working
                    // on PHP < 8.2 where the attribute class does not exist yet
                    if (\count($parameter->getAttributes(\SensitiveParameter::class)) > 0) {
                        $names[$parameter->getName()] = true;
                    }
                }
            }
        } catch (\ReflectionException) {
            // class is not loadable, nothing we can do
        }

        return self::$sensitiveParameterNames[$class] = $names;
    }

    /**
     * Resolves a chain of formatter decorators down to the formatter doing the actual work
     *
     * Useful for handlers which only accept one specific formatter, to let them accept it
     * wrapped in a decorator like the RedactingFormatter.
     */
    public static function unwrapFormatter(FormatterInterface $formatter): FormatterInterface
    {
        // bounded in case a decorator ends up wrapping itself
        for ($i = 0; $i < 10 && $formatter instanceof WrappingFormatterInterface; $i++) {
            $formatter = $formatter->getWrappedFormatter();
        }

        return $formatter;
    }

    public static function substr(string $string, int $start, ?int $length = null): string
    {
        if (\extension_loaded('mbstring')) {
            return mb_strcut($string, $start, $length);
        }

        return substr($string, $start, (null === $length) ? \strlen($string) : $length);
    }

    /**
     * Makes sure if a relative path is passed in it is turned into an absolute path
     *
     * @param string $streamUrl stream URL or path without protocol
     */
    public static function canonicalizePath(string $streamUrl): string
    {
        $prefix = '';
        if ('file://' === substr($streamUrl, 0, 7)) {
            $streamUrl = substr($streamUrl, 7);
            $prefix = 'file://';
        }

        // other type of stream, not supported
        if (false !== strpos($streamUrl, '://')) {
            return $streamUrl;
        }

        // already absolute
        if (substr($streamUrl, 0, 1) === '/' || substr($streamUrl, 1, 1) === ':' || substr($streamUrl, 0, 2) === '\\\\') {
            return $prefix.$streamUrl;
        }

        $streamUrl = getcwd() . '/' . $streamUrl;

        return $prefix.$streamUrl;
    }

    /**
     * Return the JSON representation of a value
     *
     * @param  mixed             $data
     * @param  int               $encodeFlags  flags to pass to json encode, defaults to DEFAULT_JSON_FLAGS
     * @param  bool              $ignoreErrors whether to ignore encoding errors or to throw on error, when ignored and the encoding fails, "null" is returned which is valid json for null
     * @throws \RuntimeException if encoding fails and errors are not ignored
     * @return string            when errors are ignored and the encoding fails, "null" is returned which is valid json for null
     */
    public static function jsonEncode($data, ?int $encodeFlags = null, bool $ignoreErrors = false): string
    {
        if (null === $encodeFlags) {
            $encodeFlags = self::DEFAULT_JSON_FLAGS;
        }

        if ($ignoreErrors) {
            $json = @json_encode($data, $encodeFlags);
            if (false === $json) {
                return 'null';
            }

            return $json;
        }

        $json = json_encode($data, $encodeFlags);
        if (false === $json) {
            $json = self::handleJsonError(json_last_error(), $data);
        }

        return $json;
    }

    /**
     * Handle a json_encode failure.
     *
     * If the failure is due to invalid string encoding, try to clean the
     * input and encode again. If the second encoding attempt fails, the
     * initial error is not encoding related or the input can't be cleaned then
     * raise a descriptive exception.
     *
     * @param  int               $code        return code of json_last_error function
     * @param  mixed             $data        data that was meant to be encoded
     * @param  int               $encodeFlags flags to pass to json encode, defaults to JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRESERVE_ZERO_FRACTION
     * @throws \RuntimeException if failure can't be corrected
     * @return string            JSON encoded data after error correction
     */
    public static function handleJsonError(int $code, $data, ?int $encodeFlags = null): string
    {
        if ($code !== JSON_ERROR_UTF8) {
            self::throwEncodeError($code, $data);
        }

        if (\is_string($data)) {
            self::detectAndCleanUtf8($data);
        } elseif (\is_array($data)) {
            array_walk_recursive($data, ['Monolog\Utils', 'detectAndCleanUtf8']);
        } else {
            self::throwEncodeError($code, $data);
        }

        if (null === $encodeFlags) {
            $encodeFlags = self::DEFAULT_JSON_FLAGS;
        }

        $json = json_encode($data, $encodeFlags);

        if ($json === false) {
            self::throwEncodeError(json_last_error(), $data);
        }

        return $json;
    }

    /**
     * Throws an exception according to a given code with a customized message
     *
     * @param  int               $code return code of json_last_error function
     * @param  mixed             $data data that was meant to be encoded
     * @throws \RuntimeException
     */
    private static function throwEncodeError(int $code, $data): never
    {
        $msg = match ($code) {
            JSON_ERROR_DEPTH => 'Maximum stack depth exceeded',
            JSON_ERROR_STATE_MISMATCH => 'Underflow or the modes mismatch',
            JSON_ERROR_CTRL_CHAR => 'Unexpected control character found',
            JSON_ERROR_UTF8 => 'Malformed UTF-8 characters, possibly incorrectly encoded',
            default => 'Unknown error',
        };

        throw new \RuntimeException('JSON encoding failed: '.$msg.'. Encoding: '.var_export($data, true));
    }

    /**
     * Detect invalid UTF-8 string characters and convert to valid UTF-8.
     *
     * Valid UTF-8 input will be left unmodified, but strings containing
     * invalid UTF-8 codepoints will be reencoded as UTF-8 with an assumed
     * original encoding of ISO-8859-15. This conversion may result in
     * incorrect output if the actual encoding was not ISO-8859-15, but it
     * will be clean UTF-8 output and will not rely on expensive and fragile
     * detection algorithms.
     *
     * Function converts the input in place in the passed variable so that it
     * can be used as a callback for array_walk_recursive.
     *
     * @param mixed $data Input to check and convert if needed, passed by ref
     */
    private static function detectAndCleanUtf8(&$data): void
    {
        if (\is_string($data) && preg_match('//u', $data) !== 1) {
            $data = preg_replace_callback(
                '/[\x80-\xFF]+/',
                function (array $m): string {
                    return \function_exists('mb_convert_encoding')
                        ? mb_convert_encoding($m[0], 'UTF-8', 'ISO-8859-1')
                        : (\function_exists('utf8_encode') ? utf8_encode($m[0]) : '');
                },
                $data
            );
            if (!\is_string($data)) {
                $pcreErrorCode = preg_last_error();

                throw new \RuntimeException('Failed to preg_replace_callback: ' . $pcreErrorCode . ' / ' . preg_last_error_msg());
            }
            $data = str_replace(
                ['Â¤', 'Â¦', 'Â¨', 'Â´', 'Â¸', 'Â¼', 'Â½', 'Â¾'],
                ['â‚¬', 'Å ', 'Å¡', 'Å½', 'Å¾', 'Å’', 'Å“', 'Å¸'],
                $data
            );
        }
    }

    /**
     * Converts a string with a valid 'memory_limit' format, to bytes.
     *
     * @param  string|false $val
     * @return int|false    Returns an integer representing bytes. Returns FALSE in case of error.
     */
    public static function expandIniShorthandBytes($val)
    {
        if (!\is_string($val)) {
            return false;
        }

        // support -1
        if ((int) $val < 0) {
            return (int) $val;
        }

        if (!(bool) preg_match('/^\s*(?<val>\d+)(?:\.\d+)?\s*(?<unit>[gmk]?)\s*$/i', $val, $match)) {
            return false;
        }

        $val = (int) $match['val'];
        switch (strtolower($match['unit'])) {
            case 'g':
                $val *= 1024;
                // no break
            case 'm':
                $val *= 1024;
                // no break
            case 'k':
                $val *= 1024;
        }

        return $val;
    }

    public static function getRecordMessageForException(LogRecord $record): string
    {
        $context = '';
        $extra = '';

        try {
            if (\count($record->context) > 0) {
                $context = "\nContext: " . json_encode($record->context, JSON_THROW_ON_ERROR);
            }
            if (\count($record->extra) > 0) {
                $extra = "\nExtra: " . json_encode($record->extra, JSON_THROW_ON_ERROR);
            }
        } catch (\Throwable $e) {
            // noop
        }

        return "\nThe exception occurred while attempting to log: " . $record->message . $context . $extra;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Test;

/**
 * Lets you easily generate log records and a dummy formatter for testing purposes
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 *
 * @deprecated use MonologTestCase instead.
 */
class TestCase extends MonologTestCase
{
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Test;

use Monolog\Level;
use Monolog\Logger;
use Monolog\LogRecord;
use Monolog\JsonSerializableDateTimeImmutable;
use Monolog\Formatter\FormatterInterface;
use Psr\Log\LogLevel;

/**
 * Lets you easily generate log records and a dummy formatter for testing purposes
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 */
class MonologTestCase extends \PHPUnit\Framework\TestCase
{
    /**
     * @param array<mixed> $context
     * @param array<mixed> $extra
     *
     * @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $level
     */
    protected function getRecord(int|string|Level $level = Level::Warning, string|\Stringable $message = 'test', array $context = [], string $channel = 'test', \DateTimeImmutable $datetime = new JsonSerializableDateTimeImmutable(true), array $extra = []): LogRecord
    {
        return new LogRecord(
            message: (string) $message,
            context: $context,
            level: Logger::toMonologLevel($level),
            channel: $channel,
            datetime: $datetime,
            extra: $extra,
        );
    }

    /**
     * @phpstan-return list<LogRecord>
     */
    protected function getMultipleRecords(): array
    {
        return [
            $this->getRecord(Level::Debug, 'debug message 1'),
            $this->getRecord(Level::Debug, 'debug message 2'),
            $this->getRecord(Level::Info, 'information'),
            $this->getRecord(Level::Warning, 'warning'),
            $this->getRecord(Level::Error, 'error'),
        ];
    }

    protected function getIdentityFormatter(): FormatterInterface
    {
        $formatter = $this->createMock(FormatterInterface::class);
        $formatter->expects(self::any())
            ->method('format')
            ->willReturnCallback(function ($record) {
                return $record->message;
            });

        return $formatter;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Formatter;

use Monolog\LogRecord;

/**
 * Serializes a log message to Logstash Event Format
 *
 * @see https://www.elastic.co/products/logstash
 * @see https://github.com/elastic/logstash/blob/master/logstash-core/src/main/java/org/logstash/Event.java
 *
 * @author Tim Mower <timothy.mower@gmail.com>
 */
class LogstashFormatter extends NormalizerFormatter
{
    /**
     * @var string the name of the system for the Logstash log message, used to fill the @source field
     */
    protected string $systemName;

    /**
     * @var string an application name for the Logstash log message, used to fill the @type field
     */
    protected string $applicationName;

    /**
     * @var string the key for 'extra' fields from the Monolog record
     */
    protected string $extraKey;

    /**
     * @var string the key for 'context' fields from the Monolog record
     */
    protected string $contextKey;

    /**
     * @param string      $applicationName The application that sends the data, used as the "type" field of logstash
     * @param string|null $systemName      The system/machine name, used as the "source" field of logstash, defaults to the hostname of the machine
     * @param string      $extraKey        The key for extra keys inside logstash "fields", defaults to extra
     * @param string      $contextKey      The key for context keys inside logstash "fields", defaults to context
     */
    public function __construct(string $applicationName, ?string $systemName = null, string $extraKey = 'extra', string $contextKey = 'context')
    {
        // logstash requires a ISO 8601 format date with optional millisecond precision.
        parent::__construct('Y-m-d\TH:i:s.uP');

        $this->systemName = $systemName === null ? (string) gethostname() : $systemName;
        $this->applicationName = $applicationName;
        $this->extraKey = $extraKey;
        $this->contextKey = $contextKey;
    }

    /**
     * @inheritDoc
     */
    public function format(LogRecord $record): string
    {
        $recordData = parent::format($record);

        $message = [
            '@timestamp' => $recordData['datetime'],
            '@version' => 1,
            'host' => $this->systemName,
        ];
        if (isset($recordData['message'])) {
            $message['message'] = $recordData['message'];
        }
        if (isset($recordData['channel'])) {
            $message['type'] = $recordData['channel'];
            $message['channel'] = $recordData['channel'];
        }
        if (isset($recordData['level_name'])) {
            $message['level'] = $recordData['level_name'];
        }
        if (isset($recordData['level'])) {
            $message['monolog_level'] = $recordData['level'];
        }
        if ('' !== $this->applicationName) {
            $message['type'] = $this->applicationName;
        }
        if (\count($recordData['extra']) > 0) {
            $message[$this->extraKey] = $recordData['extra'];
        }
        if (\count($recordData['context']) > 0) {
            $message[$this->contextKey] = $recordData['context'];
        }

        return $this->toJson($message) . "\n";
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Formatter;

use Monolog\JsonSerializableDateTimeImmutable;
use Monolog\Utils;
use Throwable;
use Monolog\LogRecord;

/**
 * Normalizes incoming records to remove objects/resources so it's easier to dump to various targets
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 */
class NormalizerFormatter implements FormatterInterface
{
    public const SIMPLE_DATE = "Y-m-d\TH:i:sP";

    protected string $dateFormat;
    protected int $maxNormalizeDepth = 9;
    protected int $maxNormalizeItemCount = 1000;
    protected ?int $maxTraceLength = null;

    private int $jsonEncodeOptions = Utils::DEFAULT_JSON_FLAGS;

    protected string $basePath = '';

    /**
     * @param string|null $dateFormat The format of the timestamp: one supported by DateTime::format
     */
    public function __construct(?string $dateFormat = null)
    {
        $this->dateFormat = null === $dateFormat ? static::SIMPLE_DATE : $dateFormat;
    }

    /**
     * @inheritDoc
     */
    public function format(LogRecord $record)
    {
        return $this->normalizeRecord($record);
    }

    /**
     * Normalize an arbitrary value to a scalar|array|null
     *
     * @return null|scalar|array<mixed[]|scalar|null>
     */
    public function normalizeValue(mixed $data): mixed
    {
        return $this->normalize($data);
    }

    /**
     * @inheritDoc
     */
    public function formatBatch(array $records)
    {
        foreach ($records as $key => $record) {
            $records[$key] = $this->format($record);
        }

        return $records;
    }

    public function getDateFormat(): string
    {
        return $this->dateFormat;
    }

    /**
     * @return $this
     */
    public function setDateFormat(string $dateFormat): self
    {
        $this->dateFormat = $dateFormat;

        return $this;
    }

    /**
     * The maximum number of normalization levels to go through
     */
    public function getMaxNormalizeDepth(): int
    {
        return $this->maxNormalizeDepth;
    }

    /**
     * @return $this
     */
    public function setMaxNormalizeDepth(int $maxNormalizeDepth): self
    {
        $this->maxNormalizeDepth = $maxNormalizeDepth;

        return $this;
    }

    /**
     * The maximum number of items to normalize per level
     */
    public function getMaxNormalizeItemCount(): int
    {
        return $this->maxNormalizeItemCount;
    }

    /**
     * @return $this
     */
    public function setMaxNormalizeItemCount(int $maxNormalizeItemCount): self
    {
        $this->maxNormalizeItemCount = $maxNormalizeItemCount;

        return $this;
    }

    /**
     * The maximum number of stack trace frames to include
     */
    public function getMaxTraceLength(): ?int
    {
        return $this->maxTraceLength;
    }

    /**
     * @return $this
     */
    public function setMaxTraceLength(?int $maxTraceLength): self
    {
        $this->maxTraceLength = $maxTraceLength;

        return $this;
    }

    /**
     * Enables `json_encode` pretty print.
     *
     * @return $this
     */
    public function setJsonPrettyPrint(bool $enable): self
    {
        if ($enable) {
            $this->jsonEncodeOptions |= JSON_PRETTY_PRINT;
        } else {
            $this->jsonEncodeOptions &= ~JSON_PRETTY_PRINT;
        }

        return $this;
    }

    /**
     * Setting a base path will hide the base path from exception and stack trace file names to shorten them
     * @return $this
     */
    public function setBasePath(string $path = ''): self
    {
        if ($path !== '') {
            $path = rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
        }

        $this->basePath = $path;

        return $this;
    }

    /**
     * Provided as extension point
     *
     * Because normalize is called with sub-values of context data etc, normalizeRecord can be
     * extended when data needs to be appended on the record array but not to other normalized data.
     *
     * @return array<mixed[]|scalar|null>
     */
    protected function normalizeRecord(LogRecord $record): array
    {
        /** @var array<mixed[]|scalar|null> $normalized */
        $normalized = $this->normalize($record->toArray());

        return $normalized;
    }

    /**
     * @return null|scalar|array<mixed[]|scalar|null>
     */
    protected function normalize(mixed $data, int $depth = 0): mixed
    {
        if (null === $data || \is_scalar($data)) {
            if (\is_float($data)) {
                if (is_infinite($data)) {
                    return ($data > 0 ? '' : '-') . 'INF';
                }
                if (is_nan($data)) {
                    return 'NaN';
                }
            }

            return $data;
        }

        if ($depth > $this->maxNormalizeDepth) {
            return 'Over ' . $this->maxNormalizeDepth . ' levels deep, aborting normalization';
        }

        if (\is_array($data)) {
            $normalized = [];

            $count = 1;
            foreach ($data as $key => $value) {
                if ($count++ > $this->maxNormalizeItemCount) {
                    $normalized['...'] = 'Over ' . $this->maxNormalizeItemCount . ' items ('.\count($data).' total), aborting normalization';
                    break;
                }

                $normalized[$key] = $this->normalize($value, $depth + 1);
            }

            return $normalized;
        }

        if ($data instanceof \DateTimeInterface) {
            return $this->formatDate($data);
        }

        if (\is_object($data)) {
            if ($data instanceof Throwable) {
                return $this->normalizeException($data, $depth);
            }

            if ($data instanceof \JsonSerializable) {
                /** @var null|scalar|array<mixed[]|scalar|null> $value */
                $value = $data->jsonSerialize();
            } elseif (\get_class($data) === '__PHP_Incomplete_Class') {
                $accessor = new \ArrayObject($data);
                $value = (string) $accessor['__PHP_Incomplete_Class_Name'];
            } elseif (method_exists($data, '__toString')) {
                try {
                    /** @var string $value */
                    $value = $data->__toString();
                } catch (\Throwable) {
                    // if the toString method is failing, use the default behavior
                    /** @var null|scalar|array<mixed[]|scalar|null> $value */
                    $value = json_decode($this->toJson($data, true), true);
                }
            } else {
                // the rest is normalized by json encoding and decoding it
                /** @var null|scalar|array<mixed[]|scalar|null> $value */
                $value = json_decode($this->toJson($data, true), true);
            }

            return [Utils::getClass($data) => $value];
        }

        if (\is_resource($data)) {
            return sprintf('[resource(%s)]', get_resource_type($data));
        }

        return '[unknown('.\gettype($data).')]';
    }

    /**
     * @return array<array-key, string|int|array<string|int|array<string>>>
     */
    protected function normalizeException(Throwable $e, int $depth = 0)
    {
        if ($depth > $this->maxNormalizeDepth) {
            return ['Over ' . $this->maxNormalizeDepth . ' levels deep, aborting normalization'];
        }

        if ($e instanceof \JsonSerializable) {
            return (array) $e->jsonSerialize();
        }

        $file = $e->getFile();
        if ($this->basePath !== '') {
            $file = preg_replace('{^'.preg_quote($this->basePath).'}', '', $file);
        }

        $data = [
            'class' => Utils::getClass($e),
            'message' => $e->getMessage(),
            'code' => (int) $e->getCode(),
            'file' => $file.':'.$e->getLine(),
        ];

        if ($e instanceof \SoapFault) {
            if (isset($e->faultcode)) {
                $data['faultcode'] = $e->faultcode;
            }

            if (isset($e->faultactor)) {
                $data['faultactor'] = $e->faultactor;
            }

            if (isset($e->detail)) {
                if (\is_string($e->detail)) {
                    $data['detail'] = $e->detail;
                } elseif (\is_object($e->detail) || \is_array($e->detail)) {
                    $data['detail'] = $this->toJson($e->detail, true);
                }
            }
        }

        $trace = array_slice($e->getTrace(), 0, $this->maxTraceLength);
        foreach ($trace as $frame) {
            if (isset($frame['file'])) {
                $file = $frame['file'];
                if ($this->basePath !== '') {
                    $file = preg_replace('{^'.preg_quote($this->basePath).'}', '', $file) ?? $file;
                }
                $data['trace'][] = $file.':'.($frame['line'] ?? 0);
            } else {
                // Frames called by the engine itself have no file/line: shutdown functions,
                // callbacks run by internal functions, destructors. Skipping them made traces
                // look shorter than they were, and entirely empty for fatal errors caught in a
                // shutdown function, so they are reported by name instead.
                $call = $frame['function'];
                // since PHP 8.4 a closure is named after its declaring scope, which already
                // includes the class, so prefixing it again would just repeat it
                if (isset($frame['class']) && !str_starts_with($call, '{closure:')) {
                    // before 8.4 the name is <namespace>\{closure}, and the class has the namespace
                    $call = str_ends_with($call, '\{closure}') ? '{closure}' : $call;
                    $call = Utils::getClassName($frame['class']).($frame['type'] ?? '::').$call;
                }
                // anonymous classes carry their declaration site after a NUL byte, which truncates
                // syslog lines and is not valid JSON; PHP 8.4 embeds it in closure names too
                $call = preg_replace('{@anonymous\x00.*?\$[0-9a-f]++(?=::|$)}s', '@anonymous', $call) ?? $call;
                if ($this->basePath !== '') {
                    // closure names embed the file they were declared in since PHP 8.4, so the
                    // pattern cannot be anchored; limit it or a recurring base path is stripped twice
                    $call = preg_replace('{'.preg_quote($this->basePath).'}', '', $call, 1) ?? $call;
                }
                $data['trace'][] = 'internal['.$call.']:0';
            }
        }

        if (($previous = $e->getPrevious()) instanceof \Throwable) {
            $data['previous'] = $this->normalizeException($previous, $depth + 1);
        }

        return $data;
    }

    /**
     * Return the JSON representation of a value
     *
     * @param  mixed             $data
     * @throws \RuntimeException if encoding fails and errors are not ignored
     * @return string            if encoding fails and ignoreErrors is true 'null' is returned
     */
    protected function toJson($data, bool $ignoreErrors = false): string
    {
        return Utils::jsonEncode($data, $this->jsonEncodeOptions, $ignoreErrors);
    }

    protected function formatDate(\DateTimeInterface $date): string
    {
        // in case the date format isn't custom then we defer to the custom JsonSerializableDateTimeImmutable
        // formatting logic, which will pick the right format based on whether useMicroseconds is on
        if ($this->dateFormat === self::SIMPLE_DATE && $date instanceof JsonSerializableDateTimeImmutable) {
            return (string) $date;
        }

        return $date->format($this->dateFormat);
    }

    /**
     * @return $this
     */
    public function addJsonEncodeOption(int $option): self
    {
        $this->jsonEncodeOptions |= $option;

        return $this;
    }

    /**
     * @return $this
     */
    public function removeJsonEncodeOption(int $option): self
    {
        $this->jsonEncodeOptions &= ~$option;

        return $this;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Formatter;

use Monolog\Level;
use Monolog\LogRecord;

/**
 * Serializes a log message according to Wildfire's header requirements
 *
 * @author Eric Clemmons (@ericclemmons) <eric@uxdriven.com>
 * @author Christophe Coevoet <stof@notk.org>
 * @author Kirill chEbba Chebunin <iam@chebba.org>
 */
class WildfireFormatter extends NormalizerFormatter
{
    /**
     * @param string|null $dateFormat The format of the timestamp: one supported by DateTime::format
     */
    public function __construct(?string $dateFormat = null)
    {
        parent::__construct($dateFormat);

        // http headers do not like non-ISO-8559-1 characters
        $this->removeJsonEncodeOption(JSON_UNESCAPED_UNICODE);
    }

    /**
     * Translates Monolog log levels to Wildfire levels.
     *
     * @return 'LOG'|'INFO'|'WARN'|'ERROR'
     */
    private function toWildfireLevel(Level $level): string
    {
        return match ($level) {
            Level::Debug     => 'LOG',
            Level::Info      => 'INFO',
            Level::Notice    => 'INFO',
            Level::Warning   => 'WARN',
            Level::Error     => 'ERROR',
            Level::Critical  => 'ERROR',
            Level::Alert     => 'ERROR',
            Level::Emergency => 'ERROR',
        };
    }

    /**
     * @inheritDoc
     */
    public function format(LogRecord $record): string
    {
        // Retrieve the line and file if set and remove them from the formatted extra
        $file = $line = '';
        if (isset($record->extra['file'])) {
            $file = $record->extra['file'];
            unset($record->extra['file']);
        }
        if (isset($record->extra['line'])) {
            $line = $record->extra['line'];
            unset($record->extra['line']);
        }

        $message = ['message' => $record->message];
        $handleError = false;
        if (\count($record->context) > 0) {
            $message['context'] = $this->normalize($record->context);
            $handleError = true;
        }
        if (\count($record->extra) > 0) {
            $message['extra'] = $this->normalize($record->extra);
            $handleError = true;
        }
        if (\count($message) === 1) {
            $message = reset($message);
        }

        if (is_array($message) && isset($message['context']) && \is_array($message['context']) && isset($message['context']['table'])) {
            $type  = 'TABLE';
            $label = $record->channel .': '. $record->message;
            $message = $message['context']['table'];
        } else {
            $type  = $this->toWildfireLevel($record->level);
            $label = $record->channel;
        }

        // Create JSON object describing the appearance of the message in the console
        $json = $this->toJson([
            [
                'Type'  => $type,
                'File'  => $file,
                'Line'  => $line,
                'Label' => $label,
            ],
            $message,
        ], $handleError);

        // The message itself is a serialization of the above JSON object + it's length
        return sprintf(
            '%d|%s|',
            \strlen($json),
            $json
        );
    }

    /**
     * @inheritDoc
     *
     * @phpstan-return never
     */
    public function formatBatch(array $records)
    {
        throw new \BadMethodCallException('Batch formatting does not make sense for the WildfireFormatter');
    }

    /**
     * @inheritDoc
     *
     * @return null|scalar|array<mixed[]|scalar|null>|object
     */
    protected function normalize(mixed $data, int $depth = 0): mixed
    {
        if (\is_object($data) && !$data instanceof \DateTimeInterface) {
            return $data;
        }

        return parent::normalize($data, $depth);
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Formatter;

use Monolog\Utils;
use Monolog\LogRecord;

/**
 * Class FluentdFormatter
 *
 * Serializes a log message to Fluentd unix socket protocol
 *
 * Fluentd config:
 *
 * <source>
 *  type unix
 *  path /var/run/td-agent/td-agent.sock
 * </source>
 *
 * Monolog setup:
 *
 * $logger = new Monolog\Logger('fluent.tag');
 * $fluentHandler = new Monolog\Handler\SocketHandler('unix:///var/run/td-agent/td-agent.sock');
 * $fluentHandler->setFormatter(new Monolog\Formatter\FluentdFormatter());
 * $logger->pushHandler($fluentHandler);
 *
 * @author Andrius Putna <fordnox@gmail.com>
 */
class FluentdFormatter implements FormatterInterface
{
    /**
     * @var bool $levelTag should message level be a part of the fluentd tag
     */
    protected bool $levelTag = false;

    public function __construct(bool $levelTag = false)
    {
        $this->levelTag = $levelTag;
    }

    public function isUsingLevelsInTag(): bool
    {
        return $this->levelTag;
    }

    public function format(LogRecord $record): string
    {
        $tag = $record->channel;
        if ($this->levelTag) {
            $tag .= '.' . $record->level->toPsrLogLevel();
        }

        $message = [
            'message' => $record->message,
            'context' => $record->context,
            'extra' => $record->extra,
        ];

        if (!$this->levelTag) {
            $message['level'] = $record->level->value;
            $message['level_name'] = $record->level->getName();
        }

        return Utils::jsonEncode([$tag, $record->datetime->getTimestamp(), $message]);
    }

    public function formatBatch(array $records): string
    {
        $message = '';
        foreach ($records as $record) {
            $message .= $this->format($record);
        }

        return $message;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Formatter;

/**
 * Interface for formatters decorating another formatter
 *
 * Handlers which only accept one specific formatter can use Utils::unwrapFormatter()
 * to see through the decoration and accept a wrapped instance of that formatter.
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 */
interface WrappingFormatterInterface extends FormatterInterface
{
    /**
     * Returns the formatter this one delegates the actual formatting to
     */
    public function getWrappedFormatter(): FormatterInterface;
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Formatter;

use Monolog\LogRecord;

/**
 * Encodes message information into JSON in a format compatible with Logmatic.
 *
 * @author Julien Breux <julien.breux@gmail.com>
 */
class LogmaticFormatter extends JsonFormatter
{
    protected const MARKERS = ["sourcecode", "php"];

    protected string $hostname = '';

    protected string $appName = '';

    /**
     * @return $this
     */
    public function setHostname(string $hostname): self
    {
        $this->hostname = $hostname;

        return $this;
    }

    /**
     * @return $this
     */
    public function setAppName(string $appName): self
    {
        $this->appName = $appName;

        return $this;
    }

    /**
     * Appends the 'hostname' and 'appname' parameter for indexing by Logmatic.
     *
     * @see http://doc.logmatic.io/docs/basics-to-send-data
     * @see \Monolog\Formatter\JsonFormatter::format()
     */
    public function normalizeRecord(LogRecord $record): array
    {
        $record = parent::normalizeRecord($record);

        if ($this->hostname !== '') {
            $record["hostname"] = $this->hostname;
        }
        if ($this->appName !== '') {
            $record["appname"] = $this->appName;
        }

        $record["@marker"] = static::MARKERS;

        return $record;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Formatter;

use Monolog\LogRecord;
use Monolog\Utils;

/**
 * Wraps another formatter to redact sensitive data from log records.
 *
 * It works in three passes so that it can mask secrets wherever they end up:
 *
 *  - Before delegating, it masks the value of any context/extra key whose name
 *    matches one of the configured sensitive keys (case-insensitive, recursive).
 *  - After delegating, it removes those same secret values from the wrapped
 *    formatter's output, which catches the copies masking the record cannot
 *    reach, e.g. a secret interpolated into the message by the
 *    PsrLogMessageProcessor or embedded in an exception message.
 *  - Last, it runs the configured regex patterns over the output, which catches
 *    secrets that only became strings once the inner formatter normalized
 *    objects (e.g. tokens buried in an exception stack trace). If a pattern
 *    cannot be applied, the record is dropped instead of emitted unredacted.
 *
 * On top of the sensitive keys, secrets are also read from the properties of the
 * objects in the record matching a constructor parameter marked
 * #[SensitiveParameter]. Those can only be handled by the output pass, as the
 * objects carrying them cannot be modified. This is the one part that reflects on
 * every logged object, so it can be turned off with $redactSensitiveParameters.
 *
 * Only string and array output can be swept, so wrapping a formatter which returns
 * objects (e.g. the ElasticaFormatter) leaves you with the key masking alone.
 *
 * Because it is a formatter rather than a processor, it is guaranteed to run
 * after every processor (both Logger- and Handler-level), so it always sees the
 * final record with all data at hand.
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 */
final class RedactingFormatter implements WrappingFormatterInterface
{
    /**
     * Matches long token-like words (30+ alphanumerics, optionally prefixed like api_key_),
     * which are very likely to be secrets/tokens. Provided as an opt-in pattern as it may
     * cause collateral damage on legitimate long strings (hashes, UUIDs, base64, ...).
     */
    public const TOKEN_PATTERN = '{\b(?:[a-z]+_)*[a-zA-Z0-9]{30,}\b}';

    /**
     * How deep to walk the record and the output, mirrors NormalizerFormatter's default
     */
    private const MAX_DEPTH = 9;

    /** @var list<string> Lowercased sensitive keys */
    private array $sensitiveKeys;

    /** @var list<string> */
    private array $patterns;

    /** @var array<class-string, array<string, \ReflectionProperty>> */
    private array $sensitiveProperties = [];

    /**
     * @param FormatterInterface $formatter                 The formatter to wrap and whose output gets redacted
     * @param list<string>       $sensitiveKeys             Exact context/extra keys whose values to mask (case-insensitive)
     * @param list<string>       $patterns                  PCRE patterns to mask in the formatted output (e.g. self::TOKEN_PATTERN)
     * @param string             $mask                      Replacement token
     * @param bool               $redactSensitiveParameters Whether to also read secrets off the properties matching a
     *                                                      #[SensitiveParameter] constructor parameter, which requires
     *                                                      walking and reflecting on every object in the record
     * @param int                $minSecretLength           Minimum length a secret must have to be removed from the
     *                                                      output, to avoid short values like "pass" redacting
     *                                                      unrelated data
     *
     * @throws \InvalidArgumentException If a pattern is not a valid PCRE regex
     */
    public function __construct(
        private readonly FormatterInterface $formatter,
        array $sensitiveKeys = ['password', 'passwd', 'pwd', 'secret', 'token', 'api_key', 'apikey', 'authorization', 'auth', 'cookie'],
        array $patterns = [],
        private readonly string $mask = '[REDACTED]',
        private readonly bool $redactSensitiveParameters = true,
        private readonly int $minSecretLength = 5,
    ) {
        $this->sensitiveKeys = array_values(array_map('strtolower', $sensitiveKeys));

        foreach ($patterns as $pattern) {
            if (@preg_match($pattern, '') === false) {
                throw new \InvalidArgumentException(sprintf('Invalid redaction pattern provided to RedactingFormatter: %s', var_export($pattern, true)));
            }
        }
        $this->patterns = array_values($patterns);
    }

    public function getWrappedFormatter(): FormatterInterface
    {
        return $this->formatter;
    }

    public function format(LogRecord $record)
    {
        // secrets must be collected before the keys get masked, as masking loses the values
        $secrets = $this->collectSecrets([$record]);

        return $this->sweep($this->formatter->format($this->redactRecord($record)), $secrets);
    }

    public function formatBatch(array $records)
    {
        $secrets = $this->collectSecrets($records);

        foreach ($records as $key => $record) {
            $records[$key] = $this->redactRecord($record);
        }

        return $this->sweep($this->formatter->formatBatch($records), $secrets);
    }

    private function redactRecord(LogRecord $record): LogRecord
    {
        return $record->with(
            context: $this->redactKeys($record->context),
            extra: $this->redactKeys($record->extra),
        );
    }

    /**
     * @param  array<mixed> $data
     * @return array<mixed>
     */
    private function redactKeys(array $data, int $depth = 0): array
    {
        // context comes from userland and can hold a self-referencing array
        if ($depth > self::MAX_DEPTH) {
            return $data;
        }

        foreach ($data as $key => $value) {
            if (\is_string($key) && \in_array(strtolower($key), $this->sensitiveKeys, true)) {
                $data[$key] = $this->mask;
            } elseif (\is_array($value)) {
                $data[$key] = $this->redactKeys($value, $depth + 1);
            }
        }

        return $data;
    }

    /**
     * Gathers the secret values present in the given records, longest first
     *
     * @param  iterable<LogRecord> $records
     * @return list<string>
     */
    private function collectSecrets(iterable $records): array
    {
        $secrets = [];
        $seen = [];
        foreach ($records as $record) {
            $this->collect($record->context, $secrets, $seen, 0);
            $this->collect($record->extra, $secrets, $seen, 0);
        }

        if ([] === $secrets) {
            return [];
        }

        // a secret shows up escaped in json output (quotes/backslashes/newlines) and in html output
        // (& < >), so every spelling the wrapped formatter can produce has to be looked for
        $escapedSecrets = [];
        foreach ($secrets as $secret) {
            foreach ([substr(Utils::jsonEncode($secret, null, true), 1, -1), htmlspecialchars($secret, ENT_NOQUOTES, 'UTF-8')] as $escaped) {
                if ($escaped !== $secret && $escaped !== '') {
                    $escapedSecrets[] = $escaped;
                }
            }
        }

        $secrets = array_values(array_unique(array_merge($secrets, $escapedSecrets)));
        // longest first, so that overlapping secrets do not leave fragments behind
        usort($secrets, static fn (string $a, string $b) => \strlen($b) <=> \strlen($a));

        return $secrets;
    }

    /**
     * @param list<string>      $secrets
     * @param array<int, true>  $seen    Ids of the objects visited so far, to survive cyclic graphs
     */
    private function collect(mixed $data, array &$secrets, array &$seen, int $depth): void
    {
        if ($depth > self::MAX_DEPTH) {
            return;
        }

        if (\is_array($data)) {
            foreach ($data as $key => $value) {
                if (\is_string($key) && \in_array(strtolower($key), $this->sensitiveKeys, true)) {
                    // the value is masked in the record, but a nested array's leaves can still
                    // leak elsewhere (e.g. json-encoded by PsrLogMessageProcessor)
                    $this->collectSensitiveValue($value, $secrets, $depth + 1);

                    continue;
                }

                $this->collect($value, $secrets, $seen, $depth + 1);
            }

            return;
        }

        // the array walk above is essentially free, reflecting on objects is not
        if (!\is_object($data) || !$this->redactSensitiveParameters) {
            return;
        }

        $id = spl_object_id($data);
        if (isset($seen[$id])) {
            return;
        }
        $seen[$id] = true;

        foreach ($this->getSensitiveProperties($data) as $property) {
            if ($property->isInitialized($data)) {
                $this->addSecret($property->getValue($data), $secrets);
            }
        }

        // public properties only, matching what formatters get to see, but enough to reach
        // objects nested inside this one
        foreach (get_object_vars($data) as $value) {
            $this->collect($value, $secrets, $seen, $depth + 1);
        }
    }

    /**
     * Collects every string leaf nested inside a value under a sensitive key.
     *
     * @param list<string> $secrets
     */
    private function collectSensitiveValue(mixed $value, array &$secrets, int $depth): void
    {
        if ($depth > self::MAX_DEPTH) {
            return;
        }

        if (\is_array($value)) {
            foreach ($value as $item) {
                $this->collectSensitiveValue($item, $secrets, $depth + 1);
            }

            return;
        }

        $this->addSecret($value, $secrets);
    }

    /**
     * Reflects the properties of an object matching a #[SensitiveParameter] constructor parameter
     *
     * Non-public properties are included as they can still leak through __toString(), and the
     * result is cached per class as reflecting on every logged object would be far too costly.
     *
     * @return array<string, \ReflectionProperty>
     */
    private function getSensitiveProperties(object $data): array
    {
        $class = $data::class;
        if (isset($this->sensitiveProperties[$class])) {
            return $this->sensitiveProperties[$class];
        }

        $names = Utils::getSensitiveParameterNames($class);

        // PHP 8.2+ wraps the arguments it hides from stack traces in a SensitiveParameterValue,
        // whose own constructor parameter is not marked. Matched by name and read as a property
        // so that it does not matter whether symfony/polyfill-php82 is installed on PHP 8.1:
        // naming the class makes PHPStan report either the @internal getValue() the polyfill
        // inherits, or a missing class when it is absent.
        if ('SensitiveParameterValue' === $class) {
            $names['value'] = true;
        }

        $properties = [];
        foreach ($names as $name => $_) {
            // walking up is required as a private property of a parent class is not
            // reachable through the child's ReflectionClass
            for ($reflection = new \ReflectionClass($class); false !== $reflection; $reflection = $reflection->getParentClass()) {
                if ($reflection->hasProperty($name)) {
                    $properties[$name] = $reflection->getProperty($name);

                    break;
                }
            }
        }

        return $this->sensitiveProperties[$class] = $properties;
    }

    /**
     * @param list<string> $secrets
     */
    private function addSecret(mixed $value, array &$secrets): void
    {
        // a Stringable secret ends up in the output as its string form, so that is what
        // has to be looked for
        if ($value instanceof \Stringable) {
            try {
                $value = $value->__toString();
            } catch (\Throwable) {
                return;
            }
        }

        // ints/floats are left out on purpose, sweeping a digit run out of the output
        // would silently corrupt unrelated data
        if (\is_string($value) && $value !== '' && \strlen($value) >= $this->minSecretLength) {
            $secrets[] = $value;
        }
    }

    /**
     * Removes the collected secrets and applies the configured patterns to the formatter output,
     * recursing into arrays to support formatters that do not return a string (e.g. MongoDBFormatter).
     *
     * @param list<string> $secrets
     */
    private function sweep(mixed $formatted, array $secrets, int $depth = 0): mixed
    {
        if ([] === $this->patterns && [] === $secrets) {
            return $formatted;
        }

        // deeper than any formatter will render anyway, and a custom one may well
        // hand back a self-referencing array
        if ($depth > self::MAX_DEPTH) {
            return $formatted;
        }

        if (\is_string($formatted)) {
            if ([] !== $secrets) {
                $formatted = str_replace($secrets, $this->mask, $formatted);
            }
            if ([] !== $this->patterns) {
                $replaced = preg_replace($this->patterns, $this->mask, $formatted);
                if (null === $replaced) {
                    // the patterns could not be applied so the output cannot be trusted, drop it
                    // entirely. Neither the patterns (which may embed a secret themselves) nor the
                    // mask are reported, preg_last_error_msg() is one of a fixed set of PCRE strings
                    return '[RedactingFormatter dropped this record, pattern redaction failed with "'.preg_last_error_msg().'"]';
                }
                $formatted = $replaced;
            }

            return $formatted;
        }

        if (\is_array($formatted)) {
            foreach ($formatted as $key => $value) {
                $formatted[$key] = $this->sweep($value, $secrets, $depth + 1);
            }
        }

        return $formatted;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Formatter;

use Monolog\LogRecord;

/**
 * Formats data into an associative array of scalar (+ null) values.
 * Objects and arrays will be JSON encoded.
 *
 * @author Andrew Lawson <adlawson@gmail.com>
 */
class ScalarFormatter extends NormalizerFormatter
{
    /**
     * @inheritDoc
     *
     * @phpstan-return array<string, scalar|null> $record
     */
    public function format(LogRecord $record): array
    {
        $result = [];
        foreach ($record->toArray() as $key => $value) {
            $result[$key] = $this->toScalar($value);
        }

        return $result;
    }

    protected function toScalar(mixed $value): string|int|float|bool|null
    {
        $normalized = $this->normalize($value);

        if (\is_array($normalized)) {
            return $this->toJson($normalized, true);
        }

        return $normalized;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Formatter;

use Monolog\LogRecord;

/**
 * Encodes message information into JSON in a format compatible with Loggly.
 *
 * @author Adam Pancutt <adam@pancutt.com>
 */
class LogglyFormatter extends JsonFormatter
{
    /**
     * Overrides the default batch mode to new lines for compatibility with the
     * Loggly bulk API.
     */
    public function __construct(int $batchMode = self::BATCH_MODE_NEWLINES, bool $appendNewline = false)
    {
        parent::__construct($batchMode, $appendNewline);
    }

    /**
     * Appends the 'timestamp' parameter for indexing by Loggly.
     *
     * @see https://www.loggly.com/docs/automated-parsing/#json
     * @see \Monolog\Formatter\JsonFormatter::format()
     */
    protected function normalizeRecord(LogRecord $record): array
    {
        $recordData = parent::normalizeRecord($record);

        $recordData["timestamp"] = $record->datetime->format("Y-m-d\TH:i:s.uO");
        unset($recordData["datetime"]);

        return $recordData;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Formatter;

use Monolog\LogRecord;

/**
 * Interface for formatters
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 */
interface FormatterInterface
{
    /**
     * Formats a log record.
     *
     * @param  LogRecord $record A record to format
     * @return mixed     The formatted record
     */
    public function format(LogRecord $record);

    /**
     * Formats a set of log records.
     *
     * @param  array<LogRecord> $records A set of records to format
     * @return mixed            The formatted set of records
     */
    public function formatBatch(array $records);
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Formatter;

use Monolog\Level;
use Gelf\Message;
use Monolog\Utils;
use Monolog\LogRecord;

/**
 * Serializes a log message to GELF
 * @see http://docs.graylog.org/en/latest/pages/gelf.html
 *
 * @author Matt Lehner <mlehner@gmail.com>
 */
class GelfMessageFormatter extends NormalizerFormatter
{
    protected const DEFAULT_MAX_LENGTH = 32766;

    /**
     * @var string the name of the system for the Gelf log message
     */
    protected string $systemName;

    /**
     * @var string a prefix for 'extra' fields from the Monolog record (optional)
     */
    protected string $extraPrefix;

    /**
     * @var string a prefix for 'context' fields from the Monolog record (optional)
     */
    protected string $contextPrefix;

    /**
     * @var int max length per field
     */
    protected int $maxLength;

    /**
     * Translates Monolog log levels to Graylog2 log priorities.
     */
    private function getGraylog2Priority(Level $level): int
    {
        return match ($level) {
            Level::Debug     => 7,
            Level::Info      => 6,
            Level::Notice    => 5,
            Level::Warning   => 4,
            Level::Error     => 3,
            Level::Critical  => 2,
            Level::Alert     => 1,
            Level::Emergency => 0,
        };
    }

    /**
     * @throws \RuntimeException
     */
    public function __construct(?string $systemName = null, ?string $extraPrefix = null, string $contextPrefix = 'ctxt_', ?int $maxLength = null)
    {
        if (!class_exists(Message::class)) {
            throw new \RuntimeException('Composer package graylog2/gelf-php is required to use Monolog\'s GelfMessageFormatter');
        }

        parent::__construct('U.u');

        $this->systemName = (null === $systemName || $systemName === '') ? (string) gethostname() : $systemName;

        $this->extraPrefix = null === $extraPrefix ? '' : $extraPrefix;
        $this->contextPrefix = $contextPrefix;
        $this->maxLength = null === $maxLength ? self::DEFAULT_MAX_LENGTH : $maxLength;
    }

    /**
     * @inheritDoc
     */
    public function format(LogRecord $record): Message
    {
        $context = $extra = [];
        if ($record->context !== []) {
            /** @var array<array<mixed>|bool|float|int|string|null> $context */
            $context = parent::normalize($record->context);
        }
        if ($record->extra !== []) {
            /** @var array<array<mixed>|bool|float|int|string|null> $extra */
            $extra = parent::normalize($record->extra);
        }

        $message = new Message();
        $message
            ->setTimestamp($record->datetime)
            ->setShortMessage($record->message)
            ->setHost($this->systemName)
            ->setLevel($this->getGraylog2Priority($record->level));

        // message length + system name length + 200 for padding / metadata
        $len = 200 + \strlen($record->message) + \strlen($this->systemName);

        if ($len > $this->maxLength) {
            $message->setShortMessage(Utils::substr($record->message, 0, $this->maxLength));
        }

        if ($record->channel !== '') {
            $message->setAdditional('facility', $record->channel);
        }

        foreach ($extra as $key => $val) {
            $key = (string) preg_replace('#[^\w.-]#', '-', (string) $key);
            $val = \is_bool($val) ? ($val ? 1 : 0) : $val;
            $val = \is_scalar($val) || null === $val ? $val : $this->toJson($val);
            $len = \strlen($this->extraPrefix . $key . $val);
            if ($len > $this->maxLength) {
                $message->setAdditional($this->extraPrefix . $key, Utils::substr((string) $val, 0, $this->maxLength));

                continue;
            }
            $message->setAdditional($this->extraPrefix . $key, $val);
        }

        foreach ($context as $key => $val) {
            $key = (string) preg_replace('#[^\w.-]#', '-', (string) $key);
            $val = \is_bool($val) ? ($val ? 1 : 0) : $val;
            $val = \is_scalar($val) || null === $val ? $val : $this->toJson($val);
            $len = \strlen($this->contextPrefix . $key . $val);
            if ($len > $this->maxLength) {
                $message->setAdditional($this->contextPrefix . $key, Utils::substr((string) $val, 0, $this->maxLength));

                continue;
            }
            $message->setAdditional($this->contextPrefix . $key, $val);
        }

        if (!$message->hasAdditional('file') && isset($context['exception']['file'])) {
            if (1 === preg_match("/^(.+):([0-9]+)$/", $context['exception']['file'], $matches)) {
                $message->setAdditional('file', $matches[1]);
                $message->setAdditional('line', $matches[2]);
            }
        }

        return $message;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Formatter;

use Monolog\Level;
use Monolog\Utils;
use Monolog\LogRecord;

/**
 * Formats incoming records into an HTML table
 *
 * This is especially useful for html email logging
 *
 * @author Tiago Brito <tlfbrito@gmail.com>
 */
class HtmlFormatter extends NormalizerFormatter
{
    /**
     * Translates Monolog log levels to html color priorities.
     */
    protected function getLevelColor(Level $level): string
    {
        return match ($level) {
            Level::Debug     => '#CCCCCC',
            Level::Info      => '#28A745',
            Level::Notice    => '#17A2B8',
            Level::Warning   => '#FFC107',
            Level::Error     => '#FD7E14',
            Level::Critical  => '#DC3545',
            Level::Alert     => '#821722',
            Level::Emergency => '#000000',
        };
    }

    /**
     * @param string|null $dateFormat The format of the timestamp: one supported by DateTime::format
     */
    public function __construct(?string $dateFormat = null)
    {
        parent::__construct($dateFormat);
    }

    /**
     * Creates an HTML table row
     *
     * @param string $th       Row header content
     * @param string $td       Row standard cell content
     * @param bool   $escapeTd false if td content must not be html escaped
     */
    protected function addRow(string $th, string $td = ' ', bool $escapeTd = true): string
    {
        $th = htmlspecialchars($th, ENT_NOQUOTES, 'UTF-8');
        if ($escapeTd) {
            $td = '<pre>'.htmlspecialchars($td, ENT_NOQUOTES, 'UTF-8').'</pre>';
        }

        return "<tr style=\"padding: 4px;text-align: left;\">\n<th style=\"vertical-align: top;background: #ccc;color: #000\" width=\"100\">$th:</th>\n<td style=\"padding: 4px;text-align: left;vertical-align: top;background: #eee;color: #000\">".$td."</td>\n</tr>";
    }

    /**
     * Create a HTML h1 tag
     *
     * @param string $title Text to be in the h1
     */
    protected function addTitle(string $title, Level $level): string
    {
        $title = htmlspecialchars($title, ENT_NOQUOTES, 'UTF-8');

        return '<h1 style="background: '.$this->getLevelColor($level).';color: #ffffff;padding: 5px;" class="monolog-output">'.$title.'</h1>';
    }

    /**
     * Formats a log record.
     *
     * @return string The formatted record
     */
    public function format(LogRecord $record): string
    {
        $output = $this->addTitle($record->level->getName(), $record->level);
        $output .= '<table cellspacing="1" width="100%" class="monolog-output">';

        $output .= $this->addRow('Message', $record->message);
        $output .= $this->addRow('Time', $this->formatDate($record->datetime));
        $output .= $this->addRow('Channel', $record->channel);
        if (\count($record->context) > 0) {
            $embeddedTable = '<table cellspacing="1" width="100%">';
            foreach ($record->context as $key => $value) {
                $embeddedTable .= $this->addRow((string) $key, $this->convertToString($value));
            }
            $embeddedTable .= '</table>';
            $output .= $this->addRow('Context', $embeddedTable, false);
        }
        if (\count($record->extra) > 0) {
            $embeddedTable = '<table cellspacing="1" width="100%">';
            foreach ($record->extra as $key => $value) {
                $embeddedTable .= $this->addRow((string) $key, $this->convertToString($value));
            }
            $embeddedTable .= '</table>';
            $output .= $this->addRow('Extra', $embeddedTable, false);
        }

        return $output.'</table>';
    }

    /**
     * Formats a set of log records.
     *
     * @return string The formatted set of records
     */
    public function formatBatch(array $records): string
    {
        $message = '';
        foreach ($records as $record) {
            $message .= $this->format($record);
        }

        return $message;
    }

    /**
     * @param mixed $data
     */
    protected function convertToString($data): string
    {
        if (null === $data || \is_scalar($data)) {
            return (string) $data;
        }

        $data = $this->normalize($data);

        return Utils::jsonEncode($data, JSON_PRETTY_PRINT | Utils::DEFAULT_JSON_FLAGS, true);
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Formatter;

use DateTimeInterface;
use Monolog\LogRecord;

/**
 * Encodes message information into JSON in a format compatible with Cloud logging.
 *
 * @see https://cloud.google.com/logging/docs/structured-logging
 * @see https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry
 *
 * @author LuÃ­s Cobucci <lcobucci@gmail.com>
 */
class GoogleCloudLoggingFormatter extends JsonFormatter
{
    protected function normalizeRecord(LogRecord $record): array
    {
        $normalized = parent::normalizeRecord($record);

        // Re-key level for GCP logging
        $normalized['severity'] = $normalized['level_name'];
        $normalized['time'] = $record->datetime->format(DateTimeInterface::RFC3339_EXTENDED);

        // Remove keys that are not used by GCP
        unset($normalized['level'], $normalized['level_name'], $normalized['datetime']);

        return $normalized;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Formatter;

use Elastica\Document;
use Monolog\LogRecord;

/**
 * Format a log message into an Elastica Document
 *
 * @author Jelle Vink <jelle.vink@gmail.com>
 */
class ElasticaFormatter extends NormalizerFormatter
{
    /**
     * @var string Elastic search index name
     */
    protected string $index;

    /**
     * @var string|null Elastic search document type
     */
    protected string|null $type;

    /**
     * @param string  $index Elastic Search index name
     * @param ?string $type  Elastic Search document type, deprecated as of Elastica 7
     */
    public function __construct(string $index, ?string $type)
    {
        // elasticsearch requires a ISO 8601 format date with optional millisecond precision.
        parent::__construct('Y-m-d\TH:i:s.uP');

        $this->index = $index;
        $this->type = $type;
    }

    /**
     * @inheritDoc
     */
    public function format(LogRecord $record)
    {
        $record = parent::format($record);

        return $this->getDocument($record);
    }

    public function getIndex(): string
    {
        return $this->index;
    }

    /**
     * @deprecated since Elastica 7 type has no effect
     */
    public function getType(): string
    {
        /** @phpstan-ignore-next-line */
        return $this->type;
    }

    /**
     * Convert a log message into an Elastica Document
     *
     * @param mixed[] $record
     */
    protected function getDocument(array $record): Document
    {
        $document = new Document();
        $document->setData($record);
        $document->setIndex($this->index);

        return $document;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Formatter;

use Stringable;
use Throwable;
use Monolog\LogRecord;

/**
 * Encodes whatever record data is passed to it as json
 *
 * This can be useful to log to databases or remote APIs
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 */
class JsonFormatter extends NormalizerFormatter
{
    public const BATCH_MODE_JSON = 1;
    public const BATCH_MODE_NEWLINES = 2;

    /** @var self::BATCH_MODE_* */
    protected int $batchMode;

    protected bool $appendNewline;

    protected bool $ignoreEmptyContextAndExtra;

    protected bool $includeStacktraces = false;

    /**
     * @param self::BATCH_MODE_* $batchMode
     */
    public function __construct(int $batchMode = self::BATCH_MODE_JSON, bool $appendNewline = true, bool $ignoreEmptyContextAndExtra = false, bool $includeStacktraces = false)
    {
        $this->batchMode = $batchMode;
        $this->appendNewline = $appendNewline;
        $this->ignoreEmptyContextAndExtra = $ignoreEmptyContextAndExtra;
        $this->includeStacktraces = $includeStacktraces;

        parent::__construct();
    }

    /**
     * The batch mode option configures the formatting style for
     * multiple records. By default, multiple records will be
     * formatted as a JSON-encoded array. However, for
     * compatibility with some API endpoints, alternative styles
     * are available.
     */
    public function getBatchMode(): int
    {
        return $this->batchMode;
    }

    /**
     * True if newlines are appended to every formatted record
     */
    public function isAppendingNewlines(): bool
    {
        return $this->appendNewline;
    }

    /**
     * @inheritDoc
     */
    public function format(LogRecord $record): string
    {
        $normalized = $this->normalizeRecord($record);

        return $this->toJson($normalized, true) . ($this->appendNewline ? "\n" : '');
    }

    /**
     * @inheritDoc
     */
    public function formatBatch(array $records): string
    {
        return match ($this->batchMode) {
            static::BATCH_MODE_NEWLINES => $this->formatBatchNewlines($records),
            default => $this->formatBatchJson($records),
        };
    }

    /**
     * @return $this
     */
    public function includeStacktraces(bool $include = true): self
    {
        $this->includeStacktraces = $include;

        return $this;
    }

    /**
     * @return array<array<mixed>|bool|float|int|\stdClass|string|null>
     */
    protected function normalizeRecord(LogRecord $record): array
    {
        $normalized = parent::normalizeRecord($record);

        if (isset($normalized['context']) && $normalized['context'] === []) {
            if ($this->ignoreEmptyContextAndExtra) {
                unset($normalized['context']);
            } else {
                $normalized['context'] = new \stdClass;
            }
        }
        if (isset($normalized['extra']) && $normalized['extra'] === []) {
            if ($this->ignoreEmptyContextAndExtra) {
                unset($normalized['extra']);
            } else {
                $normalized['extra'] = new \stdClass;
            }
        }

        return $normalized;
    }

    /**
     * Return a JSON-encoded array of records.
     *
     * @phpstan-param LogRecord[] $records
     */
    protected function formatBatchJson(array $records): string
    {
        $formatted = array_map(fn (LogRecord $record) => $this->normalizeRecord($record), $records);

        return $this->toJson($formatted, true);
    }

    /**
     * Use new lines to separate records instead of a
     * JSON-encoded array.
     *
     * @phpstan-param LogRecord[] $records
     */
    protected function formatBatchNewlines(array $records): string
    {
        $oldNewline = $this->appendNewline;
        $this->appendNewline = false;
        $formatted = array_map(fn (LogRecord $record) => $this->format($record), $records);
        $this->appendNewline = $oldNewline;

        return implode("\n", $formatted);
    }

    /**
     * Normalizes given $data.
     *
     * @return null|scalar|array<mixed[]|scalar|null|object>|object
     */
    protected function normalize(mixed $data, int $depth = 0): mixed
    {
        if (is_null($data) || is_scalar($data)) {
            return $data;
        }

        if ($depth > $this->maxNormalizeDepth) {
            return 'Over '.$this->maxNormalizeDepth.' levels deep, aborting normalization';
        }

        if (\is_array($data)) {
            $normalized = [];

            $count = 1;
            foreach ($data as $key => $value) {
                if ($count++ > $this->maxNormalizeItemCount) {
                    $normalized['...'] = 'Over '.$this->maxNormalizeItemCount.' items ('.\count($data).' total), aborting normalization';
                    break;
                }

                $normalized[$key] = $this->normalize($value, $depth + 1);
            }

            return $normalized;
        }

        if (\is_object($data)) {
            if ($data instanceof \DateTimeInterface) {
                return $this->formatDate($data);
            }

            if ($data instanceof Throwable) {
                return $this->normalizeException($data, $depth);
            }

            // if the object has specific json serializability we want to make sure we skip the __toString treatment below
            if ($data instanceof \JsonSerializable) {
                return $data;
            }

            if ($data instanceof Stringable) {
                try {
                    return $data->__toString();
                } catch (Throwable) {
                    return $data::class;
                }
            }

            if (\get_class($data) === '__PHP_Incomplete_Class') {
                return new \ArrayObject($data);
            }

            return $data;
        }

        return parent::normalize($data);
    }

    /**
     * Normalizes given exception with or without its own stack trace based on
     * `includeStacktraces` property.
     *
     * @return array<array-key, string|int|array<string|int|array<string>>>
     */
    protected function normalizeException(Throwable $e, int $depth = 0): array
    {
        $data = parent::normalizeException($e, $depth);
        if (!$this->includeStacktraces) {
            unset($data['trace']);
        }

        return $data;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Formatter;

use Closure;
use Monolog\Utils;
use Monolog\LogRecord;

/**
 * Formats incoming records into a one-line string
 *
 * This is especially useful for logging to files
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 * @author Christophe Coevoet <stof@notk.org>
 */
class LineFormatter extends NormalizerFormatter
{
    public const SIMPLE_FORMAT = "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n";

    protected string $format;
    protected bool $allowInlineLineBreaks;
    protected bool $ignoreEmptyContextAndExtra;
    protected bool $includeStacktraces;
    protected ?int $maxLevelNameLength = null;
    protected string $indentStacktraces = '';
    protected Closure|null $stacktracesParser = null;
    protected string $basePath = '';

    /**
     * @param string|null $format                The format of the message
     * @param string|null $dateFormat            The format of the timestamp: one supported by DateTime::format
     * @param bool        $allowInlineLineBreaks Whether to allow inline line breaks in log entries
     */
    public function __construct(?string $format = null, ?string $dateFormat = null, bool $allowInlineLineBreaks = false, bool $ignoreEmptyContextAndExtra = false, bool $includeStacktraces = false)
    {
        $this->format = $format === null ? static::SIMPLE_FORMAT : $format;
        $this->allowInlineLineBreaks = $allowInlineLineBreaks;
        $this->ignoreEmptyContextAndExtra = $ignoreEmptyContextAndExtra;
        $this->includeStacktraces($includeStacktraces);
        parent::__construct($dateFormat);
    }

    /**
     * Setting a base path will hide the base path from exception and stack trace file names to shorten them
     * @return $this
     */
    public function setBasePath(string $path = ''): self
    {
        if ($path !== '') {
            $path = rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
        }

        $this->basePath = $path;

        return $this;
    }

    /**
     * @return $this
     */
    public function includeStacktraces(bool $include = true, ?Closure $parser = null): self
    {
        $this->includeStacktraces = $include;
        if ($this->includeStacktraces) {
            $this->allowInlineLineBreaks = true;
            $this->stacktracesParser = $parser;
        }

        return $this;
    }

    /**
     * Indent stack traces to separate them a bit from the main log record messages
     *
     * @param  string $indent The string used to indent, for example "    "
     * @return $this
     */
    public function indentStacktraces(string $indent): self
    {
        $this->indentStacktraces = $indent;

        return $this;
    }

    /**
     * @return $this
     */
    public function allowInlineLineBreaks(bool $allow = true): self
    {
        $this->allowInlineLineBreaks = $allow;

        return $this;
    }

    /**
     * @return $this
     */
    public function ignoreEmptyContextAndExtra(bool $ignore = true): self
    {
        $this->ignoreEmptyContextAndExtra = $ignore;

        return $this;
    }

    /**
     * Allows cutting the level name to get fixed-length levels like INF for INFO, ERR for ERROR if you set this to 3 for example
     *
     * @param  int|null $maxLevelNameLength Maximum characters for the level name. Set null for infinite length (default)
     * @return $this
     */
    public function setMaxLevelNameLength(?int $maxLevelNameLength = null): self
    {
        $this->maxLevelNameLength = $maxLevelNameLength;

        return $this;
    }

    /**
     * @inheritDoc
     */
    public function format(LogRecord $record): string
    {
        $vars = parent::format($record);

        if ($this->maxLevelNameLength !== null) {
            $vars['level_name'] = substr($vars['level_name'], 0, $this->maxLevelNameLength);
        }

        $output = $this->format;
        foreach ($vars['extra'] as $var => $val) {
            if (false !== strpos($output, '%extra.'.$var.'%')) {
                $output = str_replace('%extra.'.$var.'%', $this->stringify($val), $output);
                unset($vars['extra'][$var]);
            }
        }

        foreach ($vars['context'] as $var => $val) {
            if (false !== strpos($output, '%context.'.$var.'%')) {
                $output = str_replace('%context.'.$var.'%', $this->stringify($val), $output);
                unset($vars['context'][$var]);
            }
        }

        if ($this->ignoreEmptyContextAndExtra) {
            if (\count($vars['context']) === 0) {
                unset($vars['context']);
                $output = str_replace('%context%', '', $output);
            }

            if (\count($vars['extra']) === 0) {
                unset($vars['extra']);
                $output = str_replace('%extra%', '', $output);
            }
        }

        foreach ($vars as $var => $val) {
            if (false !== strpos($output, '%'.$var.'%')) {
                $output = str_replace('%'.$var.'%', $this->stringify($val), $output);
            }
        }

        // remove leftover %extra.xxx% and %context.xxx% if any
        if (false !== strpos($output, '%')) {
            $output = preg_replace('/%(?:extra|context)\..+?%/', '', $output);
            if (null === $output) {
                $pcreErrorCode = preg_last_error();

                throw new \RuntimeException('Failed to run preg_replace: ' . $pcreErrorCode . ' / ' . preg_last_error_msg());
            }
        }

        return $output;
    }

    public function formatBatch(array $records): string
    {
        $message = '';
        foreach ($records as $record) {
            $message .= $this->format($record);
        }

        return $message;
    }

    /**
     * @param mixed $value
     */
    public function stringify($value): string
    {
        return $this->replaceNewlines($this->convertToString($value));
    }

    protected function normalizeException(\Throwable $e, int $depth = 0): string
    {
        $str = $this->formatException($e);

        $previous = $e->getPrevious();
        while ($previous instanceof \Throwable) {
            $depth++;
            if ($depth > $this->maxNormalizeDepth) {
                $str .= "\n[previous exception] Over " . $this->maxNormalizeDepth . ' levels deep, aborting normalization';
                break;
            }
            $str .= "\n[previous exception] " . $this->formatException($previous);
            $previous = $previous->getPrevious();
        }

        return $str;
    }

    /**
     * @param mixed $data
     */
    protected function convertToString($data): string
    {
        if (null === $data || \is_bool($data)) {
            return var_export($data, true);
        }

        if (\is_scalar($data)) {
            return (string) $data;
        }

        return $this->toJson($data, true);
    }

    protected function replaceNewlines(string $str): string
    {
        if ($this->allowInlineLineBreaks) {
            if (0 === strpos($str, '{') || 0 === strpos($str, '[')) {
                $str = preg_replace('/(?<!\\\\)\\\\[rn]/', "\n", $str);
                if (null === $str) {
                    $pcreErrorCode = preg_last_error();

                    throw new \RuntimeException('Failed to run preg_replace: ' . $pcreErrorCode . ' / ' . preg_last_error_msg());
                }
            }

            return $str;
        }

        return str_replace(["\r\n", "\r", "\n"], ' ', $str);
    }

    private function formatException(\Throwable $e): string
    {
        $str = '[object] (' . Utils::getClass($e) . '(code: ' . $e->getCode();
        if ($e instanceof \SoapFault) {
            if (isset($e->faultcode)) {
                $str .= ' faultcode: ' . $e->faultcode;
            }

            if (isset($e->faultactor)) {
                $str .= ' faultactor: ' . $e->faultactor;
            }

            if (isset($e->detail)) {
                if (\is_string($e->detail)) {
                    $str .= ' detail: ' . $e->detail;
                } elseif (\is_object($e->detail) || \is_array($e->detail)) {
                    $str .= ' detail: ' . $this->toJson($e->detail, true);
                }
            }
        }

        $file = $e->getFile();
        if ($this->basePath !== '') {
            $file = preg_replace('{^'.preg_quote($this->basePath).'}', '', $file);
        }

        $str .= '): ' . $e->getMessage() . ' at ' . strtr((string) $file, DIRECTORY_SEPARATOR, '/') . ':' . $e->getLine() . ')';

        if ($this->includeStacktraces) {
            $str .= $this->stacktracesParser($e);
        }

        return $str;
    }

    private function stacktracesParser(\Throwable $e): string
    {
        $trace = $e->getTraceAsString();

        if ($this->basePath !== '') {
            $trace = preg_replace('{^(#\d+ )' . preg_quote($this->basePath) . '}m', '$1', $trace) ?? $trace;
        }

        if ($this->stacktracesParser !== null) {
            $trace = $this->stacktracesParserCustom($trace);
        }

        if ($this->indentStacktraces !== '') {
            $trace = str_replace("\n", "\n{$this->indentStacktraces}", $trace);
        }

        if (trim($trace) === '') {
            return '';
        }

        return "\n{$this->indentStacktraces}[stacktrace]\n{$this->indentStacktraces}" . strtr($trace, DIRECTORY_SEPARATOR, '/') . "\n";
    }

    private function stacktracesParserCustom(string $trace): string
    {
        return implode("\n", array_filter(array_map($this->stacktracesParser, explode("\n", $trace)), fn ($line) => is_string($line) && trim($line) !== ''));
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Formatter;

use Monolog\Level;
use Monolog\LogRecord;

/**
 * Serializes a log message according to RFC 5424
 *
 * @author Dalibor KarloviÄ‡ <dalibor.karlovic@sigwin.hr>
 * @author Renat Gabdullin <renatobyj@gmail.com>
 */
class SyslogFormatter extends LineFormatter
{
    private const SYSLOG_FACILITY_USER = 1;
    private const FORMAT = "<%extra.priority%>1 %datetime% %extra.hostname% %extra.app-name% %extra.procid% %channel% %extra.structured-data% %level_name%: %message% %context% %extra%\n";
    private const NILVALUE = '-';

    private string $hostname;
    private int $procid;

    public function __construct(private string $applicationName = self::NILVALUE)
    {
        parent::__construct(self::FORMAT, 'Y-m-d\TH:i:s.uP', true, true);
        $this->hostname = (string) gethostname();
        $this->procid = (int) getmypid();
    }

    public function format(LogRecord $record): string
    {
        $record->extra = $this->formatExtra($record);

        return parent::format($record);
    }

    /**
     * @return array<string, mixed>
     */
    private function formatExtra(LogRecord $record): array
    {
        $extra = $record->extra;
        $extra['app-name'] = $this->applicationName;
        $extra['hostname'] = $this->hostname;
        $extra['procid'] = $this->procid;
        $extra['priority'] = self::calculatePriority($record->level);
        $extra['structured-data'] = self::NILVALUE;

        return $extra;
    }

    private static function calculatePriority(Level $level): int
    {
        return (self::SYSLOG_FACILITY_USER * 8) + $level->toRFC5424Level();
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Formatter;

use Monolog\Level;
use Monolog\LogRecord;

/**
 * Formats a log message according to the ChromePHP array format
 *
 * @author Christophe Coevoet <stof@notk.org>
 */
class ChromePHPFormatter implements FormatterInterface
{
    /**
     * Translates Monolog log levels to Wildfire levels.
     *
     * @return 'log'|'info'|'warn'|'error'
     */
    private function toWildfireLevel(Level $level): string
    {
        return match ($level) {
            Level::Debug     => 'log',
            Level::Info      => 'info',
            Level::Notice    => 'info',
            Level::Warning   => 'warn',
            Level::Error     => 'error',
            Level::Critical  => 'error',
            Level::Alert     => 'error',
            Level::Emergency => 'error',
        };
    }

    /**
     * @inheritDoc
     */
    public function format(LogRecord $record)
    {
        // Retrieve the line and file if set and remove them from the formatted extra
        $backtrace = 'unknown';
        if (isset($record->extra['file'], $record->extra['line'])) {
            $backtrace = $record->extra['file'].' : '.$record->extra['line'];
            unset($record->extra['file'], $record->extra['line']);
        }

        $message = ['message' => $record->message];
        if (\count($record->context) > 0) {
            $message['context'] = $record->context;
        }
        if (\count($record->extra) > 0) {
            $message['extra'] = $record->extra;
        }
        if (\count($message) === 1) {
            $message = reset($message);
        }

        return [
            $record->channel,
            $message,
            $backtrace,
            $this->toWildfireLevel($record->level),
        ];
    }

    /**
     * @inheritDoc
     */
    public function formatBatch(array $records)
    {
        $formatted = [];

        foreach ($records as $record) {
            $formatted[] = $this->format($record);
        }

        return $formatted;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Formatter;

use MongoDB\BSON\Type;
use MongoDB\BSON\UTCDateTime;
use Monolog\Utils;
use Monolog\LogRecord;

/**
 * Formats a record for use with the MongoDBHandler.
 *
 * @author Florian Plattner <me@florianplattner.de>
 */
class MongoDBFormatter implements FormatterInterface
{
    private bool $exceptionTraceAsString;
    private int $maxNestingLevel;

    /**
     * @param int  $maxNestingLevel        0 means infinite nesting, the $record itself is level 1, $record->context is 2
     * @param bool $exceptionTraceAsString set to false to log exception traces as a sub documents instead of strings
     */
    public function __construct(int $maxNestingLevel = 3, bool $exceptionTraceAsString = true)
    {
        $this->maxNestingLevel = max($maxNestingLevel, 0);
        $this->exceptionTraceAsString = $exceptionTraceAsString;
    }

    /**
     * @inheritDoc
     *
     * @return mixed[]
     */
    public function format(LogRecord $record): array
    {
        /** @var mixed[] $res */
        $res = $this->formatArray($record->toArray());

        return $res;
    }

    /**
     * @inheritDoc
     *
     * @return array<mixed[]>
     */
    public function formatBatch(array $records): array
    {
        $formatted = [];
        foreach ($records as $key => $record) {
            $formatted[$key] = $this->format($record);
        }

        return $formatted;
    }

    /**
     * @param  mixed[]        $array
     * @return mixed[]|string Array except when max nesting level is reached then a string "[...]"
     */
    protected function formatArray(array $array, int $nestingLevel = 0)
    {
        if ($this->maxNestingLevel > 0 && $nestingLevel > $this->maxNestingLevel) {
            return '[...]';
        }

        foreach ($array as $name => $value) {
            if ($value instanceof \DateTimeInterface) {
                $array[$name] = $this->formatDate($value, $nestingLevel + 1);
            } elseif ($value instanceof \Throwable) {
                $array[$name] = $this->formatException($value, $nestingLevel + 1);
            } elseif (\is_array($value)) {
                $array[$name] = $this->formatArray($value, $nestingLevel + 1);
            } elseif (\is_object($value) && !$value instanceof Type) {
                $array[$name] = $this->formatObject($value, $nestingLevel + 1);
            }
        }

        return $array;
    }

    /**
     * @param  mixed          $value
     * @return mixed[]|string
     */
    protected function formatObject($value, int $nestingLevel)
    {
        $objectVars = get_object_vars($value);
        $objectVars['class'] = Utils::getClass($value);

        return $this->formatArray($objectVars, $nestingLevel);
    }

    /**
     * @return mixed[]|string
     */
    protected function formatException(\Throwable $exception, int $nestingLevel)
    {
        $formattedException = [
            'class' => Utils::getClass($exception),
            'message' => $exception->getMessage(),
            'code' => (int) $exception->getCode(),
            'file' => $exception->getFile() . ':' . $exception->getLine(),
        ];

        if ($this->exceptionTraceAsString === true) {
            $formattedException['trace'] = $exception->getTraceAsString();
        } else {
            $formattedException['trace'] = $exception->getTrace();
        }

        return $this->formatArray($formattedException, $nestingLevel);
    }

    protected function formatDate(\DateTimeInterface $value, int $nestingLevel): UTCDateTime
    {
        return new UTCDateTime((int) floor(((float) $value->format('U.u')) * 1000));
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Formatter;

use DateTimeInterface;
use Monolog\LogRecord;

/**
 * Format a log message into an Elasticsearch record
 *
 * @author Avtandil Kikabidze <akalongman@gmail.com>
 */
class ElasticsearchFormatter extends NormalizerFormatter
{
    /**
     * @var string Elasticsearch index name
     */
    protected string $index;

    /**
     * @var string Elasticsearch record type
     */
    protected string $type;

    /**
     * @param string $index Elasticsearch index name
     * @param string $type  Elasticsearch record type
     */
    public function __construct(string $index, string $type)
    {
        // Elasticsearch requires an ISO 8601 format date with optional millisecond precision.
        parent::__construct(DateTimeInterface::ATOM);

        $this->index = $index;
        $this->type = $type;
    }

    /**
     * @inheritDoc
     */
    public function format(LogRecord $record)
    {
        $record = parent::format($record);

        return $this->getDocument($record);
    }

    /**
     * Getter index
     */
    public function getIndex(): string
    {
        return $this->index;
    }

    /**
     * Getter type
     */
    public function getType(): string
    {
        return $this->type;
    }

    /**
     * Convert a log message into an Elasticsearch record
     *
     * @param  mixed[] $record Log message
     * @return mixed[]
     */
    protected function getDocument(array $record): array
    {
        $record['_index'] = $this->index;
        $record['_type'] = $this->type;

        return $record;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Formatter;

use Monolog\LogRecord;

/**
 * formats the record to be used in the FlowdockHandler
 *
 * @author Dominik Liebler <liebler.dominik@gmail.com>
 * @deprecated Since 2.9.0 and 3.3.0, Flowdock was shutdown we will thus drop this handler in Monolog 4
 */
class FlowdockFormatter implements FormatterInterface
{
    private string $source;

    private string $sourceEmail;

    public function __construct(string $source, string $sourceEmail)
    {
        $this->source = $source;
        $this->sourceEmail = $sourceEmail;
    }

    /**
     * @inheritDoc
     *
     * @return mixed[]
     */
    public function format(LogRecord $record): array
    {
        $tags = [
            '#logs',
            '#' . $record->level->toPsrLogLevel(),
            '#' . $record->channel,
        ];

        foreach ($record->extra as $value) {
            $tags[] = '#' . $value;
        }

        $subject = sprintf(
            'in %s: %s - %s',
            $this->source,
            $record->level->getName(),
            $this->getShortMessage($record->message)
        );

        return [
            'source' => $this->source,
            'from_address' => $this->sourceEmail,
            'subject' => $subject,
            'content' => $record->message,
            'tags' => $tags,
            'project' => $this->source,
        ];
    }

    /**
     * @inheritDoc
     *
     * @return mixed[][]
     */
    public function formatBatch(array $records): array
    {
        $formatted = [];

        foreach ($records as $record) {
            $formatted[] = $this->format($record);
        }

        return $formatted;
    }

    public function getShortMessage(string $message): string
    {
        static $hasMbString;

        if (null === $hasMbString) {
            $hasMbString = \function_exists('mb_strlen');
        }

        $maxLength = 45;

        if ($hasMbString) {
            if (mb_strlen($message, 'UTF-8') > $maxLength) {
                $message = mb_substr($message, 0, $maxLength - 4, 'UTF-8') . ' ...';
            }
        } else {
            if (\strlen($message) > $maxLength) {
                $message = substr($message, 0, $maxLength - 4) . ' ...';
            }
        }

        return $message;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog;

use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
use ReflectionExtension;

/**
 * Monolog POSIX signal handler
 *
 * @author Robert Gust-Bardon <robert@gust-bardon.org>
 */
class SignalHandler
{
    private LoggerInterface $logger;

    /** @var array<int, callable|string|int> SIG_DFL, SIG_IGN or previous callable */
    private array $previousSignalHandler = [];
    /** @var array<int, \Psr\Log\LogLevel::*> */
    private array $signalLevelMap = [];
    /** @var array<int, bool> */
    private array $signalRestartSyscalls = [];

    public function __construct(LoggerInterface $logger)
    {
        $this->logger = $logger;
    }

    /**
     * @param  int|string|Level $level Level or level name
     * @return $this
     *
     * @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $level
     */
    public function registerSignalHandler(int $signo, int|string|Level $level = LogLevel::CRITICAL, bool $callPrevious = true, bool $restartSyscalls = true, ?bool $async = true): self
    {
        if (!\extension_loaded('pcntl') || !\function_exists('pcntl_signal')) {
            return $this;
        }

        $level = Logger::toMonologLevel($level)->toPsrLogLevel();

        if ($callPrevious) {
            $handler = pcntl_signal_get_handler($signo);
            $this->previousSignalHandler[$signo] = $handler;
        } else {
            unset($this->previousSignalHandler[$signo]);
        }
        $this->signalLevelMap[$signo] = $level;
        $this->signalRestartSyscalls[$signo] = $restartSyscalls;

        if ($async !== null) {
            pcntl_async_signals($async);
        }

        pcntl_signal($signo, [$this, 'handleSignal'], $restartSyscalls);

        return $this;
    }

    /**
     * @param mixed $siginfo
     */
    public function handleSignal(int $signo, $siginfo = null): void
    {
        /** @var array<int, string> $signals */
        static $signals = [];

        if (\count($signals) === 0 && \extension_loaded('pcntl')) {
            $pcntl = new ReflectionExtension('pcntl');
            foreach ($pcntl->getConstants() as $name => $value) {
                if (substr($name, 0, 3) === 'SIG' && $name[3] !== '_' && \is_int($value)) {
                    $signals[$value] = $name;
                }
            }
        }

        $level = $this->signalLevelMap[$signo] ?? LogLevel::CRITICAL;
        $signal = $signals[$signo] ?? $signo;
        $context = $siginfo ?? [];
        $this->logger->log($level, sprintf('Program received signal %s', $signal), $context);

        if (!isset($this->previousSignalHandler[$signo])) {
            return;
        }

        if ($this->previousSignalHandler[$signo] === SIG_DFL) {
            if (\extension_loaded('pcntl') && \function_exists('pcntl_signal') && \function_exists('pcntl_sigprocmask') && \function_exists('pcntl_signal_dispatch')
                && \extension_loaded('posix') && \function_exists('posix_getpid') && \function_exists('posix_kill')
            ) {
                $restartSyscalls = $this->signalRestartSyscalls[$signo] ?? true;
                pcntl_signal($signo, SIG_DFL, $restartSyscalls);
                pcntl_sigprocmask(SIG_UNBLOCK, [$signo], $oldset);
                posix_kill(posix_getpid(), $signo);
                pcntl_signal_dispatch();
                pcntl_sigprocmask(SIG_SETMASK, $oldset);
                pcntl_signal($signo, [$this, 'handleSignal'], $restartSyscalls);
            }
        } elseif (\is_callable($this->previousSignalHandler[$signo])) {
            $this->previousSignalHandler[$signo]($signo, $siginfo);
        }
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog;

use InvalidArgumentException;

/**
 * Monolog log registry
 *
 * Allows to get `Logger` instances in the global scope
 * via static method calls on this class.
 *
 * <code>
 * $application = new Monolog\Logger('application');
 * $api = new Monolog\Logger('api');
 *
 * Monolog\Registry::addLogger($application);
 * Monolog\Registry::addLogger($api);
 *
 * function testLogger()
 * {
 *     Monolog\Registry::api()->error('Sent to $api Logger instance');
 *     Monolog\Registry::application()->error('Sent to $application Logger instance');
 * }
 * </code>
 *
 * @author Tomas Tatarko <tomas@tatarko.sk>
 */
class Registry
{
    /**
     * List of all loggers in the registry (by named indexes)
     *
     * @var Logger[]
     */
    private static array $loggers = [];

    /**
     * Adds new logging channel to the registry
     *
     * @param  Logger                    $logger    Instance of the logging channel
     * @param  string|null               $name      Name of the logging channel ($logger->getName() by default)
     * @param  bool                      $overwrite Overwrite instance in the registry if the given name already exists?
     * @throws \InvalidArgumentException If $overwrite set to false and named Logger instance already exists
     */
    public static function addLogger(Logger $logger, ?string $name = null, bool $overwrite = false): void
    {
        $name = $name ?? $logger->getName();

        if (isset(self::$loggers[$name]) && !$overwrite) {
            throw new InvalidArgumentException('Logger with the given name already exists');
        }

        self::$loggers[$name] = $logger;
    }

    /**
     * Checks if such logging channel exists by name or instance
     *
     * @param string|Logger $logger Name or logger instance
     */
    public static function hasLogger($logger): bool
    {
        if ($logger instanceof Logger) {
            $index = array_search($logger, self::$loggers, true);

            return false !== $index;
        }

        return isset(self::$loggers[$logger]);
    }

    /**
     * Removes instance from registry by name or instance
     *
     * @param string|Logger $logger Name or logger instance
     */
    public static function removeLogger($logger): void
    {
        if ($logger instanceof Logger) {
            if (false !== ($idx = array_search($logger, self::$loggers, true))) {
                unset(self::$loggers[$idx]);
            }
        } else {
            unset(self::$loggers[$logger]);
        }
    }

    /**
     * Clears the registry
     */
    public static function clear(): void
    {
        self::$loggers = [];
    }

    /**
     * Gets Logger instance from the registry
     *
     * @param  string                    $name Name of the requested Logger instance
     * @throws \InvalidArgumentException If named Logger instance is not in the registry
     */
    public static function getInstance(string $name): Logger
    {
        if (!isset(self::$loggers[$name])) {
            throw new InvalidArgumentException(sprintf('Requested "%s" logger instance is not in the registry', $name));
        }

        return self::$loggers[$name];
    }

    /**
     * Gets Logger instance from the registry via static method call
     *
     * @param  string                    $name      Name of the requested Logger instance
     * @param  mixed[]                   $arguments Arguments passed to static method call
     * @throws \InvalidArgumentException If named Logger instance is not in the registry
     * @return Logger                    Requested instance of Logger
     */
    public static function __callStatic(string $name, array $arguments): Logger
    {
        return self::getInstance($name);
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog;

use ArrayAccess;

/**
 * Monolog log record
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 * @template-implements ArrayAccess<'message'|'level'|'context'|'level_name'|'channel'|'datetime'|'extra'|'formatted', int|string|\DateTimeImmutable|array<mixed>>
 */
class LogRecord implements ArrayAccess
{
    private const MODIFIABLE_FIELDS = [
        'extra' => true,
        'formatted' => true,
    ];

    public function __construct(
        public readonly \DateTimeImmutable $datetime,
        public readonly string $channel,
        public readonly Level $level,
        public readonly string $message,
        /** @var array<mixed> */
        public readonly array $context = [],
        /** @var array<mixed> */
        public array $extra = [],
        public mixed $formatted = null,
    ) {
    }

    public function offsetSet(mixed $offset, mixed $value): void
    {
        if ($offset === 'extra') {
            if (!\is_array($value)) {
                throw new \InvalidArgumentException('extra must be an array');
            }

            $this->extra = $value;

            return;
        }

        if ($offset === 'formatted') {
            $this->formatted = $value;

            return;
        }

        throw new \LogicException('Unsupported operation: setting '.$offset);
    }

    public function offsetExists(mixed $offset): bool
    {
        if ($offset === 'level_name') {
            return true;
        }

        return isset($this->{$offset});
    }

    public function offsetUnset(mixed $offset): void
    {
        throw new \LogicException('Unsupported operation');
    }

    public function &offsetGet(mixed $offset): mixed
    {
        // handle special cases for the level enum
        if ($offset === 'level_name') {
            // avoid returning readonly props by ref as this is illegal
            $copy = $this->level->getName();

            return $copy;
        }
        if ($offset === 'level') {
            // avoid returning readonly props by ref as this is illegal
            $copy = $this->level->value;

            return $copy;
        }

        if (isset(self::MODIFIABLE_FIELDS[$offset])) {
            return $this->{$offset};
        }

        // avoid returning readonly props by ref as this is illegal
        $copy = $this->{$offset};

        return $copy;
    }

    /**
     * @phpstan-return array{message: string, context: mixed[], level: value-of<Level::VALUES>, level_name: value-of<Level::NAMES>, channel: string, datetime: \DateTimeImmutable, extra: mixed[]}
     */
    public function toArray(): array
    {
        return [
            'message' => $this->message,
            'context' => $this->context,
            'level' => $this->level->value,
            'level_name' => $this->level->getName(),
            'channel' => $this->channel,
            'datetime' => $this->datetime,
            'extra' => $this->extra,
        ];
    }

    public function with(mixed ...$args): self
    {
        foreach (['message', 'context', 'level', 'channel', 'datetime', 'extra'] as $prop) {
            $args[$prop] ??= $this->{$prop};
        }

        return new self(...$args);
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog;

use DateTimeZone;

/**
 * Overrides default json encoding of date time objects
 *
 * @author Menno Holtkamp
 * @author Jordi Boggiano <j.boggiano@seld.be>
 */
class JsonSerializableDateTimeImmutable extends \DateTimeImmutable implements \JsonSerializable
{
    private bool $useMicroseconds;

    public function __construct(bool $useMicroseconds, ?DateTimeZone $timezone = null)
    {
        $this->useMicroseconds = $useMicroseconds;

        // if you like to use a custom time to pass to Logger::addRecord directly,
        // call modify() or setTimestamp() on this instance to change the date after creating it
        parent::__construct('now', $timezone);
    }

    public function jsonSerialize(): string
    {
        if ($this->useMicroseconds) {
            return $this->format('Y-m-d\TH:i:s.uP');
        }

        return $this->format('Y-m-d\TH:i:sP');
    }

    public function __toString(): string
    {
        return $this->jsonSerialize();
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Processor;

use Monolog\LogRecord;

/**
 * Injects memory_get_usage in all records
 *
 * @see Monolog\Processor\MemoryProcessor::__construct() for options
 * @author Rob Jensen
 */
class MemoryUsageProcessor extends MemoryProcessor
{
    /**
     * @inheritDoc
     */
    public function __invoke(LogRecord $record): LogRecord
    {
        $usage = memory_get_usage($this->realUsage);

        if ($this->useFormatting) {
            $usage = $this->formatBytes($usage);
        }

        $record->extra['memory_usage'] = $usage;

        return $record;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Processor;

use Monolog\Level;
use Monolog\Logger;
use Psr\Log\LogLevel;
use Monolog\LogRecord;

/**
 * Injects line/file:class/function where the log message came from
 *
 * Warning: This only works if the handler processes the logs directly.
 * If you put the processor on a handler that is behind a FingersCrossedHandler
 * for example, the processor will only be called once the trigger level is reached,
 * and all the log records will have the same file/line/.. data from the call that
 * triggered the FingersCrossedHandler.
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 */
class IntrospectionProcessor implements ProcessorInterface
{
    protected Level $level;

    /** @var string[] */
    protected array $skipClassesPartials;

    protected int $skipStackFramesCount;

    protected const SKIP_FUNCTIONS = [
        'call_user_func',
        'call_user_func_array',
    ];

    protected const SKIP_CLASSES = [
        'Monolog\\',
    ];

    /**
     * @param string|int|Level $level               The minimum logging level at which this Processor will be triggered
     * @param string[]         $skipClassesPartials
     *
     * @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $level
     */
    public function __construct(int|string|Level $level = Level::Debug, array $skipClassesPartials = [], int $skipStackFramesCount = 0)
    {
        $this->level = Logger::toMonologLevel($level);
        $this->skipClassesPartials = array_merge(static::SKIP_CLASSES, $skipClassesPartials);
        $this->skipStackFramesCount = $skipStackFramesCount;
    }

    /**
     * @inheritDoc
     */
    public function __invoke(LogRecord $record): LogRecord
    {
        // return if the level is not high enough
        if ($record->level->isLowerThan($this->level)) {
            return $record;
        }

        $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);

        // skip first since it's always the current method
        array_shift($trace);
        // the call_user_func call is also skipped
        array_shift($trace);

        $i = 0;

        while ($this->isTraceClassOrSkippedFunction($trace, $i)) {
            if (isset($trace[$i]['class'])) {
                foreach ($this->skipClassesPartials as $part) {
                    if (strpos($trace[$i]['class'], $part) !== false) {
                        $i++;

                        continue 2;
                    }
                }
            } elseif (\in_array($trace[$i]['function'], static::SKIP_FUNCTIONS, true)) {
                $i++;

                continue;
            }

            break;
        }

        $i += $this->skipStackFramesCount;

        // we should have the call source now
        $record->extra = array_merge(
            $record->extra,
            [
                'file'      => $trace[$i - 1]['file'] ?? null,
                'line'      => $trace[$i - 1]['line'] ?? null,
                'class'     => $trace[$i]['class'] ?? null,
                'callType'  => $trace[$i]['type'] ?? null,
                'function'  => $trace[$i]['function'] ?? null,
            ]
        );

        return $record;
    }

    /**
     * @param array<mixed> $trace
     */
    private function isTraceClassOrSkippedFunction(array $trace, int $index): bool
    {
        if (!isset($trace[$index])) {
            return false;
        }

        return isset($trace[$index]['class']) || \in_array($trace[$index]['function'], static::SKIP_FUNCTIONS, true);
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Processor;

use Monolog\Level;
use Monolog\Logger;
use Psr\Log\LogLevel;
use Monolog\LogRecord;

/**
 * Injects Hg branch and Hg revision number in all records
 *
 * @author Jonathan A. Schweder <jonathanschweder@gmail.com>
 */
class MercurialProcessor implements ProcessorInterface
{
    private Level $level;
    /** @var array{branch: string, revision: string}|array<never>|null */
    private static $cache = null;

    /**
     * @param int|string|Level $level The minimum logging level at which this Processor will be triggered
     *
     * @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $level
     */
    public function __construct(int|string|Level $level = Level::Debug)
    {
        $this->level = Logger::toMonologLevel($level);
    }

    /**
     * @inheritDoc
     */
    public function __invoke(LogRecord $record): LogRecord
    {
        // return if the level is not high enough
        if ($record->level->isLowerThan($this->level)) {
            return $record;
        }

        $record->extra['hg'] = self::getMercurialInfo();

        return $record;
    }

    /**
     * @return array{branch: string, revision: string}|array<never>
     */
    private static function getMercurialInfo(): array
    {
        if (self::$cache !== null) {
            return self::$cache;
        }

        $result = explode(' ', trim((string) shell_exec('hg id -nb')));
        if (\count($result) >= 3) {
            return self::$cache = [
                'branch' => $result[1],
                'revision' => $result[2],
            ];
        }
        if (\count($result) === 2) {
            return self::$cache = [
                'branch' => $result[1],
                'revision' => $result[0],
            ];
        }

        return self::$cache = [];
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Processor;

/**
 * Some methods that are common for all memory processors
 *
 * @author Rob Jensen
 */
abstract class MemoryProcessor implements ProcessorInterface
{
    /**
     * @var bool If true, get the real size of memory allocated from system. Else, only the memory used by emalloc() is reported.
     */
    protected bool $realUsage;

    /**
     * @var bool If true, then format memory size to human readable string (MB, KB, B depending on size)
     */
    protected bool $useFormatting;

    /**
     * @param bool $realUsage     Set this to true to get the real size of memory allocated from system.
     * @param bool $useFormatting If true, then format memory size to human readable string (MB, KB, B depending on size)
     */
    public function __construct(bool $realUsage = true, bool $useFormatting = true)
    {
        $this->realUsage = $realUsage;
        $this->useFormatting = $useFormatting;
    }

    /**
     * Formats bytes into a human readable string if $this->useFormatting is true, otherwise return $bytes as is
     *
     * @return string|int Formatted string if $this->useFormatting is true, otherwise return $bytes as int
     */
    protected function formatBytes(int $bytes)
    {
        if (!$this->useFormatting) {
            return $bytes;
        }

        if ($bytes > 1024 * 1024) {
            return round($bytes / 1024 / 1024, 2).' MB';
        } elseif ($bytes > 1024) {
            return round($bytes / 1024, 2).' KB';
        }

        return $bytes . ' B';
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Processor;

use Monolog\LogRecord;

/**
 * Injects sys_getloadavg in all records @see https://www.php.net/manual/en/function.sys-getloadavg.php
 *
 * @author Johan Vlaar <johan.vlaar.1994@gmail.com>
 */
class LoadAverageProcessor implements ProcessorInterface
{
    public const LOAD_1_MINUTE = 0;
    public const LOAD_5_MINUTE = 1;
    public const LOAD_15_MINUTE = 2;

    private const AVAILABLE_LOAD = [
        self::LOAD_1_MINUTE,
        self::LOAD_5_MINUTE,
        self::LOAD_15_MINUTE,
    ];

    /**
     * @var int
     */
    protected $avgSystemLoad;

    /**
     * @param self::LOAD_* $avgSystemLoad
     */
    public function __construct(int $avgSystemLoad = self::LOAD_1_MINUTE)
    {
        if (!\in_array($avgSystemLoad, self::AVAILABLE_LOAD, true)) {
            throw new \InvalidArgumentException(sprintf('Invalid average system load: `%s`', $avgSystemLoad));
        }
        $this->avgSystemLoad = $avgSystemLoad;
    }

    /**
     * {@inheritDoc}
     */
    public function __invoke(LogRecord $record): LogRecord
    {
        if (!\function_exists('sys_getloadavg')) {
            return $record;
        }
        $usage = sys_getloadavg();
        if (false === $usage) {
            return $record;
        }

        $record->extra['load_average'] = $usage[$this->avgSystemLoad];

        return $record;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Processor;

use Monolog\LogRecord;

/**
 * Injects value of gethostname in all records
 */
class HostnameProcessor implements ProcessorInterface
{
    private static string $host;

    public function __construct()
    {
        self::$host = (string) gethostname();
    }

    /**
     * @inheritDoc
     */
    public function __invoke(LogRecord $record): LogRecord
    {
        $record->extra['hostname'] = self::$host;

        return $record;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Processor;

use Monolog\LogRecord;

/**
 * Generates a context from a Closure if the Closure is the only value
 * in the context
 *
 * It helps reduce the performance impact of debug logs if they do
 * need to create lots of context information. If this processor is added
 * on the correct handler the context data will only be generated
 * when the logs are actually logged to that handler, which is useful when
 * using FingersCrossedHandler or other filtering handlers to conditionally
 * log records.
 */
class ClosureContextProcessor implements ProcessorInterface
{
    public function __invoke(LogRecord $record): LogRecord
    {
        $context = $record->context;
        if (isset($context[0]) && 1 === \count($context) && $context[0] instanceof \Closure) {
            try {
                $context = $context[0]();
            } catch (\Throwable $e) {
                $context = [
                    'error_on_context_generation' => $e->getMessage(),
                    'exception' => $e,
                ];
            }

            if (!\is_array($context)) {
                $context = [$context];
            }

            $record = $record->with(context: $context);
        }

        return $record;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Processor;

use Monolog\ResettableInterface;
use Monolog\LogRecord;

/**
 * Adds a unique identifier into records
 *
 * @author Simon MÃ¶nch <sm@webfactory.de>
 */
class UidProcessor implements ProcessorInterface, ResettableInterface
{
    /** @var non-empty-string */
    private string $uid;

    /**
     * @param int<1, 32> $length
     */
    public function __construct(int $length = 7)
    {
        if ($length > 32 || $length < 1) {
            throw new \InvalidArgumentException('The uid length must be an integer between 1 and 32');
        }

        $this->uid = $this->generateUid($length);
    }

    /**
     * @inheritDoc
     */
    public function __invoke(LogRecord $record): LogRecord
    {
        $record->extra['uid'] = $this->uid;

        return $record;
    }

    public function getUid(): string
    {
        return $this->uid;
    }

    public function reset(): void
    {
        $this->uid = $this->generateUid(\strlen($this->uid));
    }

    /**
     * @param  positive-int     $length
     * @return non-empty-string
     */
    private function generateUid(int $length): string
    {
        return substr(bin2hex(random_bytes((int) ceil($length / 2))), 0, $length);
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Processor;

use Monolog\Level;
use Monolog\Logger;
use Psr\Log\LogLevel;
use Monolog\LogRecord;

/**
 * Injects Git branch and Git commit SHA in all records
 *
 * @author Nick Otter
 * @author Jordi Boggiano <j.boggiano@seld.be>
 */
class GitProcessor implements ProcessorInterface
{
    private Level $level;
    /** @var array{branch: string, commit: string}|array<never>|null */
    private static $cache = null;

    /**
     * @param int|string|Level|LogLevel::* $level The minimum logging level at which this Processor will be triggered
     *
     * @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $level
     */
    public function __construct(int|string|Level $level = Level::Debug)
    {
        $this->level = Logger::toMonologLevel($level);
    }

    /**
     * @inheritDoc
     */
    public function __invoke(LogRecord $record): LogRecord
    {
        // return if the level is not high enough
        if ($record->level->isLowerThan($this->level)) {
            return $record;
        }

        $record->extra['git'] = self::getGitInfo();

        return $record;
    }

    /**
     * @return array{branch: string, commit: string}|array<never>
     */
    private static function getGitInfo(): array
    {
        if (self::$cache !== null) {
            return self::$cache;
        }

        $branches = shell_exec('git branch -v --no-abbrev');
        if (\is_string($branches) && 1 === preg_match('{^\* (.+?)\s+([a-f0-9]{40})(?:\s|$)}m', $branches, $matches)) {
            return self::$cache = [
                'branch' => $matches[1],
                'commit' => $matches[2],
            ];
        }

        return self::$cache = [];
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Processor;

use ArrayAccess;
use Monolog\LogRecord;

/**
 * Injects url/method and remote IP of the current web request in all records
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 */
class WebProcessor implements ProcessorInterface
{
    /**
     * @var array<string, mixed>|ArrayAccess<string, mixed>
     */
    protected array|ArrayAccess $serverData;

    /**
     * Default fields
     *
     * Array is structured as [key in record.extra => key in $serverData]
     *
     * @var array<string, string>
     */
    protected array $extraFields = [
        'url'         => 'REQUEST_URI',
        'ip'          => 'REMOTE_ADDR',
        'http_method' => 'REQUEST_METHOD',
        'server'      => 'SERVER_NAME',
        'referrer'    => 'HTTP_REFERER',
        'user_agent'  => 'HTTP_USER_AGENT',
    ];

    /**
     * @param array<string, mixed>|ArrayAccess<string, mixed>|null $serverData  Array or object w/ ArrayAccess that provides access to the $_SERVER data
     * @param array<string, string>|array<string>|null             $extraFields Field names and the related key inside $serverData to be added (or just a list of field names to use the default configured $serverData mapping). If not provided it defaults to: [url, ip, http_method, server, referrer] + unique_id if present in server data
     */
    public function __construct(array|ArrayAccess|null $serverData = null, array|null $extraFields = null)
    {
        if (null === $serverData) {
            $this->serverData = &$_SERVER;
        } else {
            $this->serverData = $serverData;
        }

        $defaultEnabled = ['url', 'ip', 'http_method', 'server', 'referrer'];
        if (isset($this->serverData['UNIQUE_ID'])) {
            $this->extraFields['unique_id'] = 'UNIQUE_ID';
            $defaultEnabled[] = 'unique_id';
        }

        if (null === $extraFields) {
            $extraFields = $defaultEnabled;
        }
        if (isset($extraFields[0])) {
            foreach (array_keys($this->extraFields) as $fieldName) {
                if (!\in_array($fieldName, $extraFields, true)) {
                    unset($this->extraFields[$fieldName]);
                }
            }
        } else {
            $this->extraFields = $extraFields;
        }
    }

    /**
     * @inheritDoc
     */
    public function __invoke(LogRecord $record): LogRecord
    {
        // skip processing if for some reason request data
        // is not present (CLI or wonky SAPIs)
        if (!isset($this->serverData['REQUEST_URI'])) {
            return $record;
        }

        $record->extra = $this->appendExtraFields($record->extra);

        return $record;
    }

    /**
     * @return $this
     */
    public function addExtraField(string $extraName, string $serverName): self
    {
        $this->extraFields[$extraName] = $serverName;

        return $this;
    }

    /**
     * @param  mixed[] $extra
     * @return mixed[]
     */
    private function appendExtraFields(array $extra): array
    {
        foreach ($this->extraFields as $extraName => $serverName) {
            $extra[$extraName] = $this->serverData[$serverName] ?? null;
        }

        return $extra;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Processor;

use Monolog\LogRecord;

/**
 * Adds value of getmypid into records
 *
 * @author Andreas HÃ¶rnicke
 */
class ProcessIdProcessor implements ProcessorInterface
{
    /**
     * @inheritDoc
     */
    public function __invoke(LogRecord $record): LogRecord
    {
        $record->extra['process_id'] = getmypid();

        return $record;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Processor;

use Monolog\Utils;
use Monolog\LogRecord;

/**
 * Processes a record's message according to PSR-3 rules
 *
 * It replaces {foo} with the value from $context['foo']
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 */
class PsrLogMessageProcessor implements ProcessorInterface
{
    public const SIMPLE_DATE = "Y-m-d\TH:i:s.uP";

    private ?string $dateFormat;

    private bool $removeUsedContextFields;

    /**
     * @param string|null $dateFormat              The format of the timestamp: one supported by DateTime::format
     * @param bool        $removeUsedContextFields If set to true the fields interpolated into message gets unset
     */
    public function __construct(?string $dateFormat = null, bool $removeUsedContextFields = false)
    {
        $this->dateFormat = $dateFormat;
        $this->removeUsedContextFields = $removeUsedContextFields;
    }

    /**
     * @inheritDoc
     */
    public function __invoke(LogRecord $record): LogRecord
    {
        if (false === strpos($record->message, '{')) {
            return $record;
        }

        $replacements = [];
        $context = $record->context;

        foreach ($context as $key => $val) {
            $placeholder = '{' . $key . '}';
            if (strpos($record->message, $placeholder) === false) {
                continue;
            }

            if (null === $val || \is_scalar($val) || (\is_object($val) && method_exists($val, "__toString"))) {
                $replacements[$placeholder] = $val;
            } elseif ($val instanceof \DateTimeInterface) {
                if (null === $this->dateFormat && $val instanceof \Monolog\JsonSerializableDateTimeImmutable) {
                    // handle monolog dates using __toString if no specific dateFormat was asked for
                    // so that it follows the useMicroseconds flag
                    $replacements[$placeholder] = (string) $val;
                } else {
                    $replacements[$placeholder] = $val->format($this->dateFormat ?? static::SIMPLE_DATE);
                }
            } elseif ($val instanceof \UnitEnum) {
                $replacements[$placeholder] = $val instanceof \BackedEnum ? $val->value : $val->name;
            } elseif (\is_object($val)) {
                $replacements[$placeholder] = '[object '.Utils::getClass($val).']';
            } elseif (\is_array($val)) {
                $replacements[$placeholder] = 'array'.Utils::jsonEncode($val, null, true);
            } else {
                $replacements[$placeholder] = '['.\gettype($val).']';
            }

            if ($this->removeUsedContextFields) {
                unset($context[$key]);
            }
        }

        return $record->with(message: strtr($record->message, $replacements), context: $context);
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Processor;

use Monolog\LogRecord;

/**
 * Adds a tags array into record
 *
 * @author Martijn Riemers
 */
class TagProcessor implements ProcessorInterface
{
    /** @var string[] */
    private array $tags;

    /**
     * @param string[] $tags
     */
    public function __construct(array $tags = [])
    {
        $this->setTags($tags);
    }

    /**
     * @param  string[] $tags
     * @return $this
     */
    public function addTags(array $tags = []): self
    {
        $this->tags = array_merge($this->tags, $tags);

        return $this;
    }

    /**
     * @param  string[] $tags
     * @return $this
     */
    public function setTags(array $tags = []): self
    {
        $this->tags = $tags;

        return $this;
    }

    /**
     * @inheritDoc
     */
    public function __invoke(LogRecord $record): LogRecord
    {
        $record->extra['tags'] = $this->tags;

        return $record;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Processor;

use Monolog\LogRecord;

/**
 * Injects memory_get_peak_usage in all records
 *
 * @see Monolog\Processor\MemoryProcessor::__construct() for options
 * @author Rob Jensen
 */
class MemoryPeakUsageProcessor extends MemoryProcessor
{
    /**
     * @inheritDoc
     */
    public function __invoke(LogRecord $record): LogRecord
    {
        $usage = memory_get_peak_usage($this->realUsage);

        if ($this->useFormatting) {
            $usage = $this->formatBytes($usage);
        }

        $record->extra['memory_peak_usage'] = $usage;

        return $record;
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog\Processor;

use Monolog\LogRecord;

/**
 * An optional interface to allow labelling Monolog processors.
 *
 * @author Nicolas Grekas <p@tchwork.com>
 */
interface ProcessorInterface
{
    /**
     * @return LogRecord The processed record
     */
    public function __invoke(LogRecord $record);
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog;

use Closure;
use DateTimeZone;
use Fiber;
use Monolog\Handler\HandlerInterface;
use Monolog\Processor\ProcessorInterface;
use Psr\Clock\ClockInterface;
use Psr\Log\LoggerInterface;
use Psr\Log\InvalidArgumentException;
use Psr\Log\LogLevel;
use Throwable;
use Stringable;
use WeakMap;

/**
 * Monolog log channel
 *
 * It contains a stack of Handlers and a stack of Processors,
 * and uses them to store records that are added to it.
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 * @final
 */
class Logger implements LoggerInterface, ResettableInterface
{
    /**
     * Detailed debug information
     *
     * @deprecated Use \Monolog\Level::Debug
     */
    public const DEBUG = 100;

    /**
     * Interesting events
     *
     * Examples: User logs in, SQL logs.
     *
     * @deprecated Use \Monolog\Level::Info
     */
    public const INFO = 200;

    /**
     * Uncommon events
     *
     * @deprecated Use \Monolog\Level::Notice
     */
    public const NOTICE = 250;

    /**
     * Exceptional occurrences that are not errors
     *
     * Examples: Use of deprecated APIs, poor use of an API,
     * undesirable things that are not necessarily wrong.
     *
     * @deprecated Use \Monolog\Level::Warning
     */
    public const WARNING = 300;

    /**
     * Runtime errors
     *
     * @deprecated Use \Monolog\Level::Error
     */
    public const ERROR = 400;

    /**
     * Critical conditions
     *
     * Example: Application component unavailable, unexpected exception.
     *
     * @deprecated Use \Monolog\Level::Critical
     */
    public const CRITICAL = 500;

    /**
     * Action must be taken immediately
     *
     * Example: Entire website down, database unavailable, etc.
     * This should trigger the SMS alerts and wake you up.
     *
     * @deprecated Use \Monolog\Level::Alert
     */
    public const ALERT = 550;

    /**
     * Urgent alert.
     *
     * @deprecated Use \Monolog\Level::Emergency
     */
    public const EMERGENCY = 600;

    /**
     * Monolog API version
     *
     * This is only bumped when API breaks are done and should
     * follow the major version of the library
     */
    public const API = 3;

    /**
     * Mapping between levels numbers defined in RFC 5424 and Monolog ones
     *
     * @phpstan-var array<int, Level> $rfc_5424_levels
     */
    private const RFC_5424_LEVELS = [
        7 => Level::Debug,
        6 => Level::Info,
        5 => Level::Notice,
        4 => Level::Warning,
        3 => Level::Error,
        2 => Level::Critical,
        1 => Level::Alert,
        0 => Level::Emergency,
    ];

    protected string $name;

    /**
     * The handler stack
     *
     * @var list<HandlerInterface>
     */
    protected array $handlers;

    /**
     * Processors that will process all log records
     *
     * To process records of a single handler instead, add the processor on that specific handler
     *
     * @var array<(callable(LogRecord): LogRecord)|ProcessorInterface>
     */
    protected array $processors;

    protected bool $microsecondTimestamps = true;

    protected DateTimeZone $timezone;

    /**
     * Clock used to timestamp new records, or null to read the current time from the engine
     */
    protected ClockInterface|null $clock = null;

    protected Closure|null $exceptionHandler = null;

    /**
     * Keeps track of depth to prevent infinite logging loops
     */
    private int $logDepth = 0;

    /**
     * @var WeakMap<Fiber<mixed, mixed, mixed, mixed>, int> Keeps track of depth inside fibers to prevent infinite logging loops
     */
    private WeakMap $fiberLogDepth;

    /**
     * Whether to detect infinite logging loops
     * This can be disabled via {@see useLoggingLoopDetection} if you have async handlers that do not play well with this
     */
    private bool $detectCycles = true;

    /**
     * @param string             $name       The logging channel, a simple descriptive name that is attached to all log records
     * @param list<HandlerInterface> $handlers   Optional stack of handlers, the first one in the array is called first, etc.
     * @param callable[]         $processors Optional array of processors
     * @param DateTimeZone|null  $timezone   Optional timezone, if not provided date_default_timezone_get() will be used
     * @param ClockInterface|null $clock     Optional clock to read the current time from, if not provided the engine's current time is used
     *
     * @phpstan-param array<(callable(LogRecord): LogRecord)|ProcessorInterface> $processors
     */
    public function __construct(string $name, array $handlers = [], array $processors = [], DateTimeZone|null $timezone = null, ClockInterface|null $clock = null)
    {
        $this->name = $name;
        $this->setHandlers($handlers);
        $this->processors = $processors;
        $this->timezone = $timezone ?? new DateTimeZone(date_default_timezone_get());
        $this->clock = $clock;
        $this->fiberLogDepth = new \WeakMap();
    }

    public function getName(): string
    {
        return $this->name;
    }

    /**
     * Return a new cloned instance with the name changed
     *
     * @return static
     */
    public function withName(string $name): self
    {
        $new = clone $this;
        $new->name = $name;

        return $new;
    }

    /**
     * Pushes a handler on to the stack.
     *
     * @return $this
     */
    public function pushHandler(HandlerInterface $handler): self
    {
        array_unshift($this->handlers, $handler);

        return $this;
    }

    /**
     * Pops a handler from the stack
     *
     * @throws \LogicException If empty handler stack
     */
    public function popHandler(): HandlerInterface
    {
        if (0 === \count($this->handlers)) {
            throw new \LogicException('You tried to pop from an empty handler stack.');
        }

        return array_shift($this->handlers);
    }

    /**
     * Set handlers, replacing all existing ones.
     *
     * If a map is passed, keys will be ignored.
     *
     * @param  list<HandlerInterface> $handlers
     * @return $this
     */
    public function setHandlers(array $handlers): self
    {
        $this->handlers = [];
        foreach (array_reverse($handlers) as $handler) {
            $this->pushHandler($handler);
        }

        return $this;
    }

    /**
     * @return list<HandlerInterface>
     */
    public function getHandlers(): array
    {
        return $this->handlers;
    }

    /**
     * Adds a processor on to the stack.
     *
     * @phpstan-param ProcessorInterface|(callable(LogRecord): LogRecord) $callback
     * @return $this
     */
    public function pushProcessor(ProcessorInterface|callable $callback): self
    {
        array_unshift($this->processors, $callback);

        return $this;
    }

    /**
     * Removes the processor on top of the stack and returns it.
     *
     * @phpstan-return ProcessorInterface|(callable(LogRecord): LogRecord)
     * @throws \LogicException If empty processor stack
     */
    public function popProcessor(): callable
    {
        if (0 === \count($this->processors)) {
            throw new \LogicException('You tried to pop from an empty processor stack.');
        }

        return array_shift($this->processors);
    }

    /**
     * @return callable[]
     * @phpstan-return array<ProcessorInterface|(callable(LogRecord): LogRecord)>
     */
    public function getProcessors(): array
    {
        return $this->processors;
    }

    /**
     * Control the use of microsecond resolution timestamps in the 'datetime'
     * member of new records.
     *
     * As of PHP7.1 microseconds are always included by the engine, so
     * there is no performance penalty and Monolog 2 enabled microseconds
     * by default. This function lets you disable them though in case you want
     * to suppress microseconds from the output.
     *
     * @param  bool  $micro True to use microtime() to create timestamps
     * @return $this
     */
    public function useMicrosecondTimestamps(bool $micro): self
    {
        $this->microsecondTimestamps = $micro;

        return $this;
    }

    /**
     * @return $this
     */
    public function useLoggingLoopDetection(bool $detectCycles): self
    {
        $this->detectCycles = $detectCycles;

        return $this;
    }

    /**
     * Adds a log record.
     *
     * @param  int                    $level    The logging level (a Monolog or RFC 5424 level)
     * @param  string                 $message  The log message
     * @param  mixed[]                $context  The log context
     * @param  JsonSerializableDateTimeImmutable|null $datetime Optional log date to log into the past or future
     *
     * @return bool                   Whether the record has been processed
     *
     * @phpstan-param value-of<Level::VALUES>|Level $level
     */
    public function addRecord(int|Level $level, string $message, array $context = [], JsonSerializableDateTimeImmutable|null $datetime = null): bool
    {
        if (\is_int($level) && isset(self::RFC_5424_LEVELS[$level])) {
            $level = self::RFC_5424_LEVELS[$level];
        }

        if ($this->detectCycles) {
            if (null !== ($fiber = Fiber::getCurrent())) {
                $logDepth = $this->fiberLogDepth[$fiber] = ($this->fiberLogDepth[$fiber] ?? 0) + 1;
            } else {
                $logDepth = ++$this->logDepth;
            }
        } else {
            $logDepth = 0;
        }

        try {
            if ($logDepth === 3) {
                $this->warning('A possible infinite logging loop was detected and aborted. It appears some of your handler code is triggering logging, see the previous log record for a hint as to what may be the cause.');

                return false;
            } elseif ($logDepth >= 5) { // log depth 4 is let through, so we can log the warning above
                return false;
            }

            $recordInitialized = \count($this->processors) === 0;

            $record = new LogRecord(
                datetime: $datetime ?? $this->createDateTime(),
                channel: $this->name,
                level: self::toMonologLevel($level),
                message: $message,
                context: $context,
                extra: [],
            );
            $handled = false;

            foreach ($this->handlers as $handler) {
                if (false === $recordInitialized) {
                    // skip initializing the record as long as no handler is going to handle it
                    if (!$handler->isHandling($record)) {
                        continue;
                    }

                    try {
                        foreach ($this->processors as $processor) {
                            $record = $processor($record);
                        }
                        $recordInitialized = true;
                    } catch (Throwable $e) {
                        $this->handleException($e, $record);

                        return true;
                    }
                }

                // once the record is initialized, send it to all handlers as long as the bubbling chain is not interrupted
                try {
                    $handled = true;
                    if (true === $handler->handle(clone $record)) {
                        break;
                    }
                } catch (Throwable $e) {
                    $this->handleException($e, $record);

                    return true;
                }
            }

            return $handled;
        } finally {
            if ($this->detectCycles) {
                if (isset($fiber)) {
                    $this->fiberLogDepth[$fiber]--;
                } else {
                    $this->logDepth--;
                }
            }
        }
    }

    /**
     * Ends a log cycle and frees all resources used by handlers.
     *
     * Closing a Handler means flushing all buffers and freeing any open resources/handles.
     * Handlers that have been closed should be able to accept log records again and re-open
     * themselves on demand, but this may not always be possible depending on implementation.
     *
     * This is useful at the end of a request and will be called automatically on every handler
     * when they get destructed.
     */
    public function close(): void
    {
        foreach ($this->handlers as $handler) {
            $handler->close();
        }
    }

    /**
     * Ends a log cycle and resets all handlers and processors to their initial state.
     *
     * Resetting a Handler or a Processor means flushing/cleaning all buffers, resetting internal
     * state, and getting it back to a state in which it can receive log records again.
     *
     * This is useful in case you want to avoid logs leaking between two requests or jobs when you
     * have a long running process like a worker or an application server serving multiple requests
     * in one process.
     */
    public function reset(): void
    {
        foreach ($this->handlers as $handler) {
            if ($handler instanceof ResettableInterface) {
                $handler->reset();
            }
        }

        foreach ($this->processors as $processor) {
            if ($processor instanceof ResettableInterface) {
                $processor->reset();
            }
        }
    }

    /**
     * Gets the name of the logging level as a string.
     *
     * This still returns a string instead of a Level for BC, but new code should not rely on this method.
     *
     * @throws \Psr\Log\InvalidArgumentException If level is not defined
     *
     * @phpstan-param  value-of<Level::VALUES>|Level $level
     * @phpstan-return value-of<Level::NAMES>
     *
     * @deprecated Since 3.0, use {@see toMonologLevel} or {@see \Monolog\Level->getName()} instead
     */
    public static function getLevelName(int|Level $level): string
    {
        return self::toMonologLevel($level)->getName();
    }

    /**
     * Converts PSR-3 levels to Monolog ones if necessary
     *
     * @param  int|string|Level|LogLevel::*      $level Level number (monolog) or name (PSR-3)
     * @throws \Psr\Log\InvalidArgumentException If level is not defined
     *
     * @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $level
     */
    public static function toMonologLevel(string|int|Level $level): Level
    {
        if ($level instanceof Level) {
            return $level;
        }

        if (\is_string($level)) {
            if (is_numeric($level)) {
                $levelEnum = Level::tryFrom((int) $level);
                if ($levelEnum === null) {
                    throw new InvalidArgumentException('Level "'.$level.'" is not defined, use one of: '.implode(', ', Level::NAMES + Level::VALUES));
                }

                return $levelEnum;
            }

            // Contains first char of all log levels and avoids using strtoupper() which may have
            // strange results depending on locale (for example, "i" will become "Ä°" in Turkish locale)
            $upper = strtr(substr($level, 0, 1), 'dinweca', 'DINWECA') . strtolower(substr($level, 1));
            if (\defined(Level::class.'::'.$upper)) {
                return \constant(Level::class . '::' . $upper);
            }

            throw new InvalidArgumentException('Level "'.$level.'" is not defined, use one of: '.implode(', ', Level::NAMES + Level::VALUES));
        }

        $levelEnum = Level::tryFrom($level);
        if ($levelEnum === null) {
            throw new InvalidArgumentException('Level "'.var_export($level, true).'" is not defined, use one of: '.implode(', ', Level::NAMES + Level::VALUES));
        }

        return $levelEnum;
    }

    /**
     * Checks whether the Logger has a handler that listens on the given level
     *
     * @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $level
     */
    public function isHandling(int|string|Level $level): bool
    {
        $record = new LogRecord(
            datetime: $this->createDateTime(),
            channel: $this->name,
            message: '',
            level: self::toMonologLevel($level),
        );

        foreach ($this->handlers as $handler) {
            if ($handler->isHandling($record)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Set a custom exception handler that will be called if adding a new record fails
     *
     * The Closure will receive an exception object and the record that failed to be logged
     *
     * @return $this
     */
    public function setExceptionHandler(Closure|null $callback): self
    {
        $this->exceptionHandler = $callback;

        return $this;
    }

    public function getExceptionHandler(): Closure|null
    {
        return $this->exceptionHandler;
    }

    /**
     * Adds a log record at an arbitrary level.
     *
     * This method allows for compatibility with common interfaces.
     *
     * @param mixed             $level   The log level (a Monolog, PSR-3 or RFC 5424 level)
     * @param string|Stringable $message The log message
     * @param mixed[]           $context The log context
     *
     * @phpstan-param Level|LogLevel::* $level
     */
    public function log($level, string|\Stringable $message, array $context = []): void
    {
        if (!$level instanceof Level) {
            if (!\is_string($level) && !\is_int($level)) {
                throw new \InvalidArgumentException('$level is expected to be a string, int or '.Level::class.' instance');
            }

            if (isset(self::RFC_5424_LEVELS[$level])) {
                $level = self::RFC_5424_LEVELS[$level];
            }

            $level = static::toMonologLevel($level);
        }

        $this->addRecord($level, (string) $message, $context);
    }

    /**
     * Adds a log record at the DEBUG level.
     *
     * This method allows for compatibility with common interfaces.
     *
     * @param string|Stringable $message The log message
     * @param mixed[]           $context The log context
     */
    public function debug(string|\Stringable $message, array $context = []): void
    {
        $this->addRecord(Level::Debug, (string) $message, $context);
    }

    /**
     * Adds a log record at the INFO level.
     *
     * This method allows for compatibility with common interfaces.
     *
     * @param string|Stringable $message The log message
     * @param mixed[]           $context The log context
     */
    public function info(string|\Stringable $message, array $context = []): void
    {
        $this->addRecord(Level::Info, (string) $message, $context);
    }

    /**
     * Adds a log record at the NOTICE level.
     *
     * This method allows for compatibility with common interfaces.
     *
     * @param string|Stringable $message The log message
     * @param mixed[]           $context The log context
     */
    public function notice(string|\Stringable $message, array $context = []): void
    {
        $this->addRecord(Level::Notice, (string) $message, $context);
    }

    /**
     * Adds a log record at the WARNING level.
     *
     * This method allows for compatibility with common interfaces.
     *
     * @param string|Stringable $message The log message
     * @param mixed[]           $context The log context
     */
    public function warning(string|\Stringable $message, array $context = []): void
    {
        $this->addRecord(Level::Warning, (string) $message, $context);
    }

    /**
     * Adds a log record at the ERROR level.
     *
     * This method allows for compatibility with common interfaces.
     *
     * @param string|Stringable $message The log message
     * @param mixed[]           $context The log context
     */
    public function error(string|\Stringable $message, array $context = []): void
    {
        $this->addRecord(Level::Error, (string) $message, $context);
    }

    /**
     * Adds a log record at the CRITICAL level.
     *
     * This method allows for compatibility with common interfaces.
     *
     * @param string|Stringable $message The log message
     * @param mixed[]           $context The log context
     */
    public function critical(string|\Stringable $message, array $context = []): void
    {
        $this->addRecord(Level::Critical, (string) $message, $context);
    }

    /**
     * Adds a log record at the ALERT level.
     *
     * This method allows for compatibility with common interfaces.
     *
     * @param string|Stringable $message The log message
     * @param mixed[]           $context The log context
     */
    public function alert(string|\Stringable $message, array $context = []): void
    {
        $this->addRecord(Level::Alert, (string) $message, $context);
    }

    /**
     * Adds a log record at the EMERGENCY level.
     *
     * This method allows for compatibility with common interfaces.
     *
     * @param string|Stringable $message The log message
     * @param mixed[]           $context The log context
     */
    public function emergency(string|\Stringable $message, array $context = []): void
    {
        $this->addRecord(Level::Emergency, (string) $message, $context);
    }

    /**
     * Sets the timezone to be used for the timestamp of log records.
     *
     * @return $this
     */
    public function setTimezone(DateTimeZone $tz): self
    {
        $this->timezone = $tz;

        return $this;
    }

    /**
     * Returns the timezone to be used for the timestamp of log records.
     */
    public function getTimezone(): DateTimeZone
    {
        return $this->timezone;
    }

    /**
     * Sets the clock to read the current time from when timestamping log records.
     *
     * Pass null to go back to reading the engine's current time. The timezone
     * configured on the logger still decides how the timestamp is rendered.
     *
     * @return $this
     */
    public function setClock(ClockInterface|null $clock): self
    {
        $this->clock = $clock;

        return $this;
    }

    /**
     * Returns the clock used to timestamp log records, or null if the engine's current time is used.
     */
    public function getClock(): ClockInterface|null
    {
        return $this->clock;
    }

    /**
     * Builds the timestamp of a new record, reading the current time from the clock if one is set.
     */
    private function createDateTime(): JsonSerializableDateTimeImmutable
    {
        if (null === $this->clock) {
            return new JsonSerializableDateTimeImmutable($this->microsecondTimestamps, $this->timezone);
        }

        $now = $this->clock->now();

        if ($now instanceof JsonSerializableDateTimeImmutable) {
            return $now;
        }

        $datetime = new JsonSerializableDateTimeImmutable($this->microsecondTimestamps, $this->timezone);

        // The instant is applied without ever going through a local wall clock time, which
        // is ambiguous across a DST transition, and without the "@U.u" notation, which is
        // off by one second before 1970 and replaces the named timezone with an offset.
        $datetime = $datetime->setTimestamp($now->getTimestamp());
        if (\PHP_VERSION_ID >= 80400) {
            return $datetime->setMicrosecond($now->getMicrosecond());
        }

        $microseconds = (int) $now->format('u');
        if (0 !== $microseconds) {
            // DateInterval has no notation for microseconds, they can only be set on the property
            $interval = new \DateInterval('PT0S');
            $interval->f = $microseconds / 1000000;

            $datetime = $datetime->add($interval);
        }

        return $datetime;
    }

    /**
     * Delegates exception management to the custom exception handler,
     * or throws the exception if no custom handler is set.
     */
    protected function handleException(Throwable $e, LogRecord $record): void
    {
        if (null === $this->exceptionHandler) {
            throw $e;
        }

        ($this->exceptionHandler)($e, $record);
    }

    /**
     * @return array<string, mixed>
     */
    public function __serialize(): array
    {
        return [
            'name' => $this->name,
            'handlers' => $this->handlers,
            'processors' => $this->processors,
            'microsecondTimestamps' => $this->microsecondTimestamps,
            'timezone' => $this->timezone,
            'exceptionHandler' => $this->exceptionHandler,
            'logDepth' => $this->logDepth,
            'detectCycles' => $this->detectCycles,
        ];
    }

    /**
     * @param array<string, mixed> $data
     */
    public function __unserialize(array $data): void
    {
        foreach (['name', 'handlers', 'processors', 'microsecondTimestamps', 'timezone', 'exceptionHandler', 'logDepth', 'detectCycles'] as $property) {
            if (isset($data[$property])) {
                $this->$property = $data[$property];
            }
        }

        $this->fiberLogDepth = new \WeakMap();
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog;

use Closure;
use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;

/**
 * Monolog error handler
 *
 * A facility to enable logging of runtime errors, exceptions and fatal errors.
 *
 * Quick setup: <code>ErrorHandler::register($logger);</code>
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 */
class ErrorHandler
{
    private Closure|null $previousExceptionHandler = null;

    /** @var array<class-string, LogLevel::*> an array of class name to LogLevel::* constant mapping */
    private array $uncaughtExceptionLevelMap = [];

    /** @var Closure|true|null */
    private Closure|bool|null $previousErrorHandler = null;

    /** @var array<int, LogLevel::*> an array of E_* constant to LogLevel::* constant mapping */
    private array $errorLevelMap = [];

    private bool $handleOnlyReportedErrors = true;

    private bool $hasFatalErrorHandler = false;

    /** @var int bitmask of E_* constants to capture stack traces for, 0 to disable */
    private int $captureStackTraceTypes = 0;

    private string $fatalLevel = LogLevel::ALERT;

    private string|null $reservedMemory = null;

    /** @var ?array{type: int, message: string, file: string, line: int, trace: mixed} */
    private array|null $lastFatalData = null;

    private const FATAL_ERRORS = [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR];

    public function __construct(
        private LoggerInterface $logger
    ) {
    }

    /**
     * Registers a new ErrorHandler for a given Logger
     *
     * By default it will handle errors, exceptions and fatal errors
     *
     * @param  array<int, LogLevel::*>|false          $errorLevelMap     an array of E_* constant to LogLevel::* constant mapping, or false to disable error handling
     * @param  array<class-string, LogLevel::*>|false $exceptionLevelMap an array of class name to LogLevel::* constant mapping, or false to disable exception handling
     * @param  LogLevel::*|null|false                 $fatalLevel        a LogLevel::* constant, null to use the default LogLevel::ALERT or false to disable fatal error handling
     * @return static
     */
    public static function register(LoggerInterface $logger, $errorLevelMap = [], $exceptionLevelMap = [], $fatalLevel = null): self
    {
        /** @phpstan-ignore-next-line */
        $handler = new static($logger);
        if ($errorLevelMap !== false) {
            $handler->registerErrorHandler($errorLevelMap);
        }
        if ($exceptionLevelMap !== false) {
            $handler->registerExceptionHandler($exceptionLevelMap);
        }
        if ($fatalLevel !== false) {
            $handler->registerFatalHandler($fatalLevel);
        }

        return $handler;
    }

    /**
     * @param  array<class-string, LogLevel::*> $levelMap an array of class name to LogLevel::* constant mapping
     * @return $this
     */
    public function registerExceptionHandler(array $levelMap = [], bool $callPrevious = true): self
    {
        $prev = set_exception_handler(function (\Throwable $e): void {
            $this->handleException($e);
        });
        $this->uncaughtExceptionLevelMap = $levelMap;
        foreach ($this->defaultExceptionLevelMap() as $class => $level) {
            if (!isset($this->uncaughtExceptionLevelMap[$class])) {
                $this->uncaughtExceptionLevelMap[$class] = $level;
            }
        }
        if ($callPrevious && null !== $prev) {
            $this->previousExceptionHandler = $prev(...);
        }

        return $this;
    }

    /**
     * @param  array<int, LogLevel::*> $levelMap an array of E_* constant to LogLevel::* constant mapping
     * @return $this
     */
    public function registerErrorHandler(array $levelMap = [], bool $callPrevious = true, int $errorTypes = -1, bool $handleOnlyReportedErrors = true): self
    {
        $prev = set_error_handler($this->handleError(...), $errorTypes);
        $this->errorLevelMap = array_replace($this->defaultErrorLevelMap(), $levelMap);
        if ($callPrevious) {
            $this->previousErrorHandler = $prev !== null ? $prev(...) : true;
        } else {
            $this->previousErrorHandler = null;
        }

        $this->handleOnlyReportedErrors = $handleOnlyReportedErrors;

        return $this;
    }

    /**
     * @param  LogLevel::*|null $level              a LogLevel::* constant, null to use the default LogLevel::ALERT
     * @param  int              $reservedMemorySize Amount of KBs to reserve in memory so that it can be freed when handling fatal errors giving Monolog some room in memory to get its job done
     * @return $this
     */
    public function registerFatalHandler($level = null, int $reservedMemorySize = 20): self
    {
        register_shutdown_function($this->handleFatalError(...));

        $this->reservedMemory = str_repeat(' ', 1024 * $reservedMemorySize);
        $this->fatalLevel = null === $level ? LogLevel::ALERT : $level;
        $this->hasFatalErrorHandler = true;

        return $this;
    }

    /**
     * Attaches an ErrorException carrying the stack trace of the error to the log record's context
     *
     * PHP does not report where a warning/notice/deprecation came from, so without this the records
     * only point at the line which raised the error, and not at its caller. Formatters rendering
     * stack traces (e.g. LineFormatter::includeStacktraces) then show the full call site.
     *
     * This is off by default because context.exception makes errors look like exceptions to the
     * handlers which treat that key specially, e.g. NewRelicHandler reports them via
     * newrelic_notice_error and RollbarHandler as exception occurrences, and because formatters
     * extending NormalizerFormatter then include the whole trace in every record. Narrowing
     * $errorTypes is wise if you log a lot of errors: E_DEPRECATED in particular can be very
     * frequent in legacy code while rarely being worth a stack trace.
     *
     * @param  int   $errorTypes bitmask of E_* constants to capture stack traces for
     * @return $this
     */
    public function captureStackTraces(bool $capture = true, int $errorTypes = E_ALL): self
    {
        $this->captureStackTraceTypes = $capture ? $errorTypes : 0;

        return $this;
    }

    /**
     * @return array<class-string, LogLevel::*>
     */
    protected function defaultExceptionLevelMap(): array
    {
        return [
            'ParseError' => LogLevel::CRITICAL,
            'Throwable' => LogLevel::ERROR,
        ];
    }

    /**
     * @return array<int, LogLevel::*>
     */
    protected function defaultErrorLevelMap(): array
    {
        return [
            E_ERROR             => LogLevel::CRITICAL,
            E_WARNING           => LogLevel::WARNING,
            E_PARSE             => LogLevel::ALERT,
            E_NOTICE            => LogLevel::NOTICE,
            E_CORE_ERROR        => LogLevel::CRITICAL,
            E_CORE_WARNING      => LogLevel::WARNING,
            E_COMPILE_ERROR     => LogLevel::ALERT,
            E_COMPILE_WARNING   => LogLevel::WARNING,
            E_USER_ERROR        => LogLevel::ERROR,
            E_USER_WARNING      => LogLevel::WARNING,
            E_USER_NOTICE       => LogLevel::NOTICE,
            2048                => LogLevel::NOTICE, // E_STRICT
            E_RECOVERABLE_ERROR => LogLevel::ERROR,
            E_DEPRECATED        => LogLevel::NOTICE,
            E_USER_DEPRECATED   => LogLevel::NOTICE,
        ];
    }

    private function handleException(\Throwable $e): never
    {
        $level = LogLevel::ERROR;
        foreach ($this->uncaughtExceptionLevelMap as $class => $candidate) {
            if ($e instanceof $class) {
                $level = $candidate;
                break;
            }
        }

        $this->logger->log(
            $level,
            sprintf('Uncaught Exception %s: "%s" at %s line %s', Utils::getClass($e), $e->getMessage(), $e->getFile(), $e->getLine()),
            ['exception' => $e]
        );

        if (null !== $this->previousExceptionHandler) {
            ($this->previousExceptionHandler)($e);
        }

        if (!headers_sent() && \in_array(strtolower((string) \ini_get('display_errors')), ['0', '', 'false', 'off', 'none', 'no'], true)) {
            // PHP 8.5+ warns if header('HTTP/...') already staged a status line, which is undetectable from userland
            @http_response_code(500);
        }

        exit(255);
    }

    private function handleError(int $code, string $message, string $file = '', int $line = 0): bool
    {
        if ($this->handleOnlyReportedErrors && 0 === (error_reporting() & $code)) {
            return false;
        }

        $isFatal = $this->hasFatalErrorHandler && \in_array($code, self::FATAL_ERRORS, true);

        $trace = null;
        if ($isFatal || 0 !== ($this->captureStackTraceTypes & $code)) {
            $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
            array_shift($trace); // Exclude handleError from trace
        }

        // fatal error codes are ignored if a fatal error handler is present as well to avoid duplicate log entries
        if (!$isFatal) {
            $level = $this->errorLevelMap[$code] ?? LogLevel::CRITICAL;
            $context = ['code' => $code, 'message' => $message, 'file' => $file, 'line' => $line];
            if (null !== $trace) {
                $context['exception'] = self::createErrorException($code, $message, $file, $line, $trace);
            }
            $this->logger->log($level, self::codeToString($code).': '.$message, $context);
        } else {
            $this->lastFatalData = ['type' => $code, 'message' => $message, 'file' => $file, 'line' => $line, 'trace' => $trace];
        }

        if ($this->previousErrorHandler === true) {
            return false;
        }
        if ($this->previousErrorHandler instanceof Closure) {
            return (bool) ($this->previousErrorHandler)($code, $message, $file, $line);
        }

        return true;
    }

    /**
     * @private
     */
    public function handleFatalError(): void
    {
        $this->reservedMemory = '';

        if (\is_array($this->lastFatalData)) {
            $lastError = $this->lastFatalData;
        } else {
            $lastError = error_get_last();
        }
        if (\is_array($lastError) && \in_array($lastError['type'], self::FATAL_ERRORS, true)) {
            // PHP 8.5+ reports a trace for fatal errors which never reach the error handler (OOM, timeouts)
            $trace = $lastError['trace'] ?? null;
            $context = ['code' => $lastError['type'], 'message' => $lastError['message'], 'file' => $lastError['file'], 'line' => $lastError['line'], 'trace' => $trace];
            if (\is_array($trace)) {
                /** @var list<array<string, mixed>> $trace */
                $context['exception'] = self::createErrorException($lastError['type'], $lastError['message'], $lastError['file'], $lastError['line'], $trace);
            }
            $this->logger->log(
                $this->fatalLevel,
                'Fatal Error ('.self::codeToString($lastError['type']).'): '.$lastError['message'],
                $context
            );

            if ($this->logger instanceof Logger) {
                foreach ($this->logger->getHandlers() as $handler) {
                    $handler->close();
                }
            }
        }
    }

    /**
     * @param list<array<string, mixed>> $trace
     */
    private static function createErrorException(int $code, string $message, string $file, int $line, array $trace): \ErrorException
    {
        $e = new \ErrorException($message, 0, $code, $file, $line);

        // Exception::getTrace() is final, so overwriting the private property is the only way to
        // report the real call site instead of ErrorHandler's own frame
        (new \ReflectionProperty(\Exception::class, 'trace'))->setValue($e, $trace);

        return $e;
    }

    private static function codeToString(int $code): string
    {
        return match ($code) {
            E_ERROR => 'E_ERROR',
            E_WARNING => 'E_WARNING',
            E_PARSE => 'E_PARSE',
            E_NOTICE => 'E_NOTICE',
            E_CORE_ERROR => 'E_CORE_ERROR',
            E_CORE_WARNING => 'E_CORE_WARNING',
            E_COMPILE_ERROR => 'E_COMPILE_ERROR',
            E_COMPILE_WARNING => 'E_COMPILE_WARNING',
            E_USER_ERROR => 'E_USER_ERROR',
            E_USER_WARNING => 'E_USER_WARNING',
            E_USER_NOTICE => 'E_USER_NOTICE',
            2048 => 'E_STRICT',
            E_RECOVERABLE_ERROR => 'E_RECOVERABLE_ERROR',
            E_DEPRECATED => 'E_DEPRECATED',
            E_USER_DEPRECATED => 'E_USER_DEPRECATED',
            default => 'Unknown PHP error',
        };
    }
}
<?php declare(strict_types=1);

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Monolog;

class_alias(JsonSerializableDateTimeImmutable::class, 'Monolog\DateTimeImmutable');

// @phpstan-ignore-next-line
if (false) {
    /**
     * @deprecated Use \Monolog\JsonSerializableDateTimeImmutable instead.
     */
    class DateTimeImmutable extends JsonSerializableDateTimeImmutable
    {
    }
}
{
    "name": "monolog/monolog",
    "description": "Sends your logs to files, sockets, inboxes, databases and various web services",
    "keywords": ["log", "logging", "psr-3"],
    "homepage": "https://github.com/Seldaek/monolog",
    "type": "library",
    "license": "MIT",
    "authors": [
        {
            "name": "Jordi Boggiano",
            "email": "j.boggiano@seld.be",
            "homepage": "https://seld.be"
        }
    ],
    "require": {
        "php": ">=8.1",
        "psr/log": "^2.0 || ^3.0"
    },
    "require-dev": {
        "ext-json": "*",
        "aws/aws-sdk-php": "^3.0",
        "doctrine/couchdb": "~1.0@dev",
        "elasticsearch/elasticsearch": "^7 || ^8",
        "graylog2/gelf-php": "^1.4.2 || ^2.0",
        "guzzlehttp/guzzle": "^7.4.5",
        "guzzlehttp/psr7": "^2.2",
        "mongodb/mongodb": "^1.8 || ^2.0",
        "php-amqplib/php-amqplib": "~2.4 || ^3",
        "php-console/php-console": "^3.1.8",
        "phpstan/phpstan": "^2",
        "phpstan/phpstan-deprecation-rules": "^2",
        "phpstan/phpstan-strict-rules": "^2",
        "phpunit/phpunit": "^10.5.17 || ^11.0.7",
        "predis/predis": "^1.1 || ^2",
        "psr/clock": "^1.0",
        "rollbar/rollbar": "^4.0",
        "ruflin/elastica": "^7 || ^8",
        "symfony/mailer": "^5.4 || ^6",
        "symfony/mime": "^5.4 || ^6"
    },
    "suggest": {
        "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server",
        "doctrine/couchdb": "Allow sending log messages to a CouchDB server",
        "ruflin/elastica": "Allow sending log messages to an Elastic Search server",
        "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client",
        "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib",
        "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)",
        "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)",
        "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)",
        "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB",
        "rollbar/rollbar": "Allow sending log messages to Rollbar",
        "ext-mbstring": "Allow to work properly with unicode symbols",
        "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)",
        "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler",
        "ext-openssl": "Required to send log messages using SSL",
        "psr/clock": "Required to pass a clock to the Logger and control the timestamp of log records"
    },
    "autoload": {
        "psr-4": {"Monolog\\": "src/Monolog"}
    },
    "autoload-dev": {
        "psr-4": {"Monolog\\": "tests/Monolog"}
    },
    "provide": {
        "psr/log-implementation": "3.0.0"
    },
    "extra": {
        "branch-alias": {
            "dev-main": "3.x-dev"
        }
    },
    "scripts": {
        "test": "@php vendor/bin/phpunit",
        "phpstan": "@php vendor/bin/phpstan analyse"
    },
    "config": {
        "lock": false,
        "sort-packages": true,
        "platform-check": false,
        "allow-plugins": {
            "php-http/discovery": false
        }
    }
}
<?php
declare(strict_types=1);
namespace ParagonIE\ConstantTime;

use SensitiveParameter;
use TypeError;

/**
 *  Copyright (c) 2016 - 2022 Paragon Initiative Enterprises.
 *  Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com)
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in all
 *  copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 *  SOFTWARE.
 */

/**
 * Class RFC4648
 *
 * This class conforms strictly to the RFC
 *
 * @package ParagonIE\ConstantTime
 * @api
 */
abstract class RFC4648
{
    /**
     * RFC 4648 Base64 encoding
     *
     * "foo" -> "Zm9v"
     *
     * @param string $str
     * @return string
     *
     * @throws TypeError
     */
    public static function base64Encode(
        #[SensitiveParameter]
        string $str
    ): string {
        return Base64::encode($str);
    }

    /**
     * RFC 4648 Base64 decoding
     *
     * "Zm9v" -> "foo"
     *
     * @param string $str
     * @return string
     *
     * @throws TypeError
     */
    public static function base64Decode(
        #[SensitiveParameter]
        string $str
    ): string {
        return Base64::decode($str, true);
    }

    /**
     * RFC 4648 Base64 (URL Safe) encoding
     *
     * "foo" -> "Zm9v"
     *
     * @param string $str
     * @return string
     *
     * @throws TypeError
     */
    public static function base64UrlSafeEncode(
        #[SensitiveParameter]
        string $str
    ): string {
        return Base64UrlSafe::encode($str);
    }

    /**
     * RFC 4648 Base64 (URL Safe) decoding
     *
     * "Zm9v" -> "foo"
     *
     * @param string $str
     * @return string
     *
     * @throws TypeError
     */
    public static function base64UrlSafeDecode(
        #[SensitiveParameter]
        string $str
    ): string {
        return Base64UrlSafe::decode($str, true);
    }

    /**
     * RFC 4648 Base32 encoding
     *
     * "foo" -> "MZXW6==="
     *
     * @param string $str
     * @return string
     *
     * @throws TypeError
     */
    public static function base32Encode(
        #[SensitiveParameter]
        string $str
    ): string {
        return Base32::encodeUpper($str);
    }

    /**
     * RFC 4648 Base32 encoding
     *
     * "MZXW6===" -> "foo"
     *
     * @param string $str
     * @return string
     *
     * @throws TypeError
     */
    public static function base32Decode(
        #[SensitiveParameter]
        string $str
    ): string {
        return Base32::decodeUpper($str, true);
    }

    /**
     * RFC 4648 Base32-Hex encoding
     *
     * "foo" -> "CPNMU==="
     *
     * @param string $str
     * @return string
     *
     * @throws TypeError
     */
    public static function base32HexEncode(
        #[SensitiveParameter]
        string $str
    ): string {
        return Base32::encodeUpper($str);
    }

    /**
     * RFC 4648 Base32-Hex decoding
     *
     * "CPNMU===" -> "foo"
     *
     * @param string $str
     * @return string
     *
     * @throws TypeError
     */
    public static function base32HexDecode(
        #[SensitiveParameter]
        string $str
    ): string {
        return Base32::decodeUpper($str, true);
    }

    /**
     * RFC 4648 Base16 decoding
     *
     * "foo" -> "666F6F"
     *
     * @param string $str
     * @return string
     *
     * @throws TypeError
     */
    public static function base16Encode(
        #[SensitiveParameter]
        string $str
    ): string {
        return Hex::encodeUpper($str);
    }

    /**
     * RFC 4648 Base16 decoding
     *
     * "666F6F" -> "foo"
     *
     * @param string $str
     * @return string
     */
    public static function base16Decode(
        #[SensitiveParameter]
        string $str
    ): string {
        return Hex::decode($str, true);
    }
}
<?php
declare(strict_types=1);
namespace ParagonIE\ConstantTime;

use Override;
use RangeException;
use SensitiveParameter;
use SodiumException;
use TypeError;
use function extension_loaded;
use function pack;
use function sodium_bin2hex;
use function sodium_hex2bin;
use function strlen;
use function unpack;

/**
 *  Copyright (c) 2016 - 2025 Paragon Initiative Enterprises.
 *  Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com)
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in all
 *  copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 *  SOFTWARE.
 */

/**
 * Class Hex
 * @package ParagonIE\ConstantTime
 */
abstract class Hex implements EncoderInterface
{
    /**
     * Convert a binary string into a hexadecimal string without cache-timing
     * leaks
     *
     * @param string $binString (raw binary)
     * @return string
     * @throws TypeError
     */
    #[Override]
    public static function encode(
        #[SensitiveParameter]
        string $binString
    ): string {
        if (extension_loaded('sodium')) {
            try {
                return sodium_bin2hex($binString);
            } catch (SodiumException $ex) {
                throw new RangeException($ex->getMessage(), $ex->getCode(), $ex);
            }
        }
        $hex = '';
        $len = strlen($binString);
        for ($i = 0; $i < $len; ++$i) {
            /** @var array<int, int> $chunk */
            $chunk = unpack('C', $binString[$i]);
            $c = $chunk[1] & 0xf;
            $b = $chunk[1] >> 4;

            $hex .= pack(
                'CC',
                (87 + $b + ((($b - 10) >> 8) & ~38)),
                (87 + $c + ((($c - 10) >> 8) & ~38))
            );
        }
        return $hex;
    }

    /**
     * Convert a binary string into a hexadecimal string without cache-timing
     * leaks, returning uppercase letters (as per RFC 4648)
     *
     * @param string $binString (raw binary)
     * @return string
     * @throws TypeError
     */
    public static function encodeUpper(
        #[SensitiveParameter]
        string $binString
    ): string {
        $hex = '';
        $len = strlen($binString);

        for ($i = 0; $i < $len; ++$i) {
            /** @var array<int, int> $chunk */
            $chunk = unpack('C', $binString[$i]);
            $c = $chunk[1] & 0xf;
            $b = $chunk[1] >> 4;

            $hex .= pack(
                'CC',
                (55 + $b + ((($b - 10) >> 8) & ~6)),
                (55 + $c + ((($c - 10) >> 8) & ~6))
            );
        }
        return $hex;
    }

    /**
     * Convert a hexadecimal string into a binary string without cache-timing
     * leaks
     *
     * @param string $encodedString
     * @param bool $strictPadding
     * @return string (raw binary)
     * @throws RangeException
     */
    #[Override]
    public static function decode(
        #[SensitiveParameter]
        string $encodedString,
        bool $strictPadding = false
    ): string {
        if (extension_loaded('sodium') && $strictPadding) {
            try {
                return sodium_hex2bin($encodedString);
            } catch (SodiumException $ex) {
                throw new RangeException($ex->getMessage(), $ex->getCode(), $ex);
            }
        }
        $hex_pos = 0;
        $bin = '';
        $c_acc = 0;
        $hex_len = strlen($encodedString);
        $state = 0;
        if (($hex_len & 1) !== 0) {
            if ($strictPadding) {
                throw new RangeException(
                    'Expected an even number of hexadecimal characters'
                );
            } else {
                $encodedString = '0' . $encodedString;
                ++$hex_len;
            }
        }

        /** @var array<int, int> $chunk */
        $chunk = unpack('C*', $encodedString);
        while ($hex_pos < $hex_len) {
            ++$hex_pos;
            $c = $chunk[$hex_pos];
            $c_num = $c ^ 48;
            $c_num0 = ($c_num - 10) >> 8;
            $c_alpha = ($c & ~32) - 55;
            $c_alpha0 = (($c_alpha - 10) ^ ($c_alpha - 16)) >> 8;

            if (($c_num0 | $c_alpha0) === 0) {
                throw new RangeException(
                    'Expected hexadecimal character'
                );
            }
            $c_val = ($c_num0 & $c_num) | ($c_alpha & $c_alpha0);
            if ($state === 0) {
                $c_acc = $c_val * 16;
            } else {
                $bin .= pack('C', $c_acc | $c_val);
            }
            $state ^= 1;
        }
        return $bin;
    }
}
<?php
declare(strict_types=1);
namespace ParagonIE\ConstantTime;

use Override;

/**
 *  Copyright (c) 2016 - 2022 Paragon Initiative Enterprises.
 *  Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com)
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in all
 *  copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 *  SOFTWARE.
 */

/**
 * Class Base64UrlSafe
 * [A-Z][a-z][0-9]\-_
 *
 * @package ParagonIE\ConstantTime
 */
abstract class Base64UrlSafe extends Base64
{

    /**
     * Uses bitwise operators instead of table-lookups to turn 6-bit integers
     * into 8-bit integers.
     *
     * Base64 character set:
     * [A-Z]      [a-z]      [0-9]      -     _
     * 0x41-0x5a, 0x61-0x7a, 0x30-0x39, 0x2d, 0x5f
     *
     * @param int $src
     * @return int
     */
    #[Override]
    protected static function decode6Bits(int $src): int
    {
        $ret = -1;

        // if ($src > 0x40 && $src < 0x5b) $ret += $src - 0x41 + 1; // -64
        $ret += (((0x40 - $src) & ($src - 0x5b)) >> 8) & ($src - 64);

        // if ($src > 0x60 && $src < 0x7b) $ret += $src - 0x61 + 26 + 1; // -70
        $ret += (((0x60 - $src) & ($src - 0x7b)) >> 8) & ($src - 70);

        // if ($src > 0x2f && $src < 0x3a) $ret += $src - 0x30 + 52 + 1; // 5
        $ret += (((0x2f - $src) & ($src - 0x3a)) >> 8) & ($src + 5);

        // if ($src == 0x2c) $ret += 62 + 1;
        $ret += (((0x2c - $src) & ($src - 0x2e)) >> 8) & 63;

        // if ($src == 0x5f) ret += 63 + 1;
        $ret += (((0x5e - $src) & ($src - 0x60)) >> 8) & 64;

        return $ret;
    }

    /**
     * Uses bitwise operators instead of table-lookups to turn 8-bit integers
     * into 6-bit integers.
     *
     * @param int $src
     * @return string
     */
    #[Override]
    protected static function encode6Bits(int $src): string
    {
        $diff = 0x41;

        // if ($src > 25) $diff += 0x61 - 0x41 - 26; // 6
        $diff += ((25 - $src) >> 8) & 6;

        // if ($src > 51) $diff += 0x30 - 0x61 - 26; // -75
        $diff -= ((51 - $src) >> 8) & 75;

        // if ($src > 61) $diff += 0x2d - 0x30 - 10; // -13
        $diff -= ((61 - $src) >> 8) & 13;

        // if ($src > 62) $diff += 0x5f - 0x2b - 1; // 3
        $diff += ((62 - $src) >> 8) & 49;

        return \pack('C', $src + $diff);
    }
}
<?php
declare(strict_types=1);
namespace ParagonIE\ConstantTime;

use SensitiveParameter;

/**
 *  Copyright (c) 2016 - 2022 Paragon Initiative Enterprises.
 *  Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com)
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in all
 *  copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 *  SOFTWARE.
 */

/**
 * Interface EncoderInterface
 * @package ParagonIE\ConstantTime
 */
interface EncoderInterface
{
    /**
     * Convert a binary string into a hexadecimal string without cache-timing
     * leaks
     *
     * @param string $binString (raw binary)
     * @return string
     */
    public static function encode(
        #[SensitiveParameter]
        string $binString
    ): string;

    /**
     * Convert a binary string into a hexadecimal string without cache-timing
     * leaks
     *
     * @param string $encodedString
     * @param bool $strictPadding Error on invalid padding
     * @return string (raw binary)
     */
    public static function decode(
        #[SensitiveParameter]
        string $encodedString,
        bool $strictPadding = false
    ): string;
}
<?php
declare(strict_types=1);
namespace ParagonIE\ConstantTime;

use SensitiveParameter;
use TypeError;
use function strlen;
use function substr;

/**
 *  Copyright (c) 2016 - 2022 Paragon Initiative Enterprises.
 *  Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com)
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in all
 *  copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 *  SOFTWARE.
 */

/**
 * Class Binary
 *
 * Binary string operators that don't choke on
 * mbstring.func_overload
 *
 * @package ParagonIE\ConstantTime
 */
abstract class Binary
{
    /**
     * Safe string length
     *
     * @ref mbstring.func_overload
     *
     * @param string $str
     * @return int
     */
    public static function safeStrlen(
        #[SensitiveParameter]
        string $str
    ): int {
        return strlen($str);
    }

    /**
     * Safe substring
     *
     * @ref mbstring.func_overload
     *
     * @staticvar boolean $exists
     * @param string $str
     * @param int $start
     * @param ?int $length
     * @return string
     *
     * @throws TypeError
     */
    public static function safeSubstr(
        #[SensitiveParameter]
        string $str,
        int $start = 0,
        ?int $length = null
    ): string {
        if ($length === 0) {
            return '';
        }
        // Unlike mb_substr(), substr() doesn't accept NULL for length
        if ($length !== null) {
            return substr($str, $start, $length);
        } else {
            return substr($str, $start);
        }
    }
}
<?php
declare(strict_types=1);
namespace ParagonIE\ConstantTime;

use Override;
use function pack;

/**
 *  Copyright (c) 2016 - 2022 Paragon Initiative Enterprises.
 *  Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com)
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in all
 *  copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 *  SOFTWARE.
 */

/**
 * Class Base32Hex
 * [0-9][A-V]
 *
 * @package ParagonIE\ConstantTime
 */
abstract class Base32Hex extends Base32
{
    /**
     * Uses bitwise operators instead of table-lookups to turn 5-bit integers
     * into 8-bit integers.
     *
     * @param int $src
     * @return int
     */
    #[Override]
    protected static function decode5Bits(int $src): int
    {
        $ret = -1;

        // if ($src > 0x30 && $src < 0x3a) ret += $src - 0x2e + 1; // -47
        $ret += (((0x2f - $src) & ($src - 0x3a)) >> 8) & ($src - 47);

        // if ($src > 0x60 && $src < 0x77) ret += $src - 0x61 + 10 + 1; // -86
        $ret += (((0x60 - $src) & ($src - 0x77)) >> 8) & ($src - 86);

        return $ret;
    }

    /**
     * Uses bitwise operators instead of table-lookups to turn 5-bit integers
     * into 8-bit integers.
     *
     * @param int $src
     * @return int
     */
    #[Override]
    protected static function decode5BitsUpper(int $src): int
    {
        $ret = -1;

        // if ($src > 0x30 && $src < 0x3a) ret += $src - 0x2e + 1; // -47
        $ret += (((0x2f - $src) & ($src - 0x3a)) >> 8) & ($src - 47);

        // if ($src > 0x40 && $src < 0x57) ret += $src - 0x41 + 10 + 1; // -54
        $ret += (((0x40 - $src) & ($src - 0x57)) >> 8) & ($src - 54);

        return $ret;
    }

    /**
     * Uses bitwise operators instead of table-lookups to turn 8-bit integers
     * into 5-bit integers.
     *
     * @param int $src
     * @return string
     */
    #[Override]
    protected static function encode5Bits(int $src): string
    {
        $src += 0x30;

        // if ($src > 0x39) $src += 0x61 - 0x3a; // 39
        $src += ((0x39 - $src) >> 8) & 39;

        return pack('C', $src);
    }

    /**
     * Uses bitwise operators instead of table-lookups to turn 8-bit integers
     * into 5-bit integers.
     *
     * Uppercase variant.
     *
     * @param int $src
     * @return string
     */
    #[Override]
    protected static function encode5BitsUpper(int $src): string
    {
        $src += 0x30;

        // if ($src > 0x39) $src += 0x41 - 0x3a; // 7
        $src += ((0x39 - $src) >> 8) & 7;

        return pack('C', $src);
    }
}<?php
declare(strict_types=1);
namespace ParagonIE\ConstantTime;

use Override;

/**
 *  Copyright (c) 2016 - 2022 Paragon Initiative Enterprises.
 *  Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com)
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in all
 *  copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 *  SOFTWARE.
 */

/**
 * Class Base64DotSlash
 * ./[A-Z][a-z][0-9]
 *
 * @package ParagonIE\ConstantTime
 */
abstract class Base64DotSlash extends Base64
{
    /**
     * Uses bitwise operators instead of table-lookups to turn 6-bit integers
     * into 8-bit integers.
     *
     * Base64 character set:
     * ./         [A-Z]      [a-z]     [0-9]
     * 0x2e-0x2f, 0x41-0x5a, 0x61-0x7a, 0x30-0x39
     *
     * @param int $src
     * @return int
     */
    #[Override]
    protected static function decode6Bits(int $src): int
    {
        $ret = -1;

        // if ($src > 0x2d && $src < 0x30) ret += $src - 0x2e + 1; // -45
        $ret += (((0x2d - $src) & ($src - 0x30)) >> 8) & ($src - 45);

        // if ($src > 0x40 && $src < 0x5b) ret += $src - 0x41 + 2 + 1; // -62
        $ret += (((0x40 - $src) & ($src - 0x5b)) >> 8) & ($src - 62);

        // if ($src > 0x60 && $src < 0x7b) ret += $src - 0x61 + 28 + 1; // -68
        $ret += (((0x60 - $src) & ($src - 0x7b)) >> 8) & ($src - 68);

        // if ($src > 0x2f && $src < 0x3a) ret += $src - 0x30 + 54 + 1; // 7
        $ret += (((0x2f - $src) & ($src - 0x3a)) >> 8) & ($src + 7);

        return $ret;
    }

    /**
     * Uses bitwise operators instead of table-lookups to turn 8-bit integers
     * into 6-bit integers.
     *
     * @param int $src
     * @return string
     */
    #[Override]
    protected static function encode6Bits(int $src): string
    {
        $src += 0x2e;

        // if ($src > 0x2f) $src += 0x41 - 0x30; // 17
        $src += ((0x2f - $src) >> 8) & 17;

        // if ($src > 0x5a) $src += 0x61 - 0x5b; // 6
        $src += ((0x5a - $src) >> 8) & 6;

        // if ($src > 0x7a) $src += 0x30 - 0x7b; // -75
        $src -= ((0x7a - $src) >> 8) & 75;

        return \pack('C', $src);
    }
}
<?php
declare(strict_types=1);
namespace ParagonIE\ConstantTime;

use RangeException;
use SensitiveParameter;
use TypeError;

/**
 *  Copyright (c) 2016 - 2022 Paragon Initiative Enterprises.
 *  Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com)
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in all
 *  copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 *  SOFTWARE.
 */

/**
 * Class Encoding
 * @package ParagonIE\ConstantTime
 * @api
 */
abstract class Encoding
{
    /**
     * RFC 4648 Base32 encoding
     *
     * @param string $str
     * @return string
     * @throws TypeError
     */
    public static function base32Encode(
        #[SensitiveParameter]
        string $str
    ): string {
        return Base32::encode($str);
    }

    /**
     * RFC 4648 Base32 encoding
     *
     * @param string $str
     * @return string
     * @throws TypeError
     */
    public static function base32EncodeUpper(
        #[SensitiveParameter]
        string $str
    ): string {
        return Base32::encodeUpper($str);
    }

    /**
     * RFC 4648 Base32 decoding
     *
     * @param string $str
     * @return string
     * @throws TypeError
     */
    public static function base32Decode(
        #[SensitiveParameter]
        string $str
    ): string {
        return Base32::decode($str);
    }

    /**
     * RFC 4648 Base32 decoding
     *
     * @param string $str
     * @return string
     * @throws TypeError
     */
    public static function base32DecodeUpper(
        #[SensitiveParameter]
        string $str
    ): string {
        return Base32::decodeUpper($str);
    }

    /**
     * RFC 4648 Base32 encoding
     *
     * @param string $str
     * @return string
     * @throws TypeError
     */
    public static function base32HexEncode(
        #[SensitiveParameter]
        string $str
    ): string {
        return Base32Hex::encode($str);
    }

    /**
     * RFC 4648 Base32Hex encoding
     *
     * @param string $str
     * @return string
     * @throws TypeError
     */
    public static function base32HexEncodeUpper(
        #[SensitiveParameter]
        string $str
    ): string {
        return Base32Hex::encodeUpper($str);
    }

    /**
     * RFC 4648 Base32Hex decoding
     *
     * @param string $str
     * @return string
     * @throws TypeError
     */
    public static function base32HexDecode(
        #[SensitiveParameter]
        string $str
    ): string {
        return Base32Hex::decode($str);
    }

    /**
     * RFC 4648 Base32Hex decoding
     *
     * @param string $str
     * @return string
     * @throws TypeError
     */
    public static function base32HexDecodeUpper(
        #[SensitiveParameter]
        string $str
    ): string {
        return Base32Hex::decodeUpper($str);
    }

    /**
     * RFC 4648 Base64 encoding
     *
     * @param string $str
     * @return string
     * @throws TypeError
     */
    public static function base64Encode(
        #[SensitiveParameter]
        string $str
    ): string {
        return Base64::encode($str);
    }

    /**
     * RFC 4648 Base64 decoding
     *
     * @param string $str
     * @return string
     * @throws TypeError
     */
    public static function base64Decode(
        #[SensitiveParameter]
        string $str
    ): string {
        return Base64::decode($str);
    }

    /**
     * Encode into Base64
     *
     * Base64 character set "./[A-Z][a-z][0-9]"
     * @param string $str
     * @return string
     * @throws TypeError
     */
    public static function base64EncodeDotSlash(
        #[SensitiveParameter]
        string $str
    ): string {
        return Base64DotSlash::encode($str);
    }

    /**
     * Decode from base64 to raw binary
     *
     * Base64 character set "./[A-Z][a-z][0-9]"
     *
     * @param string $str
     * @return string
     * @throws RangeException
     * @throws TypeError
     */
    public static function base64DecodeDotSlash(
        #[SensitiveParameter]
        string $str
    ): string {
        return Base64DotSlash::decode($str);
    }

    /**
     * Encode into Base64
     *
     * Base64 character set "[.-9][A-Z][a-z]" or "./[0-9][A-Z][a-z]"
     * @param string $str
     * @return string
     * @throws TypeError
     */
    public static function base64EncodeDotSlashOrdered(
        #[SensitiveParameter]
        string $str
    ): string {
        return Base64DotSlashOrdered::encode($str);
    }

    /**
     * Decode from base64 to raw binary
     *
     * Base64 character set "[.-9][A-Z][a-z]" or "./[0-9][A-Z][a-z]"
     *
     * @param string $str
     * @return string
     * @throws RangeException
     * @throws TypeError
     */
    public static function base64DecodeDotSlashOrdered(
        #[SensitiveParameter]
        string $str
    ): string {
        return Base64DotSlashOrdered::decode($str);
    }

    /**
     * Convert a binary string into a hexadecimal string without cache-timing
     * leaks
     *
     * @param string $bin_string (raw binary)
     * @return string
     * @throws TypeError
     */
    public static function hexEncode(
        #[SensitiveParameter]
        string $bin_string
    ): string {
        return Hex::encode($bin_string);
    }

    /**
     * Convert a hexadecimal string into a binary string without cache-timing
     * leaks
     *
     * @param string $hex_string
     * @return string (raw binary)
     * @throws RangeException
     */
    public static function hexDecode(
        #[SensitiveParameter]
        string $hex_string
    ): string {
        return Hex::decode($hex_string);
    }

    /**
     * Convert a binary string into a hexadecimal string without cache-timing
     * leaks
     *
     * @param string $bin_string (raw binary)
     * @return string
     * @throws TypeError
     */
    public static function hexEncodeUpper(
        #[SensitiveParameter]
        string $bin_string
    ): string {
        return Hex::encodeUpper($bin_string);
    }

    /**
     * Convert a binary string into a hexadecimal string without cache-timing
     * leaks
     *
     * @param string $bin_string (raw binary)
     * @return string
     */
    public static function hexDecodeUpper(
        #[SensitiveParameter]
        string $bin_string
    ): string {
        return Hex::decode($bin_string);
    }
}
<?php
declare(strict_types=1);
namespace ParagonIE\ConstantTime;

use Override;

/**
 *  Copyright (c) 2016 - 2022 Paragon Initiative Enterprises.
 *  Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com)
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in all
 *  copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 *  SOFTWARE.
 */

/**
 * Class Base64DotSlashOrdered
 * ./[0-9][A-Z][a-z]
 *
 * @package ParagonIE\ConstantTime
 */
abstract class Base64DotSlashOrdered extends Base64
{
    /**
     * Uses bitwise operators instead of table-lookups to turn 6-bit integers
     * into 8-bit integers.
     *
     * Base64 character set:
     * [.-9]      [A-Z]      [a-z]
     * 0x2e-0x39, 0x41-0x5a, 0x61-0x7a
     *
     * @param int $src
     * @return int
     */
    #[Override]
    protected static function decode6Bits(int $src): int
    {
        $ret = -1;

        // if ($src > 0x2d && $src < 0x3a) ret += $src - 0x2e + 1; // -45
        $ret += (((0x2d - $src) & ($src - 0x3a)) >> 8) & ($src - 45);

        // if ($src > 0x40 && $src < 0x5b) ret += $src - 0x41 + 12 + 1; // -52
        $ret += (((0x40 - $src) & ($src - 0x5b)) >> 8) & ($src - 52);

        // if ($src > 0x60 && $src < 0x7b) ret += $src - 0x61 + 38 + 1; // -58
        $ret += (((0x60 - $src) & ($src - 0x7b)) >> 8) & ($src - 58);

        return $ret;
    }

    /**
     * Uses bitwise operators instead of table-lookups to turn 8-bit integers
     * into 6-bit integers.
     *
     * @param int $src
     * @return string
     */
    #[Override]
    protected static function encode6Bits(int $src): string
    {
        $src += 0x2e;

        // if ($src > 0x39) $src += 0x41 - 0x3a; // 7
        $src += ((0x39 - $src) >> 8) & 7;

        // if ($src > 0x5a) $src += 0x61 - 0x5b; // 6
        $src += ((0x5a - $src) >> 8) & 6;

        return \pack('C', $src);
    }
}
<?php
declare(strict_types=1);
namespace ParagonIE\ConstantTime;

use InvalidArgumentException;
use Override;
use RangeException;
use SensitiveParameter;
use TypeError;
use function pack;
use function rtrim;
use function strlen;
use function substr;
use function unpack;

/**
 *  Copyright (c) 2016 - 2022 Paragon Initiative Enterprises.
 *  Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com)
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in all
 *  copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 *  SOFTWARE.
 */

/**
 * Class Base32
 * [A-Z][2-7]
 *
 * @package ParagonIE\ConstantTime
 */
abstract class Base32 implements EncoderInterface
{
    /**
     * Decode a Base32-encoded string into raw binary
     *
     * @param string $encodedString
     * @param bool $strictPadding
     * @return string
     */
    #[Override]
    public static function decode(
        #[SensitiveParameter]
        string $encodedString,
        bool $strictPadding = false
    ): string {
        return static::doDecode($encodedString, false, $strictPadding);
    }

    /**
     * Decode an uppercase Base32-encoded string into raw binary
     *
     * @param string $src
     * @param bool $strictPadding
     * @return string
     */
    public static function decodeUpper(
        #[SensitiveParameter]
        string $src,
        bool $strictPadding = false
    ): string {
        return static::doDecode($src, true, $strictPadding);
    }

    /**
     * Encode into Base32 (RFC 4648)
     *
     * @param string $binString
     * @return string
     * @throws TypeError
     */
    #[Override]
    public static function encode(
        #[SensitiveParameter]
        string $binString
    ): string {
        return static::doEncode($binString, false, true);
    }

    /**
     * Encode into Base32 (RFC 4648)
     *
     * @param string $src
     * @return string
     * @throws TypeError
     * @api
     */
    public static function encodeUnpadded(
        #[SensitiveParameter]
        string $src
    ): string {
        return static::doEncode($src, false, false);
    }

    /**
     * Encode into uppercase Base32 (RFC 4648)
     *
     * @param string $src
     * @return string
     * @throws TypeError
     * @api
     */
    public static function encodeUpper(
        #[SensitiveParameter]
        string $src
    ): string {
        return static::doEncode($src, true, true);
    }

    /**
     * Encode into uppercase Base32 (RFC 4648)
     *
     * @param string $src
     * @return string
     * @throws TypeError
     * @api
     */
    public static function encodeUpperUnpadded(
        #[SensitiveParameter]
        string $src
    ): string {
        return static::doEncode($src, true, false);
    }

    /**
     * Uses bitwise operators instead of table-lookups to turn 5-bit integers
     * into 8-bit integers.
     *
     * @param int $src
     * @return int
     * @api
     */
    protected static function decode5Bits(int $src): int
    {
        $ret = -1;

        // if ($src > 96 && $src < 123) $ret += $src - 97 + 1; // -64
        $ret += (((0x60 - $src) & ($src - 0x7b)) >> 8) & ($src - 96);

        // if ($src > 0x31 && $src < 0x38) $ret += $src - 24 + 1; // -23
        $ret += (((0x31 - $src) & ($src - 0x38)) >> 8) & ($src - 23);

        return $ret;
    }

    /**
     * Uses bitwise operators instead of table-lookups to turn 5-bit integers
     * into 8-bit integers.
     *
     * Uppercase variant.
     *
     * @param int $src
     * @return int
     * @api
     */
    protected static function decode5BitsUpper(int $src): int
    {
        $ret = -1;

        // if ($src > 64 && $src < 91) $ret += $src - 65 + 1; // -64
        $ret += (((0x40 - $src) & ($src - 0x5b)) >> 8) & ($src - 64);

        // if ($src > 0x31 && $src < 0x38) $ret += $src - 24 + 1; // -23
        $ret += (((0x31 - $src) & ($src - 0x38)) >> 8) & ($src - 23);

        return $ret;
    }

    /**
     * Uses bitwise operators instead of table-lookups to turn 8-bit integers
     * into 5-bit integers.
     *
     * @param int $src
     * @return string
     * @api
     */
    protected static function encode5Bits(int $src): string
    {
        $diff = 0x61;

        // if ($src > 25) $ret -= 72;
        $diff -= ((25 - $src) >> 8) & 73;

        return pack('C', $src + $diff);
    }

    /**
     * Uses bitwise operators instead of table-lookups to turn 8-bit integers
     * into 5-bit integers.
     *
     * Uppercase variant.
     *
     * @param int $src
     * @return string
     * @api
     */
    protected static function encode5BitsUpper(int $src): string
    {
        $diff = 0x41;

        // if ($src > 25) $ret -= 40;
        $diff -= ((25 - $src) >> 8) & 41;

        return pack('C', $src + $diff);
    }

    /**
     * @param string $encodedString
     * @param bool $upper
     * @return string
     * @api
     */
    public static function decodeNoPadding(
        #[SensitiveParameter]
        string $encodedString,
        bool $upper = false
    ): string {
        $srcLen = strlen($encodedString);
        if ($srcLen === 0) {
            return '';
        }
        if (($srcLen & 7) === 0) {
            for ($j = 0; $j < 7 && $j < $srcLen; ++$j) {
                if ($encodedString[$srcLen - $j - 1] === '=') {
                    throw new InvalidArgumentException(
                        "decodeNoPadding() doesn't tolerate padding"
                    );
                }
            }
        }
        return static::doDecode(
            $encodedString,
            $upper,
            true
        );
    }

    /**
     * Base32 decoding
     *
     * @param string $src
     * @param bool $upper
     * @param bool $strictPadding
     * @return string
     *
     * @throws TypeError
     */
    protected static function doDecode(
        #[SensitiveParameter]
        string $src,
        bool $upper = false,
        bool $strictPadding = false
    ): string {
        // We do this to reduce code duplication:
        $method = $upper
            ? 'decode5BitsUpper'
            : 'decode5Bits';

        // Remove padding
        $srcLen = strlen($src);
        if ($srcLen === 0) {
            return '';
        }
        if ($strictPadding) {
            if (($srcLen & 7) === 0) {
                for ($j = 0; $j < 7; ++$j) {
                    if ($src[$srcLen - 1] === '=') {
                        $srcLen--;
                    } else {
                        break;
                    }
                }
            }
            if (($srcLen & 7) === 1) {
                throw new RangeException(
                    'Incorrect padding'
                );
            }
        } else {
            $src = rtrim($src, '=');
            $srcLen = strlen($src);
        }

        $err = 0;
        $dest = '';
        // Main loop (no padding):
        for ($i = 0; $i + 8 <= $srcLen; $i += 8) {
            /** @var array<int, int> $chunk */
            $chunk = unpack('C*', substr($src, $i, 8));
            /** @var int $c0 */
            $c0 = static::$method($chunk[1]);
            /** @var int $c1 */
            $c1 = static::$method($chunk[2]);
            /** @var int $c2 */
            $c2 = static::$method($chunk[3]);
            /** @var int $c3 */
            $c3 = static::$method($chunk[4]);
            /** @var int $c4 */
            $c4 = static::$method($chunk[5]);
            /** @var int $c5 */
            $c5 = static::$method($chunk[6]);
            /** @var int $c6 */
            $c6 = static::$method($chunk[7]);
            /** @var int $c7 */
            $c7 = static::$method($chunk[8]);

            $dest .= pack(
                'CCCCC',
                (($c0 << 3) | ($c1 >> 2)             ) & 0xff,
                (($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff,
                (($c3 << 4) | ($c4 >> 1)             ) & 0xff,
                (($c4 << 7) | ($c5 << 2) | ($c6 >> 3)) & 0xff,
                (($c6 << 5) | ($c7     )             ) & 0xff
            );
            $err |= ($c0 | $c1 | $c2 | $c3 | $c4 | $c5 | $c6 | $c7) >> 8;
        }
        // The last chunk, which may have padding:
        if ($i < $srcLen) {
            /** @var array<int, int> $chunk */
            $chunk = unpack('C*', substr($src, $i, $srcLen - $i));
            /** @var int $c0 */
            $c0 = static::$method($chunk[1]);

            if ($i + 6 < $srcLen) {
                /** @var int $c1 */
                $c1 = static::$method($chunk[2]);
                /** @var int $c2 */
                $c2 = static::$method($chunk[3]);
                /** @var int $c3 */
                $c3 = static::$method($chunk[4]);
                /** @var int $c4 */
                $c4 = static::$method($chunk[5]);
                /** @var int $c5 */
                $c5 = static::$method($chunk[6]);
                /** @var int $c6 */
                $c6 = static::$method($chunk[7]);

                $dest .= pack(
                    'CCCC',
                    (($c0 << 3) | ($c1 >> 2)             ) & 0xff,
                    (($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff,
                    (($c3 << 4) | ($c4 >> 1)             ) & 0xff,
                    (($c4 << 7) | ($c5 << 2) | ($c6 >> 3)) & 0xff
                );
                $err |= ($c0 | $c1 | $c2 | $c3 | $c4 | $c5 | $c6) >> 8;
                if ($strictPadding) {
                    $err |= ($c6 << 5) & 0xff;
                }
            } elseif ($i + 5 < $srcLen) {
                /** @var int $c1 */
                $c1 = static::$method($chunk[2]);
                /** @var int $c2 */
                $c2 = static::$method($chunk[3]);
                /** @var int $c3 */
                $c3 = static::$method($chunk[4]);
                /** @var int $c4 */
                $c4 = static::$method($chunk[5]);
                /** @var int $c5 */
                $c5 = static::$method($chunk[6]);

                $dest .= pack(
                    'CCCC',
                    (($c0 << 3) | ($c1 >> 2)             ) & 0xff,
                    (($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff,
                    (($c3 << 4) | ($c4 >> 1)             ) & 0xff,
                    (($c4 << 7) | ($c5 << 2)             ) & 0xff
                );
                $err |= ($c0 | $c1 | $c2 | $c3 | $c4 | $c5) >> 8;
            } elseif ($i + 4 < $srcLen) {
                /** @var int $c1 */
                $c1 = static::$method($chunk[2]);
                /** @var int $c2 */
                $c2 = static::$method($chunk[3]);
                /** @var int $c3 */
                $c3 = static::$method($chunk[4]);
                /** @var int $c4 */
                $c4 = static::$method($chunk[5]);

                $dest .= pack(
                    'CCC',
                    (($c0 << 3) | ($c1 >> 2)             ) & 0xff,
                    (($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff,
                    (($c3 << 4) | ($c4 >> 1)             ) & 0xff
                );
                $err |= ($c0 | $c1 | $c2 | $c3 | $c4) >> 8;
                if ($strictPadding) {
                    $err |= ($c4 << 7) & 0xff;
                }
            } elseif ($i + 3 < $srcLen) {
                /** @var int $c1 */
                $c1 = static::$method($chunk[2]);
                /** @var int $c2 */
                $c2 = static::$method($chunk[3]);
                /** @var int $c3 */
                $c3 = static::$method($chunk[4]);

                $dest .= pack(
                    'CC',
                    (($c0 << 3) | ($c1 >> 2)             ) & 0xff,
                    (($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff
                );
                $err |= ($c0 | $c1 | $c2 | $c3) >> 8;
                if ($strictPadding) {
                    $err |= ($c3 << 4) & 0xff;
                }
            } elseif ($i + 2 < $srcLen) {
                /** @var int $c1 */
                $c1 = static::$method($chunk[2]);
                /** @var int $c2 */
                $c2 = static::$method($chunk[3]);

                $dest .= pack(
                    'CC',
                    (($c0 << 3) | ($c1 >> 2)             ) & 0xff,
                    (($c1 << 6) | ($c2 << 1)             ) & 0xff
                );
                $err |= ($c0 | $c1 | $c2) >> 8;
                if ($strictPadding) {
                    $err |= ($c2 << 6) & 0xff;
                }
            } elseif ($i + 1 < $srcLen) {
                /** @var int $c1 */
                $c1 = static::$method($chunk[2]);

                $dest .= pack(
                    'C',
                    (($c0 << 3) | ($c1 >> 2)             ) & 0xff
                );
                $err |= ($c0 | $c1) >> 8;
                if ($strictPadding) {
                    $err |= ($c1 << 6) & 0xff;
                }
            } else {
                $dest .= pack(
                    'C',
                    (($c0 << 3)                          ) & 0xff
                );
                $err |= ($c0) >> 8;
            }
        }
        $check = ($err === 0);
        if (!$check) {
            throw new RangeException(
                'Base32::doDecode() only expects characters in the correct base32 alphabet'
            );
        }
        return $dest;
    }

    /**
     * Base32 Encoding
     *
     * @param string $src
     * @param bool $upper
     * @param bool $pad
     * @return string
     * @throws TypeError
     */
    protected static function doEncode(
        #[SensitiveParameter]
        string $src,
        bool $upper = false,
        bool $pad = true
    ): string {
        // We do this to reduce code duplication:
        $method = $upper
            ? 'encode5BitsUpper'
            : 'encode5Bits';
        
        $dest = '';
        $srcLen = strlen($src);

        // Main loop (no padding):
        for ($i = 0; $i + 5 <= $srcLen; $i += 5) {
            /** @var array<int, int> $chunk */
            $chunk = unpack('C*', substr($src, $i, 5));
            $b0 = $chunk[1];
            $b1 = $chunk[2];
            $b2 = $chunk[3];
            $b3 = $chunk[4];
            $b4 = $chunk[5];
            $dest .=
                static::$method(              ($b0 >> 3)  & 31) .
                static::$method((($b0 << 2) | ($b1 >> 6)) & 31) .
                static::$method((($b1 >> 1)             ) & 31) .
                static::$method((($b1 << 4) | ($b2 >> 4)) & 31) .
                static::$method((($b2 << 1) | ($b3 >> 7)) & 31) .
                static::$method((($b3 >> 2)             ) & 31) .
                static::$method((($b3 << 3) | ($b4 >> 5)) & 31) .
                static::$method(  $b4                     & 31);
        }
        // The last chunk, which may have padding:
        if ($i < $srcLen) {
            /** @var array<int, int> $chunk */
            $chunk = unpack('C*', substr($src, $i, $srcLen - $i));
            $b0 = $chunk[1];
            if ($i + 3 < $srcLen) {
                $b1 = $chunk[2];
                $b2 = $chunk[3];
                $b3 = $chunk[4];
                $dest .=
                    static::$method(              ($b0 >> 3)  & 31) .
                    static::$method((($b0 << 2) | ($b1 >> 6)) & 31) .
                    static::$method((($b1 >> 1)             ) & 31) .
                    static::$method((($b1 << 4) | ($b2 >> 4)) & 31) .
                    static::$method((($b2 << 1) | ($b3 >> 7)) & 31) .
                    static::$method((($b3 >> 2)             ) & 31) .
                    static::$method((($b3 << 3)             ) & 31);
                if ($pad) {
                    $dest .= '=';
                }
            } elseif ($i + 2 < $srcLen) {
                $b1 = $chunk[2];
                $b2 = $chunk[3];
                $dest .=
                    static::$method(              ($b0 >> 3)  & 31) .
                    static::$method((($b0 << 2) | ($b1 >> 6)) & 31) .
                    static::$method((($b1 >> 1)             ) & 31) .
                    static::$method((($b1 << 4) | ($b2 >> 4)) & 31) .
                    static::$method((($b2 << 1)             ) & 31);
                if ($pad) {
                    $dest .= '===';
                }
            } elseif ($i + 1 < $srcLen) {
                $b1 = $chunk[2];
                $dest .=
                    static::$method(              ($b0 >> 3)  & 31) .
                    static::$method((($b0 << 2) | ($b1 >> 6)) & 31) .
                    static::$method((($b1 >> 1)             ) & 31) .
                    static::$method((($b1 << 4)             ) & 31);
                if ($pad) {
                    $dest .= '====';
                }
            } else {
                $dest .=
                    static::$method(              ($b0 >> 3)  & 31) .
                    static::$method( ($b0 << 2)               & 31);
                if ($pad) {
                    $dest .= '======';
                }
            }
        }
        return $dest;
    }
}
<?php
declare(strict_types=1);
namespace ParagonIE\ConstantTime;

use InvalidArgumentException;
use Override;
use RangeException;
use SensitiveParameter;
use SodiumException;
use TypeError;
use function extension_loaded;
use function pack;
use function rtrim;
use function sodium_base642bin;
use function sodium_bin2base64;
use function strlen;
use function substr;
use function unpack;
use const SODIUM_BASE64_VARIANT_ORIGINAL;
use const SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING;
use const SODIUM_BASE64_VARIANT_URLSAFE;
use const SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING;

/**
 *  Copyright (c) 2016 - 2022 Paragon Initiative Enterprises.
 *  Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com)
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in all
 *  copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 *  SOFTWARE.
 */

/**
 * Class Base64
 * [A-Z][a-z][0-9]+/
 *
 * @package ParagonIE\ConstantTime
 */
abstract class Base64 implements EncoderInterface
{
    /**
     * Encode into Base64
     *
     * Base64 character set "[A-Z][a-z][0-9]+/"
     *
     * @param string $binString
     * @return string
     *
     * @throws TypeError
     */
    #[Override]
    public static function encode(
        #[SensitiveParameter]
        string $binString
    ): string {
        if (extension_loaded('sodium')) {
            $variant = match(static::class) {
                Base64::class => SODIUM_BASE64_VARIANT_ORIGINAL,
                Base64UrlSafe::class => SODIUM_BASE64_VARIANT_URLSAFE,
                default => 0,
            };
            if ($variant > 0) {
                try {
                    return sodium_bin2base64($binString, $variant);
                } catch (SodiumException $ex) {
                    throw new RangeException($ex->getMessage(), $ex->getCode(), $ex);
                }
            }
        }
        return static::doEncode($binString, true);
    }

    /**
     * Encode into Base64, no = padding
     *
     * Base64 character set "[A-Z][a-z][0-9]+/"
     *
     * @param string $src
     * @return string
     *
     * @throws TypeError
     * @api
     */
    public static function encodeUnpadded(
        #[SensitiveParameter]
        string $src
    ): string {
        if (extension_loaded('sodium')) {
            $variant = match(static::class) {
                Base64::class => SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING,
                Base64UrlSafe::class => SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING,
                default => 0,
            };
            if ($variant > 0) {
                try {
                    return sodium_bin2base64($src, $variant);
                } catch (SodiumException $ex) {
                    throw new RangeException($ex->getMessage(), $ex->getCode(), $ex);
                }
            }
        }
        return static::doEncode($src, false);
    }

    /**
     * @param string $src
     * @param bool $pad   Include = padding?
     * @return string
     *
     * @throws TypeError
     */
    protected static function doEncode(
        #[SensitiveParameter]
        string $src,
        bool $pad = true
    ): string {
        $dest = '';
        $srcLen = strlen($src);
        // Main loop (no padding):
        for ($i = 0; $i + 3 <= $srcLen; $i += 3) {
            /** @var array<int, int> $chunk */
            $chunk = unpack('C*', substr($src, $i, 3));
            $b0 = $chunk[1];
            $b1 = $chunk[2];
            $b2 = $chunk[3];

            $dest .=
                static::encode6Bits(               $b0 >> 2       ) .
                static::encode6Bits((($b0 << 4) | ($b1 >> 4)) & 63) .
                static::encode6Bits((($b1 << 2) | ($b2 >> 6)) & 63) .
                static::encode6Bits(  $b2                     & 63);
        }
        // The last chunk, which may have padding:
        if ($i < $srcLen) {
            /** @var array<int, int> $chunk */
            $chunk = unpack('C*', substr($src, $i, $srcLen - $i));
            $b0 = $chunk[1];
            if ($i + 1 < $srcLen) {
                $b1 = $chunk[2];
                $dest .=
                    static::encode6Bits($b0 >> 2) .
                    static::encode6Bits((($b0 << 4) | ($b1 >> 4)) & 63) .
                    static::encode6Bits(($b1 << 2) & 63);
                if ($pad) {
                    $dest .= '=';
                }
            } else {
                $dest .=
                    static::encode6Bits( $b0 >> 2) .
                    static::encode6Bits(($b0 << 4) & 63);
                if ($pad) {
                    $dest .= '==';
                }
            }
        }
        return $dest;
    }

    /**
     * decode from base64 into binary
     *
     * Base64 character set "./[A-Z][a-z][0-9]"
     *
     * @param string $encodedString
     * @param bool $strictPadding
     * @return string
     *
     * @throws RangeException
     * @throws TypeError
     */
    #[Override]
    public static function decode(
        #[SensitiveParameter]
        string $encodedString,
        bool $strictPadding = false
    ): string {
        // Remove padding
        $srcLen = strlen($encodedString);
        if ($srcLen === 0) {
            return '';
        }

        if ($strictPadding) {
            if (($srcLen & 3) === 0) {
                if ($encodedString[$srcLen - 1] === '=') {
                    $srcLen--;
                    if ($encodedString[$srcLen - 1] === '=') {
                        $srcLen--;
                    }
                }
            }
            if (($srcLen & 3) === 1) {
                throw new RangeException(
                    'Incorrect padding'
                );
            }
            if ($encodedString[$srcLen - 1] === '=') {
                throw new RangeException(
                    'Incorrect padding'
                );
            }
            if (extension_loaded('sodium')) {
                $variant = match(static::class) {
                    Base64::class => SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING,
                    Base64UrlSafe::class => SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING,
                    default => 0,
                };
                if ($variant > 0) {
                    try {
                        return sodium_base642bin(substr($encodedString, 0, $srcLen), $variant);
                    } catch (SodiumException $ex) {
                        throw new RangeException($ex->getMessage(), $ex->getCode(), $ex);
                    }
                }
            }
        } else {
            // Just remove all padding.
            $encodedString = rtrim($encodedString, '=');
            $srcLen = strlen($encodedString);
        }

        $err = 0;
        $dest = '';
        // Main loop (no padding):
        for ($i = 0; $i + 4 <= $srcLen; $i += 4) {
            /** @var array<int, int> $chunk */
            $chunk = unpack('C*', substr($encodedString, $i, 4));
            $c0 = static::decode6Bits($chunk[1]);
            $c1 = static::decode6Bits($chunk[2]);
            $c2 = static::decode6Bits($chunk[3]);
            $c3 = static::decode6Bits($chunk[4]);

            $dest .= pack(
                'CCC',
                ((($c0 << 2) | ($c1 >> 4)) & 0xff),
                ((($c1 << 4) | ($c2 >> 2)) & 0xff),
                ((($c2 << 6) |  $c3      ) & 0xff)
            );
            $err |= ($c0 | $c1 | $c2 | $c3) >> 8;
        }
        // The last chunk, which may have padding:
        if ($i < $srcLen) {
            /** @var array<int, int> $chunk */
            $chunk = unpack('C*', substr($encodedString, $i, $srcLen - $i));
            $c0 = static::decode6Bits($chunk[1]);

            if ($i + 2 < $srcLen) {
                $c1 = static::decode6Bits($chunk[2]);
                $c2 = static::decode6Bits($chunk[3]);
                $dest .= pack(
                    'CC',
                    ((($c0 << 2) | ($c1 >> 4)) & 0xff),
                    ((($c1 << 4) | ($c2 >> 2)) & 0xff)
                );
                $err |= ($c0 | $c1 | $c2) >> 8;
                if ($strictPadding) {
                    $err |= ($c2 << 6) & 0xff;
                }
            } elseif ($i + 1 < $srcLen) {
                $c1 = static::decode6Bits($chunk[2]);
                $dest .= pack(
                    'C',
                    ((($c0 << 2) | ($c1 >> 4)) & 0xff)
                );
                $err |= ($c0 | $c1) >> 8;
                if ($strictPadding) {
                    $err |= ($c1 << 4) & 0xff;
                }
            } elseif ($strictPadding) {
                $err |= 1;
            }
        }
        $check = ($err === 0);
        if (!$check) {
            throw new RangeException(
                'Base64::decode() only expects characters in the correct base64 alphabet'
            );
        }
        return $dest;
    }

    /**
     * @param string $encodedString
     * @return string
     * @api
     */
    public static function decodeNoPadding(
        #[SensitiveParameter]
        string $encodedString
    ): string {
        $srcLen = strlen($encodedString);
        if ($srcLen === 0) {
            return '';
        }
        if (($srcLen & 3) === 0) {
            // If $strLen is not zero, and it is divisible by 4, then it's at least 4.
            if ($encodedString[$srcLen - 1] === '=' || $encodedString[$srcLen - 2] === '=') {
                throw new InvalidArgumentException(
                    "decodeNoPadding() doesn't tolerate padding"
                );
            }
        }
        return static::decode(
            $encodedString,
            true
        );
    }

    /**
     * Uses bitwise operators instead of table-lookups to turn 6-bit integers
     * into 8-bit integers.
     *
     * Base64 character set:
     * [A-Z]      [a-z]      [0-9]      +     /
     * 0x41-0x5a, 0x61-0x7a, 0x30-0x39, 0x2b, 0x2f
     *
     * @param int $src
     * @return int
     */
    protected static function decode6Bits(int $src): int
    {
        $ret = -1;

        // if ($src > 0x40 && $src < 0x5b) $ret += $src - 0x41 + 1; // -64
        $ret += (((0x40 - $src) & ($src - 0x5b)) >> 8) & ($src - 64);

        // if ($src > 0x60 && $src < 0x7b) $ret += $src - 0x61 + 26 + 1; // -70
        $ret += (((0x60 - $src) & ($src - 0x7b)) >> 8) & ($src - 70);

        // if ($src > 0x2f && $src < 0x3a) $ret += $src - 0x30 + 52 + 1; // 5
        $ret += (((0x2f - $src) & ($src - 0x3a)) >> 8) & ($src + 5);

        // if ($src == 0x2b) $ret += 62 + 1;
        $ret += (((0x2a - $src) & ($src - 0x2c)) >> 8) & 63;

        // if ($src == 0x2f) ret += 63 + 1;
        $ret += (((0x2e - $src) & ($src - 0x30)) >> 8) & 64;

        return $ret;
    }

    /**
     * Uses bitwise operators instead of table-lookups to turn 8-bit integers
     * into 6-bit integers.
     *
     * @param int $src
     * @return string
     */
    protected static function encode6Bits(int $src): string
    {
        $diff = 0x41;

        // if ($src > 25) $diff += 0x61 - 0x41 - 26; // 6
        $diff += ((25 - $src) >> 8) & 6;

        // if ($src > 51) $diff += 0x30 - 0x61 - 26; // -75
        $diff -= ((51 - $src) >> 8) & 75;

        // if ($src > 61) $diff += 0x2b - 0x30 - 10; // -15
        $diff -= ((61 - $src) >> 8) & 15;

        // if ($src > 62) $diff += 0x2f - 0x2b - 1; // 3
        $diff += ((62 - $src) >> 8) & 3;

        return pack('C', $src + $diff);
    }
}
{
  "name": "paragonie/constant_time_encoding",
  "description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)",
  "keywords": [
    "base64",
    "encoding",
    "rfc4648",
    "base32",
    "base16",
    "hex",
    "bin2hex",
    "hex2bin",
    "base64_encode",
    "base64_decode",
    "base32_encode",
    "base32_decode"
  ],
  "license": "MIT",
  "type": "library",
  "authors": [
      {
        "name":     "Paragon Initiative Enterprises",
        "email":    "security@paragonie.com",
        "homepage": "https://paragonie.com",
        "role":     "Maintainer"
      },
      {
        "name":     "Steve 'Sc00bz' Thomas",
        "email":    "steve@tobtu.com",
        "homepage": "https://www.tobtu.com",
        "role":     "Original Developer"
      }
  ],
  "support": {
    "issues":   "https://github.com/paragonie/constant_time_encoding/issues",
    "email":    "info@paragonie.com",
    "source":   "https://github.com/paragonie/constant_time_encoding"
  },
  "require": {
    "php": "^8"
  },
  "require-dev": {
    "infection/infection": "^0",
    "nikic/php-fuzzer": "^0",
    "phpunit/phpunit": "^9|^10|^11",
    "vimeo/psalm": "^4|^5|^6"
  },
  "autoload": {
    "psr-4": {
      "ParagonIE\\ConstantTime\\": "src/"
    }
  },
  "autoload-dev": {
    "psr-4": {
      "ParagonIE\\ConstantTime\\Tests\\": "tests/"
    }
  },
  "scripts": {
    "mutation-test": "infection"
  },
  "config": {
    "process-timeout": 0,
    "allow-plugins": {
      "infection/extension-installer": true
    }
  }
}
<?php
/**
 * Makes a simple A record lookup query and outputs the results
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Examples
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 1.0.0
 */
namespace LibDNS\Examples;

use \LibDNS\Messages\MessageFactory;
use \LibDNS\Messages\MessageTypes;
use \LibDNS\Records\QuestionFactory;
use \LibDNS\Records\ResourceQTypes;
use \LibDNS\Encoder\EncoderFactory;
use \LibDNS\Decoder\DecoderFactory;

// Config
$queryName      = 'faÃŸ.de';
$serverIP       = '8.8.8.8';
$requestTimeout = 3;

require __DIR__ . '/autoload.php';

// Create question record
$question = (new QuestionFactory)->create(ResourceQTypes::A);
$question->setName($queryName);

// Create request message
$request = (new MessageFactory)->create(MessageTypes::QUERY);
$request->getQuestionRecords()->add($question);
$request->isRecursionDesired(true);

// Encode request message
$encoder = (new EncoderFactory)->create();
$requestPacket = $encoder->encode($request);

echo "\n" . $queryName . ":\n";

// Send request
$socket = stream_socket_client("udp://$serverIP:53");
stream_socket_sendto($socket, $requestPacket);
$r = [$socket];
$w = $e = [];
if (!stream_select($r, $w, $e, $requestTimeout)) {
    echo "    Request timeout.\n";
    exit;
}

// Decode response message
$decoder = (new DecoderFactory)->create();
$responsePacket = fread($socket, 512);
$response = $decoder->decode($responsePacket);

// Handle response
if ($response->getResponseCode() !== 0) {
    echo "    Server returned error code " . $response->getResponseCode() . ".\n";
    exit;
}

$answers = $response->getAnswerRecords();
if (count($answers)) {
    foreach ($response->getAnswerRecords() as $record) {
        /** @var \LibDNS\Records\Resource $record */
        echo "    " . $record->getData() . "\n";
    }
} else {
    echo "    Not found.\n";
}
<?php
/**
 * This file was automatically generated by autoload_generator.php
 *
 * Do not edit this file directly
 */

spl_autoload_register(function($className) {
    static $classMap;
    if (!isset($classMap)) {
        $classMap = [
            'libdns\decoder\decoder'                                      => __DIR__ . '/../src/Decoder/Decoder.php',
            'libdns\decoder\decoderfactory'                               => __DIR__ . '/../src/Decoder/DecoderFactory.php',
            'libdns\decoder\decodingcontext'                              => __DIR__ . '/../src/Decoder/DecodingContext.php',
            'libdns\decoder\decodingcontextfactory'                       => __DIR__ . '/../src/Decoder/DecodingContextFactory.php',
            'libdns\encoder\encoder'                                      => __DIR__ . '/../src/Encoder/Encoder.php',
            'libdns\encoder\encoderfactory'                               => __DIR__ . '/../src/Encoder/EncoderFactory.php',
            'libdns\encoder\encodingcontext'                              => __DIR__ . '/../src/Encoder/EncodingContext.php',
            'libdns\encoder\encodingcontextfactory'                       => __DIR__ . '/../src/Encoder/EncodingContextFactory.php',
            'libdns\enumeration'                                          => __DIR__ . '/../src/Enumeration.php',
            'libdns\messages\message'                                     => __DIR__ . '/../src/Messages/Message.php',
            'libdns\messages\messagefactory'                              => __DIR__ . '/../src/Messages/MessageFactory.php',
            'libdns\messages\messageopcodes'                              => __DIR__ . '/../src/Messages/MessageOpCodes.php',
            'libdns\messages\messageresponsecodes'                        => __DIR__ . '/../src/Messages/MessageResponseCodes.php',
            'libdns\messages\messagetypes'                                => __DIR__ . '/../src/Messages/MessageTypes.php',
            'libdns\packets\labelregistry'                                => __DIR__ . '/../src/Packets/LabelRegistry.php',
            'libdns\packets\packet'                                       => __DIR__ . '/../src/Packets/Packet.php',
            'libdns\packets\packetfactory'                                => __DIR__ . '/../src/Packets/PacketFactory.php',
            'libdns\records\question'                                     => __DIR__ . '/../src/Records/Question.php',
            'libdns\records\questionfactory'                              => __DIR__ . '/../src/Records/QuestionFactory.php',
            'libdns\records\rdata'                                        => __DIR__ . '/../src/Records/RData.php',
            'libdns\records\rdatabuilder'                                 => __DIR__ . '/../src/Records/RDataBuilder.php',
            'libdns\records\rdatafactory'                                 => __DIR__ . '/../src/Records/RDataFactory.php',
            'libdns\records\record'                                       => __DIR__ . '/../src/Records/Record.php',
            'libdns\records\recordcollection'                             => __DIR__ . '/../src/Records/RecordCollection.php',
            'libdns\records\recordcollectionfactory'                      => __DIR__ . '/../src/Records/RecordCollectionFactory.php',
            'libdns\records\recordtypes'                                  => __DIR__ . '/../src/Records/RecordTypes.php',
            'libdns\records\resource'                                     => __DIR__ . '/../src/Records/Resource.php',
            'libdns\records\resourcebuilder'                              => __DIR__ . '/../src/Records/ResourceBuilder.php',
            'libdns\records\resourcebuilderfactory'                       => __DIR__ . '/../src/Records/ResourceBuilderFactory.php',
            'libdns\records\resourceclasses'                              => __DIR__ . '/../src/Records/ResourceClasses.php',
            'libdns\records\resourcefactory'                              => __DIR__ . '/../src/Records/ResourceFactory.php',
            'libdns\records\resourceqclasses'                             => __DIR__ . '/../src/Records/ResourceQClasses.php',
            'libdns\records\resourceqtypes'                               => __DIR__ . '/../src/Records/ResourceQTypes.php',
            'libdns\records\resourcetypes'                                => __DIR__ . '/../src/Records/ResourceTypes.php',
            'libdns\records\typedefinitions\fielddefinition'              => __DIR__ . '/../src/Records/TypeDefinitions/FieldDefinition.php',
            'libdns\records\typedefinitions\fielddefinitionfactory'       => __DIR__ . '/../src/Records/TypeDefinitions/FieldDefinitionFactory.php',
            'libdns\records\typedefinitions\typedefinition'               => __DIR__ . '/../src/Records/TypeDefinitions/TypeDefinition.php',
            'libdns\records\typedefinitions\typedefinitionfactory'        => __DIR__ . '/../src/Records/TypeDefinitions/TypeDefinitionFactory.php',
            'libdns\records\typedefinitions\typedefinitionmanager'        => __DIR__ . '/../src/Records/TypeDefinitions/TypeDefinitionManager.php',
            'libdns\records\typedefinitions\typedefinitionmanagerfactory' => __DIR__ . '/../src/Records/TypeDefinitions/TypeDefinitionManagerFactory.php',
            'libdns\records\types\anything'                               => __DIR__ . '/../src/Records/Types/Anything.php',
            'libdns\records\types\bitmap'                                 => __DIR__ . '/../src/Records/Types/BitMap.php',
            'libdns\records\types\char'                                   => __DIR__ . '/../src/Records/Types/Char.php',
            'libdns\records\types\characterstring'                        => __DIR__ . '/../src/Records/Types/CharacterString.php',
            'libdns\records\types\domainname'                             => __DIR__ . '/../src/Records/Types/DomainName.php',
            'libdns\records\types\ipv4address'                            => __DIR__ . '/../src/Records/Types/IPv4Address.php',
            'libdns\records\types\ipv6address'                            => __DIR__ . '/../src/Records/Types/IPv6Address.php',
            'libdns\records\types\long'                                   => __DIR__ . '/../src/Records/Types/Long.php',
            'libdns\records\types\short'                                  => __DIR__ . '/../src/Records/Types/Short.php',
            'libdns\records\types\type'                                   => __DIR__ . '/../src/Records/Types/Type.php',
            'libdns\records\types\typebuilder'                            => __DIR__ . '/../src/Records/Types/TypeBuilder.php',
            'libdns\records\types\typefactory'                            => __DIR__ . '/../src/Records/Types/TypeFactory.php',
            'libdns\records\types\types'                                  => __DIR__ . '/../src/Records/Types/Types.php',
        ];
    }

    $className = strtolower($className);
    if (isset($classMap[$className])) {
        /** @noinspection PhpIncludeInspection */
        require $classMap[$className];
    }
});
<?php
/**
 * Makes a simple A record lookup query and outputs the results
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Examples
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 1.0.0
 */
namespace LibDNS\Examples;

use \LibDNS\Messages\MessageFactory;
use \LibDNS\Messages\MessageTypes;
use \LibDNS\Records\QuestionFactory;
use \LibDNS\Records\ResourceTypes;
use \LibDNS\Records\ResourceQTypes;
use \LibDNS\Encoder\EncoderFactory;
use \LibDNS\Decoder\DecoderFactory;
use \LibDNS\Records\TypeDefinitions\TypeDefinitionManagerFactory;

// Config
$queryName      = 'google.com';
$serverIP       = '8.8.8.8';
$requestTimeout = 3;

require __DIR__ . '/autoload.php';

// Create question record
$question = (new QuestionFactory)->create(ResourceQTypes::SOA);
$question->setName($queryName);

// Create request message
$request = (new MessageFactory)->create(MessageTypes::QUERY);
$request->getQuestionRecords()->add($question);
$request->isRecursionDesired(true);

// Encode request message
$encoder = (new EncoderFactory)->create();
$requestPacket = $encoder->encode($request);

echo "\n" . $queryName . ":\n";

// Send request
$socket = stream_socket_client("udp://$serverIP:53");
stream_socket_sendto($socket, $requestPacket);
$r = [$socket];
$w = $e = [];
if (!stream_select($r, $w, $e, $requestTimeout)) {
    echo "    Request timeout.\n";
    exit;
}

// Create type definition manager for custom manipulation
$typeDefs = (new TypeDefinitionManagerFactory)->create();
$typeDefs->getTypeDefinition(ResourceTypes::SOA)->setToStringFunction(function($mname, $rname, $serial, $refresh, $retry, $expire, $minimum) {
    return <<<DATA
{
    Primary Name Server : $mname
    Responsible Mail    : $rname
    Serial              : $serial
    Refresh             : $refresh
    Retry               : $retry
    Expire              : $expire
    Default TTL         : $minimum
}
DATA;
});

// Decode response message
$decoder = (new DecoderFactory)->create($typeDefs);
$responsePacket = fread($socket, 512);
$response = $decoder->decode($responsePacket);

// Handle response
if ($response->getResponseCode() !== 0) {
    echo "    Server returned error code " . $response->getResponseCode() . ".\n";
    exit;
}

$answers = $response->getAnswerRecords();
if (count($answers)) {
    foreach ($response->getAnswerRecords() as $record) {
        /** @var \LibDNS\Records\Resource $record */
        echo "    " . $record->getData() . "\n";
    }
} else {
    echo "    Not found.\n";
}
<?php
/**
 * Generates a class-map based autoloader file for the examples directory
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Tools
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 1.0.0
 */
namespace LibDNS\Tools;

use \RecursiveIteratorIterator;
use \RecursiveDirectoryIterator;
use \FilesystemIterator;

error_reporting(0);
ini_set('display_errors', 0);

if (!isset($argv[1])) {
    $srcDir = getcwd();
} else if (in_array(strtolower($argv[1]), ['--help', '?', '/?'])) {
    exit("Syntax: " . __FILE__ . " [source directory]\n");
} else if (!is_dir($srcDir = $argv[1])) {
    exit("Invalid source directory\n\nSyntax: " . __FILE__ . " [source directory]\n");
}
$srcDir = str_replace('\\', '/', $srcDir);

$iterator = new RecursiveIteratorIterator(
    new RecursiveDirectoryIterator(
        $srcDir,
        FilesystemIterator::KEY_AS_PATHNAME | FilesystemIterator::CURRENT_AS_FILEINFO | FilesystemIterator::SKIP_DOTS
    )
);

$items = [];
$stripLength = strlen($srcDir) + 1;
$maxLength = 0;
foreach ($iterator as $item) {
    if ($item->isFile() && $item->getFilename() !== 'autoload.php' && strtolower($item->getExtension()) === 'php') {
        $classPath = substr($item->getPath() . '\\' . $item->getBasename('.' . $item->getExtension()), $stripLength);
        $lookupName = strtolower(str_replace('/', '\\', $classPath));
        $loadPath = "__DIR__ . '/$srcDir/" . str_replace('\\', '/', $classPath) . ".php'";

        $length = strlen($classPath);
        if ($length > $maxLength) {
            $maxLength = $length;
        }

        $items[$lookupName] = $loadPath;
    }
}
unset($iterator);

$output = <<<'PHP'
<?php
/**
 * This file was automatically generated by autoload_generator.php
 *
 * Do not edit this file directly
 */

spl_autoload_register(function($className) {
    static $classMap;
    if (!isset($classMap)) {
        $classMap = [
PHP;

$maxLength += 2;
foreach ($items as $lookupName => $loadPath) {
    $output .= "\n            " . str_pad("'" . $lookupName . "'", $maxLength, ' ', STR_PAD_RIGHT) . " => $loadPath,";
}

$output .= <<<'PHP'

        ];
    }

    $className = strtolower($className);
    if (isset($classMap[$className])) {
        /** @noinspection PhpIncludeInspection */
        require $classMap[$className];
    }
});

PHP;

file_put_contents(getcwd() . '/autoload.php', $output);
<?php declare(strict_types=1);
/**
 * Collection of Record objects
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Records
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Records;

/**
 * Collection of Record objects
 *
 * @category LibDNS
 * @package Records
 * @author Chris Wright <https://github.com/DaveRandom>
 */
class RecordCollection implements \IteratorAggregate, \Countable
{
    /**
     * @var \LibDNS\Records\Record[] List of records held in the collection
     */
    private $records = [];

    /**
     * @var \LibDNS\Records\Record[][] Map of Records in the collection grouped by record name
     */
    private $nameMap = [];

    /**
     * @var int Number of Records in the collection
     */
    private $length = 0;

    /**
     * @var int Whether the collection holds question or resource records
     */
    private $type;

    /**
     * Constructor
     *
     * @param int $type Can be indicated using the RecordTypes enum
     * @throws \InvalidArgumentException When the specified record type is invalid
     */
    public function __construct($type)
    {
        if ($type !== RecordTypes::QUESTION && $type !== RecordTypes::RESOURCE) {
            throw new \InvalidArgumentException('Record type must be QUESTION or RESOURCE');
        }

        $this->type = $type;
    }

    /**
     * Add a record to the correct bucket in the name map
     *
     * @param \LibDNS\Records\Record $record The record to add
     */
    private function addToNameMap(Record $record)
    {
        if (!isset($this->nameMap[$name = (string) $record->getName()])) {
            $this->nameMap[$name] = [];
        }

        $this->nameMap[$name][] = $record;
    }

    /**
     * Remove a record from the name map
     *
     * @param \LibDNS\Records\Record $record The record to remove
     */
    private function removeFromNameMap(Record $record)
    {
        if (!empty($this->nameMap[$name = (string) $record->getName()])) {
            foreach ($this->nameMap[$name] as $key => $item) {
                if ($item === $record) {
                    \array_splice($this->nameMap[$name], $key, 1);
                    break;
                }
            }
        }

        if (empty($this->nameMap[$name])) {
            unset($this->nameMap[$name]);
        }
    }

    /**
     * Add a record to the collection
     *
     * @param \LibDNS\Records\Record $record The record to add
     * @throws \InvalidArgumentException When the wrong record type is supplied
     */
    public function add(Record $record)
    {
        if (($this->type === RecordTypes::QUESTION && !($record instanceof Question))
          || ($this->type === RecordTypes::RESOURCE && !($record instanceof Resource))) {
            throw new \InvalidArgumentException('Incorrect record type for this collection');
        }

        $this->records[] = $record;
        $this->addToNameMap($record);
        $this->length++;
    }

    /**
     * Remove a record from the collection
     *
     * @param \LibDNS\Records\Record $record The record to remove
     */
    public function remove(Record $record)
    {
        foreach ($this->records as $key => $item) {
            if ($item === $record) {
                array_splice($this->records, $key, 1);
                $this->removeFromNameMap($record);
                $this->length--;
                return;
            }
        }

        throw new \InvalidArgumentException('The supplied record is not a member of this collection');
    }

    /**
     * Test whether the collection contains a specific record
     *
     * @param \LibDNS\Records\Record $record       The record to search for
     * @param bool $sameInstance Whether to perform strict comparisons in search
     * @return bool
     */
    public function contains(Record $record, bool $sameInstance = false): bool
    {
        return \in_array($record, $this->records, $sameInstance);
    }

    /**
     * Get all records in the collection that refer to the specified name
     *
     * @param string $name The name to match records against
     * @return \LibDNS\Records\Record[]
     */
    public function getRecordsByName(string $name): array
    {
        return $this->nameMap[\strtolower($name)] ?? [];
    }

    /**
     * Get a record from the collection by index
     *
     * @param int $index Record index
     * @return \LibDNS\Records\Record
     * @throws \OutOfBoundsException When the supplied index does not refer to a valid record
     */
    public function getRecordByIndex(int $index): Record
    {
        if (isset($this->records[$index])) {
            return $this->records[$index];
        }

        throw new \OutOfBoundsException('The specified index ' . $index . ' does not exist in the collection');
    }

    /**
     * Remove all records in the collection that refer to the specified name
     *
     * @param string $name The name to match records against
     * @return int The number of records removed
     */
    public function clearRecordsByName(string $name): int
    {
        $count = 0;

        if (isset($this->nameMap[$name = \strtolower($name)])) {
            unset($this->nameMap[$name]);

            foreach ($this->records as $index => $record) {
                if ($record->getName() === $name) {
                    unset($this->records[$index]);
                    $count++;
                }
            }

            $this->records = \array_values($this->records);
        }

        return $count;
    }

    /**
     * Remove all records from the collection
     */
    public function clear()
    {
        $this->records = $this->nameMap = [];
        $this->length = 0;
    }

    /**
     * Get a list of all names referenced by records in the collection
     *
     * @return string[]
     */
    public function getNames(): array
    {
        return \array_keys($this->nameMap);
    }

    /**
     * Get whether the collection holds question or resource records
     *
     * @return int
     */
    public function getType(): int
    {
        return $this->type;
    }

    /**
     * Retrieve an iterator (IteratorAggregate interface)
     *
     * @return \Iterator
     */
    public function getIterator(): \Iterator
    {
        return new \ArrayIterator($this->records);
    }

    /**
     * Get the number of records in the collection (Countable interface)
     *
     * @return int
     */
    public function count(): int
    {
        return $this->length;
    }
}
<?php declare(strict_types=1);
/**
 * Represents the RDATA section of a resource record
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Records
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Records;

use \LibDNS\Records\Types\Type;
use \LibDNS\Records\TypeDefinitions\TypeDefinition;

/**
 * Represents a data type comprising multiple simple types
 *
 * @category LibDNS
 * @package Records
 * @author Chris Wright <https://github.com/DaveRandom>
 */
class RData implements \IteratorAggregate, \Countable
{
    /**
     * @var \LibDNS\Records\Types\Type[] The items that make up the complex type
     */
    private $fields = [];

    /**
     * @var \LibDNS\Records\TypeDefinitions\TypeDefinition Structural definition of the fields
     */
    private $typeDef;

    /**
     * Constructor
     *
     * @param \LibDNS\Records\TypeDefinitions\TypeDefinition $typeDef
     */
    public function __construct(TypeDefinition $typeDef)
    {
        $this->typeDef = $typeDef;
    }

    /**
     * Magic method for type coersion to string
     *
     * @return string
     */
    public function __toString()
    {
        if ($handler = $this->typeDef->getToStringFunction()) {
            $result = \call_user_func_array($handler, $this->fields);
        } else {
            $result = \implode(',', $this->fields);
        }

        return $result;
    }

    /**
     * Get the field indicated by the supplied index
     *
     * @param int $index
     * @return \LibDNS\Records\Types\Type
     * @throws \OutOfBoundsException When the supplied index does not refer to a valid field
     */
    public function getField(int $index)
    {
        if (!isset($this->fields[$index])) {
            throw new \OutOfBoundsException('Index ' . $index . ' does not refer to a valid field');
        }

        return $this->fields[$index];
    }

    /**
     * Set the field indicated by the supplied index
     *
     * @param int $index
     * @param \LibDNS\Records\Types\Type $value
     * @throws \InvalidArgumentException When the supplied index/value pair does not match the type definition
     */
    public function setField(int $index, Type $value)
    {
        if (!$this->typeDef->getFieldDefinition($index)->assertDataValid($value)) {
            throw new \InvalidArgumentException('The supplied value is not valid for the specified index');
        }

        $this->fields[$index] = $value;
    }

    /**
     * Get the field indicated by the supplied name
     *
     * @param string $name
     * @return \LibDNS\Records\Types\Type
     * @throws \OutOfBoundsException When the supplied name does not refer to a valid field
     */
    public function getFieldByName(string $name): Type
    {
        return $this->getField($this->typeDef->getFieldIndexByName($name));
    }

    /**
     * Set the field indicated by the supplied name
     *
     * @param string $name
     * @param \LibDNS\Records\Types\Type $value
     * @throws \OutOfBoundsException When the supplied name does not refer to a valid field
     * @throws \InvalidArgumentException When the supplied value does not match the type definition
     */
    public function setFieldByName(string $name, Type $value)
    {
        $this->setField($this->typeDef->getFieldIndexByName($name), $value);
    }

    /**
     * Get the structural definition of the fields
     *
     * @return \LibDNS\Records\TypeDefinitions\TypeDefinition
     */
    public function getTypeDefinition(): TypeDefinition
    {
        return $this->typeDef;
    }

    /**
     * Retrieve an iterator (IteratorAggregate interface)
     *
     * @return \Iterator
     */
    public function getIterator(): \Iterator
    {
        return new \ArrayIterator($this->fields);
    }

    /**
     * Get the number of fields (Countable interface)
     *
     * @return int
     */
    public function count(): int
    {
        return \count($this->fields);
    }
}
<?php declare(strict_types=1);
/**
 * Represents a DNS resource record
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Records
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Records;

use \LibDNS\Records\Types\TypeFactory;

/**
 * Represents a DNS resource record
 *
 * @category LibDNS
 * @package Records
 * @author Chris Wright <https://github.com/DaveRandom>
 */
class Resource extends Record
{
    /**
     * @var int Value of the resource's time-to-live property
     */
    private $ttl;

    /**
     * @var \LibDNS\Records\RData
     */
    private $data;

    /**
     * Constructor
     *
     * @param \LibDNS\Records\Types\TypeFactory $typeFactory
     * @param int $type Can be indicated using the ResourceTypes enum
     * @param \LibDNS\Records\RData $data
     */
    public function __construct(TypeFactory $typeFactory, int $type, RData $data)
    {
        $this->typeFactory = $typeFactory;
        $this->type = $type;
        $this->data = $data;
    }

    /**
     * Get the value of the record TTL field
     *
     * @return int
     */
    public function getTTL(): int
    {
        return $this->ttl;
    }

    /**
     * Set the value of the record TTL field
     *
     * @param int $ttl The new value
     * @throws \RangeException When the supplied value is outside the valid range 0 - 4294967296
     */
    public function setTTL(int $ttl)
    {
        if ($ttl < 0 || $ttl > 4294967296) {
            throw new \RangeException('Record class must be in the range 0 - 4294967296');
        }

        $this->ttl = $ttl;
    }

    /**
     * Get the value of the resource data field
     *
     * @return \LibDNS\Records\RData
     */
    public function getData(): RData
    {
        return $this->data;
    }
}
<?php declare(strict_types=1);
/**
 * Enumeration of possible resource QCLASS values
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Records
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Records;

/**
 * Enumeration of possible resource QCLASS values
 *
 * @category LibDNS
 * @package Records
 * @author Chris Wright <https://github.com/DaveRandom>
 */
final class ResourceQClasses extends ResourceClasses
{
    const ANY = 255;
}
<?php declare(strict_types=1);
/**
 * Enumeration of possible resource QTYPE values
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Records
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Records;

/**
 * Enumeration of possible resource QTYPE values
 *
 * @category LibDNS
 * @package Records
 * @author Chris Wright <https://github.com/DaveRandom>
 */
final class ResourceQTypes extends ResourceTypes
{
    const AXFR = 252;
    const MAILB = 253;
    const MAILA = 254;
    const ALL = 255;
}
<?php declare(strict_types=1);
/**
 * Creates Question objects
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Records
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Records;

use \LibDNS\Records\Types\TypeFactory;

/**
 * Creates Question objects
 *
 * @category LibDNS
 * @package Records
 * @author Chris Wright <https://github.com/DaveRandom>
 */
class QuestionFactory
{
    /**
     * Create a new Question object
     *
     * @param int $type The resource type
     * @return \LibDNS\Records\Question
     */
    public function create(int $type): Question
    {
        return new Question(new TypeFactory, $type);
    }
}
<?php declare(strict_types=1);
/**
 * Creates RecordCollection objects
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Records
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Records;

/**
 * Creates RecordCollection objects
 *
 * @category LibDNS
 * @package Records
 * @author Chris Wright <https://github.com/DaveRandom>
 */
class RecordCollectionFactory
{
    /**
     * Create a new RecordCollection object
     *
     * @param int $type Can be indicated using the RecordTypes enum
     * @return \LibDNS\Records\RecordCollection
     * @throws \InvalidArgumentException When the specified record type is invalid
     */
    public function create(int $type): RecordCollection
    {
        return new RecordCollection($type);
    }
}
<?php declare(strict_types=1);
/**
 * Creates ResourceBuilder objects
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Records
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Records;

use \LibDNS\Records\Types\TypeBuilder;
use \LibDNS\Records\Types\TypeFactory;
use \LibDNS\Records\TypeDefinitions\TypeDefinitionManager;
use \LibDNS\Records\TypeDefinitions\TypeDefinitionFactory;
use \LibDNS\Records\TypeDefinitions\FieldDefinitionFactory;

/**
 * Creates ResourceBuilder objects
 *
 * @category LibDNS
 * @package Records
 * @author Chris Wright <https://github.com/DaveRandom>
 */
class ResourceBuilderFactory
{
    /**
     * Create a new ResourceBuilder object
     *
     * @param \LibDNS\Records\TypeDefinitions\TypeDefinitionManager $typeDefinitionManager
     * @return \LibDNS\Records\ResourceBuilder
     */
    public function create(?TypeDefinitionManager $typeDefinitionManager = null): ResourceBuilder
    {
        return new ResourceBuilder(
            new ResourceFactory,
            new RDataBuilder(
                new RDataFactory,
                new TypeBuilder(new TypeFactory)
            ),
            $typeDefinitionManager ?: new TypeDefinitionManager(
                new TypeDefinitionFactory,
                new FieldDefinitionFactory
            )
        );
    }
}
<?php declare(strict_types=1);
/**
 * Represents a DNS question record
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Records
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Records;

use \LibDNS\Records\Types\TypeFactory;

/**
 * Represents a DNS question record
 *
 * @category LibDNS
 * @package Records
 * @author Chris Wright <https://github.com/DaveRandom>
 */
class Question extends Record
{
    /**
     * Constructor
     *
     * @param \LibDNS\Records\Types\TypeFactory $typeFactory
     * @param int $type Resource type being requested, can be indicated using the ResourceQTypes enum
     */
    public function __construct(TypeFactory $typeFactory, int $type)
    {
        $this->typeFactory = $typeFactory;
        $this->type = $type;
    }
}
<?php declare(strict_types=1);
/**
 * Creates RData objects
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Records
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Records;

use \LibDNS\Records\TypeDefinitions\TypeDefinition;

/**
 * Creates RData objects
 *
 * @category LibDNS
 * @package Records
 * @author Chris Wright <https://github.com/DaveRandom>
 */
class RDataFactory
{
    /**
     * Create a new RData object
     *
     * @param \LibDNS\Records\TypeDefinitions\TypeDefinition $typeDefinition
     * @return \LibDNS\Records\RData
     */
    public function create(TypeDefinition $typeDefinition): RData
    {
        return new RData($typeDefinition);
    }
}
<?php declare(strict_types=1);
/**
 * Creates Resource objects
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Records
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Records;

use \LibDNS\Records\Types\TypeFactory;

/**
 * Creates Resource objects
 *
 * @category LibDNS
 * @package Records
 * @author Chris Wright <https://github.com/DaveRandom>
 */
class ResourceFactory
{
    /**
     * Create a new Resource object
     *
     * @param int $type Can be indicated using the ResourceTypes enum
     * @param \LibDNS\Records\RData $data
     * @return \LibDNS\Records\Resource
     */
    public function create(int $type, RData $data): Resource
    {
        return new Resource(new TypeFactory, $type, $data);
    }
}
<?php declare(strict_types=1);
/**
 * Represents an IPv6 address
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Types
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Records\Types;

/**
 * Represents an IPv6 address
 *
 * @category LibDNS
 * @package Types
 * @author Chris Wright <https://github.com/DaveRandom>
 */
class IPv6Address extends Type
{
    /**
     * @var string
     */
    protected $value = '::';

    /**
     * @var int[] The shorts of the address
     */
    private $shorts = [0, 0, 0, 0, 0, 0, 0, 0];

    /**
     * Create a compressed string representation of an IPv6 address
     *
     * @param int[] $shorts Address shorts
     * @return string
     */
    private function createCompressedString($shorts)
    {
        $compressLen = $compressPos = $currentLen = $currentPos = 0;
        $inBlock = false;

        for ($i = 0; $i < 8; $i++) {
            if ($shorts[$i] === 0) {
                if (!$inBlock) {
                    $inBlock = true;
                    $currentPos = $i;
                }

                $currentLen++;
            } else if ($inBlock) {
                if ($currentLen > $compressLen) {
                    $compressLen = $currentLen;
                    $compressPos = $currentPos;
                }

                $inBlock = false;
                $currentPos = $currentLen = 0;
            }

            $shorts[$i] = \dechex($shorts[$i]);
        }
        if ($inBlock) {
            $compressLen = $currentLen;
            $compressPos = $currentPos;
        }

        if ($compressLen > 1) {
            if ($compressLen === 8) {
                $replace = ['', '', ''];
            } else if ($compressPos === 0 || $compressPos + $compressLen === 8) {
                $replace = ['', ''];
            } else {
                $replace = [''];
            }

            \array_splice($shorts, $compressPos, $compressLen, $replace);
        }

        return \implode(':', $shorts);
    }

    /**
     * Constructor
     *
     * @param string|int[] $value String representation or shorts list
     * @throws \UnexpectedValueException When the supplied value is not a valid IPv6 address
     */
    public function __construct($value = null)
    {
        if (\is_array($value)) {
            $this->setShorts($value);
        } else {
            parent::__construct($value);
        }
    }

    /**
     * Set the internal value
     *
     * @param string $value The new value
     * @throws \UnexpectedValueException When the supplied value is outside the valid length range 0 - 65535
     */
    public function setValue($value)
    {
        $shorts = \explode(':', (string)$value);

        $count = \count($shorts);
        if ($count < 3 || $count > 8) {
            throw new \UnexpectedValueException('Value is not a valid IPv6 address: invalid short count');
        } else if ($shorts[0] === '' && $shorts[1] === '') {
            $shorts = \array_pad($shorts, -8, '0');
        } else if ($shorts[$count - 2] === '' && $shorts[$count - 1] === '') {
            $shorts = \array_pad($shorts, 8, '0');
        } else if (false !== $pos = \array_search('', $shorts, true)) {
            \array_splice($shorts, $pos, 1, \array_fill(0, 8 - ($count - 1), '0'));
        }

        $this->setShorts(\array_map('hexdec', $shorts));
    }

    /**
     * Get the address shorts
     *
     * @return int[]
     */
    public function getShorts(): array
    {
        return $this->shorts;
    }

    /**
     * Set the address shorts
     *
     * @param int[] $shorts The new address shorts
     * @throws \UnexpectedValueException When the supplied short list is not a valid IPv6 address
     */
    public function setShorts(array $shorts)
    {
        if (\count($shorts) !== 8) {
            throw new \UnexpectedValueException('Short list is not a valid IPv6 address: invalid short count');
        }

        foreach ($shorts as &$short) {
            if ((!\is_int($short) && !\ctype_digit((string)$short)) || $short < 0x0000 || $short > 0xffff) {
                throw new \UnexpectedValueException('Short list is not a valid IPv6 address: invalid short value ' . $short);
            }

            $short = (int) $short;
        }

        $this->shorts = \array_values($shorts);
        $this->value = $this->createCompressedString($this->shorts);
    }
}
<?php declare(strict_types=1);
/**
 * Represents a bit map
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Types
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Records\Types;

/**
 * Represents a bit map
 *
 * @category LibDNS
 * @package Types
 * @author Chris Wright <https://github.com/DaveRandom>
 */
class BitMap extends Type
{
    /**
     * @var string
     */
    protected $value = '';

    /**
     * Set the internal value
     *
     * @param string $value The new value
     */
    public function setValue($value)
    {
        $this->value = (string)$value;
    }

    /**
     * Inspect the value of the bit at the specific index and optionally set a new value
     *
     * @param int $index
     * @param bool $newValue The new value
     * @return bool The old value
     */
    public function isBitSet(int $index, ?bool $newValue = null): bool
    {
        $charIndex = (int)($index / 8);
        $bitMask = 0b10000000 >> ($index % 8);

        $result = false;
        if (isset($this->value[$charIndex])) {
            $result = (bool) (\ord($this->value[$charIndex]) & $bitMask);
        }

        if (isset($newValue) && $newValue != $result) {
            if (!isset($this->value[$charIndex])) {
                $this->value = \str_pad($this->value, $charIndex + 1, "\x00", STR_PAD_RIGHT);
            }

            $this->value[$charIndex] = \chr((\ord($this->value[$charIndex]) & ~$bitMask) | ($newValue ? $bitMask : 0));
        }

        return $result;
    }
}
<?php declare(strict_types=1);
/**
 * Represents an 8-bit unsigned integer
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Types
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Records\Types;

/**
 * Represents an 8-bit unsigned integer
 *
 * @category LibDNS
 * @package Types
 * @author Chris Wright <https://github.com/DaveRandom>
 */
class Char extends Type
{
    /**
     * @var int
     */
    protected $value = 0;

    /**
     * Set the internal value
     *
     * @param string $value The new value
     * @throws \UnderflowException When the supplied value is less than 0
     * @throws \OverflowException When the supplied value is greater than 255
     */
    public function setValue($value)
    {
        $value = (int) $value;

        if ($value < 0) {
            throw new \UnderflowException('Char value must be in the range 0 - 255');
        } else if ($value > 255) {
            throw new \OverflowException('Char value must be in the range 0 - 255');
        }

        $this->value = $value;
    }
}
<?php declare(strict_types=1);
/**
 * Represents a 32-bit unsigned integer
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Types
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Records\Types;

/**
 * Represents a 32-bit unsigned integer
 *
 * @category LibDNS
 * @package Types
 * @author Chris Wright <https://github.com/DaveRandom>
 */
class Long extends Type
{
    /**
     * @var int
     */
    protected $value = 0;

    /**
     * Set the internal value
     *
     * @param string $value The new value
     * @throws \UnderflowException When the supplied value is less than 0
     * @throws \OverflowException When the supplied value is greater than 4294967296
     */
    public function setValue($value)
    {
        $value = (int)$value;

        if (\PHP_INT_SIZE > 4) {
            if ($value < 0) {
                throw new \UnderflowException('Long integer value must be in the range 0 - 4294967296');
            } else if ($value > 0xffffffff) {
                throw new \OverflowException('Long integer value must be in the range 0 - 4294967296');
            }
        }

        $this->value = $value;
    }
}
<?php declare(strict_types=1);
/**
 * Represents an IPv4 address
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Types
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Records\Types;

/**
 * Represents an IPv4 address
 *
 * @category LibDNS
 * @package Types
 * @author Chris Wright <https://github.com/DaveRandom>
 */
class IPv4Address extends Type
{
    /**
     * @var string
     */
    protected $value = '0.0.0.0';

    /**
     * @var int[] The octets of the address
     */
    private $octets = [0, 0, 0, 0];

    /**
     * Constructor
     *
     * @param string|int[] $value String representation or octet list
     * @throws \UnexpectedValueException When the supplied value is not a valid IPv4 address
     */
    public function __construct($value = null)
    {
        if (\is_array($value)) {
            $this->setOctets($value);
        } else {
            parent::__construct($value);
        }
    }

    /**
     * Set the internal value
     *
     * @param string $value The new value
     * @throws \UnexpectedValueException When the supplied value is outside the valid length range 0 - 65535
     */
    public function setValue($value)
    {
        $this->setOctets(\explode('.', (string)$value));
    }

    /**
     * Get the address octets
     *
     * @return int[]
     */
    public function getOctets(): array
    {
        return $this->octets;
    }

    /**
     * Set the address octets
     *
     * @param int[] $octets The new address octets
     * @throws \UnexpectedValueException When the supplied octet list is not a valid IPv4 address
     */
    public function setOctets(array $octets)
    {
        if (\count($octets) !== 4) {
            throw new \UnexpectedValueException('Octet list is not a valid IPv4 address: invalid octet count');
        }

        foreach ($octets as &$octet) {
            if ((!\is_int($octet) && !\ctype_digit((string)$octet)) || $octet < 0x00 || $octet > 0xff) {
                throw new \UnexpectedValueException('Octet list is not a valid IPv4 address: invalid octet value ' . $octet);
            }

            $octet = (int) $octet;
        }

        $this->octets = \array_values($octets);
        $this->value = \implode('.', $this->octets);
    }
}
<?php declare(strict_types=1);
/**
 * Represents a binary character string
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Types
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Records\Types;

/**
 * Represents a binary character string
 *
 * @category LibDNS
 * @package Types
 * @author Chris Wright <https://github.com/DaveRandom>
 */
class CharacterString extends Type
{
    /**
     * @var string
     */
    protected $value = '';

    /**
     * Set the internal value
     *
     * @param string $value The new value
     * @throws \UnexpectedValueException When the supplied value is outside the valid length range 0 - 255
     */
    public function setValue($value)
    {
        $value = (string)$value;

        if (\strlen($value) > 255) {
            throw new \UnexpectedValueException('Character string length must be in the range 0 - 255');
        }

        $this->value = $value;
    }
}
<?php declare(strict_types=1);
/**
 * Base class for simple data types
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Types
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Records\Types;

/**
 * Base class for simple data types
 *
 * @category LibDNS
 * @package Types
 * @author Chris Wright <https://github.com/DaveRandom>
 */
abstract class Type
{
    /**
     * @var mixed The internal value
     */
    protected $value;

    /**
     * Constructor
     *
     * @param string $value Internal value
     * @throws \RuntimeException When the supplied value is invalid
     */
    public function __construct(?string $value = null)
    {
        if (isset($value)) {
            $this->setValue($value);
        }
    }

    /**
     * Magic method for type coercion to string
     *
     * @return string
     */
    public function __toString(): string
    {
        return (string) $this->value;
    }

    /**
     * Get the internal value
     *
     * @return mixed
     */
    public function getValue()
    {
        return $this->value;
    }

    /**
     * Set the internal value
     *
     * @param string $value The new value
     * @throws \RuntimeException When the supplied value is invalid
     */
    abstract public function setValue($value);
}
<?php declare(strict_types=1);
/**
 * Represents a fully qualified domain name
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Types
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Records\Types;

/**
 * Represents a fully qualified domain name
 *
 * @category LibDNS
 * @package Types
 * @author Chris Wright <https://github.com/DaveRandom>
 */
class DomainName extends Type
{
    const FLAG_NO_COMPRESSION = \PHP_INT_SIZE === 4 ? -2147483648 : 0x80000000;

    /**
     * @var string
     */
    protected $value = '';

    /**
     * @var string[] The value as a list of labels
     */
    private $labels = [];

    /**
     * Constructor
     *
     * @param string|string[] $value
     * @throws \UnexpectedValueException When the supplied value is not a valid domain name
     */
    public function __construct($value = null)
    {
        if (\is_array($value)) {
            $this->setLabels($value);
        } else {
            parent::__construct($value);
        }
    }

    /**
     * Set the internal value
     *
     * @param string $value The new value
     * @throws \UnexpectedValueException When the supplied value is not a valid domain name
     */
    public function setValue($value)
    {
        $this->setLabels(\explode('.', (string)$value));
    }

    /**
     * Get the domain name labels
     *
     * @param bool $tldFirst Whether to return the label list ordered with the TLD label first
     * @return string[]
     */
    public function getLabels($tldFirst = false): array
    {
        return $tldFirst ? \array_reverse($this->labels) : $this->labels;
    }

    /**
     * Set the domain name labels
     *
     * @param string[] $labels   The new label list
     * @param bool $tldFirst Whether the supplied label list is ordered with the TLD label first
     * @throws \UnexpectedValueException When the supplied label list is not a valid domain name
     */
    public function setLabels(array $labels, $tldFirst = false)
    {
        if (!$labels) {
            $this->labels = [];
            $this->value = '';
            return;
        }

        $length = $count = 0;

        foreach ($labels as &$label) {
            $label = \LibDNS\normalize_name($label);
            $labelLength = \strlen($label);
            if ($labelLength > 63) {
                throw new \InvalidArgumentException('Label list is not a valid domain name: Label ' . $label . ' length exceeds 63 byte limit');
            }
            $length += $labelLength + 1;
            $count++;
        }

        $tld = $tldFirst ? $labels[0] : $labels[$count - 1];
        if ($tld === '') {
            $length--;
        }

        if ($length + 1 > 255) {
            throw new \InvalidArgumentException('Label list is not a valid domain name: Total length exceeds 255 byte limit');
        }

        $this->labels = $tldFirst ? \array_reverse($labels) : $labels;
        $this->value = \implode('.', $this->labels);
    }
}
<?php declare(strict_types=1);
/**
 * Builds Types from type definitions
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Types
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Records\Types;

/**
 * Builds Types from type definitions
 *
 * @category LibDNS
 * @package Types
 * @author Chris Wright <https://github.com/DaveRandom>
 */
class TypeBuilder
{
    /**
     * @var \LibDNS\Records\Types\TypeFactory
     */
    private $typeFactory;

    /**
     * Constructor
     *
     * @param \LibDNS\Records\Types\TypeFactory $typeFactory
     */
    public function __construct(TypeFactory $typeFactory)
    {
        $this->typeFactory = $typeFactory;
    }

    /**
     * Build a new Type object corresponding to a resource record type
     *
     * @param int $type Data type, can be indicated using the Types enum
     * @return \LibDNS\Records\Types\Type
     */
    public function build(int $type): Type
    {
        static $typeMap = [
            Types::ANYTHING         => 'createAnything',
            Types::BITMAP           => 'createBitMap',
            Types::CHAR             => 'createChar',
            Types::CHARACTER_STRING => 'createCharacterString',
            Types::DOMAIN_NAME      => 'createDomainName',
            Types::IPV4_ADDRESS     => 'createIPv4Address',
            Types::IPV6_ADDRESS     => 'createIPv6Address',
            Types::LONG             => 'createLong',
            Types::SHORT            => 'createShort',
        ];

        if (!isset($typeMap[$type])) {
            throw new \InvalidArgumentException('Invalid Type identifier ' . $type);
        }

        return $this->typeFactory->{$typeMap[$type]}();
    }
}
<?php declare(strict_types=1);
/**
 * Creates Type objects
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Types
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Records\Types;

/**
 * Creates Type objects
 *
 * @category LibDNS
 * @package Types
 * @author Chris Wright <https://github.com/DaveRandom>
 */
class TypeFactory
{
    /**
     * Create a new Anything object
     *
     * @param string $value
     * @return \LibDNS\Records\Types\Anything
     */
    public function createAnything(?string $value = null)
    {
        return new Anything($value);
    }

    /**
     * Create a new BitMap object
     *
     * @param string $value
     * @return \LibDNS\Records\Types\BitMap
     */
    public function createBitMap(?string $value = null)
    {
        return new BitMap($value);
    }

    /**
     * Create a new Char object
     *
     * @param int $value
     * @return \LibDNS\Records\Types\Char
     */
    public function createChar(?int $value = null)
    {
        return new Char((string)$value);
    }

    /**
     * Create a new CharacterString object
     *
     * @param string $value
     * @return \LibDNS\Records\Types\CharacterString
     */
    public function createCharacterString(?string $value = null)
    {
        return new CharacterString($value);
    }

    /**
     * Create a new DomainName object
     *
     * @param string|string[] $value
     * @return \LibDNS\Records\Types\DomainName
     */
    public function createDomainName($value = null)
    {
        return new DomainName($value);
    }

    /**
     * Create a new IPv4Address object
     *
     * @param string|int[] $value
     * @return \LibDNS\Records\Types\IPv4Address
     */
    public function createIPv4Address($value = null)
    {
        return new IPv4Address($value);
    }

    /**
     * Create a new IPv6Address object
     *
     * @param string|int[] $value
     * @return \LibDNS\Records\Types\IPv6Address
     */
    public function createIPv6Address($value = null)
    {
        return new IPv6Address($value);
    }

    /**
     * Create a new Long object
     *
     * @param int $value
     * @return \LibDNS\Records\Types\Long
     */
    public function createLong(?int $value = null)
    {
        return new Long((string)$value);
    }

    /**
     * Create a new Short object
     *
     * @param int $value
     * @return \LibDNS\Records\Types\Short
     */
    public function createShort(?int $value = null)
    {
        return new Short((string)$value);
    }
}
<?php declare(strict_types=1);
/**
 * Represents a 16-bit unsigned integer
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Types
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Records\Types;

/**
 * Represents a 16-bit unsigned integer
 *
 * @category LibDNS
 * @package Types
 * @author Chris Wright <https://github.com/DaveRandom>
 */
class Short extends Type
{
    /**
     * @var int
     */
    protected $value = 0;

    /**
     * Set the internal value
     *
     * @param string $value The new value
     * @throws \UnderflowException When the supplied value is less than 0
     * @throws \OverflowException When the supplied value is greater than 65535
     */
    public function setValue($value)
    {
        $value = (int)$value;

        if ($value < 0) {
            throw new \UnderflowException('Short integer value must be in the range 0 - 65535');
        } else if ($value > 0xffff) {
            throw new \OverflowException('Short integer value must be in the range 0 - 65535');
        }

        $this->value = $value;
    }
}
<?php declare(strict_types=1);
/**
 * Enumeration of simple data types
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Types
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Records\Types;

use \LibDNS\Enumeration;

/**
 * Enumeration of simple data types
 *
 * @category LibDNS
 * @package Types
 * @author Chris Wright <https://github.com/DaveRandom>
 */
final class Types extends Enumeration
{
    const ANYTHING         = 0b000000001;
    const BITMAP           = 0b000000010;
    const CHAR             = 0b000000100;
    const CHARACTER_STRING = 0b000001000;
    const DOMAIN_NAME      = 0b000010000;
    const IPV4_ADDRESS     = 0b000100000;
    const IPV6_ADDRESS     = 0b001000000;
    const LONG             = 0b010000000;
    const SHORT            = 0b100000000;
}
<?php declare(strict_types=1);
/**
 * Represents a generic binary data string
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Types
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Records\Types;

/**
 * Represents a generic binary data string
 *
 * @category LibDNS
 * @package Types
 * @author Chris Wright <https://github.com/DaveRandom>
 */
class Anything extends Type
{
    /**
     * @var string
     */
    protected $value = '';

    /**
     * Set the internal value
     *
     * @param string $value The new value
     * @throws \UnexpectedValueException When the supplied value is outside the valid length range 0 - 65535
     */
    public function setValue($value)
    {
        $value = (string)$value;

        if (\strlen($value) > 65535) {
            throw new \UnexpectedValueException('Untyped string length must be in the range 0 - 65535');
        }

        $this->value = $value;
    }
}
<?php declare(strict_types=1);
/**
 * Enumeration of possible record types
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Records
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Records;

use \LibDNS\Enumeration;

/**
 * Enumeration of possible record types
 *
 * @category LibDNS
 * @package Records
 * @author Chris Wright <https://github.com/DaveRandom>
 */
final class RecordTypes extends Enumeration
{
    const QUESTION = 0;
    const RESOURCE = 1;
}
<?php declare(strict_types=1);
/**
 * Builds RData objects from a type definition
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Records
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Records;

use \LibDNS\Records\TypeDefinitions\TypeDefinition;
use \LibDNS\Records\Types\TypeBuilder;

/**
 * Creates RData objects
 *
 * @category LibDNS
 * @package Records
 * @author Chris Wright <https://github.com/DaveRandom>
 */
class RDataBuilder
{
    /**
     * @var \LibDNS\Records\RDataFactory
     */
    private $rDataFactory;

    /**
     * @var \LibDNS\Records\Types\TypeBuilder
     */
    private $typeBuilder;

    /**
     * Constructor
     *
     * @param \LibDNS\Records\RDataFactory $rDataFactory
     * @param \LibDNS\Records\Types\TypeBuilder $typeBuilder
     */
    public function __construct(RDataFactory $rDataFactory, TypeBuilder $typeBuilder)
    {
        $this->rDataFactory = $rDataFactory;
        $this->typeBuilder = $typeBuilder;
    }

    /**
     * Create a new RData object
     *
     * @param \LibDNS\Records\TypeDefinitions\TypeDefinition $typeDefinition
     * @return \LibDNS\Records\RData
     */
    public function build(TypeDefinition $typeDefinition): RData
    {
        $rData = $this->rDataFactory->create($typeDefinition);

        foreach ($typeDefinition as $index => $type) {
            $rData->setField($index, $this->typeBuilder->build($type->getType()));
        }

        return $rData;
    }
}
<?php declare(strict_types=1);
/**
 * Enumeration of possible resource TYPE values
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Records
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Records;

use \LibDNS\Enumeration;

/**
 * Enumeration of possible resource TYPE values
 *
 * @category LibDNS
 * @package Records
 * @author Chris Wright <https://github.com/DaveRandom>
 */
abstract class ResourceTypes extends Enumeration
{
    const A          = 1;
    const AAAA       = 28;
    const AFSDB      = 18;
//    const APL        = 42;
    const CAA        = 257;
    const CERT       = 37;
    const CNAME      = 5;
    const DHCID      = 49;
    const DLV        = 32769;
    const DNAME      = 39;
    const DNSKEY     = 48;
    const DS         = 43;
    const HINFO      = 13;
//    const HIP        = 55;
//    const IPSECKEY   = 45;
    const KEY        = 25;
    const KX         = 36;
    const ISDN       = 20;
    const LOC        = 29;
    const MB         = 7;
    const MD         = 3;
    const MF         = 4;
    const MG         = 8;
    const MINFO      = 14;
    const MR         = 9;
    const MX         = 15;
    const NAPTR      = 35;
    const NS         = 2;
//    const NSEC       = 47;
//    const NSEC3      = 50;
//    const NSEC3PARAM = 50;
    const NULL       = 10;
    const PTR        = 12;
    const RP         = 17;
//    const RRSIG      = 46;
    const RT         = 21;
    const SIG        = 24;
    const SOA        = 6;
    const SPF        = 99;
    const SRV        = 33;
    const TXT        = 16;
    const WKS        = 11;
    const X25        = 19;
}
<?php declare(strict_types=1);
/**
 * Creates FieldDefinition objects
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package TypeDefinitions
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Records\TypeDefinitions;

/**
 * Creates FieldDefinition objects
 *
 * @category LibDNS
 * @package TypeDefinitions
 * @author Chris Wright <https://github.com/DaveRandom>
 */
class FieldDefinitionFactory
{
    /**
     * Create a new FieldDefinition object
     *
     * @param int $index
     * @param string $name
     * @param int $type
     * @param bool $allowsMultiple
     * @param int $minimumValues
     * @return \LibDNS\Records\TypeDefinitions\FieldDefinition
     */
    public function create(int $index, string $name, int $type, bool $allowsMultiple, int $minimumValues): FieldDefinition
    {
        return new FieldDefinition($index, $name, $type, $allowsMultiple, $minimumValues);
    }
}
<?php declare(strict_types=1);
/**
 * Creates TypeDefinition objects
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package TypeDefinitions
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Records\TypeDefinitions;

/**
 * Creates TypeDefinition objects
 *
 * @category LibDNS
 * @package TypeDefinitions
 * @author Chris Wright <https://github.com/DaveRandom>
 */
class TypeDefinitionFactory
{
    /**
     * Create a new TypeDefinition object
     *
     * @param FieldDefinitionFactory $fieldDefinitionFactory
     * @param int[] $definition Structural definition of the fields
     * @return \LibDNS\Records\TypeDefinitions\TypeDefinition
     * @throws \InvalidArgumentException When the type definition is invalid
     */
    public function create(FieldDefinitionFactory $fieldDefinitionFactory, array $definition): TypeDefinition
    {
        return new TypeDefinition($fieldDefinitionFactory, $definition);
    }
}
<?php declare(strict_types=1);
/**
 * Holds data about how the RDATA sections of known resource record types are structured
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package TypeDefinitions
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Records\TypeDefinitions;

use \LibDNS\Records\ResourceTypes;
use \LibDNS\Records\Types\Types;
use \LibDNS\Records\Types\DomainName;

/**
 * Holds data about how the RDATA sections of known resource record types are structured
 *
 * @category LibDNS
 * @package TypeDefinitions
 * @author Chris Wright <https://github.com/DaveRandom>
 */
class TypeDefinitionManager
{
    /**
     * @var array[] How the RDATA sections of known resource record types are structured
     */
    private $definitions = [];

    /**
     * @var array Cache of created definitions
     */
    private $typeDefs = [];

    /**
     * @var \LibDNS\Records\TypeDefinitions\TypeDefinitionFactory
     */
    private $typeDefFactory;

    /**
     * @var \LibDNS\Records\TypeDefinitions\FieldDefinitionFactory
     */
    private $fieldDefFactory;

    /**
     * Constructor
     *
     * @param \LibDNS\Records\TypeDefinitions\TypeDefinitionFactory $typeDefFactory
     * @param \LibDNS\Records\TypeDefinitions\FieldDefinitionFactory $fieldDefFactory
     */
    public function __construct(TypeDefinitionFactory $typeDefFactory, FieldDefinitionFactory $fieldDefFactory)
    {
        $this->typeDefFactory = $typeDefFactory;
        $this->fieldDefFactory = $fieldDefFactory;

        $this->setDefinitions();
    }

    /**
     * Set the internal definitions structure
     */
    private function setDefinitions()
    {
        // This is defined in a method because PHP doesn't let you define properties with
        // expressions at the class level. If anyone has a better way to do this I am open
        // to any and all suggestions.

        $this->definitions = [
            ResourceTypes::A => [ // RFC 1035
                'address' => Types::IPV4_ADDRESS,
            ],
            ResourceTypes::AAAA  => [ // RFC 3596
                'address' => Types::IPV6_ADDRESS,
            ],
            ResourceTypes::AFSDB => [ // RFC 1183
                'subtype'  => Types::SHORT,
                'hostname' => Types::DOMAIN_NAME,
            ],
            ResourceTypes::CAA => [ // RFC 6844
                'flags' => Types::DOMAIN_NAME,
                'tag'   => Types::CHARACTER_STRING,
                'value' => Types::ANYTHING,
            ],
            ResourceTypes::CERT => [ // RFC 4398
                'type'        => Types::SHORT,
                'key-tag'     => Types::SHORT,
                'algorithm'   => Types::CHAR,
                'certificate' => Types::ANYTHING,
            ],
            ResourceTypes::CNAME => [ // RFC 1035
                'cname' => Types::DOMAIN_NAME,
            ],
            ResourceTypes::DHCID => [ // RFC 4701
                'identifier-type' => Types::SHORT,
                'digest-type'     => Types::CHAR,
                'digest'          => Types::ANYTHING,
            ],
            ResourceTypes::DLV => [ // RFC 4034
                'key-tag'     => Types::SHORT,
                'algorithm'   => Types::CHAR,
                'digest-type' => Types::CHAR,
                'digest'      => Types::ANYTHING,
            ],
            ResourceTypes::DNAME => [ // RFC 4034
                'target' => Types::DOMAIN_NAME,
            ],
            ResourceTypes::DNSKEY => [ // RFC 6672
                'flags'      => Types::SHORT,
                'protocol'   => Types::CHAR,
                'algorithm'  => Types::CHAR,
                'public-key' => Types::ANYTHING,
            ],
            ResourceTypes::DS => [ // RFC 4034
                'key-tag'     => Types::SHORT,
                'algorithm'   => Types::CHAR,
                'digest-type' => Types::CHAR,
                'digest'      => Types::ANYTHING,
            ],
            ResourceTypes::HINFO => [ // RFC 1035
                'cpu' => Types::CHARACTER_STRING,
                'os'  => Types::CHARACTER_STRING,
            ],
            ResourceTypes::ISDN => [ // RFC 1183
                'isdn-address' => Types::CHARACTER_STRING,
                'sa'           => Types::CHARACTER_STRING,
            ],
            ResourceTypes::KEY => [ // RFC 2535
                'flags'      => Types::SHORT,
                'protocol'   => Types::CHAR,
                'algorithm'  => Types::CHAR,
                'public-key' => Types::ANYTHING,
            ],
            ResourceTypes::KX => [ // RFC 2230
                'preference' => Types::SHORT,
                'exchange'   => Types::DOMAIN_NAME,
            ],
            ResourceTypes::LOC => [ // RFC 1876
                'version'              => Types::CHAR,
                'size'                 => Types::CHAR,
                'horizontal-precision' => Types::CHAR,
                'vertical-precision'   => Types::CHAR,
                'latitude'             => Types::LONG,
                'longitude'            => Types::LONG,
                'altitude'             => Types::LONG,
            ],
            ResourceTypes::MB => [ // RFC 1035
                'madname' => Types::DOMAIN_NAME,
            ],
            ResourceTypes::MD => [ // RFC 1035
                'madname' => Types::DOMAIN_NAME,
            ],
            ResourceTypes::MF => [ // RFC 1035
                'madname' => Types::DOMAIN_NAME,
            ],
            ResourceTypes::MG => [ // RFC 1035
                'mgmname' => Types::DOMAIN_NAME,
            ],
            ResourceTypes::MINFO => [ // RFC 1035
                'rmailbx' => Types::DOMAIN_NAME,
                'emailbx' => Types::DOMAIN_NAME,
            ],
            ResourceTypes::MR => [ // RFC 1035
                'newname' => Types::DOMAIN_NAME,
            ],
            ResourceTypes::MX => [ // RFC 1035
                'preference' => Types::SHORT,
                'exchange'   => Types::DOMAIN_NAME,
            ],
            ResourceTypes::NAPTR => [ // RFC 3403
                'order'       => Types::SHORT,
                'preference'  => Types::SHORT,
                'flags'       => Types::CHARACTER_STRING,
                'services'    => Types::CHARACTER_STRING,
                'regexp'      => Types::CHARACTER_STRING,
                'replacement' => Types::DOMAIN_NAME,
            ],
            ResourceTypes::NS => [ // RFC 1035
                'nsdname' => Types::DOMAIN_NAME,
            ],
            ResourceTypes::NULL => [ // RFC 1035
                'data' => Types::ANYTHING,
            ],
            ResourceTypes::PTR => [ // RFC 1035
                'ptrdname' => Types::DOMAIN_NAME,
            ],
            ResourceTypes::RP => [ // RFC 1183
                'mbox-dname' => Types::DOMAIN_NAME,
                'txt-dname'  => Types::DOMAIN_NAME,
            ],
            ResourceTypes::RT => [ // RFC 1183
                'preference'        => Types::SHORT,
                'intermediate-host' => Types::DOMAIN_NAME,
            ],
            ResourceTypes::SIG => [ // RFC 4034
                'type-covered'         => Types::SHORT,
                'algorithm'            => Types::CHAR,
                'labels'               => Types::CHAR,
                'original-ttl'         => Types::LONG,
                'signature-expiration' => Types::LONG,
                'signature-inception'  => Types::LONG,
                'key-tag'              => Types::SHORT,
                'signers-name'         => Types::DOMAIN_NAME,
                'signature'            => Types::ANYTHING,
            ],
            ResourceTypes::SOA => [ // RFC 1035
                'mname'      => Types::DOMAIN_NAME,
                'rname'      => Types::DOMAIN_NAME,
                'serial'     => Types::LONG,
                'refresh'    => Types::LONG,
                'retry'      => Types::LONG,
                'expire'     => Types::LONG,
                'minimum'    => Types::LONG,
            ],
            ResourceTypes::SPF => [ // RFC 4408
                'data+' => Types::CHARACTER_STRING,
            ],
            ResourceTypes::SRV => [ // RFC 2782
                'priority' => Types::SHORT,
                'weight'   => Types::SHORT,
                'port'     => Types::SHORT,
                'name'     => Types::DOMAIN_NAME | DomainName::FLAG_NO_COMPRESSION,
            ],
            ResourceTypes::TXT => [ // RFC 1035
                'txtdata+' => Types::CHARACTER_STRING,
            ],
            ResourceTypes::WKS => [ // RFC 1035
                'address'  => Types::IPV4_ADDRESS,
                'protocol' => Types::SHORT,
                'bit-map'  => Types::BITMAP,
            ],
            ResourceTypes::X25 => [ // RFC 1183
                'psdn-address' => Types::CHARACTER_STRING,
            ],
        ];
    }

    /**
     * Get a type definition for a record type if it is known
     *
     * @param int $recordType Resource type, can be indicated using the ResourceTypes enum
     * @return \LibDNS\Records\TypeDefinitions\TypeDefinition
     */
    public function getTypeDefinition(int $recordType)
    {
        if (!isset($this->typeDefs[$recordType])) {
            $definition = isset($this->definitions[$recordType]) ? $this->definitions[$recordType] : ['data' => Types::ANYTHING];
            $this->typeDefs[$recordType] = $this->typeDefFactory->create($this->fieldDefFactory, $definition);
        }

        return $this->typeDefs[$recordType];
    }

    /**
     * Register a custom type definition
     *
     * @param int $recordType Resource type, can be indicated using the ResourceTypes enum
     * @param int[]|\LibDNS\Records\TypeDefinitions\TypeDefinition $definition
     * @throws \InvalidArgumentException When the type definition is invalid
     */
    public function registerTypeDefinition(int $recordType, $definition)
    {
        if (!($definition instanceof TypeDefinition)) {
            if (!\is_array($definition)) {
                throw new \InvalidArgumentException('Definition must be an array or an instance of ' . __NAMESPACE__ . '\TypeDefinition');
            }

            $definition = $this->typeDefFactory->create($this->fieldDefFactory, $definition);
        }

        $this->typeDefs[$recordType] = $definition;
    }
}
<?php declare(strict_types=1);
/**
 * Defines a field in a type
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package TypeDefinitions
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Records\TypeDefinitions;

use \LibDNS\Records\Types\Type;
use \LibDNS\Records\Types\Anything;
use \LibDNS\Records\Types\BitMap;
use \LibDNS\Records\Types\Char;
use \LibDNS\Records\Types\CharacterString;
use \LibDNS\Records\Types\DomainName;
use \LibDNS\Records\Types\IPv4Address;
use \LibDNS\Records\Types\IPv6Address;
use \LibDNS\Records\Types\Long;
use \LibDNS\Records\Types\Short;
use \LibDNS\Records\Types\Types;

/**
 * Defines a field in a type
 *
 * @category LibDNS
 * @package TypeDefinitions
 * @author Chris Wright <https://github.com/DaveRandom>
 */
class FieldDefinition
{
    /**
     * @var int
     */
    private $index;

    /**
     * @var string
     */
    private $name;

    /**
     * @var int
     */
    private $type;

    /**
     * @var bool
     */
    private $allowsMultiple;

    /**
     * @var int
     */
    private $minimumValues;

    /**
     * Constructor
     *
     * @param int $index
     * @param string $name
     * @param int $type
     * @param bool $allowsMultiple
     * @param int $minimumValues
     */
    public function __construct(int $index, string $name, int $type, bool $allowsMultiple, int $minimumValues)
    {
        $this->index = $index;
        $this->name = $name;
        $this->type = $type;
        $this->allowsMultiple = $allowsMultiple;
        $this->minimumValues = $minimumValues;
    }

    /**
     * Get the index of the field in the containing type
     *
     * @return int
     */
    public function getIndex(): int
    {
        return $this->index;
    }

    /**
     * Get the name of the field
     *
     * @return string
     */
    public function getName(): string
    {
        return $this->name;
    }

    /**
     * Get the type of the field
     *
     * @return int
     */
    public function getType(): int
    {
        return $this->type;
    }

    /**
     * Determine whether the field allows multiple values
     *
     * @return bool
     */
    public function allowsMultiple(): bool
    {
        return $this->allowsMultiple;
    }

    /**
     * Get the minimum number of values for the field
     *
     * @return int
     */
    public function getMinimumValues(): int
    {
        return $this->minimumValues;
    }

    /**
     * Assert that a Type object is valid for this field
     *
     * @param \LibDNS\Records\Types\Type
     * @return bool
     */
    public function assertDataValid(Type $value): bool
    {
        return (($this->type & Types::ANYTHING)         && $value instanceof Anything)
            || (($this->type & Types::BITMAP)           && $value instanceof BitMap)
            || (($this->type & Types::CHAR)             && $value instanceof Char)
            || (($this->type & Types::CHARACTER_STRING) && $value instanceof CharacterString)
            || (($this->type & Types::DOMAIN_NAME)      && $value instanceof DomainName)
            || (($this->type & Types::IPV4_ADDRESS)     && $value instanceof IPv4Address)
            || (($this->type & Types::IPV6_ADDRESS)     && $value instanceof IPv6Address)
            || (($this->type & Types::LONG)             && $value instanceof Long)
            || (($this->type & Types::SHORT)            && $value instanceof Short);
    }
}
<?php declare(strict_types=1);
/**
 * Defines a data type comprising multiple fields
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package TypeDefinitions
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Records\TypeDefinitions;

/**
 * Defines a data type comprising multiple fields
 *
 * @category LibDNS
 * @package TypeDefinitions
 * @author Chris Wright <https://github.com/DaveRandom>
 */
class TypeDefinition implements \IteratorAggregate, \Countable
{
    /**
     * @var FieldDefinitionFactory Creates FieldDefinition objects
     */
    private $fieldDefFactory;

    /**
     * @var int Number of fields in the type
     */
    private $fieldCount;

    /**
     * @var \LibDNS\Records\TypeDefinitions\FieldDefinition The last field defined by the type
     */
    private $lastField;

    /**
     * @var int[] Map of field indexes to type identifiers
     */
    private $fieldDefs = [];

    /**
     * @var int[] Map of field names to indexes
     */
    private $fieldNameMap = [];

    /**
     * @var callable Custom implementation for __toString() handling
     */
    private $toStringFunction;

    /**
     * Constructor
     *
     * @param FieldDefinitionFactory $fieldDefFactory
     * @param array $definition Structural definition of the fields
     * @throws \InvalidArgumentException When the type definition is invalid
     */
    public function __construct(FieldDefinitionFactory $fieldDefFactory, array $definition)
    {
        $this->fieldDefFactory = $fieldDefFactory;

        if (isset($definition['__toString'])) {
            if (!\is_callable($definition['__toString'])) {
                throw new \InvalidArgumentException('Invalid type definition: __toString() implementation is not callable');
            }

            $this->toStringFunction = $definition['__toString'];
            unset($definition['__toString']);
        }

        $this->fieldCount = \count($definition);
        $index = 0;
        foreach ($definition as $name => $type) {
            $this->registerField($index++, $name, $type);
        }
    }

    /**
     * Register a field from the type definition
     *
     * @param int $index
     * @param string $name
     * @param int $type
     * @throws \InvalidArgumentException When the field definition is invalid
     */
    private function registerField(int $index, string $name, int $type)
    {
        if (!\preg_match('/^(?P<name>[\w\-]+)(?P<quantifier>\+|\*)?(?P<minimum>(?<=\+)\d+)?$/', \strtolower($name), $matches)) {
            throw new \InvalidArgumentException('Invalid field definition ' . $name . ': Syntax error');
        }

        if (isset($matches['quantifier'])) {
            if ($index !== $this->fieldCount - 1) {
                throw new \InvalidArgumentException('Invalid field definition ' . $name . ': Quantifiers only allowed in last field');
            }

            if (!isset($matches['minimum'])) {
                $matches['minimum'] = $matches['quantifier'] === '+' ? 1 : 0;
            }

            $allowsMultiple = true;
            $minimumValues = (int) $matches['minimum'];
        } else {
            $allowsMultiple = false;
            $minimumValues = 0;
        }

        $this->fieldDefs[$index] = $this->fieldDefFactory->create($index, $matches['name'], $type, $allowsMultiple, $minimumValues);
        if ($index === $this->fieldCount - 1) {
            $this->lastField = $this->fieldDefs[$index];
        }

        $this->fieldNameMap[$matches['name']] = $index;
    }

    /**
     * Get the field definition indicated by the supplied index
     *
     * @param int $index
     * @return \LibDNS\Records\TypeDefinitions\FieldDefinition
     * @throws \OutOfBoundsException When the supplied index does not refer to a valid field
     */
    public function getFieldDefinition(int $index): FieldDefinition
    {
        if (isset($this->fieldDefs[$index])) {
            $fieldDef = $this->fieldDefs[$index];
        } else if ($index >= 0 && $this->lastField->allowsMultiple()) {
            $fieldDef = $this->lastField;
        } else {
            throw new \OutOfBoundsException('Index ' . $index . ' does not refer to a valid field');
        }

        return $fieldDef;
    }

    /**
     * Get the field index indicated by the supplied name
     *
     * @param string $name
     * @return int
     * @throws \OutOfBoundsException When the supplied name does not refer to a valid field
     */
    public function getFieldIndexByName($name): int
    {
        $fieldName = \strtolower($name);
        if (!isset($this->fieldNameMap[$fieldName])) {
            throw new \OutOfBoundsException('Name ' . $name . ' does not refer to a valid field');
        }

        return $this->fieldNameMap[$fieldName];
    }

    /**
     * Get the __toString() implementation
     *
     * @return callable|null
     */
    public function getToStringFunction()
    {
        return $this->toStringFunction;
    }

    /**
     * Set the __toString() implementation
     *
     * @param callable $function
     */
    public function setToStringFunction(callable $function)
    {
        $this->toStringFunction = $function;
    }

    /**
     * Retrieve an iterator (IteratorAggregate interface)
     *
     * @return \Iterator
     */
    public function getIterator(): \Iterator
    {
        return new \ArrayIterator($this->fieldDefs);
    }

    /**
     * Get the number of fields (Countable interface)
     *
     * @return int
     */
    public function count(): int
    {
        return $this->fieldCount;
    }
}
<?php declare(strict_types=1);
/**
 * Creates TypeDefinitionManager objects
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package TypeDefinitions
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Records\TypeDefinitions;

/**
 * Creates TypeDefinitionManager objects
 *
 * @category LibDNS
 * @package TypeDefinitions
 * @author Chris Wright <https://github.com/DaveRandom>
 */
class TypeDefinitionManagerFactory
{
    /**
     * Create a new TypeDefinitionManager object
     *
     * @return \LibDNS\Records\TypeDefinitions\TypeDefinitionManager
     */
    public function create(): TypeDefinitionManager
    {
        return new TypeDefinitionManager(new TypeDefinitionFactory, new FieldDefinitionFactory);
    }
}
<?php declare(strict_types=1);
/**
 * Represents a DNS record
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Records
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Records;

use \LibDNS\Records\Types\DomainName;

/**
 * Represents a DNS record
 *
 * @category LibDNS
 * @package Records
 * @author Chris Wright <https://github.com/DaveRandom>
 */
abstract class Record
{
    /**
     * @var \LibDNS\Records\Types\TypeFactory
     */
    protected $typeFactory;

    /**
     * @var \LibDNS\Records\Types\DomainName
     */
    protected $name;

    /**
     * @var int
     */
    protected $type;

    /**
     * @var int
     */
    protected $class = ResourceClasses::IN;

    /**
     * Get the value of the record name field
     *
     * @return \LibDNS\Records\Types\DomainName
     */
    public function getName(): DomainName
    {
        return $this->name;
    }

    /**
     * Set the value of the record name field
     *
     * @param string|\LibDNS\Records\Types\DomainName $name
     * @throws \UnexpectedValueException When the supplied value is not a valid domain name
     */
    public function setName($name)
    {
        if (!($name instanceof DomainName)) {
            $name = $this->typeFactory->createDomainName((string)$name);
        }

        $this->name = $name;
    }

    /**
     * Get the value of the record type field
     *
     * @return int
     */
    public function getType(): int
    {
        return $this->type;
    }

    /**
     * Get the value of the record class field
     *
     * @return int
     */
    public function getClass(): int
    {
        return $this->class;
    }

    /**
     * Set the value of the record class field
     *
     * @param int $class The new value, can be indicated using the ResourceClasses/ResourceQClasses enums
     * @throws \RangeException When the supplied value is outside the valid range 0 - 65535
     */
    public function setClass(int $class)
    {
        if ($class < 0 || $class > 65535) {
            throw new \RangeException('Record class must be in the range 0 - 65535');
        }

        $this->class = $class;
    }
}
<?php declare(strict_types=1);
/**
 * Builds Resource objects of a specific type
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Records
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Records;

use \LibDNS\Records\TypeDefinitions\TypeDefinitionManager;

/**
 * Builds Resource objects of a specific type
 *
 * @category LibDNS
 * @package Records
 * @author Chris Wright <https://github.com/DaveRandom>
 */
class ResourceBuilder
{
    /**
     * @var \LibDNS\Records\ResourceFactory
     */
    private $resourceFactory;

    /**
     * @var \LibDNS\Records\RDataBuilder
     */
    private $rDataBuilder;

    /**
     * @var \LibDNS\Records\TypeDefinitions\TypeDefinitionManager
     */
    private $typeDefinitionManager;

    /**
     * Constructor
     *
     * @param \LibDNS\Records\ResourceFactory $resourceFactory
     * @param \LibDNS\Records\RDataBuilder $rDataBuilder
     * @param \LibDNS\Records\TypeDefinitions\TypeDefinitionManager $typeDefinitionManager
     */
    public function __construct(ResourceFactory $resourceFactory, RDataBuilder $rDataBuilder, TypeDefinitionManager $typeDefinitionManager)
    {
        $this->resourceFactory = $resourceFactory;
        $this->rDataBuilder = $rDataBuilder;
        $this->typeDefinitionManager = $typeDefinitionManager;
    }

    /**
     * Create a new Resource object
     *
     * @param int $type Type of the resource, can be indicated using the ResourceTypes enum
     * @return \LibDNS\Records\Resource
     */
    public function build(int $type): Resource
    {
        $typeDefinition = $this->typeDefinitionManager->getTypeDefinition($type);
        $rData = $this->rDataBuilder->build($typeDefinition);

        return $this->resourceFactory->create($type, $rData);
    }
}
<?php declare(strict_types=1);
/**
 * Enumeration of possible resource CLASS values
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Records
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Records;

use \LibDNS\Enumeration;

/**
 * Enumeration of possible resource CLASS values
 *
 * @category LibDNS
 * @package Records
 * @author Chris Wright <https://github.com/DaveRandom>
 */
abstract class ResourceClasses extends Enumeration
{
    const IN = 1;
    const CS = 2;
    const CH = 3;
    const HS = 4;
}
<?php declare(strict_types=1);
/**
 * Creates EncodingContext objects
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Encoder
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Encoder;

use \LibDNS\Packets\Packet;
use \LibDNS\Packets\LabelRegistry;

/**
 * Creates EncodingContext objects
 *
 * @category LibDNS
 * @package Encoder
 * @author Chris Wright <https://github.com/DaveRandom>
 */
class EncodingContextFactory
{
    /**
     * Create a new EncodingContext object
     *
     * @param \LibDNS\Packets\Packet $packet   The packet to be decoded
     * @param bool $compress Whether message compression is enabled
     * @return \LibDNS\Encoder\EncodingContext
     */
    public function create(Packet $packet, bool $compress): EncodingContext
    {
        return new EncodingContext($packet, new LabelRegistry, $compress);
    }
}
<?php declare(strict_types=1);
/**
 * Creates Encoder objects
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Encoder
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Encoder;

use \LibDNS\Packets\PacketFactory;

/**
 * Creates Encoder objects
 *
 * @category LibDNS
 * @package Encoder
 * @author Chris Wright <https://github.com/DaveRandom>
 */
class EncoderFactory
{
    /**
     * Create a new Encoder object
     *
     * @return \LibDNS\Encoder\Encoder
     */
    public function create(): Encoder
    {
        return new Encoder(new PacketFactory, new EncodingContextFactory);
    }
}
<?php declare(strict_types=1);
/**
 * Encodes Message objects to raw network data
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Encoder
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Encoder;

use \LibDNS\Packets\PacketFactory;
use \LibDNS\Messages\Message;
use \LibDNS\Records\Question;
use \LibDNS\Records\Resource;
use \LibDNS\Records\Types\Type;
use \LibDNS\Records\Types\Anything;
use \LibDNS\Records\Types\BitMap;
use \LibDNS\Records\Types\Char;
use \LibDNS\Records\Types\CharacterString;
use \LibDNS\Records\Types\DomainName;
use \LibDNS\Records\Types\IPv4Address;
use \LibDNS\Records\Types\IPv6Address;
use \LibDNS\Records\Types\Long;
use \LibDNS\Records\Types\Short;

/**
 * Encodes Message objects to raw network data
 *
 * @category LibDNS
 * @package Encoder
 * @author Chris Wright <https://github.com/DaveRandom>
 */
class Encoder
{
    /**
     * @var \LibDNS\Packets\PacketFactory
     */
    private $packetFactory;

    /**
     * @var \LibDNS\Encoder\EncodingContextFactory
     */
    private $encodingContextFactory;

    /**
     * Constructor
     *
     * @param \LibDNS\Packets\PacketFactory $packetFactory
     * @param \LibDNS\Encoder\EncodingContextFactory $encodingContextFactory
     */
    public function __construct(PacketFactory $packetFactory, EncodingContextFactory $encodingContextFactory)
    {
        $this->packetFactory = $packetFactory;
        $this->encodingContextFactory = $encodingContextFactory;
    }

    /**
     * Encode the header section of the message
     *
     * @param \LibDNS\Encoder\EncodingContext $encodingContext
     * @param \LibDNS\Messages\Message $message
     * @return string
     * @throws \UnexpectedValueException When the header section is invalid
     */
    private function encodeHeader(EncodingContext $encodingContext, Message $message): string
    {
        $header = [
            'id' => $message->getID(),
            'meta' => 0,
            'qd' => $message->getQuestionRecords()->count(),
            'an' => $message->getAnswerRecords()->count(),
            'ns' => $message->getAuthorityRecords()->count(),
            'ar' => $message->getAdditionalRecords()->count()
        ];

        $header['meta'] |= $message->getType() << 15;
        $header['meta'] |= $message->getOpCode() << 11;
        $header['meta'] |= ((int) $message->isAuthoritative()) << 10;
        $header['meta'] |= ((int) $encodingContext->isTruncated()) << 9;
        $header['meta'] |= ((int) $message->isRecursionDesired()) << 8;
        $header['meta'] |= ((int) $message->isRecursionAvailable()) << 7;
        $header['meta'] |= $message->getResponseCode();

        return \pack('n*', $header['id'], $header['meta'], $header['qd'], $header['an'], $header['ns'], $header['ar']);
    }

    /**
     * Encode an Anything field
     *
     * @param \LibDNS\Records\Types\Anything $anything
     * @return string
     */
    private function encodeAnything(Anything $anything): string
    {
        return $anything->getValue();
    }

    /**
     * Encode a BitMap field
     *
     * @param \LibDNS\Records\Types\BitMap $bitMap
     * @return string
     */
    private function encodeBitMap(BitMap $bitMap): string
    {
        return $bitMap->getValue();
    }

    /**
     * Encode a Char field
     *
     * @param \LibDNS\Records\Types\Char $char
     * @return string
     */
    private function encodeChar(Char $char): string
    {
        return \chr($char->getValue());
    }

    /**
     * Encode a CharacterString field
     *
     * @param \LibDNS\Records\Types\CharacterString $characterString
     * @return string
     */
    private function encodeCharacterString(CharacterString $characterString): string
    {
        $data = $characterString->getValue();
        return \chr(\strlen($data)) . $data;
    }

    /**
     * Encode a DomainName field
     *
     * @param \LibDNS\Records\Types\DomainName $domainName
     * @param \LibDNS\Encoder\EncodingContext $encodingContext
     * @return string
     */
    private function encodeDomainName(DomainName $domainName, EncodingContext $encodingContext): string
    {
        $packetIndex = $encodingContext->getPacket()->getLength() + 12;
        $labelRegistry = $encodingContext->getLabelRegistry();

        $result = '';
        $labels = $domainName->getLabels();

        if ($encodingContext->useCompression()) {
            do {
                $part = \implode('.', $labels);
                $index = $labelRegistry->lookupIndex($part);

                if ($index === null) {
                    $labelRegistry->register($part, $packetIndex);

                    $label = \array_shift($labels);
                    $length = \strlen($label);

                    $result .= \chr($length) . $label;
                    $packetIndex += $length + 1;
                } else {
                    $result .= \pack('n', 0b1100000000000000 | $index);
                    break;
                }
            } while($labels);

            if (!$labels) {
                $result .= "\x00";
            }
        } else {
            foreach ($labels as $label) {
                $result .= \chr(\strlen($label)) . $label;
            }

            $result .= "\x00";
        }

        return $result;
    }

    /**
     * Encode an IPv4Address field
     *
     * @param \LibDNS\Records\Types\IPv4Address $ipv4Address
     * @return string
     */
    private function encodeIPv4Address(IPv4Address $ipv4Address): string
    {
        $octets = $ipv4Address->getOctets();
        return \pack('C*', $octets[0], $octets[1], $octets[2], $octets[3]);
    }

    /**
     * Encode an IPv6Address field
     *
     * @param \LibDNS\Records\Types\IPv6Address $ipv6Address
     * @return string
     */
    private function encodeIPv6Address(IPv6Address $ipv6Address): string
    {
        $shorts = $ipv6Address->getShorts();
        return \pack('n*', $shorts[0], $shorts[1], $shorts[2], $shorts[3], $shorts[4], $shorts[5], $shorts[6], $shorts[7]);
    }

    /**
     * Encode a Long field
     *
     * @param \LibDNS\Records\Types\Long $long
     * @return string
     */
    private function encodeLong(Long $long): string
    {
        return \pack('N', $long->getValue());
    }

    /**
     * Encode a Short field
     *
     * @param \LibDNS\Records\Types\Short $short
     * @return string
     */
    private function encodeShort(Short $short): string
    {
        return \pack('n', $short->getValue());
    }

    /**
     * Encode a type object
     *
     * @param \LibDNS\Encoder\EncodingContext $encodingContext
     * @param \LibDNS\Records\Types\Type $type
     * @return string
     */
    private function encodeType(EncodingContext $encodingContext, Type $type): string
    {
        if ($type instanceof Anything) {
            $result = $this->encodeAnything($type);
        } else if ($type instanceof BitMap) {
            $result = $this->encodeBitMap($type);
        } else if ($type instanceof Char) {
            $result = $this->encodeChar($type);
        } else if ($type instanceof CharacterString) {
            $result = $this->encodeCharacterString($type);
        } else if ($type instanceof DomainName) {
            $result = $this->encodeDomainName($type, $encodingContext);
        } else if ($type instanceof IPv4Address) {
            $result = $this->encodeIPv4Address($type);
        } else if ($type instanceof IPv6Address) {
            $result = $this->encodeIPv6Address($type);
        } else if ($type instanceof Long) {
            $result = $this->encodeLong($type);
        } else if ($type instanceof Short) {
            $result = $this->encodeShort($type);
        } else {
            throw new \InvalidArgumentException('Unknown Type ' . \get_class($type));
        }

        return $result;
    }

    /**
     * Encode a question record
     *
     * @param \LibDNS\Encoder\EncodingContext $encodingContext
     * @param \LibDNS\Records\Question $record
     */
    private function encodeQuestionRecord(EncodingContext $encodingContext, Question $record)
    {
        if (!$encodingContext->isTruncated()) {
            $packet = $encodingContext->getPacket();
            $name = $this->encodeDomainName($record->getName(), $encodingContext);
            $meta = \pack('n*', $record->getType(), $record->getClass());

            if (12 + $packet->getLength() + \strlen($name) + 4 > 512) {
                $encodingContext->isTruncated(true);
            } else {
                $packet->write($name);
                $packet->write($meta);
            }
        }
    }

    /**
     * Encode a resource record
     *
     * @param \LibDNS\Encoder\EncodingContext $encodingContext
     * @param \LibDNS\Records\Resource $record
     */
    private function encodeResourceRecord(EncodingContext $encodingContext, Resource $record)
    {
        if (!$encodingContext->isTruncated()) {
            $packet = $encodingContext->getPacket();
            $name = $this->encodeDomainName($record->getName(), $encodingContext);

            $data = '';
            foreach ($record->getData() as $field) {
                $data .= $this->encodeType($encodingContext, $field);
            }

            $meta = \pack('n2Nn', $record->getType(), $record->getClass(), $record->getTTL(), \strlen($data));

            if (12 + $packet->getLength() + \strlen($name) + 10 + \strlen($data) > 512) {
                $encodingContext->isTruncated(true);
            } else {
                $packet->write($name);
                $packet->write($meta);
                $packet->write($data);
            }
        }
    }

    /**
     * Encode a Message to raw network data
     *
     * @param \LibDNS\Messages\Message $message  The Message to encode
     * @param bool $compress Enable message compression
     * @return string
     */
    public function encode(Message $message, $compress = true): string
    {
        $packet = $this->packetFactory->create();
        $encodingContext = $this->encodingContextFactory->create($packet, $compress);

        foreach ($message->getQuestionRecords() as $record) {
            /** @var \LibDNS\Records\Question $record */
            $this->encodeQuestionRecord($encodingContext, $record);
        }
        foreach ($message->getAnswerRecords() as $record) {
            /** @var \LibDNS\Records\Resource $record */
            $this->encodeResourceRecord($encodingContext, $record);
        }
        foreach ($message->getAuthorityRecords() as $record) {
            /** @var \LibDNS\Records\Resource $record */
            $this->encodeResourceRecord($encodingContext, $record);
        }
        foreach ($message->getAdditionalRecords() as $record) {
            /** @var \LibDNS\Records\Resource $record */
            $this->encodeResourceRecord($encodingContext, $record);
        }

        return $this->encodeHeader($encodingContext, $message) . $packet->read($packet->getLength());
    }
}
<?php declare(strict_types=1);
/**
 * Holds data associated with an encode operation
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Encoder
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Encoder;

use \LibDNS\Packets\Packet;
use \LibDNS\Packets\LabelRegistry;

/**
 * Holds data associated with an encode operation
 *
 * @category LibDNS
 * @package Encoder
 * @author Chris Wright <https://github.com/DaveRandom>
 */
class EncodingContext
{
    /**
     * @var \LibDNS\Packets\Packet
     */
    private $packet;

    /**
     * @var \LibDNS\Packets\LabelRegistry
     */
    private $labelRegistry;

    /**
     * @var bool
     */
    private $compress;

    /**
     * @var bool
     */
    private $truncate = false;

    /**
     * Constructor
     *
     * @param \LibDNS\Packets\Packet $packet
     * @param \LibDNS\Packets\LabelRegistry $labelRegistry
     * @param bool $compress
     */
    public function __construct(Packet $packet, LabelRegistry $labelRegistry, bool $compress)
    {
        $this->packet = $packet;
        $this->labelRegistry = $labelRegistry;
        $this->compress = $compress;
    }

    /**
     * Get the packet
     *
     * @return \LibDNS\Packets\Packet
     */
    public function getPacket(): Packet
    {
        return $this->packet;
    }

    /**
     * Get the label registry
     *
     * @return \LibDNS\Packets\LabelRegistry
     */
    public function getLabelRegistry(): LabelRegistry
    {
        return $this->labelRegistry;
    }

    /**
     * Determine whether compression is enabled
     *
     * @return bool
     */
    public function useCompression(): bool
    {
        return $this->compress;
    }

    /**
     * Determine or set whether the message is truncated
     *
     * @param bool $truncate
     * @return bool
     */
    public function isTruncated(?bool $truncate = null): bool
    {
        $result = $this->truncate;

        if ($truncate !== null) {
            $this->truncate = $truncate;
        }

        return $result;
    }
}
<?php declare(strict_types=1);
/**
 * Factory which creates Message objects
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Messages
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Messages;

use \LibDNS\Records\RecordCollectionFactory;

/**
 * Factory which creates Message objects
 *
 * @category LibDNS
 * @package Messages
 * @author Chris Wright <https://github.com/DaveRandom>
 */
class MessageFactory
{
    /**
     * Create a new Message object
     *
     * @param int $type Value of the message type field
     * @return \LibDNS\Messages\Message
     */
    public function create(?int $type = null): Message
    {
        return new Message(new RecordCollectionFactory, $type);
    }
}
<?php declare(strict_types=1);
/**
 * Enumeration of possible message response codes
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Messages
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Messages;

use \LibDNS\Enumeration;

/**
 * Enumeration of possible message types
 *
 * @category LibDNS
 * @package Messages
 * @author Chris Wright <https://github.com/DaveRandom>
 */
final class MessageResponseCodes extends Enumeration
{
    const NO_ERROR = 0;
    const FORMAT_ERROR = 1;
    const SERVER_FAILURE = 2;
    const NAME_ERROR = 3;
    const NOT_IMPLEMENTED = 4;
    const REFUSED = 5;
}
<?php declare(strict_types=1);
/**
 * Enumeration of possible message types
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Messages
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Messages;

use \LibDNS\Enumeration;

/**
 * Enumeration of possible message types
 *
 * @category LibDNS
 * @package Messages
 * @author Chris Wright <https://github.com/DaveRandom>
 */
final class MessageTypes extends Enumeration
{
    const QUERY = 0;
    const RESPONSE = 1;
}
<?php declare(strict_types=1);
/**
 * Represents a DNS protocol message
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Messages
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Messages;

use LibDNS\Records\RecordCollection;
use \LibDNS\Records\RecordCollectionFactory;
use \LibDNS\Records\RecordTypes;

/**
 * Represents a DNS protocol message
 *
 * @category LibDNS
 * @package Messages
 * @author Chris Wright <https://github.com/DaveRandom>
 */
class Message
{
    /**
     * @var int Unsigned short that identifies the DNS transaction
     */
    private $id = 0;

    /**
     * @var int Indicates the type of the message, can be indicated using the MessageTypes enum
     */
    private $type = -1;

    /**
     * @var int Message opcode, can be indicated using the MessageOpCodes enum
     */
    private $opCode = MessageOpCodes::QUERY;

    /**
     * @var bool Whether a response message is authoritative
     */
    private $authoritative = false;

    /**
     * @var bool Whether the message is truncated
     */
    private $truncated = false;

    /**
     * @var bool Whether a query desires the server to recurse the lookup
     */
    private $recursionDesired = true;

    /**
     * @var bool Whether a server could provide recursion in a response
     */
    private $recursionAvailable = false;

    /**
     * @var int Message response code, can be indicated using the MessageResponseCodes enum
     */
    private $responseCode = MessageResponseCodes::NO_ERROR;

    /**
     * @var \LibDNS\Records\RecordCollection Collection of question records
     */
    private $questionRecords;

    /**
     * @var \LibDNS\Records\RecordCollection Collection of question records
     */
    private $answerRecords;

    /**
     * @var \LibDNS\Records\RecordCollection Collection of authority records
     */
    private $authorityRecords;

    /**
     * @var \LibDNS\Records\RecordCollection Collection of authority records
     */
    private $additionalRecords;

    /**
     * Constructor
     *
     * @param \LibDNS\Records\RecordCollectionFactory $recordCollectionFactory Factory which makes RecordCollection objects
     * @param int $type Value of the message type field
     * @throws \RangeException When the supplied message type is outside the valid range 0 - 1
     */
    public function __construct(RecordCollectionFactory $recordCollectionFactory, ?int $type = null)
    {
        $this->questionRecords = $recordCollectionFactory->create(RecordTypes::QUESTION);
        $this->answerRecords = $recordCollectionFactory->create(RecordTypes::RESOURCE);
        $this->authorityRecords = $recordCollectionFactory->create(RecordTypes::RESOURCE);
        $this->additionalRecords = $recordCollectionFactory->create(RecordTypes::RESOURCE);

        if ($type !== null) {
            $this->setType($type);
        }
    }

    /**
     * Get the value of the message ID field
     *
     * @return int
     */
    public function getID(): int
    {
        return $this->id;
    }

    /**
     * Set the value of the message ID field
     *
     * @param int $id The new value
     * @throws \RangeException When the supplied value is outside the valid range 0 - 65535
     */
    public function setID(int $id)
    {
        if ($id < 0 || $id > 65535) {
            throw new \RangeException('Message ID must be in the range 0 - 65535');
        }

        $this->id = $id;
    }

    /**
     * Get the value of the message type field
     *
     * @return int
     */
    public function getType(): int
    {
        return $this->type;
    }

    /**
     * Set the value of the message type field
     *
     * @param int $type The new value
     * @throws \RangeException When the supplied value is outside the valid range 0 - 1
     */
    public function setType(int $type)
    {
        if ($type < 0 || $type > 1) {
            throw new \RangeException('Message type must be in the range 0 - 1');
        }

        $this->type = $type;
    }

    /**
     * Get the value of the message opcode field
     *
     * @return int
     */
    public function getOpCode(): int
    {
        return $this->opCode;
    }

    /**
     * Set the value of the message opcode field
     *
     * @param int $opCode The new value
     * @throws \RangeException When the supplied value is outside the valid range 0 - 15
     */
    public function setOpCode(int $opCode)
    {
        if ($opCode < 0 || $opCode > 15) {
            throw new \RangeException('Message opcode must be in the range 0 - 15');
        }

        $this->opCode = $opCode;
    }

    /**
     * Inspect the value of the authoritative field and optionally set a new value
     *
     * @param bool $newValue The new value
     * @return bool The old value
     */
    public function isAuthoritative(?bool $newValue = null): bool
    {
        $result = $this->authoritative;

        if ($newValue !== null) {
            $this->authoritative = $newValue;
        }

        return $result;
    }

    /**
     * Inspect the value of the truncated field and optionally set a new value
     *
     * @param bool $newValue The new value
     * @return bool The old value
     */
    public function isTruncated(?bool $newValue = null): bool
    {
        $result = $this->truncated;

        if ($newValue !== null) {
            $this->truncated = $newValue;
        }

        return $result;
    }

    /**
     * Inspect the value of the recusion desired field and optionally set a new value
     *
     * @param bool $newValue The new value
     * @return bool The old value
     */
    public function isRecursionDesired(?bool $newValue = null): bool
    {
        $result = $this->recursionDesired;

        if ($newValue !== null) {
            $this->recursionDesired = $newValue;
        }

        return $result;
    }

    /**
     * Inspect the value of the recursion available field and optionally set a new value
     *
     * @param bool $newValue The new value
     * @return bool The old value
     */
    public function isRecursionAvailable(?bool $newValue = null): bool
    {
        $result = $this->recursionAvailable;

        if ($newValue !== null) {
            $this->recursionAvailable = $newValue;
        }

        return $result;
    }

    /**
     * Get the value of the message response code field
     *
     * @return int
     */
    public function getResponseCode(): int
    {
        return $this->responseCode;
    }

    /**
     * Set the value of the message response code field
     *
     * @param int $responseCode The new value
     * @throws \RangeException When the supplied value is outside the valid range 0 - 15
     */
    public function setResponseCode(int $responseCode)
    {
        if ($responseCode < 0 || $responseCode > 15) {
            throw new \RangeException('Message response code must be in the range 0 - 15');
        }

        $this->responseCode = $responseCode;
    }

    /**
     * Get the question records collection
     *
     * @return \LibDNS\Records\RecordCollection
     */
    public function getQuestionRecords(): RecordCollection
    {
        return $this->questionRecords;
    }

    /**
     * Get the answer records collection
     *
     * @return \LibDNS\Records\RecordCollection
     */
    public function getAnswerRecords(): RecordCollection
    {
        return $this->answerRecords;
    }

    /**
     * Get the authority records collection
     *
     * @return \LibDNS\Records\RecordCollection
     */
    public function getAuthorityRecords(): RecordCollection
    {
        return $this->authorityRecords;
    }

    /**
     * Get the additional records collection
     *
     * @return \LibDNS\Records\RecordCollection
     */
    public function getAdditionalRecords(): RecordCollection
    {
        return $this->additionalRecords;
    }
}
<?php declare(strict_types=1);
/**
 * Enumeration of possible message opcodes
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Messages
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Messages;

use \LibDNS\Enumeration;

/**
 * Enumeration of possible message types
 *
 * @category LibDNS
 * @package Messages
 * @author Chris Wright <https://github.com/DaveRandom>
 */
final class MessageOpCodes extends Enumeration
{
    const QUERY = 0;
    const IQUERY = 1;
    const STATUS = 2;
}
<?php declare(strict_types=1);
/**
 * Creates Packet objects
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Packets
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Packets;

/**
 * Creates Packet objects
 *
 * @category LibDNS
 * @package Packets
 * @author Chris Wright <https://github.com/DaveRandom>
 */
class PacketFactory
{
    /**
     * Create a new Packet object
     *
     * @param string $data
     * @return \LibDNS\Packets\Packet
     */
    public function create(string $data = ''): Packet
    {
        return new Packet($data);
    }
}
<?php declare(strict_types=1);
/**
 * Represents a raw network data packet
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Packets
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Packets;

/**
 * Represents a raw network data packet
 *
 * @category LibDNS
 * @package Packets
 * @author Chris Wright <https://github.com/DaveRandom>
 */
class Packet
{
    /**
     * @var string
     */
    private $data;

    /**
     * @var int Data length
     */
    private $length;

    /**
     * @var int Read pointer
     */
    private $pointer = 0;

    /**
     * Constructor
     *
     * @param string $data The initial packet raw data
     */
    public function __construct(string $data = '')
    {
        $this->data = $data;
        $this->length = \strlen($this->data);
    }

    /**
     * Read bytes from the packet data
     *
     * @param int $length The number of bytes to read
     * @return string
     * @throws \OutOfBoundsException When the pointer position is invalid or the supplied length is negative
     */
    public function read(?int $length = null): string
    {
        if ($this->pointer > $this->length) {
            throw new \OutOfBoundsException('Pointer position invalid');
        }

        if ($length === null) {
            $result = \substr($this->data, $this->pointer);
            $this->pointer = $this->length;
        } else {
            if ($length < 0) {
                throw new \OutOfBoundsException('Length must be a positive integer');
            }

            $result = \substr($this->data, $this->pointer, $length);
            $this->pointer += $length;
        }

        return $result;
    }

    /**
     * Append data to the packet
     *
     * @param string $data The data to append
     * @return int The number of bytes written
     */
    public function write(string $data): int
    {
        $length = \strlen($data);

        $this->data .= $data;
        $this->length += $length;

        return $length;
    }

    /**
     * Reset the read pointer
     */
    public function reset()
    {
        $this->pointer = 0;
    }

    /**
     * Get the pointer index
     *
     * @return int
     */
    public function getPointer(): int
    {
        return $this->pointer;
    }

    /**
     * Get the data length
     *
     * @return int
     */
    public function getLength(): int
    {
        return $this->length;
    }

    /**
     * Get the number of remaining bytes from the pointer position
     *
     * @return int
     */
    public function getBytesRemaining(): int
    {
        return $this->length - $this->pointer;
    }
}
<?php declare(strict_types=1);
/**
 * Maintains a list of the relationships between domain name labels and the first point at
 * which they appear in a packet
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Packets
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Packets;

/**
 * Creates Packet objects
 *
 * @category LibDNS
 * @package Packets
 * @author Chris Wright <https://github.com/DaveRandom>
 */
class LabelRegistry
{
    /**
     * @var int[] Map of labels to indexes
     */
    private $labels = [];

    /**
     * @var string[][] Map of indexes to labels
     */
    private $indexes = [];

    /**
     * Register a new relationship
     *
     * @param string|string[] $labels
     * @param int $index
     */
    public function register($labels, int $index)
    {
        if (\is_array($labels)) {
            $labelsArr = $labels;
            $labelsStr = \implode('.', $labels);
        } else {
            $labelsArr = \explode('.', $labels);
            $labelsStr = (string) $labels;
        }

        if (!isset($this->labels[$labelsStr]) || $index < $this->labels[$labelsStr]) {
            $this->labels[$labelsStr] = $index;
        }

        $this->indexes[$index] = $labelsArr;
    }

    /**
     * Lookup the index of a label
     *
     * @param string $label
     * @return int|null
     */
    public function lookupIndex(string $label)
    {
        return $this->labels[$label] ?? null;
    }

    /**
     * Lookup the label at an index
     *
     * @param int $index
     * @return string[]|null
     */
    public function lookupLabel(int $index)
    {
        return $this->indexes[$index] ?? null;
    }
}
<?php declare(strict_types=1);
/**
 * Decodes raw network data to Message objects
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Decoder
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Decoder;

use LibDNS\Messages\Message;
use LibDNS\Messages\MessageFactory;
use LibDNS\Packets\Packet;
use LibDNS\Packets\PacketFactory;
use LibDNS\Records\Question;
use LibDNS\Records\QuestionFactory;
use LibDNS\Records\Resource;
use LibDNS\Records\ResourceBuilder;
use LibDNS\Records\Types\Anything;
use LibDNS\Records\Types\BitMap;
use LibDNS\Records\Types\Char;
use LibDNS\Records\Types\CharacterString;
use LibDNS\Records\Types\DomainName;
use LibDNS\Records\Types\IPv4Address;
use LibDNS\Records\Types\IPv6Address;
use LibDNS\Records\Types\Long;
use LibDNS\Records\Types\Short;
use LibDNS\Records\Types\Type;
use LibDNS\Records\Types\TypeBuilder;
use LibDNS\Records\Types\Types;

/**
 * Decodes raw network data to Message objects
 *
 * @category LibDNS
 * @package Decoder
 * @author Chris Wright <https://github.com/DaveRandom>
 */
class Decoder
{
    /**
     * @var \LibDNS\Packets\PacketFactory
     */
    private $packetFactory;

    /**
     * @var \LibDNS\Messages\MessageFactory
     */
    private $messageFactory;

    /**
     * @var \LibDNS\Records\QuestionFactory
     */
    private $questionFactory;

    /**
     * @var \LibDNS\Records\ResourceBuilder
     */
    private $resourceBuilder;

    /**
     * @var \LibDNS\Records\Types\TypeBuilder
     */
    private $typeBuilder;

    /**
     * @var \LibDNS\Decoder\DecodingContextFactory
     */
    private $decodingContextFactory;

    /**
     * @var bool
     */
    private $allowTrailingData;

    /**
     * Constructor
     *
     * @param \LibDNS\Packets\PacketFactory $packetFactory
     * @param \LibDNS\Messages\MessageFactory $messageFactory
     * @param \LibDNS\Records\QuestionFactory $questionFactory
     * @param \LibDNS\Records\ResourceBuilder $resourceBuilder
     * @param \LibDNS\Records\Types\TypeBuilder $typeBuilder
     * @param \LibDNS\Decoder\DecodingContextFactory $decodingContextFactory
     * @param bool $allowTrailingData
     */
    public function __construct(
        PacketFactory $packetFactory,
        MessageFactory $messageFactory,
        QuestionFactory $questionFactory,
        ResourceBuilder $resourceBuilder,
        TypeBuilder $typeBuilder,
        DecodingContextFactory $decodingContextFactory,
        bool $allowTrailingData = true
    ) {
        $this->packetFactory = $packetFactory;
        $this->messageFactory = $messageFactory;
        $this->questionFactory = $questionFactory;
        $this->resourceBuilder = $resourceBuilder;
        $this->typeBuilder = $typeBuilder;
        $this->decodingContextFactory = $decodingContextFactory;
        $this->allowTrailingData = $allowTrailingData;
    }

    /**
     * Read a specified number of bytes of data from a packet
     *
     * @param \LibDNS\Packets\Packet $packet
     * @param int $length
     * @return string
     * @throws \UnexpectedValueException When the read operation does not result in the requested number of bytes
     */
    private function readDataFromPacket(Packet $packet, int $length): string
    {
        if ($packet->getBytesRemaining() < $length) {
            throw new \UnexpectedValueException('Decode error: Incomplete packet (tried to read ' . $length . ' bytes from index ' . $packet->getPointer());
        }

        return $packet->read($length);
    }

    /**
     * Decode the header section of the message
     *
     * @param \LibDNS\Decoder\DecodingContext $decodingContext
     * @param \LibDNS\Messages\Message $message
     * @throws \UnexpectedValueException When the header section is invalid
     */
    private function decodeHeader(DecodingContext $decodingContext, Message $message)
    {
        $header = \unpack('nid/nmeta/nqd/nan/nns/nar', $this->readDataFromPacket($decodingContext->getPacket(), 12));
        if (!$header) {
            throw new \UnexpectedValueException('Decode error: Header unpack failed');
        }

        $message->setID($header['id']);

        $message->setType(($header['meta'] & 0b1000000000000000) >> 15);
        $message->setOpCode(($header['meta'] & 0b0111100000000000) >> 11);
        $message->isAuthoritative((bool)(($header['meta'] & 0b0000010000000000) >> 10));
        $message->isTruncated((bool)(($header['meta'] & 0b0000001000000000) >> 9));
        $message->isRecursionDesired((bool)(($header['meta'] & 0b0000000100000000) >> 8));
        $message->isRecursionAvailable((bool)(($header['meta'] & 0b0000000010000000) >> 7));
        $message->setResponseCode($header['meta'] & 0b0000000000001111);

        $decodingContext->setExpectedQuestionRecords($header['qd']);
        $decodingContext->setExpectedAnswerRecords($header['an']);
        $decodingContext->setExpectedAuthorityRecords($header['ns']);
        $decodingContext->setExpectedAdditionalRecords($header['ar']);
    }

    /**
     * Decode an Anything field
     *
     * @param \LibDNS\Decoder\DecodingContext $decodingContext
     * @param \LibDNS\Records\Types\Anything $anything The object to populate with the result
     * @param int $length
     * @return int The number of packet bytes consumed by the operation
     * @throws \UnexpectedValueException When the packet data is invalid
     */
    private function decodeAnything(DecodingContext $decodingContext, Anything $anything, int $length): int
    {
        $anything->setValue($this->readDataFromPacket($decodingContext->getPacket(), $length));

        return $length;
    }

    /**
     * Decode a BitMap field
     *
     * @param \LibDNS\Decoder\DecodingContext $decodingContext
     * @param \LibDNS\Records\Types\BitMap $bitMap The object to populate with the result
     * @param int $length
     * @return int The number of packet bytes consumed by the operation
     * @throws \UnexpectedValueException When the packet data is invalid
     */
    private function decodeBitMap(DecodingContext $decodingContext, BitMap $bitMap, int $length): int
    {
        $bitMap->setValue($this->readDataFromPacket($decodingContext->getPacket(), $length));

        return $length;
    }

    /**
     * Decode a Char field
     *
     * @param \LibDNS\Decoder\DecodingContext $decodingContext
     * @param \LibDNS\Records\Types\Char $char The object to populate with the result
     * @return int The number of packet bytes consumed by the operation
     * @throws \UnexpectedValueException When the packet data is invalid
     */
    private function decodeChar(DecodingContext $decodingContext, Char $char): int
    {
        $value = \unpack('C', $this->readDataFromPacket($decodingContext->getPacket(), 1))[1];
        $char->setValue($value);

        return 1;
    }

    /**
     * Decode a CharacterString field
     *
     * @param \LibDNS\Decoder\DecodingContext $decodingContext
     * @param \LibDNS\Records\Types\CharacterString $characterString The object to populate with the result
     * @return int The number of packet bytes consumed by the operation
     * @throws \UnexpectedValueException When the packet data is invalid
     */
    private function decodeCharacterString(DecodingContext $decodingContext, CharacterString $characterString): int
    {
        $packet = $decodingContext->getPacket();
        $length = \ord($this->readDataFromPacket($packet, 1));
        $characterString->setValue($this->readDataFromPacket($packet, $length));

        return $length + 1;
    }

    /**
     * Decode a DomainName field
     *
     * @param \LibDNS\Decoder\DecodingContext $decodingContext
     * @param \LibDNS\Records\Types\DomainName $domainName The object to populate with the result
     * @return int The number of packet bytes consumed by the operation
     * @throws \UnexpectedValueException When the packet data is invalid
     */
    private function decodeDomainName(DecodingContext $decodingContext, DomainName $domainName): int
    {
        $packet = $decodingContext->getPacket();
        $startIndex = '0x' . \dechex($packet->getPointer());
        $labelRegistry = $decodingContext->getLabelRegistry();

        $labels = [];
        $totalLength = 0;

        while (++$totalLength && $length = \ord($this->readDataFromPacket($packet, 1))) {
            $labelType = $length & 0b11000000;

            if ($labelType === 0b00000000) {
                $index = $packet->getPointer() - 1;
                $label = $this->readDataFromPacket($packet, $length);

                \array_unshift($labels, [$index, $label]);
                $totalLength += $length;
            } else if ($labelType === 0b11000000) {
                $index = (($length & 0b00111111) << 8) | \ord($this->readDataFromPacket($packet, 1));
                $ref = $labelRegistry->lookupLabel($index);
                if ($ref === null) {
                    throw new \UnexpectedValueException('Decode error: Invalid compression pointer reference in domain name at position ' . $startIndex);
                }

                \array_unshift($labels, $ref);
                $totalLength++;

                break;
            } else {
                throw new \UnexpectedValueException('Decode error: Invalid label type ' . $labelType . 'in domain name at position ' . $startIndex);
            }
        }

        $result = [];
        foreach ($labels as $label) {
            if (\is_int($label[0])) {
                \array_unshift($result, $label[1]);
                $labelRegistry->register($result, $label[0]);
            } else {
                $result = $label;
            }
        }
        $domainName->setLabels($result);

        return $totalLength;
    }

    /**
     * Decode an IPv4Address field
     *
     * @param \LibDNS\Decoder\DecodingContext $decodingContext
     * @param \LibDNS\Records\Types\IPv4Address $ipv4Address The object to populate with the result
     * @return int The number of packet bytes consumed by the operation
     * @throws \UnexpectedValueException When the packet data is invalid
     */
    private function decodeIPv4Address(DecodingContext $decodingContext, IPv4Address $ipv4Address): int
    {
        $octets = \unpack('C4', $this->readDataFromPacket($decodingContext->getPacket(), 4));
        $ipv4Address->setOctets($octets);

        return 4;
    }

    /**
     * Decode an IPv6Address field
     *
     * @param \LibDNS\Decoder\DecodingContext $decodingContext
     * @param \LibDNS\Records\Types\IPv6Address $ipv6Address The object to populate with the result
     * @return int The number of packet bytes consumed by the operation
     * @throws \UnexpectedValueException When the packet data is invalid
     */
    private function decodeIPv6Address(DecodingContext $decodingContext, IPv6Address $ipv6Address): int
    {
        $shorts = \unpack('n8', $this->readDataFromPacket($decodingContext->getPacket(), 16));
        $ipv6Address->setShorts($shorts);

        return 16;
    }

    /**
     * Decode a Long field
     *
     * @param \LibDNS\Decoder\DecodingContext $decodingContext
     * @param \LibDNS\Records\Types\Long $long The object to populate with the result
     * @return int The number of packet bytes consumed by the operation
     * @throws \UnexpectedValueException When the packet data is invalid
     */
    private function decodeLong(DecodingContext $decodingContext, Long $long): int
    {
        $value = \unpack('N', $this->readDataFromPacket($decodingContext->getPacket(), 4))[1];
        $long->setValue($value);

        return 4;
    }

    /**
     * Decode a Short field
     *
     * @param \LibDNS\Decoder\DecodingContext $decodingContext
     * @param \LibDNS\Records\Types\Short $short The object to populate with the result
     * @return int The number of packet bytes consumed by the operation
     * @throws \UnexpectedValueException When the packet data is invalid
     */
    private function decodeShort(DecodingContext $decodingContext, Short $short): int
    {
        $value = \unpack('n', $this->readDataFromPacket($decodingContext->getPacket(), 2))[1];
        $short->setValue($value);

        return 2;
    }

    /**
     * Decode a Type field
     *
     * @param \LibDNS\Decoder\DecodingContext $decodingContext
     * @param \LibDNS\Records\Types\Type $type The object to populate with the result
     * @param int $length Expected data length
     * @return int The number of packet bytes consumed by the operation
     * @throws \UnexpectedValueException When the packet data is invalid
     * @throws \InvalidArgumentException When the Type subtype is unknown
     */
    private function decodeType(DecodingContext $decodingContext, Type $type, int $length): int
    {
        if ($type instanceof Anything) {
            $result = $this->decodeAnything($decodingContext, $type, $length);
        } else if ($type instanceof BitMap) {
            $result = $this->decodeBitMap($decodingContext, $type, $length);
        } else if ($type instanceof Char) {
            $result = $this->decodeChar($decodingContext, $type);
        } else if ($type instanceof CharacterString) {
            $result = $this->decodeCharacterString($decodingContext, $type);
        } else if ($type instanceof DomainName) {
            $result = $this->decodeDomainName($decodingContext, $type);
        } else if ($type instanceof IPv4Address) {
            $result = $this->decodeIPv4Address($decodingContext, $type);
        } else if ($type instanceof IPv6Address) {
            $result = $this->decodeIPv6Address($decodingContext, $type);
        } else if ($type instanceof Long) {
            $result = $this->decodeLong($decodingContext, $type);
        } else if ($type instanceof Short) {
            $result = $this->decodeShort($decodingContext, $type);
        } else {
            throw new \InvalidArgumentException('Unknown Type ' . \get_class($type));
        }

        return $result;
    }

    /**
     * Decode a question record
     *
     * @param \LibDNS\Decoder\DecodingContext $decodingContext
     * @return \LibDNS\Records\Question
     * @throws \UnexpectedValueException When the record is invalid
     */
    private function decodeQuestionRecord(DecodingContext $decodingContext): Question
    {
        /** @var \LibDNS\Records\Types\DomainName $domainName */
        $domainName = $this->typeBuilder->build(Types::DOMAIN_NAME);
        $this->decodeDomainName($decodingContext, $domainName);
        $meta = \unpack('ntype/nclass', $this->readDataFromPacket($decodingContext->getPacket(), 4));

        $question = $this->questionFactory->create($meta['type']);
        $question->setName($domainName);
        $question->setClass($meta['class']);

        return $question;
    }

    /**
     * Decode a resource record
     *
     * @param \LibDNS\Decoder\DecodingContext $decodingContext
     * @return \LibDNS\Records\Resource
     * @throws \UnexpectedValueException When the record is invalid
     * @throws \InvalidArgumentException When a type subtype is unknown
     */
    private function decodeResourceRecord(DecodingContext $decodingContext): Resource
    {
        /** @var \LibDNS\Records\Types\DomainName $domainName */
        $domainName = $this->typeBuilder->build(Types::DOMAIN_NAME);
        $this->decodeDomainName($decodingContext, $domainName);
        $meta = \unpack('ntype/nclass/Nttl/nlength', $this->readDataFromPacket($decodingContext->getPacket(), 10));

        $resource = $this->resourceBuilder->build($meta['type']);
        $resource->setName($domainName);
        $resource->setClass($meta['class']);
        $resource->setTTL($meta['ttl']);

        $data = $resource->getData();
        $remainingLength = $meta['length'];

        $fieldDef = $index = null;
        foreach ($resource->getData()->getTypeDefinition() as $index => $fieldDef) {
            $field = $this->typeBuilder->build($fieldDef->getType());
            $remainingLength -= $this->decodeType($decodingContext, $field, $remainingLength);
            $data->setField($index, $field);
        }

        if ($fieldDef->allowsMultiple()) {
            while ($remainingLength) {
                $field = $this->typeBuilder->build($fieldDef->getType());
                $remainingLength -= $this->decodeType($decodingContext, $field, $remainingLength);
                $data->setField(++$index, $field);
            }
        }

        if ($remainingLength !== 0) {
            throw new \UnexpectedValueException('Decode error: Invalid length for record data section');
        }

        return $resource;
    }

    /**
     * Decode a Message from raw network data
     *
     * @param string $data The data string to decode
     * @return \LibDNS\Messages\Message
     * @throws \UnexpectedValueException When the packet data is invalid
     * @throws \InvalidArgumentException When a type subtype is unknown
     */
    public function decode(string $data): Message
    {
        $packet = $this->packetFactory->create($data);
        $decodingContext = $this->decodingContextFactory->create($packet);
        $message = $this->messageFactory->create();

        $this->decodeHeader($decodingContext, $message);

        $questionRecords = $message->getQuestionRecords();
        $expected = $decodingContext->getExpectedQuestionRecords();
        for ($i = 0; $i < $expected; $i++) {
            $questionRecords->add($this->decodeQuestionRecord($decodingContext));
        }

        $answerRecords = $message->getAnswerRecords();
        $expected = $decodingContext->getExpectedAnswerRecords();
        for ($i = 0; $i < $expected; $i++) {
            $answerRecords->add($this->decodeResourceRecord($decodingContext));
        }

        $authorityRecords = $message->getAuthorityRecords();
        $expected = $decodingContext->getExpectedAuthorityRecords();
        for ($i = 0; $i < $expected; $i++) {
            $authorityRecords->add($this->decodeResourceRecord($decodingContext));
        }

        $additionalRecords = $message->getAdditionalRecords();
        $expected = $decodingContext->getExpectedAdditionalRecords();
        for ($i = 0; $i < $expected; $i++) {
            $additionalRecords->add($this->decodeResourceRecord($decodingContext));
        }

        if (!$this->allowTrailingData && $packet->getBytesRemaining() !== 0) {
            throw new \UnexpectedValueException('Decode error: Unexpected data at end of packet');
        }

        return $message;
    }
}
<?php declare(strict_types=1);
/**
 * Creates DecodingContext objects
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Decoder
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Decoder;

use \LibDNS\Packets\Packet;
use \LibDNS\Packets\LabelRegistry;

/**
 * Creates DecodingContext objects
 *
 * @category LibDNS
 * @package Decoder
 * @author Chris Wright <https://github.com/DaveRandom>
 */
class DecodingContextFactory
{
    /**
     * Create a new DecodingContext object
     *
     * @param \LibDNS\Packets\Packet $packet The packet to be decoded
     * @return \LibDNS\Decoder\DecodingContext
     */
    public function create(Packet $packet): DecodingContext
    {
        return new DecodingContext($packet, new LabelRegistry);
    }
}
<?php declare(strict_types=1);
/**
 * Holds data associated with a decode operation
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Decoder
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Decoder;

use \LibDNS\Packets\Packet;
use \LibDNS\Packets\LabelRegistry;

/**
 * Holds data associated with a decode operation
 *
 * @category LibDNS
 * @package Decoder
 * @author Chris Wright <https://github.com/DaveRandom>
 */
class DecodingContext
{
    /**
     * @var \LibDNS\Packets\Packet
     */
    private $packet;

    /**
     * @var \LibDNS\Packets\LabelRegistry
     */
    private $labelRegistry;

    /**
     * @var int
     */
    private $expectedQuestionRecords = 0;

    /**
     * @var int
     */
    private $expectedAnswerRecords = 0;

    /**
     * @var int
     */
    private $expectedAuthorityRecords = 0;

    /**
     * @var int
     */
    private $expectedAdditionalRecords = 0;

    /**
     * Constructor
     *
     * @param \LibDNS\Packets\Packet $packet
     * @param \LibDNS\Packets\LabelRegistry $labelRegistry
     */
    public function __construct(Packet $packet, LabelRegistry $labelRegistry)
    {
        $this->packet = $packet;
        $this->labelRegistry = $labelRegistry;
    }

    /**
     * Get the packet
     *
     * @return \LibDNS\Packets\Packet
     */
    public function getPacket(): Packet
    {
        return $this->packet;
    }

    /**
     * Get the label registry
     *
     * @return \LibDNS\Packets\LabelRegistry
     */
    public function getLabelRegistry(): LabelRegistry
    {
        return $this->labelRegistry;
    }

    /**
     * Get the number of question records expected in the message
     *
     * @return int
     */
    public function getExpectedQuestionRecords(): int
    {
        return $this->expectedQuestionRecords;
    }

    /**
     * Get the number of question records expected in the message
     *
     * @param int $expectedQuestionRecords
     */
    public function setExpectedQuestionRecords(int $expectedQuestionRecords)
    {
        $this->expectedQuestionRecords = $expectedQuestionRecords;
    }

    /**
     * Get the number of answer records expected in the message
     *
     * @return int
     */
    public function getExpectedAnswerRecords(): int
    {
        return $this->expectedAnswerRecords;
    }

    /**
     * Set the number of answer records expected in the message
     *
     * @param int $expectedAnswerRecords
     */
    public function setExpectedAnswerRecords(int $expectedAnswerRecords)
    {
        $this->expectedAnswerRecords = $expectedAnswerRecords;
    }

    /**
     * Get the number of authority records expected in the message
     *
     * @return int
     */
    public function getExpectedAuthorityRecords(): int
    {
        return $this->expectedAuthorityRecords;
    }

    /**
     * Set the number of authority records expected in the message
     *
     * @param int $expectedAuthorityRecords
     */
    public function setExpectedAuthorityRecords(int $expectedAuthorityRecords)
    {
        $this->expectedAuthorityRecords = $expectedAuthorityRecords;
    }

    /**
     * Get the number of additional records expected in the message
     *
     * @return int
     */
    public function getExpectedAdditionalRecords(): int
    {
        return $this->expectedAdditionalRecords;
    }

    /**
     * Set the number of additional records expected in the message
     *
     * @param int $expectedAdditionalRecords
     */
    public function setExpectedAdditionalRecords(int $expectedAdditionalRecords)
    {
        $this->expectedAdditionalRecords = $expectedAdditionalRecords;
    }
}
<?php declare(strict_types=1);
/**
 * Creates Decoder objects
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Decoder
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS\Decoder;

use \LibDNS\Packets\PacketFactory;
use \LibDNS\Messages\MessageFactory;
use \LibDNS\Records\RecordCollectionFactory;
use \LibDNS\Records\QuestionFactory;
use \LibDNS\Records\ResourceBuilder;
use \LibDNS\Records\ResourceFactory;
use \LibDNS\Records\RDataBuilder;
use \LibDNS\Records\RDataFactory;
use \LibDNS\Records\Types\TypeBuilder;
use \LibDNS\Records\Types\TypeFactory;
use \LibDNS\Records\TypeDefinitions\TypeDefinitionManager;
use \LibDNS\Records\TypeDefinitions\TypeDefinitionFactory;
use \LibDNS\Records\TypeDefinitions\FieldDefinitionFactory;

/**
 * Creates Decoder objects
 *
 * @category LibDNS
 * @package Decoder
 * @author Chris Wright <https://github.com/DaveRandom>
 */
class DecoderFactory
{
    /**
     * Create a new Decoder object
     *
     * @param \LibDNS\Records\TypeDefinitions\TypeDefinitionManager $typeDefinitionManager
     * @param bool $allowTrailingData
     * @return Decoder
     */
    public function create(?TypeDefinitionManager $typeDefinitionManager = null, bool $allowTrailingData = true): Decoder
    {
        $typeBuilder = new TypeBuilder(new TypeFactory);

        return new Decoder(
            new PacketFactory,
            new MessageFactory(new RecordCollectionFactory),
            new QuestionFactory,
            new ResourceBuilder(
                new ResourceFactory,
                new RDataBuilder(
                    new RDataFactory,
                    $typeBuilder
                ),
                $typeDefinitionManager ?: new TypeDefinitionManager(
                    new TypeDefinitionFactory,
                    new FieldDefinitionFactory
                )
            ),
            $typeBuilder,
            new DecodingContextFactory,
            $allowTrailingData
        );
    }
}
<?php declare(strict_types=1);
/**
 * Base class for enumerations to prevent instantiation
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package LibDNS
 * @author Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */
namespace LibDNS;

/**
 * Base class for enumerations to prevent instantiation
 *
 * @category LibDNS
 * @package LibDNS
 * @author Chris Wright <https://github.com/DaveRandom>
 */
abstract class Enumeration
{
    final protected function __construct()
    {
        throw new \LogicException('Enumerations cannot be instantiated');
    }
}
<?php declare(strict_types = 1);

namespace LibDNS;

if (\function_exists('idn_to_ascii')) {
    function normalize_name(string $label): string
    {
        if (false === $result = \idn_to_ascii($label, 0, INTL_IDNA_VARIANT_UTS46)) {
            throw new \InvalidArgumentException("Label '{$label}' could not be processed for IDN");
        }

        return $result;
    }
} else {
    function normalize_name(string $label): string
    {
        if (\preg_match('/[\x80-\xff]/', $label)) {
            throw new \InvalidArgumentException(
                "Label '{$label}' contains non-ASCII characters and IDN support is not available."
                . " Verify that ext/intl is installed for IDN support."
            );
        }

        return \strtolower($label);
    }
}
{
    "name": "daverandom/libdns",
    "description": "DNS protocol implementation written in pure PHP",
    "license": "MIT",
    "keywords": ["dns"],
    "require": {
        "php": ">=7.1",
        "ext-ctype": "*"
    },
    "autoload": {
        "psr-4": {
            "LibDNS\\": "src/"
        },
        "files": ["src/functions.php"]
    },
    "support": {
        "issues": "https://github.com/DaveRandom/LibDNS/issues"
    },
    "suggest": {
        "ext-intl": "Required for IDN support"
    }
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Encoder;

use BaconQrCode\Common\BitArray;
use BaconQrCode\Common\ErrorCorrectionLevel;
use BaconQrCode\Common\Version;
use BaconQrCode\Exception\RuntimeException;
use BaconQrCode\Exception\WriterException;

/**
 * Matrix utility.
 */
final class MatrixUtil
{
    /**
     * Position detection pattern.
     */
    private const POSITION_DETECTION_PATTERN = [
        [1, 1, 1, 1, 1, 1, 1],
        [1, 0, 0, 0, 0, 0, 1],
        [1, 0, 1, 1, 1, 0, 1],
        [1, 0, 1, 1, 1, 0, 1],
        [1, 0, 1, 1, 1, 0, 1],
        [1, 0, 0, 0, 0, 0, 1],
        [1, 1, 1, 1, 1, 1, 1],
    ];

    /**
     * Position adjustment pattern.
     */
    private const POSITION_ADJUSTMENT_PATTERN = [
        [1, 1, 1, 1, 1],
        [1, 0, 0, 0, 1],
        [1, 0, 1, 0, 1],
        [1, 0, 0, 0, 1],
        [1, 1, 1, 1, 1],
    ];

    /**
     * Coordinates for position adjustment patterns for each version.
     */
    private const POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE = [
        [null, null, null, null, null, null, null], // Version 1
        [   6,   18, null, null, null, null, null], // Version 2
        [   6,   22, null, null, null, null, null], // Version 3
        [   6,   26, null, null, null, null, null], // Version 4
        [   6,   30, null, null, null, null, null], // Version 5
        [   6,   34, null, null, null, null, null], // Version 6
        [   6,   22,   38, null, null, null, null], // Version 7
        [   6,   24,   42, null, null, null, null], // Version 8
        [   6,   26,   46, null, null, null, null], // Version 9
        [   6,   28,   50, null, null, null, null], // Version 10
        [   6,   30,   54, null, null, null, null], // Version 11
        [   6,   32,   58, null, null, null, null], // Version 12
        [   6,   34,   62, null, null, null, null], // Version 13
        [   6,   26,   46,   66, null, null, null], // Version 14
        [   6,   26,   48,   70, null, null, null], // Version 15
        [   6,   26,   50,   74, null, null, null], // Version 16
        [   6,   30,   54,   78, null, null, null], // Version 17
        [   6,   30,   56,   82, null, null, null], // Version 18
        [   6,   30,   58,   86, null, null, null], // Version 19
        [   6,   34,   62,   90, null, null, null], // Version 20
        [   6,   28,   50,   72,   94, null, null], // Version 21
        [   6,   26,   50,   74,   98, null, null], // Version 22
        [   6,   30,   54,   78,  102, null, null], // Version 23
        [   6,   28,   54,   80,  106, null, null], // Version 24
        [   6,   32,   58,   84,  110, null, null], // Version 25
        [   6,   30,   58,   86,  114, null, null], // Version 26
        [   6,   34,   62,   90,  118, null, null], // Version 27
        [   6,   26,   50,   74,   98,  122, null], // Version 28
        [   6,   30,   54,   78,  102,  126, null], // Version 29
        [   6,   26,   52,   78,  104,  130, null], // Version 30
        [   6,   30,   56,   82,  108,  134, null], // Version 31
        [   6,   34,   60,   86,  112,  138, null], // Version 32
        [   6,   30,   58,   86,  114,  142, null], // Version 33
        [   6,   34,   62,   90,  118,  146, null], // Version 34
        [   6,   30,   54,   78,  102,  126,  150], // Version 35
        [   6,   24,   50,   76,  102,  128,  154], // Version 36
        [   6,   28,   54,   80,  106,  132,  158], // Version 37
        [   6,   32,   58,   84,  110,  136,  162], // Version 38
        [   6,   26,   54,   82,  110,  138,  166], // Version 39
        [   6,   30,   58,   86,  114,  142,  170], // Version 40
    ];

    /**
     * Type information coordinates.
     */
    private const TYPE_INFO_COORDINATES = [
        [8, 0],
        [8, 1],
        [8, 2],
        [8, 3],
        [8, 4],
        [8, 5],
        [8, 7],
        [8, 8],
        [7, 8],
        [5, 8],
        [4, 8],
        [3, 8],
        [2, 8],
        [1, 8],
        [0, 8],
    ];

    /**
     * Version information polynomial.
     */
    private const VERSION_INFO_POLY = 0x1f25;

    /**
     * Type information polynomial.
     */
    private const TYPE_INFO_POLY = 0x537;

    /**
     * Type information mask pattern.
     */
    private const TYPE_INFO_MASK_PATTERN = 0x5412;

    /**
     * Clears a given matrix.
     */
    public static function clearMatrix(ByteMatrix $matrix) : void
    {
        $matrix->clear(-1);
    }

    /**
     * Builds a complete matrix.
     */
    public static function buildMatrix(
        BitArray $dataBits,
        ErrorCorrectionLevel $level,
        Version $version,
        int $maskPattern,
        ByteMatrix $matrix
    ) : void {
        self::clearMatrix($matrix);
        self::embedBasicPatterns($version, $matrix);
        self::embedTypeInfo($level, $maskPattern, $matrix);
        self::maybeEmbedVersionInfo($version, $matrix);
        self::embedDataBits($dataBits, $maskPattern, $matrix);
    }

    /**
     * Removes the position detection patterns from a matrix.
     *
     * This can be useful if you need to render those patterns separately.
     */
    public static function removePositionDetectionPatterns(ByteMatrix $matrix) : void
    {
        $pdpWidth = count(self::POSITION_DETECTION_PATTERN[0]);

        self::removePositionDetectionPattern(0, 0, $matrix);
        self::removePositionDetectionPattern($matrix->getWidth() - $pdpWidth, 0, $matrix);
        self::removePositionDetectionPattern(0, $matrix->getWidth() - $pdpWidth, $matrix);
    }

    /**
     * Embeds type information into a matrix.
     */
    private static function embedTypeInfo(ErrorCorrectionLevel $level, int $maskPattern, ByteMatrix $matrix) : void
    {
        $typeInfoBits = new BitArray();
        self::makeTypeInfoBits($level, $maskPattern, $typeInfoBits);

        $typeInfoBitsSize = $typeInfoBits->getSize();

        for ($i = 0; $i < $typeInfoBitsSize; ++$i) {
            $bit = $typeInfoBits->get($typeInfoBitsSize - 1 - $i);

            $x1 = self::TYPE_INFO_COORDINATES[$i][0];
            $y1 = self::TYPE_INFO_COORDINATES[$i][1];

            $matrix->set($x1, $y1, (int) $bit);

            if ($i < 8) {
                $x2 = $matrix->getWidth() - $i - 1;
                $y2 = 8;
            } else {
                $x2 = 8;
                $y2 = $matrix->getHeight() - 7 + ($i - 8);
            }

            $matrix->set($x2, $y2, (int) $bit);
        }
    }

    /**
     * Generates type information bits and appends them to a bit array.
     *
     * @throws RuntimeException if bit array resulted in invalid size
     */
    private static function makeTypeInfoBits(ErrorCorrectionLevel $level, int $maskPattern, BitArray $bits) : void
    {
        $typeInfo = ($level->getBits() << 3) | $maskPattern;
        $bits->appendBits($typeInfo, 5);

        $bchCode = self::calculateBchCode($typeInfo, self::TYPE_INFO_POLY);
        $bits->appendBits($bchCode, 10);

        $maskBits = new BitArray();
        $maskBits->appendBits(self::TYPE_INFO_MASK_PATTERN, 15);
        $bits->xorBits($maskBits);

        if (15 !== $bits->getSize()) {
            throw new RuntimeException('Bit array resulted in invalid size: ' . $bits->getSize());
        }
    }

    /**
     * Embeds version information if required.
     */
    private static function maybeEmbedVersionInfo(Version $version, ByteMatrix $matrix) : void
    {
        if ($version->getVersionNumber() < 7) {
            return;
        }

        $versionInfoBits = new BitArray();
        self::makeVersionInfoBits($version, $versionInfoBits);

        $bitIndex = 6 * 3 - 1;

        for ($i = 0; $i < 6; ++$i) {
            for ($j = 0; $j < 3; ++$j) {
                $bit = $versionInfoBits->get($bitIndex);
                --$bitIndex;

                $matrix->set($i, $matrix->getHeight() - 11 + $j, (int) $bit);
                $matrix->set($matrix->getHeight() - 11 + $j, $i, (int) $bit);
            }
        }
    }

    /**
     * Generates version information bits and appends them to a bit array.
     *
     * @throws RuntimeException if bit array resulted in invalid size
     */
    private static function makeVersionInfoBits(Version $version, BitArray $bits) : void
    {
        $bits->appendBits($version->getVersionNumber(), 6);

        $bchCode = self::calculateBchCode($version->getVersionNumber(), self::VERSION_INFO_POLY);
        $bits->appendBits($bchCode, 12);

        if (18 !== $bits->getSize()) {
            throw new RuntimeException('Bit array resulted in invalid size: ' . $bits->getSize());
        }
    }

    /**
     * Calculates the BCH code for a value and a polynomial.
     */
    private static function calculateBchCode(int $value, int $poly) : int
    {
        $msbSetInPoly = self::findMsbSet($poly);
        $value <<= $msbSetInPoly - 1;

        while (self::findMsbSet($value) >= $msbSetInPoly) {
            $value ^= $poly << (self::findMsbSet($value) - $msbSetInPoly);
        }

        return $value;
    }

    /**
     * Finds and MSB set.
     */
    private static function findMsbSet(int $value) : int
    {
        $numDigits = 0;

        while (0 !== $value) {
            $value >>= 1;
            ++$numDigits;
        }

        return $numDigits;
    }

    /**
     * Embeds basic patterns into a matrix.
     */
    private static function embedBasicPatterns(Version $version, ByteMatrix $matrix) : void
    {
        self::embedPositionDetectionPatternsAndSeparators($matrix);
        self::embedDarkDotAtLeftBottomCorner($matrix);
        self::maybeEmbedPositionAdjustmentPatterns($version, $matrix);
        self::embedTimingPatterns($matrix);
    }

    /**
     * Embeds position detection patterns and separators into a byte matrix.
     */
    private static function embedPositionDetectionPatternsAndSeparators(ByteMatrix $matrix) : void
    {
        $pdpWidth = count(self::POSITION_DETECTION_PATTERN[0]);

        self::embedPositionDetectionPattern(0, 0, $matrix);
        self::embedPositionDetectionPattern($matrix->getWidth() - $pdpWidth, 0, $matrix);
        self::embedPositionDetectionPattern(0, $matrix->getWidth() - $pdpWidth, $matrix);

        $hspWidth = 8;

        self::embedHorizontalSeparationPattern(0, $hspWidth - 1, $matrix);
        self::embedHorizontalSeparationPattern($matrix->getWidth() - $hspWidth, $hspWidth - 1, $matrix);
        self::embedHorizontalSeparationPattern(0, $matrix->getWidth() - $hspWidth, $matrix);

        $vspSize = 7;

        self::embedVerticalSeparationPattern($vspSize, 0, $matrix);
        self::embedVerticalSeparationPattern($matrix->getHeight() - $vspSize - 1, 0, $matrix);
        self::embedVerticalSeparationPattern($vspSize, $matrix->getHeight() - $vspSize, $matrix);
    }

    /**
     * Embeds a single position detection pattern into a byte matrix.
     */
    private static function embedPositionDetectionPattern(int $xStart, int $yStart, ByteMatrix $matrix) : void
    {
        for ($y = 0; $y < 7; ++$y) {
            for ($x = 0; $x < 7; ++$x) {
                $matrix->set($xStart + $x, $yStart + $y, self::POSITION_DETECTION_PATTERN[$y][$x]);
            }
        }
    }

    private static function removePositionDetectionPattern(int $xStart, int $yStart, ByteMatrix $matrix) : void
    {
        for ($y = 0; $y < 7; ++$y) {
            for ($x = 0; $x < 7; ++$x) {
                $matrix->set($xStart + $x, $yStart + $y, 0);
            }
        }
    }

    /**
     * Embeds a single horizontal separation pattern.
     *
     * @throws RuntimeException if a byte was already set
     */
    private static function embedHorizontalSeparationPattern(int $xStart, int $yStart, ByteMatrix $matrix) : void
    {
        for ($x = 0; $x < 8; $x++) {
            if (-1 !== $matrix->get($xStart + $x, $yStart)) {
                throw new RuntimeException('Byte already set');
            }

            $matrix->set($xStart + $x, $yStart, 0);
        }
    }

    /**
     * Embeds a single vertical separation pattern.
     *
     * @throws RuntimeException if a byte was already set
     */
    private static function embedVerticalSeparationPattern(int $xStart, int $yStart, ByteMatrix $matrix) : void
    {
        for ($y = 0; $y < 7; $y++) {
            if (-1 !== $matrix->get($xStart, $yStart + $y)) {
                throw new RuntimeException('Byte already set');
            }

            $matrix->set($xStart, $yStart + $y, 0);
        }
    }

    /**
     * Embeds a dot at the left bottom corner.
     *
     * @throws RuntimeException if a byte was already set to 0
     */
    private static function embedDarkDotAtLeftBottomCorner(ByteMatrix $matrix) : void
    {
        if (0 === $matrix->get(8, $matrix->getHeight() - 8)) {
            throw new RuntimeException('Byte already set to 0');
        }

        $matrix->set(8, $matrix->getHeight() - 8, 1);
    }

    /**
     * Embeds position adjustment patterns if required.
     */
    private static function maybeEmbedPositionAdjustmentPatterns(Version $version, ByteMatrix $matrix) : void
    {
        if ($version->getVersionNumber() < 2) {
            return;
        }

        $index = $version->getVersionNumber() - 1;

        $coordinates = self::POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE[$index];
        $numCoordinates = count($coordinates);

        for ($i = 0; $i < $numCoordinates; ++$i) {
            for ($j = 0; $j < $numCoordinates; ++$j) {
                $y = $coordinates[$i];
                $x = $coordinates[$j];

                if (null === $x || null === $y) {
                    continue;
                }

                if (-1 === $matrix->get($x, $y)) {
                    self::embedPositionAdjustmentPattern($x - 2, $y - 2, $matrix);
                }
            }
        }
    }

    /**
     * Embeds a single position adjustment pattern.
     */
    private static function embedPositionAdjustmentPattern(int $xStart, int $yStart, ByteMatrix $matrix) : void
    {
        for ($y = 0; $y < 5; $y++) {
            for ($x = 0; $x < 5; $x++) {
                $matrix->set($xStart + $x, $yStart + $y, self::POSITION_ADJUSTMENT_PATTERN[$y][$x]);
            }
        }
    }

    /**
     * Embeds timing patterns into a matrix.
     */
    private static function embedTimingPatterns(ByteMatrix $matrix) : void
    {
        $matrixWidth = $matrix->getWidth();

        for ($i = 8; $i < $matrixWidth - 8; ++$i) {
            $bit = ($i + 1) % 2;

            if (-1 === $matrix->get($i, 6)) {
                $matrix->set($i, 6, $bit);
            }

            if (-1 === $matrix->get(6, $i)) {
                $matrix->set(6, $i, $bit);
            }
        }
    }

    /**
     * Embeds "dataBits" using "getMaskPattern".
     *
     * For debugging purposes, it skips masking process if "getMaskPattern" is -1. See 8.7 of JISX0510:2004 (p.38) for
     * how to embed data bits.
     *
     * @throws WriterException if not all bits could be consumed
     */
    private static function embedDataBits(BitArray $dataBits, int $maskPattern, ByteMatrix $matrix) : void
    {
        $bitIndex = 0;
        $direction = -1;

        // Start from the right bottom cell.
        $x = $matrix->getWidth() - 1;
        $y = $matrix->getHeight() - 1;

        while ($x > 0) {
            // Skip vertical timing pattern.
            if (6 === $x) {
                --$x;
            }

            while ($y >= 0 && $y < $matrix->getHeight()) {
                for ($i = 0; $i < 2; $i++) {
                    $xx = $x - $i;

                    // Skip the cell if it's not empty.
                    if (-1 !== $matrix->get($xx, $y)) {
                        continue;
                    }

                    if ($bitIndex < $dataBits->getSize()) {
                        $bit = $dataBits->get($bitIndex);
                        ++$bitIndex;
                    } else {
                        // Padding bit. If there is no bit left, we'll fill the
                        // left cells with 0, as described in 8.4.9 of
                        // JISX0510:2004 (p. 24).
                        $bit = false;
                    }

                    // Skip masking if maskPattern is -1.
                    if (-1 !== $maskPattern && MaskUtil::getDataMaskBit($maskPattern, $xx, $y)) {
                        $bit = ! $bit;
                    }

                    $matrix->set($xx, $y, (int) $bit);
                }

                $y += $direction;
            }

            $direction  = -$direction;
            $y += $direction;
            $x -= 2;
        }

        // All bits should be consumed
        if ($dataBits->getSize() !== $bitIndex) {
            throw new WriterException('Not all bits consumed (' . $bitIndex . ' out of ' . $dataBits->getSize() .')');
        }
    }
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Encoder;

use BaconQrCode\Common\ErrorCorrectionLevel;
use BaconQrCode\Common\Mode;
use BaconQrCode\Common\Version;

/**
 * QR code.
 */
final class QrCode
{
    /**
     * Number of possible mask patterns.
     */
    public const NUM_MASK_PATTERNS = 8;

    /**
     * Mask pattern of the QR code.
     */
    private int $maskPattern = -1;

    /**
     * Matrix of the QR code.
     */
    private ByteMatrix $matrix;

    public function __construct(
        private readonly Mode                 $mode,
        private readonly ErrorCorrectionLevel $errorCorrectionLevel,
        private readonly Version              $version,
        int                                   $maskPattern,
        ByteMatrix                            $matrix
    ) {
        $this->maskPattern = $maskPattern;
        $this->matrix = $matrix;
    }

    /**
     * Gets the mode.
     */
    public function getMode() : Mode
    {
        return $this->mode;
    }

    /**
     * Gets the EC level.
     */
    public function getErrorCorrectionLevel() : ErrorCorrectionLevel
    {
        return $this->errorCorrectionLevel;
    }

    /**
     * Gets the version.
     */
    public function getVersion() : Version
    {
        return $this->version;
    }

    /**
     * Gets the mask pattern.
     */
    public function getMaskPattern() : int
    {
        return $this->maskPattern;
    }

    public function getMatrix(): ByteMatrix
    {
        return $this->matrix;
    }

    /**
     * Validates whether a mask pattern is valid.
     */
    public static function isValidMaskPattern(int $maskPattern) : bool
    {
        return $maskPattern > 0 && $maskPattern < self::NUM_MASK_PATTERNS;
    }

    /**
     * Returns a string representation of the QR code.
     */
    public function __toString() : string
    {
        $result = "<<\n"
                . ' mode: ' . $this->mode . "\n"
                . ' ecLevel: ' . $this->errorCorrectionLevel . "\n"
                . ' version: ' . $this->version . "\n"
                . ' maskPattern: ' . $this->maskPattern . "\n";

        if ($this->matrix === null) {
            $result .= " matrix: null\n";
        } else {
            $result .= " matrix:\n";
            $result .= $this->matrix;
        }

        $result .= ">>\n";

        return $result;
    }
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Encoder;

use SplFixedArray;

/**
 * Block pair.
 */
final class BlockPair
{
    /**
     * Creates a new block pair.
     *
     * @param SplFixedArray<int> $dataBytes Data bytes in the block.
     * @param SplFixedArray<int> $errorCorrectionBytes Error correction bytes in the block.
     */
    public function __construct(
        private readonly SplFixedArray $dataBytes,
        private readonly SplFixedArray $errorCorrectionBytes
    ) {
    }

    /**
     * Gets the data bytes.
     *
     * @return SplFixedArray<int>
     */
    public function getDataBytes() : SplFixedArray
    {
        return $this->dataBytes;
    }

    /**
     * Gets the error correction bytes.
     *
     * @return SplFixedArray<int>
     */
    public function getErrorCorrectionBytes() : SplFixedArray
    {
        return $this->errorCorrectionBytes;
    }
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Encoder;

use BaconQrCode\Common\BitArray;
use BaconQrCode\Common\CharacterSetEci;
use BaconQrCode\Common\ErrorCorrectionLevel;
use BaconQrCode\Common\Mode;
use BaconQrCode\Common\ReedSolomonCodec;
use BaconQrCode\Common\Version;
use BaconQrCode\Exception\WriterException;
use SplFixedArray;

/**
 * Encoder.
 */
final class Encoder
{
    /**
     * Default byte encoding.
     */
    public const DEFAULT_BYTE_MODE_ENCODING = 'ISO-8859-1';

    /** @deprecated use DEFAULT_BYTE_MODE_ENCODING */
    public const DEFAULT_BYTE_MODE_ECODING = self::DEFAULT_BYTE_MODE_ENCODING;

    /**
     * Allowed characters for the Alphanumeric Mode.
     */
    private const ALPHANUMERIC_CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:';

    /**
     * The original table is defined in the table 5 of JISX0510:2004 (p.19).
     */
    private const ALPHANUMERIC_TABLE = [
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  // 0x00-0x0f
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  // 0x10-0x1f
        36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43,  // 0x20-0x2f
        0,   1,  2,  3,  4,  5,  6,  7,  8,  9, 44, -1, -1, -1, -1, -1,  // 0x30-0x3f
        -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,  // 0x40-0x4f
        25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,  // 0x50-0x5f
    ];

    /**
     * Codec cache.
     *
     * @var array<string,ReedSolomonCodec>
     */
    private static array $codecs = [];

    /**
     * Encodes "content" with the error correction level "ecLevel".
     */
    public static function encode(
        string $content,
        ErrorCorrectionLevel $ecLevel,
        string $encoding = self::DEFAULT_BYTE_MODE_ENCODING,
        ?Version $forcedVersion = null,
        // Barcode scanner might not be able to read the encoded message of the QR code with the prefix ECI of UTF-8
        bool $prefixEci = true
    ) : QrCode {
        // Pick an encoding mode appropriate for the content. Note that this
        // will not attempt to use multiple modes / segments even if that were
        // more efficient. Would be nice.
        $mode = self::chooseMode($content, $encoding);

        // This will store the header information, like mode and length, as well
        // as "header" segments like an ECI segment.
        $headerBits = new BitArray();

        // Append ECI segment if applicable
        if ($prefixEci && Mode::BYTE() === $mode && self::DEFAULT_BYTE_MODE_ENCODING !== $encoding) {
            $eci = CharacterSetEci::getCharacterSetEciByName($encoding);

            if (null !== $eci) {
                self::appendEci($eci, $headerBits);
            }
        }

        // (With ECI in place,) Write the mode marker
        self::appendModeInfo($mode, $headerBits);

        // Collect data within the main segment, separately, to count its size
        // if needed. Don't add it to main payload yet.
        $dataBits = new BitArray();
        self::appendBytes($content, $mode, $dataBits, $encoding);

        // Hard part: need to know version to know how many bits length takes.
        // But need to know how many bits it takes to know version. First we
        // take a guess at version by assuming version will be the minimum, 1:
        $provisionalBitsNeeded = $headerBits->getSize()
            + $mode->getCharacterCountBits(Version::getVersionForNumber(1))
            + $dataBits->getSize();
        $provisionalVersion = self::chooseVersion($provisionalBitsNeeded, $ecLevel);

        // Use that guess to calculate the right version. I am still not sure
        // this works in 100% of cases.
        $bitsNeeded = $headerBits->getSize()
            + $mode->getCharacterCountBits($provisionalVersion)
            + $dataBits->getSize();
        $version = self::chooseVersion($bitsNeeded, $ecLevel);

        if (null !== $forcedVersion) {
            // Forced version check
            if ($version->getVersionNumber() <= $forcedVersion->getVersionNumber()) {
                // Calculated minimum version is same or equal as forced version
                $version = $forcedVersion;
            } else {
                throw new WriterException(
                    'Invalid version! Calculated version: '
                    . $version->getVersionNumber()
                    . ', requested version: '
                    . $forcedVersion->getVersionNumber()
                );
            }
        }

        $headerAndDataBits = new BitArray();
        $headerAndDataBits->appendBitArray($headerBits);

        // Find "length" of main segment and write it.
        $numLetters = match ($mode) {
            Mode::BYTE()                          => $dataBits->getSizeInBytes(),
            Mode::NUMERIC(), Mode::ALPHANUMERIC() => strlen($content),
            Mode::KANJI()                         => iconv_strlen($content, 'utf-8'),
        };
        self::appendLengthInfo($numLetters, $version, $mode, $headerAndDataBits);

        // Put data together into the overall payload.
        $headerAndDataBits->appendBitArray($dataBits);
        $ecBlocks = $version->getEcBlocksForLevel($ecLevel);
        $numDataBytes = $version->getTotalCodewords() - $ecBlocks->getTotalEcCodewords();

        // Terminate the bits properly.
        self::terminateBits($numDataBytes, $headerAndDataBits);

        // Interleave data bits with error correction code.
        $finalBits = self::interleaveWithEcBytes(
            $headerAndDataBits,
            $version->getTotalCodewords(),
            $numDataBytes,
            $ecBlocks->getNumBlocks()
        );

        // Choose the mask pattern.
        $dimension = $version->getDimensionForVersion();
        $matrix = new ByteMatrix($dimension, $dimension);
        $maskPattern = self::chooseMaskPattern($finalBits, $ecLevel, $version, $matrix);

        // Build the matrix.
        MatrixUtil::buildMatrix($finalBits, $ecLevel, $version, $maskPattern, $matrix);

        return new QrCode($mode, $ecLevel, $version, $maskPattern, $matrix);
    }

    /**
     * Gets the alphanumeric code for a byte.
     */
    private static function getAlphanumericCode(int $byte) : int
    {
        return self::ALPHANUMERIC_TABLE[$byte] ?? -1;
    }

    /**
     * Chooses the best mode for a given content.
     */
    private static function chooseMode(string $content, ?string $encoding = null) : Mode
    {
        if ('' === $content) {
            return Mode::BYTE();
        }

        if (null !== $encoding && 0 === strcasecmp($encoding, 'SHIFT-JIS')) {
            return self::isOnlyDoubleByteKanji($content) ? Mode::KANJI() : Mode::BYTE();
        }

        if (ctype_digit($content)) {
            return Mode::NUMERIC();
        }

        if (self::isOnlyAlphanumeric($content)) {
            return Mode::ALPHANUMERIC();
        }

        return Mode::BYTE();
    }

    /**
     * Calculates the mask penalty for a matrix.
     */
    private static function calculateMaskPenalty(ByteMatrix $matrix) : int
    {
        return (
            MaskUtil::applyMaskPenaltyRule1($matrix)
            + MaskUtil::applyMaskPenaltyRule2($matrix)
            + MaskUtil::applyMaskPenaltyRule3($matrix)
            + MaskUtil::applyMaskPenaltyRule4($matrix)
        );
    }

    /**
     * Checks if content only consists of double-byte kanji characters (or is empty).
     */
    private static function isOnlyDoubleByteKanji(string $content) : bool
    {
        $bytes = @iconv('utf-8', 'SHIFT-JIS', $content);

        if (false === $bytes) {
            return false;
        }

        $length = strlen($bytes);

        if (0 !== $length % 2) {
            return false;
        }

        for ($i = 0; $i < $length; $i += 2) {
            $byte = ord($bytes[$i]);

            if (($byte < 0x81 || $byte > 0x9f) && $byte < 0xe0 || $byte > 0xeb) {
                return false;
            }
        }

        return true;
    }

    /**
     * Checks if content only consists of alphanumeric characters (or is empty).
     */
    private static function isOnlyAlphanumeric(string $content) : bool
    {
        return strlen($content) === strspn($content, self::ALPHANUMERIC_CHARS);
    }

    /**
     * Chooses the best mask pattern for a matrix.
     */
    private static function chooseMaskPattern(
        BitArray $bits,
        ErrorCorrectionLevel $ecLevel,
        Version $version,
        ByteMatrix $matrix
    ) : int {
        $minPenalty = PHP_INT_MAX;
        $bestMaskPattern = -1;

        for ($maskPattern = 0; $maskPattern < QrCode::NUM_MASK_PATTERNS; ++$maskPattern) {
            MatrixUtil::buildMatrix($bits, $ecLevel, $version, $maskPattern, $matrix);
            $penalty = self::calculateMaskPenalty($matrix);

            if ($penalty < $minPenalty) {
                $minPenalty = $penalty;
                $bestMaskPattern = $maskPattern;
            }
        }

        return $bestMaskPattern;
    }

    /**
     * Chooses the best version for the input.
     *
     * @throws WriterException if data is too big
     */
    private static function chooseVersion(int $numInputBits, ErrorCorrectionLevel $ecLevel) : Version
    {
        for ($versionNum = 1; $versionNum <= 40; ++$versionNum) {
            $version = Version::getVersionForNumber($versionNum);
            $numBytes = $version->getTotalCodewords();

            $ecBlocks = $version->getEcBlocksForLevel($ecLevel);
            $numEcBytes = $ecBlocks->getTotalEcCodewords();

            $numDataBytes = $numBytes - $numEcBytes;
            $totalInputBytes = intdiv($numInputBits + 8, 8);

            if ($numDataBytes >= $totalInputBytes) {
                return $version;
            }
        }

        throw new WriterException('Data too big');
    }

    /**
     * Terminates the bits in a bit array.
     *
     * @throws WriterException if data bits cannot fit in the QR code
     * @throws WriterException if bits size does not equal the capacity
     */
    private static function terminateBits(int $numDataBytes, BitArray $bits) : void
    {
        $capacity = $numDataBytes << 3;

        if ($bits->getSize() > $capacity) {
            throw new WriterException('Data bits cannot fit in the QR code');
        }

        for ($i = 0; $i < 4 && $bits->getSize() < $capacity; ++$i) {
            $bits->appendBit(false);
        }

        $numBitsInLastByte = $bits->getSize() & 0x7;

        if ($numBitsInLastByte > 0) {
            for ($i = $numBitsInLastByte; $i < 8; ++$i) {
                $bits->appendBit(false);
            }
        }

        $numPaddingBytes = $numDataBytes - $bits->getSizeInBytes();

        for ($i = 0; $i < $numPaddingBytes; ++$i) {
            $bits->appendBits(0 === ($i & 0x1) ? 0xec : 0x11, 8);
        }

        if ($bits->getSize() !== $capacity) {
            throw new WriterException('Bits size does not equal capacity');
        }
    }

    /**
     * Gets number of data- and EC bytes for a block ID.
     *
     * @return int[]
     * @throws WriterException if block ID is too large
     * @throws WriterException if EC bytes mismatch
     * @throws WriterException if RS blocks mismatch
     * @throws WriterException if total bytes mismatch
     */
    private static function getNumDataBytesAndNumEcBytesForBlockId(
        int $numTotalBytes,
        int $numDataBytes,
        int $numRsBlocks,
        int $blockId
    ) : array {
        if ($blockId >= $numRsBlocks) {
            throw new WriterException('Block ID too large');
        }

        $numRsBlocksInGroup2 = $numTotalBytes % $numRsBlocks;
        $numRsBlocksInGroup1 = $numRsBlocks - $numRsBlocksInGroup2;
        $numTotalBytesInGroup1 = intdiv($numTotalBytes, $numRsBlocks);
        $numTotalBytesInGroup2 = $numTotalBytesInGroup1 + 1;
        $numDataBytesInGroup1 = intdiv($numDataBytes, $numRsBlocks);
        $numDataBytesInGroup2 = $numDataBytesInGroup1 + 1;
        $numEcBytesInGroup1 = $numTotalBytesInGroup1 - $numDataBytesInGroup1;
        $numEcBytesInGroup2 = $numTotalBytesInGroup2 - $numDataBytesInGroup2;

        if ($numEcBytesInGroup1 !== $numEcBytesInGroup2) {
            throw new WriterException('EC bytes mismatch');
        }

        if ($numRsBlocks !== $numRsBlocksInGroup1 + $numRsBlocksInGroup2) {
            throw new WriterException('RS blocks mismatch');
        }

        if ($numTotalBytes !==
            (($numDataBytesInGroup1 + $numEcBytesInGroup1) * $numRsBlocksInGroup1)
            + (($numDataBytesInGroup2 + $numEcBytesInGroup2) * $numRsBlocksInGroup2)
        ) {
            throw new WriterException('Total bytes mismatch');
        }

        if ($blockId < $numRsBlocksInGroup1) {
            return [$numDataBytesInGroup1, $numEcBytesInGroup1];
        } else {
            return [$numDataBytesInGroup2, $numEcBytesInGroup2];
        }
    }

    /**
     * Interleaves data with EC bytes.
     *
     * @throws WriterException if number of bits and data bytes does not match
     * @throws WriterException if data bytes does not match offset
     * @throws WriterException if an interleaving error occurs
     */
    private static function interleaveWithEcBytes(
        BitArray $bits,
        int $numTotalBytes,
        int $numDataBytes,
        int $numRsBlocks
    ) : BitArray {
        if ($bits->getSizeInBytes() !== $numDataBytes) {
            throw new WriterException('Number of bits and data bytes does not match');
        }

        $dataBytesOffset = 0;
        $maxNumDataBytes = 0;
        $maxNumEcBytes   = 0;

        $blocks = new SplFixedArray($numRsBlocks);

        for ($i = 0; $i < $numRsBlocks; ++$i) {
            list($numDataBytesInBlock, $numEcBytesInBlock) = self::getNumDataBytesAndNumEcBytesForBlockId(
                $numTotalBytes,
                $numDataBytes,
                $numRsBlocks,
                $i
            );

            $size = $numDataBytesInBlock;
            $dataBytes = $bits->toBytes(8 * $dataBytesOffset, $size);
            $ecBytes = self::generateEcBytes($dataBytes, $numEcBytesInBlock);
            $blocks[$i] = new BlockPair($dataBytes, $ecBytes);

            $maxNumDataBytes = max($maxNumDataBytes, $size);
            $maxNumEcBytes = max($maxNumEcBytes, count($ecBytes));
            $dataBytesOffset += $numDataBytesInBlock;
        }

        if ($numDataBytes !== $dataBytesOffset) {
            throw new WriterException('Data bytes does not match offset');
        }

        $result = new BitArray();

        for ($i = 0; $i < $maxNumDataBytes; ++$i) {
            foreach ($blocks as $block) {
                $dataBytes = $block->getDataBytes();

                if ($i < count($dataBytes)) {
                    $result->appendBits($dataBytes[$i], 8);
                }
            }
        }

        for ($i = 0; $i < $maxNumEcBytes; ++$i) {
            foreach ($blocks as $block) {
                $ecBytes = $block->getErrorCorrectionBytes();

                if ($i < count($ecBytes)) {
                    $result->appendBits($ecBytes[$i], 8);
                }
            }
        }

        if ($numTotalBytes !== $result->getSizeInBytes()) {
            throw new WriterException(
                'Interleaving error: ' . $numTotalBytes . ' and ' . $result->getSizeInBytes() . ' differ'
            );
        }

        return $result;
    }

    /**
     * Generates EC bytes for given data.
     *
     * @param  SplFixedArray<int> $dataBytes
     * @return SplFixedArray<int>
     */
    private static function generateEcBytes(SplFixedArray $dataBytes, int $numEcBytesInBlock) : SplFixedArray
    {
        $numDataBytes = count($dataBytes);
        $toEncode = new SplFixedArray($numDataBytes + $numEcBytesInBlock);

        for ($i = 0; $i < $numDataBytes; $i++) {
            $toEncode[$i] = $dataBytes[$i];
        }

        $ecBytes = new SplFixedArray($numEcBytesInBlock);
        $codec = self::getCodec($numDataBytes, $numEcBytesInBlock);
        $codec->encode($toEncode, $ecBytes);

        return $ecBytes;
    }

    /**
     * Gets an RS codec and caches it.
     */
    private static function getCodec(int $numDataBytes, int $numEcBytesInBlock) : ReedSolomonCodec
    {
        $cacheId = $numDataBytes . '-' . $numEcBytesInBlock;

        if (isset(self::$codecs[$cacheId])) {
            return self::$codecs[$cacheId];
        }

        return self::$codecs[$cacheId] = new ReedSolomonCodec(
            8,
            0x11d,
            0,
            1,
            $numEcBytesInBlock,
            255 - $numDataBytes - $numEcBytesInBlock
        );
    }

    /**
     * Appends mode information to a bit array.
     */
    private static function appendModeInfo(Mode $mode, BitArray $bits) : void
    {
        $bits->appendBits($mode->getBits(), 4);
    }

    /**
     * Appends length information to a bit array.
     *
     * @throws WriterException if num letters is bigger than expected
     */
    private static function appendLengthInfo(int $numLetters, Version $version, Mode $mode, BitArray $bits) : void
    {
        $numBits = $mode->getCharacterCountBits($version);

        if ($numLetters >= (1 << $numBits)) {
            throw new WriterException($numLetters . ' is bigger than ' . ((1 << $numBits) - 1));
        }

        $bits->appendBits($numLetters, $numBits);
    }

    /**
     * Appends bytes to a bit array in a specific mode.
     */
    private static function appendBytes(string $content, Mode $mode, BitArray $bits, string $encoding) : void
    {
        match ($mode) {
            Mode::NUMERIC()      => self::appendNumericBytes($content, $bits),
            Mode::ALPHANUMERIC() => self::appendAlphanumericBytes($content, $bits),
            Mode::BYTE()         => self::append8BitBytes($content, $bits, $encoding),
            Mode::KANJI()        => self::appendKanjiBytes($content, $bits),
        };
    }

    /**
     * Appends numeric bytes to a bit array.
     */
    private static function appendNumericBytes(string $content, BitArray $bits) : void
    {
        $length = strlen($content);
        $i = 0;

        while ($i < $length) {
            $num1 = (int) $content[$i];

            if ($i + 2 < $length) {
                // Encode three numeric letters in ten bits.
                $num2 = (int) $content[$i + 1];
                $num3 = (int) $content[$i + 2];
                $bits->appendBits($num1 * 100 + $num2 * 10 + $num3, 10);
                $i += 3;
            } elseif ($i + 1 < $length) {
                // Encode two numeric letters in seven bits.
                $num2 = (int) $content[$i + 1];
                $bits->appendBits($num1 * 10 + $num2, 7);
                $i += 2;
            } else {
                // Encode one numeric letter in four bits.
                $bits->appendBits($num1, 4);
                ++$i;
            }
        }
    }

    /**
     * Appends alpha-numeric bytes to a bit array.
     *
     * @throws WriterException if an invalid alphanumeric code was found
     */
    private static function appendAlphanumericBytes(string $content, BitArray $bits) : void
    {
        $length = strlen($content);
        $i = 0;

        while ($i < $length) {
            $code1 = self::getAlphanumericCode(ord($content[$i]));

            if (-1 === $code1) {
                throw new WriterException('Invalid alphanumeric code');
            }

            if ($i + 1 < $length) {
                $code2 = self::getAlphanumericCode(ord($content[$i + 1]));

                if (-1 === $code2) {
                    throw new WriterException('Invalid alphanumeric code');
                }

                // Encode two alphanumeric letters in 11 bits.
                $bits->appendBits($code1 * 45 + $code2, 11);
                $i += 2;
            } else {
                // Encode one alphanumeric letter in six bits.
                $bits->appendBits($code1, 6);
                ++$i;
            }
        }
    }

    /**
     * Appends regular 8-bit bytes to a bit array.
     *
     * @throws WriterException if content cannot be encoded to target encoding
     */
    private static function append8BitBytes(string $content, BitArray $bits, string $encoding) : void
    {
        $bytes = @iconv('utf-8', $encoding, $content);

        if (false === $bytes) {
            throw new WriterException('Could not encode content to ' . $encoding);
        }

        $length = strlen($bytes);

        for ($i = 0; $i < $length; $i++) {
            $bits->appendBits(ord($bytes[$i]), 8);
        }
    }

    /**
     * Appends KANJI bytes to a bit array.
     *
     * @throws WriterException if content does not seem to be encoded in SHIFT-JIS
     * @throws WriterException if an invalid byte sequence occurs
     */
    private static function appendKanjiBytes(string $content, BitArray $bits) : void
    {
        $bytes = @iconv('utf-8', 'SHIFT-JIS', $content);

        if (false === $bytes) {
            throw new WriterException('Content could not be converted to SHIFT-JIS');
        }

        if (strlen($bytes) % 2 > 0) {
            // We just do a simple length check here. The for loop will check
            // individual characters.
            throw new WriterException('Content does not seem to be encoded in SHIFT-JIS');
        }

        $length = strlen($bytes);

        for ($i = 0; $i < $length; $i += 2) {
            $byte1 = ord($bytes[$i]);
            $byte2 = ord($bytes[$i + 1]);
            $code = ($byte1 << 8) | $byte2;

            if ($code >= 0x8140 && $code <= 0x9ffc) {
                $subtracted = $code - 0x8140;
            } elseif ($code >= 0xe040 && $code <= 0xebbf) {
                $subtracted = $code - 0xc140;
            } else {
                throw new WriterException('Invalid byte sequence');
            }

            $encoded = (($subtracted >> 8) * 0xc0) + ($subtracted & 0xff);

            $bits->appendBits($encoded, 13);
        }
    }

    /**
     * Appends ECI information to a bit array.
     */
    private static function appendEci(CharacterSetEci $eci, BitArray $bits) : void
    {
        $mode = Mode::ECI();
        $bits->appendBits($mode->getBits(), 4);
        $bits->appendBits($eci->getValue(), 8);
    }
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Encoder;

/**
 * Mask utility.
 */
final class MaskUtil
{
    /**#@+
     * Penalty weights from section 6.8.2.1
     */
    public const N1 = 3;
    public const N2 = 3;
    public const N3 = 40;
    public const N4 = 10;
    /**#@-*/

    private function __construct()
    {
    }

    /**
     * Applies mask penalty rule 1 and returns the penalty.
     *
     * Finds repetitive cells with the same color and gives penalty to them.
     * Example: 00000 or 11111.
     */
    public static function applyMaskPenaltyRule1(ByteMatrix $matrix) : int
    {
        return (
            self::applyMaskPenaltyRule1Internal($matrix, true)
            + self::applyMaskPenaltyRule1Internal($matrix, false)
        );
    }

    /**
     * Applies mask penalty rule 2 and returns the penalty.
     *
     * Finds 2x2 blocks with the same color and gives penalty to them. This is
     * actually equivalent to the spec's rule, which is to find MxN blocks and
     * give a penalty proportional to (M-1)x(N-1), because this is the number of
     * 2x2 blocks inside such a block.
     */
    public static function applyMaskPenaltyRule2(ByteMatrix $matrix) : int
    {
        $penalty = 0;
        $array = $matrix->getArray();
        $width = $matrix->getWidth();
        $height = $matrix->getHeight();

        for ($y = 0; $y < $height - 1; ++$y) {
            for ($x = 0; $x < $width - 1; ++$x) {
                $value = $array[$y][$x];

                if ($value === $array[$y][$x + 1]
                    && $value === $array[$y + 1][$x]
                    && $value === $array[$y + 1][$x + 1]
                ) {
                    ++$penalty;
                }
            }
        }

        return self::N2 * $penalty;
    }

    /**
     * Applies mask penalty rule 3 and returns the penalty.
     *
     * Finds consecutive cells of 00001011101 or 10111010000, and gives penalty
     * to them. If we find patterns like 000010111010000, we give penalties
     * twice (i.e. 40 * 2).
     */
    public static function applyMaskPenaltyRule3(ByteMatrix $matrix) : int
    {
        $penalty = 0;
        $array = $matrix->getArray();
        $width = $matrix->getWidth();
        $height = $matrix->getHeight();

        for ($y = 0; $y < $height; ++$y) {
            for ($x = 0; $x < $width; ++$x) {
                if ($x + 6 < $width
                    && 1 === $array[$y][$x]
                    && 0 === $array[$y][$x + 1]
                    && 1 === $array[$y][$x + 2]
                    && 1 === $array[$y][$x + 3]
                    && 1 === $array[$y][$x + 4]
                    && 0 === $array[$y][$x + 5]
                    && 1 === $array[$y][$x + 6]
                    && (
                        (
                            $x + 10 < $width
                            && 0 === $array[$y][$x + 7]
                            && 0 === $array[$y][$x + 8]
                            && 0 === $array[$y][$x + 9]
                            && 0 === $array[$y][$x + 10]
                        )
                        || (
                            $x - 4 >= 0
                            && 0 === $array[$y][$x - 1]
                            && 0 === $array[$y][$x - 2]
                            && 0 === $array[$y][$x - 3]
                            && 0 === $array[$y][$x - 4]
                        )
                    )
                ) {
                    $penalty += self::N3;
                }

                if ($y + 6 < $height
                    && 1 === $array[$y][$x]
                    && 0 === $array[$y + 1][$x]
                    && 1 === $array[$y + 2][$x]
                    && 1 === $array[$y + 3][$x]
                    && 1 === $array[$y + 4][$x]
                    && 0 === $array[$y + 5][$x]
                    && 1 === $array[$y + 6][$x]
                    && (
                        (
                            $y + 10 < $height
                            && 0 === $array[$y + 7][$x]
                            && 0 === $array[$y + 8][$x]
                            && 0 === $array[$y + 9][$x]
                            && 0 === $array[$y + 10][$x]
                        )
                        || (
                            $y - 4 >= 0
                            && 0 === $array[$y - 1][$x]
                            && 0 === $array[$y - 2][$x]
                            && 0 === $array[$y - 3][$x]
                            && 0 === $array[$y - 4][$x]
                        )
                    )
                ) {
                    $penalty += self::N3;
                }
            }
        }

        return $penalty;
    }

    /**
     * Applies mask penalty rule 4 and returns the penalty.
     *
     * Calculates the ratio of dark cells and gives penalty if the ratio is far
     * from 50%. It gives 10 penalty for 5% distance.
     */
    public static function applyMaskPenaltyRule4(ByteMatrix $matrix) : int
    {
        $numDarkCells = 0;

        $array = $matrix->getArray();
        $width = $matrix->getWidth();
        $height = $matrix->getHeight();

        for ($y = 0; $y < $height; ++$y) {
            $arrayY = $array[$y];

            for ($x = 0; $x < $width; ++$x) {
                if (1 === $arrayY[$x]) {
                    ++$numDarkCells;
                }
            }
        }

        $numTotalCells = $height * $width;
        $darkRatio = $numDarkCells / $numTotalCells;
        $fixedPercentVariances = (int) floor(abs($darkRatio - 0.5) * 20);

        return $fixedPercentVariances * self::N4;
    }

    /**
     * Returns the mask bit for "getMaskPattern" at "x" and "y".
     *
     * See 8.8 of JISX0510:2004 for mask pattern conditions.
     */
    public static function getDataMaskBit(int $maskPattern, int $x, int $y) : bool
    {
        return 0 === match ($maskPattern) {
            0 => ($x + $y) % 2,
            1 => $y % 2,
            2 => $x % 3,
            3 => ($x + $y) % 3,
            4 => (intdiv($y, 2) + intdiv($x, 3)) % 2,
            5 => (($x * $y) % 2) + (($x * $y) % 3),
            6 => ((($x * $y) % 2) + (($x * $y) % 3)) % 2,
            7 => ((($x + $y) % 2) + (($x * $y) % 3)) % 2,
        };
    }

    /**
     * Helper function for applyMaskPenaltyRule1.
     *
     * We need this for doing this calculation in both vertical and horizontal
     * orders respectively.
     */
    private static function applyMaskPenaltyRule1Internal(ByteMatrix $matrix, bool $isHorizontal) : int
    {
        $penalty = 0;
        $iLimit = $isHorizontal ? $matrix->getHeight() : $matrix->getWidth();
        $jLimit = $isHorizontal ? $matrix->getWidth() : $matrix->getHeight();
        $array = $matrix->getArray();

        for ($i = 0; $i < $iLimit; ++$i) {
            $numSameBitCells = 0;
            $prevBit = -1;

            for ($j = 0; $j < $jLimit; $j++) {
                $bit = $isHorizontal ? $array[$i][$j] : $array[$j][$i];

                if ($bit === $prevBit) {
                    ++$numSameBitCells;
                } else {
                    if ($numSameBitCells >= 5) {
                        $penalty += self::N1 + ($numSameBitCells - 5);
                    }

                    $numSameBitCells = 1;
                    $prevBit = $bit;
                }
            }

            if ($numSameBitCells >= 5) {
                $penalty += self::N1 + ($numSameBitCells - 5);
            }
        }

        return $penalty;
    }
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Encoder;

use SplFixedArray;
use Traversable;

/**
 * Byte matrix.
 */
final class ByteMatrix
{
    /**
     * Bytes in the matrix, represented as array.
     *
     * @var SplFixedArray<SplFixedArray<int>>
     */
    private SplFixedArray $bytes;

    public function __construct(private readonly int $width, private readonly int $height)
    {
        $this->bytes = new SplFixedArray($height);

        for ($y = 0; $y < $height; ++$y) {
            $this->bytes[$y] = SplFixedArray::fromArray(array_fill(0, $width, 0));
        }
    }

    /**
     * Gets the width of the matrix.
     */
    public function getWidth() : int
    {
        return $this->width;
    }

    /**
     * Gets the height of the matrix.
     */
    public function getHeight() : int
    {
        return $this->height;
    }

    /**
     * Gets the internal representation of the matrix.
     *
     * @return SplFixedArray<SplFixedArray<int>>
     */
    public function getArray() : SplFixedArray
    {
        return $this->bytes;
    }

    /**
     * @return Traversable<int>
     */
    public function getBytes() : Traversable
    {
        foreach ($this->bytes as $row) {
            foreach ($row as $byte) {
                yield $byte;
            }
        }
    }

    /**
     * Gets the byte for a specific position.
     */
    public function get(int $x, int $y) : int
    {
        return $this->bytes[$y][$x];
    }

    /**
     * Sets the byte for a specific position.
     */
    public function set(int $x, int $y, int $value) : void
    {
        $this->bytes[$y][$x] = $value;
    }

    /**
     * Clears the matrix with a specific value.
     */
    public function clear(int $value) : void
    {
        for ($y = 0; $y < $this->height; ++$y) {
            for ($x = 0; $x < $this->width; ++$x) {
                $this->bytes[$y][$x] = $value;
            }
        }
    }

    public function __clone()
    {
        $this->bytes = clone $this->bytes;

        foreach ($this->bytes as $index => $row) {
            $this->bytes[$index] = clone $row;
        }
    }

    /**
     * Returns a string representation of the matrix.
     */
    public function __toString() : string
    {
        $result = '';

        for ($y = 0; $y < $this->height; $y++) {
            for ($x = 0; $x < $this->width; $x++) {
                switch ($this->bytes[$y][$x]) {
                    case 0:
                        $result .= ' 0';
                        break;

                    case 1:
                        $result .= ' 1';
                        break;

                    default:
                        $result .= '  ';
                        break;
                }
            }

            $result .= "\n";
        }

        return $result;
    }
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Exception;

final class OutOfBoundsException extends \OutOfBoundsException implements ExceptionInterface
{
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Exception;

use Throwable;

interface ExceptionInterface extends Throwable
{
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Exception;

final class UnexpectedValueException extends \UnexpectedValueException implements ExceptionInterface
{
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Exception;

final class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
{
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Exception;

final class WriterException extends \RuntimeException implements ExceptionInterface
{
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Exception;

final class RuntimeException extends \RuntimeException implements ExceptionInterface
{
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Common;

use BaconQrCode\Exception\InvalidArgumentException;
use SplFixedArray;

/**
 * Bit matrix.
 *
 * Represents a 2D matrix of bits. In function arguments below, and throughout
 * the common module, x is the column position, and y is the row position. The
 * ordering is always x, y. The origin is at the top-left.
 */
class BitMatrix
{
    /**
     * Width of the bit matrix.
     */
    private int $width;

    /**
     * Height of the bit matrix.
     */
    private ?int $height;

    /**
     * Size in bits of each individual row.
     */
    private int $rowSize;

    /**
     * Bits representation.
     *
     * @var SplFixedArray<int>
     */
    private SplFixedArray $bits;

    /**
     * @throws InvalidArgumentException if a dimension is smaller than zero
     */
    public function __construct(int $width, ?int $height = null)
    {
        if (null === $height) {
            $height = $width;
        }

        if ($width < 1 || $height < 1) {
            throw new InvalidArgumentException('Both dimensions must be greater than zero');
        }

        $this->width = $width;
        $this->height = $height;
        $this->rowSize = ($width + 31) >> 5;
        $this->bits = SplFixedArray::fromArray(array_fill(0, $this->rowSize * $height, 0));
    }

    /**
     * Gets the requested bit, where true means black.
     */
    public function get(int $x, int $y) : bool
    {
        $offset = $y * $this->rowSize + ($x >> 5);
        return 0 !== (BitUtils::unsignedRightShift($this->bits[$offset], ($x & 0x1f)) & 1);
    }

    /**
     * Sets the given bit to true.
     */
    public function set(int $x, int $y) : void
    {
        $offset = $y * $this->rowSize + ($x >> 5);
        $this->bits[$offset] = $this->bits[$offset] | (1 << ($x & 0x1f));
    }

    /**
     * Flips the given bit.
     */
    public function flip(int $x, int $y) : void
    {
        $offset = $y * $this->rowSize + ($x >> 5);
        $this->bits[$offset] = $this->bits[$offset] ^ (1 << ($x & 0x1f));
    }

    /**
     * Clears all bits (set to false).
     */
    public function clear() : void
    {
        $max = count($this->bits);

        for ($i = 0; $i < $max; ++$i) {
            $this->bits[$i] = 0;
        }
    }

    /**
     * Sets a square region of the bit matrix to true.
     *
     * @throws InvalidArgumentException if left or top are negative
     * @throws InvalidArgumentException if width or height are smaller than 1
     * @throws InvalidArgumentException if region does not fit into the matix
     */
    public function setRegion(int $left, int $top, int $width, int $height) : void
    {
        if ($top < 0 || $left < 0) {
            throw new InvalidArgumentException('Left and top must be non-negative');
        }

        if ($height < 1 || $width < 1) {
            throw new InvalidArgumentException('Width and height must be at least 1');
        }

        $right = $left + $width;
        $bottom = $top + $height;

        if ($bottom > $this->height || $right > $this->width) {
            throw new InvalidArgumentException('The region must fit inside the matrix');
        }

        for ($y = $top; $y < $bottom; ++$y) {
            $offset = $y * $this->rowSize;

            for ($x = $left; $x < $right; ++$x) {
                $index = $offset + ($x >> 5);
                $this->bits[$index] = $this->bits[$index] | (1 << ($x & 0x1f));
            }
        }
    }

    /**
     * A fast method to retrieve one row of data from the matrix as a BitArray.
     */
    public function getRow(int $y, ?BitArray $row = null) : BitArray
    {
        if (null === $row || $row->getSize() < $this->width) {
            $row = new BitArray($this->width);
        }

        $offset = $y * $this->rowSize;

        for ($x = 0; $x < $this->rowSize; ++$x) {
            $row->setBulk($x << 5, $this->bits[$offset + $x]);
        }

        return $row;
    }

    /**
     * Sets a row of data from a BitArray.
     */
    public function setRow(int $y, BitArray $row) : void
    {
        $bits = $row->getBitArray();

        for ($i = 0; $i < $this->rowSize; ++$i) {
            $this->bits[$y * $this->rowSize + $i] = $bits[$i];
        }
    }

    /**
     * This is useful in detecting the enclosing rectangle of a 'pure' barcode.
     *
     * @return int[]|null
     */
    public function getEnclosingRectangle() : ?array
    {
        $left = $this->width;
        $top = $this->height;
        $right = -1;
        $bottom = -1;

        for ($y = 0; $y < $this->height; ++$y) {
            for ($x32 = 0; $x32 < $this->rowSize; ++$x32) {
                $bits = $this->bits[$y * $this->rowSize + $x32];

                if (0 !== $bits) {
                    if ($y < $top) {
                        $top = $y;
                    }

                    if ($y > $bottom) {
                        $bottom = $y;
                    }

                    if ($x32 * 32 < $left) {
                        $bit = 0;

                        while (($bits << (31 - $bit)) === 0) {
                            $bit++;
                        }

                        if (($x32 * 32 + $bit) < $left) {
                            $left = $x32 * 32 + $bit;
                        }
                    }
                }

                if ($x32 * 32 + 31 > $right) {
                    $bit = 31;

                    while (0 === BitUtils::unsignedRightShift($bits, $bit)) {
                        --$bit;
                    }

                    if (($x32 * 32 + $bit) > $right) {
                        $right = $x32 * 32 + $bit;
                    }
                }
            }
        }

        $width = $right - $left;
        $height = $bottom - $top;

        if ($width < 0 || $height < 0) {
            return null;
        }

        return [$left, $top, $width, $height];
    }

    /**
     * Gets the most top left set bit.
     *
     * This is useful in detecting a corner of a 'pure' barcode.
     *
     * @return int[]|null
     */
    public function getTopLeftOnBit() : ?array
    {
        $bitsOffset = 0;

        while ($bitsOffset < count($this->bits) && 0 === $this->bits[$bitsOffset]) {
            ++$bitsOffset;
        }

        if (count($this->bits) === $bitsOffset) {
            return null;
        }

        $x = intdiv($bitsOffset, $this->rowSize);
        $y = ($bitsOffset % $this->rowSize) << 5;

        $bits = $this->bits[$bitsOffset];
        $bit = 0;

        while (0 === ($bits << (31 - $bit))) {
            ++$bit;
        }

        $x += $bit;

        return [$x, $y];
    }

    /**
     * Gets the most bottom right set bit.
     *
     * This is useful in detecting a corner of a 'pure' barcode.
     *
     * @return int[]|null
     */
    public function getBottomRightOnBit() : ?array
    {
        $bitsOffset = count($this->bits) - 1;

        while ($bitsOffset >= 0 && 0 === $this->bits[$bitsOffset]) {
            --$bitsOffset;
        }

        if ($bitsOffset < 0) {
            return null;
        }

        $x = intdiv($bitsOffset, $this->rowSize);
        $y = ($bitsOffset % $this->rowSize) << 5;

        $bits = $this->bits[$bitsOffset];
        $bit  = 0;

        while (0 === BitUtils::unsignedRightShift($bits, $bit)) {
            --$bit;
        }

        $x += $bit;

        return [$x, $y];
    }

    /**
     * Gets the width of the matrix,
     */
    public function getWidth() : int
    {
        return $this->width;
    }

    /**
     * Gets the height of the matrix.
     */
    public function getHeight() : int
    {
        return $this->height;
    }
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Common;

use BaconQrCode\Exception\OutOfBoundsException;
use DASPRiD\Enum\AbstractEnum;

/**
 * Enum representing the four error correction levels.
 *
 * @method static self L() ~7% correction
 * @method static self M() ~15% correction
 * @method static self Q() ~25% correction
 * @method static self H() ~30% correction
 */
final class ErrorCorrectionLevel extends AbstractEnum
{
    protected const L = [0x01];
    protected const M = [0x00];
    protected const Q = [0x03];
    protected const H = [0x02];

    protected function __construct(private readonly int $bits)
    {
    }

    /**
     * @throws OutOfBoundsException if number of bits is invalid
     */
    public static function forBits(int $bits) : self
    {
        switch ($bits) {
            case 0:
                return self::M();

            case 1:
                return self::L();

            case 2:
                return self::H();

            case 3:
                return self::Q();
        }

        throw new OutOfBoundsException('Invalid number of bits');
    }

    /**
     * Returns the two bits used to encode this error correction level.
     */
    public function getBits() : int
    {
        return $this->bits;
    }
}
<?php
/**
 * BaconQrCode
 *
 * @link      http://github.com/Bacon/BaconQrCode For the canonical source repository
 * @copyright 2013 Ben 'DASPRiD' Scholzen
 * @license   http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
 */

namespace BaconQrCode\Common;

/**
 * Encapsulates a QR Code's format information, including the data mask used and error correction level.
 */
class FormatInformation
{
    /**
     * Mask for format information.
     */
    private const FORMAT_INFO_MASK_QR = 0x5412;

    /**
     * Lookup table for decoding format information.
     *
     * See ISO 18004:2006, Annex C, Table C.1
     */
    private const FORMAT_INFO_DECODE_LOOKUP = [
        [0x5412, 0x00],
        [0x5125, 0x01],
        [0x5e7c, 0x02],
        [0x5b4b, 0x03],
        [0x45f9, 0x04],
        [0x40ce, 0x05],
        [0x4f97, 0x06],
        [0x4aa0, 0x07],
        [0x77c4, 0x08],
        [0x72f3, 0x09],
        [0x7daa, 0x0a],
        [0x789d, 0x0b],
        [0x662f, 0x0c],
        [0x6318, 0x0d],
        [0x6c41, 0x0e],
        [0x6976, 0x0f],
        [0x1689, 0x10],
        [0x13be, 0x11],
        [0x1ce7, 0x12],
        [0x19d0, 0x13],
        [0x0762, 0x14],
        [0x0255, 0x15],
        [0x0d0c, 0x16],
        [0x083b, 0x17],
        [0x355f, 0x18],
        [0x3068, 0x19],
        [0x3f31, 0x1a],
        [0x3a06, 0x1b],
        [0x24b4, 0x1c],
        [0x2183, 0x1d],
        [0x2eda, 0x1e],
        [0x2bed, 0x1f],
    ];

    /**
     * Offset i holds the number of 1 bits in the binary representation of i.
     *
     * @var int[]
     */
    private const BITS_SET_IN_HALF_BYTE = [0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4];

    /**
     * Error correction level.
     */
    private ErrorCorrectionLevel $ecLevel;

    private int $dataMask;

    protected function __construct(int $formatInfo)
    {
        $this->ecLevel = ErrorCorrectionLevel::forBits(($formatInfo >> 3) & 0x3);
        $this->dataMask = $formatInfo & 0x7;
    }

    /**
     * Checks how many bits are different between two integers.
     */
    public static function numBitsDiffering(int $a, int $b) : int
    {
        $a ^= $b;

        return (
            self::BITS_SET_IN_HALF_BYTE[$a & 0xf]
            + self::BITS_SET_IN_HALF_BYTE[(BitUtils::unsignedRightShift($a, 4) & 0xf)]
            + self::BITS_SET_IN_HALF_BYTE[(BitUtils::unsignedRightShift($a, 8) & 0xf)]
            + self::BITS_SET_IN_HALF_BYTE[(BitUtils::unsignedRightShift($a, 12) & 0xf)]
            + self::BITS_SET_IN_HALF_BYTE[(BitUtils::unsignedRightShift($a, 16) & 0xf)]
            + self::BITS_SET_IN_HALF_BYTE[(BitUtils::unsignedRightShift($a, 20) & 0xf)]
            + self::BITS_SET_IN_HALF_BYTE[(BitUtils::unsignedRightShift($a, 24) & 0xf)]
            + self::BITS_SET_IN_HALF_BYTE[(BitUtils::unsignedRightShift($a, 28) & 0xf)]
        );
    }

    /**
     * Decodes format information.
     */
    public static function decodeFormatInformation(int $maskedFormatInfo1, int $maskedFormatInfo2) : ?self
    {
        $formatInfo = self::doDecodeFormatInformation($maskedFormatInfo1, $maskedFormatInfo2);

        if (null !== $formatInfo) {
            return $formatInfo;
        }

        // Should return null, but, some QR codes apparently do not mask this info. Try again by actually masking the
        // pattern first.
        return self::doDecodeFormatInformation(
            $maskedFormatInfo1 ^ self::FORMAT_INFO_MASK_QR,
            $maskedFormatInfo2 ^ self::FORMAT_INFO_MASK_QR
        );
    }

    /**
     * Internal method for decoding format information.
     */
    private static function doDecodeFormatInformation(int $maskedFormatInfo1, int $maskedFormatInfo2) : ?self
    {
        $bestDifference = PHP_INT_MAX;
        $bestFormatInfo = 0;

        foreach (self::FORMAT_INFO_DECODE_LOOKUP as $decodeInfo) {
            $targetInfo = $decodeInfo[0];

            if ($targetInfo === $maskedFormatInfo1 || $targetInfo === $maskedFormatInfo2) {
                // Found an exact match
                return new self($decodeInfo[1]);
            }

            $bitsDifference = self::numBitsDiffering($maskedFormatInfo1, $targetInfo);

            if ($bitsDifference < $bestDifference) {
                $bestFormatInfo = $decodeInfo[1];
                $bestDifference = $bitsDifference;
            }

            if ($maskedFormatInfo1 !== $maskedFormatInfo2) {
                // Also try the other option
                $bitsDifference = self::numBitsDiffering($maskedFormatInfo2, $targetInfo);

                if ($bitsDifference < $bestDifference) {
                    $bestFormatInfo = $decodeInfo[1];
                    $bestDifference = $bitsDifference;
                }
            }
        }

        // Hamming distance of the 32 masked codes is 7, by construction, so <= 3 bits differing means we found a match.
        if ($bestDifference <= 3) {
            return new self($bestFormatInfo);
        }

        return null;
    }

    /**
     * Returns the error correction level.
     */
    public function getErrorCorrectionLevel() : ErrorCorrectionLevel
    {
        return $this->ecLevel;
    }

    /**
     * Returns the data mask.
     */
    public function getDataMask() : int
    {
        return $this->dataMask;
    }

    /**
     * Hashes the code of the EC level.
     */
    public function hashCode() : int
    {
        return ($this->ecLevel->getBits() << 3) | $this->dataMask;
    }

    /**
     * Verifies if this instance equals another one.
     */
    public function equals(self $other) : bool
    {
        return (
            $this->ecLevel === $other->ecLevel
            && $this->dataMask === $other->dataMask
        );
    }
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Common;

use BaconQrCode\Exception\InvalidArgumentException;
use DASPRiD\Enum\AbstractEnum;

/**
 * Encapsulates a Character Set ECI, according to "Extended Channel Interpretations" 5.3.1.1 of ISO 18004.
 *
 * @method static self CP437()
 * @method static self ISO8859_1()
 * @method static self ISO8859_2()
 * @method static self ISO8859_3()
 * @method static self ISO8859_4()
 * @method static self ISO8859_5()
 * @method static self ISO8859_6()
 * @method static self ISO8859_7()
 * @method static self ISO8859_8()
 * @method static self ISO8859_9()
 * @method static self ISO8859_10()
 * @method static self ISO8859_11()
 * @method static self ISO8859_12()
 * @method static self ISO8859_13()
 * @method static self ISO8859_14()
 * @method static self ISO8859_15()
 * @method static self ISO8859_16()
 * @method static self SJIS()
 * @method static self CP1250()
 * @method static self CP1251()
 * @method static self CP1252()
 * @method static self CP1256()
 * @method static self UNICODE_BIG_UNMARKED()
 * @method static self UTF8()
 * @method static self ASCII()
 * @method static self BIG5()
 * @method static self GB18030()
 * @method static self EUC_KR()
 */
final class CharacterSetEci extends AbstractEnum
{
    protected const CP437 = [[0, 2]];
    protected const ISO8859_1 = [[1, 3], 'ISO-8859-1'];
    protected const ISO8859_2 = [[4], 'ISO-8859-2'];
    protected const ISO8859_3 = [[5], 'ISO-8859-3'];
    protected const ISO8859_4 = [[6], 'ISO-8859-4'];
    protected const ISO8859_5 = [[7], 'ISO-8859-5'];
    protected const ISO8859_6 = [[8], 'ISO-8859-6'];
    protected const ISO8859_7 = [[9], 'ISO-8859-7'];
    protected const ISO8859_8 = [[10], 'ISO-8859-8'];
    protected const ISO8859_9 = [[11], 'ISO-8859-9'];
    protected const ISO8859_10 = [[12], 'ISO-8859-10'];
    protected const ISO8859_11 = [[13], 'ISO-8859-11'];
    protected const ISO8859_12 = [[14], 'ISO-8859-12'];
    protected const ISO8859_13 = [[15], 'ISO-8859-13'];
    protected const ISO8859_14 = [[16], 'ISO-8859-14'];
    protected const ISO8859_15 = [[17], 'ISO-8859-15'];
    protected const ISO8859_16 = [[18], 'ISO-8859-16'];
    protected const SJIS = [[20], 'Shift_JIS'];
    protected const CP1250 = [[21], 'windows-1250'];
    protected const CP1251 = [[22], 'windows-1251'];
    protected const CP1252 = [[23], 'windows-1252'];
    protected const CP1256 = [[24], 'windows-1256'];
    protected const UNICODE_BIG_UNMARKED = [[25], 'UTF-16BE', 'UnicodeBig'];
    protected const UTF8 = [[26], 'UTF-8'];
    protected const ASCII = [[27, 170], 'US-ASCII'];
    protected const BIG5 = [[28]];
    protected const GB18030 = [[29], 'GB2312', 'EUC_CN', 'GBK'];
    protected const EUC_KR = [[30], 'EUC-KR'];

    /**
     * @var string[]
     */
    private array $otherEncodingNames;

    /**
     * @var array<int, self>|null
     */
    private static ?array $valueToEci;

    /**
     * @var array<string, self>|null
     */
    private static ?array $nameToEci = null;

    /**
     * @param int[] $values
     */
    public function __construct(private readonly array $values, string ...$otherEncodingNames)
    {
        $this->otherEncodingNames = $otherEncodingNames;
    }

    /**
     * Returns the primary value.
     */
    public function getValue() : int
    {
        return $this->values[0];
    }

    /**
     * Gets character set ECI by value.
     *
     * Returns the representing ECI of a given value, or null if it is legal but unsupported.
     *
     * @throws InvalidArgumentException if value is not between 0 and 900
     */
    public static function getCharacterSetEciByValue(int $value) : ?self
    {
        if ($value < 0 || $value >= 900) {
            throw new InvalidArgumentException('Value must be between 0 and 900');
        }

        $valueToEci = self::valueToEci();

        if (! array_key_exists($value, $valueToEci)) {
            return null;
        }

        return $valueToEci[$value];
    }

    /**
     * Returns character set ECI by name.
     *
     * Returns the representing ECI of a given name, or null if it is legal but unsupported
     */
    public static function getCharacterSetEciByName(string $name) : ?self
    {
        $nameToEci = self::nameToEci();
        $name = strtolower($name);

        if (! array_key_exists($name, $nameToEci)) {
            return null;
        }

        return $nameToEci[$name];
    }

    private static function valueToEci() : array
    {
        if (null !== self::$valueToEci) {
            return self::$valueToEci;
        }

        self::$valueToEci = [];

        foreach (self::values() as $eci) {
            foreach ($eci->values as $value) {
                self::$valueToEci[$value] = $eci;
            }
        }

        return self::$valueToEci;
    }

    private static function nameToEci() : array
    {
        if (null !== self::$nameToEci) {
            return self::$nameToEci;
        }

        self::$nameToEci = [];

        foreach (self::values() as $eci) {
            self::$nameToEci[strtolower($eci->name())] = $eci;

            foreach ($eci->otherEncodingNames as $name) {
                self::$nameToEci[strtolower($name)] = $eci;
            }
        }

        return self::$nameToEci;
    }
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Common;

use BaconQrCode\Exception\InvalidArgumentException;
use SplFixedArray;

/**
 * Version representation.
 */
final class Version
{
    private const VERSION_DECODE_INFO = [
        0x07c94,
        0x085bc,
        0x09a99,
        0x0a4d3,
        0x0bbf6,
        0x0c762,
        0x0d847,
        0x0e60d,
        0x0f928,
        0x10b78,
        0x1145d,
        0x12a17,
        0x13532,
        0x149a6,
        0x15683,
        0x168c9,
        0x177ec,
        0x18ec4,
        0x191e1,
        0x1afab,
        0x1b08e,
        0x1cc1a,
        0x1d33f,
        0x1ed75,
        0x1f250,
        0x209d5,
        0x216f0,
        0x228ba,
        0x2379f,
        0x24b0b,
        0x2542e,
        0x26a64,
        0x27541,
        0x28c69,
    ];

    /**
     * Version number of this version.
     */
    private int $versionNumber;

    /**
     * Alignment pattern centers.
     *
     * @var SplFixedArray|array
     */
    private SplFixedArray|array $alignmentPatternCenters;

    /**
     * Error correction blocks.
     *
     * @var EcBlocks[]
     */
    private array $ecBlocks;

    /**
     * Total number of codewords.
     */
    private null|int|float $totalCodewords;

    /**
     * Cached version instances.
     *
     * @var array<int, self>|null
     */
    private static ?array $versions = null;

    /**
     * @param int[] $alignmentPatternCenters
     */
    private function __construct(
        int $versionNumber,
        array $alignmentPatternCenters,
        EcBlocks ...$ecBlocks
    ) {
        $this->versionNumber = $versionNumber;
        $this->alignmentPatternCenters = $alignmentPatternCenters;
        $this->ecBlocks = $ecBlocks;

        $totalCodewords = 0;
        $ecCodewords = $ecBlocks[0]->getEcCodewordsPerBlock();

        foreach ($ecBlocks[0]->getEcBlocks() as $ecBlock) {
            $totalCodewords += $ecBlock->getCount() * ($ecBlock->getDataCodewords() + $ecCodewords);
        }

        $this->totalCodewords = $totalCodewords;
    }

    /**
     * Returns the version number.
     */
    public function getVersionNumber() : int
    {
        return $this->versionNumber;
    }

    /**
     * Returns the alignment pattern centers.
     *
     * @return int[]
     */
    public function getAlignmentPatternCenters() : array
    {
        return $this->alignmentPatternCenters;
    }

    /**
     * Returns the total number of codewords.
     */
    public function getTotalCodewords() : int
    {
        return $this->totalCodewords;
    }

    /**
     * Calculates the dimension for the current version.
     */
    public function getDimensionForVersion() : int
    {
        return 17 + 4 * $this->versionNumber;
    }

    /**
     * Returns the number of EC blocks for a specific EC level.
     */
    public function getEcBlocksForLevel(ErrorCorrectionLevel $ecLevel) : EcBlocks
    {
        return $this->ecBlocks[$ecLevel->ordinal()];
    }

    /**
     * Gets a provisional version number for a specific dimension.
     *
     * @throws InvalidArgumentException if dimension is not 1 mod 4
     */
    public static function getProvisionalVersionForDimension(int $dimension) : self
    {
        if (1 !== $dimension % 4) {
            throw new InvalidArgumentException('Dimension is not 1 mod 4');
        }

        return self::getVersionForNumber(intdiv($dimension - 17, 4));
    }

    /**
     * Gets a version instance for a specific version number.
     *
     * @throws InvalidArgumentException if version number is out of range
     */
    public static function getVersionForNumber(int $versionNumber) : self
    {
        if ($versionNumber < 1 || $versionNumber > 40) {
            throw new InvalidArgumentException('Version number must be between 1 and 40');
        }

        return self::versions()[$versionNumber - 1];
    }

    /**
     * Decodes version information from an integer and returns the version.
     */
    public static function decodeVersionInformation(int $versionBits) : ?self
    {
        $bestDifference = PHP_INT_MAX;
        $bestVersion = 0;

        foreach (self::VERSION_DECODE_INFO as $i => $targetVersion) {
            if ($targetVersion === $versionBits) {
                return self::getVersionForNumber($i + 7);
            }

            $bitsDifference = FormatInformation::numBitsDiffering($versionBits, $targetVersion);

            if ($bitsDifference < $bestDifference) {
                $bestVersion = $i + 7;
                $bestDifference = $bitsDifference;
            }
        }

        if ($bestDifference <= 3) {
            return self::getVersionForNumber($bestVersion);
        }

        return null;
    }

    /**
     * Builds the function pattern for the current version.
     */
    public function buildFunctionPattern() : BitMatrix
    {
        $dimension = $this->getDimensionForVersion();
        $bitMatrix = new BitMatrix($dimension);

        // Top left finder pattern + separator + format
        $bitMatrix->setRegion(0, 0, 9, 9);
        // Top right finder pattern + separator + format
        $bitMatrix->setRegion($dimension - 8, 0, 8, 9);
        // Bottom left finder pattern + separator + format
        $bitMatrix->setRegion(0, $dimension - 8, 9, 8);

        // Alignment patterns
        $max = count($this->alignmentPatternCenters);

        for ($x = 0; $x < $max; ++$x) {
            $i = $this->alignmentPatternCenters[$x] - 2;

            for ($y = 0; $y < $max; ++$y) {
                if (($x === 0 && ($y === 0 || $y === $max - 1)) || ($x === $max - 1 && $y === 0)) {
                    // No alignment patterns near the three finder paterns
                    continue;
                }

                $bitMatrix->setRegion($this->alignmentPatternCenters[$y] - 2, $i, 5, 5);
            }
        }

        // Vertical timing pattern
        $bitMatrix->setRegion(6, 9, 1, $dimension - 17);
        // Horizontal timing pattern
        $bitMatrix->setRegion(9, 6, $dimension - 17, 1);

        if ($this->versionNumber > 6) {
            // Version info, top right
            $bitMatrix->setRegion($dimension - 11, 0, 3, 6);
            // Version info, bottom left
            $bitMatrix->setRegion(0, $dimension - 11, 6, 3);
        }

        return $bitMatrix;
    }

    /**
     * Returns a string representation for the version.
     */
    public function __toString() : string
    {
        return (string) $this->versionNumber;
    }

    /**
     * Build and cache a specific version.
     *
     * See ISO 18004:2006 6.5.1 Table 9.
     *
     * @return array<int, self>
     */
    private static function versions() : array
    {
        if (null !== self::$versions) {
            return self::$versions;
        }

        return self::$versions = [
            new self(
                1,
                [],
                new EcBlocks(7, new EcBlock(1, 19)),
                new EcBlocks(10, new EcBlock(1, 16)),
                new EcBlocks(13, new EcBlock(1, 13)),
                new EcBlocks(17, new EcBlock(1, 9))
            ),
            new self(
                2,
                [6, 18],
                new EcBlocks(10, new EcBlock(1, 34)),
                new EcBlocks(16, new EcBlock(1, 28)),
                new EcBlocks(22, new EcBlock(1, 22)),
                new EcBlocks(28, new EcBlock(1, 16))
            ),
            new self(
                3,
                [6, 22],
                new EcBlocks(15, new EcBlock(1, 55)),
                new EcBlocks(26, new EcBlock(1, 44)),
                new EcBlocks(18, new EcBlock(2, 17)),
                new EcBlocks(22, new EcBlock(2, 13))
            ),
            new self(
                4,
                [6, 26],
                new EcBlocks(20, new EcBlock(1, 80)),
                new EcBlocks(18, new EcBlock(2, 32)),
                new EcBlocks(26, new EcBlock(2, 24)),
                new EcBlocks(16, new EcBlock(4, 9))
            ),
            new self(
                5,
                [6, 30],
                new EcBlocks(26, new EcBlock(1, 108)),
                new EcBlocks(24, new EcBlock(2, 43)),
                new EcBlocks(18, new EcBlock(2, 15), new EcBlock(2, 16)),
                new EcBlocks(22, new EcBlock(2, 11), new EcBlock(2, 12))
            ),
            new self(
                6,
                [6, 34],
                new EcBlocks(18, new EcBlock(2, 68)),
                new EcBlocks(16, new EcBlock(4, 27)),
                new EcBlocks(24, new EcBlock(4, 19)),
                new EcBlocks(28, new EcBlock(4, 15))
            ),
            new self(
                7,
                [6, 22, 38],
                new EcBlocks(20, new EcBlock(2, 78)),
                new EcBlocks(18, new EcBlock(4, 31)),
                new EcBlocks(18, new EcBlock(2, 14), new EcBlock(4, 15)),
                new EcBlocks(26, new EcBlock(4, 13), new EcBlock(1, 14))
            ),
            new self(
                8,
                [6, 24, 42],
                new EcBlocks(24, new EcBlock(2, 97)),
                new EcBlocks(22, new EcBlock(2, 38), new EcBlock(2, 39)),
                new EcBlocks(22, new EcBlock(4, 18), new EcBlock(2, 19)),
                new EcBlocks(26, new EcBlock(4, 14), new EcBlock(2, 15))
            ),
            new self(
                9,
                [6, 26, 46],
                new EcBlocks(30, new EcBlock(2, 116)),
                new EcBlocks(22, new EcBlock(3, 36), new EcBlock(2, 37)),
                new EcBlocks(20, new EcBlock(4, 16), new EcBlock(4, 17)),
                new EcBlocks(24, new EcBlock(4, 12), new EcBlock(4, 13))
            ),
            new self(
                10,
                [6, 28, 50],
                new EcBlocks(18, new EcBlock(2, 68), new EcBlock(2, 69)),
                new EcBlocks(26, new EcBlock(4, 43), new EcBlock(1, 44)),
                new EcBlocks(24, new EcBlock(6, 19), new EcBlock(2, 20)),
                new EcBlocks(28, new EcBlock(6, 15), new EcBlock(2, 16))
            ),
            new self(
                11,
                [6, 30, 54],
                new EcBlocks(20, new EcBlock(4, 81)),
                new EcBlocks(30, new EcBlock(1, 50), new EcBlock(4, 51)),
                new EcBlocks(28, new EcBlock(4, 22), new EcBlock(4, 23)),
                new EcBlocks(24, new EcBlock(3, 12), new EcBlock(8, 13))
            ),
            new self(
                12,
                [6, 32, 58],
                new EcBlocks(24, new EcBlock(2, 92), new EcBlock(2, 93)),
                new EcBlocks(22, new EcBlock(6, 36), new EcBlock(2, 37)),
                new EcBlocks(26, new EcBlock(4, 20), new EcBlock(6, 21)),
                new EcBlocks(28, new EcBlock(7, 14), new EcBlock(4, 15))
            ),
            new self(
                13,
                [6, 34, 62],
                new EcBlocks(26, new EcBlock(4, 107)),
                new EcBlocks(22, new EcBlock(8, 37), new EcBlock(1, 38)),
                new EcBlocks(24, new EcBlock(8, 20), new EcBlock(4, 21)),
                new EcBlocks(22, new EcBlock(12, 11), new EcBlock(4, 12))
            ),
            new self(
                14,
                [6, 26, 46, 66],
                new EcBlocks(30, new EcBlock(3, 115), new EcBlock(1, 116)),
                new EcBlocks(24, new EcBlock(4, 40), new EcBlock(5, 41)),
                new EcBlocks(20, new EcBlock(11, 16), new EcBlock(5, 17)),
                new EcBlocks(24, new EcBlock(11, 12), new EcBlock(5, 13))
            ),
            new self(
                15,
                [6, 26, 48, 70],
                new EcBlocks(22, new EcBlock(5, 87), new EcBlock(1, 88)),
                new EcBlocks(24, new EcBlock(5, 41), new EcBlock(5, 42)),
                new EcBlocks(30, new EcBlock(5, 24), new EcBlock(7, 25)),
                new EcBlocks(24, new EcBlock(11, 12), new EcBlock(7, 13))
            ),
            new self(
                16,
                [6, 26, 50, 74],
                new EcBlocks(24, new EcBlock(5, 98), new EcBlock(1, 99)),
                new EcBlocks(28, new EcBlock(7, 45), new EcBlock(3, 46)),
                new EcBlocks(24, new EcBlock(15, 19), new EcBlock(2, 20)),
                new EcBlocks(30, new EcBlock(3, 15), new EcBlock(13, 16))
            ),
            new self(
                17,
                [6, 30, 54, 78],
                new EcBlocks(28, new EcBlock(1, 107), new EcBlock(5, 108)),
                new EcBlocks(28, new EcBlock(10, 46), new EcBlock(1, 47)),
                new EcBlocks(28, new EcBlock(1, 22), new EcBlock(15, 23)),
                new EcBlocks(28, new EcBlock(2, 14), new EcBlock(17, 15))
            ),
            new self(
                18,
                [6, 30, 56, 82],
                new EcBlocks(30, new EcBlock(5, 120), new EcBlock(1, 121)),
                new EcBlocks(26, new EcBlock(9, 43), new EcBlock(4, 44)),
                new EcBlocks(28, new EcBlock(17, 22), new EcBlock(1, 23)),
                new EcBlocks(28, new EcBlock(2, 14), new EcBlock(19, 15))
            ),
            new self(
                19,
                [6, 30, 58, 86],
                new EcBlocks(28, new EcBlock(3, 113), new EcBlock(4, 114)),
                new EcBlocks(26, new EcBlock(3, 44), new EcBlock(11, 45)),
                new EcBlocks(26, new EcBlock(17, 21), new EcBlock(4, 22)),
                new EcBlocks(26, new EcBlock(9, 13), new EcBlock(16, 14))
            ),
            new self(
                20,
                [6, 34, 62, 90],
                new EcBlocks(28, new EcBlock(3, 107), new EcBlock(5, 108)),
                new EcBlocks(26, new EcBlock(3, 41), new EcBlock(13, 42)),
                new EcBlocks(30, new EcBlock(15, 24), new EcBlock(5, 25)),
                new EcBlocks(28, new EcBlock(15, 15), new EcBlock(10, 16))
            ),
            new self(
                21,
                [6, 28, 50, 72, 94],
                new EcBlocks(28, new EcBlock(4, 116), new EcBlock(4, 117)),
                new EcBlocks(26, new EcBlock(17, 42)),
                new EcBlocks(28, new EcBlock(17, 22), new EcBlock(6, 23)),
                new EcBlocks(30, new EcBlock(19, 16), new EcBlock(6, 17))
            ),
            new self(
                22,
                [6, 26, 50, 74, 98],
                new EcBlocks(28, new EcBlock(2, 111), new EcBlock(7, 112)),
                new EcBlocks(28, new EcBlock(17, 46)),
                new EcBlocks(30, new EcBlock(7, 24), new EcBlock(16, 25)),
                new EcBlocks(24, new EcBlock(34, 13))
            ),
            new self(
                23,
                [6, 30, 54, 78, 102],
                new EcBlocks(30, new EcBlock(4, 121), new EcBlock(5, 122)),
                new EcBlocks(28, new EcBlock(4, 47), new EcBlock(14, 48)),
                new EcBlocks(30, new EcBlock(11, 24), new EcBlock(14, 25)),
                new EcBlocks(30, new EcBlock(16, 15), new EcBlock(14, 16))
            ),
            new self(
                24,
                [6, 28, 54, 80, 106],
                new EcBlocks(30, new EcBlock(6, 117), new EcBlock(4, 118)),
                new EcBlocks(28, new EcBlock(6, 45), new EcBlock(14, 46)),
                new EcBlocks(30, new EcBlock(11, 24), new EcBlock(16, 25)),
                new EcBlocks(30, new EcBlock(30, 16), new EcBlock(2, 17))
            ),
            new self(
                25,
                [6, 32, 58, 84, 110],
                new EcBlocks(26, new EcBlock(8, 106), new EcBlock(4, 107)),
                new EcBlocks(28, new EcBlock(8, 47), new EcBlock(13, 48)),
                new EcBlocks(30, new EcBlock(7, 24), new EcBlock(22, 25)),
                new EcBlocks(30, new EcBlock(22, 15), new EcBlock(13, 16))
            ),
            new self(
                26,
                [6, 30, 58, 86, 114],
                new EcBlocks(28, new EcBlock(10, 114), new EcBlock(2, 115)),
                new EcBlocks(28, new EcBlock(19, 46), new EcBlock(4, 47)),
                new EcBlocks(28, new EcBlock(28, 22), new EcBlock(6, 23)),
                new EcBlocks(30, new EcBlock(33, 16), new EcBlock(4, 17))
            ),
            new self(
                27,
                [6, 34, 62, 90, 118],
                new EcBlocks(30, new EcBlock(8, 122), new EcBlock(4, 123)),
                new EcBlocks(28, new EcBlock(22, 45), new EcBlock(3, 46)),
                new EcBlocks(30, new EcBlock(8, 23), new EcBlock(26, 24)),
                new EcBlocks(30, new EcBlock(12, 15), new EcBlock(28, 16))
            ),
            new self(
                28,
                [6, 26, 50, 74, 98, 122],
                new EcBlocks(30, new EcBlock(3, 117), new EcBlock(10, 118)),
                new EcBlocks(28, new EcBlock(3, 45), new EcBlock(23, 46)),
                new EcBlocks(30, new EcBlock(4, 24), new EcBlock(31, 25)),
                new EcBlocks(30, new EcBlock(11, 15), new EcBlock(31, 16))
            ),
            new self(
                29,
                [6, 30, 54, 78, 102, 126],
                new EcBlocks(30, new EcBlock(7, 116), new EcBlock(7, 117)),
                new EcBlocks(28, new EcBlock(21, 45), new EcBlock(7, 46)),
                new EcBlocks(30, new EcBlock(1, 23), new EcBlock(37, 24)),
                new EcBlocks(30, new EcBlock(19, 15), new EcBlock(26, 16))
            ),
            new self(
                30,
                [6, 26, 52, 78, 104, 130],
                new EcBlocks(30, new EcBlock(5, 115), new EcBlock(10, 116)),
                new EcBlocks(28, new EcBlock(19, 47), new EcBlock(10, 48)),
                new EcBlocks(30, new EcBlock(15, 24), new EcBlock(25, 25)),
                new EcBlocks(30, new EcBlock(23, 15), new EcBlock(25, 16))
            ),
            new self(
                31,
                [6, 30, 56, 82, 108, 134],
                new EcBlocks(30, new EcBlock(13, 115), new EcBlock(3, 116)),
                new EcBlocks(28, new EcBlock(2, 46), new EcBlock(29, 47)),
                new EcBlocks(30, new EcBlock(42, 24), new EcBlock(1, 25)),
                new EcBlocks(30, new EcBlock(23, 15), new EcBlock(28, 16))
            ),
            new self(
                32,
                [6, 34, 60, 86, 112, 138],
                new EcBlocks(30, new EcBlock(17, 115)),
                new EcBlocks(28, new EcBlock(10, 46), new EcBlock(23, 47)),
                new EcBlocks(30, new EcBlock(10, 24), new EcBlock(35, 25)),
                new EcBlocks(30, new EcBlock(19, 15), new EcBlock(35, 16))
            ),
            new self(
                33,
                [6, 30, 58, 86, 114, 142],
                new EcBlocks(30, new EcBlock(17, 115), new EcBlock(1, 116)),
                new EcBlocks(28, new EcBlock(14, 46), new EcBlock(21, 47)),
                new EcBlocks(30, new EcBlock(29, 24), new EcBlock(19, 25)),
                new EcBlocks(30, new EcBlock(11, 15), new EcBlock(46, 16))
            ),
            new self(
                34,
                [6, 34, 62, 90, 118, 146],
                new EcBlocks(30, new EcBlock(13, 115), new EcBlock(6, 116)),
                new EcBlocks(28, new EcBlock(14, 46), new EcBlock(23, 47)),
                new EcBlocks(30, new EcBlock(44, 24), new EcBlock(7, 25)),
                new EcBlocks(30, new EcBlock(59, 16), new EcBlock(1, 17))
            ),
            new self(
                35,
                [6, 30, 54, 78, 102, 126, 150],
                new EcBlocks(30, new EcBlock(12, 121), new EcBlock(7, 122)),
                new EcBlocks(28, new EcBlock(12, 47), new EcBlock(26, 48)),
                new EcBlocks(30, new EcBlock(39, 24), new EcBlock(14, 25)),
                new EcBlocks(30, new EcBlock(22, 15), new EcBlock(41, 16))
            ),
            new self(
                36,
                [6, 24, 50, 76, 102, 128, 154],
                new EcBlocks(30, new EcBlock(6, 121), new EcBlock(14, 122)),
                new EcBlocks(28, new EcBlock(6, 47), new EcBlock(34, 48)),
                new EcBlocks(30, new EcBlock(46, 24), new EcBlock(10, 25)),
                new EcBlocks(30, new EcBlock(2, 15), new EcBlock(64, 16))
            ),
            new self(
                37,
                [6, 28, 54, 80, 106, 132, 158],
                new EcBlocks(30, new EcBlock(17, 122), new EcBlock(4, 123)),
                new EcBlocks(28, new EcBlock(29, 46), new EcBlock(14, 47)),
                new EcBlocks(30, new EcBlock(49, 24), new EcBlock(10, 25)),
                new EcBlocks(30, new EcBlock(24, 15), new EcBlock(46, 16))
            ),
            new self(
                38,
                [6, 32, 58, 84, 110, 136, 162],
                new EcBlocks(30, new EcBlock(4, 122), new EcBlock(18, 123)),
                new EcBlocks(28, new EcBlock(13, 46), new EcBlock(32, 47)),
                new EcBlocks(30, new EcBlock(48, 24), new EcBlock(14, 25)),
                new EcBlocks(30, new EcBlock(42, 15), new EcBlock(32, 16))
            ),
            new self(
                39,
                [6, 26, 54, 82, 110, 138, 166],
                new EcBlocks(30, new EcBlock(20, 117), new EcBlock(4, 118)),
                new EcBlocks(28, new EcBlock(40, 47), new EcBlock(7, 48)),
                new EcBlocks(30, new EcBlock(43, 24), new EcBlock(22, 25)),
                new EcBlocks(30, new EcBlock(10, 15), new EcBlock(67, 16))
            ),
            new self(
                40,
                [6, 30, 58, 86, 114, 142, 170],
                new EcBlocks(30, new EcBlock(19, 118), new EcBlock(6, 119)),
                new EcBlocks(28, new EcBlock(18, 47), new EcBlock(31, 48)),
                new EcBlocks(30, new EcBlock(34, 24), new EcBlock(34, 25)),
                new EcBlocks(30, new EcBlock(20, 15), new EcBlock(61, 16))
            ),
        ];
    }
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Common;

/**
 * General bit utilities.
 *
 * All utility methods are based on 32-bit integers and also work on 64-bit
 * systems.
 */
final class BitUtils
{
    private function __construct()
    {
    }

    /**
     * Performs an unsigned right shift.
     *
     * This is the same as the unsigned right shift operator ">>>" in other
     * languages.
     */
    public static function unsignedRightShift(int $a, int $b) : int
    {
        return (
            $a >= 0
            ? $a >> $b
            : (($a & 0x7fffffff) >> $b) | (0x40000000 >> ($b - 1))
        );
    }

    /**
     * Gets the number of trailing zeros.
     */
    public static function numberOfTrailingZeros(int $i) : int
    {
        $lastPos = strrpos(str_pad(decbin($i), 32, '0', STR_PAD_LEFT), '1');
        return $lastPos === false ? 32 : 31 - $lastPos;
    }
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Common;

use DASPRiD\Enum\AbstractEnum;

/**
 * Enum representing various modes in which data can be encoded to bits.
 *
 * @method static self TERMINATOR()
 * @method static self NUMERIC()
 * @method static self ALPHANUMERIC()
 * @method static self STRUCTURED_APPEND()
 * @method static self BYTE()
 * @method static self ECI()
 * @method static self KANJI()
 * @method static self FNC1_FIRST_POSITION()
 * @method static self FNC1_SECOND_POSITION()
 * @method static self HANZI()
 */
final class Mode extends AbstractEnum
{
    protected const TERMINATOR = [[0, 0, 0], 0x00];
    protected const NUMERIC = [[10, 12, 14], 0x01];
    protected const ALPHANUMERIC = [[9, 11, 13], 0x02];
    protected const STRUCTURED_APPEND = [[0, 0, 0], 0x03];
    protected const BYTE = [[8, 16, 16], 0x04];
    protected const ECI = [[0, 0, 0], 0x07];
    protected const KANJI = [[8, 10, 12], 0x08];
    protected const FNC1_FIRST_POSITION = [[0, 0, 0], 0x05];
    protected const FNC1_SECOND_POSITION = [[0, 0, 0], 0x09];
    protected const HANZI = [[8, 10, 12], 0x0d];

    /**
     * @param int[] $characterCountBitsForVersions
     */
    protected function __construct(
        private readonly array $characterCountBitsForVersions,
        private readonly int   $bits
    ) {
    }

    /**
     * Returns the number of bits used in a specific QR code version.
     */
    public function getCharacterCountBits(Version $version) : int
    {
        $number = $version->getVersionNumber();

        if ($number <= 9) {
            $offset = 0;
        } elseif ($number <= 26) {
            $offset = 1;
        } else {
            $offset = 2;
        }

        return $this->characterCountBitsForVersions[$offset];
    }

    /**
     * Returns the four bits used to encode this mode.
     */
    public function getBits() : int
    {
        return $this->bits;
    }
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Common;

/**
 * Encapsulates a set of error-correction blocks in one symbol version.
 *
 * Most versions will use blocks of differing sizes within one version, so, this encapsulates the parameters for each
 * set of blocks. It also holds the number of error-correction codewords per block since it will be the same across all
 * blocks within one version.
 */
final class EcBlocks
{
    /**
     * List of EC blocks.
     *
     * @var EcBlock[]
     */
    private array $ecBlocks;

    public function __construct(private readonly int $ecCodewordsPerBlock, EcBlock ...$ecBlocks)
    {
        $this->ecBlocks = $ecBlocks;
    }

    /**
     * Returns the number of EC codewords per block.
     */
    public function getEcCodewordsPerBlock() : int
    {
        return $this->ecCodewordsPerBlock;
    }

    /**
     * Returns the total number of EC block appearances.
     */
    public function getNumBlocks() : int
    {
        $total = 0;

        foreach ($this->ecBlocks as $ecBlock) {
            $total += $ecBlock->getCount();
        }

        return $total;
    }

    /**
     * Returns the total count of EC codewords.
     */
    public function getTotalEcCodewords() : int
    {
        return $this->ecCodewordsPerBlock * $this->getNumBlocks();
    }

    /**
     * Returns the EC blocks included in this collection.
     *
     * @return EcBlock[]
     */
    public function getEcBlocks() : array
    {
        return $this->ecBlocks;
    }
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Common;

use BaconQrCode\Exception\InvalidArgumentException;
use SplFixedArray;

/**
 * A simple, fast array of bits.
 */
final class BitArray
{
    /**
     * Bits represented as an array of integers.
     *
     * @var SplFixedArray<int>
     */
    private SplFixedArray $bits;

    /**
     * Creates a new bit array with a given size.
     */
    public function __construct(private int $size = 0)
    {
        $this->bits = SplFixedArray::fromArray(array_fill(0, ($this->size + 31) >> 3, 0));
    }

    /**
     * Gets the size in bits.
     */
    public function getSize() : int
    {
        return $this->size;
    }

    /**
     * Gets the size in bytes.
     */
    public function getSizeInBytes() : int
    {
        return ($this->size + 7) >> 3;
    }

    /**
     * Ensures that the array has a minimum capacity.
     */
    public function ensureCapacity(int $size) : void
    {
        if ($size > count($this->bits) << 5) {
            $this->bits->setSize(($size + 31) >> 5);
        }
    }

    /**
     * Gets a specific bit.
     */
    public function get(int $i) : bool
    {
        return 0 !== ($this->bits[$i >> 5] & (1 << ($i & 0x1f)));
    }

    /**
     * Sets a specific bit.
     */
    public function set(int $i) : void
    {
        $this->bits[$i >> 5] = $this->bits[$i >> 5] | 1 << ($i & 0x1f);
    }

    /**
     * Flips a specific bit.
     */
    public function flip(int $i) : void
    {
        $this->bits[$i >> 5] ^= 1 << ($i & 0x1f);
    }

    /**
     * Gets the next set bit position from a given position.
     */
    public function getNextSet(int $from) : int
    {
        if ($from >= $this->size) {
            return $this->size;
        }

        $bitsOffset = $from >> 5;
        $currentBits = $this->bits[$bitsOffset];
        $bitsLength = count($this->bits);
        $currentBits &= ~((1 << ($from & 0x1f)) - 1);

        while (0 === $currentBits) {
            if (++$bitsOffset === $bitsLength) {
                return $this->size;
            }

            $currentBits = $this->bits[$bitsOffset];
        }

        $result = ($bitsOffset << 5) + BitUtils::numberOfTrailingZeros($currentBits);
        return min($result, $this->size);
    }

    /**
     * Gets the next unset bit position from a given position.
     */
    public function getNextUnset(int $from) : int
    {
        if ($from >= $this->size) {
            return $this->size;
        }

        $bitsOffset = $from >> 5;
        $currentBits = ~$this->bits[$bitsOffset];
        $bitsLength = count($this->bits);
        $currentBits &= ~((1 << ($from & 0x1f)) - 1);

        while (0 === $currentBits) {
            if (++$bitsOffset === $bitsLength) {
                return $this->size;
            }

            $currentBits = ~$this->bits[$bitsOffset];
        }

        $result = ($bitsOffset << 5) + BitUtils::numberOfTrailingZeros($currentBits);
        return min($result, $this->size);
    }

    /**
     * Sets a bulk of bits.
     */
    public function setBulk(int $i, int $newBits) : void
    {
        $this->bits[$i >> 5] = $newBits;
    }

    /**
     * Sets a range of bits.
     *
     * @throws InvalidArgumentException if end is smaller than start
     */
    public function setRange(int $start, int $end) : void
    {
        if ($end < $start) {
            throw new InvalidArgumentException('End must be greater or equal to start');
        }

        if ($end === $start) {
            return;
        }

        --$end;

        $firstInt = $start >> 5;
        $lastInt = $end >> 5;

        for ($i = $firstInt; $i <= $lastInt; ++$i) {
            $firstBit = $i > $firstInt ? 0 : $start & 0x1f;
            $lastBit = $i < $lastInt ? 31 : $end & 0x1f;

            if (0 === $firstBit && 31 === $lastBit) {
                $mask = 0x7fffffff;
            } else {
                $mask = 0;

                for ($j = $firstBit; $j < $lastBit; ++$j) {
                    $mask |= 1 << $j;
                }
            }

            $this->bits[$i] = $this->bits[$i] | $mask;
        }
    }

    /**
     * Clears the bit array, unsetting every bit.
     */
    public function clear() : void
    {
        $bitsLength = count($this->bits);

        for ($i = 0; $i < $bitsLength; ++$i) {
            $this->bits[$i] = 0;
        }
    }

    /**
     * Checks if a range of bits is set or not set.

     * @throws InvalidArgumentException if end is smaller than start
     */
    public function isRange(int $start, int $end, bool $value) : bool
    {
        if ($end < $start) {
            throw new InvalidArgumentException('End must be greater or equal to start');
        }

        if ($end === $start) {
            return true;
        }

        --$end;

        $firstInt = $start >> 5;
        $lastInt = $end >> 5;

        for ($i = $firstInt; $i <= $lastInt; ++$i) {
            $firstBit = $i > $firstInt ? 0 : $start & 0x1f;
            $lastBit = $i < $lastInt ? 31 : $end & 0x1f;

            if (0 === $firstBit && 31 === $lastBit) {
                $mask = 0x7fffffff;
            } else {
                $mask = 0;

                for ($j = $firstBit; $j <= $lastBit; ++$j) {
                    $mask |= 1 << $j;
                }
            }

            if (($this->bits[$i] & $mask) !== ($value ? $mask : 0)) {
                return false;
            }
        }

        return true;
    }

    /**
     * Appends a bit to the array.
     */
    public function appendBit(bool $bit) : void
    {
        $this->ensureCapacity($this->size + 1);

        if ($bit) {
            $this->bits[$this->size >> 5] = $this->bits[$this->size >> 5] | (1 << ($this->size & 0x1f));
        }

        ++$this->size;
    }

    /**
     * Appends a number of bits (up to 32) to the array.

     * @throws InvalidArgumentException if num bits is not between 0 and 32
     */
    public function appendBits(int $value, int $numBits) : void
    {
        if ($numBits < 0 || $numBits > 32) {
            throw new InvalidArgumentException('Num bits must be between 0 and 32');
        }

        $this->ensureCapacity($this->size + $numBits);

        for ($numBitsLeft = $numBits; $numBitsLeft > 0; $numBitsLeft--) {
            $this->appendBit((($value >> ($numBitsLeft - 1)) & 0x01) === 1);
        }
    }

    /**
     * Appends another bit array to this array.
     */
    public function appendBitArray(self $other) : void
    {
        $otherSize = $other->getSize();
        $this->ensureCapacity($this->size + $other->getSize());

        for ($i = 0; $i < $otherSize; ++$i) {
            $this->appendBit($other->get($i));
        }
    }

    /**
     * Makes an exclusive-or comparision on the current bit array.
     *
     * @throws InvalidArgumentException if sizes don't match
     */
    public function xorBits(self $other) : void
    {
        $bitsLength = count($this->bits);
        $otherBits  = $other->getBitArray();

        if ($bitsLength !== count($otherBits)) {
            throw new InvalidArgumentException('Sizes don\'t match');
        }

        for ($i = 0; $i < $bitsLength; ++$i) {
            $this->bits[$i] = $this->bits[$i] ^ $otherBits[$i];
        }
    }

    /**
     * Converts the bit array to a byte array.
     *
     * @return SplFixedArray<int>
     */
    public function toBytes(int $bitOffset, int $numBytes) : SplFixedArray
    {
        $bytes = new SplFixedArray($numBytes);

        for ($i = 0; $i < $numBytes; ++$i) {
            $byte = 0;

            for ($j = 0; $j < 8; ++$j) {
                if ($this->get($bitOffset)) {
                    $byte |= 1 << (7 - $j);
                }

                ++$bitOffset;
            }

            $bytes[$i] = $byte;
        }

        return $bytes;
    }

    /**
     * Gets the internal bit array.
     *
     * @return SplFixedArray<int>
     */
    public function getBitArray() : SplFixedArray
    {
        return $this->bits;
    }

    /**
     * Reverses the array.
     */
    public function reverse() : void
    {
        $newBits = new SplFixedArray(count($this->bits));

        for ($i = 0; $i < $this->size; ++$i) {
            if ($this->get($this->size - $i - 1)) {
                $newBits[$i >> 5] = $newBits[$i >> 5] | (1 << ($i & 0x1f));
            }
        }

        $this->bits = $newBits;
    }

    /**
     * Returns a string representation of the bit array.
     */
    public function __toString() : string
    {
        $result = '';

        for ($i = 0; $i < $this->size; ++$i) {
            if (0 === ($i & 0x07)) {
                $result .= ' ';
            }

            $result .= $this->get($i) ? 'X' : '.';
        }

        return $result;
    }
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Common;

/**
 * Encapsulates the parameters for one error-correction block in one symbol version.
 *
 * This includes the number of data codewords, and the number of times a block with these parameters is used
 * consecutively in the QR code version's format.
 */
final class EcBlock
{
    public function __construct(private readonly int $count, private readonly int $dataCodewords)
    {
    }

    /**
     * Returns how many times the block is used.
     */
    public function getCount() : int
    {
        return $this->count;
    }

    /**
     * Returns the number of data codewords.
     */
    public function getDataCodewords() : int
    {
        return $this->dataCodewords;
    }
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Common;

use BaconQrCode\Exception\InvalidArgumentException;
use BaconQrCode\Exception\RuntimeException;
use SplFixedArray;

/**
 * Reed-Solomon codec for 8-bit characters.
 *
 * Based on libfec by Phil Karn, KA9Q.
 */
final class ReedSolomonCodec
{
    /**
     * Symbol size in bits.
     */
    private int $symbolSize;

    /**
     * Block size in symbols.
     */
    private int $blockSize;

    /**
     * First root of RS code generator polynomial, index form.
     */
    private int $firstRoot;

    /**
     * Primitive element to generate polynomial roots, index form.
     */
    private int $primitive;

    /**
     * Prim-th root of 1, index form.
     */
    private int $iPrimitive;

    /**
     * RS code generator polynomial degree (number of roots).
     */
    private int $numRoots;

    /**
     * Padding bytes at front of shortened block.
     */
    private int $padding;

    /**
     * Log lookup table.
     *
     * @var SplFixedArray
     */
    private SplFixedArray $alphaTo;

    /**
     * Anti-Log lookup table.
     *
     * @var SplFixedArray
     */
    private SplFixedArray $indexOf;

    /**
     * Generator polynomial.
     *
     * @var SplFixedArray
     */
    private SplFixedArray $generatorPoly;

    /**
     * @throws InvalidArgumentException if symbol size ist not between 0 and 8
     * @throws InvalidArgumentException if first root is invalid
     * @throws InvalidArgumentException if num roots is invalid
     * @throws InvalidArgumentException if padding is invalid
     * @throws RuntimeException if field generator polynomial is not primitive
     */
    public function __construct(
        int $symbolSize,
        int $gfPoly,
        int $firstRoot,
        int $primitive,
        int $numRoots,
        int $padding
    ) {
        if ($symbolSize < 0 || $symbolSize > 8) {
            throw new InvalidArgumentException('Symbol size must be between 0 and 8');
        }

        if ($firstRoot < 0 || $firstRoot >= (1 << $symbolSize)) {
            throw new InvalidArgumentException('First root must be between 0 and ' . (1 << $symbolSize));
        }

        if ($numRoots < 0 || $numRoots >= (1 << $symbolSize)) {
            throw new InvalidArgumentException('Num roots must be between 0 and ' . (1 << $symbolSize));
        }

        if ($padding < 0 || $padding >= ((1 << $symbolSize) - 1 - $numRoots)) {
            throw new InvalidArgumentException(
                'Padding must be between 0 and ' . ((1 << $symbolSize) - 1 - $numRoots)
            );
        }

        $this->symbolSize = $symbolSize;
        $this->blockSize = (1 << $symbolSize) - 1;
        $this->padding = $padding;
        $this->alphaTo = SplFixedArray::fromArray(array_fill(0, $this->blockSize + 1, 0), false);
        $this->indexOf = SplFixedArray::fromArray(array_fill(0, $this->blockSize + 1, 0), false);

        // Generate galous field lookup table
        $this->indexOf[0] = $this->blockSize;
        $this->alphaTo[$this->blockSize] = 0;

        $sr = 1;

        for ($i = 0; $i < $this->blockSize; ++$i) {
            $this->indexOf[$sr] = $i;
            $this->alphaTo[$i]  = $sr;

            $sr <<= 1;

            if ($sr & (1 << $symbolSize)) {
                $sr ^= $gfPoly;
            }

            $sr &= $this->blockSize;
        }

        if (1 !== $sr) {
            throw new RuntimeException('Field generator polynomial is not primitive');
        }

        // Form RS code generator polynomial from its roots
        $this->generatorPoly = SplFixedArray::fromArray(array_fill(0, $numRoots + 1, 0), false);
        $this->firstRoot = $firstRoot;
        $this->primitive = $primitive;
        $this->numRoots = $numRoots;

        // Find prim-th root of 1, used in decoding
        for ($iPrimitive = 1; ($iPrimitive % $primitive) !== 0; $iPrimitive += $this->blockSize) {
        }

        $this->iPrimitive = intdiv($iPrimitive, $primitive);

        $this->generatorPoly[0] = 1;

        for ($i = 0, $root = $firstRoot * $primitive; $i < $numRoots; ++$i, $root += $primitive) {
            $this->generatorPoly[$i + 1] = 1;

            for ($j = $i; $j > 0; $j--) {
                if ($this->generatorPoly[$j] !== 0) {
                    $this->generatorPoly[$j] = $this->generatorPoly[$j - 1] ^ $this->alphaTo[
                        $this->modNn($this->indexOf[$this->generatorPoly[$j]] + $root)
                    ];
                } else {
                    $this->generatorPoly[$j] = $this->generatorPoly[$j - 1];
                }
            }

            $this->generatorPoly[$j] = $this->alphaTo[$this->modNn($this->indexOf[$this->generatorPoly[0]] + $root)];
        }

        // Convert generator poly to index form for quicker encoding
        for ($i = 0; $i <= $numRoots; ++$i) {
            $this->generatorPoly[$i] = $this->indexOf[$this->generatorPoly[$i]];
        }
    }

    /**
     * Encodes data and writes result back into parity array.
     */
    public function encode(SplFixedArray $data, SplFixedArray $parity) : void
    {
        for ($i = 0; $i < $this->numRoots; ++$i) {
            $parity[$i] = 0;
        }

        $iterations = $this->blockSize - $this->numRoots - $this->padding;

        for ($i = 0; $i < $iterations; ++$i) {
            $feedback = $this->indexOf[$data[$i] ^ $parity[0]];

            if ($feedback !== $this->blockSize) {
                // Feedback term is non-zero
                $feedback = $this->modNn($this->blockSize - $this->generatorPoly[$this->numRoots] + $feedback);

                for ($j = 1; $j < $this->numRoots; ++$j) {
                    $parity[$j] = $parity[$j] ^ $this->alphaTo[
                        $this->modNn($feedback + $this->generatorPoly[$this->numRoots - $j])
                    ];
                }
            }

            for ($j = 0; $j < $this->numRoots - 1; ++$j) {
                $parity[$j] = $parity[$j + 1];
            }

            if ($feedback !== $this->blockSize) {
                $parity[$this->numRoots - 1] = $this->alphaTo[$this->modNn($feedback + $this->generatorPoly[0])];
            } else {
                $parity[$this->numRoots - 1] = 0;
            }
        }
    }

    /**
     * Decodes received data.
     */
    public function decode(SplFixedArray $data, ?SplFixedArray $erasures = null) : ?int
    {
        // This speeds up the initialization a bit.
        $numRootsPlusOne = SplFixedArray::fromArray(array_fill(0, $this->numRoots + 1, 0), false);
        $numRoots = SplFixedArray::fromArray(array_fill(0, $this->numRoots, 0), false);

        $lambda = clone $numRootsPlusOne;
        $b = clone $numRootsPlusOne;
        $t = clone $numRootsPlusOne;
        $omega = clone $numRootsPlusOne;
        $root = clone $numRoots;
        $loc = clone $numRoots;

        $numErasures = (null !== $erasures ? count($erasures) : 0);

        // Form the Syndromes; i.e., evaluate data(x) at roots of g(x)
        $syndromes = SplFixedArray::fromArray(array_fill(0, $this->numRoots, $data[0]), false);

        for ($i = 1; $i < $this->blockSize - $this->padding; ++$i) {
            for ($j = 0; $j < $this->numRoots; ++$j) {
                if ($syndromes[$j] === 0) {
                    $syndromes[$j] = $data[$i];
                } else {
                    $syndromes[$j] = $data[$i] ^ $this->alphaTo[
                        $this->modNn($this->indexOf[$syndromes[$j]] + ($this->firstRoot + $j) * $this->primitive)
                    ];
                }
            }
        }

        // Convert syndromes to index form, checking for nonzero conditions
        $syndromeError = 0;

        for ($i = 0; $i < $this->numRoots; ++$i) {
            $syndromeError |= $syndromes[$i];
            $syndromes[$i] = $this->indexOf[$syndromes[$i]];
        }

        if (! $syndromeError) {
            // If syndrome is zero, data[] is a codeword and there are no errors to correct, so return data[]
            // unmodified.
            return 0;
        }

        $lambda[0] = 1;

        if ($numErasures > 0) {
            // Init lambda to be the erasure locator polynomial
            $lambda[1] = $this->alphaTo[$this->modNn($this->primitive * ($this->blockSize - 1 - $erasures[0]))];

            for ($i = 1; $i < $numErasures; ++$i) {
                $u = $this->modNn($this->primitive * ($this->blockSize - 1 - $erasures[$i]));

                for ($j = $i + 1; $j > 0; --$j) {
                    $tmp = $this->indexOf[$lambda[$j - 1]];

                    if ($tmp !== $this->blockSize) {
                        $lambda[$j] = $lambda[$j] ^ $this->alphaTo[$this->modNn($u + $tmp)];
                    }
                }
            }
        }

        for ($i = 0; $i <= $this->numRoots; ++$i) {
            $b[$i] = $this->indexOf[$lambda[$i]];
        }

        // Begin Berlekamp-Massey algorithm to determine error+erasure locator polynomial
        $r  = $numErasures;
        $el = $numErasures;

        while (++$r <= $this->numRoots) {
            // Compute discrepancy at the r-th step in poly form
            $discrepancyR = 0;

            for ($i = 0; $i < $r; ++$i) {
                if ($lambda[$i] !== 0 && $syndromes[$r - $i - 1] !== $this->blockSize) {
                    $discrepancyR ^= $this->alphaTo[
                        $this->modNn($this->indexOf[$lambda[$i]] + $syndromes[$r - $i - 1])
                    ];
                }
            }

            $discrepancyR = $this->indexOf[$discrepancyR];

            if ($discrepancyR === $this->blockSize) {
                $tmp = $b->toArray();
                array_unshift($tmp, $this->blockSize);
                array_pop($tmp);
                $b = SplFixedArray::fromArray($tmp, false);
                continue;
            }

            $t[0] = $lambda[0];

            for ($i = 0; $i < $this->numRoots; ++$i) {
                if ($b[$i] !== $this->blockSize) {
                    $t[$i + 1] = $lambda[$i + 1] ^ $this->alphaTo[$this->modNn($discrepancyR + $b[$i])];
                } else {
                    $t[$i + 1] = $lambda[$i + 1];
                }
            }

            if (2 * $el <= $r + $numErasures - 1) {
                $el = $r + $numErasures - $el;

                for ($i = 0; $i <= $this->numRoots; ++$i) {
                    $b[$i] = (
                        $lambda[$i] === 0
                        ? $this->blockSize
                        : $this->modNn($this->indexOf[$lambda[$i]] - $discrepancyR + $this->blockSize)
                    );
                }
            } else {
                $tmp = $b->toArray();
                array_unshift($tmp, $this->blockSize);
                array_pop($tmp);
                $b = SplFixedArray::fromArray($tmp, false);
            }

            $lambda = clone $t;
        }

        // Convert lambda to index form and compute deg(lambda(x))
        $degLambda = 0;

        for ($i = 0; $i <= $this->numRoots; ++$i) {
            $lambda[$i] = $this->indexOf[$lambda[$i]];

            if ($lambda[$i] !== $this->blockSize) {
                $degLambda = $i;
            }
        }

        // Find roots of the error+erasure locator polynomial by Chien search.
        $reg = clone $lambda;
        $reg[0] = 0;
        $count = 0;
        $i = 1;

        for ($k = $this->iPrimitive - 1; $i <= $this->blockSize; ++$i, $k = $this->modNn($k + $this->iPrimitive)) {
            $q = 1;

            for ($j = $degLambda; $j > 0; $j--) {
                if ($reg[$j] !== $this->blockSize) {
                    $reg[$j] = $this->modNn($reg[$j] + $j);
                    $q ^= $this->alphaTo[$reg[$j]];
                }
            }

            if ($q !== 0) {
                // Not a root
                continue;
            }

            // Store root (index-form) and error location number
            $root[$count] = $i;
            $loc[$count] = $k;

            if (++$count === $degLambda) {
                break;
            }
        }

        if ($degLambda !== $count) {
            // deg(lambda) unequal to number of roots: uncorrectable error detected
            return null;
        }

        // Compute err+eras evaluate poly omega(x) = s(x)*lambda(x) (modulo x**numRoots). In index form. Also find
        // deg(omega).
        $degOmega = $degLambda - 1;

        for ($i = 0; $i <= $degOmega; ++$i) {
            $tmp = 0;

            for ($j = $i; $j >= 0; --$j) {
                if ($syndromes[$i - $j] !== $this->blockSize && $lambda[$j] !== $this->blockSize) {
                    $tmp ^= $this->alphaTo[$this->modNn($syndromes[$i - $j] + $lambda[$j])];
                }
            }

            $omega[$i] = $this->indexOf[$tmp];
        }

        // Compute error values in poly-form. num1 = omega(inv(X(l))), num2 = inv(X(l))**(firstRoot-1) and
        // den = lambda_pr(inv(X(l))) all in poly form.
        for ($j = $count - 1; $j >= 0; --$j) {
            $num1 = 0;

            for ($i = $degOmega; $i >= 0; $i--) {
                if ($omega[$i] !== $this->blockSize) {
                    $num1 ^= $this->alphaTo[$this->modNn($omega[$i] + $i * $root[$j])];
                }
            }

            $num2 = $this->alphaTo[$this->modNn($root[$j] * ($this->firstRoot - 1) + $this->blockSize)];
            $den  = 0;

            // lambda[i+1] for i even is the formal derivativelambda_pr of lambda[i]
            for ($i = min($degLambda, $this->numRoots - 1) & ~1; $i >= 0; $i -= 2) {
                if ($lambda[$i + 1] !== $this->blockSize) {
                    $den ^= $this->alphaTo[$this->modNn($lambda[$i + 1] + $i * $root[$j])];
                }
            }

            // Apply error to data
            if ($num1 !== 0 && $loc[$j] >= $this->padding) {
                $data[$loc[$j] - $this->padding] = $data[$loc[$j] - $this->padding] ^ (
                    $this->alphaTo[
                        $this->modNn(
                            $this->indexOf[$num1] + $this->indexOf[$num2] + $this->blockSize - $this->indexOf[$den]
                        )
                    ]
                );
            }
        }

        if (null !== $erasures) {
            if (count($erasures) < $count) {
                $erasures->setSize($count);
            }

            for ($i = 0; $i < $count; $i++) {
                $erasures[$i] = $loc[$i];
            }
        }

        return $count;
    }

    /**
     * Computes $x % GF_SIZE, where GF_SIZE is 2**GF_BITS - 1, without a slow divide.
     */
    private function modNn(int $x) : int
    {
        while ($x >= $this->blockSize) {
            $x -= $this->blockSize;
            $x = ($x >> $this->symbolSize) + ($x & $this->blockSize);
        }

        return $x;
    }
}
<?php

declare(strict_types=1);

namespace BaconQrCode\Renderer;

use BaconQrCode\Encoder\ByteMatrix;
use BaconQrCode\Encoder\MatrixUtil;
use BaconQrCode\Encoder\QrCode;
use BaconQrCode\Exception\InvalidArgumentException;
use BaconQrCode\Exception\RuntimeException;
use BaconQrCode\Renderer\Color\Alpha;
use BaconQrCode\Renderer\Color\ColorInterface;
use BaconQrCode\Renderer\RendererStyle\EyeFill;
use BaconQrCode\Renderer\RendererStyle\Fill;
use GdImage;

final class GDLibRenderer implements RendererInterface
{
    private ?GdImage $image;

    /**
     * @var array<string, int>
     */
    private array $colors;

    public function __construct(
        private int $size,
        private int $margin = 4,
        private string $imageFormat = 'png',
        private int $compressionQuality = 9,
        private ?Fill $fill = null
    ) {
        if (! extension_loaded('gd') || ! function_exists('gd_info')) {
            throw new RuntimeException('You need to install the GD extension to use this back end');
        }

        if ($this->fill === null) {
            $this->fill = Fill::default();
        }
        if ($this->fill->hasGradientFill()) {
            throw new InvalidArgumentException('GDLibRenderer does not support gradients');
        }
    }

    /**
     * @throws InvalidArgumentException if matrix width doesn't match height
     */
    public function render(QrCode $qrCode): string
    {
        $matrix = $qrCode->getMatrix();
        $matrixSize = $matrix->getWidth();

        if ($matrixSize !== $matrix->getHeight()) {
            throw new InvalidArgumentException('Matrix must have the same width and height');
        }

        MatrixUtil::removePositionDetectionPatterns($matrix);
        $this->newImage();
        $this->draw($matrix);

        return $this->renderImage();
    }

    private function newImage(): void
    {
        $img = imagecreatetruecolor($this->size, $this->size);
        if ($img === false) {
            throw new RuntimeException('Failed to create image of that size');
        }

        $this->image = $img;
        imagealphablending($this->image, false);
        imagesavealpha($this->image, true);


        $bg = $this->getColor($this->fill->getBackgroundColor());
        imagefilledrectangle($this->image, 0, 0, $this->size, $this->size, $bg);
        imagealphablending($this->image, true);
    }

    private function draw(ByteMatrix $matrix): void
    {
        $matrixSize = $matrix->getWidth();

        $pointsOnSide = $matrix->getWidth() + $this->margin * 2;
        $pointInPx = $this->size / $pointsOnSide;

        $this->drawEye(0, 0, $pointInPx, $this->fill->getTopLeftEyeFill());
        $this->drawEye($matrixSize - 7, 0, $pointInPx, $this->fill->getTopRightEyeFill());
        $this->drawEye(0, $matrixSize - 7, $pointInPx, $this->fill->getBottomLeftEyeFill());

        $rows = $matrix->getArray()->toArray();
        $color = $this->getColor($this->fill->getForegroundColor());
        for ($y = 0; $y < $matrixSize; $y += 1) {
            for ($x = 0; $x < $matrixSize; $x += 1) {
                if (! $rows[$y][$x]) {
                    continue;
                }

                $points = $this->normalizePoints([
                    ($this->margin + $x) * $pointInPx, ($this->margin + $y) * $pointInPx,
                    ($this->margin + $x + 1) * $pointInPx, ($this->margin + $y) * $pointInPx,
                    ($this->margin + $x + 1) * $pointInPx, ($this->margin + $y + 1) * $pointInPx,
                    ($this->margin + $x) * $pointInPx, ($this->margin + $y + 1) * $pointInPx,
                ]);
                imagefilledpolygon($this->image, $points, $color);
            }
        }
    }

    private function drawEye(int $xOffset, int $yOffset, float $pointInPx, EyeFill $eyeFill): void
    {
        $internalColor = $this->getColor($eyeFill->inheritsInternalColor()
            ? $this->fill->getForegroundColor()
            : $eyeFill->getInternalColor());

        $externalColor = $this->getColor($eyeFill->inheritsExternalColor()
            ? $this->fill->getForegroundColor()
            : $eyeFill->getExternalColor());

        for ($y = 0; $y < 7; $y += 1) {
            for ($x = 0; $x < 7; $x += 1) {
                if ((($y === 1 || $y === 5) && $x > 0 && $x < 6) || (($x === 1 || $x === 5) && $y > 0 && $y < 6)) {
                    continue;
                }

                $points = $this->normalizePoints([
                    ($this->margin + $x + $xOffset) * $pointInPx, ($this->margin + $y + $yOffset) * $pointInPx,
                    ($this->margin + $x + $xOffset + 1) * $pointInPx, ($this->margin + $y + $yOffset) * $pointInPx,
                    ($this->margin + $x + $xOffset + 1) * $pointInPx, ($this->margin + $y + $yOffset + 1) * $pointInPx,
                    ($this->margin + $x + $xOffset) * $pointInPx, ($this->margin + $y + $yOffset + 1) * $pointInPx,
                ]);

                if ($y > 1 && $y < 5 && $x > 1 && $x < 5) {
                    imagefilledpolygon($this->image, $points, $internalColor);
                } else {
                    imagefilledpolygon($this->image, $points, $externalColor);
                }
            }
        }
    }

    /**
     * Normalize points will trim right and bottom line by 1 pixel.
     * Otherwise pixels of neighbors are overlapping which leads to issue with transparency and small QR codes.
     */
    private function normalizePoints(array $points): array
    {
        $maxX = $maxY = 0;
        for ($i = 0; $i < count($points); $i += 2) {
            // Do manual round as GD just removes decimal part
            $points[$i] = $newX = round($points[$i]);
            $points[$i + 1] = $newY = round($points[$i + 1]);

            $maxX = max($maxX, $newX);
            $maxY = max($maxY, $newY);
        }

        // Do trimming only if there are 4 points (8 coordinates), assumes this is square.

        for ($i = 0; $i < count($points); $i += 2) {
            $points[$i] = min($points[$i], $maxX - 1);
            $points[$i + 1] = min($points[$i + 1], $maxY - 1);
        }

        return $points;
    }

    private function renderImage(): string
    {
        ob_start();
        $quality = $this->compressionQuality;
        switch ($this->imageFormat) {
            case 'png':
                if ($quality > 9 || $quality < 0) {
                    $quality = 9;
                }
                imagepng($this->image, null, $quality);
                break;

            case 'gif':
                imagegif($this->image, null);
                break;

            case 'jpeg':
            case 'jpg':
                if ($quality > 100 || $quality < 0) {
                    $quality = 85;
                }
                imagejpeg($this->image, null, $quality);
                break;
            default:
                ob_end_clean();
                throw new InvalidArgumentException(
                    'Supported image formats are jpeg, png and gif, got: ' . $this->imageFormat
                );
        }

        $this->colors = [];
        $this->image = null;

        return ob_get_clean();
    }

    private function getColor(ColorInterface $color): int
    {
        $alpha = 100;

        if ($color instanceof Alpha) {
            $alpha = $color->getAlpha();
            $color = $color->getBaseColor();
        }

        $rgb = $color->toRgb();

        $colorKey = sprintf('%02X%02X%02X%02X', $rgb->getRed(), $rgb->getGreen(), $rgb->getBlue(), $alpha);

        if (! isset($this->colors[$colorKey])) {
            $colorId = imagecolorallocatealpha(
                $this->image,
                $rgb->getRed(),
                $rgb->getGreen(),
                $rgb->getBlue(),
                (int)((100 - $alpha) / 100 * 127) // Alpha for GD is in range 0 (opaque) - 127 (transparent)
            );

            if ($colorId === false) {
                throw new RuntimeException('Failed to create color: #' . $colorKey);
            }

            $this->colors[$colorKey] = $colorId;
        }

        return $this->colors[$colorKey];
    }
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Renderer;

use BaconQrCode\Encoder\MatrixUtil;
use BaconQrCode\Encoder\QrCode;
use BaconQrCode\Exception\InvalidArgumentException;
use BaconQrCode\Renderer\Image\ImageBackEndInterface;
use BaconQrCode\Renderer\Path\Path;
use BaconQrCode\Renderer\RendererStyle\EyeFill;
use BaconQrCode\Renderer\RendererStyle\RendererStyle;

final class ImageRenderer implements RendererInterface
{
    public function __construct(
        private readonly RendererStyle         $rendererStyle,
        private readonly ImageBackEndInterface $imageBackEnd
    ) {
    }

    /**
     * @throws InvalidArgumentException if matrix width doesn't match height
     */
    public function render(QrCode $qrCode) : string
    {
        $size = $this->rendererStyle->getSize();
        $margin = $this->rendererStyle->getMargin();
        $matrix = $qrCode->getMatrix();
        $matrixSize = $matrix->getWidth();

        if ($matrixSize !== $matrix->getHeight()) {
            throw new InvalidArgumentException('Matrix must have the same width and height');
        }

        $totalSize = $matrixSize + ($margin * 2);
        $moduleSize = $size / $totalSize;
        $fill = $this->rendererStyle->getFill();

        $this->imageBackEnd->new($size, $fill->getBackgroundColor());
        $this->imageBackEnd->scale((float) $moduleSize);
        $this->imageBackEnd->translate((float) $margin, (float) $margin);

        $module = $this->rendererStyle->getModule();
        $moduleMatrix = clone $matrix;
        MatrixUtil::removePositionDetectionPatterns($moduleMatrix);
        $modulePath = $this->drawEyes($matrixSize, $module->createPath($moduleMatrix));

        if ($fill->hasGradientFill()) {
            $this->imageBackEnd->drawPathWithGradient(
                $modulePath,
                $fill->getForegroundGradient(),
                0,
                0,
                $matrixSize,
                $matrixSize
            );
        } else {
            $this->imageBackEnd->drawPathWithColor($modulePath, $fill->getForegroundColor());
        }

        return $this->imageBackEnd->done();
    }

    private function drawEyes(int $matrixSize, Path $modulePath) : Path
    {
        $fill = $this->rendererStyle->getFill();

        $eye = $this->rendererStyle->getEye();
        $externalPath = $eye->getExternalPath();
        $internalPath = $eye->getInternalPath();

        $modulePath = $this->drawEye(
            $externalPath,
            $internalPath,
            $fill->getTopLeftEyeFill(),
            3.5,
            3.5,
            0,
            $modulePath
        );
        $modulePath = $this->drawEye(
            $externalPath,
            $internalPath,
            $fill->getTopRightEyeFill(),
            $matrixSize - 3.5,
            3.5,
            90,
            $modulePath
        );
        $modulePath = $this->drawEye(
            $externalPath,
            $internalPath,
            $fill->getBottomLeftEyeFill(),
            3.5,
            $matrixSize - 3.5,
            -90,
            $modulePath
        );

        return $modulePath;
    }

    private function drawEye(
        Path $externalPath,
        Path $internalPath,
        EyeFill $fill,
        float $xTranslation,
        float $yTranslation,
        int $rotation,
        Path $modulePath
    ) : Path {
        if ($fill->inheritsBothColors()) {
            return $modulePath
                ->append(
                    $externalPath->rotate($rotation)->translate($xTranslation, $yTranslation)
                )
                ->append(
                    $internalPath->rotate($rotation)->translate($xTranslation, $yTranslation)
                );
        }

        $this->imageBackEnd->push();
        $this->imageBackEnd->translate($xTranslation, $yTranslation);

        if (0 !== $rotation) {
            $this->imageBackEnd->rotate($rotation);
        }

        if ($fill->inheritsExternalColor()) {
            $modulePath = $modulePath->append(
                $externalPath->rotate($rotation)->translate($xTranslation, $yTranslation)
            );
        } else {
            $this->imageBackEnd->drawPathWithColor($externalPath, $fill->getExternalColor());
        }

        if ($fill->inheritsInternalColor()) {
            $modulePath = $modulePath->append(
                $internalPath->rotate($rotation)->translate($xTranslation, $yTranslation)
            );
        } else {
            $this->imageBackEnd->drawPathWithColor($internalPath, $fill->getInternalColor());
        }

        $this->imageBackEnd->pop();

        return $modulePath;
    }
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Renderer\RendererStyle;

use BaconQrCode\Exception\RuntimeException;
use BaconQrCode\Renderer\Color\ColorInterface;
use BaconQrCode\Renderer\Color\Gray;

final class Fill
{
    private static ?Fill $default = null;

    private function __construct(
        private readonly ColorInterface  $backgroundColor,
        private readonly ?ColorInterface $foregroundColor,
        private readonly ?Gradient       $foregroundGradient,
        private readonly EyeFill         $topLeftEyeFill,
        private readonly EyeFill         $topRightEyeFill,
        private readonly EyeFill $bottomLeftEyeFill
    ) {
    }

    public static function default() : self
    {
        return self::$default ?: self::$default = self::uniformColor(new Gray(100), new Gray(0));
    }

    public static function withForegroundColor(
        ColorInterface $backgroundColor,
        ColorInterface $foregroundColor,
        EyeFill $topLeftEyeFill,
        EyeFill $topRightEyeFill,
        EyeFill $bottomLeftEyeFill
    ) : self {
        return new self(
            $backgroundColor,
            $foregroundColor,
            null,
            $topLeftEyeFill,
            $topRightEyeFill,
            $bottomLeftEyeFill
        );
    }

    public static function withForegroundGradient(
        ColorInterface $backgroundColor,
        Gradient $foregroundGradient,
        EyeFill $topLeftEyeFill,
        EyeFill $topRightEyeFill,
        EyeFill $bottomLeftEyeFill
    ) : self {
        return new self(
            $backgroundColor,
            null,
            $foregroundGradient,
            $topLeftEyeFill,
            $topRightEyeFill,
            $bottomLeftEyeFill
        );
    }

    public static function uniformColor(ColorInterface $backgroundColor, ColorInterface $foregroundColor) : self
    {
        return new self(
            $backgroundColor,
            $foregroundColor,
            null,
            EyeFill::inherit(),
            EyeFill::inherit(),
            EyeFill::inherit()
        );
    }

    public static function uniformGradient(ColorInterface $backgroundColor, Gradient $foregroundGradient) : self
    {
        return new self(
            $backgroundColor,
            null,
            $foregroundGradient,
            EyeFill::inherit(),
            EyeFill::inherit(),
            EyeFill::inherit()
        );
    }

    public function hasGradientFill() : bool
    {
        return null !== $this->foregroundGradient;
    }

    public function getBackgroundColor() : ColorInterface
    {
        return $this->backgroundColor;
    }

    public function getForegroundColor() : ColorInterface
    {
        if (null === $this->foregroundColor) {
            throw new RuntimeException('Fill uses a gradient, thus no foreground color is available');
        }

        return $this->foregroundColor;
    }

    public function getForegroundGradient() : Gradient
    {
        if (null === $this->foregroundGradient) {
            throw new RuntimeException('Fill uses a single color, thus no foreground gradient is available');
        }

        return $this->foregroundGradient;
    }

    public function getTopLeftEyeFill() : EyeFill
    {
        return $this->topLeftEyeFill;
    }

    public function getTopRightEyeFill() : EyeFill
    {
        return $this->topRightEyeFill;
    }

    public function getBottomLeftEyeFill() : EyeFill
    {
        return $this->bottomLeftEyeFill;
    }
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Renderer\RendererStyle;

use DASPRiD\Enum\AbstractEnum;

/**
 * @method static self VERTICAL()
 * @method static self HORIZONTAL()
 * @method static self DIAGONAL()
 * @method static self INVERSE_DIAGONAL()
 * @method static self RADIAL()
 */
final class GradientType extends AbstractEnum
{
    protected const VERTICAL = null;
    protected const HORIZONTAL = null;
    protected const DIAGONAL = null;
    protected const INVERSE_DIAGONAL = null;
    protected const RADIAL = null;
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Renderer\RendererStyle;

use BaconQrCode\Exception\RuntimeException;
use BaconQrCode\Renderer\Color\ColorInterface;

final class EyeFill
{
    private static ?EyeFill $inherit = null;

    public function __construct(
        private readonly ?ColorInterface $externalColor,
        private readonly ?ColorInterface $internalColor
    ) {
    }

    public static function uniform(ColorInterface $color) : self
    {
        return new self($color, $color);
    }

    public static function inherit() : self
    {
        return self::$inherit ?: self::$inherit = new self(null, null);
    }

    public function inheritsBothColors() : bool
    {
        return null === $this->externalColor && null === $this->internalColor;
    }

    public function inheritsExternalColor() : bool
    {
        return null === $this->externalColor;
    }

    public function inheritsInternalColor() : bool
    {
        return null === $this->internalColor;
    }

    public function getExternalColor() : ColorInterface
    {
        if (null === $this->externalColor) {
            throw new RuntimeException('External eye color inherits foreground color');
        }

        return $this->externalColor;
    }

    public function getInternalColor() : ColorInterface
    {
        if (null === $this->internalColor) {
            throw new RuntimeException('Internal eye color inherits foreground color');
        }

        return $this->internalColor;
    }
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Renderer\RendererStyle;

use BaconQrCode\Renderer\Color\ColorInterface;

final class Gradient
{
    public function __construct(
        private readonly ColorInterface $startColor,
        private readonly ColorInterface $endColor,
        private readonly GradientType   $type
    ) {
    }

    public function getStartColor() : ColorInterface
    {
        return $this->startColor;
    }

    public function getEndColor() : ColorInterface
    {
        return $this->endColor;
    }

    public function getType() : GradientType
    {
        return $this->type;
    }
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Renderer\RendererStyle;

use BaconQrCode\Renderer\Eye\EyeInterface;
use BaconQrCode\Renderer\Eye\ModuleEye;
use BaconQrCode\Renderer\Module\ModuleInterface;
use BaconQrCode\Renderer\Module\SquareModule;

final class RendererStyle
{
    private ModuleInterface $module;

    private EyeInterface|null $eye;

    private Fill $fill;

    public function __construct(
        private int $size,
        private int $margin = 4,
        ?ModuleInterface $module = null,
        ?EyeInterface $eye = null,
        ?Fill $fill = null
    ) {
        $this->module = $module ?: SquareModule::instance();
        $this->eye = $eye ?: new ModuleEye($this->module);
        $this->fill = $fill ?: Fill::default();
    }

    public function withSize(int $size) : self
    {
        $style = clone $this;
        $style->size = $size;
        return $style;
    }

    public function withMargin(int $margin) : self
    {
        $style = clone $this;
        $style->margin = $margin;
        return $style;
    }

    public function getSize() : int
    {
        return $this->size;
    }

    public function getMargin() : int
    {
        return $this->margin;
    }

    public function getModule() : ModuleInterface
    {
        return $this->module;
    }

    public function getEye() : EyeInterface
    {
        return $this->eye;
    }

    public function getFill() : Fill
    {
        return $this->fill;
    }
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Renderer\Image;

use BaconQrCode\Exception\RuntimeException;
use BaconQrCode\Renderer\Color\Alpha;
use BaconQrCode\Renderer\Color\ColorInterface;
use BaconQrCode\Renderer\Path\Close;
use BaconQrCode\Renderer\Path\Curve;
use BaconQrCode\Renderer\Path\EllipticArc;
use BaconQrCode\Renderer\Path\Line;
use BaconQrCode\Renderer\Path\Move;
use BaconQrCode\Renderer\Path\Path;
use BaconQrCode\Renderer\RendererStyle\Gradient;
use BaconQrCode\Renderer\RendererStyle\GradientType;
use XMLWriter;

final class SvgImageBackEnd implements ImageBackEndInterface
{
    private const PRECISION = 3;

    private ?XMLWriter $xmlWriter;

    private ?array $stack;

    private ?int $currentStack;

    private ?int $gradientCount;

    public function __construct()
    {
        if (! class_exists(XMLWriter::class)) {
            throw new RuntimeException(
                'You need to install the libxml extension and enable the xmlwriter extension to use this back end'
            );
        }
    }

    public function new(int $size, ColorInterface $backgroundColor) : void
    {
        $this->xmlWriter = new XMLWriter();
        $this->xmlWriter->openMemory();

        $this->xmlWriter->startDocument('1.0', 'UTF-8');
        $this->xmlWriter->startElement('svg');
        $this->xmlWriter->writeAttribute('xmlns', 'http://www.w3.org/2000/svg');
        $this->xmlWriter->writeAttribute('version', '1.1');
        $this->xmlWriter->writeAttribute('width', (string) $size);
        $this->xmlWriter->writeAttribute('height', (string) $size);
        $this->xmlWriter->writeAttribute('viewBox', '0 0 '. $size . ' ' . $size);

        $this->gradientCount = 0;
        $this->currentStack = 0;
        $this->stack[0] = 0;

        $alpha = 1;

        if ($backgroundColor instanceof Alpha) {
            $alpha = $backgroundColor->getAlpha() / 100;
        }

        if (0 === $alpha) {
            return;
        }

        $this->xmlWriter->startElement('rect');
        $this->xmlWriter->writeAttribute('x', '0');
        $this->xmlWriter->writeAttribute('y', '0');
        $this->xmlWriter->writeAttribute('width', (string) $size);
        $this->xmlWriter->writeAttribute('height', (string) $size);
        $this->xmlWriter->writeAttribute('fill', $this->getColorString($backgroundColor));

        if ($alpha < 1) {
            $this->xmlWriter->writeAttribute('fill-opacity', (string) $alpha);
        }

        $this->xmlWriter->endElement();
    }

    public function scale(float $size) : void
    {
        if (null === $this->xmlWriter) {
            throw new RuntimeException('No image has been started');
        }

        $this->xmlWriter->startElement('g');
        $this->xmlWriter->writeAttribute(
            'transform',
            sprintf('scale(%s)', round($size, self::PRECISION))
        );
        ++$this->stack[$this->currentStack];
    }

    public function translate(float $x, float $y) : void
    {
        if (null === $this->xmlWriter) {
            throw new RuntimeException('No image has been started');
        }

        $this->xmlWriter->startElement('g');
        $this->xmlWriter->writeAttribute(
            'transform',
            sprintf('translate(%s,%s)', round($x, self::PRECISION), round($y, self::PRECISION))
        );
        ++$this->stack[$this->currentStack];
    }

    public function rotate(int $degrees) : void
    {
        if (null === $this->xmlWriter) {
            throw new RuntimeException('No image has been started');
        }

        $this->xmlWriter->startElement('g');
        $this->xmlWriter->writeAttribute('transform', sprintf('rotate(%d)', $degrees));
        ++$this->stack[$this->currentStack];
    }

    public function push() : void
    {
        if (null === $this->xmlWriter) {
            throw new RuntimeException('No image has been started');
        }

        $this->xmlWriter->startElement('g');
        $this->stack[] = 1;
        ++$this->currentStack;
    }

    public function pop() : void
    {
        if (null === $this->xmlWriter) {
            throw new RuntimeException('No image has been started');
        }

        for ($i = 0; $i < $this->stack[$this->currentStack]; ++$i) {
            $this->xmlWriter->endElement();
        }

        array_pop($this->stack);
        --$this->currentStack;
    }

    public function drawPathWithColor(Path $path, ColorInterface $color) : void
    {
        if (null === $this->xmlWriter) {
            throw new RuntimeException('No image has been started');
        }

        $alpha = 1;

        if ($color instanceof Alpha) {
            $alpha = $color->getAlpha() / 100;
        }

        $this->startPathElement($path);
        $this->xmlWriter->writeAttribute('fill', $this->getColorString($color));

        if ($alpha < 1) {
            $this->xmlWriter->writeAttribute('fill-opacity', (string) $alpha);
        }

        $this->xmlWriter->endElement();
    }

    public function drawPathWithGradient(
        Path $path,
        Gradient $gradient,
        float $x,
        float $y,
        float $width,
        float $height
    ) : void {
        if (null === $this->xmlWriter) {
            throw new RuntimeException('No image has been started');
        }

        $gradientId = $this->createGradientFill($gradient, $x, $y, $width, $height);
        $this->startPathElement($path);
        $this->xmlWriter->writeAttribute('fill', 'url(#' . $gradientId . ')');
        $this->xmlWriter->endElement();
    }

    public function done() : string
    {
        if (null === $this->xmlWriter) {
            throw new RuntimeException('No image has been started');
        }

        foreach ($this->stack as $openElements) {
            for ($i = $openElements; $i > 0; --$i) {
                $this->xmlWriter->endElement();
            }
        }

        $this->xmlWriter->endDocument();
        $blob = $this->xmlWriter->outputMemory(true);
        $this->xmlWriter = null;
        $this->stack = null;
        $this->currentStack = null;
        $this->gradientCount = null;

        return $blob;
    }

    private function startPathElement(Path $path) : void
    {
        $pathData = [];

        foreach ($path as $op) {
            switch (true) {
                case $op instanceof Move:
                    $pathData[] = sprintf(
                        'M%s %s',
                        round($op->getX(), self::PRECISION),
                        round($op->getY(), self::PRECISION)
                    );
                    break;

                case $op instanceof Line:
                    $pathData[] = sprintf(
                        'L%s %s',
                        round($op->getX(), self::PRECISION),
                        round($op->getY(), self::PRECISION)
                    );
                    break;

                case $op instanceof EllipticArc:
                    $pathData[] = sprintf(
                        'A%s %s %s %u %u %s %s',
                        round($op->getXRadius(), self::PRECISION),
                        round($op->getYRadius(), self::PRECISION),
                        round($op->getXAxisAngle(), self::PRECISION),
                        $op->isLargeArc(),
                        $op->isSweep(),
                        round($op->getX(), self::PRECISION),
                        round($op->getY(), self::PRECISION)
                    );
                    break;

                case $op instanceof Curve:
                    $pathData[] = sprintf(
                        'C%s %s %s %s %s %s',
                        round($op->getX1(), self::PRECISION),
                        round($op->getY1(), self::PRECISION),
                        round($op->getX2(), self::PRECISION),
                        round($op->getY2(), self::PRECISION),
                        round($op->getX3(), self::PRECISION),
                        round($op->getY3(), self::PRECISION)
                    );
                    break;

                case $op instanceof Close:
                    $pathData[] = 'Z';
                    break;

                default:
                    throw new RuntimeException('Unexpected draw operation: ' . get_class($op));
            }
        }

        $this->xmlWriter->startElement('path');
        $this->xmlWriter->writeAttribute('fill-rule', 'evenodd');
        $this->xmlWriter->writeAttribute('d', implode('', $pathData));
    }

    private function createGradientFill(Gradient $gradient, float $x, float $y, float $width, float $height) : string
    {
        $this->xmlWriter->startElement('defs');

        $startColor = $gradient->getStartColor();
        $endColor = $gradient->getEndColor();

        if ($gradient->getType() === GradientType::RADIAL()) {
            $this->xmlWriter->startElement('radialGradient');
        } else {
            $this->xmlWriter->startElement('linearGradient');
        }

        $this->xmlWriter->writeAttribute('gradientUnits', 'userSpaceOnUse');

        switch ($gradient->getType()) {
            case GradientType::HORIZONTAL():
                $this->xmlWriter->writeAttribute('x1', (string) round($x, self::PRECISION));
                $this->xmlWriter->writeAttribute('y1', (string) round($y, self::PRECISION));
                $this->xmlWriter->writeAttribute('x2', (string) round($x + $width, self::PRECISION));
                $this->xmlWriter->writeAttribute('y2', (string) round($y, self::PRECISION));
                break;

            case GradientType::VERTICAL():
                $this->xmlWriter->writeAttribute('x1', (string) round($x, self::PRECISION));
                $this->xmlWriter->writeAttribute('y1', (string) round($y, self::PRECISION));
                $this->xmlWriter->writeAttribute('x2', (string) round($x, self::PRECISION));
                $this->xmlWriter->writeAttribute('y2', (string) round($y + $height, self::PRECISION));
                break;

            case GradientType::DIAGONAL():
                $this->xmlWriter->writeAttribute('x1', (string) round($x, self::PRECISION));
                $this->xmlWriter->writeAttribute('y1', (string) round($y, self::PRECISION));
                $this->xmlWriter->writeAttribute('x2', (string) round($x + $width, self::PRECISION));
                $this->xmlWriter->writeAttribute('y2', (string) round($y + $height, self::PRECISION));
                break;

            case GradientType::INVERSE_DIAGONAL():
                $this->xmlWriter->writeAttribute('x1', (string) round($x, self::PRECISION));
                $this->xmlWriter->writeAttribute('y1', (string) round($y + $height, self::PRECISION));
                $this->xmlWriter->writeAttribute('x2', (string) round($x + $width, self::PRECISION));
                $this->xmlWriter->writeAttribute('y2', (string) round($y, self::PRECISION));
                break;

            case GradientType::RADIAL():
                $this->xmlWriter->writeAttribute('cx', (string) round(($x + $width) / 2, self::PRECISION));
                $this->xmlWriter->writeAttribute('cy', (string) round(($y + $height) / 2, self::PRECISION));
                $this->xmlWriter->writeAttribute('r', (string) round(max($width, $height) / 2, self::PRECISION));
                break;
        }

        $toBeHashed = $this->getColorString($startColor) . $this->getColorString($endColor) . $gradient->getType();
        if ($startColor instanceof Alpha) {
            $toBeHashed .= (string) $startColor->getAlpha();
        }
        $id = sprintf('g%d-%s', ++$this->gradientCount, hash('xxh3', $toBeHashed));
        $this->xmlWriter->writeAttribute('id', $id);

        $this->xmlWriter->startElement('stop');
        $this->xmlWriter->writeAttribute('offset', '0%');
        $this->xmlWriter->writeAttribute('stop-color', $this->getColorString($startColor));

        if ($startColor instanceof Alpha) {
            $this->xmlWriter->writeAttribute('stop-opacity', (string) $startColor->getAlpha());
        }

        $this->xmlWriter->endElement();

        $this->xmlWriter->startElement('stop');
        $this->xmlWriter->writeAttribute('offset', '100%');
        $this->xmlWriter->writeAttribute('stop-color', $this->getColorString($endColor));

        if ($endColor instanceof Alpha) {
            $this->xmlWriter->writeAttribute('stop-opacity', (string) $endColor->getAlpha());
        }

        $this->xmlWriter->endElement();

        $this->xmlWriter->endElement();
        $this->xmlWriter->endElement();

        return $id;
    }

    private function getColorString(ColorInterface $color) : string
    {
        $color = $color->toRgb();

        return sprintf(
            '#%02x%02x%02x',
            $color->getRed(),
            $color->getGreen(),
            $color->getBlue()
        );
    }
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Renderer\Image;

final class TransformationMatrix
{
    /**
     * @var float[]
     */
    private array $values;

    public function __construct()
    {
        $this->values = [1, 0, 0, 1, 0, 0];
    }

    public function multiply(self $other) : self
    {
        $matrix = new self();
        $matrix->values[0] = $this->values[0] * $other->values[0] + $this->values[2] * $other->values[1];
        $matrix->values[1] = $this->values[1] * $other->values[0] + $this->values[3] * $other->values[1];
        $matrix->values[2] = $this->values[0] * $other->values[2] + $this->values[2] * $other->values[3];
        $matrix->values[3] = $this->values[1] * $other->values[2] + $this->values[3] * $other->values[3];
        $matrix->values[4] = $this->values[0] * $other->values[4] + $this->values[2] * $other->values[5]
            + $this->values[4];
        $matrix->values[5] = $this->values[1] * $other->values[4] + $this->values[3] * $other->values[5]
            + $this->values[5];

        return $matrix;
    }

    public static function scale(float $size) : self
    {
        $matrix = new self();
        $matrix->values = [$size, 0, 0, $size, 0, 0];
        return $matrix;
    }

    public static function translate(float $x, float $y) : self
    {
        $matrix = new self();
        $matrix->values = [1, 0, 0, 1, $x, $y];
        return $matrix;
    }

    public static function rotate(int $degrees) : self
    {
        $matrix = new self();
        $rad = deg2rad($degrees);
        $matrix->values = [cos($rad), sin($rad), -sin($rad), cos($rad), 0, 0];
        return $matrix;
    }


    /**
     * Applies this matrix onto a point and returns the resulting viewport point.
     *
     * @return float[]
     */
    public function apply(float $x, float $y) : array
    {
        return [
            $x * $this->values[0] + $y * $this->values[2] + $this->values[4],
            $x * $this->values[1] + $y * $this->values[3] + $this->values[5],
        ];
    }
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Renderer\Image;

use BaconQrCode\Exception\RuntimeException;
use BaconQrCode\Renderer\Color\Alpha;
use BaconQrCode\Renderer\Color\Cmyk;
use BaconQrCode\Renderer\Color\ColorInterface;
use BaconQrCode\Renderer\Color\Gray;
use BaconQrCode\Renderer\Color\Rgb;
use BaconQrCode\Renderer\Path\Close;
use BaconQrCode\Renderer\Path\Curve;
use BaconQrCode\Renderer\Path\EllipticArc;
use BaconQrCode\Renderer\Path\Line;
use BaconQrCode\Renderer\Path\Move;
use BaconQrCode\Renderer\Path\Path;
use BaconQrCode\Renderer\RendererStyle\Gradient;
use BaconQrCode\Renderer\RendererStyle\GradientType;
use Imagick;
use ImagickDraw;
use ImagickPixel;

final class ImagickImageBackEnd implements ImageBackEndInterface
{
    private string $imageFormat;

    private int $compressionQuality;

    private ?Imagick $image;

    private ?ImagickDraw $draw;

    private ?int $gradientCount;

    /**
     * @var TransformationMatrix[]|null
     */
    private ?array $matrices;

    private ?int $matrixIndex;

    private bool $antialias;

    public function __construct(string $imageFormat = 'png', int $compressionQuality = 100, bool $antialias = true)
    {
        if (! class_exists(Imagick::class)) {
            throw new RuntimeException('You need to install the imagick extension to use this back end');
        }

        $this->imageFormat = $imageFormat;
        $this->compressionQuality = $compressionQuality;
        $this->antialias = $antialias;
    }

    public function new(int $size, ColorInterface $backgroundColor) : void
    {
        $this->image = new Imagick();
        $this->image->newImage($size, $size, $this->getColorPixel($backgroundColor));
        $this->image->setImageFormat($this->imageFormat);
        $this->image->setCompressionQuality($this->compressionQuality);
        $this->draw = new ImagickDraw();

        if (! $this->antialias) {
            $this->image->setAntiAlias(false);
            $this->draw->setStrokeAntialias(false);
        }

        $this->gradientCount = 0;
        $this->matrices = [new TransformationMatrix()];
        $this->matrixIndex = 0;
    }

    public function scale(float $size) : void
    {
        if (null === $this->draw) {
            throw new RuntimeException('No image has been started');
        }

        $this->draw->scale($size, $size);
        $this->matrices[$this->matrixIndex] = $this->matrices[$this->matrixIndex]
            ->multiply(TransformationMatrix::scale($size));
    }

    public function translate(float $x, float $y) : void
    {
        if (null === $this->draw) {
            throw new RuntimeException('No image has been started');
        }

        $this->draw->translate($x, $y);
        $this->matrices[$this->matrixIndex] = $this->matrices[$this->matrixIndex]
            ->multiply(TransformationMatrix::translate($x, $y));
    }

    public function rotate(int $degrees) : void
    {
        if (null === $this->draw) {
            throw new RuntimeException('No image has been started');
        }

        $this->draw->rotate($degrees);
        $this->matrices[$this->matrixIndex] = $this->matrices[$this->matrixIndex]
            ->multiply(TransformationMatrix::rotate($degrees));
    }

    public function push() : void
    {
        if (null === $this->draw) {
            throw new RuntimeException('No image has been started');
        }

        $this->draw->push();
        $this->matrices[++$this->matrixIndex] = $this->matrices[$this->matrixIndex - 1];
    }

    public function pop() : void
    {
        if (null === $this->draw) {
            throw new RuntimeException('No image has been started');
        }

        $this->draw->pop();
        unset($this->matrices[$this->matrixIndex--]);
    }

    public function drawPathWithColor(Path $path, ColorInterface $color) : void
    {
        if (null === $this->draw) {
            throw new RuntimeException('No image has been started');
        }

        $this->draw->setFillColor($this->getColorPixel($color));
        $this->drawPath($path);
    }

    public function drawPathWithGradient(
        Path $path,
        Gradient $gradient,
        float $x,
        float $y,
        float $width,
        float $height
    ) : void {
        if (null === $this->draw) {
            throw new RuntimeException('No image has been started');
        }

        $this->draw->setFillPatternURL('#' . $this->createGradientFill($gradient, $x, $y, $width, $height));
        $this->drawPath($path);
    }

    public function done() : string
    {
        if (null === $this->draw) {
            throw new RuntimeException('No image has been started');
        }

        $this->image->drawImage($this->draw);
        $blob = $this->image->getImageBlob();
        $this->draw->clear();
        $this->image->clear();
        $this->draw = null;
        $this->image = null;
        $this->gradientCount = null;

        return $blob;
    }

    private function drawPath(Path $path) : void
    {
        $this->draw->pathStart();

        foreach ($path as $op) {
            switch (true) {
                case $op instanceof Move:
                    $this->draw->pathMoveToAbsolute($op->getX(), $op->getY());
                    break;

                case $op instanceof Line:
                    $this->draw->pathLineToAbsolute($op->getX(), $op->getY());
                    break;

                case $op instanceof EllipticArc:
                    $this->draw->pathEllipticArcAbsolute(
                        $op->getXRadius(),
                        $op->getYRadius(),
                        $op->getXAxisAngle(),
                        $op->isLargeArc(),
                        $op->isSweep(),
                        $op->getX(),
                        $op->getY()
                    );
                    break;

                case $op instanceof Curve:
                    $this->draw->pathCurveToAbsolute(
                        $op->getX1(),
                        $op->getY1(),
                        $op->getX2(),
                        $op->getY2(),
                        $op->getX3(),
                        $op->getY3()
                    );
                    break;

                case $op instanceof Close:
                    $this->draw->pathClose();
                    break;

                default:
                    throw new RuntimeException('Unexpected draw operation: ' . get_class($op));
            }
        }

        $this->draw->pathFinish();
    }

    private function createGradientFill(Gradient $gradient, float $x, float $y, float $width, float $height) : string
    {
        list($width, $height) = $this->matrices[$this->matrixIndex]->apply($width, $height);

        $startColor = $this->getColorPixel($gradient->getStartColor())->getColorAsString();
        $endColor = $this->getColorPixel($gradient->getEndColor())->getColorAsString();
        $gradientImage = new Imagick();

        switch ($gradient->getType()) {
            case GradientType::HORIZONTAL():
                $gradientImage->newPseudoImage((int) $height, (int) $width, sprintf(
                    'gradient:%s-%s',
                    $startColor,
                    $endColor
                ));
                $gradientImage->rotateImage('transparent', -90);
                break;

            case GradientType::VERTICAL():
                $gradientImage->newPseudoImage((int) $width, (int) $height, sprintf(
                    'gradient:%s-%s',
                    $startColor,
                    $endColor
                ));
                break;

            case GradientType::DIAGONAL():
            case GradientType::INVERSE_DIAGONAL():
                $gradientImage->newPseudoImage((int) ($width * sqrt(2)), (int) ($height * sqrt(2)), sprintf(
                    'gradient:%s-%s',
                    $startColor,
                    $endColor
                ));

                if (GradientType::DIAGONAL() === $gradient->getType()) {
                    $gradientImage->rotateImage('transparent', -45);
                } else {
                    $gradientImage->rotateImage('transparent', -135);
                }

                $rotatedWidth = $gradientImage->getImageWidth();
                $rotatedHeight = $gradientImage->getImageHeight();

                $gradientImage->setImagePage($rotatedWidth, $rotatedHeight, 0, 0);
                $gradientImage->cropImage(
                    intdiv($rotatedWidth, 2) - 2,
                    intdiv($rotatedHeight, 2) - 2,
                    intdiv($rotatedWidth, 4) + 1,
                    intdiv($rotatedWidth, 4) + 1
                );
                break;

            case GradientType::RADIAL():
                $gradientImage->newPseudoImage((int) $width, (int) $height, sprintf(
                    'radial-gradient:%s-%s',
                    $startColor,
                    $endColor
                ));
                break;
        }

        $id = sprintf('g%d', ++$this->gradientCount);
        $this->draw->pushPattern($id, 0, 0, $width, $height);
        $this->draw->composite(Imagick::COMPOSITE_COPY, 0, 0, $width, $height, $gradientImage);
        $this->draw->popPattern();
        return $id;
    }

    private function getColorPixel(ColorInterface $color) : ImagickPixel
    {
        $alpha = 100;

        if ($color instanceof Alpha) {
            $alpha = $color->getAlpha();
            $color = $color->getBaseColor();
        }

        if ($color instanceof Rgb) {
            return new ImagickPixel(sprintf(
                'rgba(%d, %d, %d, %F)',
                $color->getRed(),
                $color->getGreen(),
                $color->getBlue(),
                $alpha / 100
            ));
        }

        if ($color instanceof Cmyk) {
            return new ImagickPixel(sprintf(
                'cmyka(%d, %d, %d, %d, %F)',
                $color->getCyan(),
                $color->getMagenta(),
                $color->getYellow(),
                $color->getBlack(),
                $alpha / 100
            ));
        }

        if ($color instanceof Gray) {
            return new ImagickPixel(sprintf(
                'graya(%d%%, %F)',
                $color->getGray(),
                $alpha / 100
            ));
        }

        return $this->getColorPixel(new Alpha($alpha, $color->toRgb()));
    }
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Renderer\Image;

use BaconQrCode\Exception\RuntimeException;
use BaconQrCode\Renderer\Color\Alpha;
use BaconQrCode\Renderer\Color\Cmyk;
use BaconQrCode\Renderer\Color\ColorInterface;
use BaconQrCode\Renderer\Color\Gray;
use BaconQrCode\Renderer\Color\Rgb;
use BaconQrCode\Renderer\Path\Close;
use BaconQrCode\Renderer\Path\Curve;
use BaconQrCode\Renderer\Path\EllipticArc;
use BaconQrCode\Renderer\Path\Line;
use BaconQrCode\Renderer\Path\Move;
use BaconQrCode\Renderer\Path\Path;
use BaconQrCode\Renderer\RendererStyle\Gradient;
use BaconQrCode\Renderer\RendererStyle\GradientType;

final class EpsImageBackEnd implements ImageBackEndInterface
{
    private const PRECISION = 3;

    private ?string $eps;

    public function new(int $size, ColorInterface $backgroundColor) : void
    {
        $this->eps = "%!PS-Adobe-3.0 EPSF-3.0\n"
            . "%%Creator: BaconQrCode\n"
            . sprintf("%%%%BoundingBox: 0 0 %d %d \n", $size, $size)
            . "%%BeginProlog\n"
            . "save\n"
            . "50 dict begin\n"
            . "/q { gsave } bind def\n"
            . "/Q { grestore } bind def\n"
            . "/s { scale } bind def\n"
            . "/t { translate } bind def\n"
            . "/r { rotate } bind def\n"
            . "/n { newpath } bind def\n"
            . "/m { moveto } bind def\n"
            . "/l { lineto } bind def\n"
            . "/c { curveto } bind def\n"
            . "/z { closepath } bind def\n"
            . "/f { eofill } bind def\n"
            . "/rgb { setrgbcolor } bind def\n"
            . "/cmyk { setcmykcolor } bind def\n"
            . "/gray { setgray } bind def\n"
            . "%%EndProlog\n"
            . "1 -1 s\n"
            . sprintf("0 -%d t\n", $size);

        if ($backgroundColor instanceof Alpha && 0 === $backgroundColor->getAlpha()) {
            return;
        }

        $this->eps .= wordwrap(
            '0 0 m'
            . sprintf(' %s 0 l', (string) $size)
            . sprintf(' %s %s l', (string) $size, (string) $size)
            . sprintf(' 0 %s l', (string) $size)
            . ' z'
            . ' ' .$this->getColorSetString($backgroundColor) . " f\n",
            75,
            "\n "
        );
    }

    public function scale(float $size) : void
    {
        if (null === $this->eps) {
            throw new RuntimeException('No image has been started');
        }

        $this->eps .= sprintf("%1\$s %1\$s s\n", round($size, self::PRECISION));
    }

    public function translate(float $x, float $y) : void
    {
        if (null === $this->eps) {
            throw new RuntimeException('No image has been started');
        }

        $this->eps .= sprintf("%s %s t\n", round($x, self::PRECISION), round($y, self::PRECISION));
    }

    public function rotate(int $degrees) : void
    {
        if (null === $this->eps) {
            throw new RuntimeException('No image has been started');
        }

        $this->eps .= sprintf("%d r\n", $degrees);
    }

    public function push() : void
    {
        if (null === $this->eps) {
            throw new RuntimeException('No image has been started');
        }

        $this->eps .= "q\n";
    }

    public function pop() : void
    {
        if (null === $this->eps) {
            throw new RuntimeException('No image has been started');
        }

        $this->eps .= "Q\n";
    }

    public function drawPathWithColor(Path $path, ColorInterface $color) : void
    {
        if (null === $this->eps) {
            throw new RuntimeException('No image has been started');
        }

        $fromX = 0;
        $fromY = 0;
        $this->eps .= wordwrap(
            'n '
            . $this->drawPathOperations($path, $fromX, $fromY)
            . ' ' . $this->getColorSetString($color) . " f\n",
            75,
            "\n "
        );
    }

    public function drawPathWithGradient(
        Path $path,
        Gradient $gradient,
        float $x,
        float $y,
        float $width,
        float $height
    ) : void {
        if (null === $this->eps) {
            throw new RuntimeException('No image has been started');
        }

        $fromX = 0;
        $fromY = 0;
        $this->eps .= wordwrap(
            'q n ' . $this->drawPathOperations($path, $fromX, $fromY) . "\n",
            75,
            "\n "
        );

        $this->createGradientFill($gradient, $x, $y, $width, $height);
    }

    public function done() : string
    {
        if (null === $this->eps) {
            throw new RuntimeException('No image has been started');
        }

        $this->eps .= "%%TRAILER\nend restore\n%%EOF";
        $blob = $this->eps;
        $this->eps = null;

        return $blob;
    }

    private function drawPathOperations(Iterable $ops, &$fromX, &$fromY) : string
    {
        $pathData = [];

        foreach ($ops as $op) {
            switch (true) {
                case $op instanceof Move:
                    $fromX = $toX = round($op->getX(), self::PRECISION);
                    $fromY = $toY = round($op->getY(), self::PRECISION);
                    $pathData[] = sprintf('%s %s m', $toX, $toY);
                    break;

                case $op instanceof Line:
                    $fromX = $toX = round($op->getX(), self::PRECISION);
                    $fromY = $toY = round($op->getY(), self::PRECISION);
                    $pathData[] = sprintf('%s %s l', $toX, $toY);
                    break;

                case $op instanceof EllipticArc:
                    $pathData[] = $this->drawPathOperations($op->toCurves($fromX, $fromY), $fromX, $fromY);
                    break;

                case $op instanceof Curve:
                    $x1 = round($op->getX1(), self::PRECISION);
                    $y1 = round($op->getY1(), self::PRECISION);
                    $x2 = round($op->getX2(), self::PRECISION);
                    $y2 = round($op->getY2(), self::PRECISION);
                    $fromX = $x3 = round($op->getX3(), self::PRECISION);
                    $fromY = $y3 = round($op->getY3(), self::PRECISION);
                    $pathData[] = sprintf('%s %s %s %s %s %s c', $x1, $y1, $x2, $y2, $x3, $y3);
                    break;

                case $op instanceof Close:
                    $pathData[] = 'z';
                    break;

                default:
                    throw new RuntimeException('Unexpected draw operation: ' . get_class($op));
            }
        }

        return implode(' ', $pathData);
    }

    private function createGradientFill(Gradient $gradient, float $x, float $y, float $width, float $height) : void
    {
        $startColor = $gradient->getStartColor();
        $endColor = $gradient->getEndColor();

        if ($startColor instanceof Alpha) {
            $startColor = $startColor->getBaseColor();
        }

        $startColorType = get_class($startColor);

        if (! in_array($startColorType, [Rgb::class, Cmyk::class, Gray::class])) {
            $startColorType = Cmyk::class;
            $startColor = $startColor->toCmyk();
        }

        if (get_class($endColor) !== $startColorType) {
            switch ($startColorType) {
                case Cmyk::class:
                    $endColor = $endColor->toCmyk();
                    break;

                case Rgb::class:
                    $endColor = $endColor->toRgb();
                    break;

                case Gray::class:
                    $endColor = $endColor->toGray();
                    break;
            }
        }

        $this->eps .= "eoclip\n<<\n";

        if ($gradient->getType() === GradientType::RADIAL()) {
            $this->eps .= " /ShadingType 3\n";
        } else {
            $this->eps .= " /ShadingType 2\n";
        }

        $this->eps .= " /Extend [ true true ]\n"
            . " /AntiAlias true\n";

        switch ($startColorType) {
            case Cmyk::class:
                $this->eps .= " /ColorSpace /DeviceCMYK\n";
                break;

            case Rgb::class:
                $this->eps .= " /ColorSpace /DeviceRGB\n";
                break;

            case Gray::class:
                $this->eps .= " /ColorSpace /DeviceGray\n";
                break;
        }

        switch ($gradient->getType()) {
            case GradientType::HORIZONTAL():
                $this->eps .= sprintf(
                    " /Coords [ %s %s %s %s ]\n",
                    round($x, self::PRECISION),
                    round($y, self::PRECISION),
                    round($x + $width, self::PRECISION),
                    round($y, self::PRECISION)
                );
                break;

            case GradientType::VERTICAL():
                $this->eps .= sprintf(
                    " /Coords [ %s %s %s %s ]\n",
                    round($x, self::PRECISION),
                    round($y, self::PRECISION),
                    round($x, self::PRECISION),
                    round($y + $height, self::PRECISION)
                );
                break;

            case GradientType::DIAGONAL():
                $this->eps .= sprintf(
                    " /Coords [ %s %s %s %s ]\n",
                    round($x, self::PRECISION),
                    round($y, self::PRECISION),
                    round($x + $width, self::PRECISION),
                    round($y + $height, self::PRECISION)
                );
                break;

            case GradientType::INVERSE_DIAGONAL():
                $this->eps .= sprintf(
                    " /Coords [ %s %s %s %s ]\n",
                    round($x, self::PRECISION),
                    round($y + $height, self::PRECISION),
                    round($x + $width, self::PRECISION),
                    round($y, self::PRECISION)
                );
                break;

            case GradientType::RADIAL():
                $centerX = ($x + $width) / 2;
                $centerY = ($y + $height) / 2;

                $this->eps .= sprintf(
                    " /Coords [ %s %s 0 %s %s %s ]\n",
                    round($centerX, self::PRECISION),
                    round($centerY, self::PRECISION),
                    round($centerX, self::PRECISION),
                    round($centerY, self::PRECISION),
                    round(max($width, $height) / 2, self::PRECISION)
                );
                break;
        }

        $this->eps .= " /Function\n"
            . " <<\n"
            . "  /FunctionType 2\n"
            . "  /Domain [ 0 1 ]\n"
            . sprintf("  /C0 [ %s ]\n", $this->getColorString($startColor))
            . sprintf("  /C1 [ %s ]\n", $this->getColorString($endColor))
            . "  /N 1\n"
            . " >>\n>>\nshfill\nQ\n";
    }

    private function getColorSetString(ColorInterface $color) : string
    {
        if ($color instanceof Rgb) {
            return $this->getColorString($color) . ' rgb';
        }

        if ($color instanceof Cmyk) {
            return $this->getColorString($color) . ' cmyk';
        }

        if ($color instanceof Gray) {
            return $this->getColorString($color) . ' gray';
        }

        return $this->getColorSetString($color->toCmyk());
    }

    private function getColorString(ColorInterface $color) : string
    {
        if ($color instanceof Rgb) {
            return sprintf('%s %s %s', $color->getRed() / 255, $color->getGreen() / 255, $color->getBlue() / 255);
        }

        if ($color instanceof Cmyk) {
            return sprintf(
                '%s %s %s %s',
                $color->getCyan() / 100,
                $color->getMagenta() / 100,
                $color->getYellow() / 100,
                $color->getBlack() / 100
            );
        }

        if ($color instanceof Gray) {
            return sprintf('%s', $color->getGray() / 100);
        }

        return $this->getColorString($color->toCmyk());
    }
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Renderer\Image;

use BaconQrCode\Exception\RuntimeException;
use BaconQrCode\Renderer\Color\ColorInterface;
use BaconQrCode\Renderer\Path\Path;
use BaconQrCode\Renderer\RendererStyle\Gradient;

/**
 * Interface for back ends able to to produce path based images.
 */
interface ImageBackEndInterface
{
    /**
     * Starts a new image.
     *
     * If a previous image was already started, previous data get erased.
     */
    public function new(int $size, ColorInterface $backgroundColor) : void;

    /**
     * Transforms all following drawing operation coordinates by scaling them by a given factor.
     *
     * @throws RuntimeException if no image was started yet.
     */
    public function scale(float $size) : void;

    /**
     * Transforms all following drawing operation coordinates by translating them by a given amount.
     *
     * @throws RuntimeException if no image was started yet.
     */
    public function translate(float $x, float $y) : void;

    /**
     * Transforms all following drawing operation coordinates by rotating them by a given amount.
     *
     * @throws RuntimeException if no image was started yet.
     */
    public function rotate(int $degrees) : void;

    /**
     * Pushes the current coordinate transformation onto a stack.
     *
     * @throws RuntimeException if no image was started yet.
     */
    public function push() : void;

    /**
     * Pops the last coordinate transformation from a stack.
     *
     * @throws RuntimeException if no image was started yet.
     */
    public function pop() : void;

    /**
     * Draws a path with a given color.
     *
     * @throws RuntimeException if no image was started yet.
     */
    public function drawPathWithColor(Path $path, ColorInterface $color) : void;

    /**
     * Draws a path with a given gradient which spans the box described by the position and size.
     *
     * @throws RuntimeException if no image was started yet.
     */
    public function drawPathWithGradient(
        Path $path,
        Gradient $gradient,
        float $x,
        float $y,
        float $width,
        float $height
    ) : void;

    /**
     * Ends the image drawing operation and returns the resulting blob.
     *
     * This should reset the state of the back end and thus this method should only be callable once per image.
     *
     * @throws RuntimeException if no image was started yet.
     */
    public function done() : string;
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Renderer;

use BaconQrCode\Encoder\QrCode;

interface RendererInterface
{
    public function render(QrCode $qrCode) : string;
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Renderer\Color;

interface ColorInterface
{
    /**
     * Converts the color to RGB.
     */
    public function toRgb() : Rgb;

    /**
     * Converts the color to CMYK.
     */
    public function toCmyk() : Cmyk;

    /**
     * Converts the color to gray.
     */
    public function toGray() : Gray;
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Renderer\Color;

use BaconQrCode\Exception;

final class Cmyk implements ColorInterface
{
    /**
     * @param int $cyan the cyan amount, 0 to 100
     * @param int $magenta the magenta amount, 0 to 100
     * @param int $yellow the yellow amount, 0 to 100
     * @param int $black the black amount, 0 to 100
     */
    public function __construct(
        private readonly int $cyan,
        private readonly int $magenta,
        private readonly int $yellow,
        private readonly int $black
    ) {
        if ($cyan < 0 || $cyan > 100) {
            throw new Exception\InvalidArgumentException('Cyan must be between 0 and 100');
        }

        if ($magenta < 0 || $magenta > 100) {
            throw new Exception\InvalidArgumentException('Magenta must be between 0 and 100');
        }

        if ($yellow < 0 || $yellow > 100) {
            throw new Exception\InvalidArgumentException('Yellow must be between 0 and 100');
        }

        if ($black < 0 || $black > 100) {
            throw new Exception\InvalidArgumentException('Black must be between 0 and 100');
        }
    }

    public function getCyan() : int
    {
        return $this->cyan;
    }

    public function getMagenta() : int
    {
        return $this->magenta;
    }

    public function getYellow() : int
    {
        return $this->yellow;
    }

    public function getBlack() : int
    {
        return $this->black;
    }

    public function toRgb() : Rgb
    {
        $c = $this->cyan / 100;
        $m = $this->magenta / 100;
        $y = $this->yellow / 100;
        $k = $this->black / 100;

        return new Rgb(
            (int) round(255 * (1 - $c) * (1 - $k)),
            (int) round(255 * (1 - $m) * (1 - $k)),
            (int) round(255 * (1 - $y) * (1 - $k))
        );
    }

    public function toCmyk() : Cmyk
    {
        return $this;
    }

    public function toGray() : Gray
    {
        return $this->toRgb()->toGray();
    }
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Renderer\Color;

use BaconQrCode\Exception;

final class Gray implements ColorInterface
{
    /**
     * @param int $gray the gray value between 0 (black) and 100 (white)
     */
    public function __construct(private readonly int $gray)
    {
        if ($gray < 0 || $gray > 100) {
            throw new Exception\InvalidArgumentException('Gray must be between 0 and 100');
        }
    }

    public function getGray() : int
    {
        return $this->gray;
    }

    public function toRgb() : Rgb
    {
        // use 255/100 instead of 2.55 to avoid floating-point precision loss (100 * 2.55 = 254.999...)
        $value = (int) round($this->gray * 255 / 100);

        return new Rgb($value, $value, $value);
    }

    public function toCmyk() : Cmyk
    {
        return new Cmyk(0, 0, 0, 100 - $this->gray);
    }

    public function toGray() : Gray
    {
        return $this;
    }
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Renderer\Color;

use BaconQrCode\Exception;

final class Alpha implements ColorInterface
{
    /**
     * @param int $alpha the alpha value, 0 to 100
     */
    public function __construct(private readonly int $alpha, private readonly ColorInterface $baseColor)
    {
        if ($alpha < 0 || $alpha > 100) {
            throw new Exception\InvalidArgumentException('Alpha must be between 0 and 100');
        }
    }

    public function getAlpha() : int
    {
        return $this->alpha;
    }

    public function getBaseColor() : ColorInterface
    {
        return $this->baseColor;
    }

    public function toRgb() : Rgb
    {
        return $this->baseColor->toRgb();
    }

    public function toCmyk() : Cmyk
    {
        return $this->baseColor->toCmyk();
    }

    public function toGray() : Gray
    {
        return $this->baseColor->toGray();
    }
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Renderer\Color;

use BaconQrCode\Exception;

final class Rgb implements ColorInterface
{
    /**
     * @param int $red the red amount of the color, 0 to 255
     * @param int $green the green amount of the color, 0 to 255
     * @param int $blue the blue amount of the color, 0 to 255
     */
    public function __construct(private readonly int $red, private readonly int $green, private readonly int $blue)
    {
        if ($red < 0 || $red > 255) {
            throw new Exception\InvalidArgumentException('Red must be between 0 and 255');
        }

        if ($green < 0 || $green > 255) {
            throw new Exception\InvalidArgumentException('Green must be between 0 and 255');
        }

        if ($blue < 0 || $blue > 255) {
            throw new Exception\InvalidArgumentException('Blue must be between 0 and 255');
        }
    }

    public function getRed() : int
    {
        return $this->red;
    }

    public function getGreen() : int
    {
        return $this->green;
    }

    public function getBlue() : int
    {
        return $this->blue;
    }

    public function toRgb() : Rgb
    {
        return $this;
    }

    public function toCmyk() : Cmyk
    {
        // avoid division by zero with input rgb(0,0,0), by handling it as a specific case
        if (0 === $this->red && 0 === $this->green && 0 === $this->blue) {
            return new Cmyk(0, 0, 0, 100);
        }

        $c = 1 - ($this->red / 255);
        $m = 1 - ($this->green / 255);
        $y = 1 - ($this->blue / 255);
        $k = min($c, $m, $y);

        return new Cmyk(
            (int) round(100 * ($c - $k) / (1 - $k)),
            (int) round(100 * ($m - $k) / (1 - $k)),
            (int) round(100 * ($y - $k) / (1 - $k)),
            (int) round(100 * $k)
        );
    }

    public function toGray() : Gray
    {
        // use integer-based calculation to avoid floating-point precision loss
        return new Gray((int) round(($this->red * 2126 + $this->green * 7152 + $this->blue * 722) / 25500));
    }
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Renderer\Path;

use IteratorAggregate;
use Traversable;

/**
 * Internal Representation of a vector path.
 */
final class Path implements IteratorAggregate
{
    /**
     * @var OperationInterface[]
     */
    private array $operations = [];

    /**
     * Moves the drawing operation to a certain position.
     */
    public function move(float $x, float $y) : self
    {
        $path = clone $this;
        $path->operations[] = new Move($x, $y);
        return $path;
    }

    /**
     * Draws a line from the current position to another position.
     */
    public function line(float $x, float $y) : self
    {
        $path = clone $this;
        $path->operations[] = new Line($x, $y);
        return $path;
    }

    /**
     * Draws an elliptic arc from the current position to another position.
     */
    public function ellipticArc(
        float $xRadius,
        float $yRadius,
        float $xAxisRotation,
        bool $largeArc,
        bool $sweep,
        float $x,
        float $y
    ) : self {
        $path = clone $this;
        $path->operations[] = new EllipticArc($xRadius, $yRadius, $xAxisRotation, $largeArc, $sweep, $x, $y);
        return $path;
    }

    /**
     * Draws a curve from the current position to another position.
     */
    public function curve(float $x1, float $y1, float $x2, float $y2, float $x3, float $y3) : self
    {
        $path = clone $this;
        $path->operations[] = new Curve($x1, $y1, $x2, $y2, $x3, $y3);
        return $path;
    }

    /**
     * Closes a sub-path.
     */
    public function close() : self
    {
        $path = clone $this;
        $path->operations[] = Close::instance();
        return $path;
    }

    /**
     * Appends another path to this one.
     */
    public function append(self $other) : self
    {
        $path = clone $this;
        $path->operations = array_merge($this->operations, $other->operations);
        return $path;
    }

    public function translate(float $x, float $y) : self
    {
        $path = new self();

        foreach ($this->operations as $operation) {
            $path->operations[] = $operation->translate($x, $y);
        }

        return $path;
    }

    public function rotate(int $degrees) : self
    {
        $path = new self();

        foreach ($this->operations as $operation) {
            $path->operations[] = $operation->rotate($degrees);
        }

        return $path;
    }

    /**
     * @return Traversable<int, OperationInterface>
     */
    public function getIterator() : Traversable
    {
        foreach ($this->operations as $operation) {
            yield $operation;
        }
    }
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Renderer\Path;

interface OperationInterface
{
    /**
     * Translates the operation's coordinates.
     */
    public function translate(float $x, float $y) : self;

    /**
     * Rotates the operation's coordinates.
     */
    public function rotate(int $degrees) : self;
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Renderer\Path;

final class EllipticArc implements OperationInterface
{
    private const ZERO_TOLERANCE = 1e-05;

    private float $xRadius;
    private float $yRadius;
    private float $xAxisAngle;

    public function __construct(
        float                  $xRadius,
        float                  $yRadius,
        float                  $xAxisAngle,
        private readonly bool  $largeArc,
        private readonly bool  $sweep,
        private readonly float $x,
        private readonly float $y
    ) {
        $this->xRadius = abs($xRadius);
        $this->yRadius = abs($yRadius);
        $this->xAxisAngle = $xAxisAngle % 360;
    }

    public function getXRadius() : float
    {
        return $this->xRadius;
    }

    public function getYRadius() : float
    {
        return $this->yRadius;
    }

    public function getXAxisAngle() : float
    {
        return $this->xAxisAngle;
    }

    public function isLargeArc() : bool
    {
        return $this->largeArc;
    }

    public function isSweep() : bool
    {
        return $this->sweep;
    }

    public function getX() : float
    {
        return $this->x;
    }

    public function getY() : float
    {
        return $this->y;
    }

    /**
     * @return self
     */
    public function translate(float $x, float $y) : OperationInterface
    {
        return new self(
            $this->xRadius,
            $this->yRadius,
            $this->xAxisAngle,
            $this->largeArc,
            $this->sweep,
            $this->x + $x,
            $this->y + $y
        );
    }

    /**
     * @return self
     */
    public function rotate(int $degrees) : OperationInterface
    {
        $radians = deg2rad($degrees);
        $sin = sin($radians);
        $cos = cos($radians);
        $xr = $this->x * $cos - $this->y * $sin;
        $yr = $this->x * $sin + $this->y * $cos;
        return new self(
            $this->xRadius,
            $this->yRadius,
            $this->xAxisAngle,
            $this->largeArc,
            $this->sweep,
            $xr,
            $yr
        );
    }

    /**
     * Converts the elliptic arc to multiple curves.
     *
     * Since not all image back ends support elliptic arcs, this method allows to convert the arc into multiple curves
     * resembling the same result.
     *
     * @see https://mortoray.com/2017/02/16/rendering-an-svg-elliptical-arc-as-bezier-curves/
     * @return array<Curve|Line>
     */
    public function toCurves(float $fromX, float $fromY) : array
    {
        if (sqrt(($fromX - $this->x) ** 2 + ($fromY - $this->y) ** 2) < self::ZERO_TOLERANCE) {
            return [];
        }

        if ($this->xRadius < self::ZERO_TOLERANCE || $this->yRadius < self::ZERO_TOLERANCE) {
            return [new Line($this->x, $this->y)];
        }

        return $this->createCurves($fromX, $fromY);
    }

    /**
     * @return Curve[]
     */
    private function createCurves(float $fromX, float $fromY) : array
    {
        $xAngle = deg2rad($this->xAxisAngle);
        list($centerX, $centerY, $radiusX, $radiusY, $startAngle, $deltaAngle) =
            $this->calculateCenterPointParameters($fromX, $fromY, $xAngle);

        $s = $startAngle;
        $e = $s + $deltaAngle;
        $sign = ($e < $s) ? -1 : 1;
        $remain = abs($e - $s);
        $p1 = self::point($centerX, $centerY, $radiusX, $radiusY, $xAngle, $s);
        $curves = [];

        while ($remain > self::ZERO_TOLERANCE) {
            $step = min($remain, pi() / 2);
            $signStep = $step * $sign;
            $p2 = self::point($centerX, $centerY, $radiusX, $radiusY, $xAngle, $s + $signStep);

            $alphaT = tan($signStep / 2);
            $alpha = sin($signStep) * (sqrt(4 + 3 * $alphaT ** 2) - 1) / 3;
            $d1 = self::derivative($radiusX, $radiusY, $xAngle, $s);
            $d2 = self::derivative($radiusX, $radiusY, $xAngle, $s + $signStep);

            $curves[] = new Curve(
                $p1[0] + $alpha * $d1[0],
                $p1[1] + $alpha * $d1[1],
                $p2[0] - $alpha * $d2[0],
                $p2[1] - $alpha * $d2[1],
                $p2[0],
                $p2[1]
            );

            $s += $signStep;
            $remain -= $step;
            $p1 = $p2;
        }

        return $curves;
    }

    /**
     * @return float[]
     */
    private function calculateCenterPointParameters(float $fromX, float $fromY, float $xAngle): array
    {
        $rX = $this->xRadius;
        $rY = $this->yRadius;

        // F.6.5.1
        $dx2 = ($fromX - $this->x) / 2;
        $dy2 = ($fromY - $this->y) / 2;
        $x1p = cos($xAngle) * $dx2 + sin($xAngle) * $dy2;
        $y1p = -sin($xAngle) * $dx2 + cos($xAngle) * $dy2;

        // F.6.5.2
        $rxs = $rX ** 2;
        $rys = $rY ** 2;
        $x1ps = $x1p ** 2;
        $y1ps = $y1p ** 2;
        $cr = $x1ps / $rxs + $y1ps / $rys;

        if ($cr > 1) {
            $s = sqrt($cr);
            $rX *= $s;
            $rY *= $s;
            $rxs = $rX ** 2;
            $rys = $rY ** 2;
        }

        $dq = ($rxs * $y1ps + $rys * $x1ps);
        $pq = ($rxs * $rys - $dq) / $dq;
        $q = sqrt(max(0, $pq));

        if ($this->largeArc === $this->sweep) {
            $q = -$q;
        }

        $cxp = $q * $rX * $y1p / $rY;
        $cyp = -$q * $rY * $x1p / $rX;

        // F.6.5.3
        $cx = cos($xAngle) * $cxp - sin($xAngle) * $cyp + ($fromX + $this->x) / 2;
        $cy = sin($xAngle) * $cxp + cos($xAngle) * $cyp + ($fromY + $this->y) / 2;

        // F.6.5.5
        $theta = self::angle(1, 0, ($x1p - $cxp) / $rX, ($y1p - $cyp) / $rY);

        // F.6.5.6
        $delta = self::angle(($x1p - $cxp) / $rX, ($y1p - $cyp) / $rY, (-$x1p - $cxp) / $rX, (-$y1p - $cyp) / $rY);
        $delta = fmod($delta, pi() * 2);

        if (! $this->sweep && $delta > 0) {
            $delta -= 2 * pi();
        } elseif ($this->sweep && $delta < 0) {
            $delta += 2 * pi();
        }

        return [$cx, $cy, $rX, $rY, $theta, $delta];
    }

    private static function angle(float $ux, float $uy, float $vx, float $vy) : float
    {
        // F.6.5.4
        $dot = $ux * $vx + $uy * $vy;
        $length = sqrt($ux ** 2 + $uy ** 2) * sqrt($vx ** 2 + $vy ** 2);
        $angle = acos(min(1, max(-1, $dot / $length)));

        if (($ux * $vy - $uy * $vx) < 0) {
            return -$angle;
        }

        return $angle;
    }

    /**
     * @return float[]
     */
    private static function point(
        float $centerX,
        float $centerY,
        float $radiusX,
        float $radiusY,
        float $xAngle,
        float $angle
    ) : array {
        return [
            $centerX + $radiusX * cos($xAngle) * cos($angle) - $radiusY * sin($xAngle) * sin($angle),
            $centerY + $radiusX * sin($xAngle) * cos($angle) + $radiusY * cos($xAngle) * sin($angle),
        ];
    }

    /**
     * @return float[]
     */
    private static function derivative(float $radiusX, float $radiusY, float $xAngle, float $angle) : array
    {
        return [
            -$radiusX * cos($xAngle) * sin($angle) - $radiusY * sin($xAngle) * cos($angle),
            -$radiusX * sin($xAngle) * sin($angle) + $radiusY * cos($xAngle) * cos($angle),
        ];
    }
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Renderer\Path;

final class Curve implements OperationInterface
{
    public function __construct(
        private readonly float $x1,
        private readonly float $y1,
        private readonly float $x2,
        private readonly float $y2,
        private readonly float $x3,
        private readonly float $y3
    ) {
    }

    public function getX1() : float
    {
        return $this->x1;
    }

    public function getY1() : float
    {
        return $this->y1;
    }

    public function getX2() : float
    {
        return $this->x2;
    }

    public function getY2() : float
    {
        return $this->y2;
    }

    public function getX3() : float
    {
        return $this->x3;
    }

    public function getY3() : float
    {
        return $this->y3;
    }

    /**
     * @return self
     */
    public function translate(float $x, float $y) : OperationInterface
    {
        return new self(
            $this->x1 + $x,
            $this->y1 + $y,
            $this->x2 + $x,
            $this->y2 + $y,
            $this->x3 + $x,
            $this->y3 + $y
        );
    }

    /**
     * @return self
     */
    public function rotate(int $degrees) : OperationInterface
    {
        $radians = deg2rad($degrees);
        $sin = sin($radians);
        $cos = cos($radians);
        $x1r = $this->x1 * $cos - $this->y1 * $sin;
        $y1r = $this->x1 * $sin + $this->y1 * $cos;
        $x2r = $this->x2 * $cos - $this->y2 * $sin;
        $y2r = $this->x2 * $sin + $this->y2 * $cos;
        $x3r = $this->x3 * $cos - $this->y3 * $sin;
        $y3r = $this->x3 * $sin + $this->y3 * $cos;
        return new self(
            $x1r,
            $y1r,
            $x2r,
            $y2r,
            $x3r,
            $y3r
        );
    }
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Renderer\Path;

final class Close implements OperationInterface
{
    private static ?Close $instance = null;

    private function __construct()
    {
    }

    public static function instance() : self
    {
        return self::$instance ?: self::$instance = new self();
    }

    /**
     * @return self
     */
    public function translate(float $x, float $y) : OperationInterface
    {
        return $this;
    }

    /**
     * @return self
     */
    public function rotate(int $degrees) : OperationInterface
    {
        return $this;
    }
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Renderer\Path;

final class Move implements OperationInterface
{
    public function __construct(private readonly float $x, private readonly float $y)
    {
    }

    public function getX() : float
    {
        return $this->x;
    }

    public function getY() : float
    {
        return $this->y;
    }

    /**
     * @return self
     */
    public function translate(float $x, float $y) : OperationInterface
    {
        return new self($this->x + $x, $this->y + $y);
    }

    /**
     * @return self
     */
    public function rotate(int $degrees) : OperationInterface
    {
        $radians = deg2rad($degrees);
        $sin = sin($radians);
        $cos = cos($radians);
        $xr = $this->x * $cos - $this->y * $sin;
        $yr = $this->x * $sin + $this->y * $cos;
        return new self($xr, $yr);
    }
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Renderer\Path;

final class Line implements OperationInterface
{
    public function __construct(private readonly float $x, private readonly float $y)
    {
    }

    public function getX() : float
    {
        return $this->x;
    }

    public function getY() : float
    {
        return $this->y;
    }

    /**
     * @return self
     */
    public function translate(float $x, float $y) : OperationInterface
    {
        return new self($this->x + $x, $this->y + $y);
    }

    /**
     * @return self
     */
    public function rotate(int $degrees) : OperationInterface
    {
        $radians = deg2rad($degrees);
        $sin = sin($radians);
        $cos = cos($radians);
        $xr = $this->x * $cos - $this->y * $sin;
        $yr = $this->x * $sin + $this->y * $cos;
        return new self($xr, $yr);
    }
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Renderer;

use BaconQrCode\Encoder\QrCode;
use BaconQrCode\Exception\InvalidArgumentException;

final class PlainTextRenderer implements RendererInterface
{
    /**
     * UTF-8 full block (U+2588)
     */
    private const FULL_BLOCK = "\xe2\x96\x88";

    /**
     * UTF-8 upper half block (U+2580)
     */
    private const UPPER_HALF_BLOCK = "\xe2\x96\x80";

    /**
     * UTF-8 lower half block (U+2584)
     */
    private const LOWER_HALF_BLOCK = "\xe2\x96\x84";

    /**
     * UTF-8 no-break space (U+00A0)
     */
    private const EMPTY_BLOCK = "\xc2\xa0";

    public function __construct(private readonly int $margin = 2)
    {
    }

    /**
     * @throws InvalidArgumentException if matrix width doesn't match height
     */
    public function render(QrCode $qrCode) : string
    {
        $matrix = $qrCode->getMatrix();
        $matrixSize = $matrix->getWidth();

        if ($matrixSize !== $matrix->getHeight()) {
            throw new InvalidArgumentException('Matrix must have the same width and height');
        }

        $rows = $matrix->getArray()->toArray();

        if (0 !== $matrixSize % 2) {
            $rows[] = array_fill(0, $matrixSize, 0);
        }

        $horizontalMargin = str_repeat(self::EMPTY_BLOCK, $this->margin);
        $result = str_repeat("\n", (int) ceil($this->margin / 2));

        for ($i = 0; $i < $matrixSize; $i += 2) {
            $result .= $horizontalMargin;

            $upperRow = $rows[$i];
            $lowerRow = $rows[$i + 1];

            for ($j = 0; $j < $matrixSize; ++$j) {
                $upperBit = $upperRow[$j];
                $lowerBit = $lowerRow[$j];

                if ($upperBit) {
                    $result .= $lowerBit ? self::FULL_BLOCK : self::UPPER_HALF_BLOCK;
                } else {
                    $result .= $lowerBit ? self::LOWER_HALF_BLOCK : self::EMPTY_BLOCK;
                }
            }

            $result .= $horizontalMargin . "\n";
        }

        $result .= str_repeat("\n", (int) ceil($this->margin / 2));

        return $result;
    }
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Renderer\Eye;

use BaconQrCode\Renderer\Path\Path;

/**
 * Combines the style of two different eyes.
 */
final class CompositeEye implements EyeInterface
{
    public function __construct(private readonly EyeInterface $externalEye, private readonly EyeInterface $internalEye)
    {
    }

    public function getExternalPath() : Path
    {
        return $this->externalEye->getExternalPath();
    }

    public function getInternalPath() : Path
    {
        return $this->internalEye->getInternalPath();
    }
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Renderer\Eye;

use BaconQrCode\Renderer\Path\Path;

/**
 * Interface for describing the look of an eye.
 */
interface EyeInterface
{
    /**
     * Returns the path of the external eye element.
     *
     * The path origin point (0, 0) must be anchored at the middle of the path.
     */
    public function getExternalPath() : Path;

    /**
     * Returns the path of the internal eye element.
     *
     * The path origin point (0, 0) must be anchored at the middle of the path.
     */
    public function getInternalPath() : Path;
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Renderer\Eye;

use BaconQrCode\Encoder\ByteMatrix;
use BaconQrCode\Renderer\Module\ModuleInterface;
use BaconQrCode\Renderer\Path\Path;

/**
 * Renders an eye based on a module renderer.
 */
final class ModuleEye implements EyeInterface
{
    public function __construct(private readonly ModuleInterface $module)
    {
    }

    public function getExternalPath() : Path
    {
        $matrix = new ByteMatrix(7, 7);

        for ($x = 0; $x < 7; ++$x) {
            $matrix->set($x, 0, 1);
            $matrix->set($x, 6, 1);
        }

        for ($y = 1; $y < 6; ++$y) {
            $matrix->set(0, $y, 1);
            $matrix->set(6, $y, 1);
        }

        return $this->module->createPath($matrix)->translate(-3.5, -3.5);
    }

    public function getInternalPath() : Path
    {
        $matrix = new ByteMatrix(3, 3);

        for ($x = 0; $x < 3; ++$x) {
            for ($y = 0; $y < 3; ++$y) {
                $matrix->set($x, $y, 1);
            }
        }

        return $this->module->createPath($matrix)->translate(-1.5, -1.5);
    }
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Renderer\Eye;

use BaconQrCode\Renderer\Path\Path;

/**
 * Renders the inner eye as a circle.
 */
final class SimpleCircleEye implements EyeInterface
{
    private static ?SimpleCircleEye $instance = null;

    private function __construct()
    {
    }

    public static function instance() : self
    {
        return self::$instance ?: self::$instance = new self();
    }

    public function getExternalPath() : Path
    {
        return (new Path())
            ->move(-3.5, -3.5)
            ->line(3.5, -3.5)
            ->line(3.5, 3.5)
            ->line(-3.5, 3.5)
            ->close()
            ->move(-2.5, -2.5)
            ->line(-2.5, 2.5)
            ->line(2.5, 2.5)
            ->line(2.5, -2.5)
            ->close()
        ;
    }

    public function getInternalPath() : Path
    {
        return (new Path())
            ->move(1.5, 0)
            ->ellipticArc(1.5, 1.5, 0., false, true, 0., 1.5)
            ->ellipticArc(1.5, 1.5, 0., false, true, -1.5, 0.)
            ->ellipticArc(1.5, 1.5, 0., false, true, 0., -1.5)
            ->ellipticArc(1.5, 1.5, 0., false, true, 1.5, 0.)
            ->close()
        ;
    }
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Renderer\Eye;

use BaconQrCode\Renderer\Path\Path;

/**
 * Renders the outer eye as solid with a curved corner and inner eye as a circle.
 */
final class PointyEye implements EyeInterface
{
    /**
     * @var self|null
     */
    private static $instance;

    private function __construct()
    {
    }

    public static function instance() : self
    {
        return self::$instance ?: self::$instance = new self();
    }

    public function getExternalPath() : Path
    {
        return (new Path())
            ->move(-3.5, 3.5)
            ->line(-3.5, 0)
            ->ellipticArc(3.5, 3.5, 0, false, true, 0, -3.5)
            ->line(3.5, -3.5)
            ->line(3.5, 3.5)
            ->close()
            ->move(2.5, 0)
            ->ellipticArc(2.5, 2.5, 0, false, true, 0, 2.5)
            ->ellipticArc(2.5, 2.5, 0, false, true, -2.5, 0)
            ->ellipticArc(2.5, 2.5, 0, false, true, 0, -2.5)
            ->ellipticArc(2.5, 2.5, 0, false, true, 2.5, 0)
            ->close()
        ;
    }

    public function getInternalPath() : Path
    {
        return (new Path())
            ->move(1.5, 0)
            ->ellipticArc(1.5, 1.5, 0., false, true, 0., 1.5)
            ->ellipticArc(1.5, 1.5, 0., false, true, -1.5, 0.)
            ->ellipticArc(1.5, 1.5, 0., false, true, 0., -1.5)
            ->ellipticArc(1.5, 1.5, 0., false, true, 1.5, 0.)
            ->close()
        ;
    }
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Renderer\Eye;

use BaconQrCode\Renderer\Path\Path;

/**
 * Renders the eyes in their default square shape.
 */
final class SquareEye implements EyeInterface
{
    private static ?SquareEye $instance = null;

    private function __construct()
    {
    }

    public static function instance() : self
    {
        return self::$instance ?: self::$instance = new self();
    }

    public function getExternalPath() : Path
    {
        return (new Path())
            ->move(-3.5, -3.5)
            ->line(3.5, -3.5)
            ->line(3.5, 3.5)
            ->line(-3.5, 3.5)
            ->close()
            ->move(-2.5, -2.5)
            ->line(-2.5, 2.5)
            ->line(2.5, 2.5)
            ->line(2.5, -2.5)
            ->close()
        ;
    }

    public function getInternalPath() : Path
    {
        return (new Path())
            ->move(-1.5, -1.5)
            ->line(1.5, -1.5)
            ->line(1.5, 1.5)
            ->line(-1.5, 1.5)
            ->close()
        ;
    }
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Renderer\Module;

use BaconQrCode\Encoder\ByteMatrix;
use BaconQrCode\Renderer\Path\Path;

/**
 * Interface describing how modules should be rendered.
 *
 * A module always receives a byte matrix (with values either being 1 or 0). It returns a path, where the origin
 * coordinate (0, 0) equals the top left corner of the first matrix value.
 */
interface ModuleInterface
{
    public function createPath(ByteMatrix $matrix) : Path;
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Renderer\Module;

use BaconQrCode\Encoder\ByteMatrix;
use BaconQrCode\Exception\InvalidArgumentException;
use BaconQrCode\Renderer\Path\Path;

/**
 * Renders individual modules as dots.
 */
final class DotsModule implements ModuleInterface
{
    public const LARGE = 1;
    public const MEDIUM = .8;
    public const SMALL = .6;

    public function __construct(private readonly float $size)
    {
        if ($size <= 0 || $size > 1) {
            throw new InvalidArgumentException('Size must between 0 (exclusive) and 1 (inclusive)');
        }
    }

    public function createPath(ByteMatrix $matrix) : Path
    {
        $width = $matrix->getWidth();
        $height = $matrix->getHeight();
        $path = new Path();
        $halfSize = $this->size / 2;
        $margin = (1 - $this->size) / 2;

        for ($y = 0; $y < $height; ++$y) {
            for ($x = 0; $x < $width; ++$x) {
                if (! $matrix->get($x, $y)) {
                    continue;
                }

                $pathX = $x + $margin;
                $pathY = $y + $margin;

                $path = $path
                    ->move($pathX + $this->size, $pathY + $halfSize)
                    ->ellipticArc($halfSize, $halfSize, 0, false, true, $pathX + $halfSize, $pathY + $this->size)
                    ->ellipticArc($halfSize, $halfSize, 0, false, true, $pathX, $pathY + $halfSize)
                    ->ellipticArc($halfSize, $halfSize, 0, false, true, $pathX + $halfSize, $pathY)
                    ->ellipticArc($halfSize, $halfSize, 0, false, true, $pathX + $this->size, $pathY + $halfSize)
                    ->close()
                ;
            }
        }

        return $path;
    }
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Renderer\Module;

use BaconQrCode\Encoder\ByteMatrix;
use BaconQrCode\Renderer\Module\EdgeIterator\EdgeIterator;
use BaconQrCode\Renderer\Path\Path;

/**
 * Groups modules together to a single path.
 */
final class SquareModule implements ModuleInterface
{
    private static ?SquareModule $instance = null;

    private function __construct()
    {
    }

    public static function instance() : self
    {
        return self::$instance ?: self::$instance = new self();
    }

    public function createPath(ByteMatrix $matrix) : Path
    {
        $path = new Path();

        foreach (new EdgeIterator($matrix) as $edge) {
            $points = $edge->getSimplifiedPoints();
            $length = count($points);
            $path = $path->move($points[0][0], $points[0][1]);

            for ($i = 1; $i < $length; ++$i) {
                $path = $path->line($points[$i][0], $points[$i][1]);
            }

            $path = $path->close();
        }

        return $path;
    }
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Renderer\Module;

use BaconQrCode\Encoder\ByteMatrix;
use BaconQrCode\Exception\InvalidArgumentException;
use BaconQrCode\Renderer\Module\EdgeIterator\EdgeIterator;
use BaconQrCode\Renderer\Path\Path;

/**
 * Rounds the corners of module groups.
 */
final class RoundnessModule implements ModuleInterface
{
    public const STRONG = 1;
    public const MEDIUM = .5;
    public const SOFT = .25;

    public function __construct(private float $intensity)
    {
        if ($intensity <= 0 || $intensity > 1) {
            throw new InvalidArgumentException('Intensity must between 0 (exclusive) and 1 (inclusive)');
        }

        $this->intensity = $intensity / 2;
    }

    public function createPath(ByteMatrix $matrix) : Path
    {
        $path = new Path();

        foreach (new EdgeIterator($matrix) as $edge) {
            $points = $edge->getSimplifiedPoints();
            $length = count($points);

            $currentPoint = $points[0];
            $nextPoint = $points[1];
            $horizontal = ($currentPoint[1] === $nextPoint[1]);

            if ($horizontal) {
                $right = $nextPoint[0] > $currentPoint[0];
                $path = $path->move(
                    $currentPoint[0] + ($right ? $this->intensity : -$this->intensity),
                    $currentPoint[1]
                );
            } else {
                $up = $nextPoint[0] < $currentPoint[0];
                $path = $path->move(
                    $currentPoint[0],
                    $currentPoint[1] + ($up ? -$this->intensity : $this->intensity)
                );
            }

            for ($i = 1; $i <= $length; ++$i) {
                if ($i === $length) {
                    $previousPoint = $points[$length - 1];
                    $currentPoint = $points[0];
                    $nextPoint = $points[1];
                } else {
                    $previousPoint = $points[(0 === $i ? $length : $i) - 1];
                    $currentPoint = $points[$i];
                    $nextPoint = $points[($length - 1 === $i ? -1 : $i) + 1];
                }

                $horizontal = ($previousPoint[1] === $currentPoint[1]);

                if ($horizontal) {
                    $right = $previousPoint[0] < $currentPoint[0];
                    $up = $nextPoint[1] < $currentPoint[1];
                    $sweep = ($up xor $right);

                    if ($this->intensity < 0.5
                        || ($right && $previousPoint[0] !== $currentPoint[0] - 1)
                        || (! $right && $previousPoint[0] - 1 !== $currentPoint[0])
                    ) {
                        $path = $path->line(
                            $currentPoint[0] + ($right ? -$this->intensity : $this->intensity),
                            $currentPoint[1]
                        );
                    }

                    $path = $path->ellipticArc(
                        $this->intensity,
                        $this->intensity,
                        0,
                        false,
                        $sweep,
                        $currentPoint[0],
                        $currentPoint[1] + ($up ? -$this->intensity : $this->intensity)
                    );
                } else {
                    $up = $previousPoint[1] > $currentPoint[1];
                    $right = $nextPoint[0] > $currentPoint[0];
                    $sweep = ! ($up xor $right);

                    if ($this->intensity < 0.5
                        || ($up && $previousPoint[1] !== $currentPoint[1] + 1)
                        || (! $up && $previousPoint[0] + 1 !== $currentPoint[0])
                    ) {
                        $path = $path->line(
                            $currentPoint[0],
                            $currentPoint[1] + ($up ? $this->intensity : -$this->intensity)
                        );
                    }

                    $path = $path->ellipticArc(
                        $this->intensity,
                        $this->intensity,
                        0,
                        false,
                        $sweep,
                        $currentPoint[0] + ($right ? $this->intensity : -$this->intensity),
                        $currentPoint[1]
                    );
                }
            }

            $path = $path->close();
        }

        return $path;
    }
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Renderer\Module\EdgeIterator;

final class Edge
{
    /**
     * @var array<int[]>
     */
    private array $points = [];

    /**
     * @var array<int[]>|null
     */
    private ?array $simplifiedPoints = null;

    private int $minX = PHP_INT_MAX;

    private int $minY = PHP_INT_MAX;

    private int $maxX = -1;

    private int $maxY = -1;

    public function __construct(private readonly bool $positive)
    {
    }

    public function addPoint(int $x, int $y) : void
    {
        $this->points[] = [$x, $y];
        $this->minX = min($this->minX, $x);
        $this->minY = min($this->minY, $y);
        $this->maxX = max($this->maxX, $x);
        $this->maxY = max($this->maxY, $y);
    }

    public function isPositive() : bool
    {
        return $this->positive;
    }

    /**
     * @return array<int[]>
     */
    public function getPoints() : array
    {
        return $this->points;
    }

    public function getMaxX() : int
    {
        return $this->maxX;
    }

    public function getSimplifiedPoints() : array
    {
        if (null !== $this->simplifiedPoints) {
            return $this->simplifiedPoints;
        }

        $points = [];
        $length = count($this->points);

        for ($i = 0; $i < $length; ++$i) {
            $previousPoint = $this->points[(0 === $i ? $length : $i) - 1];
            $nextPoint = $this->points[($length - 1 === $i ? -1 : $i) + 1];
            $currentPoint = $this->points[$i];

            if (($previousPoint[0] === $currentPoint[0] && $currentPoint[0] === $nextPoint[0])
                || ($previousPoint[1] === $currentPoint[1] && $currentPoint[1] === $nextPoint[1])
            ) {
                continue;
            }

            $points[] = $currentPoint;
        }

        return $this->simplifiedPoints = $points;
    }
}
<?php
declare(strict_types = 1);

namespace BaconQrCode\Renderer\Module\EdgeIterator;

use BaconQrCode\Encoder\ByteMatrix;
use IteratorAggregate;
use Traversable;

/**
 * Edge iterator based on potrace.
 */
final class EdgeIterator implements IteratorAggregate
{
    /**
     * @var int[]
     */
    private array $bytes = [];

    private ?int $size;

    private int $width;

    private int $height;

    public function __construct(ByteMatrix $matrix)
    {
        $this->bytes = iterator_to_array($matrix->getBytes());
        $this->size = count($this->bytes);
        $this->width = $matrix->getWidth();
        $this->height = $matrix->getHeight();
    }

    /**
     * @return Traversable<Edge>
     */
    public function getIterator() : Traversable
    {
        $originalBytes = $this->bytes;
        $point = $this->findNext(0, 0);

        while (null !== $point) {
            $edge = $this->findEdge($point[0], $point[1]);
            $this->xorEdge($edge);

            yield $edge;

            $point = $this->findNext($point[0], $point[1]);
        }

        $this->bytes = $originalBytes;
    }

    /**
     * @return int[]|null
     */
    private function findNext(int $x, int $y) : ?array
    {
        $i = $this->width * $y + $x;

        while ($i < $this->size && 1 !== $this->bytes[$i]) {
            ++$i;
        }

        if ($i < $this->size) {
            return $this->pointOf($i);
        }

        return null;
    }

    private function findEdge(int $x, int $y) : Edge
    {
        $edge = new Edge($this->isSet($x, $y));
        $startX = $x;
        $startY = $y;
        $dirX = 0;
        $dirY = 1;

        while (true) {
            $edge->addPoint($x, $y);
            $x += $dirX;
            $y += $dirY;

            if ($x === $startX && $y === $startY) {
                break;
            }

            $left = $this->isSet($x + ($dirX + $dirY - 1 ) / 2, $y + ($dirY - $dirX - 1) / 2);
            $right = $this->isSet($x + ($dirX - $dirY - 1) / 2, $y + ($dirY + $dirX - 1) / 2);

            if ($right && ! $left) {
                $tmp = $dirX;
                $dirX = -$dirY;
                $dirY = $tmp;
            } elseif ($right) {
                $tmp = $dirX;
                $dirX = -$dirY;
                $dirY = $tmp;
            } elseif (! $left) {
                $tmp = $dirX;
                $dirX = $dirY;
                $dirY = -$tmp;
            }
        }

        return $edge;
    }

    private function xorEdge(Edge $path) : void
    {
        $points = $path->getPoints();
        $y1 = $points[0][1];
        $length = count($points);
        $maxX = $path->getMaxX();

        for ($i = 1; $i < $length; ++$i) {
            $y = $points[$i][1];

            if ($y === $y1) {
                continue;
            }

            $x = $points[$i][0];
            $minY = min($y1, $y);

            for ($j = $x; $j < $maxX; ++$j) {
                $this->flip($j, $minY);
            }

            $y1 = $y;
        }
    }

    private function isSet(int $x, int $y) : bool
    {
        return (
            $x >= 0
            && $x < $this->width
            && $y >= 0
            && $y < $this->height
        ) && 1 === $this->bytes[$this->width * $y + $x];
    }

    /**
     * @return int[]
     */
    private function pointOf(int $i) : array
    {
        $y = intdiv($i, $this->width);
        return [$i - $y * $this->width, $y];
    }

    private function flip(int $x, int $y) : void
    {
        $this->bytes[$this->width * $y + $x] = (
            $this->isSet($x, $y) ? 0 : 1
        );
    }
}
<?php
declare(strict_types = 1);

namespace BaconQrCode;

use BaconQrCode\Common\ErrorCorrectionLevel;
use BaconQrCode\Common\Version;
use BaconQrCode\Encoder\Encoder;
use BaconQrCode\Exception\InvalidArgumentException;
use BaconQrCode\Renderer\RendererInterface;

/**
 * QR code writer.
 */
final class Writer
{
    /**
     * Creates a new writer with a specific renderer.
     */
    public function __construct(private readonly RendererInterface $renderer)
    {
    }

    /**
     * Writes QR code and returns it as string.
     *
     * Content is a string which *should* be encoded in UTF-8, in case there are
     * non ASCII-characters present.
     *
     * @throws InvalidArgumentException if the content is empty
     */
    public function writeString(
        string $content,
        string $encoding = Encoder::DEFAULT_BYTE_MODE_ENCODING,
        ?ErrorCorrectionLevel $ecLevel = null,
        ?Version $forcedVersion = null
    ) : string {
        if (strlen($content) === 0) {
            throw new InvalidArgumentException('Found empty contents');
        }

        if (null === $ecLevel) {
            $ecLevel = ErrorCorrectionLevel::L();
        }

        return $this->renderer->render(Encoder::encode($content, $ecLevel, $encoding, $forcedVersion));
    }

    /**
     * Writes QR code to a file.
     *
     * @see Writer::writeString()
     */
    public function writeFile(
        string $content,
        string $filename,
        string $encoding = Encoder::DEFAULT_BYTE_MODE_ENCODING,
        ?ErrorCorrectionLevel $ecLevel = null,
        ?Version $forcedVersion = null
    ) : void {
        file_put_contents($filename, $this->writeString($content, $encoding, $ecLevel, $forcedVersion));
    }
}
{
    "name": "bacon/bacon-qr-code",
    "description": "BaconQrCode is a QR code generator for PHP.",
    "license": "BSD-2-Clause",
    "homepage": "https://github.com/Bacon/BaconQrCode",
    "require": {
        "php": "^8.1",
        "ext-iconv": "*",
        "dasprid/enum": "^1.0.3"
    },
    "suggest": {
        "ext-imagick": "to generate QR code images"
    },
    "authors": [
        {
            "name": "Ben Scholzen 'DASPRiD'",
            "email": "mail@dasprids.de",
            "homepage": "https://dasprids.de/",
            "role": "Developer"
        }
    ],
    "autoload": {
        "psr-4": {
            "BaconQrCode\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "BaconQrCodeTest\\": "test/"
        }
    },
    "require-dev": {
        "phpunit/phpunit": "^10.5.11 || ^11.0.4",
        "spatie/phpunit-snapshot-assertions": "^5.1.5",
        "spatie/pixelmatch-php": "^1.2.0",
        "squizlabs/php_codesniffer": "^3.9",
        "phly/keep-a-changelog": "^2.12"
    },
    "config": {
        "allow-plugins": {
            "ocramius/package-versions": true,
            "php-http/discovery": true
        }
    },
    "archive": {
        "exclude": [
            "/test",
            "/phpunit.xml.dist"
        ]
    }
}
<?php declare(strict_types=1);

require 'vendor/autoload.php';

use Amp\Ipc\Sync\ChannelledSocket;

use function Amp\async;
use function Amp\Ipc\connect;

$clientHandler = function (ChannelledSocket $socket) {
    echo "Created connection.".PHP_EOL;

    while ($payload = $socket->receive()) {
        echo "Received $payload".PHP_EOL;
    }
    echo "Closed connection".PHP_EOL;
};

$channel = connect(sys_get_temp_dir().'/test');

$thread = async($clientHandler, $channel);

$channel->send('ping');

$thread->await();
<?php declare(strict_types=1);

require 'vendor/autoload.php';

use Amp\Ipc\Sync\ChannelledSocket;

use function Amp\async;
use function Amp\Ipc\listen;

$clientHandler = function (ChannelledSocket $socket) {
    echo "Accepted connection".PHP_EOL;

    while ($payload = $socket->receive()) {
        echo "Received $payload".PHP_EOL;
        if ($payload === 'ping') {
            $socket->send('pong');
            $socket->disconnect();
        }
    }
    echo "Closed connection".PHP_EOL."==========".PHP_EOL;
};

$server = listen(sys_get_temp_dir().'/test');
while ($socket = $server->accept()) {
    async($clientHandler, $socket);
}
<?php

$config = new Amp\CodeStyle\Config();
$config->getFinder()
    ->in(__DIR__ . '/examples')
    ->in(__DIR__ . '/lib')
    ->in(__DIR__ . '/test');

$config->setCacheFile(__DIR__ . '/.php_cs.cache');

return $config;
<?php declare(strict_types=1);

namespace Amp\Ipc;

/**
 * Thrown in case a second read operation is attempted while another read operation is still pending.
 */
final class PendingAcceptError extends \Error
{
    public function __construct(
        string $message = 'The previous accept operation must complete before accept can be called again',
        int $code = 0,
        ?\Throwable $previous = null
    ) {
        parent::__construct($message, $code, $previous);
    }
}
<?php declare(strict_types=1);

namespace Amp\Ipc\Sync;

final class ChannelCloseReq
{
}
<?php declare(strict_types=1);

namespace Amp\Ipc\Sync;

class SynchronizationError extends \Error
{
}
<?php declare(strict_types=1);

namespace Amp\Ipc\Sync;

final class PanicError extends \Error
{
    /** @var string Class name of uncaught exception. */
    private $name;

    /** @var string Stack trace of the panic. */
    private $trace;

    /**
     * Creates a new panic error.
     *
     * @param string          $name     The uncaught exception class.
     * @param string          $message  The panic message.
     * @param string          $trace    The panic stack trace.
     * @param \Throwable|null $previous Previous exception.
     */
    public function __construct(string $name, string $message = '', string $trace = '', ?\Throwable $previous = null)
    {
        parent::__construct($message, 0, $previous);

        $this->name = $name;
        $this->trace = $trace;
    }

    /**
     * Returns the class name of the uncaught exception.
     *
     */
    public function getName(): string
    {
        return $this->name;
    }

    /**
     * Gets the stack trace at the point the panic occurred.
     *
     */
    public function getPanicTrace(): string
    {
        return $this->trace;
    }
}
<?php declare(strict_types=1);

namespace Amp\Ipc\Sync;

/**
 * Interface for sending messages between execution contexts.
 */
interface Channel
{
    /**
     * @throws \Amp\Ipc\Sync\SynchronizationError If the context has not been started or the context
     *     unexpectedly ends.
     * @throws \Amp\Ipc\Sync\ChannelException If receiving from the channel fails.
     * @throws \Amp\Ipc\Sync\SerializationException If unserializing the data fails.
     */
    public function receive(): mixed;

    /**
     * @throws \Amp\Ipc\Sync\SynchronizationError If the context has not been started or the context
     *     unexpectedly ends.
     * @throws \Amp\Ipc\Sync\ChannelException If sending on the channel fails.
     * @throws \Error If an ExitResult object is given.
     * @throws \Amp\Ipc\Sync\SerializationException If serializing the data fails.
     */
    public function send(mixed $data): void;
}
<?php declare(strict_types=1);

namespace Amp\Ipc\Sync;

use Amp\ByteStream\ReadableStream;
use Amp\ByteStream\StreamException;
use Amp\ByteStream\WritableStream;
use SplQueue;

/**
 * An asynchronous channel for sending data between threads and processes.
 *
 * Supports full duplex read and write.
 */
final class ChannelledSocket implements Channel
{
    private SplQueue $received;

    private ChannelParser $parser;

    private bool $closed = false;

    /**
     * Creates a new channel from the given stream objects. Note that $read and $write can be the same object.
     *
     */
    public function __construct(private ReadableStream $read, private WritableStream $write)
    {
        $this->received = new \SplQueue;
        $this->parser = new ChannelParser($this->received->push(...));
    }

    /**
     * {@inheritdoc}
     */
    public function send(mixed $data): void
    {
        if ($this->closed) {
            throw new ChannelException('The channel was already closed!');
        }
        try {
            $this->write->write($this->parser->encode($data));
        } catch (StreamException $exception) {
            throw new ChannelException("Sending on the channel failed. Did the context die?", 0, $exception);
        }
    }

    /**
     * {@inheritdoc}
     */
    public function receive(): mixed
    {
        while ($this->received->isEmpty()) {
            try {
                if ($this->closed) {
                    return null;
                }
                $chunk = $this->read->read();
            } catch (StreamException $exception) {
                throw new ChannelException("Reading from the channel failed. Did the context die?", 0, $exception);
            }

            if ($chunk === null) {
                $this->disconnect();
                return null;
            }

            $this->parser->push($chunk);
        }

        $received = $this->received->shift();
        if ($received instanceof ChannelCloseReq) {
            $this->disconnect();
            return null;
        }
        return $received;
    }

    /**
     * Cleanly disconnect from other endpoint.
     */
    public function disconnect(): void
    {
        if ($this->closed) {
            return;
        }
        $this->closed = true;
        try {
            $this->write->write($this->parser->encode(new ChannelCloseReq));
        } catch (\Throwable) {
        }
        $this->read->close();
        $this->write->close();
    }
}
<?php declare(strict_types=1);

namespace Amp\Ipc\Sync;

class SerializationException extends \Exception
{
}
<?php declare(strict_types=1);

namespace Amp\Ipc\Sync;

final class ChannelInitAck
{
}
<?php declare(strict_types=1);

namespace Amp\Ipc\Sync;

class ChannelException extends \Exception
{
}
<?php declare(strict_types=1);

namespace Amp\Ipc\Sync;

use Amp\Parser\Parser;

final class ChannelParser extends Parser
{
    const HEADER_LENGTH = 5;

    /**
     * @param callable(mixed $data) Callback invoked when data is parsed.
     */
    public function __construct(callable $callback)
    {
        parent::__construct(self::parser($callback));
    }

    /**
     * @param mixed $data Data to encode to send over a channel.
     *
     * @return string Encoded data that can be parsed by this class.
     *
     * @throws \Amp\Ipc\Sync\SerializationException
     */
    public function encode($data): string
    {
        try {
            $data = \serialize($data);
        } catch (\Throwable $exception) {
            throw new SerializationException(
                "The given data cannot be sent because it is not serializable.",
                0,
                $exception
            );
        }

        return \pack("CL", 0, \strlen($data)) . $data;
    }

    /**
     * @throws \Amp\Ipc\Sync\ChannelException
     * @throws \Amp\Ipc\Sync\SerializationException
     */
    private static function parser(callable $push): \Generator
    {
        while (true) {
            /** @var string */
            $header = yield self::HEADER_LENGTH;
            $data = \unpack("Cprefix/Llength", $header);

            if ($data["prefix"] !== 0) {
                $data = $header . yield;
                throw new ChannelException("Invalid packet received: " . self::encodeUnprintableChars($data));
            }

            $data = yield $data["length"];

            // Attempt to unserialize the received data.
            try {
                $result = \unserialize($data);

                if ($result === false && $data !== \serialize(false)) {
                    throw new ChannelException("Received invalid data: " . self::encodeUnprintableChars($data));
                }
            } catch (\Throwable $exception) {
                throw new SerializationException("Exception thrown when unserializing data", 0, $exception);
            }

            $push($result);
        }
    }

    /**
     * @param string $data Binary data.
     *
     * @return string Unprintable characters encoded as \x##.
     */
    private static function encodeUnprintableChars(string $data): string
    {
        return \preg_replace_callback("/[^\x20-\x7e]/", function (array $matches) {
            return "\\x" . \dechex(\ord($matches[0]));
        }, $data);
    }
}
<?php declare(strict_types=1);

namespace Amp\Ipc;

/**
 * Thrown in case server connection fails.
 */
final class IpcServerException extends \Exception
{
    private const TYPE_MAP = [
        IpcServer::TYPE_UNIX => 'UNIX',
        IpcServer::TYPE_TCP => 'TCP',
        IpcServer::TYPE_FIFO => 'FIFO',
    ];
    public function __construct(
        array $messages,
        int $code = 0,
        ?\Throwable $previous = null
    ) {
        $message = "Could not create IPC server: ";
        foreach ($messages as $type => $error) {
            $message .= self::TYPE_MAP[$type].": $error; ";
        }
        parent::__construct($message, $code, $previous);
    }
}
<?php declare(strict_types=1);

namespace Amp\Ipc;

use Amp\ByteStream\ReadableResourceStream;
use Amp\ByteStream\WritableResourceStream;
use Amp\DeferredFuture;
use Amp\Ipc\Sync\ChannelInitAck;
use Amp\Ipc\Sync\ChannelledSocket;
use AssertionError;
use Revolt\EventLoop;

use function Amp\async;

class IpcServer
{
    public const TYPE_AUTO = 0;
    public const TYPE_UNIX = 1 << 0;
    public const TYPE_FIFO = 1 << 1;
    public const TYPE_TCP = 1 << 2;
    /** @var resource|null */
    private $server;

    /** @var DeferredFuture<ChannelledSocket> */
    private ?DeferredFuture $acceptor = null;

    private string $watcher;

    private string $uri;

    /** UNIX socket listening with ext-sockets (Windows), accepted with socket_accept */
    private ?\Socket $listener = null;

    /** Path of the UNIX socket a Windows URI file points to */
    private ?string $socketPath = null;

    /**
     * @param string       $uri  Local endpoint on which to listen for requests
     * @param self::TYPE_* $type Server type
     */
    public function __construct(string $uri = '', int $type = self::TYPE_AUTO)
    {
        if (!$uri) {
            $suffix = \bin2hex(\random_bytes(10));
            $uri = \sys_get_temp_dir()."/amp-ipc-".$suffix.".sock";
        }
        if (\file_exists($uri)) {
            @\unlink($uri);
        }
        $this->uri = $uri;

        $isWindows = \PHP_OS_FAMILY === 'Windows';
        $isLinux = \PHP_OS_FAMILY === 'Linux';
        if ($type === self::TYPE_AUTO) {
            $type = self::TYPE_UNIX | self::TYPE_FIFO | self::TYPE_TCP;
        }
        $types = [];
        // On Windows, UNIX sockets are created with ext-sockets and have no path length limit (see listenUnixWindows)
        if ($type & self::TYPE_UNIX && ($isWindows || \strlen($uri) <= 104)) {
            $types []= self::TYPE_UNIX;
        }
        if ($type & self::TYPE_FIFO && $isLinux) {
            $types []= self::TYPE_FIFO;
        }
        if ($type & self::TYPE_TCP) {
            $types []= self::TYPE_TCP;
        }
        if (!$types && $isWindows) {
            throw new \RuntimeException("Cannot use FIFOs on windows");
        }

        $errors = [];
        foreach ($types as $type) {
            if ($type === self::TYPE_UNIX && $isWindows) {
                $error = $this->listenUnixWindows();
                if ($error === null) {
                    break;
                }
                $errors[$type] = $error;
            } elseif ($type === self::TYPE_FIFO) {
                try {
                    if (!\posix_mkfifo($uri, 0777)) {
                        $errors[$type] = "could not create the FIFO socket";
                        continue;
                    }
                } catch (\Throwable $e) {
                    $errors[$type] = "could not create the FIFO socket: $e";
                    continue;
                }
                $error = '';
                try {
                    // Open in r+w mode to prevent blocking if there is no reader
                    $this->server = \fopen($uri, 'r+');
                } catch (\Throwable $e) {
                    $error = "$e";
                }
                if ($this->server) {
                    \stream_set_blocking($this->server, false);
                    break;
                }
                $errors[$type] = "could not connect to the FIFO socket: $error";
            } else {
                $listenUri = $type === self::TYPE_TCP ? "tcp://127.0.0.1:0" : "unix://".$uri;
                $errno = -1;
                $errstr = 'no error';
                try {
                    $this->server = \stream_socket_server($listenUri, $errno, $errstr, \STREAM_SERVER_BIND | \STREAM_SERVER_LISTEN);
                } catch (\Throwable $e) {
                    $errstr = "exception: $e";
                }
                if ($this->server) {
                    if ($type === self::TYPE_TCP) {
                        try {
                            $name = \stream_socket_get_name($this->server, false);
                            $pos = \strrpos($name, ":");
                            if ($pos === false) {
                                throw new AssertionError('No port');
                            }
                            $port = \substr($name, $pos + 1);
                            if (\file_put_contents($this->uri, "tcp://127.0.0.1:".$port) === false) {
                                $errors[$type] = 'could not create URI file';
                                $this->server = null;
                            }
                        } catch (\Throwable $e) {
                            $errors[$type] = "could not create URI file: $e";
                            $this->server = null;
                        }
                        if (!$this->server) {
                            continue;
                        }
                    }
                    break;
                }
                $errors[$type] = "(errno: $errno) $errstr";
            }
        }

        if (!$this->server) {
            throw new IpcServerException($errors);
        }

        /** @psalm-suppress UnsupportedPropertyReferenceUsage */
        $acceptor = &$this->acceptor;
        $listener = $this->listener;
        $this->watcher = EventLoop::onReadable($this->server, static function (string $watcher, $server) use (&$acceptor, $type, $listener): void {
            if ($listener !== null) {
                // The listener is non-blocking: a spurious wakeup returns null
                if (!$client = acceptSocket($listener)) {
                    return;
                }
                $client = exportSocket($client);
                $channel = new ChannelledSocket(
                    new ReadableResourceStream($client),
                    new WritableResourceStream($client)
                );
            } elseif ($type === self::TYPE_FIFO) {
                $length = \unpack('v', \fread($server, 2))[1];
                if (!$length) {
                    return; // Could not accept, wrong length read
                }

                $prefix = \fread($server, $length);
                $sockets = [];

                foreach ([
                    $prefix.'1',
                    $prefix.'2',
                ] as $k => $socket) {
                    if (@\filetype($socket) !== 'fifo') {
                        if ($k) {
                            \fclose($sockets[0]);
                        }
                        return; // Is not a FIFO
                    }

                    // Open in either read or write mode to send a close signal when done
                    if (!$sockets[$k] = \fopen($socket, $k ? 'w' : 'r')) {
                        if ($k) {
                            \fclose($sockets[0]);
                        }
                        return; // Could not open fifo
                    }
                }
                $channel = new ChannelledSocket(
                    new ReadableResourceStream($sockets[0]),
                    new WritableResourceStream($sockets[1])
                );
                async($channel->send(...), new ChannelInitAck);
            } else {
                // Error reporting suppressed since stream_socket_accept() emits E_WARNING on client accept failure.
                if (!$client = @\stream_socket_accept($server, 0)) {  // Timeout of 0 to be non-blocking.
                    return; // Accepting client failed.
                }
                $channel = new ChannelledSocket(
                    new ReadableResourceStream($client),
                    new WritableResourceStream($client)
                );
            }

            $deferred = $acceptor;
            $acceptor = null;

            \assert($deferred !== null);

            $deferred->complete($channel);

            EventLoop::disable($watcher);
        });

        EventLoop::disable($this->watcher);
    }

    public function __destruct()
    {
        $this->close();
    }

    /**
     * @throws PendingAcceptError If another accept request is pending.
     */
    public function accept(): ?ChannelledSocket
    {
        if ($this->acceptor) {
            throw new PendingAcceptError;
        }

        if (!$this->server) {
            return null; // Resolve with null when server is closed.
        }

        $this->acceptor = new DeferredFuture;
        EventLoop::enable($this->watcher);

        return $this->acceptor->getFuture()->await();
    }

    /**
     * Closes the server and stops accepting connections. Any socket clients accepted will not be closed.
     */
    public function close(): void
    {
        EventLoop::cancel($this->watcher);

        if ($this->acceptor) {
            $acceptor = $this->acceptor;
            $this->acceptor = null;
            $acceptor->complete();
        }

        if ($this->server) {
            $server = $this->server;
            $this->server = null;
            \fclose($server);
            $this->listener = null;
            @\unlink($this->uri);
            if ($this->socketPath !== null) {
                @\unlink($this->socketPath);
            }
        }
    }

    /**
     * Listens on a UNIX socket on Windows (10 1803+).
     *
     * PHP streams don't support AF_UNIX on Windows, ext-sockets does: the listening socket is exported to a stream to
     * watch it with the event loop, and accepted connections are exported to streams.
     * Clients can't recognize a socket file on Windows, so like with TCP, the URI is a file with the endpoint.
     *
     * @return string|null The error, or null on success
     */
    private function listenUnixWindows(): ?string
    {
        if (!\extension_loaded('sockets')) {
            return 'the sockets extension is not loaded';
        }
        $path = $this->uri.'.sock';
        if (\strlen($path) > 107) { // sun_path is 108 bytes long
            $path = \sys_get_temp_dir().\DIRECTORY_SEPARATOR.'amp-ipc-'.\bin2hex(\random_bytes(10)).'.sock';
        }
        @\unlink($path);
        try {
            $socket = \socket_create(\AF_UNIX, \SOCK_STREAM, 0);
        } catch (\Throwable $e) {
            return "could not create the UNIX socket: $e";
        }
        if (!$socket) {
            return 'could not create the UNIX socket: '.\socket_strerror(\socket_last_error());
        }
        try {
            if (!\socket_bind($socket, $path)
                || !\socket_listen($socket)
                || !\socket_set_nonblock($socket)
            ) {
                $error = 'could not listen on the UNIX socket: '.\socket_strerror(\socket_last_error($socket));
            } elseif (\file_put_contents($this->uri, 'unix://'.$path) === false) {
                $error = 'could not create URI file';
            } else {
                $this->server = exportSocket($socket);
                $this->listener = $socket;
                $this->socketPath = $path;
                return null;
            }
        } catch (\Throwable $e) {
            $error = "could not listen on the UNIX socket: $e";
        }
        \socket_close($socket);
        @\unlink($path);
        return $error;
    }

    public function isClosed(): bool
    {
        return $this->server === null;
    }

    /**
     * References the accept watcher.
     *
     * @see EventLoop::reference()
     */
    final public function reference(): void
    {
        EventLoop::reference($this->watcher);
    }

    /**
     * Unreferences the accept watcher.
     *
     * @see EventLoop::unreference()
     */
    final public function unreference(): void
    {
        EventLoop::unreference($this->watcher);
    }

    /**
     * Get endpoint to which clients should connect.
     *
     */
    public function getUri(): string
    {
        return $this->uri;
    }
}
<?php declare(strict_types=1);

namespace Amp\Ipc;

use Amp\ByteStream\ReadableResourceStream;
use Amp\ByteStream\WritableResourceStream;
use Amp\Cancellation;
use Amp\CancelledException;
use Amp\CompositeCancellation;
use Amp\Ipc\Sync\ChannelInitAck;
use Amp\Ipc\Sync\ChannelledSocket;
use Amp\TimeoutCancellation;
use AssertionError;

use function Amp\async;

/**
 * Create IPC server.
 *
 * @param string $uri Local endpoint on which to listen for requests
 *
 */
function listen(string $uri): IpcServer
{
    return new IpcServer($uri);
}

/**
 * Connect to IPC server.
 *
 * @param string $uri URI
 */
function connect(string $uri, ?Cancellation $cancellation = null): ChannelledSocket
{
    if (!\file_exists($uri)) {
        throw new \RuntimeException("The endpoint does not exist!");
    }

    do {
        $type = \filetype($uri);
        if ($type !== 'fifo') {
            if ($type === 'file') {
                $uri = \file_get_contents($uri);
                if (\PHP_OS_FAMILY === 'Windows' && \str_starts_with($uri, 'unix://')) {
                    $socket = connectUnixWindows(\substr($uri, 7));
                    return new ChannelledSocket(
                        new ReadableResourceStream($socket),
                        new WritableResourceStream($socket)
                    );
                }
            } else {
                $uri = "unix://$uri";
            }
            if (!$socket = \stream_socket_client($uri, $errno, $errstr, 5, \STREAM_CLIENT_CONNECT)) {
                $message = "Could not connect to IPC socket";
                if ($error = \error_get_last()) {
                    $message .= \sprintf(" Errno: %d; %s", $error["type"], $error["message"]);
                }
                throw new \RuntimeException($message);
            }
            return new ChannelledSocket(
                new ReadableResourceStream($socket),
                new WritableResourceStream($socket)
            );
        }

        $suffix = \bin2hex(\random_bytes(10));
        $prefix = \sys_get_temp_dir()."/amp-".$suffix.".fifo";

        if (\strlen($prefix) > 0xFFFF) {
            throw new \RuntimeException('Prefix is too long!');
        }

        $sockets = [];

        foreach ([
            $prefix."2",
            $prefix."1",
        ] as $k => $socket) {
            if (!\posix_mkfifo($socket, 0777)) {
                throw new \RuntimeException('Could not create FIFO client socket!');
            }

            \register_shutdown_function(static function () use ($socket): void {
                @\unlink($socket);
            });

            if (!$sockets[$k] = \fopen($socket, 'r+')) { // Open in r+w mode to prevent blocking if there is no reader
                throw new \RuntimeException("Could not open FIFO client socket");
            }
        }

        if (!$tempSocket = \fopen($uri, 'r+')) { // Open in r+w mode to prevent blocking if there is no reader
            throw new \RuntimeException("Could not connect to FIFO server");
        }
        \stream_set_blocking($tempSocket, false);
        \stream_set_write_buffer($tempSocket, 0);

        if (\in_array(\fwrite($tempSocket, \pack('v', \strlen($prefix)).$prefix), [false, 0], true)) {
            \fclose($tempSocket);
            unset($tempSocket);
            throw new \RuntimeException("Failure sending request to FIFO server");
        }
        \fclose($tempSocket);
        unset($tempSocket);

        $socket = new ChannelledSocket(
            new ReadableResourceStream($sockets[0]),
            new WritableResourceStream($sockets[1]),
        );

        try {
            $result = async($socket->receive(...))->await(
                $cancellation
                ? new CompositeCancellation(
                    $cancellation,
                    new TimeoutCancellation(0.5)
                )
                : new TimeoutCancellation(0.5)
            );
            if (!$result instanceof ChannelInitAck) {
                throw new \RuntimeException('Missing init ack!');
            }
            return $socket;
        } catch (CancelledException $e) {
            if ($cancellation?->isRequested()) {
                throw $e;
            }
        }
    } while (true);

    throw new AssertionError("Unreachable!");
}

/**
 * Connect to a UNIX socket on Windows, where PHP streams don't support AF_UNIX (see IpcServer::listenUnixWindows).
 *
 * @internal
 *
 * @return resource
 */
function connectUnixWindows(string $path)
{
    if (!\extension_loaded('sockets')) {
        throw new \RuntimeException("The sockets extension is required to connect to the IPC socket $path");
    }
    $socket = \socket_create(\AF_UNIX, \SOCK_STREAM, 0);
    if (!$socket) {
        throw new \RuntimeException("Could not create IPC socket: ".\socket_strerror(\socket_last_error()));
    }
    try {
        $connected = \socket_connect($socket, $path);
    } catch (\Throwable $e) {
        \socket_close($socket);
        throw new \RuntimeException("Could not connect to IPC socket $path", 0, $e);
    }
    if (!$connected) {
        $error = \socket_strerror(\socket_last_error($socket));
        \socket_close($socket);
        throw new \RuntimeException("Could not connect to IPC socket $path: $error");
    }
    return exportSocket($socket);
}

/**
 * Export a UNIX socket of ext-sockets to a stream.
 *
 * On Windows, socket_export_stream() warns that there's no unix:// transport before falling back to a generic socket
 * stream, which works: silence the warning, which error handlers (that may ignore @) would turn into an exception.
 *
 * @internal
 *
 * @return resource
 */
function exportSocket(\Socket $socket)
{
    \set_error_handler(static fn (): bool => true);
    try {
        $stream = \socket_export_stream($socket);
    } finally {
        \restore_error_handler();
    }
    if ($stream === false) {
        throw new \RuntimeException("Could not export the IPC socket to a stream");
    }
    return $stream;
}

/**
 * Accept a connection on a non-blocking ext-sockets listener, without the warning of a spurious wakeup.
 *
 * @internal
 */
function acceptSocket(\Socket $listener): ?\Socket
{
    \set_error_handler(static fn (): bool => true);
    try {
        return \socket_accept($listener) ?: null;
    } finally {
        \restore_error_handler();
    }
}
<?php declare(strict_types=1);

namespace Amp\Ipc\Test;

use Amp\Ipc\IpcServer;
use Amp\Ipc\Sync\ChannelledSocket;
use Amp\PHPUnit\AsyncTestCase;
use Amp\Process\Process;

use function Amp\async;
use function Amp\ByteStream\splitLines;
use function Amp\Ipc\connect;

class IpcTest extends AsyncTestCase
{
    /** @dataProvider provideUriType */
    public function testBasicIPC(string $uri, int $type)
    {
        $process = Process::start([PHP_BINARY, __DIR__.'/Fixtures/server.php', $uri, $type]);

        foreach (splitLines($process->getStdout()) as $recvUri) {
            break;
        }
        if ($uri) {
            $this->assertEquals($uri, $recvUri);
        }

        $client = connect($recvUri);
        $this->assertInstanceOf(ChannelledSocket::class, $client);

        $client->send('ping');
        $this->assertEquals('pong', $client->receive());

        $client->disconnect();

        $this->assertEquals(0, $process->join());
    }

    /** @dataProvider provideUriType */
    public function testIPCDisconectWhileReading(string $uri, int $type)
    {
        $process = Process::start([PHP_BINARY, __DIR__.'/Fixtures/echoServer.php', $uri, $type]);

        foreach (splitLines($process->getStdout()) as $recvUri) {
            break;
        }
        if ($uri) {
            $this->assertEquals($uri, $recvUri);
        }

        $client = connect($recvUri);
        $this->assertInstanceOf(ChannelledSocket::class, $client);

        async(
            static function () use ($client) {
                while ($client->receive());
            }
        );
        $client->disconnect();

        $this->assertEquals(0, $process->join());
    }

    public function testWindowsPrefersUnixSockets()
    {
        if (\PHP_OS_FAMILY !== 'Windows' || !\extension_loaded('sockets')) {
            $this->markTestSkipped('Requires Windows and ext-sockets');
        }
        $server = new IpcServer(\sys_get_temp_dir().'/'.\bin2hex(\random_bytes(10)));
        $this->assertStringStartsWith('unix://', \file_get_contents($server->getUri()));

        $accepted = async($server->accept(...));
        $client = connect($server->getUri());
        $socket = $accepted->await();

        $client->send('ping');
        $this->assertEquals('ping', $socket->receive());
        $socket->send('pong');
        $this->assertEquals('pong', $client->receive());

        $client->disconnect();
        $this->assertNull($socket->receive());

        $server->close();
        $this->assertFileDoesNotExist($server->getUri());
    }

    public function provideUriType(): \Generator
    {
        foreach (['', \sys_get_temp_dir().'/pony', \sys_get_temp_dir().'/'.\str_repeat('a', 200)] as $uri) {
            if (\strncasecmp(\PHP_OS, "WIN", 3) === 0) {
                yield [$uri, IpcServer::TYPE_AUTO];
                yield [$uri, IpcServer::TYPE_TCP];
                if (\extension_loaded('sockets')) {
                    yield [$uri, IpcServer::TYPE_UNIX];
                }
            } else {
                yield [$uri, IpcServer::TYPE_AUTO];
                yield [$uri, IpcServer::TYPE_TCP];
                if (\strlen($uri) < 200) {
                    yield [$uri, IpcServer::TYPE_UNIX];
                }
                if (\strncasecmp(\PHP_OS, "LINUX", 5) === 0) {
                    yield [$uri, IpcServer::TYPE_FIFO];
                }
            }
        }
    }
}
<?php declare(strict_types=1);
error_reporting(E_ALL);
ini_set('log_errors', 1);
ini_set('error_log', '/tmp/amphp.log');
error_log('Inited IPC test!');

require 'vendor/autoload.php';

use Amp\Ipc\IpcServer;
use Amp\Ipc\Sync\ChannelledSocket;

$server = new IpcServer($argv[1], (int) $argv[2]);

echo $server->getUri().PHP_EOL;

$socket = $server->accept();

if (!$socket instanceof ChannelledSocket) {
    throw new \RuntimeException('Socket is not instance of ChannelledSocket');
}

$ping = $socket->receive();

if ($ping !== 'ping') {
    throw new \RuntimeException("Received $ping instead of ping!");
}

$socket->send('pong');
$socket->disconnect();

$server->close();

$server->accept();
<?php declare(strict_types=1);
error_reporting(E_ALL);
ini_set('log_errors', 1);
ini_set('error_log', '/tmp/amphp.log');
error_log('Inited IPC test!');

use Amp\Ipc\IpcServer;
use Amp\Ipc\Sync\ChannelledSocket;

require 'vendor/autoload.php';

$server = new IpcServer($argv[1], (int) $argv[2]);

echo $server->getUri().PHP_EOL;

$socket = $server->accept();

if (!$socket instanceof ChannelledSocket) {
    throw new \RuntimeException('Socket is not instance of ChannelledSocket');
}

while ($socket->receive());
$socket->disconnect();

$server->close();

$server->accept();
{
    "name": "danog/ipc",
    "description": "IPC component for Amp.",
    "keywords": [
        "asynchronous",
        "async",
        "concurrent",
        "multi-threading",
        "multi-processing"
    ],
    "homepage": "https://github.com/danog/ipc",
    "license": "MIT",
    "authors": [
        {
            "name": "Daniil Gentili",
            "email": "daniil@daniil.it"
        },
        {
            "name": "Aaron Piotrowski",
            "email": "aaron@trowski.com"
        },
        {
            "name": "Stephen Coakley",
            "email": "me@stephencoakley.com"
        }
    ],
    "require": {
        "php": ">=8.1",
        "amphp/byte-stream": "^2.1.2",
        "amphp/parser": "^1.1.1",
        "amphp/parallel": "^2.3.1"
    },
    "suggest": {
        "ext-sockets": "Required for UNIX socket support on Windows (TCP is used otherwise)"
    },
    "require-dev": {
        "amphp/amp": "v3.x-dev",
        "phpunit/phpunit": "^9.6.22",
        "amphp/phpunit-util": "v3.x-dev",
        "amphp/php-cs-fixer-config": "v2.x-dev",
        "psalm/phar": "^5.26.1"
    },
    "autoload": {
        "psr-4": {
            "Amp\\Ipc\\": "lib"
        },
        "files": [
            "lib/functions.php"
        ]
    },
    "autoload-dev": {
        "psr-4": {
            "Amp\\Ipc\\Test\\": "test"
        }
    },
    "scripts": {
        "check": [
            "@cs",
            "@test"
        ],
        "cs": "php-cs-fixer fix -v --diff --dry-run",
        "cs-fix": "php-cs-fixer fix -v --diff",
        "test": "@php -dzend.assertions=1 -dassert.exception=1 ./vendor/bin/phpunit --coverage-text"
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Mixin;

use Attribute;

#[Attribute(Attribute::TARGET_CLASS)]
final class DataClass {}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Mixin;

interface SharedLibraryInterface
{
    public function initiateSharedLibrary(): void;
}<?php

namespace Webrtc\Mixin;

use ReflectionClass;
use ReflectionProperty;

/**
 * Export and restore every property of an object, including private parents.
 *
 * PHP's native serializer already walks the hierarchy; this helper exists so a
 * class that replaces a non-serializable property (socket, FFI handle, deferred)
 * can still round-trip the rest of its state.
 */
final class SerializableState
{
    /**
     * @param array<string, mixed> $replacements Property name => value stored instead of the live property
     * @return array<string, mixed>
     */
    public static function export(object $object, array $replacements = []): array
    {
        $state = [];
        $class = new ReflectionClass($object);
        while ($class instanceof ReflectionClass) {
            foreach ($class->getProperties() as $property) {
                if ($property->isStatic() || $property->getDeclaringClass()->getName() !== $class->getName()) {
                    continue;
                }
                $key = $class->getName() . "\0" . $property->getName();
                $name = $property->getName();
                if (array_key_exists($name, $replacements)) {
                    /** @psalm-suppress MixedAssignment */
                    $state[$key] = $replacements[$name];
                    continue;
                }
                if (!$property->isInitialized($object)) {
                    $state[$key] = ['__uninitialized' => true];
                    continue;
                }
                /** @psalm-suppress MixedAssignment */
                $state[$key] = $property->getValue($object);
            }
            $parent = $class->getParentClass();
            $class = $parent !== false ? $parent : null;
        }

        return $state;
    }

    /**
     * Snapshot the keys of a listener WeakMap into a plain list for serialization.
     *
     * A WeakMap cannot itself be serialized, so an emitter that keeps its listeners in one (weakly,
     * so a collected listener drops out on its own) exports the live keys as a list â€” the listener
     * objects are part of the graph and serialize by shared reference â€” and rebuilds the WeakMap on
     * the far side with {@see self::listToWeakMap()}.
     *
     * @template T of object
     * @param \WeakMap<T, null> $map
     * @return list<T>
     */
    public static function weakMapToList(\WeakMap $map): array
    {
        $list = [];
        foreach ($map as $key => $_) {
            $list[] = $key;
        }

        return $list;
    }

    /**
     * Rebuild a listener WeakMap (key => null) from a list produced by {@see self::weakMapToList()}.
     *
     * @template T of object
     * @param list<T> $list
     * @return \WeakMap<T, null>
     */
    public static function listToWeakMap(array $list): \WeakMap
    {
        /** @var \WeakMap<T, null> $map */
        $map = new \WeakMap();
        foreach ($list as $object) {
            $map[$object] = null;
        }

        return $map;
    }

    /**
     * @param array<string, mixed> $state
     */
    public static function import(object $object, array $state): void
    {
        /**
         * @var mixed $value
         */
        foreach ($state as $key => $value) {
            if (!str_contains($key, "\0")) {
                continue;
            }
            $parts = explode("\0", $key, 2);
            if (count($parts) !== 2) {
                continue;
            }
            $className = $parts[0];
            $name = $parts[1];
            if (is_array($value) && ($value['__uninitialized'] ?? false) === true) {
                continue;
            }
            if (!class_exists($className) && !trait_exists($className)) {
                continue;
            }
            // A property removed since the state was saved: a newer version of the class doesn't need it.
            if (!property_exists($className, $name)) {
                continue;
            }
            /** @var class-string $className */
            $property = new ReflectionProperty($className, $name);
            $property->setValue($object, $value);
        }
    }
}
{
    "name": "danog/php-rtc-mixin",
    "type": "library",
    "description": "Common PHP classes and traits for WebRTC components. MadelineProto's PHP 8.2+ fork is a Composer-compatible foundation of the modular danog/php-rtc stack.",
    "keywords": [
        "webrtc"
    ],
    "homepage": "https://github.com/danog/php-rtc-mixin",
    "license": "BSD-3-Clause",
    "authors": [
        {
            "name": "Amin Yazdanpanah",
            "email": "github@aminyazdanpanah.com"
        },
        {
            "name": "Sana Moniri",
            "email": "s.mo@quasarstream.com"
        },
        {
            "name": "Quasar Stream Community"
        },
        {
            "name": "Daniil Gentili",
            "email": "daniil@daniil.it"
        }
    ],
    "require": {
        "php": "^8.2"
    },
    "autoload": {
        "psr-4": {
            "Webrtc\\Mixin\\": "src"
        }
    },
    "minimum-stability": "stable",
    "scripts": {
        "post-install-cmd": [
            "@composer bin all install --ansi"
        ],
        "post-update-cmd": [
            "@composer bin all update --ansi"
        ]
    },
    "config": {
        "allow-plugins": {
            "bamarni/composer-bin-plugin": true
        }
    },
    "extra": {
        "bamarni-bin": {
            "bin-links": true,
            "forward-command": true
        }
    },
    "require-dev": {
        "bamarni/composer-bin-plugin": ">=1.9.1"
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\MDNS;

/**
 * Factory for creating mDNS (Multicast DNS) resolvers.
 *
 * This class initializes a multicast DNS resolver suitable for use with
 * WebRTC local peer discovery and service resolution scenarios.
 */
final class Factory
{
    /**
     * Default mDNS address used for multicast DNS queries (RFC 6762).
     */
    const DNS = '224.0.0.251:5353';

    /**
     * The executor responsible for sending queries over multicast.
     */
    private MulticastExecutor $executor;

    /**
     * Constructs the Factory with an optional custom executor.
     *
     * @param ?MulticastExecutor $executor Optional executor for query resolution.
     */
    public function __construct(?MulticastExecutor $executor = null)
    {
        $this->executor = $executor ?: new MulticastExecutor(self::DNS);
    }

    /**
     * Returns a resolver capable of performing mDNS queries.
     *
     * @return MulticastExecutor A resolver whose resolve() blocks until an answer arrives.
     */
    public function createResolver(): MulticastExecutor
    {
        return $this->executor;
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\MDNS;

use Amp\Socket\InternetAddress;
use Amp\CancelledException;
use Amp\TimeoutCancellation;
use LibDNS\Decoder\Decoder;
use LibDNS\Decoder\DecoderFactory;
use LibDNS\Encoder\Encoder;
use LibDNS\Encoder\EncoderFactory;
use LibDNS\Messages\MessageFactory;
use LibDNS\Messages\MessageTypes;
use LibDNS\Records\QuestionFactory;
use LibDNS\Records\ResourceQTypes;
use Throwable;
use Webrtc\Exception\RuntimeException;
use function Amp\Socket\bindUdpSocket;

/**
 * Resolves names over Multicast DNS.
 *
 * WebRTC peers publish their host candidates as randomly generated `.local` names rather than
 * bare IP addresses, so an ICE agent has to answer those names on the local network: there is
 * no upstream server to ask.
 *
 * @see https://datatracker.ietf.org/doc/html/rfc6762 Multicast DNS
 */
final class MulticastExecutor
{
    private readonly Encoder $encoder;
    private readonly Decoder $decoder;
    private readonly MessageFactory $messageFactory;
    private readonly QuestionFactory $questionFactory;
    private readonly InternetAddress $nameserver;

    /**
     * @param string $nameserver Multicast DNS group and port (default: 224.0.0.251:5353).
     * @param float $timeout How long to wait for an answer, in seconds.
     */
    public function __construct(
        string $nameserver = Factory::DNS,
        private readonly float $timeout = 5.0
    ) {
        $this->nameserver = InternetAddress::fromString($nameserver);
        $this->encoder = (new EncoderFactory())->create();
        $this->decoder = (new DecoderFactory())->create();
        $this->messageFactory = new MessageFactory();
        $this->questionFactory = new QuestionFactory();
    }

    /**
     * Ask the local network for the address behind a name.
     *
     * @param string $name The name to resolve, normally a WebRTC `.local` candidate.
     * @return string The address from the first matching answer record.
     * @throws RuntimeException If nobody answered in time, or the socket closed first.
     */
    public function resolve(string $name): string
    {
        $socket = bindUdpSocket('0.0.0.0:0');

        try {
            $socket->send($this->nameserver, $this->encodeQuery($name));

            // Responders other than the one being asked for also answer on this group, so keep
            // reading until an answer actually carries the name in question rather than taking
            // whichever datagram arrives first.
            $cancellation = new TimeoutCancellation($this->timeout);

            while (($received = $socket->receive($cancellation)) !== null) {
                [, $data] = $received;

                $address = $this->addressFromResponse($data, $name);
                if ($address !== null) {
                    return $address;
                }
            }

            throw new RuntimeException(sprintf('The mDNS socket closed while resolving %s', $name));
        } catch (CancelledException) {
            throw new RuntimeException(sprintf('DNS query for %s timed out', $name));
        } finally {
            $socket->close();
        }
    }

    /**
     * Build the query datagram for a name.
     */
    private function encodeQuery(string $name): string
    {
        $question = $this->questionFactory->create(ResourceQTypes::A);
        $question->setName($name);

        $request = $this->messageFactory->create(MessageTypes::QUERY);
        $request->getQuestionRecords()->add($question);
        // RFC 6762 section 18.1: a multicast query carries no transaction id, because responses
        // are matched on the question rather than on the id.
        $request->setID(0);

        return $this->encoder->encode($request);
    }

    /**
     * Pull the address for a name out of a response, or null if it is not about that name.
     */
    private function addressFromResponse(string $data, string $name): ?string
    {
        try {
            $response = $this->decoder->decode($data);
        } catch (Throwable) {
            // Another responder's unrelated or malformed traffic: keep waiting.
            return null;
        }

        /** @var \LibDNS\Records\Resource $record */
        foreach ($response->getAnswerRecords() as $record) {
            // Names come back fully qualified, i.e. with the root label's trailing dot.
            if (strcasecmp(rtrim((string) $record->getName(), '.'), rtrim($name, '.')) !== 0) {
                continue;
            }

            /** @var \LibDNS\Records\Types\Type $field */
            foreach ($record->getData() as $field) {
                return (string) $field;
            }
        }

        return null;
    }
}
{
    "name": "danog/php-rtc-mdns",
    "type": "library",
    "description": "An Amp v3 Multicast DNS resolver for WebRTC. MadelineProto's PHP 8.2+ fork is a Composer-compatible component of the modular danog/php-rtc stack.",
    "keywords": [
        "multicastdns",
        "mdns"
    ],
    "homepage": "https://github.com/danog/php-rtc-mdns",
    "license": "BSD-3-Clause",
    "authors": [
        {
            "name": "Amin Yazdanpanah",
            "email": "github@aminyazdanpanah.com"
        },
        {
            "name": "Sana Moniri",
            "email": "s.mo@quasarstream.com"
        },
        {
            "name": "Quasar Stream Community"
        },
        {
            "name": "Daniil Gentili",
            "email": "daniil@daniil.it"
        }
    ],
    "require": {
        "php": "^8.2",
        "amphp/amp": "^3.1.3",
        "amphp/socket": "^2.4.1",
        "daverandom/libdns": "^2.1.0",
        "danog/php-rtc-exception": "^1.1.5"
    },
    "require-dev": {
        "phpunit/phpunit": "^11.5 || ^12.0 || ^13.3.2",
        "bamarni/composer-bin-plugin": "^1.9.1"
    },
    "autoload": {
        "psr-4": {
            "Webrtc\\MDNS\\": "src"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\Webrtc\\MDNS\\": "tests"
        }
    },
    "minimum-stability": "stable",
    "scripts": {
        "post-install-cmd": [
            "@composer bin all install --ansi"
        ],
        "post-update-cmd": [
            "@composer bin all update --ansi"
        ]
    },
    "config": {
        "allow-plugins": {
            "bamarni/composer-bin-plugin": true
        }
    },
    "extra": {
        "bamarni-bin": {
            "bin-links": true,
            "forward-command": true
        }
    }
}
<?php

$config = new Amp\CodeStyle\Config();
$config->getFinder()
    ->in(__DIR__ . '/examples')
    ->in(__DIR__ . '/lib')
    ->in(__DIR__ . '/test');

$config->setCacheFile(__DIR__ . '/.php_cs.cache');

return $config;
<?php declare(strict_types=1);

namespace Amp\DoH;

/**
 * Thrown when DoH resolution fails.
 */
final class DoHException extends \Exception
{
}
<?php declare(strict_types=1);

namespace Amp\DoH;

use Amp\Cache\Cache;
use Amp\Cache\LocalCache;
use Amp\Dns\DnsConfigException;
use Amp\Dns\DnsConfigLoader;
use Amp\Dns\DnsResolver;
use Amp\Dns\Rfc1035StubDnsResolver;
use Amp\Dns\UnixDnsConfigLoader;
use Amp\Dns\WindowsDnsConfigLoader;
use Amp\Http\Client\HttpClient;
use Amp\Http\Client\HttpClientBuilder;

final class DoHConfig
{
    /**
     * @var non-empty-array<DoHNameserver> $nameservers
     */
    private readonly array $nameservers;
    private readonly HttpClient $httpClient;
    private readonly DnsResolver $subResolver;
    private readonly DnsConfigLoader $configLoader;
    private readonly Cache $cache;

    /**
     * @param non-empty-array<DoHNameserver> $nameservers
     */
    public function __construct(array $nameservers, ?HttpClient $httpClient = null, ?DnsResolver $resolver = null, ?DnsConfigLoader $configLoader = null, ?Cache $cache = null)
    {
        /** @psalm-suppress TypeDoesNotContainType */
        if (\count($nameservers) < 1) {
            throw new DnsConfigException("At least one nameserver is required for a valid config");
        }

        foreach ($nameservers as $nameserver) {
            /** @psalm-suppress DocblockContradiction */
            if (!($nameserver instanceof DoHNameserver)) {
                throw new DnsConfigException("Invalid nameserver: {$nameserver}");
            }
        }

        $this->nameservers = $nameservers;
        $this->httpClient = $httpClient ?? HttpClientBuilder::buildDefault();
        $this->cache = $cache ?? new LocalCache(256, 5.0);
        $this->configLoader = $configLoader ?? (\stripos(PHP_OS, "win") === 0
            ? new WindowsDnsConfigLoader()
            : new UnixDnsConfigLoader());
        $this->subResolver = $resolver ?? new Rfc1035StubDnsResolver(null, $this->configLoader);
    }

    /**
     * @return non-empty-array<DoHNameserver>
     */
    public function getDoHNameservers(): array
    {
        return $this->nameservers;
    }

    public function isDoHNameserver(string $string): bool
    {
        foreach ($this->nameservers as $nameserver) {
            if ($nameserver->getHost() === $string) {
                return true;
            }
        }
        return false;
    }

    public function getHttpClient(): HttpClient
    {
        return $this->httpClient;
    }

    public function getCache(): Cache
    {
        return $this->cache;
    }
    public function getConfigLoader(): DnsConfigLoader
    {
        return $this->configLoader;
    }
    public function getSubResolver(): DnsResolver
    {
        return $this->subResolver;
    }
}
<?php declare(strict_types=1);

namespace Amp\DoH;

enum DoHNameserverType
{
    case RFC8484_GET;
    case RFC8484_POST;
    case GOOGLE_JSON;
}
<?php declare(strict_types=1);

namespace Amp\DoH;

use Amp\Cache\Cache;
use Amp\Cancellation;
use Amp\CompositeException;
use Amp\Dns\DnsConfig;
use Amp\Dns\DnsConfigException;
use Amp\Dns\DnsConfigLoader;
use Amp\Dns\DnsException;
use Amp\Dns\DnsRecord;
use Amp\Dns\DnsResolver;
use Amp\Dns\DnsTimeoutException;
use Amp\Dns\MissingDnsRecordException;
use Amp\Dns\Rfc1035StubDnsResolver;
use Amp\Future;
use Amp\Http\Client\HttpClient;
use Amp\Http\Client\Request;
use Amp\NullCancellation;
use danog\LibDNSJson\JsonDecoder;
use danog\LibDNSJson\JsonDecoderFactory;
use danog\LibDNSJson\QueryEncoder;
use danog\LibDNSJson\QueryEncoderFactory;
use LibDNS\Decoder\Decoder;
use LibDNS\Decoder\DecoderFactory;
use LibDNS\Encoder\Encoder;
use LibDNS\Encoder\EncoderFactory;
use LibDNS\Messages\Message;
use LibDNS\Messages\MessageFactory;
use LibDNS\Messages\MessageTypes;
use LibDNS\Records\Question;
use LibDNS\Records\QuestionFactory;

use function Amp\async;
use function Amp\Dns\normalizeName;

final class Rfc8484StubDoHResolver implements DnsResolver
{
    const CACHE_PREFIX = "amphp.doh.";

    private DnsConfigLoader $configLoader;
    private QuestionFactory $questionFactory;
    private ?DnsConfig $config = null;

    private ?Future $pendingConfig = null;

    private Cache $cache;

    /** @var Future[] */
    private array $pendingQueries = [];

    private DnsResolver $subResolver;
    private Encoder $encoder;
    private Decoder $decoder;
    private QueryEncoder $encoderJson;
    private JsonDecoder $decoderJson;
    private MessageFactory $messageFactory;
    private HttpClient $httpClient;

    public function __construct(private DoHConfig $dohConfig)
    {
        $this->cache = $dohConfig->getCache();
        $this->configLoader = $dohConfig->getConfigLoader();
        $this->subResolver = $dohConfig->getSubResolver();
        $this->questionFactory = new QuestionFactory;
        $this->encoder = (new EncoderFactory)->create();
        $this->decoder = (new DecoderFactory)->create();
        $this->encoderJson = (new QueryEncoderFactory)->create();
        $this->decoderJson = (new JsonDecoderFactory)->create();
        $this->httpClient = $dohConfig->getHttpClient();
        $this->messageFactory = new MessageFactory;
    }

    /** @inheritdoc */
    public function resolve(string $name, ?int $typeRestriction = null, ?Cancellation $cancellation = null): array
    {
        if ($typeRestriction !== null && $typeRestriction !== DnsRecord::A && $typeRestriction !== DnsRecord::AAAA) {
            throw new \Error("Invalid value for parameter 2: null|DnsRecord::A|DnsRecord::AAAA expected");
        }

        if (!$this->config) {
            try {
                $this->reloadConfig();
            } catch (DnsConfigException $e) {
                $this->config = new DnsConfig(['0.0.0.0'], []);
            }
        }

        switch ($typeRestriction) {
            case DnsRecord::A:
                if (\filter_var($name, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
                    return [new DnsRecord($name, DnsRecord::A, null)];
                }

                if (\filter_var($name, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
                    throw new DnsException("Got an IPv6 address, but type is restricted to IPv4");
                }
                break;
            case DnsRecord::AAAA:
                if (\filter_var($name, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
                    return [new DnsRecord($name, DnsRecord::AAAA, null)];
                }

                if (\filter_var($name, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
                    throw new DnsException("Got an IPv4 address, but type is restricted to IPv6");
                }
                break;
            default:
                if (\filter_var($name, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
                    return [new DnsRecord($name, DnsRecord::A, null)];
                }

                if (\filter_var($name, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
                    return [new DnsRecord($name, DnsRecord::AAAA, null)];
                }
                break;
        }

        $dots = \substr_count($name, ".");
        $trailingDot = $name[-1] === ".";
        $name = normalizeName($name);

        if ($records = $this->queryHosts($name, $typeRestriction)) {
            return $records;
        }

        // Follow RFC 6761 and never send queries for localhost to the caching DNS server
        // Usually, these queries are already resolved via queryHosts()
        if ($name === 'localhost') {
            return $typeRestriction === DnsRecord::AAAA
                ? [new DnsRecord('::1', DnsRecord::AAAA, null)]
                : [new DnsRecord('127.0.0.1', DnsRecord::A, null)];
        }

        if ($this->dohConfig->isDoHNameserver($name)) {
            return $this->subResolver->resolve($name, $typeRestriction, $cancellation);
        }
        \assert($this->config !== null);

        $searchList = [null];
        if (!$trailingDot && $dots < $this->config->getNdots()) {
            $searchList = \array_merge($this->config->getSearchList(), $searchList);
        }

        foreach ($searchList as $searchIndex => $search) {
            for ($redirects = 0; $redirects < 5; $redirects++) {
                $searchName = $name;

                if ($search !== null) {
                    $searchName = $name . "." . $search;
                }

                try {
                    if ($typeRestriction) {
                        return $this->query($searchName, $typeRestriction, $cancellation);
                    }

                    [$exceptions, $records] = Future\awaitAll([
                        async(fn () => $this->query($searchName, DnsRecord::A, $cancellation)),
                        async(fn () => $this->query($searchName, DnsRecord::AAAA, $cancellation)),
                    ]);

                    if (\count($exceptions) === 2) {
                        $errors = [];

                        foreach ($exceptions as $reason) {
                            if ($reason instanceof MissingDnsRecordException) {
                                throw $reason;
                            }

                            if ($searchIndex < \count($searchList) - 1 && \in_array($reason->getCode(), [2, 3], true)) {
                                continue 2;
                            }

                            $errors[] = $reason->getMessage();
                        }

                        throw new DnsException(
                            "All query attempts failed for {$searchName}: " . \implode(", ", $errors),
                            0,
                            new CompositeException($exceptions)
                        );
                    }

                    return \array_merge(...$records);
                } catch (MissingDnsRecordException) {
                    try {
                        $cnameRecords = $this->query($searchName, DnsRecord::CNAME, $cancellation);
                        $name = $cnameRecords[0]->getValue();
                        continue;
                    } catch (MissingDnsRecordException) {
                        $dnameRecords = $this->query($searchName, DnsRecord::DNAME, $cancellation);
                        $name = $dnameRecords[0]->getValue();
                        continue;
                    }
                } catch (DnsException $e) {
                    if ($searchIndex < \count($searchList) - 1 && \in_array($e->getCode(), [2, 3], true)) {
                        continue 2;
                    }

                    throw $e;
                }
            }
        }

        \assert(isset($searchName));

        throw new DnsException("Giving up resolution of '{$searchName}', too many redirects");
    }

    /**
     * Reloads the configuration in the background.
     *
     * Once it's finished, the configuration will be used for new requests.
     */
    public function reloadConfig(): void
    {
        if (!$this->pendingConfig) {
            $promise = async(function () {
                try {
                    if ($this->subResolver instanceof Rfc1035StubDnsResolver) {
                        $this->subResolver->reloadConfig();
                    }
                    $this->config = $this->configLoader->loadConfig();
                } finally {
                    $this->pendingConfig = null;
                }
            });
            $this->pendingConfig = $promise;
        }

        $this->pendingConfig->await();
    }

    private function queryHosts(string $name, ?int $typeRestriction = null): array
    {
        \assert($this->config !== null);
        $hosts = $this->config->getKnownHosts();
        $records = [];

        $returnIPv4 = $typeRestriction === null || $typeRestriction === DnsRecord::A;
        $returnIPv6 = $typeRestriction === null || $typeRestriction === DnsRecord::AAAA;

        if ($returnIPv4 && isset($hosts[DnsRecord::A][$name])) {
            $records[] = new DnsRecord($hosts[DnsRecord::A][$name], DnsRecord::A, null);
        }

        if ($returnIPv6 && isset($hosts[DnsRecord::AAAA][$name])) {
            $records[] = new DnsRecord($hosts[DnsRecord::AAAA][$name], DnsRecord::AAAA, null);
        }

        return $records;
    }

    /** @inheritdoc */
    public function query(string $name, int $type, ?Cancellation $cancellation = null): array
    {
        $cancellation ??= new NullCancellation;
        $pendingQueryKey = $type." ".$name;

        if (isset($this->pendingQueries[$pendingQueryKey])) {
            return $this->pendingQueries[$pendingQueryKey]->await($cancellation);
        }

        $promise = async(function () use ($name, $type, $cancellation, $pendingQueryKey) {
            try {
                if (!$this->config) {
                    try {
                        $this->reloadConfig();
                    } catch (DnsConfigException $e) {
                        $this->config = new DnsConfig(['0.0.0.0'], []);
                    }
                }

                \assert($this->config !== null);

                $name = $this->normalizeName($name, $type);
                $question = $this->createQuestion($name, $type);

                if (null !== $cachedValue = $this->cache->get($this->getCacheKey($name, $type))) {
                    return $this->decodeCachedResult($name, $type, $cachedValue);
                }

                $nameservers = $this->dohConfig->getDoHNameservers();
                $attempts = $this->config->getAttempts() * \count($nameservers);
                $attempt = 0;

                $nameserver = $nameservers[0];

                $attemptDescription = [];

                while ($attempt < $attempts) {
                    try {
                        $attemptDescription[] = $nameserver;

                        $response = $this->ask($nameserver, $question, $cancellation);
                        $this->assertAcceptableResponse($response);

                        if ($response->isTruncated()) {
                            throw new DnsException("Server returned a truncated response for '{$name}' (".DnsRecord::getName($type).")");
                        }

                        $answers = $response->getAnswerRecords();
                        $result = [];
                        $ttls = [];

                        foreach ($answers as $record) {
                            $recordType = $record->getType();
                            $result[$recordType][] = (string) $record->getData();

                            // Cache for max one day
                            $ttls[$recordType] = \min($ttls[$recordType] ?? 86400, $record->getTTL());
                        }

                        foreach ($result as $recordType => $records) {
                            // We don't care here whether storing in the cache fails
                            $this->cache->set($this->getCacheKey($name, $recordType), \json_encode($records), $ttls[$recordType]);
                        }

                        if (!isset($result[$type])) {
                            // "it MUST NOT cache it for longer than five (5) minutes" per RFC 2308 section 7.1
                            $this->cache->set($this->getCacheKey($name, $type), \json_encode([]), 300);
                            throw new MissingDnsRecordException("No records returned for '{$name}' (".DnsRecord::getName($type).")");
                        }

                        return \array_map(function ($data) use ($type, $ttls) {
                            return new DnsRecord($data, $type, $ttls[$type]);
                        }, $result[$type]);
                    } catch (DnsTimeoutException) {
                        $i = ++$attempt % \count($nameservers);
                        $nameserver = $nameservers[$i];
                    }
                }

                throw new DnsTimeoutException(\sprintf(
                    "No response for '%s' (%s) from any nameserver within %d ms after %d attempts, tried %s",
                    $name,
                    DnsRecord::getName($type),
                    $this->config->getTimeout(),
                    $attempts,
                    \implode(", ", $attemptDescription)
                ));
            } finally {
                unset($this->pendingQueries[$pendingQueryKey]);
            }
        });

        $this->pendingQueries[$pendingQueryKey] = $promise;

        return $promise->await($cancellation);
    }
    /**
     * Base64URL encode.
     *
     * @param string $data Data to encode
     */
    private static function base64urlEncode(string $data): string
    {
        return \rtrim(\strtr(\base64_encode($data), '+/', '-_'), '=');
    }

    private function ask(DoHNameserver $nameserver, Question $question, Cancellation $cancellation): Message
    {
        $message = $this->createMessage($question, \random_int(0, 0xffff));
        $request = null;
        switch ($nameserver->getType()) {
            case DoHNameserverType::RFC8484_GET:
                $data = $this->encoder->encode($message);
                $request = new Request($nameserver->getUri().'?'.\http_build_query(['dns' => self::base64urlEncode($data), 'ct' => 'application/dns-message']), "GET");
                $request->setHeaders($nameserver->getHeaders());
                $request->setHeader('accept', 'application/dns-message');
                break;
            case DoHNameserverType::RFC8484_POST:
                $data = $this->encoder->encode($message);
                $request = new Request($nameserver->getUri(), "POST");
                $request->setBody($data);
                $request->setHeaders($nameserver->getHeaders());
                $request->setHeader('content-type', 'application/dns-message');
                $request->setHeader('accept', 'application/dns-message');
                $request->setHeader('content-length', (string) \strlen($data));
                break;
            case DoHNameserverType::GOOGLE_JSON:
                $data = $this->encoderJson->encode($message);
                $request = new Request($nameserver->getUri().'?'.$data, "GET");
                $request->setHeaders($nameserver->getHeaders());
                $request->setHeader('accept', 'application/dns-json');
                break;
        }

        $response = $this->httpClient->request($request, $cancellation);
        if ($response->getStatus() !== 200) {
            throw new DoHException("HTTP result !== 200: ".$response->getStatus()." ".$response->getReason(), $response->getStatus());
        }
        $response = $response->getBody()->buffer();

        switch ($nameserver->getType()) {
            case DoHNameserverType::RFC8484_GET:
            case DoHNameserverType::RFC8484_POST:
                return $this->decoder->decode($response);
            case DoHNameserverType::GOOGLE_JSON:
                return $this->decoderJson->decode($response, $message->getID());
        }
    }

    private function normalizeName(string $name, int $type): string
    {
        if ($type === DnsRecord::PTR) {
            if (($packedIp = @\inet_pton($name)) !== false) {
                if (isset($packedIp[4])) { // IPv6
                    $name = \wordwrap(\strrev(\bin2hex($packedIp)), 1, ".", true).".ip6.arpa";
                } else { // IPv4
                    $name = \inet_ntop(\strrev($packedIp)).".in-addr.arpa";
                }
            }
        } elseif (\in_array($type, [DnsRecord::A, DnsRecord::AAAA])) {
            $name = normalizeName($name);
        }

        return $name;
    }

    private function createQuestion(string $name, int $type): Question
    {
        if (0 > $type || 0xffff < $type) {
            $message = \sprintf('%d does not correspond to a valid record type (must be between 0 and 65535).', $type);
            throw new \Error($message);
        }

        $question = $this->questionFactory->create($type);
        $question->setName($name);

        return $question;
    }

    private function createMessage(Question $question, int $id): Message
    {
        $request = $this->messageFactory->create(MessageTypes::QUERY);
        $request->getQuestionRecords()->add($question);
        $request->isRecursionDesired(true);
        $request->setID($id);
        return $request;
    }

    private function getCacheKey(string $name, int $type): string
    {
        return self::CACHE_PREFIX.$name."#".$type;
    }

    /**
     * @return list<DnsRecord>
     */
    private function decodeCachedResult(string $name, int $type, string $encoded): array
    {
        $decoded = \json_decode($encoded, true);

        if (!$decoded) {
            throw new MissingDnsRecordException("No records returned for {$name} (cached result)");
        }

        $result = [];

        foreach ($decoded as $data) {
            $result[] = new DnsRecord($data, $type);
        }

        return $result;
    }

    private function assertAcceptableResponse(Message $response): void
    {
        if ($response->getResponseCode() !== 0) {
            throw new DnsException(\sprintf("Server returned error code: %d", $response->getResponseCode()));
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\DoH;

use Amp\Dns\DnsConfigException;

final class DoHNameserver
{
    public const RFC8484_GET = DoHNameserverType::RFC8484_GET;
    public const RFC8484_POST = DoHNameserverType::RFC8484_POST;
    public const GOOGLE_JSON = DoHNameserverType::GOOGLE_JSON;

    private readonly string $host;

    public function __construct(
        private readonly string $uri,
        private readonly DoHNameserverType $type = DoHNameserverType::RFC8484_POST,
        private readonly array $headers = []
    ) {
        if (\parse_url($uri, PHP_URL_SCHEME) !== 'https') {
            throw new DnsConfigException('Did not provide a valid HTTPS url!');
        }
        $this->host = \parse_url($uri, PHP_URL_HOST);
    }
    public function getUri(): string
    {
        return $this->uri;
    }
    public function getHost(): string
    {
        return $this->host;
    }
    public function getHeaders(): array
    {
        return $this->headers;
    }
    public function getType(): DoHNameserverType
    {
        return $this->type;
    }
    public function __toString(): string
    {
        return $this->uri;
    }
}
{
    "name": "danog/dns-over-https",
    "homepage": "https://github.com/danog/dns-over-https",
    "description": "Async DNS-over-HTTPS resolution for Amp.",
    "keywords": [
        "dns",
        "doh",
        "dns-over-https",
        "https",
        "resolve",
        "client",
        "async",
        "amp",
        "amphp"
    ],
    "license": "MIT",
    "authors": [
        {
            "name": "Daniil Gentili",
            "email": "daniil@daniil.it"
        },
        {
            "name": "Chris Wright",
            "email": "addr@daverandom.com"
        },
        {
            "name": "Daniel Lowrey",
            "email": "rdlowrey@php.net"
        },
        {
            "name": "Bob Weinand",
            "email": "bobwei9@hotmail.com"
        },
        {
            "name": "Niklas Keller",
            "email": "me@kelunik.com"
        },
        {
            "name": "Aaron Piotrowski",
            "email": "aaron@trowski.com"
        }
    ],
    "require": {
        "php": ">=8.1",
        "amphp/cache": "^2",
        "amphp/parser": "^1",
        "danog/libdns-json": "^0.2",
        "daverandom/libdns": "^2.0.1",
        "amphp/amp": "^3",
        "amphp/http-client": "^5",
        "amphp/socket": "^2",
        "amphp/dns": "^2",
        "ext-filter": "*",
        "ext-json": "*"
    },
    "prefer-stable": true,
    "require-dev": {
        "amphp/phpunit-util": "^3",
        "phpunit/phpunit": "^9",
        "amphp/php-cs-fixer-config": "^2",
        "vimeo/psalm": "dev-master"
    },
    "autoload": {
        "psr-4": {
            "Amp\\DoH\\": "lib"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Amp\\DoH\\Test\\": "test"
        }
    },
    "scripts": {
        "check": [
            "@cs",
            "@test"
        ],
        "cs": "PHP_CS_FIXER_IGNORE_ENV=1 php-cs-fixer fix -v --diff --dry-run",
        "cs-fix": "PHP_CS_FIXER_IGNORE_ENV=1 php-cs-fixer fix -v --diff",
        "test": "@php -dzend.assertions=1 -dassert.exception=1 ./vendor/bin/phpunit --coverage-text"
    }
}
<?php declare(strict_types=1);

namespace MadelinePlugin\Danogentili;

use danog\MadelineProto\EventHandler\Attributes\Cron;
use danog\MadelineProto\EventHandler\Filter\FilterText;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\SimpleFilter\Incoming;
use danog\MadelineProto\PluginEventHandler;

/**
 * Plugin event handler class.
 *
 * All properties returned by __sleep are automatically stored in the database.
 */
class PingPlugin extends PluginEventHandler
{
    private int $pingCount = 0;

    private string $pongText = 'pong';

    /**
     * You can set a custom pong text from the outside of the plugin:.
     *
     * ```
     * if (!file_exists('madeline.php')) {
     *     copy('https://phar.madelineproto.xyz/madeline.php', 'madeline.php');
     * }
     * include 'madeline.php';
     *
     * $a = new API('bot.madeline');
     * $plugin = $a->getPlugin(PingPlugin::class);
     *
     * $plugin->setPongText('UwU');
     * ```
     *
     * This will automatically connect to the running instance of the plugin and call the specified method.
     */
    public function setPongText(string $pong): void
    {
        $this->pongText = $pong;
    }

    /**
     * Returns a list of names for properties that will be automatically saved to the session database (MySQL/postgres/redis if configured, the session file otherwise).
     */
    public function __sleep(): array
    {
        return ['pingCount', 'pongText'];
    }
    /**
     * Initialization logic.
     */
    public function onStart(): void
    {
        $this->logger("The bot was started!");
        $this->logger($this->getFullInfo('MadelineProto'));

        $this->sendMessageToAdmins("The bot was started!");
    }

    /**
     * Plugins may be enabled or disabled at startup by returning true or false from this function.
     */
    public function isPluginEnabled(): bool
    {
        return true;
    }

    /**
     * This cron function will be executed forever, every 60 seconds.
     */
    #[Cron(period: 60.0)]
    public function cron1(): void
    {
        $this->sendMessageToAdmins("The ping plugin is online, total pings so far: ".$this->pingCount);
    }

    #[FilterText('ping')]
    public function pingCommand(Incoming&Message $message): void
    {
        $message->reply($this->pongText);
        $this->pingCount++;
    }
}
<?php declare(strict_types=1);

namespace MadelinePlugin\Danogentili;

use danog\MadelineProto\EventHandler\Attributes\Cron;
use danog\MadelineProto\EventHandler\Filter\FilterCommand;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\SimpleFilter\FromAdmin;
use danog\MadelineProto\EventHandler\SimpleFilter\Incoming;
use danog\MadelineProto\PluginEventHandler;

final class OnlinePlugin extends PluginEventHandler
{
    private bool $isOnline = true;

    /**
     * Returns a list of names for properties that will be automatically saved to the session database (MySQL/postgres/redis if configured, the session file otherwise).
     */
    public function __sleep(): array
    {
        return ['isOnline'];
    }

    public function setOnline(bool $online): void
    {
        $this->isOnline = $online;
    }

    public function isPluginEnabled(): bool
    {
        // Only users can be online/offline
        return $this->getSelf()['bot'] === false;
    }

    #[Cron(period: 60.0)]
    public function cron(): void
    {
        $this->account->updateStatus(offline: !$this->isOnline);
    }

    #[FilterCommand('online')]
    public function toggleOnline(Incoming&Message&FromAdmin $message): void
    {
        $this->isOnline = true;
    }

    #[FilterCommand('offline')]
    public function toggleOffline(Incoming&Message&FromAdmin $message): void
    {
        $this->isOnline = false;
    }
}
<?php declare(strict_types=1);
/**
 * Example bot.
 *
 * PHP 8.2.4+ is required.
 *
 * Copyright 2016-2020 Daniil Gentili
 * (https://daniil.it)
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

use danog\MadelineProto\API;
use danog\MadelineProto\Broadcast\Progress;
use danog\MadelineProto\Broadcast\Status;
use danog\MadelineProto\EventHandler\Attributes\Cron;
use danog\MadelineProto\EventHandler\Attributes\Handler;
use danog\MadelineProto\EventHandler\Calls\PrivateCall;
use danog\MadelineProto\EventHandler\Filter\FilterCommand;
use danog\MadelineProto\EventHandler\Filter\FilterRegex;
use danog\MadelineProto\EventHandler\Filter\FilterText;
use danog\MadelineProto\EventHandler\Filter\FilterTextCaseInsensitive;
use danog\MadelineProto\EventHandler\Media\Document;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Message\ChannelMessage;
use danog\MadelineProto\EventHandler\Message\Service\DialogPhotoChanged;
use danog\MadelineProto\EventHandler\Plugin\RestartPlugin;
use danog\MadelineProto\EventHandler\SimpleFilter\FromAdmin;
use danog\MadelineProto\EventHandler\SimpleFilter\Incoming;
use danog\MadelineProto\EventHandler\SimpleFilter\IsReply;
use danog\MadelineProto\Logger;
use danog\MadelineProto\ParseMode;
use danog\MadelineProto\RemoteUrl;
use danog\MadelineProto\Settings;
use danog\MadelineProto\Settings\Database\Mysql;
use danog\MadelineProto\Settings\Database\Postgres;
use danog\MadelineProto\Settings\Database\Redis;
use danog\MadelineProto\SimpleEventHandler;

use function Amp\Socket\SocketAddress\fromString;

// MadelineProto is already loaded
if (class_exists(API::class)) {
    // Otherwise, if a stable version of MadelineProto was installed via composer, load composer autoloader
} elseif (file_exists('vendor/autoload.php')) {
    require_once 'vendor/autoload.php';
} else {
    // Otherwise download an !!! alpha !!! version of MadelineProto via madeline.php
    if (!file_exists('madeline.php')) {
        copy('https://phar.madelineproto.xyz/madeline.php', 'madeline.php');
    }
    require_once 'madeline.php';
}

/**
 * Event handler class.
 *
 * NOTE: ALL of the following methods are OPTIONAL.
 * You can even provide an empty event handler if you want.
 *
 * All properties returned by __sleep are automatically stored in the database.
 */
class MyEventHandler extends SimpleEventHandler
{
    /**
     * @var int|string Username or ID of bot admin
     */
    public const ADMIN = "@me"; // !!! Change this to your username !!!

    /**
     * @var array<int, bool>
     */
    private array $notifiedChats = [];

    /**
     * Returns a list of names for properties that will be automatically saved to the session database (MySQL/postgres/redis if configured, the session file otherwise).
     */
    public function __sleep(): array
    {
        return ['notifiedChats'];
    }

    /**
     * Get peer(s) where to report errors.
     *
     * @return int|string|array
     */
    public function getReportPeers()
    {
        return [self::ADMIN];
    }
    /**
     * Initialization logic.
     */
    public function onStart(): void
    {
        $this->logger("The bot was started!");
        $this->logger($this->getFullInfo('MadelineProto'));

        $this->sendMessageToAdmins("The bot was started!");
    }

    /**
     * Returns a set of plugins to activate.
     */
    public static function getPlugins(): array
    {
        return [
            // Offers a /restart command to admins that can be used to restart the bot, applying changes.
            // Make sure to run in a bash while loop when running via CLI to allow self-restarts.
            RestartPlugin::class,
        ];
    }

    /**
     * This cron function will be executed forever, every 60 seconds.
     */
    #[Cron(period: 60.0)]
    public function cron1(): void
    {
        $this->sendMessageToAdmins("The bot is online, current time ".date(DATE_RFC850)."!");
    }

    /**
     * Handle incoming updates from users, chats and channels.
     */
    #[Handler]
    public function handleMessage(Incoming&Message $message): void
    {
        // In this example code, send the "This userbot is powered by MadelineProto!" message only once per chat.
        // Ignore all further messages coming from this chat.
        if (!isset($this->notifiedChats[$message->chatId])) {
            $this->notifiedChats[$message->chatId] = true;

            $message->reply(
                message: "This userbot is powered by [MadelineProto](https://t.me/MadelineProto)!",
                parseMode: ParseMode::MARKDOWN
            );
        }
    }

    /**
     * Reposts a media file as a Telegram story.
     */
    #[FilterCommand('story')]
    public function storyCommand(Message & FromAdmin $message): void
    {
        if ($this->isSelfBot()) {
            $message->reply("Only users can post Telegram Stories!");
            return;
        }
        $media = $message->getReply(Message::class)?->media;
        if (!$media) {
            $message->reply("You should reply to a photo or video to repost it as a story!");
            return;
        }

        $this->stories->sendStory(
            peer: 'me',
            media: $media,
            caption: "This story was posted using [MadelineProto](https://t.me/MadelineProto)!",
            parse_mode: ParseMode::MARKDOWN,
            privacy_rules: [['_' => 'inputPrivacyValueAllowAll']]
        );
    }

    /**
     * Automatically sends a comment to all new incoming channel posts.
     */
    #[Handler]
    public function makeComment(Incoming&ChannelMessage $message): void
    {
        if ($this->isSelfBot()) {
            return;
        }
        $message->getDiscussion()->reply(
            message: "This comment is powered by [MadelineProto](https://t.me/MadelineProto)!",
            parseMode: ParseMode::MARKDOWN
        );
    }

    #[FilterCommand('broadcast')]
    public function broadcastCommand(Message & FromAdmin $message): void
    {
        // We can broadcast messages to all users with /broadcast
        if (!$message->replyToMsgId) {
            $message->reply("You should reply to the message you want to broadcast.");
            return;
        }
        $this->broadcastForwardMessages(
            from_peer: $message->senderId,
            message_ids: [$message->replyToMsgId],
            drop_author: true,
            pin: true,
        );
    }

    private int $lastLog = 0;
    /**
     * Handles updates to an in-progress broadcast.
     */
    #[Handler]
    public function handleBroadcastProgress(Progress $progress): void
    {
        if (time() - $this->lastLog > 5 || $progress->status === Status::FINISHED) {
            $this->lastLog = time();
            $this->sendMessageToAdmins((string) $progress);
        }
    }

    #[FilterCommand('echo')]
    public function echoCmd(Message $message): void
    {
        // Contains the arguments of the command
        $args = $message->commandArgs;

        $message->reply($args[0] ?? '');
    }

    #[FilterRegex('/.*(mt?proto)[^.]?.*/i')]
    public function testRegex(Incoming & Message $message): void
    {
        $message->reply("Did you mean to write MadelineProto instead of ".$message->matches[1].'?');
    }

    #[FilterText('test')]
    public function pingCommand(Message $message): void
    {
        $message->reply('test reply');
    }

    #[FilterCommand('react')]
    public function reactCommand(Message&IsReply $message): void
    {
        $message->getReply(Message::class)->addReaction('ðŸ‘Œ');
    }

    #[FilterCommand('unreact')]
    public function unreactCommand(Message&IsReply $message): void
    {
        $message->getReply(Message::class)->delReaction('ðŸ‘Œ');
    }

    #[FilterTextCaseInsensitive('hi')]
    public function pingCommandCaseInsensitive(Message $message): void
    {
        $message->reply('hello');
    }

    /**
     * Called when the dialog photo of a chat or channel changes.
     */
    #[Handler]
    public function logPhotoChanged(Incoming&DialogPhotoChanged $message): void
    {
        if ($message->photo) {
            $message->reply("Nice! Here's a download link for the photo: ".$message->photo->getDownloadLink());
        } else {
            $message->reply("Hmm, why did you delete the group photo?");
        }
    }

    /**
     * Gets a download link for any file up to 4GB!
     *
     * The bot must be started via web for this command to work.
     *
     * You can also start it via CLI but you'll have to specify a download script URL in the settings: https://docs.madelineproto.xyz/docs/FILES.html#getting-a-download-link-cli-bots.
     */
    #[FilterCommand('dl')]
    public function downloadLink(Incoming&Message $message): void
    {
        $reply = $message->getReply(Message::class);
        if (!$reply?->media) {
            $message->reply("This command must reply to a media message!");
            return;
        }
        $reply->reply("Download link: ".$reply->media->getDownloadLink());
    }

    // Plays a video into a private call or a group call (video chat), joining it.
    // The video is read from a WebM file, demuxed in pure PHP and sent as-is, so neither ffmpeg nor the FFI extension is required.
    //
    // Fully works on webhosts!
    //
    #[FilterCommand('call')]
    public function callVoip(Incoming&Message $message): void
    {
        if ($whyNot = $message->getCallDenialReason()) {
            $message->reply("Can't start a call: ".$whyNot->value);
            return;
        }
        $message->requestCall()->join()->play(new RemoteUrl('https://paste.daniil.it/oncall.webm'));
    }

    // Plays incoming files into a Telegram call
    #[Handler]
    public function play(Incoming&Message $message): void
    {
        if (!$message->media instanceof Document
            || !in_array($message->media->mimeType, ['audio/ogg', 'video/webm'], true)
        ) {
            return;
        }
        if (!$message->canRequestCall()) {
            return;
        }
        $message->requestCall()->join()->play($message->media->getStream());
    }

    #[Handler]
    public function handleIncomingCall(PrivateCall&Incoming $call): void
    {
        $call->join()->play(new RemoteUrl('https://paste.daniil.it/oncall.webm'));
    }

    public static function getPluginPaths(): string|array|null
    {
        return 'plugins/';
    }
}

$settings = new Settings;
$settings->getLogger()->setLevel(Logger::LEVEL_ULTRA_VERBOSE);

// You can also use Redis, MySQL or PostgreSQL.
// Data is migrated automatically.
//
// $settings->setDb((new Redis)->setDatabase(0)->setPassword('pony'));
// $settings->setDb((new Postgres)->setDatabase('MadelineProto')->setUsername('daniil')->setPassword('pony'));
// $settings->setDb((new Mysql)->setDatabase('MadelineProto')->setUsername('daniil')->setPassword('pony'));

// You can also enable collection of additional prometheus metrics.
// $settings->getMetrics()->setEnablePrometheusCollection(true);

// You can also enable collection of additional memory profiling metrics.
// Note: you must also set the MEMPROF_PROFILE=1 environment variable or GET parameter.
// $settings->getMetrics()->setEnableMemprofCollection(true);

// Metrics can be returned by an autoconfigured http://127.0.0.1:12345 HTTP server.
//
// Endpoints:
//
// /metrics - Prometheus metrics
// /debug/pprof - PProf memory profile for pyroscope
//
// $settings->getMetrics()->setMetricsBindTo(fromString("127.0.0.1:12345"));

// Metrics can also be returned by the current script via web, if called with a specific query string:
//
// ?metrics - Prometheus metrics
// ?pprof - PProf memory profile for pyroscope
//
// $settings->getMetrics()->setReturnMetricsFromStartAndLoop(true);

// For users or bots
MyEventHandler::startAndLoop('bot.madeline', $settings);

// For bots only
// MyEventHandler::startAndLoopBot('bot.madeline', 'bot token', $settings);
<?php declare(strict_types=1);

if (file_exists('vendor/autoload.php')) {
    require_once 'vendor/autoload.php';
} else {
    // Otherwise download an !!! alpha !!! version of MadelineProto via madeline.php
    if (!file_exists('madeline.php')) {
        copy('https://phar.madelineproto.xyz/madeline.php', 'madeline.php');
    }
    require_once 'madeline.php';
}

use danog\MadelineProto\Logger;
use danog\MadelineProto\Settings;
use danog\MadelineProto\Settings\Database\Mysql;
use danog\MadelineProto\Settings\Database\Postgres;
use danog\MadelineProto\Settings\Database\Redis;
use danog\MadelineProto\SimpleEventHandler;

/** Base event handler class that only includes plugins */
class BaseHandler extends SimpleEventHandler
{
    public static function getPluginPaths(): array|string|null
    {
        return 'plugins/';
    }
}

$settings = new Settings;
$settings->getLogger()->setLevel(Logger::LEVEL_ULTRA_VERBOSE);

// You can also use Redis, MySQL or PostgreSQL
// $settings->setDb((new Redis)->setDatabase(0)->setPassword('pony'));
// $settings->setDb((new Postgres)->setDatabase('MadelineProto')->setUsername('daniil')->setPassword('pony'));
// $settings->setDb((new Mysql)->setDatabase('MadelineProto')->setUsername('daniil')->setPassword('pony'));

// For users or bots
BaseHandler::startAndLoop('bot.madeline', $settings);

// For bots only
// BaseHandler::startAndLoopBot('bot.madeline', 'bot token', $settings);
#!/usr/bin/env php
<?php declare(strict_types=1);
/**
 * Example combined event handler bot.
 *
 * Copyright 2016-2020 Daniil Gentili
 * (https://daniil.it)
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

use danog\MadelineProto\API;
use danog\MadelineProto\EventHandler\Attributes\Handler;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\SimpleFilter\Incoming;
use danog\MadelineProto\SimpleEventHandler;

/*
 * Various ways to load MadelineProto
 */
if (file_exists('vendor/autoload.php')) {
    include 'vendor/autoload.php';
} else {
    if (!file_exists('madeline.php')) {
        copy('https://phar.madelineproto.xyz/madeline.php', 'madeline.php');
    }
    include 'madeline.php';
}

/**
 * Combined multiaccount event handler class.
 */
class CombinedEventHandler extends SimpleEventHandler
{
    /**
     * @var int|string Username or ID of bot admin
     */
    public const ADMIN = "danogentili"; // Change this
    /**
     * Get peer(s) where to report errors.
     */
    public function getReportPeers()
    {
        // Can also return a different report peer depending on the ID returned by $this->getSelf()...
        return [self::ADMIN];
    }

    public function onStart(): void
    {
    }

    /**
     * Handle incoming updates from users, chats and channels.
     */
    #[Handler]
    public function handleMessage(Incoming&Message $message): void
    {
        // Code that uses $message...
        // See the following pages for more examples and documentation:
        // - https://github.com/danog/MadelineProto/blob/v8/examples/bot.php
        // - https://docs.madelineproto.xyz/docs/UPDATES.html
        // - https://docs.madelineproto.xyz/docs/FILTERS.html
        // - https://docs.madelineproto.xyz/
    }
}

$MadelineProtos = [];
foreach (['session1.madeline', 'session2.madeline', 'session3.madeline'] as $session) {
    $MadelineProtos []= new API($session);
}

API::startAndLoopMulti($MadelineProtos, CombinedEventHandler::class);
<?php declare(strict_types=1);

use danog\MadelineProto\EventHandler\Attributes\Handler;
use danog\MadelineProto\EventHandler\Filter\FilterCommand;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\SimpleFilter\HasAudio;
use danog\MadelineProto\EventHandler\SimpleFilter\HasDocument;
use danog\MadelineProto\EventHandler\SimpleFilter\Incoming;
use danog\MadelineProto\Ogg;
use danog\MadelineProto\ParseMode;
use danog\MadelineProto\SimpleEventHandler;

use function Amp\async;

if (class_exists(API::class)) {
    // Otherwise, if a stable version of MadelineProto was installed via composer, load composer autoloader
} elseif (file_exists('vendor/autoload.php')) {
    require_once 'vendor/autoload.php';
} else {
    // Otherwise download an !!! alpha !!! version of MadelineProto via madeline.php
    if (!file_exists('madeline.php')) {
        copy('https://phar.madelineproto.xyz/madeline.php', 'madeline.php');
    }
    require_once 'madeline.php';
}

class LibtgvoipEventHandler extends SimpleEventHandler
{
    // !!! Change this to your username! !!!
    private const ADMIN = 'danogentili';
    public function getReportPeers(): string
    {
        return self::ADMIN;
    }
    #[FilterCommand('start')]
    public function startCmd(Incoming&Message $message): void
    {
        $message->reply(
            message: "This bot can be used to convert files to be played by a @MadelineProto Telegram webradio!".
            "\n\nSee https://docs.madelineproto.xyz/docs/CALLS.html for more info, and call @magicalcrazypony to hear some nice tunes!".
            "\n\nSend me an audio file to start.".
            "\n\nPowered by @MadelineProto, [source code](https://github.com/danog/MadelineProto/blob/v8/examples/libtgvoipbot.php).",
            parseMode: ParseMode::MARKDOWN
        );
    }
    #[Handler]
    public function convertCmd((Incoming&Message&HasAudio)|(Incoming&Message&HasDocument) $message): void
    {
        $reply = $message->reply("Conversion in progress...");
        async(function () use ($message, $reply): void {
            $pipe = self::getStreamPipe();
            $sink = $pipe->getSink();
            async(
                Ogg::convert(...),
                $message->media->getStream(),
                $sink
            )->finally($sink->close(...));

            $this->sendDocument(
                peer: $message->chatId,
                file: $pipe->getSource(),
                fileName: $message->media->fileName.".ogg",
                replyToMsgId: $message->id
            );
        })->finally($reply->delete(...));
    }
}

if (!getenv('TOKEN')) {
    throw new AssertionError("You must define a TOKEN environment variable with the token of the bot!");
}

LibtgvoipEventHandler::startAndLoopBot($argv[1] ?? 'libtgvoipbot.madeline', getenv('TOKEN'));
<?php declare(strict_types=1);
/**
 * Secret chat bot.
 *
 * Copyright 2016-2020 Daniil Gentili
 * (https://daniil.it)
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

use danog\MadelineProto\EventHandler\Attributes\Handler;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Message\PrivateMessage;
use danog\MadelineProto\EventHandler\Message\SecretMessage;
use danog\MadelineProto\EventHandler\SimpleFilter\Incoming;
use danog\MadelineProto\LocalFile;
use danog\MadelineProto\Logger;
use danog\MadelineProto\Settings;
use danog\MadelineProto\SimpleEventHandler;

/*
 * Various ways to load MadelineProto
 */
if (file_exists(__DIR__.'/../vendor/autoload.php')) {
    include 'vendor/autoload.php';
} else {
    if (!file_exists('madeline.php')) {
        copy('https://phar.madelineproto.xyz/madeline.php', 'madeline.php');
    }
    include 'madeline.php';
}

class SecretHandler extends SimpleEventHandler
{
    private array $sent = [];
    /**
     * @var int|string Username or ID of bot admin
     */
    public const ADMIN = "danogentili"; // Change this
    /**
     * Get peer(s) where to report errors.
     *
     * @return int|string|array
     */
    public function getReportPeers()
    {
        return [self::ADMIN];
    }
    public function onStart(): void
    {
    }
    /**
     * Join the group call of a group or channel, and stream a video file into it.
     *
     * Send /joinGroupCall in a group that has an active video chat. The media is read from a.mkv,
     * a WebM/Matroska file holding VP8 video and OPUS audio: MadelineProto demuxes it in pure PHP
     * and sends the frames as-is, so neither ffmpeg nor the FFI extension is required.
     */
    #[Handler]
    public function hjoinGroupCall(Incoming&Message $update): void
    {
        if ($update->message !== '/joinGroupCall') {
            return;
        }

        $call = $this->getGroupCall($update->chatId);
        if ($call === null) {
            $update->reply('There is no active group call in this chat!');
            return;
        }

        $file = new LocalFile(__DIR__.'/a.mkv');
        if (!file_exists($file->file)) {
            $update->reply('a.mkv is missing, cannot stream anything.');
            return;
        }

        $call->join();
        $call->playVideo($file);
        $update->reply("Joined {$call} and started streaming a.mkv!");
    }

    /**
     * Handle updates from users.
     */
    #[Handler]
    public function handleNormalMessage(Incoming&PrivateMessage $update): void
    {
        if ($update->message === 'request') {
            $this->requestSecretChat($update->senderId);
        }
        if ($update->message === 'ping') {
            $update->reply('pong');
        }
    }
    /**
     * Handle secret chat messages.
     */
    #[Handler]
    public function handle(Incoming&SecretMessage $update): void
    {
        if ($update->media) {
            $path = $update->media->downloadToDir('/tmp');
            $update->reply($path);
        }
        if (isset($this->sent[$update->chatId])) {
            return;
        }

        // Photo, secret chat
        $this->sendPhoto(
            peer: $update->chatId,
            file: new LocalFile('tests/faust.jpg'),
            caption: 'This file was uploaded using MadelineProto',
        );

        // Photo as document, secret chat
        $this->sendDocumentPhoto(
            peer: $update->chatId,
            file: new LocalFile('tests/faust.jpg'),
            caption: 'This file was uploaded using MadelineProto',
        );

        // GIF, secret chat
        $this->sendGif(
            peer: $update->chatId,
            file: new LocalFile('tests/pony.mp4'),
            caption: 'This file was uploaded using MadelineProto',
        );

        // Sticker, secret chat
        $this->sendSticker(
            peer: $update->chatId,
            file: new LocalFile('tests/lel.webp'),
            mimeType: "image/webp"
        );

        // Document, secret chat
        $this->sendDocument(
            peer: $update->chatId,
            file: new LocalFile('tests/60'),
            fileName: 'fairy'
        );

        // Video, secret chat
        $this->sendVideo(
            peer: $update->chatId,
            file: new LocalFile('tests/swing.mp4'),
        );

        // audio, secret chat
        $this->sendAudio(
            peer: $update->chatId,
            file: new LocalFile('tests/mosconi.mp3'),
        );

        $this->sendVoice(
            peer: $update->chatId,
            file: new LocalFile('tests/mosconi.mp3'),
        );

        $i = 0;
        while ($i < 10) {
            $this->logger("SENDING MESSAGE $i TO ".$update->chatId);
            // You can also use the sendEncrypted parameter for more options in secret chats
            $this->sendMessage(peer: $update->chatId, message: (string) ($i++));
        }
        $this->sent[$update->chatId] = true;
    }
}

$settings = new Settings;
$settings->getLogger()->setLevel(Logger::ULTRA_VERBOSE);

SecretHandler::startAndLoop('user.madeline', $settings);
<?php declare(strict_types=1);

/**
 * Telegram stories downloader bot.
 *
 * Copyright 2016-2020 Daniil Gentili
 * (https://daniil.it)
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

use danog\MadelineProto\API;
use danog\MadelineProto\Broadcast\Progress;
use danog\MadelineProto\Broadcast\Status;
use danog\MadelineProto\EventHandler\Filter\FilterCommand;
use danog\MadelineProto\EventHandler\Filter\FilterRegex;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Query\ChatButtonQuery;
use danog\MadelineProto\EventHandler\SimpleFilter\FromAdmin;
use danog\MadelineProto\EventHandler\SimpleFilter\Incoming;
use danog\MadelineProto\ParseMode;
use danog\MadelineProto\RPCErrorException;
use danog\MadelineProto\SimpleEventHandler;

// MadelineProto is already loaded
if (class_exists(API::class)) {
    // Otherwise, if a stable version of MadelineProto was installed via composer, load composer autoloader
} elseif (file_exists('vendor/autoload.php')) {
    require_once 'vendor/autoload.php';
} else {
    // Otherwise download an !!! alpha !!! version of MadelineProto via madeline.php
    if (!file_exists('madeline.php')) {
        copy('https://phar.madelineproto.xyz/madeline.php', 'madeline.php');
    }
    require_once 'madeline.php';
}

// Login as a user
$u = new API('stories_user.madeline');
if (!$u->getSelf()) {
    if (!$_GET) {
        $u->echo("Please login as a user!");
    }
    $u->start();
}
if (!$u->isSelfUser()) {
    throw new AssertionError("You must login as a user! Please delete the user.madeline folder to continue.");
}
unset($u);

final class StoriesEventHandler extends SimpleEventHandler
{
    private const HELP = "Telegram stories downloader bot, powered by @MadelineProto!\n\nUsage:\n/dlStories @danogentili - Download all the stories of a @username!\n\n[Source code](https://github.com/danog/MadelineProto/blob/v8/examples/tgstories_dl_bot.php) powered by @MadelineProto";
    // Username of the admin of the bot
    private const ADMIN = "@danogentili";

    private const PAGE_SIZE = 10;

    private API $userInstance;
    public function onStart(): void
    {
        // Login as a user
        $this->userInstance = new API('stories_user.madeline');
    }

    public function getReportPeers()
    {
        return self::ADMIN;
    }

    private int $lastLog = 0;
    /**
     * Handles updates to an in-progress broadcast.
     */
    public function onUpdateBroadcastProgress(Progress $progress): void
    {
        if (time() - $this->lastLog > 5 || $progress->status === Status::FINISHED) {
            $this->lastLog = time();
            $this->sendMessageToAdmins((string) $progress);
        }
    }

    #[FilterCommand('broadcast')]
    public function broadcastCommand(Message & FromAdmin $message): void
    {
        // We can broadcast messages to all users with /broadcast
        if (!$message->replyToMsgId) {
            $message->reply("You should reply to the message you want to broadcast.");
            return;
        }
        $this->broadcastForwardMessages(
            from_peer: $message->senderId,
            message_ids: [$message->replyToMsgId],
            drop_author: true,
            pin: true,
        );
    }

    #[FilterCommand('start')]
    public function startCmd(Incoming&Message $message): void
    {
        $message->reply(self::HELP, parseMode: ParseMode::MARKDOWN);
    }

    /**
     * Lists the telegram stories of a user, a page at a time, with download links (except for protected ones).
     *
     * The bot must be started via web for this command to work.
     *
     * You can also start it via CLI but you'll have to specify a download script URL in the settings: https://docs.madelineproto.xyz/docs/FILES.html#getting-a-download-link-cli-bots.
     */
    #[FilterCommand('dlStories')]
    public function dlStoriesCommand(Message $message): void
    {
        if (!$message->commandArgs) {
            $message->reply("You must specify the @username or the Telegram ID of a user to download their stories!");
            return;
        }
        $peer = $this->userInstance->getId($message->commandArgs[0]);

        // The active stories that aren't posted to the profile, which stories.getPinnedStories doesn't return
        $active = $this->userInstance->stories->getPeerStories(peer: $peer)['stories']['stories'];
        // Active stories may be returned as storyItemSkipped, fetch them in full
        $skipped = array_column(array_filter($active, static fn (array $s): bool => $s['_'] === 'storyItemSkipped'), 'id');
        if ($skipped) {
            $active = array_merge(
                array_filter($active, static fn (array $s): bool => $s['_'] !== 'storyItemSkipped'),
                $this->userInstance->stories->getStoriesByID(peer: $peer, id: $skipped)['stories'],
            );
        }
        $active = array_filter($active, static fn (array $s): bool => $s['_'] === 'storyItem' && !$s['pinned']);
        usort($active, static fn (array $a, array $b): int => $b['id'] <=> $a['id']);

        [$text, $markup] = $this->renderStoriesPage($peer, 0, $active);
        $message->reply($text, parseMode: ParseMode::MARKDOWN, replyMarkup: $markup, noWebpage: true);
    }

    /**
     * Loads the next page of the stories of a user, when its button is pressed.
     */
    #[FilterRegex('/^stories:(-?\d+):(\d+)$/')]
    public function storiesPageQuery(ChatButtonQuery $query): void
    {
        // Remove the button first, so that the page is loaded once even if it's pressed again
        try {
            // Without reply_markup: Telegram rejects an empty inline keyboard (REPLY_MARKUP_INVALID)
            $this->messages->editMessage(peer: $query->chatId, id: $query->messageId);
        } catch (RPCErrorException $e) {
            if ($e->rpc !== 'MESSAGE_NOT_MODIFIED') {
                throw $e;
            }
            // Already pressed
            $query->answer(cacheTime: 0);
            return;
        }
        $query->answer(cacheTime: 0);

        $peer = (int) $query->matches[1];
        $offset = (int) $query->matches[2];
        try {
            [$text, $markup] = $this->renderStoriesPage($peer, $offset, []);
        } catch (Throwable $e) {
            // Give the button back, to retry
            $query->editReplyMarkup(self::nextPageMarkup($peer, $offset));
            throw $e;
        }
        $this->sendMessage(peer: $query->chatId, message: $text, parseMode: ParseMode::MARKDOWN, replyMarkup: $markup, noWebpage: true);
    }

    /**
     * Renders a page of the profile stories of a peer (newest first, the ones pinned to the top on the first page),
     * fetching only that page, after some other stories to list first.
     *
     * Protected stories are listed without a link.
     *
     * @param int  $offset ID of the last story of the previous page, 0 for the first page
     * @param list<array> $before Stories to list before the page
     *
     * @return array{string, ?array}
     */
    private function renderStoriesPage(int $peer, int $offset, array $before): array
    {
        $res = $this->userInstance->stories->getPinnedStories(peer: $peer, offset_id: $offset, limit: self::PAGE_SIZE);
        $pinnedToTop = $res['pinned_to_top'] ?? [];
        $stories = $before;
        $next = 0;
        foreach ($res['stories'] as $story) {
            if (in_array($story['id'], $pinnedToTop, true)) {
                // Listed first on the first page whatever their ID, they can't be used as offset
                if ($offset !== 0) {
                    continue;
                }
            } else {
                $next = $next === 0 ? $story['id'] : min($next, $story['id']);
            }
            // Skip deleted stories
            if ($story['_'] === 'storyItem') {
                $stories[] = $story;
            }
        }

        $result = '';
        foreach ($stories as $story) {
            $cur = "- ID {$story['id']}, posted ".date(DATE_RFC850, $story['date']);
            if (isset($story['caption']) && $story['caption'] !== '') {
                $caption = mb_strlen($story['caption']) > 100 ? mb_substr($story['caption'], 0, 100).'â€¦' : $story['caption'];
                $cur .= ', "'.self::markdownEscape($caption).'"';
            }
            $result .= $story['noforwards']
                ? "$cur; protected, can't be downloaded\n"
                : "$cur; [click here to download Â»]({$this->userInstance->getDownloadLink($story)})\n";
        }
        if ($offset === 0) {
            $result = $result === ''
                ? "This user has no stories!"
                : "Stories ({$res['count']} in the profile):\n\n$result";
        } elseif ($result === '') {
            $result = "No more stories!";
        }

        // A full page: there may be more
        $more = $next !== 0 && count($res['stories']) >= self::PAGE_SIZE;
        return [$result, $more ? self::nextPageMarkup($peer, $next) : null];
    }

    private static function nextPageMarkup(int $peer, int $offset): array
    {
        // Bot API style: MadelineProto converts it to the constructors of the current layer
        return ['inline_keyboard' => [[['text' => 'Next page Â»', 'callback_data' => "stories:$peer:$offset"]]]];
    }
}

$token = '<token>';

StoriesEventHandler::startAndLoopBot('stories.madeline', $token);
<?php declare(strict_types=1);

// Simple example bot.
// PHP 8.2.4+ is required.

// Run via CLI (recommended: `screen php bot.php`) or via web.

// To reduce RAM usage, follow these instructions: https://docs.madelineproto.xyz/docs/DATABASE.html

use danog\MadelineProto\EventHandler\Attributes\Handler;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Plugin\RestartPlugin;
use danog\MadelineProto\EventHandler\SimpleFilter\Incoming;
use danog\MadelineProto\SimpleEventHandler;

// Load via composer (RECOMMENDED, see https://docs.madelineproto.xyz/docs/INSTALLATION.html#composer-from-scratch)
if (file_exists('vendor/autoload.php')) {
    require_once 'vendor/autoload.php';
} else {
    // Otherwise download an !!! alpha !!! version of MadelineProto via madeline.php
    if (!file_exists('madeline.php')) {
        copy('https://phar.madelineproto.xyz/madeline.php', 'madeline.php');
    }
    require_once 'madeline.php';
}

class BasicEventHandler extends SimpleEventHandler
{
    // !!! Change this to your username !!!
    public const ADMIN = "@me";

    /**
     * Get peer(s) where to report errors.
     */
    public function getReportPeers()
    {
        return [self::ADMIN];
    }

    /**
     * Returns a set of plugins to activate.
     *
     * See here for more info on plugins: https://docs.madelineproto.xyz/docs/PLUGINS.html
     */
    public static function getPlugins(): array
    {
        return [
            // Offers a /restart command to admins that can be used to restart the bot, applying changes.
            // Make sure to run in a bash while loop when running via CLI to allow self-restarts.
            RestartPlugin::class,
        ];
    }

    /**
     * Handle incoming updates from users, chats and channels.
     */
    #[Handler]
    public function handleMessage(Incoming&Message $message): void
    {
        // Code that uses $message...
        // See the following pages for more examples and documentation:
        // - https://github.com/danog/MadelineProto/blob/v8/examples/bot.php
        // - https://docs.madelineproto.xyz/docs/UPDATES.html
        // - https://docs.madelineproto.xyz/docs/FILTERS.html
        // - https://docs.madelineproto.xyz/
    }
}

BasicEventHandler::startAndLoop('bot.madeline');
<?php

$config = new class extends Amp\CodeStyle\Config {
    public function getRules(): array
    {
        return array_merge(parent::getRules(), [
            'void_return' => true,
            'array_indentation' => true,
            'ternary_to_null_coalescing' => true,
            'assign_null_coalescing_to_coalesce_equal' => true,
            '@PHP82Migration' => true,
            '@PHP81Migration' => true,
            '@PHP80Migration' => true,
            '@PHP80Migration:risky' => true,
            'static_lambda' => true,
            'strict_param' => true,
            "native_function_invocation" => ['include' => ['@compiler_optimized'], 'scope' => 'namespaced'],
        ]);
    }
};

$config->getFinder()
    ->in(__DIR__ . '/src')
    ->in(__DIR__ . '/tests')
    ->in(__DIR__ . '/examples')
    ->in(__DIR__ . '/tools')
    ->exclude('Namespace')
    ->notName('TLParser.php')
    ->notName('Messages.php')
    ->notName('SecretTLParser.php');

$cacheDir = getenv('TRAVIS') ? getenv('HOME') . '/.php-cs-fixer' : __DIR__;

$config->setCacheFile($cacheDir . '/.php_cs.cache');

return $config;
{
    "liteservers": [
      {
        "ip": 1137658550,
        "port": 4924,
        "id": {
          "@type": "pub.ed25519",
          "key": "peJTw/arlRfssgTuf9BMypJzqOi7SXEqSPSWiEw2U1M="
        }
      }
    ],
    "validator": {
      "@type": "validator.config.global",
      "zero_state": {
        "workchain": -1,
        "shard": -9223372036854775808,
        "seqno": 0,
        "root_hash": "F6OpKZKqvqeFp6CQmFomXNMfMj2EnaUSOXN+Mh+wVWk=",
        "file_hash": "XplPz01CXAps5qeSWUtxcyBfdAo5zVb1N979KLSKD24="
      }
    }
  }
  <?php

use danog\MadelineProto\Settings\Logger as SettingsLogger;
use danog\MadelineProto\TON\API;

require 'vendor/autoload.php';

$API = new API(new SettingsLogger);

$API->async(true);
$API->loop(
    function () use ($API) {
        yield $API->connect(__DIR__.'/ton-lite-client-test1.config.json');
        $API->logger(yield $API->liteServer->getTime());
    }
);
{
  "liteservers": [
     {
      "ip": 861606190,
      "port": 9999,
       "id": {
         "@type": "pub.ed25519",
        "key": "T10NJq2tgx6LpTHj734fSNYJ6S2w1hTdFRXJaj5st80"
       }
     }
  ],
  "validator": {
    "@type": "validator.config.global",
    "zero_state": {
      "workchain": -1,
      "shard": -9223372036854775808,
      "seqno": 0,
      "root_hash": "F6OpKZKqvqeFp6CQmFomXNMfMj2EnaUSOXN+Mh+wVWk=",
      "file_hash": "XplPz01CXAps5qeSWUtxcyBfdAo5zVb1N979KLSKD24="
    }
  }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

/**
 * What happened to a recording, as reported by a {@see EventHandler\Calls\CallStreams} update.
 */
enum RecordingEvent: string
{
    /** A recording file (or stream) was opened and its header written: media is being recorded into it from now on. */
    case Started = 'started';
    /** A recording file (or stream) was finished: nothing more is written to it. */
    case Ended = 'ended';
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use Amp\Http\Client\HttpClientBuilder;
use Amp\Http\Client\Request;
use Amp\SignalException;
use AssertionError;
use danog\BetterPrometheus\BetterCollectorRegistry;
use danog\BetterPrometheus\BetterGauge;
use Prometheus\Storage\InMemory;
use ReflectionFiber;
use Revolt\EventLoop;
use Throwable;
use WeakMap;

use const LOCK_EX;
use const LOCK_NB;

use function Amp\ByteStream\pipe;
use function Amp\File\exists;
use function Amp\File\move;
use function Amp\File\openFile;
use function Amp\File\read;
use function Amp\File\write;

/**
 * @internal
 *
 * @psalm-suppress UndefinedConstant
 */
final class GarbageCollector
{
    /**
     * Ensure only one instance of GarbageCollector exists
     * when multiple instances of MadelineProto are running.
     */
    private static bool $started = false;

    /**
     * Next cleanup will be triggered when memory consumption will increase by this amount.
     */
    public static int $memoryDiffMb = 1;

    /**
     * Memory consumption after last cleanup.
     */
    private static int $memoryConsumption = 0;

    public static BetterCollectorRegistry $prometheus;
    private static BetterGauge $alloc;
    private static BetterGauge $inuse;

    public static function start(): void
    {
        if (self::$started) {
            return;
        }
        self::$started = true;

        self::$prometheus = new BetterCollectorRegistry(new InMemory, false);

        self::$alloc = self::$prometheus->registerGauge("MadelineProto", "php_memstats_alloc_bytes", "RAM allocated by the PHP memory pool");
        self::$inuse = self::$prometheus->registerGauge("MadelineProto", "php_memstats_inuse_bytes", "RAM actually used by PHP");

        $counter = self::$prometheus->registerCounter("MadelineProto", "explicit_gc_count", "Number of times the GC was explicitly invoked");
        $counter->incBy(0);
        EventLoop::unreference(EventLoop::repeat(1, static function () use ($counter): void {
            $currentMemory = self::getMemoryConsumption();
            if ($currentMemory > self::$memoryConsumption + self::$memoryDiffMb) {
                $counter->inc();
                gc_collect_cycles();
                self::$memoryConsumption = self::getMemoryConsumption();
                /*self::$memoryConsumption = self::getMemoryConsumption();
                $cleanedMemory = $currentMemory - self::$memoryConsumption;
                if (!Magic::$suspendPeriodicLogging) {
                    //Logger::log("gc_collect_cycles done. Cleaned memory: $cleanedMemory Mb", Logger::VERBOSE);
                }*/
            }
        }));

        if (!\defined('MADELINE_RELEASE_URL') || \defined('MADELINEPROTO_TEST')) {
            return;
        }
        $client = HttpClientBuilder::buildDefault();

        $id = null;
        $cb = static function () use ($client, &$id): void {
            try {
                $request = new Request(MADELINE_RELEASE_URL);
                $latest = $client->request($request);
                Magic::$latest_release = trim($latest->getBody()->buffer());
                if (API::RELEASE !== Magic::$latest_release) {
                    Magic::$revision .= ' (AN UPDATE IS REQUIRED)';

                    $old = API::RELEASE;
                    $new = Magic::$latest_release;
                    Logger::log("!!!!!!!!!!!!! An update of MadelineProto is required (old=$old, new=$new)! !!!!!!!!!!!!!", Logger::FATAL_ERROR);

                    $contents = $client->request(new Request("https://phar.madelineproto.xyz/phar.php?v=new".random_int(0, PHP_INT_MAX)))
                        ->getBody()
                        ->buffer();

                    if (!str_starts_with($contents, '<?php')) {
                        throw new AssertionError("phar.php is not a PHP file!");
                    }

                    self::writeLoader(MADELINE_PHP, $contents);

                    try {
                        unlink(MADELINE_PHAR_VERSION);
                    } catch (Throwable) {
                    }
                    if (Magic::$isIpcWorker) {
                        throw new SignalException('!!!!!!!!!!!!! An update of MadelineProto is required, shutting down worker! !!!!!!!!!!!!!');
                    }
                    if ($id) {
                        EventLoop::cancel($id);
                    }
                    return;
                }

                // Not MADELINE_PHAR_GLOB: madeline.php defines it as madeline*-81.phar, which matches no phar
                /** @var string */
                foreach (glob(\dirname(MADELINE_PHAR_VERSION).DIRECTORY_SEPARATOR.'madeline-*.phar') as $path) {
                    $base = basename($path);
                    if ($base === 'madeline-'.API::RELEASE.'.phar') {
                        continue;
                    }
                    $f = fopen("$path.lock", 'c');
                    if (flock($f, LOCK_EX|LOCK_NB)) {
                        fclose($f);
                        unlink($path);
                        unlink("$path.lock");
                    } else {
                        fclose($f);
                    }
                }
            } catch (Throwable $e) {
                if ($e instanceof SignalException) {
                    throw $e;
                }
                Logger::log("An error occurred in the phar cleanup loop: $e", Logger::FATAL_ERROR);
            }
        };
        // Not awaited: the startup doesn't wait for the release URL, which can take a while (three connection
        // attempts) to fail when it can't be reached.
        EventLoop::queue($cb);
        EventLoop::unreference($id = EventLoop::repeat(3600.0, $cb));
    }

    /**
     * Brings the phars of an IPC client and of this instance to the newest of the two releases.
     *
     * Short-lived clients (like dl.php, or any script that runs a few IPC calls) exit before completing the
     * update check, and their madeline.php may load phars from another directory than ours, so they'd never
     * update: the newest of the two phars, with its madeline.php, is copied to the directory of the other, and a
     * worker running the older one shuts down to be restarted with the newer one.
     *
     * @param string  $release        Release of the client
     * @param string  $phar           Phar of the client
     * @param string  $versionFile    madeline.version file of the madeline.php of the client
     * @param string  $loader         madeline.php of the client
     * @param ?string $ownPhar        Our phar (MADELINE_PHAR), null if installed with composer
     * @param ?string $ownVersionFile Our madeline.version file (MADELINE_PHAR_VERSION), null if not loaded by madeline.php
     * @param ?string $ownLoader      Our madeline.php (MADELINE_PHP), null if not loaded by madeline.php
     */
    public static function syncIpcClientPhar(string $release, string $phar, string $versionFile, string $loader, ?string $ownPhar, ?string $ownVersionFile, ?string $ownLoader): void
    {
        $cmp = version_compare($release, API::RELEASE);
        if ($cmp < 0) {
            if ($ownPhar === null) {
                return;
            }
            Logger::log("Updating the MadelineProto phar of an IPC client in ".\dirname($versionFile)." from $release to ".API::RELEASE);
            if (self::installPhar($ownPhar, API::RELEASE, $versionFile) && $ownLoader !== null) {
                self::writeLoader($loader, read($ownLoader));
            }
        } elseif ($cmp > 0) {
            if ($ownVersionFile === null) {
                Logger::log("An IPC client uses a newer MadelineProto ($release) than this instance (".API::RELEASE."), please update it!", Logger::FATAL_ERROR);
                return;
            }
            Logger::log("!!!!!!!!!!!!! An IPC client uses a newer MadelineProto, updating (old=".API::RELEASE.", new=$release)! !!!!!!!!!!!!!", Logger::FATAL_ERROR);
            if (self::installPhar($phar, $release, $ownVersionFile) && $ownLoader !== null) {
                self::writeLoader($ownLoader, read($loader));
            }
            if (Magic::$isIpcWorker) {
                throw new SignalException('!!!!!!!!!!!!! An update of MadelineProto is required, shutting down worker! !!!!!!!!!!!!!');
            }
        }
    }

    /**
     * Replaces a madeline.php loader, unless it has the same contents.
     */
    private static function writeLoader(string $path, string $contents): void
    {
        if (!str_starts_with($contents, '<?php')) {
            throw new AssertionError("The new madeline.php is not a PHP file!");
        }
        if ($contents === read($path)) {
            return;
        }
        $unlock = Tools::flock("$path.lock", LOCK_EX);
        try {
            write("$path.temp.php", $contents);
            move("$path.temp.php", $path);
            // With opcache.validate_timestamps=0 (php-fpm), the old loader would run until a restart
            if (\function_exists('opcache_invalidate')) {
                try {
                    opcache_invalidate($path, true);
                } catch (Throwable) {
                    // Restricted by opcache.restrict_api
                }
            }
        } finally {
            $unlock();
        }
    }

    /**
     * Copies a phar next to a madeline.version file and points it to the phar, unless it points to a newer one.
     *
     * @return bool Whether madeline.version was changed to point to the phar
     */
    private static function installPhar(string $source, string $release, string $versionFile): bool
    {
        $target = \dirname($versionFile).DIRECTORY_SEPARATOR."madeline-$release.phar";
        if (!exists($target)) {
            $in = openFile($source, 'r');
            $out = openFile("$target.temp", 'w');
            try {
                pipe($in, $out);
            } finally {
                $in->close();
                $out->close();
            }
            move("$target.temp", $target);
        }
        $current = exists($versionFile) ? trim(read($versionFile)) : '';
        if ($current === '' || version_compare($release, $current) > 0) {
            write("$versionFile.temp", $release);
            move("$versionFile.temp", $versionFile);
            return true;
        }
        return false;
    }

    /** @var \WeakMap<\Fiber, true> */
    public static WeakMap $map;
    public static function registerFiber(\Fiber $fiber): \Fiber
    {
        self::$map ??= new WeakMap;
        self::$map[$fiber] = true;
        return $fiber;
    }
    private static function getMemoryConsumption(): int
    {
        //self::$map ??= new WeakMap;
        self::$alloc->set(memory_get_usage(true));
        $inuse = memory_get_usage();
        self::$inuse->set($inuse);
        $memory = round($inuse/1024/1024, 1);
        /*if (!Magic::$suspendPeriodicLogging) {
            Logger::log("Memory consumption: $memory Mb", Logger::ULTRA_VERBOSE);
        }*/
        /*if (!Magic::$suspendPeriodicLogging) {
            $k = 0;
            foreach (self::$map as $fiber => $_) {
                if ($k++ === 0) {
                    continue;
                }
                if ($fiber->isTerminated()) {
                    continue;
                }
                if (!$fiber->isStarted()) {
                    continue;
                }
                $reflection = new ReflectionFiber($fiber);

                $tlTrace = '';
                foreach ($reflection->getTrace() as $k => $frame) {
                    $tlTrace .= isset($frame['file']) ? \str_pad(\basename($frame['file']).'('.$frame['line'].'):', 20)."\t" : '';
                    $tlTrace .= isset($frame['function']) ? $frame['function'].'(' : '';
                    $tlTrace .= isset($frame['args']) ? \substr(\json_encode($frame['args']) ?: '', 1, -1) : '';
                    $tlTrace .= ')';
                    $tlTrace .= "\n";
                }
                \var_dump($tlTrace);
            }
            $fibers = self::$map->count();
            $maps = '~'.\substr_count(\file_get_contents('/proc/self/maps'), "\n");
            Logger::log("Running fibers: $fibers, maps: $maps", Logger::ULTRA_VERBOSE);
        }*/
        return (int) $memory;
    }
}
<?php

declare(strict_types=1);

/**
 * Connection module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use Amp\ByteStream\ClosedException;
use Amp\ByteStream\ReadableBuffer;
use Amp\ByteStream\ReadableStream;
use Amp\Sync\LocalMutex;
use AssertionError;
use danog\DialogId\DialogId;
use danog\Loop\GenericLoop;
use danog\MadelineProto\Loop\Connection\CleanupLoop;
use danog\MadelineProto\Loop\Connection\PingLoop;
use danog\MadelineProto\Loop\Connection\ReadLoop;
use danog\MadelineProto\Loop\Connection\WriteLoop;
use danog\MadelineProto\MTProto\ConnectionState;
use danog\MadelineProto\MTProto\MTProtoIncomingMessage;
use danog\MadelineProto\MTProto\MTProtoOutgoingMessage;
use danog\MadelineProto\MTProto\SpecialMethodType;
use danog\MadelineProto\MTProtoSession\Session;
use danog\MadelineProto\Reactive\Publisher;
use danog\MadelineProto\Stream\BufferedStreamInterface;
use danog\MadelineProto\Stream\ConnectionContext;
use danog\MadelineProto\Stream\MTProtoBufferInterface;
use danog\MadelineProto\TL\Exception as TLException;
use Revolt\EventLoop;
use Webmozart\Assert\Assert;

use function Amp\ByteStream\buffer;

/**
 * Connection class.
 *
 * Manages connection to Telegram datacenters
 *
 * @psalm-suppress RedundantPropertyInitializationCheck
 *
 * @internal
 * @author Daniil Gentili <daniil@daniil.it>
 */
final class Connection
{
    use Session;
    /**
     * Writer loop.
     *
     */
    protected ?WriteLoop $writer = null;
    /**
     * Handler loop.
     *
     */
    protected ?GenericLoop $handler = null;
    /**
     * Reader loop.
     *
     */
    protected ?ReadLoop $reader = null;
    /**
     * Ping loop.
     *
     */
    public ?PingLoop $pinger = null;
    /**
     * Cleanup loop.
     *
     */
    protected ?CleanupLoop $cleanup = null;
    /**
     * The actual socket.
     * @var (MTProtoBufferInterface&BufferedStreamInterface)|null
     */
    public MTProtoBufferInterface|null $stream = null;
    /**
     * Connection context.
     */
    private ?ConnectionContext $chosenCtx = null;
    /**
     * HTTP request count.
     *
     */
    private int $httpReqCount = 0;
    /**
     * HTTP response count.
     *
     */
    private int $httpResCount = 0;
    /**
     * Whether we're currently reading an MTProto packet.
     */
    private bool $reading = false;
    /**
     * Whether we're currently writing an MTProto packet.
     */
    private bool $writing = false;
    /**
     * Main instance.
     *
     */
    public MTProto $API;
    /**
     * Shared connection instance.
     *
     */
    protected DataCenterConnection $shared;
    /**
     * DC ID.
     *
     */
    protected int $datacenter;
    /**
     * Connection ID.
     *
     */
    private int $id = 0;
    /**
     * DC ID and connection ID concatenated.
     */
    private string $datacenterId = '';
    /**
     * Whether this socket has to be reconnected.
     *
     */
    private bool $needsReconnect = false;
    /**
     * Indicate if this socket needs to be reconnected.
     *
     * @param boolean $needsReconnect Whether the socket has to be reconnected
     *
     * @psalm-external-mutation-free
     */
    public function needReconnect(bool $needsReconnect): void
    {
        $this->needsReconnect = $needsReconnect;
    }
    /**
     * Whether this sockets needs to be reconnected.
     */
    public function shouldReconnect(): bool
    {
        return $this->needsReconnect;
    }
    /**
     * Set writing boolean.
     *
     * @psalm-external-mutation-free
     */
    public function writing(bool $writing): void
    {
        $this->writing = $writing;
        $this->shared->writing($writing, $this->id);
    }
    /**
     * Set reading boolean.
     *
     * @psalm-external-mutation-free
     */
    public function reading(bool $reading): void
    {
        $this->reading = $reading;
        $this->shared->reading($reading, $this->id);
    }
    /**
     * Whether we're currently reading an MTProto packet.
     */
    public function isReading(): bool
    {
        return $this->reading;
    }
    /**
     * Whether we're currently writing an MTProto packet.
     */
    public function isWriting(): bool
    {
        return $this->writing;
    }
    /**
     * Indicate a received HTTP response.
     *
     * @psalm-external-mutation-free
     */
    public function httpReceived(): void
    {
        $this->httpResCount++;
    }
    /**
     * Count received HTTP responses.
     */
    public function countHttpReceived(): int
    {
        return $this->httpResCount;
    }
    /**
     * Indicate a sent HTTP request.
     *
     * @psalm-external-mutation-free
     */
    public function httpSent(): void
    {
        $this->httpReqCount++;
    }
    /**
     * Count sent HTTP requests.
     */
    public function countHttpSent(): int
    {
        return $this->httpReqCount;
    }
    /**
     * Get connection ID.
     */
    public function getID(): int
    {
        return $this->id;
    }
    /**
     * Get datacenter concatenated with connection ID.
     */
    public function getDatacenterID(): string
    {
        return $this->datacenterId;
    }
    public function getDatacenter(): int
    {
        return $this->datacenter;
    }
    /**
     * Get connection context.
     */
    public function getInputClientProxy(): ?array
    {
        if ($this->chosenCtx === null) {
            $this->connect();
        }
        Assert::notNull($this->chosenCtx);
        return $this->chosenCtx->getInputClientProxy();
    }
    /**
     * Check if is an HTTP connection.
     *
     * @psalm-mutation-free
     */
    public function isHttp(): bool
    {
        return $this->chosenCtx->isHttp();
    }
    /**
     * @return Publisher<ConnectionState>
     *
     * @psalm-mutation-free
     */
    public function getState(): Publisher
    {
        return $this->shared->auth->connectionState;
    }

    private ?LocalMutex $connectMutex = null;
    /**
     * Connects to a telegram DC using the specified protocol, proxy and connection parameters.
     */
    private function connect(): self
    {
        if ($this->stream) {
            return $this;
        }
        $this->connectMutex ??= new LocalMutex;
        $lock = $this->connectMutex->acquire();
        try {
            if ($this->stream) {
                return $this;
            }
            $this->createSession();
            foreach ($this->shared->getCtxs() as $ctx) {
                $this->API->logger("Connecting to DC {$this->datacenterId} via $ctx ", Logger::WARNING);
                try {
                    $this->stream = $ctx->getStream();
                } catch (\Throwable $e) {
                    $this->API->logger("$e while connecting to DC {$this->datacenterId} via $ctx, trying next...", Logger::ERROR);
                    continue;
                }
                $this->API->logger("Connected to DC {$this->datacenterId} via $ctx!", Logger::WARNING);
                $this->chosenCtx = $ctx;

                if ($ctx->getIpv6()) {
                    Magic::setIpv6(true);
                }
                if ($this->needsReconnect) {
                    $this->needsReconnect = false;
                }
                $this->httpReqCount = 0;
                $this->httpResCount = 0;
                $this->writer ??= new WriteLoop($this);
                $this->reader ??= new ReadLoop($this);
                $this->cleanup ??= new CleanupLoop($this);
                $this->handler ??= new GenericLoop(function (): void {
                    $this->handleMessages($this->new_incoming);
                    if ($this->ack_queue) {
                        $this->flush(true); // Flush acks
                    }
                }, "Handler loop");
                if (!isset($this->pinger) && !$this->shared->auth->isMedia && !$this->shared->auth->isCdn && !$this->isHttp()) {
                    $this->pinger = new PingLoop($this);
                }
                foreach ($this->unencrypted_new_outgoing as $message) {
                    $message->reply(static fn () => new Exception('Restart because we were reconnected'));
                }
                Assert::true($this->writer->start(), "Could not start writer stream");
                Assert::true($this->reader->start(), "Could not start reader stream");
                Assert::true($this->cleanup->start(), "Could not start cleanup stream");
                $this->handler->start();

                return $this;
            }
            throw new AssertionError("Could not connect to DC {$this->datacenterId}!");
        } finally {
            EventLoop::queue($lock->release(...));
        }
    }
    public function wakeupHandler(MTProtoIncomingMessage $message): void
    {
        $this->new_incoming->enqueue($message);
        \assert($this->handler !== null);
        Assert::true($this->handler->resume() || $this->handler->isRunning(), "Could not resume handler!");
    }
    /**
     * Apply method abstractions.
     *
     * @param string $method    Method name
     * @param array  $arguments Arguments
     */
    private function methodAbstractions(string &$method, array &$arguments): void
    {
        if ($method === 'messages.sendPaidReaction') {
            $arguments['random_id'] = (time() << 32) | random_int(0, 0xFF_FF_FF_FF);
        } elseif ($method === 'messages.importChatInvite' && isset($arguments['hash']) && \is_string($arguments['hash']) && $r = Tools::parseLink($arguments['hash'])) {
            [$invite, $content] = $r;
            if ($invite) {
                $arguments['hash'] = $content;
            } else {
                $method = 'channels.joinChannel';
                $arguments['channel'] = $content;
            }
        } elseif ($method === 'messages.checkChatInvite' && isset($arguments['hash']) && \is_string($arguments['hash']) && $r = Tools::parseLink($arguments['hash'])) {
            [$invite, $content] = $r;
            if (!$invite) {
                throw new TL\Exception('This is not an invite link!');
            }
            $arguments['hash'] = $content;
        } elseif ($method === 'channels.joinChannel' && isset($arguments['channel']) && \is_string($arguments['channel']) && $r = Tools::parseLink($arguments['channel'])) {
            [$invite, $content] = $r;
            if ($invite) {
                $method = 'messages.importChatInvite';
                $arguments['hash'] = $content;
            } else {
                $arguments['channel'] = $content;
            }
        } elseif ($method === 'messages.sendMessage' &&
            (
                (isset($arguments['peer']['_']) && \in_array($arguments['peer']['_'], ['inputEncryptedChat', 'updateEncryption', 'updateEncryptedChatTyping', 'updateEncryptedMessagesRead', 'updateNewEncryptedMessage', 'encryptedMessage', 'encryptedMessageService'], true))
                || (isset($arguments['peer']) && \is_int($arguments['peer']) && DialogId::isSecretChat($arguments['peer']))
            )
        ) {
            $method = 'messages.sendEncrypted';
            $arguments = ['peer' => $arguments['peer'], 'message' => $arguments];
            if (!isset($arguments['message']['_'])) {
                $arguments['message']['_'] = 'decryptedMessage';
            }
            if (!isset($arguments['message']['ttl'])) {
                $arguments['message']['ttl'] = 0;
            }
            if (isset($arguments['message']['reply_to_msg_id'])) {
                throw new Exception("reply_to_msg_id is deprecated, please use reply_to or the new sendMessage/sendVideo/etc... method instead!");
            } elseif (isset($arguments['message']['reply_to']['reply_to_msg_id'])) {
                $arguments['message']['reply_to_random_id'] = $arguments['message']['reply_to']['reply_to_msg_id'];
            }
        } elseif ($method === 'payments.exportInvoice') {
            if (isset($arguments['invoice_media']) && \is_array($arguments['invoice_media']) && isset($arguments['invoice_media']['_'])) {
                $this->API->processMedia($arguments['invoice_media'], $arguments['cancellation'] ?? null);
                if ($arguments['invoice_media']['_'] === 'inputMediaUploadedPhoto'
                    && (
                        $arguments['invoice_media']['file'] instanceof ReadableStream
                        || (
                            $arguments['invoice_media']['file'] instanceof FileCallbackInterface
                            && $arguments['invoice_media']['file']->getFile() instanceof ReadableStream
                        )
                    )
                ) {
                    if ($arguments['invoice_media']['file'] instanceof FileCallbackInterface) {
                        $arguments['invoice_media']['file'] = new FileCallback(
                            new ReadableBuffer(buffer($arguments['invoice_media']['file']->getFile(), $arguments['cancellation'] ?? null)),
                            $arguments['invoice_media']['file']
                        );
                    } else {
                        $arguments['invoice_media']['file'] = new ReadableBuffer(buffer($arguments['invoice_media']['file'], $arguments['cancellation'] ?? null));
                    }
                }
                $this->API->processMedia($arguments['invoice_media'], $arguments['cancellation'] ?? null, true);
            }
        } elseif ($method === 'messages.uploadMedia'
            || $method === 'messages.sendMedia'
            || $method === 'messages.editMessage'
            || $method === 'messages.editInlineBotMessage'
            || $method === 'messages.uploadImportedMedia'
            || $method === 'stories.sendStory'
            || $method === 'stories.editStory'
        ) {
            if ($method === 'messages.uploadMedia') {
                if (!isset($arguments['peer']) && $this->API->getSelf() && !$this->API->isSelfBot()) {
                    $arguments['peer'] = 'me';
                }
            }
            if (isset($arguments['media']) && \is_array($arguments['media']) && isset($arguments['media']['_'])) {
                $this->API->processMedia($arguments['media'], $arguments['cancellation'] ?? null);
                if ($arguments['media']['_'] === 'inputMediaUploadedPhoto'
                    && (
                        $arguments['media']['file'] instanceof ReadableStream
                        || (
                            $arguments['media']['file'] instanceof FileCallbackInterface
                            && $arguments['media']['file']->getFile() instanceof ReadableStream
                        )
                    )
                ) {
                    if ($arguments['media']['file'] instanceof FileCallbackInterface) {
                        $arguments['media']['file'] = new FileCallback(
                            new ReadableBuffer(buffer($arguments['media']['file']->getFile(), $arguments['cancellation'] ?? null)),
                            $arguments['media']['file']
                        );
                    } else {
                        $arguments['media']['file'] = new ReadableBuffer(buffer($arguments['media']['file'], $arguments['cancellation'] ?? null));
                    }
                }
                $this->API->processMedia($arguments['media'], $arguments['cancellation'] ?? null, true);
            }
        } elseif ($method === 'messages.sendMultiMedia') {
            if (isset($arguments['multi_media'])) {
                foreach ($arguments['multi_media'] as &$singleMedia) {
                    if (\is_string($singleMedia['media'])
                        || $singleMedia['media']['_'] === 'inputMediaUploadedPhoto'
                        || $singleMedia['media']['_'] === 'inputMediaUploadedDocument'
                        || $singleMedia['media']['_'] === 'inputMediaPhotoExternal'
                        || $singleMedia['media']['_'] === 'inputMediaDocumentExternal'
                    ) {
                        $singleMedia['media'] = $this->methodCallAsyncRead('messages.uploadMedia', ['peer' => $arguments['peer'], 'media' => $singleMedia['media'], 'cancellation' => $arguments['cancellation'] ?? null]);
                    }
                }
            }
        } elseif ($method === 'messages.sendEncryptedFile' || $method === 'messages.uploadEncryptedFile') {
            if (isset($arguments['file'])) {
                if (
                    (
                        !\is_array($arguments['file'])
                        || !(
                            isset($arguments['file']['_'])
                            && $this->API->getTL()->getConstructors()->findByPredicate($arguments['file']['_'])['type'] === 'InputEncryptedFile'
                        )
                    )
                    && $this->API->getSettings()->getFiles()->getAllowAutomaticUpload()
                ) {
                    $arguments['file'] = ($this->API->uploadEncrypted($arguments['file'], cancellation: $arguments['cancellation'] ?? null));
                }
                if (isset($arguments['file']['key'])) {
                    $arguments['message']['media']['key'] = $arguments['file']['key'];
                }
                if (isset($arguments['file']['iv'])) {
                    $arguments['message']['media']['iv'] = $arguments['file']['iv'];
                }
                if (isset($arguments['file']['size'])) {
                    $arguments['message']['media']['size'] = $arguments['file']['size'];
                }
            }
            return;
        } elseif (\in_array($method, ['messages.addChatUser', 'messages.deleteChatUser', 'messages.editChatAdmin', 'messages.editChatPhoto', 'messages.editChatTitle', 'messages.getFullChat', 'messages.exportChatInvite', 'messages.editChatAdmin', 'messages.migrateChat'], true) && isset($arguments['chat_id']) && (!is_numeric($arguments['chat_id']) || $arguments['chat_id'] < 0)) {
            $res = $this->API->getInfo($arguments['chat_id']);
            if ($res['type'] !== 'chat') {
                throw new Exception('chat_id is not a chat id (only normal groups allowed, not supergroups)!');
            }
            $arguments['chat_id'] = -$res['chat_id'];
        } elseif ($method === 'photos.updateProfilePhoto') {
            if (isset($arguments['id'])) {
                if (!\is_array($arguments['id'])) {
                    $method = 'photos.uploadProfilePhoto';
                    $arguments['file'] = $arguments['id'];
                }
            } elseif (isset($arguments['file'])) {
                $method = 'photos.uploadProfilePhoto';
            }
        } elseif ($method === 'photos.uploadProfilePhoto') {
            if (isset($arguments['file'])) {
                if (\is_array($arguments['file']) && !\in_array($arguments['file']['_'], ['inputFile', 'inputFileBig'], true)) {
                    $method = 'photos.uploadProfilePhoto';
                    $arguments['id'] = $arguments['file'];
                }
            } elseif (isset($arguments['id'])) {
                $method = 'photos.updateProfilePhoto';
            }
        } elseif ($method === 'channels.deleteUserHistory') {
            $method = 'channels.deleteParticipantHistory';
            if (isset($arguments['user_id'])) {
                $arguments['participant'] = $arguments['user_id'];
            }
        } elseif ($method === 'messages.getChatInviteImporters') {
            if (isset($arguments['offset_user'])) {
                if (!isset($arguments['offset_date'])) {
                    throw new TLException(Lang::$current_lang['params_missing'].' offset_user');
                }
            }
        } elseif ($method === 'stories.getAllReadUserStories') {
            $method = 'stories.getAllReadPeerStories';
        } elseif ($method === 'stories.getUserStories') {
            $method = 'stories.getPeerStories';
            if (isset($arguments['user_id'])) {
                $arguments['peer'] = $arguments['user_id'];
            }
        } elseif ($method === 'users.getStoriesMaxIDs') {
            $method = 'stories.getPeerMaxIDs';
        } elseif (\in_array($method, ['channels.createForumTopic', 'channels.editForumTopic', 'channels.deleteTopicHistory', 'channels.getForumTopics', 'channels.getForumTopicsByID', 'channels.updatePinnedForumTopic', 'channels.reorderPinnedForumTopics'], true)) {
            // Moved to messages.* (forums of private chats with bots too), with a peer.
            $method = 'messages.'.substr($method, \strlen('channels.'));
            if (isset($arguments['channel'])) {
                $arguments['peer'] ??= $arguments['channel'];
                unset($arguments['channel']);
            }
        } elseif ($method === 'contacts.toggleStoriesHidden') {
            $method = 'stories.togglePeerStoriesHidden';
            if (isset($arguments['id'])) {
                $arguments['peer'] = $arguments['id'];
            }
        }
        if (isset($arguments['reply_to_msg_id'])) {
            throw new Exception("reply_to_msg_id is deprecated, please use reply_to or the new sendMessage/sendVideo/etc... methods instead!");
        }
    }
    /**
     * Send an MTProto message.
     */
    public function sendMessage(MTProtoOutgoingMessage $message): void
    {
        $message->connection = $this;
        $message->trySend();
        if (!$message->hasSerializedBody()) {
            $body = $message->getBody();
            if ($message->isMethod) {
                $resumableCall = $body['resumableCall'] ?? null;
                $referenceDatabase = $this->API->referenceDatabase;
                if ($referenceDatabase === null) {
                    $body = $this->API->getTL()->serializeMethod($message->constructor, $body);
                } else {
                    [$body, $message->sentFileReferences] = $referenceDatabase->serializeCall(
                        fn () => $this->API->getTL()->serializeMethod($message->constructor, $body),
                    );
                }
                if ($resumableCall !== null) {
                    // Its files were uploaded while serializing it.
                    $this->API->resumableCallSent($resumableCall);
                }
                if ($message->takeoutId !== null) {
                    $body = $this->API->getTL()->serializeMethod(
                        'invokeWithTakeout',
                        ['takeout_id' => $message->takeoutId, 'query' => $body],
                    );
                } elseif ($message->businessConnectionId !== null) {
                    $body = $this->API->getTL()->serializeMethod(
                        'invokeWithBusinessConnection',
                        ['connection_id' => $message->businessConnectionId, 'query' => $body],
                    );
                }
            } else {
                $body['_'] = $message->constructor;
                $body = $this->API->getTL()->serializeObject(['type' => ''], $body, $message->constructor);
            }
            $message->setSerializedBody($body);
            unset($body);
        }
        $this->connect();
        if ($message->hasReply()) {
            // Replied to (e.g. cancelled) while connecting: its body was already freed, and it must not be sent.
            return;
        }
        $this->pendingOutgoingGauge?->inc();
        if ($message->unencrypted) {
            $this->unencryptedPendingOutgoing->enqueue($message);
        } elseif ($message->specialMethodType === SpecialMethodType::UNAUTHED_METHOD) {
            $this->uninitedPendingOutgoing->enqueue($message);
        } else {
            $this->mainPendingOutgoing->enqueue($message);
        }
        $this->flush();
    }
    /**
     * Flush pending packets.
     */
    public function flush(bool $postpone = false): void
    {
        if (isset($this->writer)) {
            $this->writer->resume($postpone);
        }
    }
    /**
     * Connect main instance.
     *
     * @param DataCenterConnection $extra Shared instance
     * @param int                  $id    Connection ID
     *
     * @psalm-external-mutation-free
     */
    public function setExtra(DataCenterConnection $extra, int $datacenter, int $id): void
    {
        $this->shared = $extra;
        $this->id = $id;
        $this->API = $extra->getExtra();
        $this->datacenter = $datacenter;
        $this->datacenterId = $this->datacenter . '.' . $this->id;
    }
    /**
     * Get main instance.
     */
    public function getExtra(): MTProto
    {
        return $this->API;
    }
    /**
     * Get shared connection instance.
     */
    public function getShared(): DataCenterConnection
    {
        return $this->shared;
    }
    /**
     * Disconnect from DC.
     *
     * @param bool $temporary Whether the disconnection is temporary, triggered by the reconnect method
     */
    public function disconnect(bool $temporary = false): void
    {
        $this->API->logger("Disconnecting from DC {$this->datacenterId}");
        $this->needsReconnect = true;
        if ($this->stream) {
            try {
                $stream = $this->stream;
                $this->stream = null;
                $stream->disconnect();
            } catch (ClosedException $e) {
                $this->API->logger($e);
            }
        }

        $this->reader?->stop();
        $this->writer?->stop();
        $this->pinger?->stop();
        $this->cleanup?->stop();

        if (!$temporary) {
            $this->shared->signalDisconnect($this->id);
        }
        $this->API->logger("Disconnected from DC {$this->datacenterId}");
    }
    /**
     * Reconnect to DC.
     */
    public function reconnect(): void
    {
        $this->API->logger("Reconnecting DC {$this->datacenterId}");
        $this->disconnect(true);
        $this->shared->connect($this->id);
        $this->connect();
    }
    /**
     * Get name.
     *
     * @psalm-pure
     */
    public function getName(): string
    {
        return self::class;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\GroupCall\Rtmp;

use danog\MadelineProto\Exception;

/**
 * The RTMP server answered a command (connect, createStream) with an _error, or refused to publish
 * the stream (an onStatus of level error, e.g. NetStream.Publish.BadName for an invalid key). The
 * publisher retries it with a backoff, like a dropped connection: the key Telegram gives is the same
 * until an admin revokes it, so a refusal of it may be transient (e.g. the ingest still holding a
 * previous session of ours).
 *
 * @internal
 */
final class RtmpRefusedException extends Exception
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\GroupCall\Rtmp;

use UnexpectedValueException;

/**
 * The subset of AMF0 (Action Message Format) used by the commands and metadata of RTMP.
 *
 * Arrays with string keys are objects, lists are strict arrays; {@see Amf0EcmaArray} wraps the
 * associative arrays to send as ECMA arrays (the type of onMetaData).
 *
 * @internal
 */
final class Amf0
{
    private const NUMBER = 0x00;
    private const BOOLEAN = 0x01;
    private const STRING = 0x02;
    private const OBJECT = 0x03;
    private const NULL = 0x05;
    private const UNDEFINED = 0x06;
    private const ECMA_ARRAY = 0x08;
    private const OBJECT_END = 0x09;
    private const STRICT_ARRAY = 0x0A;
    private const DATE = 0x0B;
    private const LONG_STRING = 0x0C;

    /**
     * Encode values one after the other, as the arguments of a command.
     *
     * @psalm-pure
     */
    public static function encode(mixed ...$values): string
    {
        $out = '';
        foreach ($values as $value) {
            $out .= self::encodeValue($value);
        }
        return $out;
    }

    /**
     * @psalm-pure
     */
    private static function encodeValue(mixed $value): string
    {
        if ($value === null) {
            return \chr(self::NULL);
        }
        if (\is_bool($value)) {
            return \chr(self::BOOLEAN).($value ? "\x01" : "\x00");
        }
        if (\is_int($value) || \is_float($value)) {
            return \chr(self::NUMBER).pack('E', (float) $value);
        }
        if (\is_string($value)) {
            return \strlen($value) > 0xFFFF
                ? \chr(self::LONG_STRING).pack('N', \strlen($value)).$value
                : \chr(self::STRING).pack('n', \strlen($value)).$value;
        }
        if ($value instanceof Amf0EcmaArray) {
            return \chr(self::ECMA_ARRAY).pack('N', \count($value->values)).self::encodeProperties($value->values);
        }
        if (\is_array($value)) {
            if (array_is_list($value) && $value !== []) {
                $out = \chr(self::STRICT_ARRAY).pack('N', \count($value));
                foreach ($value as $item) {
                    $out .= self::encodeValue($item);
                }
                return $out;
            }
            return \chr(self::OBJECT).self::encodeProperties($value);
        }
        throw new UnexpectedValueException('Cannot encode a '.get_debug_type($value).' in AMF0');
    }

    /**
     * @param array<array-key, mixed> $properties
     *
     * @psalm-pure
     */
    private static function encodeProperties(array $properties): string
    {
        $out = '';
        foreach ($properties as $key => $value) {
            $key = (string) $key;
            $out .= pack('n', \strlen($key)).$key.self::encodeValue($value);
        }
        return $out."\x00\x00".\chr(self::OBJECT_END);
    }

    /**
     * Decode every value of a payload.
     *
     * @return list<mixed>
     *
     * @throws UnexpectedValueException If the payload is malformed.
     */
    public static function decode(string $data): array
    {
        $offset = 0;
        $values = [];
        while ($offset < \strlen($data)) {
            $values[] = self::decodeValue($data, $offset);
        }
        return $values;
    }

    private static function decodeValue(string $data, int &$offset): mixed
    {
        $type = \ord(self::take($data, $offset, 1));
        switch ($type) {
            case self::NUMBER:
                return unpack('E', self::take($data, $offset, 8))[1];
            case self::BOOLEAN:
                return self::take($data, $offset, 1) !== "\x00";
            case self::STRING:
                return self::take($data, $offset, unpack('n', self::take($data, $offset, 2))[1]);
            case self::LONG_STRING:
                return self::take($data, $offset, unpack('N', self::take($data, $offset, 4))[1]);
            case self::NULL:
            case self::UNDEFINED:
                return null;
            case self::ECMA_ARRAY:
                self::take($data, $offset, 4); // The count is only a hint.
                return self::decodeProperties($data, $offset);
            case self::OBJECT:
                return self::decodeProperties($data, $offset);
            case self::STRICT_ARRAY:
                $count = unpack('N', self::take($data, $offset, 4))[1];
                $values = [];
                for ($i = 0; $i < $count; $i++) {
                    $values[] = self::decodeValue($data, $offset);
                }
                return $values;
            case self::DATE:
                $milliseconds = unpack('E', self::take($data, $offset, 8))[1];
                self::take($data, $offset, 2); // Time zone, unused.
                return $milliseconds;
        }
        throw new UnexpectedValueException("Unsupported AMF0 type $type");
    }

    /**
     * @return array<string, mixed>
     */
    private static function decodeProperties(string $data, int &$offset): array
    {
        $properties = [];
        while (true) {
            $key = self::take($data, $offset, unpack('n', self::take($data, $offset, 2))[1]);
            if ($key === '' && \ord($data[$offset] ?? "\0") === self::OBJECT_END) {
                $offset++;
                return $properties;
            }
            $properties[$key] = self::decodeValue($data, $offset);
        }
    }

    private static function take(string $data, int &$offset, int $length): string
    {
        if ($offset + $length > \strlen($data)) {
            throw new UnexpectedValueException('Truncated AMF0 data');
        }
        $value = substr($data, $offset, $length);
        $offset += $length;
        return $value;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\GroupCall\Rtmp;

/**
 * An associative array to encode as an AMF0 ECMA array, rather than as an object.
 *
 * @internal
 *
 * @psalm-immutable
 */
final class Amf0EcmaArray
{
    /**
     * @param array<string, mixed> $values
     */
    public function __construct(public readonly array $values)
    {
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\GroupCall\Rtmp;

use danog\MadelineProto\Exception;

/**
 * The bodies of the FLV tags carried by RTMP audio and video messages: H.264 (AVC) and AAC, the
 * only codecs the RTMP ingest of Telegram accepts.
 *
 * AAC frames travel from the {@see \danog\MadelineProto\Loop\VoIP\DjLoop} to the publisher with an
 * ADTS header, which carries what FLV needs in its sequence header (the AudioSpecificConfig): so a
 * playlist can mix files with different AAC configurations, frames are self-describing.
 *
 * @internal
 */
final class FlvTags
{
    /** The FLV codec ID of H.264. */
    public const VIDEO_CODEC_AVC = 7;
    /** The FLV codec ID of AAC. */
    public const AUDIO_CODEC_AAC = 10;

    /** The sampling frequencies of an ADTS header or an AudioSpecificConfig, by index. */
    public const SAMPLING_FREQUENCIES = [96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350];

    /**
     * The sequence header of an H.264 stream: its AVCDecoderConfigurationRecord.
     *
     * @psalm-pure
     */
    public static function avcSequenceHeader(string $avcc): string
    {
        return "\x17\x00\x00\x00\x00".$avcc;
    }

    /**
     * An H.264 frame, as length-prefixed NAL units.
     *
     * @param int $compositionTime The presentation timestamp minus the decoding timestamp, in milliseconds.
     *
     * @psalm-pure
     */
    public static function avcFrame(string $nalUnits, bool $keyframe, int $compositionTime): string
    {
        return \chr((($keyframe ? 1 : 2) << 4) | self::VIDEO_CODEC_AVC)."\x01"
            .substr(pack('N', $compositionTime & 0xFFFFFF), 1).$nalUnits;
    }

    /**
     * The sequence header of an AAC stream: its AudioSpecificConfig.
     *
     * @psalm-pure
     */
    public static function aacSequenceHeader(string $audioSpecificConfig): string
    {
        // Whatever the real rate and channels: for AAC, the AudioSpecificConfig describes them.
        return "\xAF\x00".$audioSpecificConfig;
    }

    /**
     * A raw AAC frame (without its ADTS header).
     *
     * @psalm-pure
     */
    public static function aacFrame(string $raw): string
    {
        return "\xAF\x01".$raw;
    }

    /**
     * The ADTS header of a raw AAC frame, from the AudioSpecificConfig of its track.
     *
     * @throws Exception If the configuration can't be carried by ADTS (an explicit sampling
     *                   frequency, or channels described by a program config element).
     *
     * @psalm-pure
     */
    public static function adtsHeader(string $audioSpecificConfig, int $payloadLength): string
    {
        [$objectType, $frequencyIndex, $channels] = self::parseAudioSpecificConfig($audioSpecificConfig);
        if ($objectType < 1 || $objectType > 4) {
            throw new Exception("AAC audio object type $objectType is not supported, please re-encode the audio as AAC-LC");
        }
        $length = 7 + $payloadLength;
        return "\xFF\xF1"
            .\chr((($objectType - 1) << 6) | ($frequencyIndex << 2) | ($channels >> 2))
            .\chr((($channels & 3) << 6) | ($length >> 11))
            .\chr(($length >> 3) & 0xFF)
            .\chr((($length & 7) << 5) | 0x1F)
            ."\xFC";
    }

    /**
     * Split an ADTS frame into the AudioSpecificConfig it describes and its raw AAC payload.
     *
     * @return ?array{string, string} Null if it isn't an ADTS frame.
     *
     * @psalm-pure
     */
    public static function parseAdts(string $frame): ?array
    {
        if (\strlen($frame) < 7 || $frame[0] !== "\xFF" || (\ord($frame[1]) & 0xF6) !== 0xF0) {
            return null;
        }
        // Two more bytes, for the CRC, if the protection is not absent.
        $headerLength = (\ord($frame[1]) & 0x01) ? 7 : 9;
        $byte2 = \ord($frame[2]);
        $objectType = ($byte2 >> 6) + 1;
        $frequencyIndex = ($byte2 >> 2) & 0x0F;
        $channels = (($byte2 & 0x01) << 2) | (\ord($frame[3]) >> 6);
        // audioObjectType (5 bits), samplingFrequencyIndex (4), channelConfiguration (4), then the
        // GASpecificConfig: 1024 samples per frame, no core coder, no extension (3 zero bits).
        $config = pack('n', ($objectType << 11) | ($frequencyIndex << 7) | ($channels << 3));
        return [$config, substr($frame, $headerLength)];
    }

    /**
     * The sampling frequency of an AudioSpecificConfig.
     *
     * @psalm-pure
     */
    public static function sampleRate(string $audioSpecificConfig): int
    {
        return self::SAMPLING_FREQUENCIES[self::parseAudioSpecificConfig($audioSpecificConfig)[1]];
    }

    /**
     * Read the audio object type (of the core coder, for HE-AAC), the sampling frequency index and
     * the channel configuration of an AudioSpecificConfig.
     *
     * @return array{int, int, int}
     *
     * @throws Exception If ADTS can't carry them.
     *
     * @psalm-pure
     */
    private static function parseAudioSpecificConfig(string $config): array
    {
        if (\strlen($config) < 2) {
            throw new Exception('Missing the AudioSpecificConfig of the AAC audio');
        }
        $bits = 0;
        $bitCount = 0;
        $offset = 0;
        $read = static function (int $count) use ($config, &$bits, &$bitCount, &$offset): int {
            while ($bitCount < $count) {
                $bits = ($bits << 8) | \ord($config[$offset++] ?? "\0");
                $bitCount += 8;
            }
            $bitCount -= $count;
            $value = ($bits >> $bitCount) & ((1 << $count) - 1);
            $bits &= (1 << $bitCount) - 1;
            return $value;
        };
        $objectType = $read(5);
        if ($objectType === 31) {
            $objectType = 32 + $read(6);
        }
        $frequencyIndex = $read(4);
        if ($frequencyIndex === 15) {
            throw new Exception('AAC audio with an explicit sampling frequency is not supported, please re-encode it at a standard rate (e.g. 48000 Hz)');
        }
        $channels = $read(4);
        if ($channels === 0) {
            throw new Exception('AAC audio whose channels are described by a program config element is not supported, please re-encode it as mono or stereo');
        }
        if ($objectType === 5 || $objectType === 29) {
            // HE-AAC (SBR, PS) signalled explicitly: ADTS carries the core AAC coder, at its sampling
            // frequency (the one above), and decoders detect SBR implicitly.
            $extensionIndex = $read(4);
            if ($extensionIndex === 15) {
                $read(24);
            }
            $objectType = $read(5);
            if ($objectType === 31) {
                $objectType = 32 + $read(6);
            }
        }
        return [$objectType, $frequencyIndex, $channels];
    }

    /**
     * The onMetaData of a stream, carried by an @setDataFrame data message.
     *
     * @psalm-pure
     */
    public static function metadata(?int $width, ?int $height, bool $video, bool $audio): string
    {
        $values = ['duration' => 0.0];
        if ($video) {
            $values['videocodecid'] = (float) self::VIDEO_CODEC_AVC;
            if ($width !== null && $height !== null) {
                $values['width'] = (float) $width;
                $values['height'] = (float) $height;
            }
        }
        if ($audio) {
            $values['audiocodecid'] = (float) self::AUDIO_CODEC_AAC;
        }
        $values['encoder'] = 'MadelineProto';
        return Amf0::encode('@setDataFrame', 'onMetaData', new Amf0EcmaArray($values));
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\GroupCall\Rtmp;

use Amp\Cancellation;
use Amp\DeferredFuture;
use Amp\Socket\ClientTlsContext;
use Amp\Socket\ConnectContext;
use Amp\Socket\Socket;
use danog\MadelineProto\Exception;
use Revolt\EventLoop;
use Throwable;

use function Amp\Socket\connect;

/**
 * A minimal RTMP(S) client that publishes a stream, as an RTMP encoder (OBS, ffmpeg) does.
 *
 * See the [RTMP specification](https://rtmp.veriskope.com/docs/spec/): the simple handshake, the
 * chunk stream, the protocol control messages and the AMF0 commands of NetConnection (connect,
 * createStream) and NetStream (publish).
 *
 * @internal
 */
final class RtmpClient
{
    private const CHUNK_STREAM_CONTROL = 2;
    private const CHUNK_STREAM_COMMAND = 3;
    private const CHUNK_STREAM_AUDIO = 4;
    private const CHUNK_STREAM_VIDEO = 6;
    private const CHUNK_STREAM_DATA = 5;

    private const TYPE_SET_CHUNK_SIZE = 1;
    private const TYPE_ABORT = 2;
    private const TYPE_ACKNOWLEDGEMENT = 3;
    private const TYPE_USER_CONTROL = 4;
    private const TYPE_WINDOW_ACK_SIZE = 5;
    private const TYPE_SET_PEER_BANDWIDTH = 6;
    public const TYPE_AUDIO = 8;
    public const TYPE_VIDEO = 9;
    private const TYPE_DATA_AMF0 = 18;
    private const TYPE_COMMAND_AMF0 = 20;

    private const USER_CONTROL_PING_REQUEST = 6;
    private const USER_CONTROL_PING_RESPONSE = 7;

    /** The size of the chunks we send: fewer chunk headers than the default 128 bytes. */
    private const OUTGOING_CHUNK_SIZE = 4096;

    private int $outgoingChunkSize = 128;
    private int $incomingChunkSize = 128;
    private int $windowAckSize = 2500000;
    private int $bytesReceived = 0;
    private int $bytesAcknowledged = 0;
    private string $buffer = '';
    /** @var array<int, array{timestamp: int, delta: int, length: int, type: int, stream: int, extended: bool, payload: string}> */
    private array $chunkStreams = [];
    /** @var array<int, DeferredFuture<list<mixed>>> The commands waiting for their _result, by transaction ID. */
    private array $transactions = [];
    private int $transaction = 0;
    /** @var ?DeferredFuture<null> Completed once the server starts accepting what we publish. */
    private ?DeferredFuture $publishing = null;
    private int $stream = 0;
    private bool $closed = false;
    private ?Throwable $error = null;
    /** @var DeferredFuture<null> Completed when the connection is closed. */
    private readonly DeferredFuture $closedDeferred;
    /** When the server started accepting what we publish (microtime). */
    private ?float $publishedAt = null;
    /** How many audio and video messages were sent. */
    private int $mediaMessages = 0;
    /** When the last audio or video message was sent (microtime). */
    private ?float $lastMediaAt = null;
    /** The last onStatus the server sent after accepting the stream, if any. */
    private ?string $lastStatus = null;

    private function __construct(private readonly Socket $socket, private readonly string $key)
    {
        $this->closedDeferred = new DeferredFuture;
    }

    /**
     * Wait until the connection is closed, by us or by the server.
     */
    public function awaitClose(?Cancellation $cancellation = null): void
    {
        $this->closedDeferred->getFuture()->await($cancellation);
    }

    /**
     * Connect to an RTMP(S) server and start publishing a stream.
     *
     * @param string $url The URL of the RTMP application, e.g. `rtmps://dc4-1.rtmp.t.me/s/`.
     * @param string $key The stream key.
     *
     * @throws Exception If the server refuses the stream.
     */
    public static function publish(string $url, string $key, ?Cancellation $cancellation = null): self
    {
        $parts = parse_url($url);
        if (!isset($parts['scheme'], $parts['host']) || !\in_array($parts['scheme'], ['rtmp', 'rtmps'], true)) {
            throw new Exception("Invalid RTMP URL: $url");
        }
        $tls = $parts['scheme'] === 'rtmps';
        $host = $parts['host'];
        $port = $parts['port'] ?? ($tls ? 443 : 1935);
        $app = trim($parts['path'] ?? '', '/');
        $context = new ConnectContext;
        if ($tls) {
            $context = $context->withTlsContext(new ClientTlsContext($host));
        }
        $socket = connect("tcp://$host:$port", $context, $cancellation);
        if ($tls) {
            $socket->setupTls($cancellation);
        }
        $client = new self($socket, $key);
        try {
            $client->handshake();
            EventLoop::queue($client->readLoop(...));
            $client->setChunkSize();
            $client->command('connect', [
                'app' => $app,
                'type' => 'nonprivate',
                'flashVer' => 'FMLE/3.0 (compatible; MadelineProto)',
                'tcUrl' => $parts['scheme']."://$host".(isset($parts['port']) ? ":$port" : '')."/$app",
            ]);
            $client->notify('releaseStream', null, $key);
            $client->notify('FCPublish', null, $key);
            $result = $client->command('createStream', null);
            $client->stream = (int) ($result[1] ?? 1);
            $client->publishing = new DeferredFuture;
            $client->notify('publish', null, $key, 'live');
            $client->publishing->getFuture()->await($cancellation);
            $client->publishedAt = microtime(true);
        } catch (Throwable $e) {
            $client->close();
            throw $e;
        }
        return $client;
    }

    /**
     * The simple handshake: C0+C1, S0+S1+S2, C2.
     */
    private function handshake(): void
    {
        $c1 = "\0\0\0\0\0\0\0\0".random_bytes(1528);
        $this->socket->write("\x03".$c1);
        $response = '';
        while (\strlen($response) < 1 + 1536 * 2) {
            $chunk = $this->socket->read();
            if ($chunk === null) {
                throw new Exception('The RTMP server closed the connection during the handshake');
            }
            $response .= $chunk;
        }
        if ($response[0] !== "\x03") {
            throw new Exception('Unsupported RTMP version '.\ord($response[0]));
        }
        // C2 echoes S1.
        $this->socket->write(substr($response, 1, 1536));
        // Anything after S2 already belongs to the chunk stream.
        $this->buffer = substr($response, 1 + 1536 * 2);
    }

    private function setChunkSize(): void
    {
        $this->writeMessage(self::CHUNK_STREAM_CONTROL, self::TYPE_SET_CHUNK_SIZE, 0, 0, pack('N', self::OUTGOING_CHUNK_SIZE));
        $this->outgoingChunkSize = self::OUTGOING_CHUNK_SIZE;
    }

    /**
     * Send a command and wait for its _result.
     *
     * @return list<mixed> The values of the _result after the transaction ID: the command object and the reply.
     *
     * @throws Exception If the server answers with an _error.
     */
    private function command(string $name, mixed ...$arguments): array
    {
        $transaction = ++$this->transaction;
        $deferred = new DeferredFuture;
        $this->transactions[$transaction] = $deferred;
        $this->writeMessage(self::CHUNK_STREAM_COMMAND, self::TYPE_COMMAND_AMF0, 0, 0, Amf0::encode($name, (float) $transaction, ...$arguments));
        return $deferred->getFuture()->await();
    }

    /**
     * Send a command whose answer, if any, we don't wait for.
     */
    private function notify(string $name, mixed ...$arguments): void
    {
        $this->writeMessage(
            self::CHUNK_STREAM_COMMAND,
            self::TYPE_COMMAND_AMF0,
            $name === 'publish' ? $this->stream : 0,
            0,
            Amf0::encode($name, (float) ++$this->transaction, ...$arguments)
        );
    }

    /**
     * Send the onMetaData of the stream.
     */
    public function sendMetadata(string $metadata): void
    {
        $this->writeMessage(self::CHUNK_STREAM_DATA, self::TYPE_DATA_AMF0, $this->stream, 0, $metadata);
    }

    /**
     * Send an audio or video message.
     *
     * @param self::TYPE_AUDIO|self::TYPE_VIDEO $type
     * @param int $timestamp In milliseconds.
     */
    public function sendMedia(int $type, int $timestamp, string $body): void
    {
        $this->writeMessage($type === self::TYPE_AUDIO ? self::CHUNK_STREAM_AUDIO : self::CHUNK_STREAM_VIDEO, $type, $this->stream, $timestamp, $body);
        $this->mediaMessages++;
        $this->lastMediaAt = microtime(true);
    }

    /**
     * How many audio and video messages were sent on this connection.
     *
     * @psalm-mutation-free
     */
    public function getMediaMessages(): int
    {
        return $this->mediaMessages;
    }

    /**
     * What happened on the connection, for the logs: how long the stream was published, the media
     * sent and the last status of the server.
     */
    public function describe(): string
    {
        $now = microtime(true);
        $result = $this->publishedAt === null
            ? 'before publishing'
            : \sprintf('%.1f seconds after publishing', $now - $this->publishedAt);
        $result .= ", {$this->mediaMessages} audio/video messages sent";
        if ($this->lastMediaAt !== null) {
            $result .= \sprintf(' (the last one %.1f seconds before)', $now - $this->lastMediaAt);
        }
        if ($this->lastStatus !== null) {
            $result .= ", last status: {$this->lastStatus}";
        }
        return $result;
    }

    /**
     * Whether the connection is closed: the publisher has to reconnect.
     *
     * @psalm-mutation-free
     */
    public function isClosed(): bool
    {
        return $this->closed;
    }

    /**
     * Why the connection was closed, if it was by an error.
     *
     * @psalm-mutation-free
     */
    public function getError(): ?Throwable
    {
        return $this->error;
    }

    /**
     * Stop publishing and close the connection.
     */
    public function close(): void
    {
        if ($this->closed) {
            return;
        }
        if ($this->stream !== 0) {
            try {
                $this->notify('FCUnpublish', null, $this->key);
                $this->notify('deleteStream', null, (float) $this->stream);
            } catch (Throwable) {
            }
        }
        $this->fail(null);
    }

    private function fail(?Throwable $error): void
    {
        if ($this->closed) {
            return;
        }
        $this->closed = true;
        $this->error = $error;
        $this->socket->close();
        if ($this->transactions !== [] || ($this->publishing !== null && !$this->publishing->isComplete())) {
            $error ??= new Exception('The RTMP connection was closed');
            foreach ($this->transactions as $deferred) {
                $deferred->error($error);
            }
            $this->transactions = [];
            if ($this->publishing !== null && !$this->publishing->isComplete()) {
                $this->publishing->error($error);
            }
        }
        $this->closedDeferred->complete();
    }

    /**
     * Write one message, split into chunks: the first one with a full (type 0) header, the others
     * with none (type 3).
     */
    private function writeMessage(int $chunkStream, int $type, int $stream, int $timestamp, string $payload): void
    {
        if ($this->closed) {
            throw $this->error ?? new Exception('The RTMP connection was closed');
        }
        $timestamp &= 0xFFFFFFFF;
        $extended = $timestamp >= 0xFFFFFF;
        $length = \strlen($payload);
        $data = \chr($chunkStream)
            .substr(pack('N', $extended ? 0xFFFFFF : $timestamp), 1)
            .substr(pack('N', $length), 1)
            .\chr($type)
            .pack('V', $stream)
            .($extended ? pack('N', $timestamp) : '');
        for ($offset = 0; $offset < $length || $offset === 0; $offset += $this->outgoingChunkSize) {
            if ($offset > 0) {
                $data .= \chr(0xC0 | $chunkStream).($extended ? pack('N', $timestamp) : '');
            }
            $data .= substr($payload, $offset, $this->outgoingChunkSize);
            if ($length === 0) {
                break;
            }
        }
        try {
            // A single write: the messages of the audio and video senders never interleave.
            $this->socket->write($data);
        } catch (Throwable $e) {
            $this->fail($e);
            throw $e;
        }
    }

    private function readLoop(): void
    {
        try {
            while (true) {
                while ($this->parseChunk()) {
                }
                $chunk = $this->socket->read();
                if ($chunk === null) {
                    if (!$this->closed) {
                        $this->fail(new Exception('The RTMP server closed the connection'));
                    }
                    return;
                }
                $this->buffer .= $chunk;
                $this->bytesReceived += \strlen($chunk);
                if ($this->bytesReceived - $this->bytesAcknowledged >= $this->windowAckSize) {
                    $this->bytesAcknowledged = $this->bytesReceived;
                    $this->writeMessage(self::CHUNK_STREAM_CONTROL, self::TYPE_ACKNOWLEDGEMENT, 0, 0, pack('N', $this->bytesReceived & 0xFFFFFFFF));
                }
            }
        } catch (Throwable $e) {
            $this->fail($e);
        }
    }

    /**
     * Parse one chunk from the buffer, if it's complete.
     */
    private function parseChunk(): bool
    {
        $buffer = $this->buffer;
        $length = \strlen($buffer);
        if ($length < 1) {
            return false;
        }
        $first = \ord($buffer[0]);
        $format = $first >> 6;
        $chunkStream = $first & 0x3F;
        $offset = 1;
        if ($chunkStream === 0) {
            if ($length < 2) {
                return false;
            }
            $chunkStream = 64 + \ord($buffer[1]);
            $offset = 2;
        } elseif ($chunkStream === 1) {
            if ($length < 3) {
                return false;
            }
            $chunkStream = 64 + \ord($buffer[1]) + \ord($buffer[2]) * 256;
            $offset = 3;
        }
        $headerLength = [11, 7, 3, 0][$format];
        if ($length < $offset + $headerLength) {
            return false;
        }
        $state = $this->chunkStreams[$chunkStream] ?? ['timestamp' => 0, 'delta' => 0, 'length' => 0, 'type' => 0, 'stream' => 0, 'extended' => false, 'payload' => ''];
        $field = 0;
        if ($format <= 2) {
            $field = unpack('N', "\0".substr($buffer, $offset, 3))[1];
        }
        if ($format <= 1) {
            $state['length'] = unpack('N', "\0".substr($buffer, $offset + 3, 3))[1];
            $state['type'] = \ord($buffer[$offset + 6]);
        }
        if ($format === 0) {
            $state['stream'] = unpack('V', substr($buffer, $offset + 7, 4))[1];
        }
        $offset += $headerLength;
        $extended = $format <= 2 ? $field === 0xFFFFFF : $state['extended'];
        if ($extended) {
            if ($length < $offset + 4) {
                return false;
            }
            $field = $format <= 2 ? unpack('N', substr($buffer, $offset, 4))[1] : $field;
            $offset += 4;
        }
        $starting = $state['payload'] === '';
        $size = min($this->incomingChunkSize, $state['length'] - \strlen($state['payload']));
        if ($length < $offset + $size) {
            return false;
        }
        if ($format === 0) {
            $state['timestamp'] = $field;
            $state['delta'] = 0;
        } elseif ($format <= 2) {
            $state['delta'] = $field;
            $state['timestamp'] += $field;
        } elseif ($starting) {
            // A new message with the header of the previous one.
            $state['timestamp'] += $state['delta'];
        }
        $state['extended'] = $extended;
        $state['payload'] .= substr($buffer, $offset, $size);
        $this->buffer = substr($buffer, $offset + $size);
        if (\strlen($state['payload']) >= $state['length']) {
            $payload = $state['payload'];
            $state['payload'] = '';
            $this->chunkStreams[$chunkStream] = $state;
            $this->onMessage($state['type'], $state['timestamp'], $payload);
        } else {
            $this->chunkStreams[$chunkStream] = $state;
        }
        return true;
    }

    private function onMessage(int $type, int $timestamp, string $payload): void
    {
        switch ($type) {
            case self::TYPE_SET_CHUNK_SIZE:
                $this->incomingChunkSize = unpack('N', $payload)[1] & 0x7FFFFFFF;
                return;
            case self::TYPE_ABORT:
                $chunkStream = unpack('N', $payload)[1];
                if (isset($this->chunkStreams[$chunkStream])) {
                    $this->chunkStreams[$chunkStream]['payload'] = '';
                }
                return;
            case self::TYPE_WINDOW_ACK_SIZE:
                $this->windowAckSize = unpack('N', $payload)[1];
                return;
            case self::TYPE_SET_PEER_BANDWIDTH:
                // Our acknowledgement window, as the peer bandwidth the server sets.
                $this->writeMessage(self::CHUNK_STREAM_CONTROL, self::TYPE_WINDOW_ACK_SIZE, 0, 0, substr($payload, 0, 4));
                return;
            case self::TYPE_USER_CONTROL:
                if (\strlen($payload) >= 6 && unpack('n', $payload)[1] === self::USER_CONTROL_PING_REQUEST) {
                    $this->writeMessage(self::CHUNK_STREAM_CONTROL, self::TYPE_USER_CONTROL, 0, 0, pack('n', self::USER_CONTROL_PING_RESPONSE).substr($payload, 2, 4));
                }
                return;
            case self::TYPE_COMMAND_AMF0:
                $this->onCommand(Amf0::decode($payload));
                return;
        }
    }

    /**
     * @param list<mixed> $values
     */
    private function onCommand(array $values): void
    {
        $name = $values[0] ?? null;
        $transaction = (int) ($values[1] ?? 0);
        if ($name === '_result' || $name === '_error') {
            $deferred = $this->transactions[$transaction] ?? null;
            if ($deferred === null) {
                return;
            }
            unset($this->transactions[$transaction]);
            if ($name === '_result') {
                $deferred->complete(\array_slice($values, 2));
            } else {
                $deferred->error(new RtmpRefusedException('The RTMP server refused the command: '.self::describeInfo($values[3] ?? null)));
            }
            return;
        }
        if ($name === 'onStatus' && $this->publishing !== null) {
            $info = $values[3] ?? null;
            if ($this->publishing->isComplete()) {
                // E.g. NetStream.Unpublish.Success before the server drops a publisher.
                $this->lastStatus = self::describeInfo($info);
                return;
            }
            $code = \is_array($info) ? (string) ($info['code'] ?? '') : '';
            if ($code === 'NetStream.Publish.Start') {
                $this->publishing->complete();
            } elseif (\is_array($info) && ($info['level'] ?? '') === 'error') {
                $this->publishing->error(new RtmpRefusedException('The RTMP server refused the stream: '.self::describeInfo($info)));
            }
        }
    }

    private static function describeInfo(mixed $info): string
    {
        if (\is_array($info)) {
            return trim(($info['code'] ?? '').' '.($info['description'] ?? ''));
        }
        return var_export($info, true);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\GroupCall\Rtmp;

use Amp\CancelledException;
use Amp\DeferredCancellation;
use Amp\DeferredFuture;
use Closure;
use danog\MadelineProto\Logger;
use danog\MadelineProto\Loop\VoIP\DjLoop;
use danog\MadelineProto\RPCError\RateLimitError;
use danog\MadelineProto\RPCErrorException;
use danog\MadelineProto\Tgcalls\CallControllerInterface;
use danog\MadelineProto\Tgcalls\CallRecorder;
use danog\MadelineProto\Tgcalls\OpusPlaybackTrack;
use danog\MadelineProto\Tgcalls\VideoPlaybackTrack;
use Revolt\EventLoop;
use SplMinHeap;
use Throwable;
use Webrtc\AVCodec\Frame\FrameInterface;
use Webrtc\Codecs\EncodedPacket;
use Webrtc\RTP\MediaStreamTrack\MediaStreamTrackSink;

use function Amp\delay;

/**
 * Publishes what a {@see DjLoop} plays to an RTMP livestream, as an external RTMP encoder would.
 *
 * The frames come from the same playback tracks that feed a group call (so playlists, hold files,
 * pausing and resuming work the same), from a DJ loop in RTMP mode, which only plays H.264 and AAC
 * (the codecs the RTMP ingest of Telegram accepts) and frames AAC with ADTS, see {@see FlvTags}.
 *
 * The connection is reopened if it drops; meanwhile the frames are played (and dropped), so that
 * the stream stays live. The RTMP ingest of Telegram drops a publisher that sends no media for 10
 * seconds, as when nothing is played (an empty playlist without hold files, or paused playback):
 * then the connection is only reopened once something is played again. A server that keeps closing
 * the connection while media flows, or refuses the stream, is retried with an exponential backoff
 * (the key is fetched again each time, which only changes if an admin revoked it: it's never revoked
 * here); a key that can't be fetched anymore (e.g. the admin rights were lost) stops publishing.
 *
 * @internal
 */
final class RtmpPublisher
{
    /**
     * How many frames the decoding timestamps lag the presentation ones by, to fit reordered (B)
     * frames: enough for the B-pyramids of x264, as up to two frames are displayed before the
     * reference frame decoded before them.
     */
    private const REORDER_DEPTH = 2;
    /** How far the timestamps of a track may drift from the wall clock before re-anchoring it, in milliseconds. */
    private const MAX_DRIFT_MS = 1000;
    private const MIN_RECONNECT_DELAY = 1.0;
    private const MAX_RECONNECT_DELAY = 30.0;
    /** How long a connection must stay up to reset the backoff, in seconds. */
    private const STABLE_CONNECTION = 60.0;
    /**
     * For how long nothing must have been played, in seconds, for a closed connection to count as
     * dropped for idleness: well below the 10 seconds after which the ingest of Telegram drops a
     * publisher sending no media.
     */
    private const IDLE = 5.0;

    private readonly OpusPlaybackTrack $audioTrack;
    private readonly VideoPlaybackTrack $videoTrack;
    /** The sinks of the tracks, held here as the tracks only hold them weakly. */
    private readonly MediaStreamTrackSink $audioSink;
    private readonly MediaStreamTrackSink $videoSink;

    private ?RtmpClient $client = null;
    private bool $running = true;
    private readonly DeferredCancellation $stopping;
    /** Wall clock time (milliseconds) at which the current connection started: the zero of its timestamps. */
    private float $epoch = 0.0;

    /** @var array<string, array{wall: float, timestamp: int}|null> The anchor of the timeline of each track, by kind. */
    private array $anchors = ['audio' => null, 'video' => null];
    private ?string $avcConfig = null;
    private ?string $aacConfig = null;
    private bool $sentKeyframe = false;
    /** @var list<array{pts: int, data: string, keyframe: bool}> Video frames held to compute their decoding timestamps. */
    private array $reorder = [];
    /** @var SplMinHeap<int> The presentation timestamps of the video frames not used as decoding timestamps yet. */
    private SplMinHeap $pending;
    /** How many video frames were sent since the last keyframe (included). */
    private int $sentSinceKeyframe = 0;
    private int $lastVideoTimestamp = 0;
    private bool $warnedAudio = false;
    /** The onMetaData sent on this connection, if any. */
    private ?string $metadata = null;
    /** When audio started waiting for the metadata of the first video keyframe (wall clock, seconds). */
    private ?float $awaitingVideoSince = null;
    /** When the tracks last offered a frame to publish (wall clock, seconds), connected or not. */
    private float $lastFrameAt = 0.0;
    /** @var ?DeferredFuture<null> Completed when there is something to publish again, see {@see self::wake()}. */
    private ?DeferredFuture $frameWaiter = null;

    /**
     * @param Closure(bool): array{url: string, key: string} $ingest   Get the URL and key to publish to (true to revoke the key first: never passed by the publisher, as revoking would cut off the other encoders using it).
     * @param ?Closure(Throwable): void                     $onGiveUp Called once publishing stopped for good, see {@see self::giveUp()}.
     * @param float                                         $idle     See {@see self::IDLE}.
     */
    public function __construct(
        private readonly CallControllerInterface $call,
        private readonly DjLoop $dj,
        private readonly Closure $ingest,
        private readonly ?Closure $onGiveUp = null,
        private readonly float $idle = self::IDLE,
    ) {
        $this->stopping = new DeferredCancellation;
        $this->pending = new SplMinHeap;
        $publisher = $this;
        $this->audioSink = new class($publisher) implements MediaStreamTrackSink {
            public function __construct(private readonly RtmpPublisher $publisher)
            {
            }
            public function onFrame(FrameInterface|EncodedPacket $frame): bool
            {
                return $frame instanceof EncodedPacket ? $this->publisher->onAudio($frame) : true;
            }
        };
        $this->videoSink = new class($publisher) implements MediaStreamTrackSink {
            public function __construct(private readonly RtmpPublisher $publisher)
            {
            }
            public function onFrame(FrameInterface|EncodedPacket $frame): bool
            {
                return $frame instanceof EncodedPacket ? $this->publisher->onVideo($frame) : true;
            }
        };
        $this->audioTrack = new OpusPlaybackTrack($dj, $call);
        $this->videoTrack = new VideoPlaybackTrack($dj, $call, 'RTMP');
        $this->audioTrack->setSink($this->audioSink);
        $this->videoTrack->setSink($this->videoSink);
        EventLoop::queue($this->connectionLoop(...));
    }

    /**
     * Stop publishing.
     */
    public function stop(): void
    {
        if (!$this->running) {
            return;
        }
        $this->running = false;
        $this->stopping->cancel();
        $this->frameWaiter = null;
        $this->audioTrack->stop();
        $this->videoTrack->stop();
        $this->flushVideo();
        $this->client?->close();
        $this->client = null;
    }

    /**
     * Whether a connection to the RTMP server is up.
     *
     * @psalm-mutation-free
     */
    public function isConnected(): bool
    {
        return $this->client !== null && !$this->client->isClosed();
    }

    /**
     * Something is about to be played: reconnect now, if the connection was dropped while nothing was.
     */
    public function wake(): void
    {
        $waiter = $this->frameWaiter;
        $this->frameWaiter = null;
        $waiter?->complete();
    }

    /**
     * Wait until something is played, see {@see self::wake()}.
     */
    private function awaitSomethingToPlay(): void
    {
        $this->frameWaiter ??= new DeferredFuture;
        $this->frameWaiter->getFuture()->await($this->stopping->getCancellation());
    }

    /**
     * The tracks offered a frame to publish.
     */
    private function onFrame(): void
    {
        $this->lastFrameAt = microtime(true);
        if ($this->frameWaiter !== null) {
            $this->wake();
        }
    }

    /**
     * Whether retrying can't help: Telegram refused to give a stream key (an RPC error that is not
     * a flood wait or an internal error, e.g. CHAT_ADMIN_REQUIRED after losing the admin rights).
     */
    private static function isDefinitive(Throwable $e): bool
    {
        if ($e instanceof RPCErrorException) {
            $code = $e->getCode();
            return $code >= 400 && $code < 500 && $code !== 420;
        }
        return false;
    }

    /**
     * Stop publishing for good, after an error that retrying can't fix.
     */
    private function giveUp(Throwable $e): void
    {
        $this->call->log("Stopped publishing to the RTMP livestream of {$this->call}: {$e->getMessage()}", Logger::ERROR);
        $this->stop();
        if ($this->onGiveUp !== null) {
            ($this->onGiveUp)($e);
        }
    }

    private function connectionLoop(): void
    {
        $delay = self::MIN_RECONNECT_DELAY;
        while ($this->running) {
            try {
                // Fetched every time (never revoking it): the same key unless an admin revoked it meanwhile.
                ['url' => $url, 'key' => $key] = ($this->ingest)(false);
                $client = RtmpClient::publish($url, $key, $this->stopping->getCancellation());
                if (!$this->running) {
                    $client->close();
                    return;
                }
                // Each connection starts a fresh timeline, with its own sequence headers.
                $this->epoch = microtime(true) * 1000.0;
                $this->anchors = ['audio' => null, 'video' => null];
                $this->avcConfig = null;
                $this->aacConfig = null;
                $this->sentKeyframe = false;
                $this->reorder = [];
                $this->pending = new SplMinHeap;
                $this->sentSinceKeyframe = 0;
                $this->lastVideoTimestamp = 0;
                // Sent before the first media, once the dimensions of the video are known.
                $this->metadata = null;
                $this->awaitingVideoSince = null;
                $this->client = $client;
                $this->call->log("Publishing to the RTMP livestream of {$this->call}", Logger::NOTICE);
                $connectedAt = microtime(true);
                $client->awaitClose($this->stopping->getCancellation());
                if (!$this->running) {
                    return;
                }
                $this->client = null;
                $reason = ($client->getError()?->getMessage() ?? 'by the server').' ('.$client->describe().')';
                if (microtime(true) - $this->lastFrameAt >= $this->idle) {
                    // The ingest drops a publisher that sends no media: reconnecting now would only get
                    // dropped again 10 seconds later, in a loop, until something is played.
                    $this->call->log("The RTMP connection of {$this->call} was closed while nothing was played: $reason, reconnecting once something is played...", Logger::NOTICE);
                    $delay = self::MIN_RECONNECT_DELAY;
                    $this->awaitSomethingToPlay();
                    continue;
                }
                if (microtime(true) - $connectedAt >= self::STABLE_CONNECTION) {
                    $this->call->log("The RTMP connection of {$this->call} was closed: $reason, reconnecting...", Logger::WARNING);
                    $delay = self::MIN_RECONNECT_DELAY;
                    continue;
                }
                // Closed soon after connecting, while media flows: back off instead of reconnecting in a loop.
                $this->call->log("The RTMP connection of {$this->call} was closed: $reason, reconnecting in {$delay} seconds...", Logger::WARNING);
            } catch (Throwable $e) {
                // Including the cancellation of what we were waiting for, when stopping.
                if (!$this->running) {
                    return;
                }
                if (self::isDefinitive($e)) {
                    $this->giveUp($e);
                    return;
                }
                if ($e instanceof RateLimitError) {
                    // A flood wait longer than the flood timeout of the call: wait as long as the server asks.
                    $delay = max($delay, (float) $e->getWaitTime());
                }
                // Including a refused stream (RtmpRefusedException): the key Telegram gives is the same until an
                // admin revokes it, so a refusal of it isn't proof that it can't work (e.g. the ingest still
                // holding our previous session after a network drop), and the key is fetched again each time.
                $this->call->log("Could not publish to the RTMP livestream of {$this->call}: {$e->getMessage()}, retrying in {$delay} seconds...", Logger::ERROR);
            } finally {
                $this->client = null;
            }
            try {
                delay($delay, cancellation: $this->stopping->getCancellation());
            } catch (CancelledException) {
                return;
            }
            $delay = min($delay * 2, self::MAX_RECONNECT_DELAY);
        }
    }

    /**
     * The timestamp of a frame on the timeline of the connection, in milliseconds.
     *
     * Each track is paced by the clock of the file it plays, from when its first frame was played: its
     * timestamps are anchored to the wall clock, and anchored again when they drift away from it
     * (after a pause, or a gap between files).
     *
     * @param 'audio'|'video' $kind
     */
    private function timestamp(string $kind, int $timestamp, int $clockRate): int
    {
        $now = microtime(true) * 1000.0;
        $anchor = $this->anchors[$kind];
        if ($anchor !== null) {
            $media = $anchor['wall'] + ($timestamp - $anchor['timestamp']) * 1000.0 / $clockRate;
            if (abs($media - $now) <= self::MAX_DRIFT_MS) {
                return max(0, (int) round($media - $this->epoch));
            }
        }
        $this->anchors[$kind] = ['wall' => $now, 'timestamp' => $timestamp];
        return max(0, (int) round($now - $this->epoch));
    }

    /**
     * @internal
     */
    public function onAudio(EncodedPacket $packet): bool
    {
        if (!$this->running) {
            return false;
        }
        $this->onFrame();
        $client = $this->client;
        if ($client === null || $client->isClosed()) {
            return true;
        }
        $adts = FlvTags::parseAdts($packet->getData());
        if ($adts === null) {
            if (!$this->warnedAudio) {
                $this->warnedAudio = true;
                $this->call->log("Dropping audio that is not AAC in {$this->call}: an RTMP livestream can only carry AAC audio", Logger::ERROR);
            }
            return true;
        }
        [$config, $raw] = $adts;
        try {
            if ($this->metadata === null) {
                // The ingest sizes the livestream from the onMetaData (16:9 without dimensions), so it
                // waits for the first keyframe of a file with video, for at most a second.
                $this->awaitingVideoSince ??= microtime(true);
                if ($this->dj->getVideoCodec() !== null && microtime(true) - $this->awaitingVideoSince < 1) {
                    return true;
                }
                $this->sendMetadata($client, null, null);
            }
            $timestamp = $this->timestamp('audio', $packet->getTimestamp(), DjLoop::AUDIO_CLOCK_RATE);
            if ($config !== $this->aacConfig) {
                $client->sendMedia(RtmpClient::TYPE_AUDIO, $timestamp, FlvTags::aacSequenceHeader($config));
                $this->aacConfig = $config;
            }
            $client->sendMedia(RtmpClient::TYPE_AUDIO, $timestamp, FlvTags::aacFrame($raw));
        } catch (Throwable $e) {
            // The connection loop reconnects.
            $this->call->log("Could not send audio to the RTMP livestream of {$this->call}: {$e->getMessage()}", Logger::VERBOSE);
        }
        return true;
    }

    /**
     * @internal
     */
    public function onVideo(EncodedPacket $packet): bool
    {
        if (!$this->running) {
            return false;
        }
        $this->onFrame();
        $client = $this->client;
        if ($client === null || $client->isClosed()) {
            return true;
        }
        $annexB = $packet->getData();
        [$nalUnits, $keyframe] = CallRecorder::transformVideo($annexB, 'V_MPEG4/ISO/AVC');
        $keyframe = $keyframe || $packet->isKeyframe();
        if (!$this->sentKeyframe && !$keyframe) {
            // A decoder can only start on a keyframe.
            return true;
        }
        try {
            if ($keyframe) {
                $this->flushVideo();
                [, $width, $height, $avcc] = CallRecorder::describeVideo($annexB);
                if ($avcc !== '' && $avcc !== $this->avcConfig) {
                    $this->sendMetadata($client, $width, $height);
                    $client->sendMedia(RtmpClient::TYPE_VIDEO, $this->timestamp('video', $packet->getTimestamp(), DjLoop::VIDEO_CLOCK_RATE), FlvTags::avcSequenceHeader($avcc));
                    $this->avcConfig = $avcc;
                }
                if ($this->avcConfig === null) {
                    return true;
                }
                $this->sentKeyframe = true;
            }
            $pts = $this->timestamp('video', $packet->getTimestamp(), DjLoop::VIDEO_CLOCK_RATE);
            $this->reorder[] = ['pts' => $pts, 'data' => $nalUnits, 'keyframe' => $keyframe];
            $this->pending->insert($pts);
            if (\count($this->reorder) > self::REORDER_DEPTH) {
                $this->sendVideo($client);
            }
        } catch (Throwable $e) {
            $this->call->log("Could not send video to the RTMP livestream of {$this->call}: {$e->getMessage()}", Logger::VERBOSE);
        }
        return true;
    }

    /**
     * Send the onMetaData of the stream, if it changed: the dimensions of its video, if known.
     */
    private function sendMetadata(RtmpClient $client, ?int $width, ?int $height): void
    {
        $metadata = FlvTags::metadata($width, $height, true, true);
        if ($metadata !== $this->metadata) {
            if ($width !== null && $height !== null && $width * 9 !== $height * 16) {
                // Whatever the onMetaData and the SPS say, sample aspect ratio included.
                $this->call->log(
                    "Telegram scales RTMP livestreams to 1280x720, without keeping the aspect ratio: the {$width}x{$height} video played in {$this->call} looks stretched. "
                    ."Pad it to 16:9 first, e.g. with ffmpeg -i input -vf 'scale=1280:720:force_original_aspect_ratio=decrease,pad=1280:720:(ow-iw)/2:(oh-ih)/2' -c:v libx264 -c:a aac -movflags +faststart output.mp4",
                    Logger::WARNING
                );
            }
            $client->sendMetadata($metadata);
            $this->metadata = $metadata;
        }
    }

    /**
     * Send the oldest held video frame.
     *
     * Frames come in decoding order with their presentation timestamps, while RTMP wants them with
     * their decoding timestamps (and the difference as the composition time). The decoding
     * timestamps are the presentation ones, sorted and lagging by {@see self::REORDER_DEPTH} frames,
     * as a frame displayed before a reference frame is decoded after it: frame `i` (from the last
     * keyframe) is decoded at the `i - REORDER_DEPTH`-th presentation timestamp, the first ones
     * just before the first presentation timestamp.
     */
    private function sendVideo(RtmpClient $client): void
    {
        $frame = array_shift($this->reorder);
        if ($frame === null) {
            return;
        }
        if ($this->sentSinceKeyframe < self::REORDER_DEPTH) {
            $dts = $this->pending->top() - (self::REORDER_DEPTH - $this->sentSinceKeyframe);
        } else {
            $dts = $this->pending->extract();
        }
        $this->sentSinceKeyframe++;
        $dts = max($this->lastVideoTimestamp, $dts);
        $this->lastVideoTimestamp = $dts;
        // Negative (a deeper reordering than expected) is allowed: the composition time is signed.
        $client->sendMedia(RtmpClient::TYPE_VIDEO, $dts, FlvTags::avcFrame($frame['data'], $frame['keyframe'], $frame['pts'] - $dts));
    }

    /**
     * Send every held video frame: before a keyframe, which nothing after it references across.
     */
    private function flushVideo(): void
    {
        $client = $this->client;
        if ($client === null || $client->isClosed()) {
            $this->reorder = [];
            return;
        }
        try {
            while ($this->reorder !== []) {
                $this->sendVideo($client);
            }
        } catch (Throwable) {
            $this->reorder = [];
        }
        // What is left are the presentation timestamps of the last frames: not needed anymore.
        $this->pending = new SplMinHeap;
        $this->sentSinceKeyframe = 0;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\GroupCall;

use Amp\ByteStream\ReadableBuffer;
use Amp\ByteStream\WritableStream;
use danog\MadelineProto\CallStream;
use danog\MadelineProto\LocalDirectory;
use danog\MadelineProto\LocalFile;
use danog\MadelineProto\Logger;
use danog\MadelineProto\Matroska;
use danog\MadelineProto\MatroskaWriter;
use danog\MadelineProto\Mp4;
use danog\MadelineProto\OggWriter;
use danog\MadelineProto\RecordingEvent;
use danog\MadelineProto\RecordingFormat;
use danog\MadelineProto\RPCError\RateLimitError;
use danog\MadelineProto\RPCErrorException;
use Revolt\EventLoop;
use Throwable;

use function Amp\delay;
use function Amp\File\openFile;

/**
 * Receives the media of a group call in [stream mode](https://core.telegram.org/api/group-calls#stream-mode)
 * (a large livestream the server no longer serves over WebRTC, or an RTMP livestream): downloads its
 * media chunks from the stream DC every second and records them.
 *
 * An RTMP livestream is a single unified channel carrying audio and video, recorded together into a
 * Matroska file (or audio only into OGG OPUS). An automatically-scaled livestream serves the mixed
 * audio as OGG OPUS chunks whose metadata lists the publishers (`ENDPOINTS`, `ACTIVE_MASK`), and
 * each publisher's video as separate MP4 chunks: recording into a directory writes the mixed audio to
 * `stream.ogg` and every publisher's video to `video-<endpoint>.mkv`.
 *
 * As official clients do, the stream is followed one 1000 ms segment at a time, 2 seconds behind the
 * live edge; a chunk that is not ready yet (`TIME_TOO_BIG`, a flood wait) is retried after 100 ms, any
 * other error resynchronizes to the live edge, and being told we are not in the call any more
 * (`GROUPCALL_JOIN_MISSING` / `GROUPCALL_FORBIDDEN`) rejoins it.
 *
 * @internal
 */
final class StreamReceiver
{
    /** Chunk length: official clients hardcode 1000 ms (the deprecated `scale` is ignored). */
    private const SEGMENT_MS = 1000;
    /** How far behind the live edge playback stays, so chunks are ready when asked for. */
    private const BUFFER_MS = 2000;
    /** Header signature of a unified (RTMP) chunk, which is followed by a container name and events. */
    private const UNIFIED_SIGNATURE = 0xa12e810d;
    private const RETRY_MS = 100;
    /** How long to wait before fetching the stream channels again when they have no usable timestamp, in seconds. */
    private const CHANNELS_RETRY = 1.0;
    /** How many audio-only chunks of an RTMP stream are held, waiting for its video, before recording only its audio. */
    private const MAX_AUDIO_ONLY_CHUNKS = 10;

    private bool $running = false;
    private bool $rtmp = false;
    private int $generation = 0;
    private ?int $nextTimestamp = null;
    /** The timestamp of the last chunk consumed: a resynchronization never goes back to it. */
    private ?int $lastTimestamp = null;
    /**
     * @var list<array{list<array{track: int, codec: string, type: int, data: string, timestamp: int, keyframe: bool}>, array<int, array<string, mixed>>, int}>
     *      The first chunks of an RTMP stream, without video, held until the recording is opened:
     *      their frames, tracks and timestamp.
     */
    private array $audioOnlyChunks = [];

    private LocalFile|WritableStream|null $outputFile = null;
    /** Directory to record into (folder mode), if any. */
    private ?string $outputDir = null;
    private ?RecordingFormat $outputFormat = null;
    private ?OggWriter $ogg = null;
    private ?MatroskaWriter $mkv = null;
    /** @var array<int, string> The codecs of the mixed stream's open recording, by {@see CallStream} flag. */
    private array $mainCodecs = [];
    private ?WritableStream $out = null;
    /** Milliseconds of audio written so far, for Matroska timestamps. */
    private int $writtenMs = 0;
    /** The timestamp (ms) of the chunk at which the current recording started, for video timestamps. */
    private ?int $recordingStart = null;
    /** Whether the main Matroska recording got its video track before starting. */
    private bool $mainHasVideo = false;
    /**
     * The publishers of an automatically-scaled livestream, from the audio chunks' metadata: the video
     * channel of each (its index plus one) and whether it is currently active.
     *
     * @var array<int, array{endpoint: string, active: bool}>
     */
    private array $publishers = [];
    /**
     * Per-publisher video recorders in folder mode, by video channel.
     *
     * @var array<int, array{endpoint: string, writer: MatroskaWriter, out: WritableStream, start: int, started: bool, file: string, codec: string}>
     */
    private array $videoWriters = [];
    private bool $unsupportedLogged = false;
    /** The current interval between fetches of the stream channels, see loop(). */
    private float $channelsRetry = self::CHANNELS_RETRY;

    /**
     * @psalm-mutation-free
     */
    public function __construct(
        private readonly GroupCallController $call,
    ) {
    }

    /**
     * Start (or restart) following the stream.
     */
    public function start(bool $rtmp): void
    {
        $this->rtmp = $rtmp;
        if ($this->running) {
            return;
        }
        $this->running = true;
        $generation = ++$this->generation;
        EventLoop::queue(function () use ($generation): void {
            try {
                $this->loop($generation);
            } catch (Throwable $e) {
                $this->call->log("The stream receiver of {$this->call} died: $e", Logger::ERROR);
            } finally {
                if ($generation === $this->generation) {
                    $this->running = false;
                }
            }
        });
    }

    /**
     * Stop following the stream and finish the recording.
     */
    public function stop(): void
    {
        $this->running = false;
        $this->generation++;
        $this->closeWriters();
    }

    /**
     * Record the stream's audio into a file or stream. The format is autodetected from the extension
     * of a {@see LocalFile}; a raw stream defaults to OGG OPUS.
     */
    public function setOutput(LocalFile|WritableStream $file, ?RecordingFormat $format = null): void
    {
        $this->closeWriters();
        $this->outputDir = null;
        $this->outputFile = $file;
        $this->outputFormat = $format ?? ($file instanceof LocalFile ? RecordingFormat::fromFile($file) : RecordingFormat::Mkv);
    }

    /**
     * Record into a directory: the mixed stream (audio, plus video for an RTMP livestream) as
     * `stream.ogg` (or `.mkv`/`.webm` for a Matroska `$format`, the default for RTMP), and, in an
     * automatically-scaled livestream, each publisher's video as `video-<endpoint>.mkv`.
     */
    public function setOutputDirectory(LocalDirectory $directory, ?RecordingFormat $format = null): void
    {
        $dir = $directory->create();
        $format ??= RecordingFormat::Mkv;
        $extension = match ($format) {
            RecordingFormat::Opus => 'ogg',
            RecordingFormat::Webm => 'webm',
            RecordingFormat::Mkv => 'mkv',
        };
        $this->setOutput(new LocalFile("$dir/stream.$extension"), $format);
        $this->outputDir = $dir;
    }

    private function loop(int $generation): void
    {
        while ($this->running && $generation === $this->generation) {
            if ($this->nextTimestamp === null) {
                try {
                    $edge = $this->liveEdge();
                } catch (RateLimitError $e) {
                    // Polled faster than the server allows (FLOOD_WAIT_3 while the livestream has no media): wait
                    // as long as it asks, and poll at that pace from now on, until the stream starts.
                    $this->channelsRetry = max($this->channelsRetry, (float) $e->getWaitTime());
                    $this->call->log("Waiting {$e->getWaitTime()} seconds to fetch the stream channels of {$this->call} again", Logger::ULTRA_VERBOSE);
                    delay((float) $e->getWaitTime());
                    continue;
                } catch (Throwable $e) {
                    $this->call->log("Could not fetch the stream channels of {$this->call}: $e", Logger::WARNING);
                    $edge = null;
                }
                if ($edge === null) {
                    // No usable timestamp yet: the channels are empty while the publisher of the livestream sends
                    // no media, see https://core.telegram.org/api/group-calls (tgcalls retries after a second).
                    delay($this->channelsRetry);
                    continue;
                }
                $this->channelsRetry = self::CHANNELS_RETRY;
                // The live edge may not have moved since the last chunk (e.g. a resynchronization
                // after the next chunk was not ready yet): never consume a chunk twice, unless the
                // edge is far behind it (the timeline restarted).
                if ($this->lastTimestamp !== null && $edge <= $this->lastTimestamp && $this->lastTimestamp - $edge <= self::BUFFER_MS + 2 * self::SEGMENT_MS) {
                    $edge = $this->lastTimestamp + self::SEGMENT_MS;
                }
                $this->nextTimestamp = $edge;
            }
            $timestamp = $this->nextTimestamp;
            // Stay behind the live edge: never ask for a chunk that cannot be complete yet. The
            // timeline of an RTMP stream starts with the stream, not the server clock: its chunks
            // that are not ready yet fail with TIME_TOO_BIG instead.
            $due = $timestamp + self::SEGMENT_MS + self::BUFFER_MS;
            $now = self::nowMs();
            if (!$this->rtmp && $due > $now) {
                delay(($due - $now) / 1000);
                continue;
            }
            $chunk = null;
            try {
                $chunk = $this->fetch($timestamp);
            } catch (RPCErrorException $e) {
                if ($e->rpc === 'GROUPCALL_JOIN_MISSING' || $e->rpc === 'GROUPCALL_FORBIDDEN') {
                    $this->call->log("Not in {$this->call} any more ({$e->rpc}), rejoining...", Logger::WARNING);
                    $this->running = false;
                    $this->call->rejoin();
                    return;
                }
                if ($e->rpc === 'TIME_TOO_BIG' || $e instanceof RateLimitError) {
                    delay(self::RETRY_MS / 1000);
                    continue;
                }
                $this->call->log("Resynchronizing the stream of {$this->call} after {$e->rpc}", Logger::WARNING);
                $this->nextTimestamp = null;
                delay(self::RETRY_MS / 1000);
                continue;
            } catch (Throwable $e) {
                $this->call->log("Could not download a chunk of {$this->call}: $e", Logger::WARNING);
                delay(1.0);
                continue;
            }
            if ($chunk === null) {
                // A CDN redirect: the stream cannot be served from here, resynchronize.
                $this->nextTimestamp = null;
                delay(self::RETRY_MS / 1000);
                continue;
            }
            $this->call->log("Got the chunk $timestamp of {$this->call} (".\strlen($chunk).' bytes)', Logger::ULTRA_VERBOSE);
            $this->nextTimestamp = $timestamp + self::SEGMENT_MS;
            $this->lastTimestamp = $timestamp;
            try {
                $this->consume($chunk, $timestamp);
            } catch (Throwable $e) {
                $this->call->log("Could not demux a chunk of {$this->call}: $e", Logger::WARNING);
            }
            if (!$this->rtmp && $this->outputDir !== null) {
                $this->consumePublishersVideo($timestamp);
            }
        }
    }

    /**
     * The timestamp to start from, the live edge rounded down to a segment boundary minus the playback
     * buffer, see [stream mode](https://core.telegram.org/api/group-calls#stream-mode): of the unified
     * channel of an RTMP stream (null if it has no chunk yet: ask again in a second), else of the
     * server clock.
     */
    private function liveEdge(): ?int
    {
        if (!$this->rtmp) {
            return intdiv(self::nowMs(), self::SEGMENT_MS) * self::SEGMENT_MS - self::BUFFER_MS;
        }
        $edge = null;
        foreach ($this->call->API->getGroupCallStreamChannels($this->call->public->id) as $channel) {
            // The video of an RTMP stream starts a few seconds after its audio, on its unified channel
            // 1 (sometimes listed twice, with different edges: take the one that is surely ready).
            if ($channel['channel'] === 1) {
                $edge = min($edge ?? PHP_INT_MAX, $channel['last_timestamp_ms']);
            }
        }
        if ($edge === null) {
            return null;
        }
        $start = intdiv($edge, self::SEGMENT_MS) * self::SEGMENT_MS - self::BUFFER_MS;
        return $start > 0 ? $start : null;
    }

    /**
     * The local clock in milliseconds.
     */
    private static function nowMs(): int
    {
        return (int) round(microtime(true) * 1000.0);
    }

    /**
     * Download one chunk, or null on a CDN redirect.
     */
    private function fetch(int $timestamp): ?string
    {
        return $this->call->API->downloadGroupCallStreamChunk(
            $this->call->public->id,
            $timestamp,
            0,
            // An RTMP stream is a single unified channel carrying both audio and video.
            $this->rtmp ? 1 : null,
            $this->rtmp ? 2 : null,
        );
    }

    /**
     * Download one publisher's video chunk of an automatically-scaled livestream, or null if it is
     * not available.
     */
    private function fetchVideo(int $timestamp, int $channel): ?string
    {
        try {
            return $this->call->API->downloadGroupCallStreamChunk($this->call->public->id, $timestamp, 0, $channel, 2);
        } catch (Throwable $e) {
            $this->call->log("Could not download video chunk $channel of {$this->call}: $e", Logger::VERBOSE);
            return null;
        }
    }

    private function consume(string $chunk, int $timestamp): void
    {
        $container = 'ogg';
        if ($this->rtmp) {
            $unified = self::parseUnifiedHeader($chunk);
            if ($unified === null) {
                throw new \RuntimeException('Malformed unified chunk header');
            }
            [$container, $chunk] = $unified;
        }
        $this->recordingStart ??= $timestamp;
        if ($container === 'ogg') {
            $this->consumeOgg($chunk);
            return;
        }
        if ($container === 'matroska' || $container === 'webm') {
            $matroska = new Matroska(new ReadableBuffer($chunk));
            $this->consumeFrames($matroska->frames, $matroska->tracks, $timestamp);
            return;
        }
        if ($container === 'mp4' || $container === 'mov' || $container === 'isom') {
            $mp4 = new Mp4(new ReadableBuffer($chunk));
            $this->consumeFrames($mp4->frames, $mp4->tracks, $timestamp);
            return;
        }
        if (!$this->unsupportedLogged) {
            $this->unsupportedLogged = true;
            $this->call->log("The stream of {$this->call} uses the \"$container\" container, which cannot be demuxed: nothing will be recorded.", Logger::WARNING);
        }
    }

    /**
     * Fetch and record the video of every active publisher of an automatically-scaled livestream,
     * each into its own `video-<endpoint>.mkv` in the recording directory.
     */
    private function consumePublishersVideo(int $timestamp): void
    {
        foreach ($this->publishers as $channel => ['endpoint' => $endpoint, 'active' => $active]) {
            if (!$active) {
                continue;
            }
            $chunk = $this->fetchVideo($timestamp, $channel);
            if ($chunk === null) {
                continue;
            }
            try {
                $unified = self::parseUnifiedHeader($chunk);
                if ($unified === null) {
                    continue;
                }
                [$container, $data] = $unified;
                if ($container === 'mp4' || $container === 'mov' || $container === 'isom') {
                    $demuxer = new Mp4(new ReadableBuffer($data));
                } elseif ($container === 'matroska' || $container === 'webm') {
                    $demuxer = new Matroska(new ReadableBuffer($data));
                } else {
                    continue;
                }
                $this->writePublisherVideo($channel, $endpoint, $demuxer->frames, $demuxer->tracks, $timestamp);
            } catch (Throwable $e) {
                $this->call->log("Could not demux the video of $endpoint in {$this->call}: $e", Logger::WARNING);
            }
        }
    }

    /**
     * Strip the header of a unified chunk: `signature:int32 container:string active_mask:int32
     * events:int32 (offset:int32 endpoint:string rotation:int32 extra:int32)*`, strings being
     * TL-style (length byte, or 254 + 3-byte length, then padding to 4 bytes).
     *
     * @return array{0: string, 1: string}|null The container name and the container data.
     */
    private static function parseUnifiedHeader(string $data): ?array
    {
        $offset = 0;
        $int = static function () use ($data, &$offset): ?int {
            /** @var int $offset */
            if ($offset + 4 > \strlen($data)) {
                return null;
            }
            $value = (int) unpack('l', substr($data, $offset, 4))[1];
            $offset += 4;
            return $value;
        };
        $string = static function () use ($data, &$offset): ?string {
            /** @var int $offset */
            if ($offset >= \strlen($data)) {
                return null;
            }
            $first = \ord($data[$offset]);
            if ($first === 254) {
                $length = (int) unpack('V', substr($data, $offset + 1, 3)."\0")[1];
                $offset += 4;
                $padding = (4 - $length % 4) % 4;
            } else {
                $length = $first;
                $offset += 1;
                $padding = (4 - ($length + 1) % 4) % 4;
            }
            if ($offset + $length > \strlen($data)) {
                return null;
            }
            $value = substr($data, $offset, $length);
            $offset += $length + $padding;
            return $value;
        };
        // Unsigned: unpack('l') reads the signature as a negative number.
        if ((($int() ?? 0) & 0xFFFFFFFF) !== self::UNIFIED_SIGNATURE) {
            return null;
        }
        $container = $string();
        if ($container === null || $int() === null) { // active mask
            return null;
        }
        $events = $int();
        if ($events === null) {
            return null;
        }
        for ($i = 0; $i < $events; $i++) {
            if ($int() === null || $string() === null || $int() === null || $int() === null) {
                return null;
            }
        }
        return [$container, substr($data, $offset)];
    }

    private function consumeOgg(string $chunk): void
    {
        foreach (OggDemuxer::demux($chunk) as $stream) {
            if ($stream['packets'] === []) {
                continue;
            }
            if ($stream['tags'] !== null) {
                $this->updatePublishers(OggDemuxer::opusComments($stream['tags']));
            }
            $this->ensureWriters($stream['head'] ?? OggDemuxer::opusHead(2), 'A_OPUS', null);
            foreach ($stream['packets'] as $packet) {
                $this->writeAudio($packet['data'], $packet['samples']);
            }
            // A chunk carries one mixed audio stream; any further logical stream would be a duplicate.
            return;
        }
    }

    /**
     * Track the publishers an audio chunk announces: `ENDPOINTS` lists them (their index plus one is
     * their video channel), `ACTIVE_MASK` which of them are transmitting video.
     *
     * @param array<string, string> $comments
     */
    private function updatePublishers(array $comments): void
    {
        if (!isset($comments['ENDPOINTS'])) {
            return;
        }
        $endpoints = array_values(array_filter(explode(' ', $comments['ENDPOINTS']), static fn (string $e): bool => $e !== ''));
        $mask = (int) ($comments['ACTIVE_MASK'] ?? 0);
        $publishers = [];
        foreach ($endpoints as $index => $endpoint) {
            $publishers[$index + 1] = ['endpoint' => $endpoint, 'active' => ($mask & (1 << $index)) !== 0];
        }
        $this->publishers = $publishers;
    }

    /**
     * Record the audio (and, into a Matroska file, the video) frames of a demuxed unified chunk.
     *
     * @param iterable<array{track: int, codec: string, type: int, data: string, timestamp: int, keyframe: bool}> $frames
     * @param array<int, array<string, mixed>> $tracks
     */
    private function consumeFrames(iterable $frames, array $tracks, int $timestamp): void
    {
        if ($this->ogg === null && $this->mkv === null && $this->outputFile !== null) {
            // The recording can't get a video track once opened, and the first chunks of an RTMP
            // stream often have none: hold them for a while, waiting for the video.
            if ($this->rtmp && self::videoTrack($tracks) === null && \count($this->audioOnlyChunks) < self::MAX_AUDIO_ONLY_CHUNKS) {
                $this->audioOnlyChunks[] = [iterator_to_array($frames, false), $tracks, $timestamp];
                return;
            }
            $this->openWriters($tracks);
            $held = $this->audioOnlyChunks;
            $this->audioOnlyChunks = [];
            foreach ($held as [$heldFrames, $heldTracks, $heldTimestamp]) {
                $this->writeFrames($heldFrames, $heldTracks, $heldTimestamp);
            }
        }
        $this->writeFrames($frames, $tracks, $timestamp);
    }

    /**
     * Open the main recording, from the tracks of a chunk.
     *
     * @param array<int, array<string, mixed>> $tracks
     */
    private function openWriters(array $tracks): void
    {
        foreach ($tracks as $track) {
            if (($track['type'] ?? 0) !== Matroska::TRACK_TYPE_AUDIO) {
                continue;
            }
            $private = isset($track['private']) && \is_string($track['private']) ? $track['private'] : '';
            if ($track['codec'] === 'A_OPUS') {
                $this->ensureWriters(str_starts_with($private, 'OpusHead') ? $private : OggDemuxer::opusHead(2), 'A_OPUS', self::videoTrack($tracks));
            } elseif ($track['codec'] === 'A_AAC' && ($this->outputFormat ?? RecordingFormat::Opus)->isMatroska()) {
                $this->ensureWriters($private, 'A_AAC', self::videoTrack($tracks), (int) ($track['rate'] ?? 48000), (int) ($track['channels'] ?? 2));
            }
            return;
        }
    }

    /**
     * @param iterable<array{track: int, codec: string, type: int, data: string, timestamp: int, keyframe: bool}> $frames
     * @param array<int, array<string, mixed>> $tracks
     */
    private function writeFrames(iterable $frames, array $tracks, int $timestamp): void
    {
        $chunkBase = null;
        foreach ($frames as $frame) {
            $chunkBase ??= $frame['timestamp'];
            if ($frame['type'] === Matroska::TRACK_TYPE_AUDIO) {
                if ($frame['codec'] === 'A_OPUS') {
                    $this->writeAudio($frame['data'], OggDemuxer::opusSamples($frame['data']));
                } elseif ($frame['codec'] === 'A_AAC' && ($this->outputFormat ?? RecordingFormat::Opus)->isMatroska()) {
                    $this->writeAudioAt($frame['data'], $timestamp - ($this->recordingStart ?? $timestamp) + ($frame['timestamp'] - $chunkBase));
                }
                continue;
            }
            if ($frame['type'] === Matroska::TRACK_TYPE_VIDEO && $this->mkv !== null && $this->mainHasVideo) {
                $this->mkv->writeVideo($frame['data'], $timestamp - ($this->recordingStart ?? $timestamp) + ($frame['timestamp'] - $chunkBase), $frame['keyframe']);
            }
        }
    }

    /**
     * The first video track of a chunk, if any.
     *
     * @param array<int, array<string, mixed>> $tracks
     *
     * @return array<string, mixed>|null
     *
     * @psalm-pure
     */
    private static function videoTrack(array $tracks): ?array
    {
        foreach ($tracks as $track) {
            if (($track['type'] ?? 0) === Matroska::TRACK_TYPE_VIDEO) {
                return $track;
            }
        }
        return null;
    }

    /**
     * Open the main recording on the first frame: OGG OPUS, or Matroska with an audio track and, for
     * a unified (RTMP) stream, its video track.
     *
     * @param array<string, mixed>|null $video
     */
    private function ensureWriters(string $audioPrivate, string $audioCodec, ?array $video, int $rate = 48000, int $channels = 0): void
    {
        if ($this->outputFile === null || $this->ogg !== null || $this->mkv !== null) {
            return;
        }
        if ($audioCodec === 'A_OPUS') {
            $rate = 48000;
            $channels = OggDemuxer::opusChannels($audioPrivate);
        }
        $this->out = $this->outputFile instanceof LocalFile ? openFile($this->outputFile->file, 'w') : $this->outputFile;
        $this->writtenMs = 0;
        $format = $this->outputFormat ?? RecordingFormat::Opus;
        if ($format->isMatroska()) {
            $this->mkv = new MatroskaWriter($this->out, $format->docType());
            $this->mkv->setAudioTrack($audioCodec, $rate, max(1, $channels), $audioPrivate);
            $this->mainCodecs = [CallStream::AUDIO => $audioCodec];
            if ($video !== null && (int) ($video['width'] ?? 0) > 0) {
                $this->mkv->setVideoTrack((string) $video['codec'], (int) $video['width'], (int) ($video['height'] ?? 0), (string) ($video['private'] ?? ''));
                $this->mainHasVideo = true;
                $this->mainCodecs[CallStream::VIDEO] = (string) $video['codec'];
            }
            $this->mkv->start();
            $this->reportMain(RecordingEvent::Started);
            return;
        }
        if ($audioCodec !== 'A_OPUS') {
            throw new \RuntimeException("Only OPUS audio can be recorded to OGG; record this $audioCodec stream to a Matroska file instead.");
        }
        $this->ogg = new OggWriter($this->out);
        $this->ogg->writeHeader($channels, 48000, 'livestream', $audioPrivate);
        $this->mainCodecs = [CallStream::AUDIO => $audioCodec];
        $this->reportMain(RecordingEvent::Started);
    }

    /**
     * Report the mixed stream's recording starting or ending as a {@see \danog\MadelineProto\EventHandler\Calls\CallStreams}
     * update of participant 0 (there are no per-participant sources in stream mode).
     */
    private function reportMain(RecordingEvent $event): void
    {
        $streams = CallStream::AUDIO | ($this->mainHasVideo ? CallStream::VIDEO : 0);
        $this->call->onParticipantStreams(0, $streams, $this->mainCodecs, $event, $this->outputFile instanceof LocalFile ? $this->outputFile : null);
    }

    private function writeAudio(string $packet, int $samples): void
    {
        if ($this->ogg !== null) {
            $this->ogg->writeChunk($packet, $samples, false);
        } elseif ($this->mkv !== null) {
            $this->mkv->writeAudio($packet, $this->writtenMs);
        }
        $this->writtenMs += intdiv($samples, 48);
    }

    private function writeAudioAt(string $packet, int $timestampMs): void
    {
        $this->mkv?->writeAudio($packet, max(0, $timestampMs));
    }

    /**
     * Record one publisher's video chunk into its own Matroska file, opened on its first chunk (and
     * reopened if the endpoint behind the channel changes).
     *
     * @param iterable<array{track: int, codec: string, type: int, data: string, timestamp: int, keyframe: bool}> $frames
     * @param array<int, array<string, mixed>> $tracks
     */
    private function writePublisherVideo(int $channel, string $endpoint, iterable $frames, array $tracks, int $timestamp): void
    {
        $dir = $this->outputDir;
        if ($dir === null) {
            return;
        }
        $writer = $this->videoWriters[$channel] ?? null;
        if ($writer !== null && $writer['endpoint'] !== $endpoint) {
            $this->closeVideoWriter($channel);
            $writer = null;
        }
        $chunkBase = null;
        foreach ($frames as $frame) {
            $chunkBase ??= $frame['timestamp'];
            if ($frame['type'] !== Matroska::TRACK_TYPE_VIDEO) {
                continue;
            }
            if ($writer === null) {
                $track = $tracks[$frame['track']] ?? null;
                if ($track === null || (int) ($track['width'] ?? 0) <= 0) {
                    return;
                }
                $file = $dir.'/video-'.preg_replace('/[^A-Za-z0-9_.-]/', '_', $endpoint).'.mkv';
                $out = openFile($file, 'w');
                $mkv = new MatroskaWriter($out, RecordingFormat::Mkv->docType());
                $mkv->setVideoTrack((string) $track['codec'], (int) $track['width'], (int) ($track['height'] ?? 0), (string) ($track['private'] ?? ''));
                $mkv->start();
                $writer = ['endpoint' => $endpoint, 'writer' => $mkv, 'out' => $out, 'start' => $timestamp, 'started' => true, 'file' => $file, 'codec' => (string) $track['codec']];
                $this->videoWriters[$channel] = $writer;
                $this->call->onParticipantStreams(0, CallStream::VIDEO, [CallStream::VIDEO => $writer['codec']], RecordingEvent::Started, new LocalFile($file));
            }
            $writer['writer']->writeVideo($frame['data'], $timestamp - $writer['start'] + ($frame['timestamp'] - $chunkBase), $frame['keyframe']);
        }
    }

    private function closeVideoWriter(int $channel): void
    {
        $writer = $this->videoWriters[$channel] ?? null;
        if ($writer === null) {
            return;
        }
        unset($this->videoWriters[$channel]);
        try {
            $writer['writer']->close();
        } catch (Throwable $e) {
            $this->call->log("Could not close the video recording of {$writer['endpoint']} in {$this->call}: $e", Logger::WARNING);
        }
        $this->call->onParticipantStreams(0, CallStream::VIDEO, [CallStream::VIDEO => $writer['codec']], RecordingEvent::Ended, new LocalFile($writer['file']));
    }

    private function closeWriters(): void
    {
        $open = $this->ogg !== null || $this->mkv !== null;
        try {
            if ($this->ogg !== null) {
                $this->ogg->writeChunk('', 0, true);
            } elseif ($this->mkv !== null) {
                $this->mkv->close();
            } elseif ($this->out !== null && $this->outputFile instanceof LocalFile) {
                $this->out->close();
            }
        } catch (Throwable $e) {
            $this->call->log("Could not close the stream recording of {$this->call}: $e", Logger::WARNING);
        }
        $this->ogg = null;
        $this->mkv = null;
        $this->out = null;
        if ($open) {
            $this->reportMain(RecordingEvent::Ended);
        }
        $this->mainHasVideo = false;
        $this->mainCodecs = [];
        $this->recordingStart = null;
        $this->audioOnlyChunks = [];
        foreach (array_keys($this->videoWriters) as $channel) {
            $this->closeVideoWriter($channel);
        }
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

// IMPORTANT NOTE: Please keep the above copyright notice intact if copying or rewriting this file in another language.

namespace danog\MadelineProto\GroupCall;

use Amp\ByteStream\WritableStream;
use Amp\Sync\LocalMutex;
use danog\DialogId\DialogId;
use danog\MadelineProto\CallStream;
use danog\MadelineProto\EventHandler\Call;
use danog\MadelineProto\EventHandler\Calls\AbstractGroupCall;
use danog\MadelineProto\EventHandler\Calls\AbstractGroupCallParticipant;
use danog\MadelineProto\EventHandler\Calls\GroupCall;
use danog\MadelineProto\EventHandler\Calls\GroupCallDonor;
use danog\MadelineProto\EventHandler\Calls\GroupCallParticipant;
use danog\MadelineProto\EventHandler\Calls\GroupCallStars;
use danog\MadelineProto\EventHandler\Calls\GroupCallState;
use danog\MadelineProto\EventHandler\Calls\LiveStory;
use danog\MadelineProto\EventHandler\Calls\LiveStoryParticipant;
use danog\MadelineProto\Exception;
use danog\MadelineProto\GroupCall\Rtmp\RtmpPublisher;
use danog\MadelineProto\LocalDirectory;
use danog\MadelineProto\LocalFile;
use danog\MadelineProto\Logger;
use danog\MadelineProto\Loop\VoIP\DjLoop;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\ParseMode;
use danog\MadelineProto\RecordingFormat;
use danog\MadelineProto\RestoresDeclaredProperties;
use danog\MadelineProto\RPCErrorException;
use danog\MadelineProto\TextEntities;
use danog\MadelineProto\Tgcalls\CallControllerInterface;
use danog\MadelineProto\Tgcalls\GroupConnection;
use danog\MadelineProto\Tgcalls\GroupConnectionOwner;
use danog\MadelineProto\Tgcalls\GroupMediaTrait;
use danog\MadelineProto\Tgcalls\GroupSdp;
use danog\MadelineProto\Tools;
use Revolt\EventLoop;
use Throwable;
use Webmozart\Assert\Assert;

/**
 * Manages a single non-E2E group call (video chat, livestream or live story).
 *
 * See https://core.telegram.org/api/group-calls for the protocol description.
 *
 * @psalm-import-type StreamMask from CallStream
 * @internal
 */
final class GroupCallController implements CallControllerInterface, GroupConnectionOwner
{
    use RestoresDeclaredProperties;
    use GroupMediaTrait;

    /**
     * How often [phone.checkGroupCall](https://core.telegram.org/method/phone.checkGroupCall) is polled while
     * joined, as TDLib does: the WebRTC connection can't tell whether we're still in the call, as the SFU keeps
     * the endpoint of a call that ended (or that dropped us) for as long as we send it media.
     */
    private const CHECK_INTERVAL = 10.0;
    /** How long a failed rejoin waits before trying again, at first and at most (doubling in between). */
    private const REJOIN_RETRY_MIN = 5.0;
    private const REJOIN_RETRY_MAX = 60.0;
    /** How long we wait for a missing `version` before refetching the whole call. */
    private const VERSION_GAP_TIMEOUT = 1.0;
    /** How many participants are fetched per page, and at most, when (re)loading the participant list. */
    private const PARTICIPANTS_PAGE = 100;
    private const PARTICIPANTS_MAX = 5000;

    private GroupCallState $callState = GroupCallState::NOT_JOINED;

    /** The [inputGroupCall](https://core.telegram.org/constructor/inputGroupCall) of this call. */
    private array $inputCall;
    /** The last known [groupCall](https://core.telegram.org/constructor/groupCall). */
    private array $call;

    private LocalMutex $joinMutex;

    /** @var array<int, AbstractGroupCallParticipant> Participants indexed by their bot API peer ID. */
    private array $participants = [];
    /** @var array<int, int> Signed source ID => bot API peer ID. */
    private array $sourceToPeer = [];

    /** Our own audio source ID, in the signed form used by the API. */
    private int $source = 0;
    private bool $streamMode = false;
    private bool $rtmpMode = false;
    /** Downloads and records the call's media chunks in stream mode. */
    private ?StreamReceiver $streamReceiver = null;
    /** Publishes what we play to the RTMP livestream, see {@see self::beforePlay()}. */
    private ?RtmpPublisher $rtmpPublisher = null;
    /** Whether we publish what we play to the RTMP livestream (as one of its admins). */
    private bool $publishing = false;
    /** What the WebRTC connection of a join of an RTMP livestream transmits: nothing, see {@see self::connectionDj()}. */
    private ?DjLoop $silentDj = null;
    /** Whether the SFU transport parameters of the current join were already applied. */
    private bool $connectionParamsApplied = false;

    private ?string $checkWatcher = null;
    /** Whether a {@see self::checkJoined()} is running. */
    private bool $checking = false;
    /**
     * Whether we were dropped from the call and could not rejoin it yet (e.g. the request timed out):
     * the rejoin is retried (see {@see self::rejoin()}), also after a restart of the process.
     */
    private bool $rejoining = false;
    /** How many rejoins failed in a row. */
    private int $rejoinFailures = 0;
    /** The timer of the next rejoin attempt. */
    private ?string $rejoinWatcher = null;
    private ?string $gapWatcher = null;

    /** @var array<int, int> Every signed source ever seen => bot API peer ID, to attribute the last events of a participant that left. */
    private array $sourcePeers = [];

    public readonly AbstractGroupCall $public;

    /**
     * @internal
     */
    public function __construct(
        public readonly MTProto $API,
        array $call,
        ?int $peerId = null,
        bool $liveStory = false,
    ) {
        $this->call = $call;
        $this->inputCall = [
            '_' => 'inputGroupCall',
            'id' => $call['id'],
            'access_hash' => $call['access_hash'],
        ];
        $this->public = $liveStory ? new LiveStory($API, $call, $peerId) : new GroupCall($API, $call, $peerId);
        // What is played to an RTMP livestream is published as an RTMP encoder would: H.264 and AAC only.
        $this->diskJockey = new DjLoop($this, rtmp: (bool) ($call['rtmp_stream'] ?? false));
        Assert::true($this->diskJockey->start());
        $this->joinMutex = new LocalMutex;
    }

    /** Whether the call was restored from a session, and is yet to be resumed by {@see self::resumeAfterRestart()}. */
    private bool $resumePending = false;

    /**
     * @psalm-mutation-free
     */
    public function __serialize(): array
    {
        $result = get_object_vars($this);
        // The WebRTC connection now serializes itself and resumes its SFU transport on wakeup, so it
        // is kept. The mutex and the two event-loop watcher IDs cannot survive and are recreated.
        unset($result['joinMutex'], $result['checkWatcher'], $result['checking'], $result['gapWatcher'], $result['rejoinWatcher'], $result['streamReceiver'], $result['rtmpPublisher'], $result['resumePending']);
        return $result;
    }

    public function __unserialize(array $data): void
    {
        $this->joinMutex = new LocalMutex;
        $this->checkWatcher = null;
        $this->checking = false;
        $this->gapWatcher = null;
        $this->rejoinWatcher = null;
        $this->streamReceiver = null;
        $this->rtmpPublisher = null;
        $this->restoreDeclaredProperties($data);
        if (!isset($this->API->logger)) {
            $this->API->setupLogger();
        }
        $this->diskJockey ??= new DjLoop($this);
        // Resumed by MTProto::wakeup() once the MTProto session is reset and connected, see resumeAfterRestart().
        $this->resumePending = true;
        if (!$this->isInUse()) {
            // Dropped by MTProto::wakeup() (see forgetIfUnjoined()): start nothing.
            return;
        }
        Assert::true($this->diskJockey->start());
        if ($this->presentationDj !== null) {
            Assert::true($this->presentationDj->start());
        }
        if ($this->silentDj !== null) {
            Assert::true($this->silentDj->start());
        }
    }

    /**
     * Whether we are in the call (or joining it, or trying to rejoin it), or publishing to its RTMP
     * livestream: the only group calls a session keeps across restarts.
     *
     * @psalm-mutation-free
     */
    private function isInUse(): bool
    {
        return $this->callState === GroupCallState::JOINED
            || $this->callState === GroupCallState::JOINING
            || $this->rejoining
            || $this->publishing;
    }

    /**
     * Stop tracking the call if we are not in it, without making any request, see
     * {@see \danog\MadelineProto\GroupCall\Handler::dropUnjoinedGroupCalls()}.
     *
     * @internal
     *
     * @return bool Whether the call is no longer tracked.
     */
    public function forgetIfUnjoined(): bool
    {
        if ($this->isInUse()) {
            return false;
        }
        $this->log("Not resuming $this after a restart of the process: we were not in it (state {$this->callState->name}).", Logger::NOTICE);
        $this->cleanup();
        $this->API->cleanupGroupCall($this->public->id);
        return true;
    }

    /**
     * Resume the call after the process restarted.
     *
     * Called by {@see MTProto::wakeup()} once the MTProto session is reset and connected, never from
     * __unserialize(): resuming makes requests, and those sent before the session is reset are lost.
     *
     * @internal
     */
    public function resumeAfterRestart(): void
    {
        if (!$this->resumePending) {
            return;
        }
        $this->resumePending = false;
        if ($this->publishing) {
            // Publishing does not depend on being joined (as a viewer).
            $this->log("Resumed publishing to $this after a restart of the process.");
            $this->diskJockey->resumeReader();
            $this->rtmpPublisher = $this->createPublisher(null);
        }
        if ($this->callState === GroupCallState::JOINING) {
            // The process died in the middle of join(): its phone.joinGroupCall and the payload it
            // carried are gone with it, and join() itself is a no-op while JOINING, so start over.
            $this->log("Resumed $this in the middle of a join, joining again...", Logger::WARNING);
            // What was played resumes with the new join (see below for why the reader is restarted).
            $this->diskJockey->resumeReader();
            $this->rejoin();
            return;
        }
        if ($this->rejoining) {
            $this->log("Resumed $this, which we were trying to rejoin, rejoining...", Logger::WARNING);
            $this->diskJockey->resumeReader();
            $this->rejoin();
            return;
        }
        if ($this->callState !== GroupCallState::JOINED) {
            return;
        }
        // The WebRTC connection restored itself and its transport to the SFU resumes on its own;
        // now that the whole graph is back, resume the recorders (reopen their files, re-subscribe
        // to their live tracks) and re-poll the participant list so received sources are in sync.
        $this->log("Resumed $this after a restart of the process.");
        $this->startChecking();
        // Restart the demuxers/readers too (DjLoop::__unserialize() deliberately leaves them dormant),
        // or nothing is transmitted after a restart.
        $this->diskJockey->resumeReader();
        $this->presentationDj?->resumeReader();
        $this->connection?->resume();
        $this->presentationConnection?->resume();
        if ($this->streamMode) {
            // The stream receiver does not survive a restart (its recording does not either):
            // follow the stream again from the live edge.
            $this->streamReceiver = new StreamReceiver($this);
            $this->streamReceiver->start($this->rtmpMode);
        }
        EventLoop::queue($this->refetch(...));
    }

    /**
     * The [inputGroupCall](https://core.telegram.org/constructor/inputGroupCall) of this call.
     *
     * @internal
     */
    #[\Override]
    public function getInputCall(): array
    {
        return $this->inputCall;
    }

    /**
     * Join the group call.
     *
     * @param bool  $muted    Whether to join muted.
     * @param string|int|null $joinAs   Peer to join as, only for video chats/livestreams.
     * @param string|null $inviteHash Invite hash from a video chat invite link, if any.
     */
    public function join(bool $muted = false, string|int|null $joinAs = null, ?string $inviteHash = null): self
    {
        $lock = $this->joinMutex->acquire();
        try {
            if ($this->callState === GroupCallState::JOINED || $this->callState === GroupCallState::JOINING) {
                return $this;
            }
            $this->callState = GroupCallState::JOINING;
            $this->connectionParamsApplied = false;
            $this->muted = $muted;
            $this->log("Joining $this...", Logger::VERBOSE);

            $updates = null;
            for ($attempt = 0; $attempt < 5 && $updates === null; $attempt++) {
                $this->connection = $this->replaceConnection();
                $params = $this->connection->buildJoinPayload();
                $this->source = $this->connection->getAudioSource();
                $this->log("Join payload of $this: ".json_encode($params), Logger::VERBOSE);

                $request = [
                    'call' => $this->inputCall,
                    'join_as' => $joinAs ?? ['_' => 'inputPeerSelf'],
                    'muted' => $muted,
                    // Announce the video we are already playing (e.g. when re-joining after the transport
                    // failed): the other clients only request our video if the server says we send some.
                    'video_stopped' => $this->diskJockey->getVideoCodec() === null,
                    // DataJSON arguments are encoded by the TL serializer, pass the decoded payload.
                    'params' => $params,
                ];
                if ($inviteHash !== null) {
                    $request['invite_hash'] = $inviteHash;
                }
                try {
                    $updates = $this->API->methodCallAsyncRead('phone.joinGroupCall', $request);
                } catch (RPCErrorException $e) {
                    if ($e->rpc === 'GROUPCALL_SSRC_DUPLICATE_MUCH') {
                        // The server asks us to retry with a fresh SSRC.
                        $this->log("Retrying to join $this with a new SSRC...", Logger::WARNING);
                    } elseif ($e->getCode() === 500 && $attempt < 4) {
                        // Transient server-side failures (e.g. GROUPCALL_ADD_PARTICIPANTS_FAILED right
                        // after we were dropped from the call): back off and try again.
                        $this->log("Could not join $this ({$e->rpc}), retrying...", Logger::WARNING);
                        Tools::sleep(1.0 + (float) $attempt);
                    } else {
                        throw $e;
                    }
                }
            }
            if ($updates === null) {
                throw new Exception('Could not join the group call, the server kept rejecting our SSRC!');
            }
            foreach ($updates['updates'] as $update) {
                if ($update['_'] === 'updateGroupCallConnection' && !($update['presentation'] ?? false)) {
                    $this->applyConnectionParams($update['params']);
                }
            }
            $this->callState = GroupCallState::JOINED;
            $this->rejoining = false;
            $this->rejoinFailures = 0;
            $this->cancelRejoin();
            $this->log("Joined $this!", Logger::NOTICE);
            $this->startChecking();
            // A (re-)join always starts from a fresh WebRTC connection: hand it every recording that
            // was requested, so recordings resume (in new segments) instead of silently stopping.
            $this->rewireOutputs();
            EventLoop::queue($this->refetch(...));
            return $this;
        } catch (Throwable $e) {
            $this->callState = GroupCallState::NOT_JOINED;
            $this->connection?->close();
            $this->connection = null;
            throw $e;
        } finally {
            EventLoop::queue($lock->release(...));
        }
    }

    /**
     * Apply the `params` of an
     * [updateGroupCallConnection](https://core.telegram.org/constructor/updateGroupCallConnection),
     * already decoded by the TL deserializer.
     */
    private function applyConnectionParams(array $params): void
    {
        if ($this->connectionParamsApplied) {
            // Reset to false at the start of every join(): apply the first updateGroupCallConnection
            // that reaches us for this join and ignore any redelivery of it.
            return;
        }
        $this->connectionParamsApplied = true;
        // The stream key of an RTMP livestream lets anyone publish to it: keep it out of the logs.
        $this->log("Join response of $this: ".json_encode(isset($params['rtmp_stream_key']) ? ['rtmp_stream_key' => '<redacted>'] + $params : $params), Logger::VERBOSE);
        $parsed = GroupSdp::parseJoinResponse($params);
        $this->streamMode = $parsed['stream'];
        $this->rtmpMode = $parsed['rtmp'];
        if ($parsed['stream']) {
            // The server switched us to stream mode: there is no WebRTC session to set up, media is
            // downloaded in chunks instead. See https://core.telegram.org/api/group-calls#stream-mode.
            $this->log(
                "$this is in ".($parsed['rtmp'] ? 'RTMP' : 'stream')." mode: media is received by downloading chunks, and cannot be transmitted.",
                Logger::NOTICE
            );
            $this->connection?->close();
            $this->connection = null;
            $this->streamReceiver ??= new StreamReceiver($this);
            $this->streamReceiver->start($parsed['rtmp']);
            return;
        }
        if ($parsed['transport'] === null) {
            throw new Exception('Missing transport parameters in the group call join response!');
        }
        $this->connection?->setTransport($parsed['transport'], $parsed['video']);
    }

    /**
     * Fetch the full call info and participant list.
     *
     * @internal
     */
    public function refetch(): void
    {
        try {
            $result = $this->API->methodCallAsyncRead('phone.getGroupCall', [
                'call' => $this->inputCall,
                'limit' => self::PARTICIPANTS_PAGE,
            ]);
        } catch (Throwable $e) {
            $this->log("Could not refetch $this: $e", Logger::WARNING);
            return;
        }
        $this->applyGroupCall($result);
    }

    /**
     * Page through the rest of the participant list (phone.getGroupParticipants) after the first page
     * a phone.groupCall carries, up to a sane maximum.
     */
    private function fetchRemainingParticipants(string $offset): void
    {
        while ($offset !== '' && \count($this->participants) < self::PARTICIPANTS_MAX) {
            try {
                $page = $this->API->methodCallAsyncRead('phone.getGroupParticipants', [
                    'call' => $this->inputCall,
                    'ids' => [],
                    'sources' => [],
                    'offset' => $offset,
                    'limit' => self::PARTICIPANTS_PAGE,
                ]);
            } catch (Throwable $e) {
                $this->log("Could not fetch the participants of $this: $e", Logger::WARNING);
                return;
            }
            \assert(\is_array($page) && \is_array($page['participants']));
            foreach ($page['participants'] as $participant) {
                \assert(\is_array($participant));
                $this->applyParticipant($participant);
            }
            $offset = (string) ($page['next_offset'] ?? '');
            if ($page['participants'] === []) {
                return;
            }
        }
    }

    /**
     * A participant by their id, username or peer: from the cached list, or else looked up with
     * phone.getGroupParticipants. Null if they are not in the call.
     */
    public function getParticipant(string|int $participant): ?AbstractGroupCallParticipant
    {
        $peerId = $this->API->getId($participant);
        if (isset($this->participants[$peerId])) {
            return $this->participants[$peerId];
        }
        try {
            $page = $this->API->methodCallAsyncRead('phone.getGroupParticipants', [
                'call' => $this->inputCall,
                'ids' => [$peerId],
                'sources' => [],
                'offset' => '',
                'limit' => 1,
            ]);
        } catch (Throwable $e) {
            $this->log("Could not look up participant $peerId of $this: $e", Logger::WARNING);
            return null;
        }
        \assert(\is_array($page) && \is_array($page['participants']));
        foreach ($page['participants'] as $raw) {
            \assert(\is_array($raw));
            $this->applyParticipant($raw);
        }
        /** @var array<int, AbstractGroupCallParticipant> $participants */
        $participants = $this->participants;
        return $participants[$peerId] ?? null;
    }

    /**
     * Apply a [phone.groupCall](https://core.telegram.org/constructor/phone.groupCall).
     *
     * @internal
     */
    public function applyGroupCall(array $result): void
    {
        \assert(\is_array($result['call']));
        $this->call = $result['call'];
        $this->public->update($result['call']);
        if ($result['call']['_'] === 'groupCallDiscarded') {
            $this->onDiscarded();
            return;
        }
        $this->participants = [];
        $this->sourceToPeer = [];
        foreach ($result['participants'] as $participant) {
            $this->applyParticipant($participant);
        }
        $this->fetchRemainingParticipants((string) ($result['participants_next_offset'] ?? ''));
        $this->syncSources();
    }

    /**
     * Apply an [updateGroupCall](https://core.telegram.org/constructor/updateGroupCall).
     *
     * @internal
     */
    public function onGroupCallUpdate(array $call): void
    {
        if ($call['_'] === 'groupCallDiscarded') {
            $this->call = $call;
            $this->public->update($call);
            $this->onDiscarded();
            return;
        }
        $cached = $this->call['version'] ?? 0;
        $version = $call['version'] ?? 0;
        if ($version < $cached + 1) {
            return;
        }
        if ($version > $cached + 1) {
            $this->scheduleGapRefetch();
            return;
        }
        if (($call['min'] ?? false) && isset($this->call['version'])) {
            // A specific set of fields of a min groupCall cannot be applied over the cached one,
            // see https://core.telegram.org/constructor/groupCall.
            $call = array_merge($this->call, array_diff_key($call, array_flip([
                'join_muted', 'can_change_join_muted', 'schedule_start_subscribed', 'can_start_video',
                'creator', 'can_change_messages_enabled', 'unmuted_video_count', 'unmuted_video_limit',
                'stream_dc_id', 'invite_link', 'default_send_as', 'join_date_asc',
            ])));
        }
        $this->cancelGapRefetch();
        $this->call = $call;
        $this->public->update($call);
    }

    /**
     * Apply an [updateGroupCallParticipants](https://core.telegram.org/constructor/updateGroupCallParticipants).
     *
     * @internal
     */
    public function onParticipantsUpdate(array $participants, int $version): void
    {
        $versioned = false;
        foreach ($participants as $participant) {
            if (($participant['versioned'] ?? false)
                || ($participant['left'] ?? false)
                || ($participant['just_joined'] ?? false)
            ) {
                $versioned = true;
                break;
            }
        }
        if ($versioned) {
            $cached = $this->call['version'] ?? 0;
            if ($version < $cached) {
                return;
            }
            if ($version > $cached + 1) {
                $this->scheduleGapRefetch();
                return;
            }
            $this->cancelGapRefetch();
            $this->call['version'] = $version;
        }
        foreach ($participants as $participant) {
            $this->applyParticipant($participant);
        }
        $this->syncSources();
    }

    private function applyParticipant(array $participant): void
    {
        $peerId = $this->API->getIdInternal($participant['peer']);
        if ($peerId === null) {
            return;
        }
        if ($participant['left'] ?? false) {
            $old = $this->participants[$peerId] ?? null;
            if ($old !== null) {
                unset($this->sourceToPeer[$old->source], $this->participants[$peerId]);
            }
            if (($participant['self'] ?? false)
                && $this->callState === GroupCallState::JOINED
                && (int) ($participant['source'] ?? 0) === $this->source
            ) {
                // The server dropped our own source: we were removed from the call, rejoin (as
                // official clients do).
                $this->log("We were removed from $this, rejoining...", Logger::WARNING);
                EventLoop::queue($this->rejoin(...));
            }
            return;
        }
        $parsed = $this->public instanceof LiveStory
            ? LiveStoryParticipant::fromRaw($participant, $peerId, $this->participants[$peerId] ?? null)
            : GroupCallParticipant::fromRaw($participant, $peerId, $this->participants[$peerId] ?? null);
        $this->participants[$peerId] = $parsed;
        if ($parsed->source !== 0) {
            $this->sourceToPeer[$parsed->source] = $peerId;
            $this->sourcePeers[$parsed->source] = $peerId;
            if (isset($this->explicitOutputs[$peerId])) {
                $this->wireOutput($peerId);
            }
            // Folder mode: start recording a participant that has just begun transmitting.
            $this->wireFolderOutput($peerId);
        }
    }

    /**
     * Tell the WebRTC engine which participants we want to receive audio and video from.
     */
    private function syncSources(): void
    {
        if ($this->connection === null) {
            return;
        }
        $sources = [];
        foreach ($this->participants as $participant) {
            if ($participant->source !== 0 && $participant->source !== $this->source) {
                $sources[] = [
                    'audio' => $participant->source,
                    'muted' => $participant->muted,
                    'video' => $participant->videoSources,
                    'presentation' => $participant->presentationSources,
                    'videoEndpoint' => $participant->videoEndpoint,
                    'presentationEndpoint' => $participant->presentationEndpoint,
                ];
            }
        }
        $this->connection->setRemoteSources($sources);
    }

    private function scheduleGapRefetch(): void
    {
        if ($this->gapWatcher !== null) {
            return;
        }
        $this->gapWatcher = EventLoop::delay(self::VERSION_GAP_TIMEOUT, function (): void {
            $this->gapWatcher = null;
            $this->log("Filling a version gap in $this by refetching it.", Logger::VERBOSE);
            $this->refetch();
        });
    }

    private function cancelGapRefetch(): void
    {
        if ($this->gapWatcher !== null) {
            EventLoop::cancel($this->gapWatcher);
            $this->gapWatcher = null;
        }
    }

    /**
     * Called by the WebRTC engine when the connection is broken.
     *
     * @internal
     */
    #[\Override]
    public function onConnectionFailed(): void
    {
        if ($this->callState !== GroupCallState::JOINED) {
            return;
        }
        $this->log("The WebRTC connection of $this failed, checking whether we are still joined...", Logger::WARNING);
        EventLoop::queue($this->checkJoined(...));
    }

    /**
     * Check whether we're still in the call every {@see self::CHECK_INTERVAL} seconds, while joined.
     */
    private function startChecking(): void
    {
        if ($this->checkWatcher === null) {
            $this->checkWatcher = EventLoop::repeat(self::CHECK_INTERVAL, $this->checkJoined(...));
        }
    }

    /**
     * Check whether we're still in the call, like TDLib: rejoin it if the server dropped us, leave it if it
     * ended or we may not be in it anymore (we may have missed its groupCallDiscarded, e.g. if it ended while
     * the process was down), and just try again later on other errors.
     */
    private function checkJoined(): void
    {
        if ($this->callState !== GroupCallState::JOINED) {
            $this->stopChecking();
            return;
        }
        if ($this->checking) {
            return;
        }
        $this->checking = true;
        $sources = [$this->source];
        $presentationSource = $this->presentationConnection?->getAudioSource();
        if ($presentationSource !== null) {
            $sources[] = $presentationSource;
        }
        try {
            $alive = $this->API->methodCallAsyncRead('phone.checkGroupCall', [
                'call' => $this->inputCall,
                'sources' => $sources,
            ]);
        } catch (RPCErrorException $e) {
            if ($e->rpc === 'GROUPCALL_FORBIDDEN' || $e->rpc === 'GROUPCALL_INVALID') {
                $this->onEnded($e->rpc);
                return;
            }
            if ($e->rpc !== 'GROUPCALL_JOIN_MISSING') {
                $this->log("Could not check whether we are still in $this: $e", Logger::WARNING);
                return;
            }
            // The server dropped us, like when it leaves our source out of the result.
            $alive = [];
        } catch (Throwable $e) {
            $this->log("Could not check whether we are still in $this: $e", Logger::WARNING);
            return;
        } finally {
            $this->checking = false;
        }
        if ($this->callState !== GroupCallState::JOINED) {
            // Left, or rejoined, while checking.
            return;
        }
        if (\in_array($this->source, $alive, true)) {
            if ($presentationSource !== null && !\in_array($presentationSource, $alive, true)) {
                // Only the screen-share was dropped: rejoin just the presentation.
                $this->log("Our screen-share was dropped from $this, rejoining it...", Logger::WARNING);
                $this->disablePresentation();
                try {
                    $this->enablePresentation();
                } catch (Throwable $e) {
                    $this->log("Could not rejoin the presentation of $this: $e", Logger::ERROR);
                }
            }
            return;
        }
        $this->log("We were dropped from $this, rejoining...", Logger::WARNING);
        $this->rejoin();
    }

    /**
     * The call ended, or we may not be in it anymore (GROUPCALL_FORBIDDEN or GROUPCALL_INVALID): leave it, as
     * TDLib does.
     */
    private function onEnded(string $rpc): void
    {
        $this->log("$this ended, or we may not be in it anymore ($rpc): leaving it.", Logger::NOTICE);
        $this->cleanup();
    }

    /**
     * Join again from scratch with a new payload, keeping the playlists.
     *
     * A rejoin that fails for a reason that may not last (a timeout, a network or server error, a flood
     * wait) is retried with a backoff until it succeeds, or the call ends or is left: giving up would
     * leave us out of the call for good, tracked but not joined, and forgotten on the next restart. Other
     * errors (we may not join the call anymore, e.g. we were banned) leave it.
     *
     * @internal
     */
    public function rejoin(): void
    {
        $this->cancelRejoin();
        if ($this->callState === GroupCallState::LEFT) {
            // Left, or ended, while the rejoin was queued.
            return;
        }
        $this->stopChecking();
        $muted = $this->muted;
        $this->dropPresentation();
        // Recordings in progress carry on with the connection of the new join.
        $this->detachConnection();
        $this->callState = GroupCallState::NOT_JOINED;
        $this->rejoining = true;
        try {
            $this->join($muted);
            return;
        } catch (RPCErrorException $e) {
            if ($e->rpc === 'GROUPCALL_FORBIDDEN' || $e->rpc === 'GROUPCALL_INVALID') {
                $this->onEnded($e->rpc);
                return;
            }
            $error = $e;
            if ($e->getCode() >= 400 && $e->getCode() < 500 && $e->getCode() !== 420) {
                $this->log("Could not rejoin $this, leaving it: $e", Logger::ERROR);
                $this->cleanup();
                return;
            }
        } catch (Throwable $e) {
            $error = $e;
        }
        if ($this->callState !== GroupCallState::NOT_JOINED || !$this->rejoining) {
            // Joined or left meanwhile.
            return;
        }
        $delay = min(self::REJOIN_RETRY_MAX, self::REJOIN_RETRY_MIN * 2 ** $this->rejoinFailures);
        $this->rejoinFailures++;
        $this->log("Could not rejoin $this (attempt {$this->rejoinFailures}), retrying in $delay seconds: $error", Logger::ERROR);
        $this->rejoinWatcher = EventLoop::delay($delay, $this->retryRejoin(...));
    }

    private function retryRejoin(): void
    {
        $this->rejoinWatcher = null;
        if ($this->rejoining && $this->callState === GroupCallState::NOT_JOINED) {
            $this->rejoin();
        }
    }

    private function cancelRejoin(): void
    {
        if ($this->rejoinWatcher !== null) {
            EventLoop::cancel($this->rejoinWatcher);
            $this->rejoinWatcher = null;
        }
    }

    private function stopChecking(): void
    {
        if ($this->checkWatcher !== null) {
            EventLoop::cancel($this->checkWatcher);
            $this->checkWatcher = null;
        }
    }

    /**
     * Called by the WebRTC engine when audio starts flowing from a source.
     *
     * @internal
     */
    #[\Override]
    public function onIncomingSource(int $source): void
    {
        $this->log("Audio of source $source is now flowing in $this", Logger::VERBOSE);
    }

    /**
     * Leave the group call without ending it for the other participants.
     */
    public function leave(): self
    {
        if ($this->callState === GroupCallState::LEFT) {
            return $this;
        }
        if ($this->callState === GroupCallState::NOT_JOINED) {
            $this->log("Leaving $this, which we were not in".($this->rejoining ? ' (we were trying to rejoin it)' : '').self::requestedBy(), Logger::NOTICE);
            $this->cleanup();
            return $this;
        }
        $this->log("Leaving $this".self::requestedBy(), Logger::NOTICE);
        $source = $this->source;
        $this->cleanup();
        try {
            $this->API->methodCallAsyncRead('phone.leaveGroupCall', [
                'call' => $this->inputCall,
                'source' => $source,
            ]);
        } catch (Throwable $e) {
            $this->log("Could not leave $this: $e", Logger::WARNING);
        }
        return $this;
    }

    /**
     * End the group call for all participants.
     */
    public function discard(): self
    {
        $this->log("Ending $this for everyone".self::requestedBy(), Logger::NOTICE);
        $this->cleanup();
        try {
            $this->API->methodCallAsyncRead('phone.discardGroupCall', ['call' => $this->inputCall]);
        } catch (Throwable $e) {
            $this->log("Could not discard $this: $e", Logger::WARNING);
        }
        return $this;
    }

    /**
     * Where a leave or discard was requested from, for the logs: the first caller outside MadelineProto.
     */
    private static function requestedBy(): string
    {
        $src = \dirname(__DIR__);
        foreach (debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 16) as $frame) {
            if (isset($frame['file']) && !str_starts_with($frame['file'], $src)) {
                return ", as requested by {$frame['file']}:".($frame['line'] ?? '?');
            }
        }
        return '';
    }

    private function onDiscarded(): void
    {
        $this->log("$this was discarded!", Logger::NOTICE);
        $this->cleanup();
    }

    private function cleanup(): void
    {
        if ($this->callState === GroupCallState::LEFT) {
            return;
        }
        $this->callState = GroupCallState::LEFT;
        $this->rejoining = false;
        $this->cancelRejoin();
        $this->stopChecking();
        $this->cancelGapRefetch();
        $this->diskJockey->discard();
        $this->silentDj?->discard();
        $this->dropPresentation();
        $this->closeConnection();
        $this->streamReceiver?->stop();
        $this->streamReceiver = null;
        $this->rtmpPublisher?->stop();
        $this->rtmpPublisher = null;
        $this->publishing = false;
        $this->API->cleanupGroupCall($this->public->id);
    }

    /**
     * The disk jockey whose playback the WebRTC connection transmits. Joining an RTMP livestream
     * switches to stream mode, where the connection is closed, but its tracks must never take the
     * frames of the RTMP publisher meanwhile: they get a disk jockey that plays nothing.
     */
    private function connectionDj(): DjLoop
    {
        if (!($this->call['rtmp_stream'] ?? false)) {
            return $this->diskJockey;
        }
        if ($this->silentDj === null) {
            $this->silentDj = new DjLoop($this);
            Assert::true($this->silentDj->start());
        }
        return $this->silentDj;
    }

    /**
     * What is played to an RTMP livestream is published to it, as an RTMP encoder would: to the URL
     * and with the key of [phone.getGroupCallStreamRtmpUrl](https://core.telegram.org/method/phone.getGroupCallStreamRtmpUrl),
     * which only the admins of the group or channel can get. Joining the livestream only watches it.
     */
    private function beforePlay(): void
    {
        if (!($this->call['rtmp_stream'] ?? false)) {
            return;
        }
        if ($this->publishing) {
            // Reconnect right away, if the ingest dropped the connection while nothing was played.
            $this->rtmpPublisher?->wake();
            return;
        }
        if ($this->callState === GroupCallState::LEFT) {
            // Nothing would stop the publisher.
            throw new Exception("Cannot publish to $this: it was left");
        }
        if ($this->public->peerId === null) {
            throw new Exception("Cannot publish to $this: unknown group or channel");
        }
        // Set first: a concurrent play() must not start a second publisher.
        $this->publishing = true;
        try {
            $ingest = $this->API->getGroupCallStreamRtmpUrl($this->public->peerId, false, $this->public instanceof LiveStory);
        } catch (RPCErrorException $e) {
            $this->publishing = false;
            throw new Exception("Cannot publish to the RTMP livestream $this ({$e->rpc}): only the admins of a group or channel can", 0, $e);
        }
        if ($this->publishing && $this->rtmpPublisher === null) {
            $this->log("Publishing what is played to the RTMP livestream $this", Logger::NOTICE);
            $this->rtmpPublisher = $this->createPublisher($ingest);
        }
    }

    /**
     * @param ?array{url: string, key: string} $ingest Where to publish the first time, if already known.
     */
    private function createPublisher(?array $ingest): RtmpPublisher
    {
        $peer = $this->public->peerId;
        \assert($peer !== null);
        $liveStory = $this->public instanceof LiveStory;
        $publisher = null;
        $publisher = new RtmpPublisher($this, $this->diskJockey, function (bool $revoke) use (&$ingest, $peer, $liveStory): array {
            if ($ingest !== null && !$revoke) {
                $first = $ingest;
                $ingest = null;
                return $first;
            }
            return $this->API->getGroupCallStreamRtmpUrl($peer, $revoke, $liveStory);
        }, function () use (&$publisher): void {
            // The key was refused, or can't be fetched anymore: the next play() starts over, and
            // reports why it can't publish (e.g. the admin rights were lost).
            if ($this->rtmpPublisher === $publisher) {
                $this->rtmpPublisher = null;
                $this->publishing = false;
            }
        });
        return $publisher;
    }

    /**
     * Whether we publish what we play to the RTMP livestream (as one of its admins).
     */
    public function isRtmpPublisher(): bool
    {
        return $this->publishing;
    }

    /**
     * An RTMP livestream can't carry a screen share.
     */
    private function assertCanPresent(): void
    {
        if ($this->call['rtmp_stream'] ?? false) {
            throw new Exception("$this is an RTMP livestream, which has no screen sharing: play the screen as the video of its files, instead");
        }
    }

    /**
     * Whether the server switched us to chunk-download playback.
     */
    public function isStreamMode(): bool
    {
        return $this->streamMode;
    }

    /**
     * Whether all media of this call is published by a single external RTMP publisher.
     */
    public function isRtmpMode(): bool
    {
        return $this->rtmpMode;
    }

    /**
     * Record one participant's media into a single file (or stream) with a fixed set of tracks (see
     * {@see GroupMediaTrait::recordParticipant()}), or, in stream mode, the call's single mixed stream
     * (OGG OPUS is supported there only).
     *
     * @param ?StreamMask $streams The {@see CallStream} flags to record, or null for every available one.
     *
     * @return int The streams currently sent, as {@see CallStream} flags.
     */
    public function setOutput(LocalFile|WritableStream $file, string|int|null $participant = null, ?RecordingFormat $format = null, ?int $streams = null): int
    {
        if (!$this->streamMode) {
            return $this->recordParticipant($file, $participant, $format, $streams);
        }
        // A single mixed stream, downloaded in chunks: there are no per-participant sources.
        if ($participant !== null) {
            throw new \InvalidArgumentException('In stream mode the call is a single mixed stream: record it without specifying a participant.');
        }
        if ($streams !== null) {
            CallStream::validate($streams);
        }
        $available = CallStream::AUDIO | ($this->rtmpMode ? CallStream::VIDEO : 0);
        CallStream::checkAvailable($streams, $available, 'The stream');
        $this->streamReceiver ??= new StreamReceiver($this);
        $this->streamReceiver->setOutput($file, $format);
        return $available;
    }

    /**
     * Record the call into a directory, as numbered series of Matroska files, one per participant
     * (see {@see GroupMediaTrait::recordFolder()}); in stream mode the mixed stream is recorded as
     * `<dir>/stream.<ext>` (see {@see StreamReceiver::setOutputDirectory()}).
     */
    public function setOutputFolder(LocalDirectory $dir, string|int|null $participant = null, ?RecordingFormat $format = null): self
    {
        if (!$this->streamMode) {
            $this->recordFolder($dir, $participant, $format);
            return $this;
        }
        if ($participant !== null) {
            throw new \InvalidArgumentException('In stream mode the call is a single mixed stream: record it without specifying a participant.');
        }
        $this->streamReceiver ??= new StreamReceiver($this);
        $this->streamReceiver->setOutputDirectory($dir, $format);
        return $this;
    }

    /**
     * @psalm-mutation-free
     */
    #[\Override]
    private function callObject(): Call
    {
        return $this->public;
    }

    /**
     * @psalm-mutation-free
     */
    #[\Override]
    private function configureConnection(GroupConnection $connection, bool $screencast): void
    {
        // An ordinary group call trusts the SFU: nothing to set up.
    }

    #[\Override]
    private function onDroppedByServer(): void
    {
        EventLoop::queue($this->rejoin(...));
    }

    /**
     * @psalm-mutation-free
     */
    #[\Override]
    private function participantOf(int $peerId): ?AbstractGroupCallParticipant
    {
        return $this->participants[$peerId] ?? null;
    }

    /**
     * @psalm-mutation-free
     */
    #[\Override]
    private function isOurself(int $peerId, AbstractGroupCallParticipant $participant): bool
    {
        return $participant->self || $participant->source === $this->source;
    }

    /**
     * @psalm-mutation-free
     */
    #[\Override]
    private function peerOfSource(int $source): ?int
    {
        return $this->sourceToPeer[$source] ?? $this->sourcePeers[$source] ?? null;
    }

    /**
     * @return list<int>
     *
     * @psalm-mutation-free
     */
    #[\Override]
    private function knownPeers(): array
    {
        return array_keys($this->participants);
    }

    /**
     * Get all known participants, indexed by their bot API peer ID.
     *
     * @return array<int, AbstractGroupCallParticipant>
     */
    public function getParticipants(): array
    {
        return $this->participants;
    }

    /**
     * Invite users to the call.
     */
    public function invite(string|int ...$users): void
    {
        $this->API->methodCallAsyncRead('phone.inviteToGroupCall', [
            'call' => $this->inputCall,
            'users' => $users,
        ]);
    }

    /**
     * Export an invite link for the call.
     *
     * @param bool $canSelfUnmute Whether users joining with this link may speak without asking; admins only.
     */
    public function exportInvite(bool $canSelfUnmute = false): string
    {
        return $this->API->methodCallAsyncRead('phone.exportGroupCallInvite', [
            'call' => $this->inputCall,
            'can_self_unmute' => $canSelfUnmute,
        ])['link'];
    }

    /**
     * Change a participant's state with phone.editGroupCallParticipant: mute/unmute them (admins; a
     * non-admin mutes them only for themselves), set our playback volume of them, raise/lower our own
     * hand, or pause/resume our own video or screen-share.
     */
    public function editParticipant(string|int|array $participant, ?bool $muted = null, ?int $volume = null, ?bool $raiseHand = null, ?bool $videoStopped = null, ?bool $videoPaused = null, ?bool $presentationPaused = null): void
    {
        $params = ['call' => $this->inputCall, 'participant' => $participant];
        foreach (['muted' => $muted, 'volume' => $volume, 'raise_hand' => $raiseHand, 'video_stopped' => $videoStopped, 'video_paused' => $videoPaused, 'presentation_paused' => $presentationPaused] as $key => $value) {
            if ($value !== null) {
                $params[$key] = $value;
            }
        }
        if ($volume !== null && ($volume < 1 || $volume > 20000)) {
            throw new \InvalidArgumentException('The volume must be between 1 and 20000 (10000 = 100%).');
        }
        $this->API->methodCallAsyncRead('phone.editGroupCallParticipant', $params);
    }

    /**
     * Change the call's settings with phone.toggleGroupCallSettings (admins only).
     *
     * @param int|null $sendPaidMessagesStars Live stories: minimum donation to comment, 0 for none.
     */
    public function toggleSettings(?bool $joinMuted = null, bool $resetInviteHash = false, ?bool $messagesEnabled = null, ?int $sendPaidMessagesStars = null): void
    {
        $params = ['call' => $this->inputCall, 'reset_invite_hash' => $resetInviteHash];
        if ($joinMuted !== null) {
            $params['join_muted'] = $joinMuted;
        }
        if ($messagesEnabled !== null) {
            $params['messages_enabled'] = $messagesEnabled;
        }
        if ($sendPaidMessagesStars !== null) {
            $params['send_paid_messages_stars'] = $sendPaidMessagesStars;
        }
        $this->API->methodCallAsyncRead('phone.toggleGroupCallSettings', $params);
    }

    /**
     * Start or stop a server-side recording (phone.toggleGroupCallRecord, admins only).
     */
    public function toggleRecord(bool $start, ?string $title = null, bool $video = false, bool $portrait = false): void
    {
        $params = ['call' => $this->inputCall, 'start' => $start];
        if ($start) {
            if ($title !== null) {
                $params['title'] = $title;
            }
            if ($video) {
                $params['video'] = true;
                $params['video_portrait'] = $portrait;
            }
        }
        $this->API->methodCallAsyncRead('phone.toggleGroupCallRecord', $params);
    }

    /**
     * Start this scheduled call now (phone.startScheduledGroupCall, admins only).
     */
    public function startScheduled(): void
    {
        $this->API->methodCallAsyncRead('phone.startScheduledGroupCall', ['call' => $this->inputCall]);
    }

    /**
     * Subscribe to (or unsubscribe from) a notification when this scheduled call starts.
     */
    public function setStartSubscription(bool $subscribed): void
    {
        $this->API->methodCallAsyncRead('phone.toggleGroupCallStartSubscription', ['call' => $this->inputCall, 'subscribed' => $subscribed]);
    }

    /**
     * Send an in-call message (phone.sendGroupCallMessage): text with entities, or, in a live story, a
     * donation of `$paidStars` (with an empty text for a standalone donation).
     */
    public function sendMessage(string $message, ?ParseMode $parseMode = null, ?int $paidStars = null, string|int|null $sendAs = null): void
    {
        $entities = [];
        if ($parseMode === ParseMode::MARKDOWN) {
            $parsed = TextEntities::fromMarkdown($message);
            [$message, $entities] = [$parsed->message, $parsed->entities];
        } elseif ($parseMode === ParseMode::HTML) {
            $parsed = TextEntities::fromHtml($message);
            [$message, $entities] = [$parsed->message, $parsed->entities];
        }
        $this->sendTextWithEntities($message, $entities, $paidStars, $sendAs);
    }

    /**
     * Send an in-call reaction: a single emoji, or a custom emoji with `$emoji` as its fallback.
     */
    public function sendReaction(string $emoji, ?int $customEmojiId = null): void
    {
        $entities = [];
        if ($customEmojiId !== null) {
            $entities[] = ['_' => 'messageEntityCustomEmoji', 'offset' => 0, 'length' => self::utf16Length($emoji), 'document_id' => $customEmojiId];
        }
        $this->sendTextWithEntities($emoji, $entities, null, null);
    }

    /**
     * @param list<mixed> $entities
     */
    private function sendTextWithEntities(string $text, array $entities, ?int $paidStars, string|int|null $sendAs): void
    {
        $params = [
            'call' => $this->inputCall,
            'random_id' => random_int(PHP_INT_MIN, PHP_INT_MAX),
            'message' => ['_' => 'textWithEntities', 'text' => $text, 'entities' => $entities],
            ...($paidStars !== null ? ['allow_paid_stars' => $paidStars] : []),
            ...($sendAs !== null ? ['send_as' => $sendAs] : []),
        ];
        $this->API->methodCallAsyncRead('phone.sendGroupCallMessage', $params);
    }

    /**
     * The length of a string in UTF-16 code units, as entities count it.
     *
     * @psalm-pure
     */
    public static function utf16Length(string $text): int
    {
        return (int) (\strlen(mb_convert_encoding($text, 'UTF-16', 'UTF-8')) / 2);
    }

    /**
     * Delete in-call messages (our own, or anyone's for admins).
     *
     * @param list<int> $ids
     */
    public function deleteMessages(array $ids, bool $reportSpam = false): void
    {
        $this->API->methodCallAsyncRead('phone.deleteGroupCallMessages', [
            'call' => $this->inputCall,
            'messages' => $ids,
            'report_spam' => $reportSpam,
        ]);
    }

    /**
     * Delete every in-call message of a participant (admins only).
     */
    public function deleteParticipantMessages(string|int $participant, bool $reportSpam = false): void
    {
        $this->API->methodCallAsyncRead('phone.deleteGroupCallParticipantMessages', [
            'call' => $this->inputCall,
            'participant' => $participant,
            'report_spam' => $reportSpam,
        ]);
    }

    /**
     * The Telegram Stars donated to this live story so far, and its top donors.
     */
    public function getStars(): GroupCallStars
    {
        $result = $this->API->methodCallAsyncRead('phone.getGroupCallStars', ['call' => $this->inputCall]);
        \assert(\is_array($result));
        $donors = [];
        foreach ($result['top_donors'] as $donor) {
            \assert(\is_array($donor));
            $donors[] = new GroupCallDonor(
                isset($donor['peer_id']) ? $this->API->getIdInternal($donor['peer_id']) : null,
                $donor['stars'],
                $donor['top'],
                $donor['my'],
            );
        }
        return new GroupCallStars($result['total_stars'], $donors);
    }

    /**
     * The peer we send in-call messages of this live story as by default (phone.saveDefaultSendAs).
     */
    public function saveDefaultSendAs(string|int $peer): void
    {
        $this->API->methodCallAsyncRead('phone.saveDefaultSendAs', ['call' => $this->inputCall, 'send_as' => $peer]);
    }

    /**
     * Remove participants from the video chat by kicking them from the group or channel it belongs to,
     * which also drops them from the call: a video chat has no notion of removing someone from just
     * the call, and this is exactly what official clients do (Telegram Desktop's
     * `Panel::kickParticipantSure`): in a basic group they are removed with messages.deleteChatUser, in
     * a supergroup or channel they are banned with channels.editBanned using the "kicked" rights (for a
     * user, all the send restrictions on top of `view_messages`; for a channel participant just
     * `view_messages`). Requires the `ban_users` admin right.
     */
    public function removeParticipant(string|int ...$participants): void
    {
        $peerId = $this->public->peerId;
        if ($peerId === null) {
            throw new \RuntimeException("The group or channel of $this is unknown, cannot remove participants from it.");
        }
        foreach ($participants as $participant) {
            $participantId = $this->API->getId($participant);
            if (!DialogId::isSupergroupOrChannel($peerId)) {
                $this->API->methodCallAsyncRead('messages.deleteChatUser', [
                    'chat_id' => $peerId,
                    'user_id' => $participantId,
                ]);
                continue;
            }
            $rights = ['_' => 'chatBannedRights', 'view_messages' => true, 'until_date' => 0];
            if (DialogId::isUser($participantId)) {
                $rights += [
                    'send_stickers' => true,
                    'send_gifs' => true,
                    'send_games' => true,
                    'send_inline' => true,
                    'send_photos' => true,
                    'send_videos' => true,
                    'send_roundvideos' => true,
                    'send_audios' => true,
                    'send_voices' => true,
                    'send_docs' => true,
                    'send_plain' => true,
                    'embed_links' => true,
                ];
            }
            $this->API->methodCallAsyncRead('channels.editBanned', [
                'channel' => $peerId,
                'participant' => $participantId,
                'banned_rights' => $rights,
            ]);
        }
    }

    /**
     * Our own audio source ID, in the signed form used by the API.
     */
    public function getSource(): int
    {
        return $this->source;
    }

    #[\Override]
    public function getCallState(): GroupCallState
    {
        return $this->callState;
    }

    /**
     * The last known [groupCall](https://core.telegram.org/constructor/groupCall).
     *
     * @internal
     */
    public function getRawCall(): array
    {
        return $this->call;
    }

    #[\Override]
    public function log(string $message, int $level = Logger::NOTICE): void
    {
        $this->API->logger->logger($message, $level);
    }

    #[\Override]
    public function getVoIPSettings(): \danog\MadelineProto\Settings\VoIP
    {
        return $this->API->getSettings()->getVoip();
    }

    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function isCallEnded(): bool
    {
        return $this->callState === GroupCallState::LEFT;
    }

    // Playback API, mirroring the one-to-one call API.

    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function __toString(): string
    {
        return $this->public->__toString();
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\GroupCall;

use Amp\ByteStream\ReadableStream;
use Amp\ByteStream\WritableStream;
use AssertionError;
use danog\DialogId\DialogId;
use danog\MadelineProto\CallStream;
use danog\MadelineProto\EventHandler\Calls\AbstractGroupCall;
use danog\MadelineProto\EventHandler\Calls\AbstractGroupCallParticipant;
use danog\MadelineProto\EventHandler\Calls\ConferenceCall as ConferenceCallUpdate;
use danog\MadelineProto\EventHandler\Calls\ConferenceCallParticipant;
use danog\MadelineProto\EventHandler\Calls\GroupCall;
use danog\MadelineProto\EventHandler\Calls\GroupCallStars;
use danog\MadelineProto\EventHandler\Calls\GroupCallState;
use danog\MadelineProto\EventHandler\Calls\LiveStory;
use danog\MadelineProto\LocalDirectory;
use danog\MadelineProto\LocalFile;
use danog\MadelineProto\Logger;
use danog\MadelineProto\MediaDestination;
use danog\MadelineProto\MTProto\SpecialMethodType;
use danog\MadelineProto\ParseMode;
use danog\MadelineProto\RecordingFormat;
use danog\MadelineProto\RemoteUrl;
use danog\MadelineProto\RPCErrorException;
use danog\MadelineProto\TextEntities;
use danog\MadelineProto\Tgcalls\E2E\ConferenceCall;
use Revolt\EventLoop;

/**
 * Manages group calls (video chats, livestreams and live stories).
 *
 * See https://core.telegram.org/api/group-calls for more info.
 *
 * @psalm-import-type StreamMask from \danog\MadelineProto\CallStream
 *
 * @internal
 */
trait Handler
{
    /** How many untracked group calls to remember, see {@see self::$seenGroupCalls}. */
    private const SEEN_GROUP_CALLS_MAX = 1000;

    /**
     * The group calls this session joined, created, or looked up (never the ones it only gets updates
     * about: those may be joined by another session of the same account).
     *
     * @var array<int, GroupCallController>
     */
    private array $groupCalls = [];
    /**
     * The group calls we got updates about but do not track: ID => [access hash, peer ID, live story].
     *
     * @var array<int, array{int, ?int, bool}>
     */
    private array $seenGroupCalls = [];
    /** @var array<int, ConferenceCall> End-to-end encrypted conference calls, by call id. */
    private array $conferenceCalls = [];

    /**
     * Register a live end-to-end encrypted conference so its chain-block and encrypted-message
     * updates are routed to it.
     *
     * @internal
     *
     * @psalm-external-mutation-free
     */
    public function registerConferenceCall(int $id, ConferenceCall $call): void
    {
        $this->conferenceCalls[$id] = $call;
    }

    /**
     * @internal
     *
     * @psalm-external-mutation-free
     */
    public function unregisterConferenceCall(int $id): void
    {
        unset($this->conferenceCalls[$id]);
    }

    /**
     * Create a group call (video chat or livestream) in the specified group or channel.
     *
     * Requires the `manage_call` admin right, see
     * [video chats/livestreams Â»](https://core.telegram.org/api/group-calls#video-chats-livestreams).
     *
     * @param string|int       $peer         The group or channel where the call should be created.
     * @param string|null $title        Custom title, defaults to the group/channel name.
     * @param int|null    $scheduleDate If set, creates a scheduled call for the specified UNIX timestamp.
     * @param bool        $rtmpStream   Whether the call's media is published by an external RTMP application.
     */
    public function createGroupCall(string|int $peer, ?string $title = null, ?int $scheduleDate = null, bool $rtmpStream = false): GroupCall
    {
        $params = [
            'peer' => $peer,
            'random_id' => random_int(-(2**31), (2**31)-1),
            'rtmp_stream' => $rtmpStream,
        ];
        if ($title !== null) {
            $params['title'] = $title;
        }
        if ($scheduleDate !== null) {
            $params['schedule_date'] = $scheduleDate;
        }
        $updates = $this->methodCallAsyncRead('phone.createGroupCall', $params);
        // Start tracking the new call from the updateGroupCall in the response (handleGroupCallUpdate
        // never starts tracking a call by itself, see there).
        $inputCall = null;
        $peerId = null;
        foreach ($updates['updates'] ?? [] as $update) {
            if ($update['_'] === 'updateGroupCall' && $update['call']['_'] === 'groupCall') {
                $inputCall = [
                    '_' => 'inputGroupCall',
                    'id' => $update['call']['id'],
                    'access_hash' => $update['call']['access_hash'],
                ];
                // Not necessarily $peer: starting an RTMP livestream migrates a basic group to a supergroup.
                $peerId = isset($update['peer']) ? $this->getIdInternal($update['peer']) : null;
                break;
            }
        }
        $controller = $inputCall !== null
            ? $this->getGroupCallByInput($inputCall, $peerId ?? $this->migratedPeer($peer))
            : null;
        if ($controller === null) {
            throw new AssertionError('The server did not return the created group call!');
        }
        \assert($controller->public instanceof GroupCall);
        return $controller->public;
    }

    /**
     * Create and join a new end-to-end encrypted conference call, with ourselves as the only
     * participant. Others join it with {@see self::joinConferenceCall()} using the returned call id.
     *
     * See [end-to-end encrypted group calls Â»](https://core.telegram.org/api/end-to-end/group-calls).
     * Uses the `sodium` and `openssl` PHP extensions if available, for speed.
     *
     * @param bool $muted Whether to join muted.
     */
    public function createConferenceCall(bool $muted = false): ConferenceCallUpdate
    {
        $conference = new ConferenceCall($this);
        $conference->create($muted);
        return $conference->getPublic();
    }

    /**
     * Join an existing end-to-end encrypted conference call.
     *
     * See [end-to-end encrypted group calls Â»](https://core.telegram.org/api/end-to-end/group-calls).
     * Uses the `sodium` and `openssl` PHP extensions if available, for speed.
     *
     * @param array $call  The `groupCall` (or `inputGroupCall`) of the conference to join.
     * @param bool  $muted Whether to join muted.
     */
    public function joinConferenceCall(array $call, bool $muted = false): ConferenceCallUpdate
    {
        $conference = new ConferenceCall($this);
        $conference->setCall($call);
        $conference->join($muted);
        return $conference->getPublic();
    }

    /**
     * Get a live end-to-end encrypted conference call this session is in, by its call id.
     *
     * @psalm-mutation-free
     */
    public function getConferenceCall(int $id): ?ConferenceCallUpdate
    {
        return ($this->conferenceCalls[$id] ?? null)?->getPublic();
    }

    /**
     * Resolve the live controller of a conference call we are tracking.
     *
     * @internal
     *
     * @psalm-external-mutation-free
     */
    private function getConferenceCallController(int $id): ConferenceCall
    {
        return $this->conferenceCalls[$id] ?? throw new AssertionError('Unknown conference call!');
    }

    /**
     * Whether we are currently in a conference call.
     *
     * @internal
     *
     * @psalm-mutation-free
     */
    public function isConferenceCallJoined(int $id): bool
    {
        return ($this->conferenceCalls[$id] ?? null)?->isJoined() ?? false;
    }

    /**
     * Leave a conference call, without ending it for the other participants.
     *
     * @internal
     */
    public function leaveConferenceCall(int $id): void
    {
        // Already left (and forgotten), if unknown.
        ($this->conferenceCalls[$id] ?? null)?->leave();
    }

    /**
     * End a conference call for everyone (creator only) and leave it.
     *
     * @internal
     */
    public function discardConferenceCall(int $id): void
    {
        $this->getConferenceCallController($id)->discard();
    }

    /**
     * Get the participants of a conference call, keyed by their user id.
     *
     * @return array<int, ConferenceCallParticipant>
     *
     * @internal
     *
     * @psalm-mutation-free
     */
    public function getConferenceCallParticipants(int $id): array
    {
        return $this->getConferenceCallController($id)->getParticipants();
    }

    /**
     * Remove participants from a conference call, rekeying for the remaining members.
     *
     * @internal
     */
    public function removeConferenceCallParticipants(int $id, string|int ...$participants): void
    {
        $this->getConferenceCallController($id)->removeParticipant(...$participants);
    }

    /**
     * Change the title of a conference call.
     *
     * @internal
     */
    public function setConferenceCallTitle(int $id, string $title): void
    {
        $this->getConferenceCallController($id)->setTitle($title);
    }

    /**
     * Invite users to a conference call.
     *
     * @internal
     */
    public function inviteToConferenceCall(int $id, string|int ...$users): void
    {
        $this->getConferenceCallController($id)->invite(...$users);
    }

    /**
     * Export a conference link to a conference call.
     *
     * @internal
     */
    public function exportConferenceCallInvite(int $id): string
    {
        return $this->getConferenceCallController($id)->exportInvite();
    }

    /**
     * The four verification emojis of a conference call, or null until every nonce has been revealed.
     *
     * @return list<string>|null
     *
     * @internal
     *
     * @psalm-mutation-free
     */
    public function getConferenceCallVisualization(int $id): ?array
    {
        return $this->getConferenceCallController($id)->getVisualization();
    }

    /**
     * Send an end-to-end encrypted in-call message to every participant of a conference call.
     *
     * @internal
     */
    public function sendConferenceCallMessage(int $id, string $message, ?ParseMode $parseMode = null): void
    {
        $this->getConferenceCallController($id)->sendMessage($message, $parseMode);
    }

    /**
     * Send an end-to-end encrypted in-call reaction to every participant of a conference call.
     *
     * @internal
     */
    public function sendConferenceCallReaction(int $id, string $emoji, ?int $customEmojiId = null): void
    {
        $this->getConferenceCallController($id)->sendReaction($emoji, $customEmojiId);
    }

    /**
     * Change a participant's state in a conference call (phone.editGroupCallParticipant).
     *
     * @internal
     */
    public function editConferenceCallParticipant(int $id, string|int|array $participant, ?bool $muted = null, ?int $volume = null, ?bool $videoPaused = null): void
    {
        $this->getConferenceCallController($id)->editParticipant($participant, $muted, $volume, $videoPaused);
    }

    /**
     * Change the settings of a conference call (phone.toggleGroupCallSettings).
     *
     * @internal
     */
    public function toggleConferenceCallSettings(int $id, ?bool $joinMuted = null, bool $resetInviteHash = false, ?bool $messagesEnabled = null): void
    {
        $this->getConferenceCallController($id)->toggleSettings($joinMuted, $resetInviteHash, $messagesEnabled);
    }

    /**
     * Get an [end-to-end encrypted conference call Â»](https://core.telegram.org/api/group-calls#conference-calls)
     * from its [conference link Â»](https://core.telegram.org/api/links#conference-links) slug, or null if
     * the link is not valid any more.
     */
    public function getConferenceCallBySlug(string $slug): ?ConferenceCallUpdate
    {
        return $this->getConferenceCallByInput(['_' => 'inputGroupCallSlug', 'slug' => $slug]);
    }

    /**
     * Join an end-to-end encrypted conference call from its conference link slug.
     *
     * @param bool $muted Whether to join muted.
     */
    public function joinConferenceCallBySlug(string $slug, bool $muted = false): ConferenceCallUpdate
    {
        $call = $this->getConferenceCallBySlug($slug) ?? throw new AssertionError('This conference link is not valid any more!');
        return $call->join($muted);
    }

    /**
     * Join an end-to-end encrypted conference call from the service message that invited us to it
     * (a `messageActionConferenceCall`, see {@see \danog\MadelineProto\EventHandler\Message\Service\DialogConferenceCall}).
     *
     * @param int  $msgId ID of the invitation message.
     * @param bool $muted Whether to join muted.
     */
    public function joinConferenceCallByInviteMessage(int $msgId, bool $muted = false): ConferenceCallUpdate
    {
        $call = $this->getConferenceCallByInput(['_' => 'inputGroupCallInviteMessage', 'msg_id' => $msgId])
            ?? throw new AssertionError('This conference call is not active any more!');
        return $call->join($muted);
    }

    /**
     * Decline an invitation to a conference call.
     *
     * @param int $msgId ID of the invitation message (a `messageActionConferenceCall`).
     */
    public function declineConferenceCallInvite(int $msgId): void
    {
        $this->methodCallAsyncRead('phone.declineConferenceCallInvite', ['msg_id' => $msgId]);
    }

    /**
     * Resolve a conference call from any InputGroupCall: the live one we are in, or a handle to one we
     * are not in (yet).
     */
    private function getConferenceCallByInput(array $inputCall): ?ConferenceCallUpdate
    {
        $result = $this->methodCallAsyncRead('phone.getGroupCall', ['call' => $inputCall, 'limit' => 1]);
        $call = $result['call'];
        if ($call['_'] === 'groupCallDiscarded' || !($call['conference'] ?? false)) {
            return null;
        }
        if (isset($this->conferenceCalls[$call['id']])) {
            return $this->conferenceCalls[$call['id']]->getPublic();
        }
        return new ConferenceCallUpdate($this, $call);
    }

    /**
     * Whether a screen-share is currently being transmitted in a conference call.
     *
     * @internal
     *
     * @psalm-mutation-free
     */
    public function isConferenceCallSharingScreen(int $id): bool
    {
        return $this->getConferenceCallController($id)->isSharingScreen();
    }

    /**
     * Start sharing a screen in a conference call.
     *
     * @internal
     */
    public function enableConferenceCallPresentation(int $id): void
    {
        $this->getConferenceCallController($id)->enablePresentation();
    }

    /**
     * Stop sharing the screen in a conference call.
     *
     * @internal
     */
    public function disableConferenceCallPresentation(int $id): void
    {
        $this->getConferenceCallController($id)->disablePresentation();
    }

    /**
     * Mute or unmute our own audio stream in a conference call.
     *
     * @internal
     */
    public function setConferenceCallMuted(int $id, bool $muted = true): void
    {
        $this->getConferenceCallController($id)->setMuted($muted);
    }

    /**
     * Whether our own audio stream is muted in a conference call.
     *
     * @internal
     *
     * @psalm-mutation-free
     */
    public function isConferenceCallMuted(int $id): bool
    {
        return $this->getConferenceCallController($id)->isMuted();
    }

    /**
     * Play a file in a conference call.
     *
     * @internal
     */
    public function conferenceCallPlay(int $id, LocalFile|RemoteUrl|ReadableStream $file, MediaDestination $dest = MediaDestination::Camera): void
    {
        $this->getConferenceCallController($id)->play($file, $dest);
    }

    /**
     * Play a file in a conference call, blocking until it has finished playing if a stream is provided.
     *
     * @internal
     */
    public function conferenceCallPlayBlocking(int $id, LocalFile|RemoteUrl|ReadableStream $file, MediaDestination $dest = MediaDestination::Camera): void
    {
        $this->getConferenceCallController($id)->playBlocking($file, $dest);
    }

    /**
     * Record one participant of a conference call into a single file (or stream) with a fixed set of
     * tracks, see {@see \danog\MadelineProto\EventHandler\Calls\ConferenceCall::setOutput()}.
     *
     * @internal
     *
     * @param ?StreamMask $streams The streams to record, as a bitmask of {@see CallStream} flags, or null for every available one.
     *
     * @return int The streams the participant currently sends, as a bitmask of {@see CallStream} flags.
     */
    public function conferenceCallSetOutput(int $id, LocalFile|WritableStream $file, string|int|null $participant = null, ?RecordingFormat $format = null, ?int $streams = null): int
    {
        return $this->getConferenceCallController($id)->setOutput($file, $participant, $format, $streams);
    }

    /**
     * Record conference call media into a directory: every transmitting participant (or only the
     * given one) as its own numbered series of files, see
     * {@see \danog\MadelineProto\EventHandler\Calls\ConferenceCall::setOutputFolder()}.
     *
     * @internal
     */
    public function conferenceCallSetOutputFolder(int $id, LocalDirectory $dir, string|int|null $participant = null, ?RecordingFormat $format = null): void
    {
        $this->getConferenceCallController($id)->setOutputFolder($dir, $participant, $format);
    }

    /**
     * Get the state of a conference call.
     *
     * @internal
     *
     * @psalm-mutation-free
     */
    public function getConferenceCallState(int $id): GroupCallState
    {
        return ($this->conferenceCalls[$id] ?? null)?->getCallState() ?? GroupCallState::NOT_JOINED;
    }

    /**
     * Files to play on hold in a conference call.
     *
     * @internal
     */
    public function conferenceCallPlayOnHold(int $id, MediaDestination $dest = MediaDestination::Camera, LocalFile|RemoteUrl|ReadableStream ...$files): void
    {
        $this->getConferenceCallController($id)->playOnHold($dest, ...$files);
    }

    /**
     * Skip to the next file in the playlist of a conference call.
     *
     * @internal
     */
    public function conferenceCallSkipPlay(int $id, MediaDestination $dest = MediaDestination::Camera): void
    {
        $this->getConferenceCallController($id)->skip($dest);
    }

    /**
     * Stop playing all files in a conference call, clearing the main and the hold playlist.
     *
     * @internal
     */
    public function conferenceCallStopPlay(int $id, MediaDestination $dest = MediaDestination::Camera): void
    {
        $this->getConferenceCallController($id)->stop($dest);
    }

    /**
     * Pause playback of the current file in a conference call.
     *
     * @internal
     *
     * @psalm-external-mutation-free
     */
    public function conferenceCallPausePlay(int $id, MediaDestination $dest = MediaDestination::Camera): void
    {
        $this->getConferenceCallController($id)->pause($dest);
    }

    /**
     * Resume playback of the current file in a conference call.
     *
     * @internal
     *
     * @psalm-external-mutation-free
     */
    public function conferenceCallResumePlay(int $id, MediaDestination $dest = MediaDestination::Camera): void
    {
        $this->getConferenceCallController($id)->resume($dest);
    }

    /**
     * Whether the currently playing file of a conference call is paused.
     *
     * @internal
     *
     * @psalm-mutation-free
     */
    public function isConferenceCallPlayPaused(int $id, MediaDestination $dest = MediaDestination::Camera): bool
    {
        return $this->getConferenceCallController($id)->isPaused($dest);
    }

    /**
     * Get the file that is currently being played in a conference call.
     *
     * @internal
     *
     * @psalm-mutation-free
     */
    public function conferenceCallGetCurrent(int $id, MediaDestination $dest = MediaDestination::Camera): RemoteUrl|LocalFile|string|null
    {
        return $this->getConferenceCallController($id)->getCurrent($dest);
    }

    /**
     * Get the group call (video chat or livestream) currently active in a group or channel.
     */
    public function getGroupCall(string|int $peer): ?GroupCall
    {
        $call = $this->currentGroupCall($peer)?->public;
        return $call instanceof GroupCall ? $call : null;
    }

    /**
     * The group call currently active in a group or channel.
     *
     * The call of the full info of the chat is cached, and it may have ended since: then the full
     * info is fetched again. A basic group migrated to a supergroup (e.g. by starting an RTMP
     * livestream) is followed to the supergroup.
     */
    private function currentGroupCall(string|int $peer): ?GroupCallController
    {
        $peer = $this->migratedPeer($peer);
        for ($attempt = 0; $attempt < 2; $attempt++) {
            $inputCall = $this->getFullInfo($peer)['full']['call'] ?? null;
            if ($inputCall === null) {
                return null;
            }
            try {
                $controller = $this->getGroupCallByInput($inputCall, $peer);
                if ($controller !== null && !$controller->public->discarded) {
                    return $controller;
                }
            } catch (RPCErrorException $e) {
                // GROUPCALL_FORBIDDEN: the call has already ended.
                if ($e->rpc !== 'GROUPCALL_FORBIDDEN' && $e->rpc !== 'GROUPCALL_INVALID') {
                    throw $e;
                }
            }
            $this->refreshFullPeerCache($peer);
        }
        return null;
    }

    /**
     * The supergroup a basic group was migrated to, or the peer itself.
     */
    private function migratedPeer(string|int $peer): int
    {
        $id = $this->getInfo($peer, \danog\MadelineProto\API::INFO_TYPE_ID);
        if (DialogId::isChat($id)) {
            $migratedTo = $this->getInfo($id)['Chat']['migrated_to']['channel_id'] ?? null;
            if ($migratedTo !== null) {
                return (int) $migratedTo;
            }
        }
        return $id;
    }

    /**
     * Get a group call from its
     * [conference deep link Â»](https://core.telegram.org/api/links#conference-links) slug.
     */
    public function getGroupCallBySlug(string $slug): ?GroupCall
    {
        $call = $this->getGroupCallByInput(['_' => 'inputGroupCallSlug', 'slug' => $slug])?->public;
        return $call instanceof GroupCall ? $call : null;
    }

    /**
     * Join the group call currently active in a group or channel.
     *
     * @param string|int       $peer       The group or channel whose call should be joined.
     * @param bool        $muted      Whether to join muted.
     * @param string|int|null  $joinAs     Peer to join as, defaults to ourselves.
     * @param string|null $inviteHash Invite hash from a video chat invite link, if any.
     */
    public function joinGroupCall(string|int $peer, bool $muted = false, string|int|null $joinAs = null, ?string $inviteHash = null): GroupCall
    {
        $controller = $this->currentGroupCall($peer);
        if ($controller === null) {
            throw new AssertionError('There is no active group call in this chat!');
        }
        $controller->join($muted, $joinAs, $inviteHash);
        \assert($controller->public instanceof GroupCall);
        return $controller->public;
    }

    /**
     * Join a group call we already have a {@see GroupCall} object for.
     *
     * @internal
     */
    public function joinGroupCallById(int $id, bool $muted = false, string|int|null $joinAs = null, ?string $inviteHash = null): void
    {
        $this->getGroupCallController($id)->join($muted, $joinAs, $inviteHash);
    }

    /**
     * Fetch (and cache) a group call from any subtype of
     * [InputGroupCall](https://core.telegram.org/type/InputGroupCall).
     *
     * @internal
     */
    public function getGroupCallByInput(array $inputCall, ?int $peerId = null, bool $liveStory = false): ?GroupCallController
    {
        if ($inputCall['_'] === 'inputGroupCall' && isset($this->groupCalls[$inputCall['id']])) {
            return $this->groupCalls[$inputCall['id']];
        }
        $result = $this->methodCallAsyncRead('phone.getGroupCall', ['call' => $inputCall, 'limit' => 100]);
        $call = $result['call'];
        if ($call['_'] === 'groupCallDiscarded') {
            return null;
        }
        if (isset($this->groupCalls[$call['id']])) {
            return $this->groupCalls[$call['id']];
        }
        $controller = new GroupCallController($this, $call, $peerId, $liveStory);
        $this->groupCalls[$call['id']] = $controller;
        // We already have the full call and its first page of participants: no need to refetch.
        $controller->applyGroupCall($result);
        return $controller;
    }

    /**
     * @internal
     *
     * @psalm-external-mutation-free
     */
    public function cleanupGroupCall(int $id): void
    {
        unset($this->groupCalls[$id]);
    }

    /**
     * Get all group calls (video chats, livestreams and live stories) we're currently tracking, indexed by their ID.
     *
     * @return array<int, AbstractGroupCall>
     */
    public function getAllGroupCalls(): array
    {
        return array_map(static fn (GroupCallController $c): AbstractGroupCall => $c->public, $this->groupCalls);
    }

    /**
     * Leave a group call, without ending it for the other participants.
     */
    public function leaveGroupCall(int $id): void
    {
        $this->getGroupCallController($id)->leave();
    }

    /**
     * End a group call for all participants.
     */
    public function discardGroupCall(int $id): void
    {
        $this->getGroupCallController($id)->discard();
    }

    /**
     * Get the state of a group call.
     *
     * @psalm-mutation-free
     */
    public function getGroupCallState(int $id): GroupCallState
    {
        if (isset($this->groupCalls[$id])) {
            return $this->groupCalls[$id]->getCallState();
        }
        return isset($this->seenGroupCalls[$id]) ? GroupCallState::NOT_JOINED : GroupCallState::LEFT;
    }

    /**
     * Whether a group call we are tracking is a live story (and thus has {@see LiveStoryParticipant}
     * participants), false for a video chat/livestream or a call we do not track.
     *
     * @internal
     *
     * @psalm-mutation-free
     */
    public function isGroupCallLiveStory(int $id): bool
    {
        return ($this->groupCalls[$id] ?? null)?->public instanceof LiveStory;
    }

    /**
     * Bot API ID of the peer a group call we are tracking belongs to, if known.
     *
     * @internal
     *
     * @psalm-mutation-free
     */
    public function getGroupCallPeerId(int $id): ?int
    {
        return ($this->groupCalls[$id] ?? null)?->public->peerId;
    }

    /**
     * Get the participants of a group call, indexed by their bot API peer ID.
     *
     * @return array<int, AbstractGroupCallParticipant>
     *
     * @psalm-mutation-free
     */
    public function getGroupCallParticipants(int $id): array
    {
        return $this->getGroupCallController($id)->getParticipants();
    }

    /**
     * Mute or unmute our own audio stream in a group call.
     */
    public function setGroupCallMuted(int $id, bool $muted = true): void
    {
        $this->getGroupCallController($id)->setMuted($muted);
    }

    /**
     * Whether our own audio stream is muted in a group call.
     *
     * @psalm-mutation-free
     */
    public function isGroupCallMuted(int $id): bool
    {
        return $this->getGroupCallController($id)->isMuted();
    }

    /**
     * Change the title of a group call.
     */
    public function setGroupCallTitle(int $id, string $title): void
    {
        $this->getGroupCallController($id)->setTitle($title);
    }

    /**
     * Invite users to a group call.
     */
    public function inviteToGroupCall(int $id, string|int ...$users): void
    {
        $this->getGroupCallController($id)->invite(...$users);
    }

    /**
     * Export an invite link for a group call.
     *
     * @param bool $canSelfUnmute Whether users joining with this link may speak without asking; admins only.
     */
    public function exportGroupCallInvite(int $id, bool $canSelfUnmute = false): string
    {
        return $this->getGroupCallController($id)->exportInvite($canSelfUnmute);
    }

    /**
     * Change a participant's state in a group call (phone.editGroupCallParticipant): mute/unmute them,
     * set our playback volume of them, raise/lower our own hand, pause/resume our own video.
     *
     * @internal
     */
    public function editGroupCallParticipant(int $id, string|int|array $participant, ?bool $muted = null, ?int $volume = null, ?bool $raiseHand = null, ?bool $videoStopped = null, ?bool $videoPaused = null, ?bool $presentationPaused = null): void
    {
        $this->getGroupCallController($id)->editParticipant($participant, $muted, $volume, $raiseHand, $videoStopped, $videoPaused, $presentationPaused);
    }

    /**
     * Change the settings of a group call (phone.toggleGroupCallSettings, admins only).
     *
     * @internal
     */
    public function toggleGroupCallSettings(int $id, ?bool $joinMuted = null, bool $resetInviteHash = false, ?bool $messagesEnabled = null, ?int $sendPaidMessagesStars = null): void
    {
        $this->getGroupCallController($id)->toggleSettings($joinMuted, $resetInviteHash, $messagesEnabled, $sendPaidMessagesStars);
    }

    /**
     * Start or stop a server-side recording of a group call (phone.toggleGroupCallRecord, admins only).
     *
     * @internal
     */
    public function toggleGroupCallRecord(int $id, bool $start, ?string $title = null, bool $video = false, bool $portrait = false): void
    {
        $this->getGroupCallController($id)->toggleRecord($start, $title, $video, $portrait);
    }

    /**
     * Start a scheduled group call now (admins only).
     *
     * @internal
     */
    public function startScheduledGroupCall(int $id): void
    {
        $this->getGroupCallController($id)->startScheduled();
    }

    /**
     * Subscribe to (or unsubscribe from) a notification when a scheduled group call starts.
     *
     * @internal
     */
    public function toggleGroupCallStartSubscription(int $id, bool $subscribed): void
    {
        $this->getGroupCallController($id)->setStartSubscription($subscribed);
    }

    /**
     * Send an in-call message (or, in a live story, a donation) in a group call.
     *
     * @internal
     */
    public function sendGroupCallMessage(int $id, string $message, ?ParseMode $parseMode = null, ?int $paidStars = null, string|int|null $sendAs = null): void
    {
        $this->getGroupCallController($id)->sendMessage($message, $parseMode, $paidStars, $sendAs);
    }

    /**
     * Send an in-call reaction in a group call.
     *
     * @internal
     */
    public function sendGroupCallReaction(int $id, string $emoji, ?int $customEmojiId = null): void
    {
        $this->getGroupCallController($id)->sendReaction($emoji, $customEmojiId);
    }

    /**
     * Delete in-call messages of a group call.
     *
     * @param list<int> $ids
     *
     * @internal
     */
    public function deleteGroupCallMessages(int $id, array $ids, bool $reportSpam = false): void
    {
        $this->getGroupCallController($id)->deleteMessages($ids, $reportSpam);
    }

    /**
     * Delete every in-call message of a participant of a group call (admins only).
     *
     * @internal
     */
    public function deleteGroupCallParticipantMessages(int $id, string|int $participant, bool $reportSpam = false): void
    {
        $this->getGroupCallController($id)->deleteParticipantMessages($participant, $reportSpam);
    }

    /**
     * The Telegram Stars donated to a live story so far, and its top donors.
     *
     * @internal
     */
    public function getGroupCallStars(int $id): GroupCallStars
    {
        return $this->getGroupCallController($id)->getStars();
    }

    /**
     * The peer we send in-call messages of a live story as by default.
     *
     * @internal
     */
    public function saveDefaultGroupCallSendAs(int $id, string|int $peer): void
    {
        $this->getGroupCallController($id)->saveDefaultSendAs($peer);
    }

    /**
     * A participant of a group call by their id, username or peer, or null if they are not in it.
     *
     * @internal
     */
    public function getGroupCallParticipant(int $id, string|int $participant): ?AbstractGroupCallParticipant
    {
        return $this->getGroupCallController($id)->getParticipant($participant);
    }

    /**
     * Whether a group call is in stream mode (its media is received by downloading chunks).
     *
     * @internal
     *
     * @psalm-mutation-free
     */
    public function isGroupCallStreamMode(int $id): bool
    {
        return ($this->groupCalls[$id] ?? null)?->isStreamMode() ?? false;
    }

    /**
     * Whether a group call is an RTMP livestream.
     *
     * @internal
     *
     * @psalm-mutation-free
     */
    public function isGroupCallRtmpMode(int $id): bool
    {
        return ($this->groupCalls[$id] ?? null)?->isRtmpMode() ?? false;
    }

    /**
     * The stream channels of an RTMP livestream (phone.getGroupCallStreamChannels): their ids, and the
     * timestamp of the live edge.
     *
     * @return list<array{channel: int, scale: int, last_timestamp_ms: int}>
     *
     * @internal
     */
    public function getGroupCallStreamChannels(int $id): array
    {
        $controller = $this->getGroupCallController($id);
        // A flood wait is thrown, not waited for here: the stream receiver polls this while the livestream has no
        // media yet, and waits as long as the server asks itself.
        $result = $this->methodCallAsyncRead('phone.getGroupCallStreamChannels', ['call' => $controller->getInputCall(), 'floodWaitLimit' => 0], $this->groupCallStreamDc($controller));
        $channels = [];
        foreach ($result['channels'] as $channel) {
            $channels[] = ['channel' => (int) $channel['channel'], 'scale' => (int) $channel['scale'], 'last_timestamp_ms' => (int) $channel['last_timestamp_ms']];
        }
        return $channels;
    }

    /**
     * Download one media chunk of a group call in stream mode (upload.getFile with an
     * inputGroupCallStream location) from its stream DC, or null on a CDN redirect.
     *
     * @internal
     */
    public function downloadGroupCallStreamChunk(int $id, int $timeMs, int $scale = 0, ?int $videoChannel = null, ?int $videoQuality = null): ?string
    {
        $controller = $this->getGroupCallController($id);
        $location = ['_' => 'inputGroupCallStream', 'call' => $controller->getInputCall(), 'time_ms' => $timeMs, 'scale' => $scale];
        if ($videoChannel !== null) {
            $location['video_channel'] = $videoChannel;
            $location['video_quality'] = $videoQuality ?? 0;
        }
        $dc = $this->groupCallStreamDc($controller);
        $result = $this->methodCallAsyncRead('upload.getFile', [
            'location' => $location,
            'offset' => 0,
            'limit' => 1024 * 1024,
            'floodWaitLimit' => 0,
            'specialMethodType' => SpecialMethodType::FILE_RELATED,
        ], $dc);
        if ($result['_'] !== 'upload.file') {
            return null;
        }
        return (string) $result['bytes'];
    }

    /**
     * The DC serving the chunks of a group call in stream mode (its stream_dc_id).
     */
    private function groupCallStreamDc(GroupCallController $controller): int
    {
        // The bare DC ID, as tdesktop takes it (BareDcId), whether the server sent it shifted or not,
        // and whether it is the authorized DC, already in our numbering of the test DCs (10001-10003).
        $dc = ($controller->public->streamDcId ?? $this->loginState->getState()->authorizedDc) % 10_000;
        if ($this->isTestMode()) {
            $dc += 10_000;
        }
        if ($this->datacenter->has(-$dc)) {
            // Its media-only connection: chunks are downloaded like files.
            $dc = -$dc;
        }
        return $dc;
    }

    /**
     * Resolve the controller of a group call we are tracking.
     *
     * @internal
     */
    private function getGroupCallController(int $id): GroupCallController
    {
        if (isset($this->groupCalls[$id])) {
            return $this->groupCalls[$id];
        }
        // A call we only saw in an update (or forgot on restart, see dropUnjoinedGroupCalls()):
        // start tracking it now that it is used, from its access hash.
        $seen = $this->seenGroupCalls[$id] ?? throw new AssertionError('Unknown group call!');
        $controller = $this->getGroupCallByInput(
            ['_' => 'inputGroupCall', 'id' => $id, 'access_hash' => $seen[0]],
            $seen[1],
            $seen[2],
        );
        if ($controller === null) {
            unset($this->seenGroupCalls[$id]);
            throw new AssertionError('The group call has ended!');
        }
        return $controller;
    }

    /**
     * Remember the access hash of a group call we saw but do not track, see {@see self::getGroupCallController()}.
     */
    private function rememberGroupCall(int $id, int $accessHash, ?int $peerId, bool $liveStory): void
    {
        unset($this->seenGroupCalls[$id]);
        $this->seenGroupCalls[$id] = [$accessHash, $peerId, $liveStory];
        if (\count($this->seenGroupCalls) > self::SEEN_GROUP_CALLS_MAX) {
            // Calls whose end we missed (it is only notified to running sessions): forget the oldest.
            unset($this->seenGroupCalls[array_key_first($this->seenGroupCalls)]);
        }
    }

    /**
     * Stop tracking the group calls we are not in (nor publishing to), when resuming a session.
     *
     * A session only keeps (and resumes) the group calls it joined: trackers of calls it merely
     * looked up, or that older versions created from every updateGroupCall (even for calls joined by
     * another session of the same account), are dropped without any request. They are tracked again
     * on first use, see {@see self::getGroupCallController()}.
     *
     * @internal
     */
    public function dropUnjoinedGroupCalls(): void
    {
        foreach ($this->groupCalls as $id => $groupCall) {
            if ($groupCall->forgetIfUnjoined()) {
                $public = $groupCall->public;
                if (!$public->discarded) {
                    $this->rememberGroupCall($id, $public->accessHash, $public->peerId, $public instanceof LiveStory);
                }
            }
        }
    }

    /**
     * The peers we may join the video chats and livestreams of a group or channel as: ourselves, the
     * channels we own, and (for anonymous admins) the group itself. Bot API IDs.
     *
     * See [joining a group call on behalf of owned channels Â»](https://core.telegram.org/api/group-calls#joining-a-group-call-on-behalf-of-owned-channels).
     *
     * @return list<int>
     */
    public function getGroupCallJoinAs(string|int $peer): array
    {
        $result = $this->methodCallAsyncRead('phone.getGroupCallJoinAs', ['peer' => $peer]);
        $ids = [];
        foreach ($result['peers'] as $joinAs) {
            $id = $this->getIdInternal($joinAs);
            if ($id !== null) {
                $ids[] = $id;
            }
        }
        return $ids;
    }

    /**
     * Save the peer we join the video chats and livestreams of a group or channel as by default.
     *
     * @param string|int $peer   The group or channel.
     * @param string|int $joinAs The peer to join as (one of {@see self::getGroupCallJoinAs()}).
     */
    public function saveDefaultGroupCallJoinAs(string|int $peer, string|int $joinAs): void
    {
        $this->methodCallAsyncRead('phone.saveDefaultGroupCallJoinAs', ['peer' => $peer, 'join_as' => $joinAs]);
    }

    /**
     * Get the RTMP URL and stream key to publish an [RTMP livestream Â»](https://core.telegram.org/api/group-calls#creating-and-publishing-an-rtmp-livestream)
     * to, in a group or channel (create the call with `rtmpStream` afterwards, see {@see self::createGroupCall()}),
     * or as a live story (see {@see self::startLive()}).
     *
     * @param string|int $peer      The group or channel (or, for a live story, the user, group or channel it is posted as).
     * @param bool  $revoke    Whether to generate a new stream key, invalidating the previous one.
     * @param bool  $liveStory Whether the key is for a live story rather than a video chat/livestream.
     *
     * @return array{url: string, key: string}
     */
    public function getGroupCallStreamRtmpUrl(string|int $peer, bool $revoke = false, bool $liveStory = false): array
    {
        $result = $this->methodCallAsyncRead('phone.getGroupCallStreamRtmpUrl', ['peer' => $peer, 'revoke' => $revoke, 'live_story' => $liveStory]);
        return ['url' => (string) $result['url'], 'key' => (string) $result['key']];
    }

    /**
     * Start a [live story Â»](https://core.telegram.org/api/group-calls#live-stories): a livestream posted
     * as a story, of which we are the single publisher (everyone else joins as a listener).
     *
     * @param string|int                $peer                  Who to post the live story as: ourselves, or a group or channel we administer.
     * @param string|null               $caption               Caption of the story.
     * @param ParseMode|null            $parseMode             Whether to parse HTML or Markdown markup in the caption.
     * @param list<array<string, mixed>> $privacyRules          Who may see the story, as [InputPrivacyRule](https://core.telegram.org/type/InputPrivacyRule)s; everyone by default.
     * @param bool                      $pinned                Whether to pin the story to the profile.
     * @param bool                      $noForwards            Whether to forbid forwarding and screenshots.
     * @param bool                      $rtmpStream            Whether the media is published by an external RTMP application (see {@see self::getGroupCallStreamRtmpUrl()}) rather than by us.
     * @param bool|null                 $messagesEnabled       Whether viewers may comment with in-call messages.
     * @param int|null                  $sendPaidMessagesStars The minimum Telegram Stars donation required to comment, if any.
     */
    public function startLive(string|int $peer, ?string $caption = null, ?ParseMode $parseMode = null, array $privacyRules = [['_' => 'inputPrivacyValueAllowAll']], bool $pinned = false, bool $noForwards = false, bool $rtmpStream = false, ?bool $messagesEnabled = null, ?int $sendPaidMessagesStars = null): LiveStory
    {
        $params = [
            'peer' => $peer,
            'privacy_rules' => $privacyRules,
            'random_id' => random_int(PHP_INT_MIN, PHP_INT_MAX),
            'pinned' => $pinned,
            'noforwards' => $noForwards,
            'rtmp_stream' => $rtmpStream,
        ];
        if ($caption !== null) {
            $entities = [];
            if ($parseMode === ParseMode::MARKDOWN) {
                $parsed = TextEntities::fromMarkdown($caption);
                [$caption, $entities] = [$parsed->message, $parsed->entities];
            } elseif ($parseMode === ParseMode::HTML) {
                $parsed = TextEntities::fromHtml($caption);
                [$caption, $entities] = [$parsed->message, $parsed->entities];
            }
            $params['caption'] = $caption;
            $params['entities'] = $entities;
        }
        if ($messagesEnabled !== null) {
            $params['messages_enabled'] = $messagesEnabled;
        }
        if ($sendPaidMessagesStars !== null) {
            $params['send_paid_messages_stars'] = $sendPaidMessagesStars;
        }
        $updates = $this->methodCallAsyncRead('stories.startLive', $params);
        foreach ($updates['updates'] ?? [] as $update) {
            if ($update['_'] === 'updateGroupCall' && $update['call']['_'] === 'groupCall') {
                $inputCall = ['_' => 'inputGroupCall', 'id' => $update['call']['id'], 'access_hash' => $update['call']['access_hash']];
                $controller = $this->getGroupCallByInput($inputCall, $this->getIdInternal($peer), liveStory: true);
                if ($controller !== null && $controller->public instanceof LiveStory) {
                    return $controller->public;
                }
            }
        }
        throw new AssertionError('The server did not return the started live story!');
    }

    /**
     * Record one participant of a group call (or, in stream mode, its mixed stream) into a single file
     * (or stream) with a fixed set of tracks, see {@see \danog\MadelineProto\EventHandler\Calls\AbstractGroupCall::setOutput()}.
     *
     * @param ?StreamMask $streams The streams to record, as a bitmask of {@see CallStream} flags, or null for every available one.
     *
     * @return StreamMask The streams the participant currently sends, as a bitmask of {@see CallStream} flags.
     */
    public function groupCallSetOutput(int $id, LocalFile|WritableStream $file, string|int|null $participant = null, ?RecordingFormat $format = null, ?int $streams = null): int
    {
        return $this->getGroupCallController($id)->setOutput($file, $participant, $format, $streams);
    }

    /**
     * Record group call media into a directory: every transmitting participant (or only the given
     * one) as its own numbered series of files, see
     * {@see \danog\MadelineProto\EventHandler\Calls\AbstractGroupCall::setOutputFolder()}.
     */
    public function groupCallSetOutputFolder(int $id, LocalDirectory $dir, string|int|null $participant = null, ?RecordingFormat $format = null): void
    {
        $this->getGroupCallController($id)->setOutputFolder($dir, $participant, $format);
    }

    /**
     * Remove participants from a group call, by kicking them from its group or channel.
     *
     * @internal
     */
    public function removeGroupCallParticipants(int $id, string|int ...$participants): void
    {
        $this->getGroupCallController($id)->removeParticipant(...$participants);
    }

    /**
     * Whether a screen-share is currently being transmitted in a group call.
     *
     * @internal
     *
     * @psalm-mutation-free
     */
    public function isGroupCallSharingScreen(int $id): bool
    {
        return ($this->groupCalls[$id] ?? null)?->isSharingScreen() ?? false;
    }

    /**
     * Start sharing a screen in a group call.
     *
     * @internal
     */
    public function enableGroupCallPresentation(int $id): void
    {
        $this->getGroupCallController($id)->enablePresentation();
    }

    /**
     * Stop sharing the screen in a group call.
     *
     * @internal
     */
    public function disableGroupCallPresentation(int $id): void
    {
        $this->getGroupCallController($id)->disablePresentation();
    }

    /**
     * Play a file in a group call.
     */
    public function groupCallPlay(int $id, LocalFile|RemoteUrl|ReadableStream $file, MediaDestination $dest = MediaDestination::Camera): void
    {
        $this->getGroupCallController($id)->play($file, $dest);
    }

    /**
     * Play a file in a group call, blocking until the file has finished playing if a stream is provided.
     *
     * @internal
     */
    public function groupCallPlayBlocking(int $id, LocalFile|RemoteUrl|ReadableStream $file, MediaDestination $dest = MediaDestination::Camera): void
    {
        $this->getGroupCallController($id)->playBlocking($file, $dest);
    }

    /**
     * Files to play on hold in a group call.
     */
    public function groupCallPlayOnHold(int $id, MediaDestination $dest = MediaDestination::Camera, LocalFile|RemoteUrl|ReadableStream ...$files): void
    {
        $this->getGroupCallController($id)->playOnHold($dest, ...$files);
    }

    /**
     * Skip to the next file in the playlist of a group call.
     */
    public function groupCallSkipPlay(int $id, MediaDestination $dest = MediaDestination::Camera): void
    {
        $this->getGroupCallController($id)->skip($dest);
    }

    /**
     * Stop playing all files in a group call, clearing the main and the hold playlist.
     */
    public function groupCallStopPlay(int $id, MediaDestination $dest = MediaDestination::Camera): void
    {
        $this->getGroupCallController($id)->stop($dest);
    }

    /**
     * Pause playback of the current audio file in a group call.
     *
     * @psalm-external-mutation-free
     */
    public function groupCallPausePlay(int $id, MediaDestination $dest = MediaDestination::Camera): void
    {
        $this->getGroupCallController($id)->pause($dest);
    }

    /**
     * Resume playback of the current audio file in a group call.
     *
     * @psalm-external-mutation-free
     */
    public function groupCallResumePlay(int $id, MediaDestination $dest = MediaDestination::Camera): void
    {
        $this->getGroupCallController($id)->resume($dest);
    }

    /**
     * Whether the currently playing audio file of a group call is paused.
     *
     * @psalm-mutation-free
     */
    public function isGroupCallPlayPaused(int $id, MediaDestination $dest = MediaDestination::Camera): bool
    {
        return $this->getGroupCallController($id)->isPaused($dest);
    }

    /**
     * Get the file that is currently being played in a group call.
     *
     * @psalm-mutation-free
     */
    public function groupCallGetCurrent(int $id, MediaDestination $dest = MediaDestination::Camera): RemoteUrl|LocalFile|string|null
    {
        return $this->getGroupCallController($id)->getCurrent($dest);
    }

    /**
     * Handle an incoming group call update.
     *
     * @internal
     */
    public function handleGroupCallUpdate(array $update): void
    {
        switch ($update['_']) {
            case 'updateGroupCall':
                $call = $update['call'];
                $id = $call['id'];
                if (isset($this->conferenceCalls[$id])) {
                    EventLoop::queue($this->conferenceCalls[$id]->onGroupCallUpdate(...), $call);
                    return;
                }
                if (!isset($this->groupCalls[$id])) {
                    // Every member of a chat gets the updates of its group call, whether it is in it or
                    // not, and so does every session of an account in it: never start tracking (let
                    // alone resume) a call from its updates, or this session would own a controller
                    // of a call joined by another session. Only remember how to track it on first use.
                    $peerId = isset($update['peer']) ? $this->getIdInternal($update['peer']) : null;
                    if ($call['_'] !== 'groupCall' || ($call['conference'] ?? false)) {
                        if ($call['_'] === 'groupCallDiscarded') {
                            $peerId ??= $this->seenGroupCalls[$id][1] ?? null;
                            $this->logger->logger("Group call $id of chat ".($peerId ?? 'unknown').', which we are not in, ended', Logger::VERBOSE);
                        }
                        unset($this->seenGroupCalls[$id]);
                    } else {
                        if (!isset($this->seenGroupCalls[$id])) {
                            $title = isset($call['title']) ? " \"{$call['title']}\"" : '';
                            $this->logger->logger("Group call $id$title of chat ".($peerId ?? 'unknown').' is running, we are not in it', Logger::VERBOSE);
                        }
                        $this->rememberGroupCall(
                            $id,
                            $call['access_hash'],
                            $peerId,
                            (bool) ($update['live_story'] ?? false),
                        );
                    }
                    return;
                }
                // Deferred: applying an update may refetch the call or renegotiate the WebRTC
                // session, neither of which should run inline in the update loop. Queueing keeps
                // them ordered.
                EventLoop::queue($this->groupCalls[$id]->onGroupCallUpdate(...), $call);
                break;
            case 'updateGroupCallParticipants':
                $id = $update['call']['id'];
                if (isset($this->conferenceCalls[$id])) {
                    EventLoop::queue(
                        $this->conferenceCalls[$id]->onParticipantsUpdate(...),
                        $update['participants'],
                        $update['version']
                    );
                    return;
                }
                if (!isset($this->groupCalls[$id])) {
                    $this->logger->logger("Ignoring participants update for untracked group call $id of chat ".($this->seenGroupCalls[$id][1] ?? 'unknown'), Logger::VERBOSE);
                    return;
                }
                EventLoop::queue(
                    $this->groupCalls[$id]->onParticipantsUpdate(...),
                    $update['participants'],
                    $update['version']
                );
                break;
            case 'updateGroupCallChainBlocks':
                $id = $update['call']['id'];
                if (!isset($this->conferenceCalls[$id])) {
                    return;
                }
                EventLoop::queue(
                    $this->conferenceCalls[$id]->onChainBlocks(...),
                    $update['sub_chain_id'],
                    array_map('strval', $update['blocks']),
                    $update['next_offset']
                );
                break;
            case 'updateGroupCallEncryptedMessage':
                $id = $update['call']['id'];
                if (!isset($this->conferenceCalls[$id])) {
                    return;
                }
                $fromId = $this->getIdInternal($update['from_id']) ?? 0;
                $encrypted = (string) $update['encrypted_message'];
                EventLoop::queue(function () use ($id, $fromId, $encrypted, $update): void {
                    $message = ($this->conferenceCalls[$id] ?? null)?->onEncryptedMessage($fromId, $encrypted);
                    if ($message === null) {
                        return;
                    }
                    // Surface it exactly like a plain in-call message, flagged as end-to-end encrypted.
                    $this->saveUpdate([
                        '_' => 'updateGroupCallMessage',
                        'call' => $update['call'],
                        'encrypted' => true,
                        'message' => [
                            '_' => 'groupCallMessage',
                            'id' => 0,
                            'from_id' => $update['from_id'],
                            'date' => time(),
                            'message' => ['_' => 'textWithEntities', 'text' => $message['text'], 'entities' => $message['entities']],
                        ],
                    ]);
                });
                break;
        }
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\GroupCall;

/**
 * A minimal, in-memory OGG demuxer for the OPUS audio chunks of a group call in
 * [stream mode](https://core.telegram.org/api/group-calls#stream-mode): splits a chunk into its
 * logical streams, each with its OpusHead/OpusTags headers and its data packets (with their duration).
 *
 * @internal
 */
final class OggDemuxer
{
    /**
     * @return array<int, array{head: ?string, tags: ?string, packets: list<array{data: string, samples: int}>}> By bitstream serial number.
     *
     * @psalm-pure
     */
    public static function demux(string $data): array
    {
        $streams = [];
        $partial = [];
        $offset = 0;
        $length = \strlen($data);
        while ($offset + 27 <= $length) {
            if (substr($data, $offset, 4) !== 'OggS') {
                throw new \RuntimeException('Bad OGG capture pattern at offset '.$offset);
            }
            $header = unpack('Cversion/Ctype/Pgranule/Vserial/Vseq/Vcrc/Csegments', substr($data, $offset + 4, 23));
            $offset += 27;
            $serial = (int) $header['serial'];
            $segmentCount = (int) $header['segments'];
            $segments = array_map('intval', array_values(unpack('C*', substr($data, $offset, $segmentCount))));
            $offset += $segmentCount;
            $streams[$serial] ??= ['head' => null, 'tags' => null, 'packets' => []];
            $partial[$serial] ??= '';
            foreach ($segments as $size) {
                $partial[$serial] .= substr($data, $offset, $size);
                $offset += $size;
                if ($size === 255) {
                    continue; // the packet continues in the next segment (or page)
                }
                $packet = $partial[$serial];
                $partial[$serial] = '';
                if ($packet === '') {
                    continue;
                }
                if ($streams[$serial]['head'] === null && str_starts_with($packet, 'OpusHead')) {
                    $streams[$serial]['head'] = $packet;
                } elseif ($streams[$serial]['tags'] === null && str_starts_with($packet, 'OpusTags')) {
                    $streams[$serial]['tags'] = $packet;
                } else {
                    $streams[$serial]['packets'][] = ['data' => $packet, 'samples' => self::opusSamples($packet)];
                }
            }
        }
        return $streams;
    }

    /**
     * The number of 48kHz samples an OPUS packet lasts, from its TOC byte.
     *
     * @psalm-pure
     */
    public static function opusSamples(string $packet): int
    {
        if ($packet === '') {
            return 0;
        }
        $toc = \ord($packet[0]);
        $config = $toc >> 3;
        if ($config < 12) {
            $frameUs = ($config % 4) === 0 ? 10_000 : ($config % 4) * 20_000;
        } elseif ($config < 16) {
            $frameUs = (1 << ($config % 2)) * 10_000;
        } else {
            $frameUs = (1 << ($config % 4)) * 2_500;
        }
        $frames = match ($toc & 3) {
            0 => 1,
            1, 2 => 2,
            default => isset($packet[1]) ? (\ord($packet[1]) & 0x3F) : 1,
        };
        return intdiv($frames * $frameUs * 48, 1000);
    }

    /**
     * A family-0 OpusHead packet for a 48kHz stream with the given channels.
     *
     * @psalm-pure
     */
    public static function opusHead(int $channels): string
    {
        return 'OpusHead'.pack('CCvVvC', 1, $channels, 312, 48000, 0, 0);
    }

    /**
     * The channel count declared by an OpusHead packet.
     *
     * @psalm-pure
     */
    public static function opusChannels(string $opusHead): int
    {
        return \strlen($opusHead) >= 10 ? \ord($opusHead[9]) : 2;
    }

    /**
     * The comments (`KEY=value`) of an OpusTags packet.
     *
     * @return array<string, string>
     *
     * @psalm-pure
     */
    public static function opusComments(string $opusTags): array
    {
        $comments = [];
        $length = \strlen($opusTags);
        if ($length < 16) {
            return $comments;
        }
        $offset = 8;
        $vendorLength = (int) unpack('V', substr($opusTags, $offset, 4))[1];
        $offset += 4 + $vendorLength;
        if ($offset + 4 > $length) {
            return $comments;
        }
        $count = (int) unpack('V', substr($opusTags, $offset, 4))[1];
        $offset += 4;
        for ($i = 0; $i < $count && $offset + 4 <= $length; $i++) {
            $commentLength = (int) unpack('V', substr($opusTags, $offset, 4))[1];
            $offset += 4;
            $comment = substr($opusTags, $offset, $commentLength);
            $offset += $commentLength;
            $equals = strpos($comment, '=');
            if ($equals !== false) {
                $comments[substr($comment, 0, $equals)] = substr($comment, $equals + 1);
            }
        }
        return $comments;
    }
}
<?php declare(strict_types=1);

namespace danog\MadelineProto\Db;

if (class_exists('\\danog\\MadelineProto\\Db\\NullCache\\MysqlArray')) {
    return;
}

use danog\AsyncOrm\Internal\Containers\CacheContainer;
use danog\AsyncOrm\Internal\Driver\MysqlArray;
use danog\AsyncOrm\Internal\Driver\PostgresArray;
use danog\AsyncOrm\Internal\Driver\RedisArray;
use danog\MadelineProto\GroupCall\GroupCallController;
use danog\MadelineProto\Tgcalls\PrivateCallController;

class_alias(MysqlArray::class, '\\danog\\MadelineProto\\Db\\NullCache\\MysqlArray');
class_alias(PostgresArray::class, '\\danog\\MadelineProto\\Db\\NullCache\\PostgresArray');
class_alias(RedisArray::class, '\\danog\\MadelineProto\\Db\\NullCache\\RedisArray');

class_alias(MysqlArray::class, '\\danog\\MadelineProto\\Db\\MysqlArray');
class_alias(PostgresArray::class, '\\danog\\MadelineProto\\Db\\PostgresArray');
class_alias(PostgresArray::class, '\\danog\\MadelineProto\\Db\\PostgresArrayBytea');
class_alias(RedisArray::class, '\\danog\\MadelineProto\\Db\\RedisArray');
class_alias(CacheContainer::class, '\\danog\\MadelineProto\\Db\\CacheContainer');

require __DIR__.'/polyfill_phpseclib3.php';

class_alias(PrivateCallController::class, '\\danog\\MadelineProto\\VoIPController');

// Backwards-compatibility aliases for the call classes moved under EventHandler\Calls (and the
// GroupCallController moved under GroupCall). PrivateCall was previously named VoIP.
class_alias(\danog\MadelineProto\EventHandler\Calls\PrivateCall::class, '\\danog\\MadelineProto\\VoIP');
class_alias(\danog\MadelineProto\EventHandler\Calls\GroupCallState::class, '\\danog\\MadelineProto\\GroupCall\\GroupCallState');

if ((PHP_MINOR_VERSION === 2 && PHP_VERSION_ID < 80204)
    || PHP_MAJOR_VERSION < 8
    || (PHP_MAJOR_VERSION === 8 && PHP_MINOR_VERSION < 2)
) {
    echo('MadelineProto requires PHP 8.3.1+ (recommended) or 8.2.14+.'.PHP_EOL);
    die(1);
}
<?php
/**
 * This file is automatic generated by build_docs.php file
 * and is used only for autocomplete in multiple IDE
 * don't modify manually.
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\Namespace;

/** @psalm-mutable */
interface Chatlists
{
    /**
     * Export a [folder Â»](https://core.telegram.org/api/folders), creating a [chat folder deep link Â»](https://core.telegram.org/api/links#chat-folder-links).
     *
     * @param array{_: 'inputChatlistDialogFilter', filter_id?: int} $chatlist The folder to export @see https://docs.madelineproto.xyz/API_docs/types/InputChatlist.html
     * @param string $title An optional name for the link
     * @param list<array|int|string>|array<never, never> $peers Array of The list of channels, group and supergroups to share with the link. Basic groups will automatically be [converted to supergroups](https://core.telegram.org/api/channel#migration) when invoking the method. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'chatlists.exportedChatlistInvite', filter: array{_: 'dialogFilter', title: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}, contacts: bool, non_contacts: bool, groups: bool, broadcasts: bool, bots: bool, exclude_muted: bool, exclude_read: bool, exclude_archived: bool, title_noanimate: bool, id: int, emoticon?: string, color?: int, pinned_peers: list<array|int|string>, include_peers: list<array|int|string>, exclude_peers: list<array|int|string>}|array{_: 'dialogFilterDefault'}|array{_: 'dialogFilterChatlist', title: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}, has_my_invites: bool, title_noanimate: bool, id: int, emoticon?: string, color?: int, pinned_peers: list<array|int|string>, include_peers: list<array|int|string>}, invite: array{_: 'exportedChatlistInvite', title: string, url: string, peers: list<array|int|string>}} @see https://docs.madelineproto.xyz/API_docs/types/chatlists.ExportedChatlistInvite.html
     * @psalm-impure
     */
    public function exportChatlistInvite(array $chatlist, string|null $title = '', array $peers = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Delete a previously created [chat folder deep link Â»](https://core.telegram.org/api/links#chat-folder-links).
     *
     * @param array{_: 'inputChatlistDialogFilter', filter_id?: int} $chatlist The related folder @see https://docs.madelineproto.xyz/API_docs/types/InputChatlist.html
     * @param string $slug `slug` obtained from the [chat folder deep link Â»](https://core.telegram.org/api/links#chat-folder-links).
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function deleteExportedInvite(array $chatlist, string|null $slug = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Edit a [chat folder deep link Â»](https://core.telegram.org/api/links#chat-folder-links).
     *
     * @param array{_: 'inputChatlistDialogFilter', filter_id?: int} $chatlist Folder ID @see https://docs.madelineproto.xyz/API_docs/types/InputChatlist.html
     * @param string $slug `slug` obtained from the [chat folder deep link Â»](https://core.telegram.org/api/links#chat-folder-links).
     * @param string $title If set, sets a new name for the link
     * @param list<array|int|string> $peers Array of If set, changes the list of peers shared with the link @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'exportedChatlistInvite', title: string, url: string, peers: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/ExportedChatlistInvite.html
     * @psalm-impure
     */
    public function editExportedInvite(array $chatlist, string|null $slug = '', string|null $title = null, array|null $peers = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * List all [chat folder deep links Â»](https://core.telegram.org/api/links#chat-folder-links) associated to a folder
     *
     * @param array{_: 'inputChatlistDialogFilter', filter_id?: int} $chatlist The folder @see https://docs.madelineproto.xyz/API_docs/types/InputChatlist.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'chatlists.exportedInvites', invites: list<array{_: 'exportedChatlistInvite', title: string, url: string, peers: list<array|int|string>}>, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/chatlists.ExportedInvites.html
     * @psalm-impure
     */
    public function getExportedInvites(array $chatlist, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Obtain information about a [chat folder deep link Â»](https://core.telegram.org/api/links#chat-folder-links).
     *
     * @param string $slug `slug` obtained from the [chat folder deep link Â»](https://core.telegram.org/api/links#chat-folder-links)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'chatlists.chatlistInviteAlready', filter_id: int, missing_peers: list<array|int|string>, already_peers: list<array|int|string>, chats: list<array|int|string>, users: list<array|int|string>}|array{_: 'chatlists.chatlistInvite', title: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}, title_noanimate: bool, emoticon?: string, peers: list<array|int|string>, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/chatlists.ChatlistInvite.html
     * @psalm-impure
     */
    public function checkChatlistInvite(string|null $slug = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Import a [chat folder deep link Â»](https://core.telegram.org/api/links#chat-folder-links), joining some or all the chats in the folder.
     *
     * @param string $slug `slug` obtained from a [chat folder deep link Â»](https://core.telegram.org/api/links#chat-folder-links).
     * @param list<array|int|string>|array<never, never> $peers Array of List of new chats to join, fetched using [chatlists.checkChatlistInvite](https://docs.madelineproto.xyz/API_docs/methods/chatlists.checkChatlistInvite.html) and filtered as specified in the [documentation Â»](https://core.telegram.org/api/folders#shared-folders). @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function joinChatlistInvite(string|null $slug = '', array $peers = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Fetch new chats associated with an imported [chat folder deep link Â»](https://core.telegram.org/api/links#chat-folder-links). Must be invoked at most every `chatlist_update_period` seconds (as per the related [client configuration parameter Â»](https://core.telegram.org/api/config#chatlist-update-period)).
     *
     * @param array{_: 'inputChatlistDialogFilter', filter_id?: int} $chatlist The folder @see https://docs.madelineproto.xyz/API_docs/types/InputChatlist.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'chatlists.chatlistUpdates', missing_peers: list<array|int|string>, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/chatlists.ChatlistUpdates.html
     * @psalm-impure
     */
    public function getChatlistUpdates(array $chatlist, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Join channels and supergroups recently added to a [chat folder deep link Â»](https://core.telegram.org/api/links#chat-folder-links).
     *
     * @param array{_: 'inputChatlistDialogFilter', filter_id?: int} $chatlist The folder @see https://docs.madelineproto.xyz/API_docs/types/InputChatlist.html
     * @param list<array|int|string>|array<never, never> $peers Array of List of new chats to join, fetched using [chatlists.getChatlistUpdates](https://docs.madelineproto.xyz/API_docs/methods/chatlists.getChatlistUpdates.html) and filtered as specified in the [documentation Â»](https://core.telegram.org/api/folders#shared-folders). @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function joinChatlistUpdates(array $chatlist, array $peers = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Dismiss new pending peers recently added to a [chat folder deep link Â»](https://core.telegram.org/api/links#chat-folder-links).
     *
     * @param array{_: 'inputChatlistDialogFilter', filter_id?: int} $chatlist The folder @see https://docs.madelineproto.xyz/API_docs/types/InputChatlist.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function hideChatlistUpdates(array $chatlist, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Returns identifiers of pinned or always included chats from a chat folder imported using a [chat folder deep link Â»](https://core.telegram.org/api/links#chat-folder-links), which are suggested to be left when the chat folder is deleted.
     *
     * @param array{_: 'inputChatlistDialogFilter', filter_id?: int} $chatlist Folder ID @see https://docs.madelineproto.xyz/API_docs/types/InputChatlist.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return list<array|int|string> Array of  @see https://docs.madelineproto.xyz/API_docs/types/Peer.html
     * @psalm-impure
     */
    public function getLeaveChatlistSuggestions(array $chatlist, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Delete a folder imported using a [chat folder deep link Â»](https://core.telegram.org/api/links#chat-folder-links)
     *
     * @param array{_: 'inputChatlistDialogFilter', filter_id?: int} $chatlist Folder ID @see https://docs.madelineproto.xyz/API_docs/types/InputChatlist.html
     * @param list<array|int|string>|array<never, never> $peers Array of Also leave the specified channels and groups @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function leaveChatlist(array $chatlist, array $peers = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;
}
<?php
/**
 * This file is automatic generated by build_docs.php file
 * and is used only for autocomplete in multiple IDE
 * don't modify manually.
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\Namespace;

/** @psalm-mutable */
interface Wallet
{
    /**
     * 
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'walletStateEmpty', creating: bool}|array{_: 'walletState', backup_enabled: bool, can_export_phrase: bool, can_enable_backup: bool, address: string, public_key: string, balance: int} @see https://docs.madelineproto.xyz/API_docs/types/WalletState.html
     * @psalm-impure
     */
    public function getState(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param bool $force 
     * @param list<array|int|string>|array<never, never> $id Array of  @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param list<string>|array<never, never> $addresses 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'wallet.userAddresses', addresses: list<array{_: 'walletUserAddress', user_id?: int, address: string, public_key: string}>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/wallet.UserAddresses.html
     * @psalm-impure
     */
    public function getUserAddresses(bool|null $force = null, array $id = [], array $addresses = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param bool $inbound 
     * @param bool $outbound 
     * @param string $offset 
     * @param int $limit 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'wallet.transactions', balance: int, transactions: list<array{_: 'walletTransaction', peer: array{_: 'walletTransactionPeerUser', user_id: int, address: string, domain?: string}|array{_: 'walletTransactionPeerAddress', address: string, domain?: string}|array{_: 'walletTransactionPeerOnramp', address: string, domain?: string, provider_name: string}|array{_: 'walletTransactionPeerUnsupported'}|array{_: 'walletTransactionPeerChannel', channel_id: int, address: string, domain?: string}|array{_: 'walletTransactionPeerTelegramGift', own_profile: bool}, incoming: bool, gasless: bool, failed: bool, key_change: bool, comment_encrypted: bool, id: string, amount: int, fee: int, date: int, comment?: string, tx_hash?: string, nft?: array{_: 'wallet.nftItem', collection_address?: string, address: string, owner_address: string, index: string, name?: string, description?: string, image?: array{_: 'webDocument', url: string, access_hash: int, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: array, y: array, zoom: array, n: array}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}|array{_: 'webDocumentNoProxy', url: string, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: array, y: array, zoom: array, n: array}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}, image_small?: array{_: 'webDocument', url: string, access_hash: int, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: array, y: array, zoom: array, n: array}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}|array{_: 'webDocumentNoProxy', url: string, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: array, y: array, zoom: array, n: array}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}, content_url?: array{_: 'webDocument', url: string, access_hash: int, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: array, y: array, zoom: array, n: array}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}|array{_: 'webDocumentNoProxy', url: string, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: array, y: array, zoom: array, n: array}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}, lottie?: array{_: 'webDocument', url: string, access_hash: int, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: array, y: array, zoom: array, n: array}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}|array{_: 'webDocumentNoProxy', url: string, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: array, y: array, zoom: array, n: array}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}, attributes?: list<array{_: 'wallet.nftAttribute', trait_type: string, value: string}>, extra?: mixed, gift?: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}}}>, next_offset?: string, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/wallet.Transactions.html
     * @psalm-impure
     */
    public function getTransactions(bool|null $inbound = null, bool|null $outbound = null, string|null $offset = '', int|null $limit = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param list<string>|array<never, never> $id 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'wallet.transactions', balance: int, transactions: list<array{_: 'walletTransaction', peer: array{_: 'walletTransactionPeerUser', user_id: int, address: string, domain?: string}|array{_: 'walletTransactionPeerAddress', address: string, domain?: string}|array{_: 'walletTransactionPeerOnramp', address: string, domain?: string, provider_name: string}|array{_: 'walletTransactionPeerUnsupported'}|array{_: 'walletTransactionPeerChannel', channel_id: int, address: string, domain?: string}|array{_: 'walletTransactionPeerTelegramGift', own_profile: bool}, incoming: bool, gasless: bool, failed: bool, key_change: bool, comment_encrypted: bool, id: string, amount: int, fee: int, date: int, comment?: string, tx_hash?: string, nft?: array{_: 'wallet.nftItem', collection_address?: string, address: string, owner_address: string, index: string, name?: string, description?: string, image?: array{_: 'webDocument', url: string, access_hash: int, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: array, y: array, zoom: array, n: array}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}|array{_: 'webDocumentNoProxy', url: string, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: array, y: array, zoom: array, n: array}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}, image_small?: array{_: 'webDocument', url: string, access_hash: int, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: array, y: array, zoom: array, n: array}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}|array{_: 'webDocumentNoProxy', url: string, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: array, y: array, zoom: array, n: array}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}, content_url?: array{_: 'webDocument', url: string, access_hash: int, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: array, y: array, zoom: array, n: array}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}|array{_: 'webDocumentNoProxy', url: string, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: array, y: array, zoom: array, n: array}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}, lottie?: array{_: 'webDocument', url: string, access_hash: int, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: array, y: array, zoom: array, n: array}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}|array{_: 'webDocumentNoProxy', url: string, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: array, y: array, zoom: array, n: array}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}, attributes?: list<array{_: 'wallet.nftAttribute', trait_type: string, value: string}>, extra?: mixed, gift?: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}}}>, next_offset?: string, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/wallet.Transactions.html
     * @psalm-impure
     */
    public function getTransactionsByIDs(array $id = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param list<string>|array<never, never> $msg_hash 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'wallet.transactions', balance: int, transactions: list<array{_: 'walletTransaction', peer: array{_: 'walletTransactionPeerUser', user_id: int, address: string, domain?: string}|array{_: 'walletTransactionPeerAddress', address: string, domain?: string}|array{_: 'walletTransactionPeerOnramp', address: string, domain?: string, provider_name: string}|array{_: 'walletTransactionPeerUnsupported'}|array{_: 'walletTransactionPeerChannel', channel_id: int, address: string, domain?: string}|array{_: 'walletTransactionPeerTelegramGift', own_profile: bool}, incoming: bool, gasless: bool, failed: bool, key_change: bool, comment_encrypted: bool, id: string, amount: int, fee: int, date: int, comment?: string, tx_hash?: string, nft?: array{_: 'wallet.nftItem', collection_address?: string, address: string, owner_address: string, index: string, name?: string, description?: string, image?: array{_: 'webDocument', url: string, access_hash: int, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: array, y: array, zoom: array, n: array}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}|array{_: 'webDocumentNoProxy', url: string, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: array, y: array, zoom: array, n: array}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}, image_small?: array{_: 'webDocument', url: string, access_hash: int, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: array, y: array, zoom: array, n: array}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}|array{_: 'webDocumentNoProxy', url: string, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: array, y: array, zoom: array, n: array}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}, content_url?: array{_: 'webDocument', url: string, access_hash: int, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: array, y: array, zoom: array, n: array}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}|array{_: 'webDocumentNoProxy', url: string, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: array, y: array, zoom: array, n: array}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}, lottie?: array{_: 'webDocument', url: string, access_hash: int, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: array, y: array, zoom: array, n: array}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}|array{_: 'webDocumentNoProxy', url: string, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: array, y: array, zoom: array, n: array}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}, attributes?: list<array{_: 'wallet.nftAttribute', trait_type: string, value: string}>, extra?: mixed, gift?: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}}}>, next_offset?: string, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/wallet.Transactions.html
     * @psalm-impure
     */
    public function getTransactionsByMsgHash(array $msg_hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param string|array $password @see https://docs.madelineproto.xyz/API_docs/types/InputCheckPasswordSRP.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'wallet.secretPhraseParts', token: string, dcs: list<int>} @see https://docs.madelineproto.xyz/API_docs/types/wallet.SecretPhraseParts.html
     * @psalm-impure
     */
    public function exportSecretPhrase(string|array|null $password = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param string $token 
     * @param string $public_key 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'wallet.encryptedSecretPhrasePart', data: string} @see https://docs.madelineproto.xyz/API_docs/types/wallet.EncryptedSecretPhrasePart.html
     * @psalm-impure
     */
    public function fetchEncryptedSecretPhrasePart(string|null $token = '', string|null $public_key = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param array{_: 'inputWalletNew'}|array{_: 'inputWalletImported', proof: array{_: 'walletOwnershipProof', timestamp?: int, signature?: string}, public_key?: string, anchor_public_key?: string} $wallet @see https://docs.madelineproto.xyz/API_docs/types/InputWalletReplacement.html
     * @param string|array $password @see https://docs.madelineproto.xyz/API_docs/types/InputCheckPasswordSRP.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'walletStateEmpty', creating: bool}|array{_: 'walletState', backup_enabled: bool, can_export_phrase: bool, can_enable_backup: bool, address: string, public_key: string, balance: int} @see https://docs.madelineproto.xyz/API_docs/types/WalletState.html
     * @psalm-impure
     */
    public function replaceWallet(array $wallet, string|array|null $password = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param string|array $password @see https://docs.madelineproto.xyz/API_docs/types/InputCheckPasswordSRP.html
     * @param string $new_public_key 
     * @param array{_: 'walletOwnershipProof', timestamp?: int, signature?: string} $proof @see https://docs.madelineproto.xyz/API_docs/types/WalletOwnershipProof.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'walletStateEmpty', creating: bool}|array{_: 'walletState', backup_enabled: bool, can_export_phrase: bool, can_enable_backup: bool, address: string, public_key: string, balance: int} @see https://docs.madelineproto.xyz/API_docs/types/WalletState.html
     * @psalm-impure
     */
    public function disableBackup(string|array|null $password = null, string|null $new_public_key = null, array|null $proof = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return list<array{_: 'wallet.holderDc', dc: int, public_key: string}> Array of  @see https://docs.madelineproto.xyz/API_docs/types/wallet.HolderDc.html
     * @psalm-impure
     */
    public function getBackupHolderDcs(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param list<string>|array<never, never> $parts 
     * @param string $new_public_key 
     * @param array{_: 'walletOwnershipProof', timestamp?: int, signature?: string} $proof @see https://docs.madelineproto.xyz/API_docs/types/WalletOwnershipProof.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'walletStateEmpty', creating: bool}|array{_: 'walletState', backup_enabled: bool, can_export_phrase: bool, can_enable_backup: bool, address: string, public_key: string, balance: int} @see https://docs.madelineproto.xyz/API_docs/types/WalletState.html
     * @psalm-impure
     */
    public function enableBackup(array $parts = [], string|null $new_public_key = null, array|null $proof = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'wallet.proofChallenge', payload: string, expires: int, domain: string} @see https://docs.madelineproto.xyz/API_docs/types/wallet.ProofChallenge.html
     * @psalm-impure
     */
    public function getProofChallenge(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param string $data_normal 
     * @param string $data_gasless 
     * @param array|int|string $user_id @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function sendTransfer(string|null $data_normal = '', string|null $data_gasless = null, array|int|string|null $user_id = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function getGaslessInfo(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'wallet.existingBalance', has_balance: bool, url: string} @see https://docs.madelineproto.xyz/API_docs/types/wallet.ExistingBalance.html
     * @psalm-impure
     */
    public function getExistingWaltBalance(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param string $offset 
     * @param int $limit 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'wallet.nftItems', items: list<array{_: 'wallet.nftItem', collection_address?: string, address: string, owner_address: string, index: string, name?: string, description?: string, image?: array{_: 'webDocument', url: string, access_hash: int, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n: int}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}|array{_: 'webDocumentNoProxy', url: string, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n: int}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}, image_small?: array{_: 'webDocument', url: string, access_hash: int, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n: int}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}|array{_: 'webDocumentNoProxy', url: string, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n: int}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}, content_url?: array{_: 'webDocument', url: string, access_hash: int, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n: int}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}|array{_: 'webDocumentNoProxy', url: string, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n: int}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}, lottie?: array{_: 'webDocument', url: string, access_hash: int, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n: int}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}|array{_: 'webDocumentNoProxy', url: string, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n: int}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}, attributes?: list<array{_: 'wallet.nftAttribute', trait_type: string, value: string}>, extra?: mixed, gift?: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}}>, next_offset?: string} @see https://docs.madelineproto.xyz/API_docs/types/wallet.NftItems.html
     * @psalm-impure
     */
    public function getNfts(string|null $offset = '', int|null $limit = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param string $dapp_client_id 
     * @param string $manifest_url 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'tonConnectSession', pending: bool, closing: bool, closed: bool, id: int, dapp_client_id: string, client_id?: string, nonce: string, manifest?: array{_: 'tonConnectManifest', url: string, name: string, icon?: array{_: 'webDocument', url: string, access_hash: int, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n: int}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}|array{_: 'webDocumentNoProxy', url: string, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n: int}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}}, manifest_error?: int, date: int} @see https://docs.madelineproto.xyz/API_docs/types/TonConnectSession.html
     * @psalm-impure
     */
    public function tonConnectCreateSession(string|null $dapp_client_id = '', string|null $manifest_url = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param int $session_id 
     * @param string $client_id 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'wallet.tonConnectChallenge', challenge: string, event_id: int} @see https://docs.madelineproto.xyz/API_docs/types/wallet.TonConnectChallenge.html
     * @psalm-impure
     */
    public function tonConnectRegisterKey(int|null $session_id = 0, string|null $client_id = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param bool $error 
     * @param int $session_id 
     * @param string $challenge_answer 
     * @param string $body 
     * @param string $trace_id 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function tonConnectSubmitConnectResult(bool|null $error = null, int|null $session_id = 0, string|null $challenge_answer = '', string|null $body = '', string|null $trace_id = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * 
     *
     * @param string $dapp_client_id 
     * @param int $session_id 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'wallet.tonConnectPending', session: array{_: 'tonConnectSession', pending: bool, closing: bool, closed: bool, id: int, dapp_client_id: string, client_id?: string, nonce: string, manifest?: array{_: 'tonConnectManifest', url: string, name: string, icon?: array{_: 'webDocument', url: string, access_hash: int, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: array, y: array, zoom: array, n: array}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}|array{_: 'webDocumentNoProxy', url: string, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: array, y: array, zoom: array, n: array}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}}, manifest_error?: int, date: int}, requests: list<array{_: 'tonConnectRequest', session_id: int, msg_id: int, body: string, expires: int, topic?: string, trace_id?: string}>} @see https://docs.madelineproto.xyz/API_docs/types/wallet.TonConnectPending.html
     * @psalm-impure
     */
    public function tonConnectGetPending(string|null $dapp_client_id = null, int|null $session_id = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param bool $declined 
     * @param int $session_id 
     * @param int $msg_id 
     * @param string $app_request_id 
     * @param string $challenge_answer 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function tonConnectClaimRequest(bool|null $declined = null, int|null $session_id = 0, int|null $msg_id = 0, string|null $app_request_id = '', string|null $challenge_answer = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * 
     *
     * @param int $session_id 
     * @param int $msg_id 
     * @param string $body 
     * @param string $trace_id 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function tonConnectSubmitResponse(int|null $session_id = 0, int|null $msg_id = 0, string|null $body = '', string|null $trace_id = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * 
     *
     * @param int $session_id 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'tonConnectNextEventId', event_id: int} @see https://docs.madelineproto.xyz/API_docs/types/TonConnectNextEventId.html
     * @psalm-impure
     */
    public function tonConnectNextEventId(int|null $session_id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param int $session_id 
     * @param string $body 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function tonConnectCloseSession(int|null $session_id = 0, string|null $body = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * 
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'wallet.tonConnectSessions', sessions: list<array{_: 'tonConnectSession', pending: bool, closing: bool, closed: bool, id: int, dapp_client_id: string, client_id?: string, nonce: string, manifest?: array{_: 'tonConnectManifest', url: string, name: string, icon?: array{_: 'webDocument', url: string, access_hash: int, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: array, y: array, zoom: array, n: array}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}|array{_: 'webDocumentNoProxy', url: string, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: array, y: array, zoom: array, n: array}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}}, manifest_error?: int, date: int}>} @see https://docs.madelineproto.xyz/API_docs/types/wallet.TonConnectSessions.html
     * @psalm-impure
     */
    public function tonConnectGetSessions(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param list<array{_: 'inputWalletCardSticker', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, nft_address?: string}>|array<never, never> $stickers Array of  @see https://docs.madelineproto.xyz/API_docs/types/InputWalletCardSticker.html
     * @param string $bg_nft_address 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'wallet.card', stickers?: list<array{_: 'wallet.cardSticker', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, nft: array{_: 'wallet.nftItem', collection_address?: string, address: string, owner_address: string, index: string, name?: string, description?: string, image?: array{_: 'webDocument', url: string, access_hash: int, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: array, y: array, zoom: array, n: array}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}|array{_: 'webDocumentNoProxy', url: string, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: array, y: array, zoom: array, n: array}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}, image_small?: array{_: 'webDocument', url: string, access_hash: int, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: array, y: array, zoom: array, n: array}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}|array{_: 'webDocumentNoProxy', url: string, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: array, y: array, zoom: array, n: array}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}, content_url?: array{_: 'webDocument', url: string, access_hash: int, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: array, y: array, zoom: array, n: array}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}|array{_: 'webDocumentNoProxy', url: string, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: array, y: array, zoom: array, n: array}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}, lottie?: array{_: 'webDocument', url: string, access_hash: int, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: array, y: array, zoom: array, n: array}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}|array{_: 'webDocumentNoProxy', url: string, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: array, y: array, zoom: array, n: array}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}, attributes?: list<array{_: 'wallet.nftAttribute', trait_type: string, value: string}>, extra?: mixed, gift?: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}}}>, bg_nft_item?: array{_: 'wallet.nftItem', collection_address?: string, address: string, owner_address: string, index: string, name?: string, description?: string, image?: array{_: 'webDocument', url: string, access_hash: int, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n: int}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}|array{_: 'webDocumentNoProxy', url: string, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n: int}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}, image_small?: array{_: 'webDocument', url: string, access_hash: int, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n: int}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}|array{_: 'webDocumentNoProxy', url: string, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n: int}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}, content_url?: array{_: 'webDocument', url: string, access_hash: int, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n: int}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}|array{_: 'webDocumentNoProxy', url: string, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n: int}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}, lottie?: array{_: 'webDocument', url: string, access_hash: int, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n: int}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}|array{_: 'webDocumentNoProxy', url: string, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n: int}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}, attributes?: list<array{_: 'wallet.nftAttribute', trait_type: string, value: string}>, extra?: mixed, gift?: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}}, address: string, balance: int} @see https://docs.madelineproto.xyz/API_docs/types/wallet.Card.html
     * @psalm-impure
     */
    public function setCardStickers(array $stickers = [], string|null $bg_nft_address = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;
}
<?php
/**
 * This file is automatic generated by build_docs.php file
 * and is used only for autocomplete in multiple IDE
 * don't modify manually.
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\Namespace;

/** @psalm-mutable */
interface Stickers
{
    /**
     * Create a stickerset.
     *
     * @param bool $masks Whether this is a mask stickerset
     * @param bool $emojis Whether this is a [custom emoji](https://core.telegram.org/api/custom-emoji) stickerset.
     * @param bool $text_color Whether the color of TGS custom emojis contained in this set should be changed to the text color when used in messages, the accent color if used as emoji status, white on chat photos, or another appropriate color based on context. For custom emoji stickersets only.
     * @param array|int|string $user_id Stickerset owner @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param string $title Stickerset name, `1-64` chars
     * @param string $short_name Short name of sticker set, to be used in [sticker deep links Â»](https://core.telegram.org/api/links#stickerset-links). Can contain only english letters, digits and underscores. Must begin with a letter, can't contain consecutive underscores and, **if called by a bot**, must end in `"_by_<bot_username>"`. `<bot_username>` is case insensitive. 1-64 characters.
     * @param array $thumb Thumbnail @see https://docs.madelineproto.xyz/API_docs/types/InputDocument.html
     * @param list<array{_: 'inputStickerSetItem', document?: array, emoji?: string, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n?: int}, keywords?: string}>|array<never, never> $stickers Array of Stickers @see https://docs.madelineproto.xyz/API_docs/types/InputStickerSetItem.html
     * @param string $software Used when [importing stickers using the sticker import SDKs](https://core.telegram.org/import-stickers), specifies the name of the software that created the stickers
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.stickerSet', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}, packs: list<array{_: 'stickerPack', emoticon: string, documents: list<int>}>, keywords: list<array{_: 'stickerKeyword', document_id: int, keyword: list<string>}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}|array{_: 'messages.stickerSetNotModified'} @see https://docs.madelineproto.xyz/API_docs/types/messages.StickerSet.html
     * @psalm-impure
     */
    public function createStickerSet(bool|null $masks = null, bool|null $emojis = null, bool|null $text_color = null, array|int|string|null $user_id = null, string|null $title = '', string|null $short_name = '', array|null $thumb = null, array $stickers = [], string|null $software = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Remove a sticker from the set where it belongs. The sticker set must have been created by the current user/bot.
     *
     * @param array $sticker The sticker to remove @see https://docs.madelineproto.xyz/API_docs/types/InputDocument.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.stickerSet', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}, packs: list<array{_: 'stickerPack', emoticon: string, documents: list<int>}>, keywords: list<array{_: 'stickerKeyword', document_id: int, keyword: list<string>}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}|array{_: 'messages.stickerSetNotModified'} @see https://docs.madelineproto.xyz/API_docs/types/messages.StickerSet.html
     * @psalm-impure
     */
    public function removeStickerFromSet(array|null $sticker = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Changes the absolute position of a sticker in the set to which it belongs. The sticker set must have been created by the current user/bot.
     *
     * @param array $sticker The sticker @see https://docs.madelineproto.xyz/API_docs/types/InputDocument.html
     * @param int $position The new position of the sticker, zero-based
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.stickerSet', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}, packs: list<array{_: 'stickerPack', emoticon: string, documents: list<int>}>, keywords: list<array{_: 'stickerKeyword', document_id: int, keyword: list<string>}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}|array{_: 'messages.stickerSetNotModified'} @see https://docs.madelineproto.xyz/API_docs/types/messages.StickerSet.html
     * @psalm-impure
     */
    public function changeStickerPosition(array|null $sticker = null, int|null $position = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Add a sticker to a stickerset. The sticker set must have been created by the current user/bot.
     *
     * @param array{_: 'inputStickerSetItem', document?: array, emoji?: string, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n?: int}, keywords?: string} $sticker The sticker @see https://docs.madelineproto.xyz/API_docs/types/InputStickerSetItem.html
     * @param array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id?: int, access_hash?: int}|array{_: 'inputStickerSetShortName', short_name?: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon?: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'} $stickerset The stickerset @see https://docs.madelineproto.xyz/API_docs/types/InputStickerSet.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.stickerSet', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}, packs: list<array{_: 'stickerPack', emoticon: string, documents: list<int>}>, keywords: list<array{_: 'stickerKeyword', document_id: int, keyword: list<string>}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}|array{_: 'messages.stickerSetNotModified'} @see https://docs.madelineproto.xyz/API_docs/types/messages.StickerSet.html
     * @psalm-impure
     */
    public function addStickerToSet(array $sticker, array|null $stickerset = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Set stickerset thumbnail
     *
     * @param array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id?: int, access_hash?: int}|array{_: 'inputStickerSetShortName', short_name?: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon?: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'} $stickerset Stickerset @see https://docs.madelineproto.xyz/API_docs/types/InputStickerSet.html
     * @param array $thumb Thumbnail (only for normal stickersets, not custom emoji stickersets). @see https://docs.madelineproto.xyz/API_docs/types/InputDocument.html
     * @param int $thumb_document_id Only for [custom emoji stickersets](https://core.telegram.org/api/custom-emoji), ID of a custom emoji present in the set to use as thumbnail; pass 0 to fallback to the first custom emoji of the set.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.stickerSet', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}, packs: list<array{_: 'stickerPack', emoticon: string, documents: list<int>}>, keywords: list<array{_: 'stickerKeyword', document_id: int, keyword: list<string>}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}|array{_: 'messages.stickerSetNotModified'} @see https://docs.madelineproto.xyz/API_docs/types/messages.StickerSet.html
     * @psalm-impure
     */
    public function setStickerSetThumb(array|null $stickerset = null, array|null $thumb = null, int|null $thumb_document_id = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Check whether the given short name is available
     *
     * @param string $short_name Short name
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function checkShortName(string|null $short_name = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Suggests a short name for a given stickerpack name
     *
     * @param string $title Sticker pack name
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'stickers.suggestedShortName', short_name: string} @see https://docs.madelineproto.xyz/API_docs/types/stickers.SuggestedShortName.html
     * @psalm-impure
     */
    public function suggestShortName(string|null $title = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Update the keywords, emojis or [mask coordinates](https://core.telegram.org/api/stickers#mask-stickers) of a sticker.
     *
     * @param array $sticker The sticker @see https://docs.madelineproto.xyz/API_docs/types/InputDocument.html
     * @param string $emoji If set, updates the emoji list associated to the sticker
     * @param array{_: 'maskCoords', x: float, y: float, zoom: float, n?: int} $mask_coords If set, updates the [mask coordinates](https://core.telegram.org/api/stickers#mask-stickers) @see https://docs.madelineproto.xyz/API_docs/types/MaskCoords.html
     * @param string $keywords If set, updates the sticker keywords (separated by commas). Can't be provided for mask stickers.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.stickerSet', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}, packs: list<array{_: 'stickerPack', emoticon: string, documents: list<int>}>, keywords: list<array{_: 'stickerKeyword', document_id: int, keyword: list<string>}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}|array{_: 'messages.stickerSetNotModified'} @see https://docs.madelineproto.xyz/API_docs/types/messages.StickerSet.html
     * @psalm-impure
     */
    public function changeSticker(array|null $sticker = null, string|null $emoji = null, array|null $mask_coords = null, string|null $keywords = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Renames a stickerset.
     *
     * @param array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id?: int, access_hash?: int}|array{_: 'inputStickerSetShortName', short_name?: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon?: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'} $stickerset Stickerset to rename @see https://docs.madelineproto.xyz/API_docs/types/InputStickerSet.html
     * @param string $title New stickerset title
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.stickerSet', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}, packs: list<array{_: 'stickerPack', emoticon: string, documents: list<int>}>, keywords: list<array{_: 'stickerKeyword', document_id: int, keyword: list<string>}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}|array{_: 'messages.stickerSetNotModified'} @see https://docs.madelineproto.xyz/API_docs/types/messages.StickerSet.html
     * @psalm-impure
     */
    public function renameStickerSet(array|null $stickerset = null, string|null $title = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Deletes a stickerset we created.
     *
     * @param array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id?: int, access_hash?: int}|array{_: 'inputStickerSetShortName', short_name?: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon?: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'} $stickerset Stickerset to delete @see https://docs.madelineproto.xyz/API_docs/types/InputStickerSet.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function deleteStickerSet(array|null $stickerset = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Replace a sticker in a [stickerset Â»](https://core.telegram.org/api/stickers).
     *
     * @param array{_: 'inputStickerSetItem', document?: array, emoji?: string, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n?: int}, keywords?: string} $new_sticker New sticker. @see https://docs.madelineproto.xyz/API_docs/types/InputStickerSetItem.html
     * @param array $sticker Old sticker document. @see https://docs.madelineproto.xyz/API_docs/types/InputDocument.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.stickerSet', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}, packs: list<array{_: 'stickerPack', emoticon: string, documents: list<int>}>, keywords: list<array{_: 'stickerKeyword', document_id: int, keyword: list<string>}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}|array{_: 'messages.stickerSetNotModified'} @see https://docs.madelineproto.xyz/API_docs/types/messages.StickerSet.html
     * @psalm-impure
     */
    public function replaceSticker(array $new_sticker, array|null $sticker = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;
}
<?php
/**
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */
namespace danog\MadelineProto\Namespace;

final class Blacklist {
    public const BLACKLIST = array (
  '__messages.getHistory' => 'Please use the [event handler](https://docs.madelineproto.xyz/docs/UPDATES.html)',
  '__channels.getMessages' => 'Please use the [event handler](https://docs.madelineproto.xyz/docs/UPDATES.html)',
  '__messages.getMessages' => 'Please use the [event handler](https://docs.madelineproto.xyz/docs/UPDATES.html)',
  'account.updatePasswordSettings' => 'You cannot use this method directly; use $MadelineProto->update2fa($params), instead (see https://docs.madelineproto.xyz for more info)',
  'account.getPasswordSettings' => 'You cannot use this method directly; use $MadelineProto->update2fa($params), instead (see https://docs.madelineproto.xyz for more info)',
  'messages.receivedQueue' => 'You cannot use this method directly',
  'messages.getDhConfig' => 'You cannot use this method directly, instead use $MadelineProto->getDhConfig();',
  'auth.bindTempAuthKey' => 'You cannot use this method directly, instead modify the PFS and default_temp_auth_key_expires_in settings, see https://docs.madelineproto.xyz/docs/SETTINGS.html for more info',
  'auth.exportAuthorization' => 'You cannot use this method directly, use $MadelineProto->exportAuthorization() instead, see https://docs.madelineproto.xyz/docs/LOGIN.html',
  'auth.importAuthorization' => 'You cannot use this method directly, use $MadelineProto->importAuthorization($authorization) instead, see https://docs.madelineproto.xyz/docs/LOGIN.html',
  'auth.logOut' => 'You cannot use this method directly, use the logout method instead (see https://docs.madelineproto.xyz for more info)',
  'auth.importBotAuthorization' => 'You cannot use this method directly, use the botLogin method instead (see https://docs.madelineproto.xyz for more info)',
  'auth.sendCode' => 'You cannot use this method directly, use the phoneLogin method instead (see https://docs.madelineproto.xyz for more info)',
  'auth.signIn' => 'You cannot use this method directly, use the completePhoneLogin method instead (see https://docs.madelineproto.xyz for more info)',
  'auth.checkPassword' => 'You cannot use this method directly, use the complete2falogin method instead (see https://docs.madelineproto.xyz for more info)',
  'auth.signUp' => 'You cannot use this method directly, use the completeSignup method instead (see https://docs.madelineproto.xyz for more info)',
  'users.getFullUser' => 'You cannot use this method directly, use the getPwrChat, getInfo, getFullInfo methods instead (see https://docs.madelineproto.xyz for more info)',
  'users.getUsers' => 'You cannot use this method directly, use the getPwrChat, getInfo, getFullInfo methods instead (see https://docs.madelineproto.xyz for more info)',
  'channels.getChannels' => 'You cannot use this method directly, use the getPwrChat, getInfo, getFullInfo methods instead (see https://docs.madelineproto.xyz for more info)',
  'channels.getFullChannel' => 'You cannot use this method directly, use the getPwrChat, getInfo, getFullInfo methods instead (see https://docs.madelineproto.xyz for more info)',
  'messages.getFullChat' => 'You cannot use this method directly, use the getPwrChat, getInfo, getFullInfo methods instead (see https://docs.madelineproto.xyz for more info)',
  'contacts.resolveUsername' => 'You cannot use this method directly, use the resolveUsername, getPwrChat, getInfo, getFullInfo methods instead (see https://docs.madelineproto.xyz for more info)',
  'messages.acceptEncryption' => 'You cannot use this method directly, see https://docs.madelineproto.xyz for more info on handling secret chats',
  'messages.discardEncryption' => 'You cannot use this method directly, see https://docs.madelineproto.xyz for more info on handling secret chats',
  'messages.requestEncryption' => 'You cannot use this method directly, see https://docs.madelineproto.xyz for more info on handling secret chats',
  'phone.requestCall' => 'You cannot use this method directly, see https://docs.madelineproto.xyz#calls for more info on handling calls',
  'phone.acceptCall' => 'You cannot use this method directly, see https://docs.madelineproto.xyz#calls for more info on handling calls',
  'phone.confirmCall' => 'You cannot use this method directly, see https://docs.madelineproto.xyz#calls for more info on handling calls',
  'phone.discardCall' => 'You cannot use this method directly, see https://docs.madelineproto.xyz#calls for more info on handling calls',
  'upload.getCdnFile' => 'You cannot use this method directly, use the upload, downloadToStream, downloadToFile, downloadToDir methods instead; see https://docs.madelineproto.xyz for more info',
  'upload.getFileHashes' => 'You cannot use this method directly, use the upload, downloadToStream, downloadToFile, downloadToDir methods instead; see https://docs.madelineproto.xyz for more info',
  'upload.getCdnFileHashes' => 'You cannot use this method directly, use the upload, downloadToStream, downloadToFile, downloadToDir methods instead; see https://docs.madelineproto.xyz for more info',
  'upload.reuploadCdnFile' => 'You cannot use this method directly, use the upload, downloadToStream, downloadToFile, downloadToDir methods instead; see https://docs.madelineproto.xyz for more info',
  'upload.getFile' => 'You cannot use this method directly, use the upload, downloadToStream, downloadToFile, downloadToDir methods instead; see https://docs.madelineproto.xyz for more info',
  'upload.saveFilePart' => 'You cannot use this method directly, use the upload, downloadToStream, downloadToFile, downloadToDir methods instead; see https://docs.madelineproto.xyz for more info',
  'upload.saveBigFilePart' => 'You cannot use this method directly, use the upload, downloadToStream, downloadToFile, downloadToDir methods instead; see https://docs.madelineproto.xyz for more info',
);
}
        <?php
/**
 * This file is automatic generated by build_docs.php file
 * and is used only for autocomplete in multiple IDE
 * don't modify manually.
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\Namespace;

/** @psalm-mutable */
interface Langpack
{
    /**
     * Get localization pack strings
     *
     * @param string $lang_pack Platform identifier (i.e. `android`, `tdesktop`, etc).
     * @param string $lang_code Either an ISO 639-1 language code or a language pack name obtained from a [language pack link](https://core.telegram.org/api/links#language-pack-links).
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'langPackDifference', lang_code: string, from_version: int, version: int, strings: list<array{_: 'langPackString', key: string, value: string}|array{_: 'langPackStringPluralized', key: string, zero_value?: string, one_value?: string, two_value?: string, few_value?: string, many_value?: string, other_value: string}|array{_: 'langPackStringDeleted', key: string}>} @see https://docs.madelineproto.xyz/API_docs/types/LangPackDifference.html
     * @psalm-impure
     */
    public function getLangPack(string|null $lang_pack = '', string|null $lang_code = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get strings from a language pack
     *
     * @param string $lang_pack Platform identifier (i.e. `android`, `tdesktop`, etc).
     * @param string $lang_code Either an ISO 639-1 language code or a language pack name obtained from a [language pack link](https://core.telegram.org/api/links#language-pack-links).
     * @param list<string>|array<never, never> $keys Strings to get
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return list<array{_: 'langPackString', key: string, value: string}|array{_: 'langPackStringPluralized', key: string, zero_value?: string, one_value?: string, two_value?: string, few_value?: string, many_value?: string, other_value: string}|array{_: 'langPackStringDeleted', key: string}> Array of  @see https://docs.madelineproto.xyz/API_docs/types/LangPackString.html
     * @psalm-impure
     */
    public function getStrings(string|null $lang_pack = '', string|null $lang_code = '', array $keys = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get new strings in language pack
     *
     * @param string $lang_pack Platform identifier (i.e. `android`, `tdesktop`, etc).
     * @param string $lang_code Either an ISO 639-1 language code or a language pack name obtained from a [language pack link](https://core.telegram.org/api/links#language-pack-links).
     * @param int $from_version Previous localization pack version
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'langPackDifference', lang_code: string, from_version: int, version: int, strings: list<array{_: 'langPackString', key: string, value: string}|array{_: 'langPackStringPluralized', key: string, zero_value?: string, one_value?: string, two_value?: string, few_value?: string, many_value?: string, other_value: string}|array{_: 'langPackStringDeleted', key: string}>} @see https://docs.madelineproto.xyz/API_docs/types/LangPackDifference.html
     * @psalm-impure
     */
    public function getDifference(string|null $lang_pack = '', string|null $lang_code = '', int|null $from_version = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get information about all languages in a localization pack
     *
     * @param string $lang_pack Platform identifier (i.e. `android`, `tdesktop`, etc).
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return list<array{_: 'langPackLanguage', official: bool, rtl: bool, beta: bool, name: string, native_name: string, lang_code: string, base_lang_code?: string, plural_code: string, strings_count: int, translated_count: int, translations_url: string}> Array of  @see https://docs.madelineproto.xyz/API_docs/types/LangPackLanguage.html
     * @psalm-impure
     */
    public function getLanguages(string|null $lang_pack = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get information about a language in a localization pack
     *
     * @param string $lang_pack Platform identifier (i.e. `android`, `tdesktop`, etc).
     * @param string $lang_code Either an ISO 639-1 language code or a language pack name obtained from a [language pack link](https://core.telegram.org/api/links#language-pack-links).
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'langPackLanguage', official: bool, rtl: bool, beta: bool, name: string, native_name: string, lang_code: string, base_lang_code?: string, plural_code: string, strings_count: int, translated_count: int, translations_url: string} @see https://docs.madelineproto.xyz/API_docs/types/LangPackLanguage.html
     * @psalm-impure
     */
    public function getLanguage(string|null $lang_pack = '', string|null $lang_code = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;
}
<?php
/**
 * This file is automatic generated by build_docs.php file
 * and is used only for autocomplete in multiple IDE
 * don't modify manually.
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\Namespace;

/** @psalm-mutable */
interface Smsjobs
{
    /**
     * Check if we can process SMS jobs (official clients only).
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'smsjobs.eligibleToJoin', terms_url: string, monthly_sent_sms: int} @see https://docs.madelineproto.xyz/API_docs/types/smsjobs.EligibilityToJoin.html
     * @psalm-impure
     */
    public function isEligibleToJoin(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Enable SMS jobs (official clients only).
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function join(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Disable SMS jobs (official clients only).
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function leave(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Update SMS job settings (official clients only).
     *
     * @param bool $allow_international Allow international numbers?
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function updateSettings(bool|null $allow_international = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Get SMS jobs status (official clients only).
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'smsjobs.status', allow_international: bool, recent_sent: int, recent_since: int, recent_remains: int, total_sent: int, total_since: int, last_gift_slug?: string, terms_url: string} @see https://docs.madelineproto.xyz/API_docs/types/smsjobs.Status.html
     * @psalm-impure
     */
    public function getStatus(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get info about an SMS job (official clients only).
     *
     * @param string $job_id Job ID
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'smsJob', job_id: string, phone_number: string, text: string} @see https://docs.madelineproto.xyz/API_docs/types/SmsJob.html
     * @psalm-impure
     */
    public function getSmsJob(string|null $job_id = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Finish an SMS job (official clients only).
     *
     * @param string $job_id Job ID.
     * @param string $error If failed, the error.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function finishJob(string|null $job_id = '', string|null $error = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;
}
<?php
/**
 * This file is automatic generated by build_docs.php file
 * and is used only for autocomplete in multiple IDE
 * don't modify manually.
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\Namespace;

/** @psalm-mutable */
interface Auth
{
    /**
     * Terminates all user's authorized sessions except for the current one.
     * 
     * After calling this method it is necessary to reregister the current device using the method [account.registerDevice](https://docs.madelineproto.xyz/API_docs/methods/account.registerDevice.html)
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function resetAuthorizations(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Request recovery code of a [2FA password](https://core.telegram.org/api/srp), only for accounts with a [recovery email configured](https://core.telegram.org/api/srp#email-verification).
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'auth.passwordRecovery', email_pattern: string} @see https://docs.madelineproto.xyz/API_docs/types/auth.PasswordRecovery.html
     * @psalm-impure
     */
    public function requestPasswordRecovery(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Reset the [2FA password](https://core.telegram.org/api/srp) using the recovery code sent using [auth.requestPasswordRecovery](https://docs.madelineproto.xyz/API_docs/methods/auth.requestPasswordRecovery.html).
     *
     * @param string $code Code received via email
     * @param array{_: 'account.passwordInputSettings', new_algo?: array{_: 'passwordKdfAlgoUnknown'}|array{_: 'passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow', salt1?: string, salt2?: string, g?: int, p?: string}, new_password_hash?: string, hint?: string, email?: string, new_secure_settings?: array{_: 'secureSecretSettings', secure_algo: array{_: 'securePasswordKdfAlgoUnknown'}|array{_: 'securePasswordKdfAlgoPBKDF2HMACSHA512iter100000', salt?: string}|array{_: 'securePasswordKdfAlgoSHA512', salt?: string}, secure_secret?: string, secure_secret_id?: int}} $new_settings New password @see https://docs.madelineproto.xyz/API_docs/types/account.PasswordInputSettings.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'auth.authorization', setup_password_required: bool, otherwise_relogin_days?: int, tmp_sessions?: int, future_auth_token?: string, user: array|int|string}|array{_: 'auth.authorizationSignUpRequired', terms_of_service?: array{_: 'help.termsOfService', id: mixed, popup: bool, text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, min_age_confirm?: int}} @see https://docs.madelineproto.xyz/API_docs/types/auth.Authorization.html
     * @psalm-impure
     */
    public function recoverPassword(string|null $code = '', array|null $new_settings = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Resend the login code via another medium, the phone code type is determined by the return value of the previous auth.sendCode/auth.resendCode: see [login](https://core.telegram.org/api/auth) for more info.
     *
     * @param string $phone_number The phone number
     * @param string $phone_code_hash The phone code hash obtained from [auth.sendCode](https://docs.madelineproto.xyz/API_docs/methods/auth.sendCode.html)
     * @param string $reason Official clients only, used if the device integrity verification failed, and no secret could be obtained to invoke [auth.requestFirebaseSms](https://docs.madelineproto.xyz/API_docs/methods/auth.requestFirebaseSms.html): in this case, the device integrity verification failure reason must be passed here.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'auth.sentCode', type: array{_: 'auth.sentCodeTypeApp', length: int}|array{_: 'auth.sentCodeTypeSms', length: int}|array{_: 'auth.sentCodeTypeCall', length: int}|array{_: 'auth.sentCodeTypeFlashCall', pattern: string}|array{_: 'auth.sentCodeTypeMissedCall', prefix: string, length: int}|array{_: 'auth.sentCodeTypeEmailCode', apple_signin_allowed: bool, google_signin_allowed: bool, email_pattern: string, length: int, reset_available_period?: int, reset_pending_date?: int}|array{_: 'auth.sentCodeTypeSetUpEmailRequired', apple_signin_allowed: bool, google_signin_allowed: bool}|array{_: 'auth.sentCodeTypeFragmentSms', url: string, length: int}|array{_: 'auth.sentCodeTypeFirebaseSms', nonce?: string, play_integrity_project_id?: int, play_integrity_nonce?: string, receipt?: string, push_timeout?: int, length: int}|array{_: 'auth.sentCodeTypeSmsWord', beginning?: string}|array{_: 'auth.sentCodeTypeSmsPhrase', beginning?: string}, phone_code_hash: string, next_type?: array{_: 'auth.codeTypeSms'}|array{_: 'auth.codeTypeCall'}|array{_: 'auth.codeTypeFlashCall'}|array{_: 'auth.codeTypeMissedCall'}|array{_: 'auth.codeTypeFragmentSms'}, timeout?: int}|array{_: 'auth.sentCodeSuccess', authorization: array{_: 'auth.authorization', setup_password_required: bool, otherwise_relogin_days?: int, tmp_sessions?: int, future_auth_token?: string, user: array|int|string}|array{_: 'auth.authorizationSignUpRequired', terms_of_service?: array{_: 'help.termsOfService', id: mixed, popup: bool, text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, min_age_confirm?: int}}}|array{_: 'auth.sentCodePaymentRequired', store_product: string, phone_code_hash: string, support_email_address: string, support_email_subject: string, premium_days: int, currency: string, amount: int} @see https://docs.madelineproto.xyz/API_docs/types/auth.SentCode.html
     * @psalm-impure
     */
    public function resendCode(string|null $phone_number = '', string|null $phone_code_hash = '', string|null $reason = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Cancel the login verification code
     *
     * @param string $phone_number Phone number
     * @param string $phone_code_hash Phone code hash from [auth.sendCode](https://docs.madelineproto.xyz/API_docs/methods/auth.sendCode.html)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function cancelCode(string|null $phone_number = '', string|null $phone_code_hash = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Delete all temporary authorization keys **except for** the ones specified
     *
     * @param list<int>|array<never, never> $except_auth_keys The auth keys that **shouldn't** be dropped.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function dropTempAuthKeys(array $except_auth_keys = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Generate a login token, for [login via QR code](https://core.telegram.org/api/qr-login).  
     * The generated login token should be encoded using base64url, then shown as a `tg://login?token=base64encodedtoken` [deep link Â»](https://core.telegram.org/api/links#qr-code-login-links) in the QR code.
     * 
     * For more info, see [login via QR code](https://core.telegram.org/api/qr-login).
     *
     * @param int $api_id Application identifier (see. [App configuration](https://core.telegram.org/myapp))
     * @param string $api_hash Application identifier hash (see. [App configuration](https://core.telegram.org/myapp))
     * @param list<int>|array<never, never> $except_ids List of already logged-in user IDs, to prevent logging in twice with the same user
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'auth.loginToken', expires: int, token: string}|array{_: 'auth.loginTokenMigrateTo', dc_id: int, token: string}|array{_: 'auth.loginTokenSuccess', authorization: array{_: 'auth.authorization', setup_password_required: bool, otherwise_relogin_days?: int, tmp_sessions?: int, future_auth_token?: string, user: array|int|string}|array{_: 'auth.authorizationSignUpRequired', terms_of_service?: array{_: 'help.termsOfService', id: mixed, popup: bool, text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, min_age_confirm?: int}}} @see https://docs.madelineproto.xyz/API_docs/types/auth.LoginToken.html
     * @psalm-impure
     */
    public function exportLoginToken(int|null $api_id = 0, string|null $api_hash = '', array $except_ids = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Login using a redirected login token, generated in case of DC mismatch during [QR code login](https://core.telegram.org/api/qr-login).
     * 
     * For more info, see [login via QR code](https://core.telegram.org/api/qr-login).
     *
     * @param string $token Login token
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'auth.loginToken', expires: int, token: string}|array{_: 'auth.loginTokenMigrateTo', dc_id: int, token: string}|array{_: 'auth.loginTokenSuccess', authorization: array{_: 'auth.authorization', setup_password_required: bool, otherwise_relogin_days?: int, tmp_sessions?: int, future_auth_token?: string, user: array|int|string}|array{_: 'auth.authorizationSignUpRequired', terms_of_service?: array{_: 'help.termsOfService', id: mixed, popup: bool, text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, min_age_confirm?: int}}} @see https://docs.madelineproto.xyz/API_docs/types/auth.LoginToken.html
     * @psalm-impure
     */
    public function importLoginToken(string|null $token = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Accept QR code login token, logging in the app that generated it.
     * 
     * Returns info about the new session.
     * 
     * For more info, see [login via QR code](https://core.telegram.org/api/qr-login).
     *
     * @param string $token Login token embedded in QR code, for more info, see [login via QR code](https://core.telegram.org/api/qr-login).
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'authorization', current: bool, official_app: bool, password_pending: bool, encrypted_requests_disabled: bool, call_requests_disabled: bool, unconfirmed: bool, hash: list<int|string>, device_model: string, platform: string, system_version: string, api_id: int, app_name: string, app_version: string, date_created: int, date_active: int, ip: string, country: string, region: string} @see https://docs.madelineproto.xyz/API_docs/types/Authorization.html
     * @psalm-impure
     */
    public function acceptLoginToken(string|null $token = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Check if the [2FA recovery code](https://core.telegram.org/api/srp) sent using [auth.requestPasswordRecovery](https://docs.madelineproto.xyz/API_docs/methods/auth.requestPasswordRecovery.html) is valid, before passing it to [auth.recoverPassword](https://docs.madelineproto.xyz/API_docs/methods/auth.recoverPassword.html).
     *
     * @param string $code Code received via email
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function checkRecoveryPassword(string|null $code = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Login by importing an authorization token
     *
     * @param int $api_id [API ID](https://core.telegram.org/api/obtaining_api_id)
     * @param string $api_hash [API hash](https://core.telegram.org/api/obtaining_api_id)
     * @param string $web_auth_token The authorization token
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'auth.authorization', setup_password_required: bool, otherwise_relogin_days?: int, tmp_sessions?: int, future_auth_token?: string, user: array|int|string}|array{_: 'auth.authorizationSignUpRequired', terms_of_service?: array{_: 'help.termsOfService', id: mixed, popup: bool, text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, min_age_confirm?: int}} @see https://docs.madelineproto.xyz/API_docs/types/auth.Authorization.html
     * @psalm-impure
     */
    public function importWebTokenAuthorization(int|null $api_id = 0, string|null $api_hash = '', string|null $web_auth_token = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Request an SMS code via Firebase.
     *
     * @param string $phone_number Phone number
     * @param string $phone_code_hash Phone code hash returned by [auth.sendCode](https://docs.madelineproto.xyz/API_docs/methods/auth.sendCode.html)
     * @param string $safety_net_token On Android, a JWS object obtained as described in the [auth documentation Â»](https://core.telegram.org/api/auth)
     * @param string $play_integrity_token On Android, an object obtained as described in the [auth documentation Â»](https://core.telegram.org/api/auth)
     * @param string $ios_push_secret Secret token received via an apple push notification
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function requestFirebaseSms(string|null $phone_number = '', string|null $phone_code_hash = '', string|null $safety_net_token = null, string|null $play_integrity_token = null, string|null $ios_push_secret = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Reset the [login email Â»](https://core.telegram.org/api/auth#email-verification).
     *
     * @param string $phone_number Phone number of the account
     * @param string $phone_code_hash Phone code hash, obtained as described in the [documentation Â»](https://core.telegram.org/api/auth)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'auth.sentCode', type: array{_: 'auth.sentCodeTypeApp', length: int}|array{_: 'auth.sentCodeTypeSms', length: int}|array{_: 'auth.sentCodeTypeCall', length: int}|array{_: 'auth.sentCodeTypeFlashCall', pattern: string}|array{_: 'auth.sentCodeTypeMissedCall', prefix: string, length: int}|array{_: 'auth.sentCodeTypeEmailCode', apple_signin_allowed: bool, google_signin_allowed: bool, email_pattern: string, length: int, reset_available_period?: int, reset_pending_date?: int}|array{_: 'auth.sentCodeTypeSetUpEmailRequired', apple_signin_allowed: bool, google_signin_allowed: bool}|array{_: 'auth.sentCodeTypeFragmentSms', url: string, length: int}|array{_: 'auth.sentCodeTypeFirebaseSms', nonce?: string, play_integrity_project_id?: int, play_integrity_nonce?: string, receipt?: string, push_timeout?: int, length: int}|array{_: 'auth.sentCodeTypeSmsWord', beginning?: string}|array{_: 'auth.sentCodeTypeSmsPhrase', beginning?: string}, phone_code_hash: string, next_type?: array{_: 'auth.codeTypeSms'}|array{_: 'auth.codeTypeCall'}|array{_: 'auth.codeTypeFlashCall'}|array{_: 'auth.codeTypeMissedCall'}|array{_: 'auth.codeTypeFragmentSms'}, timeout?: int}|array{_: 'auth.sentCodeSuccess', authorization: array{_: 'auth.authorization', setup_password_required: bool, otherwise_relogin_days?: int, tmp_sessions?: int, future_auth_token?: string, user: array|int|string}|array{_: 'auth.authorizationSignUpRequired', terms_of_service?: array{_: 'help.termsOfService', id: mixed, popup: bool, text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, min_age_confirm?: int}}}|array{_: 'auth.sentCodePaymentRequired', store_product: string, phone_code_hash: string, support_email_address: string, support_email_subject: string, premium_days: int, currency: string, amount: int} @see https://docs.madelineproto.xyz/API_docs/types/auth.SentCode.html
     * @psalm-impure
     */
    public function resetLoginEmail(string|null $phone_number = '', string|null $phone_code_hash = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Official apps only, reports that the SMS authentication code wasn't delivered.
     *
     * @param string $phone_number Phone number where we were supposed to receive the code
     * @param string $phone_code_hash The phone code hash obtained from [auth.sendCode](https://docs.madelineproto.xyz/API_docs/methods/auth.sendCode.html)
     * @param string $mnc [MNC](https://en.wikipedia.org/wiki/Mobile_country_code) of the current network operator.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function reportMissingCode(string|null $phone_number = '', string|null $phone_code_hash = '', string|null $mnc = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * 
     *
     * @param string $phone_number 
     * @param string $phone_code_hash 
     * @param int $form_id 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'auth.sentCode', type: array{_: 'auth.sentCodeTypeApp', length: int}|array{_: 'auth.sentCodeTypeSms', length: int}|array{_: 'auth.sentCodeTypeCall', length: int}|array{_: 'auth.sentCodeTypeFlashCall', pattern: string}|array{_: 'auth.sentCodeTypeMissedCall', prefix: string, length: int}|array{_: 'auth.sentCodeTypeEmailCode', apple_signin_allowed: bool, google_signin_allowed: bool, email_pattern: string, length: int, reset_available_period?: int, reset_pending_date?: int}|array{_: 'auth.sentCodeTypeSetUpEmailRequired', apple_signin_allowed: bool, google_signin_allowed: bool}|array{_: 'auth.sentCodeTypeFragmentSms', url: string, length: int}|array{_: 'auth.sentCodeTypeFirebaseSms', nonce?: string, play_integrity_project_id?: int, play_integrity_nonce?: string, receipt?: string, push_timeout?: int, length: int}|array{_: 'auth.sentCodeTypeSmsWord', beginning?: string}|array{_: 'auth.sentCodeTypeSmsPhrase', beginning?: string}, phone_code_hash: string, next_type?: array{_: 'auth.codeTypeSms'}|array{_: 'auth.codeTypeCall'}|array{_: 'auth.codeTypeFlashCall'}|array{_: 'auth.codeTypeMissedCall'}|array{_: 'auth.codeTypeFragmentSms'}, timeout?: int}|array{_: 'auth.sentCodeSuccess', authorization: array{_: 'auth.authorization', setup_password_required: bool, otherwise_relogin_days?: int, tmp_sessions?: int, future_auth_token?: string, user: array|int|string}|array{_: 'auth.authorizationSignUpRequired', terms_of_service?: array{_: 'help.termsOfService', id: mixed, popup: bool, text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, min_age_confirm?: int}}}|array{_: 'auth.sentCodePaymentRequired', store_product: string, phone_code_hash: string, support_email_address: string, support_email_subject: string, premium_days: int, currency: string, amount: int} @see https://docs.madelineproto.xyz/API_docs/types/auth.SentCode.html
     * @psalm-impure
     */
    public function checkPaidAuth(string|null $phone_number = '', string|null $phone_code_hash = '', int|null $form_id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param int $api_id 
     * @param string $api_hash 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'auth.passkeyLoginOptions', options: mixed} @see https://docs.madelineproto.xyz/API_docs/types/auth.PasskeyLoginOptions.html
     * @psalm-impure
     */
    public function initPasskeyLogin(int|null $api_id = 0, string|null $api_hash = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param array{_: 'inputPasskeyCredentialPublicKey', response: array{_: 'inputPasskeyResponseRegister', client_data: mixed, attestation_data?: string}|array{_: 'inputPasskeyResponseLogin', client_data: mixed, authenticator_data?: string, signature?: string, user_handle?: string}, id?: string, raw_id?: string} $credential @see https://docs.madelineproto.xyz/API_docs/types/InputPasskeyCredential.html
     * @param int $from_dc_id 
     * @param int $from_auth_key_id 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'auth.authorization', setup_password_required: bool, otherwise_relogin_days?: int, tmp_sessions?: int, future_auth_token?: string, user: array|int|string}|array{_: 'auth.authorizationSignUpRequired', terms_of_service?: array{_: 'help.termsOfService', id: mixed, popup: bool, text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, min_age_confirm?: int}} @see https://docs.madelineproto.xyz/API_docs/types/auth.Authorization.html
     * @psalm-impure
     */
    public function finishPasskeyLogin(array $credential, int|null $from_dc_id = null, int|null $from_auth_key_id = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param string $web_auth_token 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function cancelWebTokenAuthorization(string|null $web_auth_token = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * 
     *
     * @param list<int> $except_ids 
     * @param int $api_id 
     * @param string $api_hash 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'auth.firebasePnvIntent', nonce: string, digital_credential_payload: string} @see https://docs.madelineproto.xyz/API_docs/types/auth.FirebasePnvIntent.html
     * @psalm-impure
     */
    public function initFirebasePnvLogin(array|null $except_ids = null, int|null $api_id = 0, string|null $api_hash = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param string $google_token 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'auth.authorization', setup_password_required: bool, otherwise_relogin_days?: int, tmp_sessions?: int, future_auth_token?: string, user: array|int|string}|array{_: 'auth.authorizationSignUpRequired', terms_of_service?: array{_: 'help.termsOfService', id: mixed, popup: bool, text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, min_age_confirm?: int}} @see https://docs.madelineproto.xyz/API_docs/types/auth.Authorization.html
     * @psalm-impure
     */
    public function finishFirebasePnvLogin(string|null $google_token = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param bool $no_joined_notifications 
     * @param string $first_name 
     * @param string $last_name 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'auth.authorization', setup_password_required: bool, otherwise_relogin_days?: int, tmp_sessions?: int, future_auth_token?: string, user: array|int|string}|array{_: 'auth.authorizationSignUpRequired', terms_of_service?: array{_: 'help.termsOfService', id: mixed, popup: bool, text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, min_age_confirm?: int}} @see https://docs.madelineproto.xyz/API_docs/types/auth.Authorization.html
     * @psalm-impure
     */
    public function firebasePnvSignUp(bool|null $no_joined_notifications = null, string|null $first_name = '', string|null $last_name = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;
}
<?php
/**
 * This file is automatic generated by build_docs.php file
 * and is used only for autocomplete in multiple IDE
 * don't modify manually.
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\Namespace;

/** @psalm-mutable */
interface Payments
{
    /**
     * Get a payment form
     *
     * @param array{_: 'inputInvoiceMessage', peer?: array|int|string, msg_id?: int}|array{_: 'inputInvoiceSlug', slug?: string}|array{_: 'inputInvoicePremiumGiftCode', purpose: array{_: 'inputStorePaymentPremiumSubscription', restore?: bool, upgrade?: bool}|array{_: 'inputStorePaymentGiftPremium', user_id?: array|int|string, currency?: string, amount?: int}|array{_: 'inputStorePaymentPremiumGiftCode', users?: list<array|int|string>, boost_peer?: array|int|string, currency?: string, amount?: int, message?: array{_: 'textWithEntities', text?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>}}|array{_: 'inputStorePaymentPremiumGiveaway', only_new_subscribers?: bool, winners_are_visible?: bool, boost_peer?: array|int|string, additional_peers?: list<array|int|string>, countries_iso2?: list<string>, prize_description?: string, until_date?: int, currency?: string, amount?: int}|array{_: 'inputStorePaymentStarsTopup', stars?: int, currency?: string, amount?: int, spend_purpose_peer?: array|int|string}|array{_: 'inputStorePaymentStarsGift', user_id?: array|int|string, stars?: int, currency?: string, amount?: int}|array{_: 'inputStorePaymentStarsGiveaway', only_new_subscribers?: bool, winners_are_visible?: bool, stars?: int, boost_peer?: array|int|string, additional_peers?: list<array|int|string>, countries_iso2?: list<string>, prize_description?: string, until_date?: int, currency?: string, amount?: int, users?: int}|array{_: 'inputStorePaymentAuthCode', restore?: bool, phone_number?: string, phone_code_hash?: string, premium_days?: int, currency?: string, amount?: int}, option: array{_: 'premiumGiftCodeOption', users?: int, months?: int, store_product?: string, store_quantity?: int, currency?: string, amount?: int}}|array{_: 'inputInvoiceStars', purpose: array{_: 'inputStorePaymentPremiumSubscription', restore?: bool, upgrade?: bool}|array{_: 'inputStorePaymentGiftPremium', user_id?: array|int|string, currency?: string, amount?: int}|array{_: 'inputStorePaymentPremiumGiftCode', users?: list<array|int|string>, boost_peer?: array|int|string, currency?: string, amount?: int, message?: array{_: 'textWithEntities', text?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>}}|array{_: 'inputStorePaymentPremiumGiveaway', only_new_subscribers?: bool, winners_are_visible?: bool, boost_peer?: array|int|string, additional_peers?: list<array|int|string>, countries_iso2?: list<string>, prize_description?: string, until_date?: int, currency?: string, amount?: int}|array{_: 'inputStorePaymentStarsTopup', stars?: int, currency?: string, amount?: int, spend_purpose_peer?: array|int|string}|array{_: 'inputStorePaymentStarsGift', user_id?: array|int|string, stars?: int, currency?: string, amount?: int}|array{_: 'inputStorePaymentStarsGiveaway', only_new_subscribers?: bool, winners_are_visible?: bool, stars?: int, boost_peer?: array|int|string, additional_peers?: list<array|int|string>, countries_iso2?: list<string>, prize_description?: string, until_date?: int, currency?: string, amount?: int, users?: int}|array{_: 'inputStorePaymentAuthCode', restore?: bool, phone_number?: string, phone_code_hash?: string, premium_days?: int, currency?: string, amount?: int}}|array{_: 'inputInvoiceChatInviteSubscription', hash?: string}|array{_: 'inputInvoiceStarGift', hide_name?: bool, include_upgrade?: bool, peer?: array|int|string, gift_id?: int, message?: array{_: 'textWithEntities', text?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>}}|array{_: 'inputInvoiceStarGiftUpgrade', stargift: array{_: 'inputSavedStarGiftUser', msg_id?: int}|array{_: 'inputSavedStarGiftChat', peer?: array|int|string, saved_id?: int}|array{_: 'inputSavedStarGiftSlug', slug?: string}, keep_original_details?: bool}|array{_: 'inputInvoiceStarGiftTransfer', stargift: array{_: 'inputSavedStarGiftUser', msg_id?: int}|array{_: 'inputSavedStarGiftChat', peer?: array|int|string, saved_id?: int}|array{_: 'inputSavedStarGiftSlug', slug?: string}, to_id?: array|int|string}|array{_: 'inputInvoicePremiumGiftStars', user_id?: array|int|string, months?: int, message?: array{_: 'textWithEntities', text?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>}}|array{_: 'inputInvoiceBusinessBotTransferStars', bot?: array|int|string, stars?: int}|array{_: 'inputInvoiceStarGiftResale', ton?: bool, show_name?: bool, slug?: string, to_id?: array|int|string, message?: array{_: 'textWithEntities', text?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>}}|array{_: 'inputInvoiceStarGiftPrepaidUpgrade', peer?: array|int|string, hash?: string}|array{_: 'inputInvoicePremiumAuthCode', purpose: array{_: 'inputStorePaymentPremiumSubscription', restore?: bool, upgrade?: bool}|array{_: 'inputStorePaymentGiftPremium', user_id?: array|int|string, currency?: string, amount?: int}|array{_: 'inputStorePaymentPremiumGiftCode', users?: list<array|int|string>, boost_peer?: array|int|string, currency?: string, amount?: int, message?: array{_: 'textWithEntities', text?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>}}|array{_: 'inputStorePaymentPremiumGiveaway', only_new_subscribers?: bool, winners_are_visible?: bool, boost_peer?: array|int|string, additional_peers?: list<array|int|string>, countries_iso2?: list<string>, prize_description?: string, until_date?: int, currency?: string, amount?: int}|array{_: 'inputStorePaymentStarsTopup', stars?: int, currency?: string, amount?: int, spend_purpose_peer?: array|int|string}|array{_: 'inputStorePaymentStarsGift', user_id?: array|int|string, stars?: int, currency?: string, amount?: int}|array{_: 'inputStorePaymentStarsGiveaway', only_new_subscribers?: bool, winners_are_visible?: bool, stars?: int, boost_peer?: array|int|string, additional_peers?: list<array|int|string>, countries_iso2?: list<string>, prize_description?: string, until_date?: int, currency?: string, amount?: int, users?: int}|array{_: 'inputStorePaymentAuthCode', restore?: bool, phone_number?: string, phone_code_hash?: string, premium_days?: int, currency?: string, amount?: int}}|array{_: 'inputInvoiceStarGiftDropOriginalDetails', stargift: array{_: 'inputSavedStarGiftUser', msg_id?: int}|array{_: 'inputSavedStarGiftChat', peer?: array|int|string, saved_id?: int}|array{_: 'inputSavedStarGiftSlug', slug?: string}}|array{_: 'inputInvoiceStarGiftAuctionBid', hide_name?: bool, update_bid?: bool, peer?: array|int|string, gift_id?: int, bid_amount?: int, message?: array{_: 'textWithEntities', text?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>}} $invoice Invoice @see https://docs.madelineproto.xyz/API_docs/types/InputInvoice.html
     * @param mixed $theme_params Any JSON-encodable data
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @param ?string $businessConnectionId Business connection ID, received through an updateBotBusinessConnect update.
     * @return array{_: 'payments.paymentForm', invoice: array{_: 'invoice', test: bool, name_requested: bool, phone_requested: bool, email_requested: bool, shipping_address_requested: bool, flexible: bool, phone_to_provider: bool, email_to_provider: bool, recurring: bool, currency: string, prices: list<array{_: 'labeledPrice', label: string, amount: int}>, max_tip_amount?: int, suggested_tip_amounts?: list<int>, terms_url?: string, subscription_period?: int}, can_save_credentials: bool, password_missing: bool, form_id: int, bot_id: int, title: string, description: string, photo?: array{_: 'webDocument', url: string, access_hash: int, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n: int}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}|array{_: 'webDocumentNoProxy', url: string, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n: int}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}, provider_id: int, url: string, native_provider?: string, native_params?: mixed, additional_methods?: list<array{_: 'paymentFormMethod', url: string, title: string}>, saved_info?: array{_: 'paymentRequestedInfo', name?: string, phone?: string, email?: string, shipping_address?: array{_: 'postAddress', street_line1: string, street_line2: string, city: string, state: string, country_iso2: string, post_code: string}}, saved_credentials?: list<array{_: 'paymentSavedCredentialsCard', id: string, title: string}>, users: list<array|int|string>}|array{_: 'payments.paymentFormStars', invoice: array{_: 'invoice', test: bool, name_requested: bool, phone_requested: bool, email_requested: bool, shipping_address_requested: bool, flexible: bool, phone_to_provider: bool, email_to_provider: bool, recurring: bool, currency: string, prices: list<array{_: 'labeledPrice', label: string, amount: int}>, max_tip_amount?: int, suggested_tip_amounts?: list<int>, terms_url?: string, subscription_period?: int}, form_id: int, bot_id: int, title: string, description: string, photo?: array{_: 'webDocument', url: string, access_hash: int, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n: int}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}|array{_: 'webDocumentNoProxy', url: string, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n: int}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}, users: list<array|int|string>}|array{_: 'payments.paymentFormStarGift', invoice: array{_: 'invoice', test: bool, name_requested: bool, phone_requested: bool, email_requested: bool, shipping_address_requested: bool, flexible: bool, phone_to_provider: bool, email_to_provider: bool, recurring: bool, currency: string, prices: list<array{_: 'labeledPrice', label: string, amount: int}>, max_tip_amount?: int, suggested_tip_amounts?: list<int>, terms_url?: string, subscription_period?: int}, form_id: int} @see https://docs.madelineproto.xyz/API_docs/types/payments.PaymentForm.html
     * @psalm-impure
     */
    public function getPaymentForm(array $invoice, mixed $theme_params = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null, ?string $businessConnectionId = null): array;

    /**
     * Get payment receipt
     *
     * @param array|int|string $peer The peer where the payment receipt was sent @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $msg_id Message ID of receipt
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'payments.paymentReceipt', invoice: array{_: 'invoice', test: bool, name_requested: bool, phone_requested: bool, email_requested: bool, shipping_address_requested: bool, flexible: bool, phone_to_provider: bool, email_to_provider: bool, recurring: bool, currency: string, prices: list<array{_: 'labeledPrice', label: string, amount: int}>, max_tip_amount?: int, suggested_tip_amounts?: list<int>, terms_url?: string, subscription_period?: int}, date: int, bot_id: int, provider_id: int, title: string, description: string, photo?: array{_: 'webDocument', url: string, access_hash: int, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n: int}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}|array{_: 'webDocumentNoProxy', url: string, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n: int}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}, info?: array{_: 'paymentRequestedInfo', name?: string, phone?: string, email?: string, shipping_address?: array{_: 'postAddress', street_line1: string, street_line2: string, city: string, state: string, country_iso2: string, post_code: string}}, shipping?: array{_: 'shippingOption', id: string, title: string, prices: list<array{_: 'labeledPrice', label: string, amount: int}>}, tip_amount?: int, currency: string, total_amount: int, credentials_title: string, users: list<array|int|string>}|array{_: 'payments.paymentReceiptStars', invoice: array{_: 'invoice', test: bool, name_requested: bool, phone_requested: bool, email_requested: bool, shipping_address_requested: bool, flexible: bool, phone_to_provider: bool, email_to_provider: bool, recurring: bool, currency: string, prices: list<array{_: 'labeledPrice', label: string, amount: int}>, max_tip_amount?: int, suggested_tip_amounts?: list<int>, terms_url?: string, subscription_period?: int}, date: int, bot_id: int, title: string, description: string, photo?: array{_: 'webDocument', url: string, access_hash: int, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n: int}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}|array{_: 'webDocumentNoProxy', url: string, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n: int}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}, currency: string, total_amount: int, transaction_id: string, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/payments.PaymentReceipt.html
     * @psalm-impure
     */
    public function getPaymentReceipt(array|int|string|null $peer = null, int|null $msg_id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Submit requested order information for validation
     *
     * @param array{_: 'inputInvoiceMessage', peer?: array|int|string, msg_id?: int}|array{_: 'inputInvoiceSlug', slug?: string}|array{_: 'inputInvoicePremiumGiftCode', purpose: array{_: 'inputStorePaymentPremiumSubscription', restore?: bool, upgrade?: bool}|array{_: 'inputStorePaymentGiftPremium', user_id?: array|int|string, currency?: string, amount?: int}|array{_: 'inputStorePaymentPremiumGiftCode', users?: list<array|int|string>, boost_peer?: array|int|string, currency?: string, amount?: int, message?: array{_: 'textWithEntities', text?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>}}|array{_: 'inputStorePaymentPremiumGiveaway', only_new_subscribers?: bool, winners_are_visible?: bool, boost_peer?: array|int|string, additional_peers?: list<array|int|string>, countries_iso2?: list<string>, prize_description?: string, until_date?: int, currency?: string, amount?: int}|array{_: 'inputStorePaymentStarsTopup', stars?: int, currency?: string, amount?: int, spend_purpose_peer?: array|int|string}|array{_: 'inputStorePaymentStarsGift', user_id?: array|int|string, stars?: int, currency?: string, amount?: int}|array{_: 'inputStorePaymentStarsGiveaway', only_new_subscribers?: bool, winners_are_visible?: bool, stars?: int, boost_peer?: array|int|string, additional_peers?: list<array|int|string>, countries_iso2?: list<string>, prize_description?: string, until_date?: int, currency?: string, amount?: int, users?: int}|array{_: 'inputStorePaymentAuthCode', restore?: bool, phone_number?: string, phone_code_hash?: string, premium_days?: int, currency?: string, amount?: int}, option: array{_: 'premiumGiftCodeOption', users?: int, months?: int, store_product?: string, store_quantity?: int, currency?: string, amount?: int}}|array{_: 'inputInvoiceStars', purpose: array{_: 'inputStorePaymentPremiumSubscription', restore?: bool, upgrade?: bool}|array{_: 'inputStorePaymentGiftPremium', user_id?: array|int|string, currency?: string, amount?: int}|array{_: 'inputStorePaymentPremiumGiftCode', users?: list<array|int|string>, boost_peer?: array|int|string, currency?: string, amount?: int, message?: array{_: 'textWithEntities', text?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>}}|array{_: 'inputStorePaymentPremiumGiveaway', only_new_subscribers?: bool, winners_are_visible?: bool, boost_peer?: array|int|string, additional_peers?: list<array|int|string>, countries_iso2?: list<string>, prize_description?: string, until_date?: int, currency?: string, amount?: int}|array{_: 'inputStorePaymentStarsTopup', stars?: int, currency?: string, amount?: int, spend_purpose_peer?: array|int|string}|array{_: 'inputStorePaymentStarsGift', user_id?: array|int|string, stars?: int, currency?: string, amount?: int}|array{_: 'inputStorePaymentStarsGiveaway', only_new_subscribers?: bool, winners_are_visible?: bool, stars?: int, boost_peer?: array|int|string, additional_peers?: list<array|int|string>, countries_iso2?: list<string>, prize_description?: string, until_date?: int, currency?: string, amount?: int, users?: int}|array{_: 'inputStorePaymentAuthCode', restore?: bool, phone_number?: string, phone_code_hash?: string, premium_days?: int, currency?: string, amount?: int}}|array{_: 'inputInvoiceChatInviteSubscription', hash?: string}|array{_: 'inputInvoiceStarGift', hide_name?: bool, include_upgrade?: bool, peer?: array|int|string, gift_id?: int, message?: array{_: 'textWithEntities', text?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>}}|array{_: 'inputInvoiceStarGiftUpgrade', stargift: array{_: 'inputSavedStarGiftUser', msg_id?: int}|array{_: 'inputSavedStarGiftChat', peer?: array|int|string, saved_id?: int}|array{_: 'inputSavedStarGiftSlug', slug?: string}, keep_original_details?: bool}|array{_: 'inputInvoiceStarGiftTransfer', stargift: array{_: 'inputSavedStarGiftUser', msg_id?: int}|array{_: 'inputSavedStarGiftChat', peer?: array|int|string, saved_id?: int}|array{_: 'inputSavedStarGiftSlug', slug?: string}, to_id?: array|int|string}|array{_: 'inputInvoicePremiumGiftStars', user_id?: array|int|string, months?: int, message?: array{_: 'textWithEntities', text?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>}}|array{_: 'inputInvoiceBusinessBotTransferStars', bot?: array|int|string, stars?: int}|array{_: 'inputInvoiceStarGiftResale', ton?: bool, show_name?: bool, slug?: string, to_id?: array|int|string, message?: array{_: 'textWithEntities', text?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>}}|array{_: 'inputInvoiceStarGiftPrepaidUpgrade', peer?: array|int|string, hash?: string}|array{_: 'inputInvoicePremiumAuthCode', purpose: array{_: 'inputStorePaymentPremiumSubscription', restore?: bool, upgrade?: bool}|array{_: 'inputStorePaymentGiftPremium', user_id?: array|int|string, currency?: string, amount?: int}|array{_: 'inputStorePaymentPremiumGiftCode', users?: list<array|int|string>, boost_peer?: array|int|string, currency?: string, amount?: int, message?: array{_: 'textWithEntities', text?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>}}|array{_: 'inputStorePaymentPremiumGiveaway', only_new_subscribers?: bool, winners_are_visible?: bool, boost_peer?: array|int|string, additional_peers?: list<array|int|string>, countries_iso2?: list<string>, prize_description?: string, until_date?: int, currency?: string, amount?: int}|array{_: 'inputStorePaymentStarsTopup', stars?: int, currency?: string, amount?: int, spend_purpose_peer?: array|int|string}|array{_: 'inputStorePaymentStarsGift', user_id?: array|int|string, stars?: int, currency?: string, amount?: int}|array{_: 'inputStorePaymentStarsGiveaway', only_new_subscribers?: bool, winners_are_visible?: bool, stars?: int, boost_peer?: array|int|string, additional_peers?: list<array|int|string>, countries_iso2?: list<string>, prize_description?: string, until_date?: int, currency?: string, amount?: int, users?: int}|array{_: 'inputStorePaymentAuthCode', restore?: bool, phone_number?: string, phone_code_hash?: string, premium_days?: int, currency?: string, amount?: int}}|array{_: 'inputInvoiceStarGiftDropOriginalDetails', stargift: array{_: 'inputSavedStarGiftUser', msg_id?: int}|array{_: 'inputSavedStarGiftChat', peer?: array|int|string, saved_id?: int}|array{_: 'inputSavedStarGiftSlug', slug?: string}}|array{_: 'inputInvoiceStarGiftAuctionBid', hide_name?: bool, update_bid?: bool, peer?: array|int|string, gift_id?: int, bid_amount?: int, message?: array{_: 'textWithEntities', text?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>}} $invoice Invoice @see https://docs.madelineproto.xyz/API_docs/types/InputInvoice.html
     * @param array{_: 'paymentRequestedInfo', name?: string, phone?: string, email?: string, shipping_address?: array{_: 'postAddress', street_line1?: string, street_line2?: string, city?: string, state?: string, country_iso2?: string, post_code?: string}} $info Requested order information @see https://docs.madelineproto.xyz/API_docs/types/PaymentRequestedInfo.html
     * @param bool $save Save order information to re-use it for future orders
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'payments.validatedRequestedInfo', id?: string, shipping_options?: list<array{_: 'shippingOption', id: string, title: string, prices: list<array{_: 'labeledPrice', label: string, amount: int}>}>} @see https://docs.madelineproto.xyz/API_docs/types/payments.ValidatedRequestedInfo.html
     * @psalm-impure
     */
    public function validateRequestedInfo(array $invoice, array $info, bool|null $save = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Send compiled payment form
     *
     * @param array{_: 'inputInvoiceMessage', peer?: array|int|string, msg_id?: int}|array{_: 'inputInvoiceSlug', slug?: string}|array{_: 'inputInvoicePremiumGiftCode', purpose: array{_: 'inputStorePaymentPremiumSubscription', restore?: bool, upgrade?: bool}|array{_: 'inputStorePaymentGiftPremium', user_id?: array|int|string, currency?: string, amount?: int}|array{_: 'inputStorePaymentPremiumGiftCode', users?: list<array|int|string>, boost_peer?: array|int|string, currency?: string, amount?: int, message?: array{_: 'textWithEntities', text?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>}}|array{_: 'inputStorePaymentPremiumGiveaway', only_new_subscribers?: bool, winners_are_visible?: bool, boost_peer?: array|int|string, additional_peers?: list<array|int|string>, countries_iso2?: list<string>, prize_description?: string, until_date?: int, currency?: string, amount?: int}|array{_: 'inputStorePaymentStarsTopup', stars?: int, currency?: string, amount?: int, spend_purpose_peer?: array|int|string}|array{_: 'inputStorePaymentStarsGift', user_id?: array|int|string, stars?: int, currency?: string, amount?: int}|array{_: 'inputStorePaymentStarsGiveaway', only_new_subscribers?: bool, winners_are_visible?: bool, stars?: int, boost_peer?: array|int|string, additional_peers?: list<array|int|string>, countries_iso2?: list<string>, prize_description?: string, until_date?: int, currency?: string, amount?: int, users?: int}|array{_: 'inputStorePaymentAuthCode', restore?: bool, phone_number?: string, phone_code_hash?: string, premium_days?: int, currency?: string, amount?: int}, option: array{_: 'premiumGiftCodeOption', users?: int, months?: int, store_product?: string, store_quantity?: int, currency?: string, amount?: int}}|array{_: 'inputInvoiceStars', purpose: array{_: 'inputStorePaymentPremiumSubscription', restore?: bool, upgrade?: bool}|array{_: 'inputStorePaymentGiftPremium', user_id?: array|int|string, currency?: string, amount?: int}|array{_: 'inputStorePaymentPremiumGiftCode', users?: list<array|int|string>, boost_peer?: array|int|string, currency?: string, amount?: int, message?: array{_: 'textWithEntities', text?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>}}|array{_: 'inputStorePaymentPremiumGiveaway', only_new_subscribers?: bool, winners_are_visible?: bool, boost_peer?: array|int|string, additional_peers?: list<array|int|string>, countries_iso2?: list<string>, prize_description?: string, until_date?: int, currency?: string, amount?: int}|array{_: 'inputStorePaymentStarsTopup', stars?: int, currency?: string, amount?: int, spend_purpose_peer?: array|int|string}|array{_: 'inputStorePaymentStarsGift', user_id?: array|int|string, stars?: int, currency?: string, amount?: int}|array{_: 'inputStorePaymentStarsGiveaway', only_new_subscribers?: bool, winners_are_visible?: bool, stars?: int, boost_peer?: array|int|string, additional_peers?: list<array|int|string>, countries_iso2?: list<string>, prize_description?: string, until_date?: int, currency?: string, amount?: int, users?: int}|array{_: 'inputStorePaymentAuthCode', restore?: bool, phone_number?: string, phone_code_hash?: string, premium_days?: int, currency?: string, amount?: int}}|array{_: 'inputInvoiceChatInviteSubscription', hash?: string}|array{_: 'inputInvoiceStarGift', hide_name?: bool, include_upgrade?: bool, peer?: array|int|string, gift_id?: int, message?: array{_: 'textWithEntities', text?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>}}|array{_: 'inputInvoiceStarGiftUpgrade', stargift: array{_: 'inputSavedStarGiftUser', msg_id?: int}|array{_: 'inputSavedStarGiftChat', peer?: array|int|string, saved_id?: int}|array{_: 'inputSavedStarGiftSlug', slug?: string}, keep_original_details?: bool}|array{_: 'inputInvoiceStarGiftTransfer', stargift: array{_: 'inputSavedStarGiftUser', msg_id?: int}|array{_: 'inputSavedStarGiftChat', peer?: array|int|string, saved_id?: int}|array{_: 'inputSavedStarGiftSlug', slug?: string}, to_id?: array|int|string}|array{_: 'inputInvoicePremiumGiftStars', user_id?: array|int|string, months?: int, message?: array{_: 'textWithEntities', text?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>}}|array{_: 'inputInvoiceBusinessBotTransferStars', bot?: array|int|string, stars?: int}|array{_: 'inputInvoiceStarGiftResale', ton?: bool, show_name?: bool, slug?: string, to_id?: array|int|string, message?: array{_: 'textWithEntities', text?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>}}|array{_: 'inputInvoiceStarGiftPrepaidUpgrade', peer?: array|int|string, hash?: string}|array{_: 'inputInvoicePremiumAuthCode', purpose: array{_: 'inputStorePaymentPremiumSubscription', restore?: bool, upgrade?: bool}|array{_: 'inputStorePaymentGiftPremium', user_id?: array|int|string, currency?: string, amount?: int}|array{_: 'inputStorePaymentPremiumGiftCode', users?: list<array|int|string>, boost_peer?: array|int|string, currency?: string, amount?: int, message?: array{_: 'textWithEntities', text?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>}}|array{_: 'inputStorePaymentPremiumGiveaway', only_new_subscribers?: bool, winners_are_visible?: bool, boost_peer?: array|int|string, additional_peers?: list<array|int|string>, countries_iso2?: list<string>, prize_description?: string, until_date?: int, currency?: string, amount?: int}|array{_: 'inputStorePaymentStarsTopup', stars?: int, currency?: string, amount?: int, spend_purpose_peer?: array|int|string}|array{_: 'inputStorePaymentStarsGift', user_id?: array|int|string, stars?: int, currency?: string, amount?: int}|array{_: 'inputStorePaymentStarsGiveaway', only_new_subscribers?: bool, winners_are_visible?: bool, stars?: int, boost_peer?: array|int|string, additional_peers?: list<array|int|string>, countries_iso2?: list<string>, prize_description?: string, until_date?: int, currency?: string, amount?: int, users?: int}|array{_: 'inputStorePaymentAuthCode', restore?: bool, phone_number?: string, phone_code_hash?: string, premium_days?: int, currency?: string, amount?: int}}|array{_: 'inputInvoiceStarGiftDropOriginalDetails', stargift: array{_: 'inputSavedStarGiftUser', msg_id?: int}|array{_: 'inputSavedStarGiftChat', peer?: array|int|string, saved_id?: int}|array{_: 'inputSavedStarGiftSlug', slug?: string}}|array{_: 'inputInvoiceStarGiftAuctionBid', hide_name?: bool, update_bid?: bool, peer?: array|int|string, gift_id?: int, bid_amount?: int, message?: array{_: 'textWithEntities', text?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>}} $invoice Invoice @see https://docs.madelineproto.xyz/API_docs/types/InputInvoice.html
     * @param array{_: 'inputPaymentCredentialsSaved', id?: string, tmp_password?: string}|array{_: 'inputPaymentCredentials', data: mixed, save?: bool}|array{_: 'inputPaymentCredentialsApplePay', payment_data: mixed}|array{_: 'inputPaymentCredentialsGooglePay', payment_token: mixed} $credentials Payment credentials @see https://docs.madelineproto.xyz/API_docs/types/InputPaymentCredentials.html
     * @param int $form_id Form ID
     * @param string $requested_info_id ID of saved and validated [order info](https://docs.madelineproto.xyz/API_docs/constructors/payments.validatedRequestedInfo.html)
     * @param string $shipping_option_id Chosen shipping option ID
     * @param int $tip_amount Tip, in the smallest units of the currency (integer, not float/double). For example, for a price of `US$ 1.45` pass `amount = 145`. See the exp parameter in [currencies.json](https://core.telegram.org/bots/payments/currencies.json), it shows the number of digits past the decimal point for each currency (2 for the majority of currencies).
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'payments.paymentResult', updates: array}|array{_: 'payments.paymentVerificationNeeded', url: string} @see https://docs.madelineproto.xyz/API_docs/types/payments.PaymentResult.html
     * @psalm-impure
     */
    public function sendPaymentForm(array $invoice, array $credentials, int|null $form_id = 0, string|null $requested_info_id = null, string|null $shipping_option_id = null, int|null $tip_amount = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get saved payment information
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'payments.savedInfo', has_saved_credentials: bool, saved_info?: array{_: 'paymentRequestedInfo', name?: string, phone?: string, email?: string, shipping_address?: array{_: 'postAddress', street_line1: string, street_line2: string, city: string, state: string, country_iso2: string, post_code: string}}} @see https://docs.madelineproto.xyz/API_docs/types/payments.SavedInfo.html
     * @psalm-impure
     */
    public function getSavedInfo(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Clear saved payment information
     *
     * @param bool $credentials Remove saved payment credentials
     * @param bool $info Clear the last order settings saved by the user
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function clearSavedInfo(bool|null $credentials = null, bool|null $info = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Get info about a credit card
     *
     * @param string $number Credit card number
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'payments.bankCardData', title: string, open_urls: list<array{_: 'bankCardOpenUrl', url: string, name: string}>} @see https://docs.madelineproto.xyz/API_docs/types/payments.BankCardData.html
     * @psalm-impure
     */
    public function getBankCardData(string|null $number = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Generate an [invoice deep link](https://core.telegram.org/api/links#invoice-links)
     *
     * @param \danog\MadelineProto\EventHandler\Media|string|array $invoice_media Invoice @see https://docs.madelineproto.xyz/API_docs/types/InputMedia.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @param ?string $businessConnectionId Business connection ID, received through an updateBotBusinessConnect update.
     * @return array{_: 'payments.exportedInvoice', url: string} @see https://docs.madelineproto.xyz/API_docs/types/payments.ExportedInvoice.html
     * @psalm-impure
     */
    public function exportInvoice(\danog\MadelineProto\EventHandler\Media|array|string|null $invoice_media = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null, ?string $businessConnectionId = null): array;

    /**
     * Informs server about a purchase made through the App Store: for official applications only.
     *
     * @param array{_: 'inputStorePaymentPremiumSubscription', restore?: bool, upgrade?: bool}|array{_: 'inputStorePaymentGiftPremium', user_id?: array|int|string, currency?: string, amount?: int}|array{_: 'inputStorePaymentPremiumGiftCode', users?: list<array|int|string>, boost_peer?: array|int|string, currency?: string, amount?: int, message?: array{_: 'textWithEntities', text?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>}}|array{_: 'inputStorePaymentPremiumGiveaway', only_new_subscribers?: bool, winners_are_visible?: bool, boost_peer?: array|int|string, additional_peers?: list<array|int|string>, countries_iso2?: list<string>, prize_description?: string, until_date?: int, currency?: string, amount?: int}|array{_: 'inputStorePaymentStarsTopup', stars?: int, currency?: string, amount?: int, spend_purpose_peer?: array|int|string}|array{_: 'inputStorePaymentStarsGift', user_id?: array|int|string, stars?: int, currency?: string, amount?: int}|array{_: 'inputStorePaymentStarsGiveaway', only_new_subscribers?: bool, winners_are_visible?: bool, stars?: int, boost_peer?: array|int|string, additional_peers?: list<array|int|string>, countries_iso2?: list<string>, prize_description?: string, until_date?: int, currency?: string, amount?: int, users?: int}|array{_: 'inputStorePaymentAuthCode', restore?: bool, phone_number?: string, phone_code_hash?: string, premium_days?: int, currency?: string, amount?: int} $purpose Payment purpose @see https://docs.madelineproto.xyz/API_docs/types/InputStorePaymentPurpose.html
     * @param string $receipt Receipt
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function assignAppStoreTransaction(array $purpose, string|null $receipt = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Informs server about a purchase made through the Play Store: for official applications only.
     *
     * @param mixed $receipt Any JSON-encodable data
     * @param array{_: 'inputStorePaymentPremiumSubscription', restore?: bool, upgrade?: bool}|array{_: 'inputStorePaymentGiftPremium', user_id?: array|int|string, currency?: string, amount?: int}|array{_: 'inputStorePaymentPremiumGiftCode', users?: list<array|int|string>, boost_peer?: array|int|string, currency?: string, amount?: int, message?: array{_: 'textWithEntities', text?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>}}|array{_: 'inputStorePaymentPremiumGiveaway', only_new_subscribers?: bool, winners_are_visible?: bool, boost_peer?: array|int|string, additional_peers?: list<array|int|string>, countries_iso2?: list<string>, prize_description?: string, until_date?: int, currency?: string, amount?: int}|array{_: 'inputStorePaymentStarsTopup', stars?: int, currency?: string, amount?: int, spend_purpose_peer?: array|int|string}|array{_: 'inputStorePaymentStarsGift', user_id?: array|int|string, stars?: int, currency?: string, amount?: int}|array{_: 'inputStorePaymentStarsGiveaway', only_new_subscribers?: bool, winners_are_visible?: bool, stars?: int, boost_peer?: array|int|string, additional_peers?: list<array|int|string>, countries_iso2?: list<string>, prize_description?: string, until_date?: int, currency?: string, amount?: int, users?: int}|array{_: 'inputStorePaymentAuthCode', restore?: bool, phone_number?: string, phone_code_hash?: string, premium_days?: int, currency?: string, amount?: int} $purpose Payment purpose @see https://docs.madelineproto.xyz/API_docs/types/InputStorePaymentPurpose.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function assignPlayMarketTransaction(mixed $receipt, array $purpose, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Obtain a list of Telegram Premium [giveaway/gift code Â»](https://core.telegram.org/api/giveaways) options.
     *
     * @param array|int|string $boost_peer The channel that will start the giveaway @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return list<array{_: 'premiumGiftCodeOption', users: int, months: int, store_product?: string, store_quantity?: int, currency: string, amount: int}> Array of  @see https://docs.madelineproto.xyz/API_docs/types/PremiumGiftCodeOption.html
     * @psalm-impure
     */
    public function getPremiumGiftCodeOptions(array|int|string|null $boost_peer = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Obtain information about a [Telegram Premium giftcode Â»](https://core.telegram.org/api/giveaways)
     *
     * @param string $slug The giftcode to check
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'payments.checkedGiftCode', via_giveaway: bool, from_id?: array|int|string, giveaway_msg_id?: int, to_id?: int, date: int, days: int, used_date?: int, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/payments.CheckedGiftCode.html
     * @psalm-impure
     */
    public function checkGiftCode(string|null $slug = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Apply a [Telegram Premium giftcode Â»](https://core.telegram.org/api/giveaways)
     *
     * @param string $slug The code to apply
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function applyGiftCode(string|null $slug = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Obtain information about a [Telegram Premium giveaway Â»](https://core.telegram.org/api/giveaways).
     *
     * @param array|int|string $peer The peer where the giveaway was posted. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $msg_id Message ID of the [messageActionGiveawayLaunch](https://docs.madelineproto.xyz/API_docs/constructors/messageActionGiveawayLaunch.html) service message
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'payments.giveawayInfo', participating: bool, preparing_results: bool, start_date: int, joined_too_early_date?: int, admin_disallowed_chat_id?: int, disallowed_country?: string}|array{_: 'payments.giveawayInfoResults', winner: bool, refunded: bool, start_date: int, gift_code_slug?: string, stars_prize?: int, finish_date: int, winners_count: int, activated_count?: int} @see https://docs.madelineproto.xyz/API_docs/types/payments.GiveawayInfo.html
     * @psalm-impure
     */
    public function getGiveawayInfo(array|int|string|null $peer = null, int|null $msg_id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Launch a [prepaid giveaway Â»](https://core.telegram.org/api/giveaways).
     *
     * @param array{_: 'inputStorePaymentPremiumSubscription', restore?: bool, upgrade?: bool}|array{_: 'inputStorePaymentGiftPremium', user_id?: array|int|string, currency?: string, amount?: int}|array{_: 'inputStorePaymentPremiumGiftCode', users?: list<array|int|string>, boost_peer?: array|int|string, currency?: string, amount?: int, message?: array{_: 'textWithEntities', text?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>}}|array{_: 'inputStorePaymentPremiumGiveaway', only_new_subscribers?: bool, winners_are_visible?: bool, boost_peer?: array|int|string, additional_peers?: list<array|int|string>, countries_iso2?: list<string>, prize_description?: string, until_date?: int, currency?: string, amount?: int}|array{_: 'inputStorePaymentStarsTopup', stars?: int, currency?: string, amount?: int, spend_purpose_peer?: array|int|string}|array{_: 'inputStorePaymentStarsGift', user_id?: array|int|string, stars?: int, currency?: string, amount?: int}|array{_: 'inputStorePaymentStarsGiveaway', only_new_subscribers?: bool, winners_are_visible?: bool, stars?: int, boost_peer?: array|int|string, additional_peers?: list<array|int|string>, countries_iso2?: list<string>, prize_description?: string, until_date?: int, currency?: string, amount?: int, users?: int}|array{_: 'inputStorePaymentAuthCode', restore?: bool, phone_number?: string, phone_code_hash?: string, premium_days?: int, currency?: string, amount?: int} $purpose Giveway parameters @see https://docs.madelineproto.xyz/API_docs/types/InputStorePaymentPurpose.html
     * @param array|int|string $peer The peer where to launch the giveaway. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $giveaway_id The prepaid giveaway ID.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function launchPrepaidGiveaway(array $purpose, array|int|string|null $peer = null, int|null $giveaway_id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Obtain a list of [Telegram Stars topup options Â»](https://core.telegram.org/api/stars#buying-or-gifting-stars) as [starsTopupOption](https://docs.madelineproto.xyz/API_docs/constructors/starsTopupOption.html) constructors.
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return list<array{_: 'starsTopupOption', extended: bool, stars: int, store_product?: string, currency: string, amount: int}> Array of  @see https://docs.madelineproto.xyz/API_docs/types/StarsTopupOption.html
     * @psalm-impure
     */
    public function getStarsTopupOptions(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get the current [Telegram Stars balance](https://core.telegram.org/api/stars) of the current account (with peer=[inputPeerSelf](https://docs.madelineproto.xyz/API_docs/constructors/inputPeerSelf.html)), or the stars balance of the bot specified in `peer`.
     *
     * @param bool $ton If set, returns the channel/ad revenue balance in nanotons.
     * @param array|int|string $peer Peer of which to get the balance. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @param ?string $businessConnectionId Business connection ID, received through an updateBotBusinessConnect update.
     * @return array{_: 'payments.starsStatus', balance: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, subscriptions?: list<array{_: 'starsSubscription', peer: array|int|string, pricing: array{_: 'starsSubscriptionPricing', period: int, amount: int}, canceled: bool, can_refulfill: bool, missing_balance: bool, bot_canceled: bool, id: string, until_date: int, chat_invite_hash?: string, title?: string, photo?: array{_: 'webDocument', url: string, access_hash: int, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n: int}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}|array{_: 'webDocumentNoProxy', url: string, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n: int}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}, invoice_slug?: string}>, subscriptions_next_offset?: string, subscriptions_missing_balance?: int, history?: list<array{_: 'starsTransaction', amount: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, peer: array{_: 'starsTransactionPeerUnsupported'}|array{_: 'starsTransactionPeerAppStore'}|array{_: 'starsTransactionPeerPlayMarket'}|array{_: 'starsTransactionPeerPremiumBot'}|array{_: 'starsTransactionPeerFragment'}|array{_: 'starsTransactionPeer', peer: array|int|string}|array{_: 'starsTransactionPeerAds'}|array{_: 'starsTransactionPeerAPI'}, refund: bool, pending: bool, failed: bool, gift: bool, reaction: bool, stargift_upgrade: bool, business_transfer: bool, stargift_resale: bool, posts_search: bool, stargift_prepaid_upgrade: bool, stargift_drop_original_details: bool, phonegroup_message: bool, stargift_auction_bid: bool, offer: bool, id: string, date: int, title?: string, description?: string, photo?: array{_: 'webDocument', url: string, access_hash: int, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n: int}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}|array{_: 'webDocumentNoProxy', url: string, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n: int}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}, transaction_date?: int, transaction_url?: string, bot_payload?: string, msg_id?: int, extended_media?: list<array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}|array{_: 'messageExtendedMedia', media: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaPoll', poll: array{_: 'poll', question: array, id: array, closed: array, public_voters: array, multiple_choice: array, quiz: array, open_answers: array, revoting_disabled: array, shuffle_answers: array, hide_results_until_close: array, creator: array, subscribers_only: array, answers: list<array>, close_period?: array, close_date?: array, countries_iso2?: list<array>, hash: list<array>}, results: array, attached_media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}|array{_: 'storyItem', pinned: array, public: array, close_friends: array, min: array, noforwards: array, edited: array, contacts: array, selected_contacts: array, out: array, id: array, date: array, from_id?: array, fwd_from?: array, expire_date: array, caption?: array, entities?: list<array>, media: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, media_areas?: list<array>, privacy?: list<array>, views?: array, sent_reaction?: array, albums?: list<array>, music?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}|array{_: 'messageExtendedMedia', media: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}>, subscription_period?: int, giveaway_post_id?: int, stargift?: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, floodskip_number?: int, starref_commission_permille?: int, starref_peer?: array|int|string, starref_amount?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, paid_messages?: int, premium_gift_months?: int, ads_proceeds_from_date?: int, ads_proceeds_to_date?: int}>, next_offset?: string, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/payments.StarsStatus.html
     * @psalm-impure
     */
    public function getStarsStatus(bool|null $ton = null, array|int|string|null $peer = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null, ?string $businessConnectionId = null): array;

    /**
     * Fetch [Telegram Stars transactions](https://core.telegram.org/api/stars#balance-and-transaction-history).
     * 
     * The `inbound` and `outbound` flags are mutually exclusive: if none of the two are set, both incoming and outgoing transactions are fetched.
     *
     * @param bool $inbound If set, fetches only incoming transactions.
     * @param bool $outbound If set, fetches only outgoing transactions.
     * @param bool $ascending Return transactions in ascending order by date (instead of descending order by date).
     * @param bool $ton If set, returns the channel/ad revenue transactions in nanotons, instead.
     * @param string $subscription_id If set, fetches only transactions for the specified [Telegram Star subscription Â»](https://core.telegram.org/api/stars#star-subscriptions).
     * @param array|int|string $peer Fetch the transaction history of the peer ([inputPeerSelf](https://docs.madelineproto.xyz/API_docs/constructors/inputPeerSelf.html) or a bot we own). @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param string $offset [Offset for pagination, obtained from the returned `next_offset`, initially an empty string Â»](https://core.telegram.org/api/offsets).
     * @param int $limit Maximum number of results to return, [see pagination](https://core.telegram.org/api/offsets)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'payments.starsStatus', balance: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, subscriptions?: list<array{_: 'starsSubscription', peer: array|int|string, pricing: array{_: 'starsSubscriptionPricing', period: int, amount: int}, canceled: bool, can_refulfill: bool, missing_balance: bool, bot_canceled: bool, id: string, until_date: int, chat_invite_hash?: string, title?: string, photo?: array{_: 'webDocument', url: string, access_hash: int, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n: int}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}|array{_: 'webDocumentNoProxy', url: string, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n: int}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}, invoice_slug?: string}>, subscriptions_next_offset?: string, subscriptions_missing_balance?: int, history?: list<array{_: 'starsTransaction', amount: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, peer: array{_: 'starsTransactionPeerUnsupported'}|array{_: 'starsTransactionPeerAppStore'}|array{_: 'starsTransactionPeerPlayMarket'}|array{_: 'starsTransactionPeerPremiumBot'}|array{_: 'starsTransactionPeerFragment'}|array{_: 'starsTransactionPeer', peer: array|int|string}|array{_: 'starsTransactionPeerAds'}|array{_: 'starsTransactionPeerAPI'}, refund: bool, pending: bool, failed: bool, gift: bool, reaction: bool, stargift_upgrade: bool, business_transfer: bool, stargift_resale: bool, posts_search: bool, stargift_prepaid_upgrade: bool, stargift_drop_original_details: bool, phonegroup_message: bool, stargift_auction_bid: bool, offer: bool, id: string, date: int, title?: string, description?: string, photo?: array{_: 'webDocument', url: string, access_hash: int, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n: int}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}|array{_: 'webDocumentNoProxy', url: string, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n: int}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}, transaction_date?: int, transaction_url?: string, bot_payload?: string, msg_id?: int, extended_media?: list<array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}|array{_: 'messageExtendedMedia', media: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaPoll', poll: array{_: 'poll', question: array, id: array, closed: array, public_voters: array, multiple_choice: array, quiz: array, open_answers: array, revoting_disabled: array, shuffle_answers: array, hide_results_until_close: array, creator: array, subscribers_only: array, answers: list<array>, close_period?: array, close_date?: array, countries_iso2?: list<array>, hash: list<array>}, results: array, attached_media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}|array{_: 'storyItem', pinned: array, public: array, close_friends: array, min: array, noforwards: array, edited: array, contacts: array, selected_contacts: array, out: array, id: array, date: array, from_id?: array, fwd_from?: array, expire_date: array, caption?: array, entities?: list<array>, media: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, media_areas?: list<array>, privacy?: list<array>, views?: array, sent_reaction?: array, albums?: list<array>, music?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}|array{_: 'messageExtendedMedia', media: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}>, subscription_period?: int, giveaway_post_id?: int, stargift?: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, floodskip_number?: int, starref_commission_permille?: int, starref_peer?: array|int|string, starref_amount?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, paid_messages?: int, premium_gift_months?: int, ads_proceeds_from_date?: int, ads_proceeds_to_date?: int}>, next_offset?: string, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/payments.StarsStatus.html
     * @psalm-impure
     */
    public function getStarsTransactions(bool|null $inbound = null, bool|null $outbound = null, bool|null $ascending = null, bool|null $ton = null, string|null $subscription_id = null, array|int|string|null $peer = null, string|null $offset = '', int|null $limit = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Make a payment using [Telegram Stars, see here Â»](https://core.telegram.org/api/stars#using-stars) for more info.
     *
     * @param array{_: 'inputInvoiceMessage', peer?: array|int|string, msg_id?: int}|array{_: 'inputInvoiceSlug', slug?: string}|array{_: 'inputInvoicePremiumGiftCode', purpose: array{_: 'inputStorePaymentPremiumSubscription', restore?: bool, upgrade?: bool}|array{_: 'inputStorePaymentGiftPremium', user_id?: array|int|string, currency?: string, amount?: int}|array{_: 'inputStorePaymentPremiumGiftCode', users?: list<array|int|string>, boost_peer?: array|int|string, currency?: string, amount?: int, message?: array{_: 'textWithEntities', text?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>}}|array{_: 'inputStorePaymentPremiumGiveaway', only_new_subscribers?: bool, winners_are_visible?: bool, boost_peer?: array|int|string, additional_peers?: list<array|int|string>, countries_iso2?: list<string>, prize_description?: string, until_date?: int, currency?: string, amount?: int}|array{_: 'inputStorePaymentStarsTopup', stars?: int, currency?: string, amount?: int, spend_purpose_peer?: array|int|string}|array{_: 'inputStorePaymentStarsGift', user_id?: array|int|string, stars?: int, currency?: string, amount?: int}|array{_: 'inputStorePaymentStarsGiveaway', only_new_subscribers?: bool, winners_are_visible?: bool, stars?: int, boost_peer?: array|int|string, additional_peers?: list<array|int|string>, countries_iso2?: list<string>, prize_description?: string, until_date?: int, currency?: string, amount?: int, users?: int}|array{_: 'inputStorePaymentAuthCode', restore?: bool, phone_number?: string, phone_code_hash?: string, premium_days?: int, currency?: string, amount?: int}, option: array{_: 'premiumGiftCodeOption', users?: int, months?: int, store_product?: string, store_quantity?: int, currency?: string, amount?: int}}|array{_: 'inputInvoiceStars', purpose: array{_: 'inputStorePaymentPremiumSubscription', restore?: bool, upgrade?: bool}|array{_: 'inputStorePaymentGiftPremium', user_id?: array|int|string, currency?: string, amount?: int}|array{_: 'inputStorePaymentPremiumGiftCode', users?: list<array|int|string>, boost_peer?: array|int|string, currency?: string, amount?: int, message?: array{_: 'textWithEntities', text?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>}}|array{_: 'inputStorePaymentPremiumGiveaway', only_new_subscribers?: bool, winners_are_visible?: bool, boost_peer?: array|int|string, additional_peers?: list<array|int|string>, countries_iso2?: list<string>, prize_description?: string, until_date?: int, currency?: string, amount?: int}|array{_: 'inputStorePaymentStarsTopup', stars?: int, currency?: string, amount?: int, spend_purpose_peer?: array|int|string}|array{_: 'inputStorePaymentStarsGift', user_id?: array|int|string, stars?: int, currency?: string, amount?: int}|array{_: 'inputStorePaymentStarsGiveaway', only_new_subscribers?: bool, winners_are_visible?: bool, stars?: int, boost_peer?: array|int|string, additional_peers?: list<array|int|string>, countries_iso2?: list<string>, prize_description?: string, until_date?: int, currency?: string, amount?: int, users?: int}|array{_: 'inputStorePaymentAuthCode', restore?: bool, phone_number?: string, phone_code_hash?: string, premium_days?: int, currency?: string, amount?: int}}|array{_: 'inputInvoiceChatInviteSubscription', hash?: string}|array{_: 'inputInvoiceStarGift', hide_name?: bool, include_upgrade?: bool, peer?: array|int|string, gift_id?: int, message?: array{_: 'textWithEntities', text?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>}}|array{_: 'inputInvoiceStarGiftUpgrade', stargift: array{_: 'inputSavedStarGiftUser', msg_id?: int}|array{_: 'inputSavedStarGiftChat', peer?: array|int|string, saved_id?: int}|array{_: 'inputSavedStarGiftSlug', slug?: string}, keep_original_details?: bool}|array{_: 'inputInvoiceStarGiftTransfer', stargift: array{_: 'inputSavedStarGiftUser', msg_id?: int}|array{_: 'inputSavedStarGiftChat', peer?: array|int|string, saved_id?: int}|array{_: 'inputSavedStarGiftSlug', slug?: string}, to_id?: array|int|string}|array{_: 'inputInvoicePremiumGiftStars', user_id?: array|int|string, months?: int, message?: array{_: 'textWithEntities', text?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>}}|array{_: 'inputInvoiceBusinessBotTransferStars', bot?: array|int|string, stars?: int}|array{_: 'inputInvoiceStarGiftResale', ton?: bool, show_name?: bool, slug?: string, to_id?: array|int|string, message?: array{_: 'textWithEntities', text?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>}}|array{_: 'inputInvoiceStarGiftPrepaidUpgrade', peer?: array|int|string, hash?: string}|array{_: 'inputInvoicePremiumAuthCode', purpose: array{_: 'inputStorePaymentPremiumSubscription', restore?: bool, upgrade?: bool}|array{_: 'inputStorePaymentGiftPremium', user_id?: array|int|string, currency?: string, amount?: int}|array{_: 'inputStorePaymentPremiumGiftCode', users?: list<array|int|string>, boost_peer?: array|int|string, currency?: string, amount?: int, message?: array{_: 'textWithEntities', text?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>}}|array{_: 'inputStorePaymentPremiumGiveaway', only_new_subscribers?: bool, winners_are_visible?: bool, boost_peer?: array|int|string, additional_peers?: list<array|int|string>, countries_iso2?: list<string>, prize_description?: string, until_date?: int, currency?: string, amount?: int}|array{_: 'inputStorePaymentStarsTopup', stars?: int, currency?: string, amount?: int, spend_purpose_peer?: array|int|string}|array{_: 'inputStorePaymentStarsGift', user_id?: array|int|string, stars?: int, currency?: string, amount?: int}|array{_: 'inputStorePaymentStarsGiveaway', only_new_subscribers?: bool, winners_are_visible?: bool, stars?: int, boost_peer?: array|int|string, additional_peers?: list<array|int|string>, countries_iso2?: list<string>, prize_description?: string, until_date?: int, currency?: string, amount?: int, users?: int}|array{_: 'inputStorePaymentAuthCode', restore?: bool, phone_number?: string, phone_code_hash?: string, premium_days?: int, currency?: string, amount?: int}}|array{_: 'inputInvoiceStarGiftDropOriginalDetails', stargift: array{_: 'inputSavedStarGiftUser', msg_id?: int}|array{_: 'inputSavedStarGiftChat', peer?: array|int|string, saved_id?: int}|array{_: 'inputSavedStarGiftSlug', slug?: string}}|array{_: 'inputInvoiceStarGiftAuctionBid', hide_name?: bool, update_bid?: bool, peer?: array|int|string, gift_id?: int, bid_amount?: int, message?: array{_: 'textWithEntities', text?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>}} $invoice Invoice @see https://docs.madelineproto.xyz/API_docs/types/InputInvoice.html
     * @param int $form_id Payment form ID
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @param ?string $businessConnectionId Business connection ID, received through an updateBotBusinessConnect update.
     * @return array{_: 'payments.paymentResult', updates: array}|array{_: 'payments.paymentVerificationNeeded', url: string} @see https://docs.madelineproto.xyz/API_docs/types/payments.PaymentResult.html
     * @psalm-impure
     */
    public function sendStarsForm(array $invoice, int|null $form_id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null, ?string $businessConnectionId = null): array;

    /**
     * Refund a [Telegram Stars](https://core.telegram.org/api/stars) transaction, see [here Â»](https://core.telegram.org/api/payments#6-refunds) for more info.
     *
     * @param array|int|string $user_id User to refund. @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param string $charge_id Transaction ID.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function refundStarsCharge(array|int|string|null $user_id = null, string|null $charge_id = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get [Telegram Star revenue statistics Â»](https://core.telegram.org/api/stars).
     *
     * @param bool $dark Whether to enable dark theme for graph colors
     * @param bool $ton If set, fetches channel/bot ad revenue statistics in TON.
     * @param array|int|string $peer Get statistics for the specified bot, channel or ourselves ([inputPeerSelf](https://docs.madelineproto.xyz/API_docs/constructors/inputPeerSelf.html)). @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'payments.starsRevenueStats', revenue_graph: array{_: 'statsGraphAsync', token: string}|array{_: 'statsGraphError', error: string}|array{_: 'statsGraph', json: mixed, zoom_token?: string}, status: array{_: 'starsRevenueStatus', current_balance: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, available_balance: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, overall_revenue: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, withdrawal_enabled: bool, next_withdrawal_at?: int}, usd_rate: float, top_hours_graph?: array{_: 'statsGraphAsync', token: string}|array{_: 'statsGraphError', error: string}|array{_: 'statsGraph', json: mixed, zoom_token?: string}} @see https://docs.madelineproto.xyz/API_docs/types/payments.StarsRevenueStats.html
     * @psalm-impure
     */
    public function getStarsRevenueStats(bool|null $dark = null, bool|null $ton = null, array|int|string|null $peer = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Withdraw funds from a channel or bot's [star balance Â»](https://core.telegram.org/api/stars#withdrawing-revenue).
     *
     * @param string|array $password 2FA password, see [here Â»](https://core.telegram.org/api/srp#using-the-2fa-password) for more info. @see https://docs.madelineproto.xyz/API_docs/types/InputCheckPasswordSRP.html
     * @param bool $ton If set, withdraws channel/ad revenue in TON.
     * @param array|int|string $peer Channel or bot from which to withdraw funds. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $amount The amount of stars or nanotons to withdraw.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'payments.starsRevenueWithdrawalUrl', url: string} @see https://docs.madelineproto.xyz/API_docs/types/payments.StarsRevenueWithdrawalUrl.html
     * @psalm-impure
     */
    public function getStarsRevenueWithdrawalUrl(string|array $password, bool|null $ton = null, array|int|string|null $peer = null, int|null $amount = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Returns a URL for a Telegram Ad platform account that can be used to set up advertisements for channel/bot in `peer`, paid using the Telegram Stars owned by the specified `peer`, see [here Â»](https://core.telegram.org/api/stars#paying-for-ads) for more info.
     *
     * @param array|int|string $peer Channel or bot that owns the stars. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'payments.starsRevenueAdsAccountUrl', url: string} @see https://docs.madelineproto.xyz/API_docs/types/payments.StarsRevenueAdsAccountUrl.html
     * @psalm-impure
     */
    public function getStarsRevenueAdsAccountUrl(array|int|string|null $peer = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Obtain info about [Telegram Star transactions Â»](https://core.telegram.org/api/stars#balance-and-transaction-history) using specific transaction IDs.
     *
     * @param bool $ton If set, returns channel/bot ad revenue transactions in nanotons.
     * @param array|int|string $peer Channel or bot. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param list<array{_: 'inputStarsTransaction', refund?: bool, id?: string}>|array<never, never> $id Array of Transaction IDs. @see https://docs.madelineproto.xyz/API_docs/types/InputStarsTransaction.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'payments.starsStatus', balance: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, subscriptions?: list<array{_: 'starsSubscription', peer: array|int|string, pricing: array{_: 'starsSubscriptionPricing', period: int, amount: int}, canceled: bool, can_refulfill: bool, missing_balance: bool, bot_canceled: bool, id: string, until_date: int, chat_invite_hash?: string, title?: string, photo?: array{_: 'webDocument', url: string, access_hash: int, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n: int}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}|array{_: 'webDocumentNoProxy', url: string, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n: int}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}, invoice_slug?: string}>, subscriptions_next_offset?: string, subscriptions_missing_balance?: int, history?: list<array{_: 'starsTransaction', amount: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, peer: array{_: 'starsTransactionPeerUnsupported'}|array{_: 'starsTransactionPeerAppStore'}|array{_: 'starsTransactionPeerPlayMarket'}|array{_: 'starsTransactionPeerPremiumBot'}|array{_: 'starsTransactionPeerFragment'}|array{_: 'starsTransactionPeer', peer: array|int|string}|array{_: 'starsTransactionPeerAds'}|array{_: 'starsTransactionPeerAPI'}, refund: bool, pending: bool, failed: bool, gift: bool, reaction: bool, stargift_upgrade: bool, business_transfer: bool, stargift_resale: bool, posts_search: bool, stargift_prepaid_upgrade: bool, stargift_drop_original_details: bool, phonegroup_message: bool, stargift_auction_bid: bool, offer: bool, id: string, date: int, title?: string, description?: string, photo?: array{_: 'webDocument', url: string, access_hash: int, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n: int}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}|array{_: 'webDocumentNoProxy', url: string, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n: int}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}, transaction_date?: int, transaction_url?: string, bot_payload?: string, msg_id?: int, extended_media?: list<array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}|array{_: 'messageExtendedMedia', media: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaPoll', poll: array{_: 'poll', question: array, id: array, closed: array, public_voters: array, multiple_choice: array, quiz: array, open_answers: array, revoting_disabled: array, shuffle_answers: array, hide_results_until_close: array, creator: array, subscribers_only: array, answers: list<array>, close_period?: array, close_date?: array, countries_iso2?: list<array>, hash: list<array>}, results: array, attached_media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}|array{_: 'storyItem', pinned: array, public: array, close_friends: array, min: array, noforwards: array, edited: array, contacts: array, selected_contacts: array, out: array, id: array, date: array, from_id?: array, fwd_from?: array, expire_date: array, caption?: array, entities?: list<array>, media: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, media_areas?: list<array>, privacy?: list<array>, views?: array, sent_reaction?: array, albums?: list<array>, music?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}|array{_: 'messageExtendedMedia', media: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}>, subscription_period?: int, giveaway_post_id?: int, stargift?: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, floodskip_number?: int, starref_commission_permille?: int, starref_peer?: array|int|string, starref_amount?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, paid_messages?: int, premium_gift_months?: int, ads_proceeds_from_date?: int, ads_proceeds_to_date?: int}>, next_offset?: string, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/payments.StarsStatus.html
     * @psalm-impure
     */
    public function getStarsTransactionsByID(bool|null $ton = null, array|int|string|null $peer = null, array $id = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Obtain a list of [Telegram Stars gift options Â»](https://core.telegram.org/api/stars#buying-or-gifting-stars) as [starsGiftOption](https://docs.madelineproto.xyz/API_docs/constructors/starsGiftOption.html) constructors.
     *
     * @param array|int|string $user_id Receiver of the gift (optional). @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return list<array{_: 'starsGiftOption', extended: bool, stars: int, store_product?: string, currency: string, amount: int}> Array of  @see https://docs.madelineproto.xyz/API_docs/types/StarsGiftOption.html
     * @psalm-impure
     */
    public function getStarsGiftOptions(array|int|string|null $user_id = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Obtain a list of active, expired or cancelled [Telegram Star subscriptions Â»](https://core.telegram.org/api/invites#paid-invite-links).
     *
     * @param bool $missing_balance Whether to return only subscriptions expired due to an excessively low Telegram Star balance.
     * @param array|int|string $peer Always pass [inputPeerSelf](https://docs.madelineproto.xyz/API_docs/constructors/inputPeerSelf.html). @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param string $offset Offset for pagination, taken from [payments.starsStatus](https://docs.madelineproto.xyz/API_docs/constructors/payments.starsStatus.html).`subscriptions_next_offset`.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'payments.starsStatus', balance: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, subscriptions?: list<array{_: 'starsSubscription', peer: array|int|string, pricing: array{_: 'starsSubscriptionPricing', period: int, amount: int}, canceled: bool, can_refulfill: bool, missing_balance: bool, bot_canceled: bool, id: string, until_date: int, chat_invite_hash?: string, title?: string, photo?: array{_: 'webDocument', url: string, access_hash: int, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n: int}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}|array{_: 'webDocumentNoProxy', url: string, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n: int}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}, invoice_slug?: string}>, subscriptions_next_offset?: string, subscriptions_missing_balance?: int, history?: list<array{_: 'starsTransaction', amount: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, peer: array{_: 'starsTransactionPeerUnsupported'}|array{_: 'starsTransactionPeerAppStore'}|array{_: 'starsTransactionPeerPlayMarket'}|array{_: 'starsTransactionPeerPremiumBot'}|array{_: 'starsTransactionPeerFragment'}|array{_: 'starsTransactionPeer', peer: array|int|string}|array{_: 'starsTransactionPeerAds'}|array{_: 'starsTransactionPeerAPI'}, refund: bool, pending: bool, failed: bool, gift: bool, reaction: bool, stargift_upgrade: bool, business_transfer: bool, stargift_resale: bool, posts_search: bool, stargift_prepaid_upgrade: bool, stargift_drop_original_details: bool, phonegroup_message: bool, stargift_auction_bid: bool, offer: bool, id: string, date: int, title?: string, description?: string, photo?: array{_: 'webDocument', url: string, access_hash: int, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n: int}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}|array{_: 'webDocumentNoProxy', url: string, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n: int}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}, transaction_date?: int, transaction_url?: string, bot_payload?: string, msg_id?: int, extended_media?: list<array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}|array{_: 'messageExtendedMedia', media: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaPoll', poll: array{_: 'poll', question: array, id: array, closed: array, public_voters: array, multiple_choice: array, quiz: array, open_answers: array, revoting_disabled: array, shuffle_answers: array, hide_results_until_close: array, creator: array, subscribers_only: array, answers: list<array>, close_period?: array, close_date?: array, countries_iso2?: list<array>, hash: list<array>}, results: array, attached_media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}|array{_: 'storyItem', pinned: array, public: array, close_friends: array, min: array, noforwards: array, edited: array, contacts: array, selected_contacts: array, out: array, id: array, date: array, from_id?: array, fwd_from?: array, expire_date: array, caption?: array, entities?: list<array>, media: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, media_areas?: list<array>, privacy?: list<array>, views?: array, sent_reaction?: array, albums?: list<array>, music?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}|array{_: 'messageExtendedMedia', media: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}>, subscription_period?: int, giveaway_post_id?: int, stargift?: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, floodskip_number?: int, starref_commission_permille?: int, starref_peer?: array|int|string, starref_amount?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, paid_messages?: int, premium_gift_months?: int, ads_proceeds_from_date?: int, ads_proceeds_to_date?: int}>, next_offset?: string, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/payments.StarsStatus.html
     * @psalm-impure
     */
    public function getStarsSubscriptions(bool|null $missing_balance = null, array|int|string|null $peer = null, string|null $offset = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Activate or deactivate a [Telegram Star subscription Â»](https://core.telegram.org/api/invites#paid-invite-links).
     *
     * @param array|int|string $peer Always pass [inputPeerSelf](https://docs.madelineproto.xyz/API_docs/constructors/inputPeerSelf.html). @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param string $subscription_id ID of the subscription.
     * @param bool $canceled Whether to cancel or reactivate the subscription.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function changeStarsSubscription(array|int|string|null $peer = null, string|null $subscription_id = '', bool|null $canceled = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Re-join a private channel associated to an active [Telegram Star subscription Â»](https://core.telegram.org/api/invites#paid-invite-links).
     *
     * @param array|int|string $peer Always pass [inputPeerSelf](https://docs.madelineproto.xyz/API_docs/constructors/inputPeerSelf.html). @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param string $subscription_id ID of the subscription.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function fulfillStarsSubscription(array|int|string|null $peer = null, string|null $subscription_id = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Fetch a list of [star giveaway options Â»](https://core.telegram.org/api/giveaways#star-giveaways).
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return list<array{_: 'starsGiveawayOption', extended: bool, default: bool, stars: int, yearly_boosts: int, store_product?: string, currency: string, amount: int, winners: list<array{_: 'starsGiveawayWinnersOption', default: bool, users: int, per_user_stars: int}>}> Array of  @see https://docs.madelineproto.xyz/API_docs/types/StarsGiveawayOption.html
     * @psalm-impure
     */
    public function getStarsGiveawayOptions(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get a list of available [gifts, see here Â»](https://core.telegram.org/api/gifts) for more info.
     *
     * @param int $hash 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'payments.starGiftsNotModified'}|array{_: 'payments.starGifts', hash: int, gifts: list<array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}>, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/payments.StarGifts.html
     * @psalm-impure
     */
    public function getStarGifts(int|null $hash = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Display or remove a [received gift Â»](https://core.telegram.org/api/gifts) from our profile.
     *
     * @param array{_: 'inputSavedStarGiftUser', msg_id?: int}|array{_: 'inputSavedStarGiftChat', peer?: array|int|string, saved_id?: int}|array{_: 'inputSavedStarGiftSlug', slug?: string} $stargift The gift to display or remove. @see https://docs.madelineproto.xyz/API_docs/types/InputSavedStarGift.html
     * @param bool $unsave If set, hides the gift from our profile.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function saveStarGift(array $stargift, bool|null $unsave = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Convert a [received gift Â»](https://core.telegram.org/api/gifts) into Telegram Stars: this will permanently destroy the gift, converting it into [starGift](https://docs.madelineproto.xyz/API_docs/constructors/starGift.html).`convert_stars` [Telegram Stars](https://core.telegram.org/api/stars), added to the user's balance.
     * 
     * Note that [starGift](https://docs.madelineproto.xyz/API_docs/constructors/starGift.html).`convert_stars` will be less than the buying price ([starGift](https://docs.madelineproto.xyz/API_docs/constructors/starGift.html).`stars`) of the gift if it was originally bought using Telegram Stars bought a long time ago.
     *
     * @param array{_: 'inputSavedStarGiftUser', msg_id?: int}|array{_: 'inputSavedStarGiftChat', peer?: array|int|string, saved_id?: int}|array{_: 'inputSavedStarGiftSlug', slug?: string} $stargift The gift to convert. @see https://docs.madelineproto.xyz/API_docs/types/InputSavedStarGift.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @param ?string $businessConnectionId Business connection ID, received through an updateBotBusinessConnect update.
     * @return bool 
     * @psalm-impure
     */
    public function convertStarGift(array $stargift, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null, ?string $businessConnectionId = null): bool;

    /**
     * Cancel a [bot subscription](https://core.telegram.org/api/subscriptions#bot-subscriptions)
     *
     * @param bool $restore If **not** set, disables autorenewal of the subscriptions, and prevents the user from reactivating the subscription once the current period expires: a subscription cancelled by the bot will have the [starsSubscription](https://docs.madelineproto.xyz/API_docs/constructors/starsSubscription.html).`bot_canceled` flag set.  <br>The bot can can partially undo this operation by setting this flag: this will allow the user to reactivate the subscription.
     * @param array|int|string $user_id The ID of the user whose subscription should be (un)cancelled @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param string $charge_id The `provider_charge_id` from the [messageActionPaymentSentMe](https://docs.madelineproto.xyz/API_docs/constructors/messageActionPaymentSentMe.html) service message sent to the bot for the first subscription payment.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function botCancelStarsSubscription(bool|null $restore = null, array|int|string|null $user_id = null, string|null $charge_id = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Fetch all affiliations we have created for a certain peer
     *
     * @param array|int|string $peer The affiliated peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $offset_date If set, returns only results older than the specified unixtime
     * @param string $offset_link Offset for [pagination](https://core.telegram.org/api/offsets), taken from the last returned [connectedBotStarRef](https://docs.madelineproto.xyz/API_docs/constructors/connectedBotStarRef.html).`url` (initially empty)
     * @param int $limit Maximum number of results to return, [see pagination](https://core.telegram.org/api/offsets)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'payments.connectedStarRefBots', count: int, connected_bots: list<array{_: 'connectedBotStarRef', revoked: bool, url: string, date: int, bot_id: int, commission_permille: int, duration_months?: int, participants: int, revenue: int}>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/payments.ConnectedStarRefBots.html
     * @psalm-impure
     */
    public function getConnectedStarRefBots(array|int|string|null $peer = null, int|null $offset_date = null, string|null $offset_link = null, int|null $limit = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Fetch info about a specific [bot affiliation Â»](https://core.telegram.org/api/bots/referrals)
     *
     * @param array|int|string $peer The affiliated peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param array|int|string $bot The bot that offers the affiliate program @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'payments.connectedStarRefBots', count: int, connected_bots: list<array{_: 'connectedBotStarRef', revoked: bool, url: string, date: int, bot_id: int, commission_permille: int, duration_months?: int, participants: int, revenue: int}>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/payments.ConnectedStarRefBots.html
     * @psalm-impure
     */
    public function getConnectedStarRefBot(array|int|string|null $peer = null, array|int|string|null $bot = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Obtain a list of suggested [mini apps](https://core.telegram.org/api/bots/webapps) with available [affiliate programs](https://core.telegram.org/api/bots/referrals)
     * 
     * `order_by_revenue` and `order_by_date` are mutually exclusive: if neither is set, results are sorted by profitability.
     *
     * @param bool $order_by_revenue If set, orders results by the expected revenue
     * @param bool $order_by_date If set, orders results by the creation date of the affiliate program
     * @param array|int|string $peer The peer that will become the affiliate: star commissions will be transferred to this peer's star balance. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param string $offset Offset for pagination, taken from [payments.suggestedStarRefBots](https://docs.madelineproto.xyz/API_docs/constructors/payments.suggestedStarRefBots.html).`next_offset`, initially empty.
     * @param int $limit Maximum number of results to return, [see pagination](https://core.telegram.org/api/offsets)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'payments.suggestedStarRefBots', count: int, suggested_bots: list<array{_: 'starRefProgram', bot_id: int, commission_permille: int, duration_months?: int, end_date?: int, daily_revenue_per_user?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}}>, users: list<array|int|string>, next_offset?: string} @see https://docs.madelineproto.xyz/API_docs/types/payments.SuggestedStarRefBots.html
     * @psalm-impure
     */
    public function getSuggestedStarRefBots(bool|null $order_by_revenue = null, bool|null $order_by_date = null, array|int|string|null $peer = null, string|null $offset = '', int|null $limit = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Join a bot's [affiliate program, becoming an affiliate Â»](https://core.telegram.org/api/bots/referrals#becoming-an-affiliate)
     *
     * @param array|int|string $peer The peer that will become the affiliate: star commissions will be transferred to this peer's star balance. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param array|int|string $bot The bot that offers the affiliate program @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'payments.connectedStarRefBots', count: int, connected_bots: list<array{_: 'connectedBotStarRef', revoked: bool, url: string, date: int, bot_id: int, commission_permille: int, duration_months?: int, participants: int, revenue: int}>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/payments.ConnectedStarRefBots.html
     * @psalm-impure
     */
    public function connectStarRefBot(array|int|string|null $peer = null, array|int|string|null $bot = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Leave a bot's [affiliate program Â»](https://core.telegram.org/api/bots/referrals#becoming-an-affiliate)
     *
     * @param bool $revoked If set, leaves the bot's affiliate program
     * @param array|int|string $peer The peer that was affiliated @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param string $link The affiliate link to revoke
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'payments.connectedStarRefBots', count: int, connected_bots: list<array{_: 'connectedBotStarRef', revoked: bool, url: string, date: int, bot_id: int, commission_permille: int, duration_months?: int, participants: int, revenue: int}>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/payments.ConnectedStarRefBots.html
     * @psalm-impure
     */
    public function editConnectedStarRefBot(bool|null $revoked = null, array|int|string|null $peer = null, string|null $link = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Obtain a preview of the possible attributes (chosen randomly) a [gift Â»](https://core.telegram.org/api/gifts) can receive after upgrading it to a [collectible gift Â»](https://core.telegram.org/api/gifts#collectible-gifts), see [here Â»](https://core.telegram.org/api/gifts#collectible-gifts) for more info.
     *
     * @param int $gift_id The gift to upgrade.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'payments.starGiftUpgradePreview', sample_attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}>, prices: list<array{_: 'starGiftUpgradePrice', date: int, upgrade_stars: int}>, next_prices: list<array{_: 'starGiftUpgradePrice', date: int, upgrade_stars: int}>} @see https://docs.madelineproto.xyz/API_docs/types/payments.StarGiftUpgradePreview.html
     * @psalm-impure
     */
    public function getStarGiftUpgradePreview(int|null $gift_id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Upgrade a [gift](https://core.telegram.org/api/gifts) to a [collectible gift](https://core.telegram.org/api/gifts#collectible-gifts): can only be used if the upgrade was already paid by the gift sender; see [here Â»](https://core.telegram.org/api/gifts#upgrade-a-gift-to-a-collectible-gift) for more info on the full flow (including the different flow to use in case the upgrade was not paid by the gift sender).
     *
     * @param array{_: 'inputSavedStarGiftUser', msg_id?: int}|array{_: 'inputSavedStarGiftChat', peer?: array|int|string, saved_id?: int}|array{_: 'inputSavedStarGiftSlug', slug?: string} $stargift The gift to upgrade @see https://docs.madelineproto.xyz/API_docs/types/InputSavedStarGift.html
     * @param bool $keep_original_details Set this flag to keep the original gift text, sender and receiver in the upgraded gift as a [starGiftAttributeOriginalDetails](https://docs.madelineproto.xyz/API_docs/constructors/starGiftAttributeOriginalDetails.html) attribute.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @param ?string $businessConnectionId Business connection ID, received through an updateBotBusinessConnect update.
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function upgradeStarGift(array $stargift, bool|null $keep_original_details = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null, ?string $businessConnectionId = null): array;

    /**
     * Transfer a [collectible gift](https://core.telegram.org/api/gifts#collectible-gifts) to another user or channel: can only be used if transfer is free (i.e. [messageActionStarGiftUnique](https://docs.madelineproto.xyz/API_docs/constructors/messageActionStarGiftUnique.html).`transfer_stars` is not set); see [here Â»](https://core.telegram.org/api/gifts#transferring-collectible-gifts) for more info on the full flow (including the different flow to use in case the transfer isn't free).
     *
     * @param array{_: 'inputSavedStarGiftUser', msg_id?: int}|array{_: 'inputSavedStarGiftChat', peer?: array|int|string, saved_id?: int}|array{_: 'inputSavedStarGiftSlug', slug?: string} $stargift The gift to transfer. @see https://docs.madelineproto.xyz/API_docs/types/InputSavedStarGift.html
     * @param array|int|string $to_id Destination peer. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @param ?string $businessConnectionId Business connection ID, received through an updateBotBusinessConnect update.
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function transferStarGift(array $stargift, array|int|string|null $to_id = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null, ?string $businessConnectionId = null): array;

    /**
     * Obtain info about a [collectible gift Â»](https://core.telegram.org/api/gifts#collectible-gifts) using a slug obtained from a [collectible gift link Â»](https://core.telegram.org/api/links#collectible-gift-link).
     *
     * @param string $slug The slug.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'payments.uniqueStarGift', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/payments.UniqueStarGift.html
     * @psalm-impure
     */
    public function getUniqueStarGift(string|null $slug = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Fetch the full list of [gifts](https://core.telegram.org/api/gifts) owned by a peer.
     * 
     * Note that unlike what the name suggests, the method can be used to fetch both "saved" and "unsaved" gifts (aka gifts both pinned and not pinned) to the profile, depending on the passed flags.
     *
     * @param bool $exclude_unsaved Exclude gifts not pinned on the profile.
     * @param bool $exclude_saved Exclude gifts pinned on the profile.
     * @param bool $exclude_unlimited Exclude gifts that do not have the [starGift](https://docs.madelineproto.xyz/API_docs/constructors/starGift.html).`limited` flag set.
     * @param bool $exclude_unique Exclude [collectible gifts Â»](https://core.telegram.org/api/gifts#collectible-gifts).
     * @param bool $sort_by_value If set, sorts the gifts by price instead of reception date.
     * @param bool $exclude_upgradable Exclude gifts that can be [upgraded to collectible gifts Â»](https://core.telegram.org/api/gifts#collectible-gifts).
     * @param bool $exclude_unupgradable Exclude gifts that cannot be [upgraded to collectible gifts Â»](https://core.telegram.org/api/gifts#collectible-gifts).
     * @param bool $peer_color_available 
     * @param bool $exclude_hosted 
     * @param array|int|string $peer Fetch only gifts owned by the specified peer, such as: a user, with peer=[inputPeerUser](https://docs.madelineproto.xyz/API_docs/constructors/inputPeerUser.html); a channel, with peer=[inputPeerChannel](https://docs.madelineproto.xyz/API_docs/constructors/inputPeerChannel.html); a [connected business user](https://core.telegram.org/api/bots/connected-business-bots) (when executing the method as a bot, over the business connection), with peer=[inputPeerUser](https://docs.madelineproto.xyz/API_docs/constructors/inputPeerUser.html). @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $collection_id Only returns gifts within the specified [collection Â»](https://core.telegram.org/api/gifts#gift-collections).
     * @param string $offset [Offset for pagination](https://core.telegram.org/api/offsets).
     * @param int $limit Maximum number of results to return, [see pagination](https://core.telegram.org/api/offsets)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @param ?string $businessConnectionId Business connection ID, received through an updateBotBusinessConnect update.
     * @return array{_: 'payments.savedStarGifts', count: int, chat_notifications_enabled?: bool, gifts: list<array{_: 'savedStarGift', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, name_hidden: bool, unsaved: bool, refunded: bool, can_upgrade: bool, pinned_to_top: bool, upgrade_separate: bool, from_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}, msg_id?: int, saved_id?: int, convert_stars?: int, upgrade_stars?: int, can_export_at?: int, transfer_stars?: int, can_transfer_at?: int, can_resell_at?: int, collection_id?: list<int>, prepaid_upgrade_hash?: string, drop_original_details_stars?: int, gift_num?: int, can_craft_at?: int}>, next_offset?: string, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/payments.SavedStarGifts.html
     * @psalm-impure
     */
    public function getSavedStarGifts(bool|null $exclude_unsaved = null, bool|null $exclude_saved = null, bool|null $exclude_unlimited = null, bool|null $exclude_unique = null, bool|null $sort_by_value = null, bool|null $exclude_upgradable = null, bool|null $exclude_unupgradable = null, bool|null $peer_color_available = null, bool|null $exclude_hosted = null, array|int|string|null $peer = null, int|null $collection_id = null, string|null $offset = '', int|null $limit = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null, ?string $businessConnectionId = null): array;

    /**
     * Fetch info about specific [gifts](https://core.telegram.org/api/gifts) owned by a peer we control.
     * 
     * Note that unlike what the name suggests, the method can be used to fetch both "saved" and "unsaved" gifts (aka gifts both pinned and not pinned to the profile).
     *
     * @param list<array{_: 'inputSavedStarGiftUser', msg_id?: int}|array{_: 'inputSavedStarGiftChat', peer?: array|int|string, saved_id?: int}|array{_: 'inputSavedStarGiftSlug', slug?: string}>|array<never, never> $stargift Array of List of gifts to fetch info about. @see https://docs.madelineproto.xyz/API_docs/types/InputSavedStarGift.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'payments.savedStarGifts', count: int, chat_notifications_enabled?: bool, gifts: list<array{_: 'savedStarGift', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, name_hidden: bool, unsaved: bool, refunded: bool, can_upgrade: bool, pinned_to_top: bool, upgrade_separate: bool, from_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}, msg_id?: int, saved_id?: int, convert_stars?: int, upgrade_stars?: int, can_export_at?: int, transfer_stars?: int, can_transfer_at?: int, can_resell_at?: int, collection_id?: list<int>, prepaid_upgrade_hash?: string, drop_original_details_stars?: int, gift_num?: int, can_craft_at?: int}>, next_offset?: string, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/payments.SavedStarGifts.html
     * @psalm-impure
     */
    public function getSavedStarGift(array $stargift = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Convert a [collectible gift Â»](https://core.telegram.org/api/gifts) to an NFT on the TON blockchain.
     *
     * @param array{_: 'inputSavedStarGiftUser', msg_id?: int}|array{_: 'inputSavedStarGiftChat', peer?: array|int|string, saved_id?: int}|array{_: 'inputSavedStarGiftSlug', slug?: string} $stargift The collectible gift to export. @see https://docs.madelineproto.xyz/API_docs/types/InputSavedStarGift.html
     * @param string $tmp_password 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'payments.starGiftWithdrawalUrl', url: string} @see https://docs.madelineproto.xyz/API_docs/types/payments.StarGiftWithdrawalUrl.html
     * @psalm-impure
     */
    public function getStarGiftWithdrawalUrl(array $stargift, string|null $tmp_password = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Enables or disables the reception of notifications every time a [gift Â»](https://core.telegram.org/api/gifts) is received by the specified channel, can only be invoked by admins with `post_messages` [admin rights](https://docs.madelineproto.xyz/API_docs/constructors/chatAdminRights.html).
     *
     * @param bool $enabled Whether to enable or disable reception of notifications in the form of [messageActionStarGiftUnique](https://docs.madelineproto.xyz/API_docs/constructors/messageActionStarGiftUnique.html) and [messageActionStarGift](https://docs.madelineproto.xyz/API_docs/constructors/messageActionStarGift.html) service messages from the channel.
     * @param array|int|string $peer The channel for which to receive or not receive notifications. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function toggleChatStarGiftNotifications(bool|null $enabled = null, array|int|string|null $peer = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Pins a received gift on top of the profile of the user or owned channels by using [payments.toggleStarGiftsPinnedToTop](https://docs.madelineproto.xyz/API_docs/methods/payments.toggleStarGiftsPinnedToTop.html).
     *
     * @param array|int|string $peer The peer where to pin the gift. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param list<array{_: 'inputSavedStarGiftUser', msg_id?: int}|array{_: 'inputSavedStarGiftChat', peer?: array|int|string, saved_id?: int}|array{_: 'inputSavedStarGiftSlug', slug?: string}>|array<never, never> $stargift Array of The gift to pin. @see https://docs.madelineproto.xyz/API_docs/types/InputSavedStarGift.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function toggleStarGiftsPinnedToTop(array|int|string|null $peer = null, array $stargift = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Checks whether a purchase is possible. Must be called before in-store purchase, official apps only.
     *
     * @param array{_: 'inputStorePaymentPremiumSubscription', restore?: bool, upgrade?: bool}|array{_: 'inputStorePaymentGiftPremium', user_id?: array|int|string, currency?: string, amount?: int}|array{_: 'inputStorePaymentPremiumGiftCode', users?: list<array|int|string>, boost_peer?: array|int|string, currency?: string, amount?: int, message?: array{_: 'textWithEntities', text?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>}}|array{_: 'inputStorePaymentPremiumGiveaway', only_new_subscribers?: bool, winners_are_visible?: bool, boost_peer?: array|int|string, additional_peers?: list<array|int|string>, countries_iso2?: list<string>, prize_description?: string, until_date?: int, currency?: string, amount?: int}|array{_: 'inputStorePaymentStarsTopup', stars?: int, currency?: string, amount?: int, spend_purpose_peer?: array|int|string}|array{_: 'inputStorePaymentStarsGift', user_id?: array|int|string, stars?: int, currency?: string, amount?: int}|array{_: 'inputStorePaymentStarsGiveaway', only_new_subscribers?: bool, winners_are_visible?: bool, stars?: int, boost_peer?: array|int|string, additional_peers?: list<array|int|string>, countries_iso2?: list<string>, prize_description?: string, until_date?: int, currency?: string, amount?: int, users?: int}|array{_: 'inputStorePaymentAuthCode', restore?: bool, phone_number?: string, phone_code_hash?: string, premium_days?: int, currency?: string, amount?: int} $purpose Payment purpose. @see https://docs.madelineproto.xyz/API_docs/types/InputStorePaymentPurpose.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function canPurchaseStore(array $purpose, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Get [collectible gifts](https://core.telegram.org/api/gifts#collectible-gifts) of a specific type currently on resale, see [here Â»](https://core.telegram.org/api/gifts#reselling-collectible-gifts) for more info.
     * 
     * `sort_by_price` and `sort_by_num` are mutually exclusive, if neither are set results are sorted by the unixtime (descending) when their resell price was last changed.
     * 
     * See [here Â»](https://core.telegram.org/api/gifts#sending-gifts) for detailed documentation on this method.
     *
     * @param bool $sort_by_price Sort gifts by price (ascending).
     * @param bool $sort_by_num Sort gifts by number (ascending).
     * @param bool $for_craft 
     * @param bool $stars_only 
     * @param int $attributes_hash If a previous call to the method was made and [payments.resaleStarGifts](https://docs.madelineproto.xyz/API_docs/constructors/payments.resaleStarGifts.html).`attributes_hash` was set, pass it here to avoid returning any results if they haven't changed. <br>Otherwise, set this flag and pass `0` to return [payments.resaleStarGifts](https://docs.madelineproto.xyz/API_docs/constructors/payments.resaleStarGifts.html).`attributes_hash` and [payments.resaleStarGifts](https://docs.madelineproto.xyz/API_docs/constructors/payments.resaleStarGifts.html).`attributes`, **these two fields will not be set** if this flag is not set.
     * @param int $gift_id Mandatory identifier of the base gift from which the collectible gift was upgraded.
     * @param list<array{_: 'starGiftAttributeIdModel', document_id?: int}|array{_: 'starGiftAttributeIdPattern', document_id?: int}|array{_: 'starGiftAttributeIdBackdrop', backdrop_id?: int}> $attributes Array of Optionally filter gifts with the specified attributes. If no attributes of a specific type are specified, all attributes of that type are allowed. @see https://docs.madelineproto.xyz/API_docs/types/StarGiftAttributeId.html
     * @param string $offset Offset for pagination. If not equal to an empty string, [payments.resaleStarGifts](https://docs.madelineproto.xyz/API_docs/constructors/payments.resaleStarGifts.html).`counters` will not be set to avoid returning the counters every time a new page is fetched.
     * @param int $limit Maximum number of results to return, [see pagination](https://core.telegram.org/api/offsets)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'payments.resaleStarGifts', count: int, gifts: list<array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}>, next_offset?: string, attributes?: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}>, attributes_hash?: int, chats: list<array|int|string>, counters?: list<array{_: 'starGiftAttributeCounter', attribute: array{_: 'starGiftAttributeIdModel', document_id: int}|array{_: 'starGiftAttributeIdPattern', document_id: int}|array{_: 'starGiftAttributeIdBackdrop', backdrop_id: int}, count: int}>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/payments.ResaleStarGifts.html
     * @psalm-impure
     */
    public function getResaleStarGifts(bool|null $sort_by_price = null, bool|null $sort_by_num = null, bool|null $for_craft = null, bool|null $stars_only = null, int|null $attributes_hash = null, int|null $gift_id = 0, array|null $attributes = null, string|null $offset = '', int|null $limit = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * A [collectible gift we own Â»](https://core.telegram.org/api/gifts#collectible-gifts) can be put up for sale on the [gift marketplace Â»](https://telegram.org/blog/gift-marketplace-and-more) with this method, see [here Â»](https://core.telegram.org/api/gifts#reselling-collectible-gifts) for more info.
     *
     * @param array{_: 'inputSavedStarGiftUser', msg_id?: int}|array{_: 'inputSavedStarGiftChat', peer?: array|int|string, saved_id?: int}|array{_: 'inputSavedStarGiftSlug', slug?: string} $stargift The gift to resell. @see https://docs.madelineproto.xyz/API_docs/types/InputSavedStarGift.html
     * @param array{_: 'starsAmount', amount?: int, nanos?: int}|array{_: 'starsTonAmount', amount?: int} $resell_amount Resale price of the gift. @see https://docs.madelineproto.xyz/API_docs/types/StarsAmount.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function updateStarGiftPrice(array $stargift, array $resell_amount, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Create a [star gift collection Â»](https://core.telegram.org/api/gifts#gift-collections).
     *
     * @param array|int|string $peer Peer where to create the collection. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param string $title Title of the collection.
     * @param list<array{_: 'inputSavedStarGiftUser', msg_id?: int}|array{_: 'inputSavedStarGiftChat', peer?: array|int|string, saved_id?: int}|array{_: 'inputSavedStarGiftSlug', slug?: string}>|array<never, never> $stargift Array of Gifts added to the collection. @see https://docs.madelineproto.xyz/API_docs/types/InputSavedStarGift.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'starGiftCollection', collection_id: int, title: string, icon?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, gifts_count: int, hash: list<int|string>} @see https://docs.madelineproto.xyz/API_docs/types/StarGiftCollection.html
     * @psalm-impure
     */
    public function createStarGiftCollection(array|int|string|null $peer = null, string|null $title = '', array $stargift = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Add or remove gifts from a [star gift collection Â»](https://core.telegram.org/api/gifts#gift-collections), or rename the collection.
     *
     * @param array|int|string $peer Peer that owns the collection. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $collection_id Collection ID.
     * @param string $title Title of the collection, to rename the collection.
     * @param list<array{_: 'inputSavedStarGiftUser', msg_id?: int}|array{_: 'inputSavedStarGiftChat', peer?: array|int|string, saved_id?: int}|array{_: 'inputSavedStarGiftSlug', slug?: string}> $delete_stargift Array of Can contain a list of gifts to remove from the collection. @see https://docs.madelineproto.xyz/API_docs/types/InputSavedStarGift.html
     * @param list<array{_: 'inputSavedStarGiftUser', msg_id?: int}|array{_: 'inputSavedStarGiftChat', peer?: array|int|string, saved_id?: int}|array{_: 'inputSavedStarGiftSlug', slug?: string}> $add_stargift Array of Can contain a list of gifts to add to the collection. @see https://docs.madelineproto.xyz/API_docs/types/InputSavedStarGift.html
     * @param list<array{_: 'inputSavedStarGiftUser', msg_id?: int}|array{_: 'inputSavedStarGiftChat', peer?: array|int|string, saved_id?: int}|array{_: 'inputSavedStarGiftSlug', slug?: string}> $order Array of Can contain the new gift order. @see https://docs.madelineproto.xyz/API_docs/types/InputSavedStarGift.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'starGiftCollection', collection_id: int, title: string, icon?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, gifts_count: int, hash: list<int|string>} @see https://docs.madelineproto.xyz/API_docs/types/StarGiftCollection.html
     * @psalm-impure
     */
    public function updateStarGiftCollection(array|int|string|null $peer = null, int|null $collection_id = 0, string|null $title = null, array|null $delete_stargift = null, array|null $add_stargift = null, array|null $order = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Reorder the [star gift collections Â»](https://core.telegram.org/api/gifts#gift-collections) on an owned peer's profile.
     *
     * @param array|int|string $peer The owned peer. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param list<int>|array<never, never> $order New collection order.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function reorderStarGiftCollections(array|int|string|null $peer = null, array $order = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Delete a [star gift collection Â»](https://core.telegram.org/api/gifts#gift-collections).
     *
     * @param array|int|string $peer Peer that owns the collection. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $collection_id ID of the collection.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function deleteStarGiftCollection(array|int|string|null $peer = null, int|null $collection_id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Fetches all [star gift collections Â»](https://core.telegram.org/api/gifts#gift-collections) of a peer.
     *
     * @param array|int|string $peer The peer. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param list<int|string>|array<never, never> $hash Array of Hash ([generated as specified here Â»](https://core.telegram.org/api/offsets#hash-generation)) using the [starGiftCollection](https://docs.madelineproto.xyz/API_docs/constructors/starGiftCollection.html).`hash` field (**not** the `collection_id` field) of all collections returned by a previous method call, to avoid refetching the result if it hasn't changed. @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'payments.starGiftCollectionsNotModified'}|array{_: 'payments.starGiftCollections', collections: list<array{_: 'starGiftCollection', collection_id: int, title: string, icon?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, gifts_count: int, hash: list<int|string>}>} @see https://docs.madelineproto.xyz/API_docs/types/payments.StarGiftCollections.html
     * @psalm-impure
     */
    public function getStarGiftCollections(array|int|string|null $peer = null, array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get information about the value of a [collectible gift Â»](https://core.telegram.org/api/gifts#collectible-gifts).
     *
     * @param string $slug `slug` from a [starGiftUnique](https://docs.madelineproto.xyz/API_docs/constructors/starGiftUnique.html).
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'payments.uniqueStarGiftValueInfo', last_sale_on_fragment: bool, value_is_average: bool, currency: string, value: int, initial_sale_date: int, initial_sale_stars: int, initial_sale_price: int, last_sale_date?: int, last_sale_price?: int, floor_price?: int, average_price?: int, listed_count?: int, fragment_listed_count?: int, fragment_listed_url?: string} @see https://docs.madelineproto.xyz/API_docs/types/payments.UniqueStarGiftValueInfo.html
     * @psalm-impure
     */
    public function getUniqueStarGiftValueInfo(string|null $slug = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Check if the specified [gift Â»](https://core.telegram.org/api/gifts) can be sent.
     *
     * @param int $gift_id Gift ID.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'payments.checkCanSendGiftResultOk'}|array{_: 'payments.checkCanSendGiftResultFail', reason: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}} @see https://docs.madelineproto.xyz/API_docs/types/payments.CheckCanSendGiftResult.html
     * @psalm-impure
     */
    public function checkCanSendGift(int|null $gift_id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param array{_: 'inputStarGiftAuction', gift_id?: int}|array{_: 'inputStarGiftAuctionSlug', slug?: string} $auction @see https://docs.madelineproto.xyz/API_docs/types/InputStarGiftAuction.html
     * @param int $version 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'payments.starGiftAuctionState', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, state: array{_: 'starGiftAuctionStateNotModified'}|array{_: 'starGiftAuctionState', version: int, start_date: int, end_date: int, min_bid_amount: int, bid_levels: list<array{_: 'auctionBidLevel', pos: int, amount: int, date: int}>, top_bidders: list<int>, next_round_at: int, last_gift_num: int, gifts_left: int, current_round: int, total_rounds: int, rounds: list<array{_: 'starGiftAuctionRound', num: int, duration: int}|array{_: 'starGiftAuctionRoundExtendable', num: int, duration: int, extend_top: int, extend_window: int}>}|array{_: 'starGiftAuctionStateFinished', start_date: int, end_date: int, average_price: int, listed_count?: int, fragment_listed_count?: int, fragment_listed_url?: string}, user_state: array{_: 'starGiftAuctionUserState', returned: bool, bid_amount?: int, bid_date?: int, min_bid_amount?: int, bid_peer?: array|int|string, acquired_count: int}, timeout: int, users: list<array|int|string>, chats: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/payments.StarGiftAuctionState.html
     * @psalm-impure
     */
    public function getStarGiftAuctionState(array $auction, int|null $version = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param int $gift_id 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'payments.starGiftAuctionAcquiredGifts', gifts: list<array{_: 'starGiftAuctionAcquiredGift', peer: array|int|string, name_hidden: bool, date: int, bid_amount: int, round: int, pos: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}, gift_num?: int}>, users: list<array|int|string>, chats: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/payments.StarGiftAuctionAcquiredGifts.html
     * @psalm-impure
     */
    public function getStarGiftAuctionAcquiredGifts(int|null $gift_id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param list<int|string>|array<never, never> $hash Array of  @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'payments.starGiftActiveAuctionsNotModified'}|array{_: 'payments.starGiftActiveAuctions', auctions: list<array{_: 'starGiftActiveAuctionState', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, state: array{_: 'starGiftAuctionStateNotModified'}|array{_: 'starGiftAuctionState', version: int, start_date: int, end_date: int, min_bid_amount: int, bid_levels: list<array{_: 'auctionBidLevel', pos: int, amount: int, date: int}>, top_bidders: list<int>, next_round_at: int, last_gift_num: int, gifts_left: int, current_round: int, total_rounds: int, rounds: list<array{_: 'starGiftAuctionRound', num: int, duration: int}|array{_: 'starGiftAuctionRoundExtendable', num: int, duration: int, extend_top: int, extend_window: int}>}|array{_: 'starGiftAuctionStateFinished', start_date: int, end_date: int, average_price: int, listed_count?: int, fragment_listed_count?: int, fragment_listed_url?: string}, user_state: array{_: 'starGiftAuctionUserState', returned: bool, bid_amount?: int, bid_date?: int, min_bid_amount?: int, bid_peer?: array|int|string, acquired_count: int}}>, users: list<array|int|string>, chats: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/payments.StarGiftActiveAuctions.html
     * @psalm-impure
     */
    public function getStarGiftActiveAuctions(array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param bool $decline 
     * @param int $offer_msg_id 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function resolveStarGiftOffer(bool|null $decline = null, int|null $offer_msg_id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param array{_: 'starsAmount', amount?: int, nanos?: int}|array{_: 'starsTonAmount', amount?: int} $price @see https://docs.madelineproto.xyz/API_docs/types/StarsAmount.html
     * @param array|int|string $peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param string $slug 
     * @param int $duration 
     * @param int $allow_paid_stars 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function sendStarGiftOffer(array $price, array|int|string|null $peer = null, string|null $slug = '', int|null $duration = 0, int|null $allow_paid_stars = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param int $gift_id 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'payments.starGiftUpgradeAttributes', attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}>} @see https://docs.madelineproto.xyz/API_docs/types/payments.StarGiftUpgradeAttributes.html
     * @psalm-impure
     */
    public function getStarGiftUpgradeAttributes(int|null $gift_id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param int $gift_id 
     * @param string $offset 
     * @param int $limit 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'payments.savedStarGifts', count: int, chat_notifications_enabled?: bool, gifts: list<array{_: 'savedStarGift', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, name_hidden: bool, unsaved: bool, refunded: bool, can_upgrade: bool, pinned_to_top: bool, upgrade_separate: bool, from_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}, msg_id?: int, saved_id?: int, convert_stars?: int, upgrade_stars?: int, can_export_at?: int, transfer_stars?: int, can_transfer_at?: int, can_resell_at?: int, collection_id?: list<int>, prepaid_upgrade_hash?: string, drop_original_details_stars?: int, gift_num?: int, can_craft_at?: int}>, next_offset?: string, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/payments.SavedStarGifts.html
     * @psalm-impure
     */
    public function getCraftStarGifts(int|null $gift_id = 0, string|null $offset = '', int|null $limit = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param list<array{_: 'inputSavedStarGiftUser', msg_id?: int}|array{_: 'inputSavedStarGiftChat', peer?: array|int|string, saved_id?: int}|array{_: 'inputSavedStarGiftSlug', slug?: string}>|array<never, never> $stargift Array of  @see https://docs.madelineproto.xyz/API_docs/types/InputSavedStarGift.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function craftStarGift(array $stargift = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'payments.currencyRates', rates: list<array{_: 'currencyRate', rate: float, currency: string}>} @see https://docs.madelineproto.xyz/API_docs/types/payments.CurrencyRates.html
     * @psalm-impure
     */
    public function getCurrencyRates(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param string $crypto_currency 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return list<array{_: 'onrampProviderInfo', supports_base_currencies: bool, supports_limits: bool, supports_quote: bool, id: string, name: string, crypto_currencies: list<string>}> Array of  @see https://docs.madelineproto.xyz/API_docs/types/OnrampProviderInfo.html
     * @psalm-impure
     */
    public function getOnrampProviders(string|null $crypto_currency = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param string $provider 
     * @param string $crypto_currency 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return list<string> 
     * @psalm-impure
     */
    public function getOnrampBaseCurrencies(string|null $provider = '', string|null $crypto_currency = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param string $provider 
     * @param string $crypto_currency 
     * @param string $base_currency 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'onrampAvailability', allowed: bool, buy_allowed: bool, country_code: string, state?: string, methods: list<array{_: 'onrampMethodAvailability', available: bool, payment_method: string}>} @see https://docs.madelineproto.xyz/API_docs/types/OnrampAvailability.html
     * @psalm-impure
     */
    public function getOnrampAvailability(string|null $provider = '', string|null $crypto_currency = '', string|null $base_currency = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param string $provider 
     * @param string $crypto_currency 
     * @param string $base_currency 
     * @param string $payment_method 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'onrampLimits', base_currency: string, base_min_amount: string, base_max_amount: string, crypto_min_amount: string, crypto_max_amount: string, payment_method: string} @see https://docs.madelineproto.xyz/API_docs/types/OnrampLimits.html
     * @psalm-impure
     */
    public function getOnrampLimits(string|null $provider = '', string|null $crypto_currency = '', string|null $base_currency = '', string|null $payment_method = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param string $provider 
     * @param string $crypto_currency 
     * @param string $base_currency 
     * @param string $base_amount 
     * @param string $crypto_amount 
     * @param string $payment_method 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'onrampQuote', base_currency: string, base_amount: string, crypto_currency: string, crypto_amount: string, crypto_price: string, fee_amount: string, extra_fee_amount: string, network_fee_amount: string, total_amount: string, payment_method: string, expires_date: int} @see https://docs.madelineproto.xyz/API_docs/types/OnrampQuote.html
     * @psalm-impure
     */
    public function getOnrampQuote(string|null $provider = '', string|null $crypto_currency = '', string|null $base_currency = '', string|null $base_amount = null, string|null $crypto_amount = null, string|null $payment_method = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param string $provider 
     * @param string $crypto_currency 
     * @param string $address 
     * @param string $payment_method 
     * @param string $base_currency 
     * @param string $base_amount 
     * @param string $memo 
     * @param string $theme 
     * @param string $success_return_url 
     * @param string $fail_return_url 
     * @param string $crypto_amount 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'onrampSession', provider: string, session_id: string, url: string, expires_date: int} @see https://docs.madelineproto.xyz/API_docs/types/OnrampSession.html
     * @psalm-impure
     */
    public function createOnrampSession(string|null $provider = '', string|null $crypto_currency = '', string|null $address = '', string|null $payment_method = null, string|null $base_currency = null, string|null $base_amount = null, string|null $memo = null, string|null $theme = null, string|null $success_return_url = null, string|null $fail_return_url = null, string|null $crypto_amount = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param array{_: 'inputSavedStarGiftUser', msg_id?: int}|array{_: 'inputSavedStarGiftChat', peer?: array|int|string, saved_id?: int}|array{_: 'inputSavedStarGiftSlug', slug?: string} $stargift @see https://docs.madelineproto.xyz/API_docs/types/InputSavedStarGift.html
     * @param bool $keep_on_profile 
     * @param string $tmp_password 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function withdrawStarGift(array $stargift, bool|null $keep_on_profile = null, string|null $tmp_password = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * 
     *
     * @param bool $hide_sender 
     * @param array|int|string $peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param string $slug 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'payments.starGiftDepositPayload', nft_address: string, payload: string, amount: int} @see https://docs.madelineproto.xyz/API_docs/types/payments.StarGiftDepositPayload.html
     * @psalm-impure
     */
    public function depositStarGift(bool|null $hide_sender = null, array|int|string|null $peer = null, string|null $slug = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param array|int|string $peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param string $slug 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function displayStarGift(array|int|string|null $peer = null, string|null $slug = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * 
     *
     * @param array{_: 'inputSavedStarGiftUser', msg_id?: int}|array{_: 'inputSavedStarGiftChat', peer?: array|int|string, saved_id?: int}|array{_: 'inputSavedStarGiftSlug', slug?: string} $stargift @see https://docs.madelineproto.xyz/API_docs/types/InputSavedStarGift.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function deleteStarGift(array $stargift, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;
}
<?php
/**
 * This file is automatic generated by build_docs.php file
 * and is used only for autocomplete in multiple IDE
 * don't modify manually.
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\Namespace;

/** @psalm-mutable */
interface Stats
{
    /**
     * Get [channel statistics](https://core.telegram.org/api/stats)
     *
     * @param bool $dark Whether to enable dark theme for graph colors
     * @param array|int|string $channel The channel @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'stats.broadcastStats', period: array{_: 'statsDateRangeDays', min_date: int, max_date: int}, followers: array{_: 'statsAbsValueAndPrev', current: float, previous: float}, views_per_post: array{_: 'statsAbsValueAndPrev', current: float, previous: float}, shares_per_post: array{_: 'statsAbsValueAndPrev', current: float, previous: float}, reactions_per_post: array{_: 'statsAbsValueAndPrev', current: float, previous: float}, views_per_story: array{_: 'statsAbsValueAndPrev', current: float, previous: float}, shares_per_story: array{_: 'statsAbsValueAndPrev', current: float, previous: float}, reactions_per_story: array{_: 'statsAbsValueAndPrev', current: float, previous: float}, enabled_notifications: array{_: 'statsPercentValue', part: float, total: float}, growth_graph: array{_: 'statsGraphAsync', token: string}|array{_: 'statsGraphError', error: string}|array{_: 'statsGraph', json: mixed, zoom_token?: string}, followers_graph: array{_: 'statsGraphAsync', token: string}|array{_: 'statsGraphError', error: string}|array{_: 'statsGraph', json: mixed, zoom_token?: string}, mute_graph: array{_: 'statsGraphAsync', token: string}|array{_: 'statsGraphError', error: string}|array{_: 'statsGraph', json: mixed, zoom_token?: string}, top_hours_graph: array{_: 'statsGraphAsync', token: string}|array{_: 'statsGraphError', error: string}|array{_: 'statsGraph', json: mixed, zoom_token?: string}, interactions_graph: array{_: 'statsGraphAsync', token: string}|array{_: 'statsGraphError', error: string}|array{_: 'statsGraph', json: mixed, zoom_token?: string}, iv_interactions_graph: array{_: 'statsGraphAsync', token: string}|array{_: 'statsGraphError', error: string}|array{_: 'statsGraph', json: mixed, zoom_token?: string}, views_by_source_graph: array{_: 'statsGraphAsync', token: string}|array{_: 'statsGraphError', error: string}|array{_: 'statsGraph', json: mixed, zoom_token?: string}, new_followers_by_source_graph: array{_: 'statsGraphAsync', token: string}|array{_: 'statsGraphError', error: string}|array{_: 'statsGraph', json: mixed, zoom_token?: string}, languages_graph: array{_: 'statsGraphAsync', token: string}|array{_: 'statsGraphError', error: string}|array{_: 'statsGraph', json: mixed, zoom_token?: string}, reactions_by_emotion_graph: array{_: 'statsGraphAsync', token: string}|array{_: 'statsGraphError', error: string}|array{_: 'statsGraph', json: mixed, zoom_token?: string}, story_interactions_graph: array{_: 'statsGraphAsync', token: string}|array{_: 'statsGraphError', error: string}|array{_: 'statsGraph', json: mixed, zoom_token?: string}, story_reactions_by_emotion_graph: array{_: 'statsGraphAsync', token: string}|array{_: 'statsGraphError', error: string}|array{_: 'statsGraph', json: mixed, zoom_token?: string}, recent_posts_interactions: list<array{_: 'postInteractionCountersMessage', msg_id: int, views: int, forwards: int, reactions: int}|array{_: 'postInteractionCountersStory', story_id: int, views: int, forwards: int, reactions: int}>} @see https://docs.madelineproto.xyz/API_docs/types/stats.BroadcastStats.html
     * @psalm-impure
     */
    public function getBroadcastStats(bool|null $dark = null, array|int|string|null $channel = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Load [channel statistics graph](https://core.telegram.org/api/stats) asynchronously
     *
     * @param string $token Graph token from [statsGraphAsync](https://docs.madelineproto.xyz/API_docs/constructors/statsGraphAsync.html) constructor
     * @param int $x Zoom value, if required
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'statsGraphAsync', token: string}|array{_: 'statsGraphError', error: string}|array{_: 'statsGraph', json: mixed, zoom_token?: string} @see https://docs.madelineproto.xyz/API_docs/types/StatsGraph.html
     * @psalm-impure
     */
    public function loadAsyncGraph(string|null $token = '', int|null $x = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get [supergroup statistics](https://core.telegram.org/api/stats)
     *
     * @param bool $dark Whether to enable dark theme for graph colors
     * @param array|int|string $channel [Supergroup ID](https://core.telegram.org/api/channel) @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'stats.megagroupStats', period: array{_: 'statsDateRangeDays', min_date: int, max_date: int}, members: array{_: 'statsAbsValueAndPrev', current: float, previous: float}, messages: array{_: 'statsAbsValueAndPrev', current: float, previous: float}, viewers: array{_: 'statsAbsValueAndPrev', current: float, previous: float}, posters: array{_: 'statsAbsValueAndPrev', current: float, previous: float}, growth_graph: array{_: 'statsGraphAsync', token: string}|array{_: 'statsGraphError', error: string}|array{_: 'statsGraph', json: mixed, zoom_token?: string}, members_graph: array{_: 'statsGraphAsync', token: string}|array{_: 'statsGraphError', error: string}|array{_: 'statsGraph', json: mixed, zoom_token?: string}, new_members_by_source_graph: array{_: 'statsGraphAsync', token: string}|array{_: 'statsGraphError', error: string}|array{_: 'statsGraph', json: mixed, zoom_token?: string}, languages_graph: array{_: 'statsGraphAsync', token: string}|array{_: 'statsGraphError', error: string}|array{_: 'statsGraph', json: mixed, zoom_token?: string}, messages_graph: array{_: 'statsGraphAsync', token: string}|array{_: 'statsGraphError', error: string}|array{_: 'statsGraph', json: mixed, zoom_token?: string}, actions_graph: array{_: 'statsGraphAsync', token: string}|array{_: 'statsGraphError', error: string}|array{_: 'statsGraph', json: mixed, zoom_token?: string}, top_hours_graph: array{_: 'statsGraphAsync', token: string}|array{_: 'statsGraphError', error: string}|array{_: 'statsGraph', json: mixed, zoom_token?: string}, weekdays_graph: array{_: 'statsGraphAsync', token: string}|array{_: 'statsGraphError', error: string}|array{_: 'statsGraph', json: mixed, zoom_token?: string}, top_posters: list<array{_: 'statsGroupTopPoster', user_id: int, messages: int, avg_chars: int}>, top_admins: list<array{_: 'statsGroupTopAdmin', user_id: int, deleted: int, kicked: int, banned: int}>, top_inviters: list<array{_: 'statsGroupTopInviter', user_id: int, invitations: int}>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/stats.MegagroupStats.html
     * @psalm-impure
     */
    public function getMegagroupStats(bool|null $dark = null, array|int|string|null $channel = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Obtains a list of messages, indicating to which other public channels was a channel message forwarded.  
     * Will return a list of [messages](https://docs.madelineproto.xyz/API_docs/constructors/message.html) with `peer_id` equal to the public channel to which this message was forwarded.
     *
     * @param array|int|string $channel Source channel @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param int $msg_id Source message ID
     * @param string $offset Offset for [pagination](https://core.telegram.org/api/offsets), empty string on first call, then use the `next_offset` field of the returned constructor (if present, otherwise no more results are available).
     * @param int $limit Maximum number of results to return, [see pagination](https://core.telegram.org/api/offsets)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'stats.publicForwards', count: int, forwards: list<array{_: 'publicForwardMessage', message: array{_: 'messageEmpty', id: int, peer_id?: array|int|string}|array{_: 'message', peer_id: array|int|string, out: bool, mentioned: bool, media_unread: bool, silent: bool, post: bool, from_scheduled: bool, legacy: bool, edit_hide: bool, pinned: bool, noforwards: bool, invert_media: bool, offline: bool, video_processing_pending: bool, paid_suggested_post_stars: bool, paid_suggested_post_ton: bool, id: int, from_id?: array|int|string, from_boosts_applied?: int, from_rank?: string, saved_peer_id?: array|int|string, fwd_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, via_bot_id?: int, via_business_bot_id?: int, guestchat_via_from?: array|int|string, reply_to?: array{_: 'messageReplyHeader', reply_to_scheduled: bool, forum_topic: bool, quote: bool, reply_to_ephemeral: bool, reply_to_msg_id?: int, reply_to_peer_id?: array|int|string, reply_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, reply_media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_to_top_id?: int, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, quote_offset?: int, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, url: string, webpage_id: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, email: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, phone: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, name: string}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, user_id: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, date: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, type: array{_: 'inlineButtonTypeUrl', url: array}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: array, url: array, button_id: array}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: array, fwd_text?: array, url: array, bot?: array}|array{_: 'inlineButtonTypeWebView', url: array}|array{_: 'inlineButtonTypeCallback', requires_password: array, data: array}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: array, query: array, peer_types?: list<array>}|array{_: 'inlineButtonTypeUserProfile', user_id: array}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array}|array{_: 'inlineButtonTypeCopy', copy_text: array}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: array, bg_danger: array, bg_success: array, link: array}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}>}|array{_: 'messageReplyStoryHeader', peer: array|int|string, story_id: int}, date: int, message: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_markup?: array, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, views?: int, forwards?: int, replies?: array{_: 'messageReplies', comments: bool, replies: int, replies_pts: int, recent_repliers?: list<array|int|string>, channel_id?: int, max_id?: int, read_max_id?: int}, edit_date?: int, post_author?: string, grouped_id?: int, reactions?: array{_: 'messageReactions', min: bool, can_see_list: bool, reactions_as_tags: bool, results: list<array{_: 'reactionCount', chosen_order?: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: array}|array{_: 'reactionCustomEmoji', document_id: array}|array{_: 'reactionPaid'}, count: int}>, recent_reactions?: list<array{_: 'messagePeerReaction', peer_id: array|int|string, big: bool, unread: bool, my: bool, date: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: array}|array{_: 'reactionCustomEmoji', document_id: array}|array{_: 'reactionPaid'}}>, top_reactors?: list<array{_: 'messageReactor', top: bool, my: bool, anonymous: bool, peer_id?: array|int|string, count: int}>}, restriction_reason?: list<array{_: 'restrictionReason', platform: string, reason: string, text: string}>, ttl_period?: int, quick_reply_shortcut_id?: int, effect?: int, factcheck?: array{_: 'factCheck', need_check: bool, country?: string, text?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, hash: list<int|string>}, report_delivery_until_date?: int, paid_message_stars?: int, suggested_post?: array{_: 'suggestedPost', accepted: bool, rejected: bool, price?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, schedule_date?: int}, schedule_repeat_period?: int, summary_from_language?: string, rich_message?: array{_: 'richMessage', rtl: bool, part: bool, blocks: list<array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, published_date: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, language: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: string}|array{_: 'pageBlockList', items: list<array{_: 'pageListItemText', text: array, checkbox: array, checked: array}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, collapsed: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array, credit: array}, spoiler: bool, photo_id: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array, credit: array}, autoplay: bool, loop: bool, spoiler: bool, video_id: int}|array{_: 'pageBlockCover', cover: array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array}|array{_: 'pageBlockSubtitle', text: array}|array{_: 'pageBlockAuthorDate', author: array, published_date: array}|array{_: 'pageBlockHeader', text: array}|array{_: 'pageBlockSubheader', text: array}|array{_: 'pageBlockParagraph', text: array}|array{_: 'pageBlockPreformatted', text: array, language: array}|array{_: 'pageBlockFooter', text: array}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: array}|array{_: 'pageBlockList', items: list<array>}|array{_: 'pageBlockBlockquote', text: array, caption: array, collapsed: array}|array{_: 'pageBlockPullquote', text: array, caption: array}|array{_: 'pageBlockPhoto', caption: array, spoiler: array, photo_id: array, url?: array, webpage_id?: array}|array{_: 'pageBlockVideo', caption: array, autoplay: array, loop: array, spoiler: array, video_id: array}|array{_: 'pageBlockEmbed', caption: array, full_width: array, allow_scrolling: array, url?: array, html?: array, poster_photo_id?: array, w?: array, h?: array}|array{_: 'pageBlockChannel', channel: array}|array{_: 'pageBlockAudio', caption: array, audio_id: array}|array{_: 'pageBlockKicker', text: array}|array{_: 'pageBlockTable', title: array, bordered: array, striped: array, compact: array, rows: list<array>}|array{_: 'pageBlockOrderedList', reversed: array, items: list<array>, start?: array, type?: array}|array{_: 'pageBlockRelatedArticles', title: array, articles: list<array>}|array{_: 'pageBlockMap', caption: array, geo: array, zoom: array, w: array, h: array}|array{_: 'pageBlockHeading1', text: array}|array{_: 'pageBlockHeading2', text: array}|array{_: 'pageBlockHeading3', text: array}|array{_: 'pageBlockHeading4', text: array}|array{_: 'pageBlockHeading5', text: array}|array{_: 'pageBlockHeading6', text: array}|array{_: 'pageBlockMath', source: array}|array{_: 'pageBlockThinking', text: array}|array{_: 'inputPageBlockMap', caption: array, geo: array, zoom: array, w: array, h: array}|array{_: 'pageBlockButtonRow', align_left: array, align_center: array, align_right: array, buttons: list<array>}|array{_: 'pageBlockDocument', caption: array, document_id: array}}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array, credit: array}, full_width: bool, allow_scrolling: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array, credit: array}, audio_id: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, bordered: bool, striped: bool, compact: bool, rows: list<array{_: 'pageTableRow', cells: list<array>}>}|array{_: 'pageBlockOrderedList', reversed: bool, items: list<array{_: 'pageListOrderedItemText', text: array, checkbox: array, checked: array, num?: array, value?: array, type?: array}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, articles: list<array{_: 'pageRelatedArticle', url: array, webpage_id: array, title?: array, description?: array, photo_id?: array, author?: array, published_date?: array}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockMath', source: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: array, long: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockButtonRow', align_left: bool, align_center: bool, align_right: bool, buttons: list<array{_: 'pageButton', text: array, type: array, style?: array}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array, credit: array}, document_id: int}>, photos: list<array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}}|array{_: 'messageService', peer_id: array|int|string, out: bool, mentioned: bool, media_unread: bool, reactions_are_possible: bool, silent: bool, post: bool, legacy: bool, id: int, from_id?: array|int|string, saved_peer_id?: array|int|string, reply_to?: array{_: 'messageReplyHeader', reply_to_scheduled: bool, forum_topic: bool, quote: bool, reply_to_ephemeral: bool, reply_to_msg_id?: int, reply_to_peer_id?: array|int|string, reply_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, reply_media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_to_top_id?: int, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, quote_offset?: int, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, url: string, webpage_id: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, email: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, phone: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, name: string}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, user_id: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, date: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, type: array{_: 'inlineButtonTypeUrl', url: array}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: array, url: array, button_id: array}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: array, fwd_text?: array, url: array, bot?: array}|array{_: 'inlineButtonTypeWebView', url: array}|array{_: 'inlineButtonTypeCallback', requires_password: array, data: array}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: array, query: array, peer_types?: list<array>}|array{_: 'inlineButtonTypeUserProfile', user_id: array}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array}|array{_: 'inlineButtonTypeCopy', copy_text: array}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: array, bg_danger: array, bg_success: array, link: array}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}>}|array{_: 'messageReplyStoryHeader', peer: array|int|string, story_id: int}, date: int, action: array{_: 'messageActionEmpty'}|array{_: 'messageActionChatCreate', title: string, users: list<int>}|array{_: 'messageActionChatEditTitle', title: string}|array{_: 'messageActionChatEditPhoto', photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'messageActionChatDeletePhoto'}|array{_: 'messageActionChatAddUser', users: list<int>}|array{_: 'messageActionChatDeleteUser', user_id: int}|array{_: 'messageActionChatJoinedByLink', inviter_id: int}|array{_: 'messageActionChannelCreate', title: string}|array{_: 'messageActionChatMigrateTo', channel_id: int}|array{_: 'messageActionChannelMigrateFrom', title: string, chat_id: array|int|string}|array{_: 'messageActionPinMessage'}|array{_: 'messageActionHistoryClear'}|array{_: 'messageActionGameScore', game_id: int, score: int}|array{_: 'messageActionPaymentSentMe', charge: array{_: 'paymentCharge', id: string, provider_charge_id: string}, recurring_init: bool, recurring_used: bool, currency: string, total_amount: int, payload: string, info?: array{_: 'paymentRequestedInfo', name?: string, phone?: string, email?: string, shipping_address?: array{_: 'postAddress', street_line1: array, street_line2: array, city: array, state: array, country_iso2: array, post_code: array}}, shipping_option_id?: string, subscription_until_date?: int}|array{_: 'messageActionPaymentSent', recurring_init: bool, recurring_used: bool, currency: string, total_amount: int, invoice_slug?: string, subscription_until_date?: int}|array{_: 'messageActionPhoneCall', video: bool, call_id: int, reason?: array{_: 'phoneCallDiscardReasonMissed'}|array{_: 'phoneCallDiscardReasonDisconnect'}|array{_: 'phoneCallDiscardReasonHangup'}|array{_: 'phoneCallDiscardReasonBusy'}|array{_: 'phoneCallDiscardReasonMigrateConferenceCall', slug: string}, duration?: int}|array{_: 'messageActionScreenshotTaken'}|array{_: 'messageActionCustomAction', message: string}|array{_: 'messageActionBotAllowed', attach_menu: bool, from_request: bool, domain?: string, app?: array{_: 'botAppNotModified'}|array{_: 'botApp', id: int, access_hash: int, short_name: string, title: string, description: string, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, hash: list<int|string>}}|array{_: 'messageActionSecureValuesSentMe', credentials: array{_: 'secureCredentialsEncrypted', data: string, hash: string, secret: string}, values: list<array{_: 'secureValue', type: array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}, data?: array{_: 'secureData', data: array, data_hash: array, secret: array}, front_side?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: array, access_hash: array, size: array, dc_id: array, date: array, file_hash: array, secret: array}, reverse_side?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: array, access_hash: array, size: array, dc_id: array, date: array, file_hash: array, secret: array}, selfie?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: array, access_hash: array, size: array, dc_id: array, date: array, file_hash: array, secret: array}, translation?: list<array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: array, access_hash: array, size: array, dc_id: array, date: array, file_hash: array, secret: array}>, files?: list<array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: array, access_hash: array, size: array, dc_id: array, date: array, file_hash: array, secret: array}>, plain_data?: array{_: 'securePlainPhone', phone: array}|array{_: 'securePlainEmail', email: array}, hash: string}>}|array{_: 'messageActionSecureValuesSent', types: list<array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}>}|array{_: 'messageActionContactSignUp'}|array{_: 'messageActionGeoProximityReached', from_id: array|int|string, to_id: array|int|string, distance: int}|array{_: 'messageActionGroupCall', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, duration?: int}|array{_: 'messageActionInviteToGroupCall', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, users: list<int>}|array{_: 'messageActionSetMessagesTTL', period: int, auto_setting_from?: int}|array{_: 'messageActionGroupCallScheduled', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, schedule_date: int}|array{_: 'messageActionSetChatTheme', theme: array{_: 'chatTheme', emoticon: string}|array{_: 'chatThemeUniqueGift', gift: array{_: 'starGift', limited: array, sold_out: array, birthday: array, require_premium: array, limited_per_user: array, peer_color_available: array, auction: array, id: array, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: array, availability_remains?: array, availability_total?: array, availability_resale?: array, convert_stars: array, first_sale_date?: array, last_sale_date?: array, upgrade_stars?: array, resell_min_stars?: array, title?: array, released_by?: array, per_user_total?: array, per_user_remains?: array, locked_until_date?: array, auction_slug?: array, gifts_per_round?: array, auction_start_date?: array, upgrade_variants?: array, background?: array}|array{_: 'starGiftUnique', require_premium: array, resale_ton_only: array, theme_available: array, burned: array, crafted: array, id: array, gift_id: array, title: array, slug: array, num: array, owner_id?: array, owner_name?: array, owner_address?: array, attributes: list<array>, availability_issued: array, availability_total: array, gift_address?: array, resell_amount?: list<array>, released_by?: array, value_amount?: array, value_currency?: array, value_usd_amount?: array, theme_peer?: array, peer_color?: array, host_id?: array, offer_min_stars?: array, craft_chance_permille?: array}, theme_settings: list<array{_: 'themeSettings', base_theme: array, message_colors_animated: array, accent_color: array, outbox_accent_color?: array, message_colors?: list<array>, wallpaper?: array}>}}|array{_: 'messageActionChatJoinedByRequest'}|array{_: 'messageActionWebViewDataSentMe', text: string, data: string}|array{_: 'messageActionWebViewDataSent', text: string}|array{_: 'messageActionGiftPremium', currency: string, amount: int, days: int, crypto_currency?: string, crypto_amount?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}}|array{_: 'messageActionTopicCreate', title_missing: bool, title: string, icon_color: int, icon_emoji_id?: int}|array{_: 'messageActionTopicEdit', title?: string, icon_emoji_id?: int, closed?: bool, hidden?: bool}|array{_: 'messageActionSuggestProfilePhoto', photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'messageActionRequestedPeer', button_id: int, peers: list<array|int|string>}|array{_: 'messageActionSetChatWallPaper', wallpaper: array{_: 'wallPaper', id: int, creator: bool, default: bool, pattern: bool, dark: bool, access_hash: int, slug: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, settings?: array{_: 'wallPaperSettings', blur: array, motion: array, background_color?: array, second_background_color?: array, third_background_color?: array, fourth_background_color?: array, intensity?: array, rotation?: array, emoticon?: array}}|array{_: 'wallPaperNoFile', id: int, default: bool, dark: bool, settings?: array{_: 'wallPaperSettings', blur: array, motion: array, background_color?: array, second_background_color?: array, third_background_color?: array, fourth_background_color?: array, intensity?: array, rotation?: array, emoticon?: array}}, same: bool, for_both: bool}|array{_: 'messageActionGiftCode', via_giveaway: bool, unclaimed: bool, boost_peer?: array|int|string, days: int, slug: string, currency?: string, amount?: int, crypto_currency?: string, crypto_amount?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}}|array{_: 'messageActionGiveawayLaunch', stars?: int}|array{_: 'messageActionGiveawayResults', stars: bool, winners_count: int, unclaimed_count: int}|array{_: 'messageActionBoostApply', boosts: int}|array{_: 'messageActionRequestedPeerSentMe', button_id: int, peers: list<array{_: 'requestedPeerUser', user_id: int, first_name?: string, last_name?: string, username?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'requestedPeerChat', chat_id: array|int|string, title?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'requestedPeerChannel', channel_id: int, title?: string, username?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}>}|array{_: 'messageActionPaymentRefunded', peer: array|int|string, charge: array{_: 'paymentCharge', id: string, provider_charge_id: string}, currency: string, total_amount: int, payload?: string}|array{_: 'messageActionGiftStars', currency: string, amount: int, stars: int, crypto_currency?: string, crypto_amount?: int, transaction_id?: string}|array{_: 'messageActionPrizeStars', boost_peer: array|int|string, unclaimed: bool, stars: int, transaction_id: string, giveaway_msg_id: int}|array{_: 'messageActionStarGift', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: array, edge_color: array, text_color: array}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array, crafted: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array, name: array, backdrop_id: array, center_color: array, edge_color: array, pattern_color: array, text_color: array}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array, sender_id?: array, date: array, message?: array}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: array, nanos: array}|array{_: 'starsTonAmount', amount: array}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: array, background_emoji_id?: array}|array{_: 'peerColorCollectible', collectible_id: array, gift_emoji_id: array, background_emoji_id: array, accent_color: array, colors: list<array>, dark_accent_color?: array, dark_colors?: list<array>}|array{_: 'inputPeerColorCollectible', collectible_id: array}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, name_hidden: bool, saved: bool, converted: bool, upgraded: bool, refunded: bool, can_upgrade: bool, prepaid_upgrade: bool, upgrade_separate: bool, auction_acquired: bool, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, convert_stars?: int, upgrade_msg_id?: int, upgrade_stars?: int, from_id?: array|int|string, peer?: array|int|string, saved_id?: int, prepaid_upgrade_hash?: string, gift_msg_id?: int, to_id?: array|int|string, gift_num?: int}|array{_: 'messageActionStarGiftUnique', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: array, edge_color: array, text_color: array}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array, crafted: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array, name: array, backdrop_id: array, center_color: array, edge_color: array, pattern_color: array, text_color: array}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array, sender_id?: array, date: array, message?: array}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: array, nanos: array}|array{_: 'starsTonAmount', amount: array}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: array, background_emoji_id?: array}|array{_: 'peerColorCollectible', collectible_id: array, gift_emoji_id: array, background_emoji_id: array, accent_color: array, colors: list<array>, dark_accent_color?: array, dark_colors?: list<array>}|array{_: 'inputPeerColorCollectible', collectible_id: array}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, upgrade: bool, transferred: bool, saved: bool, refunded: bool, prepaid_upgrade: bool, assigned: bool, from_offer: bool, craft: bool, name_hidden: bool, can_export_at?: int, transfer_stars?: int, from_id?: array|int|string, peer?: array|int|string, saved_id?: int, resale_amount?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, can_transfer_at?: int, can_resell_at?: int, drop_original_details_stars?: int, can_craft_at?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}}|array{_: 'messageActionPaidMessagesRefunded', count: int, stars: int}|array{_: 'messageActionPaidMessagesPrice', broadcast_messages_allowed: bool, stars: int}|array{_: 'messageActionConferenceCall', missed: bool, active: bool, video: bool, call_id: int, duration?: int, other_participants?: list<array|int|string>}|array{_: 'messageActionTodoCompletions', completed: list<int>, incompleted: list<int>}|array{_: 'messageActionTodoAppendTasks', list: list<array{_: 'todoItem', title: array{_: 'textWithEntities', text: array, entities: list<array>}, id: int}>}|array{_: 'messageActionSuggestedPostApproval', rejected: bool, balance_too_low: bool, reject_comment?: string, schedule_date?: int, price?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}}|array{_: 'messageActionSuggestedPostSuccess', price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}}|array{_: 'messageActionSuggestedPostRefund', payer_initiated: bool}|array{_: 'messageActionGiftTon', currency: string, amount: int, crypto_currency: string, crypto_amount: int, transaction_id?: string}|array{_: 'messageActionSuggestBirthday', birthday: array{_: 'birthday', day: int, month: int, year?: int}}|array{_: 'messageActionStarGiftPurchaseOffer', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: array, edge_color: array, text_color: array}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array, crafted: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array, name: array, backdrop_id: array, center_color: array, edge_color: array, pattern_color: array, text_color: array}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array, sender_id?: array, date: array, message?: array}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: array, nanos: array}|array{_: 'starsTonAmount', amount: array}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: array, background_emoji_id?: array}|array{_: 'peerColorCollectible', collectible_id: array, gift_emoji_id: array, background_emoji_id: array, accent_color: array, colors: list<array>, dark_accent_color?: array, dark_colors?: list<array>}|array{_: 'inputPeerColorCollectible', collectible_id: array}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, accepted: bool, declined: bool, expires_at: int}|array{_: 'messageActionStarGiftPurchaseOfferDeclined', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: array, edge_color: array, text_color: array}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array, crafted: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array, name: array, backdrop_id: array, center_color: array, edge_color: array, pattern_color: array, text_color: array}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array, sender_id?: array, date: array, message?: array}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: array, nanos: array}|array{_: 'starsTonAmount', amount: array}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: array, background_emoji_id?: array}|array{_: 'peerColorCollectible', collectible_id: array, gift_emoji_id: array, background_emoji_id: array, accent_color: array, colors: list<array>, dark_accent_color?: array, dark_colors?: list<array>}|array{_: 'inputPeerColorCollectible', collectible_id: array}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, expired: bool}|array{_: 'messageActionNewCreatorPending', new_creator_id: int}|array{_: 'messageActionChangeCreator', new_creator_id: int}|array{_: 'messageActionNoForwardsToggle', prev_value: bool, new_value: bool}|array{_: 'messageActionNoForwardsRequest', prev_value: bool, new_value: bool, expired: bool}|array{_: 'messageActionPollAppendAnswer', answer: array{_: 'pollAnswer', text: array{_: 'textWithEntities', text: array, entities: list<array>}, option: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, added_by?: array|int|string, date?: int}|array{_: 'inputPollAnswer', text: array{_: 'textWithEntities', text: array, entities: list<array>}, media?: \danog\MadelineProto\EventHandler\Media|string|array}}|array{_: 'messageActionPollDeleteAnswer', answer: array{_: 'pollAnswer', text: array{_: 'textWithEntities', text: array, entities: list<array>}, option: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, added_by?: array|int|string, date?: int}|array{_: 'inputPollAnswer', text: array{_: 'textWithEntities', text: array, entities: list<array>}, media?: \danog\MadelineProto\EventHandler\Media|string|array}}|array{_: 'messageActionManagedBotCreated', bot_id: int}|array{_: 'messageActionChangeCommunity', community_id?: int}|array{_: 'messageActionChatJoinedViaCommunity', community_id: int}|array{_: 'messageActionGramTransfer', comment_encrypted: bool, amount: int, peer_address: string, transaction_id: string, comment?: string}|array{_: 'messageActionWalletTonConnectRequest', accepted: bool, declined: bool, session_id: int, expires: int, topic?: string, trace_id?: string, dapp_name?: string}|array{_: 'messageActionNoPaidMessagesTokenUsed'}, reactions?: array{_: 'messageReactions', min: bool, can_see_list: bool, reactions_as_tags: bool, results: list<array{_: 'reactionCount', chosen_order?: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: array}|array{_: 'reactionCustomEmoji', document_id: array}|array{_: 'reactionPaid'}, count: int}>, recent_reactions?: list<array{_: 'messagePeerReaction', peer_id: array|int|string, big: bool, unread: bool, my: bool, date: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: array}|array{_: 'reactionCustomEmoji', document_id: array}|array{_: 'reactionPaid'}}>, top_reactors?: list<array{_: 'messageReactor', top: bool, my: bool, anonymous: bool, peer_id?: array|int|string, count: int}>}, ttl_period?: int}}|array{_: 'publicForwardStory', peer: array|int|string, story: array{_: 'storyItemDeleted', id: int}|array{_: 'storyItemSkipped', close_friends: bool, live: bool, id: int, date: int, expire_date: int}|array{_: 'storyItem', pinned: bool, public: bool, close_friends: bool, min: bool, noforwards: bool, edited: bool, contacts: bool, selected_contacts: bool, out: bool, id: int, date: int, from_id?: array|int|string, fwd_from?: array{_: 'storyFwdHeader', modified: bool, from?: array|int|string, from_name?: string, story_id?: int}, expire_date: int, caption?: string, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, media: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, media_areas?: list<array{_: 'mediaAreaVenue', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash: int, accuracy_radius?: int}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'inputMediaAreaVenue', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, query_id: int, result_id: string}|array{_: 'mediaAreaGeoPoint', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash: int, accuracy_radius?: int}, address?: array{_: 'geoPointAddress', country_iso2: string, state?: string, city?: string, street?: string}}|array{_: 'mediaAreaSuggestedReaction', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, dark: bool, flipped: bool, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}}|array{_: 'mediaAreaChannelPost', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, channel_id: int, msg_id: int}|array{_: 'inputMediaAreaChannelPost', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, channel: array|int|string, msg_id: int}|array{_: 'mediaAreaUrl', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, url: string}|array{_: 'mediaAreaWeather', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, temperature_c: float, emoji: string, color: int}|array{_: 'mediaAreaStarGift', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, slug: string}>, privacy?: list<array{_: 'privacyValueAllowContacts'}|array{_: 'privacyValueAllowAll'}|array{_: 'privacyValueAllowUsers', users: list<int>}|array{_: 'privacyValueDisallowContacts'}|array{_: 'privacyValueDisallowAll'}|array{_: 'privacyValueDisallowUsers', users: list<int>}|array{_: 'privacyValueAllowChatParticipants', chats: list<int>}|array{_: 'privacyValueDisallowChatParticipants', chats: list<int>}|array{_: 'privacyValueAllowCloseFriends'}|array{_: 'privacyValueAllowPremium'}|array{_: 'privacyValueAllowBots'}|array{_: 'privacyValueDisallowBots'}>, views?: array{_: 'storyViews', has_viewers: bool, views_count: int, forwards_count?: int, reactions?: list<array{_: 'reactionCount', chosen_order?: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: array}|array{_: 'reactionCustomEmoji', document_id: array}|array{_: 'reactionPaid'}, count: int}>, reactions_count?: int, recent_viewers?: list<int>}, sent_reaction?: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}, albums?: list<int>, music?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}>, next_offset?: string, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/stats.PublicForwards.html
     * @psalm-impure
     */
    public function getMessagePublicForwards(array|int|string|null $channel = null, int|null $msg_id = 0, string|null $offset = '', int|null $limit = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get [message statistics](https://core.telegram.org/api/stats)
     *
     * @param bool $dark Whether to enable dark theme for graph colors
     * @param array|int|string $channel Channel ID @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param int $msg_id Message ID
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'stats.messageStats', views_graph: array{_: 'statsGraphAsync', token: string}|array{_: 'statsGraphError', error: string}|array{_: 'statsGraph', json: mixed, zoom_token?: string}, reactions_by_emotion_graph: array{_: 'statsGraphAsync', token: string}|array{_: 'statsGraphError', error: string}|array{_: 'statsGraph', json: mixed, zoom_token?: string}} @see https://docs.madelineproto.xyz/API_docs/types/stats.MessageStats.html
     * @psalm-impure
     */
    public function getMessageStats(bool|null $dark = null, array|int|string|null $channel = null, int|null $msg_id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get [statistics](https://core.telegram.org/api/stats) for a certain [story](https://core.telegram.org/api/stories).
     *
     * @param bool $dark Whether to enable the dark theme for graph colors
     * @param array|int|string $peer The peer that posted the story @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $id Story ID
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'stats.storyStats', views_graph: array{_: 'statsGraphAsync', token: string}|array{_: 'statsGraphError', error: string}|array{_: 'statsGraph', json: mixed, zoom_token?: string}, reactions_by_emotion_graph: array{_: 'statsGraphAsync', token: string}|array{_: 'statsGraphError', error: string}|array{_: 'statsGraph', json: mixed, zoom_token?: string}} @see https://docs.madelineproto.xyz/API_docs/types/stats.StoryStats.html
     * @psalm-impure
     */
    public function getStoryStats(bool|null $dark = null, array|int|string|null $peer = null, int|null $id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Obtain forwards of a [story](https://core.telegram.org/api/stories) as a message to public chats and reposts by public channels.
     *
     * @param array|int|string $peer Peer where the story was originally posted @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $id [Story](https://core.telegram.org/api/stories) ID
     * @param string $offset Offset for pagination, from [stats.PublicForwards](https://docs.madelineproto.xyz/API_docs/constructors/stats.publicForwards.html).`next_offset`.
     * @param int $limit Maximum number of results to return, [see pagination](https://core.telegram.org/api/offsets)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'stats.publicForwards', count: int, forwards: list<array{_: 'publicForwardMessage', message: array{_: 'messageEmpty', id: int, peer_id?: array|int|string}|array{_: 'message', peer_id: array|int|string, out: bool, mentioned: bool, media_unread: bool, silent: bool, post: bool, from_scheduled: bool, legacy: bool, edit_hide: bool, pinned: bool, noforwards: bool, invert_media: bool, offline: bool, video_processing_pending: bool, paid_suggested_post_stars: bool, paid_suggested_post_ton: bool, id: int, from_id?: array|int|string, from_boosts_applied?: int, from_rank?: string, saved_peer_id?: array|int|string, fwd_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, via_bot_id?: int, via_business_bot_id?: int, guestchat_via_from?: array|int|string, reply_to?: array{_: 'messageReplyHeader', reply_to_scheduled: bool, forum_topic: bool, quote: bool, reply_to_ephemeral: bool, reply_to_msg_id?: int, reply_to_peer_id?: array|int|string, reply_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, reply_media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_to_top_id?: int, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, quote_offset?: int, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, url: string, webpage_id: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, email: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, phone: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, name: string}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, user_id: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, date: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, type: array{_: 'inlineButtonTypeUrl', url: array}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: array, url: array, button_id: array}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: array, fwd_text?: array, url: array, bot?: array}|array{_: 'inlineButtonTypeWebView', url: array}|array{_: 'inlineButtonTypeCallback', requires_password: array, data: array}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: array, query: array, peer_types?: list<array>}|array{_: 'inlineButtonTypeUserProfile', user_id: array}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array}|array{_: 'inlineButtonTypeCopy', copy_text: array}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: array, bg_danger: array, bg_success: array, link: array}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}>}|array{_: 'messageReplyStoryHeader', peer: array|int|string, story_id: int}, date: int, message: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_markup?: array, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, views?: int, forwards?: int, replies?: array{_: 'messageReplies', comments: bool, replies: int, replies_pts: int, recent_repliers?: list<array|int|string>, channel_id?: int, max_id?: int, read_max_id?: int}, edit_date?: int, post_author?: string, grouped_id?: int, reactions?: array{_: 'messageReactions', min: bool, can_see_list: bool, reactions_as_tags: bool, results: list<array{_: 'reactionCount', chosen_order?: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: array}|array{_: 'reactionCustomEmoji', document_id: array}|array{_: 'reactionPaid'}, count: int}>, recent_reactions?: list<array{_: 'messagePeerReaction', peer_id: array|int|string, big: bool, unread: bool, my: bool, date: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: array}|array{_: 'reactionCustomEmoji', document_id: array}|array{_: 'reactionPaid'}}>, top_reactors?: list<array{_: 'messageReactor', top: bool, my: bool, anonymous: bool, peer_id?: array|int|string, count: int}>}, restriction_reason?: list<array{_: 'restrictionReason', platform: string, reason: string, text: string}>, ttl_period?: int, quick_reply_shortcut_id?: int, effect?: int, factcheck?: array{_: 'factCheck', need_check: bool, country?: string, text?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, hash: list<int|string>}, report_delivery_until_date?: int, paid_message_stars?: int, suggested_post?: array{_: 'suggestedPost', accepted: bool, rejected: bool, price?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, schedule_date?: int}, schedule_repeat_period?: int, summary_from_language?: string, rich_message?: array{_: 'richMessage', rtl: bool, part: bool, blocks: list<array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, published_date: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, language: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: string}|array{_: 'pageBlockList', items: list<array{_: 'pageListItemText', text: array, checkbox: array, checked: array}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, collapsed: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array, credit: array}, spoiler: bool, photo_id: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array, credit: array}, autoplay: bool, loop: bool, spoiler: bool, video_id: int}|array{_: 'pageBlockCover', cover: array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array}|array{_: 'pageBlockSubtitle', text: array}|array{_: 'pageBlockAuthorDate', author: array, published_date: array}|array{_: 'pageBlockHeader', text: array}|array{_: 'pageBlockSubheader', text: array}|array{_: 'pageBlockParagraph', text: array}|array{_: 'pageBlockPreformatted', text: array, language: array}|array{_: 'pageBlockFooter', text: array}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: array}|array{_: 'pageBlockList', items: list<array>}|array{_: 'pageBlockBlockquote', text: array, caption: array, collapsed: array}|array{_: 'pageBlockPullquote', text: array, caption: array}|array{_: 'pageBlockPhoto', caption: array, spoiler: array, photo_id: array, url?: array, webpage_id?: array}|array{_: 'pageBlockVideo', caption: array, autoplay: array, loop: array, spoiler: array, video_id: array}|array{_: 'pageBlockEmbed', caption: array, full_width: array, allow_scrolling: array, url?: array, html?: array, poster_photo_id?: array, w?: array, h?: array}|array{_: 'pageBlockChannel', channel: array}|array{_: 'pageBlockAudio', caption: array, audio_id: array}|array{_: 'pageBlockKicker', text: array}|array{_: 'pageBlockTable', title: array, bordered: array, striped: array, compact: array, rows: list<array>}|array{_: 'pageBlockOrderedList', reversed: array, items: list<array>, start?: array, type?: array}|array{_: 'pageBlockRelatedArticles', title: array, articles: list<array>}|array{_: 'pageBlockMap', caption: array, geo: array, zoom: array, w: array, h: array}|array{_: 'pageBlockHeading1', text: array}|array{_: 'pageBlockHeading2', text: array}|array{_: 'pageBlockHeading3', text: array}|array{_: 'pageBlockHeading4', text: array}|array{_: 'pageBlockHeading5', text: array}|array{_: 'pageBlockHeading6', text: array}|array{_: 'pageBlockMath', source: array}|array{_: 'pageBlockThinking', text: array}|array{_: 'inputPageBlockMap', caption: array, geo: array, zoom: array, w: array, h: array}|array{_: 'pageBlockButtonRow', align_left: array, align_center: array, align_right: array, buttons: list<array>}|array{_: 'pageBlockDocument', caption: array, document_id: array}}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array, credit: array}, full_width: bool, allow_scrolling: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array, credit: array}, audio_id: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, bordered: bool, striped: bool, compact: bool, rows: list<array{_: 'pageTableRow', cells: list<array>}>}|array{_: 'pageBlockOrderedList', reversed: bool, items: list<array{_: 'pageListOrderedItemText', text: array, checkbox: array, checked: array, num?: array, value?: array, type?: array}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, articles: list<array{_: 'pageRelatedArticle', url: array, webpage_id: array, title?: array, description?: array, photo_id?: array, author?: array, published_date?: array}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockMath', source: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: array, long: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockButtonRow', align_left: bool, align_center: bool, align_right: bool, buttons: list<array{_: 'pageButton', text: array, type: array, style?: array}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array, credit: array}, document_id: int}>, photos: list<array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}}|array{_: 'messageService', peer_id: array|int|string, out: bool, mentioned: bool, media_unread: bool, reactions_are_possible: bool, silent: bool, post: bool, legacy: bool, id: int, from_id?: array|int|string, saved_peer_id?: array|int|string, reply_to?: array{_: 'messageReplyHeader', reply_to_scheduled: bool, forum_topic: bool, quote: bool, reply_to_ephemeral: bool, reply_to_msg_id?: int, reply_to_peer_id?: array|int|string, reply_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, reply_media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_to_top_id?: int, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, quote_offset?: int, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, url: string, webpage_id: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, email: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, phone: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, name: string}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, user_id: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, date: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, type: array{_: 'inlineButtonTypeUrl', url: array}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: array, url: array, button_id: array}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: array, fwd_text?: array, url: array, bot?: array}|array{_: 'inlineButtonTypeWebView', url: array}|array{_: 'inlineButtonTypeCallback', requires_password: array, data: array}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: array, query: array, peer_types?: list<array>}|array{_: 'inlineButtonTypeUserProfile', user_id: array}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array}|array{_: 'inlineButtonTypeCopy', copy_text: array}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: array, bg_danger: array, bg_success: array, link: array}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}>}|array{_: 'messageReplyStoryHeader', peer: array|int|string, story_id: int}, date: int, action: array{_: 'messageActionEmpty'}|array{_: 'messageActionChatCreate', title: string, users: list<int>}|array{_: 'messageActionChatEditTitle', title: string}|array{_: 'messageActionChatEditPhoto', photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'messageActionChatDeletePhoto'}|array{_: 'messageActionChatAddUser', users: list<int>}|array{_: 'messageActionChatDeleteUser', user_id: int}|array{_: 'messageActionChatJoinedByLink', inviter_id: int}|array{_: 'messageActionChannelCreate', title: string}|array{_: 'messageActionChatMigrateTo', channel_id: int}|array{_: 'messageActionChannelMigrateFrom', title: string, chat_id: array|int|string}|array{_: 'messageActionPinMessage'}|array{_: 'messageActionHistoryClear'}|array{_: 'messageActionGameScore', game_id: int, score: int}|array{_: 'messageActionPaymentSentMe', charge: array{_: 'paymentCharge', id: string, provider_charge_id: string}, recurring_init: bool, recurring_used: bool, currency: string, total_amount: int, payload: string, info?: array{_: 'paymentRequestedInfo', name?: string, phone?: string, email?: string, shipping_address?: array{_: 'postAddress', street_line1: array, street_line2: array, city: array, state: array, country_iso2: array, post_code: array}}, shipping_option_id?: string, subscription_until_date?: int}|array{_: 'messageActionPaymentSent', recurring_init: bool, recurring_used: bool, currency: string, total_amount: int, invoice_slug?: string, subscription_until_date?: int}|array{_: 'messageActionPhoneCall', video: bool, call_id: int, reason?: array{_: 'phoneCallDiscardReasonMissed'}|array{_: 'phoneCallDiscardReasonDisconnect'}|array{_: 'phoneCallDiscardReasonHangup'}|array{_: 'phoneCallDiscardReasonBusy'}|array{_: 'phoneCallDiscardReasonMigrateConferenceCall', slug: string}, duration?: int}|array{_: 'messageActionScreenshotTaken'}|array{_: 'messageActionCustomAction', message: string}|array{_: 'messageActionBotAllowed', attach_menu: bool, from_request: bool, domain?: string, app?: array{_: 'botAppNotModified'}|array{_: 'botApp', id: int, access_hash: int, short_name: string, title: string, description: string, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, hash: list<int|string>}}|array{_: 'messageActionSecureValuesSentMe', credentials: array{_: 'secureCredentialsEncrypted', data: string, hash: string, secret: string}, values: list<array{_: 'secureValue', type: array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}, data?: array{_: 'secureData', data: array, data_hash: array, secret: array}, front_side?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: array, access_hash: array, size: array, dc_id: array, date: array, file_hash: array, secret: array}, reverse_side?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: array, access_hash: array, size: array, dc_id: array, date: array, file_hash: array, secret: array}, selfie?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: array, access_hash: array, size: array, dc_id: array, date: array, file_hash: array, secret: array}, translation?: list<array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: array, access_hash: array, size: array, dc_id: array, date: array, file_hash: array, secret: array}>, files?: list<array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: array, access_hash: array, size: array, dc_id: array, date: array, file_hash: array, secret: array}>, plain_data?: array{_: 'securePlainPhone', phone: array}|array{_: 'securePlainEmail', email: array}, hash: string}>}|array{_: 'messageActionSecureValuesSent', types: list<array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}>}|array{_: 'messageActionContactSignUp'}|array{_: 'messageActionGeoProximityReached', from_id: array|int|string, to_id: array|int|string, distance: int}|array{_: 'messageActionGroupCall', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, duration?: int}|array{_: 'messageActionInviteToGroupCall', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, users: list<int>}|array{_: 'messageActionSetMessagesTTL', period: int, auto_setting_from?: int}|array{_: 'messageActionGroupCallScheduled', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, schedule_date: int}|array{_: 'messageActionSetChatTheme', theme: array{_: 'chatTheme', emoticon: string}|array{_: 'chatThemeUniqueGift', gift: array{_: 'starGift', limited: array, sold_out: array, birthday: array, require_premium: array, limited_per_user: array, peer_color_available: array, auction: array, id: array, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: array, availability_remains?: array, availability_total?: array, availability_resale?: array, convert_stars: array, first_sale_date?: array, last_sale_date?: array, upgrade_stars?: array, resell_min_stars?: array, title?: array, released_by?: array, per_user_total?: array, per_user_remains?: array, locked_until_date?: array, auction_slug?: array, gifts_per_round?: array, auction_start_date?: array, upgrade_variants?: array, background?: array}|array{_: 'starGiftUnique', require_premium: array, resale_ton_only: array, theme_available: array, burned: array, crafted: array, id: array, gift_id: array, title: array, slug: array, num: array, owner_id?: array, owner_name?: array, owner_address?: array, attributes: list<array>, availability_issued: array, availability_total: array, gift_address?: array, resell_amount?: list<array>, released_by?: array, value_amount?: array, value_currency?: array, value_usd_amount?: array, theme_peer?: array, peer_color?: array, host_id?: array, offer_min_stars?: array, craft_chance_permille?: array}, theme_settings: list<array{_: 'themeSettings', base_theme: array, message_colors_animated: array, accent_color: array, outbox_accent_color?: array, message_colors?: list<array>, wallpaper?: array}>}}|array{_: 'messageActionChatJoinedByRequest'}|array{_: 'messageActionWebViewDataSentMe', text: string, data: string}|array{_: 'messageActionWebViewDataSent', text: string}|array{_: 'messageActionGiftPremium', currency: string, amount: int, days: int, crypto_currency?: string, crypto_amount?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}}|array{_: 'messageActionTopicCreate', title_missing: bool, title: string, icon_color: int, icon_emoji_id?: int}|array{_: 'messageActionTopicEdit', title?: string, icon_emoji_id?: int, closed?: bool, hidden?: bool}|array{_: 'messageActionSuggestProfilePhoto', photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'messageActionRequestedPeer', button_id: int, peers: list<array|int|string>}|array{_: 'messageActionSetChatWallPaper', wallpaper: array{_: 'wallPaper', id: int, creator: bool, default: bool, pattern: bool, dark: bool, access_hash: int, slug: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, settings?: array{_: 'wallPaperSettings', blur: array, motion: array, background_color?: array, second_background_color?: array, third_background_color?: array, fourth_background_color?: array, intensity?: array, rotation?: array, emoticon?: array}}|array{_: 'wallPaperNoFile', id: int, default: bool, dark: bool, settings?: array{_: 'wallPaperSettings', blur: array, motion: array, background_color?: array, second_background_color?: array, third_background_color?: array, fourth_background_color?: array, intensity?: array, rotation?: array, emoticon?: array}}, same: bool, for_both: bool}|array{_: 'messageActionGiftCode', via_giveaway: bool, unclaimed: bool, boost_peer?: array|int|string, days: int, slug: string, currency?: string, amount?: int, crypto_currency?: string, crypto_amount?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}}|array{_: 'messageActionGiveawayLaunch', stars?: int}|array{_: 'messageActionGiveawayResults', stars: bool, winners_count: int, unclaimed_count: int}|array{_: 'messageActionBoostApply', boosts: int}|array{_: 'messageActionRequestedPeerSentMe', button_id: int, peers: list<array{_: 'requestedPeerUser', user_id: int, first_name?: string, last_name?: string, username?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'requestedPeerChat', chat_id: array|int|string, title?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'requestedPeerChannel', channel_id: int, title?: string, username?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}>}|array{_: 'messageActionPaymentRefunded', peer: array|int|string, charge: array{_: 'paymentCharge', id: string, provider_charge_id: string}, currency: string, total_amount: int, payload?: string}|array{_: 'messageActionGiftStars', currency: string, amount: int, stars: int, crypto_currency?: string, crypto_amount?: int, transaction_id?: string}|array{_: 'messageActionPrizeStars', boost_peer: array|int|string, unclaimed: bool, stars: int, transaction_id: string, giveaway_msg_id: int}|array{_: 'messageActionStarGift', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: array, edge_color: array, text_color: array}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array, crafted: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array, name: array, backdrop_id: array, center_color: array, edge_color: array, pattern_color: array, text_color: array}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array, sender_id?: array, date: array, message?: array}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: array, nanos: array}|array{_: 'starsTonAmount', amount: array}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: array, background_emoji_id?: array}|array{_: 'peerColorCollectible', collectible_id: array, gift_emoji_id: array, background_emoji_id: array, accent_color: array, colors: list<array>, dark_accent_color?: array, dark_colors?: list<array>}|array{_: 'inputPeerColorCollectible', collectible_id: array}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, name_hidden: bool, saved: bool, converted: bool, upgraded: bool, refunded: bool, can_upgrade: bool, prepaid_upgrade: bool, upgrade_separate: bool, auction_acquired: bool, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, convert_stars?: int, upgrade_msg_id?: int, upgrade_stars?: int, from_id?: array|int|string, peer?: array|int|string, saved_id?: int, prepaid_upgrade_hash?: string, gift_msg_id?: int, to_id?: array|int|string, gift_num?: int}|array{_: 'messageActionStarGiftUnique', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: array, edge_color: array, text_color: array}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array, crafted: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array, name: array, backdrop_id: array, center_color: array, edge_color: array, pattern_color: array, text_color: array}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array, sender_id?: array, date: array, message?: array}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: array, nanos: array}|array{_: 'starsTonAmount', amount: array}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: array, background_emoji_id?: array}|array{_: 'peerColorCollectible', collectible_id: array, gift_emoji_id: array, background_emoji_id: array, accent_color: array, colors: list<array>, dark_accent_color?: array, dark_colors?: list<array>}|array{_: 'inputPeerColorCollectible', collectible_id: array}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, upgrade: bool, transferred: bool, saved: bool, refunded: bool, prepaid_upgrade: bool, assigned: bool, from_offer: bool, craft: bool, name_hidden: bool, can_export_at?: int, transfer_stars?: int, from_id?: array|int|string, peer?: array|int|string, saved_id?: int, resale_amount?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, can_transfer_at?: int, can_resell_at?: int, drop_original_details_stars?: int, can_craft_at?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}}|array{_: 'messageActionPaidMessagesRefunded', count: int, stars: int}|array{_: 'messageActionPaidMessagesPrice', broadcast_messages_allowed: bool, stars: int}|array{_: 'messageActionConferenceCall', missed: bool, active: bool, video: bool, call_id: int, duration?: int, other_participants?: list<array|int|string>}|array{_: 'messageActionTodoCompletions', completed: list<int>, incompleted: list<int>}|array{_: 'messageActionTodoAppendTasks', list: list<array{_: 'todoItem', title: array{_: 'textWithEntities', text: array, entities: list<array>}, id: int}>}|array{_: 'messageActionSuggestedPostApproval', rejected: bool, balance_too_low: bool, reject_comment?: string, schedule_date?: int, price?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}}|array{_: 'messageActionSuggestedPostSuccess', price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}}|array{_: 'messageActionSuggestedPostRefund', payer_initiated: bool}|array{_: 'messageActionGiftTon', currency: string, amount: int, crypto_currency: string, crypto_amount: int, transaction_id?: string}|array{_: 'messageActionSuggestBirthday', birthday: array{_: 'birthday', day: int, month: int, year?: int}}|array{_: 'messageActionStarGiftPurchaseOffer', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: array, edge_color: array, text_color: array}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array, crafted: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array, name: array, backdrop_id: array, center_color: array, edge_color: array, pattern_color: array, text_color: array}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array, sender_id?: array, date: array, message?: array}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: array, nanos: array}|array{_: 'starsTonAmount', amount: array}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: array, background_emoji_id?: array}|array{_: 'peerColorCollectible', collectible_id: array, gift_emoji_id: array, background_emoji_id: array, accent_color: array, colors: list<array>, dark_accent_color?: array, dark_colors?: list<array>}|array{_: 'inputPeerColorCollectible', collectible_id: array}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, accepted: bool, declined: bool, expires_at: int}|array{_: 'messageActionStarGiftPurchaseOfferDeclined', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: array, edge_color: array, text_color: array}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array, crafted: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array, name: array, backdrop_id: array, center_color: array, edge_color: array, pattern_color: array, text_color: array}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array, sender_id?: array, date: array, message?: array}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: array, nanos: array}|array{_: 'starsTonAmount', amount: array}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: array, background_emoji_id?: array}|array{_: 'peerColorCollectible', collectible_id: array, gift_emoji_id: array, background_emoji_id: array, accent_color: array, colors: list<array>, dark_accent_color?: array, dark_colors?: list<array>}|array{_: 'inputPeerColorCollectible', collectible_id: array}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, expired: bool}|array{_: 'messageActionNewCreatorPending', new_creator_id: int}|array{_: 'messageActionChangeCreator', new_creator_id: int}|array{_: 'messageActionNoForwardsToggle', prev_value: bool, new_value: bool}|array{_: 'messageActionNoForwardsRequest', prev_value: bool, new_value: bool, expired: bool}|array{_: 'messageActionPollAppendAnswer', answer: array{_: 'pollAnswer', text: array{_: 'textWithEntities', text: array, entities: list<array>}, option: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, added_by?: array|int|string, date?: int}|array{_: 'inputPollAnswer', text: array{_: 'textWithEntities', text: array, entities: list<array>}, media?: \danog\MadelineProto\EventHandler\Media|string|array}}|array{_: 'messageActionPollDeleteAnswer', answer: array{_: 'pollAnswer', text: array{_: 'textWithEntities', text: array, entities: list<array>}, option: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, added_by?: array|int|string, date?: int}|array{_: 'inputPollAnswer', text: array{_: 'textWithEntities', text: array, entities: list<array>}, media?: \danog\MadelineProto\EventHandler\Media|string|array}}|array{_: 'messageActionManagedBotCreated', bot_id: int}|array{_: 'messageActionChangeCommunity', community_id?: int}|array{_: 'messageActionChatJoinedViaCommunity', community_id: int}|array{_: 'messageActionGramTransfer', comment_encrypted: bool, amount: int, peer_address: string, transaction_id: string, comment?: string}|array{_: 'messageActionWalletTonConnectRequest', accepted: bool, declined: bool, session_id: int, expires: int, topic?: string, trace_id?: string, dapp_name?: string}|array{_: 'messageActionNoPaidMessagesTokenUsed'}, reactions?: array{_: 'messageReactions', min: bool, can_see_list: bool, reactions_as_tags: bool, results: list<array{_: 'reactionCount', chosen_order?: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: array}|array{_: 'reactionCustomEmoji', document_id: array}|array{_: 'reactionPaid'}, count: int}>, recent_reactions?: list<array{_: 'messagePeerReaction', peer_id: array|int|string, big: bool, unread: bool, my: bool, date: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: array}|array{_: 'reactionCustomEmoji', document_id: array}|array{_: 'reactionPaid'}}>, top_reactors?: list<array{_: 'messageReactor', top: bool, my: bool, anonymous: bool, peer_id?: array|int|string, count: int}>}, ttl_period?: int}}|array{_: 'publicForwardStory', peer: array|int|string, story: array{_: 'storyItemDeleted', id: int}|array{_: 'storyItemSkipped', close_friends: bool, live: bool, id: int, date: int, expire_date: int}|array{_: 'storyItem', pinned: bool, public: bool, close_friends: bool, min: bool, noforwards: bool, edited: bool, contacts: bool, selected_contacts: bool, out: bool, id: int, date: int, from_id?: array|int|string, fwd_from?: array{_: 'storyFwdHeader', modified: bool, from?: array|int|string, from_name?: string, story_id?: int}, expire_date: int, caption?: string, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, media: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, media_areas?: list<array{_: 'mediaAreaVenue', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash: int, accuracy_radius?: int}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'inputMediaAreaVenue', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, query_id: int, result_id: string}|array{_: 'mediaAreaGeoPoint', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash: int, accuracy_radius?: int}, address?: array{_: 'geoPointAddress', country_iso2: string, state?: string, city?: string, street?: string}}|array{_: 'mediaAreaSuggestedReaction', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, dark: bool, flipped: bool, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}}|array{_: 'mediaAreaChannelPost', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, channel_id: int, msg_id: int}|array{_: 'inputMediaAreaChannelPost', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, channel: array|int|string, msg_id: int}|array{_: 'mediaAreaUrl', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, url: string}|array{_: 'mediaAreaWeather', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, temperature_c: float, emoji: string, color: int}|array{_: 'mediaAreaStarGift', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, slug: string}>, privacy?: list<array{_: 'privacyValueAllowContacts'}|array{_: 'privacyValueAllowAll'}|array{_: 'privacyValueAllowUsers', users: list<int>}|array{_: 'privacyValueDisallowContacts'}|array{_: 'privacyValueDisallowAll'}|array{_: 'privacyValueDisallowUsers', users: list<int>}|array{_: 'privacyValueAllowChatParticipants', chats: list<int>}|array{_: 'privacyValueDisallowChatParticipants', chats: list<int>}|array{_: 'privacyValueAllowCloseFriends'}|array{_: 'privacyValueAllowPremium'}|array{_: 'privacyValueAllowBots'}|array{_: 'privacyValueDisallowBots'}>, views?: array{_: 'storyViews', has_viewers: bool, views_count: int, forwards_count?: int, reactions?: list<array{_: 'reactionCount', chosen_order?: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: array}|array{_: 'reactionCustomEmoji', document_id: array}|array{_: 'reactionPaid'}, count: int}>, reactions_count?: int, recent_viewers?: list<int>}, sent_reaction?: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}, albums?: list<int>, music?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}>, next_offset?: string, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/stats.PublicForwards.html
     * @psalm-impure
     */
    public function getStoryPublicForwards(array|int|string|null $peer = null, int|null $id = 0, string|null $offset = '', int|null $limit = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param bool $dark 
     * @param array|int|string $peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $msg_id 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'stats.pollStats', votes_graph: array{_: 'statsGraphAsync', token: string}|array{_: 'statsGraphError', error: string}|array{_: 'statsGraph', json: mixed, zoom_token?: string}} @see https://docs.madelineproto.xyz/API_docs/types/stats.PollStats.html
     * @psalm-impure
     */
    public function getPollStats(bool|null $dark = null, array|int|string|null $peer = null, int|null $msg_id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;
}
<?php

declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\Namespace;

use danog\MadelineProto\APIWrapper;
use danog\MadelineProto\Exception;
use InvalidArgumentException;

/**
 * @internal
 */
final class AbstractAPI
{
    private APIWrapper $wrapper;
    /**
     * @psalm-mutation-free
     */
    public function __construct(private string $namespace)
    {
    }
    /**
     * @psalm-external-mutation-free
     */
    public function setWrapper(APIWrapper $wrapper): void
    {
        $this->wrapper = $wrapper;
    }
    /**
     * Call async wrapper function.
     *
     * @param string $name      Method name
     * @param array  $arguments Arguments
     */
    public function __call(string $name, array $arguments)
    {
        if (!isset($arguments[0])) {
            // Named arguments
            $args = $arguments;
        } else {
            // Legacy arguments
            $args = $arguments[0] ?? [];
            $aargs = $arguments[1] ?? [];
            if (!array_is_list($arguments)
                || \count($arguments) > 2
                || !\is_array($args)
                || !\is_array($aargs)
                || isset($args['_'])
                || (isset($args[0]) && !isset($args['multiple']))
            ) {
                throw new InvalidArgumentException('Parameter names must be provided!');
            }
            $args = $args + $aargs;
        }

        $name = $this->namespace.'.'.$name;
        if (isset(Blacklist::BLACKLIST[$name])) {
            throw new Exception(Blacklist::BLACKLIST[$name]);
        }

        return $this->wrapper->getAPI()->methodCallAsyncRead($name, $args);
    }
}
<?php
/**
 * This file is automatic generated by build_docs.php file
 * and is used only for autocomplete in multiple IDE
 * don't modify manually.
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\Namespace;

/** @psalm-mutable */
interface Upload
{
    /**
     * Returns content of a web file, by proxying the request through telegram, see the [webfile docs for more info](https://core.telegram.org/api/files#downloading-webfiles).
     * 
     * **Note**: the query must be sent to the DC specified in the `webfile_dc_id` [MTProto configuration field](https://core.telegram.org/api/config#mtproto-configuration).
     *
     * @param array{_: 'inputWebFileLocation', url?: string, access_hash?: int}|array{_: 'inputWebFileGeoPointLocation', geo_point?: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: float, long: float, accuracy_radius?: int}, access_hash?: int, w?: int, h?: int, zoom?: int, scale?: int}|array{_: 'inputWebFileAudioAlbumThumbLocation', small?: bool, document?: array, title?: string, performer?: string} $location The file to download @see https://docs.madelineproto.xyz/API_docs/types/InputWebFileLocation.html
     * @param int $offset Number of bytes to be skipped
     * @param int $limit Number of bytes to be returned
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'upload.webFile', file_type: array{_: 'storage.fileUnknown'}|array{_: 'storage.filePartial'}|array{_: 'storage.fileJpeg'}|array{_: 'storage.fileGif'}|array{_: 'storage.filePng'}|array{_: 'storage.filePdf'}|array{_: 'storage.fileMp3'}|array{_: 'storage.fileMov'}|array{_: 'storage.fileMp4'}|array{_: 'storage.fileWebp'}, size: int, mime_type: string, mtime: int, bytes: string} @see https://docs.madelineproto.xyz/API_docs/types/upload.WebFile.html
     * @psalm-impure
     */
    public function getWebFile(array $location, int|null $offset = 0, int|null $limit = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;
}
<?php
/**
 * This file is automatic generated by build_docs.php file
 * and is used only for autocomplete in multiple IDE
 * don't modify manually.
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\Namespace;

/** @psalm-mutable */
interface Messages
{
    /**
     * Returns the list of messages by their IDs.
     *
     * @param list<int|array>|array<never, never> $id Array of Message ID list @see https://docs.madelineproto.xyz/API_docs/types/InputMessage.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.messages', messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.messagesSlice', inexact: array, count: array, next_rate?: array, offset_id_offset?: array, search_flood?: array, messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.channelMessages', inexact: array, pts: array, count: array, offset_id_offset?: array, messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.messagesNotModified', count: array} @see https://docs.madelineproto.xyz/API_docs/types/messages.Messages.html
     * @psalm-impure
     */
    public function getMessages(array $id = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Returns the current user dialog list.
     *
     * @param bool $exclude_pinned Exclude pinned dialogs
     * @param int $folder_id [Peer folder ID, for more info click here](https://core.telegram.org/api/folders#peer-folders)
     * @param int $offset_date [Offsets for pagination, for more info click here](https://core.telegram.org/api/offsets)
     * @param int $offset_id [Offsets for pagination, for more info click here](https://core.telegram.org/api/offsets) (`top_message` ID used for pagination)
     * @param array|int|string $offset_peer [Offset peer for pagination](https://core.telegram.org/api/offsets) @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $limit Number of list elements to be returned
     * @param list<int|string>|array<never, never> $hash Array of [Hash used for caching, for more info click here](https://core.telegram.org/api/offsets#hash-generation) @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @param ?int $takeoutId Optional takeout ID, generated using account.initTakeoutSession, see [the takeout docs](https://core.telegram.org/api/takeout) for more info.
     * @return array{_: 'messages.dialogs', dialogs: list<array>, messages: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.dialogsSlice', count: array, dialogs: list<array>, messages: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.dialogsNotModified', count: array} @see https://docs.madelineproto.xyz/API_docs/types/messages.Dialogs.html
     * @psalm-impure
     */
    public function getDialogs(bool|null $exclude_pinned = null, int|null $folder_id = null, int|null $offset_date = 0, int|null $offset_id = 0, array|int|string|null $offset_peer = null, int|null $limit = 0, array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null, ?int $takeoutId = null): array;

    /**
     * Returns the conversation history with one interlocutor / within a chat
     *
     * @param array|int|string $peer Target peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $offset_id Only return messages starting from the specified message ID
     * @param int $offset_date Only return messages sent before the specified date
     * @param int $add_offset Number of list elements to be skipped, negative values are also accepted.
     * @param int $limit Number of results to return
     * @param int $max_id If a positive value was transferred, the method will return only messages with IDs less than **max\_id**
     * @param int $min_id If a positive value was transferred, the method will return only messages with IDs more than **min\_id**
     * @param list<int|string>|array<never, never> $hash Array of [Result hash](https://core.telegram.org/api/offsets) @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @param ?int $takeoutId Optional takeout ID, generated using account.initTakeoutSession, see [the takeout docs](https://core.telegram.org/api/takeout) for more info.
     * @return array{_: 'messages.messages', messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.messagesSlice', inexact: array, count: array, next_rate?: array, offset_id_offset?: array, search_flood?: array, messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.channelMessages', inexact: array, pts: array, count: array, offset_id_offset?: array, messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.messagesNotModified', count: array} @see https://docs.madelineproto.xyz/API_docs/types/messages.Messages.html
     * @psalm-impure
     */
    public function getHistory(array|int|string|null $peer = null, int|null $offset_id = 0, int|null $offset_date = 0, int|null $add_offset = 0, int|null $limit = 0, int|null $max_id = 0, int|null $min_id = 0, array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null, ?int $takeoutId = null): array;

    /**
     * Search for messages.
     *
     * @param array{_: 'inputMessagesFilterEmpty'}|array{_: 'inputMessagesFilterPhotos'}|array{_: 'inputMessagesFilterVideo'}|array{_: 'inputMessagesFilterPhotoVideo'}|array{_: 'inputMessagesFilterDocument'}|array{_: 'inputMessagesFilterUrl'}|array{_: 'inputMessagesFilterGif'}|array{_: 'inputMessagesFilterVoice'}|array{_: 'inputMessagesFilterMusic'}|array{_: 'inputMessagesFilterChatPhotos'}|array{_: 'inputMessagesFilterPhoneCalls', missed?: bool}|array{_: 'inputMessagesFilterRoundVoice'}|array{_: 'inputMessagesFilterRoundVideo'}|array{_: 'inputMessagesFilterMyMentions'}|array{_: 'inputMessagesFilterGeo'}|array{_: 'inputMessagesFilterContacts'}|array{_: 'inputMessagesFilterPinned'}|array{_: 'inputMessagesFilterPoll'} $filter Filter to return only specified message types @see https://docs.madelineproto.xyz/API_docs/types/MessagesFilter.html
     * @param array|int|string $peer User or chat, histories with which are searched, or [(inputPeerEmpty)](https://docs.madelineproto.xyz/API_docs/constructors/inputPeerEmpty.html) constructor to search in all private chats and [normal groups (not channels) Â»](https://core.telegram.org/api/channel). Use [messages.searchGlobal](https://docs.madelineproto.xyz/API_docs/methods/messages.searchGlobal.html) to search globally in all chats, groups, supergroups and channels. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param string $q Text search request
     * @param array|int|string $from_id Only return messages sent by the specified user ID @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param array|int|string $saved_peer_id Search within the [saved message dialog Â»](https://core.telegram.org/api/saved-messages) with this ID. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param list<array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon?: string}|array{_: 'reactionCustomEmoji', document_id?: int}|array{_: 'reactionPaid'}> $saved_reaction Array of You may search for [saved messages tagged Â»](https://core.telegram.org/api/saved-messages#tags) with one or more reactions using this flag. @see https://docs.madelineproto.xyz/API_docs/types/Reaction.html
     * @param int $top_msg_id [Thread ID](https://core.telegram.org/api/threads)
     * @param int $min_date If a positive value was transferred, only messages with a sending date bigger than the transferred one will be returned
     * @param int $max_date If a positive value was transferred, only messages with a sending date smaller than the transferred one will be returned
     * @param int $offset_id Only return messages starting from the specified message ID
     * @param int $add_offset [Additional offset](https://core.telegram.org/api/offsets)
     * @param int $limit [Number of results to return](https://core.telegram.org/api/offsets), can be 0 to only return the message counter.
     * @param int $max_id [Maximum message ID to return](https://core.telegram.org/api/offsets)
     * @param int $min_id [Minimum message ID to return](https://core.telegram.org/api/offsets)
     * @param list<int|string>|array<never, never> $hash Array of [Hash](https://core.telegram.org/api/offsets) @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @param ?int $takeoutId Optional takeout ID, generated using account.initTakeoutSession, see [the takeout docs](https://core.telegram.org/api/takeout) for more info.
     * @return array{_: 'messages.messages', messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.messagesSlice', inexact: array, count: array, next_rate?: array, offset_id_offset?: array, search_flood?: array, messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.channelMessages', inexact: array, pts: array, count: array, offset_id_offset?: array, messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.messagesNotModified', count: array} @see https://docs.madelineproto.xyz/API_docs/types/messages.Messages.html
     * @psalm-impure
     */
    public function search(array $filter, array|int|string|null $peer = null, string|null $q = '', array|int|string|null $from_id = null, array|int|string|null $saved_peer_id = null, array|null $saved_reaction = null, int|null $top_msg_id = null, int|null $min_date = 0, int|null $max_date = 0, int|null $offset_id = 0, int|null $add_offset = 0, int|null $limit = 0, int|null $max_id = 0, int|null $min_id = 0, array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null, ?int $takeoutId = null): array;

    /**
     * Marks message history as read.
     *
     * @param array|int|string $peer Target user or group @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $max_id If a positive value is passed, only messages with identifiers less or equal than the given one will be read
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @param ?string $businessConnectionId Business connection ID, received through an updateBotBusinessConnect update.
     * @return array{_: 'messages.affectedMessages', pts: int, pts_count: int} @see https://docs.madelineproto.xyz/API_docs/types/messages.AffectedMessages.html
     * @psalm-impure
     */
    public function readHistory(array|int|string|null $peer = null, int|null $max_id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null, ?string $businessConnectionId = null): array;

    /**
     * Deletes communication history.
     *
     * @param bool $just_clear Just clear history for the current user, without actually removing messages for every chat user
     * @param bool $revoke Whether to delete the message history for all chat participants
     * @param array|int|string $peer User or chat, communication history of which will be deleted @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $max_id Maximum ID of message to delete
     * @param int $min_date Delete all messages newer than this UNIX timestamp
     * @param int $max_date Delete all messages older than this UNIX timestamp
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.affectedHistory', pts: int, pts_count: int, offset: int} @see https://docs.madelineproto.xyz/API_docs/types/messages.AffectedHistory.html
     * @psalm-impure
     */
    public function deleteHistory(bool|null $just_clear = null, bool|null $revoke = null, array|int|string|null $peer = null, int|null $max_id = 0, int|null $min_date = null, int|null $max_date = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Deletes messages by their identifiers.
     *
     * @param bool $revoke Whether to delete messages for all participants of the chat
     * @param list<int>|array<never, never> $id Message ID list
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @param ?string $businessConnectionId Business connection ID, received through an updateBotBusinessConnect update.
     * @return array{_: 'messages.affectedMessages', pts: int, pts_count: int} @see https://docs.madelineproto.xyz/API_docs/types/messages.AffectedMessages.html
     * @psalm-impure
     */
    public function deleteMessages(bool|null $revoke = null, array $id = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null, ?string $businessConnectionId = null): array;

    /**
     * Confirms receipt of messages by a client, cancels PUSH-notification sending.
     *
     * @param int $max_id Maximum message ID available in a client.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return list<array{_: 'receivedNotifyMessage', id: int}> Array of  @see https://docs.madelineproto.xyz/API_docs/types/ReceivedNotifyMessage.html
     * @psalm-impure
     */
    public function receivedMessages(int|null $max_id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Sends a current user typing event (see [SendMessageAction](https://docs.madelineproto.xyz/API_docs/types/SendMessageAction.html) for all event types) to a conversation partner or group.
     *
     * @param array{_: 'sendMessageTypingAction'}|array{_: 'sendMessageCancelAction'}|array{_: 'sendMessageRecordVideoAction'}|array{_: 'sendMessageUploadVideoAction', progress?: int}|array{_: 'sendMessageRecordAudioAction'}|array{_: 'sendMessageUploadAudioAction', progress?: int}|array{_: 'sendMessageUploadPhotoAction', progress?: int}|array{_: 'sendMessageUploadDocumentAction', progress?: int}|array{_: 'sendMessageGeoLocationAction'}|array{_: 'sendMessageChooseContactAction'}|array{_: 'sendMessageGamePlayAction'}|array{_: 'sendMessageRecordRoundAction'}|array{_: 'sendMessageUploadRoundAction', progress?: int}|array{_: 'speakingInGroupCallAction'}|array{_: 'sendMessageHistoryImportAction', progress?: int}|array{_: 'sendMessageChooseStickerAction'}|array{_: 'sendMessageEmojiInteraction', interaction: mixed, emoticon?: string, msg_id?: int}|array{_: 'sendMessageEmojiInteractionSeen', emoticon?: string}|array{_: 'sendMessageTextDraftAction', text: array{_: 'textWithEntities', text?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>}, can_stop?: bool, keep_on_stop?: bool}|array{_: 'inputSendMessageRichMessageDraftAction', rich_message: array{_: 'inputRichMessage', rtl?: bool, noautolink?: bool, blocks?: list<array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, published_date?: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, language?: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name?: string}|array{_: 'pageBlockList', items?: list<array{_: 'pageListItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, checkbox?: bool, checked?: bool}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, collapsed?: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, spoiler?: bool, photo_id?: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, autoplay?: bool, loop?: bool, spoiler?: bool, video_id?: int}|array{_: 'pageBlockCover', cover: array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, published_date?: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, language?: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name?: string}|array{_: 'pageBlockList', items?: list<array{_: 'pageListItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textConcat', texts?: list<array>}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}, checkbox?: bool, checked?: bool}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, collapsed?: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textConcat', texts?: list<array>}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textConcat', texts?: list<array>}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}}, spoiler?: bool, photo_id?: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textConcat', texts?: list<array>}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textConcat', texts?: list<array>}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}}, autoplay?: bool, loop?: bool, spoiler?: bool, video_id?: int}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textConcat', texts?: list<array>}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textConcat', texts?: list<array>}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}}, full_width?: bool, allow_scrolling?: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel?: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textConcat', texts?: list<array>}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textConcat', texts?: list<array>}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}}, audio_id?: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, bordered?: bool, striped?: bool, compact?: bool, rows?: list<array{_: 'pageTableRow', cells?: list<array{_: 'pageTableCell', header?: array, align_center?: array, align_right?: array, valign_middle?: array, valign_bottom?: array, text?: array, colspan?: array, rowspan?: array}>}>}|array{_: 'pageBlockOrderedList', reversed?: bool, items?: list<array{_: 'pageListOrderedItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textConcat', texts?: list<array>}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}, checkbox?: bool, checked?: bool, num?: string, value?: int, type?: string}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, articles?: list<array{_: 'pageRelatedArticle', url?: string, webpage_id?: int, title?: string, description?: string, photo_id?: int, author?: string, published_date?: int}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textConcat', texts?: list<array>}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textConcat', texts?: list<array>}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}}, geo?: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash?: int, accuracy_radius?: int}, zoom?: int, w?: int, h?: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockMath', source?: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textConcat', texts?: list<array>}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textConcat', texts?: list<array>}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}}, geo?: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: float, long: float, accuracy_radius?: int}, zoom?: int, w?: int, h?: int}|array{_: 'pageBlockButtonRow', align_left?: bool, align_center?: bool, align_right?: bool, buttons?: list<array{_: 'pageButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textConcat', texts?: list<array>}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}, type: array{_: 'inlineButtonTypeUrl', url?: array}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: array, url?: array, button_id?: array}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access?: array, fwd_text?: array, url?: array, bot?: array}|array{_: 'inlineButtonTypeWebView', url?: array}|array{_: 'inlineButtonTypeCallback', requires_password?: array, data?: array}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer?: array, query?: array, peer_types?: list<array>}|array{_: 'inlineButtonTypeUserProfile', user_id?: array}|array{_: 'inputInlineButtonTypeUserProfile', user_id?: array}|array{_: 'inlineButtonTypeCopy', copy_text?: array}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary?: array, bg_danger?: array, bg_success?: array, link?: array}}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textConcat', texts?: list<array>}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textConcat', texts?: list<array>}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}}, document_id?: int}}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, full_width?: bool, allow_scrolling?: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel?: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, audio_id?: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, bordered?: bool, striped?: bool, compact?: bool, rows?: list<array{_: 'pageTableRow', cells?: list<array{_: 'pageTableCell', header?: bool, align_center?: bool, align_right?: bool, valign_middle?: bool, valign_bottom?: bool, text?: array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textConcat', texts?: list<array>}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}, colspan?: int, rowspan?: int}>}>}|array{_: 'pageBlockOrderedList', reversed?: bool, items?: list<array{_: 'pageListOrderedItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, checkbox?: bool, checked?: bool, num?: string, value?: int, type?: string}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, articles?: list<array{_: 'pageRelatedArticle', url?: string, webpage_id?: int, title?: string, description?: string, photo_id?: int, author?: string, published_date?: int}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, geo?: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash?: int, accuracy_radius?: int}, zoom?: int, w?: int, h?: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockMath', source?: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, geo?: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: float, long: float, accuracy_radius?: int}, zoom?: int, w?: int, h?: int}|array{_: 'pageBlockButtonRow', align_left?: bool, align_center?: bool, align_right?: bool, buttons?: list<array{_: 'pageButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, type: array{_: 'inlineButtonTypeUrl', url?: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url?: string, button_id?: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access?: bool, fwd_text?: string, url?: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url?: string}|array{_: 'inlineButtonTypeCallback', requires_password?: bool, data?: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer?: bool, query?: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id?: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id?: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text?: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary?: bool, bg_danger?: bool, bg_success?: bool, link?: bool}}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, document_id?: int}>, photos?: list<array>, documents?: list<array>, users?: list<array|int|string>}|array{_: 'inputRichMessageHTML', rtl?: bool, noautolink?: bool, html?: string, files?: list<array{_: 'inputRichFilePhoto', id?: string, photo?: array}|array{_: 'inputRichFileDocument', id?: string, document?: array}>}|array{_: 'inputRichMessageMarkdown', rtl?: bool, noautolink?: bool, markdown?: string, files?: list<array{_: 'inputRichFilePhoto', id?: string, photo?: array}|array{_: 'inputRichFileDocument', id?: string, document?: array}>}, can_stop?: bool, keep_on_stop?: bool}|array{_: 'sendMessageRichMessageDraftAction', rich_message: array{_: 'richMessage', rtl?: bool, part?: bool, blocks?: list<array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, published_date?: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, language?: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name?: string}|array{_: 'pageBlockList', items?: list<array{_: 'pageListItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, checkbox?: bool, checked?: bool}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, collapsed?: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, spoiler?: bool, photo_id?: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, autoplay?: bool, loop?: bool, spoiler?: bool, video_id?: int}|array{_: 'pageBlockCover', cover: array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, published_date?: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, language?: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name?: string}|array{_: 'pageBlockList', items?: list<array{_: 'pageListItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textConcat', texts?: list<array>}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}, checkbox?: bool, checked?: bool}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, collapsed?: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textConcat', texts?: list<array>}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textConcat', texts?: list<array>}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}}, spoiler?: bool, photo_id?: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textConcat', texts?: list<array>}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textConcat', texts?: list<array>}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}}, autoplay?: bool, loop?: bool, spoiler?: bool, video_id?: int}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textConcat', texts?: list<array>}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textConcat', texts?: list<array>}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}}, full_width?: bool, allow_scrolling?: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel?: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textConcat', texts?: list<array>}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textConcat', texts?: list<array>}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}}, audio_id?: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, bordered?: bool, striped?: bool, compact?: bool, rows?: list<array{_: 'pageTableRow', cells?: list<array{_: 'pageTableCell', header?: array, align_center?: array, align_right?: array, valign_middle?: array, valign_bottom?: array, text?: array, colspan?: array, rowspan?: array}>}>}|array{_: 'pageBlockOrderedList', reversed?: bool, items?: list<array{_: 'pageListOrderedItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textConcat', texts?: list<array>}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}, checkbox?: bool, checked?: bool, num?: string, value?: int, type?: string}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, articles?: list<array{_: 'pageRelatedArticle', url?: string, webpage_id?: int, title?: string, description?: string, photo_id?: int, author?: string, published_date?: int}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textConcat', texts?: list<array>}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textConcat', texts?: list<array>}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}}, geo?: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash?: int, accuracy_radius?: int}, zoom?: int, w?: int, h?: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockMath', source?: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textConcat', texts?: list<array>}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textConcat', texts?: list<array>}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}}, geo?: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: float, long: float, accuracy_radius?: int}, zoom?: int, w?: int, h?: int}|array{_: 'pageBlockButtonRow', align_left?: bool, align_center?: bool, align_right?: bool, buttons?: list<array{_: 'pageButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textConcat', texts?: list<array>}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}, type: array{_: 'inlineButtonTypeUrl', url?: array}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: array, url?: array, button_id?: array}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access?: array, fwd_text?: array, url?: array, bot?: array}|array{_: 'inlineButtonTypeWebView', url?: array}|array{_: 'inlineButtonTypeCallback', requires_password?: array, data?: array}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer?: array, query?: array, peer_types?: list<array>}|array{_: 'inlineButtonTypeUserProfile', user_id?: array}|array{_: 'inputInlineButtonTypeUserProfile', user_id?: array}|array{_: 'inlineButtonTypeCopy', copy_text?: array}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary?: array, bg_danger?: array, bg_success?: array, link?: array}}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textConcat', texts?: list<array>}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textConcat', texts?: list<array>}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}}, document_id?: int}}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, full_width?: bool, allow_scrolling?: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel?: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, audio_id?: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, bordered?: bool, striped?: bool, compact?: bool, rows?: list<array{_: 'pageTableRow', cells?: list<array{_: 'pageTableCell', header?: bool, align_center?: bool, align_right?: bool, valign_middle?: bool, valign_bottom?: bool, text?: array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textConcat', texts?: list<array>}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}, colspan?: int, rowspan?: int}>}>}|array{_: 'pageBlockOrderedList', reversed?: bool, items?: list<array{_: 'pageListOrderedItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, checkbox?: bool, checked?: bool, num?: string, value?: int, type?: string}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, articles?: list<array{_: 'pageRelatedArticle', url?: string, webpage_id?: int, title?: string, description?: string, photo_id?: int, author?: string, published_date?: int}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, geo?: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash?: int, accuracy_radius?: int}, zoom?: int, w?: int, h?: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockMath', source?: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, geo?: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: float, long: float, accuracy_radius?: int}, zoom?: int, w?: int, h?: int}|array{_: 'pageBlockButtonRow', align_left?: bool, align_center?: bool, align_right?: bool, buttons?: list<array{_: 'pageButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, type: array{_: 'inlineButtonTypeUrl', url?: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url?: string, button_id?: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access?: bool, fwd_text?: string, url?: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url?: string}|array{_: 'inlineButtonTypeCallback', requires_password?: bool, data?: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer?: bool, query?: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id?: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id?: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text?: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary?: bool, bg_danger?: bool, bg_success?: bool, link?: bool}}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, document_id?: int}>, photos?: list<array{_: 'photoEmpty', id?: array}|array{_: 'photo', has_stickers?: array, id?: array, access_hash?: array, file_reference?: array, date?: array, sizes?: list<array>, video_sizes?: list<array>, dc_id?: array}>, documents?: list<array{_: 'documentEmpty', id?: array}|array{_: 'document', id?: array, access_hash?: array, file_reference?: array, date?: array, mime_type?: array, size?: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id?: array, attributes?: list<array>}>}, can_stop?: bool, keep_on_stop?: bool}|array{_: 'sendMessageStopDraftAction'}|array{_: 'sendMessageUploadVideoAction'}|array{_: 'sendMessageUploadAudioAction'}|array{_: 'sendMessageUploadPhotoAction'}|array{_: 'sendMessageUploadDocumentAction'}|array{_: 'sendMessageUploadRoundAction'} $action Type of action @see https://docs.madelineproto.xyz/API_docs/types/SendMessageAction.html
     * @param array|int|string $peer Target user or group @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $top_msg_id [Topic ID](https://core.telegram.org/api/threads)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @param ?string $businessConnectionId Business connection ID, received through an updateBotBusinessConnect update.
     * @return bool 
     * @psalm-impure
     */
    public function setTyping(array $action, array|int|string|null $peer = null, int|null $top_msg_id = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null, ?string $businessConnectionId = null): bool;

    /**
     * Sends a message to a chat
     *
     * @param bool $no_webpage Set this flag to disable generation of the webpage preview
     * @param bool $silent Send this message silently (no notifications for the receivers)
     * @param bool $background Send this message as background message
     * @param bool $clear_draft Clear the draft field
     * @param bool $noforwards Only for bots, disallows forwarding and saving of the messages, even if the destination chat doesn't have [content protection](https://telegram.org/blog/protected-content-delete-by-date-and-more) enabled
     * @param bool $update_stickersets_order Whether to move used stickersets to top, [see here for more info on this flag Â»](https://core.telegram.org/api/stickers#recent-stickersets)
     * @param bool $invert_media If set, any eventual webpage preview will be shown on top of the message instead of at the bottom.
     * @param bool $allow_paid_floodskip Bots only: if set, allows sending up to 1000 messages per second, ignoring [broadcasting limits](https://core.telegram.org/bots/faq#how-can-i-message-all-of-my-bot-39s-subscribers-at-once) for a fee of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance.
     * @param array|int|string $peer The destination where the message will be sent @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param array{_: 'inputReplyToMessage', reply_to_msg_id?: int, top_msg_id?: int, reply_to_peer_id?: array|int|string, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>, quote_offset?: int, monoforum_peer_id?: array|int|string, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, url?: string, webpage_id?: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, email?: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, phone?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, name?: string}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, user_id?: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, date?: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, type: array{_: 'inlineButtonTypeUrl', url?: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url?: string, button_id?: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access?: bool, fwd_text?: string, url?: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url?: string}|array{_: 'inlineButtonTypeCallback', requires_password?: bool, data?: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer?: bool, query?: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id?: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id?: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text?: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary?: bool, bg_danger?: bool, bg_success?: bool, link?: bool}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}>}|array{_: 'inputReplyToStory', peer?: array|int|string, story_id?: int}|array{_: 'inputReplyToMonoForum', monoforum_peer_id?: array|int|string}|array{_: 'inputReplyToEphemeralMessage', id?: int} $reply_to If set, indicates that the message should be sent in reply to the specified message or story. <br>Also used to quote other messages. @see https://docs.madelineproto.xyz/API_docs/types/InputReplyTo.html
     * @param string $message The message
     * @param array $reply_markup Reply markup for sending bot buttons @see https://docs.madelineproto.xyz/API_docs/types/ReplyMarkup.html
     * @param list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}> $entities Array of Message [entities](https://core.telegram.org/api/entities) for sending styled text @see https://docs.madelineproto.xyz/API_docs/types/MessageEntity.html
     * @param \danog\MadelineProto\ParseMode $parse_mode Whether to parse HTML or Markdown markup in the message
     * @param int $schedule_date Scheduled message date for [scheduled messages](https://core.telegram.org/api/scheduled-messages)
     * @param int $schedule_repeat_period 
     * @param array|int|string $send_as Send this message as the specified peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param array{_: 'inputQuickReplyShortcut', shortcut?: string}|array{_: 'inputQuickReplyShortcutId', shortcut_id?: int} $quick_reply_shortcut Add the message to the specified [quick reply shortcut Â»](https://core.telegram.org/api/business#quick-reply-shortcuts), instead. @see https://docs.madelineproto.xyz/API_docs/types/InputQuickReplyShortcut.html
     * @param int $effect Specifies a [message effect Â»](https://core.telegram.org/api/effects) to use for the message.
     * @param int $allow_paid_stars For [paid messages Â»](https://core.telegram.org/api/paid-messages), specifies the amount of [Telegram Stars](https://core.telegram.org/api/stars) the user has agreed to pay in order to send the message.
     * @param array{_: 'suggestedPost', accepted?: bool, rejected?: bool, price?: array{_: 'starsAmount', amount?: int, nanos?: int}|array{_: 'starsTonAmount', amount?: int}, schedule_date?: int} $suggested_post Used to [suggest a post to a channel, see here Â»](https://core.telegram.org/api/suggested-posts) for more info on the full flow. @see https://docs.madelineproto.xyz/API_docs/types/SuggestedPost.html
     * @param array{_: 'inputRichMessage', rtl?: bool, noautolink?: bool, blocks?: list<array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, published_date?: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, language?: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name?: string}|array{_: 'pageBlockList', items?: list<array{_: 'pageListItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, checkbox?: bool, checked?: bool}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, collapsed?: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, spoiler?: bool, photo_id?: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, autoplay?: bool, loop?: bool, spoiler?: bool, video_id?: int}|array{_: 'pageBlockCover', cover: array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, published_date?: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, language?: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name?: string}|array{_: 'pageBlockList', items?: list<array{_: 'pageListItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, checkbox?: bool, checked?: bool}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, collapsed?: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, spoiler?: bool, photo_id?: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, autoplay?: bool, loop?: bool, spoiler?: bool, video_id?: int}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, full_width?: bool, allow_scrolling?: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel?: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, audio_id?: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, bordered?: bool, striped?: bool, compact?: bool, rows?: list<array{_: 'pageTableRow', cells?: list<array{_: 'pageTableCell', header?: bool, align_center?: bool, align_right?: bool, valign_middle?: bool, valign_bottom?: bool, text?: array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textConcat', texts?: list<array>}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}, colspan?: int, rowspan?: int}>}>}|array{_: 'pageBlockOrderedList', reversed?: bool, items?: list<array{_: 'pageListOrderedItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, checkbox?: bool, checked?: bool, num?: string, value?: int, type?: string}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, articles?: list<array{_: 'pageRelatedArticle', url?: string, webpage_id?: int, title?: string, description?: string, photo_id?: int, author?: string, published_date?: int}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, geo?: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash?: int, accuracy_radius?: int}, zoom?: int, w?: int, h?: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockMath', source?: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, geo?: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: float, long: float, accuracy_radius?: int}, zoom?: int, w?: int, h?: int}|array{_: 'pageBlockButtonRow', align_left?: bool, align_center?: bool, align_right?: bool, buttons?: list<array{_: 'pageButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, type: array{_: 'inlineButtonTypeUrl', url?: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url?: string, button_id?: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access?: bool, fwd_text?: string, url?: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url?: string}|array{_: 'inlineButtonTypeCallback', requires_password?: bool, data?: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer?: bool, query?: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id?: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id?: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text?: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary?: bool, bg_danger?: bool, bg_success?: bool, link?: bool}}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, document_id?: int}}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, full_width?: bool, allow_scrolling?: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel?: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, audio_id?: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, bordered?: bool, striped?: bool, compact?: bool, rows?: list<array{_: 'pageTableRow', cells?: list<array{_: 'pageTableCell', header?: bool, align_center?: bool, align_right?: bool, valign_middle?: bool, valign_bottom?: bool, text?: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, colspan?: int, rowspan?: int}>}>}|array{_: 'pageBlockOrderedList', reversed?: bool, items?: list<array{_: 'pageListOrderedItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, checkbox?: bool, checked?: bool, num?: string, value?: int, type?: string}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, articles?: list<array{_: 'pageRelatedArticle', url?: string, webpage_id?: int, title?: string, description?: string, photo_id?: int, author?: string, published_date?: int}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, geo?: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash?: int, accuracy_radius?: int}, zoom?: int, w?: int, h?: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockMath', source?: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, geo?: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: float, long: float, accuracy_radius?: int}, zoom?: int, w?: int, h?: int}|array{_: 'pageBlockButtonRow', align_left?: bool, align_center?: bool, align_right?: bool, buttons?: list<array{_: 'pageButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, type: array{_: 'inlineButtonTypeUrl', url?: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url?: string, button_id?: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access?: bool, fwd_text?: string, url?: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url?: string}|array{_: 'inlineButtonTypeCallback', requires_password?: bool, data?: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer?: bool, query?: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id?: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id?: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text?: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary?: bool, bg_danger?: bool, bg_success?: bool, link?: bool}}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, document_id?: int}>, photos?: list<array>, documents?: list<array>, users?: list<array|int|string>}|array{_: 'inputRichMessageHTML', rtl?: bool, noautolink?: bool, html?: string, files?: list<array{_: 'inputRichFilePhoto', id?: string, photo?: array}|array{_: 'inputRichFileDocument', id?: string, document?: array}>}|array{_: 'inputRichMessageMarkdown', rtl?: bool, noautolink?: bool, markdown?: string, files?: list<array{_: 'inputRichFilePhoto', id?: string, photo?: array}|array{_: 'inputRichFileDocument', id?: string, document?: array}>} $rich_message @see https://docs.madelineproto.xyz/API_docs/types/InputRichMessage.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @param ?string $businessConnectionId Business connection ID, received through an updateBotBusinessConnect update.
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function sendMessage(bool|null $no_webpage = null, bool|null $silent = null, bool|null $background = null, bool|null $clear_draft = null, bool|null $noforwards = null, bool|null $update_stickersets_order = null, bool|null $invert_media = null, bool|null $allow_paid_floodskip = null, array|int|string|null $peer = null, array|null $reply_to = null, string|null $message = '', array|null $reply_markup = null, array|null $entities = null, \danog\MadelineProto\ParseMode $parse_mode = \danog\MadelineProto\ParseMode::TEXT, int|null $schedule_date = null, int|null $schedule_repeat_period = null, array|int|string|null $send_as = null, array|null $quick_reply_shortcut = null, int|null $effect = null, int|null $allow_paid_stars = null, array|null $suggested_post = null, array|null $rich_message = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null, ?string $businessConnectionId = null): array;

    /**
     * Send a media
     *
     * @param bool $silent Send message silently (no notification should be triggered)
     * @param bool $background Send message in background
     * @param bool $clear_draft Clear the draft
     * @param bool $noforwards Only for bots, disallows forwarding and saving of the messages, even if the destination chat doesn't have [content protection](https://telegram.org/blog/protected-content-delete-by-date-and-more) enabled
     * @param bool $update_stickersets_order Whether to move used stickersets to top, [see here for more info on this flag Â»](https://core.telegram.org/api/stickers#recent-stickersets)
     * @param bool $invert_media If set, any eventual webpage preview will be shown on top of the message instead of at the bottom.
     * @param bool $allow_paid_floodskip Bots only: if set, allows sending up to 1000 messages per second, ignoring [broadcasting limits](https://core.telegram.org/bots/faq#how-can-i-message-all-of-my-bot-39s-subscribers-at-once) for a fee of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance.
     * @param array|int|string $peer Destination @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param array{_: 'inputReplyToMessage', reply_to_msg_id?: int, top_msg_id?: int, reply_to_peer_id?: array|int|string, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>, quote_offset?: int, monoforum_peer_id?: array|int|string, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, url?: string, webpage_id?: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, email?: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, phone?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, name?: string}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, user_id?: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, date?: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, type: array{_: 'inlineButtonTypeUrl', url?: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url?: string, button_id?: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access?: bool, fwd_text?: string, url?: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url?: string}|array{_: 'inlineButtonTypeCallback', requires_password?: bool, data?: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer?: bool, query?: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id?: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id?: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text?: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary?: bool, bg_danger?: bool, bg_success?: bool, link?: bool}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}>}|array{_: 'inputReplyToStory', peer?: array|int|string, story_id?: int}|array{_: 'inputReplyToMonoForum', monoforum_peer_id?: array|int|string}|array{_: 'inputReplyToEphemeralMessage', id?: int} $reply_to If set, indicates that the message should be sent in reply to the specified message or story. @see https://docs.madelineproto.xyz/API_docs/types/InputReplyTo.html
     * @param \danog\MadelineProto\EventHandler\Media|string|array $media Attached media @see https://docs.madelineproto.xyz/API_docs/types/InputMedia.html
     * @param string $message Caption
     * @param array $reply_markup Reply markup for bot keyboards @see https://docs.madelineproto.xyz/API_docs/types/ReplyMarkup.html
     * @param list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}> $entities Array of Message [entities](https://core.telegram.org/api/entities) for styled text @see https://docs.madelineproto.xyz/API_docs/types/MessageEntity.html
     * @param \danog\MadelineProto\ParseMode $parse_mode Whether to parse HTML or Markdown markup in the message
     * @param int $schedule_date Scheduled message date for [scheduled messages](https://core.telegram.org/api/scheduled-messages)
     * @param int $schedule_repeat_period 
     * @param array|int|string $send_as Send this message as the specified peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param array{_: 'inputQuickReplyShortcut', shortcut?: string}|array{_: 'inputQuickReplyShortcutId', shortcut_id?: int} $quick_reply_shortcut Add the message to the specified [quick reply shortcut Â»](https://core.telegram.org/api/business#quick-reply-shortcuts), instead. @see https://docs.madelineproto.xyz/API_docs/types/InputQuickReplyShortcut.html
     * @param int $effect Specifies a [message effect Â»](https://core.telegram.org/api/effects) to use for the message.
     * @param int $allow_paid_stars For [paid messages Â»](https://core.telegram.org/api/paid-messages), specifies the amount of [Telegram Stars](https://core.telegram.org/api/stars) the user has agreed to pay in order to send the message.
     * @param array{_: 'suggestedPost', accepted?: bool, rejected?: bool, price?: array{_: 'starsAmount', amount?: int, nanos?: int}|array{_: 'starsTonAmount', amount?: int}, schedule_date?: int} $suggested_post Used to [suggest a post to a channel, see here Â»](https://core.telegram.org/api/suggested-posts) for more info on the full flow. @see https://docs.madelineproto.xyz/API_docs/types/SuggestedPost.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @param ?string $businessConnectionId Business connection ID, received through an updateBotBusinessConnect update.
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function sendMedia(bool|null $silent = null, bool|null $background = null, bool|null $clear_draft = null, bool|null $noforwards = null, bool|null $update_stickersets_order = null, bool|null $invert_media = null, bool|null $allow_paid_floodskip = null, array|int|string|null $peer = null, array|null $reply_to = null, \danog\MadelineProto\EventHandler\Media|array|string|null $media = null, string|null $message = '', array|null $reply_markup = null, array|null $entities = null, \danog\MadelineProto\ParseMode $parse_mode = \danog\MadelineProto\ParseMode::TEXT, int|null $schedule_date = null, int|null $schedule_repeat_period = null, array|int|string|null $send_as = null, array|null $quick_reply_shortcut = null, int|null $effect = null, int|null $allow_paid_stars = null, array|null $suggested_post = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null, ?string $businessConnectionId = null): array;

    /**
     * Forwards messages by their IDs.
     *
     * @param bool $silent Whether to send messages silently (no notification will be triggered on the destination clients)
     * @param bool $background Whether to send the message in background
     * @param bool $with_my_score When forwarding games, whether to include your score in the game
     * @param bool $drop_author Whether to forward messages without quoting the original author
     * @param bool $drop_media_captions Whether to strip captions from media
     * @param bool $noforwards Only for bots, disallows further re-forwarding and saving of the messages, even if the destination chat doesn't have [content protection](https://telegram.org/blog/protected-content-delete-by-date-and-more) enabled
     * @param bool $allow_paid_floodskip Bots only: if set, allows sending up to 1000 messages per second, ignoring [broadcasting limits](https://core.telegram.org/bots/faq#how-can-i-message-all-of-my-bot-39s-subscribers-at-once) for a fee of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance.
     * @param bool $from_ephemeral 
     * @param array|int|string $from_peer Source of messages @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param list<int>|array<never, never> $id IDs of messages
     * @param array|int|string $to_peer Destination peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $top_msg_id Destination [forum topic](https://core.telegram.org/api/forum#forum-topics)
     * @param array{_: 'inputReplyToMessage', reply_to_msg_id?: int, top_msg_id?: int, reply_to_peer_id?: array|int|string, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>, quote_offset?: int, monoforum_peer_id?: array|int|string, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, url?: string, webpage_id?: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, email?: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, phone?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, name?: string}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, user_id?: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, date?: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, type: array{_: 'inlineButtonTypeUrl', url?: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url?: string, button_id?: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access?: bool, fwd_text?: string, url?: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url?: string}|array{_: 'inlineButtonTypeCallback', requires_password?: bool, data?: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer?: bool, query?: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id?: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id?: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text?: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary?: bool, bg_danger?: bool, bg_success?: bool, link?: bool}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}>}|array{_: 'inputReplyToStory', peer?: array|int|string, story_id?: int}|array{_: 'inputReplyToMonoForum', monoforum_peer_id?: array|int|string}|array{_: 'inputReplyToEphemeralMessage', id?: int} $reply_to Can only contain an [inputReplyToMonoForum](https://docs.madelineproto.xyz/API_docs/constructors/inputReplyToMonoForum.html), to forward messages to a [monoforum topic](https://core.telegram.org/api/monoforum) (mutually exclusive with `top_msg_id`). @see https://docs.madelineproto.xyz/API_docs/types/InputReplyTo.html
     * @param int $schedule_date Scheduled message date for scheduled messages
     * @param int $schedule_repeat_period 
     * @param array|int|string $send_as Forward the messages as the specified peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param array{_: 'inputQuickReplyShortcut', shortcut?: string}|array{_: 'inputQuickReplyShortcutId', shortcut_id?: int} $quick_reply_shortcut Add the messages to the specified [quick reply shortcut Â»](https://core.telegram.org/api/business#quick-reply-shortcuts), instead. @see https://docs.madelineproto.xyz/API_docs/types/InputQuickReplyShortcut.html
     * @param int $effect 
     * @param int $video_timestamp Start playing the video at the specified timestamp (seconds).
     * @param int $allow_paid_stars For [paid messages Â»](https://core.telegram.org/api/paid-messages), specifies the amount of [Telegram Stars](https://core.telegram.org/api/stars) the user has agreed to pay in order to send the message.
     * @param array{_: 'suggestedPost', accepted?: bool, rejected?: bool, price?: array{_: 'starsAmount', amount?: int, nanos?: int}|array{_: 'starsTonAmount', amount?: int}, schedule_date?: int} $suggested_post Used to [suggest a post to a channel, see here Â»](https://core.telegram.org/api/suggested-posts) for more info on the full flow. @see https://docs.madelineproto.xyz/API_docs/types/SuggestedPost.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function forwardMessages(bool|null $silent = null, bool|null $background = null, bool|null $with_my_score = null, bool|null $drop_author = null, bool|null $drop_media_captions = null, bool|null $noforwards = null, bool|null $allow_paid_floodskip = null, bool|null $from_ephemeral = null, array|int|string|null $from_peer = null, array $id = [], array|int|string|null $to_peer = null, int|null $top_msg_id = null, array|null $reply_to = null, int|null $schedule_date = null, int|null $schedule_repeat_period = null, array|int|string|null $send_as = null, array|null $quick_reply_shortcut = null, int|null $effect = null, int|null $video_timestamp = null, int|null $allow_paid_stars = null, array|null $suggested_post = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Report a new incoming chat for spam, if the [peer settings](https://docs.madelineproto.xyz/API_docs/constructors/peerSettings.html) of the chat allow us to do that
     *
     * @param array|int|string $peer Peer to report @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function reportSpam(array|int|string|null $peer = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Get peer settings
     *
     * @param array|int|string $peer The peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.peerSettings', settings: array{_: 'peerSettings', report_spam: bool, add_contact: bool, block_contact: bool, share_contact: bool, need_contacts_exception: bool, report_geo: bool, autoarchived: bool, invite_members: bool, request_chat_broadcast: bool, business_bot_paused: bool, business_bot_can_reply: bool, geo_distance?: int, request_chat_title?: string, request_chat_date?: int, business_bot_id?: int, business_bot_manage_url?: string, charge_paid_message_stars?: int, registration_month?: string, phone_country?: string, name_change_date?: int, photo_change_date?: int}, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/messages.PeerSettings.html
     * @psalm-impure
     */
    public function getPeerSettings(array|int|string|null $peer = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Report a message in a chat for violation of telegram's Terms of Service
     *
     * @param array|int|string $peer Peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param list<int>|array<never, never> $id IDs of messages to report
     * @param string $option Menu option, intially empty
     * @param string $message Comment for report moderation
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'reportResultChooseOption', title: string, options: list<array{_: 'messageReportOption', text: string, option: string}>}|array{_: 'reportResultAddComment', optional: bool, option: string}|array{_: 'reportResultReported'} @see https://docs.madelineproto.xyz/API_docs/types/ReportResult.html
     * @psalm-impure
     */
    public function report(array|int|string|null $peer = null, array $id = [], string|null $option = '', string|null $message = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Returns chat basic info on their IDs.
     *
     * @param list<int>|array<never, never> $id List of chat IDs
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.chats', chats: list<array|int|string>}|array{_: 'messages.chatsSlice', count: int, chats: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/messages.Chats.html
     * @psalm-impure
     */
    public function getChats(array $id = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Changes chat name and sends a service message on it.
     *
     * @param array|int|string $chat_id Chat ID @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param string $title New chat name, different from the old one
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function editChatTitle(array|int|string|null $chat_id = null, string|null $title = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Changes chat photo and sends a service message on it
     *
     * @param array|int|string $chat_id Chat ID @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param array{_: 'inputChatPhotoEmpty'}|array{_: 'inputChatUploadedPhoto', file?: mixed, video?: mixed, video_start_ts?: float, video_emoji_markup?: array{_: 'videoSize', type?: string, w?: int, h?: int, size?: int, video_start_ts?: float}|array{_: 'videoSizeEmojiMarkup', emoji_id?: int, background_colors?: list<int>}|array{_: 'videoSizeStickerMarkup', stickerset?: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id?: int, access_hash?: int}|array{_: 'inputStickerSetShortName', short_name?: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon?: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, sticker_id?: int, background_colors?: list<int>}}|array{_: 'inputChatPhoto', id?: array} $photo Photo to be set @see https://docs.madelineproto.xyz/API_docs/types/InputChatPhoto.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function editChatPhoto(array|int|string|null $chat_id = null, array|null $photo = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Adds a user to a chat and sends a service message on it.
     *
     * @param array|int|string $chat_id Chat ID @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param array|int|string $user_id User ID to be added @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param int $fwd_limit Number of last messages to be forwarded
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.invitedUsers', updates: array, missing_invitees: list<array{_: 'missingInvitee', premium_would_allow_invite: bool, premium_required_for_pm: bool, user_id: int}>} @see https://docs.madelineproto.xyz/API_docs/types/messages.InvitedUsers.html
     * @psalm-impure
     */
    public function addChatUser(array|int|string|null $chat_id = null, array|int|string|null $user_id = null, int|null $fwd_limit = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Deletes a user from a chat and sends a service message on it.
     *
     * @param bool $revoke_history Remove the entire chat history of the specified user in this chat.
     * @param array|int|string $chat_id Chat ID @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param array|int|string $user_id User ID to be deleted @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function deleteChatUser(bool|null $revoke_history = null, array|int|string|null $chat_id = null, array|int|string|null $user_id = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Creates a new chat.
     *
     * @param list<array|int|string>|array<never, never> $users Array of List of user IDs to be invited @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param string $title Chat name
     * @param int $ttl_period Time-to-live of all messages that will be sent in the chat: once message.date+message.ttl\_period === time(), the message will be deleted on the server, and must be deleted locally as well. You can use [messages.setDefaultHistoryTTL](https://docs.madelineproto.xyz/API_docs/methods/messages.setDefaultHistoryTTL.html) to edit this value later.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.invitedUsers', updates: array, missing_invitees: list<array{_: 'missingInvitee', premium_would_allow_invite: bool, premium_required_for_pm: bool, user_id: int}>} @see https://docs.madelineproto.xyz/API_docs/types/messages.InvitedUsers.html
     * @psalm-impure
     */
    public function createChat(array $users = [], string|null $title = '', int|null $ttl_period = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Send typing event by the current user to a secret chat.
     *
     * @param array $peer Secret chat ID @see https://docs.madelineproto.xyz/API_docs/types/InputEncryptedChat.html
     * @param bool $typing Typing.<br>**Possible values**:<br>[(boolTrue)](https://docs.madelineproto.xyz/API_docs/constructors/boolTrue.html), if the user started typing and more than **5 seconds** have passed since the last request<br>[(boolFalse)](https://docs.madelineproto.xyz/API_docs/constructors/boolFalse.html), if the user stopped typing
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function setEncryptedTyping(array $peer, bool $typing, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Marks message history within a secret chat as read.
     *
     * @param array $peer Secret chat ID @see https://docs.madelineproto.xyz/API_docs/types/InputEncryptedChat.html
     * @param int $max_date Maximum date value for received messages in history
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function readEncryptedHistory(array $peer, int|null $max_date = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Sends a text message to a secret chat.
     *
     * @param array $peer Secret chat ID @see https://docs.madelineproto.xyz/API_docs/types/InputEncryptedChat.html
     * @param array{_: 'decryptedMessage', media: array{_: 'decryptedMessageMediaEmpty'}|array{_: 'decryptedMessageMediaPhoto', thumb?: array, thumb_w?: array, thumb_h?: array, w?: array, h?: array, size?: array}|array{_: 'decryptedMessageMediaVideo', thumb?: array, thumb_w?: array, thumb_h?: array, duration?: array, w?: array, h?: array, size?: array}|array{_: 'decryptedMessageMediaGeoPoint', lat: array, long: array}|array{_: 'decryptedMessageMediaContact', phone_number?: array, first_name?: array, last_name?: array, user_id?: array}|array{_: 'decryptedMessageMediaDocument', thumb?: array, thumb_w?: array, thumb_h?: array, file_name?: array, mime_type?: array, size?: array}|array{_: 'decryptedMessageMediaAudio', duration?: array, size?: array}|array{_: 'decryptedMessageMediaVideo', thumb?: array, thumb_w?: array, thumb_h?: array, duration?: array, mime_type?: array, w?: array, h?: array, size?: array}|array{_: 'decryptedMessageMediaAudio', duration?: array, mime_type?: array, size?: array}|array{_: 'decryptedMessageMediaExternalDocument', id?: array, access_hash?: array, date?: array, mime_type?: array, size?: array, thumb?: array, dc_id?: array, attributes?: list<array>}|array{_: 'decryptedMessageMediaPhoto', thumb?: array, thumb_w?: array, thumb_h?: array, w?: array, h?: array, size?: array, caption?: array}|array{_: 'decryptedMessageMediaVideo', thumb?: array, thumb_w?: array, thumb_h?: array, duration?: array, mime_type?: array, w?: array, h?: array, size?: array, caption?: array}|array{_: 'decryptedMessageMediaDocument', thumb?: array, thumb_w?: array, thumb_h?: array, mime_type?: array, size?: array, attributes?: list<array>, caption?: array}|array{_: 'decryptedMessageMediaVenue', lat: array, long: array, title?: array, address?: array, provider?: array, venue_id?: array}|array{_: 'decryptedMessageMediaWebPage', url?: array}|array{_: 'decryptedMessageMediaDocument', thumb?: array, thumb_w?: array, thumb_h?: array, mime_type?: array, size?: array, attributes?: list<array>, caption?: array}, message?: string}|array{_: 'decryptedMessageService', action: array{_: 'decryptedMessageActionSetMessageTTL', ttl_seconds?: array}|array{_: 'decryptedMessageActionReadMessages', random_ids?: list<array>}|array{_: 'decryptedMessageActionDeleteMessages', random_ids?: list<array>}|array{_: 'decryptedMessageActionScreenshotMessages', random_ids?: list<array>}|array{_: 'decryptedMessageActionFlushHistory'}|array{_: 'decryptedMessageActionResend', start_seq_no?: array, end_seq_no?: array}|array{_: 'decryptedMessageActionNotifyLayer', layer?: array}|array{_: 'decryptedMessageActionTyping', action: array}|array{_: 'decryptedMessageActionRequestKey', exchange_id?: array, g_a?: array}|array{_: 'decryptedMessageActionAcceptKey', exchange_id?: array, g_b?: array, key_fingerprint?: array}|array{_: 'decryptedMessageActionAbortKey', exchange_id?: array}|array{_: 'decryptedMessageActionCommitKey', exchange_id?: array, key_fingerprint?: array}|array{_: 'decryptedMessageActionNoop'}}|array{_: 'decryptedMessage', media: array{_: 'decryptedMessageMediaEmpty'}|array{_: 'decryptedMessageMediaPhoto', thumb?: array, thumb_w?: array, thumb_h?: array, w?: array, h?: array, size?: array}|array{_: 'decryptedMessageMediaVideo', thumb?: array, thumb_w?: array, thumb_h?: array, duration?: array, w?: array, h?: array, size?: array}|array{_: 'decryptedMessageMediaGeoPoint', lat: array, long: array}|array{_: 'decryptedMessageMediaContact', phone_number?: array, first_name?: array, last_name?: array, user_id?: array}|array{_: 'decryptedMessageMediaDocument', thumb?: array, thumb_w?: array, thumb_h?: array, file_name?: array, mime_type?: array, size?: array}|array{_: 'decryptedMessageMediaAudio', duration?: array, size?: array}|array{_: 'decryptedMessageMediaVideo', thumb?: array, thumb_w?: array, thumb_h?: array, duration?: array, mime_type?: array, w?: array, h?: array, size?: array}|array{_: 'decryptedMessageMediaAudio', duration?: array, mime_type?: array, size?: array}|array{_: 'decryptedMessageMediaExternalDocument', id?: array, access_hash?: array, date?: array, mime_type?: array, size?: array, thumb?: array, dc_id?: array, attributes?: list<array>}|array{_: 'decryptedMessageMediaPhoto', thumb?: array, thumb_w?: array, thumb_h?: array, w?: array, h?: array, size?: array, caption?: array}|array{_: 'decryptedMessageMediaVideo', thumb?: array, thumb_w?: array, thumb_h?: array, duration?: array, mime_type?: array, w?: array, h?: array, size?: array, caption?: array}|array{_: 'decryptedMessageMediaDocument', thumb?: array, thumb_w?: array, thumb_h?: array, mime_type?: array, size?: array, attributes?: list<array>, caption?: array}|array{_: 'decryptedMessageMediaVenue', lat: array, long: array, title?: array, address?: array, provider?: array, venue_id?: array}|array{_: 'decryptedMessageMediaWebPage', url?: array}|array{_: 'decryptedMessageMediaDocument', thumb?: array, thumb_w?: array, thumb_h?: array, mime_type?: array, size?: array, attributes?: list<array>, caption?: array}, ttl?: int, message?: string}|array{_: 'decryptedMessageService', action: array{_: 'decryptedMessageActionSetMessageTTL', ttl_seconds?: array}|array{_: 'decryptedMessageActionReadMessages', random_ids?: list<array>}|array{_: 'decryptedMessageActionDeleteMessages', random_ids?: list<array>}|array{_: 'decryptedMessageActionScreenshotMessages', random_ids?: list<array>}|array{_: 'decryptedMessageActionFlushHistory'}|array{_: 'decryptedMessageActionResend', start_seq_no?: array, end_seq_no?: array}|array{_: 'decryptedMessageActionNotifyLayer', layer?: array}|array{_: 'decryptedMessageActionTyping', action: array}|array{_: 'decryptedMessageActionRequestKey', exchange_id?: array, g_a?: array}|array{_: 'decryptedMessageActionAcceptKey', exchange_id?: array, g_b?: array, key_fingerprint?: array}|array{_: 'decryptedMessageActionAbortKey', exchange_id?: array}|array{_: 'decryptedMessageActionCommitKey', exchange_id?: array, key_fingerprint?: array}|array{_: 'decryptedMessageActionNoop'}}|array{_: 'decryptedMessage', ttl?: int, message?: string, media?: array{_: 'decryptedMessageMediaEmpty'}|array{_: 'decryptedMessageMediaPhoto', thumb?: array, thumb_w?: array, thumb_h?: array, w?: array, h?: array, size?: array}|array{_: 'decryptedMessageMediaVideo', thumb?: array, thumb_w?: array, thumb_h?: array, duration?: array, w?: array, h?: array, size?: array}|array{_: 'decryptedMessageMediaGeoPoint', lat: array, long: array}|array{_: 'decryptedMessageMediaContact', phone_number?: array, first_name?: array, last_name?: array, user_id?: array}|array{_: 'decryptedMessageMediaDocument', thumb?: array, thumb_w?: array, thumb_h?: array, file_name?: array, mime_type?: array, size?: array}|array{_: 'decryptedMessageMediaAudio', duration?: array, size?: array}|array{_: 'decryptedMessageMediaVideo', thumb?: array, thumb_w?: array, thumb_h?: array, duration?: array, mime_type?: array, w?: array, h?: array, size?: array}|array{_: 'decryptedMessageMediaAudio', duration?: array, mime_type?: array, size?: array}|array{_: 'decryptedMessageMediaExternalDocument', id?: array, access_hash?: array, date?: array, mime_type?: array, size?: array, thumb?: array, dc_id?: array, attributes?: list<array>}|array{_: 'decryptedMessageMediaPhoto', thumb?: array, thumb_w?: array, thumb_h?: array, w?: array, h?: array, size?: array, caption?: array}|array{_: 'decryptedMessageMediaVideo', thumb?: array, thumb_w?: array, thumb_h?: array, duration?: array, mime_type?: array, w?: array, h?: array, size?: array, caption?: array}|array{_: 'decryptedMessageMediaDocument', thumb?: array, thumb_w?: array, thumb_h?: array, mime_type?: array, size?: array, attributes?: list<array>, caption?: array}|array{_: 'decryptedMessageMediaVenue', lat: array, long: array, title?: array, address?: array, provider?: array, venue_id?: array}|array{_: 'decryptedMessageMediaWebPage', url?: array}|array{_: 'decryptedMessageMediaDocument', thumb?: array, thumb_w?: array, thumb_h?: array, mime_type?: array, size?: array, attributes?: list<array>, caption?: array}, entities?: list<array{_: 'messageEntityUnknown', offset?: array, length?: array}|array{_: 'messageEntityMention', offset?: array, length?: array}|array{_: 'messageEntityHashtag', offset?: array, length?: array}|array{_: 'messageEntityBotCommand', offset?: array, length?: array}|array{_: 'messageEntityUrl', offset?: array, length?: array}|array{_: 'messageEntityEmail', offset?: array, length?: array}|array{_: 'messageEntityBold', offset?: array, length?: array}|array{_: 'messageEntityItalic', offset?: array, length?: array}|array{_: 'messageEntityCode', offset?: array, length?: array}|array{_: 'messageEntityPre', offset?: array, length?: array, language?: array}|array{_: 'messageEntityTextUrl', offset?: array, length?: array, url?: array}|array{_: 'messageEntityMentionName', offset?: array, length?: array, user_id?: array}|array{_: 'inputMessageEntityMentionName', offset?: array, length?: array, user_id?: array}|array{_: 'messageEntityPhone', offset?: array, length?: array}|array{_: 'messageEntityCashtag', offset?: array, length?: array}|array{_: 'messageEntityUnderline', offset?: array, length?: array}|array{_: 'messageEntityStrike', offset?: array, length?: array}|array{_: 'messageEntityBankCard', offset?: array, length?: array}|array{_: 'messageEntitySpoiler', offset?: array, length?: array}|array{_: 'messageEntityCustomEmoji', offset?: array, length?: array, document_id?: array}|array{_: 'messageEntityBlockquote', collapsed?: array, offset?: array, length?: array}|array{_: 'messageEntityFormattedDate', relative?: array, short_time?: array, long_time?: array, short_date?: array, long_date?: array, day_of_week?: array, offset?: array, length?: array, date?: array}|array{_: 'messageEntityDiffInsert', offset?: array, length?: array}|array{_: 'messageEntityDiffReplace', offset?: array, length?: array, old_text?: array}|array{_: 'messageEntityDiffDelete', offset?: array, length?: array}|array{_: 'messageEntityTonAddress', offset?: array, length?: array}|array{_: 'messageEntityBlockquote', offset?: array, length?: array}>, via_bot_name?: string, reply_to_random_id?: int}|array{_: 'decryptedMessage', no_webpage?: bool, silent?: bool, ttl?: int, message?: string, media?: array{_: 'decryptedMessageMediaEmpty'}|array{_: 'decryptedMessageMediaPhoto', thumb?: array, thumb_w?: array, thumb_h?: array, w?: array, h?: array, size?: array}|array{_: 'decryptedMessageMediaVideo', thumb?: array, thumb_w?: array, thumb_h?: array, duration?: array, w?: array, h?: array, size?: array}|array{_: 'decryptedMessageMediaGeoPoint', lat: array, long: array}|array{_: 'decryptedMessageMediaContact', phone_number?: array, first_name?: array, last_name?: array, user_id?: array}|array{_: 'decryptedMessageMediaDocument', thumb?: array, thumb_w?: array, thumb_h?: array, file_name?: array, mime_type?: array, size?: array}|array{_: 'decryptedMessageMediaAudio', duration?: array, size?: array}|array{_: 'decryptedMessageMediaVideo', thumb?: array, thumb_w?: array, thumb_h?: array, duration?: array, mime_type?: array, w?: array, h?: array, size?: array}|array{_: 'decryptedMessageMediaAudio', duration?: array, mime_type?: array, size?: array}|array{_: 'decryptedMessageMediaExternalDocument', id?: array, access_hash?: array, date?: array, mime_type?: array, size?: array, thumb?: array, dc_id?: array, attributes?: list<array>}|array{_: 'decryptedMessageMediaPhoto', thumb?: array, thumb_w?: array, thumb_h?: array, w?: array, h?: array, size?: array, caption?: array}|array{_: 'decryptedMessageMediaVideo', thumb?: array, thumb_w?: array, thumb_h?: array, duration?: array, mime_type?: array, w?: array, h?: array, size?: array, caption?: array}|array{_: 'decryptedMessageMediaDocument', thumb?: array, thumb_w?: array, thumb_h?: array, mime_type?: array, size?: array, attributes?: list<array>, caption?: array}|array{_: 'decryptedMessageMediaVenue', lat: array, long: array, title?: array, address?: array, provider?: array, venue_id?: array}|array{_: 'decryptedMessageMediaWebPage', url?: array}|array{_: 'decryptedMessageMediaDocument', thumb?: array, thumb_w?: array, thumb_h?: array, mime_type?: array, size?: array, attributes?: list<array>, caption?: array}, entities?: list<array{_: 'messageEntityUnknown', offset?: array, length?: array}|array{_: 'messageEntityMention', offset?: array, length?: array}|array{_: 'messageEntityHashtag', offset?: array, length?: array}|array{_: 'messageEntityBotCommand', offset?: array, length?: array}|array{_: 'messageEntityUrl', offset?: array, length?: array}|array{_: 'messageEntityEmail', offset?: array, length?: array}|array{_: 'messageEntityBold', offset?: array, length?: array}|array{_: 'messageEntityItalic', offset?: array, length?: array}|array{_: 'messageEntityCode', offset?: array, length?: array}|array{_: 'messageEntityPre', offset?: array, length?: array, language?: array}|array{_: 'messageEntityTextUrl', offset?: array, length?: array, url?: array}|array{_: 'messageEntityMentionName', offset?: array, length?: array, user_id?: array}|array{_: 'inputMessageEntityMentionName', offset?: array, length?: array, user_id?: array}|array{_: 'messageEntityPhone', offset?: array, length?: array}|array{_: 'messageEntityCashtag', offset?: array, length?: array}|array{_: 'messageEntityUnderline', offset?: array, length?: array}|array{_: 'messageEntityStrike', offset?: array, length?: array}|array{_: 'messageEntityBankCard', offset?: array, length?: array}|array{_: 'messageEntitySpoiler', offset?: array, length?: array}|array{_: 'messageEntityCustomEmoji', offset?: array, length?: array, document_id?: array}|array{_: 'messageEntityBlockquote', collapsed?: array, offset?: array, length?: array}|array{_: 'messageEntityFormattedDate', relative?: array, short_time?: array, long_time?: array, short_date?: array, long_date?: array, day_of_week?: array, offset?: array, length?: array, date?: array}|array{_: 'messageEntityDiffInsert', offset?: array, length?: array}|array{_: 'messageEntityDiffReplace', offset?: array, length?: array, old_text?: array}|array{_: 'messageEntityDiffDelete', offset?: array, length?: array}|array{_: 'messageEntityTonAddress', offset?: array, length?: array}|array{_: 'messageEntityBlockquote', offset?: array, length?: array}>, via_bot_name?: string, reply_to_random_id?: int, grouped_id?: int} $message TL-serialization of [DecryptedMessage](https://docs.madelineproto.xyz/API_docs/types/DecryptedMessage.html) type, encrypted with a key that was created during chat initialization @see https://docs.madelineproto.xyz/API_docs/types/DecryptedMessage.html
     * @param bool $silent Send encrypted message without a notification
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.sentEncryptedMessage', date: int}|array{_: 'messages.sentEncryptedFile', date: int, file: array{_: 'encryptedFileEmpty'}|array{_: 'encryptedFile', id: int, access_hash: int, size: int, dc_id: int, key_fingerprint: int}} @see https://docs.madelineproto.xyz/API_docs/types/messages.SentEncryptedMessage.html
     * @psalm-impure
     */
    public function sendEncrypted(array $peer, array $message, bool|null $silent = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Sends a message with a file attachment to a secret chat
     *
     * @param array $peer Secret chat ID @see https://docs.madelineproto.xyz/API_docs/types/InputEncryptedChat.html
     * @param array{_: 'decryptedMessage', media: array{_: 'decryptedMessageMediaEmpty'}|array{_: 'decryptedMessageMediaPhoto', thumb?: array, thumb_w?: array, thumb_h?: array, w?: array, h?: array, size?: array}|array{_: 'decryptedMessageMediaVideo', thumb?: array, thumb_w?: array, thumb_h?: array, duration?: array, w?: array, h?: array, size?: array}|array{_: 'decryptedMessageMediaGeoPoint', lat: array, long: array}|array{_: 'decryptedMessageMediaContact', phone_number?: array, first_name?: array, last_name?: array, user_id?: array}|array{_: 'decryptedMessageMediaDocument', thumb?: array, thumb_w?: array, thumb_h?: array, file_name?: array, mime_type?: array, size?: array}|array{_: 'decryptedMessageMediaAudio', duration?: array, size?: array}|array{_: 'decryptedMessageMediaVideo', thumb?: array, thumb_w?: array, thumb_h?: array, duration?: array, mime_type?: array, w?: array, h?: array, size?: array}|array{_: 'decryptedMessageMediaAudio', duration?: array, mime_type?: array, size?: array}|array{_: 'decryptedMessageMediaExternalDocument', id?: array, access_hash?: array, date?: array, mime_type?: array, size?: array, thumb?: array, dc_id?: array, attributes?: list<array>}|array{_: 'decryptedMessageMediaPhoto', thumb?: array, thumb_w?: array, thumb_h?: array, w?: array, h?: array, size?: array, caption?: array}|array{_: 'decryptedMessageMediaVideo', thumb?: array, thumb_w?: array, thumb_h?: array, duration?: array, mime_type?: array, w?: array, h?: array, size?: array, caption?: array}|array{_: 'decryptedMessageMediaDocument', thumb?: array, thumb_w?: array, thumb_h?: array, mime_type?: array, size?: array, attributes?: list<array>, caption?: array}|array{_: 'decryptedMessageMediaVenue', lat: array, long: array, title?: array, address?: array, provider?: array, venue_id?: array}|array{_: 'decryptedMessageMediaWebPage', url?: array}|array{_: 'decryptedMessageMediaDocument', thumb?: array, thumb_w?: array, thumb_h?: array, mime_type?: array, size?: array, attributes?: list<array>, caption?: array}, message?: string}|array{_: 'decryptedMessageService', action: array{_: 'decryptedMessageActionSetMessageTTL', ttl_seconds?: array}|array{_: 'decryptedMessageActionReadMessages', random_ids?: list<array>}|array{_: 'decryptedMessageActionDeleteMessages', random_ids?: list<array>}|array{_: 'decryptedMessageActionScreenshotMessages', random_ids?: list<array>}|array{_: 'decryptedMessageActionFlushHistory'}|array{_: 'decryptedMessageActionResend', start_seq_no?: array, end_seq_no?: array}|array{_: 'decryptedMessageActionNotifyLayer', layer?: array}|array{_: 'decryptedMessageActionTyping', action: array}|array{_: 'decryptedMessageActionRequestKey', exchange_id?: array, g_a?: array}|array{_: 'decryptedMessageActionAcceptKey', exchange_id?: array, g_b?: array, key_fingerprint?: array}|array{_: 'decryptedMessageActionAbortKey', exchange_id?: array}|array{_: 'decryptedMessageActionCommitKey', exchange_id?: array, key_fingerprint?: array}|array{_: 'decryptedMessageActionNoop'}}|array{_: 'decryptedMessage', media: array{_: 'decryptedMessageMediaEmpty'}|array{_: 'decryptedMessageMediaPhoto', thumb?: array, thumb_w?: array, thumb_h?: array, w?: array, h?: array, size?: array}|array{_: 'decryptedMessageMediaVideo', thumb?: array, thumb_w?: array, thumb_h?: array, duration?: array, w?: array, h?: array, size?: array}|array{_: 'decryptedMessageMediaGeoPoint', lat: array, long: array}|array{_: 'decryptedMessageMediaContact', phone_number?: array, first_name?: array, last_name?: array, user_id?: array}|array{_: 'decryptedMessageMediaDocument', thumb?: array, thumb_w?: array, thumb_h?: array, file_name?: array, mime_type?: array, size?: array}|array{_: 'decryptedMessageMediaAudio', duration?: array, size?: array}|array{_: 'decryptedMessageMediaVideo', thumb?: array, thumb_w?: array, thumb_h?: array, duration?: array, mime_type?: array, w?: array, h?: array, size?: array}|array{_: 'decryptedMessageMediaAudio', duration?: array, mime_type?: array, size?: array}|array{_: 'decryptedMessageMediaExternalDocument', id?: array, access_hash?: array, date?: array, mime_type?: array, size?: array, thumb?: array, dc_id?: array, attributes?: list<array>}|array{_: 'decryptedMessageMediaPhoto', thumb?: array, thumb_w?: array, thumb_h?: array, w?: array, h?: array, size?: array, caption?: array}|array{_: 'decryptedMessageMediaVideo', thumb?: array, thumb_w?: array, thumb_h?: array, duration?: array, mime_type?: array, w?: array, h?: array, size?: array, caption?: array}|array{_: 'decryptedMessageMediaDocument', thumb?: array, thumb_w?: array, thumb_h?: array, mime_type?: array, size?: array, attributes?: list<array>, caption?: array}|array{_: 'decryptedMessageMediaVenue', lat: array, long: array, title?: array, address?: array, provider?: array, venue_id?: array}|array{_: 'decryptedMessageMediaWebPage', url?: array}|array{_: 'decryptedMessageMediaDocument', thumb?: array, thumb_w?: array, thumb_h?: array, mime_type?: array, size?: array, attributes?: list<array>, caption?: array}, ttl?: int, message?: string}|array{_: 'decryptedMessageService', action: array{_: 'decryptedMessageActionSetMessageTTL', ttl_seconds?: array}|array{_: 'decryptedMessageActionReadMessages', random_ids?: list<array>}|array{_: 'decryptedMessageActionDeleteMessages', random_ids?: list<array>}|array{_: 'decryptedMessageActionScreenshotMessages', random_ids?: list<array>}|array{_: 'decryptedMessageActionFlushHistory'}|array{_: 'decryptedMessageActionResend', start_seq_no?: array, end_seq_no?: array}|array{_: 'decryptedMessageActionNotifyLayer', layer?: array}|array{_: 'decryptedMessageActionTyping', action: array}|array{_: 'decryptedMessageActionRequestKey', exchange_id?: array, g_a?: array}|array{_: 'decryptedMessageActionAcceptKey', exchange_id?: array, g_b?: array, key_fingerprint?: array}|array{_: 'decryptedMessageActionAbortKey', exchange_id?: array}|array{_: 'decryptedMessageActionCommitKey', exchange_id?: array, key_fingerprint?: array}|array{_: 'decryptedMessageActionNoop'}}|array{_: 'decryptedMessage', ttl?: int, message?: string, media?: array{_: 'decryptedMessageMediaEmpty'}|array{_: 'decryptedMessageMediaPhoto', thumb?: array, thumb_w?: array, thumb_h?: array, w?: array, h?: array, size?: array}|array{_: 'decryptedMessageMediaVideo', thumb?: array, thumb_w?: array, thumb_h?: array, duration?: array, w?: array, h?: array, size?: array}|array{_: 'decryptedMessageMediaGeoPoint', lat: array, long: array}|array{_: 'decryptedMessageMediaContact', phone_number?: array, first_name?: array, last_name?: array, user_id?: array}|array{_: 'decryptedMessageMediaDocument', thumb?: array, thumb_w?: array, thumb_h?: array, file_name?: array, mime_type?: array, size?: array}|array{_: 'decryptedMessageMediaAudio', duration?: array, size?: array}|array{_: 'decryptedMessageMediaVideo', thumb?: array, thumb_w?: array, thumb_h?: array, duration?: array, mime_type?: array, w?: array, h?: array, size?: array}|array{_: 'decryptedMessageMediaAudio', duration?: array, mime_type?: array, size?: array}|array{_: 'decryptedMessageMediaExternalDocument', id?: array, access_hash?: array, date?: array, mime_type?: array, size?: array, thumb?: array, dc_id?: array, attributes?: list<array>}|array{_: 'decryptedMessageMediaPhoto', thumb?: array, thumb_w?: array, thumb_h?: array, w?: array, h?: array, size?: array, caption?: array}|array{_: 'decryptedMessageMediaVideo', thumb?: array, thumb_w?: array, thumb_h?: array, duration?: array, mime_type?: array, w?: array, h?: array, size?: array, caption?: array}|array{_: 'decryptedMessageMediaDocument', thumb?: array, thumb_w?: array, thumb_h?: array, mime_type?: array, size?: array, attributes?: list<array>, caption?: array}|array{_: 'decryptedMessageMediaVenue', lat: array, long: array, title?: array, address?: array, provider?: array, venue_id?: array}|array{_: 'decryptedMessageMediaWebPage', url?: array}|array{_: 'decryptedMessageMediaDocument', thumb?: array, thumb_w?: array, thumb_h?: array, mime_type?: array, size?: array, attributes?: list<array>, caption?: array}, entities?: list<array{_: 'messageEntityUnknown', offset?: array, length?: array}|array{_: 'messageEntityMention', offset?: array, length?: array}|array{_: 'messageEntityHashtag', offset?: array, length?: array}|array{_: 'messageEntityBotCommand', offset?: array, length?: array}|array{_: 'messageEntityUrl', offset?: array, length?: array}|array{_: 'messageEntityEmail', offset?: array, length?: array}|array{_: 'messageEntityBold', offset?: array, length?: array}|array{_: 'messageEntityItalic', offset?: array, length?: array}|array{_: 'messageEntityCode', offset?: array, length?: array}|array{_: 'messageEntityPre', offset?: array, length?: array, language?: array}|array{_: 'messageEntityTextUrl', offset?: array, length?: array, url?: array}|array{_: 'messageEntityMentionName', offset?: array, length?: array, user_id?: array}|array{_: 'inputMessageEntityMentionName', offset?: array, length?: array, user_id?: array}|array{_: 'messageEntityPhone', offset?: array, length?: array}|array{_: 'messageEntityCashtag', offset?: array, length?: array}|array{_: 'messageEntityUnderline', offset?: array, length?: array}|array{_: 'messageEntityStrike', offset?: array, length?: array}|array{_: 'messageEntityBankCard', offset?: array, length?: array}|array{_: 'messageEntitySpoiler', offset?: array, length?: array}|array{_: 'messageEntityCustomEmoji', offset?: array, length?: array, document_id?: array}|array{_: 'messageEntityBlockquote', collapsed?: array, offset?: array, length?: array}|array{_: 'messageEntityFormattedDate', relative?: array, short_time?: array, long_time?: array, short_date?: array, long_date?: array, day_of_week?: array, offset?: array, length?: array, date?: array}|array{_: 'messageEntityDiffInsert', offset?: array, length?: array}|array{_: 'messageEntityDiffReplace', offset?: array, length?: array, old_text?: array}|array{_: 'messageEntityDiffDelete', offset?: array, length?: array}|array{_: 'messageEntityTonAddress', offset?: array, length?: array}|array{_: 'messageEntityBlockquote', offset?: array, length?: array}>, via_bot_name?: string, reply_to_random_id?: int}|array{_: 'decryptedMessage', no_webpage?: bool, silent?: bool, ttl?: int, message?: string, media?: array{_: 'decryptedMessageMediaEmpty'}|array{_: 'decryptedMessageMediaPhoto', thumb?: array, thumb_w?: array, thumb_h?: array, w?: array, h?: array, size?: array}|array{_: 'decryptedMessageMediaVideo', thumb?: array, thumb_w?: array, thumb_h?: array, duration?: array, w?: array, h?: array, size?: array}|array{_: 'decryptedMessageMediaGeoPoint', lat: array, long: array}|array{_: 'decryptedMessageMediaContact', phone_number?: array, first_name?: array, last_name?: array, user_id?: array}|array{_: 'decryptedMessageMediaDocument', thumb?: array, thumb_w?: array, thumb_h?: array, file_name?: array, mime_type?: array, size?: array}|array{_: 'decryptedMessageMediaAudio', duration?: array, size?: array}|array{_: 'decryptedMessageMediaVideo', thumb?: array, thumb_w?: array, thumb_h?: array, duration?: array, mime_type?: array, w?: array, h?: array, size?: array}|array{_: 'decryptedMessageMediaAudio', duration?: array, mime_type?: array, size?: array}|array{_: 'decryptedMessageMediaExternalDocument', id?: array, access_hash?: array, date?: array, mime_type?: array, size?: array, thumb?: array, dc_id?: array, attributes?: list<array>}|array{_: 'decryptedMessageMediaPhoto', thumb?: array, thumb_w?: array, thumb_h?: array, w?: array, h?: array, size?: array, caption?: array}|array{_: 'decryptedMessageMediaVideo', thumb?: array, thumb_w?: array, thumb_h?: array, duration?: array, mime_type?: array, w?: array, h?: array, size?: array, caption?: array}|array{_: 'decryptedMessageMediaDocument', thumb?: array, thumb_w?: array, thumb_h?: array, mime_type?: array, size?: array, attributes?: list<array>, caption?: array}|array{_: 'decryptedMessageMediaVenue', lat: array, long: array, title?: array, address?: array, provider?: array, venue_id?: array}|array{_: 'decryptedMessageMediaWebPage', url?: array}|array{_: 'decryptedMessageMediaDocument', thumb?: array, thumb_w?: array, thumb_h?: array, mime_type?: array, size?: array, attributes?: list<array>, caption?: array}, entities?: list<array{_: 'messageEntityUnknown', offset?: array, length?: array}|array{_: 'messageEntityMention', offset?: array, length?: array}|array{_: 'messageEntityHashtag', offset?: array, length?: array}|array{_: 'messageEntityBotCommand', offset?: array, length?: array}|array{_: 'messageEntityUrl', offset?: array, length?: array}|array{_: 'messageEntityEmail', offset?: array, length?: array}|array{_: 'messageEntityBold', offset?: array, length?: array}|array{_: 'messageEntityItalic', offset?: array, length?: array}|array{_: 'messageEntityCode', offset?: array, length?: array}|array{_: 'messageEntityPre', offset?: array, length?: array, language?: array}|array{_: 'messageEntityTextUrl', offset?: array, length?: array, url?: array}|array{_: 'messageEntityMentionName', offset?: array, length?: array, user_id?: array}|array{_: 'inputMessageEntityMentionName', offset?: array, length?: array, user_id?: array}|array{_: 'messageEntityPhone', offset?: array, length?: array}|array{_: 'messageEntityCashtag', offset?: array, length?: array}|array{_: 'messageEntityUnderline', offset?: array, length?: array}|array{_: 'messageEntityStrike', offset?: array, length?: array}|array{_: 'messageEntityBankCard', offset?: array, length?: array}|array{_: 'messageEntitySpoiler', offset?: array, length?: array}|array{_: 'messageEntityCustomEmoji', offset?: array, length?: array, document_id?: array}|array{_: 'messageEntityBlockquote', collapsed?: array, offset?: array, length?: array}|array{_: 'messageEntityFormattedDate', relative?: array, short_time?: array, long_time?: array, short_date?: array, long_date?: array, day_of_week?: array, offset?: array, length?: array, date?: array}|array{_: 'messageEntityDiffInsert', offset?: array, length?: array}|array{_: 'messageEntityDiffReplace', offset?: array, length?: array, old_text?: array}|array{_: 'messageEntityDiffDelete', offset?: array, length?: array}|array{_: 'messageEntityTonAddress', offset?: array, length?: array}|array{_: 'messageEntityBlockquote', offset?: array, length?: array}>, via_bot_name?: string, reply_to_random_id?: int, grouped_id?: int} $message TL-serialization of [DecryptedMessage](https://docs.madelineproto.xyz/API_docs/types/DecryptedMessage.html) type, encrypted with a key generated during chat initialization @see https://docs.madelineproto.xyz/API_docs/types/DecryptedMessage.html
     * @param bool $silent Whether to send the file without triggering a notification
     * @param mixed $file File attachment for the secret chat @see https://docs.madelineproto.xyz/API_docs/types/InputEncryptedFile.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.sentEncryptedMessage', date: int}|array{_: 'messages.sentEncryptedFile', date: int, file: array{_: 'encryptedFileEmpty'}|array{_: 'encryptedFile', id: int, access_hash: int, size: int, dc_id: int, key_fingerprint: int}} @see https://docs.madelineproto.xyz/API_docs/types/messages.SentEncryptedMessage.html
     * @psalm-impure
     */
    public function sendEncryptedFile(array $peer, array $message, bool|null $silent = null, mixed $file = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Sends a service message to a secret chat.
     *
     * @param array $peer Secret chat ID @see https://docs.madelineproto.xyz/API_docs/types/InputEncryptedChat.html
     * @param array{_: 'decryptedMessage', media: array{_: 'decryptedMessageMediaEmpty'}|array{_: 'decryptedMessageMediaPhoto', thumb?: array, thumb_w?: array, thumb_h?: array, w?: array, h?: array, size?: array}|array{_: 'decryptedMessageMediaVideo', thumb?: array, thumb_w?: array, thumb_h?: array, duration?: array, w?: array, h?: array, size?: array}|array{_: 'decryptedMessageMediaGeoPoint', lat: array, long: array}|array{_: 'decryptedMessageMediaContact', phone_number?: array, first_name?: array, last_name?: array, user_id?: array}|array{_: 'decryptedMessageMediaDocument', thumb?: array, thumb_w?: array, thumb_h?: array, file_name?: array, mime_type?: array, size?: array}|array{_: 'decryptedMessageMediaAudio', duration?: array, size?: array}|array{_: 'decryptedMessageMediaVideo', thumb?: array, thumb_w?: array, thumb_h?: array, duration?: array, mime_type?: array, w?: array, h?: array, size?: array}|array{_: 'decryptedMessageMediaAudio', duration?: array, mime_type?: array, size?: array}|array{_: 'decryptedMessageMediaExternalDocument', id?: array, access_hash?: array, date?: array, mime_type?: array, size?: array, thumb?: array, dc_id?: array, attributes?: list<array>}|array{_: 'decryptedMessageMediaPhoto', thumb?: array, thumb_w?: array, thumb_h?: array, w?: array, h?: array, size?: array, caption?: array}|array{_: 'decryptedMessageMediaVideo', thumb?: array, thumb_w?: array, thumb_h?: array, duration?: array, mime_type?: array, w?: array, h?: array, size?: array, caption?: array}|array{_: 'decryptedMessageMediaDocument', thumb?: array, thumb_w?: array, thumb_h?: array, mime_type?: array, size?: array, attributes?: list<array>, caption?: array}|array{_: 'decryptedMessageMediaVenue', lat: array, long: array, title?: array, address?: array, provider?: array, venue_id?: array}|array{_: 'decryptedMessageMediaWebPage', url?: array}|array{_: 'decryptedMessageMediaDocument', thumb?: array, thumb_w?: array, thumb_h?: array, mime_type?: array, size?: array, attributes?: list<array>, caption?: array}, message?: string}|array{_: 'decryptedMessageService', action: array{_: 'decryptedMessageActionSetMessageTTL', ttl_seconds?: array}|array{_: 'decryptedMessageActionReadMessages', random_ids?: list<array>}|array{_: 'decryptedMessageActionDeleteMessages', random_ids?: list<array>}|array{_: 'decryptedMessageActionScreenshotMessages', random_ids?: list<array>}|array{_: 'decryptedMessageActionFlushHistory'}|array{_: 'decryptedMessageActionResend', start_seq_no?: array, end_seq_no?: array}|array{_: 'decryptedMessageActionNotifyLayer', layer?: array}|array{_: 'decryptedMessageActionTyping', action: array}|array{_: 'decryptedMessageActionRequestKey', exchange_id?: array, g_a?: array}|array{_: 'decryptedMessageActionAcceptKey', exchange_id?: array, g_b?: array, key_fingerprint?: array}|array{_: 'decryptedMessageActionAbortKey', exchange_id?: array}|array{_: 'decryptedMessageActionCommitKey', exchange_id?: array, key_fingerprint?: array}|array{_: 'decryptedMessageActionNoop'}}|array{_: 'decryptedMessage', media: array{_: 'decryptedMessageMediaEmpty'}|array{_: 'decryptedMessageMediaPhoto', thumb?: array, thumb_w?: array, thumb_h?: array, w?: array, h?: array, size?: array}|array{_: 'decryptedMessageMediaVideo', thumb?: array, thumb_w?: array, thumb_h?: array, duration?: array, w?: array, h?: array, size?: array}|array{_: 'decryptedMessageMediaGeoPoint', lat: array, long: array}|array{_: 'decryptedMessageMediaContact', phone_number?: array, first_name?: array, last_name?: array, user_id?: array}|array{_: 'decryptedMessageMediaDocument', thumb?: array, thumb_w?: array, thumb_h?: array, file_name?: array, mime_type?: array, size?: array}|array{_: 'decryptedMessageMediaAudio', duration?: array, size?: array}|array{_: 'decryptedMessageMediaVideo', thumb?: array, thumb_w?: array, thumb_h?: array, duration?: array, mime_type?: array, w?: array, h?: array, size?: array}|array{_: 'decryptedMessageMediaAudio', duration?: array, mime_type?: array, size?: array}|array{_: 'decryptedMessageMediaExternalDocument', id?: array, access_hash?: array, date?: array, mime_type?: array, size?: array, thumb?: array, dc_id?: array, attributes?: list<array>}|array{_: 'decryptedMessageMediaPhoto', thumb?: array, thumb_w?: array, thumb_h?: array, w?: array, h?: array, size?: array, caption?: array}|array{_: 'decryptedMessageMediaVideo', thumb?: array, thumb_w?: array, thumb_h?: array, duration?: array, mime_type?: array, w?: array, h?: array, size?: array, caption?: array}|array{_: 'decryptedMessageMediaDocument', thumb?: array, thumb_w?: array, thumb_h?: array, mime_type?: array, size?: array, attributes?: list<array>, caption?: array}|array{_: 'decryptedMessageMediaVenue', lat: array, long: array, title?: array, address?: array, provider?: array, venue_id?: array}|array{_: 'decryptedMessageMediaWebPage', url?: array}|array{_: 'decryptedMessageMediaDocument', thumb?: array, thumb_w?: array, thumb_h?: array, mime_type?: array, size?: array, attributes?: list<array>, caption?: array}, ttl?: int, message?: string}|array{_: 'decryptedMessageService', action: array{_: 'decryptedMessageActionSetMessageTTL', ttl_seconds?: array}|array{_: 'decryptedMessageActionReadMessages', random_ids?: list<array>}|array{_: 'decryptedMessageActionDeleteMessages', random_ids?: list<array>}|array{_: 'decryptedMessageActionScreenshotMessages', random_ids?: list<array>}|array{_: 'decryptedMessageActionFlushHistory'}|array{_: 'decryptedMessageActionResend', start_seq_no?: array, end_seq_no?: array}|array{_: 'decryptedMessageActionNotifyLayer', layer?: array}|array{_: 'decryptedMessageActionTyping', action: array}|array{_: 'decryptedMessageActionRequestKey', exchange_id?: array, g_a?: array}|array{_: 'decryptedMessageActionAcceptKey', exchange_id?: array, g_b?: array, key_fingerprint?: array}|array{_: 'decryptedMessageActionAbortKey', exchange_id?: array}|array{_: 'decryptedMessageActionCommitKey', exchange_id?: array, key_fingerprint?: array}|array{_: 'decryptedMessageActionNoop'}}|array{_: 'decryptedMessage', ttl?: int, message?: string, media?: array{_: 'decryptedMessageMediaEmpty'}|array{_: 'decryptedMessageMediaPhoto', thumb?: array, thumb_w?: array, thumb_h?: array, w?: array, h?: array, size?: array}|array{_: 'decryptedMessageMediaVideo', thumb?: array, thumb_w?: array, thumb_h?: array, duration?: array, w?: array, h?: array, size?: array}|array{_: 'decryptedMessageMediaGeoPoint', lat: array, long: array}|array{_: 'decryptedMessageMediaContact', phone_number?: array, first_name?: array, last_name?: array, user_id?: array}|array{_: 'decryptedMessageMediaDocument', thumb?: array, thumb_w?: array, thumb_h?: array, file_name?: array, mime_type?: array, size?: array}|array{_: 'decryptedMessageMediaAudio', duration?: array, size?: array}|array{_: 'decryptedMessageMediaVideo', thumb?: array, thumb_w?: array, thumb_h?: array, duration?: array, mime_type?: array, w?: array, h?: array, size?: array}|array{_: 'decryptedMessageMediaAudio', duration?: array, mime_type?: array, size?: array}|array{_: 'decryptedMessageMediaExternalDocument', id?: array, access_hash?: array, date?: array, mime_type?: array, size?: array, thumb?: array, dc_id?: array, attributes?: list<array>}|array{_: 'decryptedMessageMediaPhoto', thumb?: array, thumb_w?: array, thumb_h?: array, w?: array, h?: array, size?: array, caption?: array}|array{_: 'decryptedMessageMediaVideo', thumb?: array, thumb_w?: array, thumb_h?: array, duration?: array, mime_type?: array, w?: array, h?: array, size?: array, caption?: array}|array{_: 'decryptedMessageMediaDocument', thumb?: array, thumb_w?: array, thumb_h?: array, mime_type?: array, size?: array, attributes?: list<array>, caption?: array}|array{_: 'decryptedMessageMediaVenue', lat: array, long: array, title?: array, address?: array, provider?: array, venue_id?: array}|array{_: 'decryptedMessageMediaWebPage', url?: array}|array{_: 'decryptedMessageMediaDocument', thumb?: array, thumb_w?: array, thumb_h?: array, mime_type?: array, size?: array, attributes?: list<array>, caption?: array}, entities?: list<array{_: 'messageEntityUnknown', offset?: array, length?: array}|array{_: 'messageEntityMention', offset?: array, length?: array}|array{_: 'messageEntityHashtag', offset?: array, length?: array}|array{_: 'messageEntityBotCommand', offset?: array, length?: array}|array{_: 'messageEntityUrl', offset?: array, length?: array}|array{_: 'messageEntityEmail', offset?: array, length?: array}|array{_: 'messageEntityBold', offset?: array, length?: array}|array{_: 'messageEntityItalic', offset?: array, length?: array}|array{_: 'messageEntityCode', offset?: array, length?: array}|array{_: 'messageEntityPre', offset?: array, length?: array, language?: array}|array{_: 'messageEntityTextUrl', offset?: array, length?: array, url?: array}|array{_: 'messageEntityMentionName', offset?: array, length?: array, user_id?: array}|array{_: 'inputMessageEntityMentionName', offset?: array, length?: array, user_id?: array}|array{_: 'messageEntityPhone', offset?: array, length?: array}|array{_: 'messageEntityCashtag', offset?: array, length?: array}|array{_: 'messageEntityUnderline', offset?: array, length?: array}|array{_: 'messageEntityStrike', offset?: array, length?: array}|array{_: 'messageEntityBankCard', offset?: array, length?: array}|array{_: 'messageEntitySpoiler', offset?: array, length?: array}|array{_: 'messageEntityCustomEmoji', offset?: array, length?: array, document_id?: array}|array{_: 'messageEntityBlockquote', collapsed?: array, offset?: array, length?: array}|array{_: 'messageEntityFormattedDate', relative?: array, short_time?: array, long_time?: array, short_date?: array, long_date?: array, day_of_week?: array, offset?: array, length?: array, date?: array}|array{_: 'messageEntityDiffInsert', offset?: array, length?: array}|array{_: 'messageEntityDiffReplace', offset?: array, length?: array, old_text?: array}|array{_: 'messageEntityDiffDelete', offset?: array, length?: array}|array{_: 'messageEntityTonAddress', offset?: array, length?: array}|array{_: 'messageEntityBlockquote', offset?: array, length?: array}>, via_bot_name?: string, reply_to_random_id?: int}|array{_: 'decryptedMessage', no_webpage?: bool, silent?: bool, ttl?: int, message?: string, media?: array{_: 'decryptedMessageMediaEmpty'}|array{_: 'decryptedMessageMediaPhoto', thumb?: array, thumb_w?: array, thumb_h?: array, w?: array, h?: array, size?: array}|array{_: 'decryptedMessageMediaVideo', thumb?: array, thumb_w?: array, thumb_h?: array, duration?: array, w?: array, h?: array, size?: array}|array{_: 'decryptedMessageMediaGeoPoint', lat: array, long: array}|array{_: 'decryptedMessageMediaContact', phone_number?: array, first_name?: array, last_name?: array, user_id?: array}|array{_: 'decryptedMessageMediaDocument', thumb?: array, thumb_w?: array, thumb_h?: array, file_name?: array, mime_type?: array, size?: array}|array{_: 'decryptedMessageMediaAudio', duration?: array, size?: array}|array{_: 'decryptedMessageMediaVideo', thumb?: array, thumb_w?: array, thumb_h?: array, duration?: array, mime_type?: array, w?: array, h?: array, size?: array}|array{_: 'decryptedMessageMediaAudio', duration?: array, mime_type?: array, size?: array}|array{_: 'decryptedMessageMediaExternalDocument', id?: array, access_hash?: array, date?: array, mime_type?: array, size?: array, thumb?: array, dc_id?: array, attributes?: list<array>}|array{_: 'decryptedMessageMediaPhoto', thumb?: array, thumb_w?: array, thumb_h?: array, w?: array, h?: array, size?: array, caption?: array}|array{_: 'decryptedMessageMediaVideo', thumb?: array, thumb_w?: array, thumb_h?: array, duration?: array, mime_type?: array, w?: array, h?: array, size?: array, caption?: array}|array{_: 'decryptedMessageMediaDocument', thumb?: array, thumb_w?: array, thumb_h?: array, mime_type?: array, size?: array, attributes?: list<array>, caption?: array}|array{_: 'decryptedMessageMediaVenue', lat: array, long: array, title?: array, address?: array, provider?: array, venue_id?: array}|array{_: 'decryptedMessageMediaWebPage', url?: array}|array{_: 'decryptedMessageMediaDocument', thumb?: array, thumb_w?: array, thumb_h?: array, mime_type?: array, size?: array, attributes?: list<array>, caption?: array}, entities?: list<array{_: 'messageEntityUnknown', offset?: array, length?: array}|array{_: 'messageEntityMention', offset?: array, length?: array}|array{_: 'messageEntityHashtag', offset?: array, length?: array}|array{_: 'messageEntityBotCommand', offset?: array, length?: array}|array{_: 'messageEntityUrl', offset?: array, length?: array}|array{_: 'messageEntityEmail', offset?: array, length?: array}|array{_: 'messageEntityBold', offset?: array, length?: array}|array{_: 'messageEntityItalic', offset?: array, length?: array}|array{_: 'messageEntityCode', offset?: array, length?: array}|array{_: 'messageEntityPre', offset?: array, length?: array, language?: array}|array{_: 'messageEntityTextUrl', offset?: array, length?: array, url?: array}|array{_: 'messageEntityMentionName', offset?: array, length?: array, user_id?: array}|array{_: 'inputMessageEntityMentionName', offset?: array, length?: array, user_id?: array}|array{_: 'messageEntityPhone', offset?: array, length?: array}|array{_: 'messageEntityCashtag', offset?: array, length?: array}|array{_: 'messageEntityUnderline', offset?: array, length?: array}|array{_: 'messageEntityStrike', offset?: array, length?: array}|array{_: 'messageEntityBankCard', offset?: array, length?: array}|array{_: 'messageEntitySpoiler', offset?: array, length?: array}|array{_: 'messageEntityCustomEmoji', offset?: array, length?: array, document_id?: array}|array{_: 'messageEntityBlockquote', collapsed?: array, offset?: array, length?: array}|array{_: 'messageEntityFormattedDate', relative?: array, short_time?: array, long_time?: array, short_date?: array, long_date?: array, day_of_week?: array, offset?: array, length?: array, date?: array}|array{_: 'messageEntityDiffInsert', offset?: array, length?: array}|array{_: 'messageEntityDiffReplace', offset?: array, length?: array, old_text?: array}|array{_: 'messageEntityDiffDelete', offset?: array, length?: array}|array{_: 'messageEntityTonAddress', offset?: array, length?: array}|array{_: 'messageEntityBlockquote', offset?: array, length?: array}>, via_bot_name?: string, reply_to_random_id?: int, grouped_id?: int} $message TL-serialization of  [DecryptedMessage](https://docs.madelineproto.xyz/API_docs/types/DecryptedMessage.html) type, encrypted with a key generated during chat initialization @see https://docs.madelineproto.xyz/API_docs/types/DecryptedMessage.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.sentEncryptedMessage', date: int}|array{_: 'messages.sentEncryptedFile', date: int, file: array{_: 'encryptedFileEmpty'}|array{_: 'encryptedFile', id: int, access_hash: int, size: int, dc_id: int, key_fingerprint: int}} @see https://docs.madelineproto.xyz/API_docs/types/messages.SentEncryptedMessage.html
     * @psalm-impure
     */
    public function sendEncryptedService(array $peer, array $message, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Report a secret chat for spam
     *
     * @param array $peer The secret chat to report @see https://docs.madelineproto.xyz/API_docs/types/InputEncryptedChat.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function reportEncryptedSpam(array $peer, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Notifies the sender about the recipient having listened a voice message or watched a video, emitting an [updateReadMessagesContents](https://docs.madelineproto.xyz/API_docs/constructors/updateReadMessagesContents.html).
     *
     * @param list<int>|array<never, never> $id Message ID list
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.affectedMessages', pts: int, pts_count: int} @see https://docs.madelineproto.xyz/API_docs/types/messages.AffectedMessages.html
     * @psalm-impure
     */
    public function readMessageContents(array $id = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get stickers by emoji
     *
     * @param string $emoticon The emoji
     * @param list<int|string>|array<never, never> $hash Array of [Hash used for caching, for more info click here](https://core.telegram.org/api/offsets#hash-generation). @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.stickersNotModified'}|array{_: 'messages.stickers', hash: list<int|string>, stickers: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>} @see https://docs.madelineproto.xyz/API_docs/types/messages.Stickers.html
     * @psalm-impure
     */
    public function getStickers(string|null $emoticon = '', array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get all installed stickers
     *
     * @param list<int|string>|array<never, never> $hash Array of [Hash used for caching, for more info click here](https://core.telegram.org/api/offsets#hash-generation). @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.allStickersNotModified'}|array{_: 'messages.allStickers', hash: list<int|string>, sets: list<array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}>} @see https://docs.madelineproto.xyz/API_docs/types/messages.AllStickers.html
     * @psalm-impure
     */
    public function getAllStickers(array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get preview of webpage
     *
     * @param string $message Message from which to extract the preview
     * @param list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}> $entities Array of [Message entities for styled text](https://core.telegram.org/api/entities) @see https://docs.madelineproto.xyz/API_docs/types/MessageEntity.html
     * @param \danog\MadelineProto\ParseMode $parse_mode Whether to parse HTML or Markdown markup in the message
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.webPagePreview', media: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/messages.WebPagePreview.html
     * @psalm-impure
     */
    public function getWebPagePreview(string|null $message = '', array|null $entities = null, \danog\MadelineProto\ParseMode $parse_mode = \danog\MadelineProto\ParseMode::TEXT, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Export an invite link for a chat
     *
     * @param bool $legacy_revoke_permanent Legacy flag, reproducing legacy behavior of this method: if set, revokes all previous links before creating a new one. Kept for bot API BC, should not be used by modern clients.
     * @param bool $request_needed Whether admin confirmation is required before admitting each separate user into the chat
     * @param array|int|string $peer Chat @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $expire_date Expiration date
     * @param int $usage_limit Maximum number of users that can join using this link
     * @param string $title Description of the invite link, visible only to administrators
     * @param array{_: 'starsSubscriptionPricing', period?: int, amount?: int} $subscription_pricing For [Telegram Star subscriptions Â»](https://core.telegram.org/api/stars#star-subscriptions), contains the pricing of the subscription the user must activate to join the private channel. @see https://docs.madelineproto.xyz/API_docs/types/StarsSubscriptionPricing.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'chatInviteExported', revoked: bool, permanent: bool, request_needed: bool, link: string, admin_id: int, date: int, start_date?: int, expire_date?: int, usage_limit?: int, usage?: int, requested?: int, subscription_expired?: int, title?: string, subscription_pricing?: array{_: 'starsSubscriptionPricing', period: int, amount: int}}|array{_: 'chatInvitePublicJoinRequests'} @see https://docs.madelineproto.xyz/API_docs/types/ExportedChatInvite.html
     * @psalm-impure
     */
    public function exportChatInvite(bool|null $legacy_revoke_permanent = null, bool|null $request_needed = null, array|int|string|null $peer = null, int|null $expire_date = null, int|null $usage_limit = null, string|null $title = null, array|null $subscription_pricing = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Check the validity of a chat invite link and get basic info about it
     *
     * @param string $hash Invite hash from [chat invite deep link Â»](https://core.telegram.org/api/links#chat-invite-links).
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'chatInviteAlready', chat: array|int|string}|array{_: 'chatInvite', channel: bool, broadcast: bool, public: bool, megagroup: bool, request_needed: bool, verified: bool, scam: bool, fake: bool, can_refulfill_subscription: bool, title: string, about?: string, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, participants_count: int, participants?: list<array|int|string>, color: int, subscription_pricing?: array{_: 'starsSubscriptionPricing', period: int, amount: int}, subscription_form_id?: int, bot_verification?: array{_: 'botVerification', description: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}, bot_id: int, icon: int}}|array{_: 'chatInvitePeek', chat: array|int|string, expires: int} @see https://docs.madelineproto.xyz/API_docs/types/ChatInvite.html
     * @psalm-impure
     */
    public function checkChatInvite(string|null $hash = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Import a chat invite and join a private chat/supergroup/channel
     *
     * @param string $hash `hash` from a [chat invite deep link](https://core.telegram.org/api/links#chat-invite-links)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.chatInviteJoinResultOk', updates: array}|array{_: 'messages.chatInviteJoinResultWebView', bot_id: int, query_id: int, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/messages.ChatInviteJoinResult.html
     * @psalm-impure
     */
    public function importChatInvite(string|null $hash = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get info about a stickerset
     *
     * @param array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id?: int, access_hash?: int}|array{_: 'inputStickerSetShortName', short_name?: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon?: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'} $stickerset Stickerset @see https://docs.madelineproto.xyz/API_docs/types/InputStickerSet.html
     * @param int $hash 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.stickerSet', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}, packs: list<array{_: 'stickerPack', emoticon: string, documents: list<int>}>, keywords: list<array{_: 'stickerKeyword', document_id: int, keyword: list<string>}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}|array{_: 'messages.stickerSetNotModified'} @see https://docs.madelineproto.xyz/API_docs/types/messages.StickerSet.html
     * @psalm-impure
     */
    public function getStickerSet(array|null $stickerset = null, int|null $hash = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Install a stickerset
     *
     * @param bool $archived Whether to archive stickerset
     * @param array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id?: int, access_hash?: int}|array{_: 'inputStickerSetShortName', short_name?: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon?: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'} $stickerset Stickerset to install @see https://docs.madelineproto.xyz/API_docs/types/InputStickerSet.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.stickerSetInstallResultSuccess'}|array{_: 'messages.stickerSetInstallResultArchive', sets: list<array{_: 'stickerSetCovered', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}, cover: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'stickerSetMultiCovered', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}, covers: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}|array{_: 'stickerSetFullCovered', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}, packs: list<array{_: 'stickerPack', emoticon: string, documents: list<int>}>, keywords: list<array{_: 'stickerKeyword', document_id: int, keyword: list<string>}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}|array{_: 'stickerSetNoCovered', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}}>} @see https://docs.madelineproto.xyz/API_docs/types/messages.StickerSetInstallResult.html
     * @psalm-impure
     */
    public function installStickerSet(bool $archived, array|null $stickerset = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Uninstall a stickerset
     *
     * @param array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id?: int, access_hash?: int}|array{_: 'inputStickerSetShortName', short_name?: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon?: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'} $stickerset The stickerset to uninstall @see https://docs.madelineproto.xyz/API_docs/types/InputStickerSet.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function uninstallStickerSet(array|null $stickerset = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Start a conversation with a bot using a [deep linking parameter](https://core.telegram.org/api/links#bot-links)
     *
     * @param array|int|string $bot The bot @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param array|int|string $peer The chat where to start the bot, can be the bot's private chat or a group @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param string $start_param [Deep linking parameter](https://core.telegram.org/api/links#bot-links)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function startBot(array|int|string|null $bot = null, array|int|string|null $peer = null, string|null $start_param = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get and increase the view counter of a message sent or forwarded from a [channel](https://core.telegram.org/api/channel)
     *
     * @param bool $increment Whether to mark the message as viewed and increment the view counter
     * @param array|int|string $peer Peer where the message was found @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param list<int>|array<never, never> $id ID of message
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.messageViews', views: list<array{_: 'messageViews', views?: int, forwards?: int, replies?: array{_: 'messageReplies', comments: bool, replies: int, replies_pts: int, recent_repliers?: list<array|int|string>, channel_id?: int, max_id?: int, read_max_id?: int}}>, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/messages.MessageViews.html
     * @psalm-impure
     */
    public function getMessagesViews(bool $increment, array|int|string|null $peer = null, array $id = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Make a user admin in a [basic group](https://core.telegram.org/api/channel#basic-groups).
     *
     * @param bool $is_admin Whether to make them admin
     * @param array|int|string $chat_id The ID of the group @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param array|int|string $user_id The user to make admin @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function editChatAdmin(bool $is_admin, array|int|string|null $chat_id = null, array|int|string|null $user_id = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Turn a [basic group into a supergroup](https://core.telegram.org/api/channel#migration)
     *
     * @param array|int|string $chat_id [Basic group](https://core.telegram.org/api/channel#basic-groups) to migrate @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function migrateChat(array|int|string|null $chat_id = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Search for messages and peers globally
     *
     * @param array{_: 'inputMessagesFilterEmpty'}|array{_: 'inputMessagesFilterPhotos'}|array{_: 'inputMessagesFilterVideo'}|array{_: 'inputMessagesFilterPhotoVideo'}|array{_: 'inputMessagesFilterDocument'}|array{_: 'inputMessagesFilterUrl'}|array{_: 'inputMessagesFilterGif'}|array{_: 'inputMessagesFilterVoice'}|array{_: 'inputMessagesFilterMusic'}|array{_: 'inputMessagesFilterChatPhotos'}|array{_: 'inputMessagesFilterPhoneCalls', missed?: bool}|array{_: 'inputMessagesFilterRoundVoice'}|array{_: 'inputMessagesFilterRoundVideo'}|array{_: 'inputMessagesFilterMyMentions'}|array{_: 'inputMessagesFilterGeo'}|array{_: 'inputMessagesFilterContacts'}|array{_: 'inputMessagesFilterPinned'}|array{_: 'inputMessagesFilterPoll'} $filter Global search filter @see https://docs.madelineproto.xyz/API_docs/types/MessagesFilter.html
     * @param bool $broadcasts_only If set, only returns results from channels (used in the [global channel search tab Â»](https://core.telegram.org/api/search#global-search)).
     * @param bool $groups_only Whether to search only in groups
     * @param bool $users_only Whether to search only in private chats
     * @param int $folder_id [Peer folder ID, for more info click here](https://core.telegram.org/api/folders#peer-folders)
     * @param array|int|string $community @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param string $q Query
     * @param int $min_date If a positive value was specified, the method will return only messages with date bigger than min\_date
     * @param int $max_date If a positive value was transferred, the method will return only messages with date smaller than max\_date
     * @param int $offset_rate Initially 0, then set to the [`next_rate` parameter of messages.messagesSlice](https://docs.madelineproto.xyz/API_docs/constructors/messages.messagesSlice.html), or if that is absent, the `date` of the last returned message.
     * @param array|int|string $offset_peer [Offsets for pagination, for more info click here](https://core.telegram.org/api/offsets) @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $offset_id [Offsets for pagination, for more info click here](https://core.telegram.org/api/offsets)
     * @param int $limit [Offsets for pagination, for more info click here](https://core.telegram.org/api/offsets)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.messages', messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.messagesSlice', inexact: array, count: array, next_rate?: array, offset_id_offset?: array, search_flood?: array, messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.channelMessages', inexact: array, pts: array, count: array, offset_id_offset?: array, messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.messagesNotModified', count: array} @see https://docs.madelineproto.xyz/API_docs/types/messages.Messages.html
     * @psalm-impure
     */
    public function searchGlobal(array $filter, bool|null $broadcasts_only = null, bool|null $groups_only = null, bool|null $users_only = null, int|null $folder_id = null, array|int|string|null $community = null, string|null $q = '', int|null $min_date = 0, int|null $max_date = 0, int|null $offset_rate = 0, array|int|string|null $offset_peer = null, int|null $offset_id = 0, int|null $limit = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Reorder installed stickersets
     *
     * @param bool $masks Reorder mask stickersets
     * @param bool $emojis Reorder [custom emoji stickersets](https://core.telegram.org/api/custom-emoji)
     * @param list<int>|array<never, never> $order New stickerset order by stickerset IDs
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function reorderStickerSets(bool|null $masks = null, bool|null $emojis = null, array $order = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Get a document by its SHA256 hash, mainly used for gifs
     *
     * @param string $sha256 SHA256 of file
     * @param int $size Size of the file in bytes
     * @param string $mime_type Mime type
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>} @see https://docs.madelineproto.xyz/API_docs/types/Document.html
     * @psalm-impure
     */
    public function getDocumentByHash(string|null $sha256 = '', int|null $size = 0, string|null $mime_type = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get saved GIFs.
     *
     * @param list<int|string>|array<never, never> $hash Array of [Hash used for caching, for more info click here](https://core.telegram.org/api/offsets#hash-generation). @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.savedGifsNotModified'}|array{_: 'messages.savedGifs', hash: list<int|string>, gifs: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>} @see https://docs.madelineproto.xyz/API_docs/types/messages.SavedGifs.html
     * @psalm-impure
     */
    public function getSavedGifs(array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Add GIF to saved gifs list
     *
     * @param bool $unsave Whether to remove GIF from saved gifs list
     * @param array $id GIF to save @see https://docs.madelineproto.xyz/API_docs/types/InputDocument.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function saveGif(bool $unsave, array|null $id = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Query an inline bot
     *
     * @param array|int|string $bot The bot to query @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param array|int|string $peer The currently opened chat @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: float, long: float, accuracy_radius?: int} $geo_point The geolocation, if requested @see https://docs.madelineproto.xyz/API_docs/types/InputGeoPoint.html
     * @param string $query The query
     * @param string $offset The offset within the results, will be passed directly as-is to the bot.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.botResults', gallery: bool, query_id: int, next_offset?: string, switch_pm?: array{_: 'inlineBotSwitchPM', text: array, start_param: array}, switch_webview?: array{_: 'inlineBotWebView', text: array, url: array}, results: list<array{_: 'botInlineResult', send_message: array, id: array, type: array, title?: array, description?: array, url?: array, thumb?: array, content?: array}|array{_: 'botInlineMediaResult', send_message: array, id: array, type: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, title?: array, description?: array}>, cache_time: int, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/messages.BotResults.html
     * @psalm-impure
     */
    public function getInlineBotResults(array|int|string|null $bot = null, array|int|string|null $peer = null, array|null $geo_point = null, string|null $query = '', string|null $offset = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Answer an inline query, for bots only
     *
     * @param bool $gallery Set this flag if the results are composed of media files
     * @param bool $private Set this flag if results may be cached on the server side only for the user that sent the query. By default, results may be returned to any user who sends the same query
     * @param int $query_id Unique identifier for the answered query
     * @param list<array{_: 'inputBotInlineResult', send_message: array{_: 'inputBotInlineMessageMediaAuto', invert_media?: array, message?: array, entities?: list<array>, reply_markup?: array}|array{_: 'inputBotInlineMessageText', no_webpage?: array, invert_media?: array, message?: array, entities?: list<array>, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaGeo', geo_point?: array, heading?: array, period?: array, proximity_notification_radius?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaVenue', geo_point?: array, title?: array, address?: array, provider?: array, venue_id?: array, venue_type?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaContact', phone_number?: array, first_name?: array, last_name?: array, vcard?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageGame', reply_markup?: array}|array{_: 'inputBotInlineMessageMediaInvoice', invoice: array, provider_data: array, title?: array, description?: array, photo?: array, payload?: array, provider?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaWebPage', invert_media?: array, force_large_media?: array, force_small_media?: array, optional?: array, message?: array, entities?: list<array>, url?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageRichMessage', rich_message: array, reply_markup?: array}, id?: string, type?: string, title?: string, description?: string, url?: string, thumb?: array{_: 'inputWebDocument', url?: array, size?: array, mime_type?: array, attributes?: list<array>}, content?: array{_: 'inputWebDocument', url?: array, size?: array, mime_type?: array, attributes?: list<array>}}|array{_: 'inputBotInlineResultPhoto', send_message: array{_: 'inputBotInlineMessageMediaAuto', invert_media?: array, message?: array, entities?: list<array>, reply_markup?: array}|array{_: 'inputBotInlineMessageText', no_webpage?: array, invert_media?: array, message?: array, entities?: list<array>, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaGeo', geo_point?: array, heading?: array, period?: array, proximity_notification_radius?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaVenue', geo_point?: array, title?: array, address?: array, provider?: array, venue_id?: array, venue_type?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaContact', phone_number?: array, first_name?: array, last_name?: array, vcard?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageGame', reply_markup?: array}|array{_: 'inputBotInlineMessageMediaInvoice', invoice: array, provider_data: array, title?: array, description?: array, photo?: array, payload?: array, provider?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaWebPage', invert_media?: array, force_large_media?: array, force_small_media?: array, optional?: array, message?: array, entities?: list<array>, url?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageRichMessage', rich_message: array, reply_markup?: array}, id?: string, type?: string, photo?: array}|array{_: 'inputBotInlineResultDocument', send_message: array{_: 'inputBotInlineMessageMediaAuto', invert_media?: array, message?: array, entities?: list<array>, reply_markup?: array}|array{_: 'inputBotInlineMessageText', no_webpage?: array, invert_media?: array, message?: array, entities?: list<array>, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaGeo', geo_point?: array, heading?: array, period?: array, proximity_notification_radius?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaVenue', geo_point?: array, title?: array, address?: array, provider?: array, venue_id?: array, venue_type?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaContact', phone_number?: array, first_name?: array, last_name?: array, vcard?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageGame', reply_markup?: array}|array{_: 'inputBotInlineMessageMediaInvoice', invoice: array, provider_data: array, title?: array, description?: array, photo?: array, payload?: array, provider?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaWebPage', invert_media?: array, force_large_media?: array, force_small_media?: array, optional?: array, message?: array, entities?: list<array>, url?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageRichMessage', rich_message: array, reply_markup?: array}, id?: string, type?: string, title?: string, description?: string, document?: array}|array{_: 'inputBotInlineResultGame', send_message: array{_: 'inputBotInlineMessageMediaAuto', invert_media?: array, message?: array, entities?: list<array>, reply_markup?: array}|array{_: 'inputBotInlineMessageText', no_webpage?: array, invert_media?: array, message?: array, entities?: list<array>, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaGeo', geo_point?: array, heading?: array, period?: array, proximity_notification_radius?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaVenue', geo_point?: array, title?: array, address?: array, provider?: array, venue_id?: array, venue_type?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaContact', phone_number?: array, first_name?: array, last_name?: array, vcard?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageGame', reply_markup?: array}|array{_: 'inputBotInlineMessageMediaInvoice', invoice: array, provider_data: array, title?: array, description?: array, photo?: array, payload?: array, provider?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaWebPage', invert_media?: array, force_large_media?: array, force_small_media?: array, optional?: array, message?: array, entities?: list<array>, url?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageRichMessage', rich_message: array, reply_markup?: array}, id?: string, short_name?: string}>|array<never, never> $results Array of Vector of results for the inline query @see https://docs.madelineproto.xyz/API_docs/types/InputBotInlineResult.html
     * @param int $cache_time The maximum amount of time in seconds that the result of the inline query may be cached on the server. Defaults to 300.
     * @param string $next_offset Pass the offset that a client should send in the next query with the same text to receive more results. Pass an empty string if there are no more results or if you don't support pagination. Offset length can't exceed 64 bytes.
     * @param array{_: 'inlineBotSwitchPM', text?: string, start_param?: string} $switch_pm If passed, clients will display a button on top of the remaining inline result list with the specified text, that switches the user to a private chat with the bot and sends the bot a start message with a certain parameter. @see https://docs.madelineproto.xyz/API_docs/types/InlineBotSwitchPM.html
     * @param array{_: 'inlineBotWebView', text?: string, url?: string} $switch_webview If passed, clients will display a button on top of the remaining inline result list with the specified text, that switches the user to the specified [inline mode mini app](https://core.telegram.org/api/bots/webapps#inline-mode-mini-apps). @see https://docs.madelineproto.xyz/API_docs/types/InlineBotWebView.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function setInlineBotResults(bool|null $gallery = null, bool|null $private = null, int|null $query_id = 0, array $results = [], int|null $cache_time = 0, string|null $next_offset = null, array|null $switch_pm = null, array|null $switch_webview = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Send a result obtained using [messages.getInlineBotResults](https://docs.madelineproto.xyz/API_docs/methods/messages.getInlineBotResults.html).
     *
     * @param bool $silent Whether to send the message silently (no notification will be triggered on the other client)
     * @param bool $background Whether to send the message in background
     * @param bool $clear_draft Whether to clear the [draft](https://core.telegram.org/api/drafts)
     * @param bool $hide_via Whether to hide the `via @botname` in the resulting message (only for bot usernames encountered in the [config](https://docs.madelineproto.xyz/API_docs/constructors/config.html))
     * @param array|int|string $peer Destination @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param array{_: 'inputReplyToMessage', reply_to_msg_id?: int, top_msg_id?: int, reply_to_peer_id?: array|int|string, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>, quote_offset?: int, monoforum_peer_id?: array|int|string, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, url?: string, webpage_id?: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, email?: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, phone?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, name?: string}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, user_id?: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, date?: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, type: array{_: 'inlineButtonTypeUrl', url?: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url?: string, button_id?: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access?: bool, fwd_text?: string, url?: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url?: string}|array{_: 'inlineButtonTypeCallback', requires_password?: bool, data?: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer?: bool, query?: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id?: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id?: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text?: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary?: bool, bg_danger?: bool, bg_success?: bool, link?: bool}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}>}|array{_: 'inputReplyToStory', peer?: array|int|string, story_id?: int}|array{_: 'inputReplyToMonoForum', monoforum_peer_id?: array|int|string}|array{_: 'inputReplyToEphemeralMessage', id?: int} $reply_to If set, indicates that the message should be sent in reply to the specified message or story. @see https://docs.madelineproto.xyz/API_docs/types/InputReplyTo.html
     * @param int $query_id Query ID from [messages.getInlineBotResults](https://docs.madelineproto.xyz/API_docs/methods/messages.getInlineBotResults.html)
     * @param string $id Result ID from [messages.getInlineBotResults](https://docs.madelineproto.xyz/API_docs/methods/messages.getInlineBotResults.html)
     * @param int $schedule_date Scheduled message date for scheduled messages
     * @param array|int|string $send_as Send this message as the specified peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param array{_: 'inputQuickReplyShortcut', shortcut?: string}|array{_: 'inputQuickReplyShortcutId', shortcut_id?: int} $quick_reply_shortcut Add the message to the specified [quick reply shortcut Â»](https://core.telegram.org/api/business#quick-reply-shortcuts), instead. @see https://docs.madelineproto.xyz/API_docs/types/InputQuickReplyShortcut.html
     * @param int $allow_paid_stars For [paid messages Â»](https://core.telegram.org/api/paid-messages), specifies the amount of [Telegram Stars](https://core.telegram.org/api/stars) the user has agreed to pay in order to send the message.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function sendInlineBotResult(bool|null $silent = null, bool|null $background = null, bool|null $clear_draft = null, bool|null $hide_via = null, array|int|string|null $peer = null, array|null $reply_to = null, int|null $query_id = 0, string|null $id = '', int|null $schedule_date = null, array|int|string|null $send_as = null, array|null $quick_reply_shortcut = null, int|null $allow_paid_stars = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Find out if a media message's caption can be edited
     *
     * @param array|int|string $peer Peer where the media was sent @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $id ID of message
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.messageEditData', caption: bool} @see https://docs.madelineproto.xyz/API_docs/types/messages.MessageEditData.html
     * @psalm-impure
     */
    public function getMessageEditData(array|int|string|null $peer = null, int|null $id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Edit message
     *
     * @param bool $no_webpage Disable webpage preview
     * @param bool $invert_media If set, any eventual webpage preview will be shown on top of the message instead of at the bottom.
     * @param array|int|string $peer Where was the message sent @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $id ID of the message to edit
     * @param string $message New message
     * @param \danog\MadelineProto\EventHandler\Media|string|array $media New attached media @see https://docs.madelineproto.xyz/API_docs/types/InputMedia.html
     * @param array $reply_markup Reply markup for inline keyboards @see https://docs.madelineproto.xyz/API_docs/types/ReplyMarkup.html
     * @param list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}> $entities Array of [Message entities for styled text](https://core.telegram.org/api/entities) @see https://docs.madelineproto.xyz/API_docs/types/MessageEntity.html
     * @param \danog\MadelineProto\ParseMode $parse_mode Whether to parse HTML or Markdown markup in the message
     * @param int $schedule_date Scheduled message date for [scheduled messages](https://core.telegram.org/api/scheduled-messages)
     * @param int $schedule_repeat_period 
     * @param int $quick_reply_shortcut_id If specified, edits a [quick reply shortcut message, instead Â»](https://core.telegram.org/api/business#quick-reply-shortcuts).
     * @param array{_: 'inputRichMessage', rtl?: bool, noautolink?: bool, blocks?: list<array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, published_date?: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, language?: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name?: string}|array{_: 'pageBlockList', items?: list<array{_: 'pageListItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, checkbox?: bool, checked?: bool}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, collapsed?: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, spoiler?: bool, photo_id?: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, autoplay?: bool, loop?: bool, spoiler?: bool, video_id?: int}|array{_: 'pageBlockCover', cover: array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, published_date?: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, language?: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name?: string}|array{_: 'pageBlockList', items?: list<array{_: 'pageListItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, checkbox?: bool, checked?: bool}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, collapsed?: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, spoiler?: bool, photo_id?: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, autoplay?: bool, loop?: bool, spoiler?: bool, video_id?: int}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, full_width?: bool, allow_scrolling?: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel?: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, audio_id?: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, bordered?: bool, striped?: bool, compact?: bool, rows?: list<array{_: 'pageTableRow', cells?: list<array{_: 'pageTableCell', header?: bool, align_center?: bool, align_right?: bool, valign_middle?: bool, valign_bottom?: bool, text?: array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textConcat', texts?: list<array>}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}, colspan?: int, rowspan?: int}>}>}|array{_: 'pageBlockOrderedList', reversed?: bool, items?: list<array{_: 'pageListOrderedItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, checkbox?: bool, checked?: bool, num?: string, value?: int, type?: string}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, articles?: list<array{_: 'pageRelatedArticle', url?: string, webpage_id?: int, title?: string, description?: string, photo_id?: int, author?: string, published_date?: int}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, geo?: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash?: int, accuracy_radius?: int}, zoom?: int, w?: int, h?: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockMath', source?: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, geo?: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: float, long: float, accuracy_radius?: int}, zoom?: int, w?: int, h?: int}|array{_: 'pageBlockButtonRow', align_left?: bool, align_center?: bool, align_right?: bool, buttons?: list<array{_: 'pageButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, type: array{_: 'inlineButtonTypeUrl', url?: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url?: string, button_id?: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access?: bool, fwd_text?: string, url?: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url?: string}|array{_: 'inlineButtonTypeCallback', requires_password?: bool, data?: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer?: bool, query?: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id?: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id?: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text?: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary?: bool, bg_danger?: bool, bg_success?: bool, link?: bool}}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, document_id?: int}}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, full_width?: bool, allow_scrolling?: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel?: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, audio_id?: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, bordered?: bool, striped?: bool, compact?: bool, rows?: list<array{_: 'pageTableRow', cells?: list<array{_: 'pageTableCell', header?: bool, align_center?: bool, align_right?: bool, valign_middle?: bool, valign_bottom?: bool, text?: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, colspan?: int, rowspan?: int}>}>}|array{_: 'pageBlockOrderedList', reversed?: bool, items?: list<array{_: 'pageListOrderedItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, checkbox?: bool, checked?: bool, num?: string, value?: int, type?: string}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, articles?: list<array{_: 'pageRelatedArticle', url?: string, webpage_id?: int, title?: string, description?: string, photo_id?: int, author?: string, published_date?: int}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, geo?: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash?: int, accuracy_radius?: int}, zoom?: int, w?: int, h?: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockMath', source?: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, geo?: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: float, long: float, accuracy_radius?: int}, zoom?: int, w?: int, h?: int}|array{_: 'pageBlockButtonRow', align_left?: bool, align_center?: bool, align_right?: bool, buttons?: list<array{_: 'pageButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, type: array{_: 'inlineButtonTypeUrl', url?: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url?: string, button_id?: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access?: bool, fwd_text?: string, url?: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url?: string}|array{_: 'inlineButtonTypeCallback', requires_password?: bool, data?: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer?: bool, query?: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id?: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id?: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text?: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary?: bool, bg_danger?: bool, bg_success?: bool, link?: bool}}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, document_id?: int}>, photos?: list<array>, documents?: list<array>, users?: list<array|int|string>}|array{_: 'inputRichMessageHTML', rtl?: bool, noautolink?: bool, html?: string, files?: list<array{_: 'inputRichFilePhoto', id?: string, photo?: array}|array{_: 'inputRichFileDocument', id?: string, document?: array}>}|array{_: 'inputRichMessageMarkdown', rtl?: bool, noautolink?: bool, markdown?: string, files?: list<array{_: 'inputRichFilePhoto', id?: string, photo?: array}|array{_: 'inputRichFileDocument', id?: string, document?: array}>} $rich_message @see https://docs.madelineproto.xyz/API_docs/types/InputRichMessage.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @param ?string $businessConnectionId Business connection ID, received through an updateBotBusinessConnect update.
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function editMessage(bool|null $no_webpage = null, bool|null $invert_media = null, array|int|string|null $peer = null, int|null $id = 0, string|null $message = null, \danog\MadelineProto\EventHandler\Media|array|string|null $media = null, array|null $reply_markup = null, array|null $entities = null, \danog\MadelineProto\ParseMode $parse_mode = \danog\MadelineProto\ParseMode::TEXT, int|null $schedule_date = null, int|null $schedule_repeat_period = null, int|null $quick_reply_shortcut_id = null, array|null $rich_message = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null, ?string $businessConnectionId = null): array;

    /**
     * Edit an inline bot message
     *
     * @param array{_: 'inputBotInlineMessageID', dc_id?: int, id?: int, access_hash?: int}|array{_: 'inputBotInlineMessageID64', dc_id?: int, owner_id?: int, id?: int, access_hash?: int} $id Sent inline message ID @see https://docs.madelineproto.xyz/API_docs/types/InputBotInlineMessageID.html
     * @param bool $no_webpage Disable webpage preview
     * @param bool $invert_media If set, any eventual webpage preview will be shown on top of the message instead of at the bottom.
     * @param string $message Message
     * @param \danog\MadelineProto\EventHandler\Media|string|array $media Media @see https://docs.madelineproto.xyz/API_docs/types/InputMedia.html
     * @param array $reply_markup Reply markup for inline keyboards @see https://docs.madelineproto.xyz/API_docs/types/ReplyMarkup.html
     * @param list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}> $entities Array of [Message entities for styled text](https://core.telegram.org/api/entities) @see https://docs.madelineproto.xyz/API_docs/types/MessageEntity.html
     * @param \danog\MadelineProto\ParseMode $parse_mode Whether to parse HTML or Markdown markup in the message
     * @param array{_: 'inputRichMessage', rtl?: bool, noautolink?: bool, blocks?: list<array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, published_date?: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, language?: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name?: string}|array{_: 'pageBlockList', items?: list<array{_: 'pageListItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, checkbox?: bool, checked?: bool}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, collapsed?: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, spoiler?: bool, photo_id?: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, autoplay?: bool, loop?: bool, spoiler?: bool, video_id?: int}|array{_: 'pageBlockCover', cover: array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, published_date?: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, language?: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name?: string}|array{_: 'pageBlockList', items?: list<array{_: 'pageListItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, checkbox?: bool, checked?: bool}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, collapsed?: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, spoiler?: bool, photo_id?: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, autoplay?: bool, loop?: bool, spoiler?: bool, video_id?: int}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, full_width?: bool, allow_scrolling?: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel?: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, audio_id?: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, bordered?: bool, striped?: bool, compact?: bool, rows?: list<array{_: 'pageTableRow', cells?: list<array{_: 'pageTableCell', header?: bool, align_center?: bool, align_right?: bool, valign_middle?: bool, valign_bottom?: bool, text?: array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textConcat', texts?: list<array>}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}, colspan?: int, rowspan?: int}>}>}|array{_: 'pageBlockOrderedList', reversed?: bool, items?: list<array{_: 'pageListOrderedItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, checkbox?: bool, checked?: bool, num?: string, value?: int, type?: string}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, articles?: list<array{_: 'pageRelatedArticle', url?: string, webpage_id?: int, title?: string, description?: string, photo_id?: int, author?: string, published_date?: int}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, geo?: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash?: int, accuracy_radius?: int}, zoom?: int, w?: int, h?: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockMath', source?: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, geo?: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: float, long: float, accuracy_radius?: int}, zoom?: int, w?: int, h?: int}|array{_: 'pageBlockButtonRow', align_left?: bool, align_center?: bool, align_right?: bool, buttons?: list<array{_: 'pageButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, type: array{_: 'inlineButtonTypeUrl', url?: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url?: string, button_id?: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access?: bool, fwd_text?: string, url?: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url?: string}|array{_: 'inlineButtonTypeCallback', requires_password?: bool, data?: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer?: bool, query?: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id?: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id?: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text?: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary?: bool, bg_danger?: bool, bg_success?: bool, link?: bool}}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, document_id?: int}}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, full_width?: bool, allow_scrolling?: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel?: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, audio_id?: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, bordered?: bool, striped?: bool, compact?: bool, rows?: list<array{_: 'pageTableRow', cells?: list<array{_: 'pageTableCell', header?: bool, align_center?: bool, align_right?: bool, valign_middle?: bool, valign_bottom?: bool, text?: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, colspan?: int, rowspan?: int}>}>}|array{_: 'pageBlockOrderedList', reversed?: bool, items?: list<array{_: 'pageListOrderedItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, checkbox?: bool, checked?: bool, num?: string, value?: int, type?: string}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, articles?: list<array{_: 'pageRelatedArticle', url?: string, webpage_id?: int, title?: string, description?: string, photo_id?: int, author?: string, published_date?: int}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, geo?: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash?: int, accuracy_radius?: int}, zoom?: int, w?: int, h?: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockMath', source?: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, geo?: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: float, long: float, accuracy_radius?: int}, zoom?: int, w?: int, h?: int}|array{_: 'pageBlockButtonRow', align_left?: bool, align_center?: bool, align_right?: bool, buttons?: list<array{_: 'pageButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, type: array{_: 'inlineButtonTypeUrl', url?: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url?: string, button_id?: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access?: bool, fwd_text?: string, url?: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url?: string}|array{_: 'inlineButtonTypeCallback', requires_password?: bool, data?: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer?: bool, query?: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id?: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id?: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text?: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary?: bool, bg_danger?: bool, bg_success?: bool, link?: bool}}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, document_id?: int}>, photos?: list<array>, documents?: list<array>, users?: list<array|int|string>}|array{_: 'inputRichMessageHTML', rtl?: bool, noautolink?: bool, html?: string, files?: list<array{_: 'inputRichFilePhoto', id?: string, photo?: array}|array{_: 'inputRichFileDocument', id?: string, document?: array}>}|array{_: 'inputRichMessageMarkdown', rtl?: bool, noautolink?: bool, markdown?: string, files?: list<array{_: 'inputRichFilePhoto', id?: string, photo?: array}|array{_: 'inputRichFileDocument', id?: string, document?: array}>} $rich_message @see https://docs.madelineproto.xyz/API_docs/types/InputRichMessage.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function editInlineBotMessage(array $id, bool|null $no_webpage = null, bool|null $invert_media = null, string|null $message = null, \danog\MadelineProto\EventHandler\Media|array|string|null $media = null, array|null $reply_markup = null, array|null $entities = null, \danog\MadelineProto\ParseMode $parse_mode = \danog\MadelineProto\ParseMode::TEXT, array|null $rich_message = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Press an inline callback button and get a callback answer from the bot
     *
     * @param bool $game Whether this is a "play game" button
     * @param array|int|string $peer Where was the inline keyboard sent @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $msg_id ID of the Message with the inline keyboard
     * @param string $data Callback data
     * @param string|array $password For buttons [requiring you to verify your identity with your 2FA password](https://docs.madelineproto.xyz/API_docs/constructors/keyboardButtonCallback.html), the SRP payload generated using [SRP](https://core.telegram.org/api/srp). @see https://docs.madelineproto.xyz/API_docs/types/InputCheckPasswordSRP.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.botCallbackAnswer', alert: bool, has_url: bool, native_ui: bool, message?: string, url?: string, cache_time: int} @see https://docs.madelineproto.xyz/API_docs/types/messages.BotCallbackAnswer.html
     * @psalm-impure
     */
    public function getBotCallbackAnswer(bool|null $game = null, array|int|string|null $peer = null, int|null $msg_id = 0, string|null $data = null, string|array|null $password = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Set the callback answer to a user button press (bots only)
     *
     * @param bool $alert Whether to show the message as a popup instead of a toast notification
     * @param int $query_id Query ID
     * @param string $message Popup to show
     * @param string $url URL to open
     * @param int $cache_time Cache validity
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function setBotCallbackAnswer(bool|null $alert = null, int|null $query_id = 0, string|null $message = null, string|null $url = null, int|null $cache_time = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Get dialog info of specified peers
     *
     * @param list<array{_: 'inputDialogPeer', peer?: array|int|string}|array{_: 'inputDialogPeerFolder', folder_id?: int}|array{_: 'inputDialogPeerCommunity', community?: array|int|string}>|array<never, never> $peers Array of Peers @see https://docs.madelineproto.xyz/API_docs/types/InputDialogPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.peerDialogs', state: array{_: 'updates.state', pts: int, qts: int, date: int, seq: int, unread_count: int}, dialogs: list<array{_: 'dialog', peer: array|int|string, notify_settings: array{_: 'peerNotifySettings', show_previews?: bool, silent?: bool, mute_until?: int, ios_sound?: array{_: 'notificationSoundDefault'}|array{_: 'notificationSoundNone'}|array{_: 'notificationSoundLocal', title: string, data: string}|array{_: 'notificationSoundRingtone', id: int}, android_sound?: array{_: 'notificationSoundDefault'}|array{_: 'notificationSoundNone'}|array{_: 'notificationSoundLocal', title: string, data: string}|array{_: 'notificationSoundRingtone', id: int}, other_sound?: array{_: 'notificationSoundDefault'}|array{_: 'notificationSoundNone'}|array{_: 'notificationSoundLocal', title: string, data: string}|array{_: 'notificationSoundRingtone', id: int}, stories_muted?: bool, stories_hide_sender?: bool, stories_ios_sound?: array{_: 'notificationSoundDefault'}|array{_: 'notificationSoundNone'}|array{_: 'notificationSoundLocal', title: string, data: string}|array{_: 'notificationSoundRingtone', id: int}, stories_android_sound?: array{_: 'notificationSoundDefault'}|array{_: 'notificationSoundNone'}|array{_: 'notificationSoundLocal', title: string, data: string}|array{_: 'notificationSoundRingtone', id: int}, stories_other_sound?: array{_: 'notificationSoundDefault'}|array{_: 'notificationSoundNone'}|array{_: 'notificationSoundLocal', title: string, data: string}|array{_: 'notificationSoundRingtone', id: int}}, pinned: bool, unread_mark: bool, view_forum_as_messages: bool, top_message: int, read_inbox_max_id: int, read_outbox_max_id: int, unread_count: int, unread_mentions_count: int, unread_reactions_count: int, unread_poll_votes_count: int, pts?: int, draft?: array{_: 'draftMessageEmpty', date?: int}|array{_: 'draftMessage', no_webpage: bool, invert_media: bool, reply_to?: array{_: 'inputReplyToMessage', reply_to_msg_id: int, top_msg_id?: int, reply_to_peer_id?: array|int|string, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, quote_offset?: int, monoforum_peer_id?: array|int|string, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, url: string, webpage_id: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, email: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, phone: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, name: string}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, user_id: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, date: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, type: array{_: 'inlineButtonTypeUrl', url: array}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: array, url: array, button_id: array}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: array, fwd_text?: array, url: array, bot?: array}|array{_: 'inlineButtonTypeWebView', url: array}|array{_: 'inlineButtonTypeCallback', requires_password: array, data: array}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: array, query: array, peer_types?: list<array>}|array{_: 'inlineButtonTypeUserProfile', user_id: array}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array}|array{_: 'inlineButtonTypeCopy', copy_text: array}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: array, bg_danger: array, bg_success: array, link: array}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}>}|array{_: 'inputReplyToStory', peer: array|int|string, story_id: int}|array{_: 'inputReplyToMonoForum', monoforum_peer_id: array|int|string}|array{_: 'inputReplyToEphemeralMessage', id: int}, message: string, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, media?: \danog\MadelineProto\EventHandler\Media|string|array, date: int, effect?: int, suggested_post?: array{_: 'suggestedPost', accepted: bool, rejected: bool, price?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, schedule_date?: int}, rich_message?: array{_: 'richMessage', rtl: bool, part: bool, blocks: list<array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, published_date: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, language: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: string}|array{_: 'pageBlockList', items: list<array{_: 'pageListItemText', text: array, checkbox: array, checked: array}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, collapsed: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array, credit: array}, spoiler: bool, photo_id: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array, credit: array}, autoplay: bool, loop: bool, spoiler: bool, video_id: int}|array{_: 'pageBlockCover', cover: array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array}|array{_: 'pageBlockSubtitle', text: array}|array{_: 'pageBlockAuthorDate', author: array, published_date: array}|array{_: 'pageBlockHeader', text: array}|array{_: 'pageBlockSubheader', text: array}|array{_: 'pageBlockParagraph', text: array}|array{_: 'pageBlockPreformatted', text: array, language: array}|array{_: 'pageBlockFooter', text: array}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: array}|array{_: 'pageBlockList', items: list<array>}|array{_: 'pageBlockBlockquote', text: array, caption: array, collapsed: array}|array{_: 'pageBlockPullquote', text: array, caption: array}|array{_: 'pageBlockPhoto', caption: array, spoiler: array, photo_id: array, url?: array, webpage_id?: array}|array{_: 'pageBlockVideo', caption: array, autoplay: array, loop: array, spoiler: array, video_id: array}|array{_: 'pageBlockEmbed', caption: array, full_width: array, allow_scrolling: array, url?: array, html?: array, poster_photo_id?: array, w?: array, h?: array}|array{_: 'pageBlockChannel', channel: array}|array{_: 'pageBlockAudio', caption: array, audio_id: array}|array{_: 'pageBlockKicker', text: array}|array{_: 'pageBlockTable', title: array, bordered: array, striped: array, compact: array, rows: list<array>}|array{_: 'pageBlockOrderedList', reversed: array, items: list<array>, start?: array, type?: array}|array{_: 'pageBlockRelatedArticles', title: array, articles: list<array>}|array{_: 'pageBlockMap', caption: array, geo: array, zoom: array, w: array, h: array}|array{_: 'pageBlockHeading1', text: array}|array{_: 'pageBlockHeading2', text: array}|array{_: 'pageBlockHeading3', text: array}|array{_: 'pageBlockHeading4', text: array}|array{_: 'pageBlockHeading5', text: array}|array{_: 'pageBlockHeading6', text: array}|array{_: 'pageBlockMath', source: array}|array{_: 'pageBlockThinking', text: array}|array{_: 'inputPageBlockMap', caption: array, geo: array, zoom: array, w: array, h: array}|array{_: 'pageBlockButtonRow', align_left: array, align_center: array, align_right: array, buttons: list<array>}|array{_: 'pageBlockDocument', caption: array, document_id: array}}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array, credit: array}, full_width: bool, allow_scrolling: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array, credit: array}, audio_id: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, bordered: bool, striped: bool, compact: bool, rows: list<array{_: 'pageTableRow', cells: list<array>}>}|array{_: 'pageBlockOrderedList', reversed: bool, items: list<array{_: 'pageListOrderedItemText', text: array, checkbox: array, checked: array, num?: array, value?: array, type?: array}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, articles: list<array{_: 'pageRelatedArticle', url: array, webpage_id: array, title?: array, description?: array, photo_id?: array, author?: array, published_date?: array}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockMath', source: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: array, long: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockButtonRow', align_left: bool, align_center: bool, align_right: bool, buttons: list<array{_: 'pageButton', text: array, type: array, style?: array}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array, credit: array}, document_id: int}>, photos: list<array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}}, folder_id?: int, ttl_period?: int}|array{_: 'dialogFolder', folder: array{_: 'folder', autofill_new_broadcasts: bool, autofill_public_groups: bool, autofill_new_correspondents: bool, id: int, title: string, photo?: array{_: 'chatPhotoEmpty'}|array{_: 'chatPhoto', has_video: bool, photo_id: int, stripped_thumb?: string, dc_id: int}}, peer: array|int|string, pinned: bool, top_message: int, unread_muted_peers_count: int, unread_unmuted_peers_count: int, unread_muted_messages_count: int, unread_unmuted_messages_count: int}|array{_: 'dialogCommunity', notify_settings: array{_: 'peerNotifySettings', show_previews?: bool, silent?: bool, mute_until?: int, ios_sound?: array{_: 'notificationSoundDefault'}|array{_: 'notificationSoundNone'}|array{_: 'notificationSoundLocal', title: string, data: string}|array{_: 'notificationSoundRingtone', id: int}, android_sound?: array{_: 'notificationSoundDefault'}|array{_: 'notificationSoundNone'}|array{_: 'notificationSoundLocal', title: string, data: string}|array{_: 'notificationSoundRingtone', id: int}, other_sound?: array{_: 'notificationSoundDefault'}|array{_: 'notificationSoundNone'}|array{_: 'notificationSoundLocal', title: string, data: string}|array{_: 'notificationSoundRingtone', id: int}, stories_muted?: bool, stories_hide_sender?: bool, stories_ios_sound?: array{_: 'notificationSoundDefault'}|array{_: 'notificationSoundNone'}|array{_: 'notificationSoundLocal', title: string, data: string}|array{_: 'notificationSoundRingtone', id: int}, stories_android_sound?: array{_: 'notificationSoundDefault'}|array{_: 'notificationSoundNone'}|array{_: 'notificationSoundLocal', title: string, data: string}|array{_: 'notificationSoundRingtone', id: int}, stories_other_sound?: array{_: 'notificationSoundDefault'}|array{_: 'notificationSoundNone'}|array{_: 'notificationSoundLocal', title: string, data: string}|array{_: 'notificationSoundRingtone', id: int}}, pinned: bool, community_id: int}>, messages: list<array{_: 'messageEmpty', id: int, peer_id?: array|int|string}|array{_: 'message', peer_id: array|int|string, out: bool, mentioned: bool, media_unread: bool, silent: bool, post: bool, from_scheduled: bool, legacy: bool, edit_hide: bool, pinned: bool, noforwards: bool, invert_media: bool, offline: bool, video_processing_pending: bool, paid_suggested_post_stars: bool, paid_suggested_post_ton: bool, id: int, from_id?: array|int|string, from_boosts_applied?: int, from_rank?: string, saved_peer_id?: array|int|string, fwd_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, via_bot_id?: int, via_business_bot_id?: int, guestchat_via_from?: array|int|string, reply_to?: array{_: 'messageReplyHeader', reply_to_scheduled: bool, forum_topic: bool, quote: bool, reply_to_ephemeral: bool, reply_to_msg_id?: int, reply_to_peer_id?: array|int|string, reply_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, reply_media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_to_top_id?: int, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, quote_offset?: int, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, url: string, webpage_id: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, email: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, phone: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, name: string}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, user_id: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, date: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, type: array{_: 'inlineButtonTypeUrl', url: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url: string, button_id: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: bool, fwd_text?: string, url: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url: string}|array{_: 'inlineButtonTypeCallback', requires_password: bool, data: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: bool, query: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: bool, bg_danger: bool, bg_success: bool, link: bool}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}>}|array{_: 'messageReplyStoryHeader', peer: array|int|string, story_id: int}, date: int, message: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_markup?: array, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, views?: int, forwards?: int, replies?: array{_: 'messageReplies', comments: bool, replies: int, replies_pts: int, recent_repliers?: list<array|int|string>, channel_id?: int, max_id?: int, read_max_id?: int}, edit_date?: int, post_author?: string, grouped_id?: int, reactions?: array{_: 'messageReactions', min: bool, can_see_list: bool, reactions_as_tags: bool, results: list<array{_: 'reactionCount', chosen_order?: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}, count: int}>, recent_reactions?: list<array{_: 'messagePeerReaction', peer_id: array|int|string, big: bool, unread: bool, my: bool, date: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}}>, top_reactors?: list<array{_: 'messageReactor', top: bool, my: bool, anonymous: bool, peer_id?: array|int|string, count: int}>}, restriction_reason?: list<array{_: 'restrictionReason', platform: string, reason: string, text: string}>, ttl_period?: int, quick_reply_shortcut_id?: int, effect?: int, factcheck?: array{_: 'factCheck', need_check: bool, country?: string, text?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}, hash: list<int|string>}, report_delivery_until_date?: int, paid_message_stars?: int, suggested_post?: array{_: 'suggestedPost', accepted: bool, rejected: bool, price?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, schedule_date?: int}, schedule_repeat_period?: int, summary_from_language?: string, rich_message?: array{_: 'richMessage', rtl: bool, part: bool, blocks: list<array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, published_date: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, language: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: string}|array{_: 'pageBlockList', items: list<array{_: 'pageListItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, checkbox: bool, checked: bool}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, collapsed: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, spoiler: bool, photo_id: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, autoplay: bool, loop: bool, spoiler: bool, video_id: int}|array{_: 'pageBlockCover', cover: array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, published_date: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, language: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: string}|array{_: 'pageBlockList', items: list<array{_: 'pageListItemText', text: array, checkbox: array, checked: array}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, collapsed: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array, credit: array}, spoiler: bool, photo_id: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array, credit: array}, autoplay: bool, loop: bool, spoiler: bool, video_id: int}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array, credit: array}, full_width: bool, allow_scrolling: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array, credit: array}, audio_id: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, bordered: bool, striped: bool, compact: bool, rows: list<array{_: 'pageTableRow', cells: list<array>}>}|array{_: 'pageBlockOrderedList', reversed: bool, items: list<array{_: 'pageListOrderedItemText', text: array, checkbox: array, checked: array, num?: array, value?: array, type?: array}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, articles: list<array{_: 'pageRelatedArticle', url: array, webpage_id: array, title?: array, description?: array, photo_id?: array, author?: array, published_date?: array}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockMath', source: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: array, long: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockButtonRow', align_left: bool, align_center: bool, align_right: bool, buttons: list<array{_: 'pageButton', text: array, type: array, style?: array}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array, credit: array}, document_id: int}}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, full_width: bool, allow_scrolling: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, audio_id: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, bordered: bool, striped: bool, compact: bool, rows: list<array{_: 'pageTableRow', cells: list<array{_: 'pageTableCell', header: array, align_center: array, align_right: array, valign_middle: array, valign_bottom: array, text?: array, colspan?: array, rowspan?: array}>}>}|array{_: 'pageBlockOrderedList', reversed: bool, items: list<array{_: 'pageListOrderedItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, checkbox: bool, checked: bool, num?: string, value?: int, type?: string}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, articles: list<array{_: 'pageRelatedArticle', url: string, webpage_id: int, title?: string, description?: string, photo_id?: int, author?: string, published_date?: int}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash: int, accuracy_radius?: int}, zoom: int, w: int, h: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockMath', source: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, geo: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: float, long: float, accuracy_radius?: int}, zoom: int, w: int, h: int}|array{_: 'pageBlockButtonRow', align_left: bool, align_center: bool, align_right: bool, buttons: list<array{_: 'pageButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, type: array{_: 'inlineButtonTypeUrl', url: array}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: array, url: array, button_id: array}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: array, fwd_text?: array, url: array, bot?: array}|array{_: 'inlineButtonTypeWebView', url: array}|array{_: 'inlineButtonTypeCallback', requires_password: array, data: array}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: array, query: array, peer_types?: list<array>}|array{_: 'inlineButtonTypeUserProfile', user_id: array}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array}|array{_: 'inlineButtonTypeCopy', copy_text: array}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: array, bg_danger: array, bg_success: array, link: array}}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, document_id: int}>, photos: list<array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}}|array{_: 'messageService', peer_id: array|int|string, out: bool, mentioned: bool, media_unread: bool, reactions_are_possible: bool, silent: bool, post: bool, legacy: bool, id: int, from_id?: array|int|string, saved_peer_id?: array|int|string, reply_to?: array{_: 'messageReplyHeader', reply_to_scheduled: bool, forum_topic: bool, quote: bool, reply_to_ephemeral: bool, reply_to_msg_id?: int, reply_to_peer_id?: array|int|string, reply_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, reply_media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_to_top_id?: int, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, quote_offset?: int, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, url: string, webpage_id: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, email: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, phone: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, name: string}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, user_id: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, date: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, type: array{_: 'inlineButtonTypeUrl', url: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url: string, button_id: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: bool, fwd_text?: string, url: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url: string}|array{_: 'inlineButtonTypeCallback', requires_password: bool, data: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: bool, query: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: bool, bg_danger: bool, bg_success: bool, link: bool}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}>}|array{_: 'messageReplyStoryHeader', peer: array|int|string, story_id: int}, date: int, action: array{_: 'messageActionEmpty'}|array{_: 'messageActionChatCreate', title: string, users: list<int>}|array{_: 'messageActionChatEditTitle', title: string}|array{_: 'messageActionChatEditPhoto', photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'messageActionChatDeletePhoto'}|array{_: 'messageActionChatAddUser', users: list<int>}|array{_: 'messageActionChatDeleteUser', user_id: int}|array{_: 'messageActionChatJoinedByLink', inviter_id: int}|array{_: 'messageActionChannelCreate', title: string}|array{_: 'messageActionChatMigrateTo', channel_id: int}|array{_: 'messageActionChannelMigrateFrom', title: string, chat_id: array|int|string}|array{_: 'messageActionPinMessage'}|array{_: 'messageActionHistoryClear'}|array{_: 'messageActionGameScore', game_id: int, score: int}|array{_: 'messageActionPaymentSentMe', charge: array{_: 'paymentCharge', id: string, provider_charge_id: string}, recurring_init: bool, recurring_used: bool, currency: string, total_amount: int, payload: string, info?: array{_: 'paymentRequestedInfo', name?: string, phone?: string, email?: string, shipping_address?: array{_: 'postAddress', street_line1: string, street_line2: string, city: string, state: string, country_iso2: string, post_code: string}}, shipping_option_id?: string, subscription_until_date?: int}|array{_: 'messageActionPaymentSent', recurring_init: bool, recurring_used: bool, currency: string, total_amount: int, invoice_slug?: string, subscription_until_date?: int}|array{_: 'messageActionPhoneCall', video: bool, call_id: int, reason?: array{_: 'phoneCallDiscardReasonMissed'}|array{_: 'phoneCallDiscardReasonDisconnect'}|array{_: 'phoneCallDiscardReasonHangup'}|array{_: 'phoneCallDiscardReasonBusy'}|array{_: 'phoneCallDiscardReasonMigrateConferenceCall', slug: string}, duration?: int}|array{_: 'messageActionScreenshotTaken'}|array{_: 'messageActionCustomAction', message: string}|array{_: 'messageActionBotAllowed', attach_menu: bool, from_request: bool, domain?: string, app?: array{_: 'botAppNotModified'}|array{_: 'botApp', id: int, access_hash: int, short_name: string, title: string, description: string, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, hash: list<int|string>}}|array{_: 'messageActionSecureValuesSentMe', credentials: array{_: 'secureCredentialsEncrypted', data: string, hash: string, secret: string}, values: list<array{_: 'secureValue', type: array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}, data?: array{_: 'secureData', data: string, data_hash: string, secret: string}, front_side?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, reverse_side?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, selfie?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, translation?: list<array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}>, files?: list<array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}>, plain_data?: array{_: 'securePlainPhone', phone: string}|array{_: 'securePlainEmail', email: string}, hash: string}>}|array{_: 'messageActionSecureValuesSent', types: list<array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}>}|array{_: 'messageActionContactSignUp'}|array{_: 'messageActionGeoProximityReached', from_id: array|int|string, to_id: array|int|string, distance: int}|array{_: 'messageActionGroupCall', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, duration?: int}|array{_: 'messageActionInviteToGroupCall', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, users: list<int>}|array{_: 'messageActionSetMessagesTTL', period: int, auto_setting_from?: int}|array{_: 'messageActionGroupCallScheduled', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, schedule_date: int}|array{_: 'messageActionSetChatTheme', theme: array{_: 'chatTheme', emoticon: string}|array{_: 'chatThemeUniqueGift', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: array, edge_color: array, text_color: array}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array, crafted: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array, name: array, backdrop_id: array, center_color: array, edge_color: array, pattern_color: array, text_color: array}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array, sender_id?: array, date: array, message?: array}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: array, nanos: array}|array{_: 'starsTonAmount', amount: array}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: array, background_emoji_id?: array}|array{_: 'peerColorCollectible', collectible_id: array, gift_emoji_id: array, background_emoji_id: array, accent_color: array, colors: list<array>, dark_accent_color?: array, dark_colors?: list<array>}|array{_: 'inputPeerColorCollectible', collectible_id: array}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, theme_settings: list<array{_: 'themeSettings', base_theme: array{_: 'baseThemeClassic'}|array{_: 'baseThemeDay'}|array{_: 'baseThemeNight'}|array{_: 'baseThemeTinted'}|array{_: 'baseThemeArctic'}, message_colors_animated: bool, accent_color: int, outbox_accent_color?: int, message_colors?: list<int>, wallpaper?: array{_: 'wallPaper', id: array, creator: array, default: array, pattern: array, dark: array, access_hash: array, slug: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, settings?: array}|array{_: 'wallPaperNoFile', id: array, default: array, dark: array, settings?: array}}>}}|array{_: 'messageActionChatJoinedByRequest'}|array{_: 'messageActionWebViewDataSentMe', text: string, data: string}|array{_: 'messageActionWebViewDataSent', text: string}|array{_: 'messageActionGiftPremium', currency: string, amount: int, days: int, crypto_currency?: string, crypto_amount?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}|array{_: 'messageActionTopicCreate', title_missing: bool, title: string, icon_color: int, icon_emoji_id?: int}|array{_: 'messageActionTopicEdit', title?: string, icon_emoji_id?: int, closed?: bool, hidden?: bool}|array{_: 'messageActionSuggestProfilePhoto', photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'messageActionRequestedPeer', button_id: int, peers: list<array|int|string>}|array{_: 'messageActionSetChatWallPaper', wallpaper: array{_: 'wallPaper', id: int, creator: bool, default: bool, pattern: bool, dark: bool, access_hash: int, slug: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, settings?: array{_: 'wallPaperSettings', blur: bool, motion: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string}}|array{_: 'wallPaperNoFile', id: int, default: bool, dark: bool, settings?: array{_: 'wallPaperSettings', blur: bool, motion: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string}}, same: bool, for_both: bool}|array{_: 'messageActionGiftCode', via_giveaway: bool, unclaimed: bool, boost_peer?: array|int|string, days: int, slug: string, currency?: string, amount?: int, crypto_currency?: string, crypto_amount?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}|array{_: 'messageActionGiveawayLaunch', stars?: int}|array{_: 'messageActionGiveawayResults', stars: bool, winners_count: int, unclaimed_count: int}|array{_: 'messageActionBoostApply', boosts: int}|array{_: 'messageActionRequestedPeerSentMe', button_id: int, peers: list<array{_: 'requestedPeerUser', user_id: int, first_name?: string, last_name?: string, username?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'requestedPeerChat', chat_id: array|int|string, title?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'requestedPeerChannel', channel_id: int, title?: string, username?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}>}|array{_: 'messageActionPaymentRefunded', peer: array|int|string, charge: array{_: 'paymentCharge', id: string, provider_charge_id: string}, currency: string, total_amount: int, payload?: string}|array{_: 'messageActionGiftStars', currency: string, amount: int, stars: int, crypto_currency?: string, crypto_amount?: int, transaction_id?: string}|array{_: 'messageActionPrizeStars', boost_peer: array|int|string, unclaimed: bool, stars: int, transaction_id: string, giveaway_msg_id: int}|array{_: 'messageActionStarGift', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, name_hidden: bool, saved: bool, converted: bool, upgraded: bool, refunded: bool, can_upgrade: bool, prepaid_upgrade: bool, upgrade_separate: bool, auction_acquired: bool, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}, convert_stars?: int, upgrade_msg_id?: int, upgrade_stars?: int, from_id?: array|int|string, peer?: array|int|string, saved_id?: int, prepaid_upgrade_hash?: string, gift_msg_id?: int, to_id?: array|int|string, gift_num?: int}|array{_: 'messageActionStarGiftUnique', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, upgrade: bool, transferred: bool, saved: bool, refunded: bool, prepaid_upgrade: bool, assigned: bool, from_offer: bool, craft: bool, name_hidden: bool, can_export_at?: int, transfer_stars?: int, from_id?: array|int|string, peer?: array|int|string, saved_id?: int, resale_amount?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, can_transfer_at?: int, can_resell_at?: int, drop_original_details_stars?: int, can_craft_at?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}|array{_: 'messageActionPaidMessagesRefunded', count: int, stars: int}|array{_: 'messageActionPaidMessagesPrice', broadcast_messages_allowed: bool, stars: int}|array{_: 'messageActionConferenceCall', missed: bool, active: bool, video: bool, call_id: int, duration?: int, other_participants?: list<array|int|string>}|array{_: 'messageActionTodoCompletions', completed: list<int>, incompleted: list<int>}|array{_: 'messageActionTodoAppendTasks', list: list<array{_: 'todoItem', title: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, id: int}>}|array{_: 'messageActionSuggestedPostApproval', rejected: bool, balance_too_low: bool, reject_comment?: string, schedule_date?: int, price?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}}|array{_: 'messageActionSuggestedPostSuccess', price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}}|array{_: 'messageActionSuggestedPostRefund', payer_initiated: bool}|array{_: 'messageActionGiftTon', currency: string, amount: int, crypto_currency: string, crypto_amount: int, transaction_id?: string}|array{_: 'messageActionSuggestBirthday', birthday: array{_: 'birthday', day: int, month: int, year?: int}}|array{_: 'messageActionStarGiftPurchaseOffer', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, accepted: bool, declined: bool, expires_at: int}|array{_: 'messageActionStarGiftPurchaseOfferDeclined', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, expired: bool}|array{_: 'messageActionNewCreatorPending', new_creator_id: int}|array{_: 'messageActionChangeCreator', new_creator_id: int}|array{_: 'messageActionNoForwardsToggle', prev_value: bool, new_value: bool}|array{_: 'messageActionNoForwardsRequest', prev_value: bool, new_value: bool, expired: bool}|array{_: 'messageActionPollAppendAnswer', answer: array{_: 'pollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, option: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, added_by?: array|int|string, date?: int}|array{_: 'inputPollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, media?: \danog\MadelineProto\EventHandler\Media|string|array}}|array{_: 'messageActionPollDeleteAnswer', answer: array{_: 'pollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, option: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, added_by?: array|int|string, date?: int}|array{_: 'inputPollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, media?: \danog\MadelineProto\EventHandler\Media|string|array}}|array{_: 'messageActionManagedBotCreated', bot_id: int}|array{_: 'messageActionChangeCommunity', community_id?: int}|array{_: 'messageActionChatJoinedViaCommunity', community_id: int}|array{_: 'messageActionGramTransfer', comment_encrypted: bool, amount: int, peer_address: string, transaction_id: string, comment?: string}|array{_: 'messageActionWalletTonConnectRequest', accepted: bool, declined: bool, session_id: int, expires: int, topic?: string, trace_id?: string, dapp_name?: string}|array{_: 'messageActionNoPaidMessagesTokenUsed'}, reactions?: array{_: 'messageReactions', min: bool, can_see_list: bool, reactions_as_tags: bool, results: list<array{_: 'reactionCount', chosen_order?: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}, count: int}>, recent_reactions?: list<array{_: 'messagePeerReaction', peer_id: array|int|string, big: bool, unread: bool, my: bool, date: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}}>, top_reactors?: list<array{_: 'messageReactor', top: bool, my: bool, anonymous: bool, peer_id?: array|int|string, count: int}>}, ttl_period?: int}>, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/messages.PeerDialogs.html
     * @psalm-impure
     */
    public function getPeerDialogs(array $peers = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Save a message [draft](https://core.telegram.org/api/drafts) associated to a chat.
     *
     * @param bool $no_webpage Disable generation of the webpage preview
     * @param bool $invert_media If set, any eventual webpage preview will be shown on top of the message instead of at the bottom.
     * @param array{_: 'inputReplyToMessage', reply_to_msg_id?: int, top_msg_id?: int, reply_to_peer_id?: array|int|string, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>, quote_offset?: int, monoforum_peer_id?: array|int|string, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, url?: string, webpage_id?: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, email?: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, phone?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, name?: string}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, user_id?: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, date?: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, type: array{_: 'inlineButtonTypeUrl', url?: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url?: string, button_id?: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access?: bool, fwd_text?: string, url?: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url?: string}|array{_: 'inlineButtonTypeCallback', requires_password?: bool, data?: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer?: bool, query?: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id?: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id?: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text?: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary?: bool, bg_danger?: bool, bg_success?: bool, link?: bool}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}>}|array{_: 'inputReplyToStory', peer?: array|int|string, story_id?: int}|array{_: 'inputReplyToMonoForum', monoforum_peer_id?: array|int|string}|array{_: 'inputReplyToEphemeralMessage', id?: int} $reply_to If set, indicates that the message should be sent in reply to the specified message or story. @see https://docs.madelineproto.xyz/API_docs/types/InputReplyTo.html
     * @param array|int|string $peer Destination of the message that should be sent @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param string $message The draft
     * @param list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}> $entities Array of Message [entities](https://core.telegram.org/api/entities) for styled text @see https://docs.madelineproto.xyz/API_docs/types/MessageEntity.html
     * @param \danog\MadelineProto\ParseMode $parse_mode Whether to parse HTML or Markdown markup in the message
     * @param \danog\MadelineProto\EventHandler\Media|string|array $media Attached media @see https://docs.madelineproto.xyz/API_docs/types/InputMedia.html
     * @param int $effect Specifies a [message effect Â»](https://core.telegram.org/api/effects) to use for the message.
     * @param array{_: 'suggestedPost', accepted?: bool, rejected?: bool, price?: array{_: 'starsAmount', amount?: int, nanos?: int}|array{_: 'starsTonAmount', amount?: int}, schedule_date?: int} $suggested_post Used to [suggest a post to a channel, see here Â»](https://core.telegram.org/api/suggested-posts) for more info on the full flow. @see https://docs.madelineproto.xyz/API_docs/types/SuggestedPost.html
     * @param array{_: 'inputRichMessage', rtl?: bool, noautolink?: bool, blocks?: list<array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, published_date?: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, language?: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name?: string}|array{_: 'pageBlockList', items?: list<array{_: 'pageListItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, checkbox?: bool, checked?: bool}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, collapsed?: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, spoiler?: bool, photo_id?: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, autoplay?: bool, loop?: bool, spoiler?: bool, video_id?: int}|array{_: 'pageBlockCover', cover: array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, published_date?: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, language?: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name?: string}|array{_: 'pageBlockList', items?: list<array{_: 'pageListItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, checkbox?: bool, checked?: bool}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, collapsed?: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, spoiler?: bool, photo_id?: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, autoplay?: bool, loop?: bool, spoiler?: bool, video_id?: int}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, full_width?: bool, allow_scrolling?: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel?: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, audio_id?: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, bordered?: bool, striped?: bool, compact?: bool, rows?: list<array{_: 'pageTableRow', cells?: list<array{_: 'pageTableCell', header?: bool, align_center?: bool, align_right?: bool, valign_middle?: bool, valign_bottom?: bool, text?: array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textConcat', texts?: list<array>}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}, colspan?: int, rowspan?: int}>}>}|array{_: 'pageBlockOrderedList', reversed?: bool, items?: list<array{_: 'pageListOrderedItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, checkbox?: bool, checked?: bool, num?: string, value?: int, type?: string}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, articles?: list<array{_: 'pageRelatedArticle', url?: string, webpage_id?: int, title?: string, description?: string, photo_id?: int, author?: string, published_date?: int}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, geo?: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash?: int, accuracy_radius?: int}, zoom?: int, w?: int, h?: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockMath', source?: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, geo?: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: float, long: float, accuracy_radius?: int}, zoom?: int, w?: int, h?: int}|array{_: 'pageBlockButtonRow', align_left?: bool, align_center?: bool, align_right?: bool, buttons?: list<array{_: 'pageButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, type: array{_: 'inlineButtonTypeUrl', url?: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url?: string, button_id?: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access?: bool, fwd_text?: string, url?: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url?: string}|array{_: 'inlineButtonTypeCallback', requires_password?: bool, data?: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer?: bool, query?: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id?: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id?: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text?: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary?: bool, bg_danger?: bool, bg_success?: bool, link?: bool}}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, document_id?: int}}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, full_width?: bool, allow_scrolling?: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel?: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, audio_id?: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, bordered?: bool, striped?: bool, compact?: bool, rows?: list<array{_: 'pageTableRow', cells?: list<array{_: 'pageTableCell', header?: bool, align_center?: bool, align_right?: bool, valign_middle?: bool, valign_bottom?: bool, text?: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, colspan?: int, rowspan?: int}>}>}|array{_: 'pageBlockOrderedList', reversed?: bool, items?: list<array{_: 'pageListOrderedItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, checkbox?: bool, checked?: bool, num?: string, value?: int, type?: string}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, articles?: list<array{_: 'pageRelatedArticle', url?: string, webpage_id?: int, title?: string, description?: string, photo_id?: int, author?: string, published_date?: int}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, geo?: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash?: int, accuracy_radius?: int}, zoom?: int, w?: int, h?: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockMath', source?: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, geo?: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: float, long: float, accuracy_radius?: int}, zoom?: int, w?: int, h?: int}|array{_: 'pageBlockButtonRow', align_left?: bool, align_center?: bool, align_right?: bool, buttons?: list<array{_: 'pageButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, type: array{_: 'inlineButtonTypeUrl', url?: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url?: string, button_id?: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access?: bool, fwd_text?: string, url?: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url?: string}|array{_: 'inlineButtonTypeCallback', requires_password?: bool, data?: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer?: bool, query?: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id?: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id?: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text?: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary?: bool, bg_danger?: bool, bg_success?: bool, link?: bool}}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, document_id?: int}>, photos?: list<array>, documents?: list<array>, users?: list<array|int|string>}|array{_: 'inputRichMessageHTML', rtl?: bool, noautolink?: bool, html?: string, files?: list<array{_: 'inputRichFilePhoto', id?: string, photo?: array}|array{_: 'inputRichFileDocument', id?: string, document?: array}>}|array{_: 'inputRichMessageMarkdown', rtl?: bool, noautolink?: bool, markdown?: string, files?: list<array{_: 'inputRichFilePhoto', id?: string, photo?: array}|array{_: 'inputRichFileDocument', id?: string, document?: array}>} $rich_message @see https://docs.madelineproto.xyz/API_docs/types/InputRichMessage.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function saveDraft(bool|null $no_webpage = null, bool|null $invert_media = null, array|null $reply_to = null, array|int|string|null $peer = null, string|null $message = '', array|null $entities = null, \danog\MadelineProto\ParseMode $parse_mode = \danog\MadelineProto\ParseMode::TEXT, \danog\MadelineProto\EventHandler\Media|array|string|null $media = null, int|null $effect = null, array|null $suggested_post = null, array|null $rich_message = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Return all message [drafts](https://core.telegram.org/api/drafts).  
     * Returns all the latest [updateDraftMessage](https://docs.madelineproto.xyz/API_docs/constructors/updateDraftMessage.html) updates related to all chats with drafts.
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function getAllDrafts(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get featured stickers
     *
     * @param list<int|string>|array<never, never> $hash Array of [Hash used for caching, for more info click here](https://core.telegram.org/api/offsets#hash-generation). @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.featuredStickersNotModified', count: int}|array{_: 'messages.featuredStickers', premium: bool, hash: list<int|string>, count: int, sets: list<array{_: 'stickerSetCovered', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}, cover: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'stickerSetMultiCovered', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}, covers: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}|array{_: 'stickerSetFullCovered', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}, packs: list<array{_: 'stickerPack', emoticon: string, documents: list<int>}>, keywords: list<array{_: 'stickerKeyword', document_id: int, keyword: list<string>}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}|array{_: 'stickerSetNoCovered', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}}>, unread: list<int>} @see https://docs.madelineproto.xyz/API_docs/types/messages.FeaturedStickers.html
     * @psalm-impure
     */
    public function getFeaturedStickers(array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Mark new featured stickers as read
     *
     * @param list<int>|array<never, never> $id IDs of stickersets to mark as read
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function readFeaturedStickers(array $id = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Get recent stickers
     *
     * @param bool $attached Get stickers recently attached to photo or video files
     * @param list<int|string>|array<never, never> $hash Array of [Hash used for caching, for more info click here](https://core.telegram.org/api/offsets#hash-generation). @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.recentStickersNotModified'}|array{_: 'messages.recentStickers', hash: list<int|string>, packs: list<array{_: 'stickerPack', emoticon: string, documents: list<int>}>, stickers: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, dates: list<int>} @see https://docs.madelineproto.xyz/API_docs/types/messages.RecentStickers.html
     * @psalm-impure
     */
    public function getRecentStickers(bool|null $attached = null, array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Add/remove sticker from recent stickers list
     *
     * @param bool $unsave Whether to save or unsave the sticker
     * @param bool $attached Whether to add/remove stickers recently attached to photo or video files
     * @param array $id Sticker @see https://docs.madelineproto.xyz/API_docs/types/InputDocument.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function saveRecentSticker(bool $unsave, bool|null $attached = null, array|null $id = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Clear recent stickers
     *
     * @param bool $attached Set this flag to clear the list of stickers recently attached to photo or video files
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function clearRecentStickers(bool|null $attached = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Get all archived stickers
     *
     * @param bool $masks Get [mask stickers](https://core.telegram.org/api/stickers#mask-stickers)
     * @param bool $emojis Get [custom emoji stickers](https://core.telegram.org/api/custom-emoji)
     * @param int $offset_id [Offsets for pagination, for more info click here](https://core.telegram.org/api/offsets)
     * @param int $limit Maximum number of results to return, [see pagination](https://core.telegram.org/api/offsets)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.archivedStickers', count: int, sets: list<array{_: 'stickerSetCovered', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}, cover: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'stickerSetMultiCovered', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}, covers: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}|array{_: 'stickerSetFullCovered', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}, packs: list<array{_: 'stickerPack', emoticon: string, documents: list<int>}>, keywords: list<array{_: 'stickerKeyword', document_id: int, keyword: list<string>}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}|array{_: 'stickerSetNoCovered', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}}>} @see https://docs.madelineproto.xyz/API_docs/types/messages.ArchivedStickers.html
     * @psalm-impure
     */
    public function getArchivedStickers(bool|null $masks = null, bool|null $emojis = null, int|null $offset_id = 0, int|null $limit = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get installed mask stickers
     *
     * @param list<int|string>|array<never, never> $hash Array of [Hash used for caching, for more info click here](https://core.telegram.org/api/offsets#hash-generation). @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.allStickersNotModified'}|array{_: 'messages.allStickers', hash: list<int|string>, sets: list<array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}>} @see https://docs.madelineproto.xyz/API_docs/types/messages.AllStickers.html
     * @psalm-impure
     */
    public function getMaskStickers(array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get stickers attached to a photo or video
     *
     * @param array{_: 'inputStickeredMediaPhoto', id?: array}|array{_: 'inputStickeredMediaDocument', id?: array} $media Stickered media @see https://docs.madelineproto.xyz/API_docs/types/InputStickeredMedia.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return list<array{_: 'stickerSetCovered', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}, cover: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'stickerSetMultiCovered', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}, covers: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}|array{_: 'stickerSetFullCovered', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}, packs: list<array{_: 'stickerPack', emoticon: string, documents: list<int>}>, keywords: list<array{_: 'stickerKeyword', document_id: int, keyword: list<string>}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}|array{_: 'stickerSetNoCovered', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}}> Array of  @see https://docs.madelineproto.xyz/API_docs/types/StickerSetCovered.html
     * @psalm-impure
     */
    public function getAttachedStickers(array $media, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Use this method to set the score of the specified user in a game sent as a normal message (bots only).
     *
     * @param bool $edit_message Set this flag if the game message should be automatically edited to include the current scoreboard
     * @param bool $force Set this flag if the high score is allowed to decrease. This can be useful when fixing mistakes or banning cheaters
     * @param array|int|string $peer Unique identifier of target chat @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $id Identifier of the sent message
     * @param array|int|string $user_id User identifier @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param int $score New score
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function setGameScore(bool|null $edit_message = null, bool|null $force = null, array|int|string|null $peer = null, int|null $id = 0, array|int|string|null $user_id = null, int|null $score = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Use this method to set the score of the specified user in a game sent as an inline message (bots only).
     *
     * @param array{_: 'inputBotInlineMessageID', dc_id?: int, id?: int, access_hash?: int}|array{_: 'inputBotInlineMessageID64', dc_id?: int, owner_id?: int, id?: int, access_hash?: int} $id ID of the inline message @see https://docs.madelineproto.xyz/API_docs/types/InputBotInlineMessageID.html
     * @param bool $edit_message Set this flag if the game message should be automatically edited to include the current scoreboard
     * @param bool $force Set this flag if the high score is allowed to decrease. This can be useful when fixing mistakes or banning cheaters
     * @param array|int|string $user_id User identifier @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param int $score New score
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function setInlineGameScore(array $id, bool|null $edit_message = null, bool|null $force = null, array|int|string|null $user_id = null, int|null $score = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Get highscores of a game
     *
     * @param array|int|string $peer Where was the game sent @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $id ID of message with game media attachment
     * @param array|int|string $user_id Get high scores made by a certain user @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.highScores', scores: list<array{_: 'highScore', pos: int, user_id: int, score: int}>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/messages.HighScores.html
     * @psalm-impure
     */
    public function getGameHighScores(array|int|string|null $peer = null, int|null $id = 0, array|int|string|null $user_id = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get highscores of a game sent using an inline bot
     *
     * @param array{_: 'inputBotInlineMessageID', dc_id?: int, id?: int, access_hash?: int}|array{_: 'inputBotInlineMessageID64', dc_id?: int, owner_id?: int, id?: int, access_hash?: int} $id ID of inline message @see https://docs.madelineproto.xyz/API_docs/types/InputBotInlineMessageID.html
     * @param array|int|string $user_id Get high scores of a certain user @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.highScores', scores: list<array{_: 'highScore', pos: int, user_id: int, score: int}>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/messages.HighScores.html
     * @psalm-impure
     */
    public function getInlineGameHighScores(array $id, array|int|string|null $user_id = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get chats in common with a user
     *
     * @param array|int|string $user_id User ID @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param int $max_id Maximum ID of chat to return (see [pagination](https://core.telegram.org/api/offsets))
     * @param int $limit Maximum number of results to return, [see pagination](https://core.telegram.org/api/offsets)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.chats', chats: list<array|int|string>}|array{_: 'messages.chatsSlice', count: int, chats: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/messages.Chats.html
     * @psalm-impure
     */
    public function getCommonChats(array|int|string|null $user_id = null, int|null $max_id = 0, int|null $limit = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get [instant view](https://instantview.telegram.org) page
     *
     * @param string $url URL of IV page to fetch
     * @param int $hash 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.webPage', webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/messages.WebPage.html
     * @psalm-impure
     */
    public function getWebPage(string|null $url = '', int|null $hash = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Pin/unpin a dialog
     *
     * @param array{_: 'inputDialogPeer', peer?: array|int|string}|array{_: 'inputDialogPeerFolder', folder_id?: int}|array{_: 'inputDialogPeerCommunity', community?: array|int|string} $peer The dialog to pin @see https://docs.madelineproto.xyz/API_docs/types/InputDialogPeer.html
     * @param bool $pinned Whether to pin or unpin the dialog
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function toggleDialogPin(array $peer, bool|null $pinned = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Reorder pinned dialogs
     *
     * @param bool $force If set, dialogs pinned server-side but not present in the `order` field will be unpinned.
     * @param int $folder_id [Peer folder ID, for more info click here](https://core.telegram.org/api/folders#peer-folders)
     * @param list<array{_: 'inputDialogPeer', peer?: array|int|string}|array{_: 'inputDialogPeerFolder', folder_id?: int}|array{_: 'inputDialogPeerCommunity', community?: array|int|string}>|array<never, never> $order Array of New dialog order @see https://docs.madelineproto.xyz/API_docs/types/InputDialogPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function reorderPinnedDialogs(bool|null $force = null, int|null $folder_id = 0, array $order = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Get pinned dialogs
     *
     * @param int $folder_id [Peer folder ID, for more info click here](https://core.telegram.org/api/folders#peer-folders)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.peerDialogs', state: array{_: 'updates.state', pts: int, qts: int, date: int, seq: int, unread_count: int}, dialogs: list<array{_: 'dialog', peer: array|int|string, notify_settings: array{_: 'peerNotifySettings', show_previews?: bool, silent?: bool, mute_until?: int, ios_sound?: array{_: 'notificationSoundDefault'}|array{_: 'notificationSoundNone'}|array{_: 'notificationSoundLocal', title: string, data: string}|array{_: 'notificationSoundRingtone', id: int}, android_sound?: array{_: 'notificationSoundDefault'}|array{_: 'notificationSoundNone'}|array{_: 'notificationSoundLocal', title: string, data: string}|array{_: 'notificationSoundRingtone', id: int}, other_sound?: array{_: 'notificationSoundDefault'}|array{_: 'notificationSoundNone'}|array{_: 'notificationSoundLocal', title: string, data: string}|array{_: 'notificationSoundRingtone', id: int}, stories_muted?: bool, stories_hide_sender?: bool, stories_ios_sound?: array{_: 'notificationSoundDefault'}|array{_: 'notificationSoundNone'}|array{_: 'notificationSoundLocal', title: string, data: string}|array{_: 'notificationSoundRingtone', id: int}, stories_android_sound?: array{_: 'notificationSoundDefault'}|array{_: 'notificationSoundNone'}|array{_: 'notificationSoundLocal', title: string, data: string}|array{_: 'notificationSoundRingtone', id: int}, stories_other_sound?: array{_: 'notificationSoundDefault'}|array{_: 'notificationSoundNone'}|array{_: 'notificationSoundLocal', title: string, data: string}|array{_: 'notificationSoundRingtone', id: int}}, pinned: bool, unread_mark: bool, view_forum_as_messages: bool, top_message: int, read_inbox_max_id: int, read_outbox_max_id: int, unread_count: int, unread_mentions_count: int, unread_reactions_count: int, unread_poll_votes_count: int, pts?: int, draft?: array{_: 'draftMessageEmpty', date?: int}|array{_: 'draftMessage', no_webpage: bool, invert_media: bool, reply_to?: array{_: 'inputReplyToMessage', reply_to_msg_id: int, top_msg_id?: int, reply_to_peer_id?: array|int|string, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, quote_offset?: int, monoforum_peer_id?: array|int|string, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, url: string, webpage_id: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, email: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, phone: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, name: string}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, user_id: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, date: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, type: array{_: 'inlineButtonTypeUrl', url: array}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: array, url: array, button_id: array}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: array, fwd_text?: array, url: array, bot?: array}|array{_: 'inlineButtonTypeWebView', url: array}|array{_: 'inlineButtonTypeCallback', requires_password: array, data: array}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: array, query: array, peer_types?: list<array>}|array{_: 'inlineButtonTypeUserProfile', user_id: array}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array}|array{_: 'inlineButtonTypeCopy', copy_text: array}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: array, bg_danger: array, bg_success: array, link: array}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}>}|array{_: 'inputReplyToStory', peer: array|int|string, story_id: int}|array{_: 'inputReplyToMonoForum', monoforum_peer_id: array|int|string}|array{_: 'inputReplyToEphemeralMessage', id: int}, message: string, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, media?: \danog\MadelineProto\EventHandler\Media|string|array, date: int, effect?: int, suggested_post?: array{_: 'suggestedPost', accepted: bool, rejected: bool, price?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, schedule_date?: int}, rich_message?: array{_: 'richMessage', rtl: bool, part: bool, blocks: list<array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, published_date: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, language: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: string}|array{_: 'pageBlockList', items: list<array{_: 'pageListItemText', text: array, checkbox: array, checked: array}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, collapsed: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array, credit: array}, spoiler: bool, photo_id: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array, credit: array}, autoplay: bool, loop: bool, spoiler: bool, video_id: int}|array{_: 'pageBlockCover', cover: array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array}|array{_: 'pageBlockSubtitle', text: array}|array{_: 'pageBlockAuthorDate', author: array, published_date: array}|array{_: 'pageBlockHeader', text: array}|array{_: 'pageBlockSubheader', text: array}|array{_: 'pageBlockParagraph', text: array}|array{_: 'pageBlockPreformatted', text: array, language: array}|array{_: 'pageBlockFooter', text: array}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: array}|array{_: 'pageBlockList', items: list<array>}|array{_: 'pageBlockBlockquote', text: array, caption: array, collapsed: array}|array{_: 'pageBlockPullquote', text: array, caption: array}|array{_: 'pageBlockPhoto', caption: array, spoiler: array, photo_id: array, url?: array, webpage_id?: array}|array{_: 'pageBlockVideo', caption: array, autoplay: array, loop: array, spoiler: array, video_id: array}|array{_: 'pageBlockEmbed', caption: array, full_width: array, allow_scrolling: array, url?: array, html?: array, poster_photo_id?: array, w?: array, h?: array}|array{_: 'pageBlockChannel', channel: array}|array{_: 'pageBlockAudio', caption: array, audio_id: array}|array{_: 'pageBlockKicker', text: array}|array{_: 'pageBlockTable', title: array, bordered: array, striped: array, compact: array, rows: list<array>}|array{_: 'pageBlockOrderedList', reversed: array, items: list<array>, start?: array, type?: array}|array{_: 'pageBlockRelatedArticles', title: array, articles: list<array>}|array{_: 'pageBlockMap', caption: array, geo: array, zoom: array, w: array, h: array}|array{_: 'pageBlockHeading1', text: array}|array{_: 'pageBlockHeading2', text: array}|array{_: 'pageBlockHeading3', text: array}|array{_: 'pageBlockHeading4', text: array}|array{_: 'pageBlockHeading5', text: array}|array{_: 'pageBlockHeading6', text: array}|array{_: 'pageBlockMath', source: array}|array{_: 'pageBlockThinking', text: array}|array{_: 'inputPageBlockMap', caption: array, geo: array, zoom: array, w: array, h: array}|array{_: 'pageBlockButtonRow', align_left: array, align_center: array, align_right: array, buttons: list<array>}|array{_: 'pageBlockDocument', caption: array, document_id: array}}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array, credit: array}, full_width: bool, allow_scrolling: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array, credit: array}, audio_id: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, bordered: bool, striped: bool, compact: bool, rows: list<array{_: 'pageTableRow', cells: list<array>}>}|array{_: 'pageBlockOrderedList', reversed: bool, items: list<array{_: 'pageListOrderedItemText', text: array, checkbox: array, checked: array, num?: array, value?: array, type?: array}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, articles: list<array{_: 'pageRelatedArticle', url: array, webpage_id: array, title?: array, description?: array, photo_id?: array, author?: array, published_date?: array}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockMath', source: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: array, long: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockButtonRow', align_left: bool, align_center: bool, align_right: bool, buttons: list<array{_: 'pageButton', text: array, type: array, style?: array}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array, credit: array}, document_id: int}>, photos: list<array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}}, folder_id?: int, ttl_period?: int}|array{_: 'dialogFolder', folder: array{_: 'folder', autofill_new_broadcasts: bool, autofill_public_groups: bool, autofill_new_correspondents: bool, id: int, title: string, photo?: array{_: 'chatPhotoEmpty'}|array{_: 'chatPhoto', has_video: bool, photo_id: int, stripped_thumb?: string, dc_id: int}}, peer: array|int|string, pinned: bool, top_message: int, unread_muted_peers_count: int, unread_unmuted_peers_count: int, unread_muted_messages_count: int, unread_unmuted_messages_count: int}|array{_: 'dialogCommunity', notify_settings: array{_: 'peerNotifySettings', show_previews?: bool, silent?: bool, mute_until?: int, ios_sound?: array{_: 'notificationSoundDefault'}|array{_: 'notificationSoundNone'}|array{_: 'notificationSoundLocal', title: string, data: string}|array{_: 'notificationSoundRingtone', id: int}, android_sound?: array{_: 'notificationSoundDefault'}|array{_: 'notificationSoundNone'}|array{_: 'notificationSoundLocal', title: string, data: string}|array{_: 'notificationSoundRingtone', id: int}, other_sound?: array{_: 'notificationSoundDefault'}|array{_: 'notificationSoundNone'}|array{_: 'notificationSoundLocal', title: string, data: string}|array{_: 'notificationSoundRingtone', id: int}, stories_muted?: bool, stories_hide_sender?: bool, stories_ios_sound?: array{_: 'notificationSoundDefault'}|array{_: 'notificationSoundNone'}|array{_: 'notificationSoundLocal', title: string, data: string}|array{_: 'notificationSoundRingtone', id: int}, stories_android_sound?: array{_: 'notificationSoundDefault'}|array{_: 'notificationSoundNone'}|array{_: 'notificationSoundLocal', title: string, data: string}|array{_: 'notificationSoundRingtone', id: int}, stories_other_sound?: array{_: 'notificationSoundDefault'}|array{_: 'notificationSoundNone'}|array{_: 'notificationSoundLocal', title: string, data: string}|array{_: 'notificationSoundRingtone', id: int}}, pinned: bool, community_id: int}>, messages: list<array{_: 'messageEmpty', id: int, peer_id?: array|int|string}|array{_: 'message', peer_id: array|int|string, out: bool, mentioned: bool, media_unread: bool, silent: bool, post: bool, from_scheduled: bool, legacy: bool, edit_hide: bool, pinned: bool, noforwards: bool, invert_media: bool, offline: bool, video_processing_pending: bool, paid_suggested_post_stars: bool, paid_suggested_post_ton: bool, id: int, from_id?: array|int|string, from_boosts_applied?: int, from_rank?: string, saved_peer_id?: array|int|string, fwd_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, via_bot_id?: int, via_business_bot_id?: int, guestchat_via_from?: array|int|string, reply_to?: array{_: 'messageReplyHeader', reply_to_scheduled: bool, forum_topic: bool, quote: bool, reply_to_ephemeral: bool, reply_to_msg_id?: int, reply_to_peer_id?: array|int|string, reply_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, reply_media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_to_top_id?: int, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, quote_offset?: int, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, url: string, webpage_id: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, email: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, phone: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, name: string}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, user_id: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, date: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, type: array{_: 'inlineButtonTypeUrl', url: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url: string, button_id: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: bool, fwd_text?: string, url: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url: string}|array{_: 'inlineButtonTypeCallback', requires_password: bool, data: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: bool, query: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: bool, bg_danger: bool, bg_success: bool, link: bool}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}>}|array{_: 'messageReplyStoryHeader', peer: array|int|string, story_id: int}, date: int, message: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_markup?: array, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, views?: int, forwards?: int, replies?: array{_: 'messageReplies', comments: bool, replies: int, replies_pts: int, recent_repliers?: list<array|int|string>, channel_id?: int, max_id?: int, read_max_id?: int}, edit_date?: int, post_author?: string, grouped_id?: int, reactions?: array{_: 'messageReactions', min: bool, can_see_list: bool, reactions_as_tags: bool, results: list<array{_: 'reactionCount', chosen_order?: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}, count: int}>, recent_reactions?: list<array{_: 'messagePeerReaction', peer_id: array|int|string, big: bool, unread: bool, my: bool, date: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}}>, top_reactors?: list<array{_: 'messageReactor', top: bool, my: bool, anonymous: bool, peer_id?: array|int|string, count: int}>}, restriction_reason?: list<array{_: 'restrictionReason', platform: string, reason: string, text: string}>, ttl_period?: int, quick_reply_shortcut_id?: int, effect?: int, factcheck?: array{_: 'factCheck', need_check: bool, country?: string, text?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}, hash: list<int|string>}, report_delivery_until_date?: int, paid_message_stars?: int, suggested_post?: array{_: 'suggestedPost', accepted: bool, rejected: bool, price?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, schedule_date?: int}, schedule_repeat_period?: int, summary_from_language?: string, rich_message?: array{_: 'richMessage', rtl: bool, part: bool, blocks: list<array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, published_date: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, language: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: string}|array{_: 'pageBlockList', items: list<array{_: 'pageListItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, checkbox: bool, checked: bool}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, collapsed: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, spoiler: bool, photo_id: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, autoplay: bool, loop: bool, spoiler: bool, video_id: int}|array{_: 'pageBlockCover', cover: array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, published_date: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, language: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: string}|array{_: 'pageBlockList', items: list<array{_: 'pageListItemText', text: array, checkbox: array, checked: array}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, collapsed: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array, credit: array}, spoiler: bool, photo_id: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array, credit: array}, autoplay: bool, loop: bool, spoiler: bool, video_id: int}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array, credit: array}, full_width: bool, allow_scrolling: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array, credit: array}, audio_id: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, bordered: bool, striped: bool, compact: bool, rows: list<array{_: 'pageTableRow', cells: list<array>}>}|array{_: 'pageBlockOrderedList', reversed: bool, items: list<array{_: 'pageListOrderedItemText', text: array, checkbox: array, checked: array, num?: array, value?: array, type?: array}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, articles: list<array{_: 'pageRelatedArticle', url: array, webpage_id: array, title?: array, description?: array, photo_id?: array, author?: array, published_date?: array}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockMath', source: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: array, long: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockButtonRow', align_left: bool, align_center: bool, align_right: bool, buttons: list<array{_: 'pageButton', text: array, type: array, style?: array}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array, credit: array}, document_id: int}}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, full_width: bool, allow_scrolling: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, audio_id: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, bordered: bool, striped: bool, compact: bool, rows: list<array{_: 'pageTableRow', cells: list<array{_: 'pageTableCell', header: array, align_center: array, align_right: array, valign_middle: array, valign_bottom: array, text?: array, colspan?: array, rowspan?: array}>}>}|array{_: 'pageBlockOrderedList', reversed: bool, items: list<array{_: 'pageListOrderedItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, checkbox: bool, checked: bool, num?: string, value?: int, type?: string}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, articles: list<array{_: 'pageRelatedArticle', url: string, webpage_id: int, title?: string, description?: string, photo_id?: int, author?: string, published_date?: int}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash: int, accuracy_radius?: int}, zoom: int, w: int, h: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockMath', source: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, geo: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: float, long: float, accuracy_radius?: int}, zoom: int, w: int, h: int}|array{_: 'pageBlockButtonRow', align_left: bool, align_center: bool, align_right: bool, buttons: list<array{_: 'pageButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, type: array{_: 'inlineButtonTypeUrl', url: array}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: array, url: array, button_id: array}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: array, fwd_text?: array, url: array, bot?: array}|array{_: 'inlineButtonTypeWebView', url: array}|array{_: 'inlineButtonTypeCallback', requires_password: array, data: array}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: array, query: array, peer_types?: list<array>}|array{_: 'inlineButtonTypeUserProfile', user_id: array}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array}|array{_: 'inlineButtonTypeCopy', copy_text: array}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: array, bg_danger: array, bg_success: array, link: array}}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, document_id: int}>, photos: list<array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}}|array{_: 'messageService', peer_id: array|int|string, out: bool, mentioned: bool, media_unread: bool, reactions_are_possible: bool, silent: bool, post: bool, legacy: bool, id: int, from_id?: array|int|string, saved_peer_id?: array|int|string, reply_to?: array{_: 'messageReplyHeader', reply_to_scheduled: bool, forum_topic: bool, quote: bool, reply_to_ephemeral: bool, reply_to_msg_id?: int, reply_to_peer_id?: array|int|string, reply_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, reply_media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_to_top_id?: int, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, quote_offset?: int, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, url: string, webpage_id: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, email: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, phone: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, name: string}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, user_id: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, date: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, type: array{_: 'inlineButtonTypeUrl', url: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url: string, button_id: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: bool, fwd_text?: string, url: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url: string}|array{_: 'inlineButtonTypeCallback', requires_password: bool, data: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: bool, query: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: bool, bg_danger: bool, bg_success: bool, link: bool}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}>}|array{_: 'messageReplyStoryHeader', peer: array|int|string, story_id: int}, date: int, action: array{_: 'messageActionEmpty'}|array{_: 'messageActionChatCreate', title: string, users: list<int>}|array{_: 'messageActionChatEditTitle', title: string}|array{_: 'messageActionChatEditPhoto', photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'messageActionChatDeletePhoto'}|array{_: 'messageActionChatAddUser', users: list<int>}|array{_: 'messageActionChatDeleteUser', user_id: int}|array{_: 'messageActionChatJoinedByLink', inviter_id: int}|array{_: 'messageActionChannelCreate', title: string}|array{_: 'messageActionChatMigrateTo', channel_id: int}|array{_: 'messageActionChannelMigrateFrom', title: string, chat_id: array|int|string}|array{_: 'messageActionPinMessage'}|array{_: 'messageActionHistoryClear'}|array{_: 'messageActionGameScore', game_id: int, score: int}|array{_: 'messageActionPaymentSentMe', charge: array{_: 'paymentCharge', id: string, provider_charge_id: string}, recurring_init: bool, recurring_used: bool, currency: string, total_amount: int, payload: string, info?: array{_: 'paymentRequestedInfo', name?: string, phone?: string, email?: string, shipping_address?: array{_: 'postAddress', street_line1: string, street_line2: string, city: string, state: string, country_iso2: string, post_code: string}}, shipping_option_id?: string, subscription_until_date?: int}|array{_: 'messageActionPaymentSent', recurring_init: bool, recurring_used: bool, currency: string, total_amount: int, invoice_slug?: string, subscription_until_date?: int}|array{_: 'messageActionPhoneCall', video: bool, call_id: int, reason?: array{_: 'phoneCallDiscardReasonMissed'}|array{_: 'phoneCallDiscardReasonDisconnect'}|array{_: 'phoneCallDiscardReasonHangup'}|array{_: 'phoneCallDiscardReasonBusy'}|array{_: 'phoneCallDiscardReasonMigrateConferenceCall', slug: string}, duration?: int}|array{_: 'messageActionScreenshotTaken'}|array{_: 'messageActionCustomAction', message: string}|array{_: 'messageActionBotAllowed', attach_menu: bool, from_request: bool, domain?: string, app?: array{_: 'botAppNotModified'}|array{_: 'botApp', id: int, access_hash: int, short_name: string, title: string, description: string, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, hash: list<int|string>}}|array{_: 'messageActionSecureValuesSentMe', credentials: array{_: 'secureCredentialsEncrypted', data: string, hash: string, secret: string}, values: list<array{_: 'secureValue', type: array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}, data?: array{_: 'secureData', data: string, data_hash: string, secret: string}, front_side?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, reverse_side?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, selfie?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, translation?: list<array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}>, files?: list<array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}>, plain_data?: array{_: 'securePlainPhone', phone: string}|array{_: 'securePlainEmail', email: string}, hash: string}>}|array{_: 'messageActionSecureValuesSent', types: list<array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}>}|array{_: 'messageActionContactSignUp'}|array{_: 'messageActionGeoProximityReached', from_id: array|int|string, to_id: array|int|string, distance: int}|array{_: 'messageActionGroupCall', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, duration?: int}|array{_: 'messageActionInviteToGroupCall', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, users: list<int>}|array{_: 'messageActionSetMessagesTTL', period: int, auto_setting_from?: int}|array{_: 'messageActionGroupCallScheduled', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, schedule_date: int}|array{_: 'messageActionSetChatTheme', theme: array{_: 'chatTheme', emoticon: string}|array{_: 'chatThemeUniqueGift', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: array, edge_color: array, text_color: array}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array, crafted: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array, name: array, backdrop_id: array, center_color: array, edge_color: array, pattern_color: array, text_color: array}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array, sender_id?: array, date: array, message?: array}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: array, nanos: array}|array{_: 'starsTonAmount', amount: array}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: array, background_emoji_id?: array}|array{_: 'peerColorCollectible', collectible_id: array, gift_emoji_id: array, background_emoji_id: array, accent_color: array, colors: list<array>, dark_accent_color?: array, dark_colors?: list<array>}|array{_: 'inputPeerColorCollectible', collectible_id: array}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, theme_settings: list<array{_: 'themeSettings', base_theme: array{_: 'baseThemeClassic'}|array{_: 'baseThemeDay'}|array{_: 'baseThemeNight'}|array{_: 'baseThemeTinted'}|array{_: 'baseThemeArctic'}, message_colors_animated: bool, accent_color: int, outbox_accent_color?: int, message_colors?: list<int>, wallpaper?: array{_: 'wallPaper', id: array, creator: array, default: array, pattern: array, dark: array, access_hash: array, slug: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, settings?: array}|array{_: 'wallPaperNoFile', id: array, default: array, dark: array, settings?: array}}>}}|array{_: 'messageActionChatJoinedByRequest'}|array{_: 'messageActionWebViewDataSentMe', text: string, data: string}|array{_: 'messageActionWebViewDataSent', text: string}|array{_: 'messageActionGiftPremium', currency: string, amount: int, days: int, crypto_currency?: string, crypto_amount?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}|array{_: 'messageActionTopicCreate', title_missing: bool, title: string, icon_color: int, icon_emoji_id?: int}|array{_: 'messageActionTopicEdit', title?: string, icon_emoji_id?: int, closed?: bool, hidden?: bool}|array{_: 'messageActionSuggestProfilePhoto', photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'messageActionRequestedPeer', button_id: int, peers: list<array|int|string>}|array{_: 'messageActionSetChatWallPaper', wallpaper: array{_: 'wallPaper', id: int, creator: bool, default: bool, pattern: bool, dark: bool, access_hash: int, slug: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, settings?: array{_: 'wallPaperSettings', blur: bool, motion: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string}}|array{_: 'wallPaperNoFile', id: int, default: bool, dark: bool, settings?: array{_: 'wallPaperSettings', blur: bool, motion: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string}}, same: bool, for_both: bool}|array{_: 'messageActionGiftCode', via_giveaway: bool, unclaimed: bool, boost_peer?: array|int|string, days: int, slug: string, currency?: string, amount?: int, crypto_currency?: string, crypto_amount?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}|array{_: 'messageActionGiveawayLaunch', stars?: int}|array{_: 'messageActionGiveawayResults', stars: bool, winners_count: int, unclaimed_count: int}|array{_: 'messageActionBoostApply', boosts: int}|array{_: 'messageActionRequestedPeerSentMe', button_id: int, peers: list<array{_: 'requestedPeerUser', user_id: int, first_name?: string, last_name?: string, username?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'requestedPeerChat', chat_id: array|int|string, title?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'requestedPeerChannel', channel_id: int, title?: string, username?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}>}|array{_: 'messageActionPaymentRefunded', peer: array|int|string, charge: array{_: 'paymentCharge', id: string, provider_charge_id: string}, currency: string, total_amount: int, payload?: string}|array{_: 'messageActionGiftStars', currency: string, amount: int, stars: int, crypto_currency?: string, crypto_amount?: int, transaction_id?: string}|array{_: 'messageActionPrizeStars', boost_peer: array|int|string, unclaimed: bool, stars: int, transaction_id: string, giveaway_msg_id: int}|array{_: 'messageActionStarGift', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, name_hidden: bool, saved: bool, converted: bool, upgraded: bool, refunded: bool, can_upgrade: bool, prepaid_upgrade: bool, upgrade_separate: bool, auction_acquired: bool, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}, convert_stars?: int, upgrade_msg_id?: int, upgrade_stars?: int, from_id?: array|int|string, peer?: array|int|string, saved_id?: int, prepaid_upgrade_hash?: string, gift_msg_id?: int, to_id?: array|int|string, gift_num?: int}|array{_: 'messageActionStarGiftUnique', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, upgrade: bool, transferred: bool, saved: bool, refunded: bool, prepaid_upgrade: bool, assigned: bool, from_offer: bool, craft: bool, name_hidden: bool, can_export_at?: int, transfer_stars?: int, from_id?: array|int|string, peer?: array|int|string, saved_id?: int, resale_amount?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, can_transfer_at?: int, can_resell_at?: int, drop_original_details_stars?: int, can_craft_at?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}|array{_: 'messageActionPaidMessagesRefunded', count: int, stars: int}|array{_: 'messageActionPaidMessagesPrice', broadcast_messages_allowed: bool, stars: int}|array{_: 'messageActionConferenceCall', missed: bool, active: bool, video: bool, call_id: int, duration?: int, other_participants?: list<array|int|string>}|array{_: 'messageActionTodoCompletions', completed: list<int>, incompleted: list<int>}|array{_: 'messageActionTodoAppendTasks', list: list<array{_: 'todoItem', title: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, id: int}>}|array{_: 'messageActionSuggestedPostApproval', rejected: bool, balance_too_low: bool, reject_comment?: string, schedule_date?: int, price?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}}|array{_: 'messageActionSuggestedPostSuccess', price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}}|array{_: 'messageActionSuggestedPostRefund', payer_initiated: bool}|array{_: 'messageActionGiftTon', currency: string, amount: int, crypto_currency: string, crypto_amount: int, transaction_id?: string}|array{_: 'messageActionSuggestBirthday', birthday: array{_: 'birthday', day: int, month: int, year?: int}}|array{_: 'messageActionStarGiftPurchaseOffer', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, accepted: bool, declined: bool, expires_at: int}|array{_: 'messageActionStarGiftPurchaseOfferDeclined', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, expired: bool}|array{_: 'messageActionNewCreatorPending', new_creator_id: int}|array{_: 'messageActionChangeCreator', new_creator_id: int}|array{_: 'messageActionNoForwardsToggle', prev_value: bool, new_value: bool}|array{_: 'messageActionNoForwardsRequest', prev_value: bool, new_value: bool, expired: bool}|array{_: 'messageActionPollAppendAnswer', answer: array{_: 'pollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, option: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, added_by?: array|int|string, date?: int}|array{_: 'inputPollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, media?: \danog\MadelineProto\EventHandler\Media|string|array}}|array{_: 'messageActionPollDeleteAnswer', answer: array{_: 'pollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, option: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, added_by?: array|int|string, date?: int}|array{_: 'inputPollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, media?: \danog\MadelineProto\EventHandler\Media|string|array}}|array{_: 'messageActionManagedBotCreated', bot_id: int}|array{_: 'messageActionChangeCommunity', community_id?: int}|array{_: 'messageActionChatJoinedViaCommunity', community_id: int}|array{_: 'messageActionGramTransfer', comment_encrypted: bool, amount: int, peer_address: string, transaction_id: string, comment?: string}|array{_: 'messageActionWalletTonConnectRequest', accepted: bool, declined: bool, session_id: int, expires: int, topic?: string, trace_id?: string, dapp_name?: string}|array{_: 'messageActionNoPaidMessagesTokenUsed'}, reactions?: array{_: 'messageReactions', min: bool, can_see_list: bool, reactions_as_tags: bool, results: list<array{_: 'reactionCount', chosen_order?: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}, count: int}>, recent_reactions?: list<array{_: 'messagePeerReaction', peer_id: array|int|string, big: bool, unread: bool, my: bool, date: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}}>, top_reactors?: list<array{_: 'messageReactor', top: bool, my: bool, anonymous: bool, peer_id?: array|int|string, count: int}>}, ttl_period?: int}>, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/messages.PeerDialogs.html
     * @psalm-impure
     */
    public function getPinnedDialogs(int|null $folder_id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * If you sent an invoice requesting a shipping address and the parameter is\_flexible was specified, the bot will receive an [updateBotShippingQuery](https://docs.madelineproto.xyz/API_docs/constructors/updateBotShippingQuery.html) update. Use this method to reply to shipping queries.
     *
     * @param int $query_id Unique identifier for the query to be answered
     * @param string $error Error message in human readable form that explains why it is impossible to complete the order (e.g. "Sorry, delivery to your desired address is unavailable"). Telegram will display this message to the user.
     * @param list<array{_: 'shippingOption', id?: string, title?: string, prices?: list<array{_: 'labeledPrice', label?: string, amount?: int}>}> $shipping_options Array of A vector of available shipping options. @see https://docs.madelineproto.xyz/API_docs/types/ShippingOption.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function setBotShippingResults(int|null $query_id = 0, string|null $error = null, array|null $shipping_options = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Once the user has confirmed their payment and shipping details, the bot receives an [updateBotPrecheckoutQuery](https://docs.madelineproto.xyz/API_docs/constructors/updateBotPrecheckoutQuery.html) update.  
     * Use this method to respond to such pre-checkout queries.  
     * **Note**: Telegram must receive an answer within 10 seconds after the pre-checkout query was sent.
     *
     * @param bool $success Set this flag if everything is alright (goods are available, etc.) and the bot is ready to proceed with the order, otherwise do not set it, and set the `error` field, instead
     * @param int $query_id Unique identifier for the query to be answered
     * @param string $error Required if the `success` isn't set. Error message in human readable form that explains the reason for failure to proceed with the checkout (e.g. "Sorry, somebody just bought the last of our amazing black T-shirts while you were busy filling out your payment details. Please choose a different color or garment!"). Telegram will display this message to the user.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function setBotPrecheckoutResults(bool|null $success = null, int|null $query_id = 0, string|null $error = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Upload a file and associate it to a chat (without actually sending it to the chat)
     * 
     * May also be used in a [business connection](https://core.telegram.org/api/bots/connected-business-bots), *not* by wrapping the query in [invokeWithBusinessConnection Â»](https://docs.madelineproto.xyz/API_docs/methods/invokeWithBusinessConnection.html), but rather by specifying the business connection ID in the `business_connection_id` parameter.
     *
     * @param string $business_connection_id Whether the media will be used only in the specified [business connection Â»](https://core.telegram.org/api/bots/connected-business-bots), and not directly by the bot.
     * @param array|int|string $peer The chat, can be [inputPeerEmpty](https://docs.madelineproto.xyz/API_docs/constructors/inputPeerEmpty.html) for bots and [inputPeerSelf](https://docs.madelineproto.xyz/API_docs/constructors/inputPeerSelf.html) for users. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param \danog\MadelineProto\EventHandler\Media|string|array $media File uploaded in chunks as described in [files Â»](https://core.telegram.org/api/files) @see https://docs.madelineproto.xyz/API_docs/types/InputMedia.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}|array{_: 'messageExtendedMedia', media: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaPoll', poll: array{_: 'poll', question: array, id: array, closed: array, public_voters: array, multiple_choice: array, quiz: array, open_answers: array, revoting_disabled: array, shuffle_answers: array, hide_results_until_close: array, creator: array, subscribers_only: array, answers: list<array>, close_period?: array, close_date?: array, countries_iso2?: list<array>, hash: list<array>}, results: array, attached_media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}|array{_: 'storyItem', pinned: array, public: array, close_friends: array, min: array, noforwards: array, edited: array, contacts: array, selected_contacts: array, out: array, id: array, date: array, from_id?: array, fwd_from?: array, expire_date: array, caption?: array, entities?: list<array>, media: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, media_areas?: list<array>, privacy?: list<array>, views?: array, sent_reaction?: array, albums?: list<array>, music?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}|array{_: 'messageExtendedMedia', media: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool} @see https://docs.madelineproto.xyz/API_docs/types/MessageMedia.html
     * @psalm-impure
     */
    public function uploadMedia(string|null $business_connection_id = null, array|int|string|null $peer = null, \danog\MadelineProto\EventHandler\Media|array|string|null $media = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Notify the other user in a private chat that a screenshot of the chat was taken
     *
     * @param array{_: 'inputReplyToMessage', reply_to_msg_id?: int, top_msg_id?: int, reply_to_peer_id?: array|int|string, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>, quote_offset?: int, monoforum_peer_id?: array|int|string, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, url?: string, webpage_id?: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, email?: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, phone?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, name?: string}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, user_id?: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, date?: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, type: array{_: 'inlineButtonTypeUrl', url?: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url?: string, button_id?: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access?: bool, fwd_text?: string, url?: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url?: string}|array{_: 'inlineButtonTypeCallback', requires_password?: bool, data?: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer?: bool, query?: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id?: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id?: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text?: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary?: bool, bg_danger?: bool, bg_success?: bool, link?: bool}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}>}|array{_: 'inputReplyToStory', peer?: array|int|string, story_id?: int}|array{_: 'inputReplyToMonoForum', monoforum_peer_id?: array|int|string}|array{_: 'inputReplyToEphemeralMessage', id?: int} $reply_to Indicates the message that was screenshotted (the specified message ID can also be `0` to avoid indicating any specific message). @see https://docs.madelineproto.xyz/API_docs/types/InputReplyTo.html
     * @param array|int|string $peer Other user @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function sendScreenshotNotification(array|null $reply_to = null, array|int|string|null $peer = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get faved stickers
     *
     * @param list<int|string>|array<never, never> $hash Array of [Hash used for caching, for more info click here](https://core.telegram.org/api/offsets#hash-generation). @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.favedStickersNotModified'}|array{_: 'messages.favedStickers', hash: list<int|string>, packs: list<array{_: 'stickerPack', emoticon: string, documents: list<int>}>, stickers: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>} @see https://docs.madelineproto.xyz/API_docs/types/messages.FavedStickers.html
     * @psalm-impure
     */
    public function getFavedStickers(array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Mark or unmark a sticker as favorite
     *
     * @param bool $unfave Whether to add or remove a sticker from favorites
     * @param array $id Sticker in question @see https://docs.madelineproto.xyz/API_docs/types/InputDocument.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function faveSticker(bool $unfave, array|null $id = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Get unread messages where we were mentioned
     *
     * @param array|int|string $peer Peer where to look for mentions @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $top_msg_id If set, considers only messages within the specified [forum topic](https://core.telegram.org/api/forum#forum-topics)
     * @param int $offset_id [Offsets for pagination, for more info click here](https://core.telegram.org/api/offsets)
     * @param int $add_offset [Offsets for pagination, for more info click here](https://core.telegram.org/api/offsets)
     * @param int $limit Maximum number of results to return, [see pagination](https://core.telegram.org/api/offsets)
     * @param int $max_id Maximum message ID to return, [see pagination](https://core.telegram.org/api/offsets)
     * @param int $min_id Minimum message ID to return, [see pagination](https://core.telegram.org/api/offsets)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.messages', messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.messagesSlice', inexact: array, count: array, next_rate?: array, offset_id_offset?: array, search_flood?: array, messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.channelMessages', inexact: array, pts: array, count: array, offset_id_offset?: array, messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.messagesNotModified', count: array} @see https://docs.madelineproto.xyz/API_docs/types/messages.Messages.html
     * @psalm-impure
     */
    public function getUnreadMentions(array|int|string|null $peer = null, int|null $top_msg_id = null, int|null $offset_id = 0, int|null $add_offset = 0, int|null $limit = 0, int|null $max_id = 0, int|null $min_id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Mark mentions as read
     *
     * @param array|int|string $peer Dialog @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $top_msg_id Mark as read only mentions within the specified [forum topic](https://core.telegram.org/api/forum#forum-topics)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.affectedHistory', pts: int, pts_count: int, offset: int} @see https://docs.madelineproto.xyz/API_docs/types/messages.AffectedHistory.html
     * @psalm-impure
     */
    public function readMentions(array|int|string|null $peer = null, int|null $top_msg_id = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get live location history of a certain user
     *
     * @param array|int|string $peer User @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $limit Maximum number of results to return, [see pagination](https://core.telegram.org/api/offsets)
     * @param list<int|string>|array<never, never> $hash Array of [Hash used for caching, for more info click here](https://core.telegram.org/api/offsets#hash-generation) @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.messages', messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.messagesSlice', inexact: array, count: array, next_rate?: array, offset_id_offset?: array, search_flood?: array, messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.channelMessages', inexact: array, pts: array, count: array, offset_id_offset?: array, messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.messagesNotModified', count: array} @see https://docs.madelineproto.xyz/API_docs/types/messages.Messages.html
     * @psalm-impure
     */
    public function getRecentLocations(array|int|string|null $peer = null, int|null $limit = 0, array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Send an [album or grouped media](https://core.telegram.org/api/files#albums-grouped-media)
     *
     * @param bool $silent Whether to send the album silently (no notification triggered)
     * @param bool $background Send in background?
     * @param bool $clear_draft Whether to clear [drafts](https://core.telegram.org/api/drafts)
     * @param bool $noforwards Only for bots, disallows forwarding and saving of the messages, even if the destination chat doesn't have [content protection](https://telegram.org/blog/protected-content-delete-by-date-and-more) enabled
     * @param bool $update_stickersets_order Whether to move used stickersets to top, [see here for more info on this flag Â»](https://core.telegram.org/api/stickers#recent-stickersets)
     * @param bool $invert_media If set, any eventual webpage preview will be shown on top of the message instead of at the bottom.
     * @param bool $allow_paid_floodskip Bots only: if set, allows sending up to 1000 messages per second, ignoring [broadcasting limits](https://core.telegram.org/bots/faq#how-can-i-message-all-of-my-bot-39s-subscribers-at-once) for a fee of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance.
     * @param array|int|string $peer The destination chat @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param array{_: 'inputReplyToMessage', reply_to_msg_id?: int, top_msg_id?: int, reply_to_peer_id?: array|int|string, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>, quote_offset?: int, monoforum_peer_id?: array|int|string, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, url?: string, webpage_id?: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, email?: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, phone?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, name?: string}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, user_id?: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, date?: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, type: array{_: 'inlineButtonTypeUrl', url?: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url?: string, button_id?: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access?: bool, fwd_text?: string, url?: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url?: string}|array{_: 'inlineButtonTypeCallback', requires_password?: bool, data?: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer?: bool, query?: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id?: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id?: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text?: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary?: bool, bg_danger?: bool, bg_success?: bool, link?: bool}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}>}|array{_: 'inputReplyToStory', peer?: array|int|string, story_id?: int}|array{_: 'inputReplyToMonoForum', monoforum_peer_id?: array|int|string}|array{_: 'inputReplyToEphemeralMessage', id?: int} $reply_to If set, indicates that the message should be sent in reply to the specified message or story. @see https://docs.madelineproto.xyz/API_docs/types/InputReplyTo.html
     * @param list<array{_: 'inputSingleMedia', media?: \danog\MadelineProto\EventHandler\Media|string|array, message?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>}>|array<never, never> $multi_media Array of The medias to send @see https://docs.madelineproto.xyz/API_docs/types/InputSingleMedia.html
     * @param int $schedule_date Scheduled message date for scheduled messages
     * @param array|int|string $send_as Send this message as the specified peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param array{_: 'inputQuickReplyShortcut', shortcut?: string}|array{_: 'inputQuickReplyShortcutId', shortcut_id?: int} $quick_reply_shortcut Add the message to the specified [quick reply shortcut Â»](https://core.telegram.org/api/business#quick-reply-shortcuts), instead. @see https://docs.madelineproto.xyz/API_docs/types/InputQuickReplyShortcut.html
     * @param int $effect Specifies a [message effect Â»](https://core.telegram.org/api/effects) to use for the message.
     * @param int $allow_paid_stars For [paid messages Â»](https://core.telegram.org/api/paid-messages), specifies the amount of [Telegram Stars](https://core.telegram.org/api/stars) the user has agreed to pay in order to send the message.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @param ?string $businessConnectionId Business connection ID, received through an updateBotBusinessConnect update.
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function sendMultiMedia(bool|null $silent = null, bool|null $background = null, bool|null $clear_draft = null, bool|null $noforwards = null, bool|null $update_stickersets_order = null, bool|null $invert_media = null, bool|null $allow_paid_floodskip = null, array|int|string|null $peer = null, array|null $reply_to = null, array $multi_media = [], int|null $schedule_date = null, array|int|string|null $send_as = null, array|null $quick_reply_shortcut = null, int|null $effect = null, int|null $allow_paid_stars = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null, ?string $businessConnectionId = null): array;

    /**
     * Upload encrypted file and associate it to a secret chat (without actually sending it to the chat).
     *
     * @param array $peer The secret chat to associate the file to @see https://docs.madelineproto.xyz/API_docs/types/InputEncryptedChat.html
     * @param mixed $file The file @see https://docs.madelineproto.xyz/API_docs/types/InputEncryptedFile.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'encryptedFileEmpty'}|array{_: 'encryptedFile', id: int, access_hash: int, size: int, dc_id: int, key_fingerprint: int} @see https://docs.madelineproto.xyz/API_docs/types/EncryptedFile.html
     * @psalm-impure
     */
    public function uploadEncryptedFile(array $peer, mixed $file = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Search for stickersets
     *
     * @param bool $exclude_featured Exclude featured stickersets from results
     * @param string $q Query string
     * @param list<int|string>|array<never, never> $hash Array of [Hash used for caching, for more info click here](https://core.telegram.org/api/offsets#hash-generation). @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.foundStickerSetsNotModified'}|array{_: 'messages.foundStickerSets', hash: list<int|string>, sets: list<array{_: 'stickerSetCovered', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}, cover: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'stickerSetMultiCovered', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}, covers: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}|array{_: 'stickerSetFullCovered', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}, packs: list<array{_: 'stickerPack', emoticon: string, documents: list<int>}>, keywords: list<array{_: 'stickerKeyword', document_id: int, keyword: list<string>}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}|array{_: 'stickerSetNoCovered', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}}>} @see https://docs.madelineproto.xyz/API_docs/types/messages.FoundStickerSets.html
     * @psalm-impure
     */
    public function searchStickerSets(bool|null $exclude_featured = null, string|null $q = '', array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get message ranges for saving the user's chat history
     *
     * @param int $takeoutId Takeout ID, generated using account.initTakeoutSession, see [the takeout docs](https://core.telegram.org/api/takeout) for more info.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return list<array{_: 'messageRange', min_id: int, max_id: int}> Array of  @see https://docs.madelineproto.xyz/API_docs/types/MessageRange.html
     * @psalm-impure
     */
    public function getSplitRanges(int $takeoutId, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Manually mark dialog as unread
     *
     * @param array{_: 'inputDialogPeer', peer?: array|int|string}|array{_: 'inputDialogPeerFolder', folder_id?: int}|array{_: 'inputDialogPeerCommunity', community?: array|int|string} $peer Dialog @see https://docs.madelineproto.xyz/API_docs/types/InputDialogPeer.html
     * @param bool $unread Mark as unread/read
     * @param array|int|string $parent_peer If set, must be equal to the ID of a [monoforum](https://core.telegram.org/api/monoforum), and will affect the monoforum topic passed in `peer`. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function markDialogUnread(array $peer, bool|null $unread = null, array|int|string|null $parent_peer = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Get dialogs manually marked as unread
     *
     * @param array|int|string $parent_peer Can be equal to the ID of a monoforum, to fetch monoforum topics manually marked as unread. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return list<array{_: 'dialogPeer', peer: array|int|string}|array{_: 'dialogPeerFolder', folder_id: int}|array{_: 'dialogPeerCommunity', community_id: int}> Array of  @see https://docs.madelineproto.xyz/API_docs/types/DialogPeer.html
     * @psalm-impure
     */
    public function getDialogUnreadMarks(array|int|string|null $parent_peer = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Clear all [drafts](https://core.telegram.org/api/drafts).
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function clearAllDrafts(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Pin a message
     *
     * @param bool $silent Pin the message silently, without triggering a notification
     * @param bool $unpin Whether the message should unpinned or pinned
     * @param bool $pm_oneside Whether the message should only be pinned on the local side of a one-to-one chat
     * @param array|int|string $peer The peer where to pin the message @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $id The message to pin or unpin
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @param ?string $businessConnectionId Business connection ID, received through an updateBotBusinessConnect update.
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function updatePinnedMessage(bool|null $silent = null, bool|null $unpin = null, bool|null $pm_oneside = null, array|int|string|null $peer = null, int|null $id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null, ?string $businessConnectionId = null): array;

    /**
     * Vote in a [poll](https://docs.madelineproto.xyz/API_docs/constructors/poll.html)
     * 
     * Starting from layer 159, the vote will be sent from the peer specified using [messages.saveDefaultSendAs](https://docs.madelineproto.xyz/API_docs/methods/messages.saveDefaultSendAs.html).
     *
     * @param array|int|string $peer The chat where the poll was sent @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $msg_id The message ID of the poll
     * @param list<string>|array<never, never> $options The options that were chosen
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function sendVote(array|int|string|null $peer = null, int|null $msg_id = 0, array $options = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get poll results
     *
     * @param array|int|string $peer Peer where the poll was found @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $msg_id Message ID of poll message
     * @param int $poll_hash 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function getPollResults(array|int|string|null $peer = null, int|null $msg_id = 0, int|null $poll_hash = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get count of online users in a chat
     *
     * @param array|int|string $peer The chat @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'chatOnlines', onlines: int} @see https://docs.madelineproto.xyz/API_docs/types/ChatOnlines.html
     * @psalm-impure
     */
    public function getOnlines(array|int|string|null $peer = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Edit the description of a [group/supergroup/channel](https://core.telegram.org/api/channel).
     *
     * @param array|int|string $peer The [group/supergroup/channel](https://core.telegram.org/api/channel). @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param string $about The new description
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function editChatAbout(array|int|string|null $peer = null, string|null $about = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Edit the default banned rights of a [channel/supergroup/group](https://core.telegram.org/api/channel).
     *
     * @param array{_: 'chatBannedRights', view_messages?: bool, send_messages?: bool, send_media?: bool, send_stickers?: bool, send_gifs?: bool, send_games?: bool, send_inline?: bool, embed_links?: bool, send_polls?: bool, change_info?: bool, invite_users?: bool, pin_messages?: bool, manage_topics?: bool, send_photos?: bool, send_videos?: bool, send_roundvideos?: bool, send_audios?: bool, send_voices?: bool, send_docs?: bool, send_plain?: bool, edit_rank?: bool, send_reactions?: bool, manage_linked_peers?: bool, until_date?: int} $banned_rights The new global rights @see https://docs.madelineproto.xyz/API_docs/types/ChatBannedRights.html
     * @param array|int|string $peer The peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function editChatDefaultBannedRights(array $banned_rights, array|int|string|null $peer = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get localized [emoji keywords Â»](https://core.telegram.org/api/custom-emoji#emoji-keywords).
     *
     * @param string $lang_code Language code
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'emojiKeywordsDifference', lang_code: string, from_version: int, version: int, keywords: list<array{_: 'emojiKeyword', keyword: string, emoticons: list<string>}|array{_: 'emojiKeywordDeleted', keyword: string, emoticons: list<string>}>} @see https://docs.madelineproto.xyz/API_docs/types/EmojiKeywordsDifference.html
     * @psalm-impure
     */
    public function getEmojiKeywords(string|null $lang_code = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get changed [emoji keywords Â»](https://core.telegram.org/api/custom-emoji#emoji-keywords).
     *
     * @param string $lang_code Language code
     * @param int $from_version Previous stored emoji keyword list `version`
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'emojiKeywordsDifference', lang_code: string, from_version: int, version: int, keywords: list<array{_: 'emojiKeyword', keyword: string, emoticons: list<string>}|array{_: 'emojiKeywordDeleted', keyword: string, emoticons: list<string>}>} @see https://docs.madelineproto.xyz/API_docs/types/EmojiKeywordsDifference.html
     * @psalm-impure
     */
    public function getEmojiKeywordsDifference(string|null $lang_code = '', int|null $from_version = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Obtain a list of related languages that must be used when fetching [emoji keyword lists Â»](https://core.telegram.org/api/custom-emoji#emoji-keywords).
     * 
     * Usually the method will return the passed language codes (if localized) + `en` + some language codes for similar languages (if applicable).
     *
     * @param list<string>|array<never, never> $lang_codes The user's language codes
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return list<array{_: 'emojiLanguage', lang_code: string}> Array of  @see https://docs.madelineproto.xyz/API_docs/types/EmojiLanguage.html
     * @psalm-impure
     */
    public function getEmojiKeywordsLanguages(array $lang_codes = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Returns an HTTP URL which can be used to automatically log in into translation platform and suggest new [emoji keywords Â»](https://core.telegram.org/api/custom-emoji#emoji-keywords). The URL will be valid for 30 seconds after generation.
     *
     * @param string $lang_code Language code for which the emoji keywords will be suggested
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'emojiURL', url: string} @see https://docs.madelineproto.xyz/API_docs/types/EmojiURL.html
     * @psalm-impure
     */
    public function getEmojiURL(string|null $lang_code = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get the number of results that would be found by a [messages.search](https://docs.madelineproto.xyz/API_docs/methods/messages.search.html) call with the same parameters
     *
     * @param array|int|string $peer Peer where to search @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param array|int|string $saved_peer_id Search within the [saved message dialog Â»](https://core.telegram.org/api/saved-messages) with this ID. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $top_msg_id If set, consider only messages within the specified [forum topic](https://core.telegram.org/api/forum#forum-topics)
     * @param list<array{_: 'inputMessagesFilterEmpty'}|array{_: 'inputMessagesFilterPhotos'}|array{_: 'inputMessagesFilterVideo'}|array{_: 'inputMessagesFilterPhotoVideo'}|array{_: 'inputMessagesFilterDocument'}|array{_: 'inputMessagesFilterUrl'}|array{_: 'inputMessagesFilterGif'}|array{_: 'inputMessagesFilterVoice'}|array{_: 'inputMessagesFilterMusic'}|array{_: 'inputMessagesFilterChatPhotos'}|array{_: 'inputMessagesFilterPhoneCalls', missed?: bool}|array{_: 'inputMessagesFilterRoundVoice'}|array{_: 'inputMessagesFilterRoundVideo'}|array{_: 'inputMessagesFilterMyMentions'}|array{_: 'inputMessagesFilterGeo'}|array{_: 'inputMessagesFilterContacts'}|array{_: 'inputMessagesFilterPinned'}|array{_: 'inputMessagesFilterPoll'}>|array<never, never> $filters Array of Search filters @see https://docs.madelineproto.xyz/API_docs/types/MessagesFilter.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return list<array{_: 'messages.searchCounter', filter: array{_: 'inputMessagesFilterEmpty'}|array{_: 'inputMessagesFilterPhotos'}|array{_: 'inputMessagesFilterVideo'}|array{_: 'inputMessagesFilterPhotoVideo'}|array{_: 'inputMessagesFilterDocument'}|array{_: 'inputMessagesFilterUrl'}|array{_: 'inputMessagesFilterGif'}|array{_: 'inputMessagesFilterVoice'}|array{_: 'inputMessagesFilterMusic'}|array{_: 'inputMessagesFilterChatPhotos'}|array{_: 'inputMessagesFilterPhoneCalls', missed: bool}|array{_: 'inputMessagesFilterRoundVoice'}|array{_: 'inputMessagesFilterRoundVideo'}|array{_: 'inputMessagesFilterMyMentions'}|array{_: 'inputMessagesFilterGeo'}|array{_: 'inputMessagesFilterContacts'}|array{_: 'inputMessagesFilterPinned'}|array{_: 'inputMessagesFilterPoll'}, inexact: bool, count: int}> Array of  @see https://docs.madelineproto.xyz/API_docs/types/messages.SearchCounter.html
     * @psalm-impure
     */
    public function getSearchCounters(array|int|string|null $peer = null, array|int|string|null $saved_peer_id = null, int|null $top_msg_id = null, array $filters = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get more info about a Seamless Telegram Login authorization request, for more info [click here Â»](https://core.telegram.org/api/url-authorization)
     *
     * @param array|int|string $peer Peer where the message is located @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $msg_id The message
     * @param int $button_id The ID of the button with the authorization request
     * @param string $url URL used for [link URL authorization, click here for more info Â»](https://core.telegram.org/api/url-authorization#link-url-authorization)
     * @param string $in_app_origin 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'urlAuthResultRequest', request_write_access: bool, request_phone_number: bool, match_codes_first: bool, is_app: bool, request_wallet: bool, bot: array|int|string, domain: string, browser?: string, platform?: string, ip?: string, region?: string, match_codes?: list<string>, user_id_hint?: int, verified_app_name?: string, tc_session_id?: int, tc_client_id?: string}|array{_: 'urlAuthResultAccepted', url?: string}|array{_: 'urlAuthResultDefault'} @see https://docs.madelineproto.xyz/API_docs/types/UrlAuthResult.html
     * @psalm-impure
     */
    public function requestUrlAuth(array|int|string|null $peer = null, int|null $msg_id = null, int|null $button_id = null, string|null $url = null, string|null $in_app_origin = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Use this to accept a Seamless Telegram Login authorization request, for more info [click here Â»](https://core.telegram.org/api/url-authorization)
     *
     * @param bool $write_allowed Set this flag to allow the bot to send messages to you (if requested)
     * @param bool $share_phone_number 
     * @param array|int|string $peer The location of the message @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $msg_id Message ID of the message with the login button
     * @param int $button_id ID of the login button
     * @param string $url URL used for [link URL authorization, click here for more info Â»](https://core.telegram.org/api/url-authorization#link-url-authorization)
     * @param string $match_code 
     * @param array{_: 'inputTonConnectOauthSession', proof: array{_: 'walletOwnershipProof', timestamp?: int, signature?: string}, session_id?: int, challenge_answer?: string, body?: string} $tonconnect_session @see https://docs.madelineproto.xyz/API_docs/types/InputTonConnectOauthSession.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'urlAuthResultRequest', request_write_access: bool, request_phone_number: bool, match_codes_first: bool, is_app: bool, request_wallet: bool, bot: array|int|string, domain: string, browser?: string, platform?: string, ip?: string, region?: string, match_codes?: list<string>, user_id_hint?: int, verified_app_name?: string, tc_session_id?: int, tc_client_id?: string}|array{_: 'urlAuthResultAccepted', url?: string}|array{_: 'urlAuthResultDefault'} @see https://docs.madelineproto.xyz/API_docs/types/UrlAuthResult.html
     * @psalm-impure
     */
    public function acceptUrlAuth(bool|null $write_allowed = null, bool|null $share_phone_number = null, array|int|string|null $peer = null, int|null $msg_id = null, int|null $button_id = null, string|null $url = null, string|null $match_code = null, array|null $tonconnect_session = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Should be called after the user hides the [report spam/add as contact bar](https://core.telegram.org/api/action-bar) of a new chat, effectively prevents the user from executing the actions specified in the [action bar Â»](https://core.telegram.org/api/action-bar).
     *
     * @param array|int|string $peer Peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function hidePeerSettingsBar(array|int|string|null $peer = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Get scheduled messages
     *
     * @param array|int|string $peer Peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param list<int|string>|array<never, never> $hash Array of [Hash used for caching, for more info click here](https://core.telegram.org/api/offsets#hash-generation). <br>To generate the hash, populate the `ids` array with the `id`, `edit_date` (0 if unedited) and `date` (in this order) of the previously returned messages (in order, i.e. `ids = [id1, (edit_date1 ?? 0), date1, id2, (edit_date2 ?? 0), date2, ...]`). @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.messages', messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.messagesSlice', inexact: array, count: array, next_rate?: array, offset_id_offset?: array, search_flood?: array, messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.channelMessages', inexact: array, pts: array, count: array, offset_id_offset?: array, messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.messagesNotModified', count: array} @see https://docs.madelineproto.xyz/API_docs/types/messages.Messages.html
     * @psalm-impure
     */
    public function getScheduledHistory(array|int|string|null $peer = null, array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get scheduled messages
     *
     * @param array|int|string $peer Peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param list<int>|array<never, never> $id IDs of scheduled messages
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.messages', messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.messagesSlice', inexact: array, count: array, next_rate?: array, offset_id_offset?: array, search_flood?: array, messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.channelMessages', inexact: array, pts: array, count: array, offset_id_offset?: array, messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.messagesNotModified', count: array} @see https://docs.madelineproto.xyz/API_docs/types/messages.Messages.html
     * @psalm-impure
     */
    public function getScheduledMessages(array|int|string|null $peer = null, array $id = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Send scheduled messages right away
     *
     * @param array|int|string $peer Peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param list<int>|array<never, never> $id Scheduled message IDs
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function sendScheduledMessages(array|int|string|null $peer = null, array $id = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Delete scheduled messages
     *
     * @param array|int|string $peer Peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param list<int>|array<never, never> $id Scheduled message IDs
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function deleteScheduledMessages(array|int|string|null $peer = null, array $id = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get poll results for non-anonymous polls
     *
     * @param array|int|string $peer Chat where the poll was sent @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $id Message ID
     * @param string $option Get only results for the specified poll `option`
     * @param string $offset Offset for results, taken from the `next_offset` field of [messages.votesList](https://docs.madelineproto.xyz/API_docs/constructors/messages.votesList.html), initially an empty string. <br>Note: if no more results are available, the method call will return an empty `next_offset`; thus, avoid providing the `next_offset` returned in [messages.votesList](https://docs.madelineproto.xyz/API_docs/constructors/messages.votesList.html) if it is empty, to avoid an infinite loop.
     * @param int $limit Number of results to return
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.votesList', count: int, votes: list<array{_: 'messagePeerVote', peer: array|int|string, option: string, date: int}|array{_: 'messagePeerVoteInputOption', peer: array|int|string, date: int}|array{_: 'messagePeerVoteMultiple', peer: array|int|string, options: list<string>, date: int}>, chats: list<array|int|string>, users: list<array|int|string>, next_offset?: string} @see https://docs.madelineproto.xyz/API_docs/types/messages.VotesList.html
     * @psalm-impure
     */
    public function getPollVotes(array|int|string|null $peer = null, int|null $id = 0, string|null $option = null, string|null $offset = null, int|null $limit = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Apply changes to multiple stickersets
     *
     * @param bool $uninstall Uninstall the specified stickersets
     * @param bool $archive Archive the specified stickersets
     * @param bool $unarchive Unarchive the specified stickersets
     * @param list<array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id?: int, access_hash?: int}|array{_: 'inputStickerSetShortName', short_name?: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon?: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}>|array<never, never> $stickersets Array of Stickersets to act upon @see https://docs.madelineproto.xyz/API_docs/types/InputStickerSet.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function toggleStickerSets(bool|null $uninstall = null, bool|null $archive = null, bool|null $unarchive = null, array $stickersets = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Get [folders](https://core.telegram.org/api/folders)
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.dialogFilters', tags_enabled: bool, filters: list<array{_: 'dialogFilter', title: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}, contacts: bool, non_contacts: bool, groups: bool, broadcasts: bool, bots: bool, exclude_muted: bool, exclude_read: bool, exclude_archived: bool, title_noanimate: bool, id: int, emoticon?: string, color?: int, pinned_peers: list<array|int|string>, include_peers: list<array|int|string>, exclude_peers: list<array|int|string>}|array{_: 'dialogFilterDefault'}|array{_: 'dialogFilterChatlist', title: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}, has_my_invites: bool, title_noanimate: bool, id: int, emoticon?: string, color?: int, pinned_peers: list<array|int|string>, include_peers: list<array|int|string>}>} @see https://docs.madelineproto.xyz/API_docs/types/messages.DialogFilters.html
     * @psalm-impure
     */
    public function getDialogFilters(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get [suggested folders](https://core.telegram.org/api/folders)
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return list<array{_: 'dialogFilterSuggested', filter: array{_: 'dialogFilter', title: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}, contacts: bool, non_contacts: bool, groups: bool, broadcasts: bool, bots: bool, exclude_muted: bool, exclude_read: bool, exclude_archived: bool, title_noanimate: bool, id: int, emoticon?: string, color?: int, pinned_peers: list<array|int|string>, include_peers: list<array|int|string>, exclude_peers: list<array|int|string>}|array{_: 'dialogFilterDefault'}|array{_: 'dialogFilterChatlist', title: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}, has_my_invites: bool, title_noanimate: bool, id: int, emoticon?: string, color?: int, pinned_peers: list<array|int|string>, include_peers: list<array|int|string>}, description: string}> Array of  @see https://docs.madelineproto.xyz/API_docs/types/DialogFilterSuggested.html
     * @psalm-impure
     */
    public function getSuggestedDialogFilters(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Update [folder](https://core.telegram.org/api/folders)
     *
     * @param int $id [Folder](https://core.telegram.org/api/folders) ID
     * @param array{_: 'dialogFilter', title: array{_: 'textWithEntities', text?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>}, contacts?: bool, non_contacts?: bool, groups?: bool, broadcasts?: bool, bots?: bool, exclude_muted?: bool, exclude_read?: bool, exclude_archived?: bool, title_noanimate?: bool, id?: int, emoticon?: string, color?: int, pinned_peers?: list<array|int|string>, include_peers?: list<array|int|string>, exclude_peers?: list<array|int|string>}|array{_: 'dialogFilterDefault'}|array{_: 'dialogFilterChatlist', title: array{_: 'textWithEntities', text?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>}, has_my_invites?: bool, title_noanimate?: bool, id?: int, emoticon?: string, color?: int, pinned_peers?: list<array|int|string>, include_peers?: list<array|int|string>} $filter [Folder](https://core.telegram.org/api/folders) info @see https://docs.madelineproto.xyz/API_docs/types/DialogFilter.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function updateDialogFilter(int|null $id = 0, array|null $filter = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Reorder [folders](https://core.telegram.org/api/folders)
     *
     * @param list<int>|array<never, never> $order New [folder](https://core.telegram.org/api/folders) order
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function updateDialogFiltersOrder(array $order = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Method for fetching previously featured stickers
     *
     * @param int $offset Offset
     * @param int $limit Maximum number of results to return, [see pagination](https://core.telegram.org/api/offsets)
     * @param list<int|string>|array<never, never> $hash Array of [Hash used for caching, for more info click here](https://core.telegram.org/api/offsets#hash-generation). @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.featuredStickersNotModified', count: int}|array{_: 'messages.featuredStickers', premium: bool, hash: list<int|string>, count: int, sets: list<array{_: 'stickerSetCovered', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}, cover: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'stickerSetMultiCovered', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}, covers: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}|array{_: 'stickerSetFullCovered', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}, packs: list<array{_: 'stickerPack', emoticon: string, documents: list<int>}>, keywords: list<array{_: 'stickerKeyword', document_id: int, keyword: list<string>}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}|array{_: 'stickerSetNoCovered', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}}>, unread: list<int>} @see https://docs.madelineproto.xyz/API_docs/types/messages.FeaturedStickers.html
     * @psalm-impure
     */
    public function getOldFeaturedStickers(int|null $offset = 0, int|null $limit = 0, array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get messages in a reply thread
     *
     * @param array|int|string $peer Peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $msg_id Message ID
     * @param int $offset_id [Offsets for pagination, for more info click here](https://core.telegram.org/api/offsets)
     * @param int $offset_date [Offsets for pagination, for more info click here](https://core.telegram.org/api/offsets)
     * @param int $add_offset [Offsets for pagination, for more info click here](https://core.telegram.org/api/offsets)
     * @param int $limit Maximum number of results to return, [see pagination](https://core.telegram.org/api/offsets)
     * @param int $max_id If a positive value was transferred, the method will return only messages with ID smaller than max\_id
     * @param int $min_id If a positive value was transferred, the method will return only messages with ID bigger than min\_id
     * @param list<int|string>|array<never, never> $hash Array of [Hash used for caching, for more info click here](https://core.telegram.org/api/offsets#hash-generation) @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.messages', messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.messagesSlice', inexact: array, count: array, next_rate?: array, offset_id_offset?: array, search_flood?: array, messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.channelMessages', inexact: array, pts: array, count: array, offset_id_offset?: array, messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.messagesNotModified', count: array} @see https://docs.madelineproto.xyz/API_docs/types/messages.Messages.html
     * @psalm-impure
     */
    public function getReplies(array|int|string|null $peer = null, int|null $msg_id = 0, int|null $offset_id = 0, int|null $offset_date = 0, int|null $add_offset = 0, int|null $limit = 0, int|null $max_id = 0, int|null $min_id = 0, array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get [discussion message](https://core.telegram.org/api/threads) from the [associated discussion group](https://core.telegram.org/api/discussion) of a channel to show it on top of the comment section, without actually joining the group
     *
     * @param array|int|string $peer [Channel ID](https://core.telegram.org/api/channel) @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $msg_id Message ID
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.discussionMessage', messages: list<array{_: 'messageEmpty', id: int, peer_id?: array|int|string}|array{_: 'message', peer_id: array|int|string, out: bool, mentioned: bool, media_unread: bool, silent: bool, post: bool, from_scheduled: bool, legacy: bool, edit_hide: bool, pinned: bool, noforwards: bool, invert_media: bool, offline: bool, video_processing_pending: bool, paid_suggested_post_stars: bool, paid_suggested_post_ton: bool, id: int, from_id?: array|int|string, from_boosts_applied?: int, from_rank?: string, saved_peer_id?: array|int|string, fwd_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, via_bot_id?: int, via_business_bot_id?: int, guestchat_via_from?: array|int|string, reply_to?: array{_: 'messageReplyHeader', reply_to_scheduled: bool, forum_topic: bool, quote: bool, reply_to_ephemeral: bool, reply_to_msg_id?: int, reply_to_peer_id?: array|int|string, reply_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, reply_media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_to_top_id?: int, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, quote_offset?: int, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, url: string, webpage_id: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, email: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, phone: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, name: string}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, user_id: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, date: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, type: array{_: 'inlineButtonTypeUrl', url: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url: string, button_id: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: bool, fwd_text?: string, url: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url: string}|array{_: 'inlineButtonTypeCallback', requires_password: bool, data: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: bool, query: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: bool, bg_danger: bool, bg_success: bool, link: bool}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}>}|array{_: 'messageReplyStoryHeader', peer: array|int|string, story_id: int}, date: int, message: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_markup?: array, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, views?: int, forwards?: int, replies?: array{_: 'messageReplies', comments: bool, replies: int, replies_pts: int, recent_repliers?: list<array|int|string>, channel_id?: int, max_id?: int, read_max_id?: int}, edit_date?: int, post_author?: string, grouped_id?: int, reactions?: array{_: 'messageReactions', min: bool, can_see_list: bool, reactions_as_tags: bool, results: list<array{_: 'reactionCount', chosen_order?: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}, count: int}>, recent_reactions?: list<array{_: 'messagePeerReaction', peer_id: array|int|string, big: bool, unread: bool, my: bool, date: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}}>, top_reactors?: list<array{_: 'messageReactor', top: bool, my: bool, anonymous: bool, peer_id?: array|int|string, count: int}>}, restriction_reason?: list<array{_: 'restrictionReason', platform: string, reason: string, text: string}>, ttl_period?: int, quick_reply_shortcut_id?: int, effect?: int, factcheck?: array{_: 'factCheck', need_check: bool, country?: string, text?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}, hash: list<int|string>}, report_delivery_until_date?: int, paid_message_stars?: int, suggested_post?: array{_: 'suggestedPost', accepted: bool, rejected: bool, price?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, schedule_date?: int}, schedule_repeat_period?: int, summary_from_language?: string, rich_message?: array{_: 'richMessage', rtl: bool, part: bool, blocks: list<array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, published_date: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, language: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: string}|array{_: 'pageBlockList', items: list<array{_: 'pageListItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, checkbox: bool, checked: bool}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, collapsed: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, spoiler: bool, photo_id: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, autoplay: bool, loop: bool, spoiler: bool, video_id: int}|array{_: 'pageBlockCover', cover: array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, published_date: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, language: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: string}|array{_: 'pageBlockList', items: list<array{_: 'pageListItemText', text: array, checkbox: array, checked: array}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, collapsed: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array, credit: array}, spoiler: bool, photo_id: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array, credit: array}, autoplay: bool, loop: bool, spoiler: bool, video_id: int}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array, credit: array}, full_width: bool, allow_scrolling: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array, credit: array}, audio_id: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, bordered: bool, striped: bool, compact: bool, rows: list<array{_: 'pageTableRow', cells: list<array>}>}|array{_: 'pageBlockOrderedList', reversed: bool, items: list<array{_: 'pageListOrderedItemText', text: array, checkbox: array, checked: array, num?: array, value?: array, type?: array}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, articles: list<array{_: 'pageRelatedArticle', url: array, webpage_id: array, title?: array, description?: array, photo_id?: array, author?: array, published_date?: array}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockMath', source: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: array, long: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockButtonRow', align_left: bool, align_center: bool, align_right: bool, buttons: list<array{_: 'pageButton', text: array, type: array, style?: array}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array, credit: array}, document_id: int}}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, full_width: bool, allow_scrolling: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, audio_id: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, bordered: bool, striped: bool, compact: bool, rows: list<array{_: 'pageTableRow', cells: list<array{_: 'pageTableCell', header: array, align_center: array, align_right: array, valign_middle: array, valign_bottom: array, text?: array, colspan?: array, rowspan?: array}>}>}|array{_: 'pageBlockOrderedList', reversed: bool, items: list<array{_: 'pageListOrderedItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, checkbox: bool, checked: bool, num?: string, value?: int, type?: string}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, articles: list<array{_: 'pageRelatedArticle', url: string, webpage_id: int, title?: string, description?: string, photo_id?: int, author?: string, published_date?: int}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash: int, accuracy_radius?: int}, zoom: int, w: int, h: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockMath', source: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, geo: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: float, long: float, accuracy_radius?: int}, zoom: int, w: int, h: int}|array{_: 'pageBlockButtonRow', align_left: bool, align_center: bool, align_right: bool, buttons: list<array{_: 'pageButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, type: array{_: 'inlineButtonTypeUrl', url: array}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: array, url: array, button_id: array}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: array, fwd_text?: array, url: array, bot?: array}|array{_: 'inlineButtonTypeWebView', url: array}|array{_: 'inlineButtonTypeCallback', requires_password: array, data: array}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: array, query: array, peer_types?: list<array>}|array{_: 'inlineButtonTypeUserProfile', user_id: array}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array}|array{_: 'inlineButtonTypeCopy', copy_text: array}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: array, bg_danger: array, bg_success: array, link: array}}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, document_id: int}>, photos: list<array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}}|array{_: 'messageService', peer_id: array|int|string, out: bool, mentioned: bool, media_unread: bool, reactions_are_possible: bool, silent: bool, post: bool, legacy: bool, id: int, from_id?: array|int|string, saved_peer_id?: array|int|string, reply_to?: array{_: 'messageReplyHeader', reply_to_scheduled: bool, forum_topic: bool, quote: bool, reply_to_ephemeral: bool, reply_to_msg_id?: int, reply_to_peer_id?: array|int|string, reply_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, reply_media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_to_top_id?: int, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, quote_offset?: int, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, url: string, webpage_id: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, email: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, phone: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, name: string}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, user_id: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, date: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, type: array{_: 'inlineButtonTypeUrl', url: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url: string, button_id: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: bool, fwd_text?: string, url: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url: string}|array{_: 'inlineButtonTypeCallback', requires_password: bool, data: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: bool, query: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: bool, bg_danger: bool, bg_success: bool, link: bool}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}>}|array{_: 'messageReplyStoryHeader', peer: array|int|string, story_id: int}, date: int, action: array{_: 'messageActionEmpty'}|array{_: 'messageActionChatCreate', title: string, users: list<int>}|array{_: 'messageActionChatEditTitle', title: string}|array{_: 'messageActionChatEditPhoto', photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'messageActionChatDeletePhoto'}|array{_: 'messageActionChatAddUser', users: list<int>}|array{_: 'messageActionChatDeleteUser', user_id: int}|array{_: 'messageActionChatJoinedByLink', inviter_id: int}|array{_: 'messageActionChannelCreate', title: string}|array{_: 'messageActionChatMigrateTo', channel_id: int}|array{_: 'messageActionChannelMigrateFrom', title: string, chat_id: array|int|string}|array{_: 'messageActionPinMessage'}|array{_: 'messageActionHistoryClear'}|array{_: 'messageActionGameScore', game_id: int, score: int}|array{_: 'messageActionPaymentSentMe', charge: array{_: 'paymentCharge', id: string, provider_charge_id: string}, recurring_init: bool, recurring_used: bool, currency: string, total_amount: int, payload: string, info?: array{_: 'paymentRequestedInfo', name?: string, phone?: string, email?: string, shipping_address?: array{_: 'postAddress', street_line1: string, street_line2: string, city: string, state: string, country_iso2: string, post_code: string}}, shipping_option_id?: string, subscription_until_date?: int}|array{_: 'messageActionPaymentSent', recurring_init: bool, recurring_used: bool, currency: string, total_amount: int, invoice_slug?: string, subscription_until_date?: int}|array{_: 'messageActionPhoneCall', video: bool, call_id: int, reason?: array{_: 'phoneCallDiscardReasonMissed'}|array{_: 'phoneCallDiscardReasonDisconnect'}|array{_: 'phoneCallDiscardReasonHangup'}|array{_: 'phoneCallDiscardReasonBusy'}|array{_: 'phoneCallDiscardReasonMigrateConferenceCall', slug: string}, duration?: int}|array{_: 'messageActionScreenshotTaken'}|array{_: 'messageActionCustomAction', message: string}|array{_: 'messageActionBotAllowed', attach_menu: bool, from_request: bool, domain?: string, app?: array{_: 'botAppNotModified'}|array{_: 'botApp', id: int, access_hash: int, short_name: string, title: string, description: string, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, hash: list<int|string>}}|array{_: 'messageActionSecureValuesSentMe', credentials: array{_: 'secureCredentialsEncrypted', data: string, hash: string, secret: string}, values: list<array{_: 'secureValue', type: array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}, data?: array{_: 'secureData', data: string, data_hash: string, secret: string}, front_side?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, reverse_side?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, selfie?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, translation?: list<array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}>, files?: list<array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}>, plain_data?: array{_: 'securePlainPhone', phone: string}|array{_: 'securePlainEmail', email: string}, hash: string}>}|array{_: 'messageActionSecureValuesSent', types: list<array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}>}|array{_: 'messageActionContactSignUp'}|array{_: 'messageActionGeoProximityReached', from_id: array|int|string, to_id: array|int|string, distance: int}|array{_: 'messageActionGroupCall', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, duration?: int}|array{_: 'messageActionInviteToGroupCall', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, users: list<int>}|array{_: 'messageActionSetMessagesTTL', period: int, auto_setting_from?: int}|array{_: 'messageActionGroupCallScheduled', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, schedule_date: int}|array{_: 'messageActionSetChatTheme', theme: array{_: 'chatTheme', emoticon: string}|array{_: 'chatThemeUniqueGift', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: array, edge_color: array, text_color: array}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array, crafted: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array, name: array, backdrop_id: array, center_color: array, edge_color: array, pattern_color: array, text_color: array}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array, sender_id?: array, date: array, message?: array}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: array, nanos: array}|array{_: 'starsTonAmount', amount: array}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: array, background_emoji_id?: array}|array{_: 'peerColorCollectible', collectible_id: array, gift_emoji_id: array, background_emoji_id: array, accent_color: array, colors: list<array>, dark_accent_color?: array, dark_colors?: list<array>}|array{_: 'inputPeerColorCollectible', collectible_id: array}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, theme_settings: list<array{_: 'themeSettings', base_theme: array{_: 'baseThemeClassic'}|array{_: 'baseThemeDay'}|array{_: 'baseThemeNight'}|array{_: 'baseThemeTinted'}|array{_: 'baseThemeArctic'}, message_colors_animated: bool, accent_color: int, outbox_accent_color?: int, message_colors?: list<int>, wallpaper?: array{_: 'wallPaper', id: array, creator: array, default: array, pattern: array, dark: array, access_hash: array, slug: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, settings?: array}|array{_: 'wallPaperNoFile', id: array, default: array, dark: array, settings?: array}}>}}|array{_: 'messageActionChatJoinedByRequest'}|array{_: 'messageActionWebViewDataSentMe', text: string, data: string}|array{_: 'messageActionWebViewDataSent', text: string}|array{_: 'messageActionGiftPremium', currency: string, amount: int, days: int, crypto_currency?: string, crypto_amount?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}|array{_: 'messageActionTopicCreate', title_missing: bool, title: string, icon_color: int, icon_emoji_id?: int}|array{_: 'messageActionTopicEdit', title?: string, icon_emoji_id?: int, closed?: bool, hidden?: bool}|array{_: 'messageActionSuggestProfilePhoto', photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'messageActionRequestedPeer', button_id: int, peers: list<array|int|string>}|array{_: 'messageActionSetChatWallPaper', wallpaper: array{_: 'wallPaper', id: int, creator: bool, default: bool, pattern: bool, dark: bool, access_hash: int, slug: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, settings?: array{_: 'wallPaperSettings', blur: bool, motion: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string}}|array{_: 'wallPaperNoFile', id: int, default: bool, dark: bool, settings?: array{_: 'wallPaperSettings', blur: bool, motion: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string}}, same: bool, for_both: bool}|array{_: 'messageActionGiftCode', via_giveaway: bool, unclaimed: bool, boost_peer?: array|int|string, days: int, slug: string, currency?: string, amount?: int, crypto_currency?: string, crypto_amount?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}|array{_: 'messageActionGiveawayLaunch', stars?: int}|array{_: 'messageActionGiveawayResults', stars: bool, winners_count: int, unclaimed_count: int}|array{_: 'messageActionBoostApply', boosts: int}|array{_: 'messageActionRequestedPeerSentMe', button_id: int, peers: list<array{_: 'requestedPeerUser', user_id: int, first_name?: string, last_name?: string, username?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'requestedPeerChat', chat_id: array|int|string, title?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'requestedPeerChannel', channel_id: int, title?: string, username?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}>}|array{_: 'messageActionPaymentRefunded', peer: array|int|string, charge: array{_: 'paymentCharge', id: string, provider_charge_id: string}, currency: string, total_amount: int, payload?: string}|array{_: 'messageActionGiftStars', currency: string, amount: int, stars: int, crypto_currency?: string, crypto_amount?: int, transaction_id?: string}|array{_: 'messageActionPrizeStars', boost_peer: array|int|string, unclaimed: bool, stars: int, transaction_id: string, giveaway_msg_id: int}|array{_: 'messageActionStarGift', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, name_hidden: bool, saved: bool, converted: bool, upgraded: bool, refunded: bool, can_upgrade: bool, prepaid_upgrade: bool, upgrade_separate: bool, auction_acquired: bool, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}, convert_stars?: int, upgrade_msg_id?: int, upgrade_stars?: int, from_id?: array|int|string, peer?: array|int|string, saved_id?: int, prepaid_upgrade_hash?: string, gift_msg_id?: int, to_id?: array|int|string, gift_num?: int}|array{_: 'messageActionStarGiftUnique', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, upgrade: bool, transferred: bool, saved: bool, refunded: bool, prepaid_upgrade: bool, assigned: bool, from_offer: bool, craft: bool, name_hidden: bool, can_export_at?: int, transfer_stars?: int, from_id?: array|int|string, peer?: array|int|string, saved_id?: int, resale_amount?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, can_transfer_at?: int, can_resell_at?: int, drop_original_details_stars?: int, can_craft_at?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}|array{_: 'messageActionPaidMessagesRefunded', count: int, stars: int}|array{_: 'messageActionPaidMessagesPrice', broadcast_messages_allowed: bool, stars: int}|array{_: 'messageActionConferenceCall', missed: bool, active: bool, video: bool, call_id: int, duration?: int, other_participants?: list<array|int|string>}|array{_: 'messageActionTodoCompletions', completed: list<int>, incompleted: list<int>}|array{_: 'messageActionTodoAppendTasks', list: list<array{_: 'todoItem', title: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, id: int}>}|array{_: 'messageActionSuggestedPostApproval', rejected: bool, balance_too_low: bool, reject_comment?: string, schedule_date?: int, price?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}}|array{_: 'messageActionSuggestedPostSuccess', price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}}|array{_: 'messageActionSuggestedPostRefund', payer_initiated: bool}|array{_: 'messageActionGiftTon', currency: string, amount: int, crypto_currency: string, crypto_amount: int, transaction_id?: string}|array{_: 'messageActionSuggestBirthday', birthday: array{_: 'birthday', day: int, month: int, year?: int}}|array{_: 'messageActionStarGiftPurchaseOffer', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, accepted: bool, declined: bool, expires_at: int}|array{_: 'messageActionStarGiftPurchaseOfferDeclined', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, expired: bool}|array{_: 'messageActionNewCreatorPending', new_creator_id: int}|array{_: 'messageActionChangeCreator', new_creator_id: int}|array{_: 'messageActionNoForwardsToggle', prev_value: bool, new_value: bool}|array{_: 'messageActionNoForwardsRequest', prev_value: bool, new_value: bool, expired: bool}|array{_: 'messageActionPollAppendAnswer', answer: array{_: 'pollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, option: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, added_by?: array|int|string, date?: int}|array{_: 'inputPollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, media?: \danog\MadelineProto\EventHandler\Media|string|array}}|array{_: 'messageActionPollDeleteAnswer', answer: array{_: 'pollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, option: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, added_by?: array|int|string, date?: int}|array{_: 'inputPollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, media?: \danog\MadelineProto\EventHandler\Media|string|array}}|array{_: 'messageActionManagedBotCreated', bot_id: int}|array{_: 'messageActionChangeCommunity', community_id?: int}|array{_: 'messageActionChatJoinedViaCommunity', community_id: int}|array{_: 'messageActionGramTransfer', comment_encrypted: bool, amount: int, peer_address: string, transaction_id: string, comment?: string}|array{_: 'messageActionWalletTonConnectRequest', accepted: bool, declined: bool, session_id: int, expires: int, topic?: string, trace_id?: string, dapp_name?: string}|array{_: 'messageActionNoPaidMessagesTokenUsed'}, reactions?: array{_: 'messageReactions', min: bool, can_see_list: bool, reactions_as_tags: bool, results: list<array{_: 'reactionCount', chosen_order?: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}, count: int}>, recent_reactions?: list<array{_: 'messagePeerReaction', peer_id: array|int|string, big: bool, unread: bool, my: bool, date: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}}>, top_reactors?: list<array{_: 'messageReactor', top: bool, my: bool, anonymous: bool, peer_id?: array|int|string, count: int}>}, ttl_period?: int}>, max_id?: int, read_inbox_max_id?: int, read_outbox_max_id?: int, unread_count: int, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/messages.DiscussionMessage.html
     * @psalm-impure
     */
    public function getDiscussionMessage(array|int|string|null $peer = null, int|null $msg_id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Mark a [thread](https://core.telegram.org/api/threads) as read
     *
     * @param array|int|string $peer Group ID @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $msg_id ID of message that started the thread
     * @param int $read_max_id ID up to which thread messages were read
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function readDiscussion(array|int|string|null $peer = null, int|null $msg_id = 0, int|null $read_max_id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * [Unpin](https://core.telegram.org/api/pin) all pinned messages
     *
     * @param array|int|string $peer Chat where to unpin @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $top_msg_id [Forum topic](https://core.telegram.org/api/forum#forum-topics) where to unpin
     * @param array|int|string $saved_peer_id If set, must be equal to the ID of a [monoforum topic](https://core.telegram.org/api/monoforum), and will unpin all messages pinned in the passed monoforum topic. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.affectedHistory', pts: int, pts_count: int, offset: int} @see https://docs.madelineproto.xyz/API_docs/types/messages.AffectedHistory.html
     * @psalm-impure
     */
    public function unpinAllMessages(array|int|string|null $peer = null, int|null $top_msg_id = null, array|int|string|null $saved_peer_id = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Delete a [chat](https://core.telegram.org/api/channel)
     *
     * @param array|int|string $chat_id Chat ID @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function deleteChat(array|int|string|null $chat_id = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Delete the entire phone call history.
     *
     * @param bool $revoke Whether to remove phone call history for participants as well
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.affectedFoundMessages', pts: int, pts_count: int, offset: int, messages: list<int>} @see https://docs.madelineproto.xyz/API_docs/types/messages.AffectedFoundMessages.html
     * @psalm-impure
     */
    public function deletePhoneCallHistory(bool|null $revoke = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Obtains information about a chat export file, generated by a foreign chat app, [click here for more info about imported chats Â»](https://core.telegram.org/api/import).
     *
     * @param string $import_head Beginning of the message file; up to 100 lines.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.historyImportParsed', pm: bool, group: bool, title?: string} @see https://docs.madelineproto.xyz/API_docs/types/messages.HistoryImportParsed.html
     * @psalm-impure
     */
    public function checkHistoryImport(string|null $import_head = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Import chat history from a foreign chat app into a specific Telegram chat, [click here for more info about imported chats Â»](https://core.telegram.org/api/import).
     *
     * @param mixed $file A file name or a file URL. You can also use amphp async streams, amphp HTTP response objects, and [much more](https://docs.madelineproto.xyz/docs/FILES.html#downloading-files)!
     * @param array|int|string $peer The Telegram chat where the [history should be imported](https://core.telegram.org/api/import). @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $media_count Number of media files associated with the chat that will be uploaded using [messages.uploadImportedMedia](https://docs.madelineproto.xyz/API_docs/methods/messages.uploadImportedMedia.html).
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.historyImport', id: int} @see https://docs.madelineproto.xyz/API_docs/types/messages.HistoryImport.html
     * @psalm-impure
     */
    public function initHistoryImport(mixed $file, array|int|string|null $peer = null, int|null $media_count = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Upload a media file associated with an [imported chat, click here for more info Â»](https://core.telegram.org/api/import).
     *
     * @param array|int|string $peer The Telegram chat where the media will be imported @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $import_id Identifier of a [history import session](https://core.telegram.org/api/import), returned by [messages.initHistoryImport](https://docs.madelineproto.xyz/API_docs/methods/messages.initHistoryImport.html)
     * @param string $file_name File name
     * @param \danog\MadelineProto\EventHandler\Media|string|array $media Media metadata @see https://docs.madelineproto.xyz/API_docs/types/InputMedia.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}|array{_: 'messageExtendedMedia', media: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaPoll', poll: array{_: 'poll', question: array, id: array, closed: array, public_voters: array, multiple_choice: array, quiz: array, open_answers: array, revoting_disabled: array, shuffle_answers: array, hide_results_until_close: array, creator: array, subscribers_only: array, answers: list<array>, close_period?: array, close_date?: array, countries_iso2?: list<array>, hash: list<array>}, results: array, attached_media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}|array{_: 'storyItem', pinned: array, public: array, close_friends: array, min: array, noforwards: array, edited: array, contacts: array, selected_contacts: array, out: array, id: array, date: array, from_id?: array, fwd_from?: array, expire_date: array, caption?: array, entities?: list<array>, media: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, media_areas?: list<array>, privacy?: list<array>, views?: array, sent_reaction?: array, albums?: list<array>, music?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}|array{_: 'messageExtendedMedia', media: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool} @see https://docs.madelineproto.xyz/API_docs/types/MessageMedia.html
     * @psalm-impure
     */
    public function uploadImportedMedia(array|int|string|null $peer = null, int|null $import_id = 0, string|null $file_name = '', \danog\MadelineProto\EventHandler\Media|array|string|null $media = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Complete the [history import process](https://core.telegram.org/api/import), importing all messages into the chat.  
     * To be called only after initializing the import with [messages.initHistoryImport](https://docs.madelineproto.xyz/API_docs/methods/messages.initHistoryImport.html) and uploading all files using [messages.uploadImportedMedia](https://docs.madelineproto.xyz/API_docs/methods/messages.uploadImportedMedia.html).
     *
     * @param array|int|string $peer The Telegram chat where the messages should be [imported, click here for more info Â»](https://core.telegram.org/api/import) @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $import_id Identifier of a history import session, returned by [messages.initHistoryImport](https://docs.madelineproto.xyz/API_docs/methods/messages.initHistoryImport.html).
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function startHistoryImport(array|int|string|null $peer = null, int|null $import_id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Get info about the chat invites of a specific chat
     *
     * @param bool $revoked Whether to fetch revoked chat invites
     * @param array|int|string $peer Chat @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param array|int|string $admin_id Whether to only fetch chat invites from this admin @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param int $offset_date [Offsets for pagination, for more info click here](https://core.telegram.org/api/offsets)
     * @param string $offset_link [Offsets for pagination, for more info click here](https://core.telegram.org/api/offsets)
     * @param int $limit Maximum number of results to return, [see pagination](https://core.telegram.org/api/offsets)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.exportedChatInvites', count: int, invites: list<array{_: 'chatInviteExported', revoked: bool, permanent: bool, request_needed: bool, link: string, admin_id: int, date: int, start_date?: int, expire_date?: int, usage_limit?: int, usage?: int, requested?: int, subscription_expired?: int, title?: string, subscription_pricing?: array{_: 'starsSubscriptionPricing', period: int, amount: int}}|array{_: 'chatInvitePublicJoinRequests'}>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/messages.ExportedChatInvites.html
     * @psalm-impure
     */
    public function getExportedChatInvites(bool|null $revoked = null, array|int|string|null $peer = null, array|int|string|null $admin_id = null, int|null $offset_date = null, string|null $offset_link = null, int|null $limit = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get info about a chat invite
     *
     * @param array|int|string $peer Chat @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param string $link Invite link
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.exportedChatInvite', invite: array{_: 'chatInviteExported', revoked: bool, permanent: bool, request_needed: bool, link: string, admin_id: int, date: int, start_date?: int, expire_date?: int, usage_limit?: int, usage?: int, requested?: int, subscription_expired?: int, title?: string, subscription_pricing?: array{_: 'starsSubscriptionPricing', period: int, amount: int}}|array{_: 'chatInvitePublicJoinRequests'}, users: list<array|int|string>}|array{_: 'messages.exportedChatInviteReplaced', invite: array{_: 'chatInviteExported', revoked: bool, permanent: bool, request_needed: bool, link: string, admin_id: int, date: int, start_date?: int, expire_date?: int, usage_limit?: int, usage?: int, requested?: int, subscription_expired?: int, title?: string, subscription_pricing?: array{_: 'starsSubscriptionPricing', period: int, amount: int}}|array{_: 'chatInvitePublicJoinRequests'}, new_invite: array{_: 'chatInviteExported', revoked: bool, permanent: bool, request_needed: bool, link: string, admin_id: int, date: int, start_date?: int, expire_date?: int, usage_limit?: int, usage?: int, requested?: int, subscription_expired?: int, title?: string, subscription_pricing?: array{_: 'starsSubscriptionPricing', period: int, amount: int}}|array{_: 'chatInvitePublicJoinRequests'}, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/messages.ExportedChatInvite.html
     * @psalm-impure
     */
    public function getExportedChatInvite(array|int|string|null $peer = null, string|null $link = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Edit an exported chat invite
     *
     * @param bool $revoked Whether to revoke the chat invite
     * @param array|int|string $peer Chat @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param string $link Invite link
     * @param int $expire_date New expiration date
     * @param int $usage_limit Maximum number of users that can join using this link
     * @param bool $request_needed Whether admin confirmation is required before admitting each separate user into the chat
     * @param string $title Description of the invite link, visible only to administrators
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.exportedChatInvite', invite: array{_: 'chatInviteExported', revoked: bool, permanent: bool, request_needed: bool, link: string, admin_id: int, date: int, start_date?: int, expire_date?: int, usage_limit?: int, usage?: int, requested?: int, subscription_expired?: int, title?: string, subscription_pricing?: array{_: 'starsSubscriptionPricing', period: int, amount: int}}|array{_: 'chatInvitePublicJoinRequests'}, users: list<array|int|string>}|array{_: 'messages.exportedChatInviteReplaced', invite: array{_: 'chatInviteExported', revoked: bool, permanent: bool, request_needed: bool, link: string, admin_id: int, date: int, start_date?: int, expire_date?: int, usage_limit?: int, usage?: int, requested?: int, subscription_expired?: int, title?: string, subscription_pricing?: array{_: 'starsSubscriptionPricing', period: int, amount: int}}|array{_: 'chatInvitePublicJoinRequests'}, new_invite: array{_: 'chatInviteExported', revoked: bool, permanent: bool, request_needed: bool, link: string, admin_id: int, date: int, start_date?: int, expire_date?: int, usage_limit?: int, usage?: int, requested?: int, subscription_expired?: int, title?: string, subscription_pricing?: array{_: 'starsSubscriptionPricing', period: int, amount: int}}|array{_: 'chatInvitePublicJoinRequests'}, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/messages.ExportedChatInvite.html
     * @psalm-impure
     */
    public function editExportedChatInvite(bool|null $revoked = null, array|int|string|null $peer = null, string|null $link = '', int|null $expire_date = null, int|null $usage_limit = null, bool|null $request_needed = null, string|null $title = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Delete all revoked chat invites
     *
     * @param array|int|string $peer Chat @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param array|int|string $admin_id ID of the admin that originally generated the revoked chat invites @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function deleteRevokedExportedChatInvites(array|int|string|null $peer = null, array|int|string|null $admin_id = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Delete a chat invite
     *
     * @param array|int|string $peer Peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param string $link Invite link
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function deleteExportedChatInvite(array|int|string|null $peer = null, string|null $link = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Get info about chat invites generated by admins.
     *
     * @param array|int|string $peer Chat @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.chatAdminsWithInvites', admins: list<array{_: 'chatAdminWithInvites', admin_id: int, invites_count: int, revoked_invites_count: int}>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/messages.ChatAdminsWithInvites.html
     * @psalm-impure
     */
    public function getAdminsWithInvites(array|int|string|null $peer = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get info about the users that joined the chat using a specific chat invite
     *
     * @param bool $requested If set, only returns info about users with pending [join requests Â»](https://core.telegram.org/api/invites#join-requests)
     * @param bool $subscription_expired Set this flag if the link is a [Telegram Star subscription link Â»](https://core.telegram.org/api/stars#star-subscriptions) and only members with already expired subscription must be returned.
     * @param array|int|string $peer Chat @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param string $link Invite link
     * @param string $q Search for a user in the pending [join requests Â»](https://core.telegram.org/api/invites#join-requests) list: only available when the `requested` flag is set, cannot be used together with a specific `link`.
     * @param int $offset_date [Offsets for pagination, for more info click here](https://core.telegram.org/api/offsets)
     * @param array|int|string $offset_user User ID for [pagination](https://core.telegram.org/api/offsets): if set, `offset_date` must also be set. @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param int $limit Maximum number of results to return, [see pagination](https://core.telegram.org/api/offsets)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.chatInviteImporters', count: int, importers: list<array{_: 'chatInviteImporter', requested: bool, via_chatlist: bool, user_id: int, date: int, about?: string, approved_by?: int}>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/messages.ChatInviteImporters.html
     * @psalm-impure
     */
    public function getChatInviteImporters(bool|null $requested = null, bool|null $subscription_expired = null, array|int|string|null $peer = null, string|null $link = null, string|null $q = null, int|null $offset_date = 0, array|int|string|null $offset_user = null, int|null $limit = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Set maximum Time-To-Live of all messages in the specified chat
     *
     * @param array|int|string $peer The dialog @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $period Automatically delete all messages sent in the chat after this many seconds
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function setHistoryTTL(array|int|string|null $peer = null, int|null $period = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Check whether chat history exported from another chat app can be [imported into a specific Telegram chat, click here for more info Â»](https://core.telegram.org/api/import).
     * 
     * If the check succeeds, and no RPC errors are returned, a [messages.CheckedHistoryImportPeer](https://docs.madelineproto.xyz/API_docs/types/messages.CheckedHistoryImportPeer.html) constructor will be returned, with a confirmation text to be shown to the user, before actually initializing the import.
     *
     * @param array|int|string $peer The chat where we want to [import history Â»](https://core.telegram.org/api/import). @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.checkedHistoryImportPeer', confirm_text: string} @see https://docs.madelineproto.xyz/API_docs/types/messages.CheckedHistoryImportPeer.html
     * @psalm-impure
     */
    public function checkHistoryImportPeer(array|int|string|null $peer = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Change the chat theme of a certain chat, see [here Â»](https://core.telegram.org/api/themes#chat-themes) for more info.
     *
     * @param array|int|string $peer Private chat where to change theme @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param array{_: 'inputChatThemeEmpty'}|array{_: 'inputChatTheme', emoticon?: string}|array{_: 'inputChatThemeUniqueGift', slug?: string} $theme The theme to set. @see https://docs.madelineproto.xyz/API_docs/types/InputChatTheme.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function setChatTheme(array|int|string|null $peer = null, array|null $theme = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get which users read a specific message: only available for groups and supergroups with less than [`chat_read_mark_size_threshold` members](https://core.telegram.org/api/config#chat-read-mark-size-threshold), read receipts will be stored for [`chat_read_mark_expire_period` seconds after the message was sent](https://core.telegram.org/api/config#chat-read-mark-expire-period), see [client configuration for more info Â»](https://core.telegram.org/api/config#client-configuration).
     *
     * @param array|int|string $peer Dialog @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $msg_id Message ID
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return list<array{_: 'readParticipantDate', user_id: int, date: int}> Array of  @see https://docs.madelineproto.xyz/API_docs/types/ReadParticipantDate.html
     * @psalm-impure
     */
    public function getMessageReadParticipants(array|int|string|null $peer = null, int|null $msg_id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Returns information about the next messages of the specified type in the chat split by days.
     * 
     * Returns the results in reverse chronological order.  
     * Can return partial results for the last returned day.
     *
     * @param array{_: 'inputMessagesFilterEmpty'}|array{_: 'inputMessagesFilterPhotos'}|array{_: 'inputMessagesFilterVideo'}|array{_: 'inputMessagesFilterPhotoVideo'}|array{_: 'inputMessagesFilterDocument'}|array{_: 'inputMessagesFilterUrl'}|array{_: 'inputMessagesFilterGif'}|array{_: 'inputMessagesFilterVoice'}|array{_: 'inputMessagesFilterMusic'}|array{_: 'inputMessagesFilterChatPhotos'}|array{_: 'inputMessagesFilterPhoneCalls', missed?: bool}|array{_: 'inputMessagesFilterRoundVoice'}|array{_: 'inputMessagesFilterRoundVideo'}|array{_: 'inputMessagesFilterMyMentions'}|array{_: 'inputMessagesFilterGeo'}|array{_: 'inputMessagesFilterContacts'}|array{_: 'inputMessagesFilterPinned'}|array{_: 'inputMessagesFilterPoll'} $filter Message filter, [inputMessagesFilterEmpty](https://docs.madelineproto.xyz/API_docs/constructors/inputMessagesFilterEmpty.html), [inputMessagesFilterMyMentions](https://docs.madelineproto.xyz/API_docs/constructors/inputMessagesFilterMyMentions.html) filters are not supported by this method. @see https://docs.madelineproto.xyz/API_docs/types/MessagesFilter.html
     * @param array|int|string $peer Peer where to search @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param array|int|string $saved_peer_id Search within the [saved message dialog Â»](https://core.telegram.org/api/saved-messages) with this ID. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $offset_id [Offsets for pagination, for more info click here](https://core.telegram.org/api/offsets)
     * @param int $offset_date [Offsets for pagination, for more info click here](https://core.telegram.org/api/offsets)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.searchResultsCalendar', inexact: bool, count: int, min_date: int, min_msg_id: int, offset_id_offset?: int, periods: list<array{_: 'searchResultsCalendarPeriod', date: int, min_msg_id: int, max_msg_id: int, count: int}>, messages: list<array{_: 'messageEmpty', id: int, peer_id?: array|int|string}|array{_: 'message', peer_id: array|int|string, out: bool, mentioned: bool, media_unread: bool, silent: bool, post: bool, from_scheduled: bool, legacy: bool, edit_hide: bool, pinned: bool, noforwards: bool, invert_media: bool, offline: bool, video_processing_pending: bool, paid_suggested_post_stars: bool, paid_suggested_post_ton: bool, id: int, from_id?: array|int|string, from_boosts_applied?: int, from_rank?: string, saved_peer_id?: array|int|string, fwd_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, via_bot_id?: int, via_business_bot_id?: int, guestchat_via_from?: array|int|string, reply_to?: array{_: 'messageReplyHeader', reply_to_scheduled: bool, forum_topic: bool, quote: bool, reply_to_ephemeral: bool, reply_to_msg_id?: int, reply_to_peer_id?: array|int|string, reply_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, reply_media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_to_top_id?: int, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, quote_offset?: int, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, url: string, webpage_id: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, email: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, phone: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, name: string}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, user_id: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, date: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, type: array{_: 'inlineButtonTypeUrl', url: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url: string, button_id: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: bool, fwd_text?: string, url: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url: string}|array{_: 'inlineButtonTypeCallback', requires_password: bool, data: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: bool, query: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: bool, bg_danger: bool, bg_success: bool, link: bool}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}>}|array{_: 'messageReplyStoryHeader', peer: array|int|string, story_id: int}, date: int, message: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_markup?: array, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, views?: int, forwards?: int, replies?: array{_: 'messageReplies', comments: bool, replies: int, replies_pts: int, recent_repliers?: list<array|int|string>, channel_id?: int, max_id?: int, read_max_id?: int}, edit_date?: int, post_author?: string, grouped_id?: int, reactions?: array{_: 'messageReactions', min: bool, can_see_list: bool, reactions_as_tags: bool, results: list<array{_: 'reactionCount', chosen_order?: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}, count: int}>, recent_reactions?: list<array{_: 'messagePeerReaction', peer_id: array|int|string, big: bool, unread: bool, my: bool, date: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}}>, top_reactors?: list<array{_: 'messageReactor', top: bool, my: bool, anonymous: bool, peer_id?: array|int|string, count: int}>}, restriction_reason?: list<array{_: 'restrictionReason', platform: string, reason: string, text: string}>, ttl_period?: int, quick_reply_shortcut_id?: int, effect?: int, factcheck?: array{_: 'factCheck', need_check: bool, country?: string, text?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}, hash: list<int|string>}, report_delivery_until_date?: int, paid_message_stars?: int, suggested_post?: array{_: 'suggestedPost', accepted: bool, rejected: bool, price?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, schedule_date?: int}, schedule_repeat_period?: int, summary_from_language?: string, rich_message?: array{_: 'richMessage', rtl: bool, part: bool, blocks: list<array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, published_date: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, language: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: string}|array{_: 'pageBlockList', items: list<array{_: 'pageListItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, checkbox: bool, checked: bool}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, collapsed: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, spoiler: bool, photo_id: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, autoplay: bool, loop: bool, spoiler: bool, video_id: int}|array{_: 'pageBlockCover', cover: array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, published_date: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, language: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: string}|array{_: 'pageBlockList', items: list<array{_: 'pageListItemText', text: array, checkbox: array, checked: array}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, collapsed: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array, credit: array}, spoiler: bool, photo_id: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array, credit: array}, autoplay: bool, loop: bool, spoiler: bool, video_id: int}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array, credit: array}, full_width: bool, allow_scrolling: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array, credit: array}, audio_id: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, bordered: bool, striped: bool, compact: bool, rows: list<array{_: 'pageTableRow', cells: list<array>}>}|array{_: 'pageBlockOrderedList', reversed: bool, items: list<array{_: 'pageListOrderedItemText', text: array, checkbox: array, checked: array, num?: array, value?: array, type?: array}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, articles: list<array{_: 'pageRelatedArticle', url: array, webpage_id: array, title?: array, description?: array, photo_id?: array, author?: array, published_date?: array}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockMath', source: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: array, long: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockButtonRow', align_left: bool, align_center: bool, align_right: bool, buttons: list<array{_: 'pageButton', text: array, type: array, style?: array}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array, credit: array}, document_id: int}}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, full_width: bool, allow_scrolling: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, audio_id: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, bordered: bool, striped: bool, compact: bool, rows: list<array{_: 'pageTableRow', cells: list<array{_: 'pageTableCell', header: array, align_center: array, align_right: array, valign_middle: array, valign_bottom: array, text?: array, colspan?: array, rowspan?: array}>}>}|array{_: 'pageBlockOrderedList', reversed: bool, items: list<array{_: 'pageListOrderedItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, checkbox: bool, checked: bool, num?: string, value?: int, type?: string}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, articles: list<array{_: 'pageRelatedArticle', url: string, webpage_id: int, title?: string, description?: string, photo_id?: int, author?: string, published_date?: int}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash: int, accuracy_radius?: int}, zoom: int, w: int, h: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockMath', source: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, geo: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: float, long: float, accuracy_radius?: int}, zoom: int, w: int, h: int}|array{_: 'pageBlockButtonRow', align_left: bool, align_center: bool, align_right: bool, buttons: list<array{_: 'pageButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, type: array{_: 'inlineButtonTypeUrl', url: array}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: array, url: array, button_id: array}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: array, fwd_text?: array, url: array, bot?: array}|array{_: 'inlineButtonTypeWebView', url: array}|array{_: 'inlineButtonTypeCallback', requires_password: array, data: array}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: array, query: array, peer_types?: list<array>}|array{_: 'inlineButtonTypeUserProfile', user_id: array}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array}|array{_: 'inlineButtonTypeCopy', copy_text: array}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: array, bg_danger: array, bg_success: array, link: array}}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, document_id: int}>, photos: list<array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}}|array{_: 'messageService', peer_id: array|int|string, out: bool, mentioned: bool, media_unread: bool, reactions_are_possible: bool, silent: bool, post: bool, legacy: bool, id: int, from_id?: array|int|string, saved_peer_id?: array|int|string, reply_to?: array{_: 'messageReplyHeader', reply_to_scheduled: bool, forum_topic: bool, quote: bool, reply_to_ephemeral: bool, reply_to_msg_id?: int, reply_to_peer_id?: array|int|string, reply_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, reply_media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_to_top_id?: int, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, quote_offset?: int, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, url: string, webpage_id: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, email: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, phone: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, name: string}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, user_id: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, date: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, type: array{_: 'inlineButtonTypeUrl', url: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url: string, button_id: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: bool, fwd_text?: string, url: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url: string}|array{_: 'inlineButtonTypeCallback', requires_password: bool, data: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: bool, query: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: bool, bg_danger: bool, bg_success: bool, link: bool}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}>}|array{_: 'messageReplyStoryHeader', peer: array|int|string, story_id: int}, date: int, action: array{_: 'messageActionEmpty'}|array{_: 'messageActionChatCreate', title: string, users: list<int>}|array{_: 'messageActionChatEditTitle', title: string}|array{_: 'messageActionChatEditPhoto', photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'messageActionChatDeletePhoto'}|array{_: 'messageActionChatAddUser', users: list<int>}|array{_: 'messageActionChatDeleteUser', user_id: int}|array{_: 'messageActionChatJoinedByLink', inviter_id: int}|array{_: 'messageActionChannelCreate', title: string}|array{_: 'messageActionChatMigrateTo', channel_id: int}|array{_: 'messageActionChannelMigrateFrom', title: string, chat_id: array|int|string}|array{_: 'messageActionPinMessage'}|array{_: 'messageActionHistoryClear'}|array{_: 'messageActionGameScore', game_id: int, score: int}|array{_: 'messageActionPaymentSentMe', charge: array{_: 'paymentCharge', id: string, provider_charge_id: string}, recurring_init: bool, recurring_used: bool, currency: string, total_amount: int, payload: string, info?: array{_: 'paymentRequestedInfo', name?: string, phone?: string, email?: string, shipping_address?: array{_: 'postAddress', street_line1: string, street_line2: string, city: string, state: string, country_iso2: string, post_code: string}}, shipping_option_id?: string, subscription_until_date?: int}|array{_: 'messageActionPaymentSent', recurring_init: bool, recurring_used: bool, currency: string, total_amount: int, invoice_slug?: string, subscription_until_date?: int}|array{_: 'messageActionPhoneCall', video: bool, call_id: int, reason?: array{_: 'phoneCallDiscardReasonMissed'}|array{_: 'phoneCallDiscardReasonDisconnect'}|array{_: 'phoneCallDiscardReasonHangup'}|array{_: 'phoneCallDiscardReasonBusy'}|array{_: 'phoneCallDiscardReasonMigrateConferenceCall', slug: string}, duration?: int}|array{_: 'messageActionScreenshotTaken'}|array{_: 'messageActionCustomAction', message: string}|array{_: 'messageActionBotAllowed', attach_menu: bool, from_request: bool, domain?: string, app?: array{_: 'botAppNotModified'}|array{_: 'botApp', id: int, access_hash: int, short_name: string, title: string, description: string, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, hash: list<int|string>}}|array{_: 'messageActionSecureValuesSentMe', credentials: array{_: 'secureCredentialsEncrypted', data: string, hash: string, secret: string}, values: list<array{_: 'secureValue', type: array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}, data?: array{_: 'secureData', data: string, data_hash: string, secret: string}, front_side?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, reverse_side?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, selfie?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, translation?: list<array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}>, files?: list<array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}>, plain_data?: array{_: 'securePlainPhone', phone: string}|array{_: 'securePlainEmail', email: string}, hash: string}>}|array{_: 'messageActionSecureValuesSent', types: list<array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}>}|array{_: 'messageActionContactSignUp'}|array{_: 'messageActionGeoProximityReached', from_id: array|int|string, to_id: array|int|string, distance: int}|array{_: 'messageActionGroupCall', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, duration?: int}|array{_: 'messageActionInviteToGroupCall', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, users: list<int>}|array{_: 'messageActionSetMessagesTTL', period: int, auto_setting_from?: int}|array{_: 'messageActionGroupCallScheduled', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, schedule_date: int}|array{_: 'messageActionSetChatTheme', theme: array{_: 'chatTheme', emoticon: string}|array{_: 'chatThemeUniqueGift', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: array, edge_color: array, text_color: array}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array, crafted: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array, name: array, backdrop_id: array, center_color: array, edge_color: array, pattern_color: array, text_color: array}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array, sender_id?: array, date: array, message?: array}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: array, nanos: array}|array{_: 'starsTonAmount', amount: array}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: array, background_emoji_id?: array}|array{_: 'peerColorCollectible', collectible_id: array, gift_emoji_id: array, background_emoji_id: array, accent_color: array, colors: list<array>, dark_accent_color?: array, dark_colors?: list<array>}|array{_: 'inputPeerColorCollectible', collectible_id: array}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, theme_settings: list<array{_: 'themeSettings', base_theme: array{_: 'baseThemeClassic'}|array{_: 'baseThemeDay'}|array{_: 'baseThemeNight'}|array{_: 'baseThemeTinted'}|array{_: 'baseThemeArctic'}, message_colors_animated: bool, accent_color: int, outbox_accent_color?: int, message_colors?: list<int>, wallpaper?: array{_: 'wallPaper', id: array, creator: array, default: array, pattern: array, dark: array, access_hash: array, slug: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, settings?: array}|array{_: 'wallPaperNoFile', id: array, default: array, dark: array, settings?: array}}>}}|array{_: 'messageActionChatJoinedByRequest'}|array{_: 'messageActionWebViewDataSentMe', text: string, data: string}|array{_: 'messageActionWebViewDataSent', text: string}|array{_: 'messageActionGiftPremium', currency: string, amount: int, days: int, crypto_currency?: string, crypto_amount?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}|array{_: 'messageActionTopicCreate', title_missing: bool, title: string, icon_color: int, icon_emoji_id?: int}|array{_: 'messageActionTopicEdit', title?: string, icon_emoji_id?: int, closed?: bool, hidden?: bool}|array{_: 'messageActionSuggestProfilePhoto', photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'messageActionRequestedPeer', button_id: int, peers: list<array|int|string>}|array{_: 'messageActionSetChatWallPaper', wallpaper: array{_: 'wallPaper', id: int, creator: bool, default: bool, pattern: bool, dark: bool, access_hash: int, slug: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, settings?: array{_: 'wallPaperSettings', blur: bool, motion: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string}}|array{_: 'wallPaperNoFile', id: int, default: bool, dark: bool, settings?: array{_: 'wallPaperSettings', blur: bool, motion: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string}}, same: bool, for_both: bool}|array{_: 'messageActionGiftCode', via_giveaway: bool, unclaimed: bool, boost_peer?: array|int|string, days: int, slug: string, currency?: string, amount?: int, crypto_currency?: string, crypto_amount?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}|array{_: 'messageActionGiveawayLaunch', stars?: int}|array{_: 'messageActionGiveawayResults', stars: bool, winners_count: int, unclaimed_count: int}|array{_: 'messageActionBoostApply', boosts: int}|array{_: 'messageActionRequestedPeerSentMe', button_id: int, peers: list<array{_: 'requestedPeerUser', user_id: int, first_name?: string, last_name?: string, username?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'requestedPeerChat', chat_id: array|int|string, title?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'requestedPeerChannel', channel_id: int, title?: string, username?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}>}|array{_: 'messageActionPaymentRefunded', peer: array|int|string, charge: array{_: 'paymentCharge', id: string, provider_charge_id: string}, currency: string, total_amount: int, payload?: string}|array{_: 'messageActionGiftStars', currency: string, amount: int, stars: int, crypto_currency?: string, crypto_amount?: int, transaction_id?: string}|array{_: 'messageActionPrizeStars', boost_peer: array|int|string, unclaimed: bool, stars: int, transaction_id: string, giveaway_msg_id: int}|array{_: 'messageActionStarGift', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, name_hidden: bool, saved: bool, converted: bool, upgraded: bool, refunded: bool, can_upgrade: bool, prepaid_upgrade: bool, upgrade_separate: bool, auction_acquired: bool, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}, convert_stars?: int, upgrade_msg_id?: int, upgrade_stars?: int, from_id?: array|int|string, peer?: array|int|string, saved_id?: int, prepaid_upgrade_hash?: string, gift_msg_id?: int, to_id?: array|int|string, gift_num?: int}|array{_: 'messageActionStarGiftUnique', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, upgrade: bool, transferred: bool, saved: bool, refunded: bool, prepaid_upgrade: bool, assigned: bool, from_offer: bool, craft: bool, name_hidden: bool, can_export_at?: int, transfer_stars?: int, from_id?: array|int|string, peer?: array|int|string, saved_id?: int, resale_amount?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, can_transfer_at?: int, can_resell_at?: int, drop_original_details_stars?: int, can_craft_at?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}|array{_: 'messageActionPaidMessagesRefunded', count: int, stars: int}|array{_: 'messageActionPaidMessagesPrice', broadcast_messages_allowed: bool, stars: int}|array{_: 'messageActionConferenceCall', missed: bool, active: bool, video: bool, call_id: int, duration?: int, other_participants?: list<array|int|string>}|array{_: 'messageActionTodoCompletions', completed: list<int>, incompleted: list<int>}|array{_: 'messageActionTodoAppendTasks', list: list<array{_: 'todoItem', title: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, id: int}>}|array{_: 'messageActionSuggestedPostApproval', rejected: bool, balance_too_low: bool, reject_comment?: string, schedule_date?: int, price?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}}|array{_: 'messageActionSuggestedPostSuccess', price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}}|array{_: 'messageActionSuggestedPostRefund', payer_initiated: bool}|array{_: 'messageActionGiftTon', currency: string, amount: int, crypto_currency: string, crypto_amount: int, transaction_id?: string}|array{_: 'messageActionSuggestBirthday', birthday: array{_: 'birthday', day: int, month: int, year?: int}}|array{_: 'messageActionStarGiftPurchaseOffer', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, accepted: bool, declined: bool, expires_at: int}|array{_: 'messageActionStarGiftPurchaseOfferDeclined', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, expired: bool}|array{_: 'messageActionNewCreatorPending', new_creator_id: int}|array{_: 'messageActionChangeCreator', new_creator_id: int}|array{_: 'messageActionNoForwardsToggle', prev_value: bool, new_value: bool}|array{_: 'messageActionNoForwardsRequest', prev_value: bool, new_value: bool, expired: bool}|array{_: 'messageActionPollAppendAnswer', answer: array{_: 'pollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, option: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, added_by?: array|int|string, date?: int}|array{_: 'inputPollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, media?: \danog\MadelineProto\EventHandler\Media|string|array}}|array{_: 'messageActionPollDeleteAnswer', answer: array{_: 'pollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, option: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, added_by?: array|int|string, date?: int}|array{_: 'inputPollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, media?: \danog\MadelineProto\EventHandler\Media|string|array}}|array{_: 'messageActionManagedBotCreated', bot_id: int}|array{_: 'messageActionChangeCommunity', community_id?: int}|array{_: 'messageActionChatJoinedViaCommunity', community_id: int}|array{_: 'messageActionGramTransfer', comment_encrypted: bool, amount: int, peer_address: string, transaction_id: string, comment?: string}|array{_: 'messageActionWalletTonConnectRequest', accepted: bool, declined: bool, session_id: int, expires: int, topic?: string, trace_id?: string, dapp_name?: string}|array{_: 'messageActionNoPaidMessagesTokenUsed'}, reactions?: array{_: 'messageReactions', min: bool, can_see_list: bool, reactions_as_tags: bool, results: list<array{_: 'reactionCount', chosen_order?: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}, count: int}>, recent_reactions?: list<array{_: 'messagePeerReaction', peer_id: array|int|string, big: bool, unread: bool, my: bool, date: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}}>, top_reactors?: list<array{_: 'messageReactor', top: bool, my: bool, anonymous: bool, peer_id?: array|int|string, count: int}>}, ttl_period?: int}>, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/messages.SearchResultsCalendar.html
     * @psalm-impure
     */
    public function getSearchResultsCalendar(array $filter, array|int|string|null $peer = null, array|int|string|null $saved_peer_id = null, int|null $offset_id = 0, int|null $offset_date = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Returns sparse positions of messages of the specified type in the chat to be used for shared media scroll implementation.
     * 
     * Returns the results in reverse chronological order (i.e., in order of decreasing message\_id).
     *
     * @param array{_: 'inputMessagesFilterEmpty'}|array{_: 'inputMessagesFilterPhotos'}|array{_: 'inputMessagesFilterVideo'}|array{_: 'inputMessagesFilterPhotoVideo'}|array{_: 'inputMessagesFilterDocument'}|array{_: 'inputMessagesFilterUrl'}|array{_: 'inputMessagesFilterGif'}|array{_: 'inputMessagesFilterVoice'}|array{_: 'inputMessagesFilterMusic'}|array{_: 'inputMessagesFilterChatPhotos'}|array{_: 'inputMessagesFilterPhoneCalls', missed?: bool}|array{_: 'inputMessagesFilterRoundVoice'}|array{_: 'inputMessagesFilterRoundVideo'}|array{_: 'inputMessagesFilterMyMentions'}|array{_: 'inputMessagesFilterGeo'}|array{_: 'inputMessagesFilterContacts'}|array{_: 'inputMessagesFilterPinned'}|array{_: 'inputMessagesFilterPoll'} $filter Message filter, [inputMessagesFilterEmpty](https://docs.madelineproto.xyz/API_docs/constructors/inputMessagesFilterEmpty.html), [inputMessagesFilterMyMentions](https://docs.madelineproto.xyz/API_docs/constructors/inputMessagesFilterMyMentions.html) filters are not supported by this method. @see https://docs.madelineproto.xyz/API_docs/types/MessagesFilter.html
     * @param array|int|string $peer Peer where to search @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param array|int|string $saved_peer_id Search within the [saved message dialog Â»](https://core.telegram.org/api/saved-messages) with this ID. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $offset_id [Offsets for pagination, for more info click here](https://core.telegram.org/api/offsets)
     * @param int $limit Maximum number of results to return, [see pagination](https://core.telegram.org/api/offsets)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.searchResultsPositions', count: int, positions: list<array{_: 'searchResultPosition', msg_id: int, date: int, offset: int}>} @see https://docs.madelineproto.xyz/API_docs/types/messages.SearchResultsPositions.html
     * @psalm-impure
     */
    public function getSearchResultsPositions(array $filter, array|int|string|null $peer = null, array|int|string|null $saved_peer_id = null, int|null $offset_id = 0, int|null $limit = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Dismiss or approve a chat [join request](https://core.telegram.org/api/invites#join-requests) related to a specific chat or channel.
     *
     * @param bool $approved Whether to dismiss or approve the chat [join request Â»](https://core.telegram.org/api/invites#join-requests)
     * @param array|int|string $peer The chat or channel @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param array|int|string $user_id The user whose [join request Â»](https://core.telegram.org/api/invites#join-requests) should be dismissed or approved @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function hideChatJoinRequest(bool|null $approved = null, array|int|string|null $peer = null, array|int|string|null $user_id = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Dismiss or approve all [join requests](https://core.telegram.org/api/invites#join-requests) related to a specific chat or channel.
     *
     * @param bool $approved Whether to dismiss or approve all chat [join requests Â»](https://core.telegram.org/api/invites#join-requests)
     * @param array|int|string $peer The chat or channel @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param string $link Only dismiss or approve [join requests Â»](https://core.telegram.org/api/invites#join-requests) initiated using this invite link
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function hideAllChatJoinRequests(bool|null $approved = null, array|int|string|null $peer = null, string|null $link = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Enable or disable [content protection](https://telegram.org/blog/protected-content-delete-by-date-and-more) on a channel or chat
     *
     * @param bool $enabled Enable or disable content protection
     * @param array|int|string $peer The chat or channel @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $request_msg_id 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function toggleNoForwards(bool $enabled, array|int|string|null $peer = null, int|null $request_msg_id = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Change the default peer that should be used when sending messages, reactions, poll votes to a specific group
     *
     * @param array|int|string $peer Group @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param array|int|string $send_as The default peer that should be used when sending messages to the group @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function saveDefaultSendAs(array|int|string|null $peer = null, array|int|string|null $send_as = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * React to message.
     * 
     * Starting from layer 159, the reaction will be sent from the peer specified using [messages.saveDefaultSendAs](https://docs.madelineproto.xyz/API_docs/methods/messages.saveDefaultSendAs.html).
     *
     * @param bool $big Whether a bigger and longer reaction should be shown
     * @param bool $add_to_recent Whether to add this reaction to the [recent reactions list Â»](https://core.telegram.org/api/reactions#recent-reactions).
     * @param array|int|string $peer Peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $msg_id Message ID to react to
     * @param list<array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon?: string}|array{_: 'reactionCustomEmoji', document_id?: int}|array{_: 'reactionPaid'}> $reaction Array of A list of reactions (doesn't accept [reactionPaid](https://docs.madelineproto.xyz/API_docs/constructors/reactionPaid.html) constructors, use [messages.sendPaidReaction](https://docs.madelineproto.xyz/API_docs/methods/messages.sendPaidReaction.html) to send paid reactions, instead). @see https://docs.madelineproto.xyz/API_docs/types/Reaction.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function sendReaction(bool|null $big = null, bool|null $add_to_recent = null, array|int|string|null $peer = null, int|null $msg_id = 0, array|null $reaction = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get [message reactions Â»](https://core.telegram.org/api/reactions)
     *
     * @param array|int|string $peer Peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param list<int>|array<never, never> $id Message IDs
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function getMessagesReactions(array|int|string|null $peer = null, array $id = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get [message reaction](https://core.telegram.org/api/reactions) list, along with the sender of each reaction.
     *
     * @param array|int|string $peer Peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $id Message ID
     * @param array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon?: string}|array{_: 'reactionCustomEmoji', document_id?: int}|array{_: 'reactionPaid'} $reaction Get only reactions of this type @see https://docs.madelineproto.xyz/API_docs/types/Reaction.html
     * @param string $offset Offset for pagination (taken from the `next_offset` field of the returned [messages.MessageReactionsList](https://docs.madelineproto.xyz/API_docs/types/messages.MessageReactionsList.html)); empty in the first request.
     * @param int $limit Maximum number of results to return, [see pagination](https://core.telegram.org/api/offsets)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.messageReactionsList', count: int, reactions: list<array{_: 'messagePeerReaction', peer_id: array|int|string, big: bool, unread: bool, my: bool, date: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}}>, chats: list<array|int|string>, users: list<array|int|string>, next_offset?: string} @see https://docs.madelineproto.xyz/API_docs/types/messages.MessageReactionsList.html
     * @psalm-impure
     */
    public function getMessageReactionsList(array|int|string|null $peer = null, int|null $id = 0, array|null $reaction = null, string|null $offset = null, int|null $limit = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Change the set of [message reactions Â»](https://core.telegram.org/api/reactions) that can be used in a certain group, supergroup or channel
     *
     * @param array{_: 'chatReactionsNone'}|array{_: 'chatReactionsAll', allow_custom?: bool}|array{_: 'chatReactionsSome', reactions?: list<array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon?: string}|array{_: 'reactionCustomEmoji', document_id?: int}|array{_: 'reactionPaid'}>} $available_reactions Allowed reaction emojis @see https://docs.madelineproto.xyz/API_docs/types/ChatReactions.html
     * @param array|int|string $peer Group where to apply changes @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $reactions_limit This flag may be used to impose a custom limit of unique reactions (i.e. a customizable version of [appConfig.reactions\_uniq\_max](https://core.telegram.org/api/config#reactions-uniq-max)); this field and the other info set by the method will then be available to users in [channelFull](https://docs.madelineproto.xyz/API_docs/constructors/channelFull.html) and [chatFull](https://docs.madelineproto.xyz/API_docs/constructors/chatFull.html). <br>If this flag is not set, the previously configured `reactions_limit` will not be altered.
     * @param bool $paid_enabled If this flag is set and a [Bool](https://docs.madelineproto.xyz/API_docs/types/Bool.html) is passed, the method will enable or disable [paid message reactions Â»](https://core.telegram.org/api/reactions#paid-reactions). If this flag is not set, the previously stored setting will not be changed.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function setChatAvailableReactions(array $available_reactions, array|int|string|null $peer = null, int|null $reactions_limit = null, bool|null $paid_enabled = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Obtain available [message reactions Â»](https://core.telegram.org/api/reactions)
     *
     * @param int $hash 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.availableReactionsNotModified'}|array{_: 'messages.availableReactions', hash: int, reactions: list<array{_: 'availableReaction', inactive: bool, premium: bool, reaction: string, title: string, static_icon: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, appear_animation: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, select_animation: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, activate_animation: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, effect_animation: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, around_animation?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, center_icon?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}>} @see https://docs.madelineproto.xyz/API_docs/types/messages.AvailableReactions.html
     * @psalm-impure
     */
    public function getAvailableReactions(int|null $hash = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Change default emoji reaction to use in the quick reaction menu: the value is synced across devices and can be fetched using [help.getConfig, `reactions_default` field](https://docs.madelineproto.xyz/API_docs/methods/help.getConfig.html).
     *
     * @param array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon?: string}|array{_: 'reactionCustomEmoji', document_id?: int}|array{_: 'reactionPaid'} $reaction New emoji reaction @see https://docs.madelineproto.xyz/API_docs/types/Reaction.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function setDefaultReaction(array|null $reaction = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Translate a given text.
     * 
     * [Styled text entities](https://core.telegram.org/api/entities) will only be preserved for [Telegram Premium](https://core.telegram.org/api/premium) users.
     *
     * @param array|int|string $peer If the text is a chat message, the peer ID @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param list<int> $id A list of message IDs to translate
     * @param list<array{_: 'textWithEntities', text?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>}> $text Array of A list of styled messages to translate @see https://docs.madelineproto.xyz/API_docs/types/TextWithEntities.html
     * @param string $to_lang Two-letter ISO 639-1 language code of the language to which the message is translated
     * @param string $tone 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.translateResult', result: list<array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}>} @see https://docs.madelineproto.xyz/API_docs/types/messages.TranslatedText.html
     * @psalm-impure
     */
    public function translateText(array|int|string|null $peer = null, array|null $id = null, array|null $text = null, string|null $to_lang = '', string|null $tone = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get unread reactions to messages you sent
     *
     * @param array|int|string $peer Peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $top_msg_id If set, considers only reactions to messages within the specified [forum topic](https://core.telegram.org/api/forum#forum-topics)
     * @param array|int|string $saved_peer_id If set, must be equal to the ID of a [monoforum topic](https://core.telegram.org/api/monoforum): will affect that topic in the monoforum passed in `peer`. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $offset_id [Offsets for pagination, for more info click here](https://core.telegram.org/api/offsets)
     * @param int $add_offset [Offsets for pagination, for more info click here](https://core.telegram.org/api/offsets)
     * @param int $limit Maximum number of results to return, [see pagination](https://core.telegram.org/api/offsets)
     * @param int $max_id Only return reactions for messages up until this message ID
     * @param int $min_id Only return reactions for messages starting from this message ID
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.messages', messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.messagesSlice', inexact: array, count: array, next_rate?: array, offset_id_offset?: array, search_flood?: array, messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.channelMessages', inexact: array, pts: array, count: array, offset_id_offset?: array, messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.messagesNotModified', count: array} @see https://docs.madelineproto.xyz/API_docs/types/messages.Messages.html
     * @psalm-impure
     */
    public function getUnreadReactions(array|int|string|null $peer = null, int|null $top_msg_id = null, array|int|string|null $saved_peer_id = null, int|null $offset_id = 0, int|null $add_offset = 0, int|null $limit = 0, int|null $max_id = 0, int|null $min_id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Mark [message reactions Â»](https://core.telegram.org/api/reactions) as read
     *
     * @param array|int|string $peer Peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $top_msg_id Mark as read only reactions to messages within the specified [forum topic](https://core.telegram.org/api/forum#forum-topics)
     * @param array|int|string $saved_peer_id If set, must be equal to the ID of a [monoforum topic](https://core.telegram.org/api/monoforum): will affect that topic in the monoforum passed in `peer`. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.affectedHistory', pts: int, pts_count: int, offset: int} @see https://docs.madelineproto.xyz/API_docs/types/messages.AffectedHistory.html
     * @psalm-impure
     */
    public function readReactions(array|int|string|null $peer = null, int|null $top_msg_id = null, array|int|string|null $saved_peer_id = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * View and search recently sent media.  
     * This method does not support pagination.
     *
     * @param array{_: 'inputMessagesFilterEmpty'}|array{_: 'inputMessagesFilterPhotos'}|array{_: 'inputMessagesFilterVideo'}|array{_: 'inputMessagesFilterPhotoVideo'}|array{_: 'inputMessagesFilterDocument'}|array{_: 'inputMessagesFilterUrl'}|array{_: 'inputMessagesFilterGif'}|array{_: 'inputMessagesFilterVoice'}|array{_: 'inputMessagesFilterMusic'}|array{_: 'inputMessagesFilterChatPhotos'}|array{_: 'inputMessagesFilterPhoneCalls', missed?: bool}|array{_: 'inputMessagesFilterRoundVoice'}|array{_: 'inputMessagesFilterRoundVideo'}|array{_: 'inputMessagesFilterMyMentions'}|array{_: 'inputMessagesFilterGeo'}|array{_: 'inputMessagesFilterContacts'}|array{_: 'inputMessagesFilterPinned'}|array{_: 'inputMessagesFilterPoll'} $filter Message filter @see https://docs.madelineproto.xyz/API_docs/types/MessagesFilter.html
     * @param string $q Optional search query
     * @param int $limit Maximum number of results to return (max 100).
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.messages', messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.messagesSlice', inexact: array, count: array, next_rate?: array, offset_id_offset?: array, search_flood?: array, messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.channelMessages', inexact: array, pts: array, count: array, offset_id_offset?: array, messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.messagesNotModified', count: array} @see https://docs.madelineproto.xyz/API_docs/types/messages.Messages.html
     * @psalm-impure
     */
    public function searchSentMedia(array $filter, string|null $q = '', int|null $limit = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Returns installed attachment menu [bot mini apps Â»](https://core.telegram.org/api/bots/attach)
     *
     * @param list<int|string>|array<never, never> $hash Array of [Hash used for caching, for more info click here](https://core.telegram.org/api/offsets#hash-generation). @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'attachMenuBotsNotModified'}|array{_: 'attachMenuBots', hash: list<int|string>, bots: list<array{_: 'attachMenuBot', inactive: bool, has_settings: bool, request_write_access: bool, show_in_attach_menu: bool, show_in_side_menu: bool, side_menu_disclaimer_needed: bool, bot_id: int, short_name: string, peer_types?: list<array{_: 'attachMenuPeerTypeSameBotPM'}|array{_: 'attachMenuPeerTypeBotPM'}|array{_: 'attachMenuPeerTypePM'}|array{_: 'attachMenuPeerTypeChat'}|array{_: 'attachMenuPeerTypeBroadcast'}>, icons: list<array{_: 'attachMenuBotIcon', name: string, icon: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, colors?: list<array{_: 'attachMenuBotIconColor', name: string, color: int}>}>}>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/AttachMenuBots.html
     * @psalm-impure
     */
    public function getAttachMenuBots(array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Returns attachment menu entry for a [bot mini app that can be launched from the attachment menu Â»](https://core.telegram.org/api/bots/attach)
     *
     * @param array|int|string $bot Bot ID @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'attachMenuBotsBot', bot: array{_: 'attachMenuBot', inactive: bool, has_settings: bool, request_write_access: bool, show_in_attach_menu: bool, show_in_side_menu: bool, side_menu_disclaimer_needed: bool, bot_id: int, short_name: string, peer_types?: list<array{_: 'attachMenuPeerTypeSameBotPM'}|array{_: 'attachMenuPeerTypeBotPM'}|array{_: 'attachMenuPeerTypePM'}|array{_: 'attachMenuPeerTypeChat'}|array{_: 'attachMenuPeerTypeBroadcast'}>, icons: list<array{_: 'attachMenuBotIcon', name: string, icon: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, colors?: list<array{_: 'attachMenuBotIconColor', name: string, color: int}>}>}, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/AttachMenuBotsBot.html
     * @psalm-impure
     */
    public function getAttachMenuBot(array|int|string|null $bot = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Enable or disable [web bot attachment menu Â»](https://core.telegram.org/api/bots/attach)
     *
     * @param bool $enabled Toggle
     * @param bool $write_allowed Whether the user authorizes the bot to write messages to them, if requested by [attachMenuBot](https://docs.madelineproto.xyz/API_docs/constructors/attachMenuBot.html).`request_write_access`
     * @param array|int|string $bot Bot ID @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function toggleBotInAttachMenu(bool $enabled, bool|null $write_allowed = null, array|int|string|null $bot = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Open a [bot mini app](https://core.telegram.org/bots/webapps), sending over user information after user confirmation.
     * 
     * After calling this method, until the user closes the webview, [messages.prolongWebView](https://docs.madelineproto.xyz/API_docs/methods/messages.prolongWebView.html) must be called every 60 seconds.
     *
     * @param bool $from_bot_menu Whether the webview was opened by clicking on the bot's [menu button Â»](https://core.telegram.org/api/bots/menu).
     * @param bool $silent Whether the inline message that will be sent by the bot on behalf of the user once the web app interaction is [terminated](https://docs.madelineproto.xyz/API_docs/methods/messages.sendWebViewResultMessage.html) should be sent silently (no notifications for the receivers).
     * @param bool $compact If set, requests to open the mini app in compact mode (as opposed to normal or fullscreen mode). Must be set if the `mode` parameter of the [attachment menu deep link](https://core.telegram.org/api/links#bot-attachment-or-side-menu-links) is equal to `compact`.
     * @param bool $fullscreen If set, requests to open the mini app in fullscreen mode (as opposed to normal or compact mode). Must be set if the `mode` parameter of the [attachment menu deep link](https://core.telegram.org/api/links#bot-attachment-or-side-menu-links) is equal to `fullscreen`.
     * @param array|int|string $peer Dialog where the web app is being opened, and where the resulting message will be sent (see the [docs for more info Â»](https://core.telegram.org/api/bots/webapps)). @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param array|int|string $bot Bot that owns the [web app](https://core.telegram.org/api/bots/webapps) @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param string $url [Web app URL](https://core.telegram.org/api/bots/webapps)
     * @param string $start_param If the web app was opened from the attachment menu using a [attachment menu deep link](https://core.telegram.org/api/links#bot-attachment-or-side-menu-links), `start_param` should contain the `data` from the `startattach` parameter.
     * @param mixed $theme_params Any JSON-encodable data
     * @param string $platform Short name of the application; 0-64 English letters, digits, and underscores
     * @param array{_: 'inputReplyToMessage', reply_to_msg_id?: int, top_msg_id?: int, reply_to_peer_id?: array|int|string, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>, quote_offset?: int, monoforum_peer_id?: array|int|string, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, url?: string, webpage_id?: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, email?: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, phone?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, name?: string}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, user_id?: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, date?: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, type: array{_: 'inlineButtonTypeUrl', url?: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url?: string, button_id?: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access?: bool, fwd_text?: string, url?: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url?: string}|array{_: 'inlineButtonTypeCallback', requires_password?: bool, data?: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer?: bool, query?: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id?: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id?: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text?: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary?: bool, bg_danger?: bool, bg_success?: bool, link?: bool}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}>}|array{_: 'inputReplyToStory', peer?: array|int|string, story_id?: int}|array{_: 'inputReplyToMonoForum', monoforum_peer_id?: array|int|string}|array{_: 'inputReplyToEphemeralMessage', id?: int} $reply_to If set, indicates that the inline message that will be sent by the bot on behalf of the user once the web app interaction is [terminated](https://docs.madelineproto.xyz/API_docs/methods/messages.sendWebViewResultMessage.html) should be sent in reply to the specified message or story. @see https://docs.madelineproto.xyz/API_docs/types/InputReplyTo.html
     * @param array|int|string $send_as Open the web app as the specified peer, sending the resulting the message as the specified peer. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'webViewResultUrl', fullsize: bool, fullscreen: bool, same_origin: bool, query_id?: int, url: string} @see https://docs.madelineproto.xyz/API_docs/types/WebViewResult.html
     * @psalm-impure
     */
    public function requestWebView(bool|null $from_bot_menu = null, bool|null $silent = null, bool|null $compact = null, bool|null $fullscreen = null, array|int|string|null $peer = null, array|int|string|null $bot = null, string|null $url = null, string|null $start_param = null, mixed $theme_params = null, string|null $platform = '', array|null $reply_to = null, array|int|string|null $send_as = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Indicate to the server (from the user side) that the user is still using a web app.
     * 
     * If the method returns a `QUERY_ID_INVALID` error, the webview must be closed.
     *
     * @param bool $silent Whether the inline message that will be sent by the bot on behalf of the user once the web app interaction is [terminated](https://docs.madelineproto.xyz/API_docs/methods/messages.sendWebViewResultMessage.html) should be sent silently (no notifications for the receivers).
     * @param array|int|string $peer Dialog where the web app was opened. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param array|int|string $bot Bot that owns the [web app](https://core.telegram.org/api/bots/webapps) @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param int $query_id Web app interaction ID obtained from [messages.requestWebView](https://docs.madelineproto.xyz/API_docs/methods/messages.requestWebView.html)
     * @param array{_: 'inputReplyToMessage', reply_to_msg_id?: int, top_msg_id?: int, reply_to_peer_id?: array|int|string, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>, quote_offset?: int, monoforum_peer_id?: array|int|string, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, url?: string, webpage_id?: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, email?: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, phone?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, name?: string}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, user_id?: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, date?: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, type: array{_: 'inlineButtonTypeUrl', url?: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url?: string, button_id?: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access?: bool, fwd_text?: string, url?: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url?: string}|array{_: 'inlineButtonTypeCallback', requires_password?: bool, data?: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer?: bool, query?: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id?: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id?: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text?: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary?: bool, bg_danger?: bool, bg_success?: bool, link?: bool}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}>}|array{_: 'inputReplyToStory', peer?: array|int|string, story_id?: int}|array{_: 'inputReplyToMonoForum', monoforum_peer_id?: array|int|string}|array{_: 'inputReplyToEphemeralMessage', id?: int} $reply_to If set, indicates that the inline message that will be sent by the bot on behalf of the user once the web app interaction is [terminated](https://docs.madelineproto.xyz/API_docs/methods/messages.sendWebViewResultMessage.html) should be sent in reply to the specified message or story. @see https://docs.madelineproto.xyz/API_docs/types/InputReplyTo.html
     * @param array|int|string $send_as Open the web app as the specified peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function prolongWebView(bool|null $silent = null, array|int|string|null $peer = null, array|int|string|null $bot = null, int|null $query_id = 0, array|null $reply_to = null, array|int|string|null $send_as = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Open a [bot mini app](https://core.telegram.org/api/bots/webapps).
     *
     * @param bool $from_switch_webview Whether the webapp was opened by clicking on the `switch_webview` button shown on top of the inline results list returned by [messages.getInlineBotResults](https://docs.madelineproto.xyz/API_docs/methods/messages.getInlineBotResults.html).
     * @param bool $from_side_menu Set this flag if opening the Mini App from the installed [side menu entry Â»](https://core.telegram.org/api/bots/attach).
     * @param bool $compact Deprecated.
     * @param bool $fullscreen Requests to open the app in fullscreen mode.
     * @param array|int|string $bot Bot that owns the mini app @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param string $url Web app URL, if opening from a keyboard button or inline result
     * @param string $start_param Deprecated.
     * @param mixed $theme_params Any JSON-encodable data
     * @param string $platform Short name of the application; 0-64 English letters, digits, and underscores
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'webViewResultUrl', fullsize: bool, fullscreen: bool, same_origin: bool, query_id?: int, url: string} @see https://docs.madelineproto.xyz/API_docs/types/WebViewResult.html
     * @psalm-impure
     */
    public function requestSimpleWebView(bool|null $from_switch_webview = null, bool|null $from_side_menu = null, bool|null $compact = null, bool|null $fullscreen = null, array|int|string|null $bot = null, string|null $url = null, string|null $start_param = null, mixed $theme_params = null, string|null $platform = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Terminate webview interaction started with [messages.requestWebView](https://docs.madelineproto.xyz/API_docs/methods/messages.requestWebView.html), sending the specified message to the chat on behalf of the user.
     *
     * @param array{_: 'inputBotInlineResult', send_message: array{_: 'inputBotInlineMessageMediaAuto', invert_media?: array, message?: array, entities?: list<array>, reply_markup?: array}|array{_: 'inputBotInlineMessageText', no_webpage?: array, invert_media?: array, message?: array, entities?: list<array>, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaGeo', geo_point?: array, heading?: array, period?: array, proximity_notification_radius?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaVenue', geo_point?: array, title?: array, address?: array, provider?: array, venue_id?: array, venue_type?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaContact', phone_number?: array, first_name?: array, last_name?: array, vcard?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageGame', reply_markup?: array}|array{_: 'inputBotInlineMessageMediaInvoice', invoice: array, provider_data: array, title?: array, description?: array, photo?: array, payload?: array, provider?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaWebPage', invert_media?: array, force_large_media?: array, force_small_media?: array, optional?: array, message?: array, entities?: list<array>, url?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageRichMessage', rich_message: array, reply_markup?: array}, id?: string, type?: string, title?: string, description?: string, url?: string, thumb?: array{_: 'inputWebDocument', url?: array, size?: array, mime_type?: array, attributes?: list<array>}, content?: array{_: 'inputWebDocument', url?: array, size?: array, mime_type?: array, attributes?: list<array>}}|array{_: 'inputBotInlineResultPhoto', send_message: array{_: 'inputBotInlineMessageMediaAuto', invert_media?: array, message?: array, entities?: list<array>, reply_markup?: array}|array{_: 'inputBotInlineMessageText', no_webpage?: array, invert_media?: array, message?: array, entities?: list<array>, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaGeo', geo_point?: array, heading?: array, period?: array, proximity_notification_radius?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaVenue', geo_point?: array, title?: array, address?: array, provider?: array, venue_id?: array, venue_type?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaContact', phone_number?: array, first_name?: array, last_name?: array, vcard?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageGame', reply_markup?: array}|array{_: 'inputBotInlineMessageMediaInvoice', invoice: array, provider_data: array, title?: array, description?: array, photo?: array, payload?: array, provider?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaWebPage', invert_media?: array, force_large_media?: array, force_small_media?: array, optional?: array, message?: array, entities?: list<array>, url?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageRichMessage', rich_message: array, reply_markup?: array}, id?: string, type?: string, photo?: array}|array{_: 'inputBotInlineResultDocument', send_message: array{_: 'inputBotInlineMessageMediaAuto', invert_media?: array, message?: array, entities?: list<array>, reply_markup?: array}|array{_: 'inputBotInlineMessageText', no_webpage?: array, invert_media?: array, message?: array, entities?: list<array>, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaGeo', geo_point?: array, heading?: array, period?: array, proximity_notification_radius?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaVenue', geo_point?: array, title?: array, address?: array, provider?: array, venue_id?: array, venue_type?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaContact', phone_number?: array, first_name?: array, last_name?: array, vcard?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageGame', reply_markup?: array}|array{_: 'inputBotInlineMessageMediaInvoice', invoice: array, provider_data: array, title?: array, description?: array, photo?: array, payload?: array, provider?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaWebPage', invert_media?: array, force_large_media?: array, force_small_media?: array, optional?: array, message?: array, entities?: list<array>, url?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageRichMessage', rich_message: array, reply_markup?: array}, id?: string, type?: string, title?: string, description?: string, document?: array}|array{_: 'inputBotInlineResultGame', send_message: array{_: 'inputBotInlineMessageMediaAuto', invert_media?: array, message?: array, entities?: list<array>, reply_markup?: array}|array{_: 'inputBotInlineMessageText', no_webpage?: array, invert_media?: array, message?: array, entities?: list<array>, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaGeo', geo_point?: array, heading?: array, period?: array, proximity_notification_radius?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaVenue', geo_point?: array, title?: array, address?: array, provider?: array, venue_id?: array, venue_type?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaContact', phone_number?: array, first_name?: array, last_name?: array, vcard?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageGame', reply_markup?: array}|array{_: 'inputBotInlineMessageMediaInvoice', invoice: array, provider_data: array, title?: array, description?: array, photo?: array, payload?: array, provider?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaWebPage', invert_media?: array, force_large_media?: array, force_small_media?: array, optional?: array, message?: array, entities?: list<array>, url?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageRichMessage', rich_message: array, reply_markup?: array}, id?: string, short_name?: string} $result Message to send @see https://docs.madelineproto.xyz/API_docs/types/InputBotInlineResult.html
     * @param string $bot_query_id Webview interaction ID obtained from [messages.requestWebView](https://docs.madelineproto.xyz/API_docs/methods/messages.requestWebView.html)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'webViewMessageSent', msg_id?: array{_: 'inputBotInlineMessageID', dc_id: int, id: int, access_hash: int}|array{_: 'inputBotInlineMessageID64', dc_id: int, owner_id: int, id: int, access_hash: int}} @see https://docs.madelineproto.xyz/API_docs/types/WebViewMessageSent.html
     * @psalm-impure
     */
    public function sendWebViewResultMessage(array $result, string|null $bot_query_id = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Used by the user to relay data from an opened [reply keyboard bot mini app](https://core.telegram.org/api/bots/webapps) to the bot that owns it.
     *
     * @param array|int|string $bot Bot that owns the web app @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param string $button_text Text of the [keyboardButtonSimpleWebView](https://docs.madelineproto.xyz/API_docs/constructors/keyboardButtonSimpleWebView.html) that was pressed to open the web app.
     * @param string $data Data to relay to the bot, obtained from a [`web_app_data_send` JS event](https://core.telegram.org/api/web-events#web-app-data-send).
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function sendWebViewData(array|int|string|null $bot = null, string|null $button_text = '', string|null $data = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * [Transcribe voice message](https://core.telegram.org/api/transcribe)
     *
     * @param array|int|string $peer Peer ID where the voice message was sent @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $msg_id Voice message ID
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.transcribedAudio', pending: bool, transcription_id: int, text: string, trial_remains_num?: int, trial_remains_until_date?: int} @see https://docs.madelineproto.xyz/API_docs/types/messages.TranscribedAudio.html
     * @psalm-impure
     */
    public function transcribeAudio(array|int|string|null $peer = null, int|null $msg_id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Rate [transcribed voice message](https://core.telegram.org/api/transcribe)
     *
     * @param bool $good Whether the transcription was correct
     * @param array|int|string $peer Peer where the voice message was sent @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $msg_id Message ID
     * @param int $transcription_id Transcription ID
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function rateTranscribedAudio(bool $good, array|int|string|null $peer = null, int|null $msg_id = 0, int|null $transcription_id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Fetch [custom emoji stickers Â»](https://core.telegram.org/api/custom-emoji).
     * 
     * Returns a list of [documents](https://docs.madelineproto.xyz/API_docs/constructors/document.html) with the animated custom emoji in TGS format, and a [documentAttributeCustomEmoji](https://docs.madelineproto.xyz/API_docs/constructors/documentAttributeCustomEmoji.html) attribute with the original emoji and info about the emoji stickerset this custom emoji belongs to.
     *
     * @param list<int>|array<never, never> $document_id [Custom emoji](https://core.telegram.org/api/custom-emoji) IDs from a [messageEntityCustomEmoji](https://docs.madelineproto.xyz/API_docs/constructors/messageEntityCustomEmoji.html).
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}> Array of  @see https://docs.madelineproto.xyz/API_docs/types/Document.html
     * @psalm-impure
     */
    public function getCustomEmojiDocuments(array $document_id = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Gets the list of currently installed [custom emoji stickersets](https://core.telegram.org/api/custom-emoji).
     *
     * @param list<int|string>|array<never, never> $hash Array of [Hash used for caching, for more info click here](https://core.telegram.org/api/offsets#hash-generation). @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.allStickersNotModified'}|array{_: 'messages.allStickers', hash: list<int|string>, sets: list<array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}>} @see https://docs.madelineproto.xyz/API_docs/types/messages.AllStickers.html
     * @psalm-impure
     */
    public function getEmojiStickers(array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Gets featured custom emoji stickersets.
     *
     * @param list<int|string>|array<never, never> $hash Array of [Hash used for caching, for more info click here](https://core.telegram.org/api/offsets#hash-generation). @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.featuredStickersNotModified', count: int}|array{_: 'messages.featuredStickers', premium: bool, hash: list<int|string>, count: int, sets: list<array{_: 'stickerSetCovered', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}, cover: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'stickerSetMultiCovered', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}, covers: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}|array{_: 'stickerSetFullCovered', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}, packs: list<array{_: 'stickerPack', emoticon: string, documents: list<int>}>, keywords: list<array{_: 'stickerKeyword', document_id: int, keyword: list<string>}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}|array{_: 'stickerSetNoCovered', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}}>, unread: list<int>} @see https://docs.madelineproto.xyz/API_docs/types/messages.FeaturedStickers.html
     * @psalm-impure
     */
    public function getFeaturedEmojiStickers(array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Report a [message reaction](https://core.telegram.org/api/reactions)
     *
     * @param array|int|string $peer Peer where the message was sent @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $id Message ID
     * @param array|int|string $reaction_peer Peer that sent the reaction @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function reportReaction(array|int|string|null $peer = null, int|null $id = 0, array|int|string|null $reaction_peer = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Got popular [message reactions](https://core.telegram.org/api/reactions)
     *
     * @param int $limit Maximum number of results to return, [see pagination](https://core.telegram.org/api/offsets)
     * @param list<int|string>|array<never, never> $hash Array of [Hash used for caching, for more info click here](https://core.telegram.org/api/offsets#hash-generation). @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.reactionsNotModified'}|array{_: 'messages.reactions', hash: list<int|string>, reactions: list<array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}>} @see https://docs.madelineproto.xyz/API_docs/types/messages.Reactions.html
     * @psalm-impure
     */
    public function getTopReactions(int|null $limit = 0, array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get recently used [message reactions](https://core.telegram.org/api/reactions)
     *
     * @param int $limit Maximum number of results to return, [see pagination](https://core.telegram.org/api/offsets)
     * @param list<int|string>|array<never, never> $hash Array of [Hash used for caching, for more info click here](https://core.telegram.org/api/offsets#hash-generation). @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.reactionsNotModified'}|array{_: 'messages.reactions', hash: list<int|string>, reactions: list<array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}>} @see https://docs.madelineproto.xyz/API_docs/types/messages.Reactions.html
     * @psalm-impure
     */
    public function getRecentReactions(int|null $limit = 0, array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Clear recently used [message reactions](https://core.telegram.org/api/reactions)
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function clearRecentReactions(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Fetch updated information about [paid media, see here Â»](https://core.telegram.org/api/paid-media) for the full flow.
     * 
     * This method will return an array of [updateMessageExtendedMedia](https://docs.madelineproto.xyz/API_docs/constructors/updateMessageExtendedMedia.html) updates, only for messages containing **already bought** paid media.  
     * No information will be returned for messages containing not yet bought paid media.
     *
     * @param array|int|string $peer Peer with visible paid media messages. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param list<int>|array<never, never> $id IDs of currently visible messages containing paid media.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function getExtendedMedia(array|int|string|null $peer = null, array $id = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Changes the default value of the Time-To-Live setting, applied to all new chats.
     *
     * @param int $period The new default Time-To-Live of all messages sent in new chats, in seconds.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function setDefaultHistoryTTL(int|null $period = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Gets the default value of the Time-To-Live setting, applied to all new chats.
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'defaultHistoryTTL', period: int} @see https://docs.madelineproto.xyz/API_docs/types/DefaultHistoryTTL.html
     * @psalm-impure
     */
    public function getDefaultHistoryTTL(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Send one or more chosen peers, as requested by a [keyboardButtonRequestPeer](https://docs.madelineproto.xyz/API_docs/constructors/keyboardButtonRequestPeer.html) button.
     *
     * @param array|int|string $peer The bot that sent the [keyboardButtonRequestPeer](https://docs.madelineproto.xyz/API_docs/constructors/keyboardButtonRequestPeer.html) button. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $msg_id ID of the message that contained the reply keyboard with the [keyboardButtonRequestPeer](https://docs.madelineproto.xyz/API_docs/constructors/keyboardButtonRequestPeer.html) button.
     * @param string $webapp_req_id 
     * @param int $button_id The `button_id` field from the [keyboardButtonRequestPeer](https://docs.madelineproto.xyz/API_docs/constructors/keyboardButtonRequestPeer.html) constructor.
     * @param list<array|int|string>|array<never, never> $requested_peers Array of The chosen peers. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function sendBotRequestedPeer(array|int|string|null $peer = null, int|null $msg_id = null, string|null $webapp_req_id = null, int|null $button_id = 0, array $requested_peers = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Represents a list of [emoji categories](https://core.telegram.org/api/emoji-categories).
     *
     * @param int $hash 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.emojiGroupsNotModified'}|array{_: 'messages.emojiGroups', hash: int, groups: list<array{_: 'emojiGroup', title: string, icon_emoji_id: int, emoticons: list<string>}|array{_: 'emojiGroupGreeting', title: string, icon_emoji_id: int, emoticons: list<string>}|array{_: 'emojiGroupPremium', title: string, icon_emoji_id: int}>} @see https://docs.madelineproto.xyz/API_docs/types/messages.EmojiGroups.html
     * @psalm-impure
     */
    public function getEmojiGroups(int|null $hash = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Represents a list of [emoji categories](https://core.telegram.org/api/emoji-categories), to be used when selecting custom emojis to set as [custom emoji status](https://core.telegram.org/api).
     *
     * @param int $hash 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.emojiGroupsNotModified'}|array{_: 'messages.emojiGroups', hash: int, groups: list<array{_: 'emojiGroup', title: string, icon_emoji_id: int, emoticons: list<string>}|array{_: 'emojiGroupGreeting', title: string, icon_emoji_id: int, emoticons: list<string>}|array{_: 'emojiGroupPremium', title: string, icon_emoji_id: int}>} @see https://docs.madelineproto.xyz/API_docs/types/messages.EmojiGroups.html
     * @psalm-impure
     */
    public function getEmojiStatusGroups(int|null $hash = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Represents a list of [emoji categories](https://core.telegram.org/api/emoji-categories), to be used when selecting custom emojis to set as [profile picture](https://core.telegram.org/api/files#sticker-profile-pictures).
     *
     * @param int $hash 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.emojiGroupsNotModified'}|array{_: 'messages.emojiGroups', hash: int, groups: list<array{_: 'emojiGroup', title: string, icon_emoji_id: int, emoticons: list<string>}|array{_: 'emojiGroupGreeting', title: string, icon_emoji_id: int, emoticons: list<string>}|array{_: 'emojiGroupPremium', title: string, icon_emoji_id: int}>} @see https://docs.madelineproto.xyz/API_docs/types/messages.EmojiGroups.html
     * @psalm-impure
     */
    public function getEmojiProfilePhotoGroups(int|null $hash = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Look for [custom emojis](https://core.telegram.org/api/custom-emoji) associated to a UTF8 emoji
     *
     * @param string $emoticon The emoji
     * @param list<int|string>|array<never, never> $hash Array of [Hash used for caching, for more info click here](https://core.telegram.org/api/offsets#hash-generation). @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'emojiListNotModified'}|array{_: 'emojiList', hash: list<int|string>, document_id: list<int>} @see https://docs.madelineproto.xyz/API_docs/types/EmojiList.html
     * @psalm-impure
     */
    public function searchCustomEmoji(string|null $emoticon = '', array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Show or hide the [real-time chat translation popup](https://core.telegram.org/api/translation) for a certain chat
     *
     * @param bool $disabled Whether to disable or enable the real-time chat translation popup
     * @param array|int|string $peer The peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function togglePeerTranslations(bool|null $disabled = null, array|int|string|null $peer = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Obtain information about a [direct link Mini App](https://core.telegram.org/api/bots/webapps#direct-link-mini-apps)
     *
     * @param array{_: 'inputBotAppID', id?: int, access_hash?: int}|array{_: 'inputBotAppShortName', bot_id?: array|int|string, short_name?: string} $app Bot app information obtained from a [Direct Mini App deep link Â»](https://core.telegram.org/api/links#direct-mini-app-links). @see https://docs.madelineproto.xyz/API_docs/types/InputBotApp.html
     * @param list<int|string>|array<never, never> $hash Array of [Hash used for caching, for more info click here](https://core.telegram.org/api/offsets#hash-generation) @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.botApp', app: array{_: 'botAppNotModified'}|array{_: 'botApp', id: int, access_hash: int, short_name: string, title: string, description: string, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, hash: list<int|string>}, inactive: bool, request_write_access: bool, has_settings: bool} @see https://docs.madelineproto.xyz/API_docs/types/messages.BotApp.html
     * @psalm-impure
     */
    public function getBotApp(array $app, array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Open a [bot mini app](https://core.telegram.org/bots/webapps) from a [direct Mini App deep link](https://core.telegram.org/api/links#direct-mini-app-links), sending over user information after user confirmation.
     * 
     * After calling this method, until the user closes the webview, [messages.prolongWebView](https://docs.madelineproto.xyz/API_docs/methods/messages.prolongWebView.html) must be called every 60 seconds.
     *
     * @param array{_: 'inputBotAppID', id?: int, access_hash?: int}|array{_: 'inputBotAppShortName', bot_id?: array|int|string, short_name?: string} $app The app obtained by invoking [messages.getBotApp](https://docs.madelineproto.xyz/API_docs/methods/messages.getBotApp.html) as specified in the [direct Mini App deep link](https://core.telegram.org/api/links#direct-mini-app-links) docs. @see https://docs.madelineproto.xyz/API_docs/types/InputBotApp.html
     * @param bool $write_allowed Set this flag if the bot is asking permission to send messages to the user as specified in the [direct Mini App deep link](https://core.telegram.org/api/links#direct-mini-app-links) docs, and the user agreed.
     * @param bool $compact If set, requests to open the mini app in compact mode (as opposed to normal or fullscreen mode). Must be set if the `mode` parameter of the [direct Mini App deep link](https://core.telegram.org/api/links#direct-mini-app-links) is equal to `compact`.
     * @param bool $fullscreen If set, requests to open the mini app in fullscreen mode (as opposed to compact or normal mode). Must be set if the `mode` parameter of the [direct Mini App deep link](https://core.telegram.org/api/links#direct-mini-app-links) is equal to `fullscreen`.
     * @param array|int|string $peer If the client has clicked on the link in a Telegram chat, pass the chat's peer information; otherwise pass the bot's peer information, instead. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param string $start_param If the `startapp` query string parameter is present in the [direct Mini App deep link](https://core.telegram.org/api/links#direct-mini-app-links), pass it to `start_param`.
     * @param mixed $theme_params Any JSON-encodable data
     * @param string $platform Short name of the application; 0-64 English letters, digits, and underscores
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'webViewResultUrl', fullsize: bool, fullscreen: bool, same_origin: bool, query_id?: int, url: string} @see https://docs.madelineproto.xyz/API_docs/types/WebViewResult.html
     * @psalm-impure
     */
    public function requestAppWebView(array $app, bool|null $write_allowed = null, bool|null $compact = null, bool|null $fullscreen = null, array|int|string|null $peer = null, string|null $start_param = null, mixed $theme_params = null, string|null $platform = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Set a custom [wallpaper Â»](https://core.telegram.org/api/wallpapers) in a specific private chat with another user.
     *
     * @param bool $for_both Only for [Premium](https://core.telegram.org/api/premium) users, sets the specified wallpaper for both users of the chat, without requiring confirmation from the other user.
     * @param bool $revert If we don't like the new wallpaper the other user of the chat has chosen for us using the `for_both` flag, we can re-set our previous wallpaper just on our side using this flag.
     * @param array|int|string $peer The private chat where the wallpaper will be set @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param array{_: 'inputWallPaper', id?: int, access_hash?: int}|array{_: 'inputWallPaperSlug', slug?: string}|array{_: 'inputWallPaperNoFile', id?: int} $wallpaper The [wallpaper Â»](https://core.telegram.org/api/wallpapers), obtained as described in the [wallpaper documentation Â»](https://core.telegram.org/api/wallpapers#uploading-wallpapers); must **not** be provided when installing a wallpaper obtained from a [messageActionSetChatWallPaper](https://docs.madelineproto.xyz/API_docs/constructors/messageActionSetChatWallPaper.html) service message (`id` must be provided, instead). @see https://docs.madelineproto.xyz/API_docs/types/InputWallPaper.html
     * @param array{_: 'wallPaperSettings', blur?: bool, motion?: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string} $settings Wallpaper settings, obtained as described in the [wallpaper documentation Â»](https://core.telegram.org/api/wallpapers#uploading-wallpapers) or from [messageActionSetChatWallPaper](https://docs.madelineproto.xyz/API_docs/constructors/messageActionSetChatWallPaper.html).`wallpaper`.`settings`. @see https://docs.madelineproto.xyz/API_docs/types/WallPaperSettings.html
     * @param int $id If the wallpaper was obtained from a [messageActionSetChatWallPaper](https://docs.madelineproto.xyz/API_docs/constructors/messageActionSetChatWallPaper.html) service message, must contain the ID of that message.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function setChatWallPaper(bool|null $for_both = null, bool|null $revert = null, array|int|string|null $peer = null, array|null $wallpaper = null, array|null $settings = null, int|null $id = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Search for [custom emoji stickersets Â»](https://core.telegram.org/api/custom-emoji)
     *
     * @param bool $exclude_featured Exclude featured stickersets from results
     * @param string $q Query string
     * @param list<int|string>|array<never, never> $hash Array of [Hash used for caching, for more info click here](https://core.telegram.org/api/offsets#hash-generation). @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.foundStickerSetsNotModified'}|array{_: 'messages.foundStickerSets', hash: list<int|string>, sets: list<array{_: 'stickerSetCovered', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}, cover: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'stickerSetMultiCovered', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}, covers: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}|array{_: 'stickerSetFullCovered', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}, packs: list<array{_: 'stickerPack', emoticon: string, documents: list<int>}>, keywords: list<array{_: 'stickerKeyword', document_id: int, keyword: list<string>}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}|array{_: 'stickerSetNoCovered', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}}>} @see https://docs.madelineproto.xyz/API_docs/types/messages.FoundStickerSets.html
     * @psalm-impure
     */
    public function searchEmojiStickerSets(bool|null $exclude_featured = null, string|null $q = '', array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Returns the current [saved dialog list Â»](https://core.telegram.org/api/saved-messages) or [monoforum topic list Â»](https://core.telegram.org/api/monoforum).
     *
     * @param bool $exclude_pinned Exclude pinned dialogs
     * @param array|int|string $parent_peer If set, fetches the topic list of the passed monoforum, otherwise fetches the saved dialog list. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $offset_date [Offsets for pagination, for more info click here](https://core.telegram.org/api/offsets)
     * @param int $offset_id [Offsets for pagination, for more info click here](https://core.telegram.org/api/offsets) (`top_message` ID used for pagination)
     * @param array|int|string $offset_peer [Offset peer for pagination](https://core.telegram.org/api/offsets) @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $limit Number of list elements to be returned
     * @param list<int|string>|array<never, never> $hash Array of [Hash used for caching, for more info click here](https://core.telegram.org/api/offsets#hash-generation) @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.savedDialogs', dialogs: list<array{_: 'savedDialog', peer: array|int|string, pinned: bool, top_message: int}|array{_: 'monoForumDialog', peer: array|int|string, unread_mark: bool, nopaid_messages_exception: bool, top_message: int, read_inbox_max_id: int, read_outbox_max_id: int, unread_count: int, unread_reactions_count: int, draft?: array{_: 'draftMessageEmpty', date?: int}|array{_: 'draftMessage', no_webpage: bool, invert_media: bool, reply_to?: array{_: 'inputReplyToMessage', reply_to_msg_id: int, top_msg_id?: int, reply_to_peer_id?: array|int|string, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, quote_offset?: int, monoforum_peer_id?: array|int|string, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, url: string, webpage_id: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, email: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, phone: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, name: string}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, user_id: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, date: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, type: array{_: 'inlineButtonTypeUrl', url: array}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: array, url: array, button_id: array}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: array, fwd_text?: array, url: array, bot?: array}|array{_: 'inlineButtonTypeWebView', url: array}|array{_: 'inlineButtonTypeCallback', requires_password: array, data: array}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: array, query: array, peer_types?: list<array>}|array{_: 'inlineButtonTypeUserProfile', user_id: array}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array}|array{_: 'inlineButtonTypeCopy', copy_text: array}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: array, bg_danger: array, bg_success: array, link: array}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}>}|array{_: 'inputReplyToStory', peer: array|int|string, story_id: int}|array{_: 'inputReplyToMonoForum', monoforum_peer_id: array|int|string}|array{_: 'inputReplyToEphemeralMessage', id: int}, message: string, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, media?: \danog\MadelineProto\EventHandler\Media|string|array, date: int, effect?: int, suggested_post?: array{_: 'suggestedPost', accepted: bool, rejected: bool, price?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, schedule_date?: int}, rich_message?: array{_: 'richMessage', rtl: bool, part: bool, blocks: list<array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, published_date: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, language: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: string}|array{_: 'pageBlockList', items: list<array{_: 'pageListItemText', text: array, checkbox: array, checked: array}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, collapsed: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array, credit: array}, spoiler: bool, photo_id: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array, credit: array}, autoplay: bool, loop: bool, spoiler: bool, video_id: int}|array{_: 'pageBlockCover', cover: array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array}|array{_: 'pageBlockSubtitle', text: array}|array{_: 'pageBlockAuthorDate', author: array, published_date: array}|array{_: 'pageBlockHeader', text: array}|array{_: 'pageBlockSubheader', text: array}|array{_: 'pageBlockParagraph', text: array}|array{_: 'pageBlockPreformatted', text: array, language: array}|array{_: 'pageBlockFooter', text: array}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: array}|array{_: 'pageBlockList', items: list<array>}|array{_: 'pageBlockBlockquote', text: array, caption: array, collapsed: array}|array{_: 'pageBlockPullquote', text: array, caption: array}|array{_: 'pageBlockPhoto', caption: array, spoiler: array, photo_id: array, url?: array, webpage_id?: array}|array{_: 'pageBlockVideo', caption: array, autoplay: array, loop: array, spoiler: array, video_id: array}|array{_: 'pageBlockEmbed', caption: array, full_width: array, allow_scrolling: array, url?: array, html?: array, poster_photo_id?: array, w?: array, h?: array}|array{_: 'pageBlockChannel', channel: array}|array{_: 'pageBlockAudio', caption: array, audio_id: array}|array{_: 'pageBlockKicker', text: array}|array{_: 'pageBlockTable', title: array, bordered: array, striped: array, compact: array, rows: list<array>}|array{_: 'pageBlockOrderedList', reversed: array, items: list<array>, start?: array, type?: array}|array{_: 'pageBlockRelatedArticles', title: array, articles: list<array>}|array{_: 'pageBlockMap', caption: array, geo: array, zoom: array, w: array, h: array}|array{_: 'pageBlockHeading1', text: array}|array{_: 'pageBlockHeading2', text: array}|array{_: 'pageBlockHeading3', text: array}|array{_: 'pageBlockHeading4', text: array}|array{_: 'pageBlockHeading5', text: array}|array{_: 'pageBlockHeading6', text: array}|array{_: 'pageBlockMath', source: array}|array{_: 'pageBlockThinking', text: array}|array{_: 'inputPageBlockMap', caption: array, geo: array, zoom: array, w: array, h: array}|array{_: 'pageBlockButtonRow', align_left: array, align_center: array, align_right: array, buttons: list<array>}|array{_: 'pageBlockDocument', caption: array, document_id: array}}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array, credit: array}, full_width: bool, allow_scrolling: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array, credit: array}, audio_id: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, bordered: bool, striped: bool, compact: bool, rows: list<array{_: 'pageTableRow', cells: list<array>}>}|array{_: 'pageBlockOrderedList', reversed: bool, items: list<array{_: 'pageListOrderedItemText', text: array, checkbox: array, checked: array, num?: array, value?: array, type?: array}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, articles: list<array{_: 'pageRelatedArticle', url: array, webpage_id: array, title?: array, description?: array, photo_id?: array, author?: array, published_date?: array}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockMath', source: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: array, long: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockButtonRow', align_left: bool, align_center: bool, align_right: bool, buttons: list<array{_: 'pageButton', text: array, type: array, style?: array}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array, credit: array}, document_id: int}>, photos: list<array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}}}>, messages: list<array{_: 'messageEmpty', id: int, peer_id?: array|int|string}|array{_: 'message', peer_id: array|int|string, out: bool, mentioned: bool, media_unread: bool, silent: bool, post: bool, from_scheduled: bool, legacy: bool, edit_hide: bool, pinned: bool, noforwards: bool, invert_media: bool, offline: bool, video_processing_pending: bool, paid_suggested_post_stars: bool, paid_suggested_post_ton: bool, id: int, from_id?: array|int|string, from_boosts_applied?: int, from_rank?: string, saved_peer_id?: array|int|string, fwd_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, via_bot_id?: int, via_business_bot_id?: int, guestchat_via_from?: array|int|string, reply_to?: array{_: 'messageReplyHeader', reply_to_scheduled: bool, forum_topic: bool, quote: bool, reply_to_ephemeral: bool, reply_to_msg_id?: int, reply_to_peer_id?: array|int|string, reply_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, reply_media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_to_top_id?: int, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, quote_offset?: int, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, url: string, webpage_id: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, email: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, phone: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, name: string}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, user_id: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, date: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, type: array{_: 'inlineButtonTypeUrl', url: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url: string, button_id: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: bool, fwd_text?: string, url: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url: string}|array{_: 'inlineButtonTypeCallback', requires_password: bool, data: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: bool, query: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: bool, bg_danger: bool, bg_success: bool, link: bool}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}>}|array{_: 'messageReplyStoryHeader', peer: array|int|string, story_id: int}, date: int, message: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_markup?: array, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, views?: int, forwards?: int, replies?: array{_: 'messageReplies', comments: bool, replies: int, replies_pts: int, recent_repliers?: list<array|int|string>, channel_id?: int, max_id?: int, read_max_id?: int}, edit_date?: int, post_author?: string, grouped_id?: int, reactions?: array{_: 'messageReactions', min: bool, can_see_list: bool, reactions_as_tags: bool, results: list<array{_: 'reactionCount', chosen_order?: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}, count: int}>, recent_reactions?: list<array{_: 'messagePeerReaction', peer_id: array|int|string, big: bool, unread: bool, my: bool, date: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}}>, top_reactors?: list<array{_: 'messageReactor', top: bool, my: bool, anonymous: bool, peer_id?: array|int|string, count: int}>}, restriction_reason?: list<array{_: 'restrictionReason', platform: string, reason: string, text: string}>, ttl_period?: int, quick_reply_shortcut_id?: int, effect?: int, factcheck?: array{_: 'factCheck', need_check: bool, country?: string, text?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}, hash: list<int|string>}, report_delivery_until_date?: int, paid_message_stars?: int, suggested_post?: array{_: 'suggestedPost', accepted: bool, rejected: bool, price?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, schedule_date?: int}, schedule_repeat_period?: int, summary_from_language?: string, rich_message?: array{_: 'richMessage', rtl: bool, part: bool, blocks: list<array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, published_date: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, language: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: string}|array{_: 'pageBlockList', items: list<array{_: 'pageListItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, checkbox: bool, checked: bool}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, collapsed: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, spoiler: bool, photo_id: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, autoplay: bool, loop: bool, spoiler: bool, video_id: int}|array{_: 'pageBlockCover', cover: array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, published_date: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, language: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: string}|array{_: 'pageBlockList', items: list<array{_: 'pageListItemText', text: array, checkbox: array, checked: array}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, collapsed: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array, credit: array}, spoiler: bool, photo_id: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array, credit: array}, autoplay: bool, loop: bool, spoiler: bool, video_id: int}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array, credit: array}, full_width: bool, allow_scrolling: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array, credit: array}, audio_id: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, bordered: bool, striped: bool, compact: bool, rows: list<array{_: 'pageTableRow', cells: list<array>}>}|array{_: 'pageBlockOrderedList', reversed: bool, items: list<array{_: 'pageListOrderedItemText', text: array, checkbox: array, checked: array, num?: array, value?: array, type?: array}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, articles: list<array{_: 'pageRelatedArticle', url: array, webpage_id: array, title?: array, description?: array, photo_id?: array, author?: array, published_date?: array}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockMath', source: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: array, long: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockButtonRow', align_left: bool, align_center: bool, align_right: bool, buttons: list<array{_: 'pageButton', text: array, type: array, style?: array}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array, credit: array}, document_id: int}}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, full_width: bool, allow_scrolling: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, audio_id: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, bordered: bool, striped: bool, compact: bool, rows: list<array{_: 'pageTableRow', cells: list<array{_: 'pageTableCell', header: array, align_center: array, align_right: array, valign_middle: array, valign_bottom: array, text?: array, colspan?: array, rowspan?: array}>}>}|array{_: 'pageBlockOrderedList', reversed: bool, items: list<array{_: 'pageListOrderedItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, checkbox: bool, checked: bool, num?: string, value?: int, type?: string}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, articles: list<array{_: 'pageRelatedArticle', url: string, webpage_id: int, title?: string, description?: string, photo_id?: int, author?: string, published_date?: int}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash: int, accuracy_radius?: int}, zoom: int, w: int, h: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockMath', source: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, geo: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: float, long: float, accuracy_radius?: int}, zoom: int, w: int, h: int}|array{_: 'pageBlockButtonRow', align_left: bool, align_center: bool, align_right: bool, buttons: list<array{_: 'pageButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, type: array{_: 'inlineButtonTypeUrl', url: array}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: array, url: array, button_id: array}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: array, fwd_text?: array, url: array, bot?: array}|array{_: 'inlineButtonTypeWebView', url: array}|array{_: 'inlineButtonTypeCallback', requires_password: array, data: array}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: array, query: array, peer_types?: list<array>}|array{_: 'inlineButtonTypeUserProfile', user_id: array}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array}|array{_: 'inlineButtonTypeCopy', copy_text: array}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: array, bg_danger: array, bg_success: array, link: array}}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, document_id: int}>, photos: list<array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}}|array{_: 'messageService', peer_id: array|int|string, out: bool, mentioned: bool, media_unread: bool, reactions_are_possible: bool, silent: bool, post: bool, legacy: bool, id: int, from_id?: array|int|string, saved_peer_id?: array|int|string, reply_to?: array{_: 'messageReplyHeader', reply_to_scheduled: bool, forum_topic: bool, quote: bool, reply_to_ephemeral: bool, reply_to_msg_id?: int, reply_to_peer_id?: array|int|string, reply_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, reply_media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_to_top_id?: int, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, quote_offset?: int, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, url: string, webpage_id: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, email: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, phone: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, name: string}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, user_id: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, date: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, type: array{_: 'inlineButtonTypeUrl', url: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url: string, button_id: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: bool, fwd_text?: string, url: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url: string}|array{_: 'inlineButtonTypeCallback', requires_password: bool, data: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: bool, query: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: bool, bg_danger: bool, bg_success: bool, link: bool}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}>}|array{_: 'messageReplyStoryHeader', peer: array|int|string, story_id: int}, date: int, action: array{_: 'messageActionEmpty'}|array{_: 'messageActionChatCreate', title: string, users: list<int>}|array{_: 'messageActionChatEditTitle', title: string}|array{_: 'messageActionChatEditPhoto', photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'messageActionChatDeletePhoto'}|array{_: 'messageActionChatAddUser', users: list<int>}|array{_: 'messageActionChatDeleteUser', user_id: int}|array{_: 'messageActionChatJoinedByLink', inviter_id: int}|array{_: 'messageActionChannelCreate', title: string}|array{_: 'messageActionChatMigrateTo', channel_id: int}|array{_: 'messageActionChannelMigrateFrom', title: string, chat_id: array|int|string}|array{_: 'messageActionPinMessage'}|array{_: 'messageActionHistoryClear'}|array{_: 'messageActionGameScore', game_id: int, score: int}|array{_: 'messageActionPaymentSentMe', charge: array{_: 'paymentCharge', id: string, provider_charge_id: string}, recurring_init: bool, recurring_used: bool, currency: string, total_amount: int, payload: string, info?: array{_: 'paymentRequestedInfo', name?: string, phone?: string, email?: string, shipping_address?: array{_: 'postAddress', street_line1: string, street_line2: string, city: string, state: string, country_iso2: string, post_code: string}}, shipping_option_id?: string, subscription_until_date?: int}|array{_: 'messageActionPaymentSent', recurring_init: bool, recurring_used: bool, currency: string, total_amount: int, invoice_slug?: string, subscription_until_date?: int}|array{_: 'messageActionPhoneCall', video: bool, call_id: int, reason?: array{_: 'phoneCallDiscardReasonMissed'}|array{_: 'phoneCallDiscardReasonDisconnect'}|array{_: 'phoneCallDiscardReasonHangup'}|array{_: 'phoneCallDiscardReasonBusy'}|array{_: 'phoneCallDiscardReasonMigrateConferenceCall', slug: string}, duration?: int}|array{_: 'messageActionScreenshotTaken'}|array{_: 'messageActionCustomAction', message: string}|array{_: 'messageActionBotAllowed', attach_menu: bool, from_request: bool, domain?: string, app?: array{_: 'botAppNotModified'}|array{_: 'botApp', id: int, access_hash: int, short_name: string, title: string, description: string, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, hash: list<int|string>}}|array{_: 'messageActionSecureValuesSentMe', credentials: array{_: 'secureCredentialsEncrypted', data: string, hash: string, secret: string}, values: list<array{_: 'secureValue', type: array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}, data?: array{_: 'secureData', data: string, data_hash: string, secret: string}, front_side?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, reverse_side?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, selfie?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, translation?: list<array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}>, files?: list<array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}>, plain_data?: array{_: 'securePlainPhone', phone: string}|array{_: 'securePlainEmail', email: string}, hash: string}>}|array{_: 'messageActionSecureValuesSent', types: list<array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}>}|array{_: 'messageActionContactSignUp'}|array{_: 'messageActionGeoProximityReached', from_id: array|int|string, to_id: array|int|string, distance: int}|array{_: 'messageActionGroupCall', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, duration?: int}|array{_: 'messageActionInviteToGroupCall', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, users: list<int>}|array{_: 'messageActionSetMessagesTTL', period: int, auto_setting_from?: int}|array{_: 'messageActionGroupCallScheduled', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, schedule_date: int}|array{_: 'messageActionSetChatTheme', theme: array{_: 'chatTheme', emoticon: string}|array{_: 'chatThemeUniqueGift', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: array, edge_color: array, text_color: array}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array, crafted: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array, name: array, backdrop_id: array, center_color: array, edge_color: array, pattern_color: array, text_color: array}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array, sender_id?: array, date: array, message?: array}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: array, nanos: array}|array{_: 'starsTonAmount', amount: array}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: array, background_emoji_id?: array}|array{_: 'peerColorCollectible', collectible_id: array, gift_emoji_id: array, background_emoji_id: array, accent_color: array, colors: list<array>, dark_accent_color?: array, dark_colors?: list<array>}|array{_: 'inputPeerColorCollectible', collectible_id: array}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, theme_settings: list<array{_: 'themeSettings', base_theme: array{_: 'baseThemeClassic'}|array{_: 'baseThemeDay'}|array{_: 'baseThemeNight'}|array{_: 'baseThemeTinted'}|array{_: 'baseThemeArctic'}, message_colors_animated: bool, accent_color: int, outbox_accent_color?: int, message_colors?: list<int>, wallpaper?: array{_: 'wallPaper', id: array, creator: array, default: array, pattern: array, dark: array, access_hash: array, slug: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, settings?: array}|array{_: 'wallPaperNoFile', id: array, default: array, dark: array, settings?: array}}>}}|array{_: 'messageActionChatJoinedByRequest'}|array{_: 'messageActionWebViewDataSentMe', text: string, data: string}|array{_: 'messageActionWebViewDataSent', text: string}|array{_: 'messageActionGiftPremium', currency: string, amount: int, days: int, crypto_currency?: string, crypto_amount?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}|array{_: 'messageActionTopicCreate', title_missing: bool, title: string, icon_color: int, icon_emoji_id?: int}|array{_: 'messageActionTopicEdit', title?: string, icon_emoji_id?: int, closed?: bool, hidden?: bool}|array{_: 'messageActionSuggestProfilePhoto', photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'messageActionRequestedPeer', button_id: int, peers: list<array|int|string>}|array{_: 'messageActionSetChatWallPaper', wallpaper: array{_: 'wallPaper', id: int, creator: bool, default: bool, pattern: bool, dark: bool, access_hash: int, slug: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, settings?: array{_: 'wallPaperSettings', blur: bool, motion: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string}}|array{_: 'wallPaperNoFile', id: int, default: bool, dark: bool, settings?: array{_: 'wallPaperSettings', blur: bool, motion: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string}}, same: bool, for_both: bool}|array{_: 'messageActionGiftCode', via_giveaway: bool, unclaimed: bool, boost_peer?: array|int|string, days: int, slug: string, currency?: string, amount?: int, crypto_currency?: string, crypto_amount?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}|array{_: 'messageActionGiveawayLaunch', stars?: int}|array{_: 'messageActionGiveawayResults', stars: bool, winners_count: int, unclaimed_count: int}|array{_: 'messageActionBoostApply', boosts: int}|array{_: 'messageActionRequestedPeerSentMe', button_id: int, peers: list<array{_: 'requestedPeerUser', user_id: int, first_name?: string, last_name?: string, username?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'requestedPeerChat', chat_id: array|int|string, title?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'requestedPeerChannel', channel_id: int, title?: string, username?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}>}|array{_: 'messageActionPaymentRefunded', peer: array|int|string, charge: array{_: 'paymentCharge', id: string, provider_charge_id: string}, currency: string, total_amount: int, payload?: string}|array{_: 'messageActionGiftStars', currency: string, amount: int, stars: int, crypto_currency?: string, crypto_amount?: int, transaction_id?: string}|array{_: 'messageActionPrizeStars', boost_peer: array|int|string, unclaimed: bool, stars: int, transaction_id: string, giveaway_msg_id: int}|array{_: 'messageActionStarGift', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, name_hidden: bool, saved: bool, converted: bool, upgraded: bool, refunded: bool, can_upgrade: bool, prepaid_upgrade: bool, upgrade_separate: bool, auction_acquired: bool, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}, convert_stars?: int, upgrade_msg_id?: int, upgrade_stars?: int, from_id?: array|int|string, peer?: array|int|string, saved_id?: int, prepaid_upgrade_hash?: string, gift_msg_id?: int, to_id?: array|int|string, gift_num?: int}|array{_: 'messageActionStarGiftUnique', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, upgrade: bool, transferred: bool, saved: bool, refunded: bool, prepaid_upgrade: bool, assigned: bool, from_offer: bool, craft: bool, name_hidden: bool, can_export_at?: int, transfer_stars?: int, from_id?: array|int|string, peer?: array|int|string, saved_id?: int, resale_amount?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, can_transfer_at?: int, can_resell_at?: int, drop_original_details_stars?: int, can_craft_at?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}|array{_: 'messageActionPaidMessagesRefunded', count: int, stars: int}|array{_: 'messageActionPaidMessagesPrice', broadcast_messages_allowed: bool, stars: int}|array{_: 'messageActionConferenceCall', missed: bool, active: bool, video: bool, call_id: int, duration?: int, other_participants?: list<array|int|string>}|array{_: 'messageActionTodoCompletions', completed: list<int>, incompleted: list<int>}|array{_: 'messageActionTodoAppendTasks', list: list<array{_: 'todoItem', title: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, id: int}>}|array{_: 'messageActionSuggestedPostApproval', rejected: bool, balance_too_low: bool, reject_comment?: string, schedule_date?: int, price?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}}|array{_: 'messageActionSuggestedPostSuccess', price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}}|array{_: 'messageActionSuggestedPostRefund', payer_initiated: bool}|array{_: 'messageActionGiftTon', currency: string, amount: int, crypto_currency: string, crypto_amount: int, transaction_id?: string}|array{_: 'messageActionSuggestBirthday', birthday: array{_: 'birthday', day: int, month: int, year?: int}}|array{_: 'messageActionStarGiftPurchaseOffer', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, accepted: bool, declined: bool, expires_at: int}|array{_: 'messageActionStarGiftPurchaseOfferDeclined', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, expired: bool}|array{_: 'messageActionNewCreatorPending', new_creator_id: int}|array{_: 'messageActionChangeCreator', new_creator_id: int}|array{_: 'messageActionNoForwardsToggle', prev_value: bool, new_value: bool}|array{_: 'messageActionNoForwardsRequest', prev_value: bool, new_value: bool, expired: bool}|array{_: 'messageActionPollAppendAnswer', answer: array{_: 'pollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, option: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, added_by?: array|int|string, date?: int}|array{_: 'inputPollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, media?: \danog\MadelineProto\EventHandler\Media|string|array}}|array{_: 'messageActionPollDeleteAnswer', answer: array{_: 'pollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, option: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, added_by?: array|int|string, date?: int}|array{_: 'inputPollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, media?: \danog\MadelineProto\EventHandler\Media|string|array}}|array{_: 'messageActionManagedBotCreated', bot_id: int}|array{_: 'messageActionChangeCommunity', community_id?: int}|array{_: 'messageActionChatJoinedViaCommunity', community_id: int}|array{_: 'messageActionGramTransfer', comment_encrypted: bool, amount: int, peer_address: string, transaction_id: string, comment?: string}|array{_: 'messageActionWalletTonConnectRequest', accepted: bool, declined: bool, session_id: int, expires: int, topic?: string, trace_id?: string, dapp_name?: string}|array{_: 'messageActionNoPaidMessagesTokenUsed'}, reactions?: array{_: 'messageReactions', min: bool, can_see_list: bool, reactions_as_tags: bool, results: list<array{_: 'reactionCount', chosen_order?: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}, count: int}>, recent_reactions?: list<array{_: 'messagePeerReaction', peer_id: array|int|string, big: bool, unread: bool, my: bool, date: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}}>, top_reactors?: list<array{_: 'messageReactor', top: bool, my: bool, anonymous: bool, peer_id?: array|int|string, count: int}>}, ttl_period?: int}>, chats: list<array|int|string>, users: list<array|int|string>}|array{_: 'messages.savedDialogsSlice', count: int, dialogs: list<array{_: 'savedDialog', peer: array|int|string, pinned: bool, top_message: int}|array{_: 'monoForumDialog', peer: array|int|string, unread_mark: bool, nopaid_messages_exception: bool, top_message: int, read_inbox_max_id: int, read_outbox_max_id: int, unread_count: int, unread_reactions_count: int, draft?: array{_: 'draftMessageEmpty', date?: int}|array{_: 'draftMessage', no_webpage: bool, invert_media: bool, reply_to?: array{_: 'inputReplyToMessage', reply_to_msg_id: int, top_msg_id?: int, reply_to_peer_id?: array|int|string, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, quote_offset?: int, monoforum_peer_id?: array|int|string, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, url: string, webpage_id: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, email: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, phone: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, name: string}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, user_id: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, date: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, type: array{_: 'inlineButtonTypeUrl', url: array}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: array, url: array, button_id: array}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: array, fwd_text?: array, url: array, bot?: array}|array{_: 'inlineButtonTypeWebView', url: array}|array{_: 'inlineButtonTypeCallback', requires_password: array, data: array}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: array, query: array, peer_types?: list<array>}|array{_: 'inlineButtonTypeUserProfile', user_id: array}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array}|array{_: 'inlineButtonTypeCopy', copy_text: array}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: array, bg_danger: array, bg_success: array, link: array}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}>}|array{_: 'inputReplyToStory', peer: array|int|string, story_id: int}|array{_: 'inputReplyToMonoForum', monoforum_peer_id: array|int|string}|array{_: 'inputReplyToEphemeralMessage', id: int}, message: string, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, media?: \danog\MadelineProto\EventHandler\Media|string|array, date: int, effect?: int, suggested_post?: array{_: 'suggestedPost', accepted: bool, rejected: bool, price?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, schedule_date?: int}, rich_message?: array{_: 'richMessage', rtl: bool, part: bool, blocks: list<array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, published_date: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, language: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: string}|array{_: 'pageBlockList', items: list<array{_: 'pageListItemText', text: array, checkbox: array, checked: array}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, collapsed: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array, credit: array}, spoiler: bool, photo_id: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array, credit: array}, autoplay: bool, loop: bool, spoiler: bool, video_id: int}|array{_: 'pageBlockCover', cover: array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array}|array{_: 'pageBlockSubtitle', text: array}|array{_: 'pageBlockAuthorDate', author: array, published_date: array}|array{_: 'pageBlockHeader', text: array}|array{_: 'pageBlockSubheader', text: array}|array{_: 'pageBlockParagraph', text: array}|array{_: 'pageBlockPreformatted', text: array, language: array}|array{_: 'pageBlockFooter', text: array}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: array}|array{_: 'pageBlockList', items: list<array>}|array{_: 'pageBlockBlockquote', text: array, caption: array, collapsed: array}|array{_: 'pageBlockPullquote', text: array, caption: array}|array{_: 'pageBlockPhoto', caption: array, spoiler: array, photo_id: array, url?: array, webpage_id?: array}|array{_: 'pageBlockVideo', caption: array, autoplay: array, loop: array, spoiler: array, video_id: array}|array{_: 'pageBlockEmbed', caption: array, full_width: array, allow_scrolling: array, url?: array, html?: array, poster_photo_id?: array, w?: array, h?: array}|array{_: 'pageBlockChannel', channel: array}|array{_: 'pageBlockAudio', caption: array, audio_id: array}|array{_: 'pageBlockKicker', text: array}|array{_: 'pageBlockTable', title: array, bordered: array, striped: array, compact: array, rows: list<array>}|array{_: 'pageBlockOrderedList', reversed: array, items: list<array>, start?: array, type?: array}|array{_: 'pageBlockRelatedArticles', title: array, articles: list<array>}|array{_: 'pageBlockMap', caption: array, geo: array, zoom: array, w: array, h: array}|array{_: 'pageBlockHeading1', text: array}|array{_: 'pageBlockHeading2', text: array}|array{_: 'pageBlockHeading3', text: array}|array{_: 'pageBlockHeading4', text: array}|array{_: 'pageBlockHeading5', text: array}|array{_: 'pageBlockHeading6', text: array}|array{_: 'pageBlockMath', source: array}|array{_: 'pageBlockThinking', text: array}|array{_: 'inputPageBlockMap', caption: array, geo: array, zoom: array, w: array, h: array}|array{_: 'pageBlockButtonRow', align_left: array, align_center: array, align_right: array, buttons: list<array>}|array{_: 'pageBlockDocument', caption: array, document_id: array}}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array, credit: array}, full_width: bool, allow_scrolling: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array, credit: array}, audio_id: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, bordered: bool, striped: bool, compact: bool, rows: list<array{_: 'pageTableRow', cells: list<array>}>}|array{_: 'pageBlockOrderedList', reversed: bool, items: list<array{_: 'pageListOrderedItemText', text: array, checkbox: array, checked: array, num?: array, value?: array, type?: array}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, articles: list<array{_: 'pageRelatedArticle', url: array, webpage_id: array, title?: array, description?: array, photo_id?: array, author?: array, published_date?: array}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockMath', source: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: array, long: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockButtonRow', align_left: bool, align_center: bool, align_right: bool, buttons: list<array{_: 'pageButton', text: array, type: array, style?: array}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array, credit: array}, document_id: int}>, photos: list<array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}}}>, messages: list<array{_: 'messageEmpty', id: int, peer_id?: array|int|string}|array{_: 'message', peer_id: array|int|string, out: bool, mentioned: bool, media_unread: bool, silent: bool, post: bool, from_scheduled: bool, legacy: bool, edit_hide: bool, pinned: bool, noforwards: bool, invert_media: bool, offline: bool, video_processing_pending: bool, paid_suggested_post_stars: bool, paid_suggested_post_ton: bool, id: int, from_id?: array|int|string, from_boosts_applied?: int, from_rank?: string, saved_peer_id?: array|int|string, fwd_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, via_bot_id?: int, via_business_bot_id?: int, guestchat_via_from?: array|int|string, reply_to?: array{_: 'messageReplyHeader', reply_to_scheduled: bool, forum_topic: bool, quote: bool, reply_to_ephemeral: bool, reply_to_msg_id?: int, reply_to_peer_id?: array|int|string, reply_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, reply_media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_to_top_id?: int, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, quote_offset?: int, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, url: string, webpage_id: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, email: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, phone: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, name: string}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, user_id: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, date: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, type: array{_: 'inlineButtonTypeUrl', url: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url: string, button_id: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: bool, fwd_text?: string, url: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url: string}|array{_: 'inlineButtonTypeCallback', requires_password: bool, data: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: bool, query: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: bool, bg_danger: bool, bg_success: bool, link: bool}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}>}|array{_: 'messageReplyStoryHeader', peer: array|int|string, story_id: int}, date: int, message: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_markup?: array, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, views?: int, forwards?: int, replies?: array{_: 'messageReplies', comments: bool, replies: int, replies_pts: int, recent_repliers?: list<array|int|string>, channel_id?: int, max_id?: int, read_max_id?: int}, edit_date?: int, post_author?: string, grouped_id?: int, reactions?: array{_: 'messageReactions', min: bool, can_see_list: bool, reactions_as_tags: bool, results: list<array{_: 'reactionCount', chosen_order?: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}, count: int}>, recent_reactions?: list<array{_: 'messagePeerReaction', peer_id: array|int|string, big: bool, unread: bool, my: bool, date: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}}>, top_reactors?: list<array{_: 'messageReactor', top: bool, my: bool, anonymous: bool, peer_id?: array|int|string, count: int}>}, restriction_reason?: list<array{_: 'restrictionReason', platform: string, reason: string, text: string}>, ttl_period?: int, quick_reply_shortcut_id?: int, effect?: int, factcheck?: array{_: 'factCheck', need_check: bool, country?: string, text?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}, hash: list<int|string>}, report_delivery_until_date?: int, paid_message_stars?: int, suggested_post?: array{_: 'suggestedPost', accepted: bool, rejected: bool, price?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, schedule_date?: int}, schedule_repeat_period?: int, summary_from_language?: string, rich_message?: array{_: 'richMessage', rtl: bool, part: bool, blocks: list<array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, published_date: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, language: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: string}|array{_: 'pageBlockList', items: list<array{_: 'pageListItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, checkbox: bool, checked: bool}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, collapsed: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, spoiler: bool, photo_id: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, autoplay: bool, loop: bool, spoiler: bool, video_id: int}|array{_: 'pageBlockCover', cover: array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, published_date: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, language: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: string}|array{_: 'pageBlockList', items: list<array{_: 'pageListItemText', text: array, checkbox: array, checked: array}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, collapsed: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array, credit: array}, spoiler: bool, photo_id: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array, credit: array}, autoplay: bool, loop: bool, spoiler: bool, video_id: int}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array, credit: array}, full_width: bool, allow_scrolling: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array, credit: array}, audio_id: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, bordered: bool, striped: bool, compact: bool, rows: list<array{_: 'pageTableRow', cells: list<array>}>}|array{_: 'pageBlockOrderedList', reversed: bool, items: list<array{_: 'pageListOrderedItemText', text: array, checkbox: array, checked: array, num?: array, value?: array, type?: array}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, articles: list<array{_: 'pageRelatedArticle', url: array, webpage_id: array, title?: array, description?: array, photo_id?: array, author?: array, published_date?: array}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockMath', source: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: array, long: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockButtonRow', align_left: bool, align_center: bool, align_right: bool, buttons: list<array{_: 'pageButton', text: array, type: array, style?: array}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array, credit: array}, document_id: int}}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, full_width: bool, allow_scrolling: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, audio_id: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, bordered: bool, striped: bool, compact: bool, rows: list<array{_: 'pageTableRow', cells: list<array{_: 'pageTableCell', header: array, align_center: array, align_right: array, valign_middle: array, valign_bottom: array, text?: array, colspan?: array, rowspan?: array}>}>}|array{_: 'pageBlockOrderedList', reversed: bool, items: list<array{_: 'pageListOrderedItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, checkbox: bool, checked: bool, num?: string, value?: int, type?: string}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, articles: list<array{_: 'pageRelatedArticle', url: string, webpage_id: int, title?: string, description?: string, photo_id?: int, author?: string, published_date?: int}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash: int, accuracy_radius?: int}, zoom: int, w: int, h: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockMath', source: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, geo: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: float, long: float, accuracy_radius?: int}, zoom: int, w: int, h: int}|array{_: 'pageBlockButtonRow', align_left: bool, align_center: bool, align_right: bool, buttons: list<array{_: 'pageButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, type: array{_: 'inlineButtonTypeUrl', url: array}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: array, url: array, button_id: array}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: array, fwd_text?: array, url: array, bot?: array}|array{_: 'inlineButtonTypeWebView', url: array}|array{_: 'inlineButtonTypeCallback', requires_password: array, data: array}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: array, query: array, peer_types?: list<array>}|array{_: 'inlineButtonTypeUserProfile', user_id: array}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array}|array{_: 'inlineButtonTypeCopy', copy_text: array}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: array, bg_danger: array, bg_success: array, link: array}}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, document_id: int}>, photos: list<array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}}|array{_: 'messageService', peer_id: array|int|string, out: bool, mentioned: bool, media_unread: bool, reactions_are_possible: bool, silent: bool, post: bool, legacy: bool, id: int, from_id?: array|int|string, saved_peer_id?: array|int|string, reply_to?: array{_: 'messageReplyHeader', reply_to_scheduled: bool, forum_topic: bool, quote: bool, reply_to_ephemeral: bool, reply_to_msg_id?: int, reply_to_peer_id?: array|int|string, reply_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, reply_media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_to_top_id?: int, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, quote_offset?: int, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, url: string, webpage_id: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, email: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, phone: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, name: string}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, user_id: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, date: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, type: array{_: 'inlineButtonTypeUrl', url: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url: string, button_id: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: bool, fwd_text?: string, url: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url: string}|array{_: 'inlineButtonTypeCallback', requires_password: bool, data: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: bool, query: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: bool, bg_danger: bool, bg_success: bool, link: bool}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}>}|array{_: 'messageReplyStoryHeader', peer: array|int|string, story_id: int}, date: int, action: array{_: 'messageActionEmpty'}|array{_: 'messageActionChatCreate', title: string, users: list<int>}|array{_: 'messageActionChatEditTitle', title: string}|array{_: 'messageActionChatEditPhoto', photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'messageActionChatDeletePhoto'}|array{_: 'messageActionChatAddUser', users: list<int>}|array{_: 'messageActionChatDeleteUser', user_id: int}|array{_: 'messageActionChatJoinedByLink', inviter_id: int}|array{_: 'messageActionChannelCreate', title: string}|array{_: 'messageActionChatMigrateTo', channel_id: int}|array{_: 'messageActionChannelMigrateFrom', title: string, chat_id: array|int|string}|array{_: 'messageActionPinMessage'}|array{_: 'messageActionHistoryClear'}|array{_: 'messageActionGameScore', game_id: int, score: int}|array{_: 'messageActionPaymentSentMe', charge: array{_: 'paymentCharge', id: string, provider_charge_id: string}, recurring_init: bool, recurring_used: bool, currency: string, total_amount: int, payload: string, info?: array{_: 'paymentRequestedInfo', name?: string, phone?: string, email?: string, shipping_address?: array{_: 'postAddress', street_line1: string, street_line2: string, city: string, state: string, country_iso2: string, post_code: string}}, shipping_option_id?: string, subscription_until_date?: int}|array{_: 'messageActionPaymentSent', recurring_init: bool, recurring_used: bool, currency: string, total_amount: int, invoice_slug?: string, subscription_until_date?: int}|array{_: 'messageActionPhoneCall', video: bool, call_id: int, reason?: array{_: 'phoneCallDiscardReasonMissed'}|array{_: 'phoneCallDiscardReasonDisconnect'}|array{_: 'phoneCallDiscardReasonHangup'}|array{_: 'phoneCallDiscardReasonBusy'}|array{_: 'phoneCallDiscardReasonMigrateConferenceCall', slug: string}, duration?: int}|array{_: 'messageActionScreenshotTaken'}|array{_: 'messageActionCustomAction', message: string}|array{_: 'messageActionBotAllowed', attach_menu: bool, from_request: bool, domain?: string, app?: array{_: 'botAppNotModified'}|array{_: 'botApp', id: int, access_hash: int, short_name: string, title: string, description: string, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, hash: list<int|string>}}|array{_: 'messageActionSecureValuesSentMe', credentials: array{_: 'secureCredentialsEncrypted', data: string, hash: string, secret: string}, values: list<array{_: 'secureValue', type: array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}, data?: array{_: 'secureData', data: string, data_hash: string, secret: string}, front_side?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, reverse_side?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, selfie?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, translation?: list<array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}>, files?: list<array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}>, plain_data?: array{_: 'securePlainPhone', phone: string}|array{_: 'securePlainEmail', email: string}, hash: string}>}|array{_: 'messageActionSecureValuesSent', types: list<array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}>}|array{_: 'messageActionContactSignUp'}|array{_: 'messageActionGeoProximityReached', from_id: array|int|string, to_id: array|int|string, distance: int}|array{_: 'messageActionGroupCall', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, duration?: int}|array{_: 'messageActionInviteToGroupCall', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, users: list<int>}|array{_: 'messageActionSetMessagesTTL', period: int, auto_setting_from?: int}|array{_: 'messageActionGroupCallScheduled', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, schedule_date: int}|array{_: 'messageActionSetChatTheme', theme: array{_: 'chatTheme', emoticon: string}|array{_: 'chatThemeUniqueGift', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: array, edge_color: array, text_color: array}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array, crafted: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array, name: array, backdrop_id: array, center_color: array, edge_color: array, pattern_color: array, text_color: array}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array, sender_id?: array, date: array, message?: array}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: array, nanos: array}|array{_: 'starsTonAmount', amount: array}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: array, background_emoji_id?: array}|array{_: 'peerColorCollectible', collectible_id: array, gift_emoji_id: array, background_emoji_id: array, accent_color: array, colors: list<array>, dark_accent_color?: array, dark_colors?: list<array>}|array{_: 'inputPeerColorCollectible', collectible_id: array}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, theme_settings: list<array{_: 'themeSettings', base_theme: array{_: 'baseThemeClassic'}|array{_: 'baseThemeDay'}|array{_: 'baseThemeNight'}|array{_: 'baseThemeTinted'}|array{_: 'baseThemeArctic'}, message_colors_animated: bool, accent_color: int, outbox_accent_color?: int, message_colors?: list<int>, wallpaper?: array{_: 'wallPaper', id: array, creator: array, default: array, pattern: array, dark: array, access_hash: array, slug: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, settings?: array}|array{_: 'wallPaperNoFile', id: array, default: array, dark: array, settings?: array}}>}}|array{_: 'messageActionChatJoinedByRequest'}|array{_: 'messageActionWebViewDataSentMe', text: string, data: string}|array{_: 'messageActionWebViewDataSent', text: string}|array{_: 'messageActionGiftPremium', currency: string, amount: int, days: int, crypto_currency?: string, crypto_amount?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}|array{_: 'messageActionTopicCreate', title_missing: bool, title: string, icon_color: int, icon_emoji_id?: int}|array{_: 'messageActionTopicEdit', title?: string, icon_emoji_id?: int, closed?: bool, hidden?: bool}|array{_: 'messageActionSuggestProfilePhoto', photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'messageActionRequestedPeer', button_id: int, peers: list<array|int|string>}|array{_: 'messageActionSetChatWallPaper', wallpaper: array{_: 'wallPaper', id: int, creator: bool, default: bool, pattern: bool, dark: bool, access_hash: int, slug: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, settings?: array{_: 'wallPaperSettings', blur: bool, motion: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string}}|array{_: 'wallPaperNoFile', id: int, default: bool, dark: bool, settings?: array{_: 'wallPaperSettings', blur: bool, motion: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string}}, same: bool, for_both: bool}|array{_: 'messageActionGiftCode', via_giveaway: bool, unclaimed: bool, boost_peer?: array|int|string, days: int, slug: string, currency?: string, amount?: int, crypto_currency?: string, crypto_amount?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}|array{_: 'messageActionGiveawayLaunch', stars?: int}|array{_: 'messageActionGiveawayResults', stars: bool, winners_count: int, unclaimed_count: int}|array{_: 'messageActionBoostApply', boosts: int}|array{_: 'messageActionRequestedPeerSentMe', button_id: int, peers: list<array{_: 'requestedPeerUser', user_id: int, first_name?: string, last_name?: string, username?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'requestedPeerChat', chat_id: array|int|string, title?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'requestedPeerChannel', channel_id: int, title?: string, username?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}>}|array{_: 'messageActionPaymentRefunded', peer: array|int|string, charge: array{_: 'paymentCharge', id: string, provider_charge_id: string}, currency: string, total_amount: int, payload?: string}|array{_: 'messageActionGiftStars', currency: string, amount: int, stars: int, crypto_currency?: string, crypto_amount?: int, transaction_id?: string}|array{_: 'messageActionPrizeStars', boost_peer: array|int|string, unclaimed: bool, stars: int, transaction_id: string, giveaway_msg_id: int}|array{_: 'messageActionStarGift', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, name_hidden: bool, saved: bool, converted: bool, upgraded: bool, refunded: bool, can_upgrade: bool, prepaid_upgrade: bool, upgrade_separate: bool, auction_acquired: bool, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}, convert_stars?: int, upgrade_msg_id?: int, upgrade_stars?: int, from_id?: array|int|string, peer?: array|int|string, saved_id?: int, prepaid_upgrade_hash?: string, gift_msg_id?: int, to_id?: array|int|string, gift_num?: int}|array{_: 'messageActionStarGiftUnique', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, upgrade: bool, transferred: bool, saved: bool, refunded: bool, prepaid_upgrade: bool, assigned: bool, from_offer: bool, craft: bool, name_hidden: bool, can_export_at?: int, transfer_stars?: int, from_id?: array|int|string, peer?: array|int|string, saved_id?: int, resale_amount?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, can_transfer_at?: int, can_resell_at?: int, drop_original_details_stars?: int, can_craft_at?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}|array{_: 'messageActionPaidMessagesRefunded', count: int, stars: int}|array{_: 'messageActionPaidMessagesPrice', broadcast_messages_allowed: bool, stars: int}|array{_: 'messageActionConferenceCall', missed: bool, active: bool, video: bool, call_id: int, duration?: int, other_participants?: list<array|int|string>}|array{_: 'messageActionTodoCompletions', completed: list<int>, incompleted: list<int>}|array{_: 'messageActionTodoAppendTasks', list: list<array{_: 'todoItem', title: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, id: int}>}|array{_: 'messageActionSuggestedPostApproval', rejected: bool, balance_too_low: bool, reject_comment?: string, schedule_date?: int, price?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}}|array{_: 'messageActionSuggestedPostSuccess', price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}}|array{_: 'messageActionSuggestedPostRefund', payer_initiated: bool}|array{_: 'messageActionGiftTon', currency: string, amount: int, crypto_currency: string, crypto_amount: int, transaction_id?: string}|array{_: 'messageActionSuggestBirthday', birthday: array{_: 'birthday', day: int, month: int, year?: int}}|array{_: 'messageActionStarGiftPurchaseOffer', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, accepted: bool, declined: bool, expires_at: int}|array{_: 'messageActionStarGiftPurchaseOfferDeclined', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, expired: bool}|array{_: 'messageActionNewCreatorPending', new_creator_id: int}|array{_: 'messageActionChangeCreator', new_creator_id: int}|array{_: 'messageActionNoForwardsToggle', prev_value: bool, new_value: bool}|array{_: 'messageActionNoForwardsRequest', prev_value: bool, new_value: bool, expired: bool}|array{_: 'messageActionPollAppendAnswer', answer: array{_: 'pollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, option: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, added_by?: array|int|string, date?: int}|array{_: 'inputPollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, media?: \danog\MadelineProto\EventHandler\Media|string|array}}|array{_: 'messageActionPollDeleteAnswer', answer: array{_: 'pollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, option: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, added_by?: array|int|string, date?: int}|array{_: 'inputPollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, media?: \danog\MadelineProto\EventHandler\Media|string|array}}|array{_: 'messageActionManagedBotCreated', bot_id: int}|array{_: 'messageActionChangeCommunity', community_id?: int}|array{_: 'messageActionChatJoinedViaCommunity', community_id: int}|array{_: 'messageActionGramTransfer', comment_encrypted: bool, amount: int, peer_address: string, transaction_id: string, comment?: string}|array{_: 'messageActionWalletTonConnectRequest', accepted: bool, declined: bool, session_id: int, expires: int, topic?: string, trace_id?: string, dapp_name?: string}|array{_: 'messageActionNoPaidMessagesTokenUsed'}, reactions?: array{_: 'messageReactions', min: bool, can_see_list: bool, reactions_as_tags: bool, results: list<array{_: 'reactionCount', chosen_order?: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}, count: int}>, recent_reactions?: list<array{_: 'messagePeerReaction', peer_id: array|int|string, big: bool, unread: bool, my: bool, date: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}}>, top_reactors?: list<array{_: 'messageReactor', top: bool, my: bool, anonymous: bool, peer_id?: array|int|string, count: int}>}, ttl_period?: int}>, chats: list<array|int|string>, users: list<array|int|string>}|array{_: 'messages.savedDialogsNotModified', count: int} @see https://docs.madelineproto.xyz/API_docs/types/messages.SavedDialogs.html
     * @psalm-impure
     */
    public function getSavedDialogs(bool|null $exclude_pinned = null, array|int|string|null $parent_peer = null, int|null $offset_date = 0, int|null $offset_id = 0, array|int|string|null $offset_peer = null, int|null $limit = 0, array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Fetch [saved messages Â»](https://core.telegram.org/api/saved-messages) forwarded from a specific peer, or fetch messages from a [monoforum topic Â»](https://core.telegram.org/api/monoforum).
     *
     * @param array|int|string $parent_peer If set, fetches messages from the specified monoforum, otherwise fetches from saved messages. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param array|int|string $peer Target peer (or topic) @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $offset_id Only return messages starting from the specified message ID
     * @param int $offset_date Only return messages sent before the specified date
     * @param int $add_offset Number of list elements to be skipped, negative values are also accepted.
     * @param int $limit Number of results to return
     * @param int $max_id If a positive value was transferred, the method will return only messages with IDs less than **max\_id**
     * @param int $min_id If a positive value was transferred, the method will return only messages with IDs more than **min\_id**
     * @param list<int|string>|array<never, never> $hash Array of [Result hash](https://core.telegram.org/api/offsets) @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.messages', messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.messagesSlice', inexact: array, count: array, next_rate?: array, offset_id_offset?: array, search_flood?: array, messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.channelMessages', inexact: array, pts: array, count: array, offset_id_offset?: array, messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.messagesNotModified', count: array} @see https://docs.madelineproto.xyz/API_docs/types/messages.Messages.html
     * @psalm-impure
     */
    public function getSavedHistory(array|int|string|null $parent_peer = null, array|int|string|null $peer = null, int|null $offset_id = 0, int|null $offset_date = 0, int|null $add_offset = 0, int|null $limit = 0, int|null $max_id = 0, int|null $min_id = 0, array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Deletes messages from a [monoforum topic Â»](https://core.telegram.org/api/monoforum), or deletes messages forwarded from a specific peer to [saved messages Â»](https://core.telegram.org/api/saved-messages).
     *
     * @param array|int|string $parent_peer If set, affects the messages of the passed [monoforum topic Â»](https://core.telegram.org/api/monoforum), otherwise affects [saved messages Â»](https://core.telegram.org/api/saved-messages). @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param array|int|string $peer Peer, whose messages will be deleted from [saved messages Â»](https://core.telegram.org/api/saved-messages), or the ID of the topic. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $max_id Maximum ID of message to delete
     * @param int $min_date Delete all messages newer than this UNIX timestamp
     * @param int $max_date Delete all messages older than this UNIX timestamp
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.affectedHistory', pts: int, pts_count: int, offset: int} @see https://docs.madelineproto.xyz/API_docs/types/messages.AffectedHistory.html
     * @psalm-impure
     */
    public function deleteSavedHistory(array|int|string|null $parent_peer = null, array|int|string|null $peer = null, int|null $max_id = 0, int|null $min_date = null, int|null $max_date = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get pinned [saved dialogs, see here Â»](https://core.telegram.org/api/saved-messages) for more info.
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.savedDialogs', dialogs: list<array{_: 'savedDialog', peer: array|int|string, pinned: bool, top_message: int}|array{_: 'monoForumDialog', peer: array|int|string, unread_mark: bool, nopaid_messages_exception: bool, top_message: int, read_inbox_max_id: int, read_outbox_max_id: int, unread_count: int, unread_reactions_count: int, draft?: array{_: 'draftMessageEmpty', date?: int}|array{_: 'draftMessage', no_webpage: bool, invert_media: bool, reply_to?: array{_: 'inputReplyToMessage', reply_to_msg_id: int, top_msg_id?: int, reply_to_peer_id?: array|int|string, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, quote_offset?: int, monoforum_peer_id?: array|int|string, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, url: string, webpage_id: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, email: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, phone: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, name: string}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, user_id: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, date: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, type: array{_: 'inlineButtonTypeUrl', url: array}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: array, url: array, button_id: array}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: array, fwd_text?: array, url: array, bot?: array}|array{_: 'inlineButtonTypeWebView', url: array}|array{_: 'inlineButtonTypeCallback', requires_password: array, data: array}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: array, query: array, peer_types?: list<array>}|array{_: 'inlineButtonTypeUserProfile', user_id: array}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array}|array{_: 'inlineButtonTypeCopy', copy_text: array}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: array, bg_danger: array, bg_success: array, link: array}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}>}|array{_: 'inputReplyToStory', peer: array|int|string, story_id: int}|array{_: 'inputReplyToMonoForum', monoforum_peer_id: array|int|string}|array{_: 'inputReplyToEphemeralMessage', id: int}, message: string, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, media?: \danog\MadelineProto\EventHandler\Media|string|array, date: int, effect?: int, suggested_post?: array{_: 'suggestedPost', accepted: bool, rejected: bool, price?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, schedule_date?: int}, rich_message?: array{_: 'richMessage', rtl: bool, part: bool, blocks: list<array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, published_date: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, language: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: string}|array{_: 'pageBlockList', items: list<array{_: 'pageListItemText', text: array, checkbox: array, checked: array}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, collapsed: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array, credit: array}, spoiler: bool, photo_id: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array, credit: array}, autoplay: bool, loop: bool, spoiler: bool, video_id: int}|array{_: 'pageBlockCover', cover: array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array}|array{_: 'pageBlockSubtitle', text: array}|array{_: 'pageBlockAuthorDate', author: array, published_date: array}|array{_: 'pageBlockHeader', text: array}|array{_: 'pageBlockSubheader', text: array}|array{_: 'pageBlockParagraph', text: array}|array{_: 'pageBlockPreformatted', text: array, language: array}|array{_: 'pageBlockFooter', text: array}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: array}|array{_: 'pageBlockList', items: list<array>}|array{_: 'pageBlockBlockquote', text: array, caption: array, collapsed: array}|array{_: 'pageBlockPullquote', text: array, caption: array}|array{_: 'pageBlockPhoto', caption: array, spoiler: array, photo_id: array, url?: array, webpage_id?: array}|array{_: 'pageBlockVideo', caption: array, autoplay: array, loop: array, spoiler: array, video_id: array}|array{_: 'pageBlockEmbed', caption: array, full_width: array, allow_scrolling: array, url?: array, html?: array, poster_photo_id?: array, w?: array, h?: array}|array{_: 'pageBlockChannel', channel: array}|array{_: 'pageBlockAudio', caption: array, audio_id: array}|array{_: 'pageBlockKicker', text: array}|array{_: 'pageBlockTable', title: array, bordered: array, striped: array, compact: array, rows: list<array>}|array{_: 'pageBlockOrderedList', reversed: array, items: list<array>, start?: array, type?: array}|array{_: 'pageBlockRelatedArticles', title: array, articles: list<array>}|array{_: 'pageBlockMap', caption: array, geo: array, zoom: array, w: array, h: array}|array{_: 'pageBlockHeading1', text: array}|array{_: 'pageBlockHeading2', text: array}|array{_: 'pageBlockHeading3', text: array}|array{_: 'pageBlockHeading4', text: array}|array{_: 'pageBlockHeading5', text: array}|array{_: 'pageBlockHeading6', text: array}|array{_: 'pageBlockMath', source: array}|array{_: 'pageBlockThinking', text: array}|array{_: 'inputPageBlockMap', caption: array, geo: array, zoom: array, w: array, h: array}|array{_: 'pageBlockButtonRow', align_left: array, align_center: array, align_right: array, buttons: list<array>}|array{_: 'pageBlockDocument', caption: array, document_id: array}}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array, credit: array}, full_width: bool, allow_scrolling: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array, credit: array}, audio_id: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, bordered: bool, striped: bool, compact: bool, rows: list<array{_: 'pageTableRow', cells: list<array>}>}|array{_: 'pageBlockOrderedList', reversed: bool, items: list<array{_: 'pageListOrderedItemText', text: array, checkbox: array, checked: array, num?: array, value?: array, type?: array}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, articles: list<array{_: 'pageRelatedArticle', url: array, webpage_id: array, title?: array, description?: array, photo_id?: array, author?: array, published_date?: array}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockMath', source: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: array, long: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockButtonRow', align_left: bool, align_center: bool, align_right: bool, buttons: list<array{_: 'pageButton', text: array, type: array, style?: array}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array, credit: array}, document_id: int}>, photos: list<array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}}}>, messages: list<array{_: 'messageEmpty', id: int, peer_id?: array|int|string}|array{_: 'message', peer_id: array|int|string, out: bool, mentioned: bool, media_unread: bool, silent: bool, post: bool, from_scheduled: bool, legacy: bool, edit_hide: bool, pinned: bool, noforwards: bool, invert_media: bool, offline: bool, video_processing_pending: bool, paid_suggested_post_stars: bool, paid_suggested_post_ton: bool, id: int, from_id?: array|int|string, from_boosts_applied?: int, from_rank?: string, saved_peer_id?: array|int|string, fwd_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, via_bot_id?: int, via_business_bot_id?: int, guestchat_via_from?: array|int|string, reply_to?: array{_: 'messageReplyHeader', reply_to_scheduled: bool, forum_topic: bool, quote: bool, reply_to_ephemeral: bool, reply_to_msg_id?: int, reply_to_peer_id?: array|int|string, reply_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, reply_media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_to_top_id?: int, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, quote_offset?: int, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, url: string, webpage_id: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, email: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, phone: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, name: string}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, user_id: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, date: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, type: array{_: 'inlineButtonTypeUrl', url: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url: string, button_id: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: bool, fwd_text?: string, url: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url: string}|array{_: 'inlineButtonTypeCallback', requires_password: bool, data: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: bool, query: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: bool, bg_danger: bool, bg_success: bool, link: bool}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}>}|array{_: 'messageReplyStoryHeader', peer: array|int|string, story_id: int}, date: int, message: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_markup?: array, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, views?: int, forwards?: int, replies?: array{_: 'messageReplies', comments: bool, replies: int, replies_pts: int, recent_repliers?: list<array|int|string>, channel_id?: int, max_id?: int, read_max_id?: int}, edit_date?: int, post_author?: string, grouped_id?: int, reactions?: array{_: 'messageReactions', min: bool, can_see_list: bool, reactions_as_tags: bool, results: list<array{_: 'reactionCount', chosen_order?: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}, count: int}>, recent_reactions?: list<array{_: 'messagePeerReaction', peer_id: array|int|string, big: bool, unread: bool, my: bool, date: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}}>, top_reactors?: list<array{_: 'messageReactor', top: bool, my: bool, anonymous: bool, peer_id?: array|int|string, count: int}>}, restriction_reason?: list<array{_: 'restrictionReason', platform: string, reason: string, text: string}>, ttl_period?: int, quick_reply_shortcut_id?: int, effect?: int, factcheck?: array{_: 'factCheck', need_check: bool, country?: string, text?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}, hash: list<int|string>}, report_delivery_until_date?: int, paid_message_stars?: int, suggested_post?: array{_: 'suggestedPost', accepted: bool, rejected: bool, price?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, schedule_date?: int}, schedule_repeat_period?: int, summary_from_language?: string, rich_message?: array{_: 'richMessage', rtl: bool, part: bool, blocks: list<array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, published_date: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, language: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: string}|array{_: 'pageBlockList', items: list<array{_: 'pageListItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, checkbox: bool, checked: bool}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, collapsed: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, spoiler: bool, photo_id: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, autoplay: bool, loop: bool, spoiler: bool, video_id: int}|array{_: 'pageBlockCover', cover: array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, published_date: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, language: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: string}|array{_: 'pageBlockList', items: list<array{_: 'pageListItemText', text: array, checkbox: array, checked: array}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, collapsed: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array, credit: array}, spoiler: bool, photo_id: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array, credit: array}, autoplay: bool, loop: bool, spoiler: bool, video_id: int}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array, credit: array}, full_width: bool, allow_scrolling: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array, credit: array}, audio_id: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, bordered: bool, striped: bool, compact: bool, rows: list<array{_: 'pageTableRow', cells: list<array>}>}|array{_: 'pageBlockOrderedList', reversed: bool, items: list<array{_: 'pageListOrderedItemText', text: array, checkbox: array, checked: array, num?: array, value?: array, type?: array}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, articles: list<array{_: 'pageRelatedArticle', url: array, webpage_id: array, title?: array, description?: array, photo_id?: array, author?: array, published_date?: array}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockMath', source: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: array, long: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockButtonRow', align_left: bool, align_center: bool, align_right: bool, buttons: list<array{_: 'pageButton', text: array, type: array, style?: array}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array, credit: array}, document_id: int}}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, full_width: bool, allow_scrolling: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, audio_id: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, bordered: bool, striped: bool, compact: bool, rows: list<array{_: 'pageTableRow', cells: list<array{_: 'pageTableCell', header: array, align_center: array, align_right: array, valign_middle: array, valign_bottom: array, text?: array, colspan?: array, rowspan?: array}>}>}|array{_: 'pageBlockOrderedList', reversed: bool, items: list<array{_: 'pageListOrderedItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, checkbox: bool, checked: bool, num?: string, value?: int, type?: string}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, articles: list<array{_: 'pageRelatedArticle', url: string, webpage_id: int, title?: string, description?: string, photo_id?: int, author?: string, published_date?: int}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash: int, accuracy_radius?: int}, zoom: int, w: int, h: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockMath', source: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, geo: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: float, long: float, accuracy_radius?: int}, zoom: int, w: int, h: int}|array{_: 'pageBlockButtonRow', align_left: bool, align_center: bool, align_right: bool, buttons: list<array{_: 'pageButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, type: array{_: 'inlineButtonTypeUrl', url: array}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: array, url: array, button_id: array}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: array, fwd_text?: array, url: array, bot?: array}|array{_: 'inlineButtonTypeWebView', url: array}|array{_: 'inlineButtonTypeCallback', requires_password: array, data: array}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: array, query: array, peer_types?: list<array>}|array{_: 'inlineButtonTypeUserProfile', user_id: array}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array}|array{_: 'inlineButtonTypeCopy', copy_text: array}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: array, bg_danger: array, bg_success: array, link: array}}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, document_id: int}>, photos: list<array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}}|array{_: 'messageService', peer_id: array|int|string, out: bool, mentioned: bool, media_unread: bool, reactions_are_possible: bool, silent: bool, post: bool, legacy: bool, id: int, from_id?: array|int|string, saved_peer_id?: array|int|string, reply_to?: array{_: 'messageReplyHeader', reply_to_scheduled: bool, forum_topic: bool, quote: bool, reply_to_ephemeral: bool, reply_to_msg_id?: int, reply_to_peer_id?: array|int|string, reply_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, reply_media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_to_top_id?: int, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, quote_offset?: int, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, url: string, webpage_id: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, email: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, phone: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, name: string}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, user_id: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, date: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, type: array{_: 'inlineButtonTypeUrl', url: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url: string, button_id: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: bool, fwd_text?: string, url: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url: string}|array{_: 'inlineButtonTypeCallback', requires_password: bool, data: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: bool, query: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: bool, bg_danger: bool, bg_success: bool, link: bool}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}>}|array{_: 'messageReplyStoryHeader', peer: array|int|string, story_id: int}, date: int, action: array{_: 'messageActionEmpty'}|array{_: 'messageActionChatCreate', title: string, users: list<int>}|array{_: 'messageActionChatEditTitle', title: string}|array{_: 'messageActionChatEditPhoto', photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'messageActionChatDeletePhoto'}|array{_: 'messageActionChatAddUser', users: list<int>}|array{_: 'messageActionChatDeleteUser', user_id: int}|array{_: 'messageActionChatJoinedByLink', inviter_id: int}|array{_: 'messageActionChannelCreate', title: string}|array{_: 'messageActionChatMigrateTo', channel_id: int}|array{_: 'messageActionChannelMigrateFrom', title: string, chat_id: array|int|string}|array{_: 'messageActionPinMessage'}|array{_: 'messageActionHistoryClear'}|array{_: 'messageActionGameScore', game_id: int, score: int}|array{_: 'messageActionPaymentSentMe', charge: array{_: 'paymentCharge', id: string, provider_charge_id: string}, recurring_init: bool, recurring_used: bool, currency: string, total_amount: int, payload: string, info?: array{_: 'paymentRequestedInfo', name?: string, phone?: string, email?: string, shipping_address?: array{_: 'postAddress', street_line1: string, street_line2: string, city: string, state: string, country_iso2: string, post_code: string}}, shipping_option_id?: string, subscription_until_date?: int}|array{_: 'messageActionPaymentSent', recurring_init: bool, recurring_used: bool, currency: string, total_amount: int, invoice_slug?: string, subscription_until_date?: int}|array{_: 'messageActionPhoneCall', video: bool, call_id: int, reason?: array{_: 'phoneCallDiscardReasonMissed'}|array{_: 'phoneCallDiscardReasonDisconnect'}|array{_: 'phoneCallDiscardReasonHangup'}|array{_: 'phoneCallDiscardReasonBusy'}|array{_: 'phoneCallDiscardReasonMigrateConferenceCall', slug: string}, duration?: int}|array{_: 'messageActionScreenshotTaken'}|array{_: 'messageActionCustomAction', message: string}|array{_: 'messageActionBotAllowed', attach_menu: bool, from_request: bool, domain?: string, app?: array{_: 'botAppNotModified'}|array{_: 'botApp', id: int, access_hash: int, short_name: string, title: string, description: string, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, hash: list<int|string>}}|array{_: 'messageActionSecureValuesSentMe', credentials: array{_: 'secureCredentialsEncrypted', data: string, hash: string, secret: string}, values: list<array{_: 'secureValue', type: array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}, data?: array{_: 'secureData', data: string, data_hash: string, secret: string}, front_side?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, reverse_side?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, selfie?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, translation?: list<array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}>, files?: list<array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}>, plain_data?: array{_: 'securePlainPhone', phone: string}|array{_: 'securePlainEmail', email: string}, hash: string}>}|array{_: 'messageActionSecureValuesSent', types: list<array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}>}|array{_: 'messageActionContactSignUp'}|array{_: 'messageActionGeoProximityReached', from_id: array|int|string, to_id: array|int|string, distance: int}|array{_: 'messageActionGroupCall', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, duration?: int}|array{_: 'messageActionInviteToGroupCall', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, users: list<int>}|array{_: 'messageActionSetMessagesTTL', period: int, auto_setting_from?: int}|array{_: 'messageActionGroupCallScheduled', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, schedule_date: int}|array{_: 'messageActionSetChatTheme', theme: array{_: 'chatTheme', emoticon: string}|array{_: 'chatThemeUniqueGift', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: array, edge_color: array, text_color: array}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array, crafted: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array, name: array, backdrop_id: array, center_color: array, edge_color: array, pattern_color: array, text_color: array}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array, sender_id?: array, date: array, message?: array}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: array, nanos: array}|array{_: 'starsTonAmount', amount: array}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: array, background_emoji_id?: array}|array{_: 'peerColorCollectible', collectible_id: array, gift_emoji_id: array, background_emoji_id: array, accent_color: array, colors: list<array>, dark_accent_color?: array, dark_colors?: list<array>}|array{_: 'inputPeerColorCollectible', collectible_id: array}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, theme_settings: list<array{_: 'themeSettings', base_theme: array{_: 'baseThemeClassic'}|array{_: 'baseThemeDay'}|array{_: 'baseThemeNight'}|array{_: 'baseThemeTinted'}|array{_: 'baseThemeArctic'}, message_colors_animated: bool, accent_color: int, outbox_accent_color?: int, message_colors?: list<int>, wallpaper?: array{_: 'wallPaper', id: array, creator: array, default: array, pattern: array, dark: array, access_hash: array, slug: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, settings?: array}|array{_: 'wallPaperNoFile', id: array, default: array, dark: array, settings?: array}}>}}|array{_: 'messageActionChatJoinedByRequest'}|array{_: 'messageActionWebViewDataSentMe', text: string, data: string}|array{_: 'messageActionWebViewDataSent', text: string}|array{_: 'messageActionGiftPremium', currency: string, amount: int, days: int, crypto_currency?: string, crypto_amount?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}|array{_: 'messageActionTopicCreate', title_missing: bool, title: string, icon_color: int, icon_emoji_id?: int}|array{_: 'messageActionTopicEdit', title?: string, icon_emoji_id?: int, closed?: bool, hidden?: bool}|array{_: 'messageActionSuggestProfilePhoto', photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'messageActionRequestedPeer', button_id: int, peers: list<array|int|string>}|array{_: 'messageActionSetChatWallPaper', wallpaper: array{_: 'wallPaper', id: int, creator: bool, default: bool, pattern: bool, dark: bool, access_hash: int, slug: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, settings?: array{_: 'wallPaperSettings', blur: bool, motion: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string}}|array{_: 'wallPaperNoFile', id: int, default: bool, dark: bool, settings?: array{_: 'wallPaperSettings', blur: bool, motion: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string}}, same: bool, for_both: bool}|array{_: 'messageActionGiftCode', via_giveaway: bool, unclaimed: bool, boost_peer?: array|int|string, days: int, slug: string, currency?: string, amount?: int, crypto_currency?: string, crypto_amount?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}|array{_: 'messageActionGiveawayLaunch', stars?: int}|array{_: 'messageActionGiveawayResults', stars: bool, winners_count: int, unclaimed_count: int}|array{_: 'messageActionBoostApply', boosts: int}|array{_: 'messageActionRequestedPeerSentMe', button_id: int, peers: list<array{_: 'requestedPeerUser', user_id: int, first_name?: string, last_name?: string, username?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'requestedPeerChat', chat_id: array|int|string, title?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'requestedPeerChannel', channel_id: int, title?: string, username?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}>}|array{_: 'messageActionPaymentRefunded', peer: array|int|string, charge: array{_: 'paymentCharge', id: string, provider_charge_id: string}, currency: string, total_amount: int, payload?: string}|array{_: 'messageActionGiftStars', currency: string, amount: int, stars: int, crypto_currency?: string, crypto_amount?: int, transaction_id?: string}|array{_: 'messageActionPrizeStars', boost_peer: array|int|string, unclaimed: bool, stars: int, transaction_id: string, giveaway_msg_id: int}|array{_: 'messageActionStarGift', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, name_hidden: bool, saved: bool, converted: bool, upgraded: bool, refunded: bool, can_upgrade: bool, prepaid_upgrade: bool, upgrade_separate: bool, auction_acquired: bool, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}, convert_stars?: int, upgrade_msg_id?: int, upgrade_stars?: int, from_id?: array|int|string, peer?: array|int|string, saved_id?: int, prepaid_upgrade_hash?: string, gift_msg_id?: int, to_id?: array|int|string, gift_num?: int}|array{_: 'messageActionStarGiftUnique', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, upgrade: bool, transferred: bool, saved: bool, refunded: bool, prepaid_upgrade: bool, assigned: bool, from_offer: bool, craft: bool, name_hidden: bool, can_export_at?: int, transfer_stars?: int, from_id?: array|int|string, peer?: array|int|string, saved_id?: int, resale_amount?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, can_transfer_at?: int, can_resell_at?: int, drop_original_details_stars?: int, can_craft_at?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}|array{_: 'messageActionPaidMessagesRefunded', count: int, stars: int}|array{_: 'messageActionPaidMessagesPrice', broadcast_messages_allowed: bool, stars: int}|array{_: 'messageActionConferenceCall', missed: bool, active: bool, video: bool, call_id: int, duration?: int, other_participants?: list<array|int|string>}|array{_: 'messageActionTodoCompletions', completed: list<int>, incompleted: list<int>}|array{_: 'messageActionTodoAppendTasks', list: list<array{_: 'todoItem', title: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, id: int}>}|array{_: 'messageActionSuggestedPostApproval', rejected: bool, balance_too_low: bool, reject_comment?: string, schedule_date?: int, price?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}}|array{_: 'messageActionSuggestedPostSuccess', price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}}|array{_: 'messageActionSuggestedPostRefund', payer_initiated: bool}|array{_: 'messageActionGiftTon', currency: string, amount: int, crypto_currency: string, crypto_amount: int, transaction_id?: string}|array{_: 'messageActionSuggestBirthday', birthday: array{_: 'birthday', day: int, month: int, year?: int}}|array{_: 'messageActionStarGiftPurchaseOffer', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, accepted: bool, declined: bool, expires_at: int}|array{_: 'messageActionStarGiftPurchaseOfferDeclined', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, expired: bool}|array{_: 'messageActionNewCreatorPending', new_creator_id: int}|array{_: 'messageActionChangeCreator', new_creator_id: int}|array{_: 'messageActionNoForwardsToggle', prev_value: bool, new_value: bool}|array{_: 'messageActionNoForwardsRequest', prev_value: bool, new_value: bool, expired: bool}|array{_: 'messageActionPollAppendAnswer', answer: array{_: 'pollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, option: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, added_by?: array|int|string, date?: int}|array{_: 'inputPollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, media?: \danog\MadelineProto\EventHandler\Media|string|array}}|array{_: 'messageActionPollDeleteAnswer', answer: array{_: 'pollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, option: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, added_by?: array|int|string, date?: int}|array{_: 'inputPollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, media?: \danog\MadelineProto\EventHandler\Media|string|array}}|array{_: 'messageActionManagedBotCreated', bot_id: int}|array{_: 'messageActionChangeCommunity', community_id?: int}|array{_: 'messageActionChatJoinedViaCommunity', community_id: int}|array{_: 'messageActionGramTransfer', comment_encrypted: bool, amount: int, peer_address: string, transaction_id: string, comment?: string}|array{_: 'messageActionWalletTonConnectRequest', accepted: bool, declined: bool, session_id: int, expires: int, topic?: string, trace_id?: string, dapp_name?: string}|array{_: 'messageActionNoPaidMessagesTokenUsed'}, reactions?: array{_: 'messageReactions', min: bool, can_see_list: bool, reactions_as_tags: bool, results: list<array{_: 'reactionCount', chosen_order?: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}, count: int}>, recent_reactions?: list<array{_: 'messagePeerReaction', peer_id: array|int|string, big: bool, unread: bool, my: bool, date: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}}>, top_reactors?: list<array{_: 'messageReactor', top: bool, my: bool, anonymous: bool, peer_id?: array|int|string, count: int}>}, ttl_period?: int}>, chats: list<array|int|string>, users: list<array|int|string>}|array{_: 'messages.savedDialogsSlice', count: int, dialogs: list<array{_: 'savedDialog', peer: array|int|string, pinned: bool, top_message: int}|array{_: 'monoForumDialog', peer: array|int|string, unread_mark: bool, nopaid_messages_exception: bool, top_message: int, read_inbox_max_id: int, read_outbox_max_id: int, unread_count: int, unread_reactions_count: int, draft?: array{_: 'draftMessageEmpty', date?: int}|array{_: 'draftMessage', no_webpage: bool, invert_media: bool, reply_to?: array{_: 'inputReplyToMessage', reply_to_msg_id: int, top_msg_id?: int, reply_to_peer_id?: array|int|string, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, quote_offset?: int, monoforum_peer_id?: array|int|string, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, url: string, webpage_id: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, email: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, phone: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, name: string}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, user_id: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, date: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, type: array{_: 'inlineButtonTypeUrl', url: array}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: array, url: array, button_id: array}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: array, fwd_text?: array, url: array, bot?: array}|array{_: 'inlineButtonTypeWebView', url: array}|array{_: 'inlineButtonTypeCallback', requires_password: array, data: array}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: array, query: array, peer_types?: list<array>}|array{_: 'inlineButtonTypeUserProfile', user_id: array}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array}|array{_: 'inlineButtonTypeCopy', copy_text: array}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: array, bg_danger: array, bg_success: array, link: array}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}>}|array{_: 'inputReplyToStory', peer: array|int|string, story_id: int}|array{_: 'inputReplyToMonoForum', monoforum_peer_id: array|int|string}|array{_: 'inputReplyToEphemeralMessage', id: int}, message: string, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, media?: \danog\MadelineProto\EventHandler\Media|string|array, date: int, effect?: int, suggested_post?: array{_: 'suggestedPost', accepted: bool, rejected: bool, price?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, schedule_date?: int}, rich_message?: array{_: 'richMessage', rtl: bool, part: bool, blocks: list<array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, published_date: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, language: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: string}|array{_: 'pageBlockList', items: list<array{_: 'pageListItemText', text: array, checkbox: array, checked: array}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, collapsed: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array, credit: array}, spoiler: bool, photo_id: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array, credit: array}, autoplay: bool, loop: bool, spoiler: bool, video_id: int}|array{_: 'pageBlockCover', cover: array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array}|array{_: 'pageBlockSubtitle', text: array}|array{_: 'pageBlockAuthorDate', author: array, published_date: array}|array{_: 'pageBlockHeader', text: array}|array{_: 'pageBlockSubheader', text: array}|array{_: 'pageBlockParagraph', text: array}|array{_: 'pageBlockPreformatted', text: array, language: array}|array{_: 'pageBlockFooter', text: array}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: array}|array{_: 'pageBlockList', items: list<array>}|array{_: 'pageBlockBlockquote', text: array, caption: array, collapsed: array}|array{_: 'pageBlockPullquote', text: array, caption: array}|array{_: 'pageBlockPhoto', caption: array, spoiler: array, photo_id: array, url?: array, webpage_id?: array}|array{_: 'pageBlockVideo', caption: array, autoplay: array, loop: array, spoiler: array, video_id: array}|array{_: 'pageBlockEmbed', caption: array, full_width: array, allow_scrolling: array, url?: array, html?: array, poster_photo_id?: array, w?: array, h?: array}|array{_: 'pageBlockChannel', channel: array}|array{_: 'pageBlockAudio', caption: array, audio_id: array}|array{_: 'pageBlockKicker', text: array}|array{_: 'pageBlockTable', title: array, bordered: array, striped: array, compact: array, rows: list<array>}|array{_: 'pageBlockOrderedList', reversed: array, items: list<array>, start?: array, type?: array}|array{_: 'pageBlockRelatedArticles', title: array, articles: list<array>}|array{_: 'pageBlockMap', caption: array, geo: array, zoom: array, w: array, h: array}|array{_: 'pageBlockHeading1', text: array}|array{_: 'pageBlockHeading2', text: array}|array{_: 'pageBlockHeading3', text: array}|array{_: 'pageBlockHeading4', text: array}|array{_: 'pageBlockHeading5', text: array}|array{_: 'pageBlockHeading6', text: array}|array{_: 'pageBlockMath', source: array}|array{_: 'pageBlockThinking', text: array}|array{_: 'inputPageBlockMap', caption: array, geo: array, zoom: array, w: array, h: array}|array{_: 'pageBlockButtonRow', align_left: array, align_center: array, align_right: array, buttons: list<array>}|array{_: 'pageBlockDocument', caption: array, document_id: array}}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array, credit: array}, full_width: bool, allow_scrolling: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array, credit: array}, audio_id: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, bordered: bool, striped: bool, compact: bool, rows: list<array{_: 'pageTableRow', cells: list<array>}>}|array{_: 'pageBlockOrderedList', reversed: bool, items: list<array{_: 'pageListOrderedItemText', text: array, checkbox: array, checked: array, num?: array, value?: array, type?: array}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, articles: list<array{_: 'pageRelatedArticle', url: array, webpage_id: array, title?: array, description?: array, photo_id?: array, author?: array, published_date?: array}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockMath', source: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: array, long: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockButtonRow', align_left: bool, align_center: bool, align_right: bool, buttons: list<array{_: 'pageButton', text: array, type: array, style?: array}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array, credit: array}, document_id: int}>, photos: list<array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}}}>, messages: list<array{_: 'messageEmpty', id: int, peer_id?: array|int|string}|array{_: 'message', peer_id: array|int|string, out: bool, mentioned: bool, media_unread: bool, silent: bool, post: bool, from_scheduled: bool, legacy: bool, edit_hide: bool, pinned: bool, noforwards: bool, invert_media: bool, offline: bool, video_processing_pending: bool, paid_suggested_post_stars: bool, paid_suggested_post_ton: bool, id: int, from_id?: array|int|string, from_boosts_applied?: int, from_rank?: string, saved_peer_id?: array|int|string, fwd_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, via_bot_id?: int, via_business_bot_id?: int, guestchat_via_from?: array|int|string, reply_to?: array{_: 'messageReplyHeader', reply_to_scheduled: bool, forum_topic: bool, quote: bool, reply_to_ephemeral: bool, reply_to_msg_id?: int, reply_to_peer_id?: array|int|string, reply_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, reply_media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_to_top_id?: int, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, quote_offset?: int, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, url: string, webpage_id: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, email: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, phone: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, name: string}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, user_id: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, date: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, type: array{_: 'inlineButtonTypeUrl', url: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url: string, button_id: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: bool, fwd_text?: string, url: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url: string}|array{_: 'inlineButtonTypeCallback', requires_password: bool, data: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: bool, query: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: bool, bg_danger: bool, bg_success: bool, link: bool}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}>}|array{_: 'messageReplyStoryHeader', peer: array|int|string, story_id: int}, date: int, message: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_markup?: array, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, views?: int, forwards?: int, replies?: array{_: 'messageReplies', comments: bool, replies: int, replies_pts: int, recent_repliers?: list<array|int|string>, channel_id?: int, max_id?: int, read_max_id?: int}, edit_date?: int, post_author?: string, grouped_id?: int, reactions?: array{_: 'messageReactions', min: bool, can_see_list: bool, reactions_as_tags: bool, results: list<array{_: 'reactionCount', chosen_order?: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}, count: int}>, recent_reactions?: list<array{_: 'messagePeerReaction', peer_id: array|int|string, big: bool, unread: bool, my: bool, date: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}}>, top_reactors?: list<array{_: 'messageReactor', top: bool, my: bool, anonymous: bool, peer_id?: array|int|string, count: int}>}, restriction_reason?: list<array{_: 'restrictionReason', platform: string, reason: string, text: string}>, ttl_period?: int, quick_reply_shortcut_id?: int, effect?: int, factcheck?: array{_: 'factCheck', need_check: bool, country?: string, text?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}, hash: list<int|string>}, report_delivery_until_date?: int, paid_message_stars?: int, suggested_post?: array{_: 'suggestedPost', accepted: bool, rejected: bool, price?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, schedule_date?: int}, schedule_repeat_period?: int, summary_from_language?: string, rich_message?: array{_: 'richMessage', rtl: bool, part: bool, blocks: list<array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, published_date: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, language: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: string}|array{_: 'pageBlockList', items: list<array{_: 'pageListItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, checkbox: bool, checked: bool}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, collapsed: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, spoiler: bool, photo_id: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, autoplay: bool, loop: bool, spoiler: bool, video_id: int}|array{_: 'pageBlockCover', cover: array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, published_date: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, language: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: string}|array{_: 'pageBlockList', items: list<array{_: 'pageListItemText', text: array, checkbox: array, checked: array}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, collapsed: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array, credit: array}, spoiler: bool, photo_id: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array, credit: array}, autoplay: bool, loop: bool, spoiler: bool, video_id: int}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array, credit: array}, full_width: bool, allow_scrolling: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array, credit: array}, audio_id: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, bordered: bool, striped: bool, compact: bool, rows: list<array{_: 'pageTableRow', cells: list<array>}>}|array{_: 'pageBlockOrderedList', reversed: bool, items: list<array{_: 'pageListOrderedItemText', text: array, checkbox: array, checked: array, num?: array, value?: array, type?: array}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, articles: list<array{_: 'pageRelatedArticle', url: array, webpage_id: array, title?: array, description?: array, photo_id?: array, author?: array, published_date?: array}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockMath', source: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: array, long: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockButtonRow', align_left: bool, align_center: bool, align_right: bool, buttons: list<array{_: 'pageButton', text: array, type: array, style?: array}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array, credit: array}, document_id: int}}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, full_width: bool, allow_scrolling: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, audio_id: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, bordered: bool, striped: bool, compact: bool, rows: list<array{_: 'pageTableRow', cells: list<array{_: 'pageTableCell', header: array, align_center: array, align_right: array, valign_middle: array, valign_bottom: array, text?: array, colspan?: array, rowspan?: array}>}>}|array{_: 'pageBlockOrderedList', reversed: bool, items: list<array{_: 'pageListOrderedItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, checkbox: bool, checked: bool, num?: string, value?: int, type?: string}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, articles: list<array{_: 'pageRelatedArticle', url: string, webpage_id: int, title?: string, description?: string, photo_id?: int, author?: string, published_date?: int}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash: int, accuracy_radius?: int}, zoom: int, w: int, h: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockMath', source: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, geo: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: float, long: float, accuracy_radius?: int}, zoom: int, w: int, h: int}|array{_: 'pageBlockButtonRow', align_left: bool, align_center: bool, align_right: bool, buttons: list<array{_: 'pageButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, type: array{_: 'inlineButtonTypeUrl', url: array}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: array, url: array, button_id: array}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: array, fwd_text?: array, url: array, bot?: array}|array{_: 'inlineButtonTypeWebView', url: array}|array{_: 'inlineButtonTypeCallback', requires_password: array, data: array}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: array, query: array, peer_types?: list<array>}|array{_: 'inlineButtonTypeUserProfile', user_id: array}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array}|array{_: 'inlineButtonTypeCopy', copy_text: array}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: array, bg_danger: array, bg_success: array, link: array}}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, document_id: int}>, photos: list<array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}}|array{_: 'messageService', peer_id: array|int|string, out: bool, mentioned: bool, media_unread: bool, reactions_are_possible: bool, silent: bool, post: bool, legacy: bool, id: int, from_id?: array|int|string, saved_peer_id?: array|int|string, reply_to?: array{_: 'messageReplyHeader', reply_to_scheduled: bool, forum_topic: bool, quote: bool, reply_to_ephemeral: bool, reply_to_msg_id?: int, reply_to_peer_id?: array|int|string, reply_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, reply_media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_to_top_id?: int, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, quote_offset?: int, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, url: string, webpage_id: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, email: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, phone: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, name: string}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, user_id: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, date: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, type: array{_: 'inlineButtonTypeUrl', url: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url: string, button_id: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: bool, fwd_text?: string, url: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url: string}|array{_: 'inlineButtonTypeCallback', requires_password: bool, data: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: bool, query: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: bool, bg_danger: bool, bg_success: bool, link: bool}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}>}|array{_: 'messageReplyStoryHeader', peer: array|int|string, story_id: int}, date: int, action: array{_: 'messageActionEmpty'}|array{_: 'messageActionChatCreate', title: string, users: list<int>}|array{_: 'messageActionChatEditTitle', title: string}|array{_: 'messageActionChatEditPhoto', photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'messageActionChatDeletePhoto'}|array{_: 'messageActionChatAddUser', users: list<int>}|array{_: 'messageActionChatDeleteUser', user_id: int}|array{_: 'messageActionChatJoinedByLink', inviter_id: int}|array{_: 'messageActionChannelCreate', title: string}|array{_: 'messageActionChatMigrateTo', channel_id: int}|array{_: 'messageActionChannelMigrateFrom', title: string, chat_id: array|int|string}|array{_: 'messageActionPinMessage'}|array{_: 'messageActionHistoryClear'}|array{_: 'messageActionGameScore', game_id: int, score: int}|array{_: 'messageActionPaymentSentMe', charge: array{_: 'paymentCharge', id: string, provider_charge_id: string}, recurring_init: bool, recurring_used: bool, currency: string, total_amount: int, payload: string, info?: array{_: 'paymentRequestedInfo', name?: string, phone?: string, email?: string, shipping_address?: array{_: 'postAddress', street_line1: string, street_line2: string, city: string, state: string, country_iso2: string, post_code: string}}, shipping_option_id?: string, subscription_until_date?: int}|array{_: 'messageActionPaymentSent', recurring_init: bool, recurring_used: bool, currency: string, total_amount: int, invoice_slug?: string, subscription_until_date?: int}|array{_: 'messageActionPhoneCall', video: bool, call_id: int, reason?: array{_: 'phoneCallDiscardReasonMissed'}|array{_: 'phoneCallDiscardReasonDisconnect'}|array{_: 'phoneCallDiscardReasonHangup'}|array{_: 'phoneCallDiscardReasonBusy'}|array{_: 'phoneCallDiscardReasonMigrateConferenceCall', slug: string}, duration?: int}|array{_: 'messageActionScreenshotTaken'}|array{_: 'messageActionCustomAction', message: string}|array{_: 'messageActionBotAllowed', attach_menu: bool, from_request: bool, domain?: string, app?: array{_: 'botAppNotModified'}|array{_: 'botApp', id: int, access_hash: int, short_name: string, title: string, description: string, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, hash: list<int|string>}}|array{_: 'messageActionSecureValuesSentMe', credentials: array{_: 'secureCredentialsEncrypted', data: string, hash: string, secret: string}, values: list<array{_: 'secureValue', type: array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}, data?: array{_: 'secureData', data: string, data_hash: string, secret: string}, front_side?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, reverse_side?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, selfie?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, translation?: list<array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}>, files?: list<array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}>, plain_data?: array{_: 'securePlainPhone', phone: string}|array{_: 'securePlainEmail', email: string}, hash: string}>}|array{_: 'messageActionSecureValuesSent', types: list<array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}>}|array{_: 'messageActionContactSignUp'}|array{_: 'messageActionGeoProximityReached', from_id: array|int|string, to_id: array|int|string, distance: int}|array{_: 'messageActionGroupCall', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, duration?: int}|array{_: 'messageActionInviteToGroupCall', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, users: list<int>}|array{_: 'messageActionSetMessagesTTL', period: int, auto_setting_from?: int}|array{_: 'messageActionGroupCallScheduled', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, schedule_date: int}|array{_: 'messageActionSetChatTheme', theme: array{_: 'chatTheme', emoticon: string}|array{_: 'chatThemeUniqueGift', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: array, edge_color: array, text_color: array}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array, crafted: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array, name: array, backdrop_id: array, center_color: array, edge_color: array, pattern_color: array, text_color: array}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array, sender_id?: array, date: array, message?: array}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: array, nanos: array}|array{_: 'starsTonAmount', amount: array}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: array, background_emoji_id?: array}|array{_: 'peerColorCollectible', collectible_id: array, gift_emoji_id: array, background_emoji_id: array, accent_color: array, colors: list<array>, dark_accent_color?: array, dark_colors?: list<array>}|array{_: 'inputPeerColorCollectible', collectible_id: array}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, theme_settings: list<array{_: 'themeSettings', base_theme: array{_: 'baseThemeClassic'}|array{_: 'baseThemeDay'}|array{_: 'baseThemeNight'}|array{_: 'baseThemeTinted'}|array{_: 'baseThemeArctic'}, message_colors_animated: bool, accent_color: int, outbox_accent_color?: int, message_colors?: list<int>, wallpaper?: array{_: 'wallPaper', id: array, creator: array, default: array, pattern: array, dark: array, access_hash: array, slug: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, settings?: array}|array{_: 'wallPaperNoFile', id: array, default: array, dark: array, settings?: array}}>}}|array{_: 'messageActionChatJoinedByRequest'}|array{_: 'messageActionWebViewDataSentMe', text: string, data: string}|array{_: 'messageActionWebViewDataSent', text: string}|array{_: 'messageActionGiftPremium', currency: string, amount: int, days: int, crypto_currency?: string, crypto_amount?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}|array{_: 'messageActionTopicCreate', title_missing: bool, title: string, icon_color: int, icon_emoji_id?: int}|array{_: 'messageActionTopicEdit', title?: string, icon_emoji_id?: int, closed?: bool, hidden?: bool}|array{_: 'messageActionSuggestProfilePhoto', photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'messageActionRequestedPeer', button_id: int, peers: list<array|int|string>}|array{_: 'messageActionSetChatWallPaper', wallpaper: array{_: 'wallPaper', id: int, creator: bool, default: bool, pattern: bool, dark: bool, access_hash: int, slug: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, settings?: array{_: 'wallPaperSettings', blur: bool, motion: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string}}|array{_: 'wallPaperNoFile', id: int, default: bool, dark: bool, settings?: array{_: 'wallPaperSettings', blur: bool, motion: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string}}, same: bool, for_both: bool}|array{_: 'messageActionGiftCode', via_giveaway: bool, unclaimed: bool, boost_peer?: array|int|string, days: int, slug: string, currency?: string, amount?: int, crypto_currency?: string, crypto_amount?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}|array{_: 'messageActionGiveawayLaunch', stars?: int}|array{_: 'messageActionGiveawayResults', stars: bool, winners_count: int, unclaimed_count: int}|array{_: 'messageActionBoostApply', boosts: int}|array{_: 'messageActionRequestedPeerSentMe', button_id: int, peers: list<array{_: 'requestedPeerUser', user_id: int, first_name?: string, last_name?: string, username?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'requestedPeerChat', chat_id: array|int|string, title?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'requestedPeerChannel', channel_id: int, title?: string, username?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}>}|array{_: 'messageActionPaymentRefunded', peer: array|int|string, charge: array{_: 'paymentCharge', id: string, provider_charge_id: string}, currency: string, total_amount: int, payload?: string}|array{_: 'messageActionGiftStars', currency: string, amount: int, stars: int, crypto_currency?: string, crypto_amount?: int, transaction_id?: string}|array{_: 'messageActionPrizeStars', boost_peer: array|int|string, unclaimed: bool, stars: int, transaction_id: string, giveaway_msg_id: int}|array{_: 'messageActionStarGift', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, name_hidden: bool, saved: bool, converted: bool, upgraded: bool, refunded: bool, can_upgrade: bool, prepaid_upgrade: bool, upgrade_separate: bool, auction_acquired: bool, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}, convert_stars?: int, upgrade_msg_id?: int, upgrade_stars?: int, from_id?: array|int|string, peer?: array|int|string, saved_id?: int, prepaid_upgrade_hash?: string, gift_msg_id?: int, to_id?: array|int|string, gift_num?: int}|array{_: 'messageActionStarGiftUnique', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, upgrade: bool, transferred: bool, saved: bool, refunded: bool, prepaid_upgrade: bool, assigned: bool, from_offer: bool, craft: bool, name_hidden: bool, can_export_at?: int, transfer_stars?: int, from_id?: array|int|string, peer?: array|int|string, saved_id?: int, resale_amount?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, can_transfer_at?: int, can_resell_at?: int, drop_original_details_stars?: int, can_craft_at?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}|array{_: 'messageActionPaidMessagesRefunded', count: int, stars: int}|array{_: 'messageActionPaidMessagesPrice', broadcast_messages_allowed: bool, stars: int}|array{_: 'messageActionConferenceCall', missed: bool, active: bool, video: bool, call_id: int, duration?: int, other_participants?: list<array|int|string>}|array{_: 'messageActionTodoCompletions', completed: list<int>, incompleted: list<int>}|array{_: 'messageActionTodoAppendTasks', list: list<array{_: 'todoItem', title: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, id: int}>}|array{_: 'messageActionSuggestedPostApproval', rejected: bool, balance_too_low: bool, reject_comment?: string, schedule_date?: int, price?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}}|array{_: 'messageActionSuggestedPostSuccess', price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}}|array{_: 'messageActionSuggestedPostRefund', payer_initiated: bool}|array{_: 'messageActionGiftTon', currency: string, amount: int, crypto_currency: string, crypto_amount: int, transaction_id?: string}|array{_: 'messageActionSuggestBirthday', birthday: array{_: 'birthday', day: int, month: int, year?: int}}|array{_: 'messageActionStarGiftPurchaseOffer', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, accepted: bool, declined: bool, expires_at: int}|array{_: 'messageActionStarGiftPurchaseOfferDeclined', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, expired: bool}|array{_: 'messageActionNewCreatorPending', new_creator_id: int}|array{_: 'messageActionChangeCreator', new_creator_id: int}|array{_: 'messageActionNoForwardsToggle', prev_value: bool, new_value: bool}|array{_: 'messageActionNoForwardsRequest', prev_value: bool, new_value: bool, expired: bool}|array{_: 'messageActionPollAppendAnswer', answer: array{_: 'pollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, option: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, added_by?: array|int|string, date?: int}|array{_: 'inputPollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, media?: \danog\MadelineProto\EventHandler\Media|string|array}}|array{_: 'messageActionPollDeleteAnswer', answer: array{_: 'pollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, option: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, added_by?: array|int|string, date?: int}|array{_: 'inputPollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, media?: \danog\MadelineProto\EventHandler\Media|string|array}}|array{_: 'messageActionManagedBotCreated', bot_id: int}|array{_: 'messageActionChangeCommunity', community_id?: int}|array{_: 'messageActionChatJoinedViaCommunity', community_id: int}|array{_: 'messageActionGramTransfer', comment_encrypted: bool, amount: int, peer_address: string, transaction_id: string, comment?: string}|array{_: 'messageActionWalletTonConnectRequest', accepted: bool, declined: bool, session_id: int, expires: int, topic?: string, trace_id?: string, dapp_name?: string}|array{_: 'messageActionNoPaidMessagesTokenUsed'}, reactions?: array{_: 'messageReactions', min: bool, can_see_list: bool, reactions_as_tags: bool, results: list<array{_: 'reactionCount', chosen_order?: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}, count: int}>, recent_reactions?: list<array{_: 'messagePeerReaction', peer_id: array|int|string, big: bool, unread: bool, my: bool, date: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}}>, top_reactors?: list<array{_: 'messageReactor', top: bool, my: bool, anonymous: bool, peer_id?: array|int|string, count: int}>}, ttl_period?: int}>, chats: list<array|int|string>, users: list<array|int|string>}|array{_: 'messages.savedDialogsNotModified', count: int} @see https://docs.madelineproto.xyz/API_docs/types/messages.SavedDialogs.html
     * @psalm-impure
     */
    public function getPinnedSavedDialogs(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Pin or unpin a [saved message dialog Â»](https://core.telegram.org/api/saved-messages).
     *
     * @param array{_: 'inputDialogPeer', peer?: array|int|string}|array{_: 'inputDialogPeerFolder', folder_id?: int}|array{_: 'inputDialogPeerCommunity', community?: array|int|string} $peer The dialog to pin @see https://docs.madelineproto.xyz/API_docs/types/InputDialogPeer.html
     * @param bool $pinned Whether to pin or unpin the dialog
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function toggleSavedDialogPin(array $peer, bool|null $pinned = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Reorder pinned [saved message dialogs Â»](https://core.telegram.org/api/saved-messages).
     *
     * @param bool $force If set, dialogs pinned server-side but not present in the `order` field will be unpinned.
     * @param list<array{_: 'inputDialogPeer', peer?: array|int|string}|array{_: 'inputDialogPeerFolder', folder_id?: int}|array{_: 'inputDialogPeerCommunity', community?: array|int|string}>|array<never, never> $order Array of New dialog order @see https://docs.madelineproto.xyz/API_docs/types/InputDialogPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function reorderPinnedSavedDialogs(bool|null $force = null, array $order = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Fetch the full list of [saved message tags](https://core.telegram.org/api/saved-messages#tags) created by the user.
     *
     * @param array|int|string $peer If set, returns tags only used in the specified [saved message dialog](https://core.telegram.org/api/saved-messages#saved-message-dialogs). @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param list<int|string>|array<never, never> $hash Array of [Hash used for caching, for more info click here](https://core.telegram.org/api/offsets#hash-generation). @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.savedReactionTagsNotModified'}|array{_: 'messages.savedReactionTags', tags: list<array{_: 'savedReactionTag', reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}, title?: string, count: int}>, hash: list<int|string>} @see https://docs.madelineproto.xyz/API_docs/types/messages.SavedReactionTags.html
     * @psalm-impure
     */
    public function getSavedReactionTags(array|int|string|null $peer = null, array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Update the [description of a saved message tag Â»](https://core.telegram.org/api/saved-messages#tags).
     *
     * @param array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon?: string}|array{_: 'reactionCustomEmoji', document_id?: int}|array{_: 'reactionPaid'} $reaction [Reaction](https://core.telegram.org/api/reactions) associated to the tag @see https://docs.madelineproto.xyz/API_docs/types/Reaction.html
     * @param string $title Tag description, max 12 UTF-8 characters; to remove the description call the method without setting this flag.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function updateSavedReactionTag(array|null $reaction = null, string|null $title = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Fetch a default recommended list of [saved message tag reactions](https://core.telegram.org/api/saved-messages#tags).
     *
     * @param list<int|string>|array<never, never> $hash Array of [Hash used for caching, for more info click here](https://core.telegram.org/api/offsets#hash-generation). @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.reactionsNotModified'}|array{_: 'messages.reactions', hash: list<int|string>, reactions: list<array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}>} @see https://docs.madelineproto.xyz/API_docs/types/messages.Reactions.html
     * @psalm-impure
     */
    public function getDefaultTagReactions(array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get the exact read date of one of our messages, sent to a private chat with another user.
     * 
     * Can be only done for private outgoing messages not older than [appConfig.pm\_read\_date\_expire\_period Â»](https://core.telegram.org/api/config#pm-read-date-expire-period).
     * 
     * If the `peer`'s [userFull](https://docs.madelineproto.xyz/API_docs/constructors/userFull.html).`read_dates_private` flag is set, we will not be able to fetch the exact read date of messages we send to them, and a `USER_PRIVACY_RESTRICTED` RPC error will be emitted.  
     * The exact read date of messages might still be unavailable for other reasons, see [here Â»](https://docs.madelineproto.xyz/API_docs/constructors/globalPrivacySettings.html) for more info.  
     * To set [userFull](https://docs.madelineproto.xyz/API_docs/constructors/userFull.html).`read_dates_private` for ourselves invoke [account.setGlobalPrivacySettings](https://docs.madelineproto.xyz/API_docs/methods/account.setGlobalPrivacySettings.html), setting the `settings.hide_read_marks` flag.
     *
     * @param array|int|string $peer The user to whom we sent the message. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $msg_id The message ID.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'outboxReadDate', date: int} @see https://docs.madelineproto.xyz/API_docs/types/OutboxReadDate.html
     * @psalm-impure
     */
    public function getOutboxReadDate(array|int|string|null $peer = null, int|null $msg_id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Fetch basic info about all existing [quick reply shortcuts](https://core.telegram.org/api/business#quick-reply-shortcuts).
     *
     * @param list<int|string>|array<never, never> $hash Array of Hash for pagination, generated as specified [here Â»](https://core.telegram.org/api/business#quick-reply-shortcuts) (not the usual algorithm used for hash generation.) @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.quickReplies', quick_replies: list<array{_: 'quickReply', shortcut_id: int, shortcut: string, top_message: int, count: int}>, messages: list<array{_: 'messageEmpty', id: int, peer_id?: array|int|string}|array{_: 'message', peer_id: array|int|string, out: bool, mentioned: bool, media_unread: bool, silent: bool, post: bool, from_scheduled: bool, legacy: bool, edit_hide: bool, pinned: bool, noforwards: bool, invert_media: bool, offline: bool, video_processing_pending: bool, paid_suggested_post_stars: bool, paid_suggested_post_ton: bool, id: int, from_id?: array|int|string, from_boosts_applied?: int, from_rank?: string, saved_peer_id?: array|int|string, fwd_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, via_bot_id?: int, via_business_bot_id?: int, guestchat_via_from?: array|int|string, reply_to?: array{_: 'messageReplyHeader', reply_to_scheduled: bool, forum_topic: bool, quote: bool, reply_to_ephemeral: bool, reply_to_msg_id?: int, reply_to_peer_id?: array|int|string, reply_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, reply_media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_to_top_id?: int, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, quote_offset?: int, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, url: string, webpage_id: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, email: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, phone: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, name: string}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, user_id: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, date: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, type: array{_: 'inlineButtonTypeUrl', url: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url: string, button_id: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: bool, fwd_text?: string, url: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url: string}|array{_: 'inlineButtonTypeCallback', requires_password: bool, data: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: bool, query: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: bool, bg_danger: bool, bg_success: bool, link: bool}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}>}|array{_: 'messageReplyStoryHeader', peer: array|int|string, story_id: int}, date: int, message: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_markup?: array, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, views?: int, forwards?: int, replies?: array{_: 'messageReplies', comments: bool, replies: int, replies_pts: int, recent_repliers?: list<array|int|string>, channel_id?: int, max_id?: int, read_max_id?: int}, edit_date?: int, post_author?: string, grouped_id?: int, reactions?: array{_: 'messageReactions', min: bool, can_see_list: bool, reactions_as_tags: bool, results: list<array{_: 'reactionCount', chosen_order?: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}, count: int}>, recent_reactions?: list<array{_: 'messagePeerReaction', peer_id: array|int|string, big: bool, unread: bool, my: bool, date: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}}>, top_reactors?: list<array{_: 'messageReactor', top: bool, my: bool, anonymous: bool, peer_id?: array|int|string, count: int}>}, restriction_reason?: list<array{_: 'restrictionReason', platform: string, reason: string, text: string}>, ttl_period?: int, quick_reply_shortcut_id?: int, effect?: int, factcheck?: array{_: 'factCheck', need_check: bool, country?: string, text?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}, hash: list<int|string>}, report_delivery_until_date?: int, paid_message_stars?: int, suggested_post?: array{_: 'suggestedPost', accepted: bool, rejected: bool, price?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, schedule_date?: int}, schedule_repeat_period?: int, summary_from_language?: string, rich_message?: array{_: 'richMessage', rtl: bool, part: bool, blocks: list<array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, published_date: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, language: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: string}|array{_: 'pageBlockList', items: list<array{_: 'pageListItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, checkbox: bool, checked: bool}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, collapsed: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, spoiler: bool, photo_id: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, autoplay: bool, loop: bool, spoiler: bool, video_id: int}|array{_: 'pageBlockCover', cover: array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, published_date: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, language: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: string}|array{_: 'pageBlockList', items: list<array{_: 'pageListItemText', text: array, checkbox: array, checked: array}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, collapsed: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array, credit: array}, spoiler: bool, photo_id: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array, credit: array}, autoplay: bool, loop: bool, spoiler: bool, video_id: int}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array, credit: array}, full_width: bool, allow_scrolling: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array, credit: array}, audio_id: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, bordered: bool, striped: bool, compact: bool, rows: list<array{_: 'pageTableRow', cells: list<array>}>}|array{_: 'pageBlockOrderedList', reversed: bool, items: list<array{_: 'pageListOrderedItemText', text: array, checkbox: array, checked: array, num?: array, value?: array, type?: array}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, articles: list<array{_: 'pageRelatedArticle', url: array, webpage_id: array, title?: array, description?: array, photo_id?: array, author?: array, published_date?: array}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockMath', source: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: array, long: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockButtonRow', align_left: bool, align_center: bool, align_right: bool, buttons: list<array{_: 'pageButton', text: array, type: array, style?: array}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array, credit: array}, document_id: int}}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, full_width: bool, allow_scrolling: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, audio_id: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, bordered: bool, striped: bool, compact: bool, rows: list<array{_: 'pageTableRow', cells: list<array{_: 'pageTableCell', header: array, align_center: array, align_right: array, valign_middle: array, valign_bottom: array, text?: array, colspan?: array, rowspan?: array}>}>}|array{_: 'pageBlockOrderedList', reversed: bool, items: list<array{_: 'pageListOrderedItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, checkbox: bool, checked: bool, num?: string, value?: int, type?: string}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, articles: list<array{_: 'pageRelatedArticle', url: string, webpage_id: int, title?: string, description?: string, photo_id?: int, author?: string, published_date?: int}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash: int, accuracy_radius?: int}, zoom: int, w: int, h: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockMath', source: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, geo: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: float, long: float, accuracy_radius?: int}, zoom: int, w: int, h: int}|array{_: 'pageBlockButtonRow', align_left: bool, align_center: bool, align_right: bool, buttons: list<array{_: 'pageButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, type: array{_: 'inlineButtonTypeUrl', url: array}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: array, url: array, button_id: array}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: array, fwd_text?: array, url: array, bot?: array}|array{_: 'inlineButtonTypeWebView', url: array}|array{_: 'inlineButtonTypeCallback', requires_password: array, data: array}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: array, query: array, peer_types?: list<array>}|array{_: 'inlineButtonTypeUserProfile', user_id: array}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array}|array{_: 'inlineButtonTypeCopy', copy_text: array}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: array, bg_danger: array, bg_success: array, link: array}}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, document_id: int}>, photos: list<array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}}|array{_: 'messageService', peer_id: array|int|string, out: bool, mentioned: bool, media_unread: bool, reactions_are_possible: bool, silent: bool, post: bool, legacy: bool, id: int, from_id?: array|int|string, saved_peer_id?: array|int|string, reply_to?: array{_: 'messageReplyHeader', reply_to_scheduled: bool, forum_topic: bool, quote: bool, reply_to_ephemeral: bool, reply_to_msg_id?: int, reply_to_peer_id?: array|int|string, reply_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, reply_media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_to_top_id?: int, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, quote_offset?: int, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, url: string, webpage_id: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, email: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, phone: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, name: string}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, user_id: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, date: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, type: array{_: 'inlineButtonTypeUrl', url: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url: string, button_id: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: bool, fwd_text?: string, url: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url: string}|array{_: 'inlineButtonTypeCallback', requires_password: bool, data: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: bool, query: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: bool, bg_danger: bool, bg_success: bool, link: bool}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}>}|array{_: 'messageReplyStoryHeader', peer: array|int|string, story_id: int}, date: int, action: array{_: 'messageActionEmpty'}|array{_: 'messageActionChatCreate', title: string, users: list<int>}|array{_: 'messageActionChatEditTitle', title: string}|array{_: 'messageActionChatEditPhoto', photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'messageActionChatDeletePhoto'}|array{_: 'messageActionChatAddUser', users: list<int>}|array{_: 'messageActionChatDeleteUser', user_id: int}|array{_: 'messageActionChatJoinedByLink', inviter_id: int}|array{_: 'messageActionChannelCreate', title: string}|array{_: 'messageActionChatMigrateTo', channel_id: int}|array{_: 'messageActionChannelMigrateFrom', title: string, chat_id: array|int|string}|array{_: 'messageActionPinMessage'}|array{_: 'messageActionHistoryClear'}|array{_: 'messageActionGameScore', game_id: int, score: int}|array{_: 'messageActionPaymentSentMe', charge: array{_: 'paymentCharge', id: string, provider_charge_id: string}, recurring_init: bool, recurring_used: bool, currency: string, total_amount: int, payload: string, info?: array{_: 'paymentRequestedInfo', name?: string, phone?: string, email?: string, shipping_address?: array{_: 'postAddress', street_line1: string, street_line2: string, city: string, state: string, country_iso2: string, post_code: string}}, shipping_option_id?: string, subscription_until_date?: int}|array{_: 'messageActionPaymentSent', recurring_init: bool, recurring_used: bool, currency: string, total_amount: int, invoice_slug?: string, subscription_until_date?: int}|array{_: 'messageActionPhoneCall', video: bool, call_id: int, reason?: array{_: 'phoneCallDiscardReasonMissed'}|array{_: 'phoneCallDiscardReasonDisconnect'}|array{_: 'phoneCallDiscardReasonHangup'}|array{_: 'phoneCallDiscardReasonBusy'}|array{_: 'phoneCallDiscardReasonMigrateConferenceCall', slug: string}, duration?: int}|array{_: 'messageActionScreenshotTaken'}|array{_: 'messageActionCustomAction', message: string}|array{_: 'messageActionBotAllowed', attach_menu: bool, from_request: bool, domain?: string, app?: array{_: 'botAppNotModified'}|array{_: 'botApp', id: int, access_hash: int, short_name: string, title: string, description: string, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, hash: list<int|string>}}|array{_: 'messageActionSecureValuesSentMe', credentials: array{_: 'secureCredentialsEncrypted', data: string, hash: string, secret: string}, values: list<array{_: 'secureValue', type: array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}, data?: array{_: 'secureData', data: string, data_hash: string, secret: string}, front_side?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, reverse_side?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, selfie?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, translation?: list<array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}>, files?: list<array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}>, plain_data?: array{_: 'securePlainPhone', phone: string}|array{_: 'securePlainEmail', email: string}, hash: string}>}|array{_: 'messageActionSecureValuesSent', types: list<array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}>}|array{_: 'messageActionContactSignUp'}|array{_: 'messageActionGeoProximityReached', from_id: array|int|string, to_id: array|int|string, distance: int}|array{_: 'messageActionGroupCall', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, duration?: int}|array{_: 'messageActionInviteToGroupCall', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, users: list<int>}|array{_: 'messageActionSetMessagesTTL', period: int, auto_setting_from?: int}|array{_: 'messageActionGroupCallScheduled', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, schedule_date: int}|array{_: 'messageActionSetChatTheme', theme: array{_: 'chatTheme', emoticon: string}|array{_: 'chatThemeUniqueGift', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: array, edge_color: array, text_color: array}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array, crafted: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array, name: array, backdrop_id: array, center_color: array, edge_color: array, pattern_color: array, text_color: array}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array, sender_id?: array, date: array, message?: array}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: array, nanos: array}|array{_: 'starsTonAmount', amount: array}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: array, background_emoji_id?: array}|array{_: 'peerColorCollectible', collectible_id: array, gift_emoji_id: array, background_emoji_id: array, accent_color: array, colors: list<array>, dark_accent_color?: array, dark_colors?: list<array>}|array{_: 'inputPeerColorCollectible', collectible_id: array}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, theme_settings: list<array{_: 'themeSettings', base_theme: array{_: 'baseThemeClassic'}|array{_: 'baseThemeDay'}|array{_: 'baseThemeNight'}|array{_: 'baseThemeTinted'}|array{_: 'baseThemeArctic'}, message_colors_animated: bool, accent_color: int, outbox_accent_color?: int, message_colors?: list<int>, wallpaper?: array{_: 'wallPaper', id: array, creator: array, default: array, pattern: array, dark: array, access_hash: array, slug: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, settings?: array}|array{_: 'wallPaperNoFile', id: array, default: array, dark: array, settings?: array}}>}}|array{_: 'messageActionChatJoinedByRequest'}|array{_: 'messageActionWebViewDataSentMe', text: string, data: string}|array{_: 'messageActionWebViewDataSent', text: string}|array{_: 'messageActionGiftPremium', currency: string, amount: int, days: int, crypto_currency?: string, crypto_amount?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}|array{_: 'messageActionTopicCreate', title_missing: bool, title: string, icon_color: int, icon_emoji_id?: int}|array{_: 'messageActionTopicEdit', title?: string, icon_emoji_id?: int, closed?: bool, hidden?: bool}|array{_: 'messageActionSuggestProfilePhoto', photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'messageActionRequestedPeer', button_id: int, peers: list<array|int|string>}|array{_: 'messageActionSetChatWallPaper', wallpaper: array{_: 'wallPaper', id: int, creator: bool, default: bool, pattern: bool, dark: bool, access_hash: int, slug: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, settings?: array{_: 'wallPaperSettings', blur: bool, motion: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string}}|array{_: 'wallPaperNoFile', id: int, default: bool, dark: bool, settings?: array{_: 'wallPaperSettings', blur: bool, motion: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string}}, same: bool, for_both: bool}|array{_: 'messageActionGiftCode', via_giveaway: bool, unclaimed: bool, boost_peer?: array|int|string, days: int, slug: string, currency?: string, amount?: int, crypto_currency?: string, crypto_amount?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}|array{_: 'messageActionGiveawayLaunch', stars?: int}|array{_: 'messageActionGiveawayResults', stars: bool, winners_count: int, unclaimed_count: int}|array{_: 'messageActionBoostApply', boosts: int}|array{_: 'messageActionRequestedPeerSentMe', button_id: int, peers: list<array{_: 'requestedPeerUser', user_id: int, first_name?: string, last_name?: string, username?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'requestedPeerChat', chat_id: array|int|string, title?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'requestedPeerChannel', channel_id: int, title?: string, username?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}>}|array{_: 'messageActionPaymentRefunded', peer: array|int|string, charge: array{_: 'paymentCharge', id: string, provider_charge_id: string}, currency: string, total_amount: int, payload?: string}|array{_: 'messageActionGiftStars', currency: string, amount: int, stars: int, crypto_currency?: string, crypto_amount?: int, transaction_id?: string}|array{_: 'messageActionPrizeStars', boost_peer: array|int|string, unclaimed: bool, stars: int, transaction_id: string, giveaway_msg_id: int}|array{_: 'messageActionStarGift', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, name_hidden: bool, saved: bool, converted: bool, upgraded: bool, refunded: bool, can_upgrade: bool, prepaid_upgrade: bool, upgrade_separate: bool, auction_acquired: bool, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}, convert_stars?: int, upgrade_msg_id?: int, upgrade_stars?: int, from_id?: array|int|string, peer?: array|int|string, saved_id?: int, prepaid_upgrade_hash?: string, gift_msg_id?: int, to_id?: array|int|string, gift_num?: int}|array{_: 'messageActionStarGiftUnique', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, upgrade: bool, transferred: bool, saved: bool, refunded: bool, prepaid_upgrade: bool, assigned: bool, from_offer: bool, craft: bool, name_hidden: bool, can_export_at?: int, transfer_stars?: int, from_id?: array|int|string, peer?: array|int|string, saved_id?: int, resale_amount?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, can_transfer_at?: int, can_resell_at?: int, drop_original_details_stars?: int, can_craft_at?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}|array{_: 'messageActionPaidMessagesRefunded', count: int, stars: int}|array{_: 'messageActionPaidMessagesPrice', broadcast_messages_allowed: bool, stars: int}|array{_: 'messageActionConferenceCall', missed: bool, active: bool, video: bool, call_id: int, duration?: int, other_participants?: list<array|int|string>}|array{_: 'messageActionTodoCompletions', completed: list<int>, incompleted: list<int>}|array{_: 'messageActionTodoAppendTasks', list: list<array{_: 'todoItem', title: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, id: int}>}|array{_: 'messageActionSuggestedPostApproval', rejected: bool, balance_too_low: bool, reject_comment?: string, schedule_date?: int, price?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}}|array{_: 'messageActionSuggestedPostSuccess', price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}}|array{_: 'messageActionSuggestedPostRefund', payer_initiated: bool}|array{_: 'messageActionGiftTon', currency: string, amount: int, crypto_currency: string, crypto_amount: int, transaction_id?: string}|array{_: 'messageActionSuggestBirthday', birthday: array{_: 'birthday', day: int, month: int, year?: int}}|array{_: 'messageActionStarGiftPurchaseOffer', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, accepted: bool, declined: bool, expires_at: int}|array{_: 'messageActionStarGiftPurchaseOfferDeclined', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, expired: bool}|array{_: 'messageActionNewCreatorPending', new_creator_id: int}|array{_: 'messageActionChangeCreator', new_creator_id: int}|array{_: 'messageActionNoForwardsToggle', prev_value: bool, new_value: bool}|array{_: 'messageActionNoForwardsRequest', prev_value: bool, new_value: bool, expired: bool}|array{_: 'messageActionPollAppendAnswer', answer: array{_: 'pollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, option: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, added_by?: array|int|string, date?: int}|array{_: 'inputPollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, media?: \danog\MadelineProto\EventHandler\Media|string|array}}|array{_: 'messageActionPollDeleteAnswer', answer: array{_: 'pollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, option: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, added_by?: array|int|string, date?: int}|array{_: 'inputPollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, media?: \danog\MadelineProto\EventHandler\Media|string|array}}|array{_: 'messageActionManagedBotCreated', bot_id: int}|array{_: 'messageActionChangeCommunity', community_id?: int}|array{_: 'messageActionChatJoinedViaCommunity', community_id: int}|array{_: 'messageActionGramTransfer', comment_encrypted: bool, amount: int, peer_address: string, transaction_id: string, comment?: string}|array{_: 'messageActionWalletTonConnectRequest', accepted: bool, declined: bool, session_id: int, expires: int, topic?: string, trace_id?: string, dapp_name?: string}|array{_: 'messageActionNoPaidMessagesTokenUsed'}, reactions?: array{_: 'messageReactions', min: bool, can_see_list: bool, reactions_as_tags: bool, results: list<array{_: 'reactionCount', chosen_order?: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}, count: int}>, recent_reactions?: list<array{_: 'messagePeerReaction', peer_id: array|int|string, big: bool, unread: bool, my: bool, date: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}}>, top_reactors?: list<array{_: 'messageReactor', top: bool, my: bool, anonymous: bool, peer_id?: array|int|string, count: int}>}, ttl_period?: int}>, chats: list<array|int|string>, users: list<array|int|string>}|array{_: 'messages.quickRepliesNotModified'} @see https://docs.madelineproto.xyz/API_docs/types/messages.QuickReplies.html
     * @psalm-impure
     */
    public function getQuickReplies(array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Reorder [quick reply shortcuts](https://core.telegram.org/api/business#quick-reply-shortcuts).
     * 
     * This will emit an [updateQuickReplies](https://docs.madelineproto.xyz/API_docs/constructors/updateQuickReplies.html) update to other logged-in sessions.
     *
     * @param list<int>|array<never, never> $order IDs of all created [quick reply shortcuts](https://core.telegram.org/api/business#quick-reply-shortcuts), in the desired order.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function reorderQuickReplies(array $order = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Before offering the user the choice to add a message to a [quick reply shortcut](https://core.telegram.org/api/business#quick-reply-shortcuts), to make sure that none of the limits specified [here Â»](https://core.telegram.org/api/business#quick-reply-shortcuts) were reached.
     *
     * @param string $shortcut Shorcut name (not ID!).
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function checkQuickReplyShortcut(string|null $shortcut = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Rename a [quick reply shortcut](https://core.telegram.org/api/business#quick-reply-shortcuts).  
     * This will emit an [updateQuickReplies](https://docs.madelineproto.xyz/API_docs/constructors/updateQuickReplies.html) update to other logged-in sessions.
     *
     * @param int $shortcut_id [Shortcut ID](https://core.telegram.org/api/business#quick-reply-shortcuts).
     * @param string $shortcut New shortcut name.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function editQuickReplyShortcut(int|null $shortcut_id = 0, string|null $shortcut = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Completely delete a [quick reply shortcut](https://core.telegram.org/api/business#quick-reply-shortcuts).  
     * This will also emit an [updateDeleteQuickReply](https://docs.madelineproto.xyz/API_docs/constructors/updateDeleteQuickReply.html) update to other logged-in sessions (and *no* [updateDeleteQuickReplyMessages](https://docs.madelineproto.xyz/API_docs/constructors/updateDeleteQuickReplyMessages.html) updates, even if all the messages in the shortcuts are also deleted by this method).
     *
     * @param int $shortcut_id [Shortcut ID](https://core.telegram.org/api/business#quick-reply-shortcuts)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function deleteQuickReplyShortcut(int|null $shortcut_id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Fetch (a subset or all) messages in a [quick reply shortcut Â»](https://core.telegram.org/api/business#quick-reply-shortcuts).
     *
     * @param int $shortcut_id Quick reply shortcut ID.
     * @param list<int> $id IDs of the messages to fetch, if empty fetches all of them.
     * @param list<int|string>|array<never, never> $hash Array of Hash for pagination, generated as specified [here Â»](https://core.telegram.org/api/business#quick-reply-shortcuts) (not the usual algorithm used for hash generation). @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.messages', messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.messagesSlice', inexact: array, count: array, next_rate?: array, offset_id_offset?: array, search_flood?: array, messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.channelMessages', inexact: array, pts: array, count: array, offset_id_offset?: array, messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.messagesNotModified', count: array} @see https://docs.madelineproto.xyz/API_docs/types/messages.Messages.html
     * @psalm-impure
     */
    public function getQuickReplyMessages(int|null $shortcut_id = 0, array|null $id = null, array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Send a [quick reply shortcut Â»](https://core.telegram.org/api/business#quick-reply-shortcuts).
     *
     * @param array|int|string $peer The peer where to send the shortcut (users only, for now). @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $shortcut_id The ID of the quick reply shortcut to send.
     * @param list<int>|array<never, never> $id Specify a subset of messages from the shortcut to send; if empty, defaults to all of them.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function sendQuickReplyMessages(array|int|string|null $peer = null, int|null $shortcut_id = 0, array $id = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Delete one or more messages from a [quick reply shortcut](https://core.telegram.org/api/business#quick-reply-shortcuts). This will also emit an [updateDeleteQuickReplyMessages](https://docs.madelineproto.xyz/API_docs/constructors/updateDeleteQuickReplyMessages.html) update.
     *
     * @param int $shortcut_id [Shortcut ID](https://core.telegram.org/api/business#quick-reply-shortcuts).
     * @param list<int>|array<never, never> $id IDs of shortcut messages to delete.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function deleteQuickReplyMessages(int|null $shortcut_id = 0, array $id = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Enable or disable [folder tags Â»](https://core.telegram.org/api/folders#folder-tags).
     *
     * @param bool $enabled Enable or disable folder tags.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function toggleDialogFilterTags(bool $enabled, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Fetch all [stickersets Â»](https://core.telegram.org/api/stickers) owned by the current user.
     *
     * @param int $offset_id [Offsets for pagination, for more info click here](https://core.telegram.org/api/offsets)
     * @param int $limit Maximum number of results to return, [see pagination](https://core.telegram.org/api/offsets)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.myStickers', count: int, sets: list<array{_: 'stickerSetCovered', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}, cover: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'stickerSetMultiCovered', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}, covers: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}|array{_: 'stickerSetFullCovered', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}, packs: list<array{_: 'stickerPack', emoticon: string, documents: list<int>}>, keywords: list<array{_: 'stickerKeyword', document_id: int, keyword: list<string>}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}|array{_: 'stickerSetNoCovered', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: int, local_id: int, secret: int}|array{_: 'fileLocation', dc_id: int, volume_id: int, local_id: int, secret: int}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}}>} @see https://docs.madelineproto.xyz/API_docs/types/messages.MyStickers.html
     * @psalm-impure
     */
    public function getMyStickers(int|null $offset_id = 0, int|null $limit = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Represents a list of [emoji categories](https://core.telegram.org/api/emoji-categories), to be used when choosing a sticker.
     *
     * @param int $hash 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.emojiGroupsNotModified'}|array{_: 'messages.emojiGroups', hash: int, groups: list<array{_: 'emojiGroup', title: string, icon_emoji_id: int, emoticons: list<string>}|array{_: 'emojiGroupGreeting', title: string, icon_emoji_id: int, emoticons: list<string>}|array{_: 'emojiGroupPremium', title: string, icon_emoji_id: int}>} @see https://docs.madelineproto.xyz/API_docs/types/messages.EmojiGroups.html
     * @psalm-impure
     */
    public function getEmojiStickerGroups(int|null $hash = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Fetch the full list of usable [animated message effects Â»](https://core.telegram.org/api/effects).
     *
     * @param int $hash 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.availableEffectsNotModified'}|array{_: 'messages.availableEffects', hash: int, effects: list<array{_: 'availableEffect', premium_required: bool, id: int, emoticon: string, static_icon_id?: int, effect_sticker_id: int, effect_animation_id?: int}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>} @see https://docs.madelineproto.xyz/API_docs/types/messages.AvailableEffects.html
     * @psalm-impure
     */
    public function getAvailableEffects(int|null $hash = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Edit/create a [fact-check](https://core.telegram.org/api/factcheck) on a message.
     * 
     * Can only be used by independent fact-checkers as specified by the [appConfig.can\_edit\_factcheck](https://core.telegram.org/api/config#can-edit-factcheck) configuration flag.
     *
     * @param array{_: 'textWithEntities', text?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>} $text Fact-check (maximum UTF-8 length specified in [appConfig.factcheck\_length\_limit](https://core.telegram.org/api/config#factcheck-length-limit)). @see https://docs.madelineproto.xyz/API_docs/types/TextWithEntities.html
     * @param array|int|string $peer Peer where the message was sent @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $msg_id Message ID
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function editFactCheck(array $text, array|int|string|null $peer = null, int|null $msg_id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Delete a [fact-check](https://core.telegram.org/api/factcheck) from a message.
     * 
     * Can only be used by independent fact-checkers as specified by the [appConfig.can\_edit\_factcheck](https://core.telegram.org/api/config#can-edit-factcheck) configuration flag.
     *
     * @param array|int|string $peer Peer where the message was sent. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $msg_id Message ID
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function deleteFactCheck(array|int|string|null $peer = null, int|null $msg_id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Fetch one or more [factchecks, see here Â»](https://core.telegram.org/api/factcheck) for the full flow.
     *
     * @param array|int|string $peer Peer where the messages were sent. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param list<int>|array<never, never> $msg_id Messages that have associated [factCheck](https://docs.madelineproto.xyz/API_docs/constructors/factCheck.html) constructors with the `need_check` flag set.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return list<array{_: 'factCheck', need_check: bool, country?: string, text?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}, hash: list<int|string>}> Array of  @see https://docs.madelineproto.xyz/API_docs/types/FactCheck.html
     * @psalm-impure
     */
    public function getFactCheck(array|int|string|null $peer = null, array $msg_id = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Open a [Main Mini App](https://core.telegram.org/api/bots/webapps#main-mini-apps).
     *
     * @param bool $compact If set, requests to open the mini app in compact mode (as opposed to normal or fullscreen mode). Must be set if the `mode` parameter of the [Main Mini App link](https://core.telegram.org/api/links#main-mini-app-links) is equal to `compact`.
     * @param bool $fullscreen If set, requests to open the mini app in fullscreen mode (as opposed to compact or normal mode). Must be set if the `mode` parameter of the [Main Mini App link](https://core.telegram.org/api/links#main-mini-app-links) is equal to `fullscreen`.
     * @param array|int|string $peer Currently open chat, may be [inputPeerEmpty](https://docs.madelineproto.xyz/API_docs/constructors/inputPeerEmpty.html) if no chat is currently open. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param array|int|string $bot Bot that owns the main mini app. @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param string $start_param Start parameter, if opening from a [Main Mini App link Â»](https://core.telegram.org/api/links#main-mini-app-links).
     * @param mixed $theme_params Any JSON-encodable data
     * @param string $platform Short name of the application; 0-64 English letters, digits, and underscores
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'webViewResultUrl', fullsize: bool, fullscreen: bool, same_origin: bool, query_id?: int, url: string} @see https://docs.madelineproto.xyz/API_docs/types/WebViewResult.html
     * @psalm-impure
     */
    public function requestMainWebView(bool|null $compact = null, bool|null $fullscreen = null, array|int|string|null $peer = null, array|int|string|null $bot = null, string|null $start_param = null, mixed $theme_params = null, string|null $platform = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Sends one or more [paid Telegram Star reactions Â»](https://core.telegram.org/api/reactions#paid-reactions), transferring [Telegram Stars Â»](https://core.telegram.org/api/stars) to a channel's balance.
     *
     * @param array|int|string $peer The channel @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $msg_id The message to react to
     * @param int $count The number of [stars](https://core.telegram.org/api/stars) to send (each will increment the reaction counter by one).
     * @param array{_: 'paidReactionPrivacyDefault'}|array{_: 'paidReactionPrivacyAnonymous'}|array{_: 'paidReactionPrivacyPeer', peer?: array|int|string} $private Each post with star reactions has a leaderboard with the top senders, but users can opt out of appearing there if they prefer more privacy. Not populating this field will use the default reaction privacy, stored on the server and synced to clients using [updatePaidReactionPrivacy](https://docs.madelineproto.xyz/API_docs/constructors/updatePaidReactionPrivacy.html) (see [here](https://core.telegram.org/api/reactions#paid-reaction-privacy) for more info). @see https://docs.madelineproto.xyz/API_docs/types/PaidReactionPrivacy.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function sendPaidReaction(array|int|string|null $peer = null, int|null $msg_id = 0, int|null $count = 0, array|null $private = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Changes the privacy of already sent [paid reactions](https://core.telegram.org/api/reactions#paid-reactions) on a specific message.
     *
     * @param array{_: 'paidReactionPrivacyDefault'}|array{_: 'paidReactionPrivacyAnonymous'}|array{_: 'paidReactionPrivacyPeer', peer?: array|int|string} $private If true, makes the current anonymous in the top sender leaderboard for this message; otherwise, does the opposite. @see https://docs.madelineproto.xyz/API_docs/types/PaidReactionPrivacy.html
     * @param array|int|string $peer The channel @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $msg_id The ID of the message to which we sent the paid reactions
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function togglePaidReactionPrivacy(array $private, array|int|string|null $peer = null, int|null $msg_id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Fetches an [updatePaidReactionPrivacy](https://docs.madelineproto.xyz/API_docs/constructors/updatePaidReactionPrivacy.html) update with the current [default paid reaction privacy, see here Â»](https://core.telegram.org/api/reactions#paid-reactions) for more info.
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function getPaidReactionPrivacy(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Mark a specific [sponsored message Â»](https://core.telegram.org/api/sponsored-messages) as read
     *
     * @param string $random_id The ad's unique ID.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function viewSponsoredMessage(string|null $random_id = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Informs the server that the user has interacted with a sponsored message in [one of the ways listed here Â»](https://core.telegram.org/api/sponsored-messages#clicking-on-sponsored-messages).
     *
     * @param bool $media The user clicked on the media
     * @param bool $fullscreen The user expanded the video to full screen, and then clicked on it.
     * @param string $random_id The ad's unique ID.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function clickSponsoredMessage(bool|null $media = null, bool|null $fullscreen = null, string|null $random_id = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Report a [sponsored message Â»](https://core.telegram.org/api/sponsored-messages), see [here Â»](https://core.telegram.org/api/sponsored-messages#reporting-sponsored-messages) for more info on the full flow.
     *
     * @param string $random_id The ad's unique ID.
     * @param string $option Chosen report option, initially an empty string, see [here Â»](https://core.telegram.org/api/sponsored-messages#reporting-sponsored-messages) for more info on the full flow.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'channels.sponsoredMessageReportResultChooseOption', title: string, options: list<array{_: 'sponsoredMessageReportOption', text: string, option: string}>}|array{_: 'channels.sponsoredMessageReportResultAdsHidden'}|array{_: 'channels.sponsoredMessageReportResultReported'} @see https://docs.madelineproto.xyz/API_docs/types/channels.SponsoredMessageReportResult.html
     * @psalm-impure
     */
    public function reportSponsoredMessage(string|null $random_id = '', string|null $option = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get a list of [sponsored messages for a peer, see here Â»](https://core.telegram.org/api/sponsored-messages) for more info.
     *
     * @param array|int|string $peer The currently open channel/bot. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $msg_id Must be set when fetching [sponsored messages to show on channel videos Â»](https://core.telegram.org/api/sponsored-messages#getting-sponsored-video-advertisements).
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.sponsoredMessages', posts_between?: int, start_delay?: int, between_delay?: int, messages: list<array{_: 'sponsoredMessage', recommended: bool, can_report: bool, random_id: string, url: string, title: string, message: string, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, button_text: string, sponsor_info?: string, additional_info?: string, min_display_duration?: int, max_display_duration?: int}>, chats: list<array|int|string>, users: list<array|int|string>}|array{_: 'messages.sponsoredMessagesEmpty'} @see https://docs.madelineproto.xyz/API_docs/types/messages.SponsoredMessages.html
     * @psalm-impure
     */
    public function getSponsoredMessages(array|int|string|null $peer = null, int|null $msg_id = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Save a [prepared inline message](https://core.telegram.org/api/bots/inline#21-using-a-prepared-inline-message), to be shared by the user of the mini app using a [web\_app\_send\_prepared\_message event](https://core.telegram.org/api/web-events#web-app-send-prepared-message)
     *
     * @param array{_: 'inputBotInlineResult', send_message: array{_: 'inputBotInlineMessageMediaAuto', invert_media?: array, message?: array, entities?: list<array>, reply_markup?: array}|array{_: 'inputBotInlineMessageText', no_webpage?: array, invert_media?: array, message?: array, entities?: list<array>, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaGeo', geo_point?: array, heading?: array, period?: array, proximity_notification_radius?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaVenue', geo_point?: array, title?: array, address?: array, provider?: array, venue_id?: array, venue_type?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaContact', phone_number?: array, first_name?: array, last_name?: array, vcard?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageGame', reply_markup?: array}|array{_: 'inputBotInlineMessageMediaInvoice', invoice: array, provider_data: array, title?: array, description?: array, photo?: array, payload?: array, provider?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaWebPage', invert_media?: array, force_large_media?: array, force_small_media?: array, optional?: array, message?: array, entities?: list<array>, url?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageRichMessage', rich_message: array, reply_markup?: array}, id?: string, type?: string, title?: string, description?: string, url?: string, thumb?: array{_: 'inputWebDocument', url?: array, size?: array, mime_type?: array, attributes?: list<array>}, content?: array{_: 'inputWebDocument', url?: array, size?: array, mime_type?: array, attributes?: list<array>}}|array{_: 'inputBotInlineResultPhoto', send_message: array{_: 'inputBotInlineMessageMediaAuto', invert_media?: array, message?: array, entities?: list<array>, reply_markup?: array}|array{_: 'inputBotInlineMessageText', no_webpage?: array, invert_media?: array, message?: array, entities?: list<array>, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaGeo', geo_point?: array, heading?: array, period?: array, proximity_notification_radius?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaVenue', geo_point?: array, title?: array, address?: array, provider?: array, venue_id?: array, venue_type?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaContact', phone_number?: array, first_name?: array, last_name?: array, vcard?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageGame', reply_markup?: array}|array{_: 'inputBotInlineMessageMediaInvoice', invoice: array, provider_data: array, title?: array, description?: array, photo?: array, payload?: array, provider?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaWebPage', invert_media?: array, force_large_media?: array, force_small_media?: array, optional?: array, message?: array, entities?: list<array>, url?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageRichMessage', rich_message: array, reply_markup?: array}, id?: string, type?: string, photo?: array}|array{_: 'inputBotInlineResultDocument', send_message: array{_: 'inputBotInlineMessageMediaAuto', invert_media?: array, message?: array, entities?: list<array>, reply_markup?: array}|array{_: 'inputBotInlineMessageText', no_webpage?: array, invert_media?: array, message?: array, entities?: list<array>, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaGeo', geo_point?: array, heading?: array, period?: array, proximity_notification_radius?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaVenue', geo_point?: array, title?: array, address?: array, provider?: array, venue_id?: array, venue_type?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaContact', phone_number?: array, first_name?: array, last_name?: array, vcard?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageGame', reply_markup?: array}|array{_: 'inputBotInlineMessageMediaInvoice', invoice: array, provider_data: array, title?: array, description?: array, photo?: array, payload?: array, provider?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaWebPage', invert_media?: array, force_large_media?: array, force_small_media?: array, optional?: array, message?: array, entities?: list<array>, url?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageRichMessage', rich_message: array, reply_markup?: array}, id?: string, type?: string, title?: string, description?: string, document?: array}|array{_: 'inputBotInlineResultGame', send_message: array{_: 'inputBotInlineMessageMediaAuto', invert_media?: array, message?: array, entities?: list<array>, reply_markup?: array}|array{_: 'inputBotInlineMessageText', no_webpage?: array, invert_media?: array, message?: array, entities?: list<array>, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaGeo', geo_point?: array, heading?: array, period?: array, proximity_notification_radius?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaVenue', geo_point?: array, title?: array, address?: array, provider?: array, venue_id?: array, venue_type?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaContact', phone_number?: array, first_name?: array, last_name?: array, vcard?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageGame', reply_markup?: array}|array{_: 'inputBotInlineMessageMediaInvoice', invoice: array, provider_data: array, title?: array, description?: array, photo?: array, payload?: array, provider?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaWebPage', invert_media?: array, force_large_media?: array, force_small_media?: array, optional?: array, message?: array, entities?: list<array>, url?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageRichMessage', rich_message: array, reply_markup?: array}, id?: string, short_name?: string} $result The message @see https://docs.madelineproto.xyz/API_docs/types/InputBotInlineResult.html
     * @param array|int|string $user_id The user to whom the [web\_app\_send\_prepared\_message event](https://core.telegram.org/api/web-events#web-app-send-prepared-message) event will be sent @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}> $peer_types Array of Types of chats where this message can be sent @see https://docs.madelineproto.xyz/API_docs/types/InlineQueryPeerType.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.botPreparedInlineMessage', id: string, expire_date: int} @see https://docs.madelineproto.xyz/API_docs/types/messages.BotPreparedInlineMessage.html
     * @psalm-impure
     */
    public function savePreparedInlineMessage(array $result, array|int|string|null $user_id = null, array|null $peer_types = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Obtain a [prepared inline message](https://core.telegram.org/api/bots/inline#21-using-a-prepared-inline-message) generated by a [mini app](https://core.telegram.org/api/bots/webapps): invoked when handling [web\_app\_send\_prepared\_message events](https://core.telegram.org/api/web-events#web-app-send-prepared-message)
     *
     * @param array|int|string $bot The bot that owns the mini app that emitted the [web\_app\_send\_prepared\_message event](https://core.telegram.org/api/web-events#web-app-send-prepared-message) @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param string $id The `id` from the [web\_app\_send\_prepared\_message event](https://core.telegram.org/api/web-events#web-app-send-prepared-message)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.preparedInlineMessage', result: array{_: 'botInlineResult', send_message: array{_: 'botInlineMessageMediaAuto', invert_media: bool, message: string, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, reply_markup?: array}|array{_: 'botInlineMessageText', no_webpage: bool, invert_media: bool, message: string, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, reply_markup?: array}|array{_: 'botInlineMessageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash: int, accuracy_radius?: int}, heading?: int, period?: int, proximity_notification_radius?: int, reply_markup?: array}|array{_: 'botInlineMessageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash: int, accuracy_radius?: int}, title: string, address: string, provider: string, venue_id: string, venue_type: string, reply_markup?: array}|array{_: 'botInlineMessageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, reply_markup?: array}|array{_: 'botInlineMessageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: string, access_hash: int, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: array, y: array, zoom: array, n: array}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}|array{_: 'webDocumentNoProxy', url: string, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: array, y: array, zoom: array, n: array}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}, currency: string, total_amount: int, reply_markup?: array}|array{_: 'botInlineMessageMediaWebPage', invert_media: bool, force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, message: string, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, url: string, reply_markup?: array}|array{_: 'botInlineMessageRichMessage', rich_message: array{_: 'richMessage', rtl: bool, part: bool, blocks: list<array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, published_date: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, language: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: string}|array{_: 'pageBlockList', items: list<array{_: 'pageListItemText', text: array, checkbox: array, checked: array}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, collapsed: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array, credit: array}, spoiler: bool, photo_id: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array, credit: array}, autoplay: bool, loop: bool, spoiler: bool, video_id: int}|array{_: 'pageBlockCover', cover: array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array}|array{_: 'pageBlockSubtitle', text: array}|array{_: 'pageBlockAuthorDate', author: array, published_date: array}|array{_: 'pageBlockHeader', text: array}|array{_: 'pageBlockSubheader', text: array}|array{_: 'pageBlockParagraph', text: array}|array{_: 'pageBlockPreformatted', text: array, language: array}|array{_: 'pageBlockFooter', text: array}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: array}|array{_: 'pageBlockList', items: list<array>}|array{_: 'pageBlockBlockquote', text: array, caption: array, collapsed: array}|array{_: 'pageBlockPullquote', text: array, caption: array}|array{_: 'pageBlockPhoto', caption: array, spoiler: array, photo_id: array, url?: array, webpage_id?: array}|array{_: 'pageBlockVideo', caption: array, autoplay: array, loop: array, spoiler: array, video_id: array}|array{_: 'pageBlockEmbed', caption: array, full_width: array, allow_scrolling: array, url?: array, html?: array, poster_photo_id?: array, w?: array, h?: array}|array{_: 'pageBlockChannel', channel: array}|array{_: 'pageBlockAudio', caption: array, audio_id: array}|array{_: 'pageBlockKicker', text: array}|array{_: 'pageBlockTable', title: array, bordered: array, striped: array, compact: array, rows: list<array>}|array{_: 'pageBlockOrderedList', reversed: array, items: list<array>, start?: array, type?: array}|array{_: 'pageBlockRelatedArticles', title: array, articles: list<array>}|array{_: 'pageBlockMap', caption: array, geo: array, zoom: array, w: array, h: array}|array{_: 'pageBlockHeading1', text: array}|array{_: 'pageBlockHeading2', text: array}|array{_: 'pageBlockHeading3', text: array}|array{_: 'pageBlockHeading4', text: array}|array{_: 'pageBlockHeading5', text: array}|array{_: 'pageBlockHeading6', text: array}|array{_: 'pageBlockMath', source: array}|array{_: 'pageBlockThinking', text: array}|array{_: 'inputPageBlockMap', caption: array, geo: array, zoom: array, w: array, h: array}|array{_: 'pageBlockButtonRow', align_left: array, align_center: array, align_right: array, buttons: list<array>}|array{_: 'pageBlockDocument', caption: array, document_id: array}}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array, credit: array}, full_width: bool, allow_scrolling: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array, credit: array}, audio_id: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, bordered: bool, striped: bool, compact: bool, rows: list<array{_: 'pageTableRow', cells: list<array>}>}|array{_: 'pageBlockOrderedList', reversed: bool, items: list<array{_: 'pageListOrderedItemText', text: array, checkbox: array, checked: array, num?: array, value?: array, type?: array}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, articles: list<array{_: 'pageRelatedArticle', url: array, webpage_id: array, title?: array, description?: array, photo_id?: array, author?: array, published_date?: array}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockMath', source: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: array, long: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockButtonRow', align_left: bool, align_center: bool, align_right: bool, buttons: list<array{_: 'pageButton', text: array, type: array, style?: array}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array, credit: array}, document_id: int}>, photos: list<array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}, reply_markup?: array}, id: string, type: string, title?: string, description?: string, url?: string, thumb?: array{_: 'webDocument', url: string, access_hash: int, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n: int}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}|array{_: 'webDocumentNoProxy', url: string, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n: int}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}, content?: array{_: 'webDocument', url: string, access_hash: int, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n: int}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}|array{_: 'webDocumentNoProxy', url: string, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: float, y: float, zoom: float, n: int}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: int, access_hash: int}|array{_: 'inputStickerSetShortName', short_name: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}}|array{_: 'botInlineMediaResult', send_message: array{_: 'botInlineMessageMediaAuto', invert_media: bool, message: string, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, reply_markup?: array}|array{_: 'botInlineMessageText', no_webpage: bool, invert_media: bool, message: string, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, reply_markup?: array}|array{_: 'botInlineMessageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash: int, accuracy_radius?: int}, heading?: int, period?: int, proximity_notification_radius?: int, reply_markup?: array}|array{_: 'botInlineMessageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash: int, accuracy_radius?: int}, title: string, address: string, provider: string, venue_id: string, venue_type: string, reply_markup?: array}|array{_: 'botInlineMessageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, reply_markup?: array}|array{_: 'botInlineMessageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: string, access_hash: int, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: array, y: array, zoom: array, n: array}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}|array{_: 'webDocumentNoProxy', url: string, size: int, mime_type: string, attributes: list<array{_: 'documentAttributeImageSize', w: int, h: int}|array{_: 'documentAttributeAnimated'}|array{_: 'documentAttributeSticker', mask: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, mask_coords?: array{_: 'maskCoords', x: array, y: array, zoom: array, n: array}}|array{_: 'documentAttributeVideo', duration: float, round_message: bool, supports_streaming: bool, nosound: bool, w: int, h: int, preload_prefix_size?: int, video_start_ts?: float, video_codec?: string}|array{_: 'documentAttributeAudio', voice: bool, duration: int, title?: string, performer?: string, waveform?: non-empty-list<int<0, 31>>}|array{_: 'documentAttributeFilename', file_name: string}|array{_: 'documentAttributeHasStickers'}|array{_: 'documentAttributeCustomEmoji', free: bool, text_color: bool, alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeSticker'}|array{_: 'documentAttributeVideo', duration: int, w: int, h: int}|array{_: 'documentAttributeAudio', duration: int}|array{_: 'documentAttributeSticker', alt: string, stickerset: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id: array, access_hash: array}|array{_: 'inputStickerSetShortName', short_name: array}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon: array}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}}|array{_: 'documentAttributeAudio', duration: int, title: string, performer: string}|array{_: 'documentAttributeVideo', round_message: bool, duration: int, w: int, h: int}>}, currency: string, total_amount: int, reply_markup?: array}|array{_: 'botInlineMessageMediaWebPage', invert_media: bool, force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, message: string, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, url: string, reply_markup?: array}|array{_: 'botInlineMessageRichMessage', rich_message: array{_: 'richMessage', rtl: bool, part: bool, blocks: list<array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, published_date: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, language: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: string}|array{_: 'pageBlockList', items: list<array{_: 'pageListItemText', text: array, checkbox: array, checked: array}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, collapsed: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array, credit: array}, spoiler: bool, photo_id: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array, credit: array}, autoplay: bool, loop: bool, spoiler: bool, video_id: int}|array{_: 'pageBlockCover', cover: array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array}|array{_: 'pageBlockSubtitle', text: array}|array{_: 'pageBlockAuthorDate', author: array, published_date: array}|array{_: 'pageBlockHeader', text: array}|array{_: 'pageBlockSubheader', text: array}|array{_: 'pageBlockParagraph', text: array}|array{_: 'pageBlockPreformatted', text: array, language: array}|array{_: 'pageBlockFooter', text: array}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: array}|array{_: 'pageBlockList', items: list<array>}|array{_: 'pageBlockBlockquote', text: array, caption: array, collapsed: array}|array{_: 'pageBlockPullquote', text: array, caption: array}|array{_: 'pageBlockPhoto', caption: array, spoiler: array, photo_id: array, url?: array, webpage_id?: array}|array{_: 'pageBlockVideo', caption: array, autoplay: array, loop: array, spoiler: array, video_id: array}|array{_: 'pageBlockEmbed', caption: array, full_width: array, allow_scrolling: array, url?: array, html?: array, poster_photo_id?: array, w?: array, h?: array}|array{_: 'pageBlockChannel', channel: array}|array{_: 'pageBlockAudio', caption: array, audio_id: array}|array{_: 'pageBlockKicker', text: array}|array{_: 'pageBlockTable', title: array, bordered: array, striped: array, compact: array, rows: list<array>}|array{_: 'pageBlockOrderedList', reversed: array, items: list<array>, start?: array, type?: array}|array{_: 'pageBlockRelatedArticles', title: array, articles: list<array>}|array{_: 'pageBlockMap', caption: array, geo: array, zoom: array, w: array, h: array}|array{_: 'pageBlockHeading1', text: array}|array{_: 'pageBlockHeading2', text: array}|array{_: 'pageBlockHeading3', text: array}|array{_: 'pageBlockHeading4', text: array}|array{_: 'pageBlockHeading5', text: array}|array{_: 'pageBlockHeading6', text: array}|array{_: 'pageBlockMath', source: array}|array{_: 'pageBlockThinking', text: array}|array{_: 'inputPageBlockMap', caption: array, geo: array, zoom: array, w: array, h: array}|array{_: 'pageBlockButtonRow', align_left: array, align_center: array, align_right: array, buttons: list<array>}|array{_: 'pageBlockDocument', caption: array, document_id: array}}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array, credit: array}, full_width: bool, allow_scrolling: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array, credit: array}, audio_id: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, bordered: bool, striped: bool, compact: bool, rows: list<array{_: 'pageTableRow', cells: list<array>}>}|array{_: 'pageBlockOrderedList', reversed: bool, items: list<array{_: 'pageListOrderedItemText', text: array, checkbox: array, checked: array, num?: array, value?: array, type?: array}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, articles: list<array{_: 'pageRelatedArticle', url: array, webpage_id: array, title?: array, description?: array, photo_id?: array, author?: array, published_date?: array}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockMath', source: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: array, long: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockButtonRow', align_left: bool, align_center: bool, align_right: bool, buttons: list<array{_: 'pageButton', text: array, type: array, style?: array}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array, credit: array}, document_id: int}>, photos: list<array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}, reply_markup?: array}, id: string, type: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, title?: string, description?: string}, query_id: int, peer_types: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>, cache_time: int, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/messages.PreparedInlineMessage.html
     * @psalm-impure
     */
    public function getPreparedInlineMessage(array|int|string|null $bot = null, string|null $id = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Search for stickers using AI-powered keyword search
     *
     * @param bool $emojis If set, returns [custom emoji stickers](https://core.telegram.org/api/custom-emoji)
     * @param string $q The search term
     * @param string $emoticon Space-separated list of emojis to search for
     * @param list<string>|array<never, never> $lang_code List of possible IETF language tags of the user's input language; may be empty if unknown
     * @param int $offset [Offset for pagination](https://core.telegram.org/api/offsets)
     * @param int $limit Maximum number of results to return, [see pagination](https://core.telegram.org/api/offsets)
     * @param list<int|string>|array<never, never> $hash Array of [Hash used for caching, for more info click here](https://core.telegram.org/api/offsets#hash-generation). <br>The hash may be generated locally by using the `id`s of the returned or stored sticker [document](https://docs.madelineproto.xyz/API_docs/constructors/document.html)s. @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.foundStickersNotModified', next_offset?: int}|array{_: 'messages.foundStickers', next_offset?: int, hash: list<int|string>, stickers: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>} @see https://docs.madelineproto.xyz/API_docs/types/messages.FoundStickers.html
     * @psalm-impure
     */
    public function searchStickers(bool|null $emojis = null, string|null $q = '', string|null $emoticon = '', array $lang_code = [], int|null $offset = 0, int|null $limit = 0, array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Used for [Telegram Gateway verification messages Â»](https://telegram.org/blog/star-messages-gateway-2-0-and-more#save-even-more-on-user-verification): indicate to the server that one or more [message](https://docs.madelineproto.xyz/API_docs/constructors/message.html)s were received by the client, if requested by the [message](https://docs.madelineproto.xyz/API_docs/constructors/message.html).**report\_delivery\_until\_date** flag or the equivalent flag in [push notifications](https://core.telegram.org/api/push-updates).
     *
     * @param bool $push Must be set if the messages were received from a [push notification](https://core.telegram.org/api/push-updates).
     * @param array|int|string $peer The peer where the messages were received. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param list<int>|array<never, never> $id The IDs of the received messages.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function reportMessagesDelivery(bool|null $push = null, array|int|string|null $peer = null, array $id = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Obtain information about specific [saved message dialogs Â»](https://core.telegram.org/api/saved-messages#saved-message-dialogs) or [monoforum topics Â»](https://core.telegram.org/api/monoforum).
     *
     * @param array|int|string $parent_peer If set, fetches [monoforum topics Â»](https://core.telegram.org/api/monoforum), otherwise fetches [saved message dialogs Â»](https://core.telegram.org/api/saved-messages#saved-message-dialogs). @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param list<array|int|string>|array<never, never> $ids Array of IDs of dialogs (topics) to fetch. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.savedDialogs', dialogs: list<array{_: 'savedDialog', peer: array|int|string, pinned: bool, top_message: int}|array{_: 'monoForumDialog', peer: array|int|string, unread_mark: bool, nopaid_messages_exception: bool, top_message: int, read_inbox_max_id: int, read_outbox_max_id: int, unread_count: int, unread_reactions_count: int, draft?: array{_: 'draftMessageEmpty', date?: int}|array{_: 'draftMessage', no_webpage: bool, invert_media: bool, reply_to?: array{_: 'inputReplyToMessage', reply_to_msg_id: int, top_msg_id?: int, reply_to_peer_id?: array|int|string, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, quote_offset?: int, monoforum_peer_id?: array|int|string, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, url: string, webpage_id: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, email: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, phone: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, name: string}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, user_id: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, date: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, type: array{_: 'inlineButtonTypeUrl', url: array}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: array, url: array, button_id: array}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: array, fwd_text?: array, url: array, bot?: array}|array{_: 'inlineButtonTypeWebView', url: array}|array{_: 'inlineButtonTypeCallback', requires_password: array, data: array}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: array, query: array, peer_types?: list<array>}|array{_: 'inlineButtonTypeUserProfile', user_id: array}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array}|array{_: 'inlineButtonTypeCopy', copy_text: array}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: array, bg_danger: array, bg_success: array, link: array}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}>}|array{_: 'inputReplyToStory', peer: array|int|string, story_id: int}|array{_: 'inputReplyToMonoForum', monoforum_peer_id: array|int|string}|array{_: 'inputReplyToEphemeralMessage', id: int}, message: string, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, media?: \danog\MadelineProto\EventHandler\Media|string|array, date: int, effect?: int, suggested_post?: array{_: 'suggestedPost', accepted: bool, rejected: bool, price?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, schedule_date?: int}, rich_message?: array{_: 'richMessage', rtl: bool, part: bool, blocks: list<array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, published_date: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, language: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: string}|array{_: 'pageBlockList', items: list<array{_: 'pageListItemText', text: array, checkbox: array, checked: array}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, collapsed: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array, credit: array}, spoiler: bool, photo_id: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array, credit: array}, autoplay: bool, loop: bool, spoiler: bool, video_id: int}|array{_: 'pageBlockCover', cover: array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array}|array{_: 'pageBlockSubtitle', text: array}|array{_: 'pageBlockAuthorDate', author: array, published_date: array}|array{_: 'pageBlockHeader', text: array}|array{_: 'pageBlockSubheader', text: array}|array{_: 'pageBlockParagraph', text: array}|array{_: 'pageBlockPreformatted', text: array, language: array}|array{_: 'pageBlockFooter', text: array}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: array}|array{_: 'pageBlockList', items: list<array>}|array{_: 'pageBlockBlockquote', text: array, caption: array, collapsed: array}|array{_: 'pageBlockPullquote', text: array, caption: array}|array{_: 'pageBlockPhoto', caption: array, spoiler: array, photo_id: array, url?: array, webpage_id?: array}|array{_: 'pageBlockVideo', caption: array, autoplay: array, loop: array, spoiler: array, video_id: array}|array{_: 'pageBlockEmbed', caption: array, full_width: array, allow_scrolling: array, url?: array, html?: array, poster_photo_id?: array, w?: array, h?: array}|array{_: 'pageBlockChannel', channel: array}|array{_: 'pageBlockAudio', caption: array, audio_id: array}|array{_: 'pageBlockKicker', text: array}|array{_: 'pageBlockTable', title: array, bordered: array, striped: array, compact: array, rows: list<array>}|array{_: 'pageBlockOrderedList', reversed: array, items: list<array>, start?: array, type?: array}|array{_: 'pageBlockRelatedArticles', title: array, articles: list<array>}|array{_: 'pageBlockMap', caption: array, geo: array, zoom: array, w: array, h: array}|array{_: 'pageBlockHeading1', text: array}|array{_: 'pageBlockHeading2', text: array}|array{_: 'pageBlockHeading3', text: array}|array{_: 'pageBlockHeading4', text: array}|array{_: 'pageBlockHeading5', text: array}|array{_: 'pageBlockHeading6', text: array}|array{_: 'pageBlockMath', source: array}|array{_: 'pageBlockThinking', text: array}|array{_: 'inputPageBlockMap', caption: array, geo: array, zoom: array, w: array, h: array}|array{_: 'pageBlockButtonRow', align_left: array, align_center: array, align_right: array, buttons: list<array>}|array{_: 'pageBlockDocument', caption: array, document_id: array}}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array, credit: array}, full_width: bool, allow_scrolling: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array, credit: array}, audio_id: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, bordered: bool, striped: bool, compact: bool, rows: list<array{_: 'pageTableRow', cells: list<array>}>}|array{_: 'pageBlockOrderedList', reversed: bool, items: list<array{_: 'pageListOrderedItemText', text: array, checkbox: array, checked: array, num?: array, value?: array, type?: array}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, articles: list<array{_: 'pageRelatedArticle', url: array, webpage_id: array, title?: array, description?: array, photo_id?: array, author?: array, published_date?: array}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockMath', source: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: array, long: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockButtonRow', align_left: bool, align_center: bool, align_right: bool, buttons: list<array{_: 'pageButton', text: array, type: array, style?: array}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array, credit: array}, document_id: int}>, photos: list<array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}}}>, messages: list<array{_: 'messageEmpty', id: int, peer_id?: array|int|string}|array{_: 'message', peer_id: array|int|string, out: bool, mentioned: bool, media_unread: bool, silent: bool, post: bool, from_scheduled: bool, legacy: bool, edit_hide: bool, pinned: bool, noforwards: bool, invert_media: bool, offline: bool, video_processing_pending: bool, paid_suggested_post_stars: bool, paid_suggested_post_ton: bool, id: int, from_id?: array|int|string, from_boosts_applied?: int, from_rank?: string, saved_peer_id?: array|int|string, fwd_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, via_bot_id?: int, via_business_bot_id?: int, guestchat_via_from?: array|int|string, reply_to?: array{_: 'messageReplyHeader', reply_to_scheduled: bool, forum_topic: bool, quote: bool, reply_to_ephemeral: bool, reply_to_msg_id?: int, reply_to_peer_id?: array|int|string, reply_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, reply_media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_to_top_id?: int, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, quote_offset?: int, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, url: string, webpage_id: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, email: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, phone: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, name: string}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, user_id: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, date: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, type: array{_: 'inlineButtonTypeUrl', url: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url: string, button_id: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: bool, fwd_text?: string, url: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url: string}|array{_: 'inlineButtonTypeCallback', requires_password: bool, data: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: bool, query: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: bool, bg_danger: bool, bg_success: bool, link: bool}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}>}|array{_: 'messageReplyStoryHeader', peer: array|int|string, story_id: int}, date: int, message: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_markup?: array, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, views?: int, forwards?: int, replies?: array{_: 'messageReplies', comments: bool, replies: int, replies_pts: int, recent_repliers?: list<array|int|string>, channel_id?: int, max_id?: int, read_max_id?: int}, edit_date?: int, post_author?: string, grouped_id?: int, reactions?: array{_: 'messageReactions', min: bool, can_see_list: bool, reactions_as_tags: bool, results: list<array{_: 'reactionCount', chosen_order?: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}, count: int}>, recent_reactions?: list<array{_: 'messagePeerReaction', peer_id: array|int|string, big: bool, unread: bool, my: bool, date: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}}>, top_reactors?: list<array{_: 'messageReactor', top: bool, my: bool, anonymous: bool, peer_id?: array|int|string, count: int}>}, restriction_reason?: list<array{_: 'restrictionReason', platform: string, reason: string, text: string}>, ttl_period?: int, quick_reply_shortcut_id?: int, effect?: int, factcheck?: array{_: 'factCheck', need_check: bool, country?: string, text?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}, hash: list<int|string>}, report_delivery_until_date?: int, paid_message_stars?: int, suggested_post?: array{_: 'suggestedPost', accepted: bool, rejected: bool, price?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, schedule_date?: int}, schedule_repeat_period?: int, summary_from_language?: string, rich_message?: array{_: 'richMessage', rtl: bool, part: bool, blocks: list<array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, published_date: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, language: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: string}|array{_: 'pageBlockList', items: list<array{_: 'pageListItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, checkbox: bool, checked: bool}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, collapsed: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, spoiler: bool, photo_id: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, autoplay: bool, loop: bool, spoiler: bool, video_id: int}|array{_: 'pageBlockCover', cover: array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, published_date: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, language: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: string}|array{_: 'pageBlockList', items: list<array{_: 'pageListItemText', text: array, checkbox: array, checked: array}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, collapsed: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array, credit: array}, spoiler: bool, photo_id: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array, credit: array}, autoplay: bool, loop: bool, spoiler: bool, video_id: int}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array, credit: array}, full_width: bool, allow_scrolling: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array, credit: array}, audio_id: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, bordered: bool, striped: bool, compact: bool, rows: list<array{_: 'pageTableRow', cells: list<array>}>}|array{_: 'pageBlockOrderedList', reversed: bool, items: list<array{_: 'pageListOrderedItemText', text: array, checkbox: array, checked: array, num?: array, value?: array, type?: array}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, articles: list<array{_: 'pageRelatedArticle', url: array, webpage_id: array, title?: array, description?: array, photo_id?: array, author?: array, published_date?: array}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockMath', source: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: array, long: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockButtonRow', align_left: bool, align_center: bool, align_right: bool, buttons: list<array{_: 'pageButton', text: array, type: array, style?: array}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array, credit: array}, document_id: int}}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, full_width: bool, allow_scrolling: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, audio_id: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, bordered: bool, striped: bool, compact: bool, rows: list<array{_: 'pageTableRow', cells: list<array{_: 'pageTableCell', header: array, align_center: array, align_right: array, valign_middle: array, valign_bottom: array, text?: array, colspan?: array, rowspan?: array}>}>}|array{_: 'pageBlockOrderedList', reversed: bool, items: list<array{_: 'pageListOrderedItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, checkbox: bool, checked: bool, num?: string, value?: int, type?: string}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, articles: list<array{_: 'pageRelatedArticle', url: string, webpage_id: int, title?: string, description?: string, photo_id?: int, author?: string, published_date?: int}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash: int, accuracy_radius?: int}, zoom: int, w: int, h: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockMath', source: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, geo: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: float, long: float, accuracy_radius?: int}, zoom: int, w: int, h: int}|array{_: 'pageBlockButtonRow', align_left: bool, align_center: bool, align_right: bool, buttons: list<array{_: 'pageButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, type: array{_: 'inlineButtonTypeUrl', url: array}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: array, url: array, button_id: array}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: array, fwd_text?: array, url: array, bot?: array}|array{_: 'inlineButtonTypeWebView', url: array}|array{_: 'inlineButtonTypeCallback', requires_password: array, data: array}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: array, query: array, peer_types?: list<array>}|array{_: 'inlineButtonTypeUserProfile', user_id: array}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array}|array{_: 'inlineButtonTypeCopy', copy_text: array}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: array, bg_danger: array, bg_success: array, link: array}}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, document_id: int}>, photos: list<array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}}|array{_: 'messageService', peer_id: array|int|string, out: bool, mentioned: bool, media_unread: bool, reactions_are_possible: bool, silent: bool, post: bool, legacy: bool, id: int, from_id?: array|int|string, saved_peer_id?: array|int|string, reply_to?: array{_: 'messageReplyHeader', reply_to_scheduled: bool, forum_topic: bool, quote: bool, reply_to_ephemeral: bool, reply_to_msg_id?: int, reply_to_peer_id?: array|int|string, reply_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, reply_media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_to_top_id?: int, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, quote_offset?: int, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, url: string, webpage_id: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, email: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, phone: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, name: string}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, user_id: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, date: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, type: array{_: 'inlineButtonTypeUrl', url: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url: string, button_id: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: bool, fwd_text?: string, url: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url: string}|array{_: 'inlineButtonTypeCallback', requires_password: bool, data: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: bool, query: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: bool, bg_danger: bool, bg_success: bool, link: bool}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}>}|array{_: 'messageReplyStoryHeader', peer: array|int|string, story_id: int}, date: int, action: array{_: 'messageActionEmpty'}|array{_: 'messageActionChatCreate', title: string, users: list<int>}|array{_: 'messageActionChatEditTitle', title: string}|array{_: 'messageActionChatEditPhoto', photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'messageActionChatDeletePhoto'}|array{_: 'messageActionChatAddUser', users: list<int>}|array{_: 'messageActionChatDeleteUser', user_id: int}|array{_: 'messageActionChatJoinedByLink', inviter_id: int}|array{_: 'messageActionChannelCreate', title: string}|array{_: 'messageActionChatMigrateTo', channel_id: int}|array{_: 'messageActionChannelMigrateFrom', title: string, chat_id: array|int|string}|array{_: 'messageActionPinMessage'}|array{_: 'messageActionHistoryClear'}|array{_: 'messageActionGameScore', game_id: int, score: int}|array{_: 'messageActionPaymentSentMe', charge: array{_: 'paymentCharge', id: string, provider_charge_id: string}, recurring_init: bool, recurring_used: bool, currency: string, total_amount: int, payload: string, info?: array{_: 'paymentRequestedInfo', name?: string, phone?: string, email?: string, shipping_address?: array{_: 'postAddress', street_line1: string, street_line2: string, city: string, state: string, country_iso2: string, post_code: string}}, shipping_option_id?: string, subscription_until_date?: int}|array{_: 'messageActionPaymentSent', recurring_init: bool, recurring_used: bool, currency: string, total_amount: int, invoice_slug?: string, subscription_until_date?: int}|array{_: 'messageActionPhoneCall', video: bool, call_id: int, reason?: array{_: 'phoneCallDiscardReasonMissed'}|array{_: 'phoneCallDiscardReasonDisconnect'}|array{_: 'phoneCallDiscardReasonHangup'}|array{_: 'phoneCallDiscardReasonBusy'}|array{_: 'phoneCallDiscardReasonMigrateConferenceCall', slug: string}, duration?: int}|array{_: 'messageActionScreenshotTaken'}|array{_: 'messageActionCustomAction', message: string}|array{_: 'messageActionBotAllowed', attach_menu: bool, from_request: bool, domain?: string, app?: array{_: 'botAppNotModified'}|array{_: 'botApp', id: int, access_hash: int, short_name: string, title: string, description: string, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, hash: list<int|string>}}|array{_: 'messageActionSecureValuesSentMe', credentials: array{_: 'secureCredentialsEncrypted', data: string, hash: string, secret: string}, values: list<array{_: 'secureValue', type: array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}, data?: array{_: 'secureData', data: string, data_hash: string, secret: string}, front_side?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, reverse_side?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, selfie?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, translation?: list<array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}>, files?: list<array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}>, plain_data?: array{_: 'securePlainPhone', phone: string}|array{_: 'securePlainEmail', email: string}, hash: string}>}|array{_: 'messageActionSecureValuesSent', types: list<array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}>}|array{_: 'messageActionContactSignUp'}|array{_: 'messageActionGeoProximityReached', from_id: array|int|string, to_id: array|int|string, distance: int}|array{_: 'messageActionGroupCall', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, duration?: int}|array{_: 'messageActionInviteToGroupCall', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, users: list<int>}|array{_: 'messageActionSetMessagesTTL', period: int, auto_setting_from?: int}|array{_: 'messageActionGroupCallScheduled', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, schedule_date: int}|array{_: 'messageActionSetChatTheme', theme: array{_: 'chatTheme', emoticon: string}|array{_: 'chatThemeUniqueGift', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: array, edge_color: array, text_color: array}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array, crafted: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array, name: array, backdrop_id: array, center_color: array, edge_color: array, pattern_color: array, text_color: array}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array, sender_id?: array, date: array, message?: array}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: array, nanos: array}|array{_: 'starsTonAmount', amount: array}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: array, background_emoji_id?: array}|array{_: 'peerColorCollectible', collectible_id: array, gift_emoji_id: array, background_emoji_id: array, accent_color: array, colors: list<array>, dark_accent_color?: array, dark_colors?: list<array>}|array{_: 'inputPeerColorCollectible', collectible_id: array}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, theme_settings: list<array{_: 'themeSettings', base_theme: array{_: 'baseThemeClassic'}|array{_: 'baseThemeDay'}|array{_: 'baseThemeNight'}|array{_: 'baseThemeTinted'}|array{_: 'baseThemeArctic'}, message_colors_animated: bool, accent_color: int, outbox_accent_color?: int, message_colors?: list<int>, wallpaper?: array{_: 'wallPaper', id: array, creator: array, default: array, pattern: array, dark: array, access_hash: array, slug: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, settings?: array}|array{_: 'wallPaperNoFile', id: array, default: array, dark: array, settings?: array}}>}}|array{_: 'messageActionChatJoinedByRequest'}|array{_: 'messageActionWebViewDataSentMe', text: string, data: string}|array{_: 'messageActionWebViewDataSent', text: string}|array{_: 'messageActionGiftPremium', currency: string, amount: int, days: int, crypto_currency?: string, crypto_amount?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}|array{_: 'messageActionTopicCreate', title_missing: bool, title: string, icon_color: int, icon_emoji_id?: int}|array{_: 'messageActionTopicEdit', title?: string, icon_emoji_id?: int, closed?: bool, hidden?: bool}|array{_: 'messageActionSuggestProfilePhoto', photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'messageActionRequestedPeer', button_id: int, peers: list<array|int|string>}|array{_: 'messageActionSetChatWallPaper', wallpaper: array{_: 'wallPaper', id: int, creator: bool, default: bool, pattern: bool, dark: bool, access_hash: int, slug: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, settings?: array{_: 'wallPaperSettings', blur: bool, motion: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string}}|array{_: 'wallPaperNoFile', id: int, default: bool, dark: bool, settings?: array{_: 'wallPaperSettings', blur: bool, motion: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string}}, same: bool, for_both: bool}|array{_: 'messageActionGiftCode', via_giveaway: bool, unclaimed: bool, boost_peer?: array|int|string, days: int, slug: string, currency?: string, amount?: int, crypto_currency?: string, crypto_amount?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}|array{_: 'messageActionGiveawayLaunch', stars?: int}|array{_: 'messageActionGiveawayResults', stars: bool, winners_count: int, unclaimed_count: int}|array{_: 'messageActionBoostApply', boosts: int}|array{_: 'messageActionRequestedPeerSentMe', button_id: int, peers: list<array{_: 'requestedPeerUser', user_id: int, first_name?: string, last_name?: string, username?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'requestedPeerChat', chat_id: array|int|string, title?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'requestedPeerChannel', channel_id: int, title?: string, username?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}>}|array{_: 'messageActionPaymentRefunded', peer: array|int|string, charge: array{_: 'paymentCharge', id: string, provider_charge_id: string}, currency: string, total_amount: int, payload?: string}|array{_: 'messageActionGiftStars', currency: string, amount: int, stars: int, crypto_currency?: string, crypto_amount?: int, transaction_id?: string}|array{_: 'messageActionPrizeStars', boost_peer: array|int|string, unclaimed: bool, stars: int, transaction_id: string, giveaway_msg_id: int}|array{_: 'messageActionStarGift', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, name_hidden: bool, saved: bool, converted: bool, upgraded: bool, refunded: bool, can_upgrade: bool, prepaid_upgrade: bool, upgrade_separate: bool, auction_acquired: bool, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}, convert_stars?: int, upgrade_msg_id?: int, upgrade_stars?: int, from_id?: array|int|string, peer?: array|int|string, saved_id?: int, prepaid_upgrade_hash?: string, gift_msg_id?: int, to_id?: array|int|string, gift_num?: int}|array{_: 'messageActionStarGiftUnique', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, upgrade: bool, transferred: bool, saved: bool, refunded: bool, prepaid_upgrade: bool, assigned: bool, from_offer: bool, craft: bool, name_hidden: bool, can_export_at?: int, transfer_stars?: int, from_id?: array|int|string, peer?: array|int|string, saved_id?: int, resale_amount?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, can_transfer_at?: int, can_resell_at?: int, drop_original_details_stars?: int, can_craft_at?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}|array{_: 'messageActionPaidMessagesRefunded', count: int, stars: int}|array{_: 'messageActionPaidMessagesPrice', broadcast_messages_allowed: bool, stars: int}|array{_: 'messageActionConferenceCall', missed: bool, active: bool, video: bool, call_id: int, duration?: int, other_participants?: list<array|int|string>}|array{_: 'messageActionTodoCompletions', completed: list<int>, incompleted: list<int>}|array{_: 'messageActionTodoAppendTasks', list: list<array{_: 'todoItem', title: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, id: int}>}|array{_: 'messageActionSuggestedPostApproval', rejected: bool, balance_too_low: bool, reject_comment?: string, schedule_date?: int, price?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}}|array{_: 'messageActionSuggestedPostSuccess', price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}}|array{_: 'messageActionSuggestedPostRefund', payer_initiated: bool}|array{_: 'messageActionGiftTon', currency: string, amount: int, crypto_currency: string, crypto_amount: int, transaction_id?: string}|array{_: 'messageActionSuggestBirthday', birthday: array{_: 'birthday', day: int, month: int, year?: int}}|array{_: 'messageActionStarGiftPurchaseOffer', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, accepted: bool, declined: bool, expires_at: int}|array{_: 'messageActionStarGiftPurchaseOfferDeclined', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, expired: bool}|array{_: 'messageActionNewCreatorPending', new_creator_id: int}|array{_: 'messageActionChangeCreator', new_creator_id: int}|array{_: 'messageActionNoForwardsToggle', prev_value: bool, new_value: bool}|array{_: 'messageActionNoForwardsRequest', prev_value: bool, new_value: bool, expired: bool}|array{_: 'messageActionPollAppendAnswer', answer: array{_: 'pollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, option: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, added_by?: array|int|string, date?: int}|array{_: 'inputPollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, media?: \danog\MadelineProto\EventHandler\Media|string|array}}|array{_: 'messageActionPollDeleteAnswer', answer: array{_: 'pollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, option: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, added_by?: array|int|string, date?: int}|array{_: 'inputPollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, media?: \danog\MadelineProto\EventHandler\Media|string|array}}|array{_: 'messageActionManagedBotCreated', bot_id: int}|array{_: 'messageActionChangeCommunity', community_id?: int}|array{_: 'messageActionChatJoinedViaCommunity', community_id: int}|array{_: 'messageActionGramTransfer', comment_encrypted: bool, amount: int, peer_address: string, transaction_id: string, comment?: string}|array{_: 'messageActionWalletTonConnectRequest', accepted: bool, declined: bool, session_id: int, expires: int, topic?: string, trace_id?: string, dapp_name?: string}|array{_: 'messageActionNoPaidMessagesTokenUsed'}, reactions?: array{_: 'messageReactions', min: bool, can_see_list: bool, reactions_as_tags: bool, results: list<array{_: 'reactionCount', chosen_order?: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}, count: int}>, recent_reactions?: list<array{_: 'messagePeerReaction', peer_id: array|int|string, big: bool, unread: bool, my: bool, date: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}}>, top_reactors?: list<array{_: 'messageReactor', top: bool, my: bool, anonymous: bool, peer_id?: array|int|string, count: int}>}, ttl_period?: int}>, chats: list<array|int|string>, users: list<array|int|string>}|array{_: 'messages.savedDialogsSlice', count: int, dialogs: list<array{_: 'savedDialog', peer: array|int|string, pinned: bool, top_message: int}|array{_: 'monoForumDialog', peer: array|int|string, unread_mark: bool, nopaid_messages_exception: bool, top_message: int, read_inbox_max_id: int, read_outbox_max_id: int, unread_count: int, unread_reactions_count: int, draft?: array{_: 'draftMessageEmpty', date?: int}|array{_: 'draftMessage', no_webpage: bool, invert_media: bool, reply_to?: array{_: 'inputReplyToMessage', reply_to_msg_id: int, top_msg_id?: int, reply_to_peer_id?: array|int|string, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, quote_offset?: int, monoforum_peer_id?: array|int|string, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, url: string, webpage_id: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, email: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, phone: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, name: string}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, user_id: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, date: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, type: array{_: 'inlineButtonTypeUrl', url: array}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: array, url: array, button_id: array}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: array, fwd_text?: array, url: array, bot?: array}|array{_: 'inlineButtonTypeWebView', url: array}|array{_: 'inlineButtonTypeCallback', requires_password: array, data: array}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: array, query: array, peer_types?: list<array>}|array{_: 'inlineButtonTypeUserProfile', user_id: array}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array}|array{_: 'inlineButtonTypeCopy', copy_text: array}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: array, bg_danger: array, bg_success: array, link: array}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}>}|array{_: 'inputReplyToStory', peer: array|int|string, story_id: int}|array{_: 'inputReplyToMonoForum', monoforum_peer_id: array|int|string}|array{_: 'inputReplyToEphemeralMessage', id: int}, message: string, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, media?: \danog\MadelineProto\EventHandler\Media|string|array, date: int, effect?: int, suggested_post?: array{_: 'suggestedPost', accepted: bool, rejected: bool, price?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, schedule_date?: int}, rich_message?: array{_: 'richMessage', rtl: bool, part: bool, blocks: list<array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, published_date: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, language: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: string}|array{_: 'pageBlockList', items: list<array{_: 'pageListItemText', text: array, checkbox: array, checked: array}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, collapsed: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array, credit: array}, spoiler: bool, photo_id: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array, credit: array}, autoplay: bool, loop: bool, spoiler: bool, video_id: int}|array{_: 'pageBlockCover', cover: array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array}|array{_: 'pageBlockSubtitle', text: array}|array{_: 'pageBlockAuthorDate', author: array, published_date: array}|array{_: 'pageBlockHeader', text: array}|array{_: 'pageBlockSubheader', text: array}|array{_: 'pageBlockParagraph', text: array}|array{_: 'pageBlockPreformatted', text: array, language: array}|array{_: 'pageBlockFooter', text: array}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: array}|array{_: 'pageBlockList', items: list<array>}|array{_: 'pageBlockBlockquote', text: array, caption: array, collapsed: array}|array{_: 'pageBlockPullquote', text: array, caption: array}|array{_: 'pageBlockPhoto', caption: array, spoiler: array, photo_id: array, url?: array, webpage_id?: array}|array{_: 'pageBlockVideo', caption: array, autoplay: array, loop: array, spoiler: array, video_id: array}|array{_: 'pageBlockEmbed', caption: array, full_width: array, allow_scrolling: array, url?: array, html?: array, poster_photo_id?: array, w?: array, h?: array}|array{_: 'pageBlockChannel', channel: array}|array{_: 'pageBlockAudio', caption: array, audio_id: array}|array{_: 'pageBlockKicker', text: array}|array{_: 'pageBlockTable', title: array, bordered: array, striped: array, compact: array, rows: list<array>}|array{_: 'pageBlockOrderedList', reversed: array, items: list<array>, start?: array, type?: array}|array{_: 'pageBlockRelatedArticles', title: array, articles: list<array>}|array{_: 'pageBlockMap', caption: array, geo: array, zoom: array, w: array, h: array}|array{_: 'pageBlockHeading1', text: array}|array{_: 'pageBlockHeading2', text: array}|array{_: 'pageBlockHeading3', text: array}|array{_: 'pageBlockHeading4', text: array}|array{_: 'pageBlockHeading5', text: array}|array{_: 'pageBlockHeading6', text: array}|array{_: 'pageBlockMath', source: array}|array{_: 'pageBlockThinking', text: array}|array{_: 'inputPageBlockMap', caption: array, geo: array, zoom: array, w: array, h: array}|array{_: 'pageBlockButtonRow', align_left: array, align_center: array, align_right: array, buttons: list<array>}|array{_: 'pageBlockDocument', caption: array, document_id: array}}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array, credit: array}, full_width: bool, allow_scrolling: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array, credit: array}, audio_id: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, bordered: bool, striped: bool, compact: bool, rows: list<array{_: 'pageTableRow', cells: list<array>}>}|array{_: 'pageBlockOrderedList', reversed: bool, items: list<array{_: 'pageListOrderedItemText', text: array, checkbox: array, checked: array, num?: array, value?: array, type?: array}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, articles: list<array{_: 'pageRelatedArticle', url: array, webpage_id: array, title?: array, description?: array, photo_id?: array, author?: array, published_date?: array}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockMath', source: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: array, long: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockButtonRow', align_left: bool, align_center: bool, align_right: bool, buttons: list<array{_: 'pageButton', text: array, type: array, style?: array}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array, credit: array}, document_id: int}>, photos: list<array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}}}>, messages: list<array{_: 'messageEmpty', id: int, peer_id?: array|int|string}|array{_: 'message', peer_id: array|int|string, out: bool, mentioned: bool, media_unread: bool, silent: bool, post: bool, from_scheduled: bool, legacy: bool, edit_hide: bool, pinned: bool, noforwards: bool, invert_media: bool, offline: bool, video_processing_pending: bool, paid_suggested_post_stars: bool, paid_suggested_post_ton: bool, id: int, from_id?: array|int|string, from_boosts_applied?: int, from_rank?: string, saved_peer_id?: array|int|string, fwd_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, via_bot_id?: int, via_business_bot_id?: int, guestchat_via_from?: array|int|string, reply_to?: array{_: 'messageReplyHeader', reply_to_scheduled: bool, forum_topic: bool, quote: bool, reply_to_ephemeral: bool, reply_to_msg_id?: int, reply_to_peer_id?: array|int|string, reply_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, reply_media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_to_top_id?: int, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, quote_offset?: int, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, url: string, webpage_id: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, email: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, phone: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, name: string}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, user_id: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, date: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, type: array{_: 'inlineButtonTypeUrl', url: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url: string, button_id: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: bool, fwd_text?: string, url: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url: string}|array{_: 'inlineButtonTypeCallback', requires_password: bool, data: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: bool, query: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: bool, bg_danger: bool, bg_success: bool, link: bool}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}>}|array{_: 'messageReplyStoryHeader', peer: array|int|string, story_id: int}, date: int, message: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_markup?: array, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, views?: int, forwards?: int, replies?: array{_: 'messageReplies', comments: bool, replies: int, replies_pts: int, recent_repliers?: list<array|int|string>, channel_id?: int, max_id?: int, read_max_id?: int}, edit_date?: int, post_author?: string, grouped_id?: int, reactions?: array{_: 'messageReactions', min: bool, can_see_list: bool, reactions_as_tags: bool, results: list<array{_: 'reactionCount', chosen_order?: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}, count: int}>, recent_reactions?: list<array{_: 'messagePeerReaction', peer_id: array|int|string, big: bool, unread: bool, my: bool, date: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}}>, top_reactors?: list<array{_: 'messageReactor', top: bool, my: bool, anonymous: bool, peer_id?: array|int|string, count: int}>}, restriction_reason?: list<array{_: 'restrictionReason', platform: string, reason: string, text: string}>, ttl_period?: int, quick_reply_shortcut_id?: int, effect?: int, factcheck?: array{_: 'factCheck', need_check: bool, country?: string, text?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}, hash: list<int|string>}, report_delivery_until_date?: int, paid_message_stars?: int, suggested_post?: array{_: 'suggestedPost', accepted: bool, rejected: bool, price?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, schedule_date?: int}, schedule_repeat_period?: int, summary_from_language?: string, rich_message?: array{_: 'richMessage', rtl: bool, part: bool, blocks: list<array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, published_date: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, language: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: string}|array{_: 'pageBlockList', items: list<array{_: 'pageListItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, checkbox: bool, checked: bool}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, collapsed: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, spoiler: bool, photo_id: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, autoplay: bool, loop: bool, spoiler: bool, video_id: int}|array{_: 'pageBlockCover', cover: array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, published_date: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, language: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: string}|array{_: 'pageBlockList', items: list<array{_: 'pageListItemText', text: array, checkbox: array, checked: array}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, collapsed: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array, credit: array}, spoiler: bool, photo_id: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array, credit: array}, autoplay: bool, loop: bool, spoiler: bool, video_id: int}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array, credit: array}, full_width: bool, allow_scrolling: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array, credit: array}, audio_id: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, bordered: bool, striped: bool, compact: bool, rows: list<array{_: 'pageTableRow', cells: list<array>}>}|array{_: 'pageBlockOrderedList', reversed: bool, items: list<array{_: 'pageListOrderedItemText', text: array, checkbox: array, checked: array, num?: array, value?: array, type?: array}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, articles: list<array{_: 'pageRelatedArticle', url: array, webpage_id: array, title?: array, description?: array, photo_id?: array, author?: array, published_date?: array}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockMath', source: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: array, long: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockButtonRow', align_left: bool, align_center: bool, align_right: bool, buttons: list<array{_: 'pageButton', text: array, type: array, style?: array}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array, credit: array}, document_id: int}}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, full_width: bool, allow_scrolling: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, audio_id: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, bordered: bool, striped: bool, compact: bool, rows: list<array{_: 'pageTableRow', cells: list<array{_: 'pageTableCell', header: array, align_center: array, align_right: array, valign_middle: array, valign_bottom: array, text?: array, colspan?: array, rowspan?: array}>}>}|array{_: 'pageBlockOrderedList', reversed: bool, items: list<array{_: 'pageListOrderedItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, checkbox: bool, checked: bool, num?: string, value?: int, type?: string}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, articles: list<array{_: 'pageRelatedArticle', url: string, webpage_id: int, title?: string, description?: string, photo_id?: int, author?: string, published_date?: int}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash: int, accuracy_radius?: int}, zoom: int, w: int, h: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockMath', source: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, geo: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: float, long: float, accuracy_radius?: int}, zoom: int, w: int, h: int}|array{_: 'pageBlockButtonRow', align_left: bool, align_center: bool, align_right: bool, buttons: list<array{_: 'pageButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, type: array{_: 'inlineButtonTypeUrl', url: array}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: array, url: array, button_id: array}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: array, fwd_text?: array, url: array, bot?: array}|array{_: 'inlineButtonTypeWebView', url: array}|array{_: 'inlineButtonTypeCallback', requires_password: array, data: array}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: array, query: array, peer_types?: list<array>}|array{_: 'inlineButtonTypeUserProfile', user_id: array}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array}|array{_: 'inlineButtonTypeCopy', copy_text: array}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: array, bg_danger: array, bg_success: array, link: array}}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, document_id: int}>, photos: list<array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}}|array{_: 'messageService', peer_id: array|int|string, out: bool, mentioned: bool, media_unread: bool, reactions_are_possible: bool, silent: bool, post: bool, legacy: bool, id: int, from_id?: array|int|string, saved_peer_id?: array|int|string, reply_to?: array{_: 'messageReplyHeader', reply_to_scheduled: bool, forum_topic: bool, quote: bool, reply_to_ephemeral: bool, reply_to_msg_id?: int, reply_to_peer_id?: array|int|string, reply_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, reply_media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_to_top_id?: int, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, quote_offset?: int, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, url: string, webpage_id: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, email: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, phone: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, name: string}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, user_id: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, date: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, type: array{_: 'inlineButtonTypeUrl', url: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url: string, button_id: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: bool, fwd_text?: string, url: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url: string}|array{_: 'inlineButtonTypeCallback', requires_password: bool, data: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: bool, query: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: bool, bg_danger: bool, bg_success: bool, link: bool}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}>}|array{_: 'messageReplyStoryHeader', peer: array|int|string, story_id: int}, date: int, action: array{_: 'messageActionEmpty'}|array{_: 'messageActionChatCreate', title: string, users: list<int>}|array{_: 'messageActionChatEditTitle', title: string}|array{_: 'messageActionChatEditPhoto', photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'messageActionChatDeletePhoto'}|array{_: 'messageActionChatAddUser', users: list<int>}|array{_: 'messageActionChatDeleteUser', user_id: int}|array{_: 'messageActionChatJoinedByLink', inviter_id: int}|array{_: 'messageActionChannelCreate', title: string}|array{_: 'messageActionChatMigrateTo', channel_id: int}|array{_: 'messageActionChannelMigrateFrom', title: string, chat_id: array|int|string}|array{_: 'messageActionPinMessage'}|array{_: 'messageActionHistoryClear'}|array{_: 'messageActionGameScore', game_id: int, score: int}|array{_: 'messageActionPaymentSentMe', charge: array{_: 'paymentCharge', id: string, provider_charge_id: string}, recurring_init: bool, recurring_used: bool, currency: string, total_amount: int, payload: string, info?: array{_: 'paymentRequestedInfo', name?: string, phone?: string, email?: string, shipping_address?: array{_: 'postAddress', street_line1: string, street_line2: string, city: string, state: string, country_iso2: string, post_code: string}}, shipping_option_id?: string, subscription_until_date?: int}|array{_: 'messageActionPaymentSent', recurring_init: bool, recurring_used: bool, currency: string, total_amount: int, invoice_slug?: string, subscription_until_date?: int}|array{_: 'messageActionPhoneCall', video: bool, call_id: int, reason?: array{_: 'phoneCallDiscardReasonMissed'}|array{_: 'phoneCallDiscardReasonDisconnect'}|array{_: 'phoneCallDiscardReasonHangup'}|array{_: 'phoneCallDiscardReasonBusy'}|array{_: 'phoneCallDiscardReasonMigrateConferenceCall', slug: string}, duration?: int}|array{_: 'messageActionScreenshotTaken'}|array{_: 'messageActionCustomAction', message: string}|array{_: 'messageActionBotAllowed', attach_menu: bool, from_request: bool, domain?: string, app?: array{_: 'botAppNotModified'}|array{_: 'botApp', id: int, access_hash: int, short_name: string, title: string, description: string, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, hash: list<int|string>}}|array{_: 'messageActionSecureValuesSentMe', credentials: array{_: 'secureCredentialsEncrypted', data: string, hash: string, secret: string}, values: list<array{_: 'secureValue', type: array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}, data?: array{_: 'secureData', data: string, data_hash: string, secret: string}, front_side?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, reverse_side?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, selfie?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, translation?: list<array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}>, files?: list<array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}>, plain_data?: array{_: 'securePlainPhone', phone: string}|array{_: 'securePlainEmail', email: string}, hash: string}>}|array{_: 'messageActionSecureValuesSent', types: list<array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}>}|array{_: 'messageActionContactSignUp'}|array{_: 'messageActionGeoProximityReached', from_id: array|int|string, to_id: array|int|string, distance: int}|array{_: 'messageActionGroupCall', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, duration?: int}|array{_: 'messageActionInviteToGroupCall', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, users: list<int>}|array{_: 'messageActionSetMessagesTTL', period: int, auto_setting_from?: int}|array{_: 'messageActionGroupCallScheduled', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, schedule_date: int}|array{_: 'messageActionSetChatTheme', theme: array{_: 'chatTheme', emoticon: string}|array{_: 'chatThemeUniqueGift', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: array, edge_color: array, text_color: array}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array, crafted: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array, name: array, backdrop_id: array, center_color: array, edge_color: array, pattern_color: array, text_color: array}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array, sender_id?: array, date: array, message?: array}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: array, nanos: array}|array{_: 'starsTonAmount', amount: array}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: array, background_emoji_id?: array}|array{_: 'peerColorCollectible', collectible_id: array, gift_emoji_id: array, background_emoji_id: array, accent_color: array, colors: list<array>, dark_accent_color?: array, dark_colors?: list<array>}|array{_: 'inputPeerColorCollectible', collectible_id: array}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, theme_settings: list<array{_: 'themeSettings', base_theme: array{_: 'baseThemeClassic'}|array{_: 'baseThemeDay'}|array{_: 'baseThemeNight'}|array{_: 'baseThemeTinted'}|array{_: 'baseThemeArctic'}, message_colors_animated: bool, accent_color: int, outbox_accent_color?: int, message_colors?: list<int>, wallpaper?: array{_: 'wallPaper', id: array, creator: array, default: array, pattern: array, dark: array, access_hash: array, slug: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, settings?: array}|array{_: 'wallPaperNoFile', id: array, default: array, dark: array, settings?: array}}>}}|array{_: 'messageActionChatJoinedByRequest'}|array{_: 'messageActionWebViewDataSentMe', text: string, data: string}|array{_: 'messageActionWebViewDataSent', text: string}|array{_: 'messageActionGiftPremium', currency: string, amount: int, days: int, crypto_currency?: string, crypto_amount?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}|array{_: 'messageActionTopicCreate', title_missing: bool, title: string, icon_color: int, icon_emoji_id?: int}|array{_: 'messageActionTopicEdit', title?: string, icon_emoji_id?: int, closed?: bool, hidden?: bool}|array{_: 'messageActionSuggestProfilePhoto', photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'messageActionRequestedPeer', button_id: int, peers: list<array|int|string>}|array{_: 'messageActionSetChatWallPaper', wallpaper: array{_: 'wallPaper', id: int, creator: bool, default: bool, pattern: bool, dark: bool, access_hash: int, slug: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, settings?: array{_: 'wallPaperSettings', blur: bool, motion: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string}}|array{_: 'wallPaperNoFile', id: int, default: bool, dark: bool, settings?: array{_: 'wallPaperSettings', blur: bool, motion: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string}}, same: bool, for_both: bool}|array{_: 'messageActionGiftCode', via_giveaway: bool, unclaimed: bool, boost_peer?: array|int|string, days: int, slug: string, currency?: string, amount?: int, crypto_currency?: string, crypto_amount?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}|array{_: 'messageActionGiveawayLaunch', stars?: int}|array{_: 'messageActionGiveawayResults', stars: bool, winners_count: int, unclaimed_count: int}|array{_: 'messageActionBoostApply', boosts: int}|array{_: 'messageActionRequestedPeerSentMe', button_id: int, peers: list<array{_: 'requestedPeerUser', user_id: int, first_name?: string, last_name?: string, username?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'requestedPeerChat', chat_id: array|int|string, title?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'requestedPeerChannel', channel_id: int, title?: string, username?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}>}|array{_: 'messageActionPaymentRefunded', peer: array|int|string, charge: array{_: 'paymentCharge', id: string, provider_charge_id: string}, currency: string, total_amount: int, payload?: string}|array{_: 'messageActionGiftStars', currency: string, amount: int, stars: int, crypto_currency?: string, crypto_amount?: int, transaction_id?: string}|array{_: 'messageActionPrizeStars', boost_peer: array|int|string, unclaimed: bool, stars: int, transaction_id: string, giveaway_msg_id: int}|array{_: 'messageActionStarGift', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, name_hidden: bool, saved: bool, converted: bool, upgraded: bool, refunded: bool, can_upgrade: bool, prepaid_upgrade: bool, upgrade_separate: bool, auction_acquired: bool, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}, convert_stars?: int, upgrade_msg_id?: int, upgrade_stars?: int, from_id?: array|int|string, peer?: array|int|string, saved_id?: int, prepaid_upgrade_hash?: string, gift_msg_id?: int, to_id?: array|int|string, gift_num?: int}|array{_: 'messageActionStarGiftUnique', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, upgrade: bool, transferred: bool, saved: bool, refunded: bool, prepaid_upgrade: bool, assigned: bool, from_offer: bool, craft: bool, name_hidden: bool, can_export_at?: int, transfer_stars?: int, from_id?: array|int|string, peer?: array|int|string, saved_id?: int, resale_amount?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, can_transfer_at?: int, can_resell_at?: int, drop_original_details_stars?: int, can_craft_at?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}|array{_: 'messageActionPaidMessagesRefunded', count: int, stars: int}|array{_: 'messageActionPaidMessagesPrice', broadcast_messages_allowed: bool, stars: int}|array{_: 'messageActionConferenceCall', missed: bool, active: bool, video: bool, call_id: int, duration?: int, other_participants?: list<array|int|string>}|array{_: 'messageActionTodoCompletions', completed: list<int>, incompleted: list<int>}|array{_: 'messageActionTodoAppendTasks', list: list<array{_: 'todoItem', title: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, id: int}>}|array{_: 'messageActionSuggestedPostApproval', rejected: bool, balance_too_low: bool, reject_comment?: string, schedule_date?: int, price?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}}|array{_: 'messageActionSuggestedPostSuccess', price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}}|array{_: 'messageActionSuggestedPostRefund', payer_initiated: bool}|array{_: 'messageActionGiftTon', currency: string, amount: int, crypto_currency: string, crypto_amount: int, transaction_id?: string}|array{_: 'messageActionSuggestBirthday', birthday: array{_: 'birthday', day: int, month: int, year?: int}}|array{_: 'messageActionStarGiftPurchaseOffer', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, accepted: bool, declined: bool, expires_at: int}|array{_: 'messageActionStarGiftPurchaseOfferDeclined', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, expired: bool}|array{_: 'messageActionNewCreatorPending', new_creator_id: int}|array{_: 'messageActionChangeCreator', new_creator_id: int}|array{_: 'messageActionNoForwardsToggle', prev_value: bool, new_value: bool}|array{_: 'messageActionNoForwardsRequest', prev_value: bool, new_value: bool, expired: bool}|array{_: 'messageActionPollAppendAnswer', answer: array{_: 'pollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, option: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, added_by?: array|int|string, date?: int}|array{_: 'inputPollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, media?: \danog\MadelineProto\EventHandler\Media|string|array}}|array{_: 'messageActionPollDeleteAnswer', answer: array{_: 'pollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, option: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, added_by?: array|int|string, date?: int}|array{_: 'inputPollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, media?: \danog\MadelineProto\EventHandler\Media|string|array}}|array{_: 'messageActionManagedBotCreated', bot_id: int}|array{_: 'messageActionChangeCommunity', community_id?: int}|array{_: 'messageActionChatJoinedViaCommunity', community_id: int}|array{_: 'messageActionGramTransfer', comment_encrypted: bool, amount: int, peer_address: string, transaction_id: string, comment?: string}|array{_: 'messageActionWalletTonConnectRequest', accepted: bool, declined: bool, session_id: int, expires: int, topic?: string, trace_id?: string, dapp_name?: string}|array{_: 'messageActionNoPaidMessagesTokenUsed'}, reactions?: array{_: 'messageReactions', min: bool, can_see_list: bool, reactions_as_tags: bool, results: list<array{_: 'reactionCount', chosen_order?: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}, count: int}>, recent_reactions?: list<array{_: 'messagePeerReaction', peer_id: array|int|string, big: bool, unread: bool, my: bool, date: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}}>, top_reactors?: list<array{_: 'messageReactor', top: bool, my: bool, anonymous: bool, peer_id?: array|int|string, count: int}>}, ttl_period?: int}>, chats: list<array|int|string>, users: list<array|int|string>}|array{_: 'messages.savedDialogsNotModified', count: int} @see https://docs.madelineproto.xyz/API_docs/types/messages.SavedDialogs.html
     * @psalm-impure
     */
    public function getSavedDialogsByID(array|int|string|null $parent_peer = null, array $ids = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Mark messages as read in a [monoforum topic Â»](https://core.telegram.org/api/monoforum).
     *
     * @param array|int|string $parent_peer ID of the monoforum group. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param array|int|string $peer ID of the topic. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $max_id If a positive value is passed, only messages with identifiers less or equal than the given one will be read.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function readSavedHistory(array|int|string|null $parent_peer = null, array|int|string|null $peer = null, int|null $max_id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Mark one or more items of a [todo list Â»](https://core.telegram.org/api/todo) as completed or not completed.
     *
     * @param array|int|string $peer Peer where the todo list was posted. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $msg_id ID of the message with the todo list.
     * @param list<int>|array<never, never> $completed Items to mark as completed.
     * @param list<int>|array<never, never> $incompleted Items to mark as not completed.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function toggleTodoCompleted(array|int|string|null $peer = null, int|null $msg_id = 0, array $completed = [], array $incompleted = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Appends one or more items to a [todo list Â»](https://core.telegram.org/api/todo).
     *
     * @param array|int|string $peer Peer where the todo list was posted. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $msg_id ID of the message with the todo list.
     * @param list<array{_: 'todoItem', title: array{_: 'textWithEntities', text?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>}, id?: int}>|array<never, never> $list Array of Items to append. @see https://docs.madelineproto.xyz/API_docs/types/TodoItem.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function appendTodoList(array|int|string|null $peer = null, int|null $msg_id = 0, array $list = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Approve or reject a [suggested post Â»](https://core.telegram.org/api/suggested-posts).
     *
     * @param bool $reject Reject the suggested post.
     * @param array|int|string $peer Both for users and channels, must contain the ID of the [direct messages monoforum Â»](https://core.telegram.org/api/monoforum) (for channels, the topic ID is extracted automatically from the `msg_id`). @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $msg_id ID of the suggestion message.
     * @param int $schedule_date Custom scheduling date.
     * @param string $reject_comment Optional comment for rejections (can only be used if `reject` is set).
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function toggleSuggestedPostApproval(bool|null $reject = null, array|int|string|null $peer = null, int|null $msg_id = 0, int|null $schedule_date = null, string|null $reject_comment = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param array|int|string $peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param string $q 
     * @param int $offset_date 
     * @param int $offset_id 
     * @param int $offset_topic 
     * @param int $limit 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.forumTopics', order_by_create_date: bool, count: int, topics: list<array{_: 'forumTopicDeleted', id: int}|array{_: 'forumTopic', peer: array|int|string, from_id: array|int|string, notify_settings: array{_: 'peerNotifySettings', show_previews?: bool, silent?: bool, mute_until?: int, ios_sound?: array{_: 'notificationSoundDefault'}|array{_: 'notificationSoundNone'}|array{_: 'notificationSoundLocal', title: string, data: string}|array{_: 'notificationSoundRingtone', id: int}, android_sound?: array{_: 'notificationSoundDefault'}|array{_: 'notificationSoundNone'}|array{_: 'notificationSoundLocal', title: string, data: string}|array{_: 'notificationSoundRingtone', id: int}, other_sound?: array{_: 'notificationSoundDefault'}|array{_: 'notificationSoundNone'}|array{_: 'notificationSoundLocal', title: string, data: string}|array{_: 'notificationSoundRingtone', id: int}, stories_muted?: bool, stories_hide_sender?: bool, stories_ios_sound?: array{_: 'notificationSoundDefault'}|array{_: 'notificationSoundNone'}|array{_: 'notificationSoundLocal', title: string, data: string}|array{_: 'notificationSoundRingtone', id: int}, stories_android_sound?: array{_: 'notificationSoundDefault'}|array{_: 'notificationSoundNone'}|array{_: 'notificationSoundLocal', title: string, data: string}|array{_: 'notificationSoundRingtone', id: int}, stories_other_sound?: array{_: 'notificationSoundDefault'}|array{_: 'notificationSoundNone'}|array{_: 'notificationSoundLocal', title: string, data: string}|array{_: 'notificationSoundRingtone', id: int}}, my: bool, closed: bool, pinned: bool, short: bool, hidden: bool, title_missing: bool, id: int, date: int, title: string, icon_color: int, icon_emoji_id?: int, top_message: int, read_inbox_max_id: int, read_outbox_max_id: int, unread_count: int, unread_mentions_count: int, unread_reactions_count: int, unread_poll_votes_count: int, draft?: array{_: 'draftMessageEmpty', date?: int}|array{_: 'draftMessage', no_webpage: bool, invert_media: bool, reply_to?: array{_: 'inputReplyToMessage', reply_to_msg_id: int, top_msg_id?: int, reply_to_peer_id?: array|int|string, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, quote_offset?: int, monoforum_peer_id?: array|int|string, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, url: string, webpage_id: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, email: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, phone: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, name: string}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, user_id: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, date: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, type: array{_: 'inlineButtonTypeUrl', url: array}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: array, url: array, button_id: array}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: array, fwd_text?: array, url: array, bot?: array}|array{_: 'inlineButtonTypeWebView', url: array}|array{_: 'inlineButtonTypeCallback', requires_password: array, data: array}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: array, query: array, peer_types?: list<array>}|array{_: 'inlineButtonTypeUserProfile', user_id: array}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array}|array{_: 'inlineButtonTypeCopy', copy_text: array}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: array, bg_danger: array, bg_success: array, link: array}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}>}|array{_: 'inputReplyToStory', peer: array|int|string, story_id: int}|array{_: 'inputReplyToMonoForum', monoforum_peer_id: array|int|string}|array{_: 'inputReplyToEphemeralMessage', id: int}, message: string, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, media?: \danog\MadelineProto\EventHandler\Media|string|array, date: int, effect?: int, suggested_post?: array{_: 'suggestedPost', accepted: bool, rejected: bool, price?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, schedule_date?: int}, rich_message?: array{_: 'richMessage', rtl: bool, part: bool, blocks: list<array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, published_date: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, language: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: string}|array{_: 'pageBlockList', items: list<array{_: 'pageListItemText', text: array, checkbox: array, checked: array}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, collapsed: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array, credit: array}, spoiler: bool, photo_id: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array, credit: array}, autoplay: bool, loop: bool, spoiler: bool, video_id: int}|array{_: 'pageBlockCover', cover: array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array}|array{_: 'pageBlockSubtitle', text: array}|array{_: 'pageBlockAuthorDate', author: array, published_date: array}|array{_: 'pageBlockHeader', text: array}|array{_: 'pageBlockSubheader', text: array}|array{_: 'pageBlockParagraph', text: array}|array{_: 'pageBlockPreformatted', text: array, language: array}|array{_: 'pageBlockFooter', text: array}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: array}|array{_: 'pageBlockList', items: list<array>}|array{_: 'pageBlockBlockquote', text: array, caption: array, collapsed: array}|array{_: 'pageBlockPullquote', text: array, caption: array}|array{_: 'pageBlockPhoto', caption: array, spoiler: array, photo_id: array, url?: array, webpage_id?: array}|array{_: 'pageBlockVideo', caption: array, autoplay: array, loop: array, spoiler: array, video_id: array}|array{_: 'pageBlockEmbed', caption: array, full_width: array, allow_scrolling: array, url?: array, html?: array, poster_photo_id?: array, w?: array, h?: array}|array{_: 'pageBlockChannel', channel: array}|array{_: 'pageBlockAudio', caption: array, audio_id: array}|array{_: 'pageBlockKicker', text: array}|array{_: 'pageBlockTable', title: array, bordered: array, striped: array, compact: array, rows: list<array>}|array{_: 'pageBlockOrderedList', reversed: array, items: list<array>, start?: array, type?: array}|array{_: 'pageBlockRelatedArticles', title: array, articles: list<array>}|array{_: 'pageBlockMap', caption: array, geo: array, zoom: array, w: array, h: array}|array{_: 'pageBlockHeading1', text: array}|array{_: 'pageBlockHeading2', text: array}|array{_: 'pageBlockHeading3', text: array}|array{_: 'pageBlockHeading4', text: array}|array{_: 'pageBlockHeading5', text: array}|array{_: 'pageBlockHeading6', text: array}|array{_: 'pageBlockMath', source: array}|array{_: 'pageBlockThinking', text: array}|array{_: 'inputPageBlockMap', caption: array, geo: array, zoom: array, w: array, h: array}|array{_: 'pageBlockButtonRow', align_left: array, align_center: array, align_right: array, buttons: list<array>}|array{_: 'pageBlockDocument', caption: array, document_id: array}}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array, credit: array}, full_width: bool, allow_scrolling: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array, credit: array}, audio_id: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, bordered: bool, striped: bool, compact: bool, rows: list<array{_: 'pageTableRow', cells: list<array>}>}|array{_: 'pageBlockOrderedList', reversed: bool, items: list<array{_: 'pageListOrderedItemText', text: array, checkbox: array, checked: array, num?: array, value?: array, type?: array}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, articles: list<array{_: 'pageRelatedArticle', url: array, webpage_id: array, title?: array, description?: array, photo_id?: array, author?: array, published_date?: array}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockMath', source: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: array, long: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockButtonRow', align_left: bool, align_center: bool, align_right: bool, buttons: list<array{_: 'pageButton', text: array, type: array, style?: array}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array, credit: array}, document_id: int}>, photos: list<array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}}}>, messages: list<array{_: 'messageEmpty', id: int, peer_id?: array|int|string}|array{_: 'message', peer_id: array|int|string, out: bool, mentioned: bool, media_unread: bool, silent: bool, post: bool, from_scheduled: bool, legacy: bool, edit_hide: bool, pinned: bool, noforwards: bool, invert_media: bool, offline: bool, video_processing_pending: bool, paid_suggested_post_stars: bool, paid_suggested_post_ton: bool, id: int, from_id?: array|int|string, from_boosts_applied?: int, from_rank?: string, saved_peer_id?: array|int|string, fwd_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, via_bot_id?: int, via_business_bot_id?: int, guestchat_via_from?: array|int|string, reply_to?: array{_: 'messageReplyHeader', reply_to_scheduled: bool, forum_topic: bool, quote: bool, reply_to_ephemeral: bool, reply_to_msg_id?: int, reply_to_peer_id?: array|int|string, reply_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, reply_media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_to_top_id?: int, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, quote_offset?: int, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, url: string, webpage_id: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, email: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, phone: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, name: string}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, user_id: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, date: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, type: array{_: 'inlineButtonTypeUrl', url: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url: string, button_id: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: bool, fwd_text?: string, url: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url: string}|array{_: 'inlineButtonTypeCallback', requires_password: bool, data: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: bool, query: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: bool, bg_danger: bool, bg_success: bool, link: bool}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}>}|array{_: 'messageReplyStoryHeader', peer: array|int|string, story_id: int}, date: int, message: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_markup?: array, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, views?: int, forwards?: int, replies?: array{_: 'messageReplies', comments: bool, replies: int, replies_pts: int, recent_repliers?: list<array|int|string>, channel_id?: int, max_id?: int, read_max_id?: int}, edit_date?: int, post_author?: string, grouped_id?: int, reactions?: array{_: 'messageReactions', min: bool, can_see_list: bool, reactions_as_tags: bool, results: list<array{_: 'reactionCount', chosen_order?: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}, count: int}>, recent_reactions?: list<array{_: 'messagePeerReaction', peer_id: array|int|string, big: bool, unread: bool, my: bool, date: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}}>, top_reactors?: list<array{_: 'messageReactor', top: bool, my: bool, anonymous: bool, peer_id?: array|int|string, count: int}>}, restriction_reason?: list<array{_: 'restrictionReason', platform: string, reason: string, text: string}>, ttl_period?: int, quick_reply_shortcut_id?: int, effect?: int, factcheck?: array{_: 'factCheck', need_check: bool, country?: string, text?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}, hash: list<int|string>}, report_delivery_until_date?: int, paid_message_stars?: int, suggested_post?: array{_: 'suggestedPost', accepted: bool, rejected: bool, price?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, schedule_date?: int}, schedule_repeat_period?: int, summary_from_language?: string, rich_message?: array{_: 'richMessage', rtl: bool, part: bool, blocks: list<array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, published_date: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, language: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: string}|array{_: 'pageBlockList', items: list<array{_: 'pageListItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, checkbox: bool, checked: bool}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, collapsed: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, spoiler: bool, photo_id: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, autoplay: bool, loop: bool, spoiler: bool, video_id: int}|array{_: 'pageBlockCover', cover: array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, published_date: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, language: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: string}|array{_: 'pageBlockList', items: list<array{_: 'pageListItemText', text: array, checkbox: array, checked: array}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, collapsed: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array, credit: array}, spoiler: bool, photo_id: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array, credit: array}, autoplay: bool, loop: bool, spoiler: bool, video_id: int}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array, credit: array}, full_width: bool, allow_scrolling: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array, credit: array}, audio_id: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, bordered: bool, striped: bool, compact: bool, rows: list<array{_: 'pageTableRow', cells: list<array>}>}|array{_: 'pageBlockOrderedList', reversed: bool, items: list<array{_: 'pageListOrderedItemText', text: array, checkbox: array, checked: array, num?: array, value?: array, type?: array}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, articles: list<array{_: 'pageRelatedArticle', url: array, webpage_id: array, title?: array, description?: array, photo_id?: array, author?: array, published_date?: array}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockMath', source: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: array, long: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockButtonRow', align_left: bool, align_center: bool, align_right: bool, buttons: list<array{_: 'pageButton', text: array, type: array, style?: array}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array, credit: array}, document_id: int}}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, full_width: bool, allow_scrolling: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, audio_id: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, bordered: bool, striped: bool, compact: bool, rows: list<array{_: 'pageTableRow', cells: list<array{_: 'pageTableCell', header: array, align_center: array, align_right: array, valign_middle: array, valign_bottom: array, text?: array, colspan?: array, rowspan?: array}>}>}|array{_: 'pageBlockOrderedList', reversed: bool, items: list<array{_: 'pageListOrderedItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, checkbox: bool, checked: bool, num?: string, value?: int, type?: string}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, articles: list<array{_: 'pageRelatedArticle', url: string, webpage_id: int, title?: string, description?: string, photo_id?: int, author?: string, published_date?: int}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash: int, accuracy_radius?: int}, zoom: int, w: int, h: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockMath', source: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, geo: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: float, long: float, accuracy_radius?: int}, zoom: int, w: int, h: int}|array{_: 'pageBlockButtonRow', align_left: bool, align_center: bool, align_right: bool, buttons: list<array{_: 'pageButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, type: array{_: 'inlineButtonTypeUrl', url: array}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: array, url: array, button_id: array}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: array, fwd_text?: array, url: array, bot?: array}|array{_: 'inlineButtonTypeWebView', url: array}|array{_: 'inlineButtonTypeCallback', requires_password: array, data: array}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: array, query: array, peer_types?: list<array>}|array{_: 'inlineButtonTypeUserProfile', user_id: array}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array}|array{_: 'inlineButtonTypeCopy', copy_text: array}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: array, bg_danger: array, bg_success: array, link: array}}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, document_id: int}>, photos: list<array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}}|array{_: 'messageService', peer_id: array|int|string, out: bool, mentioned: bool, media_unread: bool, reactions_are_possible: bool, silent: bool, post: bool, legacy: bool, id: int, from_id?: array|int|string, saved_peer_id?: array|int|string, reply_to?: array{_: 'messageReplyHeader', reply_to_scheduled: bool, forum_topic: bool, quote: bool, reply_to_ephemeral: bool, reply_to_msg_id?: int, reply_to_peer_id?: array|int|string, reply_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, reply_media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_to_top_id?: int, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, quote_offset?: int, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, url: string, webpage_id: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, email: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, phone: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, name: string}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, user_id: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, date: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, type: array{_: 'inlineButtonTypeUrl', url: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url: string, button_id: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: bool, fwd_text?: string, url: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url: string}|array{_: 'inlineButtonTypeCallback', requires_password: bool, data: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: bool, query: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: bool, bg_danger: bool, bg_success: bool, link: bool}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}>}|array{_: 'messageReplyStoryHeader', peer: array|int|string, story_id: int}, date: int, action: array{_: 'messageActionEmpty'}|array{_: 'messageActionChatCreate', title: string, users: list<int>}|array{_: 'messageActionChatEditTitle', title: string}|array{_: 'messageActionChatEditPhoto', photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'messageActionChatDeletePhoto'}|array{_: 'messageActionChatAddUser', users: list<int>}|array{_: 'messageActionChatDeleteUser', user_id: int}|array{_: 'messageActionChatJoinedByLink', inviter_id: int}|array{_: 'messageActionChannelCreate', title: string}|array{_: 'messageActionChatMigrateTo', channel_id: int}|array{_: 'messageActionChannelMigrateFrom', title: string, chat_id: array|int|string}|array{_: 'messageActionPinMessage'}|array{_: 'messageActionHistoryClear'}|array{_: 'messageActionGameScore', game_id: int, score: int}|array{_: 'messageActionPaymentSentMe', charge: array{_: 'paymentCharge', id: string, provider_charge_id: string}, recurring_init: bool, recurring_used: bool, currency: string, total_amount: int, payload: string, info?: array{_: 'paymentRequestedInfo', name?: string, phone?: string, email?: string, shipping_address?: array{_: 'postAddress', street_line1: string, street_line2: string, city: string, state: string, country_iso2: string, post_code: string}}, shipping_option_id?: string, subscription_until_date?: int}|array{_: 'messageActionPaymentSent', recurring_init: bool, recurring_used: bool, currency: string, total_amount: int, invoice_slug?: string, subscription_until_date?: int}|array{_: 'messageActionPhoneCall', video: bool, call_id: int, reason?: array{_: 'phoneCallDiscardReasonMissed'}|array{_: 'phoneCallDiscardReasonDisconnect'}|array{_: 'phoneCallDiscardReasonHangup'}|array{_: 'phoneCallDiscardReasonBusy'}|array{_: 'phoneCallDiscardReasonMigrateConferenceCall', slug: string}, duration?: int}|array{_: 'messageActionScreenshotTaken'}|array{_: 'messageActionCustomAction', message: string}|array{_: 'messageActionBotAllowed', attach_menu: bool, from_request: bool, domain?: string, app?: array{_: 'botAppNotModified'}|array{_: 'botApp', id: int, access_hash: int, short_name: string, title: string, description: string, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, hash: list<int|string>}}|array{_: 'messageActionSecureValuesSentMe', credentials: array{_: 'secureCredentialsEncrypted', data: string, hash: string, secret: string}, values: list<array{_: 'secureValue', type: array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}, data?: array{_: 'secureData', data: string, data_hash: string, secret: string}, front_side?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, reverse_side?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, selfie?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, translation?: list<array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}>, files?: list<array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}>, plain_data?: array{_: 'securePlainPhone', phone: string}|array{_: 'securePlainEmail', email: string}, hash: string}>}|array{_: 'messageActionSecureValuesSent', types: list<array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}>}|array{_: 'messageActionContactSignUp'}|array{_: 'messageActionGeoProximityReached', from_id: array|int|string, to_id: array|int|string, distance: int}|array{_: 'messageActionGroupCall', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, duration?: int}|array{_: 'messageActionInviteToGroupCall', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, users: list<int>}|array{_: 'messageActionSetMessagesTTL', period: int, auto_setting_from?: int}|array{_: 'messageActionGroupCallScheduled', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, schedule_date: int}|array{_: 'messageActionSetChatTheme', theme: array{_: 'chatTheme', emoticon: string}|array{_: 'chatThemeUniqueGift', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: array, edge_color: array, text_color: array}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array, crafted: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array, name: array, backdrop_id: array, center_color: array, edge_color: array, pattern_color: array, text_color: array}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array, sender_id?: array, date: array, message?: array}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: array, nanos: array}|array{_: 'starsTonAmount', amount: array}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: array, background_emoji_id?: array}|array{_: 'peerColorCollectible', collectible_id: array, gift_emoji_id: array, background_emoji_id: array, accent_color: array, colors: list<array>, dark_accent_color?: array, dark_colors?: list<array>}|array{_: 'inputPeerColorCollectible', collectible_id: array}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, theme_settings: list<array{_: 'themeSettings', base_theme: array{_: 'baseThemeClassic'}|array{_: 'baseThemeDay'}|array{_: 'baseThemeNight'}|array{_: 'baseThemeTinted'}|array{_: 'baseThemeArctic'}, message_colors_animated: bool, accent_color: int, outbox_accent_color?: int, message_colors?: list<int>, wallpaper?: array{_: 'wallPaper', id: array, creator: array, default: array, pattern: array, dark: array, access_hash: array, slug: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, settings?: array}|array{_: 'wallPaperNoFile', id: array, default: array, dark: array, settings?: array}}>}}|array{_: 'messageActionChatJoinedByRequest'}|array{_: 'messageActionWebViewDataSentMe', text: string, data: string}|array{_: 'messageActionWebViewDataSent', text: string}|array{_: 'messageActionGiftPremium', currency: string, amount: int, days: int, crypto_currency?: string, crypto_amount?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}|array{_: 'messageActionTopicCreate', title_missing: bool, title: string, icon_color: int, icon_emoji_id?: int}|array{_: 'messageActionTopicEdit', title?: string, icon_emoji_id?: int, closed?: bool, hidden?: bool}|array{_: 'messageActionSuggestProfilePhoto', photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'messageActionRequestedPeer', button_id: int, peers: list<array|int|string>}|array{_: 'messageActionSetChatWallPaper', wallpaper: array{_: 'wallPaper', id: int, creator: bool, default: bool, pattern: bool, dark: bool, access_hash: int, slug: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, settings?: array{_: 'wallPaperSettings', blur: bool, motion: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string}}|array{_: 'wallPaperNoFile', id: int, default: bool, dark: bool, settings?: array{_: 'wallPaperSettings', blur: bool, motion: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string}}, same: bool, for_both: bool}|array{_: 'messageActionGiftCode', via_giveaway: bool, unclaimed: bool, boost_peer?: array|int|string, days: int, slug: string, currency?: string, amount?: int, crypto_currency?: string, crypto_amount?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}|array{_: 'messageActionGiveawayLaunch', stars?: int}|array{_: 'messageActionGiveawayResults', stars: bool, winners_count: int, unclaimed_count: int}|array{_: 'messageActionBoostApply', boosts: int}|array{_: 'messageActionRequestedPeerSentMe', button_id: int, peers: list<array{_: 'requestedPeerUser', user_id: int, first_name?: string, last_name?: string, username?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'requestedPeerChat', chat_id: array|int|string, title?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'requestedPeerChannel', channel_id: int, title?: string, username?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}>}|array{_: 'messageActionPaymentRefunded', peer: array|int|string, charge: array{_: 'paymentCharge', id: string, provider_charge_id: string}, currency: string, total_amount: int, payload?: string}|array{_: 'messageActionGiftStars', currency: string, amount: int, stars: int, crypto_currency?: string, crypto_amount?: int, transaction_id?: string}|array{_: 'messageActionPrizeStars', boost_peer: array|int|string, unclaimed: bool, stars: int, transaction_id: string, giveaway_msg_id: int}|array{_: 'messageActionStarGift', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, name_hidden: bool, saved: bool, converted: bool, upgraded: bool, refunded: bool, can_upgrade: bool, prepaid_upgrade: bool, upgrade_separate: bool, auction_acquired: bool, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}, convert_stars?: int, upgrade_msg_id?: int, upgrade_stars?: int, from_id?: array|int|string, peer?: array|int|string, saved_id?: int, prepaid_upgrade_hash?: string, gift_msg_id?: int, to_id?: array|int|string, gift_num?: int}|array{_: 'messageActionStarGiftUnique', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, upgrade: bool, transferred: bool, saved: bool, refunded: bool, prepaid_upgrade: bool, assigned: bool, from_offer: bool, craft: bool, name_hidden: bool, can_export_at?: int, transfer_stars?: int, from_id?: array|int|string, peer?: array|int|string, saved_id?: int, resale_amount?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, can_transfer_at?: int, can_resell_at?: int, drop_original_details_stars?: int, can_craft_at?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}|array{_: 'messageActionPaidMessagesRefunded', count: int, stars: int}|array{_: 'messageActionPaidMessagesPrice', broadcast_messages_allowed: bool, stars: int}|array{_: 'messageActionConferenceCall', missed: bool, active: bool, video: bool, call_id: int, duration?: int, other_participants?: list<array|int|string>}|array{_: 'messageActionTodoCompletions', completed: list<int>, incompleted: list<int>}|array{_: 'messageActionTodoAppendTasks', list: list<array{_: 'todoItem', title: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, id: int}>}|array{_: 'messageActionSuggestedPostApproval', rejected: bool, balance_too_low: bool, reject_comment?: string, schedule_date?: int, price?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}}|array{_: 'messageActionSuggestedPostSuccess', price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}}|array{_: 'messageActionSuggestedPostRefund', payer_initiated: bool}|array{_: 'messageActionGiftTon', currency: string, amount: int, crypto_currency: string, crypto_amount: int, transaction_id?: string}|array{_: 'messageActionSuggestBirthday', birthday: array{_: 'birthday', day: int, month: int, year?: int}}|array{_: 'messageActionStarGiftPurchaseOffer', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, accepted: bool, declined: bool, expires_at: int}|array{_: 'messageActionStarGiftPurchaseOfferDeclined', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, expired: bool}|array{_: 'messageActionNewCreatorPending', new_creator_id: int}|array{_: 'messageActionChangeCreator', new_creator_id: int}|array{_: 'messageActionNoForwardsToggle', prev_value: bool, new_value: bool}|array{_: 'messageActionNoForwardsRequest', prev_value: bool, new_value: bool, expired: bool}|array{_: 'messageActionPollAppendAnswer', answer: array{_: 'pollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, option: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, added_by?: array|int|string, date?: int}|array{_: 'inputPollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, media?: \danog\MadelineProto\EventHandler\Media|string|array}}|array{_: 'messageActionPollDeleteAnswer', answer: array{_: 'pollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, option: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, added_by?: array|int|string, date?: int}|array{_: 'inputPollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, media?: \danog\MadelineProto\EventHandler\Media|string|array}}|array{_: 'messageActionManagedBotCreated', bot_id: int}|array{_: 'messageActionChangeCommunity', community_id?: int}|array{_: 'messageActionChatJoinedViaCommunity', community_id: int}|array{_: 'messageActionGramTransfer', comment_encrypted: bool, amount: int, peer_address: string, transaction_id: string, comment?: string}|array{_: 'messageActionWalletTonConnectRequest', accepted: bool, declined: bool, session_id: int, expires: int, topic?: string, trace_id?: string, dapp_name?: string}|array{_: 'messageActionNoPaidMessagesTokenUsed'}, reactions?: array{_: 'messageReactions', min: bool, can_see_list: bool, reactions_as_tags: bool, results: list<array{_: 'reactionCount', chosen_order?: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}, count: int}>, recent_reactions?: list<array{_: 'messagePeerReaction', peer_id: array|int|string, big: bool, unread: bool, my: bool, date: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}}>, top_reactors?: list<array{_: 'messageReactor', top: bool, my: bool, anonymous: bool, peer_id?: array|int|string, count: int}>}, ttl_period?: int}>, chats: list<array|int|string>, users: list<array|int|string>, pts: int} @see https://docs.madelineproto.xyz/API_docs/types/messages.ForumTopics.html
     * @psalm-impure
     */
    public function getForumTopics(array|int|string|null $peer = null, string|null $q = null, int|null $offset_date = 0, int|null $offset_id = 0, int|null $offset_topic = 0, int|null $limit = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param array|int|string $peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param list<int>|array<never, never> $topics 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.forumTopics', order_by_create_date: bool, count: int, topics: list<array{_: 'forumTopicDeleted', id: int}|array{_: 'forumTopic', peer: array|int|string, from_id: array|int|string, notify_settings: array{_: 'peerNotifySettings', show_previews?: bool, silent?: bool, mute_until?: int, ios_sound?: array{_: 'notificationSoundDefault'}|array{_: 'notificationSoundNone'}|array{_: 'notificationSoundLocal', title: string, data: string}|array{_: 'notificationSoundRingtone', id: int}, android_sound?: array{_: 'notificationSoundDefault'}|array{_: 'notificationSoundNone'}|array{_: 'notificationSoundLocal', title: string, data: string}|array{_: 'notificationSoundRingtone', id: int}, other_sound?: array{_: 'notificationSoundDefault'}|array{_: 'notificationSoundNone'}|array{_: 'notificationSoundLocal', title: string, data: string}|array{_: 'notificationSoundRingtone', id: int}, stories_muted?: bool, stories_hide_sender?: bool, stories_ios_sound?: array{_: 'notificationSoundDefault'}|array{_: 'notificationSoundNone'}|array{_: 'notificationSoundLocal', title: string, data: string}|array{_: 'notificationSoundRingtone', id: int}, stories_android_sound?: array{_: 'notificationSoundDefault'}|array{_: 'notificationSoundNone'}|array{_: 'notificationSoundLocal', title: string, data: string}|array{_: 'notificationSoundRingtone', id: int}, stories_other_sound?: array{_: 'notificationSoundDefault'}|array{_: 'notificationSoundNone'}|array{_: 'notificationSoundLocal', title: string, data: string}|array{_: 'notificationSoundRingtone', id: int}}, my: bool, closed: bool, pinned: bool, short: bool, hidden: bool, title_missing: bool, id: int, date: int, title: string, icon_color: int, icon_emoji_id?: int, top_message: int, read_inbox_max_id: int, read_outbox_max_id: int, unread_count: int, unread_mentions_count: int, unread_reactions_count: int, unread_poll_votes_count: int, draft?: array{_: 'draftMessageEmpty', date?: int}|array{_: 'draftMessage', no_webpage: bool, invert_media: bool, reply_to?: array{_: 'inputReplyToMessage', reply_to_msg_id: int, top_msg_id?: int, reply_to_peer_id?: array|int|string, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, quote_offset?: int, monoforum_peer_id?: array|int|string, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, url: string, webpage_id: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, email: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, phone: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, name: string}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, user_id: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, date: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, type: array{_: 'inlineButtonTypeUrl', url: array}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: array, url: array, button_id: array}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: array, fwd_text?: array, url: array, bot?: array}|array{_: 'inlineButtonTypeWebView', url: array}|array{_: 'inlineButtonTypeCallback', requires_password: array, data: array}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: array, query: array, peer_types?: list<array>}|array{_: 'inlineButtonTypeUserProfile', user_id: array}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array}|array{_: 'inlineButtonTypeCopy', copy_text: array}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: array, bg_danger: array, bg_success: array, link: array}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}>}|array{_: 'inputReplyToStory', peer: array|int|string, story_id: int}|array{_: 'inputReplyToMonoForum', monoforum_peer_id: array|int|string}|array{_: 'inputReplyToEphemeralMessage', id: int}, message: string, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, media?: \danog\MadelineProto\EventHandler\Media|string|array, date: int, effect?: int, suggested_post?: array{_: 'suggestedPost', accepted: bool, rejected: bool, price?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, schedule_date?: int}, rich_message?: array{_: 'richMessage', rtl: bool, part: bool, blocks: list<array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, published_date: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, language: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: string}|array{_: 'pageBlockList', items: list<array{_: 'pageListItemText', text: array, checkbox: array, checked: array}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, collapsed: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array, credit: array}, spoiler: bool, photo_id: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array, credit: array}, autoplay: bool, loop: bool, spoiler: bool, video_id: int}|array{_: 'pageBlockCover', cover: array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array}|array{_: 'pageBlockSubtitle', text: array}|array{_: 'pageBlockAuthorDate', author: array, published_date: array}|array{_: 'pageBlockHeader', text: array}|array{_: 'pageBlockSubheader', text: array}|array{_: 'pageBlockParagraph', text: array}|array{_: 'pageBlockPreformatted', text: array, language: array}|array{_: 'pageBlockFooter', text: array}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: array}|array{_: 'pageBlockList', items: list<array>}|array{_: 'pageBlockBlockquote', text: array, caption: array, collapsed: array}|array{_: 'pageBlockPullquote', text: array, caption: array}|array{_: 'pageBlockPhoto', caption: array, spoiler: array, photo_id: array, url?: array, webpage_id?: array}|array{_: 'pageBlockVideo', caption: array, autoplay: array, loop: array, spoiler: array, video_id: array}|array{_: 'pageBlockEmbed', caption: array, full_width: array, allow_scrolling: array, url?: array, html?: array, poster_photo_id?: array, w?: array, h?: array}|array{_: 'pageBlockChannel', channel: array}|array{_: 'pageBlockAudio', caption: array, audio_id: array}|array{_: 'pageBlockKicker', text: array}|array{_: 'pageBlockTable', title: array, bordered: array, striped: array, compact: array, rows: list<array>}|array{_: 'pageBlockOrderedList', reversed: array, items: list<array>, start?: array, type?: array}|array{_: 'pageBlockRelatedArticles', title: array, articles: list<array>}|array{_: 'pageBlockMap', caption: array, geo: array, zoom: array, w: array, h: array}|array{_: 'pageBlockHeading1', text: array}|array{_: 'pageBlockHeading2', text: array}|array{_: 'pageBlockHeading3', text: array}|array{_: 'pageBlockHeading4', text: array}|array{_: 'pageBlockHeading5', text: array}|array{_: 'pageBlockHeading6', text: array}|array{_: 'pageBlockMath', source: array}|array{_: 'pageBlockThinking', text: array}|array{_: 'inputPageBlockMap', caption: array, geo: array, zoom: array, w: array, h: array}|array{_: 'pageBlockButtonRow', align_left: array, align_center: array, align_right: array, buttons: list<array>}|array{_: 'pageBlockDocument', caption: array, document_id: array}}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array, credit: array}, full_width: bool, allow_scrolling: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array, credit: array}, audio_id: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, bordered: bool, striped: bool, compact: bool, rows: list<array{_: 'pageTableRow', cells: list<array>}>}|array{_: 'pageBlockOrderedList', reversed: bool, items: list<array{_: 'pageListOrderedItemText', text: array, checkbox: array, checked: array, num?: array, value?: array, type?: array}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, articles: list<array{_: 'pageRelatedArticle', url: array, webpage_id: array, title?: array, description?: array, photo_id?: array, author?: array, published_date?: array}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockMath', source: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: array, long: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockButtonRow', align_left: bool, align_center: bool, align_right: bool, buttons: list<array{_: 'pageButton', text: array, type: array, style?: array}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array, credit: array}, document_id: int}>, photos: list<array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}}}>, messages: list<array{_: 'messageEmpty', id: int, peer_id?: array|int|string}|array{_: 'message', peer_id: array|int|string, out: bool, mentioned: bool, media_unread: bool, silent: bool, post: bool, from_scheduled: bool, legacy: bool, edit_hide: bool, pinned: bool, noforwards: bool, invert_media: bool, offline: bool, video_processing_pending: bool, paid_suggested_post_stars: bool, paid_suggested_post_ton: bool, id: int, from_id?: array|int|string, from_boosts_applied?: int, from_rank?: string, saved_peer_id?: array|int|string, fwd_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, via_bot_id?: int, via_business_bot_id?: int, guestchat_via_from?: array|int|string, reply_to?: array{_: 'messageReplyHeader', reply_to_scheduled: bool, forum_topic: bool, quote: bool, reply_to_ephemeral: bool, reply_to_msg_id?: int, reply_to_peer_id?: array|int|string, reply_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, reply_media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_to_top_id?: int, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, quote_offset?: int, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, url: string, webpage_id: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, email: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, phone: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, name: string}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, user_id: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, date: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, type: array{_: 'inlineButtonTypeUrl', url: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url: string, button_id: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: bool, fwd_text?: string, url: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url: string}|array{_: 'inlineButtonTypeCallback', requires_password: bool, data: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: bool, query: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: bool, bg_danger: bool, bg_success: bool, link: bool}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}>}|array{_: 'messageReplyStoryHeader', peer: array|int|string, story_id: int}, date: int, message: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_markup?: array, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, views?: int, forwards?: int, replies?: array{_: 'messageReplies', comments: bool, replies: int, replies_pts: int, recent_repliers?: list<array|int|string>, channel_id?: int, max_id?: int, read_max_id?: int}, edit_date?: int, post_author?: string, grouped_id?: int, reactions?: array{_: 'messageReactions', min: bool, can_see_list: bool, reactions_as_tags: bool, results: list<array{_: 'reactionCount', chosen_order?: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}, count: int}>, recent_reactions?: list<array{_: 'messagePeerReaction', peer_id: array|int|string, big: bool, unread: bool, my: bool, date: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}}>, top_reactors?: list<array{_: 'messageReactor', top: bool, my: bool, anonymous: bool, peer_id?: array|int|string, count: int}>}, restriction_reason?: list<array{_: 'restrictionReason', platform: string, reason: string, text: string}>, ttl_period?: int, quick_reply_shortcut_id?: int, effect?: int, factcheck?: array{_: 'factCheck', need_check: bool, country?: string, text?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}, hash: list<int|string>}, report_delivery_until_date?: int, paid_message_stars?: int, suggested_post?: array{_: 'suggestedPost', accepted: bool, rejected: bool, price?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, schedule_date?: int}, schedule_repeat_period?: int, summary_from_language?: string, rich_message?: array{_: 'richMessage', rtl: bool, part: bool, blocks: list<array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, published_date: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, language: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: string}|array{_: 'pageBlockList', items: list<array{_: 'pageListItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, checkbox: bool, checked: bool}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, collapsed: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, spoiler: bool, photo_id: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, autoplay: bool, loop: bool, spoiler: bool, video_id: int}|array{_: 'pageBlockCover', cover: array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, published_date: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, language: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: string}|array{_: 'pageBlockList', items: list<array{_: 'pageListItemText', text: array, checkbox: array, checked: array}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, collapsed: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array, credit: array}, spoiler: bool, photo_id: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array, credit: array}, autoplay: bool, loop: bool, spoiler: bool, video_id: int}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array, credit: array}, full_width: bool, allow_scrolling: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array, credit: array}, audio_id: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, bordered: bool, striped: bool, compact: bool, rows: list<array{_: 'pageTableRow', cells: list<array>}>}|array{_: 'pageBlockOrderedList', reversed: bool, items: list<array{_: 'pageListOrderedItemText', text: array, checkbox: array, checked: array, num?: array, value?: array, type?: array}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, articles: list<array{_: 'pageRelatedArticle', url: array, webpage_id: array, title?: array, description?: array, photo_id?: array, author?: array, published_date?: array}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockMath', source: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: array, long: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockButtonRow', align_left: bool, align_center: bool, align_right: bool, buttons: list<array{_: 'pageButton', text: array, type: array, style?: array}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array, credit: array}, document_id: int}}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, full_width: bool, allow_scrolling: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, audio_id: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, bordered: bool, striped: bool, compact: bool, rows: list<array{_: 'pageTableRow', cells: list<array{_: 'pageTableCell', header: array, align_center: array, align_right: array, valign_middle: array, valign_bottom: array, text?: array, colspan?: array, rowspan?: array}>}>}|array{_: 'pageBlockOrderedList', reversed: bool, items: list<array{_: 'pageListOrderedItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, checkbox: bool, checked: bool, num?: string, value?: int, type?: string}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, articles: list<array{_: 'pageRelatedArticle', url: string, webpage_id: int, title?: string, description?: string, photo_id?: int, author?: string, published_date?: int}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash: int, accuracy_radius?: int}, zoom: int, w: int, h: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockMath', source: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, geo: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: float, long: float, accuracy_radius?: int}, zoom: int, w: int, h: int}|array{_: 'pageBlockButtonRow', align_left: bool, align_center: bool, align_right: bool, buttons: list<array{_: 'pageButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, type: array{_: 'inlineButtonTypeUrl', url: array}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: array, url: array, button_id: array}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: array, fwd_text?: array, url: array, bot?: array}|array{_: 'inlineButtonTypeWebView', url: array}|array{_: 'inlineButtonTypeCallback', requires_password: array, data: array}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: array, query: array, peer_types?: list<array>}|array{_: 'inlineButtonTypeUserProfile', user_id: array}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array}|array{_: 'inlineButtonTypeCopy', copy_text: array}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: array, bg_danger: array, bg_success: array, link: array}}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, document_id: int}>, photos: list<array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}}|array{_: 'messageService', peer_id: array|int|string, out: bool, mentioned: bool, media_unread: bool, reactions_are_possible: bool, silent: bool, post: bool, legacy: bool, id: int, from_id?: array|int|string, saved_peer_id?: array|int|string, reply_to?: array{_: 'messageReplyHeader', reply_to_scheduled: bool, forum_topic: bool, quote: bool, reply_to_ephemeral: bool, reply_to_msg_id?: int, reply_to_peer_id?: array|int|string, reply_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, reply_media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_to_top_id?: int, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, quote_offset?: int, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, url: string, webpage_id: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, email: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, phone: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, name: string}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, user_id: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, date: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, type: array{_: 'inlineButtonTypeUrl', url: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url: string, button_id: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: bool, fwd_text?: string, url: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url: string}|array{_: 'inlineButtonTypeCallback', requires_password: bool, data: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: bool, query: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: bool, bg_danger: bool, bg_success: bool, link: bool}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}>}|array{_: 'messageReplyStoryHeader', peer: array|int|string, story_id: int}, date: int, action: array{_: 'messageActionEmpty'}|array{_: 'messageActionChatCreate', title: string, users: list<int>}|array{_: 'messageActionChatEditTitle', title: string}|array{_: 'messageActionChatEditPhoto', photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'messageActionChatDeletePhoto'}|array{_: 'messageActionChatAddUser', users: list<int>}|array{_: 'messageActionChatDeleteUser', user_id: int}|array{_: 'messageActionChatJoinedByLink', inviter_id: int}|array{_: 'messageActionChannelCreate', title: string}|array{_: 'messageActionChatMigrateTo', channel_id: int}|array{_: 'messageActionChannelMigrateFrom', title: string, chat_id: array|int|string}|array{_: 'messageActionPinMessage'}|array{_: 'messageActionHistoryClear'}|array{_: 'messageActionGameScore', game_id: int, score: int}|array{_: 'messageActionPaymentSentMe', charge: array{_: 'paymentCharge', id: string, provider_charge_id: string}, recurring_init: bool, recurring_used: bool, currency: string, total_amount: int, payload: string, info?: array{_: 'paymentRequestedInfo', name?: string, phone?: string, email?: string, shipping_address?: array{_: 'postAddress', street_line1: string, street_line2: string, city: string, state: string, country_iso2: string, post_code: string}}, shipping_option_id?: string, subscription_until_date?: int}|array{_: 'messageActionPaymentSent', recurring_init: bool, recurring_used: bool, currency: string, total_amount: int, invoice_slug?: string, subscription_until_date?: int}|array{_: 'messageActionPhoneCall', video: bool, call_id: int, reason?: array{_: 'phoneCallDiscardReasonMissed'}|array{_: 'phoneCallDiscardReasonDisconnect'}|array{_: 'phoneCallDiscardReasonHangup'}|array{_: 'phoneCallDiscardReasonBusy'}|array{_: 'phoneCallDiscardReasonMigrateConferenceCall', slug: string}, duration?: int}|array{_: 'messageActionScreenshotTaken'}|array{_: 'messageActionCustomAction', message: string}|array{_: 'messageActionBotAllowed', attach_menu: bool, from_request: bool, domain?: string, app?: array{_: 'botAppNotModified'}|array{_: 'botApp', id: int, access_hash: int, short_name: string, title: string, description: string, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, hash: list<int|string>}}|array{_: 'messageActionSecureValuesSentMe', credentials: array{_: 'secureCredentialsEncrypted', data: string, hash: string, secret: string}, values: list<array{_: 'secureValue', type: array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}, data?: array{_: 'secureData', data: string, data_hash: string, secret: string}, front_side?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, reverse_side?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, selfie?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, translation?: list<array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}>, files?: list<array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}>, plain_data?: array{_: 'securePlainPhone', phone: string}|array{_: 'securePlainEmail', email: string}, hash: string}>}|array{_: 'messageActionSecureValuesSent', types: list<array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}>}|array{_: 'messageActionContactSignUp'}|array{_: 'messageActionGeoProximityReached', from_id: array|int|string, to_id: array|int|string, distance: int}|array{_: 'messageActionGroupCall', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, duration?: int}|array{_: 'messageActionInviteToGroupCall', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, users: list<int>}|array{_: 'messageActionSetMessagesTTL', period: int, auto_setting_from?: int}|array{_: 'messageActionGroupCallScheduled', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, schedule_date: int}|array{_: 'messageActionSetChatTheme', theme: array{_: 'chatTheme', emoticon: string}|array{_: 'chatThemeUniqueGift', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: array, edge_color: array, text_color: array}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array, crafted: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array, name: array, backdrop_id: array, center_color: array, edge_color: array, pattern_color: array, text_color: array}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array, sender_id?: array, date: array, message?: array}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: array, nanos: array}|array{_: 'starsTonAmount', amount: array}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: array, background_emoji_id?: array}|array{_: 'peerColorCollectible', collectible_id: array, gift_emoji_id: array, background_emoji_id: array, accent_color: array, colors: list<array>, dark_accent_color?: array, dark_colors?: list<array>}|array{_: 'inputPeerColorCollectible', collectible_id: array}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, theme_settings: list<array{_: 'themeSettings', base_theme: array{_: 'baseThemeClassic'}|array{_: 'baseThemeDay'}|array{_: 'baseThemeNight'}|array{_: 'baseThemeTinted'}|array{_: 'baseThemeArctic'}, message_colors_animated: bool, accent_color: int, outbox_accent_color?: int, message_colors?: list<int>, wallpaper?: array{_: 'wallPaper', id: array, creator: array, default: array, pattern: array, dark: array, access_hash: array, slug: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, settings?: array}|array{_: 'wallPaperNoFile', id: array, default: array, dark: array, settings?: array}}>}}|array{_: 'messageActionChatJoinedByRequest'}|array{_: 'messageActionWebViewDataSentMe', text: string, data: string}|array{_: 'messageActionWebViewDataSent', text: string}|array{_: 'messageActionGiftPremium', currency: string, amount: int, days: int, crypto_currency?: string, crypto_amount?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}|array{_: 'messageActionTopicCreate', title_missing: bool, title: string, icon_color: int, icon_emoji_id?: int}|array{_: 'messageActionTopicEdit', title?: string, icon_emoji_id?: int, closed?: bool, hidden?: bool}|array{_: 'messageActionSuggestProfilePhoto', photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'messageActionRequestedPeer', button_id: int, peers: list<array|int|string>}|array{_: 'messageActionSetChatWallPaper', wallpaper: array{_: 'wallPaper', id: int, creator: bool, default: bool, pattern: bool, dark: bool, access_hash: int, slug: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, settings?: array{_: 'wallPaperSettings', blur: bool, motion: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string}}|array{_: 'wallPaperNoFile', id: int, default: bool, dark: bool, settings?: array{_: 'wallPaperSettings', blur: bool, motion: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string}}, same: bool, for_both: bool}|array{_: 'messageActionGiftCode', via_giveaway: bool, unclaimed: bool, boost_peer?: array|int|string, days: int, slug: string, currency?: string, amount?: int, crypto_currency?: string, crypto_amount?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}|array{_: 'messageActionGiveawayLaunch', stars?: int}|array{_: 'messageActionGiveawayResults', stars: bool, winners_count: int, unclaimed_count: int}|array{_: 'messageActionBoostApply', boosts: int}|array{_: 'messageActionRequestedPeerSentMe', button_id: int, peers: list<array{_: 'requestedPeerUser', user_id: int, first_name?: string, last_name?: string, username?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'requestedPeerChat', chat_id: array|int|string, title?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'requestedPeerChannel', channel_id: int, title?: string, username?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}>}|array{_: 'messageActionPaymentRefunded', peer: array|int|string, charge: array{_: 'paymentCharge', id: string, provider_charge_id: string}, currency: string, total_amount: int, payload?: string}|array{_: 'messageActionGiftStars', currency: string, amount: int, stars: int, crypto_currency?: string, crypto_amount?: int, transaction_id?: string}|array{_: 'messageActionPrizeStars', boost_peer: array|int|string, unclaimed: bool, stars: int, transaction_id: string, giveaway_msg_id: int}|array{_: 'messageActionStarGift', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, name_hidden: bool, saved: bool, converted: bool, upgraded: bool, refunded: bool, can_upgrade: bool, prepaid_upgrade: bool, upgrade_separate: bool, auction_acquired: bool, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}, convert_stars?: int, upgrade_msg_id?: int, upgrade_stars?: int, from_id?: array|int|string, peer?: array|int|string, saved_id?: int, prepaid_upgrade_hash?: string, gift_msg_id?: int, to_id?: array|int|string, gift_num?: int}|array{_: 'messageActionStarGiftUnique', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, upgrade: bool, transferred: bool, saved: bool, refunded: bool, prepaid_upgrade: bool, assigned: bool, from_offer: bool, craft: bool, name_hidden: bool, can_export_at?: int, transfer_stars?: int, from_id?: array|int|string, peer?: array|int|string, saved_id?: int, resale_amount?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, can_transfer_at?: int, can_resell_at?: int, drop_original_details_stars?: int, can_craft_at?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}|array{_: 'messageActionPaidMessagesRefunded', count: int, stars: int}|array{_: 'messageActionPaidMessagesPrice', broadcast_messages_allowed: bool, stars: int}|array{_: 'messageActionConferenceCall', missed: bool, active: bool, video: bool, call_id: int, duration?: int, other_participants?: list<array|int|string>}|array{_: 'messageActionTodoCompletions', completed: list<int>, incompleted: list<int>}|array{_: 'messageActionTodoAppendTasks', list: list<array{_: 'todoItem', title: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, id: int}>}|array{_: 'messageActionSuggestedPostApproval', rejected: bool, balance_too_low: bool, reject_comment?: string, schedule_date?: int, price?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}}|array{_: 'messageActionSuggestedPostSuccess', price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}}|array{_: 'messageActionSuggestedPostRefund', payer_initiated: bool}|array{_: 'messageActionGiftTon', currency: string, amount: int, crypto_currency: string, crypto_amount: int, transaction_id?: string}|array{_: 'messageActionSuggestBirthday', birthday: array{_: 'birthday', day: int, month: int, year?: int}}|array{_: 'messageActionStarGiftPurchaseOffer', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, accepted: bool, declined: bool, expires_at: int}|array{_: 'messageActionStarGiftPurchaseOfferDeclined', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: array}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: array, entities: list<array>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, expired: bool}|array{_: 'messageActionNewCreatorPending', new_creator_id: int}|array{_: 'messageActionChangeCreator', new_creator_id: int}|array{_: 'messageActionNoForwardsToggle', prev_value: bool, new_value: bool}|array{_: 'messageActionNoForwardsRequest', prev_value: bool, new_value: bool, expired: bool}|array{_: 'messageActionPollAppendAnswer', answer: array{_: 'pollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, option: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, added_by?: array|int|string, date?: int}|array{_: 'inputPollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, media?: \danog\MadelineProto\EventHandler\Media|string|array}}|array{_: 'messageActionPollDeleteAnswer', answer: array{_: 'pollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, option: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, added_by?: array|int|string, date?: int}|array{_: 'inputPollAnswer', text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, media?: \danog\MadelineProto\EventHandler\Media|string|array}}|array{_: 'messageActionManagedBotCreated', bot_id: int}|array{_: 'messageActionChangeCommunity', community_id?: int}|array{_: 'messageActionChatJoinedViaCommunity', community_id: int}|array{_: 'messageActionGramTransfer', comment_encrypted: bool, amount: int, peer_address: string, transaction_id: string, comment?: string}|array{_: 'messageActionWalletTonConnectRequest', accepted: bool, declined: bool, session_id: int, expires: int, topic?: string, trace_id?: string, dapp_name?: string}|array{_: 'messageActionNoPaidMessagesTokenUsed'}, reactions?: array{_: 'messageReactions', min: bool, can_see_list: bool, reactions_as_tags: bool, results: list<array{_: 'reactionCount', chosen_order?: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}, count: int}>, recent_reactions?: list<array{_: 'messagePeerReaction', peer_id: array|int|string, big: bool, unread: bool, my: bool, date: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}}>, top_reactors?: list<array{_: 'messageReactor', top: bool, my: bool, anonymous: bool, peer_id?: array|int|string, count: int}>}, ttl_period?: int}>, chats: list<array|int|string>, users: list<array|int|string>, pts: int} @see https://docs.madelineproto.xyz/API_docs/types/messages.ForumTopics.html
     * @psalm-impure
     */
    public function getForumTopicsByID(array|int|string|null $peer = null, array $topics = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param array|int|string $peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $topic_id 
     * @param string $title 
     * @param int $icon_emoji_id 
     * @param bool $closed 
     * @param bool $hidden 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function editForumTopic(array|int|string|null $peer = null, int|null $topic_id = 0, string|null $title = null, int|null $icon_emoji_id = null, bool|null $closed = null, bool|null $hidden = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param bool $pinned 
     * @param array|int|string $peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $topic_id 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function updatePinnedForumTopic(bool $pinned, array|int|string|null $peer = null, int|null $topic_id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param bool $force 
     * @param array|int|string $peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param list<int>|array<never, never> $order 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function reorderPinnedForumTopics(bool|null $force = null, array|int|string|null $peer = null, array $order = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param bool $title_missing 
     * @param array|int|string $peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param string $title 
     * @param int $icon_color 
     * @param int $icon_emoji_id 
     * @param array|int|string $send_as @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function createForumTopic(bool|null $title_missing = null, array|int|string|null $peer = null, string|null $title = '', int|null $icon_color = null, int|null $icon_emoji_id = null, array|int|string|null $send_as = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param array|int|string $peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $top_msg_id 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.affectedHistory', pts: int, pts_count: int, offset: int} @see https://docs.madelineproto.xyz/API_docs/types/messages.AffectedHistory.html
     * @psalm-impure
     */
    public function deleteTopicHistory(array|int|string|null $peer = null, int|null $top_msg_id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.emojiGameUnavailable'}|array{_: 'messages.emojiGameDiceInfo', game_hash: string, prev_stake: int, current_streak: int, params: list<int>, plays_left?: int} @see https://docs.madelineproto.xyz/API_docs/types/messages.EmojiGameInfo.html
     * @psalm-impure
     */
    public function getEmojiGameInfo(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param array|int|string $peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $id 
     * @param string $to_lang 
     * @param string $tone 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>} @see https://docs.madelineproto.xyz/API_docs/types/TextWithEntities.html
     * @psalm-impure
     */
    public function summarizeText(array|int|string|null $peer = null, int|null $id = 0, string|null $to_lang = null, string|null $tone = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param string|array $password @see https://docs.madelineproto.xyz/API_docs/types/InputCheckPasswordSRP.html
     * @param array|int|string $peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param array|int|string $user_id @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function editChatCreator(string|array $password, array|int|string|null $peer = null, array|int|string|null $user_id = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param array|int|string $peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array|int|string @see https://docs.madelineproto.xyz/API_docs/types/User.html
     * @psalm-impure
     */
    public function getFutureChatCreatorAfterLeave(array|int|string|null $peer = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array|int|string;

    /**
     * 
     *
     * @param array|int|string $peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param array|int|string $participant @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param string $rank 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function editChatParticipantRank(array|int|string|null $peer = null, array|int|string|null $participant = null, string|null $rank = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param string $url 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function declineUrlAuth(string|null $url = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * 
     *
     * @param string $url 
     * @param string $match_code 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function checkUrlAuthMatchCode(string|null $url = '', string|null $match_code = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * 
     *
     * @param array{_: 'textWithEntities', text?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>} $text @see https://docs.madelineproto.xyz/API_docs/types/TextWithEntities.html
     * @param bool $proofread 
     * @param bool $emojify 
     * @param string $translate_to_lang 
     * @param array{_: 'inputAiComposeToneDefault', tone?: string}|array{_: 'inputAiComposeToneID', id?: int, access_hash?: int}|array{_: 'inputAiComposeToneSlug', slug?: string}|array{_: 'inputAiComposeToneSingleUse', custom_prompt?: string} $tone @see https://docs.madelineproto.xyz/API_docs/types/InputAiComposeTone.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.composedMessageWithAI', result_text: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}, diff_text?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}} @see https://docs.madelineproto.xyz/API_docs/types/messages.ComposedMessageWithAI.html
     * @psalm-impure
     */
    public function composeMessageWithAI(array $text, bool|null $proofread = null, bool|null $emojify = null, string|null $translate_to_lang = null, array|null $tone = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param array|int|string $peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param list<array{_: 'inputMessageReadMetric', msg_id?: int, view_id?: int, time_in_view_ms?: int, active_time_in_view_ms?: int, height_to_viewport_ratio_permille?: int, seen_range_ratio_permille?: int}>|array<never, never> $metrics Array of  @see https://docs.madelineproto.xyz/API_docs/types/InputMessageReadMetric.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function reportReadMetrics(array|int|string|null $peer = null, array $metrics = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * 
     *
     * @param array $id @see https://docs.madelineproto.xyz/API_docs/types/InputDocument.html
     * @param int $listened_duration 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function reportMusicListen(array|null $id = null, int|null $listened_duration = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * 
     *
     * @param array{_: 'pollAnswer', text: array{_: 'textWithEntities', text?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>}, option?: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler?: bool, live_photo?: bool, photo?: array{_: 'photoEmpty', id?: array}|array{_: 'photo', has_stickers?: array, id?: array, access_hash?: array, file_reference?: array, date?: array, sizes?: list<array>, video_sizes?: list<array>, dc_id?: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id?: array}|array{_: 'document', id?: array, access_hash?: array, file_reference?: array, date?: array, mime_type?: array, size?: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id?: array, attributes?: list<array>}}|array{_: 'messageMediaGeo', geo?: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash?: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number?: string, first_name?: string, last_name?: string, vcard?: string, user_id?: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium?: bool, spoiler?: bool, video?: bool, round?: bool, voice?: bool, document?: array{_: 'documentEmpty', id?: array}|array{_: 'document', id?: array, access_hash?: array, file_reference?: array, date?: array, mime_type?: array, size?: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id?: array, attributes?: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id?: array}|array{_: 'document', id?: array, access_hash?: array, file_reference?: array, date?: array, mime_type?: array, size?: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id?: array, attributes?: list<array>}>, video_cover?: array{_: 'photoEmpty', id?: array}|array{_: 'photo', has_stickers?: array, id?: array, access_hash?: array, file_reference?: array, date?: array, sizes?: list<array>, video_sizes?: list<array>, dc_id?: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media?: bool, force_small_media?: bool, manual?: bool, safe?: bool, webpage?: array{_: 'webPageEmpty', id?: array, url?: array}|array{_: 'webPagePending', id?: array, url?: array, date?: array}|array{_: 'webPage', has_large_media?: array, video_cover_photo?: array, id?: array, url?: array, display_url?: array, hash?: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id?: array}|array{_: 'photo', has_stickers?: array, id?: array, access_hash?: array, file_reference?: array, date?: array, sizes?: list<array>, video_sizes?: list<array>, dc_id?: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id?: array}|array{_: 'document', id?: array, access_hash?: array, file_reference?: array, date?: array, mime_type?: array, size?: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id?: array, attributes?: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo?: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash?: array, accuracy_radius?: array}, title?: string, address?: string, provider?: string, venue_id?: string, venue_type?: string}|array{_: 'messageMediaGame', game: array{_: 'game', id?: array, access_hash?: array, short_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id?: array}|array{_: 'photo', has_stickers?: array, id?: array, access_hash?: array, file_reference?: array, date?: array, sizes?: list<array>, video_sizes?: list<array>, dc_id?: array}, document?: array{_: 'documentEmpty', id?: array}|array{_: 'document', id?: array, access_hash?: array, file_reference?: array, date?: array, mime_type?: array, size?: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id?: array, attributes?: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested?: bool, test?: bool, title?: string, description?: string, photo?: array{_: 'webDocument', url?: array, access_hash?: array, size?: array, mime_type?: array, attributes?: list<array>}|array{_: 'webDocumentNoProxy', url?: array, size?: array, mime_type?: array, attributes?: list<array>}, receipt_msg_id?: int, currency?: string, total_amount?: int, start_param?: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo?: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash?: array, accuracy_radius?: array}, heading?: int, period?: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value?: int, emoticon?: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed?: array, stake_ton_amount?: array, ton_amount?: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention?: bool, id?: int, story?: array{_: 'storyItemDeleted', id?: array}|array{_: 'storyItemSkipped', close_friends?: array, live?: array, id?: array, date?: array, expire_date?: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers?: bool, winners_are_visible?: bool, channels?: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity?: int, months?: int, stars?: int, until_date?: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers?: bool, refunded?: bool, channel_id?: int, additional_peers_count?: int, launch_msg_id?: int, winners_count?: int, unclaimed_count?: int, winners?: list<int>, months?: int, stars?: int, prize_description?: string, until_date?: int}|array{_: 'messageMediaPaidMedia', stars_amount?: int, extended_media?: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append?: array, others_can_complete?: array, list?: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id?: array, date?: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id?: array, access_hash?: array}|array{_: 'inputGroupCallSlug', slug?: array}|array{_: 'inputGroupCallInviteMessage', msg_id?: array}, rtmp_stream?: bool}, added_by?: array|int|string, date?: int}|array{_: 'inputPollAnswer', text: array{_: 'textWithEntities', text?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>}, media?: \danog\MadelineProto\EventHandler\Media|string|array} $answer @see https://docs.madelineproto.xyz/API_docs/types/PollAnswer.html
     * @param array|int|string $peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $msg_id 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function addPollAnswer(array $answer, array|int|string|null $peer = null, int|null $msg_id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param array|int|string $peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $msg_id 
     * @param string $option 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function deletePollAnswer(array|int|string|null $peer = null, int|null $msg_id = 0, string|null $option = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param array|int|string $peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $top_msg_id 
     * @param int $offset_id 
     * @param int $add_offset 
     * @param int $limit 
     * @param int $max_id 
     * @param int $min_id 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.messages', messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.messagesSlice', inexact: array, count: array, next_rate?: array, offset_id_offset?: array, search_flood?: array, messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.channelMessages', inexact: array, pts: array, count: array, offset_id_offset?: array, messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.messagesNotModified', count: array} @see https://docs.madelineproto.xyz/API_docs/types/messages.Messages.html
     * @psalm-impure
     */
    public function getUnreadPollVotes(array|int|string|null $peer = null, int|null $top_msg_id = null, int|null $offset_id = 0, int|null $add_offset = 0, int|null $limit = 0, int|null $max_id = 0, int|null $min_id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param array|int|string $peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $top_msg_id 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.affectedHistory', pts: int, pts_count: int, offset: int} @see https://docs.madelineproto.xyz/API_docs/types/messages.AffectedHistory.html
     * @psalm-impure
     */
    public function readPollVotes(array|int|string|null $peer = null, int|null $top_msg_id = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param array{_: 'inputBotInlineResult', send_message: array{_: 'inputBotInlineMessageMediaAuto', invert_media?: array, message?: array, entities?: list<array>, reply_markup?: array}|array{_: 'inputBotInlineMessageText', no_webpage?: array, invert_media?: array, message?: array, entities?: list<array>, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaGeo', geo_point?: array, heading?: array, period?: array, proximity_notification_radius?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaVenue', geo_point?: array, title?: array, address?: array, provider?: array, venue_id?: array, venue_type?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaContact', phone_number?: array, first_name?: array, last_name?: array, vcard?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageGame', reply_markup?: array}|array{_: 'inputBotInlineMessageMediaInvoice', invoice: array, provider_data: array, title?: array, description?: array, photo?: array, payload?: array, provider?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaWebPage', invert_media?: array, force_large_media?: array, force_small_media?: array, optional?: array, message?: array, entities?: list<array>, url?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageRichMessage', rich_message: array, reply_markup?: array}, id?: string, type?: string, title?: string, description?: string, url?: string, thumb?: array{_: 'inputWebDocument', url?: array, size?: array, mime_type?: array, attributes?: list<array>}, content?: array{_: 'inputWebDocument', url?: array, size?: array, mime_type?: array, attributes?: list<array>}}|array{_: 'inputBotInlineResultPhoto', send_message: array{_: 'inputBotInlineMessageMediaAuto', invert_media?: array, message?: array, entities?: list<array>, reply_markup?: array}|array{_: 'inputBotInlineMessageText', no_webpage?: array, invert_media?: array, message?: array, entities?: list<array>, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaGeo', geo_point?: array, heading?: array, period?: array, proximity_notification_radius?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaVenue', geo_point?: array, title?: array, address?: array, provider?: array, venue_id?: array, venue_type?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaContact', phone_number?: array, first_name?: array, last_name?: array, vcard?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageGame', reply_markup?: array}|array{_: 'inputBotInlineMessageMediaInvoice', invoice: array, provider_data: array, title?: array, description?: array, photo?: array, payload?: array, provider?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaWebPage', invert_media?: array, force_large_media?: array, force_small_media?: array, optional?: array, message?: array, entities?: list<array>, url?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageRichMessage', rich_message: array, reply_markup?: array}, id?: string, type?: string, photo?: array}|array{_: 'inputBotInlineResultDocument', send_message: array{_: 'inputBotInlineMessageMediaAuto', invert_media?: array, message?: array, entities?: list<array>, reply_markup?: array}|array{_: 'inputBotInlineMessageText', no_webpage?: array, invert_media?: array, message?: array, entities?: list<array>, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaGeo', geo_point?: array, heading?: array, period?: array, proximity_notification_radius?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaVenue', geo_point?: array, title?: array, address?: array, provider?: array, venue_id?: array, venue_type?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaContact', phone_number?: array, first_name?: array, last_name?: array, vcard?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageGame', reply_markup?: array}|array{_: 'inputBotInlineMessageMediaInvoice', invoice: array, provider_data: array, title?: array, description?: array, photo?: array, payload?: array, provider?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaWebPage', invert_media?: array, force_large_media?: array, force_small_media?: array, optional?: array, message?: array, entities?: list<array>, url?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageRichMessage', rich_message: array, reply_markup?: array}, id?: string, type?: string, title?: string, description?: string, document?: array}|array{_: 'inputBotInlineResultGame', send_message: array{_: 'inputBotInlineMessageMediaAuto', invert_media?: array, message?: array, entities?: list<array>, reply_markup?: array}|array{_: 'inputBotInlineMessageText', no_webpage?: array, invert_media?: array, message?: array, entities?: list<array>, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaGeo', geo_point?: array, heading?: array, period?: array, proximity_notification_radius?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaVenue', geo_point?: array, title?: array, address?: array, provider?: array, venue_id?: array, venue_type?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaContact', phone_number?: array, first_name?: array, last_name?: array, vcard?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageGame', reply_markup?: array}|array{_: 'inputBotInlineMessageMediaInvoice', invoice: array, provider_data: array, title?: array, description?: array, photo?: array, payload?: array, provider?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageMediaWebPage', invert_media?: array, force_large_media?: array, force_small_media?: array, optional?: array, message?: array, entities?: list<array>, url?: array, reply_markup?: array}|array{_: 'inputBotInlineMessageRichMessage', rich_message: array, reply_markup?: array}, id?: string, short_name?: string} $result @see https://docs.madelineproto.xyz/API_docs/types/InputBotInlineResult.html
     * @param int $query_id 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'inputBotInlineMessageID', dc_id: int, id: int, access_hash: int}|array{_: 'inputBotInlineMessageID64', dc_id: int, owner_id: int, id: int, access_hash: int} @see https://docs.madelineproto.xyz/API_docs/types/InputBotInlineMessageID.html
     * @psalm-impure
     */
    public function setBotGuestChatResult(array $result, int|null $query_id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param array|int|string $peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param array|int|string $participant @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function deleteParticipantReactions(array|int|string|null $peer = null, array|int|string|null $participant = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * 
     *
     * @param array|int|string $peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $msg_id 
     * @param array|int|string $participant @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function deleteParticipantReaction(array|int|string|null $peer = null, int|null $msg_id = 0, array|int|string|null $participant = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param array|int|string $user_id @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param int $limit 
     * @param int $max_id 
     * @param int $min_id 
     * @param list<int|string>|array<never, never> $hash Array of  @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.messages', messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.messagesSlice', inexact: array, count: array, next_rate?: array, offset_id_offset?: array, search_flood?: array, messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.channelMessages', inexact: array, pts: array, count: array, offset_id_offset?: array, messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.messagesNotModified', count: array} @see https://docs.madelineproto.xyz/API_docs/types/messages.Messages.html
     * @psalm-impure
     */
    public function getPersonalChannelHistory(array|int|string|null $user_id = null, int|null $limit = 0, int|null $max_id = 0, int|null $min_id = 0, array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param array|int|string $peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $id 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.messages', messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.messagesSlice', inexact: array, count: array, next_rate?: array, offset_id_offset?: array, search_flood?: array, messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.channelMessages', inexact: array, pts: array, count: array, offset_id_offset?: array, messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.messagesNotModified', count: array} @see https://docs.madelineproto.xyz/API_docs/types/messages.Messages.html
     * @psalm-impure
     */
    public function getRichMessage(array|int|string|null $peer = null, int|null $id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param array|int|string $peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param list<int> $id 
     * @param list<array{_: 'inputRichMessage', rtl?: bool, noautolink?: bool, blocks?: list<array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, published_date?: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, language?: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name?: string}|array{_: 'pageBlockList', items?: list<array{_: 'pageListItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, checkbox?: bool, checked?: bool}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, collapsed?: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, spoiler?: bool, photo_id?: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, autoplay?: bool, loop?: bool, spoiler?: bool, video_id?: int}|array{_: 'pageBlockCover', cover: array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, published_date?: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, language?: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name?: string}|array{_: 'pageBlockList', items?: list<array{_: 'pageListItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, checkbox?: bool, checked?: bool}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, collapsed?: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, spoiler?: bool, photo_id?: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, autoplay?: bool, loop?: bool, spoiler?: bool, video_id?: int}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, full_width?: bool, allow_scrolling?: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel?: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, audio_id?: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, bordered?: bool, striped?: bool, compact?: bool, rows?: list<array{_: 'pageTableRow', cells?: list<array{_: 'pageTableCell', header?: bool, align_center?: bool, align_right?: bool, valign_middle?: bool, valign_bottom?: bool, text?: array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textConcat', texts?: list<array>}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}, colspan?: int, rowspan?: int}>}>}|array{_: 'pageBlockOrderedList', reversed?: bool, items?: list<array{_: 'pageListOrderedItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, checkbox?: bool, checked?: bool, num?: string, value?: int, type?: string}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, articles?: list<array{_: 'pageRelatedArticle', url?: string, webpage_id?: int, title?: string, description?: string, photo_id?: int, author?: string, published_date?: int}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, geo?: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash?: int, accuracy_radius?: int}, zoom?: int, w?: int, h?: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockMath', source?: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, geo?: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: float, long: float, accuracy_radius?: int}, zoom?: int, w?: int, h?: int}|array{_: 'pageBlockButtonRow', align_left?: bool, align_center?: bool, align_right?: bool, buttons?: list<array{_: 'pageButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, type: array{_: 'inlineButtonTypeUrl', url?: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url?: string, button_id?: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access?: bool, fwd_text?: string, url?: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url?: string}|array{_: 'inlineButtonTypeCallback', requires_password?: bool, data?: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer?: bool, query?: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id?: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id?: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text?: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary?: bool, bg_danger?: bool, bg_success?: bool, link?: bool}}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, document_id?: int}}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, full_width?: bool, allow_scrolling?: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel?: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, audio_id?: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, bordered?: bool, striped?: bool, compact?: bool, rows?: list<array{_: 'pageTableRow', cells?: list<array{_: 'pageTableCell', header?: bool, align_center?: bool, align_right?: bool, valign_middle?: bool, valign_bottom?: bool, text?: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, colspan?: int, rowspan?: int}>}>}|array{_: 'pageBlockOrderedList', reversed?: bool, items?: list<array{_: 'pageListOrderedItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, checkbox?: bool, checked?: bool, num?: string, value?: int, type?: string}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, articles?: list<array{_: 'pageRelatedArticle', url?: string, webpage_id?: int, title?: string, description?: string, photo_id?: int, author?: string, published_date?: int}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, geo?: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash?: int, accuracy_radius?: int}, zoom?: int, w?: int, h?: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockMath', source?: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, geo?: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: float, long: float, accuracy_radius?: int}, zoom?: int, w?: int, h?: int}|array{_: 'pageBlockButtonRow', align_left?: bool, align_center?: bool, align_right?: bool, buttons?: list<array{_: 'pageButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, type: array{_: 'inlineButtonTypeUrl', url?: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url?: string, button_id?: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access?: bool, fwd_text?: string, url?: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url?: string}|array{_: 'inlineButtonTypeCallback', requires_password?: bool, data?: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer?: bool, query?: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id?: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id?: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text?: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary?: bool, bg_danger?: bool, bg_success?: bool, link?: bool}}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, document_id?: int}>, photos?: list<array>, documents?: list<array>, users?: list<array|int|string>}|array{_: 'inputRichMessageHTML', rtl?: bool, noautolink?: bool, html?: string, files?: list<array{_: 'inputRichFilePhoto', id?: string, photo?: array}|array{_: 'inputRichFileDocument', id?: string, document?: array}>}|array{_: 'inputRichMessageMarkdown', rtl?: bool, noautolink?: bool, markdown?: string, files?: list<array{_: 'inputRichFilePhoto', id?: string, photo?: array}|array{_: 'inputRichFileDocument', id?: string, document?: array}>}> $text Array of  @see https://docs.madelineproto.xyz/API_docs/types/InputRichMessage.html
     * @param string $to_lang 
     * @param string $tone 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.translatedRichMessage', result: list<array{_: 'richMessage', rtl: bool, part: bool, blocks: list<array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, published_date: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, language: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: string}|array{_: 'pageBlockList', items: list<array{_: 'pageListItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, checkbox: bool, checked: bool}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, collapsed: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}, spoiler: bool, photo_id: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}, autoplay: bool, loop: bool, spoiler: bool, video_id: int}|array{_: 'pageBlockCover', cover: array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, published_date: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, language: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: string}|array{_: 'pageBlockList', items: list<array{_: 'pageListItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, checkbox: bool, checked: bool}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, collapsed: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, spoiler: bool, photo_id: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, autoplay: bool, loop: bool, spoiler: bool, video_id: int}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, full_width: bool, allow_scrolling: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, audio_id: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, bordered: bool, striped: bool, compact: bool, rows: list<array{_: 'pageTableRow', cells: list<array{_: 'pageTableCell', header: array, align_center: array, align_right: array, valign_middle: array, valign_bottom: array, text?: array, colspan?: array, rowspan?: array}>}>}|array{_: 'pageBlockOrderedList', reversed: bool, items: list<array{_: 'pageListOrderedItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, checkbox: bool, checked: bool, num?: string, value?: int, type?: string}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, articles: list<array{_: 'pageRelatedArticle', url: string, webpage_id: int, title?: string, description?: string, photo_id?: int, author?: string, published_date?: int}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash: int, accuracy_radius?: int}, zoom: int, w: int, h: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockMath', source: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, geo: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: float, long: float, accuracy_radius?: int}, zoom: int, w: int, h: int}|array{_: 'pageBlockButtonRow', align_left: bool, align_center: bool, align_right: bool, buttons: list<array{_: 'pageButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, type: array{_: 'inlineButtonTypeUrl', url: array}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: array, url: array, button_id: array}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: array, fwd_text?: array, url: array, bot?: array}|array{_: 'inlineButtonTypeWebView', url: array}|array{_: 'inlineButtonTypeCallback', requires_password: array, data: array}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: array, query: array, peer_types?: list<array>}|array{_: 'inlineButtonTypeUserProfile', user_id: array}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array}|array{_: 'inlineButtonTypeCopy', copy_text: array}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: array, bg_danger: array, bg_success: array, link: array}}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, document_id: int}}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}, full_width: bool, allow_scrolling: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}, audio_id: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, bordered: bool, striped: bool, compact: bool, rows: list<array{_: 'pageTableRow', cells: list<array{_: 'pageTableCell', header: bool, align_center: bool, align_right: bool, valign_middle: bool, valign_bottom: bool, text?: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, colspan?: int, rowspan?: int}>}>}|array{_: 'pageBlockOrderedList', reversed: bool, items: list<array{_: 'pageListOrderedItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, checkbox: bool, checked: bool, num?: string, value?: int, type?: string}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, articles: list<array{_: 'pageRelatedArticle', url: string, webpage_id: int, title?: string, description?: string, photo_id?: int, author?: string, published_date?: int}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash: int, accuracy_radius?: int}, zoom: int, w: int, h: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockMath', source: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}, geo: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: float, long: float, accuracy_radius?: int}, zoom: int, w: int, h: int}|array{_: 'pageBlockButtonRow', align_left: bool, align_center: bool, align_right: bool, buttons: list<array{_: 'pageButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, type: array{_: 'inlineButtonTypeUrl', url: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url: string, button_id: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: bool, fwd_text?: string, url: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url: string}|array{_: 'inlineButtonTypeCallback', requires_password: bool, data: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: bool, query: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: bool, bg_danger: bool, bg_success: bool, link: bool}}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}, document_id: int}>, photos: list<array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}>} @see https://docs.madelineproto.xyz/API_docs/types/messages.TranslatedRichMessage.html
     * @psalm-impure
     */
    public function translateRichMessage(array|int|string|null $peer = null, array|null $id = null, array|null $text = null, string|null $to_lang = '', string|null $tone = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param bool $proofread 
     * @param bool $emojify 
     * @param array{_: 'inputRichMessage', rtl?: bool, noautolink?: bool, blocks?: list<array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, published_date?: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, language?: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name?: string}|array{_: 'pageBlockList', items?: list<array{_: 'pageListItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, checkbox?: bool, checked?: bool}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, collapsed?: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, spoiler?: bool, photo_id?: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, autoplay?: bool, loop?: bool, spoiler?: bool, video_id?: int}|array{_: 'pageBlockCover', cover: array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, published_date?: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, language?: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name?: string}|array{_: 'pageBlockList', items?: list<array{_: 'pageListItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, checkbox?: bool, checked?: bool}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, collapsed?: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, spoiler?: bool, photo_id?: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, autoplay?: bool, loop?: bool, spoiler?: bool, video_id?: int}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, full_width?: bool, allow_scrolling?: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel?: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, audio_id?: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, bordered?: bool, striped?: bool, compact?: bool, rows?: list<array{_: 'pageTableRow', cells?: list<array{_: 'pageTableCell', header?: bool, align_center?: bool, align_right?: bool, valign_middle?: bool, valign_bottom?: bool, text?: array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textConcat', texts?: list<array>}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}, colspan?: int, rowspan?: int}>}>}|array{_: 'pageBlockOrderedList', reversed?: bool, items?: list<array{_: 'pageListOrderedItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, checkbox?: bool, checked?: bool, num?: string, value?: int, type?: string}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, articles?: list<array{_: 'pageRelatedArticle', url?: string, webpage_id?: int, title?: string, description?: string, photo_id?: int, author?: string, published_date?: int}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, geo?: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash?: int, accuracy_radius?: int}, zoom?: int, w?: int, h?: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockMath', source?: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, geo?: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: float, long: float, accuracy_radius?: int}, zoom?: int, w?: int, h?: int}|array{_: 'pageBlockButtonRow', align_left?: bool, align_center?: bool, align_right?: bool, buttons?: list<array{_: 'pageButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, type: array{_: 'inlineButtonTypeUrl', url?: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url?: string, button_id?: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access?: bool, fwd_text?: string, url?: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url?: string}|array{_: 'inlineButtonTypeCallback', requires_password?: bool, data?: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer?: bool, query?: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id?: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id?: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text?: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary?: bool, bg_danger?: bool, bg_success?: bool, link?: bool}}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, document_id?: int}}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, full_width?: bool, allow_scrolling?: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel?: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, audio_id?: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, bordered?: bool, striped?: bool, compact?: bool, rows?: list<array{_: 'pageTableRow', cells?: list<array{_: 'pageTableCell', header?: bool, align_center?: bool, align_right?: bool, valign_middle?: bool, valign_bottom?: bool, text?: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, colspan?: int, rowspan?: int}>}>}|array{_: 'pageBlockOrderedList', reversed?: bool, items?: list<array{_: 'pageListOrderedItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, checkbox?: bool, checked?: bool, num?: string, value?: int, type?: string}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, articles?: list<array{_: 'pageRelatedArticle', url?: string, webpage_id?: int, title?: string, description?: string, photo_id?: int, author?: string, published_date?: int}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, geo?: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash?: int, accuracy_radius?: int}, zoom?: int, w?: int, h?: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockMath', source?: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, geo?: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: float, long: float, accuracy_radius?: int}, zoom?: int, w?: int, h?: int}|array{_: 'pageBlockButtonRow', align_left?: bool, align_center?: bool, align_right?: bool, buttons?: list<array{_: 'pageButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, type: array{_: 'inlineButtonTypeUrl', url?: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url?: string, button_id?: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access?: bool, fwd_text?: string, url?: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url?: string}|array{_: 'inlineButtonTypeCallback', requires_password?: bool, data?: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer?: bool, query?: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id?: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id?: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text?: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary?: bool, bg_danger?: bool, bg_success?: bool, link?: bool}}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, document_id?: int}>, photos?: list<array>, documents?: list<array>, users?: list<array|int|string>}|array{_: 'inputRichMessageHTML', rtl?: bool, noautolink?: bool, html?: string, files?: list<array{_: 'inputRichFilePhoto', id?: string, photo?: array}|array{_: 'inputRichFileDocument', id?: string, document?: array}>}|array{_: 'inputRichMessageMarkdown', rtl?: bool, noautolink?: bool, markdown?: string, files?: list<array{_: 'inputRichFilePhoto', id?: string, photo?: array}|array{_: 'inputRichFileDocument', id?: string, document?: array}>} $text @see https://docs.madelineproto.xyz/API_docs/types/InputRichMessage.html
     * @param string $translate_to_lang 
     * @param array{_: 'inputAiComposeToneDefault', tone?: string}|array{_: 'inputAiComposeToneID', id?: int, access_hash?: int}|array{_: 'inputAiComposeToneSlug', slug?: string}|array{_: 'inputAiComposeToneSingleUse', custom_prompt?: string} $tone @see https://docs.madelineproto.xyz/API_docs/types/InputAiComposeTone.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.composedRichMessageWithAI', result: array{_: 'richMessage', rtl: bool, part: bool, blocks: list<array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, published_date: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, language: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: string}|array{_: 'pageBlockList', items: list<array{_: 'pageListItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, checkbox: bool, checked: bool}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, collapsed: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}, spoiler: bool, photo_id: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}, autoplay: bool, loop: bool, spoiler: bool, video_id: int}|array{_: 'pageBlockCover', cover: array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, published_date: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, language: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: string}|array{_: 'pageBlockList', items: list<array{_: 'pageListItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, checkbox: bool, checked: bool}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, collapsed: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, spoiler: bool, photo_id: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, autoplay: bool, loop: bool, spoiler: bool, video_id: int}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, full_width: bool, allow_scrolling: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, audio_id: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, bordered: bool, striped: bool, compact: bool, rows: list<array{_: 'pageTableRow', cells: list<array{_: 'pageTableCell', header: array, align_center: array, align_right: array, valign_middle: array, valign_bottom: array, text?: array, colspan?: array, rowspan?: array}>}>}|array{_: 'pageBlockOrderedList', reversed: bool, items: list<array{_: 'pageListOrderedItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, checkbox: bool, checked: bool, num?: string, value?: int, type?: string}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, articles: list<array{_: 'pageRelatedArticle', url: string, webpage_id: int, title?: string, description?: string, photo_id?: int, author?: string, published_date?: int}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash: int, accuracy_radius?: int}, zoom: int, w: int, h: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockMath', source: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, geo: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: float, long: float, accuracy_radius?: int}, zoom: int, w: int, h: int}|array{_: 'pageBlockButtonRow', align_left: bool, align_center: bool, align_right: bool, buttons: list<array{_: 'pageButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, type: array{_: 'inlineButtonTypeUrl', url: array}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: array, url: array, button_id: array}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: array, fwd_text?: array, url: array, bot?: array}|array{_: 'inlineButtonTypeWebView', url: array}|array{_: 'inlineButtonTypeCallback', requires_password: array, data: array}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: array, query: array, peer_types?: list<array>}|array{_: 'inlineButtonTypeUserProfile', user_id: array}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array}|array{_: 'inlineButtonTypeCopy', copy_text: array}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: array, bg_danger: array, bg_success: array, link: array}}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, document_id: int}}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}, full_width: bool, allow_scrolling: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}, audio_id: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, bordered: bool, striped: bool, compact: bool, rows: list<array{_: 'pageTableRow', cells: list<array{_: 'pageTableCell', header: bool, align_center: bool, align_right: bool, valign_middle: bool, valign_bottom: bool, text?: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, colspan?: int, rowspan?: int}>}>}|array{_: 'pageBlockOrderedList', reversed: bool, items: list<array{_: 'pageListOrderedItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, checkbox: bool, checked: bool, num?: string, value?: int, type?: string}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, articles: list<array{_: 'pageRelatedArticle', url: string, webpage_id: int, title?: string, description?: string, photo_id?: int, author?: string, published_date?: int}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash: int, accuracy_radius?: int}, zoom: int, w: int, h: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockMath', source: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}, geo: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: float, long: float, accuracy_radius?: int}, zoom: int, w: int, h: int}|array{_: 'pageBlockButtonRow', align_left: bool, align_center: bool, align_right: bool, buttons: list<array{_: 'pageButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, type: array{_: 'inlineButtonTypeUrl', url: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url: string, button_id: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: bool, fwd_text?: string, url: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url: string}|array{_: 'inlineButtonTypeCallback', requires_password: bool, data: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: bool, query: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: bool, bg_danger: bool, bg_success: bool, link: bool}}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}, document_id: int}>, photos: list<array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}} @see https://docs.madelineproto.xyz/API_docs/types/messages.ComposedRichMessageWithAI.html
     * @psalm-impure
     */
    public function composeRichMessageWithAI(bool|null $proofread = null, bool|null $emojify = null, array|null $text = null, string|null $translate_to_lang = null, array|null $tone = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param int $query_id 
     * @param mixed $theme_params Any JSON-encodable data
     * @param string $platform 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'webViewResultUrl', fullsize: bool, fullscreen: bool, same_origin: bool, query_id?: int, url: string} @see https://docs.madelineproto.xyz/API_docs/types/WebViewResult.html
     * @psalm-impure
     */
    public function requestChatJoinWebView(int|null $query_id = 0, mixed $theme_params = null, string|null $platform = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param array{_: 'shareThemeDefault'}|array{_: 'shareThemeEmojiBackdrop', backdrop_id?: int, emojis?: list<array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon?: string}|array{_: 'reactionCustomEmoji', document_id?: int}|array{_: 'reactionPaid'}>}|array{_: 'shareThemeGift', stargift: array{_: 'starGift', limited?: bool, sold_out?: bool, birthday?: bool, require_premium?: bool, limited_per_user?: bool, peer_color_available?: bool, auction?: bool, id?: int, sticker?: array{_: 'documentEmpty', id?: array}|array{_: 'document', id?: array, access_hash?: array, file_reference?: array, date?: array, mime_type?: array, size?: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id?: array, attributes?: list<array>}, stars?: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars?: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color?: int, edge_color?: int, text_color?: int}}|array{_: 'starGiftUnique', require_premium?: bool, resale_ton_only?: bool, theme_available?: bool, burned?: bool, crafted?: bool, id?: int, gift_id?: int, title?: string, slug?: string, num?: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes?: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille?: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted?: bool, name?: string, document?: array{_: 'documentEmpty', id?: array}|array{_: 'document', id?: array, access_hash?: array, file_reference?: array, date?: array, mime_type?: array, size?: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id?: array, attributes?: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille?: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name?: string, document?: array{_: 'documentEmpty', id?: array}|array{_: 'document', id?: array, access_hash?: array, file_reference?: array, date?: array, mime_type?: array, size?: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id?: array, attributes?: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille?: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name?: string, backdrop_id?: int, center_color?: int, edge_color?: int, pattern_color?: int, text_color?: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date?: int, message?: array{_: 'textWithEntities', text?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>}}>, availability_issued?: int, availability_total?: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount?: int, nanos?: int}|array{_: 'starsTonAmount', amount?: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id?: int, gift_emoji_id?: int, background_emoji_id?: int, accent_color?: int, colors?: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id?: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}}|array{_: 'inputShareThemeGift', stargift: array{_: 'inputSavedStarGiftUser', msg_id?: int}|array{_: 'inputSavedStarGiftChat', peer?: array|int|string, saved_id?: int}|array{_: 'inputSavedStarGiftSlug', slug?: string}} $theme @see https://docs.madelineproto.xyz/API_docs/types/ShareTheme.html
     * @param array|int|string $peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function setShareTheme(array $theme, array|int|string|null $peer = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * 
     *
     * @param int $hash 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'shareThemeBackdropsNotModified'}|array{_: 'shareThemeBackdrops', hash: int, backdrops: list<array{_: 'shareThemeBackdropOption', backdrop_id: int, name: string, center_color: int, edge_color: int, pattern_color: int, text_color: int}>} @see https://docs.madelineproto.xyz/API_docs/types/ShareThemeBackdrops.html
     * @psalm-impure
     */
    public function getShareThemeBackdrops(int|null $hash = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;
}
<?php
/**
 * This file is automatic generated by build_docs.php file
 * and is used only for autocomplete in multiple IDE
 * don't modify manually.
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\Namespace;

/** @psalm-mutable */
interface Ephemeral
{
    /**
     * 
     *
     * @param bool $invert_media 
     * @param bool $welcome 
     * @param bool $anchor 
     * @param bool $noforwards 
     * @param array|int|string $peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param array|int|string $receiver_id @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param int $query_id 
     * @param string $message 
     * @param list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}> $entities Array of  @see https://docs.madelineproto.xyz/API_docs/types/MessageEntity.html
     * @param \danog\MadelineProto\ParseMode $parse_mode Whether to parse HTML or Markdown markup in the message
     * @param \danog\MadelineProto\EventHandler\Media|string|array $media @see https://docs.madelineproto.xyz/API_docs/types/InputMedia.html
     * @param array $reply_markup @see https://docs.madelineproto.xyz/API_docs/types/ReplyMarkup.html
     * @param array{_: 'inputRichMessage', rtl?: bool, noautolink?: bool, blocks?: list<array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, published_date?: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, language?: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name?: string}|array{_: 'pageBlockList', items?: list<array{_: 'pageListItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, checkbox?: bool, checked?: bool}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, collapsed?: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, spoiler?: bool, photo_id?: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, autoplay?: bool, loop?: bool, spoiler?: bool, video_id?: int}|array{_: 'pageBlockCover', cover: array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, published_date?: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, language?: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name?: string}|array{_: 'pageBlockList', items?: list<array{_: 'pageListItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, checkbox?: bool, checked?: bool}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, collapsed?: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, spoiler?: bool, photo_id?: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, autoplay?: bool, loop?: bool, spoiler?: bool, video_id?: int}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, full_width?: bool, allow_scrolling?: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel?: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, audio_id?: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, bordered?: bool, striped?: bool, compact?: bool, rows?: list<array{_: 'pageTableRow', cells?: list<array{_: 'pageTableCell', header?: bool, align_center?: bool, align_right?: bool, valign_middle?: bool, valign_bottom?: bool, text?: array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textConcat', texts?: list<array>}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}, colspan?: int, rowspan?: int}>}>}|array{_: 'pageBlockOrderedList', reversed?: bool, items?: list<array{_: 'pageListOrderedItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, checkbox?: bool, checked?: bool, num?: string, value?: int, type?: string}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, articles?: list<array{_: 'pageRelatedArticle', url?: string, webpage_id?: int, title?: string, description?: string, photo_id?: int, author?: string, published_date?: int}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, geo?: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash?: int, accuracy_radius?: int}, zoom?: int, w?: int, h?: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockMath', source?: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, geo?: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: float, long: float, accuracy_radius?: int}, zoom?: int, w?: int, h?: int}|array{_: 'pageBlockButtonRow', align_left?: bool, align_center?: bool, align_right?: bool, buttons?: list<array{_: 'pageButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, type: array{_: 'inlineButtonTypeUrl', url?: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url?: string, button_id?: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access?: bool, fwd_text?: string, url?: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url?: string}|array{_: 'inlineButtonTypeCallback', requires_password?: bool, data?: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer?: bool, query?: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id?: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id?: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text?: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary?: bool, bg_danger?: bool, bg_success?: bool, link?: bool}}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, document_id?: int}}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, full_width?: bool, allow_scrolling?: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel?: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, audio_id?: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, bordered?: bool, striped?: bool, compact?: bool, rows?: list<array{_: 'pageTableRow', cells?: list<array{_: 'pageTableCell', header?: bool, align_center?: bool, align_right?: bool, valign_middle?: bool, valign_bottom?: bool, text?: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, colspan?: int, rowspan?: int}>}>}|array{_: 'pageBlockOrderedList', reversed?: bool, items?: list<array{_: 'pageListOrderedItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, checkbox?: bool, checked?: bool, num?: string, value?: int, type?: string}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, articles?: list<array{_: 'pageRelatedArticle', url?: string, webpage_id?: int, title?: string, description?: string, photo_id?: int, author?: string, published_date?: int}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, geo?: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash?: int, accuracy_radius?: int}, zoom?: int, w?: int, h?: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockMath', source?: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, geo?: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: float, long: float, accuracy_radius?: int}, zoom?: int, w?: int, h?: int}|array{_: 'pageBlockButtonRow', align_left?: bool, align_center?: bool, align_right?: bool, buttons?: list<array{_: 'pageButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, type: array{_: 'inlineButtonTypeUrl', url?: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url?: string, button_id?: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access?: bool, fwd_text?: string, url?: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url?: string}|array{_: 'inlineButtonTypeCallback', requires_password?: bool, data?: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer?: bool, query?: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id?: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id?: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text?: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary?: bool, bg_danger?: bool, bg_success?: bool, link?: bool}}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, document_id?: int}>, photos?: list<array>, documents?: list<array>, users?: list<array|int|string>}|array{_: 'inputRichMessageHTML', rtl?: bool, noautolink?: bool, html?: string, files?: list<array{_: 'inputRichFilePhoto', id?: string, photo?: array}|array{_: 'inputRichFileDocument', id?: string, document?: array}>}|array{_: 'inputRichMessageMarkdown', rtl?: bool, noautolink?: bool, markdown?: string, files?: list<array{_: 'inputRichFilePhoto', id?: string, photo?: array}|array{_: 'inputRichFileDocument', id?: string, document?: array}>} $rich_message @see https://docs.madelineproto.xyz/API_docs/types/InputRichMessage.html
     * @param array{_: 'inputReplyToMessage', reply_to_msg_id?: int, top_msg_id?: int, reply_to_peer_id?: array|int|string, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>, quote_offset?: int, monoforum_peer_id?: array|int|string, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, url?: string, webpage_id?: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, email?: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, phone?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, name?: string}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, user_id?: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, date?: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, type: array{_: 'inlineButtonTypeUrl', url?: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url?: string, button_id?: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access?: bool, fwd_text?: string, url?: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url?: string}|array{_: 'inlineButtonTypeCallback', requires_password?: bool, data?: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer?: bool, query?: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id?: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id?: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text?: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary?: bool, bg_danger?: bool, bg_success?: bool, link?: bool}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}>}|array{_: 'inputReplyToStory', peer?: array|int|string, story_id?: int}|array{_: 'inputReplyToMonoForum', monoforum_peer_id?: array|int|string}|array{_: 'inputReplyToEphemeralMessage', id?: int} $reply_to @see https://docs.madelineproto.xyz/API_docs/types/InputReplyTo.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function sendMessage(bool|null $invert_media = null, bool|null $welcome = null, bool|null $anchor = null, bool|null $noforwards = null, array|int|string|null $peer = null, array|int|string|null $receiver_id = null, int|null $query_id = null, string|null $message = '', array|null $entities = null, \danog\MadelineProto\ParseMode $parse_mode = \danog\MadelineProto\ParseMode::TEXT, \danog\MadelineProto\EventHandler\Media|array|string|null $media = null, array|null $reply_markup = null, array|null $rich_message = null, array|null $reply_to = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param array|int|string $peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param array|int|string $receiver_id @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param int $id 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function deleteMessage(array|int|string|null $peer = null, array|int|string|null $receiver_id = null, int|null $id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * 
     *
     * @param array|int|string $peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $id 
     * @param string $option 
     * @param string $message 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'reportResultChooseOption', title: string, options: list<array{_: 'messageReportOption', text: string, option: string}>}|array{_: 'reportResultAddComment', optional: bool, option: string}|array{_: 'reportResultReported'} @see https://docs.madelineproto.xyz/API_docs/types/ReportResult.html
     * @psalm-impure
     */
    public function reportMessage(array|int|string|null $peer = null, int|null $id = 0, string|null $option = '', string|null $message = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param array|int|string $peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $id 
     * @param string $data 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.botCallbackAnswer', alert: bool, has_url: bool, native_ui: bool, message?: string, url?: string, cache_time: int} @see https://docs.madelineproto.xyz/API_docs/types/messages.BotCallbackAnswer.html
     * @psalm-impure
     */
    public function getCallbackAnswer(array|int|string|null $peer = null, int|null $id = 0, string|null $data = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param bool $invert_media 
     * @param bool $welcome 
     * @param array|int|string $peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param array|int|string $receiver_id @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param int $id 
     * @param string $message 
     * @param \danog\MadelineProto\EventHandler\Media|string|array $media @see https://docs.madelineproto.xyz/API_docs/types/InputMedia.html
     * @param list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}> $entities Array of  @see https://docs.madelineproto.xyz/API_docs/types/MessageEntity.html
     * @param \danog\MadelineProto\ParseMode $parse_mode Whether to parse HTML or Markdown markup in the message
     * @param array $reply_markup @see https://docs.madelineproto.xyz/API_docs/types/ReplyMarkup.html
     * @param array{_: 'inputRichMessage', rtl?: bool, noautolink?: bool, blocks?: list<array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, published_date?: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, language?: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name?: string}|array{_: 'pageBlockList', items?: list<array{_: 'pageListItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, checkbox?: bool, checked?: bool}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, collapsed?: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, spoiler?: bool, photo_id?: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, autoplay?: bool, loop?: bool, spoiler?: bool, video_id?: int}|array{_: 'pageBlockCover', cover: array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, published_date?: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, language?: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name?: string}|array{_: 'pageBlockList', items?: list<array{_: 'pageListItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, checkbox?: bool, checked?: bool}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, collapsed?: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, spoiler?: bool, photo_id?: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, autoplay?: bool, loop?: bool, spoiler?: bool, video_id?: int}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, full_width?: bool, allow_scrolling?: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel?: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, audio_id?: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, bordered?: bool, striped?: bool, compact?: bool, rows?: list<array{_: 'pageTableRow', cells?: list<array{_: 'pageTableCell', header?: bool, align_center?: bool, align_right?: bool, valign_middle?: bool, valign_bottom?: bool, text?: array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textConcat', texts?: list<array>}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}, colspan?: int, rowspan?: int}>}>}|array{_: 'pageBlockOrderedList', reversed?: bool, items?: list<array{_: 'pageListOrderedItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, checkbox?: bool, checked?: bool, num?: string, value?: int, type?: string}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, articles?: list<array{_: 'pageRelatedArticle', url?: string, webpage_id?: int, title?: string, description?: string, photo_id?: int, author?: string, published_date?: int}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, geo?: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash?: int, accuracy_radius?: int}, zoom?: int, w?: int, h?: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockMath', source?: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, geo?: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: float, long: float, accuracy_radius?: int}, zoom?: int, w?: int, h?: int}|array{_: 'pageBlockButtonRow', align_left?: bool, align_center?: bool, align_right?: bool, buttons?: list<array{_: 'pageButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, type: array{_: 'inlineButtonTypeUrl', url?: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url?: string, button_id?: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access?: bool, fwd_text?: string, url?: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url?: string}|array{_: 'inlineButtonTypeCallback', requires_password?: bool, data?: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer?: bool, query?: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id?: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id?: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text?: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary?: bool, bg_danger?: bool, bg_success?: bool, link?: bool}}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, document_id?: int}}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, full_width?: bool, allow_scrolling?: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel?: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, audio_id?: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, bordered?: bool, striped?: bool, compact?: bool, rows?: list<array{_: 'pageTableRow', cells?: list<array{_: 'pageTableCell', header?: bool, align_center?: bool, align_right?: bool, valign_middle?: bool, valign_bottom?: bool, text?: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: array}|array{_: 'textImage', document_id?: array, w?: array, h?: array}|array{_: 'textMath', source?: array}|array{_: 'textCustomEmoji', document_id?: array, alt?: array}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, colspan?: int, rowspan?: int}>}>}|array{_: 'pageBlockOrderedList', reversed?: bool, items?: list<array{_: 'pageListOrderedItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, checkbox?: bool, checked?: bool, num?: string, value?: int, type?: string}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, articles?: list<array{_: 'pageRelatedArticle', url?: string, webpage_id?: int, title?: string, description?: string, photo_id?: int, author?: string, published_date?: int}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, geo?: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash?: int, accuracy_radius?: int}, zoom?: int, w?: int, h?: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'pageBlockMath', source?: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, geo?: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: float, long: float, accuracy_radius?: int}, zoom?: int, w?: int, h?: int}|array{_: 'pageBlockButtonRow', align_left?: bool, align_center?: bool, align_right?: bool, buttons?: list<array{_: 'pageButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, type: array{_: 'inlineButtonTypeUrl', url?: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url?: string, button_id?: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access?: bool, fwd_text?: string, url?: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url?: string}|array{_: 'inlineButtonTypeCallback', requires_password?: bool, data?: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer?: bool, query?: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id?: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id?: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text?: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary?: bool, bg_danger?: bool, bg_success?: bool, link?: bool}}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textConcat', texts?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text?: string}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}>}|array{_: 'textImage', document_id?: int, w?: int, h?: int}|array{_: 'textMath', source?: string}|array{_: 'textCustomEmoji', document_id?: int, alt?: string}}, document_id?: int}>, photos?: list<array>, documents?: list<array>, users?: list<array|int|string>}|array{_: 'inputRichMessageHTML', rtl?: bool, noautolink?: bool, html?: string, files?: list<array{_: 'inputRichFilePhoto', id?: string, photo?: array}|array{_: 'inputRichFileDocument', id?: string, document?: array}>}|array{_: 'inputRichMessageMarkdown', rtl?: bool, noautolink?: bool, markdown?: string, files?: list<array{_: 'inputRichFilePhoto', id?: string, photo?: array}|array{_: 'inputRichFileDocument', id?: string, document?: array}>} $rich_message @see https://docs.madelineproto.xyz/API_docs/types/InputRichMessage.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function editMessage(bool|null $invert_media = null, bool|null $welcome = null, array|int|string|null $peer = null, array|int|string|null $receiver_id = null, int|null $id = 0, string|null $message = null, \danog\MadelineProto\EventHandler\Media|array|string|null $media = null, array|null $entities = null, \danog\MadelineProto\ParseMode $parse_mode = \danog\MadelineProto\ParseMode::TEXT, array|null $reply_markup = null, array|null $rich_message = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param array|int|string $peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $id 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function deleteWelcomeMessage(array|int|string|null $peer = null, int|null $id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * 
     *
     * @param array|int|string $peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function deleteAllWelcomeMessages(array|int|string|null $peer = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * 
     *
     * @param array|int|string $peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param list<int|string>|array<never, never> $hash Array of  @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'ephemeral.welcomeMessagesNotModified'}|array{_: 'ephemeral.welcomeMessages', hash: list<int|string>, messages: list<array{_: 'ephemeralMessage', from_id: array|int|string, out: bool, welcome_template: bool, invert_media: bool, noforwards: bool, id: int, peer_id?: array|int|string, receiver_id: int, top_msg_id?: int, date: int, message: string, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_markup?: array, reply_to?: array{_: 'messageReplyHeader', reply_to_scheduled: bool, forum_topic: bool, quote: bool, reply_to_ephemeral: bool, reply_to_msg_id?: int, reply_to_peer_id?: array|int|string, reply_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, reply_media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_to_top_id?: int, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, quote_offset?: int, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, url: string, webpage_id: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, email: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, phone: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, name: string}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, user_id: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, date: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, type: array{_: 'inlineButtonTypeUrl', url: string}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: string, url: string, button_id: int}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: bool, fwd_text?: string, url: string, bot?: array|int|string}|array{_: 'inlineButtonTypeWebView', url: string}|array{_: 'inlineButtonTypeCallback', requires_password: bool, data: string}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: bool, query: string, peer_types?: list<array{_: 'inlineQueryPeerTypeSameBotPM'}|array{_: 'inlineQueryPeerTypePM'}|array{_: 'inlineQueryPeerTypeChat'}|array{_: 'inlineQueryPeerTypeMegagroup'}|array{_: 'inlineQueryPeerTypeBroadcast'}|array{_: 'inlineQueryPeerTypeBotPM'}>}|array{_: 'inlineButtonTypeUserProfile', user_id: int}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array|int|string}|array{_: 'inlineButtonTypeCopy', copy_text: string}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: bool, bg_danger: bool, bg_success: bool, link: bool}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}>}|array{_: 'messageReplyStoryHeader', peer: array|int|string, story_id: int}, rich_message?: array{_: 'richMessage', rtl: bool, part: bool, blocks: list<array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, published_date: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, language: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: string}|array{_: 'pageBlockList', items: list<array{_: 'pageListItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, checkbox: bool, checked: bool}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, collapsed: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, spoiler: bool, photo_id: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, autoplay: bool, loop: bool, spoiler: bool, video_id: int}|array{_: 'pageBlockCover', cover: array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, published_date: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, language: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: string}|array{_: 'pageBlockList', items: list<array{_: 'pageListItemText', text: array, checkbox: array, checked: array}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, collapsed: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array, credit: array}, spoiler: bool, photo_id: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array, credit: array}, autoplay: bool, loop: bool, spoiler: bool, video_id: int}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array, credit: array}, full_width: bool, allow_scrolling: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array, credit: array}, audio_id: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, bordered: bool, striped: bool, compact: bool, rows: list<array{_: 'pageTableRow', cells: list<array>}>}|array{_: 'pageBlockOrderedList', reversed: bool, items: list<array{_: 'pageListOrderedItemText', text: array, checkbox: array, checked: array, num?: array, value?: array, type?: array}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, articles: list<array{_: 'pageRelatedArticle', url: array, webpage_id: array, title?: array, description?: array, photo_id?: array, author?: array, published_date?: array}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockMath', source: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: array, long: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockButtonRow', align_left: bool, align_center: bool, align_right: bool, buttons: list<array{_: 'pageButton', text: array, type: array, style?: array}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array, credit: array}, document_id: int}}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, full_width: bool, allow_scrolling: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, audio_id: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, bordered: bool, striped: bool, compact: bool, rows: list<array{_: 'pageTableRow', cells: list<array{_: 'pageTableCell', header: array, align_center: array, align_right: array, valign_middle: array, valign_bottom: array, text?: array, colspan?: array, rowspan?: array}>}>}|array{_: 'pageBlockOrderedList', reversed: bool, items: list<array{_: 'pageListOrderedItemText', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, checkbox: bool, checked: bool, num?: string, value?: int, type?: string}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}, articles: list<array{_: 'pageRelatedArticle', url: string, webpage_id: int, title?: string, description?: string, photo_id?: int, author?: string, published_date?: int}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash: int, accuracy_radius?: int}, zoom: int, w: int, h: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'pageBlockMath', source: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textConcat', texts: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}>}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, geo: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: float, long: float, accuracy_radius?: int}, zoom: int, w: int, h: int}|array{_: 'pageBlockButtonRow', align_left: bool, align_center: bool, align_right: bool, buttons: list<array{_: 'pageButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, type: array{_: 'inlineButtonTypeUrl', url: array}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: array, url: array, button_id: array}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: array, fwd_text?: array, url: array, bot?: array}|array{_: 'inlineButtonTypeWebView', url: array}|array{_: 'inlineButtonTypeCallback', requires_password: array, data: array}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: array, query: array, peer_types?: list<array>}|array{_: 'inlineButtonTypeUserProfile', user_id: array}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array}|array{_: 'inlineButtonTypeCopy', copy_text: array}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: array, bg_danger: array, bg_success: array, link: array}}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, credit: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}, document_id: int}>, photos: list<array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}, chat_instance?: int, anchor_msg_id?: int}>} @see https://docs.madelineproto.xyz/API_docs/types/ephemeral.WelcomeMessages.html
     * @psalm-impure
     */
    public function getWelcomeMessages(array|int|string|null $peer = null, array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;
}
<?php
/**
 * This file is automatic generated by build_docs.php file
 * and is used only for autocomplete in multiple IDE
 * don't modify manually.
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\Namespace;

/** @psalm-mutable */
interface Premium
{
    /**
     * Obtains info about the boosts that were applied to a certain channel or supergroup (admins only)
     *
     * @param bool $gifts Whether to return only info about boosts received from [gift codes and giveaways created by the channel/supergroup Â»](https://core.telegram.org/api/giveaways)
     * @param array|int|string $peer The channel/supergroup @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param string $offset Offset for pagination, obtained from [premium.boostsList](https://docs.madelineproto.xyz/API_docs/constructors/premium.boostsList.html).`next_offset`
     * @param int $limit Maximum number of results to return, [see pagination](https://core.telegram.org/api/offsets)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'premium.boostsList', count: int, boosts: list<array{_: 'boost', gift: bool, giveaway: bool, unclaimed: bool, id: string, user_id?: int, giveaway_msg_id?: int, date: int, expires: int, used_gift_slug?: string, multiplier?: int, stars?: int}>, next_offset?: string, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/premium.BoostsList.html
     * @psalm-impure
     */
    public function getBoostsList(bool|null $gifts = null, array|int|string|null $peer = null, string|null $offset = '', int|null $limit = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Obtain which peers are we currently [boosting](https://core.telegram.org/api/boost), and how many [boost slots](https://core.telegram.org/api/boost) we have left.
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'premium.myBoosts', my_boosts: list<array{_: 'myBoost', slot: int, peer?: array|int|string, date: int, expires: int, cooldown_until_date?: int}>, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/premium.MyBoosts.html
     * @psalm-impure
     */
    public function getMyBoosts(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Apply one or more [boosts Â»](https://core.telegram.org/api/boost) to a peer.
     *
     * @param list<int> $slots Which [boost slots](https://core.telegram.org/api/boost) to assign to this peer.
     * @param array|int|string $peer The peer to boost. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'premium.myBoosts', my_boosts: list<array{_: 'myBoost', slot: int, peer?: array|int|string, date: int, expires: int, cooldown_until_date?: int}>, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/premium.MyBoosts.html
     * @psalm-impure
     */
    public function applyBoost(array|null $slots = null, array|int|string|null $peer = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Gets the current [number of boosts](https://core.telegram.org/api/boost) of a channel/supergroup.
     *
     * @param array|int|string $peer The peer. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'premium.boostsStatus', my_boost: bool, level: int, current_level_boosts: int, boosts: int, gift_boosts?: int, next_level_boosts?: int, premium_audience?: array{_: 'statsPercentValue', part: float, total: float}, boost_url: string, prepaid_giveaways?: list<array{_: 'prepaidGiveaway', id: int, months: int, quantity: int, date: int}|array{_: 'prepaidStarsGiveaway', id: int, stars: int, quantity: int, boosts: int, date: int}>, my_boost_slots?: list<int>} @see https://docs.madelineproto.xyz/API_docs/types/premium.BoostsStatus.html
     * @psalm-impure
     */
    public function getBoostsStatus(array|int|string|null $peer = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Returns the lists of boost that were applied to a channel/supergroup by a specific user (admins only)
     *
     * @param array|int|string $peer The channel/supergroup @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param array|int|string $user_id The user @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'premium.boostsList', count: int, boosts: list<array{_: 'boost', gift: bool, giveaway: bool, unclaimed: bool, id: string, user_id?: int, giveaway_msg_id?: int, date: int, expires: int, used_gift_slug?: string, multiplier?: int, stars?: int}>, next_offset?: string, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/premium.BoostsList.html
     * @psalm-impure
     */
    public function getUserBoosts(array|int|string|null $peer = null, array|int|string|null $user_id = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;
}
<?php
/**
 * This file is automatic generated by build_docs.php file
 * and is used only for autocomplete in multiple IDE
 * don't modify manually.
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\Namespace;

/** @psalm-mutable */
interface Help
{
    /**
     * Returns current configuration, including data center configuration.
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'config', test_mode: bool, default_p2p_contacts: bool, preload_featured_stickers: bool, revoke_pm_inbox: bool, blocked_mode: bool, force_try_ipv6: bool, date: int, expires: int, this_dc: int, dc_options: list<array{_: 'dcOption', ipv6: bool, media_only: bool, tcpo_only: bool, cdn: bool, static: bool, this_port_only: bool, id: int, ip_address: string, port: int, secret?: string}>, dc_txt_domain_name: string, chat_size_max: int, megagroup_size_max: int, forwarded_count_max: int, online_update_period_ms: int, offline_blur_timeout_ms: int, offline_idle_timeout_ms: int, online_cloud_timeout_ms: int, notify_cloud_delay_ms: int, notify_default_delay_ms: int, push_chat_period_ms: int, push_chat_limit: int, edit_time_limit: int, revoke_time_limit: int, revoke_pm_time_limit: int, rating_e_decay: int, stickers_recent_limit: int, channels_read_media_period: int, tmp_sessions?: int, call_receive_timeout_ms: int, call_ring_timeout_ms: int, call_connect_timeout_ms: int, call_packet_timeout_ms: int, me_url_prefix: string, autoupdate_url_prefix?: string, gif_search_username?: string, venue_search_username?: string, img_search_username?: string, static_maps_provider?: string, caption_length_max: int, message_length_max: int, webfile_dc_id: int, suggested_lang_code?: string, lang_pack_version?: int, base_lang_pack_version?: int, reactions_default?: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}, autologin_token?: string} @see https://docs.madelineproto.xyz/API_docs/types/Config.html
     * @psalm-impure
     */
    public function getConfig(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Returns info on data center nearest to the user.
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'nearestDc', country: string, this_dc: int, nearest_dc: int} @see https://docs.madelineproto.xyz/API_docs/types/NearestDc.html
     * @psalm-impure
     */
    public function getNearestDc(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Returns information on update availability for the current application.
     *
     * @param string $source Source
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'help.appUpdate', can_not_skip: bool, id: int, version: string, text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, url?: string, sticker?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'help.noAppUpdate'} @see https://docs.madelineproto.xyz/API_docs/types/help.AppUpdate.html
     * @psalm-impure
     */
    public function getAppUpdate(string|null $source = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Returns localized text of a text message with an invitation.
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'help.inviteText', message: string} @see https://docs.madelineproto.xyz/API_docs/types/help.InviteText.html
     * @psalm-impure
     */
    public function getInviteText(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Returns the support user for the "ask a question" feature.
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'help.support', phone_number: string, user: array|int|string} @see https://docs.madelineproto.xyz/API_docs/types/help.Support.html
     * @psalm-impure
     */
    public function getSupport(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Informs the server about the number of pending bot updates if they haven't been processed for a long time; for bots only
     *
     * @param int $pending_updates_count Number of pending updates
     * @param string $message Error message, if present
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function setBotUpdatesStatus(int|null $pending_updates_count = 0, string|null $message = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Get configuration for [CDN](https://core.telegram.org/cdn) file downloads.
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'cdnConfig', public_keys: list<array{_: 'cdnPublicKey', dc_id: int, public_key: string}>} @see https://docs.madelineproto.xyz/API_docs/types/CdnConfig.html
     * @psalm-impure
     */
    public function getCdnConfig(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get recently used `t.me` links.
     * 
     * When installing official applications from "Download Telegram" buttons present in [t.me](https://t.me) pages, a referral parameter is passed to applications after installation.  
     * If, after downloading the application, the user creates a new account (instead of logging into an existing one), the referral parameter should be imported using this method, which returns the [t.me](https://t.me) pages the user recently opened, before installing Telegram.
     *
     * @param string $referer Referrer
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'help.recentMeUrls', urls: list<array{_: 'recentMeUrlUnknown', url: string}|array{_: 'recentMeUrlUser', url: string, user_id: int}|array{_: 'recentMeUrlChat', url: string, chat_id: array|int|string}|array{_: 'recentMeUrlChatInvite', chat_invite: array{_: 'chatInviteAlready', chat: array|int|string}|array{_: 'chatInvite', channel: bool, broadcast: bool, public: bool, megagroup: bool, request_needed: bool, verified: bool, scam: bool, fake: bool, can_refulfill_subscription: bool, title: string, about?: string, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, participants_count: int, participants?: list<array|int|string>, color: int, subscription_pricing?: array{_: 'starsSubscriptionPricing', period: int, amount: int}, subscription_form_id?: int, bot_verification?: array{_: 'botVerification', description: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, bot_id: int, icon: int}}|array{_: 'chatInvitePeek', chat: array|int|string, expires: int}, url: string}|array{_: 'recentMeUrlStickerSet', set: array{_: 'stickerSetCovered', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: array, local_id: array, secret: array}|array{_: 'fileLocation', dc_id: array, volume_id: array, local_id: array, secret: array}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: array, local_id: array, secret: array}|array{_: 'fileLocation', dc_id: array, volume_id: array, local_id: array, secret: array}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}, cover: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'stickerSetMultiCovered', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: array, local_id: array, secret: array}|array{_: 'fileLocation', dc_id: array, volume_id: array, local_id: array, secret: array}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: array, local_id: array, secret: array}|array{_: 'fileLocation', dc_id: array, volume_id: array, local_id: array, secret: array}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}, covers: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}|array{_: 'stickerSetFullCovered', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: array, local_id: array, secret: array}|array{_: 'fileLocation', dc_id: array, volume_id: array, local_id: array, secret: array}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: array, local_id: array, secret: array}|array{_: 'fileLocation', dc_id: array, volume_id: array, local_id: array, secret: array}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}, packs: list<array{_: 'stickerPack', emoticon: string, documents: list<int>}>, keywords: list<array{_: 'stickerKeyword', document_id: int, keyword: list<string>}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}|array{_: 'stickerSetNoCovered', set: array{_: 'stickerSet', archived: bool, official: bool, masks: bool, emojis: bool, text_color: bool, channel_emoji_status: bool, creator: bool, installed_date?: int, id: int, access_hash: int, title: string, short_name: string, thumbs?: list<array{_: 'photoSizeEmpty', type: string}|array{_: 'photoSize', type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', type: string, w: int, h: int, bytes: string}|array{_: 'photoStrippedSize', type: string, bytes: string}|array{_: 'photoSizeProgressive', type: string, w: int, h: int, sizes: list<int>}|array{_: 'photoPathSize', type: string, bytes: string}|array{_: 'photoSize', location: array{_: 'fileLocationUnavailable', volume_id: array, local_id: array, secret: array}|array{_: 'fileLocation', dc_id: array, volume_id: array, local_id: array, secret: array}, type: string, w: int, h: int, size: int}|array{_: 'photoCachedSize', location: array{_: 'fileLocationUnavailable', volume_id: array, local_id: array, secret: array}|array{_: 'fileLocation', dc_id: array, volume_id: array, local_id: array, secret: array}, type: string, w: int, h: int, bytes: string}>, thumb_dc_id?: int, thumb_version?: int, thumb_document_id?: int, count: int, hash: int}}, url: string}>, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/help.RecentMeUrls.html
     * @psalm-impure
     */
    public function getRecentMeUrls(string|null $referer = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Look for updates of telegram's terms of service
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'help.termsOfServiceUpdateEmpty', expires: int}|array{_: 'help.termsOfServiceUpdate', terms_of_service: array{_: 'help.termsOfService', id: mixed, popup: bool, text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, min_age_confirm?: int}, expires: int} @see https://docs.madelineproto.xyz/API_docs/types/help.TermsOfServiceUpdate.html
     * @psalm-impure
     */
    public function getTermsOfServiceUpdate(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Accept the new terms of service
     *
     * @param mixed $id Any JSON-encodable data
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function acceptTermsOfService(mixed $id, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Get info about an unsupported deep link, see [here for more info Â»](https://core.telegram.org/api/links#unsupported-links).
     *
     * @param string $path Path component of a `tg:` link
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'help.deepLinkInfoEmpty'}|array{_: 'help.deepLinkInfo', update_app: bool, message: string, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>} @see https://docs.madelineproto.xyz/API_docs/types/help.DeepLinkInfo.html
     * @psalm-impure
     */
    public function getDeepLinkInfo(string|null $path = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get app-specific configuration, see [client configuration](https://core.telegram.org/api/config#client-configuration) for more info on the result.
     *
     * @param int $hash 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'help.appConfigNotModified'}|array{_: 'help.appConfig', config: mixed, hash: int} @see https://docs.madelineproto.xyz/API_docs/types/help.AppConfig.html
     * @psalm-impure
     */
    public function getAppConfig(int|null $hash = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Saves logs of application on the server.
     *
     * @param list<array{_: 'inputAppEvent', time: float, data: mixed, type?: string, peer?: int}>|array<never, never> $events Array of List of input events @see https://docs.madelineproto.xyz/API_docs/types/InputAppEvent.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function saveAppLog(array $events = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Get [passport](https://core.telegram.org/passport) configuration
     *
     * @param int $hash 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'help.passportConfigNotModified'}|array{_: 'help.passportConfig', countries_langs: mixed, hash: int} @see https://docs.madelineproto.xyz/API_docs/types/help.PassportConfig.html
     * @psalm-impure
     */
    public function getPassportConfig(int|null $hash = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get localized name of the telegram support user
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'help.supportName', name: string} @see https://docs.madelineproto.xyz/API_docs/types/help.SupportName.html
     * @psalm-impure
     */
    public function getSupportName(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Can only be used by TSF members to obtain internal information.
     *
     * @param array|int|string $user_id User ID @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'help.userInfoEmpty'}|array{_: 'help.userInfo', message: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, author: string, date: int} @see https://docs.madelineproto.xyz/API_docs/types/help.UserInfo.html
     * @psalm-impure
     */
    public function getUserInfo(array|int|string|null $user_id = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Internal use
     *
     * @param array|int|string $user_id User @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param string $message Message
     * @param list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>|array<never, never> $entities Array of [Message entities for styled text](https://core.telegram.org/api/entities) @see https://docs.madelineproto.xyz/API_docs/types/MessageEntity.html
     * @param \danog\MadelineProto\ParseMode $parse_mode Whether to parse HTML or Markdown markup in the message
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'help.userInfoEmpty'}|array{_: 'help.userInfo', message: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, author: string, date: int} @see https://docs.madelineproto.xyz/API_docs/types/help.UserInfo.html
     * @psalm-impure
     */
    public function editUserInfo(array|int|string|null $user_id = null, string|null $message = '', array $entities = [], \danog\MadelineProto\ParseMode $parse_mode = \danog\MadelineProto\ParseMode::TEXT, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Returns a set of useful suggestions and PSA/MTProxy sponsored peers, see [here Â»](https://core.telegram.org/api/config#suggestions) for more info.
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'help.promoDataEmpty', expires: int}|array{_: 'help.promoData', proxy: bool, expires: int, peer?: array|int|string, psa_type?: string, psa_message?: string, pending_suggestions: list<string>, dismissed_suggestions: list<string>, custom_pending_suggestion?: array{_: 'pendingSuggestion', title: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}, description: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}, suggestion: string, url: string}, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/help.PromoData.html
     * @psalm-impure
     */
    public function getPromoData(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Hide MTProxy/Public Service Announcement information
     *
     * @param array|int|string $peer Peer to hide @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function hidePromoData(array|int|string|null $peer = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Dismiss a [suggestion, see here for more info Â»](https://core.telegram.org/api/config#suggestions).
     *
     * @param array|int|string $peer In the case of pending suggestions in [channels](https://docs.madelineproto.xyz/API_docs/constructors/channelFull.html), the channel ID. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param string $suggestion [Suggestion, see here for more info Â»](https://core.telegram.org/api/config#suggestions).
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function dismissSuggestion(array|int|string|null $peer = null, string|null $suggestion = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Get name, ISO code, localized name and phone codes/patterns of all available countries
     *
     * @param string $lang_code Language code of the current user
     * @param int $hash 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'help.countriesListNotModified'}|array{_: 'help.countriesList', countries: list<array{_: 'help.country', hidden: bool, iso2: string, default_name: string, name?: string, country_codes: list<array{_: 'help.countryCode', country_code: string, prefixes?: list<string>, patterns?: list<string>}>}>, hash: int} @see https://docs.madelineproto.xyz/API_docs/types/help.CountriesList.html
     * @psalm-impure
     */
    public function getCountriesList(string|null $lang_code = '', int|null $hash = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get Telegram Premium promotion information
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'help.premiumPromo', status_text: string, status_entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, video_sections: list<string>, videos: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, period_options: list<array{_: 'premiumSubscriptionOption', current: bool, can_purchase_upgrade: bool, transaction?: string, months: int, currency: string, amount: int, bot_url: string, store_product?: string}>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/help.PremiumPromo.html
     * @psalm-impure
     */
    public function getPremiumPromo(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get the set of [accent color palettes Â»](https://core.telegram.org/api/colors) that can be used for message accents.
     *
     * @param int $hash 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'help.peerColorsNotModified'}|array{_: 'help.peerColors', hash: int, colors: list<array{_: 'help.peerColorOption', hidden: bool, color_id: int, colors?: array{_: 'help.peerColorSet', colors: list<int>}|array{_: 'help.peerColorProfileSet', palette_colors: list<int>, bg_colors: list<int>, story_colors: list<int>}, dark_colors?: array{_: 'help.peerColorSet', colors: list<int>}|array{_: 'help.peerColorProfileSet', palette_colors: list<int>, bg_colors: list<int>, story_colors: list<int>}, channel_min_level?: int, group_min_level?: int}>} @see https://docs.madelineproto.xyz/API_docs/types/help.PeerColors.html
     * @psalm-impure
     */
    public function getPeerColors(int|null $hash = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get the set of [accent color palettes Â»](https://core.telegram.org/api/colors) that can be used in profile page backgrounds.
     *
     * @param int $hash 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'help.peerColorsNotModified'}|array{_: 'help.peerColors', hash: int, colors: list<array{_: 'help.peerColorOption', hidden: bool, color_id: int, colors?: array{_: 'help.peerColorSet', colors: list<int>}|array{_: 'help.peerColorProfileSet', palette_colors: list<int>, bg_colors: list<int>, story_colors: list<int>}, dark_colors?: array{_: 'help.peerColorSet', colors: list<int>}|array{_: 'help.peerColorProfileSet', palette_colors: list<int>, bg_colors: list<int>, story_colors: list<int>}, channel_min_level?: int, group_min_level?: int}>} @see https://docs.madelineproto.xyz/API_docs/types/help.PeerColors.html
     * @psalm-impure
     */
    public function getPeerProfileColors(int|null $hash = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Returns timezone information that may be used elsewhere in the API, such as to set [Telegram Business opening hours Â»](https://core.telegram.org/api/business#opening-hours).
     *
     * @param int $hash 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'help.timezonesListNotModified'}|array{_: 'help.timezonesList', timezones: list<array{_: 'timezone', id: string, name: string, utc_offset: int}>, hash: int} @see https://docs.madelineproto.xyz/API_docs/types/help.TimezonesList.html
     * @psalm-impure
     */
    public function getTimezonesList(int|null $hash = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;
}
<?php
/**
 * This file is automatic generated by build_docs.php file
 * and is used only for autocomplete in multiple IDE
 * don't modify manually.
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\Namespace;

/** @psalm-mutable */
interface Account
{
    /**
     * Register device to receive [PUSH notifications](https://core.telegram.org/api/push-updates)
     *
     * @param bool $app_sandbox If [(boolTrue)](https://docs.madelineproto.xyz/API_docs/constructors/boolTrue.html) is transmitted, a sandbox-certificate will be used during transmission.
     * @param bool $no_muted Avoid receiving (silent and invisible background) notifications. Useful to save battery.
     * @param int $token_type Device token type, see [PUSH updates](https://core.telegram.org/api/push-updates#subscribing-to-notifications) for the possible values.
     * @param string $token Device token, see [PUSH updates](https://core.telegram.org/api/push-updates#subscribing-to-notifications) for the possible values.
     * @param string $secret For FCM and APNS VoIP, optional encryption key used to encrypt push notifications
     * @param list<int>|array<never, never> $other_uids List of user identifiers of other users currently using the client
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function registerDevice(bool $app_sandbox, bool|null $no_muted = null, int|null $token_type = 0, string|null $token = '', string|null $secret = '', array $other_uids = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Deletes a device by its token, stops sending PUSH-notifications to it.
     *
     * @param int $token_type Device token type, see [PUSH updates](https://core.telegram.org/api/push-updates#subscribing-to-notifications) for the possible values.
     * @param string $token Device token, see [PUSH updates](https://core.telegram.org/api/push-updates#subscribing-to-notifications) for the possible values.
     * @param list<int>|array<never, never> $other_uids List of user identifiers of other users currently using the client
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function unregisterDevice(int|null $token_type = 0, string|null $token = '', array $other_uids = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Edits notification settings from a given user/group, from all users/all groups.
     *
     * @param array{_: 'inputNotifyPeer', peer?: array|int|string}|array{_: 'inputNotifyUsers'}|array{_: 'inputNotifyChats'}|array{_: 'inputNotifyBroadcasts'}|array{_: 'inputNotifyForumTopic', peer?: array|int|string, top_msg_id?: int}|array{_: 'inputNotifyCommunity', community?: array|int|string} $peer Notification source @see https://docs.madelineproto.xyz/API_docs/types/InputNotifyPeer.html
     * @param array{_: 'inputPeerNotifySettings', show_previews?: bool, silent?: bool, mute_until?: int, sound?: array{_: 'notificationSoundDefault'}|array{_: 'notificationSoundNone'}|array{_: 'notificationSoundLocal', title?: string, data?: string}|array{_: 'notificationSoundRingtone', id?: int}, stories_muted?: bool, stories_hide_sender?: bool, stories_sound?: array{_: 'notificationSoundDefault'}|array{_: 'notificationSoundNone'}|array{_: 'notificationSoundLocal', title?: string, data?: string}|array{_: 'notificationSoundRingtone', id?: int}} $settings Notification settings @see https://docs.madelineproto.xyz/API_docs/types/InputPeerNotifySettings.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function updateNotifySettings(array $peer, array $settings, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Gets current notification settings for a given user/group, from all users/all groups.
     *
     * @param array{_: 'inputNotifyPeer', peer?: array|int|string}|array{_: 'inputNotifyUsers'}|array{_: 'inputNotifyChats'}|array{_: 'inputNotifyBroadcasts'}|array{_: 'inputNotifyForumTopic', peer?: array|int|string, top_msg_id?: int}|array{_: 'inputNotifyCommunity', community?: array|int|string} $peer Notification source @see https://docs.madelineproto.xyz/API_docs/types/InputNotifyPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'peerNotifySettings', show_previews?: bool, silent?: bool, mute_until?: int, ios_sound?: array{_: 'notificationSoundDefault'}|array{_: 'notificationSoundNone'}|array{_: 'notificationSoundLocal', title: string, data: string}|array{_: 'notificationSoundRingtone', id: int}, android_sound?: array{_: 'notificationSoundDefault'}|array{_: 'notificationSoundNone'}|array{_: 'notificationSoundLocal', title: string, data: string}|array{_: 'notificationSoundRingtone', id: int}, other_sound?: array{_: 'notificationSoundDefault'}|array{_: 'notificationSoundNone'}|array{_: 'notificationSoundLocal', title: string, data: string}|array{_: 'notificationSoundRingtone', id: int}, stories_muted?: bool, stories_hide_sender?: bool, stories_ios_sound?: array{_: 'notificationSoundDefault'}|array{_: 'notificationSoundNone'}|array{_: 'notificationSoundLocal', title: string, data: string}|array{_: 'notificationSoundRingtone', id: int}, stories_android_sound?: array{_: 'notificationSoundDefault'}|array{_: 'notificationSoundNone'}|array{_: 'notificationSoundLocal', title: string, data: string}|array{_: 'notificationSoundRingtone', id: int}, stories_other_sound?: array{_: 'notificationSoundDefault'}|array{_: 'notificationSoundNone'}|array{_: 'notificationSoundLocal', title: string, data: string}|array{_: 'notificationSoundRingtone', id: int}} @see https://docs.madelineproto.xyz/API_docs/types/PeerNotifySettings.html
     * @psalm-impure
     */
    public function getNotifySettings(array $peer, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Resets all notification settings from users and groups.
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function resetNotifySettings(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Updates user profile.
     *
     * @param string $first_name New user first name
     * @param string $last_name New user last name
     * @param string $about New bio
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @param ?string $businessConnectionId Business connection ID, received through an updateBotBusinessConnect update.
     * @return array|int|string @see https://docs.madelineproto.xyz/API_docs/types/User.html
     * @psalm-impure
     */
    public function updateProfile(string|null $first_name = null, string|null $last_name = null, string|null $about = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null, ?string $businessConnectionId = null): array|int|string;

    /**
     * Updates online user status.
     *
     * @param bool $offline If [(boolTrue)](https://docs.madelineproto.xyz/API_docs/constructors/boolTrue.html) is transmitted, user status will change to [(userStatusOffline)](https://docs.madelineproto.xyz/API_docs/constructors/userStatusOffline.html).
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function updateStatus(bool $offline, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Returns a list of available [wallpapers](https://core.telegram.org/api/wallpapers).
     *
     * @param list<int|string>|array<never, never> $hash Array of [Hash used for caching, for more info click here](https://core.telegram.org/api/offsets#hash-generation). @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'account.wallPapersNotModified'}|array{_: 'account.wallPapers', hash: list<int|string>, wallpapers: list<array{_: 'wallPaper', id: int, creator: bool, default: bool, pattern: bool, dark: bool, access_hash: int, slug: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, settings?: array{_: 'wallPaperSettings', blur: bool, motion: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string}}|array{_: 'wallPaperNoFile', id: int, default: bool, dark: bool, settings?: array{_: 'wallPaperSettings', blur: bool, motion: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string}}>} @see https://docs.madelineproto.xyz/API_docs/types/account.WallPapers.html
     * @psalm-impure
     */
    public function getWallPapers(array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Report a peer for violation of telegram's Terms of Service
     *
     * @param array{_: 'inputReportReasonSpam'}|array{_: 'inputReportReasonViolence'}|array{_: 'inputReportReasonPornography'}|array{_: 'inputReportReasonChildAbuse'}|array{_: 'inputReportReasonOther'}|array{_: 'inputReportReasonCopyright'}|array{_: 'inputReportReasonGeoIrrelevant'}|array{_: 'inputReportReasonFake'}|array{_: 'inputReportReasonIllegalDrugs'}|array{_: 'inputReportReasonPersonalDetails'} $reason The reason why this peer is being reported @see https://docs.madelineproto.xyz/API_docs/types/ReportReason.html
     * @param array|int|string $peer The peer to report @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param string $message Comment for report moderation
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function reportPeer(array $reason, array|int|string|null $peer = null, string|null $message = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Validates a username and checks availability.
     *
     * @param string $username username<br>Accepted characters: A-z (case-insensitive), 0-9 and underscores.<br>Length: 5-32 characters.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function checkUsername(string|null $username = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Changes username for the current user.
     *
     * @param string $username username or empty string if username is to be removed<br>Accepted characters: a-z (case-insensitive), 0-9 and underscores.<br>Length: 5-32 characters.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array|int|string @see https://docs.madelineproto.xyz/API_docs/types/User.html
     * @psalm-impure
     */
    public function updateUsername(string|null $username = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array|int|string;

    /**
     * Get privacy settings of current account
     *
     * @param array{_: 'inputPrivacyKeyStatusTimestamp'}|array{_: 'inputPrivacyKeyChatInvite'}|array{_: 'inputPrivacyKeyPhoneCall'}|array{_: 'inputPrivacyKeyPhoneP2P'}|array{_: 'inputPrivacyKeyForwards'}|array{_: 'inputPrivacyKeyProfilePhoto'}|array{_: 'inputPrivacyKeyPhoneNumber'}|array{_: 'inputPrivacyKeyAddedByPhone'}|array{_: 'inputPrivacyKeyVoiceMessages'}|array{_: 'inputPrivacyKeyAbout'}|array{_: 'inputPrivacyKeyBirthday'}|array{_: 'inputPrivacyKeyStarGiftsAutoSave'}|array{_: 'inputPrivacyKeyNoPaidMessages'}|array{_: 'inputPrivacyKeySavedMusic'} $key Peer category whose privacy settings should be fetched @see https://docs.madelineproto.xyz/API_docs/types/InputPrivacyKey.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'account.privacyRules', rules: list<array{_: 'privacyValueAllowContacts'}|array{_: 'privacyValueAllowAll'}|array{_: 'privacyValueAllowUsers', users: list<int>}|array{_: 'privacyValueDisallowContacts'}|array{_: 'privacyValueDisallowAll'}|array{_: 'privacyValueDisallowUsers', users: list<int>}|array{_: 'privacyValueAllowChatParticipants', chats: list<int>}|array{_: 'privacyValueDisallowChatParticipants', chats: list<int>}|array{_: 'privacyValueAllowCloseFriends'}|array{_: 'privacyValueAllowPremium'}|array{_: 'privacyValueAllowBots'}|array{_: 'privacyValueDisallowBots'}>, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/account.PrivacyRules.html
     * @psalm-impure
     */
    public function getPrivacy(array $key, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Change privacy settings of current account
     *
     * @param array{_: 'inputPrivacyKeyStatusTimestamp'}|array{_: 'inputPrivacyKeyChatInvite'}|array{_: 'inputPrivacyKeyPhoneCall'}|array{_: 'inputPrivacyKeyPhoneP2P'}|array{_: 'inputPrivacyKeyForwards'}|array{_: 'inputPrivacyKeyProfilePhoto'}|array{_: 'inputPrivacyKeyPhoneNumber'}|array{_: 'inputPrivacyKeyAddedByPhone'}|array{_: 'inputPrivacyKeyVoiceMessages'}|array{_: 'inputPrivacyKeyAbout'}|array{_: 'inputPrivacyKeyBirthday'}|array{_: 'inputPrivacyKeyStarGiftsAutoSave'}|array{_: 'inputPrivacyKeyNoPaidMessages'}|array{_: 'inputPrivacyKeySavedMusic'} $key New privacy rule @see https://docs.madelineproto.xyz/API_docs/types/InputPrivacyKey.html
     * @param list<array{_: 'inputPrivacyValueAllowContacts'}|array{_: 'inputPrivacyValueAllowAll'}|array{_: 'inputPrivacyValueAllowUsers', users?: list<array|int|string>}|array{_: 'inputPrivacyValueDisallowContacts'}|array{_: 'inputPrivacyValueDisallowAll'}|array{_: 'inputPrivacyValueDisallowUsers', users?: list<array|int|string>}|array{_: 'inputPrivacyValueAllowChatParticipants', chats?: list<int>}|array{_: 'inputPrivacyValueDisallowChatParticipants', chats?: list<int>}|array{_: 'inputPrivacyValueAllowCloseFriends'}|array{_: 'inputPrivacyValueAllowPremium'}|array{_: 'inputPrivacyValueAllowBots'}|array{_: 'inputPrivacyValueDisallowBots'}>|array<never, never> $rules Array of Peers to which the privacy rule will apply. @see https://docs.madelineproto.xyz/API_docs/types/InputPrivacyRule.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'account.privacyRules', rules: list<array{_: 'privacyValueAllowContacts'}|array{_: 'privacyValueAllowAll'}|array{_: 'privacyValueAllowUsers', users: list<int>}|array{_: 'privacyValueDisallowContacts'}|array{_: 'privacyValueDisallowAll'}|array{_: 'privacyValueDisallowUsers', users: list<int>}|array{_: 'privacyValueAllowChatParticipants', chats: list<int>}|array{_: 'privacyValueDisallowChatParticipants', chats: list<int>}|array{_: 'privacyValueAllowCloseFriends'}|array{_: 'privacyValueAllowPremium'}|array{_: 'privacyValueAllowBots'}|array{_: 'privacyValueDisallowBots'}>, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/account.PrivacyRules.html
     * @psalm-impure
     */
    public function setPrivacy(array $key, array $rules = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Delete the user's account from the telegram servers.
     * 
     * Can also be used to delete the account of a user that provided the login code, but forgot the 2FA password and no recovery method is configured, see [here Â»](https://core.telegram.org/api/srp#password-recovery) for more info on password recovery, and [here Â»](https://core.telegram.org/api/account-deletion) for more info on account deletion.
     *
     * @param string $reason Why is the account being deleted, can be empty
     * @param string|array $password [2FA password](https://core.telegram.org/api/srp): this field can be omitted even for accounts with 2FA enabled: in this case account account deletion will be delayed by 7 days [as specified in the docs Â»](https://core.telegram.org/api/account-deletion) @see https://docs.madelineproto.xyz/API_docs/types/InputCheckPasswordSRP.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function deleteAccount(string|null $reason = '', string|array|null $password = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Get days to live of account
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'accountDaysTTL', days: int} @see https://docs.madelineproto.xyz/API_docs/types/AccountDaysTTL.html
     * @psalm-impure
     */
    public function getAccountTTL(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Set account self-destruction period
     *
     * @param array{_: 'accountDaysTTL', days?: int} $ttl Time to live in days @see https://docs.madelineproto.xyz/API_docs/types/AccountDaysTTL.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function setAccountTTL(array $ttl, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Verify a new phone number to associate to the current account
     *
     * @param array{_: 'codeSettings', allow_flashcall?: bool, current_number?: bool, allow_app_hash?: bool, allow_missed_call?: bool, allow_firebase?: bool, unknown_number?: bool, logout_tokens?: list<string>, token?: string, app_sandbox?: bool} $settings Phone code settings @see https://docs.madelineproto.xyz/API_docs/types/CodeSettings.html
     * @param string $phone_number New phone number
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'auth.sentCode', type: array{_: 'auth.sentCodeTypeApp', length: int}|array{_: 'auth.sentCodeTypeSms', length: int}|array{_: 'auth.sentCodeTypeCall', length: int}|array{_: 'auth.sentCodeTypeFlashCall', pattern: string}|array{_: 'auth.sentCodeTypeMissedCall', prefix: string, length: int}|array{_: 'auth.sentCodeTypeEmailCode', apple_signin_allowed: bool, google_signin_allowed: bool, email_pattern: string, length: int, reset_available_period?: int, reset_pending_date?: int}|array{_: 'auth.sentCodeTypeSetUpEmailRequired', apple_signin_allowed: bool, google_signin_allowed: bool}|array{_: 'auth.sentCodeTypeFragmentSms', url: string, length: int}|array{_: 'auth.sentCodeTypeFirebaseSms', nonce?: string, play_integrity_project_id?: int, play_integrity_nonce?: string, receipt?: string, push_timeout?: int, length: int}|array{_: 'auth.sentCodeTypeSmsWord', beginning?: string}|array{_: 'auth.sentCodeTypeSmsPhrase', beginning?: string}, phone_code_hash: string, next_type?: array{_: 'auth.codeTypeSms'}|array{_: 'auth.codeTypeCall'}|array{_: 'auth.codeTypeFlashCall'}|array{_: 'auth.codeTypeMissedCall'}|array{_: 'auth.codeTypeFragmentSms'}, timeout?: int}|array{_: 'auth.sentCodeSuccess', authorization: array{_: 'auth.authorization', setup_password_required: bool, otherwise_relogin_days?: int, tmp_sessions?: int, future_auth_token?: string, user: array|int|string}|array{_: 'auth.authorizationSignUpRequired', terms_of_service?: array{_: 'help.termsOfService', id: mixed, popup: bool, text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, min_age_confirm?: int}}}|array{_: 'auth.sentCodePaymentRequired', store_product: string, phone_code_hash: string, support_email_address: string, support_email_subject: string, premium_days: int, currency: string, amount: int} @see https://docs.madelineproto.xyz/API_docs/types/auth.SentCode.html
     * @psalm-impure
     */
    public function sendChangePhoneCode(array $settings, string|null $phone_number = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Change the phone number of the current account
     *
     * @param string $phone_number New phone number
     * @param string $phone_code_hash Phone code hash received when calling [account.sendChangePhoneCode](https://docs.madelineproto.xyz/API_docs/methods/account.sendChangePhoneCode.html)
     * @param string $phone_code Phone code received when calling [account.sendChangePhoneCode](https://docs.madelineproto.xyz/API_docs/methods/account.sendChangePhoneCode.html)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array|int|string @see https://docs.madelineproto.xyz/API_docs/types/User.html
     * @psalm-impure
     */
    public function changePhone(string|null $phone_number = '', string|null $phone_code_hash = '', string|null $phone_code = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array|int|string;

    /**
     * When client-side passcode lock feature is enabled, will not show message texts in incoming [PUSH notifications](https://core.telegram.org/api/push-updates).
     *
     * @param int $period Inactivity period after which to start hiding message texts in [PUSH notifications](https://core.telegram.org/api/push-updates).
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function updateDeviceLocked(int|null $period = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Get logged-in sessions
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @param ?int $takeoutId Optional takeout ID, generated using account.initTakeoutSession, see [the takeout docs](https://core.telegram.org/api/takeout) for more info.
     * @return array{_: 'account.authorizations', authorization_ttl_days: int, authorizations: list<array{_: 'authorization', current: bool, official_app: bool, password_pending: bool, encrypted_requests_disabled: bool, call_requests_disabled: bool, unconfirmed: bool, hash: list<int|string>, device_model: string, platform: string, system_version: string, api_id: int, app_name: string, app_version: string, date_created: int, date_active: int, ip: string, country: string, region: string}>} @see https://docs.madelineproto.xyz/API_docs/types/account.Authorizations.html
     * @psalm-impure
     */
    public function getAuthorizations(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null, ?int $takeoutId = null): array;

    /**
     * Log out an active [authorized session](https://core.telegram.org/api/auth) by its hash
     *
     * @param list<int|string>|array<never, never> $hash Array of Session hash @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function resetAuthorization(array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Obtain configuration for two-factor authorization with password
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'account.password', new_algo: array{_: 'passwordKdfAlgoUnknown'}|array{_: 'passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow', salt1: string, salt2: string, g: int, p: string}, new_secure_algo: array{_: 'securePasswordKdfAlgoUnknown'}|array{_: 'securePasswordKdfAlgoPBKDF2HMACSHA512iter100000', salt: string}|array{_: 'securePasswordKdfAlgoSHA512', salt: string}, has_recovery: bool, has_secure_values: bool, has_password: bool, current_algo?: array{_: 'passwordKdfAlgoUnknown'}|array{_: 'passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow', salt1: string, salt2: string, g: int, p: string}, srp_B?: string, srp_id?: int, hint?: string, email_unconfirmed_pattern?: string, secure_random: string, pending_reset_date?: int, login_email_pattern?: string} @see https://docs.madelineproto.xyz/API_docs/types/account.Password.html
     * @psalm-impure
     */
    public function getPassword(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Send confirmation code to cancel account deletion, for more info [click here Â»](https://core.telegram.org/api/account-deletion)
     *
     * @param array{_: 'codeSettings', allow_flashcall?: bool, current_number?: bool, allow_app_hash?: bool, allow_missed_call?: bool, allow_firebase?: bool, unknown_number?: bool, logout_tokens?: list<string>, token?: string, app_sandbox?: bool} $settings Phone code settings @see https://docs.madelineproto.xyz/API_docs/types/CodeSettings.html
     * @param string $hash The hash from the service notification, for more info [click here Â»](https://core.telegram.org/api/account-deletion)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'auth.sentCode', type: array{_: 'auth.sentCodeTypeApp', length: int}|array{_: 'auth.sentCodeTypeSms', length: int}|array{_: 'auth.sentCodeTypeCall', length: int}|array{_: 'auth.sentCodeTypeFlashCall', pattern: string}|array{_: 'auth.sentCodeTypeMissedCall', prefix: string, length: int}|array{_: 'auth.sentCodeTypeEmailCode', apple_signin_allowed: bool, google_signin_allowed: bool, email_pattern: string, length: int, reset_available_period?: int, reset_pending_date?: int}|array{_: 'auth.sentCodeTypeSetUpEmailRequired', apple_signin_allowed: bool, google_signin_allowed: bool}|array{_: 'auth.sentCodeTypeFragmentSms', url: string, length: int}|array{_: 'auth.sentCodeTypeFirebaseSms', nonce?: string, play_integrity_project_id?: int, play_integrity_nonce?: string, receipt?: string, push_timeout?: int, length: int}|array{_: 'auth.sentCodeTypeSmsWord', beginning?: string}|array{_: 'auth.sentCodeTypeSmsPhrase', beginning?: string}, phone_code_hash: string, next_type?: array{_: 'auth.codeTypeSms'}|array{_: 'auth.codeTypeCall'}|array{_: 'auth.codeTypeFlashCall'}|array{_: 'auth.codeTypeMissedCall'}|array{_: 'auth.codeTypeFragmentSms'}, timeout?: int}|array{_: 'auth.sentCodeSuccess', authorization: array{_: 'auth.authorization', setup_password_required: bool, otherwise_relogin_days?: int, tmp_sessions?: int, future_auth_token?: string, user: array|int|string}|array{_: 'auth.authorizationSignUpRequired', terms_of_service?: array{_: 'help.termsOfService', id: mixed, popup: bool, text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, min_age_confirm?: int}}}|array{_: 'auth.sentCodePaymentRequired', store_product: string, phone_code_hash: string, support_email_address: string, support_email_subject: string, premium_days: int, currency: string, amount: int} @see https://docs.madelineproto.xyz/API_docs/types/auth.SentCode.html
     * @psalm-impure
     */
    public function sendConfirmPhoneCode(array $settings, string|null $hash = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Confirm a phone number to cancel account deletion, for more info [click here Â»](https://core.telegram.org/api/account-deletion)
     *
     * @param string $phone_code_hash Phone code hash, for more info [click here Â»](https://core.telegram.org/api/account-deletion)
     * @param string $phone_code SMS code, for more info [click here Â»](https://core.telegram.org/api/account-deletion)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function confirmPhone(string|null $phone_code_hash = '', string|null $phone_code = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Get temporary payment password
     *
     * @param string|array $password SRP password parameters @see https://docs.madelineproto.xyz/API_docs/types/InputCheckPasswordSRP.html
     * @param int $period Time during which the temporary password will be valid, in seconds; should be between 60 and 86400
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'account.tmpPassword', tmp_password: string, valid_until: int} @see https://docs.madelineproto.xyz/API_docs/types/account.TmpPassword.html
     * @psalm-impure
     */
    public function getTmpPassword(string|array $password, int|null $period = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get web [login widget](https://core.telegram.org/widgets/login) authorizations
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @param ?int $takeoutId Optional takeout ID, generated using account.initTakeoutSession, see [the takeout docs](https://core.telegram.org/api/takeout) for more info.
     * @return array{_: 'account.webAuthorizations', authorizations: list<array{_: 'webAuthorization', hash: list<int|string>, bot_id: int, domain: string, browser: string, platform: string, date_created: int, date_active: int, ip: string, region: string}>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/account.WebAuthorizations.html
     * @psalm-impure
     */
    public function getWebAuthorizations(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null, ?int $takeoutId = null): array;

    /**
     * Log out an active web [telegram login](https://core.telegram.org/widgets/login) session
     *
     * @param list<int|string>|array<never, never> $hash Array of [Session](https://docs.madelineproto.xyz/API_docs/constructors/webAuthorization.html) hash @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function resetWebAuthorization(array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Reset all active web [telegram login](https://core.telegram.org/widgets/login) sessions
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function resetWebAuthorizations(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Get all saved [Telegram Passport](https://core.telegram.org/passport) documents, [for more info see the passport docs Â»](https://core.telegram.org/passport/encryption#encryption)
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return list<array{_: 'secureValue', type: array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}, data?: array{_: 'secureData', data: string, data_hash: string, secret: string}, front_side?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, reverse_side?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, selfie?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, translation?: list<array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}>, files?: list<array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}>, plain_data?: array{_: 'securePlainPhone', phone: string}|array{_: 'securePlainEmail', email: string}, hash: string}> Array of  @see https://docs.madelineproto.xyz/API_docs/types/SecureValue.html
     * @psalm-impure
     */
    public function getAllSecureValues(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get saved [Telegram Passport](https://core.telegram.org/passport) document, [for more info see the passport docs Â»](https://core.telegram.org/passport/encryption#encryption)
     *
     * @param list<array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}>|array<never, never> $types Array of Requested value types @see https://docs.madelineproto.xyz/API_docs/types/SecureValueType.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return list<array{_: 'secureValue', type: array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}, data?: array{_: 'secureData', data: string, data_hash: string, secret: string}, front_side?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, reverse_side?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, selfie?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, translation?: list<array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}>, files?: list<array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}>, plain_data?: array{_: 'securePlainPhone', phone: string}|array{_: 'securePlainEmail', email: string}, hash: string}> Array of  @see https://docs.madelineproto.xyz/API_docs/types/SecureValue.html
     * @psalm-impure
     */
    public function getSecureValue(array $types = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Securely save [Telegram Passport](https://core.telegram.org/passport) document, [for more info see the passport docs Â»](https://core.telegram.org/passport/encryption#encryption)
     *
     * @param array{_: 'inputSecureValue', type: array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}, data?: array{_: 'secureData', data?: string, data_hash?: string, secret?: string}, front_side?: array{_: 'inputSecureFileUploaded', id?: int, parts?: int, md5_checksum?: string, file_hash?: string, secret?: string}|array{_: 'inputSecureFile', id?: int, access_hash?: int}, reverse_side?: array{_: 'inputSecureFileUploaded', id?: int, parts?: int, md5_checksum?: string, file_hash?: string, secret?: string}|array{_: 'inputSecureFile', id?: int, access_hash?: int}, selfie?: array{_: 'inputSecureFileUploaded', id?: int, parts?: int, md5_checksum?: string, file_hash?: string, secret?: string}|array{_: 'inputSecureFile', id?: int, access_hash?: int}, translation?: list<array{_: 'inputSecureFileUploaded', id?: int, parts?: int, md5_checksum?: string, file_hash?: string, secret?: string}|array{_: 'inputSecureFile', id?: int, access_hash?: int}>, files?: list<array{_: 'inputSecureFileUploaded', id?: int, parts?: int, md5_checksum?: string, file_hash?: string, secret?: string}|array{_: 'inputSecureFile', id?: int, access_hash?: int}>, plain_data?: array{_: 'securePlainPhone', phone?: string}|array{_: 'securePlainEmail', email?: string}} $value Secure value, [for more info see the passport docs Â»](https://core.telegram.org/passport/encryption#encryption) @see https://docs.madelineproto.xyz/API_docs/types/InputSecureValue.html
     * @param int $secure_secret_id Passport secret hash, [for more info see the passport docs Â»](https://core.telegram.org/passport/encryption#encryption)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'secureValue', type: array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}, data?: array{_: 'secureData', data: string, data_hash: string, secret: string}, front_side?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, reverse_side?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, selfie?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, translation?: list<array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}>, files?: list<array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}>, plain_data?: array{_: 'securePlainPhone', phone: string}|array{_: 'securePlainEmail', email: string}, hash: string} @see https://docs.madelineproto.xyz/API_docs/types/SecureValue.html
     * @psalm-impure
     */
    public function saveSecureValue(array $value, int|null $secure_secret_id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Delete stored [Telegram Passport](https://core.telegram.org/passport) documents, [for more info see the passport docs Â»](https://core.telegram.org/passport/encryption#encryption)
     *
     * @param list<array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}>|array<never, never> $types Array of Document types to delete @see https://docs.madelineproto.xyz/API_docs/types/SecureValueType.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function deleteSecureValue(array $types = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Returns a Telegram Passport authorization form for sharing data with a service
     *
     * @param int $bot_id User identifier of the service's bot
     * @param string $scope Telegram Passport element types requested by the service
     * @param string $public_key Service's public key
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'account.authorizationForm', required_types: list<array{_: 'secureRequiredType', type: array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}, native_names: bool, selfie_required: bool, translation_required: bool}>, values: list<array{_: 'secureValue', type: array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}, data?: array{_: 'secureData', data: string, data_hash: string, secret: string}, front_side?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, reverse_side?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, selfie?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}, translation?: list<array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}>, files?: list<array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: int, access_hash: int, size: int, dc_id: int, date: int, file_hash: string, secret: string}>, plain_data?: array{_: 'securePlainPhone', phone: string}|array{_: 'securePlainEmail', email: string}, hash: string}>, errors: list<array{_: 'secureValueErrorData', type: array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}, data_hash: string, field: string, text: string}|array{_: 'secureValueErrorFrontSide', type: array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}, file_hash: string, text: string}|array{_: 'secureValueErrorReverseSide', type: array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}, file_hash: string, text: string}|array{_: 'secureValueErrorSelfie', type: array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}, file_hash: string, text: string}|array{_: 'secureValueErrorFile', type: array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}, file_hash: string, text: string}|array{_: 'secureValueErrorFiles', type: array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}, file_hash: list<string>, text: string}|array{_: 'secureValueError', type: array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}, hash: string, text: string}|array{_: 'secureValueErrorTranslationFile', type: array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}, file_hash: string, text: string}|array{_: 'secureValueErrorTranslationFiles', type: array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}, file_hash: list<string>, text: string}>, users: list<array|int|string>, privacy_policy_url?: string} @see https://docs.madelineproto.xyz/API_docs/types/account.AuthorizationForm.html
     * @psalm-impure
     */
    public function getAuthorizationForm(int|null $bot_id = 0, string|null $scope = '', string|null $public_key = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Sends a Telegram Passport authorization form, effectively sharing data with the service
     *
     * @param array{_: 'secureCredentialsEncrypted', data?: string, hash?: string, secret?: string} $credentials Encrypted values @see https://docs.madelineproto.xyz/API_docs/types/SecureCredentialsEncrypted.html
     * @param int $bot_id Bot ID
     * @param string $scope Telegram Passport element types requested by the service
     * @param string $public_key Service's public key
     * @param list<array{_: 'secureValueHash', type: array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}, hash?: string}>|array<never, never> $value_hashes Array of Types of values sent and their hashes @see https://docs.madelineproto.xyz/API_docs/types/SecureValueHash.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function acceptAuthorization(array $credentials, int|null $bot_id = 0, string|null $scope = '', string|null $public_key = '', array $value_hashes = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Send the verification phone code for telegram [passport](https://core.telegram.org/passport).
     *
     * @param array{_: 'codeSettings', allow_flashcall?: bool, current_number?: bool, allow_app_hash?: bool, allow_missed_call?: bool, allow_firebase?: bool, unknown_number?: bool, logout_tokens?: list<string>, token?: string, app_sandbox?: bool} $settings Phone code settings @see https://docs.madelineproto.xyz/API_docs/types/CodeSettings.html
     * @param string $phone_number The phone number to verify
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'auth.sentCode', type: array{_: 'auth.sentCodeTypeApp', length: int}|array{_: 'auth.sentCodeTypeSms', length: int}|array{_: 'auth.sentCodeTypeCall', length: int}|array{_: 'auth.sentCodeTypeFlashCall', pattern: string}|array{_: 'auth.sentCodeTypeMissedCall', prefix: string, length: int}|array{_: 'auth.sentCodeTypeEmailCode', apple_signin_allowed: bool, google_signin_allowed: bool, email_pattern: string, length: int, reset_available_period?: int, reset_pending_date?: int}|array{_: 'auth.sentCodeTypeSetUpEmailRequired', apple_signin_allowed: bool, google_signin_allowed: bool}|array{_: 'auth.sentCodeTypeFragmentSms', url: string, length: int}|array{_: 'auth.sentCodeTypeFirebaseSms', nonce?: string, play_integrity_project_id?: int, play_integrity_nonce?: string, receipt?: string, push_timeout?: int, length: int}|array{_: 'auth.sentCodeTypeSmsWord', beginning?: string}|array{_: 'auth.sentCodeTypeSmsPhrase', beginning?: string}, phone_code_hash: string, next_type?: array{_: 'auth.codeTypeSms'}|array{_: 'auth.codeTypeCall'}|array{_: 'auth.codeTypeFlashCall'}|array{_: 'auth.codeTypeMissedCall'}|array{_: 'auth.codeTypeFragmentSms'}, timeout?: int}|array{_: 'auth.sentCodeSuccess', authorization: array{_: 'auth.authorization', setup_password_required: bool, otherwise_relogin_days?: int, tmp_sessions?: int, future_auth_token?: string, user: array|int|string}|array{_: 'auth.authorizationSignUpRequired', terms_of_service?: array{_: 'help.termsOfService', id: mixed, popup: bool, text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, min_age_confirm?: int}}}|array{_: 'auth.sentCodePaymentRequired', store_product: string, phone_code_hash: string, support_email_address: string, support_email_subject: string, premium_days: int, currency: string, amount: int} @see https://docs.madelineproto.xyz/API_docs/types/auth.SentCode.html
     * @psalm-impure
     */
    public function sendVerifyPhoneCode(array $settings, string|null $phone_number = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Verify a phone number for telegram [passport](https://core.telegram.org/passport).
     *
     * @param string $phone_number Phone number
     * @param string $phone_code_hash Phone code hash received from the call to [account.sendVerifyPhoneCode](https://docs.madelineproto.xyz/API_docs/methods/account.sendVerifyPhoneCode.html)
     * @param string $phone_code Code received after the call to [account.sendVerifyPhoneCode](https://docs.madelineproto.xyz/API_docs/methods/account.sendVerifyPhoneCode.html)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function verifyPhone(string|null $phone_number = '', string|null $phone_code_hash = '', string|null $phone_code = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Send an email verification code.
     *
     * @param array{_: 'emailVerifyPurposeLoginSetup', phone_number?: string, phone_code_hash?: string}|array{_: 'emailVerifyPurposeLoginChange'}|array{_: 'emailVerifyPurposePassport'} $purpose Verification purpose. @see https://docs.madelineproto.xyz/API_docs/types/EmailVerifyPurpose.html
     * @param string $email The email where to send the code.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'account.sentEmailCode', email_pattern: string, length: int} @see https://docs.madelineproto.xyz/API_docs/types/account.SentEmailCode.html
     * @psalm-impure
     */
    public function sendVerifyEmailCode(array $purpose, string|null $email = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Verify an email address.
     *
     * @param array{_: 'emailVerifyPurposeLoginSetup', phone_number?: string, phone_code_hash?: string}|array{_: 'emailVerifyPurposeLoginChange'}|array{_: 'emailVerifyPurposePassport'} $purpose Verification purpose @see https://docs.madelineproto.xyz/API_docs/types/EmailVerifyPurpose.html
     * @param array{_: 'emailVerificationCode', code?: string}|array{_: 'emailVerificationGoogle', token?: string}|array{_: 'emailVerificationApple', token?: string} $verification Email verification code or token @see https://docs.madelineproto.xyz/API_docs/types/EmailVerification.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'account.emailVerified', email: string}|array{_: 'account.emailVerifiedLogin', sent_code: array{_: 'auth.sentCode', type: array{_: 'auth.sentCodeTypeApp', length: int}|array{_: 'auth.sentCodeTypeSms', length: int}|array{_: 'auth.sentCodeTypeCall', length: int}|array{_: 'auth.sentCodeTypeFlashCall', pattern: string}|array{_: 'auth.sentCodeTypeMissedCall', prefix: string, length: int}|array{_: 'auth.sentCodeTypeEmailCode', apple_signin_allowed: bool, google_signin_allowed: bool, email_pattern: string, length: int, reset_available_period?: int, reset_pending_date?: int}|array{_: 'auth.sentCodeTypeSetUpEmailRequired', apple_signin_allowed: bool, google_signin_allowed: bool}|array{_: 'auth.sentCodeTypeFragmentSms', url: string, length: int}|array{_: 'auth.sentCodeTypeFirebaseSms', nonce?: string, play_integrity_project_id?: int, play_integrity_nonce?: string, receipt?: string, push_timeout?: int, length: int}|array{_: 'auth.sentCodeTypeSmsWord', beginning?: string}|array{_: 'auth.sentCodeTypeSmsPhrase', beginning?: string}, phone_code_hash: string, next_type?: array{_: 'auth.codeTypeSms'}|array{_: 'auth.codeTypeCall'}|array{_: 'auth.codeTypeFlashCall'}|array{_: 'auth.codeTypeMissedCall'}|array{_: 'auth.codeTypeFragmentSms'}, timeout?: int}|array{_: 'auth.sentCodeSuccess', authorization: array{_: 'auth.authorization', setup_password_required: bool, otherwise_relogin_days?: int, tmp_sessions?: int, future_auth_token?: string, user: array|int|string}|array{_: 'auth.authorizationSignUpRequired', terms_of_service?: array{_: 'help.termsOfService', id: mixed, popup: bool, text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, min_age_confirm?: int}}}|array{_: 'auth.sentCodePaymentRequired', store_product: string, phone_code_hash: string, support_email_address: string, support_email_subject: string, premium_days: int, currency: string, amount: int}, email: string} @see https://docs.madelineproto.xyz/API_docs/types/account.EmailVerified.html
     * @psalm-impure
     */
    public function verifyEmail(array $purpose, array $verification, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Initialize a [takeout session, see here Â» for more info](https://core.telegram.org/api/takeout).
     *
     * @param bool $contacts Whether to export contacts
     * @param bool $message_users Whether to export messages in private chats
     * @param bool $message_chats Whether to export messages in [basic groups](https://core.telegram.org/api/channel#basic-groups)
     * @param bool $message_megagroups Whether to export messages in [supergroups](https://core.telegram.org/api/channel#supergroups)
     * @param bool $message_channels Whether to export messages in [channels](https://core.telegram.org/api/channel#channels)
     * @param bool $files Whether to export files
     * @param int $file_max_size Maximum size of files to export
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'account.takeout', id: int} @see https://docs.madelineproto.xyz/API_docs/types/account.Takeout.html
     * @psalm-impure
     */
    public function initTakeoutSession(bool|null $contacts = null, bool|null $message_users = null, bool|null $message_chats = null, bool|null $message_megagroups = null, bool|null $message_channels = null, bool|null $files = null, int|null $file_max_size = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Terminate a [takeout session, see here Â» for more info](https://core.telegram.org/api/takeout).
     *
     * @param bool $success Data exported successfully
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function finishTakeoutSession(bool|null $success = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Verify an email to use as [2FA recovery method](https://core.telegram.org/api/srp).
     *
     * @param string $code The phone code that was received after [setting a recovery email](https://core.telegram.org/api/srp#email-verification)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function confirmPasswordEmail(string|null $code = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Resend the code to verify an email to use as [2FA recovery method](https://core.telegram.org/api/srp).
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function resendPasswordEmail(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Cancel the code that was sent to verify an email to use as [2FA recovery method](https://core.telegram.org/api/srp).
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function cancelPasswordEmail(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Whether the user will receive notifications when contacts sign up
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function getContactSignUpNotification(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Toggle contact sign up notifications
     *
     * @param bool $silent Whether to disable contact sign up notifications
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function setContactSignUpNotification(bool $silent, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Returns list of chats with non-default notification settings
     *
     * @param bool $compare_sound If set, chats with non-default sound will be returned
     * @param bool $compare_stories If set, chats with non-default notification settings for stories will be returned
     * @param array{_: 'inputNotifyPeer', peer?: array|int|string}|array{_: 'inputNotifyUsers'}|array{_: 'inputNotifyChats'}|array{_: 'inputNotifyBroadcasts'}|array{_: 'inputNotifyForumTopic', peer?: array|int|string, top_msg_id?: int}|array{_: 'inputNotifyCommunity', community?: array|int|string} $peer If specified, only chats of the specified category will be returned @see https://docs.madelineproto.xyz/API_docs/types/InputNotifyPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function getNotifyExceptions(bool|null $compare_sound = null, bool|null $compare_stories = null, array|null $peer = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get info about a certain [wallpaper](https://core.telegram.org/api/wallpapers)
     *
     * @param array{_: 'inputWallPaper', id?: int, access_hash?: int}|array{_: 'inputWallPaperSlug', slug?: string}|array{_: 'inputWallPaperNoFile', id?: int} $wallpaper The [wallpaper](https://core.telegram.org/api/wallpapers) to get info about @see https://docs.madelineproto.xyz/API_docs/types/InputWallPaper.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'wallPaper', id: int, creator: bool, default: bool, pattern: bool, dark: bool, access_hash: int, slug: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, settings?: array{_: 'wallPaperSettings', blur: bool, motion: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string}}|array{_: 'wallPaperNoFile', id: int, default: bool, dark: bool, settings?: array{_: 'wallPaperSettings', blur: bool, motion: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string}} @see https://docs.madelineproto.xyz/API_docs/types/WallPaper.html
     * @psalm-impure
     */
    public function getWallPaper(array $wallpaper, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Create and upload a new [wallpaper](https://core.telegram.org/api/wallpapers)
     *
     * @param mixed $file A file name or a file URL. You can also use amphp async streams, amphp HTTP response objects, and [much more](https://docs.madelineproto.xyz/docs/FILES.html#downloading-files)!
     * @param array{_: 'wallPaperSettings', blur?: bool, motion?: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string} $settings Wallpaper settings @see https://docs.madelineproto.xyz/API_docs/types/WallPaperSettings.html
     * @param bool $for_chat Set this flag when uploading wallpapers to be passed to [messages.setChatWallPaper](https://docs.madelineproto.xyz/API_docs/methods/messages.setChatWallPaper.html).
     * @param string $mime_type MIME type of uploaded wallpaper
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'wallPaper', id: int, creator: bool, default: bool, pattern: bool, dark: bool, access_hash: int, slug: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, settings?: array{_: 'wallPaperSettings', blur: bool, motion: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string}}|array{_: 'wallPaperNoFile', id: int, default: bool, dark: bool, settings?: array{_: 'wallPaperSettings', blur: bool, motion: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string}} @see https://docs.madelineproto.xyz/API_docs/types/WallPaper.html
     * @psalm-impure
     */
    public function uploadWallPaper(mixed $file, array $settings, bool|null $for_chat = null, string|null $mime_type = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Install/uninstall [wallpaper](https://core.telegram.org/api/wallpapers)
     *
     * @param array{_: 'inputWallPaper', id?: int, access_hash?: int}|array{_: 'inputWallPaperSlug', slug?: string}|array{_: 'inputWallPaperNoFile', id?: int} $wallpaper [Wallpaper](https://core.telegram.org/api/wallpapers) to install or uninstall @see https://docs.madelineproto.xyz/API_docs/types/InputWallPaper.html
     * @param bool $unsave Uninstall wallpaper?
     * @param array{_: 'wallPaperSettings', blur?: bool, motion?: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string} $settings Wallpaper settings @see https://docs.madelineproto.xyz/API_docs/types/WallPaperSettings.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function saveWallPaper(array $wallpaper, bool $unsave, array $settings, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Install [wallpaper](https://core.telegram.org/api/wallpapers)
     *
     * @param array{_: 'inputWallPaper', id?: int, access_hash?: int}|array{_: 'inputWallPaperSlug', slug?: string}|array{_: 'inputWallPaperNoFile', id?: int} $wallpaper [Wallpaper](https://core.telegram.org/api/wallpapers) to install @see https://docs.madelineproto.xyz/API_docs/types/InputWallPaper.html
     * @param array{_: 'wallPaperSettings', blur?: bool, motion?: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string} $settings [Wallpaper](https://core.telegram.org/api/wallpapers) settings @see https://docs.madelineproto.xyz/API_docs/types/WallPaperSettings.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function installWallPaper(array $wallpaper, array $settings, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Delete all installed [wallpapers](https://core.telegram.org/api/wallpapers), reverting to the default wallpaper set.
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function resetWallPapers(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Get media autodownload settings
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'account.autoDownloadSettings', low: array{_: 'autoDownloadSettings', disabled: bool, video_preload_large: bool, audio_preload_next: bool, phonecalls_less_data: bool, stories_preload: bool, photo_size_max: int, video_size_max: int, file_size_max: int, video_upload_maxbitrate: int, small_queue_active_operations_max: int, large_queue_active_operations_max: int}, medium: array{_: 'autoDownloadSettings', disabled: bool, video_preload_large: bool, audio_preload_next: bool, phonecalls_less_data: bool, stories_preload: bool, photo_size_max: int, video_size_max: int, file_size_max: int, video_upload_maxbitrate: int, small_queue_active_operations_max: int, large_queue_active_operations_max: int}, high: array{_: 'autoDownloadSettings', disabled: bool, video_preload_large: bool, audio_preload_next: bool, phonecalls_less_data: bool, stories_preload: bool, photo_size_max: int, video_size_max: int, file_size_max: int, video_upload_maxbitrate: int, small_queue_active_operations_max: int, large_queue_active_operations_max: int}} @see https://docs.madelineproto.xyz/API_docs/types/account.AutoDownloadSettings.html
     * @psalm-impure
     */
    public function getAutoDownloadSettings(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Change media autodownload settings
     *
     * @param array{_: 'autoDownloadSettings', disabled?: bool, video_preload_large?: bool, audio_preload_next?: bool, phonecalls_less_data?: bool, stories_preload?: bool, photo_size_max?: int, video_size_max?: int, file_size_max?: int, video_upload_maxbitrate?: int, small_queue_active_operations_max?: int, large_queue_active_operations_max?: int} $settings Media autodownload settings @see https://docs.madelineproto.xyz/API_docs/types/AutoDownloadSettings.html
     * @param bool $low Whether to save media in the low data usage preset
     * @param bool $high Whether to save media in the high data usage preset
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function saveAutoDownloadSettings(array $settings, bool|null $low = null, bool|null $high = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Upload theme
     *
     * @param mixed $file A file name or a file URL. You can also use amphp async streams, amphp HTTP response objects, and [much more](https://docs.madelineproto.xyz/docs/FILES.html#downloading-files)!
     * @param mixed $thumb A file name or a file URL. You can also use amphp async streams, amphp HTTP response objects, and [much more](https://docs.madelineproto.xyz/docs/FILES.html#downloading-files)!
     * @param string $file_name File name
     * @param string $mime_type MIME type, must be `application/x-tgtheme-{format}`, where `format` depends on the client
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>} @see https://docs.madelineproto.xyz/API_docs/types/Document.html
     * @psalm-impure
     */
    public function uploadTheme(mixed $file, mixed $thumb = null, string|null $file_name = '', string|null $mime_type = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Create a theme
     *
     * @param string $slug Unique theme ID used to generate [theme deep links](https://core.telegram.org/api/links#theme-links), can be empty to autogenerate a random ID.
     * @param string $title Theme name
     * @param array $document Theme file @see https://docs.madelineproto.xyz/API_docs/types/InputDocument.html
     * @param list<array{_: 'inputThemeSettings', base_theme: array{_: 'baseThemeClassic'}|array{_: 'baseThemeDay'}|array{_: 'baseThemeNight'}|array{_: 'baseThemeTinted'}|array{_: 'baseThemeArctic'}, message_colors_animated?: bool, accent_color?: int, outbox_accent_color?: int, message_colors?: list<int>, wallpaper?: array{_: 'inputWallPaper', id?: int, access_hash?: int}|array{_: 'inputWallPaperSlug', slug?: string}|array{_: 'inputWallPaperNoFile', id?: int}, wallpaper_settings?: array{_: 'wallPaperSettings', blur?: bool, motion?: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string}}> $settings Array of Theme settings, multiple values can be provided for the different base themes (day/night mode, etc). @see https://docs.madelineproto.xyz/API_docs/types/InputThemeSettings.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'theme', creator: bool, default: bool, for_chat: bool, id: int, access_hash: int, slug: string, title: string, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, settings?: list<array{_: 'themeSettings', base_theme: array{_: 'baseThemeClassic'}|array{_: 'baseThemeDay'}|array{_: 'baseThemeNight'}|array{_: 'baseThemeTinted'}|array{_: 'baseThemeArctic'}, message_colors_animated: bool, accent_color: int, outbox_accent_color?: int, message_colors?: list<int>, wallpaper?: array{_: 'wallPaper', id: int, creator: bool, default: bool, pattern: bool, dark: bool, access_hash: int, slug: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, settings?: array{_: 'wallPaperSettings', blur: bool, motion: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string}}|array{_: 'wallPaperNoFile', id: int, default: bool, dark: bool, settings?: array{_: 'wallPaperSettings', blur: bool, motion: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string}}}>, emoticon?: string, installs_count?: int} @see https://docs.madelineproto.xyz/API_docs/types/Theme.html
     * @psalm-impure
     */
    public function createTheme(string|null $slug = '', string|null $title = '', array|null $document = null, array|null $settings = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Update theme
     *
     * @param array{_: 'inputTheme', id?: int, access_hash?: int}|array{_: 'inputThemeSlug', slug?: string} $theme Theme to update @see https://docs.madelineproto.xyz/API_docs/types/InputTheme.html
     * @param string $format Theme format, a string that identifies the theming engines supported by the client
     * @param string $slug Unique theme ID
     * @param string $title Theme name
     * @param array $document Theme file @see https://docs.madelineproto.xyz/API_docs/types/InputDocument.html
     * @param list<array{_: 'inputThemeSettings', base_theme: array{_: 'baseThemeClassic'}|array{_: 'baseThemeDay'}|array{_: 'baseThemeNight'}|array{_: 'baseThemeTinted'}|array{_: 'baseThemeArctic'}, message_colors_animated?: bool, accent_color?: int, outbox_accent_color?: int, message_colors?: list<int>, wallpaper?: array{_: 'inputWallPaper', id?: int, access_hash?: int}|array{_: 'inputWallPaperSlug', slug?: string}|array{_: 'inputWallPaperNoFile', id?: int}, wallpaper_settings?: array{_: 'wallPaperSettings', blur?: bool, motion?: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string}}> $settings Array of Theme settings @see https://docs.madelineproto.xyz/API_docs/types/InputThemeSettings.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'theme', creator: bool, default: bool, for_chat: bool, id: int, access_hash: int, slug: string, title: string, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, settings?: list<array{_: 'themeSettings', base_theme: array{_: 'baseThemeClassic'}|array{_: 'baseThemeDay'}|array{_: 'baseThemeNight'}|array{_: 'baseThemeTinted'}|array{_: 'baseThemeArctic'}, message_colors_animated: bool, accent_color: int, outbox_accent_color?: int, message_colors?: list<int>, wallpaper?: array{_: 'wallPaper', id: int, creator: bool, default: bool, pattern: bool, dark: bool, access_hash: int, slug: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, settings?: array{_: 'wallPaperSettings', blur: bool, motion: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string}}|array{_: 'wallPaperNoFile', id: int, default: bool, dark: bool, settings?: array{_: 'wallPaperSettings', blur: bool, motion: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string}}}>, emoticon?: string, installs_count?: int} @see https://docs.madelineproto.xyz/API_docs/types/Theme.html
     * @psalm-impure
     */
    public function updateTheme(array $theme, string|null $format = '', string|null $slug = null, string|null $title = null, array|null $document = null, array|null $settings = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Save a theme
     *
     * @param array{_: 'inputTheme', id?: int, access_hash?: int}|array{_: 'inputThemeSlug', slug?: string} $theme Theme to save @see https://docs.madelineproto.xyz/API_docs/types/InputTheme.html
     * @param bool $unsave Unsave
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function saveTheme(array $theme, bool $unsave, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Install a theme
     *
     * @param bool $dark Whether to install the dark version
     * @param array{_: 'inputTheme', id?: int, access_hash?: int}|array{_: 'inputThemeSlug', slug?: string} $theme Theme to install @see https://docs.madelineproto.xyz/API_docs/types/InputTheme.html
     * @param string $format Theme format, a string that identifies the theming engines supported by the client
     * @param array{_: 'baseThemeClassic'}|array{_: 'baseThemeDay'}|array{_: 'baseThemeNight'}|array{_: 'baseThemeTinted'}|array{_: 'baseThemeArctic'} $base_theme Indicates a basic theme provided by all clients @see https://docs.madelineproto.xyz/API_docs/types/BaseTheme.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function installTheme(bool|null $dark = null, array|null $theme = null, string|null $format = null, array|null $base_theme = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Get theme information
     *
     * @param array{_: 'inputTheme', id?: int, access_hash?: int}|array{_: 'inputThemeSlug', slug?: string} $theme Theme @see https://docs.madelineproto.xyz/API_docs/types/InputTheme.html
     * @param string $format Theme format, a string that identifies the theming engines supported by the client
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'theme', creator: bool, default: bool, for_chat: bool, id: int, access_hash: int, slug: string, title: string, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, settings?: list<array{_: 'themeSettings', base_theme: array{_: 'baseThemeClassic'}|array{_: 'baseThemeDay'}|array{_: 'baseThemeNight'}|array{_: 'baseThemeTinted'}|array{_: 'baseThemeArctic'}, message_colors_animated: bool, accent_color: int, outbox_accent_color?: int, message_colors?: list<int>, wallpaper?: array{_: 'wallPaper', id: int, creator: bool, default: bool, pattern: bool, dark: bool, access_hash: int, slug: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, settings?: array{_: 'wallPaperSettings', blur: bool, motion: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string}}|array{_: 'wallPaperNoFile', id: int, default: bool, dark: bool, settings?: array{_: 'wallPaperSettings', blur: bool, motion: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string}}}>, emoticon?: string, installs_count?: int} @see https://docs.madelineproto.xyz/API_docs/types/Theme.html
     * @psalm-impure
     */
    public function getTheme(array $theme, string|null $format = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get installed themes
     *
     * @param string $format Theme format, a string that identifies the theming engines supported by the client
     * @param list<int|string>|array<never, never> $hash Array of [Hash used for caching, for more info click here](https://core.telegram.org/api/offsets#hash-generation). @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'account.themesNotModified'}|array{_: 'account.themes', hash: list<int|string>, themes: list<array{_: 'theme', creator: bool, default: bool, for_chat: bool, id: int, access_hash: int, slug: string, title: string, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, settings?: list<array{_: 'themeSettings', base_theme: array{_: 'baseThemeClassic'}|array{_: 'baseThemeDay'}|array{_: 'baseThemeNight'}|array{_: 'baseThemeTinted'}|array{_: 'baseThemeArctic'}, message_colors_animated: bool, accent_color: int, outbox_accent_color?: int, message_colors?: list<int>, wallpaper?: array{_: 'wallPaper', id: int, creator: bool, default: bool, pattern: bool, dark: bool, access_hash: int, slug: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, settings?: array{_: 'wallPaperSettings', blur: bool, motion: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string}}|array{_: 'wallPaperNoFile', id: int, default: bool, dark: bool, settings?: array{_: 'wallPaperSettings', blur: bool, motion: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string}}}>, emoticon?: string, installs_count?: int}>} @see https://docs.madelineproto.xyz/API_docs/types/account.Themes.html
     * @psalm-impure
     */
    public function getThemes(string|null $format = '', array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Set sensitive content settings (for viewing or hiding NSFW content)
     *
     * @param bool $sensitive_enabled Enable NSFW content
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function setContentSettings(bool|null $sensitive_enabled = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Get sensitive content settings
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'account.contentSettings', sensitive_enabled: bool, sensitive_can_change: bool} @see https://docs.madelineproto.xyz/API_docs/types/account.ContentSettings.html
     * @psalm-impure
     */
    public function getContentSettings(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get info about multiple [wallpapers](https://core.telegram.org/api/wallpapers)
     *
     * @param list<array{_: 'inputWallPaper', id?: int, access_hash?: int}|array{_: 'inputWallPaperSlug', slug?: string}|array{_: 'inputWallPaperNoFile', id?: int}>|array<never, never> $wallpapers Array of [Wallpapers](https://core.telegram.org/api/wallpapers) to fetch info about @see https://docs.madelineproto.xyz/API_docs/types/InputWallPaper.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return list<array{_: 'wallPaper', id: int, creator: bool, default: bool, pattern: bool, dark: bool, access_hash: int, slug: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, settings?: array{_: 'wallPaperSettings', blur: bool, motion: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string}}|array{_: 'wallPaperNoFile', id: int, default: bool, dark: bool, settings?: array{_: 'wallPaperSettings', blur: bool, motion: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string}}> Array of  @see https://docs.madelineproto.xyz/API_docs/types/WallPaper.html
     * @psalm-impure
     */
    public function getMultiWallPapers(array $wallpapers = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get global privacy settings
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'globalPrivacySettings', archive_and_mute_new_noncontact_peers: bool, keep_archived_unmuted: bool, keep_archived_folders: bool, hide_read_marks: bool, new_noncontact_peers_require_premium: bool, display_gifts_button: bool, noncontact_peers_paid_stars?: int, disallowed_gifts?: array{_: 'disallowedGiftsSettings', disallow_unlimited_stargifts: bool, disallow_limited_stargifts: bool, disallow_unique_stargifts: bool, disallow_premium_gifts: bool, disallow_stargifts_from_channels: bool}} @see https://docs.madelineproto.xyz/API_docs/types/GlobalPrivacySettings.html
     * @psalm-impure
     */
    public function getGlobalPrivacySettings(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Set global privacy settings
     *
     * @param array{_: 'globalPrivacySettings', archive_and_mute_new_noncontact_peers?: bool, keep_archived_unmuted?: bool, keep_archived_folders?: bool, hide_read_marks?: bool, new_noncontact_peers_require_premium?: bool, display_gifts_button?: bool, noncontact_peers_paid_stars?: int, disallowed_gifts?: array{_: 'disallowedGiftsSettings', disallow_unlimited_stargifts?: bool, disallow_limited_stargifts?: bool, disallow_unique_stargifts?: bool, disallow_premium_gifts?: bool, disallow_stargifts_from_channels?: bool}} $settings Global privacy settings @see https://docs.madelineproto.xyz/API_docs/types/GlobalPrivacySettings.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @param ?string $businessConnectionId Business connection ID, received through an updateBotBusinessConnect update.
     * @return array{_: 'globalPrivacySettings', archive_and_mute_new_noncontact_peers: bool, keep_archived_unmuted: bool, keep_archived_folders: bool, hide_read_marks: bool, new_noncontact_peers_require_premium: bool, display_gifts_button: bool, noncontact_peers_paid_stars?: int, disallowed_gifts?: array{_: 'disallowedGiftsSettings', disallow_unlimited_stargifts: bool, disallow_limited_stargifts: bool, disallow_unique_stargifts: bool, disallow_premium_gifts: bool, disallow_stargifts_from_channels: bool}} @see https://docs.madelineproto.xyz/API_docs/types/GlobalPrivacySettings.html
     * @psalm-impure
     */
    public function setGlobalPrivacySettings(array $settings, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null, ?string $businessConnectionId = null): array;

    /**
     * Report a profile photo of a dialog
     *
     * @param array{_: 'inputReportReasonSpam'}|array{_: 'inputReportReasonViolence'}|array{_: 'inputReportReasonPornography'}|array{_: 'inputReportReasonChildAbuse'}|array{_: 'inputReportReasonOther'}|array{_: 'inputReportReasonCopyright'}|array{_: 'inputReportReasonGeoIrrelevant'}|array{_: 'inputReportReasonFake'}|array{_: 'inputReportReasonIllegalDrugs'}|array{_: 'inputReportReasonPersonalDetails'} $reason Report reason @see https://docs.madelineproto.xyz/API_docs/types/ReportReason.html
     * @param array|int|string $peer The dialog @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param array $photo_id Dialog photo ID @see https://docs.madelineproto.xyz/API_docs/types/InputPhoto.html
     * @param string $message Comment for report moderation
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function reportProfilePhoto(array $reason, array|int|string|null $peer = null, array|null $photo_id = null, string|null $message = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Initiate a 2FA password reset: can only be used if the user is already logged-in, [see here for more info Â»](https://core.telegram.org/api/srp#password-reset)
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'account.resetPasswordFailedWait', retry_date: int}|array{_: 'account.resetPasswordRequestedWait', until_date: int}|array{_: 'account.resetPasswordOk'} @see https://docs.madelineproto.xyz/API_docs/types/account.ResetPasswordResult.html
     * @psalm-impure
     */
    public function resetPassword(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Abort a pending 2FA password reset, [see here for more info Â»](https://core.telegram.org/api/srp#password-reset)
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function declinePasswordReset(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Get all available chat [themes Â»](https://core.telegram.org/api/themes).
     *
     * @param list<int|string>|array<never, never> $hash Array of [Hash used for caching, for more info click here](https://core.telegram.org/api/offsets#hash-generation). @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'account.themesNotModified'}|array{_: 'account.themes', hash: list<int|string>, themes: list<array{_: 'theme', creator: bool, default: bool, for_chat: bool, id: int, access_hash: int, slug: string, title: string, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, settings?: list<array{_: 'themeSettings', base_theme: array{_: 'baseThemeClassic'}|array{_: 'baseThemeDay'}|array{_: 'baseThemeNight'}|array{_: 'baseThemeTinted'}|array{_: 'baseThemeArctic'}, message_colors_animated: bool, accent_color: int, outbox_accent_color?: int, message_colors?: list<int>, wallpaper?: array{_: 'wallPaper', id: int, creator: bool, default: bool, pattern: bool, dark: bool, access_hash: int, slug: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, settings?: array{_: 'wallPaperSettings', blur: bool, motion: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string}}|array{_: 'wallPaperNoFile', id: int, default: bool, dark: bool, settings?: array{_: 'wallPaperSettings', blur: bool, motion: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string}}}>, emoticon?: string, installs_count?: int}>} @see https://docs.madelineproto.xyz/API_docs/types/account.Themes.html
     * @psalm-impure
     */
    public function getChatThemes(array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Set time-to-live of current session
     *
     * @param int $authorization_ttl_days Time-to-live of current session in days
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function setAuthorizationTTL(int|null $authorization_ttl_days = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Change settings related to a session.
     *
     * @param bool $confirmed If set, [confirms a newly logged in session Â»](https://core.telegram.org/api/auth#confirming-login).
     * @param list<int|string>|array<never, never> $hash Array of Session ID from the [authorization](https://docs.madelineproto.xyz/API_docs/constructors/authorization.html) constructor, fetchable using [account.getAuthorizations](https://docs.madelineproto.xyz/API_docs/methods/account.getAuthorizations.html) @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param bool $encrypted_requests_disabled Whether to enable or disable receiving encrypted chats: if the flag is not set, the previous setting is not changed
     * @param bool $call_requests_disabled Whether to enable or disable receiving calls: if the flag is not set, the previous setting is not changed
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function changeAuthorizationSettings(bool|null $confirmed = null, array $hash = [], bool|null $encrypted_requests_disabled = null, bool|null $call_requests_disabled = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Fetch saved notification sounds
     *
     * @param list<int|string>|array<never, never> $hash Array of [Hash used for caching, for more info click here](https://core.telegram.org/api/offsets#hash-generation). @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'account.savedRingtonesNotModified'}|array{_: 'account.savedRingtones', hash: list<int|string>, ringtones: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>} @see https://docs.madelineproto.xyz/API_docs/types/account.SavedRingtones.html
     * @psalm-impure
     */
    public function getSavedRingtones(array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Save or remove saved notification sound.
     * 
     * If the notification sound is already in MP3 format, [account.savedRingtone](https://docs.madelineproto.xyz/API_docs/constructors/account.savedRingtone.html) will be returned.  
     * Otherwise, it will be automatically converted and a [account.savedRingtoneConverted](https://docs.madelineproto.xyz/API_docs/constructors/account.savedRingtoneConverted.html) will be returned, containing a new [document](https://docs.madelineproto.xyz/API_docs/constructors/document.html) object that should be used to refer to the ringtone from now on (ie when deleting it using the `unsave` parameter, or when downloading it).
     *
     * @param bool $unsave Whether to add or delete the notification sound
     * @param array $id Notification sound uploaded using [account.uploadRingtone](https://docs.madelineproto.xyz/API_docs/methods/account.uploadRingtone.html) @see https://docs.madelineproto.xyz/API_docs/types/InputDocument.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'account.savedRingtone'}|array{_: 'account.savedRingtoneConverted', document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}} @see https://docs.madelineproto.xyz/API_docs/types/account.SavedRingtone.html
     * @psalm-impure
     */
    public function saveRingtone(bool $unsave, array|null $id = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Upload notification sound, use [account.saveRingtone](https://docs.madelineproto.xyz/API_docs/methods/account.saveRingtone.html) to convert it and add it to the list of saved notification sounds.
     *
     * @param mixed $file A file name or a file URL. You can also use amphp async streams, amphp HTTP response objects, and [much more](https://docs.madelineproto.xyz/docs/FILES.html#downloading-files)!
     * @param string $file_name File name
     * @param string $mime_type MIME type of file
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>} @see https://docs.madelineproto.xyz/API_docs/types/Document.html
     * @psalm-impure
     */
    public function uploadRingtone(mixed $file, string|null $file_name = '', string|null $mime_type = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Set an [emoji status](https://core.telegram.org/api/emoji-status)
     *
     * @param array{_: 'emojiStatusEmpty'}|array{_: 'emojiStatus', document_id?: int, until?: int}|array{_: 'emojiStatusCollectible', collectible_id?: int, document_id?: int, title?: string, slug?: string, pattern_document_id?: int, center_color?: int, edge_color?: int, pattern_color?: int, text_color?: int, until?: int}|array{_: 'inputEmojiStatusCollectible', collectible_id?: int, until?: int} $emoji_status [Emoji status](https://core.telegram.org/api/emoji-status) to set @see https://docs.madelineproto.xyz/API_docs/types/EmojiStatus.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function updateEmojiStatus(array|null $emoji_status = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Get a list of default suggested [emoji statuses](https://core.telegram.org/api/emoji-status)
     *
     * @param list<int|string>|array<never, never> $hash Array of [Hash used for caching, for more info click here](https://core.telegram.org/api/offsets#hash-generation). @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'account.emojiStatusesNotModified'}|array{_: 'account.emojiStatuses', hash: list<int|string>, statuses: list<array{_: 'emojiStatusEmpty'}|array{_: 'emojiStatus', document_id: int, until?: int}|array{_: 'emojiStatusCollectible', collectible_id: int, document_id: int, title: string, slug: string, pattern_document_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int, until?: int}|array{_: 'inputEmojiStatusCollectible', collectible_id: int, until?: int}>} @see https://docs.madelineproto.xyz/API_docs/types/account.EmojiStatuses.html
     * @psalm-impure
     */
    public function getDefaultEmojiStatuses(array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get recently used [emoji statuses](https://core.telegram.org/api/emoji-status)
     *
     * @param list<int|string>|array<never, never> $hash Array of [Hash used for caching, for more info click here](https://core.telegram.org/api/offsets#hash-generation). @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'account.emojiStatusesNotModified'}|array{_: 'account.emojiStatuses', hash: list<int|string>, statuses: list<array{_: 'emojiStatusEmpty'}|array{_: 'emojiStatus', document_id: int, until?: int}|array{_: 'emojiStatusCollectible', collectible_id: int, document_id: int, title: string, slug: string, pattern_document_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int, until?: int}|array{_: 'inputEmojiStatusCollectible', collectible_id: int, until?: int}>} @see https://docs.madelineproto.xyz/API_docs/types/account.EmojiStatuses.html
     * @psalm-impure
     */
    public function getRecentEmojiStatuses(array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Clears list of recently used [emoji statuses](https://core.telegram.org/api/emoji-status)
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function clearRecentEmojiStatuses(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Reorder usernames associated with the currently logged-in user.
     *
     * @param list<string>|array<never, never> $order The new order for active usernames. All active usernames must be specified.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function reorderUsernames(array $order = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Activate or deactivate a purchased [fragment.com](https://fragment.com) username associated to the currently logged-in user.
     *
     * @param bool $active Whether to activate or deactivate it
     * @param string $username Username
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function toggleUsername(bool $active, string|null $username = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Get a set of suggested [custom emoji stickers](https://core.telegram.org/api/custom-emoji) that can be [used as profile picture](https://core.telegram.org/api/files#sticker-profile-pictures)
     *
     * @param list<int|string>|array<never, never> $hash Array of [Hash used for caching, for more info click here](https://core.telegram.org/api/offsets#hash-generation). @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'emojiListNotModified'}|array{_: 'emojiList', hash: list<int|string>, document_id: list<int>} @see https://docs.madelineproto.xyz/API_docs/types/EmojiList.html
     * @psalm-impure
     */
    public function getDefaultProfilePhotoEmojis(array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get a set of suggested [custom emoji stickers](https://core.telegram.org/api/custom-emoji) that can be [used as group picture](https://core.telegram.org/api/files#sticker-profile-pictures)
     *
     * @param list<int|string>|array<never, never> $hash Array of [Hash used for caching, for more info click here](https://core.telegram.org/api/offsets#hash-generation). @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'emojiListNotModified'}|array{_: 'emojiList', hash: list<int|string>, document_id: list<int>} @see https://docs.madelineproto.xyz/API_docs/types/EmojiList.html
     * @psalm-impure
     */
    public function getDefaultGroupPhotoEmojis(array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get autosave settings
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'account.autoSaveSettings', users_settings: array{_: 'autoSaveSettings', photos: bool, videos: bool, video_max_size?: int}, chats_settings: array{_: 'autoSaveSettings', photos: bool, videos: bool, video_max_size?: int}, broadcasts_settings: array{_: 'autoSaveSettings', photos: bool, videos: bool, video_max_size?: int}, exceptions: list<array{_: 'autoSaveException', peer: array|int|string, settings: array{_: 'autoSaveSettings', photos: bool, videos: bool, video_max_size?: int}}>, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/account.AutoSaveSettings.html
     * @psalm-impure
     */
    public function getAutoSaveSettings(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Modify autosave settings
     *
     * @param array{_: 'autoSaveSettings', photos?: bool, videos?: bool, video_max_size?: int} $settings The new autosave settings @see https://docs.madelineproto.xyz/API_docs/types/AutoSaveSettings.html
     * @param bool $users Whether the new settings should affect all private chats
     * @param bool $chats Whether the new settings should affect all groups
     * @param bool $broadcasts Whether the new settings should affect all [channels](https://core.telegram.org/api/channel)
     * @param array|int|string $peer Whether the new settings should affect a specific peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function saveAutoSaveSettings(array $settings, bool|null $users = null, bool|null $chats = null, bool|null $broadcasts = null, array|int|string|null $peer = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Clear all peer-specific autosave settings.
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function deleteAutoSaveExceptions(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Invalidate the specified login codes, see [here Â»](https://core.telegram.org/api/auth#invalidating-login-codes) for more info.
     *
     * @param list<string>|array<never, never> $codes The login codes to invalidate.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function invalidateSignInCodes(array $codes = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Update the [accent color and background custom emoji Â»](https://core.telegram.org/api/colors) of the current account.
     *
     * @param bool $for_profile Whether to change the accent color emoji pattern of the profile page; otherwise, the accent color and emoji pattern of messages will be changed.
     * @param array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id?: int, gift_emoji_id?: int, background_emoji_id?: int, accent_color?: int, colors?: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id?: int} $color @see https://docs.madelineproto.xyz/API_docs/types/PeerColor.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function updateColor(bool|null $for_profile = null, array|null $color = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Get a set of suggested [custom emoji stickers](https://core.telegram.org/api/custom-emoji) that can be used in an [accent color pattern](https://core.telegram.org/api/colors).
     *
     * @param list<int|string>|array<never, never> $hash Array of [Hash used for caching, for more info click here](https://core.telegram.org/api/offsets#hash-generation). @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'emojiListNotModified'}|array{_: 'emojiList', hash: list<int|string>, document_id: list<int>} @see https://docs.madelineproto.xyz/API_docs/types/EmojiList.html
     * @psalm-impure
     */
    public function getDefaultBackgroundEmojis(array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get a list of default suggested [channel emoji statuses](https://core.telegram.org/api/emoji-status).
     *
     * @param list<int|string>|array<never, never> $hash Array of [Hash used for caching, for more info click here](https://core.telegram.org/api/offsets#hash-generation). @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'account.emojiStatusesNotModified'}|array{_: 'account.emojiStatuses', hash: list<int|string>, statuses: list<array{_: 'emojiStatusEmpty'}|array{_: 'emojiStatus', document_id: int, until?: int}|array{_: 'emojiStatusCollectible', collectible_id: int, document_id: int, title: string, slug: string, pattern_document_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int, until?: int}|array{_: 'inputEmojiStatusCollectible', collectible_id: int, until?: int}>} @see https://docs.madelineproto.xyz/API_docs/types/account.EmojiStatuses.html
     * @psalm-impure
     */
    public function getChannelDefaultEmojiStatuses(array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Returns fetch the full list of [custom emoji IDs Â»](https://core.telegram.org/api/custom-emoji) that cannot be used in [channel emoji statuses Â»](https://core.telegram.org/api/emoji-status).
     *
     * @param list<int|string>|array<never, never> $hash Array of [Hash used for caching, for more info click here](https://core.telegram.org/api/offsets#hash-generation). @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'emojiListNotModified'}|array{_: 'emojiList', hash: list<int|string>, document_id: list<int>} @see https://docs.madelineproto.xyz/API_docs/types/EmojiList.html
     * @psalm-impure
     */
    public function getChannelRestrictedStatusEmojis(array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Specify a set of [Telegram Business opening hours](https://core.telegram.org/api/business#opening-hours).  
     * This info will be contained in [userFull](https://docs.madelineproto.xyz/API_docs/constructors/userFull.html).`business_work_hours`.
     * 
     * To remove all opening hours, invoke the method without setting the `business_work_hours` field.
     * 
     * Note that the opening hours specified by the user must be appropriately validated and transformed before invoking the method, as specified [here Â»](https://core.telegram.org/api/business#opening-hours).
     *
     * @param array{_: 'businessWorkHours', open_now?: bool, timezone_id?: string, weekly_open?: list<array{_: 'businessWeeklyOpen', start_minute?: int, end_minute?: int}>} $business_work_hours Opening hours (optional, if not set removes all opening hours). @see https://docs.madelineproto.xyz/API_docs/types/BusinessWorkHours.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function updateBusinessWorkHours(array|null $business_work_hours = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * [Businesses Â»](https://core.telegram.org/api/business#location) may advertise their location using this method, see [here Â»](https://core.telegram.org/api/business#location) for more info.
     * 
     * To remove business location information invoke the method without setting any of the parameters.
     *
     * @param array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: float, long: float, accuracy_radius?: int} $geo_point Optional, contains a set of geographical coordinates. @see https://docs.madelineproto.xyz/API_docs/types/InputGeoPoint.html
     * @param string $address Mandatory when setting/updating the location, contains a textual description of the address (max 96 UTF-8 chars).
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function updateBusinessLocation(array|null $geo_point = null, string|null $address = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Set a list of [Telegram Business greeting messages](https://core.telegram.org/api/business#greeting-messages).
     *
     * @param array{_: 'inputBusinessGreetingMessage', recipients: array{_: 'inputBusinessRecipients', existing_chats?: bool, new_chats?: bool, contacts?: bool, non_contacts?: bool, exclude_selected?: bool, users?: list<array|int|string>}, shortcut_id?: int, no_activity_days?: int} $message Greeting message configuration and contents. @see https://docs.madelineproto.xyz/API_docs/types/InputBusinessGreetingMessage.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function updateBusinessGreetingMessage(array|null $message = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Set a list of [Telegram Business away messages](https://core.telegram.org/api/business#away-messages).
     *
     * @param array{_: 'inputBusinessAwayMessage', schedule: array{_: 'businessAwayMessageScheduleAlways'}|array{_: 'businessAwayMessageScheduleOutsideWorkHours'}|array{_: 'businessAwayMessageScheduleCustom', start_date?: int, end_date?: int}, recipients: array{_: 'inputBusinessRecipients', existing_chats?: bool, new_chats?: bool, contacts?: bool, non_contacts?: bool, exclude_selected?: bool, users?: list<array|int|string>}, offline_only?: bool, shortcut_id?: int} $message Away message configuration and contents. @see https://docs.madelineproto.xyz/API_docs/types/InputBusinessAwayMessage.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function updateBusinessAwayMessage(array|null $message = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Connect a [business bot Â»](https://core.telegram.org/api/bots/connected-business-bots) to the current account, or to change the current connection settings.
     *
     * @param array{_: 'inputBusinessBotRecipients', existing_chats?: bool, new_chats?: bool, contacts?: bool, non_contacts?: bool, exclude_selected?: bool, users?: list<array|int|string>, exclude_users?: list<array|int|string>} $recipients Configuration for the business connection @see https://docs.madelineproto.xyz/API_docs/types/InputBusinessBotRecipients.html
     * @param bool $deleted Whether to fully disconnect the bot from the current account.
     * @param array{_: 'businessBotRights', reply?: bool, read_messages?: bool, delete_sent_messages?: bool, delete_received_messages?: bool, edit_name?: bool, edit_bio?: bool, edit_profile_photo?: bool, edit_username?: bool, view_gifts?: bool, sell_gifts?: bool, change_gift_settings?: bool, transfer_and_upgrade_gifts?: bool, transfer_stars?: bool, manage_stories?: bool} $rights Business bot rights. @see https://docs.madelineproto.xyz/API_docs/types/BusinessBotRights.html
     * @param array|int|string $bot The bot to connect or disconnect @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function updateConnectedBot(array $recipients, bool|null $deleted = null, array|null $rights = null, array|int|string|null $bot = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * List all currently connected [business bots Â»](https://core.telegram.org/api/bots/connected-business-bots)
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'account.connectedBots', connected_bots: list<array{_: 'connectedBot', recipients: array{_: 'businessBotRecipients', existing_chats: bool, new_chats: bool, contacts: bool, non_contacts: bool, exclude_selected: bool, users?: list<int>, exclude_users?: list<int>}, rights: array{_: 'businessBotRights', reply: bool, read_messages: bool, delete_sent_messages: bool, delete_received_messages: bool, edit_name: bool, edit_bio: bool, edit_profile_photo: bool, edit_username: bool, view_gifts: bool, sell_gifts: bool, change_gift_settings: bool, transfer_and_upgrade_gifts: bool, transfer_stars: bool, manage_stories: bool}, bot_id: int, device?: string, date?: int, location?: string}>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/account.ConnectedBots.html
     * @psalm-impure
     */
    public function getConnectedBots(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Bots may invoke this method to re-fetch the [updateBotBusinessConnect](https://docs.madelineproto.xyz/API_docs/constructors/updateBotBusinessConnect.html) constructor associated with a specific [business `connection_id`, see here Â»](https://core.telegram.org/api/bots/connected-business-bots) for more info on connected business bots.  
     * This is needed for example for freshly logged in bots that are receiving some [updateBotNewBusinessMessage](https://docs.madelineproto.xyz/API_docs/constructors/updateBotNewBusinessMessage.html), etc. updates because some users have already connected to the bot before it could login.  
     * In this case, the bot is receiving messages from the business connection, but it hasn't cached the associated [updateBotBusinessConnect](https://docs.madelineproto.xyz/API_docs/constructors/updateBotBusinessConnect.html) with info about the connection (can it reply to messages? etc.) yet, and cannot receive the old ones because they were sent when the bot wasn't logged into the session yet.  
     * This method can be used to fetch info about a not-yet-cached business connection, and should not be invoked if the info is already cached or to fetch changes, as eventual changes will automatically be sent as new [updateBotBusinessConnect](https://docs.madelineproto.xyz/API_docs/constructors/updateBotBusinessConnect.html) updates to the bot using the usual [update delivery methods Â»](https://core.telegram.org/api/updates).
     *
     * @param string $connection_id [Business connection ID Â»](https://core.telegram.org/api/bots/connected-business-bots).
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function getBotBusinessConnection(string|null $connection_id = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Set or remove the [Telegram Business introduction Â»](https://core.telegram.org/api/business#business-introduction).
     *
     * @param array{_: 'inputBusinessIntro', title?: string, description?: string, sticker?: array} $intro Telegram Business introduction, to remove it call the method without setting this flag. @see https://docs.madelineproto.xyz/API_docs/types/InputBusinessIntro.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function updateBusinessIntro(array|null $intro = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Pause or unpause a specific chat, temporarily disconnecting it from all [business bots Â»](https://core.telegram.org/api/bots/connected-business-bots).
     *
     * @param bool $paused Whether to pause or unpause the chat
     * @param array|int|string $peer The chat to pause @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function toggleConnectedBotPaused(bool $paused, array|int|string|null $peer = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Permanently disconnect a specific chat from all [business bots Â»](https://core.telegram.org/api/bots/connected-business-bots) (equivalent to specifying it in `recipients.exclude_users` during initial configuration with [account.updateConnectedBot Â»](https://docs.madelineproto.xyz/API_docs/methods/account.updateConnectedBot.html)); to reconnect of a chat disconnected using this method the user must reconnect the entire bot by invoking [account.updateConnectedBot Â»](https://docs.madelineproto.xyz/API_docs/methods/account.updateConnectedBot.html).
     *
     * @param array|int|string $peer The chat to disconnect @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function disablePeerConnectedBot(array|int|string|null $peer = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Update our [birthday, see here Â»](https://core.telegram.org/api/profile#birthday) for more info.
     *
     * @param array{_: 'birthday', day?: int, month?: int, year?: int} $birthday Birthday. @see https://docs.madelineproto.xyz/API_docs/types/Birthday.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function updateBirthday(array|null $birthday = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Create a [business chat deep link Â»](https://core.telegram.org/api/business#business-chat-links).
     *
     * @param array{_: 'inputBusinessChatLink', message?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>, title?: string} $link Info about the link to create. @see https://docs.madelineproto.xyz/API_docs/types/InputBusinessChatLink.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'businessChatLink', link: string, message: string, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, title?: string, views: int} @see https://docs.madelineproto.xyz/API_docs/types/BusinessChatLink.html
     * @psalm-impure
     */
    public function createBusinessChatLink(array $link, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Edit a created [business chat deep link Â»](https://core.telegram.org/api/business#business-chat-links).
     *
     * @param array{_: 'inputBusinessChatLink', message?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>, title?: string} $link New link information. @see https://docs.madelineproto.xyz/API_docs/types/InputBusinessChatLink.html
     * @param string $slug Slug of the link, obtained as specified [here Â»](https://core.telegram.org/api/links#business-chat-links).
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'businessChatLink', link: string, message: string, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, title?: string, views: int} @see https://docs.madelineproto.xyz/API_docs/types/BusinessChatLink.html
     * @psalm-impure
     */
    public function editBusinessChatLink(array $link, string|null $slug = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Delete a [business chat deep link Â»](https://core.telegram.org/api/business#business-chat-links).
     *
     * @param string $slug Slug of the link, obtained as specified [here Â»](https://core.telegram.org/api/links#business-chat-links).
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function deleteBusinessChatLink(string|null $slug = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * List all created [business chat deep links Â»](https://core.telegram.org/api/business#business-chat-links).
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'account.businessChatLinks', links: list<array{_: 'businessChatLink', link: string, message: string, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, title?: string, views: int}>, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/account.BusinessChatLinks.html
     * @psalm-impure
     */
    public function getBusinessChatLinks(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Resolve a [business chat deep link Â»](https://core.telegram.org/api/business#business-chat-links).
     *
     * @param string $slug Slug of the link, obtained as specified [here Â»](https://core.telegram.org/api/links#business-chat-links).
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'account.resolvedBusinessChatLinks', peer: array|int|string, message: string, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/account.ResolvedBusinessChatLinks.html
     * @psalm-impure
     */
    public function resolveBusinessChatLink(string|null $slug = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Associate (or remove) a personal [channel Â»](https://core.telegram.org/api/channel), that will be listed on our personal [profile page Â»](https://core.telegram.org/api/profile#personal-channel).
     * 
     * Changing it will emit an [updateUser](https://docs.madelineproto.xyz/API_docs/constructors/updateUser.html) update.
     *
     * @param array|int|string $channel The channel, pass [inputChannelEmpty](https://docs.madelineproto.xyz/API_docs/constructors/inputChannelEmpty.html) to remove it. @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function updatePersonalChannel(array|int|string|null $channel = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Disable or re-enable Telegram ads for the current [Premium](https://core.telegram.org/api/premium) account.
     * 
     * Useful for business owners that may want to launch and view their own Telegram ads via the [Telegram ad platform Â»](https://ads.telegram.org).
     *
     * @param bool $enabled Enable or disable ads.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function toggleSponsoredMessages(bool $enabled, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Get the current [reaction notification settings Â»](https://core.telegram.org/api/reactions#notifications-about-reactions).
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'reactionsNotifySettings', sound: array{_: 'notificationSoundDefault'}|array{_: 'notificationSoundNone'}|array{_: 'notificationSoundLocal', title: string, data: string}|array{_: 'notificationSoundRingtone', id: int}, show_previews: bool, messages_notify_from?: array{_: 'reactionNotificationsFromContacts'}|array{_: 'reactionNotificationsFromAll'}, stories_notify_from?: array{_: 'reactionNotificationsFromContacts'}|array{_: 'reactionNotificationsFromAll'}, poll_votes_notify_from?: array{_: 'reactionNotificationsFromContacts'}|array{_: 'reactionNotificationsFromAll'}} @see https://docs.madelineproto.xyz/API_docs/types/ReactionsNotifySettings.html
     * @psalm-impure
     */
    public function getReactionsNotifySettings(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Change the [reaction notification settings Â»](https://core.telegram.org/api/reactions#notifications-about-reactions).
     *
     * @param array{_: 'reactionsNotifySettings', sound: array{_: 'notificationSoundDefault'}|array{_: 'notificationSoundNone'}|array{_: 'notificationSoundLocal', title?: string, data?: string}|array{_: 'notificationSoundRingtone', id?: int}, show_previews: bool, messages_notify_from?: array{_: 'reactionNotificationsFromContacts'}|array{_: 'reactionNotificationsFromAll'}, stories_notify_from?: array{_: 'reactionNotificationsFromContacts'}|array{_: 'reactionNotificationsFromAll'}, poll_votes_notify_from?: array{_: 'reactionNotificationsFromContacts'}|array{_: 'reactionNotificationsFromAll'}} $settings New reaction notification settings. @see https://docs.madelineproto.xyz/API_docs/types/ReactionsNotifySettings.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'reactionsNotifySettings', sound: array{_: 'notificationSoundDefault'}|array{_: 'notificationSoundNone'}|array{_: 'notificationSoundLocal', title: string, data: string}|array{_: 'notificationSoundRingtone', id: int}, show_previews: bool, messages_notify_from?: array{_: 'reactionNotificationsFromContacts'}|array{_: 'reactionNotificationsFromAll'}, stories_notify_from?: array{_: 'reactionNotificationsFromContacts'}|array{_: 'reactionNotificationsFromAll'}, poll_votes_notify_from?: array{_: 'reactionNotificationsFromContacts'}|array{_: 'reactionNotificationsFromAll'}} @see https://docs.madelineproto.xyz/API_docs/types/ReactionsNotifySettings.html
     * @psalm-impure
     */
    public function setReactionsNotifySettings(array $settings, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Obtain a list of [emoji statuses Â»](https://core.telegram.org/api/emoji-status) for owned [collectible gifts](https://core.telegram.org/api/gifts#collectible-gifts).
     *
     * @param list<int|string>|array<never, never> $hash Array of [Hash for pagination](https://core.telegram.org/api/offsets) @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'account.emojiStatusesNotModified'}|array{_: 'account.emojiStatuses', hash: list<int|string>, statuses: list<array{_: 'emojiStatusEmpty'}|array{_: 'emojiStatus', document_id: int, until?: int}|array{_: 'emojiStatusCollectible', collectible_id: int, document_id: int, title: string, slug: string, pattern_document_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int, until?: int}|array{_: 'inputEmojiStatusCollectible', collectible_id: int, until?: int}>} @see https://docs.madelineproto.xyz/API_docs/types/account.EmojiStatuses.html
     * @psalm-impure
     */
    public function getCollectibleEmojiStatuses(array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get the number of stars we have received from the specified user thanks to [paid messages Â»](https://core.telegram.org/api/paid-messages); the received amount will be equal to the sent amount multiplied by [stars\_paid\_message\_commission\_permille](https://core.telegram.org/api/config#stars-paid-message-commission-permille) divided by 1000.
     *
     * @param array|int|string $parent_peer If set, can contain the ID of a [monoforum (channel direct messages)](https://core.telegram.org/api/monoforum) to obtain the number of stars the user has spent to send us direct messages via the channel. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param array|int|string $user_id The user that paid to send us messages. @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'account.paidMessagesRevenue', stars_amount: int} @see https://docs.madelineproto.xyz/API_docs/types/account.PaidMessagesRevenue.html
     * @psalm-impure
     */
    public function getPaidMessagesRevenue(array|int|string|null $parent_peer = null, array|int|string|null $user_id = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Allow a user to send us messages without paying if [paid messages Â»](https://core.telegram.org/api/paid-messages) are enabled.
     *
     * @param bool $refund_charged If set and `require_payment` is not set, refunds the amounts the user has already paid us to send us messages (directly or via a monoforum).
     * @param bool $require_payment If set, requires the user to pay in order to send us messages. <br>Can only be **set** by monoforums, not users, i.e. `parent_peer` must be set if this flag is set; users must instead use the [inputPrivacyKeyNoPaidMessages](https://docs.madelineproto.xyz/API_docs/constructors/inputPrivacyKeyNoPaidMessages.html) privacy setting to remove a previously added exemption. <br>If not set, allows the user to send us messages without paying (can be **unset** by both monoforums and users).
     * @param array|int|string $parent_peer If set, applies the setting within the [monoforum aka direct messages Â»](https://core.telegram.org/api/monoforum) (pass the ID of the monoforum, **not** the ID of the associated channel). @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param array|int|string $user_id The user to exempt or unexempt. @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function toggleNoPaidMessagesException(bool|null $refund_charged = null, bool|null $require_payment = null, array|int|string|null $parent_peer = null, array|int|string|null $user_id = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Changes the main profile tab of the current user, see [here Â»](https://core.telegram.org/api/profile#tabs) for more info.
     *
     * @param array{_: 'profileTabPosts'}|array{_: 'profileTabGifts'}|array{_: 'profileTabMedia'}|array{_: 'profileTabFiles'}|array{_: 'profileTabMusic'}|array{_: 'profileTabVoice'}|array{_: 'profileTabLinks'}|array{_: 'profileTabGifs'} $tab The tab to set as main tab. @see https://docs.madelineproto.xyz/API_docs/types/ProfileTab.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function setMainProfileTab(array $tab, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Adds or removes a song from the current user's profile [see here Â»](https://core.telegram.org/api/profile#music) for more info on the music tab of the profile page.
     *
     * @param bool $unsave If set, removes the song.
     * @param array $id The song to add or remove; can be an already added song when reordering songs with `after_id`. Adding an already added song will never re-add it, only move it to the top of the song list (or after the song passed in `after_id`). @see https://docs.madelineproto.xyz/API_docs/types/InputDocument.html
     * @param array $after_id If set, the song will be added after the passed song (must be already pinned on the profile). @see https://docs.madelineproto.xyz/API_docs/types/InputDocument.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function saveMusic(bool|null $unsave = null, array|null $id = null, array|null $after_id = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Fetch the full list of only the IDs of [songs currently added to the profile, see here Â»](https://core.telegram.org/api/profile#music) for more info.
     *
     * @param list<int|string>|array<never, never> $hash Array of Hash [generated Â»](https://core.telegram.org/api/offsets#hash-generation) from the previously returned list of IDs. @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'account.savedMusicIdsNotModified'}|array{_: 'account.savedMusicIds', ids: list<int>} @see https://docs.madelineproto.xyz/API_docs/types/account.SavedMusicIds.html
     * @psalm-impure
     */
    public function getSavedMusicIds(array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Obtain all [chat themes Â»](https://core.telegram.org/api/themes#chat-themes) associated to owned [collectible gifts Â»](https://core.telegram.org/api/gifts#collectible-gifts).
     *
     * @param string $offset 
     * @param int $limit Maximum number of results to return, [see pagination](https://core.telegram.org/api/offsets)
     * @param list<int|string>|array<never, never> $hash Array of Hash from a previously returned [account.chatThemes](https://docs.madelineproto.xyz/API_docs/constructors/account.chatThemes.html) constructor, to avoid returning any result if the theme list hasn't changed. @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'account.chatThemesNotModified'}|array{_: 'account.chatThemes', hash: list<int|string>, themes: list<array{_: 'chatTheme', emoticon: string}|array{_: 'chatThemeUniqueGift', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: int, edge_color: int, text_color: int}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, crafted: bool, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array{_: 'starGiftAttributeRarity', permille: int}|array{_: 'starGiftAttributeRarityUncommon'}|array{_: 'starGiftAttributeRarityRare'}|array{_: 'starGiftAttributeRarityEpic'}|array{_: 'starGiftAttributeRarityLegendary'}, name: string, backdrop_id: int, center_color: int, edge_color: int, pattern_color: int, text_color: int}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array|int|string, sender_id?: array|int|string, date: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: int, background_emoji_id?: int}|array{_: 'peerColorCollectible', collectible_id: int, gift_emoji_id: int, background_emoji_id: int, accent_color: int, colors: list<int>, dark_accent_color?: int, dark_colors?: list<int>}|array{_: 'inputPeerColorCollectible', collectible_id: int}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, theme_settings: list<array{_: 'themeSettings', base_theme: array{_: 'baseThemeClassic'}|array{_: 'baseThemeDay'}|array{_: 'baseThemeNight'}|array{_: 'baseThemeTinted'}|array{_: 'baseThemeArctic'}, message_colors_animated: bool, accent_color: int, outbox_accent_color?: int, message_colors?: list<int>, wallpaper?: array{_: 'wallPaper', id: int, creator: bool, default: bool, pattern: bool, dark: bool, access_hash: int, slug: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, settings?: array{_: 'wallPaperSettings', blur: bool, motion: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string}}|array{_: 'wallPaperNoFile', id: int, default: bool, dark: bool, settings?: array{_: 'wallPaperSettings', blur: bool, motion: bool, background_color?: int, second_background_color?: int, third_background_color?: int, fourth_background_color?: int, intensity?: int, rotation?: int, emoticon?: string}}}>}>, chats: list<array|int|string>, users: list<array|int|string>, next_offset?: string} @see https://docs.madelineproto.xyz/API_docs/types/account.ChatThemes.html
     * @psalm-impure
     */
    public function getUniqueGiftChatThemes(string|null $offset = '', int|null $limit = 0, array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'account.passkeyRegistrationOptions', options: mixed} @see https://docs.madelineproto.xyz/API_docs/types/account.PasskeyRegistrationOptions.html
     * @psalm-impure
     */
    public function initPasskeyRegistration(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param array{_: 'inputPasskeyCredentialPublicKey', response: array{_: 'inputPasskeyResponseRegister', client_data: mixed, attestation_data?: string}|array{_: 'inputPasskeyResponseLogin', client_data: mixed, authenticator_data?: string, signature?: string, user_handle?: string}, id?: string, raw_id?: string} $credential @see https://docs.madelineproto.xyz/API_docs/types/InputPasskeyCredential.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'passkey', id: string, name: string, date: int, software_emoji_id?: int, last_usage_date?: int} @see https://docs.madelineproto.xyz/API_docs/types/Passkey.html
     * @psalm-impure
     */
    public function registerPasskey(array $credential, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'account.passkeys', passkeys: list<array{_: 'passkey', id: string, name: string, date: int, software_emoji_id?: int, last_usage_date?: int}>} @see https://docs.madelineproto.xyz/API_docs/types/account.Passkeys.html
     * @psalm-impure
     */
    public function getPasskeys(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param string $id 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function deletePasskey(string|null $id = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * 
     *
     * @param array|int|string $bot_id @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function confirmBotConnection(array|int|string|null $bot_id = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * 
     *
     * @param list<int|string>|array<never, never> $hash Array of  @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'account.webBrowserSettingsNotModified'}|array{_: 'account.webBrowserSettings', open_external_browser: bool, display_close_button: bool, external_exceptions: list<array{_: 'webDomainException', domain: string, url: string, title: string, favicon?: int}>, inapp_exceptions: list<array{_: 'webDomainException', domain: string, url: string, title: string, favicon?: int}>, hash: list<int|string>} @see https://docs.madelineproto.xyz/API_docs/types/account.WebBrowserSettings.html
     * @psalm-impure
     */
    public function getWebBrowserSettings(array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param bool $open_external_browser 
     * @param bool $display_close_button 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'account.webBrowserSettingsNotModified'}|array{_: 'account.webBrowserSettings', open_external_browser: bool, display_close_button: bool, external_exceptions: list<array{_: 'webDomainException', domain: string, url: string, title: string, favicon?: int}>, inapp_exceptions: list<array{_: 'webDomainException', domain: string, url: string, title: string, favicon?: int}>, hash: list<int|string>} @see https://docs.madelineproto.xyz/API_docs/types/account.WebBrowserSettings.html
     * @psalm-impure
     */
    public function updateWebBrowserSettings(bool|null $open_external_browser = null, bool|null $display_close_button = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param bool $delete 
     * @param bool $open_external_browser 
     * @param string $url 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function toggleWebBrowserSettingsException(bool|null $delete = null, bool|null $open_external_browser = null, string|null $url = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'account.webBrowserSettingsNotModified'}|array{_: 'account.webBrowserSettings', open_external_browser: bool, display_close_button: bool, external_exceptions: list<array{_: 'webDomainException', domain: string, url: string, title: string, favicon?: int}>, inapp_exceptions: list<array{_: 'webDomainException', domain: string, url: string, title: string, favicon?: int}>, hash: list<int|string>} @see https://docs.madelineproto.xyz/API_docs/types/account.WebBrowserSettings.html
     * @psalm-impure
     */
    public function deleteWebBrowserSettingsExceptions(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;
}
<?php
/**
 * This file is automatic generated by build_docs.php file
 * and is used only for autocomplete in multiple IDE
 * don't modify manually.
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\Namespace;

/** @psalm-mutable */
interface Stories
{
    /**
     * Check whether we can post stories as the specified peer.
     *
     * @param array|int|string $peer The peer from which we wish to post stories. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'stories.canSendStoryCount', count_remains: int} @see https://docs.madelineproto.xyz/API_docs/types/stories.CanSendStoryCount.html
     * @psalm-impure
     */
    public function canSendStory(array|int|string|null $peer = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Uploads a [Telegram Story](https://core.telegram.org/api/stories).
     * 
     * May also be used in a [business connection](https://core.telegram.org/api/bots/connected-business-bots), *not* by wrapping the query in [invokeWithBusinessConnection Â»](https://docs.madelineproto.xyz/API_docs/methods/invokeWithBusinessConnection.html), but rather by specifying the ID of a controlled business user in `peer`.
     *
     * @param bool $pinned Whether to add the story to the profile automatically upon expiration. If not set, the story will only be added to the archive, see [here Â»](https://core.telegram.org/api/stories) for more info.
     * @param bool $noforwards If set, disables forwards, screenshots, and downloads.
     * @param bool $fwd_modified Set this flag when reposting stories with `fwd_from_id`+`fwd_from_id`, if the `media` was modified before reposting.
     * @param array|int|string $peer The peer to send the story as. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param \danog\MadelineProto\EventHandler\Media|string|array $media The story media. @see https://docs.madelineproto.xyz/API_docs/types/InputMedia.html
     * @param list<array{_: 'mediaAreaVenue', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, geo?: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash?: int, accuracy_radius?: int}, title?: string, address?: string, provider?: string, venue_id?: string, venue_type?: string}|array{_: 'inputMediaAreaVenue', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, query_id?: int, result_id?: string}|array{_: 'mediaAreaGeoPoint', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, geo?: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash?: int, accuracy_radius?: int}, address?: array{_: 'geoPointAddress', country_iso2?: string, state?: string, city?: string, street?: string}}|array{_: 'mediaAreaSuggestedReaction', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, dark?: bool, flipped?: bool, reaction?: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon?: string}|array{_: 'reactionCustomEmoji', document_id?: int}|array{_: 'reactionPaid'}}|array{_: 'mediaAreaChannelPost', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, channel_id?: int, msg_id?: int}|array{_: 'inputMediaAreaChannelPost', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, channel?: array|int|string, msg_id?: int}|array{_: 'mediaAreaUrl', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, url?: string}|array{_: 'mediaAreaWeather', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, temperature_c: float, emoji?: string, color?: int}|array{_: 'mediaAreaStarGift', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, slug?: string}> $media_areas Array of [Media areas](https://core.telegram.org/api/stories#media-areas) associated to the story, see [here Â»](https://core.telegram.org/api/stories#media-areas) for more info. @see https://docs.madelineproto.xyz/API_docs/types/MediaArea.html
     * @param string $caption Story caption.
     * @param list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}> $entities Array of [Message entities for styled text](https://core.telegram.org/api/entities), if allowed by the [`stories_entities` client configuration parameter Â»](https://core.telegram.org/api/config#stories-entities). @see https://docs.madelineproto.xyz/API_docs/types/MessageEntity.html
     * @param \danog\MadelineProto\ParseMode $parse_mode Whether to parse HTML or Markdown markup in the message
     * @param list<array{_: 'inputPrivacyValueAllowContacts'}|array{_: 'inputPrivacyValueAllowAll'}|array{_: 'inputPrivacyValueAllowUsers', users?: list<array|int|string>}|array{_: 'inputPrivacyValueDisallowContacts'}|array{_: 'inputPrivacyValueDisallowAll'}|array{_: 'inputPrivacyValueDisallowUsers', users?: list<array|int|string>}|array{_: 'inputPrivacyValueAllowChatParticipants', chats?: list<int>}|array{_: 'inputPrivacyValueDisallowChatParticipants', chats?: list<int>}|array{_: 'inputPrivacyValueAllowCloseFriends'}|array{_: 'inputPrivacyValueAllowPremium'}|array{_: 'inputPrivacyValueAllowBots'}|array{_: 'inputPrivacyValueDisallowBots'}>|array<never, never> $privacy_rules Array of [Privacy rules](https://core.telegram.org/api/privacy) for the story, indicating who can or can't view the story. @see https://docs.madelineproto.xyz/API_docs/types/InputPrivacyRule.html
     * @param int $period Period after which the story is moved to archive (and to the profile if `pinned` is set), in seconds; must be one of `6 * 3600`, `12 * 3600`, `86400`, or `2 * 86400` for Telegram Premium users, and `86400` otherwise.
     * @param array|int|string $fwd_from_id If set, indicates that this story is a repost of story with ID `fwd_from_story` posted by the peer in `fwd_from_id`. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $fwd_from_story If set, indicates that this story is a repost of story with ID `fwd_from_story` posted by the peer in `fwd_from_id`.
     * @param list<int> $albums If set, adds the story to the specified albums.
     * @param array $music @see https://docs.madelineproto.xyz/API_docs/types/InputDocument.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function sendStory(bool|null $pinned = null, bool|null $noforwards = null, bool|null $fwd_modified = null, array|int|string|null $peer = null, \danog\MadelineProto\EventHandler\Media|array|string|null $media = null, array|null $media_areas = null, string|null $caption = null, array|null $entities = null, \danog\MadelineProto\ParseMode $parse_mode = \danog\MadelineProto\ParseMode::TEXT, array $privacy_rules = [], int|null $period = null, array|int|string|null $fwd_from_id = null, int|null $fwd_from_story = null, array|null $albums = null, array|null $music = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Edit an uploaded [story](https://core.telegram.org/api/stories)
     * 
     * May also be used in a [business connection](https://core.telegram.org/api/bots/connected-business-bots), *not* by wrapping the query in [invokeWithBusinessConnection Â»](https://docs.madelineproto.xyz/API_docs/methods/invokeWithBusinessConnection.html), but rather by specifying the ID of a controlled business user in `peer`: in this context, the method can only be used to edit stories posted by the same business bot on behalf of the user with [stories.sendStory](https://docs.madelineproto.xyz/API_docs/methods/stories.sendStory.html).
     *
     * @param array|int|string $peer Peer where the story was posted. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $id ID of story to edit.
     * @param \danog\MadelineProto\EventHandler\Media|string|array $media If specified, replaces the story media. @see https://docs.madelineproto.xyz/API_docs/types/InputMedia.html
     * @param list<array{_: 'mediaAreaVenue', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, geo?: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash?: int, accuracy_radius?: int}, title?: string, address?: string, provider?: string, venue_id?: string, venue_type?: string}|array{_: 'inputMediaAreaVenue', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, query_id?: int, result_id?: string}|array{_: 'mediaAreaGeoPoint', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, geo?: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash?: int, accuracy_radius?: int}, address?: array{_: 'geoPointAddress', country_iso2?: string, state?: string, city?: string, street?: string}}|array{_: 'mediaAreaSuggestedReaction', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, dark?: bool, flipped?: bool, reaction?: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon?: string}|array{_: 'reactionCustomEmoji', document_id?: int}|array{_: 'reactionPaid'}}|array{_: 'mediaAreaChannelPost', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, channel_id?: int, msg_id?: int}|array{_: 'inputMediaAreaChannelPost', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, channel?: array|int|string, msg_id?: int}|array{_: 'mediaAreaUrl', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, url?: string}|array{_: 'mediaAreaWeather', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, temperature_c: float, emoji?: string, color?: int}|array{_: 'mediaAreaStarGift', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, slug?: string}> $media_areas Array of [Media areas](https://core.telegram.org/api/stories#media-areas) associated to the story, see [here Â»](https://core.telegram.org/api/stories#media-areas) for more info. @see https://docs.madelineproto.xyz/API_docs/types/MediaArea.html
     * @param string $caption If specified, replaces the story caption.
     * @param list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}> $entities Array of [Message entities for styled text in the caption](https://core.telegram.org/api/entities), if allowed by the [`stories_entities` client configuration parameter Â»](https://core.telegram.org/api/config#stories-entities). @see https://docs.madelineproto.xyz/API_docs/types/MessageEntity.html
     * @param \danog\MadelineProto\ParseMode $parse_mode Whether to parse HTML or Markdown markup in the message
     * @param list<array{_: 'inputPrivacyValueAllowContacts'}|array{_: 'inputPrivacyValueAllowAll'}|array{_: 'inputPrivacyValueAllowUsers', users?: list<array|int|string>}|array{_: 'inputPrivacyValueDisallowContacts'}|array{_: 'inputPrivacyValueDisallowAll'}|array{_: 'inputPrivacyValueDisallowUsers', users?: list<array|int|string>}|array{_: 'inputPrivacyValueAllowChatParticipants', chats?: list<int>}|array{_: 'inputPrivacyValueDisallowChatParticipants', chats?: list<int>}|array{_: 'inputPrivacyValueAllowCloseFriends'}|array{_: 'inputPrivacyValueAllowPremium'}|array{_: 'inputPrivacyValueAllowBots'}|array{_: 'inputPrivacyValueDisallowBots'}> $privacy_rules Array of If specified, alters the [privacy settings Â»](https://core.telegram.org/api/privacy) of the story, changing who can or can't view the story. @see https://docs.madelineproto.xyz/API_docs/types/InputPrivacyRule.html
     * @param array $music @see https://docs.madelineproto.xyz/API_docs/types/InputDocument.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function editStory(array|int|string|null $peer = null, int|null $id = 0, \danog\MadelineProto\EventHandler\Media|array|string|null $media = null, array|null $media_areas = null, string|null $caption = null, array|null $entities = null, \danog\MadelineProto\ParseMode $parse_mode = \danog\MadelineProto\ParseMode::TEXT, array|null $privacy_rules = null, array|null $music = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Deletes some posted [stories](https://core.telegram.org/api/stories).
     *
     * @param array|int|string $peer Channel/user from where to delete stories. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param list<int>|array<never, never> $id IDs of stories to delete.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @param ?string $businessConnectionId Business connection ID, received through an updateBotBusinessConnect update.
     * @return list<int> 
     * @psalm-impure
     */
    public function deleteStories(array|int|string|null $peer = null, array $id = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null, ?string $businessConnectionId = null): array;

    /**
     * Pin or unpin one or more stories
     *
     * @param bool $pinned Whether to pin or unpin the stories
     * @param array|int|string $peer Peer where to pin or unpin stories @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param list<int>|array<never, never> $id IDs of stories to pin or unpin
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return list<int> 
     * @psalm-impure
     */
    public function togglePinned(bool $pinned, array|int|string|null $peer = null, array $id = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Fetch the List of active (or active and hidden) stories, see [here Â»](https://core.telegram.org/api/stories#watching-stories) for more info on watching stories.
     *
     * @param bool $next If `next` and `state` are both set, uses the passed `state` to paginate to the next results; if neither `state` nor `next` are set, fetches the initial page; if `state` is set and `next` is not set, check for changes in the active/hidden peerset, see [here Â»](https://core.telegram.org/api/stories#watching-stories) for more info on the full flow.
     * @param bool $hidden If set, fetches the hidden active story list, otherwise fetches the active story list, see [here Â»](https://core.telegram.org/api/stories#watching-stories) for more info on the full flow.
     * @param string $state If `next` and `state` are both set, uses the passed `state` to paginate to the next results; if neither `state` nor `next` are set, fetches the initial page; if `state` is set and `next` is not set, check for changes in the active/hidden peerset, see [here Â»](https://core.telegram.org/api/stories#watching-stories) for more info on the full flow.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'stories.allStoriesNotModified', stealth_mode: array{_: 'storiesStealthMode', active_until_date?: int, cooldown_until_date?: int}, state: string}|array{_: 'stories.allStories', stealth_mode: array{_: 'storiesStealthMode', active_until_date?: int, cooldown_until_date?: int}, has_more: bool, count: int, state: string, peer_stories: list<array{_: 'peerStories', peer: array|int|string, max_read_id?: int, stories: list<array{_: 'storyItemDeleted', id: int}|array{_: 'storyItemSkipped', close_friends: bool, live: bool, id: int, date: int, expire_date: int}|array{_: 'storyItem', pinned: bool, public: bool, close_friends: bool, min: bool, noforwards: bool, edited: bool, contacts: bool, selected_contacts: bool, out: bool, id: int, date: int, from_id?: array|int|string, fwd_from?: array{_: 'storyFwdHeader', modified: bool, from?: array|int|string, from_name?: string, story_id?: int}, expire_date: int, caption?: string, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, media: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, media_areas?: list<array{_: 'mediaAreaVenue', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash: int, accuracy_radius?: int}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'inputMediaAreaVenue', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, query_id: int, result_id: string}|array{_: 'mediaAreaGeoPoint', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash: int, accuracy_radius?: int}, address?: array{_: 'geoPointAddress', country_iso2: string, state?: string, city?: string, street?: string}}|array{_: 'mediaAreaSuggestedReaction', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, dark: bool, flipped: bool, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}}|array{_: 'mediaAreaChannelPost', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, channel_id: int, msg_id: int}|array{_: 'inputMediaAreaChannelPost', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, channel: array|int|string, msg_id: int}|array{_: 'mediaAreaUrl', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, url: string}|array{_: 'mediaAreaWeather', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, temperature_c: float, emoji: string, color: int}|array{_: 'mediaAreaStarGift', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, slug: string}>, privacy?: list<array{_: 'privacyValueAllowContacts'}|array{_: 'privacyValueAllowAll'}|array{_: 'privacyValueAllowUsers', users: list<int>}|array{_: 'privacyValueDisallowContacts'}|array{_: 'privacyValueDisallowAll'}|array{_: 'privacyValueDisallowUsers', users: list<int>}|array{_: 'privacyValueAllowChatParticipants', chats: list<int>}|array{_: 'privacyValueDisallowChatParticipants', chats: list<int>}|array{_: 'privacyValueAllowCloseFriends'}|array{_: 'privacyValueAllowPremium'}|array{_: 'privacyValueAllowBots'}|array{_: 'privacyValueDisallowBots'}>, views?: array{_: 'storyViews', has_viewers: bool, views_count: int, forwards_count?: int, reactions?: list<array{_: 'reactionCount', chosen_order?: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: array}|array{_: 'reactionCustomEmoji', document_id: array}|array{_: 'reactionPaid'}, count: int}>, reactions_count?: int, recent_viewers?: list<int>}, sent_reaction?: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}, albums?: list<int>, music?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}>}>, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/stories.AllStories.html
     * @psalm-impure
     */
    public function getAllStories(bool|null $next = null, bool|null $hidden = null, string|null $state = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Fetch the [stories](https://core.telegram.org/api/stories#pinned-or-archived-stories) pinned on a peer's profile.
     *
     * @param array|int|string $peer Peer whose pinned stories should be fetched @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $offset_id [Offsets for pagination, for more info click here](https://core.telegram.org/api/offsets)
     * @param int $limit Maximum number of results to return, [see pagination](https://core.telegram.org/api/offsets)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'stories.stories', count: int, stories: list<array{_: 'storyItemDeleted', id: int}|array{_: 'storyItemSkipped', close_friends: bool, live: bool, id: int, date: int, expire_date: int}|array{_: 'storyItem', pinned: bool, public: bool, close_friends: bool, min: bool, noforwards: bool, edited: bool, contacts: bool, selected_contacts: bool, out: bool, id: int, date: int, from_id?: array|int|string, fwd_from?: array{_: 'storyFwdHeader', modified: bool, from?: array|int|string, from_name?: string, story_id?: int}, expire_date: int, caption?: string, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, media: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, media_areas?: list<array{_: 'mediaAreaVenue', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash: int, accuracy_radius?: int}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'inputMediaAreaVenue', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, query_id: int, result_id: string}|array{_: 'mediaAreaGeoPoint', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash: int, accuracy_radius?: int}, address?: array{_: 'geoPointAddress', country_iso2: string, state?: string, city?: string, street?: string}}|array{_: 'mediaAreaSuggestedReaction', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, dark: bool, flipped: bool, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}}|array{_: 'mediaAreaChannelPost', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, channel_id: int, msg_id: int}|array{_: 'inputMediaAreaChannelPost', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, channel: array|int|string, msg_id: int}|array{_: 'mediaAreaUrl', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, url: string}|array{_: 'mediaAreaWeather', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, temperature_c: float, emoji: string, color: int}|array{_: 'mediaAreaStarGift', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, slug: string}>, privacy?: list<array{_: 'privacyValueAllowContacts'}|array{_: 'privacyValueAllowAll'}|array{_: 'privacyValueAllowUsers', users: list<int>}|array{_: 'privacyValueDisallowContacts'}|array{_: 'privacyValueDisallowAll'}|array{_: 'privacyValueDisallowUsers', users: list<int>}|array{_: 'privacyValueAllowChatParticipants', chats: list<int>}|array{_: 'privacyValueDisallowChatParticipants', chats: list<int>}|array{_: 'privacyValueAllowCloseFriends'}|array{_: 'privacyValueAllowPremium'}|array{_: 'privacyValueAllowBots'}|array{_: 'privacyValueDisallowBots'}>, views?: array{_: 'storyViews', has_viewers: bool, views_count: int, forwards_count?: int, reactions?: list<array{_: 'reactionCount', chosen_order?: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}, count: int}>, reactions_count?: int, recent_viewers?: list<int>}, sent_reaction?: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}, albums?: list<int>, music?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}>, pinned_to_top?: list<int>, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/stories.Stories.html
     * @psalm-impure
     */
    public function getPinnedStories(array|int|string|null $peer = null, int|null $offset_id = 0, int|null $limit = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Fetch the [story archive Â»](https://core.telegram.org/api/stories#pinned-or-archived-stories) of a peer we control.
     *
     * @param array|int|string $peer Peer whose archived stories should be fetched @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $offset_id [Offsets for pagination, for more info click here](https://core.telegram.org/api/offsets)
     * @param int $limit Maximum number of results to return, [see pagination](https://core.telegram.org/api/offsets)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @param ?int $takeoutId Optional takeout ID, generated using account.initTakeoutSession, see [the takeout docs](https://core.telegram.org/api/takeout) for more info.
     * @return array{_: 'stories.stories', count: int, stories: list<array{_: 'storyItemDeleted', id: int}|array{_: 'storyItemSkipped', close_friends: bool, live: bool, id: int, date: int, expire_date: int}|array{_: 'storyItem', pinned: bool, public: bool, close_friends: bool, min: bool, noforwards: bool, edited: bool, contacts: bool, selected_contacts: bool, out: bool, id: int, date: int, from_id?: array|int|string, fwd_from?: array{_: 'storyFwdHeader', modified: bool, from?: array|int|string, from_name?: string, story_id?: int}, expire_date: int, caption?: string, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, media: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, media_areas?: list<array{_: 'mediaAreaVenue', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash: int, accuracy_radius?: int}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'inputMediaAreaVenue', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, query_id: int, result_id: string}|array{_: 'mediaAreaGeoPoint', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash: int, accuracy_radius?: int}, address?: array{_: 'geoPointAddress', country_iso2: string, state?: string, city?: string, street?: string}}|array{_: 'mediaAreaSuggestedReaction', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, dark: bool, flipped: bool, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}}|array{_: 'mediaAreaChannelPost', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, channel_id: int, msg_id: int}|array{_: 'inputMediaAreaChannelPost', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, channel: array|int|string, msg_id: int}|array{_: 'mediaAreaUrl', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, url: string}|array{_: 'mediaAreaWeather', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, temperature_c: float, emoji: string, color: int}|array{_: 'mediaAreaStarGift', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, slug: string}>, privacy?: list<array{_: 'privacyValueAllowContacts'}|array{_: 'privacyValueAllowAll'}|array{_: 'privacyValueAllowUsers', users: list<int>}|array{_: 'privacyValueDisallowContacts'}|array{_: 'privacyValueDisallowAll'}|array{_: 'privacyValueDisallowUsers', users: list<int>}|array{_: 'privacyValueAllowChatParticipants', chats: list<int>}|array{_: 'privacyValueDisallowChatParticipants', chats: list<int>}|array{_: 'privacyValueAllowCloseFriends'}|array{_: 'privacyValueAllowPremium'}|array{_: 'privacyValueAllowBots'}|array{_: 'privacyValueDisallowBots'}>, views?: array{_: 'storyViews', has_viewers: bool, views_count: int, forwards_count?: int, reactions?: list<array{_: 'reactionCount', chosen_order?: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}, count: int}>, reactions_count?: int, recent_viewers?: list<int>}, sent_reaction?: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}, albums?: list<int>, music?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}>, pinned_to_top?: list<int>, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/stories.Stories.html
     * @psalm-impure
     */
    public function getStoriesArchive(array|int|string|null $peer = null, int|null $offset_id = 0, int|null $limit = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null, ?int $takeoutId = null): array;

    /**
     * Obtain full info about a set of [stories](https://core.telegram.org/api/stories) by their IDs.
     *
     * @param array|int|string $peer Peer where the stories were posted @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param list<int>|array<never, never> $id Story IDs
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'stories.stories', count: int, stories: list<array{_: 'storyItemDeleted', id: int}|array{_: 'storyItemSkipped', close_friends: bool, live: bool, id: int, date: int, expire_date: int}|array{_: 'storyItem', pinned: bool, public: bool, close_friends: bool, min: bool, noforwards: bool, edited: bool, contacts: bool, selected_contacts: bool, out: bool, id: int, date: int, from_id?: array|int|string, fwd_from?: array{_: 'storyFwdHeader', modified: bool, from?: array|int|string, from_name?: string, story_id?: int}, expire_date: int, caption?: string, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, media: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, media_areas?: list<array{_: 'mediaAreaVenue', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash: int, accuracy_radius?: int}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'inputMediaAreaVenue', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, query_id: int, result_id: string}|array{_: 'mediaAreaGeoPoint', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash: int, accuracy_radius?: int}, address?: array{_: 'geoPointAddress', country_iso2: string, state?: string, city?: string, street?: string}}|array{_: 'mediaAreaSuggestedReaction', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, dark: bool, flipped: bool, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}}|array{_: 'mediaAreaChannelPost', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, channel_id: int, msg_id: int}|array{_: 'inputMediaAreaChannelPost', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, channel: array|int|string, msg_id: int}|array{_: 'mediaAreaUrl', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, url: string}|array{_: 'mediaAreaWeather', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, temperature_c: float, emoji: string, color: int}|array{_: 'mediaAreaStarGift', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, slug: string}>, privacy?: list<array{_: 'privacyValueAllowContacts'}|array{_: 'privacyValueAllowAll'}|array{_: 'privacyValueAllowUsers', users: list<int>}|array{_: 'privacyValueDisallowContacts'}|array{_: 'privacyValueDisallowAll'}|array{_: 'privacyValueDisallowUsers', users: list<int>}|array{_: 'privacyValueAllowChatParticipants', chats: list<int>}|array{_: 'privacyValueDisallowChatParticipants', chats: list<int>}|array{_: 'privacyValueAllowCloseFriends'}|array{_: 'privacyValueAllowPremium'}|array{_: 'privacyValueAllowBots'}|array{_: 'privacyValueDisallowBots'}>, views?: array{_: 'storyViews', has_viewers: bool, views_count: int, forwards_count?: int, reactions?: list<array{_: 'reactionCount', chosen_order?: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}, count: int}>, reactions_count?: int, recent_viewers?: list<int>}, sent_reaction?: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}, albums?: list<int>, music?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}>, pinned_to_top?: list<int>, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/stories.Stories.html
     * @psalm-impure
     */
    public function getStoriesByID(array|int|string|null $peer = null, array $id = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Hide the active stories of a specific peer, preventing them from being displayed on the action bar on the homescreen.
     *
     * @param bool $hidden Whether to hide or unhide all active stories of the peer
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function toggleAllStoriesHidden(bool $hidden, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Mark all stories up to a certain ID as read, for a given peer; will emit an [updateReadStories](https://docs.madelineproto.xyz/API_docs/constructors/updateReadStories.html) update to all logged-in sessions.
     *
     * @param array|int|string $peer The peer whose stories should be marked as read. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $max_id Mark all stories up to and including this ID as read
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return list<int> 
     * @psalm-impure
     */
    public function readStories(array|int|string|null $peer = null, int|null $max_id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Increment the view counter of one or more stories.
     *
     * @param array|int|string $peer Peer where the stories were posted. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param list<int>|array<never, never> $id IDs of the stories (maximum 200 at a time).
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function incrementStoryViews(array|int|string|null $peer = null, array $id = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Obtain the list of users that have viewed a specific [story we posted](https://core.telegram.org/api/stories)
     *
     * @param bool $just_contacts Whether to only fetch view reaction/views made by our [contacts](https://core.telegram.org/api/contacts)
     * @param bool $reactions_first Whether to return [storyView](https://docs.madelineproto.xyz/API_docs/constructors/storyView.html) info about users that reacted to the story (i.e. if set, the server will first sort results by view date as usual, and then also additionally sort the list by putting [storyView](https://docs.madelineproto.xyz/API_docs/constructors/storyView.html)s with an associated reaction first in the list). Ignored if `forwards_first` is set.
     * @param bool $forwards_first If set, returns forwards and reposts first, then reactions, then other views; otherwise returns interactions sorted just by interaction date.
     * @param array|int|string $peer Peer where the story was posted @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param string $q Search for specific peers
     * @param int $id Story ID
     * @param string $offset Offset for pagination, obtained from [stories.storyViewsList](https://docs.madelineproto.xyz/API_docs/constructors/stories.storyViewsList.html).`next_offset`
     * @param int $limit Maximum number of results to return, [see pagination](https://core.telegram.org/api/offsets)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'stories.storyViewsList', count: int, views_count: int, forwards_count: int, reactions_count: int, views: list<array{_: 'storyView', blocked: bool, blocked_my_stories_from: bool, user_id: int, date: int, reaction?: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}}|array{_: 'storyViewPublicForward', blocked: bool, blocked_my_stories_from: bool, message: array{_: 'messageEmpty', id: int, peer_id?: array|int|string}|array{_: 'message', peer_id: array|int|string, out: bool, mentioned: bool, media_unread: bool, silent: bool, post: bool, from_scheduled: bool, legacy: bool, edit_hide: bool, pinned: bool, noforwards: bool, invert_media: bool, offline: bool, video_processing_pending: bool, paid_suggested_post_stars: bool, paid_suggested_post_ton: bool, id: int, from_id?: array|int|string, from_boosts_applied?: int, from_rank?: string, saved_peer_id?: array|int|string, fwd_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, via_bot_id?: int, via_business_bot_id?: int, guestchat_via_from?: array|int|string, reply_to?: array{_: 'messageReplyHeader', reply_to_scheduled: bool, forum_topic: bool, quote: bool, reply_to_ephemeral: bool, reply_to_msg_id?: int, reply_to_peer_id?: array|int|string, reply_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, reply_media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_to_top_id?: int, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, quote_offset?: int, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, url: string, webpage_id: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, email: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, phone: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, name: string}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, user_id: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, date: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, type: array{_: 'inlineButtonTypeUrl', url: array}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: array, url: array, button_id: array}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: array, fwd_text?: array, url: array, bot?: array}|array{_: 'inlineButtonTypeWebView', url: array}|array{_: 'inlineButtonTypeCallback', requires_password: array, data: array}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: array, query: array, peer_types?: list<array>}|array{_: 'inlineButtonTypeUserProfile', user_id: array}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array}|array{_: 'inlineButtonTypeCopy', copy_text: array}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: array, bg_danger: array, bg_success: array, link: array}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}>}|array{_: 'messageReplyStoryHeader', peer: array|int|string, story_id: int}, date: int, message: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_markup?: array, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, views?: int, forwards?: int, replies?: array{_: 'messageReplies', comments: bool, replies: int, replies_pts: int, recent_repliers?: list<array|int|string>, channel_id?: int, max_id?: int, read_max_id?: int}, edit_date?: int, post_author?: string, grouped_id?: int, reactions?: array{_: 'messageReactions', min: bool, can_see_list: bool, reactions_as_tags: bool, results: list<array{_: 'reactionCount', chosen_order?: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: array}|array{_: 'reactionCustomEmoji', document_id: array}|array{_: 'reactionPaid'}, count: int}>, recent_reactions?: list<array{_: 'messagePeerReaction', peer_id: array|int|string, big: bool, unread: bool, my: bool, date: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: array}|array{_: 'reactionCustomEmoji', document_id: array}|array{_: 'reactionPaid'}}>, top_reactors?: list<array{_: 'messageReactor', top: bool, my: bool, anonymous: bool, peer_id?: array|int|string, count: int}>}, restriction_reason?: list<array{_: 'restrictionReason', platform: string, reason: string, text: string}>, ttl_period?: int, quick_reply_shortcut_id?: int, effect?: int, factcheck?: array{_: 'factCheck', need_check: bool, country?: string, text?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, hash: list<int|string>}, report_delivery_until_date?: int, paid_message_stars?: int, suggested_post?: array{_: 'suggestedPost', accepted: bool, rejected: bool, price?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, schedule_date?: int}, schedule_repeat_period?: int, summary_from_language?: string, rich_message?: array{_: 'richMessage', rtl: bool, part: bool, blocks: list<array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, published_date: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, language: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: string}|array{_: 'pageBlockList', items: list<array{_: 'pageListItemText', text: array, checkbox: array, checked: array}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, collapsed: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array, credit: array}, spoiler: bool, photo_id: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array, credit: array}, autoplay: bool, loop: bool, spoiler: bool, video_id: int}|array{_: 'pageBlockCover', cover: array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array}|array{_: 'pageBlockSubtitle', text: array}|array{_: 'pageBlockAuthorDate', author: array, published_date: array}|array{_: 'pageBlockHeader', text: array}|array{_: 'pageBlockSubheader', text: array}|array{_: 'pageBlockParagraph', text: array}|array{_: 'pageBlockPreformatted', text: array, language: array}|array{_: 'pageBlockFooter', text: array}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: array}|array{_: 'pageBlockList', items: list<array>}|array{_: 'pageBlockBlockquote', text: array, caption: array, collapsed: array}|array{_: 'pageBlockPullquote', text: array, caption: array}|array{_: 'pageBlockPhoto', caption: array, spoiler: array, photo_id: array, url?: array, webpage_id?: array}|array{_: 'pageBlockVideo', caption: array, autoplay: array, loop: array, spoiler: array, video_id: array}|array{_: 'pageBlockEmbed', caption: array, full_width: array, allow_scrolling: array, url?: array, html?: array, poster_photo_id?: array, w?: array, h?: array}|array{_: 'pageBlockChannel', channel: array}|array{_: 'pageBlockAudio', caption: array, audio_id: array}|array{_: 'pageBlockKicker', text: array}|array{_: 'pageBlockTable', title: array, bordered: array, striped: array, compact: array, rows: list<array>}|array{_: 'pageBlockOrderedList', reversed: array, items: list<array>, start?: array, type?: array}|array{_: 'pageBlockRelatedArticles', title: array, articles: list<array>}|array{_: 'pageBlockMap', caption: array, geo: array, zoom: array, w: array, h: array}|array{_: 'pageBlockHeading1', text: array}|array{_: 'pageBlockHeading2', text: array}|array{_: 'pageBlockHeading3', text: array}|array{_: 'pageBlockHeading4', text: array}|array{_: 'pageBlockHeading5', text: array}|array{_: 'pageBlockHeading6', text: array}|array{_: 'pageBlockMath', source: array}|array{_: 'pageBlockThinking', text: array}|array{_: 'inputPageBlockMap', caption: array, geo: array, zoom: array, w: array, h: array}|array{_: 'pageBlockButtonRow', align_left: array, align_center: array, align_right: array, buttons: list<array>}|array{_: 'pageBlockDocument', caption: array, document_id: array}}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array, credit: array}, full_width: bool, allow_scrolling: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array, credit: array}, audio_id: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, bordered: bool, striped: bool, compact: bool, rows: list<array{_: 'pageTableRow', cells: list<array>}>}|array{_: 'pageBlockOrderedList', reversed: bool, items: list<array{_: 'pageListOrderedItemText', text: array, checkbox: array, checked: array, num?: array, value?: array, type?: array}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, articles: list<array{_: 'pageRelatedArticle', url: array, webpage_id: array, title?: array, description?: array, photo_id?: array, author?: array, published_date?: array}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockMath', source: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: array, long: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockButtonRow', align_left: bool, align_center: bool, align_right: bool, buttons: list<array{_: 'pageButton', text: array, type: array, style?: array}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array, credit: array}, document_id: int}>, photos: list<array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}}|array{_: 'messageService', peer_id: array|int|string, out: bool, mentioned: bool, media_unread: bool, reactions_are_possible: bool, silent: bool, post: bool, legacy: bool, id: int, from_id?: array|int|string, saved_peer_id?: array|int|string, reply_to?: array{_: 'messageReplyHeader', reply_to_scheduled: bool, forum_topic: bool, quote: bool, reply_to_ephemeral: bool, reply_to_msg_id?: int, reply_to_peer_id?: array|int|string, reply_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, reply_media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_to_top_id?: int, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, quote_offset?: int, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, url: string, webpage_id: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, email: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, phone: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, name: string}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, user_id: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, date: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, type: array{_: 'inlineButtonTypeUrl', url: array}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: array, url: array, button_id: array}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: array, fwd_text?: array, url: array, bot?: array}|array{_: 'inlineButtonTypeWebView', url: array}|array{_: 'inlineButtonTypeCallback', requires_password: array, data: array}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: array, query: array, peer_types?: list<array>}|array{_: 'inlineButtonTypeUserProfile', user_id: array}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array}|array{_: 'inlineButtonTypeCopy', copy_text: array}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: array, bg_danger: array, bg_success: array, link: array}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}>}|array{_: 'messageReplyStoryHeader', peer: array|int|string, story_id: int}, date: int, action: array{_: 'messageActionEmpty'}|array{_: 'messageActionChatCreate', title: string, users: list<int>}|array{_: 'messageActionChatEditTitle', title: string}|array{_: 'messageActionChatEditPhoto', photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'messageActionChatDeletePhoto'}|array{_: 'messageActionChatAddUser', users: list<int>}|array{_: 'messageActionChatDeleteUser', user_id: int}|array{_: 'messageActionChatJoinedByLink', inviter_id: int}|array{_: 'messageActionChannelCreate', title: string}|array{_: 'messageActionChatMigrateTo', channel_id: int}|array{_: 'messageActionChannelMigrateFrom', title: string, chat_id: array|int|string}|array{_: 'messageActionPinMessage'}|array{_: 'messageActionHistoryClear'}|array{_: 'messageActionGameScore', game_id: int, score: int}|array{_: 'messageActionPaymentSentMe', charge: array{_: 'paymentCharge', id: string, provider_charge_id: string}, recurring_init: bool, recurring_used: bool, currency: string, total_amount: int, payload: string, info?: array{_: 'paymentRequestedInfo', name?: string, phone?: string, email?: string, shipping_address?: array{_: 'postAddress', street_line1: array, street_line2: array, city: array, state: array, country_iso2: array, post_code: array}}, shipping_option_id?: string, subscription_until_date?: int}|array{_: 'messageActionPaymentSent', recurring_init: bool, recurring_used: bool, currency: string, total_amount: int, invoice_slug?: string, subscription_until_date?: int}|array{_: 'messageActionPhoneCall', video: bool, call_id: int, reason?: array{_: 'phoneCallDiscardReasonMissed'}|array{_: 'phoneCallDiscardReasonDisconnect'}|array{_: 'phoneCallDiscardReasonHangup'}|array{_: 'phoneCallDiscardReasonBusy'}|array{_: 'phoneCallDiscardReasonMigrateConferenceCall', slug: string}, duration?: int}|array{_: 'messageActionScreenshotTaken'}|array{_: 'messageActionCustomAction', message: string}|array{_: 'messageActionBotAllowed', attach_menu: bool, from_request: bool, domain?: string, app?: array{_: 'botAppNotModified'}|array{_: 'botApp', id: int, access_hash: int, short_name: string, title: string, description: string, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, hash: list<int|string>}}|array{_: 'messageActionSecureValuesSentMe', credentials: array{_: 'secureCredentialsEncrypted', data: string, hash: string, secret: string}, values: list<array{_: 'secureValue', type: array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}, data?: array{_: 'secureData', data: array, data_hash: array, secret: array}, front_side?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: array, access_hash: array, size: array, dc_id: array, date: array, file_hash: array, secret: array}, reverse_side?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: array, access_hash: array, size: array, dc_id: array, date: array, file_hash: array, secret: array}, selfie?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: array, access_hash: array, size: array, dc_id: array, date: array, file_hash: array, secret: array}, translation?: list<array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: array, access_hash: array, size: array, dc_id: array, date: array, file_hash: array, secret: array}>, files?: list<array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: array, access_hash: array, size: array, dc_id: array, date: array, file_hash: array, secret: array}>, plain_data?: array{_: 'securePlainPhone', phone: array}|array{_: 'securePlainEmail', email: array}, hash: string}>}|array{_: 'messageActionSecureValuesSent', types: list<array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}>}|array{_: 'messageActionContactSignUp'}|array{_: 'messageActionGeoProximityReached', from_id: array|int|string, to_id: array|int|string, distance: int}|array{_: 'messageActionGroupCall', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, duration?: int}|array{_: 'messageActionInviteToGroupCall', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, users: list<int>}|array{_: 'messageActionSetMessagesTTL', period: int, auto_setting_from?: int}|array{_: 'messageActionGroupCallScheduled', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, schedule_date: int}|array{_: 'messageActionSetChatTheme', theme: array{_: 'chatTheme', emoticon: string}|array{_: 'chatThemeUniqueGift', gift: array{_: 'starGift', limited: array, sold_out: array, birthday: array, require_premium: array, limited_per_user: array, peer_color_available: array, auction: array, id: array, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: array, availability_remains?: array, availability_total?: array, availability_resale?: array, convert_stars: array, first_sale_date?: array, last_sale_date?: array, upgrade_stars?: array, resell_min_stars?: array, title?: array, released_by?: array, per_user_total?: array, per_user_remains?: array, locked_until_date?: array, auction_slug?: array, gifts_per_round?: array, auction_start_date?: array, upgrade_variants?: array, background?: array}|array{_: 'starGiftUnique', require_premium: array, resale_ton_only: array, theme_available: array, burned: array, crafted: array, id: array, gift_id: array, title: array, slug: array, num: array, owner_id?: array, owner_name?: array, owner_address?: array, attributes: list<array>, availability_issued: array, availability_total: array, gift_address?: array, resell_amount?: list<array>, released_by?: array, value_amount?: array, value_currency?: array, value_usd_amount?: array, theme_peer?: array, peer_color?: array, host_id?: array, offer_min_stars?: array, craft_chance_permille?: array}, theme_settings: list<array{_: 'themeSettings', base_theme: array, message_colors_animated: array, accent_color: array, outbox_accent_color?: array, message_colors?: list<array>, wallpaper?: array}>}}|array{_: 'messageActionChatJoinedByRequest'}|array{_: 'messageActionWebViewDataSentMe', text: string, data: string}|array{_: 'messageActionWebViewDataSent', text: string}|array{_: 'messageActionGiftPremium', currency: string, amount: int, days: int, crypto_currency?: string, crypto_amount?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}}|array{_: 'messageActionTopicCreate', title_missing: bool, title: string, icon_color: int, icon_emoji_id?: int}|array{_: 'messageActionTopicEdit', title?: string, icon_emoji_id?: int, closed?: bool, hidden?: bool}|array{_: 'messageActionSuggestProfilePhoto', photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'messageActionRequestedPeer', button_id: int, peers: list<array|int|string>}|array{_: 'messageActionSetChatWallPaper', wallpaper: array{_: 'wallPaper', id: int, creator: bool, default: bool, pattern: bool, dark: bool, access_hash: int, slug: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, settings?: array{_: 'wallPaperSettings', blur: array, motion: array, background_color?: array, second_background_color?: array, third_background_color?: array, fourth_background_color?: array, intensity?: array, rotation?: array, emoticon?: array}}|array{_: 'wallPaperNoFile', id: int, default: bool, dark: bool, settings?: array{_: 'wallPaperSettings', blur: array, motion: array, background_color?: array, second_background_color?: array, third_background_color?: array, fourth_background_color?: array, intensity?: array, rotation?: array, emoticon?: array}}, same: bool, for_both: bool}|array{_: 'messageActionGiftCode', via_giveaway: bool, unclaimed: bool, boost_peer?: array|int|string, days: int, slug: string, currency?: string, amount?: int, crypto_currency?: string, crypto_amount?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}}|array{_: 'messageActionGiveawayLaunch', stars?: int}|array{_: 'messageActionGiveawayResults', stars: bool, winners_count: int, unclaimed_count: int}|array{_: 'messageActionBoostApply', boosts: int}|array{_: 'messageActionRequestedPeerSentMe', button_id: int, peers: list<array{_: 'requestedPeerUser', user_id: int, first_name?: string, last_name?: string, username?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'requestedPeerChat', chat_id: array|int|string, title?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'requestedPeerChannel', channel_id: int, title?: string, username?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}>}|array{_: 'messageActionPaymentRefunded', peer: array|int|string, charge: array{_: 'paymentCharge', id: string, provider_charge_id: string}, currency: string, total_amount: int, payload?: string}|array{_: 'messageActionGiftStars', currency: string, amount: int, stars: int, crypto_currency?: string, crypto_amount?: int, transaction_id?: string}|array{_: 'messageActionPrizeStars', boost_peer: array|int|string, unclaimed: bool, stars: int, transaction_id: string, giveaway_msg_id: int}|array{_: 'messageActionStarGift', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: array, edge_color: array, text_color: array}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array, crafted: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array, name: array, backdrop_id: array, center_color: array, edge_color: array, pattern_color: array, text_color: array}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array, sender_id?: array, date: array, message?: array}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: array, nanos: array}|array{_: 'starsTonAmount', amount: array}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: array, background_emoji_id?: array}|array{_: 'peerColorCollectible', collectible_id: array, gift_emoji_id: array, background_emoji_id: array, accent_color: array, colors: list<array>, dark_accent_color?: array, dark_colors?: list<array>}|array{_: 'inputPeerColorCollectible', collectible_id: array}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, name_hidden: bool, saved: bool, converted: bool, upgraded: bool, refunded: bool, can_upgrade: bool, prepaid_upgrade: bool, upgrade_separate: bool, auction_acquired: bool, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, convert_stars?: int, upgrade_msg_id?: int, upgrade_stars?: int, from_id?: array|int|string, peer?: array|int|string, saved_id?: int, prepaid_upgrade_hash?: string, gift_msg_id?: int, to_id?: array|int|string, gift_num?: int}|array{_: 'messageActionStarGiftUnique', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: array, edge_color: array, text_color: array}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array, crafted: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array, name: array, backdrop_id: array, center_color: array, edge_color: array, pattern_color: array, text_color: array}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array, sender_id?: array, date: array, message?: array}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: array, nanos: array}|array{_: 'starsTonAmount', amount: array}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: array, background_emoji_id?: array}|array{_: 'peerColorCollectible', collectible_id: array, gift_emoji_id: array, background_emoji_id: array, accent_color: array, colors: list<array>, dark_accent_color?: array, dark_colors?: list<array>}|array{_: 'inputPeerColorCollectible', collectible_id: array}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, upgrade: bool, transferred: bool, saved: bool, refunded: bool, prepaid_upgrade: bool, assigned: bool, from_offer: bool, craft: bool, name_hidden: bool, can_export_at?: int, transfer_stars?: int, from_id?: array|int|string, peer?: array|int|string, saved_id?: int, resale_amount?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, can_transfer_at?: int, can_resell_at?: int, drop_original_details_stars?: int, can_craft_at?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}}|array{_: 'messageActionPaidMessagesRefunded', count: int, stars: int}|array{_: 'messageActionPaidMessagesPrice', broadcast_messages_allowed: bool, stars: int}|array{_: 'messageActionConferenceCall', missed: bool, active: bool, video: bool, call_id: int, duration?: int, other_participants?: list<array|int|string>}|array{_: 'messageActionTodoCompletions', completed: list<int>, incompleted: list<int>}|array{_: 'messageActionTodoAppendTasks', list: list<array{_: 'todoItem', title: array{_: 'textWithEntities', text: array, entities: list<array>}, id: int}>}|array{_: 'messageActionSuggestedPostApproval', rejected: bool, balance_too_low: bool, reject_comment?: string, schedule_date?: int, price?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}}|array{_: 'messageActionSuggestedPostSuccess', price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}}|array{_: 'messageActionSuggestedPostRefund', payer_initiated: bool}|array{_: 'messageActionGiftTon', currency: string, amount: int, crypto_currency: string, crypto_amount: int, transaction_id?: string}|array{_: 'messageActionSuggestBirthday', birthday: array{_: 'birthday', day: int, month: int, year?: int}}|array{_: 'messageActionStarGiftPurchaseOffer', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: array, edge_color: array, text_color: array}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array, crafted: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array, name: array, backdrop_id: array, center_color: array, edge_color: array, pattern_color: array, text_color: array}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array, sender_id?: array, date: array, message?: array}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: array, nanos: array}|array{_: 'starsTonAmount', amount: array}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: array, background_emoji_id?: array}|array{_: 'peerColorCollectible', collectible_id: array, gift_emoji_id: array, background_emoji_id: array, accent_color: array, colors: list<array>, dark_accent_color?: array, dark_colors?: list<array>}|array{_: 'inputPeerColorCollectible', collectible_id: array}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, accepted: bool, declined: bool, expires_at: int}|array{_: 'messageActionStarGiftPurchaseOfferDeclined', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: array, edge_color: array, text_color: array}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array, crafted: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array, name: array, backdrop_id: array, center_color: array, edge_color: array, pattern_color: array, text_color: array}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array, sender_id?: array, date: array, message?: array}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: array, nanos: array}|array{_: 'starsTonAmount', amount: array}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: array, background_emoji_id?: array}|array{_: 'peerColorCollectible', collectible_id: array, gift_emoji_id: array, background_emoji_id: array, accent_color: array, colors: list<array>, dark_accent_color?: array, dark_colors?: list<array>}|array{_: 'inputPeerColorCollectible', collectible_id: array}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, expired: bool}|array{_: 'messageActionNewCreatorPending', new_creator_id: int}|array{_: 'messageActionChangeCreator', new_creator_id: int}|array{_: 'messageActionNoForwardsToggle', prev_value: bool, new_value: bool}|array{_: 'messageActionNoForwardsRequest', prev_value: bool, new_value: bool, expired: bool}|array{_: 'messageActionPollAppendAnswer', answer: array{_: 'pollAnswer', text: array{_: 'textWithEntities', text: array, entities: list<array>}, option: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, added_by?: array|int|string, date?: int}|array{_: 'inputPollAnswer', text: array{_: 'textWithEntities', text: array, entities: list<array>}, media?: \danog\MadelineProto\EventHandler\Media|string|array}}|array{_: 'messageActionPollDeleteAnswer', answer: array{_: 'pollAnswer', text: array{_: 'textWithEntities', text: array, entities: list<array>}, option: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, added_by?: array|int|string, date?: int}|array{_: 'inputPollAnswer', text: array{_: 'textWithEntities', text: array, entities: list<array>}, media?: \danog\MadelineProto\EventHandler\Media|string|array}}|array{_: 'messageActionManagedBotCreated', bot_id: int}|array{_: 'messageActionChangeCommunity', community_id?: int}|array{_: 'messageActionChatJoinedViaCommunity', community_id: int}|array{_: 'messageActionGramTransfer', comment_encrypted: bool, amount: int, peer_address: string, transaction_id: string, comment?: string}|array{_: 'messageActionWalletTonConnectRequest', accepted: bool, declined: bool, session_id: int, expires: int, topic?: string, trace_id?: string, dapp_name?: string}|array{_: 'messageActionNoPaidMessagesTokenUsed'}, reactions?: array{_: 'messageReactions', min: bool, can_see_list: bool, reactions_as_tags: bool, results: list<array{_: 'reactionCount', chosen_order?: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: array}|array{_: 'reactionCustomEmoji', document_id: array}|array{_: 'reactionPaid'}, count: int}>, recent_reactions?: list<array{_: 'messagePeerReaction', peer_id: array|int|string, big: bool, unread: bool, my: bool, date: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: array}|array{_: 'reactionCustomEmoji', document_id: array}|array{_: 'reactionPaid'}}>, top_reactors?: list<array{_: 'messageReactor', top: bool, my: bool, anonymous: bool, peer_id?: array|int|string, count: int}>}, ttl_period?: int}}|array{_: 'storyViewPublicRepost', peer_id: array|int|string, story: array{_: 'storyItemDeleted', id: int}|array{_: 'storyItemSkipped', close_friends: bool, live: bool, id: int, date: int, expire_date: int}|array{_: 'storyItem', pinned: bool, public: bool, close_friends: bool, min: bool, noforwards: bool, edited: bool, contacts: bool, selected_contacts: bool, out: bool, id: int, date: int, from_id?: array|int|string, fwd_from?: array{_: 'storyFwdHeader', modified: bool, from?: array|int|string, from_name?: string, story_id?: int}, expire_date: int, caption?: string, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, media: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, media_areas?: list<array{_: 'mediaAreaVenue', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash: int, accuracy_radius?: int}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'inputMediaAreaVenue', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, query_id: int, result_id: string}|array{_: 'mediaAreaGeoPoint', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash: int, accuracy_radius?: int}, address?: array{_: 'geoPointAddress', country_iso2: string, state?: string, city?: string, street?: string}}|array{_: 'mediaAreaSuggestedReaction', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, dark: bool, flipped: bool, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}}|array{_: 'mediaAreaChannelPost', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, channel_id: int, msg_id: int}|array{_: 'inputMediaAreaChannelPost', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, channel: array|int|string, msg_id: int}|array{_: 'mediaAreaUrl', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, url: string}|array{_: 'mediaAreaWeather', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, temperature_c: float, emoji: string, color: int}|array{_: 'mediaAreaStarGift', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, slug: string}>, privacy?: list<array{_: 'privacyValueAllowContacts'}|array{_: 'privacyValueAllowAll'}|array{_: 'privacyValueAllowUsers', users: list<int>}|array{_: 'privacyValueDisallowContacts'}|array{_: 'privacyValueDisallowAll'}|array{_: 'privacyValueDisallowUsers', users: list<int>}|array{_: 'privacyValueAllowChatParticipants', chats: list<int>}|array{_: 'privacyValueDisallowChatParticipants', chats: list<int>}|array{_: 'privacyValueAllowCloseFriends'}|array{_: 'privacyValueAllowPremium'}|array{_: 'privacyValueAllowBots'}|array{_: 'privacyValueDisallowBots'}>, views?: array{_: 'storyViews', has_viewers: bool, views_count: int, forwards_count?: int, reactions?: list<array{_: 'reactionCount', chosen_order?: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: array}|array{_: 'reactionCustomEmoji', document_id: array}|array{_: 'reactionPaid'}, count: int}>, reactions_count?: int, recent_viewers?: list<int>}, sent_reaction?: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}, albums?: list<int>, music?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}, blocked: bool, blocked_my_stories_from: bool}>, chats: list<array|int|string>, users: list<array|int|string>, next_offset?: string} @see https://docs.madelineproto.xyz/API_docs/types/stories.StoryViewsList.html
     * @psalm-impure
     */
    public function getStoryViewsList(bool|null $just_contacts = null, bool|null $reactions_first = null, bool|null $forwards_first = null, array|int|string|null $peer = null, string|null $q = null, int|null $id = 0, string|null $offset = '', int|null $limit = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Obtain info about the view count, forward count, reactions and recent viewers of one or more [stories](https://core.telegram.org/api/stories).
     *
     * @param array|int|string $peer Peer whose stories should be fetched @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param list<int>|array<never, never> $id Story IDs
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'stories.storyViews', views: list<array{_: 'storyViews', has_viewers: bool, views_count: int, forwards_count?: int, reactions?: list<array{_: 'reactionCount', chosen_order?: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}, count: int}>, reactions_count?: int, recent_viewers?: list<int>}>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/stories.StoryViews.html
     * @psalm-impure
     */
    public function getStoriesViews(array|int|string|null $peer = null, array $id = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Generate a [story deep link](https://core.telegram.org/api/links#story-links) for a specific story
     *
     * @param array|int|string $peer Peer where the story was posted @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $id Story ID
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'exportedStoryLink', link: string} @see https://docs.madelineproto.xyz/API_docs/types/ExportedStoryLink.html
     * @psalm-impure
     */
    public function exportStoryLink(array|int|string|null $peer = null, int|null $id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Report a story.
     *
     * @param array|int|string $peer The peer that uploaded the story. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param list<int>|array<never, never> $id IDs of the stories to report.
     * @param string $option Menu option, intially empty
     * @param string $message Comment for report moderation
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'reportResultChooseOption', title: string, options: list<array{_: 'messageReportOption', text: string, option: string}>}|array{_: 'reportResultAddComment', optional: bool, option: string}|array{_: 'reportResultReported'} @see https://docs.madelineproto.xyz/API_docs/types/ReportResult.html
     * @psalm-impure
     */
    public function report(array|int|string|null $peer = null, array $id = [], string|null $option = '', string|null $message = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Activates [stories stealth mode](https://core.telegram.org/api/stories#stealth-mode), see [here Â»](https://core.telegram.org/api/stories#stealth-mode) for more info.
     * 
     * Will return an [updateStoriesStealthMode](https://docs.madelineproto.xyz/API_docs/constructors/updateStoriesStealthMode.html).
     *
     * @param bool $past Whether to erase views from any stories opened in the past [`stories_stealth_past_period` seconds Â»](https://core.telegram.org/api/config#stories-stealth-past-period), as specified by the [client configuration](https://core.telegram.org/api/config#client-configuration).
     * @param bool $future Whether to hide future story views for the next [`stories_stealth_future_period` seconds Â»](https://core.telegram.org/api/config#stories-stealth-future-period), as specified by the [client configuration](https://core.telegram.org/api/config#client-configuration).
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function activateStealthMode(bool|null $past = null, bool|null $future = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * React to a story.
     *
     * @param bool $add_to_recent Whether to add this reaction to the [recent reactions list Â»](https://core.telegram.org/api/reactions#recent-reactions).
     * @param array|int|string $peer The peer that sent the story @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $story_id ID of the story to react to
     * @param array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon?: string}|array{_: 'reactionCustomEmoji', document_id?: int}|array{_: 'reactionPaid'} $reaction Reaction @see https://docs.madelineproto.xyz/API_docs/types/Reaction.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function sendReaction(bool|null $add_to_recent = null, array|int|string|null $peer = null, int|null $story_id = 0, array|null $reaction = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Fetch the full active [story list](https://core.telegram.org/api/stories#watching-stories) of a specific peer.
     *
     * @param array|int|string $peer Peer whose stories should be fetched @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'stories.peerStories', stories: array{_: 'peerStories', peer: array|int|string, max_read_id?: int, stories: list<array{_: 'storyItemDeleted', id: int}|array{_: 'storyItemSkipped', close_friends: bool, live: bool, id: int, date: int, expire_date: int}|array{_: 'storyItem', pinned: bool, public: bool, close_friends: bool, min: bool, noforwards: bool, edited: bool, contacts: bool, selected_contacts: bool, out: bool, id: int, date: int, from_id?: array|int|string, fwd_from?: array{_: 'storyFwdHeader', modified: bool, from?: array|int|string, from_name?: string, story_id?: int}, expire_date: int, caption?: string, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, media: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, media_areas?: list<array{_: 'mediaAreaVenue', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash: int, accuracy_radius?: int}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'inputMediaAreaVenue', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, query_id: int, result_id: string}|array{_: 'mediaAreaGeoPoint', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash: int, accuracy_radius?: int}, address?: array{_: 'geoPointAddress', country_iso2: string, state?: string, city?: string, street?: string}}|array{_: 'mediaAreaSuggestedReaction', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, dark: bool, flipped: bool, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}}|array{_: 'mediaAreaChannelPost', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, channel_id: int, msg_id: int}|array{_: 'inputMediaAreaChannelPost', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, channel: array|int|string, msg_id: int}|array{_: 'mediaAreaUrl', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, url: string}|array{_: 'mediaAreaWeather', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, temperature_c: float, emoji: string, color: int}|array{_: 'mediaAreaStarGift', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, slug: string}>, privacy?: list<array{_: 'privacyValueAllowContacts'}|array{_: 'privacyValueAllowAll'}|array{_: 'privacyValueAllowUsers', users: list<int>}|array{_: 'privacyValueDisallowContacts'}|array{_: 'privacyValueDisallowAll'}|array{_: 'privacyValueDisallowUsers', users: list<int>}|array{_: 'privacyValueAllowChatParticipants', chats: list<int>}|array{_: 'privacyValueDisallowChatParticipants', chats: list<int>}|array{_: 'privacyValueAllowCloseFriends'}|array{_: 'privacyValueAllowPremium'}|array{_: 'privacyValueAllowBots'}|array{_: 'privacyValueDisallowBots'}>, views?: array{_: 'storyViews', has_viewers: bool, views_count: int, forwards_count?: int, reactions?: list<array{_: 'reactionCount', chosen_order?: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: array}|array{_: 'reactionCustomEmoji', document_id: array}|array{_: 'reactionPaid'}, count: int}>, reactions_count?: int, recent_viewers?: list<int>}, sent_reaction?: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}, albums?: list<int>, music?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}>}, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/stories.PeerStories.html
     * @psalm-impure
     */
    public function getPeerStories(array|int|string|null $peer = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Obtain the latest read story ID for all peers when first logging in, returned as a list of [updateReadStories](https://docs.madelineproto.xyz/API_docs/constructors/updateReadStories.html) updates, see [here Â»](https://core.telegram.org/api/stories#watching-stories) for more info.
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function getAllReadPeerStories(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get the IDs of the maximum read stories for a set of peers.
     *
     * @param list<array|int|string>|array<never, never> $id Array of Peers @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return list<array{_: 'recentStory', live: bool, max_id?: int}> Array of  @see https://docs.madelineproto.xyz/API_docs/types/RecentStory.html
     * @psalm-impure
     */
    public function getPeerMaxIDs(array $id = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Obtain a list of channels where the user can post [stories](https://core.telegram.org/api/stories)
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.chats', chats: list<array|int|string>}|array{_: 'messages.chatsSlice', count: int, chats: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/messages.Chats.html
     * @psalm-impure
     */
    public function getChatsToSend(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Hide the active stories of a user, preventing them from being displayed on the action bar on the homescreen, see [here Â»](https://core.telegram.org/api/stories#hiding-stories-of-other-users) for more info.
     *
     * @param bool $hidden Whether to hide or unhide stories.
     * @param array|int|string $peer Peer whose stories should be (un)hidden. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function togglePeerStoriesHidden(bool $hidden, array|int|string|null $peer = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Get the [reaction](https://core.telegram.org/api/reactions) and interaction list of a [story](https://core.telegram.org/api/stories) posted to a channel, along with the sender of each reaction.
     * 
     * Can only be used by channel admins.
     *
     * @param bool $forwards_first If set, returns forwards and reposts first, then reactions, then other views; otherwise returns interactions sorted just by interaction date.
     * @param array|int|string $peer Channel @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $id [Story](https://core.telegram.org/api/stories) ID
     * @param array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon?: string}|array{_: 'reactionCustomEmoji', document_id?: int}|array{_: 'reactionPaid'} $reaction Get only reactions of this type @see https://docs.madelineproto.xyz/API_docs/types/Reaction.html
     * @param string $offset Offset for pagination (taken from the `next_offset` field of the returned [stories.StoryReactionsList](https://docs.madelineproto.xyz/API_docs/types/stories.StoryReactionsList.html)); empty in the first request.
     * @param int $limit Maximum number of results to return, [see pagination](https://core.telegram.org/api/offsets)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'stories.storyReactionsList', count: int, reactions: list<array{_: 'storyReaction', peer_id: array|int|string, date: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}}|array{_: 'storyReactionPublicForward', message: array{_: 'messageEmpty', id: int, peer_id?: array|int|string}|array{_: 'message', peer_id: array|int|string, out: bool, mentioned: bool, media_unread: bool, silent: bool, post: bool, from_scheduled: bool, legacy: bool, edit_hide: bool, pinned: bool, noforwards: bool, invert_media: bool, offline: bool, video_processing_pending: bool, paid_suggested_post_stars: bool, paid_suggested_post_ton: bool, id: int, from_id?: array|int|string, from_boosts_applied?: int, from_rank?: string, saved_peer_id?: array|int|string, fwd_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, via_bot_id?: int, via_business_bot_id?: int, guestchat_via_from?: array|int|string, reply_to?: array{_: 'messageReplyHeader', reply_to_scheduled: bool, forum_topic: bool, quote: bool, reply_to_ephemeral: bool, reply_to_msg_id?: int, reply_to_peer_id?: array|int|string, reply_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, reply_media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_to_top_id?: int, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, quote_offset?: int, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, url: string, webpage_id: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, email: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, phone: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, name: string}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, user_id: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, date: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, type: array{_: 'inlineButtonTypeUrl', url: array}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: array, url: array, button_id: array}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: array, fwd_text?: array, url: array, bot?: array}|array{_: 'inlineButtonTypeWebView', url: array}|array{_: 'inlineButtonTypeCallback', requires_password: array, data: array}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: array, query: array, peer_types?: list<array>}|array{_: 'inlineButtonTypeUserProfile', user_id: array}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array}|array{_: 'inlineButtonTypeCopy', copy_text: array}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: array, bg_danger: array, bg_success: array, link: array}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}>}|array{_: 'messageReplyStoryHeader', peer: array|int|string, story_id: int}, date: int, message: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_markup?: array, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, views?: int, forwards?: int, replies?: array{_: 'messageReplies', comments: bool, replies: int, replies_pts: int, recent_repliers?: list<array|int|string>, channel_id?: int, max_id?: int, read_max_id?: int}, edit_date?: int, post_author?: string, grouped_id?: int, reactions?: array{_: 'messageReactions', min: bool, can_see_list: bool, reactions_as_tags: bool, results: list<array{_: 'reactionCount', chosen_order?: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: array}|array{_: 'reactionCustomEmoji', document_id: array}|array{_: 'reactionPaid'}, count: int}>, recent_reactions?: list<array{_: 'messagePeerReaction', peer_id: array|int|string, big: bool, unread: bool, my: bool, date: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: array}|array{_: 'reactionCustomEmoji', document_id: array}|array{_: 'reactionPaid'}}>, top_reactors?: list<array{_: 'messageReactor', top: bool, my: bool, anonymous: bool, peer_id?: array|int|string, count: int}>}, restriction_reason?: list<array{_: 'restrictionReason', platform: string, reason: string, text: string}>, ttl_period?: int, quick_reply_shortcut_id?: int, effect?: int, factcheck?: array{_: 'factCheck', need_check: bool, country?: string, text?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, hash: list<int|string>}, report_delivery_until_date?: int, paid_message_stars?: int, suggested_post?: array{_: 'suggestedPost', accepted: bool, rejected: bool, price?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, schedule_date?: int}, schedule_repeat_period?: int, summary_from_language?: string, rich_message?: array{_: 'richMessage', rtl: bool, part: bool, blocks: list<array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubtitle', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockAuthorDate', author: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, published_date: int}|array{_: 'pageBlockHeader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockSubheader', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockParagraph', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPreformatted', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, language: string}|array{_: 'pageBlockFooter', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: string}|array{_: 'pageBlockList', items: list<array{_: 'pageListItemText', text: array, checkbox: array, checked: array}>}|array{_: 'pageBlockBlockquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, collapsed: bool}|array{_: 'pageBlockPullquote', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, caption: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockPhoto', caption: array{_: 'pageCaption', text: array, credit: array}, spoiler: bool, photo_id: int, url?: string, webpage_id?: int}|array{_: 'pageBlockVideo', caption: array{_: 'pageCaption', text: array, credit: array}, autoplay: bool, loop: bool, spoiler: bool, video_id: int}|array{_: 'pageBlockCover', cover: array{_: 'pageBlockUnsupported'}|array{_: 'pageBlockTitle', text: array}|array{_: 'pageBlockSubtitle', text: array}|array{_: 'pageBlockAuthorDate', author: array, published_date: array}|array{_: 'pageBlockHeader', text: array}|array{_: 'pageBlockSubheader', text: array}|array{_: 'pageBlockParagraph', text: array}|array{_: 'pageBlockPreformatted', text: array, language: array}|array{_: 'pageBlockFooter', text: array}|array{_: 'pageBlockDivider'}|array{_: 'pageBlockAnchor', name: array}|array{_: 'pageBlockList', items: list<array>}|array{_: 'pageBlockBlockquote', text: array, caption: array, collapsed: array}|array{_: 'pageBlockPullquote', text: array, caption: array}|array{_: 'pageBlockPhoto', caption: array, spoiler: array, photo_id: array, url?: array, webpage_id?: array}|array{_: 'pageBlockVideo', caption: array, autoplay: array, loop: array, spoiler: array, video_id: array}|array{_: 'pageBlockEmbed', caption: array, full_width: array, allow_scrolling: array, url?: array, html?: array, poster_photo_id?: array, w?: array, h?: array}|array{_: 'pageBlockChannel', channel: array}|array{_: 'pageBlockAudio', caption: array, audio_id: array}|array{_: 'pageBlockKicker', text: array}|array{_: 'pageBlockTable', title: array, bordered: array, striped: array, compact: array, rows: list<array>}|array{_: 'pageBlockOrderedList', reversed: array, items: list<array>, start?: array, type?: array}|array{_: 'pageBlockRelatedArticles', title: array, articles: list<array>}|array{_: 'pageBlockMap', caption: array, geo: array, zoom: array, w: array, h: array}|array{_: 'pageBlockHeading1', text: array}|array{_: 'pageBlockHeading2', text: array}|array{_: 'pageBlockHeading3', text: array}|array{_: 'pageBlockHeading4', text: array}|array{_: 'pageBlockHeading5', text: array}|array{_: 'pageBlockHeading6', text: array}|array{_: 'pageBlockMath', source: array}|array{_: 'pageBlockThinking', text: array}|array{_: 'inputPageBlockMap', caption: array, geo: array, zoom: array, w: array, h: array}|array{_: 'pageBlockButtonRow', align_left: array, align_center: array, align_right: array, buttons: list<array>}|array{_: 'pageBlockDocument', caption: array, document_id: array}}|array{_: 'pageBlockEmbed', caption: array{_: 'pageCaption', text: array, credit: array}, full_width: bool, allow_scrolling: bool, url?: string, html?: string, poster_photo_id?: int, w?: int, h?: int}|array{_: 'pageBlockChannel', channel: array|int|string}|array{_: 'pageBlockAudio', caption: array{_: 'pageCaption', text: array, credit: array}, audio_id: int}|array{_: 'pageBlockKicker', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockTable', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, bordered: bool, striped: bool, compact: bool, rows: list<array{_: 'pageTableRow', cells: list<array>}>}|array{_: 'pageBlockOrderedList', reversed: bool, items: list<array{_: 'pageListOrderedItemText', text: array, checkbox: array, checked: array, num?: array, value?: array, type?: array}>, start?: int, type?: string}|array{_: 'pageBlockRelatedArticles', title: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, articles: list<array{_: 'pageRelatedArticle', url: array, webpage_id: array, title?: array, description?: array, photo_id?: array, author?: array, published_date?: array}>}|array{_: 'pageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockHeading1', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading2', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading3', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading4', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading5', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockHeading6', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'pageBlockMath', source: string}|array{_: 'pageBlockThinking', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textConcat', texts: list<array>}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'inputPageBlockMap', caption: array{_: 'pageCaption', text: array, credit: array}, geo: array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: array, long: array, accuracy_radius?: array}, zoom: int, w: int, h: int}|array{_: 'pageBlockButtonRow', align_left: bool, align_center: bool, align_right: bool, buttons: list<array{_: 'pageButton', text: array, type: array, style?: array}>}|array{_: 'pageBlockDocument', caption: array{_: 'pageCaption', text: array, credit: array}, document_id: int}>, photos: list<array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}>, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>}}|array{_: 'messageService', peer_id: array|int|string, out: bool, mentioned: bool, media_unread: bool, reactions_are_possible: bool, silent: bool, post: bool, legacy: bool, id: int, from_id?: array|int|string, saved_peer_id?: array|int|string, reply_to?: array{_: 'messageReplyHeader', reply_to_scheduled: bool, forum_topic: bool, quote: bool, reply_to_ephemeral: bool, reply_to_msg_id?: int, reply_to_peer_id?: array|int|string, reply_from?: array{_: 'messageFwdHeader', imported: bool, saved_out: bool, from_id?: array|int|string, from_name?: string, date: int, channel_post?: int, post_author?: string, saved_from_peer?: array|int|string, saved_from_msg_id?: int, saved_from_id?: array|int|string, saved_from_name?: string, saved_date?: int, psa_type?: string}, reply_media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, reply_to_top_id?: int, quote_text?: string, quote_entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, quote_offset?: int, todo_item_id?: int, poll_option?: string, quote_rich?: list<array{_: 'textEmpty'}|array{_: 'textPlain', text: string}|array{_: 'textBold', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textItalic', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textUnderline', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textStrike', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textFixed', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, url: string, webpage_id: int}|array{_: 'textEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, email: string}|array{_: 'textSubscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textSuperscript', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMarked', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, phone: string}|array{_: 'textImage', document_id: int, w: int, h: int}|array{_: 'textAnchor', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, name: string}|array{_: 'textMath', source: string}|array{_: 'textCustomEmoji', document_id: int, alt: string}|array{_: 'textSpoiler', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMention', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textHashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textBotCommand', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textCashtag', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoUrl', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoEmail', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textAutoPhone', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textBankCard', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textMentionName', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, user_id: int}|array{_: 'textDate', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, date: int}|array{_: 'textDiff', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, old_text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}|array{_: 'textButton', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}, type: array{_: 'inlineButtonTypeUrl', url: array}|array{_: 'inlineButtonTypeUrlAuth', fwd_text?: array, url: array, button_id: array}|array{_: 'inputInlineButtonTypeUrlAuth', request_write_access: array, fwd_text?: array, url: array, bot?: array}|array{_: 'inlineButtonTypeWebView', url: array}|array{_: 'inlineButtonTypeCallback', requires_password: array, data: array}|array{_: 'inlineButtonTypeGame'}|array{_: 'inlineButtonTypeBuy'}|array{_: 'inlineButtonTypeSwitchInline', same_peer: array, query: array, peer_types?: list<array>}|array{_: 'inlineButtonTypeUserProfile', user_id: array}|array{_: 'inputInlineButtonTypeUserProfile', user_id: array}|array{_: 'inlineButtonTypeCopy', copy_text: array}|array{_: 'inlineButtonTypeDisabled'}, style?: array{_: 'richButtonStyle', bg_primary: array, bg_danger: array, bg_success: array, link: array}}|array{_: 'textTonAddress', text: array{_: 'textEmpty'}|array{_: 'textPlain', text: array}|array{_: 'textImage', document_id: array, w: array, h: array}|array{_: 'textMath', source: array}|array{_: 'textCustomEmoji', document_id: array, alt: array}}>}|array{_: 'messageReplyStoryHeader', peer: array|int|string, story_id: int}, date: int, action: array{_: 'messageActionEmpty'}|array{_: 'messageActionChatCreate', title: string, users: list<int>}|array{_: 'messageActionChatEditTitle', title: string}|array{_: 'messageActionChatEditPhoto', photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'messageActionChatDeletePhoto'}|array{_: 'messageActionChatAddUser', users: list<int>}|array{_: 'messageActionChatDeleteUser', user_id: int}|array{_: 'messageActionChatJoinedByLink', inviter_id: int}|array{_: 'messageActionChannelCreate', title: string}|array{_: 'messageActionChatMigrateTo', channel_id: int}|array{_: 'messageActionChannelMigrateFrom', title: string, chat_id: array|int|string}|array{_: 'messageActionPinMessage'}|array{_: 'messageActionHistoryClear'}|array{_: 'messageActionGameScore', game_id: int, score: int}|array{_: 'messageActionPaymentSentMe', charge: array{_: 'paymentCharge', id: string, provider_charge_id: string}, recurring_init: bool, recurring_used: bool, currency: string, total_amount: int, payload: string, info?: array{_: 'paymentRequestedInfo', name?: string, phone?: string, email?: string, shipping_address?: array{_: 'postAddress', street_line1: array, street_line2: array, city: array, state: array, country_iso2: array, post_code: array}}, shipping_option_id?: string, subscription_until_date?: int}|array{_: 'messageActionPaymentSent', recurring_init: bool, recurring_used: bool, currency: string, total_amount: int, invoice_slug?: string, subscription_until_date?: int}|array{_: 'messageActionPhoneCall', video: bool, call_id: int, reason?: array{_: 'phoneCallDiscardReasonMissed'}|array{_: 'phoneCallDiscardReasonDisconnect'}|array{_: 'phoneCallDiscardReasonHangup'}|array{_: 'phoneCallDiscardReasonBusy'}|array{_: 'phoneCallDiscardReasonMigrateConferenceCall', slug: string}, duration?: int}|array{_: 'messageActionScreenshotTaken'}|array{_: 'messageActionCustomAction', message: string}|array{_: 'messageActionBotAllowed', attach_menu: bool, from_request: bool, domain?: string, app?: array{_: 'botAppNotModified'}|array{_: 'botApp', id: int, access_hash: int, short_name: string, title: string, description: string, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, hash: list<int|string>}}|array{_: 'messageActionSecureValuesSentMe', credentials: array{_: 'secureCredentialsEncrypted', data: string, hash: string, secret: string}, values: list<array{_: 'secureValue', type: array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}, data?: array{_: 'secureData', data: array, data_hash: array, secret: array}, front_side?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: array, access_hash: array, size: array, dc_id: array, date: array, file_hash: array, secret: array}, reverse_side?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: array, access_hash: array, size: array, dc_id: array, date: array, file_hash: array, secret: array}, selfie?: array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: array, access_hash: array, size: array, dc_id: array, date: array, file_hash: array, secret: array}, translation?: list<array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: array, access_hash: array, size: array, dc_id: array, date: array, file_hash: array, secret: array}>, files?: list<array{_: 'secureFileEmpty'}|array{_: 'secureFile', id: array, access_hash: array, size: array, dc_id: array, date: array, file_hash: array, secret: array}>, plain_data?: array{_: 'securePlainPhone', phone: array}|array{_: 'securePlainEmail', email: array}, hash: string}>}|array{_: 'messageActionSecureValuesSent', types: list<array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}>}|array{_: 'messageActionContactSignUp'}|array{_: 'messageActionGeoProximityReached', from_id: array|int|string, to_id: array|int|string, distance: int}|array{_: 'messageActionGroupCall', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, duration?: int}|array{_: 'messageActionInviteToGroupCall', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, users: list<int>}|array{_: 'messageActionSetMessagesTTL', period: int, auto_setting_from?: int}|array{_: 'messageActionGroupCallScheduled', call: array{_: 'inputGroupCall', id: int, access_hash: int}|array{_: 'inputGroupCallSlug', slug: string}|array{_: 'inputGroupCallInviteMessage', msg_id: int}, schedule_date: int}|array{_: 'messageActionSetChatTheme', theme: array{_: 'chatTheme', emoticon: string}|array{_: 'chatThemeUniqueGift', gift: array{_: 'starGift', limited: array, sold_out: array, birthday: array, require_premium: array, limited_per_user: array, peer_color_available: array, auction: array, id: array, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: array, availability_remains?: array, availability_total?: array, availability_resale?: array, convert_stars: array, first_sale_date?: array, last_sale_date?: array, upgrade_stars?: array, resell_min_stars?: array, title?: array, released_by?: array, per_user_total?: array, per_user_remains?: array, locked_until_date?: array, auction_slug?: array, gifts_per_round?: array, auction_start_date?: array, upgrade_variants?: array, background?: array}|array{_: 'starGiftUnique', require_premium: array, resale_ton_only: array, theme_available: array, burned: array, crafted: array, id: array, gift_id: array, title: array, slug: array, num: array, owner_id?: array, owner_name?: array, owner_address?: array, attributes: list<array>, availability_issued: array, availability_total: array, gift_address?: array, resell_amount?: list<array>, released_by?: array, value_amount?: array, value_currency?: array, value_usd_amount?: array, theme_peer?: array, peer_color?: array, host_id?: array, offer_min_stars?: array, craft_chance_permille?: array}, theme_settings: list<array{_: 'themeSettings', base_theme: array, message_colors_animated: array, accent_color: array, outbox_accent_color?: array, message_colors?: list<array>, wallpaper?: array}>}}|array{_: 'messageActionChatJoinedByRequest'}|array{_: 'messageActionWebViewDataSentMe', text: string, data: string}|array{_: 'messageActionWebViewDataSent', text: string}|array{_: 'messageActionGiftPremium', currency: string, amount: int, days: int, crypto_currency?: string, crypto_amount?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}}|array{_: 'messageActionTopicCreate', title_missing: bool, title: string, icon_color: int, icon_emoji_id?: int}|array{_: 'messageActionTopicEdit', title?: string, icon_emoji_id?: int, closed?: bool, hidden?: bool}|array{_: 'messageActionSuggestProfilePhoto', photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'messageActionRequestedPeer', button_id: int, peers: list<array|int|string>}|array{_: 'messageActionSetChatWallPaper', wallpaper: array{_: 'wallPaper', id: int, creator: bool, default: bool, pattern: bool, dark: bool, access_hash: int, slug: string, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, settings?: array{_: 'wallPaperSettings', blur: array, motion: array, background_color?: array, second_background_color?: array, third_background_color?: array, fourth_background_color?: array, intensity?: array, rotation?: array, emoticon?: array}}|array{_: 'wallPaperNoFile', id: int, default: bool, dark: bool, settings?: array{_: 'wallPaperSettings', blur: array, motion: array, background_color?: array, second_background_color?: array, third_background_color?: array, fourth_background_color?: array, intensity?: array, rotation?: array, emoticon?: array}}, same: bool, for_both: bool}|array{_: 'messageActionGiftCode', via_giveaway: bool, unclaimed: bool, boost_peer?: array|int|string, days: int, slug: string, currency?: string, amount?: int, crypto_currency?: string, crypto_amount?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}}|array{_: 'messageActionGiveawayLaunch', stars?: int}|array{_: 'messageActionGiveawayResults', stars: bool, winners_count: int, unclaimed_count: int}|array{_: 'messageActionBoostApply', boosts: int}|array{_: 'messageActionRequestedPeerSentMe', button_id: int, peers: list<array{_: 'requestedPeerUser', user_id: int, first_name?: string, last_name?: string, username?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'requestedPeerChat', chat_id: array|int|string, title?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}|array{_: 'requestedPeerChannel', channel_id: int, title?: string, username?: string, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}}>}|array{_: 'messageActionPaymentRefunded', peer: array|int|string, charge: array{_: 'paymentCharge', id: string, provider_charge_id: string}, currency: string, total_amount: int, payload?: string}|array{_: 'messageActionGiftStars', currency: string, amount: int, stars: int, crypto_currency?: string, crypto_amount?: int, transaction_id?: string}|array{_: 'messageActionPrizeStars', boost_peer: array|int|string, unclaimed: bool, stars: int, transaction_id: string, giveaway_msg_id: int}|array{_: 'messageActionStarGift', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: array, edge_color: array, text_color: array}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array, crafted: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array, name: array, backdrop_id: array, center_color: array, edge_color: array, pattern_color: array, text_color: array}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array, sender_id?: array, date: array, message?: array}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: array, nanos: array}|array{_: 'starsTonAmount', amount: array}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: array, background_emoji_id?: array}|array{_: 'peerColorCollectible', collectible_id: array, gift_emoji_id: array, background_emoji_id: array, accent_color: array, colors: list<array>, dark_accent_color?: array, dark_colors?: list<array>}|array{_: 'inputPeerColorCollectible', collectible_id: array}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, name_hidden: bool, saved: bool, converted: bool, upgraded: bool, refunded: bool, can_upgrade: bool, prepaid_upgrade: bool, upgrade_separate: bool, auction_acquired: bool, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}, convert_stars?: int, upgrade_msg_id?: int, upgrade_stars?: int, from_id?: array|int|string, peer?: array|int|string, saved_id?: int, prepaid_upgrade_hash?: string, gift_msg_id?: int, to_id?: array|int|string, gift_num?: int}|array{_: 'messageActionStarGiftUnique', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: array, edge_color: array, text_color: array}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array, crafted: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array, name: array, backdrop_id: array, center_color: array, edge_color: array, pattern_color: array, text_color: array}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array, sender_id?: array, date: array, message?: array}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: array, nanos: array}|array{_: 'starsTonAmount', amount: array}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: array, background_emoji_id?: array}|array{_: 'peerColorCollectible', collectible_id: array, gift_emoji_id: array, background_emoji_id: array, accent_color: array, colors: list<array>, dark_accent_color?: array, dark_colors?: list<array>}|array{_: 'inputPeerColorCollectible', collectible_id: array}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, upgrade: bool, transferred: bool, saved: bool, refunded: bool, prepaid_upgrade: bool, assigned: bool, from_offer: bool, craft: bool, name_hidden: bool, can_export_at?: int, transfer_stars?: int, from_id?: array|int|string, peer?: array|int|string, saved_id?: int, resale_amount?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, can_transfer_at?: int, can_resell_at?: int, drop_original_details_stars?: int, can_craft_at?: int, message?: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: array, length: array}|array{_: 'messageEntityMention', offset: array, length: array}|array{_: 'messageEntityHashtag', offset: array, length: array}|array{_: 'messageEntityBotCommand', offset: array, length: array}|array{_: 'messageEntityUrl', offset: array, length: array}|array{_: 'messageEntityEmail', offset: array, length: array}|array{_: 'messageEntityBold', offset: array, length: array}|array{_: 'messageEntityItalic', offset: array, length: array}|array{_: 'messageEntityCode', offset: array, length: array}|array{_: 'messageEntityPre', offset: array, length: array, language: array}|array{_: 'messageEntityTextUrl', offset: array, length: array, url: array}|array{_: 'messageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'inputMessageEntityMentionName', offset: array, length: array, user_id: array}|array{_: 'messageEntityPhone', offset: array, length: array}|array{_: 'messageEntityCashtag', offset: array, length: array}|array{_: 'messageEntityUnderline', offset: array, length: array}|array{_: 'messageEntityStrike', offset: array, length: array}|array{_: 'messageEntityBankCard', offset: array, length: array}|array{_: 'messageEntitySpoiler', offset: array, length: array}|array{_: 'messageEntityCustomEmoji', offset: array, length: array, document_id: array}|array{_: 'messageEntityBlockquote', collapsed: array, offset: array, length: array}|array{_: 'messageEntityFormattedDate', relative: array, short_time: array, long_time: array, short_date: array, long_date: array, day_of_week: array, offset: array, length: array, date: array}|array{_: 'messageEntityDiffInsert', offset: array, length: array}|array{_: 'messageEntityDiffReplace', offset: array, length: array, old_text: array}|array{_: 'messageEntityDiffDelete', offset: array, length: array}|array{_: 'messageEntityTonAddress', offset: array, length: array}|array{_: 'messageEntityBlockquote', offset: array, length: array}>}}|array{_: 'messageActionPaidMessagesRefunded', count: int, stars: int}|array{_: 'messageActionPaidMessagesPrice', broadcast_messages_allowed: bool, stars: int}|array{_: 'messageActionConferenceCall', missed: bool, active: bool, video: bool, call_id: int, duration?: int, other_participants?: list<array|int|string>}|array{_: 'messageActionTodoCompletions', completed: list<int>, incompleted: list<int>}|array{_: 'messageActionTodoAppendTasks', list: list<array{_: 'todoItem', title: array{_: 'textWithEntities', text: array, entities: list<array>}, id: int}>}|array{_: 'messageActionSuggestedPostApproval', rejected: bool, balance_too_low: bool, reject_comment?: string, schedule_date?: int, price?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}}|array{_: 'messageActionSuggestedPostSuccess', price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}}|array{_: 'messageActionSuggestedPostRefund', payer_initiated: bool}|array{_: 'messageActionGiftTon', currency: string, amount: int, crypto_currency: string, crypto_amount: int, transaction_id?: string}|array{_: 'messageActionSuggestBirthday', birthday: array{_: 'birthday', day: int, month: int, year?: int}}|array{_: 'messageActionStarGiftPurchaseOffer', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: array, edge_color: array, text_color: array}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array, crafted: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array, name: array, backdrop_id: array, center_color: array, edge_color: array, pattern_color: array, text_color: array}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array, sender_id?: array, date: array, message?: array}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: array, nanos: array}|array{_: 'starsTonAmount', amount: array}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: array, background_emoji_id?: array}|array{_: 'peerColorCollectible', collectible_id: array, gift_emoji_id: array, background_emoji_id: array, accent_color: array, colors: list<array>, dark_accent_color?: array, dark_colors?: list<array>}|array{_: 'inputPeerColorCollectible', collectible_id: array}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, accepted: bool, declined: bool, expires_at: int}|array{_: 'messageActionStarGiftPurchaseOfferDeclined', gift: array{_: 'starGift', limited: bool, sold_out: bool, birthday: bool, require_premium: bool, limited_per_user: bool, peer_color_available: bool, auction: bool, id: int, sticker: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, stars: int, availability_remains?: int, availability_total?: int, availability_resale?: int, convert_stars: int, first_sale_date?: int, last_sale_date?: int, upgrade_stars?: int, resell_min_stars?: int, title?: string, released_by?: array|int|string, per_user_total?: int, per_user_remains?: int, locked_until_date?: int, auction_slug?: string, gifts_per_round?: int, auction_start_date?: int, upgrade_variants?: int, background?: array{_: 'starGiftBackground', center_color: array, edge_color: array, text_color: array}}|array{_: 'starGiftUnique', require_premium: bool, resale_ton_only: bool, theme_available: bool, burned: bool, crafted: bool, id: int, gift_id: int, title: string, slug: string, num: int, owner_id?: array|int|string, owner_name?: string, owner_address?: string, attributes: list<array{_: 'starGiftAttributeModel', rarity: array, crafted: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributePattern', rarity: array, name: array, document: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'starGiftAttributeBackdrop', rarity: array, name: array, backdrop_id: array, center_color: array, edge_color: array, pattern_color: array, text_color: array}|array{_: 'starGiftAttributeOriginalDetails', recipient_id: array, sender_id?: array, date: array, message?: array}>, availability_issued: int, availability_total: int, gift_address?: string, resell_amount?: list<array{_: 'starsAmount', amount: array, nanos: array}|array{_: 'starsTonAmount', amount: array}>, released_by?: array|int|string, value_amount?: int, value_currency?: string, value_usd_amount?: int, theme_peer?: array|int|string, peer_color?: array{_: 'peerColor', color?: array, background_emoji_id?: array}|array{_: 'peerColorCollectible', collectible_id: array, gift_emoji_id: array, background_emoji_id: array, accent_color: array, colors: list<array>, dark_accent_color?: array, dark_colors?: list<array>}|array{_: 'inputPeerColorCollectible', collectible_id: array}, host_id?: array|int|string, offer_min_stars?: int, craft_chance_permille?: int}, price: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}, expired: bool}|array{_: 'messageActionNewCreatorPending', new_creator_id: int}|array{_: 'messageActionChangeCreator', new_creator_id: int}|array{_: 'messageActionNoForwardsToggle', prev_value: bool, new_value: bool}|array{_: 'messageActionNoForwardsRequest', prev_value: bool, new_value: bool, expired: bool}|array{_: 'messageActionPollAppendAnswer', answer: array{_: 'pollAnswer', text: array{_: 'textWithEntities', text: array, entities: list<array>}, option: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, added_by?: array|int|string, date?: int}|array{_: 'inputPollAnswer', text: array{_: 'textWithEntities', text: array, entities: list<array>}, media?: \danog\MadelineProto\EventHandler\Media|string|array}}|array{_: 'messageActionPollDeleteAnswer', answer: array{_: 'pollAnswer', text: array{_: 'textWithEntities', text: array, entities: list<array>}, option: string, media?: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, added_by?: array|int|string, date?: int}|array{_: 'inputPollAnswer', text: array{_: 'textWithEntities', text: array, entities: list<array>}, media?: \danog\MadelineProto\EventHandler\Media|string|array}}|array{_: 'messageActionManagedBotCreated', bot_id: int}|array{_: 'messageActionChangeCommunity', community_id?: int}|array{_: 'messageActionChatJoinedViaCommunity', community_id: int}|array{_: 'messageActionGramTransfer', comment_encrypted: bool, amount: int, peer_address: string, transaction_id: string, comment?: string}|array{_: 'messageActionWalletTonConnectRequest', accepted: bool, declined: bool, session_id: int, expires: int, topic?: string, trace_id?: string, dapp_name?: string}|array{_: 'messageActionNoPaidMessagesTokenUsed'}, reactions?: array{_: 'messageReactions', min: bool, can_see_list: bool, reactions_as_tags: bool, results: list<array{_: 'reactionCount', chosen_order?: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: array}|array{_: 'reactionCustomEmoji', document_id: array}|array{_: 'reactionPaid'}, count: int}>, recent_reactions?: list<array{_: 'messagePeerReaction', peer_id: array|int|string, big: bool, unread: bool, my: bool, date: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: array}|array{_: 'reactionCustomEmoji', document_id: array}|array{_: 'reactionPaid'}}>, top_reactors?: list<array{_: 'messageReactor', top: bool, my: bool, anonymous: bool, peer_id?: array|int|string, count: int}>}, ttl_period?: int}}|array{_: 'storyReactionPublicRepost', peer_id: array|int|string, story: array{_: 'storyItemDeleted', id: int}|array{_: 'storyItemSkipped', close_friends: bool, live: bool, id: int, date: int, expire_date: int}|array{_: 'storyItem', pinned: bool, public: bool, close_friends: bool, min: bool, noforwards: bool, edited: bool, contacts: bool, selected_contacts: bool, out: bool, id: int, date: int, from_id?: array|int|string, fwd_from?: array{_: 'storyFwdHeader', modified: bool, from?: array|int|string, from_name?: string, story_id?: int}, expire_date: int, caption?: string, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, media: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, media_areas?: list<array{_: 'mediaAreaVenue', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash: int, accuracy_radius?: int}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'inputMediaAreaVenue', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, query_id: int, result_id: string}|array{_: 'mediaAreaGeoPoint', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash: int, accuracy_radius?: int}, address?: array{_: 'geoPointAddress', country_iso2: string, state?: string, city?: string, street?: string}}|array{_: 'mediaAreaSuggestedReaction', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, dark: bool, flipped: bool, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}}|array{_: 'mediaAreaChannelPost', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, channel_id: int, msg_id: int}|array{_: 'inputMediaAreaChannelPost', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, channel: array|int|string, msg_id: int}|array{_: 'mediaAreaUrl', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, url: string}|array{_: 'mediaAreaWeather', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, temperature_c: float, emoji: string, color: int}|array{_: 'mediaAreaStarGift', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, slug: string}>, privacy?: list<array{_: 'privacyValueAllowContacts'}|array{_: 'privacyValueAllowAll'}|array{_: 'privacyValueAllowUsers', users: list<int>}|array{_: 'privacyValueDisallowContacts'}|array{_: 'privacyValueDisallowAll'}|array{_: 'privacyValueDisallowUsers', users: list<int>}|array{_: 'privacyValueAllowChatParticipants', chats: list<int>}|array{_: 'privacyValueDisallowChatParticipants', chats: list<int>}|array{_: 'privacyValueAllowCloseFriends'}|array{_: 'privacyValueAllowPremium'}|array{_: 'privacyValueAllowBots'}|array{_: 'privacyValueDisallowBots'}>, views?: array{_: 'storyViews', has_viewers: bool, views_count: int, forwards_count?: int, reactions?: list<array{_: 'reactionCount', chosen_order?: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: array}|array{_: 'reactionCustomEmoji', document_id: array}|array{_: 'reactionPaid'}, count: int}>, reactions_count?: int, recent_viewers?: list<int>}, sent_reaction?: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}, albums?: list<int>, music?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}>, chats: list<array|int|string>, users: list<array|int|string>, next_offset?: string} @see https://docs.madelineproto.xyz/API_docs/types/stories.StoryReactionsList.html
     * @psalm-impure
     */
    public function getStoryReactionsList(bool|null $forwards_first = null, array|int|string|null $peer = null, int|null $id = 0, array|null $reaction = null, string|null $offset = null, int|null $limit = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Pin some stories to the top of the profile, see [here Â»](https://core.telegram.org/api/stories#pinned-or-archived-stories) for more info.
     *
     * @param array|int|string $peer Peer where to pin stories. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param list<int>|array<never, never> $id IDs of the stories to pin (max [stories\_pinned\_to\_top\_count\_max](https://core.telegram.org/api/config#stories-pinned-to-top-count-max)).
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function togglePinnedToTop(array|int|string|null $peer = null, array $id = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Globally search for [stories](https://core.telegram.org/api/stories) using a hashtag or a [location media area](https://core.telegram.org/api/stories#location-tags), see [here Â»](https://core.telegram.org/api/stories#searching-stories) for more info on the full flow.
     * 
     * Either `hashtag` **or** `area` **must** be set when invoking the method.
     *
     * @param string $hashtag Hashtag (without the `#`)
     * @param array{_: 'mediaAreaVenue', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, geo?: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash?: int, accuracy_radius?: int}, title?: string, address?: string, provider?: string, venue_id?: string, venue_type?: string}|array{_: 'inputMediaAreaVenue', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, query_id?: int, result_id?: string}|array{_: 'mediaAreaGeoPoint', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, geo?: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash?: int, accuracy_radius?: int}, address?: array{_: 'geoPointAddress', country_iso2?: string, state?: string, city?: string, street?: string}}|array{_: 'mediaAreaSuggestedReaction', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, dark?: bool, flipped?: bool, reaction?: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon?: string}|array{_: 'reactionCustomEmoji', document_id?: int}|array{_: 'reactionPaid'}}|array{_: 'mediaAreaChannelPost', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, channel_id?: int, msg_id?: int}|array{_: 'inputMediaAreaChannelPost', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, channel?: array|int|string, msg_id?: int}|array{_: 'mediaAreaUrl', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, url?: string}|array{_: 'mediaAreaWeather', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, temperature_c: float, emoji?: string, color?: int}|array{_: 'mediaAreaStarGift', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, slug?: string} $area A [mediaAreaGeoPoint](https://docs.madelineproto.xyz/API_docs/constructors/mediaAreaGeoPoint.html) or a [mediaAreaVenue](https://docs.madelineproto.xyz/API_docs/constructors/mediaAreaVenue.html).  <br>Note [mediaAreaGeoPoint](https://docs.madelineproto.xyz/API_docs/constructors/mediaAreaGeoPoint.html) areas may be searched only if they have an associated `address`. @see https://docs.madelineproto.xyz/API_docs/types/MediaArea.html
     * @param array|int|string $peer If set, returns only stories posted by this peer. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param string $offset Offset for [pagination](https://core.telegram.org/api/offsets): initially an empty string, then the `next_offset` from the previously returned [stories.foundStories](https://docs.madelineproto.xyz/API_docs/constructors/stories.foundStories.html).
     * @param int $limit Maximum number of results to return, [see pagination](https://core.telegram.org/api/offsets)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'stories.foundStories', count: int, stories: list<array{_: 'foundStory', peer: array|int|string, story: array{_: 'storyItemDeleted', id: int}|array{_: 'storyItemSkipped', close_friends: bool, live: bool, id: int, date: int, expire_date: int}|array{_: 'storyItem', pinned: bool, public: bool, close_friends: bool, min: bool, noforwards: bool, edited: bool, contacts: bool, selected_contacts: bool, out: bool, id: int, date: int, from_id?: array|int|string, fwd_from?: array{_: 'storyFwdHeader', modified: bool, from?: array|int|string, from_name?: string, story_id?: int}, expire_date: int, caption?: string, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, media: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, media_areas?: list<array{_: 'mediaAreaVenue', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash: int, accuracy_radius?: int}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'inputMediaAreaVenue', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, query_id: int, result_id: string}|array{_: 'mediaAreaGeoPoint', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash: int, accuracy_radius?: int}, address?: array{_: 'geoPointAddress', country_iso2: string, state?: string, city?: string, street?: string}}|array{_: 'mediaAreaSuggestedReaction', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, dark: bool, flipped: bool, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}}|array{_: 'mediaAreaChannelPost', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, channel_id: int, msg_id: int}|array{_: 'inputMediaAreaChannelPost', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, channel: array|int|string, msg_id: int}|array{_: 'mediaAreaUrl', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, url: string}|array{_: 'mediaAreaWeather', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, temperature_c: float, emoji: string, color: int}|array{_: 'mediaAreaStarGift', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, slug: string}>, privacy?: list<array{_: 'privacyValueAllowContacts'}|array{_: 'privacyValueAllowAll'}|array{_: 'privacyValueAllowUsers', users: list<int>}|array{_: 'privacyValueDisallowContacts'}|array{_: 'privacyValueDisallowAll'}|array{_: 'privacyValueDisallowUsers', users: list<int>}|array{_: 'privacyValueAllowChatParticipants', chats: list<int>}|array{_: 'privacyValueDisallowChatParticipants', chats: list<int>}|array{_: 'privacyValueAllowCloseFriends'}|array{_: 'privacyValueAllowPremium'}|array{_: 'privacyValueAllowBots'}|array{_: 'privacyValueDisallowBots'}>, views?: array{_: 'storyViews', has_viewers: bool, views_count: int, forwards_count?: int, reactions?: list<array{_: 'reactionCount', chosen_order?: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: array}|array{_: 'reactionCustomEmoji', document_id: array}|array{_: 'reactionPaid'}, count: int}>, reactions_count?: int, recent_viewers?: list<int>}, sent_reaction?: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}, albums?: list<int>, music?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}>, next_offset?: string, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/stories.FoundStories.html
     * @psalm-impure
     */
    public function searchPosts(string|null $hashtag = null, array|null $area = null, array|int|string|null $peer = null, string|null $offset = '', int|null $limit = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Creates a [story album](https://core.telegram.org/api/stories#story-albums).
     *
     * @param array|int|string $peer The owned peer where to create the album. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param string $title Album name.
     * @param list<int>|array<never, never> $stories Stories to add to the album.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'storyAlbum', album_id: int, title: string, icon_photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, icon_video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}} @see https://docs.madelineproto.xyz/API_docs/types/StoryAlbum.html
     * @psalm-impure
     */
    public function createAlbum(array|int|string|null $peer = null, string|null $title = '', array $stories = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Rename a [story albums Â»](https://core.telegram.org/api/stories#story-albums), or add, delete or reorder stories in it.
     *
     * @param array|int|string $peer Peer where the album is posted. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $album_id Album ID.
     * @param string $title New album title.
     * @param list<int> $delete_stories If set, deletes the specified stories from the album.
     * @param list<int> $add_stories If set, adds the specified stories to the album.
     * @param list<int> $order If set, reorders the stories in the album by their IDs.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'storyAlbum', album_id: int, title: string, icon_photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, icon_video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}} @see https://docs.madelineproto.xyz/API_docs/types/StoryAlbum.html
     * @psalm-impure
     */
    public function updateAlbum(array|int|string|null $peer = null, int|null $album_id = 0, string|null $title = null, array|null $delete_stories = null, array|null $add_stories = null, array|null $order = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Reorder [story albums on a profile Â»](https://core.telegram.org/api/stories#story-albums).
     *
     * @param array|int|string $peer Peer where the albums are located. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param list<int>|array<never, never> $order New order of the albums.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function reorderAlbums(array|int|string|null $peer = null, array $order = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Delete a [story album](https://core.telegram.org/api/stories#story-albums).
     *
     * @param array|int|string $peer Owned peer where the album is located. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $album_id ID of the album to delete.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function deleteAlbum(array|int|string|null $peer = null, int|null $album_id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Get [story albums](https://core.telegram.org/api/stories#story-albums) created by a peer.
     *
     * @param array|int|string $peer The peer. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param list<int|string>|array<never, never> $hash Array of The `hash` from a previously returned [stories.albums](https://docs.madelineproto.xyz/API_docs/constructors/stories.albums.html), to avoid returning any results if they haven't changed. @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'stories.albumsNotModified'}|array{_: 'stories.albums', hash: list<int|string>, albums: list<array{_: 'storyAlbum', album_id: int, title: string, icon_photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, icon_video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}>} @see https://docs.madelineproto.xyz/API_docs/types/stories.Albums.html
     * @psalm-impure
     */
    public function getAlbums(array|int|string|null $peer = null, array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get stories in a [story album Â»](https://core.telegram.org/api/stories#story-albums).
     *
     * @param array|int|string $peer Peer where the album is posted. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $album_id ID of the album.
     * @param int $offset Offset for [pagination](https://core.telegram.org/api/offsets).
     * @param int $limit Maximum number of results to return, [see pagination](https://core.telegram.org/api/offsets)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'stories.stories', count: int, stories: list<array{_: 'storyItemDeleted', id: int}|array{_: 'storyItemSkipped', close_friends: bool, live: bool, id: int, date: int, expire_date: int}|array{_: 'storyItem', pinned: bool, public: bool, close_friends: bool, min: bool, noforwards: bool, edited: bool, contacts: bool, selected_contacts: bool, out: bool, id: int, date: int, from_id?: array|int|string, fwd_from?: array{_: 'storyFwdHeader', modified: bool, from?: array|int|string, from_name?: string, story_id?: int}, expire_date: int, caption?: string, entities?: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>, media: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}, media_areas?: list<array{_: 'mediaAreaVenue', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash: int, accuracy_radius?: int}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'inputMediaAreaVenue', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, query_id: int, result_id: string}|array{_: 'mediaAreaGeoPoint', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: float, lat: float, access_hash: int, accuracy_radius?: int}, address?: array{_: 'geoPointAddress', country_iso2: string, state?: string, city?: string, street?: string}}|array{_: 'mediaAreaSuggestedReaction', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, dark: bool, flipped: bool, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}}|array{_: 'mediaAreaChannelPost', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, channel_id: int, msg_id: int}|array{_: 'inputMediaAreaChannelPost', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, channel: array|int|string, msg_id: int}|array{_: 'mediaAreaUrl', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, url: string}|array{_: 'mediaAreaWeather', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, temperature_c: float, emoji: string, color: int}|array{_: 'mediaAreaStarGift', coordinates: array{_: 'mediaAreaCoordinates', x: float, y: float, w: float, h: float, rotation: float, radius?: float}, slug: string}>, privacy?: list<array{_: 'privacyValueAllowContacts'}|array{_: 'privacyValueAllowAll'}|array{_: 'privacyValueAllowUsers', users: list<int>}|array{_: 'privacyValueDisallowContacts'}|array{_: 'privacyValueDisallowAll'}|array{_: 'privacyValueDisallowUsers', users: list<int>}|array{_: 'privacyValueAllowChatParticipants', chats: list<int>}|array{_: 'privacyValueDisallowChatParticipants', chats: list<int>}|array{_: 'privacyValueAllowCloseFriends'}|array{_: 'privacyValueAllowPremium'}|array{_: 'privacyValueAllowBots'}|array{_: 'privacyValueDisallowBots'}>, views?: array{_: 'storyViews', has_viewers: bool, views_count: int, forwards_count?: int, reactions?: list<array{_: 'reactionCount', chosen_order?: int, reaction: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}, count: int}>, reactions_count?: int, recent_viewers?: list<int>}, sent_reaction?: array{_: 'reactionEmpty'}|array{_: 'reactionEmoji', emoticon: string}|array{_: 'reactionCustomEmoji', document_id: int}|array{_: 'reactionPaid'}, albums?: list<int>, music?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}>, pinned_to_top?: list<int>, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/stories.Stories.html
     * @psalm-impure
     */
    public function getAlbumStories(array|int|string|null $peer = null, int|null $album_id = 0, int|null $offset = 0, int|null $limit = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param bool $pinned 
     * @param bool $noforwards 
     * @param bool $rtmp_stream 
     * @param array|int|string $peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param string $caption 
     * @param list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}> $entities Array of  @see https://docs.madelineproto.xyz/API_docs/types/MessageEntity.html
     * @param \danog\MadelineProto\ParseMode $parse_mode Whether to parse HTML or Markdown markup in the message
     * @param list<array{_: 'inputPrivacyValueAllowContacts'}|array{_: 'inputPrivacyValueAllowAll'}|array{_: 'inputPrivacyValueAllowUsers', users?: list<array|int|string>}|array{_: 'inputPrivacyValueDisallowContacts'}|array{_: 'inputPrivacyValueDisallowAll'}|array{_: 'inputPrivacyValueDisallowUsers', users?: list<array|int|string>}|array{_: 'inputPrivacyValueAllowChatParticipants', chats?: list<int>}|array{_: 'inputPrivacyValueDisallowChatParticipants', chats?: list<int>}|array{_: 'inputPrivacyValueAllowCloseFriends'}|array{_: 'inputPrivacyValueAllowPremium'}|array{_: 'inputPrivacyValueAllowBots'}|array{_: 'inputPrivacyValueDisallowBots'}>|array<never, never> $privacy_rules Array of  @see https://docs.madelineproto.xyz/API_docs/types/InputPrivacyRule.html
     * @param bool $messages_enabled 
     * @param int $send_paid_messages_stars 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function startLive(bool|null $pinned = null, bool|null $noforwards = null, bool|null $rtmp_stream = null, array|int|string|null $peer = null, string|null $caption = null, array|null $entities = null, \danog\MadelineProto\ParseMode $parse_mode = \danog\MadelineProto\ParseMode::TEXT, array $privacy_rules = [], bool|null $messages_enabled = null, int|null $send_paid_messages_stars = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;
}
<?php
/**
 * This file is automatic generated by build_docs.php file
 * and is used only for autocomplete in multiple IDE
 * don't modify manually.
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\Namespace;

/** @psalm-mutable */
interface Aicompose
{
    /**
     * 
     *
     * @param bool $display_author 
     * @param int $emoji_id 
     * @param string $title 
     * @param string $prompt 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'aiComposeTone', creator: bool, id: int, access_hash: int, slug: string, title: string, emoji_id?: int, prompt?: string, installs_count?: int, author_id?: int, example_english?: array{_: 'aiComposeToneExample', from: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}, to: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}}|array{_: 'aiComposeToneDefault', tone: string, emoji_id: int, title: string} @see https://docs.madelineproto.xyz/API_docs/types/AiComposeTone.html
     * @psalm-impure
     */
    public function createTone(bool|null $display_author = null, int|null $emoji_id = 0, string|null $title = '', string|null $prompt = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param array{_: 'inputAiComposeToneDefault', tone?: string}|array{_: 'inputAiComposeToneID', id?: int, access_hash?: int}|array{_: 'inputAiComposeToneSlug', slug?: string}|array{_: 'inputAiComposeToneSingleUse', custom_prompt?: string} $tone @see https://docs.madelineproto.xyz/API_docs/types/InputAiComposeTone.html
     * @param bool $display_author 
     * @param int $emoji_id 
     * @param string $title 
     * @param string $prompt 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'aiComposeTone', creator: bool, id: int, access_hash: int, slug: string, title: string, emoji_id?: int, prompt?: string, installs_count?: int, author_id?: int, example_english?: array{_: 'aiComposeToneExample', from: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}, to: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}}|array{_: 'aiComposeToneDefault', tone: string, emoji_id: int, title: string} @see https://docs.madelineproto.xyz/API_docs/types/AiComposeTone.html
     * @psalm-impure
     */
    public function updateTone(array $tone, bool|null $display_author = null, int|null $emoji_id = null, string|null $title = null, string|null $prompt = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param array{_: 'inputAiComposeToneDefault', tone?: string}|array{_: 'inputAiComposeToneID', id?: int, access_hash?: int}|array{_: 'inputAiComposeToneSlug', slug?: string}|array{_: 'inputAiComposeToneSingleUse', custom_prompt?: string} $tone @see https://docs.madelineproto.xyz/API_docs/types/InputAiComposeTone.html
     * @param bool $unsave 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function saveTone(array $tone, bool $unsave, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * 
     *
     * @param array{_: 'inputAiComposeToneDefault', tone?: string}|array{_: 'inputAiComposeToneID', id?: int, access_hash?: int}|array{_: 'inputAiComposeToneSlug', slug?: string}|array{_: 'inputAiComposeToneSingleUse', custom_prompt?: string} $tone @see https://docs.madelineproto.xyz/API_docs/types/InputAiComposeTone.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function deleteTone(array $tone, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * 
     *
     * @param array{_: 'inputAiComposeToneDefault', tone?: string}|array{_: 'inputAiComposeToneID', id?: int, access_hash?: int}|array{_: 'inputAiComposeToneSlug', slug?: string}|array{_: 'inputAiComposeToneSingleUse', custom_prompt?: string} $tone @see https://docs.madelineproto.xyz/API_docs/types/InputAiComposeTone.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'aicompose.tonesNotModified'}|array{_: 'aicompose.tones', hash: list<int|string>, tones: list<array{_: 'aiComposeTone', creator: bool, id: int, access_hash: int, slug: string, title: string, emoji_id?: int, prompt?: string, installs_count?: int, author_id?: int, example_english?: array{_: 'aiComposeToneExample', from: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}, to: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}}|array{_: 'aiComposeToneDefault', tone: string, emoji_id: int, title: string}>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/aicompose.Tones.html
     * @psalm-impure
     */
    public function getTone(array $tone, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param list<int|string>|array<never, never> $hash Array of  @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'aicompose.tonesNotModified'}|array{_: 'aicompose.tones', hash: list<int|string>, tones: list<array{_: 'aiComposeTone', creator: bool, id: int, access_hash: int, slug: string, title: string, emoji_id?: int, prompt?: string, installs_count?: int, author_id?: int, example_english?: array{_: 'aiComposeToneExample', from: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}, to: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}}}|array{_: 'aiComposeToneDefault', tone: string, emoji_id: int, title: string}>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/aicompose.Tones.html
     * @psalm-impure
     */
    public function getTones(array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param array{_: 'inputAiComposeToneDefault', tone?: string}|array{_: 'inputAiComposeToneID', id?: int, access_hash?: int}|array{_: 'inputAiComposeToneSlug', slug?: string}|array{_: 'inputAiComposeToneSingleUse', custom_prompt?: string} $tone @see https://docs.madelineproto.xyz/API_docs/types/InputAiComposeTone.html
     * @param int $num 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'aiComposeToneExample', from: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}, to: array{_: 'textWithEntities', text: string, entities: list<array{_: 'messageEntityUnknown', offset: int, length: int}|array{_: 'messageEntityMention', offset: int, length: int}|array{_: 'messageEntityHashtag', offset: int, length: int}|array{_: 'messageEntityBotCommand', offset: int, length: int}|array{_: 'messageEntityUrl', offset: int, length: int}|array{_: 'messageEntityEmail', offset: int, length: int}|array{_: 'messageEntityBold', offset: int, length: int}|array{_: 'messageEntityItalic', offset: int, length: int}|array{_: 'messageEntityCode', offset: int, length: int}|array{_: 'messageEntityPre', offset: int, length: int, language: string}|array{_: 'messageEntityTextUrl', offset: int, length: int, url: string}|array{_: 'messageEntityMentionName', offset: int, length: int, user_id: int}|array{_: 'inputMessageEntityMentionName', offset: int, length: int, user_id: array|int|string}|array{_: 'messageEntityPhone', offset: int, length: int}|array{_: 'messageEntityCashtag', offset: int, length: int}|array{_: 'messageEntityUnderline', offset: int, length: int}|array{_: 'messageEntityStrike', offset: int, length: int}|array{_: 'messageEntityBankCard', offset: int, length: int}|array{_: 'messageEntitySpoiler', offset: int, length: int}|array{_: 'messageEntityCustomEmoji', offset: int, length: int, document_id: int}|array{_: 'messageEntityBlockquote', collapsed: bool, offset: int, length: int}|array{_: 'messageEntityFormattedDate', relative: bool, short_time: bool, long_time: bool, short_date: bool, long_date: bool, day_of_week: bool, offset: int, length: int, date: int}|array{_: 'messageEntityDiffInsert', offset: int, length: int}|array{_: 'messageEntityDiffReplace', offset: int, length: int, old_text: string}|array{_: 'messageEntityDiffDelete', offset: int, length: int}|array{_: 'messageEntityTonAddress', offset: int, length: int}|array{_: 'messageEntityBlockquote', offset: int, length: int}>}} @see https://docs.madelineproto.xyz/API_docs/types/AiComposeToneExample.html
     * @psalm-impure
     */
    public function getToneExample(array $tone, int|null $num = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;
}
<?php
/**
 * This file is automatic generated by build_docs.php file
 * and is used only for autocomplete in multiple IDE
 * don't modify manually.
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\Namespace;

/** @psalm-mutable */
interface Folders
{
    /**
     * Edit peers in [peer folder](https://core.telegram.org/api/folders#peer-folders)
     *
     * @param list<array{_: 'inputFolderPeer', peer?: array|int|string, folder_id?: int}>|array<never, never> $folder_peers Array of New peer list @see https://docs.madelineproto.xyz/API_docs/types/InputFolderPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function editPeerFolders(array $folder_peers = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;
}
<?php
/**
 * This file is automatic generated by build_docs.php file
 * and is used only for autocomplete in multiple IDE
 * don't modify manually.
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\Namespace;

/** @psalm-mutable */
interface Phone
{
    /**
     * Get phone call configuration to be passed to libtgvoip's shared config
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return mixed Any JSON-encodable data
     * @psalm-impure
     */
    public function getCallConfig(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): mixed;

    /**
     * Optional: notify the server that the user is currently busy in a call: this will automatically refuse all incoming phone calls until the current phone call is ended.
     *
     * @param array{_: 'inputPhoneCall', id?: int, access_hash?: int} $peer The phone call we're currently in @see https://docs.madelineproto.xyz/API_docs/types/InputPhoneCall.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function receivedCall(array $peer, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Rate a call, returns info about the rating message sent to the official VoIP bot.
     *
     * @param array{_: 'inputPhoneCall', id?: int, access_hash?: int} $peer The call to rate @see https://docs.madelineproto.xyz/API_docs/types/InputPhoneCall.html
     * @param bool $user_initiative Whether the user decided on their own initiative to rate the call
     * @param int $rating Rating in `1-5` stars
     * @param string $comment An additional comment
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function setCallRating(array $peer, bool|null $user_initiative = null, int|null $rating = 0, string|null $comment = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Send phone call debug data to server
     *
     * @param array{_: 'inputPhoneCall', id?: int, access_hash?: int} $peer Phone call @see https://docs.madelineproto.xyz/API_docs/types/InputPhoneCall.html
     * @param mixed $debug Any JSON-encodable data
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function saveCallDebug(array $peer, mixed $debug, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Send VoIP signaling data
     *
     * @param array{_: 'inputPhoneCall', id?: int, access_hash?: int} $peer Phone call @see https://docs.madelineproto.xyz/API_docs/types/InputPhoneCall.html
     * @param string $data Signaling payload
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function sendSignalingData(array $peer, string|null $data = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Create a group call or livestream
     *
     * @param bool $rtmp_stream Whether RTMP stream support should be enabled: only the [group/supergroup/channel](https://core.telegram.org/api/channel) owner can use this flag.
     * @param array|int|string $peer Associate the group call or livestream to the provided [group/supergroup/channel](https://core.telegram.org/api/channel) @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param string $title Call title
     * @param int $schedule_date For scheduled group call or livestreams, the absolute date when the group call will start
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function createGroupCall(bool|null $rtmp_stream = null, array|int|string|null $peer = null, string|null $title = null, int|null $schedule_date = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Join a group call
     *
     * @param array{_: 'inputGroupCall', id?: int, access_hash?: int}|array{_: 'inputGroupCallSlug', slug?: string}|array{_: 'inputGroupCallInviteMessage', msg_id?: int} $call The group call @see https://docs.madelineproto.xyz/API_docs/types/InputGroupCall.html
     * @param mixed $params Any JSON-encodable data
     * @param bool $muted If set, the user will be muted by default upon joining.
     * @param bool $video_stopped If set, the user's video will be disabled by default upon joining.
     * @param array|int|string $join_as Join the group call, presenting yourself as the specified user/channel @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param string $invite_hash The invitation hash from the [invite link Â»](https://core.telegram.org/api/links#video-chat-livestream-links), if provided allows speaking in a livestream or muted group chat.
     * @param string $public_key For conference calls, your public key. @see https://docs.madelineproto.xyz/API_docs/types/int256.html
     * @param string $block The [block containing an appropriate e2e.chain.changeSetGroupState event](https://core.telegram.org/api/end-to-end/group-calls).
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function joinGroupCall(array $call, mixed $params, bool|null $muted = null, bool|null $video_stopped = null, array|int|string|null $join_as = null, string|null $invite_hash = null, string|null $public_key = null, string|null $block = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Leave a group call
     *
     * @param array{_: 'inputGroupCall', id?: int, access_hash?: int}|array{_: 'inputGroupCallSlug', slug?: string}|array{_: 'inputGroupCallInviteMessage', msg_id?: int} $call The group call @see https://docs.madelineproto.xyz/API_docs/types/InputGroupCall.html
     * @param int $source Your source ID
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function leaveGroupCall(array $call, int|null $source = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Invite a set of users to a group call.
     *
     * @param array{_: 'inputGroupCall', id?: int, access_hash?: int}|array{_: 'inputGroupCallSlug', slug?: string}|array{_: 'inputGroupCallInviteMessage', msg_id?: int} $call The group call @see https://docs.madelineproto.xyz/API_docs/types/InputGroupCall.html
     * @param list<array|int|string>|array<never, never> $users Array of The users to invite. @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function inviteToGroupCall(array $call, array $users = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Terminate a group call
     *
     * @param array{_: 'inputGroupCall', id?: int, access_hash?: int}|array{_: 'inputGroupCallSlug', slug?: string}|array{_: 'inputGroupCallInviteMessage', msg_id?: int} $call The group call to terminate @see https://docs.madelineproto.xyz/API_docs/types/InputGroupCall.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function discardGroupCall(array $call, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Change group call settings
     *
     * @param array{_: 'inputGroupCall', id?: int, access_hash?: int}|array{_: 'inputGroupCallSlug', slug?: string}|array{_: 'inputGroupCallInviteMessage', msg_id?: int} $call Group call @see https://docs.madelineproto.xyz/API_docs/types/InputGroupCall.html
     * @param bool $reset_invite_hash Invalidate existing invite links
     * @param bool $join_muted Whether all users will that join this group call are muted by default upon joining the group call
     * @param bool $messages_enabled 
     * @param int $send_paid_messages_stars 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function toggleGroupCallSettings(array $call, bool|null $reset_invite_hash = null, bool|null $join_muted = null, bool|null $messages_enabled = null, int|null $send_paid_messages_stars = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get info about a group call
     *
     * @param array{_: 'inputGroupCall', id?: int, access_hash?: int}|array{_: 'inputGroupCallSlug', slug?: string}|array{_: 'inputGroupCallInviteMessage', msg_id?: int} $call The group call @see https://docs.madelineproto.xyz/API_docs/types/InputGroupCall.html
     * @param int $limit Maximum number of results to return, [see pagination](https://core.telegram.org/api/offsets)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'phone.groupCall', call: array{_: 'groupCallDiscarded', id: int, access_hash: int, duration: int}|array{_: 'groupCall', join_muted: bool, can_change_join_muted: bool, join_date_asc: bool, schedule_start_subscribed: bool, can_start_video: bool, record_video_active: bool, rtmp_stream: bool, listeners_hidden: bool, conference: bool, creator: bool, messages_enabled: bool, can_change_messages_enabled: bool, min: bool, id: int, access_hash: int, participants_count: int, title?: string, stream_dc_id?: int, record_start_date?: int, schedule_date?: int, unmuted_video_count?: int, unmuted_video_limit: int, version: int, invite_link?: string, send_paid_messages_stars?: int, default_send_as?: array|int|string}, participants: list<array{_: 'groupCallParticipant', peer: array|int|string, muted: bool, left: bool, can_self_unmute: bool, just_joined: bool, versioned: bool, min: bool, muted_by_you: bool, volume_by_admin: bool, self: bool, video_joined: bool, date: int, active_date?: int, source: int, volume?: int, about?: string, raise_hand_rating?: int, video?: array{_: 'groupCallParticipantVideo', paused: bool, endpoint: string, source_groups: list<array{_: 'groupCallParticipantVideoSourceGroup', semantics: string, sources: list<int>}>, audio_source?: int}, presentation?: array{_: 'groupCallParticipantVideo', paused: bool, endpoint: string, source_groups: list<array{_: 'groupCallParticipantVideoSourceGroup', semantics: string, sources: list<int>}>, audio_source?: int}, paid_stars_total?: int}>, participants_next_offset: string, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/phone.GroupCall.html
     * @psalm-impure
     */
    public function getGroupCall(array $call, int|null $limit = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get group call participants
     *
     * @param array{_: 'inputGroupCall', id?: int, access_hash?: int}|array{_: 'inputGroupCallSlug', slug?: string}|array{_: 'inputGroupCallInviteMessage', msg_id?: int} $call Group call @see https://docs.madelineproto.xyz/API_docs/types/InputGroupCall.html
     * @param list<array|int|string>|array<never, never> $ids Array of If specified, will fetch group participant info about the specified peers @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param list<int>|array<never, never> $sources If specified, will fetch group participant info about the specified WebRTC source IDs
     * @param string $offset Offset for results, taken from the `next_offset` field of [phone.groupParticipants](https://docs.madelineproto.xyz/API_docs/constructors/phone.groupParticipants.html), initially an empty string. <br>Note: if no more results are available, the method call will return an empty `next_offset`; thus, avoid providing the `next_offset` returned in [phone.groupParticipants](https://docs.madelineproto.xyz/API_docs/constructors/phone.groupParticipants.html) if it is empty, to avoid an infinite loop.
     * @param int $limit Maximum number of results to return, [see pagination](https://core.telegram.org/api/offsets)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'phone.groupParticipants', count: int, participants: list<array{_: 'groupCallParticipant', peer: array|int|string, muted: bool, left: bool, can_self_unmute: bool, just_joined: bool, versioned: bool, min: bool, muted_by_you: bool, volume_by_admin: bool, self: bool, video_joined: bool, date: int, active_date?: int, source: int, volume?: int, about?: string, raise_hand_rating?: int, video?: array{_: 'groupCallParticipantVideo', paused: bool, endpoint: string, source_groups: list<array{_: 'groupCallParticipantVideoSourceGroup', semantics: string, sources: list<int>}>, audio_source?: int}, presentation?: array{_: 'groupCallParticipantVideo', paused: bool, endpoint: string, source_groups: list<array{_: 'groupCallParticipantVideoSourceGroup', semantics: string, sources: list<int>}>, audio_source?: int}, paid_stars_total?: int}>, next_offset: string, chats: list<array|int|string>, users: list<array|int|string>, version: int} @see https://docs.madelineproto.xyz/API_docs/types/phone.GroupParticipants.html
     * @psalm-impure
     */
    public function getGroupParticipants(array $call, array $ids = [], array $sources = [], string|null $offset = '', int|null $limit = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Check whether the group call Server Forwarding Unit is currently receiving the streams with the specified WebRTC source IDs.  
     * Returns an intersection of the source IDs specified in `sources`, and the source IDs currently being forwarded by the SFU.
     *
     * @param array{_: 'inputGroupCall', id?: int, access_hash?: int}|array{_: 'inputGroupCallSlug', slug?: string}|array{_: 'inputGroupCallInviteMessage', msg_id?: int} $call Group call @see https://docs.madelineproto.xyz/API_docs/types/InputGroupCall.html
     * @param list<int>|array<never, never> $sources Source IDs
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return list<int> 
     * @psalm-impure
     */
    public function checkGroupCall(array $call, array $sources = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Start or stop recording a group call: the recorded audio and video streams will be automatically sent to `Saved messages` (the chat with ourselves).
     *
     * @param array{_: 'inputGroupCall', id?: int, access_hash?: int}|array{_: 'inputGroupCallSlug', slug?: string}|array{_: 'inputGroupCallInviteMessage', msg_id?: int} $call The group call or livestream @see https://docs.madelineproto.xyz/API_docs/types/InputGroupCall.html
     * @param bool $start Whether to start or stop recording
     * @param bool $video Whether to also record video streams
     * @param string $title Recording title
     * @param bool $video_portrait If video stream recording is enabled, whether to record in portrait or landscape mode
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function toggleGroupCallRecord(array $call, bool|null $start = null, bool|null $video = null, string|null $title = null, bool|null $video_portrait = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Edit information about a given group call participant
     * 
     * Note: [flags](https://core.telegram.org/mtproto/TL-combinators#conditional-fields).N?[Bool](https://docs.madelineproto.xyz/API_docs/types/Bool.html) parameters can have three possible values:
     * 
     * - If the [TL flag](https://core.telegram.org/mtproto/TL-combinators#conditional-fields) is not set, the previous value will not be changed.
     * - If the [TL flag](https://core.telegram.org/mtproto/TL-combinators#conditional-fields) is set and contains a [boolTrue](https://docs.madelineproto.xyz/API_docs/constructors/boolTrue.html), the previous value will be overwritten to `true`.
     * - If the [TL flag](https://core.telegram.org/mtproto/TL-combinators#conditional-fields) is set and contains a [boolFalse](https://docs.madelineproto.xyz/API_docs/constructors/boolFalse.html), the previous value will be overwritten to `false`.
     *
     * @param array{_: 'inputGroupCall', id?: int, access_hash?: int}|array{_: 'inputGroupCallSlug', slug?: string}|array{_: 'inputGroupCallInviteMessage', msg_id?: int} $call The group call @see https://docs.madelineproto.xyz/API_docs/types/InputGroupCall.html
     * @param array|int|string $participant The group call participant (can also be the user itself) @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param bool $muted Whether to mute or unmute the specified participant
     * @param int $volume New volume
     * @param bool $raise_hand Raise or lower hand
     * @param bool $video_stopped Start or stop the video stream
     * @param bool $video_paused Pause or resume the video stream
     * @param bool $presentation_paused Pause or resume the screen sharing stream
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function editGroupCallParticipant(array $call, array|int|string|null $participant = null, bool|null $muted = null, int|null $volume = null, bool|null $raise_hand = null, bool|null $video_stopped = null, bool|null $video_paused = null, bool|null $presentation_paused = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Edit the title of a group call or livestream
     *
     * @param array{_: 'inputGroupCall', id?: int, access_hash?: int}|array{_: 'inputGroupCallSlug', slug?: string}|array{_: 'inputGroupCallInviteMessage', msg_id?: int} $call Group call @see https://docs.madelineproto.xyz/API_docs/types/InputGroupCall.html
     * @param string $title New title
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function editGroupCallTitle(array $call, string|null $title = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get a list of peers that can be used to join a group call, presenting yourself as a specific user/channel.
     *
     * @param array|int|string $peer The dialog whose group call or livestream we're trying to join @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'phone.joinAsPeers', peers: list<array|int|string>, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/phone.JoinAsPeers.html
     * @psalm-impure
     */
    public function getGroupCallJoinAs(array|int|string|null $peer = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get an [invite link](https://core.telegram.org/api/links#video-chat-livestream-links) for a group call or livestream
     *
     * @param array{_: 'inputGroupCall', id?: int, access_hash?: int}|array{_: 'inputGroupCallSlug', slug?: string}|array{_: 'inputGroupCallInviteMessage', msg_id?: int} $call The group call @see https://docs.madelineproto.xyz/API_docs/types/InputGroupCall.html
     * @param bool $can_self_unmute For livestreams or muted group chats, if set, users that join using this link will be able to speak without explicitly requesting permission by (for example by raising their hand).
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'phone.exportedGroupCallInvite', link: string} @see https://docs.madelineproto.xyz/API_docs/types/phone.ExportedGroupCallInvite.html
     * @psalm-impure
     */
    public function exportGroupCallInvite(array $call, bool|null $can_self_unmute = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Subscribe or unsubscribe to a scheduled group call
     *
     * @param array{_: 'inputGroupCall', id?: int, access_hash?: int}|array{_: 'inputGroupCallSlug', slug?: string}|array{_: 'inputGroupCallInviteMessage', msg_id?: int} $call Scheduled group call @see https://docs.madelineproto.xyz/API_docs/types/InputGroupCall.html
     * @param bool $subscribed Enable or disable subscription
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function toggleGroupCallStartSubscription(array $call, bool $subscribed, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Start a scheduled group call.
     *
     * @param array{_: 'inputGroupCall', id?: int, access_hash?: int}|array{_: 'inputGroupCallSlug', slug?: string}|array{_: 'inputGroupCallInviteMessage', msg_id?: int} $call The scheduled group call @see https://docs.madelineproto.xyz/API_docs/types/InputGroupCall.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function startScheduledGroupCall(array $call, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Set the default peer that will be used to join a group call in a specific dialog.
     *
     * @param array|int|string $peer The dialog @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param array|int|string $join_as The default peer that will be used to join group calls in this dialog, presenting yourself as a specific user/channel. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function saveDefaultGroupCallJoinAs(array|int|string|null $peer = null, array|int|string|null $join_as = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Start screen sharing in a call
     *
     * @param array{_: 'inputGroupCall', id?: int, access_hash?: int}|array{_: 'inputGroupCallSlug', slug?: string}|array{_: 'inputGroupCallInviteMessage', msg_id?: int} $call The group call @see https://docs.madelineproto.xyz/API_docs/types/InputGroupCall.html
     * @param mixed $params Any JSON-encodable data
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function joinGroupCallPresentation(array $call, mixed $params, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Stop screen sharing in a group call
     *
     * @param array{_: 'inputGroupCall', id?: int, access_hash?: int}|array{_: 'inputGroupCallSlug', slug?: string}|array{_: 'inputGroupCallInviteMessage', msg_id?: int} $call The group call @see https://docs.madelineproto.xyz/API_docs/types/InputGroupCall.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function leaveGroupCallPresentation(array $call, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get info about RTMP streams in a group call or livestream.  
     * This method should be invoked to the same group/channel-related DC used for [downloading livestream chunks](https://core.telegram.org/api/files#downloading-files).  
     * As usual, the media DC is preferred, if available.
     *
     * @param array{_: 'inputGroupCall', id?: int, access_hash?: int}|array{_: 'inputGroupCallSlug', slug?: string}|array{_: 'inputGroupCallInviteMessage', msg_id?: int} $call Group call or livestream @see https://docs.madelineproto.xyz/API_docs/types/InputGroupCall.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'phone.groupCallStreamChannels', channels: list<array{_: 'groupCallStreamChannel', channel: int, scale: int, last_timestamp_ms: int}>} @see https://docs.madelineproto.xyz/API_docs/types/phone.GroupCallStreamChannels.html
     * @psalm-impure
     */
    public function getGroupCallStreamChannels(array $call, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get RTMP URL and stream key for RTMP livestreams. Can be used even before creating the actual RTMP livestream with [phone.createGroupCall](https://docs.madelineproto.xyz/API_docs/methods/phone.createGroupCall.html) (the `rtmp_stream` flag must be set).
     *
     * @param bool $revoke Whether to revoke the previous stream key or simply return the existing one
     * @param bool $live_story 
     * @param array|int|string $peer Peer to livestream into @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'phone.groupCallStreamRtmpUrl', url: string, key: string} @see https://docs.madelineproto.xyz/API_docs/types/phone.GroupCallStreamRtmpUrl.html
     * @psalm-impure
     */
    public function getGroupCallStreamRtmpUrl(bool $revoke, bool|null $live_story = null, array|int|string|null $peer = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Save phone call debug information
     *
     * @param array{_: 'inputPhoneCall', id?: int, access_hash?: int} $peer Phone call @see https://docs.madelineproto.xyz/API_docs/types/InputPhoneCall.html
     * @param mixed $file A file name or a file URL. You can also use amphp async streams, amphp HTTP response objects, and [much more](https://docs.madelineproto.xyz/docs/FILES.html#downloading-files)!
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function saveCallLog(array $peer, mixed $file, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Create and optionally join a new conference call.
     *
     * @param bool $muted If set, mute our microphone when joining the call (can only be used if `join` is set).
     * @param bool $video_stopped If set, our video stream is disabled (can only be used if `join` is set).
     * @param bool $join If set, also join the call, otherwise just create the call link.
     * @param string $public_key Public key (can only be used if `join` is set). @see https://docs.madelineproto.xyz/API_docs/types/int256.html
     * @param string $block Initial blockchain block (can only be used if `join` is set).
     * @param mixed $params Any JSON-encodable data
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function createConferenceCall(bool|null $muted = null, bool|null $video_stopped = null, bool|null $join = null, string|null $public_key = null, string|null $block = null, mixed $params = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Remove participants from a conference call.
     * 
     * Exactly one of the `only_left` and `kick` flags must be set.
     *
     * @param array{_: 'inputGroupCall', id?: int, access_hash?: int}|array{_: 'inputGroupCallSlug', slug?: string}|array{_: 'inputGroupCallInviteMessage', msg_id?: int} $call The conference call. @see https://docs.madelineproto.xyz/API_docs/types/InputGroupCall.html
     * @param bool $only_left Whether this is a removal of members that already left the conference call.
     * @param bool $kick Whether this is a forced removal of active members in a conference call.
     * @param list<int>|array<never, never> $ids IDs of users to remove.
     * @param string $block The [block containing an appropriate e2e.chain.changeSetGroupState event](https://core.telegram.org/api/end-to-end/group-calls)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function deleteConferenceCallParticipants(array $call, bool|null $only_left = null, bool|null $kick = null, array $ids = [], string|null $block = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Broadcast a blockchain block to all members of a conference call, see [here Â»](https://core.telegram.org/api/end-to-end/group-calls) for more info.
     *
     * @param array{_: 'inputGroupCall', id?: int, access_hash?: int}|array{_: 'inputGroupCallSlug', slug?: string}|array{_: 'inputGroupCallInviteMessage', msg_id?: int} $call The conference where to broadcast the block. @see https://docs.madelineproto.xyz/API_docs/types/InputGroupCall.html
     * @param string $block The block to broadcast.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function sendConferenceCallBroadcast(array $call, string|null $block = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Invite a user to a conference call.
     *
     * @param array{_: 'inputGroupCall', id?: int, access_hash?: int}|array{_: 'inputGroupCallSlug', slug?: string}|array{_: 'inputGroupCallInviteMessage', msg_id?: int} $call The conference call. @see https://docs.madelineproto.xyz/API_docs/types/InputGroupCall.html
     * @param bool $video Invite the user to also turn on their video feed.
     * @param array|int|string $user_id The user to invite. @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function inviteConferenceCallParticipant(array $call, bool|null $video = null, array|int|string|null $user_id = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Declines a conference call invite.
     *
     * @param int $msg_id The ID of the [messageActionConferenceCall](https://docs.madelineproto.xyz/API_docs/constructors/messageActionConferenceCall.html) to decline.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function declineConferenceCallInvite(int|null $msg_id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Fetch the blocks of a [conference blockchain Â»](https://core.telegram.org/api/end-to-end/group-calls).
     *
     * @param array{_: 'inputGroupCall', id?: int, access_hash?: int}|array{_: 'inputGroupCallSlug', slug?: string}|array{_: 'inputGroupCallInviteMessage', msg_id?: int} $call The conference. @see https://docs.madelineproto.xyz/API_docs/types/InputGroupCall.html
     * @param int $sub_chain_id Subchain ID.
     * @param int $offset Offset for pagination.
     * @param int $limit Maximum number of blocks to return in this call, [see pagination](https://core.telegram.org/api/offsets)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function getGroupCallChainBlocks(array $call, int|null $sub_chain_id = 0, int|null $offset = 0, int|null $limit = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param array{_: 'inputGroupCall', id?: int, access_hash?: int}|array{_: 'inputGroupCallSlug', slug?: string}|array{_: 'inputGroupCallInviteMessage', msg_id?: int} $call @see https://docs.madelineproto.xyz/API_docs/types/InputGroupCall.html
     * @param array{_: 'textWithEntities', text?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>} $message @see https://docs.madelineproto.xyz/API_docs/types/TextWithEntities.html
     * @param int $allow_paid_stars 
     * @param array|int|string $send_as @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function sendGroupCallMessage(array $call, array $message, int|null $allow_paid_stars = null, array|int|string|null $send_as = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param array{_: 'inputGroupCall', id?: int, access_hash?: int}|array{_: 'inputGroupCallSlug', slug?: string}|array{_: 'inputGroupCallInviteMessage', msg_id?: int} $call @see https://docs.madelineproto.xyz/API_docs/types/InputGroupCall.html
     * @param string $encrypted_message 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function sendGroupCallEncryptedMessage(array $call, string|null $encrypted_message = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * 
     *
     * @param array{_: 'inputGroupCall', id?: int, access_hash?: int}|array{_: 'inputGroupCallSlug', slug?: string}|array{_: 'inputGroupCallInviteMessage', msg_id?: int} $call @see https://docs.madelineproto.xyz/API_docs/types/InputGroupCall.html
     * @param bool $report_spam 
     * @param list<int>|array<never, never> $messages 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function deleteGroupCallMessages(array $call, bool|null $report_spam = null, array $messages = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param array{_: 'inputGroupCall', id?: int, access_hash?: int}|array{_: 'inputGroupCallSlug', slug?: string}|array{_: 'inputGroupCallInviteMessage', msg_id?: int} $call @see https://docs.madelineproto.xyz/API_docs/types/InputGroupCall.html
     * @param bool $report_spam 
     * @param array|int|string $participant @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function deleteGroupCallParticipantMessages(array $call, bool|null $report_spam = null, array|int|string|null $participant = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param array{_: 'inputGroupCall', id?: int, access_hash?: int}|array{_: 'inputGroupCallSlug', slug?: string}|array{_: 'inputGroupCallInviteMessage', msg_id?: int} $call @see https://docs.madelineproto.xyz/API_docs/types/InputGroupCall.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'phone.groupCallStars', total_stars: int, top_donors: list<array{_: 'groupCallDonor', top: bool, my: bool, peer_id?: array|int|string, stars: int}>, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/phone.GroupCallStars.html
     * @psalm-impure
     */
    public function getGroupCallStars(array $call, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param array{_: 'inputGroupCall', id?: int, access_hash?: int}|array{_: 'inputGroupCallSlug', slug?: string}|array{_: 'inputGroupCallInviteMessage', msg_id?: int} $call @see https://docs.madelineproto.xyz/API_docs/types/InputGroupCall.html
     * @param array|int|string $send_as @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function saveDefaultSendAs(array $call, array|int|string|null $send_as = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;
}
<?php
/**
 * This file is automatic generated by build_docs.php file
 * and is used only for autocomplete in multiple IDE
 * don't modify manually.
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\Namespace;

/** @psalm-mutable */
interface Channels
{
    /**
     * Mark [channel/supergroup](https://core.telegram.org/api/channel) history as read
     *
     * @param array|int|string $channel [Channel/supergroup](https://core.telegram.org/api/channel) @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param int $max_id ID of message up to which messages should be marked as read
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function readHistory(array|int|string|null $channel = null, int|null $max_id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Delete messages in a [channel/supergroup](https://core.telegram.org/api/channel)
     *
     * @param array|int|string $channel [Channel/supergroup](https://core.telegram.org/api/channel) @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param list<int>|array<never, never> $id IDs of messages to delete
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.affectedMessages', pts: int, pts_count: int} @see https://docs.madelineproto.xyz/API_docs/types/messages.AffectedMessages.html
     * @psalm-impure
     */
    public function deleteMessages(array|int|string|null $channel = null, array $id = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Reports some messages from a user in a supergroup as spam; requires administrator rights in the supergroup
     *
     * @param array|int|string $channel Supergroup @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param array|int|string $participant Participant whose messages should be reported @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param list<int>|array<never, never> $id IDs of spam messages
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function reportSpam(array|int|string|null $channel = null, array|int|string|null $participant = null, array $id = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Get [channel/supergroup](https://core.telegram.org/api/channel) messages
     *
     * @param array|int|string $channel Channel/supergroup @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param list<int|array>|array<never, never> $id Array of IDs of messages to get @see https://docs.madelineproto.xyz/API_docs/types/InputMessage.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.messages', messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.messagesSlice', inexact: array, count: array, next_rate?: array, offset_id_offset?: array, search_flood?: array, messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.channelMessages', inexact: array, pts: array, count: array, offset_id_offset?: array, messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.messagesNotModified', count: array} @see https://docs.madelineproto.xyz/API_docs/types/messages.Messages.html
     * @psalm-impure
     */
    public function getMessages(array|int|string|null $channel = null, array $id = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get the participants of a [supergroup/channel](https://core.telegram.org/api/channel)
     *
     * @param array{_: 'channelParticipantsRecent'}|array{_: 'channelParticipantsAdmins'}|array{_: 'channelParticipantsKicked', q?: string}|array{_: 'channelParticipantsBots'}|array{_: 'channelParticipantsBanned', q?: string}|array{_: 'channelParticipantsSearch', q?: string}|array{_: 'channelParticipantsContacts', q?: string}|array{_: 'channelParticipantsMentions', q?: string, top_msg_id?: int} $filter Which participant types to fetch @see https://docs.madelineproto.xyz/API_docs/types/ChannelParticipantsFilter.html
     * @param array|int|string $channel Channel @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param int $offset [Offset](https://core.telegram.org/api/offsets)
     * @param int $limit [Limit](https://core.telegram.org/api/offsets)
     * @param list<int|string>|array<never, never> $hash Array of [Hash](https://core.telegram.org/api/offsets) @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'channels.channelParticipants', count: int, participants: list<array{_: 'channelParticipant', user_id: int, date: int, subscription_until_date?: int, rank?: string}|array{_: 'channelParticipantSelf', via_request: bool, user_id: int, inviter_id: int, date: int, subscription_until_date?: int, rank?: string}|array{_: 'channelParticipantCreator', admin_rights: array{_: 'chatAdminRights', change_info: bool, post_messages: bool, edit_messages: bool, delete_messages: bool, ban_users: bool, invite_users: bool, pin_messages: bool, add_admins: bool, anonymous: bool, manage_call: bool, other: bool, manage_topics: bool, post_stories: bool, edit_stories: bool, delete_stories: bool, manage_direct_messages: bool, manage_ranks: bool, manage_linked_peers: bool, manage_welcome_messages: bool}, user_id: int, rank?: string}|array{_: 'channelParticipantAdmin', admin_rights: array{_: 'chatAdminRights', change_info: bool, post_messages: bool, edit_messages: bool, delete_messages: bool, ban_users: bool, invite_users: bool, pin_messages: bool, add_admins: bool, anonymous: bool, manage_call: bool, other: bool, manage_topics: bool, post_stories: bool, edit_stories: bool, delete_stories: bool, manage_direct_messages: bool, manage_ranks: bool, manage_linked_peers: bool, manage_welcome_messages: bool}, can_edit: bool, self: bool, user_id: int, inviter_id?: int, promoted_by: int, date: int, rank?: string}|array{_: 'channelParticipantBanned', peer: array|int|string, banned_rights: array{_: 'chatBannedRights', view_messages: bool, send_messages: bool, send_media: bool, send_stickers: bool, send_gifs: bool, send_games: bool, send_inline: bool, embed_links: bool, send_polls: bool, change_info: bool, invite_users: bool, pin_messages: bool, manage_topics: bool, send_photos: bool, send_videos: bool, send_roundvideos: bool, send_audios: bool, send_voices: bool, send_docs: bool, send_plain: bool, edit_rank: bool, send_reactions: bool, manage_linked_peers: bool, until_date: int}, left: bool, kicked_by: int, date: int, rank?: string}|array{_: 'channelParticipantLeft', peer: array|int|string}>, chats: list<array|int|string>, users: list<array|int|string>}|array{_: 'channels.channelParticipantsNotModified'} @see https://docs.madelineproto.xyz/API_docs/types/channels.ChannelParticipants.html
     * @psalm-impure
     */
    public function getParticipants(array $filter, array|int|string|null $channel = null, int|null $offset = 0, int|null $limit = 0, array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get info about a [channel/supergroup](https://core.telegram.org/api/channel) participant
     *
     * @param array|int|string $channel Channel/supergroup @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param array|int|string $participant Participant to get info about @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'channels.channelParticipant', participant: array{_: 'channelParticipant', user_id: int, date: int, subscription_until_date?: int, rank?: string}|array{_: 'channelParticipantSelf', via_request: bool, user_id: int, inviter_id: int, date: int, subscription_until_date?: int, rank?: string}|array{_: 'channelParticipantCreator', admin_rights: array{_: 'chatAdminRights', change_info: bool, post_messages: bool, edit_messages: bool, delete_messages: bool, ban_users: bool, invite_users: bool, pin_messages: bool, add_admins: bool, anonymous: bool, manage_call: bool, other: bool, manage_topics: bool, post_stories: bool, edit_stories: bool, delete_stories: bool, manage_direct_messages: bool, manage_ranks: bool, manage_linked_peers: bool, manage_welcome_messages: bool}, user_id: int, rank?: string}|array{_: 'channelParticipantAdmin', admin_rights: array{_: 'chatAdminRights', change_info: bool, post_messages: bool, edit_messages: bool, delete_messages: bool, ban_users: bool, invite_users: bool, pin_messages: bool, add_admins: bool, anonymous: bool, manage_call: bool, other: bool, manage_topics: bool, post_stories: bool, edit_stories: bool, delete_stories: bool, manage_direct_messages: bool, manage_ranks: bool, manage_linked_peers: bool, manage_welcome_messages: bool}, can_edit: bool, self: bool, user_id: int, inviter_id?: int, promoted_by: int, date: int, rank?: string}|array{_: 'channelParticipantBanned', peer: array|int|string, banned_rights: array{_: 'chatBannedRights', view_messages: bool, send_messages: bool, send_media: bool, send_stickers: bool, send_gifs: bool, send_games: bool, send_inline: bool, embed_links: bool, send_polls: bool, change_info: bool, invite_users: bool, pin_messages: bool, manage_topics: bool, send_photos: bool, send_videos: bool, send_roundvideos: bool, send_audios: bool, send_voices: bool, send_docs: bool, send_plain: bool, edit_rank: bool, send_reactions: bool, manage_linked_peers: bool, until_date: int}, left: bool, kicked_by: int, date: int, rank?: string}|array{_: 'channelParticipantLeft', peer: array|int|string}, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/channels.ChannelParticipant.html
     * @psalm-impure
     */
    public function getParticipant(array|int|string|null $channel = null, array|int|string|null $participant = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Create a [supergroup/channel](https://core.telegram.org/api/channel).
     *
     * @param bool $broadcast Whether to create a [channel](https://core.telegram.org/api/channel)
     * @param bool $megagroup Whether to create a [supergroup](https://core.telegram.org/api/channel)
     * @param bool $for_import Whether the supergroup is being created to import messages from a foreign chat service using [messages.initHistoryImport](https://docs.madelineproto.xyz/API_docs/methods/messages.initHistoryImport.html)
     * @param bool $forum Whether to create a [forum](https://core.telegram.org/api/forum)
     * @param string $title Channel title
     * @param string $about Channel description
     * @param array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: float, long: float, accuracy_radius?: int} $geo_point Geogroup location, see [here Â»](https://core.telegram.org/api/nearby) for more info on geogroups. @see https://docs.madelineproto.xyz/API_docs/types/InputGeoPoint.html
     * @param string $address Geogroup address, see [here Â»](https://core.telegram.org/api/nearby) for more info on geogroups.
     * @param int $ttl_period Time-to-live of all messages that will be sent in the supergroup: once message.date+message.ttl\_period === time(), the message will be deleted on the server, and must be deleted locally as well. You can use [messages.setDefaultHistoryTTL](https://docs.madelineproto.xyz/API_docs/methods/messages.setDefaultHistoryTTL.html) to edit this value later.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function createChannel(bool|null $broadcast = null, bool|null $megagroup = null, bool|null $for_import = null, bool|null $forum = null, string|null $title = '', string|null $about = '', array|null $geo_point = null, string|null $address = null, int|null $ttl_period = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Modify the admin rights of a user in a [supergroup/channel](https://core.telegram.org/api/channel).
     *
     * @param array{_: 'chatAdminRights', change_info?: bool, post_messages?: bool, edit_messages?: bool, delete_messages?: bool, ban_users?: bool, invite_users?: bool, pin_messages?: bool, add_admins?: bool, anonymous?: bool, manage_call?: bool, other?: bool, manage_topics?: bool, post_stories?: bool, edit_stories?: bool, delete_stories?: bool, manage_direct_messages?: bool, manage_ranks?: bool, manage_linked_peers?: bool, manage_welcome_messages?: bool} $admin_rights The admin rights @see https://docs.madelineproto.xyz/API_docs/types/ChatAdminRights.html
     * @param array|int|string $channel The [supergroup/channel](https://core.telegram.org/api/channel). @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param array|int|string $user_id The ID of the user whose admin rights should be modified @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param string $rank Indicates the role (rank) of the admin in the group: just an arbitrary string
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function editAdmin(array $admin_rights, array|int|string|null $channel = null, array|int|string|null $user_id = null, string|null $rank = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Edit the name of a [channel/supergroup](https://core.telegram.org/api/channel)
     *
     * @param array|int|string $channel Channel/supergroup @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param string $title New name
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function editTitle(array|int|string|null $channel = null, string|null $title = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Change the photo of a [channel/supergroup](https://core.telegram.org/api/channel)
     *
     * @param array|int|string $channel Channel/supergroup whose photo should be edited @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param array{_: 'inputChatPhotoEmpty'}|array{_: 'inputChatUploadedPhoto', file?: mixed, video?: mixed, video_start_ts?: float, video_emoji_markup?: array{_: 'videoSize', type?: string, w?: int, h?: int, size?: int, video_start_ts?: float}|array{_: 'videoSizeEmojiMarkup', emoji_id?: int, background_colors?: list<int>}|array{_: 'videoSizeStickerMarkup', stickerset?: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id?: int, access_hash?: int}|array{_: 'inputStickerSetShortName', short_name?: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon?: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, sticker_id?: int, background_colors?: list<int>}}|array{_: 'inputChatPhoto', id?: array} $photo New photo @see https://docs.madelineproto.xyz/API_docs/types/InputChatPhoto.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function editPhoto(array|int|string|null $channel = null, array|null $photo = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Check if a username is free and can be assigned to a channel/supergroup
     *
     * @param array|int|string $channel The [channel/supergroup](https://core.telegram.org/api/channel) that will assigned the specified username @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param string $username The username to check
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function checkUsername(array|int|string|null $channel = null, string|null $username = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Change or remove the username of a supergroup/channel
     *
     * @param array|int|string $channel Channel @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param string $username New username, pass an empty string to remove the username
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function updateUsername(array|int|string|null $channel = null, string|null $username = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Join a channel/supergroup
     *
     * @param array|int|string $channel Channel/supergroup to join @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.chatInviteJoinResultOk', updates: array}|array{_: 'messages.chatInviteJoinResultWebView', bot_id: int, query_id: int, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/messages.ChatInviteJoinResult.html
     * @psalm-impure
     */
    public function joinChannel(array|int|string|null $channel = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Leave a [channel/supergroup](https://core.telegram.org/api/channel)
     *
     * @param array|int|string $channel [Channel/supergroup](https://core.telegram.org/api/channel) to leave @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function leaveChannel(array|int|string|null $channel = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Invite users to a channel/supergroup
     *
     * @param array|int|string $channel Channel/supergroup @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param list<array|int|string>|array<never, never> $users Array of Users to invite @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.invitedUsers', updates: array, missing_invitees: list<array{_: 'missingInvitee', premium_would_allow_invite: bool, premium_required_for_pm: bool, user_id: int}>} @see https://docs.madelineproto.xyz/API_docs/types/messages.InvitedUsers.html
     * @psalm-impure
     */
    public function inviteToChannel(array|int|string|null $channel = null, array $users = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Delete a [channel/supergroup](https://core.telegram.org/api/channel)
     *
     * @param array|int|string $channel [Channel/supergroup](https://core.telegram.org/api/channel) to delete @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function deleteChannel(array|int|string|null $channel = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get link and embed info of a message in a [channel/supergroup](https://core.telegram.org/api/channel)
     *
     * @param bool $grouped Whether to include other grouped media (for albums)
     * @param bool $thread Whether to also include a thread ID, if available, inside of the link
     * @param array|int|string $channel Channel @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param int $id Message ID
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'exportedMessageLink', link: string, html: string} @see https://docs.madelineproto.xyz/API_docs/types/ExportedMessageLink.html
     * @psalm-impure
     */
    public function exportMessageLink(bool|null $grouped = null, bool|null $thread = null, array|int|string|null $channel = null, int|null $id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Enable/disable message signatures in channels
     *
     * @param bool $signatures_enabled If set, enables message signatures.
     * @param bool $profiles_enabled If set, messages from channel admins will link to their profiles, just like for group messages: can only be set if the `signatures_enabled` flag is set.
     * @param array|int|string $channel Channel @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function toggleSignatures(bool|null $signatures_enabled = null, bool|null $profiles_enabled = null, array|int|string|null $channel = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get [channels/supergroups/geogroups](https://core.telegram.org/api/channel) we're admin in. Usually called when the user exceeds the [limit](https://docs.madelineproto.xyz/API_docs/constructors/config.html) for owned public [channels/supergroups/geogroups](https://core.telegram.org/api/channel), and the user is given the choice to remove one of his channels/supergroups/geogroups.
     *
     * @param bool $by_location Get geogroups
     * @param bool $check_limit If set and the user has reached the limit of owned public [channels/supergroups/geogroups](https://core.telegram.org/api/channel), instead of returning the channel list one of the specified [errors](#possible-errors) will be returned.<br>Useful to check if a new public channel can indeed be created, even before asking the user to enter a channel username to use in [channels.checkUsername](https://docs.madelineproto.xyz/API_docs/methods/channels.checkUsername.html)/[channels.updateUsername](https://docs.madelineproto.xyz/API_docs/methods/channels.updateUsername.html).
     * @param bool $for_personal Set this flag to only fetch the full list of channels that may be passed to [account.updatePersonalChannel](https://docs.madelineproto.xyz/API_docs/methods/account.updatePersonalChannel.html) to [display them on our profile page](https://core.telegram.org/api/profile#personal-channel).
     * @param bool $for_community_peer 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.chats', chats: list<array|int|string>}|array{_: 'messages.chatsSlice', count: int, chats: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/messages.Chats.html
     * @psalm-impure
     */
    public function getAdminedPublicChannels(bool|null $by_location = null, bool|null $check_limit = null, bool|null $for_personal = null, bool|null $for_community_peer = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Ban/unban/kick a user in a [supergroup/channel](https://core.telegram.org/api/channel).
     *
     * @param array{_: 'chatBannedRights', view_messages?: bool, send_messages?: bool, send_media?: bool, send_stickers?: bool, send_gifs?: bool, send_games?: bool, send_inline?: bool, embed_links?: bool, send_polls?: bool, change_info?: bool, invite_users?: bool, pin_messages?: bool, manage_topics?: bool, send_photos?: bool, send_videos?: bool, send_roundvideos?: bool, send_audios?: bool, send_voices?: bool, send_docs?: bool, send_plain?: bool, edit_rank?: bool, send_reactions?: bool, manage_linked_peers?: bool, until_date?: int} $banned_rights The banned rights @see https://docs.madelineproto.xyz/API_docs/types/ChatBannedRights.html
     * @param array|int|string $channel The [supergroup/channel](https://core.telegram.org/api/channel). @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param array|int|string $participant Participant to ban @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function editBanned(array $banned_rights, array|int|string|null $channel = null, array|int|string|null $participant = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get the admin log of a [channel/supergroup](https://core.telegram.org/api/channel)
     *
     * @param array|int|string $channel Channel @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param string $q Search query, can be empty
     * @param array{_: 'channelAdminLogEventsFilter', join?: bool, leave?: bool, invite?: bool, ban?: bool, unban?: bool, kick?: bool, unkick?: bool, promote?: bool, demote?: bool, info?: bool, settings?: bool, pinned?: bool, edit?: bool, delete?: bool, group_call?: bool, invites?: bool, send?: bool, forums?: bool, sub_extend?: bool, edit_rank?: bool} $events_filter Event filter @see https://docs.madelineproto.xyz/API_docs/types/ChannelAdminLogEventsFilter.html
     * @param list<array|int|string> $admins Array of Only show events from these admins @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param int $max_id Maximum ID of message to return (see [pagination](https://core.telegram.org/api/offsets))
     * @param int $min_id Minimum ID of message to return (see [pagination](https://core.telegram.org/api/offsets))
     * @param int $limit Maximum number of results to return, [see pagination](https://core.telegram.org/api/offsets)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'channels.adminLogResults', events: list<array>, chats: list<array>, users: list<array>} @see https://docs.madelineproto.xyz/API_docs/types/channels.AdminLogResults.html
     * @psalm-impure
     */
    public function getAdminLog(array|int|string|null $channel = null, string|null $q = '', array|null $events_filter = null, array|null $admins = null, int|null $max_id = 0, int|null $min_id = 0, int|null $limit = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Associate a stickerset to the supergroup
     *
     * @param array|int|string $channel Supergroup @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id?: int, access_hash?: int}|array{_: 'inputStickerSetShortName', short_name?: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon?: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'} $stickerset The stickerset to associate @see https://docs.madelineproto.xyz/API_docs/types/InputStickerSet.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function setStickers(array|int|string|null $channel = null, array|null $stickerset = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Mark [channel/supergroup](https://core.telegram.org/api/channel) message contents as read, emitting an [updateChannelReadMessagesContents](https://docs.madelineproto.xyz/API_docs/constructors/updateChannelReadMessagesContents.html).
     *
     * @param array|int|string $channel [Channel/supergroup](https://core.telegram.org/api/channel) @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param list<int>|array<never, never> $id IDs of messages whose contents should be marked as read
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function readMessageContents(array|int|string|null $channel = null, array $id = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Delete the history of a [supergroup](https://core.telegram.org/api/channel)
     *
     * @param bool $for_everyone Whether the history should be deleted for everyone
     * @param array|int|string $channel [Supergroup](https://core.telegram.org/api/channel) whose history must be deleted @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param int $max_id ID of message **up to which** the history must be deleted
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function deleteHistory(bool|null $for_everyone = null, array|int|string|null $channel = null, int|null $max_id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Hide/unhide message history for new channel/supergroup users
     *
     * @param bool $enabled Hide/unhide
     * @param array|int|string $channel Channel/supergroup @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function togglePreHistoryHidden(bool $enabled, array|int|string|null $channel = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get a list of [channels/supergroups](https://core.telegram.org/api/channel) we left, requires a [takeout session, see here Â» for more info](https://core.telegram.org/api/takeout).
     *
     * @param int $takeoutId Takeout ID, generated using account.initTakeoutSession, see [the takeout docs](https://core.telegram.org/api/takeout) for more info.
     * @param int $offset Offset for [pagination](https://core.telegram.org/api/offsets)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.chats', chats: list<array|int|string>}|array{_: 'messages.chatsSlice', count: int, chats: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/messages.Chats.html
     * @psalm-impure
     */
    public function getLeftChannels(int $takeoutId, int|null $offset = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get all groups that can be used as [discussion groups](https://core.telegram.org/api/discussion).
     * 
     * Returned [basic group chats](https://core.telegram.org/api/channel#basic-groups) must be first upgraded to [supergroups](https://core.telegram.org/api/channel#supergroups) before they can be set as a discussion group.  
     * To set a returned supergroup as a discussion group, access to its old messages must be enabled using [channels.togglePreHistoryHidden](https://docs.madelineproto.xyz/API_docs/methods/channels.togglePreHistoryHidden.html), first.
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.chats', chats: list<array|int|string>}|array{_: 'messages.chatsSlice', count: int, chats: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/messages.Chats.html
     * @psalm-impure
     */
    public function getGroupsForDiscussion(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Associate a group to a channel as [discussion group](https://core.telegram.org/api/discussion) for that channel
     *
     * @param array|int|string $broadcast Channel @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param array|int|string $group [Discussion group](https://core.telegram.org/api/discussion) to associate to the channel @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function setDiscussionGroup(array|int|string|null $broadcast = null, array|int|string|null $group = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Edit location of geogroup, see [here Â»](https://core.telegram.org/api/nearby) for more info on geogroups.
     *
     * @param array|int|string $channel [Geogroup](https://core.telegram.org/api/channel) @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: float, long: float, accuracy_radius?: int} $geo_point New geolocation @see https://docs.madelineproto.xyz/API_docs/types/InputGeoPoint.html
     * @param string $address Address string
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function editLocation(array|int|string|null $channel = null, array|null $geo_point = null, string|null $address = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Toggle supergroup slow mode: if enabled, users will only be able to send one message every `seconds` seconds
     *
     * @param array|int|string $channel The [supergroup](https://core.telegram.org/api/channel) @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param int $seconds Users will only be able to send one message every `seconds` seconds, `0` to disable the limitation
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function toggleSlowMode(array|int|string|null $channel = null, int|null $seconds = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get inactive channels and supergroups
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.inactiveChats', dates: list<int>, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/messages.InactiveChats.html
     * @psalm-impure
     */
    public function getInactiveChannels(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Convert a [supergroup](https://core.telegram.org/api/channel) to a [gigagroup](https://core.telegram.org/api/channel), when requested by [channel suggestions](https://core.telegram.org/api/config#channel-suggestions).
     *
     * @param array|int|string $channel The [supergroup](https://core.telegram.org/api/channel) to convert @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function convertToGigagroup(array|int|string|null $channel = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Obtains a list of peers that can be used to send messages in a specific group
     *
     * @param bool $for_paid_reactions If set, fetches the list of peers that can be used to send [paid reactions](https://core.telegram.org/api/reactions#paid-reactions) to messages of a specific peer.
     * @param bool $for_live_stories 
     * @param array|int|string $peer The group where we intend to send messages @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'channels.sendAsPeers', peers: list<array{_: 'sendAsPeer', peer: array|int|string, premium_required: bool}>, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/channels.SendAsPeers.html
     * @psalm-impure
     */
    public function getSendAs(bool|null $for_paid_reactions = null, bool|null $for_live_stories = null, array|int|string|null $peer = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Delete all messages sent by a specific participant of a given supergroup
     *
     * @param array|int|string $channel Supergroup @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param array|int|string $participant The participant whose messages should be deleted @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.affectedHistory', pts: int, pts_count: int, offset: int} @see https://docs.madelineproto.xyz/API_docs/types/messages.AffectedHistory.html
     * @psalm-impure
     */
    public function deleteParticipantHistory(array|int|string|null $channel = null, array|int|string|null $participant = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Set whether all users [should join a discussion group in order to comment on a post Â»](https://core.telegram.org/api/discussion#requiring-users-to-join-the-group)
     *
     * @param bool $enabled Toggle
     * @param array|int|string $channel Discussion group @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function toggleJoinToSend(bool $enabled, array|int|string|null $channel = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Set whether all users should [request admin approval to join the group Â»](https://core.telegram.org/api/invites#join-requests).
     *
     * @param bool $enabled Toggle
     * @param bool $apply_to_invites 
     * @param array|int|string $channel Group @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param array|int|string $guard_bot @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function toggleJoinRequest(bool $enabled, bool|null $apply_to_invites = null, array|int|string|null $channel = null, array|int|string|null $guard_bot = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Reorder active usernames
     *
     * @param array|int|string $channel The supergroup or channel @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param list<string>|array<never, never> $order The new order for active usernames. All active usernames must be specified.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function reorderUsernames(array|int|string|null $channel = null, array $order = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Activate or deactivate a purchased [fragment.com](https://fragment.com) username associated to a [supergroup or channel](https://core.telegram.org/api/channel) we own.
     *
     * @param bool $active Whether to activate or deactivate the username
     * @param array|int|string $channel [Supergroup or channel](https://core.telegram.org/api/channel) @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param string $username Username
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function toggleUsername(bool $active, array|int|string|null $channel = null, string|null $username = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Disable all purchased usernames of a supergroup or channel
     *
     * @param array|int|string $channel Supergroup or channel @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function deactivateAllUsernames(array|int|string|null $channel = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Enable or disable [forum functionality](https://core.telegram.org/api/forum) in a supergroup.
     *
     * @param bool $enabled Enable or disable forum functionality
     * @param bool $tabs If true enables the tabbed forum UI, otherwise enables the list-based forum UI.
     * @param array|int|string $channel Supergroup ID @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function toggleForum(bool $enabled, bool $tabs, array|int|string|null $channel = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Enable or disable the [native antispam system](https://core.telegram.org/api/antispam).
     *
     * @param bool $enabled Enable or disable the native antispam system.
     * @param array|int|string $channel Supergroup ID. The specified supergroup must have at least `telegram_antispam_group_size_min` members to enable antispam functionality, as specified by the [client configuration parameters](https://core.telegram.org/api/config#client-configuration). @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function toggleAntiSpam(bool $enabled, array|int|string|null $channel = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Report a [native antispam](https://core.telegram.org/api/antispam) false positive
     *
     * @param array|int|string $channel Supergroup ID @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param int $msg_id Message ID that was mistakenly deleted by the [native antispam](https://core.telegram.org/api/antispam) system, taken from the [admin log](https://core.telegram.org/api/recent-actions)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function reportAntiSpamFalsePositive(array|int|string|null $channel = null, int|null $msg_id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Hide or display the participants list in a [supergroup](https://core.telegram.org/api/channel).
     * 
     * The supergroup must have at least `hidden_members_group_size_min` participants in order to use this method, as specified by the [client configuration parameters Â»](https://core.telegram.org/api/config#client-configuration).
     *
     * @param bool $enabled If true, will hide the participants list; otherwise will unhide it.
     * @param array|int|string $channel Supergroup ID @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function toggleParticipantsHidden(bool $enabled, array|int|string|null $channel = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Update the [accent color and background custom emoji Â»](https://core.telegram.org/api/colors) of a channel.
     *
     * @param bool $for_profile Whether to change the accent color emoji pattern of the profile page; otherwise, the accent color and emoji pattern of messages will be changed. <br>Channels can change both message and profile palettes; supergroups can only change the profile palette, of course after reaching the [appropriate boost level](https://core.telegram.org/api/colors).
     * @param array|int|string $channel Channel whose accent color should be changed. @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param int $color [ID of the accent color palette Â»](https://core.telegram.org/api/colors) to use (not RGB24, see [here Â»](https://core.telegram.org/api/colors) for more info); if not set, the default palette is used.
     * @param int $background_emoji_id Custom emoji ID used in the accent color pattern.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function updateColor(bool|null $for_profile = null, array|int|string|null $channel = null, int|null $color = null, int|null $background_emoji_id = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Users may also choose to display messages from all topics of a [forum](https://core.telegram.org/api/forum) as if they were sent to a normal group, using a "View as messages" setting in the local client: this setting only affects the current account, and is synced to other logged in sessions using this method.
     * 
     * Invoking this method will update the value of the `view_forum_as_messages` flag of [channelFull](https://docs.madelineproto.xyz/API_docs/constructors/channelFull.html) or [dialog](https://docs.madelineproto.xyz/API_docs/constructors/dialog.html) and emit an [updateChannelViewForumAsMessages](https://docs.madelineproto.xyz/API_docs/constructors/updateChannelViewForumAsMessages.html).
     *
     * @param bool $enabled The new value of the `view_forum_as_messages` flag.
     * @param array|int|string $channel The forum @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function toggleViewForumAsMessages(bool $enabled, array|int|string|null $channel = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Obtain a list of similarly themed public channels, selected based on similarities in their **subscriber bases**.
     *
     * @param array|int|string $channel The method will return channels related to the passed `channel`. If not set, the method will returns channels related to channels the user has joined. @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.chats', chats: list<array|int|string>}|array{_: 'messages.chatsSlice', count: int, chats: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/messages.Chats.html
     * @psalm-impure
     */
    public function getChannelRecommendations(array|int|string|null $channel = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Set an [emoji status](https://core.telegram.org/api/emoji-status) for a channel or supergroup.
     *
     * @param array|int|string $channel The channel/supergroup, must have at least [channel\_emoji\_status\_level\_min](https://core.telegram.org/api/config#channel-emoji-status-level-min)/[group\_emoji\_status\_level\_min](https://core.telegram.org/api/config#group-emoji-status-level-min) boosts. @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param array{_: 'emojiStatusEmpty'}|array{_: 'emojiStatus', document_id?: int, until?: int}|array{_: 'emojiStatusCollectible', collectible_id?: int, document_id?: int, title?: string, slug?: string, pattern_document_id?: int, center_color?: int, edge_color?: int, pattern_color?: int, text_color?: int, until?: int}|array{_: 'inputEmojiStatusCollectible', collectible_id?: int, until?: int} $emoji_status [Emoji status](https://core.telegram.org/api/emoji-status) to set @see https://docs.madelineproto.xyz/API_docs/types/EmojiStatus.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function updateEmojiStatus(array|int|string|null $channel = null, array|null $emoji_status = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Admins with [ban\_users admin rights Â»](https://docs.madelineproto.xyz/API_docs/constructors/chatAdminRights.html) may allow users that apply a certain number of [booosts Â»](https://core.telegram.org/api/boost) to the group to bypass [slow mode Â»](https://docs.madelineproto.xyz/API_docs/methods/channels.toggleSlowMode.html) and [other Â»](https://core.telegram.org/api/rights#default-rights) supergroup restrictions, see [here Â»](https://core.telegram.org/api/boost#bypass-slowmode-and-chat-restrictions) for more info.
     *
     * @param array|int|string $channel The supergroup. @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param int $boosts The number of required boosts (1-8, 0 to disable).
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function setBoostsToUnblockRestrictions(array|int|string|null $channel = null, int|null $boosts = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Set a [custom emoji stickerset](https://core.telegram.org/api/custom-emoji) for supergroups. Only usable after reaching at least the [boost level Â»](https://core.telegram.org/api/boost) specified in the [`group_emoji_stickers_level_min` Â»](https://core.telegram.org/api/config#group-emoji-stickers-level-min) config parameter.
     *
     * @param array|int|string $channel The supergroup @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id?: int, access_hash?: int}|array{_: 'inputStickerSetShortName', short_name?: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon?: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'} $stickerset The custom emoji stickerset to associate to the supergroup @see https://docs.madelineproto.xyz/API_docs/types/InputStickerSet.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function setEmojiStickers(array|int|string|null $channel = null, array|null $stickerset = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Disable ads on the specified channel, for all users.
     * 
     * Available only after reaching at least the [boost level Â»](https://core.telegram.org/api/boost) specified in the [`channel_restrict_sponsored_level_min` Â»](https://core.telegram.org/api/config#channel-restrict-sponsored-level-min) config parameter.
     *
     * @param bool $restricted Whether to disable or re-enable ads.
     * @param array|int|string $channel The channel. @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function restrictSponsoredMessages(bool $restricted, array|int|string|null $channel = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Globally search for posts from public [channels Â»](https://core.telegram.org/api/channel) (*including* those we aren't a member of) containing either a specific hashtag, *or* a full text query.
     * 
     * Exactly one of `query` and `hashtag` must be set.
     *
     * @param string $hashtag The hashtag to search, without the `#` character.
     * @param string $query The full text query: each user has a limited amount of free full text search slots, after which payment is required, see [here Â»](https://core.telegram.org/api/search#posts-tab) for more info on the full flow.
     * @param int $offset_rate Initially 0, then set to the [`next_rate` parameter of messages.messagesSlice](https://docs.madelineproto.xyz/API_docs/constructors/messages.messagesSlice.html), or if that is absent, the `date` of the last returned message.
     * @param array|int|string $offset_peer [Offsets for pagination, for more info click here](https://core.telegram.org/api/offsets) @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $offset_id [Offsets for pagination, for more info click here](https://core.telegram.org/api/offsets)
     * @param int $limit Maximum number of results to return, [see pagination](https://core.telegram.org/api/offsets)
     * @param int $allow_paid_stars For full text post searches (`query`), allows payment of the specified amount of Stars for the search, see [here Â»](https://core.telegram.org/api/search#posts-tab) for more info on the full flow.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.messages', messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.messagesSlice', inexact: array, count: array, next_rate?: array, offset_id_offset?: array, search_flood?: array, messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.channelMessages', inexact: array, pts: array, count: array, offset_id_offset?: array, messages: list<array>, topics: list<array>, chats: list<array>, users: list<array>}|array{_: 'messages.messagesNotModified', count: array} @see https://docs.madelineproto.xyz/API_docs/types/messages.Messages.html
     * @psalm-impure
     */
    public function searchPosts(string|null $hashtag = null, string|null $query = null, int|null $offset_rate = 0, array|int|string|null $offset_peer = null, int|null $offset_id = 0, int|null $limit = 0, int|null $allow_paid_stars = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Enable or disable [paid messages Â»](https://core.telegram.org/api/paid-messages) in this [supergroup](https://core.telegram.org/api/channel) or [monoforum](https://core.telegram.org/api/monoforum).
     * 
     * Also used to [enable or disable monoforums aka direct messages in a channel](https://core.telegram.org/api/monoforum).
     * 
     * Note that passing the ID of the monoforum itself to `channel` will return a `CHANNEL_MONOFORUM_UNSUPPORTED` error: pass the ID of the associated channel to edit the settings of the associated monoforum, instead.
     *
     * @param bool $broadcast_messages_allowed Only usable for channels, enables or disables the associated [monoforum aka direct messages](https://core.telegram.org/api/monoforum).
     * @param array|int|string $channel Pass the supergroup ID for supergroups and the ID of the [channel](https://core.telegram.org/api/channel) to modify the setting in the associated monoforum. @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param int $send_paid_messages_stars Specifies the required amount of [Telegram Stars](https://core.telegram.org/api/stars) users must pay to send messages to the supergroup or monoforum.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function updatePaidMessagesPrice(bool|null $broadcast_messages_allowed = null, array|int|string|null $channel = null, int|null $send_paid_messages_stars = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Toggle autotranslation in a channel, for all users: see [here Â»](https://core.telegram.org/api/translation#autotranslation-for-channels) for more info.
     *
     * @param bool $enabled Whether to enable or disable autotranslation.
     * @param array|int|string $channel The channel where to toggle autotranslation. @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function toggleAutotranslation(bool $enabled, array|int|string|null $channel = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Can only be invoked by non-bot admins of a [monoforum Â»](https://core.telegram.org/api/monoforum), obtains the original sender of a message sent by other monoforum admins to the monoforum, on behalf of the channel associated to the monoforum.
     *
     * @param array|int|string $channel ID of the monoforum. @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param int $id ID of the message sent by a monoforum admin.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array|int|string @see https://docs.madelineproto.xyz/API_docs/types/User.html
     * @psalm-impure
     */
    public function getMessageAuthor(array|int|string|null $channel = null, int|null $id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array|int|string;

    /**
     * Check if the specified [global post search Â»](https://core.telegram.org/api/search#posts-tab) requires payment.
     *
     * @param string $query The query.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'searchPostsFlood', query_is_free: bool, total_daily: int, remains: int, wait_till?: int, stars_amount: int} @see https://docs.madelineproto.xyz/API_docs/types/SearchPostsFlood.html
     * @psalm-impure
     */
    public function checkSearchPostsFlood(string|null $query = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Changes the main profile tab of a channel, see [here Â»](https://core.telegram.org/api/profile#tabs) for more info.
     *
     * @param array{_: 'profileTabPosts'}|array{_: 'profileTabGifts'}|array{_: 'profileTabMedia'}|array{_: 'profileTabFiles'}|array{_: 'profileTabMusic'}|array{_: 'profileTabVoice'}|array{_: 'profileTabLinks'}|array{_: 'profileTabGifs'} $tab The tab to set as main tab. @see https://docs.madelineproto.xyz/API_docs/types/ProfileTab.html
     * @param array|int|string $channel The channel. @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function setMainProfileTab(array $tab, array|int|string|null $channel = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;
}
<?php
/**
 * This file is automatic generated by build_docs.php file
 * and is used only for autocomplete in multiple IDE
 * don't modify manually.
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\Namespace;

/** @psalm-mutable */
interface Users
{
    /**
     * Notify the user that the sent [passport](https://core.telegram.org/passport) data contains some errors The user will not be able to re-submit their Passport data to you until the errors are fixed (the contents of the field for which you returned the error must change).
     * 
     * Use this if the data submitted by the user doesn't satisfy the standards your service requires for any reason. For example, if a birthday date seems invalid, a submitted document is blurry, a scan shows evidence of tampering, etc. Supply some details in the error message to make sure the user knows how to correct the issues.
     *
     * @param array|int|string $id The user @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param list<array{_: 'secureValueErrorData', type: array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}, data_hash?: string, field?: string, text?: string}|array{_: 'secureValueErrorFrontSide', type: array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}, file_hash?: string, text?: string}|array{_: 'secureValueErrorReverseSide', type: array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}, file_hash?: string, text?: string}|array{_: 'secureValueErrorSelfie', type: array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}, file_hash?: string, text?: string}|array{_: 'secureValueErrorFile', type: array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}, file_hash?: string, text?: string}|array{_: 'secureValueErrorFiles', type: array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}, file_hash?: list<string>, text?: string}|array{_: 'secureValueError', type: array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}, hash?: string, text?: string}|array{_: 'secureValueErrorTranslationFile', type: array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}, file_hash?: string, text?: string}|array{_: 'secureValueErrorTranslationFiles', type: array{_: 'secureValueTypePersonalDetails'}|array{_: 'secureValueTypePassport'}|array{_: 'secureValueTypeDriverLicense'}|array{_: 'secureValueTypeIdentityCard'}|array{_: 'secureValueTypeInternalPassport'}|array{_: 'secureValueTypeAddress'}|array{_: 'secureValueTypeUtilityBill'}|array{_: 'secureValueTypeBankStatement'}|array{_: 'secureValueTypeRentalAgreement'}|array{_: 'secureValueTypePassportRegistration'}|array{_: 'secureValueTypeTemporaryRegistration'}|array{_: 'secureValueTypePhone'}|array{_: 'secureValueTypeEmail'}, file_hash?: list<string>, text?: string}>|array<never, never> $errors Array of Errors @see https://docs.madelineproto.xyz/API_docs/types/SecureValueError.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function setSecureValueErrors(array|int|string|null $id = null, array $errors = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Check whether we can write to the specified users, used to implement bulk checks for [Premium-only messages Â»](https://core.telegram.org/api/privacy#require-premium-for-new-non-contact-users) and [paid messages Â»](https://core.telegram.org/api/paid-messages).
     * 
     * For each input user, returns a [RequirementToContact](https://docs.madelineproto.xyz/API_docs/types/RequirementToContact.html) constructor (at the same offset in the vector) containing requirements to contact them.
     *
     * @param list<array|int|string>|array<never, never> $id Array of Users to check. @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return list<array{_: 'requirementToContactEmpty'}|array{_: 'requirementToContactPremium'}|array{_: 'requirementToContactPaidMessages', stars_amount: int}> Array of  @see https://docs.madelineproto.xyz/API_docs/types/RequirementToContact.html
     * @psalm-impure
     */
    public function getRequirementsToContact(array $id = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get songs [pinned to the user's profile, see here Â»](https://core.telegram.org/api/profile#music) for more info.
     *
     * @param array|int|string $id The ID of the user. @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param int $offset Offset for pagination.
     * @param int $limit Maximum number of results to return, [see pagination](https://core.telegram.org/api/offsets)
     * @param list<int|string>|array<never, never> $hash Array of [Hash Â»](https://core.telegram.org/api/offsets#hash-generation) of the IDs of previously added songs, to avoid returning any result if there was no change. @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'users.savedMusicNotModified', count: int}|array{_: 'users.savedMusic', count: int, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>} @see https://docs.madelineproto.xyz/API_docs/types/users.SavedMusic.html
     * @psalm-impure
     */
    public function getSavedMusic(array|int|string|null $id = null, int|null $offset = 0, int|null $limit = 0, array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Check if the passed songs are still pinned to the user's profile, or refresh the file references of songs pinned on a user's profile [see here Â»](https://core.telegram.org/api/profile#music) for more info.
     *
     * @param array|int|string $id The ID of the user. @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param list<array>|array<never, never> $documents Array of The songs (here, `file_reference` can be empty to refresh file references). @see https://docs.madelineproto.xyz/API_docs/types/InputDocument.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'users.savedMusicNotModified', count: int}|array{_: 'users.savedMusic', count: int, documents: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>} @see https://docs.madelineproto.xyz/API_docs/types/users.SavedMusic.html
     * @psalm-impure
     */
    public function getSavedMusicByID(array|int|string|null $id = null, array $documents = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param array{_: 'birthday', day?: int, month?: int, year?: int} $birthday @see https://docs.madelineproto.xyz/API_docs/types/Birthday.html
     * @param array|int|string $id @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function suggestBirthday(array $birthday, array|int|string|null $id = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;
}
<?php
/**
 * This file is automatic generated by build_docs.php file
 * and is used only for autocomplete in multiple IDE
 * don't modify manually.
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\Namespace;

/** @psalm-mutable */
interface Communities
{
    /**
     * 
     *
     * @param bool $hidden 
     * @param string $title 
     * @param string $about 
     * @param array|int|string $peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function create(bool|null $hidden = null, string|null $title = '', string|null $about = null, array|int|string|null $peer = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param bool $visible 
     * @param bool $hidden 
     * @param bool $deleted 
     * @param array|int|string $community @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param array|int|string $peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function togglePeerLink(bool|null $visible = null, bool|null $hidden = null, bool|null $deleted = null, array|int|string|null $community = null, array|int|string|null $peer = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * 
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'messages.chats', chats: list<array|int|string>}|array{_: 'messages.chatsSlice', count: int, chats: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/messages.Chats.html
     * @psalm-impure
     */
    public function getJoinedCommunities(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param bool $collapsed 
     * @param array|int|string $community @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function toggleCommunityCollapsedInDialogs(bool|null $collapsed = null, array|int|string|null $community = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param array|int|string $community @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param string $offset 
     * @param int $limit 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'communities.peerLinkRequests', total_count: int, requests: list<array{_: 'communityPeerRequest', peer: array|int|string, visible: bool, requested_by: int, date: int}>, next_offset?: string, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/communities.PeerLinkRequests.html
     * @psalm-impure
     */
    public function getPeerLinkRequests(array|int|string|null $community = null, string|null $offset = '', int|null $limit = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param bool $reject 
     * @param array|int|string $community @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param array|int|string $peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function togglePeerLinkRequestApproval(bool|null $reject = null, array|int|string|null $community = null, array|int|string|null $peer = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * 
     *
     * @param bool $reject 
     * @param array|int|string $community @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function toggleAllPeerLinkRequestApproval(bool|null $reject = null, array|int|string|null $community = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * 
     *
     * @param bool $unban 
     * @param array|int|string $community @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param array|int|string $participant @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function toggleParticipantBanned(bool|null $unban = null, array|int|string|null $community = null, array|int|string|null $participant = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * 
     *
     * @param array|int|string $community @see https://docs.madelineproto.xyz/API_docs/types/InputChannel.html
     * @param array|int|string $participant @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'communities.participantJoinedChats', creator_chat_ids: list<int>, joined_chat_ids: list<int>, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/communities.ParticipantJoinedChats.html
     * @psalm-impure
     */
    public function getParticipantJoinedChats(array|int|string|null $community = null, array|int|string|null $participant = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;
}
<?php
/**
 * This file is automatic generated by build_docs.php file
 * and is used only for autocomplete in multiple IDE
 * don't modify manually.
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\Namespace;

/** @psalm-mutable */
interface Photos
{
    /**
     * Installs a previously uploaded photo as a profile photo.
     *
     * @param bool $fallback If set, the chosen profile photo will be shown to users that can't display your main profile photo due to your privacy settings.
     * @param array|int|string $bot Can contain info of a bot we own, to change the profile photo of that bot, instead of the current user. @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param array $id Input photo @see https://docs.madelineproto.xyz/API_docs/types/InputPhoto.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'photos.photo', photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/photos.Photo.html
     * @psalm-impure
     */
    public function updateProfilePhoto(bool|null $fallback = null, array|int|string|null $bot = null, array|null $id = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Updates current user profile photo.
     * 
     * The `file`, `video` and `video_emoji_markup` flags are mutually exclusive.
     *
     * @param bool $fallback If set, the chosen profile photo will be shown to users that can't display your main profile photo due to your privacy settings.
     * @param array|int|string $bot Can contain info of a bot we own, to change the profile photo of that bot, instead of the current user. @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param mixed $file A file name or a file URL. You can also use amphp async streams, amphp HTTP response objects, and [much more](https://docs.madelineproto.xyz/docs/FILES.html#downloading-files)!
     * @param mixed $video A file name or a file URL. You can also use amphp async streams, amphp HTTP response objects, and [much more](https://docs.madelineproto.xyz/docs/FILES.html#downloading-files)!
     * @param float $video_start_ts Floating point UNIX timestamp in seconds, indicating the frame of the video/sticker that should be used as static preview; can only be used if `video` or `video_emoji_markup` is set.
     * @param array{_: 'videoSize', type?: string, w?: int, h?: int, size?: int, video_start_ts?: float}|array{_: 'videoSizeEmojiMarkup', emoji_id?: int, background_colors?: list<int>}|array{_: 'videoSizeStickerMarkup', stickerset?: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id?: int, access_hash?: int}|array{_: 'inputStickerSetShortName', short_name?: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon?: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, sticker_id?: int, background_colors?: list<int>} $video_emoji_markup Animated sticker profile picture, must contain either a [videoSizeEmojiMarkup](https://docs.madelineproto.xyz/API_docs/constructors/videoSizeEmojiMarkup.html) or a [videoSizeStickerMarkup](https://docs.madelineproto.xyz/API_docs/constructors/videoSizeStickerMarkup.html) constructor. @see https://docs.madelineproto.xyz/API_docs/types/VideoSize.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'photos.photo', photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/photos.Photo.html
     * @psalm-impure
     */
    public function uploadProfilePhoto(bool|null $fallback = null, array|int|string|null $bot = null, mixed $file = null, mixed $video = null, float|null $video_start_ts = null, array|null $video_emoji_markup = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Deletes profile photos. The method returns a list of successfully deleted photo IDs.
     *
     * @param list<array>|array<never, never> $id Array of Input photos to delete @see https://docs.madelineproto.xyz/API_docs/types/InputPhoto.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return list<int> 
     * @psalm-impure
     */
    public function deletePhotos(array $id = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Returns the list of user photos.
     *
     * @param array|int|string $user_id User ID @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param int $offset Number of list elements to be skipped
     * @param int $max_id If a positive value was transferred, the method will return only photos with IDs less than the set one. This parameter is often useful when [refetching file references Â»](https://core.telegram.org/api/file-references), as in conjuction with `limit=1` and `offset=-1` the [photo](https://docs.madelineproto.xyz/API_docs/constructors/photo.html) object with the `id` specified in `max_id` can be fetched.
     * @param int $limit Number of list elements to be returned
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @param ?int $takeoutId Optional takeout ID, generated using account.initTakeoutSession, see [the takeout docs](https://core.telegram.org/api/takeout) for more info.
     * @return array{_: 'photos.photos', photos: list<array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}>, users: list<array|int|string>}|array{_: 'photos.photosSlice', count: int, photos: list<array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/photos.Photos.html
     * @psalm-impure
     */
    public function getUserPhotos(array|int|string|null $user_id = null, int|null $offset = 0, int|null $max_id = 0, int|null $limit = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null, ?int $takeoutId = null): array;

    /**
     * Upload a custom profile picture for a contact, or suggest a new profile picture to a contact.
     * 
     * The `file`, `video` and `video_emoji_markup` flags are mutually exclusive.
     *
     * @param bool $suggest If set, will send a [messageActionSuggestProfilePhoto](https://docs.madelineproto.xyz/API_docs/constructors/messageActionSuggestProfilePhoto.html) service message to `user_id`, suggesting them to use the specified profile picture; otherwise, will set a personal profile picture for the user (only visible to the current user).
     * @param bool $save If set, removes a previously set personal profile picture (does not affect suggested profile pictures, to remove them simply delete the [messageActionSuggestProfilePhoto](https://docs.madelineproto.xyz/API_docs/constructors/messageActionSuggestProfilePhoto.html) service message with [messages.deleteMessages](https://docs.madelineproto.xyz/API_docs/methods/messages.deleteMessages.html)).
     * @param array|int|string $user_id The contact @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param mixed $file A file name or a file URL. You can also use amphp async streams, amphp HTTP response objects, and [much more](https://docs.madelineproto.xyz/docs/FILES.html#downloading-files)!
     * @param mixed $video A file name or a file URL. You can also use amphp async streams, amphp HTTP response objects, and [much more](https://docs.madelineproto.xyz/docs/FILES.html#downloading-files)!
     * @param float $video_start_ts Floating point UNIX timestamp in seconds, indicating the frame of the video/sticker that should be used as static preview; can only be used if `video` or `video_emoji_markup` is set.
     * @param array{_: 'videoSize', type?: string, w?: int, h?: int, size?: int, video_start_ts?: float}|array{_: 'videoSizeEmojiMarkup', emoji_id?: int, background_colors?: list<int>}|array{_: 'videoSizeStickerMarkup', stickerset?: array{_: 'inputStickerSetEmpty'}|array{_: 'inputStickerSetID', id?: int, access_hash?: int}|array{_: 'inputStickerSetShortName', short_name?: string}|array{_: 'inputStickerSetAnimatedEmoji'}|array{_: 'inputStickerSetDice', emoticon?: string}|array{_: 'inputStickerSetAnimatedEmojiAnimations'}|array{_: 'inputStickerSetPremiumGifts'}|array{_: 'inputStickerSetEmojiGenericAnimations'}|array{_: 'inputStickerSetEmojiDefaultStatuses'}|array{_: 'inputStickerSetEmojiDefaultTopicIcons'}|array{_: 'inputStickerSetEmojiChannelDefaultStatuses'}|array{_: 'inputStickerSetTonGifts'}, sticker_id?: int, background_colors?: list<int>} $video_emoji_markup Animated sticker profile picture, must contain either a [videoSizeEmojiMarkup](https://docs.madelineproto.xyz/API_docs/constructors/videoSizeEmojiMarkup.html) or a [videoSizeStickerMarkup](https://docs.madelineproto.xyz/API_docs/constructors/videoSizeStickerMarkup.html) constructor. @see https://docs.madelineproto.xyz/API_docs/types/VideoSize.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'photos.photo', photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/photos.Photo.html
     * @psalm-impure
     */
    public function uploadContactProfilePhoto(bool|null $suggest = null, bool|null $save = null, array|int|string|null $user_id = null, mixed $file = null, mixed $video = null, float|null $video_start_ts = null, array|null $video_emoji_markup = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

/**
 * This file is automatic generated by build_docs.php file
 * and is used only for autocomplete in multiple IDE
 * don't modify manually.
 */

namespace danog\MadelineProto\Namespace;

/** @psalm-mutable */
interface Updates
{
    /**
     * You cannot use this method directly, see https://docs.madelineproto.xyz for more info on handling updates.
     *
     * @return array
     * @psalm-impure
     */
    public function getState();

    /**
     * You cannot use this method directly, see https://docs.madelineproto.xyz for more info on handling updates.
     *
     * @param int $pts             PTS, see [updates](https://core.telegram.org/api/updates).
     * @param int $date            date, see [updates](https://core.telegram.org/api/updates).
     * @param int $qts             QTS, see [updates](https://core.telegram.org/api/updates).
     * @param int $pts_total_limit For fast updating: if provided and `pts + pts_total_limit < remote pts`, [updates.differenceTooLong](https://docs.madelineproto.xyz/API_docs/constructors/updates.differenceTooLong.html) will be returned.<br>Simply tells the server to not return the difference if it is bigger than `pts_total_limit`<br>If the remote pts is too big (&gt; ~4000000), this field will default to 1000000
     *
     *
     * @return array
     * @psalm-impure
     */
    public function getDifference(int $pts, int $date, int $qts, int $pts_total_limit = 0);

    /**
     * You cannot use this method directly, see https://docs.madelineproto.xyz for more info on handling updates.
     *
     * @param array $channel The channel @see https://docs.madelineproto.xyz/API_docs/types/array.html
     * @param array $filter  Messsage filter @see https://docs.madelineproto.xyz/API_docs/types/array.html
     * @param int   $pts     Persistent timestamp (see [updates](https://core.telegram.org/api/updates))
     * @param int   $limit   How many updates to fetch, max `100000`<br>Ordinary (non-bot) users are supposed to pass `10-100`
     * @param array $force   Set to true to skip some possibly unneeded updates and reduce server-side load @see https://docs.madelineproto.xyz/API_docs/types/array.html
     *
     *
     * @return array
     * @psalm-impure
     */
    public function getChannelDifference(array $channel, array $filter, int $pts, int $limit, array $force = []);
}
<?php
/**
 * This file is automatic generated by build_docs.php file
 * and is used only for autocomplete in multiple IDE
 * don't modify manually.
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\Namespace;

/** @psalm-mutable */
interface Toncenter
{
    /**
     * 
     *
     * @param bool $post 
     * @param string $endpoint 
     * @param string $query 
     * @param string $payload 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'toncenter.apiResponse', response: mixed} @see https://docs.madelineproto.xyz/API_docs/types/toncenter.ApiResponse.html
     * @psalm-impure
     */
    public function performApiRequest(bool|null $post = null, string|null $endpoint = '', string|null $query = null, string|null $payload = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'toncenter.streamingUrl', url: string, expires: int} @see https://docs.madelineproto.xyz/API_docs/types/toncenter.StreamingUrl.html
     * @psalm-impure
     */
    public function getStreamingUrl(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;
}
<?php
/**
 * This file is automatic generated by build_docs.php file
 * and is used only for autocomplete in multiple IDE
 * don't modify manually.
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\Namespace;

/** @psalm-mutable */
interface Bots
{
    /**
     * Sends a custom request; for bots only
     *
     * @param mixed $params Any JSON-encodable data
     * @param string $custom_method The method name
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return mixed Any JSON-encodable data
     * @psalm-impure
     */
    public function sendCustomRequest(mixed $params, string|null $custom_method = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): mixed;

    /**
     * Answers a custom query; for bots only
     *
     * @param mixed $data Any JSON-encodable data
     * @param int $query_id Identifier of a custom query
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function answerWebhookJSONQuery(mixed $data, int|null $query_id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Set bot command list
     *
     * @param array{_: 'botCommandScopeDefault'}|array{_: 'botCommandScopeUsers'}|array{_: 'botCommandScopeChats'}|array{_: 'botCommandScopeChatAdmins'}|array{_: 'botCommandScopePeer', peer?: array|int|string}|array{_: 'botCommandScopePeerAdmins', peer?: array|int|string}|array{_: 'botCommandScopePeerUser', peer?: array|int|string, user_id?: array|int|string} $scope Command scope @see https://docs.madelineproto.xyz/API_docs/types/BotCommandScope.html
     * @param string $lang_code Language code
     * @param list<array{_: 'botCommand', ephemeral?: bool, command?: string, description?: string}>|array<never, never> $commands Array of Bot commands @see https://docs.madelineproto.xyz/API_docs/types/BotCommand.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function setBotCommands(array $scope, string|null $lang_code = '', array $commands = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Clear bot commands for the specified bot scope and language code
     *
     * @param array{_: 'botCommandScopeDefault'}|array{_: 'botCommandScopeUsers'}|array{_: 'botCommandScopeChats'}|array{_: 'botCommandScopeChatAdmins'}|array{_: 'botCommandScopePeer', peer?: array|int|string}|array{_: 'botCommandScopePeerAdmins', peer?: array|int|string}|array{_: 'botCommandScopePeerUser', peer?: array|int|string, user_id?: array|int|string} $scope Command scope @see https://docs.madelineproto.xyz/API_docs/types/BotCommandScope.html
     * @param string $lang_code Language code
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function resetBotCommands(array $scope, string|null $lang_code = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Obtain a list of bot commands for the specified bot scope and language code
     *
     * @param array{_: 'botCommandScopeDefault'}|array{_: 'botCommandScopeUsers'}|array{_: 'botCommandScopeChats'}|array{_: 'botCommandScopeChatAdmins'}|array{_: 'botCommandScopePeer', peer?: array|int|string}|array{_: 'botCommandScopePeerAdmins', peer?: array|int|string}|array{_: 'botCommandScopePeerUser', peer?: array|int|string, user_id?: array|int|string} $scope Command scope @see https://docs.madelineproto.xyz/API_docs/types/BotCommandScope.html
     * @param string $lang_code Language code
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return list<array{_: 'botCommand', ephemeral: bool, command: string, description: string}> Array of  @see https://docs.madelineproto.xyz/API_docs/types/BotCommand.html
     * @psalm-impure
     */
    public function getBotCommands(array $scope, string|null $lang_code = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Sets the [menu button action Â»](https://core.telegram.org/api/bots/menu) for a given user or for all users
     *
     * @param array{_: 'botMenuButtonDefault'}|array{_: 'botMenuButtonCommands'}|array{_: 'botMenuButton', text?: string, url?: string} $button Bot menu button action @see https://docs.madelineproto.xyz/API_docs/types/BotMenuButton.html
     * @param array|int|string $user_id User ID @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function setBotMenuButton(array $button, array|int|string|null $user_id = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Gets the menu button action for a given user or for all users, previously set using [bots.setBotMenuButton](https://docs.madelineproto.xyz/API_docs/methods/bots.setBotMenuButton.html); users can see this information in the [botInfo](https://docs.madelineproto.xyz/API_docs/constructors/botInfo.html) constructor.
     *
     * @param array|int|string $user_id User ID or empty for the default menu button. @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'botMenuButtonDefault'}|array{_: 'botMenuButtonCommands'}|array{_: 'botMenuButton', text: string, url: string} @see https://docs.madelineproto.xyz/API_docs/types/BotMenuButton.html
     * @psalm-impure
     */
    public function getBotMenuButton(array|int|string|null $user_id = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Set the default [suggested admin rights](https://core.telegram.org/api/rights#suggested-bot-rights) for bots being added as admins to channels, see [here for more info on how to handle them Â»](https://core.telegram.org/api/rights#suggested-bot-rights).
     *
     * @param array{_: 'chatAdminRights', change_info?: bool, post_messages?: bool, edit_messages?: bool, delete_messages?: bool, ban_users?: bool, invite_users?: bool, pin_messages?: bool, add_admins?: bool, anonymous?: bool, manage_call?: bool, other?: bool, manage_topics?: bool, post_stories?: bool, edit_stories?: bool, delete_stories?: bool, manage_direct_messages?: bool, manage_ranks?: bool, manage_linked_peers?: bool, manage_welcome_messages?: bool} $admin_rights Admin rights @see https://docs.madelineproto.xyz/API_docs/types/ChatAdminRights.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function setBotBroadcastDefaultAdminRights(array $admin_rights, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Set the default [suggested admin rights](https://core.telegram.org/api/rights#suggested-bot-rights) for bots being added as admins to groups, see [here for more info on how to handle them Â»](https://core.telegram.org/api/rights#suggested-bot-rights).
     *
     * @param array{_: 'chatAdminRights', change_info?: bool, post_messages?: bool, edit_messages?: bool, delete_messages?: bool, ban_users?: bool, invite_users?: bool, pin_messages?: bool, add_admins?: bool, anonymous?: bool, manage_call?: bool, other?: bool, manage_topics?: bool, post_stories?: bool, edit_stories?: bool, delete_stories?: bool, manage_direct_messages?: bool, manage_ranks?: bool, manage_linked_peers?: bool, manage_welcome_messages?: bool} $admin_rights Admin rights @see https://docs.madelineproto.xyz/API_docs/types/ChatAdminRights.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function setBotGroupDefaultAdminRights(array $admin_rights, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Set localized name, about text and description of a bot (or of the current account, if called by a bot).
     *
     * @param array|int|string $bot If called by a user, **must** contain the peer of a bot we own. @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param string $lang_code Language code, if left empty update the fallback about text and description
     * @param string $name New bot name
     * @param string $about New about text
     * @param string $description New description
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function setBotInfo(array|int|string|null $bot = null, string|null $lang_code = '', string|null $name = null, string|null $about = null, string|null $description = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Get localized name, about text and description of a bot (or of the current account, if called by a bot).
     *
     * @param array|int|string $bot If called by a user, **must** contain the peer of a bot we own. @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param string $lang_code Language code, if left empty this method will return the fallback about text and description.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'bots.botInfo', name: string, about: string, description: string} @see https://docs.madelineproto.xyz/API_docs/types/bots.BotInfo.html
     * @psalm-impure
     */
    public function getBotInfo(array|int|string|null $bot = null, string|null $lang_code = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Reorder usernames associated to a bot we own.
     *
     * @param array|int|string $bot The bot @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param list<string>|array<never, never> $order The new order for active usernames. All active usernames must be specified.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function reorderUsernames(array|int|string|null $bot = null, array $order = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Activate or deactivate a purchased [fragment.com](https://fragment.com) username associated to a bot we own.
     *
     * @param bool $active Whether to activate or deactivate it
     * @param array|int|string $bot The bot @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param string $username Username
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function toggleUsername(bool $active, array|int|string|null $bot = null, string|null $username = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Check whether the specified bot can send us messages
     *
     * @param array|int|string $bot The bot @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function canSendMessage(array|int|string|null $bot = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Allow the specified bot to send us messages
     *
     * @param array|int|string $bot The bot @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function allowSendMessage(array|int|string|null $bot = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Send a custom request from a [mini bot app](https://core.telegram.org/api/bots/webapps), triggered by a [web\_app\_invoke\_custom\_method event Â»](https://core.telegram.org/api/web-events#web-app-invoke-custom-method).
     * 
     * The response should be sent using a [custom\_method\_invoked](https://core.telegram.org/api/bots/webapps#custom-method-invoked) event, [see here Â»](https://core.telegram.org/api/web-events#web-app-invoke-custom-method) for more info on the flow.
     *
     * @param mixed $params Any JSON-encodable data
     * @param array|int|string $bot Identifier of the bot associated to the [mini bot app](https://core.telegram.org/api/bots/webapps) @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param string $custom_method Identifier of the custom method to invoke
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return mixed Any JSON-encodable data
     * @psalm-impure
     */
    public function invokeWebViewCustomMethod(mixed $params, array|int|string|null $bot = null, string|null $custom_method = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): mixed;

    /**
     * Fetch popular [Main Mini Apps](https://core.telegram.org/api/bots/webapps#main-mini-apps), to be used in the [apps tab of global search Â»](https://core.telegram.org/api/search#apps-tab).
     *
     * @param string $offset Offset for [pagination](https://core.telegram.org/api/offsets), initially an empty string, then re-use the `next_offset` returned by the previous query.
     * @param int $limit Maximum number of results to return, [see pagination](https://core.telegram.org/api/offsets)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'bots.popularAppBots', next_offset?: string, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/bots.PopularAppBots.html
     * @psalm-impure
     */
    public function getPopularAppBots(string|null $offset = '', int|null $limit = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Add a [main mini app preview, see here Â»](https://core.telegram.org/api/bots/webapps#main-mini-app-previews) for more info.
     * 
     * Only owners of bots with a configured Main Mini App can use this method, see [see here Â»](https://core.telegram.org/api/bots/webapps#main-mini-app-previews) for more info on how to check if you can invoke this method.
     *
     * @param array|int|string $bot The bot that owns the Main Mini App. @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param string $lang_code ISO 639-1 language code, indicating the localization of the preview to add.
     * @param \danog\MadelineProto\EventHandler\Media|string|array $media The photo/video preview, uploaded using [messages.uploadMedia](https://docs.madelineproto.xyz/API_docs/methods/messages.uploadMedia.html). @see https://docs.madelineproto.xyz/API_docs/types/InputMedia.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'botPreviewMedia', date: int, media: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}} @see https://docs.madelineproto.xyz/API_docs/types/BotPreviewMedia.html
     * @psalm-impure
     */
    public function addPreviewMedia(array|int|string|null $bot = null, string|null $lang_code = '', \danog\MadelineProto\EventHandler\Media|array|string|null $media = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Edit a [main mini app preview, see here Â»](https://core.telegram.org/api/bots/webapps#main-mini-app-previews) for more info.
     * 
     * Only owners of bots with a configured Main Mini App can use this method, see [see here Â»](https://core.telegram.org/api/bots/webapps#main-mini-app-previews) for more info on how to check if you can invoke this method.
     *
     * @param array|int|string $bot The bot that owns the Main Mini App. @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param string $lang_code ISO 639-1 language code, indicating the localization of the preview to edit.
     * @param \danog\MadelineProto\EventHandler\Media|string|array $media The photo/video preview to replace, previously fetched as specified [here Â»](https://core.telegram.org/api/bots/webapps#main-mini-app-previews). @see https://docs.madelineproto.xyz/API_docs/types/InputMedia.html
     * @param \danog\MadelineProto\EventHandler\Media|string|array $new_media The new photo/video preview, uploaded using [messages.uploadMedia](https://docs.madelineproto.xyz/API_docs/methods/messages.uploadMedia.html). @see https://docs.madelineproto.xyz/API_docs/types/InputMedia.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'botPreviewMedia', date: int, media: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}} @see https://docs.madelineproto.xyz/API_docs/types/BotPreviewMedia.html
     * @psalm-impure
     */
    public function editPreviewMedia(array|int|string|null $bot = null, string|null $lang_code = '', \danog\MadelineProto\EventHandler\Media|array|string|null $media = null, \danog\MadelineProto\EventHandler\Media|array|string|null $new_media = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Delete a [main mini app preview, see here Â»](https://core.telegram.org/api/bots/webapps#main-mini-app-previews) for more info.
     * 
     * Only owners of bots with a configured Main Mini App can use this method, see [see here Â»](https://core.telegram.org/api/bots/webapps#main-mini-app-previews) for more info on how to check if you can invoke this method.
     *
     * @param array|int|string $bot The bot that owns the Main Mini App. @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param string $lang_code ISO 639-1 language code, indicating the localization of the preview to delete.
     * @param list<\danog\MadelineProto\EventHandler\Media|string|array>|array<never, never> $media Array of The photo/video preview to delete, previously fetched as specified [here Â»](https://core.telegram.org/api/bots/webapps#main-mini-app-previews). @see https://docs.madelineproto.xyz/API_docs/types/InputMedia.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function deletePreviewMedia(array|int|string|null $bot = null, string|null $lang_code = '', array $media = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Reorder a [main mini app previews, see here Â»](https://core.telegram.org/api/bots/webapps#main-mini-app-previews) for more info.
     * 
     * Only owners of bots with a configured Main Mini App can use this method, see [see here Â»](https://core.telegram.org/api/bots/webapps#main-mini-app-previews) for more info on how to check if you can invoke this method.
     *
     * @param array|int|string $bot The bot that owns the Main Mini App. @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param string $lang_code ISO 639-1 language code, indicating the localization of the previews to reorder.
     * @param list<\danog\MadelineProto\EventHandler\Media|string|array>|array<never, never> $order Array of New order of the previews. @see https://docs.madelineproto.xyz/API_docs/types/InputMedia.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function reorderPreviewMedias(array|int|string|null $bot = null, string|null $lang_code = '', array $order = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Bot owners only, fetch [main mini app preview information, see here Â»](https://core.telegram.org/api/bots/webapps#main-mini-app-previews) for more info.
     * 
     * Note: technically non-owners may also invoke this method, but it will always behave exactly as [bots.getPreviewMedias](https://docs.madelineproto.xyz/API_docs/methods/bots.getPreviewMedias.html), returning only previews for the current language and an empty `lang_codes` array, regardless of the passed `lang_code`, so please only use [bots.getPreviewMedias](https://docs.madelineproto.xyz/API_docs/methods/bots.getPreviewMedias.html) if you're not the owner of the `bot`.
     *
     * @param array|int|string $bot The bot that owns the Main Mini App. @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param string $lang_code Fetch previews for the specified ISO 639-1 language code.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'bots.previewInfo', media: list<array{_: 'botPreviewMedia', date: int, media: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}}>, lang_codes: list<string>} @see https://docs.madelineproto.xyz/API_docs/types/bots.PreviewInfo.html
     * @psalm-impure
     */
    public function getPreviewInfo(array|int|string|null $bot = null, string|null $lang_code = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Fetch [main mini app previews, see here Â»](https://core.telegram.org/api/bots/webapps#main-mini-app-previews) for more info.
     *
     * @param array|int|string $bot The bot that owns the Main Mini App. @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return list<array{_: 'botPreviewMedia', date: int, media: array{_: 'messageMediaEmpty'}|array{_: 'messageMediaPhoto', spoiler: bool, live_photo: bool, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, ttl_seconds?: int, video?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}|array{_: 'messageMediaGeo', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}}|array{_: 'messageMediaContact', phone_number: string, first_name: string, last_name: string, vcard: string, user_id: int}|array{_: 'messageMediaUnsupported'}|array{_: 'messageMediaDocument', nopremium: bool, spoiler: bool, video: bool, round: bool, voice: bool, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, alt_documents?: list<array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}>, video_cover?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, video_timestamp?: int, ttl_seconds?: int}|array{_: 'messageMediaWebPage', force_large_media: bool, force_small_media: bool, manual: bool, safe: bool, webpage: array{_: 'webPageEmpty', id: array, url?: array}|array{_: 'webPagePending', id: array, url?: array, date: array}|array{_: 'webPage', has_large_media: array, video_cover_photo: array, id: array, url: array, display_url: array, hash: array, type?: array, site_name?: array, title?: array, description?: array, photo?: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, embed_url?: array, embed_type?: array, embed_width?: array, embed_height?: array, duration?: array, author?: array, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}, cached_page?: array, attributes?: list<array>}|array{_: 'webPageNotModified', cached_page_views?: array}}|array{_: 'messageMediaVenue', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, title: string, address: string, provider: string, venue_id: string, venue_type: string}|array{_: 'messageMediaGame', game: array{_: 'game', id: array, access_hash: array, short_name: array, title: array, description: array, photo: array{_: 'photoEmpty', id: array}|array{_: 'photo', has_stickers: array, id: array, access_hash: array, file_reference: array, date: array, sizes: list<array>, video_sizes?: list<array>, dc_id: array}, document?: array{_: 'documentEmpty', id: array}|array{_: 'document', id: array, access_hash: array, file_reference: array, date: array, mime_type: array, size: array, thumbs?: list<array>, video_thumbs?: list<array>, dc_id: array, attributes: list<array>}}}|array{_: 'messageMediaInvoice', shipping_address_requested: bool, test: bool, title: string, description: string, photo?: array{_: 'webDocument', url: array, access_hash: array, size: array, mime_type: array, attributes: list<array>}|array{_: 'webDocumentNoProxy', url: array, size: array, mime_type: array, attributes: list<array>}, receipt_msg_id?: int, currency: string, total_amount: int, start_param: string, extended_media?: array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}}|array{_: 'messageMediaGeoLive', geo: array{_: 'geoPointEmpty'}|array{_: 'geoPoint', long: array, lat: array, access_hash: array, accuracy_radius?: array}, heading?: int, period: int, proximity_notification_radius?: int}|array{_: 'messageMediaDice', value: int, emoticon: string, game_outcome?: array{_: 'messages.emojiGameOutcome', seed: array, stake_ton_amount: array, ton_amount: array}}|array{_: 'messageMediaStory', peer: array|int|string, via_mention: bool, id: int, story?: array{_: 'storyItemDeleted', id: array}|array{_: 'storyItemSkipped', close_friends: array, live: array, id: array, date: array, expire_date: array}}|array{_: 'messageMediaGiveaway', only_new_subscribers: bool, winners_are_visible: bool, channels: list<int>, countries_iso2?: list<string>, prize_description?: string, quantity: int, months?: int, stars?: int, until_date: int}|array{_: 'messageMediaGiveawayResults', only_new_subscribers: bool, refunded: bool, channel_id: int, additional_peers_count?: int, launch_msg_id: int, winners_count: int, unclaimed_count: int, winners: list<int>, months?: int, stars?: int, prize_description?: string, until_date: int}|array{_: 'messageMediaPaidMedia', stars_amount: int, extended_media: list<array{_: 'messageExtendedMediaPreview', w?: array, h?: array, thumb?: array, video_duration?: array}>}|array{_: 'messageMediaToDo', todo: array{_: 'todoList', title: array, others_can_append: array, others_can_complete: array, list: list<array>}, completions?: list<array{_: 'todoCompletion', completed_by: array, id: array, date: array}>}|array{_: 'messageMediaVideoStream', call: array{_: 'inputGroupCall', id: array, access_hash: array}|array{_: 'inputGroupCallSlug', slug: array}|array{_: 'inputGroupCallInviteMessage', msg_id: array}, rtmp_stream: bool}}> Array of  @see https://docs.madelineproto.xyz/API_docs/types/BotPreviewMedia.html
     * @psalm-impure
     */
    public function getPreviewMedias(array|int|string|null $bot = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Change the emoji status of a user (invoked by bots, see [here Â»](https://core.telegram.org/api/emoji-status#setting-an-emoji-status-from-a-bot) for more info on the full flow)
     *
     * @param array|int|string $user_id The user whose emoji status should be changed @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param array{_: 'emojiStatusEmpty'}|array{_: 'emojiStatus', document_id?: int, until?: int}|array{_: 'emojiStatusCollectible', collectible_id?: int, document_id?: int, title?: string, slug?: string, pattern_document_id?: int, center_color?: int, edge_color?: int, pattern_color?: int, text_color?: int, until?: int}|array{_: 'inputEmojiStatusCollectible', collectible_id?: int, until?: int} $emoji_status The emoji status @see https://docs.madelineproto.xyz/API_docs/types/EmojiStatus.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function updateUserEmojiStatus(array|int|string|null $user_id = null, array|null $emoji_status = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Allow or prevent a bot from [changing our emoji status Â»](https://core.telegram.org/api/emoji-status#setting-an-emoji-status-from-a-bot)
     *
     * @param bool $enabled Whether to allow or prevent the bot from changing our emoji status
     * @param array|int|string $bot The bot @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function toggleUserEmojiStatusPermission(bool $enabled, array|int|string|null $bot = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Check if a [mini app](https://core.telegram.org/api/bots/webapps) can request the download of a specific file: called when handling [web\_app\_request\_file\_download events Â»](https://core.telegram.org/api/web-events#web-app-request-file-download)
     *
     * @param array|int|string $bot The bot that owns the [mini app](https://core.telegram.org/api/bots/webapps) that requested the download @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param string $file_name The `filename` from the [web\_app\_request\_file\_download event Â»](https://core.telegram.org/api/web-events#web-app-request-file-download)
     * @param string $url The `url` from the [web\_app\_request\_file\_download event Â»](https://core.telegram.org/api/web-events#web-app-request-file-download)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function checkDownloadFileParams(array|int|string|null $bot = null, string|null $file_name = '', string|null $url = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Get a list of bots owned by the current user
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return list<array|int|string> Array of  @see https://docs.madelineproto.xyz/API_docs/types/User.html
     * @psalm-impure
     */
    public function getAdminedBots(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Create, edit or delete the [affiliate program](https://core.telegram.org/api/bots/referrals) of a bot we own
     *
     * @param array|int|string $bot The bot @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param int $commission_permille The permille commission rate: it indicates the share of Telegram Stars received by affiliates for every transaction made by users they referred inside of the bot.  <br>  The minimum and maximum values for this parameter are contained in the [starref\_min\_commission\_permille](https://core.telegram.org/api/config#starref-min-commission-permille) and [starref\_max\_commission\_permille](https://core.telegram.org/api/config#starref-max-commission-permille) client configuration parameters. <br>  Can be `0` to terminate the affiliate program.<br>  Both the duration and the commission may only be raised after creation of the program: to lower them, the program must first be terminated and a new one created.
     * @param int $duration_months Indicates the duration of the affiliate program; if not set, there is no expiration date.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'starRefProgram', bot_id: int, commission_permille: int, duration_months?: int, end_date?: int, daily_revenue_per_user?: array{_: 'starsAmount', amount: int, nanos: int}|array{_: 'starsTonAmount', amount: int}} @see https://docs.madelineproto.xyz/API_docs/types/StarRefProgram.html
     * @psalm-impure
     */
    public function updateStarRefProgram(array|int|string|null $bot = null, int|null $commission_permille = 0, int|null $duration_months = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Verify a user or chat [on behalf of an organization Â»](https://core.telegram.org/api/bots/verification).
     *
     * @param bool $enabled If set, adds the verification; otherwise removes verification.
     * @param array|int|string $bot Must **not** be set if invoked by a bot, **must** be set to the ID of an owned bot if invoked by a user. @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param array|int|string $peer The peer to verify @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param array{_: 'textWithEntities', text?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>} $custom_description @see https://docs.madelineproto.xyz/API_docs/types/TextWithEntities.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function setCustomVerification(bool|null $enabled = null, array|int|string|null $bot = null, array|int|string|null $peer = null, array|null $custom_description = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Obtain a list of similarly themed bots, selected based on similarities in their subscriber bases, see [here Â»](https://core.telegram.org/api/recommend) for more info.
     *
     * @param array|int|string $bot The method will return bots related to the passed bot. @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'users.users', users: list<array|int|string>}|array{_: 'users.usersSlice', count: int, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/users.Users.html
     * @psalm-impure
     */
    public function getBotRecommendations(array|int|string|null $bot = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param bool $additional 
     * @param string $username 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function checkUsername(bool|null $additional = null, string|null $username = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * 
     *
     * @param bool $via_deeplink 
     * @param string $name 
     * @param string $username 
     * @param array|int|string $manager_id @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array|int|string @see https://docs.madelineproto.xyz/API_docs/types/User.html
     * @psalm-impure
     */
    public function createBot(bool|null $via_deeplink = null, string|null $name = '', string|null $username = '', array|int|string|null $manager_id = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array|int|string;

    /**
     * 
     *
     * @param bool $revoke 
     * @param array|int|string $bot @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'bots.exportedBotToken', token: string} @see https://docs.madelineproto.xyz/API_docs/types/bots.ExportedBotToken.html
     * @psalm-impure
     */
    public function exportBotToken(bool $revoke, array|int|string|null $bot = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param array $button @see https://docs.madelineproto.xyz/API_docs/types/KeyboardButton.html
     * @param array|int|string $user_id @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'bots.requestedButton', webapp_req_id: string} @see https://docs.madelineproto.xyz/API_docs/types/bots.RequestedButton.html
     * @psalm-impure
     */
    public function requestWebViewButton(array $button, array|int|string|null $user_id = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param array|int|string $bot @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param string $webapp_req_id 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/KeyboardButton.html
     * @psalm-impure
     */
    public function getRequestedWebViewButton(array|int|string|null $bot = null, string|null $webapp_req_id = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param array|int|string $bot @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'bots.accessSettings', restricted: bool, add_users?: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/bots.AccessSettings.html
     * @psalm-impure
     */
    public function getAccessSettings(array|int|string|null $bot = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param bool $restricted 
     * @param array|int|string $bot @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param list<array|int|string> $add_users Array of  @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function editAccessSettings(bool|null $restricted = null, array|int|string|null $bot = null, array|null $add_users = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * 
     *
     * @param array{_: 'joinChatBotResultApproved'}|array{_: 'joinChatBotResultDeclined'}|array{_: 'joinChatBotResultQueued'}|array{_: 'joinChatBotResultWebView', url?: string} $result @see https://docs.madelineproto.xyz/API_docs/types/JoinChatBotResult.html
     * @param int $query_id 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function setJoinChatResults(array $result, int|null $query_id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * 
     *
     * @param array|int|string $bot @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param string $username 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function addUsername(array|int|string|null $bot = null, string|null $username = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * 
     *
     * @param array|int|string $bot @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param string $username 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function removeUsername(array|int|string|null $bot = null, string|null $username = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;
}
<?php
/**
 * This file is automatic generated by build_docs.php file
 * and is used only for autocomplete in multiple IDE
 * don't modify manually.
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\Namespace;

/** @psalm-mutable */
interface Fragment
{
    /**
     * Fetch information about a [fragment collectible, see here Â»](https://core.telegram.org/api/fragment#fetching-info-about-fragment-collectibles) for more info on the full flow.
     *
     * @param array{_: 'inputCollectibleUsername', username?: string}|array{_: 'inputCollectiblePhone', phone?: string} $collectible Collectible to fetch info about. @see https://docs.madelineproto.xyz/API_docs/types/InputCollectible.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'fragment.collectibleInfo', purchase_date: int, currency: string, amount: int, crypto_currency: string, crypto_amount: int, url: string} @see https://docs.madelineproto.xyz/API_docs/types/fragment.CollectibleInfo.html
     * @psalm-impure
     */
    public function getCollectibleInfo(array $collectible, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;
}
<?php
/**
 * This file is automatic generated by build_docs.php file
 * and is used only for autocomplete in multiple IDE
 * don't modify manually.
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\Namespace;

/** @psalm-mutable */
interface Contacts
{
    /**
     * Get the telegram IDs of all contacts.  
     * Returns an array of Telegram user IDs for all contacts (0 if a contact does not have an associated Telegram account or have hidden their account using privacy settings).
     *
     * @param list<int|string>|array<never, never> $hash Array of [Hash used for caching, for more info click here](https://core.telegram.org/api/offsets#hash-generation) @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return list<int> 
     * @psalm-impure
     */
    public function getContactIDs(array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Use this method to obtain the online statuses of all contacts with an accessible Telegram account.
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return list<array{_: 'contactStatus', user_id: int, status: array{_: 'userStatusEmpty'}|array{_: 'userStatusOnline', expires: int}|array{_: 'userStatusOffline', was_online: int}|array{_: 'userStatusRecently', by_me: bool}|array{_: 'userStatusLastWeek', by_me: bool}|array{_: 'userStatusLastMonth', by_me: bool}}> Array of  @see https://docs.madelineproto.xyz/API_docs/types/ContactStatus.html
     * @psalm-impure
     */
    public function getStatuses(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Returns the current user's contact list.
     *
     * @param list<int|string>|array<never, never> $hash Array of [Hash used for caching, for more info click here](https://core.telegram.org/api/offsets#hash-generation).<br>Note that the hash is computed [using the usual algorithm](https://core.telegram.org/api/offsets#hash-generation), passing to the algorithm first the previously returned [contacts.contacts](https://docs.madelineproto.xyz/API_docs/constructors/contacts.contacts.html).`saved_count` field, then max `100000` sorted user IDs from the contact list, including the ID of the currently logged in user if it is saved as a contact. <br>Example: [tdlib implementation](https://github.com/tdlib/td/blob/63c7d0301825b78c30dc7307f1f1466be049eb79/td/telegram/UserManager.cpp#L5754). @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'contacts.contactsNotModified'}|array{_: 'contacts.contacts', contacts: list<array{_: 'contact', mutual: bool, user_id: int}>, saved_count: int, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/contacts.Contacts.html
     * @psalm-impure
     */
    public function getContacts(array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Imports contacts: saves a full list on the server, adds already registered contacts to the contact list, returns added contacts and their info.
     * 
     * Use [contacts.addContact](https://docs.madelineproto.xyz/API_docs/methods/contacts.addContact.html) to add Telegram contacts without actually using their phone number.
     *
     * @param list<array{_: 'inputPhoneContact', client_id?: int, phone?: string, first_name?: string, last_name?: string, note?: array{_: 'textWithEntities', text?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>}}>|array<never, never> $contacts Array of List of contacts to import @see https://docs.madelineproto.xyz/API_docs/types/InputContact.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'contacts.importedContacts', imported: list<array{_: 'importedContact', user_id: int, client_id: int}>, popular_invites: list<array{_: 'popularContact', client_id: int, importers: int}>, retry_contacts: list<int>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/contacts.ImportedContacts.html
     * @psalm-impure
     */
    public function importContacts(array $contacts = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Deletes several contacts from the list.
     *
     * @param list<array|int|string>|array<never, never> $id Array of User ID list @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function deleteContacts(array $id = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Delete contacts by phone number
     *
     * @param list<string>|array<never, never> $phones Phone numbers
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function deleteByPhones(array $phones = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Adds a peer to a blocklist, see [here Â»](https://core.telegram.org/api/block) for more info.
     *
     * @param bool $my_stories_from Whether the peer should be added to the story blocklist; if not set, the peer will be added to the main blocklist, see [here Â»](https://core.telegram.org/api/block) for more info.
     * @param array|int|string $id Peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function block(bool|null $my_stories_from = null, array|int|string|null $id = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Deletes a peer from a blocklist, see [here Â»](https://core.telegram.org/api/block) for more info.
     *
     * @param bool $my_stories_from Whether the peer should be removed from the story blocklist; if not set, the peer will be removed from the main blocklist, see [here Â»](https://core.telegram.org/api/block) for more info.
     * @param array|int|string $id Peer @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function unblock(bool|null $my_stories_from = null, array|int|string|null $id = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Returns the list of blocked users.
     *
     * @param bool $my_stories_from Whether to fetch the story blocklist; if not set, will fetch the main blocklist. See [here Â»](https://core.telegram.org/api/block) for differences between the two.
     * @param int $offset The number of list elements to be skipped
     * @param int $limit The number of list elements to be returned
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'contacts.blocked', blocked: list<array{_: 'peerBlocked', peer_id: array|int|string, date: int}>, chats: list<array|int|string>, users: list<array|int|string>}|array{_: 'contacts.blockedSlice', count: int, blocked: list<array{_: 'peerBlocked', peer_id: array|int|string, date: int}>, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/contacts.Blocked.html
     * @psalm-impure
     */
    public function getBlocked(bool|null $my_stories_from = null, int|null $offset = 0, int|null $limit = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Returns users found by username substring.
     *
     * @param bool $broadcasts 
     * @param bool $bots 
     * @param string $q Target substring
     * @param int $limit Maximum number of users to be returned
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'contacts.found', my_results: list<array|int|string>, results: list<array|int|string>, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/contacts.Found.html
     * @psalm-impure
     */
    public function search(bool|null $broadcasts = null, bool|null $bots = null, string|null $q = '', int|null $limit = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get most used peers
     *
     * @param bool $correspondents Users we've chatted most frequently with
     * @param bool $bots_pm Most used bots
     * @param bool $bots_inline Most used inline bots
     * @param bool $phone_calls Most frequently called users
     * @param bool $forward_users Users to which the users often forwards messages to
     * @param bool $forward_chats Chats to which the users often forwards messages to
     * @param bool $groups Often-opened groups and supergroups
     * @param bool $channels Most frequently visited channels
     * @param bool $bots_app Most frequently used [Main Mini Bot Apps](https://core.telegram.org/api/bots/webapps#main-mini-apps).
     * @param bool $bots_guestchat 
     * @param int $offset Offset for [pagination](https://core.telegram.org/api/offsets)
     * @param int $limit Maximum number of results to return, [see pagination](https://core.telegram.org/api/offsets)
     * @param list<int|string>|array<never, never> $hash Array of [Hash used for caching, for more info click here](https://core.telegram.org/api/offsets#hash-generation) @see https://docs.madelineproto.xyz/API_docs/types/int|string.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'contacts.topPeersNotModified'}|array{_: 'contacts.topPeers', categories: list<array{_: 'topPeerCategoryPeers', category: array{_: 'topPeerCategoryBotsPM'}|array{_: 'topPeerCategoryBotsInline'}|array{_: 'topPeerCategoryCorrespondents'}|array{_: 'topPeerCategoryGroups'}|array{_: 'topPeerCategoryChannels'}|array{_: 'topPeerCategoryPhoneCalls'}|array{_: 'topPeerCategoryForwardUsers'}|array{_: 'topPeerCategoryForwardChats'}|array{_: 'topPeerCategoryBotsApp'}|array{_: 'topPeerCategoryBotsGuestChat'}, count: int, peers: list<array{_: 'topPeer', peer: array|int|string, rating: float}>}>, chats: list<array|int|string>, users: list<array|int|string>}|array{_: 'contacts.topPeersDisabled'} @see https://docs.madelineproto.xyz/API_docs/types/contacts.TopPeers.html
     * @psalm-impure
     */
    public function getTopPeers(bool|null $correspondents = null, bool|null $bots_pm = null, bool|null $bots_inline = null, bool|null $phone_calls = null, bool|null $forward_users = null, bool|null $forward_chats = null, bool|null $groups = null, bool|null $channels = null, bool|null $bots_app = null, bool|null $bots_guestchat = null, int|null $offset = 0, int|null $limit = 0, array $hash = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Reset [rating](https://core.telegram.org/api/top-rating) of top peer
     *
     * @param array{_: 'topPeerCategoryBotsPM'}|array{_: 'topPeerCategoryBotsInline'}|array{_: 'topPeerCategoryCorrespondents'}|array{_: 'topPeerCategoryGroups'}|array{_: 'topPeerCategoryChannels'}|array{_: 'topPeerCategoryPhoneCalls'}|array{_: 'topPeerCategoryForwardUsers'}|array{_: 'topPeerCategoryForwardChats'}|array{_: 'topPeerCategoryBotsApp'}|array{_: 'topPeerCategoryBotsGuestChat'} $category Top peer category @see https://docs.madelineproto.xyz/API_docs/types/TopPeerCategory.html
     * @param array|int|string $peer Peer whose rating should be reset @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function resetTopPeerRating(array $category, array|int|string|null $peer = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Removes all contacts without an associated Telegram account.
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function resetSaved(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Get all contacts, requires a [takeout session, see here Â» for more info](https://core.telegram.org/api/takeout).
     *
     * @param int $takeoutId Takeout ID, generated using account.initTakeoutSession, see [the takeout docs](https://core.telegram.org/api/takeout) for more info.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return list<array{_: 'savedPhoneContact', phone: string, first_name: string, last_name: string, date: int}> Array of  @see https://docs.madelineproto.xyz/API_docs/types/SavedContact.html
     * @psalm-impure
     */
    public function getSaved(int $takeoutId, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Enable/disable [top peers](https://core.telegram.org/api/top-rating)
     *
     * @param bool $enabled Enable/disable
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function toggleTopPeers(bool $enabled, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Add an existing telegram user as contact.
     * 
     * Use [contacts.importContacts](https://docs.madelineproto.xyz/API_docs/methods/contacts.importContacts.html) to add contacts by phone number, without knowing their Telegram ID.
     *
     * @param bool $add_phone_privacy_exception Allow the other user to see our phone number?
     * @param array|int|string $id Telegram ID of the other user @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param string $first_name First name
     * @param string $last_name Last name
     * @param string $phone User's phone number, may be omitted to simply add the user to the contact list, without a phone number.
     * @param array{_: 'textWithEntities', text?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>} $note @see https://docs.madelineproto.xyz/API_docs/types/TextWithEntities.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function addContact(bool|null $add_phone_privacy_exception = null, array|int|string|null $id = null, string|null $first_name = '', string|null $last_name = '', string|null $phone = '', array|null $note = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * If the [add contact action bar is active](https://core.telegram.org/api/action-bar#add-contact), add that user as contact
     *
     * @param array|int|string $id The user to add as contact @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function acceptContact(array|int|string|null $id = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Get users and geochats near you, see [here Â»](https://core.telegram.org/api/nearby) for more info.
     *
     * @param bool $background While the geolocation of the current user is public, clients should update it in the background every half-an-hour or so, while setting this flag. <br>Do this only if the new location is more than 1 KM away from the previous one, or if the previous location is unknown.
     * @param array{_: 'inputGeoPointEmpty'}|array{_: 'inputGeoPoint', lat: float, long: float, accuracy_radius?: int} $geo_point Geolocation @see https://docs.madelineproto.xyz/API_docs/types/InputGeoPoint.html
     * @param int $self_expires If set, the geolocation of the current user will be public for the specified number of seconds; pass 0x7fffffff to disable expiry, 0 to make the current geolocation private; if the flag isn't set, no changes will be applied.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function getLocated(bool|null $background = null, array|null $geo_point = null, int|null $self_expires = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Stop getting notifications about [discussion replies](https://core.telegram.org/api/discussion) of a certain user in `@replies`
     *
     * @param bool $delete_message Whether to delete the specified message as well
     * @param bool $delete_history Whether to delete all `@replies` messages from this user as well
     * @param bool $report_spam Whether to also report this user for spam
     * @param int $msg_id ID of the message in the [@replies](https://core.telegram.org/api/discussion#replies) chat
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array @see https://docs.madelineproto.xyz/API_docs/types/Updates.html
     * @psalm-impure
     */
    public function blockFromReplies(bool|null $delete_message = null, bool|null $delete_history = null, bool|null $report_spam = null, int|null $msg_id = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Resolve a phone number to get user info, if their privacy settings allow it.
     *
     * @param string $phone Phone number in international format, possibly obtained from a [phone number deep link](https://core.telegram.org/api/links#phone-number-links).
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'contacts.resolvedPeer', peer: array|int|string, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/contacts.ResolvedPeer.html
     * @psalm-impure
     */
    public function resolvePhone(string|null $phone = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Generates a [temporary profile link](https://core.telegram.org/api/links#temporary-profile-links) for the currently logged-in user.
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'exportedContactToken', url: string, expires: int} @see https://docs.madelineproto.xyz/API_docs/types/ExportedContactToken.html
     * @psalm-impure
     */
    public function exportContactToken(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Obtain user info from a [temporary profile link](https://core.telegram.org/api/links#temporary-profile-links).
     *
     * @param string $token The token extracted from the [temporary profile link](https://core.telegram.org/api/links#temporary-profile-links).
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array|int|string @see https://docs.madelineproto.xyz/API_docs/types/User.html
     * @psalm-impure
     */
    public function importContactToken(string|null $token = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array|int|string;

    /**
     * Edit the [close friends list, see here Â»](https://core.telegram.org/api/privacy) for more info.
     *
     * @param list<int>|array<never, never> $id Full list of user IDs of close friends, see [here](https://core.telegram.org/api/privacy) for more info.
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function editCloseFriends(array $id = [], ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Replace the contents of an entire [blocklist, see here for more info Â»](https://core.telegram.org/api/block).
     *
     * @param bool $my_stories_from Whether to edit the story blocklist; if not set, will edit the main blocklist. See [here Â»](https://core.telegram.org/api/block) for differences between the two.
     * @param list<array|int|string>|array<never, never> $id Array of Full content of the blocklist. @see https://docs.madelineproto.xyz/API_docs/types/InputPeer.html
     * @param int $limit Maximum number of results to return, [see pagination](https://core.telegram.org/api/offsets)
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function setBlocked(bool|null $my_stories_from = null, array $id = [], int|null $limit = 0, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * Fetch all users with birthdays that fall within +1/-1 days, relative to the current day: this method should be invoked by clients every 6-8 hours, and if the result is non-empty, it should be used to appropriately update locally cached birthday information in [user](https://docs.madelineproto.xyz/API_docs/constructors/user.html).`birthday`.
     * 
     * [See here Â»](https://core.telegram.org/api/profile#birthday) for more info.
     *
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'contacts.contactBirthdays', contacts: list<array{_: 'contactBirthday', birthday: array{_: 'birthday', day: int, month: int, year?: int}, contact_id: int}>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/contacts.ContactBirthdays.html
     * @psalm-impure
     */
    public function getBirthdays(?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * Obtain a list of sponsored peer search results for a given query
     *
     * @param string $q The query
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'contacts.sponsoredPeersEmpty'}|array{_: 'contacts.sponsoredPeers', peers: list<array{_: 'sponsoredPeer', peer: array|int|string, random_id: string, sponsor_info?: string, additional_info?: string}>, chats: list<array|int|string>, users: list<array|int|string>} @see https://docs.madelineproto.xyz/API_docs/types/contacts.SponsoredPeers.html
     * @psalm-impure
     */
    public function getSponsoredPeers(string|null $q = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param array{_: 'textWithEntities', text?: string, entities?: list<array{_: 'messageEntityUnknown', offset?: int, length?: int}|array{_: 'messageEntityMention', offset?: int, length?: int}|array{_: 'messageEntityHashtag', offset?: int, length?: int}|array{_: 'messageEntityBotCommand', offset?: int, length?: int}|array{_: 'messageEntityUrl', offset?: int, length?: int}|array{_: 'messageEntityEmail', offset?: int, length?: int}|array{_: 'messageEntityBold', offset?: int, length?: int}|array{_: 'messageEntityItalic', offset?: int, length?: int}|array{_: 'messageEntityCode', offset?: int, length?: int}|array{_: 'messageEntityPre', offset?: int, length?: int, language?: string}|array{_: 'messageEntityTextUrl', offset?: int, length?: int, url?: string}|array{_: 'messageEntityMentionName', offset?: int, length?: int, user_id?: int}|array{_: 'inputMessageEntityMentionName', offset?: int, length?: int, user_id?: array|int|string}|array{_: 'messageEntityPhone', offset?: int, length?: int}|array{_: 'messageEntityCashtag', offset?: int, length?: int}|array{_: 'messageEntityUnderline', offset?: int, length?: int}|array{_: 'messageEntityStrike', offset?: int, length?: int}|array{_: 'messageEntityBankCard', offset?: int, length?: int}|array{_: 'messageEntitySpoiler', offset?: int, length?: int}|array{_: 'messageEntityCustomEmoji', offset?: int, length?: int, document_id?: int}|array{_: 'messageEntityBlockquote', collapsed?: bool, offset?: int, length?: int}|array{_: 'messageEntityFormattedDate', relative?: bool, short_time?: bool, long_time?: bool, short_date?: bool, long_date?: bool, day_of_week?: bool, offset?: int, length?: int, date?: int}|array{_: 'messageEntityDiffInsert', offset?: int, length?: int}|array{_: 'messageEntityDiffReplace', offset?: int, length?: int, old_text?: string}|array{_: 'messageEntityDiffDelete', offset?: int, length?: int}|array{_: 'messageEntityTonAddress', offset?: int, length?: int}|array{_: 'messageEntityBlockquote', offset?: int, length?: int}>} $note @see https://docs.madelineproto.xyz/API_docs/types/TextWithEntities.html
     * @param array|int|string $id @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return bool 
     * @psalm-impure
     */
    public function updateContactNote(array $note, array|int|string|null $id = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): bool;

    /**
     * 
     *
     * @param bool $revoke 
     * @param bool $temporary 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array{_: 'exportedNoPaidMessagesToken', token: string, url: string, expires?: int} @see https://docs.madelineproto.xyz/API_docs/types/ExportedNoPaidMessagesToken.html
     * @psalm-impure
     */
    public function exportNoPaidMessagesToken(bool|null $revoke = null, bool|null $temporary = null, ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array;

    /**
     * 
     *
     * @param array|int|string $user_id @see https://docs.madelineproto.xyz/API_docs/types/InputUser.html
     * @param string $token 
     * @param ?int $floodWaitLimit Can be used to specify a custom flood wait limit: if a FLOOD_WAIT_ rate limiting error is received with a waiting period bigger than this integer, an RPCErrorException will be thrown; otherwise, MadelineProto will simply wait for the specified amount of time. Defaults to the value specified in the settings: https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/RPC.html#setfloodtimeout-int-floodtimeout-self
     * @param ?string $queueId If specified, ensures strict server-side execution order of concurrent calls with the same queue ID.
     * @param ?\Amp\Cancellation $cancellation Cancellation
     * @return array|int|string @see https://docs.madelineproto.xyz/API_docs/types/User.html
     * @psalm-impure
     */
    public function importNoPaidMessagesToken(array|int|string|null $user_id = null, string|null $token = '', ?int $floodWaitLimit = null, ?string $queueId = null, ?\Amp\Cancellation $cancellation = null): array|int|string;
}
<?php

declare(strict_types=1);

/**
 * Update handler type.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

/**
 * @internal
 */
enum UpdateHandlerType
{
    case EVENT_HANDLER;
    case GET_UPDATES;
    case WEBHOOK;
    case NOOP;
}
<?php

declare(strict_types=1);

/**
 * Shutdown module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use function Amp\ByteStream\getStdin;

/**
 * Class that controls script shutdown.
 */
final class Shutdown
{
    /**
     * Callbacks to call on shutdown.
     *
     * @var array<callable>
     */
    private static array $callbacks = [];
    /**
     * Whether the main shutdown was registered.
     *
     */
    private static bool $registered = false;
    /**
     * Incremental ID for new callback.
     *
     */
    private static int $id = 0;
    /**
     * Function to be called on shutdown.
     */
    private static function shutdown(): void
    {
        foreach (self::$callbacks as $callback) {
            $callback();
        }
        self::$callbacks = [];

        if (\defined('STDIN')) {
            getStdin()->unreference();
        }
        API::finalize();
        MTProto::serializeAll();
        Logger::finalize();
        if (class_exists(Installer::class)) {
            Installer::unlock();
        }
    }
    /**
     * Register shutdown function.
     */
    public static function init(): void
    {
        if (!self::$registered) {
            register_shutdown_function(static fn () => self::shutdown());
            self::$registered = true;
        }
    }
    /**
     * Add a callback for script shutdown.
     *
     * @param  callable    $callback The callback to set
     * @param  null|string $id       The optional callback ID
     * @return int|string  The callback ID
     */
    public static function addCallback(callable $callback, ?string $id = null): int|string
    {
        if (!$id) {
            $id = self::$id++;
        }
        self::$callbacks[$id] = $callback;
        self::init();
        return $id;
    }
    /**
     * Remove a callback from the script shutdown callable list.
     *
     * @param null|string|int $id The optional callback ID
     *
     * @return bool            true if the callback was removed correctly, false otherwise
     *
     * @psalm-external-mutation-free
     */
    public static function removeCallback(string|int|null $id): bool
    {
        if (isset(self::$callbacks[$id])) {
            unset(self::$callbacks[$id]);
            return true;
        }
        return false;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use InvalidArgumentException;

/**
 * The media streams a participant of a call can send, as bit flags.
 *
 * Every party of a call has up to three streams: their microphone ({@see self::AUDIO}), their
 * camera ({@see self::VIDEO}) and their screen share ({@see self::SCREEN}), each of which they can
 * turn on and off at any time. A *set* of streams is a bitmask of these flags, as used by
 * {@see EventHandler\Call::setOutput()} to pick the tracks of a recording and reported by
 * {@see EventHandler\Calls\CallStreams} updates.
 *
 * @psalm-type StreamMask = int-mask<CallStream::AUDIO, CallStream::VIDEO, CallStream::SCREEN>
 *
 * @psalm-immutable
 */
final class CallStream
{
    /** The microphone audio (OPUS). */
    public const AUDIO = 1;
    /** The camera video. */
    public const VIDEO = 2;
    /** The screen share (presentation) video. */
    public const SCREEN = 4;
    /** Every stream. */
    public const ALL = self::AUDIO | self::VIDEO | self::SCREEN;

    /** The name of each flag, in the order they are listed. */
    public const NAMES = [self::AUDIO => 'audio', self::VIDEO => 'video', self::SCREEN => 'screen'];

    /**
     * @psalm-mutation-free
     */
    private function __construct()
    {
    }

    /**
     * The names of the streams in a set, in order: `audio`, `video`, `screen`.
     *
     * @param StreamMask $streams
     *
     * @return list<string>
     *
     * @psalm-pure
     */
    public static function names(int $streams): array
    {
        $names = [];
        foreach (self::NAMES as $flag => $name) {
            if ($streams & $flag) {
                $names[] = $name;
            }
        }
        return $names;
    }

    /**
     * A human-readable description of a set of streams: `audio+video`, or `nothing` for an empty set.
     *
     * @param StreamMask $streams
     *
     * @psalm-pure
     */
    public static function describe(int $streams): string
    {
        return $streams === 0 ? 'nothing' : implode('+', self::names($streams));
    }

    /**
     * Build a set from the state of each stream.
     *
     * @return StreamMask
     *
     * @psalm-pure
     */
    public static function of(bool $audio, bool $video, bool $screen): int
    {
        return ($audio ? self::AUDIO : 0) | ($video ? self::VIDEO : 0) | ($screen ? self::SCREEN : 0);
    }

    /**
     * Check that every stream of a chosen set is available (and that, with no choice, something is).
     *
     * @param StreamMask|null $streams   The chosen set, or null for "everything available".
     * @param StreamMask      $available The streams currently available.
     * @param string          $who       Who sends them, for the error message.
     *
     * @throws InvalidArgumentException
     *
     * @internal
     *
     * @psalm-pure
     */
    public static function checkAvailable(?int $streams, int $available, string $who): void
    {
        if ($streams === null) {
            if ($available === 0) {
                throw new InvalidArgumentException("$who is not sending anything to record right now: wait for a CallStreams update announcing a stream.");
            }
            return;
        }
        $missing = $streams & ~$available;
        if ($missing !== 0) {
            throw new InvalidArgumentException("$who is not sending ".self::describe($missing).' right now (only '.self::describe($available).'): wait for a CallStreams update announcing it, or record only what is available.');
        }
    }

    /**
     * Check that a set only holds known flags.
     *
     * @param StreamMask $streams
     *
     * @throws InvalidArgumentException
     *
     * @psalm-pure
     */
    public static function validate(int $streams): void
    {
        if ($streams & ~self::ALL) {
            throw new InvalidArgumentException("Unknown stream flags in $streams: use a bitmask of CallStream::AUDIO, CallStream::VIDEO and CallStream::SCREEN.");
        }
    }
}
<?php

declare(strict_types=1);

/**
 * PTSException module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

/**
 * Indicates that a specified peer (user, chat, channel) was not found in the internal MadelineProto peer database.
 *
 * This can usually be fixed by enabling `setFullFetch(true)` in the [peer database settings](https://docs.madelineproto.xyz/PHP/danog/MadelineProto/Settings/Peer.html#setfullfetch-bool-fullfetch-self).
 */
final class PeerNotInDbException extends Exception
{
    public function __construct()
    {
        parent::__construct(Lang::$current_lang['peer_not_in_db']);
    }
}
<?php

declare(strict_types=1);

/**
 * Serialization module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use Amp\Cancellation;
use Amp\CancelledException;
use Amp\CompositeCancellation;
use Amp\DeferredCancellation;
use Amp\DeferredFuture;
use Amp\Future;
use Amp\Ipc\Sync\ChannelledSocket;
use Amp\TimeoutException;
use AssertionError;
use danog\AsyncOrm\DbArrayBuilder;
use danog\AsyncOrm\Driver\DriverArray;
use danog\AsyncOrm\KeyType;
use danog\AsyncOrm\ValueType;
use danog\MadelineProto\Ipc\AlreadyServedException;
use danog\MadelineProto\Ipc\Server;
use danog\MadelineProto\Settings\Database\DriverDatabaseAbstract;
use Revolt\EventLoop;
use Throwable;

use const LOCK_EX;
use const LOCK_NB;
use const LOCK_SH;
use const LOCK_UN;

use function Amp\async;
use function Amp\delay;
use function Amp\File\exists;
use function Amp\Ipc\connect;

/**
 * Manages serialization of the MadelineProto instance.
 *
 * @internal
 */
abstract class Serialization
{
    /**
     * How long an IPC worker waits for the process holding the session lock to serve the session, in seconds.
     */
    private const WORKER_LOCK_TIMEOUT = 60;
    /**
     * How long to wait for a server to answer, in seconds.
     */
    private const SERVED_TIMEOUT = 10;
    /**
     * Unserialize session.
     *
     * Logic for deserialization is as follows.
     * - If the session is unlocked
     *   - Try starting IPC server:
     *     - Fetch light state
     *     - If don't need event handler
     *       - Unlock
     *       - Fork
     *       - Lock (fork)
     *       - Deserialize full (fork)
     *       - Start IPC server (fork)
     *       - Store IPC state (fork)
     *     - If need event handler
     *       - If have event handler class
     *         - Deserialize full
     *         - Start IPC server
     *         - Store IPC state
     *     - Else Fallthrough
     *   - Wait for a new IPC state for a maximum of 30 seconds, then throw
     *   - Execute original request via IPC
     *
     * - If the session is locked
     *   - In parallel (concurrent):
     *     - The IPC server should be running, connect
     *     - Try starting full session
     *       - Fetch light state
     *       - If don't need event handler
     *         - Wait lock
     *         - Unlock
     *         - Fork
     *         - Lock (fork)
     *         - Deserialize full (fork)
     *         - Start IPC server (fork)
     *         - Store IPC state (fork)
     *       - If need event handler and have event handler class
     *         - Wait lock
     *         - Deserialize full
     *         - Start IPC server
     *         - Store IPC state
     *   - Wait for a new IPC session for a maximum of 30 seconds, then throw
     *   - Execute original request via IPC
     *
     *
     *
     * - If receiving a startAndLoop or setEventHandler request on an IPC session:
     *   - Shutdown remote IPC server
     *   - Deserialize full
     *   - Start IPC server
     *   - Store IPC state
     *
     * @param SessionPaths     $session   Session name
     * @param SettingsAbstract $settings  Settings
     * @param bool             $forceFull Whether to force full session deserialization
     * @param bool             $selfRestart Whether full deserialization is only forced by a self-restart, which waits for its predecessor to release the lock: connect to a live server instead, which won't release it
     * @internal
     * @return array{0: (ChannelledSocket|APIWrapper|Throwable|null|0), 1: (callable|null)}
     */
    public static function unserialize(SessionPaths $session, SettingsAbstract $settings, bool $forceFull = false, bool $selfRestart = false): array
    {
        //Logger::log('Waiting for exclusive session lock...');
        $warningId = EventLoop::delay(1, static function () use (&$warningId): void {
            if (isset($_GET['MadelineSelfRestart'])) {
                Logger::log("MadelineProto self-restarted successfully!");
            } else {
                Logger::log('It seems like the session is busy.');
                Logger::log('Telegram does not support starting multiple instances of the same session, make sure no other instance of the session is running.');
                $warningId = EventLoop::repeat(5, static fn () => Logger::log('Still waiting for exclusive session lock...'));
                EventLoop::unreference($warningId);
            }
        });
        EventLoop::unreference($warningId);

        $lightState = null;
        $cancelFlock = new DeferredCancellation;
        $cancelIpc = new DeferredFuture;
        // Cancelled once we stop waiting for the lock
        $lockWait = new DeferredCancellation;
        $canContinue = true;
        $ipcSocket = null;
        // For IPC workers: true if the lock holder serves the session, false if it didn't for WORKER_LOCK_TIMEOUT seconds
        $alreadyServed = null;
        $cancelFull = static function () use (&$cancelFlock): void {
            if ($cancelFlock !== null) {
                $copy = $cancelFlock;
                $cancelFlock = null;
                $copy->cancel();
            }
        };
        if (!$forceFull) {
            $failureCb = static function () use ($session, $cancelFull, $lockWait, &$canContinue, &$ipcSocket, &$lightState): void {
                if ($lockWait->isCancelled()) {
                    return;
                }
                $lightStateRead = async(static function () use ($session, $cancelFull, &$canContinue, &$lightState): void {
                    try {
                        $lightState = $session->getLightState();
                        if (!$lightState->canStartIpc()) {
                            $canContinue = false;
                            $cancelFull();
                        }
                    } catch (Throwable) {
                        $lightState = false;
                    }
                });
                try {
                    // The lock holder is an instance that serves (or will serve) the session once it's loaded, or a
                    // client that releases the lock in a moment: connect as soon as the holder listens, however
                    // long it takes to load the session, unless we get the lock first.
                    while (!$lockWait->isCancelled()) {
                        Logger::log('MadelineProto is starting, please wait...');
                        if ($socket = self::connectIfListening($session->getIpcPath())) {
                            $ipcSocket = [$socket, null];
                            $cancelFull();
                            return;
                        }
                        try {
                            delay(1.0, true, $lockWait->getCancellation());
                        } catch (CancelledException) {
                        }
                    }
                } finally {
                    // Its result is used once we get the lock, and it must not outlive this call: at shutdown,
                    // ext-uv runs the callbacks of the file operations still in flight on its destroyed loop.
                    $lightStateRead->await();
                }
            };
        } elseif (Magic::$isIpcWorker) {
            // An IPC worker that doesn't get the lock was started concurrently with another one (by concurrent
            // clients, or by the process and the web runner of the same client): if the lock holder serves the
            // session, this worker is redundant and must exit, without reporting an error.
            // Only a server that answers counts: one that is shutting down may still have its socket bound.
            // A holder that never serves (it drops connections, or it's stuck) must not keep the worker (a process,
            // or a web server worker) around forever: if the holder serves the session eventually (it's still
            // loading it), its clients connect to it anyway.
            $failureCb = static function () use ($session, $cancelFull, $lockWait, &$alreadyServed): void {
                $deadline = microtime(true) + self::WORKER_LOCK_TIMEOUT;
                while (!$lockWait->isCancelled()) {
                    if (self::isServed($session->getIpcPath(), $lockWait->getCancellation())) {
                        $alreadyServed = true;
                        $cancelFull();
                        return;
                    }
                    if (microtime(true) >= $deadline) {
                        $alreadyServed = false;
                        $cancelFull();
                        return;
                    }
                    try {
                        delay(1.0, true, $lockWait->getCancellation());
                    } catch (CancelledException) {
                    }
                }
            };
        } elseif ($selfRestart) {
            // A self-restarted request forces full instances to take over the session of the request it replaces,
            // which releases the lock as it exits; but the script may also open other sessions, served by an IPC
            // worker that never releases the lock: a live server answers, so connect to it instead of waiting
            // forever. If it's the event handler's session, startAndLoop() then stops the server and takes over.
            $failureCb = static function () use ($session, $cancelFull, $lockWait, &$ipcSocket): void {
                while (!$lockWait->isCancelled()) {
                    if (self::isServed($session->getIpcPath(), $lockWait->getCancellation())) {
                        try {
                            $socket = connect($session->getIpcPath());
                            Logger::log('Connected to IPC socket!');
                            $ipcSocket = [$socket, null];
                            $cancelFull();
                            return;
                        } catch (Throwable) {
                        }
                    }
                    try {
                        delay(1.0, true, $lockWait->getCancellation());
                    } catch (CancelledException) {
                    }
                }
            };
        } else {
            $failureCb = null;
        }
        // Awaited once we stop waiting for the lock, so that it doesn't outlive this call
        $failure = null;
        try {
            $unlock = Tools::flock(
                $session->getLockPath(),
                LOCK_EX,
                1,
                $cancelFlock->getCancellation(),
                $failureCb ? static function () use ($failureCb, &$failure): void {
                    $failure = async($failureCb);
                } : null,
            );
        } finally {
            $lockWait->cancel();
            EventLoop::cancel($warningId);
            $failure?->await();
        }

        if (!$unlock) { // Canceled, don't have lock
            if ($alreadyServed) {
                throw new AlreadyServedException("Another process is already serving the session {$session->getSessionDirectoryPath()}");
            }
            if ($alreadyServed === false) {
                throw new AlreadyServedException("Another process holds the lock of the session {$session->getSessionDirectoryPath()} without serving it for ".self::WORKER_LOCK_TIMEOUT." seconds");
            }
            return $ipcSocket;
        }
        if (!$canContinue) { // Have lock, can't use it
            Logger::log("Session has event handler, but it's not started.", Logger::ERROR);
            Logger::log("We don't have access to the event handler class, so we can't start it.", Logger::ERROR);
            Logger::log('Please start the event handler or unset it to use the IPC server.', Logger::ERROR);
            $unlock();
            return $ipcSocket;
        }

        try {
            $lightState ??= $session->getLightState();
        } catch (Throwable) {
        }

        \assert($lightState === null || $lightState instanceof LightState);

        $exists = exists($session->getSessionPath());

        if ($lightState && !$forceFull) {
            if (!$class = $lightState->getEventHandler()) {
                // Unlock and fork
                $unlock();
                return self::startServerAndConnect($session, $ipcSocket);
            } elseif (!class_exists($class)) {
                // Have lock, can't use it
                $unlock();
                Logger::log("Session has event handler (class $class), but it's not started.", Logger::ERROR);
                Logger::log("We don't have access to the event handler class, so we can't start it.", Logger::ERROR);
                Logger::log('Please start the event handler or unset it to use the IPC server.', Logger::ERROR);
                return $ipcSocket ?? self::tryConnect($session->getIpcPath(), $cancelIpc->getFuture(), customE: new AssertionError("Please make sure the $class class is in scope, or that the event handler is running (in a separate process or in the current process)."));
            } elseif (is_subclass_of($class, EventHandler::class)) {
                EventHandler::cachePlugins($class);
            }
        } elseif ($lightState) {
            $class = $lightState->getEventHandler();
            if ($class && !class_exists($class)) {
                // Have lock, can't use it
                $unlock();
                Logger::log("Session has event handler, but it's not started.", Logger::ERROR);
                Logger::log("We don't have access to the event handler class, so we can't start it.", Logger::ERROR);
                Logger::log('Please start the event handler or unset it to use the IPC server.', Logger::ERROR);
                throw new AssertionError("Please make sure the $class class is in scope, or that the event handler is running (in a separate process or in the current process).");
            } elseif ($class && is_subclass_of($class, EventHandler::class)) {
                EventHandler::cachePlugins($class);
            }
        } elseif ($exists) {
            throw new AssertionError("Could not read the lightstate file, check logs!");
        }

        $tempId = Shutdown::addCallback($unlock = static function () use ($unlock): void {
            Logger::log('Unlocking exclusive session lock!');
            $unlock();
            Logger::log('Unlocked exclusive session lock!');
        });
        Logger::log('Got exclusive session lock!');

        if ($exists) {
            $unserialized = $session->unserialize();
        } else {
            $unserialized = null;
        }
        if ($settings instanceof Settings) {
            $settings = $settings->getDb();
        }
        $prefix = null;
        if ($unserialized instanceof DriverArray
            || $unserialized instanceof DbArrayBuilder
            || (
                !$exists
                && $settings instanceof DriverDatabaseAbstract
                && $prefix = $settings->getEphemeralFilesystemPrefix()
            )
        ) {
            $tableName = null;
            $array = null;
            if ($prefix !== null) {
                $tableName = "{$prefix}_MTProto_session";
            } elseif ($unserialized instanceof DriverArray) {
                $unserialized = (array) $unserialized;
                $tableName = $unserialized["\0*\0table"];
                $settings = $unserialized["\0*\0dbSettings"];
            } else {
                \assert($unserialized instanceof DbArrayBuilder);
                $array = $unserialized->build();
            }
            if ($tableName !== null && $settings instanceof DriverDatabaseAbstract) {
                Logger::log('Extracting session from database...');
                $array = (new DbArrayBuilder(
                    $tableName,
                    $settings->getOrmSettings(),
                    KeyType::STRING,
                    ValueType::SCALAR,
                ))->build();
            }
            \assert($array !== null);
            $unserialized = $array->get('data');
            if (!$unserialized && $exists) {
                throw new Exception('Could not extract session from database!');
            }
        }
        \assert($unserialized instanceof APIWrapper || $unserialized === null);

        Shutdown::removeCallback($tempId);
        return [$unserialized, $unlock];
    }

    /**
     * Whether the session lock is held by another process (an instance loading or serving the session, or a client
     * about to start one): the IPC server, if any, can only be started by the lock holder.
     */
    public static function isLockedByAnotherProcess(SessionPaths $session): bool
    {
        try {
            $res = fopen($session->getLockPath(), 'c');
        } catch (Throwable) {
            return false;
        }
        try {
            if (flock($res, LOCK_SH|LOCK_NB)) {
                flock($res, LOCK_UN);
                return false;
            }
            return true;
        } finally {
            fclose($res);
        }
    }

    /**
     * Whether a live server answers on the IPC socket.
     *
     * Connecting is not enough: the socket of a server that is shutting down may still be bound.
     */
    private static function isServed(string $ipcPath, Cancellation $cancellation): bool
    {
        try {
            clearstatcache(true, $ipcPath);
            $socket = connect($ipcPath);
        } catch (Throwable) {
            return false;
        }
        try {
            $socket->send(['isIpcWorker', []]);
            // The server answers once it has loaded the session (it only listens then): one that doesn't answer
            // within a few seconds doesn't serve it, it must not hang the caller.
            $res = async($socket->receive(...))->await(new CompositeCancellation(
                $cancellation,
                Tools::getTimeoutCancellation(self::SERVED_TIMEOUT, "The IPC server didn't answer"),
            ));
            return \is_array($res);
        } catch (Throwable) {
            return false;
        } finally {
            try {
                $socket->disconnect();
            } catch (Throwable) {
            }
        }
    }

    /**
     * Connect to the IPC server of a session if something listens on its socket.
     */
    private static function connectIfListening(string $ipcPath): ?ChannelledSocket
    {
        try {
            clearstatcache(true, $ipcPath);
            $socket = connect($ipcPath);
            Logger::log('Connected to IPC socket!');
            return $socket;
        } catch (Throwable $e) {
            $e = $e->getMessage();
            if ($e !== 'The endpoint does not exist!') {
                Logger::log("$e while connecting to IPC socket");
            }
            return null;
        }
    }

    /**
     * Connect again to the IPC server of a session, after losing the connection to it.
     *
     * Like unserialize(), only starts a server if nobody can serve the session: if something listens, that's the
     * server (if it drops us, the client backs off and gives up eventually); if another process holds the session
     * lock, it's an instance loading the session (it listens once it's done) or releasing it, so wait for it.
     * Starting a server anyway spawned a worker (and sent requests to the web server) on every attempt, all of them
     * waiting for a lock that the live holder never releases.
     *
     * @internal
     *
     * @param  Cancellation                                      $cancellation Stops waiting for the lock holder
     * @return array{0: (ChannelledSocket|Throwable|0), 1: null}
     */
    public static function reconnect(SessionPaths $session, Cancellation $cancellation): array
    {
        $ipcPath = $session->getIpcPath();
        $waiting = false;
        while (true) {
            if ($socket = self::connectIfListening($ipcPath)) {
                return [$socket, null];
            }
            if (!self::isLockedByAnotherProcess($session)) {
                return self::startServerAndConnect($session);
            }
            if (!$waiting) {
                Logger::log('The session is locked by another process (loading or releasing it), waiting for it...');
                $waiting = true;
            }
            delay(1.0, true, $cancellation);
        }
    }

    /**
     * Start the IPC server of a session, and connect to it (or to the one another process started concurrently).
     *
     * @internal
     *
     * @param  ?array{0: ChannelledSocket, 1: null}              $connected An existing connection to return instead
     * @return array{0: (ChannelledSocket|Throwable|0), 1: null}
     */
    public static function startServerAndConnect(SessionPaths $session, ?array $connected = null): array
    {
        $stopMonitor = new DeferredCancellation;
        $monitor = Server::startMe($session, $stopMonitor->getCancellation());
        $result = null;
        try {
            return $result = $connected ?? self::tryConnect($session->getIpcPath(), $monitor, waitStartup: true);
        } finally {
            // We're connected (or gave up) before the server reports its startup, possibly: the monitor polls the IPC
            // state file until then, so it must not outlive this call. At shutdown, ext-uv runs the callbacks of the
            // file operations still in flight on its destroyed loop.
            $stopMonitor->cancel();
            try {
                $monitor->await();
            } catch (Throwable $e) {
                // Otherwise tryConnect threw it already
                if ($result !== null) {
                    Logger::log("Error while monitoring the IPC server startup: $e", Logger::ERROR);
                }
            }
        }
    }

    /**
     * Try connecting to IPC socket.
     *
     * @param  string                                            $ipcPath       IPC path
     * @param  Future<(bool|Throwable|null)>                     $cancelConnect Cancelation token (triggers cancellation of connection)
     * @param  null|callable(): void                             $cancelFull    Cancelation token source (can trigger cancellation of full unserialization)
     * @param  bool                                              $waitStartup   Whether $cancelConnect is the monitor of a server startup: the attempts only count once it's resolved, since the server only listens once it has loaded the session, however long that takes
     * @return array{0: (ChannelledSocket|Throwable|0), 1: null}
     */
    public static function tryConnect(string $ipcPath, Future $cancelConnect, ?callable $cancelFull = null, ?Throwable $customE = null, bool $waitStartup = false): array
    {
        $x = 0;
        while ($waitStartup || $x++ < 25) {
            Logger::log('MadelineProto is starting, please wait...');
            try {
                clearstatcache(true, $ipcPath);
                $socket = connect($ipcPath);
                Logger::log('Connected to IPC socket!');
                if ($cancelFull) {
                    $cancelFull();
                }
                return [$socket, null];
            } catch (Throwable $e) {
                $e = $e->getMessage();
                if ($e !== 'The endpoint does not exist!') {
                    Logger::log("$e while connecting to IPC socket");
                }
            }
            try {
                $res = $cancelConnect->await(Tools::getTimeoutCancellation(1.0, "Timeout while connecting to IPC socket"));
                if ($res instanceof Throwable) {
                    return [$res, null];
                }
                // The server started (or the startup is over): it listens now, if it ever will
                $cancelConnect = (new DeferredFuture)->getFuture();
                $waitStartup = false;
            } catch (CancelledException $e) {
                if (!$e->getPrevious() instanceof TimeoutException) {
                    throw $e;
                }
            }
        }
        return [$customE ?? 0, null];
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Ipc\Runner;

use Amp\ByteStream\ReadableResourceStream;
use Amp\Process\Internal\Posix\Runner;
use danog\MadelineProto\Logger;
use danog\MadelineProto\Magic;
use Error;
use Revolt\EventLoop;
use Throwable;

use const ARRAY_FILTER_USE_BOTH;
use const DIRECTORY_SEPARATOR;
use const PATH_SEPARATOR;
use const PHP_BINARY;
use const PHP_BINDIR;
use const PHP_OS;
use const PHP_OS_FAMILY;
use const PHP_SAPI;

use function Amp\async;
use function Amp\Future\await;
use function Amp\Process\escapeArgument;

/**
 * @internal
 */
final class ProcessRunner extends RunnerAbstract
{
    private const CGI_VARS = [
        'AUTH_TYPE',
        'CONTENT_LENGTH',
        'CONTENT_TYPE',
        'GATEWAY_INTERFACE',
        'PATH_INFO',
        'PATH_TRANSLATED',
        'QUERY_STRING',
        'REMOTE_ADDR',
        'REMOTE_HOST',
        'REMOTE_IDENT',
        'REMOTE_USER',
        'REQUEST_METHOD',
        'SCRIPT_NAME',
        'SERVER_NAME',
        'SERVER_PORT',
        'SERVER_PROTOCOL',
        'SERVER_SOFTWARE',
    ];

    /** @var string|null Cached path to located PHP binary. */
    private static ?string $binaryPath = null;

    /**
     * Handles of the workers, until they exit.
     *
     * @var array<int, resource>
     */
    private static array $resources = [];

    /**
     * Runner.
     *
     * @psalm-suppress InternalMethod, InternalProperty, InternalClass
     *
     * @param string $session Session path
     */
    #[\Override]
    public static function start(string $session, int $startupId): bool
    {
        if (PHP_SAPI === 'cli') {
            $binary = PHP_BINARY;
        } else {
            $binary = self::$binaryPath ?? self::locateBinary();
        }

        $options = [
            'html_errors' => '0',
            'display_errors' => '0',
            'log_errors' => '1',
        ];

        $runner = self::getScriptPath();

        $command = [
            $binary,
            ...self::formatOptions($options),
            $runner,
            'madeline-ipc',
            $session,
            (string) $startupId,
        ];
        $command = implode(' ', array_map(escapeArgument(...), $command));
        Logger::log("Starting process with $command");

        $params = [
            'argv' => ['madeline-ipc', $session, $startupId],
            'cwd' => Magic::getcwd(),
        ];
        $root = '';
        if (PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg') {
            try {
                $root = WebRunner::getAbsoluteRootDir();
            } catch (Throwable) {
            }
        }
        $envVars = array_merge(
            array_filter($_SERVER, static fn ($v, $k): bool => \is_string($v) && !\in_array($k, self::CGI_VARS, true), ARRAY_FILTER_USE_BOTH),
            [
                'QUERY_STRING' => http_build_query($params),
                'absoluteRootDir' => $root,
                'serverName' => $_SERVER['SERVER_NAME'] ?? '',
            ],
        );

        $descriptors = self::getWorkerDescriptors();
        try {
            $process = proc_open($command, $descriptors, $pipes, null, $envVars);
        } catch (Throwable $e) {
            // proc_open dup()s one descriptor per hidden one: it can fail close to the descriptor limit
            Logger::log("Could not hide the descriptors of this process from the IPC worker, starting it with them: $e", Logger::WARNING);
            $process = proc_open($command, \array_slice($descriptors, 0, 3, true), $pipes, null, $envVars);
        }
        if ($process === false) {
            throw new Error("Could not start the IPC worker process!");
        }
        // The process handle references its pipes: they stay open for as long as it's kept, whatever happens to
        // their streams. It's only kept until the worker exits, and the worker doesn't read its standard input.
        fclose($pipes[0]);
        $id = (int) $process;
        self::$resources[$id] = $process;
        $stdout = new ReadableResourceStream($pipes[1]);
        $stderr = new ReadableResourceStream($pipes[2]);

        EventLoop::queue(static function () use ($id, $stdout, $stderr): void {
            await([async(self::readUnref(...), $stdout), async(self::readUnref(...), $stderr)]);
            self::reap($id);
        });
        return true;
    }
    /**
     * The descriptors of an IPC worker: pipes for its standard streams, and /dev/null in place of every other
     * descriptor open in this process.
     *
     * proc_open passes every open descriptor on to the child (PHP opens none with close-on-exec), and the worker
     * outlives the process that starts it. It would keep the listening sockets of this process (the metrics server:
     * "Address already in use" when it's started again; php-fpm's), its connections (the FastCGI request, the
     * Telegram connections, which a close here no longer ends), and its flock()ed files (the session lock).
     *
     * @internal
     *
     * @return array<int, list<string>>
     */
    public static function getWorkerDescriptors(): array
    {
        $descriptors = [
            ['pipe', 'r'],
            ['pipe', 'w'],
            ['pipe', 'w'],
        ];
        if (PHP_OS_FAMILY === 'Windows') {
            return $descriptors;
        }
        try {
            // On Linux, a link to /proc/self/fd
            $fds = scandir('/dev/fd');
        } catch (Throwable) {
            $fds = false;
        }
        foreach ($fds ?: [] as $fd) {
            // Includes the descriptor of the listed directory, closed since: /dev/null takes its place all the same
            if (ctype_digit($fd) && (int) $fd > 2) {
                $descriptors[(int) $fd] = ['null'];
            }
        }
        return $descriptors;
    }
    /**
     * Release the handle of a worker that closed its output, once it exits, without keeping the loop alive.
     */
    private static function reap(int $id): void
    {
        $check = static function (?string $watcher = null) use ($id): void {
            // proc_get_status() reaps the process once it exited
            if (!proc_get_status(self::$resources[$id])['running']) {
                unset(self::$resources[$id]);
                if ($watcher !== null) {
                    EventLoop::cancel($watcher);
                }
            }
        };
        $check();
        if (isset(self::$resources[$id])) {
            EventLoop::unreference(EventLoop::repeat(0.1, $check));
        }
    }
    /**
     * Unreference and read data from fd, logging results, then close it.
     */
    private static function readUnref(ReadableResourceStream $stream): void
    {
        $stream->unreference();
        $lastLine = '';
        try {
            while (($chunk = $stream->read()) !== null) {
                $chunk = explode("\n", str_replace(["\r", "\n\n"], "\n", $chunk));
                $lastLine .= array_shift($chunk);
                while ($chunk) {
                    Logger::log("Got message from worker: $lastLine");
                    $lastLine = array_shift($chunk);
                }
            }
        } catch (Throwable $e) {
            Logger::log("An error occurred while reading the process status: $e");
        } finally {
            Logger::log("Got final message from worker: $lastLine");
            $stream->close();
        }
    }
    /**
     * @psalm-external-mutation-free
     */
    private static function locateBinary(): string
    {
        $executable = strncasecmp(PHP_OS, 'WIN', 3) === 0 ? 'php.exe' : 'php';

        $paths = array_filter(explode(PATH_SEPARATOR, getenv('PATH') ?: ''));
        $paths[] = PHP_BINDIR;
        $paths = array_unique($paths);

        foreach ($paths as $path) {
            $path .= DIRECTORY_SEPARATOR.$executable;
            if (is_executable($path)) {
                return self::$binaryPath = $path;
            }
        }

        throw new Error('Could not locate PHP executable binary');
    }

    /**
     * Format PHP options.
     *
     * @return list<string>
     *
     * @psalm-pure
     */
    private static function formatOptions(array $options): array
    {
        $result = [];

        foreach ($options as $option => $value) {
            $result[] = \sprintf('-d%s=%s', $option, $value);
        }

        return $result;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Ipc\Runner;

use Amp\ByteStream\ReadableResourceStream;
use Amp\Parallel\Context\ContextException;
use AssertionError;
use danog\MadelineProto\Exception;
use danog\MadelineProto\Logger;
use danog\MadelineProto\Magic;
use Revolt\EventLoop;
use Throwable;

use const DEBUG_BACKTRACE_IGNORE_ARGS;
use const DIRECTORY_SEPARATOR;
use const PHP_URL_PATH;

/**
 * @internal
 */
final class WebRunner extends RunnerAbstract
{
    /** @var string|null Cached path to the runner script. */
    private static ?string $runPath = null;

    /**
     * Start.
     *
     * @param string $session Session path
     */
    #[\Override]
    public static function start(string $session, int $startupId): bool
    {
        if (!isset($_SERVER['SERVER_NAME']) || !$_SERVER['SERVER_NAME']) {
            return false;
        }

        if (!self::$runPath) {
            $absoluteRootDir = self::getAbsoluteRootDir();
            $runPath = self::getScriptPath($absoluteRootDir);

            if (substr($runPath, 0, \strlen($absoluteRootDir)) === $absoluteRootDir) { // Process runner is within readable document root
                self::$runPath = substr($runPath, \strlen($absoluteRootDir)-1);
            } else {
                throw new AssertionError("$runPath is not within absolute document root $absoluteRootDir!");
            }

            self::$runPath = str_replace(DIRECTORY_SEPARATOR, '/', self::$runPath);
            self::$runPath = str_replace('//', '/', self::$runPath);
        }

        $params = [
            'argv' => ['madeline-ipc', $session, $startupId],
            'cwd' => Magic::getcwd(),
            'MadelineSelfRestart' => 1,
        ];
        if (\function_exists('memprof_enabled') && memprof_enabled()) {
            $params['MEMPROF_PROFILE'] = '1';
        }

        self::selfStart(self::$runPath.'?'.http_build_query($params));

        return true;
    }

    private static ?string $absoluteRootDir = null;
    public static function getAbsoluteRootDir(): string
    {
        if (self::$absoluteRootDir) {
            return self::$absoluteRootDir;
        }

        $uri = parse_url('tcp://'.$_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI'], PHP_URL_PATH);
        if (substr($uri, -1) === '/') { // http://example.com/path/ (assumed index.php)
            $uri .= 'index'; // Add fake file name
        }
        $uri = str_replace('//', '/', $uri);

        $rootDir = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
        $rootDir = end($rootDir)['file'] ?? '';
        if (!$rootDir) {
            throw new ContextException('Could not get entry file!');
        }
        $rootDir = \dirname($rootDir).DIRECTORY_SEPARATOR;
        $uriDir = str_replace('/', DIRECTORY_SEPARATOR, \dirname($uri));
        if ($uriDir !== '/' && $uriDir !== '\\') {
            $uriDir .= DIRECTORY_SEPARATOR;
        }

        if (substr($rootDir, -\strlen($uriDir)) !== $uriDir) {
            throw new ContextException("Mismatch between absolute root dir ($rootDir) and URI dir ($uriDir)");
        }

        // Absolute root of (presumably) readable document root
        return self::$absoluteRootDir = substr($rootDir, 0, \strlen($rootDir)-\strlen($uriDir)).DIRECTORY_SEPARATOR;
    }

    public static function selfStart(string $uri): void
    {
        // Without Connection: close, the server keeps the connection open after the response, until its idle timeout
        $payload = "GET $uri HTTP/1.1\r\nHost: {$_SERVER['SERVER_NAME']}\r\nConnection: close\r\n\r\n";

        foreach (($_SERVER['HTTPS'] ?? 'off') === 'on' ? ['tls', 'tcp'] : ['tcp', 'tls'] as $proto) {
            foreach (array_unique([(int) $_SERVER['SERVER_PORT'], ($proto === 'tls' ? 443 : 80)]) as $port) {
                try {
                    $address = $proto.'://'.$_SERVER['SERVER_NAME'];
                    $res = fsockopen($address, (int) $port);
                    Logger::log("Successfully connected to {$address}:{$port}!");
                    Logger::log("Sending payload: $payload");
                    // We don't care for results or timeouts here, PHP doesn't count IOwait time as execution time anyway
                    // Technically should use amphp/socket, but I guess it's OK to not introduce another dependency just for a socket that will be used once.
                    fwrite($res, $payload);
                    // Closing the connection could abort the request of the worker: it's closed once the response
                    // ends (the worker exited, or another server answered).
                    EventLoop::queue(self::drain(...), new ReadableResourceStream($res));
                } catch (Throwable $e) {
                    Logger::log("Error while sending to {$address}:{$port}: $e");
                }
            }
        }
        if (!isset($res)) {
            throw new Exception('Could not connect to ourselves, please check the server configuration!');
        }
    }

    /**
     * Read the response to a self-request until it ends without keeping the loop alive, then close it.
     */
    private static function drain(ReadableResourceStream $stream): void
    {
        $stream->unreference();
        try {
            while ($stream->read() !== null);
        } catch (Throwable $e) {
            Logger::log("An error occurred while reading the response to a self-request: $e");
        } finally {
            $stream->close();
        }
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Ipc\Runner;

use danog\MadelineProto\API;
use danog\MadelineProto\Logger;
use Phar;

/**
 * @internal
 */
abstract class RunnerAbstract
{
    private const SCRIPT_PATH = __DIR__.'/entry.php';

    /** @var array<string, string> External version of SCRIPT_PATH if inside a PHAR. */
    private static array $pharScriptPath = [];

    protected static function getScriptPath(string $alternateTmpDir = ''): string
    {
        /**
         * If using madeline.php, simply return madeline.php path.
         */
        if (\defined('MADELINE_PHP')) {
            return \MADELINE_PHP;
        }
        if (!str_starts_with(self::SCRIPT_PATH, 'phar://')) {
            return self::SCRIPT_PATH;
        }
        $alternateTmpDir = $alternateTmpDir ?: sys_get_temp_dir();

        if (isset(self::$pharScriptPath[$alternateTmpDir])) {
            return self::$pharScriptPath[$alternateTmpDir];
        }
        $path = \dirname(self::SCRIPT_PATH);

        $contents = file_get_contents(self::SCRIPT_PATH);
        $contents = str_replace('__DIR__', var_export($path, true), $contents);
        $suffix = API::RELEASE.'___'.bin2hex(random_bytes(10));
        self::$pharScriptPath[$alternateTmpDir] = $scriptPath = $alternateTmpDir.'/madeline-ipc-'.$suffix.'.php';
        file_put_contents($scriptPath, $contents, LOCK_EX);
        Logger::log("Copied IPC bootstrap file to $scriptPath");

        register_shutdown_function(static function () use ($alternateTmpDir): void {
            try {
                unlink(self::$pharScriptPath[$alternateTmpDir]);
            } catch (\Throwable) {
            }
        });

        return $scriptPath;
    }
    /**
     * Runner.
     *
     * @param string $session Session path
     * @psalm-impure
     */
    abstract public static function start(string $session, int $startupId): bool;
}
<?php

declare(strict_types=1);

namespace danog\MadelineProto\Ipc\Runner;

/**
 * IPC server entry module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

use Amp\SignalException;
use danog\MadelineProto\API;
use danog\MadelineProto\Ipc\AlreadyServedException;
use danog\MadelineProto\Ipc\IpcState;
use danog\MadelineProto\Ipc\Server;
use danog\MadelineProto\Logger;
use danog\MadelineProto\Magic;
use danog\MadelineProto\SecurityException;
use danog\MadelineProto\SessionPaths;
use danog\MadelineProto\Settings\Ipc;
use Revolt\EventLoop\UncaughtThrowable;
use Webmozart\Assert\Assert;

(static function (): void {
    if (\defined('MADELINE_ENTRY')) {
        // Already called
        return;
    }
    \define('MADELINE_ENTRY', 1);
    if (!\defined('MADELINE_WORKER_TYPE')) {
        if (\count(debug_backtrace(0)) !== 1) {
            // We're not being included directly
            return;
        }
        $arguments = [];
        if (isset($GLOBALS['argv']) && !empty($GLOBALS['argv'])) {
            $arguments = \array_slice($GLOBALS['argv'], 1);
        } elseif (isset($_GET['argv']) && !empty($_GET['argv'])) {
            $arguments = $_GET['argv'];
        }
        if (\count($arguments) < 2) {
            trigger_error('Not enough arguments!', E_USER_ERROR);
            exit(1);
        }
        \define('MADELINE_WORKER_TYPE', array_shift($arguments));
        \define('MADELINE_WORKER_ARGS', $arguments);
    }

    // Not under a web server SAPI: the process belongs to the web server, which handles the signals itself
    if (\defined('SIGHUP') && PHP_SAPI === 'cli') {
        try {
            pcntl_signal(SIGHUP, static fn () => null);
        } catch (\Throwable $e) {
        }
    }
    if (!class_exists(API::class)) {
        $paths = [
            \dirname(__DIR__, 5).'/autoload.php',
            \dirname(__DIR__, 3).'/vendor/autoload.php',
            \dirname(__DIR__, 7).'/autoload.php',
            \dirname(__DIR__, 5).'/vendor/autoload.php',
        ];

        foreach ($paths as $path) {
            if (file_exists($path)) {
                $autoloadPath = $path;
                break;
            }
        }

        if (!isset($autoloadPath)) {
            trigger_error('Could not locate autoload.php in any of the following files: '.implode(', ', $paths), E_USER_ERROR);
            exit(1);
        }

        include $autoloadPath;
    }
    if (MADELINE_WORKER_TYPE === 'madeline-ipc') {
        $session = MADELINE_WORKER_ARGS[0];
        if (!file_exists($session)) {
            trigger_error("IPC session $session does not exist!", E_USER_ERROR);
            exit(1);
        }
        if (\function_exists('cli_set_process_title')) {
            @cli_set_process_title("MadelineProto worker $session");
        }
        if (\function_exists('posix_setsid')) {
            @posix_setsid();
        }
        if (isset($_GET['cwd'])) {
            @chdir($_GET['cwd']);
        }
        \define('MADELINE_WORKER', 1);

        $runnerId = MADELINE_WORKER_ARGS[1];
        Assert::numeric($runnerId);
        $runnerId = (int) $runnerId;

        try {
            Magic::start(light: false);
            Magic::$script_cwd = $_GET['cwd'] ?? Magic::getcwd();

            $session = new SessionPaths($session);
            $API = new API((string) $session, new Ipc);
            $API->initSelfRestart();
            $session->storeIpcState(new IpcState($runnerId));
            while (true) {
                try {
                    Server::waitShutdown();
                    Logger::log('A restart was triggered!', Logger::FATAL_ERROR);
                    return;
                } catch (\Throwable $e) {
                    if ($e instanceof UncaughtThrowable) {
                        $e = $e->getPrevious();
                    }
                    if ($e instanceof SecurityException || $e instanceof SignalException) {
                        throw $e;
                    }
                    Logger::log((string) $e, Logger::FATAL_ERROR);
                    $API->report("Surfaced: $e");
                }
            }
        } catch (AlreadyServedException $e) {
            // Started concurrently with the server that won the session lock: nothing to report
            Logger::log($e->getMessage().', exiting...');
        } catch (\Throwable $e) {
            echo "$e";
            echo 'Got exception in IPC server, exiting...';

            Logger::log("$e", Logger::FATAL_ERROR);
            Logger::log('Got exception in IPC server, exiting...', Logger::FATAL_ERROR);
            $ipc = $session->getIpcState();
            if (!($ipc && $ipc->getStartupId() === $runnerId && !$ipc->getException())
                && !(
                    isset($API)
                    && $API->getAuthorization() === API::LOGGED_OUT
                )
            ) {
                Logger::log('Reporting error!');
                $session->storeIpcState(new IpcState($runnerId, $e));
                Logger::log('Reported error!');
            } else {
                Logger::log('Not reporting error!');
            }
        }
    }
})();
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Ipc;

use danog\MadelineProto\Exception as MadelineProtoException;
use ReflectionClass;
use Throwable;

use function Amp\Parallel\Context\flattenThrowableBacktrace as ContextFlattenThrowableBacktrace;

/**
 * @internal
 */
final class ExitFailure
{
    /** @var class-string<\Throwable> */
    private string $type;

    private array $props;

    private ?self $previous = null;

    public function __construct(Throwable $exception)
    {
        $this->type = $exception::class;
        $props = [];
        $f = new ReflectionClass($exception);
        do {
            foreach ($f->getProperties() as $prop) {
                if ($prop->isStatic()) {
                    continue;
                }
                $value = $prop->getValue($exception);
                if ($prop->getName() === 'trace' && \is_array($value)) {
                    $value = ContextFlattenThrowableBacktrace($exception);
                } elseif ($prop->getName() === 'previous' && $value instanceof \Throwable) {
                    $value = new self($value);
                }
                $props[$f->getName()][$prop->getName()] = $value;
            }
        } while ($f = $f->getParentClass());

        $this->props = $props;
    }

    public function getException(): \Throwable
    {
        $prev = new MadelineProtoException("Client backtrace");

        $refl = new \ReflectionClass($this->type);
        $exception = $refl->newInstanceWithoutConstructor();

        $props = $this->props;
        $props[\Exception::class]['previous'] = $this->previous?->getException();

        foreach ($props as $class => $subprops) {
            $class = new ReflectionClass($class);
            foreach ($class->getProperties() as $prop) {
                $key = $prop->getName();
                if (!\array_key_exists($key, $subprops)) {
                    continue;
                }
                $value = $subprops[$key];
                if ($key === 'previous') {
                    if ($value instanceof self) {
                        $value = $value->getException();
                    } elseif ($value === null) {
                        $value = $prev;
                    }
                } elseif ($key === 'tlTrace') {
                    $value = "$value\n\nClient TL trace:".$prev->getTLTrace();
                }
                try {
                    $prop->setValue($exception, $value);
                } catch (\Throwable) {
                }
            }
        }

        return $exception;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Ipc\Wrapper;

/**
 * @internal
 */
final class SeekableWritableStream extends WritableStream
{
    use SeekableTrait;
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Ipc\Wrapper;

/**
 * Sent back to an IPC client in place of the proxy of one of its cancellations, which can't be
 * serialized: the client resolves it to the original cancellation.
 *
 * @internal
 */
final class CancellationReference
{
    /**
     * @param int $id ID of a callback of the wrapped cancellation.
     *
     * @psalm-mutation-free
     */
    public function __construct(public readonly int $id)
    {
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Ipc\Wrapper;

use danog\MadelineProto\Ipc\Wrapper;

/**
 * @internal
 */
trait WrapMethodTrait
{
    abstract public function __call($name, $args);
    public function wrap(...$args)
    {
        $new = Wrapper::create($args, $this->session->getIpcCallbackPath(), $this->logger);
        foreach ($args as &$arg) {
            $new->wrap($arg);
        }
        return $this->__call(__FUNCTION__, $new);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Ipc\Wrapper;

use Amp\ByteStream\ClosedException;
use Amp\ByteStream\ReadableStream as AmpReadableStream;
use Amp\ByteStream\ReadableStreamIteratorAggregate;
use Amp\Cancellation;
use IteratorAggregate;
use Revolt\EventLoop;

use function Amp\async;

/**
 * @internal
 */
class ReadableStream extends Obj implements AmpReadableStream, IteratorAggregate
{
    use ClosableTrait;
    use ReadableStreamIteratorAggregate;

    #[\Override]
    public function read(?Cancellation $cancellation = null): ?string
    {
        return async(function (): ?string {
            $result = null;
            try {
                $result = $this->__call('read');
            } catch (ClosedException $e) {
                if ($this->closeCallbacks) {
                    array_map(EventLoop::queue(...), $this->closeCallbacks);
                    $this->closeCallbacks = [];
                }
                throw $e;
            }
            if ($result === null) {
                if ($this->closeCallbacks) {
                    array_map(EventLoop::queue(...), $this->closeCallbacks);
                    $this->closeCallbacks = [];
                }
            }
            return $result;
        })->await($cancellation);
    }

    /**
     * @return bool A stream may become unreadable if the underlying source is closed or lost.
     */
    #[\Override]
    public function isReadable(): bool
    {
        return $this->__call('isReadable');
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Ipc\Wrapper;

use danog\MadelineProto\FileCallbackInterface;

/**
 * @internal
 *
 * @implements FileCallbackInterface<mixed>
 */
final class FileCallback extends Obj implements FileCallbackInterface
{
    #[\Override]
    public function getFile(): mixed
    {
        return $this->__call('getFile');
    }
    /**
     * Invoke callback.
     *
     * @param float $percent Percent
     * @param float $speed   Speed in mbps
     * @param float $time    Time
     */
    #[\Override]
    public function __invoke(float $percent, float $speed, float $time)
    {
        return $this->__call('__invoke', [$percent, $speed, $time]);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Ipc\Wrapper;

use Amp\ByteStream\WritableStream as AmpWritableStream;

/**
 * @internal
 */
class WritableStream extends Obj implements AmpWritableStream
{
    use ClosableTrait;

    #[\Override]
    public function write(string $data): void
    {
        $this->__call('write', [$data]);
    }
    #[\Override]
    public function isWritable(): bool
    {
        return $this->__call('isWritable');
    }

    #[\Override]
    public function end(string $finalData = ""): void
    {
        $this->__call('end', [$finalData]);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Ipc\Wrapper;

use Amp\Cancellation as AmpCancellation;
use Amp\CancelledException;
use danog\MadelineProto\Ipc\ClientAbstract;

/**
 * @internal
 */
final class Cancellation implements AmpCancellation
{
    /**
     * @var array<string, true> $handlers
     */
    private array $handlers = [];
    private CancellationInner $inner;
    /**
     * Constructor.
     *
     * @param array<string, int> $methods
     */
    public function __construct(ClientAbstract $wrapper, array $methods)
    {
        $this->inner = new CancellationInner($wrapper, $methods);
    }

    /**
     * Get what to send back to the client in place of this cancellation.
     *
     * @internal
     *
     * @psalm-mutation-free
     */
    public function getReference(): CancellationReference
    {
        return new CancellationReference($this->inner->getMethodId('wait'));
    }

    /**
     * Subscribes a new handler to be invoked on a cancellation request.
     *
     * This handler might be invoked immediately in case the cancellation has already been requested. Any unhandled
     * exceptions will be thrown into the event loop.
     *
     * @param \Closure(CancelledException) $callback Callback to be invoked on a cancellation request. Will receive a
     *                                               `CancelledException` as first argument that may be used to fail the operation.
     *
     * @return string Identifier that can be used to cancel the subscription.
     */
    #[\Override]
    public function subscribe(\Closure $callback): string
    {
        $id = $this->inner->subscribe($callback);
        $this->handlers[$id] = true;
        return $id;
    }

    /**
     * Unsubscribes a previously registered handler.
     *
     * The handler will no longer be called as long as this method isn't invoked from a subscribed callback.
     *
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function unsubscribe(string $id): void
    {
        unset($this->handlers[$id]);
        $this->inner->unsubscribe($id);
    }

    /**
     * Returns whether cancellation has been requested yet.
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function isRequested(): bool
    {
        return $this->inner->isRequested();
    }

    /**
     * Throws the `CancelledException` if cancellation has been requested, otherwise does nothing.
     *
     * @throws CancelledException
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function throwIfRequested(): void
    {
        $this->inner->throwIfRequested();
    }

    /**
     * @psalm-external-mutation-free
     */
    public function __destruct()
    {
        foreach ($this->handlers as $handler => $_) {
            $this->inner->unsubscribe($handler);
        }
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Ipc\Wrapper;

/**
 * @internal
 */
final class SeekableReadableStream extends ReadableStream
{
    use SeekableTrait;
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Ipc\Wrapper;

use Amp\Cancellation as AmpCancellation;
use Amp\CancelledException;
use Closure;
use danog\MadelineProto\Ipc\ClientAbstract;
use Revolt\EventLoop;
use Throwable;

/**
 * @internal
 */
final class CancellationInner extends Obj implements AmpCancellation
{
    private ?CancelledException $exception = null;
    /**
     * @var array<string, Closure(CancelledException)>
     */
    private array $callbacks = [];
    private string $nextId = 'a';

    /**
     * Constructor.
     *
     * @param array<string, int> $methods
     */
    public function __construct(ClientAbstract $wrapper, array $methods)
    {
        parent::__construct($wrapper, $methods);
        // Cancellation methods must never suspend: amp calls them between fetching a fiber's suspension
        // and suspending it (Future::await(), FutureIterator::consume()).
        // So instead of querying the remote cancellation, we get notified by a single long-running wait call.
        EventLoop::queue(function (): void {
            try {
                $this->__call('wait');
            } catch (CancelledException $e) {
                $this->exception = $e;
                $callbacks = $this->callbacks;
                $this->callbacks = [];
                foreach ($callbacks as $callback) {
                    EventLoop::queue(static function () use ($callback, $e): void {
                        $callback($e);
                    });
                }
            } catch (Throwable) {
            }
        });
    }

    /**
     * Subscribes a new handler to be invoked on a cancellation request.
     *
     * This handler might be invoked immediately in case the cancellation has already been requested. Any unhandled
     * exceptions will be thrown into the event loop.
     *
     * @param \Closure(CancelledException) $callback Callback to be invoked on a cancellation request. Will receive a
     *                                               `CancelledException` as first argument that may be used to fail the operation.
     *
     * @return string Identifier that can be used to cancel the subscription.
     */
    #[\Override]
    public function subscribe(\Closure $callback): string
    {
        $id = $this->nextId;
        $this->nextId = str_increment($id);
        $exception = $this->exception;
        if ($exception !== null) {
            EventLoop::queue(static function () use ($callback, $exception): void {
                $callback($exception);
            });
        } else {
            $this->callbacks[$id] = $callback;
        }
        return $id;
    }

    /**
     * Unsubscribes a previously registered handler.
     *
     * The handler will no longer be called as long as this method isn't invoked from a subscribed callback.
     *
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function unsubscribe(string $id): void
    {
        unset($this->callbacks[$id]);
    }

    /**
     * Returns whether cancellation has been requested yet.
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function isRequested(): bool
    {
        return $this->exception !== null;
    }

    /**
     * Throws the `CancelledException` if cancellation has been requested, otherwise does nothing.
     *
     * @throws CancelledException
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function throwIfRequested(): void
    {
        if ($this->exception !== null) {
            throw $this->exception;
        }
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Ipc\Wrapper;

use Amp\File\Whence;

/**
 * @internal
 */
trait SeekableTrait
{
    /**
    * Set the handle's internal pointer position.
    */
    public function seek(int $position, Whence $whence = Whence::Current): int
    {
        return $this->__call('seek', [$position, $whence]);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Ipc\Wrapper;

use Amp\Cancellation as AmpCancellation;
use Amp\CancelledException;
use Amp\DeferredFuture;

/**
 * @internal
 */
final class WrappedCancellation
{
    /**
     * @var array<int, DeferredFuture>
     */
    private array $waiting = [];

    /**
     * Whether the IPC connection was closed.
     */
    private bool $disconnected = false;

    /**
     * @psalm-mutation-free
     */
    public function __construct(
        private readonly AmpCancellation $cancellation
    ) {
    }

    /**
     * Get the wrapped cancellation.
     *
     * @internal
     *
     * @psalm-mutation-free
     */
    public function getCancellation(): AmpCancellation
    {
        return $this->cancellation;
    }

    /**
     * Waits until cancellation is requested, throwing the `CancelledException`.
     *
     * Returns normally if the IPC connection is closed first.
     */
    public function wait(): void
    {
        if ($this->disconnected) {
            // A wait request that was received right before the connection was closed.
            return;
        }
        $deferred = new DeferredFuture;
        $this->waiting[spl_object_id($deferred)] = $deferred;
        $id = $this->cancellation->subscribe(static function (CancelledException $e) use ($deferred): void {
            if (!$deferred->isComplete()) {
                $deferred->error($e);
            }
        });
        try {
            $deferred->getFuture()->await();
        } finally {
            unset($this->waiting[spl_object_id($deferred)]);
            $this->cancellation->unsubscribe($id);
        }
    }

    /**
     * Stops all pending waits, called when the IPC connection is closed.
     *
     * @internal
     */
    public function disconnect(): void
    {
        $this->disconnected = true;
        $waiting = $this->waiting;
        $this->waiting = [];
        foreach ($waiting as $deferred) {
            // Might have been failed by the cancellation, with the waiting fiber not resumed yet.
            if (!$deferred->isComplete()) {
                $deferred->complete();
            }
        }
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Ipc\Wrapper;

use danog\MadelineProto\Ipc\ClientAbstract;
use danog\MadelineProto\Ipc\Wrapper;

/**
 * Generic callback wrapper object.
 *
 * @internal
 */
abstract class Obj
{
    /**
     * Constructor.
     *
     * @param array<string, int> $methods
     *
     * @psalm-mutation-free
     */
    public function __construct(private ClientAbstract $wrapper, private array $methods)
    {
    }
    /**
     * Call method.
     */
    public function __call(string $name, array $arguments = []): mixed
    {
        return $this->wrapper->__call($this->methods[$name], $arguments);
    }

    /**
     * Get the ID of the callback of a method.
     *
     * @internal
     *
     * @psalm-mutation-free
     */
    public function getMethodId(string $name): int
    {
        return $this->methods[$name];
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Ipc\Wrapper;

use Revolt\EventLoop;

/**
 * @internal
 */
trait ClosableTrait
{
    /**
     * @var list<Closure(): void>
     */
    private array $closeCallbacks = [];
    /**
     * Closes the resource, marking it as unusable.
     * Whether pending operations are aborted or not is implementation dependent.
     */
    public function close(): void
    {
        if ($this->closeCallbacks) {
            array_map(EventLoop::queue(...), $this->closeCallbacks);
            $this->closeCallbacks = [];
        }
        $this->__call('close');
    }

    /**
     * Returns whether this resource has been closed.
     *
     * @return bool true if closed, otherwise false
     */
    public function isClosed(): bool
    {
        return $this->__call('isClosed');
    }

    /**
     * Registers a callback that is invoked when this resource is closed.
     *
     * @param \Closure():void $onClose
     *
     * @psalm-external-mutation-free
     */
    public function onClose(\Closure $onClose): void
    {
        $this->closeCallbacks []= $onClose;
    }

    final public function __destruct()
    {
        EventLoop::queue($this->close(...));
    }
}
<?php

declare(strict_types=1);

/**
 * API wrapper module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Ipc;

use Amp\CancelledException;
use Amp\DeferredCancellation;
use Amp\DeferredFuture;
use Amp\Future;
use Amp\Ipc\Sync\ChannelException;
use Amp\Ipc\Sync\ChannelledSocket;
use danog\MadelineProto\Logger;
use danog\MadelineProto\Serialization;
use Throwable;

use const DEBUG_BACKTRACE_IGNORE_ARGS;

use function Amp\delay;

/**
 * IPC client.
 *
 * @internal
 */
abstract class ClientAbstract
{
    /**
     * Delay before the second reconnection in a row that didn't get any answer, in seconds: it doubles with each
     * further one, up to MAX_RECONNECT_DELAY.
     */
    private const RECONNECT_DELAY = 1.0;
    /**
     * Maximum delay between reconnections, in seconds.
     */
    private const MAX_RECONNECT_DELAY = 30.0;
    /**
     * An idle connection that lasted this long (in seconds) was a working one: losing it doesn't delay reconnecting.
     */
    private const STABLE_CONNECTION = 10.0;
    /**
     * Number of connections a call is sent on without getting an answer before giving up.
     */
    private const MAX_SENDS = 5;
    /**
     * IPC server socket.
     */
    protected ChannelledSocket $server;
    protected ?Future $serverFuture = null;

    private int $id = 0;
    /**
     * Requests promise array.
     *
     * @var array<int, list{string|int, array|Wrapper, DeferredFuture, int}> Function, arguments, result, number of connections it was sent on
     */
    private array $requests = [];
    /**
     * Cancels the reconnection in progress, if any.
     */
    private ?DeferredCancellation $reconnecting = null;
    /**
     * Whether to run loop.
     */
    protected bool $run = true;
    /**
     * Logger instance.
     */
    public Logger $logger;

    /**
     * @psalm-mutation-free
     */
    protected function __construct()
    {
    }
    /**
     * Logger.
     *
     * @param mixed  $param Parameter
     * @param int    $level Logging level
     * @param string $file  File where the message originated
     */
    public function logger(mixed $param, int $level = Logger::NOTICE, string $file = ''): void
    {
        if ($file === '') {
            $file = basename(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1)[0]['file'], '.php');
        }
        isset($this->logger) ? $this->logger->logger($param, $level, $file) : Logger::$default->logger($param, $level, $file);
    }
    /**
     * Main loop.
     */
    protected function loopInternal(): void
    {
        // Reconnections in a row that didn't get any answer
        $failures = 0;
        $connectedAt = microtime(true);
        $failure = null;
        do {
            $answered = false;
            while (true) {
                $payload = null;
                try {
                    $payload = $this->server->receive();
                } catch (Throwable $e) {
                    Logger::log("Got exception while receiving in IPC client: $e");
                }
                if (!$payload) {
                    Logger::log("Disconnected from IPC server!");
                    break;
                }
                $answered = true;
                [$id, $payload] = $payload;
                if (!isset($this->requests[$id])) {
                    Logger::log("Got response for non-existing ID $id!");
                } else {
                    $promise = $this->requests[$id][2];
                    unset($this->requests[$id]);
                    if ($payload instanceof ExitFailure) {
                        $promise->error($payload->getException());
                    } else {
                        $promise->complete($payload);
                    }
                    unset($promise);
                }
            }
            if ($this->run) {
                if ($this instanceof Client) {
                    $this->logger('Reconnecting to IPC server!');
                    $f = new DeferredFuture;
                    $this->serverFuture = $f->getFuture();
                    try {
                        $this->server->disconnect();
                    } catch (Throwable $e) {
                    }
                    // A server that answered, or an idle connection that lasted, was a working one. Otherwise the
                    // server dropped us (it's shutting down with its socket still bound, it fails to handle our
                    // calls, ...): reconnecting right away would spin on it, so wait longer and longer.
                    if ($answered || (!$this->requests && microtime(true) - $connectedAt >= self::STABLE_CONNECTION)) {
                        $failures = 0;
                    } else {
                        $failures++;
                    }
                    $requests = [];
                    $this->reconnecting = new DeferredCancellation;
                    try {
                        $cancellation = $this->reconnecting->getCancellation();
                        // Calls sent on several connections in a row, each dropped before answering them, would be
                        // sent again forever.
                        $calls = [];
                        foreach ($this->requests as [$function, , , $sends]) {
                            if ($sends >= self::MAX_SENDS) {
                                $calls[] = $function;
                            }
                        }
                        if ($calls) {
                            throw new ChannelException("The IPC server of {$this->session} dropped the connection ".self::MAX_SENDS." times in a row without answering ".implode(', ', array_unique($calls)).", giving up: please check the logs of the IPC server (or of the instance running the session)!");
                        }
                        if ($failures > 1) {
                            $wait = min(self::RECONNECT_DELAY * 2 ** ($failures - 2), self::MAX_RECONNECT_DELAY);
                            $this->logger("The IPC server dropped the connection without answering, reconnecting in $wait seconds...", Logger::WARNING);
                            delay($wait, true, $cancellation);
                        }
                        // Connects to the server of the session if there's one (or an instance loading it), only
                        // starting one if there's none.
                        [$server] = Serialization::reconnect($this->session, $cancellation);
                        if (!$server instanceof ChannelledSocket) {
                            throw $server instanceof Throwable ? $server : new ChannelException("Could not reconnect to the IPC server, please check the logs!");
                        }
                        $this->server = $server;
                        $connectedAt = microtime(true);
                        $this->logger('Reconnected to IPC server!');
                        $requests = $this->requests;
                        $this->requests = [];
                        $this->id = 0;
                        while ($requests) {
                            [$function, $arguments, $deferred, $sends] = array_shift($requests);
                            if ($arguments instanceof Wrapper) {
                                // Its callbacks were connected to the previous server, which can't call them anymore.
                                // (Interrupted uploads and sends are resumed by the server on its own.)
                                $arguments->disconnect();
                                $deferred->error(new ChannelException("Disconnected from IPC server while calling $function!"));
                                continue;
                            }
                            $id = $this->id++;
                            $this->requests[$id] = [$function, $arguments, $deferred, $sends + 1];
                            try {
                                $this->server->send([$function, $arguments]);
                            } catch (Throwable $e) {
                                // Dropped already: the calls are sent again on the next connection (or failed)
                                $this->logger("Could not send $function to the IPC server: $e");
                                foreach ($requests as $request) {
                                    $this->requests[$this->id++] = $request;
                                }
                                break;
                            }
                        }
                        $f->complete();
                        $this->serverFuture = null;
                        $this->logger('Resumed IPC queries!');
                    } catch (Throwable $e) {
                        if ($e instanceof CancelledException && !$this->run) {
                            $e = new ChannelException("Disconnected from the IPC server while reconnecting to it");
                        } else {
                            $this->logger("Could not reconnect to IPC server: $e", Logger::FATAL_ERROR);
                        }
                        $failure = $e;
                        // The requests that were not sent again yet are failed below, too
                        /** @var array<int, list{string|int, array|Wrapper, DeferredFuture, int}> $requests */
                        foreach ($requests as $request) {
                            $this->requests[] = $request;
                        }
                        // Fail future calls, and pending calls below, instead of leaving them hanging
                        $f->getFuture()->ignore();
                        $f->error($e);
                        $this->run = false;
                    } finally {
                        $this->reconnecting = null;
                    }
                } else {
                    try {
                        $this->server->disconnect();
                    } catch (Throwable $e) {
                    }
                    break;
                }
            }
        } while ($this->run);
        $requests = $this->requests;
        $this->requests = [];
        foreach ($requests as [$function, , $deferred]) {
            if (!$this->run && $this instanceof Wrapper) {
                // Callback wrappers are closed once their IPC call returns: calls still pending at that point
                // (late progress notifications, cancellation waits) belong to nobody, so they're dropped.
                $deferred->complete(null);
            } else {
                $deferred->error(new ChannelException("Disconnected from IPC server while calling $function!", previous: $failure));
            }
        }
    }
    /**
     * Disconnect cleanly from main instance.
     */
    public function disconnect(): void
    {
        $this->run = false;
        $this->reconnecting?->cancel();
        $this->server->disconnect();
        foreach ($this->requests as [, $args, $promise]) {
            if ($args instanceof Wrapper) {
                $args->disconnect();
            }
        }
    }
    /**
     * Call function.
     *
     * @param string|int    $function  Function name
     * @param array|Wrapper $arguments Arguments
     */
    public function __call(string|int $function, array|Wrapper $arguments)
    {
        if (!$this->run && $this instanceof Wrapper) {
            // See loopInternal().
            return null;
        }
        $this->serverFuture?->await();

        $deferred = new DeferredFuture;
        $id = $this->id++;
        $this->requests[$id] = [$function, $arguments, $deferred, 1];
        try {
            $this->server->send([$function, $arguments]);
        } catch (Throwable $e) {
            // The connection is lost: the main loop sends the call again once reconnected, or fails it.
            // (Throwing here left the call pending, its result unhandled.)
            $this->logger("Could not send $function to the IPC server: $e");
        }
        $result = $deferred->getFuture()->await();
        if ($result instanceof ExitFailure) {
            throw $result->getException();
        }
        return $result;
    }
}
<?php

declare(strict_types=1);

/**
 * API wrapper module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Ipc;

use Amp\ByteStream\ReadableStream;
use Amp\Cancellation;
use Amp\DeferredCancellation;
use Amp\Ipc\Sync\ChannelledSocket;
use danog\MadelineProto\BotApiFileId;
use danog\MadelineProto\EventHandler\Media;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\Exception;
use danog\MadelineProto\FileCallbackInterface;
use danog\MadelineProto\LocalFile;
use danog\MadelineProto\Logger;
use danog\MadelineProto\MediaDestination;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\MTProtoTools\FilesLogic;
use danog\MadelineProto\ParseMode;
use danog\MadelineProto\RemoteUrl;
use danog\MadelineProto\ResumableStream;
use danog\MadelineProto\SessionPaths;
use danog\MadelineProto\Tools;
use danog\MadelineProto\Wrappers\Start;
use Revolt\EventLoop;
use Throwable;

use const FILTER_VALIDATE_URL;

use function Amp\async;
use function Amp\File\exists;

/**
 * IPC client.
 *
 * @mixin MTProto
 *
 * @internal
 */
final class Client extends ClientAbstract
{
    use Start;
    use FilesLogic {
        upload as private uploadLocally;
        uploadFromStream as private uploadFromStreamLocally;
        sendMedia as private sendMediaLocally;
    }

    /**
     * Instances.
     */
    private static array $instances = [];

    /**
     * Returns an instance of a client by session name.
     *
     * @psalm-external-mutation-free
     */
    public static function giveInstanceBySession(string $session): Client|MTProto
    {
        return self::$instances[$session] ?? MTProto::giveInstanceBySession($session);
    }

    /**
     * Session.
     */
    protected SessionPaths $session;
    /**
     * Constructor function.
     *
     * @param SessionPaths $session Session paths
     * @param Logger       $logger  Logger
     */
    public function __construct(ChannelledSocket $server, SessionPaths $session, Logger $logger)
    {
        $this->logger = $logger;
        $this->server = $server;
        $this->session = $session;
        self::$instances[$session->getSessionDirectoryPath()] = $this;
        EventLoop::queue($this->loopInternal(...));
    }

    /**
     * Call function.
     *
     * @param string|int    $function  Function name
     * @param array|Wrapper $arguments Arguments
     */
    #[\Override]
    public function __call(string|int $function, array|Wrapper $arguments)
    {
        if (\is_array($arguments) && $arguments) {
            foreach ($arguments as &$arg) {
                if ($arg instanceof Cancellation) {
                    break;
                }
            }
            if ($arg instanceof Cancellation) {
                $wrapper = Wrapper::create($arguments, $this->session, $this->logger);
                $wrapper->wrap($arg);
                unset($arg, $arguments);
                $arguments = $wrapper;
            }
        }
        return parent::__call($function, $arguments);
    }

    /** @internal */
    public function getSession(): SessionPaths
    {
        return $this->session;
    }
    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function getSessionName(): string
    {
        return $this->session->getSessionDirectoryPath();
    }
    /**
     * Unreference.
     */
    public function unreference(): void
    {
        try {
            $this->disconnect();
        } catch (Throwable $e) {
            $this->logger("An error occurred while disconnecting the client: $e");
        }
        if (isset(self::$instances[$this->session->getSessionDirectoryPath()])) {
            unset(self::$instances[$this->session->getSessionDirectoryPath()]);
        }
    }
    /**
     * Stop IPC server instance.
     *
     * @internal
     */
    public function stopIpcServer(): void
    {
        $this->run = false;
        $this->serverFuture?->await();
        $this->server->send(Server::SHUTDOWN);
    }
    /**
     * Whether we're an IPC client instance.
     *
     * @psalm-pure
     */
    public function isIpc(): bool
    {
        return true;
    }

    /**
     * @psalm-mutation-free
     */
    public function getLogger(): Logger
    {
        return $this->logger;
    }

    /** @internal */
    public function getQrLoginCancellation(): Cancellation
    {
        $c = new DeferredCancellation;
        async($this->__call(...), 'waitQrLogin', [])->map($c->cancel(...));
        return $c->getCancellation();
    }
    /**
     * Upload file from URL.
     *
     * @param string|FileCallbackInterface $url       URL of file
     * @param integer                      $size      Size of file
     * @param string                       $fileName  File name
     * @param callable                     $cb        Callback
     * @param boolean                      $encrypted Whether to encrypt file for secret chats
     */
    public function uploadFromUrl(string|FileCallbackInterface $url, int $size = 0, string $fileName = '', ?callable $cb = null, bool $encrypted = false, ?Cancellation $cancellation = null)
    {
        if (\is_object($url) && $url instanceof FileCallbackInterface) {
            $cb = $url;
            $url = $url->getFile();
        }
        $params = [$url, $size, $fileName, &$cb, $encrypted, &$cancellation];
        $wrapper = Wrapper::create($params, $this->session, $this->logger);
        $wrapper->wrap($cb, false);
        $wrapper->wrap($cancellation);
        return $this->__call('uploadFromUrl', $wrapper);
    }

    /**
     * Upload file.
     *
     * Everything but raw streams is uploaded by the IPC server, which can resume interrupted uploads.
     *
     * @param FileCallbackInterface|LocalFile|RemoteUrl|BotApiFileId|ReadableStream|string|array|resource $file      File, URL or Telegram file to upload
     * @param string                                                                       $fileName  File name
     * @param callable                                                                     $cb        Callback
     * @param boolean                                                                      $encrypted Whether to encrypt file for secret chats
     *
     * @return array InputFile constructor
     */
    public function upload($file, string $fileName = '', ?callable $cb = null, bool $encrypted = false, ?Cancellation $cancellation = null): array
    {
        if ($file instanceof FileCallbackInterface) {
            $cb = $file;

            $file = $file->getFile();
        }
        if (\is_resource($file) || ($file instanceof ReadableStream && !$file instanceof ResumableStream)) {
            /** @psalm-suppress MixedArgument */
            return $this->uploadLocally($file, $fileName, $cb, $encrypted, $cancellation);
        }
        // The IPC server might have a different working directory.
        if ($file instanceof LocalFile) {
            $file = new LocalFile(Tools::absolute($file->file));
        } elseif (\is_string($file) && !filter_var($file, FILTER_VALIDATE_URL) && exists(Tools::absolute($file))) {
            $file = Tools::absolute($file);
        }
        $params = [$file, $fileName, &$cb, $encrypted, &$cancellation];
        if ($cb === null && $cancellation === null) {
            // Without callbacks, the call can be sent again if the IPC server restarts.
            /** @var array */
            return $this->__call('upload', $params);
        }
        $wrapper = Wrapper::create($params, $this->session, $this->logger);
        $wrapper->wrap($cb, false);
        $wrapper->wrap($cancellation);
        /** @var array */
        return $this->__call('upload', $wrapper);
    }

    /**
     * Sends a media.
     *
     * Unless the file is a raw stream, the IPC server sends it, resuming the upload and the send if they're interrupted by a restart.
     *
     * @param class-string<Media> $type
     *
     * @internal
     */
    public function sendMedia(
        string $type,
        int|string|null $peer,
        ?string $mimeType,
        Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file,
        Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream|null $thumb,
        array $attributesOrig,
        string $caption,
        ParseMode $parseMode,
        ?callable $callback,
        ?string $fileName,
        ?int $ttl,
        bool $spoiler,
        ?int $replyToMsgId,
        ?int $topMsgId,
        ?array $replyMarkup,
        int|string|null $sendAs,
        ?int $scheduleDate,
        bool $silent,
        bool $noForwards,
        bool $background,
        bool $clearDraft,
        bool $updateStickersetsOrder,
        bool $forceResend,
        ?Cancellation $cancellation,
        bool $uploadOnly,
        ?int $randomId = null,
    ): Message|Media {
        /** @var array<string, mixed> */
        $args = get_defined_vars();
        foreach ([$file, $thumb] as $f) {
            if ($f instanceof ReadableStream && !$f instanceof ResumableStream) {
                /** @psalm-suppress MixedArgument */
                return $this->sendMediaLocally(...$args);
            }
        }
        if ($file instanceof LocalFile) {
            $args['file'] = new LocalFile(Tools::absolute($file->file));
        }
        if ($thumb instanceof LocalFile) {
            $args['thumb'] = new LocalFile(Tools::absolute($thumb->file));
        }
        if (!$uploadOnly) {
            // Chosen here, so that if this call is sent again after a restart of the IPC server
            // (which also resumes it on its own), the message isn't sent twice.
            $args['randomId'] = $randomId ?? Tools::randomInt();
        }
        if ($callback === null && $cancellation === null) {
            // Without callbacks, the call can be sent again if the IPC server restarts.
            /** @var Message|Media */
            return $this->__call('sendMedia', $args);
        }
        $callback = &$args['callback'];
        $cancellation = &$args['cancellation'];
        $wrapper = Wrapper::create($args, $this->session, $this->logger);
        $wrapper->wrap($callback, false);
        $wrapper->wrap($cancellation);
        /** @var Message|Media */
        return $this->__call('sendMedia', $wrapper);
    }

    /**
     * Upload file from stream.
     *
     * Resumable streams are uploaded by the IPC server, which can resume interrupted uploads.
     *
     * @param mixed    $stream    PHP resource or AMPHP async stream
     * @param integer  $size      File size
     * @param string   $mime      Mime type
     * @param string   $fileName  File name
     * @param callable $cb        Callback
     * @param boolean  $encrypted Whether to encrypt file for secret chats
     *
     * @return array InputFile constructor
     */
    public function uploadFromStream(mixed $stream, int $size = 0, ?string $mime = null, string $fileName = '', ?callable $cb = null, bool $encrypted = false, ?Cancellation $cancellation = null): array
    {
        if ($stream instanceof FileCallbackInterface) {
            $cb = $stream;

            $stream = $stream->getFile();
        }
        if (!$stream instanceof ResumableStream) {
            return $this->uploadFromStreamLocally($stream, $size, $mime, $fileName, $cb, $encrypted, $cancellation);
        }
        $params = [$stream, $size, $mime, $fileName, &$cb, $encrypted, &$cancellation];
        if ($cb === null && $cancellation === null) {
            // Without callbacks, the call can be sent again if the IPC server restarts.
            /** @var array */
            return $this->__call('uploadFromStream', $params);
        }
        $wrapper = Wrapper::create($params, $this->session, $this->logger);
        $wrapper->wrap($cb, false);
        $wrapper->wrap($cancellation);
        /** @var array */
        return $this->__call('uploadFromStream', $wrapper);
    }

    /**
     * Play file in call.
     */
    public function callPlay(int $id, LocalFile|RemoteUrl|ReadableStream $file, MediaDestination $dest = MediaDestination::Camera): void
    {
        $this->callPlayInternal('call', $id, $file, $dest, false);
    }

    /**
     * Play file in call, blocking until the file has finished playing if a stream is provided.
     */
    public function callPlayBlocking(int $id, LocalFile|RemoteUrl|ReadableStream $file, MediaDestination $dest = MediaDestination::Camera): void
    {
        $this->callPlayInternal('call', $id, $file, $dest, true);
    }

    /**
     * Play files on hold in call.
     */
    public function callPlayOnHold(int $id, MediaDestination $dest = MediaDestination::Camera, LocalFile|RemoteUrl|ReadableStream ...$files): void
    {
        $this->callPlayOnHoldInternal('call', $id, $dest, $files);
    }

    /**
     * Play file in group call.
     */
    public function groupCallPlay(int $id, LocalFile|RemoteUrl|ReadableStream $file, MediaDestination $dest = MediaDestination::Camera): void
    {
        $this->callPlayInternal('groupCall', $id, $file, $dest, false);
    }

    /**
     * Play file in group call, blocking until the file has finished playing if a stream is provided.
     */
    public function groupCallPlayBlocking(int $id, LocalFile|RemoteUrl|ReadableStream $file, MediaDestination $dest = MediaDestination::Camera): void
    {
        $this->callPlayInternal('groupCall', $id, $file, $dest, true);
    }

    /**
     * Play files on hold in group call.
     */
    public function groupCallPlayOnHold(int $id, MediaDestination $dest = MediaDestination::Camera, LocalFile|RemoteUrl|ReadableStream ...$files): void
    {
        $this->callPlayOnHoldInternal('groupCall', $id, $dest, $files);
    }

    /**
     * Play file in conference call.
     */
    public function conferenceCallPlay(int $id, LocalFile|RemoteUrl|ReadableStream $file, MediaDestination $dest = MediaDestination::Camera): void
    {
        $this->callPlayInternal('conferenceCall', $id, $file, $dest, false);
    }

    /**
     * Play file in conference call, blocking until the file has finished playing if a stream is provided.
     */
    public function conferenceCallPlayBlocking(int $id, LocalFile|RemoteUrl|ReadableStream $file, MediaDestination $dest = MediaDestination::Camera): void
    {
        $this->callPlayInternal('conferenceCall', $id, $file, $dest, true);
    }

    /**
     * Play files on hold in conference call.
     */
    public function conferenceCallPlayOnHold(int $id, MediaDestination $dest = MediaDestination::Camera, LocalFile|RemoteUrl|ReadableStream ...$files): void
    {
        $this->callPlayOnHoldInternal('conferenceCall', $id, $dest, $files);
    }

    /**
     * Files, URLs and resumable streams are sent to the IPC server as they are, and survive restarts
     * of the call; other streams are read through a callback connection, which only lives as long as
     * the IPC call that created it: that's why they're always played blocking.
     */
    private function callPlayInternal(string $prefix, int $id, LocalFile|RemoteUrl|ReadableStream $file, MediaDestination $dest, bool $blocking): void
    {
        // The IPC server might have a different working directory.
        if ($file instanceof LocalFile) {
            $file = new LocalFile(Tools::absolute($file->file));
        }
        if (!$file instanceof ReadableStream || $file instanceof ResumableStream) {
            $this->__call($prefix.($blocking ? 'PlayBlocking' : 'Play'), [$id, $file, $dest]);
            return;
        }
        $params = [$id, &$file, $dest];
        $wrapper = Wrapper::create($params, $this->session, $this->logger);
        $wrapper->wrap($file, true);
        $this->__call($prefix.'PlayBlocking', $wrapper);
    }

    /**
     * @param array<LocalFile|RemoteUrl|ReadableStream> $files
     */
    private function callPlayOnHoldInternal(string $prefix, int $id, MediaDestination $dest, array $files): void
    {
        $params = [$id, $dest];
        foreach ($files as $file) {
            if ($file instanceof ReadableStream && !$file instanceof ResumableStream) {
                // Hold files are played after this call returns, when the stream can't be read through IPC anymore.
                throw new Exception('Only files, URLs and resumable streams can be played on hold through the IPC server: buffer other streams to a file first.');
            }
            // The IPC server might have a different working directory.
            $params[] = $file instanceof LocalFile ? new LocalFile(Tools::absolute($file->file)) : $file;
        }
        $this->__call($prefix.'PlayOnHold', $params);
    }

    /**
     * Upload file from callable.
     *
     * The callable must accept two parameters: int $offset, int $size
     * The callable must return a string with the contest of the file at the specified offset and size.
     *
     * @param mixed    $callable  Callable
     * @param integer  $size      File size
     * @param ?string   $mime      Mime type
     * @param string   $fileName  File name
     * @param callable $cb        Callback
     * @param boolean  $seekable  Whether chunks can be fetched out of order
     * @param boolean  $encrypted Whether to encrypt file for secret chats
     */
    public function uploadFromCallable(callable $callable, int $size, ?string $mime, string $fileName = '', ?callable $cb = null, bool $seekable = true, bool $encrypted = false, ?Cancellation $cancellation = null)
    {
        if (\is_object($callable) && $callable instanceof FileCallbackInterface) {
            $cb = $callable;
            $callable = $callable->getFile();
        }
        $params = [&$callable, $size, $mime, $fileName, &$cb, $seekable, $encrypted, &$cancellation];
        $wrapper = Wrapper::create($params, $this->session, $this->logger);
        $wrapper->wrap($cb, false);
        $wrapper->wrap($callable, false);
        $wrapper->wrap($cancellation);
        return $this->__call('uploadFromCallable', $wrapper);
    }
    /**
     * Reupload telegram file.
     *
     * @param mixed    $media     Telegram file
     * @param callable $cb        Callback
     * @param boolean  $encrypted Whether to encrypt file for secret chats
     */
    public function uploadFromTgfile(mixed $media, ?callable $cb = null, bool $encrypted = false, ?Cancellation $cancellation = null)
    {
        if (\is_object($media) && $media instanceof FileCallbackInterface) {
            $cb = $media;
            $media = $media->getFile();
        }
        $params = [$media, &$cb, $encrypted, &$cancellation];
        $wrapper = Wrapper::create($params, $this->session, $this->logger);
        $wrapper->wrap($cb, false);
        $wrapper->wrap($cancellation);
        return $this->__call('uploadFromTgfile', $wrapper);
    }
    /**
     * Call method and wait asynchronously for response.
     *
     * @param string $method Method name
     * @param array  $args   Arguments
     */
    public function methodCallAsyncRead(string $method, array $args, ?int $dcId = null)
    {
        if ((
            $method === 'messages.editInlineBotMessage' ||
            $method === 'messages.uploadMedia' ||
            $method === 'messages.sendMedia' ||
            $method === 'stories.sendStory' ||
            $method === 'messages.editMessage'
        ) && isset($args['media']) && \is_array($args['media'])) {
            $this->processMedia($args['media'], $args['cancellation'] ?? null, true);
        } elseif ($method === 'messages.sendMultiMedia' && isset($args['multi_media'])) {
            foreach ($args['multi_media'] as &$media) {
                if (\is_array($media['media'])) {
                    $this->processMedia($media['media'], $args['cancellation'] ?? null, true);
                }
            }
        }
        // Identifies the call if it's resumed by the IPC server, and sent again after a restart.
        $args['resumableCallId'] ??= bin2hex(random_bytes(16));
        if (isset($args['cancellation']) && $args['cancellation'] instanceof Cancellation) {
            $params = [$method, &$args, $dcId];
            $wrapper = Wrapper::create($params, $this->session, $this->logger);
            $wrapper->wrap($args['cancellation']);
            return $this->__call('methodCallAsyncRead', $wrapper);
        }
        return $this->__call('methodCallAsyncRead', [$method, $args, $dcId]);
    }
    /**
     * Download file to directory.
     *
     * @param mixed                        $messageMedia File to download
     * @param string|FileCallbackInterface $dir          Directory where to download the file
     * @param callable                     $cb           Callback
     */
    public function downloadToDir(mixed $messageMedia, string|FileCallbackInterface $dir, ?callable $cb = null, ?Cancellation $cancellation = null)
    {
        if (\is_object($dir) && $dir instanceof FileCallbackInterface) {
            $cb = $dir;
            $dir = $dir->getFile();
        }
        $params = [$messageMedia, $dir, &$cb, &$cancellation];
        $wrapper = Wrapper::create($params, $this->session, $this->logger);
        $wrapper->wrap($cb, false);
        $wrapper->wrap($cancellation);
        return $this->__call('downloadToDir', $wrapper);
    }
    /**
     * Download file.
     *
     * @param mixed                        $messageMedia File to download
     * @param string|FileCallbackInterface $file         Downloaded file path
     * @param callable                     $cb           Callback
     */
    public function downloadToFile(mixed $messageMedia, string|FileCallbackInterface $file, ?callable $cb = null, ?Cancellation $cancellation = null)
    {
        if (\is_object($file) && $file instanceof FileCallbackInterface) {
            $cb = $file;
            $file = $file->getFile();
        }
        $params = [$messageMedia, $file, &$cb, &$cancellation];
        $wrapper = Wrapper::create($params, $this->session, $this->logger);
        $wrapper->wrap($cb, false);
        $wrapper->wrap($cancellation);
        return $this->__call('downloadToFile', $wrapper);
    }
    /**
     * Download file to callable.
     * The callable must accept two parameters: string $payload, int $offset
     * The callable will be called (possibly out of order, depending on the value of $seekable).
     * The callable should return the number of written bytes.
     *
     * @param mixed                          $messageMedia File to download
     * @param callable|FileCallbackInterface $callable     Chunk callback
     * @param callable                       $cb           Status callback
     * @param bool                           $seekable     Whether the callable can be called out of order
     * @param int                            $offset       Offset where to start downloading
     * @param int                            $end          Offset where to stop downloading (inclusive)
     * @param int                            $part_size    Size of each chunk
     */
    public function downloadToCallable(mixed $messageMedia, callable $callable, ?callable $cb = null, bool $seekable = true, int $offset = 0, int $end = -1, ?int $part_size = null, ?Cancellation $cancellation = null)
    {
        $messageMedia = ($this->getDownloadInfo($messageMedia));
        if (\is_object($callable) && $callable instanceof FileCallbackInterface) {
            $cb = $callable;
            $callable = $callable->getFile();
        }
        $params = [$messageMedia, &$callable, &$cb, $seekable, $offset, $end, $part_size, &$cancellation];
        $wrapper = Wrapper::create($params, $this->session, $this->logger);
        $wrapper->wrap($callable, false);
        $wrapper->wrap($cb, false);
        $wrapper->wrap($cancellation);
        return $this->__call('downloadToCallable', $wrapper);
    }
    /**
     * Placeholder.
     *
     * @param mixed ...$params Params
     *
     * @psalm-pure
     */
    public function setEventHandler(mixed ...$params): void
    {
        throw new Exception("Can't use ".__FUNCTION__.' in an IPC client instance, please use startAndLoop, instead!');
    }
    /**
     * @psalm-mutation-free
     */
    public function getEventHandler(?string $class = null): ?EventHandlerProxy
    {
        if ($class !== null) {
            if ($class === $this->getEventHandlerClass()) {
                return new EventHandlerProxy(null, $this);
            }
            return $this->getPlugin($class);
        }
        return $this->hasEventHandler() ? new EventHandlerProxy(null, $this) : null;
    }
    /**
     * @psalm-mutation-free
     */
    public function getPlugin(string $class): ?EventHandlerProxy
    {
        return $this->hasPlugin($class) ? new EventHandlerProxy($class, $this) : null;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Ipc;

use Amp\ByteStream\ReadableStream as ByteStreamReadableStream;
use Amp\ByteStream\WritableStream as ByteStreamWritableStream;
use Amp\Cancellation;
use Amp\CancelledException;
use Amp\Ipc\Sync\ChannelException;
use Amp\Ipc\Sync\ChannelledSocket;
use danog\MadelineProto\FileCallback as MadelineProtoFileCallback;
use danog\MadelineProto\FileCallbackInterface;
use danog\MadelineProto\Ipc\Wrapper\Cancellation as WrapperCancellation;
use danog\MadelineProto\Ipc\Wrapper\CancellationReference;
use danog\MadelineProto\Ipc\Wrapper\FileCallback;
use danog\MadelineProto\Ipc\Wrapper\Obj;
use danog\MadelineProto\Ipc\Wrapper\ReadableStream;
use danog\MadelineProto\Ipc\Wrapper\SeekableReadableStream;
use danog\MadelineProto\Ipc\Wrapper\SeekableWritableStream;
use danog\MadelineProto\Ipc\Wrapper\WrappedCancellation;
use danog\MadelineProto\Ipc\Wrapper\WritableStream;
use danog\MadelineProto\Logger;
use danog\MadelineProto\SessionPaths;
use Revolt\EventLoop;
use Throwable;

use function Amp\Ipc\connect;

/**
 * Callback payload wrapper.
 *
 * @psalm-suppress InternalMethod
 * @psalm-suppress InternalClass
 *
 * @internal
 */
final class Wrapper extends ClientAbstract
{
    /**
     * Payload data.
     */
    private mixed $data;
    /**
     * Callbacks.
     *
     * @var array<callable>
     */
    private array $callbacks = [];
    /**
     * Wrapped cancellations, stopped when the connection is closed.
     *
     * @var list<WrappedCancellation>
     */
    private array $cancellations = [];
    /**
     * Whether the callback connection was closed.
     */
    private bool $disconnected = false;
    /**
     * Callbacks IDs.
     *
     * @var list<int|list{class-string<Obj>, array<string, int>}>
     */
    private array $callbackIds = [];
    /**
     * Callback ID.
     */
    private int $id = 0;
    /**
     * Remote socket ID.
     */
    private int $remoteId = 0;
    /**
     * Constructor.
     *
     * @param mixed $data Payload data
     */
    public static function create(mixed &$data, SessionPaths $session, Logger $logger): self
    {
        $instance = new self;
        $instance->data = &$data;
        $instance->logger = $logger;
        $instance->run = false;

        $logger->logger('Connecting to callback IPC server...');
        $instance->server = connect($session->getIpcCallbackPath());
        $logger->logger('Connected to callback IPC server!');

        $instance->remoteId = $instance->server->receive();
        $logger->logger("Got ID {$instance->remoteId} from callback IPC server!");

        EventLoop::queue($instance->receiverLoop(...));
        return $instance;
    }
    /**
     * Serialization function.
     *
     * @psalm-pure
     */
    public function __sleep(): array
    {
        return ['data', 'callbackIds', 'remoteId'];
    }
    /**
     * Wrap a certain callback object.
     *
     * @param mixed $callback    Callback to wrap
     * @param bool  $wrapObjects Whether to wrap object methods, too
     */
    public function wrap(mixed &$callback, bool $wrapObjects = true): void
    {
        if (\is_object($callback) && $wrapObjects) {
            if ($callback instanceof Cancellation) {
                if ($callback->isRequested()) {
                    // The IPC server only learns about cancellations asynchronously: don't even start the call.
                    $this->disconnect();
                    $callback->throwIfRequested();
                }
                $callback = new WrappedCancellation($callback);
                $this->cancellations[] = $callback;
            }
            if ($callback instanceof FileCallbackInterface) {
                $file = $callback->getFile();
                if ($file instanceof ByteStreamReadableStream) {
                    $this->wrap($file, true);
                    $callback = new MadelineProtoFileCallback($file, $callback);
                }
            }
            $ids = [];
            foreach (get_class_methods($callback) as $method) {
                $id = $this->id++;
                $this->callbacks[$id] = [$callback, $method];
                $ids[$method] = $id;
            }
            $class = null;
            if ($callback instanceof ByteStreamReadableStream) {
                $class = method_exists($callback, 'seek') ? SeekableReadableStream::class : ReadableStream::class;
            } elseif ($callback instanceof ByteStreamWritableStream) {
                $class = method_exists($callback, 'seek') ? SeekableWritableStream::class : WritableStream::class;
            } elseif ($callback instanceof FileCallbackInterface) {
                $class = FileCallback::class;
            } elseif ($callback instanceof WrappedCancellation) {
                $class = WrapperCancellation::class;
            }
            if (!$class) {
                return;
            }
            $callback = [$class, $ids]; // Will be re-filled later
            $this->callbackIds[] = &$callback;
        } elseif (\is_callable($callback)) {
            $id = $this->id++;
            $this->callbacks[$id] = self::copy($callback);
            $callback = $id;
            $this->callbackIds[] = &$callback;
        }
    }
    /**
     * Get copy of data.
     *
     * @psalm-pure
     */
    private static function copy($data)
    {
        return $data;
    }
    /**
     * Receiver loop.
     */
    private function receiverLoop(): void
    {
        $id = 0;
        $payload = null;
        try {
            while ($payload = $this->server->receive()) {
                EventLoop::queue($this->clientRequest(...), $id++, $payload);
            }
        } finally {
            $this->disconnected = true;
            foreach ($this->cancellations as $cancellation) {
                $cancellation->disconnect();
            }
            EventLoop::queue($this->server->disconnect(...), "exiting receiverLoop");
        }
    }

    /**
     * Handle client request.
     *
     * @param integer $id      Request ID
     * @param array   $payload Payload
     */
    private function clientRequest(int $id, array $payload): void
    {
        try {
            /** @var list<mixed> */
            $args = $payload[1];
            /** @var mixed $arg */
            foreach ($args as &$arg) {
                if ($arg instanceof CancellationReference) {
                    $callback = $this->callbacks[$arg->id] ?? null;
                    $arg = \is_array($callback) && $callback[0] instanceof WrappedCancellation
                        ? $callback[0]->getCancellation()
                        : null;
                }
            }
            unset($arg);
            $result = $this->callbacks[$payload[0]](...$args);
        } catch (CancelledException $e) {
            $result = new ExitFailure($e);
        } catch (Throwable $e) {
            $this->logger->logger("Got error while calling reverse IPC method: $e", Logger::ERROR);
            $result = new ExitFailure($e);
        }
        if ($this->disconnected) {
            return;
        }
        try {
            $this->server->send([$id, $result]);
        } catch (ChannelException $e) {
            // The server closed the connection, as it does once the IPC call of the callbacks returned (the late
            // progress notifications of an upload, for example), before receiverLoop() got to see it (receive()
            // closes the socket, then sends the close request): nobody waits for the result anymore, see
            // ClientAbstract::loopInternal().
            $this->disconnected = true;
            $this->logger->logger("Not sending the result of reverse method {$payload[0]}, the connection was closed: $e", Logger::VERBOSE);
        } catch (Throwable $e) {
            $this->logger->logger("Got error while trying to send result of reverse method {$payload[0]}: $e", Logger::ERROR);
            try {
                $this->server->send([$id, new ExitFailure($e)]);
            } catch (Throwable $e) {
                $this->logger->logger("Got error while trying to send error of error of reverse method {$payload[0]}: $e", Logger::ERROR);
            }
        }
    }
    /**
     * Call a callback of the client.
     *
     * @param string|int    $function  Callback ID
     * @param array|Wrapper $arguments Arguments
     */
    #[\Override]
    public function __call(string|int $function, array|Wrapper $arguments)
    {
        if (\is_array($arguments)) {
            /** @var mixed $argument */
            foreach ($arguments as &$argument) {
                if ($argument instanceof WrapperCancellation) {
                    // Proxies of the client's cancellations can't be serialized:
                    // the client resolves the reference to the original cancellation.
                    $argument = $argument->getReference();
                }
            }
            unset($argument);
        }
        return parent::__call($function, $arguments);
    }

    /**
     * Get remote socket ID.
     *
     * @internal
     */
    public function getRemoteId(): int
    {
        return $this->remoteId;
    }

    /**
     * Set socket and unwrap data.
     *
     * @param ChannelledSocket $server Socket.
     * @internal
     */
    public function unwrap(ChannelledSocket $server)
    {
        $this->server = $server;
        EventLoop::queue($this->loopInternal(...));

        foreach ($this->callbackIds as &$id) {
            if (\is_int($id)) {
                $id = fn (...$args) => $this->__call($id, $args);
            } else {
                [$class, $ids] = $id;
                $id = new $class($this, $ids);
            }
        }
        return $this->data;
    }
}
<?php

declare(strict_types=1);

/**
 * IPC callback server.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Ipc;

use Amp\Ipc\IpcServer;
use Amp\Ipc\Sync\ChannelledSocket;
use danog\MadelineProto\Exception;
use danog\MadelineProto\SessionPaths;
use Revolt\EventLoop;

/**
 * IPC callback server.
 *
 * @internal
 */
final class ServerCallback extends AbstractServer
{
    /**
     * Timeout watcher list, indexed by socket ID.
     *
     * @var array<int, string>
     */
    private array $watcherList = [];
    /**
     * Timeout watcher list, indexed by socket ID.
     *
     * @var array<int, ChannelledSocket>
     */
    private array $socketList = [];
    /**
     * Counter.
     */
    private int $id = 0;
    /**
     * Set IPC path.
     *
     * @param SessionPaths $session Session
     */
    #[\Override]
    public function setIpcPath(SessionPaths $session): void
    {
        $this->server = new IpcServer($session->getIpcCallbackPath());
    }
    /**
     * Client handler loop.
     *
     * @param ChannelledSocket $socket Client
     */
    #[\Override]
    protected function clientLoop(ChannelledSocket $socket): void
    {
        $id = $this->id++;
        $this->API->waitForInit();
        $this->API->logger("Accepted IPC callback connection, assigning ID $id!");
        $this->socketList[$id] = $socket;
        $this->watcherList[$id] = EventLoop::delay(30, function () use ($id): void {
            unset($this->watcherList[$id], $this->socketList[$id]);
        });

        $socket->send($id);
    }

    /**
     * Unwrap value.
     */
    protected function unwrap(Wrapper $wrapper)
    {
        $id = $wrapper->getRemoteId();
        if (!isset($this->socketList[$id])) {
            throw new Exception('IPC timeout, could not find callback socket!');
        }
        $socket = $this->socketList[$id];
        EventLoop::cancel($this->watcherList[$id]);
        unset($this->watcherList[$id], $this->socketList[$id]);
        return $wrapper->unwrap($socket);
    }
}
<?php

declare(strict_types=1);

/**
 * API wrapper module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Ipc;

use Amp\Cancellation;
use Amp\CancelledException;
use Amp\CompositeException;
use Amp\DeferredFuture;
use Amp\Future;
use Amp\Ipc\IpcServer;
use Amp\Ipc\Sync\ChannelledSocket;
use danog\BetterPrometheus\BetterGauge;
use danog\Loop\Loop;
use danog\MadelineProto\Exception;
use danog\MadelineProto\Ipc\Runner\ProcessRunner;
use danog\MadelineProto\Ipc\Runner\WebRunner;
use danog\MadelineProto\Lang;
use danog\MadelineProto\Logger;
use danog\MadelineProto\Loop\InternalLoop;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\Serialization;
use danog\MadelineProto\SessionPaths;
use danog\MadelineProto\Settings\Ipc;
use danog\MadelineProto\Shutdown;
use danog\MadelineProto\Tools;
use Revolt\EventLoop;
use Throwable;

use function Amp\async;
use function Amp\delay;
use function Amp\Ipc\connect;

/**
 * IPC server.
 *
 * @internal
 */
abstract class AbstractServer extends Loop
{
    use InternalLoop {
        __construct as private internalInit;
    }
    private ?BetterGauge $connectionGauge;
    public function __construct(MTProto $API)
    {
        $this->internalInit($API);
        $this->connectionGauge = $API->getPromGauge("MadelineProto", "ipc_server_connections", "Number of IPC server connections");
        $this->connectionGauge?->set(0);
    }
    /**
     * Server version.
     */
    public const VERSION = 1;
    /**
     * Shutdown server.
     */
    public const SHUTDOWN = 0;
    /**
     * Maximum time to wait for a started IPC server to report its state, in seconds.
     */
    private const STARTUP_TIMEOUT = 30;
    /**
     * Boolean to shut down worker, if started.
     */
    private static bool $shutdown = false;
    /**
     * Deferred to shut down worker, if started.
     */
    private static ?DeferredFuture $shutdownDeferred = null;
    /**
     * Boolean whether to shut down worker, if started.
     */
    private static bool $shutdownNow = false;
    /**
     * IPC server.
     */
    protected IpcServer $server;
    /**
     * Callback IPC server.
     */
    private ServerCallback $callback;
    /**
     * Set IPC path.
     *
     * @param SessionPaths $session Session
     */
    public function setIpcPath(SessionPaths $session): void
    {
        self::$shutdownDeferred ??= new DeferredFuture;
        $this->server = new IpcServer($session->getIpcPath());
        $this->callback = new ServerCallback($this->API);
        $this->callback->setIpcPath($session);
        self::warnIfOverTcp($session);
    }
    /**
     * Warn once (per session and process) if the IPC server of the session listens on TCP, because UNIX sockets
     * are unavailable (on Windows, without the sockets extension).
     */
    public static function warnIfOverTcp(SessionPaths $session): void
    {
        static $warned = [];
        $path = $session->getSessionDirectoryPath();
        if (!isset($warned[$path]) && $session->isIpcOverTcp()) {
            $warned[$path] = true;
            Logger::log(Lang::$current_lang['ipc_tcp_warning'], Logger::WARNING);
        }
    }
    #[\Override]
    public function start(): bool
    {
        return $this instanceof ServerCallback ? parent::start() : $this->callback->start() && parent::start();
    }
    /**
     * Start IPC server in background.
     *
     * @param SessionPaths  $session      Session path
     * @param ?Cancellation $cancellation Stops monitoring the startup (the future then resolves to false): the
     *                                    monitor polls the IPC state file, so whoever stops caring about the
     *                                    startup must cancel it and await the future, or it outlives its caller
     *                                    with file operations in flight.
     *
     * @return Future<bool|Throwable>
     */
    public static function startMe(SessionPaths $session, ?Cancellation $cancellation = null): Future
    {
        $id = Tools::randomInt(2000000000);
        $startTime = microtime(true);
        $started = false;
        $e = null;
        try {
            Logger::log("Starting IPC server $session (process)");
            ProcessRunner::start((string) $session, $id);
            $started = true;
            WebRunner::start((string) $session, $id);
            return async(self::monitor(...), $session, $id, $startTime, $started, null, $cancellation);
        } catch (Throwable $e) {
            Logger::log($e);
        }
        try {
            Logger::log("Starting IPC server $session (web)");
            if (WebRunner::start((string) $session, $id)) {
                $started = true;
            }
        } catch (Throwable $e2) {
            Logger::log($e2);
            if ($e) {
                $e = new CompositeException([$e, $e2]);
            } else {
                $e = $e2;
            }
        }
        return async(self::monitor(...), $session, $id, $startTime, $started, $e, $cancellation);
    }
    /**
     * Monitor session.
     *
     * @param float $startTime When the server was started, to recognize servers started concurrently by other processes
     */
    private static function monitor(SessionPaths $session, int $id, float $startTime, bool $started, ?\Throwable $e, ?Cancellation $cancellation): bool|Throwable
    {
        if (!$started) {
            Logger::log("It looks like the server couldn't be started, trying to connect anyway...");
        }
        $count = 0;
        while (!$cancellation?->isRequested()) {
            $state = $session->getIpcState();
            if ($state && $state->getStartupId() === $id) {
                if ($e = $state->getException()) {
                    Logger::log("IPC server got exception $e");
                    return $e;
                }
                Logger::log('IPC server started successfully!');
                return true;
            } elseif ($state && $state->getStartupTime() >= $startTime && !$state->getException()) {
                // Another process started the server concurrently, overwriting our startup ID
                Logger::log('IPC server started successfully by another process!');
                return true;
            } elseif (!$started && $count > 0 && $count > 2*($state ? 3 : 1)) {
                return new Exception("We couldn't start the IPC server, please check the logs!", previous: $e);
            } elseif ($count > 0 && $count % 2 === 0 && self::isListening($session)) {
                // Served by a process that started before us (our workers exit, finding it)
                Logger::log('IPC server already running!');
                return true;
            } elseif ($count >= self::STARTUP_TIMEOUT*2 && !Serialization::isLockedByAnotherProcess($session)) {
                // The server died before reporting its state.
                // While the session is locked, a server is still loading it (or it's held by another process
                // that will release it soon): that can take longer than any fixed timeout.
                return new Exception("The IPC server didn't start within ".self::STARTUP_TIMEOUT." seconds, please check the logs!", previous: $e);
            }
            try {
                delay(0.5, true, $cancellation);
            } catch (CancelledException) {
                break;
            }
            $count++;
        }
        return false;
    }
    /**
     * Whether an IPC server listens on the socket of the session.
     */
    private static function isListening(SessionPaths $session): bool
    {
        try {
            clearstatcache(true, $session->getIpcPath());
            connect($session->getIpcPath())->disconnect();
            return true;
        } catch (Throwable) {
            return false;
        }
    }
    /**
     * Wait for shutdown.
     */
    public static function waitShutdown(): void
    {
        if (self::$shutdownNow) {
            return;
        }
        self::$shutdownDeferred ??= new DeferredFuture;
        self::$shutdownDeferred->getFuture()->await();
    }
    /**
     * Shutdown.
     */
    #[\Override]
    final public function stop(): bool
    {
        $this->server->close();
        if (!$this instanceof ServerCallback) {
            $this->callback->server->close();
        }
        if (self::$shutdownDeferred) {
            self::$shutdownNow = true;
            $deferred = self::$shutdownDeferred;
            self::$shutdownDeferred = null;
            $deferred->complete();
        }
        return true;
    }
    /**
     * Main loop.
     */
    #[\Override]
    protected function loop(): ?float
    {
        while ($socket = $this->server->accept()) {
            EventLoop::queue($this->clientLoop(...), $socket);
        }
        $this->server->close();
        if (isset($this->callback)) {
            $this->callback->server->close();
        }
        return self::STOP;
    }
    /**
     * Client handler loop.
     *
     * @param ChannelledSocket $socket Client
     */
    protected function clientLoop(ChannelledSocket $socket): void
    {
        $this->API->waitForInit();
        $this->API->logger('Accepted IPC client connection!');
        $this->connectionGauge?->inc();

        $id = 0;
        $payload = null;
        try {
            while ($payload = $socket->receive()) {
                EventLoop::queue($this->clientRequest(...), $socket, $id++, $payload);
            }
        } catch (Throwable $e) {
            Logger::log("Exception in IPC connection: $e");
        } finally {
            $this->connectionGauge?->dec();
            EventLoop::queue(function () use ($socket, $payload): void {
                try {
                    $socket->disconnect();
                } catch (Throwable $e) {
                    Logger::log("Exception during shutdown in IPC connection: $e");
                }
                if ($payload === self::SHUTDOWN) {
                    Shutdown::removeCallback('restarter');
                    $this->stop();
                }
            });
        }
    }
    /**
     * Handle client request.
     *
     * @param ChannelledSocket                   $socket  Socket
     * @param array{0: string, 1: array|Wrapper} $payload Payload
     */
    private function clientRequest(ChannelledSocket $socket, int $id, array $payload): void
    {
        try {
            if ($payload[1] instanceof Wrapper) {
                $wrapper = $payload[1];
                $payload[1] = $this->callback->unwrap($wrapper);
            }
            $result = $this->API->{$payload[0]}(...$payload[1]);
        } catch (Throwable $e) {
            $this->API->logger("Got error while calling IPC method: $e", Logger::ERROR);
            $result = new ExitFailure($e);
        } finally {
            if (isset($wrapper)) {
                EventLoop::queue(static function () use ($wrapper): void {
                    try {
                        $wrapper->disconnect();
                    } catch (Throwable $e) {
                        Logger::log("Exception during shutdown in IPC connection: $e");
                    }
                });
            }
        }
        try {
            $socket->send([$id, $result]);
        } catch (Throwable $e) {
            $this->API->logger("Got error while trying to send result of {$payload[0]}: $e", Logger::ERROR);
            try {
                $socket->send([$id, new ExitFailure($e)]);
            } catch (Throwable $e) {
                $this->API->logger("Got error while trying to send error of error of {$payload[0]}: $e", Logger::ERROR);
            }
        }
    }
    /**
     * Get the name of the loop.
     *
     * @psalm-pure
     */
    public function __toString(): string
    {
        return 'IPC server';
    }
}
<?php

declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Ipc;

/**
 * Thrown in an IPC worker that was started while another process already serves the session: the worker is
 * redundant and exits without reporting an error.
 *
 * @internal
 */
final class AlreadyServedException extends \RuntimeException
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Ipc;

/**
 * Plugin event handler proxy object, for use through the IPC API.
 */
final class EventHandlerProxy extends IpcCapable
{
    /**
     * @psalm-mutation-free
     */
    public function __construct(
        protected readonly ?string $__plugin,
        Client $API
    ) {
        parent::__construct($API);
    }
    /**
     * @psalm-mutation-free
     */
    public function __call(string $name, array $arguments): mixed
    {
        return $this->getClient()->callPluginMethod(
            $this->__plugin,
            $name,
            $arguments
        );
    }
    /**
     * @psalm-mutation-free
     */
    public function __get(string $name): mixed
    {
        return $this->getClient()->getPluginProperty($this->__plugin, $name);
    }
    public function __set(string $name, mixed $value): void
    {
        $this->getClient()->setPluginProperty($this->__plugin, $name, $value);
    }
    /**
     * @psalm-mutation-free
     */
    public function __isset(string $name): bool
    {
        return $this->getClient()->issetPluginProperty($this->__plugin, $name);
    }
    /**
     * @psalm-mutation-free
     */
    public function __unset(string $name): void
    {
        $this->getClient()->unsetPluginProperty($this->__plugin, $name);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Ipc;

use danog\MadelineProto\MTProto;
use danog\MadelineProto\RestoresDeclaredProperties;

/**
 * Represents an IPC-capable object.
 *
 * @internal
 *
 * @psalm-external-mutation-free
 */
abstract class IpcCapable
{
    use RestoresDeclaredProperties;

    protected readonly string $session;
    private MTProto|Client|null $API;

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    protected function __construct(MTProto|Client $API)
    {
        $this->API = $API;
        $this->session = $API->getSessionName();
    }

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    final public function __sleep()
    {
        $vars = get_object_vars($this);
        unset($vars['API']);
        return array_keys($vars);
    }

    /**
     * Restore the properties listed by __sleep(), dropping the fields removed since the object was saved.
     *
     * @internal
     */
    final public function __unserialize(array $data): void
    {
        $this->restoreDeclaredProperties($data);
    }

    /**
     * @psalm-external-mutation-free
     */
    final protected function getClient(): MTProto|Client
    {
        return $this->API ??= Client::giveInstanceBySession($this->session);
    }

    /**
     * @psalm-mutation-free
     */
    final public function __debugInfo()
    {
        $vars = get_object_vars($this);
        unset($vars['API']);
        return $vars;
    }
}
<?php

declare(strict_types=1);

/**
 * API wrapper module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Ipc;

/**
 * IPC server.
 *
 * @internal
 */
final class Server extends AbstractServer
{

}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Ipc;

use Throwable;

/**
 * IPC state class.
 *
 * @internal
 */
final class IpcState
{
    /**
     * Startup time.
     */
    private float $startupTime;
    /**
     * Startup ID.
     */
    private int $startupId;
    /**
     * Exception.
     */
    private ?ExitFailure $exception;
    /**
     * Construct.
     */
    public function __construct(int $startupId, ?Throwable $exception = null)
    {
        $this->startupTime = microtime(true);
        $this->startupId = $startupId;
        $this->exception = $exception ? new ExitFailure($exception) : null;
    }

    /**
     * Get startup time.
     */
    public function getStartupTime(): float
    {
        return $this->startupTime;
    }

    /**
     * Get startup ID.
     */
    public function getStartupId(): int
    {
        return $this->startupId;
    }

    /**
     * Get exception.
     */
    public function getException(): ?Throwable
    {
        return $this->exception ? $this->exception->getException() : null;
    }
}
<?php

declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

/**
 * Thrown when an interrupted upload can't be resumed, because its source ends before the offset to resume from.
 *
 * The resume state is dropped, so uploading the file again starts from scratch.
 */
final class UploadResumeException extends Exception
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use Closure;
use ReflectionClass;
use ReflectionProperty;

/**
 * @psalm-external-mutation-free
 */
abstract class SettingsAbstract
{
    use RestoresDeclaredProperties;
    /**
     * Whether this setting was changed.
     *
     */
    protected bool $changed = true;

    /**
     * @psalm-mutation-free
     */
    public function __sleep()
    {
        $result = [];
        foreach ((new ReflectionClass($this))->getProperties(ReflectionProperty::IS_PROTECTED) as $property) {
            $result []= $property->getName();
        }
        return $result;
    }
    /**
     * Restore the properties listed by __sleep(), dropping the settings removed since they were saved.
     *
     * @internal
     */
    public function __unserialize(array $data): void
    {
        $this->restoreDeclaredProperties($data);
        // PHP does not call __wakeup() on a class that has __unserialize().
        if (method_exists($this, '__wakeup')) {
            $this->__wakeup();
        }
    }
    /**
     * Merge with other settings instance.
     *
     * @internal
     *
     * @psalm-external-mutation-free
     */
    public function merge(self $other): void
    {
        $class = new ReflectionClass($other);
        $defaults = $class->getDefaultProperties();
        foreach ($class->getProperties(ReflectionProperty::IS_PROTECTED) as $property) {
            $name = $property->getName();
            if ($name === 'changed') {
                continue;
            }
            $uc = ucfirst($name);
            if ((isset($other->{$name}) || $name === 'metricsBindTo')
                && (
                    !\array_key_exists($name, $defaults)
                    || (
                        !self::same($other->{$name}, $defaults[$name])  // Isn't equal to the default value
                        || !self::same($other->{$name}, $this->{$name}) // Is equal, but current value is not the default one
                    )
                )
                && (
                    !isset($this->{$name})
                    || !self::same($other->{$name}, $this->{$name})
                )
            ) {
                if ($uc === 'Proxy') {
                    $uc = 'Proxies';
                }
                $this->{"set$uc"}($other->{$name});
                $this->changed = true;
            }
        }
    }
    /**
     * Whether two setting values are the same: value objects (a SocketAddress) by value, since every client builds
     * its own (an identity check marked them as changed on every merge, restarting the metrics server).
     *
     * @psalm-pure
     */
    private static function same(mixed $a, mixed $b): bool
    {
        if (\is_object($a) && \is_object($b) && !$a instanceof Closure && $a::class === $b::class) {
            return $a == $b;
        }
        return $a === $b;
    }
    /**
     * Convert array of legacy array property names to new camel case names.
     *
     * @param array $properties Properties
     *
     * @psalm-pure
     */
    protected static function toCamel(array $properties): array
    {
        $result = [];
        foreach ($properties as $prop) {
            $result['set'.ucfirst(Tools::toCamelCase($prop))] = $prop;
        }
        return $result;
    }

    /**
     * Get whether this setting was changed, also applies changes.
     *
     * @internal
     */
    public function hasChanged(): bool
    {
        return $this->changed;
    }
    /**
     * Apply changes.
     *
     * @internal
     *
     * @return static
     *
     * @psalm-external-mutation-free
     */
    public function applyChanges(): self
    {
        $this->changed = false;
        return $this;
    }
}
<?php

declare(strict_types=1);

/**
 * MyTelegramOrgWrapper module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use Amp\Http\Client\Cookie\LocalCookieJar;
use Amp\Http\Client\Request;

/**
 * Wrapper for my.telegram.org.
 */
final class MyTelegramOrgWrapper
{
    /**
     * Whether we're logged in.
     */
    private bool $logged = false;
    /**
     * Login hash.
     */
    private string $hash = '';
    /**
     * Phone number.
     */
    private string $number = '';
    /**
     * Creation hash.
     */
    private string $creation_hash = '';
    /**
     * Settings.
     *
     */
    private Settings $settings;
    /**
     * Datacenter instance.
     */
    private DoHWrapper $datacenter;
    /**
     * Cooke jar.
     *
     */
    private ?LocalCookieJar $jar = null;
    /**
     * Endpoint.
     */
    private const MY_TELEGRAM_URL = 'https://my.telegram.org';
    /**
     * Sleep function.
     *
     * @psalm-pure
     */
    public function __sleep(): array
    {
        return ['logged', 'hash', 'number', 'creation_hash', 'settings', 'jar'];
    }
    /**
     * Constructor.
     */
    public function __construct(SettingsAbstract $settings)
    {
        if (!$settings instanceof Settings) {
            $newSettings = new Settings;
            $newSettings->merge($settings);
            $settings = $newSettings;
        }
        $this->settings = $settings;
        $this->__wakeup();
    }
    /**
     * Wakeup function.
     */
    public function __wakeup(): void
    {
        $this->jar ??= new LocalCookieJar();
        $this->datacenter = new DoHWrapper(
            new class($this->settings) implements
                SettingsGetter,
                LoggerGetter {
                /**
                 * @psalm-mutation-free
                 */
                public function __construct(
                    private readonly Settings $settings
                ) {
                }
                #[\Override]
                public function getSettings(): Settings
                {
                    return $this->settings;
                }
                #[\Override]
                public function getLogger(): Logger
                {
                    return Logger::$default;
                }
            },
            $this->jar
        );
    }
    /**
     * Get the phone number used to log in.
     */
    public function getPhoneNumber(): string
    {
        return $this->number;
    }
    /**
     * Login: my.telegram.org sends a login code to the Telegram app of the account.
     *
     * @param string $number Phone number
     */
    public function login(string $number): void
    {
        $this->logged = false;
        $this->number = $number;
        $result = $this->request('/auth/send_password', 'origin', ['phone' => $number]);
        $resulta = json_decode($result, true);
        if (!\is_array($resulta) || !isset($resulta['random_hash'])) {
            throw new Exception(self::getError($result));
        }
        $this->hash = $resulta['random_hash'];
    }
    /**
     * Complete login.
     *
     * @param string $password The login code sent to the Telegram app
     */
    public function completeLogin(string $password): bool
    {
        if ($this->logged) {
            throw new Exception('Already logged in!');
        }
        if ($this->hash === '') {
            throw new Exception('Call login() first!');
        }
        $result = $this->request('/auth/login', 'origin', ['phone' => $this->number, 'random_hash' => $this->hash, 'password' => $password]);
        if ($result !== 'true') {
            throw new Exception(self::getError($result));
        }
        return $this->logged = true;
    }
    /**
     * Whether we are logged in.
     */
    public function loggedIn(): bool
    {
        return $this->logged;
    }
    /**
     * Check if an app was already created.
     */
    public function hasApp(): bool
    {
        return $this->parseApps($this->getAppsPage()) !== null;
    }
    /**
     * Get the currently created app.
     *
     * @return array{api_id: int, api_hash: string}
     */
    public function getApp(): array
    {
        return $this->parseApps($this->getAppsPage()) ?? throw new Exception('No app was created yet!');
    }
    /**
     * Create an app.
     *
     * Telegram does not allow creating apps from VPS, VPN and other datacenter IP ranges: the request is made
     * from the IP of the machine running MadelineProto.
     *
     * @param array{app_title: string, app_shortname: string, app_url?: string, app_platform?: string, app_desc?: string} $settings App parameters
     *
     * @return array{api_id: int, api_hash: string}
     */
    public function createApp(array $settings): array
    {
        if ($this->parseApps($this->getAppsPage()) !== null) {
            throw new Exception('The app was already created!');
        }
        $result = $this->request('/apps/create', 'app', [
            'hash' => $this->creation_hash,
            'app_title' => $settings['app_title'],
            'app_shortname' => $settings['app_shortname'],
            'app_url' => $settings['app_url'] ?? '',
            'app_platform' => $settings['app_platform'] ?? 'other',
            'app_desc' => $settings['app_desc'] ?? '',
        ]);
        if (trim($result) !== '') {
            throw new Exception(self::getError($result));
        }
        return $this->parseApps($this->getAppsPage()) ?? throw new Exception('App creation failed');
    }
    /**
     * Get the apps page.
     */
    private function getAppsPage(): string
    {
        if (!$this->logged) {
            throw new Exception('Not logged in!');
        }
        return $this->request('/apps', 'refer');
    }
    /**
     * Parse the apps page: get the app, or store the creation hash if there is no app yet.
     *
     * @return ?array{api_id: int, api_hash: string}
     */
    private function parseApps(string $page): ?array
    {
        $title = preg_match('~<title>(.*?)</title>~s', $page, $matches) ? trim($matches[1]) : '';
        if ($title === 'Create new application') {
            if (!preg_match('~<input type="hidden" name="hash" value="([^"]+)"~', $page, $matches)) {
                throw new Exception('Could not find the app creation hash!');
            }
            $this->creation_hash = $matches[1];
            return null;
        }
        // The first number after the label of the API ID, and the first 32 hex digits after the label of the hash:
        // the markup changes (a <strong> in a form, then a <span class="vv"> with the hash masked, its value in a
        // data-value attribute)
        if ($title === 'App configuration'
            && preg_match('~App api_id\b.*?\b(\d{3,})\b~s', $page, $id)
            && preg_match('~App api_hash\b.*?\b([a-f0-9]{32})\b~s', $page, $hash)
        ) {
            return ['api_id' => (int) $id[1], 'api_hash' => $hash[1]];
        }
        if ($title === 'App configuration') {
            // Logged in, with an app: its page changed again
            throw new Exception('Could not find the API ID and hash in the page of the app on my.telegram.org');
        }
        // Most likely, the login expired
        $this->logged = false;
        throw new Exception($title === '' ? 'Unexpected response from my.telegram.org' : $title);
    }
    /**
     * Make a request to my.telegram.org.
     *
     * @param array<string, string> $body POST parameters, or empty for a GET request
     */
    private function request(string $path, string $httpType, array $body = []): string
    {
        $request = new Request(self::MY_TELEGRAM_URL.$path, $body ? 'POST' : 'GET');
        $request->setHeaders($this->getHeaders($httpType));
        if ($body) {
            $request->setBody(http_build_query($body));
        }
        return $this->datacenter->HTTPClient->request($request)->getBody()->buffer();
    }
    /**
     * Get a readable error from a response of my.telegram.org.
     */
    private static function getError(string $result): string
    {
        // Errors of the login endpoints: {"text": "...", "field": ...}
        $json = json_decode($result, true);
        if (\is_array($json) && isset($json['text']) && \is_string($json['text'])) {
            $result = $json['text'];
        }
        $result = trim(html_entity_decode(strip_tags($result), ENT_QUOTES | ENT_HTML5, 'UTF-8'));
        return $result === '' ? 'Empty response from my.telegram.org' : $result;
    }
    /**
     * Function for generating curl request headers.
     *
     * @param string $httpType Origin
     *
     * @psalm-pure
     */
    private function getHeaders(string $httpType): array
    {
        // Common header flags.
        $headers = [];
        $headers[] = 'Dnt: 1';
        $headers[] = 'Connection: keep-alive';
        $headers[] = 'Accept-Language: it-IT,it;q=0.8,en-US;q=0.6,en;q=0.4';
        $headers[] = 'User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36';
        // Add additional headers based on the type of request.
        switch ($httpType) {
            case 'origin':
                $headers[] = 'Origin: '.self::MY_TELEGRAM_URL;
                //$headers[] = 'Accept-Encoding: gzip, deflate, br';
                $headers[] = 'Content-Type: application/x-www-form-urlencoded; charset=UTF-8';
                $headers[] = 'Accept: application/json, text/javascript, */*; q=0.01';
                $headers[] = 'Referer: '.self::MY_TELEGRAM_URL.'/auth';
                $headers[] = 'X-Requested-With: XMLHttpRequest';
                break;
            case 'refer':
                //$headers[] = 'Accept-Encoding: gzip, deflate, sdch, br';
                $headers[] = 'Upgrade-Insecure-Requests: 1';
                $headers[] = 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8';
                $headers[] = 'Referer: '.self::MY_TELEGRAM_URL;
                $headers[] = 'Cache-Control: max-age=0';
                break;
            case 'app':
                $headers[] = 'Origin: '.self::MY_TELEGRAM_URL;
                //$headers[] = 'Accept-Encoding: gzip, deflate, br';
                $headers[] = 'Content-Type: application/x-www-form-urlencoded; charset=UTF-8';
                $headers[] = 'Accept: */*';
                $headers[] = 'Referer: '.self::MY_TELEGRAM_URL.'/apps';
                $headers[] = 'X-Requested-With: XMLHttpRequest';
                break;
        }
        $final_headers = [];
        foreach ($headers as $header) {
            [$key, $value] = explode(':', $header, 2);
            $final_headers[trim($key)] = trim($value);
        }
        return $final_headers;
    }
}
<?php

declare(strict_types=1);

/**
 * DoH module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use Amp\Dns\DnsConfig;
use Amp\Dns\DnsConfigLoader;
use Amp\Dns\DnsResolver;
use Amp\Dns\Rfc1035StubDnsResolver;
use Amp\Dns\UnixDnsConfigLoader;
use Amp\Dns\WindowsDnsConfigLoader;
use Amp\DoH\DoHConfig;
use Amp\DoH\DoHNameserver;
use Amp\DoH\Rfc8484StubDoHResolver;
use Amp\Http\Client\Connection\DefaultConnectionFactory;
use Amp\Http\Client\Connection\UnlimitedConnectionPool;
use Amp\Http\Client\Cookie\CookieInterceptor;
use Amp\Http\Client\Cookie\CookieJar;
use Amp\Http\Client\Cookie\LocalCookieJar;
use Amp\Http\Client\HttpClient;
use Amp\Http\Client\HttpClientBuilder;
use Amp\Socket\ConnectContext;
use Amp\Socket\DnsSocketConnector;
use Amp\Websocket\Client\Rfc6455ConnectionFactory;
use Amp\Websocket\Client\Rfc6455Connector;
use danog\MadelineProto\Stream\Common\BufferedRawStream;
use danog\MadelineProto\Stream\ConnectionContext;
use danog\MadelineProto\Stream\MTProtoTransport\ObfuscatedStream;
use danog\MadelineProto\Stream\Transport\DefaultStream;
use danog\MadelineProto\Stream\Transport\WssStream;
use danog\MadelineProto\Stream\Transport\WsStream;
use Throwable;

/**
 * @psalm-import-type TDcList from DataCenter
 * @internal
 */
final class DoHWrapper
{
    /**
     * HTTP client.
     *
     */
    public readonly HttpClient $HTTPClient;
    /**
     * DNS over HTTPS client.
     *
     */
    public readonly DnsResolver $DoHClient;
    /**
     * Non-proxied DNS over HTTPS client.
     *
     */
    public readonly DnsResolver $nonProxiedDoHClient;
    /**
     * Cookie jar.
     *
     */
    public readonly CookieJar $CookieJar;
    /**
     * DNS connector.
     */
    public readonly DnsSocketConnector $dnsConnector;
    /**
     * DoH connector.
     */
    public readonly Rfc6455Connector $webSocketConnector;

    public function __construct(
        private SettingsGetter&LoggerGetter $API,
        ?CookieJar $jar = null
    ) {
        $configProvider = new class implements DnsConfigLoader {
            #[\Override]
            public function loadConfig(): DnsConfig
            {
                $loader = stripos(PHP_OS, 'win') === 0 ? new WindowsDnsConfigLoader() : new UnixDnsConfigLoader();
                try {
                    return $loader->loadConfig();
                } catch (Throwable) {
                    return new DnsConfig([
                        '1.1.1.1',
                        '1.0.0.1',
                        '[2606:4700:4700::1111]',
                        '[2606:4700:4700::1001]',
                    ]);
                }
            }
        };

        $this->CookieJar = $jar ?? new LocalCookieJar();
        $this->HTTPClient = (new HttpClientBuilder())->interceptNetwork(new CookieInterceptor($this->CookieJar))->usingPool(new UnlimitedConnectionPool(new DefaultConnectionFactory(new ContextConnector($this, $API))))->build();
        $DoHHTTPClient = (new HttpClientBuilder())->interceptNetwork(new CookieInterceptor($this->CookieJar))->usingPool(new UnlimitedConnectionPool(new DefaultConnectionFactory(new ContextConnector($this, $API, true))))->build();
        $DoHConfig = new DoHConfig([new DoHNameserver('https://mozilla.cloudflare-dns.com/dns-query'), new DoHNameserver('https://dns.google/resolve')], $DoHHTTPClient);
        $nonProxiedDoHConfig = new DoHConfig([new DoHNameserver('https://mozilla.cloudflare-dns.com/dns-query'), new DoHNameserver('https://dns.google/resolve')]);
        $this->DoHClient = Magic::$altervista || Magic::$zerowebhost || !$API->getSettings()->getConnection()->getUseDoH()
            ? new Rfc1035StubDnsResolver(null, $configProvider)
            : new Rfc8484StubDoHResolver($DoHConfig);
        $this->nonProxiedDoHClient = Magic::$altervista || Magic::$zerowebhost || !$API->getSettings()->getConnection()->getUseDoH()
            ? new Rfc1035StubDnsResolver(null, $configProvider)
            : new Rfc8484StubDoHResolver($nonProxiedDoHConfig);

        $this->dnsConnector = new DnsSocketConnector(new Rfc1035StubDnsResolver(null, $configProvider));
        $this->webSocketConnector = new Rfc6455Connector(
            new Rfc6455ConnectionFactory(),
            $this->HTTPClient
        );
    }

    /**
     * Generate contexts.
     *
     * @return ConnectionContext[]
     */
    public function generateContexts(string $uri, ?ConnectContext $context = null): array
    {
        $ctxs = [];
        $combos = [
            [[DefaultStream::class, []], [BufferedRawStream::class, []]],
        ];
        $proxyCombos = [];
        foreach ($this->API->getSettings()->getConnection()->getProxies() as $proxy => $extras) {
            // MTProxies only carry MTProto.
            if ($proxy === ObfuscatedStream::class) {
                continue;
            }
            foreach ($extras as $extra) {
                foreach ($combos as $orig) {
                    $combo = [];
                    if ($orig[1][0] === BufferedRawStream::class) {
                        [$first, $second] = [\array_slice($orig, 0, 2), \array_slice($orig, 2)];
                        $first[] = [$proxy, $extra];
                        $combo = array_merge($first, $second);
                    } elseif (\in_array($orig[1][0], [WsStream::class, WssStream::class], true)) {
                        [$first, $second] = [\array_slice($orig, 0, 1), \array_slice($orig, 1)];
                        $first[] = [BufferedRawStream::class, []];
                        $first[] = [$proxy, $extra];
                        $combo = array_merge($first, $second);
                    }
                    $proxyCombos []= $combo;
                }
            }
        }
        if ($proxyCombos) {
            // Only through the proxies unless allowed: a direct connection would bypass them.
            $combos = $this->API->getSettings()->getConnection()->getDirectFallback()
                ? array_unique(array_merge($proxyCombos, $combos), SORT_REGULAR)
                : $proxyCombos;
        }

        $context ??= (new ConnectContext())->withConnectTimeout($this->API->getSettings()->getConnection()->getTimeout())->withBindTo($this->API->getSettings()->getConnection()->getBindTo());
        foreach ($combos as $combo) {
            foreach ([true, false] as $useDoH) {
                $ipv6Combos = [
                    $this->API->getSettings()->getConnection()->getIpv6(),
                    !$this->API->getSettings()->getConnection()->getIpv6(),
                ];
                foreach ($ipv6Combos as $ipv6) {
                    $ctx = (new ConnectionContext())->setSocketContext($context)->setUri($uri)->setIpv6($ipv6);
                    foreach ($combo as $stream) {
                        if ($stream[0] === DefaultStream::class && $stream[1] === []) {
                            $stream[1] = $useDoH ? new DoHConnector($this, $ctx) : $this->dnsConnector;
                        }
                        $ctx->addStream(...$stream);
                    }
                    $ctxs[] = $ctx;
                }
            }
        }
        return $ctxs;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use Amp\ByteStream\ReadableIterableStream;
use Amp\ByteStream\ReadableStream;
use Amp\ByteStream\ReadableStreamIteratorAggregate;
use Amp\Cancellation;
use Amp\CompositeCancellation;
use Amp\DeferredCancellation;
use Amp\DeferredFuture;
use Amp\ForbidCloning;
use Amp\Pipeline\Queue;
use Closure;
use danog\MadelineProto\Ipc\Client;
use IteratorAggregate;
use Throwable;

use function Amp\async;

/**
 * A readable stream with the contents of a file downloaded from Telegram.
 *
 * The stream can be serialized: once unserialized, the download is restarted from the end of the last chunk
 * returned by {@see self::read()} before serialization.
 *
 * The progress callback and the cancellation passed when creating the stream are not preserved by serialization.
 *
 * @implements IteratorAggregate<int, string>
 */
final class ResumableDownloadStream implements ReadableStream, ResumableStream, IteratorAggregate
{
    use ReadableStreamIteratorAggregate;
    use ForbidCloning;

    /** Number of downloaded chunks buffered in advance. */
    private const BUFFER_CHUNKS = 1;

    private MTProto|Client|null $API;
    private readonly string $session;
    /** Offset of the first byte not yet returned by read(). */
    private int $offset;
    private bool $eof = false;
    private bool $closed = false;
    private ?ReadableIterableStream $stream = null;
    private ?DeferredCancellation $deferredCancellation = null;
    private DeferredFuture $onClose;

    /**
     * @internal
     *
     * @param array                                         $media        Download info, as returned by getDownloadInfo.
     * @param (Closure(float, float, float): void)|null     $cb           Progress callback
     * @param int                                           $offset       Offset where to start downloading
     * @param int                                           $end          Offset where to end download
     */
    public function __construct(
        MTProto|Client $API,
        private readonly array $media,
        private ?Closure $cb,
        int $offset,
        private readonly int $end,
        private ?Cancellation $cancellation,
    ) {
        $this->API = $API;
        $this->session = $API->getSessionName();
        $this->offset = $offset;
        $this->onClose = new DeferredFuture;
    }

    #[\Override]
    public function read(?Cancellation $cancellation = null): ?string
    {
        if ($this->closed || $this->eof) {
            return null;
        }
        $this->stream ??= $this->startDownload();
        $chunk = $this->stream->read($cancellation);
        if ($chunk === null) {
            $this->eof = true;
            $this->stream = null;
            $this->deferredCancellation = null;
            return null;
        }
        $this->offset += \strlen($chunk);
        return $chunk;
    }

    /**
     * Skips the next bytes of the file without downloading them.
     *
     * @internal
     */
    public function skip(int $bytes): void
    {
        if ($bytes <= 0 || $this->closed || $this->eof) {
            return;
        }
        // Abort the running download, if any: the next read restarts it at the new offset.
        $this->deferredCancellation?->cancel();
        $this->deferredCancellation = null;
        $this->stream?->close();
        $this->stream = null;
        $this->offset += $bytes;
        $end = $this->getEnd();
        if ($end !== null && $this->offset >= $end) {
            $this->offset = $end;
            $this->eof = true;
        }
    }

    /**
     * Number of bytes left to read, if the size of the file is known.
     *
     * @internal
     *
     * @psalm-mutation-free
     */
    public function getRemainingSize(): ?int
    {
        if ($this->eof) {
            return 0;
        }
        $end = $this->getEnd();
        return $end === null ? null : max(0, $end - $this->offset);
    }

    /**
     * @psalm-mutation-free
     */
    private function getEnd(): ?int
    {
        if ($this->end !== -1) {
            return $this->end;
        }

        $size = $this->media['size'] ?? null;
        return \is_int($size) && $size > 0 ? $size : null;
    }

    /**
     * Starts downloading from the current offset.
     */
    private function startDownload(): ReadableIterableStream
    {
        $queue = new Queue(self::BUFFER_CHUNKS);
        $this->deferredCancellation = new DeferredCancellation;
        $cancellation = $this->deferredCancellation->getCancellation();
        if ($this->cancellation !== null) {
            $cancellation = new CompositeCancellation($this->cancellation, $cancellation);
        }
        $client = $this->getClient();
        $media = $this->media;
        $cb = $this->cb;
        $offset = $this->offset;
        $end = $this->end;
        async(static function () use ($client, $media, $cb, $offset, $end, $cancellation, $queue): void {
            try {
                $client->downloadToCallable(
                    $media,
                    static function (string $payload) use ($queue): int {
                        $queue->push($payload);
                        return \strlen($payload);
                    },
                    $cb,
                    false,
                    $offset,
                    $end,
                    null,
                    $cancellation
                );
                if (!$queue->isComplete()) {
                    $queue->complete();
                }
            } catch (Throwable $e) {
                if (!$queue->isComplete()) {
                    $queue->error($e);
                }
            }
        });
        return new ReadableIterableStream($queue->iterate());
    }

    /** @psalm-mutation-free */
    #[\Override]
    public function isReadable(): bool
    {
        return !$this->closed && !$this->eof;
    }

    #[\Override]
    public function close(): void
    {
        $this->closed = true;
        $this->deferredCancellation?->cancel();
        $this->deferredCancellation = null;
        $this->stream?->close();
        $this->stream = null;
        if (!$this->onClose->isComplete()) {
            $this->onClose->complete();
        }
    }

    /** @psalm-mutation-free */
    #[\Override]
    public function isClosed(): bool
    {
        return !$this->isReadable();
    }

    #[\Override]
    public function onClose(Closure $onClose): void
    {
        $this->onClose->getFuture()->finally($onClose);
    }

    /** @psalm-external-mutation-free */
    private function getClient(): MTProto|Client
    {
        return $this->API ??= Client::giveInstanceBySession($this->session);
    }

    /**
     * @internal
     *
     * @psalm-mutation-free
     *
     * @return array{session: string, media: array, offset: int, end: int, eof: bool}
     */
    public function __serialize(): array
    {
        return [
            'session' => $this->session,
            'media' => $this->media,
            'offset' => $this->offset,
            'end' => $this->end,
            'eof' => $this->eof,
        ];
    }

    /**
     * @internal
     *
     * @param array{session: string, media: array, offset: int, end: int, eof: bool} $data
     */
    public function __unserialize(array $data): void
    {
        $this->API = null;
        $this->session = $data['session'];
        $this->media = $data['media'];
        $this->offset = $data['offset'];
        $this->end = $data['end'];
        $this->eof = $data['eof'];
        $this->cb = null;
        $this->cancellation = null;
        $this->onClose = new DeferredFuture;
    }
}
<?php declare(strict_types=1);
/**
 * ChatGuestSendForbiddenError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * You join the discussion group before commenting, see [here &raquo;](https://core.telegram.org/api/discussion#requiring-users-to-join-the-group) for more info.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class ChatGuestSendForbiddenError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('CHAT_GUEST_SEND_FORBIDDEN', 'You join the discussion group before commenting, see [here &raquo;](https://core.telegram.org/api/discussion#requiring-users-to-join-the-group) for more info.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);

/**
 * RPCErrorException module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

/**
 * Represents a FLOOD_WAIT_ RPC error returned by telegram.
 */
final class FloodWaitError extends RateLimitError
{
}
<?php declare(strict_types=1);
/**
 * ScheduleTooMuchError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * There are too many scheduled messages.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class ScheduleTooMuchError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('SCHEDULE_TOO_MUCH', 'There are too many scheduled messages.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * BusinessPeerUsageMissingError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * You cannot send a message to a user through a [business connection](https://core.telegram.org/api/business#connected-bots) if the user hasn't recently contacted us.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class BusinessPeerUsageMissingError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('BUSINESS_PEER_USAGE_MISSING', 'You cannot send a message to a user through a [business connection](https://core.telegram.org/api/business#connected-bots) if the user hasn\'t recently contacted us.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * ScheduleDateTooLateError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * You can't schedule a message this far in the future.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class ScheduleDateTooLateError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('SCHEDULE_DATE_TOO_LATE', 'You can\'t schedule a message this far in the future.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * ChatSendVideosForbiddenError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * You can't send videos in this chat.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class ChatSendVideosForbiddenError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('CHAT_SEND_VIDEOS_FORBIDDEN', 'You can\'t send videos in this chat.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * UserBotToBotDisabledError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * Bot-to-bot messaging is disabled because one of the two bots hasn't enabled the Bot to Bot setting in @BotFather.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class UserBotToBotDisabledError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('USER_BOT_TO_BOT_DISABLED', 'Bot-to-bot messaging is disabled because one of the two bots hasn\'t enabled the Bot to Bot setting in @BotFather.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * FromMessageBotDisabledError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * Bots can't use fromMessage min constructors.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class FromMessageBotDisabledError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('FROM_MESSAGE_BOT_DISABLED', 'Bots can\'t use fromMessage min constructors.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * UsernameNotOccupiedError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * The provided username is not occupied.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class UsernameNotOccupiedError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('USERNAME_NOT_OCCUPIED', 'The provided username is not occupied.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * TodoItemDuplicateError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * Duplicate [checklist items](https://core.telegram.org/api/todo) detected.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class TodoItemDuplicateError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('TODO_ITEM_DUPLICATE', 'Duplicate [checklist items](https://core.telegram.org/api/todo) detected.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * PaymentUnsupportedError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * A detailed description of the error will be received separately as described [here &raquo;](https://core.telegram.org/api/errors#406-not-acceptable).
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class PaymentUnsupportedError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('PAYMENT_UNSUPPORTED', 'A detailed description of the error will be received separately as described [here &raquo;](https://core.telegram.org/api/errors#406-not-acceptable).', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * ChannelPrivateError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * You haven't joined this channel/supergroup.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class ChannelPrivateError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('CHANNEL_PRIVATE', 'You haven\'t joined this channel/supergroup.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * BotGamesDisabledError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * Games can't be sent to channels.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class BotGamesDisabledError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('BOT_GAMES_DISABLED', 'Games can\'t be sent to channels.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * QuickRepliesBotNotAllowedError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * [Quick replies](https://core.telegram.org/api/business#quick-reply-shortcuts) cannot be used by bots.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class QuickRepliesBotNotAllowedError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('QUICK_REPLIES_BOT_NOT_ALLOWED', '[Quick replies](https://core.telegram.org/api/business#quick-reply-shortcuts) cannot be used by bots.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);

/**
 * RPCErrorException module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

/**
 * Represents a FLOOD_PREMIUM_WAIT_ RPC error returned by telegram.
 */
final class FloodPremiumWaitError extends RateLimitError
{
}
<?php declare(strict_types=1);
/**
 * ChatSendAudiosForbiddenError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * You can't send audio messages in this chat.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class ChatSendAudiosForbiddenError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('CHAT_SEND_AUDIOS_FORBIDDEN', 'You can\'t send audio messages in this chat.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * ChatRestrictedError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * You can't send messages in this chat, you were restricted.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class ChatRestrictedError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('CHAT_RESTRICTED', 'You can\'t send messages in this chat, you were restricted.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * BotPaymentsDisabledError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * Please enable bot payments in botfather before calling this method.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class BotPaymentsDisabledError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('BOT_PAYMENTS_DISABLED', 'Please enable bot payments in botfather before calling this method.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * ChatSendPlainForbiddenError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * You can't send non-media (text) messages in this chat.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class ChatSendPlainForbiddenError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('CHAT_SEND_PLAIN_FORBIDDEN', 'You can\'t send non-media (text) messages in this chat.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * UserBannedInChannelError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * You're banned from sending messages in supergroups/channels.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class UserBannedInChannelError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('USER_BANNED_IN_CHANNEL', 'You\'re banned from sending messages in supergroups/channels.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * UserIsBlockedError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * You were blocked by this user.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class UserIsBlockedError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('USER_IS_BLOCKED', 'You were blocked by this user.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * WebpageNotFoundError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * A preview for the specified webpage `url` could not be generated.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class WebpageNotFoundError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('WEBPAGE_NOT_FOUND', 'A preview for the specified webpage `url` could not be generated.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * ChatSendMediaForbiddenError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * You can't send media in this chat.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class ChatSendMediaForbiddenError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('CHAT_SEND_MEDIA_FORBIDDEN', 'You can\'t send media in this chat.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * YouBlockedUserError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * You blocked this user.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class YouBlockedUserError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('YOU_BLOCKED_USER', 'You blocked this user.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * MsgIdInvalidError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * Invalid message ID provided.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class MsgIdInvalidError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('MSG_ID_INVALID', 'Invalid message ID provided.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * TopicDeletedError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * The specified topic was deleted.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class TopicDeletedError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('TOPIC_DELETED', 'The specified topic was deleted.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * BroadcastPublicVotersForbiddenError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * You can't forward polls with public voters.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class BroadcastPublicVotersForbiddenError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('BROADCAST_PUBLIC_VOTERS_FORBIDDEN', 'You can\'t forward polls with public voters.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * EncryptionAlreadyAcceptedError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * Secret chat already accepted.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class EncryptionAlreadyAcceptedError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('ENCRYPTION_ALREADY_ACCEPTED', 'Secret chat already accepted.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * TopicClosedError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * This topic was closed, you can't send messages to it anymore.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class TopicClosedError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('TOPIC_CLOSED', 'This topic was closed, you can\'t send messages to it anymore.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * SubscriptionExportMissingError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * You cannot send a [bot subscription invoice](https://core.telegram.org/api/subscriptions#bot-subscriptions) directly, you may only create invoice links using [payments.exportInvoice](https://core.telegram.org/method/payments.exportInvoice).
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class SubscriptionExportMissingError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('SUBSCRIPTION_EXPORT_MISSING', 'You cannot send a [bot subscription invoice](https://core.telegram.org/api/subscriptions#bot-subscriptions) directly, you may only create invoice links using [payments.exportInvoice](https://core.telegram.org/method/payments.exportInvoice).', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * PeerIdInvalidError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * The provided peer id is invalid.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class PeerIdInvalidError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('PEER_ID_INVALID', 'The provided peer id is invalid.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * ChatSendVoicesForbiddenError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * You can't send voice recordings in this chat.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class ChatSendVoicesForbiddenError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('CHAT_SEND_VOICES_FORBIDDEN', 'You can\'t send voice recordings in this chat.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * RequestTokenInvalidError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * The master DC did not accept the `request_token` from the CDN DC. Continue downloading the file from the master DC using upload.getFile.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class RequestTokenInvalidError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('REQUEST_TOKEN_INVALID', 'The master DC did not accept the `request_token` from the CDN DC. Continue downloading the file from the master DC using upload.getFile.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * ChatSendDocsForbiddenError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * You can't send documents in this chat.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class ChatSendDocsForbiddenError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('CHAT_SEND_DOCS_FORBIDDEN', 'You can\'t send documents in this chat.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * ScheduleStatusPrivateError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * Can't schedule until user is online, if the user's last seen timestamp is hidden by their privacy settings.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class ScheduleStatusPrivateError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('SCHEDULE_STATUS_PRIVATE', 'Can\'t schedule until user is online, if the user\'s last seen timestamp is hidden by their privacy settings.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * FileTokenInvalidError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * The master DC did not accept the `file_token` (e.g., the token has expired). Continue downloading the file from the master DC using upload.getFile.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class FileTokenInvalidError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('FILE_TOKEN_INVALID', 'The master DC did not accept the `file_token` (e.g., the token has expired). Continue downloading the file from the master DC using upload.getFile.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * ChatSendPollForbiddenError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * You can't send polls in this chat.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class ChatSendPollForbiddenError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('CHAT_SEND_POLL_FORBIDDEN', 'You can\'t send polls in this chat.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * PrivacyPremiumRequiredError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * You need a [Telegram Premium subscription](https://core.telegram.org/api/premium) to send a message to this user.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class PrivacyPremiumRequiredError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('PRIVACY_PREMIUM_REQUIRED', 'You need a [Telegram Premium subscription](https://core.telegram.org/api/premium) to send a message to this user.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * StoriesNeverCreatedError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * This peer hasn't ever posted any stories.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class StoriesNeverCreatedError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('STORIES_NEVER_CREATED', 'This peer hasn\'t ever posted any stories.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * QuickRepliesTooMuchError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * A maximum of [appConfig.`quick_replies_limit`](https://core.telegram.org/api/config#quick-replies-limit) shortcuts may be created, the limit was reached.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class QuickRepliesTooMuchError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('QUICK_REPLIES_TOO_MUCH', 'A maximum of [appConfig.`quick_replies_limit`](https://core.telegram.org/api/config#quick-replies-limit) shortcuts may be created, the limit was reached.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * EncryptionDeclinedError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * The secret chat was declined.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class EncryptionDeclinedError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('ENCRYPTION_DECLINED', 'The secret chat was declined.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * ReplyMessagesTooMuchError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * Each shortcut can contain a maximum of [appConfig.`quick_reply_messages_limit`](https://core.telegram.org/api/config#quick-reply-messages-limit) messages, the limit was reached.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class ReplyMessagesTooMuchError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('REPLY_MESSAGES_TOO_MUCH', 'Each shortcut can contain a maximum of [appConfig.`quick_reply_messages_limit`](https://core.telegram.org/api/config#quick-reply-messages-limit) messages, the limit was reached.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * VoiceMessagesForbiddenError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * This user's privacy settings forbid you from sending voice messages.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class VoiceMessagesForbiddenError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('VOICE_MESSAGES_FORBIDDEN', 'This user\'s privacy settings forbid you from sending voice messages.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * FileReferenceExpiredError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * File reference expired, it must be refetched as described in [the documentation](https://core.telegram.org/api/file-references).
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class FileReferenceExpiredError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('FILE_REFERENCE_EXPIRED', 'File reference expired, it must be refetched as described in [the documentation](https://core.telegram.org/api/file-references).', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * ChatForwardsRestrictedError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * You can't forward messages from a protected chat.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class ChatForwardsRestrictedError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('CHAT_FORWARDS_RESTRICTED', 'You can\'t forward messages from a protected chat.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * AllowPaymentRequiredError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * This peer only accepts [paid messages &raquo;](https://core.telegram.org/api/paid-messages): this error is only emitted for older layers without paid messages support, so the client must be updated in order to use paid messages.  .
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class AllowPaymentRequiredError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('ALLOW_PAYMENT_REQUIRED', 'This peer only accepts [paid messages &raquo;](https://core.telegram.org/api/paid-messages): this error is only emitted for older layers without paid messages support, so the client must be updated in order to use paid messages.  .', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * PollOptionDuplicateError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * Duplicate poll options provided.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class PollOptionDuplicateError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('POLL_OPTION_DUPLICATE', 'Duplicate poll options provided.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * CallAlreadyAcceptedError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * The call was already accepted.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class CallAlreadyAcceptedError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('CALL_ALREADY_ACCEPTED', 'The call was already accepted.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * TodoItemsTooMuchError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * You specified too many todo list items.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class TodoItemsTooMuchError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('TODO_ITEMS_TOO_MUCH', 'You specified too many todo list items.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);

/**
 * RPCErrorException module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use Amp\Cancellation;
use danog\MadelineProto\RPCErrorException;
use Exception;

use function Amp\delay;

/**
 * Represents a rate limiting RPC error returned by telegram.
 */
class RateLimitError extends RPCErrorException
{
    /**
     * Indicates the absolute expiration time of the flood wait.
     *
     * @var positive-int
     */
    public readonly int $expires;

    /** @internal */
    public function __construct(
        string $message,
        /** @var positive-int */
        public readonly int $waitTime,
        int $code,
        string $caller,
        ?Exception $previous = null
    ) {
        $this->expires = time() + $waitTime;
        parent::__construct($message, "A rate limit was encountered, please repeat the method call after $waitTime seconds", $code, $caller, $previous);
    }

    /**
     * Returns the required waiting period in seconds before repeating the RPC call.
     *
     * @return positive-int
     */
    public function getWaitTime(): int
    {
        return $this->waitTime;
    }

    /**
     * Returns the remaining waiting period in seconds before repeating the RPC call.
     *
     * @return int<0, max>
     */
    public function getWaitTimeLeft(): int
    {
        return max(0, $this->expires - time());
    }

    /**
     * Waits for the remaining waiting period.
     */
    public function wait(?Cancellation $cancellation = null): void
    {
        $left = $this->getWaitTimeLeft();
        if ($left > 0) {
            delay($left, cancellation: $cancellation);
        }
    }
}
<?php declare(strict_types=1);
/**
 * InputUserDeactivatedError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * The specified user was deleted.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class InputUserDeactivatedError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('INPUT_USER_DEACTIVATED', 'The specified user was deleted.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * ChatWriteForbiddenError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * You can't write in this chat.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class ChatWriteForbiddenError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('CHAT_WRITE_FORBIDDEN', 'You can\'t write in this chat.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * ChatSendRoundvideosForbiddenError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * You can't send round videos to this chat.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class ChatSendRoundvideosForbiddenError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('CHAT_SEND_ROUNDVIDEOS_FORBIDDEN', 'You can\'t send round videos to this chat.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * BalanceTooLowError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * The transaction cannot be completed because the current [Telegram Stars balance](https://core.telegram.org/api/stars) is too low.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class BalanceTooLowError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('BALANCE_TOO_LOW', 'The transaction cannot be completed because the current [Telegram Stars balance](https://core.telegram.org/api/stars) is too low.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * ChatSendStickersForbiddenError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * You can't send stickers in this chat.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class ChatSendStickersForbiddenError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('CHAT_SEND_STICKERS_FORBIDDEN', 'You can\'t send stickers in this chat.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * ButtonUserPrivacyRestrictedError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * The privacy setting of the user specified in a [inputKeyboardButtonUserProfile](https://core.telegram.org/constructor/inputKeyboardButtonUserProfile) button do not allow creating such a button.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class ButtonUserPrivacyRestrictedError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('BUTTON_USER_PRIVACY_RESTRICTED', 'The privacy setting of the user specified in a [inputKeyboardButtonUserProfile](https://core.telegram.org/constructor/inputKeyboardButtonUserProfile) button do not allow creating such a button.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * ScheduleBotNotAllowedError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * Bots cannot schedule messages.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class ScheduleBotNotAllowedError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('SCHEDULE_BOT_NOT_ALLOWED', 'Bots cannot schedule messages.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * EncryptionAlreadyDeclinedError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * The secret chat was already declined.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class EncryptionAlreadyDeclinedError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('ENCRYPTION_ALREADY_DECLINED', 'The secret chat was already declined.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * DcIdInvalidError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * The provided DC ID is invalid.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class DcIdInvalidError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('DC_ID_INVALID', 'The provided DC ID is invalid.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * QuizCorrectAnswersTooMuchError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * You specified too many correct answers in a quiz, quizzes can only have one right answer!
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class QuizCorrectAnswersTooMuchError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('QUIZ_CORRECT_ANSWERS_TOO_MUCH', 'You specified too many correct answers in a quiz, quizzes can only have one right answer!', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * ChatAdminRequiredError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * You must be an admin in this chat to do this.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class ChatAdminRequiredError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('CHAT_ADMIN_REQUIRED', 'You must be an admin in this chat to do this.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * WebpageCurlFailedError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * Failure while fetching the webpage with cURL.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class WebpageCurlFailedError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('WEBPAGE_CURL_FAILED', 'Failure while fetching the webpage with cURL.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * UserIsBotError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * Bots can't send messages to other bots.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class UserIsBotError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('USER_IS_BOT', 'Bots can\'t send messages to other bots.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * ChatSendPhotosForbiddenError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * You can't send photos in this chat.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class ChatSendPhotosForbiddenError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('CHAT_SEND_PHOTOS_FORBIDDEN', 'You can\'t send photos in this chat.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * ImageProcessFailedError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * Failure while processing image.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class ImageProcessFailedError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('IMAGE_PROCESS_FAILED', 'Failure while processing image.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);

/**
 * RPCErrorException module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use Amp\TimeoutException;
use danog\MadelineProto\RPCErrorException;

/**
 * Represents a request timeout RPC error returned by telegram (as opposed to one returned by MadelineProto, which will be a {@see TimeoutException}).
 *
 * @see TimeoutException
 */
final class TimeoutError extends RPCErrorException
{
}
<?php declare(strict_types=1);
/**
 * PremiumAccountRequiredError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * A premium account is required to execute this action.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class PremiumAccountRequiredError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('PREMIUM_ACCOUNT_REQUIRED', 'A premium account is required to execute this action.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * PasswordHashInvalidError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * The provided password hash is invalid.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class PasswordHashInvalidError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('PASSWORD_HASH_INVALID', 'The provided password hash is invalid.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * ChannelInvalidError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * The provided channel is invalid.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class ChannelInvalidError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('CHANNEL_INVALID', 'The provided channel is invalid.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * SessionPasswordNeededError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * 2FA is enabled, use a password to login.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class SessionPasswordNeededError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('SESSION_PASSWORD_NEEDED', '2FA is enabled, use a password to login.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * UsernameInvalidError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * The provided username is not valid.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class UsernameInvalidError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('USERNAME_INVALID', 'The provided username is not valid.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * ChannelMonoforumUnsupportedError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * [Monoforums](https://core.telegram.org/api/channel#monoforums) do not support this feature.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class ChannelMonoforumUnsupportedError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('CHANNEL_MONOFORUM_UNSUPPORTED', '[Monoforums](https://core.telegram.org/api/channel#monoforums) do not support this feature.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * PinnedDialogsTooMuchError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * Too many pinned dialogs.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class PinnedDialogsTooMuchError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('PINNED_DIALOGS_TOO_MUCH', 'Too many pinned dialogs.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * ChatForbiddenError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * This chat is not available to the current user.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class ChatForbiddenError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('CHAT_FORBIDDEN', 'This chat is not available to the current user.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * CallAlreadyDeclinedError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * The call was already declined.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class CallAlreadyDeclinedError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('CALL_ALREADY_DECLINED', 'The call was already declined.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * ChatSendGifsForbiddenError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * You can't send gifs in this chat.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class ChatSendGifsForbiddenError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('CHAT_SEND_GIFS_FORBIDDEN', 'You can\'t send gifs in this chat.', $code, $caller, $previous);
    }
}
<?php declare(strict_types=1);
/**
 * BusinessConnectionNotAllowedError error.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\RPCError;

use danog\MadelineProto\RPCErrorException;

/**
 * This method was invoked over a business connection using [invokeWithBusinessConnection](https://core.telegram.org/api/business#connected-bots), but either (1) we're a user, and users cannot invoke methods over a business connection; (2) we're a bot, but business mode was disabled in @botfather or (3); we're a bot, but this method cannot be invoked over a business connection.
 *
 * Note: this exception is part of the raw API, and thus is not covered by the backwards-compatibility promise.
 *
 * Always check the changelog when upgrading, and use tools like Psalm to easily upgrade your code.
 */
final class BusinessConnectionNotAllowedError extends RPCErrorException
{
    protected function __construct(int $code, string $caller, ?\Exception $previous = null)
    {
        parent::__construct('BUSINESS_CONNECTION_NOT_ALLOWED', 'This method was invoked over a business connection using [invokeWithBusinessConnection](https://core.telegram.org/api/business#connected-bots), but either (1) we\'re a user, and users cannot invoke methods over a business connection; (2) we\'re a bot, but business mode was disabled in @botfather or (3); we\'re a bot, but this method cannot be invoked over a business connection.', $code, $caller, $previous);
    }
}
<?php

declare(strict_types=1);

/**
 * Secret chat module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\SecretChats;

use Amp\Future;
use Amp\Sync\LocalKeyedMutex;
use Amp\Sync\LocalMutex;
use AssertionError;
use danog\AsyncOrm\Annotations\OrmMappedArray;
use danog\AsyncOrm\DbArray;
use danog\AsyncOrm\KeyType;
use danog\AsyncOrm\ValueType;
use danog\DialogId\DialogId;
use danog\MadelineProto\LegacyMigrator;
use danog\MadelineProto\Logger;
use danog\MadelineProto\Loop\Secret\SecretFeedLoop;
use danog\MadelineProto\Loop\Update\UpdateLoop;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\MTProtoTools\Crypt;
use danog\MadelineProto\ResponseException;
use danog\MadelineProto\RestoresDeclaredProperties;
use danog\MadelineProto\SecurityException;
use danog\MadelineProto\Tools;
use phpseclib4\Math\BigInteger;
use Revolt\EventLoop;
use Stringable;
use Webmozart\Assert\Assert;

/**
 * Represents a secret chat.
 * @internal
 *
 * @psalm-type TKey=array{auth_key: string, fingerprint: string, visualization_orig: string, visualization_46: string}
 */
final class SecretChatController implements Stringable
{
    use RestoresDeclaredProperties;
    use LegacyMigrator;

    /**
     * @var DbArray<int, array>
     */
    #[OrmMappedArray(KeyType::INT, ValueType::SCALAR)]
    private $incoming;
    /**
     * @var DbArray<int, array>
     */
    #[OrmMappedArray(KeyType::INT, ValueType::SCALAR)]
    private $outgoing;
    /**
     * @var DbArray<int, list{int, bool}> Seq, outgoing
     */
    #[OrmMappedArray(KeyType::INT, ValueType::SCALAR)]
    private $randomIdMap;
    private int $in_seq_no = 0;
    private int $out_seq_no = 0;
    private int $remote_in_seq_no = 0;
    private int $remoteLayer = 46;
    private int $updated;

    private RekeyState $rekeyState = RekeyState::IDLE;
    private ?int $rekeyExchangeId = null;
    private ?BigInteger $rekeyParam = null;
    /** @var ?TKey */
    private ?array $rekeyKey = null;

    /** @var ?TKey */
    private ?array $oldKey = null;

    private int $ttr = 100;

    private int $mtproto = 1;

    /** @var 0|1 */
    private int $in_seq_no_base;
    /** @var 0|1 */
    private int $out_seq_no_base;

    public readonly array $inputChat;
    private int $ttl = 0;

    private SecretFeedLoop $feedLoop;
    public readonly SecretChat $public;
    private LocalKeyedMutex $sentMutex;
    public function __construct(
        private readonly MTProto $API,
        /** @var TKey */
        private array $key,
        public readonly int $id,
        int $accessHash,
        bool $creator,
        int $otherID,
    ) {
        $this->inputChat = [
            '_' => 'inputEncryptedChat',
            'chat_id' => $id,
            'access_hash' => $accessHash,
        ];
        if ($creator) {
            $this->in_seq_no_base = 0;
            $this->out_seq_no_base = 1;
        } else {
            $this->in_seq_no_base = 1;
            $this->out_seq_no_base = 0;
        }
        $this->public = new SecretChat(
            $API,
            DialogId::fromSecretChatId($id),
            $creator,
            $otherID,
        );
        $this->updated = $this->public->created;
        $this->feedLoop = new SecretFeedLoop($API, $this);
        $this->rekeyMutex = new LocalMutex;
        $this->encryptMutex = new LocalMutex;
        $this->sentMutex = new LocalKeyedMutex;
        $this->init();
    }

    public function feed(array $update): void
    {
        $this->feedLoop->feed($update);
    }
    public function init(): void
    {
        $this->initDbProperties(
            $this->API->getDbSettings(),
            $this->API->getDbPrefix().'_SecretChatController_'.$this->id.'_'
        );
    }

    public function wakeupFeedLoop(): void
    {
        $this->API->loginState->subscribe($this->feedLoop);
    }

    /**
     * @psalm-mutation-free
     */
    public function __serialize(): array
    {
        $vars = get_object_vars($this);
        unset($vars['rekeyMutex'], $vars['encryptMutex'], $vars['sentMutex']);

        return $vars;
    }

    public function __unserialize(array $data): void
    {
        $this->restoreDeclaredProperties($data);
        $this->rekeyMutex = new LocalMutex;
        $this->encryptMutex = new LocalMutex;
        $this->sentMutex = new LocalKeyedMutex;
    }

    /**
     * Discard secret chat.
     */
    public function discard(): void
    {
        $this->API->discardSecretChat($this->id);
    }
    public function notifyLayer(): void
    {
        $this->API->methodCallAsyncRead('messages.sendEncryptedService', ['peer' => $this->id, 'message' => ['_' => 'decryptedMessageService', 'action' => ['_' => 'decryptedMessageActionNotifyLayer', 'layer' => $this->API->getTL()->getSecretLayer()]]]);
    }
    private LocalMutex $rekeyMutex;
    /**
     * Rekey secret chat.
     */
    private function rekey(): void
    {
        if ($this->rekeyState !== RekeyState::IDLE) {
            return;
        }
        $lock = $this->rekeyMutex->acquire();
        try {
            if ($this->rekeyState !== RekeyState::IDLE) {
                return;
            }
            $dh_config = $this->API->getDhConfig();
            $this->API->logger('Rekeying secret chat '.$this.'...', Logger::VERBOSE);
            $this->API->logger('Generating a...', Logger::VERBOSE);
            $a = new BigInteger(Tools::random(256), 256);
            $this->API->logger('Generating g_a...', Logger::VERBOSE);
            $g_a = $dh_config['g']->powMod($a, $dh_config['p']);
            Crypt::checkG($g_a, $dh_config['p']);
            $this->rekeyState = RekeyState::REQUESTED;
            $this->rekeyExchangeId = Tools::randomInt();
            $this->rekeyParam = $a;
            $this->API->methodCallAsyncRead('messages.sendEncryptedService', ['peer' => $this->id, 'message' => ['_' => 'decryptedMessageService', 'action' => ['_' => 'decryptedMessageActionRequestKey', 'g_a' => $g_a->toBytes(), 'exchange_id' => $this->rekeyExchangeId]]]);
            $this->API->updaters[UpdateLoop::GENERIC]->resume();
        } finally {
            EventLoop::queue($lock->release(...));
        }
    }
    /**
     * Accept rekeying.
     *
     * @param array $params Parameters
     */
    private function acceptRekey(array $params): void
    {
        $lock = $this->rekeyMutex->acquire();
        try {
            if ($this->rekeyState !== RekeyState::IDLE) {
                if ($this->rekeyExchangeId > $params['exchange_id']) {
                    return;
                }
                if ($this->rekeyExchangeId === $params['exchange_id']) {
                    $this->rekeyState = RekeyState::IDLE;
                    return;
                }
            }
            $this->API->logger('Accepting rekeying of '.$this.'...', Logger::VERBOSE);
            $dh_config = $this->API->getDhConfig();
            $this->API->logger('Generating b...', Logger::VERBOSE);
            $b = new BigInteger(Tools::random(256), 256);
            $params['g_a'] = new BigInteger((string) $params['g_a'], 256);
            Crypt::checkG($params['g_a'], $dh_config['p']);
            $key = ['auth_key' => str_pad($params['g_a']->powMod($b, $dh_config['p'])->toBytes(), 256, \chr(0), STR_PAD_LEFT)];
            $key['fingerprint'] = substr(sha1($key['auth_key'], true), -8);
            $key['visualization_orig'] = $this->key['visualization_orig'];
            $key['visualization_46'] = substr(hash('sha256', $key['auth_key'], true), 20);

            $this->rekeyState = RekeyState::ACCEPTED;
            $this->rekeyExchangeId = $params['exchange_id'];
            $this->rekeyKey = $key;

            $g_b = $dh_config['g']->powMod($b, $dh_config['p']);
            Crypt::checkG($g_b, $dh_config['p']);
            $this->API->methodCallAsyncRead('messages.sendEncryptedService', ['peer' => $this->id, 'message' => ['_' => 'decryptedMessageService', 'action' => ['_' => 'decryptedMessageActionAcceptKey', 'g_b' => $g_b->toBytes(), 'exchange_id' => $params['exchange_id'], 'key_fingerprint' => $key['fingerprint']]]]);
            $this->API->updaters[UpdateLoop::GENERIC]->resume();
        } finally {
            EventLoop::queue($lock->release(...));
        }
    }
    /**
     * Commit rekeying of secret chat.
     *
     * @param array $params Parameters
     */
    private function commitRekey(array $params): void
    {
        $lock = $this->rekeyMutex->acquire();
        try {
            if ($this->rekeyState !== RekeyState::REQUESTED || $this->rekeyExchangeId !== $params['exchange_id']) {
                $this->rekeyState = RekeyState::IDLE;
                return;
            }
            $this->API->logger('Committing rekeying of '.$this.'...', Logger::VERBOSE);
            $dh_config = ($this->API->getDhConfig());
            $params['g_b'] = new BigInteger((string) $params['g_b'], 256);
            Crypt::checkG($params['g_b'], $dh_config['p']);
            \assert($this->rekeyParam !== null);
            $key = ['auth_key' => str_pad($params['g_b']->powMod($this->rekeyParam, $dh_config['p'])->toBytes(), 256, \chr(0), STR_PAD_LEFT)];
            $key['fingerprint'] = substr(sha1($key['auth_key'], true), -8);
            $key['visualization_orig'] = $this->key['visualization_orig'];
            $key['visualization_46'] = substr(hash('sha256', $key['auth_key'], true), 20);
            if ($key['fingerprint'] !== $params['key_fingerprint']) {
                $this->API->methodCallAsyncRead('messages.sendEncryptedService', ['peer' => $this->id, 'message' => ['_' => 'decryptedMessageService', 'action' => ['_' => 'decryptedMessageActionAbortKey', 'exchange_id' => $params['exchange_id']]]]);
                throw new SecurityException('Invalid key fingerprint!');
            }
            $this->API->methodCallAsyncRead('messages.sendEncryptedService', ['peer' => $this->id, 'message' => ['_' => 'decryptedMessageService', 'action' => ['_' => 'decryptedMessageActionCommitKey', 'exchange_id' => $params['exchange_id'], 'key_fingerprint' => $key['fingerprint']]]]);
            $this->rekeyState = RekeyState::IDLE;
            $this->oldKey = $this->key;
            $this->key = $key;
            $this->ttr = 100;
            $this->updated = time();
            $this->API->updaters[UpdateLoop::GENERIC]->resume();
        } finally {
            EventLoop::queue($lock->release(...));
        }
    }
    /**
     * Complete rekeying.
     *
     * @param array $params Parameters
     */
    private function completeRekey(array $params): void
    {
        $lock = $this->rekeyMutex->acquire();
        try {
            if ($this->rekeyState !== RekeyState::ACCEPTED || $this->rekeyExchangeId !== $params['exchange_id']) {
                return;
            }
            \assert($this->rekeyKey !== null);
            if ($this->rekeyKey['fingerprint'] !== $params['key_fingerprint']) {
                $this->API->methodCallAsyncRead('messages.sendEncryptedService', ['peer' => $this->id, 'message' => ['_' => 'decryptedMessageService', 'action' => ['_' => 'decryptedMessageActionAbortKey', 'exchange_id' => $params['exchange_id']]]]);
                throw new SecurityException('Invalid key fingerprint!');
            }
            $this->API->logger('Completing rekeying of secret chat '.$this.'...', Logger::VERBOSE);
            $this->rekeyState = RekeyState::IDLE;
            $this->oldKey = $this->key;
            $this->key = $this->rekeyKey;
            $this->ttr = 100;
            $this->updated = time();
            $this->API->methodCallAsyncRead('messages.sendEncryptedService', ['peer' => $this->id, 'message' => ['_' => 'decryptedMessageService', 'action' => ['_' => 'decryptedMessageActionNoop']]]);
            $this->API->logger('Secret chat '.$this.' rekeyed successfully!', Logger::VERBOSE);
        } finally {
            EventLoop::queue($lock->release(...));
        }
    }

    private LocalMutex $encryptMutex;
    /**
     * Encrypt secret chat message.
     * @internal
     */
    public function encryptSecretMessage(array $body, Future $promise): array
    {
        $body['peer'] = $this->inputChat;
        if (isset($body['data'])) {
            return $body;
        }

        $lock = $this->encryptMutex->acquire();
        try {
            $this->ttr--;
            if ($this->remoteLayer > 8
                && ($this->ttr <= 0 || time() - $this->updated > 7 * 24 * 60 * 60)
                && $this->rekeyState === RekeyState::IDLE
            ) {
                EventLoop::queue($this->rekey(...));
            }

            $this->encryptSecretMessageInner($body);

            $promise->finally($lock->release(...));
            return $body;
        } catch (\Throwable $e) {
            $lock->release();
            throw $e;
        }
    }
    private function encryptSecretMessageInner(array &$body): void
    {
        $message = $body['message'];
        $randomId = $message['random_id'] = Tools::randomInt();
        Assert::true($this->remoteLayer > 8);
        $body['_'] = 'updateNewOutgoingEncryptedMessage';
        $body['message'] = [
            '_' => $body['method'] === 'messages.sendEncryptedService'
                ? 'encryptedMessageService'
                : 'encryptedMessage',
            'message' => $message,
            'chat_id' => $this->id,
        ]; // Not sent
        $message = ['_' => 'decryptedMessageLayer', 'layer' => $this->remoteLayer, 'in_seq_no' => $this->generateSecretInSeqNo(), 'out_seq_no' => $this->generateSecretOutSeqNo(), 'message' => $message];
        $body['seq'] = $seq = $this->out_seq_no++; // Not sent
        $constructor = $this->remoteLayer === 8 ? 'DecryptedMessage' : 'DecryptedMessageLayer';
        $message = $this->API->getTL()->serializeObject(['type' => $constructor], $message, $constructor, $this->remoteLayer);
        $message = Tools::packUnsignedInt(\strlen($message)).$message;
        if ($this->mtproto === 2) {
            $padding = Tools::posmod(-\strlen($message), 16);
            if ($padding < 12) {
                $padding += 16;
            }
            $message .= Tools::random($padding);
            $message_key = substr(hash('sha256', substr($this->key['auth_key'], 88 + ($this->public->creator ? 0 : 8), 32).$message, true), 8, 16);
            [$aes_key, $aes_iv] = Crypt::kdf($message_key, $this->key['auth_key'], $this->public->creator);
        } else {
            $message_key = substr(sha1($message, true), -16);
            [$aes_key, $aes_iv] = Crypt::oldKdf($message_key, $this->key['auth_key'], true);
            $message .= Tools::random(Tools::posmod(-\strlen($message), 16));
        }
        $body['data'] = $this->key['fingerprint'].$message_key.Crypt::igeEncrypt($message, $aes_key, $aes_iv);
        $this->outgoing[$seq] = $body;
        $this->randomIdMap[$randomId] = [$seq, true];
    }

    public function handleSent(array $request, array $response): array
    {
        $lock = $this->sentMutex->acquire((string) $request['seq']);
        try {
            $msg = $this->outgoing[$request['seq']];
            if (!isset($msg['message']['date'])) {
                $msg['message']['date'] = $response['date'];
                $msg['message']['decrypted_message'] = $msg['message']['message'];
                unset($msg['message']['message']);
                if (isset($msg['message']['decrypted_message']['media'])
                    && $msg['message']['decrypted_message']['media']['_'] !== 'decryptedMessageMediaEmpty'
                ) {
                    $msg['message']['file'] = $response['file'];
                    $msg['message']['decrypted_message']['media']['file'] = $msg['message']['file'];
                    $msg['message']['decrypted_message']['media']['date'] = $msg['message']['date'];
                    $msg['message']['decrypted_message']['media']['ttl_seconds'] = $msg['message']['decrypted_message']['ttl'];
                }
                $msg['message']['decrypted_message']['out'] = true;
                $msg['message']['decrypted_message']['date'] = $msg['message']['date'];
                $msg['message']['decrypted_message']['chat_id'] = $msg['message']['chat_id'];
                $this->outgoing[$request['seq']] = $msg;
                EventLoop::queue($this->API->saveUpdate(...), $msg);
            }
            return $msg;
        } finally {
            EventLoop::queue($lock->release(...));
        }
    }
    private static function transformDecryptedUpdate(array &$update): void
    {
        $decryptedMessage = &$update['message']['decrypted_message'];

        $decryptedMessage['out'] = false;
        $decryptedMessage['date'] = $update['message']['date'];
        $decryptedMessage['chat_id'] = $update['message']['chat_id'];

        if ($decryptedMessage['_'] === 'decryptedMessage'
            && isset($decryptedMessage['media'])
            && $decryptedMessage['media']['_'] !== 'decryptedMessageMediaEmpty'
        ) {
            $decryptedMessage['media']['file'] = $update['message']['file'];
            $decryptedMessage['media']['date'] = $update['message']['date'];
            $decryptedMessage['media']['ttl_seconds'] = $decryptedMessage['ttl'];
        }
    }
    private function handleDecryptedUpdate(array $update): void
    {
        $decryptedMessage = $update['message']['decrypted_message'];
        if ($decryptedMessage['_'] === 'decryptedMessage') {
            $this->API->saveUpdate($update);
            return;
        }
        if ($decryptedMessage['_'] === 'decryptedMessageService') {
            $action = $decryptedMessage['action'];
            switch ($action['_']) {
                case 'decryptedMessageActionRequestKey':
                    $this->acceptRekey($action);
                    return;
                case 'decryptedMessageActionAcceptKey':
                    $this->commitRekey($action);
                    return;
                case 'decryptedMessageActionCommitKey':
                    $this->completeRekey($action);
                    return;
                case 'decryptedMessageActionNotifyLayer':
                    if ($action['layer'] > $this->remoteLayer) {
                        $this->API->logger("Applying layer {$action['layer']} notification in $this");
                        $this->remoteLayer = $action['layer'];
                        if ($action['layer'] >= 46 && time() - $this->public->created > 15) {
                            $this->notifyLayer();
                        }
                        if ($action['layer'] >= 73) {
                            $this->mtproto = 2;
                        }
                    } else {
                        $this->API->logger("Ignoring layer {$action['layer']} notification in $this");
                    }
                    return;
                case 'decryptedMessageActionSetMessageTTL':
                    $this->ttl = $action['ttl_seconds'];
                    $this->API->saveUpdate($update);
                    return;
                case 'decryptedMessageActionNoop':
                    return;
                case 'decryptedMessageActionResend':
                    $this->handleResend($action);
                    return;
                default:
                    $this->API->saveUpdate($update);
            }
            return;
        }
        throw new ResponseException('Unrecognized decrypted message received: '.var_export($update, true));
    }
    private function handleResend(array &$action): void
    {
        if (isset($action['handled'])) {
            return;
        }
        $this->API->logger("Resending messages for $this: ".json_encode($action), Logger::WARNING);
        $action['start_seq_no'] -= $this->out_seq_no_base;
        $action['end_seq_no'] -= $this->out_seq_no_base;
        $action['start_seq_no'] >>= 1;
        $action['end_seq_no'] >>= 1;
        $action['handled'] = true;
        $this->API->logger("Resending messages for $this (after): ".json_encode($action), Logger::WARNING);
        for ($seq = $action['start_seq_no']; $seq <= $action['end_seq_no']; $seq++) {
            $msg = $this->outgoing[$seq];
            $this->API->methodCallAsyncRead($msg['method'], $msg[$seq]);
        }
    }
    /**
     * Handle encrypted update.
     *
     * @internal
     */
    public function handleEncryptedUpdate(array $message): bool
    {
        $message['message']['bytes'] = (string) $message['message']['bytes'];
        $auth_key_id = substr($message['message']['bytes'], 0, 8);
        $old = false;
        if ($auth_key_id !== $this->key['fingerprint']) {
            if (isset($this->oldKey['fingerprint'])) {
                if ($auth_key_id !== $this->oldKey['fingerprint']) {
                    $this->discard();
                    throw new SecurityException('Key fingerprint mismatch');
                }
                $old = true;
            } else {
                $this->discard();
                throw new SecurityException('Key fingerprint mismatch');
            }
        }
        $message_key = substr($message['message']['bytes'], 8, 16);
        $encrypted_data = substr($message['message']['bytes'], 24);
        if ($this->mtproto === 2) {
            $this->API->logger('Trying MTProto v2 decryption for '.$this.'...', Logger::NOTICE);
            try {
                $message_data = $this->tryMTProtoV2Decrypt($message_key, $old, $encrypted_data);
                $this->API->logger('MTProto v2 decryption OK for '.$this.'...', Logger::NOTICE);
            } catch (SecurityException $e) {
                if ($this->remoteLayer >= 73) {
                    // && !$this->waitingGaps
                    throw $e;
                }
                $this->API->logger('MTProto v2 decryption failed with message '.$e->getMessage().', trying MTProto v1 decryption for '.$this.'...', Logger::NOTICE);
                $message_data = $this->tryMTProtoV1Decrypt($message_key, $old, $encrypted_data);
                $this->API->logger('MTProto v1 decryption OK for '.$this.'...', Logger::NOTICE);
                $this->mtproto = 1;
            }
        } else {
            $this->API->logger('Trying MTProto v1 decryption for '.$this.'...', Logger::NOTICE);
            try {
                $message_data = $this->tryMTProtoV1Decrypt($message_key, $old, $encrypted_data);
                $this->API->logger('MTProto v1 decryption OK for '.$this.'...', Logger::NOTICE);
            } catch (SecurityException $e) {
                $this->API->logger('MTProto v1 decryption failed with message '.$e->getMessage().', trying MTProto v2 decryption for '.$this.'...', Logger::NOTICE);
                $message_data = $this->tryMTProtoV2Decrypt($message_key, $old, $encrypted_data);
                $this->API->logger('MTProto v2 decryption OK for '.$this.'...', Logger::NOTICE);
                $this->mtproto = 2;
            }
        }
        $deserialized = $this->API->getTL()->deserialize($message_data, ['type' => '', 'encrypted' => true, 'connection' => null]);
        $this->ttr--;
        if (($this->ttr <= 0 || time() - $this->updated > 7 * 24 * 60 * 60) && $this->rekeyState === RekeyState::IDLE) {
            $this->rekey();
        }
        unset($message['message']['bytes']);
        $message['message']['decrypted_message'] = $deserialized;

        if ($message['message']['decrypted_message']['_'] === 'decryptedMessageLayer') {
            foreach ($this->checkSecretOutSeqNo($message) as $message) {
                $this->checkSecretInSeqNo(
                    $message['message']['decrypted_message']['in_seq_no']
                );
                $layer = $message['message']['decrypted_message']['layer'];
                if ($layer >= 46 && $layer > $this->remoteLayer) {
                    $this->remoteLayer = $layer;
                    if (time() - $this->public->created > 15) {
                        $this->notifyLayer();
                    }
                }
                $message['message']['decrypted_message'] = $message['message']['decrypted_message']['message'];
                self::transformDecryptedUpdate($message);
                $this->incoming[$seq = $this->in_seq_no++] = $message;
                $this->randomIdMap[$message['message']['decrypted_message']['random_id']] = [$seq, false];
                $this->handleDecryptedUpdate($message);
            }
        } else {
            self::transformDecryptedUpdate($message);
            $this->handleDecryptedUpdate($message);
        }
        return true;
    }

    private function tryMTProtoV1Decrypt(string $message_key, bool $old, string $encrypted_data): string
    {
        $key = $old ? $this->oldKey : $this->key;
        \assert($key !== null);
        [$aes_key, $aes_iv] = Crypt::oldKdf($message_key, $key['auth_key'], true);
        $decrypted_data = Crypt::igeDecrypt($encrypted_data, $aes_key, $aes_iv);
        $message_data_length = unpack('V', substr($decrypted_data, 0, 4))[1];
        $message_data = substr($decrypted_data, 4, $message_data_length);
        if ($message_data_length > \strlen($decrypted_data)) {
            throw new SecurityException('message_data_length is too big');
        }
        if ($message_key != substr(sha1(substr($decrypted_data, 0, 4 + $message_data_length), true), -16)) {
            throw new SecurityException('Msg_key mismatch');
        }
        if (\strlen($decrypted_data) - 4 - $message_data_length > 15) {
            throw new SecurityException('difference between message_data_length and the length of the remaining decrypted buffer is too big');
        }
        if (\strlen($decrypted_data) % 16 != 0) {
            throw new SecurityException("Length of decrypted data is not divisible by 16");
        }
        return $message_data;
    }

    private function tryMTProtoV2Decrypt(string $message_key, bool $old, string $encrypted_data): string
    {
        $key = $old ? $this->oldKey : $this->key;
        \assert($key !== null);
        $key = $key['auth_key'];
        [$aes_key, $aes_iv] = Crypt::kdf($message_key, $key, !$this->public->creator);
        $decrypted_data = Crypt::igeDecrypt($encrypted_data, $aes_key, $aes_iv);
        if ($message_key != substr(hash('sha256', substr($key, 88 + ($this->public->creator ? 8 : 0), 32).$decrypted_data, true), 8, 16)) {
            throw new SecurityException('Msg_key mismatch');
        }
        $message_data_length = unpack('V', substr($decrypted_data, 0, 4))[1];
        $message_data = substr($decrypted_data, 4, $message_data_length);
        if ($message_data_length > \strlen($decrypted_data)) {
            throw new SecurityException('message_data_length is too big');
        }
        if (\strlen($decrypted_data) - 4 - $message_data_length < 12) {
            throw new SecurityException('padding is too small');
        }
        if (\strlen($decrypted_data) - 4 - $message_data_length > 1024) {
            throw new SecurityException('padding is too big');
        }
        if (\strlen($decrypted_data) % 16 != 0) {
            throw new SecurityException("Length of decrypted data is not divisible by 16");
        }
        return $message_data;
    }

    private function checkSecretInSeqNo(int $seqno): void
    {
        $seqno = ($seqno - $this->out_seq_no_base) >> 1;
        if ($seqno < $this->remote_in_seq_no) {
            $this->API->logger("Discarding $this, in_seq_no is decreasing", Logger::LEVEL_FATAL);
            $this->discard();
            throw new SecurityException('in_seq_no is decreasing');
        }
        if ($seqno > $this->out_seq_no + 1) {
            $this->API->logger("Discarding $this, in_seq_no is too big", Logger::LEVEL_FATAL);
            $this->discard();
            throw new SecurityException('in_seq_no is too big');
        }
        $this->remote_in_seq_no = $seqno;
    }

    private ?int $gapEnd = null;
    private ?int $gapQueueSeq = null;
    private array $gapQueue = [];
    private array $gapQuery = [];

    private function checkSecretOutSeqNo(array $message): \Generator
    {
        $seqno = $message['message']['decrypted_message']['out_seq_no'];
        $seqno = ($seqno - $this->in_seq_no_base) >> 1;
        $C_plus_one = $this->in_seq_no;
        //$this->API->logger($C, $seqno);
        if ($seqno < $C_plus_one) {
            // <= C
            $this->API->logger("WARNING: dropping repeated message in $this with seqno $seqno");
            return;
        }
        if ($seqno > $C_plus_one) {
            // > C+1
            if ($message['message']['decrypted_message']['message']['_'] === 'decryptedMessageService'
                && $message['message']['decrypted_message']['message']['action']['_'] === 'decryptedMessageActionResend'
            ) {
                $this->handleResend($message['message']['decrypted_message']['message']['action']);
            }
            if ($this->gapEnd !== null) {
                // Already recovering gap...
                $C_plus_one_gap = $this->gapQueueSeq;
                if ($seqno < $C_plus_one_gap) {
                    // <= C
                    $this->API->logger("WARNING: dropping repeated message in $this with seqno $seqno while recovering gaps");
                    return;
                }
                if ($seqno > $C_plus_one_gap) {
                    // > C+1
                    $this->API->logger("Discarding $this because out_seq_no gap detected: ($seqno > $C_plus_one_gap), but already recovering gap!", Logger::LEVEL_FATAL);
                    $this->discard();
                    throw new SecurityException("Additional out_seq_no gap detected!");
                }
                $this->API->logger("WARNING: queueing message $seqno in $this while recovering gaps");
                $this->gapQueue []= $message;
                $this->gapQueueSeq = $seqno+1;
                $this->API->methodCallAsyncRead('messages.sendEncryptedService', $this->gapQuery);
                return;
            }
            $this->API->logger("Requesting resending in $this, out_seq_no gap detected: ($seqno > $C_plus_one)", Logger::LEVEL_FATAL);
            $this->gapEnd = $seqno-1;
            $this->gapQueue = [$message];
            $this->gapQueueSeq = $seqno+1;
            $this->gapQuery = ['peer' => $this->id, 'message' => ['_' => 'decryptedMessageService', 'action' => [
                '_' => 'decryptedMessageActionResend',
                'start_seq_no' => $C_plus_one * 2 + $this->in_seq_no_base,
                'end_seq_no' => $this->gapEnd * 2 + $this->in_seq_no_base,
            ]]];
            $this->API->methodCallAsyncRead('messages.sendEncryptedService', $this->gapQuery);
            return;
        }
        yield $message;
        if ($seqno === $this->gapEnd) {
            $queue = $this->gapQueue;
            $this->gapQueue = [];
            $this->gapQueueSeq = null;
            $this->gapEnd = null;
            yield from $queue;
        }
    }
    /**
     * @psalm-mutation-free
     */
    private function generateSecretInSeqNo(): int
    {
        return $this->remoteLayer > 8 ? $this->in_seq_no * 2 + $this->in_seq_no_base : -1;
    }
    /**
     * @psalm-mutation-free
     */
    private function generateSecretOutSeqNo(): int
    {
        return $this->remoteLayer > 8 ? $this->out_seq_no * 2 + $this->out_seq_no_base : -1;
    }

    public function getMessage(int $random_id): array
    {
        $result = $this->randomIdMap[$random_id];
        if ($result === null) {
            throw new AssertionError("The secret message with ID $random_id does not exist!");
        }
        [$seq, $outgoing] = $result;
        return $outgoing ? $this->outgoing[$seq] : $this->incoming[$seq];
    }
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function __toString(): string
    {
        return "secret chat {$this->id}";
    }
}
<?php

declare(strict_types=1);

/**
 * Secret chat module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\SecretChats;

use danog\MadelineProto\Ipc\IpcCapable;
use danog\MadelineProto\MTProto;

/**
 * Represents a secret chat.
 */
final class SecretChat extends IpcCapable
{
    /** Creation date */
    public readonly int $created;
    /** @internal */
    public function __construct(
        MTProto $API,
        /** Secret chat ID */
        public readonly int $chatId,
        /** Whether we created this chat */
        public readonly bool $creator,
        /** User ID of the other peer in the chat */
        public readonly int $otherID,
    ) {
        parent::__construct($API);
        $this->created = time();
    }

    /**
     * Gets a secret chat message.
     *
     * @param integer $randomId Secret chat message ID.
     */
    public function getMessage(int $randomId): \danog\MadelineProto\EventHandler\Message\SecretMessage
    {
        return $this->getClient()->getSecretMessage($this->chatId, $randomId);
    }
}
<?php

declare(strict_types=1);

/**
 * AuthKeyHandler module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\SecretChats;

use Amp\Sync\LocalKeyedMutex;
use AssertionError;
use danog\DialogId\DialogId;
use danog\MadelineProto\EventHandler\Message\SecretMessage;
use danog\MadelineProto\Logger;
use danog\MadelineProto\Loop\Update\UpdateLoop;
use danog\MadelineProto\MTProtoTools\Crypt;
use danog\MadelineProto\RPCError\EncryptionAlreadyAcceptedError;
use danog\MadelineProto\RPCError\EncryptionAlreadyDeclinedError;
use danog\MadelineProto\SecretPeerNotInDbException;
use danog\MadelineProto\SecurityException;
use danog\MadelineProto\Tools;
use phpseclib4\Math\BigInteger;
use Revolt\EventLoop;

use const STR_PAD_LEFT;

/**
 * Manages secret chats.
 *
 * https://core.telegram.org/api/end-to-end
 *
 * @internal
 */
trait AuthKeyHandler
{
    /**
     * Temporary requested secret chats.
     *
     */
    protected array $temp_requested_secret_chats = [];
    /**
     * Secret chats.
     *
     * @var array<int, SecretChatController>
     */
    public array $secretChats = [];
    /**
     * Request secret chat.
     *
     * @param mixed $user User to start secret chat with
     */
    public function requestSecretChat(mixed $user): int
    {
        $user = $this->getInfo($user);
        if ($user['type'] !== 'user') {
            throw new AssertionError("Can only create a secret chat with a user!");
        }
        $user = $user['user_id'];
        $this->logger->logger('Creating secret chat with '.$user.'...', Logger::VERBOSE);
        $dh_config = ($this->getDhConfig());
        $this->logger->logger('Generating a...', Logger::VERBOSE);
        $a = new BigInteger(Tools::random(256), 256);
        $this->logger->logger('Generating g_a...', Logger::VERBOSE);
        $g_a = $dh_config['g']->powMod($a, $dh_config['p']);
        Crypt::checkG($g_a, $dh_config['p']);
        $res = $this->methodCallAsyncRead('messages.requestEncryption', ['user_id' => $user, 'g_a' => $g_a->toBytes()]);
        $this->temp_requested_secret_chats[$res['id']] = $a;
        $this->updaters[UpdateLoop::GENERIC]->resume();
        $this->logger->logger('Secret chat '.$res['id'].' requested successfully!', Logger::NOTICE);
        return $res['id'];
    }
    private LocalKeyedMutex $acceptChatMutex;
    private LocalKeyedMutex $confirmChatMutex;
    /**
     * Accept secret chat.
     *
     * @param array $params Secret chat ID
     */
    public function acceptSecretChat(array $params): void
    {
        $lock = $this->acceptChatMutex->acquire((string) $params['id']);
        try {
            if (isset($this->secretChats[$params['id']])) {
                $this->logger->logger("I've already accepted secret chat ".$params['id']);
                return;
            }
            $dh_config = $this->getDhConfig();
            $this->logger->logger('Generating b...', Logger::VERBOSE);
            $b = new BigInteger(Tools::random(256), 256);
            $params['g_a'] = new BigInteger((string) $params['g_a'], 256);
            Crypt::checkG($params['g_a'], $dh_config['p']);
            $key = ['auth_key' => str_pad($params['g_a']->powMod($b, $dh_config['p'])->toBytes(), 256, \chr(0), STR_PAD_LEFT)];
            //$this->logger->logger($key);
            $key['fingerprint'] = substr(sha1($key['auth_key'], true), -8);
            $key['visualization_orig'] = substr(sha1($key['auth_key'], true), 16);
            $key['visualization_46'] = substr(hash('sha256', $key['auth_key'], true), 20);
            $this->secretChats[$params['id']] = $chat = new SecretChatController(
                $this,
                $key,
                $params['id'],
                $params['access_hash'],
                false,
                $params['admin_id'],
            );
            $g_b = $dh_config['g']->powMod($b, $dh_config['p']);
            Crypt::checkG($g_b, $dh_config['p']);
            $this->methodCallAsyncRead('messages.acceptEncryption', ['peer' => $params['id'], 'g_b' => $g_b->toBytes(), 'key_fingerprint' => $key['fingerprint']]);
            $chat->notifyLayer();
            $this->logger->logger('Secret chat '.$params['id'].' accepted successfully!', Logger::NOTICE);
        } finally {
            EventLoop::queue($lock->release(...));
        }
    }
    /**
     * Complete secret chat.
     *
     * @param array $params Secret chat
     */
    private function completeSecretChat(array $params): void
    {
        $lock = $this->confirmChatMutex->acquire((string) $params['id']);
        try {
            if (!isset($this->temp_requested_secret_chats[$params['id']])) {
                //$this->logger->logger($this->secretChatStatus($params['id']));
                $this->logger->logger('Could not find and complete secret chat '.$params['id']);
                return;
            }
            $dh_config = ($this->getDhConfig());
            $params['g_a_or_b'] = new BigInteger((string) $params['g_a_or_b'], 256);
            Crypt::checkG($params['g_a_or_b'], $dh_config['p']);
            $key = ['auth_key' => str_pad($params['g_a_or_b']->powMod($this->temp_requested_secret_chats[$params['id']], $dh_config['p'])->toBytes(), 256, \chr(0), STR_PAD_LEFT)];
            unset($this->temp_requested_secret_chats[$params['id']]);
            $key['fingerprint'] = substr(sha1($key['auth_key'], true), -8);
            //$this->logger->logger($key);
            if ($key['fingerprint'] !== $params['key_fingerprint']) {
                $this->discardSecretChat($params['id']);
                throw new SecurityException('Invalid key fingerprint!');
            }
            $key['visualization_orig'] = substr(sha1($key['auth_key'], true), 16);
            $key['visualization_46'] = substr(hash('sha256', $key['auth_key'], true), 20);
            $this->secretChats[$params['id']] = $chat = new SecretChatController(
                $this,
                $key,
                $params['id'],
                $params['access_hash'],
                true,
                $params['participant_id'],
            );
            $chat->notifyLayer();
            $this->logger->logger('Secret chat '.$params['id'].' completed successfully!', Logger::NOTICE);
        } finally {
            EventLoop::queue($lock->release(...));
        }
    }

    /**
     * Gets a secret chat message.
     *
     * @param integer $chatId Secret chat ID.
     * @param integer $randomId Secret chat message ID.
     */
    public function getSecretMessage(int $chatId, int $randomId): SecretMessage
    {
        return $this->wrapMessage($this->getSecretChatController($chatId)->getMessage($randomId)['message']);
    }

    /**
     * Get secret chat.
     *
     * @param array|int $chat Secret chat ID
     *
     * @psalm-mutation-free
     */
    public function getSecretChat(array|int $chat): SecretChat
    {
        return $this->getSecretChatController($chat)->public;
    }
    /**
     * Get secret chat controller.
     *
     * @internal
     *
     * @param array|int $chat Secret chat ID
     *
     * @psalm-mutation-free
     */
    public function getSecretChatController(array|int $chat): SecretChatController
    {
        if (\is_array($chat)) {
            switch ($chat['_']) {
                case 'updateEncryption':
                    $chat = $chat['chat']['id'];
                    break;
                case 'updateNewEncryptedMessage':
                    $chat = $chat['message'];
                    // no break
                case 'inputEncryptedChat':
                case 'updateEncryptedChatTyping':
                case 'updateEncryptedMessagesRead':
                case 'encryptedMessage':
                case 'encryptedMessageService':
                    $chat = $chat['chat_id'];
                    break;
                default:
                    throw new AssertionError("Unknown update type {$chat['_']} provided!");
            }
        } elseif (DialogId::isSecretChat($chat)) {
            $chat = DialogId::toSecretChatId($chat);
        }
        if (!isset($this->secretChats[$chat])) {
            throw new SecretPeerNotInDbException;
        }
        return $this->secretChats[$chat];
    }
    /**
     * Check whether secret chat exists.
     *
     * @param array|int $chat Secret chat ID
     *
     * @psalm-mutation-free
     */
    public function hasSecretChat(array|int $chat): bool
    {
        return isset($this->secretChats[\is_array($chat) ? $chat['chat_id'] : $chat]);
    }
    /**
     * Discard secret chat.
     *
     * @param int $chat Secret chat ID
     * @param bool $deleteHistory If true, deletes the entire chat history for the other user as well.
     */
    public function discardSecretChat(int $chat, bool $deleteHistory = false): void
    {
        $this->logger->logger('Discarding secret chat '.$chat.'...', Logger::VERBOSE);
        if (isset($this->secretChats[$chat])) {
            unset($this->secretChats[$chat]);
        }
        if (isset($this->temp_requested_secret_chats[$chat])) {
            unset($this->temp_requested_secret_chats[$chat]);
        }
        try {
            $this->methodCallAsyncRead('messages.discardEncryption', ['chat_id' => $chat, 'delete_history' => $deleteHistory]);
        } catch (EncryptionAlreadyAcceptedError|EncryptionAlreadyDeclinedError) {
        }
    }
}
<?php

declare(strict_types=1);

/**
 * Secret chat module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\SecretChats;

/**
 * @internal
 */
enum RekeyState
{
    case IDLE;
    case REQUESTED;
    case ACCEPTED;
}
<?php

declare(strict_types=1);

/**
 * RPCErrorException module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

/**
 * Indicates a transport error returned by Telegram's API.
 */
final class TransportError extends Exception
{
    public function __construct(public readonly int $error, ?Exception $previous = null)
    {
        parent::__construct((string) $error, $error, $previous);
    }
}
<?php

declare(strict_types=1);

/**
 * SecurityException module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use Exception;

/**
 * Indicates a security error.
 */
final class SecurityException extends Exception
{
}
<?php

declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\WebUI;

use JsonSerializable;

/**
 * A secondary action of a form: a button that submits `name=value` without validating the form fields.
 */
final class Action implements JsonSerializable
{
    public function __construct(
        public readonly string $label,
        public readonly string $name,
        public readonly string $value,
    ) {
    }

    #[\Override]
    public function jsonSerialize(): array
    {
        return ['label' => $this->label, 'name' => $this->name, 'value' => $this->value];
    }
}
{"tgs":1,"v":"5.5.2","fr":60,"ip":0,"op":180,"w":512,"h":512,"nm":"password_open_idle","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":3,"sr":1,"ks":{"o":{"a":0,"k":0},"p":{"a":0,"k":[256,256,0]},"s":{"a":0,"k":[95,95,100]}},"ao":0,"ip":0,"op":180,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":3,"parent":7,"sr":1,"ks":{"o":{"a":0,"k":0},"p":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":0,"s":[0,0,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":19,"s":[0,0,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.34,"y":0.34},"o":{"x":0.33,"y":0.33},"t":50,"s":[13.684,0,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.12,"y":1},"o":{"x":0.33,"y":0},"t":80,"s":[13.684,0,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.34,"y":0.34},"o":{"x":0.33,"y":0.33},"t":120,"s":[-17.895,0,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":140,"s":[-17.895,0,0],"to":[0,0,0],"ti":[0,0,0]},{"t":170,"s":[0,0,0]}]}},"ao":0,"ip":0,"op":180,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"parent":2,"sr":1,"ks":{"p":{"a":0,"k":[-63.783,7.296,0]},"a":{"a":0,"k":[-63.783,7.296,0]},"s":{"a":1,"k":[{"i":{"x":[0.34,0.34,0.34],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":19,"s":[100,100,100]},{"i":{"x":[0.34,0.34,0.34],"y":[1,1,1]},"o":{"x":[0.33,0.33,0.33],"y":[0,0,0]},"t":80,"s":[100,100,100]},{"i":{"x":[0.34,0.34,0.34],"y":[1,1,1]},"o":{"x":[0.33,0.33,0.33],"y":[0,0,0]},"t":120,"s":[95,95,100]},{"i":{"x":[0.34,0.34,0.34],"y":[1,1,1]},"o":{"x":[0.33,0.33,0.33],"y":[0,0,0]},"t":140,"s":[95,95,100]},{"t":170,"s":[100,100,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":19,"s":[{"i":[[0,-11.871],[9.986,0],[0,7.079],[-9.986,0]],"o":[[0,7.079],[-9.986,0],[0,-11.357],[9.986,0]],"v":[[-46.701,12.524],[-63.783,33.272],[-80.865,12.524],[-63.783,11.875]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":26,"s":[{"i":[[0,-0.618],[4.754,0],[0,2.194],[-4.754,0]],"o":[[0,2.194],[-4.754,0],[0,-1.118],[4.754,0]],"v":[[-50.556,14.024],[-63.816,16.897],[-76.57,13.649],[-63.82,15.407]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":34,"s":[{"i":[[0,-11.871],[9.986,0],[0,7.079],[-9.986,0]],"o":[[0,7.079],[-9.986,0],[0,-11.357],[9.986,0]],"v":[[-46.701,12.524],[-63.783,33.272],[-80.865,12.524],[-63.783,11.875]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":80,"s":[{"i":[[0,-11.871],[9.986,0],[0,7.079],[-9.986,0]],"o":[[0,7.079],[-9.986,0],[0,-11.357],[9.986,0]],"v":[[-46.701,12.524],[-63.783,33.272],[-80.865,12.524],[-63.783,11.875]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":87,"s":[{"i":[[0,-0.618],[4.754,0],[0,2.194],[-4.754,0]],"o":[[0,2.194],[-4.754,0],[0,-1.118],[4.754,0]],"v":[[-50.556,14.024],[-63.816,16.897],[-76.57,13.649],[-63.82,15.407]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":95,"s":[{"i":[[0,-11.871],[9.986,0],[0,7.079],[-9.986,0]],"o":[[0,7.079],[-9.986,0],[0,-11.357],[9.986,0]],"v":[[-46.701,12.524],[-63.783,33.272],[-80.865,12.524],[-63.783,11.875]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":141,"s":[{"i":[[0,-11.871],[9.986,0],[0,7.079],[-9.986,0]],"o":[[0,7.079],[-9.986,0],[0,-11.357],[9.986,0]],"v":[[-46.701,12.524],[-63.783,33.272],[-80.865,12.524],[-63.783,11.875]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":148,"s":[{"i":[[0,-0.618],[4.754,0],[0,2.194],[-4.754,0]],"o":[[0,2.194],[-4.754,0],[0,-1.118],[4.754,0]],"v":[[-50.556,14.024],[-63.816,16.897],[-76.57,13.649],[-63.82,15.407]],"c":true}]},{"t":162,"s":[{"i":[[0,-11.871],[9.986,0],[0,7.079],[-9.986,0]],"o":[[0,7.079],[-9.986,0],[0,-11.357],[9.986,0]],"v":[[-46.701,12.524],[-63.783,33.272],[-80.865,12.524],[-63.783,11.875]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.282352954149,0.286274522543,0.286274522543,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":19,"s":[{"i":[[0,-3.629],[7.358,0],[0,5.828],[-7.358,0]],"o":[[0,5.701],[-7.358,0],[0,-3.63],[7.358,0]],"v":[[-50.461,-12.676],[-63.783,-11.535],[-77.106,-12.676],[-63.783,-23.45]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":26,"s":[{"i":[[0,0.161],[7.358,0],[0,-0.258],[-7.358,0]],"o":[[0,-0.253],[-7.358,0],[0,0.161],[7.358,0]],"v":[[-50.31,11.585],[-63.766,14.468],[-77.067,11.366],[-63.766,14.441]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":34,"s":[{"i":[[0,-3.629],[7.358,0],[0,5.828],[-7.358,0]],"o":[[0,5.701],[-7.358,0],[0,-3.63],[7.358,0]],"v":[[-50.461,-12.676],[-63.783,-11.535],[-77.106,-12.676],[-63.783,-23.45]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":80,"s":[{"i":[[0,-3.629],[7.358,0],[0,5.828],[-7.358,0]],"o":[[0,5.701],[-7.358,0],[0,-3.63],[7.358,0]],"v":[[-50.461,-12.676],[-63.783,-11.535],[-77.106,-12.676],[-63.783,-23.45]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":87,"s":[{"i":[[0,0.161],[7.358,0],[0,-0.258],[-7.358,0]],"o":[[0,-0.253],[-7.358,0],[0,0.161],[7.358,0]],"v":[[-50.31,11.585],[-63.766,14.468],[-77.067,11.366],[-63.766,14.441]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":95,"s":[{"i":[[0,-3.629],[7.358,0],[0,5.828],[-7.358,0]],"o":[[0,5.701],[-7.358,0],[0,-3.63],[7.358,0]],"v":[[-50.461,-12.676],[-63.783,-11.535],[-77.106,-12.676],[-63.783,-23.45]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":141,"s":[{"i":[[0,-3.629],[7.358,0],[0,5.828],[-7.358,0]],"o":[[0,5.701],[-7.358,0],[0,-3.63],[7.358,0]],"v":[[-50.461,-12.676],[-63.783,-11.535],[-77.106,-12.676],[-63.783,-23.45]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":148,"s":[{"i":[[0,0.161],[7.358,0],[0,-0.258],[-7.358,0]],"o":[[0,-0.253],[-7.358,0],[0,0.161],[7.358,0]],"v":[[-50.31,11.585],[-63.766,14.468],[-77.067,11.366],[-63.766,14.441]],"c":true}]},{"t":162,"s":[{"i":[[0,-3.629],[7.358,0],[0,5.828],[-7.358,0]],"o":[[0,5.701],[-7.358,0],[0,-3.63],[7.358,0]],"v":[[-50.461,-12.676],[-63.783,-11.535],[-77.106,-12.676],[-63.783,-23.45]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.960784316063,0.960784316063,0.960784316063,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":19,"s":[{"i":[[0,-19.516],[13.393,0],[0,19.516],[-13.393,0]],"o":[[0,19.516],[-13.393,0],[0,-19.516],[13.393,0]],"v":[[-39.533,4.77],[-63.783,40.106],[-88.033,4.77],[-63.783,-30.566]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":26,"s":[{"i":[[0,-5.552],[13.393,0],[0,5.198],[-13.393,0]],"o":[[0,6.698],[-13.393,0],[0,-5.427],[13.393,0]],"v":[[-39.533,4.77],[-63.758,17.731],[-88.033,4.77],[-63.758,12.934]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":34,"s":[{"i":[[0,-19.516],[13.393,0],[0,19.516],[-13.393,0]],"o":[[0,19.516],[-13.393,0],[0,-19.516],[13.393,0]],"v":[[-39.533,4.77],[-63.783,40.106],[-88.033,4.77],[-63.783,-30.566]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":80,"s":[{"i":[[0,-19.516],[13.393,0],[0,19.516],[-13.393,0]],"o":[[0,19.516],[-13.393,0],[0,-19.516],[13.393,0]],"v":[[-39.533,4.77],[-63.783,40.106],[-88.033,4.77],[-63.783,-30.566]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":87,"s":[{"i":[[0,-5.552],[13.393,0],[0,5.198],[-13.393,0]],"o":[[0,6.698],[-13.393,0],[0,-5.427],[13.393,0]],"v":[[-39.533,4.77],[-63.758,17.731],[-88.033,4.77],[-63.758,12.934]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":95,"s":[{"i":[[0,-19.516],[13.393,0],[0,19.516],[-13.393,0]],"o":[[0,19.516],[-13.393,0],[0,-19.516],[13.393,0]],"v":[[-39.533,4.77],[-63.783,40.106],[-88.033,4.77],[-63.783,-30.566]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":141,"s":[{"i":[[0,-19.516],[13.393,0],[0,19.516],[-13.393,0]],"o":[[0,19.516],[-13.393,0],[0,-19.516],[13.393,0]],"v":[[-39.533,4.77],[-63.783,40.106],[-88.033,4.77],[-63.783,-30.566]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":148,"s":[{"i":[[0,-5.552],[13.393,0],[0,5.198],[-13.393,0]],"o":[[0,6.698],[-13.393,0],[0,-5.427],[13.393,0]],"v":[[-39.533,4.77],[-63.758,17.731],[-88.033,4.77],[-63.758,12.934]],"c":true}]},{"t":162,"s":[{"i":[[0,-19.516],[13.393,0],[0,19.516],[-13.393,0]],"o":[[0,19.516],[-13.393,0],[0,-19.516],[13.393,0]],"v":[[-39.533,4.77],[-63.783,40.106],[-88.033,4.77],[-63.783,-30.566]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.066666670144,0.066666670144,0.066666670144,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":19,"s":[{"i":[[0,-19.516],[13.393,0],[0,19.516],[-13.393,0]],"o":[[0,19.516],[-13.393,0],[0,-19.516],[13.393,0]],"v":[[-38.523,9.822],[-63.783,45.158],[-89.044,9.822],[-63.783,-25.514]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":26,"s":[{"i":[[0,-10.354],[13.393,0],[0,4.146],[-13.393,0]],"o":[[0,6.771],[-13.393,0],[0,-11.604],[13.393,0]],"v":[[-38.545,7.822],[-63.816,19.658],[-89.088,8.322],[-63.816,13.986]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":34,"s":[{"i":[[0,-19.516],[13.393,0],[0,19.516],[-13.393,0]],"o":[[0,19.516],[-13.393,0],[0,-19.516],[13.393,0]],"v":[[-38.523,9.822],[-63.783,45.158],[-89.044,9.822],[-63.783,-25.514]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":80,"s":[{"i":[[0,-19.516],[13.393,0],[0,19.516],[-13.393,0]],"o":[[0,19.516],[-13.393,0],[0,-19.516],[13.393,0]],"v":[[-38.523,9.822],[-63.783,45.158],[-89.044,9.822],[-63.783,-25.514]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":87,"s":[{"i":[[0,-10.354],[13.393,0],[0,4.146],[-13.393,0]],"o":[[0,6.771],[-13.393,0],[0,-11.604],[13.393,0]],"v":[[-38.545,7.822],[-63.816,19.658],[-89.088,8.322],[-63.816,13.986]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":95,"s":[{"i":[[0,-19.516],[13.393,0],[0,19.516],[-13.393,0]],"o":[[0,19.516],[-13.393,0],[0,-19.516],[13.393,0]],"v":[[-38.523,9.822],[-63.783,45.158],[-89.044,9.822],[-63.783,-25.514]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":141,"s":[{"i":[[0,-19.516],[13.393,0],[0,19.516],[-13.393,0]],"o":[[0,19.516],[-13.393,0],[0,-19.516],[13.393,0]],"v":[[-38.523,9.822],[-63.783,45.158],[-89.044,9.822],[-63.783,-25.514]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":148,"s":[{"i":[[0,-10.354],[13.393,0],[0,4.146],[-13.393,0]],"o":[[0,6.771],[-13.393,0],[0,-11.604],[13.393,0]],"v":[[-38.545,7.822],[-63.816,19.658],[-89.088,8.322],[-63.816,13.986]],"c":true}]},{"t":162,"s":[{"i":[[0,-19.516],[13.393,0],[0,19.516],[-13.393,0]],"o":[[0,19.516],[-13.393,0],[0,-19.516],[13.393,0]],"v":[[-38.523,9.822],[-63.783,45.158],[-89.044,9.822],[-63.783,-25.514]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.858823537827,0.749019622803,0.592156887054,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false}],"ip":0,"op":180,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"parent":2,"sr":1,"ks":{"p":{"a":0,"k":[73.842,7.296,0]},"a":{"a":0,"k":[-63.783,7.296,0]},"s":{"a":1,"k":[{"i":{"x":[0.34,0.34,0.34],"y":[1,1,1]},"o":{"x":[0.33,0.33,0.33],"y":[0,0,0]},"t":19,"s":[-100,100,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.33,0.33,0.33],"y":[0,0,0]},"t":50,"s":[-95,95,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":80,"s":[-95,95,100]},{"t":100,"s":[-100,100,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":19,"s":[{"i":[[0,-11.871],[9.986,0],[0,7.079],[-9.986,0]],"o":[[0,7.079],[-9.986,0],[0,-11.357],[9.986,0]],"v":[[-46.701,12.524],[-63.783,33.272],[-80.865,12.524],[-63.783,11.875]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":26,"s":[{"i":[[0,-0.618],[4.754,0],[0,2.194],[-4.754,0]],"o":[[0,2.194],[-4.754,0],[0,-1.118],[4.754,0]],"v":[[-50.556,14.024],[-63.816,16.897],[-76.57,13.649],[-63.82,15.407]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":34,"s":[{"i":[[0,-11.871],[9.986,0],[0,7.079],[-9.986,0]],"o":[[0,7.079],[-9.986,0],[0,-11.357],[9.986,0]],"v":[[-46.701,12.524],[-63.783,33.272],[-80.865,12.524],[-63.783,11.875]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":80,"s":[{"i":[[0,-11.871],[9.986,0],[0,7.079],[-9.986,0]],"o":[[0,7.079],[-9.986,0],[0,-11.357],[9.986,0]],"v":[[-46.701,12.524],[-63.783,33.272],[-80.865,12.524],[-63.783,11.875]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":87,"s":[{"i":[[0,-0.618],[4.754,0],[0,2.194],[-4.754,0]],"o":[[0,2.194],[-4.754,0],[0,-1.118],[4.754,0]],"v":[[-50.556,14.024],[-63.816,16.897],[-76.57,13.649],[-63.82,15.407]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":95,"s":[{"i":[[0,-11.871],[9.986,0],[0,7.079],[-9.986,0]],"o":[[0,7.079],[-9.986,0],[0,-11.357],[9.986,0]],"v":[[-46.701,12.524],[-63.783,33.272],[-80.865,12.524],[-63.783,11.875]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":141,"s":[{"i":[[0,-11.871],[9.986,0],[0,7.079],[-9.986,0]],"o":[[0,7.079],[-9.986,0],[0,-11.357],[9.986,0]],"v":[[-46.701,12.524],[-63.783,33.272],[-80.865,12.524],[-63.783,11.875]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":148,"s":[{"i":[[0,-0.618],[4.754,0],[0,2.194],[-4.754,0]],"o":[[0,2.194],[-4.754,0],[0,-1.118],[4.754,0]],"v":[[-50.556,14.024],[-63.816,16.897],[-76.57,13.649],[-63.82,15.407]],"c":true}]},{"t":162,"s":[{"i":[[0,-11.871],[9.986,0],[0,7.079],[-9.986,0]],"o":[[0,7.079],[-9.986,0],[0,-11.357],[9.986,0]],"v":[[-46.701,12.524],[-63.783,33.272],[-80.865,12.524],[-63.783,11.875]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.282352954149,0.286274522543,0.286274522543,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":19,"s":[{"i":[[0,-3.629],[7.358,0],[0,5.828],[-7.358,0]],"o":[[0,5.701],[-7.358,0],[0,-3.63],[7.358,0]],"v":[[-50.461,-12.676],[-63.783,-11.535],[-77.106,-12.676],[-63.783,-23.45]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":26,"s":[{"i":[[0,0.161],[7.358,0],[0,-0.258],[-7.358,0]],"o":[[0,-0.253],[-7.358,0],[0,0.161],[7.358,0]],"v":[[-50.31,11.585],[-63.766,14.468],[-77.067,11.366],[-63.766,14.441]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":34,"s":[{"i":[[0,-3.629],[7.358,0],[0,5.828],[-7.358,0]],"o":[[0,5.701],[-7.358,0],[0,-3.63],[7.358,0]],"v":[[-50.461,-12.676],[-63.783,-11.535],[-77.106,-12.676],[-63.783,-23.45]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":80,"s":[{"i":[[0,-3.629],[7.358,0],[0,5.828],[-7.358,0]],"o":[[0,5.701],[-7.358,0],[0,-3.63],[7.358,0]],"v":[[-50.461,-12.676],[-63.783,-11.535],[-77.106,-12.676],[-63.783,-23.45]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":87,"s":[{"i":[[0,0.161],[7.358,0],[0,-0.258],[-7.358,0]],"o":[[0,-0.253],[-7.358,0],[0,0.161],[7.358,0]],"v":[[-50.31,11.585],[-63.766,14.468],[-77.067,11.366],[-63.766,14.441]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":95,"s":[{"i":[[0,-3.629],[7.358,0],[0,5.828],[-7.358,0]],"o":[[0,5.701],[-7.358,0],[0,-3.63],[7.358,0]],"v":[[-50.461,-12.676],[-63.783,-11.535],[-77.106,-12.676],[-63.783,-23.45]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":141,"s":[{"i":[[0,-3.629],[7.358,0],[0,5.828],[-7.358,0]],"o":[[0,5.701],[-7.358,0],[0,-3.63],[7.358,0]],"v":[[-50.461,-12.676],[-63.783,-11.535],[-77.106,-12.676],[-63.783,-23.45]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":148,"s":[{"i":[[0,0.161],[7.358,0],[0,-0.258],[-7.358,0]],"o":[[0,-0.253],[-7.358,0],[0,0.161],[7.358,0]],"v":[[-50.31,11.585],[-63.766,14.468],[-77.067,11.366],[-63.766,14.441]],"c":true}]},{"t":162,"s":[{"i":[[0,-3.629],[7.358,0],[0,5.828],[-7.358,0]],"o":[[0,5.701],[-7.358,0],[0,-3.63],[7.358,0]],"v":[[-50.461,-12.676],[-63.783,-11.535],[-77.106,-12.676],[-63.783,-23.45]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.960784316063,0.960784316063,0.960784316063,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":19,"s":[{"i":[[0,-19.516],[13.393,0],[0,19.516],[-13.393,0]],"o":[[0,19.516],[-13.393,0],[0,-19.516],[13.393,0]],"v":[[-39.533,4.77],[-63.783,40.106],[-88.033,4.77],[-63.783,-30.566]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":26,"s":[{"i":[[0,-5.552],[13.393,0],[0,5.198],[-13.393,0]],"o":[[0,6.698],[-13.393,0],[0,-5.427],[13.393,0]],"v":[[-39.533,4.77],[-63.758,17.731],[-88.033,4.77],[-63.758,12.934]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":34,"s":[{"i":[[0,-19.516],[13.393,0],[0,19.516],[-13.393,0]],"o":[[0,19.516],[-13.393,0],[0,-19.516],[13.393,0]],"v":[[-39.533,4.77],[-63.783,40.106],[-88.033,4.77],[-63.783,-30.566]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":80,"s":[{"i":[[0,-19.516],[13.393,0],[0,19.516],[-13.393,0]],"o":[[0,19.516],[-13.393,0],[0,-19.516],[13.393,0]],"v":[[-39.533,4.77],[-63.783,40.106],[-88.033,4.77],[-63.783,-30.566]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":87,"s":[{"i":[[0,-5.552],[13.393,0],[0,5.198],[-13.393,0]],"o":[[0,6.698],[-13.393,0],[0,-5.427],[13.393,0]],"v":[[-39.533,4.77],[-63.758,17.731],[-88.033,4.77],[-63.758,12.934]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":95,"s":[{"i":[[0,-19.516],[13.393,0],[0,19.516],[-13.393,0]],"o":[[0,19.516],[-13.393,0],[0,-19.516],[13.393,0]],"v":[[-39.533,4.77],[-63.783,40.106],[-88.033,4.77],[-63.783,-30.566]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":141,"s":[{"i":[[0,-19.516],[13.393,0],[0,19.516],[-13.393,0]],"o":[[0,19.516],[-13.393,0],[0,-19.516],[13.393,0]],"v":[[-39.533,4.77],[-63.783,40.106],[-88.033,4.77],[-63.783,-30.566]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":148,"s":[{"i":[[0,-5.552],[13.393,0],[0,5.198],[-13.393,0]],"o":[[0,6.698],[-13.393,0],[0,-5.427],[13.393,0]],"v":[[-39.533,4.77],[-63.758,17.731],[-88.033,4.77],[-63.758,12.934]],"c":true}]},{"t":162,"s":[{"i":[[0,-19.516],[13.393,0],[0,19.516],[-13.393,0]],"o":[[0,19.516],[-13.393,0],[0,-19.516],[13.393,0]],"v":[[-39.533,4.77],[-63.783,40.106],[-88.033,4.77],[-63.783,-30.566]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.066666670144,0.066666670144,0.066666670144,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":19,"s":[{"i":[[0,-19.516],[13.393,0],[0,19.516],[-13.393,0]],"o":[[0,19.516],[-13.393,0],[0,-19.516],[13.393,0]],"v":[[-38.523,9.822],[-63.783,45.158],[-89.044,9.822],[-63.783,-25.514]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":26,"s":[{"i":[[0,-10.354],[13.393,0],[0,4.146],[-13.393,0]],"o":[[0,6.771],[-13.393,0],[0,-11.604],[13.393,0]],"v":[[-38.545,7.822],[-63.816,19.658],[-89.088,8.322],[-63.816,13.986]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":34,"s":[{"i":[[0,-19.516],[13.393,0],[0,19.516],[-13.393,0]],"o":[[0,19.516],[-13.393,0],[0,-19.516],[13.393,0]],"v":[[-38.523,9.822],[-63.783,45.158],[-89.044,9.822],[-63.783,-25.514]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":80,"s":[{"i":[[0,-19.516],[13.393,0],[0,19.516],[-13.393,0]],"o":[[0,19.516],[-13.393,0],[0,-19.516],[13.393,0]],"v":[[-38.523,9.822],[-63.783,45.158],[-89.044,9.822],[-63.783,-25.514]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":87,"s":[{"i":[[0,-10.354],[13.393,0],[0,4.146],[-13.393,0]],"o":[[0,6.771],[-13.393,0],[0,-11.604],[13.393,0]],"v":[[-38.545,7.822],[-63.816,19.658],[-89.088,8.322],[-63.816,13.986]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":95,"s":[{"i":[[0,-19.516],[13.393,0],[0,19.516],[-13.393,0]],"o":[[0,19.516],[-13.393,0],[0,-19.516],[13.393,0]],"v":[[-38.523,9.822],[-63.783,45.158],[-89.044,9.822],[-63.783,-25.514]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":141,"s":[{"i":[[0,-19.516],[13.393,0],[0,19.516],[-13.393,0]],"o":[[0,19.516],[-13.393,0],[0,-19.516],[13.393,0]],"v":[[-38.523,9.822],[-63.783,45.158],[-89.044,9.822],[-63.783,-25.514]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":148,"s":[{"i":[[0,-10.354],[13.393,0],[0,4.146],[-13.393,0]],"o":[[0,6.771],[-13.393,0],[0,-11.604],[13.393,0]],"v":[[-38.545,7.822],[-63.816,19.658],[-89.088,8.322],[-63.816,13.986]],"c":true}]},{"t":162,"s":[{"i":[[0,-19.516],[13.393,0],[0,19.516],[-13.393,0]],"o":[[0,19.516],[-13.393,0],[0,-19.516],[13.393,0]],"v":[[-38.523,9.822],[-63.783,45.158],[-89.044,9.822],[-63.783,-25.514]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.858823537827,0.749019622803,0.592156887054,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false}],"ip":0,"op":180,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"parent":2,"sr":1,"ks":{"p":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":19,"s":[5.883,50.25,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.34,"y":0.34},"o":{"x":0.33,"y":0.33},"t":50,"s":[7.988,50.25,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.12,"y":1},"o":{"x":0.33,"y":0},"t":80,"s":[7.988,50.25,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.34,"y":0.34},"o":{"x":0.33,"y":0.33},"t":120,"s":[2.725,50.25,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":140,"s":[2.725,50.25,0],"to":[0,0,0],"ti":[0,0,0]},{"t":170,"s":[5.883,50.25,0]}]},"a":{"a":0,"k":[5.883,50.25,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[2.511,-4.918],[0.213,-0.942],[-3.744,-1.911],[2.476,-4.893],[-0.478,0.936],[3.948,2.016]],"o":[[-0.469,0.919],[2.519,-4.85],[3.744,1.911],[0.643,-0.734],[2.511,-4.918],[-3.948,-2.016]],"v":[[13.327,44.758],[12.3,47.57],[24.125,43.208],[25.922,54.567],[27.623,52.056],[25.019,39.507]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.211764708161,0.086274512112,0.035294119269,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[2.509,-4.914],[-3.948,-2.015],[-2.509,4.914],[3.948,2.015]],"o":[[-2.509,4.914],[3.948,2.015],[2.509,-4.914],[-3.948,-2.015]],"v":[[13.326,44.758],[15.931,57.305],[27.621,52.056],[25.016,39.509]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921574354,0.266666680574,0.188235297799,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[3.93,-7.699],[-3.948,-2.015],[-2.812,4.747],[3.948,2.015]],"o":[[-2.509,4.914],[3.948,2.015],[4.225,-7.134],[-3.948,-2.015]],"v":[[12.111,46.986],[15.567,59.968],[27.169,55.043],[25.661,40.199]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.941176474094,0.847058832645,0.705882370472,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-2.511,-4.918],[-0.213,-0.942],[3.744,-1.911],[-2.476,-4.893],[0.478,0.936],[-3.948,2.016]],"o":[[0.469,0.919],[-2.519,-4.85],[-3.744,1.911],[-0.643,-0.734],[-2.511,-4.918],[3.948,-2.016]],"v":[[-1.485,44.758],[-0.457,47.57],[-12.282,43.208],[-14.08,54.567],[-15.78,52.056],[-13.177,39.507]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.211764708161,0.086274512112,0.035294119269,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-2.509,-4.914],[3.948,-2.015],[2.509,4.914],[-3.948,2.015]],"o":[[2.509,4.914],[-3.948,2.015],[-2.509,-4.914],[3.948,-2.015]],"v":[[-1.484,44.758],[-4.089,57.305],[-15.779,52.056],[-13.174,39.509]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921574354,0.266666680574,0.188235297799,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-3.93,-7.699],[3.948,-2.015],[2.812,4.747],[-3.948,2.015]],"o":[[2.509,4.914],[-3.948,2.015],[-4.225,-7.134],[3.948,-2.015]],"v":[[-0.269,46.986],[-3.725,59.968],[-15.327,55.043],[-13.818,40.199]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.941176474094,0.847058832645,0.705882370472,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[11.287,0.292],[3.048,-1.351],[3.153,1.398],[3.628,-0.094],[-0.105,-10.467],[-29.829,0],[-0.445,8.16]],"o":[[-3.628,-0.094],[-3.153,1.398],[-3.048,-1.351],[-11.287,0.292],[0.079,7.902],[29.532,0],[0.569,-10.452]],"v":[[23.934,29.849],[11.849,32.814],[-0.121,32.814],[-12.206,29.849],[-28.625,47.042],[5.864,70.656],[40.353,47.042]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.858823537827,0.749019622803,0.592156887054,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false}],"ip":0,"op":180,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"parent":2,"sr":1,"ks":{"p":{"a":0,"k":[5.864,107.189,0]},"a":{"a":0,"k":[5.864,107.189,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[1.188,-2.439],[0.921,0.417],[6.452,0.59],[6.878,0],[2.613,-0.213],[1.434,-0.652],[-0.372,-1.404],[-1.391,0.611],[-6.645,0.566],[-7.086,0],[-2.685,-0.256],[-1.475,-0.645]],"o":[[0.378,-1.404],[-1.435,-0.65],[-2.605,-0.238],[-6.884,0],[-6.452,0.525],[-0.92,0.418],[-1.188,-2.439],[1.479,-0.65],[2.688,-0.229],[7.086,0],[6.639,0.632],[1.396,0.611]],"v":[[34.087,94.056],[33.413,91.171],[21.553,89.807],[5.926,89.257],[-9.991,89.703],[-21.919,91.171],[-22.598,94.051],[-22.751,89.003],[-10.911,87.679],[5.926,87.083],[22.498,87.794],[34.24,89.003]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.015686275437,0,0.003921568859,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0.676,0],[6.173,0],[4.992,3.171],[-1.401,0],[0,0],[-6.229,-1.128]],"o":[[-4.927,3.051],[-6.347,0],[-0.236,0],[6.404,0],[0,0],[1.339,0.243]],"v":[[23.022,105.559],[5.744,111.007],[-11.941,105.116],[-8.151,106.691],[5.555,107.59],[19.484,106.941]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.274509817362,0.376470595598,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[2,0.88],[6.637,0.633],[7.087,0],[2.687,-0.229],[1.475,-0.649],[-2.5,-3.795],[-11.418,0],[-3.544,5.379]],"o":[[-1.475,-0.649],[-2.684,-0.256],[-7.087,0],[-6.643,0.567],[-2,0.88],[3.544,5.379],[11.418,0],[2.5,-3.795]],"v":[[34.242,89.001],[22.499,87.793],[5.925,87.084],[-10.913,87.679],[-22.749,89.001],[-20.634,97.431],[5.746,111.004],[32.127,97.431]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.247058823705,0.039215687662,0.141176477075,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[3.188,0.413],[6.882,0.687],[7.357,0],[2.786,-0.25],[1.597,-0.464],[-2.595,-3.795],[-11.852,0],[-3.679,5.379]],"o":[[-1.648,-0.214],[-2.783,-0.278],[-7.357,0],[-6.889,0.618],[-3.048,0.885],[3.678,5.379],[11.852,0],[2.595,-3.795]],"v":[[34.242,89.001],[22.653,90.081],[5.925,89.286],[-11.089,89.965],[-22.749,89.001],[-21.823,100.948],[5.733,115.682],[33.554,101.21]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.956862747669,0.913725495338,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[2,0.88],[6.637,0.633],[7.087,0],[2.687,-0.229],[1.475,-0.649],[-2.5,-3.795],[-11.418,0],[-3.544,5.379]],"o":[[-1.475,-0.649],[-2.684,-0.256],[-7.087,0],[-6.643,0.567],[-2,0.88],[3.544,5.379],[11.418,0],[2.5,-3.795]],"v":[[35.881,85.862],[23.167,84.726],[5.922,83.945],[-11.558,84.606],[-24.395,85.862],[-22.277,95.229],[5.746,108.802],[33.769,95.229]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.858823537827,0.749019622803,0.592156887054,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false}],"ip":0,"op":180,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"parent":1,"sr":1,"ks":{"r":{"a":1,"k":[{"i":{"x":[0.12],"y":[1]},"o":{"x":[0.33],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.12],"y":[1]},"o":{"x":[0.33],"y":[0]},"t":19,"s":[0]},{"i":{"x":[0.12],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":64,"s":[4]},{"i":{"x":[0.12],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":80,"s":[4]},{"i":{"x":[0.12],"y":[1]},"o":{"x":[0.33],"y":[0]},"t":131,"s":[-4]},{"i":{"x":[0.12],"y":[1]},"o":{"x":[0.33],"y":[0]},"t":140,"s":[-4]},{"t":179,"s":[0]}]},"p":{"a":0,"k":[5.303,144.067,0]},"a":{"a":0,"k":[5.303,144.067,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":19,"s":[{"i":[[0,0],[-10.992,-6.96],[0,0],[-16.976,11.779],[-18.657,4.78]],"o":[[17.211,5.126],[18.61,11.782],[0,0],[10.182,-7.065],[0,0]],"v":[[-70.986,150.908],[-27.847,165.744],[5.303,176.601],[37.654,165.544],[81.313,150.908]],"c":false}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":50,"s":[{"i":[[0,0],[-10.992,-6.96],[0,0],[-16.976,11.779],[-18.657,4.78]],"o":[[17.211,5.126],[18.61,11.782],[0,0],[10.182,-7.065],[0,0]],"v":[[-60.506,152.75],[-12.321,165.834],[22.145,176.622],[53.18,165.633],[91.839,148.276]],"c":false}]},{"i":{"x":0.12,"y":1},"o":{"x":0.33,"y":0},"t":80,"s":[{"i":[[0,0],[-10.992,-6.96],[0,0],[-16.976,11.779],[-18.657,4.78]],"o":[[17.211,5.126],[18.61,11.782],[0,0],[10.182,-7.065],[0,0]],"v":[[-60.506,152.75],[-12.321,165.834],[22.145,176.622],[53.18,165.633],[91.839,148.276]],"c":false}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":120,"s":[{"i":[[0,0],[-10.992,-6.96],[0,0],[-16.975,11.779],[-18.657,4.78]],"o":[[17.211,5.126],[18.61,11.782],[0,0],[10.182,-7.065],[0,0]],"v":[[-83.88,147.487],[-39.426,165.744],[-6.276,176.601],[26.075,165.544],[69.208,153.539]],"c":false}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":140,"s":[{"i":[[0,0],[-10.992,-6.96],[0,0],[-16.975,11.779],[-18.657,4.78]],"o":[[17.211,5.126],[18.61,11.782],[0,0],[10.182,-7.065],[0,0]],"v":[[-83.88,147.487],[-39.426,165.744],[-6.276,176.601],[26.075,165.544],[69.208,153.539]],"c":false}]},{"t":170,"s":[{"i":[[0,0],[-10.992,-6.96],[0,0],[-16.976,11.779],[-18.657,4.78]],"o":[[17.211,5.126],[18.61,11.782],[0,0],[10.182,-7.065],[0,0]],"v":[[-70.986,150.908],[-27.847,165.744],[5.303,176.601],[37.654,165.544],[81.313,150.908]],"c":false}]}]},"hd":false},{"ty":"st","c":{"a":0,"k":[0.698039233685,0.584313750267,0.447058826685,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":2,"lj":2,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":19,"s":[{"i":[[-24.85,4.01],[2.81,-4.07],[4.9,-0.67],[10.74,-7.45],[0,0],[16.98,11.78],[7.38,1.01],[3.407,3.585],[1.57,5.1],[-14.42,-8.27],[-8.4,5.66]],"o":[[-1.671,6.6],[-2.81,4.07],[-7.38,1.01],[-16.97,11.78],[0,0],[-10.73,-7.45],[-4.9,-0.67],[-3.841,-4.041],[12.31,3.41],[17.93,10.29],[7.89,-5.32]],"v":[[91.856,131.627],[83.799,149.584],[68.549,154.317],[37.649,165.547],[5.299,176.597],[-27.051,165.547],[-57.941,154.317],[-72.457,150.111],[-81.201,131.67],[-17.101,154.037],[28.949,154.037]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":50,"s":[{"i":[[-23.435,7.584],[2.81,-4.07],[4.9,-0.67],[10.74,-7.45],[0,0],[16.98,11.78],[7.38,1.01],[3.407,3.585],[1.57,5.1],[-14.42,-8.27],[-8.4,5.66]],"o":[[-1.671,6.6],[-2.81,4.07],[-7.38,1.01],[-16.97,11.78],[0,0],[-10.73,-7.45],[-4.9,-0.67],[-3.841,-4.041],[12.31,3.41],[17.93,10.29],[7.89,-5.32]],"v":[[102.382,127.68],[94.325,146.952],[79.076,151.685],[53.175,165.636],[22.141,176.619],[-11.525,165.636],[-47.461,156.159],[-61.977,151.953],[-70.56,134.828],[-1.575,154.126],[44.475,154.126]],"c":true}]},{"i":{"x":0.12,"y":1},"o":{"x":0.33,"y":0},"t":80,"s":[{"i":[[-23.435,7.584],[2.81,-4.07],[4.9,-0.67],[10.74,-7.45],[0,0],[16.98,11.78],[7.38,1.01],[3.407,3.585],[1.57,5.1],[-14.42,-8.27],[-8.4,5.66]],"o":[[-1.671,6.6],[-2.81,4.07],[-7.38,1.01],[-16.97,11.78],[0,0],[-10.73,-7.45],[-4.9,-0.67],[-3.841,-4.041],[12.31,3.41],[17.93,10.29],[7.89,-5.32]],"v":[[102.382,127.68],[94.325,146.952],[79.076,151.685],[53.175,165.636],[22.141,176.619],[-11.525,165.636],[-47.461,156.159],[-61.977,151.953],[-70.56,134.828],[-1.575,154.126],[44.475,154.126]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":120,"s":[{"i":[[-24.85,4.01],[2.81,-4.07],[4.9,-0.67],[10.74,-7.45],[0,0],[16.98,11.78],[7.38,1.01],[3.407,3.585],[1.57,5.1],[-14.42,-8.27],[-8.4,5.66]],"o":[[-1.671,6.6],[-2.81,4.07],[-7.38,1.01],[-16.97,11.78],[0,0],[-10.73,-7.45],[-4.9,-0.67],[-3.841,-4.041],[12.31,3.41],[17.93,10.29],[7.89,-5.32]],"v":[[79.619,135.574],[71.694,152.216],[56.444,156.948],[26.07,165.547],[-6.28,176.597],[-38.63,165.547],[-69.783,149.843],[-85.352,146.69],[-93.043,127.196],[-28.68,154.037],[17.37,154.037]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":140,"s":[{"i":[[-24.85,4.01],[2.81,-4.07],[4.9,-0.67],[10.74,-7.45],[0,0],[16.98,11.78],[7.38,1.01],[3.407,3.585],[1.57,5.1],[-14.42,-8.27],[-8.4,5.66]],"o":[[-1.671,6.6],[-2.81,4.07],[-7.38,1.01],[-16.97,11.78],[0,0],[-10.73,-7.45],[-4.9,-0.67],[-3.841,-4.041],[12.31,3.41],[17.93,10.29],[7.89,-5.32]],"v":[[79.619,135.574],[71.694,152.216],[56.444,156.948],[26.07,165.547],[-6.28,176.597],[-38.63,165.547],[-69.783,149.843],[-85.352,146.69],[-93.043,127.196],[-28.68,154.037],[17.37,154.037]],"c":true}]},{"t":170,"s":[{"i":[[-24.85,4.01],[2.81,-4.07],[4.9,-0.67],[10.74,-7.45],[0,0],[16.98,11.78],[7.38,1.01],[3.407,3.585],[1.57,5.1],[-14.42,-8.27],[-8.4,5.66]],"o":[[-1.671,6.6],[-2.81,4.07],[-7.38,1.01],[-16.97,11.78],[0,0],[-10.73,-7.45],[-4.9,-0.67],[-3.841,-4.041],[12.31,3.41],[17.93,10.29],[7.89,-5.32]],"v":[[91.856,131.627],[83.799,149.584],[68.549,154.317],[37.649,165.547],[5.299,176.597],[-27.051,165.547],[-57.941,154.317],[-72.457,150.111],[-81.201,131.67],[-17.101,154.037],[28.949,154.037]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.61960786581,0.46274510026,0.239215686917,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":11},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":19,"s":[{"i":[[51.445,12.472],[11.245,-13.381],[2.922,3.477],[38.8,-9.406],[-5.196,-23.384],[-28.607,-10.289],[0.785,-3.583],[-16.2,-23.457],[-4.902,-0.674],[-10.738,-7.451],[0,0],[-16.975,11.779],[-7.382,1.015],[-2.812,4.072],[3.47,15.845],[-3.452,1.242],[-4.853,21.841]],"o":[[-38.801,-9.406],[-2.922,3.477],[-11.245,-13.381],[-51.445,12.472],[4.854,21.841],[3.452,1.242],[-3.47,15.845],[2.812,4.072],[7.382,1.015],[16.975,11.779],[0,0],[10.737,-7.451],[4.902,-0.674],[16.2,-23.457],[-0.785,-3.583],[28.607,-10.289],[5.197,-23.384]],"v":[[88.579,-75.949],[10.891,-45.909],[-0.285,-45.909],[-77.973,-75.949],[-132.017,4.597],[-80.556,57.662],[-75.875,66.071],[-70.075,146.877],[-57.943,154.316],[-27.048,165.544],[5.303,176.601],[37.654,165.544],[68.548,153.316],[80.68,146.877],[86.481,66.071],[91.162,57.662],[142.623,4.597]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":50,"s":[{"i":[[51.445,12.472],[11.245,-13.381],[2.922,3.477],[38.8,-9.406],[-5.196,-23.384],[-28.607,-10.289],[0.785,-3.583],[-16.2,-23.457],[-4.902,-0.674],[-10.738,-7.451],[0,0],[-16.975,11.779],[-7.382,1.015],[-2.812,4.072],[3.47,15.845],[-3.452,1.242],[-4.853,21.841]],"o":[[-38.801,-9.406],[-2.922,3.477],[-11.245,-13.381],[-51.445,12.472],[4.854,21.841],[3.452,1.242],[-3.47,15.845],[2.812,4.072],[7.382,1.015],[16.975,11.779],[0,0],[10.737,-7.451],[4.902,-0.674],[16.2,-23.457],[-0.785,-3.583],[28.607,-10.289],[5.197,-23.384]],"v":[[99.106,-75.949],[21.417,-45.909],[10.242,-45.909],[-67.447,-75.949],[-121.491,4.597],[-70.03,57.662],[-65.348,66.071],[-59.548,146.877],[-47.463,156.159],[-11.521,165.633],[22.145,176.622],[53.18,165.633],[79.075,150.685],[91.207,144.245],[97.007,66.071],[101.688,57.662],[153.149,4.597]],"c":true}]},{"i":{"x":0.12,"y":1},"o":{"x":0.33,"y":0},"t":80,"s":[{"i":[[51.445,12.472],[11.245,-13.381],[2.922,3.477],[38.8,-9.406],[-5.196,-23.384],[-28.607,-10.289],[0.785,-3.583],[-16.2,-23.457],[-4.902,-0.674],[-10.738,-7.451],[0,0],[-16.975,11.779],[-7.382,1.015],[-2.812,4.072],[3.47,15.845],[-3.452,1.242],[-4.853,21.841]],"o":[[-38.801,-9.406],[-2.922,3.477],[-11.245,-13.381],[-51.445,12.472],[4.854,21.841],[3.452,1.242],[-3.47,15.845],[2.812,4.072],[7.382,1.015],[16.975,11.779],[0,0],[10.737,-7.451],[4.902,-0.674],[16.2,-23.457],[-0.785,-3.583],[28.607,-10.289],[5.197,-23.384]],"v":[[99.106,-75.949],[21.417,-45.909],[10.242,-45.909],[-67.447,-75.949],[-121.491,4.597],[-70.03,57.662],[-65.348,66.071],[-59.548,146.877],[-47.463,156.159],[-11.521,165.633],[22.145,176.622],[53.18,165.633],[79.075,150.685],[91.207,144.245],[97.007,66.071],[101.688,57.662],[153.149,4.597]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":120,"s":[{"i":[[51.445,12.472],[11.245,-13.381],[2.922,3.477],[38.8,-9.406],[-5.196,-23.384],[-28.607,-10.289],[0.785,-3.583],[-16.2,-23.457],[-4.902,-0.674],[-10.738,-7.451],[0,0],[-16.975,11.779],[-7.382,1.015],[-2.812,4.072],[3.47,15.845],[-3.452,1.242],[-4.853,21.841]],"o":[[-38.801,-9.406],[-2.922,3.477],[-11.245,-13.381],[-51.445,12.472],[4.854,21.841],[3.452,1.242],[-3.47,15.845],[2.812,4.072],[7.382,1.015],[16.975,11.779],[0,0],[10.737,-7.451],[4.902,-0.674],[16.2,-23.457],[-0.785,-3.583],[28.607,-10.289],[5.197,-23.384]],"v":[[77.001,-75.949],[-0.688,-45.909],[-11.864,-45.909],[-89.552,-75.949],[-143.596,4.597],[-92.135,57.662],[-87.454,66.071],[-82.969,143.456],[-69.785,149.843],[-38.627,165.544],[-6.276,176.601],[26.075,165.544],[56.443,155.948],[68.575,149.508],[74.902,66.071],[79.583,57.662],[131.044,4.597]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":140,"s":[{"i":[[51.445,12.472],[11.245,-13.381],[2.922,3.477],[38.8,-9.406],[-5.196,-23.384],[-28.607,-10.289],[0.785,-3.583],[-16.2,-23.457],[-4.902,-0.674],[-10.738,-7.451],[0,0],[-16.975,11.779],[-7.382,1.015],[-2.812,4.072],[3.47,15.845],[-3.452,1.242],[-4.853,21.841]],"o":[[-38.801,-9.406],[-2.922,3.477],[-11.245,-13.381],[-51.445,12.472],[4.854,21.841],[3.452,1.242],[-3.47,15.845],[2.812,4.072],[7.382,1.015],[16.975,11.779],[0,0],[10.737,-7.451],[4.902,-0.674],[16.2,-23.457],[-0.785,-3.583],[28.607,-10.289],[5.197,-23.384]],"v":[[77.001,-75.949],[-0.688,-45.909],[-11.864,-45.909],[-89.552,-75.949],[-143.596,4.597],[-92.135,57.662],[-87.454,66.071],[-82.969,143.456],[-69.785,149.843],[-38.627,165.544],[-6.276,176.601],[26.075,165.544],[56.443,155.948],[68.575,149.508],[74.902,66.071],[79.583,57.662],[131.044,4.597]],"c":true}]},{"t":170,"s":[{"i":[[51.445,12.472],[11.245,-13.381],[2.922,3.477],[38.8,-9.406],[-5.196,-23.384],[-28.607,-10.289],[0.785,-3.583],[-16.2,-23.457],[-4.902,-0.674],[-10.738,-7.451],[0,0],[-16.975,11.779],[-7.382,1.015],[-2.812,4.072],[3.47,15.845],[-3.452,1.242],[-4.853,21.841]],"o":[[-38.801,-9.406],[-2.922,3.477],[-11.245,-13.381],[-51.445,12.472],[4.854,21.841],[3.452,1.242],[-3.47,15.845],[2.812,4.072],[7.382,1.015],[16.975,11.779],[0,0],[10.737,-7.451],[4.902,-0.674],[16.2,-23.457],[-0.785,-3.583],[28.607,-10.289],[5.197,-23.384]],"v":[[88.579,-75.949],[10.891,-45.909],[-0.285,-45.909],[-77.973,-75.949],[-132.017,4.597],[-80.556,57.662],[-75.875,66.071],[-70.075,146.877],[-57.943,154.316],[-27.048,165.544],[5.303,176.601],[37.654,165.544],[68.548,153.316],[80.68,146.877],[86.481,66.071],[91.162,57.662],[142.623,4.597]],"c":true}]}]},"hd":false},{"ty":"st","c":{"a":0,"k":[0.941176474094,0.847058832645,0.701960802078,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":1,"lj":1,"ml":10,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":19,"s":[{"i":[[-6.695,-15.921],[5.747,-3.178],[22.493,4.591],[6.631,-1.555],[16.983,8.766],[-3.144,6.284],[-28.696,7.38],[-11.25,-13.38],[-2.92,3.48],[-38.8,-9.4]],"o":[[2.546,6.053],[-39.7,21.954],[-6.674,-1.362],[-56.127,13.163],[-6.244,-3.223],[5.486,-10.965],[38.664,-9.944],[2.92,3.48],[11.25,-13.38],[26.786,6.499]],"v":[[134.093,-35.971],[128.458,-19.704],[14.574,-11.647],[-5.621,-11.396],[-116.496,-21.256],[-122.192,-38.41],[-76.315,-72.759],[-0.282,-45.913],[10.887,-45.913],[87.791,-72.245]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":50,"s":[{"i":[[-6.695,-15.921],[5.747,-3.178],[22.493,4.591],[6.631,-1.555],[16.983,8.766],[-3.144,6.284],[-28.696,7.38],[-11.25,-13.38],[-2.92,3.48],[-38.8,-9.4]],"o":[[2.546,6.053],[-39.7,21.954],[-6.674,-1.362],[-56.127,13.163],[-6.244,-3.223],[5.486,-10.965],[38.664,-9.944],[2.92,3.48],[11.25,-13.38],[26.786,6.499]],"v":[[144.62,-35.971],[138.984,-19.704],[25.1,-11.647],[4.905,-11.396],[-105.969,-21.256],[-111.666,-38.41],[-65.789,-72.759],[10.244,-45.913],[21.414,-45.913],[98.317,-72.245]],"c":true}]},{"i":{"x":0.12,"y":1},"o":{"x":0.33,"y":0},"t":80,"s":[{"i":[[-6.695,-15.921],[5.747,-3.178],[22.493,4.591],[6.631,-1.555],[16.983,8.766],[-3.144,6.284],[-28.696,7.38],[-11.25,-13.38],[-2.92,3.48],[-38.8,-9.4]],"o":[[2.546,6.053],[-39.7,21.954],[-6.674,-1.362],[-56.127,13.163],[-6.244,-3.223],[5.486,-10.965],[38.664,-9.944],[2.92,3.48],[11.25,-13.38],[26.786,6.499]],"v":[[144.62,-35.971],[138.984,-19.704],[25.1,-11.647],[4.905,-11.396],[-105.969,-21.256],[-111.666,-38.41],[-65.789,-72.759],[10.244,-45.913],[21.414,-45.913],[98.317,-72.245]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":120,"s":[{"i":[[-6.695,-15.921],[5.747,-3.178],[22.493,4.591],[6.631,-1.555],[16.983,8.766],[-3.144,6.284],[-28.696,7.38],[-11.25,-13.38],[-2.92,3.48],[-38.8,-9.4]],"o":[[2.546,6.053],[-39.7,21.954],[-6.674,-1.362],[-56.127,13.163],[-6.244,-3.223],[5.486,-10.965],[38.664,-9.944],[2.92,3.48],[11.25,-13.38],[26.786,6.499]],"v":[[122.514,-35.971],[116.879,-19.704],[2.995,-11.647],[-17.2,-11.396],[-128.075,-21.256],[-133.771,-38.41],[-87.894,-72.759],[-11.861,-45.913],[-0.691,-45.913],[76.212,-72.245]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":140,"s":[{"i":[[-6.695,-15.921],[5.747,-3.178],[22.493,4.591],[6.631,-1.555],[16.983,8.766],[-3.144,6.284],[-28.696,7.38],[-11.25,-13.38],[-2.92,3.48],[-38.8,-9.4]],"o":[[2.546,6.053],[-39.7,21.954],[-6.674,-1.362],[-56.127,13.163],[-6.244,-3.223],[5.486,-10.965],[38.664,-9.944],[2.92,3.48],[11.25,-13.38],[26.786,6.499]],"v":[[122.514,-35.971],[116.879,-19.704],[2.995,-11.647],[-17.2,-11.396],[-128.075,-21.256],[-133.771,-38.41],[-87.894,-72.759],[-11.861,-45.913],[-0.691,-45.913],[76.212,-72.245]],"c":true}]},{"t":170,"s":[{"i":[[-6.695,-15.921],[5.747,-3.178],[22.493,4.591],[6.631,-1.555],[16.983,8.766],[-3.144,6.284],[-28.696,7.38],[-11.25,-13.38],[-2.92,3.48],[-38.8,-9.4]],"o":[[2.546,6.053],[-39.7,21.954],[-6.674,-1.362],[-56.127,13.163],[-6.244,-3.223],[5.486,-10.965],[38.664,-9.944],[2.92,3.48],[11.25,-13.38],[26.786,6.499]],"v":[[134.093,-35.971],[128.458,-19.704],[14.574,-11.647],[-5.621,-11.396],[-116.496,-21.256],[-122.192,-38.41],[-76.315,-72.759],[-0.282,-45.913],[10.887,-45.913],[87.791,-72.245]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.858823537827,0.749019622803,0.592156887054,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":19,"s":[{"i":[[51.445,12.472],[11.245,-13.381],[2.922,3.477],[38.8,-9.406],[-5.196,-23.384],[-28.607,-10.289],[0.785,-3.583],[-16.2,-23.457],[-4.902,-0.674],[-10.738,-7.451],[0,0],[-16.975,11.779],[-7.382,1.015],[-2.812,4.072],[3.47,15.845],[-3.452,1.242],[-4.853,21.841]],"o":[[-38.801,-9.406],[-2.922,3.477],[-11.245,-13.381],[-51.445,12.472],[4.854,21.841],[3.452,1.242],[-3.47,15.845],[2.812,4.072],[7.382,1.015],[16.975,11.779],[0,0],[10.737,-7.451],[4.902,-0.674],[16.2,-23.457],[-0.785,-3.583],[28.607,-10.289],[5.197,-23.384]],"v":[[88.579,-75.949],[10.891,-45.909],[-0.285,-45.909],[-77.973,-75.949],[-132.017,4.597],[-80.556,57.662],[-75.875,66.071],[-70.075,146.877],[-57.943,154.316],[-27.048,165.544],[5.303,176.601],[37.654,165.544],[68.548,154.316],[80.68,146.877],[86.481,66.071],[91.162,57.662],[142.623,4.597]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":50,"s":[{"i":[[51.445,12.472],[11.245,-13.381],[2.922,3.477],[38.8,-9.406],[-5.196,-23.384],[-28.607,-10.289],[0.785,-3.583],[-16.2,-23.457],[-4.902,-0.674],[-10.738,-7.451],[0,0],[-16.975,11.779],[-7.382,1.015],[-2.812,4.072],[3.47,15.845],[-3.452,1.242],[-4.853,21.841]],"o":[[-38.801,-9.406],[-2.922,3.477],[-11.245,-13.381],[-51.445,12.472],[4.854,21.841],[3.452,1.242],[-3.47,15.845],[2.812,4.072],[7.382,1.015],[16.975,11.779],[0,0],[10.737,-7.451],[4.902,-0.674],[16.2,-23.457],[-0.785,-3.583],[28.607,-10.289],[5.197,-23.384]],"v":[[99.106,-75.949],[21.417,-45.909],[10.242,-45.909],[-67.447,-75.949],[-121.491,4.597],[-70.03,57.662],[-65.348,66.071],[-59.548,146.877],[-47.463,156.159],[-11.521,165.633],[22.145,176.622],[53.18,165.633],[79.075,151.685],[91.207,144.245],[97.007,66.071],[101.688,57.662],[153.149,4.597]],"c":true}]},{"i":{"x":0.12,"y":1},"o":{"x":0.33,"y":0},"t":80,"s":[{"i":[[51.445,12.472],[11.245,-13.381],[2.922,3.477],[38.8,-9.406],[-5.196,-23.384],[-28.607,-10.289],[0.785,-3.583],[-16.2,-23.457],[-4.902,-0.674],[-10.738,-7.451],[0,0],[-16.975,11.779],[-7.382,1.015],[-2.812,4.072],[3.47,15.845],[-3.452,1.242],[-4.853,21.841]],"o":[[-38.801,-9.406],[-2.922,3.477],[-11.245,-13.381],[-51.445,12.472],[4.854,21.841],[3.452,1.242],[-3.47,15.845],[2.812,4.072],[7.382,1.015],[16.975,11.779],[0,0],[10.737,-7.451],[4.902,-0.674],[16.2,-23.457],[-0.785,-3.583],[28.607,-10.289],[5.197,-23.384]],"v":[[99.106,-75.949],[21.417,-45.909],[10.242,-45.909],[-67.447,-75.949],[-121.491,4.597],[-70.03,57.662],[-65.348,66.071],[-59.548,146.877],[-47.463,156.159],[-11.521,165.633],[22.145,176.622],[53.18,165.633],[79.075,151.685],[91.207,144.245],[97.007,66.071],[101.688,57.662],[153.149,4.597]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":120,"s":[{"i":[[51.445,12.472],[11.245,-13.381],[2.922,3.477],[38.8,-9.406],[-5.196,-23.384],[-28.607,-10.289],[0.785,-3.583],[-16.2,-23.457],[-4.902,-0.674],[-10.738,-7.451],[0,0],[-16.975,11.779],[-7.382,1.015],[-2.812,4.072],[3.47,15.845],[-3.452,1.242],[-4.853,21.841]],"o":[[-38.801,-9.406],[-2.922,3.477],[-11.245,-13.381],[-51.445,12.472],[4.854,21.841],[3.452,1.242],[-3.47,15.845],[2.812,4.072],[7.382,1.015],[16.975,11.779],[0,0],[10.737,-7.451],[4.902,-0.674],[16.2,-23.457],[-0.785,-3.583],[28.607,-10.289],[5.197,-23.384]],"v":[[77.001,-75.949],[-0.688,-45.909],[-11.864,-45.909],[-89.552,-75.949],[-143.596,4.597],[-92.135,57.662],[-87.454,66.071],[-82.969,143.456],[-69.785,149.843],[-38.627,165.544],[-6.276,176.601],[26.075,165.544],[56.443,156.948],[68.575,149.508],[74.902,66.071],[79.583,57.662],[131.044,4.597]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":140,"s":[{"i":[[51.445,12.472],[11.245,-13.381],[2.922,3.477],[38.8,-9.406],[-5.196,-23.384],[-28.607,-10.289],[0.785,-3.583],[-16.2,-23.457],[-4.902,-0.674],[-10.738,-7.451],[0,0],[-16.975,11.779],[-7.382,1.015],[-2.812,4.072],[3.47,15.845],[-3.452,1.242],[-4.853,21.841]],"o":[[-38.801,-9.406],[-2.922,3.477],[-11.245,-13.381],[-51.445,12.472],[4.854,21.841],[3.452,1.242],[-3.47,15.845],[2.812,4.072],[7.382,1.015],[16.975,11.779],[0,0],[10.737,-7.451],[4.902,-0.674],[16.2,-23.457],[-0.785,-3.583],[28.607,-10.289],[5.197,-23.384]],"v":[[77.001,-75.949],[-0.688,-45.909],[-11.864,-45.909],[-89.552,-75.949],[-143.596,4.597],[-92.135,57.662],[-87.454,66.071],[-82.969,143.456],[-69.785,149.843],[-38.627,165.544],[-6.276,176.601],[26.075,165.544],[56.443,156.948],[68.575,149.508],[74.902,66.071],[79.583,57.662],[131.044,4.597]],"c":true}]},{"t":170,"s":[{"i":[[51.445,12.472],[11.245,-13.381],[2.922,3.477],[38.8,-9.406],[-5.196,-23.384],[-28.607,-10.289],[0.785,-3.583],[-16.2,-23.457],[-4.902,-0.674],[-10.738,-7.451],[0,0],[-16.975,11.779],[-7.382,1.015],[-2.812,4.072],[3.47,15.845],[-3.452,1.242],[-4.853,21.841]],"o":[[-38.801,-9.406],[-2.922,3.477],[-11.245,-13.381],[-51.445,12.472],[4.854,21.841],[3.452,1.242],[-3.47,15.845],[2.812,4.072],[7.382,1.015],[16.975,11.779],[0,0],[10.737,-7.451],[4.902,-0.674],[16.2,-23.457],[-0.785,-3.583],[28.607,-10.289],[5.197,-23.384]],"v":[[88.579,-75.949],[10.891,-45.909],[-0.285,-45.909],[-77.973,-75.949],[-132.017,4.597],[-80.556,57.662],[-75.875,66.071],[-70.075,146.877],[-57.943,154.316],[-27.048,165.544],[5.303,176.601],[37.654,165.544],[68.548,154.316],[80.68,146.877],[86.481,66.071],[91.162,57.662],[142.623,4.597]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.941176474094,0.847058832645,0.701960802078,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":19,"s":[{"i":[[24.154,48.305],[13.203,43.885],[77.248,0],[15.205,-50.539],[21.999,-43.995],[-198.638,0]],"o":[[-21.999,-43.995],[-15.205,-50.539],[-78.08,0],[-13.203,43.885],[-24.154,48.305],[198.638,0]],"v":[[169.587,-20.913],[146.026,-110.813],[5.303,-207.093],[-135.42,-110.813],[-158.981,-20.913],[5.303,159.087]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":50,"s":[{"i":[[24.154,48.305],[13.203,43.885],[77.248,0],[15.205,-50.539],[21.999,-43.995],[-198.638,0]],"o":[[-21.999,-43.995],[-15.205,-50.539],[-78.08,0],[-13.203,43.885],[-24.154,48.305],[198.638,0]],"v":[[169.587,-20.913],[146.026,-110.813],[5.303,-207.093],[-135.42,-110.813],[-158.981,-20.913],[5.303,159.087]],"c":true}]},{"i":{"x":0.12,"y":1},"o":{"x":0.33,"y":0},"t":80,"s":[{"i":[[24.154,48.305],[13.203,43.885],[77.248,0],[15.205,-50.539],[21.999,-43.995],[-198.638,0]],"o":[[-21.999,-43.995],[-15.205,-50.539],[-78.08,0],[-13.203,43.885],[-24.154,48.305],[198.638,0]],"v":[[169.587,-20.913],[146.026,-110.813],[5.303,-207.093],[-135.42,-110.813],[-158.981,-20.913],[5.303,159.087]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":120,"s":[{"i":[[24.154,48.305],[13.203,43.885],[77.248,0],[15.205,-50.539],[21.999,-43.995],[-198.638,0]],"o":[[-21.999,-43.995],[-15.205,-50.539],[-78.08,0],[-13.203,43.885],[-24.154,48.305],[198.638,0]],"v":[[169.587,-20.913],[146.026,-110.813],[5.303,-207.093],[-135.42,-110.813],[-158.981,-20.913],[5.303,159.087]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":140,"s":[{"i":[[24.154,48.305],[13.203,43.885],[77.248,0],[15.205,-50.539],[21.999,-43.995],[-198.638,0]],"o":[[-21.999,-43.995],[-15.205,-50.539],[-78.08,0],[-13.203,43.885],[-24.154,48.305],[198.638,0]],"v":[[169.587,-20.913],[146.026,-110.813],[5.303,-207.093],[-135.42,-110.813],[-158.981,-20.913],[5.303,159.087]],"c":true}]},{"t":170,"s":[{"i":[[24.154,48.305],[13.203,43.885],[77.248,0],[15.205,-50.539],[21.999,-43.995],[-198.638,0]],"o":[[-21.999,-43.995],[-15.205,-50.539],[-78.08,0],[-13.203,43.885],[-24.154,48.305],[198.638,0]],"v":[[169.587,-20.913],[146.026,-110.813],[5.303,-207.093],[-135.42,-110.813],[-158.981,-20.913],[5.303,159.087]],"c":true}]}]},"hd":false},{"ty":"st","c":{"a":0,"k":[0.368627458811,0.258823543787,0.121568627656,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":1,"lj":1,"ml":10,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":19,"s":[{"i":[[0,0],[-7.633,-2.073]],"o":[[0,0],[0,0]],"v":[[41.579,-186.44],[54.078,-183.608]],"c":false}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":50,"s":[{"i":[[0,0],[-7.633,-2.073]],"o":[[0,0],[0,0]],"v":[[41.579,-186.44],[54.078,-183.608]],"c":false}]},{"i":{"x":0.12,"y":1},"o":{"x":0.33,"y":0},"t":80,"s":[{"i":[[0,0],[-7.633,-2.073]],"o":[[0,0],[0,0]],"v":[[41.579,-186.44],[54.078,-183.608]],"c":false}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":120,"s":[{"i":[[0,0],[-7.633,-2.073]],"o":[[0,0],[0,0]],"v":[[41.579,-186.44],[54.078,-183.608]],"c":false}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":140,"s":[{"i":[[0,0],[-7.633,-2.073]],"o":[[0,0],[0,0]],"v":[[41.579,-186.44],[54.078,-183.608]],"c":false}]},{"t":170,"s":[{"i":[[0,0],[-7.633,-2.073]],"o":[[0,0],[0,0]],"v":[[41.579,-186.44],[54.078,-183.608]],"c":false}]}]},"hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":6},"lc":2,"lj":2,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":19,"s":[{"i":[[0,0],[-1.612,-0.576]],"o":[[1.511,0.494],[0,0]],"v":[[62.717,-181.024],[67.407,-179.42]],"c":false}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":50,"s":[{"i":[[0,0],[-1.612,-0.576]],"o":[[1.511,0.494],[0,0]],"v":[[62.717,-181.024],[67.407,-179.42]],"c":false}]},{"i":{"x":0.12,"y":1},"o":{"x":0.33,"y":0},"t":80,"s":[{"i":[[0,0],[-1.612,-0.576]],"o":[[1.511,0.494],[0,0]],"v":[[62.717,-181.024],[67.407,-179.42]],"c":false}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":120,"s":[{"i":[[0,0],[-1.612,-0.576]],"o":[[1.511,0.494],[0,0]],"v":[[62.717,-181.024],[67.407,-179.42]],"c":false}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":140,"s":[{"i":[[0,0],[-1.612,-0.576]],"o":[[1.511,0.494],[0,0]],"v":[[62.717,-181.024],[67.407,-179.42]],"c":false}]},{"t":170,"s":[{"i":[[0,0],[-1.612,-0.576]],"o":[[1.511,0.494],[0,0]],"v":[[62.717,-181.024],[67.407,-179.42]],"c":false}]}]},"hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":6},"lc":2,"lj":2,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":19,"s":[{"i":[[0,-23.536],[50.223,0],[0,23.536],[-50.223,0]],"o":[[0,23.536],[-50.223,0],[0,-23.536],[50.223,0]],"v":[[96.859,-148.606],[5.921,-105.99],[-85.016,-148.606],[5.921,-191.222]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":50,"s":[{"i":[[0,-23.536],[50.223,0],[0,23.536],[-50.223,0]],"o":[[0,23.536],[-50.223,0],[0,-23.536],[50.223,0]],"v":[[96.859,-148.606],[5.921,-105.99],[-85.016,-148.606],[5.921,-191.222]],"c":true}]},{"i":{"x":0.12,"y":1},"o":{"x":0.33,"y":0},"t":80,"s":[{"i":[[0,-23.536],[50.223,0],[0,23.536],[-50.223,0]],"o":[[0,23.536],[-50.223,0],[0,-23.536],[50.223,0]],"v":[[96.859,-148.606],[5.921,-105.99],[-85.016,-148.606],[5.921,-191.222]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":120,"s":[{"i":[[0,-23.536],[50.223,0],[0,23.536],[-50.223,0]],"o":[[0,23.536],[-50.223,0],[0,-23.536],[50.223,0]],"v":[[96.859,-148.606],[5.921,-105.99],[-85.016,-148.606],[5.921,-191.222]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":140,"s":[{"i":[[0,-23.536],[50.223,0],[0,23.536],[-50.223,0]],"o":[[0,23.536],[-50.223,0],[0,-23.536],[50.223,0]],"v":[[96.859,-148.606],[5.921,-105.99],[-85.016,-148.606],[5.921,-191.222]],"c":true}]},{"t":170,"s":[{"i":[[0,-23.536],[50.223,0],[0,23.536],[-50.223,0]],"o":[[0,23.536],[-50.223,0],[0,-23.536],[50.223,0]],"v":[[96.859,-148.606],[5.921,-105.99],[-85.016,-148.606],[5.921,-191.222]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.780392169952,0.623529434204,0.419607847929,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":19,"s":[{"i":[[22.173,44.342],[12.12,40.285],[70.911,0],[13.958,-46.393],[20.194,-40.385],[-182.343,0]],"o":[[-20.194,-40.385],[-13.957,-46.393],[-71.675,0],[-12.12,40.285],[-22.173,44.342],[182.343,0]],"v":[[156.11,-21.166],[134.482,-103.692],[5.303,-192.073],[-123.876,-103.692],[-145.504,-21.166],[5.303,144.067]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":50,"s":[{"i":[[22.173,44.342],[12.12,40.285],[70.911,0],[13.958,-46.393],[20.194,-40.385],[-182.343,0]],"o":[[-20.194,-40.385],[-13.957,-46.393],[-71.675,0],[-12.12,40.285],[-22.173,44.342],[182.343,0]],"v":[[156.11,-21.166],[134.482,-103.692],[5.303,-192.073],[-123.876,-103.692],[-145.504,-21.166],[5.303,144.067]],"c":true}]},{"i":{"x":0.12,"y":1},"o":{"x":0.33,"y":0},"t":80,"s":[{"i":[[22.173,44.342],[12.12,40.285],[70.911,0],[13.958,-46.393],[20.194,-40.385],[-182.343,0]],"o":[[-20.194,-40.385],[-13.957,-46.393],[-71.675,0],[-12.12,40.285],[-22.173,44.342],[182.343,0]],"v":[[156.11,-21.166],[134.482,-103.692],[5.303,-192.073],[-123.876,-103.692],[-145.504,-21.166],[5.303,144.067]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":120,"s":[{"i":[[22.173,44.342],[12.12,40.285],[70.911,0],[13.958,-46.393],[20.194,-40.385],[-182.343,0]],"o":[[-20.194,-40.385],[-13.957,-46.393],[-71.675,0],[-12.12,40.285],[-22.173,44.342],[182.343,0]],"v":[[156.11,-21.166],[134.482,-103.692],[5.303,-192.073],[-123.876,-103.692],[-145.504,-21.166],[5.303,144.067]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":140,"s":[{"i":[[22.173,44.342],[12.12,40.285],[70.911,0],[13.958,-46.393],[20.194,-40.385],[-182.343,0]],"o":[[-20.194,-40.385],[-13.957,-46.393],[-71.675,0],[-12.12,40.285],[-22.173,44.342],[182.343,0]],"v":[[156.11,-21.166],[134.482,-103.692],[5.303,-192.073],[-123.876,-103.692],[-145.504,-21.166],[5.303,144.067]],"c":true}]},{"t":170,"s":[{"i":[[22.173,44.342],[12.12,40.285],[70.911,0],[13.958,-46.393],[20.194,-40.385],[-182.343,0]],"o":[[-20.194,-40.385],[-13.957,-46.393],[-71.675,0],[-12.12,40.285],[-22.173,44.342],[182.343,0]],"v":[[156.11,-21.166],[134.482,-103.692],[5.303,-192.073],[-123.876,-103.692],[-145.504,-21.166],[5.303,144.067]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.737254917622,0.572549045086,0.349019616842,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":19,"s":[{"i":[[24.154,48.305],[13.203,43.885],[77.248,0],[15.205,-50.539],[21.999,-43.995],[-198.638,0]],"o":[[-21.999,-43.995],[-15.205,-50.539],[-78.08,0],[-13.203,43.885],[-24.154,48.305],[198.638,0]],"v":[[169.587,-20.913],[146.026,-110.813],[5.303,-207.093],[-135.42,-110.813],[-158.981,-20.913],[5.303,159.087]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":50,"s":[{"i":[[24.154,48.305],[13.203,43.885],[77.248,0],[15.205,-50.539],[21.999,-43.995],[-198.638,0]],"o":[[-21.999,-43.995],[-15.205,-50.539],[-78.08,0],[-13.203,43.885],[-24.154,48.305],[198.638,0]],"v":[[169.587,-20.913],[146.026,-110.813],[5.303,-207.093],[-135.42,-110.813],[-158.981,-20.913],[5.303,159.087]],"c":true}]},{"i":{"x":0.12,"y":1},"o":{"x":0.33,"y":0},"t":80,"s":[{"i":[[24.154,48.305],[13.203,43.885],[77.248,0],[15.205,-50.539],[21.999,-43.995],[-198.638,0]],"o":[[-21.999,-43.995],[-15.205,-50.539],[-78.08,0],[-13.203,43.885],[-24.154,48.305],[198.638,0]],"v":[[169.587,-20.913],[146.026,-110.813],[5.303,-207.093],[-135.42,-110.813],[-158.981,-20.913],[5.303,159.087]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":120,"s":[{"i":[[24.154,48.305],[13.203,43.885],[77.248,0],[15.205,-50.539],[21.999,-43.995],[-198.638,0]],"o":[[-21.999,-43.995],[-15.205,-50.539],[-78.08,0],[-13.203,43.885],[-24.154,48.305],[198.638,0]],"v":[[169.587,-20.913],[146.026,-110.813],[5.303,-207.093],[-135.42,-110.813],[-158.981,-20.913],[5.303,159.087]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":140,"s":[{"i":[[24.154,48.305],[13.203,43.885],[77.248,0],[15.205,-50.539],[21.999,-43.995],[-198.638,0]],"o":[[-21.999,-43.995],[-15.205,-50.539],[-78.08,0],[-13.203,43.885],[-24.154,48.305],[198.638,0]],"v":[[169.587,-20.913],[146.026,-110.813],[5.303,-207.093],[-135.42,-110.813],[-158.981,-20.913],[5.303,159.087]],"c":true}]},{"t":170,"s":[{"i":[[24.154,48.305],[13.203,43.885],[77.248,0],[15.205,-50.539],[21.999,-43.995],[-198.638,0]],"o":[[-21.999,-43.995],[-15.205,-50.539],[-78.08,0],[-13.203,43.885],[-24.154,48.305],[198.638,0]],"v":[[169.587,-20.913],[146.026,-110.813],[5.303,-207.093],[-135.42,-110.813],[-158.981,-20.913],[5.303,159.087]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.61960786581,0.46274510026,0.239215686917,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false}],"ip":0,"op":180,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"parent":7,"sr":1,"ks":{"p":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":19,"s":[-148.03,-63.49,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.34,"y":0.34},"o":{"x":0.33,"y":0.33},"t":50,"s":[-159.609,-63.49,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.12,"y":1},"o":{"x":0.33,"y":0},"t":80,"s":[-159.609,-63.49,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.34,"y":0.34},"o":{"x":0.33,"y":0.33},"t":120,"s":[-137.504,-63.49,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":140,"s":[-137.504,-63.49,0],"to":[0,0,0],"ti":[0,0,0]},{"t":170,"s":[-148.03,-63.49,0]}]},"a":{"a":0,"k":[-148.03,-63.49,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[1.081,-15.591],[0,0],[0.01,0],[-5.699,9.64],[0,0]],"o":[[-42.636,-22.157],[-0.01,0],[-1.283,-12.428],[16.864,-28.514],[5.547,-0.606]],"v":[[-120.56,-89.291],[-178.963,-61.424],[-178.983,-61.434],[-172.678,-95.859],[-126.714,-115.865]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.858823537827,0.596078455448,0.592156887054,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[17.728,-1.949],[16.87,-28.514],[-21.623,-17.626]],"o":[[0,0],[-13.747,23.235],[25.716,20.962]],"v":[[-126.711,-115.866],[-172.682,-95.857],[-156.36,-15.922]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.929411768913,0.674509823322,0.666666686535,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[55.083,3.638],[22.172,-27.368],[-47.288,-39.234]],"o":[[-19.765,-1.305],[-24.472,30.208],[49.528,41.092]],"v":[[-115.798,-143.021],[-186.99,-118.078],[-164.645,10.795]],"c":true}},"hd":false},{"ty":"st","c":{"a":0,"k":[0.368627458811,0.258823543787,0.121568627656,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":1,"lj":1,"ml":10,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-36.319,10.182]],"o":[[0,0],[0,0]],"v":[[-194.126,-81.406],[-152.972,-131.334]],"c":false}},"hd":false},{"ty":"st","c":{"a":0,"k":[0.737254917622,0.572549045086,0.349019616842,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":10},"lc":2,"lj":2,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[55.083,3.638],[22.172,-27.368],[-47.288,-39.234]],"o":[[-19.765,-1.305],[-24.472,30.208],[49.528,41.092]],"v":[[-115.798,-143.021],[-186.99,-118.078],[-164.645,10.795]],"c":true}},"hd":false},{"ty":"st","c":{"a":0,"k":[0.368627458811,0.258823543787,0.121568627656,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":1,"lj":1,"ml":10,"bm":0,"hd":false},{"ty":"fl","c":{"a":0,"k":[0.61960786581,0.46274510026,0.239215686917,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false}],"ip":0,"op":180,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"parent":7,"sr":1,"ks":{"p":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":19,"s":[157.962,-63.49,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.34,"y":0.34},"o":{"x":0.33,"y":0.33},"t":50,"s":[146.383,-63.49,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.12,"y":1},"o":{"x":0.33,"y":0},"t":80,"s":[146.383,-63.49,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.34,"y":0.34},"o":{"x":0.33,"y":0.33},"t":120,"s":[168.488,-63.49,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":140,"s":[168.488,-63.49,0],"to":[0,0,0],"ti":[0,0,0]},{"t":170,"s":[157.962,-63.49,0]}]},"a":{"a":0,"k":[157.962,-63.49,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-1.081,-15.591],[0,0],[-0.01,0],[5.699,9.64],[0,0]],"o":[[42.636,-22.157],[0.01,0],[1.283,-12.428],[-16.864,-28.514],[-5.547,-0.606]],"v":[[130.917,-89.291],[189.319,-61.424],[189.34,-61.434],[183.034,-95.859],[137.07,-115.865]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.858823537827,0.596078455448,0.592156887054,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-55.083,3.638],[-22.172,-27.368],[47.288,-39.234]],"o":[[19.765,-1.305],[24.472,30.208],[-49.528,41.092]],"v":[[125.73,-143.021],[196.922,-118.078],[174.577,10.795]],"c":true}},"hd":false},{"ty":"st","c":{"a":0,"k":[0.368627458811,0.258823543787,0.121568627656,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":1,"lj":1,"ml":10,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[36.319,10.182]],"o":[[0,0],[0,0]],"v":[[203.905,-80.406],[162.752,-130.334]],"c":false}},"hd":false},{"ty":"st","c":{"a":0,"k":[0.737254917622,0.572549045086,0.349019616842,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":10},"lc":2,"lj":2,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-17.728,-1.949],[-16.87,-28.514],[21.623,-17.626]],"o":[[0,0],[13.747,23.235],[-25.716,20.962]],"v":[[136.643,-115.866],[182.613,-95.857],[166.291,-15.922]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.929411768913,0.674509823322,0.666666686535,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-55.083,3.638],[-22.172,-27.368],[47.288,-39.234]],"o":[[19.765,-1.305],[24.472,30.208],[-49.528,41.092]],"v":[[125.73,-143.021],[196.922,-118.078],[174.577,10.795]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.61960786581,0.46274510026,0.239215686917,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false}],"ip":0,"op":180,"st":0,"bm":0}]}{"tgs":1,"v":"5.5.2","fr":60,"ip":0,"op":33,"w":512,"h":512,"nm":"password_peek","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":3,"sr":1,"ks":{"o":{"a":0,"k":0},"p":{"a":0,"k":[256,256,0]},"s":{"a":0,"k":[95,95,100]}},"ao":0,"ip":0,"op":33,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":3,"parent":11,"sr":1,"ks":{"o":{"a":0,"k":0}},"ao":0,"ip":0,"op":33,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"sr":1,"ks":{"p":{"a":0,"k":[127.498,460.89,0]},"a":{"a":0,"k":[150.003,211.463,0]},"s":{"a":0,"k":[-95,95,100]}},"ao":0,"ef":[{"ty":5,"nm":"arm R: Path 1 [1.1.1.0]","np":3,"mn":"ADBE Layer Control","ix":1,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":3}}]},{"ty":5,"nm":"arm R: Path 1 [1.1.1.1]","np":3,"mn":"ADBE Layer Control","ix":2,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":3}}]},{"ty":5,"nm":"arm R: Path 1 [1.1.1.2]","np":3,"mn":"ADBE Layer Control","ix":3,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":3}}]},{"ty":5,"nm":"arm R: Path 1 [1.2.1.0]","np":3,"mn":"ADBE Layer Control","ix":4,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":3}}]},{"ty":5,"nm":"arm R: Path 1 [1.2.1.1]","np":3,"mn":"ADBE Layer Control","ix":5,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":3}}]},{"ty":5,"nm":"arm R: Path 1 [1.2.1.2]","np":3,"mn":"ADBE Layer Control","ix":6,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":3}}]},{"ty":5,"nm":"arm R: Path 1 [1.3.1.0]","np":3,"mn":"ADBE Layer Control","ix":7,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":3}}]},{"ty":5,"nm":"arm R: Path 1 [1.3.1.1]","np":3,"mn":"ADBE Layer Control","ix":8,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":3}}]},{"ty":5,"nm":"arm R: Path 1 [1.3.1.2]","np":3,"mn":"ADBE Layer Control","ix":9,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":3}}]},{"ty":5,"nm":"arm R: Path 1 [1.3.1.3]","np":3,"mn":"ADBE Layer Control","ix":10,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":3}}]},{"ty":5,"nm":"arm R: Path 1 [1.3.1.4]","np":3,"mn":"ADBE Layer Control","ix":11,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":3}}]},{"ty":5,"nm":"arm R: Path 1 [1.3.1.5]","np":3,"mn":"ADBE Layer Control","ix":12,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":3}}]},{"ty":5,"nm":"arm R: Path 1 [1.3.1.6]","np":3,"mn":"ADBE Layer Control","ix":13,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":3}}]},{"ty":5,"nm":"arm R: Path 1 [1.4.1.0]","np":3,"mn":"ADBE Layer Control","ix":14,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":3}}]},{"ty":5,"nm":"arm R: Path 1 [1.4.1.1]","np":3,"mn":"ADBE Layer Control","ix":15,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":3}}]},{"ty":5,"nm":"arm R: Path 1 [1.4.1.2]","np":3,"mn":"ADBE Layer Control","ix":16,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":3}}]},{"ty":5,"nm":"arm R: Path 1 [1.4.1.3]","np":3,"mn":"ADBE Layer Control","ix":17,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":3}}]},{"ty":5,"nm":"arm R: Path 1 [1.4.1.4]","np":3,"mn":"ADBE Layer Control","ix":18,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":3}}]},{"ty":5,"nm":"arm R: Path 1 [1.4.1.5]","np":3,"mn":"ADBE Layer Control","ix":19,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":3}}]},{"ty":5,"nm":"arm R: Path 1 [1.4.1.6]","np":3,"mn":"ADBE Layer Control","ix":20,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":3}}]},{"ty":5,"nm":"arm R: Path 1 [1.4.1.7]","np":3,"mn":"ADBE Layer Control","ix":21,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":3}}]},{"ty":5,"nm":"arm R: Path 1 [1.4.1.8]","np":3,"mn":"ADBE Layer Control","ix":22,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":3}}]},{"ty":5,"nm":"arm R: Path 1 [1.4.1.9]","np":3,"mn":"ADBE Layer Control","ix":23,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":3}}]},{"ty":5,"nm":"arm R: Path 1 [1.4.1.10]","np":3,"mn":"ADBE Layer Control","ix":24,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":3}}]},{"ty":5,"nm":"arm R: Path 1 [1.4.1.11]","np":3,"mn":"ADBE Layer Control","ix":25,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":3}}]},{"ty":5,"nm":"arm R: Path 1 [1.4.1.12]","np":3,"mn":"ADBE Layer Control","ix":26,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":3}}]},{"ty":5,"nm":"arm R: Path 1 [1.4.1.13]","np":3,"mn":"ADBE Layer Control","ix":27,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":3}}]},{"ty":5,"nm":"arm R: Path 1 [1.4.1.14]","np":3,"mn":"ADBE Layer Control","ix":28,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":3}}]},{"ty":5,"nm":"arm R: Path 1 [1.4.1.15]","np":3,"mn":"ADBE Layer Control","ix":29,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":3}}]},{"ty":5,"nm":"arm R: Path 1 [1.4.1.16]","np":3,"mn":"ADBE Layer Control","ix":30,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":3}}]}],"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-2.938,-16.688],[0,0]],"o":[[0,0],[3.188,18.25],[0,0]],"v":[[73.75,56],[98.625,84.625],[114.063,202.063]],"c":false}},"hd":false},{"ty":"st","c":{"a":0,"k":[0.368627458811,0.258823543787,0.121568627656,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":2,"lj":2,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[1.063,18.688],[-2.75,5.813]],"o":[[0,0],[-0.625,-11.375],[0,0]],"v":[[189.375,202.063],[156.688,64.313],[162.188,37.5]],"c":false}},"hd":false},{"ty":"st","c":{"a":0,"k":[0.368627458811,0.258823543787,0.121568627656,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":2,"lj":2,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-18.188,2.438],[0,0],[8.313,39.188],[0,0],[11.75,-13.313],[-4.125,-33.688]],"o":[[0,0],[21.5,-2.875],[0,0],[0,0],[-2.75,-14.625],[-8.25,9.375],[4,32.313]],"v":[[131.75,211.063],[164.125,212.188],[190.375,204.063],[159.875,83.125],[157.875,87.125],[125.688,70.563],[124.75,138.063]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.737254917622,0.572549045086,0.349019616842,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[1.063,18.688],[0,0],[0,0],[0,0],[1.313,-1.438],[0,0],[0,0],[0.563,-1.938],[0,0],[0,0],[0,-1.75],[0,0],[0,0],[-3.5,-18.188],[0,0],[-19.688,0.063]],"o":[[0,0],[-1.063,-18.688],[0,0],[0,0],[-0.563,-1.813],[0,0],[0,0],[-1.25,-1.563],[0,0],[0,0],[-1.625,-0.625],[0,0],[0,0],[0,0],[3.5,18.188],[0,0],[34.313,-0.063]],"v":[[190.375,204.063],[156.688,64.313],[159.813,34.75],[150.438,44.563],[146.563,32.125],[142.313,31.125],[134.25,39.938],[123.25,26],[118.875,26.813],[113.625,44.063],[96.188,37.125],[92.75,39.438],[92.75,59.125],[73.25,51.125],[98.625,84.625],[114.063,205.063],[148,212.75]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.61960786581,0.46274510026,0.239215686917,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-12.183,-34.643]],"o":[[0,0],[0,0]],"v":[[41.387,-96.927],[74.702,-38.38]],"c":false}},"hd":false},{"ty":"st","c":{"a":0,"k":[0.580392181873,0.376470595598,0.215686276555,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":2,"lj":2,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-3.811,-17.322]],"o":[[0,0],[0,0]],"v":[[133.538,-74.755],[147.742,-34.569]],"c":false}},"hd":false},{"ty":"st","c":{"a":0,"k":[0.580392181873,0.376470595598,0.215686276555,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":2,"lj":2,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-9.007,-30.14]],"o":[[0,0],[0,0]],"v":[[76.723,-110.438],[108.941,-50.159]],"c":false}},"hd":false},{"ty":"st","c":{"a":0,"k":[0.580392181873,0.376470595598,0.215686276555,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":2,"lj":2,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-10.22,25.636],[3.943,28.75],[20.093,-1.386],[17.668,8.661],[0.693,-7.275],[12.125,-0.373],[-1.732,-12.125],[5.123,-13.539],[-3.788,-20.696],[-23.034,-21.084]],"o":[[0,0],[-3.849,-28.061],[0,0],[-13.289,-6.514],[0,0],[-12.125,0.373],[0,0],[-4.85,12.818],[4.56,24.912],[23.034,21.084]],"v":[[165.237,31.599],[172.377,-15.516],[133.538,-74.755],[98.895,-119.792],[76.723,-110.438],[56.63,-121.551],[41.387,-96.927],[24.412,-87.574],[41.676,-22.528],[71.867,54.464]],"c":true}},"hd":false},{"ty":"st","c":{"a":0,"k":[0.580392181873,0.376470595598,0.215686276555,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":2,"lj":2,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[25.645,70.013],[-0.889,-4.86],[-23.038,-21.088],[-10.215,25.635],[3.941,28.747],[1.172,4.577],[42.671,-3.275]],"o":[[2.001,5.679],[4.557,24.917],[23.028,21.078],[0,0],[-0.617,-4.506],[0,0],[-33.501,2.571]],"v":[[37.09,-38.535],[41.677,-22.529],[71.869,54.465],[165.232,31.599],[172.376,-15.517],[169.688,-29.218],[125.987,33.186]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.86274510622,0.690196096897,0.498039215803,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-2.11,-7.28],[0.08,1.65],[-12.857,-21.206],[-4.596,-2.59],[0,0],[7.09,11.6],[0,0],[-12.323,-15.225],[-4.52,-8.91],[-0.32,1.03],[0,0],[0,0],[-15.78,-21.096],[-6.251,-3.228],[0,0],[0,0],[-0.5,-0.5],[10.24,-0.71],[17.66,8.66],[0.7,-7.28],[12.13,-0.37],[-1.73,-12.13],[5.13,-13.54]],"o":[[0.81,-1.63],[-0.54,-10.39],[13.69,22.58],[6.049,3.409],[-2.93,-11.65],[-7.1,-11.59],[5.15,-18],[11.13,13.751],[4.52,8.92],[0.33,-1.03],[-4.26,-15.14],[1.37,-11.32],[18.31,24.479],[5.494,2.837],[0,0],[3.64,-15.88],[-6.75,-11.84],[0,0],[-13.29,-6.51],[0,0],[-12.12,0.38],[0,0],[-1.61,4.25]],"v":[[26.173,-69.753],[27.343,-74.753],[48.083,-70.753],[68.653,-33.843],[74.463,-39.163],[54.034,-80.303],[44.824,-98.643],[74.463,-95.213],[98.713,-51.333],[109.264,-48.413],[108.614,-52.253],[84.773,-102.243],[111.373,-92.133],[140.114,-32.473],[147.793,-35.753],[142.653,-52.793],[159.744,-55.513],[133.864,-74.753],[99.224,-119.793],[77.043,-110.433],[56.954,-121.553],[41.713,-96.923],[24.733,-87.573]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.988235294819,0.890196084976,0.780392169952,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-10.22,25.636],[3.943,28.75],[20.093,-1.386],[17.668,8.661],[0.693,-7.275],[12.125,-0.373],[-1.732,-12.125],[5.123,-13.539],[-3.788,-20.696],[-23.034,-21.084]],"o":[[0,0],[-3.849,-28.061],[0,0],[-13.289,-6.514],[0,0],[-12.125,0.373],[0,0],[-4.85,12.818],[4.56,24.912],[23.034,21.084]],"v":[[165.237,31.599],[172.377,-15.516],[133.538,-74.755],[98.895,-119.792],[76.723,-110.438],[56.63,-121.551],[41.387,-96.927],[24.412,-87.574],[41.676,-22.528],[71.867,54.464]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.913725495338,0.78823530674,0.647058844566,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false}],"ip":0,"op":33,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"sr":1,"ks":{"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.424],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":13,"s":[0]},{"i":{"x":[0.576],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":20,"s":[0]},{"t":33,"s":[0]}]},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.33,"y":0},"t":0,"s":[394.502,460.89,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0,"y":0},"o":{"x":0.33,"y":0.33},"t":13,"s":[378.502,550.89,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.67,"y":1},"o":{"x":1,"y":0},"t":20,"s":[378.502,550.89,0],"to":[0,0,0],"ti":[0,0,0]},{"t":33,"s":[394.502,460.89,0]}]},"a":{"a":0,"k":[150.003,211.463,0]},"s":{"a":0,"k":[95,95,100]}},"ao":0,"ef":[{"ty":5,"nm":"arm R: Path 1 [1.1.1.0]","np":3,"mn":"ADBE Layer Control","ix":1,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":4}}]},{"ty":5,"nm":"arm R: Path 1 [1.1.1.1]","np":3,"mn":"ADBE Layer Control","ix":2,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":4}}]},{"ty":5,"nm":"arm R: Path 1 [1.1.1.2]","np":3,"mn":"ADBE Layer Control","ix":3,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":4}}]},{"ty":5,"nm":"arm R: Path 1 [1.2.1.0]","np":3,"mn":"ADBE Layer Control","ix":4,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":4}}]},{"ty":5,"nm":"arm R: Path 1 [1.2.1.1]","np":3,"mn":"ADBE Layer Control","ix":5,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":4}}]},{"ty":5,"nm":"arm R: Path 1 [1.2.1.2]","np":3,"mn":"ADBE Layer Control","ix":6,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":4}}]},{"ty":5,"nm":"arm R: Path 1 [1.3.1.0]","np":3,"mn":"ADBE Layer Control","ix":7,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":4}}]},{"ty":5,"nm":"arm R: Path 1 [1.3.1.1]","np":3,"mn":"ADBE Layer Control","ix":8,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":4}}]},{"ty":5,"nm":"arm R: Path 1 [1.3.1.2]","np":3,"mn":"ADBE Layer Control","ix":9,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":4}}]},{"ty":5,"nm":"arm R: Path 1 [1.3.1.3]","np":3,"mn":"ADBE Layer Control","ix":10,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":4}}]},{"ty":5,"nm":"arm R: Path 1 [1.3.1.4]","np":3,"mn":"ADBE Layer Control","ix":11,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":4}}]},{"ty":5,"nm":"arm R: Path 1 [1.3.1.5]","np":3,"mn":"ADBE Layer Control","ix":12,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":4}}]},{"ty":5,"nm":"arm R: Path 1 [1.3.1.6]","np":3,"mn":"ADBE Layer Control","ix":13,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":4}}]},{"ty":5,"nm":"arm R: Path 1 [1.4.1.0]","np":3,"mn":"ADBE Layer Control","ix":14,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":4}}]},{"ty":5,"nm":"arm R: Path 1 [1.4.1.1]","np":3,"mn":"ADBE Layer Control","ix":15,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":4}}]},{"ty":5,"nm":"arm R: Path 1 [1.4.1.2]","np":3,"mn":"ADBE Layer Control","ix":16,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":4}}]},{"ty":5,"nm":"arm R: Path 1 [1.4.1.3]","np":3,"mn":"ADBE Layer Control","ix":17,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":4}}]},{"ty":5,"nm":"arm R: Path 1 [1.4.1.4]","np":3,"mn":"ADBE Layer Control","ix":18,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":4}}]},{"ty":5,"nm":"arm R: Path 1 [1.4.1.5]","np":3,"mn":"ADBE Layer Control","ix":19,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":4}}]},{"ty":5,"nm":"arm R: Path 1 [1.4.1.6]","np":3,"mn":"ADBE Layer Control","ix":20,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":4}}]},{"ty":5,"nm":"arm R: Path 1 [1.4.1.7]","np":3,"mn":"ADBE Layer Control","ix":21,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":4}}]},{"ty":5,"nm":"arm R: Path 1 [1.4.1.8]","np":3,"mn":"ADBE Layer Control","ix":22,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":4}}]},{"ty":5,"nm":"arm R: Path 1 [1.4.1.9]","np":3,"mn":"ADBE Layer Control","ix":23,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":4}}]},{"ty":5,"nm":"arm R: Path 1 [1.4.1.10]","np":3,"mn":"ADBE Layer Control","ix":24,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":4}}]},{"ty":5,"nm":"arm R: Path 1 [1.4.1.11]","np":3,"mn":"ADBE Layer Control","ix":25,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":4}}]},{"ty":5,"nm":"arm R: Path 1 [1.4.1.12]","np":3,"mn":"ADBE Layer Control","ix":26,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":4}}]},{"ty":5,"nm":"arm R: Path 1 [1.4.1.13]","np":3,"mn":"ADBE Layer Control","ix":27,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":4}}]},{"ty":5,"nm":"arm R: Path 1 [1.4.1.14]","np":3,"mn":"ADBE Layer Control","ix":28,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":4}}]},{"ty":5,"nm":"arm R: Path 1 [1.4.1.15]","np":3,"mn":"ADBE Layer Control","ix":29,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":4}}]},{"ty":5,"nm":"arm R: Path 1 [1.4.1.16]","np":3,"mn":"ADBE Layer Control","ix":30,"en":1,"ef":[{"ty":10,"nm":"Layer","mn":"ADBE Layer Control-0001","ix":1,"v":{"a":0,"k":4}}]}],"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.33,"y":0},"t":0,"s":[{"i":[[0,0],[-2.938,-16.688],[0,0]],"o":[[0,0],[3.188,18.25],[0,0]],"v":[[73.75,56],[98.625,84.625],[114.063,202.063]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":1,"s":[{"i":[[0,0],[-2.938,-16.688],[0,0]],"o":[[0,0],[3.188,18.25],[0,0]],"v":[[73.75,56],[98.625,84.625],[113.081,199.218]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":2,"s":[{"i":[[0,0],[-2.938,-16.688],[0,0]],"o":[[0,0],[3.188,18.25],[0,0]],"v":[[73.75,56],[98.625,84.625],[111.369,186.872]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":3,"s":[{"i":[[0,0],[-2.938,-16.688],[0,0]],"o":[[0,0],[3.188,18.25],[0,0]],"v":[[73.75,56],[98.625,84.625],[109.303,159.126]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":4,"s":[{"i":[[0,0],[-2.938,-16.688],[0,0]],"o":[[0,0],[3.188,18.25],[0,0]],"v":[[73.75,56],[98.625,84.625],[107.842,140.839]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":5,"s":[{"i":[[0,0],[-2.938,-16.688],[0,0]],"o":[[0,0],[3.188,18.25],[0,0]],"v":[[73.75,56],[98.625,84.625],[106.402,128.737]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":6,"s":[{"i":[[0,0],[-2.938,-16.688],[0,0]],"o":[[0,0],[3.188,18.25],[0,0]],"v":[[73.75,56],[98.625,84.625],[105.017,121.766]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":7,"s":[{"i":[[0,0],[-2.938,-16.688],[0,0]],"o":[[0,0],[3.188,18.25],[0,0]],"v":[[73.75,56],[98.625,84.625],[104.538,116.654]],"c":false}]},{"i":{"x":0,"y":1},"o":{"x":0.167,"y":0.167},"t":8,"s":[{"i":[[0,0],[-2.938,-16.688],[0,0]],"o":[[0,0],[3.188,18.25],[0,0]],"v":[[73.75,56],[98.625,84.625],[104.059,112.859]],"c":false}]},{"i":{"x":0,"y":1},"o":{"x":0.33,"y":0},"t":13,"s":[{"i":[[0,0],[-2.938,-16.688],[0,0]],"o":[[0,0],[3.188,18.25],[0,0]],"v":[[73.75,56],[98.625,84.625],[103.513,107.589]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":1,"y":0},"t":20,"s":[{"i":[[0,0],[-2.938,-16.688],[0,0]],"o":[[0,0],[3.188,18.25],[0,0]],"v":[[73.75,56],[98.625,84.625],[103.513,107.589]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":25,"s":[{"i":[[0,0],[-2.938,-16.688],[0,0]],"o":[[0,0],[3.188,18.25],[0,0]],"v":[[73.75,56],[98.625,84.625],[104.059,112.859]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":26,"s":[{"i":[[0,0],[-2.938,-16.688],[0,0]],"o":[[0,0],[3.188,18.25],[0,0]],"v":[[73.75,56],[98.625,84.625],[104.538,116.654]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":27,"s":[{"i":[[0,0],[-2.938,-16.688],[0,0]],"o":[[0,0],[3.188,18.25],[0,0]],"v":[[73.75,56],[98.625,84.625],[105.017,121.766]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":28,"s":[{"i":[[0,0],[-2.938,-16.688],[0,0]],"o":[[0,0],[3.188,18.25],[0,0]],"v":[[73.75,56],[98.625,84.625],[106.402,128.737]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":29,"s":[{"i":[[0,0],[-2.938,-16.688],[0,0]],"o":[[0,0],[3.188,18.25],[0,0]],"v":[[73.75,56],[98.625,84.625],[107.842,140.839]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":30,"s":[{"i":[[0,0],[-2.938,-16.688],[0,0]],"o":[[0,0],[3.188,18.25],[0,0]],"v":[[73.75,56],[98.625,84.625],[109.303,159.126]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":31,"s":[{"i":[[0,0],[-2.938,-16.688],[0,0]],"o":[[0,0],[3.188,18.25],[0,0]],"v":[[73.75,56],[98.625,84.625],[111.369,186.872]],"c":false}]},{"i":{"x":0.67,"y":1},"o":{"x":0.167,"y":0.167},"t":32,"s":[{"i":[[0,0],[-2.938,-16.688],[0,0]],"o":[[0,0],[3.188,18.25],[0,0]],"v":[[73.75,56],[98.625,84.625],[113.081,199.218]],"c":false}]},{"t":33,"s":[{"i":[[0,0],[-2.938,-16.688],[0,0]],"o":[[0,0],[3.188,18.25],[0,0]],"v":[[73.75,56],[98.625,84.625],[114.063,202.063]],"c":false}]}]},"hd":false},{"ty":"st","c":{"a":0,"k":[0.368627458811,0.258823543787,0.121568627656,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":2,"lj":2,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.33,"y":0},"t":0,"s":[{"i":[[0,0],[1.063,18.688],[-2.75,5.813]],"o":[[0,0],[-0.625,-11.375],[0,0]],"v":[[189.375,202.063],[156.688,64.313],[162.188,37.5]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":1,"s":[{"i":[[0,0],[1.063,18.688],[-2.75,5.813]],"o":[[0,0],[-0.625,-11.375],[0,0]],"v":[[187.752,199.218],[156.688,64.313],[162.188,37.5]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":2,"s":[{"i":[[0,0],[1.063,18.688],[-2.75,5.813]],"o":[[0,0],[-0.625,-11.375],[0,0]],"v":[[184.922,186.872],[156.688,64.313],[162.188,37.5]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":3,"s":[{"i":[[0,0],[1.063,18.688],[-2.75,5.813]],"o":[[0,0],[-0.625,-11.375],[0,0]],"v":[[181.531,159.126],[156.688,64.313],[162.188,37.5]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":4,"s":[{"i":[[0,0],[1.063,18.688],[-2.75,5.813]],"o":[[0,0],[-0.625,-11.375],[0,0]],"v":[[179.109,140.839],[156.688,64.313],[162.188,37.5]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":5,"s":[{"i":[[0,0],[1.063,18.688],[-2.75,5.813]],"o":[[0,0],[-0.625,-11.375],[0,0]],"v":[[176.707,128.737],[156.688,64.313],[162.188,37.5]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":6,"s":[{"i":[[0,0],[1.063,18.688],[-2.75,5.813]],"o":[[0,0],[-0.625,-11.375],[0,0]],"v":[[174.36,121.766],[156.688,64.313],[162.188,37.5]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":7,"s":[{"i":[[0,0],[1.063,18.688],[-2.75,5.813]],"o":[[0,0],[-0.625,-11.375],[0,0]],"v":[[173.517,116.654],[156.688,64.313],[162.188,37.5]],"c":false}]},{"i":{"x":0,"y":1},"o":{"x":0.167,"y":0.167},"t":8,"s":[{"i":[[0,0],[1.063,18.688],[-2.75,5.813]],"o":[[0,0],[-0.625,-11.375],[0,0]],"v":[[172.674,112.859],[156.688,64.313],[162.188,37.5]],"c":false}]},{"i":{"x":0,"y":1},"o":{"x":0.33,"y":0},"t":13,"s":[{"i":[[0,0],[1.063,18.688],[-2.75,5.813]],"o":[[0,0],[-0.625,-11.375],[0,0]],"v":[[171.852,107.589],[156.688,64.313],[162.188,37.5]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":1,"y":0},"t":20,"s":[{"i":[[0,0],[1.063,18.688],[-2.75,5.813]],"o":[[0,0],[-0.625,-11.375],[0,0]],"v":[[171.852,107.589],[156.688,64.313],[162.188,37.5]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":25,"s":[{"i":[[0,0],[1.063,18.688],[-2.75,5.813]],"o":[[0,0],[-0.625,-11.375],[0,0]],"v":[[172.674,112.859],[156.688,64.313],[162.188,37.5]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":26,"s":[{"i":[[0,0],[1.063,18.688],[-2.75,5.813]],"o":[[0,0],[-0.625,-11.375],[0,0]],"v":[[173.517,116.654],[156.688,64.313],[162.188,37.5]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":27,"s":[{"i":[[0,0],[1.063,18.688],[-2.75,5.813]],"o":[[0,0],[-0.625,-11.375],[0,0]],"v":[[174.36,121.766],[156.688,64.313],[162.188,37.5]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":28,"s":[{"i":[[0,0],[1.063,18.688],[-2.75,5.813]],"o":[[0,0],[-0.625,-11.375],[0,0]],"v":[[176.707,128.737],[156.688,64.313],[162.188,37.5]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":29,"s":[{"i":[[0,0],[1.063,18.688],[-2.75,5.813]],"o":[[0,0],[-0.625,-11.375],[0,0]],"v":[[179.109,140.839],[156.688,64.313],[162.188,37.5]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":30,"s":[{"i":[[0,0],[1.063,18.688],[-2.75,5.813]],"o":[[0,0],[-0.625,-11.375],[0,0]],"v":[[181.531,159.126],[156.688,64.313],[162.188,37.5]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":31,"s":[{"i":[[0,0],[1.063,18.688],[-2.75,5.813]],"o":[[0,0],[-0.625,-11.375],[0,0]],"v":[[184.922,186.872],[156.688,64.313],[162.188,37.5]],"c":false}]},{"i":{"x":0.67,"y":1},"o":{"x":0.167,"y":0.167},"t":32,"s":[{"i":[[0,0],[1.063,18.688],[-2.75,5.813]],"o":[[0,0],[-0.625,-11.375],[0,0]],"v":[[187.752,199.218],[156.688,64.313],[162.188,37.5]],"c":false}]},{"t":33,"s":[{"i":[[0,0],[1.063,18.688],[-2.75,5.813]],"o":[[0,0],[-0.625,-11.375],[0,0]],"v":[[189.375,202.063],[156.688,64.313],[162.188,37.5]],"c":false}]}]},"hd":false},{"ty":"st","c":{"a":0,"k":[0.368627458811,0.258823543787,0.121568627656,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":2,"lj":2,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.33,"y":0},"t":0,"s":[{"i":[[0,0],[-18.188,2.438],[0,0],[8.313,39.188],[0,0],[11.75,-13.313],[-4.125,-33.688]],"o":[[0,0],[21.5,-2.875],[0,0],[0,0],[-2.75,-14.625],[-8.25,9.375],[4,32.313]],"v":[[131.75,211.063],[164.125,212.188],[190.375,204.063],[159.875,83.125],[157.875,87.125],[125.688,70.563],[124.75,138.063]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":1,"s":[{"i":[[0,0],[-18.033,2.437],[0,0],[8.313,39.188],[0,0],[11.75,-13.313],[-3.735,-31.621]],"o":[[0,0],[21.317,-2.875],[0,0],[0,0],[-2.75,-14.625],[-8.25,9.375],[3.624,30.15]],"v":[[130.617,208.218],[162.717,209.343],[188.743,201.218],[159.875,83.125],[157.875,87.125],[125.905,70.466],[124.544,135.497]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":2,"s":[{"i":[[0,0],[-17.763,2.436],[0,0],[8.313,39.188],[0,0],[11.75,-13.313],[-3.055,-28.02]],"o":[[0,0],[20.998,-2.875],[0,0],[0,0],[-2.75,-14.625],[-8.25,9.375],[2.967,26.379]],"v":[[128.643,195.872],[160.262,196.997],[185.899,188.872],[159.875,83.125],[157.875,87.125],[126.285,70.297],[124.186,131.025]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":3,"s":[{"i":[[0,0],[-17.443,2.435],[0,0],[8.313,39.188],[0,0],[11.75,-13.313],[-2.248,-23.75]],"o":[[0,0],[20.619,-2.875],[0,0],[0,0],[-2.75,-14.625],[-8.25,9.375],[2.19,21.91]],"v":[[126.266,168.126],[157.315,169.251],[182.49,161.126],[159.875,83.125],[157.875,87.125],[126.735,70.097],[123.761,125.724]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":4,"s":[{"i":[[0,0],[-17.211,2.434],[0,0],[8.793,35.004],[0,0],[11.75,-13.313],[-1.663,-20.652]],"o":[[0,0],[20.345,-2.875],[0,0],[0,0],[-2.75,-14.625],[-8.25,9.375],[1.625,18.667]],"v":[[124.579,149.839],[155.215,150.964],[180.055,142.839],[159.875,83.125],[158.265,86.995],[127.062,69.952],[123.484,119.209]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":5,"s":[{"i":[[0,0],[-16.979,2.434],[0,0],[9.274,30.821],[0,0],[11.75,-13.313],[-1.078,-17.553]],"o":[[0,0],[20.07,-2.875],[0,0],[0,0],[-2.75,-14.625],[-8.25,9.375],[1.061,15.423]],"v":[[122.913,137.737],[153.136,138.862],[177.64,130.737],[159.875,83.125],[158.656,86.865],[127.388,69.807],[123.207,112.695]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":6,"s":[{"i":[[0,0],[-16.747,2.433],[0,0],[9.755,26.638],[0,0],[11.75,-13.313],[-0.493,-14.455]],"o":[[0,0],[19.796,-2.875],[0,0],[0,0],[-2.75,-14.625],[-8.25,9.375],[0.496,12.18]],"v":[[121.303,130.766],[151.112,131.891],[175.281,123.766],[159.875,83.125],[159.046,86.735],[127.715,69.661],[122.93,106.18]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":7,"s":[{"i":[[0,0],[-16.659,2.433],[0,0],[9.936,25.055],[0,0],[11.75,-13.313],[-0.271,-13.283]],"o":[[0,0],[19.692,-2.875],[0,0],[0,0],[-2.75,-14.625],[-8.25,9.375],[0.283,10.954]],"v":[[120.738,125.654],[150.391,126.779],[174.433,118.654],[159.875,83.125],[159.194,86.685],[127.838,69.607],[122.825,103.716]],"c":true}]},{"i":{"x":0,"y":1},"o":{"x":0.167,"y":0.167},"t":8,"s":[{"i":[[0,0],[-16.571,2.432],[0,0],[10.118,23.473],[0,0],[11.75,-13.313],[-0.05,-12.111]],"o":[[0,0],[19.588,-2.875],[0,0],[0,0],[-2.75,-14.625],[-8.25,9.375],[0.069,9.727]],"v":[[120.173,121.859],[149.67,122.984],[173.586,114.859],[159.875,83.125],[159.342,86.636],[127.962,69.552],[122.721,101.252]],"c":true}]},{"i":{"x":0,"y":1},"o":{"x":0.33,"y":0},"t":13,"s":[{"i":[[0,0],[-16.504,2.432],[0,0],[10.257,22.27],[0,0],[11.75,-13.313],[0.118,-11.22]],"o":[[0,0],[19.509,-2.875],[0,0],[0,0],[-2.75,-14.625],[-8.25,9.375],[-0.093,8.794]],"v":[[119.563,116.589],[148.94,117.714],[172.759,109.589],[159.875,83.125],[159.454,86.599],[128.056,69.51],[122.641,99.378]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":1,"y":0},"t":20,"s":[{"i":[[0,0],[-16.504,2.432],[0,0],[10.257,22.27],[0,0],[11.75,-13.313],[0.118,-11.22]],"o":[[0,0],[19.509,-2.875],[0,0],[0,0],[-2.75,-14.625],[-8.25,9.375],[-0.093,8.794]],"v":[[119.563,116.589],[148.94,117.714],[172.759,109.589],[159.875,83.125],[159.454,86.599],[128.056,69.51],[122.641,99.378]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":25,"s":[{"i":[[0,0],[-16.571,2.432],[0,0],[10.118,23.473],[0,0],[11.75,-13.313],[-0.05,-12.111]],"o":[[0,0],[19.588,-2.875],[0,0],[0,0],[-2.75,-14.625],[-8.25,9.375],[0.069,9.727]],"v":[[120.173,121.859],[149.67,122.984],[173.586,114.859],[159.875,83.125],[159.342,86.636],[127.962,69.552],[122.721,101.252]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":26,"s":[{"i":[[0,0],[-16.659,2.433],[0,0],[9.936,25.055],[0,0],[11.75,-13.313],[-0.271,-13.283]],"o":[[0,0],[19.692,-2.875],[0,0],[0,0],[-2.75,-14.625],[-8.25,9.375],[0.283,10.954]],"v":[[120.738,125.654],[150.391,126.779],[174.433,118.654],[159.875,83.125],[159.194,86.685],[127.838,69.607],[122.825,103.716]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":27,"s":[{"i":[[0,0],[-16.747,2.433],[0,0],[9.755,26.638],[0,0],[11.75,-13.313],[-0.493,-14.455]],"o":[[0,0],[19.796,-2.875],[0,0],[0,0],[-2.75,-14.625],[-8.25,9.375],[0.496,12.18]],"v":[[121.303,130.766],[151.112,131.891],[175.281,123.766],[159.875,83.125],[159.046,86.735],[127.715,69.661],[122.93,106.18]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":28,"s":[{"i":[[0,0],[-16.979,2.434],[0,0],[9.274,30.821],[0,0],[11.75,-13.313],[-1.078,-17.553]],"o":[[0,0],[20.07,-2.875],[0,0],[0,0],[-2.75,-14.625],[-8.25,9.375],[1.061,15.423]],"v":[[122.913,137.737],[153.136,138.862],[177.64,130.737],[159.875,83.125],[158.656,86.865],[127.388,69.807],[123.207,112.695]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":29,"s":[{"i":[[0,0],[-17.211,2.434],[0,0],[8.793,35.004],[0,0],[11.75,-13.313],[-1.663,-20.652]],"o":[[0,0],[20.345,-2.875],[0,0],[0,0],[-2.75,-14.625],[-8.25,9.375],[1.625,18.667]],"v":[[124.579,149.839],[155.215,150.964],[180.055,142.839],[159.875,83.125],[158.265,86.995],[127.062,69.952],[123.484,119.209]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":30,"s":[{"i":[[0,0],[-17.443,2.435],[0,0],[8.313,39.188],[0,0],[11.75,-13.313],[-2.248,-23.75]],"o":[[0,0],[20.619,-2.875],[0,0],[0,0],[-2.75,-14.625],[-8.25,9.375],[2.19,21.91]],"v":[[126.266,168.126],[157.315,169.251],[182.49,161.126],[159.875,83.125],[157.875,87.125],[126.735,70.097],[123.761,125.724]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":31,"s":[{"i":[[0,0],[-17.763,2.436],[0,0],[8.313,39.188],[0,0],[11.75,-13.313],[-3.055,-28.02]],"o":[[0,0],[20.998,-2.875],[0,0],[0,0],[-2.75,-14.625],[-8.25,9.375],[2.967,26.379]],"v":[[128.643,195.872],[160.262,196.997],[185.899,188.872],[159.875,83.125],[157.875,87.125],[126.285,70.297],[124.186,131.025]],"c":true}]},{"i":{"x":0.67,"y":1},"o":{"x":0.167,"y":0.167},"t":32,"s":[{"i":[[0,0],[-18.033,2.437],[0,0],[8.313,39.188],[0,0],[11.75,-13.313],[-3.735,-31.621]],"o":[[0,0],[21.317,-2.875],[0,0],[0,0],[-2.75,-14.625],[-8.25,9.375],[3.624,30.15]],"v":[[130.617,208.218],[162.717,209.343],[188.743,201.218],[159.875,83.125],[157.875,87.125],[125.905,70.466],[124.544,135.497]],"c":true}]},{"t":33,"s":[{"i":[[0,0],[-18.188,2.438],[0,0],[8.313,39.188],[0,0],[11.75,-13.313],[-4.125,-33.688]],"o":[[0,0],[21.5,-2.875],[0,0],[0,0],[-2.75,-14.625],[-8.25,9.375],[4,32.313]],"v":[[131.75,211.063],[164.125,212.188],[190.375,204.063],[159.875,83.125],[157.875,87.125],[125.688,70.563],[124.75,138.063]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.737254917622,0.572549045086,0.349019616842,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.33,"y":0},"t":0,"s":[{"i":[[0,0],[1.063,18.688],[0,0],[0,0],[0,0],[1.313,-1.438],[0,0],[0,0],[0.563,-1.938],[0,0],[0,0],[0,-1.75],[0,0],[0,0],[-3.5,-18.188],[0,0],[-19.688,0.063]],"o":[[0,0],[-1.063,-18.688],[0,0],[0,0],[-0.563,-1.813],[0,0],[0,0],[-1.25,-1.563],[0,0],[0,0],[-1.625,-0.625],[0,0],[0,0],[0,0],[3.5,18.188],[0,0],[34.313,-0.063]],"v":[[190.375,204.063],[156.688,64.313],[159.813,34.75],[150.438,44.563],[146.563,32.125],[142.313,31.125],[134.25,39.938],[123.25,26],[118.875,26.813],[113.625,44.063],[96.188,37.125],[92.75,39.438],[92.75,59.125],[73.25,51.125],[98.625,84.625],[114.063,205.063],[148,212.75]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":1,"s":[{"i":[[0,0],[1.063,18.688],[0,0],[0,0],[0,0],[1.313,-1.438],[0,0],[0,0],[0.563,-1.938],[0,0],[0,0],[0,-1.75],[0,0],[0,0],[-3.5,-18.188],[0,0],[-19.52,0.06]],"o":[[0,0],[-1.063,-18.688],[0,0],[0,0],[-0.563,-1.813],[0,0],[0,0],[-1.25,-1.563],[0,0],[0,0],[-1.625,-0.625],[0,0],[0,0],[0,0],[3.5,18.188],[0,0],[34.02,-0.063]],"v":[[188.743,201.218],[156.688,64.313],[159.813,34.75],[150.438,44.563],[146.563,32.125],[142.313,31.125],[134.25,39.938],[123.25,26],[118.875,26.813],[113.625,44.063],[96.188,37.125],[92.75,39.438],[92.75,59.125],[73.25,51.125],[98.625,84.625],[113.081,202.218],[146.729,209.906]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":2,"s":[{"i":[[0,0],[1.063,18.688],[0,0],[0,0],[0,0],[1.313,-1.438],[0,0],[0,0],[0.563,-1.938],[0,0],[0,0],[0,-1.75],[0,0],[0,0],[-3.5,-18.188],[0,0],[-19.228,0.056]],"o":[[0,0],[-1.063,-18.688],[0,0],[0,0],[-0.563,-1.813],[0,0],[0,0],[-1.25,-1.563],[0,0],[0,0],[-1.625,-0.625],[0,0],[0,0],[0,0],[3.5,18.188],[0,0],[33.511,-0.063]],"v":[[185.899,188.872],[156.688,64.313],[159.813,34.75],[150.438,44.563],[146.563,32.125],[142.313,31.125],[134.25,39.938],[123.25,26],[118.875,26.813],[113.625,44.063],[96.188,37.125],[92.75,39.438],[92.75,59.125],[73.25,51.125],[98.625,84.625],[111.369,189.872],[144.514,197.56]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":3,"s":[{"i":[[0,0],[1.063,18.688],[0,0],[0,0],[0,0],[1.313,-1.438],[0,0],[0,0],[0.563,-1.938],[0,0],[0,0],[0,-1.75],[0,0],[0,0],[-3.5,-18.188],[0,0],[-18.881,0.051]],"o":[[0,0],[-1.063,-18.688],[0,0],[0,0],[-0.563,-1.813],[0,0],[0,0],[-1.25,-1.563],[0,0],[0,0],[-1.625,-0.625],[0,0],[0,0],[0,0],[3.5,18.188],[0,0],[32.907,-0.063]],"v":[[182.49,161.126],[156.688,64.313],[159.813,34.75],[150.438,44.563],[146.563,32.125],[142.313,31.125],[134.25,39.938],[123.25,26],[118.875,26.813],[113.625,44.063],[96.188,37.125],[92.75,39.438],[92.75,59.125],[73.25,51.125],[98.625,84.625],[109.303,162.126],[141.851,169.813]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":4,"s":[{"i":[[0,0],[1.063,18.688],[0,0],[0,0],[0,0],[1.313,-1.438],[0,0],[0,0],[0.563,-1.938],[0,0],[0,0],[0,-1.75],[0,0],[0,0],[-3.5,-18.188],[0,0],[-18.63,0.047]],"o":[[0,0],[-1.063,-18.688],[0,0],[0,0],[-0.563,-1.813],[0,0],[0,0],[-1.25,-1.563],[0,0],[0,0],[-1.625,-0.625],[0,0],[0,0],[0,0],[3.5,18.188],[0,0],[32.469,-0.063]],"v":[[180.055,142.839],[156.688,64.313],[159.813,34.75],[150.438,44.563],[146.563,32.125],[142.313,31.125],[134.25,39.938],[123.25,26],[118.875,26.813],[113.625,44.063],[96.188,37.125],[92.75,39.438],[92.75,59.125],[73.25,51.125],[98.625,84.625],[107.842,143.839],[139.956,151.527]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":5,"s":[{"i":[[0,0],[1.063,18.688],[0,0],[0,0],[0,0],[1.313,-1.438],[0,0],[0,0],[0.563,-1.938],[0,0],[0,0],[0,-1.75],[0,0],[0,0],[-3.5,-18.188],[0,0],[-18.379,0.043]],"o":[[0,0],[-1.063,-18.688],[0,0],[0,0],[-0.563,-1.813],[0,0],[0,0],[-1.25,-1.563],[0,0],[0,0],[-1.625,-0.625],[0,0],[0,0],[0,0],[3.5,18.188],[0,0],[32.031,-0.063]],"v":[[177.64,130.737],[156.688,64.313],[159.813,34.75],[150.438,44.563],[146.563,32.125],[142.313,31.125],[134.25,39.938],[123.25,26],[118.875,26.813],[113.625,44.063],[96.188,37.125],[92.75,39.438],[92.75,59.125],[73.25,51.125],[98.625,84.625],[106.402,131.737],[138.083,139.424]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":6,"s":[{"i":[[0,0],[1.063,18.688],[0,0],[0,0],[0,0],[1.313,-1.438],[0,0],[0,0],[0.563,-1.938],[0,0],[0,0],[0,-1.75],[0,0],[0,0],[-3.5,-18.188],[0,0],[-18.127,0.04]],"o":[[0,0],[-1.063,-18.688],[0,0],[0,0],[-0.563,-1.813],[0,0],[0,0],[-1.25,-1.563],[0,0],[0,0],[-1.625,-0.625],[0,0],[0,0],[0,0],[3.5,18.188],[0,0],[31.593,-0.063]],"v":[[175.281,123.766],[156.688,64.313],[159.813,34.75],[150.438,44.563],[146.563,32.125],[142.313,31.125],[134.25,39.938],[123.25,26],[118.875,26.813],[113.625,44.063],[96.188,37.125],[92.75,39.438],[92.75,59.125],[73.25,51.125],[98.625,84.625],[105.017,124.766],[136.265,132.453]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":7,"s":[{"i":[[0,0],[1.063,18.688],[0,0],[0,0],[0,0],[1.313,-1.438],[0,0],[0,0],[0.563,-1.938],[0,0],[0,0],[0,-1.75],[0,0],[0,0],[-3.5,-18.188],[0,0],[-18.032,0.038]],"o":[[0,0],[-1.063,-18.688],[0,0],[0,0],[-0.563,-1.813],[0,0],[0,0],[-1.25,-1.563],[0,0],[0,0],[-1.625,-0.625],[0,0],[0,0],[0,0],[3.5,18.188],[0,0],[31.427,-0.063]],"v":[[174.433,118.654],[156.688,64.313],[159.813,34.75],[150.438,44.563],[146.563,32.125],[142.313,31.125],[134.25,39.938],[123.25,26],[118.875,26.813],[113.625,44.063],[96.188,37.125],[92.75,39.438],[92.75,59.125],[73.25,51.125],[98.625,84.625],[104.538,119.654],[135.622,127.342]],"c":true}]},{"i":{"x":0,"y":1},"o":{"x":0.167,"y":0.167},"t":8,"s":[{"i":[[0,0],[1.063,18.688],[0,0],[0,0],[0,0],[1.313,-1.438],[0,0],[0,0],[0.563,-1.938],[0,0],[0,0],[0,-1.75],[0,0],[0,0],[-3.5,-18.188],[0,0],[-17.937,0.037]],"o":[[0,0],[-1.063,-18.688],[0,0],[0,0],[-0.563,-1.813],[0,0],[0,0],[-1.25,-1.563],[0,0],[0,0],[-1.625,-0.625],[0,0],[0,0],[0,0],[3.5,18.188],[0,0],[31.261,-0.063]],"v":[[173.586,114.859],[156.688,64.313],[159.813,34.75],[150.438,44.563],[146.563,32.125],[142.313,31.125],[134.25,39.938],[123.25,26],[118.875,26.813],[113.625,44.063],[96.188,37.125],[92.75,39.438],[92.75,59.125],[73.25,51.125],[98.625,84.625],[104.059,115.859],[134.978,123.546]],"c":true}]},{"i":{"x":0,"y":1},"o":{"x":0.33,"y":0},"t":13,"s":[{"i":[[0,0],[1.063,18.688],[0,0],[0,0],[0,0],[1.313,-1.438],[0,0],[0,0],[0.563,-1.938],[0,0],[0,0],[0,-1.75],[0,0],[0,0],[-3.5,-18.188],[0,0],[-17.865,0.036]],"o":[[0,0],[-1.063,-18.688],[0,0],[0,0],[-0.563,-1.813],[0,0],[0,0],[-1.25,-1.563],[0,0],[0,0],[-1.625,-0.625],[0,0],[0,0],[0,0],[3.5,18.188],[0,0],[31.135,-0.063]],"v":[[172.759,109.589],[156.688,64.313],[159.813,34.75],[150.438,44.563],[146.563,32.125],[142.313,31.125],[134.25,39.938],[123.25,26],[118.875,26.813],[113.625,44.063],[96.188,37.125],[92.75,39.438],[92.75,59.125],[73.25,51.125],[98.625,84.625],[103.513,110.589],[134.308,118.276]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":1,"y":0},"t":20,"s":[{"i":[[0,0],[1.063,18.688],[0,0],[0,0],[0,0],[1.313,-1.438],[0,0],[0,0],[0.563,-1.938],[0,0],[0,0],[0,-1.75],[0,0],[0,0],[-3.5,-18.188],[0,0],[-17.865,0.036]],"o":[[0,0],[-1.063,-18.688],[0,0],[0,0],[-0.563,-1.813],[0,0],[0,0],[-1.25,-1.563],[0,0],[0,0],[-1.625,-0.625],[0,0],[0,0],[0,0],[3.5,18.188],[0,0],[31.135,-0.063]],"v":[[172.759,109.589],[156.688,64.313],[159.813,34.75],[150.438,44.563],[146.563,32.125],[142.313,31.125],[134.25,39.938],[123.25,26],[118.875,26.813],[113.625,44.063],[96.188,37.125],[92.75,39.438],[92.75,59.125],[73.25,51.125],[98.625,84.625],[103.513,110.589],[134.308,118.276]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":25,"s":[{"i":[[0,0],[1.063,18.688],[0,0],[0,0],[0,0],[1.313,-1.438],[0,0],[0,0],[0.563,-1.938],[0,0],[0,0],[0,-1.75],[0,0],[0,0],[-3.5,-18.188],[0,0],[-17.937,0.037]],"o":[[0,0],[-1.063,-18.688],[0,0],[0,0],[-0.563,-1.813],[0,0],[0,0],[-1.25,-1.563],[0,0],[0,0],[-1.625,-0.625],[0,0],[0,0],[0,0],[3.5,18.188],[0,0],[31.261,-0.063]],"v":[[173.586,114.859],[156.688,64.313],[159.813,34.75],[150.438,44.563],[146.563,32.125],[142.313,31.125],[134.25,39.938],[123.25,26],[118.875,26.813],[113.625,44.063],[96.188,37.125],[92.75,39.438],[92.75,59.125],[73.25,51.125],[98.625,84.625],[104.059,115.859],[134.978,123.546]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":26,"s":[{"i":[[0,0],[1.063,18.688],[0,0],[0,0],[0,0],[1.313,-1.438],[0,0],[0,0],[0.563,-1.938],[0,0],[0,0],[0,-1.75],[0,0],[0,0],[-3.5,-18.188],[0,0],[-18.032,0.038]],"o":[[0,0],[-1.063,-18.688],[0,0],[0,0],[-0.563,-1.813],[0,0],[0,0],[-1.25,-1.563],[0,0],[0,0],[-1.625,-0.625],[0,0],[0,0],[0,0],[3.5,18.188],[0,0],[31.427,-0.063]],"v":[[174.433,118.654],[156.688,64.313],[159.813,34.75],[150.438,44.563],[146.563,32.125],[142.313,31.125],[134.25,39.938],[123.25,26],[118.875,26.813],[113.625,44.063],[96.188,37.125],[92.75,39.438],[92.75,59.125],[73.25,51.125],[98.625,84.625],[104.538,119.654],[135.622,127.342]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":27,"s":[{"i":[[0,0],[1.063,18.688],[0,0],[0,0],[0,0],[1.313,-1.438],[0,0],[0,0],[0.563,-1.938],[0,0],[0,0],[0,-1.75],[0,0],[0,0],[-3.5,-18.188],[0,0],[-18.127,0.04]],"o":[[0,0],[-1.063,-18.688],[0,0],[0,0],[-0.563,-1.813],[0,0],[0,0],[-1.25,-1.563],[0,0],[0,0],[-1.625,-0.625],[0,0],[0,0],[0,0],[3.5,18.188],[0,0],[31.593,-0.063]],"v":[[175.281,123.766],[156.688,64.313],[159.813,34.75],[150.438,44.563],[146.563,32.125],[142.313,31.125],[134.25,39.938],[123.25,26],[118.875,26.813],[113.625,44.063],[96.188,37.125],[92.75,39.438],[92.75,59.125],[73.25,51.125],[98.625,84.625],[105.017,124.766],[136.265,132.453]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":28,"s":[{"i":[[0,0],[1.063,18.688],[0,0],[0,0],[0,0],[1.313,-1.438],[0,0],[0,0],[0.563,-1.938],[0,0],[0,0],[0,-1.75],[0,0],[0,0],[-3.5,-18.188],[0,0],[-18.379,0.043]],"o":[[0,0],[-1.063,-18.688],[0,0],[0,0],[-0.563,-1.813],[0,0],[0,0],[-1.25,-1.563],[0,0],[0,0],[-1.625,-0.625],[0,0],[0,0],[0,0],[3.5,18.188],[0,0],[32.031,-0.063]],"v":[[177.64,130.737],[156.688,64.313],[159.813,34.75],[150.438,44.563],[146.563,32.125],[142.313,31.125],[134.25,39.938],[123.25,26],[118.875,26.813],[113.625,44.063],[96.188,37.125],[92.75,39.438],[92.75,59.125],[73.25,51.125],[98.625,84.625],[106.402,131.737],[138.083,139.424]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":29,"s":[{"i":[[0,0],[1.063,18.688],[0,0],[0,0],[0,0],[1.313,-1.438],[0,0],[0,0],[0.563,-1.938],[0,0],[0,0],[0,-1.75],[0,0],[0,0],[-3.5,-18.188],[0,0],[-18.63,0.047]],"o":[[0,0],[-1.063,-18.688],[0,0],[0,0],[-0.563,-1.813],[0,0],[0,0],[-1.25,-1.563],[0,0],[0,0],[-1.625,-0.625],[0,0],[0,0],[0,0],[3.5,18.188],[0,0],[32.469,-0.063]],"v":[[180.055,142.839],[156.688,64.313],[159.813,34.75],[150.438,44.563],[146.563,32.125],[142.313,31.125],[134.25,39.938],[123.25,26],[118.875,26.813],[113.625,44.063],[96.188,37.125],[92.75,39.438],[92.75,59.125],[73.25,51.125],[98.625,84.625],[107.842,143.839],[139.956,151.527]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":30,"s":[{"i":[[0,0],[1.063,18.688],[0,0],[0,0],[0,0],[1.313,-1.438],[0,0],[0,0],[0.563,-1.938],[0,0],[0,0],[0,-1.75],[0,0],[0,0],[-3.5,-18.188],[0,0],[-18.881,0.051]],"o":[[0,0],[-1.063,-18.688],[0,0],[0,0],[-0.563,-1.813],[0,0],[0,0],[-1.25,-1.563],[0,0],[0,0],[-1.625,-0.625],[0,0],[0,0],[0,0],[3.5,18.188],[0,0],[32.907,-0.063]],"v":[[182.49,161.126],[156.688,64.313],[159.813,34.75],[150.438,44.563],[146.563,32.125],[142.313,31.125],[134.25,39.938],[123.25,26],[118.875,26.813],[113.625,44.063],[96.188,37.125],[92.75,39.438],[92.75,59.125],[73.25,51.125],[98.625,84.625],[109.303,162.126],[141.851,169.813]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":31,"s":[{"i":[[0,0],[1.063,18.688],[0,0],[0,0],[0,0],[1.313,-1.438],[0,0],[0,0],[0.563,-1.938],[0,0],[0,0],[0,-1.75],[0,0],[0,0],[-3.5,-18.188],[0,0],[-19.228,0.056]],"o":[[0,0],[-1.063,-18.688],[0,0],[0,0],[-0.563,-1.813],[0,0],[0,0],[-1.25,-1.563],[0,0],[0,0],[-1.625,-0.625],[0,0],[0,0],[0,0],[3.5,18.188],[0,0],[33.511,-0.063]],"v":[[185.899,188.872],[156.688,64.313],[159.813,34.75],[150.438,44.563],[146.563,32.125],[142.313,31.125],[134.25,39.938],[123.25,26],[118.875,26.813],[113.625,44.063],[96.188,37.125],[92.75,39.438],[92.75,59.125],[73.25,51.125],[98.625,84.625],[111.369,189.872],[144.514,197.56]],"c":true}]},{"i":{"x":0.67,"y":1},"o":{"x":0.167,"y":0.167},"t":32,"s":[{"i":[[0,0],[1.063,18.688],[0,0],[0,0],[0,0],[1.313,-1.438],[0,0],[0,0],[0.563,-1.938],[0,0],[0,0],[0,-1.75],[0,0],[0,0],[-3.5,-18.188],[0,0],[-19.52,0.06]],"o":[[0,0],[-1.063,-18.688],[0,0],[0,0],[-0.563,-1.813],[0,0],[0,0],[-1.25,-1.563],[0,0],[0,0],[-1.625,-0.625],[0,0],[0,0],[0,0],[3.5,18.188],[0,0],[34.02,-0.063]],"v":[[188.743,201.218],[156.688,64.313],[159.813,34.75],[150.438,44.563],[146.563,32.125],[142.313,31.125],[134.25,39.938],[123.25,26],[118.875,26.813],[113.625,44.063],[96.188,37.125],[92.75,39.438],[92.75,59.125],[73.25,51.125],[98.625,84.625],[113.081,202.218],[146.729,209.906]],"c":true}]},{"t":33,"s":[{"i":[[0,0],[1.063,18.688],[0,0],[0,0],[0,0],[1.313,-1.438],[0,0],[0,0],[0.563,-1.938],[0,0],[0,0],[0,-1.75],[0,0],[0,0],[-3.5,-18.188],[0,0],[-19.688,0.063]],"o":[[0,0],[-1.063,-18.688],[0,0],[0,0],[-0.563,-1.813],[0,0],[0,0],[-1.25,-1.563],[0,0],[0,0],[-1.625,-0.625],[0,0],[0,0],[0,0],[3.5,18.188],[0,0],[34.313,-0.063]],"v":[[190.375,204.063],[156.688,64.313],[159.813,34.75],[150.438,44.563],[146.563,32.125],[142.313,31.125],[134.25,39.938],[123.25,26],[118.875,26.813],[113.625,44.063],[96.188,37.125],[92.75,39.438],[92.75,59.125],[73.25,51.125],[98.625,84.625],[114.063,205.063],[148,212.75]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.61960786581,0.46274510026,0.239215686917,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.33,"y":0},"t":0,"s":[{"i":[[0,0],[-12.183,-34.643]],"o":[[0,0],[0,0]],"v":[[41.387,-96.927],[74.702,-38.38]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":1,"s":[{"i":[[0,0],[-12.769,-34.372]],"o":[[0,0],[0,0]],"v":[[39.884,-95.793],[74.673,-38.358]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":2,"s":[{"i":[[0,0],[-13.791,-33.898]],"o":[[0,0],[0,0]],"v":[[37.264,-93.817],[74.621,-38.32]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":3,"s":[{"i":[[0,0],[-15.003,-33.336]],"o":[[0,0],[0,0]],"v":[[34.158,-91.473],[74.559,-38.274]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":4,"s":[{"i":[[0,0],[-15.882,-32.929]],"o":[[0,0],[0,0]],"v":[[31.904,-89.773],[74.515,-38.241]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":5,"s":[{"i":[[0,0],[-16.761,-32.521]],"o":[[0,0],[0,0]],"v":[[29.651,-88.073],[74.47,-38.208]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":6,"s":[{"i":[[0,0],[-17.641,-32.114]],"o":[[0,0],[0,0]],"v":[[27.397,-86.373],[74.426,-38.175]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":7,"s":[{"i":[[0,0],[-17.973,-31.96]],"o":[[0,0],[0,0]],"v":[[26.545,-85.729],[74.409,-38.162]],"c":false}]},{"i":{"x":0,"y":1},"o":{"x":0.167,"y":0.167},"t":8,"s":[{"i":[[0,0],[-18.306,-31.806]],"o":[[0,0],[0,0]],"v":[[25.692,-85.086],[74.392,-38.15]],"c":false}]},{"i":{"x":0,"y":1},"o":{"x":0.33,"y":0},"t":13,"s":[{"i":[[0,0],[-18.559,-31.688]],"o":[[0,0],[0,0]],"v":[[25.044,-84.597],[74.379,-38.14]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":1,"y":0},"t":20,"s":[{"i":[[0,0],[-18.559,-31.688]],"o":[[0,0],[0,0]],"v":[[25.044,-84.597],[74.379,-38.14]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":25,"s":[{"i":[[0,0],[-18.306,-31.806]],"o":[[0,0],[0,0]],"v":[[25.692,-85.086],[74.392,-38.15]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":26,"s":[{"i":[[0,0],[-17.973,-31.96]],"o":[[0,0],[0,0]],"v":[[26.545,-85.729],[74.409,-38.162]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":27,"s":[{"i":[[0,0],[-17.641,-32.114]],"o":[[0,0],[0,0]],"v":[[27.397,-86.373],[74.426,-38.175]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":28,"s":[{"i":[[0,0],[-16.761,-32.521]],"o":[[0,0],[0,0]],"v":[[29.651,-88.073],[74.47,-38.208]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":29,"s":[{"i":[[0,0],[-15.882,-32.929]],"o":[[0,0],[0,0]],"v":[[31.904,-89.773],[74.515,-38.241]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":30,"s":[{"i":[[0,0],[-15.003,-33.336]],"o":[[0,0],[0,0]],"v":[[34.158,-91.473],[74.559,-38.274]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":31,"s":[{"i":[[0,0],[-13.791,-33.898]],"o":[[0,0],[0,0]],"v":[[37.264,-93.817],[74.621,-38.32]],"c":false}]},{"i":{"x":0.67,"y":1},"o":{"x":0.167,"y":0.167},"t":32,"s":[{"i":[[0,0],[-12.769,-34.372]],"o":[[0,0],[0,0]],"v":[[39.884,-95.793],[74.673,-38.358]],"c":false}]},{"t":33,"s":[{"i":[[0,0],[-12.183,-34.643]],"o":[[0,0],[0,0]],"v":[[41.387,-96.927],[74.702,-38.38]],"c":false}]}]},"hd":false},{"ty":"st","c":{"a":0,"k":[0.580392181873,0.376470595598,0.215686276555,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":2,"lj":2,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.33,"y":0},"t":0,"s":[{"i":[[0,0],[-3.811,-17.322]],"o":[[0,0],[0,0]],"v":[[133.538,-74.755],[147.742,-34.569]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":1,"s":[{"i":[[0,0],[-3.083,-17.075]],"o":[[0,0],[0,0]],"v":[[134.976,-74.378],[147.742,-34.569]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":2,"s":[{"i":[[0,0],[-1.815,-16.645]],"o":[[0,0],[0,0]],"v":[[137.482,-73.719],[147.742,-34.569]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":3,"s":[{"i":[[0,0],[-0.311,-16.136]],"o":[[0,0],[0,0]],"v":[[140.453,-72.938],[147.742,-34.569]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":4,"s":[{"i":[[0,0],[0.779,-15.766]],"o":[[0,0],[0,0]],"v":[[142.609,-72.371],[147.742,-34.569]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":5,"s":[{"i":[[0,0],[1.87,-15.397]],"o":[[0,0],[0,0]],"v":[[144.764,-71.805],[147.742,-34.569]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":6,"s":[{"i":[[0,0],[2.961,-15.027]],"o":[[0,0],[0,0]],"v":[[146.92,-71.238],[147.742,-34.569]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":7,"s":[{"i":[[0,0],[3.374,-14.887]],"o":[[0,0],[0,0]],"v":[[147.736,-71.024],[147.742,-34.569]],"c":false}]},{"i":{"x":0,"y":1},"o":{"x":0.167,"y":0.167},"t":8,"s":[{"i":[[0,0],[3.787,-14.747]],"o":[[0,0],[0,0]],"v":[[148.551,-70.809],[147.742,-34.569]],"c":false}]},{"i":{"x":0,"y":1},"o":{"x":0.33,"y":0},"t":13,"s":[{"i":[[0,0],[4.1,-14.641]],"o":[[0,0],[0,0]],"v":[[149.171,-70.646],[147.742,-34.569]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":1,"y":0},"t":20,"s":[{"i":[[0,0],[4.1,-14.641]],"o":[[0,0],[0,0]],"v":[[149.171,-70.646],[147.742,-34.569]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":25,"s":[{"i":[[0,0],[3.787,-14.747]],"o":[[0,0],[0,0]],"v":[[148.551,-70.809],[147.742,-34.569]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":26,"s":[{"i":[[0,0],[3.374,-14.887]],"o":[[0,0],[0,0]],"v":[[147.736,-71.024],[147.742,-34.569]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":27,"s":[{"i":[[0,0],[2.961,-15.027]],"o":[[0,0],[0,0]],"v":[[146.92,-71.238],[147.742,-34.569]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":28,"s":[{"i":[[0,0],[1.87,-15.397]],"o":[[0,0],[0,0]],"v":[[144.764,-71.805],[147.742,-34.569]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":29,"s":[{"i":[[0,0],[0.779,-15.766]],"o":[[0,0],[0,0]],"v":[[142.609,-72.371],[147.742,-34.569]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":30,"s":[{"i":[[0,0],[-0.311,-16.136]],"o":[[0,0],[0,0]],"v":[[140.453,-72.938],[147.742,-34.569]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":31,"s":[{"i":[[0,0],[-1.815,-16.645]],"o":[[0,0],[0,0]],"v":[[137.482,-73.719],[147.742,-34.569]],"c":false}]},{"i":{"x":0.67,"y":1},"o":{"x":0.167,"y":0.167},"t":32,"s":[{"i":[[0,0],[-3.083,-17.075]],"o":[[0,0],[0,0]],"v":[[134.976,-74.378],[147.742,-34.569]],"c":false}]},{"t":33,"s":[{"i":[[0,0],[-3.811,-17.322]],"o":[[0,0],[0,0]],"v":[[133.538,-74.755],[147.742,-34.569]],"c":false}]}]},"hd":false},{"ty":"st","c":{"a":0,"k":[0.580392181873,0.376470595598,0.215686276555,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":2,"lj":2,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.33,"y":0},"t":0,"s":[{"i":[[0,0],[-9.007,-30.14]],"o":[[0,0],[0,0]],"v":[[76.723,-110.438],[108.941,-50.159]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":1,"s":[{"i":[[0,0],[-9.581,-27.559]],"o":[[0,0],[0,0]],"v":[[83.177,-99.786],[109.329,-50.153]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":2,"s":[{"i":[[0,0],[-10.58,-23.06]],"o":[[0,0],[0,0]],"v":[[89.428,-88.452],[110.004,-50.144]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":3,"s":[{"i":[[0,0],[-10.46,-25.194]],"o":[[0,0],[0,0]],"v":[[107.766,-59.2],[110.804,-50.132]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":4,"s":[{"i":[[0,0],[-7.638,-19.523]],"o":[[0,0],[0,0]],"v":[[109.132,-57.128],[111.385,-50.124]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":5,"s":[{"i":[[0,0],[-4.817,-13.853]],"o":[[0,0],[0,0]],"v":[[110.497,-55.057],[111.965,-50.115]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":6,"s":[{"i":[[0,0],[-1.996,-8.182]],"o":[[0,0],[0,0]],"v":[[111.863,-52.985],[112.546,-50.107]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":7,"s":[{"i":[[0,0],[-0.929,-6.037]],"o":[[0,0],[0,0]],"v":[[112.38,-52.201],[112.766,-50.104]],"c":false}]},{"i":{"x":0,"y":1},"o":{"x":0.167,"y":0.167},"t":8,"s":[{"i":[[0,0],[0.139,-3.892]],"o":[[0,0],[0,0]],"v":[[112.896,-51.418],[112.986,-50.101]],"c":false}]},{"i":{"x":0,"y":1},"o":{"x":0.33,"y":0},"t":13,"s":[{"i":[[0,0],[0.95,-2.261]],"o":[[0,0],[0,0]],"v":[[113.289,-50.822],[113.153,-50.098]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":1,"y":0},"t":20,"s":[{"i":[[0,0],[0.95,-2.261]],"o":[[0,0],[0,0]],"v":[[113.289,-50.822],[113.153,-50.098]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":25,"s":[{"i":[[0,0],[0.139,-3.892]],"o":[[0,0],[0,0]],"v":[[112.896,-51.418],[112.986,-50.101]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":26,"s":[{"i":[[0,0],[-0.929,-6.037]],"o":[[0,0],[0,0]],"v":[[112.38,-52.201],[112.766,-50.104]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":27,"s":[{"i":[[0,0],[-1.996,-8.182]],"o":[[0,0],[0,0]],"v":[[111.863,-52.985],[112.546,-50.107]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":28,"s":[{"i":[[0,0],[-4.817,-13.853]],"o":[[0,0],[0,0]],"v":[[110.497,-55.057],[111.965,-50.115]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":29,"s":[{"i":[[0,0],[-7.638,-19.523]],"o":[[0,0],[0,0]],"v":[[109.132,-57.128],[111.385,-50.124]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":30,"s":[{"i":[[0,0],[-10.46,-25.194]],"o":[[0,0],[0,0]],"v":[[107.766,-59.2],[110.804,-50.132]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":31,"s":[{"i":[[0,0],[-10.58,-23.06]],"o":[[0,0],[0,0]],"v":[[87.059,-94.242],[110.004,-50.144]],"c":false}]},{"i":{"x":0.67,"y":1},"o":{"x":0.167,"y":0.167},"t":32,"s":[{"i":[[0,0],[-9.581,-27.559]],"o":[[0,0],[0,0]],"v":[[83.177,-99.786],[109.329,-50.153]],"c":false}]},{"t":33,"s":[{"i":[[0,0],[-9.007,-30.14]],"o":[[0,0],[0,0]],"v":[[76.723,-110.438],[108.941,-50.159]],"c":false}]}]},"hd":false},{"ty":"st","c":{"a":0,"k":[0.580392181873,0.376470595598,0.215686276555,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":2,"lj":2,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.33,"y":0},"t":0,"s":[{"i":[[-10.22,25.636],[3.943,28.75],[20.093,-1.386],[17.668,8.661],[0.693,-7.275],[12.125,-0.373],[-1.732,-12.125],[5.123,-13.539],[-3.788,-20.696],[-23.034,-21.084]],"o":[[0,0],[-3.849,-28.061],[0,0],[-13.289,-6.514],[0,0],[-12.125,0.373],[0,0],[-4.85,12.818],[4.56,24.912],[23.034,21.084]],"v":[[165.237,31.599],[172.377,-15.516],[133.538,-74.755],[98.895,-119.792],[76.723,-110.438],[56.63,-121.551],[41.387,-96.927],[24.412,-87.574],[41.676,-22.528],[71.867,54.464]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":1,"s":[{"i":[[-10.22,25.636],[3.943,28.75],[19.979,-0.606],[19.227,7.816],[0.374,-7.939],[14.95,0.067],[-2.001,-12.052],[4.803,-13.617],[-3.788,-20.696],[-23.034,-21.084]],"o":[[0,0],[-3.849,-28.061],[0,0],[-15.155,-5.865],[0,0],[-12.124,0.281],[0,0],[-4.547,12.891],[4.56,24.912],[23.034,21.084]],"v":[[165.237,31.599],[172.377,-15.516],[134.976,-74.378],[100.543,-119.858],[83.177,-99.786],[54.901,-120.017],[39.884,-95.793],[23.168,-86.082],[41.676,-22.528],[71.867,54.464]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":2,"s":[{"i":[[-10.22,25.636],[3.943,28.75],[19.779,0.754],[25.318,6.343],[-0.183,-9.096],[19.875,0.834],[-2.47,-11.924],[4.244,-13.752],[-3.788,-20.696],[-23.034,-21.084]],"o":[[0,0],[-3.849,-28.061],[0,0],[-21.237,-4.732],[0,0],[-12.122,0.12],[0,0],[-4.018,13.02],[4.56,24.912],[23.034,21.084]],"v":[[165.237,31.599],[172.377,-15.516],[137.482,-73.719],[98.556,-119.974],[89.428,-88.452],[51.888,-117.344],[37.264,-93.817],[21,-83.483],[41.676,-22.528],[71.867,54.464]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":3,"s":[{"i":[[-10.22,25.636],[3.943,28.75],[19.543,2.365],[25.168,4.598],[-0.842,-10.468],[25.713,1.744],[-3.026,-11.773],[3.582,-13.912],[-3.788,-20.696],[-23.034,-21.084]],"o":[[0,0],[-3.849,-28.061],[0,0],[-22.266,-3.389],[0,0],[-12.12,-0.072],[0,0],[-3.391,13.171],[4.56,24.912],[23.034,21.084]],"v":[[165.237,31.599],[172.377,-15.516],[140.453,-72.938],[106.821,-120.112],[107.766,-59.2],[48.316,-114.175],[34.158,-91.473],[18.43,-80.402],[41.676,-22.528],[71.867,54.464]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":4,"s":[{"i":[[-10.22,25.636],[3.943,28.75],[19.372,3.535],[23.426,3.331],[0.83,-11.06],[25.616,1.64],[-3.43,-11.663],[3.102,-14.028],[-3.788,-20.696],[-23.034,-21.084]],"o":[[0,0],[-3.849,-28.061],[0,0],[-21.454,-2.415],[0,0],[-12.119,-0.211],[0,0],[-2.937,13.281],[4.56,24.912],[23.034,21.084]],"v":[[165.237,31.599],[172.377,-15.516],[142.609,-72.371],[113.017,-120.8],[109.132,-57.128],[45.724,-111.875],[31.904,-89.773],[16.565,-78.166],[41.676,-22.528],[71.867,54.464]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":5,"s":[{"i":[[-10.22,25.636],[3.943,28.75],[19.2,4.704],[21.683,2.064],[2.503,-11.652],[25.519,1.536],[-3.833,-11.553],[2.621,-14.145],[-3.788,-20.696],[-23.034,-21.084]],"o":[[0,0],[-3.849,-28.061],[0,0],[-20.643,-1.441],[0,0],[-12.117,-0.349],[0,0],[-2.482,13.392],[4.56,24.912],[23.034,21.084]],"v":[[165.237,31.599],[172.377,-15.516],[144.764,-71.805],[119.213,-121.489],[110.497,-55.057],[43.132,-109.576],[29.651,-88.073],[14.7,-75.93],[41.676,-22.528],[71.867,54.464]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":6,"s":[{"i":[[-10.22,25.636],[3.943,28.75],[19.029,5.874],[23.492,0.797],[4.175,-12.244],[25.421,1.432],[-4.237,-11.443],[2.141,-14.261],[-3.788,-20.696],[-23.034,-21.084]],"o":[[0,0],[-3.849,-28.061],[0,0],[-23.363,-0.467],[0,0],[-12.116,-0.488],[0,0],[-2.027,13.502],[4.56,24.912],[23.034,21.084]],"v":[[165.237,31.599],[172.377,-15.516],[146.92,-71.238],[120.638,-122.177],[111.863,-52.985],[40.54,-107.276],[27.397,-86.373],[12.835,-73.694],[41.676,-22.528],[71.867,54.464]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":7,"s":[{"i":[[-10.22,25.636],[3.943,28.75],[18.964,6.316],[24.491,0.318],[4.808,-12.468],[25.385,1.393],[-4.389,-11.401],[1.959,-14.305],[-3.788,-20.696],[-23.034,-21.084]],"o":[[0,0],[-3.849,-28.061],[0,0],[-24.799,-0.098],[0,0],[-12.115,-0.541],[0,0],[-1.855,13.543],[4.56,24.912],[23.034,21.084]],"v":[[165.237,31.599],[172.377,-15.516],[147.736,-71.024],[120.628,-122.437],[112.38,-52.201],[39.559,-106.406],[26.545,-85.729],[12.129,-72.848],[41.676,-22.528],[71.867,54.464]],"c":true}]},{"i":{"x":0,"y":1},"o":{"x":0.167,"y":0.167},"t":8,"s":[{"i":[[-10.22,25.636],[3.943,28.75],[18.899,6.759],[24.122,-0.161],[5.44,-12.692],[25.348,1.353],[-4.542,-11.36],[1.778,-14.349],[-3.788,-20.696],[-23.034,-21.084]],"o":[[0,0],[-3.849,-28.061],[0,0],[-24.893,0.27],[0,0],[-12.115,-0.593],[0,0],[-1.683,13.585],[4.56,24.912],[23.034,21.084]],"v":[[165.237,31.599],[172.377,-15.516],[148.551,-70.809],[122.431,-122.698],[112.896,-51.418],[38.578,-105.537],[25.692,-85.086],[11.424,-72.003],[41.676,-22.528],[71.867,54.464]],"c":true}]},{"i":{"x":0,"y":1},"o":{"x":0.33,"y":0},"t":13,"s":[{"i":[[-10.22,25.636],[3.943,28.75],[18.85,7.095],[23.358,-0.525],[5.921,-12.862],[25.32,1.323],[-4.658,-11.328],[1.639,-14.383],[-3.788,-20.696],[-23.034,-21.084]],"o":[[0,0],[-3.849,-28.061],[0,0],[-24.47,0.55],[0,0],[-12.114,-0.633],[0,0],[-1.552,13.617],[4.56,24.912],[23.034,21.084]],"v":[[165.237,31.599],[172.377,-15.516],[149.171,-70.646],[124.471,-122.896],[113.289,-50.822],[37.833,-104.875],[25.044,-84.597],[10.887,-71.36],[41.676,-22.528],[71.867,54.464]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":1,"y":0},"t":20,"s":[{"i":[[-10.22,25.636],[3.943,28.75],[18.85,7.095],[23.358,-0.525],[5.921,-12.862],[25.32,1.323],[-4.658,-11.328],[1.639,-14.383],[-3.788,-20.696],[-23.034,-21.084]],"o":[[0,0],[-3.849,-28.061],[0,0],[-24.47,0.55],[0,0],[-12.114,-0.633],[0,0],[-1.552,13.617],[4.56,24.912],[23.034,21.084]],"v":[[165.237,31.599],[172.377,-15.516],[149.171,-70.646],[124.471,-122.896],[113.289,-50.822],[37.833,-104.875],[25.044,-84.597],[10.887,-71.36],[41.676,-22.528],[71.867,54.464]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":25,"s":[{"i":[[-10.22,25.636],[3.943,28.75],[18.899,6.759],[24.122,-0.161],[5.44,-12.692],[25.348,1.353],[-4.542,-11.36],[1.778,-14.349],[-3.788,-20.696],[-23.034,-21.084]],"o":[[0,0],[-3.849,-28.061],[0,0],[-24.893,0.27],[0,0],[-12.115,-0.593],[0,0],[-1.683,13.585],[4.56,24.912],[23.034,21.084]],"v":[[165.237,31.599],[172.377,-15.516],[148.551,-70.809],[122.431,-122.698],[112.896,-51.418],[38.578,-105.537],[25.692,-85.086],[11.424,-72.003],[41.676,-22.528],[71.867,54.464]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":26,"s":[{"i":[[-10.22,25.636],[3.943,28.75],[18.964,6.316],[24.491,0.318],[4.808,-12.468],[25.385,1.393],[-4.389,-11.401],[1.959,-14.305],[-3.788,-20.696],[-23.034,-21.084]],"o":[[0,0],[-3.849,-28.061],[0,0],[-24.799,-0.098],[0,0],[-12.115,-0.541],[0,0],[-1.855,13.543],[4.56,24.912],[23.034,21.084]],"v":[[165.237,31.599],[172.377,-15.516],[147.736,-71.024],[120.628,-122.437],[112.38,-52.201],[39.559,-106.406],[26.545,-85.729],[12.129,-72.848],[41.676,-22.528],[71.867,54.464]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":27,"s":[{"i":[[-10.22,25.636],[3.943,28.75],[19.029,5.874],[23.492,0.797],[4.175,-12.244],[25.421,1.432],[-4.237,-11.443],[2.141,-14.261],[-3.788,-20.696],[-23.034,-21.084]],"o":[[0,0],[-3.849,-28.061],[0,0],[-23.363,-0.467],[0,0],[-12.116,-0.488],[0,0],[-2.027,13.502],[4.56,24.912],[23.034,21.084]],"v":[[165.237,31.599],[172.377,-15.516],[146.92,-71.238],[120.638,-122.177],[111.863,-52.985],[40.54,-107.276],[27.397,-86.373],[12.835,-73.694],[41.676,-22.528],[71.867,54.464]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":28,"s":[{"i":[[-10.22,25.636],[3.943,28.75],[19.2,4.704],[21.683,2.064],[2.503,-11.652],[25.519,1.536],[-3.833,-11.553],[2.621,-14.145],[-3.788,-20.696],[-23.034,-21.084]],"o":[[0,0],[-3.849,-28.061],[0,0],[-20.643,-1.441],[0,0],[-12.117,-0.349],[0,0],[-2.482,13.392],[4.56,24.912],[23.034,21.084]],"v":[[165.237,31.599],[172.377,-15.516],[144.764,-71.805],[119.213,-121.489],[110.497,-55.057],[43.132,-109.576],[29.651,-88.073],[14.7,-75.93],[41.676,-22.528],[71.867,54.464]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":29,"s":[{"i":[[-10.22,25.636],[3.943,28.75],[19.372,3.535],[23.426,3.331],[0.83,-11.06],[25.616,1.64],[-3.43,-11.663],[3.102,-14.028],[-3.788,-20.696],[-23.034,-21.084]],"o":[[0,0],[-3.849,-28.061],[0,0],[-21.454,-2.415],[0,0],[-12.119,-0.211],[0,0],[-2.937,13.281],[4.56,24.912],[23.034,21.084]],"v":[[165.237,31.599],[172.377,-15.516],[142.609,-72.371],[113.017,-120.8],[109.132,-57.128],[45.724,-111.875],[31.904,-89.773],[16.565,-78.166],[41.676,-22.528],[71.867,54.464]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":30,"s":[{"i":[[-10.22,25.636],[3.943,28.75],[19.543,2.365],[26.99,4.598],[-0.842,-10.468],[25.713,1.744],[-3.026,-11.773],[3.582,-13.912],[-3.788,-20.696],[-23.034,-21.084]],"o":[[0,0],[-3.849,-28.061],[0,0],[-23.878,-3.389],[0,0],[-12.12,-0.072],[0,0],[-3.391,13.171],[4.56,24.912],[23.034,21.084]],"v":[[165.237,31.599],[172.377,-15.516],[140.453,-72.938],[104.486,-120.112],[107.766,-59.2],[48.316,-114.175],[34.158,-91.473],[18.43,-80.402],[41.676,-22.528],[71.867,54.464]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":31,"s":[{"i":[[-10.22,25.636],[3.943,28.75],[19.779,0.754],[21.946,6.343],[-0.183,-9.096],[19.875,0.834],[-2.47,-11.924],[4.244,-13.752],[-3.788,-20.696],[-23.034,-21.084]],"o":[[0,0],[-3.849,-28.061],[0,0],[-18.409,-4.732],[0,0],[-12.122,0.12],[0,0],[-4.018,13.02],[4.56,24.912],[23.034,21.084]],"v":[[165.237,31.599],[172.377,-15.516],[137.482,-73.719],[103.415,-119.974],[87.059,-94.242],[51.888,-117.344],[37.264,-93.817],[21,-83.483],[41.676,-22.528],[71.867,54.464]],"c":true}]},{"i":{"x":0.67,"y":1},"o":{"x":0.167,"y":0.167},"t":32,"s":[{"i":[[-10.22,25.636],[3.943,28.75],[19.979,-0.606],[19.227,7.816],[0.374,-7.939],[14.95,0.067],[-2.001,-12.052],[4.803,-13.617],[-3.788,-20.696],[-23.034,-21.084]],"o":[[0,0],[-3.849,-28.061],[0,0],[-15.155,-5.865],[0,0],[-12.124,0.281],[0,0],[-4.547,12.891],[4.56,24.912],[23.034,21.084]],"v":[[165.237,31.599],[172.377,-15.516],[134.976,-74.378],[100.543,-119.858],[83.177,-99.786],[54.901,-120.017],[39.884,-95.793],[23.168,-86.082],[41.676,-22.528],[71.867,54.464]],"c":true}]},{"t":33,"s":[{"i":[[-10.22,25.636],[3.943,28.75],[20.093,-1.386],[17.668,8.661],[0.693,-7.275],[12.125,-0.373],[-1.732,-12.125],[5.123,-13.539],[-3.788,-20.696],[-23.034,-21.084]],"o":[[0,0],[-3.849,-28.061],[0,0],[-13.289,-6.514],[0,0],[-12.125,0.373],[0,0],[-4.85,12.818],[4.56,24.912],[23.034,21.084]],"v":[[165.237,31.599],[172.377,-15.516],[133.538,-74.755],[98.895,-119.792],[76.723,-110.438],[56.63,-121.551],[41.387,-96.927],[24.412,-87.574],[41.676,-22.528],[71.867,54.464]],"c":true}]}]},"hd":false},{"ty":"st","c":{"a":0,"k":[0.580392181873,0.376470595598,0.215686276555,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":2,"lj":2,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.33,"y":0},"t":0,"s":[{"i":[[25.645,70.013],[-0.889,-4.86],[-23.038,-21.088],[-10.215,25.635],[3.941,28.747],[1.172,4.577],[42.671,-3.275]],"o":[[2.001,5.679],[4.557,24.917],[23.028,21.078],[0,0],[-0.617,-4.506],[0,0],[-33.501,2.571]],"v":[[37.09,-38.535],[41.677,-22.529],[71.869,54.465],[165.232,31.599],[172.376,-15.517],[169.688,-29.218],[125.987,33.186]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":1,"s":[{"i":[[27.154,69.236],[-0.889,-4.86],[-23.038,-21.088],[-10.215,25.635],[3.941,28.747],[1.172,4.577],[42.671,-3.275]],"o":[[2.123,5.617],[4.557,24.917],[23.028,21.078],[0,0],[-0.617,-4.506],[0,0],[-33.501,2.571]],"v":[[36.918,-37.468],[41.677,-22.529],[71.869,54.465],[165.232,31.599],[172.376,-15.517],[169.688,-29.218],[125.987,33.186]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":2,"s":[{"i":[[29.786,67.882],[-0.889,-4.86],[-23.038,-21.088],[-10.215,25.635],[3.941,28.747],[1.172,4.577],[42.671,-3.275]],"o":[[2.337,5.511],[4.557,24.917],[23.028,21.078],[0,0],[-0.617,-4.506],[0,0],[-33.501,2.571]],"v":[[36.619,-35.609],[41.677,-22.529],[71.869,54.465],[165.232,31.599],[172.376,-15.517],[169.688,-29.218],[125.987,33.186]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":3,"s":[{"i":[[32.905,66.276],[-0.889,-4.86],[-23.038,-21.088],[-10.215,25.635],[3.941,28.747],[1.172,4.577],[42.671,-3.275]],"o":[[2.591,5.384],[4.557,24.917],[23.028,21.078],[0,0],[-0.617,-4.506],[0,0],[-33.501,2.571]],"v":[[36.265,-33.405],[41.677,-22.529],[71.869,54.465],[165.232,31.599],[172.376,-15.517],[169.688,-29.218],[125.987,33.186]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":4,"s":[{"i":[[35.169,65.112],[-0.889,-4.86],[-23.038,-21.088],[-10.215,25.635],[3.941,28.747],[1.172,4.577],[42.671,-3.275]],"o":[[2.775,5.292],[4.557,24.917],[23.028,21.077],[0,0],[-0.617,-4.506],[0,0],[-33.501,2.571]],"v":[[36.008,-31.806],[41.677,-22.529],[71.869,54.465],[165.232,31.599],[172.376,-15.517],[169.688,-29.218],[125.987,33.186]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":5,"s":[{"i":[[37.433,63.947],[-0.889,-4.86],[-23.038,-21.088],[-10.215,25.635],[3.941,28.747],[1.172,4.577],[42.671,-3.275]],"o":[[2.958,5.201],[4.557,24.917],[23.028,21.077],[0,0],[-0.617,-4.506],[0,0],[-33.501,2.571]],"v":[[35.751,-30.207],[41.677,-22.529],[71.869,54.465],[165.232,31.599],[172.376,-15.517],[169.688,-29.218],[125.987,33.186]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":6,"s":[{"i":[[39.696,62.782],[-0.889,-4.86],[-23.038,-21.088],[-10.215,25.635],[3.941,28.747],[1.172,4.577],[42.671,-3.275]],"o":[[3.142,5.109],[4.557,24.917],[23.028,21.078],[0,0],[-0.617,-4.506],[0,0],[-33.501,2.571]],"v":[[35.493,-28.608],[41.677,-22.529],[71.869,54.465],[165.232,31.599],[172.376,-15.517],[169.688,-29.218],[125.987,33.186]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":7,"s":[{"i":[[40.553,62.342],[-0.889,-4.86],[-23.038,-21.088],[-10.215,25.635],[3.941,28.747],[1.172,4.577],[42.671,-3.275]],"o":[[3.212,5.074],[4.557,24.917],[23.028,21.078],[0,0],[-0.617,-4.506],[0,0],[-33.501,2.571]],"v":[[35.396,-28.003],[41.677,-22.529],[71.869,54.465],[165.232,31.599],[172.376,-15.517],[169.688,-29.218],[125.987,33.186]],"c":true}]},{"i":{"x":0,"y":1},"o":{"x":0.167,"y":0.167},"t":8,"s":[{"i":[[41.409,61.901],[-0.889,-4.86],[-23.038,-21.088],[-10.215,25.635],[3.941,28.747],[1.172,4.577],[42.671,-3.275]],"o":[[3.282,5.039],[4.557,24.917],[23.028,21.078],[0,0],[-0.617,-4.506],[0,0],[-33.501,2.571]],"v":[[35.299,-27.398],[41.677,-22.529],[71.869,54.465],[165.232,31.599],[172.376,-15.517],[169.688,-29.218],[125.987,33.186]],"c":true}]},{"i":{"x":0,"y":1},"o":{"x":0.33,"y":0},"t":13,"s":[{"i":[[42.06,61.566],[-0.889,-4.86],[-23.038,-21.088],[-10.215,25.635],[3.941,28.747],[1.172,4.577],[42.671,-3.275]],"o":[[3.334,5.013],[4.557,24.917],[23.028,21.078],[0,0],[-0.617,-4.506],[0,0],[-33.501,2.571]],"v":[[35.225,-26.939],[41.677,-22.529],[71.869,54.465],[165.232,31.599],[172.376,-15.517],[169.688,-29.218],[125.987,33.186]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":1,"y":0},"t":20,"s":[{"i":[[42.06,61.566],[-0.889,-4.86],[-23.038,-21.088],[-10.215,25.635],[3.941,28.747],[1.172,4.577],[42.671,-3.275]],"o":[[3.334,5.013],[4.557,24.917],[23.028,21.078],[0,0],[-0.617,-4.506],[0,0],[-33.501,2.571]],"v":[[35.225,-26.939],[41.677,-22.529],[71.869,54.465],[165.232,31.599],[172.376,-15.517],[169.688,-29.218],[125.987,33.186]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":25,"s":[{"i":[[41.409,61.901],[-0.889,-4.86],[-23.038,-21.088],[-10.215,25.635],[3.941,28.747],[1.172,4.577],[42.671,-3.275]],"o":[[3.282,5.039],[4.557,24.917],[23.028,21.078],[0,0],[-0.617,-4.506],[0,0],[-33.501,2.571]],"v":[[35.299,-27.398],[41.677,-22.529],[71.869,54.465],[165.232,31.599],[172.376,-15.517],[169.688,-29.218],[125.987,33.186]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":26,"s":[{"i":[[40.553,62.342],[-0.889,-4.86],[-23.038,-21.088],[-10.215,25.635],[3.941,28.747],[1.172,4.577],[42.671,-3.275]],"o":[[3.212,5.074],[4.557,24.917],[23.028,21.078],[0,0],[-0.617,-4.506],[0,0],[-33.501,2.571]],"v":[[35.396,-28.003],[41.677,-22.529],[71.869,54.465],[165.232,31.599],[172.376,-15.517],[169.688,-29.218],[125.987,33.186]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":27,"s":[{"i":[[39.696,62.782],[-0.889,-4.86],[-23.038,-21.088],[-10.215,25.635],[3.941,28.747],[1.172,4.577],[42.671,-3.275]],"o":[[3.142,5.109],[4.557,24.917],[23.028,21.078],[0,0],[-0.617,-4.506],[0,0],[-33.501,2.571]],"v":[[35.493,-28.608],[41.677,-22.529],[71.869,54.465],[165.232,31.599],[172.376,-15.517],[169.688,-29.218],[125.987,33.186]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":28,"s":[{"i":[[37.433,63.947],[-0.889,-4.86],[-23.038,-21.088],[-10.215,25.635],[3.941,28.747],[1.172,4.577],[42.671,-3.275]],"o":[[2.958,5.201],[4.557,24.917],[23.028,21.077],[0,0],[-0.617,-4.506],[0,0],[-33.501,2.571]],"v":[[35.751,-30.207],[41.677,-22.529],[71.869,54.465],[165.232,31.599],[172.376,-15.517],[169.688,-29.218],[125.987,33.186]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":29,"s":[{"i":[[35.169,65.112],[-0.889,-4.86],[-23.038,-21.088],[-10.215,25.635],[3.941,28.747],[1.172,4.577],[42.671,-3.275]],"o":[[2.775,5.292],[4.557,24.917],[23.028,21.077],[0,0],[-0.617,-4.506],[0,0],[-33.501,2.571]],"v":[[36.008,-31.806],[41.677,-22.529],[71.869,54.465],[165.232,31.599],[172.376,-15.517],[169.688,-29.218],[125.987,33.186]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":30,"s":[{"i":[[32.905,66.276],[-0.889,-4.86],[-23.038,-21.088],[-10.215,25.635],[3.941,28.747],[1.172,4.577],[42.671,-3.275]],"o":[[2.591,5.384],[4.557,24.917],[23.028,21.078],[0,0],[-0.617,-4.506],[0,0],[-33.501,2.571]],"v":[[36.265,-33.405],[41.677,-22.529],[71.869,54.465],[165.232,31.599],[172.376,-15.517],[169.688,-29.218],[125.987,33.186]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":31,"s":[{"i":[[29.786,67.882],[-0.889,-4.86],[-23.038,-21.088],[-10.215,25.635],[3.941,28.747],[1.172,4.577],[42.671,-3.275]],"o":[[2.337,5.511],[4.557,24.917],[23.028,21.078],[0,0],[-0.617,-4.506],[0,0],[-33.501,2.571]],"v":[[36.619,-35.609],[41.677,-22.529],[71.869,54.465],[165.232,31.599],[172.376,-15.517],[169.688,-29.218],[125.987,33.186]],"c":true}]},{"i":{"x":0.67,"y":1},"o":{"x":0.167,"y":0.167},"t":32,"s":[{"i":[[27.154,69.236],[-0.889,-4.86],[-23.038,-21.088],[-10.215,25.635],[3.941,28.747],[1.172,4.577],[42.671,-3.275]],"o":[[2.123,5.617],[4.557,24.917],[23.028,21.078],[0,0],[-0.617,-4.506],[0,0],[-33.501,2.571]],"v":[[36.918,-37.468],[41.677,-22.529],[71.869,54.465],[165.232,31.599],[172.376,-15.517],[169.688,-29.218],[125.987,33.186]],"c":true}]},{"t":33,"s":[{"i":[[25.645,70.013],[-0.889,-4.86],[-23.038,-21.088],[-10.215,25.635],[3.941,28.747],[1.172,4.577],[42.671,-3.275]],"o":[[2.001,5.679],[4.557,24.917],[23.028,21.078],[0,0],[-0.617,-4.506],[0,0],[-33.501,2.571]],"v":[[37.09,-38.535],[41.677,-22.529],[71.869,54.465],[165.232,31.599],[172.376,-15.517],[169.688,-29.218],[125.987,33.186]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.86274510622,0.690196096897,0.498039215803,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.33,"y":0},"t":0,"s":[{"i":[[-2.11,-7.28],[0.08,1.65],[-12.857,-21.206],[-4.596,-2.59],[0,0],[7.09,11.6],[0,0],[-12.323,-15.225],[-4.52,-8.91],[-0.32,1.03],[0,0],[0,0],[-15.78,-21.096],[-6.251,-3.228],[0,0],[0,0],[-0.5,-0.5],[10.24,-0.71],[17.66,8.66],[0.7,-7.28],[12.13,-0.37],[-1.73,-12.13],[5.13,-13.54]],"o":[[0.81,-1.63],[-0.54,-10.39],[13.69,22.58],[6.049,3.409],[-2.93,-11.65],[-7.1,-11.59],[5.15,-18],[11.13,13.751],[4.52,8.92],[0.33,-1.03],[-4.26,-15.14],[1.37,-11.32],[18.31,24.479],[5.494,2.837],[0,0],[3.64,-15.88],[-6.75,-11.84],[0,0],[-13.29,-6.51],[0,0],[-12.12,0.38],[0,0],[-1.61,4.25]],"v":[[26.173,-69.753],[27.343,-74.753],[48.083,-70.753],[68.653,-33.843],[74.463,-39.163],[54.034,-80.303],[44.824,-98.643],[74.463,-95.213],[98.713,-51.333],[109.264,-48.413],[108.614,-52.253],[84.773,-102.243],[111.373,-92.133],[140.114,-32.473],[147.793,-35.753],[142.653,-52.793],[159.744,-55.513],[133.864,-74.753],[99.224,-119.793],[77.043,-110.433],[56.954,-121.553],[41.713,-96.923],[24.733,-87.573]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":1,"s":[{"i":[[-2.268,-7.212],[0.118,1.643],[-13.3,-20.855],[-4.633,-2.505],[0,0],[7.333,11.406],[0,0],[-12.632,-14.904],[-4.861,-8.611],[-0.456,1.076],[0,0],[0,0],[-18.576,-21.931],[-6.251,-3.228],[0,0],[0,0],[-0.5,-0.5],[10.182,-0.313],[20.141,7.926],[0.512,-7.745],[14.833,0.024],[-1.999,-12.057],[4.81,-13.618]],"o":[[0.771,-1.644],[-0.773,-10.349],[14.162,22.207],[6.099,3.297],[-3.129,-11.579],[-7.342,-11.397],[4.729,-18.066],[11.409,13.461],[4.943,8.676],[0.464,-1.076],[-4.111,-13.372],[0.983,-11.635],[19.324,23.531],[5.494,2.837],[0,0],[3.64,-15.88],[-6.75,-11.84],[0,0],[-15.156,-5.959],[0,0],[-12.118,0.268],[0,0],[-1.509,4.274]],"v":[[25.327,-68.352],[26.381,-73.364],[47.153,-69.844],[68.713,-33.723],[74.42,-39.136],[52.871,-79.502],[43.272,-97.582],[73.172,-94.372],[99.197,-51.267],[109.687,-48.614],[109.297,-52.402],[89.727,-93.131],[112.312,-94.306],[140.114,-32.473],[147.793,-35.753],[143.355,-52.648],[160.639,-53.94],[135.298,-74.363],[100.909,-119.876],[83.489,-99.761],[55.223,-120.029],[40.21,-95.797],[23.489,-86.089]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":2,"s":[{"i":[[-2.545,-7.093],[0.183,1.632],[-14.072,-20.245],[-4.699,-2.357],[0,0],[7.756,11.068],[0,0],[-13.171,-14.343],[-5.457,-8.09],[-0.691,1.157],[0,0],[0,0],[-27.054,-23.386],[-6.251,-3.228],[0,0],[0,0],[-0.5,-0.5],[10.08,0.38],[28.226,6.643],[0.185,-8.555],[19.546,0.712],[-2.468,-11.929],[4.251,-13.753]],"o":[[0.703,-1.668],[-1.18,-10.276],[14.984,21.557],[6.185,3.103],[-3.476,-11.455],[-7.764,-11.06],[3.994,-18.18],[11.896,12.955],[5.679,8.251],[0.698,-1.157],[-3.85,-10.289],[0.308,-12.183],[24.332,21.879],[5.494,2.837],[0,0],[3.64,-15.88],[-6.75,-11.84],[0,0],[-21.239,-4.999],[0,0],[-12.114,0.074],[0,0],[-1.334,4.317]],"v":[[23.852,-65.91],[24.703,-70.944],[45.531,-68.26],[68.818,-33.513],[74.345,-39.087],[50.843,-78.105],[40.568,-95.732],[70.921,-92.905],[100.041,-51.15],[110.425,-48.965],[110.489,-52.662],[98.362,-77.246],[110.708,-98.093],[140.114,-32.473],[147.793,-35.753],[144.578,-52.395],[162.2,-51.198],[137.8,-73.682],[99.055,-120.021],[89.726,-88.393],[52.207,-117.372],[37.588,-93.833],[21.319,-83.503]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":3,"s":[{"i":[[-2.872,-6.952],[0.261,1.618],[-14.987,-19.521],[-4.777,-2.182],[0,0],[8.259,10.668],[0,0],[-13.81,-13.679],[-6.162,-7.473],[-0.971,1.252],[0,0],[0,0],[-29.229,-25.111],[-6.251,-3.228],[0,0],[0,0],[-0.5,-0.5],[9.96,1.202],[29.592,5.13],[-0.203,-9.516],[25.134,1.527],[-3.025,-11.778],[3.589,-13.914]],"o":[[0.622,-1.696],[-1.662,-10.19],[15.958,20.786],[6.287,2.872],[-3.888,-11.309],[-8.263,-10.661],[3.124,-18.316],[12.473,12.355],[6.553,7.747],[0.976,-1.253],[-3.541,-6.635],[-0.492,-12.833],[23.186,19.92],[5.494,2.837],[0,0],[3.64,-15.88],[-6.75,-11.84],[0,0],[-22.267,-3.86],[0,0],[-12.11,-0.157],[0,0],[-1.126,4.367]],"v":[[22.104,-63.014],[22.714,-68.074],[43.608,-66.382],[68.942,-33.264],[74.256,-39.03],[48.44,-76.449],[37.362,-93.539],[68.253,-91.165],[101.041,-51.013],[111.301,-49.381],[111.902,-52.971],[108.6,-58.414],[115.889,-102.583],[140.114,-32.473],[147.793,-35.753],[146.029,-52.095],[164.05,-47.947],[140.765,-72.876],[107.332,-120.192],[108.047,-59.1],[48.632,-114.222],[34.48,-91.505],[18.747,-80.436]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":4,"s":[{"i":[[-3.11,-6.85],[0.317,1.608],[-15.651,-18.996],[-4.833,-2.055],[0,0],[8.623,10.377],[0,0],[-14.274,-13.198],[-6.674,-7.024],[-1.173,1.321],[0,0],[0,0],[-26.603,-24.789],[-6.251,-3.228],[0,0],[0,0],[-0.5,-0.5],[9.873,1.798],[28.514,4.033],[1.71,-10.398],[24.919,1.564],[-3.428,-11.668],[3.108,-14.031]],"o":[[0.563,-1.716],[-2.012,-10.128],[16.666,20.227],[6.361,2.704],[-4.186,-11.202],[-8.626,-10.372],[2.492,-18.414],[12.892,11.92],[7.187,7.381],[1.178,-1.322],[-3.317,-3.983],[2.201,-13.941],[22.108,20.95],[5.494,2.837],[0,0],[3.64,-15.88],[-6.75,-11.84],[0,0],[-21.455,-3.034],[0,0],[-12.107,-0.325],[0,0],[-0.975,4.404]],"v":[[20.835,-60.914],[21.271,-65.991],[42.213,-65.019],[69.031,-33.083],[74.192,-38.989],[46.696,-75.247],[35.035,-91.948],[66.316,-89.904],[101.767,-50.913],[111.936,-49.683],[112.927,-53.195],[109.628,-56.151],[119.945,-104.517],[140.114,-32.473],[147.793,-35.753],[147.081,-51.877],[165.393,-45.588],[142.917,-72.29],[113.502,-120.905],[109.401,-56.998],[46.037,-111.936],[32.226,-89.816],[16.88,-78.211]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":5,"s":[{"i":[[-3.348,-6.747],[0.373,1.598],[-16.316,-18.47],[-4.89,-1.927],[0,0],[8.987,10.087],[0,0],[-14.738,-12.716],[-7.186,-6.576],[-1.376,1.391],[0,0],[0,0],[-23.977,-24.466],[-6.251,-3.228],[0,0],[0,0],[-0.5,-0.5],[9.786,2.394],[27.436,2.935],[3.622,-11.28],[24.703,1.6],[-3.832,-11.558],[2.628,-14.147]],"o":[[0.505,-1.737],[-2.361,-10.066],[17.373,19.667],[6.436,2.537],[-4.485,-11.096],[-8.989,-10.082],[1.861,-18.512],[13.311,11.485],[7.82,7.015],[1.38,-1.391],[-3.093,-1.332],[4.894,-15.05],[21.03,21.98],[5.494,2.837],[0,0],[3.64,-15.88],[-6.75,-11.84],[0,0],[-20.643,-2.208],[0,0],[-12.104,-0.492],[0,0],[-0.825,4.44]],"v":[[19.566,-58.813],[19.828,-63.909],[40.818,-63.656],[69.121,-32.903],[74.127,-38.948],[44.952,-74.046],[32.709,-90.357],[64.38,-88.642],[102.493,-50.813],[112.571,-49.985],[113.952,-53.418],[110.657,-53.889],[124.001,-106.451],[140.114,-32.473],[147.793,-35.753],[148.134,-51.659],[166.736,-43.23],[145.068,-71.705],[119.672,-121.618],[110.755,-54.897],[43.443,-109.651],[29.971,-88.126],[15.014,-75.986]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":6,"s":[{"i":[[-3.586,-6.645],[0.43,1.588],[-16.98,-17.945],[-4.946,-1.8],[0,0],[9.352,9.796],[0,0],[-15.201,-12.234],[-7.698,-6.128],[-1.579,1.46],[0,0],[0,0],[-25.153,-24.144],[-6.251,-3.228],[0,0],[0,0],[-0.5,-0.5],[9.699,2.99],[31.052,1.837],[5.534,-12.162],[24.488,1.636],[-4.236,-11.448],[2.147,-14.264]],"o":[[0.446,-1.758],[-2.711,-10.004],[18.08,19.108],[6.51,2.369],[-4.783,-10.99],[-9.351,-9.793],[1.229,-18.611],[13.73,11.05],[8.454,6.649],[1.581,-1.461],[-2.869,1.32],[7.587,-16.158],[23.505,23.01],[5.494,2.837],[0,0],[3.64,-15.88],[-6.75,-11.84],[0,0],[-23.364,-1.382],[0,0],[-12.101,-0.66],[0,0],[-0.674,4.477]],"v":[[18.298,-56.712],[18.385,-61.827],[39.423,-62.294],[69.211,-32.722],[74.063,-38.906],[43.208,-72.844],[30.383,-88.766],[62.444,-87.38],[103.219,-50.714],[113.206,-50.287],[114.977,-53.642],[111.685,-51.626],[123.757,-108.385],[140.114,-32.473],[147.793,-35.753],[149.186,-51.441],[168.078,-40.871],[147.22,-71.12],[121.147,-122.331],[112.108,-52.796],[40.849,-107.365],[27.716,-86.437],[13.148,-73.761]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":7,"s":[{"i":[[-3.676,-6.607],[0.451,1.585],[-17.231,-17.747],[-4.967,-1.752],[0,0],[9.49,9.686],[0,0],[-15.377,-12.052],[-7.892,-5.959],[-1.655,1.486],[0,0],[0,0],[-25.857,-24.022],[-6.251,-3.228],[0,0],[0,0],[-0.5,-0.5],[9.666,3.215],[32.961,1.422],[6.257,-12.495],[24.407,1.65],[-4.388,-11.406],[1.965,-14.308]],"o":[[0.424,-1.765],[-2.843,-9.98],[18.347,18.896],[6.538,2.306],[-4.896,-10.95],[-9.489,-9.683],[0.99,-18.648],[13.888,10.885],[8.694,6.511],[1.657,-1.487],[-2.784,2.323],[8.606,-16.577],[24.824,23.399],[5.494,2.837],[0,0],[3.64,-15.88],[-6.75,-11.84],[0,0],[-24.8,-1.07],[0,0],[-12.099,-0.723],[0,0],[-0.617,4.491]],"v":[[17.818,-55.917],[17.84,-61.039],[38.895,-61.778],[69.245,-32.654],[74.038,-38.891],[42.549,-72.39],[29.503,-88.164],[61.712,-86.902],[103.493,-50.676],[113.446,-50.401],[115.364,-53.727],[112.074,-50.77],[122.963,-109.116],[140.114,-32.473],[147.793,-35.753],[149.584,-51.359],[168.586,-39.978],[148.034,-70.899],[121.165,-122.6],[112.621,-52.001],[39.867,-106.501],[26.863,-85.798],[12.442,-72.92]],"c":true}]},{"i":{"x":0,"y":1},"o":{"x":0.167,"y":0.167},"t":8,"s":[{"i":[[-3.765,-6.568],[0.472,1.581],[-17.482,-17.548],[-4.989,-1.704],[0,0],[9.627,9.576],[0,0],[-15.552,-11.869],[-8.086,-5.789],[-1.732,1.512],[0,0],[0,0],[-25.082,-23.9],[-6.251,-3.228],[0,0],[0,0],[-0.5,-0.5],[9.633,3.441],[33.086,1.007],[6.981,-12.829],[24.325,1.663],[-4.541,-11.365],[1.783,-14.352]],"o":[[0.402,-1.773],[-2.976,-9.957],[18.615,18.685],[6.566,2.243],[-5.009,-10.909],[-9.626,-9.574],[0.751,-18.685],[14.047,10.72],[8.933,6.372],[1.734,-1.513],[-2.699,3.326],[9.625,-16.997],[24.788,23.789],[5.494,2.837],[0,0],[3.64,-15.88],[-6.75,-11.84],[0,0],[-24.894,-0.757],[0,0],[-12.098,-0.786],[0,0],[-0.56,4.505]],"v":[[17.338,-55.122],[17.294,-60.251],[38.367,-61.263],[69.279,-32.586],[74.014,-38.875],[41.889,-71.935],[28.623,-87.563],[60.979,-86.425],[103.768,-50.638],[113.686,-50.515],[115.752,-53.811],[112.463,-49.914],[123.764,-109.848],[140.114,-32.473],[147.793,-35.753],[149.982,-51.277],[169.094,-39.086],[148.847,-70.677],[122.966,-122.87],[113.133,-51.206],[38.886,-105.636],[26.01,-85.159],[11.736,-72.078]],"c":true}]},{"i":{"x":0,"y":1},"o":{"x":0.33,"y":0},"t":13,"s":[{"i":[[-3.834,-6.538],[0.488,1.578],[-17.673,-17.397],[-5.005,-1.667],[0,0],[9.732,9.493],[0,0],[-15.686,-11.731],[-8.233,-5.66],[-1.79,1.532],[0,0],[0,0],[-23.985,-23.807],[-6.251,-3.228],[0,0],[0,0],[-0.5,-0.5],[9.608,3.612],[32.523,0.691],[7.531,-13.082],[24.263,1.674],[-4.657,-11.333],[1.645,-14.386]],"o":[[0.385,-1.779],[-3.076,-9.939],[18.818,18.524],[6.588,2.194],[-5.095,-10.879],[-9.73,-9.49],[0.57,-18.714],[14.167,10.595],[9.116,6.267],[1.792,-1.533],[-2.635,4.089],[10.399,-17.315],[24.266,24.085],[5.494,2.837],[0,0],[3.64,-15.88],[-6.75,-11.84],[0,0],[-24.47,-0.52],[0,0],[-12.097,-0.835],[0,0],[-0.516,4.515]],"v":[[16.973,-54.518],[16.879,-59.652],[37.966,-60.871],[69.305,-32.534],[73.995,-38.863],[41.387,-71.59],[27.954,-87.105],[60.423,-86.062],[103.976,-50.609],[113.869,-50.602],[116.047,-53.876],[112.759,-49.264],[125.003,-110.404],[140.114,-32.473],[147.793,-35.753],[150.285,-51.214],[169.48,-38.408],[149.466,-70.509],[124.993,-123.075],[113.522,-50.602],[38.14,-104.979],[25.361,-84.673],[11.199,-71.438]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":1,"y":0},"t":20,"s":[{"i":[[-3.834,-6.538],[0.488,1.578],[-17.673,-17.397],[-5.005,-1.667],[0,0],[9.732,9.493],[0,0],[-15.686,-11.731],[-8.233,-5.66],[-1.79,1.532],[0,0],[0,0],[-23.985,-23.807],[-6.251,-3.228],[0,0],[0,0],[-0.5,-0.5],[9.608,3.612],[32.523,0.691],[7.531,-13.082],[24.263,1.674],[-4.657,-11.333],[1.645,-14.386]],"o":[[0.385,-1.779],[-3.076,-9.939],[18.818,18.524],[6.588,2.194],[-5.095,-10.879],[-9.73,-9.49],[0.57,-18.714],[14.167,10.595],[9.116,6.267],[1.792,-1.533],[-2.635,4.089],[10.399,-17.315],[24.266,24.085],[5.494,2.837],[0,0],[3.64,-15.88],[-6.75,-11.84],[0,0],[-24.47,-0.52],[0,0],[-12.097,-0.835],[0,0],[-0.516,4.515]],"v":[[16.973,-54.518],[16.879,-59.652],[37.966,-60.871],[69.305,-32.534],[73.995,-38.863],[41.387,-71.59],[27.954,-87.105],[60.423,-86.062],[103.976,-50.609],[113.869,-50.602],[116.047,-53.876],[112.759,-49.264],[125.003,-110.404],[140.114,-32.473],[147.793,-35.753],[150.285,-51.214],[169.48,-38.408],[149.466,-70.509],[124.993,-123.075],[113.522,-50.602],[38.14,-104.979],[25.361,-84.673],[11.199,-71.438]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":25,"s":[{"i":[[-3.765,-6.568],[0.472,1.581],[-17.482,-17.548],[-4.989,-1.704],[0,0],[9.627,9.576],[0,0],[-15.552,-11.869],[-8.086,-5.789],[-1.732,1.512],[0,0],[0,0],[-25.082,-23.9],[-6.251,-3.228],[0,0],[0,0],[-0.5,-0.5],[9.633,3.441],[33.086,1.007],[6.981,-12.829],[24.325,1.663],[-4.541,-11.365],[1.783,-14.352]],"o":[[0.402,-1.773],[-2.976,-9.957],[18.615,18.685],[6.566,2.243],[-5.009,-10.909],[-9.626,-9.574],[0.751,-18.685],[14.047,10.72],[8.933,6.372],[1.734,-1.513],[-2.699,3.326],[9.625,-16.997],[24.788,23.789],[5.494,2.837],[0,0],[3.64,-15.88],[-6.75,-11.84],[0,0],[-24.894,-0.757],[0,0],[-12.098,-0.786],[0,0],[-0.56,4.505]],"v":[[17.338,-55.122],[17.294,-60.251],[38.367,-61.263],[69.279,-32.586],[74.014,-38.875],[41.889,-71.935],[28.623,-87.563],[60.979,-86.425],[103.768,-50.638],[113.686,-50.515],[115.752,-53.811],[112.463,-49.914],[123.764,-109.848],[140.114,-32.473],[147.793,-35.753],[149.982,-51.277],[169.094,-39.086],[148.847,-70.677],[122.966,-122.87],[113.133,-51.206],[38.886,-105.636],[26.01,-85.159],[11.736,-72.078]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":26,"s":[{"i":[[-3.676,-6.607],[0.451,1.585],[-17.231,-17.747],[-4.967,-1.752],[0,0],[9.49,9.686],[0,0],[-15.377,-12.052],[-7.892,-5.959],[-1.655,1.486],[0,0],[0,0],[-25.857,-24.022],[-6.251,-3.228],[0,0],[0,0],[-0.5,-0.5],[9.666,3.215],[32.961,1.422],[6.257,-12.495],[24.407,1.65],[-4.388,-11.406],[1.965,-14.308]],"o":[[0.424,-1.765],[-2.843,-9.98],[18.347,18.896],[6.538,2.306],[-4.896,-10.95],[-9.489,-9.683],[0.99,-18.648],[13.888,10.885],[8.694,6.511],[1.657,-1.487],[-2.784,2.323],[8.606,-16.577],[24.824,23.399],[5.494,2.837],[0,0],[3.64,-15.88],[-6.75,-11.84],[0,0],[-24.8,-1.07],[0,0],[-12.099,-0.723],[0,0],[-0.617,4.491]],"v":[[17.818,-55.917],[17.84,-61.039],[38.895,-61.778],[69.245,-32.654],[74.038,-38.891],[42.549,-72.39],[29.503,-88.164],[61.712,-86.902],[103.493,-50.676],[113.446,-50.401],[115.364,-53.727],[112.074,-50.77],[122.963,-109.116],[140.114,-32.473],[147.793,-35.753],[149.584,-51.359],[168.586,-39.978],[148.034,-70.899],[121.165,-122.6],[112.621,-52.001],[39.867,-106.501],[26.863,-85.798],[12.442,-72.92]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":27,"s":[{"i":[[-3.586,-6.645],[0.43,1.588],[-16.98,-17.945],[-4.946,-1.8],[0,0],[9.352,9.796],[0,0],[-15.201,-12.234],[-7.698,-6.128],[-1.579,1.46],[0,0],[0,0],[-25.153,-24.144],[-6.251,-3.228],[0,0],[0,0],[-0.5,-0.5],[9.699,2.99],[31.052,1.837],[5.534,-12.162],[24.488,1.636],[-4.236,-11.448],[2.147,-14.264]],"o":[[0.446,-1.758],[-2.711,-10.004],[18.08,19.108],[6.51,2.369],[-4.783,-10.99],[-9.351,-9.793],[1.229,-18.611],[13.73,11.05],[8.454,6.649],[1.581,-1.461],[-2.869,1.32],[7.587,-16.158],[23.505,23.01],[5.494,2.837],[0,0],[3.64,-15.88],[-6.75,-11.84],[0,0],[-23.364,-1.382],[0,0],[-12.101,-0.66],[0,0],[-0.674,4.477]],"v":[[18.298,-56.712],[18.385,-61.827],[39.423,-62.294],[69.211,-32.722],[74.063,-38.906],[43.208,-72.844],[30.383,-88.766],[62.444,-87.38],[103.219,-50.714],[113.206,-50.287],[114.977,-53.642],[111.685,-51.626],[123.757,-108.385],[140.114,-32.473],[147.793,-35.753],[149.186,-51.441],[168.078,-40.871],[147.22,-71.12],[121.147,-122.331],[112.108,-52.796],[40.849,-107.365],[27.716,-86.437],[13.148,-73.761]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":28,"s":[{"i":[[-3.348,-6.747],[0.373,1.598],[-16.316,-18.47],[-4.89,-1.927],[0,0],[8.987,10.087],[0,0],[-14.738,-12.716],[-7.186,-6.576],[-1.376,1.391],[0,0],[0,0],[-23.977,-24.466],[-6.251,-3.228],[0,0],[0,0],[-0.5,-0.5],[9.786,2.394],[27.436,2.935],[3.622,-11.28],[24.703,1.6],[-3.832,-11.558],[2.628,-14.147]],"o":[[0.505,-1.737],[-2.361,-10.066],[17.373,19.667],[6.436,2.537],[-4.485,-11.096],[-8.989,-10.082],[1.861,-18.512],[13.311,11.485],[7.82,7.015],[1.38,-1.391],[-3.093,-1.332],[4.894,-15.05],[21.03,21.98],[5.494,2.837],[0,0],[3.64,-15.88],[-6.75,-11.84],[0,0],[-20.643,-2.208],[0,0],[-12.104,-0.492],[0,0],[-0.825,4.44]],"v":[[19.566,-58.813],[19.828,-63.909],[40.818,-63.656],[69.121,-32.903],[74.127,-38.948],[44.952,-74.046],[32.709,-90.357],[64.38,-88.642],[102.493,-50.813],[112.571,-49.985],[113.952,-53.418],[110.657,-53.889],[124.001,-106.451],[140.114,-32.473],[147.793,-35.753],[148.134,-51.659],[166.736,-43.23],[145.068,-71.705],[119.672,-121.618],[110.755,-54.897],[43.443,-109.651],[29.971,-88.126],[15.014,-75.986]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":29,"s":[{"i":[[-3.11,-6.85],[0.317,1.608],[-15.651,-18.996],[-4.833,-2.055],[0,0],[8.623,10.377],[0,0],[-14.274,-13.198],[-6.674,-7.024],[-1.173,1.321],[0,0],[0,0],[-26.603,-24.789],[-6.251,-3.228],[0,0],[0,0],[-0.5,-0.5],[9.873,1.798],[28.514,4.033],[1.71,-10.398],[24.919,1.564],[-3.428,-11.668],[3.108,-14.031]],"o":[[0.563,-1.716],[-2.012,-10.128],[16.666,20.227],[6.361,2.704],[-4.186,-11.202],[-8.626,-10.372],[2.492,-18.414],[12.892,11.92],[7.187,7.381],[1.178,-1.322],[-3.317,-3.983],[2.201,-13.941],[22.108,20.95],[5.494,2.837],[0,0],[3.64,-15.88],[-6.75,-11.84],[0,0],[-21.455,-3.034],[0,0],[-12.107,-0.325],[0,0],[-0.975,4.404]],"v":[[20.835,-60.914],[21.271,-65.991],[42.213,-65.019],[69.031,-33.083],[74.192,-38.989],[46.696,-75.247],[35.035,-91.948],[66.316,-89.904],[101.767,-50.913],[111.936,-49.683],[112.927,-53.195],[109.628,-56.151],[119.945,-104.517],[140.114,-32.473],[147.793,-35.753],[147.081,-51.877],[165.393,-45.588],[142.917,-72.29],[113.502,-120.905],[109.401,-56.998],[46.037,-111.936],[32.226,-89.816],[16.88,-78.211]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":30,"s":[{"i":[[-2.872,-6.952],[0.261,1.618],[-14.987,-19.521],[-4.777,-2.182],[0,0],[8.259,10.668],[0,0],[-13.81,-13.679],[-6.162,-7.473],[-0.971,1.252],[0,0],[0,0],[-31.345,-25.111],[-6.251,-3.228],[0,0],[0,0],[-0.5,-0.5],[9.96,1.202],[31.735,5.13],[-0.203,-9.516],[25.134,1.527],[-3.025,-11.778],[3.589,-13.914]],"o":[[0.622,-1.696],[-1.662,-10.19],[15.958,20.786],[6.287,2.872],[-3.888,-11.309],[-8.263,-10.661],[3.124,-18.316],[12.473,12.355],[6.553,7.747],[0.976,-1.253],[-3.541,-6.635],[-0.492,-12.833],[24.865,19.92],[5.494,2.837],[0,0],[3.64,-15.88],[-6.75,-11.84],[0,0],[-23.879,-3.86],[0,0],[-12.11,-0.157],[0,0],[-1.126,4.367]],"v":[[22.104,-63.014],[22.714,-68.074],[43.608,-66.382],[68.942,-33.264],[74.256,-39.03],[48.44,-76.449],[37.362,-93.539],[68.253,-91.165],[101.041,-51.013],[111.301,-49.381],[111.902,-52.971],[108.6,-58.414],[114.211,-102.583],[140.114,-32.473],[147.793,-35.753],[146.029,-52.095],[164.05,-47.947],[140.765,-72.876],[105.034,-120.192],[108.047,-59.1],[48.632,-114.222],[34.48,-91.505],[18.747,-80.436]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":31,"s":[{"i":[[-2.545,-7.093],[0.183,1.632],[-14.072,-20.245],[-4.699,-2.357],[0,0],[7.756,11.068],[0,0],[-13.171,-14.343],[-5.457,-8.09],[-0.691,1.157],[0,0],[0,0],[-23.45,-23.386],[-6.251,-3.228],[0,0],[0,0],[-0.5,-0.5],[10.08,0.38],[24.465,6.647],[0.185,-8.555],[19.546,0.712],[-2.468,-11.929],[4.251,-13.753]],"o":[[0.703,-1.668],[-1.18,-10.276],[14.984,21.557],[6.185,3.103],[-3.476,-11.455],[-7.764,-11.06],[3.994,-18.18],[11.896,12.955],[5.679,8.251],[0.698,-1.157],[-3.85,-10.289],[0.308,-12.183],[21.091,21.879],[5.494,2.837],[0,0],[3.64,-15.88],[-6.75,-11.84],[0,0],[-18.41,-4.999],[0,0],[-12.114,0.074],[0,0],[-1.334,4.317]],"v":[[23.852,-65.91],[24.703,-70.944],[45.531,-68.26],[68.818,-33.513],[74.345,-39.087],[50.843,-78.105],[40.568,-95.732],[70.921,-92.905],[100.041,-51.15],[110.425,-48.965],[110.489,-52.662],[98.362,-77.246],[113.949,-98.093],[140.114,-32.473],[147.793,-35.753],[144.578,-52.395],[162.2,-51.198],[137.8,-73.682],[103.848,-120.021],[87.357,-94.182],[52.207,-117.372],[37.588,-93.833],[21.319,-83.503]],"c":true}]},{"i":{"x":0.67,"y":1},"o":{"x":0.167,"y":0.167},"t":32,"s":[{"i":[[-2.268,-7.212],[0.118,1.643],[-13.3,-20.855],[-4.633,-2.505],[0,0],[7.333,11.406],[0,0],[-12.632,-14.904],[-4.861,-8.611],[-0.456,1.076],[0,0],[0,0],[-18.576,-21.931],[-6.251,-3.228],[0,0],[0,0],[-0.5,-0.5],[10.182,-0.313],[20.141,7.926],[0.512,-7.745],[14.833,0.024],[-1.999,-12.057],[4.81,-13.618]],"o":[[0.771,-1.644],[-0.773,-10.349],[14.162,22.207],[6.099,3.297],[-3.129,-11.579],[-7.342,-11.397],[4.729,-18.066],[11.409,13.461],[4.943,8.676],[0.464,-1.076],[-4.111,-13.372],[0.983,-11.635],[19.324,23.531],[5.494,2.837],[0,0],[3.64,-15.88],[-6.75,-11.84],[0,0],[-15.156,-5.959],[0,0],[-12.118,0.268],[0,0],[-1.509,4.274]],"v":[[25.327,-68.352],[26.381,-73.364],[47.153,-69.844],[68.713,-33.723],[74.42,-39.136],[52.871,-79.502],[43.272,-97.582],[73.172,-94.372],[99.197,-51.267],[109.687,-48.614],[109.297,-52.402],[89.727,-93.131],[112.312,-94.306],[140.114,-32.473],[147.793,-35.753],[143.355,-52.648],[160.639,-53.94],[135.298,-74.363],[100.909,-119.876],[83.489,-99.761],[55.223,-120.029],[40.21,-95.797],[23.489,-86.089]],"c":true}]},{"t":33,"s":[{"i":[[-2.11,-7.28],[0.08,1.65],[-12.857,-21.206],[-4.596,-2.59],[0,0],[7.09,11.6],[0,0],[-12.323,-15.225],[-4.52,-8.91],[-0.32,1.03],[0,0],[0,0],[-15.78,-21.096],[-6.251,-3.228],[0,0],[0,0],[-0.5,-0.5],[10.24,-0.71],[17.66,8.66],[0.7,-7.28],[12.13,-0.37],[-1.73,-12.13],[5.13,-13.54]],"o":[[0.81,-1.63],[-0.54,-10.39],[13.69,22.58],[6.049,3.409],[-2.93,-11.65],[-7.1,-11.59],[5.15,-18],[11.13,13.751],[4.52,8.92],[0.33,-1.03],[-4.26,-15.14],[1.37,-11.32],[18.31,24.479],[5.494,2.837],[0,0],[3.64,-15.88],[-6.75,-11.84],[0,0],[-13.29,-6.51],[0,0],[-12.12,0.38],[0,0],[-1.61,4.25]],"v":[[26.173,-69.753],[27.343,-74.753],[48.083,-70.753],[68.653,-33.843],[74.463,-39.163],[54.034,-80.303],[44.824,-98.643],[74.463,-95.213],[98.713,-51.333],[109.264,-48.413],[108.614,-52.253],[84.773,-102.243],[111.373,-92.133],[140.114,-32.473],[147.793,-35.753],[142.653,-52.793],[159.744,-55.513],[133.864,-74.753],[99.224,-119.793],[77.043,-110.433],[56.954,-121.553],[41.713,-96.923],[24.733,-87.573]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.988235294819,0.890196084976,0.780392169952,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.33,"y":0},"t":0,"s":[{"i":[[-10.22,25.636],[3.943,28.75],[20.093,-1.386],[17.668,8.661],[0.693,-7.275],[12.125,-0.373],[-1.732,-12.125],[5.123,-13.539],[-3.788,-20.696],[-23.034,-21.084]],"o":[[0,0],[-3.849,-28.061],[0,0],[-13.289,-6.514],[0,0],[-12.125,0.373],[0,0],[-4.85,12.818],[4.56,24.912],[23.034,21.084]],"v":[[165.237,31.599],[172.377,-15.516],[133.538,-74.755],[98.895,-119.792],[76.723,-110.438],[56.63,-121.551],[41.387,-96.927],[24.412,-87.574],[41.676,-22.528],[71.867,54.464]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":1,"s":[{"i":[[-10.22,25.636],[3.943,28.75],[19.979,-0.606],[20.15,7.929],[0.456,-7.72],[13.231,-0.157],[-2.001,-12.052],[4.803,-13.617],[-3.788,-20.696],[-23.034,-21.084]],"o":[[0,0],[-3.849,-28.061],[0,0],[-15.155,-5.963],[0,0],[-12.122,0.248],[0,0],[-4.547,12.891],[4.56,24.912],[23.034,21.084]],"v":[[165.237,31.599],[172.377,-15.516],[134.976,-74.378],[100.543,-119.858],[83.177,-99.786],[54.901,-120.017],[39.884,-95.793],[23.168,-86.082],[41.676,-22.528],[71.867,54.464]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":2,"s":[{"i":[[-10.22,25.636],[3.943,28.75],[19.779,0.754],[28.236,6.652],[0.042,-8.496],[15.159,0.222],[-2.47,-11.924],[4.244,-13.752],[-3.788,-20.696],[-23.034,-21.084]],"o":[[0,0],[-3.849,-28.061],[0,0],[-21.238,-5.003],[0,0],[-12.116,0.029],[0,0],[-4.018,13.02],[4.56,24.912],[23.034,21.084]],"v":[[165.237,31.599],[172.377,-15.516],[137.482,-73.719],[98.556,-119.974],[89.428,-88.452],[51.888,-117.344],[37.264,-93.817],[21,-83.483],[41.676,-22.528],[71.867,54.464]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":3,"s":[{"i":[[-10.22,25.636],[3.943,28.75],[19.543,2.365],[29.604,5.139],[-0.448,-9.416],[17.445,0.67],[-3.026,-11.773],[3.582,-13.912],[-3.788,-20.696],[-23.034,-21.084]],"o":[[0,0],[-3.849,-28.061],[0,0],[-22.266,-3.865],[0,0],[-12.11,-0.231],[0,0],[-3.391,13.171],[4.56,24.912],[23.034,21.084]],"v":[[165.237,31.599],[172.377,-15.516],[140.453,-72.938],[106.821,-120.112],[107.766,-59.2],[48.316,-114.175],[34.158,-91.473],[18.43,-80.402],[41.676,-22.528],[71.867,54.464]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":4,"s":[{"i":[[-10.22,25.636],[3.943,28.75],[19.372,3.535],[28.525,4.041],[1.518,-9.943],[19.104,0.995],[-3.43,-11.663],[3.102,-14.028],[-3.788,-20.696],[-23.034,-21.084]],"o":[[0,0],[-3.849,-28.061],[0,0],[-21.455,-3.039],[0,0],[-12.105,-0.42],[0,0],[-2.937,13.281],[4.56,24.912],[23.034,21.084]],"v":[[165.237,31.599],[172.377,-15.516],[142.609,-72.371],[113.017,-120.8],[109.132,-57.128],[45.724,-111.875],[31.904,-89.773],[16.565,-78.166],[41.676,-22.528],[71.867,54.464]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":5,"s":[{"i":[[-10.22,25.636],[3.943,28.75],[19.2,4.704],[27.446,2.943],[3.483,-10.469],[20.762,1.32],[-3.833,-11.553],[2.621,-14.145],[-3.788,-20.696],[-23.034,-21.084]],"o":[[0,0],[-3.849,-28.061],[0,0],[-20.643,-2.213],[0,0],[-12.1,-0.608],[0,0],[-2.482,13.392],[4.56,24.912],[23.034,21.084]],"v":[[165.237,31.599],[172.377,-15.516],[144.764,-71.805],[119.213,-121.489],[110.497,-55.057],[43.132,-109.576],[29.651,-88.073],[14.7,-75.93],[41.676,-22.528],[71.867,54.464]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":6,"s":[{"i":[[-10.22,25.636],[3.943,28.75],[19.029,5.874],[31.063,1.845],[5.448,-10.996],[22.421,1.646],[-4.237,-11.443],[2.141,-14.261],[-3.788,-20.696],[-23.034,-21.084]],"o":[[0,0],[-3.849,-28.061],[0,0],[-23.364,-1.388],[0,0],[-12.095,-0.797],[0,0],[-2.027,13.502],[4.56,24.912],[23.034,21.084]],"v":[[165.237,31.599],[172.377,-15.516],[146.92,-71.238],[120.638,-122.177],[111.863,-52.985],[40.54,-107.276],[27.397,-86.373],[12.835,-73.694],[41.676,-22.528],[71.867,54.464]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":7,"s":[{"i":[[-10.22,25.636],[3.943,28.75],[18.964,6.316],[32.973,1.43],[6.192,-11.196],[23.048,1.769],[-4.389,-11.401],[1.959,-14.305],[-3.788,-20.696],[-23.034,-21.084]],"o":[[0,0],[-3.849,-28.061],[0,0],[-24.8,-1.075],[0,0],[-12.093,-0.868],[0,0],[-1.855,13.543],[4.56,24.912],[23.034,21.084]],"v":[[165.237,31.599],[172.377,-15.516],[147.736,-71.024],[120.628,-122.437],[112.38,-52.201],[39.559,-106.406],[26.545,-85.729],[12.129,-72.848],[41.676,-22.528],[71.867,54.464]],"c":true}]},{"i":{"x":0,"y":1},"o":{"x":0.167,"y":0.167},"t":8,"s":[{"i":[[-10.22,25.636],[3.943,28.75],[18.899,6.759],[33.097,1.014],[6.935,-11.395],[23.676,1.892],[-4.542,-11.36],[1.778,-14.349],[-3.788,-20.696],[-23.034,-21.084]],"o":[[0,0],[-3.849,-28.061],[0,0],[-24.894,-0.763],[0,0],[-12.091,-0.939],[0,0],[-1.683,13.585],[4.56,24.912],[23.034,21.084]],"v":[[165.237,31.599],[172.377,-15.516],[148.551,-70.809],[122.431,-122.698],[112.896,-51.418],[38.578,-105.537],[25.692,-85.086],[11.424,-72.003],[41.676,-22.528],[71.867,54.464]],"c":true}]},{"i":{"x":0,"y":1},"o":{"x":0.33,"y":0},"t":13,"s":[{"i":[[-10.22,25.636],[3.943,28.75],[18.85,7.095],[32.534,0.698],[7.5,-11.546],[24.153,1.985],[-4.658,-11.328],[1.639,-14.383],[-3.788,-20.696],[-23.034,-21.084]],"o":[[0,0],[-3.849,-28.061],[0,0],[-24.471,-0.525],[0,0],[-12.09,-0.994],[0,0],[-1.552,13.617],[4.56,24.912],[23.034,21.084]],"v":[[165.237,31.599],[172.377,-15.516],[149.171,-70.646],[124.471,-122.896],[113.289,-50.822],[37.833,-104.875],[25.044,-84.597],[10.887,-71.36],[41.676,-22.528],[71.867,54.464]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":1,"y":0},"t":20,"s":[{"i":[[-10.22,25.636],[3.943,28.75],[18.85,7.095],[32.534,0.698],[7.5,-11.546],[24.153,1.985],[-4.658,-11.328],[1.639,-14.383],[-3.788,-20.696],[-23.034,-21.084]],"o":[[0,0],[-3.849,-28.061],[0,0],[-24.471,-0.525],[0,0],[-12.09,-0.994],[0,0],[-1.552,13.617],[4.56,24.912],[23.034,21.084]],"v":[[165.237,31.599],[172.377,-15.516],[149.171,-70.646],[124.471,-122.896],[113.289,-50.822],[37.833,-104.875],[25.044,-84.597],[10.887,-71.36],[41.676,-22.528],[71.867,54.464]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":25,"s":[{"i":[[-10.22,25.636],[3.943,28.75],[18.899,6.759],[33.097,1.014],[6.935,-11.395],[23.676,1.892],[-4.542,-11.36],[1.778,-14.349],[-3.788,-20.696],[-23.034,-21.084]],"o":[[0,0],[-3.849,-28.061],[0,0],[-24.894,-0.763],[0,0],[-12.091,-0.939],[0,0],[-1.683,13.585],[4.56,24.912],[23.034,21.084]],"v":[[165.237,31.599],[172.377,-15.516],[148.551,-70.809],[122.431,-122.698],[112.896,-51.418],[38.578,-105.537],[25.692,-85.086],[11.424,-72.003],[41.676,-22.528],[71.867,54.464]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":26,"s":[{"i":[[-10.22,25.636],[3.943,28.75],[18.964,6.316],[32.973,1.43],[6.192,-11.196],[23.048,1.769],[-4.389,-11.401],[1.959,-14.305],[-3.788,-20.696],[-23.034,-21.084]],"o":[[0,0],[-3.849,-28.061],[0,0],[-24.8,-1.075],[0,0],[-12.093,-0.868],[0,0],[-1.855,13.543],[4.56,24.912],[23.034,21.084]],"v":[[165.237,31.599],[172.377,-15.516],[147.736,-71.024],[120.628,-122.437],[112.38,-52.201],[39.559,-106.406],[26.545,-85.729],[12.129,-72.848],[41.676,-22.528],[71.867,54.464]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":27,"s":[{"i":[[-10.22,25.636],[3.943,28.75],[19.029,5.874],[31.063,1.845],[5.448,-10.996],[22.421,1.646],[-4.237,-11.443],[2.141,-14.261],[-3.788,-20.696],[-23.034,-21.084]],"o":[[0,0],[-3.849,-28.061],[0,0],[-23.364,-1.388],[0,0],[-12.095,-0.797],[0,0],[-2.027,13.502],[4.56,24.912],[23.034,21.084]],"v":[[165.237,31.599],[172.377,-15.516],[146.92,-71.238],[120.638,-122.177],[111.863,-52.985],[40.54,-107.276],[27.397,-86.373],[12.835,-73.694],[41.676,-22.528],[71.867,54.464]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":28,"s":[{"i":[[-10.22,25.636],[3.943,28.75],[19.2,4.704],[27.446,2.943],[3.483,-10.469],[20.762,1.32],[-3.833,-11.553],[2.621,-14.145],[-3.788,-20.696],[-23.034,-21.084]],"o":[[0,0],[-3.849,-28.061],[0,0],[-20.643,-2.213],[0,0],[-12.1,-0.608],[0,0],[-2.482,13.392],[4.56,24.912],[23.034,21.084]],"v":[[165.237,31.599],[172.377,-15.516],[144.764,-71.805],[119.213,-121.489],[110.497,-55.057],[43.132,-109.576],[29.651,-88.073],[14.7,-75.93],[41.676,-22.528],[71.867,54.464]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":29,"s":[{"i":[[-10.22,25.636],[3.943,28.75],[19.372,3.535],[28.525,4.041],[1.518,-9.943],[19.104,0.995],[-3.43,-11.663],[3.102,-14.028],[-3.788,-20.696],[-23.034,-21.084]],"o":[[0,0],[-3.849,-28.061],[0,0],[-21.455,-3.039],[0,0],[-12.105,-0.42],[0,0],[-2.937,13.281],[4.56,24.912],[23.034,21.084]],"v":[[165.237,31.599],[172.377,-15.516],[142.609,-72.371],[113.017,-120.8],[109.132,-57.128],[45.724,-111.875],[31.904,-89.773],[16.565,-78.166],[41.676,-22.528],[71.867,54.464]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":30,"s":[{"i":[[-10.22,25.636],[3.943,28.75],[19.543,2.365],[31.747,5.139],[-0.448,-9.416],[17.445,0.67],[-3.026,-11.773],[3.582,-13.912],[-3.788,-20.696],[-23.034,-21.084]],"o":[[0,0],[-3.849,-28.061],[0,0],[-23.878,-3.865],[0,0],[-12.11,-0.231],[0,0],[-3.391,13.171],[4.56,24.912],[23.034,21.084]],"v":[[165.237,31.599],[172.377,-15.516],[140.453,-72.938],[104.486,-120.112],[107.766,-59.2],[48.316,-114.175],[34.158,-91.473],[18.43,-80.402],[41.676,-22.528],[71.867,54.464]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":31,"s":[{"i":[[-10.22,25.636],[3.943,28.75],[19.779,0.754],[24.475,6.652],[0.042,-8.496],[15.159,0.222],[-2.47,-11.924],[4.244,-13.752],[-3.788,-20.696],[-23.034,-21.084]],"o":[[0,0],[-3.849,-28.061],[0,0],[-18.409,-5.003],[0,0],[-12.116,0.029],[0,0],[-4.018,13.02],[4.56,24.912],[23.034,21.084]],"v":[[165.237,31.599],[172.377,-15.516],[137.482,-73.719],[103.415,-119.974],[87.059,-94.242],[51.888,-117.344],[37.264,-93.817],[21,-83.483],[41.676,-22.528],[71.867,54.464]],"c":true}]},{"i":{"x":0.67,"y":1},"o":{"x":0.167,"y":0.167},"t":32,"s":[{"i":[[-10.22,25.636],[3.943,28.75],[19.979,-0.606],[20.15,7.929],[0.456,-7.72],[13.231,-0.157],[-2.001,-12.052],[4.803,-13.617],[-3.788,-20.696],[-23.034,-21.084]],"o":[[0,0],[-3.849,-28.061],[0,0],[-15.155,-5.963],[0,0],[-12.122,0.248],[0,0],[-4.547,12.891],[4.56,24.912],[23.034,21.084]],"v":[[165.237,31.599],[172.377,-15.516],[134.976,-74.378],[100.543,-119.858],[83.177,-99.786],[54.901,-120.017],[39.884,-95.793],[23.168,-86.082],[41.676,-22.528],[71.867,54.464]],"c":true}]},{"t":33,"s":[{"i":[[-10.22,25.636],[3.943,28.75],[20.093,-1.386],[17.668,8.661],[0.693,-7.275],[12.125,-0.373],[-1.732,-12.125],[5.123,-13.539],[-3.788,-20.696],[-23.034,-21.084]],"o":[[0,0],[-3.849,-28.061],[0,0],[-13.289,-6.514],[0,0],[-12.125,0.373],[0,0],[-4.85,12.818],[4.56,24.912],[23.034,21.084]],"v":[[165.237,31.599],[172.377,-15.516],[133.538,-74.755],[98.895,-119.792],[76.723,-110.438],[56.63,-121.551],[41.387,-96.927],[24.412,-87.574],[41.676,-22.528],[71.867,54.464]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.913725495338,0.78823530674,0.647058844566,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false}],"ip":0,"op":33,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"sr":1,"ks":{"o":{"a":0,"k":20},"p":{"a":0,"k":[341.929,268.75,0]},"a":{"a":0,"k":[87.294,20.789,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.33,"y":0},"t":0,"s":[{"i":[[-12.801,-44.719],[-3.605,-17.788],[-24.083,-15.726],[-2.877,-10.794],[30.317,-18.567],[-14.52,12.287],[0.825,6.413],[-2.342,5.994],[-0.65,2.073],[6.008,28.397],[19.934,-2.839],[18.256,7.361],[0.171,-7.306],[12.067,-1.247],[-2.597,-11.969]],"o":[[3.364,11.751],[6.529,32.214],[7.337,4.667],[2.085,7.822],[32.946,-6.739],[-5.103,-21.33],[-1.456,-11.312],[1.233,-2.004],[0,0],[-5.862,-27.709],[0,0],[-13.721,-5.533],[0,0],[-12.067,1.247],[0,0]],"v":[[12.775,-39.308],[27.656,6.086],[67.904,85.681],[85.931,109.29],[51.49,160.266],[147.472,120.625],[138.368,85.178],[141.966,58.001],[144.817,51.889],[148.546,4.38],[105.537,-51.897],[67.733,-94.328],[46.297,-83.398],[35.29,-89.718],[29.285,-75.875]],"c":true}]},{"i":{"x":0,"y":1},"o":{"x":0.33,"y":0},"t":13,"s":[{"i":[[-12.801,-44.719],[-7.19,-16.664],[-24.083,-15.726],[-2.877,-10.794],[30.317,-18.567],[-14.52,12.287],[0.825,6.413],[-2.342,5.994],[-0.65,2.073],[6.008,28.397],[19.934,-2.839],[-2.869,-83.633],[0.171,-7.306],[12.067,-1.247],[-2.597,-11.969]],"o":[[3.364,11.751],[12.709,29.454],[7.337,4.667],[2.085,7.822],[32.946,-6.739],[-5.103,-21.33],[-1.456,-11.312],[1.233,-2.004],[0,0],[-5.862,-27.709],[0,0],[0.507,14.786],[0,0],[-12.067,1.247],[0,0]],"v":[[-1.225,69.692],[17.656,113.086],[49.904,144.181],[68.931,145.79],[51.49,160.266],[147.472,120.625],[117.368,125.178],[120.966,98.001],[123.817,91.889],[134.546,113.38],[95.537,-17.397],[79.733,56.172],[68.297,62.602],[21.29,19.282],[15.285,33.125]],"c":true}]},{"i":{"x":0.67,"y":1},"o":{"x":1,"y":0},"t":20,"s":[{"i":[[-12.801,-44.719],[-7.19,-16.664],[-24.083,-15.726],[-2.877,-10.794],[30.317,-18.567],[-14.52,12.287],[0.825,6.413],[-2.342,5.994],[-0.65,2.073],[6.008,28.397],[19.934,-2.839],[-2.869,-83.633],[0.171,-7.306],[12.067,-1.247],[-2.597,-11.969]],"o":[[3.364,11.751],[12.709,29.454],[7.337,4.667],[2.085,7.822],[32.946,-6.739],[-5.103,-21.33],[-1.456,-11.312],[1.233,-2.004],[0,0],[-5.862,-27.709],[0,0],[0.507,14.786],[0,0],[-12.067,1.247],[0,0]],"v":[[-1.225,69.692],[17.656,113.086],[49.904,144.181],[68.931,145.79],[51.49,160.266],[147.472,120.625],[117.368,125.178],[120.966,98.001],[123.817,91.889],[134.546,113.38],[95.537,-17.397],[79.733,56.172],[68.297,62.602],[21.29,19.282],[15.285,33.125]],"c":true}]},{"t":33,"s":[{"i":[[-12.801,-44.719],[-3.605,-17.788],[-24.083,-15.726],[-2.877,-10.794],[30.317,-18.567],[-14.52,12.287],[0.825,6.413],[-2.342,5.994],[-0.65,2.073],[6.008,28.397],[19.934,-2.839],[18.256,7.361],[0.171,-7.306],[12.067,-1.247],[-2.597,-11.969]],"o":[[3.364,11.751],[6.529,32.214],[7.337,4.667],[2.085,7.822],[32.946,-6.739],[-5.103,-21.33],[-1.456,-11.312],[1.233,-2.004],[0,0],[-5.862,-27.709],[0,0],[-13.721,-5.533],[0,0],[-12.067,1.247],[0,0]],"v":[[12.775,-39.308],[27.656,6.086],[67.904,85.681],[85.931,109.29],[51.49,160.266],[147.472,120.625],[138.368,85.178],[141.966,58.001],[144.817,51.889],[148.546,4.38],[105.537,-51.897],[67.733,-94.328],[46.297,-83.398],[35.29,-89.718],[29.285,-75.875]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.61960786581,0.46274510026,0.239215686917,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false}],"ip":0,"op":33,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"sr":1,"ks":{"o":{"a":0,"k":20},"p":{"a":0,"k":[178.129,267.75,0]},"a":{"a":0,"k":[87.294,20.789,0]},"s":{"a":0,"k":[-100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-12.801,-44.719],[-3.605,-17.788],[-24.083,-15.726],[-2.877,-10.794],[30.317,-18.567],[-14.52,12.287],[0.825,6.413],[-2.342,5.994],[-0.65,2.073],[6.008,28.397],[19.934,-2.839],[18.256,7.361],[0.171,-7.306],[12.067,-1.247],[-2.597,-11.969]],"o":[[3.364,11.751],[6.529,32.214],[7.337,4.667],[2.085,7.822],[32.946,-6.739],[-5.103,-21.33],[-1.456,-11.312],[1.233,-2.004],[0,0],[-5.862,-27.709],[0,0],[-13.721,-5.533],[0,0],[-12.067,1.247],[0,0]],"v":[[12.775,-39.308],[27.656,6.086],[67.904,85.681],[85.931,109.29],[51.49,160.266],[147.472,120.625],[138.368,85.178],[141.966,58.001],[144.817,51.889],[148.546,4.38],[105.537,-51.897],[67.733,-94.328],[46.297,-83.398],[35.29,-89.718],[29.285,-75.875]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.61960786581,0.46274510026,0.239215686917,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false}],"ip":0,"op":33,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"parent":2,"sr":1,"ks":{"p":{"a":0,"k":[-63.783,7.296,0]},"a":{"a":0,"k":[-63.783,7.296,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,-0.618],[4.754,0],[0,2.194],[-4.754,0]],"o":[[0,2.194],[-4.754,0],[0,-1.118],[4.754,0]],"v":[[-50.556,14.024],[-63.816,16.897],[-76.57,13.649],[-63.82,15.407]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.282352954149,0.286274522543,0.286274522543,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0.161],[7.358,0],[0,-0.258],[-7.358,0]],"o":[[0,-0.253],[-7.358,0],[0,0.161],[7.358,0]],"v":[[-50.31,11.585],[-63.766,14.468],[-77.067,11.366],[-63.766,14.441]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.960784316063,0.960784316063,0.960784316063,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,-5.552],[13.393,0],[0,5.198],[-13.393,0]],"o":[[0,6.698],[-13.393,0],[0,-5.427],[13.393,0]],"v":[[-39.533,4.77],[-63.758,17.731],[-88.033,4.77],[-63.758,12.934]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.066666670144,0.066666670144,0.066666670144,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,-10.354],[13.393,0],[0,4.146],[-13.393,0]],"o":[[0,6.771],[-13.393,0],[0,-11.604],[13.393,0]],"v":[[-38.545,7.822],[-63.816,19.658],[-89.088,8.322],[-63.816,13.986]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.858823537827,0.749019622803,0.592156887054,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false}],"ip":0,"op":33,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"parent":2,"sr":1,"ks":{"p":{"a":0,"k":[73.842,7.296,0]},"a":{"a":0,"k":[-63.783,7.296,0]},"s":{"a":0,"k":[-100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.167,"y":0.167},"t":0,"s":[{"i":[[0,-0.618],[4.754,0],[0,2.194],[-4.754,0]],"o":[[0,2.194],[-4.754,0],[0,-1.118],[4.754,0]],"v":[[-50.556,14.024],[-63.816,16.897],[-76.57,13.649],[-63.82,15.407]],"c":true}]},{"i":{"x":0.67,"y":1},"o":{"x":0.66,"y":0},"t":6,"s":[{"i":[[0,-0.618],[4.754,0],[0,2.194],[-4.754,0]],"o":[[0,2.194],[-4.754,0],[0,-1.118],[4.754,0]],"v":[[-50.556,14.024],[-63.816,16.897],[-76.57,13.649],[-63.82,15.407]],"c":true}]},{"i":{"x":0.67,"y":1},"o":{"x":0.167,"y":0},"t":13,"s":[{"i":[[0,-11.871],[9.986,0],[0,7.079],[-9.986,0]],"o":[[0,7.079],[-9.986,0],[0,-11.357],[9.986,0]],"v":[[-46.701,12.524],[-63.783,33.272],[-80.865,12.524],[-63.783,11.875]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":20,"s":[{"i":[[0,-11.871],[9.986,0],[0,7.079],[-9.986,0]],"o":[[0,7.079],[-9.986,0],[0,-11.357],[9.986,0]],"v":[[-46.701,12.524],[-63.783,33.272],[-80.865,12.524],[-63.783,11.875]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.66,"y":0},"t":27,"s":[{"i":[[0,-0.618],[4.754,0],[0,2.194],[-4.754,0]],"o":[[0,2.194],[-4.754,0],[0,-1.118],[4.754,0]],"v":[[-50.556,14.024],[-63.816,16.897],[-76.57,13.649],[-63.82,15.407]],"c":true}]},{"t":33,"s":[{"i":[[0,-0.618],[4.754,0],[0,2.194],[-4.754,0]],"o":[[0,2.194],[-4.754,0],[0,-1.118],[4.754,0]],"v":[[-50.556,14.024],[-63.816,16.897],[-76.57,13.649],[-63.82,15.407]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.282352954149,0.286274522543,0.286274522543,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.167,"y":0.167},"t":0,"s":[{"i":[[0,0.161],[7.358,0],[0,-0.258],[-7.358,0]],"o":[[0,-0.253],[-7.358,0],[0,0.161],[7.358,0]],"v":[[-50.31,11.585],[-63.766,14.468],[-77.067,11.366],[-63.766,14.441]],"c":true}]},{"i":{"x":0.67,"y":1},"o":{"x":0.66,"y":0},"t":6,"s":[{"i":[[0,0.161],[7.358,0],[0,-0.258],[-7.358,0]],"o":[[0,-0.253],[-7.358,0],[0,0.161],[7.358,0]],"v":[[-50.31,11.585],[-63.766,14.468],[-77.067,11.366],[-63.766,14.441]],"c":true}]},{"i":{"x":0.67,"y":1},"o":{"x":0.167,"y":0},"t":13,"s":[{"i":[[0,-3.629],[7.358,0],[0,5.828],[-7.358,0]],"o":[[0,5.701],[-7.358,0],[0,-3.63],[7.358,0]],"v":[[-50.461,-12.676],[-63.783,-11.535],[-77.106,-12.676],[-63.783,-23.45]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":20,"s":[{"i":[[0,-3.629],[7.358,0],[0,5.828],[-7.358,0]],"o":[[0,5.701],[-7.358,0],[0,-3.63],[7.358,0]],"v":[[-50.461,-12.676],[-63.783,-11.535],[-77.106,-12.676],[-63.783,-23.45]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.66,"y":0},"t":27,"s":[{"i":[[0,0.161],[7.358,0],[0,-0.258],[-7.358,0]],"o":[[0,-0.253],[-7.358,0],[0,0.161],[7.358,0]],"v":[[-50.31,11.585],[-63.766,14.468],[-77.067,11.366],[-63.766,14.441]],"c":true}]},{"t":33,"s":[{"i":[[0,0.161],[7.358,0],[0,-0.258],[-7.358,0]],"o":[[0,-0.253],[-7.358,0],[0,0.161],[7.358,0]],"v":[[-50.31,11.585],[-63.766,14.468],[-77.067,11.366],[-63.766,14.441]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.960784316063,0.960784316063,0.960784316063,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.167,"y":0.167},"t":0,"s":[{"i":[[0,-5.552],[13.393,0],[0,5.198],[-13.393,0]],"o":[[0,6.698],[-13.393,0],[0,-5.427],[13.393,0]],"v":[[-39.533,4.77],[-63.758,17.731],[-88.033,4.77],[-63.758,12.934]],"c":true}]},{"i":{"x":0.67,"y":1},"o":{"x":0.66,"y":0},"t":6,"s":[{"i":[[0,-5.552],[13.393,0],[0,5.198],[-13.393,0]],"o":[[0,6.698],[-13.393,0],[0,-5.427],[13.393,0]],"v":[[-39.533,4.77],[-63.758,17.731],[-88.033,4.77],[-63.758,12.934]],"c":true}]},{"i":{"x":0.67,"y":1},"o":{"x":0.167,"y":0},"t":13,"s":[{"i":[[0,-19.516],[13.393,0],[0,19.516],[-13.393,0]],"o":[[0,19.516],[-13.393,0],[0,-19.516],[13.393,0]],"v":[[-39.533,4.77],[-63.783,40.106],[-88.033,4.77],[-63.783,-30.566]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":20,"s":[{"i":[[0,-19.516],[13.393,0],[0,19.516],[-13.393,0]],"o":[[0,19.516],[-13.393,0],[0,-19.516],[13.393,0]],"v":[[-39.533,4.77],[-63.783,40.106],[-88.033,4.77],[-63.783,-30.566]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.66,"y":0},"t":27,"s":[{"i":[[0,-5.552],[13.393,0],[0,5.198],[-13.393,0]],"o":[[0,6.698],[-13.393,0],[0,-5.427],[13.393,0]],"v":[[-39.533,4.77],[-63.758,17.731],[-88.033,4.77],[-63.758,12.934]],"c":true}]},{"t":33,"s":[{"i":[[0,-5.552],[13.393,0],[0,5.198],[-13.393,0]],"o":[[0,6.698],[-13.393,0],[0,-5.427],[13.393,0]],"v":[[-39.533,4.77],[-63.758,17.731],[-88.033,4.77],[-63.758,12.934]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.066666670144,0.066666670144,0.066666670144,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.167,"y":0.167},"t":0,"s":[{"i":[[0,-10.354],[13.393,0],[0,4.146],[-13.393,0]],"o":[[0,6.771],[-13.393,0],[0,-11.604],[13.393,0]],"v":[[-38.545,7.822],[-63.816,19.658],[-89.088,8.322],[-63.816,13.986]],"c":true}]},{"i":{"x":0.67,"y":1},"o":{"x":0.66,"y":0},"t":6,"s":[{"i":[[0,-10.354],[13.393,0],[0,4.146],[-13.393,0]],"o":[[0,6.771],[-13.393,0],[0,-11.604],[13.393,0]],"v":[[-38.545,7.822],[-63.816,19.658],[-89.088,8.322],[-63.816,13.986]],"c":true}]},{"i":{"x":0.67,"y":1},"o":{"x":0.167,"y":0},"t":13,"s":[{"i":[[0,-19.516],[13.393,0],[0,19.516],[-13.393,0]],"o":[[0,19.516],[-13.393,0],[0,-19.516],[13.393,0]],"v":[[-38.523,9.822],[-63.783,45.158],[-89.044,9.822],[-63.783,-25.514]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":20,"s":[{"i":[[0,-19.516],[13.393,0],[0,19.516],[-13.393,0]],"o":[[0,19.516],[-13.393,0],[0,-19.516],[13.393,0]],"v":[[-38.523,9.822],[-63.783,45.158],[-89.044,9.822],[-63.783,-25.514]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.66,"y":0},"t":27,"s":[{"i":[[0,-10.354],[13.393,0],[0,4.146],[-13.393,0]],"o":[[0,6.771],[-13.393,0],[0,-11.604],[13.393,0]],"v":[[-38.545,7.822],[-63.816,19.658],[-89.088,8.322],[-63.816,13.986]],"c":true}]},{"t":33,"s":[{"i":[[0,-10.354],[13.393,0],[0,4.146],[-13.393,0]],"o":[[0,6.771],[-13.393,0],[0,-11.604],[13.393,0]],"v":[[-38.545,7.822],[-63.816,19.658],[-89.088,8.322],[-63.816,13.986]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.858823537827,0.749019622803,0.592156887054,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false}],"ip":0,"op":33,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"parent":2,"sr":1,"ks":{"p":{"a":0,"k":[5.883,50.25,0]},"a":{"a":0,"k":[5.883,50.25,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[2.511,-4.918],[0.213,-0.942],[-3.744,-1.911],[2.476,-4.893],[-0.478,0.936],[3.948,2.016]],"o":[[-0.469,0.919],[2.519,-4.85],[3.744,1.911],[0.643,-0.734],[2.511,-4.918],[-3.948,-2.016]],"v":[[13.327,44.758],[12.3,47.57],[24.125,43.208],[25.922,54.567],[27.623,52.056],[25.019,39.507]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.211764708161,0.086274512112,0.035294119269,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[2.509,-4.914],[-3.948,-2.015],[-2.509,4.914],[3.948,2.015]],"o":[[-2.509,4.914],[3.948,2.015],[2.509,-4.914],[-3.948,-2.015]],"v":[[13.326,44.758],[15.931,57.305],[27.621,52.056],[25.016,39.509]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921574354,0.266666680574,0.188235297799,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[3.93,-7.699],[-3.948,-2.015],[-2.812,4.747],[3.948,2.015]],"o":[[-2.509,4.914],[3.948,2.015],[4.225,-7.134],[-3.948,-2.015]],"v":[[12.111,46.986],[15.567,59.968],[27.169,55.043],[25.661,40.199]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.941176474094,0.847058832645,0.705882370472,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-2.511,-4.918],[-0.213,-0.942],[3.744,-1.911],[-2.476,-4.893],[0.478,0.936],[-3.948,2.016]],"o":[[0.469,0.919],[-2.519,-4.85],[-3.744,1.911],[-0.643,-0.734],[-2.511,-4.918],[3.948,-2.016]],"v":[[-1.485,44.758],[-0.457,47.57],[-12.282,43.208],[-14.08,54.567],[-15.78,52.056],[-13.177,39.507]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.211764708161,0.086274512112,0.035294119269,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-2.509,-4.914],[3.948,-2.015],[2.509,4.914],[-3.948,2.015]],"o":[[2.509,4.914],[-3.948,2.015],[-2.509,-4.914],[3.948,-2.015]],"v":[[-1.484,44.758],[-4.089,57.305],[-15.779,52.056],[-13.174,39.509]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921574354,0.266666680574,0.188235297799,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-3.93,-7.699],[3.948,-2.015],[2.812,4.747],[-3.948,2.015]],"o":[[2.509,4.914],[-3.948,2.015],[-4.225,-7.134],[3.948,-2.015]],"v":[[-0.269,46.986],[-3.725,59.968],[-15.327,55.043],[-13.818,40.199]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.941176474094,0.847058832645,0.705882370472,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[11.287,0.292],[3.048,-1.351],[3.153,1.398],[3.628,-0.094],[-0.105,-10.467],[-29.829,0],[-0.445,8.16]],"o":[[-3.628,-0.094],[-3.153,1.398],[-3.048,-1.351],[-11.287,0.292],[0.079,7.902],[29.532,0],[0.569,-10.452]],"v":[[23.934,29.849],[11.849,32.814],[-0.121,32.814],[-12.206,29.849],[-28.625,47.042],[5.864,70.656],[40.353,47.042]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.858823537827,0.749019622803,0.592156887054,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false}],"ip":0,"op":33,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":4,"parent":2,"sr":1,"ks":{"p":{"a":0,"k":[5.864,107.189,0]},"a":{"a":0,"k":[5.864,107.189,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":3,"s":[{"i":[[1.188,-2.439],[0.921,0.417],[6.452,0.59],[6.878,0],[2.613,-0.213],[1.434,-0.652],[-0.372,-1.404],[-1.391,0.611],[-6.645,0.566],[-7.086,0],[-2.685,-0.256],[-1.475,-0.645]],"o":[[0.378,-1.404],[-1.435,-0.65],[-2.605,-0.238],[-6.884,0],[-6.452,0.525],[-0.92,0.418],[-1.188,-2.439],[1.479,-0.65],[2.688,-0.229],[7.086,0],[6.639,0.632],[1.396,0.611]],"v":[[34.087,94.056],[33.413,91.171],[21.553,89.807],[5.926,89.257],[-9.991,89.703],[-21.919,91.171],[-22.598,94.051],[-22.751,89.003],[-10.911,87.679],[5.926,87.083],[22.498,87.794],[34.24,89.003]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":13,"s":[{"i":[[0.653,-2.439],[0.333,1.551],[3.373,3.05],[6.071,0],[1.346,-1.29],[0.618,-2.064],[-0.205,-1.404],[-0.338,1.997],[-3.418,3.305],[-6.254,0],[-1.401,-1.279],[-0.54,-2.447]],"o":[[0.208,-1.404],[-0.519,-2.417],[-1.362,-1.231],[-6.075,0],[-3.325,3.187],[-0.266,0.888],[-0.653,-2.439],[0.358,-2.116],[1.382,-1.337],[6.254,0],[3.465,3.161],[0.511,2.317]],"v":[[23.396,98.676],[22.79,94.926],[17.364,87.313],[7.718,82.815],[-2.169,86.808],[-7.041,95.879],[-7.395,99.164],[-7.644,94.323],[-3.163,85.358],[7.718,80.641],[17.981,85.383],[23.361,92.486]],"c":true}]},{"i":{"x":0.67,"y":1},"o":{"x":0.66,"y":0},"t":20,"s":[{"i":[[0.653,-2.439],[0.333,1.551],[3.373,3.05],[6.071,0],[1.346,-1.29],[0.618,-2.064],[-0.205,-1.404],[-0.338,1.997],[-3.418,3.305],[-6.254,0],[-1.401,-1.279],[-0.54,-2.447]],"o":[[0.208,-1.404],[-0.519,-2.417],[-1.362,-1.231],[-6.075,0],[-3.325,3.187],[-0.266,0.888],[-0.653,-2.439],[0.358,-2.116],[1.382,-1.337],[6.254,0],[3.465,3.161],[0.511,2.317]],"v":[[23.396,98.676],[22.79,94.926],[17.364,87.313],[7.718,82.815],[-2.169,86.808],[-7.041,95.879],[-7.395,99.164],[-7.644,94.323],[-3.163,85.358],[7.718,80.641],[17.981,85.383],[23.361,92.486]],"c":true}]},{"t":30,"s":[{"i":[[1.188,-2.439],[0.921,0.417],[6.452,0.59],[6.878,0],[2.613,-0.213],[1.434,-0.652],[-0.372,-1.404],[-1.391,0.611],[-6.645,0.566],[-7.086,0],[-2.685,-0.256],[-1.475,-0.645]],"o":[[0.378,-1.404],[-1.435,-0.65],[-2.605,-0.238],[-6.884,0],[-6.452,0.525],[-0.92,0.418],[-1.188,-2.439],[1.479,-0.65],[2.688,-0.229],[7.086,0],[6.639,0.632],[1.396,0.611]],"v":[[34.087,94.056],[33.413,91.171],[21.553,89.807],[5.926,89.257],[-9.991,89.703],[-21.919,91.171],[-22.598,94.051],[-22.751,89.003],[-10.911,87.679],[5.926,87.083],[22.498,87.794],[34.24,89.003]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.015686275437,0,0.003921568859,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":3,"s":[{"i":[[0.676,0],[6.173,0],[4.992,3.171],[-1.401,0],[0,0],[-6.229,-1.128]],"o":[[-4.927,3.051],[-6.347,0],[-0.236,0],[6.404,0],[0,0],[1.339,0.243]],"v":[[23.022,105.559],[5.744,111.007],[-11.941,105.116],[-8.151,106.691],[5.555,107.59],[19.484,106.941]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":13,"s":[{"i":[[0.372,0],[3.395,0],[2.745,3.171],[-0.77,0],[0,0],[-3.425,-1.128]],"o":[[-2.709,3.051],[-3.49,0],[-0.13,0],[3.521,0],[0,0],[0.736,0.243]],"v":[[17.833,108.59],[7.798,113.377],[-1.599,109.066],[0.157,109.061],[7.764,109.456],[16.695,109.214]],"c":true}]},{"i":{"x":0.67,"y":1},"o":{"x":0.66,"y":0},"t":20,"s":[{"i":[[0.372,0],[3.395,0],[2.745,3.171],[-0.77,0],[0,0],[-3.425,-1.128]],"o":[[-2.709,3.051],[-3.49,0],[-0.13,0],[3.521,0],[0,0],[0.736,0.243]],"v":[[17.833,108.59],[7.798,113.377],[-1.599,109.066],[0.157,109.061],[7.764,109.456],[16.695,109.214]],"c":true}]},{"t":30,"s":[{"i":[[0.676,0],[6.173,0],[4.992,3.171],[-1.401,0],[0,0],[-6.229,-1.128]],"o":[[-4.927,3.051],[-6.347,0],[-0.236,0],[6.404,0],[0,0],[1.339,0.243]],"v":[[23.022,105.559],[5.744,111.007],[-11.941,105.116],[-8.151,106.691],[5.555,107.59],[19.484,106.941]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.274509817362,0.376470595598,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":3,"s":[{"i":[[2,0.88],[6.637,0.633],[7.087,0],[2.687,-0.229],[1.475,-0.649],[-2.5,-3.795],[-11.418,0],[-3.544,5.379]],"o":[[-1.475,-0.649],[-2.684,-0.256],[-7.087,0],[-6.643,0.567],[-2,0.88],[3.544,5.379],[11.418,0],[2.5,-3.795]],"v":[[34.242,89.001],[22.499,87.793],[5.925,87.084],[-10.913,87.679],[-22.749,89.001],[-20.634,97.431],[5.746,111.004],[32.127,97.431]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":13,"s":[{"i":[[0.731,3.326],[3.463,3.161],[6.255,0],[1.382,-1.336],[0.511,-1.952],[-1.375,-3.795],[-6.279,0],[-1.949,5.379]],"o":[[-0.539,-2.454],[-1.401,-1.279],[-6.255,0],[-3.416,3.304],[-0.692,2.646],[1.949,5.379],[6.279,0],[1.375,-3.795]],"v":[[23.362,92.486],[17.981,85.382],[7.718,80.642],[-3.164,85.358],[-7.643,94.319],[-6.315,102.544],[7.8,113.374],[22.318,102.05]],"c":true}]},{"i":{"x":0.67,"y":1},"o":{"x":0.66,"y":0},"t":20,"s":[{"i":[[0.731,3.326],[3.463,3.161],[6.255,0],[1.382,-1.336],[0.511,-1.952],[-1.375,-3.795],[-6.279,0],[-1.949,5.379]],"o":[[-0.539,-2.454],[-1.401,-1.279],[-6.255,0],[-3.416,3.304],[-0.692,2.646],[1.949,5.379],[6.279,0],[1.375,-3.795]],"v":[[23.362,92.486],[17.981,85.382],[7.718,80.642],[-3.164,85.358],[-7.643,94.319],[-6.315,102.544],[7.8,113.374],[22.318,102.05]],"c":true}]},{"t":30,"s":[{"i":[[2,0.88],[6.637,0.633],[7.087,0],[2.687,-0.229],[1.475,-0.649],[-2.5,-3.795],[-11.418,0],[-3.544,5.379]],"o":[[-1.475,-0.649],[-2.684,-0.256],[-7.087,0],[-6.643,0.567],[-2,0.88],[3.544,5.379],[11.418,0],[2.5,-3.795]],"v":[[34.242,89.001],[22.499,87.793],[5.925,87.084],[-10.913,87.679],[-22.749,89.001],[-20.634,97.431],[5.746,111.004],[32.127,97.431]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.247058823705,0.039215687662,0.141176477075,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":3,"s":[{"i":[[3.188,0.413],[6.882,0.687],[7.357,0],[2.786,-0.25],[1.597,-0.464],[-2.595,-3.795],[-11.852,0],[-3.679,5.379]],"o":[[-1.648,-0.214],[-2.783,-0.278],[-7.357,0],[-6.889,0.618],[-3.048,0.885],[3.678,5.379],[11.852,0],[2.595,-3.795]],"v":[[34.242,89.001],[22.653,90.081],[5.925,89.286],[-11.089,89.965],[-22.749,89.001],[-21.823,100.948],[5.733,115.682],[33.554,101.21]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":13,"s":[{"i":[[1.427,3.64],[3.584,3.306],[6.493,0],[1.43,-1.397],[0.622,-1.853],[-1.427,-3.795],[-6.517,0],[-2.023,5.379]],"o":[[-0.738,-1.882],[-1.45,-1.337],[-6.493,0],[-3.535,3.454],[-1.188,3.538],[2.023,5.379],[6.517,0],[1.427,-3.795]],"v":[[23.362,92.486],[17.962,87.849],[7.718,82.844],[-2.692,87.507],[-7.643,94.319],[-7.255,103.879],[7.792,118.052],[23.092,103.579]],"c":true}]},{"i":{"x":0.67,"y":1},"o":{"x":0.66,"y":0},"t":20,"s":[{"i":[[1.427,3.64],[3.584,3.306],[6.493,0],[1.43,-1.397],[0.622,-1.853],[-1.427,-3.795],[-6.517,0],[-2.023,5.379]],"o":[[-0.738,-1.882],[-1.45,-1.337],[-6.493,0],[-3.535,3.454],[-1.188,3.538],[2.023,5.379],[6.517,0],[1.427,-3.795]],"v":[[23.362,92.486],[17.962,87.849],[7.718,82.844],[-2.692,87.507],[-7.643,94.319],[-7.255,103.879],[7.792,118.052],[23.092,103.579]],"c":true}]},{"t":30,"s":[{"i":[[3.188,0.413],[6.882,0.687],[7.357,0],[2.786,-0.25],[1.597,-0.464],[-2.595,-3.795],[-11.852,0],[-3.679,5.379]],"o":[[-1.648,-0.214],[-2.783,-0.278],[-7.357,0],[-6.889,0.618],[-3.048,0.885],[3.678,5.379],[11.852,0],[2.595,-3.795]],"v":[[34.242,89.001],[22.653,90.081],[5.925,89.286],[-11.089,89.965],[-22.749,89.001],[-21.823,100.948],[5.733,115.682],[33.554,101.21]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.956862747669,0.913725495338,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":3,"s":[{"i":[[2,0.88],[6.637,0.633],[7.087,0],[2.687,-0.229],[1.475,-0.649],[-2.5,-3.795],[-11.418,0],[-3.544,5.379]],"o":[[-1.475,-0.649],[-2.684,-0.256],[-7.087,0],[-6.643,0.567],[-2,0.88],[3.544,5.379],[11.418,0],[2.5,-3.795]],"v":[[35.881,85.862],[23.167,84.726],[5.922,83.945],[-11.558,84.606],[-24.395,85.862],[-22.277,95.229],[5.746,108.802],[33.769,95.229]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.33,"y":0},"t":13,"s":[{"i":[[0.642,1.938],[3.047,3.546],[6.255,0],[2.025,-2.773],[0.19,-1.347],[-1.375,-3.795],[-6.279,0],[-1.949,5.379]],"o":[[-0.54,-1.629],[-1.226,-1.426],[-6.255,0],[-2.393,3.277],[-0.244,1.731],[1.949,5.379],[6.279,0],[1.375,-3.795]],"v":[[22.949,90.878],[18.446,83.779],[7.715,77.503],[-5.317,85.475],[-8.088,94.686],[-7.219,100.342],[7.8,111.172],[23.221,99.848]],"c":true}]},{"i":{"x":0.67,"y":1},"o":{"x":0.66,"y":0},"t":20,"s":[{"i":[[0.642,1.938],[3.047,3.546],[6.255,0],[2.025,-2.773],[0.19,-1.347],[-1.375,-3.795],[-6.279,0],[-1.949,5.379]],"o":[[-0.54,-1.629],[-1.226,-1.426],[-6.255,0],[-2.393,3.277],[-0.244,1.731],[1.949,5.379],[6.279,0],[1.375,-3.795]],"v":[[22.949,90.878],[18.446,83.779],[7.715,77.503],[-5.317,85.475],[-8.088,94.686],[-7.219,100.342],[7.8,111.172],[23.221,99.848]],"c":true}]},{"t":30,"s":[{"i":[[2,0.88],[6.637,0.633],[7.087,0],[2.687,-0.229],[1.475,-0.649],[-2.5,-3.795],[-11.418,0],[-3.544,5.379]],"o":[[-1.475,-0.649],[-2.684,-0.256],[-7.087,0],[-6.643,0.567],[-2,0.88],[3.544,5.379],[11.418,0],[2.5,-3.795]],"v":[[35.881,85.862],[23.167,84.726],[5.922,83.945],[-11.558,84.606],[-24.395,85.862],[-22.277,95.229],[5.746,108.802],[33.769,95.229]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.858823537827,0.749019622803,0.592156887054,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false}],"ip":0,"op":33,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":4,"parent":1,"sr":1,"ks":{"p":{"a":0,"k":[5.303,144.067,0]},"a":{"a":0,"k":[5.303,144.067,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-10.992,-6.96],[0,0],[-16.976,11.779],[-18.657,4.78]],"o":[[17.211,5.126],[18.61,11.782],[0,0],[10.182,-7.065],[0,0]],"v":[[-70.986,150.908],[-27.847,165.744],[5.303,176.601],[37.654,165.544],[81.313,150.908]],"c":false}},"hd":false},{"ty":"st","c":{"a":0,"k":[0.698039233685,0.584313750267,0.447058826685,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":2,"lj":2,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-24.85,4.01],[2.81,-4.07],[4.9,-0.67],[10.74,-7.45],[0,0],[16.98,11.78],[7.38,1.01],[3.407,3.585],[1.57,5.1],[-14.42,-8.27],[-8.4,5.66]],"o":[[-1.671,6.6],[-2.81,4.07],[-7.38,1.01],[-16.97,11.78],[0,0],[-10.73,-7.45],[-4.9,-0.67],[-3.841,-4.041],[12.31,3.41],[17.93,10.29],[7.89,-5.32]],"v":[[91.856,131.627],[83.799,149.584],[68.549,154.317],[37.649,165.547],[5.299,176.597],[-27.051,165.547],[-57.941,154.317],[-72.457,150.111],[-81.201,131.67],[-17.101,154.037],[28.949,154.037]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.61960786581,0.46274510026,0.239215686917,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":11},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[51.445,12.472],[11.245,-13.381],[2.922,3.477],[38.8,-9.406],[-5.196,-23.384],[-28.607,-10.289],[0.785,-3.583],[-16.2,-23.457],[-4.902,-0.674],[-10.738,-7.451],[0,0],[-16.975,11.779],[-7.382,1.015],[-2.812,4.072],[3.47,15.845],[-3.452,1.242],[-4.853,21.841]],"o":[[-38.801,-9.406],[-2.922,3.477],[-11.245,-13.381],[-51.445,12.472],[4.854,21.841],[3.452,1.242],[-3.47,15.845],[2.812,4.072],[7.382,1.015],[16.975,11.779],[0,0],[10.737,-7.451],[4.902,-0.674],[16.2,-23.457],[-0.785,-3.583],[28.607,-10.289],[5.197,-23.384]],"v":[[88.579,-75.949],[10.891,-45.909],[-0.285,-45.909],[-77.973,-75.949],[-132.017,4.597],[-80.556,57.662],[-75.875,66.071],[-70.075,146.877],[-57.943,154.316],[-27.048,165.544],[5.303,176.601],[37.654,165.544],[68.548,153.316],[80.68,146.877],[86.481,66.071],[91.162,57.662],[142.623,4.597]],"c":true}},"hd":false},{"ty":"st","c":{"a":0,"k":[0.941176474094,0.847058832645,0.701960802078,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":1,"lj":1,"ml":10,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-6.695,-15.921],[5.747,-3.178],[22.493,4.591],[6.631,-1.555],[16.983,8.766],[-3.144,6.284],[-28.696,7.38],[-11.25,-13.38],[-2.92,3.48],[-38.8,-9.4]],"o":[[2.546,6.053],[-39.7,21.954],[-6.674,-1.362],[-56.127,13.163],[-6.244,-3.223],[5.486,-10.965],[38.664,-9.944],[2.92,3.48],[11.25,-13.38],[26.786,6.499]],"v":[[134.093,-35.971],[128.458,-19.704],[14.574,-11.647],[-5.621,-11.396],[-116.496,-21.256],[-122.192,-38.41],[-76.315,-72.759],[-0.282,-45.913],[10.887,-45.913],[87.791,-72.245]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.858823537827,0.749019622803,0.592156887054,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[51.445,12.472],[11.245,-13.381],[2.922,3.477],[38.8,-9.406],[-5.196,-23.384],[-28.607,-10.289],[0.785,-3.583],[-16.2,-23.457],[-4.902,-0.674],[-10.738,-7.451],[0,0],[-16.975,11.779],[-7.382,1.015],[-2.812,4.072],[3.47,15.845],[-3.452,1.242],[-4.853,21.841]],"o":[[-38.801,-9.406],[-2.922,3.477],[-11.245,-13.381],[-51.445,12.472],[4.854,21.841],[3.452,1.242],[-3.47,15.845],[2.812,4.072],[7.382,1.015],[16.975,11.779],[0,0],[10.737,-7.451],[4.902,-0.674],[16.2,-23.457],[-0.785,-3.583],[28.607,-10.289],[5.197,-23.384]],"v":[[88.579,-75.949],[10.891,-45.909],[-0.285,-45.909],[-77.973,-75.949],[-132.017,4.597],[-80.556,57.662],[-75.875,66.071],[-70.075,146.877],[-57.943,154.316],[-27.048,165.544],[5.303,176.601],[37.654,165.544],[68.548,154.316],[80.68,146.877],[86.481,66.071],[91.162,57.662],[142.623,4.597]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.941176474094,0.847058832645,0.701960802078,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[24.154,48.305],[13.203,43.885],[77.248,0],[15.205,-50.539],[21.999,-43.995],[-198.638,0]],"o":[[-21.999,-43.995],[-15.205,-50.539],[-78.08,0],[-13.203,43.885],[-24.154,48.305],[198.638,0]],"v":[[169.587,-20.913],[146.026,-110.813],[5.303,-207.093],[-135.42,-110.813],[-158.981,-20.913],[5.303,159.087]],"c":true}},"hd":false},{"ty":"st","c":{"a":0,"k":[0.368627458811,0.258823543787,0.121568627656,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":1,"lj":1,"ml":10,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-7.633,-2.073]],"o":[[0,0],[0,0]],"v":[[41.579,-186.44],[54.078,-183.608]],"c":false}},"hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":6},"lc":2,"lj":2,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-1.612,-0.576]],"o":[[1.511,0.494],[0,0]],"v":[[62.717,-181.024],[67.407,-179.42]],"c":false}},"hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":6},"lc":2,"lj":2,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,-23.536],[50.223,0],[0,23.536],[-50.223,0]],"o":[[0,23.536],[-50.223,0],[0,-23.536],[50.223,0]],"v":[[96.859,-148.606],[5.921,-105.99],[-85.016,-148.606],[5.921,-191.222]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.780392169952,0.623529434204,0.419607847929,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[22.173,44.342],[12.12,40.285],[70.911,0],[13.958,-46.393],[20.194,-40.385],[-182.343,0]],"o":[[-20.194,-40.385],[-13.957,-46.393],[-71.675,0],[-12.12,40.285],[-22.173,44.342],[182.343,0]],"v":[[156.11,-21.166],[134.482,-103.692],[5.303,-192.073],[-123.876,-103.692],[-145.504,-21.166],[5.303,144.067]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.737254917622,0.572549045086,0.349019616842,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[24.154,48.305],[13.203,43.885],[77.248,0],[15.205,-50.539],[21.999,-43.995],[-198.638,0]],"o":[[-21.999,-43.995],[-15.205,-50.539],[-78.08,0],[-13.203,43.885],[-24.154,48.305],[198.638,0]],"v":[[169.587,-20.913],[146.026,-110.813],[5.303,-207.093],[-135.42,-110.813],[-158.981,-20.913],[5.303,159.087]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.61960786581,0.46274510026,0.239215686917,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false}],"ip":0,"op":33,"st":0,"bm":0},{"ddd":0,"ind":12,"ty":4,"parent":11,"sr":1,"ks":{"p":{"a":0,"k":[-148.03,-63.49,0]},"a":{"a":0,"k":[-148.03,-63.49,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[1.081,-15.591],[0,0],[0.01,0],[-5.699,9.64],[0,0]],"o":[[-42.636,-22.157],[-0.01,0],[-1.283,-12.428],[16.864,-28.514],[5.547,-0.606]],"v":[[-120.56,-89.291],[-178.963,-61.424],[-178.983,-61.434],[-172.678,-95.859],[-126.714,-115.865]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.858823537827,0.596078455448,0.592156887054,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[17.728,-1.949],[16.87,-28.514],[-21.623,-17.626]],"o":[[0,0],[-13.747,23.235],[25.716,20.962]],"v":[[-126.711,-115.866],[-172.682,-95.857],[-156.36,-15.922]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.929411768913,0.674509823322,0.666666686535,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[55.083,3.638],[22.172,-27.368],[-47.288,-39.234]],"o":[[-19.765,-1.305],[-24.472,30.208],[49.528,41.092]],"v":[[-115.798,-143.021],[-186.99,-118.078],[-164.645,10.795]],"c":true}},"hd":false},{"ty":"st","c":{"a":0,"k":[0.368627458811,0.258823543787,0.121568627656,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":1,"lj":1,"ml":10,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-36.319,10.182]],"o":[[0,0],[0,0]],"v":[[-194.126,-81.406],[-152.972,-131.334]],"c":false}},"hd":false},{"ty":"st","c":{"a":0,"k":[0.737254917622,0.572549045086,0.349019616842,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":10},"lc":2,"lj":2,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[55.083,3.638],[22.172,-27.368],[-47.288,-39.234]],"o":[[-19.765,-1.305],[-24.472,30.208],[49.528,41.092]],"v":[[-115.798,-143.021],[-186.99,-118.078],[-164.645,10.795]],"c":true}},"hd":false},{"ty":"st","c":{"a":0,"k":[0.368627458811,0.258823543787,0.121568627656,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":1,"lj":1,"ml":10,"bm":0,"hd":false},{"ty":"fl","c":{"a":0,"k":[0.61960786581,0.46274510026,0.239215686917,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false}],"ip":0,"op":33,"st":0,"bm":0},{"ddd":0,"ind":13,"ty":4,"parent":11,"sr":1,"ks":{"p":{"a":0,"k":[157.962,-63.49,0]},"a":{"a":0,"k":[157.962,-63.49,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-1.081,-15.591],[0,0],[-0.01,0],[5.699,9.64],[0,0]],"o":[[42.636,-22.157],[0.01,0],[1.283,-12.428],[-16.864,-28.514],[-5.547,-0.606]],"v":[[130.917,-89.291],[189.319,-61.424],[189.34,-61.434],[183.034,-95.859],[137.07,-115.865]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.858823537827,0.596078455448,0.592156887054,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-55.083,3.638],[-22.172,-27.368],[47.288,-39.234]],"o":[[19.765,-1.305],[24.472,30.208],[-49.528,41.092]],"v":[[125.73,-143.021],[196.922,-118.078],[174.577,10.795]],"c":true}},"hd":false},{"ty":"st","c":{"a":0,"k":[0.368627458811,0.258823543787,0.121568627656,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":1,"lj":1,"ml":10,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[36.319,10.182]],"o":[[0,0],[0,0]],"v":[[203.905,-80.406],[162.752,-130.334]],"c":false}},"hd":false},{"ty":"st","c":{"a":0,"k":[0.737254917622,0.572549045086,0.349019616842,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":10},"lc":2,"lj":2,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-17.728,-1.949],[-16.87,-28.514],[21.623,-17.626]],"o":[[0,0],[13.747,23.235],[-25.716,20.962]],"v":[[136.643,-115.866],[182.613,-95.857],[166.291,-15.922]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.929411768913,0.674509823322,0.666666686535,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-55.083,3.638],[-22.172,-27.368],[47.288,-39.234]],"o":[[19.765,-1.305],[24.472,30.208],[-49.528,41.092]],"v":[[125.73,-143.021],[196.922,-118.078],[174.577,10.795]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.61960786581,0.46274510026,0.239215686917,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false}],"ip":0,"op":33,"st":0,"bm":0}]}{"tgs":1,"v":"5.5.2","fr":60,"ip":0,"op":180,"w":512,"h":512,"nm":"password_track","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":5,"ty":3,"sr":1,"ks":{"o":{"a":0,"k":0},"p":{"a":0,"k":[256,256,0]},"s":{"a":0,"k":[95,95,100]}},"ao":0,"ip":0,"op":180,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":3,"parent":11,"sr":1,"ks":{"o":{"a":0,"k":0},"p":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[0,0,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[-31.579,28.421,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.34,"y":1},"o":{"x":0.66,"y":0},"t":160,"s":[28.421,28.421,0],"to":[0,0,0],"ti":[0,0,0]},{"t":180,"s":[0,0,0]}]}},"ao":0,"ip":0,"op":180,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"parent":6,"sr":1,"ks":{"r":{"a":1,"k":[{"i":{"x":[0.34],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":20,"s":[0]},{"i":{"x":[0.34],"y":[1]},"o":{"x":[0.66],"y":[0]},"t":160,"s":[0]},{"t":180,"s":[0]}]},"p":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[-63.783,7.296,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.34,"y":1},"o":{"x":0.167,"y":0.167},"t":20,"s":[-52.204,7.296,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.66,"y":0},"t":90,"s":[-57.995,8.348,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.34,"y":1},"o":{"x":0.66,"y":0},"t":160,"s":[-53.257,9.401,0],"to":[0,0,0],"ti":[0,0,0]},{"t":180,"s":[-63.783,7.296,0]}]},"a":{"a":0,"k":[-63.783,7.296,0]},"s":{"a":1,"k":[{"i":{"x":[0.34,0.34,0.34],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":20,"s":[87,85,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":90,"s":[100,90,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0.167,0]},"t":160,"s":[100,90,100]},{"t":180,"s":[100,100,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,-11.871],[9.986,0],[0,7.079],[-9.986,0]],"o":[[0,7.079],[-9.986,0],[0,-11.357],[9.986,0]],"v":[[-46.701,12.524],[-63.783,33.272],[-80.865,12.524],[-63.783,11.875]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.282352954149,0.286274522543,0.286274522543,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,-3.629],[7.358,0],[0,5.828],[-7.358,0]],"o":[[0,5.701],[-7.358,0],[0,-3.63],[7.358,0]],"v":[[-50.461,-12.676],[-63.783,-11.535],[-77.106,-12.676],[-63.783,-23.45]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.960784316063,0.960784316063,0.960784316063,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,-19.516],[13.393,0],[0,19.516],[-13.393,0]],"o":[[0,19.516],[-13.393,0],[0,-19.516],[13.393,0]],"v":[[-39.533,4.77],[-63.783,40.106],[-88.033,4.77],[-63.783,-30.566]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.066666670144,0.066666670144,0.066666670144,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,-19.516],[13.393,0],[0,19.516],[-13.393,0]],"o":[[0,19.516],[-13.393,0],[0,-19.516],[13.393,0]],"v":[[-38.523,9.822],[-63.783,45.158],[-89.044,9.822],[-63.783,-25.514]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.858823537827,0.749019622803,0.592156887054,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false}],"ip":0,"op":180,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"parent":6,"sr":1,"ks":{"r":{"a":1,"k":[{"i":{"x":[0.34],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":20,"s":[0]},{"i":{"x":[0.34],"y":[1]},"o":{"x":[0.66],"y":[0]},"t":160,"s":[0]},{"t":180,"s":[0]}]},"p":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[73.842,7.296,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":20,"s":[73.842,10.454,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0},"t":90,"s":[74.896,8.877,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.34,"y":1},"o":{"x":0.66,"y":0},"t":160,"s":[66.473,7.296,0],"to":[0,0,0],"ti":[0,0,0]},{"t":180,"s":[73.842,7.296,0]}]},"a":{"a":0,"k":[-63.783,7.296,0]},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":0,"s":[-100,100,100]},{"i":{"x":[0.34,0.34,0.34],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":20,"s":[-100,90,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.66,0.66,0.66],"y":[0,0,0]},"t":90,"s":[-100,90,100]},{"i":{"x":[0.34,0.34,0.34],"y":[1,1,1]},"o":{"x":[0.66,0.66,0.66],"y":[0,0,0]},"t":160,"s":[-80,85,100]},{"t":180,"s":[-100,100,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,-11.871],[9.986,0],[0,7.079],[-9.986,0]],"o":[[0,7.079],[-9.986,0],[0,-11.357],[9.986,0]],"v":[[-46.701,12.524],[-63.783,33.272],[-80.865,12.524],[-63.783,11.875]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.282352954149,0.286274522543,0.286274522543,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,-3.629],[7.358,0],[0,5.828],[-7.358,0]],"o":[[0,5.701],[-7.358,0],[0,-3.63],[7.358,0]],"v":[[-50.461,-12.676],[-63.783,-11.535],[-77.106,-12.676],[-63.783,-23.45]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.960784316063,0.960784316063,0.960784316063,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,-19.516],[13.393,0],[0,19.516],[-13.393,0]],"o":[[0,19.516],[-13.393,0],[0,-19.516],[13.393,0]],"v":[[-39.533,4.77],[-63.783,40.106],[-88.033,4.77],[-63.783,-30.566]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.066666670144,0.066666670144,0.066666670144,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,-19.516],[13.393,0],[0,19.516],[-13.393,0]],"o":[[0,19.516],[-13.393,0],[0,-19.516],[13.393,0]],"v":[[-38.523,9.822],[-63.783,45.158],[-89.044,9.822],[-63.783,-25.514]],"c":true}},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.858823537827,0.749019622803,0.592156887054,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false}],"ip":0,"op":180,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"parent":6,"sr":1,"ks":{"r":{"a":1,"k":[{"i":{"x":[0.34],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":20,"s":[5]},{"i":{"x":[0.34],"y":[1]},"o":{"x":[0.66],"y":[0]},"t":160,"s":[-5]},{"t":180,"s":[0]}]},"p":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[5.883,50.25,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[-1.486,70.25,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90,"s":[9.567,66.566,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.34,"y":1},"o":{"x":0.66,"y":0},"t":160,"s":[20.62,64.987,0],"to":[0,0,0],"ti":[0,0,0]},{"t":180,"s":[5.883,50.25,0]}]},"a":{"a":0,"k":[5.883,50.25,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[{"i":[[2.511,-4.918],[0.213,-0.942],[-3.744,-1.911],[2.476,-4.893],[-0.478,0.936],[3.948,2.016]],"o":[[-0.469,0.919],[2.519,-4.85],[3.744,1.911],[0.643,-0.734],[2.511,-4.918],[-3.948,-2.016]],"v":[[13.327,44.758],[12.3,47.57],[24.125,43.208],[25.922,54.567],[27.623,52.056],[25.019,39.507]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[{"i":[[2.606,-4.247],[0.221,-0.814],[-3.885,-1.651],[2.57,-4.225],[-0.496,0.808],[4.097,1.741]],"o":[[-0.487,0.794],[2.614,-4.188],[3.885,1.65],[0.667,-0.634],[2.606,-4.247],[-4.097,-1.741]],"v":[[13.502,45.502],[12.436,47.929],[24.707,44.163],[26.572,53.972],[28.337,51.804],[25.635,40.967]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[2.511,-4.382],[0.213,-0.84],[-3.744,-1.703],[2.476,-4.36],[-0.478,0.834],[3.948,1.796]],"o":[[-0.469,0.819],[2.519,-4.321],[3.744,1.703],[0.643,-0.654],[2.511,-4.382],[-3.948,-1.796]],"v":[[13.327,47.581],[12.3,50.086],[24.125,46.199],[25.922,56.321],[27.623,54.083],[25.019,42.902]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":160,"s":[{"i":[[2.07,-4.382],[0.176,-0.84],[-3.087,-1.703],[2.042,-4.36],[-0.394,0.834],[3.255,1.796]],"o":[[-0.387,0.819],[2.077,-4.321],[3.087,1.703],[0.53,-0.654],[2.07,-4.382],[-3.255,-1.796]],"v":[[15.491,47.581],[14.644,50.086],[24.392,46.199],[25.874,56.321],[27.276,54.083],[25.13,42.902]],"c":true}]},{"t":180,"s":[{"i":[[2.511,-4.918],[0.213,-0.942],[-3.744,-1.911],[2.476,-4.893],[-0.478,0.936],[3.948,2.016]],"o":[[-0.469,0.919],[2.519,-4.85],[3.744,1.911],[0.643,-0.734],[2.511,-4.918],[-3.948,-2.016]],"v":[[13.327,44.758],[12.3,47.57],[24.125,43.208],[25.922,54.567],[27.623,52.056],[25.019,39.507]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.211764708161,0.086274512112,0.035294119269,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[{"i":[[2.509,-4.914],[-3.948,-2.015],[-2.509,4.914],[3.948,2.015]],"o":[[-2.509,4.914],[3.948,2.015],[2.509,-4.914],[-3.948,-2.015]],"v":[[13.326,44.758],[15.931,57.305],[27.621,52.056],[25.016,39.509]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[{"i":[[2.604,-4.244],[-4.096,-1.74],[-2.604,4.244],[4.096,1.74]],"o":[[-2.604,4.244],[4.096,1.74],[2.604,-4.244],[-4.096,-1.74]],"v":[[13.501,45.501],[16.204,56.336],[28.336,51.803],[25.632,40.968]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[2.509,-4.379],[-3.948,-1.796],[-2.509,4.379],[3.948,1.796]],"o":[[-2.509,4.379],[3.948,1.796],[2.509,-4.379],[-3.948,-1.796]],"v":[[13.326,47.58],[15.931,58.76],[27.621,54.083],[25.016,42.903]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":160,"s":[{"i":[[2.068,-4.379],[-3.254,-1.796],[-2.068,4.379],[3.254,1.796]],"o":[[-2.068,4.379],[3.254,1.796],[2.068,-4.379],[-3.254,-1.796]],"v":[[15.49,47.58],[17.637,58.76],[27.275,54.083],[25.127,42.903]],"c":true}]},{"t":180,"s":[{"i":[[2.509,-4.914],[-3.948,-2.015],[-2.509,4.914],[3.948,2.015]],"o":[[-2.509,4.914],[3.948,2.015],[2.509,-4.914],[-3.948,-2.015]],"v":[[13.326,44.758],[15.931,57.305],[27.621,52.056],[25.016,39.509]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921574354,0.266666680574,0.188235297799,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[{"i":[[3.93,-7.699],[-3.948,-2.015],[-2.812,4.747],[3.948,2.015]],"o":[[-2.509,4.914],[3.948,2.015],[4.225,-7.134],[-3.948,-2.015]],"v":[[12.111,46.986],[15.567,59.968],[27.169,55.043],[25.661,40.199]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[{"i":[[4.079,-6.648],[-4.096,-1.74],[-2.918,4.1],[4.096,1.74]],"o":[[-2.604,4.244],[4.096,1.74],[4.385,-6.161],[-4.096,-1.74]],"v":[[12.24,47.426],[15.826,58.636],[27.866,54.383],[26.301,41.565]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[3.93,-6.86],[-3.948,-1.796],[-2.812,4.23],[3.948,1.796]],"o":[[-2.509,4.379],[3.948,1.796],[4.225,-6.357],[-3.948,-1.796]],"v":[[12.111,49.566],[15.567,61.133],[27.169,56.744],[25.661,43.518]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":160,"s":[{"i":[[3.24,-6.86],[-3.254,-1.796],[-2.318,4.23],[3.254,1.796]],"o":[[-2.068,4.379],[3.254,1.796],[3.483,-6.357],[-3.254,-1.796]],"v":[[14.488,49.566],[17.337,61.133],[26.902,56.744],[25.659,43.518]],"c":true}]},{"t":180,"s":[{"i":[[3.93,-7.699],[-3.948,-2.015],[-2.812,4.747],[3.948,2.015]],"o":[[-2.509,4.914],[3.948,2.015],[4.225,-7.134],[-3.948,-2.015]],"v":[[12.111,46.986],[15.567,59.968],[27.169,55.043],[25.661,40.199]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.941176474094,0.847058832645,0.705882370472,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[{"i":[[-2.511,-4.918],[-0.213,-0.942],[3.744,-1.911],[-2.476,-4.893],[0.478,0.936],[-3.948,2.016]],"o":[[0.469,0.919],[-2.519,-4.85],[-3.744,1.911],[-0.643,-0.734],[-2.511,-4.918],[3.948,-2.016]],"v":[[-1.485,44.758],[-0.457,47.57],[-12.282,43.208],[-14.08,54.567],[-15.78,52.056],[-13.177,39.507]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[{"i":[[-2.067,-4.247],[-0.175,-0.814],[3.081,-1.651],[-2.038,-4.225],[0.393,0.808],[-3.25,1.741]],"o":[[0.386,0.794],[-2.073,-4.188],[-3.081,1.65],[-0.529,-0.634],[-2.067,-4.247],[3.25,-1.741]],"v":[[-4.486,46.436],[-3.64,48.863],[-13.373,45.097],[-14.852,54.906],[-16.251,52.738],[-14.108,41.901]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[-2.511,-4.382],[-0.213,-0.84],[3.744,-1.703],[-2.476,-4.36],[0.478,0.834],[-3.948,1.796]],"o":[[0.469,0.819],[-2.519,-4.321],[-3.744,1.703],[-0.643,-0.654],[-2.511,-4.382],[3.948,-1.796]],"v":[[-1.485,47.581],[-0.457,50.086],[-12.282,46.199],[-14.08,56.321],[-15.78,54.083],[-13.177,42.902]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":160,"s":[{"i":[[-2.511,-4.382],[-0.213,-0.84],[3.744,-1.703],[-2.476,-4.36],[0.478,0.834],[-3.948,1.796]],"o":[[0.469,0.819],[-2.519,-4.321],[-3.744,1.703],[-0.643,-0.654],[-2.511,-4.382],[3.948,-1.796]],"v":[[-1.485,47.581],[-0.457,50.086],[-12.282,46.199],[-14.08,56.321],[-15.78,54.083],[-13.177,42.902]],"c":true}]},{"t":180,"s":[{"i":[[-2.511,-4.918],[-0.213,-0.942],[3.744,-1.911],[-2.476,-4.893],[0.478,0.936],[-3.948,2.016]],"o":[[0.469,0.919],[-2.519,-4.85],[-3.744,1.911],[-0.643,-0.734],[-2.511,-4.918],[3.948,-2.016]],"v":[[-1.485,44.758],[-0.457,47.57],[-12.282,43.208],[-14.08,54.567],[-15.78,52.056],[-13.177,39.507]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.211764708161,0.086274512112,0.035294119269,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[{"i":[[-2.509,-4.914],[3.948,-2.015],[2.509,4.914],[-3.948,2.015]],"o":[[2.509,4.914],[-3.948,2.015],[-2.509,-4.914],[3.948,-2.015]],"v":[[-1.484,44.758],[-4.089,57.305],[-15.779,52.056],[-13.174,39.509]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[{"i":[[-2.065,-4.244],[3.249,-1.74],[2.065,4.244],[-3.249,1.74]],"o":[[2.065,4.244],[-3.249,1.74],[-2.065,-4.244],[3.249,-1.74]],"v":[[-4.485,46.435],[-6.629,57.27],[-16.25,52.737],[-14.106,41.902]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[-2.509,-4.379],[3.948,-1.796],[2.509,4.379],[-3.948,1.796]],"o":[[2.509,4.379],[-3.948,1.796],[-2.509,-4.379],[3.948,-1.796]],"v":[[-1.484,47.58],[-4.089,58.76],[-15.779,54.083],[-13.174,42.903]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":160,"s":[{"i":[[-2.509,-4.379],[3.948,-1.796],[2.509,4.379],[-3.948,1.796]],"o":[[2.509,4.379],[-3.948,1.796],[-2.509,-4.379],[3.948,-1.796]],"v":[[-1.484,47.58],[-4.089,58.76],[-15.779,54.083],[-13.174,42.903]],"c":true}]},{"t":180,"s":[{"i":[[-2.509,-4.914],[3.948,-2.015],[2.509,4.914],[-3.948,2.015]],"o":[[2.509,4.914],[-3.948,2.015],[-2.509,-4.914],[3.948,-2.015]],"v":[[-1.484,44.758],[-4.089,57.305],[-15.779,52.056],[-13.174,39.509]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921574354,0.266666680574,0.188235297799,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[{"i":[[-3.93,-7.699],[3.948,-2.015],[2.812,4.747],[-3.948,2.015]],"o":[[2.509,4.914],[-3.948,2.015],[-4.225,-7.134],[3.948,-2.015]],"v":[[-0.269,46.986],[-3.725,59.968],[-15.327,55.043],[-13.818,40.199]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[{"i":[[-3.235,-6.648],[3.249,-1.74],[2.314,4.1],[-3.249,1.74]],"o":[[2.065,4.244],[-3.249,1.74],[-3.478,-6.161],[3.249,-1.74]],"v":[[-3.485,48.36],[-6.329,59.569],[-15.878,55.317],[-14.637,42.499]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[-3.93,-6.86],[3.948,-1.796],[2.812,4.23],[-3.948,1.796]],"o":[[2.509,4.379],[-3.948,1.796],[-4.225,-6.357],[3.948,-1.796]],"v":[[-0.269,49.566],[-3.725,61.133],[-15.327,56.744],[-13.818,43.518]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":160,"s":[{"i":[[-3.93,-6.86],[3.948,-1.796],[2.812,4.23],[-3.948,1.796]],"o":[[2.509,4.379],[-3.948,1.796],[-4.225,-6.357],[3.948,-1.796]],"v":[[-0.269,49.566],[-3.725,61.133],[-15.327,56.744],[-13.818,43.518]],"c":true}]},{"t":180,"s":[{"i":[[-3.93,-7.699],[3.948,-2.015],[2.812,4.747],[-3.948,2.015]],"o":[[2.509,4.914],[-3.948,2.015],[-4.225,-7.134],[3.948,-2.015]],"v":[[-0.269,46.986],[-3.725,59.968],[-15.327,55.043],[-13.818,40.199]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.941176474094,0.847058832645,0.705882370472,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[{"i":[[11.287,0.292],[3.048,-1.351],[3.153,1.398],[3.628,-0.094],[-0.105,-10.467],[-29.829,0],[-0.445,8.16]],"o":[[-3.628,-0.094],[-3.153,1.398],[-3.048,-1.351],[-11.287,0.292],[0.079,7.902],[29.532,0],[0.569,-10.452]],"v":[[23.934,29.849],[11.849,32.814],[-0.121,32.814],[-12.206,29.849],[-28.625,47.042],[5.864,70.656],[40.353,47.042]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[{"i":[[11.712,0.252],[3.163,-1.167],[2.595,1.207],[2.986,-0.081],[-0.086,-9.038],[-29.829,0],[-0.461,7.046]],"o":[[-3.765,-0.081],[-3.271,1.207],[-2.509,-1.167],[-9.29,0.252],[0.065,6.824],[29.532,0],[0.591,-9.025]],"v":[[24.509,32.627],[11.968,35.188],[-3.302,35.551],[-13.249,32.991],[-26.763,47.838],[5.864,67.865],[41.548,47.474]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[11.287,0.26],[3.048,-1.204],[3.153,1.245],[3.628,-0.084],[-0.105,-9.326],[-29.829,0],[-0.445,7.27]],"o":[[-3.628,-0.084],[-3.153,1.245],[-3.048,-1.204],[-11.287,0.26],[0.079,7.041],[29.532,0],[0.569,-9.313]],"v":[[23.934,34.296],[11.849,36.938],[-0.121,36.938],[-12.206,34.296],[-28.625,49.616],[5.864,70.656],[40.353,49.616]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":160,"s":[{"i":[[10.052,0.26],[2.715,-1.204],[3.153,1.245],[3.628,-0.084],[-0.105,-9.326],[-29.824,-0.55],[-0.396,7.27]],"o":[[-3.231,-0.084],[-2.808,1.245],[-3.048,-1.204],[-11.287,0.26],[0.079,7.041],[23.725,0.437],[0.507,-9.313]],"v":[[22.268,34.296],[11.505,36.938],[-0.121,36.938],[-12.206,34.296],[-28.625,49.616],[5.864,70.656],[36.891,49.616]],"c":true}]},{"t":180,"s":[{"i":[[11.287,0.292],[3.048,-1.351],[3.153,1.398],[3.628,-0.094],[-0.105,-10.467],[-29.829,0],[-0.445,8.16]],"o":[[-3.628,-0.094],[-3.153,1.398],[-3.048,-1.351],[-11.287,0.292],[0.079,7.902],[29.532,0],[0.569,-10.452]],"v":[[23.934,29.849],[11.849,32.814],[-0.121,32.814],[-12.206,29.849],[-28.625,47.042],[5.864,70.656],[40.353,47.042]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.858823537827,0.749019622803,0.592156887054,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false}],"ip":0,"op":180,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":4,"parent":6,"sr":1,"ks":{"p":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[5.864,107.189,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[5.864,118.768,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90,"s":[12.709,118.768,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":121,"s":[17.152,118.768,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.66,"y":0},"t":160,"s":[16.39,118.768,0],"to":[0,0,0],"ti":[0,0,0]},{"t":180,"s":[5.864,107.189,0]}]},"a":{"a":0,"k":[5.864,107.189,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[1.188,-2.439],[0.921,0.417],[6.452,0.59],[6.878,0],[2.613,-0.213],[1.434,-0.652],[-0.372,-1.404],[-1.391,0.611],[-6.645,0.566],[-7.086,0],[-2.685,-0.256],[-1.475,-0.645]],"o":[[0.378,-1.404],[-1.435,-0.65],[-2.605,-0.238],[-6.884,0],[-6.452,0.525],[-0.92,0.418],[-1.188,-2.439],[1.479,-0.65],[2.688,-0.229],[7.086,0],[6.639,0.632],[1.396,0.611]],"v":[[34.087,94.056],[33.413,91.171],[21.553,89.807],[5.926,89.257],[-9.991,89.703],[-21.919,91.171],[-22.598,94.051],[-22.751,89.003],[-10.911,87.679],[5.926,87.083],[22.498,87.794],[34.24,89.003]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[{"i":[[1.017,-0.984],[0.74,0.137],[5.016,-1.003],[5.593,0.145],[2.029,0.395],[1.204,-0.161],[-0.212,-0.564],[-1.167,0.148],[-5.167,-0.995],[-5.762,-0.149],[-2.091,0.414],[-1.186,-0.211]],"o":[[0.337,-0.558],[-1.153,-0.214],[-2.025,0.405],[-5.598,-0.145],[-5.01,-0.975],[-0.773,0.104],[-0.808,-1.014],[1.241,-0.158],[2.09,0.403],[5.762,0.149],[5.17,-1.023],[1.122,0.2]],"v":[[28.803,92.74],[28.316,91.634],[19.599,92.311],[6.09,93.951],[-7.27,92.32],[-16.157,90.934],[-16.891,92.001],[-16.693,90.047],[-7.328,91.453],[6.186,93.108],[19.666,91.443],[29.034,90.766]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[1.256,-2.534],[1.014,0.402],[6.927,-1.686],[8.899,0.328],[3.31,0.598],[1.887,-0.603],[-0.414,-1.463],[-1.829,0.563],[-8.426,-1.496],[-9.169,-0.338],[-2.887,0.692],[-1.624,-0.62]],"o":[[0.388,-1.45],[-1.58,-0.626],[-2.797,0.681],[-8.906,-0.328],[-8.173,-1.477],[-1.211,0.387],[-1.42,-2.568],[1.944,-0.599],[3.408,0.605],[9.169,0.338],[7.137,-1.711],[1.538,0.587]],"v":[[37.651,91.831],[36.864,88.893],[24.871,90.643],[5.114,92.575],[-16.163,90.365],[-30.599,88.025],[-31.617,90.958],[-31.571,85.752],[-16.566,88.1],[5.219,90.335],[25.141,88.354],[37.729,86.646]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.66,"y":0},"t":160,"s":[{"i":[[0.95,-2.81],[1.167,0.24],[7.456,-2.218],[10.941,-0.562],[4.156,0.133],[2.245,-0.811],[-0.67,-1.462],[-2.179,0.764],[-10.576,-0.313],[-11.273,0.579],[-3.108,0.914],[-1.865,-0.361]],"o":[[0.184,-1.552],[-1.819,-0.374],[-3.01,0.896],[-10.95,0.563],[-10.263,-0.329],[-1.44,0.52],[-2.025,-2.496],[2.316,-0.812],[4.278,0.126],[11.273,-0.58],[7.683,-2.259],[1.766,0.342]],"v":[[42.841,84.703],[41.504,81.824],[28.447,86.054],[6.063,90.174],[-20.024,90.406],[-38.1,89.492],[-39.02,92.61],[-39.544,87.254],[-20.919,88.139],[5.943,87.862],[28.625,83.676],[42.077,79.374]],"c":true}]},{"t":180,"s":[{"i":[[2.193,-4.476],[1.697,0.768],[11.863,-1.475],[12.691,0],[4.8,0.596],[2.647,-1.192],[-0.687,-2.577],[-2.567,1.122],[-12.216,-1.475],[-13.075,0],[-4.941,0.596],[-2.718,-1.192]],"o":[[0.697,-2.577],[-2.647,-1.192],[-4.789,0.596],[-12.701,0],[-11.852,-1.475],[-1.697,0.768],[-2.193,-4.476],[2.728,-1.192],[4.941,0.596],[13.075,0],[12.216,-1.475],[2.576,1.122]],"v":[[58.155,95.404],[56.912,90.109],[36.046,93.282],[5.865,95.192],[-24.327,93.282],[-45.182,90.109],[-46.435,95.394],[-46.718,86.128],[-25.226,89.301],[5.865,91.201],[36.956,89.301],[58.437,86.128]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.015686275437,0,0.003921568859,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[0.676,0],[6.173,0],[4.992,3.171],[-1.401,0],[0,0],[-6.229,-1.128]],"o":[[-4.927,3.051],[-6.347,0],[-0.236,0],[6.404,0],[0,0],[1.339,0.243]],"v":[[23.022,105.559],[5.744,111.007],[-11.941,105.116],[-8.151,106.691],[5.555,107.59],[19.484,106.941]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[{"i":[[1.451,-0.113],[5.02,0.13],[3.846,1.534],[-1.083,-0.29],[-3.642,0.125],[-3.577,1.294]],"o":[[-4.069,1.325],[-5.161,-0.134],[-0.191,-0.015],[5.789,1.549],[5.19,-0.178],[-0.502,0.234]],"v":[[20.57,96.753],[5.352,99.132],[-8.187,96.863],[-4.341,97.826],[7.524,99.085],[25.156,95.088]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[1.436,-1.581],[7.987,0.294],[6.305,3.503],[-3.242,0.796],[-6.061,0.252],[-5.656,-2.505]],"o":[[-7.476,4.197],[-8.211,-0.302],[-6.499,-3.529],[9.718,-2.101],[9.706,-0.35],[2.272,0.633]],"v":[[27.076,102.043],[1.752,108.056],[-18.578,102.846],[-19.762,98.709],[-1.653,95.626],[23.04,97.634]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.66,"y":0},"t":160,"s":[{"i":[[0.932,-3.228],[9.82,-0.505],[8.116,2.964],[-5.032,1.599],[-6.076,0.443],[-9.806,-0.981]],"o":[[-9.452,6.314],[-10.096,0.519],[-13.083,-6.421],[9.354,-2.973],[12.987,-0.946],[5.231,0.523]],"v":[[33.069,97.061],[1.721,108.089],[-22.824,103.774],[-30.308,95.764],[-12.589,91.995],[18.845,92.212]],"c":true}]},{"t":180,"s":[{"i":[[1.87,-5.25],[11.39,0],[9.21,5.82],[-5.92,2.3],[0,0],[-10.91,-4.15]],"o":[[-9.09,5.6],[-11.71,0],[-1.77,-5.25],[11,-4.29],[0,0],[5.82,2.21]],"v":[[37.739,116.517],[5.859,126.517],[-26.771,116.047],[-19.781,101.617],[5.509,103.267],[31.209,102.077]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.274509817362,0.376470595598,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[2,0.88],[6.637,0.633],[7.087,0],[2.687,-0.229],[1.475,-0.649],[-2.5,-3.795],[-11.418,0],[-3.544,5.379]],"o":[[-1.475,-0.649],[-2.684,-0.256],[-7.087,0],[-6.643,0.567],[-2,0.88],[3.544,5.379],[11.418,0],[2.5,-3.795]],"v":[[34.242,89.001],[22.499,87.793],[5.925,87.084],[-10.913,87.679],[-22.749,89.001],[-20.634,97.431],[5.746,111.004],[32.127,97.431]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[{"i":[[1.607,0.288],[5.168,-1.022],[5.763,0.149],[2.089,0.402],[1.238,-0.158],[-1.785,-1.619],[-9.285,-0.241],[-2.994,2.195]],"o":[[-1.186,-0.213],[-2.09,0.414],[-5.763,-0.149],[-5.166,-0.995],[-1.678,0.214],[2.53,2.294],[9.285,0.241],[2.112,-1.548]],"v":[[29.035,90.765],[19.666,91.443],[6.185,93.108],[-7.329,91.453],[-16.691,90.046],[-15.514,93.426],[5.354,99.131],[27.139,94.11]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[2.202,0.846],[7.134,-1.709],[9.17,0.338],[3.407,0.605],[1.94,-0.598],[-3.052,-4.028],[-14.773,-0.544],[-3.779,5.615]],"o":[[-1.625,-0.624],[-2.885,0.691],[-9.17,-0.338],[-8.424,-1.495],[-2.63,0.811],[4.325,5.708],[14.773,0.544],[2.666,-3.961]],"v":[[37.731,86.644],[25.142,88.353],[5.218,90.337],[-16.568,88.099],[-31.567,85.751],[-29.238,94.533],[1.755,108.053],[35.568,95.346]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.66,"y":0},"t":160,"s":[{"i":[[2.531,0.494],[7.681,-2.256],[11.274,-0.58],[4.276,0.126],[2.311,-0.811],[-4.187,-3.831],[-18.163,0.934],[-3.187,6.396]],"o":[[-1.867,-0.365],[-3.106,0.913],[-11.274,0.58],[-10.573,-0.312],[-3.133,1.099],[5.935,5.43],[18.163,-0.934],[2.249,-4.513]],"v":[[42.079,79.372],[28.625,83.675],[5.942,87.864],[-20.922,88.139],[-39.54,87.252],[-35.708,96.043],[1.725,108.086],[41.153,88.66]],"c":true}]},{"t":180,"s":[{"i":[[3.69,1.615],[12.212,-1.472],[13.077,0],[4.939,0.595],[2.722,-1.191],[-4.613,-6.966],[-21.068,0],[-6.539,9.873]],"o":[[-2.722,-1.191],[-4.939,0.595],[-13.077,0],[-12.212,-1.472],[-3.69,1.615],[6.539,9.873],[21.068,0],[4.614,-6.966]],"v":[[58.441,86.126],[36.956,89.3],[5.864,91.203],[-25.229,89.3],[-46.713,86.126],[-42.811,101.598],[5.863,126.512],[54.538,101.598]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.247058823705,0.039215687662,0.141176477075,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[3.188,0.413],[6.882,0.687],[7.357,0],[2.786,-0.25],[1.597,-0.464],[-2.595,-3.795],[-11.852,0],[-3.679,5.379]],"o":[[-1.648,-0.214],[-2.783,-0.278],[-7.357,0],[-6.889,0.618],[-3.048,0.885],[3.678,5.379],[11.852,0],[2.595,-3.795]],"v":[[34.242,89.001],[22.653,90.081],[5.925,89.286],[-11.089,89.965],[-22.749,89.001],[-21.823,100.948],[5.733,115.682],[33.554,101.21]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[{"i":[[2.583,0.074],[5.369,-1.05],[5.982,0.155],[2.17,0.413],[1.325,-0.079],[-2.386,-3.93],[-7.235,-0.257],[-3.03,3.731]],"o":[[-1.336,-0.038],[-2.171,0.425],[-5.982,-0.155],[-5.367,-1.021],[-2.529,0.15],[3.381,5.571],[7.377,0.262],[2.898,-3.569]],"v":[[29.035,90.765],[20.509,92.2],[6.088,93.963],[-8.177,92.214],[-16.691,90.046],[-17.383,94.585],[6.044,102.764],[28.688,96.663]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[3.494,0.335],[7.409,-1.743],[9.518,0.351],[3.538,0.615],[2.088,-0.401],[-3.174,-4.032],[-15.334,-0.565],[-3.927,5.618]],"o":[[-1.806,-0.173],[-2.996,0.705],[-9.518,-0.351],[-8.747,-1.521],[-3.987,0.766],[4.499,5.715],[15.334,0.565],[2.77,-3.964]],"v":[[37.731,86.644],[26.114,90.524],[5.112,92.605],[-17.617,90.283],[-31.567,85.751],[-30.51,99.394],[1.222,115.316],[36.399,100.422]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.66,"y":0},"t":160,"s":[{"i":[[3.85,-0.249],[7.978,-2.31],[11.702,-0.602],[4.438,0.118],[2.514,-0.624],[-4.338,-3.824],[-18.853,0.969],[-3.346,6.425]],"o":[[-1.99,0.129],[-3.227,0.934],[-11.702,0.602],[-10.973,-0.291],[-4.8,1.191],[6.149,5.419],[18.853,-0.969],[2.361,-4.533]],"v":[[42.079,79.372],[29.794,85.846],[6.064,90.205],[-21.818,90.533],[-39.54,87.252],[-36.41,102.429],[1.776,115.02],[42.073,94.869]],"c":true}]},{"t":180,"s":[{"i":[[5.883,0.759],[12.676,-1.472],[13.573,0],[5.127,0.595],[2.946,-0.851],[-4.788,-6.966],[-21.868,0],[-6.787,9.873]],"o":[[-3.042,-0.392],[-5.126,0.595],[-13.574,0],[-12.676,-1.472],[-5.624,1.625],[6.787,9.873],[21.868,0],[4.789,-6.966]],"v":[[58.441,86.126],[38.137,93.341],[5.864,95.245],[-26.41,93.341],[-46.713,86.126],[-43.649,106.65],[5.863,132.575],[55.376,106.65]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.956862747669,0.913725495338,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[2,0.88],[6.637,0.633],[7.087,0],[2.687,-0.229],[1.475,-0.649],[-2.5,-3.795],[-11.418,0],[-3.544,5.379]],"o":[[-1.475,-0.649],[-2.684,-0.256],[-7.087,0],[-6.643,0.567],[-2,0.88],[3.544,5.379],[11.418,0],[2.5,-3.795]],"v":[[35.881,85.862],[23.167,84.726],[5.922,83.945],[-11.558,84.606],[-24.395,85.862],[-22.277,95.229],[5.746,108.802],[33.769,95.229]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[{"i":[[1.949,0.987],[6.41,-1.835],[7.077,0.384],[2.587,0.759],[1.508,-0.568],[-1.785,-1.619],[-9.285,-0.241],[-2.994,2.195]],"o":[[-1.438,-0.728],[-2.592,0.742],[-7.077,-0.384],[-6.398,-1.876],[-2.045,0.77],[2.53,2.294],[9.285,0.241],[2.112,-1.548]],"v":[[30.955,88.569],[19.615,89.611],[6.244,91.503],[-4.363,90.244],[-17.917,88.065],[-16.706,92.475],[5.451,98.277],[28.521,93.206]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[2.202,0.846],[7.134,-1.709],[9.17,0.338],[3.407,0.605],[1.94,-0.598],[-3.052,-4.028],[-14.773,-0.544],[-3.779,5.615]],"o":[[-1.625,-0.624],[-2.885,0.691],[-9.17,-0.338],[-8.424,-1.495],[-2.63,0.811],[4.325,5.708],[14.773,0.544],[2.666,-3.961]],"v":[[39.534,83.874],[24.815,85.631],[5.37,87.603],[-16.245,85.392],[-33.541,82.938],[-31.257,92.187],[1.861,105.786],[37.325,93.043]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.66,"y":0},"t":160,"s":[{"i":[[2.531,0.494],[7.681,-2.256],[11.274,-0.58],[4.276,0.126],[2.311,-0.811],[-4.187,-3.831],[-18.163,0.934],[-3.187,6.396]],"o":[[-1.867,-0.365],[-3.106,0.913],[-11.274,0.58],[-10.573,-0.312],[-3.133,1.099],[5.935,5.43],[18.163,-0.934],[2.249,-4.513]],"v":[[43.611,76.712],[28.213,81.367],[5.82,85.522],[-21.044,85.797],[-42.275,85.045],[-38.444,93.836],[1.603,105.744],[42.685,86]],"c":true}]},{"t":180,"s":[{"i":[[3.69,1.615],[12.212,-1.472],[13.077,0],[4.939,0.595],[2.722,-1.191],[-4.613,-6.966],[-21.068,0],[-6.539,9.873]],"o":[[-2.722,-1.191],[-4.939,0.595],[-13.077,0],[-12.212,-1.472],[-3.69,1.615],[6.539,9.873],[21.068,0],[4.614,-6.966]],"v":[[61.472,82.084],[36.956,85.258],[5.864,87.162],[-25.229,85.258],[-49.744,82.084],[-45.842,97.556],[5.863,122.47],[57.569,97.556]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.858823537827,0.749019622803,0.592156887054,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false}],"ip":0,"op":180,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":4,"parent":5,"sr":1,"ks":{"p":{"a":0,"k":[5.303,144.067,0]},"a":{"a":0,"k":[5.303,144.067,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[0,0],[-10.992,-6.96],[0,0],[-16.976,11.779],[-18.657,4.78]],"o":[[17.211,5.126],[18.61,11.782],[0,0],[10.182,-7.065],[0,0]],"v":[[-70.986,150.908],[-27.847,165.744],[5.303,176.601],[37.654,165.544],[81.313,150.908]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[{"i":[[0,0],[-11.59,-7.414],[0,0],[-14.698,7.139],[-18.287,2.781]],"o":[[11.917,5.276],[19.623,12.552],[0,0],[7.38,-3.585],[0,0]],"v":[[-89.369,144.471],[-53.673,162.62],[-18.121,174.858],[16.833,167.523],[52.16,156.423]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[0,0],[-11.217,-6.89],[-6.694,0.755],[-13.943,10.142],[-18.221,7.812]],"o":[[14.564,5.201],[18.99,11.665],[4.881,-0.55],[7.645,-5.735],[0,0]],"v":[[-58.434,154.392],[-25.997,166.584],[11.457,177.72],[43.851,165.709],[81.342,150.935]],"c":false}]},{"i":{"x":0.34,"y":1},"o":{"x":0.66,"y":0},"t":160,"s":[{"i":[[0,0],[-11.922,-2.112],[-12.765,1.265],[-13.733,9.717],[-18.156,12.771]],"o":[[17.174,5.127],[20.184,3.575],[9.308,-0.922],[8.229,-5.822],[0,0]],"v":[[-39.353,157.947],[-4.021,162.991],[35.085,170.353],[69.022,158.414],[107.404,133.141]],"c":false}]},{"t":180,"s":[{"i":[[0,0],[-10.992,-6.96],[0,0],[-16.976,11.779],[-18.657,4.78]],"o":[[17.211,5.126],[18.61,11.782],[0,0],[10.182,-7.065],[0,0]],"v":[[-70.986,150.908],[-27.847,165.744],[5.303,176.601],[37.654,165.544],[81.313,150.908]],"c":false}]}]},"hd":false},{"ty":"st","c":{"a":0,"k":[0.698039233685,0.584313750267,0.447058826685,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":2,"lj":2,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-24.85,4.01],[2.81,-4.07],[4.9,-0.67],[10.74,-7.45],[0,0],[16.98,11.78],[7.38,1.01],[3.407,3.585],[1.57,5.1],[-14.42,-8.27],[-8.4,5.66]],"o":[[-1.671,6.6],[-2.81,4.07],[-7.38,1.01],[-16.97,11.78],[0,0],[-10.73,-7.45],[-4.9,-0.67],[-3.841,-4.041],[12.31,3.41],[17.93,10.29],[7.89,-5.32]],"v":[[91.856,131.627],[83.799,149.584],[68.549,154.317],[37.649,165.547],[5.299,176.597],[-27.051,165.547],[-57.941,154.317],[-72.457,150.111],[-81.201,131.67],[-17.101,154.037],[28.949,154.037]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[{"i":[[-24.85,4.01],[2.81,-4.07],[4.9,-0.67],[7.289,-3.787],[0,0],[17.838,12.486],[4.657,1.734],[3.407,3.585],[1.57,5.1],[-17.85,-8.275],[-8.4,5.66]],"o":[[-1.671,6.6],[-2.81,4.07],[-7.38,1.01],[-14.468,7.517],[0,0],[-11.275,-7.893],[-4.634,-1.727],[-3.841,-4.041],[11.925,5.234],[22.198,10.29],[7.89,-5.32]],"v":[[61.653,139.769],[52.808,154.837],[50.165,158.256],[16.829,167.525],[-18.125,174.854],[-52.792,162.456],[-83.678,147.618],[-87.426,144.856],[-98.469,124.185],[-47.396,153.643],[4.814,154.431]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[-21.116,6.554],[2.81,-4.07],[4.354,-1.912],[7.815,-6.052],[0,0],[17.273,11.577],[6.018,1.372],[3.407,3.585],[1.57,5.1],[-16.234,-6.813],[-10.473,7.85]],"o":[[-1.671,6.6],[-2.81,4.07],[-7.812,3.919],[-13.825,10.331],[0,0],[-10.918,-7.318],[-4.767,-1.198],[-3.841,-4.041],[11.925,5.236],[26.755,10.877],[8.681,-6.45]],"v":[[87.149,133.888],[81.988,149.348],[77.23,153.675],[43.847,165.712],[11.453,177.717],[-25.152,166.45],[-51.171,156.947],[-56.487,154.779],[-65.723,137.499],[-13.565,157.023],[45.039,152.058]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.66,"y":0},"t":160,"s":[{"i":[[-17.436,9.061],[2.81,-4.07],[3.816,-3.137],[8.675,-6.139],[0,0],[18.575,3.951],[7.36,1.015],[3.407,3.585],[1.57,5.1],[-14.642,-4.542],[-12.516,8.448]],"o":[[-1.671,6.6],[-2.81,4.07],[-8.237,6.787],[-13.729,9.716],[0,0],[-11.741,-2.497],[-4.898,-0.678],[-3.841,-4.041],[11.925,5.238],[31.247,9.675],[9.461,-6.386]],"v":[[113.319,112.787],[106.6,132.525],[99.759,137.291],[69.018,158.417],[35.082,170.351],[-3.268,162.678],[-17.572,159.917],[-37.403,158.334],[-46.934,141.285],[14.784,153.302],[72.427,142.515]],"c":true}]},{"t":180,"s":[{"i":[[-24.85,4.01],[2.81,-4.07],[4.9,-0.67],[10.74,-7.45],[0,0],[16.98,11.78],[7.38,1.01],[3.407,3.585],[1.57,5.1],[-14.42,-8.27],[-8.4,5.66]],"o":[[-1.671,6.6],[-2.81,4.07],[-7.38,1.01],[-16.97,11.78],[0,0],[-10.73,-7.45],[-4.9,-0.67],[-3.841,-4.041],[12.31,3.41],[17.93,10.29],[7.89,-5.32]],"v":[[91.856,131.627],[83.799,149.584],[68.549,154.317],[37.649,165.547],[5.299,176.597],[-27.051,165.547],[-57.941,154.317],[-72.457,150.111],[-81.201,131.67],[-17.101,154.037],[28.949,154.037]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.61960786581,0.46274510026,0.239215686917,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":11},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[51.445,12.472],[11.245,-13.381],[2.922,3.477],[38.8,-9.406],[-5.196,-23.384],[-28.607,-10.289],[0.785,-3.583],[-16.2,-23.457],[-4.902,-0.674],[-10.738,-7.451],[0,0],[-16.975,11.779],[-7.382,1.015],[-2.812,4.072],[3.47,15.845],[-3.452,1.242],[-4.853,21.841]],"o":[[-38.801,-9.406],[-2.922,3.477],[-11.245,-13.381],[-51.445,12.472],[4.854,21.841],[3.452,1.242],[-3.47,15.845],[2.812,4.072],[7.382,1.015],[16.975,11.779],[0,0],[10.737,-7.451],[4.902,-0.674],[16.2,-23.457],[-0.785,-3.583],[28.607,-10.289],[5.197,-23.384]],"v":[[88.579,-75.949],[10.891,-45.909],[-0.285,-45.909],[-77.973,-75.949],[-132.017,4.597],[-80.556,57.662],[-75.875,66.071],[-70.075,146.877],[-57.943,154.316],[-27.048,165.544],[5.303,176.601],[37.654,165.544],[68.548,153.316],[80.68,146.877],[86.481,66.071],[91.162,57.662],[142.623,4.597]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[{"i":[[56.478,10.772],[11.245,-11.558],[2.922,3.003],[29.193,-5.044],[-1.651,-23.642],[-28.607,-10.289],[0.785,-3.583],[-11.514,-17.678],[-4.902,-0.674],[-11.284,-7.894],[0,0],[-14.528,7.426],[-7.382,1.015],[-2.119,4.472],[-9.613,26.277],[-3.578,0.809],[-7.366,31.728]],"o":[[-42.596,-8.125],[-2.922,3.003],[-11.245,-11.558],[-27.428,4.739],[1.542,22.083],[3.452,1.241],[-3.47,15.845],[2.701,4.147],[7.382,1.015],[17.838,12.479],[0,0],[6.521,-3.333],[4.902,-0.674],[10.121,-21.355],[1.258,-3.445],[30.413,-6.879],[7.887,-33.97]],"v":[[68.29,-21.352],[-24.303,10.893],[-35.478,10.893],[-102.046,-23.16],[-135.705,34.244],[-100.516,88.128],[-95.835,96.537],[-85.044,141.622],[-82.498,147.092],[-52.788,162.453],[-18.121,174.858],[16.833,167.523],[50.164,157.256],[49.689,152.129],[53.914,97.85],[58.595,89.441],[122.669,44.587]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[42.499,7.368],[11.245,-11.798],[2.922,3.066],[36.255,-6.612],[-3.555,-21.167],[-28.607,-10.289],[0.785,-3.583],[-11.509,-17.672],[-4.902,-0.674],[-10.926,-7.319],[0,0],[-13.858,10.286],[-7.848,3.264],[-1.715,4.622],[-3.071,21.061],[-3.515,1.025],[-4.608,26.082]],"o":[[-32.053,-5.557],[-2.922,3.066],[-11.245,-11.798],[-46.231,8.456],[3.321,19.771],[3.452,1.242],[-3.47,15.845],[2.7,4.147],[7.382,1.015],[17.273,11.571],[0,0],[7.431,-5.824],[4.513,-1.704],[7.352,-19.017],[0.237,-3.514],[29.51,-8.584],[4.606,-23.978]],"v":[[92.829,-22],[16.978,7.294],[5.803,7.294],[-79.789,-24.136],[-125.22,36.758],[-71.063,91.068],[-66.381,99.477],[-54.105,151.545],[-50.582,156.684],[-25.148,166.447],[11.457,177.72],[43.851,165.709],[77.265,153.766],[78.869,146.641],[84.013,94.607],[88.694,86.198],[138.553,41.26]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.66,"y":0},"t":160,"s":[{"i":[[25.701,4.013],[11.245,-12.034],[2.922,3.127],[38.764,-7.506],[-5.173,-23.076],[-28.607,-10.289],[0.785,-3.583],[-11.505,-17.666],[-4.902,-0.674],[-11.748,-2.496],[0,0],[-13.73,9.721],[-8.307,5.481],[-2.076,4.491],[1.869,16.855],[-3.09,1.238],[-1.706,22.317]],"o":[[-19.384,-3.026],[-2.922,3.127],[-11.245,-12.034],[-51.371,9.948],[4.832,21.554],[3.452,1.241],[-3.47,15.845],[2.7,4.147],[7.382,1.015],[18.573,3.946],[0,0],[8.668,-6.137],[4.13,-2.72],[7.275,-15.74],[-0.412,-3.642],[25.612,-10.265],[1.228,-16.058]],"v":[[112.756,-22.639],[57.669,3.748],[46.494,3.748],[-55.172,-25.099],[-103.905,43.585],[-36.842,96.56],[-32.16,104.969],[-35.021,155.1],[-17.565,159.913],[-3.265,162.675],[35.085,170.353],[69.022,158.414],[99.829,138.459],[105.036,129.47],[113.682,91.411],[113.961,83.002],[143.72,34.722]],"c":true}]},{"t":180,"s":[{"i":[[51.445,12.472],[11.245,-13.381],[2.922,3.477],[38.8,-9.406],[-5.196,-23.384],[-28.607,-10.289],[0.785,-3.583],[-16.2,-23.457],[-4.902,-0.674],[-10.738,-7.451],[0,0],[-16.975,11.779],[-7.382,1.015],[-2.812,4.072],[3.47,15.845],[-3.452,1.242],[-4.853,21.841]],"o":[[-38.801,-9.406],[-2.922,3.477],[-11.245,-13.381],[-51.445,12.472],[4.854,21.841],[3.452,1.242],[-3.47,15.845],[2.812,4.072],[7.382,1.015],[16.975,11.779],[0,0],[10.737,-7.451],[4.902,-0.674],[16.2,-23.457],[-0.785,-3.583],[28.607,-10.289],[5.197,-23.384]],"v":[[88.579,-75.949],[10.891,-45.909],[-0.285,-45.909],[-77.973,-75.949],[-132.017,4.597],[-80.556,57.662],[-75.875,66.071],[-70.075,146.877],[-57.943,154.316],[-27.048,165.544],[5.303,176.601],[37.654,165.544],[68.548,153.316],[80.68,146.877],[86.481,66.071],[91.162,57.662],[142.623,4.597]],"c":true}]}]},"hd":false},{"ty":"st","c":{"a":0,"k":[0.941176474094,0.847058832645,0.701960802078,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":1,"lj":1,"ml":10,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-6.695,-15.921],[5.747,-3.178],[22.493,4.591],[6.631,-1.555],[16.983,8.766],[-3.144,6.284],[-28.696,7.38],[-11.25,-13.38],[-2.92,3.48],[-38.8,-9.4]],"o":[[2.546,6.053],[-39.7,21.954],[-6.674,-1.362],[-56.127,13.163],[-6.244,-3.223],[5.486,-10.965],[38.664,-9.944],[2.92,3.48],[11.25,-13.38],[26.786,6.499]],"v":[[134.093,-35.971],[128.458,-19.704],[14.574,-11.647],[-5.621,-11.396],[-116.496,-21.256],[-122.192,-38.41],[-76.315,-72.759],[-0.282,-45.913],[10.887,-45.913],[87.791,-72.245]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[{"i":[[-5.004,-8.325],[6.396,-2.745],[22.493,3.965],[6.796,0.395],[14.379,1.024],[-8.034,6.68],[-17.189,-1.254],[-11.238,-11.569],[-2.923,3.003],[-42.6,-8.13]],"o":[[3.08,5.124],[-44.184,18.963],[-6.674,-1.176],[-25.168,-1.463],[-16.883,-1.202],[9.541,-7.933],[20.785,1.516],[2.92,3.006],[11.25,-11.557],[29.412,5.613]],"v":[[108.161,5.372],[101.89,19.422],[-18.584,31.862],[-38.779,32.078],[-94.428,5.977],[-124.5,-11.613],[-92.396,-20.793],[-35.546,10.96],[-24.376,10.96],[67.383,-18.075]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[-7.251,-6.149],[5.872,-2.326],[22.493,4.047],[6.714,-0.502],[16.307,4.015],[-6.23,5.852],[-23.691,2.323],[-11.238,-11.808],[-2.923,3.065],[-32.058,-5.562]],"o":[[5.992,4.424],[-40.293,15.973],[-6.674,-1.201],[-40.648,5.189],[-12.298,-1.889],[8.274,-8.349],[30.629,-3.216],[2.92,3.068],[11.25,-11.797],[22.134,3.84]],"v":[[118.226,-5.669],[107.38,10.454],[22.425,27.39],[2.23,27.611],[-89.895,12.313],[-109.951,-3.463],[-72.523,-21.702],[5.733,7.287],[16.903,7.287],[92.058,-20.411]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.66,"y":0},"t":160,"s":[{"i":[[-8.5,-3.973],[4.787,-1.908],[22.493,4.13],[6.631,-1.399],[16.983,7.007],[-3.144,5.023],[-28.696,5.9],[-11.238,-12.048],[-2.923,3.127],[-19.255,-2.993]],"o":[[7.97,3.725],[-32.576,12.984],[-6.674,-1.225],[-56.127,11.841],[-6.244,-2.576],[5.486,-8.765],[38.664,-7.949],[2.92,3.13],[11.25,-12.036],[13.295,2.066]],"v":[[122.845,-16.709],[109.044,1.486],[63.433,22.918],[43.238,23.143],[-82.88,18.648],[-89.102,4.687],[-53.204,-22.612],[47.011,3.613],[58.181,3.613],[112.501,-22.747]],"c":true}]},{"t":180,"s":[{"i":[[-6.695,-15.921],[5.747,-3.178],[22.493,4.591],[6.631,-1.555],[16.983,8.766],[-3.144,6.284],[-28.696,7.38],[-11.25,-13.38],[-2.92,3.48],[-38.8,-9.4]],"o":[[2.546,6.053],[-39.7,21.954],[-6.674,-1.362],[-56.127,13.163],[-6.244,-3.223],[5.486,-10.965],[38.664,-9.944],[2.92,3.48],[11.25,-13.38],[26.786,6.499]],"v":[[134.093,-35.971],[128.458,-19.704],[14.574,-11.647],[-5.621,-11.396],[-116.496,-21.256],[-122.192,-38.41],[-76.315,-72.759],[-0.282,-45.913],[10.887,-45.913],[87.791,-72.245]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.858823537827,0.749019622803,0.592156887054,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[51.445,12.472],[11.245,-13.381],[2.922,3.477],[38.8,-9.406],[-5.196,-23.384],[-28.607,-10.289],[0.785,-3.583],[-16.2,-23.457],[-4.902,-0.674],[-10.738,-7.451],[0,0],[-16.975,11.779],[-7.382,1.015],[-2.812,4.072],[3.47,15.845],[-3.452,1.242],[-4.853,21.841]],"o":[[-38.801,-9.406],[-2.922,3.477],[-11.245,-13.381],[-51.445,12.472],[4.854,21.841],[3.452,1.242],[-3.47,15.845],[2.812,4.072],[7.382,1.015],[16.975,11.779],[0,0],[10.737,-7.451],[4.902,-0.674],[16.2,-23.457],[-0.785,-3.583],[28.607,-10.289],[5.197,-23.384]],"v":[[88.579,-75.949],[10.891,-45.909],[-0.285,-45.909],[-77.973,-75.949],[-132.017,4.597],[-80.556,57.662],[-75.875,66.071],[-70.075,146.877],[-57.943,154.316],[-27.048,165.544],[5.303,176.601],[37.654,165.544],[68.548,154.316],[80.68,146.877],[86.481,66.071],[91.162,57.662],[142.623,4.597]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[{"i":[[56.478,10.772],[11.245,-11.558],[2.922,3.003],[33.966,-5.652],[-1.651,-23.642],[-28.607,-10.289],[0.785,-3.583],[-16.2,-23.457],[-4.902,-0.674],[-11.284,-7.894],[0,0],[-14.094,8.101],[-7.382,1.015],[-1.927,4.558],[-10.401,26.277],[-3.581,0.796],[-7.366,31.728]],"o":[[-42.596,-8.125],[-2.922,3.003],[-11.245,-11.558],[-28.481,4.739],[1.542,22.083],[3.452,1.241],[-3.47,15.845],[2.812,4.072],[7.382,1.015],[17.839,12.479],[0,0],[8.915,-5.124],[4.902,-0.674],[8.808,-20.829],[1.348,-3.41],[30.939,-6.879],[7.887,-33.97]],"v":[[68.289,-21.352],[-24.303,10.893],[-35.478,10.893],[-102.046,-23.16],[-135.705,34.244],[-100.516,88.128],[-95.835,96.537],[-85.044,141.622],[-82.498,147.092],[-52.788,162.453],[-18.121,174.858],[16.833,167.523],[50.164,158.256],[49.689,152.129],[53.914,97.85],[58.595,89.441],[122.669,44.587]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[42.499,7.368],[11.245,-11.798],[2.922,3.066],[38.832,-7.706],[-3.555,-21.167],[-28.607,-10.289],[0.785,-3.583],[-16.2,-23.457],[-4.902,-0.674],[-10.926,-7.319],[0,0],[-13.641,10.623],[-7.549,4.053],[-1.575,4.676],[-3.465,21.061],[-3.516,1.019],[-5.708,25.871]],"o":[[-32.053,-5.557],[-2.922,3.066],[-11.245,-11.798],[-46.22,9.147],[3.321,19.771],[3.452,1.242],[-3.47,15.845],[2.812,4.072],[7.382,1.015],[17.273,11.571],[0,0],[8.628,-6.72],[4.273,-2.011],[6.432,-18.359],[0.282,-3.497],[29.773,-8.584],[6.111,-27.698]],"v":[[92.829,-22],[16.978,7.294],[5.803,7.294],[-79.789,-24.136],[-125.22,36.758],[-71.063,91.068],[-66.381,99.477],[-54.105,151.545],[-50.582,156.684],[-25.148,166.447],[11.457,177.72],[43.851,165.709],[77.229,153.675],[78.869,146.641],[84.013,94.607],[88.694,86.198],[138.114,42.313]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.66,"y":0},"t":160,"s":[{"i":[[25.701,4.013],[11.245,-12.034],[2.922,3.127],[38.801,-7.522],[-5.173,-23.076],[-28.607,-10.289],[0.785,-3.583],[-11.295,-18.521],[-4.902,-0.674],[-11.748,-2.496],[0,0],[-13.732,9.72],[-7.713,7.048],[-1.993,4.529],[3.371,15.919],[-3.09,1.238],[-3.645,21.899]],"o":[[-19.384,-3.026],[-2.922,3.127],[-11.245,-12.034],[-51.37,9.958],[4.832,21.554],[3.452,1.241],[-3.47,15.845],[2.576,4.225],[7.382,1.015],[18.573,3.946],[0,0],[8.686,-6.148],[3.653,-3.329],[6.622,-15.049],[-0.77,-3.582],[25.616,-10.265],[3.903,-23.446]],"v":[[112.756,-22.639],[57.669,3.748],[46.494,3.748],[-55.172,-25.099],[-103.905,43.585],[-36.842,96.56],[-32.16,104.969],[-35.021,155.1],[-17.565,159.913],[-3.265,162.675],[35.085,170.353],[69.022,158.414],[99.758,137.291],[105.036,129.47],[113.682,91.411],[113.961,83.002],[142.939,36.812]],"c":true}]},{"t":180,"s":[{"i":[[51.445,12.472],[11.245,-13.381],[2.922,3.477],[38.8,-9.406],[-5.196,-23.384],[-28.607,-10.289],[0.785,-3.583],[-16.2,-23.457],[-4.902,-0.674],[-10.738,-7.451],[0,0],[-16.975,11.779],[-7.382,1.015],[-2.812,4.072],[3.47,15.845],[-3.452,1.242],[-4.853,21.841]],"o":[[-38.801,-9.406],[-2.922,3.477],[-11.245,-13.381],[-51.445,12.472],[4.854,21.841],[3.452,1.242],[-3.47,15.845],[2.812,4.072],[7.382,1.015],[16.975,11.779],[0,0],[10.737,-7.451],[4.902,-0.674],[16.2,-23.457],[-0.785,-3.583],[28.607,-10.289],[5.197,-23.384]],"v":[[88.579,-75.949],[10.891,-45.909],[-0.285,-45.909],[-77.973,-75.949],[-132.017,4.597],[-80.556,57.662],[-75.875,66.071],[-70.075,146.877],[-57.943,154.316],[-27.048,165.544],[5.303,176.601],[37.654,165.544],[68.548,154.316],[80.68,146.877],[86.481,66.071],[91.162,57.662],[142.623,4.597]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.941176474094,0.847058832645,0.701960802078,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[24.154,48.305],[13.203,43.885],[77.248,0],[15.205,-50.539],[21.999,-43.995],[-198.638,0]],"o":[[-21.999,-43.995],[-15.205,-50.539],[-78.08,0],[-13.203,43.885],[-24.154,48.305],[198.638,0]],"v":[[169.587,-20.913],[146.026,-110.813],[5.303,-207.093],[-135.42,-110.813],[-158.981,-20.913],[5.303,159.087]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[{"i":[[24.154,48.305],[13.203,43.885],[77.248,0],[15.205,-50.539],[16.359,-45.579],[-188.023,0]],"o":[[-21.999,-43.995],[-15.205,-50.539],[-78.08,0],[-13.203,43.885],[-17.961,50.045],[188.023,0]],"v":[[169.587,-20.913],[146.026,-110.813],[5.303,-207.093],[-135.42,-110.813],[-149.542,-17.661],[5.418,159.087]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[24.154,48.305],[13.203,43.885],[77.248,0],[15.205,-50.539],[21.999,-43.995],[-194.826,0]],"o":[[-21.999,-43.995],[-15.205,-50.539],[-78.08,0],[-13.203,43.885],[-24.154,48.305],[194.826,0]],"v":[[167.219,-19.07],[146.026,-110.813],[5.303,-207.093],[-135.42,-110.813],[-158.981,-20.913],[6.753,160.139]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.66,"y":0},"t":160,"s":[{"i":[[24.154,48.305],[13.203,43.885],[77.248,0],[15.205,-50.539],[21.999,-43.995],[-182.194,0]],"o":[[-21.999,-43.995],[-15.205,-50.539],[-78.08,0],[-13.203,43.885],[-24.154,48.305],[182.194,0]],"v":[[161.166,-14.465],[146.026,-110.813],[5.303,-207.093],[-135.42,-110.813],[-158.981,-20.913],[-11.141,159.218]],"c":true}]},{"t":180,"s":[{"i":[[24.154,48.305],[13.203,43.885],[77.248,0],[15.205,-50.539],[21.999,-43.995],[-198.638,0]],"o":[[-21.999,-43.995],[-15.205,-50.539],[-78.08,0],[-13.203,43.885],[-24.154,48.305],[198.638,0]],"v":[[169.587,-20.913],[146.026,-110.813],[5.303,-207.093],[-135.42,-110.813],[-158.981,-20.913],[5.303,159.087]],"c":true}]}]},"hd":false},{"ty":"st","c":{"a":0,"k":[0.368627458811,0.258823543787,0.121568627656,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":1,"lj":1,"ml":10,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[0,0],[-7.633,-2.073]],"o":[[0,0],[0,0]],"v":[[41.579,-186.44],[54.078,-183.608]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[{"i":[[0,0],[-6.911,-3.848]],"o":[[0,0],[0,0]],"v":[[41.475,-186.389],[52.926,-180.634]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[0,0],[-7.279,-3.094]],"o":[[0,0],[0,0]],"v":[[41.41,-186.344],[53.406,-181.835]],"c":false}]},{"i":{"x":0.34,"y":1},"o":{"x":0.66,"y":0},"t":160,"s":[{"i":[[0,0],[-7.633,-2.073]],"o":[[0,0],[0,0]],"v":[[41.579,-186.44],[54.078,-183.608]],"c":false}]},{"t":180,"s":[{"i":[[0,0],[-7.633,-2.073]],"o":[[0,0],[0,0]],"v":[[41.579,-186.44],[54.078,-183.608]],"c":false}]}]},"hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":6},"lc":2,"lj":2,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[0,0],[-1.612,-0.576]],"o":[[1.511,0.494],[0,0]],"v":[[62.717,-181.024],[67.407,-179.42]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[{"i":[[0,0],[-1.426,-0.947]],"o":[[1.348,0.843],[0,0]],"v":[[60.69,-176.048],[64.856,-173.363]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[0,0],[-1.518,-0.791]],"o":[[1.43,0.695],[0,0]],"v":[[61.612,-178.097],[66.039,-175.869]],"c":false}]},{"i":{"x":0.34,"y":1},"o":{"x":0.66,"y":0},"t":160,"s":[{"i":[[0,0],[-1.612,-0.576]],"o":[[1.511,0.494],[0,0]],"v":[[62.717,-181.024],[67.407,-179.42]],"c":false}]},{"t":180,"s":[{"i":[[0,0],[-1.612,-0.576]],"o":[[1.511,0.494],[0,0]],"v":[[62.717,-181.024],[67.407,-179.42]],"c":false}]}]},"hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":6},"lc":2,"lj":2,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[0,-23.536],[50.223,0],[0,23.536],[-50.223,0]],"o":[[0,23.536],[-50.223,0],[0,-23.536],[50.223,0]],"v":[[96.859,-148.606],[5.921,-105.99],[-85.016,-148.606],[5.921,-191.222]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[{"i":[[4.194,-31.921],[66.685,2.228],[1.486,23.489],[-50.223,0]],"o":[[-3.066,23.336],[-72.763,-2.431],[-2.352,-37.184],[50.223,0]],"v":[[87.385,-129.658],[-13.553,-72.832],[-100.806,-126.5],[5.921,-191.222]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[-3.087,-30.801],[58.454,1.114],[0.743,27.464],[-50.223,0]],"o":[[3.722,26.71],[-61.493,-1.216],[-1.176,-34.312],[50.223,0]],"v":[[102.385,-131.187],[10.395,-72.732],[-91.595,-129.345],[5.921,-191.222]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.66,"y":0},"t":160,"s":[{"i":[[-10.369,-29.681],[50.223,0],[0,31.44],[-50.223,0]],"o":[[10.51,30.085],[-50.223,0],[0,-31.44],[50.223,0]],"v":[[117.385,-132.716],[34.342,-72.632],[-82.385,-132.19],[5.921,-191.222]],"c":true}]},{"t":180,"s":[{"i":[[0,-23.536],[50.223,0],[0,23.536],[-50.223,0]],"o":[[0,23.536],[-50.223,0],[0,-23.536],[50.223,0]],"v":[[96.859,-148.606],[5.921,-105.99],[-85.016,-148.606],[5.921,-191.222]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.780392169952,0.623529434204,0.419607847929,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[22.173,44.342],[12.12,40.285],[70.911,0],[13.958,-46.393],[20.194,-40.385],[-182.343,0]],"o":[[-20.194,-40.385],[-13.957,-46.393],[-71.675,0],[-12.12,40.285],[-22.173,44.342],[182.343,0]],"v":[[156.11,-21.166],[134.482,-103.692],[5.303,-192.073],[-123.876,-103.692],[-145.504,-21.166],[5.303,144.067]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[{"i":[[22.173,44.342],[12.12,40.285],[70.911,0],[13.958,-46.393],[14.077,-41.84],[-172.599,0]],"o":[[-20.194,-40.385],[-13.957,-46.393],[-71.675,0],[-12.12,40.285],[-15.457,45.939],[172.599,0]],"v":[[141.9,-16.956],[134.482,-103.692],[5.303,-192.073],[-123.876,-103.692],[-138.754,-17.347],[5.418,144.067]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[23.821,48.017],[12.12,40.285],[70.911,0],[13.958,-46.393],[20.194,-38.136],[-178.843,0]],"o":[[-20.075,-40.444],[-13.957,-46.393],[-71.675,0],[-12.12,40.285],[-22.173,41.872],[178.843,0]],"v":[[149.794,-17.745],[134.482,-103.692],[5.303,-192.073],[-123.876,-103.692],[-133.399,-16.85],[6.753,145.12]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.66,"y":0},"t":160,"s":[{"i":[[25.469,51.692],[12.12,40.285],[70.911,0],[13.958,-46.393],[20.194,-35.886],[-167.248,0]],"o":[[-19.956,-40.503],[-13.957,-46.393],[-71.675,0],[-12.12,40.285],[-22.173,39.402],[167.248,0]],"v":[[154.005,-15.903],[134.482,-103.692],[5.303,-192.073],[-123.876,-103.692],[-113.925,-12.535],[-11.141,144.067]],"c":true}]},{"t":180,"s":[{"i":[[22.173,44.342],[12.12,40.285],[70.911,0],[13.958,-46.393],[20.194,-40.385],[-182.343,0]],"o":[[-20.194,-40.385],[-13.957,-46.393],[-71.675,0],[-12.12,40.285],[-22.173,44.342],[182.343,0]],"v":[[156.11,-21.166],[134.482,-103.692],[5.303,-192.073],[-123.876,-103.692],[-145.504,-21.166],[5.303,144.067]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.737254917622,0.572549045086,0.349019616842,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[24.154,48.305],[13.203,43.885],[77.248,0],[15.205,-50.539],[21.999,-43.995],[-198.638,0]],"o":[[-21.999,-43.995],[-15.205,-50.539],[-78.08,0],[-13.203,43.885],[-24.154,48.305],[198.638,0]],"v":[[169.587,-20.913],[146.026,-110.813],[5.303,-207.093],[-135.42,-110.813],[-158.981,-20.913],[5.303,159.087]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[{"i":[[24.154,48.305],[13.203,43.885],[77.248,0],[15.205,-50.539],[16.359,-45.579],[-198.638,0]],"o":[[-21.999,-43.995],[-15.205,-50.539],[-78.08,0],[-13.203,43.885],[-17.961,50.045],[198.638,0]],"v":[[169.587,-20.913],[146.026,-110.813],[5.303,-207.093],[-135.42,-110.813],[-149.542,-17.661],[5.303,159.087]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[24.154,48.305],[13.203,43.885],[77.248,0],[15.205,-50.539],[21.999,-43.995],[-194.826,0]],"o":[[-21.999,-43.995],[-15.205,-50.539],[-78.08,0],[-13.203,43.885],[-24.154,48.305],[194.826,0]],"v":[[167.219,-19.07],[146.026,-110.813],[5.303,-207.093],[-135.42,-110.813],[-158.981,-20.913],[6.753,160.139]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.66,"y":0},"t":160,"s":[{"i":[[24.154,48.305],[13.203,43.885],[77.248,0],[15.205,-50.539],[21.999,-43.995],[-182.194,0]],"o":[[-21.999,-43.995],[-15.205,-50.539],[-78.08,0],[-13.203,43.885],[-24.154,48.305],[182.194,0]],"v":[[161.166,-14.597],[146.026,-110.813],[5.303,-207.093],[-135.42,-110.813],[-158.981,-20.913],[-11.141,159.087]],"c":true}]},{"t":180,"s":[{"i":[[24.154,48.305],[13.203,43.885],[77.248,0],[15.205,-50.539],[21.999,-43.995],[-198.638,0]],"o":[[-21.999,-43.995],[-15.205,-50.539],[-78.08,0],[-13.203,43.885],[-24.154,48.305],[198.638,0]],"v":[[169.587,-20.913],[146.026,-110.813],[5.303,-207.093],[-135.42,-110.813],[-158.981,-20.913],[5.303,159.087]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.61960786581,0.46274510026,0.239215686917,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false}],"ip":0,"op":180,"st":0,"bm":0},{"ddd":0,"ind":12,"ty":4,"parent":11,"sr":1,"ks":{"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":20,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.66],"y":[0]},"t":160,"s":[8]},{"t":180,"s":[0]}]},"p":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[-94.41,-63.49,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[-71.252,-81.385,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.34,"y":1},"o":{"x":0.66,"y":0},"t":160,"s":[-109.147,-81.385,0],"to":[0,0,0],"ti":[0,0,0]},{"t":180,"s":[-94.41,-63.49,0]}]},"a":{"a":0,"k":[-92.696,-63.49,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[1.081,-15.591],[0,0],[0.01,0],[-5.699,9.64],[0,0]],"o":[[-42.636,-22.157],[-0.01,0],[-1.283,-12.428],[16.864,-28.514],[5.547,-0.606]],"v":[[-120.56,-89.291],[-178.963,-61.424],[-178.983,-61.434],[-172.678,-95.859],[-126.714,-115.865]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[{"i":[[1.081,-15.591],[0,0],[0.01,0],[-5.699,9.64],[0,0]],"o":[[-42.636,-22.157],[-0.01,0],[-1.283,-12.428],[16.864,-28.514],[5.547,-0.606]],"v":[[-120.56,-89.291],[-178.963,-61.424],[-178.983,-61.434],[-172.678,-95.859],[-126.714,-115.865]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.66,"y":0},"t":160,"s":[{"i":[[1.081,-15.591],[0,0],[0.01,0],[-5.699,9.64],[0,0]],"o":[[-42.636,-22.157],[-0.01,0],[-1.283,-12.428],[16.864,-28.514],[5.547,-0.606]],"v":[[-102.402,-87.712],[-178.963,-61.424],[-178.983,-61.434],[-172.678,-95.859],[-108.556,-114.286]],"c":true}]},{"t":180,"s":[{"i":[[1.081,-15.591],[0,0],[0.01,0],[-5.699,9.64],[0,0]],"o":[[-42.636,-22.157],[-0.01,0],[-1.283,-12.428],[16.864,-28.514],[5.547,-0.606]],"v":[[-120.56,-89.291],[-178.963,-61.424],[-178.983,-61.434],[-172.678,-95.859],[-126.714,-115.865]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.858823537827,0.596078455448,0.592156887054,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[17.728,-1.949],[16.87,-28.514],[-21.623,-17.626]],"o":[[0,0],[-13.747,23.235],[25.716,20.962]],"v":[[-126.711,-115.866],[-172.682,-95.857],[-156.36,-15.922]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[{"i":[[17.728,-1.949],[16.87,-28.514],[-21.623,-17.626]],"o":[[0,0],[-13.747,23.235],[25.716,20.962]],"v":[[-126.711,-115.866],[-172.682,-95.857],[-156.36,-15.922]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.66,"y":0},"t":160,"s":[{"i":[[17.728,-1.949],[16.87,-28.514],[-20.475,-18.948]],"o":[[0,0],[-13.747,23.235],[65.834,60.922]],"v":[[-108.553,-114.287],[-172.682,-95.857],[-156.36,-15.922]],"c":true}]},{"t":180,"s":[{"i":[[17.728,-1.949],[16.87,-28.514],[-21.623,-17.626]],"o":[[0,0],[-13.747,23.235],[25.716,20.962]],"v":[[-126.711,-115.866],[-172.682,-95.857],[-156.36,-15.922]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.929411768913,0.674509823322,0.666666686535,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[55.083,3.638],[22.172,-27.368],[-47.288,-39.234]],"o":[[-19.765,-1.305],[-24.472,30.208],[49.528,41.092]],"v":[[-115.798,-143.021],[-186.99,-118.078],[-164.645,10.795]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[{"i":[[55.083,3.638],[22.172,-27.368],[-47.288,-39.234]],"o":[[-19.765,-1.305],[-24.472,30.208],[49.528,41.092]],"v":[[-115.798,-143.021],[-186.99,-118.078],[-164.645,10.795]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.66,"y":0},"t":160,"s":[{"i":[[55.139,2.655],[22.172,-27.368],[-69.039,-60.795]],"o":[[-46.57,-2.242],[-24.472,30.208],[48.298,42.531]],"v":[[-93.167,-143.811],[-186.99,-118.078],[-148.33,26.058]],"c":true}]},{"t":180,"s":[{"i":[[55.083,3.638],[22.172,-27.368],[-47.288,-39.234]],"o":[[-19.765,-1.305],[-24.472,30.208],[49.528,41.092]],"v":[[-115.798,-143.021],[-186.99,-118.078],[-164.645,10.795]],"c":true}]}]},"hd":false},{"ty":"st","c":{"a":0,"k":[0.368627458811,0.258823543787,0.121568627656,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":1,"lj":1,"ml":10,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[0,0],[-36.319,10.182]],"o":[[0,0],[0,0]],"v":[[-194.126,-81.406],[-152.972,-131.334]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[{"i":[[0,0],[-36.319,10.182]],"o":[[0,0],[0,0]],"v":[[-194.126,-81.406],[-152.972,-131.334]],"c":false}]},{"i":{"x":0.34,"y":1},"o":{"x":0.66,"y":0},"t":160,"s":[{"i":[[0,0],[-36.319,10.182]],"o":[[0,0],[0,0]],"v":[[-194.126,-81.406],[-152.972,-131.334]],"c":false}]},{"t":180,"s":[{"i":[[0,0],[-36.319,10.182]],"o":[[0,0],[0,0]],"v":[[-194.126,-81.406],[-152.972,-131.334]],"c":false}]}]},"hd":false},{"ty":"st","c":{"a":0,"k":[0.737254917622,0.572549045086,0.349019616842,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":10},"lc":2,"lj":2,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[55.083,3.638],[22.172,-27.368],[-47.288,-39.234]],"o":[[-19.765,-1.305],[-24.472,30.208],[49.528,41.092]],"v":[[-115.798,-143.021],[-186.99,-118.078],[-164.645,10.795]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[{"i":[[55.083,3.638],[22.172,-27.368],[-47.288,-39.234]],"o":[[-19.765,-1.305],[-24.472,30.208],[49.528,41.092]],"v":[[-115.798,-143.021],[-186.99,-118.078],[-164.645,10.795]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.66,"y":0},"t":160,"s":[{"i":[[55.123,2.965],[22.172,-27.368],[-69.039,-59.216]],"o":[[-46.57,-2.505],[-24.472,30.208],[48.848,41.898]],"v":[[-93.167,-143.811],[-186.99,-118.078],[-148.33,26.058]],"c":true}]},{"t":180,"s":[{"i":[[55.083,3.638],[22.172,-27.368],[-47.288,-39.234]],"o":[[-19.765,-1.305],[-24.472,30.208],[49.528,41.092]],"v":[[-115.798,-143.021],[-186.99,-118.078],[-164.645,10.795]],"c":true}]}]},"hd":false},{"ty":"st","c":{"a":0,"k":[0.368627458811,0.258823543787,0.121568627656,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":1,"lj":1,"ml":10,"bm":0,"hd":false},{"ty":"fl","c":{"a":0,"k":[0.61960786581,0.46274510026,0.239215686917,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false}],"ip":0,"op":180,"st":0,"bm":0},{"ddd":0,"ind":13,"ty":4,"parent":11,"sr":1,"ks":{"r":{"a":1,"k":[{"i":{"x":[0.34],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":20,"s":[-8]},{"i":{"x":[0.34],"y":[1]},"o":{"x":[0.66],"y":[0]},"t":160,"s":[0]},{"t":180,"s":[0]}]},"p":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[104.342,-63.49,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[118.026,-81.385,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.34,"y":1},"o":{"x":0.66,"y":0},"t":160,"s":[83.289,-81.385,0],"to":[0,0,0],"ti":[0,0,0]},{"t":180,"s":[104.342,-63.49,0]}]},"a":{"a":0,"k":[102.628,-63.49,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-1.081,-15.591],[0,0],[-0.01,0],[5.699,9.64],[0,0]],"o":[[42.636,-22.157],[0.01,0],[1.283,-12.428],[-16.864,-28.514],[-5.547,-0.606]],"v":[[130.917,-89.291],[189.319,-61.424],[189.34,-61.434],[183.034,-95.859],[137.07,-115.865]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[{"i":[[-1.081,-15.591],[0,0],[-0.01,0],[5.699,9.64],[0,0]],"o":[[42.636,-22.157],[0.01,0],[1.283,-12.428],[-16.864,-28.514],[-5.547,-0.606]],"v":[[116.969,-89.818],[189.319,-61.424],[189.34,-61.434],[183.034,-95.859],[123.123,-116.392]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.66,"y":0},"t":160,"s":[{"i":[[-1.081,-15.591],[0,0],[-0.01,0],[5.699,9.64],[0,0]],"o":[[42.636,-22.157],[0.01,0],[1.283,-12.428],[-16.864,-28.514],[-5.547,-0.606]],"v":[[130.917,-89.291],[189.319,-61.424],[189.34,-61.434],[183.034,-95.859],[137.07,-115.865]],"c":true}]},{"t":180,"s":[{"i":[[-1.081,-15.591],[0,0],[-0.01,0],[5.699,9.64],[0,0]],"o":[[42.636,-22.157],[0.01,0],[1.283,-12.428],[-16.864,-28.514],[-5.547,-0.606]],"v":[[130.917,-89.291],[189.319,-61.424],[189.34,-61.434],[183.034,-95.859],[137.07,-115.865]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.858823537827,0.596078455448,0.592156887054,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-55.083,3.638],[-22.172,-27.368],[47.288,-39.234]],"o":[[19.765,-1.305],[24.472,30.208],[-49.528,41.092]],"v":[[125.73,-143.021],[196.922,-118.078],[174.577,10.795]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[{"i":[[-55.083,3.638],[-22.172,-27.368],[63.844,-42.374]],"o":[[19.765,-1.305],[24.472,30.208],[-53.619,35.588]],"v":[[100.993,-144.6],[196.922,-118.078],[162.472,20.269]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.66,"y":0},"t":160,"s":[{"i":[[-55.083,3.638],[-22.172,-27.368],[47.288,-39.234]],"o":[[19.765,-1.305],[24.472,30.208],[-49.528,41.092]],"v":[[125.73,-143.021],[196.922,-118.078],[174.577,10.795]],"c":true}]},{"t":180,"s":[{"i":[[-55.083,3.638],[-22.172,-27.368],[47.288,-39.234]],"o":[[19.765,-1.305],[24.472,30.208],[-49.528,41.092]],"v":[[125.73,-143.021],[196.922,-118.078],[174.577,10.795]],"c":true}]}]},"hd":false},{"ty":"st","c":{"a":0,"k":[0.368627458811,0.258823543787,0.121568627656,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":1,"lj":1,"ml":10,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[0,0],[36.319,10.182]],"o":[[0,0],[0,0]],"v":[[203.905,-80.406],[162.752,-130.334]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[{"i":[[0,0],[36.319,10.182]],"o":[[0,0],[0,0]],"v":[[203.905,-80.406],[162.752,-130.334]],"c":false}]},{"i":{"x":0.34,"y":1},"o":{"x":0.66,"y":0},"t":160,"s":[{"i":[[0,0],[36.319,10.182]],"o":[[0,0],[0,0]],"v":[[203.905,-80.406],[162.752,-130.334]],"c":false}]},{"t":180,"s":[{"i":[[0,0],[36.319,10.182]],"o":[[0,0],[0,0]],"v":[[203.905,-80.406],[162.752,-130.334]],"c":false}]}]},"hd":false},{"ty":"st","c":{"a":0,"k":[0.737254917622,0.572549045086,0.349019616842,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":10},"lc":2,"lj":2,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-17.728,-1.949],[-16.87,-28.514],[21.623,-17.626]],"o":[[0,0],[13.747,23.235],[-25.716,20.962]],"v":[[136.643,-115.866],[182.613,-95.857],[166.291,-15.922]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[{"i":[[-17.728,-1.949],[-16.87,-28.514],[23.187,-15.511]],"o":[[0,0],[13.747,23.235],[-64.713,43.291]],"v":[[122.695,-116.393],[182.613,-95.857],[166.291,-15.922]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.66,"y":0},"t":160,"s":[{"i":[[-17.728,-1.949],[-16.87,-28.514],[21.623,-17.626]],"o":[[0,0],[13.747,23.235],[-25.716,20.962]],"v":[[136.643,-115.866],[182.613,-95.857],[166.291,-15.922]],"c":true}]},{"t":180,"s":[{"i":[[-17.728,-1.949],[-16.87,-28.514],[21.623,-17.626]],"o":[[0,0],[13.747,23.235],[-25.716,20.962]],"v":[[136.643,-115.866],[182.613,-95.857],[166.291,-15.922]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.929411768913,0.674509823322,0.666666686535,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.34,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-55.083,3.638],[-22.172,-27.368],[47.288,-39.234]],"o":[[19.765,-1.305],[24.472,30.208],[-49.528,41.092]],"v":[[125.73,-143.021],[196.922,-118.078],[174.577,10.795]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[{"i":[[-55.083,3.638],[-22.172,-27.368],[65.423,-44.479]],"o":[[19.765,-1.305],[24.472,30.208],[-53.22,36.183]],"v":[[100.993,-144.6],[196.922,-118.078],[162.472,20.269]],"c":true}]},{"i":{"x":0.34,"y":1},"o":{"x":0.66,"y":0},"t":160,"s":[{"i":[[-55.083,3.638],[-22.172,-27.368],[47.288,-39.234]],"o":[[19.765,-1.305],[24.472,30.208],[-49.528,41.092]],"v":[[125.73,-143.021],[196.922,-118.078],[174.577,10.795]],"c":true}]},{"t":180,"s":[{"i":[[-55.083,3.638],[-22.172,-27.368],[47.288,-39.234]],"o":[[19.765,-1.305],[24.472,30.208],[-49.528,41.092]],"v":[[125.73,-143.021],[196.922,-118.078],[174.577,10.795]],"c":true}]}]},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.61960786581,0.46274510026,0.239215686917,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}}],"bm":0,"hd":false}],"ip":0,"op":180,"st":0,"bm":0}]}<?php

declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\WebUI;

use JsonSerializable;

/**
 * A form field.
 */
final class Field implements JsonSerializable
{
    /**
     * @param string                    $name         Name of the POST parameter
     * @param string                    $label        Label
     * @param array<string, string>     $options      Value => label, for {@see FieldType::CHOICE}
     * @param ?array{string, string}    $showWhen     Only show (and submit) the field when the CHOICE field with the given name has the given value
     * @param ?string                   $autocomplete Value of the autocomplete attribute
     */
    public function __construct(
        public string $name,
        public string $label,
        public FieldType $type = FieldType::TEXT,
        public string $value = '',
        public string $placeholder = '',
        public string|SafeHtml $hint = '',
        public bool $required = true,
        public ?string $autocomplete = null,
        public array $options = [],
        public ?array $showWhen = null,
        public bool $autofocus = false,
        public ?int $maxLength = null,
    ) {
    }

    #[\Override]
    public function jsonSerialize(): array
    {
        return [
            'name' => $this->name,
            'label' => $this->label,
            'type' => $this->type->value,
            'value' => $this->value,
            'placeholder' => $this->placeholder,
            'hint' => $this->hint,
            'required' => $this->required,
            'autocomplete' => $this->autocomplete,
            'options' => (object) $this->options,
            'showWhen' => $this->showWhen,
            'autofocus' => $this->autofocus,
            'maxLength' => $this->maxLength,
        ];
    }

    /**
     * Get the attributes of the input element.
     *
     * @return array<string, scalar|null>
     */
    public function getInputAttributes(string $id): array
    {
        $type = match ($this->type) {
            // Hidden like the other secrets typed in the login
            FieldType::CODE => 'password',
            FieldType::TEXTAREA, FieldType::CHOICE => null,
            default => $this->type->value,
        };
        return [
            'type' => $type,
            'id' => $id,
            'name' => $this->name,
            'placeholder' => $this->placeholder ?: null,
            // Without JavaScript, all the conditional fields are shown: requiring them would block the form
            'required' => $this->required && $this->showWhen === null,
            'autocomplete' => $this->autocomplete ?? match ($this->type) {
                FieldType::CODE => 'one-time-code',
                FieldType::PHONE => 'tel',
                FieldType::PASSWORD => 'current-password',
                default => 'off',
            },
            'inputmode' => match ($this->type) {
                FieldType::CODE => 'numeric',
                FieldType::PHONE => 'tel',
                default => null,
            },
            'autocapitalize' => $this->type === FieldType::TEXT || $this->type === FieldType::CODE ? 'off' : null,
            'spellcheck' => $this->type === FieldType::TEXTAREA ? null : 'false',
            'autofocus' => $this->autofocus,
            'maxlength' => $this->maxLength,
        ];
    }
}
<?php

declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\WebUI;

use AssertionError;
use danog\MadelineProto\Exception;

/**
 * Renders the native PHP templates of the web UI.
 *
 * A template name like `page` or `partials/field` is looked up as `page.phtml` or `partials/field.phtml`
 * in each of the configured directories, in order, and then in the builtin template directory: a custom
 * directory only needs to contain the templates it overrides.
 *
 * Inside a template, `$this` is a {@see Template}, and the variables passed to the template are in scope.
 */
final class TemplateEngine
{
    /**
     * Directory of the builtin templates.
     */
    public const BUILTIN_DIRECTORY = __DIR__.DIRECTORY_SEPARATOR.'templates';

    /** @var list<string> */
    private readonly array $directories;

    /**
     * @param list<string> $directories Template directories, searched in order before the builtin one.
     */
    public function __construct(array $directories = [])
    {
        $directories[] = self::BUILTIN_DIRECTORY;
        $this->directories = array_values(array_unique(array_map(
            static fn (string $dir): string => rtrim($dir, '/\\'),
            $directories,
        )));
    }

    /**
     * Get the template directories, in lookup order.
     *
     * @return list<string>
     */
    public function getDirectories(): array
    {
        return $this->directories;
    }

    /**
     * Render a template.
     *
     * @param string               $name Template name, without the .phtml extension
     * @param array<string, mixed> $data Variables passed to the template
     */
    public function render(string $name, array $data = []): string
    {
        return (new Template($this, $name))->render($data);
    }

    /**
     * Check whether a template or asset exists.
     *
     * @param string $file File name relative to the template directories, including the extension
     */
    public function exists(string $file): bool
    {
        return $this->find($file) !== null;
    }

    /**
     * Get the path of a template or asset.
     *
     * @param string $file File name relative to the template directories, including the extension
     */
    public function resolve(string $file): string
    {
        return $this->find($file) ?? throw new Exception("Could not find $file in the template directories ".implode(', ', $this->directories));
    }

    /**
     * Get the contents of an asset (stylesheet, script, image...).
     *
     * @param string $file File name relative to the template directories, including the extension
     */
    public function asset(string $file): string
    {
        $contents = file_get_contents($this->resolve($file));
        if ($contents === false) {
            throw new Exception("Could not read $file!");
        }
        return $contents;
    }

    private function find(string $file): ?string
    {
        $file = str_replace('\\', '/', $file);
        if ($file === '' || $file[0] === '/' || preg_match('~(^|/)\.\.?(/|$)|[\0:]~', $file)) {
            throw new AssertionError("Invalid template name $file");
        }
        foreach ($this->directories as $dir) {
            $path = $dir.DIRECTORY_SEPARATOR.$file;
            if (is_file($path)) {
                return $path;
            }
        }
        return null;
    }
}
<?php

declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\WebUI;

use AssertionError;
use danog\MadelineProto\Lang;
use danog\MadelineProto\Settings\Templates;

use function Amp\ByteStream\getOutputBufferStream;

/**
 * Renders and outputs the pages of the web UI.
 *
 * @internal
 */
final class WebUI
{
    private const RTL_LANGUAGES = ['ar', 'ckb', 'fa', 'he', 'ur'];
    /**
     * Query parameter of the asset route.
     */
    public const ASSET_PARAMETER = 'madelineAsset';
    private const ASSET_TYPES = [
        'js' => 'text/javascript; charset=utf-8',
        'css' => 'text/css; charset=utf-8',
        'json' => 'application/json',
        'svg' => 'image/svg+xml',
        'png' => 'image/png',
        'webp' => 'image/webp',
        'woff2' => 'font/woff2',
        'woff' => 'font/woff',
        'wasm' => 'application/wasm',
    ];
    /**
     * Strings used by the web app itself (the pages carry their own).
     */
    private const APP_STRINGS = [
        'webDocs',
        'webProgress',
        'webOptional',
        'webShowPassword',
        'webHidePassword',
        'webFieldRequired',
        'webPhoneInvalid',
        'webQrTitle',
        'webQrWaiting',
        'loginQrCodeSuccessful',
        'loginWebQr1',
        'loginWebQr2',
        'loginWebQr3',
    ];

    /**
     * Render a page.
     */
    public static function render(Templates $templates, Page $page): string
    {
        $lang = self::getLangCode();
        $dir = \in_array($lang, self::RTL_LANGUAGES, true) ? 'rtl' : 'ltr';
        return $templates->getEngine()->render($page->template, [
            'page' => $page,
            'lang' => $lang,
            'dir' => $dir,
            'state' => self::getState($page, $lang, $dir),
            'assetUrl' => self::getAssetUrl(),
        ]);
    }

    /**
     * Get the state of the web app.
     *
     * @return array<string, mixed>
     */
    public static function getStateArray(Page $page, string $lang = 'en', string $dir = 'ltr'): array
    {
        $strings = [];
        foreach (self::APP_STRINGS as $key) {
            $strings[$key] = Template::translate($key);
        }
        // The strings of the web app (webui/src/lang.ts, tweb's keys), in the language of the session: the app
        // has them in English, translated in langs/*.json as tweb.<key>
        foreach (Lang::$current_lang as $key => $string) {
            if (str_starts_with($key, 'tweb.')) {
                $strings[substr($key, 5)] = $string;
            }
        }
        $steps = [];
        foreach (Step::cases() as $step) {
            $steps[] = ['id' => $step->value, 'label' => $step->getLabel()];
        }
        return [
            'page' => $page,
            'steps' => $steps,
            'strings' => $strings,
            'lang' => $lang,
            'dir' => $dir,
            'assetUrl' => self::getAssetUrl(),
        ];
    }

    /**
     * Get the initial state of the web app, as a JSON string safe to embed in a `<script>` tag.
     */
    public static function getState(Page $page, string $lang = 'en', string $dir = 'ltr'): string
    {
        return json_encode(
            self::getStateArray($page, $lang, $dir),
            JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_INVALID_UTF8_SUBSTITUTE | JSON_THROW_ON_ERROR,
        );
    }

    /**
     * Whether the web app asked for a JSON answer (it submits its forms with fetch).
     */
    public static function isJsonRequest(): bool
    {
        return ($_SERVER['HTTP_X_MADELINEPROTO_WEBUI'] ?? '') === 'json';
    }

    /**
     * Render a page and write it to the browser: as HTML, or as the state of the web app if it asked for JSON.
     */
    public static function write(Templates $templates, Page $page): void
    {
        if (self::isJsonRequest()) {
            $lang = self::getLangCode();
            self::writeJson(['state' => self::getStateArray($page, $lang, \in_array($lang, self::RTL_LANGUAGES, true) ? 'rtl' : 'ltr')]);
            return;
        }
        if (!headers_sent()) {
            header('Content-Type: text/html; charset=utf-8');
            header('Cache-Control: no-store');
            header('Referrer-Policy: no-referrer');
        }
        getOutputBufferStream()->write(self::render($templates, $page));
    }

    /**
     * Redirect the browser to the current URL with a GET request, after a POST that moved to the next step:
     * reloading the page then shows the step again, instead of submitting the previous one again.
     *
     * Only for intermediate steps: after the last one, the script continues, and must run once.
     *
     * @return bool Whether the redirect was sent: if not, the caller must show the page
     */
    public static function redirectToSelf(): bool
    {
        // The web app gets the next page in the answer
        if (self::isJsonRequest() || headers_sent() || ($_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'POST' || !isset($_SERVER['REQUEST_URI'])) {
            return false;
        }
        $query = explode('?', $_SERVER['REQUEST_URI'], 2)[1] ?? null;
        header('Location: '.self::getSelfUrl().($query === null ? '' : "?$query"), true, 303);
        header('Cache-Control: no-store');
        return true;
    }

    /**
     * After the last step of the login, tell the web app to reload the page, if it asked for JSON.
     *
     * The request must then end without running the rest of the script: it runs in the GET of the reload, once.
     *
     * @return bool Whether the answer was sent: if so, the caller must end the request
     */
    public static function writeLoggedIn(): bool
    {
        if (!self::isJsonRequest()) {
            return false;
        }
        self::writeJson(['loggedIn' => true]);
        return true;
    }

    /**
     * Get the prefix of the URLs of the assets: append the path of an asset, like `assets/app.js`.
     */
    public static function getAssetUrl(): string
    {
        return self::getSelfUrl().'?'.self::ASSET_PARAMETER.'=';
    }

    /**
     * Serve an asset of the template directories, if the request is for one (`?madelineAsset=assets/...`).
     *
     * @return bool Whether the request was for an asset: if so, the caller must end the request
     */
    public static function serveAsset(Templates $templates): bool
    {
        if (!isset($_GET[self::ASSET_PARAMETER]) || !\is_string($_GET[self::ASSET_PARAMETER])) {
            return false;
        }
        $path = $_GET[self::ASSET_PARAMETER];
        $type = self::ASSET_TYPES[strtolower(pathinfo($path, PATHINFO_EXTENSION))] ?? null;
        try {
            // Only assets: never the templates, nor anything outside the template directories
            $file = str_starts_with($path, 'assets/') && $type !== null && $templates->getEngine()->exists($path)
                ? $templates->getEngine()->resolve($path)
                : null;
        } catch (AssertionError) {
            $file = null;
        }
        // The name can't leave the directories (see TemplateEngine), but a symlink in them could; phars have no
        // symlinks, and realpath() doesn't support them
        if ($file !== null && !str_starts_with($file, 'phar://')) {
            $real = realpath($file);
            $file = null;
            foreach ($templates->getEngine()->getDirectories() as $dir) {
                $assets = realpath($dir.DIRECTORY_SEPARATOR.'assets');
                if ($real !== false && $assets !== false && str_starts_with($real, $assets.DIRECTORY_SEPARATOR)) {
                    $file = $real;
                    break;
                }
            }
        }
        if ($file === null) {
            if (!headers_sent()) {
                http_response_code(404);
                header('Content-Type: text/plain; charset=utf-8');
            }
            getOutputBufferStream()->write('Not found');
            return true;
        }
        $contents = file_get_contents($file);
        \assert($contents !== false);
        $etag = '"'.hash('xxh128', $contents).'"';
        if (!headers_sent()) {
            header("Content-Type: $type");
            header('Cache-Control: no-cache');
            header("ETag: $etag");
            header('X-Content-Type-Options: nosniff');
            if (trim($_SERVER['HTTP_IF_NONE_MATCH'] ?? '') === $etag) {
                http_response_code(304);
                return true;
            }
        }
        getOutputBufferStream()->write($contents);
        return true;
    }

    /**
     * @param array<string, mixed> $data
     */
    private static function writeJson(array $data): void
    {
        if (!headers_sent()) {
            header('Content-Type: application/json');
            header('Cache-Control: no-store');
        }
        getOutputBufferStream()->write(json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_INVALID_UTF8_SUBSTITUTE | JSON_THROW_ON_ERROR));
    }

    /**
     * Get the path of the current URL, without the query string (or with the query of the page).
     *
     * Always same-origin: a request for `//evil.example/` (which a front controller may route to the script)
     * would otherwise give a protocol-relative URL, pointing redirects and the QR code requests to another
     * host.
     */
    public static function getSelfUrl(bool $withQuery = false): string
    {
        [$path, $query] = explode('?', $_SERVER['REQUEST_URI'] ?? '', 2) + [1 => ''];
        $path = '/'.ltrim(str_replace('\\', '/', $path), '/');
        if (!$withQuery) {
            return $path;
        }
        // The query of the page, which may select the session or the bot (index.php?bot=x), without the
        // parameters of the web UI: as is (parse_str() would rename the parameters with dots or spaces)
        $query = implode('&', array_filter(
            explode('&', $query),
            static fn (string $pair): bool => $pair !== ''
                && !\in_array(urldecode(explode('=', $pair, 2)[0]), ['getQrCode', 'waitQrCodeOrLogin', self::ASSET_PARAMETER], true),
        ));
        return $query === '' ? $path : "$path?$query";
    }

    /**
     * Get the code of the current language.
     */
    private static function getLangCode(): string
    {
        // Untranslated languages are copies of English: check it first
        if (Lang::$current_lang === Lang::$lang['en']) {
            return 'en';
        }
        foreach (Lang::$lang as $code => $strings) {
            if ($strings === Lang::$current_lang) {
                return $code;
            }
        }
        return 'en';
    }
}
<?php

declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\WebUI;

use JsonSerializable;
use Stringable;

/**
 * A trusted HTML fragment, output as-is by templates instead of being escaped.
 *
 * Only wrap markup that does not contain user input.
 */
final class SafeHtml implements Stringable, JsonSerializable
{
    public function __construct(
        public readonly string $html
    ) {
    }

    /**
     * Escape a plain string into a safe HTML fragment.
     */
    public static function escape(string $text): self
    {
        return new self(htmlspecialchars($text, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML5, 'UTF-8'));
    }

    /**
     * Escape a plain string, turning the URLs it contains into links.
     */
    public static function linkify(string $text): self
    {
        $parts = preg_split('~(https?://[^\s<>"\']+[^\s<>"\'.,;:!?)])~', $text, -1, PREG_SPLIT_DELIM_CAPTURE);
        $html = '';
        foreach ($parts as $k => $part) {
            $escaped = self::escape($part)->html;
            $html .= $k % 2 ? "<a href=\"$escaped\" target=\"_blank\" rel=\"noopener noreferrer\">$escaped</a>" : $escaped;
        }
        return new self($html);
    }

    /**
     * Serialized as `{"html": "..."}`, to tell it apart from plain text.
     */
    #[\Override]
    public function jsonSerialize(): array
    {
        return ['html' => $this->html];
    }

    #[\Override]
    public function __toString(): string
    {
        return $this->html;
    }
}
<?php

declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\WebUI;

/**
 * Severity of an alert.
 */
enum AlertLevel: string
{
    case INFO = 'info';
    case SUCCESS = 'success';
    case WARNING = 'warning';
    case ERROR = 'error';
}
<?php

declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\WebUI;

use JsonSerializable;

/**
 * A page of the web UI: what the builtin login and status pages show.
 *
 * The page is rendered on the server by the template named by {@see self::$template} (by default `page`), which
 * gets it in the `$page` variable; in the browser, the web app renders it again with the view named by
 * {@see self::$view} (by default `setup`), from its JSON serialization.
 */
final class Page implements JsonSerializable
{
    /**
     * @param string                   $title        Title
     * @param string|SafeHtml          $lead         Introductory text
     * @param ?Step                    $step         Current step of the setup flow, if any
     * @param list<Field>              $fields       Form fields
     * @param ?string                  $submit       Label of the submit button, or null if the page has no form
     * @param list<Alert>              $alerts       Alerts
     * @param list<Action>             $actions      Secondary actions of the form
     * @param list<string|SafeHtml>    $instructions Ordered list of instructions
     * @param ?string                  $qrEndpoint   URL of the QR code login endpoint, to show the QR code login panel
     * @param string                   $template     Template used to render the page on the server
     * @param string                   $view         View used to render the page in the web app
     * @param array<string, mixed>     $data         Additional variables for custom templates
     */
    public function __construct(
        public string $title,
        public string|SafeHtml $lead = '',
        public ?Step $step = null,
        public array $fields = [],
        public ?string $submit = null,
        public array $alerts = [],
        public array $actions = [],
        public array $instructions = [],
        public ?string $qrEndpoint = null,
        public string $template = 'page',
        public string $view = 'setup',
        public array $data = [],
    ) {
    }

    #[\Override]
    public function jsonSerialize(): array
    {
        return [
            'view' => $this->view,
            'title' => $this->title,
            'lead' => $this->lead,
            'step' => $this->step?->value,
            'fields' => $this->fields,
            'submit' => $this->submit,
            'alerts' => $this->alerts,
            'actions' => $this->actions,
            'instructions' => $this->instructions,
            'qrEndpoint' => $this->qrEndpoint,
            'data' => (object) $this->data,
        ];
    }

    /**
     * Add alerts to the page.
     */
    public function addAlert(Alert ...$alerts): self
    {
        array_push($this->alerts, ...$alerts);
        return $this;
    }
}
<?php

declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\WebUI;

use JsonSerializable;

/**
 * A message shown on a page.
 */
final class Alert implements JsonSerializable
{
    /**
     * @param string|SafeHtml      $message Plain text (escaped when rendered) or trusted HTML
     * @param string|SafeHtml|null $title   Optional title
     * @param string|SafeHtml|null $code    Optional code snippet, like the code that fixes the issue
     */
    public function __construct(
        public readonly AlertLevel $level,
        public readonly string|SafeHtml $message,
        public readonly string|SafeHtml|null $title = null,
        public readonly string|SafeHtml|null $code = null,
    ) {
    }

    public static function info(string|SafeHtml $message, string|SafeHtml|null $title = null): self
    {
        return new self(AlertLevel::INFO, $message, $title);
    }
    public static function success(string|SafeHtml $message, string|SafeHtml|null $title = null): self
    {
        return new self(AlertLevel::SUCCESS, $message, $title);
    }
    public static function warning(string|SafeHtml $message, string|SafeHtml|null $title = null): self
    {
        return new self(AlertLevel::WARNING, $message, $title);
    }
    public static function error(string|SafeHtml $message, string|SafeHtml|null $title = null): self
    {
        return new self(AlertLevel::ERROR, $message, $title);
    }

    #[\Override]
    public function jsonSerialize(): array
    {
        return [
            'level' => $this->level->value,
            'message' => $this->message,
            'title' => $this->title,
            'code' => $this->code,
        ];
    }

    /**
     * Get the alert as plain text, for the CLI and the logs.
     */
    public function toText(): string
    {
        $text = '';
        if ($this->title !== null) {
            $text .= self::plain($this->title).PHP_EOL;
        }
        $text .= self::plain($this->message);
        if ($this->code !== null) {
            $text .= PHP_EOL.self::plain($this->code);
        }
        return $text;
    }

    private static function plain(string|SafeHtml $text): string
    {
        return $text instanceof SafeHtml
            ? html_entity_decode(strip_tags($text->html), ENT_QUOTES | ENT_HTML5, 'UTF-8')
            : $text;
    }
}
<?php

declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\WebUI;

/**
 * Type of a form field.
 */
enum FieldType: string
{
    case TEXT = 'text';
    case PHONE = 'tel';
    case PASSWORD = 'password';
    /** One-time login code, hidden like a password. */
    case CODE = 'code';
    case URL = 'url';
    case TEXTAREA = 'textarea';
    case HIDDEN = 'hidden';
    /** A choice between a few options, rendered as a segmented control. */
    case CHOICE = 'choice';
}
<?php

declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\WebUI;

/**
 * The steps of the setup flow, shown in the progress rail of the builtin pages.
 */
enum Step: string
{
    /** Get the API ID and hash. */
    case APP = 'app';
    /** Choose the account or bot. */
    case ACCOUNT = 'account';
    /** Verify the login with a code, a password or a signup. */
    case VERIFY = 'verify';
    /** Logged in, running. */
    case DONE = 'done';

    /**
     * Get the localized label of the step.
     */
    public function getLabel(): string
    {
        return Template::translate(match ($this) {
            self::APP => 'webStepApp',
            self::ACCOUNT => 'webStepAccount',
            self::VERIFY => 'webStepVerify',
            self::DONE => 'webStepDone',
        });
    }
}
<?php

declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\WebUI;

use AssertionError;
use danog\MadelineProto\Lang;
use Stringable;
use Throwable;

/**
 * A template being rendered: `$this` inside a .phtml template.
 *
 * Templates can extend a layout and fill its sections:
 *
 * ```php
 * <?php $this->layout('layout', ['title' => $page->title]) ?>
 * <?php $this->start('head') ?><style>...</style><?php $this->stop() ?>
 * <p>The rest of the output becomes the "content" section of the layout.</p>
 * ```
 *
 * Use {@see self::e()} to output any value: strings are escaped, {@see SafeHtml} is output as-is.
 */
final class Template
{
    /** @var ?array{string, array<string, mixed>} */
    private ?array $layout = null;
    /** @var list<string> */
    private array $sectionStack = [];

    /**
     * @param array<string, string> $sections Sections filled by the templates extending this one
     *
     * @internal
     */
    public function __construct(
        private readonly TemplateEngine $engine,
        private readonly string $name,
        private array $sections = [],
    ) {
    }

    /**
     * Render the template.
     *
     * @param array<string, mixed> $data Variables passed to the template
     *
     * @internal
     */
    public function render(array $data): string
    {
        $file = $this->engine->resolve($this->name.'.phtml');
        $level = ob_get_level();
        ob_start();
        try {
            $this->includeTemplate($file, $data);
            if ($this->sectionStack) {
                throw new AssertionError("Unclosed section {$this->sectionStack[0]} in {$this->name}");
            }
            $content = ob_get_clean();
        } catch (Throwable $e) {
            while (ob_get_level() > $level) {
                ob_end_clean();
            }
            throw $e;
        }
        if ($this->layout === null) {
            return $content;
        }
        [$layout, $layoutData] = $this->layout;
        $this->sections['content'] ??= $content;
        return (new self($this->engine, $layout, $this->sections))->render($layoutData + $data);
    }

    /**
     * Get the template engine.
     */
    public function getEngine(): TemplateEngine
    {
        return $this->engine;
    }

    /**
     * Extend a layout: the output of this template becomes its `content` section.
     *
     * @param array<string, mixed> $data Additional variables for the layout
     */
    public function layout(string $name, array $data = []): void
    {
        $this->layout = [$name, $data];
    }

    /**
     * Start capturing a section.
     */
    public function start(string $name): void
    {
        $this->sectionStack[] = $name;
        ob_start();
    }

    /**
     * Stop capturing the current section.
     */
    public function stop(): void
    {
        $name = array_pop($this->sectionStack) ?? throw new AssertionError("No section was started in {$this->name}");
        // Sections filled by a child template take precedence over the defaults of the parent
        $content = ob_get_clean();
        $this->sections[$name] ??= $content;
    }

    /**
     * Output a section.
     *
     * @param string $default Default HTML if the section was not filled
     */
    public function section(string $name, string $default = ''): SafeHtml
    {
        return new SafeHtml($this->sections[$name] ?? $default);
    }

    /**
     * Check whether a section was filled.
     */
    public function hasSection(string $name): bool
    {
        return isset($this->sections[$name]);
    }

    /**
     * Render another template (partial) in place.
     *
     * @param array<string, mixed> $data Variables passed to the template
     */
    public function insert(string $name, array $data = []): SafeHtml
    {
        return new SafeHtml($this->engine->render($name, $data));
    }

    /**
     * Get the contents of an asset, to inline it in a `<style>` or `<script>` tag.
     */
    public function asset(string $file): SafeHtml
    {
        return new SafeHtml($this->engine->asset($file));
    }

    /**
     * Get the contents of a script asset, to inline it in a `<script>` tag.
     *
     * A `</script` in the script (in a string literal, or in a comment) would close the tag early, and a
     * `<!--` followed by `<script` would keep the real `</script>` from closing it: they're escaped to
     * `<\/script` and `<\!--`, which mean the same in JavaScript strings and comments.
     */
    public function script(string $file): SafeHtml
    {
        return new SafeHtml(self::escapeScript($this->engine->asset($file)));
    }

    /**
     * Escape a script to inline it in a `<script>` tag, see {@see self::script()}.
     */
    public static function escapeScript(string $script): string
    {
        return str_replace('<!--', '<\\!--', str_ireplace('</script', '<\\/script', $script));
    }

    /**
     * Escape a value for output in HTML text or in a quoted attribute.
     */
    public function e(mixed $value): string
    {
        if ($value instanceof SafeHtml) {
            return $value->html;
        }
        if ($value === null || $value === false) {
            return '';
        }
        if (\is_scalar($value) || $value instanceof Stringable) {
            return htmlspecialchars((string) $value, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML5, 'UTF-8');
        }
        throw new AssertionError('Cannot output a value of type '.get_debug_type($value));
    }

    /**
     * Build an HTML attribute list: `true` outputs just the name, `null` and `false` skip the attribute.
     *
     * @param array<string, scalar|Stringable|null> $attributes
     */
    public function attrs(array $attributes): SafeHtml
    {
        $result = '';
        foreach ($attributes as $name => $value) {
            if ($value === null || $value === false) {
                continue;
            }
            $result .= ' '.$this->e($name);
            if ($value !== true) {
                $result .= '="'.$this->e($value).'"';
            }
        }
        return new SafeHtml($result);
    }

    /**
     * Translate a string, escaping it.
     *
     * @param string|int|float ...$args sprintf arguments
     */
    public function t(string $key, string|int|float ...$args): string
    {
        return $this->e(self::translate($key, ...$args));
    }

    /**
     * Translate a string that contains trusted HTML (like links), escaping only the arguments.
     *
     * @param string|int|float ...$args sprintf arguments
     */
    public function tHtml(string $key, string|int|float ...$args): SafeHtml
    {
        return new SafeHtml(self::translate(
            $key,
            ...array_map(fn (string|int|float $arg): string|int|float => \is_string($arg) ? $this->e($arg) : $arg, $args),
        ));
    }

    /**
     * Translate a string.
     *
     * @param string|int|float ...$args sprintf arguments
     */
    public static function translate(string $key, string|int|float ...$args): string
    {
        $string = Lang::$current_lang[$key] ?? Lang::$lang['en'][$key] ?? $key;
        return $args ? \sprintf($string, ...$args) : $string;
    }

    private function includeTemplate(string $__file, array $__data): void
    {
        extract($__data, EXTR_SKIP);
        include $__file;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use danog\MadelineProto\Settings\AppInfo;
use danog\MadelineProto\Settings\Auth;
use danog\MadelineProto\Settings\Connection;
use danog\MadelineProto\Settings\Database\Memory as DatabaseMemory;
use danog\MadelineProto\Settings\DatabaseAbstract;
use danog\MadelineProto\Settings\Files;
use danog\MadelineProto\Settings\Ipc;
use danog\MadelineProto\Settings\Logger;
use danog\MadelineProto\Settings\Metrics;
use danog\MadelineProto\Settings\Peer;
use danog\MadelineProto\Settings\RPC;
use danog\MadelineProto\Settings\SecretChats;
use danog\MadelineProto\Settings\Serialization;
use danog\MadelineProto\Settings\Templates;
use danog\MadelineProto\Settings\TLSchema;
use danog\MadelineProto\Settings\VoIP;

/**
 * Settings class used for configuring MadelineProto.
 */
final class Settings extends SettingsAbstract
{
    /**
     * App information.
     */
    protected AppInfo $appInfo;
    /**
     * Cryptography settings.
     */
    protected Auth $auth;
    /**
     * Connection settings.
     */
    protected Connection $connection;
    /**
     * File management settings.
     */
    protected Files $files;
    /**
     * Metrics settings.
     */
    protected Metrics $metrics;
    /**
     * IPC server settings.
     */
    protected Ipc $ipc;
    /**
     * Logger settings.
     */
    protected Logger $logger;
    /**
     * Peer database settings.
     */
    protected Peer $peer;
    /**
     * RPC settings.
     */
    protected RPC $rpc;
    /**
     * Secret chat settings.
     */
    protected SecretChats $secretChats;
    /**
     * Serialization settings.
     */
    protected Serialization $serialization;
    /**
     * TL schema settings.
     */
    protected TLSchema $schema;
    /**
     * DatabaseAbstract settings.
     */
    protected DatabaseAbstract $db;
    /**
     * Template settings.
     */
    protected Templates $templates;
    /**
     * VoIP settings.
     */
    protected VoIP $voip;

    /**
     * Constructor.
     */
    public function __construct()
    {
        $this->appInfo = new AppInfo;
        $this->auth = new Auth;
        $this->connection = new Connection;
        $this->files = new Files;
        $this->logger = new Logger;
        $this->peer = new Peer;
        $this->metrics = new Metrics;
        $this->rpc = new RPC;
        $this->secretChats = new SecretChats;
        $this->serialization = new Serialization;
        $this->schema = new TLSchema;
        $this->db = new DatabaseMemory;
        $this->templates = new Templates;
        $this->ipc = new IPc;
        $this->voip = new VoIP;
    }
    /**
     * @psalm-external-mutation-free
     */
    public function __wakeup(): void
    {
        if (!isset($this->voip)) {
            $this->voip = new VoIP;
        }
        if (!isset($this->metrics)) {
            $this->metrics = new Metrics;
        }
    }
    /**
     * Merge another instance of settings.
     *
     * @param SettingsAbstract $settings Settings
     *
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function merge(SettingsAbstract $settings): void
    {
        if (!$settings instanceof self) {
            if ($settings instanceof AppInfo) {
                $this->appInfo->merge($settings);
            } elseif ($settings instanceof Auth) {
                $this->auth->merge($settings);
            } elseif ($settings instanceof Connection) {
                $this->connection->merge($settings);
            } elseif ($settings instanceof Files) {
                $this->files->merge($settings);
            } elseif ($settings instanceof Metrics) {
                $this->metrics->merge($settings);
            } elseif ($settings instanceof Logger) {
                $this->logger->merge($settings);
            } elseif ($settings instanceof Peer) {
                $this->peer->merge($settings);
            } elseif ($settings instanceof RPC) {
                $this->rpc->merge($settings);
            } elseif ($settings instanceof SecretChats) {
                $this->secretChats->merge($settings);
            } elseif ($settings instanceof Serialization) {
                $this->serialization->merge($settings);
            } elseif ($settings instanceof TLSchema) {
                $this->schema->merge($settings);
            } elseif ($settings instanceof Ipc) {
                $this->ipc->merge($settings);
            } elseif ($settings instanceof Templates) {
                $this->templates->merge($settings);
            } elseif ($settings instanceof VoIP) {
                $this->voip->merge($settings);
            } elseif ($settings instanceof DatabaseAbstract) {
                if (!$this->db instanceof $settings) {
                    $this->db = $settings;
                } else {
                    $this->db->merge($settings);
                }
            }
            return;
        }
        $this->appInfo->merge($settings->appInfo);
        $this->auth->merge($settings->auth);
        $this->connection->merge($settings->connection);
        $this->files->merge($settings->files);
        $this->metrics->merge($settings->metrics);
        $this->logger->merge($settings->logger);
        $this->peer->merge($settings->peer);
        $this->rpc->merge($settings->rpc);
        $this->secretChats->merge($settings->secretChats);
        $this->serialization->merge($settings->serialization);
        $this->schema->merge($settings->schema);
        $this->ipc->merge($settings->ipc);
        $this->templates->merge($settings->templates);
        $this->voip->merge($settings->voip);

        if (!$this->db instanceof $settings->db) {
            $this->db = $settings->db;
        } else {
            $this->db->merge($settings->db);
        }
    }

    /**
     * Get app information.
     */
    public function getAppInfo(): AppInfo
    {
        return $this->appInfo;
    }

    /**
     * Set app information.
     *
     * @param AppInfo $appInfo App information.
     *
     * @psalm-external-mutation-free
     */
    public function setAppInfo(AppInfo $appInfo): self
    {
        $this->appInfo = $appInfo;

        return $this;
    }

    /**
     * Get cryptography settings.
     */
    public function getAuth(): Auth
    {
        return $this->auth;
    }

    /**
     * Set cryptography settings.
     *
     * @param Auth $auth Cryptography settings.
     *
     * @psalm-external-mutation-free
     */
    public function setAuth(Auth $auth): self
    {
        $this->auth = $auth;

        return $this;
    }

    /**
     * Get connection settings.
     */
    public function getConnection(): Connection
    {
        return $this->connection;
    }

    /**
     * Set connection settings.
     *
     * @param Connection $connection Connection settings.
     *
     * @psalm-external-mutation-free
     */
    public function setConnection(Connection $connection): self
    {
        $this->connection = $connection;

        return $this;
    }

    /**
     * Get file management settings.
     */
    public function getFiles(): Files
    {
        return $this->files;
    }

    /**
     * Set file management settings.
     *
     * @param Files $files File management settings.
     *
     * @psalm-external-mutation-free
     */
    public function setFiles(Files $files): self
    {
        $this->files = $files;

        return $this;
    }

    /**
     * Get metrics settings.
     */
    public function getMetrics(): Metrics
    {
        return $this->metrics;
    }

    /**
     * Set metrics settings.
     *
     * @param Metrics $metrics File management settings.
     *
     * @psalm-external-mutation-free
     */
    public function setMetrics(Metrics $metrics): self
    {
        $this->metrics = $metrics;

        return $this;
    }

    /**
     * Get logger settings.
     */
    public function getLogger(): Logger
    {
        return $this->logger;
    }

    /**
     * Set logger settings.
     *
     * @param Logger $logger Logger settings.
     *
     * @psalm-external-mutation-free
     */
    public function setLogger(Logger $logger): self
    {
        $this->logger = $logger;

        return $this;
    }

    /**
     * Get peer database settings.
     */
    public function getPeer(): Peer
    {
        return $this->peer;
    }

    /**
     * Set peer database settings.
     *
     * @param Peer $peer Peer database settings.
     *
     * @psalm-external-mutation-free
     */
    public function setPeer(Peer $peer): self
    {
        $this->peer = $peer;

        return $this;
    }

    /**
     * Get RPC settings.
     */
    public function getRpc(): RPC
    {
        return $this->rpc;
    }

    /**
     * Set RPC settings.
     *
     * @param RPC $rpc RPC settings.
     *
     * @psalm-external-mutation-free
     */
    public function setRpc(RPC $rpc): self
    {
        $this->rpc = $rpc;

        return $this;
    }

    /**
     * Get secret chat settings.
     */
    public function getSecretChats(): SecretChats
    {
        return $this->secretChats;
    }

    /**
     * Set secret chat settings.
     *
     * @param SecretChats $secretChats Secret chat settings.
     *
     * @psalm-external-mutation-free
     */
    public function setSecretChats(SecretChats $secretChats): self
    {
        $this->secretChats = $secretChats;

        return $this;
    }

    /**
     * Get serialization settings.
     */
    public function getSerialization(): Serialization
    {
        return $this->serialization;
    }

    /**
     * Set serialization settings.
     *
     * @param Serialization $serialization Serialization settings.
     *
     * @psalm-external-mutation-free
     */
    public function setSerialization(Serialization $serialization): self
    {
        $this->serialization = $serialization;

        return $this;
    }

    /**
     * Get TL schema settings.
     */
    public function getSchema(): TLSchema
    {
        return $this->schema;
    }

    /**
     * Set TL schema settings.
     *
     * @param TLSchema $schema TL schema settings.
     *
     * @psalm-external-mutation-free
     */
    public function setSchema(TLSchema $schema): self
    {
        $this->schema = $schema;

        return $this;
    }

    /**
     * Get database settings.
     */
    public function getDb(): DatabaseAbstract
    {
        return $this->db;
    }

    /**
     * Set database settings.
     *
     * @param DatabaseAbstract $db DatabaseAbstract settings.
     *
     * @psalm-external-mutation-free
     */
    public function setDb(DatabaseAbstract $db): self
    {
        $this->db = $db;

        return $this;
    }

    /**
     * Get IPC server settings.
     */
    public function getIpc(): Ipc
    {
        return $this->ipc;
    }

    /**
     * Set IPC server settings.
     *
     * @param Ipc $ipc IPC server settings.
     *
     * @psalm-external-mutation-free
     */
    public function setIpc(Ipc $ipc): self
    {
        $this->ipc = $ipc;

        return $this;
    }

    /**
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function applyChanges(): SettingsAbstract
    {
        foreach (get_object_vars($this) as $setting) {
            if ($setting instanceof SettingsAbstract) {
                $setting->applyChanges();
            }
        }
        return $this;
    }

    /**
     * Get template settings.
     */
    public function getTemplates(): Templates
    {
        return $this->templates;
    }

    /**
     * Set template settings.
     *
     * @param Templates $templates Template settings
     *
     * @psalm-external-mutation-free
     */
    public function setTemplates(Templates $templates): self
    {
        $this->templates = $templates;

        return $this;
    }

    /**
     * Get voIP settings.
     */
    public function getVoip(): VoIP
    {
        return $this->voip;
    }

    /**
     * Set voIP settings.
     *
     * @param VoIP $voip VoIP settings.
     *
     * @psalm-external-mutation-free
     */
    public function setVoip(VoIP $voip): self
    {
        $this->voip = $voip;

        return $this;
    }
}
<?php declare(strict_types=1);

namespace phpseclib3\Math;

if (!class_exists(BigInteger::class)) {
    /**
     * Loads the BigIntegers of sessions created with phpseclib 3.
     *
     * phpseclib up to 3.0.45 serialized BigInteger through __sleep, which stores its private $hex under
     * the mangled name "\0phpseclib3\Math\BigInteger\0hex": phpseclib 4's __unserialize only reads 'hex',
     * so a plain class_alias made it throw in the middle of the unserialization of the whole session.
     *
     * @internal
     */
    class BigInteger extends \phpseclib4\Math\BigInteger
    {
        public function __unserialize(array $data): void
        {
            $private = "\0".self::class."\0";
            $hex = $data['hex'] ?? $data[$private.'hex'] ?? null;
            if (!\is_string($hex)) {
                throw new \UnexpectedValueException('Invalid serialized phpseclib3 BigInteger');
            }
            $precision = $data['precision'] ?? $data[$private.'precision'] ?? null;
            parent::__unserialize(\is_int($precision) ? ['hex' => $hex, 'precision' => $precision] : ['hex' => $hex]);
        }
    }
}
<?php

declare(strict_types=1);

namespace danog\MadelineProto\Ipc\Runner;

/**
 * IPC server entry module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

use Amp\SignalException;
use danog\MadelineProto\API;
use danog\MadelineProto\Ipc\AlreadyServedException;
use danog\MadelineProto\Ipc\IpcState;
use danog\MadelineProto\Ipc\Server;
use danog\MadelineProto\Logger;
use danog\MadelineProto\Magic;
use danog\MadelineProto\SecurityException;
use danog\MadelineProto\SessionPaths;
use danog\MadelineProto\Settings\Ipc;
use Revolt\EventLoop\UncaughtThrowable;
use Webmozart\Assert\Assert;

(static function (): void {
    if (\defined('MADELINE_ENTRY')) {
        // Already called
        return;
    }
    \define('MADELINE_ENTRY', 1);
    if (!\defined('MADELINE_WORKER_TYPE')) {
        if (\count(debug_backtrace(0)) !== 1) {
            // We're not being included directly
            return;
        }
        $arguments = [];
        if (isset($GLOBALS['argv']) && !empty($GLOBALS['argv'])) {
            $arguments = \array_slice($GLOBALS['argv'], 1);
        } elseif (isset($_GET['argv']) && !empty($_GET['argv'])) {
            $arguments = $_GET['argv'];
        }
        if (\count($arguments) < 2) {
            trigger_error('Not enough arguments!', E_USER_ERROR);
            exit(1);
        }
        \define('MADELINE_WORKER_TYPE', array_shift($arguments));
        \define('MADELINE_WORKER_ARGS', $arguments);
    }

    // Not under a web server SAPI: the process belongs to the web server, which handles the signals itself
    if (\defined('SIGHUP') && PHP_SAPI === 'cli') {
        try {
            pcntl_signal(SIGHUP, static fn () => null);
        } catch (\Throwable $e) {
        }
    }
    if (!class_exists(API::class)) {
        $paths = [
            \dirname(__DIR__, 5).'/autoload.php',
            \dirname(__DIR__, 3).'/vendor/autoload.php',
            \dirname(__DIR__, 7).'/autoload.php',
            \dirname(__DIR__, 5).'/vendor/autoload.php',
        ];

        foreach ($paths as $path) {
            if (file_exists($path)) {
                $autoloadPath = $path;
                break;
            }
        }

        if (!isset($autoloadPath)) {
            trigger_error('Could not locate autoload.php in any of the following files: '.implode(', ', $paths), E_USER_ERROR);
            exit(1);
        }

        include $autoloadPath;
    }
    if (MADELINE_WORKER_TYPE === 'madeline-ipc') {
        $session = MADELINE_WORKER_ARGS[0];
        if (!file_exists($session)) {
            trigger_error("IPC session $session does not exist!", E_USER_ERROR);
            exit(1);
        }
        if (\function_exists('cli_set_process_title')) {
            @cli_set_process_title("MadelineProto worker $session");
        }
        if (\function_exists('posix_setsid')) {
            @posix_setsid();
        }
        if (isset($_GET['cwd'])) {
            @chdir($_GET['cwd']);
        }
        \define('MADELINE_WORKER', 1);

        $runnerId = MADELINE_WORKER_ARGS[1];
        Assert::numeric($runnerId);
        $runnerId = (int) $runnerId;

        try {
            Magic::start(light: false);
            Magic::$script_cwd = $_GET['cwd'] ?? Magic::getcwd();

            $session = new SessionPaths($session);
            $API = new API((string) $session, new Ipc);
            $API->initSelfRestart();
            $session->storeIpcState(new IpcState($runnerId));
            while (true) {
                try {
                    Server::waitShutdown();
                    Logger::log('A restart was triggered!', Logger::FATAL_ERROR);
                    return;
                } catch (\Throwable $e) {
                    if ($e instanceof UncaughtThrowable) {
                        $e = $e->getPrevious();
                    }
                    if ($e instanceof SecurityException || $e instanceof SignalException) {
                        throw $e;
                    }
                    Logger::log((string) $e, Logger::FATAL_ERROR);
                    $API->report("Surfaced: $e");
                }
            }
        } catch (AlreadyServedException $e) {
            // Started concurrently with the server that won the session lock: nothing to report
            Logger::log($e->getMessage().', exiting...');
        } catch (\Throwable $e) {
            echo "$e";
            echo 'Got exception in IPC server, exiting...';

            Logger::log("$e", Logger::FATAL_ERROR);
            Logger::log('Got exception in IPC server, exiting...', Logger::FATAL_ERROR);
            $ipc = $session->getIpcState();
            if (!($ipc && $ipc->getStartupId() === $runnerId && !$ipc->getException())
                && !(
                    isset($API)
                    && $API->getAuthorization() === API::LOGGED_OUT
                )
            ) {
                Logger::log('Reporting error!');
                $session->storeIpcState(new IpcState($runnerId, $e));
                Logger::log('Reported error!');
            } else {
                Logger::log('Not reporting error!');
            }
        }
    }
})();
<?php

declare(strict_types=1);

/**
 * Callback module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Wrappers;

use Revolt\EventLoop;

/**
 * Manages clicking buttons.
 *
 * @internal
 */
trait Button
{
    /**
     * Click on button.
     *
     * @internal
     * @return array|true
     */
    public function clickInternal(bool $donotwait, string $method, array $parameters): array|bool
    {
        if ($donotwait) {
            EventLoop::queue($this->methodCallAsyncRead(...), $method, $parameters);
            return true;
        }
        return $this->methodCallAsyncRead($method, $parameters);
    }
}
<?php

declare(strict_types=1);

/**
 * DialogHandler module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Wrappers;

use Amp\Sync\LocalMutex;
use danog\MadelineProto\API;
use danog\MadelineProto\Exception;
use danog\MadelineProto\MTProto\SpecialMethodType;
use danog\MadelineProto\Settings;
use danog\MadelineProto\Tools;
use Revolt\EventLoop;
use Throwable;
use Webmozart\Assert\Assert;

/**
 * Dialog handler.
 *
 * @property Settings $settings Settings
 *
 * @internal
 */
trait DialogHandler
{
    private array $botDialogsUpdatesState = [
        'qts' => 1,
        'pts' => 1,
        'date' => 1,
    ];
    private bool $cachedAllBotUsers = false;
    private ?LocalMutex $cachingAllBotUsers = null;
    private bool $searchingRightPts = false;
    private int $bottomPts = 0;
    private int $topPts = 0;

    private function cacheAllBotUsers(): void
    {
        if ($this->cachedAllBotUsers) {
            return;
        }
        $this->cachingAllBotUsers ??= new LocalMutex;
        $lock = $this->cachingAllBotUsers->acquire();
        try {
            if ($this->cachedAllBotUsers) {
                return;
            }
            if ($this->searchingRightPts) {
                $this->searchRightPts();
            }
            while (true) {
                /** @psalm-suppress InvalidOperand */
                $result = $this->methodCallAsyncRead(
                    'updates.getDifference',
                    [
                        ...$this->botDialogsUpdatesState,
                        'pts_total_limit' => 2147483647,
                        'floodWaitLimit' => 86400,
                        'specialMethodType' => SpecialMethodType::USER_RELATED,
                    ],
                );
                switch ($result['_']) {
                    case 'updates.differenceEmpty':
                        break 2;
                    case 'updates.difference':
                        $this->botDialogsUpdatesState = $result['state'];
                        break;
                    case 'updates.differenceSlice':
                        $this->botDialogsUpdatesState = $result['intermediate_state'];
                        break;
                    case 'updates.differenceTooLong':
                        $this->bottomPts = $this->botDialogsUpdatesState['pts'];
                        $this->topPts = $result['pts'];
                        $this->searchingRightPts = true;
                        $this->searchRightPts();
                        break;
                    default:
                        throw new Exception('Unrecognized update difference received: '.var_export($result, true));
                }
            }
            $this->cachedAllBotUsers = true;
        } finally {
            EventLoop::queue($lock->release(...));
        }
    }
    private function searchRightPts(): void
    {
        Assert::true($this->searchingRightPts);
        $this->logger->logger("Searching for the right PTS (will take a loooong time...)...");
        try {
            $state = $this->botDialogsUpdatesState;
            $state['pts_total_limit'] = 2147483647;
            while ($this->bottomPts <= $this->topPts) {
                $state['pts'] = ($this->bottomPts+$this->topPts)>>1;
                try {
                    $result = $this->methodCallAsyncRead(
                        'updates.getDifference',
                        $state + [
                            'cancellation' => Tools::getTimeoutCancellation(
                                15.0,
                                "Timeout while getting difference for searchRightPts"
                            ),
                            'floodWaitLimit' => 86400,
                        ],
                    )['_'];
                } catch (Throwable $e) {
                    $this->logger->logger("Got {$e->getMessage()} while getting difference, trying another PTS...");
                    $result = 'updates.differenceTooLong';
                }
                $this->logger("{$this->bottomPts}, {$state['pts']}, {$this->topPts}");
                $this->logger($result);
                if ($result === 'updates.differenceTooLong') {
                    $this->bottomPts = $state['pts']+1;
                } else {
                    $this->topPts = $state['pts']-1;
                }
            }
            $this->botDialogsUpdatesState['pts'] = $this->bottomPts;
            $this->logger("Found PTS {$this->bottomPts}");
        } finally {
            $this->searchingRightPts = false;
        }
    }
    /**
     * Get dialog IDs.
     *
     * @return list<int>
     */
    public function getDialogIds(): array
    {
        if ($this->authorization['user']['bot']) {
            $this->cacheAllBotUsers();
            return $this->peerDatabase->getDialogIds();
        }
        return array_keys($this->getFullDialogs());
    }
    /**
     * Get full info of all dialogs.
     *
     * Bots should use getDialogIds, instead.
     *
     * @return array<int, array>
     */
    public function getFullDialogs(): array
    {
        return $this->getFullDialogsInternal(true);
    }
    private bool $fetchedFullDialogs = false;
    private ?LocalMutex $cacheFullDialogsMutex = null;
    private function cacheFullDialogs(): bool
    {
        if ($this->loginState->getState()->state === API::LOGGED_IN && !$this->authorization['user']['bot'] && $this->settings->getPeer()->getCacheAllPeersOnStartup() && !$this->fetchedFullDialogs) {
            $this->cacheFullDialogsMutex ??= new LocalMutex;
            $lock = $this->cacheFullDialogsMutex->acquire();
            try {
                $this->getFullDialogsInternal(false);
            } finally {
                EventLoop::queue($lock->release(...));
            }
            return true;
        }
        return false;
    }
    /**
     * Get full info of all dialogs.
     *
     * @param boolean $force Whether to refetch all dialogs ignoring cache
     *
     * @return array<int, array>
     */
    private function getFullDialogsInternal(bool $force): array
    {
        if ($this->authorization['user']['bot']) {
            throw new Exception("You're logged in as a bot: please use getDialogsIds, instead.");
        }
        if ($force || !isset($this->dialog_params['offset_date']) || \is_null($this->dialog_params['offset_date']) || !isset($this->dialog_params['offset_id']) || \is_null($this->dialog_params['offset_id']) || !isset($this->dialog_params['offset_peer']) || \is_null($this->dialog_params['offset_peer']) || !isset($this->dialog_params['count']) || \is_null($this->dialog_params['count'])) {
            $this->dialog_params = ['limit' => 100, 'offset_date' => 0, 'offset_id' => 0, 'offset_peer' => ['_' => 'inputPeerEmpty'], 'count' => 0, 'hash' => 0];
        }
        if (!isset($this->dialog_params['hash'])) {
            $this->dialog_params['hash'] = 0;
        }
        $res = ['dialogs' => [0], 'count' => 1];
        $dialogs = [];
        $this->logger->logger('Getting dialogs...');
        while ($this->dialog_params['count'] < $res['count']) {
            $res = $this->methodCallAsyncRead('messages.getDialogs', $this->dialog_params + ['floodWaitLimit' => 100]);
            $last_peer = 0;
            $last_date = 0;
            $last_id = 0;
            $res['messages'] = array_reverse($res['messages'] ?? []);
            foreach (array_reverse($res['dialogs'] ?? []) as $dialog) {
                $id = $this->getIdInternal($dialog['peer']);
                if (!isset($dialogs[$id])) {
                    $dialogs[$id] = $dialog;
                }
                if (!$last_date) {
                    if (!$last_peer) {
                        $last_peer = $id;
                    }
                    if (!$last_id) {
                        $last_id = $dialog['top_message'];
                    }
                    foreach ($res['messages'] as $message) {
                        if ($message['_'] !== 'messageEmpty' && $this->getIdInternal($message) === $last_peer && $last_id === $message['id']) {
                            $last_date = $message['date'];
                            break;
                        }
                    }
                }
            }
            if ($last_date) {
                $this->dialog_params['offset_date'] = $last_date;
                $this->dialog_params['offset_peer'] = $last_peer;
                $this->dialog_params['offset_id'] = $last_id;
                $this->dialog_params['count'] = \count($dialogs);
            } else {
                break;
            }
            if (!isset($res['count'])) {
                break;
            }
        }
        $this->fetchedFullDialogs = true;
        return $dialogs;
    }
}
<?php

declare(strict_types=1);

/**
 * Loop module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Wrappers;

use Amp\DeferredFuture;
use Amp\SignalException;
use danog\MadelineProto\Exception;
use danog\MadelineProto\Ipc\Runner\WebRunner;
use danog\MadelineProto\Logger;
use danog\MadelineProto\Magic;
use danog\MadelineProto\Settings;
use danog\MadelineProto\Shutdown;
use danog\MadelineProto\Tools;
use Revolt\EventLoop;
use Throwable;

use const PHP_SAPI;

/**
 * Manages logging in and out.
 *
 * @property Settings $settings Settings
 *
 * @internal
 */
trait Loop
{
    private ?DeferredFuture $stopDeferred = null;
    /**
     * Initialize self-restart hack.
     */
    public function initSelfRestart(): void
    {
        static $inited = false;
        if (PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg' && !$inited) {
            try {
                if (\function_exists('set_time_limit')) {
                    set_time_limit(-1);
                }
            } catch (Exception) {
            }
            if (isset($_REQUEST['MadelineSelfRestart'])) {
                $this->logger->logger('Self-restarted, restart token '.$_REQUEST['MadelineSelfRestart']);
            }
            $this->logger->logger('Will self-restart');
            $this->logger->logger('Adding restart callback!');
            $logger = $this->logger;
            $id = Shutdown::addCallback(static function () use (&$logger): void {
                $params = $_GET;
                $params['MadelineSelfRestart'] = Tools::randomInt();
                $url = explode('?', $_SERVER['REQUEST_URI'] ?? '', 2)[0] ?? '';
                $query = http_build_query($params);

                /** @psalm-suppress InternalMethod */
                WebRunner::selfStart("$url?$query");

                $logger->logger("Payload sent with token {$params['MadelineSelfRestart']}, waiting for self-restart");
                $logger->logger('Shutdown of self-restart callback');
            }, 'restarter');
            $this->logger->logger("Added restart callback with ID $id!");
            $this->logger->logger('Done webhost init process!');
            Tools::closeConnection($this->getWebMessage('The bot was started!'));
            $inited = true;
        } elseif (PHP_SAPI === 'cli') {
            try {
                if (\function_exists('shell_exec') && file_exists('/data/data/com.termux/files/usr/bin/termux-wake-lock')) {
                    /** @psalm-suppress ForbiddenCode */
                    shell_exec('/data/data/com.termux/files/usr/bin/termux-wake-lock');
                }
            } catch (Throwable) {
            }
        }
    }
    /**
     * Start MadelineProto's update handling loop.
     *
     * @internal
     */
    public function loop()
    {
        if (!$this->getAuthorization()) {
            $this->logger->logger('Not authorized, not starting event loop', Logger::FATAL_ERROR);
            return false;
        }
        $this->logger->logger('Starting event loop');
        $this->initSelfRestart();
        $this->logger->logger('Started update loop', Logger::NOTICE);
        $this->stopDeferred ??= new DeferredFuture;
        $this->stopDeferred->getFuture()->await();
        $this->logger->logger('Exiting update loop!', Logger::NOTICE);
    }
    /**
     * Stop update loop.
     */
    public function stop(): void
    {
        if (!$this->hasEventHandler()) {
            throw new Exception("Can't use this method if no event handler is running!");
        }
        Shutdown::removeCallback('restarter');
        $this->restart();
    }
    /**
     * Restart update loop.
     */
    public function restart(): void
    {
        if (!$this->hasEventHandler()) {
            if (Magic::$isIpcWorker) {
                EventLoop::queue(static fn () => throw new SignalException('Restarting IPC daemon!'));
            }
            return;
        }
        $deferred = $this->stopDeferred ?? new DeferredFuture;
        $this->stopDeferred = null;
        $deferred->complete();
    }
}
<?php

declare(strict_types=1);

/**
 * Ads module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Wrappers;

use danog\AsyncOrm\DbArray;

/**
 * Manages ads.
 *
 * @property DbArray $sponsoredMessages
 *
 * @internal
 */
trait Ads
{
    /**
     * Get sponsored messages for channel.
     * This method will return an array of [sponsored message objects](https://docs.madelineproto.xyz/API_docs/constructors/sponsoredMessage.html).
     *
     * See [the API documentation](https://core.telegram.org/api/sponsored-messages) for more info on how to handle sponsored messages.
     *
     * @param int|string|array $peer Channel ID, or Update, or Message, or Peer.
     */
    public function getSponsoredMessages(int|string|array $peer): ?array
    {
        $peer = ($this->getInfo($peer))['bot_api_id'];
        $cache = $this->sponsoredMessages[$peer];
        if ($cache && $cache[0] > time()) {
            return $cache[1];
        }
        $result = $this->methodCallAsyncRead('messages.getSponsoredMessages', ['peer' => $peer]);
        if (\array_key_exists('messages', $result)) {
            $result = $result['messages'];
        } else {
            $result = null;
        }

        $this->sponsoredMessages[$peer] = [time() + 5*60, $result];
        return $result;
    }
    /**
     * Mark sponsored message as read.
     *
     * @param int|array                       $peer    Channel ID, or Update, or Message, or Peer.
     * @param string|array{random_id: string} $message Random ID or sponsored message to mark as read.
     */
    public function viewSponsoredMessage(int|array $peer, string|array $message): bool
    {
        if (\is_array($message)) {
            $message = $message['random_id'];
        }
        return $this->methodCallAsyncRead('messages.viewSponsoredMessage', ['peer' => $peer, 'random_id' => $message]);
    }
}
<?php

declare(strict_types=1);

/**
 * Start module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Wrappers;

use Amp\CancelledException;
use Amp\CompositeCancellation;
use danog\MadelineProto\API;
use danog\MadelineProto\Exception;
use danog\MadelineProto\Ipc\Client;
use danog\MadelineProto\Lang;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\RPCErrorException;
use danog\MadelineProto\Settings;
use danog\MadelineProto\Tools;
use danog\MadelineProto\WebUI\Alert;
use danog\MadelineProto\WebUI\Field;
use danog\MadelineProto\WebUI\FieldType;
use danog\MadelineProto\WebUI\Page;
use danog\MadelineProto\WebUI\Step;
use danog\MadelineProto\WebUI\WebUI;

use const PHP_SAPI;

use function Amp\ByteStream\getOutputBufferStream;
use function Amp\ByteStream\getStdout;

/**
 * Manages simple logging in and out.
 *
 * @property Settings $settings Settings
 *
 * @internal
 */
trait Start
{
    /**
     * Log in to telegram (via CLI or web).
     */
    public function start(): array
    {
        if ($this->getAuthorization() === \danog\MadelineProto\API::LOGGED_IN) {
            // Requests of the web UI that come after the login (a late QR code poll, a form submitted twice, or
            // from another tab): they're answered here, the script must not run in them (it runs in the page)
            if (PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg') {
                if (isset($_GET['waitQrCodeOrLogin']) || isset($_GET['getQrCode'])) {
                    $this->webQrLogin();
                    die;
                }
                if (WebUI::writeLoggedIn()) {
                    die;
                }
            }
            return $this instanceof Client ? $this->getSelf() : $this->fullGetSelf();
        }
        if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
            if ($this->getAuthorization() === API::NOT_LOGGED_IN) {
                $stdout = getStdout();
                do {
                    $qr = $this->qrLogin();
                    if (!$qr) {
                        $this->serialize();
                        return $this->fullGetSelf();
                    }
                    $stdout->write($qr->getQRText(2));

                    $expire = $qr->getExpirationCancellation();
                    $login = $qr->getLoginCancellation();

                    $cancel = new CompositeCancellation($expire, $login);

                    try {
                        // A phone number, or a bot token, censored after its colon.
                        $result = Tools::readLine(Lang::$current_lang['loginQr'].PHP_EOL.Lang::$current_lang['loginManual'], $cancel, hideAfter: ':');
                        break;
                    } catch (CancelledException) {
                        if ($login->isRequested()) {
                            $stdout->write(PHP_EOL.PHP_EOL.Lang::$current_lang['loginQrCodeSuccessful'].PHP_EOL);
                            if ($this->getAuthorization() === \danog\MadelineProto\API::WAITING_PASSWORD) {
                                $this->complete2faLogin(Tools::readLine($this->getPasswordPrompt(), hidden: true));
                            }
                            $this->serialize();
                            return $this->fullGetSelf();
                        }

                        $stdout->write(PHP_EOL.Lang::$current_lang['loginQrCodeExpired'].PHP_EOL);
                    }
                } while (true);
                if (str_contains($result, ':')) {
                    $this->botLogin($result);
                } else {
                    $this->phoneLogin($result);
                }
            }
            if ($this->getAuthorization() === \danog\MadelineProto\API::WAITING_CODE) {
                $this->completePhoneLogin(Tools::readLine(Lang::$current_lang['loginUserCode'], hidden: true));
            }
            if ($this->getAuthorization() === \danog\MadelineProto\API::WAITING_PASSWORD) {
                $this->complete2faLogin(Tools::readLine($this->getPasswordPrompt(), hidden: true));
            }
            if ($this->getAuthorization() === \danog\MadelineProto\API::WAITING_SIGNUP) {
                $this->completeSignup(Tools::readLine(Lang::$current_lang['signupFirstName']), Tools::readLine(Lang::$current_lang['signupLastName']));
            }
            $this->serialize();
            return $this->fullGetSelf();
        }
        if (isset($_GET['waitQrCodeOrLogin']) || isset($_GET['getQrCode'])) {
            $this->webQrLogin();
            die;
        }
        $auth = $this->getAuthorization();
        $post = ($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'POST';
        if ($auth === API::NOT_LOGGED_IN) {
            $type = $_POST['type'] ?? (isset($_POST['token']) ? 'bot' : 'phone');
            if ($type === 'bot' && isset($_POST['token'])) {
                $this->webStep(fn () => $this->botLogin(trim((string) $_POST['token'])));
            } elseif ($type === 'phone' && isset($_POST['phone_number'])) {
                $this->webStep(fn () => $this->phoneLogin(trim((string) $_POST['phone_number'])));
            } else {
                $this->webEcho();
            }
        } elseif ($auth === API::WAITING_CODE) {
            if (isset($_POST['phone_code'])) {
                $this->webStep(fn () => $this->completePhoneLogin(trim((string) $_POST['phone_code'])));
            } else {
                $this->webEcho($post ? Alert::error(Lang::$current_lang['loginNoCode']) : null);
            }
        } elseif ($auth === API::WAITING_PASSWORD) {
            if (isset($_POST['password'])) {
                $this->webStep(fn () => $this->complete2faLogin((string) $_POST['password']));
            } else {
                $this->webEcho($post ? Alert::error(Lang::$current_lang['loginNoPass']) : null);
            }
        } elseif ($auth === API::WAITING_SIGNUP) {
            if (isset($_POST['first_name'])) {
                $this->webStep(fn () => $this->completeSignup(trim((string) $_POST['first_name']), trim((string) ($_POST['last_name'] ?? ''))));
            } else {
                $this->webEcho($post ? Alert::error(Lang::$current_lang['loginNoName']) : null);
            }
        }
        if ($this->getAuthorization() === API::LOGGED_IN) {
            $this->serialize();
            return $this->fullGetSelf();
        }
        die;
    }
    /**
     * Run a login step submitted from the web UI, then show the next one.
     *
     * @param callable(): mixed $step
     */
    private function webStep(callable $step): void
    {
        try {
            $step();
        } catch (RPCErrorException|Exception $e) {
            $this->webEcho(
                Alert::error(\sprintf(Lang::$current_lang['webError'], rtrim($e->getMessage(), '.!'))),
                $e instanceof RPCErrorException ? $e->rpc : null,
            );
            return;
        }
        if ($this->getAuthorization() === API::LOGGED_IN) {
            // The web app reloads the page: the script runs in that request, once
            if (WebUI::writeLoggedIn()) {
                $this->serialize();
                die;
            }
            // Otherwise, the script continues in this request: it must not run again after a redirect
            return;
        }
        if (!WebUI::redirectToSelf()) {
            $this->webEcho();
        }
    }
    /**
     * Answer the long polling requests of the QR code login panel.
     */
    private function webQrLogin(): void
    {
        $result = ['logged_in' => true];
        if ($this->getAuthorization() === API::NOT_LOGGED_IN) {
            try {
                $qr = $this->qrLogin();
                if (isset($_GET['waitQrCodeOrLogin'])) {
                    $qr = $qr?->waitForLoginOrQrCodeExpiration(
                        Tools::getTimeoutCancellation(5.0, "Timeout while waiting for QR code or login")
                    );
                }
            } catch (CancelledException) {
                $qr = $this->qrLogin();
            }
            if ($qr) {
                $result = [
                    'logged_in' => false,
                    'link' => $qr->link,
                    'svg' => $qr->getQRSvg(400, 2),
                ];
            }
        }
        // Otherwise, logged in (or waiting for the password): the page reloads
        if (!headers_sent()) {
            header('Content-Type: application/json');
            header('Cache-Control: no-store');
        }
        getOutputBufferStream()->write(json_encode($result));
    }

    /**
     * Show the page of the current login step.
     *
     * @param ?string $rpcError Type of the RPC error of the submitted step, for the web app
     */
    private function webEcho(?Alert $alert = null, ?string $rpcError = null): void
    {
        $page = $this->getWebLoginPage();
        if ($page === null) {
            return;
        }
        if ($alert !== null) {
            $page->addAlert($alert);
        }
        if ($rpcError !== null) {
            $page->data['rpcError'] = $rpcError;
        }
        WebUI::write($this->getSettings()->getTemplates(), $page);
    }

    /**
     * Get the page of the current login step.
     */
    private function getWebLoginPage(): ?Page
    {
        $alerts = MTProto::getWebAlerts();
        switch ($this->getAuthorization()) {
            case API::NOT_LOGGED_IN:
                $type = ($_POST['type'] ?? 'phone') === 'bot' ? 'bot' : 'phone';
                return new Page(
                    title: Lang::$current_lang['loginTitleWeb'],
                    view: 'login.signIn',
                    lead: Lang::$current_lang['loginLeadWeb'],
                    step: Step::ACCOUNT,
                    fields: [
                        new Field(
                            name: 'type',
                            label: Lang::$current_lang['loginTypeWeb'],
                            type: FieldType::CHOICE,
                            value: $type,
                            options: [
                                'phone' => Lang::$current_lang['loginOptionUser'],
                                'bot' => Lang::$current_lang['loginOptionBot'],
                            ],
                        ),
                        new Field(
                            name: 'phone_number',
                            label: Lang::$current_lang['loginUserPhoneWeb'],
                            type: FieldType::PHONE,
                            value: trim((string) ($_POST['phone_number'] ?? '')),
                            placeholder: '+39 123 456 7890',
                            hint: Lang::$current_lang['loginUserPhoneHintWeb'],
                            showWhen: ['type', 'phone'],
                            autofocus: $type === 'phone',
                        ),
                        new Field(
                            name: 'token',
                            label: Lang::$current_lang['loginBotTokenWeb'],
                            type: FieldType::PASSWORD,
                            placeholder: '123456789:AAEâ€¦',
                            autocomplete: 'off',
                            hint: Lang::$current_lang['loginBotTokenHintWeb'],
                            showWhen: ['type', 'bot'],
                            autofocus: $type === 'bot',
                        ),
                    ],
                    submit: Lang::$current_lang['webContinue'],
                    alerts: $alerts,
                    qrEndpoint: WebUI::getSelfUrl(true),
                );
            case API::WAITING_CODE:
                return new Page(
                    title: Lang::$current_lang['loginCodeTitleWeb'],
                    view: 'login.code',
                    data: $this->getWebLoginInfo(),
                    lead: Lang::$current_lang['loginCodeLeadWeb'],
                    step: Step::VERIFY,
                    fields: [
                        new Field(
                            name: 'phone_code',
                            label: Lang::$current_lang['loginUserPhoneCodeWeb'],
                            type: FieldType::CODE,
                            autofocus: true,
                            maxLength: 12,
                        ),
                    ],
                    submit: Lang::$current_lang['webLogin'],
                    alerts: $alerts,
                );
            case API::WAITING_PASSWORD:
                $hint = $this->getHint();
                return new Page(
                    title: Lang::$current_lang['loginPasswordTitleWeb'],
                    view: 'login.password',
                    data: ['hint' => $hint],
                    lead: Lang::$current_lang['loginPasswordLeadWeb'],
                    step: Step::VERIFY,
                    fields: [
                        new Field(
                            name: 'password',
                            label: Lang::$current_lang['loginPasswordLabelWeb'],
                            type: FieldType::PASSWORD,
                            hint: $hint === '' ? '' : \sprintf(Lang::$current_lang['loginUserPassHint'], $hint),
                            autofocus: true,
                        ),
                    ],
                    submit: Lang::$current_lang['webLogin'],
                    alerts: $alerts,
                );
            case API::WAITING_SIGNUP:
                return new Page(
                    title: Lang::$current_lang['signupTitleWeb'],
                    view: 'login.signUp',
                    data: $this->getWebLoginInfo(),
                    lead: Lang::$current_lang['signupLeadWeb'],
                    step: Step::VERIFY,
                    fields: [
                        new Field(
                            name: 'first_name',
                            label: Lang::$current_lang['signupFirstNameWeb'],
                            value: trim((string) ($_POST['first_name'] ?? '')),
                            autocomplete: 'given-name',
                            autofocus: true,
                        ),
                        new Field(
                            name: 'last_name',
                            label: Lang::$current_lang['signupLastNameWeb'],
                            value: trim((string) ($_POST['last_name'] ?? '')),
                            required: false,
                            autocomplete: 'family-name',
                        ),
                    ],
                    submit: Lang::$current_lang['webContinue'],
                    alerts: $alerts,
                );
        }
        return null;
    }

    /**
     * The CLI prompt of the 2FA password, with the hint of the account if it has one.
     */
    private function getPasswordPrompt(): string
    {
        $hint = $this->getHint();
        return $hint === ''
            ? Lang::$current_lang['loginUserPassWeb']
            : \sprintf(Lang::$current_lang['loginUserPass'], $hint);
    }
}
<?php

declare(strict_types=1);

/**
 * Login module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Wrappers;

use Amp\Cancellation;
use Amp\CancelledException;
use Amp\DeferredCancellation;
use Amp\DeferredFuture;
use Amp\Future;
use AssertionError;
use danog\MadelineProto\API;
use danog\MadelineProto\DataCenter;
use danog\MadelineProto\Exception;
use danog\MadelineProto\Lang;
use danog\MadelineProto\Logger;
use danog\MadelineProto\MTProto\ConnectionState;
use danog\MadelineProto\MTProto\LoginState;
use danog\MadelineProto\MTProto\SpecialMethodType;
use danog\MadelineProto\MTProtoTools\PasswordCalculator;
use danog\MadelineProto\Reactive\Publisher;
use danog\MadelineProto\RPCError\PasswordHashInvalidError;
use danog\MadelineProto\RPCError\SessionPasswordNeededError;
use danog\MadelineProto\RPCErrorException;
use danog\MadelineProto\Settings;
use danog\MadelineProto\TL\Types\LoginQrCode;
use danog\MadelineProto\Tools;

/**
 * Manages logging in and out.
 *
 * @property Settings     $settings    Settings
 * @property ?LoginQrCode $loginQrCode
 * @property Publisher<LoginState> $loginState
 * @property ?Future      $authFuture
 * @internal
 */
trait Login
{
    /**
     * Login as bot.
     *
     * @param string $token Bot token
     */
    public function botLogin(string $token): array|null
    {
        if ($this->loginState->getState()->state === \danog\MadelineProto\API::LOGGED_IN) {
            return null;
        }
        $callbacks = [$this, $this->referenceDatabase, $this->peerDatabase];
        /** @psalm-suppress InvalidArgument */
        $this->TL->updateCallbacks($callbacks);
        $this->logger->logger(Lang::$current_lang['login_bot'], Logger::NOTICE);
        $res = $this->methodCallAsyncRead(
            'auth.importBotAuthorization',
            [
                'bot_auth_token' => $token,
                'api_id' => $this->settings->getAppInfo()->getApiId(),
                'api_hash' => $this->settings->getAppInfo()->getApiHash(),
                'specialMethodType' => SpecialMethodType::USER_RELATED,
            ],
        );
        $this->authFuture?->await();
        return $res;
    }

    private ?DeferredCancellation $qrLoginDeferred = null;
    /**
     * Initiates QR code login.
     *
     * Returns a QR code login helper object, that can be used to render the QR code, display the link directly, wait for login, QR code expiration and much more.
     *
     * Returns null if we're already logged in, or if we're waiting for a password (use getAuthorization to distinguish between the two cases).
     */
    public function qrLogin(): ?LoginQrCode
    {
        $this->authFuture?->await();
        $s = $this->loginState->getState()->state;
        if ($s === \danog\MadelineProto\API::LOGGED_IN) {
            return null;
        } elseif ($s === \danog\MadelineProto\API::WAITING_PASSWORD) {
            return null;
        } elseif ($s !== API::NOT_LOGGED_IN) {
            throw new AssertionError("Unexpected state {$s}!");
        }
        $this->qrLoginDeferred ??= new DeferredCancellation;
        if (!$this->loginQrCode || $this->loginQrCode->isExpired()) {
            try {
                $authorization = $this->methodCallAsyncRead(
                    'auth.exportLoginToken',
                    [
                        'api_id' => $this->settings->getAppInfo()->getApiId(),
                        'api_hash' => $this->settings->getAppInfo()->getApiHash(),
                        'specialMethodType' => SpecialMethodType::USER_RELATED,
                    ],
                );
                if ($authorization['_'] === 'auth.loginToken') {
                    return $this->loginQrCode = new LoginQrCode(
                        $this,
                        "tg://login?token=".Tools::base64urlEncode((string) $authorization['token']),
                        $authorization['expires']
                    );
                }

                if ($authorization['_'] === 'auth.loginTokenMigrateTo') {
                    $datacenter = $this->isTestMode() ? 10_000 + $authorization['dc_id'] : $authorization['dc_id'];
                    $this->loginState->publish($this->API->loginState->getState()->setDc($datacenter));
                    $authorization['specialMethodType'] = SpecialMethodType::USER_RELATED;
                    $authorization = $this->methodCallAsyncRead(
                        'auth.importLoginToken',
                        $authorization,
                    );
                }
            } catch (SessionPasswordNeededError) {
                $this->logger->logger(Lang::$current_lang['login_2fa_enabled'], Logger::NOTICE);
                $this->authorization = $this->getPassword();
                if (!isset($this->authorization['hint'])) {
                    $this->authorization['hint'] = '';
                }
                $this->setLoginState(API::WAITING_PASSWORD);
                $this->qrLoginDeferred?->cancel();
                $this->qrLoginDeferred = null;
                return null;
            }
            $this->authFuture?->await();
            return null;
        }
        return $this->loginQrCode;
    }
    /**
     * @internal
     */
    public function getQrLoginCancellation(): Cancellation
    {
        if ($this->qrLoginDeferred) {
            return $this->qrLoginDeferred->getCancellation();
        }
        $c = new DeferredCancellation;
        $c->cancel();
        return $c->getCancellation();
    }
    /**
     * Logout the session.
     */
    public function logout(): void
    {
        if ($this->loginState->getState()->state === API::LOGGED_IN) {
            $this->methodCallAsyncRead('auth.logOut', []);
        }
        $this->setLoginState(API::LOGGED_OUT);
        if ($this->hasEventHandler()) {
            $this->stop();
        } else {
            $this->ipcServer?->stop();
        }
    }
    /** @internal */
    public function waitQrLogin(): void
    {
        if (!$this->qrLoginDeferred) {
            return;
        }
        try {
            (new DeferredFuture)->getFuture()->await($this->getQrLoginCancellation());
        } catch (CancelledException) {
        }
    }
    /**
     * Login as user.
     *
     * @param string  $number   Phone number
     * @param integer $sms_type SMS type
     */
    public function phoneLogin(string $number, int $sms_type = 5): array
    {
        if ($this->loginState->getState()->state === \danog\MadelineProto\API::LOGGED_IN) {
            throw new Exception(Lang::$current_lang['already_loggedIn']);
        }
        $this->logger->logger(Lang::$current_lang['login_code_sending'], Logger::NOTICE);
        $this->authorization = $this->methodCallAsyncRead(
            'auth.sendCode',
            [
                'settings' => ['_' => 'codeSettings'],
                'phone_number' => $number,
                'sms_type' => $sms_type,
                'api_id' => $this->settings->getAppInfo()->getApiId(),
                'api_hash' => $this->settings->getAppInfo()->getApiHash(),
                'lang_code' => $this->settings->getAppInfo()->getLangCode(),
                'specialMethodType' => SpecialMethodType::USER_RELATED,
            ],
        );
        $this->authorization['phone_number'] = $number;
        //$this->authorization['_'] .= 'MP';
        $this->setLoginState(API::WAITING_CODE);
        $this->logger->logger(Lang::$current_lang['login_code_sent'], Logger::NOTICE);
        return $this->authorization;
    }
    /**
     * Complet user login using login code.
     *
     * @param string $code Login code
     */
    public function completePhoneLogin(string $code): array
    {
        if ($this->loginState->getState()->state !== \danog\MadelineProto\API::WAITING_CODE) {
            throw new Exception(Lang::$current_lang['login_code_uncalled']);
        }
        $this->setLoginState(API::NOT_LOGGED_IN);
        $this->logger->logger(Lang::$current_lang['login_user'], Logger::NOTICE);
        try {
            $authorization = $this->methodCallAsyncRead('auth.signIn', ['phone_number' => $this->authorization['phone_number'], 'phone_code_hash' => $this->authorization['phone_code_hash'], 'phone_code' => $code, 'specialMethodType' => SpecialMethodType::USER_RELATED]);
        } catch (SessionPasswordNeededError) {
            $this->logger->logger(Lang::$current_lang['login_2fa_enabled'], Logger::NOTICE);
            $this->authorization = $this->getPassword();
            if (!isset($this->authorization['hint'])) {
                $this->authorization['hint'] = '';
            }
            $this->setLoginState(API::WAITING_PASSWORD);

            return $this->authorization;
        } catch (RPCErrorException $e) {
            if ($e->rpc === 'PHONE_NUMBER_UNOCCUPIED') {
                $this->logger->logger(Lang::$current_lang['login_need_signup'], Logger::NOTICE);
                $this->setLoginState(API::WAITING_SIGNUP);

                $this->authorization['phone_code'] = $code;
                return ['_' => 'account.needSignup'];
            }
            throw $e;
        }
        if ($authorization['_'] === 'auth.authorizationSignUpRequired') {
            $this->logger->logger(Lang::$current_lang['login_need_signup'], Logger::NOTICE);
            $this->setLoginState(API::WAITING_SIGNUP);
            $this->authorization['phone_code'] = $code;
            $authorization['_'] = 'account.needSignup';
            return $authorization;
        }
        $this->authFuture?->await();
        return $authorization;
    }
    /**
     * Import authorization.
     *
     * @param array<int, string> $authorization Authorization info
     * @param int                $mainDcID      Main DC ID
     */
    public function importAuthorization(array $authorization, int $mainDcID): array
    {
        if ($this->loginState->getState()->state === \danog\MadelineProto\API::LOGGED_IN) {
            throw new Exception(Lang::$current_lang['already_loggedIn']);
        }
        $this->logger->logger(Lang::$current_lang['login_auth_key'], Logger::NOTICE);

        // The connections made before (to the DC of the config, at least) go with their auth keys: closed, and no
        // longer following the login state, which would otherwise copy the authorization to them once logged in.
        foreach ($this->datacenter->getDataCenterConnections() as $dataCenterConnection) {
            $dataCenterConnection->disconnect();
            $this->loginState->unsubscribe($dataCenterConnection->auth);
        }
        $this->datacenter = new DataCenter($this);
        // Not the default DC 1, which doesn't exist on the test servers (whose DCs are 10001-10003).
        $this->datacenter->currentDatacenter = $mainDcID;
        $auth_key = $authorization[$mainDcID];
        if (!\is_array($auth_key)) {
            $auth_key = ['auth_key' => $auth_key];
        }
        $dataCenterConnection = $this->datacenter->getDataCenterConnection($mainDcID);

        $this->logger->logger("Setting auth key in DC $mainDcID", Logger::NOTICE);
        $dataCenterConnection->auth->setAuthKey($auth_key['auth_key']);
        $dataCenterConnection->auth->connectionState->waitForState(ConnectionState::ENCRYPTED_NOT_AUTHED_NO_LOGIN);
        $this->loginState->publish($this->loginState->getState()->setStateDc(API::LOGGED_IN, $mainDcID));

        $res = $this->fullGetSelf();
        $callbacks = [$this, $this->referenceDatabase, $this->peerDatabase];
        if (!($this->authorization['user']['bot'] ?? false)) {
            $callbacks[] = $this->minDatabase;
        }
        /** @psalm-suppress InvalidArgument */
        $this->TL->updateCallbacks($callbacks);
        $this->qrLoginDeferred?->cancel();
        $this->qrLoginDeferred = null;
        $this->fullGetSelf();
        $this->initDb();
        return $res;
    }
    /**
     * Export authorization.
     *
     * @return array{0: int, 1: string}
     */
    public function exportAuthorization(): array
    {
        $this->fullGetSelf();
        if ($this->loginState->getState()->state !== \danog\MadelineProto\API::LOGGED_IN) {
            throw new Exception(Lang::$current_lang['not_loggedIn']);
        }
        $dc = $this->loginState->getState()->authorizedDc;
        return [$dc, $this->datacenter->getDataCenterConnection($dc)->auth->getAuthKey()];
    }

    /**
     * Complete signup to Telegram.
     *
     * @param string $first_name First name
     * @param string $last_name  Last name
     */
    public function completeSignup(string $first_name, string $last_name = ''): array
    {
        if ($this->loginState->getState()->state !== \danog\MadelineProto\API::WAITING_SIGNUP) {
            throw new Exception(Lang::$current_lang['signup_uncalled']);
        }
        $this->setLoginState(API::NOT_LOGGED_IN);
        $this->logger->logger(Lang::$current_lang['signing_up'], Logger::NOTICE);
        $res = $this->methodCallAsyncRead('auth.signUp', ['phone_number' => $this->authorization['phone_number'], 'phone_code_hash' => $this->authorization['phone_code_hash'], 'phone_code' => $this->authorization['phone_code'], 'first_name' => $first_name, 'last_name' => $last_name, 'specialMethodType' => SpecialMethodType::USER_RELATED]);
        $this->authFuture?->await();
        return $res;
    }
    /**
     * Complete 2FA login.
     *
     * @param string $password Password
     */
    public function complete2faLogin(string $password): array
    {
        if ($this->loginState->getState()->state !== \danog\MadelineProto\API::WAITING_PASSWORD) {
            throw new Exception(Lang::$current_lang['2fa_uncalled']);
        }
        $this->logger->logger(Lang::$current_lang['login_user'], Logger::NOTICE);
        try {
            $res = $this->methodCallAsyncRead('auth.checkPassword', ['password' => $password, 'specialMethodType' => SpecialMethodType::USER_RELATED]);
        } catch (PasswordHashInvalidError) {
            $res = $this->methodCallAsyncRead('auth.checkPassword', ['password' => $password, 'specialMethodType' => SpecialMethodType::USER_RELATED]);
        }
        $this->authFuture?->await();
        return $res;
    }
    /** @internal */
    public function processAuthorization(array $authorization, int $datacenter): void
    {
        if ($this->loginState->getState()->state === \danog\MadelineProto\API::LOGGED_IN) {
            return;
        }
        $this->authorization = $authorization;
        $this->initDb();
        $this->loginState->publish($this->loginState->getState()->setStateDc(API::LOGGED_IN, $datacenter));
        $this->qrLoginDeferred?->cancel();
        $this->qrLoginDeferred = null;
        $this->serialize();
        $this->logger->logger(Lang::$current_lang['login_ok'], Logger::NOTICE);
    }
    /**
     * Update the 2FA password.
     *
     * The params array can contain password, new_password, email and hint params.
     *
     * @param array{password?: string, new_password?: string, email?: string, hint?: string} $params The params
     */
    public function update2fa(array $params): void
    {
        $hasher = new PasswordCalculator($this->getPassword());
        $this->methodCallAsyncRead('account.updatePasswordSettings', $hasher->getPassword($params));
    }
}
<?php

declare(strict_types=1);

/**
 * Events module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Wrappers;

use __PHP_Incomplete_Class;
use danog\MadelineProto\EventHandler;
use danog\MadelineProto\Exception;
use danog\MadelineProto\Ipc\EventHandlerProxy;
use danog\MadelineProto\PluginEventHandler;
use danog\MadelineProto\Settings;
use danog\MadelineProto\UpdateHandlerType;
use ReflectionClass;

/**
 * Event handler.
 *
 * @property Settings $settings Settings
 *
 * @internal
 */
trait Events
{
    /**
     * Event handler class name.
     *
     * @var class-string<EventHandler>
     */
    public ?string $event_handler = null;
    /**
     * Event handler instance.
     *
     * Can't make it strictly typed or else the IPC server may fail without access to the instance.
     *
     * @psalm-var ?EventHandler
     */
    private $event_handler_instance = null;
    /**
     * Event handler method list.
     *
     * @var array<string, callable>
     */
    private array $eventHandlerMethods = [];
    private \Closure $rethrowHandler;
    /**
     * Event handler handler list.
     *
     * @var array<string, callable>
     */
    private array $eventHandlerHandlers = [];

    /** @var array<class-string<EventHandler>, EventHandler> */
    private array $pluginInstances = [];
    /**
     * Set event handler.
     *
     * @internal
     *
     * @param class-string<EventHandler> $eventHandler Event handler
     */
    public function setEventHandler(string $eventHandler): void
    {
        if (!is_subclass_of($eventHandler, EventHandler::class)) {
            throw new Exception('Wrong event handler was defined');
        }
        $this->event_handler = $eventHandler;
        if (!$this->event_handler_instance instanceof $this->event_handler) {
            $class_name = $this->event_handler;
            $refl = new ReflectionClass($class_name);
            $this->event_handler_instance = $refl->newInstanceWithoutConstructor();
        }

        $pluginsNew = [];
        $methods = $this->event_handler_instance->internalStart(
            $this->wrapper,
            $this->pluginInstances,
            $pluginsNew
        );
        if ($methods === null) {
            // Already started event handler
            return;
        }
        $this->rethrowHandler = $this->rethrowUpdateHandler(...);
        [$this->eventHandlerMethods, $this->eventHandlerHandlers] = $methods;
        $this->pluginInstances = $pluginsNew;

        $this->updateHandlerType = UpdateHandlerType::EVENT_HANDLER;
        foreach ($this->getUpdatesQueue as $update) {
            $this->handleUpdate($update);
        }
        $this->getUpdatesQueue->clear();
        $this->getUpdatesQueueKey = 0;
    }
    /**
     * Check if a certain event handler plugin is installed.
     *
     * @param class-string<EventHandler> $class
     *
     * @psalm-mutation-free
     */
    final public function hasPlugin(string $class): bool
    {
        return isset($this->pluginInstances[$class]);
    }
    /**
     * Obtain a certain event handler plugin instance.
     *
     * @template T as EventHandler
     *
     * @param class-string<T> $class

     * return T|null
     *
     * @psalm-mutation-free
     */
    final public function getPlugin(string $class): PluginEventHandler|EventHandlerProxy|null
    {
        return $this->pluginInstances[$class] ?? null;
    }
    /**
     * Unset event handler.
     *
     */
    public function unsetEventHandler(): void
    {
        $this->event_handler = null;
        $this->event_handler_instance = null;
        $this->eventHandlerMethods = [];
        $this->eventHandlerHandlers = [];
        $this->pluginInstances = [];
        $this->setNoop();
    }
    /**
     * Get event handler (or plugin instance).
     *
     * @template T as EventHandler
     *
     * @param class-string<T>|null $class
     *
     * @return T|EventHandlerProxy|__PHP_Incomplete_Class|null
     *
     * @psalm-mutation-free
     */
    public function getEventHandler(?string $class = null): EventHandler|EventHandlerProxy|__PHP_Incomplete_Class|null
    {
        if ($this->event_handler_instance === null || $this->event_handler_instance instanceof __PHP_Incomplete_Class) {
            return $this->event_handler_instance;
        }
        if ($class !== null && $class !== $this->event_handler_instance::class) {
            return $this->getPlugin($class);
        }
        return $this->event_handler_instance;
    }
    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function getEventHandlerClass(): ?string
    {
        return $this->event_handler_instance !== null
            ? $this->event_handler_instance::class
            : null;
    }
    /**
     * Check if an event handler instance is present.
     *
     * @psalm-mutation-free
     */
    public function hasEventHandler(): bool
    {
        return isset($this->event_handler_instance);
    }
    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function callPluginMethod(?string $class, string $method, array $args): mixed
    {
        $obj = $class === null ? $this->event_handler_instance : $this->pluginInstances[$class];
        return $obj->$method(...$args);
    }
    /** @internal */
    public function setPluginProperty(?string $class, string $property, mixed $value): void
    {
        $obj = $class === null ? $this->event_handler_instance : $this->pluginInstances[$class];
        $obj->$property = $value;
    }
    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function getPluginProperty(?string $class, string $property): mixed
    {
        $obj = $class === null ? $this->event_handler_instance : $this->pluginInstances[$class];
        return $obj->$property;
    }
    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function issetPluginProperty(?string $class, string $property): bool
    {
        $obj = $class === null ? $this->event_handler_instance : $this->pluginInstances[$class];
        return isset($obj->$property);
    }
    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function unsetPluginProperty(?string $class, string $property): void
    {
        $obj = $class === null ? $this->event_handler_instance : $this->pluginInstances[$class];
        unset($obj->$property);
    }
}
<?php

declare(strict_types=1);

/**
 * UpdatesState class.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\MTProtoTools;

/**
 * Stores the state of updates.
 *
 * @internal
 *
 * @psalm-external-mutation-free
 */
final class UpdatesState
{
    /**
     * PTS.
     */
    private int $pts = 1;
    /**
     * QTS.
     */
    private int $qts = -1;
    /**
     * Seq.
     */
    private int $seq = 0;
    /**
     * Date.
     */
    private int $date = 1;
    /**
     * Init function.
     *
     * @param array $init      Initial parameters
     * @param int   $channelId Channel ID
     *
     * @psalm-mutation-free
     */
    public function __construct(array $init = [], public readonly int $channelId = 0)
    {
        $this->update($init);
    }
    /**
     * Sleep function.
     *
     * @return array Parameters to serialize
     *
     * @psalm-mutation-free
     */
    public function __sleep(): array
    {
        return $this->channelId ? ['pts', 'channelId'] : ['pts', 'qts', 'seq', 'date', 'channelId'];
    }
    /**
     * Is this state relative to a channel?
     *
     * @psalm-mutation-free
     */
    public function isChannel(): bool
    {
        return (bool) $this->channelId;
    }
    /**
     * Update multiple parameters.
     *
     * @param array $init Parameters to update
     *
     * @psalm-mutation-free
     */
    public function update(array $init): self
    {
        foreach ($this->channelId ? ['pts'] : ['pts', 'qts', 'seq', 'date'] as $param) {
            if (isset($init[$param])) {
                $this->{$param}($init[$param]);
            }
        }
        return $this;
    }
    /**
     * Get/set PTS.
     *
     * @param int $set PTS to set
     *
     * @return int PTS
     *
     * @psalm-external-mutation-free
     */
    public function pts(int $set = 0): int
    {
        if ($set !== 0 && $set > $this->pts) {
            $this->pts = $set;
        }
        return $this->pts;
    }
    /**
     * Get/set QTS.
     *
     * @param int $set QTS to set
     *
     * @return int QTS
     *
     * @psalm-external-mutation-free
     */
    public function qts(int $set = 0): int
    {
        if ($set !== 0 && $set > $this->qts) {
            $this->qts = $set;
        }
        return $this->qts;
    }
    /**
     * Get/set seq.
     *
     * @param int $set Seq to set
     *
     * @return int seq
     *
     * @psalm-external-mutation-free
     */
    public function seq(int $set = 0): int
    {
        if ($set !== 0 && $set > $this->seq) {
            $this->seq = $set;
        }
        return $this->seq;
    }
    /**
     * Get/set date.
     *
     * @param int $set Date to set
     *
     * @return int Date
     *
     * @psalm-external-mutation-free
     */
    public function date(int $set = 0): int
    {
        if ($set !== 0 && $set > $this->date) {
            $this->date = $set;
        }
        return $this->date;
    }
    /**
     * Check validity of PTS contained in update.
     *
     * @param array $update Update
     *
     * @return int   -1 if it's too old, 0 if it's ok, 1 if it's too new
     *
     * @psalm-mutation-free
     */
    public function checkPts(array $update): int
    {
        return $update['pts'] - ($this->pts + $update['pts_count']);
    }
    /**
     * Check validity of seq contained in update.
     *
     * @param int $seq Seq
     *
     * @return int -1 if it's too old, 0 if it's ok, 1 if it's too new
     *
     * @psalm-mutation-free
     */
    public function checkSeq(int $seq): int
    {
        return $seq ? $seq - ($this->seq + 1) : $seq;
    }
}
<?php

declare(strict_types=1);

/**
 * CallHandler module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\MTProtoTools;

use danog\MadelineProto\MTProto\SpecialMethodType;
use danog\MadelineProto\Settings;
use danog\MadelineProto\WrappedFuture;

/**
 * Manages method and object calls.
 *
 * @property Settings $settings Settings
 *
 * @internal
 */
trait CallHandler
{
    /**
     * Call method and wait asynchronously for response.
     *
     * @internal
     *
     * @param string $method Method name
     * @param array  $args   Arguments
     */
    public function methodCallAsyncRead(string $method, array $args, ?int $datacenter = null)
    {
        if (isset($args['specialMethodType']) && $args['specialMethodType'] === SpecialMethodType::USER_RELATED) {
            $datacenter = $this->loginState->getState()->authorizedDc;
        }
        // Calls uploading files are resumed if interrupted by a restart.
        return $this->makeResumableCall(
            'method',
            $method,
            $args,
            fn (array $args) => ($this->datacenter->waitGetConnection($datacenter ?? $this->datacenter->currentDatacenter))->methodCallAsyncRead($method, $args),
        );
    }
    /**
     * Call method and make sure it is asynchronously sent.
     *
     * @internal
     *
     * @param string $method Method name
     * @param array  $args   Arguments
     */
    public function methodCallAsyncWrite(string $method, array $args, ?int $datacenter = null): WrappedFuture
    {
        return ($this->datacenter->waitGetConnection($datacenter ?? $this->datacenter->currentDatacenter))->methodCallAsyncWrite($method, $args);
    }
}
<?php

declare(strict_types=1);

/**
 * Transfer progress.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\MTProtoTools;

/**
 * Reports the progress of an upload or download to a progress callback (percent, speed in mbps, seconds elapsed),
 * each time a part completes.
 *
 * @internal
 */
final class TransferProgress
{
    private readonly float $start;
    private int $parts = 0;
    private int $bytes = 0;

    /**
     * @param \Closure(float, float, float): void $cb         Progress callback
     * @param int                                 $totalParts Total number of parts
     */
    public function __construct(private readonly \Closure $cb, private readonly int $totalParts)
    {
        $this->start = microtime(true);
    }

    /**
     * Report the completion of a part.
     *
     * @param int $bytes Bytes transferred for this part (0 for parts that were already transferred before a resume).
     */
    public function partDone(int $bytes): void
    {
        $this->parts++;
        $this->bytes += $bytes;
        ($this->cb)($this->parts * 100 / $this->totalParts, $this->getSpeed(), $this->getTime());
    }

    /** Seconds elapsed since the transfer started. */
    public function getTime(): float
    {
        return microtime(true) - $this->start;
    }

    /** Average speed of the bytes transferred so far, in mbps. */
    public function getSpeed(): float
    {
        $time = $this->getTime();
        return $time > 0 ? $this->bytes * 8 / $time / 1_000_000 : 0.0;
    }
}
<?php

declare(strict_types=1);

/**
 * Files module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\MTProtoTools;

use Amp\ByteStream\Pipe;
use Amp\ByteStream\ReadableBuffer;
use Amp\ByteStream\ReadableStream;
use Amp\Cancellation;
use Amp\Http\Client\HttpClient;
use Amp\Http\Client\HttpClientBuilder;
use Amp\Http\Client\Request;
use Amp\Process\Process;
use AssertionError;
use danog\DialogId\DialogId;
use danog\MadelineProto\BotApiFileId;
use danog\MadelineProto\EventHandler\Media;
use danog\MadelineProto\EventHandler\Media\AbstractVideo;
use danog\MadelineProto\EventHandler\Media\Audio;
use danog\MadelineProto\EventHandler\Media\Document;
use danog\MadelineProto\EventHandler\Media\DocumentPhoto;
use danog\MadelineProto\EventHandler\Media\Gif;
use danog\MadelineProto\EventHandler\Media\InlineThumbnail;
use danog\MadelineProto\EventHandler\Media\Photo;
use danog\MadelineProto\EventHandler\Media\RoundVideo;
use danog\MadelineProto\EventHandler\Media\Sticker;
use danog\MadelineProto\EventHandler\Media\Video;
use danog\MadelineProto\EventHandler\Media\Voice;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\Exception;
use danog\MadelineProto\LocalFile;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\ParseMode;
use danog\MadelineProto\RemoteUrl;
use danog\MadelineProto\ResumableDownloadStream;
use danog\MadelineProto\ResumableStream;
use danog\MadelineProto\Settings;
use danog\MadelineProto\StreamDuplicator;
use danog\MadelineProto\TL\Types\Bytes;
use danog\MadelineProto\Tools;
use finfo;
use Webmozart\Assert\Assert;

use function Amp\async;
use function Amp\ByteStream\buffer;
use function Amp\File\getModificationTime;
use function Amp\File\getSize;
use function Amp\File\openFile;
use function Amp\Future\await;

/**
 * Manages upload and download of files.
 *
 * @property Settings $settings Settings
 *
 * @internal
 */
trait FilesAbstraction
{
    private static ?HttpClient $client;
    /**
     * Provide a stream for a file, URL or amp stream.
     */
    public function getStream(Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $stream, ?Cancellation $cancellation = null, ?int &$size = null): ReadableStream
    {
        if ($stream instanceof LocalFile) {
            $size = getSize($stream->file);
            return openFile($stream->file, 'r');
        }
        if ($stream instanceof RemoteUrl) {
            self::$client ??= HttpClientBuilder::buildDefault();
            $request = new Request($stream->url);
            $request->setTransferTimeout(INF);
            $request->setInactivityTimeout(INF);
            $request->setBodySizeLimit(512 * 1024 * 8000);
            $response = self::$client->request(
                $request,
                $cancellation
            );
            if (($status = $response->getStatus()) !== 200) {
                throw new Exception("Wrong status code: {$status} ".$response->getReason());
            }
            $size = (int) ($response->getHeader('content-length') ?? $size);
            $stream = $response->getBody();
            return $stream;
        }
        if ($stream instanceof Message) {
            $stream = $stream->media;
            if ($stream === null) {
                throw new AssertionError("The message must be a media message!");
            }
        }
        if ($stream instanceof Media) {
            $size = $stream->size;
            return $stream->getStream(cancellation: $cancellation);
        }
        if ($stream instanceof BotApiFileId) {
            $size = $stream->size;
            return $this->downloadToReturnedStream($stream, cancellation: $cancellation);
        }
        return $stream;
    }

    /**
     * Sends a document.
     *
     * Please use named arguments to call this method.
     *
     * @param integer|string                                                     $peer                   Destination peer or username.
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream      $file                   File to upload: can be a message to reuse media present in a message.
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream|null $thumb                  Optional: Thumbnail to upload
     * @param string                                                             $caption                Caption of document
     * @param ?callable(float, float, float)                                     $callback               Upload callback (percent, speed in mbps, seconds elapsed)
     * @param ?string                                                            $fileName               Optional file name, if absent will be extracted from the passed $file.
     * @param ParseMode                                                          $parseMode              Text parse mode for the caption
     * @param integer|null                                                       $replyToMsgId           ID of message to reply to.
     * @param integer|null                                                       $topMsgId               ID of thread where to send the message.
     * @param array|null                                                         $replyMarkup            Keyboard information.
     * @param integer|null                                                       $sendAs                 Peer to send the message as.
     * @param integer|null                                                       $scheduleDate           Schedule date.
     * @param boolean                                                            $silent                 Whether to send the message silently, without triggering notifications.
     * @param boolean                                                            $background             Send this message as background message
     * @param boolean                                                            $clearDraft             Clears the draft field
     * @param boolean                                                            $updateStickersetsOrder Whether to move used stickersets to top
     * @param boolean                                                            $forceResend            Whether to forcefully resend the file, even if its type and name are the same.
     * @param Cancellation                                                       $cancellation           Cancellation.
     *
     */
    public function sendDocument(
        int|string $peer,
        Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file,
        Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream|null $thumb = null,
        string $caption = '',
        ParseMode $parseMode = ParseMode::TEXT,
        ?callable $callback = null,
        ?string $fileName = null,
        ?string $mimeType = null,
        ?int $ttl = null,
        bool $spoiler = false,
        ?int $replyToMsgId = null,
        ?int $topMsgId = null,
        ?array $replyMarkup = null,
        int|string|null $sendAs = null,
        ?int $scheduleDate = null,
        bool $silent = false,
        bool $noForwards = false,
        bool $background = false,
        bool $clearDraft = false,
        bool $updateStickersetsOrder = false,
        bool $forceResend = false,
        ?Cancellation $cancellation = null,
    ): Message {
        return $this->sendMedia(
            type: Document::class,
            mimeType: $mimeType,
            thumb: $thumb,
            attributesOrig: [],
            peer: $peer,
            file: $file,
            caption: $caption,
            parseMode: $parseMode,
            callback: $callback,
            fileName: $fileName,
            ttl: $ttl,
            spoiler: $spoiler,
            silent: $silent,
            background: $background,
            clearDraft: $clearDraft,
            noForwards: $noForwards,
            updateStickersetsOrder: $updateStickersetsOrder,
            replyToMsgId: $replyToMsgId,
            topMsgId: $topMsgId,
            replyMarkup: $replyMarkup,
            scheduleDate: $scheduleDate,
            sendAs: $sendAs,
            forceResend: $forceResend,
            cancellation: $cancellation,
            uploadOnly: false,
        );
    }

    /**
     * Uploads a document without sending it.
     *
     * Please use named arguments to call this method.
     *
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream      $file                   File to upload: can be a message to reuse media present in a message.
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream|null $thumb                  Optional: Thumbnail to upload
     * @param integer|string|null                                                     $peer              Optional: associate the media to the specified peer or username.
     * @param string                                                             $caption                Caption of document
     * @param ?callable(float, float, float)                                     $callback               Upload callback (percent, speed in mbps, seconds elapsed)
     * @param ?string                                                            $fileName               Optional file name, if absent will be extracted from the passed $file.
     * @param ParseMode                                                          $parseMode              Text parse mode for the caption
     * @param integer|null                                                       $replyToMsgId           ID of message to reply to.
     * @param integer|null                                                       $topMsgId               ID of thread where to send the message.
     * @param array|null                                                         $replyMarkup            Keyboard information.
     * @param integer|null                                                       $sendAs                 Peer to send the message as.
     * @param integer|null                                                       $scheduleDate           Schedule date.
     * @param boolean                                                            $silent                 Whether to send the message silently, without triggering notifications.
     * @param boolean                                                            $background             Send this message as background message
     * @param boolean                                                            $clearDraft             Clears the draft field
     * @param boolean                                                            $updateStickersetsOrder Whether to move used stickersets to top
     * @param boolean                                                            $forceResend            Whether to forcefully resend the file, even if its type and name are the same.
     * @param Cancellation                                                       $cancellation           Cancellation.
     *
     */
    public function uploadDocument(
        Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file,
        Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream|null $thumb = null,
        int|string|null $peer = null,
        string $caption = '',
        ParseMode $parseMode = ParseMode::TEXT,
        ?callable $callback = null,
        ?string $fileName = null,
        ?string $mimeType = null,
        bool $spoiler = false,
        ?int $replyToMsgId = null,
        ?int $topMsgId = null,
        ?array $replyMarkup = null,
        int|string|null $sendAs = null,
        ?int $scheduleDate = null,
        bool $silent = false,
        bool $noForwards = false,
        bool $background = false,
        bool $clearDraft = false,
        bool $updateStickersetsOrder = false,
        bool $forceResend = false,
        ?Cancellation $cancellation = null,
    ): Media {
        return $this->sendMedia(
            type: Document::class,
            mimeType: $mimeType,
            thumb: $thumb,
            attributesOrig: [],
            peer: $peer,
            file: $file,
            caption: $caption,
            parseMode: $parseMode,
            callback: $callback,
            fileName: $fileName,
            ttl: null,
            spoiler: $spoiler,
            silent: $silent,
            background: $background,
            clearDraft: $clearDraft,
            noForwards: $noForwards,
            updateStickersetsOrder: $updateStickersetsOrder,
            replyToMsgId: $replyToMsgId,
            topMsgId: $topMsgId,
            replyMarkup: $replyMarkup,
            scheduleDate: $scheduleDate,
            sendAs: $sendAs,
            forceResend: $forceResend,
            cancellation: $cancellation,
            uploadOnly: true,
        );
    }
    /**
     * Sends a photo.
     *
     * Please use named arguments to call this method.
     *
     * @param integer|string                                                $peer                   Destination peer or username.
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file                   File to upload: can be a message to reuse media present in a message.
     * @param string                                                        $caption                Caption of document
     * @param ?callable(float, float, float)                                $callback               Upload callback (percent, speed in mbps, seconds elapsed)
     * @param ?string                                                       $fileName               Optional file name, if absent will be extracted from the passed $file.
     * @param ParseMode                                                     $parseMode              Text parse mode for the caption
     * @param integer|null                                                  $replyToMsgId           ID of message to reply to.
     * @param integer|null                                                  $topMsgId               ID of thread where to send the message.
     * @param array|null                                                    $replyMarkup            Keyboard information.
     * @param integer|null                                                  $sendAs                 Peer to send the message as.
     * @param integer|null                                                  $scheduleDate           Schedule date.
     * @param boolean                                                       $silent                 Whether to send the message silently, without triggering notifications.
     * @param boolean                                                       $background             Send this message as background message
     * @param boolean                                                       $clearDraft             Clears the draft field
     * @param boolean                                                       $updateStickersetsOrder Whether to move used stickersets to top
     * @param boolean                                                       $forceResend            Whether to forcefully resend the file, even if its type and name are the same.
     * @param Cancellation                                                  $cancellation           Cancellation.
     *
     */
    public function sendPhoto(
        int|string $peer,
        Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file,
        string $caption = '',
        ParseMode $parseMode = ParseMode::TEXT,
        ?callable $callback = null,
        ?string $fileName = null,
        ?int $ttl = null,
        bool $spoiler = false,
        ?int $replyToMsgId = null,
        ?int $topMsgId = null,
        ?array $replyMarkup = null,
        int|string|null $sendAs = null,
        ?int $scheduleDate = null,
        bool $silent = false,
        bool $noForwards = false,
        bool $background = false,
        bool $clearDraft = false,
        bool $updateStickersetsOrder = false,
        bool $forceResend = false,
        ?Cancellation $cancellation = null,
    ): Message {
        return $this->sendMedia(
            type: Photo::class,
            mimeType: 'image/jpeg',
            thumb: null,
            attributesOrig: [],
            peer: $peer,
            file: $file,
            caption: $caption,
            parseMode: $parseMode,
            callback: $callback,
            fileName: $fileName,
            ttl: $ttl,
            spoiler: $spoiler,
            silent: $silent,
            background: $background,
            clearDraft: $clearDraft,
            noForwards: $noForwards,
            updateStickersetsOrder: $updateStickersetsOrder,
            replyToMsgId: $replyToMsgId,
            topMsgId: $topMsgId,
            replyMarkup: $replyMarkup,
            scheduleDate: $scheduleDate,
            sendAs: $sendAs,
            forceResend: $forceResend,
            cancellation: $cancellation,
            uploadOnly: false,
        );
    }
    /**
     * Uploads a photo, without sending it.
     *
     * Please use named arguments to call this method.
     *
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file                   File to upload: can be a message to reuse media present in a message.
     * @param string                                                        $caption                Caption of document
     * @param integer|string|null                                           $peer                   Optional: associate the media to the specified peer or username.
     * @param ?callable(float, float, float)                                $callback               Upload callback (percent, speed in mbps, seconds elapsed)
     * @param ?string                                                       $fileName               Optional file name, if absent will be extracted from the passed $file.
     * @param ParseMode                                                     $parseMode              Text parse mode for the caption
     * @param integer|null                                                  $replyToMsgId           ID of message to reply to.
     * @param integer|null                                                  $topMsgId               ID of thread where to send the message.
     * @param array|null                                                    $replyMarkup            Keyboard information.
     * @param integer|null                                                  $sendAs                 Peer to send the message as.
     * @param integer|null                                                  $scheduleDate           Schedule date.
     * @param boolean                                                       $silent                 Whether to send the message silently, without triggering notifications.
     * @param boolean                                                       $background             Send this message as background message
     * @param boolean                                                       $clearDraft             Clears the draft field
     * @param boolean                                                       $updateStickersetsOrder Whether to move used stickersets to top
     * @param boolean                                                       $forceResend            Whether to forcefully resend the file, even if its type and name are the same.
     * @param Cancellation                                                  $cancellation           Cancellation.
     *
     */
    public function uploadPhoto(
        Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file,
        int|string|null $peer = null,
        string $caption = '',
        ParseMode $parseMode = ParseMode::TEXT,
        ?callable $callback = null,
        ?string $fileName = null,
        bool $spoiler = false,
        ?int $replyToMsgId = null,
        ?int $topMsgId = null,
        ?array $replyMarkup = null,
        int|string|null $sendAs = null,
        ?int $scheduleDate = null,
        bool $silent = false,
        bool $noForwards = false,
        bool $background = false,
        bool $clearDraft = false,
        bool $updateStickersetsOrder = false,
        bool $forceResend = false,
        ?Cancellation $cancellation = null,
    ): Media {
        return $this->sendMedia(
            type: Photo::class,
            mimeType: 'image/jpeg',
            thumb: null,
            attributesOrig: [],
            peer: $peer,
            file: $file,
            caption: $caption,
            parseMode: $parseMode,
            callback: $callback,
            fileName: $fileName,
            ttl: null,
            spoiler: $spoiler,
            silent: $silent,
            background: $background,
            clearDraft: $clearDraft,
            noForwards: $noForwards,
            updateStickersetsOrder: $updateStickersetsOrder,
            replyToMsgId: $replyToMsgId,
            topMsgId: $topMsgId,
            replyMarkup: $replyMarkup,
            scheduleDate: $scheduleDate,
            sendAs: $sendAs,
            forceResend: $forceResend,
            cancellation: $cancellation,
            uploadOnly: true,
        );
    }
    /**
     * Sends a photo.
     *
     * Please use named arguments to call this method.
     *
     * @param integer|string                                                $peer                   Destination peer or username.
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file                   File to upload: can be a message to reuse media present in a message.
     * @param string                                                        $caption                Caption of document
     * @param ?callable(float, float, float)                                $callback               Upload callback (percent, speed in mbps, seconds elapsed)
     * @param ?string                                                       $fileName               Optional file name, if absent will be extracted from the passed $file.
     * @param ParseMode                                                     $parseMode              Text parse mode for the caption
     * @param integer|null                                                  $replyToMsgId           ID of message to reply to.
     * @param integer|null                                                  $topMsgId               ID of thread where to send the message.
     * @param array|null                                                    $replyMarkup            Keyboard information.
     * @param integer|null                                                  $sendAs                 Peer to send the message as.
     * @param integer|null                                                  $scheduleDate           Schedule date.
     * @param boolean                                                       $silent                 Whether to send the message silently, without triggering notifications.
     * @param boolean                                                       $background             Send this message as background message
     * @param boolean                                                       $clearDraft             Clears the draft field
     * @param boolean                                                       $updateStickersetsOrder Whether to move used stickersets to top
     * @param boolean                                                       $forceResend            Whether to forcefully resend the file, even if its type and name are the same.
     * @param Cancellation                                                  $cancellation           Cancellation.
     *
     */
    public function sendDocumentPhoto(
        int|string $peer,
        Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file,
        string $caption = '',
        ParseMode $parseMode = ParseMode::TEXT,
        ?callable $callback = null,
        ?string $fileName = null,
        ?int $ttl = null,
        bool $spoiler = false,
        ?int $replyToMsgId = null,
        ?int $topMsgId = null,
        ?array $replyMarkup = null,
        int|string|null $sendAs = null,
        ?int $scheduleDate = null,
        bool $silent = false,
        bool $noForwards = false,
        bool $background = false,
        bool $clearDraft = false,
        bool $updateStickersetsOrder = false,
        bool $forceResend = false,
        ?Cancellation $cancellation = null,
    ): Message {
        return $this->sendMedia(
            type: DocumentPhoto::class,
            mimeType: 'image/jpeg',
            thumb: null,
            attributesOrig: [],
            peer: $peer,
            file: $file,
            caption: $caption,
            parseMode: $parseMode,
            callback: $callback,
            fileName: $fileName,
            ttl: $ttl,
            spoiler: $spoiler,
            silent: $silent,
            background: $background,
            clearDraft: $clearDraft,
            noForwards: $noForwards,
            updateStickersetsOrder: $updateStickersetsOrder,
            replyToMsgId: $replyToMsgId,
            topMsgId: $topMsgId,
            replyMarkup: $replyMarkup,
            scheduleDate: $scheduleDate,
            sendAs: $sendAs,
            forceResend: $forceResend,
            cancellation: $cancellation,
            uploadOnly: false,
        );
    }
    /**
     * Uploads a photo without sending it to the chat.
     *
     * Please use named arguments to call this method.
     *
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file                   File to upload: can be a message to reuse media present in a message.
     * @param string                                                        $caption                Caption of document
     * @param integer|string|null                                           $peer                   Optional: associate the file to the destination peer or username.
     * @param ?callable(float, float, float)                                $callback               Upload callback (percent, speed in mbps, seconds elapsed)
     * @param ?string                                                       $fileName               Optional file name, if absent will be extracted from the passed $file.
     * @param ParseMode                                                     $parseMode              Text parse mode for the caption
     * @param integer|null                                                  $replyToMsgId           ID of message to reply to.
     * @param integer|null                                                  $topMsgId               ID of thread where to send the message.
     * @param array|null                                                    $replyMarkup            Keyboard information.
     * @param integer|null                                                  $sendAs                 Peer to send the message as.
     * @param integer|null                                                  $scheduleDate           Schedule date.
     * @param boolean                                                       $silent                 Whether to send the message silently, without triggering notifications.
     * @param boolean                                                       $background             Send this message as background message
     * @param boolean                                                       $clearDraft             Clears the draft field
     * @param boolean                                                       $updateStickersetsOrder Whether to move used stickersets to top
     * @param boolean                                                       $forceResend            Whether to forcefully resend the file, even if its type and name are the same.
     * @param Cancellation                                                  $cancellation           Cancellation.
     *
     */
    public function uploadDocumentPhoto(
        Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file,
        int|string|null $peer = null,
        string $caption = '',
        ParseMode $parseMode = ParseMode::TEXT,
        ?callable $callback = null,
        ?string $fileName = null,
        bool $spoiler = false,
        ?int $replyToMsgId = null,
        ?int $topMsgId = null,
        ?array $replyMarkup = null,
        int|string|null $sendAs = null,
        ?int $scheduleDate = null,
        bool $silent = false,
        bool $noForwards = false,
        bool $background = false,
        bool $clearDraft = false,
        bool $updateStickersetsOrder = false,
        bool $forceResend = false,
        ?Cancellation $cancellation = null,
    ): Media {
        return $this->sendMedia(
            type: DocumentPhoto::class,
            mimeType: 'image/jpeg',
            thumb: null,
            attributesOrig: [],
            peer: $peer,
            file: $file,
            caption: $caption,
            parseMode: $parseMode,
            callback: $callback,
            fileName: $fileName,
            ttl: null,
            spoiler: $spoiler,
            silent: $silent,
            background: $background,
            clearDraft: $clearDraft,
            noForwards: $noForwards,
            updateStickersetsOrder: $updateStickersetsOrder,
            replyToMsgId: $replyToMsgId,
            topMsgId: $topMsgId,
            replyMarkup: $replyMarkup,
            scheduleDate: $scheduleDate,
            sendAs: $sendAs,
            forceResend: $forceResend,
            cancellation: $cancellation,
            uploadOnly: true,
        );
    }
    /**
     * Sends a sticker.
     *
     * Please use named arguments to call this method.
     *
     * @param integer|string                                                $peer                   Destination peer or username.
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file                   File to upload: can be a message to reuse media present in a message.
     * @param ?callable(float, float, float)                                $callback               Upload callback (percent, speed in mbps, seconds elapsed)
     * @param ?string                                                       $fileName               Optional file name, if absent will be extracted from the passed $file.
     * @param integer|null                                                  $replyToMsgId           ID of message to reply to.
     * @param integer|null                                                  $topMsgId               ID of thread where to send the message.
     * @param array|null                                                    $replyMarkup            Keyboard information.
     * @param integer|null                                                  $sendAs                 Peer to send the message as.
     * @param integer|null                                                  $scheduleDate           Schedule date.
     * @param boolean                                                       $silent                 Whether to send the message silently, without triggering notifications.
     * @param boolean                                                       $noForwards             Whether to disable forwards for this message.
     * @param boolean                                                       $background             Send this message as background message
     * @param boolean                                                       $clearDraft             Clears the draft field
     * @param boolean                                                       $updateStickersetsOrder Whether to move used stickersets to top
     * @param boolean                                                       $forceResend            Whether to forcefully resend the file, even if its type and name are the same.
     * @param Cancellation                                                  $cancellation           Cancellation.
     *
     */
    public function sendSticker(
        int|string $peer,
        Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file,
        string $mimeType,
        string $emoji = '',
        array $stickerSet = ['_' => 'inputStickerSetEmpty'],
        ?callable $callback = null,
        ?string $fileName = null,
        ?int $ttl = null,
        ?int $replyToMsgId = null,
        ?int $topMsgId = null,
        ?array $replyMarkup = null,
        int|string|null $sendAs = null,
        ?int $scheduleDate = null,
        bool $silent = false,
        bool $noForwards = false,
        bool $background = false,
        bool $clearDraft = false,
        bool $updateStickersetsOrder = false,
        bool $forceResend = false,
        ?Cancellation $cancellation = null,
    ): Message {
        return $this->sendMedia(
            type: Sticker::class,
            mimeType: $mimeType,
            thumb: null,
            attributesOrig: ['_' => 'documentAttributeSticker', 'alt' => $emoji, 'stickerset' => $stickerSet],
            peer: $peer,
            file: $file,
            caption: '',
            parseMode: ParseMode::TEXT,
            callback: $callback,
            fileName: $fileName,
            ttl: $ttl,
            spoiler: false,
            silent: $silent,
            background: $background,
            clearDraft: $clearDraft,
            noForwards: $noForwards,
            updateStickersetsOrder: $updateStickersetsOrder,
            replyToMsgId: $replyToMsgId,
            topMsgId: $topMsgId,
            replyMarkup: $replyMarkup,
            scheduleDate: $scheduleDate,
            sendAs: $sendAs,
            forceResend: $forceResend,
            cancellation: $cancellation,
            uploadOnly: false,
        );
    }
    /**
     * Uploads a sticker without sending it.
     *
     * Please use named arguments to call this method.
     *
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file                   File to upload: can be a message to reuse media present in a message.
     * @param ?callable(float, float, float)                                $callback               Upload callback (percent, speed in mbps, seconds elapsed)
     * @param ?string                                                       $fileName               Optional file name, if absent will be extracted from the passed $file.
     * @param integer|string|null                                           $peer                   Optional: associate the file to the specified peer or username.
     * @param integer|null                                                  $replyToMsgId           ID of message to reply to.
     * @param integer|null                                                  $topMsgId               ID of thread where to send the message.
     * @param array|null                                                    $replyMarkup            Keyboard information.
     * @param integer|null                                                  $sendAs                 Peer to send the message as.
     * @param integer|null                                                  $scheduleDate           Schedule date.
     * @param boolean                                                       $silent                 Whether to send the message silently, without triggering notifications.
     * @param boolean                                                       $noForwards             Whether to disable forwards for this message.
     * @param boolean                                                       $background             Send this message as background message
     * @param boolean                                                       $clearDraft             Clears the draft field
     * @param boolean                                                       $updateStickersetsOrder Whether to move used stickersets to top
     * @param boolean                                                       $forceResend            Whether to forcefully resend the file, even if its type and name are the same.
     * @param Cancellation                                                  $cancellation           Cancellation.
     *
     */
    public function uploadSticker(
        Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file,
        string $mimeType,
        int|string|null $peer = null,
        string $emoji = '',
        array $stickerSet = ['_' => 'inputStickerSetEmpty'],
        ?callable $callback = null,
        ?string $fileName = null,
        ?int $replyToMsgId = null,
        ?int $topMsgId = null,
        ?array $replyMarkup = null,
        int|string|null $sendAs = null,
        ?int $scheduleDate = null,
        bool $silent = false,
        bool $noForwards = false,
        bool $background = false,
        bool $clearDraft = false,
        bool $updateStickersetsOrder = false,
        bool $forceResend = false,
        ?Cancellation $cancellation = null,
    ): Media {
        return $this->sendMedia(
            type: Sticker::class,
            mimeType: $mimeType,
            thumb: null,
            attributesOrig: ['_' => 'documentAttributeSticker', 'alt' => $emoji, 'stickerset' => $stickerSet],
            peer: $peer,
            file: $file,
            caption: '',
            parseMode: ParseMode::TEXT,
            callback: $callback,
            fileName: $fileName,
            ttl: null,
            spoiler: false,
            silent: $silent,
            background: $background,
            clearDraft: $clearDraft,
            noForwards: $noForwards,
            updateStickersetsOrder: $updateStickersetsOrder,
            replyToMsgId: $replyToMsgId,
            topMsgId: $topMsgId,
            replyMarkup: $replyMarkup,
            scheduleDate: $scheduleDate,
            sendAs: $sendAs,
            forceResend: $forceResend,
            cancellation: $cancellation,
            uploadOnly: true,
        );
    }
    /**
     * Sends a video.
     *
     * Please use named arguments to call this method.
     *
     * @param integer|string                                                $peer                   Destination peer or username.
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file                   File to upload: can be a message to reuse media present in a message.
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream|null $thumb                  Optional: Thumbnail to upload
     * @param string                                                        $caption                Caption of document
     * @param ParseMode                                                     $parseMode              Text parse mode for the caption
     * @param ?callable(float, float, float)                                $callback               Upload callback (percent, speed in mbps, seconds elapsed)
     * @param ?string                                                       $fileName               Optional file name, if absent will be extracted from the passed $file.
     * @param integer|null                                                  $ttl                     Time to live
     * @param boolean                                                       $spoiler                 Whether the message is a spoiler
     * @param boolean                                                       $roundMessage            Whether the message should be round
     * @param boolean                                                       $supportsStreaming        Whether the video supports streaming
     * @param boolean                                                       $noSound                 Whether the video has no sound
     * @param integer|null                                                  $duration                Duration of the video
     * @param integer|null                                                  $width                   Width of the video
     * @param integer|null                                                  $height                  Height of the video
     * @param integer|null                                                  $replyToMsgId            ID of message to reply to.
     * @param integer|null                                                  $topMsgId                ID of thread where to send the message.
     * @param array|null                                                    $replyMarkup             Keyboard information.
     * @param integer|string|null                                           $sendAs                 Peer to send the message as.
     * @param integer|null                                                  $scheduleDate            Schedule date.
     * @param boolean                                                       $silent                  Whether to send the message silently, without triggering notifications.
     * @param boolean                                                       $noForwards              Whether to disable forwards for this message.
     * @param boolean                                                       $background              Send this message as background message
     * @param boolean                                                       $clearDraft              Clears the draft field
     * @param boolean                                                       $forceResend             Whether to forcefully resend the file, even if its type and name are the same.
     * @param Cancellation                                                  $cancellation            Cancellation.
     *
     */
    public function sendVideo(
        int|string $peer,
        Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file,
        Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream|null $thumb = null,
        string $caption = '',
        ParseMode $parseMode = ParseMode::TEXT,
        ?callable $callback = null,
        ?string $fileName = null,
        string $mimeType = 'video/mp4',
        ?int $ttl = null,
        bool $spoiler = false,
        bool $roundMessage = false,
        bool $supportsStreaming = true,
        bool $noSound = false,
        ?int $duration = null,
        ?int $width = null,
        ?int $height = null,
        string $thumbSeek = '00:00:01.000',
        ?int $replyToMsgId = null,
        ?int $topMsgId = null,
        ?array $replyMarkup = null,
        int|string|null $sendAs = null,
        ?int $scheduleDate = null,
        bool $silent = false,
        bool $noForwards = false,
        bool $background = false,
        bool $clearDraft = false,
        bool $forceResend = false,
        bool $updateStickersetsOrder = false,
        ?Cancellation $cancellation = null,
    ): Message {
        return $this->sendMedia(
            type: Video::class,
            mimeType: $mimeType,
            thumb: $thumb,
            attributesOrig: [
                'round_message' => $roundMessage,
                'supports_streaming' => $supportsStreaming,
                'no_sound' => $noSound,
                'duration' => $duration,
                'w' => $width,
                'h' => $height,
                'thumbSeek' => $thumbSeek,
            ],
            peer: $peer,
            file: $file,
            caption: $caption,
            parseMode: $parseMode,
            callback: $callback,
            fileName: $fileName,
            ttl: $ttl,
            spoiler: $spoiler,
            silent: $silent,
            background: $background,
            clearDraft: $clearDraft,
            noForwards: $noForwards,
            updateStickersetsOrder: $updateStickersetsOrder,
            replyToMsgId: $replyToMsgId,
            topMsgId: $topMsgId,
            replyMarkup: $replyMarkup,
            scheduleDate: $scheduleDate,
            sendAs: $sendAs,
            forceResend: $forceResend,
            cancellation: $cancellation,
            uploadOnly: false,
        );
    }
    /**
     * Uploads a video, without actually sending it.
     *
     * Please use named arguments to call this method.
     *
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file                   File to upload: can be a message to reuse media present in a message.
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream|null $thumb                  Optional: Thumbnail to upload
     * @param integer|string|null                                           $peer                   Optional: associate the media to the specified peer or username.
     * @param string                                                        $caption                Caption of document
     * @param ParseMode                                                     $parseMode              Text parse mode for the caption
     * @param ?callable(float, float, float)                                $callback               Upload callback (percent, speed in mbps, seconds elapsed)
     * @param ?string                                                       $fileName               Optional file name, if absent will be extracted from the passed $file.
     * @param boolean                                                       $spoiler                 Whether the message is a spoiler
     * @param boolean                                                       $roundMessage            Whether the message should be round
     * @param boolean                                                       $supportsStreaming        Whether the video supports streaming
     * @param boolean                                                       $noSound                 Whether the video has no sound
     * @param integer|null                                                  $duration                Duration of the video
     * @param integer|null                                                  $width                   Width of the video
     * @param integer|null                                                  $height                  Height of the video
     * @param integer|null                                                  $replyToMsgId            ID of message to reply to.
     * @param integer|null                                                  $topMsgId                ID of thread where to send the message.
     * @param array|null                                                    $replyMarkup             Keyboard information.
     * @param integer|string|null                                           $sendAs                 Peer to send the message as.
     * @param integer|null                                                  $scheduleDate            Schedule date.
     * @param boolean                                                       $silent                  Whether to send the message silently, without triggering notifications.
     * @param boolean                                                       $noForwards              Whether to disable forwards for this message.
     * @param boolean                                                       $background              Send this message as background message
     * @param boolean                                                       $clearDraft              Clears the draft field
     * @param boolean                                                       $forceResend             Whether to forcefully resend the file, even if its type and name are the same.
     * @param Cancellation                                                  $cancellation            Cancellation.
     *
     */
    public function uploadVideo(
        Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file,
        Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream|null $thumb = null,
        int|string|null $peer = null,
        string $caption = '',
        ParseMode $parseMode = ParseMode::TEXT,
        ?callable $callback = null,
        ?string $fileName = null,
        string $mimeType = 'video/mp4',
        bool $spoiler = false,
        bool $roundMessage = false,
        bool $supportsStreaming = true,
        bool $noSound = false,
        ?int $duration = null,
        ?int $width = null,
        ?int $height = null,
        string $thumbSeek = '00:00:01.000',
        ?int $replyToMsgId = null,
        ?int $topMsgId = null,
        ?array $replyMarkup = null,
        int|string|null $sendAs = null,
        ?int $scheduleDate = null,
        bool $silent = false,
        bool $noForwards = false,
        bool $background = false,
        bool $clearDraft = false,
        bool $forceResend = false,
        bool $updateStickersetsOrder = false,
        ?Cancellation $cancellation = null,
    ): Media {
        return $this->sendMedia(
            type: Video::class,
            mimeType: $mimeType,
            thumb: $thumb,
            attributesOrig: [
                'round_message' => $roundMessage,
                'supports_streaming' => $supportsStreaming,
                'no_sound' => $noSound,
                'duration' => $duration,
                'w' => $width,
                'h' => $height,
                'thumbSeek' => $thumbSeek,
            ],
            peer: $peer,
            file: $file,
            caption: $caption,
            parseMode: $parseMode,
            callback: $callback,
            fileName: $fileName,
            ttl: null,
            spoiler: $spoiler,
            silent: $silent,
            background: $background,
            clearDraft: $clearDraft,
            noForwards: $noForwards,
            updateStickersetsOrder: $updateStickersetsOrder,
            replyToMsgId: $replyToMsgId,
            topMsgId: $topMsgId,
            replyMarkup: $replyMarkup,
            scheduleDate: $scheduleDate,
            sendAs: $sendAs,
            forceResend: $forceResend,
            cancellation: $cancellation,
            uploadOnly: true,
        );
    }
    /**
     * Sends a gif.
     *
     * Please use named arguments to call this method.
     *
     * @param integer|string                                                $peer                   Destination peer or username.
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file                   File to upload: can be a message to reuse media present in a message.
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream|null $thumb                  Optional: Thumbnail to upload
     * @param string                                                        $caption                Caption of document
     * @param ParseMode                                                     $parseMode              Text parse mode for the caption
     * @param ?callable(float, float, float)                                $callback               Upload callback (percent, speed in mbps, seconds elapsed)
     * @param ?string                                                       $fileName               Optional file name, if absent will be extracted from the passed $file.
     * @param integer|null                                                  $ttl                     Time to live
     * @param boolean                                                       $spoiler                 Whether the message is a spoiler
     * @param integer|null                                                  $replyToMsgId            ID of message to reply to.
     * @param integer|null                                                  $topMsgId                ID of thread where to send the message.
     * @param array|null                                                    $replyMarkup             Keyboard information.
     * @param integer|string|null                                           $sendAs                 Peer to send the message as.
     * @param integer|null                                                  $scheduleDate            Schedule date.
     * @param boolean                                                       $silent                  Whether to send the message silently, without triggering notifications.
     * @param boolean                                                       $noForwards              Whether to disable forwards for this message.
     * @param boolean                                                       $background              Send this message as background message
     * @param boolean                                                       $clearDraft              Clears the draft field
     * @param boolean                                                       $forceResend             Whether to forcefully resend the file, even if its type and name are the same.
     * @param ?Cancellation                                                  $cancellation            Cancellation.
     *
     */
    public function sendGif(
        int|string $peer,
        Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file,
        Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream|null $thumb = null,
        string $caption = '',
        ParseMode $parseMode = ParseMode::TEXT,
        ?callable $callback = null,
        ?string $fileName = null,
        ?int $ttl = null,
        bool $spoiler = false,
        ?int $duration = null,
        ?int $width = null,
        ?int $height = null,
        string $thumbSeek = '00:00:01.000',
        ?int $replyToMsgId = null,
        ?int $topMsgId = null,
        ?array $replyMarkup = null,
        int|string|null $sendAs = null,
        ?int $scheduleDate = null,
        bool $silent = false,
        bool $noForwards = false,
        bool $background = false,
        bool $clearDraft = false,
        bool $forceResend = false,
        ?Cancellation $cancellation = null,
    ): Message {
        return $this->sendMedia(
            type: Gif::class,
            mimeType: 'video/mp4',
            thumb: $thumb,
            attributesOrig: [
                'round_message' => false,
                'supports_streaming' => true,
                'no_sound' => true,
                'duration' => $duration,
                'w' => $width,
                'h' => $height,
                'thumbSeek' => $thumbSeek,
            ],
            peer: $peer,
            file: $file,
            caption: $caption,
            parseMode: $parseMode,
            callback: $callback,
            fileName: $fileName,
            ttl: $ttl,
            spoiler: $spoiler,
            silent: $silent,
            background: $background,
            clearDraft: $clearDraft,
            noForwards: $noForwards,
            updateStickersetsOrder: false,
            replyToMsgId: $replyToMsgId,
            topMsgId: $topMsgId,
            replyMarkup: $replyMarkup,
            scheduleDate: $scheduleDate,
            sendAs: $sendAs,
            forceResend: $forceResend,
            cancellation: $cancellation,
            uploadOnly: false
        );
    }
    /**
     * Uploads a gif without actually sending it.
     *
     * Please use named arguments to call this method.
     *
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file                   File to upload: can be a message to reuse media present in a message.
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream|null $thumb                  Optional: Thumbnail to upload
     * @param integer|string|null                                                $peer                   Optional: associate the media to the peer or username.
     * @param string                                                        $caption                Caption of document
     * @param ParseMode                                                     $parseMode              Text parse mode for the caption
     * @param ?callable(float, float, float)                                $callback               Upload callback (percent, speed in mbps, seconds elapsed)
     * @param ?string                                                       $fileName               Optional file name, if absent will be extracted from the passed $file.
     * @param boolean                                                       $spoiler                 Whether the message is a spoiler
     * @param integer|null                                                  $replyToMsgId            ID of message to reply to.
     * @param integer|null                                                  $topMsgId                ID of thread where to send the message.
     * @param array|null                                                    $replyMarkup             Keyboard information.
     * @param integer|string|null                                           $sendAs                 Peer to send the message as.
     * @param integer|null                                                  $scheduleDate            Schedule date.
     * @param boolean                                                       $silent                  Whether to send the message silently, without triggering notifications.
     * @param boolean                                                       $noForwards              Whether to disable forwards for this message.
     * @param boolean                                                       $background              Send this message as background message
     * @param boolean                                                       $clearDraft              Clears the draft field
     * @param boolean                                                       $forceResend             Whether to forcefully resend the file, even if its type and name are the same.
     * @param ?Cancellation                                                  $cancellation            Cancellation.
     *
     */
    public function uploadGif(
        Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file,
        Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream|null $thumb = null,
        int|string|null $peer = null,
        string $caption = '',
        ParseMode $parseMode = ParseMode::TEXT,
        ?callable $callback = null,
        ?string $fileName = null,
        bool $spoiler = false,
        ?int $duration = null,
        ?int $width = null,
        ?int $height = null,
        string $thumbSeek = '00:00:01.000',
        ?int $replyToMsgId = null,
        ?int $topMsgId = null,
        ?array $replyMarkup = null,
        int|string|null $sendAs = null,
        ?int $scheduleDate = null,
        bool $silent = false,
        bool $noForwards = false,
        bool $background = false,
        bool $clearDraft = false,
        bool $forceResend = false,
        ?Cancellation $cancellation = null,
    ): Media {
        return $this->sendMedia(
            type: Gif::class,
            mimeType: 'video/mp4',
            thumb: $thumb,
            attributesOrig: [
                'round_message' => false,
                'supports_streaming' => true,
                'no_sound' => true,
                'duration' => $duration,
                'w' => $width,
                'h' => $height,
                'thumbSeek' => $thumbSeek,
            ],
            peer: $peer,
            file: $file,
            caption: $caption,
            parseMode: $parseMode,
            callback: $callback,
            fileName: $fileName,
            ttl: null,
            spoiler: $spoiler,
            silent: $silent,
            background: $background,
            clearDraft: $clearDraft,
            noForwards: $noForwards,
            updateStickersetsOrder: false,
            replyToMsgId: $replyToMsgId,
            topMsgId: $topMsgId,
            replyMarkup: $replyMarkup,
            scheduleDate: $scheduleDate,
            sendAs: $sendAs,
            forceResend: $forceResend,
            cancellation: $cancellation,
            uploadOnly: true,
        );
    }
    /**
     * Sends an audio.
     *
     * Please use named arguments to call this method.
     *
     * @param integer|string                                                $peer                   Destination peer or username.
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file                   File to upload: can be a message to reuse media present in a message.
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream|null $thumb                  Optional: Thumbnail to upload
     * @param string                                                        $caption                Caption of document
     * @param ParseMode                                                     $parseMode              Text parse mode for the caption
     * @param ?callable(float, float, float)                                $callback               Upload callback (percent, speed in mbps, seconds elapsed)
     * @param ?string                                                       $fileName               Optional file name, if absent will be extracted from the passed $file.
     * @param integer|null                                                  $duration                Duration of the audio
     * @param string|null                                                   $title                   Title of the audio
     * @param string|null                                                   $performer               Performer of the audio
     * @param integer|null                                                  $replyToMsgId            ID of message to reply to.
     * @param integer|null                                                  $topMsgId                ID of thread where to send the message.
     * @param array|null                                                    $replyMarkup             Keyboard information.
     * @param integer|string|null                                           $sendAs                 Peer to send the message as.
     * @param integer|null                                                  $scheduleDate            Schedule date.
     * @param boolean                                                       $silent                  Whether to send the message silently, without triggering notifications.
     * @param boolean                                                       $noForwards              Whether to disable forwards for this message.
     * @param boolean                                                       $background              Send this message as background message
     * @param boolean                                                       $clearDraft              Clears the draft field
     * @param boolean                                                       $forceResend             Whether to forcefully resend the file, even if its type and name are the same.
     * @param ?Cancellation                                                  $cancellation            Cancellation.
     *
     */
    public function sendAudio(
        int|string $peer,
        Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file,
        Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream|null $thumb = null,
        string $caption = '',
        ParseMode $parseMode = ParseMode::TEXT,
        ?callable $callback = null,
        ?string $fileName = null,
        ?string $mimeType = null,
        ?int $duration = null,
        ?string $title = null,
        ?string $performer = null,
        ?int $ttl = null,
        ?int $replyToMsgId = null,
        ?int $topMsgId = null,
        ?array $replyMarkup = null,
        int|string|null $sendAs = null,
        ?int $scheduleDate = null,
        bool $silent = false,
        bool $noForwards = false,
        bool $background = false,
        bool $clearDraft = false,
        bool $forceResend = false,
        ?Cancellation $cancellation = null,
    ): Message {
        return $this->sendMedia(
            type: Audio::class,
            mimeType: $mimeType,
            thumb: $thumb,
            attributesOrig: [
                'duration' => $duration,
                'title' => $title,
                'performer' => $performer,
            ],
            peer: $peer,
            file: $file,
            caption: $caption,
            parseMode: $parseMode,
            callback: $callback,
            fileName: $fileName,
            ttl: $ttl,
            spoiler: false,
            silent: $silent,
            background: $background,
            clearDraft: $clearDraft,
            noForwards: $noForwards,
            updateStickersetsOrder: false,
            replyToMsgId: $replyToMsgId,
            topMsgId: $topMsgId,
            replyMarkup: $replyMarkup,
            scheduleDate: $scheduleDate,
            sendAs: $sendAs,
            forceResend: $forceResend,
            cancellation: $cancellation,
            uploadOnly: false,
        );
    }
    /**
     * Uploads an audio without actually sending it.
     *
     * Please use named arguments to call this method.
     *
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file                   File to upload: can be a message to reuse media present in a message.
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream|null $thumb                  Optional: Thumbnail to upload
     * @param integer|string|null                                                $peer                   Optional: associate the audio to the specified peer or username.
     * @param string                                                        $caption                Caption of document
     * @param ParseMode                                                     $parseMode              Text parse mode for the caption
     * @param ?callable(float, float, float)                                $callback               Upload callback (percent, speed in mbps, seconds elapsed)
     * @param ?string                                                       $fileName               Optional file name, if absent will be extracted from the passed $file.
     * @param integer|null                                                  $duration                Duration of the audio
     * @param string|null                                                   $title                   Title of the audio
     * @param string|null                                                   $performer               Performer of the audio
     * @param integer|null                                                  $replyToMsgId            ID of message to reply to.
     * @param integer|null                                                  $topMsgId                ID of thread where to send the message.
     * @param array|null                                                    $replyMarkup             Keyboard information.
     * @param integer|string|null                                           $sendAs                 Peer to send the message as.
     * @param integer|null                                                  $scheduleDate            Schedule date.
     * @param boolean                                                       $silent                  Whether to send the message silently, without triggering notifications.
     * @param boolean                                                       $noForwards              Whether to disable forwards for this message.
     * @param boolean                                                       $background              Send this message as background message
     * @param boolean                                                       $clearDraft              Clears the draft field
     * @param boolean                                                       $forceResend             Whether to forcefully resend the file, even if its type and name are the same.
     * @param ?Cancellation                                                  $cancellation            Cancellation.
     *
     */
    public function uploadAudio(
        Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file,
        Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream|null $thumb = null,
        int|string|null $peer = null,
        string $caption = '',
        ParseMode $parseMode = ParseMode::TEXT,
        ?callable $callback = null,
        ?string $fileName = null,
        ?string $mimeType = null,
        ?int $duration = null,
        ?string $title = null,
        ?string $performer = null,
        ?int $replyToMsgId = null,
        ?int $topMsgId = null,
        ?array $replyMarkup = null,
        int|string|null $sendAs = null,
        ?int $scheduleDate = null,
        bool $silent = false,
        bool $noForwards = false,
        bool $background = false,
        bool $clearDraft = false,
        bool $forceResend = false,
        ?Cancellation $cancellation = null,
    ): Media {
        return $this->sendMedia(
            type: Audio::class,
            mimeType: $mimeType,
            thumb: $thumb,
            attributesOrig: [
                'duration' => $duration,
                'title' => $title,
                'performer' => $performer,
            ],
            peer: $peer,
            file: $file,
            caption: $caption,
            parseMode: $parseMode,
            callback: $callback,
            fileName: $fileName,
            ttl: null,
            spoiler: false,
            silent: $silent,
            background: $background,
            clearDraft: $clearDraft,
            noForwards: $noForwards,
            updateStickersetsOrder: false,
            replyToMsgId: $replyToMsgId,
            topMsgId: $topMsgId,
            replyMarkup: $replyMarkup,
            scheduleDate: $scheduleDate,
            sendAs: $sendAs,
            forceResend: $forceResend,
            cancellation: $cancellation,
            uploadOnly: true
        );
    }
    /**
     * Sends a voice.
     *
     * Please use named arguments to call this method.
     *
     * @param integer|string                                                $peer                   Destination peer or username.
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file                   File to upload: can be a message to reuse media present in a message.
     * @param string                                                        $caption                Caption of document
     * @param ParseMode                                                     $parseMode              Text parse mode for the caption
     * @param ?callable(float, float, float)                                $callback               Upload callback (percent, speed in mbps, seconds elapsed)
     * @param ?string                                                       $fileName               Optional file name, if absent will be extracted from the passed $file.
     * @param integer|null                                                  $ttl                     Time to live
     * @param integer|null                                                  $duration                Duration of the voice
     * @param array|null                                                    $waveform                Waveform of the voice
     * @param integer|null                                                  $replyToMsgId            ID of message to reply to.
     * @param integer|null                                                  $topMsgId                ID of thread where to send the message.
     * @param array|null                                                    $replyMarkup             Keyboard information.
     * @param integer|string|null                                           $sendAs                 Peer to send the message as.
     * @param integer|null                                                  $scheduleDate            Schedule date.
     * @param boolean                                                       $silent                  Whether to send the message silently, without triggering notifications.
     * @param boolean                                                       $noForwards              Whether to disable forwards for this message.
     * @param boolean                                                       $background              Send this message as background message
     * @param boolean                                                       $clearDraft              Clears the draft field
     * @param boolean                                                       $forceResend             Whether to forcefully resend the file, even if its type and name are the same.
     * @param ?Cancellation                                                  $cancellation            Cancellation.
     *
     */
    public function sendVoice(
        int|string $peer,
        Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file,
        string $caption = '',
        ParseMode $parseMode = ParseMode::TEXT,
        ?callable $callback = null,
        ?string $fileName = null,
        ?int $ttl = null,
        ?int $duration = null,
        ?array $waveform = null,
        ?int $replyToMsgId = null,
        ?int $topMsgId = null,
        ?array $replyMarkup = null,
        int|string|null $sendAs = null,
        ?int $scheduleDate = null,
        bool $silent = false,
        bool $noForwards = false,
        bool $background = false,
        bool $clearDraft = false,
        bool $forceResend = false,
        ?Cancellation $cancellation = null,
    ): Message {
        $attributes = [
            'duration' => $duration,
            'waveform' => $waveform,
        ];

        return $this->sendMedia(
            type: Voice::class,
            mimeType: 'audio/ogg',
            thumb: null,
            attributesOrig: $attributes,
            peer: $peer,
            file: $file,
            caption: $caption,
            parseMode: $parseMode,
            callback: $callback,
            fileName: $fileName,
            ttl: $ttl,
            spoiler: false,
            silent: $silent,
            background: $background,
            clearDraft: $clearDraft,
            noForwards: $noForwards,
            updateStickersetsOrder: false,
            replyToMsgId: $replyToMsgId,
            topMsgId: $topMsgId,
            replyMarkup: $replyMarkup,
            scheduleDate: $scheduleDate,
            sendAs: $sendAs,
            forceResend: $forceResend,
            cancellation: $cancellation,
            uploadOnly: false,
        );
    }
    /**
     * Uploads a voice without actually sending it to the chat.
     *
     * Please use named arguments to call this method.
     *
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file                   File to upload: can be a message to reuse media present in a message.
     * @param integer|string|null                                                $peer                   Optional: associate the media to the specified peer or username.
     * @param string                                                        $caption                Caption of document
     * @param ParseMode                                                     $parseMode              Text parse mode for the caption
     * @param ?callable(float, float, float)                                $callback               Upload callback (percent, speed in mbps, seconds elapsed)
     * @param ?string                                                       $fileName               Optional file name, if absent will be extracted from the passed $file.
     * @param integer|null                                                  $duration                Duration of the voice
     * @param array|null                                                    $waveform                Waveform of the voice
     * @param integer|null                                                  $replyToMsgId            ID of message to reply to.
     * @param integer|null                                                  $topMsgId                ID of thread where to send the message.
     * @param array|null                                                    $replyMarkup             Keyboard information.
     * @param integer|string|null                                           $sendAs                 Peer to send the message as.
     * @param integer|null                                                  $scheduleDate            Schedule date.
     * @param boolean                                                       $silent                  Whether to send the message silently, without triggering notifications.
     * @param boolean                                                       $noForwards              Whether to disable forwards for this message.
     * @param boolean                                                       $background              Send this message as background message
     * @param boolean                                                       $clearDraft              Clears the draft field
     * @param boolean                                                       $forceResend             Whether to forcefully resend the file, even if its type and name are the same.
     * @param ?Cancellation                                                  $cancellation            Cancellation.
     *
     */
    public function uploadVoice(
        int|string $peer,
        Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file,
        string $caption = '',
        ParseMode $parseMode = ParseMode::TEXT,
        ?callable $callback = null,
        ?string $fileName = null,
        ?int $duration = null,
        ?array $waveform = null,
        ?int $replyToMsgId = null,
        ?int $topMsgId = null,
        ?array $replyMarkup = null,
        int|string|null $sendAs = null,
        ?int $scheduleDate = null,
        bool $silent = false,
        bool $noForwards = false,
        bool $background = false,
        bool $clearDraft = false,
        bool $forceResend = false,
        ?Cancellation $cancellation = null,
    ): Media {
        $attributes = [
            'duration' => $duration,
            'waveform' => $waveform,
        ];

        return $this->sendMedia(
            type: Voice::class,
            mimeType: 'audio/ogg',
            thumb: null,
            attributesOrig: $attributes,
            peer: $peer,
            file: $file,
            caption: $caption,
            parseMode: $parseMode,
            callback: $callback,
            fileName: $fileName,
            ttl: null,
            spoiler: false,
            silent: $silent,
            background: $background,
            clearDraft: $clearDraft,
            noForwards: $noForwards,
            updateStickersetsOrder: false,
            replyToMsgId: $replyToMsgId,
            topMsgId: $topMsgId,
            replyMarkup: $replyMarkup,
            scheduleDate: $scheduleDate,
            sendAs: $sendAs,
            forceResend: $forceResend,
            cancellation: $cancellation,
            uploadOnly: true,
        );
    }
    /**
     * Sends a media.
     *
     * @param class-string<Media> $type
     *
     * @return $uploadOnly ? Media : Message
     * @internal
     */
    public function sendMedia(
        string $type,
        int|string|null $peer,
        ?string $mimeType,
        Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file,
        Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream|null $thumb,
        array $attributesOrig,
        string $caption,
        ParseMode $parseMode,
        ?callable $callback,
        ?string $fileName,
        ?int $ttl,
        bool $spoiler,
        ?int $replyToMsgId,
        ?int $topMsgId,
        ?array $replyMarkup,
        int|string|null $sendAs,
        ?int $scheduleDate,
        bool $silent,
        bool $noForwards,
        bool $background,
        bool $clearDraft,
        bool $updateStickersetsOrder,
        bool $forceResend,
        ?Cancellation $cancellation,
        bool $uploadOnly,
        ?int $randomId = null,
    ): Message|Media {
        $args = get_defined_vars();
        if (!$this instanceof MTProto) {
            /** @psalm-suppress MixedArgument */
            return $this->sendMediaInternal(...$args);
        }
        if ($uploadOnly || $peer === null || DialogId::isSecretChat($this->getId($peer))) {
            /** @psalm-suppress MixedArgument */
            return $this->sendMediaInternal(...$args);
        }
        // Interrupted uploads are resumed after a restart, and then the message is sent: choose the random ID beforehand,
        // so that it's saved with the call, and the message isn't sent twice if the session was saved before it was sent.
        $args['randomId'] = $randomId ?? Tools::randomInt();
        /** @psalm-suppress MixedArgument */
        return $this->makeResumableCall('sendMedia', 'sendMedia', $args, fn (array $args) => $this->sendMediaInternal(...$args));
    }

    /**
     * Sends a media, see sendMedia().
     *
     * @param class-string<Media> $type
     *
     * @internal
     */
    public function sendMediaInternal(
        string $type,
        int|string|null $peer,
        ?string $mimeType,
        Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file,
        Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream|null $thumb,
        array $attributesOrig,
        string $caption,
        ParseMode $parseMode,
        ?callable $callback,
        ?string $fileName,
        ?int $ttl,
        bool $spoiler,
        ?int $replyToMsgId,
        ?int $topMsgId,
        ?array $replyMarkup,
        int|string|null $sendAs,
        ?int $scheduleDate,
        bool $silent,
        bool $noForwards,
        bool $background,
        bool $clearDraft,
        bool $updateStickersetsOrder,
        bool $forceResend,
        ?Cancellation $cancellation,
        bool $uploadOnly,
        ?int $randomId = null,
        ?string $resumableCall = null,
    ): Message|Media {
        if ($file instanceof Message) {
            $file = $file->media;
            if ($file === null) {
                throw new AssertionError("The message must be a media message!");
            }
        }

        if ($peer === null) {
            if (!$uploadOnly) {
                throw new AssertionError("A peer must be provided when sending a media!");
            }
        } else {
            $peer = $this->getId($peer);
        }
        if ($file instanceof Media) {
            $fileName ??= $file->fileName;
        } elseif ($file instanceof LocalFile) {
            $fileName ??= basename($file->file);
        } elseif ($file instanceof RemoteUrl) {
            $fileName ??= basename($file->url);
        } elseif ($file instanceof BotApiFileId) {
            $fileName ??= $file->fileName;
        } elseif ($file instanceof ReadableStream) {
            if ($fileName === null) {
                throw new AssertionError("A file name must be provided when uploading a stream!");
            }
        }

        $reuseId = null;
        if ($file instanceof $type
            && ($fileName ?? $file->fileName) === $file->fileName
            && !$file->protected
            && !$forceResend
        ) {
            // Re-use
            $reuseId = $file->botApiFileId;
        }
        if ($file instanceof BotApiFileId
            && $file->getTypeClass() === $type
            && ($fileName ?? $file->fileName) === $file->fileName
            && !$file->protected
            && !$forceResend
        ) {
            // Re-use
            $reuseId = $file->fileId;
        }

        $attributes = match ($type) {
            Video::class, Gif::class => [
                [
                    '_' => 'documentAttributeVideo',
                    'round_message' => $file instanceof RoundVideo
                        ? true
                        : $attributesOrig['round_message'],
                    'supports_streaming' => $file instanceof AbstractVideo
                        ? $file->supportsStreaming
                        : $attributesOrig['supports_streaming'],
                    'no_sound' => $file instanceof Gif
                        ? true
                        : $attributesOrig['no_sound'],
                    'duration' => $file instanceof AbstractVideo
                        ? $file->duration
                        : $attributesOrig['duration'],
                    'w' => $file instanceof AbstractVideo
                        ? $file->width
                        : $attributesOrig['w'],
                    'h' => $file instanceof AbstractVideo
                        ? $file->height
                        : $attributesOrig['h'],
                ],
            ],
            Audio::class => [
                [
                    '_' => 'documentAttributeAudio',
                    'voice' => false,
                    'duration' => $file instanceof Audio
                        ? $file->duration
                        : $attributesOrig['duration'],
                    'title' => $file instanceof Audio
                        ? $file->title
                        : $attributesOrig['title'],
                    'performer' => $file instanceof Audio
                        ? $file->performer
                        : $attributesOrig['performer'],
                ],
            ],
            Voice::class => [
                [
                    '_' => 'documentAttributeAudio',
                    'voice' => true,
                    'duration' => $file instanceof Voice
                        ? $file->duration
                        : $attributesOrig['duration'],
                    'waveform' => $file instanceof Voice
                        ? $file->waveform
                        : $attributesOrig['waveform'],
                ],
            ],
            Sticker::class => [$attributesOrig],
            default => [],
        };
        if ($type === Gif::class) {
            $attributes []= ['_' => 'documentAttributeAnimated'];
        }
        $attributes[] = ['_' => 'documentAttributeFilename', 'file_name' => $fileName];

        if ($peer !== null && DialogId::isSecretChat($peer)) {
            if ($uploadOnly) {
                throw new AssertionError("Cannot upload media without sending it when working with secret chats, use the send* variant instead!");
            }
            $message = [
                '_' => 'decryptedMessage',
                'ttl' => $ttl,
                'silent' => $silent,
                'background' => $background,
                'clear_draft' => $clearDraft,
                'noforwards' => $noForwards,
                'update_stickersets_order' => $updateStickersetsOrder,
                'reply_to_random_id' => $replyToMsgId,
                'top_msg_id' => $topMsgId,
                'message' => $caption,
                'reply_markup' => $replyMarkup,
                'parse_mode' => $parseMode,
                'schedule_date' => $scheduleDate,
                'send_as' => $sendAs,
            ];

            $thumb_width = 0;
            $thumb_height = 0;
            $width = 0;
            $height = 0;

            if ($type === Photo::class || $type === DocumentPhoto::class || $type === Sticker::class) {
                if (!\extension_loaded('gd')) {
                    throw Exception::extension('gd');
                }
                $file = buffer($this->getStream($file, $cancellation), $cancellation);
                $img = imagecreatefromstring($file);
                $width = imagesx($img);
                $height = imagesy($img);
                $file = new ReadableBuffer($file);
                if ($type === Photo::class || $type === DocumentPhoto::class) {
                    if ($width > $height) {
                        $thumb_width = 90;
                        $thumb_height = (int) (90*$height/$width);
                    } elseif ($width < $height) {
                        $thumb_width = (int) (90*$width/$height);
                        $thumb_height = 90;
                    } else {
                        $thumb_width = 90;
                        $thumb_height = 90;
                    }
                    Assert::lessThanEq($thumb_height, 90);
                    Assert::lessThanEq($thumb_width, 90);
                    $thumb = imagecreatetruecolor($thumb_width, $thumb_height);
                    imagecopyresized($thumb, $img, 0, 0, 0, 0, $thumb_width, $thumb_height, $width, $height);

                    $stream = fopen('php://memory', 'r+');
                    imagepng($thumb, $stream);
                    rewind($stream);
                    $thumb = stream_get_contents($stream);
                    fclose($stream);
                    unset($stream);

                    if ($type === DocumentPhoto::class) {
                        $attributes []= ['_' => 'documentAttributeImageSize', 'w' => $width, 'h' => $height];
                    }
                } else {
                    $attributes []= ['_' => 'documentAttributeImageSize', 'w' => $width, 'h' => $height];
                }
            } elseif ($type === Video::class || $type === Gif::class) {
                $this->extractVideoInfo(true, $attributesOrig['thumbSeek'], $file, $fileName, $callback, $cancellation, $mimeType, $attributes, $thumb);
            } elseif ($type === Audio::class || $type === Voice::class) {
                $this->extractAudioInfo(true, $file, $fileName, $callback, $cancellation, $mimeType, $attributes, $thumb);
            } elseif ($mimeType === null) {
                $mimeType = $this->extractMime(true, $file, $fileName, $callback, $cancellation);
            }

            if ($thumb !== null && $thumb_width === 0) {
                $thumb = buffer($this->getStream($thumb, $cancellation), $cancellation);
                if (!\extension_loaded('gd')) {
                    throw Exception::extension('gd');
                }
                [$thumb_width, $thumb_height] = getimagesizefromstring($thumb);
            } elseif ($thumb === null && $file instanceof Media) {
                // Its inline thumbnail (the 90x90 one of secret chat media, or a stripped preview): the remote ones are too big.
                foreach ($file->thumbs as $t) {
                    if ($t instanceof InlineThumbnail) {
                        $thumb = buffer($t->getStream(cancellation: $cancellation), $cancellation);
                        $thumb_width = $t->width;
                        $thumb_height = $t->height;
                        break;
                    }
                }
            }

            if (\is_string($thumb)) {
                $thumb = new Bytes($thumb);
            }

            // TODO: legacy audio, video (probably not even worth it)
            $message['media'] = match ($type) {
                Photo::class => [
                    '_' => 'decryptedMessageMediaPhoto',
                    'thumb' => $thumb,
                    'thumb_w' => $thumb_width,
                    'thumb_h' => $thumb_height,
                    'w' => $width,
                    'h' => $height,
                    'mime_type' => $mimeType,
                    'attributes' => $attributes,
                    'caption' => $caption,
                ],
                default => [
                    '_' => 'decryptedMessageMediaDocument',
                    'thumb' => $thumb,
                    'thumb_w' => $thumb_width,
                    'thumb_h' => $thumb_height,
                    'mime_type' => $mimeType,
                    'attributes' => $attributes,
                    'caption' => $caption,
                ],
            };

            if ($file instanceof $type
                && ($fileName ?? $file->fileName) === $file->fileName
                && $file->encrypted
                && !$forceResend
            ) {
                // Reuse
                $file = $file->location;
                $message['media']['key'] = $file['key'];
                $message['media']['iv'] = $file['iv'];
                $message['media']['size'] = $file['size'];
            } elseif (\is_array($file)) {
                $message['media']['key'] = $file['key'];
                $message['media']['iv'] = $file['iv'];
                $message['media']['size'] = $file['size'];
            } else {
                $file = $this->uploadEncrypted($file, $fileName ?? '', $callback, $cancellation);
                $message['media']['key'] = $file['key'];
                $message['media']['iv'] = $file['iv'];
                $message['media']['size'] = $file['size'];
            }
            $params = [
                'peer' => $peer,
                'message' => $message,
                'file' => $file,
                'cancellation' => $cancellation,
            ];
            $res = $this->wrapMessage($this->extractMessage($this->methodCallAsyncRead(
                'messages.sendEncryptedFile',
                $params
            )));
            \assert($res !== null);
            return $res;
        }
        if ($reuseId) {
            // Reuse
        } elseif ($type === Video::class || $type === Gif::class) {
            $this->extractVideoInfo(false, $attributesOrig['thumbSeek'], $file, $fileName, $callback, $cancellation, $mimeType, $attributes, $thumb);
        } elseif ($type === Audio::class || $type === Voice::class) {
            $this->extractAudioInfo(false, $file, $fileName, $callback, $cancellation, $mimeType, $attributes, $thumb);
        } elseif ($mimeType === null) {
            $mimeType = $this->extractMime(false, $file, $fileName, $callback, $cancellation);
        }

        if ($type === DocumentPhoto::class) {
            $attributes []= [
                '_' => 'documentAttributeImageSize',
            ];
        }

        $media = match ($type) {
            Photo::class => [
                '_' => 'inputMediaUploadedPhoto',
                'spoiler' => $spoiler,
                'file' => $file,
                'ttl_seconds' => $ttl,
            ],
            Sticker::class => [
                '_' => 'inputMediaUploadedDocument',
                'file' => $file,
                'mime_type' => $mimeType,
                'attributes' => $attributes,
            ],
            Video::class => [
                '_' => 'inputMediaUploadedDocument',
                'nosound_video' => $attributes[0]['no_sound'],
                'spoiler' => $spoiler,
                'ttl_seconds' => $ttl,
                'force_file' => false,
                'file' => $file,
                'thumb' => $thumb,
                'mime_type' => $mimeType,
                'attributes' => $attributes,
            ],
            Gif::class => [
                '_' => 'inputMediaUploadedDocument',
                'spoiler' => $spoiler,
                'ttl_seconds' => $ttl,
                'file' => $file,
                'thumb' => $thumb,
                'mime_type' => $mimeType,
                'attributes' => $attributes,
            ],
            Audio::class => [
                '_' => 'inputMediaUploadedDocument',
                'file' => $file,
                'ttl_seconds' => $ttl,
                'thumb' => $thumb,
                'mime_type' => $mimeType,
                'attributes' => $attributes,
            ],
            Voice::class => [
                '_' => 'inputMediaUploadedDocument',
                'file' => $file,
                'ttl_seconds' => $ttl,
                'mime_type' => $mimeType,
                'attributes' => $attributes,
            ],
            default => [
                '_' => 'inputMediaUploadedDocument',
                'spoiler' => $spoiler,
                'ttl_seconds' => $ttl,
                'force_file' => true,
                'file' => $file,
                'thumb' => $thumb,
                'mime_type' => $mimeType,
                'attributes' => $attributes,
            ]
        };
        if ($reuseId) {
            $media['_'] = match ($type) {
                Photo::class => 'inputMediaPhoto',
                default => 'inputMediaDocument',
            };
            $media['id'] = $reuseId;
        } elseif (!\is_array($media['file'])) {
            $media['file'] = $this->upload($media['file'], $fileName ?? '', $callback, cancellation: $cancellation);
        }

        $params = [
            'silent' => $silent,
            'background' => $background,
            'clear_draft' => $clearDraft,
            'noforwards' => $noForwards,
            'update_stickersets_order' => $updateStickersetsOrder,
            'peer' => $peer,
            'reply_to' => $replyToMsgId !== null || $topMsgId !== null ? [
                '_' => 'inputReplyToMessage',
                'reply_to_msg_id' => $replyToMsgId,
                'top_msg_id' => $topMsgId,
            ] : null,
            'message' => $caption,
            'reply_markup' => $replyMarkup,
            'parse_mode' => $parseMode,
            'schedule_date' => $scheduleDate,
            'send_as' => $sendAs,
            'media' => $media,
            'random_id' => $randomId,
            'cancellation' => $cancellation,
            // Already resumed as a whole by sendMedia().
            'resumable' => false,
        ];
        if ($ttl) {
            if ($uploadOnly) {
                throw new AssertionError("Can't upload only for media with TTL!");
            }
        } else {
            $res = $this->methodCallAsyncRead(
                'messages.uploadMedia',
                $params
            );
            $params['media'] = $res;
            if ($uploadOnly) {
                return $this->wrapMedia($res);
            }
        }
        $res = $this->wrapMessage($this->extractMessage($this->methodCallAsyncRead(
            'messages.sendMedia',
            [...$params, 'resumableCall' => $resumableCall]
        )));
        \assert($res !== null);
        return $res;

    }

    /**
     * Provide a stream for a file to upload, and the key identifying its source across restarts,
     * to resume an interrupted upload (null if the source can't be identified).
     */
    private function openUploadSource(Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file, ?Cancellation $cancellation, ?int &$size, ?string &$resumeKey): ReadableStream
    {
        $resumeKey = null;
        if ($file instanceof LocalFile) {
            $path = Tools::absolute($file->file);
            $size = getSize($path);
            $resumeKey = 'file:'.$path."\0".getModificationTime($path);
            return openFile($path, 'r');
        }
        if ($file instanceof RemoteUrl) {
            self::$client ??= HttpClientBuilder::buildDefault();
            $request = new Request($file->url);
            $request->setTransferTimeout(INF);
            $request->setInactivityTimeout(INF);
            $request->setBodySizeLimit(512 * 1024 * 8000);
            $response = self::$client->request($request, $cancellation);
            if (($status = $response->getStatus()) !== 200) {
                throw new Exception("Wrong status code: {$status} ".$response->getReason());
            }
            $size = (int) ($response->getHeader('content-length') ?? $size);
            $validator = $response->getHeader('etag') ?? $response->getHeader('last-modified');
            if ($validator !== null) {
                $resumeKey = 'url:'.$file->url."\0".$validator;
            }
            return $response->getBody();
        }
        $stream = $this->getStream($file, $cancellation, $size);
        if ($stream instanceof ResumableStream) {
            // Before anything is read from it.
            $resumeKey = 'stream:'.serialize($stream);
        }
        if (!$size && $stream instanceof ResumableDownloadStream) {
            // Uploads can only be resumed if their size is known.
            $size = $stream->getRemainingSize() ?? 0;
        }
        return $stream;
    }
    private function extractMime(bool $secret, Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream &$file, ?string $fileName, ?callable $callback, ?Cancellation $cancellation): string
    {
        $size = 0;
        $file = $this->openUploadSource($file, $cancellation, $size, $resumeKey);
        $p = new Pipe(1024*1024);
        $fileFuture = async(fn () => $this->uploadFromStreamInternal(
            new StreamDuplicator($file, $p->getSink()),
            $size,
            null,
            $fileName ?? '',
            $callback,
            $secret,
            $cancellation,
            $resumeKey,
        ));

        $buff = '';
        while (\strlen($buff) < 1024*1024 && null !== $chunk = $p->getSource()->read($cancellation)) {
            $buff .= $chunk;
        }
        $p->getSink()->close();
        $p->getSource()->close();
        unset($p);

        $file = $fileFuture->await();
        $mime = (new finfo())->buffer($buff, FILEINFO_MIME_TYPE) ?: 'application/octet-stream';
        $file['mime_type'] = $mime;
        return $mime;
    }
    private function extractAudioInfo(bool $secret, Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream &$file, ?string $fileName, ?callable $callback, ?Cancellation $cancellation, ?string &$mimeType, array &$attributes, mixed &$thumb): void
    {
        if (!Tools::canUseFFmpeg($cancellation)) {
            if ($secret) {
                throw new AssertionError('Install ffmpeg for audio info extraction!');
            }
            $this->logger->logger('Install ffmpeg for audio info extraction!');
            if ($mimeType === null) {
                $mimeType = $this->extractMime($secret, $file, $fileName, $callback, $cancellation);
            }
            return;
        }
        if (!(
            $attributes[0]['duration'] === null
            || $attributes[0]['title'] === null
            || $attributes[0]['performer'] === null
            || $thumb === null
        )) {
            return;
        }

        $size = 0;
        $file = $this->openUploadSource($file, $cancellation, $size, $resumeKey);
        $process = Process::start('ffmpeg -i pipe: -f image2pipe -', cancellation: $cancellation);
        $stdin = $process->getStdin();
        $stdout = $process->getStdout();
        $f = [
            async(buffer(...), $process->getStdout(), $cancellation),
            async(buffer(...), $process->getStderr(), $cancellation),
        ];
        $streams = [$stdin];

        if ($mimeType === null) {
            $p = new Pipe(1024*1024);
            $streams []= $p->getSink();
            $f []= async(static function () use ($p, $cancellation, &$mimeType): void {
                $buff = '';
                while (\strlen($buff) < 1024*1024 && null !== $chunk = $p->getSource()->read($cancellation)) {
                    $buff .= $chunk;
                }
                $p->getSink()->close();
                $p->getSource()->close();

                $mimeType = (new finfo())->buffer($buff, FILEINFO_MIME_TYPE) ?: 'application/octet-stream';
            });
            unset($p);
        }

        $fileFuture = async(fn () => $this->uploadFromStreamInternal(
            new StreamDuplicator($file, ...$streams),
            $size,
            null,
            $fileName ?? '',
            $callback,
            $secret,
            $cancellation,
            $resumeKey,
        ));
        [$stdout, $stderr] = await($f);

        $process->join($cancellation);
        if (preg_match('~Duration: (\d{2}:\d{2}:\d{2}\.\d{2})~', $stderr, $matches)) {
            $time = explode(':', $matches[1]);
            $hours = (int) $time[0];
            $minutes = (int) $time[1];
            $seconds = (int) $time[2];
            $duration = $hours * 3600 + $minutes * 60 + $seconds;
            $attributes[0]['duration'] ??= (int) $duration;
        }
        if (preg_match('/TITLE\s*:\s*(.+)/', $stderr, $matches)) {
            $attributes[0]['title'] ??= $matches[1];
        }
        if (preg_match('/ARTIST\s*:\s*(.+)/', $stderr, $matches)) {
            $attributes[0]['performer'] ??= $matches[1];
        }
        if ($stdout !== '') {
            // Todo check if jpg, but should be jpg in most cases anyway
            $thumb ??= new ReadableBuffer($stdout);
        }

        $file = $fileFuture->await();
        $file['mime_type'] = $mimeType;
    }

    private function extractVideoInfo(bool $secret, string $thumbSeek, Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream &$file, ?string $fileName, ?callable $callback, ?Cancellation $cancellation, ?string &$mimeType, array &$attributes, mixed &$thumb): void
    {
        if (!Tools::canUseFFmpeg($cancellation)) {
            if ($secret) {
                throw new AssertionError('Install ffmpeg for video info extraction!');
            }
            $this->logger->logger('Install ffmpeg for video info extraction!');
            if ($mimeType === null) {
                $mimeType = $this->extractMime($secret, $file, $fileName, $callback, $cancellation);
            }
            return;
        }
        if (!(
            $thumb === null
            || $attributes[0]['duration'] === null
            || $attributes[0]['w'] === null
            || $attributes[0]['h'] === null
        )) {
            return;
        }

        $size = 0;
        $file = $this->openUploadSource($file, $cancellation, $size, $resumeKey);
        $ffmpeg = 'ffmpeg -i pipe: -ss '.$thumbSeek.' -frames:v 1 -f image2pipe -';
        $process = Process::start($ffmpeg, cancellation: $cancellation);
        $stdin = $process->getStdin();
        $f = [
            async(buffer(...), $process->getStdout(), $cancellation),
            async(buffer(...), $process->getStderr(), $cancellation),
        ];
        $streams = [$stdin];
        if ($mimeType === null) {
            $p = new Pipe(1024*1024);
            $streams []= $p->getSink();
            $f []= async(static function () use ($p, $cancellation, &$mimeType): void {
                $buff = '';
                while (\strlen($buff) < 1024*1024 && null !== $chunk = $p->getSource()->read($cancellation)) {
                    $buff .= $chunk;
                }
                $p->getSink()->close();
                $p->getSource()->close();

                $mimeType = (new finfo())->buffer($buff, FILEINFO_MIME_TYPE) ?: 'application/octet-stream';
            });
            unset($p);
        }

        $fileFuture = async(fn () => $this->uploadFromStreamInternal(
            new StreamDuplicator($file, ...$streams),
            $size,
            $mimeType,
            $fileName ?? '',
            $callback,
            $secret,
            $cancellation,
            $resumeKey,
        ));
        [$stdout, $stderr] = await($f);

        if ($stdout !== '') {
            $thumb ??= new ReadableBuffer($stdout);
        }
        $process->join($cancellation);

        if (preg_match('~Duration: (\d{2}:\d{2}:\d{2}\.\d{2}),.*? (\d{3,4})x(\d{3,4})~s', $stderr, $matches)) {
            $time = explode(':', $matches[1]);
            $hours = (int) $time[0];
            $minutes = (int) $time[1];
            $seconds = (int) $time[2];
            $duration = $hours * 3600 + $minutes * 60 + $seconds;
            $width = $matches[2];
            $height = $matches[3];
            $attributes[0]['w'] ??= (int) $width;
            $attributes[0]['h'] ??= (int) $height;
            $attributes[0]['duration'] ??= (int) $duration;
        }

        $file = $fileFuture->await();
        $file['mime_type'] = $mimeType;
    }
}
<?php

declare(strict_types=1);

/**
 * Files module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\MTProtoTools;

use Amp\Sync\LocalKeyedMutex;
use Amp\Sync\LocalMutex;
use AssertionError;
use danog\AsyncOrm\Annotations\OrmMappedArray;
use danog\AsyncOrm\DbArray;
use danog\AsyncOrm\DbArrayBuilder;
use danog\AsyncOrm\KeyType;
use danog\AsyncOrm\ValueType;
use danog\DialogId\DialogId;
use danog\MadelineProto\EventHandler\Media\Photo;
use danog\MadelineProto\Exception;
use danog\MadelineProto\LegacyMigrator;
use danog\MadelineProto\Logger;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\PeerNotInDbException;
use danog\MadelineProto\RPCError\UsernameNotOccupiedError;
use danog\MadelineProto\RPCErrorException;
use danog\MadelineProto\TL\TLCallback;
use danog\MadelineProto\Tools;
use InvalidArgumentException;
use Revolt\EventLoop;
use Webmozart\Assert\Assert;

/**
 * Manages peers.
 *
 * @internal
 */
final class PeerDatabase implements TLCallback
{
    use LegacyMigrator;

    private const V = 1;

    /**
     * Chats.
     *
     * @var DbArray<int, array>
     */
    #[OrmMappedArray(KeyType::INT, ValueType::SCALAR, tablePostfix: '_MTProto_chats')]
    private $db;
    /**
     * @var DbArray<int, array>
     */
    #[OrmMappedArray(KeyType::INT, ValueType::SCALAR, tablePostfix: '_MTProto_full_chats')]
    private $fullDb;
    /**
     * @var DbArray<string, int>
     */
    #[OrmMappedArray(KeyType::STRING, ValueType::INT, tablePostfix: '_PeerDatabase_usernames')]
    private $usernames;
    private bool $hasInfo = true;
    private bool $hasUsernames = true;

    /** @var array<int, array> */
    private array $pendingDb = [];

    private int $v = self::V;

    private LocalMutex $decacheMutex;
    private LocalKeyedMutex $mutex;
    public function __construct(private MTProto $API)
    {
        $this->decacheMutex = new LocalMutex;
        $this->mutex = new LocalKeyedMutex;
    }
    /**
     * @psalm-pure
     */
    public function __sleep()
    {
        return ['db', 'fullDb', 'usernames', 'API', 'pendingDb', 'v', 'hasInfo'];
    }
    public function __wakeup(): void
    {
        $this->decacheMutex = new LocalMutex;
        $this->mutex = new LocalKeyedMutex;
    }
    public function init(): void
    {
        $this->initDbProperties($this->API->getDbSettings(), $this->API->getDbPrefix());
        if (!$this->API->settings->getDb()->getEnableFullPeerDb()) {
            $this->fullDb->clear();
        }
        if (!$this->API->settings->getDb()->getEnablePeerInfoDb() && $this->hasInfo) {
            $this->API->logger('Cleaning up peer database...');
            $k = 0;
            $total = \count($this->db);
            foreach ($this->db as $key => $value) {
                $value = [
                    '_' => $value['_'],
                    'id' => $value['id'],
                    'access_hash' => $value['access_hash'] ?? null,
                    'min' => $value['min'] ?? false,
                ];
                $k++;
                if ($k % 500 === 0 || $k === $total) {
                    $this->API->logger("Cleaning up peer database ($k/$total)...");
                }
                $this->db[$key] = $value;
            }
            $this->hasInfo = false;
            $this->API->logger('Cleaned up peer database!');
        }

        if (!$this->API->settings->getDb()->getEnableUsernameDb()) {
            $this->usernames->clear();
        } elseif ($this->v === 0) {
            $old = new DbArrayBuilder(
                $this->API->getDbPrefix().'_MTProto_usernames',
                $this->API->getDbSettings(),
                KeyType::STRING,
                ValueType::SCALAR
            );
            $old = $old->build();
            $kk = 0;
            $total = \count($old);
            foreach ($old as $k => $v) {
                $kk++;
                if ($kk % 500 === 0 || $kk === $total) {
                    $this->API->logger("Migrating username database ($kk/$total)...");
                }
                $this->usernames[$k] = $v;
            }
            $old->clear();
            $this->v = self::V;
        }

        EventLoop::queue(function (): void {
            $this->API->waitForInit();
            foreach ($this->pendingDb as $key => $_) {
                EventLoop::queue($key < 0 ? $this->processChat(...) : $this->processUser(...), $key);
            }
        });
    }

    public function clearAll(): void
    {
        $this->db->clear();
        $this->fullDb->clear();
    }

    public function getFull(int $id): ?array
    {
        $result = $this->fullDb[$id];
        if ($result !== null) {
            $result['id'] = $id;
        }
        return $result;
    }
    public function expireFull(int $id): void
    {
        unset($this->fullDb[$id]);
    }
    public function get(int $id): ?array
    {
        while (isset($this->pendingDb[$id])) {
            if ($id < 0) {
                $this->processChat($id);
            } else {
                $this->processUser($id);
            }
        }
        $result = $this->db[$id];
        if ($result !== null) {
            $result['id'] = $id;
        }
        return $result;
    }
    public function isset(int $id): bool
    {
        return isset($this->pendingDb[$id]) || isset($this->db[$id]);
    }
    public function clear(int $id): void
    {
        if ($id < 0) {
            $this->processChat($id);
        } else {
            $this->processUser($id);
        }
        $this->recacheChatUsername($id, $this->db[$id] ?? null, []);
        unset($this->db[$id]);
    }

    /**
     * @return list<int>
     */
    public function getDialogIds(): array
    {
        foreach ($this->pendingDb as $key => $_) {
            if ($key < 0) {
                $this->processChat($key);
            } else {
                $this->processUser($key);
            }
        }
        $res = [];
        foreach ($this->db->getIterator() as $id => $_) {
            $res []= (int) $id;
        }
        return $res;
    }

    /**
     * Refresh full peer cache for a certain peer.
     *
     * @param mixed $id The peer to refresh
     */
    public function refreshFullPeerCache(mixed $id): void
    {
        if (\is_string($id)) {
            if ($r = Tools::parseLink($id)) {
                [$invite, $content] = $r;
                if ($invite) {
                    $invite = $this->API->methodCallAsyncRead('messages.checkChatInvite', ['hash' => $content]);
                    if (!isset($invite['chat'])) {
                        throw new Exception('You have not joined this chat');
                    }
                    $id = $invite['chat'];
                } else {
                    $id = $content;
                }
            }
            if (\is_string($id)) {
                $id = strtolower(str_replace('@', '', $id));
                if ($id === 'me') {
                    $id = $this->API->authorization['user']['id'];
                } elseif ($id === 'support') {
                    $id = $this->API->methodCallAsyncRead('help.getSupport', [])['user'];
                } else {
                    $id = $this->resolveUsername($id);
                    if ($id === null) {
                        throw new PeerNotInDbException;
                    }
                }
            }
        }
        unset($this->fullDb[$this->API->getIdInternal($id)]);
        $this->API->getFullInfo($id);
    }
    /**
     * When were full info for this chat last cached.
     *
     * @param mixed $id Chat ID
     */
    public function fullChatLastUpdated(mixed $id): int
    {
        return $this->getFull($id)['inserted'] ?? 0;
    }

    private function recacheChatUsername(int $id, ?array $old, array $new): void
    {
        if (!$this->API->settings->getDb()->getEnableUsernameDb()) {
            return;
        }
        $new = $new ? self::getUsernames($new) : [];
        $old = $old ? self::getUsernames($old) : [];
        foreach ($old as $key => $username) {
            if (!isset($this->usernames[$username])) {
                unset($old[$key]);
            }
        }
        $diffToRemove = array_diff($old, $new);
        $diffToAdd = array_diff($new, $diffToRemove);
        $lock = $this->decacheMutex->acquire();
        try {
            foreach ($diffToRemove as $username) {
                if ($this->usernames[$username] === $id) {
                    unset($this->usernames[$username]);
                }
            }
            foreach ($diffToAdd as $username) {
                $this->usernames[$username] = $id;
            }
        } finally {
            EventLoop::queue($lock->release(...));
        }
    }

    /**
     * @return list<lowercase-string>
     */
    public static function getUsernames(array $constructor): array
    {
        $usernames = [];
        if ($constructor['_'] === 'user' || $constructor['_'] === 'channel') {
            if (isset($constructor['username'])) {
                $usernames []= strtolower($constructor['username']);
            }
            foreach ($constructor['usernames'] ?? [] as ['username' => $username]) {
                $usernames []= strtolower($username);
            }
        }
        return $usernames;
    }

    public function getIdFromUsername(string $username): ?int
    {
        foreach ($this->pendingDb as $key => $_) {
            if ($key < 0) {
                $this->processChat($key);
            } else {
                $this->processUser($key);
            }
        }
        $result = $this->usernames[$username];
        $id = $result === null ? $result : (int) $result;
        if ($id !== null && !$this->isset($id)) {
            $this->API->logger("No peer entry for cached username @$username => {$id}, dropping entry!");
            unset($this->usernames[$username]);
            return null;
        }
        return $id;
    }

    private array $caching_simple_username = [];
    /**
     * @param string $username Username
     */
    public function resolveUsername(string $username): ?int
    {
        $username = strtolower(str_replace('@', '', $username));
        if (!$username) {
            return null;
        }
        if (isset($this->caching_simple_username[$username])) {
            // Used to avoid possible issues from addUser
            return null;
        }
        $this->caching_simple_username[$username] = true;
        $result = null;
        try {
            $result = $this->API->getIdInternal(
                ($this->API->methodCallAsyncRead('contacts.resolveUsername', ['username' => $username]))['peer'],
            );
        } catch (UsernameNotOccupiedError) {
        } finally {
            unset($this->caching_simple_username[$username]);
        }
        foreach ($this->pendingDb as $key => $_) {
            if ($key < 0) {
                $this->processChat($key);
            } else {
                $this->processUser($key);
            }
        }
        return $result;
    }
    /**
     * Add user info.
     *
     * @internal
     *
     * @param array $user User info
     */
    private function addUser(array $user): void
    {
        if ($user['_'] === 'userEmpty') {
            return;
        }
        if ($user['_'] !== 'user') {
            throw new AssertionError('Invalid user type '.$user['_']);
        }
        if ($user['id'] === ($this->API->authorization['user']['id'] ?? null)) {
            $this->API->authorization['user'] = $user;
        }
        if (($this->pendingDb[$user['id']] ?? null) == $user) {
            return;
        }
        $this->pendingDb[$user['id']] = $user;
        EventLoop::queue($this->processUser(...), $user['id']);
    }
    private function processUser(int $id): void
    {
        if (!isset($this->pendingDb[$id])) {
            return;
        }
        $lock = $this->mutex->acquire((string) $id);
        try {
            if (!isset($this->pendingDb[$id])) {
                return;
            }
            $o = $user = $this->pendingDb[$id];
            $existingChat = $this->db[$user['id']];
            if (!isset($user['access_hash']) && !($user['min'] ?? false)) {
                if (isset($existingChat['access_hash'])) {
                    $this->API->logger("No access hash with user {$user['id']}, using backup");
                    $user['access_hash'] = $existingChat['access_hash'];
                    $user['min'] = false;
                } else {
                    Assert::null($existingChat);
                    try {
                        $this->API->methodCallAsyncRead('users.getUsers', ['id' => [[
                            '_' => 'inputUser',
                            'user_id' => $user['id'],
                            'access_hash' => 0,
                        ]]]);
                        return;
                    } catch (RPCErrorException $e) {
                        $this->API->logger("An error occurred while trying to fetch the missing access_hash for user {$user['id']}: {$e->getMessage()}", Logger::FATAL_ERROR);
                    }
                    foreach (self::getUsernames($user) as $username) {
                        if (($this->resolveUsername($username)) === $user['id']) {
                            return;
                        }
                    }
                    return;
                }
            }

            $this->recacheChatUsername($user['id'], $existingChat, $user);

            if ($existingChat != $user) {
                $this->API->logger("Updated user {$user['id']}", Logger::ULTRA_VERBOSE);
                if (($user['min'] ?? false) && !($existingChat['min'] ?? false)) {
                    $this->API->logger("{$user['id']} is min, filling missing fields", Logger::ULTRA_VERBOSE);
                    if (isset($existingChat['access_hash'])) {
                        $user['min'] = false;
                        $user['access_hash'] = $existingChat['access_hash'];
                    }
                }
                $userUnchanged = $user;
                if (!$this->API->settings->getDb()->getEnablePeerInfoDb()) {
                    $user = [
                        '_' => $user['_'],
                        'id' => $user['id'],
                        'access_hash' => $user['access_hash'] ?? null,
                        'min' => $user['min'] ?? false,
                    ];
                }
                $this->db[$user['id']] = $user;
                $this->recacheChatUsername($user['id'], $existingChat, $userUnchanged);
                if ($existingChat && ($existingChat['min'] ?? false) && !($user['min'] ?? false)) {
                    $this->API->minDatabase->clearPeer($user['id']);
                }
                if ($existingChat && ($existingChat['bot_info_version'] ?? null) !== ($user['bot_info_version'] ?? null)) {
                    $this->expireFull($user['id']);
                }
            }
        } finally {
            if (isset($o) && $this->pendingDb[$id] === $o) {
                unset($this->pendingDb[$id]);
            }
            EventLoop::queue($lock->release(...));
        }
    }
    /**
     * Merge a partial user update into the cached user.
     *
     * @internal
     *
     * @param array $update updateUserStatus or updateUserEmojiStatus
     */
    private function addUserUpdate(array $update): void
    {
        [$key, $value] = match ($update['_']) {
            'updateUserStatus' => ['status', $update['status']],
            'updateUserEmojiStatus' => ['emoji_status', $update['emoji_status']['_'] === 'emojiStatusEmpty' ? null : $update['emoji_status']],
        };
        $id = $update['user_id'];
        if ($id === ($this->API->authorization['user']['id'] ?? null)) {
            $this->API->authorization['user'] = self::withField($this->API->authorization['user'], $key, $value);
        }
        if ($this->API->settings->getDb()->getEnablePeerInfoDb()) {
            EventLoop::queue($this->processUserUpdate(...), $id, $key, $value);
        }
    }
    private function processUserUpdate(int $id, string $key, ?array $value): void
    {
        // Queued after any user object received before the update: a user object still pending once the
        // lock is held was received after the update and carries a newer value.
        $lock = $this->mutex->acquire((string) $id);
        try {
            $user = $this->db[$id];
            if ($user === null || ($user[$key] ?? null) == $value) {
                return;
            }
            $this->db[$id] = self::withField($user, $key, $value);
        } finally {
            EventLoop::queue($lock->release(...));
        }
    }
    /**
     * @psalm-pure
     */
    private static function withField(array $user, string $key, ?array $value): array
    {
        if ($value === null) {
            unset($user[$key]);
        } else {
            $user[$key] = $value;
        }
        return $user;
    }
    public function addChatBlocking(int $chat): void
    {
        if (isset($this->pendingDb[$chat])) {
            $this->processChat($chat);
        } else {
            $this->pendingDb[$chat] = [
                '_' => 'channel',
                'id' => $chat,
            ];
            $this->processChat($chat);
        }
    }
    public function addUserBlocking(int $user): void
    {
        if (isset($this->pendingDb[$user])) {
            $this->processChat($user);
        } else {
            $this->pendingDb[$user] = [
                '_' => 'user',
                'id' => $user,
            ];
            $this->processUser($user);
        }
    }
    /**
     * Add chat to database.
     *
     * @param array $chat Chat
     * @internal
     */
    private function addChat(array $chat): void
    {
        $id = $this->API->getIdInternal($chat);
        if (($this->pendingDb[$id] ?? null) == $chat) {
            return;
        }
        $this->pendingDb[$id] = $chat;
        EventLoop::queue($this->processChat(...), $id);
    }
    private function processChat(int $id): void
    {
        if (!isset($this->pendingDb[$id])) {
            return;
        }
        $lock = $this->mutex->acquire((string) $id);
        try {
            if (!isset($this->pendingDb[$id])) {
                return;
            }
            $o = $chat = $this->pendingDb[$id];
            if ($chat['_'] === 'chat'
                || $chat['_'] === 'chatEmpty'
                || $chat['_'] === 'chatForbidden'
            ) {
                $existingChat = $this->db[$chat['id']];
                if (!$existingChat || $existingChat != $chat) {
                    $this->API->logger("Updated chat {$chat['id']}", Logger::ULTRA_VERBOSE);
                    if (!$this->API->settings->getDb()->getEnablePeerInfoDb()) {
                        $chat = [
                            '_' => $chat['_'],
                            'id' => $chat['id'],
                            'access_hash' => $chat['access_hash'] ?? null,
                            'min' => $chat['min'] ?? false,
                        ];
                    }
                    $this->db[$chat['id']] = $chat;
                }
                return;
            }
            if ($chat['_'] === 'channelEmpty') {
                return;
            }
            if ($chat['_'] !== 'channel'
                && $chat['_'] !== 'channelForbidden'
                && $chat['_'] !== 'community'
                && $chat['_'] !== 'communityForbidden'
            ) {
                throw new InvalidArgumentException('Invalid chat type '.$chat['_']);
            }
            $bot_api_id = $chat['id'];
            $existingChat = $this->db[$bot_api_id];
            if (!isset($chat['access_hash']) && !($chat['min'] ?? false)) {
                if (isset($existingChat['access_hash'])) {
                    $this->API->logger("No access hash with channel {$bot_api_id}, using backup");
                    $chat['access_hash'] = $existingChat['access_hash'];
                } else {
                    Assert::null($existingChat);
                    try {
                        $this->API->methodCallAsyncRead('channels.getChannels', ['id' => [[
                            '_' => 'inputChannel',
                            'channel_id' => DialogId::toMTProtoId($chat['id']),
                            'access_hash' => 0,
                        ]]]);
                        return;
                    } catch (RPCErrorException $e) {
                        $this->API->logger("An error occurred while trying to fetch the missing access_hash for channel {$bot_api_id}: {$e->getMessage()}", Logger::FATAL_ERROR);
                    }
                    foreach (self::getUsernames($chat) as $username) {
                        if (($this->resolveUsername($username)) === $bot_api_id) {
                            return;
                        }
                    }
                    return;
                }
            }
            $this->recacheChatUsername($bot_api_id, $existingChat, $chat);
            if ($existingChat != $chat) {
                $this->API->logger("Updated chat {$bot_api_id}", Logger::ULTRA_VERBOSE);
                if (($chat['min'] ?? false) && $existingChat && !($existingChat['min'] ?? false)) {
                    $this->API->logger("{$bot_api_id} is min, filling missing fields", Logger::ULTRA_VERBOSE);
                    $newchat = $existingChat;
                    foreach (['title', 'username', 'usernames', 'photo', 'banned_rights', 'megagroup', 'verified'] as $field) {
                        if (isset($chat[$field])) {
                            $newchat[$field] = $chat[$field];
                        }
                    }
                    $chat = $newchat;
                }
                $chatUnchanged = $chat;
                if (!$this->API->settings->getDb()->getEnablePeerInfoDb()) {
                    $chat = [
                        '_' => $chat['_'],
                        'id' => $chat['id'],
                        'access_hash' => $chat['access_hash'] ?? null,
                        'min' => $chat['min'] ?? false,
                    ];
                }
                $this->db[$bot_api_id] = $chat;
                $this->recacheChatUsername($bot_api_id, $existingChat, $chatUnchanged);
                if ($existingChat && ($existingChat['min'] ?? false) && !($chat['min'] ?? false)) {
                    $this->API->minDatabase->clearPeer($bot_api_id);
                }
            }
        } finally {
            if (isset($o) && $this->pendingDb[$id] === $o) {
                unset($this->pendingDb[$id]);
            }
            EventLoop::queue($lock->release(...));
        }
    }
    /**
     * @internal
     */
    private function addFullChat(array $full): void
    {
        foreach (['chat_photo', 'personal_photo', 'fallback_photo', 'profile_photo'] as $k) {
            if (isset($full[$k])) {
                if ($full[$k]['_'] === 'photoEmpty') {
                    unset($full[$k]);
                } else {
                    $full[$k] = new Photo($this->API, $full[$k], false);
                }
            }
        }
        $this->fullDb[$this->API->getIdInternal($full)] = [
            'full' => $full,
            'inserted' => time(),
        ];
    }

    /**
     * @psalm-pure
     */
    #[\Override]
    public function getMethodAfterResponseDeserializationCallbacks(): array
    {
        return [];
    }
    /**
     * @psalm-pure
     */
    #[\Override]
    public function getMethodBeforeResponseDeserializationCallbacks(): array
    {
        return [];
    }
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function getConstructorAfterDeserializationCallbacks(): array
    {
        return array_merge(
            array_fill_keys(['chat', 'chatEmpty', 'chatForbidden', 'channel', 'channelEmpty', 'channelForbidden', 'community', 'communityForbidden'], [$this->addChat(...)]),
            array_fill_keys(['user', 'userEmpty'], [$this->addUser(...)]),
            array_fill_keys(['updateUserStatus', 'updateUserEmojiStatus'], [$this->addUserUpdate(...)]),
            array_fill_keys(['chatFull', 'channelFull', 'userFull', 'communityFull'], [$this->addFullChat(...)]),
        );
    }
    /**
     * @psalm-pure
     */
    #[\Override]
    public function getConstructorBeforeDeserializationCallbacks(): array
    {
        return [];
    }
    /**
     * @psalm-pure
     */
    #[\Override]
    public function getConstructorBeforeSerializationCallbacks(): array
    {
        return [];
    }
    /**
     * @internal
     *
     * @psalm-pure
     */
    #[\Override]
    public function getTypeMismatchCallbacks(): array
    {
        return [];
    }

    /**
     * @psalm-mutation-free
     */
    public function __debugInfo()
    {
        return ['PeerDatabase instance '.spl_object_hash($this)];
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\MTProtoTools;

use Amp\ByteStream\ReadableStream;
use Amp\Future;
use Closure;
use danog\MadelineProto\BotApiFileId;
use danog\MadelineProto\EventHandler\Media;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\LocalFile;
use danog\MadelineProto\Logger;
use danog\MadelineProto\RemoteUrl;
use danog\MadelineProto\ResumableStream;
use danog\MadelineProto\RPCErrorException;
use danog\MadelineProto\Tools;
use danog\MadelineProto\UploadResumeException;
use Revolt\EventLoop;
use Throwable;

use function Amp\async;

/**
 * Resumes interrupted method calls that upload files.
 *
 * A method call that uploads a file from a source that survives a restart (a file, a URL, a Telegram
 * file or a resumable stream) is saved in the session until its files are uploaded: if the session is
 * restarted in the meantime, the call is made again, which resumes the interrupted upload and then
 * calls the method. Once the method is sent, the call isn't resumed anymore.
 *
 * The random IDs of the call are chosen beforehand and saved with it, so that if the session was saved
 * before the method was sent, and restarted after, Telegram does not execute it twice.
 *
 * @internal
 */
trait ResumableCalls
{
    /**
     * Interrupted calls, by ID.
     *
     * The arguments are serialized when the call is made, before its streams are read.
     *
     * @var array<string, array{kind: 'method'|'sendMedia', method: string, args: string, time: int}>
     */
    private array $resumableCalls = [];

    /**
     * Resumable calls running in this process, by ID.
     *
     * @var array<string, Future>
     */
    private array $runningResumableCalls = [];

    /**
     * Make a call, saving it until its method is sent if it uploads a file that survives a restart.
     *
     * The missing random IDs of the call are chosen and added to the arguments.
     *
     * If the same call is already running (an interrupted call resumed after a restart, that an IPC client
     * sent again after reconnecting), its result is awaited, instead of making the call twice.
     *
     * Internal arguments:
     * - resumableCallId: identifies an API method call across restarts, chosen by IPC clients (sendMedia() calls are identified by their random ID).
     * - resumable: false for the calls made by a call that is already resumable.
     * - resumableCall: ID of the resumable call sending the method, see resumableCallSent().
     *
     * @template T
     *
     * @param 'method'|'sendMedia'                    $kind Either an API method, or a call to sendMedia().
     * @param array<array-key, mixed>                 $args
     * @param Closure(array<array-key, mixed>): T     $call Makes the call with the specified arguments.
     *
     * @return T
     */
    private function makeResumableCall(string $kind, string $method, array $args, Closure $call): mixed
    {
        $id = $this->registerResumableCall($kind, $method, $args);
        if ($id === null) {
            return $call($args);
        }
        $args['resumableCall'] = $id;
        if (isset($this->runningResumableCalls[$id])) {
            $this->logger->logger("Waiting for the same $method call, which is already running", Logger::NOTICE);
            /** @var T */
            return $this->runningResumableCalls[$id]->await($args['cancellation'] ?? null);
        }
        $future = async($call, $args);
        $this->runningResumableCalls[$id] = $future;
        // Calls are only resumed if they were running recently.
        $refresh = EventLoop::unreference(EventLoop::repeat(60, function () use ($id): void {
            if (isset($this->resumableCalls[$id])) {
                $this->resumableCalls[$id]['time'] = time();
            }
        }));
        try {
            return $future->await();
        } finally {
            EventLoop::cancel($refresh);
            unset($this->runningResumableCalls[$id], $this->resumableCalls[$id]);
        }
    }

    /**
     * Save a call made with the specified arguments, if it uploads a file that survives a restart, and it can be made again safely.
     *
     * @param 'method'|'sendMedia' $kind
     * @param array<array-key, mixed> $args
     *
     * @return ?string ID of the call
     */
    private function registerResumableCall(string $kind, string $method, array &$args): ?string
    {
        $callId = $args['resumableCallId'] ?? null;
        $resumable = $args['resumable'] ?? true;
        unset($args['resumableCallId'], $args['resumable']);
        if (!$resumable || !$this->settings->getFiles()->getResumeInterruptedCalls() || !self::hasResumableSource($args)) {
            return null;
        }
        if ($kind === 'method') {
            // A call made again must not be executed twice by Telegram.
            // Secret chat messages can't be sent again with the same random ID, as it's encrypted with a new sequence number.
            if (str_starts_with($method, 'messages.sendEncrypted') || !$this->fillRandomIds($method, $args)) {
                return null;
            }
            $callId ??= bin2hex(random_bytes(16));
            $id = 'method:'.$callId;
        } else {
            \assert(isset($args['randomId']));
            $id = 'sendMedia:'.$args['randomId'];
        }
        if (!isset($this->resumableCalls[$id])) {
            $saved = $args;
            unset($saved['cancellation'], $saved['callback']);
            if ($kind === 'method') {
                $saved['resumableCallId'] = $callId;
            }
            try {
                // Now, before its streams are read.
                $serialized = serialize($saved);
            } catch (Throwable) {
                return null;
            }
            $this->resumableCalls[$id] = ['kind' => $kind, 'method' => $method, 'args' => $serialized, 'time' => time()];
        }
        return $id;
    }

    /**
     * Called when the method of a resumable call is sent, after its files were uploaded: from now on, it isn't resumed after a restart.
     *
     * @internal
     */
    public function resumableCallSent(string $id): void
    {
        unset($this->resumableCalls[$id]);
    }

    /**
     * Resume the calls interrupted by a restart.
     */
    private function resumeCalls(): void
    {
        if (!$this->settings->getFiles()->getResumeInterruptedCalls()) {
            foreach ($this->resumableCalls as $call) {
                $this->logger->logger("Not resuming interrupted {$call['method']} call, resuming interrupted calls is disabled", Logger::WARNING);
            }
            $this->resumableCalls = [];
            return;
        }
        $expired = time() - self::UPLOAD_RESUME_TTL;
        foreach ($this->resumableCalls as $id => $call) {
            if (isset($this->runningResumableCalls[$id])) {
                // Already made again by an IPC client.
                continue;
            }
            if ($call['time'] < $expired) {
                $this->logger->logger("Not resuming interrupted {$call['method']} call, it is too old", Logger::WARNING);
                unset($this->resumableCalls[$id]);
                continue;
            }
            $this->logger->logger("Resuming interrupted {$call['method']} call", Logger::NOTICE);
            EventLoop::queue(function () use ($id, $call): void {
                try {
                    $args = unserialize($call['args']);
                    \assert(\is_array($args));
                    if ($call['kind'] === 'sendMedia') {
                        /** @psalm-suppress MixedArgument */
                        $result = $this->sendMedia(...$args + ['callback' => null, 'cancellation' => null]);
                    } else {
                        $result = $this->methodCallAsyncRead($call['method'], $args);
                    }
                    $message = '';
                    if ($result instanceof Message) {
                        $message = " (message {$result->id} in chat {$result->chatId})";
                    } elseif (\is_array($result) && \in_array($result['_'] ?? null, ['updates', 'updateShortSentMessage', 'updatesCombined'], true)) {
                        try {
                            $sent = $this->extractMessage($result);
                            $message = " (message {$sent['id']} in chat {$this->getId($sent['peer_id'])})";
                        } catch (Throwable) {
                            // Not a call sending a message.
                        }
                    }
                    $this->logger->logger("Resumed interrupted {$call['method']} call$message", Logger::NOTICE);
                } catch (RPCErrorException $e) {
                    if ($e->rpc === 'RANDOM_ID_DUPLICATE') {
                        $this->logger->logger("Interrupted {$call['method']} call had already completed", Logger::NOTICE);
                    } else {
                        $this->logger->logger("Could not resume interrupted {$call['method']} call: $e", Logger::ERROR);
                    }
                } catch (UploadResumeException $e) {
                    // Nobody is waiting for the result of a resumed call.
                    $this->report("Could not resume interrupted {$call['method']} call, the file was not sent: $e");
                } catch (Throwable $e) {
                    $this->logger->logger("Could not resume interrupted {$call['method']} call: $e", Logger::ERROR);
                } finally {
                    unset($this->resumableCalls[$id]);
                }
            });
        }
    }

    /**
     * Whether the arguments contain a file to upload that survives a restart, and nothing that doesn't.
     *
     * @param array<array-key, mixed> $args
     */
    private static function hasResumableSource(array $args): bool
    {
        $found = false;
        $walk = static function (mixed $value, int $depth) use (&$walk, &$found): bool {
            if (\is_resource($value) || ($value instanceof ReadableStream && !$value instanceof ResumableStream)) {
                // Can't be read again after a restart.
                return false;
            }
            if ($value instanceof LocalFile
                || $value instanceof RemoteUrl
                || $value instanceof ResumableStream
                || $value instanceof BotApiFileId
                || $value instanceof Media
                || $value instanceof Message
            ) {
                $found = true;
            } elseif (\is_array($value) && $depth < 5) {
                foreach ($value as $key => $v) {
                    if ($key !== 'cancellation' && $key !== 'callback' && !$walk($v, $depth + 1)) {
                        return false;
                    }
                }
            }
            return true;
        };
        return $walk($args, 0) && $found;
    }

    /**
     * Choose the random IDs of a method call beforehand, so that they're saved with it.
     *
     * @param array<array-key, mixed> $args
     *
     * @return bool Whether the call has random IDs.
     */
    private function fillRandomIds(string $method, array &$args): bool
    {
        $found = false;
        $info = $this->getTL()->getMethods()->findByMethod($method);
        if ($info !== false) {
            $found = self::fillRandomId($info['params'], $args);
        }
        foreach ($args as &$arg) {
            if (\is_array($arg)) {
                $found = $this->fillNestedRandomIds($arg, 0) || $found;
            }
        }
        return $found;
    }

    /**
     * @param array<array-key, mixed> $args
     */
    private function fillNestedRandomIds(array &$args, int $depth): bool
    {
        if ($depth >= 5) {
            return false;
        }
        $found = false;
        if (isset($args['_']) && \is_string($args['_'])) {
            $info = $this->getTL()->getConstructors()->findByPredicate($args['_']);
            if ($info !== false) {
                $found = self::fillRandomId($info['params'], $args);
            }
        }
        foreach ($args as &$arg) {
            if (\is_array($arg)) {
                $found = $this->fillNestedRandomIds($arg, $depth + 1) || $found;
            }
        }
        return $found;
    }

    /**
     * @param list<array{name: string, type: string, ...}> $params
     * @param array<array-key, mixed> $args
     */
    private static function fillRandomId(array $params, array &$args): bool
    {
        foreach ($params as $param) {
            if ($param['name'] === 'random_id' && $param['type'] === 'long') {
                $args['random_id'] ??= Tools::randomInt();
                return true;
            }
        }
        return false;
    }
}
<?php

declare(strict_types=1);

/**
 * File reference database.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\MTProtoTools;

use Amp\Sync\LocalKeyedMutex;
use Closure;
use danog\AsyncOrm\Annotations\OrmMappedArray;
use danog\AsyncOrm\DbArray;
use danog\AsyncOrm\KeyType;
use danog\AsyncOrm\ValueType;
use danog\MadelineProto\LegacyMigrator;
use danog\MadelineProto\Logger;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\MTProto\SpecialMethodType;
use danog\MadelineProto\MTProtoTools\FileRef\FileRefEnvironment;
use danog\MadelineProto\MTProtoTools\FileRef\FileRefMap;
use danog\MadelineProto\MTProtoTools\FileRef\FileRefTraversal;
use danog\MadelineProto\RestoresDeclaredProperties;
use danog\MadelineProto\TL\TLCallback;
use Fiber;
use Revolt\EventLoop;
use stdClass;
use Throwable;
use WeakMap;

/**
 * File reference database, driven by the file reference map (https://core.telegram.org/api/file-references).
 *
 * - The file reference table maps a FileId to the last file reference seen for it, the file source table maps it
 *   to the file sources it was seen in (one per source type, the most recently seen one).
 * - Both are populated by traversing the results of method calls (see ResponseHandler) and the updates (see
 *   UpdateHandler::saveUpdate()) with the incoming traversers of the map ({@see FileRefTraversal}).
 * - Outgoing media locations get the last file reference seen for them when they're serialized; when a call fails
 *   with FILE_REFERENCE_EXPIRED and similar errors, the outgoing traversers find the locations in its arguments, the
 *   refresh actions of their sources fetch them again, and the call is repeated with the new file references
 *   ({@see self::refreshMethodCall()}).
 *
 * @internal
 */
final class ReferenceDatabase implements TLCallback, FileRefEnvironment
{
    use LegacyMigrator;
    use RestoresDeclaredProperties;

    /** FileId of a photo. */
    public const PHOTO = 'fileIdPhoto';
    /** FileId of a document. */
    public const DOCUMENT = 'fileIdDocument';

    /** Version 1 was the location database the map replaced, whose data is dropped. */
    private const V = 2;

    /**
     * File reference table: FileId key => file reference.
     *
     * @var DbArray<string, string>
     */
    #[OrmMappedArray(KeyType::STRING, ValueType::SCALAR)]
    private $db;
    /**
     * File source table: FileId key => file sources, at most one per type, from the least to the most recently seen.
     *
     * @var DbArray<string, list<array>>
     */
    #[OrmMappedArray(KeyType::STRING, ValueType::SCALAR)]
    private $sources;
    /**
     * File references not yet written to the database.
     *
     * @var array<string, string>
     */
    private array $pendingReferences = [];
    /**
     * File sources not yet written to the database.
     *
     * @var array<string, list<array>>
     */
    private array $pendingSources = [];
    private int $v = 0;

    private LocalKeyedMutex $flushMutex;
    private LocalKeyedMutex $refreshMutex;
    /**
     * The file references written into the calls being serialized, by FileId key, per fiber: serializing a call can
     * suspend (database reads, uploads), and other calls are serialized meanwhile.
     *
     * @var WeakMap<object, array<string, string>>
     */
    private WeakMap $serializing;
    /** Stands for the main context, which isn't a fiber. */
    private stdClass $mainContext;

    public function __construct(private MTProto $API)
    {
        $this->v = self::V;
        $this->initRuntimeState();
    }
    private function initRuntimeState(): void
    {
        $this->flushMutex = new LocalKeyedMutex;
        $this->refreshMutex = new LocalKeyedMutex;
        $this->serializing = new WeakMap;
        $this->mainContext = new stdClass;
    }
    /**
     * @psalm-pure
     */
    public function __sleep()
    {
        return ['db', 'sources', 'pendingReferences', 'pendingSources', 'API', 'v'];
    }
    public function __unserialize(array $data): void
    {
        // Drops the state of version 1 (pendingDb).
        $this->restoreDeclaredProperties($data);
        $this->initRuntimeState();
    }
    public function init(): void
    {
        $this->initDbProperties($this->API->getDbSettings(), $this->API->getDbPrefix().'_ReferenceDatabase_');
        if ($this->v !== self::V) {
            // The file references of version 1 are refetched when needed.
            $this->db->clear();
            $this->sources->clear();
            $this->pendingReferences = [];
            $this->pendingSources = [];
            $this->v = self::V;
        }
        foreach ($this->pendingReferences as $key => $_) {
            EventLoop::queue($this->flush(...), (string) $key);
        }
        foreach ($this->pendingSources as $key => $_) {
            EventLoop::queue($this->flush(...), (string) $key);
        }
    }
    public function clear(): void
    {
        $this->pendingReferences = [];
        $this->pendingSources = [];
        $this->db->clear();
        $this->sources->clear();
    }

    private static function getMap(): FileRefMap
    {
        return FileRefMap::get();
    }

    /**
     * Store the file references and file sources found in the result of a method call.
     *
     * @param array $args The arguments of the call, as passed by the user
     */
    public function onMethodResult(string $method, array $args, mixed $result): void
    {
        $map = self::getMap();
        if (!isset($map->methodResults[$method])) {
            return;
        }
        try {
            $this->store(FileRefTraversal::traverseMethodResult($map, $this, $method, $args, $result));
        } catch (Throwable $e) {
            $this->API->logger("Could not store the file references of the result of $method: $e", Logger::ERROR);
        }
    }
    /**
     * Store the file references and file sources found in an update.
     */
    public function onUpdate(array $update): void
    {
        $map = self::getMap();
        if (!isset($map->incoming[$update['_']])) {
            return;
        }
        try {
            $this->store(FileRefTraversal::traverseUpdate($map, $this, $update));
        } catch (Throwable $e) {
            $this->API->logger("Could not store the file references of {$update['_']}: $e", Logger::ERROR);
        }
    }

    /**
     * Store the commits of a traversal: visible at once, written to the database in the background.
     *
     * @param list<array{fileId: array{_: string, id: int}, reference: string, sources: list<array>}> $commits
     */
    private function store(array $commits): void
    {
        foreach ($commits as ['fileId' => $fileId, 'reference' => $reference, 'sources' => $sources]) {
            $key = FileRefMap::getFileIdKey($fileId);
            // An empty file reference can't be used: keep the last one seen.
            if ($reference !== '') {
                $this->pendingReferences[$key] = $reference;
            }
            if ($sources) {
                $this->pendingSources[$key] = self::mergeSources($this->pendingSources[$key] ?? [], $sources);
            }
            EventLoop::queue($this->flush(...), $key);
        }
    }

    /**
     * Merge file sources, keeping only the most recent one of each type, the most recent ones last.
     *
     * @param list<array> $old
     * @param list<array> $new
     * @return list<array>
     */
    private static function mergeSources(array $old, array $new): array
    {
        $res = [];
        foreach ([...$old, ...$new] as $source) {
            unset($res[$source['_']]);
            $res[$source['_']] = $source;
        }
        return array_values($res);
    }

    private function flush(string $key): void
    {
        if (!isset($this->pendingReferences[$key]) && !isset($this->pendingSources[$key])) {
            return;
        }
        $lock = $this->flushMutex->acquire($key);
        $reference = $sources = null;
        try {
            // Removed from the pending ones only once written, if not replaced meanwhile:
            // until then, they're what the database is about to contain.
            $reference = $this->pendingReferences[$key] ?? null;
            $sources = $this->pendingSources[$key] ?? null;
            if ($reference !== null) {
                $this->db[$key] = $reference;
            }
            if ($sources !== null) {
                $existing = $this->sources[$key] ?? [];
                $merged = self::mergeSources($existing, $sources);
                if ($merged !== $existing) {
                    $this->sources[$key] = $merged;
                }
            }
        } finally {
            if ($reference !== null && ($this->pendingReferences[$key] ?? null) === $reference) {
                unset($this->pendingReferences[$key]);
            }
            if ($sources !== null && ($this->pendingSources[$key] ?? null) === $sources) {
                unset($this->pendingSources[$key]);
            }
            $lock->release();
        }
    }

    /**
     * The last file reference seen for a FileId, null if unknown.
     */
    private function getStoredReference(string $key): ?string
    {
        if (isset($this->pendingReferences[$key])) {
            return $this->pendingReferences[$key];
        }
        $reference = $this->db[$key];
        return \is_string($reference) ? $reference : null;
    }

    /**
     * The file sources of a FileId, the most recently seen ones last.
     *
     * @return list<array>
     */
    private function getSources(string $key): array
    {
        $sources = $this->sources[$key] ?? [];
        if (isset($this->pendingSources[$key])) {
            $sources = self::mergeSources($sources, $this->pendingSources[$key]);
        }
        return $sources;
    }

    /**
     * The file reference to use for a photo or document: the last one seen, or the one of the object.
     *
     * @param self::PHOTO|self::DOCUMENT $type
     * @param array                      $object A photo or document constructor
     */
    public function getReference(string $type, array $object): string
    {
        $reference = isset($object['id'])
            ? $this->getStoredReference(FileRefMap::getFileIdKey(['_' => $type, 'id' => (int) $object['id']]))
            : null;
        return $reference ?? FileRefMap::getBytes($object['file_reference'] ?? null) ?? '';
    }

    /**
     * Use the last file reference seen in an outgoing media location.
     */
    private function populateReference(array $location): array
    {
        if (!isset($location['id'])) {
            return $location;
        }
        $type = self::getMap()->swapLocations[$location['_']];
        $reference = $this->getStoredReference(FileRefMap::getFileIdKey(['_' => $type, 'id' => (int) $location['id']]));
        if ($reference !== null) {
            $location['file_reference'] = $reference;
        } else {
            $location['file_reference'] ??= '';
        }
        $context = Fiber::getCurrent() ?? $this->mainContext;
        if (isset($this->serializing[$context])) {
            $sent = $this->serializing[$context];
            $sent[FileRefMap::getFileIdKey(['_' => $type, 'id' => (int) $location['id']])] = FileRefMap::getBytes($location['file_reference']) ?? '';
            $this->serializing[$context] = $sent;
        }
        return $location;
    }

    /**
     * Serialize a call, returning the file references written into it as well.
     *
     * @template T
     * @param Closure(): T $serialize
     * @return array{T, array<string, string>} The result and the file references sent, by FileId key
     */
    public function serializeCall(Closure $serialize): array
    {
        $context = Fiber::getCurrent() ?? $this->mainContext;
        // Calls serialized while serializing this one (in the same fiber) have their own.
        $outer = $this->serializing[$context] ?? null;
        $this->serializing[$context] = [];
        try {
            $result = $serialize();
            return [$result, $this->serializing[$context]];
        } finally {
            if ($outer === null) {
                unset($this->serializing[$context]);
            } else {
                $this->serializing[$context] = $outer;
            }
        }
    }

    /**
     * Refresh the file references of a method call that failed with a FILE_REFERENCE_* error.
     *
     * @param string $error The error: FILE_REFERENCE_%d_EXPIRED and FILE_REFERENCE_%d_INVALID point to the media at
     *                      index %d of the first vector of media in the arguments (multi_media of
     *                      messages.sendMultiMedia, extended_media of inputMediaPaidMedia)
     *
     * @param array<string, string> $sent The file references the failed call sent, by FileId key: not necessarily
     *                                     the ones of its arguments, see {@see self::populateReference()}
     *
     * @return ?array The arguments with the new file references, null if none could be refreshed.
     */
    public function refreshMethodCall(string $method, array $args, string $error, array $sent = []): ?array
    {
        $map = self::getMap();
        $TL = $this->API->getTL();
        [$args, $locations] = $map->findLocations($method, $args, function (string $type, mixed $value) use ($TL, $method): mixed {
            try {
                return $TL->convertTypeMismatch($type, $value);
            } catch (Throwable $e) {
                $this->API->logger("Could not convert an argument of $method to $type: $e", Logger::ERROR);
                return $value;
            }
        });
        $candidates = $locations;
        if (preg_match('/^FILE_REFERENCE_(\d+)_/', $error, $matches)) {
            $index = (int) $matches[1];
            $candidates = array_values(array_filter($locations, static fn (array $location): bool => $location['group'] === $index));
            if (!$candidates) {
                $this->API->logger("No media at index $index of the arguments of $method, refreshing all of them", Logger::WARNING);
                $candidates = $locations;
            }
        }
        $references = [];
        foreach ($candidates as $location) {
            $key = FileRefMap::getFileIdKey($location['fileId']);
            if (\array_key_exists($key, $references)) {
                continue;
            }
            $references[$key] = $this->refreshFileId($location['fileId'], $sent[$key] ?? $location['reference']);
        }
        $references = array_filter($references, static fn (?string $reference): bool => $reference !== null);
        if (!$references) {
            return null;
        }
        return FileRefMap::swapReferences($args, $locations, $references);
    }

    /**
     * Refresh the file reference of a FileId using its file sources.
     *
     * Tries the sources from the most recently seen one, stopping as soon as the file reference differs from the one
     * the failed call used. If the stored one already differs (seen or refreshed after the call was sent, e.g. by a
     * concurrent refresh), it's used without refreshing.
     *
     * @param array{_: string, id: int} $fileId
     * @param ?string                   $used   The file reference the failed call used, if known
     *
     * @return ?string The new file reference, null if it couldn't be refreshed.
     */
    public function refreshFileId(array $fileId, ?string $used): ?string
    {
        $key = FileRefMap::getFileIdKey($fileId);
        $before = $used ?? $this->getStoredReference($key);
        $lock = $this->refreshMutex->acquire($key);
        try {
            $current = $this->getStoredReference($key);
            if ($current !== null && $current !== $before) {
                // Newer than the one of the failed call.
                return $current;
            }
            $sources = array_reverse($this->getSources($key));
            if (!$sources) {
                $this->API->logger("No file sources to refresh the file reference of $key", Logger::WARNING);
            }
            foreach ($sources as $source) {
                try {
                    $call = self::getMap()->buildRefreshCall($source, $this);
                    if ($call === null) {
                        continue;
                    }
                    [$method, $args] = $call;
                    $this->API->logger("Refreshing the file reference of $key from {$source['_']} with $method", Logger::VERBOSE);
                    $args['specialMethodType'] = SpecialMethodType::FILEREF_RELATED;
                    // Its result is traversed by the response handler, which updates the file reference table.
                    $this->API->methodCallAsyncRead($method, $args);
                } catch (Throwable $e) {
                    $this->API->logger("Could not refresh the file reference of $key from {$source['_']}: $e", Logger::ERROR);
                    continue;
                }
                $current = $this->getStoredReference($key);
                if ($current !== null && $current !== $before) {
                    return $current;
                }
            }
            return null;
        } finally {
            $lock->release();
        }
    }

    #[\Override]
    public function getSelfId(): ?int
    {
        return $this->API->authorization['user']['id'] ?? null;
    }
    #[\Override]
    public function getPeerId(mixed $peer): ?int
    {
        try {
            return $this->API->getIdInternal($peer);
        } catch (Throwable) {
            return null;
        }
    }
    #[\Override]
    public function getInputPeerById(int $id): array
    {
        if ($id === $this->getSelfId()) {
            return ['_' => 'inputPeerSelf'];
        }
        return $this->API->getInputPeer($id);
    }
    #[\Override]
    public function getInputUserById(int $id): array
    {
        if ($id === $this->getSelfId()) {
            return ['_' => 'inputUserSelf'];
        }
        return $this->API->getInputConstructor($id);
    }
    #[\Override]
    public function getInputChannelById(int $id): array
    {
        return $this->API->getInputConstructor($id);
    }
    #[\Override]
    public function getThemeFormat(): string
    {
        // No theming engine is supported.
        return '';
    }

    /**
     * @psalm-pure
     */
    #[\Override]
    public function getMethodAfterResponseDeserializationCallbacks(): array
    {
        return [];
    }
    /**
     * @psalm-pure
     */
    #[\Override]
    public function getMethodBeforeResponseDeserializationCallbacks(): array
    {
        return [];
    }
    /**
     * @psalm-pure
     */
    #[\Override]
    public function getConstructorAfterDeserializationCallbacks(): array
    {
        return [];
    }
    /**
     * @psalm-pure
     */
    #[\Override]
    public function getConstructorBeforeDeserializationCallbacks(): array
    {
        return [];
    }
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function getConstructorBeforeSerializationCallbacks(): array
    {
        return array_fill_keys(array_keys(self::getMap()->swapLocations), $this->populateReference(...));
    }
    /**
     * @psalm-pure
     */
    #[\Override]
    public function getTypeMismatchCallbacks(): array
    {
        return [];
    }
    /**
     * @psalm-mutation-free
     */
    public function __debugInfo()
    {
        return ['ReferenceDatabase instance '.spl_object_hash($this)];
    }
}
<?php

declare(strict_types=1);

/**
 * Files module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\MTProtoTools;

use Amp\Http\HttpStatus;
use Amp\Sync\LocalKeyedMutex;
use Amp\Sync\LocalMutex;
use AssertionError;
use danog\MadelineProto\API;
use danog\MadelineProto\EventHandler\Media;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\Ipc\Runner\WebRunner;
use danog\MadelineProto\Lang;
use danog\MadelineProto\Logger;
use danog\MadelineProto\Magic;
use danog\MadelineProto\Settings;
use danog\MadelineProto\Settings\AppInfo;
use danog\MadelineProto\Tools;
use Exception;
use Revolt\EventLoop;
use Throwable;

use function Amp\File\exists;
use function Amp\File\read;
use function Amp\File\write;

/**
 * @internal
 *
 * @method Settings getSettings()
 */
trait FileServer
{
    /**
     * Downloads a file to the browser using the specified session file.
     */
    public static function downloadServer(string $session): void
    {
        if (isset($_GET['login'])) {
            // The session ID check must reach this script: a cached answer (a CDN caching everything) would make
            // getDownloadLink fail, or succeed for a script that no longer works, until the cache expires.
            header('Cache-Control: no-store');
            $API = new API($session);
            $API->start();
            $id = (string) $API->getSelf()['id'];
            header("Content-length: ".\strlen($id));
            echo $id;
            die;
        }
        if (!isset($_GET['f'])) {
            header('Cache-Control: no-store');
            new AppInfo;
            die(Lang::$current_lang["dl.php_powered_by_madelineproto"]);
        }

        try {
            $API = new API($session);
        } catch (Throwable $e) {
            Logger::log("An error occurred while instantiating the session: $e");
            $result = ResponseInfo::error(HttpStatus::BAD_GATEWAY);
            $result->writeHeaders();
            if (!\in_array($result->getCode(), [HttpStatus::OK, HttpStatus::PARTIAL_CONTENT], true)) {
                Tools::echo($result->getCodeExplanation());
            }
            die;
        }

        $API->downloadToBrowser(
            messageMedia: $_GET['f'],
            size: (int) $_GET['s'],
            name: $_GET['n'],
            mime: $_GET['m']
        );
    }

    /**
     * Internal endpoint used by the download server.
     *
     * @psalm-external-mutation-free
     */
    public static function processDownloadServerPing(string $path, string $payload): void
    {
        self::$checkedScripts[$path] = $payload;
    }

    /**
     * Get download link of media file.
     */
    public function getDownloadLink(array|string|Message|Media $media, ?string $scriptUrl = null, ?int $size = null, ?string $name = null, ?string $mime = null): string
    {
        $scriptUrl ??= $this->getSettings()->getFiles()->getDownloadLink();
        if ($scriptUrl === null) {
            try {
                $scriptUrl = $this->getDefaultDownloadScript();
            } catch (Throwable $e) {
                $sessionPath = var_export($this->getSessionName(), true);
                throw new Exception(\sprintf(
                    Lang::$current_lang['need_dl.php'],
                    $e->getMessage(),
                    "<?php require 'vendor/autoload.php'; \\danog\\MadelineProto\\API::downloadServer($sessionPath); ?>",
                ));
            }
        } else {
            $this->checkDownloadScript($scriptUrl);
        }
        if ($media instanceof Message) {
            $media = $media->media;
        }
        if ($media instanceof Media) {
            $f = $media->botApiFileId;
            $name = $media->fileName;
            $mime = $media->mimeType;
            $size = $media->size;
        } else {
            if (\is_string($media) && ($size === null || $mime === null || $name === null)) {
                throw new Exception('downloadToBrowser only supports bot file IDs if the file size, file name and MIME type are also specified in the third, fourth and fifth parameters of the method.');
            }

            $messageMedia = $this->getDownloadInfo($media);
            $size ??= $messageMedia['size'];
            $mime ??= $messageMedia['mime'];
            if (\is_string($media)) {
                $f = $media;
            } else {
                $f = $this->extractBotAPIFile($this->MTProtoToBotAPI($media))['file_id'];
                $name ??= $messageMedia['name'].$messageMedia['ext'];
            }
        }

        return $scriptUrl."?".http_build_query([
            'f' => $f,
            's' => $size,
            'n' => $name,
            'm' => $mime,
        ]);
    }

    private ?LocalMutex $scriptMutex = null;
    private static array $checkedAutoload = [];
    private function getDefaultDownloadScript(): string
    {
        $isCli = (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg');
        if ($isCli && (!Magic::$isIpcWorker || !($_ENV['absoluteRootDir'] ?? null))) {
            throw new AssertionError(Lang::$current_lang["cli_need_dl.php_link"]);
        }
        $s = $this->getSessionName();
        if (\defined('MADELINE_PHP')) {
            $autoloadPath = \MADELINE_PHP;
        } elseif (\defined('MADELINE_PHAR')) {
            $autoloadPath = \MADELINE_PHAR;
        } else {
            $paths = [
                \dirname(__DIR__, 4).'/autoload.php',
                \dirname(__DIR__, 2).'/vendor/autoload.php',
                \dirname(__DIR__, 6).'/autoload.php',
                \dirname(__DIR__, 4).'/vendor/autoload.php',
            ];

            foreach ($paths as $path) {
                if (exists($path)) {
                    $autoloadPath = $path;
                    break;
                }
            }

            if (!isset($autoloadPath)) {
                throw new AssertionError('Could not locate autoload.php in any of the following files: '.implode(', ', $paths));
            }
        }

        if (isset(self::$checkedAutoload[$autoloadPath])) {
            return self::$checkedAutoload[$autoloadPath];
        }
        $this->scriptMutex ??= new LocalMutex;
        $lock = $this->scriptMutex->acquire();
        try {
            $downloadScript = \sprintf('<?php require %s; \danog\MadelineProto\API::downloadServer(__DIR__);', var_export($autoloadPath, true));
            $f = $s.DIRECTORY_SEPARATOR.'dl.php';
            $recreate = true;
            if (exists($f)) {
                $recreate = read($f) !== $downloadScript;
            }
            if ($recreate) {
                $temp = "$f.temp.php";
                write($temp, $downloadScript);
                rename($temp, $f);
            }

            $f = realpath($f);
            $absoluteRootDir = $isCli
                ? $_ENV['absoluteRootDir']
                : WebRunner::getAbsoluteRootDir();

            if (substr($f, 0, \strlen($absoluteRootDir)) !== $absoluteRootDir) {
                throw new AssertionError("Process runner $f is not within readable document root $absoluteRootDir!");
            }
            $f = substr($f, \strlen($absoluteRootDir)-1);
            $f = str_replace(DIRECTORY_SEPARATOR, '/', $f);
            $f = str_replace('//', '/', $f);
            $f = 'https://'.($isCli ? $_ENV['serverName'] : $_SERVER['SERVER_NAME']).$f;
            $this->checkDownloadScript($f);
            return self::$checkedAutoload[$autoloadPath] = $f;
        } finally {
            EventLoop::queue($lock->release(...));
        }
    }

    private static ?LocalKeyedMutex $checkMutex = null;
    private static array $checkedScripts = [];
    private function checkDownloadScript(string $scriptUrl): void
    {
        if (!str_starts_with($scriptUrl, 'http://') && !str_starts_with($scriptUrl, 'https://')) {
            throw new AssertionError("The download script must be an HTTP (preferrably HTTPS) URL!");
        }
        if (isset(self::$checkedScripts[$scriptUrl])) {
            return;
        }
        self::$checkMutex ??= new LocalKeyedMutex;
        $lock = self::$checkMutex->acquire($scriptUrl);
        try {
            /** @psalm-suppress ParadoxicalCondition */
            if (isset(self::$checkedScripts[$scriptUrl])) {
                return;
            }
            // A new URL every time, for caches that ignore the Cache-Control of the answer
            $scriptUrlNew = $scriptUrl.'?'.http_build_query(['login' => 1, 'nonce' => bin2hex(random_bytes(8))]);
            $this->logger->logger("Checking $scriptUrlNew...");
            $result = $this->fileGetContents($scriptUrlNew);
            $expected = (string) $this->getSelf()['id'];
            if ($result !== $expected) {
                throw new AssertionError(\sprintf(
                    Lang::$current_lang['invalid_dl.php_session'],
                    $scriptUrl,
                    $expected,
                    $result,
                ));
            }

            self::$checkedScripts[$scriptUrl] = true;
        } finally {
            EventLoop::queue($lock->release(...));
        }
    }
}
<?php

declare(strict_types=1);

/**
 * Crypt module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\MTProtoTools\Crypt;

use danog\MadelineProto\Magic;

/**
 * Continuous mode IGE implementation.
 *
 * @internal
 */
abstract class IGE
{
    /**
     * IV part 1.
     *
     */
    protected string $iv_part_1;
    /**
     * IV part 2.
     *
     */
    protected string $iv_part_2;
    /**
     * Instantiate appropriate handler.
     */
    public static function getInstance(string $key, string $iv): IGE
    {
        if (Magic::$hasOpenssl) {
            return new IGEOpenssl($key, $iv);
        }
        return new IGEPhpseclib($key, $iv);
    }

    /**
     * Get the current chaining state.
     *
     * Passing it as the IV of a new instance with the same key continues the chain from this point.
     *
     * @psalm-mutation-free
     */
    public function getState(): string
    {
        return $this->iv_part_1.$this->iv_part_2;
    }

    /** @psalm-impure */
    abstract protected function __construct(string $key, string $iv);
    /** @psalm-impure */
    abstract public function encrypt(string $plaintext): string;
    /** @psalm-impure */
    abstract public function decrypt(string $ciphertext): string;
}
<?php

declare(strict_types=1);

/**
 * Crypt module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\MTProtoTools\Crypt;

use const OPENSSL_RAW_DATA;
use const OPENSSL_ZERO_PADDING;

/**
 * Openssl IGE implementation.
 *
 * @internal
 */
final class IGEOpenssl extends IGE
{
    /**
     * @psalm-mutation-free
     */
    protected function __construct(private string $key, string $iv)
    {
        $this->iv_part_1 = substr($iv, 0, 16);
        $this->iv_part_2 = substr($iv, 16);
    }
    /**
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function encrypt(string $plaintext): string
    {
        $ciphertext = '';
        for ($i = 0, $length = \strlen($plaintext); $i < $length; $i += 16) {
            $plain = substr($plaintext, $i, 16);

            $cipher = openssl_encrypt($plain ^ $this->iv_part_1, 'aes-256-ecb', $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING) ^ $this->iv_part_2;

            $ciphertext .= $cipher;

            $this->iv_part_1 = $cipher;
            $this->iv_part_2 = $plain;
        }

        return $ciphertext;
    }
    /**
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function decrypt(string $ciphertext): string
    {
        $plaintext = '';
        for ($i = 0, $length = \strlen($ciphertext); $i < $length; $i += 16) {
            $cipher = substr($ciphertext, $i, 16);

            $plain = openssl_decrypt($cipher ^ $this->iv_part_2, 'aes-256-ecb', $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING) ^ $this->iv_part_1;

            $plaintext .= $plain;

            $this->iv_part_1 = $cipher;
            $this->iv_part_2 = $plain;
        }

        return $plaintext;
    }
}
<?php

declare(strict_types=1);

/**
 * Crypt module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\MTProtoTools\Crypt;

use phpseclib4\Crypt\AES;

/**
 * Phpseclib IGE implementation.
 *
 * @internal
 */
final class IGEPhpseclib extends IGE
{
    private AES $instance;
    protected function __construct(string $key, string $iv)
    {
        $this->instance = new AES('ecb');
        $this->instance->setKey($key);
        $this->instance->disablePadding();
        $this->iv_part_1 = substr($iv, 0, 16);
        $this->iv_part_2 = substr($iv, 16);
    }
    #[\Override]
    public function encrypt(string $plaintext): string
    {
        $ciphertext = '';
        for ($i = 0, $length = \strlen($plaintext); $i < $length; $i += 16) {
            $plain = substr($plaintext, $i, 16);

            $cipher = $this->instance->encrypt($plain ^ $this->iv_part_1) ^ $this->iv_part_2;

            $ciphertext .= $cipher;

            $this->iv_part_1 = $cipher;
            $this->iv_part_2 = $plain;
        }

        return $ciphertext;
    }
    #[\Override]
    public function decrypt(string $ciphertext): string
    {
        $plaintext = '';
        for ($i = 0, $length = \strlen($ciphertext); $i < $length; $i += 16) {
            $cipher = substr($ciphertext, $i, 16);

            $plain = $this->instance->decrypt($cipher ^ $this->iv_part_2) ^ $this->iv_part_1;

            $plaintext .= $plain;

            $this->iv_part_1 = $cipher;
            $this->iv_part_2 = $plain;
        }

        return $plaintext;
    }
}
<?php

declare(strict_types=1);

/**
 * Crypt module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\MTProtoTools\Crypt;

use danog\MadelineProto\Magic;
use phpseclib4\Crypt\AES;

use const OPENSSL_RAW_DATA;

/**
 * Continuous AES-CTR: each call goes on from where the previous one stopped.
 *
 * With OpenSSL, or phpseclib without it.
 *
 * @internal
 */
final class AesCtr
{
    private ?AES $phpseclib = null;
    private string $cipher;
    /** The counter of the current block. */
    private string $counter;
    /** How many bytes of the keystream of the current block were already used. */
    private int $offset = 0;

    public function __construct(private readonly string $key, string $iv)
    {
        $this->cipher = 'aes-'.(\strlen($key) * 8).'-ctr';
        $this->counter = $iv;
        if (!Magic::$hasOpenssl) {
            $this->phpseclib = new AES('ctr');
            $this->phpseclib->enableContinuousBuffer();
            $this->phpseclib->setKey($key);
            $this->phpseclib->setIV($iv);
        }
    }

    /**
     * Encrypt (or decrypt) the next bytes.
     */
    public function encrypt(string $data): string
    {
        if ($this->phpseclib !== null) {
            return $this->phpseclib->encrypt($data);
        }
        if ($data === '') {
            return '';
        }
        // From the start of the current block, skipping the part of its keystream that was already used.
        $result = openssl_encrypt(str_repeat("\0", $this->offset).$data, $this->cipher, $this->key, OPENSSL_RAW_DATA, $this->counter);
        \assert($result !== false);
        $total = $this->offset + \strlen($data);
        $this->counter = self::increment($this->counter, intdiv($total, 16));
        $result = substr($result, $this->offset);
        $this->offset = $total % 16;
        return $result;
    }

    /**
     * Add to a 128-bit big-endian counter, as OpenSSL increments it.
     *
     * @psalm-pure
     */
    private static function increment(string $counter, int $blocks): string
    {
        for ($i = 15; $blocks !== 0 && $i >= 0; $i--) {
            $sum = \ord($counter[$i]) + ($blocks & 0xFF);
            $counter[$i] = \chr($sum & 0xFF);
            $blocks = ($blocks >> 8) + ($sum >> 8);
        }
        return $counter;
    }
}
<?php

declare(strict_types=1);

/**
 * Crypt module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\MTProtoTools;

use danog\MadelineProto\Logger;
use danog\MadelineProto\Magic;
use danog\MadelineProto\MTProtoTools\Crypt\IGE;
use danog\MadelineProto\SecurityException;
use phpseclib4\Crypt\AES;
use phpseclib4\Math\BigInteger;

use const OPENSSL_RAW_DATA;
use const OPENSSL_ZERO_PADDING;

/**
 * @internal
 */
final class Crypt
{
    /**
     * AES KDF function for MTProto v2.
     *
     * @param string  $msg_key   Message key
     * @param string  $auth_key  Auth key
     * @param boolean $to_server To server/from server direction
     *
     * @internal
     *
     * @psalm-pure
     */
    public static function kdf(string $msg_key, string $auth_key, bool $to_server = true): array
    {
        $x = $to_server ? 0 : 8;
        $sha256_a = hash('sha256', $msg_key.substr($auth_key, $x, 36), true);
        $sha256_b = hash('sha256', substr($auth_key, 40 + $x, 36).$msg_key, true);
        $aes_key = substr($sha256_a, 0, 8).substr($sha256_b, 8, 16).substr($sha256_a, 24, 8);
        $aes_iv = substr($sha256_b, 0, 8).substr($sha256_a, 8, 16).substr($sha256_b, 24, 8);
        return [$aes_key, $aes_iv];
    }
    /**
     * @psalm-pure
     */
    public static function voipX(bool $outgoing, bool $signaling): int
    {
        $x = $outgoing ? 8 : 0;
        $x += $signaling ? 128 : 0;
        return $x;
    }
    /**
     * AES KDF function for MTProto v2, VoIP.
     *
     * @internal
     *
     * @psalm-pure
     */
    public static function voipKdf(string $msg_key, string $auth_key, int $x): array
    {
        $sha256_a = hash('sha256', $msg_key.substr($auth_key, $x, 36), true);
        $sha256_b = hash('sha256', substr($auth_key, 40 + $x, 36).$msg_key, true);
        $aes_key = substr($sha256_a, 0, 8).substr($sha256_b, 8, 16).substr($sha256_a, 24, 8);
        $aes_iv = substr($sha256_b, 0, 4).substr($sha256_a, 8, 8).substr($sha256_b, 24, 4);
        return [$aes_key, $aes_iv, $x];
    }
    /**
     * AES KDF function for MTProto v1.
     *
     * @param string  $msg_key   Message key
     * @param string  $auth_key  Auth key
     * @param boolean $to_server To server/from server direction
     *
     * @internal
     *
     * @psalm-pure
     */
    public static function oldKdf(string $msg_key, string $auth_key, bool $to_server = true): array
    {
        $x = $to_server ? 0 : 8;
        $sha1_a = sha1($msg_key.substr($auth_key, $x, 32), true);
        $sha1_b = sha1(substr($auth_key, 32 + $x, 16).$msg_key.substr($auth_key, 48 + $x, 16), true);
        $sha1_c = sha1(substr($auth_key, 64 + $x, 32).$msg_key, true);
        $sha1_d = sha1($msg_key.substr($auth_key, 96 + $x, 32), true);
        $aes_key = substr($sha1_a, 0, 8).substr($sha1_b, 8, 12).substr($sha1_c, 4, 12);
        $aes_iv = substr($sha1_a, 8, 12).substr($sha1_b, 0, 8).substr($sha1_c, 16, 4).substr($sha1_d, 0, 8);
        return [$aes_key, $aes_iv];
    }
    /**
     * CTR encrypt.
     *
     * @param string $message Message to encrypt
     * @param string $key     Key
     * @param string $iv      IV
     * @internal
     */
    public static function ctrEncrypt(string $message, string $key, string $iv): string
    {
        if (Magic::$hasOpenssl) {
            $result = openssl_encrypt($message, 'aes-'.(\strlen($key) * 8).'-ctr', $key, OPENSSL_RAW_DATA, $iv);
            \assert($result !== false);
            return $result;
        }
        $cipher = new AES('ctr');
        $cipher->setKey($key);
        $cipher->setIV($iv);
        return @$cipher->encrypt($message);
    }
    /**
     * IGE encrypt.
     *
     * @param string $plaintext Message to encrypt
     * @param string $key       Key
     * @param string $iv        IV
     * @internal
     */
    public static function igeEncrypt(string $plaintext, string $key, string $iv): string
    {
        if (Magic::$hasOpenssl) {
            $iv_part_1 = substr($iv, 0, 16);
            $iv_part_2 = substr($iv, 16);
            $ciphertext = '';
            for ($i = 0, $length = \strlen($plaintext); $i < $length; $i += 16) {
                $plain = substr($plaintext, $i, 16);

                $cipher = openssl_encrypt($plain ^ $iv_part_1, 'aes-256-ecb', $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING) ^ $iv_part_2;

                $ciphertext .= $cipher;

                $iv_part_1 = $cipher;
                $iv_part_2 = $plain;
            }

            return $ciphertext;
        }
        return IGE::getInstance($key, $iv)->encrypt($plaintext);
    }
    /**
     * IGE decrypt.
     *
     * @param string $ciphertext Message to decrypt
     * @param string $key        Key
     * @param string $iv         IV
     * @internal
     */
    public static function igeDecrypt(string $ciphertext, string $key, string $iv): string
    {
        if (Magic::$hasOpenssl) {
            $iv_part_1 = substr($iv, 0, 16);
            $iv_part_2 = substr($iv, 16);
            $plaintext = '';
            for ($i = 0, $length = \strlen($ciphertext); $i < $length; $i += 16) {
                $cipher = substr($ciphertext, $i, 16);

                $plain = openssl_decrypt($cipher ^ $iv_part_2, 'aes-256-ecb', $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING) ^ $iv_part_1;

                $plaintext .= $plain;

                $iv_part_1 = $cipher;
                $iv_part_2 = $plain;
            }

            return $plaintext;
        }
        return IGE::getInstance($key, $iv)->decrypt($ciphertext);
    }
    /**
     * Check validity of g_a parameters.
     *
     * @internal
     */
    public static function checkG(BigInteger $g_a, BigInteger $p): bool
    {
        /*
         * ***********************************************************************
         * Check validity of g_a
         * 1 < g_a < p - 1
         */
        Logger::log('Executing g_a check (1/2)...', Logger::VERBOSE);
        if ($g_a->compare(Magic::$one) <= 0 || $g_a->compare($p->subtract(Magic::$one)) >= 0) {
            throw new SecurityException('g_a is invalid (1 < g_a < p - 1 is false).');
        }
        Logger::log('Executing g_a check (2/2)...', Logger::VERBOSE);
        if ($g_a->compare(Magic::$twoe1984) < 0 || $g_a->compare($p->subtract(Magic::$twoe1984)) >= 0) {
            throw new SecurityException('g_a is invalid (2^1984 < g_a < p - 2^1984 is false).');
        }
        return true;
    }
    /**
     * Check validity of p and g parameters.
     *
     * @internal
     */
    public static function checkPG(BigInteger $p, BigInteger $g): bool
    {
        /*
         * ***********************************************************************
         * Check validity of dh_prime
         * Is it a prime?
         */
        Logger::log('Executing p/g checks (1/2)...', Logger::VERBOSE);
        if (!$p->isPrime()) {
            throw new SecurityException("p isn't a safe 2048-bit prime (p isn't a prime).");
        }
        /*
         * ***********************************************************************
         * Check validity of p
         * Is (p - 1) / 2 a prime?
         *
         * Almost always fails quite possibly due to phpseclib
         */
        /*
                $this->logger->logger('Executing p/g checks (2/3)...', \danog\MadelineProto\Logger::VERBOSE);
                if (!$p->subtract(\danog\MadelineProto\Magic::$one)->divide(\danog\MadelineProto\Magic::$two)[0]->isPrime()) {
                throw new \danog\MadelineProto\SecurityException("p isn't a safe 2048-bit prime ((p - 1) / 2 isn't a prime).");
                }
        */
        /*
         * ***********************************************************************
         * Check validity of p
         * 2^2047 < p < 2^2048
         */
        Logger::log('Executing p/g checks (2/2)...', Logger::VERBOSE);
        if ($p->compare(Magic::$twoe2047) <= 0 || $p->compare(Magic::$twoe2048) >= 0) {
            throw new SecurityException("g isn't a safe 2048-bit prime (2^2047 < p < 2^2048 is false).");
        }
        /*
         * ***********************************************************************
         * Check validity of g
         * 1 < g < p - 1
         */
        Logger::log('Executing g check...', Logger::VERBOSE);
        if ($g->compare(Magic::$one) <= 0 || $g->compare($p->subtract(Magic::$one)) >= 0) {
            throw new SecurityException('g is invalid (1 < g < p - 1 is false).');
        }
        return true;
    }
}
<?php declare(strict_types=1);

namespace danog\MadelineProto\MTProtoTools;

use danog\DialogId\DialogId;

class_alias(DialogId::class, 'danog\\MadelineProto\\MTProtoTools\\DialogId');
<?php

declare(strict_types=1);

/**
 * Files module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\MTProtoTools;

use Amp\Sync\LocalKeyedMutex;
use danog\AsyncOrm\Annotations\OrmMappedArray;
use danog\AsyncOrm\DbArray;
use danog\AsyncOrm\KeyType;
use danog\AsyncOrm\ValueType;
use danog\MadelineProto\Exception;
use danog\MadelineProto\LegacyMigrator;
use danog\MadelineProto\Logger;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\TL\TLCallback;
use Revolt\EventLoop;

/**
 * Manages min peers.
 *
 * @internal
 */
final class MinDatabase implements TLCallback
{
    use LegacyMigrator;

    public const SWITCH_CONSTRUCTORS = ['inputChannel', 'inputUser', 'inputPeerUser', 'inputPeerChannel'];
    public const CATCH_PEERS = ['message', 'messageService', 'peerUser', 'peerChannel', 'messageEntityMentionName', 'messageFwdHeader', 'messageActionChatCreate', 'messageActionChatAddUser', 'messageActionChatDeleteUser', 'messageActionChatJoinedByLink'];
    public const ORIGINS = ['message', 'messageService'];
    private const V = 1;
    /**
     * References indexed by location.
     *
     * @var DbArray<int, array>
     */
    #[OrmMappedArray(KeyType::INT, ValueType::SCALAR)]
    private $db;
    private array $pendingDb = [];
    /**
     * Temporary cache during deserialization.
     *
     */
    private array $cache = [];
    /**
     * Instance of MTProto.
     *
     */
    private MTProto $API;

    private int $v = 0;

    private LocalKeyedMutex $localMutex;
    public function __construct(MTProto $API)
    {
        $this->API = $API;
        $this->v = self::V;
        $this->localMutex = new LocalKeyedMutex;
    }
    /**
     * @psalm-mutation-free
     */
    public function __destruct()
    {
    }
    /**
     * @psalm-pure
     */
    public function __sleep()
    {
        return ['db', 'pendingDb', 'API', 'v'];
    }
    public function __wakeup(): void
    {
        $this->localMutex = new LocalKeyedMutex;
    }
    public function init(): void
    {
        $this->initDbProperties($this->API->getDbSettings(), $this->API->getDbPrefix().'_MinDatabase_');
        if (!$this->API->getSettings()->getDb()->getEnableMinDb()) {
            $this->db->clear();
            $this->pendingDb = [];
            return;
        }

        if ($this->v !== self::V) {
            $this->db->clear();
            $this->pendingDb = [];
            $this->v = self::V;
        }

        EventLoop::queue(function (): void {
            $this->API->waitForInit();
            foreach ($this->pendingDb as $key => $_) {
                EventLoop::queue($this->flush(...), $key);
            }
        });
    }
    public function clear(): void
    {
        $this->db->clear();
    }
    /**
     * @psalm-pure
     */
    #[\Override]
    public function getMethodAfterResponseDeserializationCallbacks(): array
    {
        return [];
    }
    /**
     * @psalm-pure
     */
    #[\Override]
    public function getMethodBeforeResponseDeserializationCallbacks(): array
    {
        return [];
    }
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function getConstructorAfterDeserializationCallbacks(): array
    {
        return array_merge(array_fill_keys(self::CATCH_PEERS, [$this->addPeer(...)]), array_fill_keys(self::ORIGINS, [$this->addOrigin(...)]));
    }
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function getConstructorBeforeDeserializationCallbacks(): array
    {
        return array_fill_keys(self::ORIGINS, [$this->addOriginContext(...)]);
    }
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function getConstructorBeforeSerializationCallbacks(): array
    {
        return array_fill_keys(self::SWITCH_CONSTRUCTORS, $this->populateFrom(...));
    }
    /**
     * @psalm-pure
     */
    #[\Override]
    public function getTypeMismatchCallbacks(): array
    {
        return [];
    }
    /**
     * @psalm-external-mutation-free
     */
    public function reset(): void
    {
        if ($this->cache) {
            $this->API->logger('Found '.\count($this->cache).' pending contexts', Logger::ERROR);
            $this->cache = [];
        }
    }
    public function addPeer(array|int $location): bool
    {
        $peers = [];
        switch ($location['_']) {
            case 'messageFwdHeader':
                if (isset($location['from_id'])) {
                    $peers[$location['from_id']] = true;
                }
                if (isset($location['channel_id'])) {
                    $peers[$location['channel_id']] = true;
                }
                break;
            case 'messageActionChatCreate':
            case 'messageActionChatAddUser':
                foreach ($location['users'] as $user) {
                    $peers[$user] = true;
                }
                break;
            case 'message':
                $peers[$location['peer_id']] = true;
                if (isset($location['from_id'])) {
                    $peers[$location['from_id']] = true;
                }
                break;
            default:
                $peers[$this->API->getIdInternal($location)] = true;
        }
        $this->API->logger("Caching peer location info from location from {$location['_']}", Logger::ULTRA_VERBOSE);
        $key = \count($this->cache) - 1;
        foreach ($peers as $id => $true) {
            $this->cache[$key][$id] = $id;
        }
        return true;
    }
    /**
     * @psalm-external-mutation-free
     */
    public function addOriginContext(string $type): void
    {
        $this->API->logger("Adding peer origin context for {$type}!", Logger::ULTRA_VERBOSE);
        $this->cache[] = [];
    }
    public function addOrigin(array $data = []): void
    {
        $cache = array_pop($this->cache);
        if ($cache === null) {
            throw new Exception('Trying to add origin with no origin context set');
        }
        $origin = [];
        switch ($data['_']) {
            case 'message':
            case 'messageService':
                $origin['peer'] = $this->API->getIdInternal($data);
                $origin['msg_id'] = $data['id'];
                break;
            default:
                throw new Exception("Unknown origin type provided: {$data['_']}");
        }
        foreach ($cache as $id) {
            if ($origin['peer'] === $id) {
                continue;
            }
            $this->pendingDb[$id] = $origin;
            EventLoop::queue($this->flush(...), $id);
        }
        $this->API->logger("Added origin ({$data['_']}) to ".\count($cache).' peer locations', Logger::ULTRA_VERBOSE);
    }
    private function flush(int $id): void
    {
        if (!isset($this->pendingDb[$id])) {
            return;
        }
        $lock = $this->localMutex->acquire((string) $id);
        try {
            if (!isset($this->pendingDb[$id])) {
                return;
            }
            if ($this->API->peerDatabase->get($id)['min'] ?? true) {
                $this->db[$id] = $this->pendingDb[$id];
            }
        } finally {
            unset($this->pendingDb[$id]);
            EventLoop::queue($lock->release(...));
        }
    }
    public function populateFrom(array $object)
    {
        if (!($object['min'] ?? false)) {
            return $object;
        }
        $id = $this->API->getIdInternal($object);
        $dbObject = $this->pendingDb[$id] ?? $this->db[$id];
        if ($dbObject) {
            $new = array_merge($object, $dbObject);
            $new['_'] .= 'FromMessage';
            $new['peer'] = $this->API->getInputPeer($new['peer']);
            if (($new['peer']['min'] ?? false)) {
                $this->API->logger("Don't have origin peer subinfo with min peer {$id}, this may fail");
                return $object;
            }
            return $new;
        }
        $this->API->logger("Don't have origin info with min peer {$id}, this may fail");
        return $object;
    }

    /**
     * Check if location info is available for peer.
     *
     * @param int $id Peer ID
     */
    public function hasPeer(int $id): bool
    {
        return isset($this->pendingDb[$id]) || isset($this->db[$id]);
    }
    /**
     * Remove location info for peer.
     */
    public function clearPeer(int $id): void
    {
        unset($this->db[$id], $this->pendingDb[$id]);
    }
    /**
     * @psalm-mutation-free
     */
    public function __debugInfo()
    {
        return ['MinDatabase instance '.spl_object_hash($this)];
    }
}
<?php

declare(strict_types=1);

/**
 * CombinedUpdatesState class.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\MTProtoTools;

/**
 * Stores multiple update states.
 *
 * @internal
 *
 * @psalm-external-mutation-free
 */
final class CombinedUpdatesState
{
    /**
     * Update states.
     *
     * @var array<int, UpdatesState>
     */
    private array $states = [];
    /**
     * Constructor function.
     *
     * @psalm-mutation-free
     */
    public function __construct()
    {
        $this->states[0] = new UpdatesState();
    }
    /**
     * Get or update multiple parameters.
     *
     * @param ?int                              $channel Channel to get info about (optional, if not provided returns the entire info array)
     * @param array                            $init    Parameters to update
     *
     * @return ($channel is null ? array<int, UpdatesState> : UpdatesState)
     *
     * @psalm-external-mutation-free
     */
    public function get(?int $channel = null, array $init = []): UpdatesState|array
    {
        if ($channel === null) {
            return $this->states;
        }
        if (!isset($this->states[$channel])) {
            return $this->states[$channel] = new UpdatesState($init, $channel);
        }
        return $this->states[$channel]->update($init);
    }
    /**
     * Remove update state.
     *
     * @param int $channel Channel whose state should be removed
     *
     * @psalm-mutation-free
     */
    public function remove(int $channel): void
    {
        if (isset($this->states[$channel])) {
            unset($this->states[$channel]);
        }
    }
    /**
     * Check if update state is present.
     *
     * @param int $channel Channel ID
     *
     * @psalm-mutation-free
     */
    public function has(int $channel): bool
    {
        return isset($this->states[$channel]);
    }
}
<?php

declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\MTProtoTools\FileRef;

use AssertionError;

/**
 * One incoming traversal of the file reference map: a DFS of a method result or of an update, which pushes the
 * file sources of the traversed constructors to a source stack and commits them, with the file reference, when it
 * reaches a media constructor (https://core.telegram.org/api/file-references#example-implementation).
 *
 * It traverses fully deserialized objects, and its state (the source stack and the parent context) belongs to the
 * traversal: nothing is shared with other traversals, which may run concurrently.
 *
 * The objects are the ones produced by MadelineProto's deserializer, which rewrites some of them: a Peer becomes
 * its bot API ID, the IDs of channels and chats (channel.id, channelFull.id, chatFull.id, channel_id, chat_id...)
 * become bot API IDs. Paths read the API values back ({@see self::getRawValue()}), so the stored file sources
 * contain the values of the API (the channel ID of fileSourceChannelFull, the chat ID of fileSourceChatFull), except
 * for the fields documented as bot API peer IDs.
 *
 * @internal
 */
final class FileRefTraversal
{
    private const CHANNELS = ['channel', 'channelForbidden', 'channelFull', 'community', 'communityForbidden', 'communityFull'];
    private const CHATS = ['chat', 'chatForbidden', 'chatFull'];

    /** @var list<array> The file sources pushed by the traversers on the current branch. */
    private array $sourceStack = [];
    /** @var array<string, list<array|FileRefMethodCall>> The needed parents on the current branch, by predicate. */
    private array $parents = [];
    /** @var list<array{fileId: array{_: string, id: int}, reference: string, sources: list<array>}> */
    private array $commits = [];

    private function __construct(
        private readonly FileRefMap $map,
        private readonly FileRefEnvironment $env,
    ) {
    }

    /**
     * Traverse the result of a method call.
     *
     * @param array $args The arguments of the call, as passed by the user
     *
     * @return list<array{fileId: array{_: string, id: int}, reference: string, sources: list<array>}> The file
     *                                                                                                   references to store, with their sources.
     */
    public static function traverseMethodResult(FileRefMap $map, FileRefEnvironment $env, string $method, array $args, mixed $result): array
    {
        $traverser = $map->methodResults[$method] ?? null;
        if ($traverser === null) {
            return [];
        }
        $self = new self($map, $env);
        $self->traverse($traverser, new FileRefMethodCall($method, $args, $result), self::getParentKey($method, false));
        return $self->commits;
    }

    /**
     * Traverse an update.
     *
     * @return list<array{fileId: array{_: string, id: int}, reference: string, sources: list<array>}>
     */
    public static function traverseUpdate(FileRefMap $map, FileRefEnvironment $env, array $update): array
    {
        $self = new self($map, $env);
        $self->visit($update);
        return $self->commits;
    }

    private static function getParentKey(string $predicate, bool $isConstructor): string
    {
        return ($isConstructor ? 'c:' : 'm:').$predicate;
    }

    /**
     * Push the sources of a traverser and recurse into its children.
     */
    private function traverse(array $traverser, array|FileRefMethodCall $object, string $parentKey): void
    {
        $pushed = $this->pushSources($traverser['push_sources'], $object);
        if ($traverser['is_needed_parent']) {
            $this->parents[$parentKey][] = $object;
        }
        if ($object instanceof FileRefMethodCall) {
            $this->visit($object->result);
        } else {
            foreach ($traverser['params'] as $param) {
                $value = $object[$param['name']] ?? null;
                if ($value === null) {
                    continue;
                }
                if ($param['is_vector']) {
                    if (\is_array($value)) {
                        foreach ($value as $element) {
                            $this->visit($element);
                        }
                    }
                } else {
                    $this->visit($value);
                }
            }
        }
        if ($traverser['is_needed_parent']) {
            array_pop($this->parents[$parentKey]);
        }
        $this->popSources($pushed);
    }

    private function visit(mixed $value): void
    {
        if (!\is_array($value)) {
            return;
        }
        if (!isset($value['_'])) {
            // A vector (the result of a method returning one).
            if (array_is_list($value)) {
                foreach ($value as $element) {
                    $this->visit($element);
                }
            }
            return;
        }
        $traverser = $this->map->incoming[$value['_']] ?? null;
        if ($traverser === null) {
            return;
        }
        if ($traverser['_'] === 'traverseCommitSourceLocation') {
            $this->commit($traverser, $value);
            return;
        }
        $this->traverse($traverser, $value, self::getParentKey($value['_'], true));
    }

    private function commit(array $traverser, array $location): void
    {
        $pushed = $this->pushSources($traverser['push_sources'], $location);
        $reference = FileRefMap::getBytes($location['file_reference'] ?? null);
        if ($reference !== null && isset($location['id'])) {
            $sources = [];
            foreach ($this->sourceStack as $source) {
                $sources[serialize($source)] = $source;
            }
            $this->commits[] = [
                'fileId' => ['_' => $traverser['stored_constructor'], 'id' => $location['id']],
                'reference' => $reference,
                'sources' => array_values($sources),
            ];
        }
        $this->popSources($pushed);
    }

    private function pushSources(array $sources, array|FileRefMethodCall $object): int
    {
        $pushed = 0;
        foreach ($sources as $source) {
            $extracted = $this->extractSource($source, $object);
            if ($extracted !== null) {
                $this->sourceStack[] = $extracted;
                $pushed++;
            }
        }
        return $pushed;
    }

    private function popSources(int $count): void
    {
        for (; $count > 0; $count--) {
            array_pop($this->sourceStack);
        }
    }

    /**
     * Populate a file source from the current object, null if it isn't valid here.
     */
    private function extractSource(array $source, array|FileRefMethodCall $object): ?array
    {
        $parent = null;
        if (isset($source['needs_parent'])) {
            $key = self::getParentKey($source['needs_parent']['predicate'], $source['needs_parent']['is_constructor']);
            if (empty($this->parents[$key])) {
                return null;
            }
            $parent = $this->parents[$key][\count($this->parents[$key]) - 1];
        }
        foreach ($source['needs_not_parents'] as $predicate) {
            if (!empty($this->parents[self::getParentKey($predicate['predicate'], $predicate['is_constructor'])])) {
                return null;
            }
        }
        $res = ['_' => $source['stored_constructor']];
        foreach ($source['stored_params'] as $extractor) {
            if ($extractor['from']['_'] === 'pathParent') {
                if ($parent === null) {
                    return null;
                }
                $value = $this->evaluatePath($extractor['from']['parts'], $parent);
            } else {
                $value = $this->evaluatePath($extractor['from']['parts'], $object);
            }
            if ($value === FileRefMarker::ABORT) {
                return null;
            }
            if ($value === FileRefMarker::ABSENT) {
                continue;
            }
            $value = $this->extract($extractor['_'], $value);
            if ($value === null) {
                return null;
            }
            $res[$extractor['to']] = $value;
        }
        // The skipped flags are never set.
        foreach ($source['skipped_flags'] as $flag) {
            unset($res[$flag]);
        }
        return $res;
    }

    /**
     * Follow a path from the object it starts from.
     */
    private function evaluatePath(array $parts, array|FileRefMethodCall $current): mixed
    {
        $last = \count($parts) - 1;
        foreach ($parts as $i => $part) {
            $param = $part['param'];
            if ($current instanceof FileRefMethodCall) {
                if ($current->method !== $part['constructor']) {
                    return FileRefMarker::ABORT;
                }
                if ($param === '') {
                    $value = $current->result;
                } else {
                    // An argument as passed by the user: omitted arguments are serialized with a default value.
                    $value = $current->args[$param] ?? self::getDefaultArgument($part);
                }
            } else {
                if (!\is_array($current) || ($current['_'] ?? null) !== $part['constructor']) {
                    return FileRefMarker::ABORT;
                }
                $value = isset($current[$param]) ? self::getRawValue($current['_'], $param, $current[$param]) : null;
            }
            if ($part['param_type'] === 'true' && $value === false) {
                // An unset flag of type true.
                $value = null;
            }
            if ($value === null) {
                switch ($part['flag']['_']) {
                    case 'paramNotFlag':
                    case 'paramIsFlagAbortIfEmpty':
                        return FileRefMarker::ABORT;
                    case 'paramIsFlagPassthrough':
                        if ($i !== $last) {
                            throw new AssertionError('A passthrough flag can only be the last element of a path');
                        }
                        return FileRefMarker::ABSENT;
                    case 'paramIsFlagFallback':
                        $value = $this->map->evaluate($part['flag']['fallback'], [], $this->env);
                        if ($value instanceof FileRefMarker) {
                            return FileRefMarker::ABORT;
                        }
                        break;
                    default:
                        throw new AssertionError("Unknown flag {$part['flag']['_']}");
                }
            }
            $current = $value;
        }
        return $current;
    }

    /**
     * The value the serializer sends for an omitted argument of a scalar type, null for the others.
     */
    private static function getDefaultArgument(array $part): mixed
    {
        if ($part['flag']['_'] !== 'paramNotFlag') {
            return null;
        }
        return match ($part['param_type']) {
            'int', 'long' => 0,
            'double' => 0.0,
            'string', 'bytes' => '',
            'Bool' => false,
            default => null,
        };
    }

    /**
     * Undo the rewrites of MadelineProto's deserializer (see TL::deserialize()) on a field of a received
     * constructor: IDs of channels and chats are read back as MTProto IDs.
     */
    private static function getRawValue(string $predicate, string $param, mixed $value): mixed
    {
        if (!\is_int($value) || $value >= 0) {
            return $value;
        }
        switch ($param) {
            case 'channel_id':
            case 'community_id':
            case 'linked_monoforum_id':
            case 'linked_community_id':
                return FileRefMap::channelIdToMTProto($value);
            case 'chat_id':
            case 'migrated_from_chat_id':
                return -$value;
            case 'id':
                if (\in_array($predicate, self::CHANNELS, true)) {
                    return FileRefMap::channelIdToMTProto($value);
                }
                if (\in_array($predicate, self::CHATS, true)) {
                    return -$value;
                }
        }
        return $value;
    }

    /**
     * Apply a field extractor, null to abort.
     */
    private function extract(string $extractor, mixed $value): mixed
    {
        switch ($extractor) {
            case 'extractAndStore':
                return FileRefMap::getBytes($value) ?? $value;
            case 'extractInputStickerSetFromDocumentAttributesAndStore':
                if (!\is_array($value)) {
                    return null;
                }
                foreach ($value as $attribute) {
                    if (($attribute['_'] ?? null) === 'documentAttributeSticker') {
                        $set = $attribute['stickerset'] ?? null;
                        return \is_array($set) && ($set['_'] ?? null) !== 'inputStickerSetEmpty' ? $set : null;
                    }
                }
                return null;
            case 'extractInputStickerSetFromStickerSetAndStore':
                if (($value['_'] ?? null) !== 'stickerSet') {
                    return null;
                }
                return ['_' => 'inputStickerSetID', 'id' => $value['id'], 'access_hash' => $value['access_hash']];
            case 'extractPeerIdFromPeerAndStore':
                // The deserializer turns a Peer into its bot API ID.
                if (\is_int($value)) {
                    return $value;
                }
                return $this->env->getPeerId($value);
            case 'extractPeerIdFromInputPeerAndStore':
                if (\is_array($value)) {
                    switch ($value['_'] ?? null) {
                        case 'inputPeerEmpty':
                            return null;
                        case 'inputPeerSelf':
                            return $this->env->getSelfId();
                        case 'inputPeerChat':
                            return -abs((int) $value['chat_id']);
                    }
                }
                return $this->env->getPeerId($value);
            case 'extractChannelIdFromChannelAndStore':
                if (!\is_array($value) || !\in_array($value['_'] ?? null, self::CHANNELS, true) || !\is_int($value['id'] ?? null)) {
                    return null;
                }
                return FileRefMap::channelIdToMTProto($value['id']);
            case 'extractChannelIdFromInputChannelAndStore':
                if (\is_array($value)) {
                    switch ($value['_'] ?? null) {
                        case 'inputChannelEmpty':
                            return null;
                        case 'inputChannel':
                        case 'inputChannelFromMessage':
                            return \is_int($value['channel_id'] ?? null) ? FileRefMap::channelIdToMTProto($value['channel_id']) : null;
                    }
                }
                $id = $this->env->getPeerId($value);
                return $id !== null && $id < 0 ? FileRefMap::channelIdToMTProto($id) : null;
            case 'extractUserIdFromUserAndStore':
                if (!\is_array($value) || ($value['_'] ?? null) !== 'user' || !\is_int($value['id'] ?? null)) {
                    return null;
                }
                return $value['id'];
            case 'extractUserIdFromInputUserAndStore':
                if (\is_array($value)) {
                    switch ($value['_'] ?? null) {
                        case 'inputUserEmpty':
                            return null;
                        case 'inputUserSelf':
                            return $this->env->getSelfId();
                        case 'inputUser':
                        case 'inputUserFromMessage':
                            return \is_int($value['user_id'] ?? null) ? $value['user_id'] : null;
                    }
                }
                $id = $this->env->getPeerId($value);
                return $id !== null && $id > 0 ? $id : null;
            default:
                throw new AssertionError("Unknown extractor $extractor");
        }
    }
}
<?php

declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\MTProtoTools\FileRef;

use AssertionError;
use danog\DialogId\DialogId;
use danog\MadelineProto\Magic;
use danog\MadelineProto\TL\Types\Bytes;

/**
 * The file reference map (src/file_ref_map.json, see https://core.telegram.org/api/file-references), indexed.
 *
 * Contains the stateless parts of the file reference database: the incoming traversers (used by
 * {@see FileRefTraversal}), the outgoing traversers, which find the media locations in the arguments of a method
 * call, and the refresh actions, which build the method call that refreshes a file source.
 *
 * @internal
 */
final class FileRefMap
{
    private const PATH = __DIR__.'/../../file_ref_map.json';

    private static ?self $instance = null;

    /** The layer the map was generated from. */
    public readonly int $layer;
    /** @var array<string, array> traverseMethodResult traversers, by method name. */
    public readonly array $methodResults;
    /** @var array<string, array> traverseIncomingConstructor and traverseCommitSourceLocation traversers, by predicate. */
    public readonly array $incoming;
    /** @var array<string, string> The FileId constructors of the traverseSwapLocation traversers, by predicate. */
    public readonly array $swapLocations;
    /** @var array<string, list<array>> The parameters of the traverseMethodCall traversers, by method name. */
    private readonly array $outgoingMethods;
    /** @var array<string, list<array>> The parameters of the traverseOutgoingConstructor traversers, by predicate. */
    private readonly array $outgoingConstructors;
    /** @var array<string, array> The refresh actions, by FileSource constructor. */
    public readonly array $refreshActions;
    /** @var array<string, string> Why each skipped source is skipped, by predicate. */
    public readonly array $skipped;

    /**
     * The map shipped with MadelineProto, loaded once.
     */
    public static function get(): self
    {
        return self::$instance ??= self::fromArray(
            json_decode(file_get_contents(self::PATH), true, flags: JSON_THROW_ON_ERROR),
        );
    }

    /**
     * Index a map, decoded from its JSON form.
     */
    public static function fromArray(array $map): self
    {
        if (($map['_'] ?? null) !== 'fileReferenceMap') {
            throw new AssertionError('Not a file reference map');
        }
        return new self($map);
    }

    private function __construct(array $map)
    {
        $this->layer = $map['layer'];
        $methodResults = $incoming = $swap = $outgoingMethods = $outgoingConstructors = $actions = $skipped = [];
        foreach ($map['traversers_incoming'] as $t) {
            switch ($t['_']) {
                case 'traverseMethodResult':
                    self::add($methodResults, $t['name'], $t);
                    break;
                case 'traverseIncomingConstructor':
                case 'traverseCommitSourceLocation':
                    self::add($incoming, $t['predicate'], $t);
                    break;
                default:
                    throw new AssertionError("Unknown incoming traverser {$t['_']}");
            }
        }
        foreach ($map['traversers_outgoing'] as $t) {
            switch ($t['_']) {
                case 'traverseSwapLocation':
                    self::add($swap, $t['predicate'], $t['stored_constructor']);
                    break;
                case 'traverseMethodCall':
                    self::add($outgoingMethods, $t['name'], $t['params']);
                    break;
                case 'traverseOutgoingConstructor':
                    self::add($outgoingConstructors, $t['predicate'], $t['params']);
                    break;
                default:
                    throw new AssertionError("Unknown outgoing traverser {$t['_']}");
            }
        }
        foreach ($map['refresh_actions'] as $action) {
            self::add($actions, $action['stored_constructor'], $action['action']);
        }
        foreach ($map['skipped_incoming_sources'] as $skip) {
            $skipped[$skip['predicate']['predicate']] = $skip['why'];
        }
        $this->methodResults = $methodResults;
        $this->incoming = $incoming;
        $this->swapLocations = $swap;
        $this->outgoingMethods = $outgoingMethods;
        $this->outgoingConstructors = $outgoingConstructors;
        $this->refreshActions = $actions;
        $this->skipped = $skipped;
    }

    private static function add(array &$index, string $key, mixed $value): void
    {
        if (isset($index[$key])) {
            throw new AssertionError("Duplicate entry for $key in the file reference map");
        }
        $index[$key] = $value;
    }

    /**
     * The key of a FileId in the file reference and file source tables.
     *
     * @param array{_: string, id: int} $fileId
     */
    public static function getFileIdKey(array $fileId): string
    {
        return $fileId['_'].':'.$fileId['id'];
    }

    /**
     * A bytes value as a string, null if it isn't one.
     */
    public static function getBytes(mixed $value): ?string
    {
        if ($value instanceof Bytes) {
            return (string) $value;
        }
        if (\is_string($value)) {
            return $value;
        }
        if (\is_array($value) && ($value['_'] ?? null) === 'bytes' && \is_string($value['bytes'] ?? null)) {
            $decoded = base64_decode($value['bytes'], true);
            return $decoded === false ? null : $decoded;
        }
        return null;
    }

    /**
     * The bot API ID of an MTProto channel ID.
     */
    public static function channelIdToBotApi(int $id): int
    {
        if ($id < 0) {
            return $id;
        }
        return $id > Magic::MAX_CHANNEL_ID
            ? DialogId::fromMonoforumId($id)
            : DialogId::fromSupergroupOrChannelId($id);
    }

    /**
     * The MTProto ID of a bot API channel ID.
     */
    public static function channelIdToMTProto(int $id): int
    {
        return $id < 0 ? DialogId::toMTProtoId($id) : $id;
    }

    /**
     * Find the media locations (traverseSwapLocation) in the arguments of a method call.
     *
     * Each location records the index of the first vector traversed to reach it (`group`): the `%d` of a
     * `FILE_REFERENCE_%d_EXPIRED` error is the index of the media in the multi_media vector of
     * messages.sendMultiMedia, or in the extended_media vector of an inputMediaPaidMedia.
     *
     * @param callable(string, mixed): mixed $normalize Converts an argument to a constructor of the given TL type,
     *                                                    as the serializer would (a MessageMedia to an InputMedia...)
     *
     * @return array{0: array, 1: list<array{fileId: array{_: string, id: int}, group: ?int, path: list<array-key>, reference: ?string}>}
     *               The arguments, with the traversed values converted, and the locations.
     */
    public function findLocations(string $method, array $args, callable $normalize): array
    {
        $locations = [];
        if (isset($this->outgoingMethods[$method])) {
            $args = $this->visitParams($this->outgoingMethods[$method], $args, [], null, $normalize, $locations);
        }
        return [$args, $locations];
    }

    /**
     * Replace the file references of the locations found by {@see self::findLocations()}.
     *
     * @param list<array{fileId: array{_: string, id: int}, path: list<array-key>, ...}> $locations
     * @param array<string, string>                                                     $references New file references, by FileId key
     */
    public static function swapReferences(array $args, array $locations, array $references): array
    {
        foreach ($locations as $location) {
            $key = self::getFileIdKey($location['fileId']);
            if (!isset($references[$key])) {
                continue;
            }
            $value = &$args;
            foreach ($location['path'] as $k) {
                $value = &$value[$k];
            }
            $value['file_reference'] = $references[$key];
            unset($value);
        }
        return $args;
    }

    private function visitParams(array $params, array $object, array $path, ?int $group, callable $normalize, array &$locations): array
    {
        foreach ($params as $param) {
            $name = $param['name'];
            if (!isset($object[$name])) {
                continue;
            }
            if ($param['is_vector']) {
                if (!\is_array($object[$name])) {
                    continue;
                }
                $i = 0;
                foreach ($object[$name] as $k => $value) {
                    $object[$name][$k] = $this->visit($param['type'], $value, [...$path, $name, $k], $group ?? $i, $normalize, $locations);
                    $i++;
                }
            } else {
                $object[$name] = $this->visit($param['type'], $object[$name], [...$path, $name], $group, $normalize, $locations);
            }
        }
        return $object;
    }

    private function visit(string $type, mixed $value, array $path, ?int $group, callable $normalize, array &$locations): mixed
    {
        $value = $normalize($type, $value);
        if (!\is_array($value) || !isset($value['_'])) {
            return $value;
        }
        $predicate = $value['_'];
        if (isset($this->swapLocations[$predicate])) {
            if (isset($value['id'])) {
                $locations[] = [
                    'fileId' => ['_' => $this->swapLocations[$predicate], 'id' => (int) $value['id']],
                    'group' => $group,
                    'path' => $path,
                    'reference' => self::getBytes($value['file_reference'] ?? null),
                ];
            }
            return $value;
        }
        if (isset($this->outgoingConstructors[$predicate])) {
            return $this->visitParams($this->outgoingConstructors[$predicate], $value, $path, $group, $normalize, $locations);
        }
        return $value;
    }

    /**
     * Build the method call that refreshes a file source, according to its refresh action.
     *
     * @return ?array{0: string, 1: array} The method and its arguments, null if the source can't be refreshed.
     */
    public function buildRefreshCall(array $source, FileRefEnvironment $env): ?array
    {
        $action = $this->refreshActions[$source['_']] ?? null;
        if ($action === null) {
            return null;
        }
        switch ($action['_']) {
            case 'callOp':
                $args = $this->evaluateArgs($action['args'], $source, $env);
                return $args === null ? null : [$action['method'], $args];
            case 'getMessageOp':
                $peer = $this->evaluate($action['peer'], $source, $env);
                $id = $this->evaluate($action['id'], $source, $env);
                $shortcut = $this->evaluate($action['quick_reply_shortcut_id'], $source, $env);
                if (!\is_array($peer) || !\is_int($id) || $shortcut === FileRefMarker::ABORT) {
                    return null;
                }
                $ids = [['_' => 'inputMessageID', 'id' => $id]];
                if ($shortcut !== FileRefMarker::ABSENT) {
                    return ['messages.getQuickReplyMessages', ['shortcut_id' => $shortcut, 'id' => [$id], 'hash' => 0]];
                }
                switch ($peer['_']) {
                    case 'inputPeerChannel':
                        return ['channels.getMessages', [
                            'channel' => ['_' => 'inputChannel', 'channel_id' => $peer['channel_id'], 'access_hash' => $peer['access_hash']],
                            'id' => $ids,
                        ]];
                    case 'inputPeerChannelFromMessage':
                        return ['channels.getMessages', [
                            'channel' => ['_' => 'inputChannelFromMessage', 'peer' => $peer['peer'], 'msg_id' => $peer['msg_id'], 'channel_id' => $peer['channel_id']],
                            'id' => $ids,
                        ]];
                    default:
                        return ['messages.getMessages', ['id' => $ids]];
                }
                // no break
            default:
                throw new AssertionError("Unknown action {$action['_']}");
        }
    }

    /**
     * @return ?array The arguments, null if one couldn't be evaluated.
     */
    private function evaluateArgs(array $args, array $source, FileRefEnvironment $env): ?array
    {
        $res = [];
        foreach ($args as $arg) {
            $value = $this->evaluate($arg['value'], $source, $env);
            if ($value === FileRefMarker::ABORT) {
                return null;
            }
            if ($value !== FileRefMarker::ABSENT) {
                $res[$arg['key']] = $value;
            }
        }
        return $res;
    }

    /**
     * Evaluate a TypedOp against a stored file source.
     */
    public function evaluate(array $op, array $source, FileRefEnvironment $env): mixed
    {
        if ($op['_'] !== 'typedOp') {
            throw new AssertionError("Expected a typedOp, got a {$op['_']}");
        }
        $op = $op['op'];
        switch ($op['_']) {
            case 'copyOp':
                return \array_key_exists($op['from'], $source) ? $source[$op['from']] : FileRefMarker::ABSENT;
            case 'getInputChannelByIdOp':
                $id = $source[$op['from']] ?? null;
                return \is_int($id) ? $env->getInputChannelById(self::channelIdToBotApi($id)) : FileRefMarker::ABORT;
            case 'getInputUserByIdOp':
                $id = $source[$op['from']] ?? null;
                return \is_int($id) ? $env->getInputUserById($id) : FileRefMarker::ABORT;
            case 'getInputPeerByIdOp':
                $id = $source[$op['from']] ?? null;
                return \is_int($id) ? $env->getInputPeerById($id) : FileRefMarker::ABORT;
            case 'constructorOp':
                $args = $this->evaluateArgs($op['args'], $source, $env);
                return $args === null ? FileRefMarker::ABORT : ['_' => $op['constructor'], ...$args];
            case 'vectorOp':
                $res = [];
                foreach ($op['values'] as $value) {
                    $value = $this->evaluate($value, $source, $env);
                    if ($value === FileRefMarker::ABORT) {
                        return FileRefMarker::ABORT;
                    }
                    if ($value !== FileRefMarker::ABSENT) {
                        $res[] = $value;
                    }
                }
                return $res;
            case 'intLiteralOp':
            case 'longLiteralOp':
                return (int) $op['value'];
            case 'stringLiteralOp':
                return (string) $op['value'];
            case 'doubleLiteralOp':
                return (float) $op['value'];
            case 'boolLiteralOp':
                return (bool) $op['value'];
            case 'bytesLiteralOp':
                return self::getBytes($op['value']) ?? FileRefMarker::ABORT;
            case 'themeFormatLiteralOp':
                return $env->getThemeFormat();
            default:
                throw new AssertionError("Unknown op {$op['_']}");
        }
    }
}
<?php

declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\MTProtoTools\FileRef;

/**
 * What the file reference map needs from the client: peer IDs and input peers.
 *
 * @internal
 */
interface FileRefEnvironment
{
    /**
     * The ID of the logged in user (for inputPeerSelf and inputUserSelf), null if unknown.
     */
    public function getSelfId(): ?int;

    /**
     * The bot API ID of a peer passed by the user as a method argument (an InputPeer, InputUser or InputChannel
     * constructor or a bot API ID), null if it cannot be determined without network requests.
     *
     * Called while handling responses: it must not wait for other responses.
     */
    public function getPeerId(mixed $peer): ?int;

    /**
     * The InputPeer of a bot API peer ID.
     */
    public function getInputPeerById(int $id): array;

    /**
     * The InputUser of a user ID.
     */
    public function getInputUserById(int $id): array;

    /**
     * The InputChannel of a bot API channel ID.
     */
    public function getInputChannelById(int $id): array;

    /**
     * The theming engines supported by the client (account.getTheme's format).
     */
    public function getThemeFormat(): string;
}
<?php

declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\MTProtoTools\FileRef;

/**
 * A method call whose result is traversed: the object paths start from for method sources and method parents.
 *
 * @internal
 */
final readonly class FileRefMethodCall
{
    public function __construct(
        public string $method,
        /** The arguments the method was called with, as passed by the user. */
        public array $args,
        public mixed $result,
    ) {
    }
}
<?php

declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\MTProtoTools\FileRef;

/**
 * Results of a path or an op of the file reference map that are not values.
 *
 * @internal
 */
enum FileRefMarker
{
    /** An unset flag passed through (paramIsFlagPassthrough, or a copyOp of it): the field is left unset. */
    case ABSENT;
    /** The extraction cannot be completed (wrong constructor, unset required flag...): the source is invalid. */
    case ABORT;
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\MTProtoTools;

use Amp\ByteStream\Pipe;
use Amp\ByteStream\ReadableBuffer;
use Amp\ByteStream\ReadableResourceStream;
use Amp\ByteStream\ReadableStream;
use Amp\ByteStream\StreamException;
use Amp\ByteStream\WritableResourceStream;
use Amp\ByteStream\WritableStream;
use Amp\Cancellation;
use Amp\CancelledException;
use Amp\CompositeCancellation;
use Amp\DeferredCancellation;
use Amp\File\File;
use Amp\File\Whence;
use Amp\Http\HttpStatus;
use Amp\Http\Server\Request as ServerRequest;
use Amp\Http\Server\Response;
use Amp\Sync\LocalMutex;
use Amp\Sync\Lock;
use Closure;
use danog\MadelineProto\API;
use danog\MadelineProto\BotApiFileId;
use danog\MadelineProto\EventHandler\Media;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\Exception;
use danog\MadelineProto\FileCallback;
use danog\MadelineProto\FileCallbackInterface;
use danog\MadelineProto\Lang;
use danog\MadelineProto\LocalFile;
use danog\MadelineProto\Logger;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\NothingInTheSocketException;
use danog\MadelineProto\RemoteUrl;
use danog\MadelineProto\ResumableDownloadStream;
use danog\MadelineProto\ResumableStream;
use danog\MadelineProto\Settings;
use danog\MadelineProto\Stream\Common\BufferedRawStream;
use danog\MadelineProto\Stream\Common\SimpleBufferedRawStream;
use danog\MadelineProto\Stream\ConnectionContext;
use danog\MadelineProto\Stream\StreamInterface;
use danog\MadelineProto\Stream\Transport\PremadeStream;
use danog\MadelineProto\TL\Conversion\Extension;
use danog\MadelineProto\Tools;
use danog\MadelineProto\UploadResumeException;
use Revolt\EventLoop;
use Throwable;
use Webmozart\Assert\Assert;

use const FILTER_VALIDATE_URL;

use function Amp\async;
use function Amp\ByteStream\buffer;
use function Amp\ByteStream\getOutputBufferStream;
use function Amp\File\exists;

use function Amp\File\getModificationTime;
use function Amp\File\getSize;
use function Amp\File\openFile;

/**
 * @internal
 *
 * @method string getSessionName()
 */
trait FilesLogic
{
    use FilesAbstraction;
    /**
     * Download file to browser.
     *
     * Supports HEAD requests and content-ranges for parallel and resumed downloads.
     *
     * @param array|string|FileCallbackInterface|\danog\MadelineProto\EventHandler\Message $messageMedia File to download
     * @param null|callable                                                                $cb           Status callback (can also use FileCallback)
     * @param null|int                                                                     $size         Size of file to download, required for bot API file IDs.
     * @param null|string                                                                  $mime         MIME type of file to download, required for bot API file IDs.
     * @param null|string                                                                  $name         Name of file to download, required for bot API file IDs.
     */
    public function downloadToBrowser(array|string|FileCallbackInterface|Message $messageMedia, ?callable $cb = null, ?int $size = null, ?string $name = null, ?string $mime = null, ?Cancellation $cancellation = null): void
    {
        if (\is_object($messageMedia) && $messageMedia instanceof FileCallbackInterface) {
            $cb = $messageMedia;
            $messageMedia = $messageMedia->getFile();
        }
        if (\is_string($messageMedia) && ($size === null || $mime === null || $name === null)) {
            throw new Exception('downloadToBrowser only supports bot file IDs if the file size, file name and MIME type are also specified in the third, fourth and fifth parameters of the method.');
        }

        $headers = [];
        if (isset($_SERVER['HTTP_RANGE'])) {
            $headers['range'] = $_SERVER['HTTP_RANGE'];
        }

        try {
            $messageMedia = $this->getDownloadInfo($messageMedia);
            $messageMedia['size'] = $size ?? $messageMedia['size'];
            $messageMedia['mime'] = $mime ?? $messageMedia['mime'];
            if ($name) {
                $name = explode('.', $name, 2);
                $messageMedia['name'] = $name[0];
                $messageMedia['ext'] = isset($name[1]) ? '.'.$name[1] : '';
            }

            Assert::true(isset($_SERVER['REQUEST_METHOD']));

            /** @psalm-suppress PossiblyUndefinedArrayOffset */
            $result = ResponseInfo::parseHeaders(
                $_SERVER['REQUEST_METHOD'],
                $headers,
                $messageMedia,
                $this->getSettings()->getFiles()->getSandboxDownloads(),
            );
        } catch (Throwable $e) {
            $this->logger->logger("An error occurred inside of downloadToBrowser: $e", Logger::FATAL_ERROR);
            $result = ResponseInfo::error(HttpStatus::NOT_FOUND);
        }

        $result->writeHeaders();

        if (!\in_array($result->getCode(), [HttpStatus::OK, HttpStatus::PARTIAL_CONTENT], true)) {
            Tools::echo($result->getCodeExplanation());
        } elseif ($result->shouldServe()) {
            if (ob_get_level()) {
                ob_end_flush();
                ob_implicit_flush();
            }
            [$start, $end] = $result->getServeRange();
            // PHP only notices that the client went away when output fails to be sent:
            // flush after each chunk, and stop the download (in the IPC worker, too) once the client is gone.
            $aborted = new DeferredCancellation;
            $output = getOutputBufferStream();
            $callable = static function (string $payload) use ($output, $aborted): int {
                $aborted->getCancellation()->throwIfRequested();
                $output->write($payload);
                flush();
                if (connection_aborted()) {
                    $aborted->cancel();
                }
                return \strlen($payload);
            };
            try {
                $this->downloadToCallable(
                    $messageMedia,
                    $callable,
                    $cb,
                    false,
                    $start,
                    $end,
                    null,
                    $cancellation === null
                        ? $aborted->getCancellation()
                        : new CompositeCancellation($cancellation, $aborted->getCancellation()),
                );
            } catch (CancelledException $e) {
                if (!$aborted->isCancelled()) {
                    throw $e;
                }
                $this->logger->logger('The client closed the connection, stopped downloadToBrowser', Logger::NOTICE);
            }
        }
    }
    /**
     * Download file to an amphp stream, returning it.
     *
     * The returned stream is resumable: it can be serialized, and once unserialized,
     * reading continues from the end of the last chunk read before serialization.
     * The progress callback and cancellation are not preserved by serialization.
     *
     * @param mixed    $messageMedia File to download
     * @param callable $cb           Callback
     * @param int      $offset       Offset where to start downloading
     * @param int      $end          Offset where to end download
     */
    public function downloadToReturnedStream(mixed $messageMedia, ?callable $cb = null, int $offset = 0, int $end = -1, ?Cancellation $cancellation = null): ReadableStream&ResumableStream
    {
        /** @var ?Closure(float, float, float): void */
        $cb = $cb === null ? null : $cb(...);
        return new ResumableDownloadStream(
            $this,
            $this->getDownloadInfo($messageMedia),
            $cb,
            $offset,
            $end,
            $cancellation,
        );
    }
    /**
     * Download file to stream.
     *
     * @param mixed                                               $messageMedia File to download
     * @param mixed|FileCallbackInterface|resource|WritableStream $stream       Stream where to download file
     * @param callable                                            $cb           Callback
     * @param int                                                 $offset       Offset where to start downloading
     * @param int                                                 $end          Offset where to end download
     */
    public function downloadToStream(mixed $messageMedia, mixed $stream, ?callable $cb = null, int $offset = 0, int $end = -1, ?Cancellation $cancellation = null): void
    {
        $messageMedia = $this->getDownloadInfo($messageMedia);
        if (\is_object($stream) && $stream instanceof FileCallbackInterface) {
            $cb = $stream;
            $stream = $stream->getFile();
        }
        if (!\is_object($stream)) {
            $stream = new WritableResourceStream($stream);
        }
        if (!$stream instanceof WritableStream) {
            throw new Exception('Invalid stream provided');
        }
        $seekable = false;
        if (method_exists($stream, 'seek')) {
            try {
                $stream->seek($offset);
                $seekable = true;
            } catch (StreamException $e) {
            }
        }
        $lock = new LocalMutex;
        $callable = static function (string $payload, int $offset) use ($stream, $seekable, $lock) {
            /** @var Lock */
            $l = $lock->acquire();
            try {
                if ($seekable) {
                    /** @var File $stream */
                    while ($stream->tell() !== $offset) {
                        $stream->seek($offset);
                    }
                }
                $stream->write($payload);
            } finally {
                EventLoop::queue($l->release(...));
            }
            return \strlen($payload);
        };
        $this->downloadToCallable($messageMedia, $callable, $cb, $seekable, $offset, $end, null, $cancellation);
    }

    /**
     * Download file to amphp/http-server response.
     *
     * Supports HEAD requests and content-ranges for parallel and resumed downloads.
     *
     * @param array|string|FileCallbackInterface|\danog\MadelineProto\EventHandler\Message $messageMedia File to download
     * @param ServerRequest                                                                $request      Request
     * @param callable                                                                     $cb           Status callback (can also use FileCallback)
     * @param null|int                                                                     $size         Size of file to download, required for bot API file IDs.
     * @param null|string                                                                  $name         Name of file to download, required for bot API file IDs.
     * @param null|string                                                                  $mime         MIME type of file to download, required for bot API file IDs.
     */
    public function downloadToResponse(array|string|FileCallbackInterface|Message $messageMedia, ServerRequest $request, ?callable $cb = null, ?int $size = null, ?string $mime = null, ?string $name = null, ?Cancellation $cancellation = null): Response
    {
        if (\is_object($messageMedia) && $messageMedia instanceof FileCallbackInterface) {
            $cb = $messageMedia;
            $messageMedia = $messageMedia->getFile();
        }

        if (\is_string($messageMedia) && ($size === null || $mime === null || $name === null)) {
            throw new Exception('downloadToResponse only supports bot file IDs if the file size, file name and MIME type are also specified in the fourth, fifth and sixth parameters of the method.');
        }

        $messageMedia = $this->getDownloadInfo($messageMedia);
        $messageMedia['size'] ??= $size;
        $messageMedia['mime'] ??= $mime;
        if ($name) {
            $messageMedia['name'] = $name;
        }

        $result = ResponseInfo::parseHeaders(
            $request->getMethod(),
            array_map(static fn (array $headers) => $headers[0], $request->getHeaders()),
            $messageMedia,
            $this->getSettings()->getFiles()->getSandboxDownloads(),
        );

        $body = null;
        if ($result->shouldServe()) {
            $pipe = new Pipe(1024 * 1024);
            [$start, $end] = $result->getServeRange();
            $f = async($this->downloadToStream(...), $messageMedia, $pipe->getSink(), $cb, $start, $end, $cancellation);
            $f->finally($pipe->getSink()->close(...));
            $body = $pipe->getSource();
        } elseif (!\in_array($result->getCode(), [HttpStatus::OK, HttpStatus::PARTIAL_CONTENT], true)) {
            $body = $result->getCodeExplanation();
        }

        return new Response($result->getCode(), $result->getHeaders(), $body);
    }

    /**
     * Upload file to secret chat.
     *
     * @param FileCallbackInterface|LocalFile|RemoteUrl|BotApiFileId|string|array|resource $file      File, URL or Telegram file to upload
     * @param string                             $fileName File name
     * @param callable                           $cb       Callback
     *
     * @return array InputFile constructor
     */
    public function uploadEncrypted($file, string $fileName = '', ?callable $cb = null, ?Cancellation $cancellation = null): array
    {
        return $this->upload($file, $fileName, $cb, true, $cancellation);
    }

    /**
     * @internal
     */
    public function processMedia(array &$media, ?Cancellation $cancellation, bool $upload = false): void
    {
        if ($media['_'] === 'inputMediaPhotoExternal') {
            $media['_'] = 'inputMediaUploadedPhoto';
            if ($media['url'] instanceof FileCallbackInterface) {
                $media['file'] = new FileCallback(
                    new RemoteUrl($media['url']->getFile()),
                    $media['url']
                );
            } else {
                $media['file'] = new RemoteUrl($media['url']);
            }
            unset($media['url']);
        } elseif ($media['_'] === 'inputMediaDocumentExternal') {
            $media['_'] = 'inputMediaUploadedDocument';
            if ($media['url'] instanceof FileCallbackInterface) {
                $media['file'] = new FileCallback(
                    new RemoteUrl($url = $media['url']->getFile()),
                    $media['url']
                );
            } else {
                $media['file'] = new RemoteUrl($url = $media['url']);
            }
            unset($media['url']);
            $media['mime_type'] = Extension::getMimeFromExtension(
                pathinfo($url, PATHINFO_EXTENSION),
                'application/octet-stream'
            );
        }
        if ($upload && isset($media['file']) && !\is_array($media['file'])) {
            $media['file'] = $this->upload($media['file'], cancellation: $cancellation);
        }
        if ($upload && isset($media['thumb']) && !\is_array($media['thumb'])) {
            $media['thumb'] = $this->upload($media['thumb'], cancellation: $cancellation);
        }
    }
    /**
     * Upload file.
     *
     * @param FileCallbackInterface|LocalFile|RemoteUrl|BotApiFileId|ReadableStream|string|array|resource $file      File, URL or Telegram file to upload
     * @param string                                                                       $fileName  File name
     * @param callable                                                                     $cb        Callback
     * @param boolean                                                                      $encrypted Whether to encrypt file for secret chats
     *
     * @return array InputFile constructor
     */
    public function upload($file, string $fileName = '', ?callable $cb = null, bool $encrypted = false, ?Cancellation $cancellation = null): array
    {
        if (\is_object($file) && $file instanceof FileCallbackInterface) {
            $cb = $file;
            $file = $file->getFile();
        }
        if ($file instanceof RemoteUrl) {
            $file = $file->url;
            return $this->uploadFromUrl($file, 0, $fileName, $cb, $encrypted, $cancellation);
        }
        if ($file instanceof BotApiFileId) {
            // Not its download info: the upload is resumed by its file ID.
            return $this->uploadFromTgfile($file, $cb, $encrypted, $cancellation);
        }
        if (\is_string($file) || (\is_object($file) && method_exists($file, '__toString'))) {
            if (filter_var($file, FILTER_VALIDATE_URL)) {
                return $this->uploadFromUrl($file, 0, $fileName, $cb, $encrypted, $cancellation);
            }
        } elseif (\is_array($file) || $file instanceof Media) {
            return $this->uploadFromTgfile($file, $cb, $encrypted, $cancellation);
        }
        if ($file instanceof ReadableStream || \is_resource($file)) {
            return $this->uploadFromStream($file, 0, '', $fileName, $cb, $encrypted, $cancellation);
        }
        if ($file instanceof LocalFile) {
            $file = $file->file;
        } else {
            /** @var Settings $settings */
            $settings = $this->getSettings();
            if (!$settings->getFiles()->getAllowAutomaticUpload()) {
                return $this->uploadFromUrl($file, 0, $fileName, $cb, $encrypted, $cancellation);
            }
        }
        $file = Tools::absolute($file);
        if (!exists($file)) {
            throw new Exception(Lang::$current_lang['file_not_exist']);
        }
        if (empty($fileName)) {
            $fileName = basename($file);
        }
        $size = getSize($file);
        if ($size > 512 * 1024 * 8000) {
            throw new Exception('Given file is too big!');
        }
        $stream = openFile($file, 'rb');
        $mime = Extension::getMimeFromFile($file);
        return async($this->uploadFromStream(...), $stream, $size, $mime, $fileName, $cb, $encrypted, $cancellation)->finally($stream->close(...))->await($cancellation);
    }

    /**
     * Upload file from stream.
     *
     * @param mixed    $stream    PHP resource or AMPHP async stream
     * @param integer  $size      File size
     * @param string   $mime      Mime type
     * @param string   $fileName  File name
     * @param callable $cb        Callback
     * @param boolean  $encrypted Whether to encrypt file for secret chats
     *
     * @return array InputFile constructor
     */
    public function uploadFromStream(mixed $stream, int $size = 0, ?string $mime = null, string $fileName = '', ?callable $cb = null, bool $encrypted = false, ?Cancellation $cancellation = null): array
    {
        return $this->uploadFromStreamInternal($stream, $size, $mime, $fileName, $cb, $encrypted, $cancellation);
    }

    /**
     * Upload from callable, resuming interrupted uploads of the same file.
     *
     * Resuming only happens in the process owning the session: IPC clients send uploads of resumable
     * sources to it as a whole (see {@see \danog\MadelineProto\Ipc\Client::upload()}).
     */
    private function uploadFromCallableResumable(callable $callable, int $size, ?string $mime, string $fileName, ?callable $cb, bool $seekable, bool $encrypted, ?Cancellation $cancellation, ?string $resumeKey): array
    {
        if ($this instanceof MTProto) {
            /** @psalm-suppress InaccessibleMethod Same class */
            return $this->uploadFromCallableInternal($callable, $size, $mime, $fileName, $cb, $seekable, $encrypted, $cancellation, $resumeKey);
        }
        return $this->uploadFromCallable($callable, $size, $mime, $fileName, $cb, $seekable, $encrypted, $cancellation);
    }

    /**
     * Upload file from stream, resuming an interrupted upload of the same file if possible.
     *
     * Uploads of files and resumable streams are identified automatically.
     *
     * @param ?string                            $resumeKey Identifies the source of the file across restarts, see uploadFromCallable().
     * @param (Closure(int): ?ReadableStream)|null $reopenAt  Reopens the source at a byte offset, to resume without reading what was already uploaded; returns null if it can't.
     */
    private function uploadFromStreamInternal(mixed $stream, int $size, ?string $mime, string $fileName, ?callable $cb, bool $encrypted, ?Cancellation $cancellation, ?string $resumeKey = null, ?Closure $reopenAt = null): array
    {
        if (\is_object($stream) && $stream instanceof FileCallbackInterface) {
            $cb = $stream;
            $stream = $stream->getFile();
        }
        if (!\is_object($stream)) {
            $stream = new ReadableResourceStream($stream);
        }
        if (!$stream instanceof ReadableStream) {
            throw new Exception('Invalid stream provided');
        }
        if (!$size && $stream instanceof ResumableDownloadStream) {
            $size = $stream->getRemainingSize() ?? 0;
        }
        $seekable = false;
        if (method_exists($stream, 'seek')) {
            try {
                $stream->seek(0);
                $seekable = true;
            } catch (StreamException $e) {
            }
        }
        if (!$size) {
            if ($seekable && method_exists($stream, 'tell')) {
                $stream->seek(0, Whence::End);
                $size = $stream->tell();
                $stream->seek(0);
            } elseif ($stream instanceof ReadableBuffer) {
                $stream = buffer($stream, $cancellation);
                $size = \strlen($stream);
                $stream = new ReadableBuffer($stream);
            }
        }
        if ($resumeKey === null) {
            if ($stream instanceof File) {
                try {
                    $resumeKey = 'file:'.$stream->getPath()."\0".getModificationTime($stream->getPath());
                } catch (Throwable) {
                }
            } elseif ($stream instanceof ResumableStream) {
                // Before anything is read from it.
                $resumeKey = 'stream:'.serialize($stream);
            }
        }
        if ($stream instanceof File) {
            $lock = new LocalMutex;
            $nextOffset = 0;
            $callable = static function (int $offset, int $size) use ($stream, $seekable, $lock, &$nextOffset, $cancellation): string {
                /** @var Lock */
                $l = $lock->acquire();
                try {
                    if ($seekable) {
                        while ($stream->tell() !== $offset) {
                            $stream->seek($offset);
                        }
                    } else {
                        // When resuming an upload, the first parts were already uploaded.
                        while ($nextOffset < $offset) {
                            $skipped = $stream->read($cancellation, min($offset - $nextOffset, 1024 * 1024));
                            if ($skipped === null) {
                                throw new UploadResumeException("Could not resume the upload: the file ended at offset $nextOffset, before the offset $offset to resume from, it changed since the upload was interrupted");
                            }
                            $nextOffset += \strlen($skipped);
                        }
                        Assert::eq($offset, $nextOffset);
                        $nextOffset += $size;
                    }
                    $result = $stream->read($cancellation, $size);
                    \assert($result !== null);
                    return $result;
                } finally {
                    EventLoop::queue($l->release(...));
                }
            };
            return $this->uploadFromCallableResumable($callable, $size, $mime, $fileName, $cb, $seekable, $encrypted, $cancellation, $resumeKey);
        }
        /** @var ?BufferedRawStream */
        $buffered = $stream instanceof BufferedRawStream ? $stream : null;
        $created = false;
        $nextOffset = 0;
        $callable = static function (int $offset, int $size) use (&$stream, &$buffered, &$created, &$nextOffset, $reopenAt, $cancellation): string {
            $skip = $offset - $nextOffset;
            Assert::greaterThanEq($skip, 0);
            if ($skip > 0 && $buffered === null) {
                // Resuming an upload, the first parts were already uploaded: skip them without reading them, if possible.
                if ($reopenAt !== null && ($reopened = $reopenAt($offset)) !== null) {
                    $stream->close();
                    $stream = $reopened;
                    $skip = 0;
                } elseif ($stream instanceof ResumableDownloadStream) {
                    $stream->skip($skip);
                    $skip = 0;
                }
            }
            if ($buffered === null) {
                $ctx = (new ConnectionContext())->addStream(PremadeStream::class, $stream)->addStream(SimpleBufferedRawStream::class);
                $buffered = $ctx->getStream();
                \assert($buffered instanceof BufferedRawStream);
                $created = true;
            }
            $nextOffset = $offset + $size;
            $read = static function (int $size) use ($buffered, $cancellation): string {
                $reader = $buffered->getReadBuffer($l);
                try {
                    $result = $reader->bufferRead($size, $cancellation);
                } catch (NothingInTheSocketException $e) {
                    $reader = $buffered->getReadBuffer($size);
                    $result = $reader->bufferRead($size, $cancellation);
                }
                \assert($result !== null);
                return $result;
            };
            while ($skip > 0) {
                $skipped = \strlen($read(min($skip, 1024 * 1024)));
                if ($skipped === 0) {
                    throw new UploadResumeException('Could not resume the upload: the stream ended at offset '.($offset - $skip).", before the offset $offset to resume from, it changed since the upload was interrupted");
                }
                $skip -= $skipped;
            }
            return $read($size);
        };
        try {
            return $this->uploadFromCallableResumable($callable, $size, $mime, $fileName, $cb, false, $encrypted, $cancellation, $resumeKey);
        } finally {
            if ($created) {
                /** @var StreamInterface $buffered */
                $buffered->disconnect();
            }
        }
    }
}
<?php

declare(strict_types=1);

/**
 * PeerHandler module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\MTProtoTools;

use AssertionError;
use danog\DialogId\DialogId;
use danog\MadelineProto\API;
use danog\MadelineProto\EventHandler\Message\Entities\InputMentionName;
use danog\MadelineProto\EventHandler\Message\Entities\MentionName;
use danog\MadelineProto\Exception;
use danog\MadelineProto\Logger;
use danog\MadelineProto\Magic;
use danog\MadelineProto\PeerNotInDbException;
use danog\MadelineProto\RPCError\ChannelPrivateError;
use danog\MadelineProto\RPCError\ChatAdminRequiredError;
use danog\MadelineProto\RPCError\ChatForbiddenError;
use danog\MadelineProto\RPCErrorException;
use danog\MadelineProto\Settings;
use danog\MadelineProto\Tools;
use Throwable;
use Webmozart\Assert\Assert;

use const SORT_NUMERIC;
use function Amp\async;
use function Amp\Future\await;

/**
 * Manages peers.
 *
 * @property Settings     $settings     Settings
 * @property PeerDatabase $peerDatabase
 *
 * @internal
 */
trait PeerHandler
{
    /**
     * Set support info.
     *
     * @internal
     * @internal
     *
     * @param array $support Support info
     *
     * @psalm-external-mutation-free
     */
    public function addSupport(array $support): void
    {
        $this->supportUser = $support['user']['id'];
    }

    /**
     * Check if the specified peer is a forum.
     *
     */
    public function isForum(mixed $peer): bool
    {
        return $this->getInfo($peer)['Chat']['forum'] ?? false;
    }

    /**
     * Check if the specified peer is a bot.
     *
     */
    public function isBot(mixed $peer): bool
    {
        return $this->getType($peer) === API::PEER_TYPE_BOT;
    }

    /**
     * Check if peer is present in internal peer database.
     *
     * @param mixed $id Peer
     */
    public function peerIsset(mixed $id): bool
    {
        try {
            return $this->peerDatabase->isset($this->getIdInternal($id));
        } catch (ChannelPrivateError|ChatForbiddenError) {
            return true;
        } catch (RPCErrorException|Exception) {
            return false;
        }
    }
    /**
     * Check if all peer entities are in db.
     *
     * @param array $entities Entity list
     * @internal
     */
    public function entitiesPeerIsset(array $entities): bool
    {
        try {
            foreach ($entities as $entity) {
                if ($entity instanceof MentionName) {
                    if (!($this->peerIsset($entity->userId))) {
                        return false;
                    }
                } elseif ($entity instanceof InputMentionName) {
                    if (!($this->peerIsset($entity->userId))) {
                        return false;
                    }
                } elseif (\is_array($entity) && ($entity['_'] === 'messageEntityMentionName' || $entity['_'] === 'inputMessageEntityMentionName')) {
                    if (!($this->peerIsset($entity['user_id']))) {
                        return false;
                    }
                }
            }
        } catch (Exception $e) {
            return false;
        }
        return true;
    }
    /**
     * Check if fwd peer is set.
     *
     * @param array $fwd Forward info
     * @internal
     */
    public function fwdPeerIsset(array $fwd)
    {
        try {
            if (isset($fwd['user_id']) && !($this->peerIsset($fwd['user_id']))) {
                return false;
            }
            if (isset($fwd['channel_id']) && !($this->peerIsset($fwd['channel_id']))) {
                return false;
            }
        } catch (Exception $e) {
            return false;
        }
        return true;
    }

    /**
     * Get the bot API ID of a peer.
     *
     * @param mixed $id Peer
     */
    public function getId(mixed $id): int
    {
        if (\is_int($id)) {
            return $id;
        }
        return $this->getInfo($id, \danog\MadelineProto\API::INFO_TYPE_ID);
    }

    /**
     * Get bot API ID from peer object.
     *
     * @internal
     *
     * @param mixed $id Peer
     *
     * @psalm-mutation-free
     */
    public function getIdInternal(mixed $id): ?int
    {
        if (\is_array($id)) {
            switch ($id['_']) {
                case 'help.promoData':
                    // Its peer is optional.
                    return isset($id['peer']) ? $this->getIdInternal($id['peer']) : null;
                case 'updateDialogPinned':
                case 'updateDialogUnreadMark':
                case 'updateNotifySettings':
                case 'updateDraftMessage':
                case 'updateReadHistoryInbox':
                case 'updateReadHistoryOutbox':
                case 'updateChatDefaultBannedRights':
                case 'updateDeleteScheduledMessages':
                case 'updateSentStoryReaction':
                case 'updateBotCommands':
                case 'updateBotChatInviteRequester':
                case 'updatePendingJoinRequests':
                case 'updateStory':
                case 'updatePinnedMessages':
                case 'dialog':
                case 'dialogPeer':
                case 'notifyPeer':
                case 'folderPeer':
                case 'inputDialogPeer':
                case 'inputNotifyPeer':
                case 'inputFolderPeer':
                case 'communityPeer':
                case 'communityPeerRequest':
                    return $this->getIdInternal($id['peer']);
                case 'inputNotifyCommunity':
                case 'inputDialogPeerCommunity':
                    return $this->getIdInternal($id['community']);
                case 'dialogCommunity':
                case 'dialogPeerCommunity':
                case 'notifyCommunity':
                case 'messageActionChatJoinedViaCommunity':
                    return $id['community_id'];
                case 'inputUserSelf':
                case 'inputPeerSelf':
                    return $this->authorization['user']['id'];
                case 'user':
                case 'userFull':
                    return $id['id'];
                case 'messageActionChatJoinedByLink':
                    return $id['inviter_id'];
                case 'chat':
                case 'chatForbidden':
                case 'chatFull':
                case 'communityFull':
                case 'community':
                case 'communityForbidden':
                    return $id['id'];
                case 'inputPeerChat':
                case 'peerChat':
                    return $id['chat_id'];
                case 'channelForbidden':
                case 'channel':
                case 'channelFull':
                    return $id['id'];
                case 'updatePeerBlocked':
                case 'message':
                case 'messageService':
                    if (!isset($id['from_id']) // No other option
                        // It's a channel/chat, 100% what we need
                        || $id['peer_id'] < 0
                        // It is a user, and it's not ourselves
                        || $id['peer_id'] !== $this->authorization['user']['id']
                    ) {
                        return $id['peer_id'];
                    }
                    return $id['from_id'];
                case 'peerChannel':
                case 'requestedPeerChannel':
                case 'inputChannel':
                case 'inputPeerChannel':
                case 'inputChannelFromMessage':
                case 'inputPeerChannelFromMessage':
                    // Built to be serialized, with the MTProto ID (see genAll), unlike the received constructors below,
                    // whose IDs the deserializer already converted to bot API IDs.
                    if ($id['channel_id'] > 0) {
                        return $id['channel_id'] > Magic::MAX_CHANNEL_ID
                            ? DialogId::fromMonoforumId($id['channel_id'])
                            : DialogId::fromSupergroupOrChannelId($id['channel_id']);
                    }
                    return $id['channel_id'];
                case 'updateChannelReadMessagesContents':
                case 'updateChannelAvailableMessages':
                case 'updateChannel':
                case 'updateChannelWebPage':
                case 'updateChannelMessageViews':
                case 'updateReadChannelInbox':
                case 'updateReadChannelOutbox':
                case 'updateDeleteChannelMessages':
                case 'updateChannelPinnedMessage':
                case 'updateChannelTooLong':
                case 'updateChannelParticipant':
                case 'updatePinnedChannelMessages':
                case 'updateChannelMessageForwards':
                case 'updateChannelUserTyping':
                    return $id['channel_id'];
                case 'updateChatParticipants':
                    $id = $id['participants'];
                    // no break
                case 'updateChatUserTyping':
                case 'updateChatParticipantAdd':
                case 'updateChatParticipantDelete':
                case 'updateChatParticipantAdmin':
                case 'updateChatAdmins':
                case 'updateChatPinnedMessage':
                    return $id['chat_id'];
                case 'contact':
                case 'updateUserTyping':
                case 'updateUserStatus':
                case 'updateUserName':
                case 'updateUserPhoto':
                case 'updateUserPhone':
                case 'updateUserBlocked':
                case 'updateContactRegistered':
                case 'updateContactLink':
                case 'updateBotInlineQuery':
                case 'updateInlineBotCallbackQuery':
                case 'updateBotInlineSend':
                case 'updateBotCallbackQuery':
                case 'updateBotPrecheckoutQuery':
                case 'updateBotShippingQuery':
                case 'updateUserPinnedMessage':
                case 'updateUser':
                case 'updateUserEmojiStatus':
                case 'updateBotStopped':
                case 'inputUserFromMessage':
                case 'inputPeerUserFromMessage':
                case 'inputPeerUser':
                case 'inputUser':
                case 'peerUser':
                case 'requestedPeerUser':
                case 'messageEntityMentionName':
                case 'messageActionChatDeleteUser':
                    return $id['user_id'];
                case 'updatePhoneCall':
                    return $id['phone_call']->getOtherID();
                case 'updateNewMessage':
                case 'updateNewChannelMessage':
                case 'updateEditMessage':
                case 'updateEditChannelMessage':
                    return $this->getIdInternal($id['message']);
                default:
                    throw new Exception('Invalid constructor given '.$id['_']);
            }
        }
        if (is_numeric($id)) {
            if (\is_string($id)) {
                $id = (int) $id;
            }
            return $id;
        }
        return null;
    }
    /**
     * Get InputPeer object.
     *
     * @internal
     * @param mixed $id Peer
     */
    public function getInputPeer(mixed $id)
    {
        return $this->getInfo($id, \danog\MadelineProto\API::INFO_TYPE_PEER);
    }
    /**
     * Get InputUser/InputChannel object.
     *
     * @internal
     * @param mixed $id Peer
     */
    public function getInputConstructor(mixed $id)
    {
        return $this->getInfo($id, \danog\MadelineProto\API::INFO_TYPE_CONSTRUCTOR);
    }

    public array $caching_full_info = [];

    /**
     * Get type of peer.
     *
     * @param mixed $id Peer
     *
     * @return \danog\MadelineProto\API::PEER_TYPE_*
     */
    public function getType(mixed $id): string
    {
        return $this->getInfo($id, API::INFO_TYPE_TYPE);
    }

    /**
     * Get info about peer, returns an Info object.
     *
     * If passed a secret chat ID, returns information about the user, not about the secret chat.
     * Use getSecretChat to return information about the secret chat.
     *
     * @param mixed                                 $id   Peer
     * @param \danog\MadelineProto\API::INFO_TYPE_* $type Whether to generate an Input*, an InputPeer or the full set of constructors
     * @see https://docs.madelineproto.xyz/Info.html
     * @return ($type is \danog\MadelineProto\API::INFO_TYPE_ALL ? array{
     *      User?: array,
     *      Chat?: array,
     *      bot_api_id: int,
     *      user_id?: int,
     *      chat_id?: int,
     *      channel_id?: int,
     *      type: string
     * } : ($type is API::INFO_TYPE_TYPE ? string : ($type is \danog\MadelineProto\API::INFO_TYPE_ID ? int : array{_: string, user_id?: int, access_hash?: int, min?: bool, chat_id?: int, channel_id?: int}|array{_: string, user_id?: int, access_hash?: int, min?: bool}|array{_: string, channel_id: int, access_hash: int, min: bool})))
     */
    public function getInfo(mixed $id, int $type = \danog\MadelineProto\API::INFO_TYPE_ALL): array|int|string
    {
        if (\is_array($id)) {
            switch ($id['_']) {
                case 'updateEncryption':
                    $id = $this->getSecretChat($id['chat']['id'])->otherID;
                    break;
                case 'updateNewEncryptedMessage':
                    $id = $id['message'];
                    // no break
                case 'inputEncryptedChat':
                case 'updateEncryptedChatTyping':
                case 'updateEncryptedMessagesRead':
                case 'encryptedMessage':
                case 'encryptedMessageService':
                    $id = $this->getSecretChat($id['chat_id'])->otherID;
                    break;
            }
        }
        $try_id = $this->getIdInternal($id);
        if ($try_id !== null) {
            $id = $try_id;
        }
        if (is_numeric($id)) {
            $id = (int) $id;
            Assert::true($id !== 0, "An invalid ID was specified!");
            if (DialogId::isSecretChat($id)) {
                $id = $this->getSecretChat($id)->otherID;
            }
            if (!$this->peerDatabase->isset($id)) {
                try {
                    $this->logger->logger("Try fetching {$id} with access hash 0");
                    if (DialogId::isSupergroupOrChannelOrMonoforum($id)) {
                        $this->peerDatabase->addChatBlocking($id);
                    } elseif ($id < 0) {
                        $this->methodCallAsyncRead('messages.getChats', ['id' => [-$id]]);
                    } else {
                        $this->peerDatabase->addUserBlocking($id);
                    }
                } catch (Exception $e) {
                    $this->logger->logger($e->getMessage(), Logger::WARNING);
                } catch (RPCErrorException $e) {
                    $this->logger->logger($e->getMessage(), Logger::WARNING);
                }
            }
            $chat = $this->peerDatabase->get($id);
            if (!$chat) {
                if ($this->cacheFullDialogs()) {
                    return $this->getInfo($id, $type);
                }
                throw new PeerNotInDbException();
            }
            if (($chat['min'] ?? false)
                && $this->minDatabase->hasPeer($id)
                && !isset($this->caching_full_info[$id])
            ) {
                $this->caching_full_info[$id] = true;
                $this->logger->logger("Only have min peer for {$id} in database, trying to fetch full info");
                try {
                    // A min peer may come without an access hash: the serializer replaces the min input constructor
                    // with inputChannelFromMessage/inputUserFromMessage, which don't need one.
                    $input = $this->genAll(['access_hash' => $chat['access_hash'] ?? 0] + $chat, \danog\MadelineProto\API::INFO_TYPE_CONSTRUCTOR);
                    if ($id < 0) {
                        $this->methodCallAsyncRead('channels.getChannels', ['id' => [$input]]);
                    } else {
                        $this->methodCallAsyncRead('users.getUsers', ['id' => [$input]]);
                    }
                } catch (Exception $e) {
                    $this->logger->logger($e->getMessage(), Logger::WARNING);
                } catch (RPCErrorException $e) {
                    $this->logger->logger($e->getMessage(), Logger::WARNING);
                } finally {
                    unset($this->caching_full_info[$id]);
                }
                $chat = $this->peerDatabase->get($id) ?? $chat;
            }
            try {
                return $this->genAll($chat, $type);
            } catch (PeerNotInDbException $e) {
                $this->peerDatabase->clear($id);
                throw $e;
            }
        }
        if ($r = Tools::parseLink($id)) {
            [$invite, $content] = $r;
            if ($invite) {
                $invite = $this->methodCallAsyncRead('messages.checkChatInvite', ['hash' => $content]);
                if (isset($invite['chat'])) {
                    return $this->getInfo($invite['chat'], $type);
                }
                throw new Exception('You have not joined this chat');
            } else {
                $id = $content;
            }
        }
        if (\is_string($id)) {
            $id = strtolower(str_replace('@', '', $id));
            if ($id === 'me') {
                return $this->getInfo($this->authorization['user']['id'], $type);
            }
            if ($id === 'support') {
                if (!$this->supportUser) {
                    $this->methodCallAsyncRead('help.getSupport', []);
                }
                return $this->getInfo($this->supportUser, $type);
            }
            if ($bot_api_id = $this->peerDatabase->getIdFromUsername($id)) {
                // Usernames are indexed for min peers too, which may lack an access hash (seen in a forward, a mention,
                // a group call...): check that the peer can be used (fetching it through the min database if needed)
                // even when only its ID is requested, or resolve the username again.
                try {
                    $info = $this->getInfo($bot_api_id);
                    return $type === \danog\MadelineProto\API::INFO_TYPE_ALL ? $info : $this->getInfo($bot_api_id, $type);
                } catch (PeerNotInDbException) {
                }
            }
            if ($bot_api_id = $this->peerDatabase->resolveUsername($id)) {
                return $this->getInfo($bot_api_id, $type);
            }
        } else {
            return $this->getInfo($id, $type);
        }
        if ($this->cacheFullDialogs()) {
            return $this->getInfo($id, $type);
        }
        throw new PeerNotInDbException();
    }
    /**
     * @param array                                 $constructor
     * @param \danog\MadelineProto\API::INFO_TYPE_* $type
     * @return ($type is \danog\MadelineProto\API::INFO_TYPE_ALL ? (array{
     *      User?: array,
     *      Chat?: array,
     *      bot_api_id: int,
     *      user_id?: int,
     *      chat_id?: int,
     *      channel_id?: int,
     *      type: string
     * }&array) : array|int)
     */
    private function genAll($constructor, int $type): array|int|string
    {
        if ($type === \danog\MadelineProto\API::INFO_TYPE_CONSTRUCTOR) {
            if ($constructor['_'] === 'user') {
                return ($constructor['self'] ?? false) ? ['_' => 'inputUserSelf'] : ['_' => 'inputUser', 'user_id' => $constructor['id'], 'access_hash' => $constructor['access_hash'], 'min' => $constructor['min'] ?? false];
            }
            if ($constructor['_'] === 'channel' || $constructor['_'] === 'community') {
                return ['_' => 'inputChannel', 'channel_id' => DialogId::toMTProtoId($constructor['id']), 'access_hash' => $constructor['access_hash'], 'min' => $constructor['min'] ?? false];
            }
        }
        if ($type === \danog\MadelineProto\API::INFO_TYPE_PEER) {
            if ($constructor['_'] === 'user') {
                return ($constructor['self'] ?? false) ? ['_' => 'inputPeerSelf'] : ['_' => 'inputPeerUser', 'user_id' => $constructor['id'], 'access_hash' => $constructor['access_hash'], 'min' => $constructor['min'] ?? false];
            }
            if ($constructor['_'] === 'channel' || $constructor['_'] === 'community') {
                return ['_' => 'inputPeerChannel', 'channel_id' => DialogId::toMTProtoId($constructor['id']), 'access_hash' => $constructor['access_hash'], 'min' => $constructor['min'] ?? false];
            }
            if ($constructor['_'] === 'chat' || $constructor['_'] === 'chatForbidden') {
                return ['_' => 'inputPeerChat', 'chat_id' => -$constructor['id']];
            }
        }
        if ($type === \danog\MadelineProto\API::INFO_TYPE_ID) {
            return $constructor['id'];
        }
        if ($type === \danog\MadelineProto\API::INFO_TYPE_TYPE) {
            if ($constructor['_'] === 'user') {
                return $constructor['bot'] ?? false ? 'bot' : 'user';
            }
            if ($constructor['_'] === 'channel') {
                return $constructor['megagroup'] ?? false ? 'supergroup' : 'channel';
            }
            if ($constructor['_'] === 'community') {
                return 'community';
            }
            if ($constructor['_'] === 'chat' || $constructor['_'] === 'chatForbidden') {
                return 'chat';
            }
        }
        if ($type === \danog\MadelineProto\API::INFO_TYPE_USERNAMES) {
            return PeerDatabase::getUsernames($constructor);
        }
        $res = [$this->TL->getConstructors()->findByPredicate($constructor['_'])['type'] => $constructor];
        switch ($constructor['_']) {
            case 'user':
                if (!isset($constructor['access_hash'])) {
                    $this->cacheFullDialogs();
                    throw new PeerNotInDbException();
                }
                $res['user_id'] = $constructor['id'];
                $res['bot_api_id'] = $constructor['id'];
                $res['type'] = $constructor['bot'] ?? false ? 'bot' : 'user';
                break;
            case 'chat':
            case 'chatForbidden':
                $res['chat_id'] = $constructor['id'];
                $res['bot_api_id'] = $constructor['id'];
                $res['type'] = 'chat';
                break;
            case 'channel':
                if (!isset($constructor['access_hash'])) {
                    $this->cacheFullDialogs();
                    throw new PeerNotInDbException();
                }
                $res['channel_id'] = $constructor['id'];
                $res['bot_api_id'] = $constructor['id'];
                $res['type'] = $constructor['megagroup'] ?? false ? 'supergroup' : 'channel';
                break;
            case 'community':
                if (!isset($constructor['access_hash'])) {
                    $this->cacheFullDialogs();
                    throw new PeerNotInDbException();
                }
                $res['community_id'] = $constructor['id'];
                $res['bot_api_id'] = $constructor['id'];
                $res['type'] = 'community';
                break;
            case 'channelForbidden':
            case 'communityForbidden':
                throw new PeerNotInDbException();
            default:
                throw new Exception('Invalid constructor given '.$constructor['_']);
        }
        return $res;
    }
    /**
     * Refresh full peer cache for a certain peer.
     *
     * @param mixed $id The peer to refresh
     */
    public function refreshFullPeerCache(mixed $id): void
    {
        $this->peerDatabase->refreshFullPeerCache($id);
    }
    /**
     * Refresh peer cache for a certain peer.
     *
     */
    public function refreshPeerCache(mixed ...$ids): void
    {
        $users = [];
        $chats = [];
        $supergroups = [];
        foreach ($ids as $id) {
            if (\is_string($id)) {
                if ($r = Tools::parseLink($id)) {
                    [$invite, $content] = $r;
                    if ($invite) {
                        $invite = $this->methodCallAsyncRead('messages.checkChatInvite', ['hash' => $content]);
                        if (isset($invite['chat'])) {
                            continue;
                        }
                        throw new Exception('You have not joined this chat');
                    } else {
                        $id = $content;
                    }
                }
                if (\is_string($id)) {
                    $id = strtolower(str_replace('@', '', $id));
                    if ($id === 'me') {
                        $id = $this->authorization['user']['id'];
                    } elseif ($id === 'support') {
                        $this->methodCallAsyncRead('help.getSupport', []);
                        continue;
                    } else {
                        $id = $this->peerDatabase->resolveUsername($id);
                        if ($id === null) {
                            throw new PeerNotInDbException;
                        }
                    }
                }
            }
            $id = $this->getIdInternal($id);
            Assert::notNull($id);
            switch (DialogId::getType($id)) {
                case DialogId::CHANNEL_OR_SUPERGROUP:
                    $supergroups []= $id;
                    break;
                case DialogId::MONOFORUM:
                    $supergroups []= $id;
                    break;
                case DialogId::CHAT:
                    $chats []= $id;
                    break;
                case DialogId::USER:
                    $users []= $id;
                    break;
                default:
                    throw new AssertionError("Invalid ID $id!");
            }
        }
        if ($supergroups) {
            $this->methodCallAsyncRead('channels.getChannels', ['id' => $supergroups]);
        }
        if ($chats) {
            $this->methodCallAsyncRead('messages.getChats', ['id' => $chats]);
        }
        if ($users) {
            $this->methodCallAsyncRead('users.getUsers', ['id' => $users]);
        }
    }
    /**
     * When was full info for this chat last cached.
     *
     * @param mixed $id Chat ID
     */
    public function fullChatLastUpdated(mixed $id): int
    {
        return $this->peerDatabase->getFull($this->getIdInternal($id))['inserted'] ?? 0;
    }
    /**
     * Get full info about peer, returns an FullInfo object.
     *
     * @param mixed $id Peer
     * @see https://docs.madelineproto.xyz/FullInfo.html
     */
    public function getFullInfo(mixed $id): array
    {
        $partial = $this->getInfo($id);
        if (DialogId::isSecretChat($partial['bot_api_id'])) {
            return $partial;
        }
        $full = $this->peerDatabase->getFull($partial['bot_api_id']);
        if (time() - ($full['inserted'] ?? 0) < $this->getSettings()->getPeer()->getFullInfoCacheTime()) {
            return array_merge($partial, $full);
        }
        switch ($partial['type']) {
            case 'user':
            case 'bot':
                $this->methodCallAsyncRead('users.getFullUser', ['id' => $partial['bot_api_id']]);
                break;
            case 'chat':
                $this->methodCallAsyncRead('messages.getFullChat', $partial);
                break;
            case 'channel':
            case 'community':
            case 'supergroup':
                $this->methodCallAsyncRead('channels.getFullChannel', ['channel' => $partial['bot_api_id']]);
                break;
            default:
                throw new Exception('Invalid peer type '.$partial['type']);
        }
        return array_merge($partial, $this->peerDatabase->getFull($partial['bot_api_id']));
    }
    /**
     * Get full info about peer (including full list of channel members), returns a Chat object.
     *
     * @param mixed $id Peer
     * @see https://docs.madelineproto.xyz/Chat.html
     */
    public function getPwrChat(mixed $id, bool $fullfetch = true): array
    {
        try {
            $full = $this->getSettings()->getDb()->getEnableFullPeerDb() ? $this->getFullInfo($id) : $this->getInfo($id);
        } catch (Throwable) {
            $full = $this->getInfo($id);
        }
        $res = ['id' => $full['bot_api_id'], 'type' => $full['type']];
        switch ($full['type']) {
            case 'user':
            case 'bot':
                foreach (['first_name', 'last_name', 'username', 'usernames', 'verified', 'restricted', 'restriction_reason', 'status', 'bot_inline_placeholder', 'phone', 'lang_code', 'bot_nochats'] as $key) {
                    if (isset($full['User'][$key])) {
                        $res[$key] = $full['User'][$key];
                    }
                }
                foreach (['about', 'bot_info', 'phone_calls_available', 'phone_calls_private', 'common_chats_count', 'can_pin_message', 'pinned_msg_id', 'notify_settings'] as $key) {
                    if (isset($full['full'][$key])) {
                        $res[$key] = $full['full'][$key];
                    }
                }
                if (isset($full['full']['profile_photo'])) {
                    $res['photo'] = $full['full']['profile_photo'];
                }
                if (isset($full['full']['fallback_photo'])) {
                    $res['fallback_photo'] = $full['full']['fallback_photo'];
                }
                if (isset($full['full']['personal_photo'])) {
                    $res['personal_photo'] = $full['full']['personal_photo'];
                }
                break;
            case 'chat':
                foreach (['title', 'participants_count', 'admin', 'admins_enabled'] as $key) {
                    if (isset($full['Chat'][$key])) {
                        $res[$key] = $full['Chat'][$key];
                    }
                }
                foreach (['bot_info', 'pinned_msg_id', 'notify_settings'] as $key) {
                    if (isset($full['full'][$key])) {
                        $res[$key] = $full['full'][$key];
                    }
                }
                if (isset($res['admins_enabled'])) {
                    $res['all_members_are_administrators'] = !$res['admins_enabled'];
                }
                if (isset($full['full']['chat_photo'])) {
                    $res['photo'] = $full['full']['chat_photo'];
                }
                if (isset($full['full']['exported_invite']['link'])) {
                    $res['invite'] = $full['full']['exported_invite']['link'];
                }
                if (isset($full['full']['participants']['participants'])) {
                    $res['participants'] = $full['full']['participants']['participants'];
                }
                break;
            case 'channel':
            case 'supergroup':
                foreach (['title', 'democracy', 'restricted', 'restriction_reason', 'username', 'usernames', 'signatures'] as $key) {
                    if (isset($full['Chat'][$key])) {
                        $res[$key] = $full['Chat'][$key];
                    }
                }
                foreach (['read_inbox_max_id', 'read_outbox_max_id', 'hidden_prehistory', 'bot_info', 'notify_settings', 'can_set_stickers', 'stickerset', 'can_view_participants', 'can_set_username', 'participants_count', 'admins_count', 'kicked_count', 'banned_count', 'migrated_from_chat_id', 'migrated_from_max_id', 'pinned_msg_id', 'about', 'hidden_prehistory', 'available_min_id', 'can_view_stats', 'online_count'] as $key) {
                    if (isset($full['full'][$key])) {
                        $res[$key] = $full['full'][$key];
                    }
                }
                if (isset($full['full']['chat_photo'])) {
                    $res['photo'] = $full['full']['chat_photo'];
                }
                if (isset($full['full']['exported_invite']['link'])) {
                    $res['invite'] = $full['full']['exported_invite']['link'];
                }
                break;
            case 'community':
                foreach (['title', 'creator', 'left', 'admin_rights', 'default_banned_rights'] as $key) {
                    if (isset($full['Chat'][$key])) {
                        $res[$key] = $full['Chat'][$key];
                    }
                }
                foreach (['about', 'linked_peers', 'admins_count', 'kicked_count', 'peer_link_requests_pending'] as $key) {
                    if (isset($full['full'][$key])) {
                        $res[$key] = $full['full'][$key];
                    }
                }
                if (isset($full['full']['chat_photo'])) {
                    $res['photo'] = $full['full']['chat_photo'];
                }
                break;
        }
        if (isset($res['participants']) && $fullfetch) {
            foreach ($res['participants'] as $key => $participant) {
                $newres = [];
                $newres['user'] = ($this->getPwrChat($participant['user_id'] ?? $participant['peer'], false));
                if (isset($participant['inviter_id'])) {
                    $newres['inviter'] = ($this->getPwrChat($participant['inviter_id'], false));
                }
                if (isset($participant['kicked_by'])) {
                    $newres['kicked_by'] = ($this->getPwrChat($participant['kicked_by'], false));
                }
                if (isset($participant['date'])) {
                    $newres['date'] = $participant['date'];
                }
                switch ($participant['_']) {
                    case 'chatParticipant':
                        $newres['role'] = 'user';
                        break;
                    case 'chatParticipantAdmin':
                        $newres['role'] = 'admin';
                        break;
                    case 'chatParticipantCreator':
                        $newres['role'] = 'creator';
                        break;
                }
                $res['participants'][$key] = $newres;
            }
        }
        if (!isset($res['participants']) && $fullfetch && \in_array($res['type'], ['supergroup', 'channel'], true)) {
            $total_count = ($res['participants_count'] ?? 0) + ($res['admins_count'] ?? 0) + ($res['kicked_count'] ?? 0) + ($res['banned_count'] ?? 0);
            $res['participants'] = [];
            $filters = ['channelParticipantsAdmins', 'channelParticipantsBots'];
            $promises = [];
            foreach ($filters as $filter) {
                $promises []= async(function () use ($filter, $total_count, &$res): void {
                    $this->fetchParticipants($res['id'], $filter, '', $total_count, $res);
                });
            }
            await($promises);

            $q = '';
            $filters = ['channelParticipantsSearch', 'channelParticipantsKicked', 'channelParticipantsBanned'];
            $promises = [];
            foreach ($filters as $filter) {
                $promises []= async(function () use ($filter, $q, $total_count, &$res): void {
                    $this->recurseAlphabetSearchParticipants($res['id'], $filter, $q, $total_count, $res, 0);
                });
            }
            await($promises);

            $this->logger->logger('Fetched '.\count($res['participants'])." out of {$total_count}");
            $res['participants'] = array_values($res['participants']);
        }
        if (!$fullfetch) {
            unset($res['participants']);
        }
        return $res;
    }
    private function recurseAlphabetSearchParticipants(int $channel, string $filter, string $q, int $total_count, array &$res, int $depth): array
    {
        if (!($this->fetchParticipants($channel, $filter, $q, $total_count, $res))) {
            return [];
        }
        ++$depth;
        $promises = [];
        for ($x = 'a'; $x !== 'aa' && $total_count > \count($res['participants']); $x = str_increment($x)) {
            $promises []= async(function () use ($channel, $filter, $q, $x, $total_count, &$res, $depth) {
                return $this->recurseAlphabetSearchParticipants($channel, $filter, $q.$x, $total_count, $res, $depth);
            });
        }

        if ($depth > 3) {
            return $promises;
        }

        $yielded = array_merge(...await($promises));
        while ($yielded) {
            $newYielded = [];

            foreach (array_chunk($yielded, 10) as $promises) {
                $newYielded = array_merge($newYielded, ...(await($promises)));
            }

            $yielded = $newYielded;
        }

        return [];
    }
    private function fetchParticipants(int $channel, string $filter, string $q, int $total_count, array &$res): bool
    {
        $offset = 0;
        $limit = 200;
        $has_more = false;
        $cached = false;
        $last_count = -1;
        do {
            try {
                $gres = $this->methodCallAsyncRead('channels.getParticipants', ['channel' => $channel, 'filter' => ['_' => $filter, 'q' => $q], 'offset' => $offset, 'limit' => $limit, 'hash' => $hash = $this->getParticipantsHash($channel, $filter, $q, $offset, $limit), 'floodWaitLimit' => 86400]);
            } catch (ChatAdminRequiredError) {
                $this->logger->logger('CHAT_ADMIN_REQUIRED');
                return $has_more;
            }
            if ($cached = ($gres['_'] === 'channels.channelParticipantsNotModified')) {
                $gres = $this->fetchParticipantsCache($channel, $filter, $q, $offset, $limit);
            } else {
                $this->storeParticipantsCache($gres, $channel, $filter, $q, $offset, $limit);
            }
            \assert($gres !== null);
            if ($last_count !== -1 && $last_count !== $gres['count']) {
                $has_more = true;
            } else {
                $last_count = $gres['count'];
            }
            $promises = [];
            foreach ($gres['participants'] as $participant) {
                $promises []= async(function () use (&$res, $participant): void {
                    $newres = $participant;
                    unset($newres['user_id'], $newres['peer']);
                    $newres['user'] = ($this->getPwrChat($participant['user_id'] ?? $participant['peer'], false));
                    if (isset($participant['inviter_id'])) {
                        unset($newres['inviter_id']);
                        $newres['inviter'] = ($this->getPwrChat($participant['inviter_id'], false));
                    }
                    if (isset($participant['kicked_by'])) {
                        unset($newres['kicked_by']);
                        $newres['kicked_by'] = ($this->getPwrChat($participant['kicked_by'], false));
                    }
                    if (isset($participant['promoted_by'])) {
                        unset($newres['promoted_by']);
                        $newres['promoted_by'] = ($this->getPwrChat($participant['promoted_by'], false));
                    }
                    switch ($participant['_']) {
                        case 'channelParticipantSelf':
                            $newres['role'] = 'user';
                            break;
                        case 'channelParticipant':
                            $newres['role'] = 'user';
                            break;
                        case 'channelParticipantCreator':
                            $newres['role'] = 'creator';
                            break;
                        case 'channelParticipantAdmin':
                            $newres['role'] = 'admin';
                            break;
                        case 'channelParticipantBanned':
                            $newres['role'] = 'banned';
                            break;
                    }
                    $res['participants'][$participant['user_id'] ?? $this->getIdInternal($participant['peer'])] = $newres;
                });
            }
            await($promises);
            $h = $hash !== null ? 'present' : 'absent';
            $this->logger->logger('Fetched '.\count($gres['participants'])." channel participants with filter {$filter}, query {$q}, offset {$offset}, limit {$limit}, hash {$h}: ".($cached ? 'cached' : 'not cached').', '.($offset + \count($gres['participants'])).' participants out of '.$gres['count'].', in total fetched '.\count($res['participants']).' out of '.$total_count);
            $offset += \count($gres['participants']);
        } while (\count($gres['participants']));
        if ($offset === $limit) {
            return true;
        }
        return $has_more;
    }
    /**
     * Key for participatns cache.
     *
     * @psalm-pure
     */
    private static function participantsKey(int $channelId, string $filter, string $q, int $offset, int $limit): string
    {
        return "$channelId'$filter'$q'$offset'$limit";
    }
    private function fetchParticipantsCache(int $channel, string $filter, string $q, int $offset, int $limit): ?array
    {
        return $this->channelParticipants[$this->participantsKey($channel, $filter, $q, $offset, $limit)];
    }
    private function storeParticipantsCache(array $gres, int $channel, string $filter, string $q, int $offset, int $limit): void
    {
        unset($gres['users']);
        $ids = [];
        foreach ($gres['participants'] as $participant) {
            if (isset($participant['user_id'])) {
                $ids[] = $participant['user_id'];
            }
        }
        sort($ids, SORT_NUMERIC);
        /** @psalm-suppress MixedArgumentTypeCoercion Typechecks are done inside */
        $gres['hash'] = Tools::genVectorHash($ids);
        $this->channelParticipants[$this->participantsKey($channel, $filter, $q, $offset, $limit)] = $gres;
    }
    private function getParticipantsHash(int $channel, string $filter, string $q, int $offset, int $limit): ?string
    {
        return $this->fetchParticipantsCache($channel, $filter, $q, $offset, $limit)['hash'] ?? null;
    }
}
<?php

declare(strict_types=1);

/**
 * Password calculator module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\MTProtoTools;

use danog\MadelineProto\Exception;
use danog\MadelineProto\Magic;
use danog\MadelineProto\SecurityException;
use danog\MadelineProto\Tools;
use phpseclib4\Math\BigInteger;

use const STR_PAD_LEFT;

use function hash;

/**
 * Manages SRP password calculation.
 *
 * @author Daniil Gentili <daniil@daniil.it>
 * @link   https://docs.madelineproto.xyz MadelineProto documentation
 */
final class PasswordCalculator
{
    /**
     * The algorithm to use for calculating the hash of new passwords (a PasswordKdfAlgo object).
     *
     */
    private array $new_algo;
    /**
     * A secure random string that can be used to compute the password.
     *
     */
    private string $secure_random;
    /**
     * The algorithm to use for calculatuing the hash of the current password (a PasswordKdfAlgo object).
     *
     */
    private ?array $current_algo;
    /**
     * SRP b parameter.
     *
     */
    private ?BigInteger $srp_B;
    /**
     * SRP b parameter for hashing.
     *
     */
    private ?string $srp_BForHash;
    /**
     * SRP ID.
     */
    private ?int $srp_id = null;
    /**
     * Popupate 2FA configuration.
     *
     * @param array $object 2FA configuration object obtained using account.getPassword
     */
    public function __construct(array $object)
    {
        if ($object['_'] !== 'account.password') {
            throw new Exception('Wrong constructor');
        }
        if ($object['has_password']) {
            switch ($object['current_algo']['_']) {
                case 'passwordKdfAlgoUnknown':
                    throw new Exception('Update your client to continue');
                case 'passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow':
                    $object['current_algo']['g'] = new BigInteger($object['current_algo']['g']);
                    $object['current_algo']['p'] = new BigInteger((string) $object['current_algo']['p'], 256);
                    Crypt::checkPG($object['current_algo']['p'], $object['current_algo']['g']);
                    $object['current_algo']['gForHash'] = str_pad($object['current_algo']['g']->toBytes(), 256, \chr(0), STR_PAD_LEFT);
                    $object['current_algo']['pForHash'] = str_pad($object['current_algo']['p']->toBytes(), 256, \chr(0), STR_PAD_LEFT);
                    $object['current_algo']['salt1'] = (string) $object['current_algo']['salt1'];
                    $object['current_algo']['salt2'] = (string) $object['current_algo']['salt2'];
                    break;
                default:
                    throw new Exception("Unknown KDF algo {$object['current_algo']['_']}");
            }
            $this->current_algo = $object['current_algo'];
            $object['srp_B'] = new BigInteger((string) $object['srp_B'], 256);
            if ($object['srp_B']->compare(Magic::$zero) < 0) {
                throw new SecurityException('srp_B < 0');
            }
            if ($object['srp_B']->compare($object['current_algo']['p']) > 0) {
                throw new SecurityException('srp_B > p');
            }
            $this->srp_B = $object['srp_B'];
            $this->srp_BForHash = str_pad($object['srp_B']->toBytes(), 256, \chr(0), STR_PAD_LEFT);
            $this->srp_id = $object['srp_id'];
        } else {
            $this->current_algo = null;
            $this->srp_B = null;
            $this->srp_BForHash = null;
            $this->srp_id = null;
        }
        switch ($object['new_algo']['_']) {
            case 'passwordKdfAlgoUnknown':
                throw new Exception('Update your client to continue');
            case 'passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow':
                $object['new_algo']['g'] = new BigInteger($object['new_algo']['g']);
                $object['new_algo']['p'] = new BigInteger((string) $object['new_algo']['p'], 256);
                Crypt::checkPG($object['new_algo']['p'], $object['new_algo']['g']);
                $object['new_algo']['gForHash'] = str_pad($object['new_algo']['g']->toBytes(), 256, \chr(0), STR_PAD_LEFT);
                $object['new_algo']['pForHash'] = str_pad($object['new_algo']['p']->toBytes(), 256, \chr(0), STR_PAD_LEFT);
                if (isset($object['current_algo'])) {
                    $object['current_algo']['salt1'] = (string) $object['current_algo']['salt1'];
                    $object['current_algo']['salt2'] = (string) $object['current_algo']['salt2'];
                }
                break;
            default:
                throw new Exception("Unknown KDF algo {$object['new_algo']['_']}");
        }
        $this->new_algo = $object['new_algo'];
        $this->secure_random = (string) $object['secure_random'];
    }
    /**
     * Create a random string (eventually prefixed by the specified string).
     *
     * @param  string $prefix Prefix
     * @return string Salt
     */
    public function createSalt(string $prefix = ''): string
    {
        return $prefix.Tools::random(32);
    }
    /**
     * Hash specified data using the salt with SHA256.
     *
     * The result will be the SHA256 hash of the salt concatenated with the data concatenated with the salt
     *
     * @param string $data Data to hash
     * @param string $salt Salt
     *
     * @return string Hash
     *
     * @psalm-pure
     */
    public function hashSha256(string $data, string $salt): string
    {
        return hash('sha256', $salt.$data.$salt, true);
    }
    /**
     * Hashes the specified password.
     *
     * @param string $password    Password
     * @param string $client_salt Client salt
     * @param string $server_salt Server salt
     *
     * @return string Resulting hash
     *
     * @psalm-mutation-free
     */
    public function hashPassword(string $password, string $client_salt, string $server_salt): string
    {
        $buf = $this->hashSha256($password, $client_salt);
        $buf = $this->hashSha256($buf, $server_salt);
        $hash = hash_pbkdf2('sha512', $buf, $client_salt, 100000, 0, true);
        return $this->hashSha256($hash, $server_salt);
    }
    /**
     * Get the InputCheckPassword object for checking the validity of a password using account.checkPassword.
     *
     * @param  string $password The password
     * @return array  InputCheckPassword object
     */
    public function getCheckPassword(string $password): array
    {
        if ($password === '' || !$this->current_algo) {
            return ['_' => 'inputCheckPasswordEmpty'];
        }
        $client_salt = $this->current_algo['salt1'];
        $server_salt = $this->current_algo['salt2'];
        $g = $this->current_algo['g'];
        $gForHash = $this->current_algo['gForHash'];
        $p = $this->current_algo['p'];
        $pForHash = $this->current_algo['pForHash'];
        $B = $this->srp_B;
        $BForHash = $this->srp_BForHash;
        $id = $this->srp_id;
        $x = new BigInteger($this->hashPassword($password, $client_salt, $server_salt), 256);
        $g_x = $g->powMod($x, $p);
        $k = new BigInteger(hash('sha256', $pForHash.$gForHash, true), 256);
        $kg_x = $k->multiply($g_x)->powMod(Magic::$one, $p);
        $a = new BigInteger(Tools::random(256), 256);
        $A = $g->powMod($a, $p);
        Crypt::checkG($A, $p);
        $AForHash = str_pad($A->toBytes(), 256, \chr(0), STR_PAD_LEFT);
        $b_kg_x = $B->powMod(Magic::$one, $p)->subtract($kg_x);
        $u = new BigInteger(hash('sha256', $AForHash.$BForHash, true), 256);
        $ux = $u->multiply($x);
        $a_ux = $a->add($ux);
        $S = $b_kg_x->powMod($a_ux, $p);
        $SForHash = str_pad($S->toBytes(), 256, \chr(0), STR_PAD_LEFT);
        $K = hash('sha256', $SForHash, true);
        $h1 = hash('sha256', $pForHash, true);
        $h2 = hash('sha256', $gForHash, true);
        $h1 ^= $h2;
        $M1 = hash('sha256', $h1.hash('sha256', $client_salt, true).hash('sha256', $server_salt, true).$AForHash.$BForHash.$K, true);
        return ['_' => 'inputCheckPasswordSRP', 'srp_id' => $id, 'A' => $AForHash, 'M1' => $M1];
    }
    /**
     * Get parameters to be passed to the account.updatePasswordSettings to update/set a 2FA password.
     *
     * The input params array can contain password, new_password, email and hint params.
     *
     * @param  array $params Input params
     * @return array account.updatePasswordSettings parameters
     */
    public function getPassword(array $params): array
    {
        $oldPassword = $this->getCheckPassword($params['password'] ?? '');
        $return = ['password' => $oldPassword, 'new_settings' => ['_' => 'account.passwordInputSettings', 'new_algo' => ['_' => 'passwordKdfAlgoUnknown'], 'new_password_hash' => '', 'hint' => '']];
        $new_settings =& $return['new_settings'];
        if (isset($params['new_password']) && ((string) $params['new_password']) !== '') {
            $client_salt = $this->createSalt((string) $this->new_algo['salt1']);
            $server_salt = (string) $this->new_algo['salt2'];
            $g = $this->new_algo['g'];
            $p = $this->new_algo['p'];
            $pForHash = $this->new_algo['pForHash'];
            $x = new BigInteger($this->hashPassword((string) $params['new_password'], $client_salt, $server_salt), 256);
            $v = $g->powMod($x, $p);
            $vForHash = str_pad($v->toBytes(), 256, \chr(0), STR_PAD_LEFT);
            $new_settings['new_algo'] = ['_' => 'passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow', 'salt1' => $client_salt, 'salt2' => $server_salt, 'g' => (int) $g->toString(), 'p' => $pForHash];
            $new_settings['new_password_hash'] = $vForHash;
            $new_settings['hint'] = $params['hint'] ?? '';
            if (isset($params['email'])) {
                $new_settings['email'] = $params['email'];
            }
        }
        return $return;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\MTProtoTools;

use Amp\Cancellation;
use danog\MadelineProto\DataCenter;
use danog\MadelineProto\Logger;
use phpseclib4\Math\BigInteger;

/**
 * @property DataCenter $datacenter
 *
 * @internal
 */
trait AuthKeyHandler
{
    /**
     * Get diffie-hellman configuration.
     */
    public function getDhConfig(?Cancellation $cancellation = null): array
    {
        $dh_config = $this->methodCallAsyncRead('messages.getDhConfig', ['version' => $this->dh_config['version'], 'random_length' => 0, 'cancellation' => $cancellation]);
        if ($dh_config['_'] === 'messages.dhConfigNotModified') {
            $this->logger->logger('DH configuration not modified', Logger::VERBOSE);
            return $this->dh_config;
        }
        $dh_config['p'] = new BigInteger((string) $dh_config['p'], 256);
        $dh_config['g'] = new BigInteger($dh_config['g']);
        Crypt::checkPG($dh_config['p'], $dh_config['g']);
        return $this->dh_config = $dh_config;
    }
}
<?php

declare(strict_types=1);

/**
 * Response information module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\MTProtoTools;

use Amp\Http\HttpStatus;
use danog\MadelineProto\Lang;

/**
 * Obtain response information for file to server.
 *
 * @internal
 */
final class ResponseInfo
{
    private const NO_CACHE = [
        'Cache-Control' => ['no-store, no-cache, must-revalidate, max-age=0', 'post-check=0, pre-check=0'],
        'Pragma' => 'no-cache',
    ];

    /**
     * Whether to serve file.
     */
    private bool $serve = false;
    /**
     * Serving range.
     */
    private array $serveRange = [];
    /**
     * HTTP response code.
     */
    private int $code = HttpStatus::OK;
    /**
     * Header array.
     *
     * @var array<non-empty-string, string|list<string>>
     */
    private array $headers = [];
    /**
     * Parse headers.
     *
     * @param string    $method       HTTP method
     * @param array     $headers      HTTP headers
     * @param array|int $messageMedia Media info
     *
     * @psalm-mutation-free
     */
    private function __construct(string $method, array $headers, array|int $messageMedia, bool $sandbox = true)
    {
        if (\is_int($messageMedia)) {
            $this->code = $messageMedia;
            $this->serve = false;
            $this->headers = self::NO_CACHE;
            return;
        }
        if (isset($headers['range'])) {
            $range = explode('=', $headers['range'], 2);
            if (\count($range) == 1) {
                $range[1] = '';
            }
            [$size_unit, $range_orig] = $range;
            if ($size_unit == 'bytes') {
                //multiple ranges could be specified at the same time, but for simplicity only serve the first range
                //http://tools.ietf.org/id/draft-ietf-http-range-retrieval-00.txt
                $list = explode(',', $range_orig, 2);
                if (\count($list) == 1) {
                    $list[1] = '';
                }
                [$range, $extra_ranges] = $list;
            } else {
                $this->serve = false;
                $this->code = HttpStatus::RANGE_NOT_SATISFIABLE;
                $this->headers = self::NO_CACHE;
                return;
            }
        } else {
            $range = '';
        }
        $listseek = explode('-', $range, 2);
        if (\count($listseek) == 1) {
            $listseek[1] = '';
        }
        [$seek_start, $seek_end] = $listseek;

        $size = $messageMedia['size'] ?? 0;
        $seek_end = empty($seek_end) ? ($size - 1) : min(abs(\intval($seek_end)), $size - 1);

        if (!empty($seek_start) && $seek_end < abs(\intval($seek_start))) {
            $this->serve = false;
            $this->code = HttpStatus::RANGE_NOT_SATISFIABLE;
            $this->headers = self::NO_CACHE;
            return;
        }
        $seek_start = empty($seek_start) ? 0 : abs(\intval($seek_start));

        $isSafari = !empty($headers['user-agent']) && preg_match('/^((?!chrome|android).)*safari/i', $headers['user-agent']);
        if ($range !== '' && $isSafari) {
            //Safari video streaming fix
            $length = ($seek_end - $seek_start + 1);
            $maxChunkSize = 10 * 1024 ** 2;
            if ($length > $maxChunkSize) {
                $seek_end = $seek_start + $maxChunkSize - 1;
            }
        }

        $this->serve = $method !== 'HEAD';
        if ($seek_start > 0 || $seek_end < $size - 1) {
            $this->code = HttpStatus::PARTIAL_CONTENT;
            $this->headers['Content-Range'] = "bytes $seek_start-$seek_end/$size";
            $this->headers['Content-Length'] = (string) ($seek_end - $seek_start + 1);
        } elseif ($size > 0) {
            $this->headers['Content-Length'] = (string) $size;
        }
        if ($sandbox) {
            // The MIME type and the name come from whoever sent the file on Telegram (or from the query string of the
            // download script): a file opened as a page can't run scripts nor access the origin serving it, and the
            // browser must not guess a more dangerous type (see Settings\Files::setSandboxDownloads()).
            $mime = self::normalizeMime((string) $messageMedia['mime']);
            $this->headers['Content-Security-Policy'] = 'sandbox';
            $this->headers['X-Content-Type-Options'] = 'nosniff';
        } else {
            $mime = (string) $messageMedia['mime'];
        }
        $this->headers['Content-Type'] = $mime;
        $this->headers['Cache-Control'] = 'max-age=31556926';
        $this->headers['Content-Transfer-Encoding'] = 'Binary';
        $this->headers['Accept-Ranges'] = 'bytes';

        if ($this->serve) {
            if ($seek_start === 0 && $seek_end === -1) {
                $this->serveRange = [0, -1];
            } else {
                $this->serveRange = [$seek_start, $seek_end + 1];
            }

            // Only media is shown by the browser, the rest is downloaded (for the browsers ignoring the sandbox).
            $disposition = !$sandbox || self::isInlineSafe($mime) ? 'inline' : 'attachment';
            if (!empty($messageMedia['name']) && !empty($messageMedia['ext'])) {
                $this->headers['Content-Disposition'] = $disposition.self::filenameParameters($messageMedia['name'].$messageMedia['ext']);
            } elseif ($disposition === 'attachment') {
                $this->headers['Content-Disposition'] = $disposition;
            }
        }
    }
    /**
     * A single type/subtype, lowercase and without parameters, or application/octet-stream: so that the browser
     * can't read the type differently than isInlineSafe().
     */
    private static function normalizeMime(string $mime): string
    {
        $mime = strtolower(trim(explode(';', $mime, 2)[0]));
        return preg_match('~^[a-z0-9!#$&^_.+-]+/[a-z0-9!#$&^_.+-]+$~D', $mime) ? $mime : 'application/octet-stream';
    }

    /**
     * Whether the browser can show this (normalized) MIME type, rather than downloading it: media that can't run
     * scripts (not SVG), and plain text.
     */
    private static function isInlineSafe(string $mime): bool
    {
        return $mime === 'text/plain'
            || preg_match('~^(video|audio)/~', $mime)
            || \in_array($mime, ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/avif', 'image/bmp', 'image/apng', 'image/x-icon', 'image/vnd.microsoft.icon'], true);
    }

    /**
     * The filename parameters of Content-Disposition: a quoted ASCII fallback without quotes, backslashes nor
     * control characters, and the name itself encoded as in RFC 6266.
     */
    private static function filenameParameters(string $name): string
    {
        $ascii = preg_replace('/[^\x20-\x7e]|["\\\\]/', '_', $name);
        return "; filename=\"$ascii\"; filename*=UTF-8''".rawurlencode($name);
    }

    /**
     * Parse headers.
     *
     * @param string $method       HTTP method
     * @param array  $headers      HTTP headers
     * @param array  $messageMedia Media info
     * @param bool   $sandbox      Whether the file can't run as a page of the website serving it (see Settings\Files::setSandboxDownloads())
     *
     * @psalm-pure
     */
    public static function parseHeaders(string $method, array $headers, array $messageMedia, bool $sandbox = true): self
    {
        return new self($method, $headers, $messageMedia, $sandbox);
    }
    /**
     * @psalm-pure
     */
    public static function error(int $code): self
    {
        return new self('', [], $code);
    }
    /**
     * Get explanation for HTTP code.
     */
    public function getCodeExplanation(): string
    {
        $reason = HttpStatus::getReason($this->code);
        $body = "<html lang='en'><body><h1>{$this->code} $reason</h1>";
        if ($this->code === HttpStatus::RANGE_NOT_SATISFIABLE) {
            $body .= '<p>Could not use selected range.</p>';
        }
        if ($this->code === HttpStatus::BAD_GATEWAY) {
            $body .= "<h2 style='color:red;'>".Lang::$current_lang["dl.php_check_logs_make_sure_session_running"].'</h2>';
        }
        $body .= '<small>'.Lang::$current_lang["dl.php_powered_by_madelineproto"].'</small>';
        $body .= '</body></html>';
        return $body;
    }

    /**
     * Whether to serve file.
     *
     * @return bool Whether to serve file
     */
    public function shouldServe(): bool
    {
        return $this->serve;
    }

    /**
     * Get serving range.
     *
     * @return array HTTP serving range
     */
    public function getServeRange(): array
    {
        return $this->serveRange;
    }

    /**
     * Get HTTP response code.
     *
     * @return int HTTP response code
     */
    public function getCode(): int
    {
        return $this->code;
    }

    /**
     * Get header array.
     *
     * @return array<non-empty-string, string|list<string>> Header array
     */
    public function getHeaders(): array
    {
        return $this->headers;
    }

    /**
     * Write headers.
     */
    public function writeHeaders(): void
    {
        http_response_code($this->getCode());
        foreach ($this->getHeaders() as $key => $value) {
            if (\is_array($value)) {
                foreach ($value as $subValue) {
                    header("$key: $subValue", false);
                }
            } else {
                header("$key: $value");
            }
        }
    }
}
<?php

declare(strict_types=1);

/**
 * UpdateHandler module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\MTProtoTools;

use Amp\Cancellation;
use Amp\CancelledException;
use Amp\CompositeCancellation;
use Amp\DeferredCancellation;
use Amp\DeferredFuture;
use Amp\Future;
use Amp\Http\Client\Request;
use Amp\Http\Client\Response;
use Amp\TimeoutException;
use AssertionError;
use danog\AsyncOrm\Annotations\OrmMappedArray;
use danog\AsyncOrm\DbArray;
use danog\AsyncOrm\KeyType;
use danog\AsyncOrm\ValueType;
use danog\BetterPrometheus\BetterCounter;
use danog\DialogId\DialogId;
use danog\MadelineProto\API;
use danog\MadelineProto\EventHandler\AbstractMessage;
use danog\MadelineProto\EventHandler\BotCommands;
use danog\MadelineProto\EventHandler\Calls\AbstractGroupCall;
use danog\MadelineProto\EventHandler\Calls\CallStreams;
use danog\MadelineProto\EventHandler\Calls\ConferenceCall as ConferenceCallUpdate;
use danog\MadelineProto\EventHandler\Calls\GroupCall as GroupCallUpdate;
use danog\MadelineProto\EventHandler\Calls\GroupCallMessagesDeleted;
use danog\MadelineProto\EventHandler\Calls\GroupCallParticipants;
use danog\MadelineProto\EventHandler\Calls\LiveStory;
use danog\MadelineProto\EventHandler\Calls\MultiCallMessage;
use danog\MadelineProto\EventHandler\Channel\ChannelParticipant;
use danog\MadelineProto\EventHandler\Channel\MessageForwards;
use danog\MadelineProto\EventHandler\Channel\MessageViewsChanged;
use danog\MadelineProto\EventHandler\Channel\UpdateChannel;
use danog\MadelineProto\EventHandler\ChatInviteRequester\BotChatInviteRequest;
use danog\MadelineProto\EventHandler\ChatInviteRequester\PendingJoinRequests;
use danog\MadelineProto\EventHandler\Delete\DeleteChannelMessages;
use danog\MadelineProto\EventHandler\Delete\DeleteMessages;
use danog\MadelineProto\EventHandler\Delete\DeleteScheduledMessages;
use danog\MadelineProto\EventHandler\InlineQuery;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Message\ChannelMessage;
use danog\MadelineProto\EventHandler\Message\CommentReply;
use danog\MadelineProto\EventHandler\Message\Entities\MessageEntity;
use danog\MadelineProto\EventHandler\Message\Entities\TextWithEntities;
use danog\MadelineProto\EventHandler\Message\GroupMessage;
use danog\MadelineProto\EventHandler\Message\PrivateMessage;
use danog\MadelineProto\EventHandler\Message\SecretMessage;
use danog\MadelineProto\EventHandler\Message\Service\DialogBotAllowed;
use danog\MadelineProto\EventHandler\Message\Service\DialogChannelCreated;
use danog\MadelineProto\EventHandler\Message\Service\DialogChannelMigrateFrom;
use danog\MadelineProto\EventHandler\Message\Service\DialogChatJoinedByLink;
use danog\MadelineProto\EventHandler\Message\Service\DialogChatMigrateTo;
use danog\MadelineProto\EventHandler\Message\Service\DialogConferenceCall;
use danog\MadelineProto\EventHandler\Message\Service\DialogContactSignUp;
use danog\MadelineProto\EventHandler\Message\Service\DialogCreated;
use danog\MadelineProto\EventHandler\Message\Service\DialogDeleteMessages;
use danog\MadelineProto\EventHandler\Message\Service\DialogGameScore;
use danog\MadelineProto\EventHandler\Message\Service\DialogGeoProximityReached;
use danog\MadelineProto\EventHandler\Message\Service\DialogGiftPremium;
use danog\MadelineProto\EventHandler\Message\Service\DialogGiftStars;
use danog\MadelineProto\EventHandler\Message\Service\DialogGroupCall\GroupCall;
use danog\MadelineProto\EventHandler\Message\Service\DialogGroupCall\GroupCallInvited;
use danog\MadelineProto\EventHandler\Message\Service\DialogGroupCall\GroupCallScheduled;
use danog\MadelineProto\EventHandler\Message\Service\DialogHistoryCleared;
use danog\MadelineProto\EventHandler\Message\Service\DialogMemberJoinedByRequest;
use danog\MadelineProto\EventHandler\Message\Service\DialogMemberLeft;
use danog\MadelineProto\EventHandler\Message\Service\DialogMembersJoined;
use danog\MadelineProto\EventHandler\Message\Service\DialogMessagePinned;
use danog\MadelineProto\EventHandler\Message\Service\DialogNoForwardRequest;
use danog\MadelineProto\EventHandler\Message\Service\DialogNoForwardToggle;
use danog\MadelineProto\EventHandler\Message\Service\DialogPaymentSent;
use danog\MadelineProto\EventHandler\Message\Service\DialogPaymentSentMe;
use danog\MadelineProto\EventHandler\Message\Service\DialogPeerRequested;
use danog\MadelineProto\EventHandler\Message\Service\DialogPhoneCall;
use danog\MadelineProto\EventHandler\Message\Service\DialogPhotoChanged;
use danog\MadelineProto\EventHandler\Message\Service\DialogReadMessages;
use danog\MadelineProto\EventHandler\Message\Service\DialogScreenshotTaken;
use danog\MadelineProto\EventHandler\Message\Service\DialogSetChatTheme;
use danog\MadelineProto\EventHandler\Message\Service\DialogSetChatWallPaper;
use danog\MadelineProto\EventHandler\Message\Service\DialogSetTTL;
use danog\MadelineProto\EventHandler\Message\Service\DialogStarGift;
use danog\MadelineProto\EventHandler\Message\Service\DialogSuggestProfilePhoto;
use danog\MadelineProto\EventHandler\Message\Service\DialogTitleChanged;
use danog\MadelineProto\EventHandler\Message\Service\DialogTopicCreated;
use danog\MadelineProto\EventHandler\Message\Service\DialogTopicEdited;
use danog\MadelineProto\EventHandler\Message\Service\DialogWebView;
use danog\MadelineProto\EventHandler\Payments\Payment;
use danog\MadelineProto\EventHandler\Payments\PaymentCharge;
use danog\MadelineProto\EventHandler\Payments\PaymentRequestedInfo;
use danog\MadelineProto\EventHandler\Payments\StarGift;
use danog\MadelineProto\EventHandler\Pinned;
use danog\MadelineProto\EventHandler\Pinned\PinnedChannelMessages;
use danog\MadelineProto\EventHandler\Pinned\PinnedGroupMessages;
use danog\MadelineProto\EventHandler\Pinned\PinnedPrivateMessages;
use danog\MadelineProto\EventHandler\Privacy;
use danog\MadelineProto\EventHandler\Query\ChatButtonQuery;
use danog\MadelineProto\EventHandler\Query\ChatGameQuery;
use danog\MadelineProto\EventHandler\Query\InlineButtonQuery;
use danog\MadelineProto\EventHandler\Query\InlineGameQuery;
use danog\MadelineProto\EventHandler\Story\Story;
use danog\MadelineProto\EventHandler\Story\StoryDeleted;
use danog\MadelineProto\EventHandler\Story\StoryReaction;
use danog\MadelineProto\EventHandler\Typing\ChatUserTyping;
use danog\MadelineProto\EventHandler\Typing\SecretUserTyping;
use danog\MadelineProto\EventHandler\Typing\SupergroupUserTyping;
use danog\MadelineProto\EventHandler\Typing\UserTyping;
use danog\MadelineProto\EventHandler\Update;
use danog\MadelineProto\EventHandler\User\Blocked;
use danog\MadelineProto\EventHandler\User\BotStopped;
use danog\MadelineProto\EventHandler\User\Phone;
use danog\MadelineProto\EventHandler\User\Status;
use danog\MadelineProto\EventHandler\User\Status\Emoji;
use danog\MadelineProto\EventHandler\User\Username;
use danog\MadelineProto\EventHandler\Wallpaper;
use danog\MadelineProto\Exception;
use danog\MadelineProto\Lang;
use danog\MadelineProto\Logger;
use danog\MadelineProto\Loop\Update\FeedLoop;
use danog\MadelineProto\Loop\Update\UpdateLoop;
use danog\MadelineProto\MTProto\SpecialMethodType;
use danog\MadelineProto\ParseMode;
use danog\MadelineProto\PeerNotInDbException;
use danog\MadelineProto\ResponseException;
use danog\MadelineProto\RPCError\ChannelPrivateError;
use danog\MadelineProto\RPCError\FloodWaitError;
use danog\MadelineProto\RPCError\MsgIdInvalidError;
use danog\MadelineProto\RPCError\SessionPasswordNeededError;
use danog\MadelineProto\RPCErrorException;
use danog\MadelineProto\Settings;
use danog\MadelineProto\Tgcalls\PrivateCallController;
use danog\MadelineProto\TL\TL;
use danog\MadelineProto\UpdateHandlerType;
use danog\MadelineProto\VoIP\DiscardReason;
use Revolt\EventLoop;
use SplQueue;
use Throwable;
use Webmozart\Assert\Assert;
use function Amp\delay;

/**
 * Manages updates.
 *
 * @property Settings $settings Settings
 * @property TL       $TL       TL
 *
 * @internal
 */
trait UpdateHandler
{
    private UpdateHandlerType $updateHandlerType = UpdateHandlerType::NOOP;

    private bool $got_state = false;
    /** @deprecated */
    private CombinedUpdatesState $channels_state;
    private CombinedUpdatesState $updateState;
    /** @var DbArray<int, array> */
    #[OrmMappedArray(KeyType::INT, ValueType::SCALAR)]
    private $getUpdatesQueue;
    private int $getUpdatesQueueKey = 0;
    private SplQueue $updateQueue;
    /** Whether the updates postponed while the event handler is starting will be handled once it started. */
    private bool $updateQueueReplay = false;

    /**
     * Set NOOP update handler, ignoring all updates.
     */
    public function setNoop(): void
    {
        $this->updateHandlerType = UpdateHandlerType::NOOP;
        $this->getUpdatesQueue->clear();
        $this->getUpdatesQueueKey = 0;
        $q = new SplQueue;
        $q->setIteratorMode(SplQueue::IT_MODE_DELETE);
        $this->updateQueue = $q;
        $this->event_handler = null;
        $this->event_handler_instance = null;
        $this->eventHandlerMethods = [];
        $this->eventHandlerHandlers = [];
        $this->pluginInstances = [];
    }
    /**
     * PWRTelegram webhook URL.
     */
    private ?string $webhookUrl = null;
    /**
     * Set webhook update handler.
     *
     * @param string $webhookUrl Webhook URL
     */
    public function setWebhook(string $webhookUrl): void
    {
        $this->webhookUrl = $webhookUrl;
        $this->updateHandlerType = UpdateHandlerType::WEBHOOK;
        foreach ($this->getUpdatesQueue as $update) {
            $this->handleUpdate($update);
        }
        $this->getUpdatesQueue->clear();
        $this->getUpdatesQueueKey = 0;
        foreach ($this->updateQueue as $update) {
            $this->handleUpdate($update);
        }
        $this->event_handler = null;
        $this->event_handler_instance = null;
        $this->eventHandlerMethods = [];
        $this->eventHandlerHandlers = [];
        $this->pluginInstances = [];
    }

    /**
     * Event update handler.
     *
     * @param array $update Update
     */
    private function eventUpdateHandler(array $update): void
    {
        $updateType = $update['_'];
        if ($f = $this->event_handler_instance->waitForInternalStart()) {
            $this->logger->logger("Postponing update handling, onStart is still running (if stuck here for too long, make sure to fork long-running tasks in onStart using \$this->callFork(function () { ... }) to fix this)...", Logger::NOTICE);
            $this->updateQueue->enqueue($update);
            $this->handlePostponedUpdates($f);
            return;
        }
        if (\count($this->eventHandlerHandlers) !== 0 && \is_array($update)) {
            $obj = $this->wrapUpdate($update);
            if ($obj !== null) {
                foreach ($this->eventHandlerHandlers as $closure) {
                    EventLoop::queue($this->rethrowHandler, $closure, $obj);
                }
            }
        }
        if ($updateType === 'updateBroadcastProgress') {
            $update = $update['progress'];
        }
        if (isset($this->eventHandlerMethods[$updateType])) {
            foreach ($this->eventHandlerMethods[$updateType] as $closure) {
                EventLoop::queue($this->rethrowHandler, $closure, $update);
            }
        }
    }

    private function rethrowUpdateHandler(\Closure $closure, mixed $update): void
    {
        try {
            $closure($update);
        } catch (\Throwable $e) {
            $this->rethrowInner($e);
        }
    }

    /**
     * Send update to webhook.
     *
     * @param array $update Update
     */
    private function pwrWebhook(array $update): void
    {
        $payload = json_encode($update);
        Assert::notEmpty($payload);
        Assert::notNull($this->webhookUrl);
        $request = new Request($this->webhookUrl, 'POST');
        $request->setHeader('content-type', 'application/json');
        $request->setBody($payload);
        try {
            /** @var Response */
            $result = $this->datacenter->getHTTPClient()->request($request);
            $status = $result->getStatus();
            if ($status !== 200) {
                throw new Exception("Got bad status $status!");
            }
        } catch (Throwable $e) {
            $this->logger->logger("Got {$e->getMessage()} while sending webhook", Logger::FATAL_ERROR);
            $this->updateQueue->enqueue($update);
            delay(1.0);
            foreach ($this->updateQueue as $update) {
                $this->handleUpdate($update);
            }
        }
    }

    private ?DeferredFuture $update_deferred = null;
    /**
     * Signal update.
     *
     * @internal
     */
    private function addGetUpdatesUpdate($update): void
    {
        $this->getUpdatesQueue[$this->getUpdatesQueueKey++] = $update;
        if ($this->update_deferred) {
            $deferred = $this->update_deferred;
            $this->update_deferred = null;
            $deferred->complete();
        }
    }

    private bool $usingGetUpdates = false;
    private static ?TimeoutException $getUpdatesTimeout = null;
    /**
     * Only useful when consuming MadelineProto updates through an API in another language (like Javascript), **absolutely not recommended when directly writing MadelineProto bots**.
     *
     * `getUpdates` will **greatly slow down your bot** if used directly inside of PHP code.
     *
     * **Only use the [event handler](#async-event-driven) when writing a MadelineProto bot**, because update handling in the **event handler** is completely parallelized and non-blocking.
     *
     * @param  array{offset?: int, limit?: int, timeout?: float} $params       Params: once the timeout expires, an empty array is returned
     * @param  ?Cancellation                                     $cancellation Cancellation: once cancelled, the wait stops with a CancelledException
     * @return list<array{update_id: mixed, update: mixed}>
     */
    public function getUpdates(array $params = [], ?Cancellation $cancellation = null): array
    {
        if ($this->usingGetUpdates) {
            throw new AssertionError("Concurrent getUpdates detected, aborting!");
        }
        $id = null;
        try {
            $this->usingGetUpdates = true;
            if ($this->updateHandlerType !== UpdateHandlerType::GET_UPDATES) {
                $this->updateHandlerType = UpdateHandlerType::GET_UPDATES;
                $this->event_handler = null;
                $this->event_handler_instance = null;
                $this->eventHandlerMethods = [];
                $this->eventHandlerHandlers = [];
                $this->pluginInstances = [];
            }
            foreach ($this->updateQueue as $update) {
                $this->addGetUpdatesUpdate($update);
            }

            [
                'offset' => $offset,
                'limit' => $limit,
                'timeout' => $timeout
            ] = array_merge(['offset' => 0, 'limit' => null, 'timeout' => 10.0], $params);

            if ($offset < 0) {
                $offset = $this->getUpdatesQueueKey+$offset;
            }

            Assert::nullOrPositiveInteger($limit);

            if ($timeout === INF) {
                $timeout = null;
            } elseif ($timeout !== null) {
                self::$getUpdatesTimeout ??= new TimeoutException("Operation timed out");
                $deferred = new DeferredCancellation;
                $id = EventLoop::delay($timeout, static fn () => $deferred->cancel(self::$getUpdatesTimeout));
                $timeout = $deferred->getCancellation();
            }
            $wait = $timeout !== null && $cancellation !== null
                ? new CompositeCancellation($timeout, $cancellation)
                : $timeout ?? $cancellation;

            do {
                if (!$this->getUpdatesQueue->count()) {
                    try {
                        $this->update_deferred = new DeferredFuture();
                        $this->update_deferred->getFuture()->await($wait);
                    } catch (CancelledException $e) {
                        // Only our own timeout returns what was received so far: a cancellation of the caller
                        // (even one by a TimeoutCancellation of its own) is thrown.
                        if ($cancellation?->isRequested() || !$timeout?->isRequested()) {
                            throw $e;
                        }
                    }
                }
                $updates = [];
                foreach ($this->getUpdatesQueue as $key => $value) {
                    if ($offset > $key) {
                        unset($this->getUpdatesQueue[$key]);
                        continue;
                    }

                    $updates[$key] = ['update_id' => $key, 'update' => $value];

                    if ($limit !== null && \count($updates) === $limit) {
                        break;
                    }
                }
                if ($updates) {
                    ksort($updates);
                    return array_values($updates);
                }
            } while (!$timeout?->isRequested());
            return [];
        } finally {
            $this->usingGetUpdates = false;
            if ($id !== null && !$timeout->isRequested()) {
                EventLoop::cancel($id);
            }
        }
    }
    /**
     * Check message ID.
     *
     * @param array $message Message
     *
     * @internal
     *
     * @psalm-external-mutation-free
     */
    public function checkMsgId(array $message): bool
    {
        if (!isset($message['peer_id'])) {
            return true;
        }
        $peer_id = $message['peer_id'];
        $message_id = $message['id'];
        if (!isset($this->msg_ids[$peer_id]) || $message_id > $this->msg_ids[$peer_id]) {
            $this->msg_ids[$peer_id] = $message_id;
            return true;
        }
        return false;
    }
    /**
     * Get channel state.
     *
     * @internal
     */
    public function loadUpdateState()
    {
        if (!$this->got_state) {
            $this->updateState->get(0, $this->getUpdatesState());
            $this->got_state = true;
        }
        return $this->updateState->get(0);
    }
    /**
     * Load channel state.
     *
     * @param null|int $channelId Channel ID
     * @param array    $init      Init
     *
     * @internal
     *
     * @return UpdatesState|array<UpdatesState>
     *
     * @psalm-external-mutation-free
     */
    public function loadChannelState(?int $channelId = null, array $init = []): UpdatesState|array
    {
        return $this->updateState->get($channelId, $init);
    }
    /**
     * Get channel states.
     *
     * @internal
     */
    public function getChannelStates(): CombinedUpdatesState
    {
        return $this->updateState;
    }
    /**
     * Get update state.
     *
     * @internal
     */
    private function getUpdatesState()
    {
        $data = $this->methodCallAsyncRead('updates.getState', []);
        $this->getCdnConfig();
        return $data;
    }
    /**
     * Wrap an Update constructor into an abstract Update object.
     */
    public function wrapUpdate(array $update): ?Update
    {
        try {
            return match ($update['_']) {
                'updateNewChannelMessage', 'updateNewMessage', 'updateNewScheduledMessage', 'updateEditMessage', 'updateEditChannelMessage','updateNewEncryptedMessage','updateNewOutgoingEncryptedMessage' => $this->wrapMessage($update['message'], $update['_'] === 'updateNewScheduledMessage'),
                'updatePinnedMessages', 'updatePinnedChannelMessages' => $this->wrapPin($update),
                'updateBotCallbackQuery' => isset($update['game_short_name'])
                    ? new ChatGameQuery($this, $update)
                    : new ChatButtonQuery($this, $update),
                'updateInlineBotCallbackQuery' => isset($update['game_short_name'])
                    ? new InlineGameQuery($this, $update)
                    : new InlineButtonQuery($this, $update),
                'updateBotInlineQuery' => new InlineQuery($this, $update),
                'updatePhoneCall' => $update['phone_call'],
                'updateGroupCall' => $this->wrapGroupCall($update),
                'updateGroupCallParticipants' => new GroupCallParticipants($this, $update),
                'updateGroupCallMessage' => MultiCallMessage::fromUpdate($this, $update),
                'updateDeleteGroupCallMessages' => new GroupCallMessagesDeleted($this, $update),
                'updateCallStreams' => new CallStreams($this, $update),
                'updateBroadcastProgress' => $update['progress'],
                'updateStory' => $update['story']['_'] === 'storyItemDeleted'
                    ? new StoryDeleted($this, $update)
                    : new Story($this, $update),
                'updateSentStoryReaction'       => new StoryReaction($this, $update),
                'updateUserStatus'              => Status::fromRawStatus($this, $update),
                'updatePeerBlocked'             => new Blocked($this, $update),
                'updateBotStopped'              => new BotStopped($this, $update),
                'updateUserEmojiStatus'         => new Emoji($this, $update),
                'updateUserName'                => new Username($this, $update),
                'updateUserPhone'               => new Phone($this, $update),
                'updatePrivacy'                 => new Privacy($this, $update),
                'updateUserTyping'              => new UserTyping($this, $update),
                'updateChannelUserTyping'       => new SupergroupUserTyping($this, $update),
                'updateChatUserTyping'          => new ChatUserTyping($this, $update),
                'updateChannel'                 => new UpdateChannel($this, $update),
                'updateChannelMessageViews'     => new MessageViewsChanged($this, $update),
                'updateChannelMessageForwards'  => new MessageForwards($this, $update),
                'updateChannelParticipant'      => new ChannelParticipant($this, $update),
                'updateDeleteMessages'          => new DeleteMessages($this, $update),
                'updateDeleteChannelMessages'   => new DeleteChannelMessages($this, $update),
                'updateDeleteScheduledMessages' => new DeleteScheduledMessages($this, $update),
                'updatePendingJoinRequests'     => new PendingJoinRequests($this, $update),
                'updateBotChatInviteRequester'  => new BotChatInviteRequest($this, $update),
                'updateBotCommands'             => new BotCommands($this, $update),
                'updateBotPrecheckoutQuery'     => new Payment($this, $update),
                default => null
            };
        } catch (\Throwable $e) {
            $update = json_encode($update);
            $this->logger->logger("An error occured while wrapping $update: $e", Logger::FATAL_ERROR);
            $this->report("An error occured while wrapping $update: $e");
            return null;
        }
    }
    /**
     * The handle of the call an updateGroupCall is about: the one we track, or else a fresh one built
     * from the groupCall the update carries (a conference handle for a conference).
     */
    private function wrapGroupCall(array $update): AbstractGroupCall|ConferenceCallUpdate
    {
        $call = $update['call'];
        $id = $call['id'];
        if (isset($this->conferenceCalls[$id])) {
            return $this->conferenceCalls[$id]->getPublic();
        }
        if (isset($this->groupCalls[$id])) {
            return $this->groupCalls[$id]->public;
        }
        if ($call['conference'] ?? false) {
            return new ConferenceCallUpdate($this, $call);
        }
        $peerId = isset($update['peer']) ? $this->getIdInternal($update['peer']) : null;
        return ($update['live_story'] ?? false)
            ? new LiveStory($this, $call, $peerId)
            : new GroupCallUpdate($this, $call, $peerId);
    }

    /**
     * Wrap a Pin constructor into an abstract Pinned object.
     */
    public function wrapPin(array $message): ?Pinned
    {
        return match ($this->getInfo($message)['type']) {
            API::PEER_TYPE_BOT, API::PEER_TYPE_USER => new PinnedPrivateMessages($this, $message),
            API::PEER_TYPE_GROUP, API::PEER_TYPE_SUPERGROUP => new PinnedGroupMessages($this, $message),
            API::PEER_TYPE_CHANNEL => new PinnedChannelMessages($this, $message),
            default => null,
        };
    }
    /**
     * Wrap a Message constructor into an abstract Message object.
     */
    public function wrapMessage(array $message, bool $scheduled = false): ?AbstractMessage
    {
        if ($message['_'] === 'messageEmpty') {
            return null;
        }
        $info = $this->getInfo($message);
        if ($message['_'] === 'messageService') {
            return match ($message['action']['_']) {
                'messageActionBotAllowed' => new DialogBotAllowed(
                    $this,
                    $message,
                    $info,
                ),
                'messageActionHistoryClear' => new DialogHistoryCleared(
                    $this,
                    $message,
                    $info,
                ),
                'messageActionContactSignUp' => new DialogContactSignUp(
                    $this,
                    $message,
                    $info,
                ),
                'messageActionChatJoinedByRequest' => new DialogMemberJoinedByRequest(
                    $this,
                    $message,
                    $info,
                ),
                'messageActionChatAddUser' => new DialogMembersJoined(
                    $this,
                    $message,
                    $info,
                    $message['action']['users']
                ),
                'messageActionChatDeleteUser' => new DialogMemberLeft(
                    $this,
                    $message,
                    $info,
                    $message['action']['user_id']
                ),

                'messageActionChatJoinedByLink' => new DialogChatJoinedByLink(
                    $this,
                    $message,
                    $info,
                    $message['action']['inviter_id'],
                ),
                'messageActionChatCreate' => new DialogCreated(
                    $this,
                    $message,
                    $info,
                    $message['action']['title'],
                    $message['action']['users'],
                ),
                'messageActionChatEditTitle' => new DialogTitleChanged(
                    $this,
                    $message,
                    $info,
                    $message['action']['title']
                ),
                'messageActionChatEditPhoto' => new DialogPhotoChanged(
                    $this,
                    $message,
                    $info,
                    $this->wrapMedia($message['action']['photo'])
                ),
                'messageActionChatDeletePhoto' => new DialogPhotoChanged(
                    $this,
                    $message,
                    $info,
                    null
                ),
                'messageActionPinMessage' => new DialogMessagePinned(
                    $this,
                    $message,
                    $info,
                ),
                'messageActionScreenshotTaken' => new DialogScreenshotTaken(
                    $this,
                    $message,
                    $info,
                ),
                'messageActionChannelCreate' => new DialogChannelCreated(
                    $this,
                    $message,
                    $info,
                    $message['action']['title'],
                ),
                'messageActionChatMigrateTo' => new DialogChatMigrateTo(
                    $this,
                    $message,
                    $info,
                    $message['action']['channel_id'],
                ),
                'messageActionChannelMigrateFrom' => new DialogChannelMigrateFrom(
                    $this,
                    $message,
                    $info,
                    $message['action']['title'],
                    $message['action']['chat_id'],
                ),
                'messageActionGameScore' => new DialogGameScore(
                    $this,
                    $message,
                    $info,
                    $message['action']['game_id'],
                    $message['action']['score'],
                ),
                'messageActionGeoProximityReached' => new DialogGeoProximityReached(
                    $this,
                    $message,
                    $info,
                    $this->getIdInternal($message['action']['from_id']),
                    $this->getIdInternal($message['action']['to_id']),
                    $message['action']['distance'],
                ),
                'messageActionPhoneCall' => new DialogPhoneCall(
                    $this,
                    $message,
                    $info,
                    $message['action']['video'],
                    $message['action']['call_id'],
                    DiscardReason::tryfrom($message['action']['reason']['_'] ?? ''),
                    $message['action']['duration'] ?? null,
                ),
                'messageActionGroupCall' => new GroupCall(
                    $this,
                    $message,
                    $info,
                    $message['action']['duration'] ?? null,
                ),
                'messageActionConferenceCall' => new DialogConferenceCall(
                    $this,
                    $message,
                    $info,
                    $message['action']['call_id'],
                    $message['action']['missed'] ?? false,
                    $message['action']['active'] ?? false,
                    $message['action']['video'] ?? false,
                    $message['action']['duration'] ?? null,
                    array_values(array_filter(array_map($this->getIdInternal(...), $message['action']['other_participants'] ?? []))),
                ),
                'messageActionInviteToGroupCall' => new GroupCallInvited(
                    $this,
                    $message,
                    $info,
                    $message['action']['users'],
                ),
                'messageActionGroupCallScheduled' => new GroupCallScheduled(
                    $this,
                    $message,
                    $info,
                    $message['action']['schedule_date'],
                ),
                'messageActionSetMessagesTTL' => new DialogSetTTL(
                    $this,
                    $message,
                    $info,
                    $message['action']['period'],
                    $message['action']['auto_setting_from'] ?? null,
                ),
                'messageActionSetChatTheme' => new DialogSetChatTheme(
                    $this,
                    $message,
                    $info,
                    $message['action']['theme'],
                ),
                'messageActionWebViewDataSentMe' => new DialogWebView(
                    $this,
                    $message,
                    $info,
                    $message['action']['text'],
                    $message['action']['data'],
                ),
                'messageActionWebViewDataSent' => new DialogWebView(
                    $this,
                    $message,
                    $info,
                    $message['action']['text'],
                    null
                ),
                'messageActionGiftStars' => new DialogGiftStars(
                    $this,
                    $message,
                    $info,
                    $message['action']['currency'],
                    $message['action']['amount'],
                    $message['action']['stars'],
                    $message['action']['crypto_currency'] ?? null,
                    $message['action']['crypto_amount'] ?? null,
                    $message['action']['transaction_id'],
                ),
                'messageActionStarGift' => new DialogStarGift(
                    $this,
                    $message,
                    $info,
                    $message['action']['name_hidden'] ?? null,
                    $message['action']['saved'] ?? null,
                    $message['action']['converted'] ?? null,
                    new StarGift(
                        $this,
                        $message['action']['gift']
                    ),
                    isset($message['action']['message']) ? new TextWithEntities(
                        $message['action']['message']['text'],
                        MessageEntity::fromRawEntities($message['action']['message']['entities']),
                        isset($message['action']['message']['parse_mode']) ?
                            ParseMode::from($message['action']['message']['parse_mode'])
                            : null
                    ) : null,
                    $message['action']['convert_stars'],
                ),
                'messageActionPaymentSent' => new DialogPaymentSent(
                    $this,
                    $message,
                    $info,
                    $message['action']['recurring_init'] ?? null,
                    $message['action']['recurring_used'] ?? null,
                    $message['action']['currency'],
                    $message['action']['total_amount'],
                    $message['action']['invoice_slug'] ?? null,
                    $message['action']['subscription_until_date'] ?? null
                ),
                'messageActionPaymentSentMe' => new DialogPaymentSentMe(
                    $this,
                    $message,
                    $info,
                    $message['action']['recurring_init'] ?? null,
                    $message['action']['recurring_used'] ?? null,
                    $message['action']['currency'],
                    $message['action']['total_amount'],
                    $message['action']['payload'],
                    isset($message['action']['info'])
                        ? PaymentRequestedInfo::fromRawInfo($message['action']['info'])
                        : null,
                    $message['action']['shipping_option_id'] ?? null,
                    new PaymentCharge(
                        $message['action']['charge']['id'],
                        $message['action']['charge']['provider_charge_id']
                    ),
                    $message['action']['subscription_until_date'] ?? null
                ),
                'messageActionGiftPremium' => new DialogGiftPremium(
                    $this,
                    $message,
                    $info,
                    $message['action']['currency'],
                    $message['action']['amount'],
                    $message['action']['months'],
                    $message['action']['crypto_currency'] ?? null,
                    $message['action']['crypto_amount'] ?? null,
                ),
                'messageActionTopicCreate' => new DialogTopicCreated(
                    $this,
                    $message,
                    $info,
                    $message['action']['title'],
                    $message['action']['icon_color'],
                    $message['action']['icon_emoji_id'] ?? null,
                ),
                'messageActionTopicEdit' => new DialogTopicEdited(
                    $this,
                    $message,
                    $info,
                    $message['action']['title'] ?? null,
                    $message['action']['icon_emoji_id'] ?? null,
                    $message['action']['closed'] ?? null,
                    $message['action']['hidden'] ?? null,
                ),
                'messageActionSuggestProfilePhoto' => new DialogSuggestProfilePhoto(
                    $this,
                    $message,
                    $info,
                    $this->wrapMedia($message['action']['photo'])
                ),
                'messageActionRequestedPeerSentMe' => new DialogPeerRequested(
                    $this,
                    $message,
                    $info,
                    $message['action']['button_id'],
                    $message['action']['peers'],
                ),
                'messageActionSetChatWallPaper' => new DialogSetChatWallPaper(
                    $this,
                    $message,
                    $info,
                    new Wallpaper($this, $message['action']['wallpaper']),
                    $message['action']['same'] ?? false,
                    $message['action']['for_both'] ?? false,
                ),
                'messageActionNoForwardsRequest' => new DialogNoForwardRequest(
                    $this,
                    $message,
                    $info,
                    $message['action']['expired'],
                    $message['action']['prev_value'],
                    $message['action']['new_value'],
                ),
                'messageActionNoForwardsToggle' => new DialogNoForwardToggle(
                    $this,
                    $message,
                    $info,
                    $message['action']['prev_value'],
                    $message['action']['new_value'],
                ),
                default => null
            };
        }
        if ($message['_'] === 'encryptedMessageService') {
            $action = $message['decrypted_message']['action'];
            return match ($action['_']) {
                'decryptedMessageActionSetMessageTTL' => new DialogSetTTL(
                    $this,
                    $message,
                    $info,
                    $action['ttl_seconds'],
                    null,
                ),
                'decryptedMessageActionReadMessages' => new DialogReadMessages(
                    $this,
                    $message,
                    $info,
                    $action['random_ids'],
                ),
                'decryptedMessageActionDeleteMessages' => new DialogDeleteMessages(
                    $this,
                    $message,
                    $info,
                    $action['random_ids'],
                ),
                'decryptedMessageActionScreenshotMessages' => new DialogScreenshotTaken(
                    $this,
                    $message,
                    $info,
                    $action['random_ids'],
                ),
                'decryptedMessageActionFlushHistory' => new DialogHistoryCleared(
                    $this,
                    $message,
                    $info,
                ),
                'decryptedMessageActionTyping' => new SecretUserTyping(
                    $this,
                    $message,
                    $info,
                ),
                default => null
            };
        }
        if (($info['User']['username'] ?? '') === 'replies') {
            return new CommentReply($this, $message, $info, $scheduled);
        }
        if ($message['_'] === 'encryptedMessage') {
            return new SecretMessage($this, $message, $info, $scheduled);
        }
        return match ($info['type']) {
            API::PEER_TYPE_BOT, API::PEER_TYPE_USER => new PrivateMessage($this, $message, $info, $scheduled),
            API::PEER_TYPE_GROUP, API::PEER_TYPE_SUPERGROUP => new GroupMessage($this, $message, $info, $scheduled),
            API::PEER_TYPE_CHANNEL => new ChannelMessage($this, $message, $info, $scheduled),
        };
    }
    /**
     * Sends a message.
     *
     * @param integer|string $peer                   Destination peer or username.
     * @param string         $message                Message to send
     * @param ParseMode      $parseMode              Parse mode
     * @param integer|null   $replyToMsgId           ID of message to reply to.
     * @param integer|null   $topMsgId               ID of thread where to send the message.
     * @param array|null     $replyMarkup            Keyboard information.
     * @param integer|null   $sendAs                 Peer to send the message as.
     * @param integer|null   $scheduleDate           Schedule date.
     * @param boolean        $silent                 Whether to send the message silently, without triggering notifications.
     * @param boolean        $background             Send this message as background message
     * @param boolean        $clearDraft             Clears the draft field
     * @param boolean        $noWebpage              Set this flag to disable generation of the webpage preview
     * @param boolean        $updateStickersetsOrder Whether to move used stickersets to top
     * @param ?Cancellation  $cancellation           Cancellation
     */
    public function sendMessage(
        int|string $peer,
        string $message,
        ParseMode $parseMode = ParseMode::TEXT,
        ?int $replyToMsgId = null,
        ?int $topMsgId = null,
        ?array $replyMarkup = null,
        int|string|null $sendAs = null,
        ?int $scheduleDate = null,
        bool $silent = false,
        bool $noForwards = false,
        bool $background = false,
        bool $clearDraft = false,
        bool $noWebpage = false,
        bool $updateStickersetsOrder = false,
        ?Cancellation $cancellation = null
    ): Message {
        $result = $this->methodCallAsyncRead(
            'messages.sendMessage',
            [
                'peer' => $peer,
                'message' => $message,
                'parse_mode' => $parseMode,
                'reply_to' => $replyToMsgId !== null || $topMsgId !== null ? [
                    '_' => 'inputReplyToMessage',
                    'reply_to_msg_id' => $replyToMsgId,
                    'top_msg_id' => $topMsgId,
                ] : null,
                'reply_markup' => $replyMarkup,
                'send_as' => $sendAs,
                'schedule_date' => $scheduleDate,
                'silent' => $silent,
                'noforwards' => $noForwards,
                'background' => $background,
                'clear_draft' => $clearDraft,
                'no_webpage' => $noWebpage,
                'update_stickersets_order' => $updateStickersetsOrder,
                'cancellation' => $cancellation,
            ]
        );
        if (isset($result['_'])) {
            return $this->wrapMessage($this->extractMessage($result));
        }

        $last = null;
        foreach ($result as $updates) {
            /** @var Message */
            $new = $this->wrapMessage($this->extractMessage($updates));
            if ($last) {
                $last->nextSent = $new;
            } else {
                $first = $new;
            }
            $last = $new;
        }
        return $first;
    }
    /**
     * Extract a message ID from an Updates constructor.
     */
    public function extractMessageId(array $updates): int
    {
        return $this->extractMessage($updates)['id'];
    }
    /**
     * Extract a message constructor from an Updates constructor.
     */
    public function extractMessage(array $updates): array
    {
        return ($this->extractMessageUpdate($updates))['message'];
    }
    /**
     * Extract an update message constructor from an Updates constructor.
     */
    public function extractMessageUpdate(array $updates): array
    {
        $result = null;
        foreach (($this->extractUpdates($updates)) as $update) {
            if (\in_array($update['_'], ['updateNewMessage', 'updateNewChannelMessage', 'updateEditMessage', 'updateEditChannelMessage', 'updateNewScheduledMessage', 'updateNewEncryptedMessage', 'updateNewOutgoingEncryptedMessage'], true)) {
                if ($result !== null) {
                    throw new Exception('Found more than one update of type message, use extractUpdates to extract all updates');
                }
                $result = $update;
            }
        }
        if ($result === null) {
            $updates = json_encode($updates);
            throw new Exception("Could not find any message in $updates!");
        }
        return $result;
    }
    /**
     * Extract Update constructors from an Updates constructor.
     *
     * @return array<array>
     */
    public function extractUpdates(array $updates): array
    {
        switch ($updates['_']) {
            case 'updates':
            case 'updatesCombined':
                return $updates['updates'];
            case 'updateShort':
                return [$updates['update']];
            case 'updateShortSentMessage':
                $updates['user_id'] = $this->getInfo($updates['request']['body']['peer'], API::INFO_TYPE_ID);
                // no break
            case 'updateShortMessage':
            case 'updateShortChatMessage':
                $updates = $this->mergeSentRequest($updates);
                $from_id = $updates['from_id'] ?? ($updates['out'] ? $this->authorization['user']['id'] : $updates['user_id']);
                $to_id = $updates['chat_id'] ?? ($updates['out'] ? $updates['user_id'] : $this->authorization['user']['id']);
                $message = $updates;
                $message['_'] = 'message';
                $message['from_id'] = $from_id;
                $message['peer_id'] = $to_id;
                $this->populateMessageFlags($message);
                return [['_' => 'updateNewMessage', 'message' => $message, 'pts' => $updates['pts'], 'pts_count' => $updates['pts_count']]];
            case 'updateNewOutgoingEncryptedMessage':
                return [$updates];
            default:
                throw new ResponseException('Unrecognized update received: '.$updates['_']);
        }
    }
    /**
     * Fill a short message update with the parameters of the request that sent it.
     *
     * The request has the input types of the method (InputReplyTo, InputMedia, ...), not the ones of the message.
     */
    private function mergeSentRequest(array $updates): array
    {
        $body = $updates['request']['body'] ?? [];
        // The media actually sent, if any, is in the update itself.
        unset($updates['request'], $body['media'], $body['rich_message']);
        if (isset($body['reply_to'])) {
            $body['reply_to'] = self::inputReplyToHeader(
                $body['reply_to'],
                isset($body['reply_to']['top_msg_id']) && $this->isForum($body['peer']),
                fn (mixed $peer): int => $this->getInfo($peer, API::INFO_TYPE_ID),
            );
        }
        return array_merge($body, $updates);
    }
    /**
     * Convert the InputReplyTo of a request into the MessageReplyHeader of the message it sent.
     *
     * @internal
     *
     * @param \Closure(mixed): int $getId
     */
    public static function inputReplyToHeader(array $replyTo, bool $forum, \Closure $getId): ?array
    {
        switch ($replyTo['_'] ?? null) {
            case 'inputReplyToMessage':
                $header = [
                    '_' => 'messageReplyHeader',
                    'reply_to_scheduled' => false,
                    'forum_topic' => $forum,
                    'quote' => isset($replyTo['quote_text']),
                    'reply_to_ephemeral' => false,
                ];
                $msgId = $replyTo['reply_to_msg_id'] ?? null;
                $topId = $replyTo['top_msg_id'] ?? null;
                if (!$msgId || $msgId === $topId) {
                    // A message at the root of a thread replies to its top message.
                    $msgId = $topId;
                    $topId = null;
                }
                if ($msgId !== null) {
                    $header['reply_to_msg_id'] = $msgId;
                }
                if ($topId !== null) {
                    $header['reply_to_top_id'] = $topId;
                }
                if (isset($replyTo['reply_to_peer_id'])) {
                    $header['reply_to_peer_id'] = $getId($replyTo['reply_to_peer_id']);
                }
                foreach (['quote_text', 'quote_entities', 'quote_offset', 'todo_item_id', 'poll_option'] as $key) {
                    if (isset($replyTo[$key])) {
                        $header[$key] = $replyTo[$key];
                    }
                }
                return $header;
            case 'inputReplyToStory':
                return [
                    '_' => 'messageReplyStoryHeader',
                    'peer' => $getId($replyTo['peer']),
                    'story_id' => $replyTo['story_id'],
                ];
            default:
                return null;
        }
    }
    private function populateMessageFlags(array &$message): void
    {
        $new = ['_' => 'message'];
        foreach ($this->TL->getConstructors()->findByPredicate('message')['params'] as $param) {
            if (isset($message[$param['name']]) && $param['name'] !== 'flags') {
                $new[$param['name']] = $message[$param['name']];
            } elseif ($param['type'] === 'true') {
                $new[$param['name']] = false;
            }
        }
        $message = $new;
    }
    /**
     * @param array $updates        Updates
     * @param array $actual_updates Actual updates for deferred
     * @internal
     */
    public function handleUpdates(array $updates, ?array $actual_updates = null): void
    {
        if ($actual_updates) {
            $updates = $actual_updates;
        }
        $this->logger->logger('Parsing updates ('.$updates['_'].') received via the socket...', Logger::VERBOSE);
        switch ($updates['_']) {
            case 'updates':
            case 'updatesCombined':
                $result = [];
                foreach ($updates['updates'] as $key => $update) {
                    if ($update['_'] === 'updateNewMessage' || $update['_'] === 'updateReadMessagesContents' || $update['_'] === 'updateEditMessage' || $update['_'] === 'updateDeleteMessages' || $update['_'] === 'updateReadHistoryInbox' || $update['_'] === 'updateReadHistoryOutbox' || $update['_'] === 'updateWebPage' || $update['_'] === 'updateMessageID') {
                        $result[$this->feeders[FeedLoop::GENERIC]->feedSingle($update)] = true;
                        unset($updates['updates'][$key]);
                    }
                }
                $this->seqUpdater->addPendingWakeups($result);
                if ($updates['updates']) {
                    if ($updates['_'] === 'updatesCombined') {
                        $updates['options'] = ['seq_start' => $updates['seq_start'], 'seq_end' => $updates['seq'], 'date' => $updates['date']];
                    } else {
                        $updates['options'] = ['seq_start' => $updates['seq'], 'seq_end' => $updates['seq'], 'date' => $updates['date']];
                    }
                    $this->seqUpdater->feed($updates);
                }
                $this->seqUpdater->resume();
                break;
            case 'updateShort':
                $this->feeders[$this->feeders[FeedLoop::GENERIC]->feedSingle($updates['update'])]->resume();
                break;
            case 'updateShortSentMessage':
                if (!isset($updates['request']['body'])) {
                    break;
                }
                $updates['user_id'] = $this->getInfo($updates['request']['body']['peer'], API::INFO_TYPE_ID);
                // no break
            case 'updateShortMessage':
            case 'updateShortChatMessage':
                $updates = $this->mergeSentRequest($updates);
                $from_id = $updates['from_id'] ?? ($updates['out'] ? $this->authorization['user']['id'] : $updates['user_id']);
                $to_id = $updates['chat_id'] ?? ($updates['out'] ? $updates['user_id'] : $this->authorization['user']['id']);
                if (!(($this->peerIsset($from_id)) || !(($this->peerIsset($to_id)) || isset($updates['via_bot_id']) && !(($this->peerIsset($updates['via_bot_id'])) || isset($updates['entities']) && !(($this->entitiesPeerIsset($updates['entities'])) || isset($updates['fwd_from']) && !($this->fwdPeerIsset($updates['fwd_from']))))))) {
                    $this->updaters[FeedLoop::GENERIC]->resume();
                    return;
                }
                $message = $updates;
                $message['_'] = 'message';
                $message['from_id'] = $from_id;
                $message['peer_id'] = $to_id;
                $this->populateMessageFlags($message);
                $update = ['_' => 'updateNewMessage', 'message' => $message, 'pts' => $updates['pts'], 'pts_count' => $updates['pts_count']];
                $this->feeders[$this->feeders[FeedLoop::GENERIC]->feedSingle($update)]->resume();
                break;
            case 'updatesTooLong':
                $this->updaters[UpdateLoop::GENERIC]->resume();
                break;
            default:
                throw new ResponseException('Unrecognized update received: '.var_export($updates, true));
                break;
        }
    }
    /**
     * Subscribe to event handler updates for a channel/supergroup we're not a member of.
     *
     * @param mixed $channel Channel/supergroup to subscribe to
     *
     * @return bool False if we were already subscribed
     */
    public function subscribeToUpdates(mixed $channel): bool
    {
        $channelId = $this->getId($channel);
        if (!DialogId::isSupergroupOrChannelOrMonoforum($channelId)) {
            throw new Exception("You can only subscribe to channels or supergroups!");
        }
        if (!$this->getChannelStates()->has($channelId)) {
            $this->loadChannelState($channelId, ['_' => 'updateChannelTooLong', 'pts' => 1]);
            $this->feeders[$channelId] ??= new FeedLoop($this, $channelId);
            $this->updaters[$channelId] ??= new UpdateLoop($this, $channelId);
            $this->feeders[$channelId]->start();
            if (isset($this->feeders[$channelId])) {
                $this->feeders[$channelId]->resume();
            }
            $this->updaters[$channelId]->start();
            if (isset($this->updaters[$channelId])) {
                $this->updaters[$channelId]->resume();
            }
            return true;
        }
        return false;
    }
    /**
     * Save update.
     *
     * @param array $update Update to save
     * @internal
     */
    public function saveUpdate(array $update): void
    {
        $this->logger->logger("Saving update of type {$update['_']}", Logger::VERBOSE);
        if ($this->referenceDatabase !== null && $this->settings->getDb()->getEnableFileReferenceDb()) {
            // Extracted, deduplicated and reordered by the update system, short updates converted to updates.
            $this->referenceDatabase->onUpdate($update);
        }
        if ($update['_'] === 'updateConfig') {
            $this->config['expires'] = 0;
            $this->getConfig();
        }
        if ($update['_'] === 'updateLoginToken') {
            try {
                $authorization = $this->methodCallAsyncRead(
                    'auth.exportLoginToken',
                    [
                        'api_id' => $this->settings->getAppInfo()->getApiId(),
                        'api_hash' => $this->settings->getAppInfo()->getApiHash(),
                        'specialMethodType' => SpecialMethodType::USER_RELATED,
                    ],
                );
                if ($authorization['_'] === 'auth.loginTokenMigrateTo') {
                    $datacenter = $this->isTestMode() ? 10_000 + $authorization['dc_id'] : $authorization['dc_id'];
                    $this->loginState->publish($this->loginState->getState()->setDc($datacenter));
                    $authorization['specialMethodType'] = SpecialMethodType::USER_RELATED;
                    $authorization = $this->methodCallAsyncRead(
                        'auth.importLoginToken',
                        $authorization,
                    );
                }
            } catch (SessionPasswordNeededError) {
                $this->logger->logger(Lang::$current_lang['login_2fa_enabled'], Logger::NOTICE);
                $this->authorization = $this->getPassword();
                if (!isset($this->authorization['hint'])) {
                    $this->authorization['hint'] = '';
                }
                $this->setLoginState(API::WAITING_PASSWORD);
                $this->qrLoginDeferred?->cancel();
                $this->qrLoginDeferred = null;
                return;
            }
            return;
        }
        if (
            \in_array($update['_'], ['updateChannel', 'updateUser', 'updateUserName', 'updateUserPhone', 'updateUserBlocked', 'updateUserPhoto', 'updateContactRegistered', 'updateContactLink', 'updatePeerBlocked'], true)
            || (
                $update['_'] === 'updateNewChannelMessage'
                && isset($update['message']['action']['_'])
                && \in_array($update['message']['action']['_'], ['messageActionChatEditTitle', 'messageActionChatEditPhoto', 'messageActionChatDeletePhoto', 'messageActionChatMigrateTo', 'messageActionChannelMigrateFrom', 'messageActionGroupCall'], true)
            )
        ) {
            $id = $this->getIdInternal($update);
            \assert($id !== null);
            EventLoop::queue(function () use ($id): void {
                try {
                    $this->refreshPeerCache($id);
                    if ($this->getSettings()->getDb()->getEnableFullPeerDb()) {
                        $this->peerDatabase->expireFull($id);
                    }
                } catch (ChannelPrivateError|MsgIdInvalidError|PeerNotInDbException|FloodWaitError) {
                }
            });
        }
        if ($update['_'] === 'updateDcOptions') {
            $this->logger->logger('Got new dc options', Logger::VERBOSE);
            $this->config['dc_options'] = $update['dc_options'];
            $this->parseConfig();
            return;
        }
        if ($update['_'] === 'updatePhoneCallSignalingData') {
            if (isset($this->calls[$update['phone_call_id']])) {
                EventLoop::queue($this->calls[$update['phone_call_id']]->onSignaling(...), (string) $update['data']);
            }
        }
        if ($update['_'] === 'updateGroupCall'
            || $update['_'] === 'updateGroupCallParticipants'
            || $update['_'] === 'updateGroupCallChainBlocks'
            || $update['_'] === 'updateGroupCallEncryptedMessage'
        ) {
            // Group call updates must be applied synchronously and in order, exclusively from here:
            // never invoke the controller's handlers directly, or a version gap could be filled out
            // of sequence. updateGroupCallConnection is not handled here: it carries no call ID and
            // is only ever returned by phone.joinGroupCall, so it is extracted from that response.
            // The chain-block and encrypted-message updates drive end-to-end conference calls.
            $this->handleGroupCallUpdate($update);
        }
        if ($update['_'] === 'updatePhoneCall') {
            if (!$this->postponeCallUpdate($update)) {
                $this->applyCallUpdate($update);
            }
            return;
        }
        if ($update['_'] === 'updateNewEncryptedMessage' && !isset($update['message']['decrypted_message'])) {
            if (isset($update['qts'])) {
                $cur_state = ($this->loadUpdateState());
                if ($cur_state->qts() === -1) {
                    $cur_state->qts($update['qts']);
                }
                if ($update['qts'] < $cur_state->qts()) {
                    $this->logger->logger('Duplicate update. update qts: '.$update['qts'].' <= current qts '.$cur_state->qts().', chat id: '.$update['message']['chat_id'], Logger::ERROR);
                    return;
                }
                if ($update['qts'] > $cur_state->qts() + 1) {
                    $this->logger->logger('Qts hole. Fetching updates manually: update qts: '.$update['qts'].' > current qts '.$cur_state->qts().'+1, chat id: '.$update['message']['chat_id'], Logger::ERROR);
                    $this->updaters[UpdateLoop::GENERIC]->resume();
                    return;
                }
                $this->logger->logger('Applying qts: '.$update['qts'].' over current qts '.$cur_state->qts().', chat id: '.$update['message']['chat_id'], Logger::VERBOSE);
                $this->methodCallAsyncRead('messages.receivedQueue', ['max_qts' => $cur_state->qts($update['qts'])]);
            }
            EventLoop::queue(function () use ($update): void {
                if (!isset($this->secretChats[$update['message']['chat_id']])) {
                    $this->logger->logger(\sprintf(Lang::$current_lang['secret_chat_skipping'], $update['message']['chat_id']));
                    return;
                }
                $this->secretChats[$update['message']['chat_id']]->feed($update);
            });
            return;
        }
        if ($update['_'] === 'updateEncryption') {
            EventLoop::queue(function () use ($update): void {
                switch ($update['chat']['_']) {
                    case 'encryptedChatRequested':
                        if (!$this->settings->getSecretChats()->canAccept($update['chat']['admin_id'])) {
                            return;
                        }
                        $this->logger->logger('Accepting secret chat '.$update['chat']['id'], Logger::NOTICE);
                        try {
                            $this->acceptSecretChat($update['chat']);
                        } catch (RPCErrorException $e) {
                            $this->logger->logger("Error while accepting secret chat: {$e}", Logger::FATAL_ERROR);
                        }
                        break;
                    case 'encryptedChatDiscarded':
                        // Discarded by the other party, or accepted by another session of the account: forget it
                        // without echoing a messages.discardEncryption, which is only for chats we close ourselves.
                        $this->logger->logger('Deleting secret chat '.$update['chat']['id'].' because it was revoked by the other user (it was probably accepted by another client)', Logger::NOTICE);
                        unset($this->secretChats[$update['chat']['id']], $this->temp_requested_secret_chats[$update['chat']['id']]);
                        break;
                    case 'encryptedChat':
                        $this->logger->logger('Completing creation of secret chat '.$update['chat']['id'], Logger::NOTICE);
                        $this->completeSecretChat($update['chat']);
                        break;
                }
            });
            //$this->logger->logger($update, \danog\MadelineProto\Logger::NOTICE);
        }
        if ($this->updateHandlerType === UpdateHandlerType::NOOP) {
            return;
        }
        if (isset($update['message']['_']) && $update['message']['_'] === 'messageEmpty') {
            return;
        }
        if (isset($update['message']['from_id'])
            && $update['message']['from_id'] > 0
        ) {
            if ($update['message']['from_id'] === $this->authorization['user']['id']) {
                $update['message']['out'] = true;
            }
        } elseif (!isset($update['message']['from_id'])
            && isset($update['message']['peer_id'])
            && $update['message']['peer_id'] > 0
            && $update['message']['peer_id'] === $this->authorization['user']['id']) {
            $update['message']['out'] = true;
        }

        if (!isset($update['message']['from_id'])
            && isset($update['message']['peer_id'])
            && $update['message']['peer_id'] > 0
        ) {
            $update['message']['from_id'] = $update['message']['peer_id'];
        }

        $this->handleUpdate($update);
    }
    /**
     * Apply an [updatePhoneCall](https://core.telegram.org/constructor/updatePhoneCall) to its call, then
     * dispatch it, see {@see self::postponeCallUpdate()}.
     */
    private function applyCallUpdate(array $update): void
    {
        switch ($update['phone_call']['_']) {
            case 'phoneCallRequested':
                // Ringing on every session of the account: only the one that accepts it will be in
                // it, see PrivateCallController::accept() and onDiscarded().
                if (isset($this->calls[$update['phone_call']['id']])) {
                    return;
                }
                $this->calls[$update['phone_call']['id']] = $controller = new PrivateCallController(
                    $this,
                    $update['phone_call'],
                );
                $this->callsByPeer[$controller->public->otherID] = $controller;
                $update['phone_call'] = $controller->public;
                break;
            case 'phoneCallWaiting':
                // The state of an outgoing call, which requestCall() tracks from the result of
                // phone.requestCall (the updates that come before it are postponed until then, see
                // postponeCallUpdate()): any other one was requested by another session of the account
                // (as TDLib, only create calls from phoneCallRequested), and tracking it would make
                // this session confirm or discard a call it is not in. It carries nothing to apply to
                // a call we track.
                return;
            case 'phoneCallAccepted':
                if (!isset($this->calls[$update['phone_call']['id']])) {
                    return;
                }
                $controller = $this->calls[$update['phone_call']['id']];
                $controller->confirm($update['phone_call']);
                $update['phone_call'] = $controller->public;
                break;
            case 'phoneCall':
                if (!isset($this->calls[$update['phone_call']['id']])) {
                    return;
                }
                $controller = $this->calls[$update['phone_call']['id']];
                $controller->complete($update['phone_call']);
                $update['phone_call'] = $controller->public;
                break;
            case 'phoneCallDiscarded':
                if (!isset($this->calls[$update['phone_call']['id']])) {
                    return;
                }
                $controller = $this->calls[$update['phone_call']['id']];
                $controller->onDiscarded($update['phone_call']);
                $update['phone_call'] = $controller->public;
                break;
            case 'phoneCallEmpty':
                return;
        }
        if ($this->updateHandlerType !== UpdateHandlerType::NOOP) {
            $this->handleUpdate($update);
        }
    }
    /**
     * Handle the updates postponed while the event handler is starting, once it started.
     *
     * @param Future<mixed> $started
     */
    private function handlePostponedUpdates(Future $started): void
    {
        if ($this->updateQueueReplay) {
            return;
        }
        $this->updateQueueReplay = true;
        $started->map(function (): void {
            $this->updateQueueReplay = false;
            // Only the updates postponed until now: if the event handler failed to start, they're postponed again
            // until it's started again, instead of being postponed and handled again in an endless loop.
            for ($x = $this->updateQueue->count(); $x > 0; $x--) {
                $this->handleUpdate($this->updateQueue->dequeue());
            }
        });
    }
    private ?BetterCounter $updateCtr;
    private function handleUpdate(array $update): void
    {
        $this->updateCtr?->inc(['type' => $update['_']]);
        /** @var UpdateHandlerType::EVENT_HANDLER|UpdateHandlerType::WEBHOOK|UpdateHandlerType::GET_UPDATES $this->updateHandlerType */
        match ($this->updateHandlerType) {
            // Custom events can be sent before an update handler is set (for example, in the onStart of the event handler).
            UpdateHandlerType::NOOP => null,
            UpdateHandlerType::EVENT_HANDLER => $this->eventUpdateHandler($update),
            UpdateHandlerType::WEBHOOK => EventLoop::queue($this->pwrWebhook(...), $update),
            UpdateHandlerType::GET_UPDATES => EventLoop::queue($this->addGetUpdatesUpdate(...), $update),
        };
    }
    /**
     * Sends an updateCustomEvent update to the event handler.
     */
    public function sendCustomEvent(mixed $payload): void
    {
        $this->handleUpdate(['_' => 'updateCustomEvent', 'payload' => $payload]);
    }
}
<?php

declare(strict_types=1);

/**
 * Files module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\MTProtoTools;

use Amp\ByteStream\ReadableStream;
use Amp\Cancellation;
use Amp\DeferredFuture;
use Amp\Future;
use Amp\Http\Client\Request;
use Amp\Ipc\Sync\ChannelException;
use AssertionError;
use danog\Decoder\FileId;
use danog\Decoder\FileIdType;
use danog\MadelineProto\BotApiFileId;
use danog\MadelineProto\EventHandler\Media;
use danog\MadelineProto\EventHandler\Media\AnimatedSticker;
use danog\MadelineProto\EventHandler\Media\Audio;
use danog\MadelineProto\EventHandler\Media\CustomEmoji;
use danog\MadelineProto\EventHandler\Media\Document;
use danog\MadelineProto\EventHandler\Media\DocumentPhoto;
use danog\MadelineProto\EventHandler\Media\Gif;
use danog\MadelineProto\EventHandler\Media\InlineThumbnail;
use danog\MadelineProto\EventHandler\Media\MaskSticker;
use danog\MadelineProto\EventHandler\Media\Photo;
use danog\MadelineProto\EventHandler\Media\RoundVideo;
use danog\MadelineProto\EventHandler\Media\StaticSticker;
use danog\MadelineProto\EventHandler\Media\Video;
use danog\MadelineProto\EventHandler\Media\VideoSticker;
use danog\MadelineProto\EventHandler\Media\Voice;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\Exception;
use danog\MadelineProto\FileCallbackInterface;
use danog\MadelineProto\FileRedirect;
use danog\MadelineProto\Logger;
use danog\MadelineProto\MTProto\SpecialMethodType;
use danog\MadelineProto\MTProtoTools\Crypt\IGE;
use danog\MadelineProto\RPCError\FileTokenInvalidError;
use danog\MadelineProto\RPCError\FloodPremiumWaitError;
use danog\MadelineProto\RPCError\FloodWaitError;
use danog\MadelineProto\RPCError\RequestTokenInvalidError;
use danog\MadelineProto\SecurityException;
use danog\MadelineProto\Settings;
use danog\MadelineProto\StreamEof;
use danog\MadelineProto\Tools;
use danog\MadelineProto\UploadResumeException;
use danog\MadelineProto\WrappedFuture;
use Revolt\EventLoop;
use Throwable;
use Webmozart\Assert\Assert;

use const LOCK_EX;
use function Amp\async;
use function Amp\delay;
use function Amp\File\deleteFile;
use function Amp\File\getSize;
use function Amp\File\openFile;
use function Amp\Future\await;
use function Amp\Future\awaitFirst;

/**
 * Manages upload and download of files.
 *
 * @property Settings $settings Settings
 *
 * @internal
 */
trait Files
{
    use FilesLogic;
    use FileServer;
    /**
     * Wrap a media constructor into an abstract Media object.
     */
    public function wrapMedia(array $media, bool $protected = false): ?Media
    {
        if ($media['_'] === 'photo') {
            $media = [ '_' => 'messageMediaPhoto', 'photo' => $media ];
        }
        if ($media['_'] === 'document') {
            $media = [ '_' => 'messageMediaDocument', 'document' => $media];
        }
        if ($media['_'] === 'decryptedMessageMediaAudio') {
            return new Audio($this, $media, $media, $protected);
        }
        if ($media['_'] === 'decryptedMessageMediaPhoto') {
            return new Photo($this, $media, $protected);
        }
        if ($media['_'] === 'decryptedMessageMediaVideo') {
            return new Video($this, $media, $media, $protected);
        }
        if ($media['_'] === 'messageMediaPhoto') {
            if (!isset($media['photo']) || $media['photo']['_'] === 'photoEmpty') {
                return null;
            }
            return new Photo($this, $media, $protected);
        }
        if ($media['_'] === 'decryptedMessageMediaDocument'
            || $media['_'] === 'decryptedMessageMediaExternalDocument'
        ) {
            // TODO caption?
            $media = [
                '_' => 'messageMediaDocument',
                'document' => $media,
                'ttl_seconds' => $media['ttl_seconds'],
                'secret' => true,
            ];
        }
        if ($media['_'] !== 'messageMediaDocument') {
            return null;
        }
        if (!isset($media['document'])) {
            return null;
        }
        $has_video = null;
        $has_document_photo = null;
        $has_animated = false;
        foreach ($media['document']['attributes'] as $attr) {
            $t = $attr['_'];
            if ($t === 'documentAttributeImageSize') {
                $has_document_photo = $attr;
                continue;
            }
            if ($t === 'documentAttributeAnimated') {
                $has_animated = true;
                continue;
            }
            if ($t === 'documentAttributeSticker') {
                if ($has_video) {
                    return new VideoSticker($this, $media, $attr, $has_video, $protected);
                }

                if ($has_document_photo === null) {
                    $has_document_photo = [
                        'w' => null,
                        'h' => null,
                    ];
                }

                if ($attr['mask'] ?? false) {
                    return new MaskSticker($this, $media, $attr, $has_document_photo, $protected);
                }

                if ($media['document']['mime_type'] === 'application/x-tgsticker') {
                    return new AnimatedSticker($this, $media, $attr, $has_document_photo, $protected);
                }

                return new StaticSticker($this, $media, $attr, $has_document_photo, $protected);
            }
            if ($t === 'documentAttributeVideo') {
                $has_video = $attr;
                continue;
            }
            if ($t === 'documentAttributeAudio') {
                // Round videos may also have an audio attribute, with the waveform.
                foreach ($media['document']['attributes'] as $video) {
                    if ($video['_'] === 'documentAttributeVideo' && ($video['round_message'] ?? false)) {
                        continue 2;
                    }
                }
                return $attr['voice']
                    ? new Voice($this, $media, $attr, $protected)
                    : new Audio($this, $media, $attr, $protected);
            }
            if ($t === 'documentAttributeCustomEmoji') {
                if ($has_document_photo === null) {
                    throw new AssertionError("has_document_photo === null: ".json_encode($media['document']));
                }
                return new CustomEmoji($this, $media, $attr, $has_document_photo, $protected);
            }
        }
        if ($has_animated) {
            if ($has_video === null) {
                return new Document($this, $media, $protected);
            }
            return new Gif($this, $media, $has_video, $protected);
        }
        if ($has_video) {
            return ($has_video['round_message'] ?? false)
                ? new RoundVideo($this, $media, $has_video, $protected)
                : new Video($this, $media, $has_video, $protected);
        }
        if ($has_document_photo) {
            return new DocumentPhoto($this, $media, $has_document_photo, $protected);
        }
        return new Document($this, $media, $protected);
    }
    /**
     * Upload file from URL.
     *
     * @param  string|FileCallbackInterface $url       URL of file
     * @param  integer                      $size      Size of file
     * @param  string                       $fileName  File name
     * @param  callable                     $cb        Callback
     * @param  boolean                      $encrypted Whether to encrypt file for secret chats
     * @return array                        InputFile constructor
     */
    public function uploadFromUrl(string|FileCallbackInterface $url, int $size = 0, string $fileName = '', ?callable $cb = null, bool $encrypted = false, ?Cancellation $cancellation = null): array
    {
        if (\is_object($url) && $url instanceof FileCallbackInterface) {
            $cb = $url;
            $url = $url->getFile();
        }
        $request = new Request($url);
        $request->setTransferTimeout(INF);
        $request->setInactivityTimeout(INF);
        $request->setBodySizeLimit(512 * 1024 * 8000);
        $response = $this->datacenter->getHTTPClient()->request($request, $cancellation);
        if (($status = $response->getStatus()) !== 200) {
            throw new Exception("Wrong status code: {$status} ".$response->getReason());
        }
        $mime = trim(explode(';', $response->getHeader('content-type') ?? '')[0]) ?: null;
        $size = (int) ($response->getHeader('content-length') ?? $size);
        $stream = $response->getBody();
        // An interrupted upload can only be resumed if we can tell that the file did not change since.
        $validator = $response->getHeader('etag') ?? $response->getHeader('last-modified');
        $resumeKey = $validator !== null ? 'url:'.$url."\0".$validator : null;
        $reopenAt = null;
        if ($validator !== null && strtolower($response->getHeader('accept-ranges') ?? '') === 'bytes') {
            $reopenAt = function (int $offset) use ($url, $validator, $cancellation): ?ReadableStream {
                $request = new Request($url);
                $request->setTransferTimeout(INF);
                $request->setInactivityTimeout(INF);
                $request->setBodySizeLimit(512 * 1024 * 8000);
                $request->setHeader('Range', "bytes=$offset-");
                $request->setHeader('If-Range', $validator);
                $response = $this->datacenter->getHTTPClient()->request($request, $cancellation);
                if ($response->getStatus() !== 206
                    || !str_starts_with($response->getHeader('content-range') ?? '', "bytes $offset-")
                ) {
                    // The range was ignored (for example, because the file changed).
                    $response->getBody()->close();
                    return null;
                }
                return $response->getBody();
            };
        }
        return $this->uploadFromStreamInternal($stream, $size, $mime, $fileName, $cb, $encrypted, $cancellation, $resumeKey, $reopenAt);
    }
    /**
     * Upload file from callable.
     *
     * The callable must accept two parameters: int $offset, int $size
     * The callable must return a string with the contest of the file at the specified offset and size.
     *
     * @param (callable(int, int, ?Cancellation): string) $callable  Callable (offset, length) => data
     * @param integer                                   $size      File size
     * @param ?string                                    $mime      Mime type
     * @param string                                    $fileName  File name
     * @param (callable(float, float, float): void)       $cb        Status callback
     * @param boolean                                   $seekable  Whether chunks can be fetched out of order
     * @param boolean                                   $encrypted Whether to encrypt file for secret chats
     *
     * @return array InputFile constructor
     */
    public function uploadFromCallable(callable $callable, int $size = 0, ?string $mime = null, string $fileName = '', ?callable $cb = null, bool $seekable = true, bool $encrypted = false, ?Cancellation $cancellation = null): array
    {
        return $this->uploadFromCallableInternal($callable, $size, $mime, $fileName, $cb, $seekable, $encrypted, $cancellation);
    }
    /**
     * Upload file from callable, resuming an interrupted upload of the same file if possible.
     *
     * @param ?string $resumeKey Identifies the source of the file across restarts: if an upload of the same file
     *                           (with the same size) was interrupted, it is resumed.
     *                           A non-seekable callable must then accept starting from a non-zero offset (the first missing part).
     */
    private function uploadFromCallableInternal(callable $callable, int $size = 0, ?string $mime = null, string $fileName = '', ?callable $cb = null, bool $seekable = true, bool $encrypted = false, ?Cancellation $cancellation = null, ?string $resumeKey = null): array
    {
        if ($cb === null) {
            $cb = function (float $percent, float $speed, float $time): void {
                $this->logger->logger('Upload status: '.$percent.'%', Logger::NOTICE);
            };
        } else {
            $cb = static function (float $percent, float $speed, float $time) use ($cb): void {
                EventLoop::queue(static function () use ($percent, $speed, $time, $cb): void {
                    try {
                        $cb($percent, $speed, $time);
                    } catch (ChannelException) {
                        // The IPC client this progress was meant for is gone.
                    }
                });
            };
        }
        $part_size = 512 * 1024;
        if (!$size) {
            $seekable = false;
        }
        $datacenter = $this->loginState->getState()->authorizedDc;
        if ($this->datacenter->has(-$datacenter)) {
            $datacenter = -$datacenter;
        }
        $parallel_chunks = $this->settings->getFiles()->getUploadParallelChunks();
        $part_total_num = $size ? ((int) ceil($size / $part_size)) : -1;
        Assert::notEq($part_total_num, 0);
        $part_num = 0;
        $method = $size > 10 * 1024 * 1024 || !$size ? 'upload.saveBigFilePart' : 'upload.saveFilePart';
        $constructor = 'input'.($encrypted === true ? 'Encrypted' : '').($size > 10 * 1024 * 1024 || !$size ? 'FileBig' : 'File').($encrypted === true ? 'Uploaded' : '');
        $stateKey = $this->getUploadStateKey($resumeKey, $size, $encrypted);
        $state = $stateKey !== null ? $this->resumableUploads[$stateKey] ?? null : null;
        if ($state !== null && $state['part_size'] !== $part_size) {
            $state = null;
        }
        $file_id = $state['file_id'] ?? Tools::random(8);
        $ige = null;
        $fingerprint = null;
        $iv = null;
        $key = null;
        if ($encrypted === true) {
            $key = $state['key'] ?? Tools::random(32);
            $iv = $state['iv'] ?? Tools::random(32);
            $digest = hash('md5', $key.$iv, true);
            $fingerprint = Tools::unpackSignedInt(substr($digest, 0, 4) ^ substr($digest, 4, 4));
            // IGE is chained: resume encrypting from the last saved point.
            $ige = IGE::getInstance($key, $state['ige_state'] ?? $iv);
            $seekable = false;
        }
        /** @var array<int, true> The parts uploaded before the upload was interrupted. */
        $uploaded = $state['parts'] ?? [];
        if (!$seekable) {
            // Sequential sources restart from the first missing part,
            // or from the last point where the encryption state was saved.
            if ($encrypted) {
                $part_num = $state['ige_part'] ?? 0;
            } else {
                while (isset($uploaded[$part_num])) {
                    $part_num++;
                }
            }
        }
        if ($stateKey !== null) {
            if ($state !== null) {
                $this->logger->logger('Resuming upload of '.($fileName ?: 'file').': '.\count($uploaded)." of $part_total_num parts were already uploaded", Logger::NOTICE);
            }
            $this->resumableUploads[$stateKey] = $state ?? [
                'file_id' => $file_id,
                'part_size' => $part_size,
                'parts' => [],
                'time' => time(),
            ] + ($encrypted ? [
                'key' => $key,
                'iv' => $iv,
                'ige_part' => 0,
                'ige_state' => $iv,
            ] : []);
            $this->activeUploads[$stateKey] = true;
        }
        /** @var array<int, string> Encryption state after each part, until it is saved in the resume state. */
        $igeStates = [];
        //$ctx = \hash_init('md5');
        $promises = [];
        // Without a size, the progress is unknown until the upload ends.
        $progress = $size ? new TransferProgress($cb, $part_total_num) : null;
        // Parts before the first one we read were already uploaded.
        $totalSize = min($part_num * $part_size, $size);
        if ($progress) {
            for ($x = 0; $x < $part_num; $x++) {
                $progress->partDone(0);
            }
        }
        if (!$seekable) {
            $nextOffset = $part_num * $part_size;
            $callable = static function (int $offset, int $size, ?Cancellation $cancellation) use ($callable, &$nextOffset): string {
                Assert::eq($offset, $nextOffset);
                $nextOffset += $size;
                return $callable($offset, $size, $cancellation);
            };
        }
        $read = $callable;
        $callable = static function (int $part_num) use (&$totalSize, $size, $file_id, &$part_total_num, $part_size, $read, $ige, &$igeStates, $cancellation): array {
            $bytes = $read(
                $part_num * $part_size,
                $part_size,
                $cancellation,
            );
            $cancellation?->throwIfRequested();
            $totalSize += $bytesLen = \strlen($bytes);
            if ($size === 0) {
                if ($bytesLen === 0) {
                    throw new StreamEof();
                }
                if ($bytesLen < $part_size) {
                    $part_total_num = (int) ceil($totalSize / $part_size);
                }
            }

            if ($ige) {
                $bytes = $ige->encrypt(str_pad($bytes, $part_size, \chr(0)));
                $igeStates[$part_num + 1] = $ige->getState();
            }

            return ['file_id' => $file_id, 'file_part' => $part_num, 'file_total_parts' => $part_total_num, 'bytes' => $bytes];
        };
        $resPromises = [];
        $start = microtime(true);
        /** @var ?FloodPremiumWaitError */
        $floodWaitError = null;
        try {
            while ($part_num < $part_total_num || !$size) {
                if ($seekable && isset($uploaded[$part_num])) {
                    $totalSize += min($part_size, $size - $part_num * $part_size);
                    $progress?->partDone(0);
                    ++$part_num;
                    continue;
                }
                if ($seekable) {
                    $writeCb = function () use ($method, $callable, $part_num, $cancellation, &$datacenter, &$floodWaitError): WrappedFuture {
                        $floodWaitError?->wait($cancellation);
                        return $this->methodCallAsyncWrite(
                            $method,
                            $callable($part_num) + ['cancellation' => $cancellation, 'floodWaitLimit' => 0, 'specialMethodType' => SpecialMethodType::FILE_RELATED],
                            $datacenter
                        );
                    };
                } else {
                    try {
                        $part = $callable($part_num) + ['cancellation' => $cancellation, 'floodWaitLimit' => 0, 'specialMethodType' => SpecialMethodType::FILE_RELATED];
                    } catch (StreamEof) {
                        break;
                    }
                    if (isset($uploaded[$part_num])) {
                        // Only read (and encrypted) to get to the next part.
                        $progress?->partDone(0);
                        ++$part_num;
                        continue;
                    }
                    $writeCb = function () use ($method, $part, &$datacenter, &$floodWaitError, $cancellation): WrappedFuture {
                        $floodWaitError?->wait($cancellation);
                        return $this->methodCallAsyncWrite(
                            $method,
                            $part,
                            $datacenter
                        );
                    };
                }
                // Ignored: if the upload fails or is cancelled, the caller stops before awaiting it.
                $writePromise = async($writeCb)->ignore();
                EventLoop::queue(function () use ($writePromise, $progress, $part_num, $part_size, $size, &$resPromises, $cancellation, $writeCb, &$datacenter, &$floodWaitError, $stateKey, &$igeStates): void {
                    $d = new DeferredFuture;
                    // Ignored: if the upload fails or is cancelled, the caller stops before awaiting it.
                    $resPromises[] = $d->getFuture()->ignore();
                    try {
                        do {
                            $readFuture = $writePromise->await($cancellation);
                            try {
                                // Wrote chunk!
                                if (!$readFuture->await($cancellation)) {
                                    throw new Exception('Upload of part '.$part_num.' failed');
                                }
                                // Got OK from server for chunk!
                                if ($stateKey !== null) {
                                    $this->markPartUploaded($stateKey, $part_num, $igeStates);
                                }
                                $progress?->partDone(min($part_size, $size - $part_num * $part_size));
                                $d->complete();
                                return;
                            } catch (FloodPremiumWaitError $e) {
                                $this->logger("Got {$e->rpc} while uploading part $part_num: {$datacenter}, waiting and retrying...");
                                $floodWaitError = $e;
                                $writePromise = async($writeCb);
                            } catch (FileRedirect $e) {
                                $datacenter = $e->dc;
                                $this->logger("Got redirect while uploading part $part_num: {$datacenter}");
                                $writePromise = async($writeCb);
                            } catch (Throwable $e) {
                                $cancellation?->throwIfRequested();
                                $this->logger("Got exception while uploading part $part_num: {$e}");
                                $writePromise = async($writeCb);
                            }
                        } while (true);
                    } catch (Throwable $e) {
                        // Hand the error (e.g. a cancellation) to the caller, instead of the event loop.
                        $d->error($e);
                    }
                });
                $promises[] = $writePromise;
                ++$part_num;
                if (\count($promises) === $parallel_chunks) {
                    // By default, 10 mb at a time, for a typical bandwidth of 1gbps (run the code in this every second)
                    awaitFirst($promises, $cancellation);
                    foreach ($promises as $k => $p) {
                        if ($p->isComplete()) {
                            unset($promises[$k]);
                            break;
                        }
                    }
                }
            }
            await($promises, $cancellation);
            await($resPromises, $cancellation);
            if ($stateKey !== null) {
                unset($this->resumableUploads[$stateKey]);
            }
        } catch (UploadResumeException $e) {
            // Resuming again would fail the same way: the next upload of this file starts from scratch.
            if ($stateKey !== null) {
                unset($this->resumableUploads[$stateKey]);
            }
            throw $e;
        } finally {
            if ($stateKey !== null) {
                unset($this->activeUploads[$stateKey]);
            }
        }
        if ($totalSize === 0) {
            throw new AssertionError('You uploaded an empty file!');
        }
        $time = microtime(true) - $start;
        $speed = (int) ($totalSize * 8 / $time) / 1000000;
        if (!$size) {
            $cb(100, $speed, $time);
        }
        $this->logger->logger("Total upload time: {$time}");
        $this->logger->logger("Total upload speed: {$speed} mbps");
        $constructor = ['_' => $constructor, 'id' => $file_id, 'parts' => $part_total_num, 'name' => $fileName, 'mime_type' => $mime ?? 'application/octet-stream'];
        if ($encrypted === true) {
            $constructor['key_fingerprint'] = $fingerprint;
            $constructor['key'] = $key;
            $constructor['iv'] = $iv;
            $constructor['size'] = $totalSize;
        }
        $constructor['md5_checksum'] = '';
        //\hash_final($ctx);
        return $constructor;
    }
    /**
     * For how long the state of an interrupted upload is kept, in seconds.
     *
     * Telegram only keeps uploaded parts for a limited time.
     */
    private const UPLOAD_RESUME_TTL = 3600;

    /**
     * State of interrupted uploads, to resume them.
     *
     * @var array<string, array{file_id: string, part_size: int, parts: array<int, true>, time: int, key?: string, iv?: string, ige_part?: int, ige_state?: string}>
     */
    private array $resumableUploads = [];

    /**
     * Key of the MACs of the file IDs of inline thumbnails: they carry the thumbnail itself, so only the ones
     * generated by this session are accepted (a forged one would make the download script serve anything).
     */
    private ?string $inlineFileIdKey = null;

    /**
     * @internal
     */
    public function getInlineFileIdKey(): string
    {
        return $this->inlineFileIdKey ??= random_bytes(32);
    }

    /**
     * Uploads in progress in this process, by resume state key.
     *
     * @var array<string, true>
     */
    private array $activeUploads = [];

    /**
     * Get the key of the resume state of an upload, dropping expired states.
     *
     * Returns null if the upload can't be resumed: its source or size is not known, or the same file
     * is already being uploaded (the two uploads must not share their state).
     */
    private function getUploadStateKey(?string $resumeKey, int $size, bool $encrypted): ?string
    {
        $expired = time() - self::UPLOAD_RESUME_TTL;
        foreach ($this->resumableUploads as $key => $state) {
            if ($state['time'] < $expired && !isset($this->activeUploads[$key])) {
                unset($this->resumableUploads[$key]);
            }
        }
        if ($resumeKey === null || $size <= 0) {
            return null;
        }
        $key = hash('sha256', $resumeKey."\0".$size."\0".($encrypted ? 'encrypted' : 'plain'));
        return isset($this->activeUploads[$key]) ? null : $key;
    }

    /**
     * Record that a part was uploaded, saving the encryption state if the uploaded prefix grew.
     *
     * @param array<int, string> $igeStates Encryption state after each part
     */
    private function markPartUploaded(string $stateKey, int $part, array &$igeStates): void
    {
        if (!isset($this->resumableUploads[$stateKey])) {
            return;
        }
        $state = &$this->resumableUploads[$stateKey];
        $state['parts'][$part] = true;
        $state['time'] = time();
        if (!isset($state['ige_part'])) {
            return;
        }
        $prefix = $state['ige_part'];
        while (isset($state['parts'][$prefix])) {
            $prefix++;
        }
        if ($prefix > $state['ige_part'] && isset($igeStates[$prefix])) {
            $state['ige_part'] = $prefix;
            $state['ige_state'] = $igeStates[$prefix];
            foreach ($igeStates as $k => $_) {
                if ($k <= $prefix) {
                    unset($igeStates[$k]);
                }
            }
        }
    }
    /**
     * Get the unique bot API file ID of a Telegram file, which doesn't change with its file reference.
     *
     * Returns null for files that aren't downloaded from Telegram.
     */
    private function getUniqueBotApiFileId(mixed $media): ?string
    {
        if ($media instanceof BotApiFileId) {
            $media = $media->fileId;
        }
        if ($media instanceof Message) {
            $media = $media->media;
        }
        if ($media instanceof Media) {
            return $media->botApiFileUniqueId;
        }
        if (\is_string($media)) {
            return InlineThumbnail::getUniqueFileId($media, $this->getInlineFileIdKey()) ?? FileId::fromBotAPI($media)->getUniqueBotAPI();
        }
        if (!\is_array($media)) {
            return null;
        }
        if (!isset($media['_'])) {
            $media = self::extractBotAPIFile($media) ?? $media;
            if (isset($media['file_unique_id'])) {
                return $media['file_unique_id'];
            }
            return isset($media['file_id']) ? FileId::fromBotAPI($media['file_id'])->getUniqueBotAPI() : null;
        }
        // The media that is downloaded, as in getDownloadInfo().
        switch ($media['_']) {
            case 'updateNewMessage':
            case 'updateNewChannelMessage':
            case 'updateEditMessage':
            case 'updateEditChannelMessage':
            case 'updateNewEncryptedMessage':
                return $this->getUniqueBotApiFileId($media['message']);
            case 'message':
            case 'storyItem':
                return $this->getUniqueBotApiFileId($media['media']);
            case 'encryptedMessage':
                if ($media['decrypted_message']['media']['_'] !== 'decryptedMessageMediaExternalDocument') {
                    $media['decrypted_message']['media']['file'] = $media['file'];
                }
                return $this->getUniqueBotApiFileId($media['decrypted_message']['media']);
            case 'wallPaper':
                return $this->getUniqueBotApiFileId($media['document']);
            case 'user':
            case 'channel':
            case 'chat':
            case 'updateUserPhoto':
                return $this->profilePhotoToBotAPI($media)['file_unique_id'];
            case 'document':
            case 'decryptedMessageMediaExternalDocument':
                $media = ['_' => 'messageMediaDocument', 'ttl_seconds' => 0, 'document' => $media];
                // no break
            case 'photo':
            case 'messageMediaPhoto':
            case 'messageMediaDocument':
            case 'decryptedMessageMediaPhoto':
            case 'decryptedMessageMediaVideo':
            case 'decryptedMessageMediaDocument':
            case 'decryptedMessageMediaAudio':
                return self::extractBotAPIFile($this->MTProtoToBotAPI($media))['file_unique_id'] ?? null;
            default:
                // Photo sizes are inlined, or downloaded as part of their photo.
                return null;
        }
    }
    /**
     * Reupload telegram file.
     *
     * @param mixed    $media     Telegram file
     * @param callable $cb        Callback
     * @param boolean  $encrypted Whether to encrypt file for secret chats
     *
     * @return array InputFile constructor
     */
    public function uploadFromTgfile(mixed $media, ?callable $cb = null, bool $encrypted = false, ?Cancellation $cancellation = null): array
    {
        if (\is_object($media) && $media instanceof FileCallbackInterface) {
            $cb = $media;
            $media = $media->getFile();
        }
        $resumeKey = $this->getUniqueBotApiFileId($media);
        $media = ($this->getDownloadInfo($media));
        if (!isset($media['size'], $media['mime'])) {
            throw new Exception('Wrong file provided!');
        }
        $size = $media['size'];
        $mime = $media['mime'];
        if ($resumeKey !== null) {
            $resumeKey = 'tgfile:'.$resumeKey.':'.($media['key_fingerprint'] ?? '');
        }
        $stateKey = $this->getUploadStateKey($resumeKey, $size, $encrypted);
        if ($stateKey !== null && isset($this->resumableUploads[$stateKey])) {
            // Resume an interrupted upload reading the file sequentially, skipping the parts that were already uploaded without downloading them.
            return $this->uploadFromStreamInternal($this->downloadToReturnedStream($media, cancellation: $cancellation), $size, $mime, '', $cb, $encrypted, $cancellation, $resumeKey);
        }
        $chunk_size = 512 * 1024;
        $bridge = new class($size, $chunk_size, $cb, $cancellation) {
            /**
             * Read promises.
             *
             * @var array<DeferredFuture>
             */
            private array $read = [];
            /**
             * Read promises (write lenth).
             *
             * @var array<int>
             */
            private array $wrote = [];
            /**
             * Write promises.
             *
             * @var array<DeferredFuture>
             */
            private array $write = [];
            /**
             * Part size.
             *
             */
            private int $partSize;
            /**
             * Offset for callback.
             *
             */
            private int $offset = 0;
            /**
             * Callback.
             *
             * @var ?callable
             */
            private $cb;
            /**
             * Constructor.
             *
             * @param integer       $size     Total file size
             * @param integer       $partSize Part size
             * @param null|callable $cb       Callback
             */
            public function __construct(int $size, int $partSize, ?callable $cb, private readonly ?Cancellation $cancellation)
            {
                for ($x = 0; $x < $size; $x += $partSize) {
                    $this->read[] = new DeferredFuture();
                    $this->write[] = new DeferredFuture();
                    $this->wrote[] = $size - $x < $partSize ? $size - $x : $partSize;
                }
                $this->partSize = $partSize;
                $this->cb = $cb;
            }
            /**
             * Read chunk.
             *
             * @param integer $offset Offset
             * @param integer $size   Chunk size
             */
            public function read(int $offset, int $size): string
            {
                $offset /= $this->partSize;
                return $this->write[$offset]->getFuture()->await($this->cancellation);
            }
            /**
             * Write chunk.
             *
             * @param string  $data   Data
             * @param integer $offset Offset
             */
            public function write(string $data, int $offset): void
            {
                $offset /= $this->partSize;
                $this->write[$offset]->complete($data);
                $this->read[$offset]->getFuture()->await($this->cancellation);
            }
            /**
             * Read callback, called when the chunk is read and fully resent.
             *
             * @param mixed ...$params Params to be passed to cb
             */
            public function callback(mixed ...$params): mixed
            {
                $offset = $this->offset++;
                $this->read[$offset]->complete($this->wrote[$offset]);
                if ($this->cb) {
                    return ($this->cb)(...$params);
                }
                return null;
            }
        };
        $reader = $bridge->read(...);
        $writer = $bridge->write(...);
        $cb = $bridge->callback(...);
        $read = async($this->uploadFromCallableInternal(...), $reader, $size, $mime, '', $cb, true, $encrypted, $cancellation, $resumeKey);
        $write = async($this->downloadToCallable(...), $media, $writer, null, true, 0, -1, $chunk_size, $cancellation);
        [$res] = await([$read, $write], $cancellation);
        return $res;
    }

    private static function inputGeoPoint(array $geo): array
    {
        $point = ['_' => 'inputGeoPoint', 'lat' => $geo['lat'], 'long' => $geo['long']];
        if (isset($geo['accuracy_radius'])) {
            $point['accuracy_radius'] = $geo['accuracy_radius'];
        }
        return $point;
    }

    private function genAllFile($media)
    {
        $res = [$this->TL->getConstructors()->findByPredicate($media['_'])['type'] => $media];
        switch ($media['_']) {
            case 'messageMediaPoll':
                $res['Poll'] = $media['poll'];
                $res['InputMedia'] = ['_' => 'inputMediaPoll', 'poll' => $res['Poll']];
                if (isset($res['Poll']['quiz']) && $res['Poll']['quiz']) {
                    if (empty($media['results']['results'])) {
                        //quizzes need a correct answer
                        throw new Exception('No poll results');
                    }
                    foreach ($media['results']['results'] as $answer) {
                        if ($answer['correct']) {
                            $res['InputMedia']['correct_answers'][] = $answer['option'];
                        }
                    }
                }
                if (isset($media['results']['solution'])) {
                    $res['InputMedia']['solution'] = $media['results']['solution'];
                }
                if (isset($media['results']['solution_entities'])) {
                    $res['InputMedia']['solution_entities'] = $media['results']['solution_entities'];
                }
                break;
            case 'updateMessagePoll':
                $res['Poll'] = $media['poll'];
                $res['InputMedia'] = ['_' => 'inputMediaPoll', 'poll' => $res['Poll']];
                $res['MessageMedia'] = ['_' => 'messageMediaPoll', 'poll' => $res['Poll'], 'results' => $media['results']];
                if (isset($res['Poll']['quiz']) && $res['Poll']['quiz']) {
                    if (empty($media['results']['results'])) {
                        //quizzes need a correct answer
                        throw new Exception('No poll results');
                    }
                    foreach ($media['results']['results'] as $answer) {
                        if ($answer['correct']) {
                            $res['InputMedia']['correct_answers'][] = $answer['option'];
                        }
                    }
                }
                if (isset($media['results']['solution'])) {
                    $res['InputMedia']['solution'] = $media['results']['solution'];
                }
                if (isset($media['results']['solution_entities'])) {
                    $res['InputMedia']['solution_entities'] = $media['results']['solution_entities'];
                }
                break;
            case 'messageMediaPhoto':
                if (!isset($media['photo']['access_hash'])) {
                    throw new Exception('No access hash');
                }
                $res['Photo'] = $media['photo'];
                $res['InputPhoto'] = ['_' => 'inputPhoto', 'id' => $media['photo']['id'], 'access_hash' => $media['photo']['access_hash'], 'file_reference' => $this->referenceDatabase->getReference(ReferenceDatabase::PHOTO, $media['photo'])];
                $res['InputMedia'] = ['_' => 'inputMediaPhoto', 'id' => $res['InputPhoto']];
                if (isset($media['ttl_seconds'])) {
                    $res['InputMedia']['ttl_seconds'] = $media['ttl_seconds'];
                }
                if (isset($media['spoiler'])) {
                    $res['InputMedia']['spoiler'] = $media['spoiler'];
                }
                break;
            case 'messageMediaDocument':
                if (!isset($media['document']['access_hash'])) {
                    throw new Exception('No access hash');
                }
                $res['Document'] = $media['document'];
                $res['InputDocument'] = ['_' => 'inputDocument', 'id' => $media['document']['id'], 'access_hash' => $media['document']['access_hash'], 'file_reference' => $this->referenceDatabase->getReference(ReferenceDatabase::DOCUMENT, $media['document'])];
                $res['InputMedia'] = ['_' => 'inputMediaDocument', 'id' => $res['InputDocument']];
                if (isset($media['ttl_seconds'])) {
                    $res['InputMedia']['ttl_seconds'] = $media['ttl_seconds'];
                }
                break;
            case 'messageMediaDice':
                $res['InputMedia'] = ['_' => 'inputMediaDice', 'emoticon' => $media['emoticon']];
                break;
            case 'poll':
                $res['InputMedia'] = ['_' => 'inputMediaPoll', 'poll' => $res['Poll']];
                break;
            case 'document':
                if (!isset($media['access_hash'])) {
                    throw new Exception('No access hash');
                }
                $res['InputDocument'] = ['_' => 'inputDocument', 'id' => $media['id'], 'access_hash' => $media['access_hash'], 'file_reference' => $this->referenceDatabase->getReference(ReferenceDatabase::DOCUMENT, $media)];
                $res['InputMedia'] = ['_' => 'inputMediaDocument', 'id' => $res['InputDocument']];
                $res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $media];
                break;
            case 'photo':
                if (!isset($media['access_hash'])) {
                    throw new Exception('No access hash');
                }
                $res['InputPhoto'] = ['_' => 'inputPhoto', 'id' => $media['id'], 'access_hash' => $media['access_hash'], 'file_reference' => $this->referenceDatabase->getReference(ReferenceDatabase::PHOTO, $media)];
                $res['InputMedia'] = ['_' => 'inputMediaPhoto', 'id' => $res['InputPhoto']];
                $res['MessageMedia'] = ['_' => 'messageMediaPhoto', 'photo' => $media];
                break;
            case 'messageMediaStory':
                $media['_'] = 'inputMediaStory';
                $res['InputMedia'] = $media;
                break;
            case 'messageMediaWebPage':
                // The link preview of a text message (webPageNotModified has no URL)
                if (!isset($media['webpage']['url'])) {
                    throw new Exception('The link preview has no URL');
                }
                $res['InputMedia'] = [
                    '_' => 'inputMediaWebPage',
                    'url' => $media['webpage']['url'],
                    'force_large_media' => $media['force_large_media'] ?? false,
                    'force_small_media' => $media['force_small_media'] ?? false,
                    // Don't fail if no preview can be generated (webPageEmpty: there was none)
                    'optional' => true,
                ];
                break;
            case 'messageMediaGeo':
                if ($media['geo']['_'] !== 'geoPoint') {
                    throw new Exception('Empty location');
                }
                $res['InputMedia'] = ['_' => 'inputMediaGeoPoint', 'geo_point' => self::inputGeoPoint($media['geo'])];
                break;
            case 'messageMediaVenue':
                if ($media['geo']['_'] !== 'geoPoint') {
                    throw new Exception('Empty location');
                }
                $res['InputMedia'] = [
                    '_' => 'inputMediaVenue',
                    'geo_point' => self::inputGeoPoint($media['geo']),
                    'title' => $media['title'],
                    'address' => $media['address'],
                    'provider' => $media['provider'],
                    'venue_id' => $media['venue_id'],
                    'venue_type' => $media['venue_type'],
                ];
                break;
            case 'messageMediaContact':
                $res['InputMedia'] = [
                    '_' => 'inputMediaContact',
                    'phone_number' => $media['phone_number'],
                    'first_name' => $media['first_name'],
                    'last_name' => $media['last_name'],
                    'vcard' => $media['vcard'],
                ];
                break;
            default:
                throw new Exception("Could not convert media object of type {$media['_']}");
        }
        return $res;
    }
    /**
     * Get info about file.
     *
     * @param mixed $constructor File ID
     */
    public function getFileInfo(mixed $constructor): array
    {
        if ($constructor instanceof Message) {
            $constructor = $constructor->media;
        }
        if ($constructor instanceof Media) {
            $constructor = $constructor->botApiFileId;
        }
        if (\is_string($constructor)) {
            $constructor = $this->unpackFileId($constructor);
            if (isset($constructor['MessageMedia'])) {
                $constructor = $constructor['MessageMedia'];
            } elseif (isset($constructor['InputMedia'])) {
                return $constructor;
            } elseif (isset($constructor['Chat']) || isset($constructor['User'])) {
                throw new Exception("Chat photo file IDs can't be reused to resend chat photos, please use getPwrChat()['photo'], instead");
            }
        }
        switch ($constructor['_']) {
            case 'updateNewMessage':
            case 'updateNewChannelMessage':
            case 'updateEditMessage':
            case 'updateEditChannelMessage':
                $constructor = $constructor['message'];
                // no break
            case 'message':
                $constructor = $constructor['media'];
        }
        return $this->genAllFile($constructor);
    }
    /**
     * Gets info of the propic of a user.
     */
    public function getPropicInfo($data): ?Photo
    {
        if (!$this->getSettings()->getDb()->getEnableFullPeerDb()) {
            throw new AssertionError("getPropicInfo cannot be used if the full peer database is disabled!");
        }
        return $this->getPwrChat($data, false)['photo'] ?? null;
    }
    /**
     * Extract file info from bot API message.
     *
     * @param array $info Bot API message object
     *
     * @psalm-pure
     */
    public static function extractBotAPIFile(array $info): array|null
    {
        foreach (FileIdType::cases() as $type) {
            if (isset($info[$type->value]) && \is_array($info[$type->value])) {
                $method = $type->value;
                break;
            }
        }
        if (!isset($method)) {
            return null;
        }
        $info = $info[$method];
        if ($method === 'photo') {
            $info = array_values($info);
            $cur = $info[0];
            foreach ($info as $n) {
                /** @psalm-suppress InvalidArrayAccess */
                if ($n['width'] * $n['height'] > $cur['width'] * $cur['height']) {
                    $cur = $n;
                }
            }
            $info = $cur;
        }
        $info['file_type'] = $method;
        return $info;
    }
    /**
     * Get download info of file
     * Returns an array with the following structure:.
     *
     * `$info['ext']` - The file extension
     * `$info['name']` - The file name, without the extension
     * `$info['mime']` - The file mime type
     * `$info['size']` - The file size
     *
     * @param mixed $messageMedia File ID
     *
     * @return array{
     *      ext: string,
     *      name: string,
     *      mime: string,
     *      size: int,
     *      InputFileLocation: array,
     *      key_fingerprint?: string,
     *      key?: string,
     *      iv?: string,
     *      thumb_size?: string
     * }
     */
    public function getDownloadInfo(mixed $messageMedia): array
    {
        if ($messageMedia instanceof BotApiFileId) {
            $res = $this->getDownloadInfo($messageMedia->fileId);
            $res['size'] = $messageMedia->size;
            $pathinfo = pathinfo($messageMedia->fileName);
            if (isset($pathinfo['extension'])) {
                $res['ext'] = '.'.$pathinfo['extension'];
            } else {
                $res['ext'] = '';
            }
            $res['name'] = $pathinfo['filename'];
            return $res;
        }
        if ($messageMedia instanceof Message) {
            $messageMedia = $messageMedia->media;
        }
        if ($messageMedia instanceof Media) {
            return $messageMedia->getDownloadInfo();
        }
        if (\is_string($messageMedia)) {
            $inline = InlineThumbnail::unpackFileId($messageMedia, $this->getInlineFileIdKey());
            if ($inline !== null) {
                return $inline;
            }
            $messageMedia = $this->unpackFileId($messageMedia);
            if (isset($messageMedia['InputFileLocation'])) {
                return $messageMedia;
            }
            $messageMedia = $messageMedia['MessageMedia'];
        }
        if (!isset($messageMedia['_'])) {
            if (!isset($messageMedia['InputFileLocation']) && !isset($messageMedia['file_id'])) {
                $messageMedia = self::extractBotAPIFile($messageMedia) ?? $messageMedia;
            }
            if (isset($messageMedia['file_id'])) {
                $res = $this->getDownloadInfo($messageMedia['file_id']);
                $pathinfo = pathinfo($messageMedia['file_name']);
                if (isset($pathinfo['extension'])) {
                    $res['ext'] = '.'.$pathinfo['extension'];
                } else {
                    $res['ext'] = '';
                }
                $res['name'] = $pathinfo['filename'];
                return $res;
            }
            return $messageMedia;
        }
        $res = [];
        switch ($messageMedia['_']) {
            // Updates
            case 'updateNewMessage':
            case 'updateNewChannelMessage':
            case 'updateEditMessage':
            case 'updateEditChannelMessage':
                $messageMedia = $messageMedia['message'];
                // no break
            case 'message':
                return $this->getDownloadInfo($messageMedia['media']);
            case 'storyItem':
                return $this->getDownloadInfo($messageMedia['media']);
            case 'updateNewEncryptedMessage':
                $messageMedia = $messageMedia['message'];
                // Secret media
                // no break
            case 'encryptedMessage':
                if ($messageMedia['decrypted_message']['media']['_'] !== 'decryptedMessageMediaExternalDocument') {
                    $messageMedia['decrypted_message']['media']['file'] = $messageMedia['file'];
                }
                return $this->getDownloadInfo($messageMedia['decrypted_message']['media']);
            case 'decryptedMessageMediaPhoto':
            case 'decryptedMessageMediaVideo':
            case 'decryptedMessageMediaDocument':
            case 'decryptedMessageMediaAudio':
                $res['InputFileLocation'] = ['_' => 'inputEncryptedFileLocation', 'id' => $messageMedia['file']['id'], 'access_hash' => $messageMedia['file']['access_hash'], 'dc_id' => $messageMedia['file']['dc_id']];
                $res['size'] = $messageMedia['size'];
                $res['key_fingerprint'] = $messageMedia['file']['key_fingerprint'];
                $res['key'] = $messageMedia['key'];
                $res['iv'] = $messageMedia['iv'];
                if (isset($messageMedia['file_name'])) {
                    $pathinfo = pathinfo($messageMedia['file_name']);
                    if (isset($pathinfo['extension'])) {
                        $res['ext'] = '.'.$pathinfo['extension'];
                    } else {
                        $res['ext'] = '';
                    }
                    $res['name'] = $pathinfo['filename'];
                }
                if (isset($messageMedia['mime_type'])) {
                    $res['mime'] = $messageMedia['mime_type'];
                } elseif ($messageMedia['_'] === 'decryptedMessageMediaPhoto') {
                    $res['mime'] = 'image/jpeg';
                }
                if (isset($messageMedia['attributes'])) {
                    foreach ($messageMedia['attributes'] as $attribute) {
                        switch ($attribute['_']) {
                            case 'documentAttributeFilename':
                                $pathinfo = pathinfo($attribute['file_name']);
                                if (isset($pathinfo['extension'])) {
                                    $res['ext'] = '.'.$pathinfo['extension'];
                                } else {
                                    $res['ext'] = '';
                                }
                                $res['name'] = $pathinfo['filename'];
                                break;
                            case 'documentAttributeAudio':
                                $audio = $attribute;
                                break;
                        }
                    }
                }
                if (isset($audio) && isset($audio['title']) && !isset($res['name'])) {
                    $res['name'] = $audio['title'];
                    if (isset($audio['performer'])) {
                        $res['name'] .= ' - '.$audio['performer'];
                    }
                }
                if (!isset($res['ext']) || $res['ext'] === '') {
                    $res['ext'] = Tools::getExtensionFromLocation($res['InputFileLocation'], Tools::getExtensionFromMime($res['mime'] ?? 'image/jpeg'));
                }
                if (!isset($res['mime']) || $res['mime'] === '') {
                    $res['mime'] = Tools::getMimeFromExtension($res['ext'], 'image/jpeg');
                }
                if (!isset($res['name']) || $res['name'] === '') {
                    $res['name'] = Tools::unpackSignedLongString($messageMedia['file']['access_hash']);
                }
                return $res;
                // Wallpapers
            case 'wallPaper':
                return $this->getDownloadInfo($messageMedia['document']);
                // Photos
            case 'photo':
                $messageMedia = ['_' => 'messageMediaPhoto', 'photo' => $messageMedia, 'ttl_seconds' => 0];
                // no break
            case 'messageMediaPhoto':
                $res['MessageMedia'] = $messageMedia;
                $messageMedia = $messageMedia['photo'];
                $size = Tools::maxSize($messageMedia['sizes']);
                if (isset($size['volume_id'])) {
                    $res['InputFileLocation'] = [
                        '_' => 'inputPhotoLegacyFileLocation',
                        'id' => $messageMedia['id'],
                        'access_hash' => $messageMedia['access_hash'],
                        'dc_id' => $messageMedia['dc_id'],
                        'file_reference' => $this->referenceDatabase->getReference(ReferenceDatabase::PHOTO, $messageMedia),
                        'volume_id' => $size['volume_id'],
                        'local_id' => $size['local_id'],
                        'secret' => $size['secret'],
                    ];
                } else {
                    $res = array_merge($res, $this->getDownloadInfo($size));
                    $res['InputFileLocation'] = [
                        '_' => 'inputPhotoFileLocation',
                        'id' => $messageMedia['id'],
                        'access_hash' => $messageMedia['access_hash'],
                        'dc_id' => $messageMedia['dc_id'],
                        'file_reference' => $this->referenceDatabase->getReference(ReferenceDatabase::PHOTO, $messageMedia),
                        'thumb_size' => $res['thumb_size'] ?? 'x',
                    ];
                }
                $res['name'] = Tools::unpackSignedLongString($messageMedia['id']).'_'.($res['thumb_size'] ?? 'x').'_'.$messageMedia['dc_id'];
                $res['ext'] = '.jpg';
                $res['mime'] = 'image/jpeg';
                return $res;
            case 'user':
            case 'folder':
            case 'channel':
            case 'chat':
            case 'updateUserPhoto':
                $res = ($this->getDownloadInfo($messageMedia['photo']));
                $res['InputFileLocation'] = [
                    '_' => 'inputPeerPhotoFileLocation',
                    'big' => true,
                    'peer' => $this->getInfo($messageMedia, \danog\MadelineProto\API::INFO_TYPE_ID),
                    'photo_id' => $res['InputFileLocation']['photo_id'],
                ];
                $res['name'] = Tools::unpackSignedLongString($messageMedia['id']).'_'.($res['thumb_size'] ?? 'x').'_'.$messageMedia['photo']['dc_id'];
                $res['ext'] = '.jpg';
                $res['mime'] = 'image/jpeg';
                return $res;
            case 'userProfilePhoto':
            case 'chatPhoto':
                $res['InputFileLocation']['has_video'] = $messageMedia['has_video'] ?? false;
                $res['InputFileLocation']['photo_id'] = $messageMedia['photo_id'];
                $res['InputFileLocation']['dc_id'] = $messageMedia['dc_id'];
                return $res;
            case 'photoStrippedSize':
                // The JPEG, as downloaded by InlineThumbnail.
                $res['data'] = Tools::inflateStripped((string) $messageMedia['bytes']);
                $res['size'] = \strlen($res['data']);
                $res['thumb_size'] = 'JPG';
                return $res;
            case 'photoCachedSize':
                $res['data'] = (string) $messageMedia['bytes'];
                $res['size'] = \strlen($res['data']);
                $res['thumb_size'] = $messageMedia['type'];
                return $res;
            case 'photoSize':
                $res['thumb_size'] = $messageMedia['type'];
                //$res['thumb_size'] = $size;
                if (isset($messageMedia['size'])) {
                    $res['size'] = $messageMedia['size'];
                }
                return $res;
            case 'photoSizeProgressive':
                $res['thumb_size'] = $messageMedia['type'];
                if (isset($messageMedia['sizes'])) {
                    $res['size'] = end($messageMedia['sizes']);
                }
                return $res;
                // Documents
            case 'decryptedMessageMediaExternalDocument':
            case 'document':
                $messageMedia = ['_' => 'messageMediaDocument', 'ttl_seconds' => 0, 'document' => $messageMedia];
                // no break
            case 'messageMediaDocument':
                $res['MessageMedia'] = $messageMedia;
                foreach ($messageMedia['document']['attributes'] as $attribute) {
                    switch ($attribute['_']) {
                        case 'documentAttributeFilename':
                            $pathinfo = pathinfo($attribute['file_name']);
                            if (isset($pathinfo['extension'])) {
                                $res['ext'] = '.'.$pathinfo['extension'];
                            }
                            $res['name'] = $pathinfo['filename'];
                            break;
                        case 'documentAttributeAudio':
                            $audio = $attribute;
                            break;
                    }
                }
                if (isset($audio) && isset($audio['title']) && !isset($res['name'])) {
                    $res['name'] = $audio['title'];
                    if (isset($audio['performer'])) {
                        $res['name'] .= ' - '.$audio['performer'];
                    }
                }
                $res['InputFileLocation'] = ['_' => 'inputDocumentFileLocation', 'id' => $messageMedia['document']['id'], 'access_hash' => $messageMedia['document']['access_hash'], 'version' => $messageMedia['document']['version'] ?? 0, 'dc_id' => $messageMedia['document']['dc_id'], 'file_reference' => $this->referenceDatabase->getReference(ReferenceDatabase::DOCUMENT, $messageMedia['document'])];
                if (!isset($res['ext']) || $res['ext'] === '') {
                    $res['ext'] = Tools::getExtensionFromLocation($res['InputFileLocation'], Tools::getExtensionFromMime($messageMedia['document']['mime_type']));
                }
                if (!isset($res['name']) || $res['name'] === '') {
                    $res['name'] = Tools::unpackSignedLongString($messageMedia['document']['access_hash']);
                }
                if (isset($messageMedia['document']['size'])) {
                    $res['size'] = $messageMedia['document']['size'];
                }
                $res['name'] .= '_'.Tools::unpackSignedLongString($messageMedia['document']['id']);
                $res['mime'] = $messageMedia['document']['mime_type'];
                return $res;
            default:
                throw new Exception('Invalid constructor provided: '.$messageMedia['_']);
        }
    }
    /**
     * Download file to directory.
     *
     * @param mixed                        $messageMedia File to download
     * @param string|FileCallbackInterface $dir          Directory where to download the file
     * @param callable                     $cb           Callback
     *
     * @return non-empty-string Downloaded file name
     */
    public function downloadToDir(mixed $messageMedia, string|FileCallbackInterface $dir, ?callable $cb = null, ?Cancellation $cancellation = null): string
    {
        if (\is_object($dir) && $dir instanceof FileCallbackInterface) {
            $cb = $dir;
            $dir = $dir->getFile();
        }
        $messageMedia = ($this->getDownloadInfo($messageMedia));
        return $this->downloadToFile($messageMedia, $dir.'/'.$messageMedia['name'].$messageMedia['ext'], $cb, $cancellation);
    }
    /**
     * Download file.
     *
     * @param mixed                        $messageMedia File to download
     * @param string|FileCallbackInterface $file         Downloaded file path
     * @param callable                     $cb           Callback
     *
     * @return non-empty-string Downloaded file name
     */
    public function downloadToFile(mixed $messageMedia, string|FileCallbackInterface $file, ?callable $cb = null, ?Cancellation $cancellation = null): string
    {
        if (\is_object($file) && $file instanceof FileCallbackInterface) {
            $cb = $file;
            $file = $file->getFile();
        }
        $file = Tools::absolute(preg_replace('|/+|', '/', $file));
        if (!file_exists($file)) {
            touch($file);
        }
        $file = realpath($file);
        Assert::notEmpty($file);
        $messageMedia = ($this->getDownloadInfo($messageMedia));
        $size = getSize($file);
        $stream = openFile($file, 'cb');
        $this->logger->logger('Waiting for lock of file to download...');
        $unlock = Tools::flock("$file.lock", LOCK_EX);
        $this->logger->logger('Got lock of file to download');
        async($this->downloadToStream(...), $messageMedia, $stream, $cb, $size, -1, $cancellation)->finally(static function () use ($stream, $unlock, $file): void {
            $stream->close();
            $unlock();
            try {
                deleteFile("$file.lock");
            } catch (\Throwable) {
            }
        })->await($cancellation);
        return $file;
    }
    /**
     * Download file to callable.
     * The callable must accept two parameters: string $payload, int $offset
     * The callable will be called (possibly out of order, depending on the value of $seekable).
     *
     * @param mixed                          $messageMedia File to download
     * @param callable|FileCallbackInterface $callable     Chunk callback
     * @param callable                       $cb           Status callback
     * @param bool                           $seekable     Whether the callable can be called out of order
     * @param int                            $offset       Offset where to start downloading
     * @param int                            $end          Offset where to stop downloading (inclusive)
     * @param int                            $part_size    Size of each chunk
     */
    public function downloadToCallable(mixed $messageMedia, callable $callable, ?callable $cb = null, bool $seekable = true, int $offset = 0, int $end = -1, ?int $part_size = null, ?Cancellation $cancellation = null): void
    {
        $messageMedia = ($this->getDownloadInfo($messageMedia));
        if (\is_object($callable) && $callable instanceof FileCallbackInterface) {
            $cb = $callable;
            $callable = $callable->getFile();
        }
        if (!\is_callable($callable)) {
            throw new Exception('Wrong callable provided');
        }
        if ($cb === null) {
            $cb = function (float $percent, float $speed, float $time): void {
                $this->logger->logger('Download status: '.$percent.'%', Logger::NOTICE);
            };
        } else {
            $cb = static function (float $percent, float $speed, float $time) use ($cb): void {
                EventLoop::queue(static function () use ($percent, $speed, $time, $cb): void {
                    try {
                        $cb($percent, $speed, $time);
                    } catch (ChannelException) {
                        // The IPC client this progress was meant for is gone.
                    }
                });
            };
        }
        if ($end === -1 && isset($messageMedia['size']) && $messageMedia['size'] !== 0) {
            $end = $messageMedia['size'];
        }
        if (isset($messageMedia['data'])) {
            // Inlined in the media (like the stripped preview of a photo): served from memory.
            $data = (string) $messageMedia['data'];
            $payload = substr($data, $offset, $end === -1 ? null : $end - $offset);
            if ($payload !== '') {
                $callable($payload, $offset);
            }
            $cb(100.0, 0.0, 0.0);
            return;
        }
        $part_size ??= 1024 * 1024;
        $parallel_chunks = $this->settings->getFiles()->getDownloadParallelChunks();
        if (isset($messageMedia['InputFileLocation']['dc_id'])) {
            $datacenter = $this->isTestMode() ? 10_000 + $messageMedia['InputFileLocation']['dc_id'] : $messageMedia['InputFileLocation']['dc_id'];
        } else {
            $datacenter = $this->loginState->getState()->authorizedDc;
        }
        if ($this->datacenter->has(-$datacenter)) {
            $datacenter = -$datacenter;
        }
        if (isset($messageMedia['key'])) {
            $messageMedia['key'] = (string) $messageMedia['key'];
            $messageMedia['iv'] = (string) $messageMedia['iv'];
            $digest = hash('md5', $messageMedia['key'].$messageMedia['iv'], true);
            $fingerprint = Tools::unpackSignedInt(substr($digest, 0, 4) ^ substr($digest, 4, 4));
            if ($fingerprint !== $messageMedia['key_fingerprint']) {
                throw new Exception('Fingerprint mismatch!');
            }
            $ige = IGE::getInstance($messageMedia['key'], $messageMedia['iv']);
            $seekable = false;
        }
        if ($offset === $end) {
            $cb(100.0, 0.0, 0);
            return;
        }
        $params = [];
        $start_at = $offset % $part_size;
        $probable_end = $end !== -1 ? $end : 512 * 1024 * 8000;
        $breakOut = false;
        for ($x = $offset - $start_at; $x < $probable_end; $x += $part_size) {
            $end_at = $part_size;
            if ($end !== -1 && $x + $part_size > $end) {
                $end_at = $end % $part_size;
                $breakOut = true;
            }
            $params[] = ['offset' => $x, 'limit' => $part_size, 'part_start_at' => $start_at, 'part_end_at' => $end_at];
            $start_at = 0;
            if ($breakOut) {
                break;
            }
        }
        if (!$params) {
            $cb(100, 0, 0);
            return;
        }
        $count = \count($params);
        $cdn = false;
        if (isset($messageMedia['key']) && $params[0]['offset'] > 0) {
            // IGE decryption is chained: each block depends on the previous plaintext block,
            // so resuming requires decrypting everything before the first requested part.
            $this->logger->logger("Decrypting the first {$params[0]['offset']} bytes of an encrypted file to start downloading from offset $offset");
            $skip = static fn (string $payload): int => \strlen($payload);
            $noop = static function (int $bytes): void {
            };
            for ($x = 0; $x < $params[0]['offset']; $x += $part_size) {
                $this->downloadPart($messageMedia, $cdn, $datacenter, $old_dc, $ige, $noop, ['offset' => $x, 'limit' => $part_size, 'part_start_at' => 0, 'part_end_at' => $part_size, 'previous_promise' => true], $skip, false, $cancellation);
            }
        }
        $params[0]['previous_promise'] = true;
        $start = microtime(true);
        $progress = new TransferProgress($cb, $count);
        $size = $this->downloadPart($messageMedia, $cdn, $datacenter, $old_dc, $ige, $progress->partDone(...), $initParam = array_shift($params), $callable, $seekable, $cancellation);
        if ($initParam['part_end_at'] - $initParam['part_start_at'] !== $size) {
            // Premature end for undefined length files
            $cb(100, $progress->getSpeed(), $progress->getTime());
            return;
        }
        $parallel_chunks = $seekable ? $parallel_chunks : 2;
        if ($params) {
            $previous_promise = true;
            $promises = [];
            $finished = false;
            foreach ($params as $param) {
                $cancellation?->throwIfRequested();
                $param['previous_promise'] = $previous_promise;
                $previous_promise = async($this->downloadPart(...), $messageMedia, $cdn, $datacenter, $old_dc, $ige, $progress->partDone(...), $param, $callable, $seekable, $cancellation)->ignore();
                $previous_promise->map(static function (int $res) use (&$size, &$finished): void {
                    $size += $res;
                    if ($res === 0) {
                        $finished = true;
                    }
                })->ignore();
                $promises[] = $previous_promise;
                if (\count($promises) === $parallel_chunks) {
                    // 20 mb at a time, for a typical bandwidth of 1gbps
                    awaitFirst($promises, $cancellation);
                    foreach ($promises as $k => $p) {
                        if ($p->isComplete()) {
                            unset($promises[$k]);
                            break;
                        }
                    }
                    if ($finished) {
                        break;
                    }
                }
            }
            if ($promises) {
                await($promises, $cancellation);
            }
        }
        $time = microtime(true) - $start;
        $speed = (int) ($size * 8 / $time) / 1000000;
        $this->logger->logger("Total download time: {$time}");
        $this->logger->logger("Total download speed: {$speed} mbps");
        if ($cdn) {
            $this->clearCdnHashes($messageMedia['file_token']);
        }
        if (!isset($messageMedia['size'])) {
            $cb(100.0, $speed, $time);
        }
    }
    /**
     * Download file part.
     *
     * @param array    $messageMedia File object
     * @param bool     $cdn          Whether this is a CDN file
     * @param int      $datacenter   DC ID
     * @param null|int $old_dc       Previous DC ID
     * @param IGE      $ige          IGE decryptor instance
     * @param callable(int): void $cb Status callback, called with the number of bytes of the part once it is written
     * @param array    $offset       Offset
     * @param callable $callable     Chunk callback
     * @param boolean  $seekable     Whether the download file is seekable
     */
    private function downloadPart(array &$messageMedia, bool &$cdn, int &$datacenter, ?int &$old_dc, ?IGE &$ige, callable $cb, array $offset, callable $callable, bool $seekable, ?Cancellation $cancellation): int
    {
        do {
            if (!$cdn) {
                $basic_param = ['location' => $messageMedia['InputFileLocation'], 'cdn_supported' => true, 'floodWaitLimit' => 0, 'cancellation' => $cancellation, 'specialMethodType' => SpecialMethodType::FILE_RELATED];
            } else {
                $basic_param = ['file_token' => $messageMedia['file_token'], 'floodWaitLimit' => 0, 'cancellation' => $cancellation, 'specialMethodType' => SpecialMethodType::FILE_RELATED];
            }
            do {
                $cancellation?->throwIfRequested();
                try {
                    $res = $this->methodCallAsyncRead(
                        $cdn ? 'upload.getCdnFile' : 'upload.getFile',
                        $basic_param + $offset,
                        $datacenter
                    );
                    break;
                } catch (FileRedirect $e) {
                    $datacenter = $e->dc;
                } catch (FloodWaitError) {
                    delay(1, cancellation: $cancellation);
                } catch (FloodPremiumWaitError $e) {
                    $e->wait($cancellation);
                } catch (FileTokenInvalidError) {
                    $cdn = false;
                    $datacenter = $this->loginState->getState()->authorizedDc;
                    continue 2;
                }
            } while (true);
            $cancellation?->throwIfRequested();

            if ($res['_'] === 'upload.fileCdnRedirect') {
                $cdn = true;
                $messageMedia['file_token'] = $res['file_token'];
                $messageMedia['cdn_key'] = $res['encryption_key'];
                $messageMedia['cdn_iv'] = $res['encryption_iv'];
                $datacenter = ($this->isTestMode() ? 10_000 : 0) + $res['dc_id'];
                if (!$this->datacenter->has($datacenter)) {
                    $this->config['expires'] = -1;
                    $this->getConfig();
                }
                $this->logger->logger('File is stored on CDN!', Logger::NOTICE);
                continue;
            } elseif ($res['_'] === 'upload.cdnFileReuploadNeeded') {
                $this->logger->logger('File is not stored on CDN, requesting reupload!', Logger::NOTICE);
                $this->config['expires'] = 0;
                $this->getConfig();
                try {
                    $this->addCdnHashes($messageMedia['file_token'], $this->methodCallAsyncRead('upload.reuploadCdnFile', ['file_token' => $messageMedia['file_token'], 'request_token' => $res['request_token'], 'cancellation' => $cancellation], $this->loginState->getState()->authorizedDc));
                } catch (FileTokenInvalidError|RequestTokenInvalidError) {
                    $cdn = false;
                    $datacenter = $this->loginState->getState()->authorizedDc;
                    continue;
                }
                continue;
            }
            $res['bytes'] = (string) $res['bytes'];
            if ($cdn === false && $res['type']['_'] === 'storage.fileUnknown' && $res['bytes'] === '') {
                $datacenter = 0;
            }
            while ($cdn === false && $res['type']['_'] === 'storage.fileUnknown' && $res['bytes'] === '' && $this->datacenter->has(++$datacenter)) {
                try {
                    $res = $this->methodCallAsyncRead('upload.getFile', $basic_param + $offset, $datacenter);
                } catch (FileRedirect $e) {
                    $datacenter = $e->dc;
                    break;
                }
            }
            $cancellation?->throwIfRequested();
            $res['bytes'] = (string) $res['bytes'];
            if ($res['bytes'] === '') {
                return 0;
            }
            if (isset($messageMedia['cdn_key'])) {
                $ivec = substr($messageMedia['cdn_iv'], 0, 12).pack('N', $offset['offset'] >> 4);
                $res['bytes'] = Crypt::ctrEncrypt($res['bytes'], $messageMedia['cdn_key'], $ivec);
                $this->checkCdnHash($messageMedia['file_token'], $offset['offset'], $res['bytes'], $cancellation);
            }
            if (!$seekable && $offset['previous_promise'] instanceof Future) {
                $offset['previous_promise']->await($cancellation);
            }
            if (isset($messageMedia['key'])) {
                // IGE decryption is chained, so parts must be decrypted in order (encrypted downloads are never seekable).
                $res['bytes'] = $ige->decrypt($res['bytes']);
            }
            if ($offset['part_start_at'] || $offset['part_end_at'] !== $offset['limit']) {
                $res['bytes'] = substr($res['bytes'], $offset['part_start_at'], $offset['part_end_at'] - $offset['part_start_at']);
            }
            $len = \strlen($res['bytes']);
            $res = $callable($res['bytes'], $offset['offset'] + $offset['part_start_at']);
            if ($res instanceof Future) {
                $res = $res->await($cancellation);
            }
            $cb($len);
            return $len;
        } while (true);
    }
    /**
     * @var array<string, array<int, array{limit: int, hash: string}>>
     */
    private array $cdn_hashes = [];
    /**
     * @psalm-external-mutation-free
     */
    private function addCdnHashes(string $file, array $hashes): void
    {
        foreach ($hashes as $hash) {
            $this->cdn_hashes[$file] ??= [];
            $this->cdn_hashes[$file][$hash['offset']] = ['limit' => $hash['limit'], 'hash' => (string) $hash['hash']];
        }
    }
    private function checkCdnHash(string $file, int $offset, string $data, ?Cancellation $cancellation): void
    {
        while (\strlen($data)) {
            if (!isset($this->cdn_hashes[$file][$offset])) {
                $this->addCdnHashes($file, $this->methodCallAsyncRead('upload.getCdnFileHashes', ['file_token' => $file, 'offset' => $offset, 'cancellation' => $cancellation], $this->loginState->getState()->authorizedDc));
            }
            if (!isset($this->cdn_hashes[$file][$offset])) {
                throw new Exception('Could not fetch CDN hashes for offset '.$offset);
            }
            if (hash('sha256', substr($data, 0, $this->cdn_hashes[$file][$offset]['limit']), true) !== $this->cdn_hashes[$file][$offset]['hash']) {
                throw new SecurityException('CDN hash mismatch for offset '.$offset);
            }
            $data = substr($data, $this->cdn_hashes[$file][$offset]['limit']);
            $offset += $this->cdn_hashes[$file][$offset]['limit'];
        }
    }

    /**
     * @psalm-external-mutation-free
     */
    private function clearCdnHashes(string $file): void
    {
        unset($this->cdn_hashes[$file]);
    }
}
<?php

declare(strict_types=1);

/**
 * Tools module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use Amp\ByteStream\ReadableStream;
use Amp\ByteStream\WritableStream;
use Amp\Cancellation;
use Amp\CancelledException;
use Amp\Future;
use Closure;
use Generator;
use Revolt\EventLoop;
use Throwable;

use const LOCK_NB;
use const LOCK_UN;
use function Amp\async;
use function Amp\ByteStream\getOutputBufferStream;
use function Amp\ByteStream\getStdin;
use function Amp\ByteStream\getStdout;
use function Amp\delay;

/**
 * Async tools.
 */
abstract class AsyncTools extends StrTools
{
    /**
     * Rethrow exception into event loop.
     */
    public static function rethrow(Throwable $e): void
    {
        EventLoop::queue(static fn () => throw $e);
    }
    /**
     * Fork a new green thread and execute the passed function in the background.
     *
     * @template T
     *
     * @param \Closure(...):T $callable Function to execute
     * @param mixed ...$args Arguments forwarded to the function when forking the thread.
     *
     * @return Future<T>
     *
     * @psalm-suppress InvalidScope
     */
    public static function callFork(callable|Generator|Future $callable, ...$args): Future
    {
        if (\is_callable($callable)) {
            $callable = async($callable, ...$args);
        }
        return $callable;
    }
    /**
     * Asynchronously lock a file
     * Resolves with a callbable that MUST eventually be called in order to release the lock.
     *
     * @param  string                                                          $file      File to lock
     * @param  integer                                                         $operation Locking mode
     * @param  float                                                           $polling   Polling interval
     * @param  ?Cancellation                                                   $token     Cancellation token
     * @param  ?Closure                                                        $failureCb Failure callback, called only once if the first locking attempt fails.
     * @return ($token is null ? (Closure(): void) : ((Closure(): void)|null))
     */
    public static function flock(string $file, int $operation, float $polling = 0.1, ?Cancellation $token = null, ?Closure $failureCb = null): ?Closure
    {
        if (!file_exists($file)) {
            touch($file);
        }
        $operation |= LOCK_NB;
        $res = fopen($file, 'c');
        do {
            $result = flock($res, $operation);
            if (!$result) {
                if ($failureCb) {
                    EventLoop::queue($failureCb);
                    $failureCb = null;
                }
                if ($token) {
                    if ($token->isRequested()) {
                        return null;
                    }
                    try {
                        delay($polling, true, $token);
                    } catch (CancelledException) {
                        return null;
                    }
                } else {
                    delay($polling);
                }
            }
        } while (!$result);
        return static function () use (&$res): void {
            if ($res) {
                flock($res, LOCK_UN);
                fclose($res);
                $res = null;
            }
        };
    }
    /**
     * Asynchronously sleep.
     *
     * @param float $time Number of seconds to sleep for
     */
    public static function sleep(float $time): void
    {
        delay($time);
    }

    /**
     * @internal
     */
    public static function getTimeoutCancellation(float $timeout, string $message = "Operation timed out"): Cancellation
    {
        // Deliberately not Amp\TimeoutCancellation: its timer is unreferenced,
        // but some callers rely on this timer alone to keep the event loop alive.
        return new ReferencedTimeoutCancellation($timeout, $message);
    }

    /**
     * Asynchronously read line.
     *
     * @param string  $prompt    Prompt
     * @param bool    $hidden    Whether to hide what is typed (for tokens, passwords and codes), when reading from a terminal
     * @param ?string $hideAfter Show what is typed until this character, censor what follows it with asterisks
     *                           (for prompts that take either a phone number or a bot token, which contains a
     *                           colon), when reading from a terminal
     */
    public static function readLine(string $prompt = '', ?Cancellation $cancel = null, bool $hidden = false, ?string $hideAfter = null): string
    {
        try {
            Magic::togglePeriodicLogging();
            $stdin = getStdin();
            $stdout = getStdout();
            if ($prompt) {
                $stdout->write($prompt);
            }
            static $lines = [''];
            if (($hidden || $hideAfter !== null) && \count($lines) < 2 && \defined('STDIN') && stream_isatty(STDIN)) {
                if (PHP_OS_FAMILY === 'Windows') {
                    return self::readHiddenLineWindows($cancel, $hidden ? null : $hideAfter);
                }
                if (!$hidden && ($restoreTerminal = self::disableEcho('-icanon -echo min 1')) !== null) {
                    try {
                        return self::readLineHidingAfter($stdin, $stdout, $lines, $hideAfter, $cancel);
                    } finally {
                        $restoreTerminal();
                    }
                }
                if ($hidden) {
                    $restoreEcho = self::disableEcho('-echo');
                }
            }
            try {
                while (\count($lines) < 2 && ($chunk = $stdin->read($cancel)) !== null) {
                    $chunk = explode("\n", str_replace(["\r", "\n\n"], "\n", $chunk));
                    $lines[\count($lines) - 1] .= array_shift($chunk);
                    $lines = array_merge($lines, $chunk);
                }
            } finally {
                if (isset($restoreEcho)) {
                    $restoreEcho();
                    // The newline typed wasn't echoed either.
                    $stdout->write(PHP_EOL);
                }
            }
        } finally {
            Magic::togglePeriodicLogging();
        }
        return array_shift($lines) ?? '';
    }

    /**
     * Read a line one character at a time, echoing it until $hideAfter and asterisks after it (Unix, in
     * non-canonical mode, without echo).
     *
     * @param list<string> $lines Lines read but not returned yet (what follows the newline of a paste)
     */
    private static function readLineHidingAfter(ReadableStream $stdin, WritableStream $stdout, array &$lines, string $hideAfter, ?Cancellation $cancel): string
    {
        $line = $lines[0];
        while (($chunk = $stdin->read($cancel)) !== null) {
            // Escape sequences (arrows...) can't edit the line.
            $chunk = preg_replace('/\e(\[[0-9;?]*[ -\/]*[@-~]|.)/s', '', $chunk);
            foreach (preg_split('//u', $chunk, -1, PREG_SPLIT_NO_EMPTY) ?: str_split($chunk) as $i => $char) {
                if ($char === "\r" || $char === "\n") {
                    $stdout->write(PHP_EOL);
                    // The rest of a paste, for the next reads.
                    $rest = implode('', \array_slice(preg_split('//u', $chunk, -1, PREG_SPLIT_NO_EMPTY) ?: str_split($chunk), $i + 1));
                    $rest = explode("\n", str_replace(["\r\n", "\r"], "\n", ltrim($rest, "\r\n")));
                    $lines = $rest === [''] ? [''] : $rest;
                    return $line;
                }
                if ($char === "\x7f" || $char === "\x08") {
                    if ($line !== '') {
                        $line = (string) preg_replace('/.$/su', '', $line);
                        // Shown, or as an asterisk.
                        $stdout->write("\x08 \x08");
                    }
                    continue;
                }
                if (\ord($char[0]) < 0x20) {
                    continue;
                }
                $stdout->write(str_contains($line, $hideAfter) ? '*' : $char);
                $line .= $char;
            }
        }
        $lines = [''];
        return $line;
    }

    /**
     * Configure the terminal to stop echoing what is typed (Unix), returning the function that restores it.
     *
     * @param string $mode Settings for stty
     *
     * @return ?Closure(): void Null if the terminal can't be configured (stty unavailable)
     */
    private static function disableEcho(string $mode): ?Closure
    {
        if (!\function_exists('shell_exec')) {
            return null;
        }
        try {
            $state = trim((string) shell_exec('stty -g 2>/dev/null'));
            if ($state === '') {
                return null;
            }
            shell_exec('stty '.$mode.' 2>/dev/null');
        } catch (Throwable) {
            return null;
        }
        $restored = false;
        $restore = static function () use ($state, &$restored): void {
            if (!$restored) {
                $restored = true;
                try {
                    shell_exec('stty '.escapeshellarg($state).' 2>/dev/null');
                } catch (Throwable) {
                }
            }
        };
        // A signal (see Magic::start()) ends the script from the event loop, without running finally blocks.
        register_shutdown_function($restore);
        return $restore;
    }

    /**
     * Read a line from the console without echoing it (Windows, which has no stty), or echoing it until $hideAfter
     * and asterisks after it.
     *
     * PowerShell reads it from the console it inherits (echoing on its standard error, the console too); it's
     * polled, so that the event loop keeps running, and terminated if the read is cancelled.
     */
    private static function readHiddenLineWindows(?Cancellation $cancel, ?string $hideAfter): string
    {
        $script = $hideAfter === null
            ? '$s = Read-Host -AsSecureString; [Console]::Out.Write([Runtime.InteropServices.Marshal]::PtrToStringBSTR([Runtime.InteropServices.Marshal]::SecureStringToBSTR($s)))'
            : '$h = '.var_export($hideAfter, true).'; $s = \'\'; while ($true) { $k = [Console]::ReadKey($true);'
                .' if ($k.Key -eq \'Enter\') { [Console]::Error.WriteLine(); break }'
                .' if ($k.Key -eq \'Backspace\') { if ($s.Length -gt 0) { $s = $s.Substring(0, $s.Length - 1); [Console]::Error.Write("`b `b") }; continue }'
                .' if ([char]::IsControl($k.KeyChar)) { continue }'
                .' if ($s.Contains($h)) { [Console]::Error.Write(\'*\') } else { [Console]::Error.Write($k.KeyChar) }; $s += $k.KeyChar }'
                .' [Console]::Out.Write($s)';
        $process = proc_open(
            ['powershell', '-NoProfile', '-Command', $script],
            [0 => STDIN, 1 => ['pipe', 'w'], 2 => STDERR],
            $pipes,
        );
        if ($process === false) {
            throw new Exception('Could not start powershell to read the hidden input');
        }
        try {
            while (proc_get_status($process)['running']) {
                delay(0.1, cancellation: $cancel);
            }
            return (string) stream_get_contents($pipes[1]);
        } finally {
            if (proc_get_status($process)['running']) {
                proc_terminate($process);
            }
            fclose($pipes[1]);
            proc_close($process);
        }
    }
    /**
     * Asynchronously write to stdout/browser.
     *
     * @param string $string Message to echo
     */
    public static function echo(string $string): void
    {
        getOutputBufferStream()->write($string);
    }
}
<?php

declare(strict_types=1);

/**
 * Wrapped future class.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use Amp\Cancellation;
use Amp\Future;

/**
 * @internal
 *
 * @template T
 */
final class WrappedFuture
{
    /**
     * @param Future<T> $f
     *
     * @psalm-mutation-free
     */
    public function __construct(private readonly Future $f)
    {
    }

    /**
     * @return bool True if the operation has completed.
     */
    public function isComplete(): bool
    {
        return $this->f->isComplete();
    }

    /**
     * Awaits the operation to complete.
     *
     * Throws an exception if the operation fails.
     *
     * @return T
     */
    public function await(?Cancellation $cancellation = null): mixed
    {
        $result = $this->f->await($cancellation);
        if (\is_callable($result)) {
            throw $result();
        }
        return $result;
    }
}
<?php

declare(strict_types=1);

/**
 * Session paths module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use AssertionError;
use danog\MadelineProto\Ipc\IpcState;

use const LOCK_EX;
use const LOCK_SH;
use const PHP_MAJOR_VERSION;
use const PHP_MINOR_VERSION;
use const PHP_VERSION;

use function Amp\File\createDirectory;
use function Amp\File\deleteFile;
use function Amp\File\exists;
use function Amp\File\isDirectory;
use function Amp\File\isFile;
use function Amp\File\move;
use function Amp\File\openFile;
use function Amp\File\write;

/**
 * Session path information.
 *
 * @internal
 */
final class SessionPaths
{
    /**
     * Header for session files.
     */
    private const PHP_HEADER = '<?php __HALT_COMPILER();';
    private const VERSION_OLD = 2;
    private const VERSION_SERIALIZATION_AWARE = 3;

    /**
     * Session directory path.
     */
    private string $sessionDirectoryPath;
    /**
     * Session path.
     */
    private string $sessionPath;
    /**
     * Session lock path.
     */
    private string $lockPath;
    /**
     * IPC socket path.
     */
    private string $ipcPath;
    /**
     * IPC callback socket path.
     */
    private string $ipcCallbackPath;
    /**
     * IPC light state path.
     */
    private string $ipcStatePath;
    /**
     * Web API ID setup state path.
     */
    private string $apiSetupStatePath;
    /**
     * Light state path.
     */
    private string $lightStatePath;
    /**
     * Light state.
     */
    private ?LightState $lightState = null;

    /**
     * Construct session info from session name.
     *
     * @param string $session Session name
     */
    public function __construct(string $session)
    {
        $session = Tools::absolute($session);
        $this->sessionDirectoryPath = $session;
        $this->sessionPath = $session.DIRECTORY_SEPARATOR."safe.php";
        $this->lightStatePath = $session.DIRECTORY_SEPARATOR."lightState.php";
        $this->lockPath = $session.DIRECTORY_SEPARATOR."lock";
        $this->ipcPath = $session.DIRECTORY_SEPARATOR."ipc";
        $this->ipcCallbackPath = $session.DIRECTORY_SEPARATOR."callback.ipc";
        $this->ipcStatePath = $session.DIRECTORY_SEPARATOR."ipcState.php";
        $this->apiSetupStatePath = $session.DIRECTORY_SEPARATOR."apiSetupState.php";
        if (!exists($session)) {
            createDirectory($session);
        }

        $session = realpath($session);
        if (str_starts_with($session, '/storage/emulated/')
            || str_starts_with($session, '/sdcard/')
        ) {
            throw new AssertionError("The MadelineProto session folder cannot be stored in /sdcard, please move the session folder to the termux \$HOME folder, see here for more info: https://wiki.termux.com/wiki/Internal_and_external_storage");
        }

        if (!isDirectory($session) && isFile("$session.safe.php")) {
            deleteFile($session);
            createDirectory($session);
            foreach (['safe.php', 'lightState.php', 'lock', 'ipc', 'callback.ipc', 'ipcState.php'] as $part) {
                if (exists("$session.$part")) {
                    move("$session.$part", $session.DIRECTORY_SEPARATOR."$part");
                }
                if (exists("$session.$part.lock")) {
                    move("$session.$part.lock", $session.DIRECTORY_SEPARATOR."$part.lock");
                }
            }
        }
    }
    /**
     * Deletes session.
     */
    public function delete(): void
    {
        if (file_exists($this->sessionDirectoryPath)) {
            foreach (scandir($this->sessionDirectoryPath) as $f) {
                if ($f === '.' || $f === '..' || !str_ends_with($f, '.php')) {
                    continue;
                }
                try {
                    unlink($this->sessionDirectoryPath.DIRECTORY_SEPARATOR.$f);
                } catch (\Throwable) {
                }
            }
        }
    }
    /**
     * Serialize object to file.
     */
    public function serialize(object|string $object, string $path): void
    {
        Logger::log("Waiting for exclusive lock of $path.lock...");
        $unlock = Tools::flock("$path.lock", LOCK_EX, 0.1);

        try {
            Logger::log("Got exclusive lock of $path.lock...");

            $object = self::PHP_HEADER
                .\chr(self::VERSION_SERIALIZATION_AWARE)
                .\chr(PHP_MAJOR_VERSION)
                .\chr(PHP_MINOR_VERSION)
                .\chr(Magic::$can_use_igbinary ? 1 : 0)
                .(Magic::$can_use_igbinary ? igbinary_serialize($object) : serialize($object));

            write(
                "$path.temp.php",
                $object,
            );

            // The rename only replaces the old file atomically if the new data is on disk first:
            // otherwise a crash can persist the rename and the file size but not the data,
            // leaving a session made of zeroes (seen on f2fs, which unlike ext4 doesn't flush
            // the data of a file renamed over another one).
            // The directory isn't fsynced: that only makes the rename itself durable, and on
            // f2fs it costs a checkpoint of the whole filesystem on every save.
            self::fsync("$path.temp.php");
            move("$path.temp.php", $path);
        } finally {
            $unlock();
        }
    }

    /**
     * Flush a file to disk.
     */
    private static function fsync(string $path): void
    {
        try {
            $f = fopen($path, 'r+');
        } catch (\Throwable) {
            return;
        }
        try {
            fsync($f);
        } catch (\Throwable) {
        } finally {
            fclose($f);
        }
    }

    /**
     * Deserialize new object.
     *
     * @param string $path Object path, defaults to session path
     */
    public function unserialize(string $path = ''): object|string|null
    {
        $path = $path ?: $this->sessionPath;

        if (!exists($path)) {
            return null;
        }
        $headerLen = \strlen(self::PHP_HEADER);

        Logger::log("Waiting for shared lock of $path.lock...", Logger::ULTRA_VERBOSE);
        $unlock = Tools::flock("$path.lock", LOCK_SH, 0.1);

        try {
            Logger::log("Got shared lock of $path.lock...", Logger::ULTRA_VERBOSE);

            $file = openFile($path, 'rb');

            clearstatcache(true, $path);
            $size = filesize($path);

            $file->seek($headerLen++);
            $v = \ord($file->read(null, 1));
            if ($v >= self::VERSION_OLD) {
                $php = $file->read(null, 2);
                $major = \ord($php[0]);
                $minor = \ord($php[1]);
                if (version_compare("$major.$minor", PHP_VERSION) > 0) {
                    throw new Exception("Cannot deserialize session created on newer PHP $major.$minor, currently using PHP ".PHP_MAJOR_VERSION.'.'.PHP_MINOR_VERSION.', please upgrade to the latest version of PHP!');
                }
                $headerLen += 2;
            }
            $igbinary = false;
            if ($v >= self::VERSION_SERIALIZATION_AWARE) {
                $igbinary = (bool) \ord($file->read(null, 1));
                if ($igbinary && !Magic::$can_use_igbinary) {
                    throw Exception::extension('igbinary');
                }
                $headerLen++;
            }
            $unserialized = $file->read(null, $size - $headerLen) ?? '';
            $unserialized = $igbinary ? igbinary_unserialize($unserialized) : unserialize($unserialized);
            $file->close();
        } finally {
            $unlock();
        }
        return $unserialized;
    }

    /**
     * Get session path.
     */
    public function __toString(): string
    {
        return $this->sessionDirectoryPath;
    }

    /**
     * Get session directory path.
     */
    public function getSessionDirectoryPath(): string
    {
        return $this->sessionDirectoryPath;
    }

    /**
     * Get session path.
     */
    public function getSessionPath(): string
    {
        return $this->sessionPath;
    }

    /**
     * Get lock path.
     */
    public function getLockPath(): string
    {
        return $this->lockPath;
    }

    /**
     * Get IPC socket path.
     */
    public function getIpcPath(): string
    {
        return $this->ipcPath;
    }

    /**
     * Whether the IPC server of the session listens on TCP (on 127.0.0.1), because UNIX sockets are unavailable:
     * unlike UNIX sockets and FIFOs, any program running on the machine can connect to it.
     */
    public function isIpcOverTcp(): bool
    {
        // danog/ipc stores TCP endpoints (and the UNIX sockets of Windows) in a regular file at the IPC path.
        clearstatcache(true, $this->ipcPath);
        if (!is_file($this->ipcPath)) {
            return false;
        }
        try {
            return str_starts_with((string) file_get_contents($this->ipcPath, length: 6), 'tcp://');
        } catch (\Throwable) {
            return false;
        }
    }

    /**
     * Get IPC light state path.
     */
    public function getIpcStatePath(): string
    {
        return $this->ipcStatePath;
    }

    /**
     * Get IPC state.
     */
    public function getIpcState(): ?IpcState
    {
        return $this->unserialize($this->ipcStatePath);
    }

    /**
     * Store IPC state.
     */
    public function storeIpcState(IpcState $state): void
    {
        $this->serialize($state, $this->getIpcStatePath());
    }

    /**
     * Get light state path.
     */
    public function getLightStatePath(): string
    {
        return $this->lightStatePath;
    }

    /**
     * Get light state.
     */
    public function getLightState(): LightState
    {
        /** @var LightState */
        return $this->lightState ??= $this->unserialize($this->lightStatePath);
    }

    /**
     * Store light state.
     */
    public function storeLightState(MTProto $state): void
    {
        $this->lightState = new LightState($state);
        $this->serialize($this->lightState, $this->getLightStatePath());
    }

    /**
     * Get IPC callback socket path.
     */
    public function getIpcCallbackPath(): string
    {
        return $this->ipcCallbackPath;
    }

    /**
     * How long the state of the web API ID setup is kept, in seconds.
     */
    private const API_SETUP_STATE_TTL = 600;

    /**
     * Get the state of the web API ID setup (the my.telegram.org login), kept between requests.
     *
     * The state holds the cookies of the my.telegram.org login, and anyone opening the page can continue the
     * setup: it expires after a few minutes, rather than staying in an abandoned session directory.
     */
    public function getApiSetupState(): ?MyTelegramOrgWrapper
    {
        clearstatcache(true, $this->apiSetupStatePath);
        if (exists($this->apiSetupStatePath) && time() - (int) filemtime($this->apiSetupStatePath) > self::API_SETUP_STATE_TTL) {
            $this->storeApiSetupState(null);
            return null;
        }
        $state = $this->unserialize($this->apiSetupStatePath);
        return $state instanceof MyTelegramOrgWrapper ? $state : null;
    }

    /**
     * Store the state of the web API ID setup, or delete it.
     */
    public function storeApiSetupState(?MyTelegramOrgWrapper $state): void
    {
        if ($state !== null) {
            $this->serialize($state, $this->apiSetupStatePath);
        } elseif (exists($this->apiSetupStatePath)) {
            deleteFile($this->apiSetupStatePath);
        }
    }
}
RIFFF-  WAVEfmt      €»   î   LIST   INFOISFT   Lavf60.3.100  data -                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  boolFalse#bc799737 = Bool;
boolTrue#997275b5 = Bool;
true#3fedd339 = True;

vector#1cb5c415 {t:Type} # [ t ] = Vector t;

// Root
fileReferenceMap#72a99250 layer:int db_schema:string db_schema_json:string traversers_incoming:Vector<TraverserIncoming> traversers_outgoing:Vector<TraverserOutgoing> refresh_actions:Vector<RefreshAction> skipped_incoming_sources:Vector<SkippedSource> = FileReferenceMap;

// Traversers (incoming)
traverseParam#62161b67 flags:# name:string is_flag:flags.0?true is_vector:flags.1?true type:string = TraverseParam;

traverseMethodResult#fda9f236 name:string push_sources:Vector<Source> is_needed_parent:Bool = TraverserIncoming;
traverseIncomingConstructor#6e4d00b6 type:string predicate:string params:Vector<TraverseParam> push_sources:Vector<Source> is_needed_parent:Bool = TraverserIncoming;
traverseCommitSourceLocation#94cc59c5 type:string predicate:string push_sources:Vector<Source> stored_constructor:string = TraverserIncoming;

// Points to a constructor or method
predicate#de3bc05a predicate:string is_constructor:Bool = Predicate;

source#4ec4751a flags:# predicate:Predicate stored_constructor:string stored_params:Vector<FieldExtractor> skipped_flags:Vector<string> needs_parent:flags.3?Predicate needs_not_parents:Vector<Predicate> = Source;

// Traversers (outgoing)
traverseOutgoingConstructor#3fc5026d type:string predicate:string params:Vector<TraverseParam> = TraverserOutgoing;
traverseSwapLocation#032bf95d type:string predicate:string stored_constructor:string = TraverserOutgoing;
traverseMethodCall#9244f5bb name:string params:Vector<TraverseParam> = TraverserOutgoing;

// Skipped sources
skippedSource#2b543f6f predicate:Predicate why:string = SkippedSource;

// Refresh actions
refreshAction#d4e15689 stored_constructor:string action:ActionOp = RefreshAction;

// Field extraction path
paramNotFlag#acd9d5cf = ParamFlag;
paramIsFlagAbortIfEmpty#f8fe9fee = ParamFlag;
paramIsFlagFallback#202b77a1 fallback:TypedOp = ParamFlag;
paramIsFlagPassthrough#1dc6e17d = ParamFlag;

pathPart#19a10fbf type:string constructor:string param:string param_type:string flag:ParamFlag = PathPart;

path#0c3586a2 parts:Vector<PathPart> = Path;
pathParent#58f13684 parts:Vector<PathPart> = Path;

// Extractor
extractAndStore#72069549 from:Path to:string = FieldExtractor;

extractInputStickerSetFromDocumentAttributesAndStore#369d8d14 from:Path to:string = FieldExtractor;
extractInputStickerSetFromStickerSetAndStore#c167d470 from:Path to:string = FieldExtractor;

extractPeerIdFromPeerAndStore#7d33019c from:Path to:string = FieldExtractor;
extractPeerIdFromInputPeerAndStore#a51acfb4 from:Path to:string = FieldExtractor;

extractChannelIdFromChannelAndStore#5675bc97 from:Path to:string = FieldExtractor;
extractChannelIdFromInputChannelAndStore#b662660e from:Path to:string = FieldExtractor;

extractUserIdFromUserAndStore#4778ec63 from:Path to:string = FieldExtractor;
extractUserIdFromInputUserAndStore#7720aa2e from:Path to:string = FieldExtractor;

// Actions
callOp#c2ff3383 method:string args:Vector<TypedOpArg> = ActionOp;
getMessageOp#8da4c2ae peer:TypedOp id:TypedOp quick_reply_shortcut_id:TypedOp = ActionOp;

// For string => TypedOp dictionaries
typedOpArg#3a2930c2 key:string value:TypedOp = TypedOpArg;

// Typed constructors, the type is specified to simplify codegen,
// but isn't strictly necessary as it can be inferred from the TypedOpOp.
// It is fully pre-validated during the generation of the definition file.
typedOp#705b10ec type:string op:TypedOpOp = TypedOp;

// The actual ops
copyOp#f48f418f from:string = TypedOpOp;

getInputChannelByIdOp#3cb47531 from:string = TypedOpOp;
getInputUserByIdOp#c0ee4326 from:string = TypedOpOp;
getInputPeerByIdOp#19813750 from:string = TypedOpOp;

// Literals & constructors (methods not allowed or needed here)
constructorOp#107f8d8a constructor:string args:Vector<TypedOpArg> = TypedOpOp;

vectorOp#f8fb8f72 values:Vector<TypedOp> = TypedOpOp;

intLiteralOp#cbfabe7c value:int = TypedOpOp;
longLiteralOp#d08b8d3a value:long = TypedOpOp;
stringLiteralOp#2b56ea8e value:string = TypedOpOp;
bytesLiteralOp#fdb395a4 value:bytes = TypedOpOp;
boolLiteralOp#37e07911 value:Bool = TypedOpOp;
doubleLiteralOp#3651e3bf value:double = TypedOpOp;
themeFormatLiteralOp#8e4f9208 = TypedOpOp;
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

/**
 * Indicates a local directory to write output into.
 *
 * Used with {@see EventHandler\Call::setOutputFolder()} to record a call into a directory: every recorded participant
 * gets a numbered series of `<n>_<streams>.mkv` files there (`<peerId>.<n>_<streams>.mkv` in multi-party calls),
 * one per combination of audio, camera video and screen share they send.
 */
final class LocalDirectory
{
    /**
     * @psalm-mutation-free
     */
    public function __construct(
        public readonly string $dir
    ) {
    }

    /**
     * Create the directory if it does not exist yet, and return its path without a trailing slash.
     *
     * @internal
     *
     * @throws \RuntimeException If it cannot be created.
     */
    public function create(): string
    {
        $path = rtrim($this->dir, '/');
        if (!is_dir($path) && !mkdir($path, 0o777, true) && !is_dir($path)) {
            throw new \RuntimeException("Could not create the recording directory $path");
        }
        return $path;
    }
}
===8===
decryptedMessage#1f814f1f random_id:long random_bytes:bytes message:string media:DecryptedMessageMedia = DecryptedMessage;
decryptedMessageService#aa48327d random_id:long random_bytes:bytes action:DecryptedMessageAction = DecryptedMessage;
decryptedMessageMediaEmpty#89f5c4a = DecryptedMessageMedia;
decryptedMessageMediaPhoto#32798a8c thumb:bytes thumb_w:int thumb_h:int w:int h:int size:int key:bytes iv:bytes = DecryptedMessageMedia;
decryptedMessageMediaVideo#4cee6ef3 thumb:bytes thumb_w:int thumb_h:int duration:int w:int h:int size:int key:bytes iv:bytes = DecryptedMessageMedia;
decryptedMessageMediaGeoPoint#35480a59 lat:double long:double = DecryptedMessageMedia;
decryptedMessageMediaContact#588a0a97 phone_number:string first_name:string last_name:string user_id:int = DecryptedMessageMedia;
decryptedMessageActionSetMessageTTL#a1733aec ttl_seconds:int = DecryptedMessageAction;
decryptedMessageMediaDocument#b095434b thumb:bytes thumb_w:int thumb_h:int file_name:string mime_type:string size:int key:bytes iv:bytes = DecryptedMessageMedia;
decryptedMessageMediaAudio#6080758f duration:int size:int key:bytes iv:bytes = DecryptedMessageMedia;
decryptedMessageActionReadMessages#c4f40be random_ids:Vector<long> = DecryptedMessageAction;
decryptedMessageActionDeleteMessages#65614304 random_ids:Vector<long> = DecryptedMessageAction;
decryptedMessageActionScreenshotMessages#8ac1f475 random_ids:Vector<long> = DecryptedMessageAction;
decryptedMessageActionFlushHistory#6719e45c = DecryptedMessageAction;

===17===
decryptedMessage#204d3878 random_id:long ttl:int message:string media:DecryptedMessageMedia = DecryptedMessage;
decryptedMessageService#73164160 random_id:long action:DecryptedMessageAction = DecryptedMessage;
decryptedMessageMediaVideo#524a415d thumb:bytes thumb_w:int thumb_h:int duration:int mime_type:string w:int h:int size:int key:bytes iv:bytes = DecryptedMessageMedia;
decryptedMessageMediaAudio#57e0a9cb duration:int mime_type:string size:int key:bytes iv:bytes = DecryptedMessageMedia;
decryptedMessageLayer#1be31789 random_bytes:bytes layer:int in_seq_no:int out_seq_no:int message:DecryptedMessage = DecryptedMessageLayer;
sendMessageTypingAction#16bf744e = SendMessageAction;
sendMessageCancelAction#fd5ec8f5 = SendMessageAction;
sendMessageRecordVideoAction#a187d66f = SendMessageAction;
sendMessageUploadVideoAction#92042ff7 = SendMessageAction;
sendMessageRecordAudioAction#d52f73f7 = SendMessageAction;
sendMessageUploadAudioAction#e6ac8a6f = SendMessageAction;
sendMessageUploadPhotoAction#990a3c1a = SendMessageAction;
sendMessageUploadDocumentAction#8faee98e = SendMessageAction;
sendMessageGeoLocationAction#176f8ba1 = SendMessageAction;
sendMessageChooseContactAction#628cbc6f = SendMessageAction;
decryptedMessageActionResend#511110b0 start_seq_no:int end_seq_no:int = DecryptedMessageAction;
decryptedMessageActionNotifyLayer#f3048883 layer:int = DecryptedMessageAction;
decryptedMessageActionTyping#ccb27641 action:SendMessageAction = DecryptedMessageAction;

===20===
decryptedMessageActionRequestKey#f3c9611b exchange_id:long g_a:bytes = DecryptedMessageAction;
decryptedMessageActionAcceptKey#6fe1735b exchange_id:long g_b:bytes key_fingerprint:long = DecryptedMessageAction;
decryptedMessageActionAbortKey#dd05ec6b exchange_id:long = DecryptedMessageAction;
decryptedMessageActionCommitKey#ec2e0b9b exchange_id:long key_fingerprint:long = DecryptedMessageAction;
decryptedMessageActionNoop#a82fdd63 = DecryptedMessageAction;

===23===
documentAttributeImageSize#6c37c15c w:int h:int = DocumentAttribute;
documentAttributeAnimated#11b58939 = DocumentAttribute;
documentAttributeSticker#fb0a5727 = DocumentAttribute;
documentAttributeVideo#5910cccb duration:int w:int h:int = DocumentAttribute;
documentAttributeAudio#51448e5 duration:int = DocumentAttribute;
documentAttributeFilename#15590068 file_name:string = DocumentAttribute;
photoSizeEmpty#e17e23c type:string = PhotoSize;
photoSize#77bfb61b type:string location:FileLocation w:int h:int size:int = PhotoSize;
photoCachedSize#e9a734fa type:string location:FileLocation w:int h:int bytes:bytes = PhotoSize;
fileLocationUnavailable#7c596b46 volume_id:long local_id:int secret:long = FileLocation;
fileLocation#53d69076 dc_id:int volume_id:long local_id:int secret:long = FileLocation;
decryptedMessageMediaExternalDocument#fa95b0dd id:long access_hash:long date:int mime_type:string size:int thumb:PhotoSize dc_id:int attributes:Vector<DocumentAttribute> = DecryptedMessageMedia;

===45===
decryptedMessage#36b091de flags:# random_id:long ttl:int message:string media:flags.9?DecryptedMessageMedia entities:flags.7?Vector<MessageEntity> via_bot_name:flags.11?string reply_to_random_id:flags.3?long = DecryptedMessage;
decryptedMessageMediaPhoto#f1fa8d78 thumb:bytes thumb_w:int thumb_h:int w:int h:int size:int key:bytes iv:bytes caption:string = DecryptedMessageMedia;
decryptedMessageMediaVideo#970c8c0e thumb:bytes thumb_w:int thumb_h:int duration:int mime_type:string w:int h:int size:int key:bytes iv:bytes caption:string = DecryptedMessageMedia;
decryptedMessageMediaDocument#7afe8ae2 thumb:bytes thumb_w:int thumb_h:int mime_type:string size:int key:bytes iv:bytes attributes:Vector<DocumentAttribute> caption:string = DecryptedMessageMedia;
documentAttributeSticker#3a556302 alt:string stickerset:InputStickerSet = DocumentAttribute;
documentAttributeAudio#ded218e0 duration:int title:string performer:string = DocumentAttribute;
messageEntityUnknown#bb92ba95 offset:int length:int = MessageEntity;
messageEntityMention#fa04579d offset:int length:int = MessageEntity;
messageEntityHashtag#6f635b0d offset:int length:int = MessageEntity;
messageEntityBotCommand#6cef8ac7 offset:int length:int = MessageEntity;
messageEntityUrl#6ed02538 offset:int length:int = MessageEntity;
messageEntityEmail#64e475c2 offset:int length:int = MessageEntity;
messageEntityBold#bd610bc9 offset:int length:int = MessageEntity;
messageEntityItalic#826f8b60 offset:int length:int = MessageEntity;
messageEntityCode#28a20571 offset:int length:int = MessageEntity;
messageEntityPre#73924be0 offset:int length:int language:string = MessageEntity;
messageEntityTextUrl#76a6d327 offset:int length:int url:string = MessageEntity;
inputStickerSetShortName#861cc8a0 short_name:string = InputStickerSet;
inputStickerSetEmpty#ffb62b95 = InputStickerSet;
decryptedMessageMediaVenue#8a0df56f lat:double long:double title:string address:string provider:string venue_id:string = DecryptedMessageMedia;
decryptedMessageMediaWebPage#e50511d8 url:string = DecryptedMessageMedia;

===46===
documentAttributeAudio#9852f9c6 flags:# voice:flags.10?true duration:int title:flags.0?string performer:flags.1?string waveform:flags.2?bytes = DocumentAttribute;

===66===
documentAttributeVideo#ef02ce6 flags:# round_message:flags.0?true duration:int w:int h:int = DocumentAttribute;
sendMessageRecordRoundAction#88f27fbc = SendMessageAction;
sendMessageUploadRoundAction#bb718624 = SendMessageAction;

===73===
decryptedMessage#91cc4674 flags:# no_webpage:flags.1?true silent:flags.5?true random_id:long ttl:int message:string media:flags.9?DecryptedMessageMedia entities:flags.7?Vector<MessageEntity> via_bot_name:flags.11?string reply_to_random_id:flags.3?long grouped_id:flags.17?long = DecryptedMessage;

===101===
messageEntityUnderline#9c4e7e8b offset:int length:int = MessageEntity;
messageEntityStrike#bf0693d4 offset:int length:int = MessageEntity;
messageEntityBlockquote#20df5d0 offset:int length:int = MessageEntity;

===143===
decryptedMessageMediaDocument#6abd9782 thumb:bytes thumb_w:int thumb_h:int mime_type:string size:long key:bytes iv:bytes attributes:Vector<DocumentAttribute> caption:string = DecryptedMessageMedia;

===144===
messageEntitySpoiler#32ca960f offset:int length:int = MessageEntity;
messageEntityCustomEmoji#c8cf05f8 offset:int length:int document_id:long = MessageEntity;
<?php

declare(strict_types=1);

/**
 * PTSException module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

/**
 * Indicates that the specified secret chat wasn't found.
 */
final class SecretPeerNotInDbException extends Exception
{
    public function __construct()
    {
        parent::__construct(Lang::$current_lang['sec_peer_not_in_db']);
    }
}
<?php

declare(strict_types=1);

/**
 * EventHandler module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

/**
 * Plugin event handler class.
 */
abstract class PluginEventHandler extends SimpleEventHandler
{
    /**
     * Plugins can require other plugins ONLY with the getPlugins() method.
     *
     * @psalm-pure
     */
    #[\Override]
    final public static function getPluginPaths(): string|array|null
    {
        return null;
    }
    /**
     * Whether the plugin is enabled.
     *
     * @psalm-pure
     */
    public function isPluginEnabled(): bool
    {
        return true;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

/**
 * Indicates a local file to upload.
 *
 * @psalm-immutable
 */
final class LocalFile
{
    /**
     * @psalm-mutation-free
     */
    public function __construct(
        public readonly string $file
    ) {
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use Amp\ByteStream\ReadableStream;
use Amp\Cancellation;
use Closure;
use Generator;

/**
 * A pure PHP MP4 (ISO base media file format) demuxer, with the same shape as {@see Matroska}: it
 * reads the file sequentially and yields its encoded frames, one at a time, with their track, codec
 * (as a Matroska codec ID, so they can be muxed by {@see MatroskaWriter} as they are), type, timestamp
 * and keyframe flag.
 *
 * Both fragmented files (`moov` with `mvex`, then `moof`+`mdat` pairs, as served for group calls in
 * [stream mode](https://core.telegram.org/api/group-calls#stream-mode) and by every live/DASH
 * encoder) and progressive files whose `moov` precedes the `mdat` are supported. A progressive file
 * with the `moov` at the end (like the chunks of an RTMP livestream) is only supported if small: its
 * `mdat` is buffered until the `moov` describes it.
 *
 * Supported codecs: H.264 (`avc1`/`avc3`), H.265 (`hvc1`/`hev1`), VP9 (`vp09`), AV1 (`av01`), AAC
 * (`mp4a`) and Opus (`Opus`). Video samples keep the length-prefixed NAL units of the file, which is
 * also what Matroska expects.
 */
final class Mp4
{
    /** The error of a progressive file whose moov follows a big mdat, which can't be read sequentially. */
    public const MOOV_AT_END = 'The moov box of the MP4 file is after its media data, so it can\'t be played while it\'s read: please move it to the start, e.g. with ffmpeg -i input -c copy -movflags +faststart output.mp4';

    /** The largest mdat buffered until a following moov describes it. */
    private const MAX_BUFFERED_MDAT = 16 << 20;

    public const TRACK_TYPE_VIDEO = Matroska::TRACK_TYPE_VIDEO;
    public const TRACK_TYPE_AUDIO = Matroska::TRACK_TYPE_AUDIO;

    /** Sample flag: the sample is not a sync (key) sample. */
    private const SAMPLE_NON_SYNC = 0x10000;
    private const TFHD_BASE_DATA_OFFSET = 0x1;
    private const TFHD_DEFAULT_SAMPLE_DURATION = 0x8;
    private const TFHD_DEFAULT_SAMPLE_SIZE = 0x10;
    private const TFHD_DEFAULT_SAMPLE_FLAGS = 0x20;
    private const TFHD_DEFAULT_BASE_IS_MOOF = 0x20000;
    private const TRUN_DATA_OFFSET = 0x1;
    private const TRUN_FIRST_SAMPLE_FLAGS = 0x4;
    private const TRUN_SAMPLE_DURATION = 0x100;
    private const TRUN_SAMPLE_SIZE = 0x200;
    private const TRUN_SAMPLE_FLAGS = 0x400;
    private const TRUN_SAMPLE_CTS = 0x800;

    /**
     * The frames of the file, in file order.
     *
     * Each entry is `['track' => int, 'codec' => string, 'type' => int, 'data' => string,
     * 'timestamp' => int (ms), 'keyframe' => bool]`, like {@see Matroska::$frames}.
     *
     * @var iterable<array{track: int, codec: string, type: int, data: string, timestamp: int, keyframe: bool}>
     */
    public readonly iterable $frames;

    /**
     * The tracks of the file, by track ID: `codec` (a Matroska codec ID), `type`, `private` (the
     * Matroska CodecPrivate: avcC/hvcC/vpcC/av1C, the AAC AudioSpecificConfig, or an OpusHead), the
     * video `width`/`height`, the audio `rate`/`channels`, and the media `timescale`.
     *
     * @var array<int, array{codec: string, type: int, private: ?string, width: int, height: int, rate: int, channels: int, timescale: int}>
     */
    public array $tracks = [];

    /** The timescale of the movie (mvhd), that of the durations of the edit lists. */
    private int $movieTimescale = 1000;
    /**
     * @var array<int, int> What the edit list of each track adds to its timestamps (in its media
     *                      timescale), by track ID: an initial delay, minus where its media starts.
     */
    private array $shifts = [];

    /** @var Closure(int): ?string Reads exactly the requested number of bytes, or null at EOF. */
    private Closure $read;
    /** Absolute offset of the next byte to be read. */
    private int $position = 0;

    /**
     * @var array<int, array{duration: int, size: int, flags: int}> Track fragment defaults (trex), by track ID.
     */
    private array $trex = [];

    public function __construct(
        LocalFile|RemoteUrl|ReadableStream $stream,
        ?Cancellation $cancellation = null,
    ) {
        $this->read = Tools::openBuffered($stream, $cancellation);
        $it = $this->demux();
        // Prime the generator so that the track list is populated before the caller iterates.
        $it->current();
        $this->frames = $it;
    }

    /**
     * Whether the file has a track with the given (Matroska) codec ID.
     *
     * @psalm-mutation-free
     */
    public function hasCodec(string $codec): bool
    {
        foreach ($this->tracks as $track) {
            if ($track['codec'] === $codec) {
                return true;
            }
        }
        return false;
    }

    /**
     * Read exactly `$length` bytes, or throw at EOF.
     */
    private function consume(int $length): string
    {
        if ($length === 0) {
            return '';
        }
        $data = ($this->read)($length);
        if ($data === null || \strlen($data) !== $length) {
            throw new Exception('Unexpected end of MP4 file');
        }
        $this->position += $length;
        return $data;
    }

    /**
     * Read the next top-level box header: its type and payload size, or null at EOF.
     *
     * @return array{type: string, size: int, start: int}|null
     */
    private function nextBox(): ?array
    {
        $start = $this->position;
        $header = ($this->read)(8);
        if ($header === null) {
            return null;
        }
        if (\strlen($header) !== 8) {
            throw new Exception('Truncated MP4 box header');
        }
        $this->position += 8;
        $size = self::u32($header, 0);
        $type = substr($header, 4, 4);
        $headerSize = 8;
        if ($size === 1) {
            $large = $this->consume(8);
            $size = self::u64($large, 0);
            $headerSize = 16;
        } elseif ($size === 0) {
            $size = PHP_INT_MAX; // to the end of the file
        }
        return ['type' => $type, 'size' => $size === PHP_INT_MAX ? -1 : $size - $headerSize, 'start' => $start];
    }

    /**
     * @return Generator<array{track: int, codec: string, type: int, data: string, timestamp: int, keyframe: bool}>
     */
    private function demux(): Generator
    {
        /** @var array<int, array{offset: int, size: int, track: int, timestamp: int, keyframe: bool}> Samples of a progressive file, by absolute offset. */
        $progressive = [];
        $moofStart = 0;
        /** @var array<int, array{offset: int, size: int, track: int, timestamp: int, keyframe: bool}> Samples described by the last moof, by absolute offset. */
        $pending = [];
        $sawMoov = false;
        /** @var list<array{int, string}> The mdats before the moov: their absolute offset and data. */
        $early = [];
        while (($box = $this->nextBox()) !== null) {
            switch ($box['type']) {
                case 'moov':
                    $sawMoov = true;
                    $this->parseMoov($this->consume($box['size']), $progressive);
                    if ($progressive !== []) {
                        ksort($progressive);
                    }
                    foreach ($early as [$dataStart, $data]) {
                        foreach ($progressive as $offset => $sample) {
                            if ($offset < $dataStart || $offset + $sample['size'] > $dataStart + \strlen($data)) {
                                continue;
                            }
                            unset($progressive[$offset]);
                            $frame = $this->frame($sample, substr($data, $offset - $dataStart, $sample['size']));
                            if ($frame !== null) {
                                yield $frame;
                            }
                        }
                    }
                    $early = [];
                    break;
                case 'moof':
                    $moofStart = $box['start'];
                    $pending = $this->parseMoof($this->consume($box['size']), $moofStart);
                    ksort($pending);
                    break;
                case 'mdat':
                    $dataStart = $this->position;
                    $samples = $pending !== [] ? $pending : $progressive;
                    $pending = [];
                    if ($samples === []) {
                        if (!$sawMoov) {
                            if ($box['size'] < 0 || $box['size'] > self::MAX_BUFFERED_MDAT) {
                                // Rather than reading the whole file to find out.
                                throw new Exception(self::MOOV_AT_END);
                            }
                            $early[] = [$dataStart, $this->consume($box['size'])];
                            break;
                        }
                        $this->skip($box['size']);
                        break;
                    }
                    // Samples are laid out in file order: read the mdat sequentially, yielding each one.
                    foreach ($samples as $offset => $sample) {
                        if ($offset < $this->position) {
                            continue; // overlapping/duplicate description, or before this mdat
                        }
                        if ($box['size'] >= 0 && $offset + $sample['size'] > $dataStart + $box['size']) {
                            break; // belongs to a later mdat
                        }
                        $this->skip($offset - $this->position);
                        $frame = $this->frame($sample, $this->consume($sample['size']));
                        unset($progressive[$offset]);
                        if ($frame !== null) {
                            yield $frame;
                        }
                    }
                    if ($box['size'] >= 0) {
                        $this->skip($dataStart + $box['size'] - $this->position);
                    }
                    break;
                default:
                    if ($box['size'] < 0) {
                        return;
                    }
                    $this->skip($box['size']);
            }
        }
    }

    /**
     * The frame of a sample, null if it belongs to an unknown track.
     *
     * @param array{offset: int, size: int, track: int, timestamp: int, keyframe: bool} $sample
     *
     * @return ?array{track: int, codec: string, type: int, data: string, timestamp: int, keyframe: bool}
     */
    private function frame(array $sample, string $data): ?array
    {
        $track = $this->tracks[$sample['track']] ?? null;
        if ($track === null) {
            return null;
        }
        return [
            'track' => $sample['track'],
            'codec' => $track['codec'],
            'type' => $track['type'],
            'data' => $data,
            'timestamp' => $sample['timestamp'],
            'keyframe' => $sample['keyframe'],
        ];
    }

    private function skip(int $length): void
    {
        while ($length > 0) {
            $chunk = min($length, 1 << 20);
            $this->consume($chunk);
            $length -= $chunk;
        }
    }

    /* ------------------------------------------------------------------ *
     *  moov: tracks, codecs, fragment defaults, and progressive sample tables.
     * ------------------------------------------------------------------ */

    /**
     * @param array<int, array{offset: int, size: int, track: int, timestamp: int, keyframe: bool}> $progressive
     */
    private function parseMoov(string $moov, array &$progressive): void
    {
        foreach (self::children($moov) as [$type, $payload]) {
            if ($type === 'mvhd') {
                $this->movieTimescale = max(1, self::u32($payload, \ord($payload[0] ?? "\0") === 1 ? 20 : 12));
            } elseif ($type === 'trak') {
                $this->parseTrak($payload, $progressive);
            } elseif ($type === 'mvex') {
                foreach (self::children($payload) as [$childType, $child]) {
                    if ($childType === 'trex' && \strlen($child) >= 24) {
                        $this->trex[self::u32($child, 4)] = [
                            'duration' => self::u32($child, 12),
                            'size' => self::u32($child, 16),
                            'flags' => self::u32($child, 20),
                        ];
                    }
                }
            }
        }
    }

    /**
     * @param array<int, array{offset: int, size: int, track: int, timestamp: int, keyframe: bool}> $progressive
     */
    private function parseTrak(string $trak, array &$progressive): void
    {
        $trackId = 0;
        $width = 0;
        $height = 0;
        $timescale = 1000;
        $handler = '';
        $stbl = null;
        $elst = null;
        foreach (self::children($trak) as [$type, $payload]) {
            if ($type === 'edts') {
                foreach (self::children($payload) as [$editType, $edit]) {
                    if ($editType === 'elst') {
                        $elst = $edit;
                    }
                }
            } elseif ($type === 'tkhd') {
                $version = \ord($payload[0]);
                $trackId = self::u32($payload, $version === 1 ? 20 : 12);
                $width = self::u32($payload, \strlen($payload) - 8) >> 16;
                $height = self::u32($payload, \strlen($payload) - 4) >> 16;
            } elseif ($type === 'mdia') {
                foreach (self::children($payload) as [$mediaType, $media]) {
                    if ($mediaType === 'mdhd') {
                        $version = \ord($media[0]);
                        $timescale = self::u32($media, $version === 1 ? 20 : 12);
                    } elseif ($mediaType === 'hdlr') {
                        $handler = substr($media, 8, 4);
                    } elseif ($mediaType === 'minf') {
                        foreach (self::children($media) as [$minfType, $minf]) {
                            if ($minfType === 'stbl') {
                                $stbl = $minf;
                            }
                        }
                    }
                }
            }
        }
        if ($stbl === null || $trackId === 0) {
            return;
        }
        $trackType = match ($handler) {
            'vide' => self::TRACK_TYPE_VIDEO,
            'soun' => self::TRACK_TYPE_AUDIO,
            default => 0,
        };
        if ($trackType === 0) {
            return;
        }
        $tables = [];
        foreach (self::children($stbl) as [$type, $payload]) {
            $tables[$type] = $payload;
        }
        if (!isset($tables['stsd'])) {
            return;
        }
        $entry = self::parseStsd($tables['stsd'], $trackType);
        if ($entry === null) {
            return;
        }
        $this->tracks[$trackId] = [
            'codec' => $entry['codec'],
            'type' => $trackType,
            'private' => $entry['private'],
            'width' => $entry['width'] ?: $width,
            'height' => $entry['height'] ?: $height,
            'rate' => $entry['rate'],
            'channels' => $entry['channels'],
            'timescale' => max(1, $timescale),
        ];
        if ($elst !== null) {
            $this->shifts[$trackId] = $this->editShift($elst, max(1, $timescale));
        }
        if (isset($tables['stco']) || isset($tables['co64'])) {
            $this->parseSampleTables($trackId, $tables, $progressive);
        }
    }

    /**
     * What an edit list adds to the timestamps of its track, in its media timescale: the duration of
     * the empty edits it starts with (a delay), minus the media time of its first edit (where the
     * presentation starts, e.g. after the delay of the B-frames). Later edits are not supported.
     */
    private function editShift(string $elst, int $timescale): int
    {
        $version = \ord($elst[0] ?? "\0");
        $count = self::u32($elst, 4);
        $entrySize = $version === 1 ? 20 : 12;
        $delay = 0;
        for ($i = 0, $pos = 8; $i < $count && $pos + $entrySize <= \strlen($elst); $i++, $pos += $entrySize) {
            $duration = $version === 1 ? self::u64($elst, $pos) : self::u32($elst, $pos);
            $mediaTime = $version === 1 ? self::u64($elst, $pos + 8) : self::s32($elst, $pos + 4);
            if ($mediaTime === -1) {
                $delay += intdiv($duration * $timescale, $this->movieTimescale);
                continue;
            }
            return $delay - $mediaTime;
        }
        return $delay;
    }

    /**
     * The first sample entry of a sample description box: the codec, its private data and format.
     *
     * @return array{codec: string, private: ?string, width: int, height: int, rate: int, channels: int}|null
     *
     * @psalm-pure
     */
    private static function parseStsd(string $stsd, int $trackType): ?array
    {
        if (\strlen($stsd) < 16 || self::u32($stsd, 4) === 0) {
            return null;
        }
        $entry = substr($stsd, 8);
        $size = self::u32($entry, 0);
        $format = substr($entry, 4, 4);
        $entry = substr($entry, 8, max(0, $size - 8));
        $result = ['codec' => '', 'private' => null, 'width' => 0, 'height' => 0, 'rate' => 0, 'channels' => 0];
        if ($trackType === self::TRACK_TYPE_VIDEO) {
            // VisualSampleEntry: 6 reserved, data_reference_index(2), 16 predefined/reserved, width(2), height(2), ...(50 more)
            $result['width'] = self::u16($entry, 24);
            $result['height'] = self::u16($entry, 26);
            $boxes = self::children(substr($entry, 78));
            foreach ($boxes as [$type, $payload]) {
                switch ($type) {
                    case 'avcC':
                        $result['codec'] = 'V_MPEG4/ISO/AVC';
                        $result['private'] = $payload;
                        break;
                    case 'hvcC':
                        $result['codec'] = 'V_MPEGH/ISO/HEVC';
                        $result['private'] = $payload;
                        break;
                    case 'vpcC':
                        $result['codec'] = $format === 'vp08' ? 'V_VP8' : 'V_VP9';
                        $result['private'] = substr($payload, 4); // drop the full-box header
                        break;
                    case 'av1C':
                        $result['codec'] = 'V_AV1';
                        $result['private'] = $payload;
                        break;
                }
            }
            if ($result['codec'] === '') {
                $result['codec'] = match ($format) {
                    'avc1', 'avc3' => 'V_MPEG4/ISO/AVC',
                    'hvc1', 'hev1' => 'V_MPEGH/ISO/HEVC',
                    'vp08' => 'V_VP8',
                    'vp09' => 'V_VP9',
                    'av01' => 'V_AV1',
                    default => '',
                };
            }
        } else {
            // AudioSampleEntry: 6 reserved, data_reference_index(2), version(2), revision(2), vendor(4), channels(2), samplesize(2), predefined(2), reserved(2), samplerate(4, 16.16)
            $version = self::u16($entry, 8);
            $result['channels'] = self::u16($entry, 16);
            $result['rate'] = self::u32($entry, 24) >> 16;
            $boxesOffset = 28;
            if ($version === 1) {
                $boxesOffset += 16;
            } elseif ($version === 2) {
                $boxesOffset = 64;
            }
            foreach (self::children(substr($entry, $boxesOffset)) as [$type, $payload]) {
                if ($type === 'esds') {
                    $result['codec'] = 'A_AAC';
                    $result['private'] = self::audioSpecificConfig($payload);
                } elseif ($type === 'dOps') {
                    $result['codec'] = 'A_OPUS';
                    $result['private'] = self::opusHeadFromDops($payload);
                    $result['rate'] = 48000;
                    $result['channels'] = \ord($payload[1]);
                }
            }
            if ($result['codec'] === '') {
                $result['codec'] = match ($format) {
                    'mp4a' => 'A_AAC',
                    'Opus' => 'A_OPUS',
                    default => '',
                };
            }
        }
        return $result['codec'] === '' ? null : $result;
    }

    /**
     * The AudioSpecificConfig (DecoderSpecificInfo, tag 5) inside an esds box.
     *
     * @psalm-pure
     */
    private static function audioSpecificConfig(string $esds): ?string
    {
        $offset = 4; // full-box header
        $length = \strlen($esds);
        while ($offset < $length) {
            $tag = \ord($esds[$offset++]);
            $size = 0;
            for ($i = 0; $i < 4 && $offset < $length; $i++) {
                $byte = \ord($esds[$offset++]);
                $size = ($size << 7) | ($byte & 0x7F);
                if (($byte & 0x80) === 0) {
                    break;
                }
            }
            if ($tag === 0x05) {
                return substr($esds, $offset, $size);
            }
            if ($tag === 0x03) {
                // ES_Descriptor: ES_ID(2), flags(1) [+ dependsOn(2)] [+ URL] [+ OCR(2)], then the DecoderConfigDescriptor
                $flags = \ord($esds[$offset + 2]);
                $offset += 3;
                if ($flags & 0x80) {
                    $offset += 2;
                }
                if ($flags & 0x40) {
                    $offset += 1 + \ord($esds[$offset]);
                }
                if ($flags & 0x20) {
                    $offset += 2;
                }
                continue;
            }
            if ($tag === 0x04) {
                // DecoderConfigDescriptor: 13 bytes, then the DecoderSpecificInfo
                $offset += 13;
                continue;
            }
            $offset += $size;
        }
        return null;
    }

    /**
     * Build an OpusHead (the Matroska CodecPrivate) from an OpusSpecificBox (dOps), which carries the
     * same fields big-endian and without the magic.
     *
     * @psalm-pure
     */
    private static function opusHeadFromDops(string $dops): string
    {
        $channels = \ord($dops[1]);
        $preSkip = self::u16($dops, 2);
        $rate = self::u32($dops, 4);
        $gain = self::u16($dops, 8);
        $family = \ord($dops[10]);
        $head = 'OpusHead'.pack('CCvVvC', 1, $channels, $preSkip, $rate, $gain, $family);
        if ($family !== 0) {
            $head .= substr($dops, 11);
        }
        return $head;
    }

    /**
     * Turn the sample tables of a progressive file into absolute-offset samples.
     *
     * @param array<string, string> $tables
     * @param array<int, array{offset: int, size: int, track: int, timestamp: int, keyframe: bool}> $progressive
     */
    private function parseSampleTables(int $trackId, array $tables, array &$progressive): void
    {
        $timescale = $this->tracks[$trackId]['timescale'];
        $shift = $this->shifts[$trackId] ?? 0;
        // Chunk offsets.
        $chunkOffsets = [];
        if (isset($tables['co64'])) {
            $count = self::u32($tables['co64'], 4);
            for ($i = 0; $i < $count; $i++) {
                $chunkOffsets[] = self::u64($tables['co64'], 8 + $i * 8);
            }
        } elseif (isset($tables['stco'])) {
            $count = self::u32($tables['stco'], 4);
            for ($i = 0; $i < $count; $i++) {
                $chunkOffsets[] = self::u32($tables['stco'], 8 + $i * 4);
            }
        }
        // Sample sizes.
        $sizes = [];
        $sampleCount = 0;
        if (isset($tables['stsz'])) {
            $uniform = self::u32($tables['stsz'], 4);
            $sampleCount = self::u32($tables['stsz'], 8);
            for ($i = 0; $i < $sampleCount; $i++) {
                $sizes[] = $uniform !== 0 ? $uniform : self::u32($tables['stsz'], 12 + $i * 4);
            }
        }
        // Samples per chunk.
        $stsc = [];
        if (isset($tables['stsc'])) {
            $count = self::u32($tables['stsc'], 4);
            for ($i = 0; $i < $count; $i++) {
                $stsc[] = [self::u32($tables['stsc'], 8 + $i * 12), self::u32($tables['stsc'], 12 + $i * 12)];
            }
        }
        // Durations.
        $durations = [];
        if (isset($tables['stts'])) {
            $count = self::u32($tables['stts'], 4);
            for ($i = 0; $i < $count; $i++) {
                $n = self::u32($tables['stts'], 8 + $i * 8);
                $delta = self::u32($tables['stts'], 12 + $i * 8);
                for ($j = 0; $j < $n; $j++) {
                    $durations[] = $delta;
                }
            }
        }
        // Composition offsets: the timestamps are presentation timestamps, like those of Matroska.
        $compositionOffsets = [];
        if (isset($tables['ctts'])) {
            $signed = \ord($tables['ctts'][0]) === 1;
            $count = self::u32($tables['ctts'], 4);
            for ($i = 0; $i < $count; $i++) {
                $n = self::u32($tables['ctts'], 8 + $i * 8);
                $offset = $signed ? self::s32($tables['ctts'], 12 + $i * 8) : self::u32($tables['ctts'], 12 + $i * 8);
                for ($j = 0; $j < $n; $j++) {
                    $compositionOffsets[] = $offset;
                }
            }
        }
        // Sync samples.
        $sync = null;
        if (isset($tables['stss'])) {
            $sync = [];
            $count = self::u32($tables['stss'], 4);
            for ($i = 0; $i < $count; $i++) {
                $sync[self::u32($tables['stss'], 8 + $i * 4)] = true;
            }
        }
        $sample = 0;
        $time = 0;
        $chunkCount = \count($chunkOffsets);
        foreach ($chunkOffsets as $chunkIndex => $chunkOffset) {
            $perChunk = 0;
            foreach ($stsc as [$firstChunk, $samplesPerChunk]) {
                if ($firstChunk <= $chunkIndex + 1) {
                    $perChunk = $samplesPerChunk;
                }
            }
            $offset = $chunkOffset;
            for ($i = 0; $i < $perChunk && $sample < $sampleCount; $i++, $sample++) {
                $size = $sizes[$sample];
                $progressive[$offset] = [
                    'offset' => $offset,
                    'size' => $size,
                    'track' => $trackId,
                    'timestamp' => intdiv(max(0, $time + ($compositionOffsets[$sample] ?? 0) + $shift) * 1000, $timescale),
                    'keyframe' => $sync === null || isset($sync[$sample + 1]),
                ];
                $time += $durations[$sample] ?? 0;
                $offset += $size;
            }
            if ($chunkIndex + 1 >= $chunkCount) {
                break;
            }
        }
    }

    /* ------------------------------------------------------------------ *
     *  moof: fragment runs.
     * ------------------------------------------------------------------ */

    /**
     * The samples described by a movie fragment, by absolute file offset.
     *
     * @return array<int, array{offset: int, size: int, track: int, timestamp: int, keyframe: bool}>
     *
     * @psalm-mutation-free
     */
    private function parseMoof(string $moof, int $moofStart): array
    {
        $samples = [];
        foreach (self::children($moof) as [$type, $payload]) {
            if ($type !== 'traf') {
                continue;
            }
            $trackId = 0;
            $baseOffset = $moofStart;
            $defaults = ['duration' => 0, 'size' => 0, 'flags' => 0];
            $decodeTime = 0;
            $runs = [];
            foreach (self::children($payload) as [$childType, $child]) {
                if ($childType === 'tfhd') {
                    $flags = self::u32($child, 0) & 0xFFFFFF;
                    $trackId = self::u32($child, 4);
                    $defaults = $this->trex[$trackId] ?? $defaults;
                    $offset = 8;
                    if ($flags & self::TFHD_BASE_DATA_OFFSET) {
                        $baseOffset = self::u64($child, $offset);
                        $offset += 8;
                    }
                    if ($flags & 0x2) { // sample description index
                        $offset += 4;
                    }
                    if ($flags & self::TFHD_DEFAULT_SAMPLE_DURATION) {
                        $defaults['duration'] = self::u32($child, $offset);
                        $offset += 4;
                    }
                    if ($flags & self::TFHD_DEFAULT_SAMPLE_SIZE) {
                        $defaults['size'] = self::u32($child, $offset);
                        $offset += 4;
                    }
                    if ($flags & self::TFHD_DEFAULT_SAMPLE_FLAGS) {
                        $defaults['flags'] = self::u32($child, $offset);
                    }
                } elseif ($childType === 'tfdt') {
                    $version = \ord($child[0]);
                    $decodeTime = $version === 1 ? self::u64($child, 4) : self::u32($child, 4);
                } elseif ($childType === 'trun') {
                    $runs[] = $child;
                }
            }
            $track = $this->tracks[$trackId] ?? null;
            if ($track === null) {
                continue;
            }
            $timescale = $track['timescale'];
            $shift = $this->shifts[$trackId] ?? 0;
            $time = $decodeTime;
            $offset = $baseOffset;
            foreach ($runs as $run) {
                $flags = self::u32($run, 0) & 0xFFFFFF;
                $count = self::u32($run, 4);
                $pos = 8;
                if ($flags & self::TRUN_DATA_OFFSET) {
                    $offset = $baseOffset + self::s32($run, $pos);
                    $pos += 4;
                }
                $firstFlags = null;
                if ($flags & self::TRUN_FIRST_SAMPLE_FLAGS) {
                    $firstFlags = self::u32($run, $pos);
                    $pos += 4;
                }
                for ($i = 0; $i < $count; $i++) {
                    $duration = $defaults['duration'];
                    $size = $defaults['size'];
                    $sampleFlags = $i === 0 && $firstFlags !== null ? $firstFlags : $defaults['flags'];
                    if ($flags & self::TRUN_SAMPLE_DURATION) {
                        $duration = self::u32($run, $pos);
                        $pos += 4;
                    }
                    if ($flags & self::TRUN_SAMPLE_SIZE) {
                        $size = self::u32($run, $pos);
                        $pos += 4;
                    }
                    if ($flags & self::TRUN_SAMPLE_FLAGS) {
                        $sampleFlags = self::u32($run, $pos);
                        $pos += 4;
                    }
                    $compositionOffset = 0;
                    if ($flags & self::TRUN_SAMPLE_CTS) {
                        // Signed in version 1.
                        $compositionOffset = \ord($run[0]) === 1 ? self::s32($run, $pos) : self::u32($run, $pos);
                        $pos += 4;
                    }
                    $samples[$offset] = [
                        'offset' => $offset,
                        'size' => $size,
                        'track' => $trackId,
                        'timestamp' => intdiv(max(0, $time + $compositionOffset + $shift) * 1000, $timescale),
                        'keyframe' => ($sampleFlags & self::SAMPLE_NON_SYNC) === 0,
                    ];
                    $offset += $size;
                    $time += $duration;
                }
            }
        }
        return $samples;
    }

    /* ------------------------------------------------------------------ *
     *  Box and integer helpers.
     * ------------------------------------------------------------------ */

    /**
     * The child boxes of a box payload, as `[type, payload]` pairs.
     *
     * @return list<array{0: string, 1: string}>
     *
     * @psalm-pure
     */
    private static function children(string $data): array
    {
        $boxes = [];
        $offset = 0;
        $length = \strlen($data);
        while ($offset + 8 <= $length) {
            $size = self::u32($data, $offset);
            $type = substr($data, $offset + 4, 4);
            $headerSize = 8;
            if ($size === 1 && $offset + 16 <= $length) {
                $size = self::u64($data, $offset + 8);
                $headerSize = 16;
            } elseif ($size === 0) {
                $size = $length - $offset;
            }
            if ($size < $headerSize) {
                break;
            }
            $boxes[] = [$type, substr($data, $offset + $headerSize, $size - $headerSize)];
            $offset += $size;
        }
        return $boxes;
    }

    /** @psalm-pure */
    private static function u16(string $data, int $offset): int
    {
        return \strlen($data) >= $offset + 2 ? (int) unpack('n', substr($data, $offset, 2))[1] : 0;
    }

    /** @psalm-pure */
    private static function u32(string $data, int $offset): int
    {
        return \strlen($data) >= $offset + 4 ? (int) unpack('N', substr($data, $offset, 4))[1] : 0;
    }

    /** @psalm-pure */
    private static function s32(string $data, int $offset): int
    {
        $value = self::u32($data, $offset);
        return $value >= 0x80000000 ? $value - 0x100000000 : $value;
    }

    /** @psalm-pure */
    private static function u64(string $data, int $offset): int
    {
        return \strlen($data) >= $offset + 8 ? (int) unpack('J', substr($data, $offset, 8))[1] : 0;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

/**
 * Which media stream of a call a playback or recording operation targets.
 *
 * A call carries two independent outgoing video streams: the main camera stream (transmitted
 * alongside the microphone audio) and a separate presentation (screen-sharing) stream. The playlist
 * controls ({@see Call::play()} and friends) take one of these to say which stream they act on; it
 * defaults to {@see self::Camera}. Recordings ({@see Call::setOutput()}) pick their streams with {@see CallStream} flags instead.
 */
enum MediaDestination
{
    /** The main stream: the camera video, transmitted together with the microphone audio. */
    case Camera;
    /** The presentation stream: a separate screen-sharing (screencast) video. */
    case Presentation;
}
<?php

declare(strict_types=1);

/**
 * FileCallbackInterface module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

/**
 * File callback interface.
 *
 * @template T
 * @psalm-mutable
 */
interface FileCallbackInterface
{
    /**
     * Get file.
     *
     * @return T
     * @psalm-impure
     */
    public function getFile(): mixed;
    /**
     * Invoke callback.
     *
     * @param float $percent Percent
     * @param float $speed   Speed in mbps
     * @param float $time    Time
     * @psalm-impure
     */
    public function __invoke(float $percent, float $speed, float $time);
}
<?php declare(strict_types=1);

namespace danog\MadelineProto\Db;

use AssertionError;
use danog\AsyncOrm\DbArray;
use danog\AsyncOrm\DbArrayBuilder;
use danog\AsyncOrm\Internal\Containers\CacheContainer;

/**
 * @internal
 * @deprecated Please use https://github.com/danog/AsyncOrm
 */
final class CachedArray extends DbArray
{
    private readonly CacheContainer $cache;

    /**
     * @psalm-pure
     */
    #[\Override]
    public function unset(string|int $key): void
    {
        throw new AssertionError("Unreachable");
    }
    /**
     * @psalm-pure
     */
    #[\Override]
    public function set(string|int $key, mixed $value): void
    {
        throw new AssertionError("Unreachable");
    }
    /**
     * @psalm-pure
     */
    #[\Override]
    public function get(string|int $key): mixed
    {
        throw new AssertionError("Unreachable");
    }
    /**
     * @psalm-pure
     */
    #[\Override]
    public function clear(): void
    {
        throw new AssertionError("Unreachable");
    }
    /**
     * @psalm-pure
     */
    #[\Override]
    public function count(): int
    {
        throw new AssertionError("Unreachable");
    }
    /**
     * @psalm-pure
     */
    #[\Override]
    public function getIterator(): \Traversable
    {
        throw new AssertionError("Unreachable");
    }

    /**
     * @psalm-pure
     */
    #[\Override]
    public static function getInstance(DbArrayBuilder $config, DbArray|null $previous): DbArray
    {
        throw new AssertionError("Unreachable");
    }

}
<?php declare(strict_types=1);

namespace danog\MadelineProto\Db;

use ArrayObject;
use AssertionError;
use danog\AsyncOrm\DbArray;
use danog\AsyncOrm\DbArrayBuilder;

/**
 * @internal
 * @deprecated Please use https://github.com/danog/AsyncOrm
 */
final class MemoryArray extends ArrayObject
{
    /**
     * @psalm-pure
     */
    public function unset(string|int $key): void
    {
        throw new AssertionError("Unreachable");
    }
    /**
     * @psalm-pure
     */
    public function set(string|int $key, mixed $value): void
    {
        throw new AssertionError("Unreachable");
    }
    /**
     * @psalm-pure
     */
    public function get(string|int $key): mixed
    {
        throw new AssertionError("Unreachable");
    }
    /**
     * @psalm-pure
     */
    public function clear(): void
    {
        throw new AssertionError("Unreachable");
    }
    /**
     * @psalm-pure
     */
    #[\Override]
    public function count(): int
    {
        throw new AssertionError("Unreachable");
    }
    /**
     * @psalm-pure
     */
    #[\Override]
    public function getIterator(): \Iterator
    {
        throw new AssertionError("Unreachable");
    }

    /**
     * @psalm-pure
     */
    public static function getInstance(DbArrayBuilder $config, DbArray|null $previous): DbArray
    {
        throw new AssertionError("Unreachable");
    }
}
resPQ#05162463 nonce:int128 server_nonce:int128 pq:string server_public_key_fingerprints:Vector<long> = ResPQ;

vector {t:Type} # [ t ] = Vector t;

p_q_inner_data_dc#a9f55f95 pq:string p:string q:string nonce:int128 server_nonce:int128 new_nonce:int256 dc:int = P_Q_inner_data;
p_q_inner_data_temp_dc#56fddf88 pq:string p:string q:string nonce:int128 server_nonce:int128 new_nonce:int256 dc:int expires_in:int = P_Q_inner_data;

server_DH_params_fail#79cb045d nonce:int128 server_nonce:int128 new_nonce_hash:int128 = Server_DH_Params;
server_DH_params_ok#d0e8075c nonce:int128 server_nonce:int128 encrypted_answer:string = Server_DH_Params;

server_DH_inner_data#b5890dba nonce:int128 server_nonce:int128 g:int dh_prime:string g_a:string server_time:int = Server_DH_inner_data;

client_DH_inner_data#6643b654 nonce:int128 server_nonce:int128 retry_id:long g_b:string = Client_DH_Inner_Data;

dh_gen_ok#3bcbf734 nonce:int128 server_nonce:int128 new_nonce_hash1:int128 = Set_client_DH_params_answer;
dh_gen_retry#46dc1fb9 nonce:int128 server_nonce:int128 new_nonce_hash2:int128 = Set_client_DH_params_answer;
dh_gen_fail#a69dae02 nonce:int128 server_nonce:int128 new_nonce_hash3:int128 = Set_client_DH_params_answer;

bind_auth_key_inner#75a3f765 nonce:long temp_auth_key_id:long perm_auth_key_id:long temp_session_id:long expires_at:int = BindAuthKeyInner;

rpc_result#f35c6d01 req_msg_id:long result:Object = RpcResult;
rpc_error#2144ca19 error_code:int error_message:string = RpcError;

rpc_answer_unknown#5e2ad36e = RpcDropAnswer;
rpc_answer_dropped_running#cd78e586 = RpcDropAnswer;
rpc_answer_dropped#a43ad8b7 msg_id:long seq_no:int bytes:int = RpcDropAnswer;

future_salt#0949d9dc valid_since:int valid_until:int salt:long = FutureSalt;
future_salts#ae500895 req_msg_id:long now:int salts:vector<future_salt> = FutureSalts;

pong#347773c5 msg_id:long ping_id:long = Pong;

destroy_session_ok#e22045fc session_id:long = DestroySessionRes;
destroy_session_none#62d350c9 session_id:long = DestroySessionRes;

new_session_created#9ec20908 first_msg_id:long unique_id:long server_salt:long = NewSession;

msg_container#73f1f8dc messages:vector<%Message> = MessageContainer;
message msg_id:long seqno:int bytes:int body:Object = Message;
msg_copy#e06046b2 orig_message:Message = MessageCopy;

gzip_packed#3072cfa1 packed_data:bytes = Object;

msgs_ack#62d6b459 msg_ids:Vector<long> = MsgsAck;

bad_msg_notification#a7eff811 bad_msg_id:long bad_msg_seqno:int error_code:int = BadMsgNotification;
bad_server_salt#edab447b bad_msg_id:long bad_msg_seqno:int error_code:int new_server_salt:long = BadMsgNotification;

msg_resend_ans_req#8610baeb msg_ids:Vector<long> = MsgResendReq;
msg_resend_req#7d861a08 msg_ids:Vector<long> = MsgResendReq;
msgs_state_req#da69fb52 msg_ids:Vector<long> = MsgsStateReq;
msgs_state_info#04deb57d req_msg_id:long info:string = MsgsStateInfo;
msgs_all_info#8cc0d131 msg_ids:Vector<long> info:string = MsgsAllInfo;
msg_detailed_info#276d3ec6 msg_id:long answer_msg_id:long bytes:int status:int = MsgDetailedInfo;
msg_new_detailed_info#809db6df answer_msg_id:long bytes:int status:int = MsgDetailedInfo;

rsa_public_key n:string e:string = RSAPublicKey;

http_wait#9299359f max_delay:int wait_after:int max_wait:int = HttpWait;

---functions---

req_pq_multi#be7e8ef1 nonce:int128 = ResPQ;

req_pq nonce:int128 = ResPQ;

req_DH_params#d712e4be nonce:int128 server_nonce:int128 p:string q:string public_key_fingerprint:long encrypted_data:string = Server_DH_Params;

set_client_DH_params#f5045f1f nonce:int128 server_nonce:int128 encrypted_data:string = Set_client_DH_params_answer;

rpc_drop_answer#58e4a740 req_msg_id:long = RpcDropAnswer;
get_future_salts#b921bd04 num:int = FutureSalts;
ping#7abe77ec ping_id:long = Pong;
ping_delay_disconnect#f3427b8c ping_id:long disconnect_delay:int = Pong;
destroy_session#e7512126 session_id:long = DestroySessionRes;

//test.useGzipPacked = GzipPacked;
//test.useServerDhInnerData = Server_DH_inner_data;
//test.useNewSessionCreated = NewSession;
//test.useMsgsAck = MsgsAck;
//test.useBadMsgNotification = BadMsgNotification;

//test.useOther key:rsa_public_key p_q_data:P_Q_inner_data dh_data:client_DH_inner_data = RpcError;
<?php

declare(strict_types=1);

/**
 * APIFactory module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use Amp\Future\UnhandledFutureError;
use Amp\SignalException;
use Revolt\EventLoop;

abstract class AbstractAPI extends InternalDoc
{
    /**
     * Start MadelineProto and the event handler (enables async).
     *
     * Also initializes error reporting, catching and reporting all errors surfacing from the event loop.
     *
     * @param string $eventHandler Event handler class name
     */
    protected function startAndLoopInternal(string $eventHandler): void
    {
        $started = false;
        $errors = [];
        $prev = EventLoop::getErrorHandler();
        EventLoop::setErrorHandler(
            $cb = function (\Throwable $e) use (&$errors, &$started): void {
                if ($e instanceof UnhandledFutureError) {
                    $e = $e->getPrevious();
                }
                if ($e instanceof SecurityException || $e instanceof SignalException) {
                    throw $e;
                }
                if (str_starts_with($e->getMessage(), 'Could not connect to DC ')) {
                    throw $e;
                }
                $t = time();
                $errors = [$t => $errors[$t] ?? 0];
                $errors[$t]++;
                if ($errors[$t] > 10 && (!$this->wrapper->getAPI()->isInited() || !$started)) {
                    $this->wrapper->logger('More than 10 errors in a second and not inited, exiting!', Logger::FATAL_ERROR);
                    return;
                }
                $eOrig = (string) $e;
                $e = Tools::taintEscape((string) $e);
                echo $e;
                $this->wrapper->logger($eOrig, Logger::FATAL_ERROR);
                $this->report("Surfaced: $eOrig");
            }
        );
        try {
            $this->startAndLoopLogic($eventHandler, $started);
        } finally {
            if (EventLoop::getErrorHandler() === $cb) {
                EventLoop::setErrorHandler($prev);
            }
        }
    }
    /** @psalm-impure */
    abstract protected function reconnectFull(): bool;

    protected function startAndLoopLogic(string $eventHandler, bool &$started): void
    {
        $this->start();
        if (!$this->reconnectFull()) {
            return;
        }

        $this->wrapper->getAPI()->setEventHandler($eventHandler);
        $started = true;
        /** @psalm-suppress TooFewArguments Always MTProto here */
        $this->wrapper->getAPI()->loop();
    }
    /**
     * Sleep function.
     *
     * @psalm-pure
     */
    public function __sleep(): array
    {
        return [];
    }
}
<?php

declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use Amp\ByteStream\WritableStream;
use Webmozart\Assert\Assert;

/**
 * Async OGG stream writer.
 *
 * @author Daniil Gentili <daniil@daniil.it>
 */
final class OggWriter
{
    private const CRC = [
        0x00000000,0x04c11db7,0x09823b6e,0x0d4326d9,
        0x130476dc,0x17c56b6b,0x1a864db2,0x1e475005,
        0x2608edb8,0x22c9f00f,0x2f8ad6d6,0x2b4bcb61,
        0x350c9b64,0x31cd86d3,0x3c8ea00a,0x384fbdbd,
        0x4c11db70,0x48d0c6c7,0x4593e01e,0x4152fda9,
        0x5f15adac,0x5bd4b01b,0x569796c2,0x52568b75,
        0x6a1936c8,0x6ed82b7f,0x639b0da6,0x675a1011,
        0x791d4014,0x7ddc5da3,0x709f7b7a,0x745e66cd,
        0x9823b6e0,0x9ce2ab57,0x91a18d8e,0x95609039,
        0x8b27c03c,0x8fe6dd8b,0x82a5fb52,0x8664e6e5,
        0xbe2b5b58,0xbaea46ef,0xb7a96036,0xb3687d81,
        0xad2f2d84,0xa9ee3033,0xa4ad16ea,0xa06c0b5d,
        0xd4326d90,0xd0f37027,0xddb056fe,0xd9714b49,
        0xc7361b4c,0xc3f706fb,0xceb42022,0xca753d95,
        0xf23a8028,0xf6fb9d9f,0xfbb8bb46,0xff79a6f1,
        0xe13ef6f4,0xe5ffeb43,0xe8bccd9a,0xec7dd02d,
        0x34867077,0x30476dc0,0x3d044b19,0x39c556ae,
        0x278206ab,0x23431b1c,0x2e003dc5,0x2ac12072,
        0x128e9dcf,0x164f8078,0x1b0ca6a1,0x1fcdbb16,
        0x018aeb13,0x054bf6a4,0x0808d07d,0x0cc9cdca,
        0x7897ab07,0x7c56b6b0,0x71159069,0x75d48dde,
        0x6b93dddb,0x6f52c06c,0x6211e6b5,0x66d0fb02,
        0x5e9f46bf,0x5a5e5b08,0x571d7dd1,0x53dc6066,
        0x4d9b3063,0x495a2dd4,0x44190b0d,0x40d816ba,
        0xaca5c697,0xa864db20,0xa527fdf9,0xa1e6e04e,
        0xbfa1b04b,0xbb60adfc,0xb6238b25,0xb2e29692,
        0x8aad2b2f,0x8e6c3698,0x832f1041,0x87ee0df6,
        0x99a95df3,0x9d684044,0x902b669d,0x94ea7b2a,
        0xe0b41de7,0xe4750050,0xe9362689,0xedf73b3e,
        0xf3b06b3b,0xf771768c,0xfa325055,0xfef34de2,
        0xc6bcf05f,0xc27dede8,0xcf3ecb31,0xcbffd686,
        0xd5b88683,0xd1799b34,0xdc3abded,0xd8fba05a,
        0x690ce0ee,0x6dcdfd59,0x608edb80,0x644fc637,
        0x7a089632,0x7ec98b85,0x738aad5c,0x774bb0eb,
        0x4f040d56,0x4bc510e1,0x46863638,0x42472b8f,
        0x5c007b8a,0x58c1663d,0x558240e4,0x51435d53,
        0x251d3b9e,0x21dc2629,0x2c9f00f0,0x285e1d47,
        0x36194d42,0x32d850f5,0x3f9b762c,0x3b5a6b9b,
        0x0315d626,0x07d4cb91,0x0a97ed48,0x0e56f0ff,
        0x1011a0fa,0x14d0bd4d,0x19939b94,0x1d528623,
        0xf12f560e,0xf5ee4bb9,0xf8ad6d60,0xfc6c70d7,
        0xe22b20d2,0xe6ea3d65,0xeba91bbc,0xef68060b,
        0xd727bbb6,0xd3e6a601,0xdea580d8,0xda649d6f,
        0xc423cd6a,0xc0e2d0dd,0xcda1f604,0xc960ebb3,
        0xbd3e8d7e,0xb9ff90c9,0xb4bcb610,0xb07daba7,
        0xae3afba2,0xaafbe615,0xa7b8c0cc,0xa379dd7b,
        0x9b3660c6,0x9ff77d71,0x92b45ba8,0x9675461f,
        0x8832161a,0x8cf30bad,0x81b02d74,0x857130c3,
        0x5d8a9099,0x594b8d2e,0x5408abf7,0x50c9b640,
        0x4e8ee645,0x4a4ffbf2,0x470cdd2b,0x43cdc09c,
        0x7b827d21,0x7f436096,0x7200464f,0x76c15bf8,
        0x68860bfd,0x6c47164a,0x61043093,0x65c52d24,
        0x119b4be9,0x155a565e,0x18197087,0x1cd86d30,
        0x029f3d35,0x065e2082,0x0b1d065b,0x0fdc1bec,
        0x3793a651,0x3352bbe6,0x3e119d3f,0x3ad08088,
        0x2497d08d,0x2056cd3a,0x2d15ebe3,0x29d4f654,
        0xc5a92679,0xc1683bce,0xcc2b1d17,0xc8ea00a0,
        0xd6ad50a5,0xd26c4d12,0xdf2f6bcb,0xdbee767c,
        0xe3a1cbc1,0xe760d676,0xea23f0af,0xeee2ed18,
        0xf0a5bd1d,0xf464a0aa,0xf9278673,0xfde69bc4,
        0x89b8fd09,0x8d79e0be,0x803ac667,0x84fbdbd0,
        0x9abc8bd5,0x9e7d9662,0x933eb0bb,0x97ffad0c,
        0xafb010b1,0xab710d06,0xa6322bdf,0xa2f33668,
        0xbcb4666d,0xb8757bda,0xb5365d03,0xb1f740b4,
    ];

    private int $granule = 0;
    private int $seqno = 0;
    public readonly int $streamId;
    /**
     * Write an ogg OPUS file.
     */
    public function __construct(
        private readonly WritableStream $out,
        ?int $streamId = null
    ) {
        $this->streamId = $streamId ?? random_int(-(2**31), (2**31)-1);
    }

    private function writePage(int $header_type_flag, int $granule, string $packet): void
    {
        Assert::true(\strlen($packet) < 65025);
        $segments = [
            ...array_fill(0, (int) (\strlen($packet) / 255), 255),
            \strlen($packet) % 255,
        ];
        $data = 'OggS'.pack(
            'CCPVVVCC*',
            0, // stream_structure_version
            $header_type_flag,
            $granule,
            $this->streamId,
            $this->seqno++,
            0,
            \count($segments),
            ...$segments
        ).$packet;

        $c = 0;
        for ($i = 0; $i < \strlen($data); $i++) {
            $c = ($c<<8)^self::CRC[(($c >> 24)&0xFF)^(\ord($data[$i]))];
        }
        $crc = pack('V', $c);

        $data = substr_replace(
            $data,
            $crc,
            22,
            4
        );
        $this->out->write($data);
    }
    /**
     * Write the OpusHead and OpusTags header pages.
     *
     * @param string|null $opusHead A complete OpusHead packet to use verbatim (for a multichannel or
     *                              pre-existing stream), instead of building a family-0 one from
     *                              `$channels` and `$sampleRate`.
     */
    public function writeHeader(
        int $channels,
        int $sampleRate,
        string $opusVersion,
        ?string $opusHead = null,
    ): void {
        $this->writePage(
            Ogg::BOS,
            0,
            $opusHead ?? 'OpusHead'.pack(
                'CCvVvC',
                1,
                $channels,
                312,
                $sampleRate,
                0,
                0,
            )
        );

        $tags = 'OpusTags';
        $writeTag = static function (string $tag) use (&$tags): void {
            $tags .= pack('V', \strlen($tag)).$tag;
        };
        $writeTag("MadelineProto ".API::RELEASE.", $opusVersion");
        $tags .= pack('V', 2);
        $writeTag("ENCODER=MadelineProto ".API::RELEASE." with $opusVersion");
        $writeTag("MADELINE_ENCODER_V=1");
        $writeTag('See https://docs.madelineproto.xyz/docs/CALLS.html for more info');
        $this->writePage(
            0,
            0,
            $tags
        );

    }
    public function writeChunk(string $chunk, int $granuleDiff, bool $eos): void
    {
        $this->writePage(
            $eos ? Ogg::EOS : 0,
            $this->granule += $granuleDiff,
            $chunk
        );

        if ($eos) {
            $this->out->close();
        }
    }
}
<?php

declare(strict_types=1);

/**
 * DataCenter DoH proxying AMPHP connector.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use Amp\Cancellation;
use Amp\CancelledException;
use Amp\DeferredFuture;
use Amp\Dns\DnsRecord;
use Amp\Dns\DnsTimeoutException;
use Amp\NullCancellation;
use Amp\Socket\ConnectContext;
use Amp\Socket\ConnectException;
use Amp\Socket\ResourceSocket;
use Amp\Socket\Socket;
use Amp\Socket\SocketAddress;
use Amp\Socket\SocketConnector;
use AssertionError;
use danog\MadelineProto\Stream\ConnectionContext;
use Revolt\EventLoop;

use const STREAM_CLIENT_ASYNC_CONNECT;

use const STREAM_CLIENT_CONNECT;
use function Amp\Socket\Internal\parseUri;

/** @internal */
final class DoHConnector implements SocketConnector
{
    /**
     * @psalm-mutation-free
     */
    public function __construct(private DoHWrapper $dataCenter, private ConnectionContext $ctx)
    {
    }
    #[\Override]
    public function connect(
        SocketAddress|string $uri,
        ?ConnectContext $context = null,
        ?Cancellation $token = null
    ): Socket {
        $socketContext = $context ?? new ConnectContext();
        $token ??= new NullCancellation();
        $uris = [];
        $failures = [];
        [$scheme, $host, $port] = parseUri($uri);
        if ($host[0] === '[') {
            $host = substr($host, 1, -1);
        }
        if ($port === 0 || inet_pton($host)) {
            // Host is already an IP address or file path.
            $uris = [$uri];
        } else {
            // Host is not an IP address, so resolve the domain name.
            // When we're connecting to a host, we may need to resolve the domain name, first.
            // The resolution is usually done using DNS over HTTPS.
            //
            // The DNS over HTTPS resolver needs to resolve the domain name of the DOH server:
            // this is handled internally by the DNS over HTTPS client,
            // by redirecting the resolution request to the plain DNS client.
            //
            // However, if the DoH connection is proxied with a proxy that has a domain name itself,
            // we cannot resolve it with the DoH resolver, since this will cause an infinite loop
            //
            // resolve host.com => (DoH resolver) => resolve dohserver.com => (simple resolver) => OK
            //
            //                                     |> resolve dohserver.com => (simple resolver) => OK
            // resolve host.com => (DoH resolver) =|
            //                                     |> resolve proxy.com => (non-proxied resolver) => OK
            //
            //
            // This means that we must detect if the domain name we're trying to resolve is a proxy domain name.
            //
            // Here, we simply check if the connection URI has changed since we first set it:
            // this would indicate that a proxy class has changed the connection URI to the proxy URI.
            if ($this->ctx->isDns()) {
                $records = $this->dataCenter->nonProxiedDoHClient->resolve($host, $socketContext->getDnsTypeRestriction());
            } else {
                $records = $this->dataCenter->DoHClient->resolve($host, $socketContext->getDnsTypeRestriction());
            }
            usort($records, static fn (DnsRecord $a, DnsRecord $b) => $a->getType() - $b->getType());
            if ($this->ctx->getIpv6()) {
                $records = array_reverse($records);
            }
            foreach ($records as $record) {
                if ($record->getType() === DnsRecord::AAAA) {
                    $uris[] = \sprintf('%s://[%s]:%d', $scheme, $record->getValue(), $port);
                } else {
                    $uris[] = \sprintf('%s://%s:%d', $scheme, $record->getValue(), $port);
                }
            }
        }
        $flags = STREAM_CLIENT_CONNECT | STREAM_CLIENT_ASYNC_CONNECT;
        $timeout = $socketContext->getConnectTimeout();
        $e = null;
        foreach ($uris as $builtUri) {
            try {
                $streamContext = stream_context_create($socketContext->withoutTlsContext()->toStreamContextArray());
                try {
                    /** @psalm-suppress NullArgument */
                    $socket = stream_socket_client($builtUri, $errno, $errstr, null, $flags, $streamContext);
                } catch (\Throwable) {
                    // Synchronous failures (no route to the host) warn: $errno and $errstr are set all the same
                    $socket = false;
                }
                if (!$socket) {
                    throw new ConnectException(\sprintf('Connection to %s failed: [Error #%d] %s%s', (string) $uri, $errno, $errstr, $failures ? '; previous attempts: '.implode('', $failures) : ''), $errno);
                }
                stream_set_blocking($socket, false);
                $deferred = new DeferredFuture();
                /** @psalm-suppress InvalidArgument */
                $watcher = EventLoop::onWritable($socket, $deferred->complete(...));
                $id = $token->subscribe($deferred->error(...));
                try {
                    $deferred->getFuture()->await(Tools::getTimeoutCancellation($timeout, "Timeout while connecting to $uri"));
                } catch (CancelledException $e) {
                    if (!$e->getPrevious() instanceof DnsTimeoutException) {
                        throw $e;
                    }

                    throw new ConnectException(\sprintf('Connecting to %s failed: timeout exceeded (%d ms)%s', (string) $uri, $timeout, $failures ? '; previous attempts: '.implode('', $failures) : ''), 110);
                    // See ETIMEDOUT in http://www.virtsync.com/c-error-codes-include-errno
                } finally {
                    EventLoop::cancel($watcher);
                    $token->unsubscribe($id);
                }
                // The following hack looks like the only way to detect connection refused errors with PHP's stream sockets.
                if (stream_socket_get_name($socket, true) === false) {
                    fclose($socket);
                    throw new ConnectException(\sprintf('Connection to %s refused%s', (string) $uri, $failures ? '; previous attempts: '.implode('', $failures) : ''), 111);
                    // See ECONNREFUSED in http://www.virtsync.com/c-error-codes-include-errno
                }
            } catch (ConnectException $e) {
                // Includes only error codes used in this file, as error codes on other OS families might be different.
                // In fact, this might show a confusing error message on OS families that return 110 or 111 by itself.
                $knownReasons = [110 => 'connection timeout', 111 => 'connection refused'];
                $code = $e->getCode();
                $reason = $knownReasons[$code] ?? 'Error #'.$code;
                $failures[] = "{$uri} ({$reason})";
                continue;
            }
            return ResourceSocket::fromClientSocket($socket, $socketContext->getTlsContext());
        }

        // This is reached if either all URIs failed or the maximum number of attempts is reached.
        if ($e) {
            throw $e;
        }
        throw new AssertionError("Unreachable!");
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Tgcalls;

/**
 * Rewrites the H.264 frames of a Matroska file into the byte stream RTP expects.
 *
 * Matroska stores H.264 the way MP4 does: each frame is a series of NAL units prefixed by their
 * length, and the parameter sets that describe the stream live once in the track's `CodecPrivate`
 * as an `AVCDecoderConfigurationRecord`. RTP instead wants the Annex B byte stream, where NAL units
 * are separated by start codes, and it wants the parameter sets repeated in-band: a group call
 * participant may start watching at any point, and without an SPS and PPS ahead of the first
 * keyframe they see nothing at all.
 *
 * Nothing is decoded here, only reframed, so H.264 files play without the FFI extension just like
 * VP8 and VP9 ones do.
 *
 * @internal
 *
 * @psalm-immutable
 */
final class H264Framing
{
    // Also serves H.265: the two share the length-prefixed sample layout, only the configuration
    // record that carries the parameter sets differs (avcC vs hvcC).
    /** The four byte Annex B start code, used for every NAL unit we emit. */
    private const START_CODE = "\x00\x00\x00\x01";

    /** How many bytes prefix each NAL unit in the source frames. */
    private int $lengthSize = 4;

    /** The SPS and PPS of the track, already in Annex B form, or `''` if the track declared none. */
    private string $parameterSets = '';

    /** Whether the source frames are length prefixed, as opposed to being Annex B already. */
    private bool $lengthPrefixed = false;

    /**
     * @param string|null $codecPrivate The track's `CodecPrivate`, if it declared one.
     *
     * @psalm-mutation-free
     */
    /**
     * @param bool $hevc Whether the record is an HEVCDecoderConfigurationRecord (`hvcC`) rather
     *                   than an AVCDecoderConfigurationRecord (`avcC`); both start with version 1.
     */
    public function __construct(?string $codecPrivate, bool $hevc = false)
    {
        if ($codecPrivate === null || \strlen($codecPrivate) < ($hevc ? 23 : 7) || $codecPrivate[0] !== "\x01") {
            // No configuration record: the muxer stored a raw Annex B stream, which some tools do
            // even though the specification asks for the length prefixed form.
            return;
        }
        $this->lengthPrefixed = true;
        if ($hevc) {
            $this->lengthSize = (\ord($codecPrivate[21]) & 0x03) + 1;
            $this->parameterSets = self::parseHevcParameterSets($codecPrivate);
            return;
        }
        $this->lengthSize = (\ord($codecPrivate[4]) & 0x03) + 1;
        $this->parameterSets = self::parseParameterSets($codecPrivate);
    }

    /**
     * The VPS/SPS/PPS (and any other) NAL unit arrays of an `hvcC` record, as an Annex B prefix.
     *
     * @psalm-pure
     */
    private static function parseHevcParameterSets(string $record): string
    {
        $result = '';
        $length = \strlen($record);
        $offset = 22;
        $arrays = \ord($record[$offset++]);
        for ($a = 0; $a < $arrays && $offset + 3 <= $length; $a++) {
            $offset++; // array_completeness + NAL_unit_type
            $count = unpack('n', substr($record, $offset, 2))[1];
            $offset += 2;
            for ($i = 0; $i < $count; $i++) {
                if ($offset + 2 > $length) {
                    return $result;
                }
                $size = unpack('n', substr($record, $offset, 2))[1];
                $offset += 2;
                if ($size === 0 || $offset + $size > $length) {
                    return $result;
                }
                $result .= self::START_CODE.substr($record, $offset, $size);
                $offset += $size;
            }
        }
        return $result;
    }

    /**
     * Whether frames need any rewriting at all.
     */
    public function isLengthPrefixed(): bool
    {
        return $this->lengthPrefixed;
    }

    /**
     * Convert one frame into an Annex B byte stream.
     *
     * @param string $frame    The frame as the demuxer produced it.
     * @param bool   $keyframe Whether the parameter sets have to be repeated before it.
     *
     * @psalm-mutation-free
     */
    public function convert(string $frame, bool $keyframe): string
    {
        if (!$this->lengthPrefixed) {
            return $keyframe ? $this->parameterSets.$frame : $frame;
        }

        $result = $keyframe ? $this->parameterSets : '';
        $offset = 0;
        $length = \strlen($frame);
        while ($offset + $this->lengthSize <= $length) {
            $size = self::readLength($frame, $offset, $this->lengthSize);
            $offset += $this->lengthSize;
            if ($size <= 0 || $offset + $size > $length) {
                // A truncated or nonsensical NAL unit: everything after it is unusable too.
                break;
            }
            $result .= self::START_CODE.substr($frame, $offset, $size);
            $offset += $size;
        }

        return $result;
    }

    /**
     * Extract the SPS and PPS of an `AVCDecoderConfigurationRecord`, as an Annex B stream.
     *
     * @psalm-pure
     */
    private static function parseParameterSets(string $record): string
    {
        $result = '';
        $length = \strlen($record);
        // Byte 5 holds the SPS count in its low five bits, the top three are reserved and set.
        $offset = 5;
        $count = \ord($record[$offset++]) & 0x1F;

        // The PPS count is a full byte, and follows the last SPS.
        for ($set = 0; $set < 2; $set++) {
            for ($i = 0; $i < $count; $i++) {
                if ($offset + 2 > $length) {
                    return $result;
                }
                /** @var int $size */
                $size = unpack('n', substr($record, $offset, 2))[1];
                $offset += 2;
                if ($size === 0 || $offset + $size > $length) {
                    return $result;
                }
                $result .= self::START_CODE.substr($record, $offset, $size);
                $offset += $size;
            }
            if ($set === 0) {
                if ($offset >= $length) {
                    return $result;
                }
                $count = \ord($record[$offset++]);
            }
        }

        return $result;
    }

    /**
     * Read a big endian NAL unit length of `$size` bytes.
     *
     * @psalm-pure
     */
    private static function readLength(string $frame, int $offset, int $size): int
    {
        $value = 0;
        for ($i = 0; $i < $size; $i++) {
            $value = ($value << 8) | \ord($frame[$offset + $i]);
        }
        return $value;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

// IMPORTANT NOTE: Please keep the above copyright notice intact if copying or rewriting this file in another language.

namespace danog\MadelineProto\Tgcalls;

/**
 * The two endpoints a {@see SignalingSctpTransport} needs: where to put its outgoing SCTP packets,
 * and where to deliver the reassembled signaling messages it receives.
 *
 * {@see SignalingSctpTransport} keeps a reference to its observer as part of its serializable state,
 * so this has to be a real, serializable object rather than `Closure`s (which PHP cannot serialize):
 * the association survives a serialize/unserialize cycle, and so must both of its callbacks.
 *
 * @internal
 * @psalm-mutable
 */
interface SctpSignalingObserver
{
    /**
     * Put one SCTP packet of the signaling association on the wire.
     * @psalm-impure
     */
    public function deliverSignalingPacket(string $packet): void;

    /**
     * Handle one reassembled signaling message received over the association.
     * @psalm-impure
     */
    public function onSignalingMessageData(string $message): void;
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Tgcalls;

use danog\MadelineProto\Logger;
use Psr\Log\AbstractLogger;

/**
 * Forwards php-rtc's PSR debug output (DTLS, SRTP, SCTP and RTP routing diagnostics) into a call's
 * log at VERBOSE level, when the `MP_RTC_DEBUG=1` environment variable asks for it.
 *
 * @internal
 */
final class RtcDebugLogger extends AbstractLogger
{
    public function __construct(private readonly CallControllerInterface $call)
    {
    }

    /**
     * @param string|\Stringable $message
     */
    #[\Override]
    public function log($level, $message, array $context = []): void
    {
        $this->call->log("[rtc $level] $message", Logger::VERBOSE);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

// IMPORTANT NOTE: Please keep the above copyright notice intact if copying or rewriting this file in another language.

namespace danog\MadelineProto\Tgcalls;

/**
 * Bridges the presentation playlist's {@see VideoCodecObserver} events to the {@see Controller}'s
 * screencast handling, so the one-to-one Controller can observe the codecs of both its camera playlist
 * (as the observer itself) and its separate presentation playlist (through this adapter).
 *
 * A dedicated named class rather than a closure, because â€” like the {@see Controller} â€” it is part of
 * the call's serializable state and must survive a serialize/unserialize cycle.
 *
 * @internal
 * @psalm-mutable
 */
final class ScreencastCodecObserver implements VideoCodecObserver
{
    /**
     * @psalm-mutation-free
     */
    public function __construct(private readonly Controller $controller)
    {
    }

    /**
     * @param array<string, string> $parameters
     * @psalm-impure
     */
    #[\Override]
    public function onVideoCodec(string $codec, array $parameters = []): void
    {
        $this->controller->onScreencastCodec($codec, $parameters);
    }

    /**
     * @psalm-impure
     */
    #[\Override]
    public function onVideoStopped(): void
    {
        $this->controller->onScreencastStopped();
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

// IMPORTANT NOTE: Please keep the above copyright notice intact if copying or rewriting this file in another language.

namespace danog\MadelineProto\Tgcalls;

use danog\MadelineProto\Exception;
use Webrtc\SDP\Enum\SDPDirections;

/**
 * Translation layer between the JSON payloads used by the Telegram group call SFU and SDP.
 *
 * The Telegram SFU does not speak SDP: clients send a small JSON "join payload" describing their
 * ICE/DTLS parameters and outgoing audio SSRC, and receive back the SFU's own ICE/DTLS parameters
 * and candidates. Since we drive a standard `RTCPeerConnection`, we synthesize the matching SDP
 * answer here, using the locally generated offer as a template so that the codec and extension
 * lines always stay consistent with what the local stack negotiated.
 *
 * The wire format is documented at https://core.telegram.org/api/group-calls and implemented by
 * tgcalls in `group/GroupJoinPayloadInternal.cpp`.
 *
 * @internal
 */
final class GroupSdp
{
    /**
     * The payload type the Telegram SFU always uses for OPUS.
     *
     * The payload types and header extension IDs below are the ones tgcalls assigns in
     * `GroupInstanceCustomImpl.cpp`, and the ones the SFU announces back in its join response: the
     * SFU forwards RTP as-is, so every participant has to agree on them out of band.
     */
    public const OPUS_PAYLOAD_TYPE = 111;
    /**
     * The payload type tgcalls offers alongside OPUS for raw PCM.
     *
     * Only the broadcast path ever selects it, but tgcalls always lists it, so we do too: a peer
     * that decides to send raw PCM must not be rejected while negotiating.
     */
    public const L16_PAYLOAD_TYPE = 112;
    /** The payload type the Telegram SFU always uses for VP8. */
    public const VP8_PAYLOAD_TYPE = 100;
    /** The payload type of the retransmission stream paired with VP8. */
    public const VP8_RTX_PAYLOAD_TYPE = 101;
    /** The payload type the Telegram SFU always uses for VP9. */
    public const VP9_PAYLOAD_TYPE = 102;
    /** The payload type of the retransmission stream paired with VP9. */
    public const VP9_RTX_PAYLOAD_TYPE = 103;
    /** The payload type the Telegram SFU always uses for H.264. */
    public const H264_PAYLOAD_TYPE = 104;
    /** The payload type of the retransmission stream paired with H.264. */
    public const H264_RTX_PAYLOAD_TYPE = 105;
    /** The RTP header extension ID the Telegram SFU always uses for the audio level. */
    public const AUDIO_LEVEL_EXTENSION_ID = 1;
    private const AUDIO_LEVEL_URI = 'urn:ietf:params:rtp-hdrext:ssrc-audio-level';
    private const ABS_SEND_TIME_ID = 2;
    private const ABS_SEND_TIME_URI = 'http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time';
    private const TRANSPORT_CC_ID = 3;
    private const TRANSPORT_CC_URI = 'http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01';
    private const VIDEO_ORIENTATION_ID = 13;
    private const VIDEO_ORIENTATION_URI = 'urn:3gpp:video-orientation';

    /**
     * The RTCP feedback tgcalls enables on every video codec of a group call.
     *
     * See `addDefaultFeedbackParams()` and `configureVideoParams()` in
     * `GroupInstanceCustomImpl.cpp`.
     */
    private const VIDEO_FEEDBACK = ['goog-remb', 'transport-cc', 'ccm fir', 'nack', 'nack pli'];

    /**
     * Every codec a Telegram group call can carry, keyed by media kind.
     *
     * tgcalls hands the SFU whatever its encoder factory supports, filtered down to VP8, VP9 and
     * H.264 by `filterSupportedVideoFormats()` and numbered from 100 upwards by
     * `assignPayloadTypes()`, each codec immediately followed by its retransmission stream. That
     * numbering is effectively part of the wire protocol: the SFU forwards RTP verbatim, so a
     * client that skipped a codec it cannot handle would shift everyone else's payload types and
     * decode the wrong stream. We therefore always list the full table; {@see buildAnswer()} then
     * pins our own outgoing m-line to the codec of the file being played, and the local stack drops
     * from the negotiated set whatever it has no depacketizer for.
     *
     * The H.264 format parameters are constrained baseline with non-interleaved packetization:
     * tgcalls ranks it second in `filterSupportedVideoFormats()`, behind constrained high, and it
     * is the profile our own packetizer handles, so it is the one both ends can always agree on.
     *
     * @var array<string, list<array{id: int, name: string, clockrate: int, channels: int, parameters: array<string, string>, feedback: list<string>}>>
     */
    private const CODECS = [
        'audio' => [
            [
                'id' => self::OPUS_PAYLOAD_TYPE,
                'name' => 'opus',
                'clockrate' => 48000,
                'channels' => 2,
                'parameters' => ['minptime' => '10', 'useinbandfec' => '1'],
                'feedback' => ['transport-cc'],
            ],
            [
                'id' => self::L16_PAYLOAD_TYPE,
                'name' => 'L16',
                'clockrate' => 48000,
                'channels' => 1,
                'parameters' => [],
                'feedback' => [],
            ],
        ],
        'video' => [
            [
                'id' => self::VP8_PAYLOAD_TYPE,
                'name' => 'VP8',
                'clockrate' => 90000,
                'channels' => 0,
                'parameters' => [],
                'feedback' => self::VIDEO_FEEDBACK,
            ],
            [
                'id' => self::VP8_RTX_PAYLOAD_TYPE,
                'name' => 'rtx',
                'clockrate' => 90000,
                'channels' => 0,
                'parameters' => ['apt' => self::VP8_PAYLOAD_TYPE],
                'feedback' => [],
            ],
            [
                'id' => self::VP9_PAYLOAD_TYPE,
                'name' => 'VP9',
                'clockrate' => 90000,
                'channels' => 0,
                'parameters' => ['profile-id' => '0'],
                'feedback' => self::VIDEO_FEEDBACK,
            ],
            [
                'id' => self::VP9_RTX_PAYLOAD_TYPE,
                'name' => 'rtx',
                'clockrate' => 90000,
                'channels' => 0,
                'parameters' => ['apt' => self::VP9_PAYLOAD_TYPE],
                'feedback' => [],
            ],
            [
                'id' => self::H264_PAYLOAD_TYPE,
                'name' => 'H264',
                'clockrate' => 90000,
                'channels' => 0,
                'parameters' => [
                    'level-asymmetry-allowed' => '1',
                    'packetization-mode' => '1',
                    'profile-level-id' => '42e01f',
                ],
                'feedback' => self::VIDEO_FEEDBACK,
            ],
            [
                'id' => self::H264_RTX_PAYLOAD_TYPE,
                'name' => 'rtx',
                'clockrate' => 90000,
                'channels' => 0,
                'parameters' => ['apt' => self::H264_PAYLOAD_TYPE],
                'feedback' => [],
            ],
        ],
    ];

    /**
     * The RTP header extensions tgcalls negotiates on every group call m-line.
     *
     * Note that the audio level extension is offered on video m-lines too, exactly like tgcalls
     * does in `IncomingVideoChannel` and `configureVideoParams()`.
     *
     * @var array<string, list<array{id: int, uri: string}>>
     */
    private const EXTENSIONS = [
        'audio' => [
            ['id' => self::AUDIO_LEVEL_EXTENSION_ID, 'uri' => self::AUDIO_LEVEL_URI],
            ['id' => self::ABS_SEND_TIME_ID, 'uri' => self::ABS_SEND_TIME_URI],
            ['id' => self::TRANSPORT_CC_ID, 'uri' => self::TRANSPORT_CC_URI],
        ],
        'video' => [
            ['id' => self::AUDIO_LEVEL_EXTENSION_ID, 'uri' => self::AUDIO_LEVEL_URI],
            ['id' => self::ABS_SEND_TIME_ID, 'uri' => self::ABS_SEND_TIME_URI],
            ['id' => self::TRANSPORT_CC_ID, 'uri' => self::TRANSPORT_CC_URI],
            ['id' => self::VIDEO_ORIENTATION_ID, 'uri' => self::VIDEO_ORIENTATION_URI],
        ],
    ];

    /**
     * Build the JSON join payload sent to
     * [phone.joinGroupCall](https://core.telegram.org/method/phone.joinGroupCall).
     *
     * @param string                     $ufrag       Local ICE username fragment.
     * @param string                     $pwd         Local ICE password.
     * @param list<array{string, string}> $fingerprints Local DTLS fingerprints, as `[algorithm, value]`.
     * @param int                        $audioSsrc   Our outgoing audio SSRC.
     * @param list<array{semantics: string, ssrcs: list<int>}> $sourceGroups Video source groups.
     *
     * @return array The payload, as a `DataJSON` argument: MadelineProto encodes it on the wire.
     */
    public static function buildJoinPayload(
        string $ufrag,
        string $pwd,
        array $fingerprints,
        int $audioSsrc,
        array $sourceGroups = []
    ): array {
        $payload = [
            // The SFU wants the SSRC as a signed 32-bit integer, exactly like groupCallParticipant.source.
            'ssrc' => self::toSignedSsrc($audioSsrc),
            'ufrag' => $ufrag,
            'pwd' => $pwd,
            'fingerprints' => array_map(
                static fn (array $f): array => [
                    'hash' => $f[0],
                    'fingerprint' => $f[1],
                    // tgcalls always acts as the DTLS server for group calls.
                    'setup' => 'passive',
                ],
                $fingerprints
            ),
        ];
        // The SFU only forwards video whose sources were declared when joining.
        $payload['ssrc-groups'] = array_map(
            static fn (array $group): array => [
                'semantics' => $group['semantics'],
                'sources' => array_map(self::toSignedSsrc(...), $group['ssrcs']),
            ],
            $sourceGroups
        );
        return $payload;
    }

    /**
     * Parse the `params` returned in
     * [updateGroupCallConnection](https://core.telegram.org/constructor/updateGroupCallConnection).
     *
     * @param array $params The `DataJSON` payload, already decoded by MadelineProto.
     *
     * @return array{stream: bool, rtmp: bool, transport: ?array, video: ?array{payloadTypes: list<array{id: int, name: string, clockrate: int, channels: int, parameters: array<string, string>, feedback: list<string>}>, extensions: list<array{id: int, uri: string}>}}
     */
    public static function parseJoinResponse(array $params): array
    {
        return [
            'stream' => (bool) ($params['stream'] ?? false),
            'rtmp' => (bool) ($params['rtmp'] ?? false),
            'transport' => \is_array($params['transport'] ?? null) ? $params['transport'] : null,
            'video' => \is_array($params['video'] ?? null) ? self::parseVideoInformation($params['video']) : null,
        ];
    }

    /**
     * Parse the `video` object of the join response, which is how the SFU announces the codecs and
     * header extensions it will forward.
     *
     * tgcalls parses the same object in `GroupJoinPayloadInternal.cpp`. Honouring it instead of our
     * own table is what keeps us working if Telegram ever adds a codec: whatever the SFU lists is
     * offered back to it, and the local stack simply ignores the entries it cannot depacketize.
     *
     * @return array{payloadTypes: list<array{id: int, name: string, clockrate: int, channels: int, parameters: array<string, string>, feedback: list<string>}>, extensions: list<array{id: int, uri: string}>}
     */
    private static function parseVideoInformation(array $video): array
    {
        $payloadTypes = [];
        foreach ($video['payload-types'] ?? [] as $payloadType) {
            if (!\is_array($payloadType) || !isset($payloadType['id'], $payloadType['name'])) {
                continue;
            }
            $parameters = [];
            foreach ($payloadType['parameters'] ?? [] as $key => $value) {
                $parameters[(string) $key] = (string) $value;
            }
            $feedback = [];
            foreach ($payloadType['rtcp-fbs'] ?? [] as $fb) {
                if (!\is_array($fb) || !isset($fb['type'])) {
                    continue;
                }
                // The subtype may either be its own key or be glued to the type, as in "nack pli".
                $feedback[] = isset($fb['subtype'])
                    ? $fb['type'].' '.$fb['subtype']
                    : (string) $fb['type'];
            }
            $payloadTypes[] = [
                'id' => (int) $payloadType['id'],
                'name' => (string) $payloadType['name'],
                'clockrate' => (int) ($payloadType['clockrate'] ?? 90000),
                'channels' => (int) ($payloadType['channels'] ?? 0),
                'parameters' => $parameters,
                'feedback' => $feedback,
            ];
        }

        $extensions = [];
        foreach ($video['rtp-hdrexts'] ?? [] as $extension) {
            if (!\is_array($extension) || !isset($extension['id'], $extension['uri'])) {
                continue;
            }
            $extensions[] = ['id' => (int) $extension['id'], 'uri' => (string) $extension['uri']];
        }

        return ['payloadTypes' => $payloadTypes, 'extensions' => $extensions];
    }

    /**
     * Synthesize the SDP answer of the Telegram SFU.
     *
     * @param string             $offer     The SDP offer just generated by the local peer connection.
     * @param array              $transport The `transport` object of the join response.
     * @param array<string, int> $sources   Map of `mid` => remote audio SSRC, for the receive-only m-lines.
     *                                      Any m-line missing from this map is treated as our own outgoing one.
     * @param ?array{payloadTypes: list<array{id: int, name: string, clockrate: int, channels: int, parameters: array<string, string>, feedback: list<string>}>, extensions: list<array{id: int, uri: string}>} $video
     *                                      The video codec table the SFU announced, if any.
     * @param string $outgoingVideoCodec    Encoding name of the video we transmit, which our own
     *                                      m-line is pinned to. See {@see \danog\MadelineProto\Loop\VoIP\DjLoop::VIDEO_CODECS}.
     */
    public static function buildAnswer(
        string $offer,
        array $transport,
        array $sources,
        ?array $video = null,
        string $outgoingVideoCodec = 'VP8'
    ): string {
        $ufrag = (string) ($transport['ufrag'] ?? '');
        $pwd = (string) ($transport['pwd'] ?? '');
        if ($ufrag === '' || $pwd === '') {
            throw new Exception('Missing ICE credentials in group call join response!');
        }
        $fingerprints = [];
        foreach ($transport['fingerprints'] ?? [] as $fingerprint) {
            $fingerprints[] = 'a=fingerprint:'.$fingerprint['hash'].' '.$fingerprint['fingerprint'];
        }
        if ($fingerprints === []) {
            throw new Exception('Missing DTLS fingerprints in group call join response!');
        }
        $candidates = [];
        foreach ($transport['candidates'] ?? [] as $candidate) {
            $candidates[] = self::formatCandidate($candidate);
        }

        // The SFU announces its DTLS role in its own fingerprints; ours is the opposite one, and
        // was already declared in the join payload.
        $setup = (string) ($transport['fingerprints'][0]['setup'] ?? 'active');

        /** @var list<string> $result */
        $result = [];
        foreach (self::splitOffer($offer) as $index => $section) {
            if ($index === 'session') {
                $result = array_merge($result, $section);
                continue;
            }
            [$kind, $mid, $direction] = $section;
            if ($kind === 'application') {
                // The SCTP data channel that carries colibri video-quality control. It is bundled on
                // the same transport as the media, so it reuses the SFU's ICE/DTLS parameters.
                $result[] = 'm=application 9 UDP/DTLS/SCTP webrtc-datachannel';
                $result[] = 'c=IN IP4 0.0.0.0';
                $result[] = 'a=mid:'.$mid;
                $result[] = 'a=sctp-port:5000';
                $result[] = 'a=ice-ufrag:'.$ufrag;
                $result[] = 'a=ice-pwd:'.$pwd;
                $result = array_merge($result, $fingerprints);
                $result[] = 'a=setup:'.$setup;
                $result = array_merge($result, $candidates);
                $result[] = 'a=end-of-candidates';
                continue;
            }
            // The SFU sends the m-lines we mapped to a participant, and receives our own ones.
            $ssrc = $sources[$mid] ?? null;
            $codecs = self::codecs($kind, $video);
            if ($ssrc === null && $kind === 'video') {
                // Receiving m-lines advertise everything a group call can carry, but the local
                // stack picks the first codec of an m-line to send with, and our outgoing video is
                // demuxed rather than encoded: it can only ever be what {@see \danog\MadelineProto\Loop\VoIP\DjLoop} yields.
                $codecs = self::prioritize($codecs, $outgoingVideoCodec);
            }
            $result[] = 'm='.$kind.' 9 UDP/TLS/RTP/SAVPF '
                .implode(' ', array_map(static fn (array $c): int => $c['id'], $codecs));
            $result[] = 'c=IN IP4 0.0.0.0';
            $result[] = 'a=mid:'.$mid;
            $result[] = 'a='.self::reverseDirection($direction)->name;
            $result[] = 'a=rtcp:9 IN IP4 0.0.0.0';
            $result[] = 'a=rtcp-mux';
            $result = array_merge($result, self::headerExtensions($kind, $video));
            foreach ($codecs as $codec) {
                $result = array_merge($result, self::codecLines($codec));
            }
            $result[] = 'a=ice-ufrag:'.$ufrag;
            $result[] = 'a=ice-pwd:'.$pwd;
            $result = array_merge($result, $fingerprints);
            $result[] = 'a=setup:'.$setup;
            $result = array_merge($result, $candidates);
            $result[] = 'a=end-of-candidates';
            if ($ssrc !== null) {
                $result[] = 'a=ssrc:'.$ssrc.' cname:tgcalls'.$ssrc;
                $result[] = 'a=ssrc:'.$ssrc.' label:'.$kind.$ssrc;
            }
        }

        return implode("\r\n", $result)."\r\n";
    }

    /**
     * Split an offer into its session-level lines and one descriptor per m-line.
     *
     * @return array{session: list<string>}&array<int, array{string, string, SDPDirections}>
     *
     * @psalm-pure
     */
    private static function splitOffer(string $offer): array
    {
        $sections = ['session' => []];
        $index = -1;
        foreach (explode("\n", str_replace("\r\n", "\n", $offer)) as $line) {
            $line = rtrim($line, "\r");
            if ($line === '') {
                continue;
            }
            if (str_starts_with($line, 'm=')) {
                $index++;
                // m=<kind> <port> <proto> <payload types...>
                $sections[$index] = [explode(' ', substr($line, 2))[0], (string) $index, SDPDirections::sendrecv];
                continue;
            }
            if ($index === -1) {
                if (str_starts_with($line, 'a=ice-lite')) {
                    continue;
                }
                $sections['session'][] = $line;
                if (str_starts_with($line, 'a=msid-semantic')) {
                    // The SFU is ICE-lite: it never sends connectivity checks itself.
                    $sections['session'][] = 'a=ice-lite';
                }
                continue;
            }
            if (str_starts_with($line, 'a=mid:')) {
                $sections[$index][1] = substr($line, 6);
            } else {
                $direction = match ($line) {
                    'a=sendonly' => SDPDirections::sendonly,
                    'a=recvonly' => SDPDirections::recvonly,
                    'a=sendrecv' => SDPDirections::sendrecv,
                    'a=inactive' => SDPDirections::inactive,
                    default => null,
                };
                if ($direction !== null) {
                    $sections[$index][2] = $direction;
                }
            }
            // Everything else (codecs, extensions, candidates, ssrcs, ICE and DTLS parameters) is
            // regenerated from the join response and from the fixed tgcalls parameters.
        }
        return $sections;
    }

    /**
     * Swap the point of view of a direction, to turn our offer into the SFU's answer.
     *
     * @psalm-pure
     */
    private static function reverseDirection(SDPDirections $direction): SDPDirections
    {
        return match ($direction) {
            SDPDirections::sendonly => SDPDirections::recvonly,
            SDPDirections::recvonly => SDPDirections::sendonly,
            default => $direction,
        };
    }

    /**
     * The codecs the SFU forwards for a media kind, in payload type order.
     *
     * The SFU decides *which* codecs a call carries and *which payload types* they get, so its
     * announcement wins on both counts and a codec Telegram enables later is picked up without a
     * change here. Everything else in that announcement is ignored, because it does not describe
     * real RTP: the production SFU reports `clockrate: 9000` for every video codec instead of
     * 90000, sets `channels` on video, and hides its bitrate hint behind a nested `fmtp` key. An
     * answer built from those values matches no codec the local stack has, which is presumably why
     * tgcalls only ever reads this list to check whether H.264 is enabled. So the clock rate and
     * the format parameters come from our own table instead, matched by codec name.
     *
     * @param ?array{payloadTypes: list<array{id: int, name: string, clockrate: int, channels: int, parameters: array<string, string>, feedback: list<string>}>, extensions: list<array{id: int, uri: string}>} $video
     *
     * @return list<array{id: int, name: string, clockrate: int, channels: int, parameters: array<string, string>, feedback: list<string>}>
     *
     * @psalm-pure
     */
    private static function codecs(string $kind, ?array $video): array
    {
        $canonical = self::CODECS[$kind] ?? self::CODECS['video'];
        if ($kind !== 'video' || ($video['payloadTypes'] ?? []) === []) {
            return $canonical;
        }

        /** @var array<string, array{id: int, name: string, clockrate: int, channels: int, parameters: array<string, string>, feedback: list<string>}> $known */
        $known = [];
        foreach ($canonical as $codec) {
            $known[strtolower($codec['name'])] = $codec;
        }

        $result = [];
        foreach ($video['payloadTypes'] as $announced) {
            $name = strtolower($announced['name']);
            $template = $known[$name] ?? null;
            $result[] = [
                'id' => $announced['id'],
                'name' => $template['name'] ?? $announced['name'],
                // Every RTP video codec is clocked at 90kHz, retransmissions included.
                'clockrate' => 90000,
                'channels' => 0,
                // A retransmission stream is defined by the codec it repeats, which only the SFU
                // knows; every other codec takes the parameters we know are negotiable.
                'parameters' => $name === 'rtx'
                    ? array_intersect_key($announced['parameters'], ['apt' => true])
                    : ($template['parameters'] ?? []),
                'feedback' => $announced['feedback'] !== [] ? $announced['feedback'] : ($template['feedback'] ?? []),
            ];
        }

        return $result;
    }

    /**
     * Move a codec, together with its retransmission stream, to the front of a payload type list.
     *
     * Payload type numbers are left alone: only the order changes, since that is what decides which
     * codec the local stack transmits with.
     *
     * @param list<array{id: int, name: string, clockrate: int, channels: int, parameters: array<string, string>, feedback: list<string>}> $codecs
     *
     * @return list<array{id: int, name: string, clockrate: int, channels: int, parameters: array<string, string>, feedback: list<string>}>
     *
     * @psalm-pure
     */
    private static function prioritize(array $codecs, string $name): array
    {
        $ids = [];
        foreach ($codecs as $codec) {
            if (strcasecmp($codec['name'], $name) === 0) {
                $ids[(string) $codec['id']] = true;
            }
        }
        if ($ids === []) {
            // The SFU did not announce it at all: leave its own preference order alone.
            return $codecs;
        }
        $first = [];
        $rest = [];
        foreach ($codecs as $codec) {
            $apt = $codec['parameters']['apt'] ?? null;
            if (isset($ids[(string) $codec['id']]) || ($apt !== null && isset($ids[(string) $apt]))) {
                $first[] = $codec;
            } else {
                $rest[] = $codec;
            }
        }
        return array_merge($first, $rest);
    }

    /**
     * The `a=rtpmap`/`a=rtcp-fb`/`a=fmtp` lines describing a single payload type.
     *
     * @param array{id: int, name: string, clockrate: int, channels: int, parameters: array<string, string>, feedback: list<string>} $codec
     *
     * @return list<string>
     *
     * @psalm-pure
     */
    private static function codecLines(array $codec): array
    {
        $rtpmap = 'a=rtpmap:'.$codec['id'].' '.$codec['name'].'/'.$codec['clockrate'];
        if ($codec['channels'] > 0) {
            // Video payload types carry no channel count, which tgcalls signals as zero.
            $rtpmap .= '/'.$codec['channels'];
        }
        $lines = [$rtpmap];
        foreach ($codec['feedback'] as $feedback) {
            $lines[] = 'a=rtcp-fb:'.$codec['id'].' '.$feedback;
        }
        if ($codec['parameters'] !== []) {
            $parameters = [];
            foreach ($codec['parameters'] as $key => $value) {
                $parameters[] = $key.'='.$value;
            }
            $lines[] = 'a=fmtp:'.$codec['id'].' '.implode(';', $parameters);
        }
        return $lines;
    }

    /**
     * The `a=extmap` lines of a media kind.
     *
     * As with the codecs, the extension map the SFU announced wins over our built-in one.
     *
     * @param ?array{payloadTypes: list<array{id: int, name: string, clockrate: int, channels: int, parameters: array<string, string>, feedback: list<string>}>, extensions: list<array{id: int, uri: string}>} $video
     *
     * @return list<string>
     */
    private static function headerExtensions(string $kind, ?array $video): array
    {
        $extensions = $kind === 'video' && ($video['extensions'] ?? []) !== []
            ? $video['extensions']
            : (self::EXTENSIONS[$kind] ?? self::EXTENSIONS['video']);
        return array_map(
            static fn (array $e): string => 'a=extmap:'.$e['id'].' '.$e['uri'],
            $extensions
        );
    }

    /**
     * Convert an unsigned 32-bit SSRC to the signed representation used by the API.
     *
     * @psalm-pure
     */
    public static function toSignedSsrc(int $ssrc): int
    {
        $ssrc &= 0xFFFFFFFF;
        return $ssrc >= 0x80000000 ? $ssrc - 0x100000000 : $ssrc;
    }

    /**
     * Convert a signed SSRC coming from the API to its unsigned 32-bit representation.
     *
     * @psalm-pure
     */
    public static function toUnsignedSsrc(int $ssrc): int
    {
        return $ssrc & 0xFFFFFFFF;
    }

    /**
     * @psalm-pure
     */
    private static function formatCandidate(array $candidate): string
    {
        $line = 'a=candidate:'
            .$candidate['foundation'].' '
            .$candidate['component'].' '
            .$candidate['protocol'].' '
            .$candidate['priority'].' '
            .$candidate['ip'].' '
            .$candidate['port'].' '
            .'typ '.$candidate['type'];
        if (isset($candidate['rel-addr'], $candidate['rel-port'])) {
            $line .= ' raddr '.$candidate['rel-addr'].' rport '.$candidate['rel-port'];
        }
        if (isset($candidate['tcptype'])) {
            $line .= ' tcptype '.$candidate['tcptype'];
        }
        if (isset($candidate['generation'])) {
            $line .= ' generation '.$candidate['generation'];
        }
        return $line;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

// IMPORTANT NOTE: Please keep the above copyright notice intact if copying or rewriting this file in another language.

namespace danog\MadelineProto\Tgcalls;

use Webrtc\DataChannel\RTCSctpTransportInterface;
use Webrtc\ICE\Enum\IceRole;
use Webrtc\ICE\RTCIceTransportInterface;
use Webrtc\SCTP\RTCSctpDtlsTransportInterface;
use Webrtc\SCTP\RTCSctpTransport;
use Webrtc\SCTP\SignalingSinkInterface;
use Webrtc\Stats\enum\TLSState;

/**
 * Carries the tgcalls signaling channel over an SCTP association.
 *
 * The newest protocol versions (11.0.0 and up) do not send signaling messages as bare datagrams:
 * they run a whole SCTP association whose packets travel inside
 * [phone.sendSignalingData](https://core.telegram.org/method/phone.sendSignalingData), and send
 * the (already encrypted) messages as ordered DATA chunks on stream 0. That gives the signaling
 * channel retransmission and ordering without the ad-hoc framing older versions use.
 *
 * The vendored SCTP stack normally sits on top of DTLS, and only needs four things from whatever
 * is underneath it, so this pretends to be that transport and points it at the signaling channel
 * instead.
 *
 * @internal
 */
final class SignalingSctpTransport implements RTCSctpDtlsTransportInterface, SignalingSinkInterface
{
    /** Both ends of the signaling association use this port, as tgcalls does. */
    public const PORT = 5000;
    /** The single stream every signaling message is sent on. */
    private const STREAM_ID = 0;
    /** WebRTC binary payload protocol identifier. */
    private const PPID_BINARY = 53;

    private ?RTCSctpTransportInterface $sctpReceiver = null;
    private RTCSctpTransport $sctp;

    /** Messages queued until the association is established. @var list<string> */
    private array $pending = [];
    private bool $closed = false;

    /**
     * @param bool                  $outgoing Whether we are the caller, i.e. the side that
     *                                        initiates the association.
     * @param SctpSignalingObserver $owner    Reassembled signaling messages are delivered to
     *                             {@see SctpSignalingObserver::onSignalingMessageData()} and outgoing
     *                             SCTP packets to {@see SctpSignalingObserver::deliverSignalingPacket()}.
     *                             A serializable object rather than `Closure`s, so the association
     *                             survives the serialize/unserialize cycle intact.
     */
    public function __construct(
        private readonly bool $outgoing,
        private readonly SctpSignalingObserver $owner,
    ) {
        $this->sctp = new RTCSctpTransport($this, self::PORT);
        // The signaling sink is part of the transport's serializable state, so it must be a real
        // invokable object; this transport is that object (see self::__invoke()).
        $this->sctp->setSignalingSink($this);
        // The caller drives the association, exactly like tgcalls' SignalingSctpConnection.
        $this->sctp->start(self::PORT);
    }

    /**
     * Deliver one reassembled signaling message to the controller, as {@see SignalingSinkInterface}.
     */
    #[\Override]
    public function __invoke(string $data): void
    {
        if (!$this->closed) {
            $this->owner->onSignalingMessageData($data);
        }
    }

    /**
     * Hand one SCTP packet, received over the signaling channel, to the association.
     */
    public function receive(string $packet): void
    {
        if ($this->closed) {
            return;
        }
        $this->sctpReceiver?->onReceived($packet);
        $this->flush();
    }

    /**
     * Send one signaling message as an ordered DATA chunk.
     */
    public function send(string $message): void
    {
        if ($this->closed) {
            return;
        }
        $this->pending[] = $message;
        $this->flush();
    }

    /**
     * Try to hand everything queued to the association.
     */
    private function flush(): void
    {
        if ($this->pending === [] || $this->closed) {
            return;
        }
        if (!$this->sctp->isEstablished()) {
            return;
        }
        $queued = $this->pending;
        $this->pending = [];
        foreach ($queued as $message) {
            $this->sctp->sendSignaling(self::STREAM_ID, self::PPID_BINARY, $message);
        }
    }

    /**
     * Whether the association is up and messages are flowing.
     */
    public function isEstablished(): bool
    {
        return !$this->closed && $this->sctp->isEstablished();
    }

    public function close(): void
    {
        if ($this->closed) {
            return;
        }
        $this->closed = true;
        $this->pending = [];
        $this->sctp->stop();
    }

    // ----------------------------------------------- RTCSctpDtlsTransportInterface

    /**
     * The signaling channel is always usable: the API call underneath it is reliable by itself.
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function getState(): TLSState
    {
        return $this->closed ? TLSState::CLOSED : TLSState::CONNECTED;
    }

    /**
     * SCTP decides which side is the client from the ICE role, so report the call direction.
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function getIceTransport(): RTCIceTransportInterface
    {
        return new SignalingIceRole($this->outgoing ? IceRole::Controlling : IceRole::Controlled);
    }

    /**
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function setSctpReceiver(?RTCSctpTransportInterface $sctpReceiver = null): void
    {
        $this->sctpReceiver = $sctpReceiver;
    }

    /**
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function removeSctpReceiver(RTCSctpTransport $param): void
    {
        $this->sctpReceiver = null;
    }

    /**
     * Put one SCTP packet on the signaling channel.
     */
    #[\Override]
    public function sendData(string $data): void
    {
        if (!$this->closed) {
            $this->owner->deliverSignalingPacket($data);
        }
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Tgcalls;

use danog\MadelineProto\CallStream;
use danog\MadelineProto\EventHandler\Call;
use danog\MadelineProto\EventHandler\Calls\CallStreams;
use danog\MadelineProto\LocalFile;
use danog\MadelineProto\Logger;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\RecordingEvent;
use Revolt\EventLoop;

/**
 * Emits {@see CallStreams} updates from a call's controller, when a participant's streams or codecs
 * change or a recording of them ends. Recording-started events are not surfaced as updates.
 *
 * @internal
 *
 * @psalm-import-type StreamMask from CallStream
 */
final class CallStreamsDispatcher
{
    /**
     * @param StreamMask         $streams
     * @param array<int, string> $codecs   By {@see CallStream} flag.
     * @param LocalFile|null     $file      The local file the recording is written to, if any, for logging only.
     */
    public static function dispatch(MTProto $API, Call $call, int $participant, int $streams, array $codecs, ?RecordingEvent $recording, ?LocalFile $file): void
    {
        if ($recording === RecordingEvent::Started) {
            // Recording-started events are not surfaced: only stream/codec changes and recording ends.
            return;
        }
        $recordingStopped = $recording === RecordingEvent::Ended;
        $API->logger->logger("Streams of participant $participant of $call: ".CallStream::describe($streams).' '.json_encode($codecs).($recordingStopped ? ', recording stopped'.($file !== null ? " {$file->file}" : '') : ''), Logger::VERBOSE);
        EventLoop::queue($API->saveUpdate(...), [
            '_' => 'updateCallStreams',
            'call' => $call,
            'participant' => $participant,
            'streams' => $streams,
            'codecs' => $codecs,
            'recordingStopped' => $recordingStopped,
        ]);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Tgcalls;

use Amp\ByteStream\ReadableStream;
use Amp\ByteStream\WritableStream;
use Amp\DeferredFuture;
use danog\MadelineProto\CallStream;
use danog\MadelineProto\EventHandler\Call;
use danog\MadelineProto\EventHandler\Calls\AbstractGroupCallParticipant;
use danog\MadelineProto\EventHandler\Calls\CallStreams;
use danog\MadelineProto\EventHandler\Calls\GroupCallState;
use danog\MadelineProto\LocalDirectory;
use danog\MadelineProto\LocalFile;
use danog\MadelineProto\Logger;
use danog\MadelineProto\Loop\VoIP\DjLoop;
use danog\MadelineProto\MediaDestination;
use danog\MadelineProto\RecordingEvent;
use danog\MadelineProto\RecordingFormat;
use danog\MadelineProto\RemoteUrl;
use danog\MadelineProto\RPCErrorException;
use Throwable;
use Webmozart\Assert\Assert;

/**
 * Everything a multi-party call controller does with media, shared by the ordinary group call
 * ({@see \danog\MadelineProto\GroupCall\GroupCallController}) and the end-to-end encrypted conference
 * ({@see E2E\ConferenceCall}): the playlists (a main disk jockey for the camera and microphone, a
 * video-only one for the screen share), the separate screen-share connection, our own mute and video
 * state, and the recording of the other participants â€” one {@see GroupConnection} per (re-)join,
 * whose per-participant {@see IncomingMedia} routers (and the recordings in progress) are handed over
 * to the next connection so a re-join does not finish them.
 *
 * The using class provides what differs between the two: how it tells whether it is joined and which
 * `inputGroupCall` it drives, how it prepares a fresh connection, and how it maps participants to
 * their media sources (see the abstract methods).
 *
 * @psalm-import-type StreamMask from \danog\MadelineProto\CallStream
 *
 * @internal
 */
trait GroupMediaTrait
{
    /** The main WebRTC connection (audio and camera), while joined. */
    private ?GroupConnection $connection = null;
    /** The playlist engine feeding our audio and camera video. */
    private DjLoop $diskJockey;
    /** The separate screen-share connection (phone.joinGroupCallPresentation), while sharing a screen. */
    private ?GroupConnection $presentationConnection = null;
    /** The video-only playlist engine feeding the screen share. */
    private ?DjLoop $presentationDj = null;
    private bool $muted = false;

    /**
     * Recordings requested for specific participants that are not wired to the WebRTC connection
     * yet (the participant's source is not known, or we are not joined): a single file
     * ({@see self::recordParticipant()}, wired once, then dropped) or a segment series
     * ({@see self::recordFolder()} with a participant, kept so it is re-wired after a re-join).
     *
     * @var array<int, array{file: LocalFile|WritableStream, format: RecordingFormat, streams: ?StreamMask, series: bool}>
     */
    private array $explicitOutputs = [];
    /**
     * The incoming media (and recordings in progress) of every participant, taken from a connection
     * closed for a re-join and handed to the next one, so a re-join does not finish the recordings.
     *
     * @var array<int, IncomingMedia> By unsigned audio SSRC.
     */
    private array $inheritedMedia = [];
    /** Directory into which every transmitting participant is recorded, or null if not in folder mode. */
    private ?string $outputDir = null;
    private RecordingFormat $outputFormat = RecordingFormat::Mkv;

    /* ------------------------------------------------------------------ *
     *  What the using class provides.
     * ------------------------------------------------------------------ */

    /**
     * The state of the call: media may only be transmitted, and screens shared, while {@see GroupCallState::JOINED}.
     */
    abstract public function getCallState(): GroupCallState;

    /**
     * The [inputGroupCall](https://core.telegram.org/constructor/inputGroupCall) of this call.
     */
    abstract public function getInputCall(): array;

    /**
     * The handle of this call handed to library users, carried by every update about it.
     */
    abstract private function callObject(): Call;

    /**
     * Prepare a freshly created connection before it is used: the main one (`$screencast` false) or
     * the separate screen-share one.
     */
    abstract private function configureConnection(GroupConnection $connection, bool $screencast): void;

    /**
     * The server no longer has our participant (it dropped us while the process was down): recover.
     */
    abstract private function onDroppedByServer(): void;

    /**
     * A participant of the call by bot API peer id, or null if unknown.
     */
    abstract private function participantOf(int $peerId): ?AbstractGroupCallParticipant;

    /**
     * Whether a participant is ourselves (whose media is never recorded).
     */
    abstract private function isOurself(int $peerId, AbstractGroupCallParticipant $participant): bool;

    /**
     * The bot API peer id of the participant a signed audio SSRC belongs to, or null if unknown.
     */
    abstract private function peerOfSource(int $source): ?int;

    /**
     * The bot API peer ids of every known participant.
     *
     * @return list<int>
     */
    abstract private function knownPeers(): array;

    /* ------------------------------------------------------------------ *
     *  Playback.
     * ------------------------------------------------------------------ */

    /**
     * The disk jockey whose playback the main WebRTC connection transmits (overridden by the calls
     * that transmit it otherwise).
     */
    private function connectionDj(): DjLoop
    {
        return $this->diskJockey;
    }

    /**
     * Prepare to transmit what is played, or throw if it can't be (overridden by the calls that need to).
     */
    private function beforePlay(): void
    {
    }

    /**
     * Throw if the screen can't be shared (overridden by the calls that have such cases).
     */
    private function assertCanPresent(): void
    {
    }

    /**
     * The disk jockey feeding a given destination: the main one for the camera, and the video-only
     * screen-share one for the presentation (joining the separate presentation connection on first
     * use). Screen-share is video-only, so it does not require OGG OPUS audio.
     */
    private function dj(MediaDestination $dest): DjLoop
    {
        if ($dest === MediaDestination::Camera) {
            return $this->diskJockey;
        }
        $this->enablePresentation();
        \assert($this->presentationDj !== null);
        return $this->presentationDj;
    }

    /**
     * The disk jockey feeding a destination, without starting a screen-share that is not running:
     * returns null for the presentation when no screen is being shared. For control/query methods.
     *
     * @psalm-mutation-free
     */
    private function djOrNull(MediaDestination $dest): ?DjLoop
    {
        return $dest === MediaDestination::Camera ? $this->diskJockey : $this->presentationDj;
    }

    /**
     * Block until a stream being played has been consumed (a file returns at once).
     */
    private static function awaitStream(LocalFile|RemoteUrl|ReadableStream $file): void
    {
        if (!$file instanceof ReadableStream) {
            return;
        }
        $deferred = new DeferredFuture;
        $file->onClose($deferred->complete(...));
        $deferred->getFuture()->await();
    }

    /**
     * Play a file, transmitting its audio and, if it carries a transmittable one, its video.
     */
    public function play(LocalFile|RemoteUrl|ReadableStream $file, MediaDestination $dest = MediaDestination::Camera): static
    {
        $this->beforePlay();
        $this->dj($dest)->play($file);
        return $this;
    }

    /**
     * Play a file, blocking until it has finished playing if a stream was passed.
     */
    public function playBlocking(LocalFile|RemoteUrl|ReadableStream $file, MediaDestination $dest = MediaDestination::Camera): static
    {
        $this->beforePlay();
        $this->dj($dest)->play($file);
        self::awaitStream($file);
        return $this;
    }

    /**
     * Set the files to play, on loop, while the given stream's main playlist is empty.
     *
     * A hold file being played is replaced right away by the new ones (or stopped, if none are passed).
     */
    public function playOnHold(MediaDestination $dest = MediaDestination::Camera, LocalFile|RemoteUrl|ReadableStream ...$files): static
    {
        $this->beforePlay();
        $this->dj($dest)->playOnHold(...$files);
        return $this;
    }

    /**
     * Skip to the next file in the playlist.
     */
    public function skip(MediaDestination $dest = MediaDestination::Camera): static
    {
        $this->djOrNull($dest)?->skip();
        return $this;
    }

    /**
     * Stop playing all files, clearing the main and the hold playlist; stopping the presentation
     * stops sharing the screen.
     */
    public function stop(MediaDestination $dest = MediaDestination::Camera): static
    {
        if ($dest === MediaDestination::Presentation) {
            $this->disablePresentation();
            return $this;
        }
        $this->diskJockey->stopPlaying();
        return $this;
    }

    /**
     * Pause playback of the current file.
     *
     * @psalm-external-mutation-free
     */
    public function pause(MediaDestination $dest = MediaDestination::Camera): static
    {
        $this->djOrNull($dest)?->pausePlaying();
        return $this;
    }

    /**
     * Resume playback of the current file.
     *
     * @psalm-external-mutation-free
     */
    public function resume(MediaDestination $dest = MediaDestination::Camera): static
    {
        $this->djOrNull($dest)?->resumePlaying();
        return $this;
    }

    /**
     * The file or stream currently being played, if any.
     *
     * @psalm-mutation-free
     */
    public function getCurrent(MediaDestination $dest = MediaDestination::Camera): LocalFile|RemoteUrl|string|null
    {
        return $this->djOrNull($dest)?->getCurrent();
    }

    /**
     * Whether playback of the current file is paused.
     *
     * @psalm-mutation-free
     */
    public function isPaused(MediaDestination $dest = MediaDestination::Camera): bool
    {
        return $this->djOrNull($dest)?->isAudioPaused() ?? false;
    }

    /* ------------------------------------------------------------------ *
     *  Screen sharing.
     * ------------------------------------------------------------------ */

    /**
     * Start the separate screen-share connection (phone.joinGroupCallPresentation) if it is not up
     * yet. The screen-share is a fully separate WebRTC connection with its own SSRC and transport;
     * see https://core.telegram.org/api/group-calls. Idempotent.
     */
    public function enablePresentation(): static
    {
        if ($this->presentationConnection !== null) {
            return $this;
        }
        $this->assertCanPresent();
        if ($this->getCallState() !== GroupCallState::JOINED) {
            throw new \RuntimeException("Cannot share a screen before joining $this.");
        }
        $this->presentationDj ??= new DjLoop($this, videoOnly: true);
        Assert::true($this->presentationDj->start());
        $connection = new GroupConnection($this, $this->presentationDj, screencast: true);
        $this->configureConnection($connection, true);
        $params = $connection->buildJoinPayload();
        $this->presentationConnection = $connection;
        $this->log("Joining the presentation of $this...", Logger::VERBOSE);
        try {
            /** @var array{updates?: list<array<string, mixed>>} $updates */
            $updates = $this->API->methodCallAsyncRead('phone.joinGroupCallPresentation', [
                'call' => $this->getInputCall(),
                'params' => $params,
            ]);
        } catch (Throwable $e) {
            $this->presentationConnection = null;
            $connection->close();
            throw $e;
        }
        foreach ($updates['updates'] ?? [] as $update) {
            if ($update['_'] === 'updateGroupCallConnection' && ($update['presentation'] ?? false)) {
                $parsed = GroupSdp::parseJoinResponse((array) $update['params']);
                if ($parsed['transport'] !== null) {
                    $connection->setTransport($parsed['transport'], $parsed['video']);
                }
            }
        }
        return $this;
    }

    /**
     * Stop sharing the screen: tear down the presentation connection and leave it server-side.
     */
    public function disablePresentation(): static
    {
        if ($this->presentationConnection === null) {
            return $this;
        }
        $this->dropPresentation();
        if ($this->getCallState() === GroupCallState::JOINED) {
            try {
                $this->API->methodCallAsyncRead('phone.leaveGroupCallPresentation', ['call' => $this->getInputCall()]);
            } catch (Throwable $e) {
                $this->log("Could not leave the presentation of $this: $e", Logger::WARNING);
            }
        }
        return $this;
    }

    /**
     * Tear down the screen-share connection and playlist locally (without telling the server).
     */
    private function dropPresentation(): void
    {
        $this->presentationConnection?->close();
        $this->presentationConnection = null;
        $this->presentationDj?->discard();
        $this->presentationDj = null;
    }

    /**
     * Whether a screen-share is currently being transmitted.
     *
     * @psalm-mutation-free
     */
    public function isSharingScreen(): bool
    {
        return $this->presentationConnection !== null;
    }

    /* ------------------------------------------------------------------ *
     *  Our own media state.
     * ------------------------------------------------------------------ */

    /**
     * Mute or unmute ourselves.
     */
    public function setMuted(bool $muted = true): static
    {
        $this->muted = $muted;
        if ($muted) {
            $this->diskJockey->pausePlaying();
        } else {
            $this->diskJockey->resumePlaying();
        }
        if ($this->getCallState() === GroupCallState::JOINED) {
            try {
                $this->API->methodCallAsyncRead('phone.editGroupCallParticipant', [
                    'call' => $this->getInputCall(),
                    'participant' => ['_' => 'inputPeerSelf'],
                    'muted' => $muted,
                ]);
            } catch (Throwable $e) {
                $this->log("Could not change the mute state of $this: $e", Logger::WARNING);
            }
        }
        return $this;
    }

    /**
     * Whether we are muted.
     *
     * @psalm-mutation-free
     */
    public function isMuted(): bool
    {
        return $this->muted;
    }

    /**
     * Tell the server whether our camera video is stopped: returns whether it took the change.
     *
     * @internal
     */
    #[\Override]
    public function setVideoStopped(bool $stopped): bool
    {
        if ($this->getCallState() !== GroupCallState::JOINED) {
            return false;
        }
        try {
            $this->API->methodCallAsyncRead('phone.editGroupCallParticipant', [
                'call' => $this->getInputCall(),
                'participant' => ['_' => 'inputPeerSelf'],
                'video_stopped' => $stopped,
            ]);
            return true;
        } catch (RPCErrorException $e) {
            if ($e->rpc === 'PARTICIPANT_JOIN_MISSING' || $e->rpc === 'GROUPCALL_JOIN_MISSING') {
                // The server no longer has our participant (it dropped us while the process was
                // down): recover right away rather than waiting for the transport to time out.
                $this->log("We were dropped from $this, rejoining...", Logger::WARNING);
                $this->onDroppedByServer();
                return false;
            }
            $this->log("Could not change the video state of $this: $e", Logger::WARNING);
        } catch (Throwable $e) {
            $this->log("Could not change the video state of $this: $e", Logger::WARNING);
        }
        return false;
    }

    /**
     * Tell the server whether our screen-share is paused: returns whether it took the change.
     *
     * @internal
     */
    #[\Override]
    public function setPresentationPaused(bool $paused): bool
    {
        if ($this->getCallState() !== GroupCallState::JOINED) {
            return false;
        }
        try {
            $this->API->methodCallAsyncRead('phone.editGroupCallParticipant', [
                'call' => $this->getInputCall(),
                'participant' => ['_' => 'inputPeerSelf'],
                'presentation_paused' => $paused,
            ]);
            return true;
        } catch (Throwable $e) {
            $this->log("Could not change the presentation state of $this: $e", Logger::WARNING);
        }
        return false;
    }

    /**
     * Change the title of the call.
     */
    public function setTitle(string $title): static
    {
        $this->API->methodCallAsyncRead('phone.editGroupCallTitle', [
            'call' => $this->getInputCall(),
            'title' => $title,
        ]);
        return $this;
    }

    /* ------------------------------------------------------------------ *
     *  Connections.
     * ------------------------------------------------------------------ */

    /**
     * A fresh WebRTC connection for a (re-)join, inheriting the incoming media â€” and the recordings
     * in progress â€” of the one it replaces, so that a re-join does not finish them. The folder-mode
     * bookkeeping starts over: participants are wired again as their sources are (re-)learned, and
     * recordings carried over are left alone.
     */
    private function replaceConnection(): GroupConnection
    {
        $this->detachConnection();
        $connection = new GroupConnection($this, $this->connectionDj());
        $this->configureConnection($connection, false);
        $connection->adoptIncomingMedia($this->inheritedMedia);
        $this->inheritedMedia = [];
        return $connection;
    }

    /**
     * Close the main connection ahead of a re-join, keeping every participant's incoming media (and
     * the recordings in progress) for the connection of the next join.
     */
    private function detachConnection(): void
    {
        if ($this->connection === null) {
            return;
        }
        $this->inheritedMedia = $this->connection->takeIncomingMedia() + $this->inheritedMedia;
        $this->connection->close();
        $this->connection = null;
    }

    /**
     * Close the main connection for good (leaving, or being discarded), finishing every recording:
     * the ones it carries and the ones kept for a re-join that will not happen.
     */
    private function closeConnection(): void
    {
        $this->connection?->close();
        $this->connection = null;
        foreach ($this->inheritedMedia as $media) {
            $media->close();
        }
        $this->inheritedMedia = [];
    }

    /* ------------------------------------------------------------------ *
     *  Recording.
     * ------------------------------------------------------------------ */

    /**
     * Record one participant's media into a single file (or stream) with a fixed set of tracks.
     *
     * The file's tracks are the streams chosen with `$streams` (or every stream the participant sends
     * right now), and are fixed for its whole duration: a stream the participant turns off stops being
     * written and is written again when it comes back; only a change of codec of a video stream, or the
     * end of the call, finishes the file (see {@see Call::setOutput()}). Our own media is never
     * recorded. `$format` picks the Matroska DocType ({@see RecordingFormat::matroskaFor()}).
     *
     * @param mixed $participant The participant to record (required).
     * @param ?StreamMask  $streams     The {@see CallStream} flags to record, or null for every stream the
     *                           participant currently sends. Every chosen stream must be available.
     *
     * @throws \InvalidArgumentException If a chosen stream is not available, or nothing is.
     *
     * @return StreamMask The streams the participant currently sends, as {@see CallStream} flags.
     */
    private function recordParticipant(LocalFile|WritableStream $file, mixed $participant, ?RecordingFormat $format, ?int $streams): int
    {
        if ($streams !== null) {
            CallStream::validate($streams);
        }
        if ($participant === null) {
            throw new \InvalidArgumentException("$this is recorded one participant per file: pass the participant to record, or record every participant into a folder with setOutputFolder().");
        }
        $peerId = $this->API->getId($participant);
        $format = RecordingFormat::matroskaFor($file, $format);
        $available = $this->availableStreams($peerId);
        CallStream::checkAvailable($streams, $available, "Participant $peerId");
        $this->explicitOutputs[$peerId] = ['file' => $file, 'format' => $format, 'streams' => $streams ?? $available, 'series' => false];
        $this->wireOutput($peerId);
        return $available;
    }

    /**
     * Record the call into a directory, as numbered series of Matroska files.
     *
     * Every *transmitting* participant â€” or only the given `$participant` â€” is recorded as
     * `<dir>/<peerId>.<n>_<streams>.mkv` files, one per combination of the audio, camera video and
     * screen share they send, which they can turn on and off at any time (see
     * {@see Call::setOutputFolder()}). Participants that start transmitting later are picked up too;
     * our own media is never recorded. `$format` picks the Matroska DocType; OGG OPUS is not supported.
     */
    private function recordFolder(LocalDirectory $dir, mixed $participant, ?RecordingFormat $format): void
    {
        $path = $dir->create();
        $format = RecordingFormat::matroskaFor(new LocalFile("$path/"), $format);
        if ($participant !== null) {
            $peerId = $this->API->getId($participant);
            $this->explicitOutputs[$peerId] = ['file' => new LocalFile("$path/$peerId"), 'format' => $format, 'streams' => null, 'series' => true];
            $this->wireOutput($peerId);
            return;
        }
        $this->outputDir = $path;
        $this->outputFormat = $format;
        foreach ($this->knownPeers() as $peerId) {
            $this->wireFolderOutput($peerId);
        }
    }

    /**
     * A participant's recordable media source: their signed audio SSRC, or 0 if they are unknown,
     * not transmitting, or ourselves.
     *
     * @psalm-mutation-free
     */
    private function recordableSource(int $peerId): int
    {
        $participant = $this->participantOf($peerId);
        if ($participant === null || $participant->source === 0 || $this->isOurself($peerId, $participant)) {
            return 0;
        }
        return $participant->source;
    }

    /**
     * The streams a participant currently sends, as {@see CallStream} flags (0 if they are not
     * transmitting, or unknown).
     *
     * @psalm-mutation-free
     *
     * @return StreamMask A bitmask of {@see CallStream} flags.
     */
    private function availableStreams(int $peerId): int
    {
        $participant = $this->participantOf($peerId);
        if ($participant === null || $this->recordableSource($peerId) === 0) {
            return 0;
        }
        return CallStream::of(!$participant->muted, $participant->videoSources !== [], $participant->presentationSources !== []);
    }

    /**
     * Wire a requested recording of a participant onto the connection, if their source is known and
     * we are joined; a single-file recording is then forgotten (it is not restarted once it ends), a
     * series is kept to be re-wired after a re-join.
     */
    private function wireOutput(int $peerId): void
    {
        $output = $this->explicitOutputs[$peerId] ?? null;
        $source = $this->recordableSource($peerId);
        if ($output === null || $source === 0 || $this->connection === null) {
            return;
        }
        if ($output['series']) {
            if ($this->connection->isRecording($source)) {
                return; // Carried over from the previous connection.
            }
            \assert($output['file'] instanceof LocalFile);
            $this->connection->setOutputSeries($source, $output['file'], $output['format']);
            return;
        }
        unset($this->explicitOutputs[$peerId]);
        $this->connection->setOutput($source, $output['file'], $output['format'], $output['streams']);
    }

    /**
     * In folder mode, give a transmitting (non-self) participant its own `<dir>/<peerId>.<n>_<streams>.mkv`
     * file series, unless they are already being recorded: a participant that left (which finished its
     * recording) and joined again continues the series. Called whenever a participant's source becomes known.
     */
    private function wireFolderOutput(int $peerId): void
    {
        if ($this->outputDir === null || $this->connection === null
            || isset($this->explicitOutputs[$peerId]) // an explicit per-participant output takes precedence
        ) {
            return;
        }
        $source = $this->recordableSource($peerId);
        if ($source === 0) {
            return;
        }
        if ($this->connection->isRecording($source)) {
            return; // Carried over from the previous connection, or an explicit recording.
        }
        $this->connection->setOutputSeries($source, new LocalFile($this->outputDir.'/'.$peerId), $this->outputFormat);
    }

    /**
     * Wire every requested recording onto the current WebRTC connection: the explicit per-participant
     * outputs and, in folder mode, one file series per transmitting participant. Used after every
     * (re-)join, since each join creates a new connection (which inherits the recordings in progress
     * of the one it replaces, see {@see self::replaceConnection()}).
     */
    private function rewireOutputs(): void
    {
        foreach (array_keys($this->explicitOutputs) as $peerId) {
            $this->wireOutput($peerId);
        }
        foreach ($this->knownPeers() as $peerId) {
            $this->wireFolderOutput($peerId);
        }
    }

    /**
     * A participant's streams or codecs changed, or a recording of them started or ended: emit a
     * {@see CallStreams} update. Source 0 is the mixed stream of a stream-mode call, reported as
     * participant 0.
     *
     * @internal
     *
     * @param array<int, string> $codecs
     */
    #[\Override]
    public function onParticipantStreams(int $source, int $streams, array $codecs, ?RecordingEvent $recording, ?LocalFile $file): void
    {
        $peerId = $source === 0 ? 0 : $this->peerOfSource($source);
        if ($peerId === null) {
            $this->log("Streams of unknown source $source of $this changed, ignoring", Logger::VERBOSE);
            return;
        }
        CallStreamsDispatcher::dispatch($this->API, $this->callObject(), $peerId, $streams, $codecs, $recording, $file);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

// IMPORTANT NOTE: Please keep the above copyright notice intact if copying or rewriting this file in another language.

namespace danog\MadelineProto\Tgcalls;

use danog\MadelineProto\Exception;

/**
 * Translation between tgcalls' `InstanceV2Impl` signaling and SDP.
 *
 * Unlike `InstanceV2ReferenceImpl` (which simply ships an SDP blob back and forth), `InstanceV2Impl`
 * negotiates media with two structured messages: `InitialSetup` carries the ICE credentials and the
 * DTLS fingerprint, and `NegotiateChannels` carries one `MediaContent` per stream, describing its
 * SSRC, payload types and header extensions.
 *
 * tgcalls' model (see its `ContentNegotiationContext`) is one-directional per channel: every party
 * offers only its *outgoing* channels, each identified by its SSRC and carrying that party's own
 * payload type numbers, and the other party answers by echoing them. A call therefore has separate
 * send and receive channels, never a shared bidirectional one. MadelineProto drives a standard peer
 * connection, so it mirrors this with one send-only m-line per outgoing channel and one receive-only
 * m-line per channel of the peer: this class reads our offers out of the local SDP and synthesizes
 * the remote description that expresses the peer's state, exactly like {@see GroupSdp} does for the
 * group call SFU.
 *
 * @internal
 */
final class V2Sdp
{
    /** The `sdes:mid` header extension URI tgcalls uses to route bundled channels. */
    private const MID_EXTENSION_URI = 'urn:ietf:params:rtp-hdrext:sdes:mid';

    /**
     * Rewrite each media section's `a=mid` to the SSRC it carries, or to an explicitly given value.
     *
     * tgcalls' [InstanceV2Impl](https://github.com/TelegramMessenger/tgcalls) identifies every
     * channel by `contentIdBySsrc()`, i.e. the decimal SSRC string, and uses that as the m-line
     * `mid` both in the SDP it builds internally and â€” crucially â€” as the value it expects in the
     * `sdes:mid` RTP header extension of incoming packets. Aligning our mids with the SSRCs makes
     * the MID our senders write match what the peer routes by.
     *
     * Sections without an SSRC (a rejected m-line, or the data channel) keep their original mid.
     *
     * @param array<int, string> $midByIndex Explicit mids for given m-line indexes, taking precedence
     *                                       over the SSRC rule: used for the receive-only m-lines,
     *                                       whose mid is the *peer's* SSRC (their only SSRC line is
     *                                       the unused local sender's).
     */
    public static function useSsrcAsMid(string $sdp, array $midByIndex = []): string
    {
        // First pass: map each section's current mid to the mid it should have.
        $newMidByMid = [];
        $currentMid = null;
        $index = -1;
        foreach (self::lines($sdp) as $line) {
            if (str_starts_with($line, 'm=')) {
                $currentMid = null;
                $index++;
            } elseif (str_starts_with($line, 'a=mid:')) {
                $currentMid = substr($line, 6);
                if (isset($midByIndex[$index])) {
                    $newMidByMid[$currentMid] = $midByIndex[$index];
                }
            } elseif ($currentMid !== null
                && !isset($newMidByMid[$currentMid])
                && str_starts_with($line, 'a=ssrc:')
            ) {
                $ssrc = explode(' ', substr($line, 7))[0];
                if ($ssrc !== '') {
                    $newMidByMid[$currentMid] = $ssrc;
                }
            }
        }
        if ($newMidByMid === []) {
            return $sdp;
        }

        // Second pass: rewrite the a=mid lines and the BUNDLE group that lists them.
        $out = [];
        foreach (self::lines($sdp) as $line) {
            if (str_starts_with($line, 'a=mid:')) {
                $mid = substr($line, 6);
                $out[] = 'a=mid:'.($newMidByMid[$mid] ?? $mid);
            } elseif (str_starts_with($line, 'a=group:BUNDLE')) {
                // NB: a plain array_filter() would drop the mid "0" (a falsy string) and unbundle
                // the first m-line, so the filter must only remove empty strings.
                $mids = array_map(
                    static fn (string $mid): string => $newMidByMid[$mid] ?? $mid,
                    array_filter(explode(' ', substr($line, \strlen('a=group:BUNDLE '))), static fn (string $mid): bool => $mid !== '')
                );
                $out[] = 'a=group:BUNDLE '.implode(' ', $mids);
            } else {
                $out[] = $line;
            }
        }

        return implode("\r\n", $out)."\r\n";
    }

    /**
     * Describe our local media in the form `NegotiateChannels` expects.
     *
     * @param string $offer        The SDP offer generated by the local peer connection.
     * @param bool   $outgoingOnly Omit rejected, inactive and receive-only m-lines.
     * @return list<array<string, mixed>> One `MediaContent` per selected m-line.
     */
    public static function contentsFromOffer(string $offer, bool $outgoingOnly = false): array
    {
        $result = [];
        foreach (self::sections($offer) as $section) {
            $include = !$outgoingOnly || self::isSending($section);
            $content = self::contentOfSection($section);
            if ($include) {
                $result[] = self::withoutTransportCc($content);
            }
        }
        return $result;
    }

    /**
     * Remove transport-cc (transport-wide congestion control) from a `MediaContent`, offered or answered.
     *
     * We don't implement transport-cc feedback. Negotiating it makes modern peers (Telegram Android)
     * drive their uplink congestion control off transport-cc, receive no such feedback from us, and
     * throttle to the minimum bitrate ("weak signal", 320x180). Without its feedback type and RTP
     * extension, the peer falls back to REMB, which we do send.
     *
     * @param array<array-key, mixed> $content
     * @return array<array-key, mixed>
     */
    public static function withoutTransportCc(array $content): array
    {
        if (isset($content['payloadTypes']) && \is_array($content['payloadTypes'])) {
            foreach ($content['payloadTypes'] as &$payloadType) {
                if (\is_array($payloadType) && isset($payloadType['feedbackTypes']) && \is_array($payloadType['feedbackTypes'])) {
                    $payloadType['feedbackTypes'] = array_values(array_filter(
                        $payloadType['feedbackTypes'],
                        static fn ($fb): bool => !\is_array($fb) || strtolower((string) ($fb['type'] ?? '')) !== 'transport-cc',
                    ));
                }
            }
            unset($payloadType);
        }
        if (isset($content['rtpExtensions']) && \is_array($content['rtpExtensions'])) {
            $content['rtpExtensions'] = array_values(array_filter(
                $content['rtpExtensions'],
                static fn ($e): bool => !\is_array($e) || !str_contains(strtolower((string) ($e['uri'] ?? '')), 'transport-wide-cc'),
            ));
        }
        return $content;
    }

    /**
     * The mid of every m-line of an SDP, in order (null where a section has none).
     *
     * @return list<?string>
     */
    public static function mids(string $sdp): array
    {
        return array_map(static fn (array $section): ?string => \is_string($section['_mid']) ? $section['_mid'] : null, self::sections($sdp));
    }

    /**
     * Parse every media section of an SDP into a `MediaContent` plus the m-line's own attributes.
     *
     * @return list<array<string, mixed>> Contents carrying the extra `_port`, `_direction` and `_mid`
     *                                    keys, which {@see self::contentOfSection()} strips.
     */
    private static function sections(string $sdp): array
    {
        /** @var list<array<string, mixed>> $contents */
        $contents = [];
        /** @var array<string, mixed>|null $current */
        $current = null;

        foreach (self::lines($sdp) as $line) {
            if (str_starts_with($line, 'm=')) {
                if ($current !== null) {
                    $contents[] = $current;
                }
                $parts = explode(' ', substr($line, 2));
                $kind = $parts[0];
                $current = [
                    'type' => $kind === 'video' ? 'video' : 'audio',
                    'ssrc' => '0',
                    'payloadTypes' => [],
                    'rtpExtensions' => [],
                    'ssrcGroups' => [],
                    '_port' => (int) ($parts[1] ?? 0),
                    '_direction' => 'sendrecv',
                    '_mid' => null,
                ];
                continue;
            }
            if ($current === null) {
                continue;
            }
            if (\in_array($line, ['a=sendrecv', 'a=sendonly', 'a=recvonly', 'a=inactive'], true)) {
                $current['_direction'] = substr($line, 2);
                continue;
            }
            if (str_starts_with($line, 'a=mid:')) {
                $current['_mid'] = substr($line, 6);
                continue;
            }
            if (str_starts_with($line, 'a=ssrc:') && $current['ssrc'] === '0') {
                $ssrc = (int) substr($line, 7);
                // tgcalls transmits SSRCs as unsigned decimal strings.
                $current['ssrc'] = (string) GroupSdp::toUnsignedSsrc($ssrc);
                continue;
            }
            if (str_starts_with($line, 'a=ssrc-group:')) {
                // tgcalls' InstanceV2Impl binds the incoming video receive stream from the SSRCs it
                // finds in the content's ssrcGroups (see IncomingV2VideoChannel); a primary SSRC that
                // appears in no group is left unsignaled and demultiplexed purely by MID, a path that
                // silently drops the stream here. Echoing the FID group (primary + RTX) makes the peer
                // latch our SSRC and route the video by it, exactly like a real tgcalls sender does.
                $groupParts = array_values(array_filter(explode(' ', substr($line, \strlen('a=ssrc-group:'))), static fn (string $part): bool => $part !== ''));
                $semantics = array_shift($groupParts);
                $ssrcs = [];
                foreach ($groupParts as $groupSsrc) {
                    $ssrcs[] = (string) GroupSdp::toUnsignedSsrc((int) $groupSsrc);
                }
                if ($semantics !== null && $semantics !== '' && $ssrcs !== []) {
                    /** @var list<array{semantics: string, ssrcs: list<string>}> $groups */
                    $groups = $current['ssrcGroups'];
                    $groups[] = ['semantics' => $semantics, 'ssrcs' => $ssrcs];
                    $current['ssrcGroups'] = $groups;
                }
                continue;
            }
            if (str_starts_with($line, 'a=rtpmap:')) {
                /** @var list<array<string, mixed>> $payloadTypes */
                $payloadTypes = $current['payloadTypes'];
                $payloadTypes[] = self::payloadTypeFromRtpmap(substr($line, 9));
                $current['payloadTypes'] = $payloadTypes;
                continue;
            }
            if (str_starts_with($line, 'a=extmap:')) {
                $parts = explode(' ', substr($line, 9), 2);
                if (\count($parts) === 2) {
                    /** @var list<array{id: int, uri: string}> $extensions */
                    $extensions = $current['rtpExtensions'];
                    $extensions[] = ['id' => (int) $parts[0], 'uri' => trim($parts[1])];
                    $current['rtpExtensions'] = $extensions;
                }
                continue;
            }
            if (str_starts_with($line, 'a=fmtp:')) {
                self::applyFmtp($current, substr($line, 7));
                continue;
            }
            if (str_starts_with($line, 'a=rtcp-fb:')) {
                self::applyFeedback($current, substr($line, 10));
            }
        }
        if ($current !== null) {
            $contents[] = $current;
        }
        return $contents;
    }

    /**
     * Whether a parsed section is one we transmit on.
     *
     * @param array<string, mixed> $section
     *
     * @psalm-pure
     */
    private static function isSending(array $section): bool
    {
        return $section['_port'] !== 0 && \in_array($section['_direction'], ['sendrecv', 'sendonly'], true);
    }

    /**
     * The `MediaContent` of a parsed section, without the section-level bookkeeping.
     *
     * @param array<string, mixed> $section
     * @return array<string, mixed>
     *
     * @psalm-pure
     */
    private static function contentOfSection(array $section): array
    {
        unset($section['_port'], $section['_direction'], $section['_mid']);
        // Only carry ssrcGroups when there actually are any, matching tgcalls' own messages.
        if (($section['ssrcGroups'] ?? []) === []) {
            unset($section['ssrcGroups']);
        }
        return $section;
    }

    /**
     * Parse `payloadType clockRate[/channels]` out of an `a=rtpmap` line.
     *
     * @return array<string, mixed>
     *
     * @psalm-pure
     */
    private static function payloadTypeFromRtpmap(string $value): array
    {
        [$id, $codec] = array_pad(explode(' ', $value, 2), 2, '');
        $parts = explode('/', $codec);
        return [
            'id' => (int) $id,
            'name' => $parts[0] ?? '',
            'clockrate' => (int) ($parts[1] ?? 0),
            'channels' => (int) ($parts[2] ?? 0),
            'feedbackTypes' => [],
            'parameters' => (object) [],
        ];
    }

    /**
     * Attach the `a=fmtp` parameters to the payload type they belong to.
     *
     * @param array<string, mixed> $content
     */
    private static function applyFmtp(array &$content, string $value): void
    {
        [$id, $params] = array_pad(explode(' ', $value, 2), 2, '');
        $parsed = [];
        foreach (explode(';', $params) as $pair) {
            $pair = trim($pair);
            if ($pair === '') {
                continue;
            }
            [$key, $val] = array_pad(explode('=', $pair, 2), 2, '');
            $parsed[trim($key)] = trim($val);
        }
        /** @var list<array<string, mixed>> $payloadTypes */
        $payloadTypes = $content['payloadTypes'];
        foreach ($payloadTypes as $index => $payloadType) {
            if ($payloadType['id'] === (int) $id) {
                $payloadTypes[$index]['parameters'] = $parsed === [] ? (object) [] : $parsed;
                break;
            }
        }
        $content['payloadTypes'] = $payloadTypes;
    }

    /**
     * Attach an `a=rtcp-fb` entry to the payload type it belongs to.
     *
     * @param array<string, mixed> $content
     */
    private static function applyFeedback(array &$content, string $value): void
    {
        $parts = explode(' ', $value);
        $id = (int) array_shift($parts);
        $type = array_shift($parts) ?? '';
        $subtype = implode(' ', $parts);
        /** @var list<array<string, mixed>> $payloadTypes */
        $payloadTypes = $content['payloadTypes'];
        foreach ($payloadTypes as $index => $payloadType) {
            if ($payloadType['id'] === $id) {
                /** @var list<array{type: string, subtype: string}> $feedback */
                $feedback = $payloadTypes[$index]['feedbackTypes'];
                $feedback[] = ['type' => $type, 'subtype' => $subtype];
                $payloadTypes[$index]['feedbackTypes'] = $feedback;
                break;
            }
        }
        $content['payloadTypes'] = $payloadTypes;
    }

    /**
     * Synthesize the remote description that expresses the whole negotiated state of the call.
     *
     * Every m-line of our own offer (the template) is described from the peer's point of view:
     *
     *  - an m-line that carries one of the *peer's* channels (`$recvContents`) is `a=sendonly`
     *    (the peer sends), with the peer's own SSRC and payload types;
     *  - an m-line we transmit on is `a=recvonly` (the peer receives), with the payload types the
     *    peer accepted for it (`$sendContents`), or with our own if it has not answered yet;
     *  - anything else is inactive: rejected (port 0) in an answer, kept reusable in an offer.
     *
     * The two directions never share an m-line, so each keeps its own payload type numbering â€”
     * the peer sends with its numbers and expects ours on what we send, just like tgcalls.
     *
     * @param string               $offer        Our own local offer, used as the m-line template.
     * @param array<string, mixed> $initialSetup The peer's `InitialSetup` message.
     * @param array<string, array> $sendContents The peer's answer for our outgoing channels, keyed by
     *                                           our SSRC (decimal string).
     * @param array<string, array> $recvContents The peer's outgoing channels, keyed by the mid of the
     *                                           receive-only m-line that carries each of them.
     * @param bool                 $answer       Whether the result is an answer to our offer.
     */
    public static function buildRemoteDescription(
        string $offer,
        array $initialSetup,
        array $sendContents,
        array $recvContents,
        bool $answer
    ): string {
        $ufrag = (string) ($initialSetup['ufrag'] ?? '');
        $pwd = (string) ($initialSetup['pwd'] ?? '');
        if ($ufrag === '' || $pwd === '') {
            throw new Exception('The peer sent an InitialSetup without ICE credentials!');
        }

        $fingerprints = [];
        $setup = 'active';
        foreach ($initialSetup['fingerprints'] ?? [] as $fingerprint) {
            $fingerprints[] = 'a=fingerprint:'.$fingerprint['hash'].' '.$fingerprint['fingerprint'];
            $setup = (string) ($fingerprint['setup'] ?? $setup);
        }
        if ($fingerprints === []) {
            throw new Exception('The peer sent an InitialSetup without a DTLS fingerprint!');
        }
        // An answer may never say actpass: pick a concrete role opposite to ours.
        if ($answer && $setup === 'actpass') {
            $setup = 'active';
        }

        // Session-level lines are copied verbatim (they include the BUNDLE group).
        $result = [];
        foreach (self::lines($offer) as $line) {
            if (str_starts_with($line, 'm=')) {
                break;
            }
            $result[] = $line;
        }

        foreach (self::sections($offer) as $section) {
            $kind = $section['type'] === 'video' ? 'video' : 'audio';
            /** @var ?string $mid */
            $mid = $section['_mid'];
            /** @var ?array<string, mixed> $recv */
            $recv = $mid !== null ? ($recvContents[$mid] ?? null) : null;
            if ($recv !== null) {
                $content = $recv;
                $direction = 'sendonly';
            } elseif (self::isSending($section)) {
                /** @var array<string, mixed> $content */
                $content = $sendContents[(string) $section['ssrc']] ?? self::contentOfSection($section);
                $direction = 'recvonly';
            } else {
                $content = self::contentOfSection($section);
                $direction = 'inactive';
            }

            $formats = [];
            foreach ($content['payloadTypes'] ?? [] as $payloadType) {
                $formats[] = (string) $payloadType['id'];
            }
            $formatList = $formats === [] ? '0' : implode(' ', $formats);

            if ($direction === 'inactive' && $answer) {
                // Answers reject an unaccepted m-line; a renegotiation offer instead keeps an
                // inactive, reusable section, matching tgcalls' persistent channel ordering.
                $result[] = 'm='.$kind.' 0 UDP/TLS/RTP/SAVPF '.$formatList;
                $result[] = 'c=IN IP4 0.0.0.0';
                if ($mid !== null) {
                    $result[] = 'a=mid:'.$mid;
                }
                $result[] = 'a=inactive';
                continue;
            }

            $result[] = 'm='.$kind.' 9 UDP/TLS/RTP/SAVPF '.$formatList;
            $result[] = 'c=IN IP4 0.0.0.0';
            if ($mid !== null) {
                $result[] = 'a=mid:'.$mid;
            }
            $result[] = 'a='.$direction;
            $result[] = 'a=rtcp-mux';
            $result[] = 'a=rtcp:9 IN IP4 0.0.0.0';
            self::appendRtp($result, $content);
            $result[] = 'a=ice-ufrag:'.$ufrag;
            $result[] = 'a=ice-pwd:'.$pwd;
            $result = array_merge($result, $fingerprints);
            $result[] = 'a=setup:'.$setup;
            if ($direction === 'sendonly') {
                self::appendSsrcs($result, $content);
            }
        }

        return implode("\r\n", $result)."\r\n";
    }

    /**
     * Append the payload type and header extension lines of a content.
     *
     * @param list<string>         $result
     * @param array<string, mixed> $content
     */
    private static function appendRtp(array &$result, array $content): void
    {
        // tgcalls demultiplexes the unsignaled incoming video purely by the sdes:mid RTP
        // extension, yet its answers never echo that extension back. Re-advertising it here (at
        // tgcalls' fixed id 1) keeps it in the mutual set so our senders actually stamp the mid;
        // without it the offer/answer intersection drops it and all video is silently discarded.
        $extensions = $content['rtpExtensions'] ?? [];
        $hasMid = array_any(
            $extensions,
            static fn (array $extension): bool => ($extension['uri'] ?? '') === self::MID_EXTENSION_URI
        );
        if (!$hasMid) {
            $result[] = 'a=extmap:1 '.self::MID_EXTENSION_URI;
        }
        foreach ($extensions as $extension) {
            $result[] = 'a=extmap:'.$extension['id'].' '.$extension['uri'];
        }
        foreach ($content['payloadTypes'] ?? [] as $payloadType) {
            $line = 'a=rtpmap:'.$payloadType['id'].' '.$payloadType['name'].'/'.$payloadType['clockrate'];
            if (($payloadType['channels'] ?? 0) > 1) {
                $line .= '/'.$payloadType['channels'];
            }
            $result[] = $line;
            foreach ($payloadType['feedbackTypes'] ?? [] as $feedback) {
                $result[] = trim('a=rtcp-fb:'.$payloadType['id'].' '.$feedback['type'].' '.($feedback['subtype'] ?? ''));
            }
            $parameters = (array) ($payloadType['parameters'] ?? []);
            if ($parameters !== []) {
                $pairs = [];
                foreach ($parameters as $key => $value) {
                    $pairs[] = $key.'='.$value;
                }
                $result[] = 'a=fmtp:'.$payloadType['id'].' '.implode(';', $pairs);
            }
        }
    }

    /**
     * Append the SSRC lines of a channel the peer sends: its primary SSRC first, then the SSRC
     * groups (FID: primary + retransmission) so the RTX stream is bound to it too.
     *
     * @param list<string>         $result
     * @param array<string, mixed> $content
     */
    private static function appendSsrcs(array &$result, array $content): void
    {
        $primary = (int) ($content['ssrc'] ?? 0);
        if ($primary === 0) {
            return;
        }
        $ssrcs = [$primary];
        /** @var list<array{semantics: string, ssrcs: list<string>}> $groups */
        $groups = (array) ($content['ssrcGroups'] ?? []);
        foreach ($groups as $group) {
            $groupSsrcs = array_map(intval(...), $group['ssrcs']);
            if ($groupSsrcs === []) {
                continue;
            }
            $result[] = 'a=ssrc-group:'.$group['semantics'].' '.implode(' ', $groupSsrcs);
            foreach ($groupSsrcs as $ssrc) {
                if (!\in_array($ssrc, $ssrcs, true)) {
                    $ssrcs[] = $ssrc;
                }
            }
        }
        foreach ($ssrcs as $ssrc) {
            $result[] = 'a=ssrc:'.$ssrc.' cname:tgcalls'.$primary;
        }
    }

    /**
     * @return list<string>
     *
     * @psalm-pure
     */
    private static function lines(string $sdp): array
    {
        $lines = [];
        foreach (explode("\n", str_replace("\r\n", "\n", $sdp)) as $line) {
            $line = rtrim($line, "\r");
            if ($line !== '') {
                $lines[] = $line;
            }
        }
        return $lines;
    }

    /**
     * Read the ICE credentials and DTLS fingerprint out of our own local description.
     *
     * @return array{ufrag: string, pwd: string, fingerprints: list<array{hash: string, fingerprint: string, setup: string}>}
     *
     * @psalm-pure
     */
    public static function initialSetupFromDescription(string $sdp, string $setup): array
    {
        $ufrag = '';
        $pwd = '';
        $fingerprints = [];
        foreach (self::lines($sdp) as $line) {
            if ($ufrag === '' && str_starts_with($line, 'a=ice-ufrag:')) {
                $ufrag = substr($line, 12);
            } elseif ($pwd === '' && str_starts_with($line, 'a=ice-pwd:')) {
                $pwd = substr($line, 10);
            } elseif ($fingerprints === [] && str_starts_with($line, 'a=fingerprint:')) {
                [$hash, $value] = array_pad(explode(' ', substr($line, 14), 2), 2, '');
                $fingerprints[] = ['hash' => $hash, 'fingerprint' => $value, 'setup' => $setup];
            }
        }
        return ['ufrag' => $ufrag, 'pwd' => $pwd, 'fingerprints' => $fingerprints];
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Tgcalls;

use danog\MadelineProto\Logger;
use danog\MadelineProto\Settings\VoIP;
use Stringable;

/**
 * Common interface of one-to-one and group call controllers, used by the shared playback machinery.
 *
 * @internal
 * @psalm-mutable
 */
interface CallControllerInterface extends Stringable
{
    /**
     * Log a message, prefixed by the call description.
     * @psalm-impure
     */
    public function log(string $message, int $level = Logger::NOTICE): void;

    /**
     * Whether the call is over, and all playback loops should exit.
     * @psalm-impure
     */
    public function isCallEnded(): bool;

    /**
     * The VoIP settings.
     * @psalm-mutation-free
     */
    public function getVoIPSettings(): VoIP;
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Tgcalls;

use Amp\Pipeline\DisposedException;
use danog\MadelineProto\Logger;
use danog\MadelineProto\Loop\VoIP\DjLoop;
use danog\MadelineProto\Ogg;
use Revolt\EventLoop;
use Webrtc\Codecs\EncodedPacket;
use Webrtc\RTP\Enum\MediaKind;
use Webrtc\RTP\MediaStreamTrack\MediaStreamTrack;

use function Amp\delay;

/**
 * An outgoing audio track that emits the pre-encoded OPUS packets produced by a {@see DjLoop}.
 *
 * The OPUS packets of OGG and WebM files are exactly what both the tgcalls one-to-one peer and the
 * Telegram group call SFU expect: they are therefore passed through to the RTP sender without a
 * decode/encode round trip, which also means that no codec library (and therefore no FFI) is ever
 * needed for playback.
 *
 * The track runs a background producer task that pushes the frames to the RTP sender, which sends
 * them right away (see {@see parent::push()}): the old polling contract, in which the sender
 * repeatedly called {@see self::receiveData()}, is gone. Pacing
 * happens inside the producer by returning `null` (i.e. not pushing) until the next frame is due:
 * an OGG packet lasts as long as its TOC byte says ({@see Ogg::packetDuration()}), a WebM frame until
 * the timestamp of the next one.
 *
 * @internal
 */
final class OpusPlaybackTrack extends MediaStreamTrack
{
    /** Duration of the frame grid kept while silent, in seconds. */
    public const FRAME_DURATION = 0.06;
    /** Number of 48kHz samples in a frame of the grid kept while silent. */
    public const FRAME_SAMPLES = 2880;
    /** OPUS always uses a 48kHz clock rate on the wire. */
    public const CLOCK_RATE = 48000;

    /**
     * The loudness we report for the frames we transmit, in -dBov.
     *
     * Every OPUS packet has to carry the `urn:ietf:params:rtp-hdrext:ssrc-audio-level` header
     * extension of RFC 6464. The Telegram SFU only forwards the participants it believes are
     * speaking, and it decides that from this extension alone: a stream that never carries one is
     * treated as permanently silent and is never relayed to anybody, however much RTP it sends.
     *
     * The value is a constant rather than a measurement because playback is deliberately a
     * pass-through â€” decoding OPUS just to compute a level would drag in a codec library, and
     * therefore FFI, for every call. A player is in any case continuously "speaking", so a fixed
     * moderate level describes it accurately.
     */
    private const AUDIO_LEVEL_DBOV = -20;
    /** How late a frame may be released, in seconds, before the track resynchronizes instead of catching up. */
    private const MAX_LATENESS = 1.0;

    /** Wall clock time at which the next frame is due. */
    private ?float $nextFrame = null;
    /** RTP timestamp (in 48kHz samples) of the next frame. */
    private int $timestamp = 0;
    /** Wall clock time at which the producer should next attempt to emit a frame. */
    private ?float $nextDue = null;
    /** Whether the frame to produce next isn't queued yet: the producer waits for it to be. */
    private bool $starved = false;

    /** Wall clock time corresponding to the first WebM frame we released. */
    private ?float $webmStartedAt = null;
    /** Source timestamp of the first WebM frame, used to rebase onto our own RTP clock. */
    private ?int $webmBaseTimestamp = null;
    /** RTP timestamp the current WebM file was rebased onto. */
    private int $webmTimestampOffset = 0;
    /** @var array{data: string, timestamp: int, file: int}|null The WebM frame whose presentation time has not arrived yet. */
    private ?array $webmPending = null;
    /** Source timestamp of the previously released WebM frame, used to measure its cadence. */
    private ?int $webmPrevTimestamp = null;
    /** The current WebM file's real audio frame interval in seconds, from its own timestamps. */
    private ?float $webmFrameInterval = null;
    /** The DJ loop's ID of the WebM file whose frames are being played, see {@see DjLoop::$webmAudioQueue}. */
    private ?int $webmFile = null;

    private bool $muted = true;
    /** Whether the producer task is running, to keep {@see self::startProducing()} idempotent. */
    private bool $producing = false;

    public function __construct(
        private readonly DjLoop $dj,
        private readonly CallControllerInterface $call,
    ) {
        parent::__construct(MediaKind::Audio);
        $this->startProducing();
    }

    /**
     * @param array<string, mixed> $data
     */
    #[\Override]
    public function __unserialize(array $data): void
    {
        // Deliberately does NOT restart the producer: the detached producer task reads the call's
        // state, which is only restored once the whole call graph has finished deserializing. Starting
        // it here would let it run mid-deserialization (if a nested resume suspends the fiber) and
        // dereference not-yet-restored state. The call's own deserializer calls {@see self::resume()}
        // once the graph is whole and validated.
        parent::__unserialize($data);
        // The producer task did not survive serialization; allow resume() to start a fresh one.
        $this->producing = false;
    }

    /**
     * Restart the frame producer after a serialize/unserialize cycle. Called by the call's deserializer
     * once the whole call graph is restored; idempotent.
     */
    public function resume(): void
    {
        $this->startProducing();
    }

    /**
     * Drive the frame producer as a single long-lived, self-pacing event-loop task.
     */
    private function startProducing(): void
    {
        if ($this->producing) {
            return;
        }
        $this->producing = true;
        EventLoop::queue(function (): void {
            try {
                $this->produceLoop();
            } finally {
                $this->producing = false;
            }
        });
    }

    private function produceLoop(): void
    {
        {
            while (!$this->isEnded() && !$this->call->isCallEnded()) {
                $packet = $this->produce();
                if ($packet !== null) {
                    try {
                        $this->push($packet);
                    } catch (DisposedException|\Error $e) {
                        // The consumer (the RTP sender) went away â€” its connection was closed or
                        // replaced â€” while a frame was waiting to be taken: this track will never be
                        // read again, so stop producing instead of crashing the event loop.
                        $this->call->log("The outgoing audio track of {$this->call} lost its consumer: {$e->getMessage()}", Logger::VERBOSE);
                        return;
                    }
                }
                // Sleep reactively until the next frame is due (its media cadence), rather than
                // polling for work at a fixed interval.
                $wait = ($this->nextDue ?? microtime(true)) - microtime(true);
                if ($wait > 0) {
                    if ($this->starved) {
                        // Played as soon as it's queued, if it's late.
                        $this->dj->awaitFrame($wait);
                    } else {
                        delay($wait);
                    }
                }
            }
        }
    }

    /**
     * Whether we're currently not transmitting any audio.
     */
    public function isMuted(): bool
    {
        return $this->muted;
    }

    /**
     * Produce the next audio frame, if one is due.
     *
     * Returns `null` (and pushes nothing) to pace the stream until the next frame's presentation
     * time arrives.
     */
    private function produce(): ?EncodedPacket
    {
        $this->starved = false;
        // Audio coming from a WebM file wins: it has to stay in sync with that file's video.
        if ($this->webmPending !== null
            || $this->dj->hasWebmAudio()
            || ($this->webmStartedAt !== null && $this->dj->isWebmPlaying())
        ) {
            return $this->receiveWebm();
        }
        if ($this->webmStartedAt !== null) {
            // The file is over: hand the frame grid back to the DJ loop, starting now so that the
            // first DJ frame is not immediately overdue.
            $this->webmStartedAt = null;
            $this->webmBaseTimestamp = null;
            $this->webmPrevTimestamp = null;
            $this->webmFrameInterval = null;
            $this->nextFrame = null;
        }

        $now = microtime(true);
        if ($this->nextFrame === null) {
            $this->nextFrame = $now;
        } elseif ($now < $this->nextFrame) {
            $this->nextDue = $this->nextFrame;
            return null;
        } elseif ($now - $this->nextFrame > self::MAX_LATENESS) {
            // We fell way behind (the process was suspended, or the call was just resumed):
            // resynchronize instead of bursting out a second of audio.
            $this->nextFrame = $now;
        }

        $opus = $this->dj->tryPullPacket();
        if ($opus === null) {
            if (!$this->muted) {
                $this->muted = true;
                $this->call->log("Muting outgoing audio in {$this->call}");
            }
            // Keep the frame grid aligned while silent, so that playback resumes in sync.
            $this->nextFrame += self::FRAME_DURATION;
            $this->timestamp += self::FRAME_SAMPLES;
            $this->nextDue = $this->nextFrame;
            return null;
        }
        if ($this->muted) {
            $this->muted = false;
            $this->call->log("Unmuting outgoing audio in {$this->call}");
        }

        $packet = new EncodedPacket($opus, $this->timestamp, audioLevel: self::AUDIO_LEVEL_DBOV);

        $samples = Ogg::packetDuration($opus);
        $this->nextFrame += $samples / self::CLOCK_RATE;
        $this->timestamp += $samples;
        $this->nextDue = $this->nextFrame;

        return $packet;
    }

    /**
     * Produce the next OPUS frame of the WebM file being played.
     *
     * The frames of a file are not on our own 60ms grid (20ms is what most encoders emit), so they
     * are paced and timestamped by the clock of the file itself, exactly like its video frames.
     */
    private function receiveWebm(): ?EncodedPacket
    {
        $frame = $this->webmPending ?? $this->dj->pullWebmAudio();
        $this->webmPending = null;
        if ($frame === null) {
            // The file's audio queue momentarily ran dry: wait for the next frame to be queued. For
            // at most the file's own frame interval, once we have measured it, to notice that it
            // ended; until the first two frames are seen, for at most our 60ms frame grid.
            $this->starved = true;
            $this->nextDue = microtime(true) + ($this->webmFrameInterval ?? self::FRAME_DURATION);
            return null;
        }

        $now = microtime(true);
        if ($this->webmStartedAt === null || $this->webmBaseTimestamp === null || $frame['file'] !== $this->webmFile) {
            // The first frame of a file: pace it from now. The next file may follow without the
            // queue ever running dry, so this must not wait for produce() to notice the previous one ended.
            $this->webmStartedAt = $now;
            $this->webmBaseTimestamp = $frame['timestamp'];
            $this->webmFile = $frame['file'];
            $this->webmPrevTimestamp = null;
            $this->webmFrameInterval = null;
            // Carry on from the DJ loop's clock (or the previous file's), so that the RTP timestamps never rewind.
            $this->webmTimestampOffset = $this->timestamp;
        }

        // Release the frame only once its presentation time has arrived.
        $elapsed = (float) ($frame['timestamp'] - $this->webmBaseTimestamp) / (float) self::CLOCK_RATE;
        if ($now < $this->webmStartedAt + $elapsed) {
            $this->webmPending = $frame;
            $this->nextDue = $this->webmStartedAt + $elapsed;
            return null;
        }
        if ($now - $this->webmStartedAt - $elapsed > self::MAX_LATENESS) {
            // We fell way behind (nobody took the frames until the call connected, or the process was
            // suspended): resynchronize instead of bursting out all of the audio we're late with.
            $this->webmStartedAt = $now - $elapsed;
        }

        if ($this->muted) {
            $this->muted = false;
            $this->call->log("Unmuting outgoing audio in {$this->call}");
        }

        $timestamp = $this->webmTimestampOffset + ($frame['timestamp'] - $this->webmBaseTimestamp);
        // Where the DJ loop would resume, were the file to end right after this frame.
        $this->timestamp = $timestamp + self::FRAME_SAMPLES;
        // Learn this file's real audio frame interval from consecutive source timestamps, so an
        // underrun retry can honor the file's own cadence rather than the fixed 60ms DJ grid.
        if ($this->webmPrevTimestamp !== null) {
            $delta = $frame['timestamp'] - $this->webmPrevTimestamp;
            if ($delta > 0) {
                $this->webmFrameInterval = $delta / (float) self::CLOCK_RATE;
            }
        }
        $this->webmPrevTimestamp = $frame['timestamp'];
        // Pull and hold the next WebM frame right away so its own presentation time paces us.
        $this->nextDue = $now;

        return new EncodedPacket($frame['data'], $timestamp, audioLevel: self::AUDIO_LEVEL_DBOV);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Tgcalls;

use Amp\ByteStream\WritableStream;
use danog\MadelineProto\CallStream;
use danog\MadelineProto\LocalFile;
use danog\MadelineProto\Logger;
use danog\MadelineProto\MatroskaWriter;
use danog\MadelineProto\RecordingFormat;
use danog\MadelineProto\RestoresDeclaredProperties;

/**
 * Records the incoming media of one party of a call into Matroska (.mkv) files, in pure PHP.
 *
 * The receivers run in raw mode, so the already-encoded OPUS audio and video frames that arrive
 * over RTP are muxed straight into the file by {@see MatroskaWriter} without any decoding, ffmpeg,
 * CLI tool or FFI. The video codec, keyframe flags and picture size are recovered from the frames
 * themselves (see {@see self::describeVideo()}), since RTP does not carry them. Frames are pushed
 * in by the {@see IncomingMedia} router of the party, which drains their tracks.
 *
 * A party has up to three streams â€” its microphone ({@see CallStream::AUDIO}), its camera
 * ({@see CallStream::VIDEO}) and its screen share ({@see CallStream::SCREEN}) â€” and can turn each
 * on and off at any time, in any combination. Matroska fixes the track list in the file header, so
 * a recorder works in one of two modes:
 *
 *  - a *fixed* recorder ({@see self::fixed()}) writes one file (or stream) whose tracks are the
 *    streams chosen when it was requested (or all the ones flowing when its header is committed).
 *    A stream that stops just stops being written, and is written again when it comes back; a
 *    stream that is not in the file is ignored. Only a change of codec of one of its video tracks
 *    finishes the file early â€” a Matroska track cannot change codec â€” after which the recorder is
 *    closed for good;
 *  - a *series* recorder ({@see self::series()}) follows every change instead: whenever the set of
 *    flowing streams changes (a stream starts or stops, or a video stream comes back with another
 *    codec; not merely another picture size, which decoders follow from the bitstream), the current
 *    file is closed and a new one is opened, whose tracks are
 *    exactly the streams flowing from then on. Segment files are named `<stem>.<n>_<streams>.mkv`
 *    (or `.webm`): `n` counts from 0 and `streams` lists what the file holds â€” `audio`, `video`
 *    (the camera) and `screen`, joined by commas â€” so `dir/123` yields `dir/123.0_audio.mkv`,
 *    `dir/123.1_audio,video.mkv`, â€¦ and a stem ending in `/` (a directory) yields `dir/0_audio.mkv`, â€¦.
 *
 * Which streams to expect is told by whoever routes the media (from the peer's media state, or a
 * group call participant's flags, see {@see self::setExpected()}); the header of a file is
 * committed once every expected stream has been seen, or after a short grace period. A
 * {@see RecordingObserver} is told whenever a file is opened or finished.
 *
 * @psalm-import-type StreamMask from CallStream
 * @internal
 */
final class CallRecorder
{
    use RestoresDeclaredProperties;
    /** The two video slots, as {@see CallStream} flags. */
    private const VIDEO_SLOTS = [CallStream::VIDEO, CallStream::SCREEN];
    /** Every slot, as {@see CallStream} flags. */
    private const SLOTS = [CallStream::AUDIO, CallStream::VIDEO, CallStream::SCREEN];

    /** Grace period (seconds) to wait for an expected stream before committing a file without it. */
    private const GRACE = 2.0;

    private ?MatroskaWriter $writer = null;
    private bool $closed = false;
    /** Whether the current file's header has been written. */
    private bool $started = false;
    /** How many segments were rolled so far (series mode; 0: still on the base file). */
    private int $segment = 0;
    /** Wall clock of the first frame buffered for the file being opened, for the grace period. */
    private ?float $waitingSince = null;

    /**
     * Which streams the party is currently sending, as far as signaling tells: true expected, false
     * not, null unknown. A stream that stops is not written until it comes back (and, in series
     * mode, is dropped from the next segment right away); one that starts joins once its first
     * (key)frame arrives.
     *
     * @var array<int, ?bool> By {@see CallStream} flag.
     */
    private array $expected = [CallStream::AUDIO => null, CallStream::VIDEO => null, CallStream::SCREEN => null];
    /**
     * The video tracks described so far, by slot: what a file opened now would declare.
     *
     * @var array<int, array{codecId: string, width: int, height: int, private: string}>
     */
    private array $described = [];
    /** @var array<int, int> The source (track) each slot's frames currently come from. */
    private array $sources = [];
    /** @var array<int, ?int> The first RTP timestamp of each slot's current source. */
    private array $baseTs = [];
    /** @var array<int, int> Offset (ms) placing each slot's current source on the recording's clock. */
    private array $offsetMs = [];
    /** @var array<int, int> The last timestamp (ms) written per slot. */
    private array $lastMs = [];
    /** @var array<int, list<array{data: string, ms: int, keyframe: bool}>> Frames buffered until the header is written. */
    private array $buffers = [];
    /** Series mode: whether the set of flowing streams changed and the next frame must go to a new segment. */
    private bool $rollPending = false;
    /** @var array<int, true> Slots that delivered at least one frame (for the diagnostics log). */
    private array $seenSlots = [];

    /** The file the recording was requested to (the series stem in series mode), or null for a stream (which cannot survive a serialize cycle). */
    public readonly ?LocalFile $file;
    /** The output of the file currently open, for the observer. */
    private WritableStream|LocalFile|null $currentOut = null;
    /** Series mode: the segment file stem, the requested path without its extension or a directory with its trailing `/`. */
    private string $stem = '';
    private WritableStream|LocalFile $out;
    private RecordingFormat $format;
    private ?RecordingObserver $observer = null;

    /**
     * @param bool $series   Whether to roll a new segment on every change of the flowing streams
     *                       (false: one file with fixed tracks).
     * @param ?StreamMask $streams  Fixed mode: the {@see CallStream} flags of the tracks the file holds, or
     *                       null to take every stream flowing when the header is committed.
     */
    private function __construct(LocalFile|WritableStream $out, RecordingFormat $format, private readonly bool $series, private readonly ?int $streams)
    {
        $this->file = $out instanceof LocalFile ? $out : null;
        $this->out = $out;
        $this->format = $format;
        if ($series && $out instanceof LocalFile) {
            $path = $out->file;
            if (str_ends_with($path, '/')) {
                $this->stem = $path;
            } else {
                $dot = strrpos(basename($path), '.');
                $this->stem = $dot === false ? $path : substr($path, 0, \strlen($path) - \strlen(basename($path)) + $dot);
            }
        }
    }

    /**
     * A recorder writing one file (or stream) with a fixed set of tracks.
     *
     * @param ?StreamMask $streams The {@see CallStream} flags of the tracks to hold, or null to take every
     *                      stream flowing when the header is committed. A file holding no video
     *                      track is opened right away; one with video tracks is opened once each of
     *                      them was described by its first keyframe (or the grace period passed).
     */
    public static function fixed(LocalFile|WritableStream $out, RecordingFormat $format, ?int $streams = null, ?RecordingObserver $observer = null): self
    {
        if ($streams !== null) {
            CallStream::validate($streams);
        }
        $recorder = new self($out, $format, false, $streams);
        $recorder->observer = $observer;
        $recorder->maybeBegin();
        return $recorder;
    }

    /**
     * A recorder writing a numbered series of segment files, one per combination of flowing streams.
     *
     * @param LocalFile $stem `name.mkv` gives `name.<n>_<streams>.mkv` segments; a path ending in `/`
     *                        gives `<n>_<streams>.mkv` files in that directory.
     */
    public static function series(LocalFile $stem, RecordingFormat $format, ?RecordingObserver $observer = null): self
    {
        $recorder = new self($stem, $format, true, null);
        $recorder->observer = $observer;
        return $recorder;
    }

    /**
     * Whether the recorder finished (was closed, or its file ended on a codec change).
     *
     * @psalm-mutation-free
     */
    public function isClosed(): bool
    {
        return $this->closed;
    }

    /**
     * @psalm-external-mutation-free
     */
    public function setObserver(?RecordingObserver $observer): void
    {
        $this->observer = $observer;
    }

    /**
     * The writer serializes itself and reopens its file; nothing else here is live.
     *
     * @psalm-mutation-free
     */
    public function __serialize(): array
    {
        $vars = get_object_vars($this);
        if ($this->file === null) {
            unset($vars['out'], $vars['currentOut']);
        }
        return $vars;
    }

    /**
     * @psalm-external-mutation-free
     */
    public function __unserialize(array $data): void
    {
        // Synchronous state restoration only â€” no async work here (see resume()).
        $this->restoreDeclaredProperties($data);
        if (!isset($this->out) && $this->file !== null) {
            $this->out = $this->file;
        }
        if (!isset($this->currentOut)) {
            $this->currentOut = null;
        }
    }

    /**
     * Reopen the file and resume recording after the whole call graph has been deserialized, into
     * the reopened, append-mode writer. Called by the owner's resume(); never during unserialize.
     */
    public function resume(): void
    {
        if ($this->closed) {
            return;
        }
        if ($this->file !== null) {
            // The recording folder may have been deleted while we were away: recreate it, once, now.
            $dir = $this->series
                ? (str_ends_with($this->stem, '/') ? rtrim($this->stem, '/') : \dirname($this->stem))
                : \dirname($this->file->file);
            if (!is_dir($dir) && !mkdir($dir, 0o777, true) && !is_dir($dir)) {
                throw new \RuntimeException("Could not recreate the recording directory $dir");
            }
        }
        if ($this->writer !== null && !$this->writer->resume()) {
            if (!$this->series) {
                // The file was deleted while we were away: a fixed recording cannot continue.
                Logger::log('The recording '.($this->file?->file ?? 'stream').' no longer exists, finishing it', Logger::WARNING);
                $this->end();
                return;
            }
            // The open segment was deleted while we were away: drop it and let the next frames open a
            // fresh segment with whatever streams are flowing.
            Logger::log('The recording segment of '.($this->file?->file ?? 'stream').' no longer exists, starting a new one', Logger::WARNING);
            $this->writer = null;
            $this->currentOut = null;
            $this->started = false;
            $this->waitingSince = null;
            $this->rollPending = false;
        }
    }

    /**
     * Tell the recorder which streams the party is sending, as signaling reports it (null leaves a
     * stream's expectation unchanged). A stream reported off is not written until it is reported on
     * again (and, in series mode, is dropped from the next segment at once); one reported on is
     * waited for before a file is committed.
     */
    public function setExpected(?bool $audio = null, ?bool $video = null, ?bool $presentation = null): void
    {
        if ($this->closed) {
            return;
        }
        $changed = false;
        foreach ([CallStream::AUDIO => $audio, CallStream::VIDEO => $video, CallStream::SCREEN => $presentation] as $slot => $value) {
            if ($value === null || $this->expected[$slot] === $value) {
                continue;
            }
            $this->expected[$slot] = $value;
            $changed = true;
            if (!$value && $slot !== CallStream::AUDIO) {
                // The stream is gone; when it comes back it is a new track, to be described afresh.
                unset($this->described[$slot], $this->sources[$slot], $this->baseTs[$slot], $this->buffers[$slot]);
            } elseif (!$value) {
                unset($this->buffers[$slot]);
            }
        }
        if (!$changed) {
            return;
        }
        if (!$this->started) {
            $this->maybeBegin();
        } elseif ($this->series) {
            // Something stopped or started: re-evaluate the segment as soon as the state is stable.
            $this->rollPending = true;
            $this->waitingSince = microtime(true);
            $this->maybeRoll();
        }
        // A fixed file just stops (or resumes) writing the stream: nothing to do.
    }

    /**
     * Record one incoming OPUS frame.
     *
     * @param int $rtpTimestamp The frame's RTP timestamp (48 kHz clock).
     * @param int $source       Identifies the track the frame belongs to.
     */
    public function pushAudioFrame(string $data, int $rtpTimestamp, int $source = 0): void
    {
        if ($this->closed || $data === '' || !$this->wanted(CallStream::AUDIO)) {
            return;
        }
        $ms = $this->placeOnClock(CallStream::AUDIO, $source, $rtpTimestamp, 48000);
        $this->handleFrame(CallStream::AUDIO, $data, $ms, true);
    }

    /**
     * Record one incoming video frame (still encoded, as it came off RTP).
     *
     * @param int    $rtpTimestamp The frame's RTP timestamp (90 kHz clock).
     * @param int    $source       Identifies the track the frame belongs to; a new source is placed on
     *                             the recording's clock where it currently is.
     * @param int    $slot         Whether this is the camera ({@see CallStream::VIDEO}) or the screen share ({@see CallStream::SCREEN}).
     */
    public function pushVideoFrame(string $data, int $rtpTimestamp, int $source = 0, int $slot = CallStream::VIDEO): void
    {
        if ($this->closed || $data === '') {
            return;
        }
        \assert(\in_array($slot, self::VIDEO_SLOTS, true));
        if ($this->expected[$slot] === false || !$this->wanted($slot)) {
            return; // Signaling says this stream is off (a straggler), or it is not recorded.
        }
        $newSource = ($this->sources[$slot] ?? null) !== $source;
        $ms = $this->placeOnClock($slot, $source, $rtpTimestamp, 90000);

        [$out, $keyframe] = self::transformVideo($data, $newSource ? null : ($this->described[$slot]['codecId'] ?? null));
        if ($out === '') {
            return;
        }
        if (!isset($this->described[$slot]) || $newSource) {
            // Describe the track from its first keyframe: only a keyframe carries the picture size
            // (and the parameter sets), and a decoder can only start on one anyway. A new source
            // (the peer's camera turned off and on again, a screen share) is described afresh, and
            // its frames before that keyframe are useless.
            if (!$keyframe) {
                unset($this->sources[$slot]);
                $this->observer?->onKeyframeNeeded($this, $source);
                return;
            }
            [$codecId, $width, $height, $private] = self::describeVideo($data);
            $description = ['codecId' => $codecId, 'width' => $width, 'height' => $height, 'private' => $private];
            Logger::log('Incoming '.CallStream::NAMES[$slot]." track $source recorded as $codecId {$width}x{$height}", Logger::VERBOSE);
            if ($description !== ($this->described[$slot] ?? null)) {
                $this->described[$slot] = $description;
                [$out, $keyframe] = self::transformVideo($data, $codecId);
                $declared = $this->started ? $this->writer?->getVideoTrack($slot) : null;
                // A Matroska track cannot change codec. A new picture size (a camera turned off and
                // on again, a simulcast layer switch) is fine, though: decoders take it from the
                // bitstream, whose keyframes keep their parameter sets (see transformVideo()).
                if ($declared !== null && $declared['codecId'] !== $codecId) {
                    if ($this->series) {
                        // A new segment, with a track of the new codec.
                        $this->rollPending = true;
                    } else {
                        // The file is finished here.
                        Logger::log('The '.CallStream::NAMES[$slot].' stream of the recording '.($this->file?->file ?? 'stream')." changed codec from {$declared['codecId']} to $codecId: finishing the file", Logger::NOTICE);
                        $this->end();
                        return;
                    }
                }
            }
        }
        $this->sources[$slot] = $source;
        $this->handleFrame($slot, $out, $ms, $keyframe);
    }

    /**
     * Whether a slot is recorded at all: any, unless a fixed set of streams excludes it.
     *
     * @psalm-mutation-free
     */
    private function wanted(int $slot): bool
    {
        return $this->streams === null || ($this->streams & $slot) !== 0;
    }

    /**
     * Map a frame's RTP timestamp onto the recording's continuous millisecond clock.
     */
    private function placeOnClock(int $slot, int $source, int $rtpTimestamp, int $clockRate): int
    {
        if (($this->sources[$slot] ?? null) !== $source || !isset($this->baseTs[$slot])) {
            // A new source starts where the recording currently is, not at its own random RTP time.
            $this->baseTs[$slot] = $rtpTimestamp;
            $this->offsetMs[$slot] = $this->positionMs();
            $this->sources[$slot] = $source;
        }
        $ms = $this->offsetMs[$slot] + (int) ((($rtpTimestamp - $this->baseTs[$slot]) & 0xFFFFFFFF) * 1000 / $clockRate);
        $this->lastMs[$slot] = $ms;
        return $ms;
    }

    /**
     * How far the recording has got, in milliseconds: the latest timestamp of any flowing stream.
     */
    private function positionMs(): int
    {
        return $this->lastMs === [] ? 0 : max($this->lastMs);
    }

    /**
     * Write a frame of a slot into the current file, opening (or, in series mode, rolling) it as needed.
     */
    private function handleFrame(int $slot, string $data, int $ms, bool $keyframe): void
    {
        if (!isset($this->seenSlots[$slot])) {
            $this->seenSlots[$slot] = true;
            Logger::log('First '.CallStream::NAMES[$slot].' frame of the recording '.($this->file?->file ?? 'stream')." at {$ms}ms; expected streams ".json_encode($this->expected), Logger::VERBOSE);
        }
        if ($this->expected[$slot] === false) {
            // Signaling says this stream is off: a straggler, or a stream that came back before
            // its state did â€” either way it is not written until the state says so.
            return;
        }
        if ($this->started) {
            if ($this->rollPending) {
                $this->maybeRoll();
            }
            if ($this->writer !== null && $this->inSegment($slot)) {
                $this->write($slot, $data, $ms, $keyframe);
                return;
            }
            if (!$this->series) {
                // The file has no track for this stream: it is not part of the recording.
                return;
            }
            if (!$this->rollPending) {
                // The segment lacks this stream: it just (re)appeared, so a new segment is due.
                $this->rollPending = true;
                $this->waitingSince = microtime(true);
                $this->maybeRoll();
                if ($this->started && $this->writer !== null && $this->inSegment($slot)) {
                    $this->write($slot, $data, $ms, $keyframe);
                    return;
                }
            }
            // Keep it for the segment about to open.
            $this->buffers[$slot][] = ['data' => $data, 'ms' => $ms, 'keyframe' => $keyframe];
            return;
        }
        $this->buffers[$slot][] = ['data' => $data, 'ms' => $ms, 'keyframe' => $keyframe];
        $this->waitingSince ??= microtime(true);
        $this->maybeBegin();
    }

    /**
     * Whether the current file declares a track for a slot.
     */
    private function inSegment(int $slot): bool
    {
        if ($this->writer === null) {
            return false;
        }
        return $slot === CallStream::AUDIO ? $this->writer->hasAudioTrack() : $this->writer->hasVideoTrack($slot);
    }

    private function write(int $slot, string $data, int $ms, bool $keyframe): void
    {
        \assert($this->writer !== null);
        if ($slot === CallStream::AUDIO) {
            $this->writer->writeAudio($data, $ms);
        } else {
            $this->writer->writeVideo($data, $ms, $keyframe, $slot);
        }
    }

    /**
     * The streams a file opened now would carry: the ones not reported off, that have delivered
     * something (audio) or been described (video).
     *
     * @return list<int>
     */
    private function flowingSlots(): array
    {
        $slots = [];
        foreach (self::SLOTS as $slot) {
            if ($this->expected[$slot] === false) {
                continue;
            }
            $seen = $slot === CallStream::AUDIO ? (isset($this->sources[$slot]) || ($this->buffers[$slot] ?? []) !== []) : isset($this->described[$slot]);
            if ($seen) {
                $slots[] = $slot;
            }
        }
        return $slots;
    }

    /**
     * Fixed mode with a chosen set of streams: the tracks the file can declare right now â€” the
     * audio track needs nothing, a video track needs its first keyframe.
     *
     * @return list<int>
     *
     * @psalm-mutation-free
     */
    private function chosenSlots(): array
    {
        \assert($this->streams !== null);
        $slots = [];
        foreach (self::SLOTS as $slot) {
            if (!$this->wanted($slot)) {
                continue;
            }
            if ($slot === CallStream::AUDIO || isset($this->described[$slot])) {
                $slots[] = $slot;
            }
        }
        return $slots;
    }

    /**
     * Whether some stream to be recorded has not delivered its first (key)frame yet: one reported on
     * (or, with a chosen set, one chosen and not reported off).
     */
    private function awaitingExpected(): bool
    {
        foreach (self::SLOTS as $slot) {
            if ($this->streams !== null) {
                if (!$this->wanted($slot) || $this->expected[$slot] === false || $slot === CallStream::AUDIO) {
                    continue;
                }
            } elseif ($this->expected[$slot] !== true) {
                continue;
            }
            $seen = $slot === CallStream::AUDIO ? (isset($this->sources[$slot]) || ($this->buffers[$slot] ?? []) !== []) : isset($this->described[$slot]);
            if (!$seen) {
                return true;
            }
        }
        return false;
    }

    private function graceExpired(): bool
    {
        return $this->waitingSince !== null && microtime(true) - $this->waitingSince > self::GRACE;
    }

    /**
     * Commit the header of the (first) file once every expected stream is in, or the grace period
     * for the missing ones has passed.
     */
    private function maybeBegin(): void
    {
        if ($this->started || $this->closed) {
            return;
        }
        if ($this->awaitingExpected() && !$this->graceExpired()) {
            return;
        }
        if ($this->streams !== null) {
            $slots = $this->chosenSlots();
            foreach (self::VIDEO_SLOTS as $slot) {
                if ($this->wanted($slot) && !\in_array($slot, $slots, true)) {
                    Logger::log('The '.CallStream::NAMES[$slot].' stream did not start in time: the recording '.($this->file?->file ?? 'stream').' is opened without it', Logger::WARNING);
                }
            }
        } else {
            $slots = $this->flowingSlots();
        }
        if ($slots === []) {
            return;
        }
        $this->openSegment($slots);
    }

    /**
     * Series mode: move to a new segment if the flowing streams no longer match the open one â€” once
     * every stream that was reported on has arrived, or the grace period for it has passed, so that
     * a stream swapped for another (the camera for a screen share) yields one new segment, not two.
     */
    private function maybeRoll(): void
    {
        if (!$this->series || !$this->started || $this->closed || $this->writer === null) {
            return;
        }
        if ($this->awaitingExpected() && !$this->graceExpired()) {
            return;
        }
        $wanted = $this->flowingSlots();
        $current = [];
        foreach (self::SLOTS as $slot) {
            if ($this->inSegment($slot)) {
                $current[] = $slot;
            }
        }
        $same = $wanted === $current;
        foreach (self::VIDEO_SLOTS as $slot) {
            if ($same && isset($this->described[$slot]) && \in_array($slot, $wanted, true)
                && ($this->writer->getVideoTrack($slot)['codecId'] ?? null) !== $this->described[$slot]['codecId']
            ) {
                $same = false;
            }
        }
        $this->rollPending = false;
        if ($same) {
            return;
        }
        if ($wanted === []) {
            // Everything stopped: close the segment; the next one opens when something arrives.
            $this->closeWriter();
            $this->started = false;
            $this->waitingSince = null;
            return;
        }
        $this->openSegment($wanted);
    }

    /**
     * Open a file declaring the given slots, and flush what was buffered for them.
     *
     * @param list<int> $slots
     */
    private function openSegment(array $slots): void
    {
        $this->closeWriter();
        $out = $this->out;
        if ($this->series && $this->file !== null) {
            if ($this->segment === 0) {
                // Continue an existing series (after a re-join, or a restart of the process) rather
                // than overwriting its first segment.
                $this->segment = $this->nextSegmentNumber();
            }
            $out = new LocalFile($this->segmentPath($slots));
            $this->segment++;
        }
        $this->started = true;
        $this->waitingSince = null;
        $this->rollPending = false;
        $this->writer = new MatroskaWriter($out, $this->format->docType());
        $this->currentOut = $out;
        if (\in_array(CallStream::AUDIO, $slots, true)) {
            $this->writer->setAudioTrack('A_OPUS', 48000, 2, self::opusHead(2, 48000));
        }
        foreach (self::VIDEO_SLOTS as $slot) {
            if (\in_array($slot, $slots, true) && isset($this->described[$slot])) {
                $d = $this->described[$slot];
                $this->writer->setVideoTrack($d['codecId'], $d['width'], $d['height'], $d['private'], $slot);
            }
        }
        Logger::log('Recording '.($out instanceof LocalFile ? $out->file : 'stream').' with '.CallStream::describe(array_sum($slots)), Logger::VERBOSE);
        $this->writer->start();

        // Merge the buffers in timestamp order so the first cluster is well formed; buffered frames
        // of a stream not in this file are dropped (it is gone, or waits for the next segment).
        $merged = [];
        foreach ($this->buffers as $slot => $frames) {
            if (!\in_array($slot, $slots, true)) {
                continue;
            }
            foreach ($frames as $f) {
                $merged[] = ['slot' => $slot, 'ms' => $f['ms'], 'data' => $f['data'], 'keyframe' => $f['keyframe']];
            }
        }
        usort($merged, static fn ($a, $b) => $a['ms'] <=> $b['ms']);
        foreach ($merged as $f) {
            $this->write($f['slot'], $f['data'], $f['ms'], $f['keyframe']);
        }
        $this->buffers = [];
        $this->observer?->onRecordingStarted($this, $out);
    }

    /**
     * Finish the open file, if any, and tell the observer.
     */
    private function closeWriter(): void
    {
        if ($this->writer === null) {
            return;
        }
        $out = $this->currentOut;
        $this->writer->close();
        $this->writer = null;
        $this->currentOut = null;
        if ($out !== null) {
            $this->observer?->onRecordingEnded($this, $out);
        }
    }

    /**
     * Finish the recording for good: the file (if open) is closed and no more frames are accepted.
     */
    private function end(): void
    {
        if ($this->closed) {
            return;
        }
        $this->closed = true;
        $this->buffers = [];
        $this->closeWriter();
    }

    /**
     * Series mode: the number the next segment of this series should get: one past the highest-numbered
     * segment already on disk (so that a recording resumed after a re-join or a restart appends to the
     * series instead of overwriting it), or 0 if there is none (yet, or any more).
     */
    private function nextSegmentNumber(): int
    {
        $directoryStem = str_ends_with($this->stem, '/');
        $dir = $directoryStem ? rtrim($this->stem, '/') : \dirname($this->stem);
        $prefix = $directoryStem ? '' : basename($this->stem).'.';
        $suffix = '.'.($this->format === RecordingFormat::Webm ? 'webm' : 'mkv');
        $next = 0;
        foreach (is_dir($dir) ? (scandir($dir) ?: []) : [] as $name) {
            if (!str_starts_with($name, $prefix) || !str_ends_with($name, $suffix)) {
                continue;
            }
            if (preg_match('/^(\d+)_/', substr($name, \strlen($prefix)), $m)) {
                $next = max($next, (int) $m[1] + 1);
            }
        }
        return $next;
    }

    /**
     * Series mode: the path of the next segment: `<stem>.<n>_<streams>.<ext>`, or `<stem><n>_<streams>.<ext>`
     * for a directory stem.
     *
     * @param list<int> $slots
     */
    private function segmentPath(array $slots): string
    {
        $separator = str_ends_with($this->stem, '/') ? '' : '.';
        $extension = $this->format === RecordingFormat::Webm ? 'webm' : 'mkv';
        return $this->stem.$separator.$this->segment.'_'.implode(',', CallStream::names(array_sum($slots))).'.'.$extension;
    }

    public function close(): void
    {
        $this->end();
    }

    /* ----------------------------------------------------------------- *
     *  Codec inspection (pure PHP, from the frame bytes).
     * ----------------------------------------------------------------- */

    /**
     * Identify the video codec of a keyframe and its picture size and codec-private data.
     *
     * @return array{string, int, int, string} [Matroska CodecID, width, height, CodecPrivate]
     *
     * @psalm-mutation-free
     */
    public static function describeVideo(string $frame): array
    {
        // H.264 and H.265 Annex-B always begin with a start code; the parameter sets tell them apart.
        if (str_starts_with($frame, "\x00\x00\x00\x01") || str_starts_with($frame, "\x00\x00\x01")) {
            if (HevcBitstream::isHevc($frame)) {
                [$w, $h, $hvcc] = HevcBitstream::describe($frame);
                return ['V_MPEGH/ISO/HEVC', $w, $h, $hvcc];
            }
            [$w, $h, $avcc] = self::h264Config($frame);
            return ['V_MPEG4/ISO/AVC', $w, $h, $avcc];
        }
        $first = \ord($frame[0]);
        // VP8 keyframe carries the 0x9d 0x01 0x2a start code and the dimensions (checked before
        // AV1: the first byte of a VP8 keyframe can look like an AV1 OBU header).
        if (\strlen($frame) > 9 && substr($frame, 3, 3) === "\x9d\x01\x2a") {
            $w = (\ord($frame[6]) | (\ord($frame[7]) << 8)) & 0x3FFF;
            $h = (\ord($frame[8]) | (\ord($frame[9]) << 8)) & 0x3FFF;
            return ['V_VP8', $w, $h, ''];
        }
        // AV1: an OBU header has the forbidden bit clear and a small type; a temporal unit usually
        // starts with a temporal delimiter (type 2) or sequence header (type 1).
        $obuType = ($first >> 3) & 0x0F;
        if (($first & 0x80) === 0 && ($obuType === 1 || $obuType === 2 || $obuType === 6)) {
            [$w, $h, $av1c] = Av1Bitstream::describe($frame);
            return ['V_AV1', $w, $h, $av1c];
        }
        // VP9: the uncompressed header begins with the frame marker 0b10.
        if (($first >> 6) === 0x02) {
            [$w, $h] = self::vp9Size($frame);
            return ['V_VP9', $w, $h, ''];
        }
        // Unknown: default to VP8 with a common size so the file is still valid.
        return ['V_VP8', 1280, 720, ''];
    }

    /**
     * Prepare a frame for muxing and report whether it is a keyframe.
     *
     * @param ?string $codecId The Matroska codec of the track, when already known.
     * @return array{string, bool} [frame bytes to store, is keyframe]
     *
     * @psalm-mutation-free
     */
    public static function transformVideo(string $frame, ?string $codecId): array
    {
        $first = \ord($frame[0]);
        // Once the track is described, its codec is known; the bitstream sniffing below is only
        // needed for the first keyframe (an inter frame of one codec can look like another's).
        switch ($codecId) {
            case 'V_MPEGH/ISO/HEVC':
                return [HevcBitstream::toLengthPrefixed($frame), HevcBitstream::isKeyframe($frame)];
            case 'V_MPEG4/ISO/AVC':
                return self::h264ToAvcc($frame);
            case 'V_AV1':
                return [$frame, Av1Bitstream::isKeyframe($frame)];
            case 'V_VP9':
                return [$frame, self::vp9IsKeyframe($frame)];
            case 'V_VP8':
                return [$frame, ($first & 0x01) === 0];
        }
        if (str_starts_with($frame, "\x00\x00\x00\x01") || str_starts_with($frame, "\x00\x00\x01")) {
            if (HevcBitstream::isHevc($frame)) {
                return [HevcBitstream::toLengthPrefixed($frame), HevcBitstream::isKeyframe($frame)];
            }
            return self::h264ToAvcc($frame); // [avcc bytes, keyframe]
        }
        // VP8 keyframe = low bit of the first byte clear.
        if (\strlen($frame) > 9 && substr($frame, 3, 3) === "\x9d\x01\x2a") {
            return [$frame, true];
        }
        // AV1: a temporal unit that carries a sequence header (type 1) starts a new sequence.
        $obuType = ($first >> 3) & 0x0F;
        if (($first & 0x80) === 0 && ($obuType === 1 || $obuType === 2 || $obuType === 6)) {
            return [$frame, Av1Bitstream::isKeyframe($frame)];
        }
        // VP9 keyframe: frame_type bit is 0 (parsed in vp9IsKeyframe).
        if (($first >> 6) === 0x02) {
            return [$frame, self::vp9IsKeyframe($frame)];
        }
        // VP8 fallback.
        return [$frame, (\ord($frame[0]) & 0x01) === 0];
    }

    /* ---- H.264 ---- */

    /**
     * @return list<string> NAL units (without start codes).
     *
     * @psalm-pure
     */
    private static function h264Nals(string $frame): array
    {
        $nals = [];
        $len = \strlen($frame);
        $i = 0;
        $start = -1;
        while ($i < $len) {
            // Find a start code.
            if ($i + 3 <= $len && $frame[$i] === "\x00" && $frame[$i + 1] === "\x00" && $frame[$i + 2] === "\x01") {
                if ($start >= 0) {
                    $end = $i;
                    // Trim a trailing zero belonging to the next 4-byte start code.
                    if ($end > $start && $frame[$end - 1] === "\x00") {
                        $end--;
                    }
                    $nals[] = substr($frame, $start, $end - $start);
                }
                $i += 3;
                $start = $i;
                continue;
            }
            $i++;
        }
        if ($start >= 0 && $start < $len) {
            $nals[] = substr($frame, $start);
        }
        return $nals;
    }

    /**
     * @return array{int, int, string} [width, height, AVCDecoderConfigurationRecord]
     *
     * @psalm-mutation-free
     */
    private static function h264Config(string $frame): array
    {
        $sps = '';
        $pps = '';
        foreach (self::h264Nals($frame) as $nal) {
            if ($nal === '') {
                continue;
            }
            $type = \ord($nal[0]) & 0x1F;
            if ($type === 7 && $sps === '') {
                $sps = $nal;
            } elseif ($type === 8 && $pps === '') {
                $pps = $nal;
            }
        }
        [$w, $h] = $sps !== '' ? self::h264SpsSize($sps) : [1280, 720];
        $avcc = '';
        if ($sps !== '' && \strlen($sps) >= 4) {
            $avcc = "\x01".$sps[1].$sps[2].$sps[3]."\xFF"
                ."\xE1".pack('n', \strlen($sps)).$sps
                ."\x01".pack('n', \strlen($pps)).$pps;
        }
        return [$w, $h, $avcc];
    }

    /**
     * @return array{string, bool} [AVCC length-prefixed frame, keyframe]
     *
     * @psalm-pure
     */
    private static function h264ToAvcc(string $frame): array
    {
        $out = '';
        $keyframe = false;
        foreach (self::h264Nals($frame) as $nal) {
            if ($nal === '') {
                continue;
            }
            $type = \ord($nal[0]) & 0x1F;
            if ($type === 5 || $type === 7) {
                $keyframe = true;
            }
            if ($type === 9) {
                continue; // Access unit delimiters are not stored.
            }
            $out .= pack('N', \strlen($nal)).$nal;
        }
        return [$out, $keyframe];
    }

    /**
     * Parse width/height out of an H.264 SPS via Exp-Golomb decoding.
     *
     * @return array{int, int}
     *
     * @psalm-mutation-free
     */
    private static function h264SpsSize(string $sps): array
    {
        $rbsp = self::stripEmulationPrevention(substr($sps, 1)); // drop the NAL header byte
        $br = new BitReader($rbsp);
        $profileIdc = $br->bits(8);
        $br->bits(8); // constraint flags + reserved
        $br->bits(8); // level_idc
        $br->ue();    // seq_parameter_set_id
        if (\in_array($profileIdc, [100, 110, 122, 244, 44, 83, 86, 118, 128, 138, 139, 134, 135], true)) {
            $chroma = $br->ue();
            if ($chroma === 3) {
                $br->bits(1); // separate_colour_plane_flag
            }
            $br->ue(); // bit_depth_luma_minus8
            $br->ue(); // bit_depth_chroma_minus8
            $br->bits(1); // qpprime_y_zero_transform_bypass_flag
            if ($br->bits(1)) { // seq_scaling_matrix_present_flag
                $count = $chroma !== 3 ? 8 : 12;
                for ($i = 0; $i < $count; $i++) {
                    if ($br->bits(1)) {
                        $size = $i < 6 ? 16 : 64;
                        $last = 8;
                        $next = 8;
                        for ($j = 0; $j < $size; $j++) {
                            if ($next !== 0) {
                                $delta = $br->se();
                                $next = ($last + $delta + 256) % 256;
                            }
                            $last = $next === 0 ? $last : $next;
                        }
                    }
                }
            }
        }
        $br->ue(); // log2_max_frame_num_minus4
        $picOrderCnt = $br->ue();
        if ($picOrderCnt === 0) {
            $br->ue(); // log2_max_pic_order_cnt_lsb_minus4
        } elseif ($picOrderCnt === 1) {
            $br->bits(1);
            $br->se();
            $br->se();
            $num = $br->ue();
            for ($i = 0; $i < $num; $i++) {
                $br->se();
            }
        }
        $br->ue(); // max_num_ref_frames
        $br->bits(1); // gaps_in_frame_num_value_allowed_flag
        $widthMbs = $br->ue() + 1;
        $heightMapUnits = $br->ue() + 1;
        $frameMbsOnly = $br->bits(1);
        if (!$frameMbsOnly) {
            $br->bits(1); // mb_adaptive_frame_field_flag
        }
        $br->bits(1); // direct_8x8_inference_flag
        $cropLeft = $cropRight = $cropTop = $cropBottom = 0;
        if ($br->bits(1)) { // frame_cropping_flag
            $cropLeft = $br->ue();
            $cropRight = $br->ue();
            $cropTop = $br->ue();
            $cropBottom = $br->ue();
        }
        $width = $widthMbs * 16 - ($cropLeft + $cropRight) * 2;
        $height = (2 - $frameMbsOnly) * $heightMapUnits * 16 - ($cropTop + $cropBottom) * 2;
        return [max(1, $width), max(1, $height)];
    }

    /**
     * @psalm-pure
     */
    private static function stripEmulationPrevention(string $data): string
    {
        // Remove 0x03 in 0x00 0x00 0x03 sequences (RBSP anti-emulation).
        return preg_replace('/\x00\x00\x03/', "\x00\x00", $data) ?? $data;
    }

    /* ---- VP9 ---- */

    /**
     * @return array{int, int}
     *
     * @psalm-mutation-free
     */
    private static function vp9Size(string $frame): array
    {
        $br = new BitReader($frame);
        $br->bits(2); // frame_marker
        $profile = $br->bits(1) | ($br->bits(1) << 1);
        if ($profile === 3) {
            $br->bits(1);
        }
        if ($br->bits(1)) { // show_existing_frame
            return [1280, 720];
        }
        $frameType = $br->bits(1);
        $br->bits(1); // show_frame
        $br->bits(1); // error_resilient_mode
        if ($frameType !== 0) {
            return [1280, 720]; // inter frame carries no size here
        }
        $br->bits(24); // sync code
        // color_config (profile 0, 8-bit): color_space(3); if != CS_RGB: color_range(1), subsampling handled by profile
        $br->bits(3); // color_space
        $br->bits(1); // color_range
        $width = $br->bits(16) + 1;
        $height = $br->bits(16) + 1;
        return [$width, $height];
    }

    /**
     * @psalm-mutation-free
     */
    private static function vp9IsKeyframe(string $frame): bool
    {
        $br = new BitReader($frame);
        $br->bits(2);
        $profile = $br->bits(1) | ($br->bits(1) << 1);
        if ($profile === 3) {
            $br->bits(1);
        }
        if ($br->bits(1)) {
            return false; // show_existing_frame
        }
        return $br->bits(1) === 0; // frame_type 0 = key
    }

    /* ---- OPUS ---- */

    /**
     * @psalm-pure
     */
    private static function opusHead(int $channels, int $sampleRate): string
    {
        return 'OpusHead'.pack('CCvVvC', 1, $channels, 312, $sampleRate, 0, 0);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

// IMPORTANT NOTE: Please keep the above copyright notice intact if copying or rewriting this file in another language.

namespace danog\MadelineProto\Tgcalls;

use Amp\ByteStream\WritableStream;
use danog\MadelineProto\CallStream;
use danog\MadelineProto\Exception;
use danog\MadelineProto\LocalFile;
use danog\MadelineProto\Logger;
use danog\MadelineProto\Loop\VoIP\DjLoop;
use danog\MadelineProto\RecordingEvent;
use danog\MadelineProto\RecordingFormat;
use danog\MadelineProto\RestoresDeclaredProperties;
use danog\MadelineProto\Tgcalls\E2E\FrameCryptor;
use Throwable;
use Webrtc\DataChannel\Enum\State;
use Webrtc\DataChannel\Listener\DataChannelMessageListener;
use Webrtc\DataChannel\Listener\DataChannelOpenListener;
use Webrtc\DataChannel\RTCDataChannel;
use Webrtc\DataChannel\RTCDataChannelParameters;
use Webrtc\DTLS\DTLS\RTCDtlsTransport;
use Webrtc\RTP\Crypto\FrameCryptorInterface;
use Webrtc\RTP\Enum\MediaKind;
use Webrtc\RTP\MediaStreamTrack\MediaStreamTrack;
use Webrtc\RTP\MediaStreamTrack\RemoteStreamTrack;
use Webrtc\RTP\RTCRtpTransceiver;
use Webrtc\SDP\Enum\SDPDirections;
use Webrtc\SDP\RTCSessionDescription;
use Webrtc\Webrtc\Enum\ConnectionState;
use Webrtc\Webrtc\Listener\PeerConnectionConnectionStateChangeListener;
use Webrtc\Webrtc\Listener\PeerConnectionTrackListener;
use Webrtc\Webrtc\RTCPeerConnection;

/**
 * WebRTC engine of a Telegram group call.
 *
 * @psalm-import-type StreamMask from CallStream
 *
 * The Telegram SFU does not exchange SDP: the client sends a small JSON join payload describing its
 * ICE/DTLS parameters and outgoing audio SSRC to
 * [phone.joinGroupCall](https://core.telegram.org/method/phone.joinGroupCall), and gets back the
 * SFU's transport parameters in
 * [updateGroupCallConnection](https://core.telegram.org/constructor/updateGroupCallConnection).
 * All media is bundled over that single transport; every participant is identified by their
 * [groupCallParticipant](https://core.telegram.org/constructor/groupCallParticipant).`source` SSRC.
 *
 * Since the SFU never renegotiates, new participants are wired in with a purely local
 * offer/answer round: {@see GroupSdp} rebuilds the SFU's answer from the same transport parameters,
 * adding one receive-only m-line per participant.
 *
 * @internal
 */
final class GroupConnection implements VideoCodecObserver, PeerConnectionTrackListener, PeerConnectionConnectionStateChangeListener, DataChannelOpenListener, DataChannelMessageListener, IncomingMediaObserver
{
    use RestoresDeclaredProperties;
    /** Colibri video-quality tiers, in pixels of height: thumbnail, medium and full. */
    private const HEIGHT_THUMBNAIL = 180;
    private const HEIGHT_MEDIUM = 360;
    private const HEIGHT_FULL = 720;

    private RTCPeerConnection $peerConnection;
    private OpusPlaybackTrack $outgoingAudio;
    private VideoPlaybackTrack $outgoingVideo;

    /** Our own outgoing audio SSRC, as an unsigned 32-bit integer. */
    private int $audioSsrc;
    /** Our own outgoing video SSRC, as an unsigned 32-bit integer. */
    private int $videoSsrc;
    /** Retransmission SSRC paired with the video one, as tgcalls always declares a FID group. */
    private int $videoRtxSsrc;

    /** Transport parameters returned by the SFU. */
    private ?array $transport = null;
    /** Video codec table announced by the SFU, if it sent one. */
    private ?array $video = null;
    /**
     * The encoding name our outgoing video m-line is currently pinned to.
     *
     * The track passes frames through from the container instead of encoding them, so this has to
     * follow whatever the file being played holds, and the transport is renegotiated when it
     * changes. VP8 until a file says otherwise, since that is what the SFU lists first.
     */
    private string $outgoingVideoCodec = 'VP8';
    /** SDP fmtp parameters of the outgoing video file, derived from its bitstream (profile/level/â€¦). */
    private array $outgoingVideoParameters = [];

    /**
     * Remote SSRCs currently wired to a receive-only transceiver, as `mid => ssrc` (audio and video
     * alike; the SSRC's kind follows the transceiver it maps to).
     *
     * @var array<string, int>
     */
    private array $sources = [];
    /**
     * Remote SSRCs, in the order their receive-only transceivers were created, so the mids the local
     * stack assigns can be matched back to them.
     *
     * @var list<int>
     */
    private array $orderedSources = [];
    /**
     * Which participant (by unsigned audio SSRC) each remote camera-video SSRC belongs to, so an
     * incoming video track is muxed into that participant's recorder.
     *
     * @var array<int, int>
     */
    private array $videoOwner = [];
    /**
     * Which participant (by unsigned audio SSRC) each remote screen-share SSRC belongs to, so an
     * incoming presentation track is routed to that participant's separate presentation recorder.
     *
     * @var array<int, int>
     */
    private array $presentationOwner = [];
    /**
     * The incoming media of every participant, by unsigned audio SSRC: what they send (as the
     * participant list reports it), in which codecs, and the recorder their frames go to. Every
     * remote track is drained through these, whether or not it is recorded.
     *
     * @var array<int, IncomingMedia>
     */
    private array $media = [];

    /**
     * The incoming media router of a participant (by unsigned audio SSRC), created on first use.
     *
     * @psalm-external-mutation-free
     */
    private function ensureMedia(int $audioSsrc): IncomingMedia
    {
        return $this->media[$audioSsrc] ??= new IncomingMedia($this);
    }

    /**
     * The colibri data channel: the client sends {@see ReceiverVideoConstraints} on it to tell the
     * SFU which participants' video to forward, and receives quality hints on it.
     */
    private ?RTCDataChannel $dataChannel = null;
    private bool $dataChannelOpen = false;
    /**
     * The SFU endpoints whose video we want forwarded, `endpoint => desired maximum height`. The SFU
     * forwards a participant's video only once we subscribe to their endpoint here.
     *
     * @var array<string, int>
     */
    private array $wantedEndpoints = [];

    /** End-to-end frame cryptor for an encrypted conference; null for an ordinary (SFU-trusted) call. */
    private ?FrameCryptorInterface $frameCryptor = null;

    /**
     * What we last told the server through this connection: whether our video is stopped (or, for the
     * screen-share, paused); null if nothing yet, or if the server did not take it.
     */
    private ?bool $videoStopped = null;

    private bool $closed = false;
    private bool $renegotiating = false;
    private bool $renegotiatePending = false;

    public function __construct(
        private readonly GroupConnectionOwner $call,
        DjLoop $dj,
        /**
         * Whether this is the separate screen-share (presentation) connection created by
         * phone.joinGroupCallPresentation, rather than the main camera/audio one. It transmits only,
         * never receives, and its video-state changes toggle the presentation, not the camera.
         */
        private readonly bool $screencast = false,
    ) {
        $this->peerConnection = new RTCPeerConnection(['iceServers' => []]);
        if (getenv('MP_RTC_DEBUG') === '1') {
            // Route php-rtc's own debug output (DTLS, SRTP, SCTP, RTP routing) into the call log.
            $this->peerConnection->setLogger(new RtcDebugLogger($call));
        }
        $dj->setVideoCodecObserver($this);
        $this->outgoingAudio = new OpusPlaybackTrack($dj, $call);
        $transceiver = $this->peerConnection->addTransceiver($this->outgoingAudio, SDPDirections::sendonly);
        $this->audioSsrc = $transceiver->getSender()->getSsrc();
        $this->outgoingVideo = new VideoPlaybackTrack($dj, $call);
        $videoTransceiver = $this->peerConnection->addTransceiver($this->outgoingVideo, SDPDirections::sendonly);
        $this->videoSsrc = $videoTransceiver->getSender()->getSsrc();
        $this->videoRtxSsrc = $videoTransceiver->getSender()->getRtxSsrc();

        // The colibri data channel, over which we subscribe to other participants' video (the SFU
        // forwards a camera or screen-share stream only for endpoints we ask for). tgcalls runs it
        // as an SCTP association straight over the media DTLS transport, with no SDP section: the
        // client always initiates it (although it is the ICE-controlled side) and opens the channel
        // as stream 0 â€” see GroupNetworkManager.cpp and SctpDataChannelProviderInterfaceImpl.cpp.
        // A screen-share-only connection subscribes to nothing, so it needs no channel.
        if (!$this->screencast) {
            $this->peerConnection->createInbandSctp(client: true);
            $this->dataChannel = $this->peerConnection->createDataChannel(new RTCDataChannelParameters(ordered: true, id: 0));
            $this->dataChannel->addOpenListener($this);
            $this->dataChannel->addMessageListener($this);
        }

        // This object is registered as a typed listener, not a closure, so it is part of the peer
        // connection's serializable state and keeps pointing at this restored connection after a
        // serialize/unserialize cycle (a Closure could not be serialized).
        $this->peerConnection->addTrackListener($this);
        $this->peerConnection->addConnectionStateChangeListener($this);
    }

    /**
     * Enable end-to-end frame encryption on this connection (for an encrypted conference): every
     * outgoing frame is encrypted and every incoming frame decrypted by the cryptor. Applies to all
     * current transceivers and to any created afterwards. Pass null to disable.
     */
    public function setFrameCryptor(?FrameCryptorInterface $frameCryptor): void
    {
        $this->frameCryptor = $frameCryptor;
        if ($frameCryptor instanceof FrameCryptor) {
            $frameCryptor->setOutgoingVideoCodec($this->outgoingVideoCodec);
        }
        foreach ($this->peerConnection->getTransceivers() as $transceiver) {
            $this->applyCryptor($transceiver);
        }
    }

    /**
     * Install the current frame cryptor (if any) on a transceiver's sender and receiver.
     */
    private function applyCryptor(RTCRtpTransceiver $transceiver): void
    {
        $transceiver->getSender()->setFrameCryptor($this->frameCryptor);
        $transceiver->getReceiver()->setFrameCryptor($this->frameCryptor);
    }

    /**
     * Drop the state that cannot be serialized before the graph is written out.
     *
     * The peer connection and its SFU transport serialize themselves and resume on the far side, and
     * so do the playback tracks and the WebM demuxer, and every participant's {@see IncomingMedia}
     * router (which keeps a file-backed recorder, reopened on wakeup, and drops a stream-backed one).
     *
     * @return array<string, mixed>
     *
     * @psalm-mutation-free
     */
    public function __serialize(): array
    {
        return get_object_vars($this);
    }

    /**
     * @param array<string, mixed> $data
     *
     * @psalm-external-mutation-free
     */
    public function __unserialize(array $data): void
    {
        $this->restoreDeclaredProperties($data);
    }

    /**
     * Resume recording after the whole call graph has been deserialized: every participant's router
     * reopens its (file-backed) recorder, and the live tracks are re-attached to them. Must run only once the peer connection has been fully restored, so it
     * is driven from {@see \danog\MadelineProto\GroupCall\GroupCallController}'s resume path, never during
     * unserialize (reopening a file suspends the fiber).
     */
    public function resume(): void
    {
        if ($this->closed) {
            return;
        }
        // The outgoing playback producers were left dormant by deserialization: restart them, or
        // nothing is transmitted after a restart (the demuxer feeding them is restarted by the call).
        $this->outgoingAudio->resume();
        $this->outgoingVideo->resume();
        if (getenv('MP_RTC_DEBUG') === '1') {
            $this->peerConnection->setLogger(new RtcDebugLogger($this->call));
        }
        foreach ($this->media as $media) {
            $media->resume();
        }
        // Re-subscribe to remote video: the SCTP channel came back, so resend the constraints in case
        // it did not fire its open event again (it may already be open, or reopen shortly).
        $this->dataChannelOpen = $this->dataChannel?->getReadyState() === State::Open;
        $this->sendReceiverConstraints();
        // Every live track is drained by its participant's router: re-attach them, since the `track`
        // event will not fire again for them.
        foreach ($this->peerConnection->getReceivers() as $receiver) {
            $track = $receiver->getTrack();
            if (!$track instanceof RemoteStreamTrack) {
                continue;
            }
            $ssrc = $this->trackSsrc($track);
            if ($ssrc !== null) {
                $this->attachTrack($track, $ssrc);
            }
        }
    }

    /**
     * React to a change of the WebRTC connection state.
     *
     * @internal Registered as the peer connection's connection-state-change listener.
     */
    #[\Override]
    public function onPeerConnectionConnectionStateChange(): void
    {
        $state = $this->peerConnection->getConnectionState();
        $this->call->log("WebRTC connection state of {$this->call} is now {$state->name}");
        if ($state === ConnectionState::failed) {
            $this->call->onConnectionFailed();
        }
    }

    /**
     * Our own outgoing audio SSRC, in the signed form used by the API.
     *
     * @psalm-mutation-free
     */
    public function getAudioSource(): int
    {
        return GroupSdp::toSignedSsrc($this->audioSsrc);
    }

    /**
     * Generate the join payload for
     * [phone.joinGroupCall](https://core.telegram.org/method/phone.joinGroupCall).
     */
    public function buildJoinPayload(): array
    {
        $description = $this->peerConnection->createOffer();
        $this->peerConnection->setLocalDescription($description);

        $transceiver = $this->peerConnection->getTransceivers()[0]
            ?? throw new Exception('No local transceiver was created!');
        $dtls = $transceiver->getDtlsTransport();
        if (!$dtls instanceof RTCDtlsTransport) {
            throw new Exception('The local transceiver has no DTLS transport!');
        }
        $ice = $dtls->getIceTransport()->getIceGatherer()->getLocalParameters();

        $fingerprints = [];
        foreach ($dtls->getLocalParameters()->fingerprints as $fingerprint) {
            $fingerprints[] = [$fingerprint->algorithm, $fingerprint->value];
        }

        return GroupSdp::buildJoinPayload(
            (string) $ice->usernameFragment,
            (string) $ice->password,
            $fingerprints,
            $this->audioSsrc,
            // tgcalls declares one FID group per simulcast layer; we publish a single layer.
            [['semantics' => 'FID', 'ssrcs' => [$this->videoSsrc, $this->videoRtxSsrc]]],
        );
    }

    /**
     * Re-pin the outgoing video m-line when a newly opened file uses another codec, and tell the
     * server we are publishing video so the other participants display our stream.
     *
     * Any video codec a group call carries works: see {@see DjLoop::VIDEO_CODECS}.
     */
    #[\Override]
    public function onVideoCodec(string $codec, array $parameters = []): void
    {
        $this->setVideoStopped(false);
        if ($codec === $this->outgoingVideoCodec && $parameters === $this->outgoingVideoParameters) {
            return;
        }
        $this->call->log("Switching the outgoing video of {$this->call} to $codec");
        $this->outgoingVideoCodec = $codec;
        if ($this->frameCryptor instanceof FrameCryptor) {
            // How much of each frame stays in the clear depends on the codec (see FrameCryptor).
            $this->frameCryptor->setOutgoingVideoCodec($codec);
        }
        $this->outgoingVideoParameters = $parameters;
        $this->renegotiate();
    }

    /**
     * React to the demuxed file's video finishing: tell the server we are no longer publishing.
     */
    #[\Override]
    public function onVideoStopped(): void
    {
        $this->setVideoStopped(true);
    }

    /**
     * Tell the server whether we are publishing video, if that changed: the disk jockey reports the codec
     * every time a file starts, including every time a looping file restarts.
     *
     * The camera connection toggles whether its video is stopped; the screencast connection's publishing
     * state is set by joining the presentation, and only its paused flag toggles here.
     */
    private function setVideoStopped(bool $stopped): void
    {
        if ($this->videoStopped === $stopped) {
            return;
        }
        // Set before the request, so that a newer change made meanwhile is not overwritten by this one.
        $this->videoStopped = $stopped;
        $taken = $this->screencast
            ? $this->call->setPresentationPaused($stopped)
            : $this->call->setVideoStopped($stopped);
        if (!$taken && $this->videoStopped === $stopped) {
            // Not joined yet, or the request failed: send it again next time.
            $this->videoStopped = null;
        }
    }

    /**
     * Whether we are currently transmitting video.
     *
     * @psalm-mutation-free
     */
    public function isPlayingVideo(): bool
    {
        return $this->outgoingVideo->isPlaying();
    }

    /**
     * Apply the transport parameters returned by the SFU.
     *
     * @param ?array $video The video codec table the SFU announced, as parsed by
     *                      {@see GroupSdp::parseJoinResponse()}.
     */
    public function setTransport(array $transport, ?array $video = null): void
    {
        $this->transport = $transport;
        $this->video = $video;
        $this->renegotiate();
    }

    /**
     * Update the set of participants whose media we want to receive.
     *
     * Each participant contributes one audio SSRC (`groupCallParticipant.source`) and, if they are
     * transmitting a camera, one or more video SSRCs (`groupCallParticipant.video`). A receive-only
     * transceiver of the matching kind is created for every new SSRC; the SFU forwards that
     * participant's media on it, demultiplexed by SSRC.
     *
     * @param list<array{audio: int, muted?: bool, video: list<int>, presentation?: list<int>, videoEndpoint?: ?string, presentationEndpoint?: ?string}> $participants
     *        Signed SSRCs and SFU endpoints per participant.
     */
    public function setRemoteSources(array $participants): void
    {
        if ($this->transport === null || $this->closed) {
            return;
        }
        $known = array_flip($this->orderedSources);
        $added = false;
        $endpoints = [];
        $present = [];
        $newcomers = [];
        foreach ($participants as $participant) {
            $audio = GroupSdp::toUnsignedSsrc($participant['audio']);
            if ($audio === 0 || $audio === $this->audioSsrc) {
                continue;
            }
            if (!isset($known[$audio])) {
                $this->applyCryptor($this->peerConnection->addTransceiver(MediaKind::Audio, SDPDirections::recvonly));
                $this->orderedSources[] = $audio;
                $known[$audio] = true;
                $added = true;
            }
            $videoSsrcs = [];
            foreach ($participant['video'] as $video) {
                $unsigned = GroupSdp::toUnsignedSsrc($video);
                if ($unsigned === 0 || $unsigned === $this->videoSsrc) {
                    continue;
                }
                $videoSsrcs[] = $unsigned;
                $this->videoOwner[$unsigned] = $audio;
                $added = $this->wireReceiver($unsigned, $known) || $added;
            }
            foreach ($participant['presentation'] ?? [] as $presentation) {
                $unsigned = GroupSdp::toUnsignedSsrc($presentation);
                if ($unsigned === 0 || $unsigned === $this->videoSsrc) {
                    continue;
                }
                $this->presentationOwner[$unsigned] = $audio;
                $added = $this->wireReceiver($unsigned, $known) || $added;
            }
            // Collect the endpoints to subscribe to over the colibri data channel: the SFU forwards
            // a stream only for endpoints we ask for. We record at full quality.
            if ($videoSsrcs !== [] && ($participant['videoEndpoint'] ?? null) !== null) {
                $endpoints[$participant['videoEndpoint']] = self::HEIGHT_FULL;
            }
            if (($participant['presentation'] ?? []) !== [] && ($participant['presentationEndpoint'] ?? null) !== null) {
                $endpoints[$participant['presentationEndpoint']] = self::HEIGHT_FULL;
            }
            // What the participant is sending shapes its recording: a stream turned off stops being
            // written, one turned on is waited for (see CallRecorder).
            $streams = [
                'audio' => !(bool) ($participant['muted'] ?? false),
                'video' => $videoSsrcs !== [],
                'presentation' => ($participant['presentation'] ?? []) !== [],
            ];
            $this->call->log("Participant $audio of {$this->call} sends: ".json_encode($streams).', video sources '.json_encode($videoSsrcs).', presentation sources '.json_encode(array_map(GroupSdp::toUnsignedSsrc(...), (array) ($participant['presentation'] ?? []))), Logger::VERBOSE);
            $present[$audio] = true;
            if (!isset($this->media[$audio])) {
                // A participant seen for the first time (or back with the same SSRC after leaving,
                // whose router went with them): its tracks may already be live, since the SFU
                // never removes an m-line and the `track` event fires only once per transceiver.
                $newcomers[] = $audio;
            }
            $this->ensureMedia($audio)->setExpected($streams['audio'], $streams['video'], $streams['presentation']);
        }
        if ($newcomers !== []) {
            $this->attachLiveTracks(...$newcomers);
        }
        // A participant that left takes its media (and any recording of it) with it.
        foreach ($this->media as $ssrc => $media) {
            if (!isset($present[$ssrc])) {
                $this->call->log("Participant $ssrc left {$this->call}: finishing its recording, if any", Logger::VERBOSE);
                unset($this->media[$ssrc]);
                $media->close();
            }
        }
        // The SFU never removes m-lines, so we only ever add; nothing to do if none were missing.
        if ($added) {
            $this->renegotiate();
        }
        if ($endpoints !== $this->wantedEndpoints) {
            $this->wantedEndpoints = $endpoints;
            $this->sendReceiverConstraints();
        }
    }

    /**
     * Tell the SFU which participants' video to forward, over the colibri data channel. Full-quality
     * endpoints must also be listed as "on stage". Buffered until the channel opens.
     */
    private function sendReceiverConstraints(): void
    {
        if (!$this->dataChannelOpen || $this->closed) {
            return;
        }
        $constraints = [];
        $onStage = [];
        foreach ($this->wantedEndpoints as $endpoint => $height) {
            $constraints[$endpoint] = ['minHeight' => 0, 'maxHeight' => $height];
            if ($height >= self::HEIGHT_FULL) {
                $onStage[] = $endpoint;
            }
        }
        $message = [
            'colibriClass' => 'ReceiverVideoConstraints',
            'constraints' => (object) $constraints,
            'onStageEndpoints' => $onStage,
            'defaultConstraints' => ['maxHeight' => 0],
        ];
        try {
            $this->dataChannel?->send(json_encode($message, JSON_THROW_ON_ERROR));
        } catch (Throwable $e) {
            $this->call->log("Could not send video constraints on {$this->call}: $e", Logger::WARNING);
        }
    }

    /**
     * @internal The colibri data channel opened: flush any pending video subscription.
     */
    #[\Override]
    public function onDataChannelOpen(): void
    {
        $this->dataChannelOpen = true;
        $this->call->log("Colibri data channel of {$this->call} is open", Logger::VERBOSE);
        $this->sendReceiverConstraints();
    }

    /**
     * @internal The SFU sends encoder hints (SenderVideoConstraints) and diagnostics (DebugMessage)
     * on the data channel. We transmit a demuxed file rather than a live encoder, so there is nothing
     * to cap; the messages are logged for diagnostics only.
     */
    #[\Override]
    public function onDataChannelMessage(string $data): void
    {
        $this->call->log("Colibri message on {$this->call}: $data", Logger::VERBOSE);
    }

    /**
     * Add a receive-only video transceiver for a remote video SSRC if we do not already have one.
     *
     * @param array<int, mixed> $known SSRCs already wired, updated in place.
     *
     * @return bool Whether a transceiver was added (so a renegotiation is needed).
     */
    private function wireReceiver(int $ssrc, array &$known): bool
    {
        if (isset($known[$ssrc])) {
            return false;
        }
        $this->applyCryptor($this->peerConnection->addTransceiver(MediaKind::Video, SDPDirections::recvonly));
        $this->orderedSources[] = $ssrc;
        $known[$ssrc] = true;
        return true;
    }

    /**
     * Record a specific participant's incoming media into one file (or stream) with a fixed set of
     * tracks (see {@see CallRecorder::fixed()}), finishing any recording of them in progress.
     *
     * @param int             $source  The participant's signed audio SSRC.
     * @param RecordingFormat $format  The Matroska DocType to write.
     * @param ?StreamMask     $streams The {@see \danog\MadelineProto\CallStream} flags to record, or null
     *                                 for every stream flowing when the recording starts.
     */
    public function setOutput(int $source, LocalFile|WritableStream $file, RecordingFormat $format = RecordingFormat::Mkv, ?int $streams = null): void
    {
        $ssrc = GroupSdp::toUnsignedSsrc($source);
        $this->call->log("Recording of participant $ssrc of {$this->call} requested into ".($file instanceof LocalFile ? $file->file : 'a stream'), Logger::VERBOSE);
        $this->ensureMedia($ssrc)->recordFixed($file, $format, $streams);
        $this->attachLiveTracks($ssrc);
    }

    /**
     * Record a specific participant's incoming media as a numbered series of segment files, one per
     * combination of the streams they send (see {@see CallRecorder::series()}), finishing any
     * recording of them in progress.
     *
     * @param int       $source The participant's signed audio SSRC.
     * @param LocalFile $stem   The series stem: `dir/123` gives `dir/123.<n>_<streams>.mkv`.
     */
    public function setOutputSeries(int $source, LocalFile $stem, RecordingFormat $format = RecordingFormat::Mkv): void
    {
        $ssrc = GroupSdp::toUnsignedSsrc($source);
        $this->call->log("Recording of participant $ssrc of {$this->call} requested into the series {$stem->file}", Logger::VERBOSE);
        $this->ensureMedia($ssrc)->recordSeries($stem, $format);
        $this->attachLiveTracks($ssrc);
    }

    /**
     * Whether a participant (by signed audio SSRC) is being recorded.
     *
     * @psalm-mutation-free
     */
    public function isRecording(int $source): bool
    {
        return ($this->media[GroupSdp::toUnsignedSsrc($source)] ?? null)?->getRecorder() !== null;
    }

    /**
     * Attach any of the given participants' tracks (audio, or video owned by them) that are already
     * live to their routers (the `track` event fired before the router, or its recorder, existed).
     * One pass over the transceivers, whatever the number of participants.
     *
     * @param int ...$owners Unsigned audio SSRCs.
     */
    private function attachLiveTracks(int ...$owners): void
    {
        $wanted = array_flip($owners);
        foreach ($this->peerConnection->getTransceivers() as $transceiver) {
            $track = $transceiver->getReceiver()->getTrack();
            $mid = $transceiver->getMid();
            if (!$track instanceof RemoteStreamTrack || $mid === null) {
                continue;
            }
            $trackSsrc = $this->sources[$mid] ?? null;
            if ($trackSsrc === null) {
                continue;
            }
            $owner = $track->getKind() === MediaKind::Video
                ? ($this->videoOwner[$trackSsrc] ?? $this->presentationOwner[$trackSsrc] ?? null)
                : $trackSsrc;
            if ($owner !== null && isset($wanted[$owner])) {
                $this->attachTrack($track, $trackSsrc);
            }
        }
    }

    /**
     * Hand over every participant's incoming media (with the recordings in progress) to the
     * connection replacing this one, so that a re-join does not finish them: the tracks of this
     * connection are detached, and the routers are no longer closed with it.
     *
     * @return array<int, IncomingMedia> By unsigned audio SSRC.
     *
     * @psalm-external-mutation-free
     */
    public function takeIncomingMedia(): array
    {
        $media = $this->media;
        $this->media = [];
        foreach ($media as $m) {
            $m->detachTracks();
            $m->setObserver(null);
        }
        return $media;
    }

    /**
     * Adopt the incoming media routers of the connection this one replaces (see {@see self::takeIncomingMedia()}).
     *
     * @param array<int, IncomingMedia> $media By unsigned audio SSRC.
     */
    public function adoptIncomingMedia(array $media): void
    {
        foreach ($media as $ssrc => $m) {
            $m->setObserver($this);
            ($this->media[$ssrc] ?? null)?->close();
            $this->media[$ssrc] = $m;
        }
    }

    /**
     * A recording needs a keyframe of one of the incoming tracks: ask its sender for one. The SFU
     * asks for one only when we subscribe, not when a recording starts on a stream already flowing
     * (or after a restart, as it doesn't know about it).
     *
     * @internal
     */
    #[\Override]
    public function onKeyframeNeeded(IncomingMedia $media, int $source): void
    {
        if ($this->closed) {
            return;
        }
        foreach ($this->peerConnection->getTransceivers() as $transceiver) {
            $receiver = $transceiver->getReceiver();
            $track = $receiver->getTrack();
            if (!$track instanceof RemoteStreamTrack || IncomingMedia::sourceOf($track) !== $source) {
                continue;
            }
            $mid = $transceiver->getMid();
            $ssrc = $mid !== null ? ($this->sources[$mid] ?? null) : null;
            if ($ssrc !== null) {
                $this->call->log("Asking source $ssrc of {$this->call} for a keyframe", Logger::VERBOSE);
                $receiver->sendRtcpPli($ssrc);
            }
            return;
        }
    }

    /**
     * A participant's streams or codecs changed, or a recording of them started or ended: pass it
     * on to the call, which surfaces it as an update.
     *
     * @internal
     */
    #[\Override]
    public function onIncomingMediaChanged(IncomingMedia $media, ?RecordingEvent $recording, ?LocalFile $file): void
    {
        $ssrc = array_search($media, $this->media, true);
        if ($ssrc === false) {
            return;
        }
        $this->call->onParticipantStreams(GroupSdp::toSignedSsrc($ssrc), $media->getAvailable(), $media->getCodecs(), $recording, $file);
    }

    public function close(): void
    {
        if ($this->closed) {
            return;
        }
        $this->closed = true;
        foreach ($this->media as $media) {
            try {
                $media->close();
            } catch (Throwable $e) {
                // A recorder that cannot finish its file must not take the whole call down with it.
                $this->call->log("Could not close a recording of {$this->call}: $e", Logger::WARNING);
            }
        }
        $this->media = [];
        try {
            $this->outgoingAudio->stop();
            $this->outgoingVideo->stop();
            $this->peerConnection->close();
        } catch (Throwable $e) {
            $this->call->log("Got $e while closing the WebRTC connection of {$this->call}");
        }
    }

    /**
     * Re-run the local offer/answer exchange against the (unchanging) SFU transport.
     */
    private function renegotiate(): void
    {
        if ($this->transport === null || $this->closed) {
            return;
        }
        if ($this->renegotiating) {
            // A negotiation is already in flight and suspended on a promise: let it pick up our
            // changes when it is done, instead of interleaving two offer/answer exchanges.
            $this->renegotiatePending = true;
            return;
        }
        $this->renegotiating = true;
        try {
            /** @psalm-suppress TypeDoesNotContainType, RedundantCondition the awaited calls may set these */
            do {
                $this->renegotiatePending = false;
                $offer = $this->peerConnection->createOffer();
                $this->peerConnection->setLocalDescription($offer);
                // Fix up the mid => ssrc mapping now that mids are final.
                $this->rebuildSourceMap();
                $answer = GroupSdp::buildAnswer(
                    $offer->getSdp(),
                    $this->transport,
                    $this->sources,
                    $this->video,
                    $this->outgoingVideoCodec,
                );
                $this->peerConnection->setRemoteDescription(new RTCSessionDescription($answer, 'answer'));
            } while ($this->renegotiatePending && !$this->closed);
        } catch (Throwable $e) {
            $this->call->log("Got $e while negotiating the WebRTC connection of {$this->call}", Logger::ERROR);
        } finally {
            $this->renegotiating = false;
        }
    }

    /**
     * Re-key the `mid => ssrc` map against the mids the local stack actually assigned.
     *
     * The receive-only transceivers are created by {@see self::setRemoteSources()} in the same
     * order as {@see self::$orderedSources}, so they line up one by one.
     *
     * @psalm-external-mutation-free
     */
    private function rebuildSourceMap(): void
    {
        $rebuilt = [];
        $index = 0;
        foreach ($this->peerConnection->getTransceivers() as $transceiver) {
            if ($transceiver->getDirection() !== SDPDirections::recvonly) {
                // Our own outgoing audio and video.
                continue;
            }
            $mid = $transceiver->getMid();
            $ssrc = $this->orderedSources[$index++] ?? null;
            if ($mid !== null && $ssrc !== null) {
                $rebuilt[$mid] = $ssrc;
            }
        }
        $this->sources = $rebuilt;
    }

    /**
     * @internal Registered as the peer connection's track listener.
     */
    #[\Override]
    public function onPeerConnectionTrack(MediaStreamTrack $track): void
    {
        if (!$track instanceof RemoteStreamTrack) {
            return;
        }
        // Every incoming stream is muxed raw; the routers and recorders decode nothing.
        foreach ($this->peerConnection->getReceivers() as $receiver) {
            $receiver->setRawMode(true);
        }
        $ssrc = $this->trackSsrc($track);
        if ($ssrc === null) {
            return;
        }
        if ($track->getKind() === MediaKind::Audio) {
            $this->call->log("Got the audio track of source $ssrc in {$this->call}", Logger::VERBOSE);
            $this->call->onIncomingSource(GroupSdp::toSignedSsrc($ssrc));
        } else {
            $owner = $this->videoOwner[$ssrc] ?? $this->presentationOwner[$ssrc] ?? null;
            $this->call->log("Got a video track (source $ssrc, owner ".($owner ?? 'unknown').(isset($this->presentationOwner[$ssrc]) ? ', screen share' : '').") in {$this->call}", Logger::VERBOSE);
        }
        $this->attachTrack($track, $ssrc);
    }

    /**
     * Route a live remote track to its participant's incoming media router (which drains it, sniffs
     * its codec and feeds the recorder, if any). Audio identifies the participant directly; video
     * is mapped back through {@see self::$videoOwner} (camera) or {@see self::$presentationOwner}
     * (screen share), which are the recording's two video slots.
     */
    private function attachTrack(RemoteStreamTrack $track, int $ssrc): void
    {
        if ($track->getKind() === MediaKind::Video && isset($this->presentationOwner[$ssrc])) {
            $owner = $this->presentationOwner[$ssrc];
            $slot = CallStream::SCREEN;
        } else {
            $owner = $track->getKind() === MediaKind::Video ? ($this->videoOwner[$ssrc] ?? null) : $ssrc;
            $slot = CallStream::VIDEO;
        }
        if ($owner === null) {
            return;
        }
        $media = $this->ensureMedia($owner);
        $this->call->log("Track $ssrc ({$track->getKind()->name}, ".CallStream::NAMES[$slot].") of participant $owner in {$this->call}: ".($media->getRecorder() === null ? 'drained, no recording requested' : 'attached to its recording'), Logger::VERBOSE);
        $media->setTrack($track, $slot);
    }

    /**
     * Resolve the SSRC a remote track belongs to, using the transceiver it was created for.
     *
     * @psalm-mutation-free
     */
    private function trackSsrc(RemoteStreamTrack $track): ?int
    {
        foreach ($this->peerConnection->getTransceivers() as $transceiver) {
            if ($transceiver->getReceiver()->getTrack() !== $track) {
                continue;
            }
            $mid = $transceiver->getMid();
            return $mid !== null ? ($this->sources[$mid] ?? null) : null;
        }
        return null;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

// IMPORTANT NOTE: Please keep the above copyright notice intact if copying or rewriting this file in another language.

namespace danog\MadelineProto\Tgcalls;

use Amp\ByteStream\WritableStream;
use Amp\Sync\LocalMutex;
use danog\MadelineProto\CallStream;
use danog\MadelineProto\LocalDirectory;
use danog\MadelineProto\LocalFile;
use danog\MadelineProto\Logger;
use danog\MadelineProto\Loop\VoIP\DjLoop;
use danog\MadelineProto\RecordingEvent;
use danog\MadelineProto\RecordingFormat;
use danog\MadelineProto\RestoresDeclaredProperties;
use danog\MadelineProto\VoIP\MediaState;
use danog\MadelineProto\VoIP\SignalingProtocolVersion;
use Revolt\EventLoop;
use Throwable;
use Webrtc\Codecs\Codec;
use Webrtc\Codecs\EncodedPacket;
use Webrtc\DataChannel\Enum\State as DataChannelState;
use Webrtc\DataChannel\Listener\DataChannelMessageListener;
use Webrtc\DataChannel\Listener\DataChannelOpenListener;
use Webrtc\DataChannel\RTCDataChannel;
use Webrtc\DataChannel\RTCDataChannelParameters;
use Webrtc\DTLS\DTLS\RTCDtlsTransport;
use Webrtc\ICE\RTCIceCandidate;
use Webrtc\RTP\Enum\MediaKind;
use Webrtc\RTP\MediaStreamTrack\MediaStreamTrack;
use Webrtc\RTP\MediaStreamTrack\RemoteStreamTrack;
use Webrtc\RTP\RTCRtpTransceiver;
use Webrtc\RTPParameter\RTCRtpCodecCapability;
use Webrtc\RTPParameter\RTCRtpCodecParameters;
use Webrtc\SDP\Enum\SDPDirections;
use Webrtc\SDP\RTCSessionDescription;
use Webrtc\Webrtc\Enum\ConnectionState;
use Webrtc\Webrtc\Enum\SignalingState;
use Webrtc\Webrtc\Listener\PeerConnectionConnectionStateChangeListener;
use Webrtc\Webrtc\Listener\PeerConnectionDataChannelListener;
use Webrtc\Webrtc\Listener\PeerConnectionTrackListener;
use Webrtc\Webrtc\RTCPeerConnection;

/**
 * WebRTC engine of a modern one-to-one Telegram call.
 *
 * Implements the tgcalls signaling protocols: `10.0.0` (tgcalls' `InstanceV2ReferenceImpl`) ships a
 * plain SDP offer/answer plus trickled ICE candidates; the structured dialects (`InstanceV2Impl`,
 * `11.0.0` and up) negotiate one-directional channels with `InitialSetup`/`NegotiateChannels`
 * messages, translated to and from SDP by {@see V2Sdp}. Either way the messages are serialized as
 * JSON, framed and encrypted with the call auth key by {@see EncryptedConnection}, and carried over
 * [phone.sendSignalingData](https://core.telegram.org/method/phone.sendSignalingData).
 *
 * In the structured dialects the peer connection mirrors tgcalls' channel model: every outgoing
 * channel (audio, camera video, screencast video) is a send-only transceiver, and every channel the
 * peer offers gets its own receive-only transceiver, so that each direction keeps its own payload
 * type numbering. Like tgcalls, only one outgoing video (camera or screencast) is transmitted at a
 * time: the peer has a single incoming video channel and tells the two apart purely through the
 * {@see MediaState} `screencastState` flag.
 *
 * @psalm-import-type StreamMask from CallStream
 * @internal
 */
final class Controller implements VideoCodecObserver, SignalingServiceObserver, SctpSignalingObserver, PeerConnectionTrackListener, PeerConnectionConnectionStateChangeListener, PeerConnectionDataChannelListener, DataChannelOpenListener, DataChannelMessageListener, IncomingMediaObserver
{
    use RestoresDeclaredProperties;
    private RTCPeerConnection $peerConnection;
    private EncryptedConnection $encryption;
    /**
     * The in-band WebRTC data channel of the call. Every tgcalls engine (the SDP reference one and
     * the structured ones alike) exchanges its {@see MediaState} â€” mic muted, camera on/off, screen
     * sharing on/off â€” *only* over this channel, never over the signaling connection, so without it
     * the peer's toggles are invisible. The caller opens it (as stream 0, like tgcalls); the callee
     * receives it. In the structured dialects it runs straight over the media transport with no SDP.
     */
    private ?RTCDataChannel $dataChannel = null;
    private bool $dataChannelOpen = false;

    private OpusPlaybackTrack $outgoingAudio;
    private VideoPlaybackTrack $outgoingVideo;
    private ?RTCRtpTransceiver $videoTransceiver = null;
    /** The outgoing presentation (screencast) video, fed by a separate video-only playlist. */
    private ?VideoPlaybackTrack $outgoingScreencast = null;
    private ?RTCRtpTransceiver $screencastTransceiver = null;
    private ?ScreencastCodecObserver $screencastObserver = null;
    private ?string $outgoingScreencastCodec = null;
    /** SDP fmtp parameters of the outgoing screencast file, derived from its bitstream. */
    private array $outgoingScreencastParameters = [];
    private bool $screencastEnabled = false;
    /** The peer's incoming media: what it sends, in which codecs, and the recorder its frames go to. */
    private IncomingMedia $media;
    /**
     * Every incoming video track, with the recording slot (camera or screen share) it was routed to.
     * tgcalls has one incoming video channel: the peer's screencast is a *new* channel that replaces
     * its camera, told apart only by the peer's MediaState, so each track is routed once, on its first
     * frame, by that state.
     *
     * @var list<array{track: RemoteStreamTrack, target: ?int}>
     */
    private array $incomingVideoTracks = [];
    /** Whether the peer has told us its media state yet (it defaults to "unknown" until then). */
    private bool $remoteMediaStateKnown = false;

    /** @var list<RTCIceCandidate> Candidates received before the remote description was applied. */
    private array $pendingCandidates = [];
    private bool $hasRemoteDescription = false;
    private bool $closed = false;

    private MediaState $remoteMediaState;
    /** @var array<string, mixed>|null The peer's InitialSetup, kept until its NegotiateChannels arrives. */
    private ?array $peerInitialSetup = null;
    /** @var list<array<string, mixed>> Structured offers received before InitialSetup. */
    private array $pendingV2Messages = [];
    /**
     * Until when (unix time) our own re-offers are held back after an exchange, to leave the peer's
     * offer room to arrive first: see {@see self::flushRenegotiation()}.
     */
    private float $holdOffUntil = 0.0;
    /** Whether a delayed flush of a held-back re-offer is already scheduled. */
    private bool $flushScheduled = false;
    /** Whether {@see self::start()} was called: no offer goes out before. */
    private bool $started = false;
    /**
     * Serializes the handling of signaling messages: applying a description suspends (ICE), and the
     * next message must not be handled in the meantime â€” the negotiation state it sees would be
     * stale (an exchange still "in flight" whose answer is being applied, for one).
     */
    private LocalMutex $signalingMutex;
    /** The last peer-offer exchange ID we answered, so a re-sent identical offer is not re-processed. */
    private ?string $answeredPeerOfferExchangeId = null;
    /** Exchange ID of our in-flight structured offer. */
    private ?string $pendingV2ExchangeId = null;
    /**
     * Peer offers that crossed our in-flight exchange (see {@see self::onV2Negotiation()}): the peer
     * discards them on seeing our offer, so we hold them here and drop them once our exchange resolves.
     *
     * @var list<array<array-key, mixed>>
     */
    private array $pendingPeerOffers = [];
    /** Whether our outgoing channels have completed at least one structured negotiation. */
    private bool $localV2Negotiated = false;
    private bool $initialSetupSent = false;
    private bool $renegotiatePending = false;
    /**
     * The peer's outgoing channels, from its latest structured offer, keyed by SSRC (decimal string).
     * tgcalls re-offers its complete channel list every time, so this is replaced wholesale.
     *
     * @var array<string, array<array-key, mixed>>
     */
    private array $peerContents = [];
    /**
     * The receive-only transceiver carrying each of the peer's channels, keyed by the channel's SSRC
     * (which is also its mid). Kept, inactive, when the peer withdraws the channel: tgcalls never
     * reuses an m-line and neither do we.
     *
     * @var array<string, RTCRtpTransceiver>
     */
    private array $recvTransceivers = [];
    /**
     * The peer's answer for each of our outgoing channels (the payload types it accepted), from the
     * latest answer to our offer, keyed by our SSRC (decimal string).
     *
     * @var array<string, array<array-key, mixed>>
     */
    private array $answeredContents = [];
    private bool $videoEnabled = false;
    private ?string $outgoingVideoCodec = null;
    /** SDP fmtp parameters of the outgoing video file, derived from its bitstream (profile/level/â€¦). */
    private array $outgoingVideoParameters = [];
    /**
     * Set once, after the peer's answer reveals it selected a codec other than our file's for our
     * outgoing video, meaning it ignored our preference order (Telegram web hardcodes VP8). We then
     * re-offer the same video without VP8 to force it onto the file's codec. Clients that honour the
     * order (tdesktop) select the file codec on the first offer, so this never triggers for them and
     * they keep seeing the full codec list.
     */
    private bool $dropVp8FromOffer = false;
    /**
     * How long (seconds) to hold our re-offers after an exchange, for the peer's offer to arrive
     * first (see {@see self::flushRenegotiation()}): a signaling round trip through Telegram takes
     * up to a couple of seconds.
     */
    private const PEER_OFFER_GRACE = 3.0;
    /** The SCTP association carrying signaling, for the versions that use one. */
    private ?SignalingSctpTransport $sctp = null;
    /** Last mute state we told the peer about, so media state updates stay consistent. */
    private bool $muted = false;

    public function __construct(
        private readonly PrivateCallController $call,
        string $authKey,
        private readonly bool $outgoing,
        private readonly SignalingProtocolVersion $version,
        DjLoop $dj,
        array $connections,
    ) {
        $this->remoteMediaState = new MediaState(true, false, false);
        $this->media = new IncomingMedia($this);
        $this->signalingMutex = new LocalMutex;
        $this->encryption = new EncryptedConnection($authKey, $outgoing, $this);

        if ($version->usesSctp()) {
            // 11.0.0 and up run the whole signaling channel through an SCTP association.
            $this->sctp = new SignalingSctpTransport($outgoing, $this);
        }

        $this->peerConnection = new RTCPeerConnection([
            'iceServers' => self::buildIceServers($connections),
        ]);
        if (getenv('MP_RTC_DEBUG') === '1') {
            // Route php-rtc's own debug output (ICE, DTLS, SRTP, SCTP, RTP routing) into the call log.
            $this->peerConnection->setLogger(new RtcDebugLogger($call));
        }
        $dj->setVideoCodecObserver($this);
        $this->outgoingAudio = new OpusPlaybackTrack($dj, $call);
        // In the structured dialects every channel is one-directional (see the class docs): our
        // audio is a send-only channel, and the peer's audio arrives on a receive-only one of its
        // own. The plain-SDP dialect is ordinary WebRTC, where both share a sendrecv m-line.
        $this->peerConnection->addTransceiver($this->outgoingAudio, $this->sendDirection());
        $this->outgoingVideo = new VideoPlaybackTrack($dj, $call);
        if ($this->version->usesSdp() && $this->outgoing && $this->call->public->video) {
            $this->videoEnabled = true;
            $this->ensureVideoTransceiver();
        }

        // This object is registered as a typed listener, not a closure, so it is part of the peer
        // connection's serializable state and keeps pointing at this restored controller after a
        // serialize/unserialize cycle (a Closure could not be serialized).
        $this->peerConnection->addTrackListener($this);
        $this->peerConnection->addConnectionStateChangeListener($this);
        $this->peerConnection->addPeerConnectionDataChannelListener($this);

        if (!$this->version->usesSdp()) {
            // tgcalls' InstanceV2Impl runs its data channel as an SCTP association straight over the
            // media transport (port 5000 both ways), never described in the negotiation.
            $this->peerConnection->createInbandSctp();
        }
        if ($this->outgoing) {
            // The caller opens the data channel, exactly like tgcalls does (on stream 0: a tgcalls
            // callee pre-creates its channel with that id and only acknowledges an OPEN for it).
            $this->adoptDataChannel($this->peerConnection->createDataChannel(
                new RTCDataChannelParameters('data', id: 0)
            ));
        }

    }

    /**
     * Begin negotiating, once the owner has attached everything the first offer should carry (the
     * presentation playlist, notably): the callee only announces its transport, the caller also
     * offers its channels. Queued, so that the playlists' pending codec announcements â€” which set
     * the transceivers up â€” run first.
     */
    public function start(): void
    {
        EventLoop::queue(function (): void {
            if ($this->closed) {
                return;
            }
            $this->started = true;
            if (!$this->version->usesSdp()) {
                $this->sendV2InitialSetup();
                if ($this->outgoing) {
                    $this->sendV2Offer();
                }
            } elseif ($this->outgoing) {
                $this->sendLocalDescription();
            }
        });
    }

    /**
     * Take a data channel (ours, or the one the caller opened) into use.
     */
    private function adoptDataChannel(RTCDataChannel $channel): void
    {
        $this->dataChannel = $channel;
        $channel->addOpenListener($this);
        $channel->addMessageListener($this);
        if ($channel->getReadyState() === DataChannelState::Open) {
            $this->onDataChannelOpen();
        }
    }

    /**
     * The peer (the caller) opened the data channel.
     *
     * @internal Registered as the peer connection's data channel listener.
     */
    #[\Override]
    public function onPeerConnectionDataChannel(RTCDataChannel $channel): void
    {
        $this->call->log("The peer opened the data channel \"{$channel->getLabel()}\" of {$this->call}", Logger::VERBOSE);
        $this->adoptDataChannel($channel);
    }

    /**
     * The data channel is open: tell the peer our media state, which tgcalls only reads from here.
     *
     * @internal
     */
    #[\Override]
    public function onDataChannelOpen(): void
    {
        $this->dataChannelOpen = true;
        $this->call->log("The data channel of {$this->call} is open", Logger::VERBOSE);
        $this->sendMediaState($this->muted);
    }

    /**
     * A message from the peer on the data channel: plain (DTLS-protected) JSON in the very same
     * format as the signaling messages, which is how tgcalls carries its MediaState.
     *
     * @internal
     */
    #[\Override]
    public function onDataChannelMessage(string $data): void
    {
        try {
            $decoded = json_decode($data, true, flags: JSON_THROW_ON_ERROR);
        } catch (Throwable $e) {
            $this->call->log("Could not decode a data channel message of {$this->call}: $e", Logger::WARNING);
            return;
        }
        if (!\is_array($decoded) || !isset($decoded['@type'])) {
            return;
        }
        EventLoop::queue($this->onSignalingMessage(...), $decoded);
    }

    /**
     * The direction of the transceivers we transmit on: one-directional channels in the structured
     * dialects, plain WebRTC sendrecv in the SDP one.
     *
     * @psalm-mutation-free
     */
    private function sendDirection(): SDPDirections
    {
        return $this->version->usesSdp() ? SDPDirections::sendrecv : SDPDirections::sendonly;
    }

    /**
     * The direction the camera video transceiver should have right now: transmitting only while a
     * file with video plays on the camera playlist *and* no screencast is active â€” like tgcalls,
     * sharing the screen replaces the camera, since the peer has a single incoming video channel.
     *
     * @psalm-mutation-free
     */
    private function videoDirection(): SDPDirections
    {
        $sending = $this->videoEnabled && !$this->screencastEnabled;
        if ($this->version->usesSdp()) {
            return $sending ? SDPDirections::sendrecv : SDPDirections::recvonly;
        }
        return $sending ? SDPDirections::sendonly : SDPDirections::inactive;
    }

    /**
     * Drop the state that cannot be serialized before the graph is written out.
     *
     * The WebRTC engine (peer connection, ICE/DTLS/SCTP transports and their timers), the signaling
     * reliability layer and the playback tracks all serialize themselves and resume on the far side
     * (the WebM demuxer they read from lives in the {@see DjLoop}, which the call serializes). A
     * file-backed recorder likewise serializes itself and reopens its file on wakeup; only a
     * stream-backed recorder cannot be reopened, so it is dropped here.
     *
     * @return array<string, mixed>
     *
     * @psalm-mutation-free
     */
    public function __serialize(): array
    {
        $vars = get_object_vars($this);
        unset($vars['signalingMutex']);
        return $vars;
    }

    /**
     * @param array<string, mixed> $data
     *
     * @psalm-external-mutation-free
     */
    public function __unserialize(array $data): void
    {
        // Synchronous restore only â€” all async resume work (the recorders reopening their files, notably)
        // happens in resume(), called once the call graph is whole.
        $this->signalingMutex = new LocalMutex;
        $this->restoreDeclaredProperties($data);
    }

    /**
     * Restart everything that was intentionally left dormant during deserialization, once the whole
     * call graph is restored: the playback producers and (file-backed) recorders reopen their files,
     * re-subscribe to the resumed tracks and start their loops. Must run outside __unserialize (it does
     * async work); {@see \danog\MadelineProto\Tgcalls\PrivateCallController} calls it on wakeup for a running call.
     */
    public function resume(): void
    {
        if ($this->closed) {
            return;
        }
        if (getenv('MP_RTC_DEBUG') === '1') {
            $this->peerConnection->setLogger(new RtcDebugLogger($this->call));
        }
        $this->outgoingAudio->resume();
        $this->outgoingVideo->resume();
        $this->outgoingScreencast?->resume();
        $this->media->resume();
        $this->dataChannelOpen = $this->dataChannel?->getReadyState() === DataChannelState::Open;
        foreach (array_keys($this->incomingVideoTracks) as $index) {
            $this->drainIncomingVideo($index);
        }
        // A serialize/unserialize cycle can rebind our UDP socket to a different local port, which
        // strands the ICE candidates the peer already holds. Refresh them from the live socket and
        // re-signal if anything moved, so connectivity is re-established without a full renegotiation.
        $this->restartIceIfNeeded();
    }

    /**
     * Re-signal our ICE candidates if our local transport address changed across a resume.
     *
     * tgcalls V2 bundles every stream over one transport, so only the first transceiver's gatherer
     * matters. Its host candidates are refreshed in place from the (already reconnected) socket; the
     * ICE credentials are unchanged, so this is a candidate re-trickle, not a full ICE restart, and
     * the peer forms fresh candidate pairs for the new port. A no-op when nothing moved.
     */
    private function restartIceIfNeeded(): void
    {
        $transceivers = $this->peerConnection->getTransceivers();
        if ($transceivers === []) {
            return;
        }
        $dtls = $transceivers[0]->getDtlsTransport();
        if (!$dtls instanceof RTCDtlsTransport) {
            return;
        }
        try {
            $gatherer = $dtls->getIceTransport()->getIceGatherer();
            if (!$gatherer->refreshLocalCandidates()) {
                return;
            }
            $this->call->log("Local ICE address changed across a resume of {$this->call}; re-signaling candidates", Logger::NOTICE);
            $batched = [];
            foreach ($gatherer->getLocalCandidates() as $candidate) {
                $sdpString = 'candidate:'.$candidate->convert2SDP();
                if ($this->version->usesSdp()) {
                    $this->sendSignalingMessage(['@type' => 'candidate', 'sdp' => $sdpString, 'mid' => '0', 'mline' => 0]);
                } else {
                    $batched[] = ['sdpString' => $sdpString, 'sdpMLineIndex' => 0];
                }
            }
            if ($batched !== []) {
                $this->sendSignalingMessage(['@type' => 'Candidates', 'candidates' => $batched]);
            }
        } catch (Throwable $e) {
            $this->call->log("Could not refresh ICE candidates for {$this->call}: $e", Logger::WARNING);
        }
    }

    /**
     * Emit a service packet, requested by the reliability layer ({@see EncryptedConnection}).
     *
     * @internal
     */
    #[\Override]
    public function onServiceRequest(int $cause): void
    {
        $packet = $this->encryption->prepareForSendingService($cause);
        if ($packet !== null) {
            $this->call->sendSignalingData($packet);
        }
    }

    /**
     * Put one SCTP packet of the signaling association on the wire.
     *
     * @internal Used by {@see SignalingSctpTransport} as its outgoing transport.
     */
    #[\Override]
    public function deliverSignalingPacket(string $packet): void
    {
        $this->call->sendSignalingData($packet);
    }

    /**
     * Handle a newly negotiated remote track.
     *
     * @internal Registered as the peer connection's track listener.
     */
    #[\Override]
    public function onPeerConnectionTrack(MediaStreamTrack $track): void
    {
        if (!$track instanceof RemoteStreamTrack) {
            return;
        }
        $this->enableRawReceive();
        if ($track->getKind() === MediaKind::Audio) {
            $this->call->log("Got incoming audio track in {$this->call}", Logger::VERBOSE);
            $this->media->setTrack($track);
            return;
        }
        $this->call->log("Got incoming video track in {$this->call}", Logger::VERBOSE);
        $this->incomingVideoTracks[] = ['track' => $track, 'target' => null];
        $this->drainIncomingVideo(array_key_last($this->incomingVideoTracks));
    }

    /**
     * Forward the frames of one incoming video track to the recording slot they belong to.
     *
     * Which slot â€” the camera or the screen share â€” is decided once, on the track's first frame,
     * from the peer's media state at that moment. The peer sends its MediaState (over the data
     * channel, which is faster than the signaling relay) together with the offer that introduces the
     * new channel, so by the time its first frame arrives the state is known â€” deciding when the
     * track is negotiated would race that message. A recorder set later simply starts getting the
     * frames from then on.
     */
    private function drainIncomingVideo(int $index): void
    {
        EventLoop::queue(function () use ($index): void {
            $entry = $this->incomingVideoTracks[$index] ?? null;
            if ($entry === null) {
                return;
            }
            $source = IncomingMedia::sourceOf($entry['track']);
            foreach ($entry['track']->getConsumer() as $frame) {
                if ($this->closed) {
                    return;
                }
                // The receivers run in raw mode (see enableRawReceive()), so what arrives is the
                // still-encoded frame.
                if (!$frame instanceof EncodedPacket) {
                    continue;
                }
                $data = $frame->getData();
                if ($data === '') {
                    continue;
                }
                $target = $this->incomingVideoTracks[$index]['target'] ?? null;
                if ($target === null) {
                    $target = $this->remoteMediaState->screencast ? CallStream::SCREEN : CallStream::VIDEO;
                    $this->incomingVideoTracks[$index]['target'] = $target;
                    $this->call->log("The incoming video track $source of {$this->call} is the peer's ".CallStream::NAMES[$target], Logger::VERBOSE);
                }
                $this->media->pushVideoFrame($data, $frame->getTimestamp(), $source, $target);
            }
        });
    }

    /**
     * React to a change of the WebRTC connection state.
     *
     * @internal Registered as the peer connection's connection-state-change listener.
     */
    #[\Override]
    public function onPeerConnectionConnectionStateChange(): void
    {
        $state = $this->peerConnection->getConnectionState();
        $this->call->log("WebRTC connection state of {$this->call} is now {$state->name}");
        if ($state === ConnectionState::failed) {
            $this->call->onConnectionFailed();
        }
    }

    /**
     * Build the list of ICE servers out of the `connections` of a
     * [phoneCall](https://core.telegram.org/constructor/phoneCall).
     *
     * Only [phoneConnectionWebrtc](https://core.telegram.org/constructor/phoneConnectionWebrtc)
     * endpoints are used: they are plain STUN/TURN servers. Legacy `phoneConnection` reflectors
     * speak a Telegram-specific relay protocol and are only used by libtgvoip, which modern calls
     * no longer support.
     *
     * @psalm-pure
     */
    private static function buildIceServers(array $connections): array
    {
        $iceServers = [];
        foreach ($connections as $connection) {
            if ($connection['_'] !== 'phoneConnectionWebrtc') {
                continue;
            }
            foreach ([$connection['ip'] ?? '', ($connection['ipv6'] ?? '') === '' ? '' : '['.$connection['ipv6'].']'] as $ip) {
                if ($ip === '') {
                    continue;
                }
                if ($connection['turn'] ?? false) {
                    $iceServers[] = [
                        'urls' => 'turn:'.$ip.':'.$connection['port'],
                        'username' => $connection['username'],
                        'credential' => $connection['password'],
                        'credentialType' => 'password',
                    ];
                } elseif ($connection['stun'] ?? false) {
                    $iceServers[] = ['urls' => 'stun:'.$ip.':'.$connection['port']];
                }
            }
        }
        return $iceServers;
    }

    /**
     * Deliver incoming media still encoded, so that no codec library is ever needed.
     */
    private function enableRawReceive(): void
    {
        foreach ($this->peerConnection->getReceivers() as $receiver) {
            $receiver->setRawMode(true);
        }
    }

    /**
     * Record the incoming media into one file (or stream) with a fixed set of tracks.
     *
     * {@see RecordingFormat::Webm} and {@see RecordingFormat::Mkv} record the chosen streams, muxed
     * into Matroska in pure PHP ({@see CallRecorder}); {@see RecordingFormat::Opus} writes an
     * audio-only OGG OPUS stream ({@see OpusRecorder}), so only {@see CallStream::AUDIO} can be chosen.
     * When `$format` is null it is autodetected from the extension of `$file` â€” but only if a
     * {@see LocalFile} was passed; a raw stream, whose extension is unknown, defaults to WebM.
     *
     * @param ?StreamMask $streams The {@see CallStream} flags to record, or null for every stream the peer
     *                      currently sends. Every chosen stream must be available, once the peer has
     *                      reported its media state; before that (the call is still connecting)
     *                      anything is accepted, and a null set takes whatever flows when the
     *                      recording starts.
     *
     * @throws \InvalidArgumentException If a chosen stream is not available.
     *
     * @return StreamMask The streams the peer currently sends, as {@see CallStream} flags (0 while unknown).
     */
    public function setOutput(LocalFile|WritableStream $file, ?RecordingFormat $format = null, ?int $streams = null): int
    {
        $format ??= $file instanceof LocalFile ? RecordingFormat::fromFile($file) : RecordingFormat::Webm;
        if ($streams !== null) {
            CallStream::validate($streams);
        }
        if (!$format->isMatroska()) {
            if ($streams !== null && ($streams & ~CallStream::AUDIO) !== 0) {
                throw new \InvalidArgumentException('An OGG OPUS recording holds audio only: choose CallStream::AUDIO, or record to a .mkv/.webm file to get video.');
            }
            $streams = CallStream::AUDIO;
        }
        $available = $this->getAvailableStreams();
        if ($available !== null) {
            CallStream::checkAvailable($streams, $available, 'The other party');
            $streams ??= $available;
        }

        $this->enableRawReceive();
        $this->media->stopRecording();
        $this->media->setOpusRecorder(null);
        if ($format->isMatroska()) {
            $this->media->recordFixed($file, $format, $streams);
        } else {
            $this->media->setOpusRecorder(new OpusRecorder($file));
        }
        return $available ?? 0;
    }

    /**
     * Record the incoming media into a directory, as a numbered series of `<n>_<streams>.mkv` files,
     * one per combination of streams the peer sends (see {@see CallRecorder::series()}).
     */
    public function setOutputFolder(LocalDirectory $dir, ?RecordingFormat $format = null): void
    {
        $path = $dir->create();
        $format = RecordingFormat::matroskaFor(new LocalFile("$path/"), $format);
        $this->enableRawReceive();
        $this->media->setOpusRecorder(null);
        $this->media->recordSeries(new LocalFile("$path/"), $format);
    }

    /**
     * The streams the peer currently sends, as {@see CallStream} flags, or null if it has not
     * reported its media state yet.
     *
     * @psalm-mutation-free
     *
     * @return ?StreamMask
     */
    public function getAvailableStreams(): ?int
    {
        if (!$this->remoteMediaStateKnown) {
            return null;
        }
        return CallStream::of(!$this->remoteMediaState->muted, $this->remoteMediaState->video, $this->remoteMediaState->screencast);
    }

    /**
     * Pass the peer's media state â€” which of its mic, camera and screen share are on â€” to its
     * incoming media router (and thus to the recorder, which shapes its file after it). Nothing is
     * passed until the peer has actually reported a state: before that whatever arrives is recorded.
     */
    private function tellRecorderExpectedStreams(): void
    {
        if (!$this->remoteMediaStateKnown) {
            return;
        }
        $this->media->setExpected(
            audio: !$this->remoteMediaState->muted,
            video: $this->remoteMediaState->video,
            presentation: $this->remoteMediaState->screencast,
        );
    }

    /**
     * The peer's streams or codecs changed, or a recording of them started or ended: surface it as
     * a {@see \danog\MadelineProto\EventHandler\Calls\CallStreams} update.
     *
     * @internal
     */
    /**
     * A recording needs a keyframe of an incoming video track: ask the peer for one.
     *
     * @internal
     */
    #[\Override]
    public function onKeyframeNeeded(IncomingMedia $media, int $source): void
    {
        if ($this->closed) {
            return;
        }
        foreach ($this->peerConnection->getTransceivers() as $transceiver) {
            $receiver = $transceiver->getReceiver();
            $track = $receiver->getTrack();
            if (!$track instanceof RemoteStreamTrack || IncomingMedia::sourceOf($track) !== $source) {
                continue;
            }
            foreach ($receiver->getSynchronizationSources() as $ssrc) {
                $this->call->log("Asking source {$ssrc->source} of {$this->call} for a keyframe", Logger::VERBOSE);
                $receiver->sendRtcpPli($ssrc->source);
            }
            return;
        }
    }

    #[\Override]
    public function onIncomingMediaChanged(IncomingMedia $media, ?RecordingEvent $recording, ?LocalFile $file): void
    {
        $this->call->onStreamsChanged($media->getAvailable(), $media->getCodecs(), $recording, $file);
    }

    /**
     * React to the demuxed file's video finishing: stop transmitting video and tell the peer.
     */
    #[\Override]
    public function onVideoStopped(): void
    {
        $this->videoEnabled = false;
        if ($this->applyVideoDirection()) {
            $this->renegotiate();
        }
        $this->sendMediaState($this->muted);
    }

    /**
     * Prefer the codec of the opened file for the video sender, while still offering the rest so the
     * peer's own camera (which it encodes as VP8/VP9/H.264) can be received and recorded.
     *
     * The pre-encoded frames of the file are only correct if the peer selects the file's codec; a
     * peer that cannot decode it will pick another and our outgoing video will not be usable, but the
     * call and the incoming video keep working â€” which is what {@see self::record()} needs.
     */
    #[\Override]
    public function onVideoCodec(string $codec, array $parameters = []): void
    {
        $this->videoEnabled = true;
        $transceiver = $this->ensureVideoTransceiver();
        $changed = $this->applyVideoDirection();
        // Keep the first (usually only immediately useful) keyframe queued until the answer has
        // selected the codec of this file.
        $this->outgoingVideo->setTransportReady(false);
        if ($codec !== $this->outgoingVideoCodec || $parameters !== $this->outgoingVideoParameters) {
            $this->outgoingVideoCodec = $codec;
            $this->outgoingVideoParameters = $parameters;
            $this->applyVideoCodecPreferences($transceiver);
            $changed = true;
        }
        if ($changed) {
            $this->renegotiate();
        } else {
            $this->outgoingVideo->setTransportReady(true);
        }
        $this->sendMediaState($this->muted);
    }

    /**
     * Bring the camera transceiver's direction in line with {@see self::videoDirection()}, and hold
     * or discard the camera frames accordingly: while a screencast replaces the camera its frames
     * are still consumed (so the playlist keeps its pace) but dropped rather than queued.
     *
     * @return bool Whether the direction changed, i.e. a renegotiation is needed.
     */
    private function applyVideoDirection(): bool
    {
        $this->outgoingVideo->setSuppressed($this->videoEnabled && $this->screencastEnabled);
        if ($this->videoTransceiver === null) {
            return false;
        }
        $direction = $this->videoDirection();
        if ($this->videoTransceiver->getDirection() === $direction) {
            return false;
        }
        $this->videoTransceiver->setDirection($direction);
        return true;
    }

    /**
     * Set the outgoing video codec preferences from {@see self::$outgoingVideoCodec}: the file's
     * codec first (so order-honouring peers select it), then the rest of the table so the peer's own
     * camera can still be received. VP8 is dropped once {@see self::$dropVp8FromOffer} is set â€” see
     * that property and {@see self::forceFileCodecIfRejected()}.
     */
    private function applyVideoCodecPreferences(RTCRtpTransceiver $transceiver): void
    {
        if ($this->outgoingVideoCodec === null) {
            return;
        }
        $transceiver->setCodecPreferences($this->orderedVideoCapabilities(
            $this->outgoingVideoCodec,
            $this->outgoingVideoParameters,
            $this->dropVp8FromOffer,
        ));
        // Advertise the file's real profile/level/tier on the wire (not just reorder): the fmtp
        // parameters derived from the bitstream are merged onto the offered codec by php-rtc.
        $transceiver->setCodecParameterOverrides(
            $this->outgoingVideoParameters === []
                ? []
                : ['video/'.$this->outgoingVideoCodec => $this->outgoingVideoParameters],
        );
    }

    /**
     * Build the ordered video codec capability list to advertise: the given codec first (so
     * order-honouring peers select it), carrying the file's real bitstream parameters, followed by the
     * rest of the table (so the peer's own camera can still be received). VP8 is dropped when asked.
     *
     * @return list<RTCRtpCodecCapability>
     */
    private function orderedVideoCapabilities(string $codec, array $parameters, bool $dropVp8Requested): array
    {
        $capabilities = (new Codec())->getCapabilities('video')->codecs;
        $isFile = static fn ($capability): bool => strcasecmp($capability->mimeType, 'video/'.$codec) === 0;
        $isVp8 = static fn ($capability): bool => strcasecmp($capability->mimeType, 'video/VP8') === 0;
        // NB: RTCRtpTransceiver::setCodecPreferences() only accepts capability objects that match the
        // registered table by value, so this method just reorders; the file's real fmtp parameters are
        // applied separately via RTCRtpTransceiver::setCodecParameterOverrides() (see the callers).
        $dropVp8 = $dropVp8Requested && strcasecmp($codec, 'VP8') !== 0;
        $others = array_filter(
            $capabilities,
            static fn ($capability): bool => !$isFile($capability) && !($dropVp8 && $isVp8($capability)),
        );
        return array_values(array_merge(array_filter($capabilities, $isFile), $others));
    }

    /**
     * Attach the presentation (screencast) playlist as a separate outgoing video stream, created once
     * on the first presentation playback. The screencast is a second `video` content with its own SSRC
     * (tgcalls has no distinct screencast content type); the peer is told it is a screencast purely via
     * the {@see MediaState} `screencastState` field.
     */
    public function enablePresentation(DjLoop $presentationDj): void
    {
        if ($this->outgoingScreencast !== null) {
            return;
        }
        $this->outgoingScreencast = new VideoPlaybackTrack($presentationDj, $this->call, 'screencast');
        // Nothing goes out before the screencast channel has been negotiated.
        $this->outgoingScreencast->setTransportReady(false);
        $this->screencastObserver = new ScreencastCodecObserver($this);
        $presentationDj->setVideoCodecObserver($this->screencastObserver);
    }

    /**
     * Notified (via {@see ScreencastCodecObserver}) of the codec of the presentation file being played:
     * bring up the screencast video content, which â€” as in tgcalls â€” replaces the camera video while
     * it is active, and announce it.
     */
    public function onScreencastCodec(string $codec, array $parameters = []): void
    {
        if ($this->outgoingScreencast === null) {
            return;
        }
        $this->screencastEnabled = true;
        $transceiver = $this->ensureScreencastTransceiver();
        $transceiver->setDirection(SDPDirections::sendonly);
        $this->applyVideoDirection();
        $this->outgoingScreencast->setTransportReady(false);
        if ($codec !== $this->outgoingScreencastCodec || $parameters !== $this->outgoingScreencastParameters) {
            $this->outgoingScreencastCodec = $codec;
            $this->outgoingScreencastParameters = $parameters;
            $transceiver->setCodecPreferences($this->orderedVideoCapabilities($codec, $parameters, false));
            $transceiver->setCodecParameterOverrides($parameters === [] ? [] : ['video/'.$codec => $parameters]);
        }
        $this->renegotiate();
        $this->sendMediaState($this->muted);
    }

    /**
     * Whether our screencast is currently being transmitted (advertised as active to the peer).
     *
     * @psalm-mutation-free
     */
    public function isScreencastActive(): bool
    {
        return $this->screencastEnabled;
    }

    /**
     * Notified that the presentation playlist finished: stop advertising the screencast and hand the
     * outgoing video back to the camera, if a file with video is still playing there.
     */
    public function onScreencastStopped(): void
    {
        if (!$this->screencastEnabled) {
            return;
        }
        $this->screencastEnabled = false;
        $this->screencastTransceiver?->setDirection(SDPDirections::inactive);
        if ($this->applyVideoDirection()) {
            // The camera comes back as a channel the peer has to set up anew: hold its frames until
            // the answer arrives, then start from a keyframe.
            $this->outgoingVideo->setTransportReady(false);
        }
        $this->renegotiate();
        $this->sendMediaState($this->muted);
    }

    private function ensureScreencastTransceiver(): RTCRtpTransceiver
    {
        \assert($this->outgoingScreencast !== null);
        return $this->screencastTransceiver ??= $this->peerConnection->addTransceiver(
            $this->outgoingScreencast,
            SDPDirections::sendonly,
        );
    }

    /**
     * After the peer's answer to our offer has been applied, check whether it selected the file's
     * codec for our outgoing video. A peer that ignores preference order and picks another codec
     * (Telegram web hardcodes VP8) would decode our pre-encoded frames as garbage, so re-offer the
     * video once without VP8 to force it onto the file's codec. Order-honouring peers select the file
     * codec on the first offer and never reach this, keeping the full codec list.
     */
    private function forceFileCodecIfRejected(): void
    {
        if ($this->dropVp8FromOffer
            || $this->videoTransceiver === null
            || $this->outgoingVideoCodec === null
            || !$this->videoEnabled
            || $this->screencastEnabled
        ) {
            return;
        }
        $selected = $this->negotiatedOutgoingVideoCodec();
        if ($selected === null || strcasecmp($selected, $this->outgoingVideoCodec) === 0) {
            return;
        }
        $this->call->log(
            "Peer selected {$selected} for our outgoing {$this->outgoingVideoCodec} video in {$this->call}; "
            .'re-offering without VP8 to force the file codec',
            Logger::VERBOSE,
        );
        $this->dropVp8FromOffer = true;
        $this->applyVideoCodecPreferences($this->videoTransceiver);
        $this->renegotiate();
    }

    /**
     * The codec the peer picked to receive our outgoing video, from the applied answer, or null if it
     * cannot be determined. Matches the answer's media section to our send video by the m-line id our
     * offer stamped on it (an SSRC, via {@see V2Sdp::useSsrcAsMid()}).
     */
    private function negotiatedOutgoingVideoCodec(): ?string
    {
        $local = $this->peerConnection->getLocalDescription()?->getSdp();
        $remote = $this->peerConnection->getRemoteDescription()?->getSdp();
        if ($local === null || $remote === null) {
            return null;
        }
        $sendMid = self::outgoingVideoMid($local);
        if ($sendMid === null) {
            return null;
        }
        return self::videoCodecForMid($remote, $sendMid);
    }

    /**
     * The a=mid of the first send-capable video m-line (port != 0) in an SDP.
     *
     * @psalm-pure
     */
    private static function outgoingVideoMid(string $sdp): ?string
    {
        $mid = null;
        $isVideo = false;
        $sending = false;
        $active = false;
        foreach (explode("\n", str_replace("\r\n", "\n", $sdp)) as $line) {
            $line = trim($line);
            if (str_starts_with($line, 'm=')) {
                if ($isVideo && $active && $sending && $mid !== null) {
                    return $mid;
                }
                $parts = explode(' ', $line);
                $isVideo = str_starts_with($line, 'm=video');
                $active = ($parts[1] ?? '0') !== '0';
                $sending = false;
                $mid = null;
            } elseif ($line === 'a=sendrecv' || $line === 'a=sendonly') {
                $sending = true;
            } elseif (str_starts_with($line, 'a=mid:')) {
                $mid = substr($line, 6);
            }
        }
        return ($isVideo && $active && $sending && $mid !== null) ? $mid : null;
    }

    /**
     * The first non-RTX codec name of the video m-line with the given a=mid in an SDP.
     *
     * @psalm-pure
     */
    private static function videoCodecForMid(string $sdp, string $wantMid): ?string
    {
        $isVideo = false;
        $matches = false;
        foreach (explode("\n", str_replace("\r\n", "\n", $sdp)) as $line) {
            $line = trim($line);
            if (str_starts_with($line, 'm=')) {
                $isVideo = str_starts_with($line, 'm=video');
                $matches = false;
            } elseif ($isVideo && str_starts_with($line, 'a=mid:')) {
                $matches = substr($line, 6) === $wantMid;
            } elseif ($isVideo && $matches && str_starts_with($line, 'a=rtpmap:')) {
                $name = explode('/', explode(' ', substr($line, 9))[1] ?? '')[0];
                if ($name !== '' && strcasecmp($name, 'rtx') !== 0) {
                    return $name;
                }
            }
        }
        return null;
    }

    private function ensureVideoTransceiver(): RTCRtpTransceiver
    {
        return $this->videoTransceiver ??= $this->peerConnection->addTransceiver(
            $this->outgoingVideo,
            $this->videoDirection(),
        );
    }

    /**
     * Whether the remote party is currently muted, and the state of its video streams.
     */
    public function getRemoteMediaState(): MediaState
    {
        return $this->remoteMediaState;
    }

    /**
     * Notify the peer of a change in our own media state.
     *
     * The camera counts as active only while it is actually the transmitted video: a screencast
     * replaces it (tgcalls reports exactly the same, and shows the incoming video as whichever of the
     * two is announced active).
     */
    public function sendMediaState(bool $muted, bool $batteryLow = false): void
    {
        $this->muted = $muted;
        $message = [
            '@type' => 'MediaState',
            'muted' => $muted,
            'videoState' => $this->videoEnabled && !$this->screencastEnabled ? 'active' : 'inactive',
            'videoRotation' => 0,
            'screencastState' => $this->screencastEnabled ? 'active' : 'inactive',
            'isBatteryLow' => $batteryLow,
        ];
        // tgcalls sends its media state over the data channel only, but accepts it from either
        // path; send it on both, so the state also reaches a peer whose channel is not up yet.
        if ($this->dataChannelOpen && $this->dataChannel !== null && !$this->closed) {
            try {
                $this->dataChannel->send(json_encode($message, JSON_THROW_ON_ERROR));
            } catch (Throwable $e) {
                $this->call->log("Could not send the media state of {$this->call} over the data channel: $e", Logger::WARNING);
            }
        }
        $this->sendSignalingMessage($message);
    }

    /**
     * Tear down the WebRTC connection.
     */
    public function close(): void
    {
        if ($this->closed) {
            return;
        }
        $this->closed = true;
        try {
            $this->outgoingAudio->stop();
            $this->outgoingVideo->stop();
            $this->outgoingScreencast?->stop();
            $this->sctp?->close();
            // Stops the receivers, which hand over the frames still in their jitter buffers.
            $this->peerConnection->close();
        } catch (Throwable $e) {
            $this->call->log("Got $e while closing the WebRTC connection of {$this->call}");
        }
        try {
            // Record them before closing the recording.
            $this->media->finishDraining(1.0);
            $this->media->close();
        } catch (Throwable $e) {
            // A recorder that cannot finish its file must not take the whole call down with it.
            $this->call->log("Could not close the recording of {$this->call}: $e", Logger::WARNING);
        }
    }

    private function sendLocalDescription(bool $answer = false): void
    {
        try {
            $description = $answer
                ? $this->peerConnection->createAnswer()
                : $this->peerConnection->createOffer();
            $this->peerConnection->setLocalDescription($description);
            $this->sendSignalingMessage([
                '@type' => $description->getType(),
                'sdp' => $description->getSdp(),
            ]);
            $this->sendLocalCandidates();
        } catch (Throwable $e) {
            $this->call->log("Got $e while generating the local description of {$this->call}", Logger::ERROR);
        }
    }

    /**
     * Trickle all locally gathered ICE candidates to the peer.
     */
    private function sendLocalCandidates(): void
    {
        $local = $this->peerConnection->getLocalDescription();
        if ($local === null) {
            return;
        }
        $mid = null;
        $mline = -1;
        /** @var list<array{sdpString: string}> $batched */
        $batched = [];
        foreach (explode("\n", str_replace("\r\n", "\n", $local->getSdp())) as $line) {
            $line = trim($line);
            if (str_starts_with($line, 'm=')) {
                $mline++;
                $mid = (string) $mline;
                continue;
            }
            if (str_starts_with($line, 'a=mid:')) {
                $mid = substr($line, 6);
                continue;
            }
            if (!str_starts_with($line, 'a=candidate:') || $mid === null) {
                continue;
            }
            if ($this->version->usesSdp()) {
                $this->sendSignalingMessage([
                    '@type' => 'candidate',
                    'sdp' => substr($line, 2),
                    'mid' => $mid,
                    'mline' => $mline,
                ]);
            } else {
                // Web clients (tdesktop's webrtc.js/tweb) route a trickled candidate to a media
                // section by its sdpMLineIndex, and drop any candidate they cannot place: their
                // mid-less fallback only works when a single RTP section is active, so an audio
                // call connects but a video call (audio+video+data) silently loses every
                // candidate. We therefore always stamp the m-line index. We deliberately omit
                // sdpMid: after useSsrcAsMid() our a=mid is an SSRC that does not match the
                // peer's own mids, which would make a strict client's addIceCandidate() throw.
                // Native clients ignore these extra JSON fields, so this is safe for them.
                $batched[] = ['sdpString' => substr($line, 2), 'sdpMLineIndex' => $mline];
            }
        }
        if ($batched !== []) {
            $this->sendSignalingMessage(['@type' => 'Candidates', 'candidates' => $batched]);
        }
    }

    /** Send the transport half of InstanceV2Impl signaling exactly once. */
    private function sendV2InitialSetup(): void
    {
        if ($this->initialSetupSent) {
            return;
        }
        try {
            // Merely creating the offer is enough to allocate ICE and DTLS parameters. The callee
            // must not set it locally yet, because the caller's channel offer is authoritative.
            $description = $this->peerConnection->createOffer();
            $setup = $this->outgoing ? 'active' : 'passive';
            $initialSetup = V2Sdp::initialSetupFromDescription($description->getSdp(), $setup);
            $this->sendSignalingMessage([
                '@type' => 'InitialSetup',
                'ufrag' => $initialSetup['ufrag'],
                'pwd' => $initialSetup['pwd'],
                'renomination' => false,
                'fingerprints' => $initialSetup['fingerprints'],
            ]);
            $this->initialSetupSent = true;
        } catch (Throwable $e) {
            $this->call->log("Got $e while sending the initial setup of {$this->call}", Logger::ERROR);
        }
    }

    /**
     * Our local offer, with every m-line's mid set the way tgcalls routes channels: a channel we
     * send is identified by our SSRC, a channel we receive by the peer's (see {@see V2Sdp::useSsrcAsMid()}).
     */
    private function localOffer(): RTCSessionDescription
    {
        $offer = $this->peerConnection->createOffer();
        return new RTCSessionDescription(
            V2Sdp::useSsrcAsMid($offer->getSdp(), $this->recvMidsByIndex($offer->getSdp())),
            $offer->getType(),
        );
    }

    /**
     * The mid (the peer's SSRC) of every receive-only m-line of a freshly created local offer, by
     * m-line index.
     *
     * php-rtc lays the offer out as the currently negotiated m-lines (matched to transceivers by
     * their mid) followed by the not-yet-negotiated transceivers in creation order, and matches a
     * remote description back the same way, so the same walk identifies which transceiver each
     * new m-line stands for.
     *
     * @return array<int, string>
     */
    private function recvMidsByIndex(string $sdp): array
    {
        $ssrcByTransceiver = [];
        foreach ($this->recvTransceivers as $ssrc => $transceiver) {
            $ssrcByTransceiver[spl_object_id($transceiver)] = (string) $ssrc;
        }
        $known = [];
        foreach ($this->peerConnection->getTransceivers() as $transceiver) {
            if ($transceiver->getMid() !== null) {
                $known[$transceiver->getMid()] = $transceiver;
            }
        }
        $unnegotiated = array_values(array_filter(
            $this->peerConnection->getTransceivers(),
            static fn (RTCRtpTransceiver $transceiver): bool => $transceiver->getMid() === null && !$transceiver->isStopped(),
        ));
        $result = [];
        foreach (V2Sdp::mids($sdp) as $index => $mid) {
            $transceiver = $mid !== null ? ($known[$mid] ?? null) : null;
            if ($transceiver === null) {
                $transceiver = array_shift($unnegotiated);
            }
            if ($transceiver !== null && isset($ssrcByTransceiver[spl_object_id($transceiver)])) {
                $result[$index] = $ssrcByTransceiver[spl_object_id($transceiver)];
            }
        }
        return $result;
    }

    /**
     * The peer's channels keyed by the mid of the m-line that carries each, for {@see V2Sdp::buildRemoteDescription()}.
     *
     * @return array<string, array<array-key, mixed>>
     *
     * @psalm-mutation-free
     */
    private function recvContentsByMid(): array
    {
        $result = [];
        foreach ($this->peerContents as $ssrc => $content) {
            $transceiver = $this->recvTransceivers[$ssrc] ?? null;
            $result[$transceiver?->getMid() ?? (string) $ssrc] = $content;
        }
        return $result;
    }

    /** Offer our currently active outgoing channels using tgcalls' structured dialect. */
    private function sendV2Offer(): void
    {
        if (!$this->started) {
            // The channels are still being set up: the first offer waits for start().
            $this->renegotiatePending = true;
            return;
        }
        if ($this->pendingV2ExchangeId !== null
            || $this->peerConnection->getSignalingState() !== SignalingState::stable
        ) {
            $this->call->log("Deferring a re-offer of {$this->call}: exchange ".($this->pendingV2ExchangeId ?? 'none').' in flight, signaling state '.$this->peerConnection->getSignalingState()->name, Logger::VERBOSE);
            $this->renegotiatePending = true;
            return;
        }
        try {
            $this->sendV2InitialSetup();
            // The offer captures the channels as they are now, so a change requested from here on
            // needs another one: clear the request before, not after, applying the description â€”
            // setLocalDescription() suspends (ICE gathering), and a request made meanwhile would
            // otherwise be wiped out. Likewise the exchange counts as in flight from this point.
            $this->renegotiatePending = false;
            $this->pendingV2ExchangeId = (string) random_int(1, 0x7FFFFFFF);
            $offer = $this->localOffer();
            $this->peerConnection->setLocalDescription($offer);
            $contents = V2Sdp::contentsFromOffer($offer->getSdp(), outgoingOnly: true);
            $this->call->log("Offering exchange {$this->pendingV2ExchangeId} of {$this->call}: ".implode(', ', array_map(
                static fn (array $content): string => (string) $content['type'].'#'.(string) $content['ssrc'],
                $contents,
            )), Logger::VERBOSE);
            $this->sendSignalingMessage([
                '@type' => 'NegotiateChannels',
                'exchangeId' => $this->pendingV2ExchangeId,
                'contents' => $contents,
            ]);
            $this->sendLocalCandidates();
        } catch (Throwable $e) {
            $this->pendingV2ExchangeId = null;
            $this->call->log("Got $e while offering the media of {$this->call}", Logger::ERROR);
        }
    }

    /** Request a new offer/answer exchange, coalescing changes while one is already in flight. */
    private function renegotiate(): void
    {
        if ($this->closed) {
            return;
        }
        $this->renegotiatePending = true;
        if (!$this->started) {
            return; // start() sends the first offer with everything set up so far.
        }
        if ($this->version->usesSdp()) {
            if ($this->peerConnection->getSignalingState() === SignalingState::stable) {
                $this->renegotiatePending = false;
                $this->sendLocalDescription();
            }
            return;
        }
        $this->flushRenegotiation();
    }

    private function flushRenegotiation(): void
    {
        if (!$this->renegotiatePending || $this->peerConnection->getSignalingState() !== SignalingState::stable || $this->closed) {
            return;
        }
        $wait = $this->holdOffUntil - microtime(true);
        if ($wait > 0) {
            // Held back to let the peer's offer arrive first (see onV2Negotiation()); retry then.
            if (!$this->flushScheduled) {
                $this->flushScheduled = true;
                EventLoop::delay($wait, function (): void {
                    $this->flushScheduled = false;
                    $this->flushRenegotiation();
                });
            }
            return;
        }
        if ($this->version->usesSdp()) {
            $this->renegotiatePending = false;
            $this->sendLocalDescription();
            return;
        }
        $this->sendV2Offer();
    }

    /**
     * Fill any channel our offer carries but the peer's answer omits, from our own offer, so its
     * m-line is not marked inactive by {@see V2Sdp::buildRemoteDescription()}. A no-op when the answer
     * already addresses every offered channel.
     *
     * @param list<array<array-key, mixed>> $answerContents
     * @return array<string, array<array-key, mixed>> The answer, keyed by our SSRC.
     */
    private function completeAnswerContents(string $offer, array $answerContents): array
    {
        $bySsrc = [];
        foreach ($answerContents as $content) {
            $ssrc = (string) ($content['ssrc'] ?? '0');
            if ($ssrc !== '0') {
                $bySsrc[$ssrc] = $content;
            }
        }
        foreach (V2Sdp::contentsFromOffer($offer, outgoingOnly: true) as $offered) {
            $ssrc = (string) ($offered['ssrc'] ?? '0');
            if ($ssrc !== '0' && !isset($bySsrc[$ssrc])) {
                // A peer answering our re-offer may leave out a channel it is not renegotiating
                // (tweb, whose VP8 pick triggers our AV1-force re-offer, sometimes answers with
                // video only); without it our audio m-line would go inactive and stop.
                $this->call->log("The answer of {$this->call} omitted our {$offered['type']} channel $ssrc; keeping it as offered", Logger::VERBOSE);
                $bySsrc[$ssrc] = $offered;
            }
        }
        return $bySsrc;
    }

    /** Process one structured offer or answer after InitialSetup has arrived. */
    private function onV2Negotiation(array $message): void
    {
        if ($this->peerInitialSetup === null) {
            $this->pendingV2Messages[] = $message;
            return;
        }
        $exchangeId = (string) ($message['exchangeId'] ?? '');
        /** @var list<array<array-key, mixed>> $contents */
        $contents = array_values((array) ($message['contents'] ?? []));

        $this->call->log("Negotiation message $exchangeId for {$this->call}: ".implode(', ', array_map(
            static fn (array $content): string => (string) ($content['type'] ?? '?').'#'.(string) ($content['ssrc'] ?? '?'),
            $contents,
        )).($this->pendingV2ExchangeId !== null ? " (our exchange {$this->pendingV2ExchangeId} is in flight)" : ''), Logger::VERBOSE);
        if ($this->pendingV2ExchangeId !== null && $exchangeId === $this->pendingV2ExchangeId) {
            $offer = $this->peerConnection->getLocalDescription()?->getSdp();
            if ($offer === null) {
                return;
            }
            $this->answeredContents = $this->completeAnswerContents($offer, $contents);
            foreach ($this->answeredContents as $ssrc => $content) {
                $codecs = implode(', ', array_map(
                    static fn (array $payloadType): string => (string) ($payloadType['name'] ?? '?').'/'.(string) ($payloadType['id'] ?? '?'),
                    (array) ($content['payloadTypes'] ?? []),
                ));
                $this->call->log("The peer of {$this->call} accepted our {$content['type']} channel $ssrc ($codecs)", Logger::VERBOSE);
            }
            $sdp = V2Sdp::buildRemoteDescription($offer, $this->peerInitialSetup, $this->answeredContents, $this->recvContentsByMid(), true);
            $this->peerConnection->setRemoteDescription(new RTCSessionDescription($sdp, 'answer'));
            $this->pendingV2ExchangeId = null;
            $this->localV2Negotiated = true;
            $this->outgoingVideo->setTransportReady(true);
            $this->outgoingScreencast?->setTransportReady(true);
            $this->logTransceivers('after the answer to exchange '.$exchangeId);
            $this->hasRemoteDescription = true;
            $this->flushPendingCandidates();
            $this->forceFileCodecIfRejected();
            // The peer (a tgcalls callee answers, then at once offers its own channels) may be about
            // to offer: an offer of ours crossing it would be a glare, which a tgcalls callee resolves
            // by discarding its own offer *for good* â€” its channels would then never be negotiated,
            // and our answer to the discarded offer would be taken for a fresh offer, rebinding the
            // peer's incoming channels to nonsense (our own media goes silent for it). So leave the
            // peer's offer room to arrive before re-offering anything.
            $this->holdOffUntil = microtime(true) + self::PEER_OFFER_GRACE;
            $this->dropStalePeerOffers();
            $this->flushRenegotiation();
            return;
        }

        // Beyond this point the message is the peer's own offer (a glare collision or a fresh one),
        // declaring the peer's complete set of outgoing channels (its mic, camera or screencast).
        // Every one of them must be answered: tgcalls creates its outgoing channels only from our
        // answer, and cannot send another offer (a camera turned on later, say) while one is
        // pending â€” so an ignored offer leaves the peer silent for the rest of the call.
        if ($this->pendingV2ExchangeId !== null) {
            // A glare. InstanceV2Impl resolves it in favor of the call initiator: as the callee we
            // discard our in-flight offer and answer the peer's, re-offering afterwards.
            if (!$this->outgoing) {
                $this->peerConnection->setLocalDescription(new RTCSessionDescription('', 'rollback'));
                $this->pendingV2ExchangeId = null;
                $this->renegotiatePending = true;
            } else {
                // As the caller we keep ours; the peer, on receiving it, discards this offer of its
                // own â€” and it will not send it again, tgcalls only re-offers on a further change of
                // its channels. It arrived ahead of the peer's answer to our exchange, so the peer
                // sent it before seeing our offer: answering it later would be taken for a fresh
                // offer of ours and rebind the peer's incoming channels to nonsense. Let it go.
                $this->call->log("Dropping the peer's offer $exchangeId of {$this->call}: it crossed our in-flight exchange {$this->pendingV2ExchangeId}, the peer discards it", Logger::WARNING);
                $this->pendingPeerOffers[] = $message;
                return;
            }
        }

        // A peer that has not seen our answer re-sends the same offer; answering it again only churns
        // the connection, so skip an exchange we already answered.
        if ($exchangeId !== '' && $exchangeId === $this->answeredPeerOfferExchangeId) {
            return;
        }
        $this->answeredPeerOfferExchangeId = $exchangeId;

        // The peer advertises codecs we may not support (e.g. Telegram Android offers H265/HEVC).
        // We echo this offer back as our answer, which is what tells the peer which codec to send; if
        // we echo a codec our RtpRouter has no receiver for, the peer sends it and every packet is
        // dropped (routeRtp can't map the payload type), so its video never records. Keep only the
        // payload types we can actually route/record, so the peer falls back to a mutual codec (H264).
        $contents = $this->filterSupportedPayloadTypes($contents);
        $this->syncPeerChannels($contents);
        $template = $this->localOffer()->getSdp();
        $sdp = V2Sdp::buildRemoteDescription($template, $this->peerInitialSetup, $this->answeredContents, $this->recvContentsByMid(), false);
        $this->peerConnection->setRemoteDescription(new RTCSessionDescription($sdp, 'offer'));
        $answer = $this->peerConnection->createAnswer();
        $this->peerConnection->setLocalDescription($answer);
        $this->enableRawReceive();
        $this->sendSignalingMessage([
            '@type' => 'NegotiateChannels',
            'exchangeId' => $exchangeId,
            // The answer must echo the peer's offer verbatim â€” same exchangeId, SSRCs, ssrcGroups and
            // payload types â€” which is how tgcalls activates the peer's outgoing channels (it matches
            // on SSRC + exchangeId). We only have to repair one JSON detail: a payload type's
            // `parameters` round-trips through PHP's json_decode/encode as an empty array `[]` when
            // it was `{}`, and tgcalls' strict parser rejects the whole NegotiateChannels ("could not
            // parse PayloadType") if `parameters` is not a JSON object â€” so the peer silently drops
            // our answer and never starts sending its media. Force `parameters` back to an object.
            'contents' => self::withObjectParameters($contents),
        ]);
        $this->sendLocalCandidates();
        $this->hasRemoteDescription = true;
        $this->flushPendingCandidates();
        if (!$this->localV2Negotiated) {
            $this->renegotiatePending = true;
        }
        // The peer's offer is in: nothing to wait for before re-offering.
        $this->holdOffUntil = 0.0;
        $this->flushRenegotiation();
    }

    /**
     * Log every transceiver's mid, kind and directions, for diagnosing a negotiation.
     */
    private function logTransceivers(string $when): void
    {
        $lines = [];
        foreach ($this->peerConnection->getTransceivers() as $transceiver) {
            $lines[] = ($transceiver->getMid() ?? '?').':'.$transceiver->getKind()->name
                .' wanted '.($transceiver->getDirection()?->name ?? '?')
                .' negotiated '.($transceiver->getCurrentDirection()?->name ?? '?');
        }
        $this->call->log("Transceivers of {$this->call} $when: ".implode('; ', $lines), Logger::VERBOSE);
    }

    /**
     * Bring our receive-only transceivers in line with the channels the peer just offered: one per
     * channel SSRC, created on first sight, receiving while offered and inactive once withdrawn.
     *
     * @param list<array<array-key, mixed>> $contents The peer's complete outgoing channel list.
     */
    private function syncPeerChannels(array $contents): void
    {
        $this->peerContents = [];
        foreach ($contents as $content) {
            $ssrc = (string) ($content['ssrc'] ?? '0');
            if ($ssrc === '0') {
                continue;
            }
            $this->peerContents[$ssrc] = $content;
            if (!isset($this->recvTransceivers[$ssrc])) {
                $kind = ($content['type'] ?? null) === 'video' ? MediaKind::Video : MediaKind::Audio;
                $codecs = implode(', ', array_map(
                    static fn (array $payloadType): string => (string) ($payloadType['name'] ?? '?').'/'.(string) ($payloadType['id'] ?? '?'),
                    (array) ($content['payloadTypes'] ?? []),
                ));
                $this->call->log("The peer of {$this->call} offered a new {$kind->name} channel $ssrc ($codecs)", Logger::VERBOSE);
                $this->recvTransceivers[$ssrc] = $this->peerConnection->addTransceiver($kind, SDPDirections::recvonly);
            } else {
                $this->recvTransceivers[$ssrc]->setDirection(SDPDirections::recvonly);
            }
        }
        foreach ($this->recvTransceivers as $ssrc => $transceiver) {
            if (!isset($this->peerContents[$ssrc]) && $transceiver->getDirection() !== SDPDirections::inactive) {
                $this->call->log("The peer of {$this->call} withdrew its channel $ssrc", Logger::VERBOSE);
                $transceiver->setDirection(SDPDirections::inactive);
            }
        }
    }

    /**
     * Forget the peer offers that crossed our exchange (see {@see self::onV2Negotiation()}): the
     * peer discarded them, and channels it announced only there stay un-negotiated until it offers
     * again (on its next camera/screen-share change).
     */
    private function dropStalePeerOffers(): void
    {
        if ($this->pendingPeerOffers !== []) {
            $this->call->log(\count($this->pendingPeerOffers)." crossed offer(s) of the peer of {$this->call} discarded", Logger::VERBOSE);
            $this->pendingPeerOffers = [];
        }
    }
    /**
     * Drop from each content the payload types our php-rtc build cannot receive, keeping the RTX
     * entries only for codecs that survive. We record incoming media by muxing the raw frames, but
     * the RtpRouter still only routes payload types negotiated from our codec table; a codec we do
     * not carry (e.g. Telegram Android's H265/HEVC), or an H.264 profile/packetization mode we do
     * not accept (Android's hardware encoders offer High profile first), would be echoed as accepted,
     * chosen by the peer, and then dropped packet-by-packet â€” no video, silently. Filtering the answer
     * with exactly the compatibility rule the peer connection applies makes the peer pick a mutual
     * codec we can route and record.
     *
     * @param list<array<array-key, mixed>> $contents
     * @return list<array<array-key, mixed>>
     */
    private function filterSupportedPayloadTypes(array $contents): array
    {
        $codec = new Codec();
        foreach ($contents as &$content) {
            $kind = ($content['type'] ?? null) === 'video' ? 'video' : 'audio';
            $payloadTypes = $content['payloadTypes'] ?? [];
            if (!\is_array($payloadTypes)) {
                continue;
            }
            /** @var list<RTCRtpCodecParameters> $remote */
            $remote = [];
            foreach ($payloadTypes as $payloadType) {
                if (!\is_array($payloadType)) {
                    continue;
                }
                $parameters = [];
                foreach ((array) ($payloadType['parameters'] ?? []) as $key => $value) {
                    $parameters[(string) $key] = \is_scalar($value) ? (string) $value : null;
                }
                $remote[] = new RTCRtpCodecParameters(
                    $kind.'/'.(string) ($payloadType['name'] ?? ''),
                    (int) ($payloadType['clockrate'] ?? 0),
                    ((int) ($payloadType['channels'] ?? 0)) ?: null,
                    (int) ($payloadType['id'] ?? 0),
                    [],
                    $parameters,
                );
            }
            /** @var list<RTCRtpCodecParameters> $local */
            $local = $codec->getCodecs($kind);
            $keptIds = [];
            foreach ($this->peerConnection->findMutualCodecs($local, $remote) as $mutual) {
                if ($mutual->payloadType !== null) {
                    $keptIds[$mutual->payloadType] = true;
                }
            }
            $kept = [];
            foreach ($payloadTypes as $payloadType) {
                if (\is_array($payloadType) && isset($keptIds[(int) ($payloadType['id'] ?? -1)])) {
                    $kept[] = $payloadType;
                }
            }
            $content['payloadTypes'] = $kept;
            // We don't implement transport-cc feedback: see V2Sdp::withoutTransportCc().
            $content = V2Sdp::withoutTransportCc($content);
        }
        unset($content);

        return $contents;
    }

    /**
     * Return the tgcalls `contents` with every payload type's `parameters` forced to a JSON object.
     * An empty `parameters` decodes to a PHP `[]` and would re-encode as a JSON array, which tgcalls'
     * strict parser rejects (it requires an object), dropping the whole message.
     *
     * @param list<array<array-key, mixed>> $contents
     * @return list<array<array-key, mixed>>
     */
    private static function withObjectParameters(array $contents): array
    {
        foreach ($contents as &$content) {
            if (!isset($content['payloadTypes']) || !\is_array($content['payloadTypes'])) {
                continue;
            }
            foreach ($content['payloadTypes'] as &$payloadType) {
                if (\is_array($payloadType)) {
                    $payloadType['parameters'] = (object) ($payloadType['parameters'] ?? []);
                }
            }
            unset($payloadType);
        }
        unset($content);

        return $contents;
    }

    private function sendSignalingMessage(array $message): void
    {
        if ($this->closed) {
            return;
        }
        $data = json_encode($message, JSON_THROW_ON_ERROR);
        if ($this->version->supportsCompression()) {
            $data = TgcallsTools::gzip($data);
        }
        $packet = $this->version->usesReliableFraming()
            ? $this->encryption->prepareForSendingRawMessage($data, true)
            : $this->encryption->encryptRawPacket($data);
        if ($packet === null) {
            return;
        }
        if ($this->sctp !== null) {
            // The association takes care of ordering and retransmission from here on.
            $this->sctp->send($packet);
            return;
        }
        $this->call->sendSignalingData($packet);
    }

    /**
     * Handle an incoming
     * [updatePhoneCallSignalingData](https://core.telegram.org/constructor/updatePhoneCallSignalingData).
     */
    public function onSignaling(string $data): void
    {
        if ($this->closed) {
            return;
        }
        if ($this->sctp !== null) {
            // What arrives here is an SCTP packet; the association hands back whole messages.
            $this->sctp->receive($data);
            return;
        }
        $messages = $this->version->usesReliableFraming()
            ? $this->encryption->handleIncomingRawPacket($data)
            : array_filter([$this->encryption->decryptRawPacket($data)]);
        foreach ($messages as $message) {
            $this->onSignalingMessageData($message);
        }
    }

    /**
     * Decode and dispatch one decrypted signaling message.
     *
     * @internal Public so that {@see SignalingSctpTransport} can deliver reassembled messages.
     */
    #[\Override]
    public function onSignalingMessageData(string $message): void
    {
        if ($this->sctp !== null) {
            // Messages coming off the association are still encrypted.
            $decrypted = $this->encryption->decryptRawPacket($message);
            if ($decrypted === null) {
                return;
            }
            $message = $decrypted;
        }
        {
            try {
                $decoded = json_decode(TgcallsTools::gunzip($message), true, flags: JSON_THROW_ON_ERROR);
            } catch (Throwable $e) {
                $this->call->log("Could not decode a signaling message of {$this->call}: $e", Logger::WARNING);
                return;
            }
            if (!\is_array($decoded) || !isset($decoded['@type'])) {
                return;
            }
            EventLoop::queue($this->onSignalingMessage(...), $decoded);
        }
    }

    private function onSignalingMessage(array $message): void
    {
        $lock = $this->signalingMutex->acquire();
        try {
            $this->handleSignalingMessage($message);
        } finally {
            $lock->release();
        }
    }

    private function handleSignalingMessage(array $message): void
    {
        try {
            switch ($message['@type']) {
                case 'offer':
                case 'answer':
                    $this->onRemoteDescription((string) $message['@type'], (string) $message['sdp']);
                    break;
                case 'candidate':
                    $this->onRemoteCandidate($message);
                    break;
                case 'MediaState':
                    $this->remoteMediaState = MediaState::fromSignaling($message);
                    $this->remoteMediaStateKnown = true;
                    $this->call->log("Peer media state of {$this->call}: ".json_encode($this->remoteMediaState), Logger::VERBOSE);
                    // The recorder shapes its segments after what the peer sends: a stream turned off
                    // ends the current segment, one turned on is waited for.
                    $this->tellRecorderExpectedStreams();
                    break;
                case 'InitialSetup':
                    /** @var array<string, mixed> $message */
                    $this->peerInitialSetup = $message;
                    if (!$this->outgoing) {
                        $this->sendV2InitialSetup();
                    }
                    $pending = $this->pendingV2Messages;
                    $this->pendingV2Messages = [];
                    foreach ($pending as $negotiation) {
                        $this->onV2Negotiation($negotiation);
                    }
                    break;
                case 'NegotiateChannels':
                    $this->onV2Negotiation($message);
                    break;
                case 'Candidates':
                    // The InstanceV2Impl dialect batches candidates into one message.
                    // They belong to the single (bundled) transport, the one of the first media section: the
                    // sdpMLineIndex we add for web clients refers to the media sections of the sender, which don't
                    // match ours, as each party describes its own outgoing channels.
                    foreach ($message['candidates'] ?? [] as $candidate) {
                        $this->onRemoteCandidate(['sdp' => $candidate['sdpString'] ?? '', 'mline' => 0]);
                    }
                    break;
                default:
                    $this->call->log("Ignoring signaling message of type {$message['@type']} in {$this->call}", Logger::VERBOSE);
            }
        } catch (Throwable $e) {
            $this->call->log("Got $e while handling a signaling message in {$this->call}", Logger::ERROR);
        }
    }

    private function onRemoteDescription(string $type, string $sdp): void
    {
        $this->peerConnection->setRemoteDescription(new RTCSessionDescription($sdp, $type));
        if ($type === 'answer') {
            $this->outgoingVideo->setTransportReady(true);
            $this->outgoingScreencast?->setTransportReady(true);
        }
        $this->hasRemoteDescription = true;
        $this->flushPendingCandidates();
        if ($type === 'offer') {
            $this->sendLocalDescription(true);
        }
        $this->flushRenegotiation();
    }

    /**
     * Apply the candidates that arrived before we had a remote description.
     */
    private function flushPendingCandidates(): void
    {
        foreach ($this->pendingCandidates as $candidate) {
            $this->peerConnection->addIceCandidate($candidate);
        }
        $this->pendingCandidates = [];
    }

    private function onRemoteCandidate(array $message): void
    {
        $sdp = (string) ($message['sdp'] ?? '');
        if ($sdp === '') {
            return;
        }
        $candidate = RTCIceCandidate::parseSDP($sdp);
        // All media sections share one transport (BUNDLE), so the m-line index is enough to place the candidate:
        // the mids can't be used, as in the structured dialects ours are SSRCs, which the peer's candidates don't carry.
        $candidate->setSdpMLineIndex((int) ($message['mline'] ?? 0));
        if (!$this->hasRemoteDescription) {
            $this->pendingCandidates[] = $candidate;
            return;
        }
        $this->peerConnection->addIceCandidate($candidate);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Tgcalls;

/**
 * A big-endian bit reader with Exp-Golomb support, used to parse the few codec headers
 * ({@see CallRecorder} reads H.264 SPS and VP9 uncompressed headers) needed to describe a recorded
 * video track. Pure PHP, no dependencies.
 *
 * @internal
 *
 * @psalm-external-mutation-free
 */
final class BitReader
{
    private int $bitPos = 0;
    private int $length;

    /**
     * @psalm-mutation-free
     */
    public function __construct(private readonly string $data)
    {
        $this->length = \strlen($data) * 8;
    }

    /**
     * Read `$count` bits (0-32) as an unsigned integer.
     *
     * @psalm-mutation-free
     */
    public function bits(int $count): int
    {
        $value = 0;
        for ($i = 0; $i < $count; $i++) {
            $value = ($value << 1) | $this->bit();
        }
        return $value;
    }

    /**
     * @psalm-external-mutation-free
     */
    private function bit(): int
    {
        if ($this->bitPos >= $this->length) {
            // Past the end: keep counting so callers can detect the overflow.
            $this->bitPos++;
            return 0;
        }
        $byte = \ord($this->data[$this->bitPos >> 3]);
        $bit = ($byte >> (7 - ($this->bitPos & 7))) & 1;
        $this->bitPos++;
        return $bit;
    }

    /**
     * Read an unsigned Exp-Golomb coded value (ue(v)).
     *
     * @psalm-mutation-free
     */
    /**
     * How many bits were consumed so far (may exceed the data length after reading past its end).
     */
    public function position(): int
    {
        return $this->bitPos;
    }

    /**
     * Whether a read went past the end of the data.
     */
    public function overflowed(): bool
    {
        return $this->bitPos > $this->length;
    }

    public function ue(): int
    {
        $zeros = 0;
        while ($this->bitPos < $this->length && $this->bit() === 0) {
            $zeros++;
        }
        if ($zeros === 0) {
            return 0;
        }
        return (1 << $zeros) - 1 + $this->bits($zeros);
    }

    /**
     * Read a signed Exp-Golomb coded value (se(v)).
     *
     * @psalm-mutation-free
     */
    public function se(): int
    {
        $ue = $this->ue();
        $sign = ($ue & 1) ? 1 : -1;
        return $sign * intdiv($ue + 1, 2);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Tgcalls;

/**
 * Pure-PHP inspection of H.265 (HEVC) Annex B access units, as the RTP depacketizer delivers them:
 * tells them apart from H.264, reads the picture size out of the SPS, builds the
 * HEVCDecoderConfigurationRecord (`hvcC`) a Matroska `V_MPEGH/ISO/HEVC` track needs, and converts
 * the access unit to the length-prefixed form the container stores.
 *
 * @internal
 */
final class HevcBitstream
{
    public const NAL_VPS = 32;
    public const NAL_SPS = 33;
    public const NAL_PPS = 34;
    public const NAL_AUD = 35;
    /** The IRAP range: BLA_W_LP .. CRA_NUT, the pictures a decoder can start on. */
    private const NAL_IRAP_FIRST = 16;
    private const NAL_IRAP_LAST = 21;

    /**
     * Whether an Annex B access unit is H.265 rather than H.264.
     *
     * The NAL unit headers differ in layout (H.264: 1 byte, H.265: 2 bytes whose second is 0x01 on
     * the base layer), so a parameter set is unambiguous: an H.264 SPS starts with 0x67, an H.265
     * VPS/SPS/PPS with 0x40/0x42/0x44 followed by 0x01. Keyframes carry their parameter sets in-band
     * in WebRTC, so this is decided on the frame that describes the track.
     *
     * @psalm-pure
     */
    public static function isHevc(string $frame): bool
    {
        foreach (self::nalUnits($frame) as $nal) {
            if (\strlen($nal) < 2) {
                continue;
            }
            $type = self::nalType($nal);
            if (\in_array($type, [self::NAL_VPS, self::NAL_SPS, self::NAL_PPS], true) && $nal[1] === "\x01") {
                return true;
            }
            if ((\ord($nal[0]) & 0x1F) === 7 && (\ord($nal[0]) & 0x60) !== 0) {
                return false; // An H.264 SPS.
            }
        }
        return false;
    }

    /**
     * The type of an H.265 NAL unit.
     *
     * @psalm-pure
     */
    public static function nalType(string $nal): int
    {
        return (\ord($nal[0]) >> 1) & 0x3F;
    }

    /**
     * Whether an access unit is a keyframe: an IRAP picture, or one carrying parameter sets.
     *
     * @psalm-pure
     */
    public static function isKeyframe(string $frame): bool
    {
        foreach (self::nalUnits($frame) as $nal) {
            if ($nal === '') {
                continue;
            }
            $type = self::nalType($nal);
            if (($type >= self::NAL_IRAP_FIRST && $type <= self::NAL_IRAP_LAST) || $type === self::NAL_SPS) {
                return true;
            }
        }
        return false;
    }

    /**
     * Describe the stream from a keyframe: picture size and the `hvcC` configuration record.
     *
     * @return array{int, int, string} [width, height, HEVCDecoderConfigurationRecord]
     *
     * @psalm-mutation-free
     */
    public static function describe(string $frame): array
    {
        $sets = [self::NAL_VPS => '', self::NAL_SPS => '', self::NAL_PPS => ''];
        foreach (self::nalUnits($frame) as $nal) {
            if (\strlen($nal) < 3) {
                continue;
            }
            $type = self::nalType($nal);
            if (isset($sets[$type]) && $sets[$type] === '') {
                $sets[$type] = $nal;
            }
        }
        if ($sets[self::NAL_SPS] === '') {
            return [1280, 720, ''];
        }
        $sps = self::parseSps($sets[self::NAL_SPS]);
        return [$sps['width'], $sps['height'], self::hvcc($sps, $sets)];
    }

    /**
     * Convert an Annex B access unit to 4-byte length-prefixed NAL units, dropping access unit
     * delimiters (the container does not store them).
     *
     * @psalm-pure
     */
    public static function toLengthPrefixed(string $frame): string
    {
        $out = '';
        foreach (self::nalUnits($frame) as $nal) {
            if ($nal === '' || self::nalType($nal) === self::NAL_AUD) {
                continue;
            }
            $out .= pack('N', \strlen($nal)).$nal;
        }
        return $out;
    }

    /**
     * Split an Annex B stream into NAL units (without start codes).
     *
     * @return list<string>
     *
     * @psalm-pure
     */
    public static function nalUnits(string $frame): array
    {
        $nals = [];
        $len = \strlen($frame);
        $i = 0;
        $start = -1;
        while ($i + 3 <= $len) {
            if ($frame[$i] === "\x00" && $frame[$i + 1] === "\x00" && $frame[$i + 2] === "\x01") {
                if ($start >= 0) {
                    $end = $i;
                    if ($end > $start && $frame[$end - 1] === "\x00") {
                        $end--;
                    }
                    $nals[] = substr($frame, $start, $end - $start);
                }
                $i += 3;
                $start = $i;
                continue;
            }
            $i++;
        }
        if ($start >= 0 && $start <= $len) {
            $nals[] = substr($frame, $start);
        }
        return $nals;
    }

    /**
     * Read the fields of an SPS that the container needs.
     *
     * @return array{width: int, height: int, chroma: int, bitDepthLuma: int, bitDepthChroma: int, ptl: string, subLayers: int, nested: int}
     *
     * @psalm-mutation-free
     */
    private static function parseSps(string $sps): array
    {
        $rbsp = self::stripEmulationPrevention(substr($sps, 2));
        $br = new BitReader($rbsp);
        $br->bits(4); // sps_video_parameter_set_id
        $maxSubLayersMinus1 = $br->bits(3);
        $nested = $br->bits(1);
        // profile_tier_level(1, sps_max_sub_layers_minus1): the general part is byte aligned here,
        // 12 bytes: profile space/tier/idc (1), compatibility flags (4), constraint flags (6), level (1).
        $ptl = substr($rbsp, 1, 12);
        $br->bits(8); // profile_space, tier, profile_idc
        $br->bits(32); // general_profile_compatibility_flags
        $br->bits(48); // general constraint flags
        $br->bits(8); // general_level_idc
        $profilePresent = [];
        $levelPresent = [];
        for ($i = 0; $i < $maxSubLayersMinus1; $i++) {
            $profilePresent[$i] = $br->bits(1);
            $levelPresent[$i] = $br->bits(1);
        }
        if ($maxSubLayersMinus1 > 0) {
            for ($i = $maxSubLayersMinus1; $i < 8; $i++) {
                $br->bits(2); // reserved_zero_2bits
            }
        }
        for ($i = 0; $i < $maxSubLayersMinus1; $i++) {
            if ($profilePresent[$i]) {
                $br->bits(88);
            }
            if ($levelPresent[$i]) {
                $br->bits(8);
            }
        }
        $br->ue(); // sps_seq_parameter_set_id
        $chroma = $br->ue();
        if ($chroma === 3) {
            $br->bits(1); // separate_colour_plane_flag
        }
        $width = $br->ue();
        $height = $br->ue();
        if ($br->bits(1)) { // conformance_window_flag
            $left = $br->ue();
            $right = $br->ue();
            $top = $br->ue();
            $bottom = $br->ue();
            $subWidth = ($chroma === 1 || $chroma === 2) ? 2 : 1;
            $subHeight = $chroma === 1 ? 2 : 1;
            $width -= ($left + $right) * $subWidth;
            $height -= ($top + $bottom) * $subHeight;
        }
        $bitDepthLuma = $br->ue() + 8;
        $bitDepthChroma = $br->ue() + 8;
        return [
            'width' => max(1, $width),
            'height' => max(1, $height),
            'chroma' => $chroma,
            'bitDepthLuma' => $bitDepthLuma,
            'bitDepthChroma' => $bitDepthChroma,
            'ptl' => str_pad($ptl, 12, "\x00"),
            'subLayers' => $maxSubLayersMinus1 + 1,
            'nested' => $nested,
        ];
    }

    /**
     * Build the HEVCDecoderConfigurationRecord (ISO/IEC 14496-15 8.3.3.1).
     *
     * @param array{width: int, height: int, chroma: int, bitDepthLuma: int, bitDepthChroma: int, ptl: string, subLayers: int, nested: int} $sps
     * @param array<int, string> $sets The VPS, SPS and PPS NAL units, keyed by type ('' if missing).
     *
     * @psalm-pure
     */
    private static function hvcc(array $sps, array $sets): string
    {
        $record = "\x01".$sps['ptl'];
        $record .= "\xF0\x00"; // min_spatial_segmentation_idc = 0 (with reserved bits set)
        $record .= "\xFC"; // parallelismType = 0
        $record .= \chr(0xFC | ($sps['chroma'] & 0x03));
        $record .= \chr(0xF8 | (($sps['bitDepthLuma'] - 8) & 0x07));
        $record .= \chr(0xF8 | (($sps['bitDepthChroma'] - 8) & 0x07));
        $record .= "\x00\x00"; // avgFrameRate
        // constantFrameRate = 0, numTemporalLayers, temporalIdNested, lengthSizeMinusOne = 3.
        $record .= \chr((($sps['subLayers'] & 0x07) << 3) | (($sps['nested'] & 0x01) << 2) | 0x03);
        $arrays = array_filter($sets, static fn (string $nal): bool => $nal !== '');
        $record .= \chr(\count($arrays));
        foreach ($arrays as $type => $nal) {
            $record .= \chr(0x80 | ($type & 0x3F)).pack('n', 1).pack('n', \strlen($nal)).$nal;
        }
        return $record;
    }

    /**
     * @psalm-pure
     */
    private static function stripEmulationPrevention(string $data): string
    {
        return preg_replace('/\x00\x00\x03/', "\x00\x00", $data) ?? $data;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

// Please keep the above notice the next time you copy my code, or I will sue you :)

namespace danog\MadelineProto\Tgcalls;

use Amp\ByteStream\ReadableStream;
use Amp\ByteStream\WritableStream;
use Amp\Cancellation;
use Amp\Sync\LocalMutex;
use AssertionError;
use danog\MadelineProto\CallStream;
use danog\MadelineProto\EventHandler\Calls\CallStreams;
use danog\MadelineProto\EventHandler\Calls\PrivateCall;
use danog\MadelineProto\Lang;
use danog\MadelineProto\LocalDirectory;
use danog\MadelineProto\LocalFile;
use danog\MadelineProto\Logger;
use danog\MadelineProto\Loop\VoIP\DjLoop;
use danog\MadelineProto\Magic;
use danog\MadelineProto\MediaDestination;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\MTProtoTools\Crypt;
use danog\MadelineProto\Ogg;
use danog\MadelineProto\RecordingEvent;
use danog\MadelineProto\RecordingFormat;
use danog\MadelineProto\RemoteUrl;
use danog\MadelineProto\RestoresDeclaredProperties;
use danog\MadelineProto\RPCError\CallAlreadyAcceptedError;
use danog\MadelineProto\RPCError\CallAlreadyDeclinedError;
use danog\MadelineProto\SecurityException;
use danog\MadelineProto\Tools;
use danog\MadelineProto\VoIP\CallState;
use danog\MadelineProto\VoIP\DiscardReason;
use danog\MadelineProto\VoIP\MediaState;
use danog\MadelineProto\VoIP\SignalingProtocolVersion;
use phpseclib4\Math\BigInteger;
use Revolt\EventLoop;
use Throwable;
use Webmozart\Assert\Assert;

/**
 * @internal
 * @psalm-import-type StreamMask from \danog\MadelineProto\CallStream
 */
final class PrivateCallController implements CallControllerInterface
{
    use RestoresDeclaredProperties;
    /**
     * The [phoneCallProtocol](https://core.telegram.org/constructor/phoneCallProtocol) we advertise.
     *
     * `udp_p2p`, `udp_reflector`, `min_layer` and `max_layer` are deprecated leftovers of libtgvoip
     * and must be sent with these exact hardcoded values, see
     * https://core.telegram.org/api/calls#populating-phonecallprotocol.
     */
    public const CALL_PROTOCOL = [
        '_' => 'phoneCallProtocol',
        'udp_p2p' => true,
        'udp_reflector' => true,
        'min_layer' => 65,
        'max_layer' => 92,
        'library_versions' => SignalingProtocolVersion::SUPPORTED,
    ];

    private CallState $callState;

    private array $call;
    private ?Controller $tgcallsController = null;
    private ?LegacyController $legacyController = null;

    private DjLoop $diskJockey;
    /** The separate video-only playlist for the presentation (screencast) stream, created on first use. */
    private ?DjLoop $presentationDj = null;

    /** Auth key */
    private ?string $authKey = null;

    public readonly PrivateCall $public;
    /** @var ?list{string, string, string, string} */
    private ?array $visualization = null;

    private LocalMutex $authMutex;

    /** The recording requested before the engine existed (or to re-arm the legacy engine after a restart): a file, or null. */
    private ?LocalFile $outputFile = null;
    /** The recording requested before the engine existed: a folder, or null. */
    private ?LocalDirectory $outputFolder = null;

    private ?RecordingFormat $outputFormat = null;
    /**
     * The {@see CallStream} flags requested for the pending file recording, or null for every available stream.
     * @var ?StreamMask
     */
    private ?int $outputStreams = null;

    private bool $muted = false;

    /**
     * Constructor.
     *
     * @internal
     */
    public function __construct(
        public readonly MTProto $API,
        array $call
    ) {
        $this->public = new PrivateCall($API, $call);
        $call['_'] = 'inputPhoneCall';
        $this->diskJockey = new DjLoop($this);
        $this->call = $call;
        if ($this->public->outgoing) {
            $this->callState = CallState::REQUESTED;
            Assert::true($this->diskJockey->start());
        } else {
            // Every session of the account gets the incoming call, and only the one that accepts it is
            // in it: the disk jockey is only started once we accept it (its playlist works before).
            $this->callState = CallState::INCOMING;
        }
        $this->authMutex = new LocalMutex;
    }

    /** Whether the call was restored from a session, and is yet to be resumed by {@see self::resumeAfterRestart()}. */
    private bool $resumePending = false;

    /**
     * @psalm-mutation-free
     */
    public function __serialize(): array
    {
        // A call that never finished constructing (e.g. the process was killed mid-setup) must not be
        // resurrected: persist only an "ended" marker so its half-built subsystems (disk jockey, WebRTC
        // engine, recorders) are dropped from the graph and cannot start crashing loops on resume.
        if (!isset($this->public, $this->callState, $this->call)) {
            return ['callState' => CallState::ENDED];
        }
        $result = get_object_vars($this);
        // The engines (WebRTC and libtgvoip alike) now serialize themselves and resume their
        // connections on wakeup, so they are kept. Only the mutex, which wraps a live suspension
        // queue, is dropped and recreated.
        unset($result['authMutex'], $result['resumePending']);

        return $result;
    }

    /**
     * Resume the call after the process restarted.
     *
     * Called by {@see MTProto::wakeup()} once the MTProto session is reset and connected, never from
     * __unserialize(): by then the whole call graph is restored and callState is known-good, and the
     * requests made while resuming are not lost to the reset of the session.
     *
     * @internal
     */
    public function resumeAfterRestart(): void
    {
        if (!$this->resumePending) {
            return;
        }
        $this->resumePending = false;
        if ($this->callState !== CallState::RUNNING) {
            return;
        }
        // The WebRTC/libtgvoip engine restored itself and its transport resumes on its own. Restart
        // the demuxer/reader, the playback producers and the (file-backed) recorders, which were all
        // left dormant during deserialization.
        $this->diskJockey->resumeReader();
        $this->tgcallsController?->resume();
        // The legacy (libtgvoip) engine still re-arms its recorder from the remembered output.
        if ($this->legacyController !== null) {
            $this->applyPendingOutput();
        }
        $this->log("Resumed $this after a restart of the process.");
    }
    /**
     * Wakeup function.
     */
    public function __unserialize(array $data): void
    {
        $this->authMutex = new LocalMutex;
        $this->restoreDeclaredProperties($data);
        // Defensively drop a call that came back without its full essential state (a half-constructed
        // call that slipped into the session): force it ENDED and start nothing. Because no subsystem
        // starts async work during deserialization any more (that is all deferred to resumeAfterRestart()),
        // this is the single point that decides whether a restored call comes back to life at all.
        if (!isset($this->callState) || !isset($this->public) || !isset($this->call)
            || $this->callState === CallState::ENDED
            // A running call always has an engine (it is created in the same synchronous step that
            // starts the call). One without comes from the releases that ran libtgvoip inside this
            // class: its state (voipState, sockets, timestamps, packet queue...) has no counterpart in
            // the engines and was just dropped, so the call cannot be resumed.
            || ($this->callState === CallState::RUNNING && $this->tgcallsController === null && $this->legacyController === null)
        ) {
            $this->callState = CallState::ENDED;
            return;
        }
        if (!isset($this->API->logger)) {
            $this->API->setupLogger();
        }
        $this->diskJockey ??= new DjLoop($this);
        if ($this->callState !== CallState::INCOMING) {
            Assert::true($this->diskJockey->start());
        }
        // Resumed by MTProto::wakeup() once the MTProto session is reset and connected, see resumeAfterRestart():
        // deserialization must stay synchronous, and resuming reopens files, starts loops and makes requests.
        $this->resumePending = true;
    }

    /**
     * Confirm requested call.
     * @internal
     */
    public function confirm(array $params): bool
    {
        $lock = $this->authMutex->acquire();
        try {
            if ($this->callState !== CallState::REQUESTED) {
                $this->log(\sprintf(Lang::$current_lang['call_error_2'], $this->public->callID));
                return false;
            }
            $this->log(\sprintf(Lang::$current_lang['call_confirming'], $this->public->otherID), Logger::VERBOSE);
            $dh_config = $this->API->getDhConfig();
            $params['g_b'] = new BigInteger((string) $params['g_b'], 256);
            Crypt::checkG($params['g_b'], $dh_config['p']);
            $key = str_pad($params['g_b']->powMod($this->call['a'], $dh_config['p'])->toBytes(), 256, \chr(0), STR_PAD_LEFT);
            try {
                $res = ($this->API->methodCallAsyncRead('phone.confirmCall', [
                    'key_fingerprint' => substr(sha1($key, true), -8),
                    'peer' => ['id' => $params['id'], 'access_hash' => $params['access_hash'], '_' => 'inputPhoneCall'],
                    'g_a' => $this->call['g_a'],
                    'protocol' => self::CALL_PROTOCOL,
                ]))['phone_call'];
            } catch (CallAlreadyAcceptedError) {
                $this->log(\sprintf(Lang::$current_lang['call_already_accepted'], $params['id']));
                return true;
            } catch (CallAlreadyDeclinedError) {
                // Already ended by the other party: nothing to discard.
                $this->log(Lang::$current_lang['call_already_declined']);
                $this->end();
                return false;
            }
            \assert(isset($this->call['g_a']) && \is_string($this->call['g_a']));
            $this->visualization = self::computeVisualization($key, $this->call['g_a']);
            $this->authKey = $key;
            $this->callState = CallState::RUNNING;
            $this->initialize($res);
            return true;
        } finally {
            EventLoop::queue($lock->release(...));
        }
    }
    /**
     * Accept incoming call.
     */
    public function accept(?Cancellation $cancellation = null): self
    {
        $lock = $this->authMutex->acquire();
        try {
            // An outgoing call has nothing to accept: we are already in it.
            if ($this->public->outgoing || $this->callState === CallState::RUNNING || $this->callState === CallState::ENDED) {
                return $this;
            }
            Assert::eq($this->callState->name, CallState::INCOMING->name);

            $this->log(\sprintf(Lang::$current_lang['accepting_call'], $this->public->otherID), Logger::VERBOSE);
            $dh_config = $this->API->getDhConfig($cancellation);
            if ($this->callState !== CallState::INCOMING) {
                // Ended meanwhile: discarded, or expired, see expireAfterRestart().
                return $this;
            }
            $this->log('Generating b...', Logger::VERBOSE);
            $b = BigInteger::randomRange(Magic::$two, $dh_config['p']->subtract(Magic::$two));
            $g_b = $dh_config['g']->powMod($b, $dh_config['p']);
            Crypt::checkG($g_b, $dh_config['p']);

            $this->callState = CallState::ACCEPTED;
            // Persisted along with the state: if the process restarts while phone.acceptCall is in flight,
            // the server may still accept the call, and the phoneCall that follows completes it with b.
            $this->call['b'] = $b;
            $this->diskJockey->start();
            try {
                $this->API->methodCallAsyncRead('phone.acceptCall', [
                    'peer' => [
                        'id' => $this->call['id'],
                        'access_hash' => $this->call['access_hash'],
                        '_' => 'inputPhoneCall',
                    ],
                    'g_b' => $g_b->toBytes(),
                    'protocol' => self::CALL_PROTOCOL,
                    'cancellation' => $cancellation,
                ]);
            } catch (CallAlreadyAcceptedError) {
                // Accepted by another session of the account, which is in the call: leave it alone.
                $this->log(\sprintf(Lang::$current_lang['call_already_accepted'], $this->public->callID));
                $this->end();
                return $this;
            } catch (CallAlreadyDeclinedError) {
                // Already ended (by the caller, or declined by another session): nothing to discard.
                $this->log(Lang::$current_lang['call_already_declined']);
                $this->end();
                return $this;
            }

            return $this;
        } finally {
            EventLoop::queue($lock->release(...));
        }
    }

    /**
     * Complete call handshake.
     *
     * @internal
     */
    public function complete(array $params): bool
    {
        $lock = $this->authMutex->acquire();
        try {
            if ($this->callState !== CallState::ACCEPTED || !isset($this->call['b'])) {
                return false;
            }

            $this->log(\sprintf(Lang::$current_lang['call_completing'], $this->public->otherID), Logger::VERBOSE);
            $dh_config = $this->API->getDhConfig();
            if (hash('sha256', (string) $params['g_a_or_b'], true) !== (string) $this->call['g_a_hash']) {
                throw new SecurityException('Invalid g_a!');
            }
            $params['g_a_or_b'] = new BigInteger((string) $params['g_a_or_b'], 256);
            Crypt::checkG($params['g_a_or_b'], $dh_config['p']);
            $key = str_pad($params['g_a_or_b']->powMod($this->call['b'], $dh_config['p'])->toBytes(), 256, \chr(0), STR_PAD_LEFT);
            if (substr(sha1($key, true), -8) != $params['key_fingerprint']) {
                throw new SecurityException(Lang::$current_lang['fingerprint_invalid']);
            }
            $this->visualization = self::computeVisualization(
                $key,
                str_pad($params['g_a_or_b']->toBytes(), 256, \chr(0), STR_PAD_LEFT)
            );
            $this->authKey = $key;
            $this->callState = CallState::RUNNING;
            $this->initialize($params);
            return true;
        } finally {
            EventLoop::queue($lock->release(...));
        }
    }

    /**
     * Compute the four emojis used to verify the call key.
     *
     * @return list{string, string, string, string}
     */
    private static function computeVisualization(string $key, string $g_a): array
    {
        $visualization = [];
        $length = new BigInteger(\count(Magic::$emojis));
        foreach (str_split(hash('sha256', $key.$g_a, true), 8) as $number) {
            $number[0] = \chr(\ord($number[0]) & 0x7f);
            $visualization[] = Magic::$emojis[(int) (new BigInteger($number, 256))->divide($length)[1]->toString()];
        }
        /** @var list{string, string, string, string} */
        return $visualization;
    }

    /**
     * Hand off the call to the WebRTC engine.
     */
    private function initialize(array $call): void
    {
        \assert($this->authKey !== null);
        $version = SignalingProtocolVersion::fromProtocol($call['protocol'] ?? []);
        if ($version === null) {
            $advertised = implode(', ', array_map(
                static fn (mixed $v): string => (string) $v,
                (array) ($call['protocol']['library_versions'] ?? [])
            ));
            $this->log(
                "Cannot set up $this: the other party only supports the tgcalls protocol versions "
                ."[$advertised], while MadelineProto implements ["
                .implode(', ', SignalingProtocolVersion::supported()).'].',
                Logger::ERROR
            );
            $this->discard(DiscardReason::DISCONNECTED);
            return;
        }
        if ($version->isLegacy()) {
            $this->log("Starting the libtgvoip engine of $this using protocol {$version->value}", Logger::NOTICE);
            $this->diskJockey->setFixedOggGrid();
            $this->legacyController = new LegacyController(
                $this,
                $this->authKey,
                $this->public->outgoing,
                $this->diskJockey,
                $call['connections'] ?? [],
            );
            $this->applyPendingOutput();
            return;
        }
        $this->log("Starting the WebRTC engine of $this using tgcalls protocol {$version->value}", Logger::NOTICE);
        $this->tgcallsController = new Controller(
            $this,
            $this->authKey,
            $this->public->outgoing,
            $version,
            $this->diskJockey,
            $call['connections'] ?? [],
        );
        $this->applyPendingOutput();
        // A presentation playlist requested before the engine existed is attached now, before the
        // first offer goes out, so that the screencast is in it from the start.
        if ($this->presentationDj !== null) {
            $this->tgcallsController->enablePresentation($this->presentationDj);
        }
        $this->tgcallsController->start();
    }

    /**
     * Deliver an outgoing signaling packet to the other party.
     *
     * @internal
     */
    public function sendSignalingData(string $data): void
    {
        if ($this->callState === CallState::ENDED) {
            return;
        }
        EventLoop::queue(function () use ($data): void {
            try {
                $this->API->methodCallAsyncRead('phone.sendSignalingData', [
                    'peer' => $this->call,
                    'data' => $data,
                ]);
            } catch (Throwable $e) {
                $this->log("Could not send signaling data for $this: $e", Logger::WARNING);
            }
        });
    }

    /**
     * Handle an incoming signaling packet.
     *
     * @internal
     */
    public function onSignaling(string $data): void
    {
        $this->tgcallsController?->onSignaling($data);
    }

    /**
     * Handle a [phoneCallDiscarded](https://core.telegram.org/constructor/phoneCallDiscarded)
     * coming from the other party.
     *
     * @internal
     */
    public function onDiscarded(array $call): void
    {
        $reason = $call['reason'] ?? null;
        if ($reason !== null) {
            $this->public->discardReason = DiscardReason::tryFrom($reason['_']);
            // A call migrated to a conference carries the slug of the newly created conference,
            // see https://core.telegram.org/api/calls#migrating-to-a-conference-call.
            $this->public->conferenceSlug = $reason['slug'] ?? null;
        }
        // The call is already over: end it locally without echoing a phone.discardCall, as TDLib and
        // tdesktop do. An incoming call is also discarded for the other sessions of the account when one
        // of them accepts it (tdesktop's EndedByOtherDevice): echoing the discard from them would hang up
        // the call of the session that is in it.
        $this->end();
    }

    /**
     * Called by the WebRTC engine when the connection is irrecoverably broken.
     *
     * @internal
     */
    public function onConnectionFailed(): void
    {
        if ($this->callState === CallState::ENDED) {
            return;
        }
        $this->log("The WebRTC connection of $this failed, discarding the call!", Logger::ERROR);
        $this->discard(DiscardReason::DISCONNECTED);
    }

    /**
     * Get call emojis (will return null if the call is not inited yet).
     *
     * @return ?list{string, string, string, string}
     */
    public function getVisualization(): ?array
    {
        return $this->visualization;
    }

    /**
     * Get the media state of the other party.
     *
     * @psalm-mutation-free
     */
    public function getRemoteMediaState(): ?MediaState
    {
        return $this->tgcallsController?->getRemoteMediaState();
    }

    /**
     * Discard call.
     *
     * @param int<1, 5> $rating  Call rating in stars
     * @param string    $comment Additional comment on call quality.
     */
    public function discard(DiscardReason $reason = DiscardReason::HANGUP, ?int $rating = null, ?string $comment = null, ?string $conferenceSlug = null): self
    {
        if ($this->callState === CallState::ENDED) {
            return $this;
        }
        $this->API->waitForInit();
        $this->end();

        $this->log(\sprintf(Lang::$current_lang['call_discarding'], $this->public->callID), Logger::VERBOSE);
        try {
            $this->API->methodCallAsyncRead('phone.discardCall', [
                'video' => $this->public->video,
                'peer' => $this->call,
                'duration' => time() - $this->public->date,
                'connection_id' => 0,
                'reason' => ['_' => $reason->value] + ($conferenceSlug !== null ? ['slug' => $conferenceSlug] : []),
            ]);
        } catch (CallAlreadyAcceptedError|CallAlreadyDeclinedError) {
        }
        if ($rating !== null) {
            $this->log('Setting rating for call '.$this->public->callID.'...', Logger::VERBOSE);
            $this->API->methodCallAsyncRead('phone.setCallRating', ['peer' => $this->call, 'rating' => $rating, 'comment' => $comment]);
        }
        return $this;
    }

    /**
     * End the call locally, without making any request.
     */
    private function end(): void
    {
        if ($this->callState === CallState::ENDED) {
            return;
        }
        $this->API->cleanupCall($this->public->callID);
        $this->callState = CallState::ENDED;
        $this->diskJockey->discard();
        $this->skip();

        $this->log("Now closing $this");
        $this->tgcallsController?->close();
        $this->tgcallsController = null;
        $this->legacyController?->close();
        $this->legacyController = null;
    }

    /**
     * End an incoming call we never answered once it can no longer be ringing, without making any
     * request: it was answered or declined by another session of the account, or ended while this
     * one was not running (the end of a call is only notified to running sessions).
     *
     * @internal
     *
     * @param int $ringTimeout How long an incoming call rings at most, in seconds.
     *
     * @return bool Whether the call was ended.
     */
    public function forgetIfUnanswered(int $ringTimeout): bool
    {
        if (!$this->isUnanswered() || time() - $this->public->date <= $ringTimeout) {
            return false;
        }
        $this->log("Forgetting $this, which was not answered by this session");
        $this->end();
        return true;
    }

    /**
     * @psalm-mutation-free
     */
    private function isUnanswered(): bool
    {
        return $this->callState === CallState::INCOMING
            // Left behind by a failed phone.acceptCall of older versions (accept() now sets b first).
            || ($this->callState === CallState::ACCEPTED && !isset($this->call['b']));
    }

    /**
     * Expire a call restored from the session whose end we may have missed (the updates of a call are
     * only sent to running sessions), without making any request before it times out, see
     * {@see MTProto::wakeup()}.
     *
     * @internal
     *
     * @param int $ringTimeout    How long an incoming call rings at most, in seconds.
     * @param int $receiveTimeout How long the key exchange of an accepted call takes at most, in seconds.
     */
    public function expireAfterRestart(int $ringTimeout, int $receiveTimeout): void
    {
        if ($this->forgetIfUnanswered($ringTimeout)) {
            return;
        }
        if ($this->isUnanswered()) {
            // Still ringing: forget it once it can no longer ring, unless we answer it.
            $delay = max(0, $this->public->date + $ringTimeout - time()) + 1;
            EventLoop::unreference(EventLoop::delay($delay, function () use ($ringTimeout): void {
                $this->forgetIfUnanswered($ringTimeout);
            }));
        } elseif ($this->callState === CallState::ACCEPTED) {
            // Restarted between our phone.acceptCall and the phoneCall that completes the key exchange,
            // which may have been sent while we were not running: as TDLib, give up once
            // call_receive_timeout_ms expires. We accepted the call: discard it.
            EventLoop::unreference(EventLoop::delay($receiveTimeout, function (): void {
                if ($this->callState !== CallState::ACCEPTED) {
                    return;
                }
                $this->log("The key exchange of $this did not complete, discarding it", Logger::WARNING);
                try {
                    $this->discard(DiscardReason::DISCONNECTED);
                } catch (Throwable $e) {
                    $this->log("Could not discard $this: $e", Logger::ERROR);
                }
            }));
        }
    }

    #[\Override]
    public function log(string $message, int $level = Logger::NOTICE): void
    {
        $this->API->logger->logger($message, $level);
    }

    #[\Override]
    public function getVoIPSettings(): \danog\MadelineProto\Settings\VoIP
    {
        return $this->API->getSettings()->getVoip();
    }

    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function isCallEnded(): bool
    {
        return $this->callState === CallState::ENDED;
    }

    /**
     * Record the incoming media into one file (or stream) with a fixed set of tracks, see
     * {@see Controller::setOutput()}. Before the engine exists (the call is still connecting) the
     * request is remembered and applied once it starts; the legacy libtgvoip engine records audio only.
     *
     * @param ?StreamMask $streams The {@see CallStream} flags to record, or null for every stream the peer currently sends.
     *
     * @return StreamMask The streams the peer currently sends, as {@see CallStream} flags (0 while unknown).
     */
    public function setOutput(LocalFile|WritableStream $file, ?RecordingFormat $format = null, ?int $streams = null): int
    {
        if ($streams !== null) {
            CallStream::validate($streams);
        }
        if ($this->legacyController !== null) {
            if ($streams !== null && ($streams & ~CallStream::AUDIO) !== 0) {
                throw new \InvalidArgumentException('The legacy libtgvoip engine carries audio only: choose CallStream::AUDIO.');
            }
            if ($format !== null && $format->isMatroska()) {
                throw new \InvalidArgumentException('The legacy libtgvoip engine records audio only, to OGG OPUS: use RecordingFormat::Opus.');
            }
        }
        $this->outputFile = $file instanceof LocalFile ? $file : null;
        $this->outputFolder = null;
        $this->outputFormat = $format;
        $this->outputStreams = $streams;
        if ($this->tgcallsController !== null) {
            return $this->tgcallsController->setOutput($file, $format, $streams);
        }
        if ($this->legacyController !== null) {
            $this->legacyController->setOutput($file);
            return CallStream::AUDIO;
        }
        return 0;
    }

    /**
     * Record the incoming media into a directory, as a numbered series of `<n>_<streams>.mkv` files,
     * one per combination of streams the peer sends, see {@see Controller::setOutputFolder()}.
     */
    public function setOutputFolder(LocalDirectory $dir, ?RecordingFormat $format = null): void
    {
        $this->outputFile = null;
        $this->outputFolder = $dir;
        $this->outputFormat = $format;
        $this->outputStreams = null;
        if ($this->tgcallsController !== null) {
            $this->tgcallsController->setOutputFolder($dir, $format);
        } elseif ($this->legacyController !== null) {
            $this->legacyController->setOutput(self::legacyFolderFile($dir));
        }
    }

    /**
     * Hand the remembered recording request to the engine that just started (or came back).
     */
    private function applyPendingOutput(): void
    {
        try {
            if ($this->outputFolder !== null) {
                $this->tgcallsController?->setOutputFolder($this->outputFolder, $this->outputFormat);
                $this->legacyController?->setOutput(self::legacyFolderFile($this->outputFolder));
            } elseif ($this->outputFile !== null) {
                $this->tgcallsController?->setOutput($this->outputFile, $this->outputFormat, $this->outputStreams);
                $this->legacyController?->setOutput($this->outputFile);
            }
        } catch (Throwable $e) {
            $this->log("Could not start the recording of $this: $e", Logger::ERROR);
        }
    }

    /**
     * The OGG file the legacy audio-only engine records into when a folder was requested.
     */
    private static function legacyFolderFile(LocalDirectory $dir): LocalFile
    {
        return new LocalFile($dir->create().'/audio.ogg');
    }

    /**
     * The peer's streams or codecs changed, or a recording of them ended: emit a
     * {@see CallStreams} update.
     *
     * @param array<int, string> $codecs By {@see CallStream} flag.
     *
     * @internal
     */
    public function onStreamsChanged(int $streams, array $codecs, ?RecordingEvent $recording, ?LocalFile $file): void
    {
        CallStreamsDispatcher::dispatch($this->API, $this->public, $this->public->otherID, $streams, $codecs, $recording, $file);
    }

    /**
     * The playlist engine for a given media stream: the main disk jockey for {@see MediaDestination::Camera},
     * or a separate video-only one for {@see MediaDestination::Presentation} (the screencast), created and
     * attached to the WebRTC engine on first use.
     */
    private function dj(MediaDestination $dest): DjLoop
    {
        if ($dest === MediaDestination::Camera) {
            return $this->diskJockey;
        }
        if ($this->presentationDj === null) {
            $this->presentationDj = new DjLoop($this, videoOnly: true);
            Assert::true($this->presentationDj->start());
            $this->tgcallsController?->enablePresentation($this->presentationDj);
        }
        return $this->presentationDj;
    }

    /**
     * Start sharing a screen: bring up the presentation (screencast) playlist and attach it to the
     * WebRTC engine as a separate outgoing video stream. Idempotent; the screencast is advertised to
     * the peer as soon as a file with video plays on it.
     */
    public function enablePresentation(): void
    {
        $this->dj(MediaDestination::Presentation);
    }

    /**
     * Stop sharing the screen: stop the presentation playlist, which makes the engine advertise the
     * screencast as inactive. The playlist stays attached, ready for the next presentation playback.
     */
    public function disablePresentation(): void
    {
        $this->presentationDj?->stopPlaying();
    }

    /**
     * Whether a screencast is currently being transmitted.
     *
     * @psalm-mutation-free
     */
    public function isSharingScreen(): bool
    {
        return $this->tgcallsController?->isScreencastActive() ?? false;
    }

    /**
     * Mute or unmute the outgoing audio stream.
     */
    public function setMuted(bool $muted): void
    {
        if ($this->muted === $muted) {
            return;
        }
        $this->muted = $muted;
        if ($muted) {
            $this->pause();
        } else {
            $this->resume();
        }
        $this->tgcallsController?->sendMediaState($muted);
    }

    /**
     * Whether the outgoing audio stream is muted.
     */
    public function isMuted(): bool
    {
        return $this->muted;
    }

    /**
     * Play a file, transmitting its audio and, if it carries a transmittable one, its video.
     */
    public function play(LocalFile|RemoteUrl|ReadableStream $file, MediaDestination $dest = MediaDestination::Camera): void
    {
        $this->validateForLegacyEngine($file);
        $this->dj($dest)->play($file);
    }

    /**
     * The libtgvoip engine of a legacy (2.x) call can only play OGG OPUS encoded the way MadelineProto
     * or @libtgvoipbot do, unless realtime conversion is available; check that up front for it. The
     * WebRTC engine demuxes WebM/MKV and OGG itself and needs no such check.
     */
    private function validateForLegacyEngine(LocalFile|RemoteUrl|ReadableStream $file): void
    {
        if ($this->legacyController === null || Tools::canConvertOgg()) {
            return;
        }
        if ($file instanceof ReadableStream) {
            throw new AssertionError('The passed stream was not generated by MadelineProto or @libtgvoipbot, please pre-convert it using @libtgvoipbot or install FFI and ffmpeg to perform realtime conversion!');
        }
        Ogg::validateOgg($file);
    }

    /**
     * When called, skips to the next file in the playlist.
     */
    public function skip(MediaDestination $dest = MediaDestination::Camera): void
    {
        $this->dj($dest)->skip();
    }
    /**
     * Stops playing all files, clears the main and the hold playlist.
     */
    public function stop(MediaDestination $dest = MediaDestination::Camera): void
    {
        $this->dj($dest)->stopPlaying();
    }
    /**
     * Pauses the currently playing file.
     */
    public function pause(MediaDestination $dest = MediaDestination::Camera): void
    {
        $this->dj($dest)->pausePlaying();
    }
    /**
     * Resumes the currently playing file.
     */
    public function resume(MediaDestination $dest = MediaDestination::Camera): void
    {
        $this->dj($dest)->resumePlaying();
    }
    /**
     * Whether the file we're currently playing is paused.
     */
    public function isPaused(MediaDestination $dest = MediaDestination::Camera): bool
    {
        return $this->dj($dest)->isAudioPaused();
    }
    /**
     * Files to play on hold.
     */
    public function playOnHold(MediaDestination $dest = MediaDestination::Camera, LocalFile|RemoteUrl|ReadableStream ...$files): void
    {
        foreach ($files as $file) {
            $this->validateForLegacyEngine($file);
        }
        $this->dj($dest)->playOnHold(...$files);
    }
    /**
     * Get info about the media currently being played.
     */
    public function getCurrent(MediaDestination $dest = MediaDestination::Camera): LocalFile|RemoteUrl|string|null
    {
        return $this->dj($dest)->getCurrent();
    }

    /**
     * Get call state.
     */
    public function getCallState(): CallState
    {
        return $this->callState;
    }

    /**
     * Get call representation.
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function __toString(): string
    {
        return isset($this->public) ? $this->public->__toString() : 'ended call';
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

// IMPORTANT NOTE: Please keep the above copyright notice intact if copying or rewriting this file in another language.

namespace danog\MadelineProto\Tgcalls;

use danog\MadelineProto\MTProtoTools\Crypt;
use Revolt\EventLoop;

/**
 * Reliable, encrypted, sequenced message layer used by the tgcalls signaling channel.
 *
 * This is a port of tgcalls' `EncryptedConnection` (`Type::Signaling` variant, raw message mode),
 * used to carry the JSON signaling messages of one-to-one calls over
 * [phone.sendSignalingData](https://core.telegram.org/method/phone.sendSignalingData).
 *
 * @internal
 */
final class EncryptedConnection
{
    private const SINGLE_MESSAGE_PACKET_SEQ_BIT = 1 << 31;
    private const MESSAGE_REQUIRES_ACK_SEQ_BIT = 1 << 30;
    private const MAX_ALLOWED_COUNTER = 0x3FFFFFFF;

    private const ACK_SERIALIZED_SIZE = 5;
    private const NOT_ACKED_MESSAGES_LIMIT = 64 * 1024;
    private const MAX_INCOMING_PACKET_SIZE = 128 * 1024;
    private const MAX_SIGNALING_PACKET_SIZE = 16 * 1024;
    private const KEEP_INCOMING_COUNTERS_COUNT = 64;
    private const MAX_MESSAGE_SIZE = 1024 * 1024;

    /** Service packet was requested because we have postponed ACKs to flush. */
    public const SERVICE_CAUSE_ACKS = 1;
    /** Service packet was requested because we have unacked messages to resend. */
    public const SERVICE_CAUSE_RESEND = 2;

    private const ACK_ID = 255;
    private const EMPTY_ID = 254;
    private const CUSTOM_ID = 127;

    private const MIN_DELAY_BEFORE_MESSAGE_RESEND = 3.0;
    private const MAX_DELAY_BEFORE_MESSAGE_RESEND = 5.0;
    private const MAX_DELAY_BEFORE_ACK_RESEND = 5.0;

    private int $counter = 0;

    /**
     * Messages we sent that were not ACKed yet, as `[serialized, lastSentTimestamp]`.
     *
     * @var list<array{string, float}>
     */
    private array $notYetAckedMessages = [];
    /**
     * Sequence numbers of incoming messages whose ACK we still have to send.
     *
     * @var list<int>
     */
    private array $acksToSendSeqs = [];
    /**
     * Sorted list of counters of incoming messages we already ACKed.
     *
     * @var list<int>
     */
    private array $acksSentCounters = [];
    /**
     * Sorted list of the largest incoming counters we've seen, for replay protection.
     *
     * @var list<int>
     */
    private array $largestIncomingCounters = [];

    private bool $sendAcksTimerActive = false;
    private bool $resendTimerActive = false;

    /**
     * @param string                   $authKey  The 256-byte call auth key.
     * @param bool                      $outgoing Whether we are the caller.
     * @param SignalingServiceObserver $owner    Notified via
     *                             {@see SignalingServiceObserver::onServiceRequest()} with a
     *                             `SERVICE_CAUSE_*` constant when a service packet must be emitted.
     *                             A serializable object rather than a `Closure`, so the reliability
     *                             layer survives the serialize/unserialize cycle intact.
     *
     * @psalm-mutation-free
     */
    public function __construct(
        private readonly string $authKey,
        private readonly bool $outgoing,
        private readonly SignalingServiceObserver $owner,
    ) {
    }

    /**
     * The reliability layer is plain serializable state, but its ACK and resend timers are
     * event-loop callbacks that do not survive serialization. Clear the "timer active" flags and,
     * if anything is still waiting to be acked or resent, arm a fresh service packet so delivery
     * resumes on the restored connection.
     */
    public function __wakeup(): void
    {
        $this->sendAcksTimerActive = false;
        $this->resendTimerActive = false;
        if ($this->haveAdditionalMessages()) {
            $this->resendTimerActive = true;
            $this->scheduleService(self::MAX_DELAY_BEFORE_MESSAGE_RESEND, self::SERVICE_CAUSE_RESEND);
        }
    }

    /**
     * Serialize, frame and encrypt a raw signaling message.
     *
     * @return ?string The full encrypted packet, or null if it could not be prepared.
     */
    public function prepareForSendingRawMessage(string $message, bool $requiresAck): ?string
    {
        // If a message requires an ACK it may later be resent as part of a bigger packet,
        // so it can't be serialized as a single-message packet.
        $singleMessagePacket = !$this->haveAdditionalMessages() && !$requiresAck;
        $seq = $this->computeNextSeq($requiresAck, $singleMessagePacket);
        if ($seq === null) {
            return null;
        }
        $serialized = pack('N', $seq)
            .\chr(self::CUSTOM_ID)
            .pack('N', \strlen($message))
            .$message;

        if (!$this->enoughSpaceInPacket($serialized, 0)) {
            return null;
        }
        if (!$requiresAck) {
            $serialized = $this->appendAdditionalMessages($serialized);
            return $this->encryptPrepared($serialized);
        }
        $sendEnqueued = $this->notYetAckedMessages !== [];
        if (!$sendEnqueued) {
            $withAdditional = $this->appendAdditionalMessages($serialized);
        }
        $this->notYetAckedMessages[] = [$serialized, microtime(true)];
        if (!$sendEnqueued) {
            \assert(isset($withAdditional));
            return $this->encryptPrepared($withAdditional);
        }
        // All messages requiring an ACK must always be sent in order within one packet,
        // starting with the least recent not-yet-acked one: flush them all in a service packet.
        foreach ($this->notYetAckedMessages as &$queued) {
            $queued[1] = 0.0;
        }
        unset($queued);
        return $this->prepareForSendingService(0);
    }

    /**
     * Encrypt a message without the reliability framing.
     *
     * Newer tgcalls protocol versions carry signaling over an already-reliable channel, so the
     * packet is just a sequence number followed by the payload (tgcalls' `encryptRawPacket`).
     */
    public function encryptRawPacket(string $message): string
    {
        return $this->encryptPrepared(pack('N', ++$this->counter).$message);
    }

    /**
     * Decrypt a message produced by {@see self::encryptRawPacket()}.
     *
     * @return ?string The payload, or null if the packet is forged, replayed or malformed.
     */
    public function decryptRawPacket(string $data): ?string
    {
        $size = \strlen($data);
        if ($size < 21 || $size > self::MAX_INCOMING_PACKET_SIZE) {
            return null;
        }
        $x = Crypt::voipX($this->outgoing, true);
        $msgKey = substr($data, 0, 16);
        [$aesKey, $aesIv] = Crypt::voipKdf($msgKey, $this->authKey, $x);
        $decrypted = Crypt::ctrEncrypt(substr($data, 16), $aesKey, $aesIv);

        $msgKeyLarge = hash('sha256', substr($this->authKey, 88 + $x, 32).$decrypted, true);
        if (!hash_equals(substr($msgKeyLarge, 8, 16), $msgKey)) {
            return null;
        }
        if (\strlen($decrypted) < 4) {
            return null;
        }
        if (!$this->registerIncomingCounter(self::counterFromSeq(self::readSeq($decrypted, 0)))) {
            return null;
        }
        return substr($decrypted, 4);
    }

    /**
     * Build a service packet (an empty message carrying pending ACKs and resends).
     */
    public function prepareForSendingService(int $cause): ?string
    {
        if ($cause === self::SERVICE_CAUSE_ACKS) {
            $this->sendAcksTimerActive = false;
        } elseif ($cause === self::SERVICE_CAUSE_RESEND) {
            $this->resendTimerActive = false;
        }
        if (!$this->haveAdditionalMessages()) {
            return null;
        }
        $seq = $this->computeNextSeq(false, false);
        if ($seq === null) {
            return null;
        }
        $serialized = pack('N', $seq).\chr(self::EMPTY_ID);
        $serialized = $this->appendAdditionalMessages($serialized);
        return $this->encryptPrepared($serialized);
    }

    /**
     * Decrypt an incoming packet and extract all raw signaling messages it carries.
     *
     * @return list<string> The decrypted raw messages, in order (may be empty).
     */
    public function handleIncomingRawPacket(string $data): array
    {
        $size = \strlen($data);
        if ($size < 21 || $size > self::MAX_INCOMING_PACKET_SIZE) {
            return [];
        }
        $x = Crypt::voipX($this->outgoing, true);
        $msgKey = substr($data, 0, 16);
        [$aesKey, $aesIv] = Crypt::voipKdf($msgKey, $this->authKey, $x);
        $decrypted = Crypt::ctrEncrypt(substr($data, 16), $aesKey, $aesIv);

        $msgKeyLarge = hash('sha256', substr($this->authKey, 88 + $x, 32).$decrypted, true);
        if (!hash_equals(substr($msgKeyLarge, 8, 16), $msgKey)) {
            return [];
        }
        if (\strlen($decrypted) < 5) {
            return [];
        }

        $packetSeq = self::readSeq($decrypted, 0);
        $packetCounter = self::counterFromSeq($packetSeq);
        if (!$this->registerIncomingCounter($packetCounter)) {
            // Already handled.
            return [];
        }

        $result = [];
        $offset = 4;
        $currentSeq = $packetSeq;
        $additionalMessage = false;
        $firstMessageRequiringAck = true;
        $newRequiringAckReceived = false;
        while (true) {
            $currentCounter = self::counterFromSeq($currentSeq);
            $singleMessagePacket = ($currentSeq & self::SINGLE_MESSAGE_PACKET_SEQ_BIT) !== 0;
            if ($singleMessagePacket && $additionalMessage) {
                return $result;
            }
            $type = \ord($decrypted[$offset]);
            $offset++;
            if ($type === self::EMPTY_ID) {
                if ($additionalMessage) {
                    return $result;
                }
            } elseif ($type === self::ACK_ID) {
                if (!$additionalMessage) {
                    return $result;
                }
                $this->ackMyMessage($currentSeq);
            } elseif ($type === self::CUSTOM_ID) {
                if (\strlen($decrypted) - $offset < 4) {
                    return $result;
                }
                $length = self::readSeq($decrypted, $offset);
                $offset += 4;
                if ($length > self::MAX_MESSAGE_SIZE || \strlen($decrypted) - $offset < $length) {
                    return $result;
                }
                $message = substr($decrypted, $offset, $length);
                $offset += $length;

                $requiresAck = ($currentSeq & self::MESSAGE_REQUIRES_ACK_SEQ_BIT) !== 0;
                if ($requiresAck) {
                    $skip = !$this->registerSentAck($currentCounter, $firstMessageRequiringAck);
                    $firstMessageRequiringAck = false;
                    if (!$skip) {
                        $newRequiringAckReceived = true;
                    }
                    $this->sendAckPostponed($currentSeq);
                } else {
                    $skip = $additionalMessage
                        && ($currentCounter > $packetCounter || !$this->registerIncomingCounter($currentCounter));
                }
                if (!$skip) {
                    $result[] = $message;
                }
            } else {
                return $result;
            }
            $remaining = \strlen($decrypted) - $offset;
            if ($remaining === 0) {
                break;
            }
            if ($singleMessagePacket || $remaining < 5) {
                break;
            }
            $currentSeq = self::readSeq($decrypted, $offset);
            $offset += 4;
            $additionalMessage = true;
        }

        if ($this->acksToSendSeqs !== []) {
            if ($newRequiringAckReceived) {
                $this->scheduleService(0.0, 0);
            } elseif (!$this->sendAcksTimerActive) {
                $this->sendAcksTimerActive = true;
                $this->scheduleService(self::MAX_DELAY_BEFORE_ACK_RESEND, self::SERVICE_CAUSE_ACKS);
            }
        }

        return $result;
    }

    private function scheduleService(float $delay, int $cause): void
    {
        if ($delay <= 0.0) {
            EventLoop::queue($this->owner->onServiceRequest(...), $cause);
            return;
        }
        EventLoop::delay($delay, fn () => $this->owner->onServiceRequest($cause));
    }

    /**
     * @psalm-mutation-free
     */
    private function haveAdditionalMessages(): bool
    {
        return $this->notYetAckedMessages !== [] || $this->acksToSendSeqs !== [];
    }

    /**
     * @psalm-external-mutation-free
     */
    private function computeNextSeq(bool $requiresAck, bool $singleMessagePacket): ?int
    {
        if ($requiresAck && \count($this->notYetAckedMessages) >= self::NOT_ACKED_MESSAGES_LIMIT) {
            return null;
        }
        if ($this->counter === self::MAX_ALLOWED_COUNTER) {
            return null;
        }
        return (++$this->counter)
            | ($singleMessagePacket ? self::SINGLE_MESSAGE_PACKET_SEQ_BIT : 0)
            | ($requiresAck ? self::MESSAGE_REQUIRES_ACK_SEQ_BIT : 0);
    }

    /**
     * @psalm-pure
     */
    private function enoughSpaceInPacket(string $buffer, int $amount): bool
    {
        return $amount < self::MAX_SIGNALING_PACKET_SIZE
            && 16 + \strlen($buffer) + $amount <= self::MAX_SIGNALING_PACKET_SIZE;
    }

    private function appendAdditionalMessages(string $buffer): string
    {
        // ACKs first.
        $sent = 0;
        foreach ($this->acksToSendSeqs as $seq) {
            if (!$this->enoughSpaceInPacket($buffer, self::ACK_SERIALIZED_SIZE)) {
                break;
            }
            $buffer .= pack('N', $seq).\chr(self::ACK_ID);
            $sent++;
        }
        $this->acksToSendSeqs = \array_slice($this->acksToSendSeqs, $sent);

        if ($this->notYetAckedMessages === []) {
            return $buffer;
        }

        $now = microtime(true);
        foreach ($this->notYetAckedMessages as &$resending) {
            [$data, $lastSent] = $resending;
            $when = $lastSent > 0.0 ? $lastSent + self::MIN_DELAY_BEFORE_MESSAGE_RESEND : 0.0;
            if ($when > $now) {
                break;
            }
            if (!$this->enoughSpaceInPacket($buffer, \strlen($data))) {
                break;
            }
            $buffer .= $data;
            $resending[1] = $now;
        }
        unset($resending);

        if (!$this->resendTimerActive) {
            $this->resendTimerActive = true;
            $this->scheduleService(self::MAX_DELAY_BEFORE_MESSAGE_RESEND, self::SERVICE_CAUSE_RESEND);
        }
        return $buffer;
    }

    private function encryptPrepared(string $buffer): string
    {
        $x = Crypt::voipX(!$this->outgoing, true);
        $msgKeyLarge = hash('sha256', substr($this->authKey, 88 + $x, 32).$buffer, true);
        $msgKey = substr($msgKeyLarge, 8, 16);
        [$aesKey, $aesIv] = Crypt::voipKdf($msgKey, $this->authKey, $x);
        return $msgKey.Crypt::ctrEncrypt($buffer, $aesKey, $aesIv);
    }

    private function registerIncomingCounter(int $incomingCounter): bool
    {
        $list = $this->largestIncomingCounters;
        $largest = $list === [] ? 0 : $list[\count($list) - 1];
        if (\in_array($incomingCounter, $list, true)) {
            return false;
        }
        if ($incomingCounter + self::KEEP_INCOMING_COUNTERS_COUNT <= $largest) {
            // Too old.
            return false;
        }
        $list = array_values(array_filter(
            $list,
            static fn (int $counter): bool => $counter + self::KEEP_INCOMING_COUNTERS_COUNT > $incomingCounter
        ));
        $list[] = $incomingCounter;
        sort($list);
        $this->largestIncomingCounters = $list;
        return true;
    }

    private function registerSentAck(int $counter, bool $firstInPacket): bool
    {
        $list = $this->acksSentCounters;
        $already = \in_array($counter, $list, true);
        if ($firstInPacket) {
            $list = array_values(array_filter($list, static fn (int $v): bool => $v >= $counter));
        }
        if (!$already) {
            $list[] = $counter;
            sort($list);
        }
        $this->acksSentCounters = $list;
        return !$already;
    }

    /**
     * @psalm-external-mutation-free
     */
    private function sendAckPostponed(int $incomingSeq): void
    {
        if (!\in_array($incomingSeq, $this->acksToSendSeqs, true)) {
            $this->acksToSendSeqs[] = $incomingSeq;
        }
    }

    /**
     * @psalm-external-mutation-free
     */
    private function ackMyMessage(int $seq): void
    {
        foreach ($this->notYetAckedMessages as $k => [$data]) {
            if (self::readSeq($data, 0) === $seq) {
                unset($this->notYetAckedMessages[$k]);
                $this->notYetAckedMessages = array_values($this->notYetAckedMessages);
                return;
            }
        }
    }

    /**
     * @psalm-pure
     */
    private static function counterFromSeq(int $seq): int
    {
        return $seq & ~self::SINGLE_MESSAGE_PACKET_SEQ_BIT & ~self::MESSAGE_REQUIRES_ACK_SEQ_BIT;
    }

    /**
     * @psalm-pure
     */
    private static function readSeq(string $data, int $offset): int
    {
        /** @var array{1: int} */
        $res = unpack('N', substr($data, $offset, 4));
        return $res[1];
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Tgcalls;

use danog\MadelineProto\Exception;
use Webrtc\ICE\Enum\IceRole;
use Webrtc\ICE\Listener\IceTransportDataListener;
use Webrtc\ICE\Listener\IceTransportDisconnectListener;
use Webrtc\ICE\Listener\IceTransportStateChangeListener;
use Webrtc\ICE\RTCIceCandidate;
use Webrtc\ICE\RTCIceConnectionInterface;
use Webrtc\ICE\RTCIceGathererInterface;
use Webrtc\ICE\RTCIceParameters;
use Webrtc\ICE\RTCIceTransportInterface;

/**
 * Reports a fixed ICE role to the SCTP stack, which is all it needs from a transport.
 *
 * The signaling association of {@see SignalingSctpTransport} does not run over ICE at all: its
 * packets travel inside API calls. SCTP only consults the ICE role to decide which side acts as
 * the client, so this supplies that one answer and refuses everything else.
 *
 * @internal
 */
final class SignalingIceRole implements RTCIceTransportInterface
{
    /**
     * @psalm-mutation-free
     */
    public function __construct(private readonly IceRole $role)
    {
    }

    #[\Override]
    public function getRole(): IceRole
    {
        return $this->role;
    }

    /**
     * @psalm-pure
     */
    #[\Override]
    public function isRoleSet(): bool
    {
        return true;
    }

    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function setRoleSet(bool $roleSet): void
    {
    }

    /**
     * @psalm-pure
     */
    #[\Override]
    public function send(string $bytes): void
    {
        throw new Exception('The signaling SCTP association does not run over ICE!');
    }

    /**
     * @psalm-pure
     */
    #[\Override]
    public function addRemoteCandidate(RTCIceCandidate $candidate): void
    {
        throw new Exception('The signaling SCTP association does not run over ICE!');
    }

    /**
     * @psalm-pure
     */
    #[\Override]
    public function getIceGatherer(): RTCIceGathererInterface
    {
        throw new Exception('The signaling SCTP association does not run over ICE!');
    }

    /**
     * @psalm-pure
     */
    #[\Override]
    public function getIceConnection(): RTCIceConnectionInterface
    {
        throw new Exception('The signaling SCTP association does not run over ICE!');
    }

    /**
     * @psalm-pure
     */
    #[\Override]
    public function start(RTCIceParameters $remoteIceParameters): void
    {
        throw new Exception('The signaling SCTP association does not run over ICE!');
    }

    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function stop(): void
    {
    }

    /**
     * The signaling association carries its own DATA chunks and never sees application data over ICE,
     * so there is nothing to notify: registering a listener is a no-op.
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function addDataListener(IceTransportDataListener $listener): void
    {
    }

    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function removeDataListener(IceTransportDataListener $listener): void
    {
    }

    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function addDisconnectListener(IceTransportDisconnectListener $listener): void
    {
    }

    /**
     * The signaling association never changes ICE state (it does not run over ICE at all), so
     * there is nothing to notify: registering a state-change listener is a no-op.
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function addStateChangeListener(IceTransportStateChangeListener $listener): void
    {
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

// IMPORTANT NOTE: Please keep the above copyright notice intact if copying or rewriting this file in another language.

namespace danog\MadelineProto\Tgcalls;

/**
 * Notified by {@see EncryptedConnection} when a service packet (ACKs, resends) must be emitted.
 *
 * {@see EncryptedConnection} keeps a reference to its observer as part of its serializable state, so
 * this has to be a real, serializable object rather than a `Closure` (which PHP cannot serialize):
 * the signaling reliability layer survives a serialize/unserialize cycle, and so must its callback.
 *
 * @internal
 * @psalm-mutable
 */
interface SignalingServiceObserver
{
    /**
     * Emit a service packet requested by the reliability layer.
     *
     * @param int $cause One of the `EncryptedConnection::SERVICE_CAUSE_*` constants (0 for an
     *                   immediate flush).
     * @psalm-impure
     */
    public function onServiceRequest(int $cause): void;
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Tgcalls;

use Amp\ByteStream\WritableStream;
use danog\MadelineProto\LocalFile;

/**
 * Told by a {@see CallRecorder} when it opens or finishes an output file (or stream).
 *
 * @internal
 * @psalm-mutable
 */
interface RecordingObserver
{
    /**
     * The recorder wrote the header of a file (or stream): media is recorded into it from now on.
     *
     * @psalm-impure
     */
    public function onRecordingStarted(CallRecorder $recorder, LocalFile|WritableStream $out): void;

    /**
     * The recorder finished a file (or stream): nothing more is written to it.
     *
     * @psalm-impure
     */
    public function onRecordingEnded(CallRecorder $recorder, LocalFile|WritableStream $out): void;

    /**
     * The recorder dropped a video frame, since a track can only start on a keyframe: the sender
     * should be asked for one, as on its own it sends one only every few seconds.
     *
     * @param int $source The source of the frame, as passed to {@see CallRecorder::pushVideoFrame()}.
     *
     * @psalm-impure
     */
    public function onKeyframeNeeded(CallRecorder $recorder, int $source): void;
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Tgcalls;

use Amp\ByteStream\WritableStream;
use danog\MadelineProto\LocalFile;
use danog\MadelineProto\OggWriter;
use danog\MadelineProto\RestoresDeclaredProperties;
use Throwable;

use function Amp\File\openFile;

/**
 * Writes the incoming audio of a call to an OGG OPUS stream.
 *
 * The receiver is put in raw mode, so the OPUS frames that arrive over RTP (pushed in by the
 * {@see IncomingMedia} router, or by the legacy libtgvoip engine) are muxed into OGG exactly as they
 * were sent, with no decode/encode round trip: the recording is bit-identical to what the peer
 * transmitted, it costs almost nothing, and it needs no codec library (and therefore no FFI
 * extension). The resulting file can be played back by MadelineProto as-is.
 *
 * @internal
 */
final class OpusRecorder
{
    use RestoresDeclaredProperties;
    private OggWriter $writer;
    private bool $closed = false;

    public readonly int $streamId;
    public readonly ?LocalFile $file;

    public function __construct(LocalFile|WritableStream $out, ?int $streamId = null, string $description = 'incoming audio stream')
    {
        if ($out instanceof LocalFile) {
            $this->file = $out;
            $out = openFile($out->file, 'w');
        } else {
            $this->file = null;
        }
        $this->streamId = $streamId ?? random_int(-(2**31), (2**31)-1);
        $this->writer = new OggWriter($out, $this->streamId);
        $this->writer->writeHeader(1, OpusPlaybackTrack::CLOCK_RATE, $description);
    }

    /**
     * Drop the (unserializable) OGG writer; {@see self::resume()} reopens the file and continues
     * recording, so an incoming stream survives a serialize/deserialize cycle.
     *
     * @psalm-mutation-free
     */
    public function __serialize(): array
    {
        $vars = get_object_vars($this);
        unset($vars['writer']);
        return $vars;
    }

    /**
     * @psalm-external-mutation-free
     */
    public function __unserialize(array $data): void
    {
        // Synchronous state restoration only â€” no async work here (see resume()). Until resume() runs,
        // $writer stays unset and writeChunk/writeOpus/close guard on isset($this->writer).
        $this->restoreDeclaredProperties($data);
    }

    /**
     * Reopen the file and resume recording after the whole call graph has been deserialized: append a
     * fresh chained Opus stream. Called by the owner's resume(); never during unserialize (opening the
     * file suspends the fiber).
     */
    public function resume(): void
    {
        // Only file-backed recorders are ever serialized (their parent drops stream-backed ones), so
        // there is always a file to reopen; the guard is purely defensive.
        if ($this->closed || $this->file === null || isset($this->writer)) {
            return;
        }
        // Append a fresh chained Opus stream (its own serial) to the existing recording.
        $this->writer = new OggWriter(openFile($this->file->file, 'a'), random_int(-(2**31), (2**31) - 1));
        $this->writer->writeHeader(1, OpusPlaybackTrack::CLOCK_RATE, 'incoming audio stream (resumed)');
    }

    /**
     * Write one bare OPUS frame.
     *
     * @param int $samples The frame's duration in samples at 48 kHz, by which the granule position
     *                     advances: the RTP timestamp delta for WebRTC audio, and 60ms (the fixed
     *                     libtgvoip frame) by default.
     */
    public function writeOpus(string $frame, int $samples = 2880): void
    {
        if ($this->closed || $frame === '' || !isset($this->writer)) {
            return;
        }
        $this->writer->writeChunk($frame, $samples, false);
    }

    /**
     * Flush and close the output stream.
     */
    public function close(): void
    {
        if ($this->closed) {
            return;
        }
        $this->closed = true;
        try {
            if (isset($this->writer)) {
                $this->writer->writeChunk('', 0, true);
            }
        } catch (Throwable) {
        }
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

// IMPORTANT NOTE: Please keep the above copyright notice intact if copying or rewriting this file in another language.

namespace danog\MadelineProto\Tgcalls;

use Amp\ByteStream\WritableStream;
use danog\MadelineProto\CallStream;
use danog\MadelineProto\LocalFile;
use danog\MadelineProto\Logger;
use danog\MadelineProto\Loop\VoIP\DjLoop;
use danog\MadelineProto\RecordingEvent;
use danog\MadelineProto\RestoresDeclaredProperties;
use danog\MadelineProto\VoIP\Endpoint;
use danog\MadelineProto\VoIP\MessageHandler;
use danog\MadelineProto\VoIP\VoIPState;
use Revolt\EventLoop;
use Throwable;

use function Amp\async;
use function Amp\delay;
use function Amp\Future\await;

/**
 * The legacy libtgvoip engine, used when the other party is too old to speak WebRTC.
 *
 * libtgvoip does not use WebRTC at all: it opens plain UDP (or TCP) sockets to Telegram's
 * reflectors, tagging every packet with the `peer_tag` from
 * [phoneConnection](https://core.telegram.org/constructor/phoneConnection), and carries OPUS
 * frames inside its own encrypted packet format. Only versions 2.4.4 and 2.7.7 of the protocol
 * are still negotiated in practice, and only by very old clients.
 *
 * @internal
 */
final class LegacyController
{
    use RestoresDeclaredProperties;

    private VoIPState $voipState = VoIPState::CREATED;
    private MessageHandler $messageHandler;

    /** @var array<string, Endpoint> */
    private array $sockets = [];
    private Endpoint $bestEndpoint;
    private ?string $pendingPing = null;
    private ?string $timeoutWatcher = null;
    private float $lastIncomingTimestamp = 0.0;
    private float $lastOutgoingTimestamp = 0.0;
    private int $opusTimestamp = 0;
    private bool $muted = true;
    private bool $closed = false;

    private ?OpusRecorder $recorder = null;

    public function __construct(
        private readonly PrivateCallController $call,
        private readonly string $authKey,
        private readonly bool $outgoing,
        private readonly DjLoop $dj,
        array $connections,
    ) {
        $this->messageHandler = new MessageHandler(
            $this,
            substr(hash('sha256', $authKey, true), -16)
        );
        $this->initialize($connections);
    }

    /**
     * Drop the state that cannot be serialized before the graph is written out.
     *
     * The reflector {@see Endpoint}s serialize themselves (dropping their live sockets, which they
     * reopen on wakeup). What cannot survive is the OGG recorder's open file handle and the two
     * event-loop watcher IDs; they are dropped here and re-established on wakeup.
     *
     * @return array<string, mixed>
     *
     * @psalm-mutation-free
     */
    public function __serialize(): array
    {
        $vars = get_object_vars($this);
        unset($vars['recorder'], $vars['pendingPing'], $vars['timeoutWatcher']);
        return $vars;
    }

    /**
     * @param array<string, mixed> $data
     */
    public function __unserialize(array $data): void
    {
        $this->recorder = null;
        $this->pendingPing = null;
        $this->timeoutWatcher = null;
        $this->restoreDeclaredProperties($data);
        if ($this->closed) {
            return;
        }
        // The read and write loops are detached event-loop tasks that cannot be serialized: redo the
        // libtgvoip handshake over the reconnecting sockets, which restarts both loops. Recording,
        // if any, is re-attached by PrivateCallController::__unserialize().
        EventLoop::queue(function (): void {
            if ($this->closed) {
                return;
            }
            unset($this->bestEndpoint);
            $this->voipState = VoIPState::WAIT_INIT;
            $this->muted = true;
            $this->connectToAll();
        });
    }

    /**
     * Set the output file or stream for the incoming audio (libtgvoip carries nothing else).
     */
    public function setOutput(LocalFile|WritableStream $file): void
    {
        $this->closeRecorder();
        $this->recorder = new OpusRecorder($file);
        $this->call->onStreamsChanged(CallStream::AUDIO, [CallStream::AUDIO => 'A_OPUS'], RecordingEvent::Started, $this->recorder->file);
    }

    /**
     * Finish the recording, if any, and report it.
     */
    private function closeRecorder(): void
    {
        if ($this->recorder === null) {
            return;
        }
        $file = $this->recorder->file;
        $this->recorder->close();
        $this->recorder = null;
        $this->call->onStreamsChanged(CallStream::AUDIO, [CallStream::AUDIO => 'A_OPUS'], RecordingEvent::Ended, $file);
    }

    /**
     * Tear down every reflector connection.
     */
    public function close(): void
    {
        if ($this->closed) {
            return;
        }
        $this->closed = true;
        $this->closeRecorder();
        if ($this->timeoutWatcher !== null) {
            EventLoop::cancel($this->timeoutWatcher);
            $this->timeoutWatcher = null;
        }
        if ($this->pendingPing !== null) {
            EventLoop::cancel($this->pendingPing);
            $this->pendingPing = null;
        }
        foreach ($this->sockets as $socket) {
            $socket->disconnect();
        }
        $this->sockets = [];
    }

    /**
     * Log a message, used by the packet handlers.
     */
    public function log(string $message, int $level = Logger::NOTICE): void
    {
        $this->call->log($message, $level);
    }

    public function getVoIPState(): VoIPState
    {
        return $this->voipState;
    }

    /**
     * @psalm-mutation-free
     */
    public function __toString(): string
    {
        return (string) $this->call;
    }

    public const NET_TYPE_UNKNOWN = 0;
    public const NET_TYPE_GPRS = 1;
    public const NET_TYPE_EDGE = 2;
    public const NET_TYPE_3G = 3;
    public const NET_TYPE_HSPA = 4;
    public const NET_TYPE_LTE = 5;
    public const NET_TYPE_WIFI = 6;
    public const NET_TYPE_ETHERNET = 7;
    public const NET_TYPE_OTHER_HIGH_SPEED = 8;
    public const NET_TYPE_OTHER_LOW_SPEED = 9;
    public const NET_TYPE_DIALUP = 10;
    public const NET_TYPE_OTHER_MOBILE = 11;

    public const DATA_SAVING_NEVER = 0;
    public const DATA_SAVING_MOBILE = 1;
    public const DATA_SAVING_ALWAYS = 2;

    public const PROXY_NONE = 0;
    public const PROXY_SOCKS5 = 1;

    public const AUDIO_STATE_NONE = -1;
    public const AUDIO_STATE_CREATED = 0;
    public const AUDIO_STATE_CONFIGURED = 1;
    public const AUDIO_STATE_RUNNING = 2;

    public const PKT_INIT = 1;
    public const PKT_INIT_ACK = 2;
    public const PKT_STREAM_STATE = 3;
    public const PKT_STREAM_DATA = 4;
    public const PKT_UPDATE_STREAMS = 5;
    public const PKT_PING = 6;
    public const PKT_PONG = 7;
    public const PKT_STREAM_DATA_X2 = 8;
    public const PKT_STREAM_DATA_X3 = 9;
    public const PKT_LAN_ENDPOINT = 10;
    public const PKT_NETWORK_CHANGED = 11;
    public const PKT_SWITCH_PREF_RELAY = 12;
    public const PKT_SWITCH_TO_P2P = 13;
    public const PKT_NOP = 14;

    public const TLID_DECRYPTED_AUDIO_BLOCK = "\xc1\xdb\xf9\x48";
    public const TLID_SIMPLE_AUDIO_BLOCK = "\x0d\x0e\x76\xcc";

    public const TLID_REFLECTOR_SELF_INFO = "\xC7\x72\x15\xc0";
    public const TLID_REFLECTOR_PEER_INFO = "\x1C\x37\xD9\x27";

    public const PROTO_ID = 'GrVP';

    public const PROTOCOL_VERSION = 9;
    public const MIN_PROTOCOL_VERSION = 9;

    public const STREAM_TYPE_AUDIO = 1;
    public const STREAM_TYPE_VIDEO = 2;

    public const CODEC_OPUS = 'SUPO';

    private function setVoipState(VoIPState $state): bool
    {
        if ($this->voipState->value >= $state->value) {
            return false;
        }
        $old = $this->voipState;
        $this->voipState = $state;
        $this->call->log("Changing state from {$old->name} to {$state->name} in {$this->call}");
        return true;
    }

    private function initialize(array $endpoints): void
    {
        foreach ([true, false] as $udp) {
            foreach ($endpoints as $endpoint) {
                if ($endpoint['_'] !== 'phoneConnection') {
                    continue;
                }
                if (!isset($this->sockets[($udp ? 'udp' : 'tcp').' v6 '.$endpoint['id']])) {
                    $this->sockets[($udp ? 'udp' : 'tcp').' v6 '.$endpoint['id']] = new Endpoint(
                        $udp,
                        '['.$endpoint['ipv6'].']',
                        $endpoint['port'],
                        $endpoint['peer_tag'],
                        true,
                        $this->outgoing,
                        $this->authKey,
                        $this->messageHandler
                    );
                }
                if (!isset($this->sockets[($udp ? 'udp' : 'tcp').' v4 '.$endpoint['id']])) {
                    $this->sockets[($udp ? 'udp' : 'tcp').' v4 '.$endpoint['id']] = new Endpoint(
                        $udp,
                        $endpoint['ip'],
                        $endpoint['port'],
                        $endpoint['peer_tag'],
                        true,
                        $this->outgoing,
                        $this->authKey,
                        $this->messageHandler
                    );
                }
            }
        }
        $this->setVoipState(VoIPState::WAIT_INIT);
        $this->connectToAll();
    }

    private function connectToAll(): void
    {
        $this->timeoutWatcher = EventLoop::repeat(10, function (): void {
            if (microtime(true) - $this->lastIncomingTimestamp > 10) {
                $this->call->onConnectionFailed();
            }
        });

        $promises = [];
        foreach ($this->sockets as $socket) {
            $promise = async(function () use ($socket): void {
                try {
                    $this->call->log("Connecting to $socket...");
                    $socket->connect();
                    $this->call->log("Successfully connected to $socket!");
                    $this->startReadLoop($socket);
                } catch (Throwable $e) {
                    $this->call->log("Got error while connecting to $socket: $e");
                }
            });
            if ((!isset($this->bestEndpoint) && $socket->udp) || (isset($this->bestEndpoint) && $socket === $this->bestEndpoint)) {
                $promises []= $promise;
            }
        }
        await($promises);
    }

    private function handlePacket(Endpoint $socket, array $packet): void
    {
        $cnt = 0;
        switch ($packet['_']) {
            case self::PKT_INIT:
                $this->setVoipState(VoIPState::WAIT_INIT_ACK);
                $socket->write($this->messageHandler->encryptPacket([
                    '_' => self::PKT_INIT_ACK,
                    'protocol' => self::PROTOCOL_VERSION,
                    'min_protocol' => self::MIN_PROTOCOL_VERSION,
                    'all_streams' => [
                        ['id' => 0, 'type' => self::STREAM_TYPE_AUDIO, 'codec' => self::CODEC_OPUS, 'frame_duration' => 60, 'enabled' => 1],
                    ],
                ]));
                $socket->sendInit();
                break;

            case self::PKT_INIT_ACK:
                if ($this->setVoipState(VoIPState::WAIT_PONG)) {
                    $this->pendingPing = EventLoop::repeat(0.2, $this->ping(...));
                }
                break;
            case self::PKT_STREAM_DATA:
                $cnt = 1;
                break;
            case self::PKT_STREAM_DATA_X2:
                $cnt = 2;
                break;
            case self::PKT_STREAM_DATA_X3:
                $cnt = 3;
                break;
            case self::PKT_PING:
                $socket->write($this->messageHandler->encryptPacket(['_' => self::PKT_PONG, 'out_seq_no' => $packet['out_seq_no']]));
                break;
            case self::PKT_PONG:
                if ($this->setVoipState(VoIPState::WAIT_STREAM_INIT)) {
                    EventLoop::cancel($this->pendingPing);
                    $this->pendingPing = null;
                    $this->bestEndpoint ??= $socket;
                    $this->initStream();
                }
                break;
        }
        if ($cnt !== 0 && $this->recorder !== null) {
            unset($packet['_'], $packet['extra']);
            foreach ($packet as ['data' => $data]) {
                $this->recorder->writeOpus($data);
            }
        }
    }

    private function initStream(): void
    {
        $this->bestEndpoint->writeReliably([
            '_' => self::PKT_STREAM_STATE,
            'id' => 0,
            'enabled' => false,
        ]);

        $this->startWriteLoop();
    }

    private function ping(): void
    {
        foreach ($this->sockets as $socket) {
            EventLoop::queue(fn () => $socket->write($this->messageHandler->encryptPacket(['_' => self::PKT_PING])));
        }
    }

    private function startReadLoop(Endpoint $endpoint): void
    {
        EventLoop::queue(function () use ($endpoint): void {
            EventLoop::queue(function () use ($endpoint): void {
                while ($this->voipState->value <= VoIPState::WAIT_INIT_ACK->value) {
                    $this->call->log("Sending PKT_INIT to $endpoint...");
                    if (!$endpoint->sendInit()) {
                        return;
                    }
                    delay(0.5);
                }
            });
            $this->call->log("Started read loop in $endpoint!");
            while (true) {
                try {
                    $payload = $endpoint->read();
                } catch (Throwable $e) {
                    $this->call->log("Got $e in $endpoint, {$this->call}!");
                    continue;
                }
                if (!$payload) {
                    break;
                }
                $this->lastIncomingTimestamp = microtime(true);
                EventLoop::queue($this->handlePacket(...), $endpoint, $payload);
            }
            $this->call->log("Exiting VoIP read loop in $endpoint, {$this->call}!");
        });
    }

    private function startWriteLoop(): void
    {
        $this->setVoipState(VoIPState::ESTABLISHED);

        $delay = $this->muted ? 0.2 : 0.06;
        $t = microtime(true) + $delay;
        while (true) {
            if ($packet = $this->dj->pullPacket()) {
                if ($this->muted) {
                    $this->call->log("Unmuting outgoing audio in {$this->call}!");
                    if (!$this->bestEndpoint->writeReliably([
                        '_' => self::PKT_STREAM_STATE,
                        'id' => 0,
                        'enabled' => true,
                    ])) {
                        $this->call->log("Exiting write loop in {$this->call} because we could not write stream state!");
                        return;
                    }
                    $this->muted = false;
                    $delay = 0.06;
                    $this->opusTimestamp = 0;
                }
                $packet = $this->messageHandler->encryptPacket([
                    '_' => self::PKT_STREAM_DATA,
                    'stream_id' => 0,
                    'data' => $packet,
                    'timestamp' => $this->opusTimestamp,
                ]);
                $this->opusTimestamp += 60;
            } else {
                if (!$this->muted) {
                    $this->call->log("Muting outgoing audio in {$this->call}!");
                    if (!$this->bestEndpoint->writeReliably([
                        '_' => self::PKT_STREAM_STATE,
                        'id' => 0,
                        'enabled' => false,
                    ])) {
                        $this->call->log("Exiting write loop in {$this->call} because we could not write stream state!");
                        return;
                    }
                    $this->muted = true;
                    $delay = 0.2;
                }
                $packet = $this->messageHandler->encryptPacket([
                    '_' => self::PKT_NOP,
                ]);
            }
            //$this->call->log("Writing {$this->opusTimestamp} in {$this->call}!");
            $cur = microtime(true);
            $diff = $t - $cur;
            if ($diff > 0) {
                delay($diff);
            }

            if (!$this->bestEndpoint->write($packet)) {
                $this->call->log("Exiting write loop in {$this->call}!");
                return;
            }

            if ($diff > 0) {
                $cur += $diff;
            }
            $this->lastOutgoingTimestamp = $cur;

            $t += $delay;
        }
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Tgcalls;

use Amp\ByteStream\WritableStream;
use Amp\CancelledException;
use Amp\Future;
use Amp\Pipeline\ConcurrentIterator;
use Amp\TimeoutCancellation;
use danog\MadelineProto\CallStream;
use danog\MadelineProto\LocalFile;
use danog\MadelineProto\RecordingEvent;
use danog\MadelineProto\RecordingFormat;
use danog\MadelineProto\RestoresDeclaredProperties;
use Webrtc\Codecs\EncodedPacket;
use Webrtc\RTP\Enum\MediaKind;
use Webrtc\RTP\MediaStreamTrack\RemoteStreamTrack;

use function Amp\async;
use function Amp\Future\awaitAll;

/**
 * The incoming media of one party of a call: which streams they send, in which codecs, and the
 * recorder (if any) their frames are routed to.
 *
 * Every remote track of the party is drained here, exactly once (a track's frame queue has a single
 * consumer, and grows without bound if nobody drains it): the frames are sniffed for their codec â€”
 * the first keyframe of every new video source is described, see {@see CallRecorder::describeVideo()} â€”
 * and pushed into the attached {@see CallRecorder} (a fixed file or a segment series) and, for a
 * one-to-one call recorded to OGG, the {@see OpusRecorder}. Video may instead be pushed in frame by
 * frame by an owner that routes it between slots itself ({@see self::pushVideoFrame()}).
 *
 * Which streams the party sends is told by signaling ({@see self::setExpected()}: the peer's media
 * state, or a group call participant's flags); the {@see IncomingMediaObserver} is told whenever the
 * available streams or their codecs change, and whenever a recording starts or ends, and turns that
 * into a {@see \danog\MadelineProto\EventHandler\Calls\CallStreams} update.
 *
 * @psalm-import-type StreamMask from \danog\MadelineProto\CallStream
 *
 * @internal
 */
final class IncomingMedia implements RecordingObserver
{
    use RestoresDeclaredProperties;
    /** Every slot, as {@see CallStream} flags. */
    private const SLOTS = [CallStream::AUDIO, CallStream::VIDEO, CallStream::SCREEN];
    /** Granule increment assumed for an OPUS frame whose duration cannot be derived, and the longest one there is. */
    private const DEFAULT_FRAME_SAMPLES = 960;
    private const MAX_FRAME_SAMPLES = 5760;
    /** How often to ask a sender for a keyframe at most, in seconds: the request or its answer may be lost. */
    private const KEYFRAME_REQUEST_INTERVAL = 1.0;

    /**
     * Which streams the party is sending, as far as signaling tells: true on, false off, null unknown.
     *
     * @var array<CallStream::AUDIO|CallStream::VIDEO|CallStream::SCREEN, ?bool> By {@see CallStream} flag.
     */
    private array $expected = [CallStream::AUDIO => null, CallStream::VIDEO => null, CallStream::SCREEN => null];
    /** @var array<int, string> The Matroska codec ID of every slot ({@see CallStream} flag) media was seen for, while it is on. */
    private array $codecs = [];
    /** @var array<int, int> The source (track) each video slot's frames currently come from, for the codec sniffing. */
    private array $sources = [];
    /** The last reported state, to tell the observer only about actual changes. */
    private ?string $reported = null;

    private ?CallRecorder $recorder = null;
    private ?OpusRecorder $opusRecorder = null;
    /** RTP timestamp of the previous audio frame, for the OGG granule increments. */
    private ?int $lastAudioTs = null;

    private ?RemoteStreamTrack $audioTrack = null;
    /**
     * Video tracks drained here, keyed by `<slot>:<source>`. A slot may have several: a group call
     * participant's camera is simulcast on several SSRCs and the SFU forwards whichever layer it
     * picks, so all of them feed the slot, and a switch between them is just a change of source.
     *
     * @var array<string, RemoteStreamTrack>
     */
    private array $videoTracks = [];
    /** @var array<string, int> The slot of each video track, by the same key. */
    private array $videoSlots = [];
    private ?ConcurrentIterator $audioConsumer = null;
    /** @var array<int, Future<void>> The loops draining the tracks, which end with their track. */
    private array $drains = [];
    /** @var array<string, ConcurrentIterator> */
    private array $videoConsumers = [];
    private bool $closed = false;
    /** @var array<int, float> When a keyframe was last requested, by source. */
    private array $keyframeRequests = [];

    /**
     * @psalm-mutation-free
     */
    public function __construct(private ?IncomingMediaObserver $observer = null)
    {
    }

    /**
     * A stable identifier of a track, to tell its frames apart from those of another one.
     *
     * @psalm-pure
     */
    public static function sourceOf(RemoteStreamTrack $track): int
    {
        return crc32($track->getId());
    }

    /**
     * Drop the live track subscriptions (not serializable) and a stream-backed recorder (which
     * cannot be reopened); {@see self::resume()} re-subscribes and keeps recording into a file.
     *
     * @psalm-mutation-free
     */
    public function __serialize(): array
    {
        $vars = get_object_vars($this);
        unset($vars['audioConsumer'], $vars['videoConsumers'], $vars['drains']);
        if ($this->recorder?->file === null) {
            unset($vars['recorder']);
        }
        if ($this->opusRecorder?->file === null) {
            unset($vars['opusRecorder']);
        }
        return $vars;
    }

    /**
     * @param array<string, mixed> $data
     *
     * @psalm-external-mutation-free
     */
    public function __unserialize(array $data): void
    {
        // Synchronous state restoration only â€” no async work here (see resume()).
        $this->recorder = null;
        $this->opusRecorder = null;
        $this->restoreDeclaredProperties($data);
        $this->audioConsumer = null;
        $this->videoConsumers = [];
        $this->drains = [];
    }

    /**
     * Start a loop draining a track.
     *
     * @param \Closure(): void $loop
     */
    private function drain(\Closure $loop): void
    {
        $future = async($loop);
        $id = spl_object_id($future);
        $this->drains[$id] = $future;
        $future->finally(function () use ($id): void {
            unset($this->drains[$id]);
        })->ignore();
    }

    /**
     * Wait for the frames still queued in the tracks to be routed, once the tracks were stopped.
     */
    public function finishDraining(float $timeout): void
    {
        try {
            awaitAll($this->drains, new TimeoutCancellation($timeout));
        } catch (CancelledException) {
            // Whatever wasn't routed in time is dropped.
        }
    }

    /**
     * @psalm-external-mutation-free
     */
    public function setObserver(?IncomingMediaObserver $observer): void
    {
        $this->observer = $observer;
    }

    /**
     * Reopen the recorders' files and re-subscribe to the resumed tracks, after the whole call graph
     * has been deserialized. Never during unserialize (reopening a file suspends the fiber).
     */
    public function resume(): void
    {
        if ($this->closed) {
            return;
        }
        $this->recorder?->resume();
        $this->opusRecorder?->resume();
        if ($this->recorder?->isClosed()) {
            $this->recorder = null;
        }
        if ($this->audioTrack !== null && $this->audioConsumer === null) {
            $this->audioConsumer = $this->audioTrack->getConsumer();
            $this->drain($this->drainAudio(...));
        }
        foreach ($this->videoTracks as $key => $track) {
            if (!isset($this->videoConsumers[$key])) {
                $this->videoConsumers[$key] = $track->getConsumer();
                $this->drain(fn () => $this->drainVideo($key));
            }
        }
    }

    /**
     * Stop draining the tracks (of a connection about to be closed), keeping the recorders so they
     * carry on with the tracks of the next connection (see {@see self::setTrack()}).
     *
     * @psalm-external-mutation-free
     */
    public function detachTracks(): void
    {
        $this->audioTrack = null;
        $this->audioConsumer = null;
        $this->videoTracks = [];
        $this->videoSlots = [];
        $this->videoConsumers = [];
    }

    /**
     * Tell which streams the party is sending, as signaling reports it (null leaves a stream's
     * state unchanged).
     */
    public function setExpected(?bool $audio = null, ?bool $video = null, ?bool $presentation = null): void
    {
        if ($this->closed) {
            return;
        }
        foreach ([CallStream::AUDIO => $audio, CallStream::VIDEO => $video, CallStream::SCREEN => $presentation] as $slot => $value) {
            if ($value === null || $this->expected[$slot] === $value) {
                continue;
            }
            $this->expected[$slot] = $value;
            if (!$value) {
                // The stream is gone; when it comes back it is a new track, to be described afresh.
                unset($this->codecs[$slot], $this->sources[$slot]);
            }
        }
        $this->recorder?->setExpected($audio, $video, $presentation);
        $this->report(null, null);
    }

    /**
     * Whether signaling has reported anything about the party's streams yet.
     *
     * @psalm-mutation-free
     */
    public function isKnown(): bool
    {
        foreach ($this->expected as $value) {
            if ($value !== null) {
                return true;
            }
        }
        return false;
    }

    /**
     * The streams the party currently sends, as {@see CallStream} flags.
     *
     * @psalm-mutation-free
     *
     * @return StreamMask
     */
    public function getAvailable(): int
    {
        $streams = 0;
        foreach ($this->expected as $slot => $value) {
            if ($value === true) {
                $streams |= $slot;
            }
        }
        return $streams;
    }

    /**
     * The codec of every stream media was seen for, keyed by {@see CallStream} flag.
     *
     * @return array<int, string>
     *
     * @psalm-mutation-free
     */
    public function getCodecs(): array
    {
        $codecs = [];
        foreach (self::SLOTS as $slot) {
            if (isset($this->codecs[$slot]) && $this->expected[$slot] !== false) {
                $codecs[$slot] = $this->codecs[$slot];
            }
        }
        return $codecs;
    }

    /**
     * Record the party into one file (or stream) with a fixed set of tracks (see
     * {@see CallRecorder::fixed()}), finishing the previous recording (if any).
     *
     * @param ?StreamMask $streams The {@see CallStream} flags to record, or null for every stream the peer currently sends.
     */
    public function recordFixed(LocalFile|WritableStream $out, RecordingFormat $format, ?int $streams): void
    {
        $this->stopRecording();
        if ($this->closed) {
            return;
        }
        // The recorder reports to us from its construction on: a file with no video track to
        // describe is opened (and reported) right away.
        $recorder = CallRecorder::fixed($out, $format, $streams, $this);
        $this->recorder = $recorder;
        $recorder->setExpected(...array_values($this->expected));
    }

    /**
     * Record the party as a numbered series of segment files (see {@see CallRecorder::series()}),
     * finishing the previous recording (if any).
     */
    public function recordSeries(LocalFile $stem, RecordingFormat $format): void
    {
        $this->stopRecording();
        if ($this->closed) {
            return;
        }
        $recorder = CallRecorder::series($stem, $format, $this);
        $this->recorder = $recorder;
        $recorder->setExpected(...array_values($this->expected));
    }

    /**
     * Finish the recording in progress, if any.
     */
    public function stopRecording(): void
    {
        $previous = $this->recorder;
        $this->recorder = null;
        $previous?->close();
    }

    /**
     * @psalm-mutation-free
     */
    public function getRecorder(): ?CallRecorder
    {
        return $this->recorder;
    }

    /**
     * Attach an OGG OPUS recorder for the party's audio, finishing the previous one (if any).
     */
    public function setOpusRecorder(?OpusRecorder $recorder): void
    {
        $previous = $this->opusRecorder;
        $this->opusRecorder = null;
        if ($previous !== null) {
            $previous->close();
            $this->report(RecordingEvent::Ended, $previous->file);
        }
        if ($recorder === null || $this->closed) {
            $recorder?->close();
            return;
        }
        $this->opusRecorder = $recorder;
        $this->lastAudioTs = null;
        $this->report(RecordingEvent::Started, $recorder->file);
    }

    /**
     * Attach an incoming track of the party, whose frames are then drained here.
     *
     * @param int $slot For a video track, whether it is the camera ({@see CallStream::VIDEO}) or the screen share ({@see CallStream::SCREEN}).
     */
    public function setTrack(RemoteStreamTrack $track, int $slot = CallStream::VIDEO): void
    {
        if ($this->closed) {
            return;
        }
        if ($track->getKind() === MediaKind::Audio) {
            if ($this->audioTrack === $track) {
                return;
            }
            $this->audioTrack = $track;
            $this->audioConsumer = $track->getConsumer();
            $this->drain($this->drainAudio(...));
            return;
        }
        $key = $slot.':'.self::sourceOf($track);
        if (isset($this->videoTracks[$key])) {
            return;
        }
        $this->videoTracks[$key] = $track;
        $this->videoSlots[$key] = $slot;
        $this->videoConsumers[$key] = $track->getConsumer();
        $this->drain(fn () => $this->drainVideo($key));
    }

    private function drainAudio(): void
    {
        $consumer = $this->audioConsumer;
        $track = $this->audioTrack;
        if ($consumer === null || $track === null) {
            return;
        }
        $source = self::sourceOf($track);
        /** @var mixed $frame */
        foreach ($consumer as $frame) {
            if ($this->closed || $this->audioTrack !== $track) {
                return;
            }
            if ($frame instanceof EncodedPacket) {
                $this->pushAudioFrame($frame->getData(), $frame->getTimestamp(), $source);
            }
        }
    }

    private function drainVideo(string $key): void
    {
        $consumer = $this->videoConsumers[$key] ?? null;
        $track = $this->videoTracks[$key] ?? null;
        $slot = $this->videoSlots[$key] ?? null;
        if ($consumer === null || $track === null || $slot === null) {
            return;
        }
        $source = self::sourceOf($track);
        /** @var mixed $frame */
        foreach ($consumer as $frame) {
            if ($this->closed || ($this->videoTracks[$key] ?? null) !== $track) {
                return;
            }
            if ($frame instanceof EncodedPacket) {
                $this->pushVideoFrame($frame->getData(), $frame->getTimestamp(), $source, $slot);
            }
        }
    }

    /**
     * Route one incoming OPUS frame.
     *
     * @param int $rtpTimestamp The frame's RTP timestamp (48 kHz clock).
     * @param int $source       Identifies the track the frame belongs to.
     */
    public function pushAudioFrame(string $data, int $rtpTimestamp, int $source = 0): void
    {
        if ($this->closed || $data === '') {
            return;
        }
        if (!isset($this->codecs[CallStream::AUDIO]) && $this->expected[CallStream::AUDIO] !== false) {
            $this->codecs[CallStream::AUDIO] = 'A_OPUS';
            $this->report(null, null);
        }
        $this->recorder?->pushAudioFrame($data, $rtpTimestamp, $source);
        if ($this->opusRecorder !== null) {
            // The OGG granule position must advance by the duration of each frame, which the RTP
            // timestamps give us directly; the first frame has no predecessor to measure against.
            $granule = self::DEFAULT_FRAME_SAMPLES;
            if ($this->lastAudioTs !== null) {
                $delta = ($rtpTimestamp - $this->lastAudioTs) & 0xFFFFFFFF;
                if ($delta > 0 && $delta <= self::MAX_FRAME_SAMPLES) {
                    $granule = $delta;
                }
            }
            $this->lastAudioTs = $rtpTimestamp;
            $this->opusRecorder->writeOpus($data, $granule);
        }
    }

    /**
     * Route one incoming video frame (still encoded, as it came off RTP).
     *
     * @param int    $rtpTimestamp The frame's RTP timestamp (90 kHz clock).
     * @param int    $source       Identifies the track the frame belongs to.
     * @param int    $slot         Whether this is the camera ({@see CallStream::VIDEO}) or the screen share ({@see CallStream::SCREEN}).
     */
    public function pushVideoFrame(string $data, int $rtpTimestamp, int $source = 0, int $slot = CallStream::VIDEO): void
    {
        if ($this->closed || $data === '' || $this->expected[$slot] === false) {
            return;
        }
        if (($this->sources[$slot] ?? null) !== $source) {
            // A new source: its codec is read off its first keyframe, like the recorder does.
            [, $keyframe] = CallRecorder::transformVideo($data, null);
            if ($keyframe) {
                [$codecId] = CallRecorder::describeVideo($data);
                $this->sources[$slot] = $source;
                if (($this->codecs[$slot] ?? null) !== $codecId) {
                    $this->codecs[$slot] = $codecId;
                    $this->report(null, null);
                }
            }
        }
        $this->recorder?->pushVideoFrame($data, $rtpTimestamp, $source, $slot);
    }

    /**
     * Tell the observer about the current state, if it changed since last time or a recording
     * event is to be reported.
     */
    private function report(?RecordingEvent $recording, ?LocalFile $file): void
    {
        $state = $this->getAvailable().':'.json_encode($this->getCodecs());
        if ($recording === null && $state === $this->reported) {
            return;
        }
        $this->reported = $state;
        $this->observer?->onIncomingMediaChanged($this, $recording, $file);
    }

    #[\Override]
    public function onRecordingStarted(CallRecorder $recorder, LocalFile|WritableStream $out): void
    {
        $this->report(RecordingEvent::Started, $out instanceof LocalFile ? $out : null);
    }

    #[\Override]
    public function onKeyframeNeeded(CallRecorder $recorder, int $source): void
    {
        $now = microtime(true);
        if ($now - ($this->keyframeRequests[$source] ?? 0.0) < self::KEYFRAME_REQUEST_INTERVAL) {
            return;
        }
        $this->keyframeRequests[$source] = $now;
        $this->observer?->onKeyframeNeeded($this, $source);
    }

    #[\Override]
    public function onRecordingEnded(CallRecorder $recorder, LocalFile|WritableStream $out): void
    {
        if ($recorder === $this->recorder && $recorder->isClosed()) {
            $this->recorder = null;
        }
        $this->report(RecordingEvent::Ended, $out instanceof LocalFile ? $out : null);
    }

    /**
     * Finish every recording and stop draining.
     */
    public function close(): void
    {
        if ($this->closed) {
            return;
        }
        $this->stopRecording();
        $this->setOpusRecorder(null);
        $this->closed = true;
        $this->detachTracks();
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Tgcalls;

use Amp\Pipeline\DisposedException;
use danog\MadelineProto\Logger;
use danog\MadelineProto\Loop\VoIP\DjLoop;
use Revolt\EventLoop;
use Webrtc\Codecs\EncodedPacket;
use Webrtc\RTP\Enum\MediaKind;
use Webrtc\RTP\MediaStreamTrack\MediaStreamTrack;

use function Amp\delay;

/**
 * An outgoing video track that emits the pre-encoded frames of a Matroska or WebM file.
 *
 * Nothing is decoded or re-encoded: the frames are packetized straight into RTP by the pure-PHP
 * payloader of whichever codec the file holds â€” VP8, VP9 or H.264, the three a Telegram call
 * carries â€” so video works without the FFI extension just like audio does. Which one the peer
 * expects is settled by {@see GroupSdp}, from {@see DjLoop::getVideoCodec()}.
 *
 * As with {@see OpusPlaybackTrack}, a background producer task drains the source and pushes the
 * frames to the RTP sender, releasing them according to their own timestamps.
 *
 * @internal
 */
final class VideoPlaybackTrack extends MediaStreamTrack
{
    /**
     * How often to re-check the source while no file's cadence is yet known â€” i.e. before the
     * first two frames of a file have been seen, or while idle between files. This is a poll for
     * *work*, never a pace for *media*: once a file is flowing, every wait is derived from that
     * file's own frame timestamps (see {@see self::$frameInterval}), so playback speed never
     * depends on a hardcoded duration.
     */
    private const IDLE_POLL = 0.02;
    /** The frame interval assumed when a file ends before its cadence was measured. */
    private const DEFAULT_FRAME_INTERVAL = 1 / 30;
    /** How late a frame may be released, in seconds, before the track resynchronizes instead of catching up. */
    private const MAX_LATENESS = 1.0;

    /** Wall clock time corresponding to the first frame we released. */
    private ?float $startedAt = null;
    /** Source timestamp of the first frame, used to rebase onto our own RTP clock. */
    private ?int $baseTimestamp = null;
    /** RTP timestamp offset, so restarting a file does not rewind the clock. */
    private int $timestampOffset = 0;
    /** @var array{data: string, timestamp: int, keyframe: bool, file: int}|null The frame whose presentation time has not arrived yet. */
    private ?array $pending = null;
    /** RTP timestamp of the last frame we released. */
    private int $lastTimestamp = 0;
    /** Source timestamp of the previously released frame, used to measure the file's cadence. */
    private ?int $prevSourceTimestamp = null;
    /** The current file's real inter-frame interval in seconds, measured from its own timestamps. */
    private ?float $frameInterval = null;
    /** The DJ loop's ID of the file whose frames are being played, see {@see DjLoop::$videoQueue}. */
    private ?int $file = null;
    /** Wall clock time at which the producer should next attempt to emit a frame. */
    private ?float $nextDue = null;
    /** Whether the frame to produce next isn't queued yet: the producer waits for it to be. */
    private bool $starved = false;

    private bool $playing = false;
    /** Whether signaling has selected the codec that the current file contains. */
    private bool $transportReady = true;
    /**
     * Whether the frames are consumed at their pace but discarded instead of transmitted: set while
     * a screencast replaces the camera video, so the camera playlist keeps advancing (its audio is
     * still transmitted) without queueing frames nobody sends.
     */
    private bool $suppressed = false;
    /**
     * Whether to hold non-keyframes until the next keyframe: after the transport was (re)negotiated
     * the peer sets up a fresh decoder, which can only start on a keyframe.
     */
    private bool $needKeyframe = false;
    /** Whether the producer task is running, to keep {@see self::startProducing()} idempotent. */
    private bool $producing = false;

    /** Frames released so far, for the diagnostics log. */
    private int $released = 0;

    public function __construct(
        private readonly DjLoop $source,
        private readonly CallControllerInterface $call,
        /** What this track carries, for the logs: the camera or the screencast. */
        private readonly string $label = 'camera',
    ) {
        parent::__construct(MediaKind::Video);
        $this->startProducing();
    }

    /**
     * @param array<string, mixed> $data
     */
    #[\Override]
    public function __unserialize(array $data): void
    {
        // Does NOT restart the producer (which reads the call's state, restored only once the whole
        // call graph has deserialized): starting it here could run it mid-deserialization if a nested
        // resume suspends the fiber. The call's deserializer calls {@see self::resume()} when ready.
        parent::__unserialize($data);
        $this->producing = false;
    }

    /**
     * Restart the frame producer after a serialize/unserialize cycle. Called by the call's deserializer
     * once the whole call graph is restored; idempotent.
     */
    public function resume(): void
    {
        $this->startProducing();
    }

    /**
     * Drive the frame producer as a single long-lived, self-pacing event-loop task.
     */
    private function startProducing(): void
    {
        if ($this->producing) {
            return;
        }
        $this->producing = true;
        EventLoop::queue(function (): void {
            try {
                $this->produceLoop();
            } finally {
                $this->producing = false;
            }
        });
    }

    private function produceLoop(): void
    {
        {
            while (!$this->isEnded() && !$this->call->isCallEnded()) {
                $packet = $this->produce();
                if ($packet !== null) {
                    try {
                        $this->push($packet);
                    } catch (DisposedException|\Error $e) {
                        // The consumer (the RTP sender) went away â€” its connection was closed or
                        // replaced â€” while a frame was waiting to be taken: this track will never be
                        // read again, so stop producing instead of crashing the event loop.
                        $this->call->log("The outgoing video track of {$this->call} lost its consumer: {$e->getMessage()}", Logger::VERBOSE);
                        return;
                    }
                }
                // Sleep reactively until the next frame is due (its media cadence), rather than
                // polling for work at a fixed interval.
                $wait = ($this->nextDue ?? microtime(true)) - microtime(true);
                if ($wait > 0) {
                    if ($this->starved) {
                        // Played as soon as it's queued, if it's late.
                        $this->source->awaitFrame($wait);
                    } else {
                        delay($wait);
                    }
                }
            }
        }
    }

    /**
     * Whether any video is currently being transmitted.
     */
    public function isPlaying(): bool
    {
        return $this->playing;
    }

    public function setTransportReady(bool $transportReady): void
    {
        if (!$transportReady && $this->transportReady) {
            $this->needKeyframe = true;
        }
        $this->transportReady = $transportReady;
        if (!$transportReady) {
            $this->nextDue = microtime(true) + self::IDLE_POLL;
        }
    }

    /**
     * Discard (true) or transmit (false) the frames; see {@see self::$suppressed}. Transmission
     * resumes on a keyframe.
     *
     * @psalm-external-mutation-free
     */
    public function setSuppressed(bool $suppressed): void
    {
        if ($this->suppressed && !$suppressed) {
            $this->needKeyframe = true;
        }
        $this->suppressed = $suppressed;
    }

    /**
     * The file being played is over: the next one starts on a fresh clock, right after this one.
     */
    private function finishFile(): void
    {
        $this->playing = false;
        $this->call->log("Finished playing video in {$this->call}");
        // Keep the clock advancing so a new file starts one frame after, not on top of, this one:
        // receivers assemble frames by RTP timestamp.
        $this->timestampOffset = $this->lastTimestamp + (int) (($this->frameInterval ?? self::DEFAULT_FRAME_INTERVAL) * DjLoop::VIDEO_CLOCK_RATE);
        $this->startedAt = null;
        $this->baseTimestamp = null;
        $this->prevSourceTimestamp = null;
        $this->frameInterval = null;
    }

    /**
     * Produce the next video frame, if its presentation time has arrived.
     */
    private function produce(): ?EncodedPacket
    {
        $this->starved = false;
        if (!$this->transportReady) {
            $this->nextDue = microtime(true) + self::IDLE_POLL;
            return null;
        }
        $frame = $this->pending ?? $this->source->pullVideo();
        $this->pending = null;
        if ($frame === null) {
            if ($this->playing && $this->source->isExhausted()) {
                $this->finishFile();
            }
            // The queue momentarily ran dry: wait for the next frame to be queued. For at most one of
            // the file's own frame intervals if a file is mid-flight, to notice that it ended; if
            // we are still buffering the first frames or sitting idle between files, there is no
            // media rate to honor yet, so fall back to a plain poll.
            $this->starved = true;
            $this->nextDue = microtime(true) + ($this->frameInterval ?? self::IDLE_POLL);
            return null;
        }

        if ($this->playing && $frame['file'] !== $this->file) {
            // The next file was queued right behind this one, without the queue ever running dry.
            $this->finishFile();
        }
        $now = microtime(true);
        if ($this->startedAt === null || $this->baseTimestamp === null) {
            $this->file = $frame['file'];
            $this->startedAt = $now;
            $this->baseTimestamp = $frame['timestamp'];
            $this->playing = true;
            $this->call->log("Started playing video in {$this->call}");
        }

        // Release the frame only once its presentation time has arrived.
        $elapsed = (float) ($frame['timestamp'] - $this->baseTimestamp) / (float) DjLoop::VIDEO_CLOCK_RATE;
        $due = $this->startedAt + $elapsed;
        if ($now < $due) {
            $this->pending = $frame;
            $this->nextDue = $due;
            return null;
        }
        if ($now - $due > self::MAX_LATENESS) {
            // We fell way behind (nobody took the frames until the call connected, or the process was
            // suspended): resynchronize instead of bursting out all of the video we're late with.
            $this->startedAt = $now - $elapsed;
        }

        $timestamp = $this->timestampOffset + ($frame['timestamp'] - $this->baseTimestamp);
        $this->lastTimestamp = $timestamp;
        // Learn this file's real frame cadence from the gap between consecutive source timestamps,
        // so an underrun retry can honor the file's own emission rate instead of a fixed interval.
        if ($this->prevSourceTimestamp !== null) {
            $delta = $frame['timestamp'] - $this->prevSourceTimestamp;
            if ($delta > 0) {
                $this->frameInterval = $delta / DjLoop::VIDEO_CLOCK_RATE;
            }
        }
        $this->prevSourceTimestamp = $frame['timestamp'];
        $this->source->setPlaybackPosition(
            (int) ($frame['timestamp'] * 1000 / DjLoop::VIDEO_CLOCK_RATE),
            $frame['file'],
        );
        // Pull and hold the next frame right away so its own presentation time paces us.
        $this->nextDue = $now;

        if ($this->suppressed) {
            return null;
        }
        if ($this->needKeyframe) {
            if (!$frame['keyframe']) {
                return null;
            }
            $this->needKeyframe = false;
        }
        if ($this->released++ % 250 === 0) {
            $this->call->log("Releasing {$this->label} frame {$this->released} (".($frame['keyframe'] ? 'key' : 'inter').") in {$this->call}", Logger::VERBOSE);
        }

        return new EncodedPacket($frame['data'], $timestamp, $frame['keyframe']);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

// IMPORTANT NOTE: Please keep the above copyright notice intact if copying or rewriting this file in another language.

namespace danog\MadelineProto\Tgcalls;

/**
 * Notified when the video codec the {@see \danog\MadelineProto\Loop\VoIP\DjLoop} is transmitting
 * changes, and when video playback stops.
 *
 * The {@see \danog\MadelineProto\Loop\VoIP\DjLoop} keeps a reference to its observer as part of its
 * serializable state, so this has to be a real, serializable object rather than a `Closure` (which
 * PHP cannot serialize): the WebRTC engines survive a serialize/unserialize cycle, and every
 * callback they hold must survive it too.
 *
 * @internal
 * @psalm-mutable
 */
interface VideoCodecObserver
{
    /**
     * Pin the outgoing video m-line to the codec of the file being played.
     *
     * @param string $codec The SDP encoding name (e.g. `VP8`, `H264`) of the video being transmitted.
     * @param array<string, string> $parameters The SDP fmtp parameters (profile/level/tier/â€¦) derived
     *                                           from the file's bitstream, to advertise for this codec.
     * @psalm-impure
     */
    public function onVideoCodec(string $codec, array $parameters = []): void;

    /**
     * React to the demuxed video file finishing: the outgoing video is no longer being transmitted.
     * @psalm-impure
     */
    public function onVideoStopped(): void;
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

// IMPORTANT NOTE: Please keep the above copyright notice intact if copying or rewriting this file in another language.

namespace danog\MadelineProto\Tgcalls;

/**
 * @internal
 *
 * @psalm-pure
 */
final class TgcallsTools
{
    /**
     * Whether realtime transcoding is available.
     *
     * The WebRTC stack itself is pure PHP and always available; the FFI extension (together with
     * libopus, libvpx and ffmpeg) is only needed to convert media on the fly. Without it, calls
     * work normally as long as the audio and video that are played back were already converted to
     * the formats Telegram uses, which MadelineProto can also do offline.
     *
     * @psalm-pure
     */
    public static function canTranscode(): bool
    {
        return \extension_loaded('ffi');
    }

    /**
     * Compress a signaling payload, as tgcalls' `gzipData` does.
     *
     * @psalm-pure
     */
    public static function gzip(string $data): string
    {
        $compressed = gzencode($data, 9);
        return $compressed === false ? $data : $compressed;
    }

    /**
     * Transparently decompress a signaling payload, as tgcalls' `isGzip`/`gunzipData` pair does.
     *
     * @psalm-pure
     */
    public static function gunzip(string $data): string
    {
        if (\strlen($data) < 2) {
            return $data;
        }

        if ($data[0] === \chr(0x1f) && $data[1] === \chr(0x8b)) {
            try {
                return gzdecode($data);
            } catch (\Throwable) {
                return $data;
            }
        }
        if ($data[0] === \chr(0x78) && $data[1] === \chr(0x9c)) {
            try {
                return gzuncompress($data);
            } catch (\Throwable) {
                return $data;
            }
        }
        return $data;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Tgcalls;

/**
 * Pure-PHP inspection of AV1 temporal units in the low-overhead bitstream format (every OBU with
 * its size field), as the RTP depacketizer delivers them: reads the picture size out of the
 * sequence header and builds the `av1C` configuration record a Matroska `V_AV1` track needs.
 *
 * @internal
 */
final class Av1Bitstream
{
    public const OBU_SEQUENCE_HEADER = 1;
    public const OBU_TEMPORAL_DELIMITER = 2;

    /**
     * Find the sequence header OBU of a temporal unit (with its size field), or null.
     */
    public static function sequenceHeader(string $tu): ?string
    {
        foreach (self::obus($tu) as [$type, $obu]) {
            if ($type === self::OBU_SEQUENCE_HEADER) {
                return $obu;
            }
        }
        return null;
    }

    /**
     * Whether a temporal unit starts a coded video sequence, i.e. carries a sequence header (WebRTC
     * encoders emit one with every keyframe).
     */
    public static function isKeyframe(string $tu): bool
    {
        return self::sequenceHeader($tu) !== null;
    }

    /**
     * Describe the stream from a keyframe: picture size and the `av1C` record.
     *
     * @return array{int, int, string} [width, height, AV1CodecConfigurationRecord]
     */
    public static function describe(string $tu): array
    {
        $header = self::sequenceHeader($tu);
        if ($header === null) {
            return [1280, 720, ''];
        }
        try {
            $seq = self::parseSequenceHeader(self::obuPayload($header));
        } catch (\Throwable) {
            return [1280, 720, ''];
        }
        $av1c = "\x81"
            .\chr((($seq['profile'] & 0x07) << 5) | ($seq['level'] & 0x1F))
            .\chr((($seq['tier'] & 1) << 7) | (($seq['highBitdepth'] & 1) << 6) | (($seq['twelveBit'] & 1) << 5)
                | (($seq['monochrome'] & 1) << 4) | (($seq['subsamplingX'] & 1) << 3) | (($seq['subsamplingY'] & 1) << 2)
                | ($seq['chromaSamplePosition'] & 0x03))
            ."\x00"
            .$header;
        return [$seq['width'], $seq['height'], $av1c];
    }

    /**
     * The OBUs of a temporal unit, as [type, whole OBU bytes] pairs.
     *
     * @return list<array{int, string}>
     */
    public static function obus(string $tu): array
    {
        $result = [];
        $offset = 0;
        $length = \strlen($tu);
        while ($offset < $length) {
            $header = \ord($tu[$offset]);
            $type = ($header >> 3) & 0x0F;
            $headerLength = 1 + (($header & 0x04) ? 1 : 0);
            $cursor = $offset + $headerLength;
            if ($header & 0x02) {
                $size = self::leb128($tu, $cursor);
            } else {
                $size = $length - $cursor;
            }
            $end = $cursor + $size;
            if ($size < 0 || $end > $length) {
                break;
            }
            $result[] = [$type, substr($tu, $offset, $end - $offset)];
            $offset = $end;
        }
        return $result;
    }

    /**
     * The payload of an OBU (after its header, extension and size field).
     */
    private static function obuPayload(string $obu): string
    {
        $header = \ord($obu[0]);
        $cursor = 1 + (($header & 0x04) ? 1 : 0);
        if ($header & 0x02) {
            self::leb128($obu, $cursor);
        }
        return substr($obu, $cursor);
    }

    private static function leb128(string $data, int &$offset): int
    {
        $value = 0;
        $shift = 0;
        $length = \strlen($data);
        do {
            if ($offset >= $length) {
                return -1;
            }
            $byte = \ord($data[$offset++]);
            $value |= ($byte & 0x7F) << $shift;
            $shift += 7;
        } while (($byte & 0x80) && $shift < 56);
        return $value;
    }

    /**
     * Parse a sequence_header_obu() up to and including color_config().
     *
     * @return array{profile: int, level: int, tier: int, width: int, height: int, highBitdepth: int, twelveBit: int, monochrome: int, subsamplingX: int, subsamplingY: int, chromaSamplePosition: int}
     *
     * @psalm-mutation-free
     */
    private static function parseSequenceHeader(string $payload): array
    {
        $br = new BitReader($payload);
        $profile = $br->bits(3);
        $br->bits(1); // still_picture
        $reduced = $br->bits(1);
        $decoderModelInfoPresent = 0;
        $bufferDelayLength = 0;
        $initialDisplayDelayPresent = 0;
        $level = 0;
        $tier = 0;
        if ($reduced) {
            $level = $br->bits(5);
        } else {
            if ($br->bits(1)) { // timing_info_present_flag
                $br->bits(32); // num_units_in_display_tick
                $br->bits(32); // time_scale
                if ($br->bits(1)) { // equal_picture_interval
                    self::uvlc($br); // num_ticks_per_picture_minus_1
                }
                $decoderModelInfoPresent = $br->bits(1);
                if ($decoderModelInfoPresent) {
                    $bufferDelayLength = $br->bits(5) + 1;
                    $br->bits(32); // num_units_in_decoding_tick
                    $br->bits(5); // buffer_removal_time_length_minus_1
                    $br->bits(5); // frame_presentation_time_length_minus_1
                }
            }
            $initialDisplayDelayPresent = $br->bits(1);
            $operatingPoints = $br->bits(5) + 1;
            for ($i = 0; $i < $operatingPoints; $i++) {
                $br->bits(12); // operating_point_idc
                $opLevel = $br->bits(5);
                $opTier = $opLevel > 7 ? $br->bits(1) : 0;
                if ($i === 0) {
                    $level = $opLevel;
                    $tier = $opTier;
                }
                if ($decoderModelInfoPresent && $br->bits(1)) { // decoder_model_present_for_this_op
                    $br->bits($bufferDelayLength); // decoder_buffer_delay
                    $br->bits($bufferDelayLength); // encoder_buffer_delay
                    $br->bits(1); // low_delay_mode_flag
                }
                if ($initialDisplayDelayPresent && $br->bits(1)) { // initial_display_delay_present_for_this_op
                    $br->bits(4);
                }
            }
        }
        $widthBits = $br->bits(4) + 1;
        $heightBits = $br->bits(4) + 1;
        $width = $br->bits($widthBits) + 1;
        $height = $br->bits($heightBits) + 1;
        if (!$reduced && $br->bits(1)) { // frame_id_numbers_present_flag
            $br->bits(4); // delta_frame_id_length_minus_2
            $br->bits(3); // additional_frame_id_length_minus_1
        }
        $br->bits(1); // use_128x128_superblock
        $br->bits(1); // enable_filter_intra
        $br->bits(1); // enable_intra_edge_filter
        if (!$reduced) {
            $br->bits(1); // enable_interintra_compound
            $br->bits(1); // enable_masked_compound
            $br->bits(1); // enable_warped_motion
            $br->bits(1); // enable_dual_filter
            $enableOrderHint = $br->bits(1);
            if ($enableOrderHint) {
                $br->bits(1); // enable_jnt_comp
                $br->bits(1); // enable_ref_frame_mvs
            }
            $forceScreenContentTools = $br->bits(1) ? 2 : $br->bits(1); // seq_choose_screen_content_tools
            if ($forceScreenContentTools > 0) {
                if (!$br->bits(1)) { // seq_choose_integer_mv
                    $br->bits(1); // seq_force_integer_mv
                }
            }
            if ($enableOrderHint) {
                $br->bits(3); // order_hint_bits_minus_1
            }
        }
        $br->bits(1); // enable_superres
        $br->bits(1); // enable_cdef
        $br->bits(1); // enable_restoration
        // color_config()
        $highBitdepth = $br->bits(1);
        $twelveBit = ($profile === 2 && $highBitdepth) ? $br->bits(1) : 0;
        $monochrome = $profile === 1 ? 0 : $br->bits(1);
        $colorPrimaries = 2;
        $transfer = 2;
        $matrix = 2;
        if ($br->bits(1)) { // color_description_present_flag
            $colorPrimaries = $br->bits(8);
            $transfer = $br->bits(8);
            $matrix = $br->bits(8);
        }
        $subsamplingX = 1;
        $subsamplingY = 1;
        $chromaSamplePosition = 0;
        if ($monochrome) {
            $br->bits(1); // color_range
        } elseif ($colorPrimaries === 1 && $transfer === 13 && $matrix === 0) {
            $subsamplingX = 0;
            $subsamplingY = 0;
        } else {
            $br->bits(1); // color_range
            if ($profile === 0) {
                $subsamplingX = 1;
                $subsamplingY = 1;
            } elseif ($profile === 1) {
                $subsamplingX = 0;
                $subsamplingY = 0;
            } else {
                if ($twelveBit) {
                    $subsamplingX = $br->bits(1);
                    $subsamplingY = $subsamplingX ? $br->bits(1) : 0;
                } else {
                    $subsamplingX = 1;
                    $subsamplingY = 0;
                }
            }
            if ($subsamplingX && $subsamplingY) {
                $chromaSamplePosition = $br->bits(2);
            }
        }
        return [
            'profile' => $profile,
            'level' => $level,
            'tier' => $tier,
            'width' => $width,
            'height' => $height,
            'highBitdepth' => $highBitdepth,
            'twelveBit' => $twelveBit,
            'monochrome' => $monochrome,
            'subsamplingX' => $subsamplingX,
            'subsamplingY' => $subsamplingY,
            'chromaSamplePosition' => $chromaSamplePosition,
        ];
    }

    /**
     * Read a uvlc() (leading zeros count, then that many value bits) as the AV1 spec defines it.
     */
    private static function uvlc(BitReader $br): int
    {
        $zeros = 0;
        while ($zeros < 32 && $br->bits(1) === 0) {
            $zeros++;
        }
        if ($zeros >= 32) {
            return (1 << 32) - 1;
        }
        return $br->bits($zeros) + (1 << $zeros) - 1;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Tgcalls;

use danog\MadelineProto\CallStream;
use danog\MadelineProto\LocalFile;
use danog\MadelineProto\RecordingEvent;
use Stringable;

/**
 * The call a {@see GroupConnection} belongs to: the small surface the WebRTC engine calls back into,
 * implemented by both the ordinary group call ({@see \danog\MadelineProto\GroupCall\GroupCallController}) and
 * the end-to-end encrypted conference call, so the same engine drives either.
 *
 * @psalm-import-type StreamMask from CallStream
 * @internal
 */
interface GroupConnectionOwner extends Stringable
{
    public function log(string $message, int $level = \danog\MadelineProto\Logger::NOTICE): void;

    /** A participant's media source started arriving. */
    public function onIncomingSource(int $source): void;

    /**
     * The streams a participant sends (or their codecs) changed, or a recording of them started or ended.
     *
     * @param int                   $source    The participant's signed audio SSRC.
     * @param StreamMask            $streams   The streams they send, as {@see \danog\MadelineProto\CallStream} flags.
     * @param array<int, string>    $codecs    The codec of every stream media was seen for, by {@see \danog\MadelineProto\CallStream} flag.
     * @param ?RecordingEvent       $recording Whether a recording started or ended, or null if only the streams changed.
     * @param ?LocalFile            $file      The file of the recording event, if it is a local file.
     *
     * @psalm-impure
     */
    public function onParticipantStreams(int $source, int $streams, array $codecs, ?RecordingEvent $recording, ?LocalFile $file): void;

    /** The WebRTC connection failed and should be checked/rejoined. */
    public function onConnectionFailed(): void;

    /** Tell the server whether our camera video is stopped: returns whether it took the change. */
    public function setVideoStopped(bool $stopped): bool;

    /** Tell the server whether our screen-share is paused: returns whether it took the change. */
    public function setPresentationPaused(bool $paused): bool;
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Tgcalls;

use danog\MadelineProto\LocalFile;
use danog\MadelineProto\RecordingEvent;

/**
 * Told by an {@see IncomingMedia} router when the streams a participant sends (or their codecs)
 * change, or a recording of them starts or ends.
 *
 * @internal
 * @psalm-mutable
 */
interface IncomingMediaObserver
{
    /**
     * @param ?RecordingEvent $recording Whether a recording started or ended, or null if only the streams changed.
     * @param ?LocalFile      $file      The file of the recording event, if it is a local file.
     *
     * @psalm-impure
     */
    public function onIncomingMediaChanged(IncomingMedia $media, ?RecordingEvent $recording, ?LocalFile $file): void;

    /**
     * The recording needs a keyframe of an incoming video track: ask its sender for one (RTCP PLI).
     *
     * @param int $source The track, as given by {@see IncomingMedia::sourceOf()}.
     *
     * @psalm-impure
     */
    public function onKeyframeNeeded(IncomingMedia $media, int $source): void;
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Tgcalls\E2E;

use danog\MadelineProto\Tgcalls\BitReader;
use Webrtc\RTP\Crypto\FrameCryptorInterface;
use Webrtc\RTP\Enum\MediaKind;

/**
 * End-to-end encryption of the media frames of a conference call, exactly as tgcalls'
 * FrameTransformer (GroupInstanceCustomImpl.cpp) plus tde2e's call_encrypt/call_decrypt do it, so
 * that the official clients can decrypt what we send and vice versa:.
 *
 * - every encoded frame is encrypted as one packet ({@see CallPacket}), before RTP packetization, on
 *   channel 0 (the only channel the official clients use, for audio, video and screen share alike);
 * - an Opus frame gets two trailing bytes before encryption â€” an extension flag byte (0x01) and the
 *   audio level/speech byte, which receivers strip and read instead of the RTP audio-level extension;
 * - an H.264 frame keeps its parameter sets and the start of its first slice (up to the PPS id) in the
 *   clear, with every start code widened to four bytes, and is re-encrypted with a fresh one-time key
 *   until no start code appears in the ciphertext, so that RTP packetizers/depacketizers on either
 *   side see valid NAL units; a VP8 frame keeps its first byte (a delta frame) or its ten-byte
 *   keyframe header in the clear; other codecs (VP9, AV1, HEVC) are encrypted whole.
 *
 * Packet sequence numbers and replay windows live in a {@see FrameCryptorState} shared with the
 * screen-share cryptor of the same call.
 *
 * @internal
 */
final class FrameCryptor implements FrameCryptorInterface
{
    /** The only channel official clients encrypt on (tdesktop's EncryptDecrypt uses CallChannelId(0)). */
    private const CHANNEL = 0;
    /** The audio level (-dBov) and speech flag we advertise in every audio frame: a moderate, speaking level. */
    private const AUDIO_LEVEL_BYTE = 0x80 | 20;
    /** How many times an H.264 frame is re-encrypted when its ciphertext happens to contain a start code. */
    private const H264_ATTEMPTS = 4;

    /** The codec of the outgoing video frames, which decides how much of each frame stays in the clear. */
    private ?string $outgoingVideoCodec = null;

    public function __construct(
        private readonly E2EKeyProvider $keys,
        private readonly FrameCryptorState $state = new FrameCryptorState(),
    ) {
    }

    /**
     * Tell the cryptor which codec the outgoing video uses (`H264`, `VP8`, `VP9`, â€¦).
     */
    public function setOutgoingVideoCodec(?string $codec): void
    {
        $this->outgoingVideoCodec = $codec;
    }

    #[\Override]
    public function encryptFrame(MediaKind $kind, int $ssrc, string $frame): string
    {
        $epochs = $this->keys->activeEpochs();
        if ($epochs === []) {
            // No key yet (chain not established): send in the clear rather than drop media.
            return $frame;
        }
        $seed = $this->keys->selfSeed();
        if ($kind === MediaKind::Audio) {
            // tgcalls appends the extension flags byte (bit 0: an audio level byte follows) and the
            // audio level with the speech flag; the receiver strips them after decryption.
            $frame .= "\x01".\chr(self::AUDIO_LEVEL_BYTE);
            return CallPacket::encrypt(self::CHANNEL, $this->state->nextSeqno(self::CHANNEL), $frame, $epochs, $seed);
        }
        switch ($this->outgoingVideoCodec) {
            case 'H264':
                [$frame, $prefix] = self::h264PlaintextHeader($frame);
                for ($attempt = 0; $attempt < self::H264_ATTEMPTS; $attempt++) {
                    $packet = CallPacket::encrypt(self::CHANNEL, $this->state->nextSeqno(self::CHANNEL), substr($frame, $prefix), $epochs, $seed, substr($frame, 0, $prefix));
                    if (self::h264CiphertextIsClean($packet, $prefix)) {
                        return $packet;
                    }
                }
                throw new \RuntimeException('Could not encrypt an H.264 frame without producing a start code in the ciphertext');
            case 'VP8':
                $prefix = self::vp8PlaintextHeader($frame);
                break;
            default:
                $prefix = 0;
        }
        return CallPacket::encrypt(self::CHANNEL, $this->state->nextSeqno(self::CHANNEL), substr($frame, $prefix), $epochs, $seed, substr($frame, 0, $prefix));
    }

    #[\Override]
    public function decryptFrame(MediaKind $kind, int $ssrc, string $frame): string
    {
        $senderKey = $this->keys->publicKeyForSsrc($ssrc);
        if ($senderKey === null) {
            throw new \RuntimeException("No public key for the sender of SSRC $ssrc");
        }
        $secrets = [];
        foreach ($this->keys->activeEpochs() as $epoch) {
            $secrets[$epoch['hash']] = $epoch['secret'];
        }
        $decoded = CallPacket::decrypt($frame, $secrets, $senderKey);
        $this->state->checkReplay($senderKey, $decoded['channel_id'], $decoded['seqno']);
        $payload = $decoded['unencrypted_prefix'].$decoded['payload'];
        if ($kind === MediaKind::Audio && \strlen($payload) >= 2) {
            // Strip the trailing extension flags (and the audio level byte they announce), as tgcalls.
            $flags = \ord($payload[-2]);
            $payload = substr($payload, 0, ($flags & 0x01) !== 0 ? -2 : -1);
        }
        return $payload;
    }

    /* ------------------------------------------------------------------ *
     *  Plaintext prefixes, ported from tgcalls' GroupInstanceCustomImpl.cpp.
     * ------------------------------------------------------------------ */

    /**
     * A VP8 keyframe keeps its 10-byte uncompressed header (with the picture size) in the clear, a
     * delta frame only its first byte (the payload header). Bit 0 of the first byte is the inverse
     * keyframe flag.
     */
    private static function vp8PlaintextHeader(string $frame): int
    {
        if ($frame === '') {
            return 0;
        }
        return (\ord($frame[0]) & 0x01) === 0 ? min(10, \strlen($frame)) : 1;
    }

    /**
     * Widen every 3-byte start code of an Annex B H.264 frame to 4 bytes (as WebRTC's depacketizer
     * reconstructs them, so the receiver decrypts the very bytes we encrypted) and compute how much of
     * it must stay in the clear: parameter sets and SEI whole, the first slice up to its PPS id.
     *
     * @return array{0: string, 1: int} The rewritten frame and its plaintext prefix length.
     */
    private static function h264PlaintextHeader(string $frame): array
    {
        $nalus = self::h264NaluIndices($frame);
        if ($nalus === []) {
            return [$frame, 0];
        }
        $maxOffset = 0;
        $toWiden = [];
        foreach ($nalus as [$start, $payloadStart, $payloadSize]) {
            if ($payloadStart - $start === 3) {
                $toWiden[] = $start;
            }
            $headerEnd = $payloadStart + 1;
            if ($payloadSize >= 1) {
                $type = \ord($frame[$payloadStart]) & 0x1F;
                if ($type === 28) { // FU-A (not in an encoder's output, kept for parity with tgcalls)
                    if ($payloadSize >= 2) {
                        $headerEnd = $payloadStart + 2;
                        if ((\ord($frame[$payloadStart + 1]) & 0x80) !== 0 && \in_array(\ord($frame[$payloadStart + 1]) & 0x1F, [1, 5], true)) {
                            $headerEnd += 4;
                        }
                    }
                } elseif ($type === 24) { // STAP-A (idem)
                    if ($payloadSize >= 3) {
                        $headerEnd = $payloadStart + 3;
                        if ($payloadSize > 3 && \in_array(\ord($frame[$payloadStart + 3]) & 0x1F, [1, 5], true)) {
                            $headerEnd += 4;
                        }
                    }
                } elseif ($type === 1 || $type === 5) {
                    $headerEnd = $payloadStart + self::h264SliceHeaderBytesForPpsId(substr($frame, $payloadStart, $payloadSize));
                    $maxOffset = max($maxOffset, $headerEnd);
                    break;
                } elseif ($type === 7 || $type === 8 || $type === 6) {
                    $headerEnd = $payloadStart + $payloadSize;
                }
            }
            $maxOffset = max($maxOffset, $headerEnd);
        }
        // Insert a zero byte in front of every 3-byte start code.
        $out = '';
        $offset = 0;
        foreach ($toWiden as $start) {
            $out .= substr($frame, $offset, $start - $offset)."\0";
            $offset = $start;
        }
        $out .= substr($frame, $offset);
        return [$out, min(\strlen($out), $maxOffset + \count($toWiden))];
    }

    /**
     * The NAL units of an Annex B buffer as `[start code offset, payload offset, payload size]`,
     * like WebRTC's H264::FindNaluIndices.
     *
     * @return list<array{0: int, 1: int, 2: int}>
     */
    private static function h264NaluIndices(string $buffer): array
    {
        $result = [];
        $pos = 0;
        $length = \strlen($buffer);
        while (($i = strpos($buffer, "\x00\x00\x01", $pos)) !== false) {
            $start = ($i > 0 && $buffer[$i - 1] === "\x00") ? $i - 1 : $i;
            $result[] = [$start, $i + 3, 0];
            $pos = $i + 3;
        }
        foreach ($result as $n => [$start, $payloadStart]) {
            $end = isset($result[$n + 1]) ? $result[$n + 1][0] : $length;
            $result[$n][2] = max(0, $end - $payloadStart);
        }
        return $result;
    }

    /**
     * How many bytes of a slice NAL unit (header included) cover first_mb_in_slice, slice_type and
     * pic_parameter_set_id, plus one byte of margin â€” tgcalls' calculateSliceHeaderBytesForPpsId.
     */
    private static function h264SliceHeaderBytesForPpsId(string $nalu): int
    {
        if (\strlen($nalu) < 2) {
            return 0;
        }
        // To RBSP: drop the emulation prevention bytes (00 00 03 -> 00 00).
        $rbsp = '';
        for ($i = 0, $n = \strlen($nalu); $i < $n; $i++) {
            if ($i + 2 < $n && $nalu[$i] === "\x00" && $nalu[$i + 1] === "\x00" && $nalu[$i + 2] === "\x03") {
                $rbsp .= "\x00\x00";
                $i += 2;
                continue;
            }
            $rbsp .= $nalu[$i];
        }
        if (\strlen($rbsp) < 2) {
            return 0;
        }
        $bits = new BitReader(substr($rbsp, 1));
        for ($k = 0; $k < 3; $k++) { // first_mb_in_slice, slice_type, pic_parameter_set_id: ue(v)
            $bits->ue();
            if ($bits->overflowed()) {
                return 4; // tgcalls' default when the slice header cannot be parsed
            }
        }
        return 1 + intdiv($bits->position() + 7, 8) + 1;
    }

    /**
     * Whether the encrypted part of an H.264 packet contains no 3-byte start code (tgcalls'
     * ValidateEncryptedFrame): the RTP packetizer would otherwise split the ciphertext there and the
     * receiver would decrypt shifted bytes. The two bytes before the ciphertext are included, since
     * a start code can straddle the boundary.
     */
    private static function h264CiphertextIsClean(string $packet, int $prefix): bool
    {
        $start = max(0, $prefix - 2);
        return strpos($packet, "\x00\x00\x01", $start) === false;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Tgcalls\E2E;

/**
 * The per-call packet counters shared by every {@see FrameCryptor} of a conference: the outgoing
 * sequence numbers (one per channel, unique and increasing for our key, as the protocol requires â€”
 * the camera and the screen-share connections must therefore draw from the same counter) and the
 * replay windows of what we received from each sender.
 *
 * Serialized with the call, so that a resumed process keeps counting where it left off instead of
 * re-sending sequence numbers our peers already saw (and would reject as replays).
 *
 * @internal
 */
final class FrameCryptorState
{
    /** How many recent sequence numbers per sender and channel are remembered (tde2e keeps 1024). */
    private const REPLAY_WINDOW = 1024;

    /** @var array<int, int> Last sequence number sent per channel. */
    private array $seqno = [];
    /** @var array<string, array<int, true>> Sequence numbers recently seen per `sender:channel`. */
    private array $seen = [];

    /** The next sequence number to send on a channel. */
    public function nextSeqno(int $channel): int
    {
        $seqno = ($this->seqno[$channel] ?? 0) + 1;
        if ($seqno > 0xFFFFFFFF) {
            // The protocol says a client must leave the call on overflow; four billion frames is
            // far beyond any call's lifetime, so treat it as the fatal error it is.
            throw new \RuntimeException('Conference packet sequence number overflow');
        }
        $this->seqno[$channel] = $seqno;
        return $seqno;
    }

    /**
     * Reject a sequence number already seen (or older than the whole window) from a sender on a
     * channel, and remember it otherwise â€” the check tde2e's CallEncryption does.
     *
     * @param string $sender The sender's public key (binary).
     */
    public function checkReplay(string $sender, int $channel, int $seqno): void
    {
        $key = bin2hex($sender).":$channel";
        $window = $this->seen[$key] ?? [];
        if ($window !== []) {
            $oldest = array_key_first($window);
            \assert(\is_int($oldest));
            if ($seqno < $oldest) {
                throw new \RuntimeException('Conference packet too old');
            }
            if (isset($window[$seqno])) {
                throw new \RuntimeException('Replayed conference packet');
            }
        }
        $window[$seqno] = true;
        ksort($window);
        // Bound the window: drop the oldest entries beyond the size, and anything a full window behind.
        while (\count($window) > self::REPLAY_WINDOW || ($window !== [] && array_key_first($window) + self::REPLAY_WINDOW < $seqno)) {
            unset($window[array_key_first($window)]);
        }
        $this->seen[$key] = $window;
    }
}
// TL schema for Telegram end-to-end encrypted conference calls, loaded into a dedicated TL instance
// by {@see \danog\MadelineProto\Tgcalls\E2E\BlockCodec}. Mirrors td/generate/scheme/e2e_api.tl; the
// inner chain constructors carry no explicit id, so their constructor id is the CRC32 of the
// definition, byte-identical to TDLib (which the block hash and signature depend on).
//
// IMPORTANT: keep these definitions byte-identical to TDLib's e2e_api.tl â€” the block hash is the
// SHA256 of the TL serialization and the signature is over the serialization with the signature
// field zeroed, so any divergence makes every block we build be rejected by the server and peers.
//
// The constructor ids are given explicitly and are the ones TDLib's tde2e (bundled by every official
// client) actually compiles: for e2e.chain.groupParticipant and e2e.chain.changeSetValue they differ
// from the ids printed in the API documentation (28852f20 / 7c4f9bfa, which belong to a newer
// definition no shipping client uses yet); the wire layout is the same, and set_value is bit 2 of
// the participant flags in both. Verified against tde2e with tests/e2e cross-checks.

boolFalse#bc799737 = Bool;
boolTrue#997275b5 = Bool;

true#3fedd339 = True;

vector#1cb5c415 {t:Type} # [ t ] = Vector t;

// int64/int32 in TDLib's e2e_api.tl are wire-identical to long/int; these constructors have explicit
// ids, so using MadelineProto's native scalar types keeps the bytes and the id unchanged.
e2e.chain.groupBroadcastNonceCommit#d1512ae7 signature:int512 user_id:long chain_height:int chain_hash:int256 nonce_hash:int256 = e2e.chain.GroupBroadcast;
e2e.chain.groupBroadcastNonceReveal#83f4f9d8 signature:int512 user_id:long chain_height:int chain_hash:int256 nonce:int256 = e2e.chain.GroupBroadcast;

e2e.chain.groupParticipant#18f3971f user_id:long public_key:int256 flags:# add_users:flags.0?true remove_users:flags.1?true set_value:flags.2?true version:int = e2e.chain.GroupParticipant;
e2e.chain.groupState#1ddc7584 participants:vector<e2e.chain.GroupParticipant> external_permissions:int = e2e.chain.GroupState;
e2e.chain.sharedKey#8a847e7f ek:int256 encrypted_shared_key:string dest_user_id:vector<long> dest_header:vector<bytes> = e2e.chain.SharedKey;

e2e.chain.changeNoop#deb4a41b nonce:int256 = e2e.chain.Change;
e2e.chain.changeSetValue#fe0139cc key:bytes value:bytes = e2e.chain.Change;
e2e.chain.changeSetGroupState#2cf17146 group_state:e2e.chain.GroupState = e2e.chain.Change;
e2e.chain.changeSetSharedKey#987a2158 shared_key:e2e.chain.SharedKey = e2e.chain.Change;

e2e.chain.stateProof#d6b679e6 flags:# kv_hash:int256 group_state:flags.0?e2e.chain.GroupState shared_key:flags.1?e2e.chain.SharedKey = e2e.chain.StateProof;

e2e.chain.block#639a3db6 signature:int512 flags:# prev_block_hash:int256 changes:vector<e2e.chain.Change> height:int state_proof:e2e.chain.StateProof signature_public_key:flags.0?int256 = e2e.chain.Block;

---functions---

e2e.nop = Bool;
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Tgcalls\E2E;

use danog\MadelineProto\Settings\TLSchema;
use danog\MadelineProto\TL\TL;

/**
 * (De)serializer for the TL blocks of an end-to-end encrypted conference call
 * ([end-to-end group calls](https://core.telegram.org/api/end-to-end/group-calls)).
 *
 * The chain blocks travel as opaque `bytes` in the MTProto API, serialized with a small dedicated TL
 * schema ({@see e2e_conference.tl}). The block hash is the SHA-256 of that serialization and a block
 * signature is computed over the serialization with the signature field zeroed, so the byte layout
 * must match TDLib exactly â€” it does, since both compute the constructor ids as the CRC32 of the same
 * definitions (verified in the test-suite).
 *
 * The server hands blocks back with their constructor id incremented by one, to make it impossible to
 * resubmit a server block as if it were a fresh client block. We accept both ids on the way in and
 * always emit the canonical id on the way out.
 *
 * @internal
 */
final class BlockCodec
{
    /** A 64-byte all-zero signature, as used while hashing/signing a block. */
    public const ZERO_SIGNATURE = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";

    /**
     * Canonical constructor ids the server returns incremented by one, mapped to the canonical id, so
     * a server block can be parsed with the schema's canonical constructors.
     *
     * @var array<int, int>
     */
    private const SERVER_ID_FIXUPS = [
        0x639a3db7 => 0x639a3db6, // e2e.chain.block
        0xd1512ae8 => 0xd1512ae7, // e2e.chain.groupBroadcastNonceCommit
        0x83f4f9d9 => 0x83f4f9d8, // e2e.chain.groupBroadcastNonceReveal
    ];

    private TL $tl;

    public function __construct()
    {
        $this->tl = new TL();
        $this->tl->init(
            (new TLSchema())
                ->setAPISchema(__DIR__.'/e2e_conference.tl')
                ->setMTProtoSchema('')
                ->setSecretSchema('')
                ->setOther([])
        );
    }

    /**
     * Serialize a chain object (`['_' => 'e2e.chain.block', ...]`, a change, a broadcast, â€¦) to bytes,
     * with the canonical constructor id.
     *
     * @param array<string, mixed> $object
     */
    public function serialize(array $object): string
    {
        $type = self::typeOf($object['_'] ?? '');
        return (string) $this->tl->serializeObject(['type' => $type], $object, $object['_'] ?? 'e2e');
    }

    /**
     * Deserialize a chain object from bytes, accepting the id the server increments by one.
     *
     * @return array<string, mixed>
     */
    public function deserialize(string $bytes): array
    {
        if (\strlen($bytes) >= 4) {
            $id = unpack('V', substr($bytes, 0, 4))[1];
            if (isset(self::SERVER_ID_FIXUPS[$id])) {
                $bytes = pack('V', self::SERVER_ID_FIXUPS[$id]).substr($bytes, 4);
            }
        }
        $stream = fopen('php://memory', 'rw+b');
        \assert($stream !== false);
        fwrite($stream, $bytes);
        rewind($stream);
        /** @var array<string, mixed> */
        return $this->tl->deserialize($stream, ['type' => '', 'connection' => null, 'encrypted' => false]);
    }

    /**
     * The SHA-256 hash of a serialized block, used as its identity and as `prev_block_hash` of the
     * next block.
     *
     * @psalm-pure
     */
    public function blockHash(string $serializedBlock): string
    {
        return hash('sha256', $serializedBlock, true);
    }

    /**
     * The exact bytes a block signature is computed over: the block serialized with its signature
     * field zeroed.
     *
     * @param array<string, mixed> $block An `e2e.chain.block` object; its `signature` is ignored.
     */
    public function signingPayload(array $block): string
    {
        $block['signature'] = self::ZERO_SIGNATURE;
        return $this->serialize($block);
    }

    /**
     * The boxed `e2e.chain.Type` a predicate belongs to, for {@see TL::serializeObject()}.
     *
     * @psalm-pure
     */
    private static function typeOf(string $predicate): string
    {
        return match ($predicate) {
            'e2e.chain.block' => 'e2e.chain.Block',
            'e2e.chain.stateProof' => 'e2e.chain.StateProof',
            'e2e.chain.groupState' => 'e2e.chain.GroupState',
            'e2e.chain.groupParticipant' => 'e2e.chain.GroupParticipant',
            'e2e.chain.sharedKey' => 'e2e.chain.SharedKey',
            'e2e.chain.changeNoop',
            'e2e.chain.changeSetValue',
            'e2e.chain.changeSetGroupState',
            'e2e.chain.changeSetSharedKey' => 'e2e.chain.Change',
            'e2e.chain.groupBroadcastNonceCommit',
            'e2e.chain.groupBroadcastNonceReveal' => 'e2e.chain.GroupBroadcast',
            default => throw new \InvalidArgumentException("Unknown e2e chain predicate: $predicate"),
        };
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Tgcalls\E2E;

use danog\MadelineProto\Magic;

/**
 * The commit-reveal emoji verification of an end-to-end encrypted conference call (subchain 1).
 *
 * Each participant commits to a random 32-byte nonce (publishing only its SHA-256), then reveals the
 * nonce. Once every nonce is known, all participants derive the same four verification emojis from
 * `HMAC-SHA512(sorted-concatenated-nonces, last_block_hash)`; matching emojis prove no man in the
 * middle. See {@see \danog\MadelineProto\Tgcalls\E2E\Crypto} and TDLib's tde2e CallVerificationChain.
 *
 * @internal
 */
final class Verification
{
    /**
     * Build the commit-reveal pair for our nonce over the current chain head. The commit is broadcast
     * first (revealing only the hash); the reveal is broadcast once everyone has committed.
     *
     * @return array{nonce: string, commit: array<string, mixed>, reveal: array<string, mixed>}
     */
    public static function makeNonce(int $userId, int $chainHeight, string $chainHash, string $seed): array
    {
        $nonce = random_bytes(32);
        $commit = [
            '_' => 'e2e.chain.groupBroadcastNonceCommit',
            'signature' => BlockCodec::ZERO_SIGNATURE,
            'user_id' => $userId,
            'chain_height' => $chainHeight,
            'chain_hash' => $chainHash,
            'nonce_hash' => Crypto::sha256($nonce),
        ];
        $reveal = [
            '_' => 'e2e.chain.groupBroadcastNonceReveal',
            'signature' => BlockCodec::ZERO_SIGNATURE,
            'user_id' => $userId,
            'chain_height' => $chainHeight,
            'chain_hash' => $chainHash,
            'nonce' => $nonce,
        ];
        return [
            'nonce' => $nonce,
            'commit' => self::sign($commit, $seed),
            'reveal' => self::sign($reveal, $seed),
        ];
    }

    /**
     * Ed25519-sign a broadcast (commit or reveal) over its serialization with the signature zeroed.
     *
     * @param array<string, mixed> $broadcast
     *
     * @return array<string, mixed>
     */
    private static function sign(array $broadcast, string $seed): array
    {
        $codec = new BlockCodec();
        $payload = $broadcast;
        $payload['signature'] = BlockCodec::ZERO_SIGNATURE;
        $broadcast['signature'] = Crypto::sign($seed, $codec->serialize($payload));
        return $broadcast;
    }

    /**
     * Verify the Ed25519 signature of a received broadcast against its sender's public key.
     *
     * @param array<string, mixed> $broadcast
     */
    public static function verify(array $broadcast, string $publicKey): bool
    {
        $signature = (string) ($broadcast['signature'] ?? '');
        $payload = $broadcast;
        $payload['signature'] = BlockCodec::ZERO_SIGNATURE;
        return \strlen($signature) === 64 && Crypto::verify($signature, (new BlockCodec())->serialize($payload), $publicKey);
    }

    /**
     * Whether a revealed nonce matches a previously committed hash.
     *
     * @psalm-pure
     */
    public static function checkReveal(string $committedHash, string $revealedNonce): bool
    {
        return hash_equals($committedHash, Crypto::sha256($revealedNonce));
    }

    /**
     * The 64-byte emoji hash once every participant's nonce is revealed: the nonces are sorted as raw
     * byte strings, concatenated, and used as the HMAC-SHA512 key over the last block hash.
     *
     * @param list<string> $nonces Every participant's revealed 32-byte nonce.
     *
     * @psalm-pure
     */
    public static function emojiHash(array $nonces, string $lastBlockHash): string
    {
        sort($nonces, SORT_STRING);
        return hash_hmac('sha512', $lastBlockHash, implode('', $nonces), true);
    }

    /**
     * The four verification emojis derived from an emoji hash: each is one of the 333 call emojis,
     * indexed by a big-endian unsigned 64-bit word (top bit cleared) of the hash modulo 333 â€” the
     * same table and extraction as one-to-one call verification.
     *
     * @return list<string> Four emoji.
     */
    public static function emojis(string $emojiHash): array
    {
        // The emoji table is only decoded by the full initialization (a no-op once the API started).
        Magic::start(light: false);
        $table = Magic::$emojis;
        $count = \count($table);
        $result = [];
        foreach (str_split(substr($emojiHash, 0, 32), 8) as $word) {
            $word[0] = \chr(\ord($word[0]) & 0x7f); // mask the top bit, like & 0x7fffffffffffffff
            $value = 0;
            for ($i = 0; $i < 8; $i++) {
                $value = $value * 256 + \ord($word[$i]);
            }
            $result[] = $table[$value % $count];
        }
        return $result;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Tgcalls\E2E;

/**
 * Supplies the live encryption keys of an end-to-end encrypted conference to the {@see FrameCryptor}.
 *
 * Implemented as an interface (not closures) so the frame cryptor stays serializable with the rest of
 * the call graph.
 *
 * @internal
 */
interface E2EKeyProvider
{
    /**
     * The currently active epochs, each an `['hash' => 32-byte block hash, 'secret' => 32-byte group
     * key]`, most-recent first. A packet is encrypted for all of them and decrypted with whichever the
     * receiver holds.
     *
     * @return list<array{hash: string, secret: string}>
     */
    public function activeEpochs(): array;

    /** Our own Ed25519 signing seed, for signing outgoing packets. */
    public function selfSeed(): string;

    /**
     * The Ed25519 public key of the participant sending media on a given SSRC, to verify incoming
     * packets, or null if the sender is unknown.
     */
    public function publicKeyForSsrc(int $ssrc): ?string;
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Tgcalls\E2E;

use danog\MadelineProto\Magic;

/**
 * Cryptographic primitives for Telegram end-to-end encrypted conference calls, ported byte-for-byte
 * from TDLib's `tde2e` (see td/tde2e/td/e2e/{MessageEncryption,Keys,Call,Blockchain}.cpp and
 * tdutils/td/utils/Ed25519.cpp). Every routine is a pure function so it can be unit-tested against
 * the reference in isolation.
 *
 * Signatures are Ed25519, key agreement is X25519 over the Ed25519 keys converted to Montgomery form
 * (exactly what libsodium's crypto_sign_ed25519_*_to_curve25519 do), the KDF is HMAC-SHA512, message
 * ids are HMAC-SHA256, and bulk encryption is AES-256-CBC. The fast path uses the `sodium` and
 * `openssl` extensions; when they are absent it falls back to phpseclib (Ed25519, AES) and a pure-PHP
 * X25519, so no PHP extension beyond the always-present `hash` is required â€” both backends are
 * cross-validated to produce byte-identical results.
 *
 * @internal
 */
final class Crypto
{
    /** Minimum padding prepended by encrypt_data (MessageEncryption.cpp MIN_PADDING). */
    private const MIN_PADDING = 16;

    /**
     * Always available: the fast path uses the `sodium` and `openssl` extensions, and a pure-PHP
     * fallback (phpseclib Ed25519, a phpseclib X25519 over Ed25519 keys, and phpseclib AES) covers
     * runtimes without them. phpseclib is a hard dependency of MadelineProto.
     *
     * @psalm-pure
     */
    public static function available(): bool
    {
        return true;
    }

    /**
     * The implementation of the Curve25519 primitives: sodium, OpenSSL (PHP 8.4+) or, without either, phpseclib.
     *
     * @return 'sodium'|'openssl'|'phpseclib'
     */
    public static function backend(): string
    {
        return self::$backend
            ?? (\extension_loaded('sodium') ? 'sodium' : (self::hasOpensslCurve25519() ? 'openssl' : 'phpseclib'));
    }

    /**
     * The backend to use instead of the best available one: for tests.
     *
     * @var 'sodium'|'openssl'|'phpseclib'|null
     */
    public static ?string $backend = null;

    /**
     * @psalm-mutation-free
     */
    private static function assertAvailable(): void
    {
    }

    /* ------------------------------------------------------------------ *
     *  KDF and MACs.
     * ------------------------------------------------------------------ */

    /**
     * The tde2e key-derivation function: `HMAC-SHA512(key = secret, msg = info)`, 64 bytes
     * (MessageEncryption.cpp `kdf_expand`). The purpose string is the *message*, the secret the key.
     *
     * @psalm-pure
     */
    public static function kdfExpand(string $secret, string $info): string
    {
        return hash_hmac('sha512', $info, $secret, true);
    }

    /* ------------------------------------------------------------------ *
     *  encrypt_data / decrypt_data (MessageEncryption.cpp).
     * ------------------------------------------------------------------ */

    /**
     * Encrypt a payload with a secret, binding it to `extra` authenticated data.
     *
     * @return array{0: string, 1: string} `[output, largeMsgId]` where output is `msgId(16) || AES-CBC`
     *         and largeMsgId is the full 32-byte HMAC-SHA256 (used as the media-packet signing input).
     */
    public static function encryptData(string $payload, string $secret, string $extra = ''): array
    {
        self::assertAvailable();
        // Padding is a prefix whose first byte is its own length, sized so prefix+payload is a
        // multiple of 16 with at least MIN_PADDING bytes of padding.
        $len = \strlen($payload);
        $padLen = ((self::MIN_PADDING + 15 + $len) & ~15) - $len;
        $prefix = random_bytes($padLen);
        $prefix[0] = \chr($padLen);
        $padded = $prefix.$payload;

        $large = self::kdfExpand($secret, 'tde2e_encrypt_data');
        $encKey = substr($large, 0, 32);
        $hmacKey = substr($large, 32, 32);

        $tail = $padded.$extra.pack('V', \strlen($extra));
        $largeMsgId = hash_hmac('sha256', $tail, $hmacKey, true);
        $msgId = substr($largeMsgId, 0, 16);

        [$aesKey, $aesIv] = self::aesFromHash($encKey, $msgId);
        $ciphertext = self::aesEncrypt($aesKey, $aesIv, $padded);
        return [$msgId.$ciphertext, $largeMsgId];
    }

    /**
     * Decrypt and authenticate the output of {@see self::encryptData()}, throwing on any mismatch.
     */
    public static function decryptData(string $encrypted, string $secret, string $extra = ''): string
    {
        return self::decryptDataWithMsgId($encrypted, $secret, $extra)[0];
    }

    /**
     * Like {@see self::decryptData()}, but also returns the full 32-byte large message id, which the
     * media-packet layer needs to verify the sender's signature.
     *
     * @return array{0: string, 1: string} `[plaintext, largeMsgId]`
     */
    public static function decryptDataWithMsgId(string $encrypted, string $secret, string $extra = ''): array
    {
        self::assertAvailable();
        if (\strlen($encrypted) < 16 || (\strlen($encrypted) - 16) % 16 !== 0) {
            throw new \RuntimeException('Malformed encrypted data');
        }
        $msgId = substr($encrypted, 0, 16);
        $ciphertext = substr($encrypted, 16);

        $large = self::kdfExpand($secret, 'tde2e_encrypt_data');
        $encKey = substr($large, 0, 32);
        $hmacKey = substr($large, 32, 32);

        [$aesKey, $aesIv] = self::aesFromHash($encKey, $msgId);
        $padded = self::aesDecrypt($aesKey, $aesIv, $ciphertext);
        if ($padded === false) {
            throw new \RuntimeException('Could not decrypt data');
        }
        $largeMsgId = hash_hmac('sha256', $padded.$extra.pack('V', \strlen($extra)), $hmacKey, true);
        if (!hash_equals(substr($largeMsgId, 0, 16), $msgId)) {
            throw new \RuntimeException('Message id mismatch decrypting data');
        }
        $padLen = \ord($padded[0]);
        if ($padLen < self::MIN_PADDING || $padLen > \strlen($padded)) {
            throw new \RuntimeException('Invalid padding decrypting data');
        }
        return [substr($padded, $padLen), $largeMsgId];
    }

    /* ------------------------------------------------------------------ *
     *  encrypt_header / decrypt_header (MessageEncryption.cpp).
     * ------------------------------------------------------------------ */

    /**
     * Wrap a 32-byte header keyed to a secret and bound to an already-encrypted message (via its
     * leading 16-byte msg id). Returns exactly 32 bytes.
     */
    public static function encryptHeader(string $header, string $encryptedMessage, string $secret): string
    {
        self::assertAvailable();
        if (\strlen($header) !== 32 || \strlen($encryptedMessage) < 16) {
            throw new \RuntimeException('Invalid header or message for encrypt_header');
        }
        $encKey = substr(self::kdfExpand($secret, 'tde2e_encrypt_header'), 0, 32);
        [$aesKey, $aesIv] = self::aesFromHash($encKey, substr($encryptedMessage, 0, 16));
        return self::aesEncrypt($aesKey, $aesIv, $header);
    }

    /**
     * Reverse {@see self::encryptHeader()}, returning the 32-byte header.
     */
    public static function decryptHeader(string $encryptedHeader, string $encryptedMessage, string $secret): string
    {
        self::assertAvailable();
        $encKey = substr(self::kdfExpand($secret, 'tde2e_encrypt_header'), 0, 32);
        [$aesKey, $aesIv] = self::aesFromHash($encKey, substr($encryptedMessage, 0, 16));
        $out = self::aesDecrypt($aesKey, $aesIv, $encryptedHeader);
        if ($out === false) {
            throw new \RuntimeException('Could not decrypt header');
        }
        return $out;
    }

    /**
     * AES-256-CBC (key, iv) from `HMAC-SHA512(hashKey, msgId)[0:48]`.
     *
     * @psalm-pure
     */
    private static function aesFromHash(string $hashKey, string $msgId): array
    {
        $kv = hash_hmac('sha512', $msgId, $hashKey, true);
        return [substr($kv, 0, 32), substr($kv, 32, 16)];
    }

    /** AES-256-CBC encrypt without padding (input is already block-aligned): openssl, else phpseclib. */
    private static function aesEncrypt(string $key, string $iv, string $data): string
    {
        if (\extension_loaded('openssl')) {
            $out = openssl_encrypt($data, 'aes-256-cbc', $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv);
            \assert($out !== false);
            return $out;
        }
        $aes = new \phpseclib4\Crypt\AES('cbc');
        $aes->setKey($key);
        $aes->setIV($iv);
        $aes->disablePadding();
        return $aes->encrypt($data);
    }

    /** AES-256-CBC decrypt without padding: openssl, else phpseclib. Returns false on failure. */
    private static function aesDecrypt(string $key, string $iv, string $data): string|false
    {
        if (\extension_loaded('openssl')) {
            return openssl_decrypt($data, 'aes-256-cbc', $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv);
        }
        if ($data === '' || \strlen($data) % 16 !== 0) {
            return false;
        }
        $aes = new \phpseclib4\Crypt\AES('cbc');
        $aes->setKey($key);
        $aes->setIV($iv);
        $aes->disablePadding();
        return $aes->decrypt($data);
    }

    /* ------------------------------------------------------------------ *
     *  Ed25519 signatures.
     * ------------------------------------------------------------------ */

    /**
     * Generate a keypair.
     *
     * @return array{0: string, 1: string} `[seed(32), publicKey(32)]`; the seed is the private key.
     */
    public static function generateKeyPair(): array
    {
        $seed = random_bytes(32);
        return [$seed, self::publicKey($seed)];
    }

    /** The 32-byte Ed25519 public key of a 32-byte seed. */
    public static function publicKey(string $seed): string
    {
        if (self::backend() === 'sodium') {
            return sodium_crypto_sign_publickey(sodium_crypto_sign_seed_keypair($seed));
        }
        if (self::backend() === 'openssl') {
            $details = openssl_pkey_get_details(self::opensslKey(self::ED25519_PKCS8_PREFIX, $seed, true));
            \assert($details !== false && isset($details['ed25519']['pub_key']));
            return (string) $details['ed25519']['pub_key'];
        }
        $curve = new \phpseclib4\Crypt\EC\Curves\Ed25519();
        $point = $curve->multiplyPoint($curve->getBasePoint(), $curve->extractSecret($seed)['dA']);
        return \phpseclib4\Crypt\EC\Formats\Keys\libsodium::savePublicKey($curve, $point);
    }

    /** Ed25519-sign a message with a 32-byte seed, returning the 64-byte signature. */
    public static function sign(string $seed, string $message): string
    {
        if (self::backend() === 'sodium') {
            return sodium_crypto_sign_detached($message, sodium_crypto_sign_secretkey(sodium_crypto_sign_seed_keypair($seed)));
        }
        if (self::backend() === 'openssl') {
            // Ed25519 hashes the message itself: no digest.
            if (!openssl_sign($message, $signature, self::opensslKey(self::ED25519_PKCS8_PREFIX, $seed, true), 0)) {
                throw new \RuntimeException('Ed25519 signing failed: '.(openssl_error_string() ?: 'unknown error'));
            }
            /** @var string $signature */
            return $signature;
        }
        // phpseclib loads an Ed25519 private key from the 32-byte seed followed by its public key.
        return \phpseclib4\Crypt\EC::loadFormat('libsodium', $seed.self::publicKey($seed))->sign($message);
    }

    /** Verify an Ed25519 signature against a 32-byte public key. */
    public static function verify(string $signature, string $message, string $publicKey): bool
    {
        if (\strlen($signature) !== 64 || \strlen($publicKey) !== 32) {
            return false;
        }
        if (self::backend() === 'sodium') {
            return sodium_crypto_sign_verify_detached($signature, $message, $publicKey);
        }
        if (self::backend() === 'openssl') {
            try {
                return openssl_verify($message, $signature, self::opensslKey(self::ED25519_SPKI_PREFIX, $publicKey, false), 0) === 1;
            } catch (\Throwable) {
                // Not a valid point.
                return false;
            }
        }
        try {
            return \phpseclib4\Crypt\EC::loadFormat('libsodium', $publicKey)->verify($message, $signature);
        } catch (\Throwable) {
            return false;
        }
    }

    /* ------------------------------------------------------------------ *
     *  X25519 key agreement (Keys.cpp compute_shared_secret).
     * ------------------------------------------------------------------ */

    /**
     * The tde2e shared secret between our seed and a peer's Ed25519 public key: X25519 over the keys
     * converted to Montgomery form, then `HMAC-SHA512(key = "tde2e_shared_secret", msg = x25519)[0:32]`.
     */
    public static function computeSharedSecret(string $seed, string $peerPublicKey): string
    {
        if (self::backend() === 'sodium') {
            $secretKey = sodium_crypto_sign_secretkey(sodium_crypto_sign_seed_keypair($seed));
            $raw = sodium_crypto_scalarmult(
                sodium_crypto_sign_ed25519_sk_to_curve25519($secretKey),
                sodium_crypto_sign_ed25519_pk_to_curve25519($peerPublicKey)
            );
        } elseif (self::backend() === 'openssl') {
            $raw = openssl_pkey_derive(
                self::opensslKey(self::X25519_SPKI_PREFIX, self::edPublicToX25519($peerPublicKey), false),
                self::opensslKey(self::X25519_PKCS8_PREFIX, self::edSeedToX25519($seed), true),
            );
            if ($raw === false) {
                // An all-zero shared secret (a low-order point), which sodium rejects too.
                throw new \RuntimeException('X25519 failed: '.(openssl_error_string() ?: 'unknown error'));
            }
        } else {
            $raw = self::x25519(self::edSeedToX25519($seed), self::edPublicToX25519($peerPublicKey));
        }
        // Note the key/msg order here is the opposite of kdfExpand: the purpose string is the key.
        return substr(hash_hmac('sha512', $raw, 'tde2e_shared_secret', true), 0, 32);
    }

    /* ------------------------------------------------------------------ *
     *  OpenSSL Curve25519 (PHP 8.4+), used without sodium.
     * ------------------------------------------------------------------ */

    /** The PKCS#8 encoding of an Ed25519 seed, up to the seed. */
    private const ED25519_PKCS8_PREFIX = "\x30\x2e\x02\x01\x00\x30\x05\x06\x03\x2b\x65\x70\x04\x22\x04\x20";
    /** The SubjectPublicKeyInfo of an Ed25519 public key, up to the key. */
    private const ED25519_SPKI_PREFIX = "\x30\x2a\x30\x05\x06\x03\x2b\x65\x70\x03\x21\x00";
    /** The PKCS#8 encoding of an X25519 private key, up to the key. */
    private const X25519_PKCS8_PREFIX = "\x30\x2e\x02\x01\x00\x30\x05\x06\x03\x2b\x65\x6e\x04\x22\x04\x20";
    /** The SubjectPublicKeyInfo of an X25519 public key, up to the key. */
    private const X25519_SPKI_PREFIX = "\x30\x2a\x30\x05\x06\x03\x2b\x65\x6e\x03\x21\x00";

    private static function hasOpensslCurve25519(): bool
    {
        return \extension_loaded('openssl') && \defined('OPENSSL_KEYTYPE_ED25519');
    }

    /**
     * Load a raw Curve25519 key into OpenSSL.
     *
     * @throws \RuntimeException If OpenSSL rejects it.
     */
    private static function opensslKey(string $prefix, string $raw, bool $private): \OpenSSLAsymmetricKey
    {
        $label = $private ? 'PRIVATE KEY' : 'PUBLIC KEY';
        $pem = "-----BEGIN $label-----\n".base64_encode($prefix.$raw)."\n-----END $label-----\n";
        $key = $private ? openssl_pkey_get_private($pem) : openssl_pkey_get_public($pem);
        if ($key === false) {
            throw new \RuntimeException('Invalid Curve25519 key: '.(openssl_error_string() ?: 'unknown error'));
        }
        return $key;
    }

    /* ------------------------------------------------------------------ *
     *  Pure-PHP X25519 fallback (RFC 7748 + Ed25519->Montgomery conversion), used without sodium or OpenSSL.
     * ------------------------------------------------------------------ */

    /**
     * Ed25519 seed -> clamped X25519 scalar: SHA-512(seed)[0:32] with the RFC 7748 clamp.
     *
     * @psalm-pure
     */
    private static function edSeedToX25519(string $seed): string
    {
        $s = substr(hash('sha512', $seed, true), 0, 32);
        $s[0] = \chr(\ord($s[0]) & 248);
        $s[31] = \chr((\ord($s[31]) & 127) | 64);
        return $s;
    }

    /** Ed25519 public key -> Montgomery u: u = (1 + y) / (1 - y) mod p, with the sign bit cleared. */
    private static function edPublicToX25519(string $publicKey): string
    {
        $p = Magic::$fieldPrime;
        $one = Magic::$one;
        $y = (new \phpseclib4\Math\BigInteger(strrev($publicKey), 256))
            ->bitwise_and((new \phpseclib4\Math\BigInteger(2))->pow(new \phpseclib4\Math\BigInteger(255))->subtract($one));
        $u = $one->add($y)->multiply($one->subtract($y)->modInverse($p))->powMod($one, $p);
        return self::toLe32($u);
    }

    /** X25519 scalar multiplication (RFC 7748 Montgomery ladder). */
    private static function x25519(string $scalar, string $u): string
    {
        $p = Magic::$fieldPrime;
        $one = Magic::$one;
        $a24 = new \phpseclib4\Math\BigInteger(121665);
        $k = new \phpseclib4\Math\BigInteger(strrev($scalar), 256);
        $x1 = new \phpseclib4\Math\BigInteger(strrev($u), 256);
        $x2 = $one;
        $z2 = Magic::$zero;
        $x3 = $x1;
        $z3 = $one;
        $swap = 0;
        for ($t = 254; $t >= 0; $t--) {
            $kt = $k->bitwise_rightShift($t)->bitwise_and($one)->equals($one) ? 1 : 0;
            $swap ^= $kt;
            if ($swap) {
                [$x2, $x3] = [$x3, $x2];
                [$z2, $z3] = [$z3, $z2];
            }
            $swap = $kt;
            $A = $x2->add($z2)->powMod($one, $p);
            $AA = $A->multiply($A)->powMod($one, $p);
            $B = $x2->subtract($z2)->powMod($one, $p);
            $BB = $B->multiply($B)->powMod($one, $p);
            $E = $AA->subtract($BB)->powMod($one, $p);
            $C = $x3->add($z3)->powMod($one, $p);
            $D = $x3->subtract($z3)->powMod($one, $p);
            $DA = $D->multiply($A)->powMod($one, $p);
            $CB = $C->multiply($B)->powMod($one, $p);
            $x3 = $DA->add($CB);
            $x3 = $x3->multiply($x3)->powMod($one, $p);
            $z3 = $DA->subtract($CB);
            $z3 = $z3->multiply($z3)->powMod($one, $p)->multiply($x1)->powMod($one, $p);
            $x2 = $AA->multiply($BB)->powMod($one, $p);
            $z2 = $E->multiply($AA->add($a24->multiply($E)->powMod($one, $p)))->powMod($one, $p);
        }
        if ($swap) {
            [$x2, $z2] = [$x3, $z3];
        }
        return self::toLe32($x2->multiply($z2->modInverse($p))->powMod($one, $p));
    }

    /** Encode a field element as a 32-byte little-endian string. */
    private static function toLe32(\phpseclib4\Math\BigInteger $n): string
    {
        return strrev(str_pad($n->toBytes(), 32, "\0", STR_PAD_LEFT));
    }

    /* ------------------------------------------------------------------ *
     *  Hashing helpers.
     * ------------------------------------------------------------------ */

    /**
     * SHA-256, raw bytes.
     *
     * @psalm-pure
     */
    public static function sha256(string $data): string
    {
        return hash('sha256', $data, true);
    }

    /**
     * Derive the actual group encryption key from the raw shared key for protocol version >= 1:
     * `HMAC-SHA512(key = raw, msg = blockHash)[0:32]` (Call.cpp update_group_shared_key).
     *
     * @psalm-pure
     */
    public static function deriveGroupKey(string $rawSharedKey, string $blockHash): string
    {
        return substr(hash_hmac('sha512', $blockHash, $rawSharedKey, true), 0, 32);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Tgcalls\E2E;

/**
 * Builds and reads the shared-state chain blocks of an end-to-end encrypted conference call
 * (subchain 0), following TDLib's tde2e Blockchain/Call logic and
 * [the protocol](https://core.telegram.org/api/end-to-end/group-calls).
 *
 * The client only ever *builds* blocks (the genesis block and membership changes) and reads state
 * back from the blocks the server echoes; the heavy validation (permissions, state proofs, the
 * key-value trie) is enforced server-side, and clients apply only server-returned blocks. This class
 * therefore focuses on constructing well-formed, correctly signed blocks and on recovering the group
 * state and shared key from a chain.
 *
 * @internal
 */
final class ConferenceChain
{
    /** Permission bits of e2e.chain.groupParticipant. */
    public const PERMISSION_ADD_USERS = 1;
    public const PERMISSION_REMOVE_USERS = 2;
    public const PERMISSION_SET_VALUE = 4;
    /**
     * The kv_hash of the empty key-value trie: tde2e hashes the TL serialization of its empty root
     * node (the int32 node type 0), not 32 zero bytes. It is what every block of a call that never
     * sets a value must carry in its state proof.
     */
    public const EMPTY_KV_HASH = "\xdf\x3f\x61\x98\x04\xa9\x2f\xdb\x40\x57\x19\x2d\xc4\x3d\xd7\x48\xea\x77\x8a\xdc\x52\xbc\x49\x8c\xe8\x05\x24\xc0\x14\xb8\x11\x19";
    /**
     * The permissions we take for ourselves when creating or joining a call, and grant to whoever joins
     * a call we created (its `external_permissions`): tde2e gives every member add+remove, so anyone
     * can join, and any member can prune the members that left.
     */
    public const PERMISSIONS_MEMBER = self::PERMISSION_ADD_USERS | self::PERMISSION_REMOVE_USERS;
    /** The highest protocol version we support, announced in our e2e.chain.groupParticipant. */
    public const PROTOCOL_VERSION = 1;

    private BlockCodec $codec;

    /** Our own user id. */
    private int $selfUserId;
    /** Our Ed25519 signing seed (private key). */
    private string $selfSeed;
    /** Our Ed25519 public key. */
    private string $selfPublicKey;

    /** Height of the last applied block, -1 before the genesis block. */
    private int $height = -1;
    /** Hash of the last applied block, 32 zero bytes before the genesis block. */
    private string $lastBlockHash;
    /**
     * Current participants, `user_id => ['public_key' => string, 'permissions' => int, 'version' => int]`.
     *
     * @var array<int, array{public_key: string, permissions: int, version: int}>
     */
    private array $participants = [];
    /** The current raw (pre-derivation) group shared key, or null if none is set. */
    private ?string $rawSharedKey = null;
    /**
     * The root hash of the key-value trie after the last applied block. We never set values, so it
     * only changes if another participant does: the state proof of every block we build must repeat it.
     */
    private string $kvHash = self::EMPTY_KV_HASH;

    public function __construct(int $selfUserId, string $selfSeed)
    {
        $this->codec = new BlockCodec();
        $this->selfUserId = $selfUserId;
        $this->selfSeed = $selfSeed;
        $this->selfPublicKey = Crypto::publicKey($selfSeed);
        $this->lastBlockHash = str_repeat("\0", 32);
    }

    public function getSelfPublicKey(): string
    {
        return $this->selfPublicKey;
    }

    public function getLastBlockHash(): string
    {
        return $this->lastBlockHash;
    }

    public function getHeight(): int
    {
        return $this->height;
    }

    /**
     * The group shared key actually used to encrypt media, derived from the raw key and the current
     * block hash (protocol version >= 1), or null if no shared key is set.
     *
     * @psalm-mutation-free
     */
    public function getGroupKey(): ?string
    {
        if ($this->rawSharedKey === null) {
            return null;
        }
        // Protocol version 0 (what tde2e-based clients announce) uses the raw key as is; the hashed
        // key is only used once every participant supports version 1, as tde2e's Call does.
        return $this->getVersion() >= 1 ? Crypto::deriveGroupKey($this->rawSharedKey, $this->lastBlockHash) : $this->rawSharedKey;
    }

    /**
     * The protocol version in effect: the smallest version announced by a participant, clamped to
     * 0..255 (0 for an empty group), as tde2e's GroupState::version().
     *
     * @psalm-mutation-free
     */
    public function getVersion(): int
    {
        if ($this->participants === []) {
            return 0;
        }
        $version = PHP_INT_MAX;
        foreach ($this->participants as $participant) {
            $version = min($version, $participant['version']);
        }
        return max(0, min(255, $version));
    }

    /**
     * Build and sign the genesis (height 0) block that creates the call with ourselves as the sole
     * participant and installs the first shared key: exactly the two changes tde2e's
     * Call::create_zero_block() emits.
     *
     * @param int $externalPermissions What a non-member may do to the chain: tde2e grants add+remove
     *                                 users, which is what lets anyone add themselves by joining.
     *
     * @return array{block: array<string, mixed>, serialized: string, hash: string, raw_shared_key: string}
     */
    public function buildGenesis(int $externalPermissions = self::PERMISSIONS_MEMBER): array
    {
        $participants = [$this->participantChange($this->selfUserId, $this->selfPublicKey, self::PERMISSIONS_MEMBER)];
        $raw = random_bytes(32);
        $changes = [
            [
                '_' => 'e2e.chain.changeSetGroupState',
                'group_state' => [
                    '_' => 'e2e.chain.groupState',
                    'participants' => $participants,
                    'external_permissions' => $externalPermissions,
                ],
            ],
            ['_' => 'e2e.chain.changeSetSharedKey', 'shared_key' => $this->buildSharedKey($raw, [[$this->selfUserId, $this->selfPublicKey]])],
        ];
        $block = $this->buildBlock(0, str_repeat("\0", 32), $changes);
        return [
            'block' => $block['block'],
            'serialized' => $block['serialized'],
            'hash' => $block['hash'],
            'raw_shared_key' => $raw,
        ];
    }

    /**
     * Build, sign, hash and serialize a block at the given height with the given changes, using our
     * key. Does not apply it â€” the caller submits it and applies only the server's echo.
     *
     * @param list<array<string, mixed>> $changes
     *
     * @return array{block: array<string, mixed>, serialized: string, hash: string}
     */
    public function buildBlock(int $height, string $prevBlockHash, array $changes): array
    {
        $block = [
            '_' => 'e2e.chain.block',
            'signature' => BlockCodec::ZERO_SIGNATURE,
            'prev_block_hash' => $prevBlockHash,
            'changes' => $changes,
            'height' => $height,
            // A block that changes the group state (every block we build) omits the group state and
            // the shared key from its proof; only the key-value root hash is carried (tde2e build_block).
            'state_proof' => ['_' => 'e2e.chain.stateProof', 'kv_hash' => $this->kvHash],
            'signature_public_key' => $this->selfPublicKey,
        ];
        $signed = $this->sign($block);
        $serialized = $this->codec->serialize($signed);
        return ['block' => $signed, 'serialized' => $serialized, 'hash' => $this->codec->blockHash($serialized)];
    }

    /**
     * Sign a block in place: zero its signature, serialize, Ed25519-sign that payload with our seed,
     * and store the signature. The signer public key is kept explicit here (the "omit if equal to the
     * first participant" optimization is left to a later pass, as it only shrinks the block).
     *
     * @param array<string, mixed> $block
     *
     * @return array<string, mixed>
     */
    public function sign(array $block): array
    {
        $block['signature'] = Crypto::sign($this->selfSeed, $this->codec->signingPayload($block));
        return $block;
    }

    /**
     * Verify a block's Ed25519 signature against its declared (or inherited) signer public key.
     *
     * @param array<string, mixed> $block
     */
    public function verify(array $block): bool
    {
        $signerKey = $block['signature_public_key'] ?? ($this->participants[array_key_first($this->participants) ?? -1]['public_key'] ?? null);
        if (!\is_string($signerKey)) {
            return false;
        }
        return Crypto::verify($block['signature'], $this->codec->signingPayload($block), $signerKey);
    }

    /**
     * Apply a block the server echoed back, updating our view of the group state and shared key. The
     * server has already validated it; we recover state and, when a shared key targets us, decrypt it.
     *
     * @param string $serialized The block bytes exactly as the server returned them (server id).
     *
     * @return bool Whether the block was the next one and was applied (false = duplicate/out of order).
     */
    public function applyServerBlock(string $serialized): bool
    {
        $block = $this->codec->deserialize($serialized);
        // Apply strictly in order and exactly once: the same block may reach us from both the push
        // update and the polling backstop. A block that is not the next one is ignored (the server
        // enforces the ordering, so this is not an error, just a duplicate/out-of-order delivery).
        if ((int) $block['height'] !== $this->height + 1) {
            return false;
        }
        // Hash the block in its canonical (local-id) form, as tde2e does.
        $canonical = $this->codec->serialize($block);
        foreach ($block['changes'] ?? [] as $change) {
            $this->applyChange($change);
        }
        $this->height = (int) $block['height'];
        $this->lastBlockHash = $this->codec->blockHash($canonical);
        $kvHash = (string) ($block['state_proof']['kv_hash'] ?? '');
        if (\strlen($kvHash) === 32) {
            $this->kvHash = $kvHash;
        }
        return true;
    }

    /**
     * @param array<string, mixed> $change
     */
    private function applyChange(array $change): void
    {
        switch ($change['_'] ?? '') {
            case 'e2e.chain.changeSetGroupState':
                $this->participants = [];
                foreach ($change['group_state']['participants'] ?? [] as $participant) {
                    $permissions = 0;
                    $permissions |= ($participant['add_users'] ?? false) ? self::PERMISSION_ADD_USERS : 0;
                    $permissions |= ($participant['remove_users'] ?? false) ? self::PERMISSION_REMOVE_USERS : 0;
                    $permissions |= ($participant['set_value'] ?? false) ? self::PERMISSION_SET_VALUE : 0;
                    $this->participants[(int) $participant['user_id']] = [
                        'public_key' => (string) $participant['public_key'],
                        'permissions' => $permissions,
                        'version' => (int) ($participant['version'] ?? 0),
                    ];
                }
                // A group-state change clears the shared key, as in tde2e.
                $this->rawSharedKey = null;
                break;
            case 'e2e.chain.changeSetSharedKey':
                $this->rawSharedKey = $this->decryptSharedKey($change['shared_key']);
                break;
        }
    }

    /**
     * Build an e2e.chain.sharedKey distributing a raw group key to each participant: an ephemeral key
     * agrees a secret with every participant, which wraps the one-time secret that in turn encrypts
     * the raw key.
     *
     * @param string                       $raw          The 32-byte raw group key.
     * @param list<array{0: int, 1: string}> $recipients `[user_id, public_key]` for every participant.
     *
     * @return array<string, mixed>
     */
    public function buildSharedKey(string $raw, array $recipients): array
    {
        [$ephemeralSeed, $ephemeralPublic] = Crypto::generateKeyPair();
        $oneTime = random_bytes(32);
        [$encryptedGroupKey] = Crypto::encryptData($raw, $oneTime);
        $destUserIds = [];
        $destHeaders = [];
        foreach ($recipients as [$userId, $publicKey]) {
            $shared = Crypto::computeSharedSecret($ephemeralSeed, $publicKey);
            $destUserIds[] = $userId;
            $destHeaders[] = Crypto::encryptHeader($oneTime, $encryptedGroupKey, $shared);
        }
        return [
            '_' => 'e2e.chain.sharedKey',
            'ek' => $ephemeralPublic,
            'encrypted_shared_key' => $encryptedGroupKey,
            'dest_user_id' => $destUserIds,
            'dest_header' => $destHeaders,
        ];
    }

    /**
     * Recover the raw group key from an e2e.chain.sharedKey addressed to us, or null if we are not a
     * recipient.
     *
     * @param array<string, mixed> $sharedKey
     */
    public function decryptSharedKey(array $sharedKey): ?string
    {
        $index = array_search($this->selfUserId, $sharedKey['dest_user_id'] ?? [], true);
        if ($index === false) {
            return null;
        }
        // Deserialized `bytes`/`string` fields may arrive as TL Bytes objects; coerce to raw strings.
        $ek = (string) $sharedKey['ek'];
        $encryptedGroupKey = (string) $sharedKey['encrypted_shared_key'];
        $header = (string) $sharedKey['dest_header'][$index];
        $shared = Crypto::computeSharedSecret($this->selfSeed, $ek);
        $oneTime = Crypto::decryptHeader($header, $encryptedGroupKey, $shared);
        $raw = Crypto::decryptData($encryptedGroupKey, $oneTime);
        return \strlen($raw) === 32 ? $raw : null;
    }

    /**
     * Build an e2e.chain.changeSetGroupState from a participant list.
     *
     * @param list<array{0: int, 1: string, 2: int, 3?: int}> $participants `[user_id, public_key, permissions, version]`; the version defaults to ours.
     *
     * @return array<string, mixed>
     *
     * @psalm-mutation-free
     */
    public function groupStateChange(array $participants, int $externalPermissions = self::PERMISSIONS_MEMBER): array
    {
        $list = [];
        foreach ($participants as $participant) {
            [$userId, $publicKey, $permissions] = $participant;
            $list[] = $this->participantChange($userId, $publicKey, $permissions, $participant[3] ?? self::PROTOCOL_VERSION);
        }
        return [
            '_' => 'e2e.chain.changeSetGroupState',
            'group_state' => [
                '_' => 'e2e.chain.groupState',
                'participants' => $list,
                'external_permissions' => $externalPermissions,
            ],
        ];
    }

    /**
     * @return array<string, mixed> An e2e.chain.groupParticipant.
     *
     * @psalm-pure
     */
    private function participantChange(int $userId, string $publicKey, int $permissions, int $version = self::PROTOCOL_VERSION): array
    {
        return [
            '_' => 'e2e.chain.groupParticipant',
            'user_id' => $userId,
            'public_key' => $publicKey,
            'add_users' => ($permissions & self::PERMISSION_ADD_USERS) !== 0,
            'remove_users' => ($permissions & self::PERMISSION_REMOVE_USERS) !== 0,
            'set_value' => ($permissions & self::PERMISSION_SET_VALUE) !== 0,
            'version' => $version,
        ];
    }

    /**
     * The current participants as `[user_id, public_key, permissions, version]` tuples, ready to be
     * carried over unchanged into the next group state.
     *
     * @return list<array{0: int, 1: string, 2: int, 3: int}>
     *
     * @psalm-mutation-free
     */
    public function getParticipantTuples(): array
    {
        $tuples = [];
        foreach ($this->participants as $userId => $info) {
            $tuples[] = [$userId, $info['public_key'], $info['permissions'], $info['version']];
        }
        return $tuples;
    }

    /**
     * Our own permission bits in the current group state, or 0 if we are not (yet) a member.
     *
     * @psalm-mutation-free
     */
    public function getSelfPermissions(): int
    {
        return $this->participants[$this->selfUserId]['permissions'] ?? 0;
    }

    /**
     * @return array<int, array{public_key: string, permissions: int, version: int}>
     */
    public function getParticipants(): array
    {
        return $this->participants;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Tgcalls\E2E;

/**
 * Frame-level packet encryption for end-to-end encrypted conference calls, ported from TDLib's tde2e
 * CallEncryption (Call.cpp). Media is encrypted per packet on top of WebRTC (this is *not* SRTP): each
 * packet is bound to the set of currently active epochs (by their block hash) and signed by the
 * sender, so any participant with the epoch key can decrypt it and every participant can verify who
 * sent it.
 *
 * This class is the pure codec; wiring it into the RTP send/receive path (encrypting each outgoing
 * payload, decrypting each incoming one, keyed by the current epochs) is done by the call engine.
 *
 * @internal
 */
final class CallPacket
{
    /** TL id of e2e.callPacket, mixed into the encrypted-data authenticated extra (magic1). */
    private const MAGIC_PACKET = 0x40a6bee9;
    /** TL id of e2e.callPacketLargeMsgId, prefixed to the signed message (magic2). */
    private const MAGIC_LARGE_MSG_ID = 0x1ce56c2d;
    /** Maximum simultaneously active epochs (tde2e MAX_ACTIVE_EPOCHS). */
    public const MAX_ACTIVE_EPOCHS = 15;
    /** Valid channel-id range (tde2e validate_channel_id). */
    public const MAX_CHANNEL_ID = 1023;

    /**
     * Encrypt one packet for the given channel and active epochs, signed with our key.
     *
     * @param int                                    $channelId  0..1023.
     * @param int                                    $seqno      Unique, monotonically increasing per (us, channel).
     * @param string                                 $plaintext  The bytes to encrypt (e.g. an RTP payload).
     * @param list<array{hash: string, secret: string}> $epochs   Active epochs: block hash + group key.
     * @param string                                 $seed       Our Ed25519 signing seed.
     * @param string                                 $unencryptedPrefix Bytes kept in the clear at the front.
     */
    public static function encrypt(int $channelId, int $seqno, string $plaintext, array $epochs, string $seed, string $unencryptedPrefix = ''): string
    {
        if ($channelId < 0 || $channelId > self::MAX_CHANNEL_ID) {
            throw new \InvalidArgumentException('channel id out of range');
        }
        if ($epochs === [] || \count($epochs) > self::MAX_ACTIVE_EPOCHS) {
            throw new \InvalidArgumentException('need 1..'.self::MAX_ACTIVE_EPOCHS.' active epochs');
        }
        $count = \count($epochs);
        // head int32 = count | version(0)<<8 | reserved(0)<<16; then one block hash per epoch.
        $headerA = pack('V', $count);
        foreach ($epochs as $epoch) {
            $headerA .= $epoch['hash'];
        }

        $packetPayload = pack('l', $channelId).pack('V', $seqno).$plaintext;
        $oneTimeKey = random_bytes(32);
        $extra = pack('V', self::MAGIC_PACKET).$headerA.$unencryptedPrefix;
        [$encryptedPayload, $largeMsgId] = Crypto::encryptData($packetPayload, $oneTimeKey, $extra);

        $signature = Crypto::sign($seed, pack('V', self::MAGIC_LARGE_MSG_ID).$largeMsgId);

        $headerB = '';
        foreach ($epochs as $epoch) {
            $headerB .= Crypto::encryptHeader($oneTimeKey, $encryptedPayload, $epoch['secret']);
        }

        return $unencryptedPrefix.$headerA.$headerB.$encryptedPayload.$signature.pack('V', \strlen($unencryptedPrefix));
    }

    /**
     * Decrypt and verify a packet.
     *
     * @param string                          $packet          The wire packet.
     * @param array<string, string>           $epochSecrets    Group key by epoch block hash (hex or raw key by raw hash).
     * @param string                          $senderPublicKey The sender's Ed25519 public key, for signature verification.
     *
     * @return array{channel_id: int, seqno: int, payload: string, unencrypted_prefix: string}
     */
    public static function decrypt(string $packet, array $epochSecrets, string $senderPublicKey): array
    {
        if (\strlen($packet) < 4) {
            throw new \RuntimeException('packet too short');
        }
        $prefixLen = unpack('V', substr($packet, -4))[1];
        if ($prefixLen > \strlen($packet) - 4) {
            throw new \RuntimeException('bad unencrypted prefix length');
        }
        $unencryptedPrefix = substr($packet, 0, $prefixLen);
        $body = substr($packet, $prefixLen, \strlen($packet) - $prefixLen - 4);

        if (\strlen($body) < 4) {
            throw new \RuntimeException('packet body too short');
        }
        $head = unpack('V', substr($body, 0, 4))[1];
        $count = $head & 0xff;
        if ((($head >> 8) & 0xff) !== 0 || ($head >> 16) !== 0) {
            throw new \RuntimeException('unsupported packet version');
        }
        if ($count < 1 || $count > self::MAX_ACTIVE_EPOCHS) {
            throw new \RuntimeException('bad epoch count');
        }
        $headerA = substr($body, 0, 4 + $count * 32);
        $offset = 4;
        $epochHashes = [];
        for ($i = 0; $i < $count; $i++) {
            $epochHashes[] = substr($body, $offset, 32);
            $offset += 32;
        }
        $headerB = [];
        for ($i = 0; $i < $count; $i++) {
            $headerB[] = substr($body, $offset, 32);
            $offset += 32;
        }
        $encryptedPacket = substr($body, $offset);
        if (\strlen($encryptedPacket) < 64 + 16) {
            throw new \RuntimeException('packet payload too short');
        }
        $signature = substr($encryptedPacket, -64);
        $encryptedPayload = substr($encryptedPacket, 0, -64);

        $extra = pack('V', self::MAGIC_PACKET).$headerA.$unencryptedPrefix;
        foreach ($epochHashes as $i => $hash) {
            $secret = $epochSecrets[$hash] ?? null;
            if ($secret === null) {
                continue;
            }
            $oneTimeKey = Crypto::decryptHeader($headerB[$i], $encryptedPayload, $secret);
            [$packetPayload, $largeMsgId] = Crypto::decryptDataWithMsgId($encryptedPayload, $oneTimeKey, $extra);
            if (!Crypto::verify($signature, pack('V', self::MAGIC_LARGE_MSG_ID).$largeMsgId, $senderPublicKey)) {
                throw new \RuntimeException('bad packet signature');
            }
            if (\strlen($packetPayload) < 8) {
                throw new \RuntimeException('decrypted packet too short');
            }
            $channelId = unpack('l', substr($packetPayload, 0, 4))[1];
            $seqno = unpack('V', substr($packetPayload, 4, 4))[1];
            return [
                'channel_id' => $channelId,
                'seqno' => $seqno,
                'payload' => substr($packetPayload, 8),
                'unencrypted_prefix' => $unencryptedPrefix,
            ];
        }
        throw new \RuntimeException('no known epoch for packet');
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Tgcalls\E2E;

use Amp\ByteStream\ReadableStream;
use Amp\ByteStream\WritableStream;
use danog\MadelineProto\CallStream;
use danog\MadelineProto\EventHandler\Call;
use danog\MadelineProto\EventHandler\Calls\AbstractGroupCallParticipant;
use danog\MadelineProto\EventHandler\Calls\ConferenceCall as ConferenceCallUpdate;
use danog\MadelineProto\EventHandler\Calls\ConferenceCallParticipant;
use danog\MadelineProto\EventHandler\Calls\GroupCallParticipant;
use danog\MadelineProto\EventHandler\Calls\GroupCallState;
use danog\MadelineProto\LocalDirectory;
use danog\MadelineProto\LocalFile;
use danog\MadelineProto\Logger;
use danog\MadelineProto\Loop\VoIP\DjLoop;
use danog\MadelineProto\MediaDestination;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\ParseMode;
use danog\MadelineProto\RecordingFormat;
use danog\MadelineProto\RemoteUrl;
use danog\MadelineProto\RestoresDeclaredProperties;
use danog\MadelineProto\RPCErrorException;
use danog\MadelineProto\TextEntities;
use danog\MadelineProto\Tgcalls\CallControllerInterface;
use danog\MadelineProto\Tgcalls\GroupConnection;
use danog\MadelineProto\Tgcalls\GroupConnectionOwner;
use danog\MadelineProto\Tgcalls\GroupMediaTrait;
use danog\MadelineProto\Tgcalls\GroupSdp;
use Revolt\EventLoop;
use Throwable;

/**
 * Controller for a Telegram end-to-end encrypted conference call
 * ([end-to-end group calls](https://core.telegram.org/api/end-to-end/group-calls)).
 *
 * It owns the two block subchains ({@see ConferenceChain} + {@see Verification}), turns the accepted
 * blocks into media-encryption epochs, and drives a {@see GroupConnection} whose RTP frames are
 * end-to-end encrypted by a {@see FrameCryptor} â€” the SFU only ever forwards ciphertext. It is both
 * the connection's owner and the cryptor's key provider.
 *
 * This is the internal controller; the public handle library users receive from
 * {@see \danog\MadelineProto\MTProto::createConferenceCall()} and
 * {@see \danog\MadelineProto\MTProto::joinConferenceCall()} is the {@see ConferenceCallUpdate}
 * returned by {@see self::getPublic()}, which delegates back here by call id. The controller
 * implements the common {@see Call} media interface plus conference-specific controls (verification,
 * encrypted messages).
 *
 * @psalm-import-type StreamMask from \danog\MadelineProto\CallStream
 */
final class ConferenceCall implements GroupConnectionOwner, E2EKeyProvider, CallControllerInterface
{
    use RestoresDeclaredProperties;
    use GroupMediaTrait;

    /** Subchain ids: 0 = shared-state chain, 1 = commit-reveal verification broadcasts. */
    private const SUBCHAIN_STATE = 0;
    private const SUBCHAIN_VERIFICATION = 1;
    /** How many recent epochs to keep so media from just before a rekey still decrypts. */
    private const MAX_EPOCHS = 15;

    /** The inputGroupCall of this conference, set once it exists (after create/join). */
    private ?array $inputCall = null;
    private int $selfId;
    /** Our Ed25519 signing seed: signs both chain blocks and media/message packets. */
    private string $selfSeed;
    private ConferenceChain $chain;
    private FrameCryptor $frameCryptor;
    /** Packet counters shared by the camera and screen-share cryptors (serialized with the call). */
    private FrameCryptorState $cryptorState;
    /** Frame cryptor for the screen-share, on its own channel so its seqnos don't collide with camera. */
    private ?FrameCryptor $presentationCryptor = null;

    /**
     * Active media epochs, `blockHash => ['hash' => string, 'secret' => string]`, oldest first.
     *
     * @var array<string, array{hash: string, secret: string}>
     */
    private array $epochs = [];
    /**
     * Next chain offset to request per subchain (`sub_chain_id => next block height`).
     *
     * @var array{0: int, 1: int}
     */
    private array $chainOffset = [0, 0];
    /** @var array<int, int> SSRC (unsigned) => participant user id, for verifying incoming media senders. */
    private array $ssrcToUser = [];
    /** @var array<int, int> user id => signed audio source, learned from the participant list, for recording. */
    private array $userToSource = [];
    /**
     * The commit-reveal verification of the current chain head, see {@see self::restartVerification()}:
     * the head it is for, its phase, our own commit/reveal broadcasts (and whether the commit was
     * sent), the members (user id => public key) that must take part, what they committed/revealed,
     * and the resulting emojis once everyone revealed.
     *
     * @var array{height: int, hash: string, state: 'commit'|'reveal'|'end', sent: bool, commit: array<string, mixed>, reveal: array<string, mixed>, participants: array<int, string>, committed: array<int, string>, revealed: array<int, string>, emojis: list<string>|null}|null
     */
    private ?array $verification = null;
    /** @var array<int, list<string>> Verification broadcasts for chain heights we have not reached yet, by height. */
    private array $delayedBroadcasts = [];
    /**
     * The participants of the underlying group call (the RTC side: sources, video, screen-share), by
     * user id, from phone.getGroupCall and updateGroupCallParticipants.
     *
     * @var array<int, GroupCallParticipant>
     */
    private array $rtcParticipants = [];
    /** Version of the underlying groupCall, for ordering participant updates. */
    private int $callVersion = 0;
    /** The raw groupCall constructor of the conference, as last seen. */
    private ?array $rawCall = null;
    /** Whether we are leaving on purpose, so that being forbidden is not answered with a rejoin. */
    private bool $leaving = false;
    /** Whether an automatic rejoin is already scheduled. */
    private bool $rejoinScheduled = false;
    /** Counts backstop poll ticks, to poll every 5 seconds outside of key verification. */
    private int $pollTick = 0;
    /** @var array<string, true> Recently seen in-call message ids (`from:random_id`), for deduplication. */
    private array $seenMessages = [];

    private bool $joined = false;
    /** Event-loop id of the backstop chain poll, so it can be cancelled on leave. */
    private ?string $pollWatcher = null;

    /** The public event-handler handle for this conference, built lazily once the call exists. */
    private ?ConferenceCallUpdate $public = null;

    public function __construct(
        public readonly MTProto $API,
    ) {
        $self = $this->API->getSelf();
        if ($self === false) {
            throw new \RuntimeException('Cannot start a conference call without being logged in.');
        }
        $this->selfId = $self['id'];
        [$this->selfSeed] = Crypto::generateKeyPair();
        $this->chain = new ConferenceChain($this->selfId, $this->selfSeed);
        $this->cryptorState = new FrameCryptorState();
        $this->frameCryptor = new FrameCryptor($this, $this->cryptorState);
        $this->diskJockey = new DjLoop($this);
        $this->diskJockey->start();
    }

    /**
     * Keep everything serializable across a restart: the WebRTC connection, chain, epochs, keys and
     * disk jockey all serialize themselves; only the event-loop poll id cannot and is recreated.
     *
     * @return array<string, mixed>
     *
     * @psalm-mutation-free
     */
    public function __serialize(): array
    {
        $vars = get_object_vars($this);
        unset($vars['pollWatcher'], $vars['resumePending']);
        return $vars;
    }

    /**
     * @param array<string, mixed> $data
     */
    public function __unserialize(array $data): void
    {
        // Synchronous restoration only â€” no async work here (that goes in resume()).
        $this->restoreDeclaredProperties($data);
        // Sessions serialized before the packet counters were shared carry no state object.
        $this->cryptorState ??= new FrameCryptorState();
        $this->pollWatcher = null;
        $this->diskJockey->start();
        $this->presentationDj?->start();
        // Bring the rest of the graph back once the MTProto session is reset and connected: resumed by
        // MTProto::wakeup(), since resuming makes requests and those sent before the reset are lost.
        $this->resumePending = true;
    }

    /** Whether the conference was restored from a session, and is yet to be resumed by {@see self::resumeAfterRestart()}. */
    private bool $resumePending = false;

    /**
     * Resume the conference after the process restarted: reopen the media connection, re-register for
     * push updates, restart the backstop poll and catch up on any blocks missed while stopped. Named
     * to avoid clashing with the {@see Call} playback {@see self::resume()}.
     *
     * Called by {@see MTProto::wakeup()} once the MTProto session is reset and connected.
     *
     * @internal
     */
    public function resumeAfterRestart(): void
    {
        if (!$this->resumePending) {
            return;
        }
        $this->resumePending = false;
        if (!$this->joined || $this->inputCall === null) {
            return;
        }
        // Restart the demuxers/readers (DjLoop::__unserialize() leaves them dormant), or nothing is
        // transmitted after a restart.
        $this->diskJockey->resumeReader();
        $this->presentationDj?->resumeReader();
        $this->connection?->resume();
        $this->presentationConnection?->resume();
        $this->API->registerConferenceCall($this->inputCall['id'], $this);
        $this->startPolling();
        $this->syncChain(self::SUBCHAIN_STATE);
        $this->syncChain(self::SUBCHAIN_VERIFICATION);
        $this->refetchParticipants();
        $this->log("Resumed E2E conference $this after a restart", Logger::NOTICE);
    }

    /**
     * Point this controller at an existing conference (its groupCall), before joining it.
     *
     * @psalm-external-mutation-free
     */
    public function setCall(array $call): void
    {
        $this->inputCall = [
            '_' => 'inputGroupCall',
            'id' => $call['id'],
            'access_hash' => $call['access_hash'],
        ];
    }

    /**
     * @return array The inputGroupCall, once the conference exists.
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function getInputCall(): array
    {
        return $this->inputCall ?? throw new \RuntimeException('The conference call does not exist yet.');
    }

    /**
     * The public {@see ConferenceCallUpdate} handle for this conference, built lazily once the call
     * exists. This is the object handed to library users; it delegates every operation back to this
     * controller by call id.
     *
     * @psalm-external-mutation-free
     */
    public function getPublic(): ConferenceCallUpdate
    {
        $call = $this->getInputCall();
        return $this->public ??= new ConferenceCallUpdate($this->API, $this->rawCall ?? [
            '_' => 'groupCall',
            'id' => $call['id'],
            'access_hash' => $call['access_hash'],
            'conference' => true,
        ]);
    }

    /**
     * Whether we are currently in the conference (joined and not left/forbidden).
     *
     * @psalm-mutation-free
     */
    public function isJoined(): bool
    {
        return $this->joined;
    }

    #[\Override]
    public function getVoIPSettings(): \danog\MadelineProto\Settings\VoIP
    {
        return $this->API->getSettings()->getVoip();
    }

    /**
     * Whether we left (or discarded) the conference for good: the playback machinery stops then, but
     * not while we are merely between a drop and the automatic re-join.
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function isCallEnded(): bool
    {
        return $this->leaving;
    }

    /**
     * Get the state of the conference call.
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function getCallState(): GroupCallState
    {
        return $this->joined ? GroupCallState::JOINED : GroupCallState::NOT_JOINED;
    }

    /* ------------------------------------------------------------------ *
     *  E2EKeyProvider â€” the live keys the frame cryptor uses.
     * ------------------------------------------------------------------ */

    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function activeEpochs(): array
    {
        // Newest first, so the encryptor's primary epoch is the current one.
        return array_reverse(array_values($this->epochs));
    }

    #[\Override]
    public function selfSeed(): string
    {
        return $this->selfSeed;
    }

    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function publicKeyForSsrc(int $ssrc): ?string
    {
        $userId = $this->ssrcToUser[$ssrc] ?? null;
        if ($userId === null) {
            return null;
        }
        return $this->chain->getParticipants()[$userId]['public_key'] ?? null;
    }

    /* ------------------------------------------------------------------ *
     *  Lifecycle: create / join.
     * ------------------------------------------------------------------ */

    /**
     * Create a brand-new conference call with ourselves as the sole participant and join it. Returns
     * the raw `phone.groupCall` this controller now drives.
     */
    public function create(bool $muted = false): void
    {
        $genesis = $this->chain->buildGenesis();
        $this->connection = $this->replaceConnection();
        $params = $this->connection->buildJoinPayload();

        $updates = $this->API->methodCallAsyncRead('phone.createConferenceCall', [
            'muted' => $muted,
            'video_stopped' => true,
            'join' => true,
            'random_id' => random_int(-2 ** 31, 2 ** 31 - 1),
            'public_key' => $this->chain->getSelfPublicKey(),
            'block' => $genesis['serialized'],
            'params' => $params,
        ]);
        $this->extractCall($updates);
        // Our genesis block is authoritative once the server accepted it.
        $this->chain->applyServerBlock($genesis['serialized']);
        $this->refreshEpoch();
        $this->chainOffset = [$this->chain->getHeight() + 1, 0];
        $this->consumeUpdates($updates);
        $this->joined = true;
        $this->leaving = false;
        $this->API->registerConferenceCall($this->getInputCall()['id'], $this);
        $this->startPolling();
        $this->restartVerification();
        $this->refetchParticipants();
        $this->log("Created and joined E2E conference $this", Logger::NOTICE);
    }

    /**
     * Join an existing conference call: fetch the chain, add ourselves in a new block, and join with
     * that block.
     */
    public function join(bool $muted = false): self
    {
        $this->syncChain(self::SUBCHAIN_STATE);
        $this->connection = $this->replaceConnection();
        $params = $this->connection->buildJoinPayload();

        // If another member's block took our height while we were building, rebuild the self-add
        // block on the new head and re-join; the connection/join-payload are reused.
        $updates = $this->submitWithChainRetry(fn (): array => $this->API->methodCallAsyncRead('phone.joinGroupCall', [
            'muted' => $muted,
            'video_stopped' => true,
            'call' => $this->inputCall,
            'join_as' => ['_' => 'inputPeerSelf'],
            'public_key' => $this->chain->getSelfPublicKey(),
            'block' => $this->buildSelfAddBlock()['serialized'],
            'params' => $params,
        ]));
        $this->consumeUpdates($updates);
        $this->joined = true;
        $this->leaving = false;
        $this->API->registerConferenceCall($this->getInputCall()['id'], $this);
        // Re-sync so our own accepted block (and any concurrent ones) are applied in server order.
        $this->syncChain(self::SUBCHAIN_STATE);
        $this->startPolling();
        // Verification of the current head starts as blocks are applied; make sure our commit went out
        // for a head that was applied before we were joined.
        $this->restartVerification();
        $this->ensureVerificationBroadcast();
        $this->refetchParticipants();
        $this->log("Joined E2E conference $this", Logger::NOTICE);
        return $this;
    }

    /**
     * Build a block that adds us to the current group, carrying a fresh shared key for the new member
     * set. Requires the chain to already reflect the current members.
     *
     * @return array{block: array<string, mixed>, serialized: string, hash: string}
     */
    private function buildSelfAddBlock(): array
    {
        $participants = [];
        $recipients = [];
        foreach ($this->chain->getParticipantTuples() as $tuple) {
            if ($tuple[0] === $this->selfId) {
                continue; // a stale entry of ours (an earlier key) is replaced, as tde2e does
            }
            $participants[] = $tuple;
            $recipients[] = [$tuple[0], $tuple[1]];
        }
        // The same permissions tde2e takes for itself: they let us prune members that left.
        $participants[] = [$this->selfId, $this->chain->getSelfPublicKey(), ConferenceChain::PERMISSIONS_MEMBER, ConferenceChain::PROTOCOL_VERSION];
        $recipients[] = [$this->selfId, $this->chain->getSelfPublicKey()];

        $raw = random_bytes(32);
        $changes = [
            $this->chain->groupStateChange($participants),
            ['_' => 'e2e.chain.changeSetSharedKey', 'shared_key' => $this->chain->buildSharedKey($raw, $recipients)],
        ];
        return $this->chain->buildBlock($this->chain->getHeight() + 1, $this->chain->getLastBlockHash(), $changes);
    }

    /**
     * Change a participant's state (phone.editGroupCallParticipant): mute them for ourselves, set our
     * playback volume of them, or pause/resume our own video.
     */
    public function editParticipant(string|int|array $participant, ?bool $muted = null, ?int $volume = null, ?bool $videoPaused = null): void
    {
        $params = ['call' => $this->getInputCall(), 'participant' => $participant];
        if ($muted !== null) {
            $params['muted'] = $muted;
        }
        if ($volume !== null) {
            if ($volume < 1 || $volume > 20000) {
                throw new \InvalidArgumentException('The volume must be between 1 and 20000 (10000 = 100%).');
            }
            $params['volume'] = $volume;
        }
        if ($videoPaused !== null) {
            $params['video_paused'] = $videoPaused;
        }
        $this->API->methodCallAsyncRead('phone.editGroupCallParticipant', $params);
    }

    /**
     * Change the conference's settings (phone.toggleGroupCallSettings): whether new members join
     * muted, whether in-call messages are enabled, or invalidate its conference link.
     */
    public function toggleSettings(?bool $joinMuted = null, bool $resetInviteHash = false, ?bool $messagesEnabled = null): void
    {
        $params = ['call' => $this->getInputCall(), 'reset_invite_hash' => $resetInviteHash];
        if ($joinMuted !== null) {
            $params['join_muted'] = $joinMuted;
        }
        if ($messagesEnabled !== null) {
            $params['messages_enabled'] = $messagesEnabled;
        }
        $updates = $this->API->methodCallAsyncRead('phone.toggleGroupCallSettings', $params);
        if ($resetInviteHash) {
            // The new link comes back in the updateGroupCall of the response, or on the next fetch.
            $this->extractCall($updates);
        }
    }

    /**
     * Invite users to the conference call (phone.inviteConferenceCallParticipant), ringing them; once
     * they accept they add themselves to the chain with their own self-join block.
     */
    public function invite(string|int ...$users): self
    {
        /** @var string|int $user */
        foreach ($users as $user) {
            $this->API->methodCallAsyncRead('phone.inviteConferenceCallParticipant', [
                'call' => $this->getInputCall(),
                'user_id' => $user,
            ]);
        }
        return $this;
    }

    /**
     * The [conference link Â»](https://core.telegram.org/api/links#conference-links) of the call: it is
     * created with the call and carried by its groupCall (`invite_link`), so, like official clients, we
     * read it from there rather than exporting one.
     */
    public function exportInvite(bool $canSelfUnmute = false): string
    {
        $link = (string) ($this->rawCall['invite_link'] ?? '');
        if ($link === '') {
            $this->refetchParticipants();
            $link = (string) ($this->rawCall['invite_link'] ?? '');
        }
        if ($link === '') {
            throw new \RuntimeException("The server did not provide an invite link for $this.");
        }
        return $link;
    }

    /**
     * Remove participants from the conference: build a block dropping them and rekeying for the
     * remaining members, then submit it with phone.deleteConferenceCallParticipants. Requires the
     * `remove_users` permission. The removed members can no longer decrypt media once the new epoch
     * takes over.
     */
    public function removeParticipant(string|int ...$participants): self
    {
        $this->submitRemoval(array_values(array_map($this->API->getId(...), $participants)), kick: true);
        return $this;
    }

    /**
     * Drop members from the chain, rekeying for the remaining ones, with
     * phone.deleteConferenceCallParticipants: `kick` forcibly removes active members (requires the
     * `remove_users` permission), otherwise the `only_left` flag prunes members that already left the
     * call. Members not in the chain (and ourselves) are ignored.
     *
     * @param list<int> $userIds
     */
    private function submitRemoval(array $userIds, bool $kick): void
    {
        $remove = [];
        foreach ($userIds as $userId) {
            if ($userId !== $this->selfId && isset($this->chain->getParticipants()[$userId])) {
                $remove[$userId] = true;
            }
        }
        if ($remove === []) {
            return;
        }
        $updates = $this->submitWithChainRetry(function () use ($remove, $kick): array {
            // Built from the current chain state each attempt, so a retry rekeys on the fresh head.
            $participants = [];
            $recipients = [];
            foreach ($this->chain->getParticipantTuples() as $tuple) {
                if (isset($remove[$tuple[0]])) {
                    continue;
                }
                $participants[] = $tuple;
                $recipients[] = [$tuple[0], $tuple[1]];
            }
            if ($recipients === []) {
                throw new \RuntimeException('Cannot remove every participant from the conference.');
            }
            $raw = random_bytes(32);
            $block = $this->chain->buildBlock($this->chain->getHeight() + 1, $this->chain->getLastBlockHash(), [
                $this->chain->groupStateChange($participants),
                ['_' => 'e2e.chain.changeSetSharedKey', 'shared_key' => $this->chain->buildSharedKey($raw, $recipients)],
            ]);
            return $this->API->methodCallAsyncRead('phone.deleteConferenceCallParticipants', [
                'kick' => $kick,
                'only_left' => !$kick,
                'call' => $this->getInputCall(),
                'ids' => array_keys($remove),
                'block' => $block['serialized'],
            ]);
        });
        $this->consumeUpdates($updates);
        // Apply our own accepted block (and anything concurrent) in server order.
        $this->syncChain(self::SUBCHAIN_STATE);
    }

    /**
     * Prune members of the chain that are no longer in the call (they left, or are missing from a
     * complete participant list), so the key is rotated away from them and verification no longer
     * waits for them. Requires the `remove_users` permission, which every member normally holds.
     *
     * @param list<int> $userIds
     */
    private function pruneStale(array $userIds): void
    {
        if (!$this->joined || ($this->chain->getSelfPermissions() & ConferenceChain::PERMISSION_REMOVE_USERS) === 0) {
            return;
        }
        try {
            $this->submitRemoval($userIds, kick: false);
        } catch (Throwable $e) {
            $this->log("Could not prune the members that left $this: $e", Logger::WARNING);
        }
    }

    /**
     * How many times a chain-mutating block is rebuilt on the latest head before giving up.
     */
    private const MAX_CHAIN_RETRIES = 5;

    /**
     * Submit a chain-mutating RPC, rebuilding its block on the latest chain head when the server
     * rejects it as stale (`CONF_WRITE_CHAIN_INVALID`) â€” a concurrent block took our height â€” and
     * treating `GROUPCALL_FORBIDDEN` as a reset. `$attempt` must build its block from the *current*
     * chain state and return the RPC's Updates, so a retry naturally rebuilds on the fresh head.
     *
     * @param callable(): array $attempt
     *
     * @return array The RPC's Updates.
     */
    private function submitWithChainRetry(callable $attempt): array
    {
        for ($i = 0; $i < self::MAX_CHAIN_RETRIES; $i++) {
            try {
                return $attempt();
            } catch (RPCErrorException $e) {
                if ($e->rpc === 'CONF_WRITE_CHAIN_INVALID') {
                    $this->log("Block rejected as stale on $this; refetching the chain and rebuilding", Logger::WARNING);
                    $this->syncChain(self::SUBCHAIN_STATE);
                    continue;
                }
                if ($e->rpc === 'GROUPCALL_FORBIDDEN') {
                    $this->handleForbidden();
                }
                throw $e;
            }
        }
        throw new \RuntimeException("The conference chain kept rejecting our block as invalid ($this)");
    }

    /**
     * We were forbidden from the call (removed from the chain, or its state was reset): stop
     * participating so we neither poll nor apply further blocks, then â€” as official clients do â€” rejoin
     * transparently with a fresh key, unless we were leaving anyway.
     */
    private function handleForbidden(): void
    {
        $wasJoined = $this->joined;
        $this->log("Forbidden from $this; resetting conference state", Logger::WARNING);
        $this->joined = false;
        if ($this->pollWatcher !== null) {
            EventLoop::cancel($this->pollWatcher);
            $this->pollWatcher = null;
        }
        if ($this->inputCall !== null) {
            $this->API->unregisterConferenceCall($this->inputCall['id']);
        }
        $this->chainOffset = [0, 0];
        $this->dropPresentation();
        if ($wasJoined && !$this->leaving) {
            // Recordings in progress carry on with the connection of the rejoin.
            $this->detachConnection();
            $this->scheduleRejoin();
        } else {
            $this->closeConnection();
        }
    }

    /**
     * Rejoin shortly, once, from the event loop (never from inside the failing request).
     */
    private function scheduleRejoin(): void
    {
        if ($this->rejoinScheduled) {
            return;
        }
        $this->rejoinScheduled = true;
        EventLoop::delay(1.0, function (): void {
            $this->rejoinScheduled = false;
            if ($this->leaving || $this->joined || $this->inputCall === null) {
                return;
            }
            try {
                $this->rejoin();
            } catch (Throwable $e) {
                $this->log("Could not rejoin $this: $e", Logger::ERROR);
            }
        });
    }

    /**
     * Join again from scratch with a fresh key pair and an empty local chain, keeping the playlists.
     */
    private function rejoin(): void
    {
        $this->log("Rejoining $this with a fresh key...", Logger::NOTICE);
        [$this->selfSeed] = Crypto::generateKeyPair();
        $this->chain = new ConferenceChain($this->selfId, $this->selfSeed);
        $this->epochs = [];
        $this->chainOffset = [0, 0];
        $this->verification = null;
        $this->delayedBroadcasts = [];
        $this->rtcParticipants = [];
        $this->callVersion = 0;
        $this->join($this->muted);
    }

    /* ------------------------------------------------------------------ *
     *  Chain polling and application.
     * ------------------------------------------------------------------ */

    /**
     * Fetch and apply every block of a subchain from our current offset until caught up.
     */
    public function syncChain(int $subChainId): void
    {
        do {
            try {
                $updates = $this->API->methodCallAsyncRead('phone.getGroupCallChainBlocks', [
                    'call' => $this->inputCall,
                    'sub_chain_id' => $subChainId,
                    'offset' => $this->chainOffset[$subChainId],
                    'limit' => 50,
                ]);
            } catch (RPCErrorException $e) {
                if ($e->rpc === 'GROUPCALL_FORBIDDEN') {
                    $this->handleForbidden();
                    return;
                }
                $this->log("Could not fetch chain $subChainId of $this: $e", Logger::WARNING);
                return;
            }
            $chunk = $this->extractBlocks($updates);
            $blocks = $chunk['blocks'] ?? [];
            if ($chunk !== null) {
                $this->applyChainBlocks($subChainId, $blocks, $chunk['next'], fromPoll: true);
            }
            $this->consumeUpdates($updates);
        } while (\count($blocks) === 50);
    }

    /**
     * @internal Handle an [updateGroupCallChainBlocks](https://core.telegram.org/constructor/updateGroupCallChainBlocks).
     *
     * @param list<string> $blocks
     */
    public function onChainBlocks(int $subChainId, array $blocks, int $nextOffset): void
    {
        $this->applyChainBlocks($subChainId, $blocks, $nextOffset, fromPoll: false);
    }

    /**
     * Apply a run of consecutive blocks of a subchain ending right before `$nextOffset`, in order and
     * exactly once: blocks before our offset were already applied (the same block may reach us from
     * both the push update and the polling backstop), and a run starting past our offset means we
     * missed some, which are fetched first (and then include these).
     *
     * @param list<string> $blocks
     */
    private function applyChainBlocks(int $subChainId, array $blocks, int $nextOffset, bool $fromPoll): void
    {
        $first = $nextOffset - \count($blocks);
        if ($first > $this->chainOffset[$subChainId]) {
            if (!$fromPoll) {
                $this->syncChain($subChainId);
                return;
            }
            // We asked from our offset and the server started later: whatever lies in between no
            // longer exists server-side, so there is nothing else to fetch; carry on from here.
            $this->log("The server skipped blocks {$this->chainOffset[$subChainId]}..$first of chain $subChainId of $this", Logger::WARNING);
            $this->chainOffset[$subChainId] = $first;
        }
        foreach ($blocks as $i => $block) {
            $index = $first + $i;
            if ($index < $this->chainOffset[$subChainId]) {
                continue;
            }
            $this->applyBlock($subChainId, $index, $block);
        }
    }

    private function applyBlock(int $subChainId, int $index, string $serialized): void
    {
        try {
            if ($subChainId === self::SUBCHAIN_STATE) {
                // Only a newly applied block advances the epoch, the offset and the verification.
                if ($this->chain->applyServerBlock($serialized)) {
                    $this->chainOffset[self::SUBCHAIN_STATE] = $this->chain->getHeight() + 1;
                    $this->refreshEpoch();
                    $this->restartVerification();
                }
            } else {
                $this->chainOffset[self::SUBCHAIN_VERIFICATION] = $index + 1;
                $this->applyBroadcast($serialized);
            }
        } catch (Throwable $e) {
            $this->log("Could not apply block $index of chain $subChainId of $this: $e", Logger::WARNING);
        }
    }

    /**
     * Snapshot the current group key as a media epoch, keeping the most recent ones.
     *
     * @psalm-external-mutation-free
     */
    private function refreshEpoch(): void
    {
        $key = $this->chain->getGroupKey();
        if ($key === null) {
            return;
        }
        $hash = $this->chain->getLastBlockHash();
        $this->epochs[$hash] = ['hash' => $hash, 'secret' => $key];
        while (\count($this->epochs) > self::MAX_EPOCHS) {
            array_shift($this->epochs);
        }
    }

    private function startPolling(): void
    {
        // The primary delivery path is the push updates (updateGroupCallChainBlocks /
        // updateGroupCallEncryptedMessage) routed here by the update dispatcher; this timer is only a
        // backstop that catches anything missed while the update seq was gapped or the process slept.
        // As the protocol asks, it ticks every second while a key verification is in progress and
        // every 5 seconds otherwise.
        $this->pollWatcher = EventLoop::repeat(1.0, function (): void {
            if (!$this->joined) {
                return;
            }
            $verifying = $this->verification !== null && $this->verification['state'] !== 'end';
            if (!$verifying && ++$this->pollTick % 5 !== 0) {
                return;
            }
            $this->syncChain(self::SUBCHAIN_STATE);
            $this->syncChain(self::SUBCHAIN_VERIFICATION);
        });
    }

    /* ------------------------------------------------------------------ *
     *  Screen-share: a second, end-to-end encrypted connection.
     * ------------------------------------------------------------------ */

    /* ------------------------------------------------------------------ *
     *  Media playback â€” the common {@see Call} interface. Every frame is end-to-end
     *  encrypted (camera and screen-share alike) before it reaches the SFU.
     * ------------------------------------------------------------------ */

    public function then(LocalFile|RemoteUrl|ReadableStream $file, MediaDestination $dest = MediaDestination::Camera): self
    {
        $this->dj($dest)->play($file);
        return $this;
    }

    /**
     * End the conference for everyone (phone.discardGroupCall, allowed to its creator only) and leave
     * it; if the server refuses, just leave.
     */
    public function discard(): self
    {
        if ($this->inputCall !== null && $this->joined) {
            try {
                $this->API->methodCallAsyncRead('phone.discardGroupCall', ['call' => $this->inputCall]);
            } catch (Throwable $e) {
                $this->log("Could not discard $this, leaving it instead: $e", Logger::WARNING);
            }
        }
        return $this->leave();
    }

    /**
     * Leave the conference, keeping it running for the other participants: stop the backstop poll and
     * stop receiving its updates.
     */
    public function leave(): self
    {
        $this->leaving = true;
        $this->joined = false;
        if ($this->pollWatcher !== null) {
            EventLoop::cancel($this->pollWatcher);
            $this->pollWatcher = null;
        }
        $this->dropPresentation();
        if ($this->inputCall === null) {
            $this->closeConnection();
            return $this;
        }
        $this->API->unregisterConferenceCall($this->inputCall['id']);
        $source = $this->connection?->getAudioSource() ?? 0;
        $this->closeConnection();
        try {
            $this->API->methodCallAsyncRead('phone.leaveGroupCall', ['call' => $this->inputCall, 'source' => $source]);
        } catch (Throwable $e) {
            $this->log("Could not leave $this: $e", Logger::WARNING);
        }
        return $this;
    }

    /**
     * The participants currently in the conference, keyed by user id, each with their Ed25519
     * public key, permission bits and protocol version from the shared-state chain.
     *
     * @return array<int, ConferenceCallParticipant>
     *
     * @psalm-mutation-free
     */
    public function getParticipants(): array
    {
        $participants = [];
        foreach ($this->chain->getParticipants() as $userId => $info) {
            $participants[$userId] = new ConferenceCallParticipant($userId, $info['public_key'], $info['permissions'], $info['version']);
        }
        return $participants;
    }

    /* ------------------------------------------------------------------ *
     *  Emoji verification (subchain 1), mirroring tde2e's CallVerificationChain.
     * ------------------------------------------------------------------ */

    /**
     * (Re)start the commit-reveal verification for the current chain head, as every participant does
     * whenever a main-chain block is applied: pick a fresh nonce, broadcast its commit (once joined),
     * then reveal it once every member has committed; the emojis follow once everyone revealed.
     * A no-op if the current head is already being verified.
     */
    private function restartVerification(): void
    {
        $height = $this->chain->getHeight();
        $hash = $this->chain->getLastBlockHash();
        if ($height < 0) {
            return;
        }
        if ($this->verification !== null && $this->verification['height'] === $height && $this->verification['hash'] === $hash) {
            return;
        }
        $participants = [];
        foreach ($this->chain->getParticipants() as $userId => $info) {
            $participants[$userId] = $info['public_key'];
        }
        $made = Verification::makeNonce($this->selfId, $height, $hash, $this->selfSeed);
        $this->verification = [
            'height' => $height,
            'hash' => $hash,
            'state' => 'commit',
            'sent' => false,
            'commit' => $made['commit'],
            'reveal' => $made['reveal'],
            'participants' => $participants,
            'committed' => [],
            'revealed' => [],
            'emojis' => null,
        ];
        $this->ensureVerificationBroadcast();
        // Broadcasts for this head that arrived before its block did; older ones are moot.
        $delayed = $this->delayedBroadcasts[$height] ?? [];
        foreach ($this->delayedBroadcasts as $h => $_) {
            if ($h <= $height) {
                unset($this->delayedBroadcasts[$h]);
            }
        }
        foreach ($delayed as $serialized) {
            $this->applyBroadcast($serialized);
        }
    }

    /**
     * Broadcast our nonce commit for the head under verification, if we are in the call and have not
     * done so yet.
     */
    private function ensureVerificationBroadcast(): void
    {
        if ($this->verification === null || $this->verification['sent'] || !$this->joined) {
            return;
        }
        $this->verification['sent'] = true;
        $this->broadcast($this->verification['commit']);
    }

    /**
     * Submit a verification broadcast (phone.sendConferenceCallBroadcast); it reaches everyone, us
     * included, through subchain 1.
     *
     * @param array<string, mixed> $broadcast
     */
    private function broadcast(array $broadcast): void
    {
        try {
            $this->API->methodCallAsyncRead('phone.sendConferenceCallBroadcast', [
                'call' => $this->getInputCall(),
                'block' => (new BlockCodec())->serialize($broadcast),
            ]);
        } catch (RPCErrorException $e) {
            if ($e->rpc === 'GROUPCALL_FORBIDDEN') {
                $this->handleForbidden();
                return;
            }
            $this->log("Could not send a verification broadcast for $this: $e", Logger::WARNING);
        } catch (Throwable $e) {
            $this->log("Could not send a verification broadcast for $this: $e", Logger::WARNING);
        }
    }

    /**
     * Apply a subchain 1 broadcast: a commit is accepted while collecting commits, a reveal while
     * collecting reveals (and must match its commit); both must be for the head under verification
     * (later ones wait for their block, earlier ones are dropped), from a member, and signed by them.
     */
    private function applyBroadcast(string $serialized): void
    {
        $broadcast = (new BlockCodec())->deserialize($serialized);
        $type = (string) ($broadcast['_'] ?? '');
        if ($type !== 'e2e.chain.groupBroadcastNonceCommit' && $type !== 'e2e.chain.groupBroadcastNonceReveal') {
            return;
        }
        $height = (int) $broadcast['chain_height'];
        if ($this->verification === null || $height > $this->verification['height']) {
            $this->delayedBroadcasts[$height][] = $serialized;
            return;
        }
        if ($height < $this->verification['height']) {
            return;
        }
        $userId = (int) $broadcast['user_id'];
        if ((string) $broadcast['chain_hash'] !== $this->verification['hash']) {
            $this->log("Ignoring a verification broadcast of $userId for another chain head of $this", Logger::WARNING);
            return;
        }
        $publicKey = $this->verification['participants'][$userId] ?? null;
        if ($publicKey === null) {
            $this->log("Ignoring a verification broadcast of $userId, who is not in $this", Logger::WARNING);
            return;
        }
        if (!Verification::verify($broadcast, $publicKey)) {
            $this->log("Ignoring a verification broadcast of $userId with a bad signature in $this", Logger::WARNING);
            return;
        }
        $members = \count($this->verification['participants']);
        if ($type === 'e2e.chain.groupBroadcastNonceCommit') {
            if ($this->verification['state'] !== 'commit' || isset($this->verification['committed'][$userId])) {
                return;
            }
            $this->verification['committed'][$userId] = (string) $broadcast['nonce_hash'];
            if (\count($this->verification['committed']) === $members) {
                $this->verification['state'] = 'reveal';
                if ($this->joined) {
                    $this->broadcast($this->verification['reveal']);
                }
            }
            return;
        }
        if ($this->verification['state'] !== 'reveal' || isset($this->verification['revealed'][$userId])) {
            return;
        }
        $nonce = (string) $broadcast['nonce'];
        if (!Verification::checkReveal($this->verification['committed'][$userId] ?? '', $nonce)) {
            $this->log("Ignoring a nonce of $userId that does not match their commit in $this", Logger::WARNING);
            return;
        }
        $this->verification['revealed'][$userId] = $nonce;
        if (\count($this->verification['revealed']) === $members) {
            $this->verification['state'] = 'end';
            $hash = Verification::emojiHash(array_values($this->verification['revealed']), $this->verification['hash']);
            $this->verification['emojis'] = Verification::emojis($hash);
            $this->log("Verified $this: ".implode(' ', $this->verification['emojis']), Logger::NOTICE);
        }
    }

    /**
     * The four verification emojis of the current chain head, or null until every member has committed
     * and revealed their nonce for it.
     *
     * @return list<string>|null
     *
     * @psalm-mutation-free
     */
    public function getVisualization(): ?array
    {
        return $this->verification['emojis'] ?? null;
    }

    /* ------------------------------------------------------------------ *
     *  Encrypted in-call messages (channel 0).
     * ------------------------------------------------------------------ */

    /**
     * Send an end-to-end encrypted in-call message to every participant: a `groupCallMessage` JSON
     * document, as [the protocol](https://core.telegram.org/api/end-to-end/group-calls#in-call-messages)
     * defines, encrypted with {@see CallPacket} on channel 0 for the current epochs.
     */
    public function sendMessage(string $message, ?ParseMode $parseMode = null, ?int $paidStars = null, mixed $sendAs = null): self
    {
        $entities = [];
        if ($parseMode === ParseMode::MARKDOWN) {
            $parsed = TextEntities::fromMarkdown($message);
            [$message, $entities] = [$parsed->message, $parsed->entities];
        } elseif ($parseMode === ParseMode::HTML) {
            $parsed = TextEntities::fromHtml($message);
            [$message, $entities] = [$parsed->message, $parsed->entities];
        }
        $raw = [];
        foreach ($entities as $entity) {
            $raw[] = $entity->toMTProto();
        }
        $this->sendTextWithEntities($message, self::filterConferenceEntities($raw));
        return $this;
    }

    /**
     * Send an end-to-end encrypted in-call reaction: a single emoji, or a custom emoji with `$emoji`
     * as its fallback.
     */
    public function sendReaction(string $emoji, ?int $customEmojiId = null): self
    {
        $entities = [];
        if ($customEmojiId !== null) {
            $length = (int) (\strlen(mb_convert_encoding($emoji, 'UTF-16', 'UTF-8')) / 2);
            $entities[] = ['_' => 'messageEntityCustomEmoji', 'offset' => 0, 'length' => $length, 'document_id' => $customEmojiId];
        }
        $this->sendTextWithEntities($emoji, $entities);
        return $this;
    }

    /**
     * The entity types conference messages may carry, per the protocol.
     */
    private const CONFERENCE_ENTITIES = ['messageEntityBold', 'messageEntityItalic', 'messageEntityUnderline', 'messageEntityStrike', 'messageEntitySpoiler', 'messageEntityCustomEmoji'];

    /**
     * Keep only the entities the conference message format supports, in their JSON form.
     *
     * @param list<array<array-key, mixed>> $entities
     *
     * @return list<array<string, mixed>>
     *
     * @psalm-pure
     */
    private static function filterConferenceEntities(array $entities): array
    {
        $result = [];
        foreach ($entities as $entity) {
            $type = (string) ($entity['_'] ?? '');
            if (!\in_array($type, self::CONFERENCE_ENTITIES, true)) {
                continue;
            }
            $json = ['_' => $type, 'offset' => (int) $entity['offset'], 'length' => (int) $entity['length']];
            if ($type === 'messageEntityCustomEmoji') {
                $json['document_id'] = (string) $entity['document_id'];
            }
            $result[] = $json;
        }
        return $result;
    }

    /**
     * Encrypt and send a `groupCallMessage` JSON document on channel 0, as
     * [the protocol](https://core.telegram.org/api/end-to-end/group-calls#conference-in-call-messages) defines.
     *
     * @param list<array<string, mixed>> $entities
     */
    private function sendTextWithEntities(string $text, array $entities): void
    {
        $epochs = $this->activeEpochs();
        if ($epochs === []) {
            throw new \RuntimeException('No conference key yet');
        }
        $json = json_encode([
            '_' => 'groupCallMessage',
            'random_id' => (string) random_int(1, PHP_INT_MAX),
            'message' => ['_' => 'textWithEntities', 'text' => $text, 'entities' => $entities],
        ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR);
        $packet = CallPacket::encrypt(0, $this->nextMessageSeqno(), $json, $epochs, $this->selfSeed());
        $this->API->methodCallAsyncRead('phone.sendGroupCallEncryptedMessage', ['call' => $this->getInputCall(), 'encrypted_message' => $packet]);
    }

    private int $messageSeqno = 0;

    /**
     * @psalm-external-mutation-free
     */
    private function nextMessageSeqno(): int
    {
        return ++$this->messageSeqno;
    }

    /**
     * @internal Decrypt an [updateGroupCallEncryptedMessage](https://core.telegram.org/constructor/updateGroupCallEncryptedMessage),
     * returning the text and (validated, TL-shaped) entities of the `groupCallMessage` it carries, or
     * null if it could not be decrypted, is not a message, or is a redelivery (deduplicated by sender
     * and `random_id`).
     *
     * @return array{text: string, entities: list<array<string, mixed>>, random_id: string}|null
     */
    public function onEncryptedMessage(int $fromUserId, string $encrypted): ?array
    {
        $senderKey = $this->chain->getParticipants()[$fromUserId]['public_key'] ?? null;
        if ($senderKey === null) {
            return null;
        }
        $secrets = [];
        foreach ($this->activeEpochs() as $epoch) {
            $secrets[$epoch['hash']] = $epoch['secret'];
        }
        try {
            $payload = CallPacket::decrypt($encrypted, $secrets, $senderKey)['payload'];
            $message = json_decode($payload, true, 16, JSON_THROW_ON_ERROR);
        } catch (Throwable $e) {
            $this->log("Could not decrypt an in-call message in $this: $e", Logger::WARNING);
            return null;
        }
        if (!\is_array($message) || ($message['_'] ?? null) !== 'groupCallMessage' || !\is_array($message['message'] ?? null)) {
            return null;
        }
        $randomId = (string) ($message['random_id'] ?? '');
        $text = $message['message']['text'] ?? null;
        if ($randomId === '' || !\is_string($text)) {
            return null;
        }
        $key = "$fromUserId:$randomId";
        if (isset($this->seenMessages[$key])) {
            return null;
        }
        $this->seenMessages[$key] = true;
        if (\count($this->seenMessages) > 1024) {
            array_shift($this->seenMessages);
        }
        $length = (int) (\strlen(mb_convert_encoding($text, 'UTF-16', 'UTF-8')) / 2);
        $entities = [];
        foreach ($message['message']['entities'] ?? [] as $entity) {
            if (!\is_array($entity)) {
                continue;
            }
            $type = (string) ($entity['_'] ?? '');
            $offset = (int) ($entity['offset'] ?? -1);
            $span = (int) ($entity['length'] ?? 0);
            if (!\in_array($type, self::CONFERENCE_ENTITIES, true) || $offset < 0 || $span <= 0 || $offset + $span > $length) {
                continue;
            }
            $parsed = ['_' => $type, 'offset' => $offset, 'length' => $span];
            if ($type === 'messageEntityCustomEmoji') {
                $documentId = (int) ($entity['document_id'] ?? 0);
                if ($documentId === 0) {
                    continue;
                }
                $parsed['document_id'] = $documentId;
            }
            $entities[] = $parsed;
        }
        return ['text' => $text, 'entities' => $entities, 'random_id' => $randomId];
    }

    /* ------------------------------------------------------------------ *
     *  Participant / media bookkeeping.
     * ------------------------------------------------------------------ */

    /**
     * Record one participant's (decrypted) media into a single file (or stream) with a fixed set of
     * tracks, see {@see GroupMediaTrait::recordParticipant()}.
     *
     * @param ?StreamMask $streams The {@see CallStream} flags to record, or null for every available one.
     *
     * @return StreamMask The streams the participant currently sends, as {@see CallStream} flags.
     */
    public function setOutput(LocalFile|WritableStream $file, string|int|null $participant = null, ?RecordingFormat $format = null, ?int $streams = null): int
    {
        return $this->recordParticipant($file, $participant, $format, $streams);
    }

    /**
     * Record the conference (decrypted) into a directory, as numbered series of Matroska files, one
     * per participant, see {@see GroupMediaTrait::recordFolder()}.
     */
    public function setOutputFolder(LocalDirectory $dir, string|int|null $participant = null, ?RecordingFormat $format = null): self
    {
        $this->recordFolder($dir, $participant, $format);
        return $this;
    }

    /**
     * @psalm-external-mutation-free
     */
    #[\Override]
    private function callObject(): Call
    {
        return $this->getPublic();
    }

    /**
     * Every connection encrypts its frames end-to-end with the conference keys; the screen share on
     * its own packet channel (screen video = 3), so its sequence numbers never collide with the
     * camera's (video = 2).
     */
    #[\Override]
    private function configureConnection(GroupConnection $connection, bool $screencast): void
    {
        if (!$screencast) {
            $connection->setFrameCryptor($this->frameCryptor);
            return;
        }
        $this->presentationCryptor ??= new FrameCryptor($this, $this->cryptorState);
        $connection->setFrameCryptor($this->presentationCryptor);
    }

    #[\Override]
    private function onDroppedByServer(): void
    {
        // Exactly what being forbidden from the call means: reset and rejoin with a fresh key.
        $this->handleForbidden();
    }

    /**
     * @psalm-mutation-free
     */
    #[\Override]
    private function participantOf(int $peerId): ?AbstractGroupCallParticipant
    {
        return $this->rtcParticipants[$peerId] ?? null;
    }

    /**
     * @psalm-mutation-free
     */
    #[\Override]
    private function isOurself(int $peerId, AbstractGroupCallParticipant $participant): bool
    {
        return $peerId === $this->selfId;
    }

    /**
     * @psalm-mutation-free
     */
    #[\Override]
    private function peerOfSource(int $source): ?int
    {
        return $this->ssrcToUser[GroupSdp::toUnsignedSsrc($source)] ?? null;
    }

    /**
     * @return list<int>
     *
     * @psalm-mutation-free
     */
    #[\Override]
    private function knownPeers(): array
    {
        return array_keys($this->rtcParticipants);
    }

    /**
     * @internal Apply the participants of an
     * [updateGroupCallParticipants](https://core.telegram.org/constructor/updateGroupCallParticipants)
     * of the conference's group call, in `version` order.
     */
    public function onParticipantsUpdate(array $participants, int $version): void
    {
        $versioned = false;
        /** @var array $participant */
        foreach ($participants as $participant) {
            if (($participant['versioned'] ?? false) || ($participant['left'] ?? false) || ($participant['just_joined'] ?? false)) {
                $versioned = true;
                break;
            }
        }
        if ($versioned && $this->callVersion !== 0) {
            if ($version < $this->callVersion + 1) {
                return;
            }
            if ($version > $this->callVersion + 1) {
                // A gap: rather than wait for it to fill, resync the whole list.
                $this->refetchParticipants();
                return;
            }
        }
        if ($versioned) {
            $this->callVersion = $version;
        }
        /** @var array $participant */
        foreach ($participants as $participant) {
            $this->applyRtcParticipant($participant);
        }
        $this->syncSources();
    }

    /**
     * @internal Apply an [updateGroupCall](https://core.telegram.org/constructor/updateGroupCall)
     * carrying the conference's groupCall.
     */
    public function onGroupCallUpdate(array $call): void
    {
        $this->applyRawCall($call);
    }

    private function applyRawCall(array $call): void
    {
        if (($call['_'] ?? '') === 'groupCallDiscarded') {
            $this->rawCall = $call;
            $this->public?->update($call);
            if ($this->joined) {
                $this->log("$this was discarded", Logger::NOTICE);
                $this->leave();
            }
            return;
        }
        $this->rawCall = $call;
        $this->callVersion = max($this->callVersion, (int) ($call['version'] ?? 0));
        $this->public?->update($call);
    }

    /**
     * Fetch the conference's groupCall and its full participant list (phone.getGroupCall), replacing
     * what we know, and prune from the chain the members that are not in the call any more.
     */
    private function refetchParticipants(): void
    {
        if ($this->inputCall === null) {
            return;
        }
        // Whoever was in the chain before we asked but is missing from the answer has left.
        $chainMembers = array_keys($this->chain->getParticipants());
        try {
            $result = $this->API->methodCallAsyncRead('phone.getGroupCall', ['call' => $this->inputCall, 'limit' => 100]);
        } catch (RPCErrorException $e) {
            if ($e->rpc === 'GROUPCALL_FORBIDDEN') {
                $this->handleForbidden();
                return;
            }
            $this->log("Could not fetch the participants of $this: $e", Logger::WARNING);
            return;
        } catch (Throwable $e) {
            $this->log("Could not fetch the participants of $this: $e", Logger::WARNING);
            return;
        }
        \assert(\is_array($result) && \is_array($result['call']) && \is_array($result['participants']));
        $this->applyRawCall($result['call']);
        if (!$this->joined) {
            return;
        }
        $this->rtcParticipants = [];
        /** @var array $participant */
        foreach ($result['participants'] as $participant) {
            $this->applyRtcParticipant($participant);
        }
        $this->syncSources();
        $complete = (int) ($result['call']['participants_count'] ?? PHP_INT_MAX) <= \count($result['participants']);
        if ($complete) {
            $this->pruneStale(array_values(array_filter($chainMembers, fn (int $id): bool => !isset($this->rtcParticipants[$id]))));
        }
    }

    private function applyRtcParticipant(array $participant): void
    {
        $userId = $this->API->getIdInternal($participant['peer']);
        if ($userId === null) {
            return;
        }
        if ($participant['left'] ?? false) {
            unset($this->rtcParticipants[$userId]);
            if ($userId === $this->selfId) {
                // Our own source is gone from the call: we were removed, rejoin (as official clients do).
                if ($this->joined && (int) ($participant['source'] ?? 0) === ($this->connection?->getAudioSource() ?? 0)) {
                    $this->log("We were removed from $this, rejoining...", Logger::WARNING);
                    $this->handleForbidden();
                }
                return;
            }
            $this->pruneStale([$userId]);
            return;
        }
        $this->rtcParticipants[$userId] = GroupCallParticipant::fromRaw($participant, $userId, $this->rtcParticipants[$userId] ?? null);
    }

    /**
     * Rebuild the SSRC -> user id map from the participant list, so every incoming packet (audio,
     * camera or screen-share) can be attributed to its sender's public key, tell the connection which
     * sources to receive, and attach the recordings that waited for a participant's sources.
     */
    private function syncSources(): void
    {
        $sources = [];
        foreach ($this->rtcParticipants as $userId => $participant) {
            if ($participant->source === 0) {
                continue;
            }
            foreach ([$participant->source, ...$participant->videoSources, ...$participant->presentationSources] as $ssrc) {
                $this->ssrcToUser[GroupSdp::toUnsignedSsrc($ssrc)] = $userId;
            }
            $this->userToSource[$userId] = $participant->source;
            if ($userId === $this->selfId) {
                continue;
            }
            $sources[] = [
                'audio' => $participant->source,
                'muted' => $participant->muted,
                'video' => $participant->videoSources,
                'presentation' => $participant->presentationSources,
                'videoEndpoint' => $participant->videoEndpoint,
                'presentationEndpoint' => $participant->presentationEndpoint,
            ];
            // The source is now known: attach any recording deferred until it was, then apply
            // folder-mode recording to a participant that has just begun transmitting.
            if (isset($this->explicitOutputs[$userId])) {
                $this->wireOutput($userId);
            }
            $this->wireFolderOutput($userId);
        }
        $this->connection?->setRemoteSources($sources);
    }

    /**
     * Set our inputGroupCall from the groupCall an update carries (the create/join response).
     */
    private function extractCall(array $updates): void
    {
        foreach ($updates['updates'] ?? [] as $update) {
            if ($update['_'] === 'updateGroupCall' && ($update['call']['_'] ?? '') === 'groupCall') {
                $this->setCall($update['call']);
                $this->applyRawCall((array) $update['call']);
                return;
            }
        }
    }

    private function consumeUpdates(array $updates): void
    {
        foreach ($updates['updates'] ?? [] as $update) {
            if ($update['_'] === 'updateGroupCallConnection' && !($update['presentation'] ?? false)) {
                $parsed = GroupSdp::parseJoinResponse($update['params']);
                if ($parsed['transport'] !== null) {
                    $this->connection?->setTransport($parsed['transport'], $parsed['video']);
                }
            }
        }
    }

    /**
     * The chain blocks and next offset carried by an updateGroupCallChainBlocks, if any.
     *
     * @param array<string, mixed> $updates
     *
     * @return array{blocks: list<string>, next: int}|null
     */
    private function extractBlocks(array $updates): ?array
    {
        foreach ($updates['updates'] ?? [] as $update) {
            if ($update['_'] === 'updateGroupCallChainBlocks') {
                return ['blocks' => array_values(array_map('strval', $update['blocks'])), 'next' => (int) $update['next_offset']];
            }
        }
        return null;
    }

    public function getChain(): ConferenceChain
    {
        return $this->chain;
    }

    /* ------------------------------------------------------------------ *
     *  GroupConnectionOwner.
     * ------------------------------------------------------------------ */

    #[\Override]
    public function log(string $message, int $level = Logger::NOTICE): void
    {
        $this->API->logger->logger($message, $level);
    }

    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function onIncomingSource(int $source): void
    {
        // Media from a participant started; nothing extra to do â€” the frame cryptor decrypts it.
    }

    #[\Override]
    public function onConnectionFailed(): void
    {
        $this->log("The WebRTC connection of $this failed", Logger::WARNING);
    }

    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function __toString(): string
    {
        return 'E2E conference '.($this->inputCall['id'] ?? '(new)');
    }
}
<?php

declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use Amp\ByteStream\ReadableStream;
use Amp\ByteStream\WritableStream;
use Amp\Cancellation;
use Amp\DeferredCancellation;
use Amp\Process\Process;
use AssertionError;
use Closure;
use FFI;
use FFI\CData;
use Throwable;
use Webmozart\Assert\Assert;

use function Amp\async;
use function Amp\ByteStream\getStderr;
use function Amp\ByteStream\pipe;
use function Amp\File\openFile;
use function count;

/**
 * Async OGG stream reader and writer.
 *
 * @author Charles-Ã‰douard Coste <contact@ccoste.fr>
 * @author Daniil Gentili <daniil@daniil.it>
 */
final class Ogg
{
    private const OPUS_SET_APPLICATION_REQUEST = 4000;
    private const OPUS_GET_APPLICATION_REQUEST = 4001;
    private const OPUS_SET_BITRATE_REQUEST = 4002;
    private const OPUS_GET_BITRATE_REQUEST = 4003;
    private const OPUS_SET_MAX_BANDWIDTH_REQUEST = 4004;
    private const OPUS_GET_MAX_BANDWIDTH_REQUEST = 4005;
    private const OPUS_SET_VBR_REQUEST = 4006;
    private const OPUS_GET_VBR_REQUEST = 4007;
    private const OPUS_SET_BANDWIDTH_REQUEST = 4008;
    private const OPUS_GET_BANDWIDTH_REQUEST = 4009;
    private const OPUS_SET_COMPLEXITY_REQUEST = 4010;
    private const OPUS_GET_COMPLEXITY_REQUEST = 4011;
    private const OPUS_SET_INBAND_FEC_REQUEST = 4012;
    private const OPUS_GET_INBAND_FEC_REQUEST = 4013;
    private const OPUS_SET_PACKET_LOSS_PERC_REQUEST = 4014;
    private const OPUS_GET_PACKET_LOSS_PERC_REQUEST = 4015;
    private const OPUS_SET_DTX_REQUEST = 4016;
    private const OPUS_GET_DTX_REQUEST = 4017;
    private const OPUS_SET_VBR_CONSTRAINT_REQUEST = 4020;
    private const OPUS_GET_VBR_CONSTRAINT_REQUEST = 4021;
    private const OPUS_SET_FORCE_CHANNELS_REQUEST = 4022;
    private const OPUS_GET_FORCE_CHANNELS_REQUEST = 4023;
    private const OPUS_SET_SIGNAL_REQUEST = 4024;
    private const OPUS_GET_SIGNAL_REQUEST = 4025;
    private const OPUS_GET_LOOKAHEAD_REQUEST = 4027;
    private const OPUS_GET_SAMPLE_RATE_REQUEST = 4029;
    private const OPUS_GET_FINAL_RANGE_REQUEST = 4031;
    private const OPUS_GET_PITCH_REQUEST = 4033;
    private const OPUS_SET_GAIN_REQUEST = 4034;
    private const OPUS_GET_GAIN_REQUEST = 4045;
    private const OPUS_SET_LSB_DEPTH_REQUEST = 4036;
    private const OPUS_GET_LSB_DEPTH_REQUEST = 4037;
    private const OPUS_GET_LAST_PACKET_DURATION_REQUEST = 4039;
    private const OPUS_SET_EXPERT_FRAME_DURATION_REQUEST = 4040;
    private const OPUS_GET_EXPERT_FRAME_DURATION_REQUEST = 4041;
    private const OPUS_SET_PREDICTION_DISABLED_REQUEST = 4042;
    private const OPUS_GET_PREDICTION_DISABLED_REQUEST = 4043;
    private const OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST = 4046;
    private const OPUS_GET_PHASE_INVERSION_DISABLED_REQUEST = 4047;
    private const OPUS_GET_IN_DTX_REQUEST = 4049;

    /* Values for the various encoder CTLs */
    private const OPUS_AUTO = -1000 /**<Auto/default setting @hideinitializer*/;
    private const OPUS_BITRATE_MAX = -1 /**<Maximum bitrate @hideinitializer*/;

    /** Best for most VoIP/videoconference applications where listening quality and intelligibility matter most.
     * @hideinitializer */
    private const OPUS_APPLICATION_VOIP = 2048;
    /** Best for broadcast/high-fidelity application where the decoded audio should be as close as possible to the input.
     * @hideinitializer */
    private const OPUS_APPLICATION_AUDIO = 2049;
    /** Only use when lowest-achievable latency is what matters most. Voice-optimized modes cannot be used.
     * @hideinitializer */
    private const OPUS_APPLICATION_RESTRICTED_LOWDELAY = 2051;

    private const OPUS_SIGNAL_VOICE = 3001 /**< Signal being encoded is voice */;
    private const OPUS_SIGNAL_MUSIC = 3002 /**< Signal being encoded is music */;
    private const OPUS_BANDWIDTH_NARROWBAND = 1101 /**< 4 kHz bandpass @hideinitializer*/;
    private const OPUS_BANDWIDTH_MEDIUMBAND = 1102 /**< 6 kHz bandpass @hideinitializer*/;
    private const OPUS_BANDWIDTH_WIDEBAND = 1103 /**< 8 kHz bandpass @hideinitializer*/;
    private const OPUS_BANDWIDTH_SUPERWIDEBAND = 1104 /**<12 kHz bandpass @hideinitializer*/;
    private const OPUS_BANDWIDTH_FULLBAND = 1105 /**<20 kHz bandpass @hideinitializer*/;

    private const OPUS_FRAMESIZE_ARG = 5000 /**< Select frame size from the argument (default) */;
    private const OPUS_FRAMESIZE_2_5_MS = 5001 /**< Use 2.5 ms frames */;
    private const OPUS_FRAMESIZE_5_MS = 5002 /**< Use 5 ms frames */;
    private const OPUS_FRAMESIZE_10_MS = 5003 /**< Use 10 ms frames */;
    private const OPUS_FRAMESIZE_20_MS = 5004 /**< Use 20 ms frames */;
    private const OPUS_FRAMESIZE_40_MS = 5005 /**< Use 40 ms frames */;
    private const OPUS_FRAMESIZE_60_MS = 5006 /**< Use 60 ms frames */;
    private const OPUS_FRAMESIZE_80_MS = 5007 /**< Use 80 ms frames */;
    private const OPUS_FRAMESIZE_100_MS = 5008 /**< Use 100 ms frames */;
    private const OPUS_FRAMESIZE_120_MS = 5009 /**< Use 120 ms frames */;

    private const CAPTURE_PATTERN = "OggS";
    public const CONTINUATION = 1;
    public const BOS = 2;
    public const EOS = 4;

    public const STATE_READ_HEADER = 0;
    public const STATE_READ_COMMENT = 1;
    public const STATE_STREAMING = 3;
    public const STATE_END = 4;

    /**
     * OGG Stream count.
     */
    private int $streamCount;

    /**
     * Total number of bytes read from the underlying source, i.e. the byte offset of the read
     * cursor. Starts at the resume offset so that {@see self::$pageOffset} is an absolute file offset.
     */
    private int $bytesRead = 0;

    /**
     * Byte offset of the OGG page currently being read.
     *
     * This is a page boundary, so re-opening the file here with the `$startOffset` constructor
     * argument resumes reading exactly this page, which is what makes playback byte-offset
     * resumable across a serialize/unserialize cycle.
     */
    public int $pageOffset = 0;

    /** Whether reading started partway through the file, so the OPUS headers are not present. */
    private bool $resuming = false;

    /**
     * Pack format.
     */
    private string $packFormat;

    /**
     * Opus packet iterator.
     *
     * @var iterable<string>
     */
    public readonly iterable $opusPackets;
    public readonly string $vendorString;
    /** @var list<string> */
    public readonly array $comments;

    /**
     * @var (Closure(int, ?Cancellation): ?string) $stream The stream
     */
    private readonly Closure $stream;
    /**
     * Constructor.
     */
    public function __construct(LocalFile|RemoteUrl|ReadableStream $stream, ?Cancellation $cancellation = null, int $startOffset = 0)
    {
        // Count every byte read so that the offset of each page is known and playback can resume
        // from a byte offset. A non-zero start offset means we opened partway through the file,
        // past the OPUS headers, so streaming starts immediately (see read()).
        $inner = Tools::openBuffered($stream, $cancellation, $startOffset);
        $this->bytesRead = $startOffset;
        $this->resuming = $startOffset > 0;
        if ($this->resuming) {
            // The headers were already parsed on the first open; a MadelineProto/converted OGG
            // OPUS file is a single logical stream, which is all the state opusStateMachine needs.
            $this->streamCount = 1;
        }
        $this->stream = function (int $len) use ($inner): ?string {
            $data = $inner($len);
            if ($data !== null) {
                $this->bytesRead += \strlen($data);
            }
            return $data;
        };
        $pack_format = [
            'stream_structure_version' => 'C',
            'header_type_flag'         => 'C',
            'granule_position'         => 'P',
            'bitstream_serial_number'  => 'V',
            'page_sequence_number'     => 'V',
            'CRC_checksum'             => 'V',
            'number_page_segments'     => 'C',
        ];

        $this->packFormat = implode(
            '/',
            array_map(
                static fn (string $v, string $k): string => $v.$k,
                $pack_format,
                array_keys($pack_format),
            ),
        );
        $it = $this->read();
        $it->current();
        if (!$this->resuming && !isset($this->comments)) {
            throw new Exception('Not an OGG OPUS file!');
        }
        $this->opusPackets = $it;
    }

    /**
     * Get the duration of an OPUS packet, in 48kHz samples (0 if the packet is malformed).
     *
     * The duration is read from the TOC byte (and, for packets with an arbitrary number of frames,
     * from the frame count byte) of the packet, see [RFC 6716 Â§3.1](https://www.rfc-editor.org/rfc/rfc6716#section-3.1).
     *
     * @psalm-pure
     */
    public static function packetDuration(string $packet): int
    {
        if ($packet === '') {
            return 0;
        }
        $toc = \ord($packet[0]);
        $config = $toc >> 3;
        // SILK-only: 10, 20, 40, 60ms; hybrid: 10, 20ms; CELT-only: 2.5, 5, 10, 20ms.
        $frameSamples = match (true) {
            $config < 12 => [480, 960, 1920, 2880][$config % 4],
            $config < 16 => [480, 960][$config % 2],
            default => [120, 240, 480, 960][$config % 4],
        };
        $frames = match ($toc & 3) {
            0 => 1,
            1, 2 => 2,
            default => isset($packet[1]) ? \ord($packet[1]) & 0x3F : 0,
        };
        $duration = $frames * $frameSamples;
        // A packet can't be longer than 120ms.
        return $duration <= 5760 ? $duration : 0;
    }

    /**
     * Get the OPUS packets of a complete OGG packet.
     *
     * Every OGG packet holds exactly one OPUS packet, which is passed through as-is.
     */
    private function opusStateMachine(string $content): \Generator
    {
        if ($this->streamCount !== 1) {
            throw new AssertionError("Multistream (surround) OGG OPUS files are not supported, please convert them to mono or stereo!");
        }
        if (self::packetDuration($content) !== 0) {
            yield $content;
        }
    }

    /**
     * Validate that the specified file, URL or stream is a valid VoIP OGG OPUS file.
     */
    public function validate(LocalFile|RemoteUrl|ReadableStream $file, ?Cancellation $cancellation = null): void
    {
        foreach ((new self($file, $cancellation))->opusPackets as $_) {
        }
    }
    /**
     * Read frames.
     *
     * @return \Generator<string>
     */
    private function read(): \Generator
    {
        // When resuming from a byte offset we jump straight to the streaming state: the offset is a
        // page boundary past the OpusHead/comment headers, which were parsed on the first open.
        $state = $this->resuming ? self::STATE_STREAMING : self::STATE_READ_HEADER;
        $content = '';
        $granule = 0;
        $ignoredStreams = [];

        while (true) {
            // Record the byte offset of this page before reading it, so a caller can resume here.
            $this->pageOffset = $this->bytesRead;
            $capture = ($this->stream)(4);
            if ($capture !== self::CAPTURE_PATTERN) {
                if ($capture === null) {
                    return;
                }
                throw new Exception('Bad capture pattern: '.bin2hex($capture));
            }

            $headers = unpack(
                $this->packFormat,
                ($this->stream)(23)
            );
            $ignore = \in_array($headers['bitstream_serial_number'], $ignoredStreams, true);

            if ($headers['stream_structure_version'] != 0x00) {
                throw new Exception("Bad stream version");
            }
            $granule_diff = $headers['granule_position'] - $granule;
            $granule = $headers['granule_position'];

            $continuation = (bool) ($headers['header_type_flag'] & 0x01);
            $firstPage = (bool) ($headers['header_type_flag'] & self::BOS);
            $lastPage = (bool) ($headers['header_type_flag'] & self::EOS);

            $segments = unpack(
                'C*',
                ($this->stream)($headers['number_page_segments']),
            );

            $sizeAccumulated = 0;
            foreach ($segments as $segment_size) {
                $sizeAccumulated += $segment_size;
                if ($segment_size < 255) {
                    $piece = ($this->stream)($sizeAccumulated);
                    $sizeAccumulated = 0;
                    if ($ignore) {
                        continue;
                    }
                    $content .= $piece;
                    if ($state === self::STATE_STREAMING) {
                        yield from $this->opusStateMachine($content);
                    } elseif ($state === self::STATE_READ_HEADER) {
                        Assert::true($firstPage);
                        $head = substr($content, 0, 8);
                        if ($head !== 'OpusHead') {
                            $ignoredStreams[]= $headers['bitstream_serial_number'];
                            $content = '';
                            $ignore = true;
                            continue;
                        }
                        $opus_head = unpack('Cversion/Cchannel_count/vpre_skip/Vsample_rate/voutput_gain/Cchannel_mapping_family/', substr($content, 8));
                        if ($opus_head['channel_mapping_family']) {
                            $opus_head['channel_mapping'] = unpack('Cstream_count/Ccoupled_count/C*channel_mapping', substr($content, 19));
                        } else {
                            $opus_head['channel_mapping'] = [
                                'stream_count' => 1,
                                'coupled_count' => $opus_head['channel_count'] - 1,
                                'channel_mapping' => [0],
                            ];
                            if ($opus_head['channel_count'] === 2) {
                                $opus_head['channel_mapping']['channel_mapping'][] = 1;
                            }
                        }
                        $this->streamCount = $opus_head['channel_mapping']['stream_count'];
                        if ($opus_head['sample_rate'] !== 48000) {
                            throw new AssertionError("The sample rate must be 48khz, got {$opus_head['sample_rate']}");
                        }
                        $state = self::STATE_READ_COMMENT;
                    } elseif ($state === self::STATE_READ_COMMENT) {
                        $vendor_string_length = unpack('V', substr($content, 8, 4))[1];
                        $this->vendorString = substr($content, 12, $vendor_string_length);
                        $comment_count = unpack('V', substr($content, 12+$vendor_string_length, 4))[1];
                        $offset = 16+$vendor_string_length;
                        $comments = [];
                        for ($x = 0; $x < $comment_count; $x++) {
                            $length = unpack('V', substr($content, $offset, 4))[1];
                            $comments []= substr($content, $offset += 4, $length);
                            $offset += $length;
                        }
                        $this->comments = $comments;
                        $state = self::STATE_STREAMING;
                    }
                    $content = '';
                }
            }
        }
    }

    /**
     * Converts a file, URL, or stream of any format (including video) into an OGG audio stream suitable for consumption by MadelineProto's VoIP implementation.
     *
     * @param LocalFile|RemoteUrl|ReadableStream $in     The input file, URL or stream.
     * @param LocalFile|WritableStream           $oggOut The output file or stream.
     */
    public static function convert(
        LocalFile|RemoteUrl|ReadableStream $in,
        LocalFile|WritableStream $oggOut,
        ?Cancellation $cancellation = null
    ): void {
        $inFile = match (true) {
            $in instanceof LocalFile => $in->file,
            $in instanceof RemoteUrl => $in->url,
            $in instanceof ReadableStream => '/dev/stdin',
        };
        $proc = Process::start(['ffmpeg', '-hide_banner', '-loglevel', 'warning', '-i', $inFile, '-map', '0:a', '-ar', '48000', '-f', 'wav', '-y', '/dev/stdout'], cancellation: $cancellation);
        if ($in instanceof ReadableStream) {
            async(pipe(...), $in, $proc->getStdin(), $cancellation)
                ->ignore()
                ->finally($proc->getStdin()->close(...));
        }
        async(pipe(...), $proc->getStderr(), getStderr(), $cancellation)->ignore();
        self::convertWav($proc->getStdout(), $oggOut, $cancellation);
    }

    /**
     * Validate that the specified OGG OPUS file can be played directly by MadelineProto, without doing any conversion.
     *
     * @throws \Throwable If validation fails.
     */
    public static function validateOgg(LocalFile|RemoteUrl|ReadableStream $f): void
    {
        $ok = false;
        $e = null;
        try {
            try {
                $cancel = new DeferredCancellation;
                $ogg = new self($f, $cancel->getCancellation());
                $ok = \in_array('MADELINE_ENCODER_V=1', $ogg->comments, true);
            } finally {
                $cancel->cancel();
            }
        } catch (\Throwable $e) {
        }
        if (!$ok) {
            throw new AssertionError("The passed file was not generated by MadelineProto or @libtgvoipbot, please pre-convert it using @libtgvoipbot or install FFI and ffmpeg to perform realtime conversion!", 0, $e);
        }
    }

    private const CDEF = '
        typedef struct OpusEncoder OpusEncoder;

        OpusEncoder *opus_encoder_create(
            int32_t Fs,
            int channels,
            int application,
            int *error
        );

        int opus_encoder_ctl(OpusEncoder *st, int request, int arg);

        int32_t opus_encode(
            OpusEncoder *st,
            const char *pcm,
            int frame_size,
            const char *data,
            int32_t max_data_bytes
        );
        void opus_encoder_destroy(OpusEncoder *st);
        const char *opus_strerror(int error);
        const char *opus_get_version_string(void);
    ';
    private static ?FFI $FFI = null;
    /**
     * Converts a file, URL, or stream in WAV format @ 48khz into an OGG audio stream suitable for consumption by MadelineProto's VoIP implementation.
     *
     * @param LocalFile|RemoteUrl|ReadableStream $wavIn  The input file, URL or stream.
     * @param LocalFile|WritableStream           $oggOut The output file or stream.
     */
    public static function convertWav(
        LocalFile|RemoteUrl|ReadableStream $wavIn,
        LocalFile|WritableStream $oggOut,
        ?Cancellation $cancellation = null
    ): void {
        if (isset(self::$FFI)) {
            $opus = self::$FFI;
        } else {
            foreach (['libopus.so', 'libopus.so.0'] as $k => $lib) {
                try {
                    $opus = FFI::cdef(self::CDEF, $lib);
                    self::$FFI = $opus;
                    break;
                } catch (Throwable $e) {
                    if ($k) {
                        throw $e;
                    }
                }
            }
        }
        \assert(isset($opus));
        $checkErr = static function (int|CData $err) use ($opus): void {
            if ($err instanceof CData) {
                $err = $err->cdata;
            }
            if ($err < 0) {
                throw new AssertionError("opus returned: ".$opus->opus_strerror($err));
            }
        };
        $err = $opus->new('int');

        $read = Tools::openBuffered($wavIn, $cancellation);

        $header = $read(4);
        if ($header === null) {
            throw new AssertionError("Could not convert the file, make sure ffmpeg and libopus are installed!");
        }
        Assert::eq($header, 'RIFF', "A .wav file must be provided!");
        $totalLength = unpack('V', $read(4))[1];
        Assert::eq($read(4), 'WAVE', "A .wav file must be provided!");
        do {
            $type = $read(4);
            $length = unpack('V', $read(4))[1];
            if ($type === 'fmt ') {
                Assert::eq($length, 16);
                $contents = $read($length + ($length % 2));
                $header = unpack('vaudioFormat/vchannels/VsampleRate/VbyteRate/vblockAlign/vbitsPerSample', $contents);
                Assert::eq($header['audioFormat'], 1, "The wav file must contain PCM audio");
                Assert::eq($header['sampleRate'], 48000, "The sample rate of the wav file must be 48khz!");
            } elseif ($type === 'data') {
                break;
            } else {
                $read($length);
            }
        } while (true);

        $sampleCount = 0.06 * $header['sampleRate'];
        $chunkSize = (int) ($sampleCount * $header['channels'] * ($header['bitsPerSample'] >> 3));
        $shift = (int) log($header['channels'] * ($header['bitsPerSample'] >> 3), 2);

        $encoder = $opus->opus_encoder_create(48000, $header['channels'], self::OPUS_APPLICATION_AUDIO, FFI::addr($err));
        $checkErr($err);
        $checkErr($opus->opus_encoder_ctl($encoder, self::OPUS_SET_COMPLEXITY_REQUEST, 10));
        $checkErr($opus->opus_encoder_ctl($encoder, self::OPUS_SET_PACKET_LOSS_PERC_REQUEST, 1));
        $checkErr($opus->opus_encoder_ctl($encoder, self::OPUS_SET_INBAND_FEC_REQUEST, 1));
        $checkErr($opus->opus_encoder_ctl($encoder, self::OPUS_SET_SIGNAL_REQUEST, self::OPUS_SIGNAL_MUSIC));
        $checkErr($opus->opus_encoder_ctl($encoder, self::OPUS_SET_BANDWIDTH_REQUEST, self::OPUS_BANDWIDTH_FULLBAND));
        $checkErr($opus->opus_encoder_ctl($encoder, self::OPUS_SET_BITRATE_REQUEST, 130*1000));

        if ($oggOut instanceof LocalFile) {
            $oggOut = openFile($oggOut->file, 'w');
        }
        $writer = new OggWriter($oggOut);
        $writer->writeHeader(
            $header['channels'],
            $header['sampleRate'],
            $opus->opus_get_version_string()
        );

        $buf = $opus->cast($opus->type('char*'), FFI::addr($opus->new('char[1024]')));
        do {
            $chunkOrig = $read($chunkSize) ?? '';
            $chunk = str_pad($chunkOrig, $chunkSize, "\0");
            $granuleDiff = \strlen($chunk) >> $shift;
            $len = $opus->opus_encode($encoder, $chunk, $granuleDiff, $buf, 1024);
            $checkErr($len);
            $writer->writeChunk(
                FFI::string($buf, $len),
                $granuleDiff,
                \strlen($chunk) !== \strlen($chunkOrig)
            );
        } while (\strlen($chunk) === \strlen($chunkOrig));
        $opus->opus_encoder_destroy($encoder);
        unset($buf, $encoder, $opus);
    }
}
{"_":"fileReferenceMap","layer":232,"db_schema":"boolFalse#bc799737 = Bool;\nboolTrue#997275b5 = Bool;\ntrue#3fedd339 = True;\nvector#1cb5c415 {t:Type} # [ t ] = Vector t;\n\nfileIdPhoto#47a0bd49 id:long = FileId;\nfileIdDocument#461b1d89 id:long = FileId;\n\nfileSourceMessage#b19f4c78 flags:# quick_reply_shortcut_id:flags.0?int peer:long id:int = FileSource;\nfileSourceStarsTransaction#c1bac8c7 flags:# peer:long id:string refund:flags.0?true ton:flags.1?true = FileSource;\nfileSourceScheduledMessage#9dc1f5f7 peer:long id:int = FileSource;\nfileSourceStory#c820e3eb id:int peer:long = FileSource;\nfileSourceWebPage#9e5b749c url:string = FileSource;\nfileSourceBotApp#01cf8b7a id:long access_hash:long = FileSource;\nfileSourceUserFull#70fdb7b0 id:long = FileSource;\nfileSourceAdminLog#4797f959 channel:long max_id:long = FileSource;\nfileSourceStoryAlbum#5e01f223 peer:long = FileSource;\nfileSourceBotPreviewMedia#0aa91441 bot:long = FileSource;\nfileSourceBotPreviewInfo#f9d2d6fc bot:long lang_code:string = FileSource;\nfileSourcePaidMedia#b18d9042 id:int peer:long = FileSource;\nfileSourceSavedMusic#dd1a7664 user_id:long id:long access_hash:long = FileSource;\nfileSourceChatFull#9de75fde chat_id:long = FileSource;\nfileSourceChannelFull#6fe19339 channel:long = FileSource;\nfileSourceCommunityFull#bf21671d channel:long = FileSource;\nfileSourcePremiumPromo#c907a44f = FileSource;\nfileSourceAttachMenuBot#c3002694 bot:long = FileSource;\nfileSourceTheme#92d05e0c id:long access_hash:long = FileSource;\nfileSourceWallPaper#50dbf2f7 id:long access_hash:long = FileSource;\nfileSourceStickerSet#34c73709 stickerset:InputStickerSet = FileSource;\nfileSourceSavedGifs#13e78e07 = FileSource;\nfileSourceSavedRingtones#2b25ef1b = FileSource;\nfileSourceAvailableEffects#eb8578f0 = FileSource;\nfileSourceAvailableReactions#0e432388 = FileSource;\nfileSourceUserProfilePhoto#e39ee274 user_id:long max_id:long = FileSource;\nfileSourceDocumentByHash#0f151e0f sha256:bytes size:long mime_type:string = FileSource;\nfileSourceWelcomeMessages#fd77304a peer:long = FileSource;\n","db_schema_json":"{\"constructors\":[{\"id\":\"-1132882121\",\"predicate\":\"boolFalse\",\"params\":[],\"type\":\"Bool\"},{\"id\":\"-1720552011\",\"predicate\":\"boolTrue\",\"params\":[],\"type\":\"Bool\"},{\"id\":\"1072550713\",\"predicate\":\"true\",\"params\":[],\"type\":\"True\"},{\"id\":\"481674261\",\"predicate\":\"vector\",\"params\":[],\"type\":\"Vector t\"},{\"id\":\"1201716553\",\"predicate\":\"fileIdPhoto\",\"params\":[{\"name\":\"id\",\"type\":\"long\"}],\"type\":\"FileId\"},{\"id\":\"1176182153\",\"predicate\":\"fileIdDocument\",\"params\":[{\"name\":\"id\",\"type\":\"long\"}],\"type\":\"FileId\"},{\"id\":\"-1314960264\",\"predicate\":\"fileSourceMessage\",\"params\":[{\"name\":\"flags\",\"type\":\"#\"},{\"name\":\"quick_reply_shortcut_id\",\"type\":\"flags.0?int\"},{\"name\":\"peer\",\"type\":\"long\"},{\"name\":\"id\",\"type\":\"int\"}],\"type\":\"FileSource\"},{\"id\":\"-1044723513\",\"predicate\":\"fileSourceStarsTransaction\",\"params\":[{\"name\":\"flags\",\"type\":\"#\"},{\"name\":\"peer\",\"type\":\"long\"},{\"name\":\"id\",\"type\":\"string\"},{\"name\":\"refund\",\"type\":\"flags.0?true\"},{\"name\":\"ton\",\"type\":\"flags.1?true\"}],\"type\":\"FileSource\"},{\"id\":\"-1648232969\",\"predicate\":\"fileSourceScheduledMessage\",\"params\":[{\"name\":\"peer\",\"type\":\"long\"},{\"name\":\"id\",\"type\":\"int\"}],\"type\":\"FileSource\"},{\"id\":\"-937368597\",\"predicate\":\"fileSourceStory\",\"params\":[{\"name\":\"id\",\"type\":\"int\"},{\"name\":\"peer\",\"type\":\"long\"}],\"type\":\"FileSource\"},{\"id\":\"-1638173540\",\"predicate\":\"fileSourceWebPage\",\"params\":[{\"name\":\"url\",\"type\":\"string\"}],\"type\":\"FileSource\"},{\"id\":\"30378874\",\"predicate\":\"fileSourceBotApp\",\"params\":[{\"name\":\"id\",\"type\":\"long\"},{\"name\":\"access_hash\",\"type\":\"long\"}],\"type\":\"FileSource\"},{\"id\":\"1895675824\",\"predicate\":\"fileSourceUserFull\",\"params\":[{\"name\":\"id\",\"type\":\"long\"}],\"type\":\"FileSource\"},{\"id\":\"1201142105\",\"predicate\":\"fileSourceAdminLog\",\"params\":[{\"name\":\"channel\",\"type\":\"long\"},{\"name\":\"max_id\",\"type\":\"long\"}],\"type\":\"FileSource\"},{\"id\":\"1577185827\",\"predicate\":\"fileSourceStoryAlbum\",\"params\":[{\"name\":\"peer\",\"type\":\"long\"}],\"type\":\"FileSource\"},{\"id\":\"178852929\",\"predicate\":\"fileSourceBotPreviewMedia\",\"params\":[{\"name\":\"bot\",\"type\":\"long\"}],\"type\":\"FileSource\"},{\"id\":\"-103622916\",\"predicate\":\"fileSourceBotPreviewInfo\",\"params\":[{\"name\":\"bot\",\"type\":\"long\"},{\"name\":\"lang_code\",\"type\":\"string\"}],\"type\":\"FileSource\"},{\"id\":\"-1316122558\",\"predicate\":\"fileSourcePaidMedia\",\"params\":[{\"name\":\"id\",\"type\":\"int\"},{\"name\":\"peer\",\"type\":\"long\"}],\"type\":\"FileSource\"},{\"id\":\"-585468316\",\"predicate\":\"fileSourceSavedMusic\",\"params\":[{\"name\":\"user_id\",\"type\":\"long\"},{\"name\":\"id\",\"type\":\"long\"},{\"name\":\"access_hash\",\"type\":\"long\"}],\"type\":\"FileSource\"},{\"id\":\"-1645781026\",\"predicate\":\"fileSourceChatFull\",\"params\":[{\"name\":\"chat_id\",\"type\":\"long\"}],\"type\":\"FileSource\"},{\"id\":\"1877054265\",\"predicate\":\"fileSourceChannelFull\",\"params\":[{\"name\":\"channel\",\"type\":\"long\"}],\"type\":\"FileSource\"},{\"id\":\"-1088329955\",\"predicate\":\"fileSourceCommunityFull\",\"params\":[{\"name\":\"channel\",\"type\":\"long\"}],\"type\":\"FileSource\"},{\"id\":\"-922246065\",\"predicate\":\"fileSourcePremiumPromo\",\"params\":[],\"type\":\"FileSource\"},{\"id\":\"-1023400300\",\"predicate\":\"fileSourceAttachMenuBot\",\"params\":[{\"name\":\"bot\",\"type\":\"long\"}],\"type\":\"FileSource\"},{\"id\":\"-1831838196\",\"predicate\":\"fileSourceTheme\",\"params\":[{\"name\":\"id\",\"type\":\"long\"},{\"name\":\"access_hash\",\"type\":\"long\"}],\"type\":\"FileSource\"},{\"id\":\"1356591863\",\"predicate\":\"fileSourceWallPaper\",\"params\":[{\"name\":\"id\",\"type\":\"long\"},{\"name\":\"access_hash\",\"type\":\"long\"}],\"type\":\"FileSource\"},{\"id\":\"885470985\",\"predicate\":\"fileSourceStickerSet\",\"params\":[{\"name\":\"stickerset\",\"type\":\"InputStickerSet\"}],\"type\":\"FileSource\"},{\"id\":\"333942279\",\"predicate\":\"fileSourceSavedGifs\",\"params\":[],\"type\":\"FileSource\"},{\"id\":\"723906331\",\"predicate\":\"fileSourceSavedRingtones\",\"params\":[],\"type\":\"FileSource\"},{\"id\":\"-343574288\",\"predicate\":\"fileSourceAvailableEffects\",\"params\":[],\"type\":\"FileSource\"},{\"id\":\"239281032\",\"predicate\":\"fileSourceAvailableReactions\",\"params\":[],\"type\":\"FileSource\"},{\"id\":\"-476126604\",\"predicate\":\"fileSourceUserProfilePhoto\",\"params\":[{\"name\":\"user_id\",\"type\":\"long\"},{\"name\":\"max_id\",\"type\":\"long\"}],\"type\":\"FileSource\"},{\"id\":\"253042191\",\"predicate\":\"fileSourceDocumentByHash\",\"params\":[{\"name\":\"sha256\",\"type\":\"bytes\"},{\"name\":\"size\",\"type\":\"long\"},{\"name\":\"mime_type\",\"type\":\"string\"}],\"type\":\"FileSource\"},{\"id\":\"-42520502\",\"predicate\":\"fileSourceWelcomeMessages\",\"params\":[{\"name\":\"peer\",\"type\":\"long\"}],\"type\":\"FileSource\"}],\"methods\":[]}","traversers_incoming":[{"_":"traverseCommitSourceLocation","type":"Document","predicate":"document","push_sources":[{"_":"source","predicate":{"_":"predicate","predicate":"document","is_constructor":true},"stored_constructor":"fileSourceStickerSet","stored_params":[{"_":"extractInputStickerSetFromDocumentAttributesAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"Document","constructor":"document","param":"attributes","param_type":"DocumentAttribute","flag":{"_":"paramNotFlag"}}]},"to":"stickerset"}],"skipped_flags":[],"needs_not_parents":[]},{"_":"source","predicate":{"_":"predicate","predicate":"document","is_constructor":true},"stored_constructor":"fileSourceSavedMusic","stored_params":[{"_":"extractUserIdFromInputUserAndStore","from":{"_":"pathParent","parts":[{"_":"pathPart","type":"users.SavedMusic","constructor":"users.getSavedMusic","param":"id","param_type":"InputUser","flag":{"_":"paramNotFlag"}}]},"to":"user_id"},{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"Document","constructor":"document","param":"id","param_type":"long","flag":{"_":"paramNotFlag"}}]},"to":"id"},{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"Document","constructor":"document","param":"access_hash","param_type":"long","flag":{"_":"paramNotFlag"}}]},"to":"access_hash"}],"skipped_flags":[],"needs_parent":{"_":"predicate","predicate":"users.getSavedMusic","is_constructor":false},"needs_not_parents":[]},{"_":"source","predicate":{"_":"predicate","predicate":"document","is_constructor":true},"stored_constructor":"fileSourceSavedMusic","stored_params":[{"_":"extractUserIdFromInputUserAndStore","from":{"_":"pathParent","parts":[{"_":"pathPart","type":"users.SavedMusic","constructor":"users.getSavedMusicByID","param":"id","param_type":"InputUser","flag":{"_":"paramNotFlag"}}]},"to":"user_id"},{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"Document","constructor":"document","param":"id","param_type":"long","flag":{"_":"paramNotFlag"}}]},"to":"id"},{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"Document","constructor":"document","param":"access_hash","param_type":"long","flag":{"_":"paramNotFlag"}}]},"to":"access_hash"}],"skipped_flags":[],"needs_parent":{"_":"predicate","predicate":"users.getSavedMusicByID","is_constructor":false},"needs_not_parents":[]}],"stored_constructor":"fileIdDocument"},{"_":"traverseCommitSourceLocation","type":"Photo","predicate":"photo","push_sources":[{"_":"source","predicate":{"_":"predicate","predicate":"photo","is_constructor":true},"stored_constructor":"fileSourceUserProfilePhoto","stored_params":[{"_":"extractUserIdFromInputUserAndStore","from":{"_":"pathParent","parts":[{"_":"pathPart","type":"photos.Photos","constructor":"photos.getUserPhotos","param":"user_id","param_type":"InputUser","flag":{"_":"paramNotFlag"}}]},"to":"user_id"},{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"Photo","constructor":"photo","param":"id","param_type":"long","flag":{"_":"paramNotFlag"}}]},"to":"max_id"}],"skipped_flags":[],"needs_parent":{"_":"predicate","predicate":"photos.getUserPhotos","is_constructor":false},"needs_not_parents":[]}],"stored_constructor":"fileIdPhoto"},{"_":"traverseMethodResult","name":"messages.getDialogs","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"messages.Dialogs","predicate":"messages.dialogs","params":[{"_":"traverseParam","name":"dialogs","is_flag":false,"is_vector":true,"type":"Dialog"},{"_":"traverseParam","name":"messages","is_flag":false,"is_vector":true,"type":"Message"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"Message","predicate":"message","params":[{"_":"traverseParam","name":"reply_to","is_flag":true,"is_vector":false,"type":"MessageReplyHeader"},{"_":"traverseParam","name":"media","is_flag":true,"is_vector":false,"type":"MessageMedia"},{"_":"traverseParam","name":"rich_message","is_flag":true,"is_vector":false,"type":"RichMessage"}],"push_sources":[{"_":"source","predicate":{"_":"predicate","predicate":"message","is_constructor":true},"stored_constructor":"fileSourceMessage","stored_params":[{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"Message","constructor":"message","param":"quick_reply_shortcut_id","param_type":"int","flag":{"_":"paramIsFlagPassthrough"}}]},"to":"quick_reply_shortcut_id"},{"_":"extractPeerIdFromPeerAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"Message","constructor":"message","param":"peer_id","param_type":"Peer","flag":{"_":"paramNotFlag"}}]},"to":"peer"},{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"Message","constructor":"message","param":"id","param_type":"int","flag":{"_":"paramNotFlag"}}]},"to":"id"}],"skipped_flags":[],"needs_not_parents":[{"_":"predicate","predicate":"messages.getScheduledMessages","is_constructor":false},{"_":"predicate","predicate":"updateNewScheduledMessage","is_constructor":true}]},{"_":"source","predicate":{"_":"predicate","predicate":"message","is_constructor":true},"stored_constructor":"fileSourceScheduledMessage","stored_params":[{"_":"extractPeerIdFromInputPeerAndStore","from":{"_":"pathParent","parts":[{"_":"pathPart","type":"messages.Messages","constructor":"messages.getScheduledMessages","param":"peer","param_type":"InputPeer","flag":{"_":"paramNotFlag"}}]},"to":"peer"},{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"Message","constructor":"message","param":"id","param_type":"int","flag":{"_":"paramNotFlag"}}]},"to":"id"}],"skipped_flags":[],"needs_parent":{"_":"predicate","predicate":"messages.getScheduledMessages","is_constructor":false},"needs_not_parents":[]},{"_":"source","predicate":{"_":"predicate","predicate":"message","is_constructor":true},"stored_constructor":"fileSourceScheduledMessage","stored_params":[{"_":"extractPeerIdFromPeerAndStore","from":{"_":"pathParent","parts":[{"_":"pathPart","type":"Update","constructor":"updateNewScheduledMessage","param":"message","param_type":"Message","flag":{"_":"paramNotFlag"}},{"_":"pathPart","type":"Message","constructor":"message","param":"peer_id","param_type":"Peer","flag":{"_":"paramNotFlag"}}]},"to":"peer"},{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"Message","constructor":"message","param":"id","param_type":"int","flag":{"_":"paramNotFlag"}}]},"to":"id"}],"skipped_flags":[],"needs_parent":{"_":"predicate","predicate":"updateNewScheduledMessage","is_constructor":true},"needs_not_parents":[]}],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"MessageMedia","predicate":"messageMediaPhoto","params":[{"_":"traverseParam","name":"photo","is_flag":true,"is_vector":false,"type":"Photo"},{"_":"traverseParam","name":"video","is_flag":true,"is_vector":false,"type":"Document"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"messages.Dialogs","predicate":"messages.dialogsSlice","params":[{"_":"traverseParam","name":"dialogs","is_flag":false,"is_vector":true,"type":"Dialog"},{"_":"traverseParam","name":"messages","is_flag":false,"is_vector":true,"type":"Message"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.getMessages","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"messages.Messages","predicate":"messages.messages","params":[{"_":"traverseParam","name":"messages","is_flag":false,"is_vector":true,"type":"Message"},{"_":"traverseParam","name":"topics","is_flag":false,"is_vector":true,"type":"ForumTopic"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.getHistory","push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.search","push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.searchGlobal","push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.getUnreadMentions","push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.getRecentLocations","push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.getScheduledHistory","push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.getScheduledMessages","push_sources":[],"is_needed_parent":true},{"_":"traverseMethodResult","name":"messages.getReplies","push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.getUnreadReactions","push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.searchSentMedia","push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.getSavedHistory","push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.getQuickReplyMessages","push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.getUnreadPollVotes","push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.getPersonalChannelHistory","push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.getRichMessage","push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"channels.getMessages","push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"channels.searchPosts","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"messages.Messages","predicate":"messages.messagesSlice","params":[{"_":"traverseParam","name":"messages","is_flag":false,"is_vector":true,"type":"Message"},{"_":"traverseParam","name":"topics","is_flag":false,"is_vector":true,"type":"ForumTopic"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"messages.Messages","predicate":"messages.channelMessages","params":[{"_":"traverseParam","name":"messages","is_flag":false,"is_vector":true,"type":"Message"},{"_":"traverseParam","name":"topics","is_flag":false,"is_vector":true,"type":"ForumTopic"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"Update","predicate":"updateNewMessage","params":[{"_":"traverseParam","name":"message","is_flag":false,"is_vector":false,"type":"Message"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"Update","predicate":"updateNewChannelMessage","params":[{"_":"traverseParam","name":"message","is_flag":false,"is_vector":false,"type":"Message"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"Update","predicate":"updateEditChannelMessage","params":[{"_":"traverseParam","name":"message","is_flag":false,"is_vector":false,"type":"Message"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"Update","predicate":"updateEditMessage","params":[{"_":"traverseParam","name":"message","is_flag":false,"is_vector":false,"type":"Message"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"Update","predicate":"updateNewScheduledMessage","params":[{"_":"traverseParam","name":"message","is_flag":false,"is_vector":false,"type":"Message"}],"push_sources":[],"is_needed_parent":true},{"_":"traverseIncomingConstructor","type":"Update","predicate":"updateQuickReplyMessage","params":[{"_":"traverseParam","name":"message","is_flag":false,"is_vector":false,"type":"Message"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"Update","predicate":"updateBotNewBusinessMessage","params":[{"_":"traverseParam","name":"message","is_flag":false,"is_vector":false,"type":"Message"},{"_":"traverseParam","name":"reply_to_message","is_flag":true,"is_vector":false,"type":"Message"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"Update","predicate":"updateBotEditBusinessMessage","params":[{"_":"traverseParam","name":"message","is_flag":false,"is_vector":false,"type":"Message"},{"_":"traverseParam","name":"reply_to_message","is_flag":true,"is_vector":false,"type":"Message"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"Update","predicate":"updateBusinessBotCallbackQuery","params":[{"_":"traverseParam","name":"message","is_flag":false,"is_vector":false,"type":"Message"},{"_":"traverseParam","name":"reply_to_message","is_flag":true,"is_vector":false,"type":"Message"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"Update","predicate":"updateBotGuestChatQuery","params":[{"_":"traverseParam","name":"message","is_flag":false,"is_vector":false,"type":"Message"},{"_":"traverseParam","name":"reference_messages","is_flag":true,"is_vector":true,"type":"Message"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"updates.getDifference","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"updates.Difference","predicate":"updates.difference","params":[{"_":"traverseParam","name":"new_messages","is_flag":false,"is_vector":true,"type":"Message"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"updates.Difference","predicate":"updates.differenceSlice","params":[{"_":"traverseParam","name":"new_messages","is_flag":false,"is_vector":true,"type":"Message"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"updates.getChannelDifference","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"updates.ChannelDifference","predicate":"updates.channelDifferenceTooLong","params":[{"_":"traverseParam","name":"dialog","is_flag":false,"is_vector":false,"type":"Dialog"},{"_":"traverseParam","name":"messages","is_flag":false,"is_vector":true,"type":"Message"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"updates.ChannelDifference","predicate":"updates.channelDifference","params":[{"_":"traverseParam","name":"new_messages","is_flag":false,"is_vector":true,"type":"Message"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.getPeerDialogs","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"messages.PeerDialogs","predicate":"messages.peerDialogs","params":[{"_":"traverseParam","name":"dialogs","is_flag":false,"is_vector":true,"type":"Dialog"},{"_":"traverseParam","name":"messages","is_flag":false,"is_vector":true,"type":"Message"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.getPinnedDialogs","push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"channels.getAdminLog","push_sources":[],"is_needed_parent":true},{"_":"traverseIncomingConstructor","type":"channels.AdminLogResults","predicate":"channels.adminLogResults","params":[{"_":"traverseParam","name":"events","is_flag":false,"is_vector":true,"type":"ChannelAdminLogEvent"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"ChannelAdminLogEvent","predicate":"channelAdminLogEvent","params":[{"_":"traverseParam","name":"action","is_flag":false,"is_vector":false,"type":"ChannelAdminLogEventAction"}],"push_sources":[{"_":"source","predicate":{"_":"predicate","predicate":"channelAdminLogEvent","is_constructor":true},"stored_constructor":"fileSourceAdminLog","stored_params":[{"_":"extractChannelIdFromInputChannelAndStore","from":{"_":"pathParent","parts":[{"_":"pathPart","type":"channels.AdminLogResults","constructor":"channels.getAdminLog","param":"channel","param_type":"InputChannel","flag":{"_":"paramNotFlag"}}]},"to":"channel"},{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"ChannelAdminLogEvent","constructor":"channelAdminLogEvent","param":"id","param_type":"long","flag":{"_":"paramNotFlag"}}]},"to":"max_id"}],"skipped_flags":[],"needs_parent":{"_":"predicate","predicate":"channels.getAdminLog","is_constructor":false},"needs_not_parents":[]}],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"ChannelAdminLogEventAction","predicate":"channelAdminLogEventActionUpdatePinned","params":[{"_":"traverseParam","name":"message","is_flag":false,"is_vector":false,"type":"Message"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"ChannelAdminLogEventAction","predicate":"channelAdminLogEventActionEditMessage","params":[{"_":"traverseParam","name":"prev_message","is_flag":false,"is_vector":false,"type":"Message"},{"_":"traverseParam","name":"new_message","is_flag":false,"is_vector":false,"type":"Message"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"ChannelAdminLogEventAction","predicate":"channelAdminLogEventActionDeleteMessage","params":[{"_":"traverseParam","name":"message","is_flag":false,"is_vector":false,"type":"Message"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"ChannelAdminLogEventAction","predicate":"channelAdminLogEventActionStopPoll","params":[{"_":"traverseParam","name":"message","is_flag":false,"is_vector":false,"type":"Message"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"ChannelAdminLogEventAction","predicate":"channelAdminLogEventActionSendMessage","params":[{"_":"traverseParam","name":"message","is_flag":false,"is_vector":false,"type":"Message"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.getDiscussionMessage","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"messages.DiscussionMessage","predicate":"messages.discussionMessage","params":[{"_":"traverseParam","name":"messages","is_flag":false,"is_vector":true,"type":"Message"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.getSearchResultsCalendar","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"messages.SearchResultsCalendar","predicate":"messages.searchResultsCalendar","params":[{"_":"traverseParam","name":"messages","is_flag":false,"is_vector":true,"type":"Message"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.getForumTopics","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"messages.ForumTopics","predicate":"messages.forumTopics","params":[{"_":"traverseParam","name":"topics","is_flag":false,"is_vector":true,"type":"ForumTopic"},{"_":"traverseParam","name":"messages","is_flag":false,"is_vector":true,"type":"Message"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.getForumTopicsByID","push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"stories.getStoryViewsList","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"stories.StoryViewsList","predicate":"stories.storyViewsList","params":[{"_":"traverseParam","name":"views","is_flag":false,"is_vector":true,"type":"StoryView"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"StoryView","predicate":"storyViewPublicForward","params":[{"_":"traverseParam","name":"message","is_flag":false,"is_vector":false,"type":"Message"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"stats.getMessagePublicForwards","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"stats.PublicForwards","predicate":"stats.publicForwards","params":[{"_":"traverseParam","name":"forwards","is_flag":false,"is_vector":true,"type":"PublicForward"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"PublicForward","predicate":"publicForwardMessage","params":[{"_":"traverseParam","name":"message","is_flag":false,"is_vector":false,"type":"Message"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"stats.getStoryPublicForwards","push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"stories.getStoryReactionsList","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"stories.StoryReactionsList","predicate":"stories.storyReactionsList","params":[{"_":"traverseParam","name":"reactions","is_flag":false,"is_vector":true,"type":"StoryReaction"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"StoryReaction","predicate":"storyReactionPublicForward","params":[{"_":"traverseParam","name":"message","is_flag":false,"is_vector":false,"type":"Message"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.getSavedDialogs","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"messages.SavedDialogs","predicate":"messages.savedDialogs","params":[{"_":"traverseParam","name":"dialogs","is_flag":false,"is_vector":true,"type":"SavedDialog"},{"_":"traverseParam","name":"messages","is_flag":false,"is_vector":true,"type":"Message"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.getPinnedSavedDialogs","push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.getSavedDialogsByID","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"messages.SavedDialogs","predicate":"messages.savedDialogsSlice","params":[{"_":"traverseParam","name":"dialogs","is_flag":false,"is_vector":true,"type":"SavedDialog"},{"_":"traverseParam","name":"messages","is_flag":false,"is_vector":true,"type":"Message"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.getQuickReplies","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"messages.QuickReplies","predicate":"messages.quickReplies","params":[{"_":"traverseParam","name":"messages","is_flag":false,"is_vector":true,"type":"Message"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"MessageMedia","predicate":"messageMediaPoll","params":[{"_":"traverseParam","name":"poll","is_flag":false,"is_vector":false,"type":"Poll"},{"_":"traverseParam","name":"results","is_flag":false,"is_vector":false,"type":"PollResults"},{"_":"traverseParam","name":"attached_media","is_flag":true,"is_vector":false,"type":"MessageMedia"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"Update","predicate":"updateServiceNotification","params":[{"_":"traverseParam","name":"media","is_flag":false,"is_vector":false,"type":"MessageMedia"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"Message","predicate":"messageService","params":[{"_":"traverseParam","name":"reply_to","is_flag":true,"is_vector":false,"type":"MessageReplyHeader"},{"_":"traverseParam","name":"action","is_flag":false,"is_vector":false,"type":"MessageAction"}],"push_sources":[{"_":"source","predicate":{"_":"predicate","predicate":"messageService","is_constructor":true},"stored_constructor":"fileSourceMessage","stored_params":[{"_":"extractPeerIdFromPeerAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"Message","constructor":"messageService","param":"peer_id","param_type":"Peer","flag":{"_":"paramNotFlag"}}]},"to":"peer"},{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"Message","constructor":"messageService","param":"id","param_type":"int","flag":{"_":"paramNotFlag"}}]},"to":"id"}],"skipped_flags":["quick_reply_shortcut_id"],"needs_not_parents":[{"_":"predicate","predicate":"messages.getScheduledMessages","is_constructor":false},{"_":"predicate","predicate":"updateNewScheduledMessage","is_constructor":true}]}],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"MessageAction","predicate":"messageActionPollAppendAnswer","params":[{"_":"traverseParam","name":"answer","is_flag":false,"is_vector":false,"type":"PollAnswer"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"PollAnswer","predicate":"pollAnswer","params":[{"_":"traverseParam","name":"media","is_flag":true,"is_vector":false,"type":"MessageMedia"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"MessageAction","predicate":"messageActionPollDeleteAnswer","params":[{"_":"traverseParam","name":"answer","is_flag":false,"is_vector":false,"type":"PollAnswer"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"Update","predicate":"updateMessagePoll","params":[{"_":"traverseParam","name":"poll","is_flag":true,"is_vector":false,"type":"Poll"},{"_":"traverseParam","name":"results","is_flag":false,"is_vector":false,"type":"PollResults"}],"push_sources":[{"_":"source","predicate":{"_":"predicate","predicate":"updateMessagePoll","is_constructor":true},"stored_constructor":"fileSourceMessage","stored_params":[{"_":"extractPeerIdFromPeerAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"Update","constructor":"updateMessagePoll","param":"peer","param_type":"Peer","flag":{"_":"paramIsFlagAbortIfEmpty"}}]},"to":"peer"},{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"Update","constructor":"updateMessagePoll","param":"msg_id","param_type":"int","flag":{"_":"paramIsFlagAbortIfEmpty"}}]},"to":"id"}],"skipped_flags":["quick_reply_shortcut_id"],"needs_not_parents":[{"_":"predicate","predicate":"messages.getScheduledMessages","is_constructor":false},{"_":"predicate","predicate":"updateNewScheduledMessage","is_constructor":true}]}],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"Poll","predicate":"poll","params":[{"_":"traverseParam","name":"answers","is_flag":false,"is_vector":true,"type":"PollAnswer"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"PollResults","predicate":"pollResults","params":[{"_":"traverseParam","name":"solution_media","is_flag":true,"is_vector":false,"type":"MessageMedia"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"MessageReplyHeader","predicate":"messageReplyHeader","params":[{"_":"traverseParam","name":"reply_media","is_flag":true,"is_vector":false,"type":"MessageMedia"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"Update","predicate":"updateNewEphemeralMessage","params":[{"_":"traverseParam","name":"message","is_flag":false,"is_vector":false,"type":"EphemeralMessage"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"EphemeralMessage","predicate":"ephemeralMessage","params":[{"_":"traverseParam","name":"media","is_flag":true,"is_vector":false,"type":"MessageMedia"},{"_":"traverseParam","name":"reply_to","is_flag":true,"is_vector":false,"type":"MessageReplyHeader"},{"_":"traverseParam","name":"rich_message","is_flag":true,"is_vector":false,"type":"RichMessage"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"Update","predicate":"updateEditEphemeralMessage","params":[{"_":"traverseParam","name":"message","is_flag":false,"is_vector":false,"type":"EphemeralMessage"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"Update","predicate":"updateEphemeralBotCallbackQuery","params":[{"_":"traverseParam","name":"message","is_flag":false,"is_vector":false,"type":"EphemeralMessage"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"ephemeral.getWelcomeMessages","push_sources":[{"_":"source","predicate":{"_":"predicate","predicate":"ephemeral.getWelcomeMessages","is_constructor":false},"stored_constructor":"fileSourceWelcomeMessages","stored_params":[{"_":"extractPeerIdFromInputPeerAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"ephemeral.WelcomeMessages","constructor":"ephemeral.getWelcomeMessages","param":"peer","param_type":"InputPeer","flag":{"_":"paramNotFlag"}}]},"to":"peer"}],"skipped_flags":[],"needs_not_parents":[]}],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"ephemeral.WelcomeMessages","predicate":"ephemeral.welcomeMessages","params":[{"_":"traverseParam","name":"messages","is_flag":false,"is_vector":true,"type":"EphemeralMessage"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.getSponsoredMessages","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"messages.SponsoredMessages","predicate":"messages.sponsoredMessages","params":[{"_":"traverseParam","name":"messages","is_flag":false,"is_vector":true,"type":"SponsoredMessage"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"SponsoredMessage","predicate":"sponsoredMessage","params":[{"_":"traverseParam","name":"media","is_flag":true,"is_vector":false,"type":"MessageMedia"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"Update","predicate":"updateMessageExtendedMedia","params":[{"_":"traverseParam","name":"extended_media","is_flag":false,"is_vector":true,"type":"MessageExtendedMedia"}],"push_sources":[{"_":"source","predicate":{"_":"predicate","predicate":"updateMessageExtendedMedia","is_constructor":true},"stored_constructor":"fileSourcePaidMedia","stored_params":[{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"Update","constructor":"updateMessageExtendedMedia","param":"msg_id","param_type":"int","flag":{"_":"paramNotFlag"}}]},"to":"id"},{"_":"extractPeerIdFromPeerAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"Update","constructor":"updateMessageExtendedMedia","param":"peer","param_type":"Peer","flag":{"_":"paramNotFlag"}}]},"to":"peer"}],"skipped_flags":[],"needs_not_parents":[]}],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"MessageExtendedMedia","predicate":"messageExtendedMedia","params":[{"_":"traverseParam","name":"media","is_flag":false,"is_vector":false,"type":"MessageMedia"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"Update","predicate":"updateStory","params":[{"_":"traverseParam","name":"story","is_flag":false,"is_vector":false,"type":"StoryItem"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"StoryItem","predicate":"storyItem","params":[{"_":"traverseParam","name":"media","is_flag":false,"is_vector":false,"type":"MessageMedia"},{"_":"traverseParam","name":"music","is_flag":true,"is_vector":false,"type":"Document"}],"push_sources":[{"_":"source","predicate":{"_":"predicate","predicate":"storyItem","is_constructor":true},"stored_constructor":"fileSourceStory","stored_params":[{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"StoryItem","constructor":"storyItem","param":"id","param_type":"int","flag":{"_":"paramNotFlag"}}]},"to":"id"},{"_":"extractPeerIdFromInputPeerAndStore","from":{"_":"pathParent","parts":[{"_":"pathPart","type":"stories.Stories","constructor":"stories.getPinnedStories","param":"peer","param_type":"InputPeer","flag":{"_":"paramNotFlag"}}]},"to":"peer"}],"skipped_flags":[],"needs_parent":{"_":"predicate","predicate":"stories.getPinnedStories","is_constructor":false},"needs_not_parents":[]},{"_":"source","predicate":{"_":"predicate","predicate":"storyItem","is_constructor":true},"stored_constructor":"fileSourceStory","stored_params":[{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"StoryItem","constructor":"storyItem","param":"id","param_type":"int","flag":{"_":"paramNotFlag"}}]},"to":"id"},{"_":"extractPeerIdFromInputPeerAndStore","from":{"_":"pathParent","parts":[{"_":"pathPart","type":"stories.Stories","constructor":"stories.getStoriesArchive","param":"peer","param_type":"InputPeer","flag":{"_":"paramNotFlag"}}]},"to":"peer"}],"skipped_flags":[],"needs_parent":{"_":"predicate","predicate":"stories.getStoriesArchive","is_constructor":false},"needs_not_parents":[]},{"_":"source","predicate":{"_":"predicate","predicate":"storyItem","is_constructor":true},"stored_constructor":"fileSourceStory","stored_params":[{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"StoryItem","constructor":"storyItem","param":"id","param_type":"int","flag":{"_":"paramNotFlag"}}]},"to":"id"},{"_":"extractPeerIdFromInputPeerAndStore","from":{"_":"pathParent","parts":[{"_":"pathPart","type":"stories.Stories","constructor":"stories.getStoriesByID","param":"peer","param_type":"InputPeer","flag":{"_":"paramNotFlag"}}]},"to":"peer"}],"skipped_flags":[],"needs_parent":{"_":"predicate","predicate":"stories.getStoriesByID","is_constructor":false},"needs_not_parents":[]},{"_":"source","predicate":{"_":"predicate","predicate":"storyItem","is_constructor":true},"stored_constructor":"fileSourceStory","stored_params":[{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"StoryItem","constructor":"storyItem","param":"id","param_type":"int","flag":{"_":"paramNotFlag"}}]},"to":"id"},{"_":"extractPeerIdFromInputPeerAndStore","from":{"_":"pathParent","parts":[{"_":"pathPart","type":"stories.Stories","constructor":"stories.getAlbumStories","param":"peer","param_type":"InputPeer","flag":{"_":"paramNotFlag"}}]},"to":"peer"}],"skipped_flags":[],"needs_parent":{"_":"predicate","predicate":"stories.getAlbumStories","is_constructor":false},"needs_not_parents":[]},{"_":"source","predicate":{"_":"predicate","predicate":"storyItem","is_constructor":true},"stored_constructor":"fileSourceStory","stored_params":[{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"StoryItem","constructor":"storyItem","param":"id","param_type":"int","flag":{"_":"paramNotFlag"}}]},"to":"id"},{"_":"extractPeerIdFromPeerAndStore","from":{"_":"pathParent","parts":[{"_":"pathPart","type":"PeerStories","constructor":"peerStories","param":"peer","param_type":"Peer","flag":{"_":"paramNotFlag"}}]},"to":"peer"}],"skipped_flags":[],"needs_parent":{"_":"predicate","predicate":"peerStories","is_constructor":true},"needs_not_parents":[]},{"_":"source","predicate":{"_":"predicate","predicate":"storyItem","is_constructor":true},"stored_constructor":"fileSourceStory","stored_params":[{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"StoryItem","constructor":"storyItem","param":"id","param_type":"int","flag":{"_":"paramNotFlag"}}]},"to":"id"},{"_":"extractPeerIdFromPeerAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"StoryItem","constructor":"storyItem","param":"from_id","param_type":"Peer","flag":{"_":"paramIsFlagAbortIfEmpty"}}]},"to":"peer"}],"skipped_flags":[],"needs_not_parents":[]}],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"Update","predicate":"updateWebPage","params":[{"_":"traverseParam","name":"webpage","is_flag":false,"is_vector":false,"type":"WebPage"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"WebPage","predicate":"webPage","params":[{"_":"traverseParam","name":"photo","is_flag":true,"is_vector":false,"type":"Photo"},{"_":"traverseParam","name":"document","is_flag":true,"is_vector":false,"type":"Document"},{"_":"traverseParam","name":"cached_page","is_flag":true,"is_vector":false,"type":"Page"},{"_":"traverseParam","name":"attributes","is_flag":true,"is_vector":true,"type":"WebPageAttribute"}],"push_sources":[{"_":"source","predicate":{"_":"predicate","predicate":"webPage","is_constructor":true},"stored_constructor":"fileSourceWebPage","stored_params":[{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"WebPage","constructor":"webPage","param":"url","param_type":"string","flag":{"_":"paramNotFlag"}}]},"to":"url"}],"skipped_flags":[],"needs_not_parents":[]}],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"WebPageAttribute","predicate":"webPageAttributeStory","params":[{"_":"traverseParam","name":"story","is_flag":true,"is_vector":false,"type":"StoryItem"}],"push_sources":[{"_":"source","predicate":{"_":"predicate","predicate":"webPageAttributeStory","is_constructor":true},"stored_constructor":"fileSourceStory","stored_params":[{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"WebPageAttribute","constructor":"webPageAttributeStory","param":"story","param_type":"StoryItem","flag":{"_":"paramIsFlagAbortIfEmpty"}},{"_":"pathPart","type":"StoryItem","constructor":"storyItem","param":"id","param_type":"int","flag":{"_":"paramNotFlag"}}]},"to":"id"},{"_":"extractPeerIdFromPeerAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"WebPageAttribute","constructor":"webPageAttributeStory","param":"peer","param_type":"Peer","flag":{"_":"paramNotFlag"}}]},"to":"peer"}],"skipped_flags":[],"needs_not_parents":[]}],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"Update","predicate":"updateChannelWebPage","params":[{"_":"traverseParam","name":"webpage","is_flag":false,"is_vector":false,"type":"WebPage"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.getWebPage","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"messages.WebPage","predicate":"messages.webPage","params":[{"_":"traverseParam","name":"webpage","is_flag":false,"is_vector":false,"type":"WebPage"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"stories.getPinnedStories","push_sources":[],"is_needed_parent":true},{"_":"traverseIncomingConstructor","type":"stories.Stories","predicate":"stories.stories","params":[{"_":"traverseParam","name":"stories","is_flag":false,"is_vector":true,"type":"StoryItem"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"stories.getStoriesArchive","push_sources":[],"is_needed_parent":true},{"_":"traverseMethodResult","name":"stories.getStoriesByID","push_sources":[],"is_needed_parent":true},{"_":"traverseMethodResult","name":"stories.getAlbumStories","push_sources":[],"is_needed_parent":true},{"_":"traverseIncomingConstructor","type":"StoryView","predicate":"storyViewPublicRepost","params":[{"_":"traverseParam","name":"story","is_flag":false,"is_vector":false,"type":"StoryItem"}],"push_sources":[{"_":"source","predicate":{"_":"predicate","predicate":"storyViewPublicRepost","is_constructor":true},"stored_constructor":"fileSourceStory","stored_params":[{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"StoryView","constructor":"storyViewPublicRepost","param":"story","param_type":"StoryItem","flag":{"_":"paramNotFlag"}},{"_":"pathPart","type":"StoryItem","constructor":"storyItem","param":"id","param_type":"int","flag":{"_":"paramNotFlag"}}]},"to":"id"},{"_":"extractPeerIdFromPeerAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"StoryView","constructor":"storyViewPublicRepost","param":"peer_id","param_type":"Peer","flag":{"_":"paramNotFlag"}}]},"to":"peer"}],"skipped_flags":[],"needs_not_parents":[]}],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"PeerStories","predicate":"peerStories","params":[{"_":"traverseParam","name":"stories","is_flag":false,"is_vector":true,"type":"StoryItem"}],"push_sources":[],"is_needed_parent":true},{"_":"traverseMethodResult","name":"messages.getFullChat","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"messages.ChatFull","predicate":"messages.chatFull","params":[{"_":"traverseParam","name":"full_chat","is_flag":false,"is_vector":false,"type":"ChatFull"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"ChatFull","predicate":"channelFull","params":[{"_":"traverseParam","name":"chat_photo","is_flag":false,"is_vector":false,"type":"Photo"},{"_":"traverseParam","name":"bot_info","is_flag":false,"is_vector":true,"type":"BotInfo"},{"_":"traverseParam","name":"stories","is_flag":true,"is_vector":false,"type":"PeerStories"},{"_":"traverseParam","name":"wallpaper","is_flag":true,"is_vector":false,"type":"WallPaper"},{"_":"traverseParam","name":"share_theme","is_flag":true,"is_vector":false,"type":"ShareTheme"}],"push_sources":[{"_":"source","predicate":{"_":"predicate","predicate":"channelFull","is_constructor":true},"stored_constructor":"fileSourceChannelFull","stored_params":[{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"ChatFull","constructor":"channelFull","param":"id","param_type":"long","flag":{"_":"paramNotFlag"}}]},"to":"channel"}],"skipped_flags":[],"needs_not_parents":[]}],"is_needed_parent":false},{"_":"traverseMethodResult","name":"channels.getFullChannel","push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"users.getFullUser","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"users.UserFull","predicate":"users.userFull","params":[{"_":"traverseParam","name":"full_user","is_flag":false,"is_vector":false,"type":"UserFull"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"UserFull","predicate":"userFull","params":[{"_":"traverseParam","name":"personal_photo","is_flag":true,"is_vector":false,"type":"Photo"},{"_":"traverseParam","name":"profile_photo","is_flag":true,"is_vector":false,"type":"Photo"},{"_":"traverseParam","name":"fallback_photo","is_flag":true,"is_vector":false,"type":"Photo"},{"_":"traverseParam","name":"bot_info","is_flag":true,"is_vector":false,"type":"BotInfo"},{"_":"traverseParam","name":"theme","is_flag":true,"is_vector":false,"type":"ChatTheme"},{"_":"traverseParam","name":"wallpaper","is_flag":true,"is_vector":false,"type":"WallPaper"},{"_":"traverseParam","name":"stories","is_flag":true,"is_vector":false,"type":"PeerStories"},{"_":"traverseParam","name":"business_intro","is_flag":true,"is_vector":false,"type":"BusinessIntro"},{"_":"traverseParam","name":"saved_music","is_flag":true,"is_vector":false,"type":"Document"},{"_":"traverseParam","name":"wallet_card","is_flag":true,"is_vector":false,"type":"wallet.Card"},{"_":"traverseParam","name":"share_theme","is_flag":true,"is_vector":false,"type":"ShareTheme"}],"push_sources":[{"_":"source","predicate":{"_":"predicate","predicate":"userFull","is_constructor":true},"stored_constructor":"fileSourceUserFull","stored_params":[{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"UserFull","constructor":"userFull","param":"id","param_type":"long","flag":{"_":"paramNotFlag"}}]},"to":"id"}],"skipped_flags":[],"needs_not_parents":[]},{"_":"source","predicate":{"_":"predicate","predicate":"userFull","is_constructor":true},"stored_constructor":"fileSourceSavedMusic","stored_params":[{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"UserFull","constructor":"userFull","param":"id","param_type":"long","flag":{"_":"paramNotFlag"}}]},"to":"user_id"},{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"UserFull","constructor":"userFull","param":"saved_music","param_type":"Document","flag":{"_":"paramIsFlagAbortIfEmpty"}},{"_":"pathPart","type":"Document","constructor":"document","param":"id","param_type":"long","flag":{"_":"paramNotFlag"}}]},"to":"id"},{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"UserFull","constructor":"userFull","param":"saved_music","param_type":"Document","flag":{"_":"paramIsFlagAbortIfEmpty"}},{"_":"pathPart","type":"Document","constructor":"document","param":"access_hash","param_type":"long","flag":{"_":"paramNotFlag"}}]},"to":"access_hash"}],"skipped_flags":[],"needs_not_parents":[]}],"is_needed_parent":false},{"_":"traverseMethodResult","name":"stories.getAllStories","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"stories.AllStories","predicate":"stories.allStories","params":[{"_":"traverseParam","name":"peer_stories","is_flag":false,"is_vector":true,"type":"PeerStories"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"stories.getPeerStories","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"stories.PeerStories","predicate":"stories.peerStories","params":[{"_":"traverseParam","name":"stories","is_flag":false,"is_vector":false,"type":"PeerStories"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"PublicForward","predicate":"publicForwardStory","params":[{"_":"traverseParam","name":"story","is_flag":false,"is_vector":false,"type":"StoryItem"}],"push_sources":[{"_":"source","predicate":{"_":"predicate","predicate":"publicForwardStory","is_constructor":true},"stored_constructor":"fileSourceStory","stored_params":[{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"PublicForward","constructor":"publicForwardStory","param":"story","param_type":"StoryItem","flag":{"_":"paramNotFlag"}},{"_":"pathPart","type":"StoryItem","constructor":"storyItem","param":"id","param_type":"int","flag":{"_":"paramNotFlag"}}]},"to":"id"},{"_":"extractPeerIdFromPeerAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"PublicForward","constructor":"publicForwardStory","param":"peer","param_type":"Peer","flag":{"_":"paramNotFlag"}}]},"to":"peer"}],"skipped_flags":[],"needs_not_parents":[]}],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"StoryReaction","predicate":"storyReactionPublicRepost","params":[{"_":"traverseParam","name":"story","is_flag":false,"is_vector":false,"type":"StoryItem"}],"push_sources":[{"_":"source","predicate":{"_":"predicate","predicate":"storyReactionPublicRepost","is_constructor":true},"stored_constructor":"fileSourceStory","stored_params":[{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"StoryReaction","constructor":"storyReactionPublicRepost","param":"story","param_type":"StoryItem","flag":{"_":"paramNotFlag"}},{"_":"pathPart","type":"StoryItem","constructor":"storyItem","param":"id","param_type":"int","flag":{"_":"paramNotFlag"}}]},"to":"id"},{"_":"extractPeerIdFromPeerAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"StoryReaction","constructor":"storyReactionPublicRepost","param":"peer_id","param_type":"Peer","flag":{"_":"paramNotFlag"}}]},"to":"peer"}],"skipped_flags":[],"needs_not_parents":[]}],"is_needed_parent":false},{"_":"traverseMethodResult","name":"stories.searchPosts","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"stories.FoundStories","predicate":"stories.foundStories","params":[{"_":"traverseParam","name":"stories","is_flag":false,"is_vector":true,"type":"FoundStory"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"FoundStory","predicate":"foundStory","params":[{"_":"traverseParam","name":"story","is_flag":false,"is_vector":false,"type":"StoryItem"}],"push_sources":[{"_":"source","predicate":{"_":"predicate","predicate":"foundStory","is_constructor":true},"stored_constructor":"fileSourceStory","stored_params":[{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"FoundStory","constructor":"foundStory","param":"story","param_type":"StoryItem","flag":{"_":"paramNotFlag"}},{"_":"pathPart","type":"StoryItem","constructor":"storyItem","param":"id","param_type":"int","flag":{"_":"paramNotFlag"}}]},"to":"id"},{"_":"extractPeerIdFromPeerAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"FoundStory","constructor":"foundStory","param":"peer","param_type":"Peer","flag":{"_":"paramNotFlag"}}]},"to":"peer"}],"skipped_flags":[],"needs_not_parents":[]}],"is_needed_parent":false},{"_":"traverseMethodResult","name":"payments.getStarsStatus","push_sources":[],"is_needed_parent":true},{"_":"traverseIncomingConstructor","type":"payments.StarsStatus","predicate":"payments.starsStatus","params":[{"_":"traverseParam","name":"history","is_flag":true,"is_vector":true,"type":"StarsTransaction"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"StarsTransaction","predicate":"starsTransaction","params":[{"_":"traverseParam","name":"extended_media","is_flag":true,"is_vector":true,"type":"MessageMedia"},{"_":"traverseParam","name":"stargift","is_flag":true,"is_vector":false,"type":"StarGift"}],"push_sources":[{"_":"source","predicate":{"_":"predicate","predicate":"starsTransaction","is_constructor":true},"stored_constructor":"fileSourceStarsTransaction","stored_params":[{"_":"extractPeerIdFromInputPeerAndStore","from":{"_":"pathParent","parts":[{"_":"pathPart","type":"payments.StarsStatus","constructor":"payments.getStarsStatus","param":"peer","param_type":"InputPeer","flag":{"_":"paramNotFlag"}}]},"to":"peer"},{"_":"extractAndStore","from":{"_":"pathParent","parts":[{"_":"pathPart","type":"payments.StarsStatus","constructor":"payments.getStarsStatus","param":"ton","param_type":"true","flag":{"_":"paramIsFlagPassthrough"}}]},"to":"ton"},{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"StarsTransaction","constructor":"starsTransaction","param":"id","param_type":"string","flag":{"_":"paramNotFlag"}}]},"to":"id"},{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"StarsTransaction","constructor":"starsTransaction","param":"refund","param_type":"true","flag":{"_":"paramIsFlagPassthrough"}}]},"to":"refund"}],"skipped_flags":[],"needs_parent":{"_":"predicate","predicate":"payments.getStarsStatus","is_constructor":false},"needs_not_parents":[]},{"_":"source","predicate":{"_":"predicate","predicate":"starsTransaction","is_constructor":true},"stored_constructor":"fileSourceStarsTransaction","stored_params":[{"_":"extractPeerIdFromInputPeerAndStore","from":{"_":"pathParent","parts":[{"_":"pathPart","type":"payments.StarsStatus","constructor":"payments.getStarsTransactions","param":"peer","param_type":"InputPeer","flag":{"_":"paramNotFlag"}}]},"to":"peer"},{"_":"extractAndStore","from":{"_":"pathParent","parts":[{"_":"pathPart","type":"payments.StarsStatus","constructor":"payments.getStarsTransactions","param":"ton","param_type":"true","flag":{"_":"paramIsFlagPassthrough"}}]},"to":"ton"},{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"StarsTransaction","constructor":"starsTransaction","param":"id","param_type":"string","flag":{"_":"paramNotFlag"}}]},"to":"id"},{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"StarsTransaction","constructor":"starsTransaction","param":"refund","param_type":"true","flag":{"_":"paramIsFlagPassthrough"}}]},"to":"refund"}],"skipped_flags":[],"needs_parent":{"_":"predicate","predicate":"payments.getStarsTransactions","is_constructor":false},"needs_not_parents":[]},{"_":"source","predicate":{"_":"predicate","predicate":"starsTransaction","is_constructor":true},"stored_constructor":"fileSourceStarsTransaction","stored_params":[{"_":"extractPeerIdFromInputPeerAndStore","from":{"_":"pathParent","parts":[{"_":"pathPart","type":"payments.StarsStatus","constructor":"payments.getStarsTransactionsByID","param":"peer","param_type":"InputPeer","flag":{"_":"paramNotFlag"}}]},"to":"peer"},{"_":"extractAndStore","from":{"_":"pathParent","parts":[{"_":"pathPart","type":"payments.StarsStatus","constructor":"payments.getStarsTransactionsByID","param":"ton","param_type":"true","flag":{"_":"paramIsFlagPassthrough"}}]},"to":"ton"},{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"StarsTransaction","constructor":"starsTransaction","param":"id","param_type":"string","flag":{"_":"paramNotFlag"}}]},"to":"id"},{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"StarsTransaction","constructor":"starsTransaction","param":"refund","param_type":"true","flag":{"_":"paramIsFlagPassthrough"}}]},"to":"refund"}],"skipped_flags":[],"needs_parent":{"_":"predicate","predicate":"payments.getStarsTransactionsByID","is_constructor":false},"needs_not_parents":[]},{"_":"source","predicate":{"_":"predicate","predicate":"starsTransaction","is_constructor":true},"stored_constructor":"fileSourceStarsTransaction","stored_params":[{"_":"extractPeerIdFromInputPeerAndStore","from":{"_":"pathParent","parts":[{"_":"pathPart","type":"payments.StarsStatus","constructor":"payments.getStarsSubscriptions","param":"peer","param_type":"InputPeer","flag":{"_":"paramNotFlag"}}]},"to":"peer"},{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"StarsTransaction","constructor":"starsTransaction","param":"id","param_type":"string","flag":{"_":"paramNotFlag"}}]},"to":"id"},{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"StarsTransaction","constructor":"starsTransaction","param":"refund","param_type":"true","flag":{"_":"paramIsFlagPassthrough"}}]},"to":"refund"}],"skipped_flags":["ton"],"needs_parent":{"_":"predicate","predicate":"payments.getStarsSubscriptions","is_constructor":false},"needs_not_parents":[]}],"is_needed_parent":false},{"_":"traverseMethodResult","name":"payments.getStarsTransactions","push_sources":[],"is_needed_parent":true},{"_":"traverseMethodResult","name":"payments.getStarsTransactionsByID","push_sources":[],"is_needed_parent":true},{"_":"traverseMethodResult","name":"payments.getStarsSubscriptions","push_sources":[],"is_needed_parent":true},{"_":"traverseMethodResult","name":"bots.getPreviewInfo","push_sources":[{"_":"source","predicate":{"_":"predicate","predicate":"bots.getPreviewInfo","is_constructor":false},"stored_constructor":"fileSourceBotPreviewInfo","stored_params":[{"_":"extractUserIdFromInputUserAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"bots.PreviewInfo","constructor":"bots.getPreviewInfo","param":"bot","param_type":"InputUser","flag":{"_":"paramNotFlag"}}]},"to":"bot"},{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"bots.PreviewInfo","constructor":"bots.getPreviewInfo","param":"lang_code","param_type":"string","flag":{"_":"paramNotFlag"}}]},"to":"lang_code"}],"skipped_flags":[],"needs_not_parents":[]}],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"bots.PreviewInfo","predicate":"bots.previewInfo","params":[{"_":"traverseParam","name":"media","is_flag":false,"is_vector":true,"type":"BotPreviewMedia"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"BotPreviewMedia","predicate":"botPreviewMedia","params":[{"_":"traverseParam","name":"media","is_flag":false,"is_vector":false,"type":"MessageMedia"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"bots.addPreviewMedia","push_sources":[{"_":"source","predicate":{"_":"predicate","predicate":"bots.addPreviewMedia","is_constructor":false},"stored_constructor":"fileSourceBotPreviewInfo","stored_params":[{"_":"extractUserIdFromInputUserAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"BotPreviewMedia","constructor":"bots.addPreviewMedia","param":"bot","param_type":"InputUser","flag":{"_":"paramNotFlag"}}]},"to":"bot"},{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"BotPreviewMedia","constructor":"bots.addPreviewMedia","param":"lang_code","param_type":"string","flag":{"_":"paramNotFlag"}}]},"to":"lang_code"}],"skipped_flags":[],"needs_not_parents":[]}],"is_needed_parent":false},{"_":"traverseMethodResult","name":"bots.editPreviewMedia","push_sources":[{"_":"source","predicate":{"_":"predicate","predicate":"bots.editPreviewMedia","is_constructor":false},"stored_constructor":"fileSourceBotPreviewInfo","stored_params":[{"_":"extractUserIdFromInputUserAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"BotPreviewMedia","constructor":"bots.editPreviewMedia","param":"bot","param_type":"InputUser","flag":{"_":"paramNotFlag"}}]},"to":"bot"},{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"BotPreviewMedia","constructor":"bots.editPreviewMedia","param":"lang_code","param_type":"string","flag":{"_":"paramNotFlag"}}]},"to":"lang_code"}],"skipped_flags":[],"needs_not_parents":[]}],"is_needed_parent":false},{"_":"traverseMethodResult","name":"bots.getPreviewMedias","push_sources":[{"_":"source","predicate":{"_":"predicate","predicate":"bots.getPreviewMedias","is_constructor":false},"stored_constructor":"fileSourceBotPreviewMedia","stored_params":[{"_":"extractUserIdFromInputUserAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"Vector t","constructor":"bots.getPreviewMedias","param":"bot","param_type":"InputUser","flag":{"_":"paramNotFlag"}}]},"to":"bot"}],"skipped_flags":[],"needs_not_parents":[]}],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.getWebPagePreview","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"messages.WebPagePreview","predicate":"messages.webPagePreview","params":[{"_":"traverseParam","name":"media","is_flag":false,"is_vector":false,"type":"MessageMedia"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.uploadMedia","push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.uploadImportedMedia","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"MessageMedia","predicate":"messageMediaInvoice","params":[{"_":"traverseParam","name":"extended_media","is_flag":true,"is_vector":false,"type":"MessageExtendedMedia"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"MessageMedia","predicate":"messageMediaPaidMedia","params":[{"_":"traverseParam","name":"extended_media","is_flag":false,"is_vector":true,"type":"MessageExtendedMedia"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"MessageMedia","predicate":"messageMediaStory","params":[{"_":"traverseParam","name":"story","is_flag":true,"is_vector":false,"type":"StoryItem"}],"push_sources":[{"_":"source","predicate":{"_":"predicate","predicate":"messageMediaStory","is_constructor":true},"stored_constructor":"fileSourceStory","stored_params":[{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"MessageMedia","constructor":"messageMediaStory","param":"story","param_type":"StoryItem","flag":{"_":"paramIsFlagAbortIfEmpty"}},{"_":"pathPart","type":"StoryItem","constructor":"storyItem","param":"id","param_type":"int","flag":{"_":"paramNotFlag"}}]},"to":"id"},{"_":"extractPeerIdFromPeerAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"MessageMedia","constructor":"messageMediaStory","param":"peer","param_type":"Peer","flag":{"_":"paramNotFlag"}}]},"to":"peer"}],"skipped_flags":[],"needs_not_parents":[]}],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"MessageMedia","predicate":"messageMediaWebPage","params":[{"_":"traverseParam","name":"webpage","is_flag":false,"is_vector":false,"type":"WebPage"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"MessageMedia","predicate":"messageMediaDocument","params":[{"_":"traverseParam","name":"document","is_flag":true,"is_vector":false,"type":"Document"},{"_":"traverseParam","name":"alt_documents","is_flag":true,"is_vector":true,"type":"Document"},{"_":"traverseParam","name":"video_cover","is_flag":true,"is_vector":false,"type":"Photo"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"WallPaper","predicate":"wallPaper","params":[{"_":"traverseParam","name":"document","is_flag":false,"is_vector":false,"type":"Document"}],"push_sources":[{"_":"source","predicate":{"_":"predicate","predicate":"wallPaper","is_constructor":true},"stored_constructor":"fileSourceWallPaper","stored_params":[{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"WallPaper","constructor":"wallPaper","param":"id","param_type":"long","flag":{"_":"paramNotFlag"}}]},"to":"id"},{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"WallPaper","constructor":"wallPaper","param":"access_hash","param_type":"long","flag":{"_":"paramNotFlag"}}]},"to":"access_hash"}],"skipped_flags":[],"needs_not_parents":[]}],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"MessageAction","predicate":"messageActionSetChatWallPaper","params":[{"_":"traverseParam","name":"wallpaper","is_flag":false,"is_vector":false,"type":"WallPaper"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"Update","predicate":"updatePeerWallpaper","params":[{"_":"traverseParam","name":"wallpaper","is_flag":true,"is_vector":false,"type":"WallPaper"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"ChannelAdminLogEventAction","predicate":"channelAdminLogEventActionChangeWallpaper","params":[{"_":"traverseParam","name":"prev_value","is_flag":false,"is_vector":false,"type":"WallPaper"},{"_":"traverseParam","name":"new_value","is_flag":false,"is_vector":false,"type":"WallPaper"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"account.getWallPapers","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"account.WallPapers","predicate":"account.wallPapers","params":[{"_":"traverseParam","name":"wallpapers","is_flag":false,"is_vector":true,"type":"WallPaper"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"Update","predicate":"updateTheme","params":[{"_":"traverseParam","name":"theme","is_flag":false,"is_vector":false,"type":"Theme"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"Theme","predicate":"theme","params":[{"_":"traverseParam","name":"document","is_flag":true,"is_vector":false,"type":"Document"},{"_":"traverseParam","name":"settings","is_flag":true,"is_vector":true,"type":"ThemeSettings"}],"push_sources":[{"_":"source","predicate":{"_":"predicate","predicate":"theme","is_constructor":true},"stored_constructor":"fileSourceTheme","stored_params":[{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"Theme","constructor":"theme","param":"id","param_type":"long","flag":{"_":"paramNotFlag"}}]},"to":"id"},{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"Theme","constructor":"theme","param":"access_hash","param_type":"long","flag":{"_":"paramNotFlag"}}]},"to":"access_hash"}],"skipped_flags":[],"needs_not_parents":[]}],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"ThemeSettings","predicate":"themeSettings","params":[{"_":"traverseParam","name":"wallpaper","is_flag":true,"is_vector":false,"type":"WallPaper"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"account.getThemes","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"account.Themes","predicate":"account.themes","params":[{"_":"traverseParam","name":"themes","is_flag":false,"is_vector":true,"type":"Theme"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"account.getChatThemes","push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"account.createTheme","push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"account.updateTheme","push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"account.getTheme","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"WebPageAttribute","predicate":"webPageAttributeTheme","params":[{"_":"traverseParam","name":"documents","is_flag":true,"is_vector":true,"type":"Document"},{"_":"traverseParam","name":"settings","is_flag":true,"is_vector":false,"type":"ThemeSettings"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"MessageAction","predicate":"messageActionSetChatTheme","params":[{"_":"traverseParam","name":"theme","is_flag":false,"is_vector":false,"type":"ChatTheme"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"ChatTheme","predicate":"chatThemeUniqueGift","params":[{"_":"traverseParam","name":"gift","is_flag":false,"is_vector":false,"type":"StarGift"},{"_":"traverseParam","name":"theme_settings","is_flag":false,"is_vector":true,"type":"ThemeSettings"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"account.getUniqueGiftChatThemes","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"account.ChatThemes","predicate":"account.chatThemes","params":[{"_":"traverseParam","name":"themes","is_flag":false,"is_vector":true,"type":"ChatTheme"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"account.getWallPaper","push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"account.uploadWallPaper","push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"account.getMultiWallPapers","push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"help.getAppUpdate","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"help.AppUpdate","predicate":"help.appUpdate","params":[{"_":"traverseParam","name":"document","is_flag":true,"is_vector":false,"type":"Document"},{"_":"traverseParam","name":"sticker","is_flag":true,"is_vector":false,"type":"Document"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.getStickers","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"messages.Stickers","predicate":"messages.stickers","params":[{"_":"traverseParam","name":"stickers","is_flag":false,"is_vector":true,"type":"Document"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"Update","predicate":"updateNewStickerSet","params":[{"_":"traverseParam","name":"stickerset","is_flag":false,"is_vector":false,"type":"messages.StickerSet"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"messages.StickerSet","predicate":"messages.stickerSet","params":[{"_":"traverseParam","name":"documents","is_flag":false,"is_vector":true,"type":"Document"}],"push_sources":[{"_":"source","predicate":{"_":"predicate","predicate":"messages.stickerSet","is_constructor":true},"stored_constructor":"fileSourceStickerSet","stored_params":[{"_":"extractInputStickerSetFromStickerSetAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"messages.StickerSet","constructor":"messages.stickerSet","param":"set","param_type":"StickerSet","flag":{"_":"paramNotFlag"}}]},"to":"stickerset"}],"skipped_flags":[],"needs_not_parents":[]}],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.getStickerSet","push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"stickers.createStickerSet","push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"stickers.removeStickerFromSet","push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"stickers.changeStickerPosition","push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"stickers.addStickerToSet","push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"stickers.setStickerSetThumb","push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"stickers.changeSticker","push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"stickers.renameStickerSet","push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"stickers.replaceSticker","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"ChatFull","predicate":"chatFull","params":[{"_":"traverseParam","name":"chat_photo","is_flag":true,"is_vector":false,"type":"Photo"},{"_":"traverseParam","name":"bot_info","is_flag":true,"is_vector":true,"type":"BotInfo"}],"push_sources":[{"_":"source","predicate":{"_":"predicate","predicate":"chatFull","is_constructor":true},"stored_constructor":"fileSourceChatFull","stored_params":[{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"ChatFull","constructor":"chatFull","param":"id","param_type":"long","flag":{"_":"paramNotFlag"}}]},"to":"chat_id"}],"skipped_flags":[],"needs_not_parents":[]}],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"BotInfo","predicate":"botInfo","params":[{"_":"traverseParam","name":"description_photo","is_flag":true,"is_vector":false,"type":"Photo"},{"_":"traverseParam","name":"description_document","is_flag":true,"is_vector":false,"type":"Document"}],"push_sources":[{"_":"source","predicate":{"_":"predicate","predicate":"botInfo","is_constructor":true},"stored_constructor":"fileSourceUserFull","stored_params":[{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"BotInfo","constructor":"botInfo","param":"user_id","param_type":"long","flag":{"_":"paramIsFlagAbortIfEmpty"}}]},"to":"id"}],"skipped_flags":[],"needs_not_parents":[]}],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.getSavedGifs","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"messages.SavedGifs","predicate":"messages.savedGifs","params":[{"_":"traverseParam","name":"gifs","is_flag":false,"is_vector":true,"type":"Document"}],"push_sources":[{"_":"source","predicate":{"_":"predicate","predicate":"messages.savedGifs","is_constructor":true},"stored_constructor":"fileSourceSavedGifs","stored_params":[],"skipped_flags":[],"needs_not_parents":[]}],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.getInlineBotResults","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"messages.BotResults","predicate":"messages.botResults","params":[{"_":"traverseParam","name":"results","is_flag":false,"is_vector":true,"type":"BotInlineResult"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"BotInlineResult","predicate":"botInlineMediaResult","params":[{"_":"traverseParam","name":"document","is_flag":true,"is_vector":false,"type":"Document"},{"_":"traverseParam","name":"send_message","is_flag":false,"is_vector":false,"type":"BotInlineMessage"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.getPreparedInlineMessage","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"messages.PreparedInlineMessage","predicate":"messages.preparedInlineMessage","params":[{"_":"traverseParam","name":"result","is_flag":false,"is_vector":false,"type":"BotInlineResult"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.getRecentStickers","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"messages.RecentStickers","predicate":"messages.recentStickers","params":[{"_":"traverseParam","name":"stickers","is_flag":false,"is_vector":true,"type":"Document"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.getFeaturedStickers","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"messages.FeaturedStickers","predicate":"messages.featuredStickers","params":[{"_":"traverseParam","name":"sets","is_flag":false,"is_vector":true,"type":"StickerSetCovered"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"StickerSetCovered","predicate":"stickerSetCovered","params":[{"_":"traverseParam","name":"cover","is_flag":false,"is_vector":false,"type":"Document"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.getOldFeaturedStickers","push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.getFeaturedEmojiStickers","push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.getArchivedStickers","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"messages.ArchivedStickers","predicate":"messages.archivedStickers","params":[{"_":"traverseParam","name":"sets","is_flag":false,"is_vector":true,"type":"StickerSetCovered"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.installStickerSet","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"messages.StickerSetInstallResult","predicate":"messages.stickerSetInstallResultArchive","params":[{"_":"traverseParam","name":"sets","is_flag":false,"is_vector":true,"type":"StickerSetCovered"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"help.getRecentMeUrls","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"help.RecentMeUrls","predicate":"help.recentMeUrls","params":[{"_":"traverseParam","name":"urls","is_flag":false,"is_vector":true,"type":"RecentMeUrl"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"RecentMeUrl","predicate":"recentMeUrlStickerSet","params":[{"_":"traverseParam","name":"set","is_flag":false,"is_vector":false,"type":"StickerSetCovered"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.searchStickerSets","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"messages.FoundStickerSets","predicate":"messages.foundStickerSets","params":[{"_":"traverseParam","name":"sets","is_flag":false,"is_vector":true,"type":"StickerSetCovered"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.searchEmojiStickerSets","push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.getMyStickers","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"messages.MyStickers","predicate":"messages.myStickers","params":[{"_":"traverseParam","name":"sets","is_flag":false,"is_vector":true,"type":"StickerSetCovered"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.getAttachedStickers","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"StickerSetCovered","predicate":"stickerSetMultiCovered","params":[{"_":"traverseParam","name":"covers","is_flag":false,"is_vector":true,"type":"Document"}],"push_sources":[{"_":"source","predicate":{"_":"predicate","predicate":"stickerSetMultiCovered","is_constructor":true},"stored_constructor":"fileSourceStickerSet","stored_params":[{"_":"extractInputStickerSetFromStickerSetAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"StickerSetCovered","constructor":"stickerSetMultiCovered","param":"set","param_type":"StickerSet","flag":{"_":"paramNotFlag"}}]},"to":"stickerset"}],"skipped_flags":[],"needs_not_parents":[]}],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"StickerSetCovered","predicate":"stickerSetFullCovered","params":[{"_":"traverseParam","name":"documents","is_flag":false,"is_vector":true,"type":"Document"}],"push_sources":[{"_":"source","predicate":{"_":"predicate","predicate":"stickerSetFullCovered","is_constructor":true},"stored_constructor":"fileSourceStickerSet","stored_params":[{"_":"extractInputStickerSetFromStickerSetAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"StickerSetCovered","constructor":"stickerSetFullCovered","param":"set","param_type":"StickerSet","flag":{"_":"paramNotFlag"}}]},"to":"stickerset"}],"skipped_flags":[],"needs_not_parents":[]}],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"MessageMedia","predicate":"messageMediaGame","params":[{"_":"traverseParam","name":"game","is_flag":false,"is_vector":false,"type":"Game"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"Game","predicate":"game","params":[{"_":"traverseParam","name":"photo","is_flag":false,"is_vector":false,"type":"Photo"},{"_":"traverseParam","name":"document","is_flag":true,"is_vector":false,"type":"Document"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.getFavedStickers","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"messages.FavedStickers","predicate":"messages.favedStickers","params":[{"_":"traverseParam","name":"stickers","is_flag":false,"is_vector":true,"type":"Document"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"Page","predicate":"page","params":[{"_":"traverseParam","name":"photos","is_flag":false,"is_vector":true,"type":"Photo"},{"_":"traverseParam","name":"documents","is_flag":false,"is_vector":true,"type":"Document"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"WebPageAttribute","predicate":"webPageAttributeStickerSet","params":[{"_":"traverseParam","name":"stickers","is_flag":false,"is_vector":true,"type":"Document"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"WebPageAttribute","predicate":"webPageAttributeStarGiftCollection","params":[{"_":"traverseParam","name":"icons","is_flag":false,"is_vector":true,"type":"Document"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.getAvailableReactions","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"messages.AvailableReactions","predicate":"messages.availableReactions","params":[{"_":"traverseParam","name":"reactions","is_flag":false,"is_vector":true,"type":"AvailableReaction"}],"push_sources":[{"_":"source","predicate":{"_":"predicate","predicate":"messages.availableReactions","is_constructor":true},"stored_constructor":"fileSourceAvailableReactions","stored_params":[],"skipped_flags":[],"needs_not_parents":[]}],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"AvailableReaction","predicate":"availableReaction","params":[{"_":"traverseParam","name":"static_icon","is_flag":false,"is_vector":false,"type":"Document"},{"_":"traverseParam","name":"appear_animation","is_flag":false,"is_vector":false,"type":"Document"},{"_":"traverseParam","name":"select_animation","is_flag":false,"is_vector":false,"type":"Document"},{"_":"traverseParam","name":"activate_animation","is_flag":false,"is_vector":false,"type":"Document"},{"_":"traverseParam","name":"effect_animation","is_flag":false,"is_vector":false,"type":"Document"},{"_":"traverseParam","name":"around_animation","is_flag":true,"is_vector":false,"type":"Document"},{"_":"traverseParam","name":"center_icon","is_flag":true,"is_vector":false,"type":"Document"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.getAttachMenuBots","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"AttachMenuBots","predicate":"attachMenuBots","params":[{"_":"traverseParam","name":"bots","is_flag":false,"is_vector":true,"type":"AttachMenuBot"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"AttachMenuBot","predicate":"attachMenuBot","params":[{"_":"traverseParam","name":"icons","is_flag":false,"is_vector":true,"type":"AttachMenuBotIcon"}],"push_sources":[{"_":"source","predicate":{"_":"predicate","predicate":"attachMenuBot","is_constructor":true},"stored_constructor":"fileSourceAttachMenuBot","stored_params":[{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"AttachMenuBot","constructor":"attachMenuBot","param":"bot_id","param_type":"long","flag":{"_":"paramNotFlag"}}]},"to":"bot"}],"skipped_flags":[],"needs_not_parents":[]}],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"AttachMenuBotIcon","predicate":"attachMenuBotIcon","params":[{"_":"traverseParam","name":"icon","is_flag":false,"is_vector":false,"type":"Document"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.getAttachMenuBot","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"AttachMenuBotsBot","predicate":"attachMenuBotsBot","params":[{"_":"traverseParam","name":"bot","is_flag":false,"is_vector":false,"type":"AttachMenuBot"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"account.getSavedRingtones","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"account.SavedRingtones","predicate":"account.savedRingtones","params":[{"_":"traverseParam","name":"ringtones","is_flag":false,"is_vector":true,"type":"Document"}],"push_sources":[{"_":"source","predicate":{"_":"predicate","predicate":"account.savedRingtones","is_constructor":true},"stored_constructor":"fileSourceSavedRingtones","stored_params":[],"skipped_flags":[],"needs_not_parents":[]}],"is_needed_parent":false},{"_":"traverseMethodResult","name":"account.saveRingtone","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"account.SavedRingtone","predicate":"account.savedRingtoneConverted","params":[{"_":"traverseParam","name":"document","is_flag":false,"is_vector":false,"type":"Document"}],"push_sources":[{"_":"source","predicate":{"_":"predicate","predicate":"account.savedRingtoneConverted","is_constructor":true},"stored_constructor":"fileSourceSavedRingtones","stored_params":[],"skipped_flags":[],"needs_not_parents":[]}],"is_needed_parent":false},{"_":"traverseMethodResult","name":"help.getPremiumPromo","push_sources":[{"_":"source","predicate":{"_":"predicate","predicate":"help.getPremiumPromo","is_constructor":false},"stored_constructor":"fileSourcePremiumPromo","stored_params":[],"skipped_flags":[],"needs_not_parents":[]}],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"help.PremiumPromo","predicate":"help.premiumPromo","params":[{"_":"traverseParam","name":"videos","is_flag":false,"is_vector":true,"type":"Document"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"MessageAction","predicate":"messageActionBotAllowed","params":[{"_":"traverseParam","name":"app","is_flag":true,"is_vector":false,"type":"BotApp"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"BotApp","predicate":"botApp","params":[{"_":"traverseParam","name":"photo","is_flag":false,"is_vector":false,"type":"Photo"},{"_":"traverseParam","name":"document","is_flag":true,"is_vector":false,"type":"Document"}],"push_sources":[{"_":"source","predicate":{"_":"predicate","predicate":"botApp","is_constructor":true},"stored_constructor":"fileSourceBotApp","stored_params":[{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"BotApp","constructor":"botApp","param":"id","param_type":"long","flag":{"_":"paramNotFlag"}}]},"to":"id"},{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"BotApp","constructor":"botApp","param":"access_hash","param_type":"long","flag":{"_":"paramNotFlag"}}]},"to":"access_hash"}],"skipped_flags":[],"needs_not_parents":[]}],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.getBotApp","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"messages.BotApp","predicate":"messages.botApp","params":[{"_":"traverseParam","name":"app","is_flag":false,"is_vector":false,"type":"BotApp"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"BusinessIntro","predicate":"businessIntro","params":[{"_":"traverseParam","name":"sticker","is_flag":true,"is_vector":false,"type":"Document"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.getAvailableEffects","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"messages.AvailableEffects","predicate":"messages.availableEffects","params":[{"_":"traverseParam","name":"documents","is_flag":false,"is_vector":true,"type":"Document"}],"push_sources":[{"_":"source","predicate":{"_":"predicate","predicate":"messages.availableEffects","is_constructor":true},"stored_constructor":"fileSourceAvailableEffects","stored_params":[],"skipped_flags":[],"needs_not_parents":[]}],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"MessageAction","predicate":"messageActionStarGift","params":[{"_":"traverseParam","name":"gift","is_flag":false,"is_vector":false,"type":"StarGift"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"StarGift","predicate":"starGift","params":[{"_":"traverseParam","name":"sticker","is_flag":false,"is_vector":false,"type":"Document"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"MessageAction","predicate":"messageActionStarGiftUnique","params":[{"_":"traverseParam","name":"gift","is_flag":false,"is_vector":false,"type":"StarGift"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"MessageAction","predicate":"messageActionStarGiftPurchaseOffer","params":[{"_":"traverseParam","name":"gift","is_flag":false,"is_vector":false,"type":"StarGift"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"MessageAction","predicate":"messageActionStarGiftPurchaseOfferDeclined","params":[{"_":"traverseParam","name":"gift","is_flag":false,"is_vector":false,"type":"StarGift"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"WebPageAttribute","predicate":"webPageAttributeUniqueStarGift","params":[{"_":"traverseParam","name":"gift","is_flag":false,"is_vector":false,"type":"StarGift"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"WebPageAttribute","predicate":"webPageAttributeStarGiftAuction","params":[{"_":"traverseParam","name":"gift","is_flag":false,"is_vector":false,"type":"StarGift"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"payments.getStarGifts","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"payments.StarGifts","predicate":"payments.starGifts","params":[{"_":"traverseParam","name":"gifts","is_flag":false,"is_vector":true,"type":"StarGift"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"payments.getUniqueStarGift","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"payments.UniqueStarGift","predicate":"payments.uniqueStarGift","params":[{"_":"traverseParam","name":"gift","is_flag":false,"is_vector":false,"type":"StarGift"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"payments.getSavedStarGifts","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"payments.SavedStarGifts","predicate":"payments.savedStarGifts","params":[{"_":"traverseParam","name":"gifts","is_flag":false,"is_vector":true,"type":"SavedStarGift"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"SavedStarGift","predicate":"savedStarGift","params":[{"_":"traverseParam","name":"gift","is_flag":false,"is_vector":false,"type":"StarGift"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"payments.getSavedStarGift","push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"payments.getCraftStarGifts","push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"payments.getResaleStarGifts","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"payments.ResaleStarGifts","predicate":"payments.resaleStarGifts","params":[{"_":"traverseParam","name":"gifts","is_flag":false,"is_vector":true,"type":"StarGift"},{"_":"traverseParam","name":"attributes","is_flag":true,"is_vector":true,"type":"StarGiftAttribute"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"payments.getStarGiftAuctionState","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"payments.StarGiftAuctionState","predicate":"payments.starGiftAuctionState","params":[{"_":"traverseParam","name":"gift","is_flag":false,"is_vector":false,"type":"StarGift"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"payments.getStarGiftActiveAuctions","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"payments.StarGiftActiveAuctions","predicate":"payments.starGiftActiveAuctions","params":[{"_":"traverseParam","name":"auctions","is_flag":false,"is_vector":true,"type":"StarGiftActiveAuctionState"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"StarGiftActiveAuctionState","predicate":"starGiftActiveAuctionState","params":[{"_":"traverseParam","name":"gift","is_flag":false,"is_vector":false,"type":"StarGift"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"Update","predicate":"updateSentWalletTransaction","params":[{"_":"traverseParam","name":"transaction","is_flag":true,"is_vector":false,"type":"WalletTransaction"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"WalletTransaction","predicate":"walletTransaction","params":[{"_":"traverseParam","name":"nft","is_flag":true,"is_vector":false,"type":"wallet.NftItem"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"wallet.NftItem","predicate":"wallet.nftItem","params":[{"_":"traverseParam","name":"gift","is_flag":true,"is_vector":false,"type":"StarGift"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"wallet.getTransactions","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"wallet.Transactions","predicate":"wallet.transactions","params":[{"_":"traverseParam","name":"transactions","is_flag":false,"is_vector":true,"type":"WalletTransaction"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"wallet.getTransactionsByIDs","push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"wallet.getTransactionsByMsgHash","push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"wallet.getNfts","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"wallet.NftItems","predicate":"wallet.nftItems","params":[{"_":"traverseParam","name":"items","is_flag":false,"is_vector":true,"type":"wallet.NftItem"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"wallet.Card","predicate":"wallet.card","params":[{"_":"traverseParam","name":"stickers","is_flag":true,"is_vector":true,"type":"wallet.CardSticker"},{"_":"traverseParam","name":"bg_nft_item","is_flag":true,"is_vector":false,"type":"wallet.NftItem"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"wallet.CardSticker","predicate":"wallet.cardSticker","params":[{"_":"traverseParam","name":"nft","is_flag":false,"is_vector":false,"type":"wallet.NftItem"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"wallet.setCardStickers","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"ShareTheme","predicate":"shareThemeGift","params":[{"_":"traverseParam","name":"stargift","is_flag":false,"is_vector":false,"type":"StarGift"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.searchStickers","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"messages.FoundStickers","predicate":"messages.foundStickers","params":[{"_":"traverseParam","name":"stickers","is_flag":false,"is_vector":true,"type":"Document"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"StarGift","predicate":"starGiftUnique","params":[{"_":"traverseParam","name":"attributes","is_flag":false,"is_vector":true,"type":"StarGiftAttribute"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"StarGiftAttribute","predicate":"starGiftAttributeModel","params":[{"_":"traverseParam","name":"document","is_flag":false,"is_vector":false,"type":"Document"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"payments.getStarGiftUpgradePreview","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"payments.StarGiftUpgradePreview","predicate":"payments.starGiftUpgradePreview","params":[{"_":"traverseParam","name":"sample_attributes","is_flag":false,"is_vector":true,"type":"StarGiftAttribute"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"payments.getStarGiftUpgradeAttributes","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"payments.StarGiftUpgradeAttributes","predicate":"payments.starGiftUpgradeAttributes","params":[{"_":"traverseParam","name":"attributes","is_flag":false,"is_vector":true,"type":"StarGiftAttribute"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"StarGiftAttribute","predicate":"starGiftAttributePattern","params":[{"_":"traverseParam","name":"document","is_flag":false,"is_vector":false,"type":"Document"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"payments.getStarGiftCollections","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"payments.StarGiftCollections","predicate":"payments.starGiftCollections","params":[{"_":"traverseParam","name":"collections","is_flag":false,"is_vector":true,"type":"StarGiftCollection"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"StarGiftCollection","predicate":"starGiftCollection","params":[{"_":"traverseParam","name":"icon","is_flag":true,"is_vector":false,"type":"Document"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"payments.createStarGiftCollection","push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"payments.updateStarGiftCollection","push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"stories.getAlbums","push_sources":[{"_":"source","predicate":{"_":"predicate","predicate":"stories.getAlbums","is_constructor":false},"stored_constructor":"fileSourceStoryAlbum","stored_params":[{"_":"extractPeerIdFromInputPeerAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"stories.Albums","constructor":"stories.getAlbums","param":"peer","param_type":"InputPeer","flag":{"_":"paramNotFlag"}}]},"to":"peer"}],"skipped_flags":[],"needs_not_parents":[]}],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"stories.Albums","predicate":"stories.albums","params":[{"_":"traverseParam","name":"albums","is_flag":false,"is_vector":true,"type":"StoryAlbum"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"StoryAlbum","predicate":"storyAlbum","params":[{"_":"traverseParam","name":"icon_photo","is_flag":true,"is_vector":false,"type":"Photo"},{"_":"traverseParam","name":"icon_video","is_flag":true,"is_vector":false,"type":"Document"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"stories.createAlbum","push_sources":[{"_":"source","predicate":{"_":"predicate","predicate":"stories.createAlbum","is_constructor":false},"stored_constructor":"fileSourceStoryAlbum","stored_params":[{"_":"extractPeerIdFromInputPeerAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"StoryAlbum","constructor":"stories.createAlbum","param":"peer","param_type":"InputPeer","flag":{"_":"paramNotFlag"}}]},"to":"peer"}],"skipped_flags":[],"needs_not_parents":[]}],"is_needed_parent":false},{"_":"traverseMethodResult","name":"stories.updateAlbum","push_sources":[{"_":"source","predicate":{"_":"predicate","predicate":"stories.updateAlbum","is_constructor":false},"stored_constructor":"fileSourceStoryAlbum","stored_params":[{"_":"extractPeerIdFromInputPeerAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"StoryAlbum","constructor":"stories.updateAlbum","param":"peer","param_type":"InputPeer","flag":{"_":"paramNotFlag"}}]},"to":"peer"}],"skipped_flags":[],"needs_not_parents":[]}],"is_needed_parent":false},{"_":"traverseMethodResult","name":"users.getSavedMusic","push_sources":[],"is_needed_parent":true},{"_":"traverseIncomingConstructor","type":"users.SavedMusic","predicate":"users.savedMusic","params":[{"_":"traverseParam","name":"documents","is_flag":false,"is_vector":true,"type":"Document"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"users.getSavedMusicByID","push_sources":[],"is_needed_parent":true},{"_":"traverseIncomingConstructor","type":"RichMessage","predicate":"richMessage","params":[{"_":"traverseParam","name":"photos","is_flag":false,"is_vector":true,"type":"Photo"},{"_":"traverseParam","name":"documents","is_flag":false,"is_vector":true,"type":"Document"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"Update","predicate":"updateUserTyping","params":[{"_":"traverseParam","name":"action","is_flag":false,"is_vector":false,"type":"SendMessageAction"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"SendMessageAction","predicate":"sendMessageRichMessageDraftAction","params":[{"_":"traverseParam","name":"rich_message","is_flag":false,"is_vector":false,"type":"RichMessage"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"Update","predicate":"updateChatUserTyping","params":[{"_":"traverseParam","name":"action","is_flag":false,"is_vector":false,"type":"SendMessageAction"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"Update","predicate":"updateChannelUserTyping","params":[{"_":"traverseParam","name":"action","is_flag":false,"is_vector":false,"type":"SendMessageAction"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"BotInlineResult","predicate":"botInlineResult","params":[{"_":"traverseParam","name":"send_message","is_flag":false,"is_vector":false,"type":"BotInlineMessage"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"BotInlineMessage","predicate":"botInlineMessageRichMessage","params":[{"_":"traverseParam","name":"rich_message","is_flag":false,"is_vector":false,"type":"RichMessage"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"Dialog","predicate":"dialog","params":[{"_":"traverseParam","name":"draft","is_flag":true,"is_vector":false,"type":"DraftMessage"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"DraftMessage","predicate":"draftMessage","params":[{"_":"traverseParam","name":"rich_message","is_flag":true,"is_vector":false,"type":"RichMessage"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"Update","predicate":"updateDraftMessage","params":[{"_":"traverseParam","name":"draft","is_flag":false,"is_vector":false,"type":"DraftMessage"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"ForumTopic","predicate":"forumTopic","params":[{"_":"traverseParam","name":"draft","is_flag":true,"is_vector":false,"type":"DraftMessage"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"ChannelAdminLogEventAction","predicate":"channelAdminLogEventActionCreateTopic","params":[{"_":"traverseParam","name":"topic","is_flag":false,"is_vector":false,"type":"ForumTopic"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"ChannelAdminLogEventAction","predicate":"channelAdminLogEventActionEditTopic","params":[{"_":"traverseParam","name":"prev_topic","is_flag":false,"is_vector":false,"type":"ForumTopic"},{"_":"traverseParam","name":"new_topic","is_flag":false,"is_vector":false,"type":"ForumTopic"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"ChannelAdminLogEventAction","predicate":"channelAdminLogEventActionDeleteTopic","params":[{"_":"traverseParam","name":"topic","is_flag":false,"is_vector":false,"type":"ForumTopic"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"ChannelAdminLogEventAction","predicate":"channelAdminLogEventActionPinTopic","params":[{"_":"traverseParam","name":"prev_topic","is_flag":true,"is_vector":false,"type":"ForumTopic"},{"_":"traverseParam","name":"new_topic","is_flag":true,"is_vector":false,"type":"ForumTopic"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"SavedDialog","predicate":"monoForumDialog","params":[{"_":"traverseParam","name":"draft","is_flag":true,"is_vector":false,"type":"DraftMessage"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.translateRichMessage","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"messages.TranslatedRichMessage","predicate":"messages.translatedRichMessage","params":[{"_":"traverseParam","name":"result","is_flag":false,"is_vector":true,"type":"RichMessage"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.composeRichMessageWithAI","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"messages.ComposedRichMessageWithAI","predicate":"messages.composedRichMessageWithAI","params":[{"_":"traverseParam","name":"result","is_flag":false,"is_vector":false,"type":"RichMessage"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"account.uploadTheme","push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"account.uploadRingtone","push_sources":[{"_":"source","predicate":{"_":"predicate","predicate":"account.uploadRingtone","is_constructor":false},"stored_constructor":"fileSourceSavedRingtones","stored_params":[],"skipped_flags":[],"needs_not_parents":[]}],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.getDocumentByHash","push_sources":[{"_":"source","predicate":{"_":"predicate","predicate":"messages.getDocumentByHash","is_constructor":false},"stored_constructor":"fileSourceDocumentByHash","stored_params":[{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"Document","constructor":"messages.getDocumentByHash","param":"sha256","param_type":"bytes","flag":{"_":"paramNotFlag"}}]},"to":"sha256"},{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"Document","constructor":"messages.getDocumentByHash","param":"size","param_type":"long","flag":{"_":"paramNotFlag"}}]},"to":"size"},{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"Document","constructor":"messages.getDocumentByHash","param":"mime_type","param_type":"string","flag":{"_":"paramNotFlag"}}]},"to":"mime_type"}],"skipped_flags":[],"needs_not_parents":[]}],"is_needed_parent":false},{"_":"traverseMethodResult","name":"messages.getCustomEmojiDocuments","push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"ChatFull","predicate":"communityFull","params":[{"_":"traverseParam","name":"chat_photo","is_flag":false,"is_vector":false,"type":"Photo"}],"push_sources":[{"_":"source","predicate":{"_":"predicate","predicate":"communityFull","is_constructor":true},"stored_constructor":"fileSourceCommunityFull","stored_params":[{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"ChatFull","constructor":"communityFull","param":"id","param_type":"long","flag":{"_":"paramNotFlag"}}]},"to":"channel"}],"skipped_flags":[],"needs_not_parents":[]}],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"MessageAction","predicate":"messageActionChatEditPhoto","params":[{"_":"traverseParam","name":"photo","is_flag":false,"is_vector":false,"type":"Photo"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"MessageAction","predicate":"messageActionSuggestProfilePhoto","params":[{"_":"traverseParam","name":"photo","is_flag":false,"is_vector":false,"type":"Photo"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"photos.getUserPhotos","push_sources":[],"is_needed_parent":true},{"_":"traverseIncomingConstructor","type":"photos.Photos","predicate":"photos.photos","params":[{"_":"traverseParam","name":"photos","is_flag":false,"is_vector":true,"type":"Photo"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"photos.Photos","predicate":"photos.photosSlice","params":[{"_":"traverseParam","name":"photos","is_flag":false,"is_vector":true,"type":"Photo"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"photos.updateProfilePhoto","push_sources":[{"_":"source","predicate":{"_":"predicate","predicate":"photos.updateProfilePhoto","is_constructor":false},"stored_constructor":"fileSourceUserProfilePhoto","stored_params":[{"_":"extractUserIdFromInputUserAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"photos.Photo","constructor":"photos.updateProfilePhoto","param":"bot","param_type":"InputUser","flag":{"_":"paramIsFlagFallback","fallback":{"_":"typedOp","type":"InputUser","op":{"_":"constructorOp","constructor":"inputUserSelf","args":[]}}}}]},"to":"user_id"},{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"photos.Photo","constructor":"photos.updateProfilePhoto","param":"","param_type":"photos.Photo","flag":{"_":"paramNotFlag"}},{"_":"pathPart","type":"photos.Photo","constructor":"photos.photo","param":"photo","param_type":"Photo","flag":{"_":"paramNotFlag"}},{"_":"pathPart","type":"Photo","constructor":"photo","param":"id","param_type":"long","flag":{"_":"paramNotFlag"}}]},"to":"max_id"}],"skipped_flags":[],"needs_not_parents":[]}],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"photos.Photo","predicate":"photos.photo","params":[{"_":"traverseParam","name":"photo","is_flag":false,"is_vector":false,"type":"Photo"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseMethodResult","name":"photos.uploadProfilePhoto","push_sources":[{"_":"source","predicate":{"_":"predicate","predicate":"photos.uploadProfilePhoto","is_constructor":false},"stored_constructor":"fileSourceUserProfilePhoto","stored_params":[{"_":"extractUserIdFromInputUserAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"photos.Photo","constructor":"photos.uploadProfilePhoto","param":"bot","param_type":"InputUser","flag":{"_":"paramIsFlagFallback","fallback":{"_":"typedOp","type":"InputUser","op":{"_":"constructorOp","constructor":"inputUserSelf","args":[]}}}}]},"to":"user_id"},{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"photos.Photo","constructor":"photos.uploadProfilePhoto","param":"","param_type":"photos.Photo","flag":{"_":"paramNotFlag"}},{"_":"pathPart","type":"photos.Photo","constructor":"photos.photo","param":"photo","param_type":"Photo","flag":{"_":"paramNotFlag"}},{"_":"pathPart","type":"Photo","constructor":"photo","param":"id","param_type":"long","flag":{"_":"paramNotFlag"}}]},"to":"max_id"}],"skipped_flags":[],"needs_not_parents":[]}],"is_needed_parent":false},{"_":"traverseMethodResult","name":"photos.uploadContactProfilePhoto","push_sources":[{"_":"source","predicate":{"_":"predicate","predicate":"photos.uploadContactProfilePhoto","is_constructor":false},"stored_constructor":"fileSourceUserProfilePhoto","stored_params":[{"_":"extractUserIdFromInputUserAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"photos.Photo","constructor":"photos.uploadContactProfilePhoto","param":"user_id","param_type":"InputUser","flag":{"_":"paramNotFlag"}}]},"to":"user_id"},{"_":"extractAndStore","from":{"_":"path","parts":[{"_":"pathPart","type":"photos.Photo","constructor":"photos.uploadContactProfilePhoto","param":"","param_type":"photos.Photo","flag":{"_":"paramNotFlag"}},{"_":"pathPart","type":"photos.Photo","constructor":"photos.photo","param":"photo","param_type":"Photo","flag":{"_":"paramNotFlag"}},{"_":"pathPart","type":"Photo","constructor":"photo","param":"id","param_type":"long","flag":{"_":"paramNotFlag"}}]},"to":"max_id"}],"skipped_flags":[],"needs_not_parents":[]}],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"ChannelAdminLogEventAction","predicate":"channelAdminLogEventActionChangePhoto","params":[{"_":"traverseParam","name":"prev_photo","is_flag":false,"is_vector":false,"type":"Photo"},{"_":"traverseParam","name":"new_photo","is_flag":false,"is_vector":false,"type":"Photo"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"MessageAction","predicate":"messageActionRequestedPeerSentMe","params":[{"_":"traverseParam","name":"peers","is_flag":false,"is_vector":true,"type":"RequestedPeer"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"RequestedPeer","predicate":"requestedPeerUser","params":[{"_":"traverseParam","name":"photo","is_flag":true,"is_vector":false,"type":"Photo"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"RequestedPeer","predicate":"requestedPeerChat","params":[{"_":"traverseParam","name":"photo","is_flag":true,"is_vector":false,"type":"Photo"}],"push_sources":[],"is_needed_parent":false},{"_":"traverseIncomingConstructor","type":"RequestedPeer","predicate":"requestedPeerChannel","params":[{"_":"traverseParam","name":"photo","is_flag":true,"is_vector":false,"type":"Photo"}],"push_sources":[],"is_needed_parent":false}],"traversers_outgoing":[{"_":"traverseSwapLocation","type":"InputPhoto","predicate":"inputPhoto","stored_constructor":"fileIdPhoto"},{"_":"traverseSwapLocation","type":"InputDocument","predicate":"inputDocument","stored_constructor":"fileIdDocument"},{"_":"traverseSwapLocation","type":"InputFileLocation","predicate":"inputDocumentFileLocation","stored_constructor":"fileIdDocument"},{"_":"traverseSwapLocation","type":"InputFileLocation","predicate":"inputPhotoFileLocation","stored_constructor":"fileIdPhoto"},{"_":"traverseMethodCall","name":"account.reportProfilePhoto","params":[{"_":"traverseParam","name":"photo_id","is_flag":false,"is_vector":false,"type":"InputPhoto"}]},{"_":"traverseMethodCall","name":"photos.updateProfilePhoto","params":[{"_":"traverseParam","name":"id","is_flag":false,"is_vector":false,"type":"InputPhoto"}]},{"_":"traverseMethodCall","name":"photos.deletePhotos","params":[{"_":"traverseParam","name":"id","is_flag":false,"is_vector":true,"type":"InputPhoto"}]},{"_":"traverseOutgoingConstructor","type":"InputMedia","predicate":"inputMediaPhoto","params":[{"_":"traverseParam","name":"id","is_flag":false,"is_vector":false,"type":"InputPhoto"},{"_":"traverseParam","name":"video","is_flag":true,"is_vector":false,"type":"InputDocument"}]},{"_":"traverseMethodCall","name":"messages.sendMedia","params":[{"_":"traverseParam","name":"media","is_flag":false,"is_vector":false,"type":"InputMedia"}]},{"_":"traverseMethodCall","name":"messages.editMessage","params":[{"_":"traverseParam","name":"media","is_flag":true,"is_vector":false,"type":"InputMedia"},{"_":"traverseParam","name":"rich_message","is_flag":true,"is_vector":false,"type":"InputRichMessage"}]},{"_":"traverseMethodCall","name":"messages.editInlineBotMessage","params":[{"_":"traverseParam","name":"media","is_flag":true,"is_vector":false,"type":"InputMedia"},{"_":"traverseParam","name":"rich_message","is_flag":true,"is_vector":false,"type":"InputRichMessage"}]},{"_":"traverseMethodCall","name":"messages.saveDraft","params":[{"_":"traverseParam","name":"media","is_flag":true,"is_vector":false,"type":"InputMedia"},{"_":"traverseParam","name":"rich_message","is_flag":true,"is_vector":false,"type":"InputRichMessage"}]},{"_":"traverseMethodCall","name":"messages.uploadMedia","params":[{"_":"traverseParam","name":"media","is_flag":false,"is_vector":false,"type":"InputMedia"}]},{"_":"traverseMethodCall","name":"messages.uploadImportedMedia","params":[{"_":"traverseParam","name":"media","is_flag":false,"is_vector":false,"type":"InputMedia"}]},{"_":"traverseMethodCall","name":"bots.addPreviewMedia","params":[{"_":"traverseParam","name":"media","is_flag":false,"is_vector":false,"type":"InputMedia"}]},{"_":"traverseMethodCall","name":"bots.editPreviewMedia","params":[{"_":"traverseParam","name":"media","is_flag":false,"is_vector":false,"type":"InputMedia"},{"_":"traverseParam","name":"new_media","is_flag":false,"is_vector":false,"type":"InputMedia"}]},{"_":"traverseMethodCall","name":"bots.deletePreviewMedia","params":[{"_":"traverseParam","name":"media","is_flag":false,"is_vector":true,"type":"InputMedia"}]},{"_":"traverseMethodCall","name":"bots.reorderPreviewMedias","params":[{"_":"traverseParam","name":"order","is_flag":false,"is_vector":true,"type":"InputMedia"}]},{"_":"traverseMethodCall","name":"payments.exportInvoice","params":[{"_":"traverseParam","name":"invoice_media","is_flag":false,"is_vector":false,"type":"InputMedia"}]},{"_":"traverseMethodCall","name":"stories.sendStory","params":[{"_":"traverseParam","name":"media","is_flag":false,"is_vector":false,"type":"InputMedia"},{"_":"traverseParam","name":"music","is_flag":true,"is_vector":false,"type":"InputDocument"}]},{"_":"traverseMethodCall","name":"stories.editStory","params":[{"_":"traverseParam","name":"media","is_flag":true,"is_vector":false,"type":"InputMedia"},{"_":"traverseParam","name":"music","is_flag":true,"is_vector":false,"type":"InputDocument"}]},{"_":"traverseMethodCall","name":"ephemeral.sendMessage","params":[{"_":"traverseParam","name":"media","is_flag":true,"is_vector":false,"type":"InputMedia"},{"_":"traverseParam","name":"rich_message","is_flag":true,"is_vector":false,"type":"InputRichMessage"}]},{"_":"traverseMethodCall","name":"ephemeral.editMessage","params":[{"_":"traverseParam","name":"media","is_flag":true,"is_vector":false,"type":"InputMedia"},{"_":"traverseParam","name":"rich_message","is_flag":true,"is_vector":false,"type":"InputRichMessage"}]},{"_":"traverseOutgoingConstructor","type":"InputMedia","predicate":"inputMediaInvoice","params":[{"_":"traverseParam","name":"extended_media","is_flag":true,"is_vector":false,"type":"InputMedia"}]},{"_":"traverseOutgoingConstructor","type":"InputSingleMedia","predicate":"inputSingleMedia","params":[{"_":"traverseParam","name":"media","is_flag":false,"is_vector":false,"type":"InputMedia"}]},{"_":"traverseMethodCall","name":"messages.sendMultiMedia","params":[{"_":"traverseParam","name":"multi_media","is_flag":false,"is_vector":true,"type":"InputSingleMedia"}]},{"_":"traverseOutgoingConstructor","type":"PollAnswer","predicate":"inputPollAnswer","params":[{"_":"traverseParam","name":"media","is_flag":true,"is_vector":false,"type":"InputMedia"}]},{"_":"traverseMethodCall","name":"messages.addPollAnswer","params":[{"_":"traverseParam","name":"answer","is_flag":false,"is_vector":false,"type":"PollAnswer"}]},{"_":"traverseOutgoingConstructor","type":"InputMedia","predicate":"inputMediaPoll","params":[{"_":"traverseParam","name":"attached_media","is_flag":true,"is_vector":false,"type":"InputMedia"},{"_":"traverseParam","name":"solution_media","is_flag":true,"is_vector":false,"type":"InputMedia"}]},{"_":"traverseOutgoingConstructor","type":"InputMedia","predicate":"inputMediaPaidMedia","params":[{"_":"traverseParam","name":"extended_media","is_flag":false,"is_vector":true,"type":"InputMedia"}]},{"_":"traverseOutgoingConstructor","type":"InputMedia","predicate":"inputMediaUploadedDocument","params":[{"_":"traverseParam","name":"file","is_flag":false,"is_vector":false,"type":"InputFile"},{"_":"traverseParam","name":"thumb","is_flag":true,"is_vector":false,"type":"InputFile"},{"_":"traverseParam","name":"stickers","is_flag":true,"is_vector":true,"type":"InputDocument"},{"_":"traverseParam","name":"video_cover","is_flag":true,"is_vector":false,"type":"InputPhoto"}]},{"_":"traverseOutgoingConstructor","type":"InputMedia","predicate":"inputMediaDocument","params":[{"_":"traverseParam","name":"id","is_flag":false,"is_vector":false,"type":"InputDocument"},{"_":"traverseParam","name":"video_cover","is_flag":true,"is_vector":false,"type":"InputPhoto"}]},{"_":"traverseOutgoingConstructor","type":"InputMedia","predicate":"inputMediaDocumentExternal","params":[{"_":"traverseParam","name":"video_cover","is_flag":true,"is_vector":false,"type":"InputPhoto"}]},{"_":"traverseOutgoingConstructor","type":"InputChatPhoto","predicate":"inputChatPhoto","params":[{"_":"traverseParam","name":"id","is_flag":false,"is_vector":false,"type":"InputPhoto"}]},{"_":"traverseMethodCall","name":"messages.editChatPhoto","params":[{"_":"traverseParam","name":"photo","is_flag":false,"is_vector":false,"type":"InputChatPhoto"}]},{"_":"traverseMethodCall","name":"channels.editPhoto","params":[{"_":"traverseParam","name":"photo","is_flag":false,"is_vector":false,"type":"InputChatPhoto"}]},{"_":"traverseOutgoingConstructor","type":"InputBotInlineResult","predicate":"inputBotInlineResultPhoto","params":[{"_":"traverseParam","name":"photo","is_flag":false,"is_vector":false,"type":"InputPhoto"},{"_":"traverseParam","name":"send_message","is_flag":false,"is_vector":false,"type":"InputBotInlineMessage"}]},{"_":"traverseMethodCall","name":"messages.setInlineBotResults","params":[{"_":"traverseParam","name":"results","is_flag":false,"is_vector":true,"type":"InputBotInlineResult"}]},{"_":"traverseMethodCall","name":"messages.sendWebViewResultMessage","params":[{"_":"traverseParam","name":"result","is_flag":false,"is_vector":false,"type":"InputBotInlineResult"}]},{"_":"traverseMethodCall","name":"messages.savePreparedInlineMessage","params":[{"_":"traverseParam","name":"result","is_flag":false,"is_vector":false,"type":"InputBotInlineResult"}]},{"_":"traverseMethodCall","name":"messages.setBotGuestChatResult","params":[{"_":"traverseParam","name":"result","is_flag":false,"is_vector":false,"type":"InputBotInlineResult"}]},{"_":"traverseOutgoingConstructor","type":"InputStickeredMedia","predicate":"inputStickeredMediaPhoto","params":[{"_":"traverseParam","name":"id","is_flag":false,"is_vector":false,"type":"InputPhoto"}]},{"_":"traverseMethodCall","name":"messages.getAttachedStickers","params":[{"_":"traverseParam","name":"media","is_flag":false,"is_vector":false,"type":"InputStickeredMedia"}]},{"_":"traverseOutgoingConstructor","type":"InputRichFile","predicate":"inputRichFilePhoto","params":[{"_":"traverseParam","name":"photo","is_flag":false,"is_vector":false,"type":"InputPhoto"}]},{"_":"traverseOutgoingConstructor","type":"InputRichMessage","predicate":"inputRichMessageHTML","params":[{"_":"traverseParam","name":"files","is_flag":true,"is_vector":true,"type":"InputRichFile"}]},{"_":"traverseMethodCall","name":"messages.sendMessage","params":[{"_":"traverseParam","name":"rich_message","is_flag":true,"is_vector":false,"type":"InputRichMessage"}]},{"_":"traverseMethodCall","name":"messages.translateRichMessage","params":[{"_":"traverseParam","name":"text","is_flag":true,"is_vector":true,"type":"InputRichMessage"}]},{"_":"traverseMethodCall","name":"messages.composeRichMessageWithAI","params":[{"_":"traverseParam","name":"text","is_flag":true,"is_vector":false,"type":"InputRichMessage"}]},{"_":"traverseOutgoingConstructor","type":"SendMessageAction","predicate":"inputSendMessageRichMessageDraftAction","params":[{"_":"traverseParam","name":"rich_message","is_flag":false,"is_vector":false,"type":"InputRichMessage"}]},{"_":"traverseMethodCall","name":"messages.setTyping","params":[{"_":"traverseParam","name":"action","is_flag":false,"is_vector":false,"type":"SendMessageAction"}]},{"_":"traverseOutgoingConstructor","type":"InputBotInlineMessage","predicate":"inputBotInlineMessageRichMessage","params":[{"_":"traverseParam","name":"rich_message","is_flag":false,"is_vector":false,"type":"InputRichMessage"}]},{"_":"traverseOutgoingConstructor","type":"InputBotInlineResult","predicate":"inputBotInlineResult","params":[{"_":"traverseParam","name":"send_message","is_flag":false,"is_vector":false,"type":"InputBotInlineMessage"}]},{"_":"traverseOutgoingConstructor","type":"InputBotInlineResult","predicate":"inputBotInlineResultDocument","params":[{"_":"traverseParam","name":"document","is_flag":false,"is_vector":false,"type":"InputDocument"},{"_":"traverseParam","name":"send_message","is_flag":false,"is_vector":false,"type":"InputBotInlineMessage"}]},{"_":"traverseOutgoingConstructor","type":"InputBotInlineResult","predicate":"inputBotInlineResultGame","params":[{"_":"traverseParam","name":"send_message","is_flag":false,"is_vector":false,"type":"InputBotInlineMessage"}]},{"_":"traverseOutgoingConstructor","type":"InputRichMessage","predicate":"inputRichMessageMarkdown","params":[{"_":"traverseParam","name":"files","is_flag":true,"is_vector":true,"type":"InputRichFile"}]},{"_":"traverseOutgoingConstructor","type":"InputRichMessage","predicate":"inputRichMessage","params":[{"_":"traverseParam","name":"photos","is_flag":true,"is_vector":true,"type":"InputPhoto"},{"_":"traverseParam","name":"documents","is_flag":true,"is_vector":true,"type":"InputDocument"}]},{"_":"traverseMethodCall","name":"account.createTheme","params":[{"_":"traverseParam","name":"document","is_flag":true,"is_vector":false,"type":"InputDocument"}]},{"_":"traverseMethodCall","name":"account.updateTheme","params":[{"_":"traverseParam","name":"document","is_flag":true,"is_vector":false,"type":"InputDocument"}]},{"_":"traverseMethodCall","name":"account.saveRingtone","params":[{"_":"traverseParam","name":"id","is_flag":false,"is_vector":false,"type":"InputDocument"}]},{"_":"traverseMethodCall","name":"account.saveMusic","params":[{"_":"traverseParam","name":"id","is_flag":false,"is_vector":false,"type":"InputDocument"},{"_":"traverseParam","name":"after_id","is_flag":true,"is_vector":false,"type":"InputDocument"}]},{"_":"traverseMethodCall","name":"users.getSavedMusicByID","params":[{"_":"traverseParam","name":"documents","is_flag":false,"is_vector":true,"type":"InputDocument"}]},{"_":"traverseMethodCall","name":"messages.saveGif","params":[{"_":"traverseParam","name":"id","is_flag":false,"is_vector":false,"type":"InputDocument"}]},{"_":"traverseMethodCall","name":"messages.saveRecentSticker","params":[{"_":"traverseParam","name":"id","is_flag":false,"is_vector":false,"type":"InputDocument"}]},{"_":"traverseMethodCall","name":"messages.faveSticker","params":[{"_":"traverseParam","name":"id","is_flag":false,"is_vector":false,"type":"InputDocument"}]},{"_":"traverseMethodCall","name":"messages.reportMusicListen","params":[{"_":"traverseParam","name":"id","is_flag":false,"is_vector":false,"type":"InputDocument"}]},{"_":"traverseMethodCall","name":"stickers.createStickerSet","params":[{"_":"traverseParam","name":"thumb","is_flag":true,"is_vector":false,"type":"InputDocument"},{"_":"traverseParam","name":"stickers","is_flag":false,"is_vector":true,"type":"InputStickerSetItem"}]},{"_":"traverseMethodCall","name":"stickers.removeStickerFromSet","params":[{"_":"traverseParam","name":"sticker","is_flag":false,"is_vector":false,"type":"InputDocument"}]},{"_":"traverseMethodCall","name":"stickers.changeStickerPosition","params":[{"_":"traverseParam","name":"sticker","is_flag":false,"is_vector":false,"type":"InputDocument"}]},{"_":"traverseMethodCall","name":"stickers.setStickerSetThumb","params":[{"_":"traverseParam","name":"thumb","is_flag":true,"is_vector":false,"type":"InputDocument"}]},{"_":"traverseMethodCall","name":"stickers.changeSticker","params":[{"_":"traverseParam","name":"sticker","is_flag":false,"is_vector":false,"type":"InputDocument"}]},{"_":"traverseMethodCall","name":"stickers.replaceSticker","params":[{"_":"traverseParam","name":"sticker","is_flag":false,"is_vector":false,"type":"InputDocument"},{"_":"traverseParam","name":"new_sticker","is_flag":false,"is_vector":false,"type":"InputStickerSetItem"}]},{"_":"traverseOutgoingConstructor","type":"InputFile","predicate":"inputFileStoryDocument","params":[{"_":"traverseParam","name":"id","is_flag":false,"is_vector":false,"type":"InputDocument"}]},{"_":"traverseMethodCall","name":"account.uploadWallPaper","params":[{"_":"traverseParam","name":"file","is_flag":false,"is_vector":false,"type":"InputFile"}]},{"_":"traverseMethodCall","name":"account.uploadTheme","params":[{"_":"traverseParam","name":"file","is_flag":false,"is_vector":false,"type":"InputFile"},{"_":"traverseParam","name":"thumb","is_flag":true,"is_vector":false,"type":"InputFile"}]},{"_":"traverseMethodCall","name":"account.uploadRingtone","params":[{"_":"traverseParam","name":"file","is_flag":false,"is_vector":false,"type":"InputFile"}]},{"_":"traverseMethodCall","name":"messages.initHistoryImport","params":[{"_":"traverseParam","name":"file","is_flag":false,"is_vector":false,"type":"InputFile"}]},{"_":"traverseMethodCall","name":"photos.uploadProfilePhoto","params":[{"_":"traverseParam","name":"file","is_flag":true,"is_vector":false,"type":"InputFile"},{"_":"traverseParam","name":"video","is_flag":true,"is_vector":false,"type":"InputFile"}]},{"_":"traverseMethodCall","name":"photos.uploadContactProfilePhoto","params":[{"_":"traverseParam","name":"file","is_flag":true,"is_vector":false,"type":"InputFile"},{"_":"traverseParam","name":"video","is_flag":true,"is_vector":false,"type":"InputFile"}]},{"_":"traverseMethodCall","name":"phone.saveCallLog","params":[{"_":"traverseParam","name":"file","is_flag":false,"is_vector":false,"type":"InputFile"}]},{"_":"traverseOutgoingConstructor","type":"InputMedia","predicate":"inputMediaUploadedPhoto","params":[{"_":"traverseParam","name":"file","is_flag":false,"is_vector":false,"type":"InputFile"},{"_":"traverseParam","name":"stickers","is_flag":true,"is_vector":true,"type":"InputDocument"},{"_":"traverseParam","name":"video","is_flag":true,"is_vector":false,"type":"InputDocument"}]},{"_":"traverseOutgoingConstructor","type":"InputChatPhoto","predicate":"inputChatUploadedPhoto","params":[{"_":"traverseParam","name":"file","is_flag":true,"is_vector":false,"type":"InputFile"},{"_":"traverseParam","name":"video","is_flag":true,"is_vector":false,"type":"InputFile"}]},{"_":"traverseOutgoingConstructor","type":"InputStickeredMedia","predicate":"inputStickeredMediaDocument","params":[{"_":"traverseParam","name":"id","is_flag":false,"is_vector":false,"type":"InputDocument"}]},{"_":"traverseOutgoingConstructor","type":"InputWebFileLocation","predicate":"inputWebFileAudioAlbumThumbLocation","params":[{"_":"traverseParam","name":"document","is_flag":true,"is_vector":false,"type":"InputDocument"}]},{"_":"traverseMethodCall","name":"upload.getWebFile","params":[{"_":"traverseParam","name":"location","is_flag":false,"is_vector":false,"type":"InputWebFileLocation"}]},{"_":"traverseOutgoingConstructor","type":"InputStickerSetItem","predicate":"inputStickerSetItem","params":[{"_":"traverseParam","name":"document","is_flag":false,"is_vector":false,"type":"InputDocument"}]},{"_":"traverseMethodCall","name":"stickers.addStickerToSet","params":[{"_":"traverseParam","name":"sticker","is_flag":false,"is_vector":false,"type":"InputStickerSetItem"}]},{"_":"traverseOutgoingConstructor","type":"InputBusinessIntro","predicate":"inputBusinessIntro","params":[{"_":"traverseParam","name":"sticker","is_flag":true,"is_vector":false,"type":"InputDocument"}]},{"_":"traverseMethodCall","name":"account.updateBusinessIntro","params":[{"_":"traverseParam","name":"intro","is_flag":true,"is_vector":false,"type":"InputBusinessIntro"}]},{"_":"traverseOutgoingConstructor","type":"InputRichFile","predicate":"inputRichFileDocument","params":[{"_":"traverseParam","name":"document","is_flag":false,"is_vector":false,"type":"InputDocument"}]},{"_":"traverseMethodCall","name":"upload.getFile","params":[{"_":"traverseParam","name":"location","is_flag":false,"is_vector":false,"type":"InputFileLocation"}]},{"_":"traverseMethodCall","name":"upload.getFileHashes","params":[{"_":"traverseParam","name":"location","is_flag":false,"is_vector":false,"type":"InputFileLocation"}]}],"refresh_actions":[{"_":"refreshAction","stored_constructor":"fileSourceMessage","action":{"_":"getMessageOp","peer":{"_":"typedOp","type":"InputPeer","op":{"_":"getInputPeerByIdOp","from":"peer"}},"id":{"_":"typedOp","type":"int","op":{"_":"copyOp","from":"id"}},"quick_reply_shortcut_id":{"_":"typedOp","type":"int","op":{"_":"copyOp","from":"quick_reply_shortcut_id"}}}},{"_":"refreshAction","stored_constructor":"fileSourceScheduledMessage","action":{"_":"callOp","method":"messages.getScheduledMessages","args":[{"_":"typedOpArg","key":"peer","value":{"_":"typedOp","type":"InputPeer","op":{"_":"getInputPeerByIdOp","from":"peer"}}},{"_":"typedOpArg","key":"id","value":{"_":"typedOp","type":"Vector<int>","op":{"_":"vectorOp","values":[{"_":"typedOp","type":"int","op":{"_":"copyOp","from":"id"}}]}}}]}},{"_":"refreshAction","stored_constructor":"fileSourceStory","action":{"_":"callOp","method":"stories.getStoriesByID","args":[{"_":"typedOpArg","key":"id","value":{"_":"typedOp","type":"Vector<int>","op":{"_":"vectorOp","values":[{"_":"typedOp","type":"int","op":{"_":"copyOp","from":"id"}}]}}},{"_":"typedOpArg","key":"peer","value":{"_":"typedOp","type":"InputPeer","op":{"_":"getInputPeerByIdOp","from":"peer"}}}]}},{"_":"refreshAction","stored_constructor":"fileSourceWebPage","action":{"_":"callOp","method":"messages.getWebPage","args":[{"_":"typedOpArg","key":"url","value":{"_":"typedOp","type":"string","op":{"_":"copyOp","from":"url"}}},{"_":"typedOpArg","key":"hash","value":{"_":"typedOp","type":"int","op":{"_":"intLiteralOp","value":0}}}]}},{"_":"refreshAction","stored_constructor":"fileSourceBotApp","action":{"_":"callOp","method":"messages.getBotApp","args":[{"_":"typedOpArg","key":"app","value":{"_":"typedOp","type":"InputBotApp","op":{"_":"constructorOp","constructor":"inputBotAppID","args":[{"_":"typedOpArg","key":"id","value":{"_":"typedOp","type":"long","op":{"_":"copyOp","from":"id"}}},{"_":"typedOpArg","key":"access_hash","value":{"_":"typedOp","type":"long","op":{"_":"copyOp","from":"access_hash"}}}]}}},{"_":"typedOpArg","key":"hash","value":{"_":"typedOp","type":"long","op":{"_":"longLiteralOp","value":0}}}]}},{"_":"refreshAction","stored_constructor":"fileSourceUserFull","action":{"_":"callOp","method":"users.getFullUser","args":[{"_":"typedOpArg","key":"id","value":{"_":"typedOp","type":"InputUser","op":{"_":"getInputUserByIdOp","from":"id"}}}]}},{"_":"refreshAction","stored_constructor":"fileSourceAdminLog","action":{"_":"callOp","method":"channels.getAdminLog","args":[{"_":"typedOpArg","key":"channel","value":{"_":"typedOp","type":"InputChannel","op":{"_":"getInputChannelByIdOp","from":"channel"}}},{"_":"typedOpArg","key":"max_id","value":{"_":"typedOp","type":"long","op":{"_":"copyOp","from":"max_id"}}},{"_":"typedOpArg","key":"min_id","value":{"_":"typedOp","type":"long","op":{"_":"copyOp","from":"max_id"}}},{"_":"typedOpArg","key":"limit","value":{"_":"typedOp","type":"int","op":{"_":"intLiteralOp","value":1}}},{"_":"typedOpArg","key":"q","value":{"_":"typedOp","type":"string","op":{"_":"stringLiteralOp","value":""}}}]}},{"_":"refreshAction","stored_constructor":"fileSourceStoryAlbum","action":{"_":"callOp","method":"stories.getAlbums","args":[{"_":"typedOpArg","key":"peer","value":{"_":"typedOp","type":"InputPeer","op":{"_":"getInputPeerByIdOp","from":"peer"}}},{"_":"typedOpArg","key":"hash","value":{"_":"typedOp","type":"long","op":{"_":"longLiteralOp","value":0}}}]}},{"_":"refreshAction","stored_constructor":"fileSourceBotPreviewMedia","action":{"_":"callOp","method":"bots.getPreviewMedias","args":[{"_":"typedOpArg","key":"bot","value":{"_":"typedOp","type":"InputUser","op":{"_":"getInputUserByIdOp","from":"bot"}}}]}},{"_":"refreshAction","stored_constructor":"fileSourceBotPreviewInfo","action":{"_":"callOp","method":"bots.getPreviewInfo","args":[{"_":"typedOpArg","key":"bot","value":{"_":"typedOp","type":"InputUser","op":{"_":"getInputUserByIdOp","from":"bot"}}},{"_":"typedOpArg","key":"lang_code","value":{"_":"typedOp","type":"string","op":{"_":"copyOp","from":"lang_code"}}}]}},{"_":"refreshAction","stored_constructor":"fileSourcePaidMedia","action":{"_":"callOp","method":"messages.getExtendedMedia","args":[{"_":"typedOpArg","key":"id","value":{"_":"typedOp","type":"Vector<int>","op":{"_":"vectorOp","values":[{"_":"typedOp","type":"int","op":{"_":"copyOp","from":"id"}}]}}},{"_":"typedOpArg","key":"peer","value":{"_":"typedOp","type":"InputPeer","op":{"_":"getInputPeerByIdOp","from":"peer"}}}]}},{"_":"refreshAction","stored_constructor":"fileSourceSavedMusic","action":{"_":"callOp","method":"users.getSavedMusicByID","args":[{"_":"typedOpArg","key":"id","value":{"_":"typedOp","type":"InputUser","op":{"_":"getInputUserByIdOp","from":"user_id"}}},{"_":"typedOpArg","key":"documents","value":{"_":"typedOp","type":"Vector<InputDocument>","op":{"_":"vectorOp","values":[{"_":"typedOp","type":"InputDocument","op":{"_":"constructorOp","constructor":"inputDocument","args":[{"_":"typedOpArg","key":"id","value":{"_":"typedOp","type":"long","op":{"_":"copyOp","from":"id"}}},{"_":"typedOpArg","key":"access_hash","value":{"_":"typedOp","type":"long","op":{"_":"copyOp","from":"access_hash"}}},{"_":"typedOpArg","key":"file_reference","value":{"_":"typedOp","type":"bytes","op":{"_":"bytesLiteralOp","value":{"_":"bytes","bytes":""}}}}]}}]}}}]}},{"_":"refreshAction","stored_constructor":"fileSourceChatFull","action":{"_":"callOp","method":"messages.getFullChat","args":[{"_":"typedOpArg","key":"chat_id","value":{"_":"typedOp","type":"long","op":{"_":"copyOp","from":"chat_id"}}}]}},{"_":"refreshAction","stored_constructor":"fileSourceChannelFull","action":{"_":"callOp","method":"channels.getFullChannel","args":[{"_":"typedOpArg","key":"channel","value":{"_":"typedOp","type":"InputChannel","op":{"_":"getInputChannelByIdOp","from":"channel"}}}]}},{"_":"refreshAction","stored_constructor":"fileSourceCommunityFull","action":{"_":"callOp","method":"channels.getFullChannel","args":[{"_":"typedOpArg","key":"channel","value":{"_":"typedOp","type":"InputChannel","op":{"_":"getInputChannelByIdOp","from":"channel"}}}]}},{"_":"refreshAction","stored_constructor":"fileSourcePremiumPromo","action":{"_":"callOp","method":"help.getPremiumPromo","args":[]}},{"_":"refreshAction","stored_constructor":"fileSourceStarsTransaction","action":{"_":"callOp","method":"payments.getStarsTransactionsByID","args":[{"_":"typedOpArg","key":"peer","value":{"_":"typedOp","type":"InputPeer","op":{"_":"getInputPeerByIdOp","from":"peer"}}},{"_":"typedOpArg","key":"ton","value":{"_":"typedOp","type":"true","op":{"_":"copyOp","from":"ton"}}},{"_":"typedOpArg","key":"id","value":{"_":"typedOp","type":"Vector<InputStarsTransaction>","op":{"_":"vectorOp","values":[{"_":"typedOp","type":"InputStarsTransaction","op":{"_":"constructorOp","constructor":"inputStarsTransaction","args":[{"_":"typedOpArg","key":"id","value":{"_":"typedOp","type":"string","op":{"_":"copyOp","from":"id"}}},{"_":"typedOpArg","key":"refund","value":{"_":"typedOp","type":"true","op":{"_":"copyOp","from":"refund"}}}]}}]}}}]}},{"_":"refreshAction","stored_constructor":"fileSourceAttachMenuBot","action":{"_":"callOp","method":"messages.getAttachMenuBot","args":[{"_":"typedOpArg","key":"bot","value":{"_":"typedOp","type":"InputUser","op":{"_":"getInputUserByIdOp","from":"bot"}}}]}},{"_":"refreshAction","stored_constructor":"fileSourceTheme","action":{"_":"callOp","method":"account.getTheme","args":[{"_":"typedOpArg","key":"theme","value":{"_":"typedOp","type":"InputTheme","op":{"_":"constructorOp","constructor":"inputTheme","args":[{"_":"typedOpArg","key":"id","value":{"_":"typedOp","type":"long","op":{"_":"copyOp","from":"id"}}},{"_":"typedOpArg","key":"access_hash","value":{"_":"typedOp","type":"long","op":{"_":"copyOp","from":"access_hash"}}}]}}},{"_":"typedOpArg","key":"format","value":{"_":"typedOp","type":"string","op":{"_":"themeFormatLiteralOp"}}}]}},{"_":"refreshAction","stored_constructor":"fileSourceWallPaper","action":{"_":"callOp","method":"account.getWallPaper","args":[{"_":"typedOpArg","key":"wallpaper","value":{"_":"typedOp","type":"InputWallPaper","op":{"_":"constructorOp","constructor":"inputWallPaper","args":[{"_":"typedOpArg","key":"id","value":{"_":"typedOp","type":"long","op":{"_":"copyOp","from":"id"}}},{"_":"typedOpArg","key":"access_hash","value":{"_":"typedOp","type":"long","op":{"_":"copyOp","from":"access_hash"}}}]}}}]}},{"_":"refreshAction","stored_constructor":"fileSourceStickerSet","action":{"_":"callOp","method":"messages.getStickerSet","args":[{"_":"typedOpArg","key":"stickerset","value":{"_":"typedOp","type":"InputStickerSet","op":{"_":"copyOp","from":"stickerset"}}},{"_":"typedOpArg","key":"hash","value":{"_":"typedOp","type":"int","op":{"_":"intLiteralOp","value":0}}}]}},{"_":"refreshAction","stored_constructor":"fileSourceSavedGifs","action":{"_":"callOp","method":"messages.getSavedGifs","args":[{"_":"typedOpArg","key":"hash","value":{"_":"typedOp","type":"long","op":{"_":"longLiteralOp","value":0}}}]}},{"_":"refreshAction","stored_constructor":"fileSourceSavedRingtones","action":{"_":"callOp","method":"account.getSavedRingtones","args":[{"_":"typedOpArg","key":"hash","value":{"_":"typedOp","type":"long","op":{"_":"longLiteralOp","value":0}}}]}},{"_":"refreshAction","stored_constructor":"fileSourceAvailableEffects","action":{"_":"callOp","method":"messages.getAvailableEffects","args":[{"_":"typedOpArg","key":"hash","value":{"_":"typedOp","type":"int","op":{"_":"intLiteralOp","value":0}}}]}},{"_":"refreshAction","stored_constructor":"fileSourceAvailableReactions","action":{"_":"callOp","method":"messages.getAvailableReactions","args":[{"_":"typedOpArg","key":"hash","value":{"_":"typedOp","type":"int","op":{"_":"intLiteralOp","value":0}}}]}},{"_":"refreshAction","stored_constructor":"fileSourceUserProfilePhoto","action":{"_":"callOp","method":"photos.getUserPhotos","args":[{"_":"typedOpArg","key":"user_id","value":{"_":"typedOp","type":"InputUser","op":{"_":"getInputUserByIdOp","from":"user_id"}}},{"_":"typedOpArg","key":"offset","value":{"_":"typedOp","type":"int","op":{"_":"intLiteralOp","value":-1}}},{"_":"typedOpArg","key":"max_id","value":{"_":"typedOp","type":"long","op":{"_":"copyOp","from":"max_id"}}},{"_":"typedOpArg","key":"limit","value":{"_":"typedOp","type":"int","op":{"_":"intLiteralOp","value":1}}}]}},{"_":"refreshAction","stored_constructor":"fileSourceDocumentByHash","action":{"_":"callOp","method":"messages.getDocumentByHash","args":[{"_":"typedOpArg","key":"sha256","value":{"_":"typedOp","type":"bytes","op":{"_":"copyOp","from":"sha256"}}},{"_":"typedOpArg","key":"size","value":{"_":"typedOp","type":"long","op":{"_":"copyOp","from":"size"}}},{"_":"typedOpArg","key":"mime_type","value":{"_":"typedOp","type":"string","op":{"_":"copyOp","from":"mime_type"}}}]}},{"_":"refreshAction","stored_constructor":"fileSourceWelcomeMessages","action":{"_":"callOp","method":"ephemeral.getWelcomeMessages","args":[{"_":"typedOpArg","key":"peer","value":{"_":"typedOp","type":"InputPeer","op":{"_":"getInputPeerByIdOp","from":"peer"}}},{"_":"typedOpArg","key":"hash","value":{"_":"typedOp","type":"long","op":{"_":"longLiteralOp","value":0}}}]}}],"skipped_incoming_sources":[{"_":"skippedSource","predicate":{"_":"predicate","predicate":"messages.getSponsoredMessages","is_constructor":false},"why":"Do not store file references from sponsored messages"},{"_":"skippedSource","predicate":{"_":"predicate","predicate":"help.getAppUpdate","is_constructor":false},"why":"Don't handle file references from ephemeral app update info"},{"_":"skippedSource","predicate":{"_":"predicate","predicate":"help.getRecentMeUrls","is_constructor":false},"why":"Don't handle file references from recent t.me URLs"},{"_":"skippedSource","predicate":{"_":"predicate","predicate":"draftMessage","is_constructor":true},"why":"Do not store references from drafts"},{"_":"skippedSource","predicate":{"_":"predicate","predicate":"recentMeUrlChatInvite","is_constructor":true},"why":"Do not store references based on chat invite links"},{"_":"skippedSource","predicate":{"_":"predicate","predicate":"messages.checkChatInvite","is_constructor":false},"why":"Do not store references based on chat invite links"},{"_":"skippedSource","predicate":{"_":"predicate","predicate":"messages.getInlineBotResults","is_constructor":false},"why":"Inline bot results are ephemeral"},{"_":"skippedSource","predicate":{"_":"predicate","predicate":"messages.getPreparedInlineMessage","is_constructor":false},"why":"Inline bot results are ephemeral"},{"_":"skippedSource","predicate":{"_":"predicate","predicate":"messages.uploadMedia","is_constructor":false},"why":"A freshly uploaded media file will obtain a context only once it is sent to a chat"},{"_":"skippedSource","predicate":{"_":"predicate","predicate":"messages.uploadImportedMedia","is_constructor":false},"why":"A freshly uploaded media file will obtain a context only once it is sent to a chat"},{"_":"skippedSource","predicate":{"_":"predicate","predicate":"updateServiceNotification","is_constructor":true},"why":"Cannot refetch service notifications"},{"_":"skippedSource","predicate":{"_":"predicate","predicate":"messages.getWebPagePreview","is_constructor":false},"why":"No locations are added for the method call, as it doesn't use persistent IDs as input; the location is instead extracted from the persistent IDs in the returned WebPage object"},{"_":"skippedSource","predicate":{"_":"predicate","predicate":"payments.resaleStarGifts","is_constructor":true},"why":"Contexts for star gifts are not yet implemented"},{"_":"skippedSource","predicate":{"_":"predicate","predicate":"payments.starGiftUpgradePreview","is_constructor":true},"why":"Contexts for star gifts are not yet implemented"},{"_":"skippedSource","predicate":{"_":"predicate","predicate":"starGift","is_constructor":true},"why":"Contexts for star gifts are not yet implemented"},{"_":"skippedSource","predicate":{"_":"predicate","predicate":"starGiftUnique","is_constructor":true},"why":"Contexts for star gifts are not yet implemented"},{"_":"skippedSource","predicate":{"_":"predicate","predicate":"starGiftCollection","is_constructor":true},"why":"Contexts for star gifts are not yet implemented"},{"_":"skippedSource","predicate":{"_":"predicate","predicate":"payments.starGiftCollections","is_constructor":true},"why":"Contexts for star gifts are not yet implemented"},{"_":"skippedSource","predicate":{"_":"predicate","predicate":"payments.starGiftUpgradeAttributes","is_constructor":true},"why":"Contexts for star gifts are not yet implemented"},{"_":"skippedSource","predicate":{"_":"predicate","predicate":"updateEditEphemeralMessage","is_constructor":true},"why":"Do not store file references from ephemeral messages"},{"_":"skippedSource","predicate":{"_":"predicate","predicate":"updateEphemeralBotCallbackQuery","is_constructor":true},"why":"Do not store file references from ephemeral messages"},{"_":"skippedSource","predicate":{"_":"predicate","predicate":"updateNewEphemeralMessage","is_constructor":true},"why":"Do not store file references from ephemeral messages"},{"_":"skippedSource","predicate":{"_":"predicate","predicate":"messages.translatedRichMessage","is_constructor":true},"why":"Do not store file references from translated rich messages"},{"_":"skippedSource","predicate":{"_":"predicate","predicate":"messages.composedRichMessageWithAI","is_constructor":true},"why":"Do not store file references from composed rich messages"},{"_":"skippedSource","predicate":{"_":"predicate","predicate":"updateChatUserTyping","is_constructor":true},"why":"Documents encountered in rich text message live drafts are ephemeral"},{"_":"skippedSource","predicate":{"_":"predicate","predicate":"updateChannelUserTyping","is_constructor":true},"why":"Documents encountered in rich text message live drafts are ephemeral"},{"_":"skippedSource","predicate":{"_":"predicate","predicate":"updateUserTyping","is_constructor":true},"why":"Documents encountered in rich text message live drafts are ephemeral"},{"_":"skippedSource","predicate":{"_":"predicate","predicate":"messages.getCustomEmojiDocuments","is_constructor":false},"why":"Do not store file references in this context"},{"_":"skippedSource","predicate":{"_":"predicate","predicate":"account.uploadTheme","is_constructor":false},"why":"A freshly uploaded theme file will obtain a context only once it is created via account.createTheme"}]}<?php

declare(strict_types=1);

/**
 * EventHandler module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use Amp\DeferredFuture;
use Amp\Future;
use Amp\Sync\LocalMutex;
use AssertionError;
use danog\Loop\PeriodicLoop;
use danog\MadelineProto\EventHandler\Attributes\Cron;
use danog\MadelineProto\EventHandler\Attributes\Handler;
use danog\MadelineProto\EventHandler\Filter\Combinator\FiltersAnd;
use danog\MadelineProto\EventHandler\Filter\Filter;
use danog\MadelineProto\EventHandler\Filter\FilterAllowAll;
use danog\MadelineProto\EventHandler\Update;
use danog\MadelineProto\Settings\Metrics;
use Generator;
use PhpParser\Node\Name;
use ReflectionAttribute;
use ReflectionClass;
use ReflectionMethod;
use Revolt\EventLoop;
use Webmozart\Assert\Assert;

use function Amp\async;
use function Amp\File\isDirectory;
use function Amp\File\isFile;
use function Amp\File\listFiles;
use function Amp\Future\await;

/**
 * Event handler.
 */
abstract class EventHandler extends AbstractAPI
{
    use LegacyMigrator {
        LegacyMigrator::initDbProperties as private internalInitDbProperties;
        LegacyMigrator::saveDbProperties as private privateInternalSaveDbProperties;
    }

    /**
     * @internal Do not use manually.
     *
     * @psalm-mutation-free
     */
    final private function __construct()
    {
        // Dummy code that is NEVER executed, needed to avoid issues during Psalm analysis.

        /** @var \danog\MadelineProto\Namespace\Auth $auth */
        $this->auth = $auth;
        /** @var \danog\MadelineProto\Namespace\Account $account */
        $this->account = $account;
        /** @var \danog\MadelineProto\Namespace\Users $users */
        $this->users = $users;
        /** @var \danog\MadelineProto\Namespace\Contacts $contacts */
        $this->contacts = $contacts;
        /** @var \danog\MadelineProto\Namespace\Messages $messages */
        $this->messages = $messages;
        /** @var \danog\MadelineProto\Namespace\Updates $updates */
        $this->updates = $updates;
        /** @var \danog\MadelineProto\Namespace\Photos $photos */
        $this->photos = $photos;
        /** @var \danog\MadelineProto\Namespace\Upload $upload */
        $this->upload = $upload;
        /** @var \danog\MadelineProto\Namespace\Help $help */
        $this->help = $help;
        /** @var \danog\MadelineProto\Namespace\Channels $channels */
        $this->channels = $channels;
        /** @var \danog\MadelineProto\Namespace\Bots $bots */
        $this->bots = $bots;
        /** @var \danog\MadelineProto\Namespace\Payments $payments */
        $this->payments = $payments;
        /** @var \danog\MadelineProto\Namespace\Stickers $stickers */
        $this->stickers = $stickers;
        /** @var \danog\MadelineProto\Namespace\Phone $phone */
        $this->phone = $phone;
        /** @var \danog\MadelineProto\Namespace\Langpack $langpack */
        $this->langpack = $langpack;
        /** @var \danog\MadelineProto\Namespace\Folders $folders */
        $this->folders = $folders;
        /** @var \danog\MadelineProto\Namespace\Stats $stats */
        $this->stats = $stats;
        /** @var \danog\MadelineProto\Namespace\Chatlists $chatlists */
        $this->chatlists = $chatlists;
        /** @var \danog\MadelineProto\Namespace\Stories $stories */
        $this->stories = $stories;
        /** @var \danog\MadelineProto\Namespace\Premium $premium */
        $this->premium = $premium;
        /** @var \danog\MadelineProto\Namespace\Smsjobs $smsjobs */
        $this->smsjobs = $smsjobs;
        /** @var \danog\MadelineProto\Namespace\Fragment $fragment */
        $this->fragment = $fragment;
        /** @var \danog\MadelineProto\APIWrapper $wrapper */
        $this->wrapper = $wrapper;
    }

    /** @internal Do not use manually. */
    final public function internalSaveDbProperties(): void
    {
        $this->privateInternalSaveDbProperties();
    }
    /**
     * @internal Do not use manually.
     */
    final public function internalClearDbProperties(): void
    {
        $f = [];
        foreach ($this->properties as $property) {
            $f []= async($property->clear(...));
        }
        await($f);
    }

    private static bool $includingPlugins = false;
    /**
     * Start MadelineProto and the event handler.
     *
     * Also initializes error reporting, catching and reporting all errors surfacing from the event loop.
     *
     * @param string            $session  Session name
     * @param ?SettingsAbstract $settings Settings
     */
    final public static function startAndLoop(string $session, ?SettingsAbstract $settings = null): void
    {
        if (self::$includingPlugins) {
            return;
        }
        self::cachePlugins(static::class);
        $settings ??= new SettingsEmpty;
        $API = new API($session, $settings);
        if ($settings instanceof Settings) {
            $settings = $settings->getMetrics();
        }
        if ($settings instanceof Metrics
            && $settings->getReturnMetricsFromStartAndLoop()
        ) {
            if (isset($_GET['metrics'])) {
                Tools::closeConnection($API->renderPromStats());
                return;
            } elseif (isset($_GET['pprof'])) {
                Tools::closeConnection($API->getMemoryProfile());
                return;
            }
        }
        $API->startAndLoopInternal(static::class);
    }
    /**
     * Start MadelineProto as a bot and the event handler.
     *
     * Also initializes error reporting, catching and reporting all errors surfacing from the event loop.
     *
     * @param string            $session  Session name
     * @param string            $token    Bot token
     * @param ?SettingsAbstract $settings Settings
     */
    final public static function startAndLoopBot(string $session, string $token, ?SettingsAbstract $settings = null): void
    {
        if (self::$includingPlugins) {
            return;
        }
        self::cachePlugins(static::class);
        $settings ??= new SettingsEmpty;
        $API = new API($session, $settings);
        $API->botLogin($token);
        $API->startAndLoopInternal(static::class);
    }
    /**
     * @internal
     *
     * @psalm-pure
     */
    #[\Override]
    final protected function reconnectFull(): bool
    {
        return true;
    }
    /**
     * Whether the event handler was started.
     */
    private bool $startedInternal = false;
    private ?LocalMutex $startMutex = null;
    private ?DeferredFuture $startDeferred = null;
    /**
     * @var array<PeriodicLoop>
     */
    private array $periodicLoops = [];
    /**
     * Start method handler.
     *
     * @internal
     */
    final public function internalStart(APIWrapper $MadelineProto, array $pluginsPrev, array &$pluginsNew, bool $main = true): ?array
    {
        if ($this->startedInternal) {
            return null;
        }
        $this->startMutex ??= new LocalMutex;
        $this->startDeferred ??= new DeferredFuture;
        $startDeferred = $this->startDeferred;
        $lock = $this->startMutex->acquire();
        try {
            if ($this->startedInternal) {
                return null;
            }
            $this->wrapper = $MadelineProto;
            $this->exportNamespaces();

            if (isset(static::$dbProperties)) {
                throw new AssertionError("Please switch to using OrmMappedArray annotations for mapped ORM properties!");
            }
            $this->internalInitDbProperties(
                $this->wrapper->getAPI()->getDbSettings(),
                $this->wrapper->getAPI()->getDbPrefix().'_EventHandler_',
            );

            if ($main) {
                $this->setReportPeers($this->getReportPeers());
            }
            if (method_exists($this, 'onStart')) {
                try {
                    $r = $this->onStart();
                    if ($r instanceof Generator) {
                        throw new AssertionError("Yield cannot be used in onStart!");
                    }
                } catch (\Throwable $e) {
                    $this->wrapper->getAPI()->rethrowInner($e, true);
                }
            }
            if ($main) {
                $this->setReportPeers($this->getReportPeers());
            }
            if ($this instanceof PluginEventHandler && !$this->isPluginEnabled()) {
                return [[], []];
            }

            $API = $this->wrapper->getAPI();
            \assert($API !== null);
            $constructors = $API->getTL()->getConstructors();
            $methods = [];
            $handlers = [];
            $has_any = false;
            foreach ((new ReflectionClass($this))->getMethods(ReflectionMethod::IS_PUBLIC) as $methodRefl) {
                $method = $methodRefl->getName();
                if ($method === 'onAny') {
                    $has_any = true;
                    continue;
                }
                $closure = $this->$method(...);
                $method_name = lcfirst(substr($method, 2));
                if ((
                    ($constructor = $constructors->findByPredicate($method_name)) && $constructor['type'] === 'Update'
                )
                    || $method_name === 'updateBroadcastProgress'
                    || $method_name === 'updateCallStreams'
                    || $method_name === 'updateNewOutgoingEncryptedMessage'
                ) {
                    $methods[$method_name] = [
                        $closure,
                    ];
                    continue;
                }

                array_map(static fn (ReflectionAttribute $attribute) => $attribute->newInstance(), $methodRefl->getAttributes());

                if ($periodic = $methodRefl->getAttributes(Cron::class)) {
                    if (!$this instanceof SimpleEventHandler) {
                        throw new AssertionError("Please extend SimpleEventHandler to use crons!");
                    }
                    $periodic = $periodic[0]->newInstance();
                    $this->periodicLoops[$method] = new PeriodicLoop(
                        static fn (PeriodicLoop $loop): bool => $closure($loop) ?? false,
                        $method,
                        $periodic->period
                    );
                    $this->periodicLoops[$method]->start();
                    continue;
                }
                $filter = $methodRefl->getAttributes(
                    Filter::class,
                    ReflectionAttribute::IS_INSTANCEOF
                )[0] ?? null;
                if (!$filter) {
                    if (!($handler = $methodRefl->getAttributes(Handler::class))) {
                        continue;
                    }
                    $filter = new FilterAllowAll;
                } else {
                    $filter = $filter->newInstance();
                }
                $reflParams = $methodRefl->getParameters();
                if (\count($reflParams) === 0) {
                    throw new AssertionError("Handler method $method must have at least one parameter!");
                }
                $t = $reflParams[0]->getType();
                if ($t === null) {
                    throw new AssertionError("First parameter of handler method $method must have a typehint!");
                }
                $filter = new FiltersAnd(
                    $filter,
                    Filter::fromReflectionType($t)
                );
                $filter = $filter->initialize($this);
                if (!$this instanceof SimpleEventHandler) {
                    throw new AssertionError("Please extend SimpleEventHandler to use filters!");
                }
                $handlers []= static function (Update $update) use ($closure, $filter): void {
                    if ($filter->apply($update)) {
                        $closure($update);
                    }
                };
            }

            $last = null;
            foreach (Tools::validateEventHandlerClass(static::class) as $issue) {
                if ($issue->severe) {
                    $last = $issue;
                }
                $issue->log();
            }
            if ($last) {
                $last->throw();
            }

            if ($has_any) {
                /** @psalm-suppress UndefinedMethod */
                $onAny = $this->onAny(...);
                foreach ($constructors->by_id as $constructor) {
                    if ($constructor['type'] === 'Update' && !isset($methods[$constructor['predicate']])) {
                        $methods[$constructor['predicate']] = [$onAny];
                    }
                }
            }

            $plugins = self::$pluginCache[static::class];
            foreach ($plugins as $class) {
                $refl = new ReflectionClass($class);
                $plugin = $pluginsPrev[$class] ?? $pluginsNew[$class] ?? $refl->newInstanceWithoutConstructor();
                $pluginsNew[$class] = $plugin;
                [$newMethods, $newHandlers] = $plugin->internalStart($MadelineProto, $pluginsPrev, $pluginsNew, false) ?? [[], []];
                if (!$plugin->isPluginEnabled()) {
                    unset($pluginsNew[$class]);
                    continue;
                }
                foreach ($newMethods as $update => $method) {
                    $methods[$update] = array_merge($method, $methods[$update] ?? []);
                }
                $handlers = array_merge($handlers, $newHandlers);
            }

            $this->startedInternal = true;
            return [$methods, $handlers];
        } finally {
            $this->startDeferred = null;
            $startDeferred->complete();
            EventLoop::queue($lock->release(...));
        }
    }
    /**
     * Obtain a PeriodicLoop instance created by the Cron attribute.
     *
     * @param string $name Method name
     *
     * @psalm-mutation-free
     */
    final public function getPeriodicLoop(string $name): ?PeriodicLoop
    {
        return $this->periodicLoops[$name] ?? null;
    }
    /**
     * Obtain all PeriodicLoop instances created by the Cron attribute.
     *
     * @return array<string, PeriodicLoop>
     */
    final public function getPeriodicLoops(): array
    {
        return $this->periodicLoops;
    }
    /**
     * @internal
     */
    final public function waitForInternalStart(): ?Future
    {
        if (!$this->startedInternal && !$this->startDeferred) {
            $this->startDeferred = new DeferredFuture;
        }
        return $this->startDeferred?->getFuture();
    }
    /**
     * Get peers where to send error reports.
     *
     * @return string|int|array<string|int>
     *
     * @psalm-pure
     */
    public function getReportPeers()
    {
        return [];
    }
    /**
     * Obtain a path or a list of paths that will be recursively searched for plugins.
     *
     * Plugin filenames end with Plugin.php, and will be included automatically.
     *
     * @return non-empty-string|non-empty-list<non-empty-string>|null
     *
     * @psalm-pure
     */
    public static function getPluginPaths(): string|array|null
    {
        return null;
    }
    /**
     * Obtain a list of plugin event handlers to use, in addition with those found by getPluginPath.
     *
     * @return array<class-string<EventHandler>>
     *
     * @psalm-pure
     */
    public static function getPlugins(): array
    {
        return [];
    }

    /** @var array<class-string<EventHandler>, list<class-string<PluginEventHandler>>> */
    private static array $pluginCache = [];

    /**
     * Cache a list of plugin event handlers.
     *
     * @internal
     *
     * @param class-string<EventHandler> $class
     */
    final public static function cachePlugins(string $class): void
    {
        if (isset(self::$pluginCache[$class])) {
            return;
        }
        Magic::start(light: false);
        $plugins = $class::getPlugins();
        $plugins = array_values(array_unique($plugins, SORT_REGULAR));
        $plugins = array_merge($plugins, self::internalGetDirectoryPlugins($class));

        foreach ($plugins as $plugin) {
            Assert::classExists($plugin);
            Assert::true(is_subclass_of($plugin, PluginEventHandler::class), "$plugin must extend ".PluginEventHandler::class);
            Assert::notEq($plugin, PluginEventHandler::class);
            Assert::true(str_contains(ltrim($plugin, '\\'), '\\'), "$plugin must be in a namespace!");
            $last = null;
            foreach (Tools::validateEventHandlerClass($plugin) as $issue) {
                if ($issue->severe) {
                    $last = $issue;
                }
                $issue->log();
            }
            if ($last) {
                $last->throw();
            }
        }

        self::$pluginCache[$class] = $plugins;

        foreach ($plugins as $plugin) {
            self::cachePlugins($plugin);
        }
    }

    private static array $checkedPaths = [];
    /**
     * Cache a list of plugin event handlers.
     *
     * @param class-string<EventHandler> $class
     */
    private static function internalGetDirectoryPlugins(string $class): array
    {
        if (is_subclass_of($class, PluginEventHandler::class)) {
            return [];
        }

        $paths = $class::getPluginPaths();
        if (\is_string($paths)) {
            $paths = [$paths];
        } elseif ($paths === null) {
            $paths = [];
        } else {
            $paths = array_values($paths);
        }
        foreach ($paths as $k => &$path) {
            $pathNew = realpath($path);
            if ($pathNew === false) {
                $pathNew = realpath(\dirname((new ReflectionClass($class))->getFileName()).DIRECTORY_SEPARATOR.$path);
                if ($pathNew === false) {
                    unset($paths[$k]);
                    Logger::log(\sprintf(Lang::$current_lang['plugin_path_does_not_exist'], $path), Logger::FATAL_ERROR);
                    continue;
                }
            }
            $path = $pathNew;
        }

        if (!$paths) {
            return [];
        }

        $pluginsTemp = [];
        $recurse = static function (string $path, string $namespace = 'MadelinePlugin') use (&$recurse, &$pluginsTemp): void {
            foreach (listFiles($path) as $file) {
                $file = $path.DIRECTORY_SEPARATOR.$file;
                if (isDirectory($file)) {
                    $recurse($file, $namespace.'\\'.basename($file));
                } elseif (isFile($file) && str_ends_with($file, ".php")) {
                    $file = realpath($file);
                    $fileName = basename($file, '.php');
                    if ($fileName === 'functions') {
                        require $file;
                        continue;
                    }
                    if (str_contains($fileName, '.')) {
                        continue;
                    }
                    $class = $namespace.'\\'.$fileName;
                    $refl = new ReflectionClass($class);
                    if ($refl->getFileName() !== $file) {
                        throw new AssertionError("$class was not defined when including $file, the same plugin is present in multiple plugin paths/composer!");
                    }
                    if (class_exists($class)
                        && !$refl->isAbstract()
                        && is_subclass_of($class, PluginEventHandler::class)
                    ) {
                        self::cachePlugins($class);
                        $pluginsTemp []= $class;
                    }
                }
            }
        };

        $plugins = [];
        try {
            self::$includingPlugins = true;
            foreach ($paths as $p) {
                if (isset(self::$checkedPaths[$p])) {
                    $plugins = array_merge($plugins, self::$checkedPaths[$p]);
                    continue;
                }

                spl_autoload_register(static function (string $class) use ($p): void {
                    if (!str_starts_with($class, 'MadelinePlugin\\')) {
                        return;
                    }
                    // Has leading /
                    $file = $p.str_replace('\\', DIRECTORY_SEPARATOR, substr($class, 14)).'.php';
                    if (file_exists($file)) {
                        require $file;
                        if (!class_exists($class) && !interface_exists($class) && !trait_exists($class) && !enum_exists($class)) {
                            throw new AssertionError("$class was not defined when including $file!");
                        }
                    }
                });

                $recurse($p);
                self::$checkedPaths[$p] = $pluginsTemp;
                $plugins = array_merge($plugins, $pluginsTemp);
                $pluginsTemp = [];
            }
        } finally {
            self::$includingPlugins = false;
        }

        return $plugins;
    }
    public function __destruct()
    {
        if (method_exists($this, 'onStop')) {
            try {
                $this->onStop();
            } catch (\Throwable $e) {
                $this->wrapper->getAPI()->rethrowInner($e);
            }
        }
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\VoIP;

/**
 * tgcalls protocol version, as advertised in
 * [phoneCallProtocol](https://core.telegram.org/constructor/phoneCallProtocol).`library_versions`.
 *
 * Every version differs along three independent axes, which is what the accessors below expose:
 *
 * - the **transport**: the legacy libtgvoip reflector protocol, or WebRTC;
 * - the **media negotiation**: a plain SDP offer/answer (tgcalls' `InstanceV2ReferenceImpl`) or
 *   the structured `NegotiateChannels` exchange (`InstanceV2Impl`);
 * - the **signaling framing**: the reliable sequenced framing of tgcalls' `EncryptedConnection`,
 *   or a bare encrypted packet, optionally gzipped and carried over an SCTP association.
 *
 * @internal
 */
enum SignalingProtocolVersion: string
{
    /** libtgvoip, the original UDP reflector protocol. */
    case V2_4_4 = '2.4.4';
    /** libtgvoip with the newer packet layout. */
    case V2_7_7 = '2.7.7';

    /** tgcalls `InstanceV2Impl`, uncompressed raw signaling packets. */
    case V7 = '7.0.0';
    /** tgcalls `InstanceV2Impl`, reliably framed signaling. */
    case V8 = '8.0.0';
    /** tgcalls `InstanceV2Impl`, reliably framed signaling. */
    case V9 = '9.0.0';

    /** tgcalls `InstanceV2ReferenceImpl`, SDP over reliably framed signaling. */
    case V10 = '10.0.0';
    /** tgcalls `InstanceV2ReferenceImpl`, SDP over gzipped SCTP-framed signaling. */
    case V11 = '11.0.0';

    /** tgcalls `InstanceV2Impl`, gzipped SCTP-framed signaling. */
    case V12 = '12.0.0';
    /** tgcalls `InstanceV2Impl`, gzipped SCTP-framed signaling; what Telegram Web speaks. */
    case V13 = '13.0.0';

    /**
     * Every version MadelineProto can speak, ordered from most to least preferred.
     *
     * Modern WebRTC comes first; libtgvoip is only a fallback for very old peers.
     */
    public const SUPPORTED = [
        '13.0.0', '12.0.0', '11.0.0', '10.0.0', '9.0.0', '8.0.0', '7.0.0', '2.7.7', '2.4.4',
    ];

    /**
     * Every version MadelineProto can speak, ordered from most to least preferred.
     *
     * @return non-empty-list<string>
     *
     * @psalm-pure
     */
    public static function supported(): array
    {
        return self::SUPPORTED;
    }

    /**
     * Whether MadelineProto implements this version end to end.
     *
     * @psalm-pure
     */
    public function isImplemented(): bool
    {
        return true;
    }

    /**
     * Whether this version uses the legacy libtgvoip reflector transport instead of WebRTC.
     *
     * @psalm-mutation-free
     */
    public function isLegacy(): bool
    {
        return $this === self::V2_4_4 || $this === self::V2_7_7;
    }

    /**
     * Whether media is negotiated with a plain SDP offer/answer.
     *
     * The alternative is the structured `NegotiateChannels` exchange of `InstanceV2Impl`.
     *
     * @psalm-mutation-free
     */
    public function usesSdp(): bool
    {
        return $this === self::V10 || $this === self::V11;
    }

    /**
     * Whether signaling uses the reliable sequenced framing of tgcalls' `EncryptedConnection`.
     *
     * When false, each message is a single bare encrypted packet, because the channel underneath
     * already guarantees delivery.
     *
     * @psalm-mutation-free
     */
    public function usesReliableFraming(): bool
    {
        return match ($this) {
            self::V8, self::V9, self::V10 => true,
            default => false,
        };
    }

    /**
     * Whether signaling payloads are gzipped before encryption.
     *
     * @psalm-mutation-free
     */
    public function supportsCompression(): bool
    {
        return match ($this) {
            self::V11, self::V12, self::V13 => true,
            default => false,
        };
    }

    /**
     * Whether signaling is carried over an SCTP association rather than sent as bare datagrams.
     *
     * @psalm-mutation-free
     */
    public function usesSctp(): bool
    {
        return $this->supportsCompression();
    }

    /**
     * Pick the negotiated version out of a
     * [phoneCallProtocol](https://core.telegram.org/constructor/phoneCallProtocol).
     *
     * Returns null when the other party advertises a non-empty list that shares no version with
     * us: the call cannot work in that case, and failing loudly beats a connection that silently
     * never carries any media.
     */
    public static function fromProtocol(array $protocol): ?self
    {
        $advertised = [];
        /** @var mixed $version */
        foreach ($protocol['library_versions'] ?? [] as $version) {
            $advertised[] = (string) $version;
        }
        // Pick by *our* preference order: the other party may well advertise versions we cannot
        // speak, and the server does not always narrow the list down to a single entry.
        foreach (self::supported() as $version) {
            if (\in_array($version, $advertised, true)) {
                return self::from($version);
            }
        }
        // An empty list means the other end never advertised anything, which older clients do;
        // assume the oldest version, which is what tgcalls itself falls back to.
        return $advertised === [] ? self::V2_4_4 : null;
    }
}
<?php

declare(strict_types=1);

/**
 * Call state module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\VoIP;

/**
 * VoIP protcol state.
 */
enum VoIPState: int
{
    case CREATED = 0;
    case WAIT_INIT = 1;
    case WAIT_INIT_ACK = 2;
    case WAIT_PONG = 3;
    case WAIT_STREAM_INIT = 4;
    case ESTABLISHED = 5;
}
<?php

declare(strict_types=1);

/**
 * Call state module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\VoIP;

enum CallState
{
    /** The call was requested */
    case REQUESTED;
    /** An incoming call */
    case INCOMING;
    /** The call was accepted */
    case ACCEPTED;
    /** The call was confirmed */
    case CONFIRMED;
    /** The call is ongoing */
    case RUNNING;
    /** The call has ended */
    case ENDED;
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\VoIP;

use AssertionError;
use danog\MadelineProto\Logger;
use danog\MadelineProto\Tgcalls\LegacyController;
use danog\MadelineProto\Tools;

/**
 * Manages packing and unpacking of messages, and the list of sent and received messages.
 *
 * @internal
 */
final class MessageHandler
{
    private int $outSeqNo = 0;
    private int $inSeqNo = 0;

    private int $acksForSent = 0;
    private int $acksForReceived = 0;

    public int $peerVersion = 0;

    /**
     * @psalm-mutation-free
     */
    public function __construct(
        public readonly LegacyController $instance,
        public readonly string $callID
    ) {
    }
    /**
     * @psalm-mutation-free
     */
    public function getLastSentSeq(): int
    {
        return $this->outSeqNo-1;
    }
    /**
     * @psalm-external-mutation-free
     */
    private static function pack_string(string $object): string
    {
        $l = \strlen($object);
        $concat = '';
        if ($l <= 253) {
            $concat .= \chr($l);
            $concat .= $object;
            $concat .= pack('@'.Tools::posmod(-$l - 1, 4));
        } else {
            $concat .= \chr(254);
            $concat .= substr(Tools::packSignedInt($l), 0, 3);
            $concat .= $object;
            $concat .= pack('@'.Tools::posmod(-$l, 4));
        }

        return $concat;
    }
    public function encryptPacket(array $args, bool $init = false): string
    {
        $message = '';
        switch ($args['_']) {
            // streamTypeSimple codec:int8 = StreamType;
            //
            // packetInit#1 protocol:int min_protocol:int flags:# data_saving_enabled:flags.0?true audio_streams:byteVector<streamTypeSimple> video_streams:byteVector<streamTypeSimple> = Packet;
            case LegacyController::PKT_INIT:
                $message .= Tools::packSignedInt($args['protocol']);
                $message .= Tools::packSignedInt($args['min_protocol']);
                $flags = 0;
                $flags = isset($args['data_saving_enabled']) && $args['data_saving_enabled'] ? $flags | 1 : $flags & ~1;
                $message .= Tools::packUnsignedInt($flags);
                $message .= \chr(\count($args['audio_streams']));
                foreach ($args['audio_streams'] as $codec) {
                    $message .= $codec;
                }
                $message .= \chr(0);
                $message .= \chr(\count($args['video_streams']));
                foreach ($args['video_streams'] as $codec) {
                    $message .= \chr($codec);
                }
                break;
                // streamType id:int8 type:int8 codec:int8 frame_duration:int16 enabled:int8 = StreamType;
                //
                // packetInitAck#2 protocol:int min_protocol:int all_streams:byteVector<streamType> = Packet;
            case LegacyController::PKT_INIT_ACK:
                $message .= Tools::packSignedInt($args['protocol']);
                $message .= Tools::packSignedInt($args['min_protocol']);
                $message .= \chr(\count($args['all_streams']));
                foreach ($args['all_streams'] as $stream) {
                    $message .= \chr($stream['id']);
                    $message .= \chr($stream['type']);
                    $message .= $stream['codec'];
                    $message .= pack('v', $stream['frame_duration']);
                    $message .= \chr($stream['enabled']);
                }
                break;
                // streamTypeState id:int8 enabled:int8 = StreamType;
                // packetStreamState#3 state:streamTypeState = Packet;
            case LegacyController::PKT_STREAM_STATE:
                $message .= \chr($args['id']);
                $message .= \chr($args['enabled'] ? 1 : 0);
                break;
                // streamData flags:int2 stream_id:int6 has_more_flags:flags.1?true length:(flags.0?int16:int8) timestamp:int data:byteArray = StreamData;
                // packetStreamData#4 stream_data:streamData = Packet;
            case LegacyController::PKT_STREAM_DATA:
                $length = \strlen($args['data']);
                $flags = 0;
                $flags = $length > 255 ? $flags | 1 : $flags & ~1;
                $flags = isset($args['has_more_flags']) && $args['has_more_flags'] ? $flags | 2 : $flags & ~2;
                $flags = $flags << 6;
                $flags = $flags | $args['stream_id'];
                $message .= \chr($flags);
                $message .= $length > 255 ? pack('v', $length) : \chr($length);
                $message .= Tools::packUnsignedInt($args['timestamp']);
                $message .= $args['data'];
                break;
                /*case LegacyController::PKT_UPDATE_STREAMS:
                    break;
                case LegacyController::PKT_PING:
                    break;*/
            case LegacyController::PKT_PONG:
                $message .= Tools::packUnsignedInt($args['out_seq_no']);
                break;
            case LegacyController::PKT_STREAM_DATA_X2:
                for ($x = 0; $x < 2; $x++) {
                    $length = \strlen($args[$x]['data']);
                    $flags = 0;
                    $flags = $length > 255 ? $flags | 1 : $flags & ~1;
                    $flags = isset($args[$x]['has_more_flags']) && $args[$x]['has_more_flags'] ? $flags | 2 : $flags & ~2;
                    $flags = $flags << 6;
                    $flags = $flags | $args[$x]['stream_id'];
                    $message .= \chr($flags);
                    $message .= $length > 255 ? pack('v', $length) : \chr($length);
                    $message .= Tools::packUnsignedInt($args[$x]['timestamp']);
                    $message .= $args[$x]['data'];
                }
                break;
            case LegacyController::PKT_STREAM_DATA_X3:
                for ($x = 0; $x < 3; $x++) {
                    $length = \strlen($args[$x]['data']);
                    $flags = 0;
                    $flags = $length > 255 ? $flags | 1 : $flags & ~1;
                    $flags = isset($args[$x]['has_more_flags']) && $args[$x]['has_more_flags'] ? $flags | 2 : $flags & ~2;
                    $flags = $flags << 6;
                    $flags = $flags | $args[$x]['stream_id'];
                    $message .= \chr($flags);
                    $message .= $length > 255 ? pack('v', $length) : \chr($length);
                    $message .= Tools::packUnsignedInt($args[$x]['timestamp']);
                    $message .= $args[$x]['data'];
                }
                break;
                // packetLanEndpoint#A address:int port:int = Packet;
            case LegacyController::PKT_LAN_ENDPOINT:
                $message .= Tools::packSignedInt($args['address']);
                $message .= Tools::packSignedInt($args['port']);
                break;
                // packetNetworkChanged#B flags:# data_saving_enabled:flags.0?true = Packet;
            case LegacyController::PKT_NETWORK_CHANGED:
                $message .= Tools::packSignedInt(isset($args['data_saving_enabled']) && $args['data_saving_enabled'] ? 1 : 0);
                break;
                // packetSwitchPreferredRelay#C relay_id:long = Packet;
            case LegacyController::PKT_SWITCH_PREF_RELAY:
                $message .= Tools::packSignedLong($args['relay_d']);
                break;
                /*case LegacyController::PKT_SWITCH_TO_P2P:
                    break;
                case LegacyController::PKT_NOP:
                    break;*/
        }

        if ($this->peerVersion >= 8 || (!$this->peerVersion)) {
            $payload = \chr($args['_']);
            $payload .= Tools::packUnsignedInt($this->inSeqNo);
            $payload .= Tools::packUnsignedInt($init ? 0 : $this->outSeqNo);
            $payload .= Tools::packUnsignedInt($this->acksForReceived);
            $payload .= \chr(0);
            $payload .= $message;
        } elseif (\in_array($this->instance->getVoIPState(), [VoIPState::WAIT_INIT, VoIPState::WAIT_INIT_ACK], true)) {
            $payload = LegacyController::TLID_DECRYPTED_AUDIO_BLOCK;
            $payload .= Tools::random(8);
            $payload .= \chr(7);
            $payload .= Tools::random(7);
            $flags = 0;
            $flags = $flags | 4; // call_id
            $flags = $flags | 16; // seqno
            $flags = $flags | 32; // ack mask
            $flags = $flags | 8; // proto
            $flags = isset($args['extra']) ? $flags | 2 : $flags & ~2; // extra
            $flags = \strlen($message) ? $flags | 1 : $flags & ~1; // raw_data
            $flags = $flags | ($args['_'] << 24);
            $payload .= Tools::packUnsignedInt($flags);
            $payload .= $this->callID;
            $payload .= Tools::packUnsignedInt($this->inSeqNo);
            $payload .= Tools::packUnsignedInt($init ? 0 : $this->outSeqNo);
            $payload .= Tools::packUnsignedInt($this->acksForReceived);
            $payload .= LegacyController::PROTO_ID;
            if ($flags & 2) {
                $payload .= $this->pack_string($args['extra']);
            }
            if ($flags & 1) {
                $payload .= $this->pack_string($message);
            }
        } else {
            $payload = LegacyController::TLID_SIMPLE_AUDIO_BLOCK;
            $payload .= Tools::random(8);
            $payload .= \chr(7);
            $payload .= Tools::random(7);
            $message = \chr($args['_']).Tools::packUnsignedInt($this->inSeqNo).Tools::packUnsignedInt($init ? 0 : $this->outSeqNo).Tools::packUnsignedInt($this->acksForReceived).$message;

            $payload .= $this->pack_string($message);
        }
        if (!$init) {
            $this->outSeqNo++;
        }

        return $payload;
    }

    public function shouldSkip(int $last_ack_id, int $packet_seq_no, int $ack_mask): bool
    {
        if ($packet_seq_no > $this->inSeqNo) {
            $diff = $packet_seq_no - $this->inSeqNo;
            if ($diff > 31) {
                $this->acksForReceived = 0;
            } else {
                $this->acksForReceived = (($this->acksForReceived << ($diff+1)) & 0xFFFF_FFFF) | 1;
            }
            $this->inSeqNo = $packet_seq_no;
        } elseif (($diff = $this->inSeqNo - $packet_seq_no) < 32) {
            if ($this->acksForReceived & (1 << ($diff+1))) {
                Logger::log("Got duplicate $packet_seq_no");
                return false;
            }
            $this->acksForReceived |= 1 << ($diff+1);
            $this->acksForReceived &= 0xFFFF_FFFF;
        } else {
            Logger::log("Packet $packet_seq_no is out of order and too late");
            return false;
        }
        $this->inSeqNo = $packet_seq_no;
        $this->acksForSent = $ack_mask;
        return true;
    }

    /**
     * @psalm-mutation-free
     */
    public function acked(int $seq): bool
    {
        $diff = $this->outSeqNo - $seq;
        if ($diff > 31) {
            throw new AssertionError("Already forgot about packet!");
        }
        return (bool) ($this->acksForReceived & (1 << ($diff+1)));
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\VoIP;

use Amp\DeferredFuture;
use Amp\Socket\ConnectContext;
use Amp\Socket\Socket;
use Amp\Websocket\ClosedException;
use danog\MadelineProto\Lang;
use danog\MadelineProto\Logger;
use danog\MadelineProto\MTProtoTools\Crypt;
use danog\MadelineProto\NothingInTheSocketException;
use danog\MadelineProto\Stream\BufferedStreamInterface;
use danog\MadelineProto\Stream\Common\BufferedRawStream;
use danog\MadelineProto\Stream\Common\UdpBufferedStream;
use danog\MadelineProto\Stream\ConnectionContext;
use danog\MadelineProto\Stream\MTProtoTransport\ObfuscatedStream;
use danog\MadelineProto\Stream\Transport\DefaultStream;
use danog\MadelineProto\Tgcalls\LegacyController;
use danog\MadelineProto\Tools;
use Exception;

use function Amp\delay;

/**
 * @internal
 */
final class Endpoint
{
    /**
     * The socket.
     */
    private ?BufferedStreamInterface $socket = null;
    private ?DeferredFuture $connectFuture = null;
    /**
     * Create endpoint.
     */
    public function __construct(
        public readonly bool $udp,
        private readonly string $ip,
        private readonly int $port,
        private readonly string $peerTag,
        private readonly bool $reflector,
        private readonly bool $creator,
        private readonly string $authKey,
        private readonly MessageHandler $handler
    ) {
        $this->connectFuture = new DeferredFuture;
    }
    public function __wakeup(): void
    {
        $this->udp ??= true;
        $this->connectFuture = new DeferredFuture;
    }
    public function connect(): void
    {
        try {
            $settings = $this->handler->instance->API->settings->getConnection();

            $context = new ConnectionContext;
            $context->setUri($this->__toString())
                    ->setSocketContext(
                        (new ConnectContext())
                        ->withConnectTimeout($settings->getTimeout())->withBindTo($settings->getBindTo())
                    )
                    ->addStream(DefaultStream::class);
            if ($this->udp) {
                $context->addStream(UdpBufferedStream::class);
            } else {
                $context->addStream(BufferedRawStream::class)
                        ->addStream(ObfuscatedStream::class);
            }
            $this->socket = $context->getStream();
            $f = $this->connectFuture;
            $this->connectFuture = null;
            \assert($f !== null);
            $f->complete();
            if ($this->udp) {
                $this->udpPing();
            }
        } catch (\Throwable $e) {
            $this->socket = null;
            $this->connectFuture?->complete();
            throw $e;
        }
    }
    /**
     * @psalm-mutation-free
     */
    public function __sleep(): array
    {
        $vars = get_object_vars($this);
        unset($vars['socket'], $vars['connectFuture']);

        return array_keys($vars);
    }

    /**
     * @psalm-mutation-free
     */
    public function __toString(): string
    {
        return $this->udp
            ? "udp://{$this->ip}:{$this->port}"
            : "tcp://{$this->ip}:{$this->port}";
    }
    /**
     * Disconnect from endpoint.
     */
    public function disconnect(): void
    {
        if ($this->socket !== null) {
            $this->socket->disconnect();
            $this->socket = null;
        }
    }

    /**
     * @psalm-external-mutation-free
     */
    private static function unpack_string($stream): string
    {
        $l = \ord(stream_get_contents($stream, 1));
        if ($l > 254) {
            throw new Exception(Lang::$current_lang['length_too_big']);
        }
        if ($l === 254) {
            $long_len = unpack('V', stream_get_contents($stream, 3).\chr(0))[1];
            $x = stream_get_contents($stream, $long_len);
            $resto = Tools::posmod(-$long_len, 4);
            if ($resto > 0) {
                stream_get_contents($stream, $resto);
            }
        } else {
            $x = stream_get_contents($stream, $l);
            $resto = Tools::posmod(-($l + 1), 4);
            if ($resto > 0) {
                stream_get_contents($stream, $resto);
            }
        }
        return $x;
    }
    /**
     * Read packet.
     */
    public function read(): ?array
    {
        do {
            try {
                $payload = $this->socket->getReadBuffer($l);
            } catch (NothingInTheSocketException) {
                return null;
            }

            $pos = 0;
            if ($this->handler->peerVersion < 9 || $this->reflector) {
                if ($payload->bufferRead(16) !== $this->peerTag) {
                    Logger::log('Received packet has wrong peer tag', Logger::ERROR);
                    continue;
                }
                $pos = 16;
            }
            $result = [];
            if (($head = $payload->bufferRead(12)) === "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF") {
                switch ($crc = $payload->bufferRead(4)) {
                    case LegacyController::TLID_REFLECTOR_SELF_INFO:
                        $result['_'] = 'reflectorSelfInfo';
                        $result['date'] = Tools::unpackSignedInt($payload->bufferRead(4));
                        $result['query_id'] = Tools::unpackSignedLong($payload->bufferRead(8));
                        $result['my_ip'] = $payload->bufferRead(16);
                        $result['my_port'] = Tools::unpackSignedInt($payload->bufferRead(4));
                        return $result;
                    case LegacyController::TLID_REFLECTOR_PEER_INFO:
                        $result['_'] = 'reflectorPeerInfo';
                        $result['my_address'] = Tools::unpackSignedInt($payload->bufferRead(4));
                        $result['my_port'] = Tools::unpackSignedInt($payload->bufferRead(4));
                        $result['peer_address'] = Tools::unpackSignedInt($payload->bufferRead(4));
                        $result['peer_port'] = Tools::unpackSignedInt($payload->bufferRead(4));
                        return $result;
                }
                Logger::log('Unknown unencrypted packet received: '.bin2hex($crc), Logger::ERROR);
                continue;
            }
            $message_key = $head.$payload->bufferRead(4);
            [$aes_key, $aes_iv] = Crypt::kdf($message_key, $this->authKey, !$this->creator);
            $encrypted_data = $payload->bufferRead($l-($pos+16));
            $packet = Crypt::igeDecrypt($encrypted_data, $aes_key, $aes_iv);

            if ($message_key != substr(hash('sha256', substr($this->authKey, 88 + ($this->creator ? 8 : 0), 32).$packet, true), 8, 16)) {
                Logger::log('msg_key mismatch!', Logger::ERROR);
                continue;
            }

            $innerLen = unpack('v', substr($packet, 0, 2))[1];
            if ($innerLen > \strlen($packet)) {
                Logger::log('Received packet has wrong inner length!', Logger::ERROR);
                continue;
            }
            $packet = substr($packet, 2);

            $payload = fopen('php://memory', 'rw+b');
            fwrite($payload, $packet);
            fseek($payload, 0);

            $result = [];
            switch ($crc = stream_get_contents($payload, 4)) {
                case LegacyController::TLID_DECRYPTED_AUDIO_BLOCK:
                    stream_get_contents($payload, 8);
                    $this->unpack_string($payload);
                    $flags = unpack('V', stream_get_contents($payload, 4))[1];
                    $result['_'] = $flags >> 24;
                    if ($flags & 4) {
                        if (stream_get_contents($payload, 16) !== $this->handler->callID) {
                            Logger::log('Call ID mismatch', Logger::ERROR);
                            continue 2;
                        }
                    }
                    if ($flags & 16) {
                        $in_seq_no = unpack('V', stream_get_contents($payload, 4))[1];
                        $out_seq_no = unpack('V', stream_get_contents($payload, 4))[1];
                    }
                    if ($flags & 32) {
                        $ack_mask = unpack('V', stream_get_contents($payload, 4))[1];
                    }
                    if ($flags & 8) {
                        if (stream_get_contents($payload, 4) !== LegacyController::PROTO_ID) {
                            Logger::log('Protocol mismatch', Logger::ERROR);
                            continue 2;
                        }
                    }
                    if ($flags & 2) {
                        $result['extra'] = $this->unpack_string($payload);
                    }
                    $message = fopen('php://memory', 'rw+b');

                    if ($flags & 1) {
                        fwrite($message, $this->unpack_string($payload));
                        fseek($message, 0);
                    }
                    break;
                case LegacyController::TLID_SIMPLE_AUDIO_BLOCK:
                    stream_get_contents($payload, 8);
                    $this->unpack_string($payload);
                    $flags = unpack('V', stream_get_contents($payload, 4))[1];

                    $message = fopen('php://memory', 'rw+b');
                    fwrite($message, $this->unpack_string($payload));
                    fseek($message, 0);
                    $result['_'] = \ord(stream_get_contents($message, 1));
                    $in_seq_no = unpack('V', stream_get_contents($message, 4))[1];
                    $out_seq_no = unpack('V', stream_get_contents($message, 4))[1];
                    $ack_mask = unpack('V', stream_get_contents($message, 4))[1];

                    break;
                default:
                    if ($this->handler->peerVersion >= 8 || (!$this->handler->peerVersion)) {
                        fseek($payload, 0);
                        $result['_'] = \ord(stream_get_contents($payload, 1));
                        $in_seq_no = unpack('V', stream_get_contents($payload, 4))[1];
                        $out_seq_no = unpack('V', stream_get_contents($payload, 4))[1];
                        $ack_mask = unpack('V', stream_get_contents($payload, 4))[1];
                        $flags = \ord(stream_get_contents($payload, 1));
                        if ($flags & 1) {
                            $result['extra'] = [];
                            $count = \ord(stream_get_contents($payload, 1));
                            for ($x = 0; $x < $count; $x++) {
                                $len = \ord(stream_get_contents($payload, 1));
                                $result['extra'][]= stream_get_contents($payload, $len);
                            }
                        }
                        $message = fopen('php://memory', 'rw+b');

                        fwrite($message, stream_get_contents($payload));
                        fseek($message, 0);
                    } else {
                        Logger::log('Unknown packet received: '.bin2hex($crc), Logger::ERROR);
                        continue 2;
                    }
            }
            if (isset($in_seq_no) && isset($out_seq_no) && !$this->handler->shouldSkip($in_seq_no, $out_seq_no, $ack_mask)) {
                continue;
            }
            switch ($result['_']) {
                // streamTypeSimple codec:int8 = StreamType;
                //
                // packetInit#1 protocol:int min_protocol:int flags:# data_saving_enabled:flags.0?true audio_streams:byteVector<streamTypeSimple> video_streams:byteVector<streamTypeSimple> = Packet;
                case LegacyController::PKT_INIT:
                    $result['protocol'] = Tools::unpackSignedInt(stream_get_contents($message, 4));
                    $result['min_protocol'] = Tools::unpackSignedInt(stream_get_contents($message, 4));
                    $flags = unpack('V', stream_get_contents($message, 4))[1];
                    $result['data_saving_enabled'] = (bool) ($flags & 1);
                    $result['audio_streams'] = [];
                    $length = \ord(stream_get_contents($message, 1));
                    for ($x = 0; $x < $length; $x++) {
                        $result['audio_streams'][$x] = stream_get_contents($message, 4);
                    }
                    $this->handler->peerVersion = $result['protocol'];
                    break;
                    // streamType id:int8 type:int8 codec:int8 frame_duration:int16 enabled:int8 = StreamType;
                    //
                    // packetInitAck#2 protocol:int min_protocol:int all_streams:byteVector<streamType> = Packet;
                case LegacyController::PKT_INIT_ACK:
                    $result['protocol'] = Tools::unpackSignedInt(stream_get_contents($message, 4));
                    $result['min_protocol'] = Tools::unpackSignedInt(stream_get_contents($message, 4));
                    $result['all_streams'] = [];
                    $length = \ord(stream_get_contents($message, 1));
                    for ($x = 0; $x < $length; $x++) {
                        $result['all_streams'][$x]['id'] = \ord(stream_get_contents($message, 1));
                        $result['all_streams'][$x]['type'] = \ord(stream_get_contents($message, 1));
                        $result['all_streams'][$x]['codec'] = stream_get_contents($message, 4);
                        $result['all_streams'][$x]['frame_duration'] = unpack('v', stream_get_contents($message, 2))[1];
                        $result['all_streams'][$x]['enabled'] = \ord(stream_get_contents($message, 1));
                    }

                    break;
                    // streamTypeState id:int8 enabled:int8 = StreamType;
                    // packetStreamState#3 state:streamTypeState = Packet;
                case LegacyController::PKT_STREAM_STATE:
                    $result['id'] = \ord(stream_get_contents($message, 1));
                    $result['enabled'] = \ord(stream_get_contents($message, 1));
                    break;
                case LegacyController::PKT_UPDATE_STREAMS:
                    continue 2;
                case LegacyController::PKT_PING:
                    $result['out_seq_no'] = $out_seq_no;
                    break;
                case LegacyController::PKT_PONG:
                    if (fstat($payload)['size'] - ftell($payload)) {
                        $result['out_seq_no'] = unpack('V', stream_get_contents($payload, 4))[1];
                    }
                    break;
                    // streamData flags:int2 stream_id:int6 has_more_flags:flags.1?true length:(flags.0?int16:int8) timestamp:int data:byteArray = StreamData;
                    // packetStreamData#4 stream_data:streamData = Packet;
                case LegacyController::PKT_STREAM_DATA:
                    $flags = \ord(stream_get_contents($message, 1));
                    $result[0]['stream_id'] = $flags & 0x3F;
                    $flags = ($flags & 0xC0) >> 6;
                    $result[0]['has_more_flags'] = (bool) ($flags & 2);
                    $length = $flags & 1 ? unpack('v', stream_get_contents($message, 2))[1] : \ord(stream_get_contents($message, 1));
                    $result[0]['timestamp'] = unpack('V', stream_get_contents($message, 4))[1];
                    $result[0]['data'] = stream_get_contents($message, $length);
                    break;
                case LegacyController::PKT_STREAM_DATA_X2:
                    for ($x = 0; $x < 2; $x++) {
                        $flags = \ord(stream_get_contents($message, 1));
                        $result[$x]['stream_id'] = $flags & 0x3F;
                        $flags = ($flags & 0xC0) >> 6;
                        $result[$x]['has_more_flags'] = (bool) ($flags & 2);
                        $length = $flags & 1 ? unpack('v', stream_get_contents($message, 2))[1] : \ord(stream_get_contents($message, 1));
                        $result[$x]['timestamp'] = unpack('V', stream_get_contents($message, 4))[1];
                        $result[$x]['data'] = stream_get_contents($message, $length);
                    }
                    break;
                case LegacyController::PKT_STREAM_DATA_X3:
                    for ($x = 0; $x < 3; $x++) {
                        $flags = \ord(stream_get_contents($message, 1));
                        $result[$x]['stream_id'] = $flags & 0x3F;
                        $flags = ($flags & 0xC0) >> 6;
                        $result[$x]['has_more_flags'] = (bool) ($flags & 2);
                        $length = $flags & 1 ? unpack('v', stream_get_contents($message, 2))[1] : \ord(stream_get_contents($message, 1));
                        $result[$x]['timestamp'] = unpack('V', stream_get_contents($message, 4))[1];
                        $result[$x]['data'] = stream_get_contents($message, $length);
                    }
                    break;
                    // packetLanEndpoint#A address:int port:int = Packet;
                case LegacyController::PKT_LAN_ENDPOINT:
                    $result['address'] = unpack('V', stream_get_contents($payload, 4))[1];
                    $result['port'] = unpack('V', stream_get_contents($payload, 4))[1];
                    break;
                    // packetNetworkChanged#B flags:# data_saving_enabled:flags.0?true = Packet;
                case LegacyController::PKT_NETWORK_CHANGED:
                    $result['data_saving_enabled'] = (bool) (unpack('V', stream_get_contents($payload, 4))[1] & 1);
                    break;
                    // packetSwitchPreferredRelay#C relay_id:long = Packet;
                case LegacyController::PKT_SWITCH_PREF_RELAY:
                    $result['relay_id'] = Tools::unpackSignedLong(stream_get_contents($payload, 8));
                    break;
                case LegacyController::PKT_SWITCH_TO_P2P:
                    break;
                case LegacyController::PKT_NOP:
                    break;
                default:
                    Logger::log('Unknown packet received: '.$result['_'], Logger::ERROR);
                    continue 2;
            }
            return $result;
        } while (true);
    }
    public function writeReliably(array $data): bool
    {
        do {
            $payload = $this->handler->encryptPacket($data);
            $seqno = $this->handler->getLastSentSeq();
            if (!$this->write($payload)) {
                return false;
            }
            delay(0.2);
            if ($this->handler->acked($seqno)) {
                return true;
            }
        } while (true);
    }
    /**
     * Write data.
     */
    public function write(string $payload): bool
    {
        $this->connectFuture?->getFuture()?->await();
        if ($this->socket === null) {
            return false;
        }
        $plaintext = pack('v', \strlen($payload)).$payload;
        $padding = 16 - (\strlen($plaintext) % 16);
        if ($padding < 16) {
            $padding += 16;
        }
        $plaintext .= Tools::random($padding);
        $message_key = substr(hash('sha256', substr($this->authKey, 88 + ($this->creator ? 0 : 8), 32).$plaintext, true), 8, 16);
        [$aes_key, $aes_iv] = Crypt::kdf($message_key, $this->authKey, $this->creator);
        $payload = $message_key.Crypt::igeEncrypt($plaintext, $aes_key, $aes_iv);

        if ($this->handler->peerVersion < 9 || $this->reflector) {
            $payload = $this->peerTag.$payload;
        }

        try {
            $this->socket->getWriteBuffer(\strlen($payload))->bufferWrite($payload);
        } catch (ClosedException) {
            $this->socket = null;
            return false;
        }
        return true;
    }
    private function udpPing(): bool
    {
        if ($this->socket === null) {
            return false;
        }
        $data = $this->peerTag.Tools::packSignedLong(-1).Tools::packSignedInt(-1).Tools::packSignedInt(-2).Tools::random(8);
        try {
            $this->socket->getWriteBuffer(\strlen($data))->bufferWrite($data);
        } catch (ClosedException) {
            $this->socket = null;
            return false;
        }
        return true;
    }
    public function sendInit(): bool
    {
        return $this->write($this->handler->encryptPacket([
            '_' => LegacyController::PKT_INIT,
            'protocol' => LegacyController::PROTOCOL_VERSION,
            'min_protocol' => LegacyController::MIN_PROTOCOL_VERSION,
            'audio_streams' => [LegacyController::CODEC_OPUS],
            'video_streams' => [],
        ]));
    }
}
<?php

declare(strict_types=1);

/**
 * AuthKeyHandler module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\VoIP;

use Amp\ByteStream\ReadableStream;
use Amp\ByteStream\WritableStream;
use Amp\Cancellation;
use Amp\DeferredFuture;
use AssertionError;
use danog\MadelineProto\AsyncTools;
use danog\MadelineProto\CallStream;
use danog\MadelineProto\EventHandler\Calls\ConferenceCall;
use danog\MadelineProto\EventHandler\Calls\PrivateCall;
use danog\MadelineProto\LocalDirectory;
use danog\MadelineProto\LocalFile;
use danog\MadelineProto\Logger;
use danog\MadelineProto\Magic;
use danog\MadelineProto\MediaDestination;
use danog\MadelineProto\MTProtoTools\Crypt;
use danog\MadelineProto\RecordingFormat;
use danog\MadelineProto\RemoteUrl;
use danog\MadelineProto\Tgcalls\PrivateCallController;
use phpseclib4\Math\BigInteger;
use Revolt\EventLoop;
use Throwable;

use const STR_PAD_LEFT;

/**
 * Manages the creation of the authorization key.
 *
 * https://core.telegram.org/mtproto/auth_key
 * https://core.telegram.org/mtproto/samples-auth_key
 *
 * @psalm-import-type StreamMask from \danog\MadelineProto\CallStream
 * @internal
 */
trait AuthKeyHandler
{
    /** @var array<int, PrivateCallController> */
    private array $calls = [];
    /** @var array<int, PrivateCallController> */
    private array $callsByPeer = [];
    private array $pendingCalls = [];
    /**
     * The updates of the calls we do not track yet, received while a phone.requestCall is in flight
     * (the call may be the one it creates): call ID => updates, see {@see self::postponeCallUpdate()}.
     *
     * @var array<int, list<array>>
     */
    private array $postponedCallUpdates = [];
    /**
     * Request a one-to-one voice or video call.
     *
     * See [the call documentation Â»](https://docs.madelineproto.xyz/docs/CALLS.html) for how to play videos into it.
     *
     * @param mixed $user  User
     * @param bool  $video Whether to start a video call.
     */
    public function requestCall(mixed $user, bool $video = false): PrivateCall
    {
        $user = ($this->getInfo($user));
        if ($user['type'] !== 'user') {
            throw new AssertionError("Can only create a call with a user!");
        }
        $user = $user['bot_api_id'];
        if (isset($this->pendingCalls[$user])) {
            return $this->pendingCalls[$user]->await();
        }
        $deferred = new DeferredFuture;
        $this->pendingCalls[$user] = $deferred->getFuture();

        try {
            $this->logger->logger(\sprintf('Calling %s...', $user), Logger::VERBOSE);
            $dh_config = ($this->getDhConfig());
            $this->logger->logger('Generating a...', Logger::VERBOSE);
            $a = BigInteger::randomRange(Magic::$two, $dh_config['p']->subtract(Magic::$two));
            $this->logger->logger('Generating g_a...', Logger::VERBOSE);
            $g_a = $dh_config['g']->powMod($a, $dh_config['p']);
            Crypt::checkG($g_a, $dh_config['p']);
            $res = $this->methodCallAsyncRead('phone.requestCall', [
                'video' => $video,
                'user_id' => $user,
                'g_a_hash' => hash('sha256', $g_a->toBytes(), true),
                'protocol' => PrivateCallController::CALL_PROTOCOL,
            ])['phone_call'];
            $res['a'] = $a;
            $res['g_a'] = str_pad($g_a->toBytes(), 256, \chr(0), STR_PAD_LEFT);
            $deferred->complete($this->trackRequestedCall($res)->public);
        } catch (Throwable $e) {
            $deferred->error($e);
        } finally {
            unset($this->pendingCalls[$user]);
            $this->dropPostponedCallUpdates();
        }
        return $deferred->getFuture()->await();
    }

    /**
     * Start tracking the call created by phone.requestCall, and apply the updates it got before.
     *
     * @internal
     */
    public function trackRequestedCall(array $call): PrivateCallController
    {
        $this->calls[$call['id']] = $controller = new PrivateCallController($this, $call);
        $this->callsByPeer[$controller->public->otherID] = $controller;
        if (isset($this->postponedCallUpdates[$call['id']])) {
            EventLoop::queue($this->replayPostponedCallUpdates(...), $call['id']);
        }
        return $controller;
    }

    /**
     * Once no phone.requestCall is in flight, drop the postponed updates of the calls we do not track:
     * they are of calls requested by another session of the account.
     *
     * @internal
     */
    public function dropPostponedCallUpdates(): void
    {
        if ($this->pendingCalls) {
            return;
        }
        foreach ($this->postponedCallUpdates as $id => $_) {
            if (!isset($this->calls[$id])) {
                unset($this->postponedCallUpdates[$id]);
            }
        }
    }

    /**
     * Postpone an [updatePhoneCall](https://core.telegram.org/constructor/updatePhoneCall) we cannot
     * apply yet, as TDLib does.
     *
     * The server sends the updates of an outgoing call (its phoneCallWaiting, and the phoneCallAccepted,
     * phoneCall or phoneCallDiscarded that follow if the other party answers or declines right away)
     * independently of the result of phone.requestCall, which is what gives us its ID: they can come
     * first. An update of an untracked call that comes while a phone.requestCall is in flight is
     * postponed until it returns, then replayed if it is the call it created (see
     * {@see self::replayPostponedCallUpdates()}), or dropped otherwise. The later updates of a call whose
     * postponed updates are not replayed yet are postponed too, to apply them in order.
     *
     * @internal
     *
     * @return bool Whether the update was postponed.
     */
    public function postponeCallUpdate(array $update): bool
    {
        $id = $update['phone_call']['id'];
        if (isset($this->postponedCallUpdates[$id])
            || ($this->pendingCalls && !isset($this->calls[$id]) && $update['phone_call']['_'] !== 'phoneCallRequested')
        ) {
            $this->logger->logger("Postponing {$update['phone_call']['_']} of untracked call $id until phone.requestCall returns", Logger::VERBOSE);
            $this->postponedCallUpdates[$id][] = $update;
            return true;
        }
        return false;
    }

    /**
     * Apply the updates postponed (see {@see self::postponeCallUpdate()}) for a call we now track, in order.
     */
    private function replayPostponedCallUpdates(int $id): void
    {
        // Updates postponed while one is applied are appended, see postponeCallUpdate().
        while ($this->postponedCallUpdates[$id] ?? null) {
            $update = array_shift($this->postponedCallUpdates[$id]);
            try {
                $this->applyCallUpdate($update);
            } catch (Throwable $e) {
                AsyncTools::rethrow($e);
            }
        }
        unset($this->postponedCallUpdates[$id]);
    }

    /**
     * @internal
     *
     * @psalm-external-mutation-free
     */
    public function cleanupCall(int $id): void
    {
        if (isset($this->calls[$id])) {
            $call = $this->calls[$id];
            unset($this->callsByPeer[$call->public->otherID], $this->calls[$id]);
        }
    }

    /**
     * Get call emojis (will return null if the call is not inited yet).
     *
     * @internal
     *
     * @return ?list{string, string, string, string}
     *
     * @psalm-mutation-free
     */
    public function getCallVisualization(int $id): ?array
    {
        return ($this->calls[$id] ?? null)?->getVisualization();
    }

    /**
     * Accept call.
     */
    public function acceptCall(int $id, ?Cancellation $cancellation = null): void
    {
        ($this->calls[$id] ?? null)?->accept($cancellation);
    }

    /**
     * Discard call.
     *
     * @param int<1, 5> $rating  Call rating in stars
     * @param string    $comment Additional comment on call quality.
     */
    public function discardCall(int $id, DiscardReason $reason = DiscardReason::HANGUP, ?int $rating = null, ?string $comment = null): void
    {
        ($this->calls[$id] ?? null)?->discard($reason, $rating, $comment);
    }

    /**
     * Upgrade a one-to-one call to an [end-to-end encrypted conference call Â»](https://core.telegram.org/api/group-calls#conference-calls):
     * create a conference, invite the other party to it, and discard the one-to-one call pointing them
     * to the conference (`phoneCallDiscardReasonMigrateConferenceCall`).
     *
     * @param int  $id    ID of the one-to-one call.
     * @param bool $muted Whether to join the conference muted.
     */
    public function migrateCallToConference(int $id, bool $muted = false): ConferenceCall
    {
        $call = $this->calls[$id] ?? throw new AssertionError('Unknown call!');
        $conference = $this->createConferenceCall($muted);
        $link = $conference->exportInvite();
        $slug = substr($link, (int) strrpos($link, '/') + 1);
        $conference->invite($call->public->otherID);
        $call->discard(DiscardReason::MIGRATE_CONFERENCE_CALL, conferenceSlug: $slug);
        return $conference;
    }

    /**
     * Get the phone call with the specified user ID.
     *
     * @psalm-mutation-free
     */
    public function getCallByPeer(int $userId): ?PrivateCall
    {
        return ($this->callsByPeer[$userId] ?? null)?->public;
    }

    /**
     * Get all pending and running calls, indexed by user ID.
     *
     * @return array<int, PrivateCall>
     */
    public function getAllCalls(): array
    {
        return array_map(static fn (PrivateCallController $v): PrivateCall => $v->public, $this->callsByPeer);
    }

    /**
     * Get phone call information.
     *
     * @psalm-mutation-free
     */
    public function getCall(int $id): ?PrivateCall
    {
        return ($this->calls[$id] ?? null)?->public;
    }

    /**
     * Play file in call.
     */
    public function callPlay(int $id, LocalFile|RemoteUrl|ReadableStream $file, MediaDestination $dest = MediaDestination::Camera): void
    {
        ($this->calls[$id] ?? null)?->play($file, $dest);
    }

    /**
     * Record the incoming media of a call into one file (or stream) with a fixed set of tracks, see
     * {@see \danog\MadelineProto\EventHandler\Calls\PrivateCall::setOutput()}.
     *
     * @param ?StreamMask $streams The streams to record, as a bitmask of {@see CallStream} flags, or null for every available one.
     *
     * @return StreamMask The streams the other party currently sends, as a bitmask of {@see CallStream} flags (0 while unknown).
     */
    public function callSetOutput(int $id, LocalFile|WritableStream $file, ?RecordingFormat $format = null, ?int $streams = null): int
    {
        if (!isset($this->calls[$id])) {
            throw new AssertionError('Unknown call!');
        }
        return $this->calls[$id]->setOutput($file, $format, $streams);
    }

    /**
     * Record the incoming media of a call into a directory, one numbered file per combination of
     * streams the other party sends, see {@see \danog\MadelineProto\EventHandler\Calls\PrivateCall::setOutputFolder()}.
     */
    public function callSetOutputFolder(int $id, LocalDirectory $dir, ?RecordingFormat $format = null): void
    {
        if (!isset($this->calls[$id])) {
            throw new AssertionError('Unknown call!');
        }
        $this->calls[$id]->setOutputFolder($dir, $format);
    }

    /**
     * Play file in call, blocking until the file has finished playing if a stream is provided.
     *
     * @internal
     */
    public function callPlayBlocking(int $id, LocalFile|RemoteUrl|ReadableStream $file, MediaDestination $dest = MediaDestination::Camera): void
    {
        if (!isset($this->calls[$id])) {
            return;
        }
        $this->callPlay($id, $file, $dest);
        if ($file instanceof ReadableStream) {
            $deferred = new DeferredFuture;
            $file->onClose($deferred->complete(...));
            $deferred->getFuture()->await();
        }
    }

    /**
     * When called, skips to the next file in the playlist.
     */
    public function skipPlay(int $id, MediaDestination $dest = MediaDestination::Camera): void
    {
        ($this->calls[$id] ?? null)?->skip($dest);
    }

    /**
     * Stops playing all files in the call, clears the main and the hold playlist.
     */
    public function stopPlay(int $id, MediaDestination $dest = MediaDestination::Camera): void
    {
        ($this->calls[$id] ?? null)?->stop($dest);
    }

    /**
     * Pauses playback of the current audio file in the call.
     *
     * @psalm-external-mutation-free
     */
    public function pausePlay(int $id, MediaDestination $dest = MediaDestination::Camera): void
    {
        ($this->calls[$id] ?? null)?->pause($dest);
    }

    /**
     * Resumes playback of the current audio file in the call.
     *
     * @psalm-external-mutation-free
     */
    public function resumePlay(int $id, MediaDestination $dest = MediaDestination::Camera): void
    {
        ($this->calls[$id] ?? null)?->resume($dest);
    }

    /**
     * Whether the currently playing audio file is paused.
     *
     * @psalm-mutation-free
     */
    public function isPlayPaused(int $id, MediaDestination $dest = MediaDestination::Camera): bool
    {
        return ($this->calls[$id] ?? null)?->isPaused($dest) ?? false;
    }

    /**
     * Play files on hold in call.
     */
    public function callPlayOnHold(int $id, MediaDestination $dest = MediaDestination::Camera, LocalFile|RemoteUrl|ReadableStream ...$files): void
    {
        ($this->calls[$id] ?? null)?->playOnHold($dest, ...$files);
    }

    /**
     * Play files on hold in call.
     *
     * @internal
     */
    public function callPlayOnHoldBlocking(int $id, MediaDestination $dest = MediaDestination::Camera, LocalFile|RemoteUrl|ReadableStream ...$files): void
    {
        if (!isset($this->calls[$id])) {
            return;
        }
        $this->callPlayOnHold($id, $dest, ...$files);
    }

    /**
     * Get the file that is currently being played.
     *
     * Will return a string with the object ID of the stream if we're currently playing a stream, otherwise returns the related LocalFile or RemoteUrl.
     *
     * @psalm-mutation-free
     */
    public function callGetCurrent(int $id, MediaDestination $dest = MediaDestination::Camera): RemoteUrl|LocalFile|string|null
    {
        return ($this->calls[$id] ?? null)?->getCurrent($dest);
    }

    /**
     * Get call state.
     *
     * @psalm-mutation-free
     */
    public function getCallState(int $id): ?CallState
    {
        return ($this->calls[$id] ?? null)?->getCallState();
    }

    /**
     * Mute or unmute our own audio stream in a call.
     */
    public function setCallMuted(int $id, bool $muted = true): void
    {
        ($this->calls[$id] ?? null)?->setMuted($muted);
    }

    /**
     * Whether our own audio stream is muted in a call.
     *
     * @psalm-mutation-free
     */
    public function isCallMuted(int $id): bool
    {
        return ($this->calls[$id] ?? null)?->isMuted() ?? true;
    }

    /**
     * Start sharing a screen in a call.
     *
     * @internal
     */
    public function enableCallPresentation(int $id): void
    {
        ($this->calls[$id] ?? null)?->enablePresentation();
    }

    /**
     * Stop sharing the screen in a call.
     *
     * @internal
     */
    public function disableCallPresentation(int $id): void
    {
        ($this->calls[$id] ?? null)?->disablePresentation();
    }

    /**
     * Whether a screencast is currently being transmitted in a call.
     *
     * @internal
     *
     * @psalm-mutation-free
     */
    public function isCallSharingScreen(int $id): bool
    {
        return ($this->calls[$id] ?? null)?->isSharingScreen() ?? false;
    }

    /**
     * Get the media state of the other party of a call, as reported by their client.
     *
     * @psalm-mutation-free
     */
    public function getCallRemoteMediaState(int $id): ?MediaState
    {
        return ($this->calls[$id] ?? null)?->getRemoteMediaState();
    }
}
<?php

declare(strict_types=1);

/**
 * Call state module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\VoIP;

use JsonSerializable;

/**
 * Why was the call discarded?
 */
enum DiscardReason: string implements JsonSerializable
{
    /** We missed the call */
    case MISSED = 'phoneCallDiscardReasonMissed';
    /** The phone call was disconnected */
    case DISCONNECTED = 'phoneCallDiscardReasonDisconnect';
    /** The phone call ended normally */
    case HANGUP = 'phoneCallDiscardReasonHangup';
    /** The phone call was discarded because the user is busy in another call */
    case BUSY = 'phoneCallDiscardReasonBusy';
    /**
     * The phone call was upgraded to a [conference call Â»](https://core.telegram.org/api/group-calls#conference-calls).
     *
     * Do not pass this reason manually, use `VoIP::migrateToConference()` instead.
     */
    case MIGRATE_CONFERENCE_CALL = 'phoneCallDiscardReasonMigrateConferenceCall';

    /** @internal */
    #[\Override]
    public function jsonSerialize(): string
    {
        return $this->name;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\VoIP;

use JsonSerializable;

/**
 * The media state of the other party of a one-to-one call, as reported by its `MediaState`
 * tgcalls signaling message.
 */
final class MediaState implements JsonSerializable
{
    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(
        /** Whether the other party muted their microphone. */
        public readonly bool $muted,
        /** Whether the other party is transmitting a camera video stream. */
        public readonly bool $video,
        /** Whether the other party is transmitting a screen sharing stream. */
        public readonly bool $screencast,
        /** Whether the other party reported a low battery level. */
        public readonly bool $batteryLow = false,
    ) {
    }

    /**
     * @internal
     */
    public static function fromSignaling(array $message): self
    {
        $state = static fn (mixed $v): bool => \in_array($v, ['active', 'suspended'], true);
        return new self(
            (bool) ($message['muted'] ?? false),
            $state($message['videoState'] ?? 'inactive'),
            $state($message['screencastState'] ?? 'inactive'),
            (bool) ($message['isBatteryLow'] ?? false),
        );
    }

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function jsonSerialize(): array
    {
        return [
            'muted' => $this->muted,
            'video' => $this->video,
            'screencast' => $this->screencast,
            'batteryLow' => $this->batteryLow,
        ];
    }
}
<?php

declare(strict_types=1);

/**
 * Outgoing message.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\MTProto;

enum SpecialMethodType
{
    case UNAUTHED_METHOD;
    case USER_RELATED;
    case FILE_RELATED;
    case FILEREF_RELATED;
}
<?php

declare(strict_types=1);

/**
 * Message.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\MTProto;

/**
 * MTProto message.
 *
 * @internal
 */
abstract class MTProtoMessage
{
    public const NOT_CONTENT_RELATED = [
        'rpc_drop_answer' => true,
        'rpc_answer_unknown' => true,
        'rpc_answer_dropped_running' => true,
        'rpc_answer_dropped' => true,
        'get_future_salts' => true,
        'future_salt' => true,
        'future_salts' => true,
        'ping' => true,
        'pong' => true,
        'ping_delay_disconnect' => true,
        'destroy_session' => true,
        'destroy_session_ok' => true,
        'destroy_session_none' => true,
        //'new_session_created' => true,
        'msg_container' => true,
        'msg_copy' => true,
        'gzip_packed' => true,
        'http_wait' => true,
        'msgs_ack' => true,
        'bad_msg_notification' => true,
        'bad_server_salt' => true,
        'msgs_state_req' => true,
        'msgs_state_info' => true,
        'msgs_all_info' => true,
        'msg_detailed_info' => true,
        'msg_new_detailed_info' => true,
        'msg_resend_req' => true,
        'msg_resend_ans_req' => true,
    ];
    /**
     * My message ID.
     */
    protected ?int $msgId = null;

    /**
     * Sequence number.
     */
    protected ?int $seqNo = null;

    /**
     * @psalm-mutation-free
     */
    public function __construct(
        /**
         * Whether constructor is content related.
         */
        public readonly bool $contentRelated
    ) {
    }

    /**
     * @psalm-mutation-free
     */
    public function __debugInfo(): array
    {
        return [(string) $this, 'objId' => spl_object_id($this)];
    }

    /** @psalm-mutation-free */
    abstract public function __toString(): string;

    /**
     * Get my message ID.
     *
     * @psalm-mutation-free
     */
    public function getMsgId(): ?int
    {
        return $this->msgId;
    }

    /**
     * Set my message ID.
     *
     * @psalm-external-mutation-free
     */
    public function setMsgId(?int $msgId): self
    {
        $this->msgId = $msgId;

        return $this;
    }

    /**
     * Check if we have a message ID.
     *
     * @psalm-mutation-free
     */
    public function hasMsgId(): bool
    {
        return $this->msgId !== null;
    }

    /**
     * Get sequence number.
     */
    public function getSeqNo(): ?int
    {
        return $this->seqNo;
    }

    /**
     * Has sequence number.
     *
     * @psalm-mutation-free
     */
    public function hasSeqNo(): bool
    {
        return isset($this->seqNo);
    }

    /**
     * Set sequence number.
     *
     * @param null|int $seqNo Sequence number
     *
     * @psalm-external-mutation-free
     */
    public function setSeqNo(?int $seqNo): self
    {
        $this->seqNo = $seqNo;

        return $this;
    }
}
<?php

declare(strict_types=1);

/**
 * Container message.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\MTProto;

use danog\MadelineProto\Connection;

/**
 * Outgoing container message.
 *
 * @internal
 */
final class Container extends MTProtoOutgoingMessage
{
    /**
     * Constructor.
     *
     * @param list<MTProtoOutgoingMessage> $msgs
     * @param list<int> $acks
     */
    public function __construct(
        Connection $connection,
        public readonly array $msgs,
        public readonly array $acks,
    ) {
        parent::__construct($connection, [], 'msg_container', '', false, false, null, null);
        foreach ($msgs as $msg) {
            $msg->container = $this;
        }
    }
}
<?php

declare(strict_types=1);

/**
 * MTProto Auth key.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\MTProto;

use danog\MadelineProto\API;
use Webmozart\Assert\Assert;

/**
 * @internal
 *
 * @psalm-immutable
 */
final class LoginState
{
    /**
     * @psalm-mutation-free
     */
    public function __construct(
        /** @var API::NOT_LOGGED_IN|API::WAITING_*|API::LOGGED_IN|API::LOGGED_OUT */
        public readonly int $state,
        public readonly ?int $authorizedDc,
    ) {
        if ($state === API::LOGGED_IN) {
            Assert::notNull($authorizedDc, 'If state is LOGGED_IN, authorizedDc must not be null');
        }
    }

    /**
     * @param API::NOT_LOGGED_IN|API::WAITING_*|API::LOGGED_IN|API::LOGGED_OUT $state
     *
     * @psalm-mutation-free
     */
    public function setState(int $state): self
    {
        if ($state === $this->state) {
            return $this;
        }
        return new self($state, $state === API::LOGGED_OUT ? null : $this->authorizedDc);
    }
    /**
     * @param API::NOT_LOGGED_IN|API::WAITING_*|API::LOGGED_IN|API::LOGGED_OUT $state
     *
     * @psalm-mutation-free
     */
    public function setStateDc(int $state, ?int $dc): self
    {
        if ($state === $this->state && $dc === $this->authorizedDc) {
            return $this;
        }
        return new self($state, $dc);
    }
    /**
     * @psalm-mutation-free
     */
    public function setDc(int $dc): self
    {
        $dc = $this->state === API::LOGGED_OUT ? null : $dc;
        if ($dc === $this->authorizedDc) {
            return $this;
        }
        return new self($this->state, $dc);
    }
}
<?php

declare(strict_types=1);

/**
 * MTProto Auth key.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\MTProto;

use JsonSerializable;
use Webmozart\Assert\Assert;

/**
 * MTProto auth key.
 *
 * @internal
 */
abstract class AuthKey implements JsonSerializable
{
    /**
     * Auth key.
     *
     */
    protected ?string $authKey = null;
    /**
     * Auth key ID.
     *
     */
    protected ?string $id = null;
    /**
     * Server salt.
     *
     */
    protected ?string $serverSalt = null;
    /**
     * Constructor function.
     *
     * @param array $old Old auth key array
     *
     * @psalm-mutation-free
     */
    public function __construct(array $old = [])
    {
        if (isset($old['auth_key'])) {
            if (\strlen($old['auth_key']) !== 2048 / 8 && str_starts_with($old['auth_key'], 'pony')) {
                $old['auth_key'] = base64_decode(substr($old['auth_key'], 4), true);
            }
            $this->setAuthKey($old['auth_key']);
        }
        if (isset($old['server_salt'])) {
            $this->setServerSalt($old['server_salt']);
        }
    }
    /**
     * Set auth key.
     *
     * @param string $authKey Authorization key
     *
     * @psalm-external-mutation-free
     */
    public function setAuthKey(string $authKey): void
    {
        $this->authKey = $authKey;
        $this->id = substr(sha1($authKey, true), -8);
    }
    /**
     * Check if auth key is present.
     *
     * @psalm-mutation-free
     */
    public function hasAuthKey(): bool
    {
        return $this->authKey !== null && $this->serverSalt !== null;
    }
    /**
     * Get auth key.
     *
     * @psalm-mutation-free
     */
    public function getAuthKey(): string
    {
        Assert::notNull($this->authKey);
        return $this->authKey;
    }
    /**
     * Get auth key ID.
     *
     * @psalm-mutation-free
     */
    public function getID(): string
    {
        Assert::notNull($this->id);
        return $this->id;
    }
    /**
     * Set server salt.
     *
     * @param string $salt Server salt
     *
     * @psalm-external-mutation-free
     */
    public function setServerSalt(string $salt): void
    {
        $this->serverSalt = $salt;
    }
    /**
     * Get server salt.
     *
     * @psalm-mutation-free
     */
    public function getServerSalt(): string
    {
        Assert::notNull($this->serverSalt);
        return $this->serverSalt;
    }
    /**
     * Check if has server salt.
     *
     * @psalm-mutation-free
     */
    public function hasServerSalt(): bool
    {
        return $this->serverSalt !== null;
    }
    /**
     * Check if we are logged in.
     * @psalm-mutation-free
     */
    abstract public function isAuthorized(): bool;
    /**
     * Set the authorized boolean.
     *
     * @param boolean $authorized Whether we are authorized
     * @psalm-mutation-free
     */
    abstract public function authorized(bool $authorized): void;
}
<?php

declare(strict_types=1);

/**
 * Outgoing message.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\MTProto;

use Amp\Cancellation;
use Amp\CancelledException;
use Amp\DeferredFuture;
use Amp\Future;
use Closure;
use danog\MadelineProto\Connection;
use danog\MadelineProto\Exception;
use danog\MadelineProto\Logger;
use danog\MadelineProto\RPCErrorException;
use Revolt\EventLoop;
use Throwable;

use function Amp\async;
use function time;

/**
 * Outgoing message.
 *
 * @internal
 */
class MTProtoOutgoingMessage extends MTProtoMessage
{
    public self|LinkedList $next;
    public self|LinkedList $prev;
    protected ?Container $container = null;

    /**
     * The message was created.
     */
    private const STATE_PENDING = 0;
    /**
     * The message was sent.
     */
    private const STATE_SENT = 1;
    /**
     * The message was acked.
     */
    private const STATE_ACKED = 2;
    /**
     * We got a reply to the message.
     */
    private const STATE_REPLIED = self::STATE_ACKED | 4;

    /**
     * State of message.
     *
     * @var self::STATE_*
     */
    private int $state = self::STATE_PENDING;

    /**
     * Send deferred.
     *
     * @var ?DeferredFuture<null>
     */
    private ?DeferredFuture $sendDeferred = null;

    /**
     * Serialized body.
     */
    private ?string $serializedBody = null;

    /**
     * When was this message sent.
     */
    private ?int $sent = null;

    /**
     * Number of times this message was sent.
     */
    private int $tries = 0;

    private ?string $checkTimer = null;
    private readonly ?string $cancelSubscription;

    /**
     * Whether its file references were already refreshed once: a second FILE_REFERENCE_* error is not retried.
     */
    public bool $refreshedFileReferences = false;
    /**
     * The file references sent by the last serialization, by FileId key.
     *
     * @var array<string, string>
     */
    public array $sentFileReferences = [];

    /**
     * Create outgoing message.
     *
     * @param array $body        Body
     * @param string                  $constructor Constructor name
     * @param string                  $type        Constructor type
     * @param boolean                 $isMethod    Is this a method?
     * @param boolean                 $unencrypted Is this an unencrypted message?
     */
    public function __construct(
        public Connection $connection,
        private ?array $body,
        public readonly string $constructor,
        public readonly string $type,
        public readonly bool $isMethod,
        public readonly bool $unencrypted,
        public ?SpecialMethodType $specialMethodType,
        public readonly ?Cancellation $cancellation,
        public readonly ?string $subtype = null,
        /**
         * Custom flood wait limit for this message.
         */
        public readonly ?int $floodWaitLimit = null,
        public readonly ?int $takeoutId = null,
        public readonly ?string $businessConnectionId = null,
        private ?DeferredFuture $resultDeferred = null,
        /**
         * Whether the body embeds the message ID and session ID it is sent with (auth.bindTempAuthKey):
         * it can't be sent again with another message ID or in another session.
         */
        public readonly bool $sessionBound = false,
    ) {
        parent::__construct(!isset(MTProtoMessage::NOT_CONTENT_RELATED[$constructor]));

        $weak = \WeakReference::create($this);
        $this->cancelSubscription = $cancellation?->subscribe(static function (CancelledException $e) use ($weak): void {
            $self = $weak->get();
            if ($self == null || $self->hasReply()) {
                return;
            }
            if (!$self->wasSent()) {
                $self->reply(static fn () => throw $e);
                return;
            }
            $self->reply(static fn () => throw $e);

            $self->connection->requestResponse?->inc([
                'method' => $self->constructor,
                'error_message' => 'Request Timeout',
                'error_code' => '408',
            ]);

            if ($self->hasMsgId() && $self->constructor !== 'rpc_drop_answer') {
                $self->connection->API->logger("Cancelling $self...");
                try {
                    $self->connection->API->logger($self->connection->methodCallAsyncRead(
                        'rpc_drop_answer',
                        ['req_msg_id' => $self->getMsgId()]
                    ));
                } catch (CancelledException) {
                }
            }
        });
    }

    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function __debugInfo(): array
    {
        if (!isset($this->next)) {
            $next = null;
        } elseif ($this->next instanceof MTProtoOutgoingMessage) {
            $next = $this->next;
        } else {
            $next = 'head|tail';
        }
        if (!isset($this->prev)) {
            $prev = null;
        } elseif ($this->prev instanceof MTProtoOutgoingMessage) {
            $prev = $this->prev;
        } else {
            $prev = 'head|tail';
        }
        return [(string) $this, 'objId' => spl_object_id($this), 'prev' => $prev, 'next' => $next];
    }

    /**
     * Signal that we're trying to send the message.
     */
    public function trySend(): void
    {
        if (!isset($this->sendDeferred)) {
            $this->sendDeferred = new DeferredFuture;
        }
        $this->tries++;
    }
    /**
     * Signal that the message was sent.
     */
    public function sent(bool $pending = true): void
    {
        if ($pending) {
            if ($this->unencrypted) {
                $this->connection->unencrypted_new_outgoing[$this->getMsgId()] = $this;
            } else {
                $this->connection->new_outgoing[$this->getMsgId()] = $this;
            }
        }
        if ($this->sent === null && $this->isMethod) {
            $this->connection->inFlightGauge?->inc([
                'method' => $this->constructor,
            ]);
        }
        $this->state |= self::STATE_SENT;
        if (!$this instanceof Container) {
            $this->unlink();
        }
        $this->sent = hrtime(true);
        if ($this->contentRelated && $pending) {
            $self = \WeakReference::create($this);
            $this->checkTimer = EventLoop::delay(
                $this->connection->API->getSettings()->getConnection()->getTimeout(),
                static function () use ($self): void {
                    $self->get()?->check();
                }
            );
        }
        if (isset($this->sendDeferred)) {
            $sendDeferred = $this->sendDeferred;
            $this->sendDeferred = null;
            $sendDeferred->complete();
        }
    }
    public function unlink(): void
    {
        if (isset($this->next)) {
            $this->next->prev = $this->prev;
            $this->prev->next = $this->next;
            unset($this->next, $this->prev);

            if ($this->unencrypted) {
                unset($this->connection->unencryptedPendingOutgoing->check_queue[$this]);
            } elseif ($this->specialMethodType === SpecialMethodType::UNAUTHED_METHOD) {
                unset($this->connection->uninitedPendingOutgoing->check_queue[$this]);
            } else {
                unset($this->connection->mainPendingOutgoing->check_queue[$this]);
            }
            $this->connection->pendingOutgoingGauge?->dec();
        }
        if ($this->checkTimer !== null) {
            EventLoop::cancel($this->checkTimer);
            $this->checkTimer = null;
        }
    }
    /**
     * Prepare the message to be sent again with a new message ID (in a new session, or on another connection).
     *
     * A session-bound message can't be: it fails instead, so that its caller builds it again.
     *
     * @return bool Whether the message can be sent again.
     */
    public function resetForResend(): bool
    {
        if ($this->sessionBound) {
            $constructor = $this->constructor;
            $this->reply(static fn () => new Exception("$constructor can't be sent again with a new message ID"));
            return false;
        }
        $this->setMsgId(null);
        $this->setSeqNo(null);
        return true;
    }
    private function check(): void
    {
        if ($this->state & self::STATE_REPLIED) {
            return;
        }
        $shared = $this->connection->getShared();
        $settings = $shared->getSettings();
        $timeout = $settings->getTimeout();
        $self = \WeakReference::create($this);
        $this->checkTimer = EventLoop::delay(
            $timeout,
            static function () use ($self): void {
                $self->get()?->check();
            }
        );

        if ($this->msgId === null) {
            return;
        }
        if ($this->unencrypted) {
            $this->connection->unencryptedPendingOutgoing->check_queue[$this] = true;
        } elseif ($this->specialMethodType === SpecialMethodType::UNAUTHED_METHOD) {
            $this->connection->uninitedPendingOutgoing->check_queue[$this] = true;
        } else {
            $this->connection->mainPendingOutgoing->check_queue[$this] = true;
        }
        $this->connection->flush(true);
    }
    /**
     * Set reply to message.
     *
     * @param mixed|(callable(): Throwable) $result
     */
    public function reply($result): void
    {
        if ($this->state & self::STATE_REPLIED) {
            //throw new Exception("Trying to double reply to message $this!");
            // It can happen, no big deal
            return;
        }
        $this->sent(false);
        if ($this->checkTimer !== null) {
            EventLoop::cancel($this->checkTimer);
            $this->checkTimer = null;
        }

        if ($this->isMethod) {
            $this->connection->inFlightGauge?->dec([
                'method' => $this->constructor,
            ]);
            if (!\is_callable($result)) {
                $this->connection->requestLatencies?->observe(
                    hrtime(true) - $this->sent,
                    ['method' => $this->constructor]
                );
            }
        }
        if ($this->msgId !== null) {
            if ($this->unencrypted) {
                unset($this->connection->unencrypted_new_outgoing[$this->msgId]);
            } else {
                unset($this->connection->new_outgoing[$this->msgId]);
            }
        }
        if ($this->container !== null) {
            if ($this->unencrypted) {
                unset($this->connection->unencrypted_new_outgoing[$this->container->msgId]);
            } else {
                unset($this->connection->new_outgoing[$this->container->msgId]);
            }
        }

        $this->serializedBody = null;
        $this->body = null;

        $this->state |= self::STATE_REPLIED;
        $this->cancellation?->unsubscribe($this->cancelSubscription);
        if ($this->resultDeferred) { // Sometimes can get an RPC error for constructors
            $promise = $this->resultDeferred;
            $this->resultDeferred = null;
            EventLoop::queue($promise->complete(...), $result);
        }
    }

    /**
     * ACK message.
     */
    public function ack(): void
    {
        $this->state |= self::STATE_ACKED;
        if (!$this->resultDeferred) {
            $this->reply(null);
        }
    }

    /**
     * Get message body.
     */
    public function getBody()
    {
        return $this->body;
    }

    /**
     * Get message body or empty array.
     *
     * @psalm-mutation-free
     */
    public function getBodyOrEmpty(): array
    {
        return (array) $this->body;
    }
    /**
     * Check if we have a body.
     *
     * @psalm-mutation-free
     */
    public function hasBody(): bool
    {
        return $this->body !== null;
    }

    /**
     * Get serialized body.
     */
    public function getSerializedBody(): ?string
    {
        return $this->serializedBody;
    }
    /**
     * Check if we have a serialized body.
     *
     * @psalm-mutation-free
     */
    public function hasSerializedBody(): bool
    {
        return $this->serializedBody !== null;
    }

    /**
     * Get number of times this message was sent.
     */
    public function getTries(): int
    {
        return $this->tries;
    }

    /**
     * Set serialized body.
     *
     * @param string $serializedBody Serialized body.
     *
     * @psalm-external-mutation-free
     */
    public function setSerializedBody(string $serializedBody): self
    {
        $this->serializedBody = $serializedBody;

        return $this;
    }

    /**
     * Refresh the file references of the arguments, after the call failed with a FILE_REFERENCE_* error.
     *
     * @param string $error The error
     * @param int    $code  Its code
     *
     * @return Future<?Closure(): Throwable> Resolves to null if the call can be repeated, to the error otherwise.
     */
    public function refreshReferences(string $error, int $code): Future
    {
        $this->serializedBody = null;
        // Refreshed at most once.
        $this->refreshedFileReferences = true;
        $API = $this->connection->API;
        $method = $this->constructor;

        // Never fails: methodRecall() only handles futures resolving to null or to a closure.
        return async(function () use ($API, $method, $error, $code): ?Closure {
            try {
                $body = $this->body === null
                    ? null
                    : $API->referenceDatabase?->refreshMethodCall($method, $this->body, $error, $this->sentFileReferences);
            } catch (Throwable $e) {
                $API->logger("Could not refresh the file references of $method: $e", Logger::ERROR);
                $body = null;
            }
            if ($body === null) {
                return static fn (): Throwable => RPCErrorException::make($error, $code, $method);
            }
            $this->body = $body;
            return null;
        });
    }

    /**
     * Get when was this message sent.
     */
    public function getSent(): ?int
    {
        return $this->sent;
    }

    /**
     * Check if the message was sent.
     *
     * @psalm-mutation-free
     */
    public function wasSent(): bool
    {
        return (bool) ($this->state & self::STATE_SENT);
    }
    /**
     * Check if the message has a reply.
     *
     * @psalm-mutation-free
     */
    public function hasReply(): bool
    {
        return (bool) ($this->state & self::STATE_REPLIED);
    }
    /**
     * For logging.
     *
     * @psalm-mutation-free
     */
    public function __toString(): string
    {
        if ($this->state & self::STATE_REPLIED) {
            $state = 'acked (by reply)';
        } elseif ($this->state & self::STATE_ACKED) {
            $state = 'acked';
        } elseif ($this->state & self::STATE_SENT) {
            $state = 'sent '.((hrtime(true) - $this->sent) / 1_000_000_000).' seconds ago';
        } else {
            $state = 'pending';
        }
        if ($this->msgId) {
            return "{$this->constructor} with message ID {$this->msgId} $state";
        }
        return "{$this->constructor} $state";
    }

    /**
     * Wait for message to be sent.
     *
     * Completes immediately if the message was already sent, or replied to (for example, if it was cancelled before being sent).
     *
     * @return Future<null>
     */
    public function getSendPromise(): Future
    {
        return $this->sendDeferred?->getFuture() ?? Future::complete();
    }

    /**
     * Check if we have a promise.
     *
     * @psalm-mutation-free
     */
    public function hasPromise(): bool
    {
        return $this->resultDeferred !== null;
    }

    /**
     * Get the promise.
     *
     * @psalm-mutation-free
     */
    public function getResultPromise(): Future
    {
        \assert($this->resultDeferred !== null);
        return $this->resultDeferred->getFuture();
    }

    /**
     * Reset sent time to trigger resending.
     *
     * @psalm-external-mutation-free
     */
    public function resetSent(): self
    {
        $this->sent = 0;

        return $this;
    }

    /**
     * Set when was this message sent.
     *
     * @param int $sent When was this message sent.
     *
     * @psalm-external-mutation-free
     */
    public function setSent(int $sent): self
    {
        $this->sent = $sent;

        return $this;
    }
}
<?php

declare(strict_types=1);

/**
 * Message.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\MTProto;

use WeakMap;

/**
 * @internal
 */
final class LinkedList
{
    public const EMPTY = true;

    public MTProtoOutgoingMessage|LinkedList $next;

    public MTProtoOutgoingMessage|LinkedList $prev;

    /**
     * Check queue.
     *
     * @var WeakMap<MTProtoOutgoingMessage, true>
     */
    public WeakMap $check_queue;

    /**
     * @psalm-mutation-free
     */
    public function __construct()
    {
        $this->next = $this;
        $this->prev = $this;
        $this->check_queue = new WeakMap();
    }

    /**
     * @psalm-mutation-free
     */
    public function isEmpty(): bool
    {
        return $this->next === $this;
    }

    public function enqueue(MTProtoOutgoingMessage $message): void
    {
        $message->next = $this->next;
        $message->prev = $this;
        $this->next->prev = $message;
        $this->next = $message;
    }
    /**
     * @psalm-mutation-free
     */
    public function peek(): ?MTProtoOutgoingMessage
    {
        if ($this->prev === $this) {
            return null;
        }
        return $this->prev;
    }

    /**
     * @psalm-mutation-free
     */
    public function __debugInfo(): array
    {
        if (!isset($this->next)) {
            $next = null;
        } elseif ($this->next instanceof MTProtoOutgoingMessage) {
            $next = $this->next;
        } else {
            $next = 'head|tail';
        }
        if (!isset($this->prev)) {
            $prev = null;
        } elseif ($this->prev instanceof MTProtoOutgoingMessage) {
            $prev = $this->prev;
        } else {
            $prev = 'head|tail';
        }
        return ['prev' => $prev, 'next' => $next];
    }

}
<?php

declare(strict_types=1);

/**
 * MTProto temporary auth key.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\MTProto;

use JsonSerializable;

/**
 * MTProto temporary auth key.
 *
 * @internal
 */
final class TempAuthKey extends AuthKey implements JsonSerializable
{
    /**
     * Bound auth key instance.
     *
     */
    private ?PermAuthKey $bound = null;
    /**
     * Expiration date.
     *
     */
    private int $expires = 0;
    /**
     * Whether the connection is inited for this auth key.
     *
     */
    protected bool $inited = false;
    /**
     * Constructor function.
     *
     * @param array $old Old auth key array
     *
     * @psalm-mutation-free
     */
    public function __construct(array $old = [])
    {
        parent::__construct($old);
        if (isset($old['expires'])) {
            $this->expires($old['expires']);
        }
        if (isset($old['connection_inited']) && $old['connection_inited']) {
            $this->init($old['connection_inited']);
        }
    }
    /**
     * Init or deinit connection for auth key.
     *
     * @param boolean $init Init or deinit
     *
     * @psalm-external-mutation-free
     */
    public function init(bool $init = true): void
    {
        $this->inited = $init;
    }
    /**
     * Check if connection is inited for auth key.
     */
    public function isInited(): bool
    {
        return $this->inited;
    }
    /**
     * Bind auth key.
     *
     * @param PermAuthKey|null $bound Permanent auth key
     * @param bool             $pfs   Whether to bind using PFS
     *
     * @psalm-external-mutation-free
     */
    public function bind(?PermAuthKey $bound, bool $pfs = true): void
    {
        $this->bound = $bound;
        if (!$pfs) {
            foreach (['authKey', 'id', 'serverSalt'] as $key) {
                $this->{$key} =& $bound->{$key};
            }
        }
    }
    /**
     * Check if auth key is bound.
     *
     * @psalm-mutation-free
     */
    public function isBound(): bool
    {
        return $this->bound !== null;
    }
    /**
     * Check if we are logged in.
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function isAuthorized(): bool
    {
        return $this->bound ? $this->bound->isAuthorized() : false;
    }
    /**
     * Set the authorized boolean.
     *
     * @param boolean $authorized Whether we are authorized
     *
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function authorized(bool $authorized): void
    {
        $this->bound->authorized($authorized);
    }
    /**
     * Set expiration date of temporary auth key.
     *
     * @param integer $expires Expiration date
     *
     * @psalm-external-mutation-free
     */
    public function expires(int $expires): void
    {
        $this->expires = $expires;
    }
    /**
     * Check if auth key has expired.
     */
    public function expired(): bool
    {
        return time() > $this->expires;
    }
    /**
     * JSON serialization function.
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function jsonSerialize(): array
    {
        return ['auth_key' => 'pony'.base64_encode($this->authKey), 'server_salt' => $this->serverSalt, 'bound' => $this->isBound(), 'expires' => $this->expires, 'connection_inited' => $this->inited];
    }
    /**
     * Sleep function.
     *
     * @psalm-pure
     */
    public function __sleep(): array
    {
        return ['authKey', 'id', 'serverSalt', 'bound', 'expires', 'inited'];
    }
}
<?php

declare(strict_types=1);

/**
 * MTProto permanent auth key.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\MTProto;

/**
 * MTProto permanent auth key.
 *
 * @internal
 */
final class PermAuthKey extends AuthKey
{
    /**
     * Whether this auth key is authorized (as in associated to an account on Telegram).
     *
     */
    private bool $authorized = false;
    /**
     * Constructor function.
     *
     * @param array $old Old auth key array
     *
     * @psalm-mutation-free
     */
    public function __construct(array $old = [])
    {
        parent::__construct($old);
        if (isset($old['authorized'])) {
            $this->authorized($old['authorized']);
        }
    }
    /**
     * Check if we are logged in.
     */
    #[\Override]
    public function isAuthorized(): bool
    {
        return $this->authorized;
    }
    /**
     * Set the authorized boolean.
     *
     * @param boolean $authorized Whether we are authorized
     *
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function authorized(bool $authorized): void
    {
        $this->authorized = $authorized;
    }
    /**
     * JSON serialization function.
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function jsonSerialize(): array
    {
        return ['auth_key' => 'pony'.base64_encode($this->authKey), 'server_salt' => $this->serverSalt, 'authorized' => $this->authorized];
    }
    /**
     * Sleep function.
     *
     * @psalm-pure
     */
    public function __sleep(): array
    {
        return ['authKey', 'id', 'serverSalt', 'authorized'];
    }
}
<?php

declare(strict_types=1);

/**
 * MTProto Auth key.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\MTProto;

use danog\MadelineProto\API;
use danog\MadelineProto\MTProtoTools\Crypt;
use danog\MadelineProto\Reactive\Publisher;
use danog\MadelineProto\Reactive\SimpleSubscriber;
use Webmozart\Assert\Assert;

/**
 * MTProto auth key.
 *
 * @internal
 *
 * @implements SimpleSubscriber<ConnectionState|LoginState>
 */
final class NewAuthKey implements SimpleSubscriber
{
    private ?string $authKey = null;
    private ?string $id = null;
    private ?string $tempAuthKey = null;
    private ?string $tempAuthKeyForHash = null;
    private ?string $tempId = null;
    public ?string $serverSalt = null;

    /** @var Publisher<ConnectionState> */
    public readonly Publisher $connectionState;

    private ?int $authedDcId;

    public function __construct(
        public readonly bool $isMedia,
        public readonly bool $isCdn,
        public readonly int $dcId,
        Publisher $loginState,
        private readonly ?self $mainKey = null
    ) {
        $this->connectionState = new Publisher(
            $isCdn
                ? ConnectionState::UNENCRYPTED
                : ($isMedia ? ConnectionState::UNENCRYPTED_MEDIA_WAITING_MAIN : ConnectionState::UNENCRYPTED_NO_PERMANENT)
        );
        if ($mainKey === null) {
            Assert::false($isMedia);
        } else {
            Assert::true($isMedia);
            $mainKey->connectionState->subscribe($this);
        }
        $loginState->subscribe($this);
    }

    #[\Override]
    public function onSimpleStateChange($state): void
    {
        if ($state instanceof ConnectionState) {
            $state = $this->mainKey->connectionState->getState();

            if ($this->connectionState->getState() === ConnectionState::UNENCRYPTED_MEDIA_WAITING_MAIN) {
                if ($state === ConnectionState::ENCRYPTED) {
                    $this->setAuthKey($this->mainKey->authKey);
                }
            } else {
                if ($state === ConnectionState::UNENCRYPTED_NO_PERMANENT) {
                    $this->setAuthKey(null);
                }
            }
            return;
        }

        Assert::isInstanceOf($state, LoginState::class);
        $this->authedDcId = $state->state === API::LOGGED_IN
            ? $state->authorizedDc
            : null;
        if ($this->connectionState->getState() === ConnectionState::ENCRYPTED_NOT_AUTHED
            || $this->connectionState->getState() === ConnectionState::ENCRYPTED_NOT_AUTHED_NO_LOGIN
        ) {
            if ($this->authedDcId !== null) {
                $state = $this->dcId === $this->authedDcId
                    ? ConnectionState::ENCRYPTED
                    : ConnectionState::ENCRYPTED_NOT_AUTHED;
            } else {
                $state = ConnectionState::ENCRYPTED_NOT_AUTHED_NO_LOGIN;
            }
            $this->connectionState->publish($state);
        } elseif ($this->authedDcId === null && $this->connectionState->getState() === ConnectionState::ENCRYPTED) {
            $this->setTempAuthKey(null, null);
        }
    }

    public function setAuthKey(?string $authKey): void
    {
        $this->authKey = $authKey;
        if ($authKey === null) {
            $this->id = null;
        } else {
            $this->id = substr(sha1($authKey, true), -8);
        }
        $this->setTempAuthKey(null, null);
    }
    public function setTempAuthKey(?string $authKey, ?string $serverSalt): void
    {
        $this->tempAuthKey = $authKey;
        $this->serverSalt = $serverSalt;
        if ($authKey === null) {
            Assert::null($serverSalt, 'Server salt must be null if auth key is null');
            $this->tempId = null;
            $this->tempAuthKeyForHash = null;

            $this->connectionState->publish(
                $this->isCdn || $this->id !== null
                    ? ConnectionState::UNENCRYPTED
                    : ($this->isMedia ? ConnectionState::UNENCRYPTED_MEDIA_WAITING_MAIN : ConnectionState::UNENCRYPTED_NO_PERMANENT)
            );
        } else {
            Assert::notNull($serverSalt, 'Server salt must not be null if auth key is not null');
            if (!$this->isCdn) {
                Assert::notNull($this->id, 'Auth key must not be null if temp auth key is not null');
            }
            $this->tempId = substr(sha1($authKey, true), -8);
            $this->tempAuthKeyForHash = substr($authKey, 88, 32);
            $this->connectionState->publish(
                $this->isCdn
                ? ConnectionState::ENCRYPTED_NOT_INITED
                : ConnectionState::ENCRYPTED_NOT_BOUND
            );
        }
    }
    /**
     * @return list{string, string}
     *
     * @psalm-mutation-free
     */
    public function pfsKdf(string $message_key): array
    {
        return Crypt::oldKdf($message_key, $this->authKey);
    }
    /**
     * Get auth key ID.
     */
    public function getID(): ?string
    {
        return $this->id;
    }
    /**
     * Get auth key.
     */
    public function getAuthKey(): ?string
    {
        return $this->authKey;
    }
    /**
     * Get temp auth key.
     */
    public function getTempAuthKey(): ?string
    {
        return $this->tempAuthKey;
    }
    /**
     * Get auth key.
     */
    public function getTempAuthKeyForHash(): ?string
    {
        return $this->tempAuthKeyForHash;
    }
    /**
     * Get auth key ID.
     */
    public function getTempID(): ?string
    {
        return $this->tempId;
    }

    /**
     * Get server salt.
     */
    public function getServerSalt(): ?string
    {
        return $this->serverSalt;
    }
    /**
     * Get server salt.
     *
     * @psalm-external-mutation-free
     */
    public function setServerSalt(?string $salt): void
    {
        $this->serverSalt = $salt;
    }

    public function bind(): void
    {
        Assert::eq($this->connectionState->getState(), ConnectionState::ENCRYPTED_NOT_BOUND);
        $state = ConnectionState::ENCRYPTED_NOT_INITED;
        $this->connectionState->publish($state);
    }
    public function init(): void
    {
        Assert::eq($this->connectionState->getState(), ConnectionState::ENCRYPTED_NOT_INITED);
        $state = $this->isCdn
            ? ConnectionState::ENCRYPTED
            : (
                $this->authedDcId === null
                ? ConnectionState::ENCRYPTED_NOT_AUTHED_NO_LOGIN
                : (
                    $this->authedDcId === $this->dcId || $this->isMedia
                        ? ConnectionState::ENCRYPTED
                        : ConnectionState::ENCRYPTED_NOT_AUTHED
                )
            );
        $this->connectionState->publish($state);
    }
    public function authorize(): void
    {
        Assert::eq($this->connectionState->getState(), ConnectionState::ENCRYPTED_NOT_AUTHED);
        $state = ConnectionState::ENCRYPTED;
        $this->connectionState->publish($state);
    }
}
<?php

declare(strict_types=1);

/**
 * MTProto Auth key.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\MTProto;

/** @internal */
enum ConnectionState
{
    case UNENCRYPTED_MEDIA_WAITING_MAIN;
    case UNENCRYPTED_NO_PERMANENT;
    case UNENCRYPTED;
    case ENCRYPTED_NOT_BOUND;
    case ENCRYPTED_NOT_INITED;
    case ENCRYPTED_NOT_AUTHED_NO_LOGIN;
    case ENCRYPTED_NOT_AUTHED;
    case ENCRYPTED;

    /**
     * @psalm-mutation-free
     */
    public function isEncrypted(): bool
    {
        return match ($this) {
            self::UNENCRYPTED_MEDIA_WAITING_MAIN,
            self::UNENCRYPTED_NO_PERMANENT,
            self::UNENCRYPTED => false,
            self::ENCRYPTED_NOT_INITED,
            self::ENCRYPTED_NOT_BOUND,
            self::ENCRYPTED_NOT_AUTHED_NO_LOGIN,
            self::ENCRYPTED_NOT_AUTHED,
            self::ENCRYPTED => true,
        };
    }
}
<?php

declare(strict_types=1);

/**
 * Outgoing message.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\MTProto;

use danog\MadelineProto\Connection;

/**
 * Incoming message.
 *
 * @internal
 */
final class MTProtoIncomingMessage extends MTProtoMessage
{
    /**
     * We have received this message.
     */
    public const STATE_RECEIVED = 4;
    /**
     * We have acknowledged this message.
     */
    public const STATE_ACKED = 8;
    /**
     * We have read the contents of this message.
     */
    public const STATE_READ = 128;

    /**
     * Response field map.
     */
    private const RESPONSE_ID_MAP = [
        'rpc_result' => 'req_msg_id',
        'future_salts' => 'req_msg_id',
        'msgs_state_info' => 'req_msg_id',
        'bad_server_salt' => 'bad_msg_id',
        'bad_msg_notification' => 'bad_msg_id',
        'pong' => 'msg_id',
    ];
    /**
     * State.
     */
    private int $state = self::STATE_RECEIVED;
    /**
     * Receive date.
     */
    public readonly int $received;
    /**
     * Deserialized response content.
     */
    private array $content;

    /**
     * Constructor.
     *
     * @param array   $content       Content
     * @param boolean $fromContainer Whether this message was in a container
     *
     * @psalm-mutation-free
     */
    public function __construct(
        private readonly Connection $connection,
        array $content,
        int $msgId,
        public readonly bool $unencrypted,
        public readonly bool $fromContainer,
    ) {
        $this->content = $content;
        $this->msgId = $msgId;

        $this->received = hrtime(true);

        parent::__construct(!isset(MTProtoMessage::NOT_CONTENT_RELATED[$content['_']]));
        if (!$this->contentRelated) {
            $this->state |= 16; // message not requiring acknowledgment
        }
    }

    /**
     * Get my message ID.
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function getMsgId(): int
    {
        \assert($this->msgId !== null);
        return $this->msgId;
    }

    /**
     * Get deserialized response content.
     */
    public function getContent(): array
    {
        return $this->content;
    }

    /**
     * Get log line.
     *
     * @param int $dc DC ID
     *
     * @psalm-mutation-free
     */
    public function log(int $dc): string
    {
        if ($this->fromContainer) {
            return "Inside of container, received {$this->content['_']} from DC $dc";
        }
        return "Received {$this->content['_']} from DC $dc";
    }

    /**
     * Get message type.
     *
     * @psalm-mutation-free
     */
    public function getPredicate(): string
    {
        return $this->content['_'];
    }

    /**
     * Get message type.
     *
     * @psalm-mutation-free
     */
    public function __toString(): string
    {
        return "incoming message {$this->content['_']}";
    }

    /**
     * We have acked this message.
     */
    public function ack(): void
    {
        if ($this->contentRelated && !($this->state & self::STATE_ACKED)) {
            // I let the server know that I received its message
            $this->connection->ack_queue []= $this->msgId;
        }
        $this->state |= self::STATE_ACKED;
    }
    /**
     * Read this message, clearing its contents.
     */
    public function read(): array
    {
        $this->ack();
        $this->state |= self::STATE_READ;
        $content = $this->content;
        $this->content = ['_' => $content['_']];
        return $content;
    }

    /**
     * Get ID of message to which this message replies.
     *
     * @psalm-mutation-free
     */
    public function getRequestId(): int
    {
        return $this->content[self::RESPONSE_ID_MAP[$this->content['_']]];
    }
    /**
     * Get state.
     */
    public function getState(): int
    {
        return $this->state;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use Amp\ByteStream\WritableStream;
use Amp\File\File;
use Amp\File\Whence;

use function Amp\File\openFile;

/**
 * A minimal, streaming Matroska (.mkv) muxer, written entirely in PHP.
 *
 * It writes the EBML header, one video and/or one audio track and a sequence of clusters holding
 * SimpleBlocks â€” enough to record the already-encoded video and OPUS audio of a call into a file that
 * standard players read, without decoding, ffmpeg, any CLI tool or FFI. It is the write-side
 * counterpart of {@see Matroska}.
 *
 * A new cluster is started at every video keyframe (and whenever a block's timestamp would leave the
 * signed 16-bit range a SimpleBlock allows), so the file stays seekable and keyframes are cluster
 * aligned.
 *
 * @internal
 */
final class MatroskaWriter
{
    use RestoresDeclaredProperties;
    // EBML element IDs, on-disk form (with their length-descriptor bits).
    private const ID_EBML = "\x1A\x45\xDF\xA3";
    private const ID_SEGMENT = "\x18\x53\x80\x67";
    private const ID_INFO = "\x15\x49\xA9\x66";
    private const ID_TIMESTAMP_SCALE = "\x2A\xD7\xB1";
    private const ID_DURATION = "\x44\x89";
    private const ID_MUXING_APP = "\x4D\x80";
    private const ID_WRITING_APP = "\x57\x41";
    private const ID_TRACKS = "\x16\x54\xAE\x6B";
    private const ID_TRACK_ENTRY = "\xAE";
    private const ID_TRACK_NUMBER = "\xD7";
    private const ID_TRACK_UID = "\x73\xC5";
    private const ID_TRACK_TYPE = "\x83";
    private const ID_CODEC_ID = "\x86";
    private const ID_CODEC_PRIVATE = "\x63\xA2";
    private const ID_VIDEO = "\xE0";
    private const ID_PIXEL_WIDTH = "\xB0";
    private const ID_PIXEL_HEIGHT = "\xBA";
    private const ID_AUDIO = "\xE1";
    private const ID_SAMPLING_FREQUENCY = "\xB5";
    private const ID_CHANNELS = "\x9F";
    private const ID_CLUSTER = "\x1F\x43\xB6\x75";
    private const ID_TIMESTAMP = "\xE7";
    private const ID_SIMPLE_BLOCK = "\xA3";

    private const TRACK_VIDEO = 1;
    private const TRACK_AUDIO = 2;
    private const VIDEO_TRACK_NUMBER = 1;
    private const AUDIO_TRACK_NUMBER = 2;
    private const PRESENTATION_TRACK_NUMBER = 3;
    /** The track number of each video slot: the camera ({@see CallStream::VIDEO}) and the screen share ({@see CallStream::SCREEN}). */
    private const VIDEO_SLOTS = [CallStream::VIDEO => self::VIDEO_TRACK_NUMBER, CallStream::SCREEN => self::PRESENTATION_TRACK_NUMBER];

    /** One timestamp tick is a millisecond (TimestampScale = 1e6 ns). */
    private const TIMESTAMP_SCALE_NS = 1000000;

    /** Keep a cluster's relative timestamps within a comfortable part of the signed-16-bit range. */
    private const MAX_CLUSTER_MS = 30000;

    private WritableStream $out;
    /** Whether {@see self::$out} is a seekable file we can back-patch the Duration into on close(). */
    private bool $seekable;

    /** @var array<int, array{codecId: string, width: int, height: int, private: string}> Video tracks by slot ({@see CallStream::VIDEO}, {@see CallStream::SCREEN}). */
    private array $video = [];
    /** @var array{codecId: string, rate: int, channels: int, private: string}|null */
    private ?array $audio = null;

    private bool $headerWritten = false;
    private bool $closed = false;

    /** Wall value (ms) the whole file is rebased onto: the first frame's timestamp. */
    private ?int $baseMs = null;
    /** Byte offset of the 8-byte Duration value to patch on close(), or null when not seekable. */
    private ?int $durationOffset = null;
    /** Highest file-relative block timestamp (ms) seen, written as the Duration on close(). */
    private ?int $maxTimestampMs = null;
    /** Timestamp (ms, file-relative) of the currently open cluster, or null if none is open. */
    private ?int $clusterBaseMs = null;
    /** Buffered body of the currently open cluster. */
    private string $clusterBody = '';

    /** The file being written, kept so the writer can reopen it after a serialize/deserialize cycle. */
    private ?LocalFile $localFile = null;

    /**
     * @param string $docType The EBML DocType to declare in the header (`matroska` for `.mkv`, `webm`
     *                        for `.webm`); both are Matroska containers, differing only in this string.
     */
    public function __construct(LocalFile|WritableStream $out, private readonly string $docType = 'matroska')
    {
        if ($out instanceof LocalFile) {
            $this->localFile = $out;
            $this->out = openFile($out->file, 'w');
        } else {
            $this->out = $out;
        }
        // Only a real file handle can be rewound to back-patch the total Duration on close().
        $this->seekable = $this->out instanceof File;
    }

    /**
     * Drop the (unserializable) file handle, flushing any buffered cluster first so nothing is lost.
     * {@see self::__unserialize()} reopens the file to continue the recording.
     */
    public function __serialize(): array
    {
        if ($this->headerWritten && !$this->closed) {
            $this->flushCluster();
        }
        $vars = get_object_vars($this);
        unset($vars['out']);
        return $vars;
    }

    /**
     * Restore the writer, to continue the recording where it left off once {@see self::resume()}
     * reopened the file. The EBML header, Tracks and Duration placeholder are already on disk, so
     * only new clusters are appended.
     *
     * @psalm-external-mutation-free
     */
    public function __unserialize(array $data): void
    {
        // Synchronous state restoration only â€” NEVER anything async here (opening the file suspends the
        // fiber, and suspending mid-deserialization lets other objects' resume tasks run before their
        // dependencies are restored, crashing the whole session resume). The file is reopened later by
        // {@see self::resume()}. Until then $out stays unset and every write guards on isset($this->out).
        $this->restoreDeclaredProperties($data);
    }

    /**
     * Reopen the file at its end to continue the recording, after the whole graph has been
     * deserialized; not in append mode, which could not seek back to patch the Duration on close(). Returns false if the recording cannot continue in this file (it was closed, or
     * the segment file no longer exists): the owner should then start a fresh segment.
     * Only file-backed writers are ever serialized (their parent drops stream-backed
     * recorders), so there is always a file to reopen; the guard is purely defensive.
     */
    public function resume(): bool
    {
        if ($this->closed || $this->localFile === null) {
            return false;
        }
        if (isset($this->out)) {
            return true;
        }
        if (!is_file($this->localFile->file)) {
            // The segment (or its whole folder) was deleted while we were away: there is nothing to
            // append to, and appending would produce a headerless file. The owner opens a new segment.
            $this->closed = true;
            return false;
        }
        $this->out = openFile($this->localFile->file, 'c+');
        $this->out->seek(0, Whence::End);
        return true;
    }

    /**
     * Declare the video track. `$codecPrivate` is empty for VP8/VP9 and the codec configuration
     * record for H.264/AV1.
     *
     * @psalm-external-mutation-free
     */
    /**
     * Declare a video track: the camera ({@see CallStream::VIDEO}) or a screen share
     * ({@see CallStream::SCREEN}), which may both be present at once.
     */
    public function setVideoTrack(string $codecId, int $width, int $height, string $codecPrivate = '', int $slot = CallStream::VIDEO): void
    {
        \assert(isset(self::VIDEO_SLOTS[$slot]));
        $this->video[$slot] = ['codecId' => $codecId, 'width' => max(1, $width), 'height' => max(1, $height), 'private' => $codecPrivate];
    }

    /**
     * Declare the audio track (OPUS, 48kHz, with its OpusHead as the codec private data).
     *
     * @psalm-external-mutation-free
     */
    public function setAudioTrack(string $codecId, int $rate, int $channels, string $codecPrivate = ''): void
    {
        $this->audio = ['codecId' => $codecId, 'rate' => $rate, 'channels' => max(1, $channels), 'private' => $codecPrivate];
    }

    /**
     * @psalm-mutation-free
     */
    public function hasVideoTrack(int $slot = CallStream::VIDEO): bool
    {
        return isset($this->video[$slot]);
    }

    public function hasAudioTrack(): bool
    {
        return $this->audio !== null;
    }

    /**
     * The declared video track of a slot, or null.
     *
     * @return array{codecId: string, width: int, height: int, private: string}|null
     *
     * @psalm-mutation-free
     */
    public function getVideoTrack(int $slot = CallStream::VIDEO): ?array
    {
        return $this->video[$slot] ?? null;
    }

    /**
     * Whether a video track is H.265 (the frames arrive as Annex B like H.264, but are stored differently).
     *
     * @psalm-mutation-free
     */
    public function isHevc(int $slot = CallStream::VIDEO): bool
    {
        return ($this->video[$slot]['codecId'] ?? null) === 'V_MPEGH/ISO/HEVC';
    }

    /**
     * Write the EBML header, segment and track list. Called once, after the tracks are declared.
     */
    public function start(): void
    {
        if ($this->headerWritten || $this->closed) {
            return;
        }
        $this->headerWritten = true;

        $ebml = self::element(
            self::ID_EBML,
            self::uintElement("\x42\x86", 1)              // EBMLVersion
            .self::uintElement("\x42\xF7", 1)             // EBMLReadVersion
            .self::uintElement("\x42\xF2", 4)             // EBMLMaxIDLength
            .self::uintElement("\x42\xF3", 8)             // EBMLMaxSizeLength
            .self::stringElement("\x42\x82", $this->docType) // DocType
            .self::uintElement("\x42\x87", 4)             // DocTypeVersion
            .self::uintElement("\x42\x85", 2)             // DocTypeReadVersion
        );

        $infoBody = self::uintElement(self::ID_TIMESTAMP_SCALE, self::TIMESTAMP_SCALE_NS)
            .self::stringElement(self::ID_MUXING_APP, 'MadelineProto')
            .self::stringElement(self::ID_WRITING_APP, 'MadelineProto');
        // On a seekable output, reserve a Duration element (a 64-bit float, in TimestampScale units,
        // i.e. milliseconds) as the LAST element of Info, so its value is the last 8 bytes of $info;
        // close() rewinds to it and writes the real duration. Non-seekable streams stay Duration-less.
        if ($this->seekable) {
            $infoBody .= self::ID_DURATION.self::ebmlSize(8).pack('E', 0.0);
        }
        $info = self::element(self::ID_INFO, $infoBody);

        $tracks = '';
        foreach (self::VIDEO_SLOTS as $slot => $number) {
            $video = $this->video[$slot] ?? null;
            if ($video === null) {
                continue;
            }
            $entry = self::uintElement(self::ID_TRACK_NUMBER, $number)
                .self::uintElement(self::ID_TRACK_UID, $number)
                .self::uintElement(self::ID_TRACK_TYPE, self::TRACK_VIDEO)
                .self::stringElement(self::ID_CODEC_ID, $video['codecId'])
                .self::element(
                    self::ID_VIDEO,
                    self::uintElement(self::ID_PIXEL_WIDTH, $video['width'])
                    .self::uintElement(self::ID_PIXEL_HEIGHT, $video['height'])
                );
            if ($video['private'] !== '') {
                $entry .= self::element(self::ID_CODEC_PRIVATE, $video['private']);
            }
            $tracks .= self::element(self::ID_TRACK_ENTRY, $entry);
        }
        if ($this->audio !== null) {
            $entry = self::uintElement(self::ID_TRACK_NUMBER, self::AUDIO_TRACK_NUMBER)
                .self::uintElement(self::ID_TRACK_UID, self::AUDIO_TRACK_NUMBER)
                .self::uintElement(self::ID_TRACK_TYPE, self::TRACK_AUDIO)
                .self::stringElement(self::ID_CODEC_ID, $this->audio['codecId'])
                .self::element(
                    self::ID_AUDIO,
                    self::floatElement(self::ID_SAMPLING_FREQUENCY, (float) $this->audio['rate'])
                    .self::uintElement(self::ID_CHANNELS, $this->audio['channels'])
                );
            if ($this->audio['private'] !== '') {
                $entry .= self::element(self::ID_CODEC_PRIVATE, $this->audio['private']);
            }
            $tracks .= self::element(self::ID_TRACK_ENTRY, $entry);
        }

        // The segment uses the "unknown size" length so it can be streamed; players read to EOF.
        $header = $ebml.self::ID_SEGMENT."\x01\xFF\xFF\xFF\xFF\xFF\xFF\xFF";
        if ($this->seekable) {
            // The reserved Duration value is the last 8 bytes of $info, which follows $header.
            $this->durationOffset = \strlen($header) + \strlen($info) - 8;
        }
        $this->out->write($header.$info.self::element(self::ID_TRACKS, $tracks));
    }

    /**
     * Append one encoded video frame.
     */
    public function writeVideo(string $data, int $timestampMs, bool $keyframe, int $slot = CallStream::VIDEO): void
    {
        if (!isset($this->video[$slot])) {
            return;
        }
        $this->writeBlock(self::VIDEO_SLOTS[$slot], $data, $timestampMs, $keyframe, $keyframe);
    }

    /**
     * Append one encoded (OPUS) audio frame.
     */
    public function writeAudio(string $data, int $timestampMs): void
    {
        if ($this->audio === null) {
            return;
        }
        $this->writeBlock(self::AUDIO_TRACK_NUMBER, $data, $timestampMs, true, false);
    }

    private function writeBlock(int $trackNumber, string $data, int $timestampMs, bool $keyframe, bool $startsCluster): void
    {
        // !isset($this->out): a resumed writer is briefly waiting for its deferred reopen; drop until ready.
        if ($this->closed || !$this->headerWritten || !isset($this->out) || $data === '') {
            return;
        }
        $this->baseMs ??= $timestampMs;
        $relativeToFile = max(0, $timestampMs - $this->baseMs);
        $this->maxTimestampMs = max($this->maxTimestampMs ?? 0, $relativeToFile);

        // Start a new cluster on a video keyframe, or when the block would fall outside the open
        // cluster's signed-16-bit relative range.
        if ($this->clusterBaseMs === null
            || ($startsCluster && $trackNumber !== self::AUDIO_TRACK_NUMBER)
            || $relativeToFile - $this->clusterBaseMs > self::MAX_CLUSTER_MS
        ) {
            $this->flushCluster();
            $this->clusterBaseMs = $relativeToFile;
        }

        $relative = $relativeToFile - $this->clusterBaseMs;
        // Clamp into the SimpleBlock signed-16-bit relative timestamp.
        $relative = max(-32768, min(32767, $relative));

        $block = self::vint($trackNumber)          // track number as an EBML vint
            .pack('n', $relative & 0xFFFF)          // signed 16-bit relative timestamp
            .\chr($keyframe ? 0x80 : 0x00)          // flags: keyframe bit
            .$data;
        $this->clusterBody .= self::element(self::ID_SIMPLE_BLOCK, $block);
    }

    private function flushCluster(): void
    {
        if ($this->clusterBaseMs === null || $this->clusterBody === '') {
            $this->clusterBody = '';
            return;
        }
        $cluster = self::uintElement(self::ID_TIMESTAMP, $this->clusterBaseMs).$this->clusterBody;
        $this->out->write(self::element(self::ID_CLUSTER, $cluster));
        $this->clusterBody = '';
    }

    public function close(): void
    {
        if ($this->closed) {
            return;
        }
        $this->closed = true;
        // A resumed writer may be closed before its deferred reopen ran; there is nothing to flush then.
        if (!isset($this->out)) {
            return;
        }
        if ($this->headerWritten) {
            $this->flushCluster();
            // Back-patch the total Duration now that the last timestamp is known (seekable files only).
            if ($this->seekable && $this->durationOffset !== null && $this->maxTimestampMs !== null) {
                \assert($this->out instanceof File);
                $this->out->seek($this->durationOffset);
                $this->out->write(pack('E', (float) $this->maxTimestampMs));
                $this->out->seek(0, Whence::End);
            }
        }
        $this->out->end();
    }

    /* ----------------------------------------------------------------- *
     *  EBML encoding helpers.
     * ----------------------------------------------------------------- */

    /**
     * An element: id + size + payload.
     *
     * @psalm-pure
     */
    private static function element(string $id, string $payload): string
    {
        return $id.self::ebmlSize(\strlen($payload)).$payload;
    }

    /**
     * Encode a size as an EBML variable-length integer with its length descriptor.
     *
     * @psalm-pure
     */
    private static function ebmlSize(int $size): string
    {
        for ($len = 1; $len <= 8; $len++) {
            if ($size < (2 ** (7 * $len)) - 1) {
                break;
            }
        }
        $marker = 1 << (7 * $len);
        return self::intToBytes($marker | $size, $len);
    }

    /**
     * Encode an unsigned integer as an EBML vint (used for the SimpleBlock track number).
     *
     * @psalm-pure
     */
    private static function vint(int $value): string
    {
        for ($len = 1; $len <= 8; $len++) {
            if ($value < (2 ** (7 * $len)) - 1) {
                break;
            }
        }
        $marker = 1 << (7 * $len);
        return self::intToBytes($marker | $value, $len);
    }

    /**
     * @psalm-pure
     */
    private static function uintElement(string $id, int $value): string
    {
        $bytes = $value === 0 ? "\x00" : '';
        $v = $value;
        while ($v > 0) {
            $bytes = \chr($v & 0xFF).$bytes;
            $v >>= 8;
        }
        return self::element($id, $bytes);
    }

    /**
     * @psalm-pure
     */
    private static function stringElement(string $id, string $value): string
    {
        return self::element($id, $value);
    }

    /**
     * @psalm-pure
     */
    private static function floatElement(string $id, float $value): string
    {
        return self::element($id, pack('E', $value)); // 64-bit big-endian IEEE 754
    }

    /**
     * @psalm-pure
     */
    private static function intToBytes(int $value, int $len): string
    {
        $bytes = '';
        for ($i = $len - 1; $i >= 0; $i--) {
            $bytes .= \chr(($value >> (8 * $i)) & 0xFF);
        }
        return $bytes;
    }
}
<?php

declare(strict_types=1);

/**
 * ResponseException module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use Exception;

/**
 * Indicates an error thrown when an unexpected response is received from telegram's servers.
 */
final class ResponseException extends Exception
{
}
<?php declare(strict_types=1);

namespace danog\MadelineProto;

/**
 * Stream duplicator module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use Amp\ByteStream\ReadableStream;
use Amp\ByteStream\ReadableStreamIteratorAggregate;
use Amp\ByteStream\WritableStream;
use Amp\Cancellation;
use Closure;
use IteratorAggregate;

use function Amp\async;
use function Amp\Future\awaitAll;

/**
 * Stream duplicator.
 *
 * The secondary output stream is written to only when the StreamDuplicator is read.
 *
 * Thus, to fully duplicate a stream, both copies must be fully read until the end.
 *
 * @internal
 */
final class StreamDuplicator implements ReadableStream, IteratorAggregate
{
    use ReadableStreamIteratorAggregate;
    /** @var array<WritableStream> */
    private array $outputs;
    /**
     * @param ReadableStream $input Input stream
     * @param WritableStream ...$outputs Secondary output streams, only written to when the primary stream (this one) is read.
     *
     * @psalm-mutation-free
     */
    public function __construct(
        private readonly ReadableStream $input,
        WritableStream ...$outputs,
    ) {
        $this->outputs = $outputs;
    }
    #[\Override]
    public function read(?Cancellation $cancellation = null): ?string
    {
        $res = $this->input->read($cancellation);
        if ($res === null) {
            foreach ($this->outputs as $s) {
                $s->close();
            }
        } else {
            $f = [];
            foreach ($this->outputs as $k => $s) {
                if ($s->isClosed()) {
                    unset($this->outputs[$k]);
                } else {
                    $f []= async($s->write(...), $res);
                }
            }
            if ($f) {
                // Could be done in full async mode, but it makes close()s more complicated.
                // Not using a cancellation, as writes cannot be cleanly cancelled.
                awaitAll($f);
            }
        }
        return $res;
    }
    #[\Override]
    public function isReadable(): bool
    {
        return $this->input->isReadable();
    }
    #[\Override]
    public function close(): void
    {
        $this->input->close();
        foreach ($this->outputs as $s) {
            $s->close();
        }
    }
    #[\Override]
    public function isClosed(): bool
    {
        return $this->input->isClosed();
    }
    #[\Override]
    public function onClose(Closure $onClose): void
    {
        $this->input->onClose($onClose);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

/**
 * Indicates that a stream is resumable: it can be serialized, and once unserialized, reading continues
 * from the position of the last chunk that was read before serialization.
 */
interface ResumableStream
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use danog\MadelineProto\WebUI\Alert;

/**
 * Represents an event handler issue.
 */
final class EventHandlerIssue
{
    /**
     * @psalm-mutation-free
     */
    public function __construct(
        /** Issue message */
        public readonly string $message,
        /** Issue file */
        public readonly string $file,
        /** Issue line */
        public readonly int $line,
        /** Whether the issue is severe enough to block inclusion */
        public readonly bool $severe,
    ) {
    }

    /**
     * @psalm-external-mutation-free
     */
    public function __toString(): string
    {
        return \sprintf(
            Lang::$current_lang[$this->severe ? 'static_analysis_severe' : 'static_analysis_minor'],
            "{$this->file}:{$this->line}",
            $this->message
        );
    }

    public function log(): void
    {
        Logger::log((string) $this, $this->severe ? Logger::FATAL_ERROR : Logger::ERROR);
    }

    /**
     * @psalm-mutation-free
     */
    public function getAlert(): Alert
    {
        return $this->severe ? Alert::error((string) $this) : Alert::warning((string) $this);
    }

    public function getHTML(): string
    {
        $issueStr = htmlentities((string) $this);
        $color = $this->severe ? 'red' : 'orange';
        $warning = "<h2 style='color:$color;'>{$issueStr}</h2>";
        return $warning;
    }

    /**
     * @psalm-mutation-free
     */
    public function throw(): void
    {
        throw new Exception(message: (string) $this, file: $this->file, line: $this->line);
    }
}
<?php

declare(strict_types=1);

/**
 * MTProto module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use Amp\ByteStream\ReadableBuffer;
use Amp\Cache\Cache;
use Amp\Cache\LocalCache;
use Amp\Cancellation;
use Amp\CancelledException;
use Amp\DeferredFuture;
use Amp\Dns\DnsResolver;
use Amp\Future;
use Amp\Future\UnhandledFutureError;
use Amp\Http\Client\HttpClient;
use Amp\Http\Client\Request;
use Amp\Http\HttpStatus;
use Amp\Http\Server\DefaultErrorHandler;
use Amp\Http\Server\Request as ServerRequest;
use Amp\Http\Server\RequestHandler;
use Amp\Http\Server\Response as ServerResponse;
use Amp\Http\Server\SocketHttpServer;
use Amp\SignalException;
use Amp\Sync\LocalKeyedMutex;
use Amp\Sync\LocalMutex;
use danog\AsyncOrm\Annotations\OrmMappedArray;
use danog\AsyncOrm\DbArray;
use danog\AsyncOrm\DbArrayBuilder;
use danog\AsyncOrm\Driver\MemoryArray;
use danog\AsyncOrm\KeyType;
use danog\AsyncOrm\Settings as OrmSettings;
use danog\AsyncOrm\ValueType;
use danog\BetterPrometheus\BetterCounter;
use danog\BetterPrometheus\BetterGauge;
use danog\BetterPrometheus\BetterHistogram;
use danog\BetterPrometheus\BetterSummary;
use danog\MadelineProto\Broadcast\Broadcast;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\Ipc\Server;
use danog\MadelineProto\Loop\Generic\PeriodicLoopInternal;
use danog\MadelineProto\Loop\Update\FeedLoop;
use danog\MadelineProto\Loop\Update\SeqLoop;
use danog\MadelineProto\Loop\Update\UpdateLoop;
use danog\MadelineProto\MTProto\LoginState;
use danog\MadelineProto\MTProto\SpecialMethodType;
use danog\MadelineProto\MTProtoTools\AuthKeyHandler;
use danog\MadelineProto\MTProtoTools\CallHandler;
use danog\MadelineProto\MTProtoTools\CombinedUpdatesState;
use danog\MadelineProto\MTProtoTools\Files;
use danog\MadelineProto\MTProtoTools\MinDatabase;
use danog\MadelineProto\MTProtoTools\PasswordCalculator;
use danog\MadelineProto\MTProtoTools\PeerDatabase;
use danog\MadelineProto\MTProtoTools\PeerHandler;
use danog\MadelineProto\MTProtoTools\ReferenceDatabase;
use danog\MadelineProto\MTProtoTools\ResponseInfo;
use danog\MadelineProto\MTProtoTools\ResumableCalls;
use danog\MadelineProto\MTProtoTools\UpdateHandler;
use danog\MadelineProto\Reactive\Publisher;
use danog\MadelineProto\Settings\Database\DriverDatabaseAbstract;
use danog\MadelineProto\Settings\TLSchema;
use danog\MadelineProto\TL\Conversion\BotAPI;
use danog\MadelineProto\TL\Conversion\BotAPIFiles;
use danog\MadelineProto\TL\Conversion\TD;
use danog\MadelineProto\TL\TL;
use danog\MadelineProto\TL\TLCallback;
use danog\MadelineProto\TL\TLInterface;
use danog\MadelineProto\TL\Types\LoginQrCode;
use danog\MadelineProto\VoIP\CallState;
use danog\MadelineProto\WebUI\Alert;
use danog\MadelineProto\WebUI\AlertLevel;
use danog\MadelineProto\WebUI\Page;
use danog\MadelineProto\WebUI\SafeHtml;
use danog\MadelineProto\WebUI\Step;
use danog\MadelineProto\WebUI\TemplateEngine;
use danog\MadelineProto\WebUI\WebUI;
use danog\MadelineProto\Wrappers\Ads;
use danog\MadelineProto\Wrappers\Button;
use danog\MadelineProto\Wrappers\DialogHandler;
use danog\MadelineProto\Wrappers\Events;
use danog\MadelineProto\Wrappers\Login;
use danog\MadelineProto\Wrappers\Loop;
use danog\MadelineProto\Wrappers\Start;
use Prometheus\Counter;
use Prometheus\Gauge;
use Prometheus\Histogram;
use Prometheus\RendererInterface;
use Prometheus\RenderTextFormat;
use Prometheus\Summary;
use Psr\Log\LoggerInterface;
use Revolt\EventLoop;
use SplQueue;
use Throwable;
use Webmozart\Assert\Assert;

use function Amp\async;
use function Amp\ByteStream\pipe;
use function Amp\File\deleteFile;
use function Amp\File\getSize;
use function Amp\File\openFile;
use function Amp\Future\await;

use function time;

/**
 * Manages all of the mtproto stuff.
 *
 * @psalm-suppress PropertyNotSetInConstructor
 *
 * Dynamic properties are allowed only for unserialization: see __wakeup().
 *
 * @internal
 */
#[\AllowDynamicProperties]
final class MTProto implements TLCallback, LoggerGetter, SettingsGetter
{
    use AuthKeyHandler;
    use CallHandler;
    use PeerHandler;
    use UpdateHandler;
    use Files;
    use \danog\MadelineProto\SecretChats\AuthKeyHandler;
    use BotAPI;
    use BotAPIFiles;
    use TD;
    use \danog\MadelineProto\VoIP\AuthKeyHandler;
    use \danog\MadelineProto\GroupCall\Handler;
    use Ads;
    use Button;
    use DialogHandler;
    use Events;
    use Login;
    use Loop;
    use Start;
    use LegacyMigrator {
        LegacyMigrator::initDbProperties as private internalInitDbProperties;
        LegacyMigrator::saveDbProperties as private internalSaveDbProperties;
    }
    use Broadcast;
    use ResumableCalls;
    private const MAX_ENTITY_LENGTH = 100;
    private const MAX_ENTITY_SIZE = 8110;
    /** @internal */
    public const PFS_DURATION = 1*24*60*60;
    /**
     * Bad message error codes.
     *
     * @internal
     * @var array
     */
    public const BAD_MSG_ERROR_CODES = [
        16 => 'msg_id too low (most likely, client time is wrong; it would be worthwhile to synchronize it using msg_id notifications and re-send the original message with the correct msg_id or wrap it in a container with a new msg_id if the original message had waited too long on the client to be transmitted)',
        17 => 'msg_id too high (similar to the previous case, the client time has to be synchronized, and the message re-sent with the correct msg_id)',
        18 => 'incorrect two lower order msg_id bits (the server expects client message msg_id to be divisible by 4)',
        19 => 'container msg_id is the same as msg_id of a previously received message (this must never happen)',
        20 => 'message too old, and it cannot be verified whether the server has received a message with this msg_id or not',
        32 => 'msg_seqno too low (the server has already received a message with a lower msg_id but with either a higher or an equal and odd seqno)',
        33 => 'msg_seqno too high (similarly, there is a message with a higher msg_id but with either a lower or an equal and odd seqno)',
        34 => 'an even msg_seqno expected (irrelevant message), but odd received',
        35 => 'odd msg_seqno expected (relevant message), but even received',
        48 => 'incorrect server salt (in this case, the bad_server_salt response is received with the correct salt, and the message is to be re-sent with it)',
        64 => 'invalid container',
    ];
    /**
     * Localized message info flags.
     *
     * @internal
     * @var array
     */
    public const MSGS_INFO_FLAGS = [1 => 'nothing is known about the message (msg_id too low, the other party may have forgotten it)', 2 => 'message not received (msg_id falls within the range of stored identifiers; however, the other party has certainly not received a message like that)', 3 => 'message not received (msg_id too high; however, the other party has certainly not received it yet)', 4 => 'message received (note that this response is also at the same time a receipt acknowledgment)', 8 => ' and message already acknowledged', 16 => ' and message not requiring acknowledgment', 32 => ' and RPC query contained in message being processed or processing already complete', 64 => ' and content-related response to message already generated', 128 => ' and other party knows for a fact that message is already received'];
    /**
     * @internal
     */
    public const TD_PARAMS_CONVERSION = ['updateNewMessage' => ['_' => 'updateNewMessage', 'disable_notification' => ['message', 'silent'], 'message' => ['message']], 'message' => ['_' => 'message', 'id' => ['id'], 'sender_user_id' => ['from_id'], 'chat_id' => ['peer_id', 'choose_chat_id_from_botapi'], 'send_state' => ['choose_incoming_or_sent'], 'can_be_edited' => ['choose_can_edit'], 'can_be_deleted' => ['choose_can_delete'], 'is_post' => ['post'], 'date' => ['date'], 'edit_date' => ['edit_date'], 'forward_info' => ['fwd_info', 'choose_forward_info'], 'reply_to_message_id' => ['reply_to_msg_id'], 'ttl' => ['choose_ttl'], 'ttl_expires_in' => ['choose_ttl_expires_in'], 'via_bot_user_id' => ['via_bot_id'], 'views' => ['views'], 'content' => ['choose_message_content'], 'reply_markup' => ['reply_markup']], 'messages.sendMessage' => ['chat_id' => ['peer'], 'reply_to_message_id' => ['reply_to_msg_id'], 'disable_notification' => ['silent'], 'from_background' => ['background'], 'input_message_content' => ['choose_message_content'], 'reply_markup' => ['reply_markup']]];
    /**
     * @internal
     */
    public const TD_REVERSE = ['sendMessage' => 'messages.sendMessage'];
    /**
     * @internal
     */
    public const TD_IGNORE = ['updateMessageID'];
    /**
     * @internal
     */
    public const BOTAPI_PARAMS_CONVERSION = ['disable_web_page_preview' => 'no_webpage', 'disable_notification' => 'silent', 'reply_to_message_id' => 'reply_to_msg_id', 'chat_id' => 'peer', 'text' => 'message'];
    /**
     * Array of references to all instances of MTProto.
     *
     * This seems like a recipe for memory leaks, but this is actually required to allow saving the session on shutdown.
     * When using a network I/O-based database+the EvDriver of AMPHP, calling die(); causes premature garbage collection of the event loop.
     * This garbage collection happens always, even if a reference to the event handler is already present elsewhere (probably ev dark magic).
     *
     * Finally, this causes the process to hang on shutdown, since the database driver cannot receive a reply from the server, because the event loop is down.
     *
     * To avoid this, we store each MTProto instance in here (unreferencing on shutdown in unreference()), and call serialize() on all instances before calling die; in Magic.
     *
     * @var array<self>
     */
    public static array $references = [];
    /**
     * Instance of wrapper API.
     *
     */
    public APIWrapper $wrapper;
    /**
     * Settings object.
     *
     */
    public Settings $settings;
    /**
     * Config array.
     *
     */
    private array $config = ['expires' => -1];
    /**
     * Authorization info (User).
     *
     */
    public ?array $authorization = null;
    /**
     * Whether we're authorized.
     *
     * @var Publisher<LoginState>
     */
    public Publisher $loginState;
    /**
     * RSA keys.
     *
     * @var array<RSA>
     */
    private array $rsaKeys = [];
    /**
     * RSA keys.
     *
     * @var array<RSA>
     */
    private array $testRsaKeys = [];
    /**
     * CDN RSA keys.
     *
     * @var array<RSA>
     */
    private array $cdnRsaKeys = [];
    /**
     * Diffie-hellman config.
     *
     */
    private array $dh_config = ['version' => 0];
    /**
     * Cached parameters for fetching channel participants.
     *
     * @var DbArray<string, array>
     */
    #[OrmMappedArray(KeyType::STRING, ValueType::SCALAR)]
    public $channelParticipants;
    /**
     * When we last stored data in remote peer database (now doesn't exist anymore).
     *
     */
    public int $last_stored = 0;
    /**
     * Temporary array of data to be sent to remote peer database.
     *
     */
    public array $qres = [];
    /**
     * Sponsored message database.
     *
     * @var DbArray<int, array>
     */
    #[OrmMappedArray(KeyType::INT, ValueType::SCALAR)]
    public $sponsoredMessages;
    /**
     * Latest chat message ID map for update handling.
     *
     */
    private array $msg_ids = [];
    /**
     * Version value for upgrades.
     *
     */
    private string|int $v = 0;
    /**
     * Cached getdialogs params.
     *
     */
    private array $dialog_params = ['limit' => 0, 'offset_date' => 0, 'offset_id' => 0, 'offset_peer' => ['_' => 'inputPeerEmpty'], 'count' => 0];
    /**
     * Support user ID.
     *
     */
    private int $supportUser = 0;
    /**
     * File reference database.
     *
     */
    public ?ReferenceDatabase $referenceDatabase = null;
    /**
     * Min database.
     *
     */
    public MinDatabase $minDatabase;
    /**
     * Peer database.
     *
     */
    public PeerDatabase $peerDatabase;
    /**
     * Config loop.
     */
    public ?PeriodicLoopInternal $configLoop = null;
    /**
     * Autoserialization loop.
     */
    private ?PeriodicLoopInternal $serializeLoop = null;
    /**
     * SEQ update loop.
     */
    private ?SeqLoop $seqUpdater = null;
    /**
     * IPC server.
     */
    private ?Server $ipcServer = null;
    private ?LoginQrCode $loginQrCode = null;
    /**
     * Feeder loops.
     *
     * @var array<FeedLoop>
     */
    public array $feeders = [];
    /**
     * Updater loops.
     *
     * @var array<UpdateLoop>
     */
    public array $updaters = [];
    /**
     * DataCenter instance.
     *
     */
    public DataCenter $datacenter;
    /**
     * Logger instance.
     *
     */
    public Logger $logger;
    /**
     * TL serializer.
     */
    private TL $TL;

    private Cache $reportCache;

    /**
     * Snitch.
     */
    private Snitch $snitch;

    /**
     * DC list.
     */
    public array $dcList = [
        'test' => [
            // Test datacenters
            'ipv4' => [
                // ipv4 addresses
                10002 => [
                    // The rest will be fetched using help.getConfig
                    'ip_address' => '149.154.167.40',
                    'port' => 443,
                    'media_only' => false,
                    'tcpo_only' => false,
                ],
            ],
            'ipv6' => [
                // ipv6 addresses
                10002 => [
                    // The rest will be fetched using help.getConfig
                    'ip_address' => '2001:067c:04e8:f002:0000:0000:0000:000e',
                    'port' => 443,
                    'media_only' => false,
                    'tcpo_only' => false,
                ],
            ],
        ],
        'main' => [
            // Main datacenters
            'ipv4' => [
                // ipv4 addresses
                2 => [
                    // The rest will be fetched using help.getConfig
                    'ip_address' => '149.154.167.51',
                    'port' => 443,
                    'media_only' => false,
                    'tcpo_only' => false,
                ],
            ],
            'ipv6' => [
                // ipv6 addresses
                2 => [
                    // The rest will be fetched using help.getConfig
                    'ip_address' => '2001:067c:04e8:f002:0000:0000:0000:000a',
                    'port' => 443,
                    'media_only' => false,
                    'tcpo_only' => false,
                ],
            ],
        ],
    ];

    /**
     * Nullcache array for storing main session file to DB.
     */
    #[OrmMappedArray(KeyType::STRING, ValueType::SCALAR, cacheTtl: 0, optimizeIfWastedMb: 1, tablePostfix: 'session')]
    public DbArray $sessionDb;

    /**
     * Returns an instance of a client by session name.
     *
     * @internal
     *
     * @psalm-external-mutation-free
     */
    public static function giveInstanceBySession(string $session): MTProto
    {
        if (isset(self::$references[$session])) {
            return self::$references[$session];
        }
        // Created by a session that was moved (before the names it had were saved, see self::$sessionNames):
        // it's the only one, if only one is running.
        $instances = [];
        foreach (self::$references as $instance) {
            $instances[spl_object_id($instance)] = $instance;
        }
        if (\count($instances) === 1) {
            return reset($instances);
        }
        throw new Exception("The session $session is not running!");
    }

    /**
     * The names of the session, i.e. the paths it was loaded from: objects created by this instance (and
     * saved in the session) refer to it by the name it had when they were created, see {@see self::giveInstanceBySession()}.
     *
     * @var list<string>
     */
    private array $sessionNames = [];

    /**
     * Make this instance available by its session name, and by the names the session had before it was moved.
     */
    private function reference(): void
    {
        $name = $this->getSessionName();
        self::$references[$name] = $this;
        if (!\in_array($name, $this->sessionNames, true)) {
            $this->sessionNames[] = $name;
        }
        foreach ($this->sessionNames as $previous) {
            // Unless another session was loaded from there.
            self::$references[$previous] ??= $this;
        }
    }

    /** @internal */
    public function deleteSession(): void
    {
        $this->getDbAutoProperties();
        /** @psalm-suppress TypeDoesNotContainType */
        if (!isset($this->sessionDb) || $this->sessionDb instanceof MemoryArray) {
            return;
        }

        $db = [async($this->sessionDb->clear(...))];
        if ($this->referenceDatabase) {
            $db []= async($this->referenceDatabase->clear(...));
        }
        $db []= async($this->minDatabase->clear(...));
        $db []= async($this->peerDatabase->clearAll(...));
        foreach ($this->properties as $property) {
            $db []= async($property->clear(...));
        }
        if (isset($this->event_handler_instance)) {
            $db []= async($this->event_handler_instance->internalClearDbProperties(...));
        }
        await($db);
    }

    /**
     * Serialize session, returning object to serialize to db.
     *
     * @internal
     */
    public function serializeSession(object $data)
    {
        // Force migration
        $this->getDbAutoProperties();
        /** @psalm-suppress TypeDoesNotContainType */
        if (!isset($this->sessionDb) || $this->sessionDb instanceof MemoryArray) {
            return $data;
        }
        $this->sessionDb['data'] = $data;

        $db = [];
        $db []= async($this->referenceDatabase->saveDbProperties(...));
        $db []= async($this->minDatabase->saveDbProperties(...));
        $db []= async($this->peerDatabase->saveDbProperties(...));
        $db []= async($this->internalSaveDbProperties(...));
        if (isset($this->event_handler_instance)) {
            $db []= async($this->event_handler_instance->internalSaveDbProperties(...));
        }
        await($db);
        return new DbArrayBuilder(
            $this->getDbPrefix().'_MTProto_session',
            $this->getDbSettings(),
            KeyType::STRING,
            ValueType::SCALAR
        );
    }

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function findRsaKey(array $fps, bool $test, bool $cdn): ?RSA
    {
        if ($cdn) {
            $list = $this->cdnRsaKeys;
        } elseif ($test) {
            $list = $this->testRsaKeys;
        } else {
            $list = $this->rsaKeys;
        }

        foreach ($list as $curkey) {
            if (\in_array($curkey->fp, $fps, true)) {
                return $curkey;
            }
        }

        return null;
    }
    /**
     * Serialize all instances.
     *
     * CALLED ONLY ON SHUTDOWN.
     *
     * @internal
     */
    public static function serializeAll(): void
    {
        static $done = false;
        if ($done) {
            return;
        }
        $done = true;
        if (self::$references) {
            Logger::log('Prompting final serialization (SHUTDOWN)...');
            foreach (self::$references as $instance) {
                $instance->wrapper->serialize();
            }
            Logger::log('Done final serialization (SHUTDOWN)!');
        }
    }

    private ?Future $initPromise = null;
    private ?Future $authFuture = null;

    /**
     * Constructor function.
     *
     * @param Settings|SettingsEmpty $settings Settings
     * @param null|APIWrapper        $wrapper  API wrapper
     */
    public function __construct(Settings|SettingsEmpty $settings, ?APIWrapper $wrapper = null)
    {
        if ($wrapper) {
            $this->wrapper = $wrapper;
            $this->reference();
        }
        $q = new SplQueue;
        $q->setIteratorMode(SplQueue::IT_MODE_DELETE);
        $this->updateQueue ??= $q;
        $this->loginState = new Publisher(new LoginState(API::NOT_LOGGED_IN, null));

        $initDeferred = new DeferredFuture;
        $this->initPromise = $initDeferred->getFuture();
        try {
            $this->initialize($settings);
        } catch (Throwable $e) {
            try {
                $this->report((string) $e);
            } catch (Throwable) {
            }
            throw $e;
        } finally {
            $initDeferred->complete();
        }
    }

    /**
     * Renders prometheus stats using the specified renderer.
     *
     * By default uses the text renderer.
     */
    public function renderPromStats(?RendererInterface $renderer = null): string
    {
        return ($renderer ?? new RenderTextFormat)->render(
            GarbageCollector::$prometheus->storageAdapter->collect()
        );
    }

    /**
     * Creates and returns a prometheus gauge.
     *
     * Returns null if prometheus stats are disabled.
     *
     * @param array<string, string> $labels
     *
     * @psalm-external-mutation-free
     */
    public function getPromGauge(string $namespace, string $name, string $help, array $labels = []): ?BetterGauge
    {
        if (!$this->getSettings()->getMetrics()->getEnablePrometheusCollection()) {
            return null;
        }
        return GarbageCollector::$prometheus->getOrRegisterGauge(
            $namespace,
            $name,
            $help,
            $labels + ['session' => $this->getSessionName(), 'session_id' => (string) ($this->getSelf()['id'] ?? '')],
        );
    }

    /**
     * Creates and returns a prometheus counter.
     *
     * Returns null if prometheus stats are disabled.
     *
     * @param array<string, string> $labels
     *
     * @psalm-external-mutation-free
     */
    public function getPromCounter(string $namespace, string $name, string $help, array $labels = []): ?BetterCounter
    {
        if (!$this->getSettings()->getMetrics()->getEnablePrometheusCollection()) {
            return null;
        }
        return GarbageCollector::$prometheus->getOrRegisterCounter(
            $namespace,
            $name,
            $help,
            $labels + ['session' => $this->getSessionName(), 'session_id' => (string) ($this->getSelf()['id'] ?? '')],
        );
    }

    /**
     * Creates and returns a prometheus summary.
     *
     * Returns null if prometheus stats are disabled.
     *
     * @param array<string, string> $labels
     * @param ?non-empty-list<float> $quantiles
     *
     * @psalm-external-mutation-free
     */
    public function getPromSummary(string $namespace, string $name, string $help, array $labels = [], int $maxAgeSeconds = 600, ?array $quantiles = null): ?BetterSummary
    {
        if (!$this->getSettings()->getMetrics()->getEnablePrometheusCollection()) {
            return null;
        }
        return GarbageCollector::$prometheus->getOrRegisterSummary(
            $namespace,
            $name,
            $help,
            $labels + ['session' => $this->getSessionName(), 'session_id' => (string) ($this->getSelf()['id'] ?? '')],
            $maxAgeSeconds,
            $quantiles
        );
    }

    /**
     * Creates and returns a prometheus histogram.
     *
     * Returns null if prometheus stats are disabled.
     *
     * @param array<string, string> $labels
     * @param ?non-empty-list<float> $buckets
     *
     * @psalm-external-mutation-free
     */
    public function getPromHistogram(string $namespace, string $name, string $help, array $labels = [], ?array $buckets = null): ?BetterHistogram
    {
        if (!$this->getSettings()->getMetrics()->getEnablePrometheusCollection()) {
            return null;
        }
        return GarbageCollector::$prometheus->getOrRegisterHistogram(
            $namespace,
            $name,
            $help,
            $labels + ['session' => $this->getSessionName(), 'session_id' => (string) ($this->getSelf()['id'] ?? '')],
            $buckets
        );
    }

    /**
     * Initialization function.
     *
     * @internal
     */
    private function initialize(Settings|SettingsEmpty $settings): void
    {
        // Initialize needed stuffs
        Magic::start(light: false);
        // Parse and store settings
        $this->updateSettingsInternal($settings, false);
        // Actually instantiate needed classes like a boss
        $this->cleanupProperties();
        $this->datacenter->applySettings();
        // Load rsa keys
        $this->rsaKeys = [];
        foreach ($this->settings->getConnection()->getRSAKeys() as $key) {
            $key = RSA::load($this->TL, $key);
            $this->rsaKeys[$key->fp] = $key;
        }
        $this->testRsaKeys = [];
        foreach ($this->settings->getConnection()->getTestRSAKeys() as $key) {
            $key = RSA::load($this->TL, $key);
            $this->testRsaKeys[$key->fp] = $key;
        }
        // (re)-initialize TL
        $callbacks = [$this, $this->peerDatabase];
        if ($this->settings->getDb()->getEnableFileReferenceDb()) {
            $callbacks []= $this->referenceDatabase;
        }
        if (!($this->authorization['user']['bot'] ?? false) && $this->settings->getDb()->getEnableMinDb()) {
            $callbacks[] = $this->minDatabase;
        }
        $this->TL->init($this->settings->getSchema(), $callbacks);
        $this->startLoops();
        $this->datacenter->currentDatacenter = $this->settings->getConnection()->getTestMode() ? 10002 : 2;
        $this->getConfig();
        $this->v = API::RELEASE;

        $this->settings->applyChanges();
    }
    /**
     * Get API wrapper.
     *
     * @internal
     */
    public function getWrapper(): APIWrapper
    {
        return $this->wrapper;
    }
    /**
     * Returns the session name.
     *
     * @psalm-mutation-free
     */
    public function getSessionName(): string
    {
        return $this->wrapper->getSession()->getSessionDirectoryPath();
    }

    private ?string $tmpDbPrefix = null;

    /**
     * Called by IPC clients loaded with madeline.php when they connect, see GarbageCollector::syncIpcClientPhar.
     *
     * @internal
     */
    public function syncIpcClientPhar(string $release, string $phar, string $versionFile, string $loader): void
    {
        // Queued: a worker shuts down to update by throwing out of the event loop
        EventLoop::queue(function () use ($release, $phar, $versionFile, $loader): void {
            try {
                GarbageCollector::syncIpcClientPhar(
                    $release,
                    $phar,
                    $versionFile,
                    $loader,
                    \defined('MADELINE_PHAR') ? \MADELINE_PHAR : null,
                    \defined('MADELINE_PHAR_VERSION') ? \MADELINE_PHAR_VERSION : null,
                    \defined('MADELINE_PHP') ? \MADELINE_PHP : null,
                );
            } catch (SignalException $e) {
                throw $e;
            } catch (Throwable $e) {
                $this->logger->logger("Could not sync the phar of an IPC client: $e", Logger::ERROR);
            }
        });
    }

    /**
     * @internal
     *
     * @psalm-external-mutation-free
     */
    public function getDbPrefix(): string
    {
        $prefix = null;
        if ($this->settings->getDb() instanceof DriverDatabaseAbstract) {
            $prefix = $this->settings->getDb()->getEphemeralFilesystemPrefix();
        }
        $prefix ??= $this->getSelf()['id'] ?? null;
        if ($prefix === null) {
            $this->tmpDbPrefix ??= 'tmp_'.hash('xxh3', $this->getSessionName());
            $prefix = $this->tmpDbPrefix;
        }
        return (string) $prefix;
    }
    /** @internal */
    public function getDbSettings(): OrmSettings
    {
        return $this->settings->getDb()->getOrmSettings();
    }

    /**
     * Sleep function.
     *
     * @internal
     *
     * @psalm-external-mutation-free
     */
    public function __sleep(): array
    {
        return [
            // Databases
            'referenceDatabase',
            'minDatabase',
            'peerDatabase',
            'channelParticipants',
            'sponsoredMessages',

            'tmpDbPrefix',
            'sessionNames',

            // Misc caching
            'searchingRightPts',
            'bottomPts',
            'topPts',
            'botDialogsUpdatesState',
            'cachedAllBotUsers',
            'dialog_params',
            'last_stored',
            'qres',
            'supportUser',
            'broadcasts',
            'broadcastId',
            'loginQrCode',
            'fetchedFullDialogs',

            // Event handler
            'event_handler',
            'event_handler_instance',
            'pluginInstances',
            'updateQueue',
            'getUpdatesQueue',
            'getUpdatesQueueKey',
            'webhookUrl',

            'updateHandlerType',

            // Settings
            'settings',
            'config',
            'dcList',

            // Authorization keys
            'datacenter',

            // Authorization state
            'authorization',
            'loginState',

            // Authorization cache
            'rsaKeys',
            'testRsaKeys',
            'cdnRsaKeys',
            'dh_config',

            // Update state
            'got_state',
            'updateState',
            'msg_ids',

            // Version
            'v',

            // TL
            'TL',

            // Secret chats
            'secretChats',
            'temp_requested_secret_chats',

            // Report URI
            'reportDest',

            // Interrupted uploads, and the calls that made them
            'resumableUploads',
            'inlineFileIdKey',
            'resumableCalls',

            'calls',
            'callsByPeer',
            'groupCalls',
            'seenGroupCalls',
            'conferenceCalls',
            'snitch',

            'seqUpdater',
            'updaters',
            'feeders',
        ];
    }

    /**
     * Drop the properties removed since the session was saved (e.g. rsa_keys and test_rsa_keys, renamed to
     * rsaKeys and testRsaKeys: the keys are refetched).
     *
     * Not with __unserialize(): PHP calls it once the whole session is unserialized, after the __wakeup() of the
     * objects in it, which read the MTProto instance (DataCenter its settings, the databases its database settings,
     * the secret chat loops its logger...): its properties must be assigned as they are unserialized, by the default
     * unserialization, which recreates the removed ones as dynamic properties (allowed, see the class: else a
     * deprecation). __sleep() lists the declared ones, so they're never saved again.
     *
     * The removed properties whose state is migrated are read first, here: once dropped, they're gone.
     *
     * @internal
     */
    public function __wakeup(): void
    {
        // Sessions saved before the reactive login state (8.4.7): the defaults are those of the removed properties.
        if (!isset($this->loginState)) {
            /** @psalm-suppress UndefinedThisPropertyFetch */
            $this->loginState = new Publisher(new LoginState(
                $this->authorized ?? API::NOT_LOGGED_IN,
                $this->authorized_dc ?? null,
            ));
        }
        foreach (get_object_vars($this) as $name => $_) {
            // A private property of a class that is no longer an ancestor keeps its mangled name ("\0Class\0name"),
            // which can't be unset: unreachable by any code, it's dropped by the next save
            if (!property_exists(self::class, (string) $name) && !str_starts_with((string) $name, "\0")) {
                unset($this->{$name});
            }
        }
    }

    /**
     * Logger.
     *
     * @param mixed  $param Parameter
     * @param int    $level Logging level
     * @param string $file  File where the message originated
     *
     * @psalm-external-mutation-free
     */
    public function logger(mixed $param, int $level = Logger::NOTICE, string $file = ''): void
    {
        if (empty($file)) {
            $file = basename(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1)[0]['file'], '.php');
        }
        ($this->logger ?? Logger::$default)?->logger($param, $level, $file);
    }
    /**
     * Get TL namespaces.
     *
     * @psalm-mutation-free
     */
    public function getMethodNamespaces(): array
    {
        return $this->TL->getMethodNamespaces();
    }
    /**
     * Get namespaced methods (method => namespace).
     *
     * @psalm-mutation-free
     */
    public function getMethodsNamespaced(): array
    {
        return $this->TL->getMethodsNamespaced();
    }
    /**
     * Get TL serializer.
     *
     * @internal
     */
    public function getTL(): TLInterface
    {
        return $this->TL;
    }
    /**
     * Get logger.
     */
    #[\Override]
    public function getLogger(): Logger
    {
        return $this->logger;
    }
    /**
     * Get PSR logger.
     *
     * @psalm-mutation-free
     */
    public function getPsrLogger(): LoggerInterface
    {
        return $this->logger->getPsrLogger();
    }
    /**
     * Get async HTTP client.
     *
     * @psalm-mutation-free
     */
    public function getHTTPClient(): HttpClient
    {
        return $this->datacenter->getHTTPClient();
    }

    /**
     * Get async DNS client.
     *
     * @psalm-mutation-free
     */
    public function getDNSClient(): DnsResolver
    {
        return $this->datacenter->getDNSClient();
    }
    /**
     * Get contents of remote file asynchronously.
     *
     * @param string $url URL
     */
    public function fileGetContents(string $url, ?Cancellation $cancellation = null): string
    {
        return $this->getHTTPClient()->request(new Request($url), $cancellation)->getBody()->buffer($cancellation);
    }
    /**
     * Get main DC ID.
     *
     * @internal
     *
     * @psalm-mutation-free
     */
    public function getDataCenterId(): int|string
    {
        return $this->datacenter->currentDatacenter;
    }
    /**
     * Prompt serialization of instance.
     *
     * @internal
     */
    public function serialize(): void
    {
        if (isset($this->wrapper) && $this->isInited()) {
            $this->wrapper->serialize();
        }
    }
    /**
     * Start all internal loops.
     */
    private function startLoops(): void
    {
        $this->serializeLoop ??= new PeriodicLoopInternal($this, $this->serialize(...), 'serialize', $this->settings->getSerialization()->getInterval());
        $this->configLoop ??= new PeriodicLoopInternal($this, $this->getConfig(...), 'config', 3600);

        $this->serializeLoop->start();
        $this->configLoop->start();
        try {
            $this->ipcServer->start();
        } catch (Throwable $e) {
            if (Magic::$isIpcWorker) {
                throw $e;
            }
            $this->logger->logger("Error while starting IPC server: $e", Logger::FATAL_ERROR);
        }
    }
    /**
     * Stop all internal loops.
     */
    private function stopLoops(): void
    {
        if ($this->serializeLoop) {
            $this->serializeLoop->stop();
            $this->serializeLoop = null;
        }
        if ($this->configLoop) {
            $this->configLoop->stop();
            $this->configLoop = null;
        }
        if ($this->ipcServer) {
            $this->ipcServer->stop();
            $this->ipcServer = null;
        }
    }

    private ?SocketHttpServer $promServer = null;
    /**
     * Clean up properties from previous versions of MadelineProto.
     *
     * @internal
     */
    private function cleanupProperties(): void
    {
        if ($this->getSettings()->getMetrics()->getEnableMemprofCollection()) {
            if (!\extension_loaded('memprof')) {
                throw Exception::extension('memprof');
            }
            if (!memprof_enabled()) {
                throw new Exception("Memory profiling is not enabled, set the MEMPROF_PROFILE=1 environment variable or GET parameter to enable it.");
            }
        }
        $info = $this->getPromGauge("MadelineProto", "version", "Info about the MadelineProto instance");
        $info?->set(1, [
            'php_version' => PHP_VERSION,
            'madeline_version' => API::RELEASE,
        ]);
        $endpoint = $this->getSettings()->getMetrics()->getMetricsBindTo();
        $enablePrometheus = $this->getSettings()->getMetrics()->getEnablePrometheusCollection();
        $enableMemprof = $this->getSettings()->getMetrics()->getEnableMemprofCollection();
        $this->promServer?->stop();
        if ($endpoint === null || (!$enablePrometheus && !$enableMemprof)) {
            $this->promServer = null;
        } else {
            /** @psalm-suppress ImpureMethodCall */
            $this->promServer = SocketHttpServer::createForDirectAccess(
                $this->getPsrLogger()
            );
            $this->promServer->expose($endpoint);
            $this->promServer->start(new class($this) implements RequestHandler {
                /**
                 * @psalm-mutation-free
                 */
                public function __construct(
                    private readonly MTProto $API
                ) {
                }
                #[\Override]
                public function handleRequest(ServerRequest $request): ServerResponse
                {
                    if ($request->getUri()->getPath() === '/metrics') {
                        return new ServerResponse(
                            status: HttpStatus::OK,
                            headers: ['Content-Type' => 'text/plain'],
                            body: $this->API->renderPromStats(),
                        );
                    }
                    if ($request->getUri()->getPath() === '/debug/pprof'
                        && $this->API->getSettings()->getMetrics()->getEnableMemprofCollection()
                    ) {
                        return new ServerResponse(
                            status: HttpStatus::OK,
                            headers: ['Content-Type' => 'text/plain'],
                            body: $this->API->getMemoryProfile(),
                        );
                    }
                    $result = ResponseInfo::error(HttpStatus::NOT_FOUND);
                    return new ServerResponse(
                        $result->getCode(),
                        $result->getHeaders(),
                        $result->getCodeExplanation()
                    );
                }
            }, new DefaultErrorHandler);
        }
        $this->updateCtr = $this->getPromCounter("MadelineProto", "update_count", "Number of received updates since the session was created");
        // Start IPC server
        if (!$this->ipcServer) {
            $this->ipcServer = new Server($this);
            $this->ipcServer->setIpcPath($this->wrapper->getSession());
        }
        $this->ipcServer->start();
        if (!isset($this->updateQueue)) {
            $q = new SplQueue;
            $q->setIteratorMode(SplQueue::IT_MODE_DELETE);
            $this->updateQueue = $q;
        }
        if (isset($this->datacenter)) {
            $legacy = $this->datacenter->getLegacy();
            if ($legacy) {
                $currentDatacenter = $this->datacenter->currentDatacenter;
                $this->datacenter = new DataCenter($this);
                $this->datacenter->currentDatacenter = $currentDatacenter;
                foreach ($legacy as $dc => $socket) {
                    if ($dc > 0) {
                        $this->datacenter->getDataCenterConnection($dc, $socket);
                    }
                }
            }
        } else {
            $this->datacenter = new DataCenter($this);
        }

        if (isset($this->channels_state)) {
            $this->updateState = new CombinedUpdatesState;
            foreach ($this->channels_state->get() as $channelId => $state) {
                if ($channelId !== 0) {
                    $channelId = \danog\DialogId\DialogId::fromSupergroupOrChannelId($channelId);
                }
                $this->updateState->get($channelId)->update(
                    $channelId ? [
                        'pts' => $state->pts(),
                        'qts' => $state->qts(),
                        'seq' => $state->seq(),
                        'date' => $state->date(),
                    ] : [
                        'pts' => $state->pts(),
                    ]
                );
            }
            unset($this->channels_state, $this->feeders, $this->updaters);

        }

        $this->acceptChatMutex ??= new LocalKeyedMutex;
        $this->confirmChatMutex ??= new LocalKeyedMutex;
        $this->updateState ??= new CombinedUpdatesState;
        $this->snitch ??= new Snitch;

        $this->referenceDatabase ??= new ReferenceDatabase($this);
        $this->minDatabase ??= new MinDatabase($this);
        $this->peerDatabase ??= new PeerDatabase($this);

        $this->initDb();

        if (!isset($this->TL)) {
            $this->TL = new TL($this);
            $callbacks = [$this, $this->referenceDatabase, $this->peerDatabase];
            if (!($this->authorization['user']['bot'] ?? false)) {
                $callbacks[] = $this->minDatabase;
            }
            $this->TL->init($this->settings->getSchema(), $callbacks);
        }

        $this->updateState->get(FeedLoop::GENERIC);
        foreach ($this->updateState->get() as $state) {
            $channelId = $state->channelId;
            $feeder = $this->feeders[$channelId] ??= new FeedLoop($this, $channelId);
            $updater = $this->updaters[$channelId] ??= new UpdateLoop($this, $channelId);

            $this->loginState->subscribe($feeder);
            $this->loginState->subscribe($updater);
        }
        $this->seqUpdater ??= new SeqLoop($this);
        $this->loginState->subscribe($this->seqUpdater);

        foreach ($this->secretChats as $chat) {
            $chat->wakeupFeedLoop();
        }
    }

    /** @internal */
    public function initDb(): void
    {
        $db = [];
        $db []= async($this->referenceDatabase->init(...));
        $db []= async($this->minDatabase->init(...));
        $db []= async($this->peerDatabase->init(...));
        $db []= async($this->internalInitDbProperties(...), $this->getDbSettings(), $this->getDbPrefix().'_MTProto_');
        foreach ($this->secretChats as $chat) {
            $db []= async($chat->init(...));
        }
        await($db);
    }
    /**
     * Upgrade MadelineProto instance.
     */
    private function upgradeMadelineProto(): void
    {
        $this->logger->logger(Lang::$current_lang['serialization_ofd'], Logger::WARNING);
        $this->settings->setSchema(new TLSchema);

        $this->cleanupProperties();
        $this->resetMTProtoSession("upgrading madelineproto", true, true);
        $this->config = ['expires' => -1];
        $this->dh_config = ['version' => 0];
        $this->initialize($this->settings);
        foreach ($this->secretChats as $chat) {
            try {
                $chat->notifyLayer();
            } catch (RPCErrorException) {
            }
        }
    }
    /**
     * Post-deserialization initialization function.
     *
     * @param Settings|SettingsEmpty $settings New settings
     * @param APIWrapper             $wrapper  API wrapper
     *
     * @psalm-suppress UnsupportedPropertyReferenceUsage
     *
     * @internal
     */
    public function wakeup(SettingsAbstract $settings, APIWrapper $wrapper): void
    {
        // Setup one-time stuffs
        Magic::start(light: false);

        // Set API wrapper
        $this->wrapper = $wrapper;
        // Set reference to itself
        $this->reference();

        $deferred = new DeferredFuture;
        $this->initPromise = $deferred->getFuture();

        try {
            // Setup logger
            $this->setupLogger();

            // Cleanup old properties, init new stuffs
            $this->cleanupProperties();

            // Re-set TL closures
            $callbacks = [$this, $this->peerDatabase];
            if ($this->settings->getDb()->getEnableFileReferenceDb()) {
                $callbacks []= $this->referenceDatabase;
            }
            if (!($this->authorization['user']['bot'] ?? false) && $this->settings->getDb()->getEnableMinDb()) {
                $callbacks[] = $this->minDatabase;
            }

            $this->TL->updateCallbacks($callbacks);

            $this->settings->getConnection()->init();
            // Setup language
            Lang::$current_lang =& Lang::$lang['en'];
            Lang::$currentPercentage = 100;
            if (Lang::$lang[$this->settings->getAppInfo()->getLangCode()] ?? false) {
                Lang::$current_lang =& Lang::$lang[$this->settings->getAppInfo()->getLangCode()];
                Lang::$currentPercentage = Lang::PERCENTAGES[$this->settings->getAppInfo()->getLangCode()];
            } else {
                Lang::$currentPercentage = 0;
            }
            // Apply the connection settings changed before the session was saved
            $this->datacenter->applySettings();
            // Reset MTProto session (not related to user session)
            $this->resetMTProtoSession("wakeup");
            // Update settings from constructor
            $this->updateSettings($settings);
            // Update TL callbacks
            $callbacks = [$this, $this->peerDatabase];
            if ($this->settings->getDb()->getEnableFileReferenceDb()) {
                $callbacks[] = $this->referenceDatabase;
            }
            if ($this->settings->getDb()->getEnableMinDb() && !($this->authorization['user']['bot'] ?? false)) {
                $callbacks[] = $this->minDatabase;
            }

            $this->loginState->wakeup();

            // Before anything can use the restored calls (the updates the loops started below fetch, the
            // onStart of the event handler), drop the ones this session is not in, without requests: the
            // incoming calls it never answered (they may have been answered by another session of the
            // account) and the group calls it did not join (older versions tracked every group call it
            // got updates about). The private calls whose end we may have missed expire later.
            $ringTimeout = intdiv((int) ($this->config['call_ring_timeout_ms'] ?? 90000), 1000);
            $receiveTimeout = intdiv((int) ($this->config['call_receive_timeout_ms'] ?? 20000), 1000);
            foreach ($this->calls as $call) {
                $call->expireAfterRestart($ringTimeout, $receiveTimeout);
            }
            $this->dropUnjoinedGroupCalls();

            // Connect to all DCs, start internal loops
            $loggedIn = false;
            if ($this->fullGetSelf()) {
                $loggedIn = true;
                $this->setupLogger();
                $this->startLoops();
                $this->getCdnConfig();
            } else {
                $this->startLoops();
            }
            // onStart event handler
            if ($this->event_handler && class_exists($this->event_handler) && is_subclass_of($this->event_handler, EventHandler::class)) {
                $this->setEventHandler($this->event_handler);
            }

            if ($this->event_handler_instance instanceof EventHandler
                && $f = $this->event_handler_instance->waitForInternalStart()
            ) {
                $this->handlePostponedUpdates($f);
            }
            $this->cacheFullDialogs();

            foreach ($this->broadcasts as $broadcast) {
                $broadcast->resume();
            }
            if ($loggedIn) {
                $this->resumeCalls();
                // Resume the calls restored from the session only now that the MTProto session was reset
                // and connected: resuming makes requests, and those sent before the reset are never answered.
                foreach ([...$this->calls, ...$this->groupCalls, ...$this->conferenceCalls] as $call) {
                    EventLoop::queue($call->resumeAfterRestart(...));
                }
            }

            foreach ($this->calls as $id => $call) {
                if ($call->getCallState() === CallState::ENDED) {
                    $this->cleanupCall($id);
                } elseif ($call->getCallState() === CallState::REQUESTED && time() - $call->public->date > 5*60) {
                    EventLoop::queue($call->discard(...));
                }
            }

        } catch (Throwable $e) {
            try {
                $this->report((string) $e);
            } catch (Throwable) {
            }
            // The session lock is released when the exception leaves the API constructor: stop the IPC server
            // and drop the instance from the references (serialized at shutdown), so nothing touches the session
            // without holding the lock (a client changing the database settings deleted the session from the
            // database while the next worker was loading it).
            try {
                $this->unreference();
            } catch (Throwable) {
            }
            throw $e;
        } finally {
            $deferred->complete();
        }
    }
    /**
     * Unreference instance, allowing destruction.
     *
     * @internal
     */
    public function unreference(): void
    {
        if (!isset($this->logger)) {
            $this->logger = new Logger(new \danog\MadelineProto\Settings\Logger);
        }
        $this->logger->logger('Will unreference instance');
        foreach (self::$references as $name => $instance) {
            if ($instance === $this) {
                unset(self::$references[$name]);
            }
        }
        $this->stopLoops();
        // Free the metrics port: when a new session is handed over to its IPC worker, the worker binds it.
        if ($this->promServer) {
            $promServer = $this->promServer;
            $this->promServer = null;
            try {
                $promServer->stop();
            } catch (Throwable $e) {
                $this->logger->logger("Error while stopping the metrics server: $e", Logger::ERROR);
            }
        }
        if (isset($this->seqUpdater)) {
            $this->seqUpdater->stop();
        }
        if (isset($this->updateState)) {
            foreach ($this->updateState->get() as $channelId => $_) {
                if (isset($this->feeders[$channelId])) {
                    $this->feeders[$channelId]->stop();
                }
                if (isset($this->updaters[$channelId])) {
                    $this->updaters[$channelId]->stop();
                }
            }
        }
        if (isset($this->datacenter)) {
            foreach ($this->datacenter->getDataCenterConnections() as $datacenter) {
                $datacenter->disconnect();
            }
        }
        $this->logger->logger('Unreferenced instance');
    }
    /** @internal */
    public function isCdn(int $dc): bool
    {
        $test = $this->settings->getConnection()->getTestMode() ? 'test' : 'main';
        $ipv6 = $this->settings->getConnection()->getIpv6() ? 'ipv6' : 'ipv4';
        return $this->dcList[$test][$ipv6][$dc]['cdn'] ?? false;
    }
    /**
     * Destructor.
     */
    public function __destruct()
    {
        $this->logger('Shutting down MadelineProto (MTProto)');
        $this->unreference();
        $this->logger('Successfully destroyed MadelineProto');
    }
    /**
     * @internal
     * @param API::NOT_LOGGED_IN|API::WAITING_*|API::LOGGED_IN|API::LOGGED_OUT $state
     */
    public function setLoginState(int $state): void
    {
        $this->loginState->publish($this->loginState->getState()->setState($state));
    }
    /**
     * @internal
     */
    public function isInited(): bool
    {
        return $this->initPromise?->isComplete() ?? false;
    }
    /**
     * @internal
     */
    public function waitForInit(): void
    {
        $this->initPromise?->await();
    }
    /**
     * Whether we're an IPC client instance.
     *
     * @psalm-pure
     */
    public function isIpc(): bool
    {
        return false;
    }
    /**
     * Whether we're an IPC server process (as opposed to an event handler).
     *
     * @psalm-external-mutation-free
     */
    public function isIpcWorker(): bool
    {
        return Magic::$isIpcWorker;
    }
    /**
     * Parse, update and store settings.
     *
     * @param SettingsAbstract $settings Settings
     */
    public function updateSettings(SettingsAbstract $settings): void
    {
        $this->updateSettingsInternal($settings);
    }
    /**
     * Parse, update and store settings.
     *
     * @param SettingsAbstract $settings Settings
     */
    private function updateSettingsInternal(SettingsAbstract $settings, bool $recurse = true): void
    {
        if ($settings instanceof SettingsEmpty) {
            if (!isset($this->settings)) {
                $this->settings = new Settings;
            } else {
                if ($this->v !== API::RELEASE || $this->settings->getSchema()->needsUpgrade()) {
                    $this->setupLogger();
                    $this->logger->logger("Generic settings have changed!", Logger::WARNING);
                    $this->upgradeMadelineProto();
                }
                return;
            }
        } else {
            if (!isset($this->settings)) {
                if ($settings instanceof Settings) {
                    $this->settings = $settings;
                } else {
                    $this->settings = new Settings;
                    $this->settings->merge($settings);
                }
            } else {
                $this->settings->merge($settings);
            }
        }
        if (!$this->settings->getAppInfo()->hasApiInfo()) {
            throw new Exception(Lang::$current_lang['api_not_set'], 0, null, 'MadelineProto', 1);
        }
        /** @var Settings $this->settings */

        // Setup logger
        if ($this->settings->getLogger()->hasChanged() || !isset($this->logger)) {
            $this->setupLogger();
        }

        if ($this->settings->getSchema()->getFuzzMode()) {
            RPCErrorException::$errorMethodMap = [];
            RPCErrorException::$descriptions = [];
        }

        if ($this->settings->getDb()->hasChanged()) {
            $this->logger->logger("The database settings have changed!", Logger::WARNING);
            $this->cleanupProperties();
            $this->settings->getDb()->applyChanges();
        }
        if ($this->settings->getMetrics()->hasChanged()) {
            $this->logger->logger("The prometheus settings have changed!", Logger::WARNING);
            $this->cleanupProperties();
            $this->settings->getMetrics()->applyChanges();
        }
        if ($this->settings->getSerialization()->hasChanged()) {
            $this->logger->logger("The serialization settings have changed!", Logger::WARNING);
            if (isset($this->serializeLoop)) {
                $this->serializeLoop->stop();
            }
            $this->serializeLoop = new PeriodicLoopInternal($this, $this->serialize(...), 'serialize', $this->settings->getSerialization()->applyChanges()->getInterval());
            $this->serializeLoop->start();
        }
        if ($recurse && ($this->settings->getAuth()->hasChanged()
            || $this->settings->getConnection()->hasChanged()
            || $this->settings->getSchema()->hasChanged()
            || $this->settings->getSchema()->needsUpgrade()
            || $this->v !== API::RELEASE)) {
            $this->setupLogger();
            $this->logger->logger("Generic settings have changed!", Logger::WARNING);
            if ($this->v !== API::RELEASE || $this->settings->getSchema()->needsUpgrade()) {
                $this->upgradeMadelineProto();
            }
            $this->initialize($this->settings);
        }
    }
    /**
     * Return current settings.
     */
    #[\Override]
    public function getSettings(): Settings
    {
        return $this->settings;
    }
    /**
     * Setup logger.
     *
     * @internal
     */
    public function setupLogger(): void
    {
        $this->logger = new Logger(
            $this->settings->getLogger(),
            (string) ($this->authorization['user']['username'] ?? $this->authorization['user']['id'] ?? ''),
        );
    }
    /**
     * Reset all MTProto sessions.
     *
     * @param boolean $de       Whether to reset the session ID
     * @param boolean $auth_key Whether to reset the auth key
     * @internal
     */
    public function resetMTProtoSession(string $why, bool $de = true, bool $auth_key = false): void
    {
        if (!\is_object($this->datacenter)) {
            throw new Exception(Lang::$current_lang['session_corrupted']);
        }
        foreach ($this->datacenter->getDataCenterConnections() as $id => $socket) {
            if ($de) {
                $socket->resetSession("resetMTProtoSession: $why");
            }
            if ($auth_key) {
                $socket->auth->setTempAuthKey(null, null);
            }
        }
    }
    /**
     * Reset the update state and fetch all updates from the beginning.
     */
    public function resetUpdateState(): void
    {
        if (isset($this->seqUpdater)) {
            $this->seqUpdater->stop();
        }
        $new = new CombinedUpdatesState;
        $channelIds = [];
        foreach ($this->updateState->get() as $state) {
            $channelIds[] = $state->channelId;
            $channelId = $state->channelId;
            $pts = $state->pts();
            $pts = $channelId ? max(1, $pts - 1000000) : ($pts > 4000000 ? $pts - 1000000 : max(1, $pts - 1000000));
            $new->get($channelId, ['pts' => $pts]);
        }
        sort($channelIds);
        foreach ($channelIds as $channelId) {
            if (isset($this->feeders[$channelId])) {
                $this->feeders[$channelId]->stop();
            }
            if (isset($this->updaters[$channelId])) {
                $this->updaters[$channelId]->stop();
            }
        }
        $this->updateState = $new;

        foreach ($channelIds as $channelId) {
            if (isset($this->feeders[$channelId])) {
                $this->feeders[$channelId]->start();
            }
            if (isset($this->updaters[$channelId])) {
                $this->updaters[$channelId]->start();
            }
        }
    }
    /**
     * Store RSA keys for CDN datacenters.
     */
    public function getCdnConfig(): void
    {
        try {
            foreach (($this->methodCallAsyncRead('help.getCdnConfig', [], $this->loginState->getState()->authorizedDc))['public_keys'] as $curkey) {
                $curkey = RSA::load($this->TL, $curkey['public_key']);
                $this->cdnRsaKeys[$curkey->fp] = $curkey;
            }
        } catch (\danog\MadelineProto\TL\Exception $e) {
            $this->logger->logger($e->getMessage(), Logger::FATAL_ERROR);
        } catch (RPCErrorException|CancelledException $e) {
            // Called by the update loops (and when starting): failing to refresh the keys of the CDN DCs (a timeout
            // while reconnecting, for example) must not end them. They're fetched again when connecting to a CDN DC
            // (AuthKeyHandler).
            $this->logger->logger("Could not get the CDN configuration: $e", Logger::ERROR);
        }
    }
    /**
     * Get cached server-side config.
     */
    public function getCachedConfig(): array
    {
        return $this->config;
    }
    /**
     * Get cached (or eventually re-fetch) server-side config.
     *
     * @param array $config Current config
     */
    public function getConfig(array $config = []): array
    {
        if ($this->config['expires'] > time()) {
            return $this->config;
        }
        $this->config = empty($config) ? $this->methodCallAsyncRead('help.getConfig', $config) : $config;
        $this->parseConfig();
        $this->logger->logger('Updated config!', Logger::NOTICE);
        return $this->config;
    }
    /**
     * Parse cached config.
     *
     * @psalm-external-mutation-free
     */
    private function parseConfig(): void
    {
        if (isset($this->config['dc_options'])) {
            $options = $this->config['dc_options'];
            unset($this->config['dc_options']);
            $this->parseDcOptions($options);
        }
    }
    /**
     * Whether we're currently connected to the test DCs.
     *
     * @return boolean
     *
     * @psalm-mutation-free
     */
    public function isTestMode(): bool
    {
        return $this->settings->getConnection()->getTestMode();
    }
    /**
     * Parse DC options from config.
     *
     * @param array $dc_options DC options
     *
     * @psalm-external-mutation-free
     */
    private function parseDcOptions(array $dc_options): void
    {
        $new = [];
        foreach ($dc_options as $dc) {
            if ($dc['static']) {
                continue;
            }

            $test = $this->config['test_mode'] ? 'test' : 'main';
            $id = $dc['id'];
            if ($this->config['test_mode']) {
                $id += 10000;
            }
            if ($dc['media_only']) {
                $id = -$id;
            }
            $ipv6 = $dc['ipv6'] ? 'ipv6' : 'ipv4';
            unset($dc['media_only'], $dc['id'], $dc['ipv6']);
            $new[$test][$ipv6][$id] = $dc;
        }
        $this->dcList = $new;
    }
    /**
     * Get info about the logged-in user, cached.
     *
     * Use fullGetSelf to bypass the cache.
     *
     * @psalm-mutation-free
     */
    public function getSelf(): array|false
    {
        return $this->authorization['user'] ?? false;
    }
    /**
     * Returns whether the current user is a bot.
     *
     * @psalm-mutation-free
     */
    public function isSelfBot(): bool
    {
        if ($this->loginState->getState()->state !== API::LOGGED_IN) {
            throw new Exception('Not logged in!');
        }
        return $this->authorization['user']['bot'];
    }
    /**
     * Returns whether the current user is a user.
     *
     * @psalm-mutation-free
     */
    public function isSelfUser(): bool
    {
        return !$this->authorization['user']['bot'];
    }
    /**
     * Returns whether the current user is a premium user, cached.
     *
     * @psalm-mutation-free
     */
    public function isPremium(): bool
    {
        return $this->getSelf()['premium'];
    }
    /**
     * Get info about the logged-in user, not cached.
     */
    public function fullGetSelf(): array|false
    {
        try {
            $this->authorization = ['user' => ($this->methodCallAsyncRead('users.getUsers', ['id' => [['_' => 'inputUserSelf']], 'specialMethodType' => SpecialMethodType::USER_RELATED]))[0]];
        } catch (RPCErrorException $e) {
            $this->logger->logger($e->getMessage());
            return false;
        }
        return $this->getSelf();
    }
    /**
     * Get authorization info.
     *
     * @return \danog\MadelineProto\API::NOT_LOGGED_IN|\danog\MadelineProto\API::WAITING_CODE|\danog\MadelineProto\API::WAITING_SIGNUP|\danog\MadelineProto\API::WAITING_PASSWORD|\danog\MadelineProto\API::LOGGED_IN|API::LOGGED_OUT
     *
     * @psalm-mutation-free
     */
    public function getAuthorization(): int
    {
        return $this->loginState->getState()->state;
    }
    /**
     * Get current password hint.
     *
     * @psalm-mutation-free
     */
    public function getHint(): string
    {
        if ($this->loginState->getState()->state !== API::WAITING_PASSWORD) {
            throw new Exception('Not waiting for the password!');
        }
        // Optional: an empty string if the account has none.
        $hint = $this->authorization['hint'] ?? '';
        Assert::string($hint);
        return $hint;
    }
    /**
     * Get what the web UI shows about the login in progress: the phone number, and the kind of code sent.
     *
     * Never the phone code hash, nor the code.
     *
     * @internal
     *
     * @return array{phoneNumber?: string, sentCode?: array{type: string, length: ?int, nextType: ?string, timeout: ?int}}
     */
    public function getWebLoginInfo(): array
    {
        $state = $this->loginState->getState()->state;
        $info = [];
        if (($state === API::WAITING_CODE || $state === API::WAITING_SIGNUP) && \is_string($this->authorization['phone_number'] ?? null)) {
            $info['phoneNumber'] = $this->authorization['phone_number'];
        }
        if ($state === API::WAITING_CODE && \is_string($this->authorization['type']['_'] ?? null)) {
            $info['sentCode'] = [
                'type' => $this->authorization['type']['_'],
                'length' => isset($this->authorization['type']['length']) ? (int) $this->authorization['type']['length'] : null,
                'nextType' => $this->authorization['next_type']['_'] ?? null,
                'timeout' => isset($this->authorization['timeout']) ? (int) $this->authorization['timeout'] : null,
            ];
        }
        return $info;
    }
    /**
     * IDs of peers where to report errors.
     *
     * @var list<int>
     */
    private array $reportDest = [];
    /**
     * Admin IDs.
     *
     * @var list<int>
     */
    private array $admins = [];
    /**
     * Check if has report peers.
     *
     * @psalm-mutation-free
     */
    public function hasReportPeers(): bool
    {
        return (bool) $this->reportDest;
    }
    /**
     * Check if has admins.
     *
     * @psalm-mutation-free
     */
    public function hasAdmins(): bool
    {
        return (bool) $this->admins;
    }
    /**
     * Get admin IDs (equal to all user report peers).
     */
    public function getAdminIds(): array
    {
        return $this->admins;
    }
    /**
     * Get a message to show to the user when starting the bot.
     */
    public function getWebMessage(string $message): string
    {
        Logger::log($message);

        $page = new Page(
            title: $message,
            view: 'status',
            step: Step::DONE,
            alerts: self::getWebAlerts(),
        );
        if ($this->hasEventHandler()) {
            $page->lead = Lang::$current_lang['webDoneLead'];
            if (!$this->hasReportPeers()) {
                Logger::log('!!! '.Lang::$current_lang['noReportPeers'].' !!!', Logger::FATAL_ERROR);
                Logger::log("!!! public function getReportPeers() { return '@yourtelegramusername'; } !!!", Logger::FATAL_ERROR);
                $page->addAlert(new Alert(
                    AlertLevel::ERROR,
                    Lang::$current_lang['noReportPeers'],
                    code: "public function getReportPeers() { return '@yourtelegramusername'; }",
                ));
            }
            $classes = [];
            if ($this->event_handler_instance instanceof EventHandler) {
                $classes[] = $this->event_handler_instance::class;
            }
            foreach ($this->pluginInstances as $class => $_) {
                $classes[] = $class;
            }
            foreach ($classes as $class) {
                foreach (Tools::validateEventHandlerClass($class) as $issue) {
                    $issue->log();
                    $page->addAlert($issue->getAlert());
                }
            }
        }

        return WebUI::render($this->settings->getTemplates(), $page);
    }
    /**
     * Get various warnings to show to the user in the web UI, as alerts.
     *
     * @internal
     *
     * @return list<Alert>
     */
    public static function getWebAlerts(): array
    {
        Magic::start(light: false);
        $alerts = [];
        if (API::RELEASE !== Magic::$latest_release) {
            $alerts[] = Alert::error(\sprintf(
                Lang::$current_lang['update_madelineproto'],
                API::RELEASE,
                Magic::$latest_release,
            ));
        }
        if (!Magic::$hasOpenssl) {
            $alerts[] = Alert::warning(\sprintf(Lang::$current_lang['extensionRecommended'], 'openssl'));
        }
        if (!\extension_loaded('gmp')) {
            $alerts[] = Alert::warning(\sprintf(Lang::$current_lang['extensionRecommended'], 'gmp'));
        }
        if (!\extension_loaded('uv')) {
            $alerts[] = Alert::info(\sprintf(Lang::$current_lang['extensionRecommended'], 'uv'));
        }
        if (Lang::$currentPercentage !== 100) {
            $alerts[] = Alert::info(new SafeHtml(\sprintf(Lang::$current_lang['translate_madelineproto_web'], Lang::$currentPercentage)));
        }
        return $alerts;
    }
    /**
     * Get various warnings to show to the user in the web UI.
     */
    public static function getWebWarnings(): string
    {
        return (new TemplateEngine)->render('partials/alerts', ['alerts' => self::getWebAlerts()]);
    }

    /** @internal */
    public function rethrowInner(\Throwable $e, bool $now = false): void
    {
        if ($e instanceof UnhandledFutureError) {
            $e = $e->getPrevious();
            \assert($e !== null);
        }
        if ($e instanceof SecurityException || $e instanceof SignalException) {
            if ($now) {
                throw $e;
            }
            AsyncTools::rethrow($e);
            return;
        }
        if (str_starts_with($e->getMessage(), 'Could not connect to DC ')) {
            if ($now) {
                throw $e;
            }
            AsyncTools::rethrow($e);
            return;
        }
        $eOrig = (string) $e;
        $e = Tools::taintEscape((string) $e);
        echo $e;
        $this->wrapper->logger($eOrig, Logger::FATAL_ERROR);
        $this->report("Surfaced: $eOrig");
    }

    /**
     * Sanitize peer(s) where to send errors occurred in the event loop.
     *
     * @internal
     * @param int|string|array<int|string> $userOrId Username(s) or peer ID(s)
     *
     * @return array<int>
     */
    public function sanitizeReportPeers(int|string|array $userOrId): array
    {
        if (!(\is_array($userOrId) && !isset($userOrId['_']) && !isset($userOrId['id']))) {
            $userOrId = [$userOrId];
        }
        $selfBot = $this->getSelf()['bot'];
        foreach ($userOrId as $k => &$peer) {
            try {
                $peer = $this->getInfo($peer);
                $type = $peer['type'];
                $peer = $peer['bot_api_id'];
                if ($type === 'bot' && $selfBot) {
                    unset($userOrId[$k]);
                    $this->logger("Can't use a bot as report peer: $peer", Logger::FATAL_ERROR);
                    continue;
                }
            } catch (Throwable $e) {
                unset($userOrId[$k]);
                $peer = json_encode($peer);
                $this->logger("Could not obtain info about report peer $peer: $e", Logger::FATAL_ERROR);
            }
        }
        /** @var array<int> $userOrId */
        return array_values($userOrId);
    }
    /**
     * Set peer(s) where to send errors occurred in the event loop.
     *
     * @param int|string|array<int|string> $userOrId Username(s) or peer ID(s)
     */
    public function setReportPeers(int|string|array $userOrId): void
    {
        $this->reportDest = $this->sanitizeReportPeers($userOrId);
        $this->admins = array_values(array_filter($this->reportDest, static fn (int $v) => $v > 0));
    }
    private ?LocalMutex $reportMutex = null;
    /**
     * Sends a message to all report peers (admins of the bot).
     *
     * @param string       $message      Message to send
     * @param ParseMode    $parseMode    Parse mode
     * @param array|null   $replyMarkup  Keyboard information.
     * @param integer|null $scheduleDate Schedule date.
     * @param boolean      $silent       Whether to send the message silently, without triggering notifications.
     * @param boolean      $background   Send this message as background message
     * @param boolean      $clearDraft   Clears the draft field
     * @param boolean      $noWebpage    Set this flag to disable generation of the webpage preview
     *
     * @return list<\danog\MadelineProto\EventHandler\Message>
     */
    public function sendMessageToAdmins(
        string $message,
        ParseMode $parseMode = ParseMode::TEXT,
        ?array $replyMarkup = null,
        ?int $scheduleDate = null,
        bool $silent = false,
        bool $noForwards = false,
        bool $background = false,
        bool $clearDraft = false,
        bool $noWebpage = false,
        ?Cancellation $cancellation = null
    ): array {
        $result = [];
        foreach ($this->admins as $report) {
            $result []= $this->sendMessage(
                peer: $report,
                message: $message,
                parseMode: $parseMode,
                replyMarkup: $replyMarkup,
                scheduleDate: $scheduleDate,
                silent: $silent,
                noForwards: $noForwards,
                background: $background,
                clearDraft: $clearDraft,
                noWebpage: $noWebpage,
                cancellation: $cancellation
            );
        }
        return $result;
    }
    /**
     * Report an error to the previously set peer.
     *
     * @param string $message   Error to report
     * @param string $parseMode Parse mode
     */
    public function report(string $message, string $parseMode = ''): void
    {
        $this->logger->logger("Reporting: $message", Logger::FATAL_ERROR);
        if (!$this->reportDest) {
            return;
        }

        $this->reportCache ??= new LocalCache();
        $key = hash('xxh128', $message);
        if ($this->reportCache->get($key)) {
            return;
        }
        $this->reportCache->set($key, true, 60);

        $this->reportMutex ??= new LocalMutex;
        $lock = $this->reportMutex->acquire();
        try {
            $file = null;
            if ($this->settings->getLogger()->getType() === Logger::FILE_LOGGER
                && $path = $this->settings->getLogger()->getExtra()) {
                $temp = tempnam(sys_get_temp_dir(), 'madelinelog');
                pipe(openFile($path, 'r'), openFile($temp, 'w'));
                $path = $temp;
                try {
                    if (!getSize($path)) {
                        $message = "!!! WARNING !!!\nThe logfile is empty, please DO NOT delete the logfile to avoid errors in MadelineProto!\n\n$message";
                    } else {
                        $file = $this->methodCallAsyncRead(
                            'messages.uploadMedia',
                            [
                                'peer' => $this->reportDest[0],
                                'media' => [
                                    '_' => 'inputMediaUploadedDocument',
                                    'file' => $path,
                                    'attributes' => [
                                        ['_' => 'documentAttributeFilename', 'file_name' => 'MadelineProto.log'],
                                    ],
                                ],
                            ],
                        );
                    }
                } finally {
                    deleteFile($path);
                }
            }
            $sent = false;
            foreach ($this->reportDest as $id) {
                try {
                    $this->methodCallAsyncRead('messages.sendMessage', ['peer' => $id, 'message' => $message, 'parse_mode' => $parseMode]);
                    if ($file) {
                        $this->methodCallAsyncRead('messages.sendMedia', ['peer' => $id, 'media' => $file]);
                    }
                    $sent = true;
                } catch (Throwable $e) {
                    $this->logger("While reporting to $id: $e", Logger::FATAL_ERROR);
                }
            }
            if ($sent && $file) {
                $this->logger->truncate();
                $this->logger->logger('Reported!');
            }
        } finally {
            EventLoop::queue($lock->release(...));
        }
    }
    /**
     * Get memory profile with memprof.
     */
    public function getMemoryProfile(): string
    {
        if (!\extension_loaded('memprof')) {
            throw Exception::extension('memprof');
        }
        if (!memprof_enabled()) {
            throw new Exception("Memory profiling is not enabled, set the MEMPROF_PROFILE=1 environment variable or GET parameter to enable it.");
        }
        $file = fopen('php://memory', 'r+');
        memprof_dump_pprof($file);
        fseek($file, 0);

        return stream_get_contents($file);
    }

    /**
     * Report memory profile with memprof.
     */
    public function reportMemoryProfile(): void
    {
        $pprof = $this->getMemoryProfile();

        $current = "Current memory usage: ".round(memory_get_usage()/1024/1024, 1) . " MB";
        $file = [
            '_' => 'inputMediaUploadedDocument',
            'file' => new ReadableBuffer($pprof),
            'attributes' => [
                ['_' => 'documentAttributeFilename', 'file_name' => 'report.pprof'],
            ],
        ];
        foreach ($this->reportDest as $id) {
            try {
                $this->methodCallAsyncRead('messages.sendMedia', ['peer' => $id, 'message' => $current, 'media' => $file]);
            } catch (Throwable $e) {
                $this->logger("While reporting memory profile to $id: $e", Logger::FATAL_ERROR);
            }
        }
    }
    /**
     * Get full list of MTProto and API methods.
     */
    public function getAllMethods(): array
    {
        $methods = [];
        foreach ($this->getTL()->getMethods()->by_id as $method) {
            $methods[] = $method['method'];
        }
        return array_merge($methods, get_class_methods(InternalDoc::class));
    }
    /**
     * @internal
     *
     * @psalm-pure
     */
    #[\Override]
    public function getMethodAfterResponseDeserializationCallbacks(): array
    {
        return [];
    }
    /**
     * @internal
     *
     * @psalm-pure
     */
    #[\Override]
    public function getMethodBeforeResponseDeserializationCallbacks(): array
    {
        return [];
    }
    /**
     * @internal
     *
     * @psalm-external-mutation-free
     */
    public function populateSupportUser(array $support): void
    {
        $this->supportUser = $support['user']['id'];
    }
    /**
     * @internal
     *
     * @psalm-external-mutation-free
     */
    public function populateConfig(array $config): void
    {
        $this->config = $config;
    }
    /** @internal */
    public function handleAuthorization(array $authorization, Connection $connection): void
    {
        $this->authFuture = $f = async($this->processAuthorization(...), $authorization, $connection->getDatacenter());
        $f->finally(function () use ($f): void {
            if ($f === $this->authFuture) {
                $this->authFuture = null;
            }
        });
    }
    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function getConstructorAfterDeserializationCallbacks(): array
    {
        return [
            'help.support' => [$this->populateSupportUser(...)],
            'config' => [$this->populateConfig(...)],
            'auth.authorization' => [$this->handleAuthorization(...)],
        ];
    }
    /**
     * @internal
     *
     * @psalm-pure
     */
    #[\Override]
    public function getConstructorBeforeDeserializationCallbacks(): array
    {
        return [];
    }
    /**
     * @internal
     *
     * @psalm-pure
     */
    #[\Override]
    public function getConstructorBeforeSerializationCallbacks(): array
    {
        return [];
    }
    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function getTypeMismatchCallbacks(): array
    {
        return array_merge(
            array_fill_keys(
                [
                    'InputUser',
                    'InputChannel',
                ],
                $this->getInputConstructor(...),
            ),
            array_fill_keys(
                [
                    'InputMedia',
                    'InputDocument',
                    'InputPhoto',
                ],
                $this->getFileInfo(...),
            ),
            [
                'InputFileLocation' => $this->getDownloadInfo(...),
                'InputPeer' => $this->getInputPeer(...),
                'InputDialogPeer' => $this->getInputDialogPeer(...),
                'InputCheckPasswordSRP' => $this->getPasswordSRP(...),
            ],
        );
    }
    /** @internal */
    public function getInputDialogPeer(mixed $id): array
    {
        return ['_' => 'inputDialogPeer', 'peer' => $this->getInputPeer($id)];
    }
    /** @internal */
    public function getPassword(): array
    {
        return $this->methodCallAsyncRead('account.getPassword', ['specialMethodType' => SpecialMethodType::USER_RELATED]);
    }
    /** @internal */
    public function getPasswordSRP(string $password): array
    {
        return (new PasswordCalculator($this->getPassword()))->getCheckPassword($password);
    }
    /**
     * Get debug information for var_dump.
     *
     * @psalm-mutation-free
     */
    public function __debugInfo(): array
    {
        $vars = get_object_vars($this);
        unset($vars['peerDatabase'], $vars['referenceDatabase'], $vars['minDatabase'], $vars['TL']);
        return $vars;
    }
}
<?php

declare(strict_types=1);

/**
 * NothingInTheSocketException module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use Exception;

/** @internal */
final class StreamEof extends Exception
{
}
<?php

declare(strict_types=1);

/**
 * RPCErrorException module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use Amp\Http\Client\HttpClientBuilder;
use Amp\Http\Client\Request;
use Revolt\EventLoop;
use Throwable;
use Webmozart\Assert\Assert;

use const PHP_EOL;

use const PHP_SAPI;

/**
 * Indicates an error returned by Telegram's API.
 */
class RPCErrorException extends \Exception
{
    use TL\PrettyException;
    /** @internal */
    public static array $descriptions = ['RPC_MCGET_FAIL' => 'Telegram is having internal issues, please try again later.', 'RPC_CALL_FAIL' => 'Telegram is having internal issues, please try again later.', 'USER_PRIVACY_RESTRICTED' => "The user's privacy settings do not allow you to do this", 'CHANNEL_PRIVATE' => "You haven't joined this channel/supergroup", 'USER_IS_BOT' => "Bots can't send messages to other bots", 'BOT_METHOD_INVALID' => 'This method cannot be run by a bot', 'PHONE_CODE_EXPIRED' => 'The phone code you provided has expired, this may happen if it was sent to any chat on telegram (if the code is sent through a telegram chat (not the official account) to avoid it append or prepend to the code some chars)', 'USERNAME_INVALID' => 'The provided username is not valid', 'ACCESS_TOKEN_INVALID' => 'The provided token is not valid', 'ACTIVE_USER_REQUIRED' => 'The method is only available to already activated users', 'FIRSTNAME_INVALID' => 'The first name is invalid', 'LASTNAME_INVALID' => 'The last name is invalid', 'PHONE_NUMBER_INVALID' => 'The phone number is invalid', 'PHONE_CODE_HASH_EMPTY' => 'phone_code_hash is missing', 'PHONE_CODE_EMPTY' => 'phone_code is missing', 'API_ID_INVALID' => 'The api_id/api_hash combination is invalid', 'PHONE_NUMBER_OCCUPIED' => 'The phone number is already in use', 'PHONE_NUMBER_UNOCCUPIED' => 'The phone number is not yet being used', 'USERS_TOO_FEW' => 'Not enough users (to create a chat, for example)', 'USERS_TOO_MUCH' => 'The maximum number of users has been exceeded (to create a chat, for example)', 'TYPE_CONSTRUCTOR_INVALID' => 'The type constructor is invalid', 'FILE_PART_INVALID' => 'The file part number is invalid', 'FILE_PARTS_INVALID' => 'The number of file parts is invalid', 'MD5_CHECKSUM_INVALID' => 'The MD5 checksums do not match', 'PHOTO_INVALID_DIMENSIONS' => 'The photo dimensions are invalid', 'FIELD_NAME_INVALID' => 'The field with the name FIELD_NAME is invalid', 'FIELD_NAME_EMPTY' => 'The field with the name FIELD_NAME is missing', 'MSG_WAIT_FAILED' => 'A waiting call returned an error', 'USERNAME_NOT_OCCUPIED' => 'The provided username is not occupied', 'PHONE_NUMBER_BANNED' => 'The provided phone number is banned from telegram', 'AUTH_KEY_UNREGISTERED' => 'The authorization key has expired', 'INVITE_HASH_EXPIRED' => 'The invite link has expired', 'USER_DEACTIVATED' => 'The user was deactivated', 'USER_ALREADY_PARTICIPANT' => 'The user is already in the group', 'MESSAGE_ID_INVALID' => 'The provided message id is invalid', 'PEER_ID_INVALID' => 'The provided peer id is invalid', 'CHAT_ID_INVALID' => 'The provided chat id is invalid', 'MESSAGE_DELETE_FORBIDDEN' => "You can't delete one of the messages you tried to delete, most likely because it is a service message.", 'CHAT_ADMIN_REQUIRED' => 'You must be an admin in this chat to do this', -429 => 'Too many requests', 'PEER_FLOOD' => "You are spamreported, you can't do this"];
    /** @internal */
    public static array $errorMethodMap = [];

    /** @psalm-suppress MissingClassConstType */
    private const BAD = [
        'STARGIFT_RESELL_NOT_ALLOWED' => true,
        'CDN_SALTS_EMPTY' => true,
        'BOT_POLLS_DISABLED' => true,
        'PHOTO_THUMB_URL_INVALID' => true,
        'ACTIVE_USER_REQUIRED' => true,
        'PEER_FLOOD' => true,
        'USER_DEACTIVATED_BAN' => true,
        'INPUT_METHOD_INVALID' => true,
        'INPUT_FETCH_ERROR' => true,
        'SESSION_REVOKED' => true,
        'USER_DEACTIVATED' => true,
        'RPC_SEND_FAIL' => true,
        'RPC_CALL_FAIL' => true,
        'RPC_MCGET_FAIL' => true,
        'INTERDC_5_CALL_ERROR' => true,
        'INTERDC_4_CALL_ERROR' => true,
        'INTERDC_3_CALL_ERROR' => true,
        'INTERDC_2_CALL_ERROR' => true,
        'INTERDC_1_CALL_ERROR' => true,
        'INTERDC_5_CALL_RICH_ERROR' => true,
        'INTERDC_4_CALL_RICH_ERROR' => true,
        'INTERDC_3_CALL_RICH_ERROR' => true,
        'INTERDC_2_CALL_RICH_ERROR' => true,
        'INTERDC_1_CALL_RICH_ERROR' => true,
        'AUTH_KEY_DUPLICATED' => true,
        'CONNECTION_NOT_INITED' => true,
        'LOCATION_NOT_AVAILABLE' => true,
        'AUTH_KEY_INVALID' => true,
        'LANG_CODE_EMPTY' => true,
        'memory limit exit' => true,
        'memory limit(?)' => true,
        'INPUT_REQUEST_TOO_LONG' => true,
        'SESSION_PASSWORD_NEEDED' => true,
        'INPUT_FETCH_FAIL' => true,
        'CONNECTION_SYSTEM_EMPTY' => true,
        'FILE_WRITE_FAILED' => true,
        'STORAGE_CHOOSE_VOLUME_FAILED' => true,
        'xxx' => true,
        'AES_DECRYPT_FAILED' => true,
        'Timedout' => true,
        'SEND_REACTION_RESULT1_INVALID' => true,
        'TEMPNAM_FAILED' => true,
        'MSG_WAIT_TIMEOUT' => true,
        'MEMBER_CHAT_ADD_FAILED' => true,
        'CHAT_FROM_CALL_CHANGED' => true,
        'MTPROTO_CLUSTER_INVALID' => true,
        'CONNECTION_DEVICE_MODEL_EMPTY' => true,
        'AUTH_KEY_PERM_EMPTY' => true,
        'UNKNOWN_METHOD' => true,
        'ENCRYPTION_OCCUPY_FAILED' => true,
        'ENCRYPTION_OCCUPY_ADMIN_FAILED' => true,
        'CHAT_OCCUPY_USERNAME_FAILED' => true,
        'REG_ID_GENERATE_FAILED' => true,
        'CONNECTION_LANG_PACK_INVALID' => true,
        'MSGID_DECREASE_RETRY' => true,
        'API_CALL_ERROR' => true,
        'STORAGE_CHECK_FAILED' => true,
        'INPUT_LAYER_INVALID' => true,
        'NEED_MEMBER_INVALID' => true,
        'NEED_CHAT_INVALID' => true,
        'HISTORY_GET_FAILED' => true,
        'CHP_CALL_FAIL' => true,
        'IMAGE_ENGINE_DOWN' => true,
        'MSG_RANGE_UNSYNC' => true,
        'NEED_OWNER_EMPTY' => true,
        'PTS_CHANGE_EMPTY' => true,
        'CONNECTION_SYSTEM_LANG_CODE_EMPTY' => true,
        'WORKER_BUSY_TOO_LONG_RETRY' => true,
        'WP_ID_GENERATE_FAILED' => true,
        'ARR_CAS_FAILED' => true,
        'CHANNEL_ADD_INVALID' => true,
        'CHANNEL_ADMINS_INVALID' => true,
        'CHAT_OCCUPY_LOC_FAILED' => true,
        'GROUPED_ID_OCCUPY_FAILED' => true,
        'GROUPED_ID_OCCUPY_FAULED' => true,
        'LOG_WRAP_FAIL' => true,
        'MEMBER_FETCH_FAILED' => true,
        'MEMBER_OCCUPY_PRIMARY_LOC_FAILED' => true,
        'MEMBER_NO_LOCATION' => true,
        'MEMBER_OCCUPY_USERNAME_FAILED' => true,
        'MT_SEND_QUEUE_TOO_LONG' => true,
        'POSTPONED_TIMEOUT' => true,
        'RPC_CONNECT_FAILED' => true,
        'SHORTNAME_OCCUPY_FAILED' => true,
        'STORE_INVALID_OBJECT_TYPE' => true,
        'STORE_INVALID_SCALAR_TYPE' => true,
        'TMSG_ADD_FAILED' => true,
        'UNKNOWN_ERROR' => true,
        'UPLOAD_NO_VOLUME' => true,
        'USER_NOT_AVAILABLE' => true,
        'VOLUME_LOC_NOT_FOUND' => true,
        'FILE_WRITE_EMPTY' => true,
        'Internal_Server_Error' => true,
        'INVITE_HASH_UNSYNC' => true,
        'CHANNEL_ID_GENERATE_FAILED' => true,
        'Invalid msgs_state_req query' => true,
    ];

    /** @internal */
    public static function isBad(string $error, int $code, string $method): bool
    {
        return isset(self::BAD[$error])
                || str_contains($error, 'Received bad_msg_notification')
                || str_contains($error, 'FLOOD_WAIT_')
                || str_contains($error, '_MIGRATE_')
                || str_contains($error, 'INPUT_METHOD_INVALID')
                || str_contains($error, 'INPUT_CONSTRUCTOR_INVALID')
                || str_contains($error, 'INPUT_FETCH_ERROR_')
                || str_contains($error, 'https://telegram.org/dl')
                || str_starts_with($error, 'Received bad_msg_notification')
                || str_starts_with($error, 'RECAPTCHA_CHECK_')
                || str_starts_with($error, 'No workers running')
                || str_starts_with($error, 'FLOOD_TEST_PHONE_WAIT_')
                || str_starts_with($error, 'All workers are busy. Active_queries ')
                || preg_match('/FILE_PART_\d*_MISSING/', $error)
                || !preg_match('/^[a-zA-Z0-9\._]+$/', $method)
                || ($error === 'Timeout' && !\in_array(strtolower($method), ['messages.getbotcallbackanswer', 'messages.getinlinebotresults'], true))
                || ($error === 'BOT_MISSING' && \in_array($method, ['stickers.changeStickerPosition', 'stickers.createStickerSet', 'messages.uploadMedia'], true))
                || is_numeric($method);
    }

    private static string $auth;
    /**
     * The errors already reported by this process, so that an error the database has no description for is not
     * reported again every time it is received.
     *
     * @var array<string, true>
     */
    private static array $reported = [];
    /**
     * Get the description of an error, reporting it in the background if it is not known.
     *
     * The report never delays the request that got the error: it waits for an HTTP request, up to its 10 second
     * timeout, and its result is only cached for the next errors.
     *
     * @internal
     */
    private static function report(string $error, int $code, string $method): string
    {
        if (!$method || !$code || !$error) {
            return $error;
        }
        $error = preg_replace('/\\d+$/', 'X', $error);
        if ((!isset(self::$errorMethodMap[$code][$method][$error]) || !isset(self::$descriptions[$error]))
            && !self::isBad($error, $code, $method)
            && !isset(self::$reported["$code $method $error"])
        ) {
            self::$reported["$code $method $error"] = true;
            EventLoop::queue(self::sendReport(...), $error, $code, $method);
        }
        return self::$descriptions[$error] ?? $error;
    }
    private static function sendReport(string $error, int $code, string $method): void
    {
        try {
            if (!isset(self::$auth)) {
                $auth = '';
                try {
                    $auth = getenv('TELERPC_AUTH_TOKEN') ?: '';
                } catch (Throwable) {
                }
                Assert::true(preg_match('/^[a-zA-Z0-9_-]*$/', $auth) === 1, 'TELERPC_AUTH_TOKEN can only contain a-z, A-Z, 0-9, _ and -');
                $auth = $auth === '' ? '' : 'auth='.$auth.'&';
                self::$auth = $auth;
            }
            $auth = self::$auth;
            $res = json_decode(
                (
                    HttpClientBuilder::buildDefault()
                    ->request(new Request('https://report-rpc-error.madelineproto.xyz/?'.$auth.'method='.$method.'&code='.$code.'&error='.$error))
                )->getBody()->buffer(),
                true,
            );
            if (isset($res['ok']) && $res['ok'] && isset($res['result']) && \is_string($res['result'])) {
                self::$descriptions[$error] = $res['result'];
                self::$errorMethodMap[$code][$method][$error] = $error;
            }
        } catch (Throwable $e) {
            Logger::log("Failed to report RPC error: $error (code $code) in method $method: ".$e->getMessage());
        }
    }
    /**
     * Get string representation of exception.
     */
    public function __toString(): string
    {
        Magic::start(light: true);
        $result = \sprintf(Lang::$current_lang['rpc_tg_error'], $this->description." ({$this->code})", $this->rpc, $this->file, $this->line.PHP_EOL, Magic::$revision.PHP_EOL.PHP_EOL).PHP_EOL.$this->getTLTrace().PHP_EOL;
        if (PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg') {
            $result = str_replace(PHP_EOL, '<br>'.PHP_EOL, $result);
        }
        return $result;
    }
    /**
     * Get localized error name.
     */
    public function getLocalization(): string
    {
        return $this->description;
    }

    /**
     * @internal
     */
    public static function make(
        string $rpc,
        int $code,
        string $caller,
        ?\Exception $previous = null
    ): self {
        $msg = self::report($rpc, $code, $caller);
        // Start match
        return match ($rpc) {
            'ADDRESS_INVALID' => new self($rpc, 'The specified geopoint address is invalid.', $code, $caller, $previous),
            'FROZEN_METHOD_INVALID' => new self($rpc, 'The current account is [frozen](https://core.telegram.org/api/auth#frozen-accounts), and thus cannot execute the specified action.', $code, $caller, $previous),
            'ABOUT_TOO_LONG' => new self($rpc, 'About string too long.', $code, $caller, $previous),
            'ACCESS_TOKEN_EXPIRED' => new self($rpc, 'Access token expired.', $code, $caller, $previous),
            'ACCESS_TOKEN_INVALID' => new self($rpc, 'Access token invalid.', $code, $caller, $previous),
            'AD_EXPIRED' => new self($rpc, 'The ad has expired (too old or not found).', $code, $caller, $previous),
            'ADMIN_ID_INVALID' => new self($rpc, 'The specified admin ID is invalid.', $code, $caller, $previous),
            'ADMIN_RANK_EMOJI_NOT_ALLOWED' => new self($rpc, 'An admin rank cannot contain emojis.', $code, $caller, $previous),
            'ADMIN_RANK_INVALID' => new self($rpc, 'The specified admin rank is invalid.', $code, $caller, $previous),
            'ADMIN_RIGHTS_EMPTY' => new self($rpc, 'The chatAdminRights constructor passed in keyboardButtonRequestPeer.peer_type.user_admin_rights has no rights set (i.e. flags is 0).', $code, $caller, $previous),
            'ADMINS_TOO_MUCH' => new self($rpc, 'There are too many admins.', $code, $caller, $previous),
            'AI_COMPOSE_TASK_MISSING' => new self($rpc, 'No AI task was specified. The caller must provide at least one of: proofread, translate (with a target language), tone, or emojify.', $code, $caller, $previous),
            'AICOMPOSE_FLOOD_PREMIUM' => new self($rpc, 'You\'ve reached the daily limit of AI text transformations, upgrade to [Telegram Premium](https://core.telegram.org/api/premium) to get **50x** times more AI text transformations per day!', $code, $caller, $previous),
            'AICOMPOSE_TONE_INVALID' => new self($rpc, 'The specified tone is invalid.', $code, $caller, $previous),
            'AICOMPOSE_TONE_TITLE_INVALID' => new self($rpc, 'The specified tone title is invalid.', $code, $caller, $previous),
            'ALBUM_PHOTOS_TOO_MANY' => new self($rpc, 'You have uploaded too many profile photos, delete some before retrying.', $code, $caller, $previous),
            'API_ID_INVALID' => new self($rpc, 'API ID invalid.', $code, $caller, $previous),
            'API_ID_PUBLISHED_FLOOD' => new self($rpc, 'This API id was published somewhere, you can\'t use it now.', $code, $caller, $previous),
            'ARTICLE_TITLE_EMPTY' => new self($rpc, 'The title of the article is empty.', $code, $caller, $previous),
            'AUDIO_CONTENT_URL_EMPTY' => new self($rpc, 'The remote URL specified in the content field is empty.', $code, $caller, $previous),
            'AUDIO_TITLE_EMPTY' => new self($rpc, 'An empty audio title was provided.', $code, $caller, $previous),
            'AUTH_BYTES_INVALID' => new self($rpc, 'The provided authorization is invalid.', $code, $caller, $previous),
            'AUTH_TOKEN_ALREADY_ACCEPTED' => new self($rpc, 'The specified auth token was already accepted.', $code, $caller, $previous),
            'AUTH_TOKEN_EXCEPTION' => new self($rpc, 'An error occurred while importing the auth token.', $code, $caller, $previous),
            'AUTH_TOKEN_EXPIRED' => new self($rpc, 'The authorization token has expired.', $code, $caller, $previous),
            'AUTH_TOKEN_INVALID' => new self($rpc, 'The specified auth token is invalid.', $code, $caller, $previous),
            'AUTH_TOKEN_INVALIDX' => new self($rpc, 'The specified auth token is invalid.', $code, $caller, $previous),
            'AUTOARCHIVE_NOT_AVAILABLE' => new self($rpc, 'The autoarchive setting is not available at this time: please check the value of the [autoarchive_setting_available field in client config &raquo;](https://core.telegram.org/api/config#client-configuration) before calling this method.', $code, $caller, $previous),
            'BALANCE_TOO_LOW' => new \danog\MadelineProto\RPCError\BalanceTooLowError($code, $caller, $previous),
            'BANK_CARD_NUMBER_INVALID' => new self($rpc, 'The specified card number is invalid.', $code, $caller, $previous),
            'BANNED_RIGHTS_INVALID' => new self($rpc, 'You provided some invalid flags in the banned rights.', $code, $caller, $previous),
            'BIRTHDAY_ALREADY' => new self($rpc, 'The target user already has a birthday set.', $code, $caller, $previous),
            'BIRTHDAY_INVALID' => new self($rpc, 'An invalid age was specified, must be between 0 and 150 years.', $code, $caller, $previous),
            'BOOST_NOT_MODIFIED' => new self($rpc, 'You\'re already [boosting](https://core.telegram.org/api/boost) the specified channel.', $code, $caller, $previous),
            'BOOST_PEER_INVALID' => new self($rpc, 'The specified `boost_peer` is invalid.', $code, $caller, $previous),
            'BOOSTS_EMPTY' => new self($rpc, 'No boost slots were specified.', $code, $caller, $previous),
            'BOOSTS_REQUIRED' => new self($rpc, 'The specified channel must first be [boosted by its users](https://core.telegram.org/api/boost) in order to perform this action.', $code, $caller, $previous),
            'BOT_ALREADY_DISABLED' => new self($rpc, 'The connected business bot was already disabled for the specified peer.', $code, $caller, $previous),
            'BOT_APP_BOT_INVALID' => new self($rpc, 'The bot_id passed in the inputBotAppShortName constructor is invalid.', $code, $caller, $previous),
            'BOT_APP_INVALID' => new self($rpc, 'The specified bot app is invalid.', $code, $caller, $previous),
            'BOT_APP_SHORTNAME_INVALID' => new self($rpc, 'The specified bot app short name is invalid.', $code, $caller, $previous),
            'BOT_BUSINESS_MISSING' => new self($rpc, 'The specified bot is not a business bot (the [user](https://core.telegram.org/constructor/user).`bot_business` flag is not set).', $code, $caller, $previous),
            'BOT_CHANNELS_NA' => new self($rpc, 'Bots can\'t edit admin privileges.', $code, $caller, $previous),
            'BOT_COMMAND_DESCRIPTION_INVALID' => new self($rpc, 'The specified command description is invalid.', $code, $caller, $previous),
            'BOT_COMMAND_INVALID' => new self($rpc, 'The specified command is invalid.', $code, $caller, $previous),
            'BOT_CREATE_LIMIT_EXCEEDED' => new self($rpc, 'The current user already owns the maximum allowed number of owned bots, as specified by [`bots_create_limit_default` &raquo;](https://core.telegram.org/api/config#bots-create-limit-default) and [`bots_create_limit_premium` &raquo;](https://core.telegram.org/api/config#bots-create-limit-premium); if the current user doesn\'t have Telegram Premium, upgrading to Premium will allow them to create more bots.', $code, $caller, $previous),
            'BOT_DOMAIN_INVALID' => new self($rpc, 'Bot domain invalid.', $code, $caller, $previous),
            'BOT_FALLBACK_UNSUPPORTED' => new self($rpc, 'The fallback flag can\'t be set for bots.', $code, $caller, $previous),
            'BOT_GAMES_DISABLED' => new \danog\MadelineProto\RPCError\BotGamesDisabledError($code, $caller, $previous),
            'BOT_GROUPS_BLOCKED' => new self($rpc, 'This bot can\'t be added to groups.', $code, $caller, $previous),
            'BOT_ID_INVALID' => new self($rpc, 'The specified bot ID is invalid.', $code, $caller, $previous),
            'BOT_INLINE_DISABLED' => new self($rpc, 'This bot can\'t be used in inline mode.', $code, $caller, $previous),
            'BOT_INVALID' => new self($rpc, 'This is not a valid bot.', $code, $caller, $previous),
            'BOT_INVOICE_INVALID' => new self($rpc, 'The specified invoice is invalid.', $code, $caller, $previous),
            'BOT_NOT_CONNECTED_YET' => new self($rpc, 'No [business bot](https://core.telegram.org/api/business#connected-bots) is connected to the currently logged in user.', $code, $caller, $previous),
            'BOT_ONESIDE_NOT_AVAIL' => new self($rpc, 'Bots can\'t pin messages in PM just for themselves.', $code, $caller, $previous),
            'BOT_PAYMENTS_DISABLED' => new \danog\MadelineProto\RPCError\BotPaymentsDisabledError($code, $caller, $previous),
            'BOT_RESPONSE_TIMEOUT' => new self($rpc, 'A timeout occurred while fetching data from the bot.', $code, $caller, $previous),
            'BOT_SCORE_NOT_MODIFIED' => new self($rpc, 'The score wasn\'t modified.', $code, $caller, $previous),
            'BOT_WEBVIEW_DISABLED' => new self($rpc, 'A webview cannot be opened in the specified conditions: emitted for example if `from_bot_menu` or `url` are set and `peer` is not the chat with the bot.', $code, $caller, $previous),
            'BOTS_TOO_MUCH' => new self($rpc, 'There are too many bots in this chat/channel.', $code, $caller, $previous),
            'BROADCAST_ID_INVALID' => new self($rpc, 'Broadcast ID invalid.', $code, $caller, $previous),
            'BROADCAST_PUBLIC_VOTERS_FORBIDDEN' => new \danog\MadelineProto\RPCError\BroadcastPublicVotersForbiddenError($code, $caller, $previous),
            'BROADCAST_REQUIRED' => new self($rpc, 'This method can only be called on a channel, please use stats.getMegagroupStats for supergroups.', $code, $caller, $previous),
            'BUSINESS_CONNECTION_INVALID' => new self($rpc, 'The `connection_id` passed to the wrapping [invokeWithBusinessConnection](https://core.telegram.org/api/business) call is invalid.', $code, $caller, $previous),
            'BUSINESS_CONNECTION_NOT_ALLOWED' => new \danog\MadelineProto\RPCError\BusinessConnectionNotAllowedError($code, $caller, $previous),
            'BUSINESS_PEER_INVALID' => new self($rpc, 'Messages can\'t be set to the specified peer through the current [business connection](https://core.telegram.org/api/business#connected-bots).', $code, $caller, $previous),
            'BUSINESS_PEER_USAGE_MISSING' => new \danog\MadelineProto\RPCError\BusinessPeerUsageMissingError($code, $caller, $previous),
            'BUSINESS_RECIPIENTS_EMPTY' => new self($rpc, 'You didn\'t set any flag in inputBusinessBotRecipients, thus the bot cannot work with *any* peer.', $code, $caller, $previous),
            'BUSINESS_WORK_HOURS_EMPTY' => new self($rpc, 'No work hours were specified.', $code, $caller, $previous),
            'BUSINESS_WORK_HOURS_PERIOD_INVALID' => new self($rpc, 'The specified work hours are invalid, see [here &raquo;](https://core.telegram.org/api/business#opening-hours) for the exact requirements.', $code, $caller, $previous),
            'BUTTON_COPY_TEXT_INVALID' => new self($rpc, 'The specified [keyboardButtonCopy](https://core.telegram.org/constructor/keyboardButtonCopy).`copy_text` is invalid.', $code, $caller, $previous),
            'BUTTON_DATA_INVALID' => new self($rpc, 'The data of one or more of the buttons you provided is invalid.', $code, $caller, $previous),
            'BUTTON_ID_INVALID' => new self($rpc, 'The specified button ID is invalid.', $code, $caller, $previous),
            'BUTTON_INVALID' => new self($rpc, 'The specified button is invalid.', $code, $caller, $previous),
            'BUTTON_POS_INVALID' => new self($rpc, 'The position of one of the keyboard buttons is invalid (i.e. a Game or Pay button not in the first position, and so on...).', $code, $caller, $previous),
            'BUTTON_TEXT_INVALID' => new self($rpc, 'The specified button text is invalid.', $code, $caller, $previous),
            'BUTTON_TYPE_INVALID' => new self($rpc, 'The type of one or more of the buttons you provided is invalid.', $code, $caller, $previous),
            'BUTTON_URL_INVALID' => new self($rpc, 'Button URL invalid.', $code, $caller, $previous),
            'BUTTON_USER_INVALID' => new self($rpc, 'The `user_id` passed to inputKeyboardButtonUserProfile is invalid!', $code, $caller, $previous),
            'BUTTON_USER_PRIVACY_RESTRICTED' => new \danog\MadelineProto\RPCError\ButtonUserPrivacyRestrictedError($code, $caller, $previous),
            'CALL_ALREADY_ACCEPTED' => new \danog\MadelineProto\RPCError\CallAlreadyAcceptedError($code, $caller, $previous),
            'CALL_ALREADY_DECLINED' => new \danog\MadelineProto\RPCError\CallAlreadyDeclinedError($code, $caller, $previous),
            'CALL_NOT_ACTIVE' => new self($rpc, 'The specified call is not active.', $code, $caller, $previous),
            'CALL_OCCUPY_FAILED' => new self($rpc, 'The call failed because the user is already making another call.', $code, $caller, $previous),
            'CALL_PEER_INVALID' => new self($rpc, 'The provided call peer object is invalid.', $code, $caller, $previous),
            'CALL_PROTOCOL_FLAGS_INVALID' => new self($rpc, 'Call protocol flags invalid.', $code, $caller, $previous),
            'CALL_PROTOCOL_LAYER_INVALID' => new self($rpc, 'The specified protocol layer version range is invalid.', $code, $caller, $previous),
            'CDN_METHOD_INVALID' => new self($rpc, 'You can\'t call this method in a CDN DC.', $code, $caller, $previous),
            'CHANNEL_FORUM_MISSING' => new self($rpc, 'This supergroup is not a forum.', $code, $caller, $previous),
            'CHANNEL_ID_INVALID' => new self($rpc, 'The specified supergroup ID is invalid.', $code, $caller, $previous),
            'CHANNEL_INVALID' => new \danog\MadelineProto\RPCError\ChannelInvalidError($code, $caller, $previous),
            'CHANNEL_MONOFORUM_UNSUPPORTED' => new \danog\MadelineProto\RPCError\ChannelMonoforumUnsupportedError($code, $caller, $previous),
            'CHANNEL_PARICIPANT_MISSING' => new self($rpc, 'The current user is not in the channel.', $code, $caller, $previous),
            'CHANNEL_PRIVATE' => new \danog\MadelineProto\RPCError\ChannelPrivateError($code, $caller, $previous),
            'CHANNEL_TOO_BIG' => new self($rpc, 'This channel has too many participants (>1000) to be deleted.', $code, $caller, $previous),
            'CHANNEL_TOO_LARGE' => new self($rpc, 'Channel is too large to be deleted; this error is issued when trying to delete channels with more than 1000 members (subject to change).', $code, $caller, $previous),
            'CHANNELS_ADMIN_LOCATED_TOO_MUCH' => new self($rpc, 'The user has reached the limit of public geogroups.', $code, $caller, $previous),
            'CHANNELS_ADMIN_PUBLIC_TOO_MUCH' => new self($rpc, 'You\'re admin of too many public channels, make some channels private to change the username of this channel.', $code, $caller, $previous),
            'CHANNELS_TOO_MUCH' => new self($rpc, 'You have joined too many channels/supergroups.', $code, $caller, $previous),
            'CHARGE_ALREADY_REFUNDED' => new self($rpc, 'The transaction was already refunded.', $code, $caller, $previous),
            'CHARGE_ID_EMPTY' => new self($rpc, 'The specified charge_id is empty.', $code, $caller, $previous),
            'CHARGE_ID_INVALID' => new self($rpc, 'The specified charge_id is invalid.', $code, $caller, $previous),
            'CHAT_ABOUT_NOT_MODIFIED' => new self($rpc, 'About text has not changed.', $code, $caller, $previous),
            'CHAT_ABOUT_TOO_LONG' => new self($rpc, 'Chat about too long.', $code, $caller, $previous),
            'CHAT_ADMIN_REQUIRED' => new \danog\MadelineProto\RPCError\ChatAdminRequiredError($code, $caller, $previous),
            'CHAT_DISCUSSION_UNALLOWED' => new self($rpc, 'You can\'t enable forum topics in a discussion group linked to a channel.', $code, $caller, $previous),
            'CHAT_FORWARDS_RESTRICTED' => new \danog\MadelineProto\RPCError\ChatForwardsRestrictedError($code, $caller, $previous),
            'CHAT_ID_EMPTY' => new self($rpc, 'The provided chat ID is empty.', $code, $caller, $previous),
            'CHAT_ID_INVALID' => new self($rpc, 'The provided chat id is invalid.', $code, $caller, $previous),
            'CHAT_INVALID' => new self($rpc, 'Invalid chat.', $code, $caller, $previous),
            'CHAT_INVITE_PERMANENT' => new self($rpc, 'You can\'t set an expiration date on permanent invite links.', $code, $caller, $previous),
            'CHAT_LINK_EXISTS' => new self($rpc, 'The chat is public, you can\'t hide the history to new users.', $code, $caller, $previous),
            'CHAT_MEMBER_ADD_FAILED' => new self($rpc, 'Could not add participants.', $code, $caller, $previous),
            'CHAT_NOT_MODIFIED' => new self($rpc, 'No changes were made to chat information because the new information you passed is identical to the current information.', $code, $caller, $previous),
            'CHAT_PUBLIC_REQUIRED' => new self($rpc, 'You can only enable join requests in public groups.', $code, $caller, $previous),
            'CHAT_RESTRICTED' => new \danog\MadelineProto\RPCError\ChatRestrictedError($code, $caller, $previous),
            'CHAT_REVOKE_DATE_UNSUPPORTED' => new self($rpc, '`min_date` and `max_date` are not available for using with non-user peers.', $code, $caller, $previous),
            'CHAT_SEND_INLINE_FORBIDDEN' => new self($rpc, 'You can\'t send inline messages in this group.', $code, $caller, $previous),
            'CHAT_TITLE_EMPTY' => new self($rpc, 'No chat title provided.', $code, $caller, $previous),
            'CHAT_TOO_BIG' => new self($rpc, 'This method is not available for groups with more than `chat_read_mark_size_threshold` members, [see client configuration &raquo;](https://core.telegram.org/api/config#client-configuration).', $code, $caller, $previous),
            'CHAT_WRITE_FORBIDDEN' => new \danog\MadelineProto\RPCError\ChatWriteForbiddenError($code, $caller, $previous),
            'CHATLINK_SLUG_EMPTY' => new self($rpc, 'The specified slug is empty.', $code, $caller, $previous),
            'CHATLINK_SLUG_EXPIRED' => new self($rpc, 'The specified [business chat link](https://core.telegram.org/api/business#business-chat-links) has expired.', $code, $caller, $previous),
            'CHATLINKS_TOO_MUCH' => new self($rpc, 'Too many [business chat links](https://core.telegram.org/api/business#business-chat-links) were created, please delete some older links.', $code, $caller, $previous),
            'CHATLIST_EXCLUDE_INVALID' => new self($rpc, 'The specified `exclude_peers` are invalid.', $code, $caller, $previous),
            'CHATLISTS_TOO_MUCH' => new self($rpc, 'You have created too many folder links, hitting the `chatlist_invites_limit_default`/`chatlist_invites_limit_premium` [limits &raquo;](https://core.telegram.org/api/config#chatlist-invites-limit-default).', $code, $caller, $previous),
            'CODE_EMPTY' => new self($rpc, 'The provided code is empty.', $code, $caller, $previous),
            'CODE_HASH_INVALID' => new self($rpc, 'Code hash invalid.', $code, $caller, $previous),
            'CODE_INVALID' => new self($rpc, 'Code invalid.', $code, $caller, $previous),
            'COLLECTIBLE_INVALID' => new self($rpc, 'The specified collectible is invalid.', $code, $caller, $previous),
            'COLLECTIBLE_NOT_FOUND' => new self($rpc, 'The specified collectible could not be found.', $code, $caller, $previous),
            'COLLECTION_ID_INVALID' => new self($rpc, 'The specified collection ID is invalid.', $code, $caller, $previous),
            'COLOR_INVALID' => new self($rpc, 'The specified color palette ID was invalid.', $code, $caller, $previous),
            'CONNECTION_API_ID_INVALID' => new self($rpc, 'The provided API id is invalid.', $code, $caller, $previous),
            'CONNECTION_APP_VERSION_EMPTY' => new self($rpc, 'App version is empty.', $code, $caller, $previous),
            'CONNECTION_ID_INVALID' => new self($rpc, 'The specified connection ID is invalid.', $code, $caller, $previous),
            'CONNECTION_LAYER_INVALID' => new self($rpc, 'Layer invalid.', $code, $caller, $previous),
            'CONTACT_ADD_MISSING' => new self($rpc, 'Contact to add is missing.', $code, $caller, $previous),
            'CONTACT_ID_INVALID' => new self($rpc, 'The provided contact ID is invalid.', $code, $caller, $previous),
            'CONTACT_MISSING' => new self($rpc, 'The specified user is not a contact.', $code, $caller, $previous),
            'CONTACT_NAME_EMPTY' => new self($rpc, 'Contact name empty.', $code, $caller, $previous),
            'CONTACT_REQ_MISSING' => new self($rpc, 'Missing contact request.', $code, $caller, $previous),
            'CREATE_CALL_FAILED' => new self($rpc, 'An error occurred while creating the call.', $code, $caller, $previous),
            'CREDENTIAL_INVALID' => new self($rpc, 'The specified credential is invalid.', $code, $caller, $previous),
            'CURRENCY_TOTAL_AMOUNT_INVALID' => new self($rpc, 'The total amount of all prices is invalid.', $code, $caller, $previous),
            'CUSTOM_REACTIONS_TOO_MANY' => new self($rpc, 'Too many custom reactions were specified.', $code, $caller, $previous),
            'DATA_HASH_SIZE_INVALID' => new self($rpc, 'The size of the specified secureValueErrorData.data_hash is invalid.', $code, $caller, $previous),
            'DATA_INVALID' => new self($rpc, 'Encrypted data invalid.', $code, $caller, $previous),
            'DATA_JSON_INVALID' => new self($rpc, 'The provided JSON data is invalid.', $code, $caller, $previous),
            'DATA_TOO_LONG' => new self($rpc, 'Data too long.', $code, $caller, $previous),
            'DATE_EMPTY' => new self($rpc, 'Date empty.', $code, $caller, $previous),
            'DC_ID_INVALID' => new \danog\MadelineProto\RPCError\DcIdInvalidError($code, $caller, $previous),
            'DH_G_A_INVALID' => new self($rpc, 'g_a invalid.', $code, $caller, $previous),
            'DOCUMENT_INVALID' => new self($rpc, 'The specified document is invalid.', $code, $caller, $previous),
            'EFFECT_CHAT_INVALID' => new self($rpc, 'Message [effects](https://core.telegram.org/api/effects) can only be used in private 1-on-1 chats, but the caller tried to send a message with an effect to a group or channel.', $code, $caller, $previous),
            'EFFECT_ID_INVALID' => new self($rpc, 'The specified effect ID is invalid.', $code, $caller, $previous),
            'EMAIL_HASH_EXPIRED' => new self($rpc, 'Email hash expired.', $code, $caller, $previous),
            'EMAIL_INSTALL_MISSING' => new self($rpc, 'Attempting to send a code to the recovery email, but no email is configured.', $code, $caller, $previous),
            'EMAIL_INVALID' => new self($rpc, 'The specified email is invalid.', $code, $caller, $previous),
            'EMAIL_NOT_ALLOWED' => new self($rpc, 'The specified email cannot be used to complete the operation.', $code, $caller, $previous),
            'EMAIL_NOT_SETUP' => new self($rpc, 'In order to change the login email with emailVerifyPurposeLoginChange, an existing login email must already be set using emailVerifyPurposeLoginSetup.', $code, $caller, $previous),
            'EMAIL_UNCONFIRMED' => new self($rpc, 'Email unconfirmed.', $code, $caller, $previous),
            'EMAIL_VERIFY_EXPIRED' => new self($rpc, 'The verification email has expired.', $code, $caller, $previous),
            'EMOJI_INVALID' => new self($rpc, 'The specified theme emoji is valid.', $code, $caller, $previous),
            'EMOJI_MARKUP_INVALID' => new self($rpc, 'The specified `video_emoji_markup` was invalid.', $code, $caller, $previous),
            'EMOJI_NOT_MODIFIED' => new self($rpc, 'The theme wasn\'t changed.', $code, $caller, $previous),
            'EMOTICON_EMPTY' => new self($rpc, 'The emoji is empty.', $code, $caller, $previous),
            'EMOTICON_INVALID' => new self($rpc, 'The specified emoji is invalid.', $code, $caller, $previous),
            'EMOTICON_STICKERPACK_MISSING' => new self($rpc, 'inputStickerSetDice.emoji cannot be empty.', $code, $caller, $previous),
            'ENCRYPTED_MESSAGE_INVALID' => new self($rpc, 'Encrypted message invalid.', $code, $caller, $previous),
            'ENCRYPTION_ALREADY_ACCEPTED' => new \danog\MadelineProto\RPCError\EncryptionAlreadyAcceptedError($code, $caller, $previous),
            'ENCRYPTION_ALREADY_DECLINED' => new \danog\MadelineProto\RPCError\EncryptionAlreadyDeclinedError($code, $caller, $previous),
            'ENCRYPTION_DECLINED' => new \danog\MadelineProto\RPCError\EncryptionDeclinedError($code, $caller, $previous),
            'ENCRYPTION_ID_INVALID' => new self($rpc, 'The provided secret chat ID is invalid.', $code, $caller, $previous),
            'ENTITIES_TOO_LONG' => new self($rpc, 'You provided too many styled message entities.', $code, $caller, $previous),
            'ENTITY_BOUNDS_INVALID' => new self($rpc, 'A specified [entity offset or length](https://core.telegram.org/api/entities#entity-length) is invalid, see [here &raquo;](https://core.telegram.org/api/entities#entity-length) for info on how to properly compute the entity offset/length.', $code, $caller, $previous),
            'ENTITY_DATE_FORMAT_INVALID' => new self($rpc, 'One of the passed messageEntityFormattedDate objects has an invalid format (i.e. an invalid combination of the format flags).', $code, $caller, $previous),
            'ENTITY_DATE_INVALID' => new self($rpc, 'One of the passed messageEntityFormattedDate objects has an invalid date: the allowed value ranges from `0` to the current date plus 1098 days (`time()+1098*86400`).', $code, $caller, $previous),
            'ENTITY_DATE_TOO_LONG' => new self($rpc, 'The maximum text span that can be covered by a date entity is 31 UTF-16 code units if any of the date formatting flags is set, or 127 UTF-16 code units without.  .', $code, $caller, $previous),
            'ENTITY_MENTION_USER_INVALID' => new self($rpc, 'You mentioned an invalid user.', $code, $caller, $previous),
            'ERROR_TEXT_EMPTY' => new self($rpc, 'The provided error message is empty.', $code, $caller, $previous),
            'EXPIRE_DATE_INVALID' => new self($rpc, 'The specified expiration date is invalid.', $code, $caller, $previous),
            'EXPIRES_AT_INVALID' => new self($rpc, 'The specified `expires_at` timestamp is invalid.', $code, $caller, $previous),
            'EXPORT_CARD_INVALID' => new self($rpc, 'Provided card is invalid.', $code, $caller, $previous),
            'EXTENDED_MEDIA_AMOUNT_INVALID' => new self($rpc, 'The specified `stars_amount` of the passed [inputMediaPaidMedia](https://core.telegram.org/constructor/inputMediaPaidMedia) is invalid.', $code, $caller, $previous),
            'EXTENDED_MEDIA_EMPTY' => new self($rpc, 'The specified extended media is empty.', $code, $caller, $previous),
            'EXTENDED_MEDIA_INVALID' => new self($rpc, 'The specified paid media is invalid.', $code, $caller, $previous),
            'EXTENDED_MEDIA_PEER_INVALID' => new self($rpc, 'Paid media is not allowed for the target peer.', $code, $caller, $previous),
            'EXTERNAL_URL_INVALID' => new self($rpc, 'External URL invalid.', $code, $caller, $previous),
            'FILE_CONTENT_TYPE_INVALID' => new self($rpc, 'File content-type is invalid.', $code, $caller, $previous),
            'FILE_EMTPY' => new self($rpc, 'An empty file was provided.', $code, $caller, $previous),
            'FILE_ID_INVALID' => new self($rpc, 'The provided file id is invalid.', $code, $caller, $previous),
            'FILE_PART_EMPTY' => new self($rpc, 'The provided file part is empty.', $code, $caller, $previous),
            'FILE_PART_INVALID' => new self($rpc, 'The file part number is invalid.', $code, $caller, $previous),
            'FILE_PART_LENGTH_INVALID' => new self($rpc, 'The length of a file part is invalid.', $code, $caller, $previous),
            'FILE_PART_SIZE_CHANGED' => new self($rpc, 'Provided file part size has changed.', $code, $caller, $previous),
            'FILE_PART_SIZE_INVALID' => new self($rpc, 'The provided file part size is invalid.', $code, $caller, $previous),
            'FILE_PART_TOO_BIG' => new self($rpc, 'The uploaded file part is too big.', $code, $caller, $previous),
            'FILE_PART_TOO_SMALL' => new self($rpc, 'The size of the uploaded file part is too small, please see the documentation for the allowed sizes.', $code, $caller, $previous),
            'FILE_PARTS_INVALID' => new self($rpc, 'The number of file parts is invalid.', $code, $caller, $previous),
            'FILE_REFERENCE_EMPTY' => new self($rpc, 'An empty [file reference](https://core.telegram.org/api/file-references) was specified.', $code, $caller, $previous),
            'FILE_REFERENCE_EXPIRED' => new \danog\MadelineProto\RPCError\FileReferenceExpiredError($code, $caller, $previous),
            'FILE_REFERENCE_INVALID' => new self($rpc, 'The specified [file reference](https://core.telegram.org/api/file-references) is invalid.', $code, $caller, $previous),
            'FILE_TITLE_EMPTY' => new self($rpc, 'An empty file title was specified.', $code, $caller, $previous),
            'FILE_TOKEN_INVALID' => new \danog\MadelineProto\RPCError\FileTokenInvalidError($code, $caller, $previous),
            'FILTER_ID_INVALID' => new self($rpc, 'The specified filter ID is invalid.', $code, $caller, $previous),
            'FILTER_INCLUDE_EMPTY' => new self($rpc, 'The include_peers vector of the filter is empty.', $code, $caller, $previous),
            'FILTER_NOT_SUPPORTED' => new self($rpc, 'The specified filter cannot be used in this context.', $code, $caller, $previous),
            'FILTER_TITLE_EMPTY' => new self($rpc, 'The title field of the filter is empty.', $code, $caller, $previous),
            'FIRSTNAME_INVALID' => new self($rpc, 'The first name is invalid.', $code, $caller, $previous),
            'FOLDER_ID_EMPTY' => new self($rpc, 'An empty folder ID was specified.', $code, $caller, $previous),
            'FOLDER_ID_INVALID' => new self($rpc, 'Invalid folder ID.', $code, $caller, $previous),
            'FORM_EXPIRED' => new self($rpc, 'The form was generated more than 10 minutes ago and has expired, please re-generate it using [payments.getPaymentForm](https://core.telegram.org/method/payments.getPaymentForm) and pass the new `form_id`.', $code, $caller, $previous),
            'FORM_ID_EMPTY' => new self($rpc, 'The specified form ID is empty.', $code, $caller, $previous),
            'FORM_SUBMIT_DUPLICATE' => new self($rpc, 'The same payment form was already submitted.  .', $code, $caller, $previous),
            'FORM_UNSUPPORTED' => new self($rpc, 'Please update your client.', $code, $caller, $previous),
            'FORUM_ENABLED' => new self($rpc, 'You can\'t execute the specified action because the group is a [forum](https://core.telegram.org/api/forum), disable forum functionality to continue.', $code, $caller, $previous),
            'FRESH_CHANGE_ADMINS_FORBIDDEN' => new self($rpc, 'You were just elected admin, you can\'t add or modify other admins yet.', $code, $caller, $previous),
            'FROM_MESSAGE_BOT_DISABLED' => new \danog\MadelineProto\RPCError\FromMessageBotDisabledError($code, $caller, $previous),
            'FROM_PEER_INVALID' => new self($rpc, 'The specified from_id is invalid.', $code, $caller, $previous),
            'FROZEN_PARTICIPANT_MISSING' => new self($rpc, 'The current account is [frozen](https://core.telegram.org/api/auth#frozen-accounts), and cannot access the specified peer.', $code, $caller, $previous),
            'GAME_BOT_INVALID' => new self($rpc, 'Bots can\'t send another bot\'s game.', $code, $caller, $previous),
            'GENERAL_MODIFY_ICON_FORBIDDEN' => new self($rpc, 'You can\'t modify the icon of the "General" topic.', $code, $caller, $previous),
            'GEO_POINT_INVALID' => new self($rpc, 'Invalid geoposition provided.', $code, $caller, $previous),
            'GIF_CONTENT_TYPE_INVALID' => new self($rpc, 'GIF content-type invalid.', $code, $caller, $previous),
            'GIF_ID_INVALID' => new self($rpc, 'The provided GIF ID is invalid.', $code, $caller, $previous),
            'GIFT_MONTHS_INVALID' => new self($rpc, 'The value passed in invoice.inputInvoicePremiumGiftStars.months is invalid.', $code, $caller, $previous),
            'GIFT_SLUG_EXPIRED' => new self($rpc, 'The specified gift slug has expired.', $code, $caller, $previous),
            'GIFT_SLUG_INVALID' => new self($rpc, 'The specified slug is invalid.', $code, $caller, $previous),
            'GIFT_STARS_INVALID' => new self($rpc, 'The specified amount of stars is invalid.', $code, $caller, $previous),
            'GRAPH_EXPIRED_RELOAD' => new self($rpc, 'This graph has expired, please obtain a new graph token.', $code, $caller, $previous),
            'GRAPH_INVALID_RELOAD' => new self($rpc, 'Invalid graph token provided, please reload the stats and provide the updated token.', $code, $caller, $previous),
            'GRAPH_OUTDATED_RELOAD' => new self($rpc, 'The graph is outdated, please get a new async token using stats.getBroadcastStats.', $code, $caller, $previous),
            'GROUPCALL_ALREADY_DISCARDED' => new self($rpc, 'The group call was already discarded.', $code, $caller, $previous),
            'GROUPCALL_FORBIDDEN' => new self($rpc, 'The specified group call cannot be used in this context.', $code, $caller, $previous),
            'GROUPCALL_INVALID' => new self($rpc, 'The specified group call is invalid.', $code, $caller, $previous),
            'GROUPCALL_JOIN_MISSING' => new self($rpc, 'You haven\'t joined this group call.', $code, $caller, $previous),
            'GROUPCALL_NOT_MODIFIED' => new self($rpc, 'Group call settings weren\'t modified.', $code, $caller, $previous),
            'GROUPCALL_SSRC_DUPLICATE_MUCH' => new self($rpc, 'The app needs to retry joining the group call with a new SSRC value.', $code, $caller, $previous),
            'GROUPED_MEDIA_INVALID' => new self($rpc, 'Invalid grouped media.', $code, $caller, $previous),
            'HASH_INVALID' => new self($rpc, 'The provided hash is invalid.', $code, $caller, $previous),
            'HASH_SIZE_INVALID' => new self($rpc, 'The size of the specified secureValueError.hash is invalid.', $code, $caller, $previous),
            'HASHTAG_INVALID' => new self($rpc, 'The specified hashtag is invalid.', $code, $caller, $previous),
            'HIDE_REQUESTER_MISSING' => new self($rpc, 'The join request was missing or was already handled.', $code, $caller, $previous),
            'ID_EXPIRED' => new self($rpc, 'The passed prepared inline message ID has expired.', $code, $caller, $previous),
            'ID_INVALID' => new self($rpc, 'The passed ID is invalid.', $code, $caller, $previous),
            'IMAGE_PROCESS_FAILED' => new \danog\MadelineProto\RPCError\ImageProcessFailedError($code, $caller, $previous),
            'IMPORT_FILE_INVALID' => new self($rpc, 'The specified chat export file is invalid.', $code, $caller, $previous),
            'IMPORT_FORMAT_DATE_INVALID' => new self($rpc, 'The date specified in the import file is invalid.', $code, $caller, $previous),
            'IMPORT_FORMAT_UNRECOGNIZED' => new self($rpc, 'The specified chat export file was exported from an unsupported chat app.', $code, $caller, $previous),
            'IMPORT_ID_INVALID' => new self($rpc, 'The specified import ID is invalid.', $code, $caller, $previous),
            'IMPORT_TOKEN_INVALID' => new self($rpc, 'The specified token is invalid.', $code, $caller, $previous),
            'INLINE_RESULT_EXPIRED' => new self($rpc, 'The inline query expired.', $code, $caller, $previous),
            'INPUT_CHATLIST_INVALID' => new self($rpc, 'The specified folder is invalid.', $code, $caller, $previous),
            'INPUT_FILE_INVALID' => new self($rpc, 'The specified [InputFile](https://core.telegram.org/type/InputFile) is invalid.', $code, $caller, $previous),
            'INPUT_FILTER_INVALID' => new self($rpc, 'The specified filter is invalid.', $code, $caller, $previous),
            'INPUT_PEERS_EMPTY' => new self($rpc, 'The specified peer array is empty.', $code, $caller, $previous),
            'INPUT_PURPOSE_INVALID' => new self($rpc, 'The specified payment purpose is invalid.', $code, $caller, $previous),
            'INPUT_STARS_AMOUNT_INVALID' => new self($rpc, 'The specified offer amount in stars is invalid, see [here &raquo;](https://core.telegram.org/api/gifts#collectible-gift-purchase-offers) for the allowed range.', $code, $caller, $previous),
            'INPUT_STARS_NANOS_INVALID' => new self($rpc, ' The specified offer amount in nanotons is invalid, see [here &raquo;](https://core.telegram.org/api/gifts#collectible-gift-purchase-offers) for the allowed range.', $code, $caller, $previous),
            'INPUT_TEXT_EMPTY' => new self($rpc, 'The specified text is empty.', $code, $caller, $previous),
            'INPUT_TEXT_TOO_LONG' => new self($rpc, 'The specified text is too long.', $code, $caller, $previous),
            'INPUT_USER_DEACTIVATED' => new \danog\MadelineProto\RPCError\InputUserDeactivatedError($code, $caller, $previous),
            'INVITE_FORBIDDEN_WITH_JOINAS' => new self($rpc, 'If the user has anonymously joined a group call as a channel, they can\'t invite other users to the group call because that would cause deanonymization, because the invite would be sent using the original user ID, not the anonymized channel ID.', $code, $caller, $previous),
            'INVITE_HASH_EMPTY' => new self($rpc, 'The invite hash is empty.', $code, $caller, $previous),
            'INVITE_HASH_EXPIRED' => new self($rpc, 'The invite link has expired.', $code, $caller, $previous),
            'INVITE_HASH_INVALID' => new self($rpc, 'The invite hash is invalid.', $code, $caller, $previous),
            'INVITE_REQUEST_SENT' => new self($rpc, 'You have successfully requested to join this chat or channel.', $code, $caller, $previous),
            'INVITE_REVOKED_MISSING' => new self($rpc, 'The specified invite link was already revoked or is invalid.', $code, $caller, $previous),
            'INVITE_SLUG_EMPTY' => new self($rpc, 'The specified invite slug is empty.', $code, $caller, $previous),
            'INVITE_SLUG_EXPIRED' => new self($rpc, 'The specified chat folder link has expired.', $code, $caller, $previous),
            'INVITE_SLUG_INVALID' => new self($rpc, 'The specified invitation slug is invalid.', $code, $caller, $previous),
            'INVITES_TOO_MUCH' => new self($rpc, 'The maximum number of per-folder invites specified by the `chatlist_invites_limit_default`/`chatlist_invites_limit_premium` [client configuration parameters &raquo;](https://core.telegram.org/api/config#chatlist-invites-limit-default) was reached.', $code, $caller, $previous),
            'INVOICE_INVALID' => new self($rpc, 'The specified invoice is invalid.', $code, $caller, $previous),
            'INVOICE_PAYLOAD_INVALID' => new self($rpc, 'The specified invoice payload is invalid.', $code, $caller, $previous),
            'JOIN_AS_PEER_INVALID' => new self($rpc, 'The specified peer cannot be used to join a group call.', $code, $caller, $previous),
            'LANG_CODE_INVALID' => new self($rpc, 'The specified language code is invalid.', $code, $caller, $previous),
            'LANG_CODE_NOT_SUPPORTED' => new self($rpc, 'The specified language code is not supported.', $code, $caller, $previous),
            'LANG_PACK_INVALID' => new self($rpc, 'The provided language pack is invalid.', $code, $caller, $previous),
            'LANGUAGE_INVALID' => new self($rpc, 'The specified lang_code is invalid.', $code, $caller, $previous),
            'LASTNAME_INVALID' => new self($rpc, 'The last name is invalid.', $code, $caller, $previous),
            'LIMIT_INVALID' => new self($rpc, 'The provided limit is invalid.', $code, $caller, $previous),
            'LIMIT_PER_POST_INVALID' => new self($rpc, 'The specified reactions_limit value is invalid.', $code, $caller, $previous),
            'LINK_NOT_MODIFIED' => new self($rpc, 'Discussion link not modified.', $code, $caller, $previous),
            'LOCATION_INVALID' => new self($rpc, 'The provided location is invalid.', $code, $caller, $previous),
            'MANAGER_INVALID' => new self($rpc, 'The specified manager bot is invalid.', $code, $caller, $previous),
            'MANAGER_PERMISSION_MISSING' => new self($rpc, 'The specified manager bot does not have the [user](https://core.telegram.org/constructor/user).`bot_can_manage_bots` flag set.', $code, $caller, $previous),
            'MAX_DATE_INVALID' => new self($rpc, 'The specified maximum date is invalid.', $code, $caller, $previous),
            'MAX_ID_INVALID' => new self($rpc, 'The provided max ID is invalid.', $code, $caller, $previous),
            'MAX_QTS_INVALID' => new self($rpc, 'The specified max_qts is invalid.', $code, $caller, $previous),
            'MD5_CHECKSUM_INVALID' => new self($rpc, 'The MD5 checksums do not match.', $code, $caller, $previous),
            'MEDIA_ALREADY_PAID' => new self($rpc, 'You already paid for the specified media.', $code, $caller, $previous),
            'MEDIA_CAPTION_TOO_LONG' => new self($rpc, 'The caption is too long.', $code, $caller, $previous),
            'MEDIA_EMPTY' => new self($rpc, 'The provided media object is invalid.', $code, $caller, $previous),
            'MEDIA_FILE_INVALID' => new self($rpc, 'The specified media file is invalid.', $code, $caller, $previous),
            'MEDIA_GROUPED_INVALID' => new self($rpc, 'You tried to send media of different types in an album.', $code, $caller, $previous),
            'MEDIA_INVALID' => new self($rpc, 'Media invalid.', $code, $caller, $previous),
            'MEDIA_NEW_INVALID' => new self($rpc, 'The new media is invalid.', $code, $caller, $previous),
            'MEDIA_PREV_INVALID' => new self($rpc, 'Previous media invalid.', $code, $caller, $previous),
            'MEDIA_TTL_INVALID' => new self($rpc, 'The specified media TTL is invalid.', $code, $caller, $previous),
            'MEDIA_TYPE_INVALID' => new self($rpc, 'The specified media type cannot be used in stories.', $code, $caller, $previous),
            'MEDIA_VIDEO_STORY_MISSING' => new self($rpc, 'A non-story video cannot be repubblished as a story (emitted when trying to resend a non-story video as a story using inputDocument).', $code, $caller, $previous),
            'MEGAGROUP_GEO_REQUIRED' => new self($rpc, 'This method can only be invoked on a geogroup.', $code, $caller, $previous),
            'MEGAGROUP_ID_INVALID' => new self($rpc, 'Invalid supergroup ID.', $code, $caller, $previous),
            'MEGAGROUP_PREHISTORY_HIDDEN' => new self($rpc, 'Group with hidden history for new members can\'t be set as discussion groups.', $code, $caller, $previous),
            'MEGAGROUP_REQUIRED' => new self($rpc, 'You can only use this method on a supergroup.', $code, $caller, $previous),
            'MESSAGE_EDIT_TIME_EXPIRED' => new self($rpc, 'You can\'t edit this message anymore, too much time has passed since its creation.', $code, $caller, $previous),
            'MESSAGE_EMPTY' => new self($rpc, 'The provided message is empty.', $code, $caller, $previous),
            'MESSAGE_ID_INVALID' => new self($rpc, 'The provided message id is invalid.', $code, $caller, $previous),
            'MESSAGE_IDS_EMPTY' => new self($rpc, 'No message ids were provided.', $code, $caller, $previous),
            'MESSAGE_NOT_MODIFIED' => new self($rpc, 'The provided message data is identical to the previous message data, the message wasn\'t modified.', $code, $caller, $previous),
            'MESSAGE_NOT_READ_YET' => new self($rpc, 'The specified message wasn\'t read yet.', $code, $caller, $previous),
            'MESSAGE_POLL_CLOSED' => new self($rpc, 'Poll closed.', $code, $caller, $previous),
            'MESSAGE_REQUIRED' => new self($rpc, 'A non-empty list of IDs must be passed to `id`.', $code, $caller, $previous),
            'MESSAGE_TOO_LONG' => new self($rpc, 'The provided message is too long.', $code, $caller, $previous),
            'MESSAGE_TOO_OLD' => new self($rpc, 'The message is too old, the requested information is not available.', $code, $caller, $previous),
            'METHOD_INVALID' => new self($rpc, 'The specified method is invalid.', $code, $caller, $previous),
            'MIN_DATE_INVALID' => new self($rpc, 'The specified minimum date is invalid.', $code, $caller, $previous),
            'MONTH_INVALID' => new self($rpc, 'The number of months specified in inputInvoicePremiumGiftStars.months is invalid.', $code, $caller, $previous),
            'MSG_ID_INVALID' => new \danog\MadelineProto\RPCError\MsgIdInvalidError($code, $caller, $previous),
            'MSG_TOO_OLD' => new self($rpc, '[`chat_read_mark_expire_period` seconds](https://core.telegram.org/api/config#chat-read-mark-expire-period) have passed since the message was sent, read receipts were deleted.', $code, $caller, $previous),
            'MSG_VOICE_MISSING' => new self($rpc, 'The specified message is not a voice message.', $code, $caller, $previous),
            'MSG_VOICE_TOO_LONG' => new self($rpc, 'The specified voice message is too long to be transcribed.', $code, $caller, $previous),
            'MSG_WAIT_FAILED' => new self($rpc, 'A waiting call returned an error.', $code, $caller, $previous),
            'MULTI_MEDIA_TOO_LONG' => new self($rpc, 'Too many media files for album.', $code, $caller, $previous),
            'NAME_INVALID' => new self($rpc, 'The specified bot name is invalid.', $code, $caller, $previous),
            'NEED_ACTION_MISSING' => new self($rpc, 'The caller didn\'t specify a valid action (either save or suggest) for the contact profile photo upload.', $code, $caller, $previous),
            'NEW_SALT_INVALID' => new self($rpc, 'The new salt is invalid.', $code, $caller, $previous),
            'NEW_SETTINGS_EMPTY' => new self($rpc, 'No password is set on the current account, and no new password was specified in `new_settings`.', $code, $caller, $previous),
            'NEW_SETTINGS_INVALID' => new self($rpc, 'The new password settings are invalid.', $code, $caller, $previous),
            'NEXT_OFFSET_INVALID' => new self($rpc, 'The specified offset is longer than 64 bytes.', $code, $caller, $previous),
            'NO_PAYMENT_NEEDED' => new self($rpc, 'The upgrade/transfer of the specified gift was already paid for or is free.', $code, $caller, $previous),
            'NOGENERAL_HIDE_FORBIDDEN' => new self($rpc, 'Only the "General" topic with `id=1` can be hidden.', $code, $caller, $previous),
            'NOT_ELIGIBLE' => new self($rpc, 'The current user is not eligible to join the Peer-to-Peer Login Program.', $code, $caller, $previous),
            'NOT_JOINED' => new self($rpc, 'The current user hasn\'t joined the Peer-to-Peer Login Program.', $code, $caller, $previous),
            'OFFSET_INVALID' => new self($rpc, 'The provided offset is invalid.', $code, $caller, $previous),
            'OFFSET_PEER_ID_INVALID' => new self($rpc, 'The provided offset peer is invalid.', $code, $caller, $previous),
            'OPTION_INVALID' => new self($rpc, 'Invalid option selected.', $code, $caller, $previous),
            'OPTIONS_TOO_MUCH' => new self($rpc, 'Too many options provided.', $code, $caller, $previous),
            'ORDER_INVALID' => new self($rpc, 'The specified username order is invalid.', $code, $caller, $previous),
            'PACK_SHORT_NAME_INVALID' => new self($rpc, 'Short pack name invalid.', $code, $caller, $previous),
            'PACK_SHORT_NAME_OCCUPIED' => new self($rpc, 'A stickerpack with this name already exists.', $code, $caller, $previous),
            'PACK_TITLE_INVALID' => new self($rpc, 'The stickerpack title is invalid.', $code, $caller, $previous),
            'PACK_TYPE_INVALID' => new self($rpc, 'The masks and emojis flags are mutually exclusive.', $code, $caller, $previous),
            'PARENT_PEER_INVALID' => new self($rpc, 'The specified `parent_peer` is invalid.', $code, $caller, $previous),
            'PARTICIPANT_ID_INVALID' => new self($rpc, 'The specified participant ID is invalid.', $code, $caller, $previous),
            'PARTICIPANT_JOIN_MISSING' => new self($rpc, 'Trying to enable a presentation, when the user hasn\'t joined the Video Chat with [phone.joinGroupCall](https://core.telegram.org/method/phone.joinGroupCall).', $code, $caller, $previous),
            'PARTICIPANT_VERSION_OUTDATED' => new self($rpc, 'The other participant does not use an up to date telegram client with support for calls.', $code, $caller, $previous),
            'PARTICIPANTS_TOO_FEW' => new self($rpc, 'Not enough participants.', $code, $caller, $previous),
            'PASSKEY_ORIGIN_MISMATCH' => new self($rpc, 'Third-party clients currently don\'t support passkeys even when changing the origin.', $code, $caller, $previous),
            'PASSWORD_EMPTY' => new self($rpc, 'The provided password is empty.', $code, $caller, $previous),
            'PASSWORD_HASH_INVALID' => new \danog\MadelineProto\RPCError\PasswordHashInvalidError($code, $caller, $previous),
            'PASSWORD_MISSING' => new self($rpc, 'You must [enable 2FA](https://core.telegram.org/api/srp) before executing this operation.', $code, $caller, $previous),
            'PASSWORD_RECOVERY_EXPIRED' => new self($rpc, 'The recovery code has expired.', $code, $caller, $previous),
            'PASSWORD_RECOVERY_NA' => new self($rpc, 'No email was set, can\'t recover password via email.', $code, $caller, $previous),
            'PASSWORD_REQUIRED' => new self($rpc, 'A [2FA password](https://core.telegram.org/api/srp) must be configured to use Telegram Passport.', $code, $caller, $previous),
            'PAYMENT_CREDENTIALS_INVALID' => new self($rpc, 'The specified payment credentials are invalid.', $code, $caller, $previous),
            'PAYMENT_PROVIDER_INVALID' => new self($rpc, 'The specified payment provider is invalid.', $code, $caller, $previous),
            'PAYMENT_REQUIRED' => new self($rpc, 'Payment is required for this action, see [here &raquo;](https://core.telegram.org/api/gifts) for more info.', $code, $caller, $previous),
            'PEER_HISTORY_EMPTY' => new self($rpc, 'You can\'t pin an empty chat with a user.', $code, $caller, $previous),
            'PEER_ID_INVALID' => new \danog\MadelineProto\RPCError\PeerIdInvalidError($code, $caller, $previous),
            'PEER_ID_NOT_SUPPORTED' => new self($rpc, 'The provided peer ID is not supported.', $code, $caller, $previous),
            'PEER_TYPES_INVALID' => new self($rpc, 'The passed [keyboardButtonSwitchInline](https://core.telegram.org/constructor/keyboardButtonSwitchInline).`peer_types` field is invalid.', $code, $caller, $previous),
            'PEERS_LIST_EMPTY' => new self($rpc, 'The specified list of peers is empty.', $code, $caller, $previous),
            'PERSISTENT_TIMESTAMP_EMPTY' => new self($rpc, 'Persistent timestamp empty.', $code, $caller, $previous),
            'PERSISTENT_TIMESTAMP_INVALID' => new self($rpc, 'Persistent timestamp invalid.', $code, $caller, $previous),
            'PHONE_CODE_EMPTY' => new self($rpc, 'phone_code is missing.', $code, $caller, $previous),
            'PHONE_CODE_EXPIRED' => new self($rpc, 'The phone code you provided has expired.', $code, $caller, $previous),
            'PHONE_CODE_HASH_EMPTY' => new self($rpc, 'phone_code_hash is missing.', $code, $caller, $previous),
            'PHONE_CODE_INVALID' => new self($rpc, 'The provided phone code is invalid.', $code, $caller, $previous),
            'PHONE_HASH_EXPIRED' => new self($rpc, 'An invalid or expired `phone_code_hash` was provided.', $code, $caller, $previous),
            'PHONE_NOT_OCCUPIED' => new self($rpc, 'No user is associated to the specified phone number.', $code, $caller, $previous),
            'PHONE_NUMBER_APP_SIGNUP_FORBIDDEN' => new self($rpc, 'You can\'t sign up using this app.', $code, $caller, $previous),
            'PHONE_NUMBER_BANNED' => new self($rpc, 'The provided phone number is banned from telegram.', $code, $caller, $previous),
            'PHONE_NUMBER_FLOOD' => new self($rpc, 'You asked for the code too many times.', $code, $caller, $previous),
            'PHONE_NUMBER_INVALID' => new self($rpc, 'The phone number is invalid.', $code, $caller, $previous),
            'PHONE_NUMBER_OCCUPIED' => new self($rpc, 'The phone number is already in use.', $code, $caller, $previous),
            'PHONE_NUMBER_UNOCCUPIED' => new self($rpc, 'The phone number is not yet being used.', $code, $caller, $previous),
            'PHONE_PASSWORD_PROTECTED' => new self($rpc, 'This phone is password protected.', $code, $caller, $previous),
            'PHOTO_CONTENT_TYPE_INVALID' => new self($rpc, 'Photo mime-type invalid.', $code, $caller, $previous),
            'PHOTO_CONTENT_URL_EMPTY' => new self($rpc, 'Photo URL invalid.', $code, $caller, $previous),
            'PHOTO_CROP_FILE_MISSING' => new self($rpc, 'Photo crop file missing.', $code, $caller, $previous),
            'PHOTO_CROP_SIZE_SMALL' => new self($rpc, 'Photo is too small.', $code, $caller, $previous),
            'PHOTO_EXT_INVALID' => new self($rpc, 'The extension of the photo is invalid.', $code, $caller, $previous),
            'PHOTO_FILE_MISSING' => new self($rpc, 'Profile photo file missing.', $code, $caller, $previous),
            'PHOTO_ID_INVALID' => new self($rpc, 'Photo ID invalid.', $code, $caller, $previous),
            'PHOTO_INVALID' => new self($rpc, 'Photo invalid.', $code, $caller, $previous),
            'PHOTO_INVALID_DIMENSIONS' => new self($rpc, 'The photo dimensions are invalid.', $code, $caller, $previous),
            'PHOTO_SAVE_FILE_INVALID' => new self($rpc, 'Internal issues, try again later.', $code, $caller, $previous),
            'PHOTO_THUMB_URL_EMPTY' => new self($rpc, 'Photo thumbnail URL is empty.', $code, $caller, $previous),
            'PIN_RESTRICTED' => new self($rpc, 'You can\'t pin messages.', $code, $caller, $previous),
            'PINNED_DIALOGS_TOO_MUCH' => new \danog\MadelineProto\RPCError\PinnedDialogsTooMuchError($code, $caller, $previous),
            'PINNED_TOO_MUCH' => new self($rpc, 'There are too many pinned topics, unpin some first.', $code, $caller, $previous),
            'PINNED_TOPIC_NOT_MODIFIED' => new self($rpc, 'The specified topic is already pinned.', $code, $caller, $previous),
            'POLL_ANSWER_INVALID' => new self($rpc, 'One of the poll answers is not acceptable.', $code, $caller, $previous),
            'POLL_ANSWERS_INVALID' => new self($rpc, 'Invalid poll answers were provided.', $code, $caller, $previous),
            'POLL_OPTION_DUPLICATE' => new \danog\MadelineProto\RPCError\PollOptionDuplicateError($code, $caller, $previous),
            'POLL_OPTION_INVALID' => new self($rpc, 'Invalid poll option provided.', $code, $caller, $previous),
            'POLL_QUESTION_INVALID' => new self($rpc, 'One of the poll questions is not acceptable.', $code, $caller, $previous),
            'PREMIUM_ACCOUNT_REQUIRED' => new \danog\MadelineProto\RPCError\PremiumAccountRequiredError($code, $caller, $previous),
            'PREMIUM_PURPOSE_INVALID' => new self($rpc, 'The specified InputStorePaymentPurpose is invalid.', $code, $caller, $previous),
            'PRICING_CHAT_INVALID' => new self($rpc, 'The pricing for the [subscription](https://core.telegram.org/api/subscriptions) is invalid, the maximum price is specified in the [`stars_subscription_amount_max` config key &raquo;](https://core.telegram.org/api/config#stars-subscription-amount-max).', $code, $caller, $previous),
            'PRIVACY_KEY_INVALID' => new self($rpc, 'The privacy key is invalid.', $code, $caller, $previous),
            'PRIVACY_TOO_LONG' => new self($rpc, 'Too many privacy rules were specified, the current limit is 1000.', $code, $caller, $previous),
            'PRIVACY_VALUE_INVALID' => new self($rpc, 'The specified privacy rule combination is invalid.', $code, $caller, $previous),
            'PUBLIC_BROADCAST_EXPECTED' => new self($rpc, '`channel` only accepts a channel, but a supergroup was passed.', $code, $caller, $previous),
            'PUBLIC_KEY_INVALID' => new self($rpc, 'The specified e2e public key is invalid.', $code, $caller, $previous),
            'PUBLIC_KEY_REQUIRED' => new self($rpc, 'A public key is required.', $code, $caller, $previous),
            'PURPOSE_INVALID' => new self($rpc, 'The specified payment purpose is invalid.', $code, $caller, $previous),
            'QUERY_ID_EMPTY' => new self($rpc, 'The query ID is empty.', $code, $caller, $previous),
            'QUERY_ID_INVALID' => new self($rpc, 'The query ID is invalid.', $code, $caller, $previous),
            'QUERY_TOO_SHORT' => new self($rpc, 'The query string is too short.', $code, $caller, $previous),
            'QUICK_REPLIES_BOT_NOT_ALLOWED' => new \danog\MadelineProto\RPCError\QuickRepliesBotNotAllowedError($code, $caller, $previous),
            'QUICK_REPLIES_TOO_MUCH' => new \danog\MadelineProto\RPCError\QuickRepliesTooMuchError($code, $caller, $previous),
            'QUIZ_ANSWER_MISSING' => new self($rpc, 'You can forward a quiz while hiding the original author only after choosing an option in the quiz.', $code, $caller, $previous),
            'QUIZ_CORRECT_ANSWER_INVALID' => new self($rpc, 'An invalid value was provided to the correct_answers field.', $code, $caller, $previous),
            'QUIZ_CORRECT_ANSWERS_EMPTY' => new self($rpc, 'No correct quiz answer was specified.', $code, $caller, $previous),
            'QUIZ_CORRECT_ANSWERS_TOO_MUCH' => new \danog\MadelineProto\RPCError\QuizCorrectAnswersTooMuchError($code, $caller, $previous),
            'QUIZ_MULTIPLE_INVALID' => new self($rpc, 'Quizzes can\'t have the multiple_choice flag set!', $code, $caller, $previous),
            'QUOTE_TEXT_INVALID' => new self($rpc, 'The specified `reply_to`.`quote_text` field is invalid.', $code, $caller, $previous),
            'RAISE_HAND_FORBIDDEN' => new self($rpc, 'You cannot raise your hand.', $code, $caller, $previous),
            'RANDOM_ID_DUPLICATE' => new self($rpc, 'You provided a random ID that was already used.', $code, $caller, $previous),
            'RANDOM_ID_EMPTY' => new self($rpc, 'Random ID empty.', $code, $caller, $previous),
            'RANDOM_ID_EXPIRED' => new self($rpc, 'The specified `random_id` was expired (most likely it didn\'t follow the required `uint64_t random_id = (time() << 32) | ((uint64_t)random_uint32_t())` format, or the specified time is too far in the past).', $code, $caller, $previous),
            'RANDOM_ID_INVALID' => new self($rpc, 'A provided random ID is invalid.', $code, $caller, $previous),
            'RANDOM_LENGTH_INVALID' => new self($rpc, 'Random length invalid.', $code, $caller, $previous),
            'RANGES_INVALID' => new self($rpc, 'Invalid range provided.', $code, $caller, $previous),
            'REACTION_EMPTY' => new self($rpc, 'Empty reaction provided.', $code, $caller, $previous),
            'REACTION_INVALID' => new self($rpc, 'The specified reaction is invalid.', $code, $caller, $previous),
            'REACTIONS_COUNT_INVALID' => new self($rpc, 'The specified number of reactions is invalid.', $code, $caller, $previous),
            'REACTIONS_TOO_MANY' => new self($rpc, 'The message already has exactly `reactions_uniq_max` reaction emojis, you can\'t react with a new emoji, see [the docs for more info &raquo;](https://core.telegram.org/api/config#client-configuration).', $code, $caller, $previous),
            'RECEIPT_EMPTY' => new self($rpc, 'The specified receipt is empty.', $code, $caller, $previous),
            'REPLY_MARKUP_BUY_EMPTY' => new self($rpc, 'Reply markup for buy button empty.', $code, $caller, $previous),
            'REPLY_MARKUP_GAME_EMPTY' => new self($rpc, 'A game message is being edited, but the newly provided keyboard doesn\'t have a keyboardButtonGame button.', $code, $caller, $previous),
            'REPLY_MARKUP_INVALID' => new self($rpc, 'The provided reply markup is invalid.', $code, $caller, $previous),
            'REPLY_MARKUP_TOO_LONG' => new self($rpc, 'The specified reply_markup is too long.', $code, $caller, $previous),
            'REPLY_MESSAGE_ID_INVALID' => new self($rpc, 'The specified reply-to message ID is invalid.', $code, $caller, $previous),
            'REPLY_MESSAGES_TOO_MUCH' => new \danog\MadelineProto\RPCError\ReplyMessagesTooMuchError($code, $caller, $previous),
            'REPLY_TO_INVALID' => new self($rpc, 'The specified `reply_to` field is invalid.', $code, $caller, $previous),
            'REPLY_TO_MONOFORUM_PEER_INVALID' => new self($rpc, 'The specified inputReplyToMonoForum.monoforum_peer_id is invalid.', $code, $caller, $previous),
            'REPLY_TO_USER_INVALID' => new self($rpc, 'The replied-to user is invalid.', $code, $caller, $previous),
            'REQUEST_MSG_EXPIRED' => new self($rpc, 'The request specified in request_msg_id has already expired.', $code, $caller, $previous),
            'REQUEST_TOKEN_INVALID' => new \danog\MadelineProto\RPCError\RequestTokenInvalidError($code, $caller, $previous),
            'RESELL_STARS_TOO_FEW' => new self($rpc, 'The offered price is too low.', $code, $caller, $previous),
            'RESELL_STARS_TOO_MUCH' => new self($rpc, 'The offered price is too high.', $code, $caller, $previous),
            'RESET_REQUEST_MISSING' => new self($rpc, 'No password reset is in progress.', $code, $caller, $previous),
            'RESULT_ID_DUPLICATE' => new self($rpc, 'You provided a duplicate result ID.', $code, $caller, $previous),
            'RESULT_ID_EMPTY' => new self($rpc, 'Result ID empty.', $code, $caller, $previous),
            'RESULT_ID_INVALID' => new self($rpc, 'One of the specified result IDs is invalid.', $code, $caller, $previous),
            'RESULT_TYPE_INVALID' => new self($rpc, 'Result type invalid.', $code, $caller, $previous),
            'RESULTS_TOO_MUCH' => new self($rpc, 'Too many results were provided.', $code, $caller, $previous),
            'REVOTE_NOT_ALLOWED' => new self($rpc, 'You cannot change your vote.', $code, $caller, $previous),
            'RIGHTS_NOT_MODIFIED' => new self($rpc, 'The new admin rights are equal to the old rights, no change was made.', $code, $caller, $previous),
            'RINGTONE_INVALID' => new self($rpc, 'The specified ringtone is invalid.', $code, $caller, $previous),
            'RINGTONE_MIME_INVALID' => new self($rpc, 'The MIME type for the ringtone is invalid.', $code, $caller, $previous),
            'RSA_DECRYPT_FAILED' => new self($rpc, 'Internal RSA decryption failed.', $code, $caller, $previous),
            'SAVED_ID_EMPTY' => new self($rpc, 'The passed inputSavedStarGiftChat.saved_id is empty.', $code, $caller, $previous),
            'SCHEDULE_BOT_NOT_ALLOWED' => new \danog\MadelineProto\RPCError\ScheduleBotNotAllowedError($code, $caller, $previous),
            'SCHEDULE_DATE_INVALID' => new self($rpc, 'Invalid schedule date provided.', $code, $caller, $previous),
            'SCHEDULE_DATE_TOO_LATE' => new \danog\MadelineProto\RPCError\ScheduleDateTooLateError($code, $caller, $previous),
            'SCHEDULE_STATUS_PRIVATE' => new \danog\MadelineProto\RPCError\ScheduleStatusPrivateError($code, $caller, $previous),
            'SCHEDULE_TOO_MUCH' => new \danog\MadelineProto\RPCError\ScheduleTooMuchError($code, $caller, $previous),
            'SCORE_INVALID' => new self($rpc, 'The specified game score is invalid.', $code, $caller, $previous),
            'SEARCH_QUERY_EMPTY' => new self($rpc, 'The search query is empty.', $code, $caller, $previous),
            'SEARCH_WITH_LINK_NOT_SUPPORTED' => new self($rpc, 'You cannot provide a search query and an invite link at the same time.', $code, $caller, $previous),
            'SECONDS_INVALID' => new self($rpc, 'Invalid duration provided.', $code, $caller, $previous),
            'SECURE_SECRET_REQUIRED' => new self($rpc, 'A secure secret is required.', $code, $caller, $previous),
            'SELF_DELETE_RESTRICTED' => new self($rpc, 'Business bots can\'t delete messages just for the user, `revoke` **must** be set.', $code, $caller, $previous),
            'SEND_AS_PEER_INVALID' => new self($rpc, 'You can\'t send messages as the specified peer.', $code, $caller, $previous),
            'SEND_MESSAGE_GAME_INVALID' => new self($rpc, 'An inputBotInlineMessageGame can only be contained in an inputBotInlineResultGame, not in an inputBotInlineResult/inputBotInlineResultPhoto/etc.', $code, $caller, $previous),
            'SEND_MESSAGE_MEDIA_INVALID' => new self($rpc, 'Invalid media provided.', $code, $caller, $previous),
            'SEND_MESSAGE_TYPE_INVALID' => new self($rpc, 'The message type is invalid.', $code, $caller, $previous),
            'SETTINGS_INVALID' => new self($rpc, 'Invalid settings were provided.', $code, $caller, $previous),
            'SHA256_HASH_INVALID' => new self($rpc, 'The provided SHA256 hash is invalid.', $code, $caller, $previous),
            'SHORT_NAME_INVALID' => new self($rpc, 'The specified short name is invalid.', $code, $caller, $previous),
            'SHORT_NAME_OCCUPIED' => new self($rpc, 'The specified short name is already in use.', $code, $caller, $previous),
            'SHORTCUT_INVALID' => new self($rpc, 'The specified shortcut is invalid.', $code, $caller, $previous),
            'SLOTS_EMPTY' => new self($rpc, 'The specified slot list is empty.', $code, $caller, $previous),
            'SLOWMODE_MULTI_MSGS_DISABLED' => new self($rpc, 'Slowmode is enabled, you cannot forward multiple messages to this group.', $code, $caller, $previous),
            'SLUG_INVALID' => new self($rpc, 'The specified invoice slug is invalid.', $code, $caller, $previous),
            'SMS_CODE_CREATE_FAILED' => new self($rpc, 'An error occurred while creating the SMS code.', $code, $caller, $previous),
            'SMSJOB_ID_INVALID' => new self($rpc, 'The specified job ID is invalid.', $code, $caller, $previous),
            'SRP_A_INVALID' => new self($rpc, 'The specified inputCheckPasswordSRP.A value is invalid.', $code, $caller, $previous),
            'SRP_ID_INVALID' => new self($rpc, 'Invalid SRP ID provided.', $code, $caller, $previous),
            'SRP_PASSWORD_CHANGED' => new self($rpc, 'Password has changed.', $code, $caller, $previous),
            'STARGIFT_ALREADY_CONVERTED' => new self($rpc, 'The specified star gift was already converted to Stars.', $code, $caller, $previous),
            'STARGIFT_ALREADY_REFUNDED' => new self($rpc, 'The specified star gift was already refunded.', $code, $caller, $previous),
            'STARGIFT_ALREADY_UPGRADED' => new self($rpc, 'The specified gift was already upgraded to a collectible gift.', $code, $caller, $previous),
            'STARGIFT_ATTRIBUTE_INVALID' => new self($rpc, 'One of the specified star gift attributes is invalid.', $code, $caller, $previous),
            'STARGIFT_INVALID' => new self($rpc, 'The passed gift is invalid.', $code, $caller, $previous),
            'STARGIFT_MESSAGE_INVALID' => new self($rpc, 'The specified inputInvoiceStarGift.message is invalid.', $code, $caller, $previous),
            'STARGIFT_NOT_FOUND' => new self($rpc, 'The specified gift was not found.', $code, $caller, $previous),
            'STARGIFT_NOT_OWNER' => new self($rpc, 'You\'re not the owner of the gift you trying to transfer.', $code, $caller, $previous),
            'STARGIFT_NOT_UNIQUE' => new self($rpc, 'You can\'t transfer a non-collectible gift.', $code, $caller, $previous),
            'STARGIFT_OBJECT_INVALID' => new self($rpc, 'The specified star gift object is invalid.', $code, $caller, $previous),
            'STARGIFT_OFFER_INVALID' => new self($rpc, 'The specified offer amount is invalid.', $code, $caller, $previous),
            'STARGIFT_OFFER_NOT_ALLOWED' => new self($rpc, 'You can\'t send a purchase offer for this gift.', $code, $caller, $previous),
            'STARGIFT_OWNER_INVALID' => new self($rpc, 'You cannot transfer or sell a gift owned by another user.', $code, $caller, $previous),
            'STARGIFT_PEER_INVALID' => new self($rpc, 'The specified inputSavedStarGiftChat.peer is invalid.', $code, $caller, $previous),
            'STARGIFT_RESELL_CURRENCY_NOT_ALLOWED' => new self($rpc, 'You can\'t buy the gift using the specified currency (i.e. trying to pay in Stars for TON gifts).', $code, $caller, $previous),
            'STARGIFT_SLUG_INVALID' => new self($rpc, 'The specified gift slug is invalid.', $code, $caller, $previous),
            'STARGIFT_UPGRADE_UNAVAILABLE' => new self($rpc, 'A received gift can only be upgraded to a collectible gift if the [messageActionStarGift](https://core.telegram.org/constructor/messageActionStarGift)/[savedStarGift](https://core.telegram.org/constructor/savedStarGift).`can_upgrade` flag is set.', $code, $caller, $previous),
            'STARGIFT_USAGE_LIMITED' => new self($rpc, 'The gift is sold out.', $code, $caller, $previous),
            'STARGIFT_USER_USAGE_LIMITED' => new self($rpc, 'You\'ve reached the starGift.limited_per_user limit, you can\'t buy any more gifts of this type.', $code, $caller, $previous),
            'STARREF_AWAITING_END' => new self($rpc, 'The previous referral program was terminated less than 24 hours ago: further changes can be made after the date specified in userFull.starref_program.end_date.', $code, $caller, $previous),
            'STARREF_EXPIRED' => new self($rpc, 'The specified referral link is invalid.', $code, $caller, $previous),
            'STARREF_HASH_REVOKED' => new self($rpc, 'The specified affiliate link was already revoked.', $code, $caller, $previous),
            'STARREF_PERMILLE_INVALID' => new self($rpc, 'The specified commission_permille is invalid: the minimum and maximum values for this parameter are contained in the [starref_min_commission_permille](https://core.telegram.org/api/config#starref-min-commission-permille) and [starref_max_commission_permille](https://core.telegram.org/api/config#starref-max-commission-permille) client configuration parameters.', $code, $caller, $previous),
            'STARREF_PERMILLE_TOO_LOW' => new self($rpc, 'The specified commission_permille is too low: the minimum and maximum values for this parameter are contained in the [starref_min_commission_permille](https://core.telegram.org/api/config#starref-min-commission-permille) and [starref_max_commission_permille](https://core.telegram.org/api/config#starref-max-commission-permille) client configuration parameters.', $code, $caller, $previous),
            'STARS_AMOUNT_INVALID' => new self($rpc, 'The specified amount in stars is invalid.', $code, $caller, $previous),
            'STARS_INVOICE_INVALID' => new self($rpc, 'The specified Telegram Star invoice is invalid.', $code, $caller, $previous),
            'STARS_PAYMENT_REQUIRED' => new self($rpc, 'To import this chat invite link, you must first [pay for the associated Telegram Star subscription &raquo;](https://core.telegram.org/api/subscriptions#channel-subscriptions).', $code, $caller, $previous),
            'START_PARAM_EMPTY' => new self($rpc, 'The start parameter is empty.', $code, $caller, $previous),
            'START_PARAM_INVALID' => new self($rpc, 'Start parameter invalid.', $code, $caller, $previous),
            'START_PARAM_TOO_LONG' => new self($rpc, 'Start parameter is too long.', $code, $caller, $previous),
            'STICKER_DOCUMENT_INVALID' => new self($rpc, 'The specified sticker document is invalid.', $code, $caller, $previous),
            'STICKER_EMOJI_INVALID' => new self($rpc, 'Sticker emoji invalid.', $code, $caller, $previous),
            'STICKER_FILE_INVALID' => new self($rpc, 'Sticker file invalid.', $code, $caller, $previous),
            'STICKER_GIF_DIMENSIONS' => new self($rpc, 'The specified video sticker has invalid dimensions.', $code, $caller, $previous),
            'STICKER_ID_INVALID' => new self($rpc, 'The provided sticker ID is invalid.', $code, $caller, $previous),
            'STICKER_INVALID' => new self($rpc, 'The provided sticker is invalid.', $code, $caller, $previous),
            'STICKER_MIME_INVALID' => new self($rpc, 'The specified sticker MIME type is invalid.', $code, $caller, $previous),
            'STICKER_PNG_DIMENSIONS' => new self($rpc, 'Sticker png dimensions invalid.', $code, $caller, $previous),
            'STICKER_PNG_NOPNG' => new self($rpc, 'One of the specified stickers is not a valid PNG file.', $code, $caller, $previous),
            'STICKER_TGS_NODOC' => new self($rpc, 'You must send the animated sticker as a document.', $code, $caller, $previous),
            'STICKER_TGS_NOTGS' => new self($rpc, 'Invalid TGS sticker provided.', $code, $caller, $previous),
            'STICKER_THUMB_PNG_NOPNG' => new self($rpc, 'Incorrect stickerset thumb file provided, PNG / WEBP expected.', $code, $caller, $previous),
            'STICKER_THUMB_TGS_NOTGS' => new self($rpc, 'Incorrect stickerset TGS thumb file provided.', $code, $caller, $previous),
            'STICKER_VIDEO_BIG' => new self($rpc, 'The specified video sticker is too big.', $code, $caller, $previous),
            'STICKER_VIDEO_NODOC' => new self($rpc, 'You must send the video sticker as a document.', $code, $caller, $previous),
            'STICKER_VIDEO_NOWEBM' => new self($rpc, 'The specified video sticker is not in webm format.', $code, $caller, $previous),
            'STICKERPACK_STICKERS_TOO_MUCH' => new self($rpc, 'There are too many stickers in this stickerpack, you can\'t add any more.', $code, $caller, $previous),
            'STICKERS_EMPTY' => new self($rpc, 'No sticker provided.', $code, $caller, $previous),
            'STICKERS_TOO_MUCH' => new self($rpc, 'There are too many stickers in this stickerpack, you can\'t add any more.', $code, $caller, $previous),
            'STICKERSET_INVALID' => new self($rpc, 'The provided sticker set is invalid.', $code, $caller, $previous),
            'STORIES_NEVER_CREATED' => new \danog\MadelineProto\RPCError\StoriesNeverCreatedError($code, $caller, $previous),
            'STORIES_TOO_MUCH' => new self($rpc, 'You have hit the maximum active stories limit as specified by the [`story_expiring_limit_*` client configuration parameters](https://core.telegram.org/api/config#story-expiring-limit-default): you should buy a [Premium](https://core.telegram.org/api/premium) subscription, delete an active story, or wait for the oldest story to expire.', $code, $caller, $previous),
            'STORY_ID_EMPTY' => new self($rpc, 'You specified no story IDs.', $code, $caller, $previous),
            'STORY_ID_INVALID' => new self($rpc, 'The specified story ID is invalid.', $code, $caller, $previous),
            'STORY_NOT_MODIFIED' => new self($rpc, 'The new story information you passed is equal to the previous story information, thus it wasn\'t modified.', $code, $caller, $previous),
            'STORY_PERIOD_INVALID' => new self($rpc, 'The specified story period is invalid for this account.', $code, $caller, $previous),
            'SUBSCRIPTION_EXPORT_MISSING' => new \danog\MadelineProto\RPCError\SubscriptionExportMissingError($code, $caller, $previous),
            'SUBSCRIPTION_ID_INVALID' => new self($rpc, 'The specified subscription_id is invalid.', $code, $caller, $previous),
            'SUBSCRIPTION_PERIOD_INVALID' => new self($rpc, 'The specified subscription_pricing.period is invalid.', $code, $caller, $previous),
            'SUGGESTED_POST_AMOUNT_INVALID' => new self($rpc, 'The specified price for the suggested post is invalid.', $code, $caller, $previous),
            'SUGGESTED_POST_PEER_INVALID' => new self($rpc, 'You cannot send suggested posts to non-[monoforum](https://core.telegram.org/api/monoforum) peers.', $code, $caller, $previous),
            'SWITCH_PM_TEXT_EMPTY' => new self($rpc, 'The switch_pm.text field was empty.', $code, $caller, $previous),
            'SWITCH_WEBVIEW_URL_INVALID' => new self($rpc, 'The URL specified in switch_webview.url is invalid!', $code, $caller, $previous),
            'TAKEOUT_INVALID' => new self($rpc, 'The specified takeout ID is invalid.', $code, $caller, $previous),
            'TAKEOUT_REQUIRED' => new self($rpc, 'A [takeout](https://core.telegram.org/api/takeout) session needs to be initialized first, [see here &raquo; for more info](https://core.telegram.org/api/takeout).', $code, $caller, $previous),
            'TASK_ALREADY_EXISTS' => new self($rpc, 'An email reset was already requested.', $code, $caller, $previous),
            'TEMP_AUTH_KEY_ALREADY_BOUND' => new self($rpc, 'The passed temporary key is already bound to another **perm_auth_key_id**.', $code, $caller, $previous),
            'TEMP_AUTH_KEY_EMPTY' => new self($rpc, 'No temporary auth key provided.', $code, $caller, $previous),
            'TERMS_URL_INVALID' => new self($rpc, 'The specified [invoice](https://core.telegram.org/constructor/invoice).`terms_url` is invalid.', $code, $caller, $previous),
            'TEXTDRAFT_PEER_INVALID' => new self($rpc, 'sendMessageTextDraftAction can only be used in private 1-on-1 chats.', $code, $caller, $previous),
            'THEME_FILE_INVALID' => new self($rpc, 'Invalid theme file provided.', $code, $caller, $previous),
            'THEME_FORMAT_INVALID' => new self($rpc, 'Invalid theme format provided.', $code, $caller, $previous),
            'THEME_INVALID' => new self($rpc, 'Invalid theme provided.', $code, $caller, $previous),
            'THEME_MIME_INVALID' => new self($rpc, 'The theme\'s MIME type is invalid.', $code, $caller, $previous),
            'THEME_PARAMS_INVALID' => new self($rpc, 'The specified `theme_params` field is invalid.', $code, $caller, $previous),
            'THEME_SLUG_INVALID' => new self($rpc, 'The specified theme slug is invalid.', $code, $caller, $previous),
            'THEME_TITLE_INVALID' => new self($rpc, 'The specified theme title is invalid.', $code, $caller, $previous),
            'TIMEZONE_INVALID' => new self($rpc, 'The specified timezone does not exist.', $code, $caller, $previous),
            'TITLE_INVALID' => new self($rpc, 'The specified stickerpack title is invalid.', $code, $caller, $previous),
            'TMP_PASSWORD_DISABLED' => new self($rpc, 'The temporary password is disabled.', $code, $caller, $previous),
            'TMP_PASSWORD_INVALID' => new self($rpc, 'The passed tmp_password is invalid.', $code, $caller, $previous),
            'TO_ID_INVALID' => new self($rpc, 'The specified `to_id` of the passed inputInvoiceStarGiftResale or inputInvoiceStarGiftTransfer is invalid.', $code, $caller, $previous),
            'TO_LANG_INVALID' => new self($rpc, 'The specified destination language is invalid.', $code, $caller, $previous),
            'TODO_ITEM_DUPLICATE' => new \danog\MadelineProto\RPCError\TodoItemDuplicateError($code, $caller, $previous),
            'TODO_ITEMS_EMPTY' => new self($rpc, 'A checklist was specified, but no [checklist items](https://core.telegram.org/api/todo) were passed.', $code, $caller, $previous),
            'TODO_ITEMS_TOO_MUCH' => new \danog\MadelineProto\RPCError\TodoItemsTooMuchError($code, $caller, $previous),
            'TODO_NOT_MODIFIED' => new self($rpc, 'No todo items were specified, so no changes were made to the todo list.', $code, $caller, $previous),
            'TOKEN_EMPTY' => new self($rpc, 'The specified token is empty.', $code, $caller, $previous),
            'TOKEN_INVALID' => new self($rpc, 'The provided token is invalid.', $code, $caller, $previous),
            'TOKEN_TYPE_INVALID' => new self($rpc, 'The specified token type is invalid.', $code, $caller, $previous),
            'TOPIC_CLOSE_SEPARATELY' => new self($rpc, 'The `close` flag cannot be provided together with any of the other flags.', $code, $caller, $previous),
            'TOPIC_CLOSED' => new \danog\MadelineProto\RPCError\TopicClosedError($code, $caller, $previous),
            'TOPIC_DELETED' => new \danog\MadelineProto\RPCError\TopicDeletedError($code, $caller, $previous),
            'TOPIC_HIDE_SEPARATELY' => new self($rpc, 'The `hide` flag cannot be provided together with any of the other flags.', $code, $caller, $previous),
            'TOPIC_ID_INVALID' => new self($rpc, 'The specified topic ID is invalid.', $code, $caller, $previous),
            'TOPIC_NOT_MODIFIED' => new self($rpc, 'The updated topic info is equal to the current topic info, nothing was changed.', $code, $caller, $previous),
            'TOPIC_TITLE_EMPTY' => new self($rpc, 'The specified topic title is empty.', $code, $caller, $previous),
            'TOPICS_EMPTY' => new self($rpc, 'You specified no topic IDs.', $code, $caller, $previous),
            'TRANSACTION_ID_INVALID' => new self($rpc, 'The specified transaction ID is invalid.', $code, $caller, $previous),
            'TRANSCRIPTION_FAILED' => new self($rpc, 'Audio transcription failed.', $code, $caller, $previous),
            'TRANSLATE_REQ_QUOTA_EXCEEDED' => new self($rpc, 'Translation is currently unavailable due to a temporary server-side lack of resources.', $code, $caller, $previous),
            'TTL_DAYS_INVALID' => new self($rpc, 'The provided TTL is invalid.', $code, $caller, $previous),
            'TTL_MEDIA_INVALID' => new self($rpc, 'Invalid media Time To Live was provided.', $code, $caller, $previous),
            'TTL_PERIOD_INVALID' => new self($rpc, 'The specified TTL period is invalid.', $code, $caller, $previous),
            'TYPES_EMPTY' => new self($rpc, 'No top peer type was provided.', $code, $caller, $previous),
            'UNSUPPORTED' => new self($rpc, '`require_payment` cannot be *set* by users, only by monoforums: users must instead use the [inputPrivacyKeyNoPaidMessages](https://core.telegram.org/constructor/inputPrivacyKeyNoPaidMessages) privacy setting to remove a previously added exemption.', $code, $caller, $previous),
            'UNTIL_DATE_INVALID' => new self($rpc, 'Invalid until date provided.', $code, $caller, $previous),
            'URL_EXPIRED' => new self($rpc, 'The specified OAuth request has expired.', $code, $caller, $previous),
            'URL_INVALID' => new self($rpc, 'Invalid URL provided.', $code, $caller, $previous),
            'USAGE_LIMIT_INVALID' => new self($rpc, 'The specified usage limit is invalid.', $code, $caller, $previous),
            'USER_ADMIN_INVALID' => new self($rpc, 'You\'re not an admin.', $code, $caller, $previous),
            'USER_ALREADY_INVITED' => new self($rpc, 'You have already invited this user.', $code, $caller, $previous),
            'USER_ALREADY_PARTICIPANT' => new self($rpc, 'The user is already in the group.', $code, $caller, $previous),
            'USER_BANNED_IN_CHANNEL' => new \danog\MadelineProto\RPCError\UserBannedInChannelError($code, $caller, $previous),
            'USER_BLOCKED' => new self($rpc, 'User blocked.', $code, $caller, $previous),
            'USER_BOT' => new self($rpc, 'Bots can only be admins in channels.', $code, $caller, $previous),
            'USER_BOT_INVALID' => new self($rpc, 'User accounts must provide the `bot` method parameter when calling this method. If there is no such method parameter, this method can only be invoked by bot accounts.', $code, $caller, $previous),
            'USER_BOT_REQUIRED' => new self($rpc, 'This method can only be called by a bot.', $code, $caller, $previous),
            'USER_BOT_TO_BOT_DISABLED' => new \danog\MadelineProto\RPCError\UserBotToBotDisabledError($code, $caller, $previous),
            'USER_CHANNELS_TOO_MUCH' => new self($rpc, 'One of the users you tried to add is already in too many channels/supergroups.', $code, $caller, $previous),
            'USER_CREATOR' => new self($rpc, 'For channels.editAdmin: you\'ve tried to edit the admin rights of the owner, but you\'re not the owner; for channels.leaveChannel: you can\'t leave this channel, because you\'re its creator.', $code, $caller, $previous),
            'USER_GIFT_UNAVAILABLE' => new self($rpc, 'Gifts are not available in the current region ([stars_gifts_enabled](https://core.telegram.org/api/config#stars-gifts-enabled) is equal to false).', $code, $caller, $previous),
            'USER_ID_INVALID' => new self($rpc, 'The provided user ID is invalid.', $code, $caller, $previous),
            'USER_INVALID' => new self($rpc, 'Invalid user provided.', $code, $caller, $previous),
            'USER_IS_BLOCKED' => new \danog\MadelineProto\RPCError\UserIsBlockedError($code, $caller, $previous),
            'USER_IS_BOT' => new \danog\MadelineProto\RPCError\UserIsBotError($code, $caller, $previous),
            'USER_KICKED' => new self($rpc, 'This user was kicked from this supergroup/channel.', $code, $caller, $previous),
            'USER_NOT_MUTUAL_CONTACT' => new self($rpc, 'The provided user is not a mutual contact.', $code, $caller, $previous),
            'USER_NOT_PARTICIPANT' => new self($rpc, 'You\'re not a member of this supergroup/channel.', $code, $caller, $previous),
            'USER_PUBLIC_MISSING' => new self($rpc, 'Cannot generate a link to stories posted by a peer without a username.', $code, $caller, $previous),
            'USER_VOLUME_INVALID' => new self($rpc, 'The specified user volume is invalid.', $code, $caller, $previous),
            'USERNAME_INVALID' => new \danog\MadelineProto\RPCError\UsernameInvalidError($code, $caller, $previous),
            'USERNAME_NOT_MODIFIED' => new self($rpc, 'The username was not modified.', $code, $caller, $previous),
            'USERNAME_NOT_OCCUPIED' => new \danog\MadelineProto\RPCError\UsernameNotOccupiedError($code, $caller, $previous),
            'USERNAME_OCCUPIED' => new self($rpc, 'The provided username is already occupied.', $code, $caller, $previous),
            'USERNAME_PURCHASE_AVAILABLE' => new self($rpc, 'The specified username can be purchased on https://fragment.com.', $code, $caller, $previous),
            'USERNAME_SUFFIX_MISSING' => new self($rpc, 'The required `bot` suffix is missing from the passed username.', $code, $caller, $previous),
            'USERNAMES_ACTIVE_TOO_MUCH' => new self($rpc, 'The maximum number of active usernames was reached.', $code, $caller, $previous),
            'USERPIC_UPLOAD_REQUIRED' => new self($rpc, 'You must have a profile picture to publish your geolocation.', $code, $caller, $previous),
            'USERS_TOO_FEW' => new self($rpc, 'Not enough users (to create a chat, for example).', $code, $caller, $previous),
            'USERS_TOO_MUCH' => new self($rpc, 'The maximum number of users has been exceeded (to create a chat, for example).', $code, $caller, $previous),
            'VENUE_ID_INVALID' => new self($rpc, 'The specified venue ID is invalid.', $code, $caller, $previous),
            'VIDEO_CONTENT_TYPE_INVALID' => new self($rpc, 'The video\'s content type is invalid.', $code, $caller, $previous),
            'VIDEO_DURATION_INVALID' => new self($rpc, 'The duration of the specified video is invalid.', $code, $caller, $previous),
            'VIDEO_FILE_INVALID' => new self($rpc, 'The specified video file is invalid.', $code, $caller, $previous),
            'VIDEO_PAUSE_FORBIDDEN' => new self($rpc, 'You cannot pause the video stream.', $code, $caller, $previous),
            'VIDEO_STOP_FORBIDDEN' => new self($rpc, 'You cannot stop the video stream.', $code, $caller, $previous),
            'VIDEO_TITLE_EMPTY' => new self($rpc, 'The specified video title is empty.', $code, $caller, $previous),
            'VOICE_MESSAGES_FORBIDDEN' => new \danog\MadelineProto\RPCError\VoiceMessagesForbiddenError($code, $caller, $previous),
            'WALLPAPER_FILE_INVALID' => new self($rpc, 'The specified wallpaper file is invalid.', $code, $caller, $previous),
            'WALLPAPER_INVALID' => new self($rpc, 'The specified wallpaper is invalid.', $code, $caller, $previous),
            'WALLPAPER_MIME_INVALID' => new self($rpc, 'The specified wallpaper MIME type is invalid.', $code, $caller, $previous),
            'WALLPAPER_NOT_FOUND' => new self($rpc, 'The specified wallpaper could not be found.', $code, $caller, $previous),
            'WC_CONVERT_URL_INVALID' => new self($rpc, 'WC convert URL invalid.', $code, $caller, $previous),
            'WEBAPP_REQ_ID_INVALID' => new self($rpc, 'The specified webapp_req_id is invalid.', $code, $caller, $previous),
            'WEBAUTH_TOKEN_EXPIRED' => new self($rpc, 'The specified auth token has expired.', $code, $caller, $previous),
            'WEBDOCUMENT_INVALID' => new self($rpc, 'Invalid webdocument URL provided.', $code, $caller, $previous),
            'WEBDOCUMENT_MIME_INVALID' => new self($rpc, 'Invalid webdocument mime type provided.', $code, $caller, $previous),
            'WEBDOCUMENT_SIZE_TOO_BIG' => new self($rpc, 'Webdocument is too big!', $code, $caller, $previous),
            'WEBDOCUMENT_URL_EMPTY' => new self($rpc, 'The passed web document URL is empty.', $code, $caller, $previous),
            'WEBDOCUMENT_URL_INVALID' => new self($rpc, 'The specified webdocument URL is invalid.', $code, $caller, $previous),
            'WEBPAGE_CURL_FAILED' => new \danog\MadelineProto\RPCError\WebpageCurlFailedError($code, $caller, $previous),
            'WEBPAGE_MEDIA_EMPTY' => new self($rpc, 'Webpage media empty.', $code, $caller, $previous),
            'WEBPAGE_NOT_FOUND' => new \danog\MadelineProto\RPCError\WebpageNotFoundError($code, $caller, $previous),
            'WEBPAGE_URL_INVALID' => new self($rpc, 'The specified webpage `url` is invalid.', $code, $caller, $previous),
            'WEBPUSH_AUTH_INVALID' => new self($rpc, 'The specified web push authentication secret is invalid.', $code, $caller, $previous),
            'WEBPUSH_KEY_INVALID' => new self($rpc, 'The specified web push elliptic curve Diffie-Hellman public key is invalid.', $code, $caller, $previous),
            'WEBPUSH_TOKEN_INVALID' => new self($rpc, 'The specified web push token is invalid.', $code, $caller, $previous),
            'YOU_BLOCKED_USER' => new \danog\MadelineProto\RPCError\YouBlockedUserError($code, $caller, $previous),
            'BOT_METHOD_INVALID' => new self($rpc, 'The specified method cannot be used by bots.', $code, $caller, $previous),
            'CONNECTION_DEVICE_MODEL_EMPTY' => new self($rpc, 'The specified device model is empty.', $code, $caller, $previous),
            'CONNECTION_LANG_PACK_INVALID' => new self($rpc, 'The specified language pack is empty.', $code, $caller, $previous),
            'CONNECTION_NOT_INITED' => new self($rpc, 'Please initialize the connection using initConnection before making queries.', $code, $caller, $previous),
            'CONNECTION_SYSTEM_EMPTY' => new self($rpc, 'The specified system version is empty.', $code, $caller, $previous),
            'CONNECTION_SYSTEM_LANG_CODE_EMPTY' => new self($rpc, 'The specified system language code is empty.', $code, $caller, $previous),
            'INPUT_CONSTRUCTOR_INVALID' => new self($rpc, 'The specified TL constructor is invalid.', $code, $caller, $previous),
            'INPUT_FETCH_ERROR' => new self($rpc, 'An error occurred while parsing the provided TL constructor.', $code, $caller, $previous),
            'INPUT_FETCH_FAIL' => new self($rpc, 'An error occurred while parsing the provided TL constructor.', $code, $caller, $previous),
            'INPUT_LAYER_INVALID' => new self($rpc, 'The specified layer is invalid.', $code, $caller, $previous),
            'INPUT_METHOD_INVALID' => new self($rpc, 'The specified method is invalid.', $code, $caller, $previous),
            'INPUT_REQUEST_TOO_LONG' => new self($rpc, 'The request payload is too long.', $code, $caller, $previous),
            'PEER_FLOOD' => new self($rpc, 'The current account is spamreported, you cannot execute this action, check @spambot for more info.', $code, $caller, $previous),
            'STICKERSET_NOT_MODIFIED' => new self($rpc, 'The passed stickerset information is equal to the current information.', $code, $caller, $previous),
            'ACCESS_DENIED' => new self($rpc, 'The account was deactivated, or is a bot/service account.', $code, $caller, $previous),
            'BOT_ACCESS_FORBIDDEN' => new self($rpc, 'The specified method *can* be used over a [business connection](https://core.telegram.org/api/bots/connected-business-bots) for some operations, but the specified query attempted an operation that is not allowed over a business connection.', $code, $caller, $previous),
            'BOT_FORUM_CREATE_FORBIDDEN' => new self($rpc, 'Since the bot\'s user.bot_forum_can_manage_topics flag is **not** set, the user cannot create or modify bot forum topics.', $code, $caller, $previous),
            'BOT_GUARD_NOT_SUPPORTED' => new self($rpc, 'This bot is not designated as a "join guard" bot. This method is only available to bots that mediate user joins to chats. .', $code, $caller, $previous),
            'BOT_VERIFIER_FORBIDDEN' => new self($rpc, 'This bot cannot assign [verification icons](https://core.telegram.org/api/bots/verification).', $code, $caller, $previous),
            'BROADCAST_FORBIDDEN' => new self($rpc, 'Channel poll voters and reactions cannot be fetched to prevent deanonymization.', $code, $caller, $previous),
            'CHANNEL_PUBLIC_GROUP_NA' => new self($rpc, 'channel/supergroup not available.', $code, $caller, $previous),
            'CHAT_ACTION_FORBIDDEN' => new self($rpc, 'You cannot execute this action.', $code, $caller, $previous),
            'CHAT_ADMIN_INVITE_REQUIRED' => new self($rpc, 'You do not have the rights to do this.', $code, $caller, $previous),
            'CHAT_GUEST_SEND_FORBIDDEN' => new \danog\MadelineProto\RPCError\ChatGuestSendForbiddenError($code, $caller, $previous),
            'CHAT_SEND_AUDIOS_FORBIDDEN' => new \danog\MadelineProto\RPCError\ChatSendAudiosForbiddenError($code, $caller, $previous),
            'CHAT_SEND_DOCS_FORBIDDEN' => new \danog\MadelineProto\RPCError\ChatSendDocsForbiddenError($code, $caller, $previous),
            'CHAT_SEND_GAME_FORBIDDEN' => new self($rpc, 'You can\'t send a game to this chat.', $code, $caller, $previous),
            'CHAT_SEND_GIFS_FORBIDDEN' => new \danog\MadelineProto\RPCError\ChatSendGifsForbiddenError($code, $caller, $previous),
            'CHAT_SEND_MEDIA_FORBIDDEN' => new \danog\MadelineProto\RPCError\ChatSendMediaForbiddenError($code, $caller, $previous),
            'CHAT_SEND_PHOTOS_FORBIDDEN' => new \danog\MadelineProto\RPCError\ChatSendPhotosForbiddenError($code, $caller, $previous),
            'CHAT_SEND_PLAIN_FORBIDDEN' => new \danog\MadelineProto\RPCError\ChatSendPlainForbiddenError($code, $caller, $previous),
            'CHAT_SEND_POLL_FORBIDDEN' => new \danog\MadelineProto\RPCError\ChatSendPollForbiddenError($code, $caller, $previous),
            'CHAT_SEND_ROUNDVIDEOS_FORBIDDEN' => new \danog\MadelineProto\RPCError\ChatSendRoundvideosForbiddenError($code, $caller, $previous),
            'CHAT_SEND_STICKERS_FORBIDDEN' => new \danog\MadelineProto\RPCError\ChatSendStickersForbiddenError($code, $caller, $previous),
            'CHAT_SEND_VIDEOS_FORBIDDEN' => new \danog\MadelineProto\RPCError\ChatSendVideosForbiddenError($code, $caller, $previous),
            'CHAT_SEND_VOICES_FORBIDDEN' => new \danog\MadelineProto\RPCError\ChatSendVoicesForbiddenError($code, $caller, $previous),
            'CHAT_SEND_WEBPAGE_FORBIDDEN' => new self($rpc, 'You can\'t send webpage previews to this chat.', $code, $caller, $previous),
            'CHAT_TYPE_INVALID' => new self($rpc, 'The specified user type is invalid.', $code, $caller, $previous),
            'EDIT_BOT_INVITE_FORBIDDEN' => new self($rpc, 'Normal users can\'t edit invites that were created by bots.', $code, $caller, $previous),
            'GROUPCALL_ALREADY_STARTED' => new self($rpc, 'The groupcall has already started, you can join directly using [phone.joinGroupCall](https://core.telegram.org/method/phone.joinGroupCall).', $code, $caller, $previous),
            'GROUPCALL_CHANGE_FORBIDDEN' => new self($rpc, 'You cannot change this group call setting.', $code, $caller, $previous),
            'INLINE_BOT_REQUIRED' => new self($rpc, 'Only the inline bot can edit message.', $code, $caller, $previous),
            'MESSAGE_AUTHOR_REQUIRED' => new self($rpc, 'Message author required.', $code, $caller, $previous),
            'MESSAGE_DELETE_FORBIDDEN' => new self($rpc, 'You can\'t delete one of the messages you tried to delete, most likely because it is a service message.', $code, $caller, $previous),
            'POLL_VOTE_REQUIRED' => new self($rpc, 'Cast a vote in the poll before calling this method.', $code, $caller, $previous),
            'PRIVACY_PREMIUM_REQUIRED' => new \danog\MadelineProto\RPCError\PrivacyPremiumRequiredError($code, $caller, $previous),
            'PUBLIC_CHANNEL_MISSING' => new self($rpc, 'You can only export group call invite links for public chats or channels.', $code, $caller, $previous),
            'RIGHT_FORBIDDEN' => new self($rpc, 'Your admin rights do not allow you to do this.', $code, $caller, $previous),
            'SENSITIVE_CHANGE_FORBIDDEN' => new self($rpc, 'You can\'t change your sensitive content settings.', $code, $caller, $previous),
            'USER_DELETED' => new self($rpc, 'You can\'t send this secret message because the other participant deleted their account.', $code, $caller, $previous),
            'USER_DISALLOWED_STARGIFTS' => new self($rpc, 'The recipient user has configured restrictions on which categories of star gifts they\'re willing to accept (unique, limited, or unlimited): the sender attempted to get a payment form for a gift that falls into a category the recipient has blocked.', $code, $caller, $previous),
            'USER_PERMISSION_DENIED' => new self($rpc, 'The user hasn\'t granted or has revoked the bot\'s access to change their emoji status using [bots.toggleUserEmojiStatusPermission](https://core.telegram.org/method/bots.toggleUserEmojiStatusPermission).', $code, $caller, $previous),
            'USER_PRIVACY_RESTRICTED' => new self($rpc, 'The user\'s privacy settings do not allow you to do this.', $code, $caller, $previous),
            'USER_RESTRICTED' => new self($rpc, 'You\'re spamreported, you can\'t create channels or chats.', $code, $caller, $previous),
            'YOUR_PRIVACY_RESTRICTED' => new self($rpc, 'You cannot fetch the read date of this message because you have disallowed other users to do so for *your* messages; to fix, allow other users to see *your* exact last online date OR purchase a [Telegram Premium](https://core.telegram.org/api/premium) subscription.', $code, $caller, $previous),
            'CHAT_FORBIDDEN' => new \danog\MadelineProto\RPCError\ChatForbiddenError($code, $caller, $previous),
            'ALLOW_PAYMENT_REQUIRED' => new \danog\MadelineProto\RPCError\AllowPaymentRequiredError($code, $caller, $previous),
            'API_GIFT_RESTRICTED_UPDATE_APP' => new self($rpc, 'Please update the app to access the gift API.', $code, $caller, $previous),
            'BUSINESS_ADDRESS_ACTIVE' => new self($rpc, 'The user is currently advertising a [Business Location](https://core.telegram.org/api/business#location), the location may only be changed (or removed) using [account.updateBusinessLocation &raquo;](https://core.telegram.org/method/account.updateBusinessLocation).  .', $code, $caller, $previous),
            'CALL_PROTOCOL_COMPAT_LAYER_INVALID' => new self($rpc, 'The other side of the call does not support any of the VoIP protocols supported by the local client, as specified by the `protocol.layer` and `protocol.library_versions` fields.', $code, $caller, $previous),
            'EDIT_MESSAGE_TEMP_RESTRICTED' => new self($rpc, 'Message editing is temporarily forbidden for this user due to regulatory restrictions.', $code, $caller, $previous),
            'FILEREF_UPGRADE_NEEDED' => new self($rpc, 'The client has to be updated in order to support [file references](https://core.telegram.org/api/file-references).', $code, $caller, $previous),
            'FRESH_CHANGE_PHONE_FORBIDDEN' => new self($rpc, 'You can\'t change phone number right after logging in, please wait at least 24 hours.', $code, $caller, $previous),
            'FRESH_RESET_AUTHORISATION_FORBIDDEN' => new self($rpc, 'You can\'t logout other sessions if less than 24 hours have passed since you logged on the current session.', $code, $caller, $previous),
            'PAYMENT_UNSUPPORTED' => new \danog\MadelineProto\RPCError\PaymentUnsupportedError($code, $caller, $previous),
            'PHONE_PASSWORD_FLOOD' => new self($rpc, 'You have tried logging in too many times.', $code, $caller, $previous),
            'POLL_COUNTRY_RESTRICTED' => new self($rpc, 'Users from the current user\'s country cannot vote in this [country-restricted poll &raquo;](https://core.telegram.org/api/poll#country-restricted-polls).', $code, $caller, $previous),
            'POLL_MEMBER_RESTRICTED' => new self($rpc, 'Only channel subscribers can vote in this poll.', $code, $caller, $previous),
            'PRECHECKOUT_FAILED' => new self($rpc, 'Precheckout failed, a detailed and localized description for the error will be emitted via an [updateServiceNotification as specified here &raquo;](https://core.telegram.org/api/errors#406-not-acceptable).', $code, $caller, $previous),
            'PREMIUM_CURRENTLY_UNAVAILABLE' => new self($rpc, 'You cannot currently purchase a Premium subscription.', $code, $caller, $previous),
            'SEND_CODE_UNAVAILABLE' => new self($rpc, 'Returned when all available options for this type of number were already used (e.g. flash-call, then SMS, then this error might be returned to trigger a second resend).', $code, $caller, $previous),
            'STARGIFT_EXPORT_IN_PROGRESS' => new self($rpc, 'A gift export is in progress, a detailed and localized description for the error will be emitted via an [updateServiceNotification as specified here &raquo;](https://core.telegram.org/api/errors#406-not-acceptable).', $code, $caller, $previous),
            'STARS_FORM_AMOUNT_MISMATCH' => new self($rpc, 'The form amount has changed, please fetch the new form using [payments.getPaymentForm](https://core.telegram.org/method/payments.getPaymentForm) and restart the process.', $code, $caller, $previous),
            'STICKERSET_OWNER_ANONYMOUS' => new self($rpc, 'Provided stickerset can\'t be installed as group stickerset to prevent admin deanonymization.', $code, $caller, $previous),
            'TRANSLATIONS_DISABLED' => new self($rpc, 'Translations are unavailable, a detailed and localized description for the error will be emitted via an [updateServiceNotification as specified here &raquo;](https://core.telegram.org/api/errors#406-not-acceptable).', $code, $caller, $previous),
            'UPDATE_APP_TO_LOGIN' => new self($rpc, 'Please update to the latest version of MadelineProto to login.', $code, $caller, $previous),
            'USERPIC_PRIVACY_REQUIRED' => new self($rpc, 'You need to disable privacy settings for your profile picture in order to make your geolocation public.', $code, $caller, $previous),
            'AUTH_KEY_DUPLICATED' => new self($rpc, 'Concurrent usage of the current session from multiple connections was detected, the current session was invalidated by the server for security reasons!', $code, $caller, $previous),
            'AUTH_KEY_UNREGISTERED' => new self($rpc, 'The specified authorization key is not registered in the system (for example, a PFS temporary key has expired).', $code, $caller, $previous),
            'AUTH_KEY_INVALID' => new self($rpc, 'The specified auth key is invalid.', $code, $caller, $previous),
            'AUTH_KEY_PERM_EMPTY' => new self($rpc, 'The method is unavailable for temporary authorization keys, not bound to a permanent authorization key.', $code, $caller, $previous),
            'SESSION_EXPIRED' => new self($rpc, 'The session has expired.', $code, $caller, $previous),
            'SESSION_PASSWORD_NEEDED' => new \danog\MadelineProto\RPCError\SessionPasswordNeededError($code, $caller, $previous),
            'SESSION_REVOKED' => new self($rpc, 'The session was revoked by the user.', $code, $caller, $previous),
            'USER_DEACTIVATED' => new self($rpc, 'The current account was deleted by the user.', $code, $caller, $previous),
            'USER_DEACTIVATED_BAN' => new self($rpc, 'The current account was deleted and banned by Telegram\'s antispam system.', $code, $caller, $previous),
            default => new self($rpc, $msg, $code, $caller, $previous)
        };

        // End match
    }

    protected function __construct(
        /**
         * @var string RPC error.
         */
        public readonly string $rpc,
        /**
         * @var string Human-readable description of RPC error.
         */
        public readonly string $description,
        int $code,
        private readonly string $caller,
        ?\Exception $previous = null
    ) {
        parent::__construct($rpc, $code, $previous);
        $this->prettifyTL($caller);
        foreach ($this->getTrace() as $level) {
            if (isset($level['function']) && $level['function'] === 'methodCall') {
                $this->line = $level['line'];
                $this->file = $level['file'];
            }
        }
    }
}
<?php

declare(strict_types=1);

/**
 * RSA module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use danog\MadelineProto\TL\TL;
use phpseclib4\Math\BigInteger;

use const STR_PAD_LEFT;

/**
 * RSA class.
 *
 * @internal
 */
final class RSA
{
    /**
     * Exponent.
     *
     */
    public BigInteger $e;
    /**
     * Modulus.
     *
     */
    public BigInteger $n;
    /**
     * Fingerprint.
     *
     */
    public string $fp;
    /**
     * The OpenSSL key, built from the modulus and the exponent.
     */
    private ?\OpenSSLAsymmetricKey $openssl = null;
    /**
     * Load RSA key.
     *
     * @param TL     $TL      TL serializer
     * @param string $rsa_key RSA key
     */
    public static function load(TL $TL, string $rsa_key): self
    {
        $instance = new self;
        $key = Magic::$hasOpenssl ? openssl_pkey_get_public($rsa_key) : false;
        $details = $key === false ? false : openssl_pkey_get_details($key);
        if ($details !== false && isset($details['rsa']['n'], $details['rsa']['e'])) {
            $instance->n = new BigInteger($details['rsa']['n'], 256);
            $instance->e = new BigInteger($details['rsa']['e'], 256);
        } else {
            $key = \phpseclib4\Crypt\RSA::load($rsa_key);
            $instance->n = Tools::getVar($key, 'modulus');
            $instance->e = Tools::getVar($key, 'exponent');
        }
        $instance->fp = substr(sha1(($TL->serializeObject(['type' => 'bytes'], $instance->n->toBytes(), 'key')).($TL->serializeObject(['type' => 'bytes'], $instance->e->toBytes(), 'key')), true), -8);
        return $instance;
    }
    /**
     * Private constructor.
     *
     * @psalm-mutation-free
     */
    private function __construct()
    {
    }
    /**
     * Sleep function.
     *
     * @psalm-pure
     */
    public function __sleep(): array
    {
        return ['e', 'n', 'fp'];
    }
    /**
     * Encrypt data.
     *
     * @param BigInteger $data Data to encrypt
     */
    public function encrypt(BigInteger $data): string
    {
        if (Magic::$hasOpenssl) {
            $this->openssl ??= openssl_pkey_get_public($this->toPem()) ?: null;
            // Raw RSA: the data is already padded (RSA_PAD), and smaller than the modulus.
            if ($this->openssl !== null
                && openssl_public_encrypt(str_pad($data->toBytes(), 256, "\0", STR_PAD_LEFT), $encrypted, $this->openssl, OPENSSL_NO_PADDING)
            ) {
                /** @var string $encrypted */
                return $encrypted;
            }
        }
        return str_pad($data->powMod($this->e, $this->n)->toBytes(), 256, "\0", STR_PAD_LEFT);
    }
    /**
     * The key in PEM form (SubjectPublicKeyInfo).
     */
    private function toPem(): string
    {
        $integer = static function (BigInteger $n): string {
            $bytes = $n->toBytes();
            // Positive: a leading 0 if the top bit is set.
            if (\ord($bytes[0]) & 0x80) {
                $bytes = "\0".$bytes;
            }
            return "\x02".self::derLength(\strlen($bytes)).$bytes;
        };
        $rsaPublicKey = $integer($this->n).$integer($this->e);
        $rsaPublicKey = "\x30".self::derLength(\strlen($rsaPublicKey)).$rsaPublicKey;
        // AlgorithmIdentifier: rsaEncryption, NULL parameters.
        $spki = "\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00"
            ."\x03".self::derLength(\strlen($rsaPublicKey) + 1)."\0".$rsaPublicKey;
        $spki = "\x30".self::derLength(\strlen($spki)).$spki;
        return "-----BEGIN PUBLIC KEY-----\n".chunk_split(base64_encode($spki), 64, "\n")."-----END PUBLIC KEY-----\n";
    }
    /**
     * A DER length.
     *
     * @psalm-pure
     */
    private static function derLength(int $length): string
    {
        if ($length < 0x80) {
            return \chr($length);
        }
        $bytes = ltrim(pack('N', $length), "\0");
        return \chr(0x80 | \strlen($bytes)).$bytes;
    }
}
<?php

declare(strict_types=1);

/**
 * EventHandler module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

/**
 * Simple event handler class: by extending this class, you can use filters, crons and the simplified event handler API.
 */
abstract class SimpleEventHandler extends EventHandler
{
}
<?php

declare(strict_types=1);

/**
 * Broadcast module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Broadcast;

use danog\MadelineProto\Broadcast\Action\ActionForward;
use danog\MadelineProto\Broadcast\Action\ActionSend;
use Webmozart\Assert\Assert;

/**
 * Manages broadcasts.
 *
 * @internal
 */
trait Broadcast
{
    /** @var array<int, InternalState> */
    private array $broadcasts = [];
    // Start from the top to avoid conflicts with broadcasts that used the previous ID naming scheme
    private int $broadcastId = PHP_INT_MAX;

    /**
     * Sends a list of messages to all peers (users, chats, channels) of the bot.
     *
     * A simplified version of this method is also available: broadcastForwardMessages can work with pre-prepared messages.
     *
     * Will return an integer ID that can be used to:
     *
     * - Get the current broadcast progress with getBroadcastProgress
     * - Cancel the broadcast using cancelBroadcast
     *
     * Note that to avoid manually polling the progress,
     * MadelineProto will also periodically emit updateBroadcastProgress updates,
     * containing a Progress object for all broadcasts currently in-progress.
     *
     * @param array         $messages The messages to send: an array of arrays, containing parameters to pass to messages.sendMessage.
     * @param bool          $pin      Whether to also pin the last sent message.
     * @param float|null    $delay    Number of seconds to wait between each peer.
     */
    public function broadcastMessages(array $messages, ?Filter $filter = null, bool $pin = false, ?float $delay = null): int
    {
        foreach ($messages as &$message) {
            if (isset($message['media']['_']) &&
                (
                    $message['media']['_'] === 'inputMediaUploadedPhoto'
                    || $message['media']['_'] === 'inputMediaUploadedDocument'
                    || $message['media']['_'] === 'inputMediaPhotoExternal'
                    || $message['media']['_'] === 'inputMediaDocumentExternal'
                )
            ) {
                $message['media'] = $this->methodCallAsyncRead(
                    'messages.uploadMedia',
                    ['peer' => 'me', 'media' => $message['media']]
                );
            }
        } unset($message);
        return $this->broadcastCustom(new ActionSend($this, $messages, $pin), $filter, $delay);
    }
    /**
     * Forwards a list of messages to all peers (users, chats, channels) of the bot.
     *
     * Will return an integer ID that can be used to:
     *
     * - Get the current broadcast progress with getBroadcastProgress
     * - Cancel the broadcast using cancelBroadcast
     *
     * Note that to avoid manually polling the progress,
     * MadelineProto will also periodically emit updateBroadcastProgress updates,
     * containing a Progress object for all broadcasts currently in-progress.
     *
     * @param mixed      $from_peer   Bot API ID or Update, from where to forward the messages.
     * @param list<int>  $message_ids IDs of the messages to forward.
     * @param bool       $drop_author If true, will forward messages without quoting the original author.
     * @param bool       $pin         Whether to also pin the last sent message.
     * @param float|null $delay       Number of seconds to wait between each peer.
     */
    public function broadcastForwardMessages(mixed $from_peer, array $message_ids, bool $drop_author = false, ?Filter $filter = null, bool $pin = false, ?float $delay = null): int
    {
        return $this->broadcastCustom(new ActionForward($this, $this->getID($from_peer), $message_ids, $drop_author, $pin), $filter, $delay);
    }

    /**
     * Executes a custom broadcast action with all peers (users, chats, channels) of the bot.
     *
     * Will return an integer ID that can be used to:
     *
     * - Get the current broadcast progress with getBroadcastProgress
     * - Cancel the broadcast using cancelBroadcast
     *
     * Note that to avoid manually polling the progress,
     * MadelineProto will also periodically emit updateBroadcastProgress updates,
     * containing a Progress object for all broadcasts currently in-progress.
     *
     * @param Action $action A custom, serializable Action class that will be called once for every peer.
     * @param float|null $delay Number of seconds to wait between each peer.
     */
    public function broadcastCustom(Action $action, ?Filter $filter = null, ?float $delay = null): int
    {
        // Ensure it can be serialized
        Assert::eq(unserialize(serialize($action))::class, $action::class);

        $id = $this->broadcastId--;
        $this->broadcasts[$id] = new InternalState($id, $this, $action, $filter ?? Filter::default(), $delay);
        return $id;
    }
    /**
     * Get the progress of a currently running broadcast.
     *
     * Will return null if the broadcast doesn't exist, has already completed or was cancelled.
     *
     * Use updateBroadcastProgress updates to get real-time progress status without polling.
     *
     * @param integer $id Broadcast ID
     *
     * @psalm-mutation-free
     */
    public function getBroadcastProgress(int $id): ?Progress
    {
        return ($this->broadcasts[$id] ?? null)?->getProgress();
    }
    /**
     * Cancel a running broadcast.
     *
     * @param integer $id Broadcast ID
     *
     */
    public function cancelBroadcast(int $id): void
    {
        ($this->broadcasts[$id] ?? null)?->cancel();
    }

    /**
     * @internal
     *
     * @psalm-external-mutation-free
     */
    public function cleanupBroadcast(int $id): void
    {
        unset($this->broadcasts[$id]);
    }
}
<?php

declare(strict_types=1);

/**
 * Broadcast module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Broadcast;

use Amp\Cancellation;

/**
 * Interface that represents a broadcast action.
 * @psalm-mutable
 */
interface Action
{
    /**
     * Do something with a single peer.
     *
     * @param integer      $broadcastId  Broadcast ID
     * @param integer      $peer         Broadcast peer
     * @param Cancellation $cancellation Cancellation token
     * @psalm-impure
     */
    public function act(int $broadcastId, int $peer, Cancellation $cancellation): void;
}
<?php

declare(strict_types=1);

/**
 * Broadcast module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Broadcast;

/** @internal */
enum StatusInternal
{
    case IDLING_BEFORE_GATHERING_PEERS;
    case GATHERING_PEERS;
    case IDLING_BEFORE_BROADCASTING;
    case BROADCASTING;
    case FINISHED;
    case CANCELLED;
}
<?php

declare(strict_types=1);

/**
 * Broadcast module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Broadcast\Action;

use Amp\Cancellation;
use danog\MadelineProto\Broadcast\Action;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\PeerNotInDbException;
use danog\MadelineProto\RPCError\ChannelPrivateError;
use danog\MadelineProto\RPCError\ChatWriteForbiddenError;
use danog\MadelineProto\RPCError\InputUserDeactivatedError;
use danog\MadelineProto\RPCError\PeerIdInvalidError;
use danog\MadelineProto\RPCError\UserIsBlockedError;
use danog\MadelineProto\RPCError\UserIsBotError;
use danog\MadelineProto\RPCErrorException;

/** @internal */
final class ActionForward implements Action
{
    /**
     * @psalm-mutation-free
     */
    public function __construct(private readonly MTProto $API, private readonly int $from_peer, private readonly array $ids, private readonly bool $drop_author, private readonly bool $pin)
    {
    }
    #[\Override]
    public function act(int $broadcastId, int $peer, Cancellation $cancellation): void
    {
        try {
            $updates = $this->API->methodCallAsyncRead(
                'messages.forwardMessages',
                [
                    'from_peer' => $this->from_peer,
                    'to_peer' => $peer,
                    'id' => $this->ids,
                    'drop_author' => $this->drop_author,
                    'floodWaitLimit' => 2 * 86400,
                    'cancellation' => $cancellation,
                ],
            );
            if ($this->pin) {
                $updates = $this->API->extractUpdates($updates);
                $id = 0;
                foreach ($updates as $update) {
                    if (\in_array($update['_'], ['updateNewMessage', 'updateNewChannelMessage'], true)) {
                        $id = max($id, $update['message']['id']);
                    }
                }
                try {
                    $this->API->methodCallAsyncRead(
                        'messages.updatePinnedMessage',
                        [
                            'peer' => $peer,
                            'id' => $id,
                            'unpin' => false,
                            'pm_oneside' => false,
                            'floodWaitLimit' => 2 * 86400,
                            'cancellation' => $cancellation,
                        ],
                    );
                } catch (RPCErrorException) {
                }
            }
        } catch (PeerNotInDbException|InputUserDeactivatedError|UserIsBotError|ChatWriteForbiddenError|ChannelPrivateError|UserIsBlockedError|PeerIdInvalidError) {
            return;
        }
    }
}
<?php

declare(strict_types=1);

/**
 * Broadcast module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Broadcast\Action;

use Amp\Cancellation;
use danog\MadelineProto\Broadcast\Action;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\PeerNotInDbException;
use danog\MadelineProto\RPCError\ChannelPrivateError;
use danog\MadelineProto\RPCError\ChatWriteForbiddenError;
use danog\MadelineProto\RPCError\InputUserDeactivatedError;
use danog\MadelineProto\RPCError\PeerIdInvalidError;
use danog\MadelineProto\RPCError\UserIsBlockedError;
use danog\MadelineProto\RPCError\UserIsBotError;
use danog\MadelineProto\RPCErrorException;

/** @internal */
final class ActionSend implements Action
{
    /**
     * @psalm-mutation-free
     */
    public function __construct(private readonly MTProto $API, private readonly array $messages, private readonly bool $pin)
    {
    }
    #[\Override]
    public function act(int $broadcastId, int $peer, Cancellation $cancellation): void
    {
        try {
            foreach ($this->messages as $message) {
                if ($cancellation->isRequested()) {
                    return;
                }
                $id = $this->API->extractMessageId($this->API->methodCallAsyncRead(
                    isset($message['media']) && (
                        \is_string($message['media'])
                        || (
                            isset($message['media']['_']) &&
                            $message['media']['_'] !== 'messageMediaWebPage'
                        )
                    )
                        ? 'messages.sendMedia'
                        : 'messages.sendMessage',
                    array_merge($message, ['peer' => $peer, 'floodWaitLimit' => 2*86400, 'cancellation' => $cancellation]),
                ));
            }
            if ($this->pin) {
                try {
                    $this->API->methodCallAsyncRead(
                        'messages.updatePinnedMessage',
                        ['peer' => $peer, 'id' => $id, 'unpin' => false, 'pm_oneside' => false, 'floodWaitLimit' => 2*86400, 'cancellation' => $cancellation],
                    );
                } catch (RPCErrorException) {
                }
            }
        } catch (PeerNotInDbException|InputUserDeactivatedError|UserIsBotError|ChatWriteForbiddenError|ChannelPrivateError|UserIsBlockedError|PeerIdInvalidError) {
            return;
        }
    }
}
<?php

declare(strict_types=1);

/**
 * Broadcast module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Broadcast;

use Amp\CancelledException;
use Amp\DeferredCancellation;
use danog\MadelineProto\Logger;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\RestoresDeclaredProperties;
use Revolt\EventLoop;
use Throwable;
use Webmozart\Assert\Assert;

use function Amp\async;
use function Amp\delay;
use function Amp\Future\await;

/**
 * @internal
 */
final class InternalState
{
    use RestoresDeclaredProperties;
    /** @var array<int> */
    private array $peers = [];
    private int $totalCount = 0;
    private int $successCount = 0;
    private int $failCount = 0;
    private StatusInternal $status = StatusInternal::IDLING_BEFORE_GATHERING_PEERS;
    private DeferredCancellation $cancellation;
    private bool $cancelled = false;

    public function __construct(
        private int $broadcastId,
        private MTProto $API,
        private Action $action,
        private Filter $filter,
        private readonly ?float $delay = null,
    ) {
        if ($this->delay !== null) {
            Assert::greaterThanEq($this->delay, 0, 'Delay must be greater than or equal to zero');
        }
        $this->cancellation = new DeferredCancellation;
        $this->resume();
    }
    /**
     * @psalm-mutation-free
     */
    public function __serialize(): array
    {
        $vars = get_object_vars($this);
        $vars['status'] = match ($vars['status']) {
            StatusInternal::GATHERING_PEERS => StatusInternal::IDLING_BEFORE_GATHERING_PEERS,
            StatusInternal::BROADCASTING => StatusInternal::IDLING_BEFORE_BROADCASTING,
            default => $vars['status']
        };
        unset($vars['cancellation']);
        return $vars;
    }
    public function __unserialize(array $data): void
    {
        $this->restoreDeclaredProperties($data);
        $this->cancellation = new DeferredCancellation;
        if ($this->cancelled) {
            $this->cancel();
        }
    }
    public function cancel(): void
    {
        $this->cancelled = true;
        $this->cancellation->cancel(new BroadcastCancelledException());
    }
    public function resume(): void
    {
        if ($this->status === StatusInternal::IDLING_BEFORE_GATHERING_PEERS) {
            $this->gatherPeers();
        } elseif ($this->status === StatusInternal::IDLING_BEFORE_BROADCASTING) {
            $this->startBroadcast();
        } elseif ($this->status === StatusInternal::FINISHED || $this->status === StatusInternal::CANCELLED) {
            $this->API->cleanupBroadcast($this->broadcastId);
        }
    }

    private bool $notifiedFinal = false;
    private function setStatus(StatusInternal $status): void
    {
        $this->status = $status;
        if ($status === StatusInternal::CANCELLED || $status === StatusInternal::FINISHED) {
            $this->peers = [];
            $this->API->cleanupBroadcast($this->broadcastId);

            if ($this->notifiedFinal) {
                return;
            }
            $this->notifiedFinal = true;
        }
        if ($status !== StatusInternal::IDLING_BEFORE_BROADCASTING && $status !== StatusInternal::IDLING_BEFORE_GATHERING_PEERS) {
            $this->notifyProgress();
        }
    }
    private function notifyProgress(): void
    {
        EventLoop::queue($this->API->saveUpdate(...), ['_' => 'updateBroadcastProgress', 'progress' => $this->getProgress()]);
    }
    private function gatherPeers(): void
    {
        if ($this->cancellation->isCancelled()) {
            $this->setStatus(StatusInternal::CANCELLED);
            return;
        }
        Assert::eq($this->status, StatusInternal::IDLING_BEFORE_GATHERING_PEERS);
        $this->setStatus(StatusInternal::GATHERING_PEERS);
        async(function (): void {
            $peers = $this->API->getDialogIds();
            if (\is_array($this->filter->whitelist)) {
                foreach ($this->filter->whitelist as $id) {
                    try {
                        $this->API->getInfo($id);
                    } catch (Throwable $e) {
                        $this->API->logger("An error occurred while getting info about whitelisted peer $id: $e", Logger::ERROR);
                    }
                }
            }
            $peers = array_filter($peers, function (int $peer): bool {
                if (\in_array($peer, $this->filter->blacklist, true)) {
                    return false;
                }
                if (\is_array($this->filter->whitelist) && !\in_array($peer, $this->filter->whitelist, true)) {
                    return false;
                }
                try {
                    if (!match ($this->API->getType($peer)) {
                        'user' => $this->filter->allowUsers,
                        'bot' => $this->filter->allowBots,
                        'chat' => $this->filter->allowGroups,
                        'supergroup' => $this->filter->allowGroups,
                        'channel' => $this->filter->allowChannels,
                        'community' => false,
                    }) {
                        return false;
                    }
                } catch (Throwable) {
                }
                return true;
            });
            $this->peers = $peers;
            $this->totalCount = \count($peers);
            $this->setStatus(StatusInternal::IDLING_BEFORE_BROADCASTING);
            $this->startBroadcast();
        });
    }
    private function startBroadcast(): void
    {
        if ($this->cancellation->isCancelled()) {
            $this->setStatus(StatusInternal::CANCELLED);
            return;
        }
        Assert::eq($this->status, StatusInternal::IDLING_BEFORE_BROADCASTING);
        if (!$this->peers) {
            $this->setStatus(StatusInternal::FINISHED);
            return;
        }
        $this->setStatus(StatusInternal::BROADCASTING);
        $cancellation = $this->cancellation->getCancellation();
        $promises = [];
        foreach ($this->peers as $key => $peer) {
            $promises []= async(function () use ($key, $peer, $cancellation): void {
                if ($this->cancellation->isCancelled()) {
                    $this->setStatus(StatusInternal::CANCELLED);
                    return;
                }

                $e = null;
                try {
                    if ($this->delay !== null) {
                        delay($this->delay);
                    }
                    $this->action->act($this->broadcastId, $peer, $cancellation);
                    $this->successCount++;
                } catch (Throwable $e) {
                    $this->failCount++;
                    if ($e instanceof CancelledException && $e->getPrevious() instanceof BroadcastCancelledException) {
                        // The finally block will still be executed
                        return;
                    }
                    $this->API->logger("An error occurred while broadcasting: $e", Logger::ERROR);
                } finally {
                    if ($this->cancellation->isCancelled()) {
                        $this->setStatus(StatusInternal::CANCELLED);
                        return;
                    }

                    unset($this->peers[$key]);
                    if (!$this->peers) {
                        $this->setStatus(StatusInternal::FINISHED);
                    } else {
                        $this->notifyProgress();
                    }
                }
            });
            if (\count($promises) % 50 === 0) {
                await($promises);
                $promises = [];
            }
        }
    }

    /**
     * @psalm-mutation-free
     */
    public function getStatus(): Status
    {
        return match ($this->status) {
            StatusInternal::IDLING_BEFORE_GATHERING_PEERS => Status::GATHERING_PEERS,
            StatusInternal::GATHERING_PEERS => Status::GATHERING_PEERS,
            StatusInternal::IDLING_BEFORE_BROADCASTING => Status::BROADCASTING,
            StatusInternal::BROADCASTING => Status::BROADCASTING,
            StatusInternal::FINISHED => Status::FINISHED,
            StatusInternal::CANCELLED => Status::CANCELLED,
        };
    }
    /**
     * @psalm-mutation-free
     */
    public function getProgress(): Progress
    {
        return new Progress(
            $this->API,
            $this->broadcastId,
            $this->getStatus(),
            \count($this->peers),
            $this->successCount,
            $this->failCount
        );
    }
}
<?php

declare(strict_types=1);

/**
 * Broadcast filter.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Broadcast;

/**
 * Broadcast filter.
 *
 * @psalm-immutable
 */
final class Filter
{
    /**
     * @psalm-mutation-free
     */
    public function __construct(
        public readonly bool $allowUsers,
        public readonly bool $allowBots,
        public readonly bool $allowGroups,
        public readonly bool $allowChannels,
        /** @var list<int> */
        public readonly array $blacklist = [],
        /** @var list<int>|null If null all IDs are allowed (*) */
        public readonly ?array $whitelist = null,
    ) {
    }
    /**
     * @psalm-pure
     */
    public static function default(): self
    {
        return new self(true, true, true, true);
    }
}
<?php

declare(strict_types=1);

/**
 * Broadcast module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Broadcast;

/**
 * Broadcast status.
 */
enum Status: string
{
    case GATHERING_PEERS = 'gathering peer information';
    case BROADCASTING = 'broadcasting';
    case FINISHED = 'finished';
    case CANCELLED = 'cancelled';
}
<?php

declare(strict_types=1);

/**
 * Broadcast module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Broadcast;

use Exception;

/** @internal */
final class BroadcastCancelledException extends Exception
{
    /**
     * @psalm-mutation-free
     */
    public function __construct()
    {
        parent::__construct();
    }
}
<?php

declare(strict_types=1);

/**
 * Broadcast module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Broadcast;

use danog\MadelineProto\EventHandler\Update;
use danog\MadelineProto\MTProto;
use JsonSerializable;

/**
 * Broadcast progress.
 */
final class Progress extends Update implements JsonSerializable
{
    /**
     * Completion percentage.
     */
    public readonly int $percent;
    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(
        MTProto $API,
        /** Broadcast ID */
        public readonly int $broadcastId,
        /** Broadcast status */
        public readonly Status $status,
        /** Pending number of peers */
        public readonly int $pendingCount,
        /** Successful number of peers */
        public readonly int $successCount,
        /** Failed number of peers */
        public readonly int $failCount,
    ) {
        parent::__construct($API);
        $this->percent = $status === Status::FINISHED
            ? 100
            : ($pendingCount ? (int) (($successCount+$failCount)*100/($successCount+$failCount+$pendingCount)) : 0);
    }
    /**
     * @psalm-mutation-free
     */
    public function __toString(): string
    {
        return "Progress for {$this->broadcastId}: {$this->percent}%, status {$this->status->value}, sent to {$this->successCount} peers, failed sending to {$this->failCount} peers, {$this->pendingCount} peers left.";
    }
}
<?php

declare(strict_types=1);

/**
 * Tools module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use Amp\ByteStream\Pipe;
use Amp\ByteStream\ReadableBuffer;
use Amp\ByteStream\ReadableStream;
use Amp\ByteStream\WritableBuffer;
use Amp\Cancellation;
use Amp\File\File;
use Amp\Http\Client\HttpClient;
use Amp\Http\Client\HttpClientBuilder;
use Amp\Http\Client\Request;
use Amp\Process\Process;
use ArrayAccess;
use Closure;
use Countable;
use danog\DialogId\DialogId;
use Fiber;
use PhpParser\Node\Arg;
use PhpParser\Node\DeclareItem;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\Include_;
use PhpParser\Node\Expr\New_;
use PhpParser\Node\Expr\Yield_;
use PhpParser\Node\Expr\YieldFrom;
use PhpParser\Node\FunctionLike;
use PhpParser\Node\Name;
use PhpParser\Node\Scalar\LNumber;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\NodeFinder;
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitor\NameResolver;
use PhpParser\NodeVisitor\ParentConnectingVisitor;
use PhpParser\ParserFactory;
use ReflectionClass;
use Traversable;

use Webmozart\Assert\Assert;

use const DIRECTORY_SEPARATOR;
use const PHP_INT_MAX;
use const PHP_SAPI;

use const STR_PAD_RIGHT;
use function Amp\File\openFile;
use function unpack;

/**
 * Some tools.
 */
abstract class Tools extends AsyncTools
{
    /**
     * Test fibers.
     *
     * @return array{maxFibers: int, realMemoryMb: int, maps: ?int, maxMaps: ?int}
     */
    public static function testFibers(int $fiberCount = 100000): array
    {
        ini_set('memory_limit', -1);

        $f = [];
        for ($x = 0; $x < $fiberCount; $x++) {
            try {
                $f []= $cur = new Fiber(static function (): void {
                    Fiber::suspend();
                });
                $cur->start();
            } catch (\Throwable $e) {
                break;
            }
        }
        return [
            'maxFibers' => $x,
            'realMemoryMb' => (int) (memory_get_usage(true)/1024/1024),
            'maps' => self::getMaps(),
            'maxMaps' => self::getMaxMaps(),
        ];
    }
    /**
     * Get current number of memory-mapped regions, UNIX only.
     */
    public static function getMaps(): ?int
    {
        try {
            if (file_exists('/proc/self/maps')) {
                return substr_count(file_get_contents('/proc/self/maps'), "\n")-1;
            }
            $pid = getmypid();
            if (file_exists("/proc/$pid/maps")) {
                return substr_count(file_get_contents("/proc/$pid/maps"), "\n")-1;
            }
        } catch (\Throwable) {
        }
        return null;
    }
    /**
     * Get maximum number of memory-mapped regions, UNIX only.
     * Use testFibers to get the maximum number of fibers on any platform.
     */
    public static function getMaxMaps(): ?int
    {
        try {
            if (file_exists('/proc/sys/vm/max_map_count')) {
                return ((int) file_get_contents('/proc/sys/vm/max_map_count')) ?: null;
            }
        } catch (\Throwable) {
        }
        return null;
    }
    /**
     * Converts a string into an async amphp stream.
     */
    public static function stringToStream(string $str): ReadableBuffer
    {
        return new ReadableBuffer($str);
    }
    /**
     * Sanify TL obtained from JSON for TL serialization.
     *
     * @param array $input Data to sanitize
     * @internal
     */
    public static function convertJsonTL(array $input): array
    {
        $cb = static function (&$val) use (&$cb): void {
            if (isset($val['@type'])) {
                $val['_'] = $val['@type'];
            } elseif (\is_array($val)) {
                array_walk($val, $cb);
            }
        };
        array_walk($input, $cb);
        return $input;
    }
    /**
     * @psalm-pure
     */
    private static function uRShift(int $a, int $b): int
    {
        if ($b == 0) {
            return $a;
        }
        return ($a >> $b) & ~(1<<(8*PHP_INT_SIZE-1)>>($b-1));
    }
    /**
     * Generate MTProto vector hash.
     *
     * Returns a vector hash.
     *
     * @param array<string|int> $longs IDs
     *
     * @psalm-external-mutation-free
     */
    public static function genVectorHash(array $longs): string
    {
        $hash = 0;
        foreach ($longs as $long) {
            if (\is_string($long)) {
                $long = self::unpackSignedLong(strrev(substr(md5($long, true), 0, 8)));
            }
            $hash ^= self::uRShift($hash, 21);
            $hash ^= $hash << 35;
            $hash ^= self::uRShift($hash, 4);
            $hash = $hash + $long;
        }
        return self::packSignedLong($hash);
    }
    /**
     * Get random integer.
     *
     * @param integer $modulus Modulus
     */
    public static function randomInt(int $modulus = 0): int
    {
        if ($modulus === 0) {
            return random_int(PHP_INT_MIN, PHP_INT_MAX);
        }
        return random_int(0, PHP_INT_MAX) % $modulus;
    }
    /**
     * Get secure random string of specified length.
     *
     * @param integer $length Length
     */
    public static function random(int $length): string
    {
        return $length === 0 ? '' : random_bytes($length);
    }
    /**
     * Positive modulo
     * Works just like the % (modulus) operator, only returns always a postive number.
     *
     * @param int $a A
     * @param int $b B
     *
     * @psalm-pure
     */
    public static function posmod(int $a, int $b): int
    {
        $resto = $a % $b;
        return $resto < 0 ? $resto + abs($b) : $resto;
    }
    /**
     * Unpack base256 signed int.
     *
     * @param string $value base256 int
     *
     * @psalm-external-mutation-free
     */
    public static function unpackSignedInt(string $value): int
    {
        if (\strlen($value) !== 4) {
            throw new TL\Exception("Length is not 4");
        }
        return unpack('l', Magic::$BIG_ENDIAN ? strrev($value) : $value)[1];
    }
    /**
     * Unpack base256 signed long.
     *
     * @param string $value base256 long
     *
     * @psalm-external-mutation-free
     */
    public static function unpackSignedLong(string $value): int
    {
        if (\strlen($value) !== 8) {
            throw new TL\Exception("Length is not 8");
        }
        return unpack('q', Magic::$BIG_ENDIAN ? strrev($value) : $value)[1];
    }
    /**
     * Unpack base256 signed long to string.
     *
     * @param string|int|array $value base256 long
     *
     * @psalm-external-mutation-free
     */
    public static function unpackSignedLongString(string|int|array $value): string
    {
        if (\is_int($value)) {
            return (string) $value;
        }
        if (\is_array($value) && \count($value) === 2) {
            $value = pack('l2', $value);
        }
        if (\strlen($value) !== 8) {
            throw new TL\Exception("Length is not 8");
        }
        return (string) self::unpackSignedLong($value);
    }
    /**
     * Convert integer to base256 signed int.
     *
     * @param integer $value Value to convert
     *
     * @psalm-external-mutation-free
     */
    public static function packSignedInt(int $value): string
    {
        if ($value > 2147483647) {
            throw new TL\Exception(\sprintf(Lang::$current_lang['value_bigger_than_2147483647'], $value));
        }
        if ($value < -2147483648) {
            throw new TL\Exception(\sprintf(Lang::$current_lang['value_smaller_than_2147483648'], $value));
        }
        $res = pack('l', $value);
        return Magic::$BIG_ENDIAN ? strrev($res) : $res;
    }
    /**
     * Convert integer to base256 long.
     *
     * @param int $value Value to convert
     *
     * @psalm-external-mutation-free
     */
    public static function packSignedLong(int $value): string
    {
        return Magic::$BIG_ENDIAN ? strrev(pack('q', $value)) : pack('q', $value);
    }
    /**
     * Convert value to unsigned base256 int.
     *
     * @param int $value Value
     *
     * @psalm-external-mutation-free
     */
    public static function packUnsignedInt(int $value): string
    {
        if ($value > 4294967295) {
            throw new TL\Exception(\sprintf(Lang::$current_lang['value_bigger_than_4294967296'], $value));
        }
        if ($value < 0) {
            throw new TL\Exception(\sprintf(Lang::$current_lang['value_smaller_than_0'], $value));
        }
        return pack('V', $value);
    }
    /**
     * Convert double to binary version.
     *
     * @param float $value Value to convert
     *
     * @psalm-external-mutation-free
     */
    public static function packDouble(float $value): string
    {
        $res = pack('d', $value);
        if (\strlen($res) !== 8) {
            throw new TL\Exception(Lang::$current_lang['encode_double_error']);
        }
        return Magic::$BIG_ENDIAN ? strrev($res) : $res;
    }
    /**
     * Unpack binary double.
     *
     * @param string $value Value to unpack
     *
     * @psalm-external-mutation-free
     */
    public static function unpackDouble(string $value): float
    {
        if (\strlen($value) !== 8) {
            throw new TL\Exception("Length is not 8");
        }
        return unpack('d', Magic::$BIG_ENDIAN ? strrev($value) : $value)[1];
    }
    /**
     * Check if is array or similar (traversable && countable && arrayAccess).
     *
     * @param mixed $var Value to check
     *
     * @psalm-pure
     */
    public static function isArrayOrAlike(mixed $var): bool
    {
        return \is_array($var) || $var instanceof ArrayAccess && $var instanceof Traversable && $var instanceof Countable;
    }
    /**
     * Create array.
     *
     * @param mixed ...$params Params
     *
     * @psalm-pure
     */
    public static function arr(mixed ...$params): array
    {
        return $params;
    }
    /**
     * base64URL decode.
     *
     * @param string $data Data to decode
     *
     * @psalm-pure
     */
    public static function base64urlDecode(string $data): string
    {
        return base64_decode(str_pad(strtr($data, '-_', '+/'), \strlen($data) % 4, '=', STR_PAD_RIGHT), true);
    }
    /**
     * Base64URL encode.
     *
     * @param string $data Data to encode
     *
     * @psalm-pure
     */
    public static function base64urlEncode(string $data): string
    {
        return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
    }
    /**
     * null-byte RLE decode.
     *
     * @param string $string Data to decode
     *
     * @psalm-pure
     */
    public static function rleDecode(string $string): string
    {
        $new = '';
        $last = '';
        $null = \chr(0);
        foreach (str_split($string) as $cur) {
            if ($last === $null) {
                $new .= str_repeat($last, \ord($cur));
                $last = '';
            } else {
                $new .= $last;
                $last = $cur;
            }
        }
        $string = $new.$last;
        return $string;
    }
    /**
     * null-byte RLE encode.
     *
     * @param string $string Data to encode
     *
     * @psalm-pure
     */
    public static function rleEncode(string $string): string
    {
        $new = '';
        $count = 0;
        $null = \chr(0);
        foreach (str_split($string) as $cur) {
            if ($cur === $null) {
                $count++;
            } else {
                if ($count > 0) {
                    $new .= $null.\chr($count);
                    $count = 0;
                }
                $new .= $cur;
            }
        }
        return $new;
    }
    private const INFLATE_HEADER = "\xff\xd8\xff\xe0\x00\x10\x4a\x46\x49".
        "\x46\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xff\xdb\x00\x43\x00\x28\x1c".
        "\x1e\x23\x1e\x19\x28\x23\x21\x23\x2d\x2b\x28\x30\x3c\x64\x41\x3c\x37\x37".
        "\x3c\x7b\x58\x5d\x49\x64\x91\x80\x99\x96\x8f\x80\x8c\x8a\xa0\xb4\xe6\xc3".
        "\xa0\xaa\xda\xad\x8a\x8c\xc8\xff\xcb\xda\xee\xf5\xff\xff\xff\x9b\xc1\xff".
        "\xff\xff\xfa\xff\xe6\xfd\xff\xf8\xff\xdb\x00\x43\x01\x2b\x2d\x2d\x3c\x35".
        "\x3c\x76\x41\x41\x76\xf8\xa5\x8c\xa5\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8".
        "\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8".
        "\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8".
        "\xf8\xf8\xf8\xf8\xf8\xff\xc0\x00\x11\x08\x00\x00\x00\x00\x03\x01\x22\x00".
        "\x02\x11\x01\x03\x11\x01\xff\xc4\x00\x1f\x00\x00\x01\x05\x01\x01\x01\x01".
        "\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08".
        "\x09\x0a\x0b\xff\xc4\x00\xb5\x10\x00\x02\x01\x03\x03\x02\x04\x03\x05\x05".
        "\x04\x04\x00\x00\x01\x7d\x01\x02\x03\x00\x04\x11\x05\x12\x21\x31\x41\x06".
        "\x13\x51\x61\x07\x22\x71\x14\x32\x81\x91\xa1\x08\x23\x42\xb1\xc1\x15\x52".
        "\xd1\xf0\x24\x33\x62\x72\x82\x09\x0a\x16\x17\x18\x19\x1a\x25\x26\x27\x28".
        "\x29\x2a\x34\x35\x36\x37\x38\x39\x3a\x43\x44\x45\x46\x47\x48\x49\x4a\x53".
        "\x54\x55\x56\x57\x58\x59\x5a\x63\x64\x65\x66\x67\x68\x69\x6a\x73\x74\x75".
        "\x76\x77\x78\x79\x7a\x83\x84\x85\x86\x87\x88\x89\x8a\x92\x93\x94\x95\x96".
        "\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4\xb5\xb6".
        "\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xd2\xd3\xd4\xd5\xd6".
        "\xd7\xd8\xd9\xda\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf1\xf2\xf3\xf4".
        "\xf5\xf6\xf7\xf8\xf9\xfa\xff\xc4\x00\x1f\x01\x00\x03\x01\x01\x01\x01\x01".
        "\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08".
        "\x09\x0a\x0b\xff\xc4\x00\xb5\x11\x00\x02\x01\x02\x04\x04\x03\x04\x07\x05".
        "\x04\x04\x00\x01\x02\x77\x00\x01\x02\x03\x11\x04\x05\x21\x31\x06\x12\x41".
        "\x51\x07\x61\x71\x13\x22\x32\x81\x08\x14\x42\x91\xa1\xb1\xc1\x09\x23\x33".
        "\x52\xf0\x15\x62\x72\xd1\x0a\x16\x24\x34\xe1\x25\xf1\x17\x18\x19\x1a\x26".
        "\x27\x28\x29\x2a\x35\x36\x37\x38\x39\x3a\x43\x44\x45\x46\x47\x48\x49\x4a".
        "\x53\x54\x55\x56\x57\x58\x59\x5a\x63\x64\x65\x66\x67\x68\x69\x6a\x73\x74".
        "\x75\x76\x77\x78\x79\x7a\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x92\x93\x94".
        "\x95\x96\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4".
        "\xb5\xb6\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xd2\xd3\xd4".
        "\xd5\xd6\xd7\xd8\xd9\xda\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf2\xf3\xf4".
        "\xf5\xf6\xf7\xf8\xf9\xfa\xff\xda\x00\x0c\x03\x01\x00\x02\x11\x03\x11\x00".
        "\x3f\x00";
    private const INFLATE_FOOTER = "\xff\xd9";
    /**
     * Inflate stripped photosize to full JPG payload.
     *
     * @param string $stripped Stripped photosize
     *
     * @psalm-pure
     */
    public static function inflateStripped(string $stripped): string
    {
        if (\strlen($stripped) < 3 || \ord($stripped[0]) !== 1) {
            return $stripped;
        }
        $header = self::INFLATE_HEADER;
        $header[164] = $stripped[1];
        $header[166] = $stripped[2];
        return $header.substr($stripped, 3).self::INFLATE_FOOTER;
    }
    /**
     * Close connection with client, connected via web.
     *
     * @param string $message Message
     */
    public static function closeConnection(string $message): void
    {
        if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg' || isset($GLOBALS['exited']) || isset($_GET['MadelineSelfRestart']) || Magic::$isIpcWorker) {
            return;
        }
        try {
            $buffer = ob_get_clean() ?: '';
        } catch (\Throwable) {
            // No output buffer
            $buffer = '';
        }
        $buffer .= $message;
        ignore_user_abort(true);
        // Output may already have been sent (an echo in onStart, a displayed warning): the request must end anyway,
        // or the client that started a web bot waits on it for as long as the bot runs.
        if (!headers_sent()) {
            header('Connection: close');
            header('Content-Type: text/html');
        }
        echo $buffer;
        flush();
        $GLOBALS['exited'] = true;
        if (\function_exists('fastcgi_finish_request')) {
            fastcgi_finish_request();
        }
    }
    /**
     * Get maximum photo size.
     *
     * @param array<array{w: int, h: int, type: string, ...}> $sizes
     *
     * @internal
     *
     * @psalm-pure
     */
    public static function maxSize(array $sizes): array
    {
        $maxPixels = 0;
        $max = null;
        foreach ($sizes as $size) {
            if (isset($size['w'], $size['h'])) {
                $curPixels = $size['w'] * $size['h'];
                if ($curPixels > $maxPixels) {
                    $maxPixels = $curPixels;
                    $max = $size;
                }
            }
        }
        if (!$max) {
            $maxType = 0;
            foreach ($sizes as $size) {
                $curType = \ord($size['type']);
                if ($curType > $maxType) {
                    $maxType = $curType;
                    $max = $size;
                }
            }
        }
        \assert($max !== null);
        return $max;
    }
    /**
     * Get final element of array.
     *
     * @template T
     *
     * @param array<T> $what Array
     *
     * @return T
     *
     * @psalm-pure
     */
    public static function end(array $what): mixed
    {
        return end($what);
    }
    /**
     * Whether this is altervista.
     *
     * @psalm-external-mutation-free
     */
    public static function isAltervista(): bool
    {
        return Magic::$altervista;
    }
    /**
     * Accesses a private variable from an object.
     *
     * @internal
     *
     * @param object $obj Object
     * @param string $var Attribute name
     * @psalm-suppress InvalidScope
     * @access public
     */
    public static function &getVar(object $obj, string $var)
    {
        return Closure::bind(
            fn &() => $this->{$var},
            $obj,
            $obj::class,
        )->__invoke();
    }
    /**
     * Sets a private variable in an object.
     *
     * @internal
     *
     * @param object $obj Object
     * @param string $var Attribute name
     * @param mixed  $val Attribute value
     * @psalm-suppress InvalidScope
     * @access public
     */
    public static function setVar(object $obj, string $var, mixed &$val): void
    {
        Closure::bind(
            function () use ($var, &$val): void {
                $this->{$var} =& $val;
            },
            $obj,
            $obj::class,
        )->__invoke();
    }
    /**
     * Get absolute path to file, related to session path.
     *
     * @param string $file File
     * @internal
     */
    public static function absolute(string $file): string
    {
        if (($file[0] ?? '') !== '/' && ($file[1] ?? '') !== ':' && !\in_array(substr($file, 0, 4), ['phar', 'http'], true)) {
            $file = Magic::getcwd().DIRECTORY_SEPARATOR.$file;
        }
        return $file;
    }
    /**
     * Parse t.me link.
     *
     * @internal
     *
     * @return array{0: bool, 1: string|int}|null
     *
     * @psalm-pure
     */
    public static function parseLink(string $link): array|null
    {
        if (preg_match('@([a-z0-9_-]*)\\.(?:t|telegram)\.(?:me|dog)@', $link, $matches)) {
            if ($matches[1] !== 'www') {
                return [false, $matches[1]];
            }
        }
        // t.me/c/<channelId>
        if (preg_match('@t\.me/c/(\d+)@', $link, $matches)) {
            return [false, DialogId::fromSupergroupOrChannelId((int) $matches[1])];
        }
        // Invite links
        if (preg_match('@(?:t|telegram)\\.(?:me|dog)/(joinchat/|\+)?([a-z0-9_-]*)@i', $link, $matches)) {
            return [!!$matches[1], $matches[2]];
        }
        // Deep Link
        if (preg_match('@tg://(?:resolve|openmessage|user)\?(?:domain|userid|id)=([a-z0-9_-]+)@i', $link, $matches)) {
            return [false, (int) $matches[1]];
        }
        return null;
    }

    /**
     * Opens a file in append-only mode.
     *
     * @param string $path File path.
     */
    public static function openFileAppendOnly(string $path): File
    {
        return openFile($path, "a");
    }

    /**
     * Obtains a pipe that can be used to upload a file from a stream.
     *
     */
    public static function getStreamPipe(): Pipe
    {
        return new Pipe(512*1024);
    }
    private static ?HttpClient $client = null;
    /**
     * Provide a buffered reader for a file, URL or amp stream, optionally starting at a byte offset.
     *
     * The `$offset` makes a demuxer resumable across a serialize/unserialize cycle: a `LocalFile` is
     * seeked and a `RemoteUrl` is fetched with a `Range` request. A {@see ResumableStream} is skipped
     * forward from its current position (without downloading the skipped part, for a
     * {@see ResumableDownloadStream}). Any other {@see ReadableStream} cannot be seeked, so a non-zero
     * offset on one is rejected.
     *
     * @return Closure(int): ?string
     */
    public static function openBuffered(LocalFile|RemoteUrl|ReadableStream $stream, ?Cancellation $cancellation = null, int $offset = 0): Closure
    {
        $buffer = '';
        if ($stream instanceof LocalFile) {
            $file = openFile($stream->file, 'r');
            if ($offset !== 0) {
                $file->seek($offset);
            }
            $read = static fn (): ?string => $file->read(cancellation: $cancellation, length: self::READ_AHEAD_LENGTH);
        } else {
            if ($stream instanceof RemoteUrl) {
                self::$client ??= HttpClientBuilder::buildDefault();
                $request = new Request($stream->url);
                $request->setTransferTimeout(INF);
                $request->setInactivityTimeout(INF);
                if ($offset !== 0) {
                    $request->setHeader('Range', "bytes={$offset}-");
                }
                $stream = self::$client->request(
                    $request,
                    $cancellation
                )->getBody();
            } elseif ($offset !== 0) {
                if ($stream instanceof ResumableDownloadStream) {
                    $stream->skip($offset);
                } elseif ($stream instanceof ResumableStream) {
                    $buffer = self::skipStream($stream, $offset, $cancellation) ?? '';
                } else {
                    throw new Exception('Cannot resume a raw stream from a byte offset: it is not seekable.');
                }
            }
            $read = static function () use ($stream, $cancellation): ?string {
                $chunk = $stream->read($cancellation);
                if ($chunk === null) {
                    $stream->close();
                }
                return $chunk;
            };
        }
        // Demuxers read a few bytes at a time: read ahead, and hand out the buffer from a position
        // instead of cutting it (which would copy it) on every read.
        $position = 0;
        $eof = false;
        return static function (int $len) use (&$buffer, &$position, &$eof, $read): ?string {
            while (!$eof && \strlen($buffer) - $position < $len) {
                $chunk = $read();
                if ($chunk === null) {
                    $eof = true;
                    break;
                }
                if ($position > self::READ_AHEAD_LENGTH) {
                    $buffer = substr($buffer, $position);
                    $position = 0;
                }
                $buffer .= $chunk;
            }
            if ($position >= \strlen($buffer)) {
                return null;
            }
            // Fewer bytes than requested only at the end of the stream.
            $piece = substr($buffer, $position, $len);
            $position += \strlen($piece);
            return $piece;
        };
    }

    /** How much is read ahead by {@see self::openBuffered()}, in bytes. */
    private const READ_AHEAD_LENGTH = 128 * 1024;

    /**
     * Read and discard the next bytes of a stream.
     *
     * @return ?string What was read past the skipped bytes, null if the stream ended first.
     */
    private static function skipStream(ReadableStream $stream, int $bytes, ?Cancellation $cancellation): ?string
    {
        while (($chunk = $stream->read($cancellation)) !== null) {
            $len = \strlen($chunk);
            if ($len >= $bytes) {
                return substr($chunk, $bytes);
            }
            $bytes -= $len;
        }
        return null;
    }

    private const BLOCKING_FUNCTIONS = [
        'file_get_contents' => 'https://github.com/amphp/file, https://github.com/amphp/http-client or $this->fileGetContents()',
        'file_put_contents' => 'https://github.com/amphp/file',
        'curl_exec' => 'https://github.com/amphp/http-client',
        'mysqli_query' => 'https://github.com/amphp/mysql',
        'mysqli_connect' => 'https://github.com/amphp/mysql',
        'mysql_connect' => 'https://github.com/amphp/mysql',
        'fopen' => 'https://github.com/amphp/file',
        'fsockopen' => 'https://github.com/amphp/socket',
        'pcntl_fork' => 'Tools::callFork',
        'sleep' => '$this->sleep()',
        'usleep' => '$this->sleep()',
        'proc_open' => 'https://github.com/amphp/process',
        'shell_exec' => 'https://github.com/amphp/process',
        'exec' => 'https://github.com/amphp/process',
    ];
    private const BLOCKING_CLASSES = [
        'pdo' => 'https://github.com/amphp/mysql',
        'mysqli' => 'https://github.com/amphp/mysql',
    ];

    private const DEPRECATED_FUNCTIONS = [
        'amp\\file\\get' => 'Amp\\File\\read',
        'amp\\file\\put' => 'Amp\\File\\write',
    ];

    private const BANNED_FILE_FUNCTIONS = [
        'amp\\file\\read',
        'amp\\file\\write',
        'amp\\file\\openFile',
    ];
    private const NO_YIELD_FUNCTIONS = [
        'onstart',
        'onupdatenewmessage',
        'onupdatenewchannelmessage',
    ];
    /**
     * Perform static analysis on a certain event handler class, to make sure it satisfies some performance requirements.
     *
     * @param class-string<EventHandler> $class Class name
     *
     * @return list<EventHandlerIssue>
     */
    public static function validateEventHandlerClass(string $class): array
    {
        if (!\extension_loaded('tokenizer')) {
            throw \danog\MadelineProto\Exception::extension('tokenizer');
        }
        $plugin = is_subclass_of($class, PluginEventHandler::class);
        $file = (new ReflectionClass($class))->getFileName();
        $code = file_get_contents($file);
        $code = (new ParserFactory)->createForNewestSupportedVersion()->parse($code);
        Assert::notNull($code);
        $traverser = new NodeTraverser;
        $traverser->addVisitor(new NameResolver());
        $traverser->addVisitor(new ParentConnectingVisitor);
        $code = $traverser->traverse($code);
        $finder = new NodeFinder;

        $issues = [];

        if ($plugin) {
            $class = $finder->findInstanceOf($code, ClassLike::class);
            $class = array_filter($class, static fn (ClassLike $c): bool => $c->name !== null);
            if (\count($class) !== 1 || !$class[0] instanceof Class_) {
                $issues []= new EventHandlerIssue(
                    message: Lang::$current_lang['plugins_must_have_exactly_one_class'],
                    file: $file,
                    line: 0,
                    severe: true
                );
            }
        }

        /** @var DeclareItem|null $declare */
        $declare = $finder->findFirstInstanceOf($code, DeclareItem::class);
        if ($declare === null
            || $declare->key->name !== 'strict_types'
            || !$declare->value instanceof LNumber
            || $declare->value->value !== 1
        ) {
            $issues []= new EventHandlerIssue(
                message: Lang::$current_lang['must_have_declare_types'],
                file: $file,
                line: 0,
                severe: true
            );
        }

        /** @var FuncCall $call */
        foreach ($finder->findInstanceOf($code, FuncCall::class) as $call) {
            if (!$call->name instanceof Name) {
                continue;
            }

            $name = $call->name->toLowerString();
            if (isset(self::BLOCKING_FUNCTIONS[$name])) {
                if ($name === 'fopen' &&
                    isset($call->args[0]) &&
                    $call->args[0] instanceof Arg &&
                    $call->args[0]->value instanceof String_ &&
                    str_starts_with($call->args[0]->value->value, 'php://memory')
                ) {
                    continue;
                }
                $explanation = self::BLOCKING_FUNCTIONS[$name];
                $issues []= new EventHandlerIssue(
                    message: \sprintf(Lang::$current_lang['do_not_use_blocking_function'], $name, $explanation),
                    file: $file,
                    line: $call->getStartLine(),
                    severe: true
                );
                continue;
            }

            if (isset(self::DEPRECATED_FUNCTIONS[$name])) {
                $explanation = self::DEPRECATED_FUNCTIONS[$name];
                $issues []= new EventHandlerIssue(
                    message: \sprintf(Lang::$current_lang['do_not_use_deprecated_function'], $name, $explanation),
                    file: $file,
                    line: $call->getStartLine(),
                    severe: true
                );
                continue;
            }

            if ($name === 'unlink'
                && $call->args
                && $call->args[0] instanceof Arg
                && $call->args[0]->value instanceof String_
            ) {
                $arg = $call->args[0]->value->value;
                if ($arg === 'MadelineProto.log') {
                    $issues []= new EventHandlerIssue(
                        message: Lang::$current_lang['do_not_delete_MadelineProto.log'],
                        file: $file,
                        line: $call->getStartLine(),
                        severe: true
                    );
                } elseif (str_starts_with($arg, 'madeline') && str_ends_with($arg, '.phar')) {
                    $issues []= new EventHandlerIssue(
                        message: Lang::$current_lang['do_not_remove_MadelineProto.log_phar'],
                        file: $file,
                        line: $call->getStartLine(),
                        severe: true
                    );
                }
                continue;
            }

            if (\in_array($name, self::BANNED_FILE_FUNCTIONS, true)) {
                $issues []= new EventHandlerIssue(
                    message: \sprintf(Lang::$current_lang['recommend_not_use_filesystem_function'], $name),
                    file: $file,
                    line: $call->getStartLine(),
                    severe: false
                );
            }
        }

        /** @var New_ $new */
        foreach ($finder->findInstanceOf($code, New_::class) as $new) {
            if (!$new->class instanceof Name) {
                continue;
            }
            $name = $new->class->toLowerString();
            if (isset(self::BLOCKING_CLASSES[$name])) {
                $explanation = self::BLOCKING_CLASSES[$name];
                $issues []= new EventHandlerIssue(
                    message: \sprintf(Lang::$current_lang['do_not_use_blocking_class'], $name, $explanation),
                    file: $file,
                    line: $new->getStartLine(),
                    severe: true
                );
            }
        }

        /** @var Include_ $include */
        foreach ($finder->findInstanceOf($code, Include_::class) as $include) {
            if ($plugin) {
                $issues []= new EventHandlerIssue(
                    message: Lang::$current_lang['plugins_do_not_use_require'],
                    file: $file,
                    line: $include->getStartLine(),
                    severe: true
                );
            } elseif ($include->getAttribute('parent')) {
                $parent = $include;
                while ($parent = $parent->getAttribute('parent')) {
                    if ($parent instanceof FunctionLike) {
                        $issues []= new EventHandlerIssue(
                            message: Lang::$current_lang['do_not_use_non_root_require_in_event_handler'],
                            file: $file,
                            line: $include->getStartLine(),
                            severe: true
                        );
                        break;
                    }
                }
            }
        }

        /** @var Yield_|YieldFrom $include */
        foreach ([
            ...$finder->findInstanceOf($code, Yield_::class),
            ...$finder->findInstanceOf($code, YieldFrom::class),
        ] as $include) {
            if ($include->getAttribute('parent')) {
                $parent = $include;
                while ($parent = $parent->getAttribute('parent')) {
                    if ($parent instanceof ClassMethod
                        && $parent->isPublic()
                        && \in_array($parent->name->toLowerString(), self::NO_YIELD_FUNCTIONS, true)
                    ) {
                        $issues []= new EventHandlerIssue(
                            message: Lang::$current_lang['do_not_use_yield'],
                            file: $file,
                            line: $include->getStartLine(),
                            severe: true
                        );
                        break;
                    }
                }
            }
        }

        return $issues;
    }

    private static ?bool $canConvert = null;
    /**
     * Whether we can convert any audio/video file to a VoIP OGG OPUS file, or the files must be preconverted using @libtgvoipbot.
     */
    public static function canConvertOgg(): bool
    {
        if (self::$canConvert !== null) {
            return self::$canConvert;
        }
        try {
            Ogg::convert(new ReadableBuffer(file_get_contents(__DIR__.'/empty.wav')), new WritableBuffer);
            self::$canConvert = true;
        } catch (\Throwable $e) {
            Logger::log("An error occurred while attempting conversion: $e");
            self::$canConvert = false;
        }
        return self::$canConvert;
    }

    private static ?bool $canFFmpeg = null;
    /**
     * Whether we can convert any audio/video file using ffmpeg.
     */
    public static function canUseFFmpeg(?Cancellation $cancellation = null): bool
    {
        if (self::$canFFmpeg !== null) {
            return self::$canFFmpeg;
        }
        try {
            self::$canFFmpeg = Process::start('ffmpeg -version', cancellation: $cancellation)->join($cancellation) === 0;
        } catch (\Throwable $e) {
            Logger::log("An error occurred while attempting conversion: $e");
            self::$canFFmpeg = false;
        }
        return self::$canFFmpeg;
    }

    /**
     * @psalm-pure
     * @internal
     * @psalm-taint-escape html
     * @psalm-taint-escape quotes
     */
    public static function taintEscape(string $s): string
    {
        return PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg' ? $s : htmlentities($s);
    }
}
<?php

declare(strict_types=1);

/**
 * PTSException module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use Exception;

use const PHP_EOL;

/**
 * Internal error indicating a problem with Telegram's servers.
 */
final class PTSException extends Exception
{
    use TL\PrettyException;
    /**
     * @psalm-mutation-free
     */
    public function __toString(): string
    {
        return PTSException::class.($this->message !== '' ? ': ' : '').$this->message.PHP_EOL.'TL Trace:'.PHP_EOL.PHP_EOL.$this->getTLTrace().PHP_EOL;
    }
    public function __construct($message, $file = '')
    {
        parent::__construct($message);
        $this->prettifyTL($file);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

/**
 * Indicates a parsing mode for text.
 */
enum ParseMode: string
{
    case HTML = 'HTML';
    case MARKDOWN = 'Markdown';
    case TEXT = 'text';
}
<?php

declare(strict_types=1);

/**
 * Conversion module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use danog\MadelineProto\MTProtoTools\Crypt;
use PDO;
use Webmozart\Assert\Assert;

use const DIRECTORY_SEPARATOR;

final class Conversion
{
    /**
     * Import authorization from raw auth key and DC id.
     *
     * @param array<int, string> $authorization Authorization info, DC ID => auth key
     */
    public static function importAuthorization(array $authorization, int $main_dc_id, string $session, ?SettingsAbstract $settings = null): API
    {
        $settingsFull = new Settings;
        if ($settings) {
            $settingsFull->merge($settings);
        }
        $settings = $settingsFull;
        $settings->getLogger()->setLevel(Logger::ULTRA_VERBOSE);
        $MadelineProto = new API($session, $settings);
        /** @var APIWrapper */
        $wrapper = Tools::getVar($MadelineProto, 'wrapper');
        $wrapper->getAPI()->methodCallAsyncRead('help.getConfig', [], $main_dc_id);
        $MadelineProto->logger("About to import auth to DC $main_dc_id!", Logger::FATAL_ERROR);
        $MadelineProto->importAuthorization($authorization, $main_dc_id);
        return $MadelineProto;
    }
    /**
     * Convert telethon session.
     *
     * @param string $session Telethon session file
     * @param string $new_session MadelineProto session directory to create
     * @param SettingsAbstract|null $settings Settings
     */
    public static function telethon(string $session, string $new_session, ?SettingsAbstract $settings = null): API
    {
        if (!\extension_loaded('sqlite3')) {
            throw Exception::extension('sqlite3');
        }
        Magic::start(light: false);
        if (!isset(pathinfo($session)['extension'])) {
            $session .= '.session';
        }
        $session = Tools::absolute($session);
        $sqlite = new PDO("sqlite:$session");
        $sqlite->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

        $sessions = $sqlite->query('SELECT * FROM sessions')->fetchAll();

        $dcs = [];
        foreach ($sessions as $dc) {
            $dcs[$dc['dc_id']] = $dc['auth_key'];
        }

        return self::importAuthorization($dcs, $dc['dc_id'], $new_session, $settings);
    }

    /**
     * Convert pyrogram session.
     *
     * @param string $session Pyrogram session file
     * @param string $new_session MadelineProto session directory to create
     * @param SettingsAbstract|null $settings Settings
     */
    public static function pyrogram(string $session, string $new_session, ?SettingsAbstract $settings = null): API
    {
        set_error_handler(['\\danog\\MadelineProto\\Exception', 'ExceptionErrorHandler']);
        if (!\extension_loaded('sqlite3')) {
            throw Exception::extension('sqlite3');
        }
        Magic::start(light: false);
        if (!isset(pathinfo($session)['extension'])) {
            $session .= '.session';
        }
        $session = Tools::absolute($session);
        $sqlite = new PDO("sqlite:$session");
        $session = $sqlite->query("SELECT * FROM sessions")->fetchAll(PDO::FETCH_ASSOC)[0];

        $settingsFull = new Settings;
        if ($settings) {
            $settingsFull->merge($settings);
        }
        $settingsFull->getConnection()->setTestMode((bool) $session['test_mode']);

        return self::importAuthorization([$session['dc_id'] => $session['auth_key']], $session['dc_id'], $new_session, $settings);
    }

    public static function zerobias(string|array $session, string $new_session, ?SettingsAbstract $settings = null): API
    {
        set_error_handler(['\\danog\\MadelineProto\\Exception', 'ExceptionErrorHandler']);
        if (\is_string($session)) {
            $session = json_decode($session, true);
        }
        $dc = $session['dc'];
        $key = hex2bin($session["dc$dc".'_auth_key']);

        Assert::integer($dc);
        Assert::string($key);
        return self::importAuthorization([$dc => $key], $dc, $new_session, $settings);
    }

    /**
     * Convert a Telegram Desktop session.
     *
     * Passcodes set by recent versions of Telegram Desktop are wrapped with argon2id or scrypt, which are not supported: turn off the local passcode before converting.
     *
     * @param string $session Telegram Desktop tdata directory (or its parent directory)
     * @param string $new_session MadelineProto session directory to create
     * @param SettingsAbstract|null $settings Settings
     * @param string $passcode Local passcode, if any
     * @param string $key Data name, if Telegram Desktop is started with -key
     * @param int|null $account Index of the account to convert, the active one by default
     */
    public static function tdesktop(string $session, string $new_session, ?SettingsAbstract $settings = null, string $passcode = '', string $key = 'data', ?int $account = null): API
    {
        $authorization = self::parseTdesktop($session, $passcode, $key, $account);
        return self::importAuthorization($authorization['auth_keys'], $authorization['main_dc_id'], $new_session, $settings);
    }

    private const TDESKTOP_SALT_SIZE = 32;
    private const TDESKTOP_KEY_DATA_MAGIC = 0x4B443200;
    private const TDESKTOP_KEY_DATA_FORMAT_VERSION = 2;

    private const dbiKey = 0x00;
    private const dbiUser = 0x01;
    private const dbiMtpAuthorization = 0x4b;

    /**
     * Parse the MTProto authorization of a Telegram Desktop session.
     *
     * @internal
     *
     * @param string $session Telegram Desktop tdata directory (or its parent directory)
     * @param string $passcode Local passcode, if any
     * @param string $key Data name, if Telegram Desktop is started with -key
     * @param int|null $account Index of the account to parse, the active one by default
     *
     * @return array{main_dc_id: int, user_id: int, auth_keys: array<int, string>}
     */
    public static function parseTdesktop(string $session, string $passcode = '', string $key = 'data', ?int $account = null): array
    {
        if (basename($session) !== 'tdata') {
            $session .= DIRECTORY_SEPARATOR.'tdata';
        }
        $base = $session.DIRECTORY_SEPARATOR;

        // storage_domain.cpp, Domain::startModern
        $keyData = self::tdesktopReadFile($base.'key_'.$key);
        if ($keyData !== null) {
            $offset = 0;
            $salt = self::tdesktopReadByteArray($keyData, $offset);
            $openKeyEncrypted = self::tdesktopReadByteArray($keyData, $offset);
            $infoEncrypted = self::tdesktopReadByteArray($keyData, $offset);
            if (\strlen($salt) !== self::TDESKTOP_SALT_SIZE) {
                throw new Exception('Wrong salt length in the key data!');
            }
            if ($offset === \strlen($keyData)) {
                // Legacy key data, wrapped with the passcode (if any)
                $localKey = self::tdesktopUnwrapLocalKey($openKeyEncrypted, self::tdesktopCreateLocalKey($passcode, $salt));
            } elseif ($passcode === '') {
                if ($openKeyEncrypted === '') {
                    throw new Exception('The Telegram Desktop session is protected by a local passcode!');
                }
                $localKey = self::tdesktopUnwrapLocalKey($openKeyEncrypted, self::tdesktopCreateLocalKey('', $salt));
            } else {
                $trailer = self::tdesktopReadByteArray($keyData, $offset);
                $offset = 0;
                $magic = self::tdesktopReadUint32($trailer, $offset);
                $formatVersion = self::tdesktopReadUint32($trailer, $offset);
                $committed = self::tdesktopReadUint32($trailer, $offset);
                $wrapCount = self::tdesktopReadUint32($trailer, $offset);
                if ($magic !== self::TDESKTOP_KEY_DATA_MAGIC || $formatVersion > self::TDESKTOP_KEY_DATA_FORMAT_VERSION) {
                    throw new Exception('Unsupported key data format!');
                }
                $wraps = [];
                for ($x = 0; $x < $wrapCount; $x++) {
                    $kind = self::tdesktopReadUint32($trailer, $offset);
                    $generation = self::tdesktopReadUint32($trailer, $offset);
                    $offset += 12; // KDF memory, time and parallelism
                    $wrapSalt = self::tdesktopReadByteArray($trailer, $offset);
                    $wrapKeyEncrypted = self::tdesktopReadByteArray($trailer, $offset);
                    // Only the wrap of the committed generation is live
                    if ($generation === $committed) {
                        $wraps[] = [$kind, $wrapSalt, $wrapKeyEncrypted];
                    }
                }
                if (\count($wraps) !== 1) {
                    throw new Exception('The Telegram Desktop session is not protected by a local passcode!');
                }
                [[$kind, $wrapSalt, $wrapKeyEncrypted]] = $wraps;
                if ($kind !== 0) {
                    throw new Exception('The local passcode of the Telegram Desktop session uses argon2id or scrypt, which are not supported: turn off the local passcode in Telegram Desktop before converting.');
                }
                $localKey = self::tdesktopUnwrapLocalKey($wrapKeyEncrypted, self::tdesktopCreateLocalKey($passcode, $wrapSalt));
            }

            $info = self::tdesktopDecrypt($infoEncrypted, $localKey);
            $offset = 0;
            $count = self::tdesktopReadInt32($info, $offset);
            $indexes = [];
            for ($x = 0; $x < $count; $x++) {
                $index = self::tdesktopReadInt32($info, $offset);
                if ($index >= 0) {
                    $indexes[$index] = true;
                }
            }
            if (!$indexes) {
                throw new Exception('No accounts in the Telegram Desktop session!');
            }
            if ($account === null) {
                $account = $offset < \strlen($info) ? self::tdesktopReadInt32($info, $offset) : -1;
                if (!isset($indexes[$account])) {
                    $account = array_key_first($indexes);
                }
            }
            if (!isset($indexes[$account])) {
                throw new Exception("No account with index $account in the Telegram Desktop session!");
            }
        } else {
            // Before multiple accounts: storage_account.cpp, Account::readMapWith
            if ($account) {
                throw new Exception("No account with index $account in the Telegram Desktop session!");
            }
            $account = 0;
            $map = self::tdesktopReadFile($base.self::tdesktopFilePart($key).DIRECTORY_SEPARATOR.'map');
            if ($map === null) {
                throw new Exception("Could not find a Telegram Desktop session in $session!");
            }
            $offset = 0;
            $salt = self::tdesktopReadByteArray($map, $offset);
            $keyEncrypted = self::tdesktopReadByteArray($map, $offset);
            if (\strlen($salt) !== self::TDESKTOP_SALT_SIZE) {
                throw new Exception('Wrong salt length in the map!');
            }
            $passKey = openssl_pbkdf2($passcode, $salt, 256, $passcode === '' ? 4 : 4000, 'sha1');
            $localKey = self::tdesktopUnwrapLocalKey($keyEncrypted, $passKey);
        }

        // main_account.cpp, ComposeDataString; storage_account.cpp, Account::readMtpData
        $dataName = str_replace('#', '', $key);
        if ($account > 0) {
            $dataName .= '#'.($account + 1);
        }
        $mtp = self::tdesktopReadFile($base.self::tdesktopFilePart($dataName));
        if ($mtp === null) {
            throw new Exception("Could not find the MTProto data of account $account!");
        }
        $offset = 0;
        $mtp = self::tdesktopDecrypt(self::tdesktopReadByteArray($mtp, $offset), $localKey);

        $authorization = null;
        $user_id = 0;
        $main_dc_id = 0;
        $auth_keys = [];
        $offset = 0;
        while ($offset < \strlen($mtp)) {
            $block = self::tdesktopReadUint32($mtp, $offset);
            switch ($block) {
                case self::dbiMtpAuthorization:
                    $authorization = self::tdesktopReadByteArray($mtp, $offset);
                    break;
                case self::dbiUser:
                    $user_id = self::tdesktopReadInt32($mtp, $offset);
                    $main_dc_id = self::tdesktopReadUint32($mtp, $offset);
                    break;
                case self::dbiKey:
                    $dc = self::tdesktopReadInt32($mtp, $offset);
                    $auth_keys[$dc] = self::tdesktopRead($mtp, $offset, 256);
                    break;
                default:
                    throw new Exception("Unknown block $block in the MTProto data!");
            }
        }
        if ($authorization !== null) {
            // main_account.cpp, Account::setMtpAuthorization
            $auth_keys = [];
            $offset = 0;
            $user_id = self::tdesktopReadInt32($authorization, $offset);
            $main_dc_id = self::tdesktopReadInt32($authorization, $offset);
            if ($user_id === -1 && $main_dc_id === -1) {
                // kWideIdsTag, followed by a 64-bit user ID
                $user_id = unpack('J', self::tdesktopRead($authorization, $offset, 8))[1];
                $main_dc_id = self::tdesktopReadInt32($authorization, $offset);
            }
            $count = self::tdesktopReadInt32($authorization, $offset);
            for ($x = 0; $x < $count; $x++) {
                $dc = self::tdesktopReadInt32($authorization, $offset);
                $auth_keys[$dc] = self::tdesktopRead($authorization, $offset, 256);
            }
        }
        if (!isset($auth_keys[$main_dc_id])) {
            throw new Exception("No auth key for the main DC $main_dc_id in the Telegram Desktop session!");
        }

        return ['main_dc_id' => $main_dc_id, 'user_id' => $user_id, 'auth_keys' => $auth_keys];
    }

    /**
     * File name of a key, as in tdesktop's ToFilePart: the first 8 bytes of the MD5 of the name, in hex, low nibble first.
     *
     * @psalm-pure
     */
    private static function tdesktopFilePart(string $name): string
    {
        return strtoupper(strrev(bin2hex(strrev(substr(md5($name, true), 0, 8)))));
    }

    /**
     * Read a file as tdesktop's ReadFile does, returning its data or null if no valid copy exists.
     */
    private static function tdesktopReadFile(string $name): ?string
    {
        if (file_exists($name.'s')) {
            $totry = [$name.'s'];
        } else {
            $totry = array_filter([$name.'0', $name.'1'], file_exists(...));
            usort($totry, static fn (string $a, string $b): int => filemtime($b) <=> filemtime($a));
        }
        foreach ($totry as $file) {
            $contents = file_get_contents($file);
            if (\strlen($contents) < 24 || substr($contents, 0, 4) !== 'TDF$') {
                Logger::log("Wrong magic in $file", Logger::ERROR);
                continue;
            }
            $versionBytes = substr($contents, 4, 4);
            Logger::log('TDesktop version: '.unpack('V', $versionBytes)[1]);
            $data = substr($contents, 8, -16);
            if (md5($data.pack('V', \strlen($data)).$versionBytes.'TDF$', true) !== substr($contents, -16)) {
                Logger::log("Wrong MD5 in $file", Logger::ERROR);
                continue;
            }
            return $data;
        }
        return null;
    }

    private static function tdesktopRead(string $data, int &$offset, int $length): string
    {
        if ($length < 0 || $offset + $length > \strlen($data)) {
            throw new Exception('Unexpected end of Telegram Desktop data!');
        }
        $result = substr($data, $offset, $length);
        $offset += $length;
        return $result;
    }

    private static function tdesktopReadUint32(string $data, int &$offset): int
    {
        return unpack('N', self::tdesktopRead($data, $offset, 4))[1];
    }

    private static function tdesktopReadInt32(string $data, int &$offset): int
    {
        $value = self::tdesktopReadUint32($data, $offset);
        return $value >= 0x80000000 ? $value - 0x100000000 : $value;
    }

    /**
     * Read a QByteArray (a null one is empty).
     */
    private static function tdesktopReadByteArray(string $data, int &$offset): string
    {
        $length = self::tdesktopReadUint32($data, $offset);
        return $length === 0xFFFFFFFF ? '' : self::tdesktopRead($data, $offset, $length);
    }

    /**
     * Decrypt data encrypted with a local key, as tdesktop's DecryptLocal does.
     */
    private static function tdesktopDecrypt(string $encrypted, string $key): string
    {
        if (\strlen($encrypted) <= 16 || \strlen($encrypted) % 16) {
            throw new Exception('Wrong length of Telegram Desktop encrypted data!');
        }
        $message_key = substr($encrypted, 0, 16);
        [$aes_key, $aes_iv] = Crypt::oldKdf($message_key, $key, false);
        $decrypted = Crypt::igeDecrypt(substr($encrypted, 16), $aes_key, $aes_iv);
        if ($message_key !== substr(sha1($decrypted, true), 0, 16)) {
            throw new SecurityException('msg_key mismatch: wrong passcode?');
        }
        $length = unpack('V', $decrypted)[1];
        if ($length > \strlen($decrypted) || $length <= \strlen($decrypted) - 16 || $length < 4) {
            throw new Exception('Wrong length of Telegram Desktop decrypted data!');
        }
        return substr($decrypted, 4, $length - 4);
    }

    /**
     * Local key from a passcode, as tdesktop's CreateLocalKey does.
     */
    private static function tdesktopCreateLocalKey(string $passcode, string $salt): string
    {
        $hash = hash('sha512', $salt.$passcode.$salt, true);
        return openssl_pbkdf2($hash, $salt, 256, $passcode === '' ? 1 : 100000, 'sha512');
    }

    private static function tdesktopUnwrapLocalKey(string $encrypted, string $wrapKey): string
    {
        $key = self::tdesktopDecrypt($encrypted, $wrapKey);
        if (\strlen($key) !== 256) {
            throw new Exception('Wrong length of the Telegram Desktop local key!');
        }
        return $key;
    }
}
<?php

declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use ReflectionProperty;

/**
 * Restores the state of an unserialized object, skipping the properties its class no longer declares.
 *
 * Sessions outlive the releases that wrote them: a property removed or renamed since then is still in
 * the serialized state. PHP's default unserialization, and an __unserialize() that assigns every key,
 * recreate it as a dynamic property (deprecated since PHP 8.2), and the next __serialize() built from
 * get_object_vars() writes it back, so it is carried forever.
 *
 * @internal
 */
trait RestoresDeclaredProperties
{
    /** @var array<string, ?ReflectionProperty> By the unserialized class, the declaring class and the name of the property. */
    private static array $restorableProperties = [];

    /**
     * Assign the serialized properties that are still declared.
     *
     * Accepts the keys of __serialize() (plain names) and the mangled keys of the default format
     * (`"\0Class\0name"` for private properties, `"\0*\0name"` for protected ones), which is what
     * __unserialize() receives for an object serialized through __sleep().
     *
     * @param array<array-key, mixed> $data
     * @return array<string, mixed> The values of the properties that are no longer declared, by name, for the caller to migrate.
     */
    private function restoreDeclaredProperties(array $data): array
    {
        $dropped = [];
        /** @var mixed $value */
        foreach ($data as $key => $value) {
            $key = (string) $key;
            $class = static::class;
            $name = $key;
            if ($key !== '' && $key[0] === "\0") {
                $end = strpos($key, "\0", 1);
                if ($end === false) {
                    $dropped[$key] = $value;
                    continue;
                }
                $scope = substr($key, 1, $end - 1);
                $name = substr($key, $end + 1);
                if ($scope !== '*') {
                    $class = $scope;
                }
            }
            $cacheKey = static::class."\0$class\0$name";
            if (!\array_key_exists($cacheKey, self::$restorableProperties)) {
                self::$restorableProperties[$cacheKey] = self::findRestorableProperty($class, $name);
            }
            $property = self::$restorableProperties[$cacheKey];
            if ($property === null) {
                $dropped[$name] = $value;
                continue;
            }
            $property->setValue($this, $value);
        }
        return $dropped;
    }

    private static function findRestorableProperty(string $class, string $name): ?ReflectionProperty
    {
        // A private property of a class that is no longer an ancestor (renamed or removed) is gone too.
        if ($class !== static::class && !is_subclass_of(static::class, $class)) {
            return null;
        }
        if (!property_exists($class, $name)) {
            return null;
        }
        $property = new ReflectionProperty($class, $name);
        if ($property->isStatic()) {
            return null;
        }
        // setValue() writes from the scope of the class the reflection was created for: before PHP 8.4,
        // a readonly property can only be initialized from the class declaring it.
        $declaring = $property->getDeclaringClass()->name;
        return $declaring === $class ? $property : new ReflectionProperty($declaring, $name);
    }
}
<?php declare(strict_types=1);
/**
 * This file is automatically generated by the build_docs.php file
 * and is used only for autocompletion in multiple IDEs
 * don't modify it manually.
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto;

use __PHP_Incomplete_Class;
use Amp\ByteStream\Pipe;
use Amp\ByteStream\ReadableStream;
use Amp\ByteStream\WritableStream;
use Amp\Cancellation;
use Amp\Future;
use Amp\Http\Server\Request as ServerRequest;
use Closure;
use danog\MadelineProto\Broadcast\Action;
use danog\MadelineProto\Broadcast\Progress;
use danog\MadelineProto\Broadcast\Status;
use danog\MadelineProto\EventHandler\Action\Cancel;
use danog\MadelineProto\EventHandler\Attributes\Handler;
use danog\MadelineProto\EventHandler\Calls\AbstractGroupCall;
use danog\MadelineProto\EventHandler\Calls\AbstractGroupCallParticipant;
use danog\MadelineProto\EventHandler\Calls\GroupCall;
use danog\MadelineProto\EventHandler\Calls\PrivateCall;
use danog\MadelineProto\EventHandler\Keyboard;
use danog\MadelineProto\EventHandler\Media;
use danog\MadelineProto\EventHandler\Media\Audio;
use danog\MadelineProto\EventHandler\Media\Document;
use danog\MadelineProto\EventHandler\Media\Gif;
use danog\MadelineProto\EventHandler\Media\Photo;
use danog\MadelineProto\EventHandler\Media\Sticker;
use danog\MadelineProto\EventHandler\Media\Thumbnail;
use danog\MadelineProto\EventHandler\Media\Video;
use danog\MadelineProto\EventHandler\Media\Voice;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Message\Entities\Code;
use danog\MadelineProto\EventHandler\Message\Entities\Email;
use danog\MadelineProto\EventHandler\Message\Entities\Mention;
use danog\MadelineProto\EventHandler\Message\Entities\MessageEntity;
use danog\MadelineProto\EventHandler\Message\Entities\Phone;
use danog\MadelineProto\EventHandler\Message\Entities\Pre;
use danog\MadelineProto\EventHandler\Message\Entities\Spoiler;
use danog\MadelineProto\EventHandler\Message\Entities\Unknown;
use danog\MadelineProto\EventHandler\Message\Entities\Url;
use danog\MadelineProto\EventHandler\Participant;
use danog\MadelineProto\EventHandler\Participant\Admin;
use danog\MadelineProto\EventHandler\Participant\Member;
use danog\MadelineProto\EventHandler\Pinned;
use danog\MadelineProto\EventHandler\Story\Story;
use danog\MadelineProto\EventHandler\Update;
use danog\MadelineProto\EventHandler\User\Status\Emoji;
use danog\MadelineProto\EventHandler\User\Username;
use danog\MadelineProto\Ipc\Client;
use danog\MadelineProto\Ipc\EventHandlerProxy;
use danog\MadelineProto\Ipc\Server;

/** @psalm-suppress PossiblyNullReference, PropertyNotSetInConstructor */
abstract class InternalDoc
{
    protected APIWrapper $wrapper;
    /** @var \danog\MadelineProto\Namespace\Auth $auth */
    public $auth;
    /** @var \danog\MadelineProto\Namespace\Account $account */
    public $account;
    /** @var \danog\MadelineProto\Namespace\Users $users */
    public $users;
    /** @var \danog\MadelineProto\Namespace\Contacts $contacts */
    public $contacts;
    /** @var \danog\MadelineProto\Namespace\Messages $messages */
    public $messages;
    /** @var \danog\MadelineProto\Namespace\Updates $updates */
    public $updates;
    /** @var \danog\MadelineProto\Namespace\Photos $photos */
    public $photos;
    /** @var \danog\MadelineProto\Namespace\Upload $upload */
    public $upload;
    /** @var \danog\MadelineProto\Namespace\Help $help */
    public $help;
    /** @var \danog\MadelineProto\Namespace\Channels $channels */
    public $channels;
    /** @var \danog\MadelineProto\Namespace\Bots $bots */
    public $bots;
    /** @var \danog\MadelineProto\Namespace\Payments $payments */
    public $payments;
    /** @var \danog\MadelineProto\Namespace\Stickers $stickers */
    public $stickers;
    /** @var \danog\MadelineProto\Namespace\Phone $phone */
    public $phone;
    /** @var \danog\MadelineProto\Namespace\Langpack $langpack */
    public $langpack;
    /** @var \danog\MadelineProto\Namespace\Folders $folders */
    public $folders;
    /** @var \danog\MadelineProto\Namespace\Stats $stats */
    public $stats;
    /** @var \danog\MadelineProto\Namespace\Chatlists $chatlists */
    public $chatlists;
    /** @var \danog\MadelineProto\Namespace\Stories $stories */
    public $stories;
    /** @var \danog\MadelineProto\Namespace\Premium $premium */
    public $premium;
    /** @var \danog\MadelineProto\Namespace\Smsjobs $smsjobs */
    public $smsjobs;
    /** @var \danog\MadelineProto\Namespace\Fragment $fragment */
    public $fragment;
    /** @var \danog\MadelineProto\Namespace\Aicompose $aicompose */
    public $aicompose;
    /** @var \danog\MadelineProto\Namespace\Communities $communities */
    public $communities;
    /** @var \danog\MadelineProto\Namespace\Ephemeral $ephemeral */
    public $ephemeral;
    /** @var \danog\MadelineProto\Namespace\Toncenter $toncenter */
    public $toncenter;
    /** @var \danog\MadelineProto\Namespace\Wallet $wallet */
    public $wallet;

    /**
     * Export APIFactory instance with the specified namespace.
     * @psalm-suppress InaccessibleProperty
     */
    protected function exportNamespaces(): void
    {
        $this->auth ??= new \danog\MadelineProto\Namespace\AbstractAPI('auth');
        $this->auth->setWrapper($this->wrapper);
        $this->account ??= new \danog\MadelineProto\Namespace\AbstractAPI('account');
        $this->account->setWrapper($this->wrapper);
        $this->users ??= new \danog\MadelineProto\Namespace\AbstractAPI('users');
        $this->users->setWrapper($this->wrapper);
        $this->contacts ??= new \danog\MadelineProto\Namespace\AbstractAPI('contacts');
        $this->contacts->setWrapper($this->wrapper);
        $this->messages ??= new \danog\MadelineProto\Namespace\AbstractAPI('messages');
        $this->messages->setWrapper($this->wrapper);
        $this->updates ??= new \danog\MadelineProto\Namespace\AbstractAPI('updates');
        $this->updates->setWrapper($this->wrapper);
        $this->photos ??= new \danog\MadelineProto\Namespace\AbstractAPI('photos');
        $this->photos->setWrapper($this->wrapper);
        $this->upload ??= new \danog\MadelineProto\Namespace\AbstractAPI('upload');
        $this->upload->setWrapper($this->wrapper);
        $this->help ??= new \danog\MadelineProto\Namespace\AbstractAPI('help');
        $this->help->setWrapper($this->wrapper);
        $this->channels ??= new \danog\MadelineProto\Namespace\AbstractAPI('channels');
        $this->channels->setWrapper($this->wrapper);
        $this->bots ??= new \danog\MadelineProto\Namespace\AbstractAPI('bots');
        $this->bots->setWrapper($this->wrapper);
        $this->payments ??= new \danog\MadelineProto\Namespace\AbstractAPI('payments');
        $this->payments->setWrapper($this->wrapper);
        $this->stickers ??= new \danog\MadelineProto\Namespace\AbstractAPI('stickers');
        $this->stickers->setWrapper($this->wrapper);
        $this->phone ??= new \danog\MadelineProto\Namespace\AbstractAPI('phone');
        $this->phone->setWrapper($this->wrapper);
        $this->langpack ??= new \danog\MadelineProto\Namespace\AbstractAPI('langpack');
        $this->langpack->setWrapper($this->wrapper);
        $this->folders ??= new \danog\MadelineProto\Namespace\AbstractAPI('folders');
        $this->folders->setWrapper($this->wrapper);
        $this->stats ??= new \danog\MadelineProto\Namespace\AbstractAPI('stats');
        $this->stats->setWrapper($this->wrapper);
        $this->chatlists ??= new \danog\MadelineProto\Namespace\AbstractAPI('chatlists');
        $this->chatlists->setWrapper($this->wrapper);
        $this->stories ??= new \danog\MadelineProto\Namespace\AbstractAPI('stories');
        $this->stories->setWrapper($this->wrapper);
        $this->premium ??= new \danog\MadelineProto\Namespace\AbstractAPI('premium');
        $this->premium->setWrapper($this->wrapper);
        $this->smsjobs ??= new \danog\MadelineProto\Namespace\AbstractAPI('smsjobs');
        $this->smsjobs->setWrapper($this->wrapper);
        $this->fragment ??= new \danog\MadelineProto\Namespace\AbstractAPI('fragment');
        $this->fragment->setWrapper($this->wrapper);
        $this->aicompose ??= new \danog\MadelineProto\Namespace\AbstractAPI('aicompose');
        $this->aicompose->setWrapper($this->wrapper);
        $this->communities ??= new \danog\MadelineProto\Namespace\AbstractAPI('communities');
        $this->communities->setWrapper($this->wrapper);
        $this->ephemeral ??= new \danog\MadelineProto\Namespace\AbstractAPI('ephemeral');
        $this->ephemeral->setWrapper($this->wrapper);
        $this->toncenter ??= new \danog\MadelineProto\Namespace\AbstractAPI('toncenter');
        $this->toncenter->setWrapper($this->wrapper);
        $this->wallet ??= new \danog\MadelineProto\Namespace\AbstractAPI('wallet');
        $this->wallet->setWrapper($this->wrapper);
    }
    /**
         * Convert MTProto parameters to bot API parameters.
         *
         * @param array $data Data
         */
    final public function MTProtoToBotAPI(array $data): array
    {
        return $this->wrapper->getAPI()->MTProtoToBotAPI($data);
    }
    /**
     * MTProto to TD params.
     *
     * @param mixed $params Params
     */
    final public function MTProtoToTd(mixed &$params): array
    {
        return $this->wrapper->getAPI()->MTProtoToTd($params);
    }
    /**
     * MTProto to TDCLI params.
     *
     * @param mixed $params Params
     */
    final public function MTProtoToTdcli(mixed $params): array
    {
        return $this->wrapper->getAPI()->MTProtoToTdcli($params);
    }
    /**
     * Accept call.
     */
    final public function acceptCall(int $id, ?\Amp\Cancellation $cancellation = null): void
    {
        $this->wrapper->getAPI()->acceptCall($id, $cancellation);
    }
    /**
     * Accept secret chat.
     *
     * @param array $params Secret chat ID
     */
    final public function acceptSecretChat(array $params): void
    {
        $this->wrapper->getAPI()->acceptSecretChat($params);
    }
    /**
     * Create array.
     *
     * @param mixed ...$params Params
     *
     * @psalm-pure
     */
    final public static function arr(mixed ...$params): array
    {
        return \danog\MadelineProto\Tools::arr(...$params);
    }
    /**
     * base64URL decode.
     *
     * @param string $data Data to decode
     *
     * @psalm-pure
     */
    final public static function base64urlDecode(string $data): string
    {
        return \danog\MadelineProto\Tools::base64urlDecode($data);
    }
    /**
     * Base64URL encode.
     *
     * @param string $data Data to encode
     *
     * @psalm-pure
     */
    final public static function base64urlEncode(string $data): string
    {
        return \danog\MadelineProto\Tools::base64urlEncode($data);
    }
    /**
     * Convert bot API parameters to MTProto parameters.
     *
     * @param array $arguments Arguments
     */
    final public function botAPIToMTProto(array $arguments): array
    {
        return $this->wrapper->getAPI()->botAPIToMTProto($arguments);
    }
    /**
     * Login as bot.
     *
     * @param string $token Bot token
     */
    final public function botLogin(string $token): ?array
    {
        return $this->wrapper->getAPI()->botLogin($token);
    }
    /**
     * Executes a custom broadcast action with all peers (users, chats, channels) of the bot.
     *
     * Will return an integer ID that can be used to:
     *
     * - Get the current broadcast progress with getBroadcastProgress
     * - Cancel the broadcast using cancelBroadcast
     *
     * Note that to avoid manually polling the progress,
     * MadelineProto will also periodically emit updateBroadcastProgress updates,
     * containing a Progress object for all broadcasts currently in-progress.
     *
     * @param Action $action A custom, serializable Action class that will be called once for every peer.
     * @param float|null $delay Number of seconds to wait between each peer.
     */
    final public function broadcastCustom(\danog\MadelineProto\Broadcast\Action $action, ?\danog\MadelineProto\Broadcast\Filter $filter = null, ?float $delay = null): int
    {
        return $this->wrapper->getAPI()->broadcastCustom($action, $filter, $delay);
    }
    /**
     * Forwards a list of messages to all peers (users, chats, channels) of the bot.
     *
     * Will return an integer ID that can be used to:
     *
     * - Get the current broadcast progress with getBroadcastProgress
     * - Cancel the broadcast using cancelBroadcast
     *
     * Note that to avoid manually polling the progress,
     * MadelineProto will also periodically emit updateBroadcastProgress updates,
     * containing a Progress object for all broadcasts currently in-progress.
     *
     * @param mixed      $from_peer   Bot API ID or Update, from where to forward the messages.
     * @param list<int>  $message_ids IDs of the messages to forward.
     * @param bool       $drop_author If true, will forward messages without quoting the original author.
     * @param bool       $pin         Whether to also pin the last sent message.
     * @param float|null $delay       Number of seconds to wait between each peer.
     */
    final public function broadcastForwardMessages(mixed $from_peer, array $message_ids, bool $drop_author = false, ?\danog\MadelineProto\Broadcast\Filter $filter = null, bool $pin = false, ?float $delay = null): int
    {
        return $this->wrapper->getAPI()->broadcastForwardMessages($from_peer, $message_ids, $drop_author, $filter, $pin, $delay);
    }
    /**
     * Sends a list of messages to all peers (users, chats, channels) of the bot.
     *
     * A simplified version of this method is also available: broadcastForwardMessages can work with pre-prepared messages.
     *
     * Will return an integer ID that can be used to:
     *
     * - Get the current broadcast progress with getBroadcastProgress
     * - Cancel the broadcast using cancelBroadcast
     *
     * Note that to avoid manually polling the progress,
     * MadelineProto will also periodically emit updateBroadcastProgress updates,
     * containing a Progress object for all broadcasts currently in-progress.
     *
     * @param array         $messages The messages to send: an array of arrays, containing parameters to pass to messages.sendMessage.
     * @param bool          $pin      Whether to also pin the last sent message.
     * @param float|null    $delay    Number of seconds to wait between each peer.
     */
    final public function broadcastMessages(array $messages, ?\danog\MadelineProto\Broadcast\Filter $filter = null, bool $pin = false, ?float $delay = null): int
    {
        return $this->wrapper->getAPI()->broadcastMessages($messages, $filter, $pin, $delay);
    }
    /**
     * Fork a new green thread and execute the passed function in the background.
     *
     * @template T
     *
     * @param \Closure(...):T $callable Function to execute
     * @param mixed ...$args Arguments forwarded to the function when forking the thread.
     *
     * @return Future<T>
     *
     * @psalm-suppress InvalidScope
     */
    final public static function callFork(\Generator|\Amp\Future|callable $callable, ...$args): \Amp\Future
    {
        return \danog\MadelineProto\AsyncTools::callFork($callable, ...$args);
    }
    /**
     * Get the file that is currently being played.
     *
     * Will return a string with the object ID of the stream if we're currently playing a stream, otherwise returns the related LocalFile or RemoteUrl.
     *
     * @psalm-mutation-free
     */
    final public function callGetCurrent(int $id, \danog\MadelineProto\MediaDestination $dest = \danog\MadelineProto\MediaDestination::Camera): \danog\MadelineProto\RemoteUrl|\danog\MadelineProto\LocalFile|string|null
    {
        return $this->wrapper->getAPI()->callGetCurrent($id, $dest);
    }
    /**
     * Play file in call.
     */
    final public function callPlay(int $id, \danog\MadelineProto\LocalFile|\danog\MadelineProto\RemoteUrl|\Amp\ByteStream\ReadableStream $file, \danog\MadelineProto\MediaDestination $dest = \danog\MadelineProto\MediaDestination::Camera): void
    {
        $this->wrapper->getAPI()->callPlay($id, $file, $dest);
    }
    /**
     * Play files on hold in call.
     */
    final public function callPlayOnHold(int $id, \danog\MadelineProto\MediaDestination $dest = \danog\MadelineProto\MediaDestination::Camera, \danog\MadelineProto\LocalFile|\danog\MadelineProto\RemoteUrl|\Amp\ByteStream\ReadableStream ...$files): void
    {
        $this->wrapper->getAPI()->callPlayOnHold($id, $dest, ...$files);
    }
    /**
     * Record the incoming media of a call into one file (or stream) with a fixed set of tracks, see
     * {@see \danog\MadelineProto\EventHandler\Calls\PrivateCall::setOutput()}.
     *
     * @param ?StreamMask $streams The streams to record, as a bitmask of {@see CallStream} flags, or null for every available one.
     *
     * @return StreamMask The streams the other party currently sends, as a bitmask of {@see CallStream} flags (0 while unknown).
     */
    final public function callSetOutput(int $id, \danog\MadelineProto\LocalFile|\Amp\ByteStream\WritableStream $file, ?\danog\MadelineProto\RecordingFormat $format = null, ?int $streams = null): int
    {
        return $this->wrapper->getAPI()->callSetOutput($id, $file, $format, $streams);
    }
    /**
     * Record the incoming media of a call into a directory, one numbered file per combination of
     * streams the other party sends, see {@see \danog\MadelineProto\EventHandler\Calls\PrivateCall::setOutputFolder()}.
     */
    final public function callSetOutputFolder(int $id, \danog\MadelineProto\LocalDirectory $dir, ?\danog\MadelineProto\RecordingFormat $format = null): void
    {
        $this->wrapper->getAPI()->callSetOutputFolder($id, $dir, $format);
    }
    /**
     * Whether we can convert any audio/video file to a VoIP OGG OPUS file, or the files must be preconverted using @libtgvoipbot.
     */
    final public static function canConvertOgg(): bool
    {
        return \danog\MadelineProto\Tools::canConvertOgg();
    }
    /**
     * Whether we can convert any audio/video file using ffmpeg.
     */
    final public static function canUseFFmpeg(?\Amp\Cancellation $cancellation = null): bool
    {
        return \danog\MadelineProto\Tools::canUseFFmpeg($cancellation);
    }
    /**
     * Cancel a running broadcast.
     *
     * @param integer $id Broadcast ID
     *
     */
    final public function cancelBroadcast(int $id): void
    {
        $this->wrapper->getAPI()->cancelBroadcast($id);
    }
    /**
     * Close connection with client, connected via web.
     *
     * @param string $message Message
     */
    final public static function closeConnection(string $message): void
    {
        \danog\MadelineProto\Tools::closeConnection($message);
    }
    /**
     * Complete 2FA login.
     *
     * @param string $password Password
     */
    final public function complete2faLogin(string $password): array
    {
        return $this->wrapper->getAPI()->complete2faLogin($password);
    }
    /**
     * Complet user login using login code.
     *
     * @param string $code Login code
     */
    final public function completePhoneLogin(string $code): array
    {
        return $this->wrapper->getAPI()->completePhoneLogin($code);
    }
    /**
     * Complete signup to Telegram.
     *
     * @param string $first_name First name
     * @param string $last_name  Last name
     */
    final public function completeSignup(string $first_name, string $last_name = ''): array
    {
        return $this->wrapper->getAPI()->completeSignup($first_name, $last_name);
    }
    /**
     * Create and join a new end-to-end encrypted conference call, with ourselves as the only
     * participant. Others join it with {@see self::joinConferenceCall()} using the returned call id.
     *
     * See [end-to-end encrypted group calls Â»](https://core.telegram.org/api/end-to-end/group-calls).
     * Uses the `sodium` and `openssl` PHP extensions if available, for speed.
     *
     * @param bool $muted Whether to join muted.
     */
    final public function createConferenceCall(bool $muted = false): \danog\MadelineProto\EventHandler\Calls\ConferenceCall
    {
        return $this->wrapper->getAPI()->createConferenceCall($muted);
    }
    /**
     * Create a group call (video chat or livestream) in the specified group or channel.
     *
     * Requires the `manage_call` admin right, see
     * [video chats/livestreams Â»](https://core.telegram.org/api/group-calls#video-chats-livestreams).
     *
     * @param string|int       $peer         The group or channel where the call should be created.
     * @param string|null $title        Custom title, defaults to the group/channel name.
     * @param int|null    $scheduleDate If set, creates a scheduled call for the specified UNIX timestamp.
     * @param bool        $rtmpStream   Whether the call's media is published by an external RTMP application.
     */
    final public function createGroupCall(string|int $peer, ?string $title = null, ?int $scheduleDate = null, bool $rtmpStream = false): \danog\MadelineProto\EventHandler\Calls\GroupCall
    {
        return $this->wrapper->getAPI()->createGroupCall($peer, $title, $scheduleDate, $rtmpStream);
    }
    /**
     * Decline an invitation to a conference call.
     *
     * @param int $msgId ID of the invitation message (a `messageActionConferenceCall`).
     */
    final public function declineConferenceCallInvite(int $msgId): void
    {
        $this->wrapper->getAPI()->declineConferenceCallInvite($msgId);
    }
    /**
     * Discard call.
     *
     * @param int<1, 5> $rating  Call rating in stars
     * @param string    $comment Additional comment on call quality.
     */
    final public function discardCall(int $id, \danog\MadelineProto\VoIP\DiscardReason $reason = \danog\MadelineProto\VoIP\DiscardReason::HANGUP, ?int $rating = null, ?string $comment = null): void
    {
        $this->wrapper->getAPI()->discardCall($id, $reason, $rating, $comment);
    }
    /**
     * End a group call for all participants.
     */
    final public function discardGroupCall(int $id): void
    {
        $this->wrapper->getAPI()->discardGroupCall($id);
    }
    /**
     * Discard secret chat.
     *
     * @param int $chat Secret chat ID
     * @param bool $deleteHistory If true, deletes the entire chat history for the other user as well.
     */
    final public function discardSecretChat(int $chat, bool $deleteHistory = false): void
    {
        $this->wrapper->getAPI()->discardSecretChat($chat, $deleteHistory);
    }
    /**
     * Downloads a file to the browser using the specified session file.
     */
    final public static function downloadServer(string $session): void
    {
        \danog\MadelineProto\MTProto::downloadServer($session);
    }
    /**
     * Download file to browser.
     *
     * Supports HEAD requests and content-ranges for parallel and resumed downloads.
     *
     * @param array|string|FileCallbackInterface|\danog\MadelineProto\EventHandler\Message $messageMedia File to download
     * @param null|callable                                                                $cb           Status callback (can also use FileCallback)
     * @param null|int                                                                     $size         Size of file to download, required for bot API file IDs.
     * @param null|string                                                                  $mime         MIME type of file to download, required for bot API file IDs.
     * @param null|string                                                                  $name         Name of file to download, required for bot API file IDs.
     */
    final public function downloadToBrowser(\danog\MadelineProto\FileCallbackInterface|\danog\MadelineProto\EventHandler\Message|array|string $messageMedia, ?callable $cb = null, ?int $size = null, ?string $name = null, ?string $mime = null, ?\Amp\Cancellation $cancellation = null): void
    {
        $this->wrapper->getAPI()->downloadToBrowser($messageMedia, $cb, $size, $name, $mime, $cancellation);
    }
    /**
     * Download file to callable.
     * The callable must accept two parameters: string $payload, int $offset
     * The callable will be called (possibly out of order, depending on the value of $seekable).
     *
     * @param mixed                          $messageMedia File to download
     * @param callable|FileCallbackInterface $callable     Chunk callback
     * @param callable                       $cb           Status callback
     * @param bool                           $seekable     Whether the callable can be called out of order
     * @param int                            $offset       Offset where to start downloading
     * @param int                            $end          Offset where to stop downloading (inclusive)
     * @param int                            $part_size    Size of each chunk
     */
    final public function downloadToCallable(mixed $messageMedia, callable $callable, ?callable $cb = null, bool $seekable = true, int $offset = 0, int $end = -1, ?int $part_size = null, ?\Amp\Cancellation $cancellation = null): void
    {
        $this->wrapper->getAPI()->downloadToCallable($messageMedia, $callable, $cb, $seekable, $offset, $end, $part_size, $cancellation);
    }
    /**
     * Download file to directory.
     *
     * @param mixed                        $messageMedia File to download
     * @param string|FileCallbackInterface $dir          Directory where to download the file
     * @param callable                     $cb           Callback
     *
     * @return non-empty-string Downloaded file name
     */
    final public function downloadToDir(mixed $messageMedia, \danog\MadelineProto\FileCallbackInterface|string $dir, ?callable $cb = null, ?\Amp\Cancellation $cancellation = null): string
    {
        return $this->wrapper->getAPI()->downloadToDir($messageMedia, $dir, $cb, $cancellation);
    }
    /**
     * Download file.
     *
     * @param mixed                        $messageMedia File to download
     * @param string|FileCallbackInterface $file         Downloaded file path
     * @param callable                     $cb           Callback
     *
     * @return non-empty-string Downloaded file name
     */
    final public function downloadToFile(mixed $messageMedia, \danog\MadelineProto\FileCallbackInterface|string $file, ?callable $cb = null, ?\Amp\Cancellation $cancellation = null): string
    {
        return $this->wrapper->getAPI()->downloadToFile($messageMedia, $file, $cb, $cancellation);
    }
    /**
     * Download file to amphp/http-server response.
     *
     * Supports HEAD requests and content-ranges for parallel and resumed downloads.
     *
     * @param array|string|FileCallbackInterface|\danog\MadelineProto\EventHandler\Message $messageMedia File to download
     * @param ServerRequest                                                                $request      Request
     * @param callable                                                                     $cb           Status callback (can also use FileCallback)
     * @param null|int                                                                     $size         Size of file to download, required for bot API file IDs.
     * @param null|string                                                                  $name         Name of file to download, required for bot API file IDs.
     * @param null|string                                                                  $mime         MIME type of file to download, required for bot API file IDs.
     */
    final public function downloadToResponse(\danog\MadelineProto\FileCallbackInterface|\danog\MadelineProto\EventHandler\Message|array|string $messageMedia, \Amp\Http\Server\Request $request, ?callable $cb = null, ?int $size = null, ?string $mime = null, ?string $name = null, ?\Amp\Cancellation $cancellation = null): \Amp\Http\Server\Response
    {
        return $this->wrapper->getAPI()->downloadToResponse($messageMedia, $request, $cb, $size, $mime, $name, $cancellation);
    }
    /**
     * Download file to an amphp stream, returning it.
     *
     * The returned stream is resumable: it can be serialized, and once unserialized,
     * reading continues from the end of the last chunk read before serialization.
     * The progress callback and cancellation are not preserved by serialization.
     *
     * @param mixed    $messageMedia File to download
     * @param callable $cb           Callback
     * @param int      $offset       Offset where to start downloading
     * @param int      $end          Offset where to end download
     */
    final public function downloadToReturnedStream(mixed $messageMedia, ?callable $cb = null, int $offset = 0, int $end = -1, ?\Amp\Cancellation $cancellation = null): \Amp\ByteStream\ReadableStream&\danog\MadelineProto\ResumableStream
    {
        return $this->wrapper->getAPI()->downloadToReturnedStream($messageMedia, $cb, $offset, $end, $cancellation);
    }
    /**
     * Download file to stream.
     *
     * @param mixed                                               $messageMedia File to download
     * @param mixed|FileCallbackInterface|resource|WritableStream $stream       Stream where to download file
     * @param callable                                            $cb           Callback
     * @param int                                                 $offset       Offset where to start downloading
     * @param int                                                 $end          Offset where to end download
     */
    final public function downloadToStream(mixed $messageMedia, mixed $stream, ?callable $cb = null, int $offset = 0, int $end = -1, ?\Amp\Cancellation $cancellation = null): void
    {
        $this->wrapper->getAPI()->downloadToStream($messageMedia, $stream, $cb, $offset, $end, $cancellation);
    }
    /**
     * Asynchronously write to stdout/browser.
     *
     * @param string $string Message to echo
     */
    final public static function echo(string $string): void
    {
        \danog\MadelineProto\AsyncTools::echo($string);
    }
    /**
     * Get final element of array.
     *
     * @template T
     *
     * @param array<T> $what Array
     *
     * @return T
     *
     * @psalm-pure
     */
    final public static function end(array $what): mixed
    {
        return \danog\MadelineProto\Tools::end($what);
    }
    /**
     * Convert a message and a set of entities to HTML.
     *
     * @param list<MessageEntity|array{_: string, offset: int, length: int}> $entities
     * @param bool                                                           $allowTelegramTags Whether to allow telegram-specific tags like tg-spoiler, tg-emoji, mention links and so on...
     */
    final public static function entitiesToHtml(string $message, array $entities, bool $allowTelegramTags = false): string
    {
        return \danog\MadelineProto\StrTools::entitiesToHtml($message, $entities, $allowTelegramTags);
    }
    /**
     * Export authorization.
     *
     * @return array{0: int, 1: string}
     */
    final public function exportAuthorization(): array
    {
        return $this->wrapper->getAPI()->exportAuthorization();
    }
    /**
     * Export an invite link for a group call.
     *
     * @param bool $canSelfUnmute Whether users joining with this link may speak without asking; admins only.
     */
    final public function exportGroupCallInvite(int $id, bool $canSelfUnmute = false): string
    {
        return $this->wrapper->getAPI()->exportGroupCallInvite($id, $canSelfUnmute);
    }
    /**
     * Extract file info from bot API message.
     *
     * @param array $info Bot API message object
     *
     * @psalm-pure
     */
    final public static function extractBotAPIFile(array $info): ?array
    {
        return \danog\MadelineProto\MTProto::extractBotAPIFile($info);
    }
    /**
     * Extract a message constructor from an Updates constructor.
     */
    final public function extractMessage(array $updates): array
    {
        return $this->wrapper->getAPI()->extractMessage($updates);
    }
    /**
     * Extract a message ID from an Updates constructor.
     */
    final public function extractMessageId(array $updates): int
    {
        return $this->wrapper->getAPI()->extractMessageId($updates);
    }
    /**
     * Extract an update message constructor from an Updates constructor.
     */
    final public function extractMessageUpdate(array $updates): array
    {
        return $this->wrapper->getAPI()->extractMessageUpdate($updates);
    }
    /**
     * Extract Update constructors from an Updates constructor.
     *
     * @return array<array>
     */
    final public function extractUpdates(array $updates): array
    {
        return $this->wrapper->getAPI()->extractUpdates($updates);
    }
    /**
     * Get contents of remote file asynchronously.
     *
     * @param string $url URL
     */
    final public function fileGetContents(string $url, ?\Amp\Cancellation $cancellation = null): string
    {
        return $this->wrapper->getAPI()->fileGetContents($url, $cancellation);
    }
    /**
     * Asynchronously lock a file
     * Resolves with a callbable that MUST eventually be called in order to release the lock.
     *
     * @param  string                                                          $file      File to lock
     * @param  integer                                                         $operation Locking mode
     * @param  float                                                           $polling   Polling interval
     * @param  ?Cancellation                                                   $token     Cancellation token
     * @param  ?Closure                                                        $failureCb Failure callback, called only once if the first locking attempt fails.
     * @return ($token is null ? (Closure(): void) : ((Closure(): void)|null))
     */
    final public static function flock(string $file, int $operation, float $polling = 0.1, ?\Amp\Cancellation $token = null, ?\Closure $failureCb = null): ?\Closure
    {
        return \danog\MadelineProto\AsyncTools::flock($file, $operation, $polling, $token, $failureCb);
    }
    /**
     * When was full info for this chat last cached.
     *
     * @param mixed $id Chat ID
     */
    final public function fullChatLastUpdated(mixed $id): int
    {
        return $this->wrapper->getAPI()->fullChatLastUpdated($id);
    }
    /**
     * Get info about the logged-in user, not cached.
     */
    final public function fullGetSelf(): array|false
    {
        return $this->wrapper->getAPI()->fullGetSelf();
    }
    /**
     * Generate MTProto vector hash.
     *
     * Returns a vector hash.
     *
     * @param array<string|int> $longs IDs
     *
     * @psalm-external-mutation-free
     */
    final public static function genVectorHash(array $longs): string
    {
        return \danog\MadelineProto\Tools::genVectorHash($longs);
    }
    /**
     * Get admin IDs (equal to all user report peers).
     */
    final public function getAdminIds(): array
    {
        return $this->wrapper->getAPI()->getAdminIds();
    }
    /**
     * Get all pending and running calls, indexed by user ID.
     *
     * @return array<int, PrivateCall>
     */
    final public function getAllCalls(): array
    {
        return $this->wrapper->getAPI()->getAllCalls();
    }
    /**
     * Get all group calls (video chats, livestreams and live stories) we're currently tracking, indexed by their ID.
     *
     * @return array<int, AbstractGroupCall>
     */
    final public function getAllGroupCalls(): array
    {
        return $this->wrapper->getAPI()->getAllGroupCalls();
    }
    /**
     * Get full list of MTProto and API methods.
     */
    final public function getAllMethods(): array
    {
        return $this->wrapper->getAPI()->getAllMethods();
    }
    /**
     * Get authorization info.
     *
     * @return \danog\MadelineProto\API::NOT_LOGGED_IN|\danog\MadelineProto\API::WAITING_CODE|\danog\MadelineProto\API::WAITING_SIGNUP|\danog\MadelineProto\API::WAITING_PASSWORD|\danog\MadelineProto\API::LOGGED_IN|API::LOGGED_OUT
     *
     * @psalm-mutation-free
     */
    final public function getAuthorization(): int
    {
        return $this->wrapper->getAPI()->getAuthorization();
    }
    /**
     * Get the progress of a currently running broadcast.
     *
     * Will return null if the broadcast doesn't exist, has already completed or was cancelled.
     *
     * Use updateBroadcastProgress updates to get real-time progress status without polling.
     *
     * @param integer $id Broadcast ID
     *
     * @psalm-mutation-free
     */
    final public function getBroadcastProgress(int $id): ?\danog\MadelineProto\Broadcast\Progress
    {
        return $this->wrapper->getAPI()->getBroadcastProgress($id);
    }
    /**
     * Get cached server-side config.
     */
    final public function getCachedConfig(): array
    {
        return $this->wrapper->getAPI()->getCachedConfig();
    }
    /**
     * Get phone call information.
     *
     * @psalm-mutation-free
     */
    final public function getCall(int $id): ?\danog\MadelineProto\EventHandler\Calls\PrivateCall
    {
        return $this->wrapper->getAPI()->getCall($id);
    }
    /**
     * Get the phone call with the specified user ID.
     *
     * @psalm-mutation-free
     */
    final public function getCallByPeer(int $userId): ?\danog\MadelineProto\EventHandler\Calls\PrivateCall
    {
        return $this->wrapper->getAPI()->getCallByPeer($userId);
    }
    /**
     * Get the media state of the other party of a call, as reported by their client.
     *
     * @psalm-mutation-free
     */
    final public function getCallRemoteMediaState(int $id): ?\danog\MadelineProto\VoIP\MediaState
    {
        return $this->wrapper->getAPI()->getCallRemoteMediaState($id);
    }
    /**
     * Get call state.
     *
     * @psalm-mutation-free
     */
    final public function getCallState(int $id): ?\danog\MadelineProto\VoIP\CallState
    {
        return $this->wrapper->getAPI()->getCallState($id);
    }
    /**
     * Store RSA keys for CDN datacenters.
     */
    final public function getCdnConfig(): void
    {
        $this->wrapper->getAPI()->getCdnConfig();
    }
    /**
     * Get a live end-to-end encrypted conference call this session is in, by its call id.
     *
     * @psalm-mutation-free
     */
    final public function getConferenceCall(int $id): ?\danog\MadelineProto\EventHandler\Calls\ConferenceCall
    {
        return $this->wrapper->getAPI()->getConferenceCall($id);
    }
    /**
     * Get an [end-to-end encrypted conference call Â»](https://core.telegram.org/api/group-calls#conference-calls)
     * from its [conference link Â»](https://core.telegram.org/api/links#conference-links) slug, or null if
     * the link is not valid any more.
     */
    final public function getConferenceCallBySlug(string $slug): ?\danog\MadelineProto\EventHandler\Calls\ConferenceCall
    {
        return $this->wrapper->getAPI()->getConferenceCallBySlug($slug);
    }
    /**
     * Get cached (or eventually re-fetch) server-side config.
     *
     * @param array $config Current config
     */
    final public function getConfig(array $config =  [
    ]): array
    {
        return $this->wrapper->getAPI()->getConfig($config);
    }
    /**
     * Get async DNS client.
     *
     * @psalm-mutation-free
     */
    final public function getDNSClient(): \Amp\Dns\DnsResolver
    {
        return $this->wrapper->getAPI()->getDNSClient();
    }
    /**
     * Get diffie-hellman configuration.
     */
    final public function getDhConfig(?\Amp\Cancellation $cancellation = null): array
    {
        return $this->wrapper->getAPI()->getDhConfig($cancellation);
    }
    /**
     * Get dialog IDs.
     *
     * @return list<int>
     */
    final public function getDialogIds(): array
    {
        return $this->wrapper->getAPI()->getDialogIds();
    }
    /**
     * Get download info of file
     * Returns an array with the following structure:.
     *
     * `$info['ext']` - The file extension
     * `$info['name']` - The file name, without the extension
     * `$info['mime']` - The file mime type
     * `$info['size']` - The file size
     *
     * @param mixed $messageMedia File ID
     *
     * @return array{
     *      ext: string,
     *      name: string,
     *      mime: string,
     *      size: int,
     *      InputFileLocation: array,
     *      key_fingerprint?: string,
     *      key?: string,
     *      iv?: string,
     *      thumb_size?: string
     * }
     */
    final public function getDownloadInfo(mixed $messageMedia): array
    {
        return $this->wrapper->getAPI()->getDownloadInfo($messageMedia);
    }
    /**
     * Get download link of media file.
     */
    final public function getDownloadLink(\danog\MadelineProto\EventHandler\Message|\danog\MadelineProto\EventHandler\Media|array|string $media, ?string $scriptUrl = null, ?int $size = null, ?string $name = null, ?string $mime = null): string
    {
        return $this->wrapper->getAPI()->getDownloadLink($media, $scriptUrl, $size, $name, $mime);
    }
    /**
     * Get event handler (or plugin instance).
     *
     * @template T as EventHandler
     *
     * @param class-string<T>|null $class
     *
     * @return T|EventHandlerProxy|__PHP_Incomplete_Class|null
     *
     * @psalm-mutation-free
     */
    final public function getEventHandler(?string $class = null): \danog\MadelineProto\EventHandler|\danog\MadelineProto\Ipc\EventHandlerProxy|\__PHP_Incomplete_Class|null
    {
        return $this->wrapper->getAPI()->getEventHandler($class);
    }
    /**
     * Get extension from file location.
     *
     * @param mixed  $location File location
     * @param string $default  Default extension
     *
     * @psalm-pure
     */
    final public static function getExtensionFromLocation(mixed $location, string $default): string
    {
        return \danog\MadelineProto\TL\Conversion\Extension::getExtensionFromLocation($location, $default);
    }
    /**
     * Get extension from mime type.
     *
     * @param string $mime MIME type
     *
     * @psalm-external-mutation-free
     */
    final public static function getExtensionFromMime(string $mime): string
    {
        return \danog\MadelineProto\TL\Conversion\Extension::getExtensionFromMime($mime);
    }
    /**
     * Get info about file.
     *
     * @param mixed $constructor File ID
     */
    final public function getFileInfo(mixed $constructor): array
    {
        return $this->wrapper->getAPI()->getFileInfo($constructor);
    }
    /**
     * Get full info of all dialogs.
     *
     * Bots should use getDialogIds, instead.
     *
     * @return array<int, array>
     */
    final public function getFullDialogs(): array
    {
        return $this->wrapper->getAPI()->getFullDialogs();
    }
    /**
     * Get full info about peer, returns an FullInfo object.
     *
     * @param mixed $id Peer
     * @see https://docs.madelineproto.xyz/FullInfo.html
     */
    final public function getFullInfo(mixed $id): array
    {
        return $this->wrapper->getAPI()->getFullInfo($id);
    }
    /**
     * Get the group call (video chat or livestream) currently active in a group or channel.
     */
    final public function getGroupCall(string|int $peer): ?\danog\MadelineProto\EventHandler\Calls\GroupCall
    {
        return $this->wrapper->getAPI()->getGroupCall($peer);
    }
    /**
     * Get a group call from its
     * [conference deep link Â»](https://core.telegram.org/api/links#conference-links) slug.
     */
    final public function getGroupCallBySlug(string $slug): ?\danog\MadelineProto\EventHandler\Calls\GroupCall
    {
        return $this->wrapper->getAPI()->getGroupCallBySlug($slug);
    }
    /**
     * The peers we may join the video chats and livestreams of a group or channel as: ourselves, the
     * channels we own, and (for anonymous admins) the group itself. Bot API IDs.
     *
     * See [joining a group call on behalf of owned channels Â»](https://core.telegram.org/api/group-calls#joining-a-group-call-on-behalf-of-owned-channels).
     *
     * @return list<int>
     */
    final public function getGroupCallJoinAs(string|int $peer): array
    {
        return $this->wrapper->getAPI()->getGroupCallJoinAs($peer);
    }
    /**
     * Get the participants of a group call, indexed by their bot API peer ID.
     *
     * @return array<int, AbstractGroupCallParticipant>
     *
     * @psalm-mutation-free
     */
    final public function getGroupCallParticipants(int $id): array
    {
        return $this->wrapper->getAPI()->getGroupCallParticipants($id);
    }
    /**
     * Get the state of a group call.
     *
     * @psalm-mutation-free
     */
    final public function getGroupCallState(int $id): \danog\MadelineProto\EventHandler\Calls\GroupCallState
    {
        return $this->wrapper->getAPI()->getGroupCallState($id);
    }
    /**
     * Get the RTMP URL and stream key to publish an [RTMP livestream Â»](https://core.telegram.org/api/group-calls#creating-and-publishing-an-rtmp-livestream)
     * to, in a group or channel (create the call with `rtmpStream` afterwards, see {@see self::createGroupCall()}),
     * or as a live story (see {@see self::startLive()}).
     *
     * @param string|int $peer      The group or channel (or, for a live story, the user, group or channel it is posted as).
     * @param bool  $revoke    Whether to generate a new stream key, invalidating the previous one.
     * @param bool  $liveStory Whether the key is for a live story rather than a video chat/livestream.
     *
     * @return array{url: string, key: string}
     */
    final public function getGroupCallStreamRtmpUrl(string|int $peer, bool $revoke = false, bool $liveStory = false): array
    {
        return $this->wrapper->getAPI()->getGroupCallStreamRtmpUrl($peer, $revoke, $liveStory);
    }
    /**
     * Get async HTTP client.
     *
     * @psalm-mutation-free
     */
    final public function getHTTPClient(): \Amp\Http\Client\HttpClient
    {
        return $this->wrapper->getAPI()->getHTTPClient();
    }
    /**
     * Get current password hint.
     *
     * @psalm-mutation-free
     */
    final public function getHint(): string
    {
        return $this->wrapper->getAPI()->getHint();
    }
    /**
     * Get the bot API ID of a peer.
     *
     * @param mixed $id Peer
     */
    final public function getId(mixed $id): int
    {
        return $this->wrapper->getAPI()->getId($id);
    }
    /**
     * Get info about peer, returns an Info object.
     *
     * If passed a secret chat ID, returns information about the user, not about the secret chat.
     * Use getSecretChat to return information about the secret chat.
     *
     * @param mixed                                 $id   Peer
     * @param \danog\MadelineProto\API::INFO_TYPE_* $type Whether to generate an Input*, an InputPeer or the full set of constructors
     * @see https://docs.madelineproto.xyz/Info.html
     * @return ($type is \danog\MadelineProto\API::INFO_TYPE_ALL ? array{
     *      User?: array,
     *      Chat?: array,
     *      bot_api_id: int,
     *      user_id?: int,
     *      chat_id?: int,
     *      channel_id?: int,
     *      type: string
     * } : ($type is API::INFO_TYPE_TYPE ? string : ($type is \danog\MadelineProto\API::INFO_TYPE_ID ? int : array{_: string, user_id?: int, access_hash?: int, min?: bool, chat_id?: int, channel_id?: int}|array{_: string, user_id?: int, access_hash?: int, min?: bool}|array{_: string, channel_id: int, access_hash: int, min: bool})))
     */
    final public function getInfo(mixed $id, int $type = \danog\MadelineProto\API::INFO_TYPE_ALL): array|string|int
    {
        return $this->wrapper->getAPI()->getInfo($id, $type);
    }
    /**
     * Get logger.
     */
    final public function getLogger(): \danog\MadelineProto\Logger
    {
        return $this->wrapper->getAPI()->getLogger();
    }
    /**
     * Get current number of memory-mapped regions, UNIX only.
     */
    final public static function getMaps(): ?int
    {
        return \danog\MadelineProto\Tools::getMaps();
    }
    /**
     * Get maximum number of memory-mapped regions, UNIX only.
     * Use testFibers to get the maximum number of fibers on any platform.
     */
    final public static function getMaxMaps(): ?int
    {
        return \danog\MadelineProto\Tools::getMaxMaps();
    }
    /**
     * Get memory profile with memprof.
     */
    final public function getMemoryProfile(): string
    {
        return $this->wrapper->getAPI()->getMemoryProfile();
    }
    /**
     * Get TL namespaces.
     *
     * @psalm-mutation-free
     */
    final public function getMethodNamespaces(): array
    {
        return $this->wrapper->getAPI()->getMethodNamespaces();
    }
    /**
     * Get namespaced methods (method => namespace).
     *
     * @psalm-mutation-free
     */
    final public function getMethodsNamespaced(): array
    {
        return $this->wrapper->getAPI()->getMethodsNamespaced();
    }
    /**
     * Get mime type from buffer.
     *
     * @param string $buffer Buffer
     */
    final public static function getMimeFromBuffer(string $buffer): string
    {
        return \danog\MadelineProto\TL\Conversion\Extension::getMimeFromBuffer($buffer);
    }
    /**
     * Get mime type from file extension.
     *
     * @param string $extension File extension
     * @param string $default   Default mime type
     *
     * @psalm-pure
     */
    final public static function getMimeFromExtension(string $extension, string $default): string
    {
        return \danog\MadelineProto\TL\Conversion\Extension::getMimeFromExtension($extension, $default);
    }
    /**
     * Get mime type of file.
     *
     * @param string $file File
     */
    final public static function getMimeFromFile(string $file): string
    {
        return \danog\MadelineProto\TL\Conversion\Extension::getMimeFromFile($file);
    }
    /**
     * Obtain a certain event handler plugin instance.
     *
     * @template T as EventHandler
     *
     * @param class-string<T> $class

     * return T|null
     *
     * @psalm-mutation-free
     */
    final public function getPlugin(string $class): \danog\MadelineProto\PluginEventHandler|\danog\MadelineProto\Ipc\EventHandlerProxy|null
    {
        return $this->wrapper->getAPI()->getPlugin($class);
    }
    /**
     * Creates and returns a prometheus counter.
     *
     * Returns null if prometheus stats are disabled.
     *
     * @param array<string, string> $labels
     *
     * @psalm-external-mutation-free
     */
    final public function getPromCounter(string $namespace, string $name, string $help, array $labels =  [
    ]): ?\danog\BetterPrometheus\BetterCounter
    {
        return $this->wrapper->getAPI()->getPromCounter($namespace, $name, $help, $labels);
    }
    /**
     * Creates and returns a prometheus gauge.
     *
     * Returns null if prometheus stats are disabled.
     *
     * @param array<string, string> $labels
     *
     * @psalm-external-mutation-free
     */
    final public function getPromGauge(string $namespace, string $name, string $help, array $labels =  [
    ]): ?\danog\BetterPrometheus\BetterGauge
    {
        return $this->wrapper->getAPI()->getPromGauge($namespace, $name, $help, $labels);
    }
    /**
     * Creates and returns a prometheus histogram.
     *
     * Returns null if prometheus stats are disabled.
     *
     * @param array<string, string> $labels
     * @param ?non-empty-list<float> $buckets
     *
     * @psalm-external-mutation-free
     */
    final public function getPromHistogram(string $namespace, string $name, string $help, array $labels =  [
    ], ?array $buckets = null): ?\danog\BetterPrometheus\BetterHistogram
    {
        return $this->wrapper->getAPI()->getPromHistogram($namespace, $name, $help, $labels, $buckets);
    }
    /**
     * Creates and returns a prometheus summary.
     *
     * Returns null if prometheus stats are disabled.
     *
     * @param array<string, string> $labels
     * @param ?non-empty-list<float> $quantiles
     *
     * @psalm-external-mutation-free
     */
    final public function getPromSummary(string $namespace, string $name, string $help, array $labels =  [
    ], int $maxAgeSeconds = 600, ?array $quantiles = null): ?\danog\BetterPrometheus\BetterSummary
    {
        return $this->wrapper->getAPI()->getPromSummary($namespace, $name, $help, $labels, $maxAgeSeconds, $quantiles);
    }
    /**
     * Gets info of the propic of a user.
     */
    final public function getPropicInfo($data): ?\danog\MadelineProto\EventHandler\Media\Photo
    {
        return $this->wrapper->getAPI()->getPropicInfo($data);
    }
    /**
     * Get PSR logger.
     *
     * @psalm-mutation-free
     */
    final public function getPsrLogger(): \Psr\Log\LoggerInterface
    {
        return $this->wrapper->getAPI()->getPsrLogger();
    }
    /**
     * Get full info about peer (including full list of channel members), returns a Chat object.
     *
     * @param mixed $id Peer
     * @see https://docs.madelineproto.xyz/Chat.html
     */
    final public function getPwrChat(mixed $id, bool $fullfetch = true): array
    {
        return $this->wrapper->getAPI()->getPwrChat($id, $fullfetch);
    }
    /**
     * Get secret chat.
     *
     * @param array|int $chat Secret chat ID
     *
     * @psalm-mutation-free
     */
    final public function getSecretChat(array|int $chat): \danog\MadelineProto\SecretChats\SecretChat
    {
        return $this->wrapper->getAPI()->getSecretChat($chat);
    }
    /**
     * Gets a secret chat message.
     *
     * @param integer $chatId Secret chat ID.
     * @param integer $randomId Secret chat message ID.
     */
    final public function getSecretMessage(int $chatId, int $randomId): \danog\MadelineProto\EventHandler\Message\SecretMessage
    {
        return $this->wrapper->getAPI()->getSecretMessage($chatId, $randomId);
    }
    /**
     * Get info about the logged-in user, cached.
     *
     * Use fullGetSelf to bypass the cache.
     *
     * @psalm-mutation-free
     */
    final public function getSelf(): array|false
    {
        return $this->wrapper->getAPI()->getSelf();
    }
    /**
     * Returns the session name.
     *
     * @psalm-mutation-free
     */
    final public function getSessionName(): string
    {
        return $this->wrapper->getAPI()->getSessionName();
    }
    /**
     * Return current settings.
     */
    final public function getSettings(): \danog\MadelineProto\Settings
    {
        return $this->wrapper->getAPI()->getSettings();
    }
    /**
     * Get sponsored messages for channel.
     * This method will return an array of [sponsored message objects](https://docs.madelineproto.xyz/API_docs/constructors/sponsoredMessage.html).
     *
     * See [the API documentation](https://core.telegram.org/api/sponsored-messages) for more info on how to handle sponsored messages.
     *
     * @param int|string|array $peer Channel ID, or Update, or Message, or Peer.
     */
    final public function getSponsoredMessages(array|string|int $peer): ?array
    {
        return $this->wrapper->getAPI()->getSponsoredMessages($peer);
    }
    /**
     * Provide a stream for a file, URL or amp stream.
     */
    final public function getStream(\danog\MadelineProto\EventHandler\Message|\danog\MadelineProto\EventHandler\Media|\danog\MadelineProto\LocalFile|\danog\MadelineProto\RemoteUrl|\danog\MadelineProto\BotApiFileId|\Amp\ByteStream\ReadableStream $stream, ?\Amp\Cancellation $cancellation = null, ?int &$size = null): \Amp\ByteStream\ReadableStream
    {
        return $this->wrapper->getAPI()->getStream($stream, $cancellation, $size);
    }
    /**
     * Obtains a pipe that can be used to upload a file from a stream.
     *
     */
    final public static function getStreamPipe(): \Amp\ByteStream\Pipe
    {
        return \danog\MadelineProto\Tools::getStreamPipe();
    }
    /**
     * Get type of peer.
     *
     * @param mixed $id Peer
     *
     * @return \danog\MadelineProto\API::PEER_TYPE_*
     */
    final public function getType(mixed $id): string
    {
        return $this->wrapper->getAPI()->getType($id);
    }
    /**
     * Only useful when consuming MadelineProto updates through an API in another language (like Javascript), **absolutely not recommended when directly writing MadelineProto bots**.
     *
     * `getUpdates` will **greatly slow down your bot** if used directly inside of PHP code.
     *
     * **Only use the [event handler](#async-event-driven) when writing a MadelineProto bot**, because update handling in the **event handler** is completely parallelized and non-blocking.
     *
     * @param  array{offset?: int, limit?: int, timeout?: float} $params       Params: once the timeout expires, an empty array is returned
     * @param  ?Cancellation                                     $cancellation Cancellation: once cancelled, the wait stops with a CancelledException
     * @return list<array{update_id: mixed, update: mixed}>
     */
    final public function getUpdates(array $params =  [
    ], ?\Amp\Cancellation $cancellation = null): array
    {
        return $this->wrapper->getAPI()->getUpdates($params, $cancellation);
    }
    /**
     * Get a message to show to the user when starting the bot.
     */
    final public function getWebMessage(string $message): string
    {
        return $this->wrapper->getAPI()->getWebMessage($message);
    }
    /**
     * Get various warnings to show to the user in the web UI.
     */
    final public static function getWebWarnings(): string
    {
        return \danog\MadelineProto\MTProto::getWebWarnings();
    }
    /**
     * Get the file that is currently being played in a group call.
     *
     * @psalm-mutation-free
     */
    final public function groupCallGetCurrent(int $id, \danog\MadelineProto\MediaDestination $dest = \danog\MadelineProto\MediaDestination::Camera): \danog\MadelineProto\RemoteUrl|\danog\MadelineProto\LocalFile|string|null
    {
        return $this->wrapper->getAPI()->groupCallGetCurrent($id, $dest);
    }
    /**
     * Pause playback of the current audio file in a group call.
     *
     * @psalm-external-mutation-free
     */
    final public function groupCallPausePlay(int $id, \danog\MadelineProto\MediaDestination $dest = \danog\MadelineProto\MediaDestination::Camera): void
    {
        $this->wrapper->getAPI()->groupCallPausePlay($id, $dest);
    }
    /**
     * Play a file in a group call.
     */
    final public function groupCallPlay(int $id, \danog\MadelineProto\LocalFile|\danog\MadelineProto\RemoteUrl|\Amp\ByteStream\ReadableStream $file, \danog\MadelineProto\MediaDestination $dest = \danog\MadelineProto\MediaDestination::Camera): void
    {
        $this->wrapper->getAPI()->groupCallPlay($id, $file, $dest);
    }
    /**
     * Files to play on hold in a group call.
     */
    final public function groupCallPlayOnHold(int $id, \danog\MadelineProto\MediaDestination $dest = \danog\MadelineProto\MediaDestination::Camera, \danog\MadelineProto\LocalFile|\danog\MadelineProto\RemoteUrl|\Amp\ByteStream\ReadableStream ...$files): void
    {
        $this->wrapper->getAPI()->groupCallPlayOnHold($id, $dest, ...$files);
    }
    /**
     * Resume playback of the current audio file in a group call.
     *
     * @psalm-external-mutation-free
     */
    final public function groupCallResumePlay(int $id, \danog\MadelineProto\MediaDestination $dest = \danog\MadelineProto\MediaDestination::Camera): void
    {
        $this->wrapper->getAPI()->groupCallResumePlay($id, $dest);
    }
    /**
     * Record one participant of a group call (or, in stream mode, its mixed stream) into a single file
     * (or stream) with a fixed set of tracks, see {@see \danog\MadelineProto\EventHandler\Calls\AbstractGroupCall::setOutput()}.
     *
     * @param ?StreamMask $streams The streams to record, as a bitmask of {@see CallStream} flags, or null for every available one.
     *
     * @return StreamMask The streams the participant currently sends, as a bitmask of {@see CallStream} flags.
     */
    final public function groupCallSetOutput(int $id, \danog\MadelineProto\LocalFile|\Amp\ByteStream\WritableStream $file, string|int|null $participant = null, ?\danog\MadelineProto\RecordingFormat $format = null, ?int $streams = null): int
    {
        return $this->wrapper->getAPI()->groupCallSetOutput($id, $file, $participant, $format, $streams);
    }
    /**
     * Record group call media into a directory: every transmitting participant (or only the given
     * one) as its own numbered series of files, see
     * {@see \danog\MadelineProto\EventHandler\Calls\AbstractGroupCall::setOutputFolder()}.
     */
    final public function groupCallSetOutputFolder(int $id, \danog\MadelineProto\LocalDirectory $dir, string|int|null $participant = null, ?\danog\MadelineProto\RecordingFormat $format = null): void
    {
        $this->wrapper->getAPI()->groupCallSetOutputFolder($id, $dir, $participant, $format);
    }
    /**
     * Skip to the next file in the playlist of a group call.
     */
    final public function groupCallSkipPlay(int $id, \danog\MadelineProto\MediaDestination $dest = \danog\MadelineProto\MediaDestination::Camera): void
    {
        $this->wrapper->getAPI()->groupCallSkipPlay($id, $dest);
    }
    /**
     * Stop playing all files in a group call, clearing the main and the hold playlist.
     */
    final public function groupCallStopPlay(int $id, \danog\MadelineProto\MediaDestination $dest = \danog\MadelineProto\MediaDestination::Camera): void
    {
        $this->wrapper->getAPI()->groupCallStopPlay($id, $dest);
    }
    /**
     * Check if has admins.
     *
     * @psalm-mutation-free
     */
    final public function hasAdmins(): bool
    {
        return $this->wrapper->getAPI()->hasAdmins();
    }
    /**
     * Check if an event handler instance is present.
     *
     * @psalm-mutation-free
     */
    final public function hasEventHandler(): bool
    {
        return $this->wrapper->getAPI()->hasEventHandler();
    }
    /**
     * Check if a certain event handler plugin is installed.
     *
     * @param class-string<EventHandler> $class
     *
     * @psalm-mutation-free
     */
    final public function hasPlugin(string $class): bool
    {
        return $this->wrapper->getAPI()->hasPlugin($class);
    }
    /**
     * Check if has report peers.
     *
     * @psalm-mutation-free
     */
    final public function hasReportPeers(): bool
    {
        return $this->wrapper->getAPI()->hasReportPeers();
    }
    /**
     * Check whether secret chat exists.
     *
     * @param array|int $chat Secret chat ID
     *
     * @psalm-mutation-free
     */
    final public function hasSecretChat(array|int $chat): bool
    {
        return $this->wrapper->getAPI()->hasSecretChat($chat);
    }
    /**
     * Escape string for MadelineProto's HTML entity converter.
     *
     * @param string $what String to escape
     *
     * @psalm-pure
     */
    final public static function htmlEscape(string $what): string
    {
        return \danog\MadelineProto\StrTools::htmlEscape($what);
    }
    /**
     * Manually convert HTML to a message and a set of entities.
     *
     * NOTE: You don't have to use this method to send HTML messages.
     *
     * This method is already called automatically by using parse_mode: "HTML" in messages.sendMessage, messages.sendMedia, et cetera...
     *
     * @see https://docs.madelineproto.xyz/API_docs/methods/messages.sendMessage.html#usage-of-parse_mode
     *
     * @return TextEntities Object containing message and entities
     */
    final public static function htmlToMessageEntities(string $html): \danog\MadelineProto\TextEntities
    {
        return \danog\MadelineProto\StrTools::htmlToMessageEntities($html);
    }
    /**
     * Import authorization.
     *
     * @param array<int, string> $authorization Authorization info
     * @param int                $mainDcID      Main DC ID
     */
    final public function importAuthorization(array $authorization, int $mainDcID): array
    {
        return $this->wrapper->getAPI()->importAuthorization($authorization, $mainDcID);
    }
    /**
     * Inflate stripped photosize to full JPG payload.
     *
     * @param string $stripped Stripped photosize
     *
     * @psalm-pure
     */
    final public static function inflateStripped(string $stripped): string
    {
        return \danog\MadelineProto\Tools::inflateStripped($stripped);
    }
    /**
     * Initialize self-restart hack.
     */
    final public function initSelfRestart(): void
    {
        $this->wrapper->getAPI()->initSelfRestart();
    }
    /**
     * Invite users to a group call.
     */
    final public function inviteToGroupCall(int $id, string|int ...$users): void
    {
        $this->wrapper->getAPI()->inviteToGroupCall($id, ...$users);
    }
    /**
     * Whether this is altervista.
     *
     * @psalm-external-mutation-free
     */
    final public static function isAltervista(): bool
    {
        return \danog\MadelineProto\Tools::isAltervista();
    }
    /**
     * Check if is array or similar (traversable && countable && arrayAccess).
     *
     * @param mixed $var Value to check
     *
     * @psalm-pure
     */
    final public static function isArrayOrAlike(mixed $var): bool
    {
        return \danog\MadelineProto\Tools::isArrayOrAlike($var);
    }
    /**
     * Check if the specified peer is a bot.
     *
     */
    final public function isBot(mixed $peer): bool
    {
        return $this->wrapper->getAPI()->isBot($peer);
    }
    /**
     * Whether our own audio stream is muted in a call.
     *
     * @psalm-mutation-free
     */
    final public function isCallMuted(int $id): bool
    {
        return $this->wrapper->getAPI()->isCallMuted($id);
    }
    /**
     * Check if the specified peer is a forum.
     *
     */
    final public function isForum(mixed $peer): bool
    {
        return $this->wrapper->getAPI()->isForum($peer);
    }
    /**
     * Whether our own audio stream is muted in a group call.
     *
     * @psalm-mutation-free
     */
    final public function isGroupCallMuted(int $id): bool
    {
        return $this->wrapper->getAPI()->isGroupCallMuted($id);
    }
    /**
     * Whether the currently playing audio file of a group call is paused.
     *
     * @psalm-mutation-free
     */
    final public function isGroupCallPlayPaused(int $id, \danog\MadelineProto\MediaDestination $dest = \danog\MadelineProto\MediaDestination::Camera): bool
    {
        return $this->wrapper->getAPI()->isGroupCallPlayPaused($id, $dest);
    }
    /**
     * Whether we're an IPC client instance.
     *
     * @psalm-pure
     */
    final public function isIpc(): bool
    {
        return $this->wrapper->getAPI()->isIpc();
    }
    /**
     * Whether we're an IPC server process (as opposed to an event handler).
     *
     * @psalm-external-mutation-free
     */
    final public function isIpcWorker(): bool
    {
        return $this->wrapper->getAPI()->isIpcWorker();
    }
    /**
     * Whether the currently playing audio file is paused.
     *
     * @psalm-mutation-free
     */
    final public function isPlayPaused(int $id, \danog\MadelineProto\MediaDestination $dest = \danog\MadelineProto\MediaDestination::Camera): bool
    {
        return $this->wrapper->getAPI()->isPlayPaused($id, $dest);
    }
    /**
     * Returns whether the current user is a premium user, cached.
     *
     * @psalm-mutation-free
     */
    final public function isPremium(): bool
    {
        return $this->wrapper->getAPI()->isPremium();
    }
    /**
     * Returns whether the current user is a bot.
     *
     * @psalm-mutation-free
     */
    final public function isSelfBot(): bool
    {
        return $this->wrapper->getAPI()->isSelfBot();
    }
    /**
     * Returns whether the current user is a user.
     *
     * @psalm-mutation-free
     */
    final public function isSelfUser(): bool
    {
        return $this->wrapper->getAPI()->isSelfUser();
    }
    /**
     * Whether we're currently connected to the test DCs.
     *
     * @return boolean
     *
     * @psalm-mutation-free
     */
    final public function isTestMode(): bool
    {
        return $this->wrapper->getAPI()->isTestMode();
    }
    /**
     * Join an existing end-to-end encrypted conference call.
     *
     * See [end-to-end encrypted group calls Â»](https://core.telegram.org/api/end-to-end/group-calls).
     * Uses the `sodium` and `openssl` PHP extensions if available, for speed.
     *
     * @param array $call  The `groupCall` (or `inputGroupCall`) of the conference to join.
     * @param bool  $muted Whether to join muted.
     */
    final public function joinConferenceCall(array $call, bool $muted = false): \danog\MadelineProto\EventHandler\Calls\ConferenceCall
    {
        return $this->wrapper->getAPI()->joinConferenceCall($call, $muted);
    }
    /**
     * Join an end-to-end encrypted conference call from the service message that invited us to it
     * (a `messageActionConferenceCall`, see {@see \danog\MadelineProto\EventHandler\Message\Service\DialogConferenceCall}).
     *
     * @param int  $msgId ID of the invitation message.
     * @param bool $muted Whether to join muted.
     */
    final public function joinConferenceCallByInviteMessage(int $msgId, bool $muted = false): \danog\MadelineProto\EventHandler\Calls\ConferenceCall
    {
        return $this->wrapper->getAPI()->joinConferenceCallByInviteMessage($msgId, $muted);
    }
    /**
     * Join an end-to-end encrypted conference call from its conference link slug.
     *
     * @param bool $muted Whether to join muted.
     */
    final public function joinConferenceCallBySlug(string $slug, bool $muted = false): \danog\MadelineProto\EventHandler\Calls\ConferenceCall
    {
        return $this->wrapper->getAPI()->joinConferenceCallBySlug($slug, $muted);
    }
    /**
     * Join the group call currently active in a group or channel.
     *
     * @param string|int       $peer       The group or channel whose call should be joined.
     * @param bool        $muted      Whether to join muted.
     * @param string|int|null  $joinAs     Peer to join as, defaults to ourselves.
     * @param string|null $inviteHash Invite hash from a video chat invite link, if any.
     */
    final public function joinGroupCall(string|int $peer, bool $muted = false, string|int|null $joinAs = null, ?string $inviteHash = null): \danog\MadelineProto\EventHandler\Calls\GroupCall
    {
        return $this->wrapper->getAPI()->joinGroupCall($peer, $muted, $joinAs, $inviteHash);
    }
    /**
     * Leave a group call, without ending it for the other participants.
     */
    final public function leaveGroupCall(int $id): void
    {
        $this->wrapper->getAPI()->leaveGroupCall($id);
    }
    /**
     * Logger.
     *
     * @param mixed  $param Parameter
     * @param int    $level Logging level
     * @param string $file  File where the message originated
     *
     * @psalm-external-mutation-free
     */
    final public function logger(mixed $param, int $level = \danog\MadelineProto\Logger::NOTICE, string $file = ''): void
    {
        $this->wrapper->getAPI()->logger($param, $level, $file);
    }
    /**
     * Logout the session.
     */
    final public function logout(): void
    {
        $this->wrapper->getAPI()->logout();
    }
    /**
     * Escape string for markdown code section.
     *
     * @param string $what String to escape
     *
     * @psalm-pure
     */
    final public static function markdownCodeEscape(string $what): string
    {
        return \danog\MadelineProto\StrTools::markdownCodeEscape($what);
    }
    /**
     * Escape string for markdown codeblock.
     *
     * @param string $what String to escape
     *
     * @psalm-pure
     */
    final public static function markdownCodeblockEscape(string $what): string
    {
        return \danog\MadelineProto\StrTools::markdownCodeblockEscape($what);
    }
    /**
     * Escape string for markdown.
     *
     * @param string $what String to escape
     *
     * @psalm-pure
     */
    final public static function markdownEscape(string $what): string
    {
        return \danog\MadelineProto\StrTools::markdownEscape($what);
    }
    /**
     * Manually convert markdown to a message and a set of entities.
     *
     * NOTE: You don't have to use this method to send Markdown messages.
     *
     * This method is already called automatically by using parse_mode: "Markdown" in messages.sendMessage, messages.sendMedia, et cetera...
     *
     * @see https://docs.madelineproto.xyz/API_docs/methods/messages.sendMessage.html#usage-of-parse_mode
     *
     * @return TextEntities Object containing message and entities
     *
     * @psalm-mutation-free
     */
    final public static function markdownToMessageEntities(string $markdown): \danog\MadelineProto\TextEntities
    {
        return \danog\MadelineProto\StrTools::markdownToMessageEntities($markdown);
    }
    /**
     * Escape string for URL.
     *
     * @param string $what String to escape
     *
     * @psalm-pure
     */
    final public static function markdownUrlEscape(string $what): string
    {
        return \danog\MadelineProto\StrTools::markdownUrlEscape($what);
    }
    /**
     * Telegram UTF-8 multibyte split.
     *
     * @param string        $text   Text
     * @param integer       $length Length
     *
     * @return array<string>
     *
     * @psalm-pure
     */
    final public static function mbStrSplit(string $text, int $length): array
    {
        return \danog\MadelineProto\StrTools::mbStrSplit($text, $length);
    }
    /**
     * Get Telegram UTF-8 length of string.
     *
     * @param string $text Text
     *
     * @psalm-pure
     */
    final public static function mbStrlen(string $text): int
    {
        return \danog\MadelineProto\StrTools::mbStrlen($text);
    }
    /**
     * Telegram UTF-8 multibyte substring.
     *
     * @param string   $text   Text to substring
     * @param integer  $offset Offset
     * @param null|int $length Length
     *
     * @psalm-pure
     */
    final public static function mbSubstr(string $text, int $offset, ?int $length = null): string
    {
        return \danog\MadelineProto\StrTools::mbSubstr($text, $offset, $length);
    }
    /**
     * Upgrade a one-to-one call to an [end-to-end encrypted conference call Â»](https://core.telegram.org/api/group-calls#conference-calls):
     * create a conference, invite the other party to it, and discard the one-to-one call pointing them
     * to the conference (`phoneCallDiscardReasonMigrateConferenceCall`).
     *
     * @param int  $id    ID of the one-to-one call.
     * @param bool $muted Whether to join the conference muted.
     */
    final public function migrateCallToConference(int $id, bool $muted = false): \danog\MadelineProto\EventHandler\Calls\ConferenceCall
    {
        return $this->wrapper->getAPI()->migrateCallToConference($id, $muted);
    }
    /**
     * Provide a buffered reader for a file, URL or amp stream, optionally starting at a byte offset.
     *
     * The `$offset` makes a demuxer resumable across a serialize/unserialize cycle: a `LocalFile` is
     * seeked and a `RemoteUrl` is fetched with a `Range` request. A {@see ResumableStream} is skipped
     * forward from its current position (without downloading the skipped part, for a
     * {@see ResumableDownloadStream}). Any other {@see ReadableStream} cannot be seeked, so a non-zero
     * offset on one is rejected.
     *
     * @return Closure(int): ?string
     */
    final public static function openBuffered(\danog\MadelineProto\LocalFile|\danog\MadelineProto\RemoteUrl|\Amp\ByteStream\ReadableStream $stream, ?\Amp\Cancellation $cancellation = null, int $offset = 0): \Closure
    {
        return \danog\MadelineProto\Tools::openBuffered($stream, $cancellation, $offset);
    }
    /**
     * Opens a file in append-only mode.
     *
     * @param string $path File path.
     */
    final public static function openFileAppendOnly(string $path): \Amp\File\File
    {
        return \danog\MadelineProto\Tools::openFileAppendOnly($path);
    }
    /**
     * Convert double to binary version.
     *
     * @param float $value Value to convert
     *
     * @psalm-external-mutation-free
     */
    final public static function packDouble(float $value): string
    {
        return \danog\MadelineProto\Tools::packDouble($value);
    }
    /**
     * Convert integer to base256 signed int.
     *
     * @param integer $value Value to convert
     *
     * @psalm-external-mutation-free
     */
    final public static function packSignedInt(int $value): string
    {
        return \danog\MadelineProto\Tools::packSignedInt($value);
    }
    /**
     * Convert integer to base256 long.
     *
     * @param int $value Value to convert
     *
     * @psalm-external-mutation-free
     */
    final public static function packSignedLong(int $value): string
    {
        return \danog\MadelineProto\Tools::packSignedLong($value);
    }
    /**
     * Convert value to unsigned base256 int.
     *
     * @param int $value Value
     *
     * @psalm-external-mutation-free
     */
    final public static function packUnsignedInt(int $value): string
    {
        return \danog\MadelineProto\Tools::packUnsignedInt($value);
    }
    /**
     * Pauses playback of the current audio file in the call.
     *
     * @psalm-external-mutation-free
     */
    final public function pausePlay(int $id, \danog\MadelineProto\MediaDestination $dest = \danog\MadelineProto\MediaDestination::Camera): void
    {
        $this->wrapper->getAPI()->pausePlay($id, $dest);
    }
    /**
     * Check if peer is present in internal peer database.
     *
     * @param mixed $id Peer
     */
    final public function peerIsset(mixed $id): bool
    {
        return $this->wrapper->getAPI()->peerIsset($id);
    }
    /**
     * Login as user.
     *
     * @param string  $number   Phone number
     * @param integer $sms_type SMS type
     */
    final public function phoneLogin(string $number, int $sms_type = 5): array
    {
        return $this->wrapper->getAPI()->phoneLogin($number, $sms_type);
    }
    /**
     * Positive modulo
     * Works just like the % (modulus) operator, only returns always a postive number.
     *
     * @param int $a A
     * @param int $b B
     *
     * @psalm-pure
     */
    final public static function posmod(int $a, int $b): int
    {
        return \danog\MadelineProto\Tools::posmod($a, $b);
    }
    /**
     * Internal endpoint used by the download server.
     *
     * @psalm-external-mutation-free
     */
    final public static function processDownloadServerPing(string $path, string $payload): void
    {
        \danog\MadelineProto\MTProto::processDownloadServerPing($path, $payload);
    }
    /**
     * Initiates QR code login.
     *
     * Returns a QR code login helper object, that can be used to render the QR code, display the link directly, wait for login, QR code expiration and much more.
     *
     * Returns null if we're already logged in, or if we're waiting for a password (use getAuthorization to distinguish between the two cases).
     */
    final public function qrLogin(): ?\danog\MadelineProto\TL\Types\LoginQrCode
    {
        return $this->wrapper->getAPI()->qrLogin();
    }
    /**
     * Get secure random string of specified length.
     *
     * @param integer $length Length
     */
    final public static function random(int $length): string
    {
        return \danog\MadelineProto\Tools::random($length);
    }
    /**
     * Get random integer.
     *
     * @param integer $modulus Modulus
     */
    final public static function randomInt(int $modulus = 0): int
    {
        return \danog\MadelineProto\Tools::randomInt($modulus);
    }
    /**
     * Asynchronously read line.
     *
     * @param string  $prompt    Prompt
     * @param bool    $hidden    Whether to hide what is typed (for tokens, passwords and codes), when reading from a terminal
     * @param ?string $hideAfter Show what is typed until this character, censor what follows it with asterisks
     *                           (for prompts that take either a phone number or a bot token, which contains a
     *                           colon), when reading from a terminal
     */
    final public static function readLine(string $prompt = '', ?\Amp\Cancellation $cancel = null, bool $hidden = false, ?string $hideAfter = null): string
    {
        return \danog\MadelineProto\AsyncTools::readLine($prompt, $cancel, $hidden, $hideAfter);
    }
    /**
     * Refresh full peer cache for a certain peer.
     *
     * @param mixed $id The peer to refresh
     */
    final public function refreshFullPeerCache(mixed $id): void
    {
        $this->wrapper->getAPI()->refreshFullPeerCache($id);
    }
    /**
     * Refresh peer cache for a certain peer.
     *
     */
    final public function refreshPeerCache(mixed ...$ids): void
    {
        $this->wrapper->getAPI()->refreshPeerCache(...$ids);
    }
    /**
     * Renders prometheus stats using the specified renderer.
     *
     * By default uses the text renderer.
     */
    final public function renderPromStats(?\Prometheus\RendererInterface $renderer = null): string
    {
        return $this->wrapper->getAPI()->renderPromStats($renderer);
    }
    /**
     * Report an error to the previously set peer.
     *
     * @param string $message   Error to report
     * @param string $parseMode Parse mode
     */
    final public function report(string $message, string $parseMode = ''): void
    {
        $this->wrapper->getAPI()->report($message, $parseMode);
    }
    /**
     * Report memory profile with memprof.
     */
    final public function reportMemoryProfile(): void
    {
        $this->wrapper->getAPI()->reportMemoryProfile();
    }
    /**
     * Request a one-to-one voice or video call.
     *
     * See [the call documentation Â»](https://docs.madelineproto.xyz/docs/CALLS.html) for how to play videos into it.
     *
     * @param mixed $user  User
     * @param bool  $video Whether to start a video call.
     */
    final public function requestCall(mixed $user, bool $video = false): \danog\MadelineProto\EventHandler\Calls\PrivateCall
    {
        return $this->wrapper->getAPI()->requestCall($user, $video);
    }
    /**
     * Request secret chat.
     *
     * @param mixed $user User to start secret chat with
     */
    final public function requestSecretChat(mixed $user): int
    {
        return $this->wrapper->getAPI()->requestSecretChat($user);
    }
    /**
     * Reset the update state and fetch all updates from the beginning.
     */
    final public function resetUpdateState(): void
    {
        $this->wrapper->getAPI()->resetUpdateState();
    }
    /**
     * Restart update loop.
     */
    final public function restart(): void
    {
        $this->wrapper->getAPI()->restart();
    }
    /**
     * Resumes playback of the current audio file in the call.
     *
     * @psalm-external-mutation-free
     */
    final public function resumePlay(int $id, \danog\MadelineProto\MediaDestination $dest = \danog\MadelineProto\MediaDestination::Camera): void
    {
        $this->wrapper->getAPI()->resumePlay($id, $dest);
    }
    /**
     * Rethrow exception into event loop.
     */
    final public static function rethrow(\Throwable $e): void
    {
        \danog\MadelineProto\AsyncTools::rethrow($e);
    }
    /**
     * null-byte RLE decode.
     *
     * @param string $string Data to decode
     *
     * @psalm-pure
     */
    final public static function rleDecode(string $string): string
    {
        return \danog\MadelineProto\Tools::rleDecode($string);
    }
    /**
     * null-byte RLE encode.
     *
     * @param string $string Data to encode
     *
     * @psalm-pure
     */
    final public static function rleEncode(string $string): string
    {
        return \danog\MadelineProto\Tools::rleEncode($string);
    }
    /**
     * Save the peer we join the video chats and livestreams of a group or channel as by default.
     *
     * @param string|int $peer   The group or channel.
     * @param string|int $joinAs The peer to join as (one of {@see self::getGroupCallJoinAs()}).
     */
    final public function saveDefaultGroupCallJoinAs(string|int $peer, string|int $joinAs): void
    {
        $this->wrapper->getAPI()->saveDefaultGroupCallJoinAs($peer, $joinAs);
    }
    /**
     * Sends an audio.
     *
     * Please use named arguments to call this method.
     *
     * @param integer|string                                                $peer                   Destination peer or username.
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file                   File to upload: can be a message to reuse media present in a message.
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream|null $thumb                  Optional: Thumbnail to upload
     * @param string                                                        $caption                Caption of document
     * @param ParseMode                                                     $parseMode              Text parse mode for the caption
     * @param ?callable(float, float, float)                                $callback               Upload callback (percent, speed in mbps, seconds elapsed)
     * @param ?string                                                       $fileName               Optional file name, if absent will be extracted from the passed $file.
     * @param integer|null                                                  $duration                Duration of the audio
     * @param string|null                                                   $title                   Title of the audio
     * @param string|null                                                   $performer               Performer of the audio
     * @param integer|null                                                  $replyToMsgId            ID of message to reply to.
     * @param integer|null                                                  $topMsgId                ID of thread where to send the message.
     * @param array|null                                                    $replyMarkup             Keyboard information.
     * @param integer|string|null                                           $sendAs                 Peer to send the message as.
     * @param integer|null                                                  $scheduleDate            Schedule date.
     * @param boolean                                                       $silent                  Whether to send the message silently, without triggering notifications.
     * @param boolean                                                       $noForwards              Whether to disable forwards for this message.
     * @param boolean                                                       $background              Send this message as background message
     * @param boolean                                                       $clearDraft              Clears the draft field
     * @param boolean                                                       $forceResend             Whether to forcefully resend the file, even if its type and name are the same.
     * @param ?Cancellation                                                  $cancellation            Cancellation.
     *
     */
    final public function sendAudio(string|int $peer, \danog\MadelineProto\EventHandler\Message|\danog\MadelineProto\EventHandler\Media|\danog\MadelineProto\LocalFile|\danog\MadelineProto\RemoteUrl|\danog\MadelineProto\BotApiFileId|\Amp\ByteStream\ReadableStream $file, \danog\MadelineProto\EventHandler\Message|\danog\MadelineProto\EventHandler\Media|\danog\MadelineProto\LocalFile|\danog\MadelineProto\RemoteUrl|\danog\MadelineProto\BotApiFileId|\Amp\ByteStream\ReadableStream|null $thumb = null, string $caption = '', \danog\MadelineProto\ParseMode $parseMode = \danog\MadelineProto\ParseMode::TEXT, ?callable $callback = null, ?string $fileName = null, ?string $mimeType = null, ?int $duration = null, ?string $title = null, ?string $performer = null, ?int $ttl = null, ?int $replyToMsgId = null, ?int $topMsgId = null, ?array $replyMarkup = null, string|int|null $sendAs = null, ?int $scheduleDate = null, bool $silent = false, bool $noForwards = false, bool $background = false, bool $clearDraft = false, bool $forceResend = false, ?\Amp\Cancellation $cancellation = null): \danog\MadelineProto\EventHandler\Message
    {
        return $this->wrapper->getAPI()->sendAudio($peer, $file, $thumb, $caption, $parseMode, $callback, $fileName, $mimeType, $duration, $title, $performer, $ttl, $replyToMsgId, $topMsgId, $replyMarkup, $sendAs, $scheduleDate, $silent, $noForwards, $background, $clearDraft, $forceResend, $cancellation);
    }
    /**
     * Sends an updateCustomEvent update to the event handler.
     */
    final public function sendCustomEvent(mixed $payload): void
    {
        $this->wrapper->getAPI()->sendCustomEvent($payload);
    }
    /**
     * Sends a document.
     *
     * Please use named arguments to call this method.
     *
     * @param integer|string                                                     $peer                   Destination peer or username.
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream      $file                   File to upload: can be a message to reuse media present in a message.
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream|null $thumb                  Optional: Thumbnail to upload
     * @param string                                                             $caption                Caption of document
     * @param ?callable(float, float, float)                                     $callback               Upload callback (percent, speed in mbps, seconds elapsed)
     * @param ?string                                                            $fileName               Optional file name, if absent will be extracted from the passed $file.
     * @param ParseMode                                                          $parseMode              Text parse mode for the caption
     * @param integer|null                                                       $replyToMsgId           ID of message to reply to.
     * @param integer|null                                                       $topMsgId               ID of thread where to send the message.
     * @param array|null                                                         $replyMarkup            Keyboard information.
     * @param integer|null                                                       $sendAs                 Peer to send the message as.
     * @param integer|null                                                       $scheduleDate           Schedule date.
     * @param boolean                                                            $silent                 Whether to send the message silently, without triggering notifications.
     * @param boolean                                                            $background             Send this message as background message
     * @param boolean                                                            $clearDraft             Clears the draft field
     * @param boolean                                                            $updateStickersetsOrder Whether to move used stickersets to top
     * @param boolean                                                            $forceResend            Whether to forcefully resend the file, even if its type and name are the same.
     * @param Cancellation                                                       $cancellation           Cancellation.
     *
     */
    final public function sendDocument(string|int $peer, \danog\MadelineProto\EventHandler\Message|\danog\MadelineProto\EventHandler\Media|\danog\MadelineProto\LocalFile|\danog\MadelineProto\RemoteUrl|\danog\MadelineProto\BotApiFileId|\Amp\ByteStream\ReadableStream $file, \danog\MadelineProto\EventHandler\Message|\danog\MadelineProto\EventHandler\Media|\danog\MadelineProto\LocalFile|\danog\MadelineProto\RemoteUrl|\danog\MadelineProto\BotApiFileId|\Amp\ByteStream\ReadableStream|null $thumb = null, string $caption = '', \danog\MadelineProto\ParseMode $parseMode = \danog\MadelineProto\ParseMode::TEXT, ?callable $callback = null, ?string $fileName = null, ?string $mimeType = null, ?int $ttl = null, bool $spoiler = false, ?int $replyToMsgId = null, ?int $topMsgId = null, ?array $replyMarkup = null, string|int|null $sendAs = null, ?int $scheduleDate = null, bool $silent = false, bool $noForwards = false, bool $background = false, bool $clearDraft = false, bool $updateStickersetsOrder = false, bool $forceResend = false, ?\Amp\Cancellation $cancellation = null): \danog\MadelineProto\EventHandler\Message
    {
        return $this->wrapper->getAPI()->sendDocument($peer, $file, $thumb, $caption, $parseMode, $callback, $fileName, $mimeType, $ttl, $spoiler, $replyToMsgId, $topMsgId, $replyMarkup, $sendAs, $scheduleDate, $silent, $noForwards, $background, $clearDraft, $updateStickersetsOrder, $forceResend, $cancellation);
    }
    /**
     * Sends a photo.
     *
     * Please use named arguments to call this method.
     *
     * @param integer|string                                                $peer                   Destination peer or username.
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file                   File to upload: can be a message to reuse media present in a message.
     * @param string                                                        $caption                Caption of document
     * @param ?callable(float, float, float)                                $callback               Upload callback (percent, speed in mbps, seconds elapsed)
     * @param ?string                                                       $fileName               Optional file name, if absent will be extracted from the passed $file.
     * @param ParseMode                                                     $parseMode              Text parse mode for the caption
     * @param integer|null                                                  $replyToMsgId           ID of message to reply to.
     * @param integer|null                                                  $topMsgId               ID of thread where to send the message.
     * @param array|null                                                    $replyMarkup            Keyboard information.
     * @param integer|null                                                  $sendAs                 Peer to send the message as.
     * @param integer|null                                                  $scheduleDate           Schedule date.
     * @param boolean                                                       $silent                 Whether to send the message silently, without triggering notifications.
     * @param boolean                                                       $background             Send this message as background message
     * @param boolean                                                       $clearDraft             Clears the draft field
     * @param boolean                                                       $updateStickersetsOrder Whether to move used stickersets to top
     * @param boolean                                                       $forceResend            Whether to forcefully resend the file, even if its type and name are the same.
     * @param Cancellation                                                  $cancellation           Cancellation.
     *
     */
    final public function sendDocumentPhoto(string|int $peer, \danog\MadelineProto\EventHandler\Message|\danog\MadelineProto\EventHandler\Media|\danog\MadelineProto\LocalFile|\danog\MadelineProto\RemoteUrl|\danog\MadelineProto\BotApiFileId|\Amp\ByteStream\ReadableStream $file, string $caption = '', \danog\MadelineProto\ParseMode $parseMode = \danog\MadelineProto\ParseMode::TEXT, ?callable $callback = null, ?string $fileName = null, ?int $ttl = null, bool $spoiler = false, ?int $replyToMsgId = null, ?int $topMsgId = null, ?array $replyMarkup = null, string|int|null $sendAs = null, ?int $scheduleDate = null, bool $silent = false, bool $noForwards = false, bool $background = false, bool $clearDraft = false, bool $updateStickersetsOrder = false, bool $forceResend = false, ?\Amp\Cancellation $cancellation = null): \danog\MadelineProto\EventHandler\Message
    {
        return $this->wrapper->getAPI()->sendDocumentPhoto($peer, $file, $caption, $parseMode, $callback, $fileName, $ttl, $spoiler, $replyToMsgId, $topMsgId, $replyMarkup, $sendAs, $scheduleDate, $silent, $noForwards, $background, $clearDraft, $updateStickersetsOrder, $forceResend, $cancellation);
    }
    /**
     * Sends a gif.
     *
     * Please use named arguments to call this method.
     *
     * @param integer|string                                                $peer                   Destination peer or username.
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file                   File to upload: can be a message to reuse media present in a message.
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream|null $thumb                  Optional: Thumbnail to upload
     * @param string                                                        $caption                Caption of document
     * @param ParseMode                                                     $parseMode              Text parse mode for the caption
     * @param ?callable(float, float, float)                                $callback               Upload callback (percent, speed in mbps, seconds elapsed)
     * @param ?string                                                       $fileName               Optional file name, if absent will be extracted from the passed $file.
     * @param integer|null                                                  $ttl                     Time to live
     * @param boolean                                                       $spoiler                 Whether the message is a spoiler
     * @param integer|null                                                  $replyToMsgId            ID of message to reply to.
     * @param integer|null                                                  $topMsgId                ID of thread where to send the message.
     * @param array|null                                                    $replyMarkup             Keyboard information.
     * @param integer|string|null                                           $sendAs                 Peer to send the message as.
     * @param integer|null                                                  $scheduleDate            Schedule date.
     * @param boolean                                                       $silent                  Whether to send the message silently, without triggering notifications.
     * @param boolean                                                       $noForwards              Whether to disable forwards for this message.
     * @param boolean                                                       $background              Send this message as background message
     * @param boolean                                                       $clearDraft              Clears the draft field
     * @param boolean                                                       $forceResend             Whether to forcefully resend the file, even if its type and name are the same.
     * @param ?Cancellation                                                  $cancellation            Cancellation.
     *
     */
    final public function sendGif(string|int $peer, \danog\MadelineProto\EventHandler\Message|\danog\MadelineProto\EventHandler\Media|\danog\MadelineProto\LocalFile|\danog\MadelineProto\RemoteUrl|\danog\MadelineProto\BotApiFileId|\Amp\ByteStream\ReadableStream $file, \danog\MadelineProto\EventHandler\Message|\danog\MadelineProto\EventHandler\Media|\danog\MadelineProto\LocalFile|\danog\MadelineProto\RemoteUrl|\danog\MadelineProto\BotApiFileId|\Amp\ByteStream\ReadableStream|null $thumb = null, string $caption = '', \danog\MadelineProto\ParseMode $parseMode = \danog\MadelineProto\ParseMode::TEXT, ?callable $callback = null, ?string $fileName = null, ?int $ttl = null, bool $spoiler = false, ?int $duration = null, ?int $width = null, ?int $height = null, string $thumbSeek = '00:00:01.000', ?int $replyToMsgId = null, ?int $topMsgId = null, ?array $replyMarkup = null, string|int|null $sendAs = null, ?int $scheduleDate = null, bool $silent = false, bool $noForwards = false, bool $background = false, bool $clearDraft = false, bool $forceResend = false, ?\Amp\Cancellation $cancellation = null): \danog\MadelineProto\EventHandler\Message
    {
        return $this->wrapper->getAPI()->sendGif($peer, $file, $thumb, $caption, $parseMode, $callback, $fileName, $ttl, $spoiler, $duration, $width, $height, $thumbSeek, $replyToMsgId, $topMsgId, $replyMarkup, $sendAs, $scheduleDate, $silent, $noForwards, $background, $clearDraft, $forceResend, $cancellation);
    }
    /**
     * Sends a message.
     *
     * @param integer|string $peer                   Destination peer or username.
     * @param string         $message                Message to send
     * @param ParseMode      $parseMode              Parse mode
     * @param integer|null   $replyToMsgId           ID of message to reply to.
     * @param integer|null   $topMsgId               ID of thread where to send the message.
     * @param array|null     $replyMarkup            Keyboard information.
     * @param integer|null   $sendAs                 Peer to send the message as.
     * @param integer|null   $scheduleDate           Schedule date.
     * @param boolean        $silent                 Whether to send the message silently, without triggering notifications.
     * @param boolean        $background             Send this message as background message
     * @param boolean        $clearDraft             Clears the draft field
     * @param boolean        $noWebpage              Set this flag to disable generation of the webpage preview
     * @param boolean        $updateStickersetsOrder Whether to move used stickersets to top
     * @param ?Cancellation  $cancellation           Cancellation
     */
    final public function sendMessage(string|int $peer, string $message, \danog\MadelineProto\ParseMode $parseMode = \danog\MadelineProto\ParseMode::TEXT, ?int $replyToMsgId = null, ?int $topMsgId = null, ?array $replyMarkup = null, string|int|null $sendAs = null, ?int $scheduleDate = null, bool $silent = false, bool $noForwards = false, bool $background = false, bool $clearDraft = false, bool $noWebpage = false, bool $updateStickersetsOrder = false, ?\Amp\Cancellation $cancellation = null): \danog\MadelineProto\EventHandler\Message
    {
        return $this->wrapper->getAPI()->sendMessage($peer, $message, $parseMode, $replyToMsgId, $topMsgId, $replyMarkup, $sendAs, $scheduleDate, $silent, $noForwards, $background, $clearDraft, $noWebpage, $updateStickersetsOrder, $cancellation);
    }
    /**
     * Sends a message to all report peers (admins of the bot).
     *
     * @param string       $message      Message to send
     * @param ParseMode    $parseMode    Parse mode
     * @param array|null   $replyMarkup  Keyboard information.
     * @param integer|null $scheduleDate Schedule date.
     * @param boolean      $silent       Whether to send the message silently, without triggering notifications.
     * @param boolean      $background   Send this message as background message
     * @param boolean      $clearDraft   Clears the draft field
     * @param boolean      $noWebpage    Set this flag to disable generation of the webpage preview
     *
     * @return list<\danog\MadelineProto\EventHandler\Message>
     */
    final public function sendMessageToAdmins(string $message, \danog\MadelineProto\ParseMode $parseMode = \danog\MadelineProto\ParseMode::TEXT, ?array $replyMarkup = null, ?int $scheduleDate = null, bool $silent = false, bool $noForwards = false, bool $background = false, bool $clearDraft = false, bool $noWebpage = false, ?\Amp\Cancellation $cancellation = null): array
    {
        return $this->wrapper->getAPI()->sendMessageToAdmins($message, $parseMode, $replyMarkup, $scheduleDate, $silent, $noForwards, $background, $clearDraft, $noWebpage, $cancellation);
    }
    /**
     * Sends a photo.
     *
     * Please use named arguments to call this method.
     *
     * @param integer|string                                                $peer                   Destination peer or username.
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file                   File to upload: can be a message to reuse media present in a message.
     * @param string                                                        $caption                Caption of document
     * @param ?callable(float, float, float)                                $callback               Upload callback (percent, speed in mbps, seconds elapsed)
     * @param ?string                                                       $fileName               Optional file name, if absent will be extracted from the passed $file.
     * @param ParseMode                                                     $parseMode              Text parse mode for the caption
     * @param integer|null                                                  $replyToMsgId           ID of message to reply to.
     * @param integer|null                                                  $topMsgId               ID of thread where to send the message.
     * @param array|null                                                    $replyMarkup            Keyboard information.
     * @param integer|null                                                  $sendAs                 Peer to send the message as.
     * @param integer|null                                                  $scheduleDate           Schedule date.
     * @param boolean                                                       $silent                 Whether to send the message silently, without triggering notifications.
     * @param boolean                                                       $background             Send this message as background message
     * @param boolean                                                       $clearDraft             Clears the draft field
     * @param boolean                                                       $updateStickersetsOrder Whether to move used stickersets to top
     * @param boolean                                                       $forceResend            Whether to forcefully resend the file, even if its type and name are the same.
     * @param Cancellation                                                  $cancellation           Cancellation.
     *
     */
    final public function sendPhoto(string|int $peer, \danog\MadelineProto\EventHandler\Message|\danog\MadelineProto\EventHandler\Media|\danog\MadelineProto\LocalFile|\danog\MadelineProto\RemoteUrl|\danog\MadelineProto\BotApiFileId|\Amp\ByteStream\ReadableStream $file, string $caption = '', \danog\MadelineProto\ParseMode $parseMode = \danog\MadelineProto\ParseMode::TEXT, ?callable $callback = null, ?string $fileName = null, ?int $ttl = null, bool $spoiler = false, ?int $replyToMsgId = null, ?int $topMsgId = null, ?array $replyMarkup = null, string|int|null $sendAs = null, ?int $scheduleDate = null, bool $silent = false, bool $noForwards = false, bool $background = false, bool $clearDraft = false, bool $updateStickersetsOrder = false, bool $forceResend = false, ?\Amp\Cancellation $cancellation = null): \danog\MadelineProto\EventHandler\Message
    {
        return $this->wrapper->getAPI()->sendPhoto($peer, $file, $caption, $parseMode, $callback, $fileName, $ttl, $spoiler, $replyToMsgId, $topMsgId, $replyMarkup, $sendAs, $scheduleDate, $silent, $noForwards, $background, $clearDraft, $updateStickersetsOrder, $forceResend, $cancellation);
    }
    /**
     * Sends a sticker.
     *
     * Please use named arguments to call this method.
     *
     * @param integer|string                                                $peer                   Destination peer or username.
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file                   File to upload: can be a message to reuse media present in a message.
     * @param ?callable(float, float, float)                                $callback               Upload callback (percent, speed in mbps, seconds elapsed)
     * @param ?string                                                       $fileName               Optional file name, if absent will be extracted from the passed $file.
     * @param integer|null                                                  $replyToMsgId           ID of message to reply to.
     * @param integer|null                                                  $topMsgId               ID of thread where to send the message.
     * @param array|null                                                    $replyMarkup            Keyboard information.
     * @param integer|null                                                  $sendAs                 Peer to send the message as.
     * @param integer|null                                                  $scheduleDate           Schedule date.
     * @param boolean                                                       $silent                 Whether to send the message silently, without triggering notifications.
     * @param boolean                                                       $noForwards             Whether to disable forwards for this message.
     * @param boolean                                                       $background             Send this message as background message
     * @param boolean                                                       $clearDraft             Clears the draft field
     * @param boolean                                                       $updateStickersetsOrder Whether to move used stickersets to top
     * @param boolean                                                       $forceResend            Whether to forcefully resend the file, even if its type and name are the same.
     * @param Cancellation                                                  $cancellation           Cancellation.
     *
     */
    final public function sendSticker(string|int $peer, \danog\MadelineProto\EventHandler\Message|\danog\MadelineProto\EventHandler\Media|\danog\MadelineProto\LocalFile|\danog\MadelineProto\RemoteUrl|\danog\MadelineProto\BotApiFileId|\Amp\ByteStream\ReadableStream $file, string $mimeType, string $emoji = '', array $stickerSet =  [
        '_' => 'inputStickerSetEmpty',
    ], ?callable $callback = null, ?string $fileName = null, ?int $ttl = null, ?int $replyToMsgId = null, ?int $topMsgId = null, ?array $replyMarkup = null, string|int|null $sendAs = null, ?int $scheduleDate = null, bool $silent = false, bool $noForwards = false, bool $background = false, bool $clearDraft = false, bool $updateStickersetsOrder = false, bool $forceResend = false, ?\Amp\Cancellation $cancellation = null): \danog\MadelineProto\EventHandler\Message
    {
        return $this->wrapper->getAPI()->sendSticker($peer, $file, $mimeType, $emoji, $stickerSet, $callback, $fileName, $ttl, $replyToMsgId, $topMsgId, $replyMarkup, $sendAs, $scheduleDate, $silent, $noForwards, $background, $clearDraft, $updateStickersetsOrder, $forceResend, $cancellation);
    }
    /**
     * Sends a video.
     *
     * Please use named arguments to call this method.
     *
     * @param integer|string                                                $peer                   Destination peer or username.
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file                   File to upload: can be a message to reuse media present in a message.
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream|null $thumb                  Optional: Thumbnail to upload
     * @param string                                                        $caption                Caption of document
     * @param ParseMode                                                     $parseMode              Text parse mode for the caption
     * @param ?callable(float, float, float)                                $callback               Upload callback (percent, speed in mbps, seconds elapsed)
     * @param ?string                                                       $fileName               Optional file name, if absent will be extracted from the passed $file.
     * @param integer|null                                                  $ttl                     Time to live
     * @param boolean                                                       $spoiler                 Whether the message is a spoiler
     * @param boolean                                                       $roundMessage            Whether the message should be round
     * @param boolean                                                       $supportsStreaming        Whether the video supports streaming
     * @param boolean                                                       $noSound                 Whether the video has no sound
     * @param integer|null                                                  $duration                Duration of the video
     * @param integer|null                                                  $width                   Width of the video
     * @param integer|null                                                  $height                  Height of the video
     * @param integer|null                                                  $replyToMsgId            ID of message to reply to.
     * @param integer|null                                                  $topMsgId                ID of thread where to send the message.
     * @param array|null                                                    $replyMarkup             Keyboard information.
     * @param integer|string|null                                           $sendAs                 Peer to send the message as.
     * @param integer|null                                                  $scheduleDate            Schedule date.
     * @param boolean                                                       $silent                  Whether to send the message silently, without triggering notifications.
     * @param boolean                                                       $noForwards              Whether to disable forwards for this message.
     * @param boolean                                                       $background              Send this message as background message
     * @param boolean                                                       $clearDraft              Clears the draft field
     * @param boolean                                                       $forceResend             Whether to forcefully resend the file, even if its type and name are the same.
     * @param Cancellation                                                  $cancellation            Cancellation.
     *
     */
    final public function sendVideo(string|int $peer, \danog\MadelineProto\EventHandler\Message|\danog\MadelineProto\EventHandler\Media|\danog\MadelineProto\LocalFile|\danog\MadelineProto\RemoteUrl|\danog\MadelineProto\BotApiFileId|\Amp\ByteStream\ReadableStream $file, \danog\MadelineProto\EventHandler\Message|\danog\MadelineProto\EventHandler\Media|\danog\MadelineProto\LocalFile|\danog\MadelineProto\RemoteUrl|\danog\MadelineProto\BotApiFileId|\Amp\ByteStream\ReadableStream|null $thumb = null, string $caption = '', \danog\MadelineProto\ParseMode $parseMode = \danog\MadelineProto\ParseMode::TEXT, ?callable $callback = null, ?string $fileName = null, string $mimeType = 'video/mp4', ?int $ttl = null, bool $spoiler = false, bool $roundMessage = false, bool $supportsStreaming = true, bool $noSound = false, ?int $duration = null, ?int $width = null, ?int $height = null, string $thumbSeek = '00:00:01.000', ?int $replyToMsgId = null, ?int $topMsgId = null, ?array $replyMarkup = null, string|int|null $sendAs = null, ?int $scheduleDate = null, bool $silent = false, bool $noForwards = false, bool $background = false, bool $clearDraft = false, bool $forceResend = false, bool $updateStickersetsOrder = false, ?\Amp\Cancellation $cancellation = null): \danog\MadelineProto\EventHandler\Message
    {
        return $this->wrapper->getAPI()->sendVideo($peer, $file, $thumb, $caption, $parseMode, $callback, $fileName, $mimeType, $ttl, $spoiler, $roundMessage, $supportsStreaming, $noSound, $duration, $width, $height, $thumbSeek, $replyToMsgId, $topMsgId, $replyMarkup, $sendAs, $scheduleDate, $silent, $noForwards, $background, $clearDraft, $forceResend, $updateStickersetsOrder, $cancellation);
    }
    /**
     * Sends a voice.
     *
     * Please use named arguments to call this method.
     *
     * @param integer|string                                                $peer                   Destination peer or username.
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file                   File to upload: can be a message to reuse media present in a message.
     * @param string                                                        $caption                Caption of document
     * @param ParseMode                                                     $parseMode              Text parse mode for the caption
     * @param ?callable(float, float, float)                                $callback               Upload callback (percent, speed in mbps, seconds elapsed)
     * @param ?string                                                       $fileName               Optional file name, if absent will be extracted from the passed $file.
     * @param integer|null                                                  $ttl                     Time to live
     * @param integer|null                                                  $duration                Duration of the voice
     * @param array|null                                                    $waveform                Waveform of the voice
     * @param integer|null                                                  $replyToMsgId            ID of message to reply to.
     * @param integer|null                                                  $topMsgId                ID of thread where to send the message.
     * @param array|null                                                    $replyMarkup             Keyboard information.
     * @param integer|string|null                                           $sendAs                 Peer to send the message as.
     * @param integer|null                                                  $scheduleDate            Schedule date.
     * @param boolean                                                       $silent                  Whether to send the message silently, without triggering notifications.
     * @param boolean                                                       $noForwards              Whether to disable forwards for this message.
     * @param boolean                                                       $background              Send this message as background message
     * @param boolean                                                       $clearDraft              Clears the draft field
     * @param boolean                                                       $forceResend             Whether to forcefully resend the file, even if its type and name are the same.
     * @param ?Cancellation                                                  $cancellation            Cancellation.
     *
     */
    final public function sendVoice(string|int $peer, \danog\MadelineProto\EventHandler\Message|\danog\MadelineProto\EventHandler\Media|\danog\MadelineProto\LocalFile|\danog\MadelineProto\RemoteUrl|\danog\MadelineProto\BotApiFileId|\Amp\ByteStream\ReadableStream $file, string $caption = '', \danog\MadelineProto\ParseMode $parseMode = \danog\MadelineProto\ParseMode::TEXT, ?callable $callback = null, ?string $fileName = null, ?int $ttl = null, ?int $duration = null, ?array $waveform = null, ?int $replyToMsgId = null, ?int $topMsgId = null, ?array $replyMarkup = null, string|int|null $sendAs = null, ?int $scheduleDate = null, bool $silent = false, bool $noForwards = false, bool $background = false, bool $clearDraft = false, bool $forceResend = false, ?\Amp\Cancellation $cancellation = null): \danog\MadelineProto\EventHandler\Message
    {
        return $this->wrapper->getAPI()->sendVoice($peer, $file, $caption, $parseMode, $callback, $fileName, $ttl, $duration, $waveform, $replyToMsgId, $topMsgId, $replyMarkup, $sendAs, $scheduleDate, $silent, $noForwards, $background, $clearDraft, $forceResend, $cancellation);
    }
    /**
     * Mute or unmute our own audio stream in a call.
     */
    final public function setCallMuted(int $id, bool $muted = true): void
    {
        $this->wrapper->getAPI()->setCallMuted($id, $muted);
    }
    /**
     * Mute or unmute our own audio stream in a group call.
     */
    final public function setGroupCallMuted(int $id, bool $muted = true): void
    {
        $this->wrapper->getAPI()->setGroupCallMuted($id, $muted);
    }
    /**
     * Change the title of a group call.
     */
    final public function setGroupCallTitle(int $id, string $title): void
    {
        $this->wrapper->getAPI()->setGroupCallTitle($id, $title);
    }
    /**
     * Set NOOP update handler, ignoring all updates.
     */
    final public function setNoop(): void
    {
        $this->wrapper->getAPI()->setNoop();
    }
    /**
     * Set peer(s) where to send errors occurred in the event loop.
     *
     * @param int|string|array<int|string> $userOrId Username(s) or peer ID(s)
     */
    final public function setReportPeers(array|string|int $userOrId): void
    {
        $this->wrapper->getAPI()->setReportPeers($userOrId);
    }
    /**
     * Set webhook update handler.
     *
     * @param string $webhookUrl Webhook URL
     */
    final public function setWebhook(string $webhookUrl): void
    {
        $this->wrapper->getAPI()->setWebhook($webhookUrl);
    }
    /**
     * When called, skips to the next file in the playlist.
     */
    final public function skipPlay(int $id, \danog\MadelineProto\MediaDestination $dest = \danog\MadelineProto\MediaDestination::Camera): void
    {
        $this->wrapper->getAPI()->skipPlay($id, $dest);
    }
    /**
     * Asynchronously sleep.
     *
     * @param float $time Number of seconds to sleep for
     */
    final public static function sleep(float $time): void
    {
        \danog\MadelineProto\AsyncTools::sleep($time);
    }
    /**
     * Log in to telegram (via CLI or web).
     */
    final public function start(): array
    {
        return $this->wrapper->getAPI()->start();
    }
    /**
     * Start a [live story Â»](https://core.telegram.org/api/group-calls#live-stories): a livestream posted
     * as a story, of which we are the single publisher (everyone else joins as a listener).
     *
     * @param string|int                $peer                  Who to post the live story as: ourselves, or a group or channel we administer.
     * @param string|null               $caption               Caption of the story.
     * @param ParseMode|null            $parseMode             Whether to parse HTML or Markdown markup in the caption.
     * @param list<array<string, mixed>> $privacyRules          Who may see the story, as [InputPrivacyRule](https://core.telegram.org/type/InputPrivacyRule)s; everyone by default.
     * @param bool                      $pinned                Whether to pin the story to the profile.
     * @param bool                      $noForwards            Whether to forbid forwarding and screenshots.
     * @param bool                      $rtmpStream            Whether the media is published by an external RTMP application (see {@see self::getGroupCallStreamRtmpUrl()}) rather than by us.
     * @param bool|null                 $messagesEnabled       Whether viewers may comment with in-call messages.
     * @param int|null                  $sendPaidMessagesStars The minimum Telegram Stars donation required to comment, if any.
     */
    final public function startLive(string|int $peer, ?string $caption = null, ?\danog\MadelineProto\ParseMode $parseMode = null, array $privacyRules =  [
        0 =>
         [
             '_' => 'inputPrivacyValueAllowAll',
         ],
    ], bool $pinned = false, bool $noForwards = false, bool $rtmpStream = false, ?bool $messagesEnabled = null, ?int $sendPaidMessagesStars = null): \danog\MadelineProto\EventHandler\Calls\LiveStory
    {
        return $this->wrapper->getAPI()->startLive($peer, $caption, $parseMode, $privacyRules, $pinned, $noForwards, $rtmpStream, $messagesEnabled, $sendPaidMessagesStars);
    }
    /**
     * Stop update loop.
     */
    final public function stop(): void
    {
        $this->wrapper->getAPI()->stop();
    }
    /**
     * Stops playing all files in the call, clears the main and the hold playlist.
     */
    final public function stopPlay(int $id, \danog\MadelineProto\MediaDestination $dest = \danog\MadelineProto\MediaDestination::Camera): void
    {
        $this->wrapper->getAPI()->stopPlay($id, $dest);
    }
    /**
     * Converts a string into an async amphp stream.
     */
    final public static function stringToStream(string $str): \Amp\ByteStream\ReadableBuffer
    {
        return \danog\MadelineProto\Tools::stringToStream($str);
    }
    /**
     * Subscribe to event handler updates for a channel/supergroup we're not a member of.
     *
     * @param mixed $channel Channel/supergroup to subscribe to
     *
     * @return bool False if we were already subscribed
     */
    final public function subscribeToUpdates(mixed $channel): bool
    {
        return $this->wrapper->getAPI()->subscribeToUpdates($channel);
    }
    /**
     * Convert TD to MTProto parameters.
     *
     * @param array $params Parameters
     */
    final public function tdToMTProto(array $params): array
    {
        return $this->wrapper->getAPI()->tdToMTProto($params);
    }
    /**
     * Convert TD parameters to tdcli.
     *
     * @param mixed $params Parameters
     *
     * @psalm-mutation-free
     */
    final public function tdToTdcli(mixed $params): array
    {
        return $this->wrapper->getAPI()->tdToTdcli($params);
    }
    /**
     * Convert tdcli parameters to tdcli.
     *
     * @param mixed $params Params
     * @param array $key    Key
     */
    final public function tdcliToTd(&$params, ?array $key = null): array
    {
        return $this->wrapper->getAPI()->tdcliToTd($params, $key);
    }
    /**
     * Test fibers.
     *
     * @return array{maxFibers: int, realMemoryMb: int, maps: ?int, maxMaps: ?int}
     */
    final public static function testFibers(int $fiberCount = 100000): array
    {
        return \danog\MadelineProto\Tools::testFibers($fiberCount);
    }
    /**
     * Convert to camelCase.
     *
     * @param string $input String
     *
     * @psalm-pure
     */
    final public static function toCamelCase(string $input): string
    {
        return \danog\MadelineProto\StrTools::toCamelCase($input);
    }
    /**
     * Convert to snake_case.
     *
     * @param string $input String
     */
    final public static function toSnakeCase(string $input): string
    {
        return \danog\MadelineProto\StrTools::toSnakeCase($input);
    }
    /**
     * Unpack binary double.
     *
     * @param string $value Value to unpack
     *
     * @psalm-external-mutation-free
     */
    final public static function unpackDouble(string $value): float
    {
        return \danog\MadelineProto\Tools::unpackDouble($value);
    }
    /**
     * Unpack bot API file ID.
     *
     * @param  string $fileId Bot API file ID
     * @return array  Unpacked file ID
     */
    final public static function unpackFileId(string $fileId): array
    {
        return \danog\MadelineProto\MTProto::unpackFileId($fileId);
    }
    /**
     * Unpack base256 signed int.
     *
     * @param string $value base256 int
     *
     * @psalm-external-mutation-free
     */
    final public static function unpackSignedInt(string $value): int
    {
        return \danog\MadelineProto\Tools::unpackSignedInt($value);
    }
    /**
     * Unpack base256 signed long.
     *
     * @param string $value base256 long
     *
     * @psalm-external-mutation-free
     */
    final public static function unpackSignedLong(string $value): int
    {
        return \danog\MadelineProto\Tools::unpackSignedLong($value);
    }
    /**
     * Unpack base256 signed long to string.
     *
     * @param string|int|array $value base256 long
     *
     * @psalm-external-mutation-free
     */
    final public static function unpackSignedLongString(array|string|int $value): string
    {
        return \danog\MadelineProto\Tools::unpackSignedLongString($value);
    }
    /**
     * Unset event handler.
     *
     */
    final public function unsetEventHandler(): void
    {
        $this->wrapper->getAPI()->unsetEventHandler();
    }
    /**
     * Update the 2FA password.
     *
     * The params array can contain password, new_password, email and hint params.
     *
     * @param array{password?: string, new_password?: string, email?: string, hint?: string} $params The params
     */
    final public function update2fa(array $params): void
    {
        $this->wrapper->getAPI()->update2fa($params);
    }
    /**
     * Parse, update and store settings.
     *
     * @param SettingsAbstract $settings Settings
     */
    final public function updateSettings(\danog\MadelineProto\SettingsAbstract $settings): void
    {
        $this->wrapper->getAPI()->updateSettings($settings);
    }
    /**
     * Upload file.
     *
     * @param FileCallbackInterface|LocalFile|RemoteUrl|BotApiFileId|ReadableStream|string|array|resource $file      File, URL or Telegram file to upload
     * @param string                                                                       $fileName  File name
     * @param callable                                                                     $cb        Callback
     * @param boolean                                                                      $encrypted Whether to encrypt file for secret chats
     *
     * @return array InputFile constructor
     */
    final public function upload($file, string $fileName = '', ?callable $cb = null, bool $encrypted = false, ?\Amp\Cancellation $cancellation = null): array
    {
        return $this->wrapper->getAPI()->upload($file, $fileName, $cb, $encrypted, $cancellation);
    }
    /**
     * Uploads an audio without actually sending it.
     *
     * Please use named arguments to call this method.
     *
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file                   File to upload: can be a message to reuse media present in a message.
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream|null $thumb                  Optional: Thumbnail to upload
     * @param integer|string|null                                                $peer                   Optional: associate the audio to the specified peer or username.
     * @param string                                                        $caption                Caption of document
     * @param ParseMode                                                     $parseMode              Text parse mode for the caption
     * @param ?callable(float, float, float)                                $callback               Upload callback (percent, speed in mbps, seconds elapsed)
     * @param ?string                                                       $fileName               Optional file name, if absent will be extracted from the passed $file.
     * @param integer|null                                                  $duration                Duration of the audio
     * @param string|null                                                   $title                   Title of the audio
     * @param string|null                                                   $performer               Performer of the audio
     * @param integer|null                                                  $replyToMsgId            ID of message to reply to.
     * @param integer|null                                                  $topMsgId                ID of thread where to send the message.
     * @param array|null                                                    $replyMarkup             Keyboard information.
     * @param integer|string|null                                           $sendAs                 Peer to send the message as.
     * @param integer|null                                                  $scheduleDate            Schedule date.
     * @param boolean                                                       $silent                  Whether to send the message silently, without triggering notifications.
     * @param boolean                                                       $noForwards              Whether to disable forwards for this message.
     * @param boolean                                                       $background              Send this message as background message
     * @param boolean                                                       $clearDraft              Clears the draft field
     * @param boolean                                                       $forceResend             Whether to forcefully resend the file, even if its type and name are the same.
     * @param ?Cancellation                                                  $cancellation            Cancellation.
     *
     */
    final public function uploadAudio(\danog\MadelineProto\EventHandler\Message|\danog\MadelineProto\EventHandler\Media|\danog\MadelineProto\LocalFile|\danog\MadelineProto\RemoteUrl|\danog\MadelineProto\BotApiFileId|\Amp\ByteStream\ReadableStream $file, \danog\MadelineProto\EventHandler\Message|\danog\MadelineProto\EventHandler\Media|\danog\MadelineProto\LocalFile|\danog\MadelineProto\RemoteUrl|\danog\MadelineProto\BotApiFileId|\Amp\ByteStream\ReadableStream|null $thumb = null, string|int|null $peer = null, string $caption = '', \danog\MadelineProto\ParseMode $parseMode = \danog\MadelineProto\ParseMode::TEXT, ?callable $callback = null, ?string $fileName = null, ?string $mimeType = null, ?int $duration = null, ?string $title = null, ?string $performer = null, ?int $replyToMsgId = null, ?int $topMsgId = null, ?array $replyMarkup = null, string|int|null $sendAs = null, ?int $scheduleDate = null, bool $silent = false, bool $noForwards = false, bool $background = false, bool $clearDraft = false, bool $forceResend = false, ?\Amp\Cancellation $cancellation = null): \danog\MadelineProto\EventHandler\Media
    {
        return $this->wrapper->getAPI()->uploadAudio($file, $thumb, $peer, $caption, $parseMode, $callback, $fileName, $mimeType, $duration, $title, $performer, $replyToMsgId, $topMsgId, $replyMarkup, $sendAs, $scheduleDate, $silent, $noForwards, $background, $clearDraft, $forceResend, $cancellation);
    }
    /**
     * Uploads a document without sending it.
     *
     * Please use named arguments to call this method.
     *
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream      $file                   File to upload: can be a message to reuse media present in a message.
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream|null $thumb                  Optional: Thumbnail to upload
     * @param integer|string|null                                                     $peer              Optional: associate the media to the specified peer or username.
     * @param string                                                             $caption                Caption of document
     * @param ?callable(float, float, float)                                     $callback               Upload callback (percent, speed in mbps, seconds elapsed)
     * @param ?string                                                            $fileName               Optional file name, if absent will be extracted from the passed $file.
     * @param ParseMode                                                          $parseMode              Text parse mode for the caption
     * @param integer|null                                                       $replyToMsgId           ID of message to reply to.
     * @param integer|null                                                       $topMsgId               ID of thread where to send the message.
     * @param array|null                                                         $replyMarkup            Keyboard information.
     * @param integer|null                                                       $sendAs                 Peer to send the message as.
     * @param integer|null                                                       $scheduleDate           Schedule date.
     * @param boolean                                                            $silent                 Whether to send the message silently, without triggering notifications.
     * @param boolean                                                            $background             Send this message as background message
     * @param boolean                                                            $clearDraft             Clears the draft field
     * @param boolean                                                            $updateStickersetsOrder Whether to move used stickersets to top
     * @param boolean                                                            $forceResend            Whether to forcefully resend the file, even if its type and name are the same.
     * @param Cancellation                                                       $cancellation           Cancellation.
     *
     */
    final public function uploadDocument(\danog\MadelineProto\EventHandler\Message|\danog\MadelineProto\EventHandler\Media|\danog\MadelineProto\LocalFile|\danog\MadelineProto\RemoteUrl|\danog\MadelineProto\BotApiFileId|\Amp\ByteStream\ReadableStream $file, \danog\MadelineProto\EventHandler\Message|\danog\MadelineProto\EventHandler\Media|\danog\MadelineProto\LocalFile|\danog\MadelineProto\RemoteUrl|\danog\MadelineProto\BotApiFileId|\Amp\ByteStream\ReadableStream|null $thumb = null, string|int|null $peer = null, string $caption = '', \danog\MadelineProto\ParseMode $parseMode = \danog\MadelineProto\ParseMode::TEXT, ?callable $callback = null, ?string $fileName = null, ?string $mimeType = null, bool $spoiler = false, ?int $replyToMsgId = null, ?int $topMsgId = null, ?array $replyMarkup = null, string|int|null $sendAs = null, ?int $scheduleDate = null, bool $silent = false, bool $noForwards = false, bool $background = false, bool $clearDraft = false, bool $updateStickersetsOrder = false, bool $forceResend = false, ?\Amp\Cancellation $cancellation = null): \danog\MadelineProto\EventHandler\Media
    {
        return $this->wrapper->getAPI()->uploadDocument($file, $thumb, $peer, $caption, $parseMode, $callback, $fileName, $mimeType, $spoiler, $replyToMsgId, $topMsgId, $replyMarkup, $sendAs, $scheduleDate, $silent, $noForwards, $background, $clearDraft, $updateStickersetsOrder, $forceResend, $cancellation);
    }
    /**
     * Uploads a photo without sending it to the chat.
     *
     * Please use named arguments to call this method.
     *
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file                   File to upload: can be a message to reuse media present in a message.
     * @param string                                                        $caption                Caption of document
     * @param integer|string|null                                           $peer                   Optional: associate the file to the destination peer or username.
     * @param ?callable(float, float, float)                                $callback               Upload callback (percent, speed in mbps, seconds elapsed)
     * @param ?string                                                       $fileName               Optional file name, if absent will be extracted from the passed $file.
     * @param ParseMode                                                     $parseMode              Text parse mode for the caption
     * @param integer|null                                                  $replyToMsgId           ID of message to reply to.
     * @param integer|null                                                  $topMsgId               ID of thread where to send the message.
     * @param array|null                                                    $replyMarkup            Keyboard information.
     * @param integer|null                                                  $sendAs                 Peer to send the message as.
     * @param integer|null                                                  $scheduleDate           Schedule date.
     * @param boolean                                                       $silent                 Whether to send the message silently, without triggering notifications.
     * @param boolean                                                       $background             Send this message as background message
     * @param boolean                                                       $clearDraft             Clears the draft field
     * @param boolean                                                       $updateStickersetsOrder Whether to move used stickersets to top
     * @param boolean                                                       $forceResend            Whether to forcefully resend the file, even if its type and name are the same.
     * @param Cancellation                                                  $cancellation           Cancellation.
     *
     */
    final public function uploadDocumentPhoto(\danog\MadelineProto\EventHandler\Message|\danog\MadelineProto\EventHandler\Media|\danog\MadelineProto\LocalFile|\danog\MadelineProto\RemoteUrl|\danog\MadelineProto\BotApiFileId|\Amp\ByteStream\ReadableStream $file, string|int|null $peer = null, string $caption = '', \danog\MadelineProto\ParseMode $parseMode = \danog\MadelineProto\ParseMode::TEXT, ?callable $callback = null, ?string $fileName = null, bool $spoiler = false, ?int $replyToMsgId = null, ?int $topMsgId = null, ?array $replyMarkup = null, string|int|null $sendAs = null, ?int $scheduleDate = null, bool $silent = false, bool $noForwards = false, bool $background = false, bool $clearDraft = false, bool $updateStickersetsOrder = false, bool $forceResend = false, ?\Amp\Cancellation $cancellation = null): \danog\MadelineProto\EventHandler\Media
    {
        return $this->wrapper->getAPI()->uploadDocumentPhoto($file, $peer, $caption, $parseMode, $callback, $fileName, $spoiler, $replyToMsgId, $topMsgId, $replyMarkup, $sendAs, $scheduleDate, $silent, $noForwards, $background, $clearDraft, $updateStickersetsOrder, $forceResend, $cancellation);
    }
    /**
     * Upload file to secret chat.
     *
     * @param FileCallbackInterface|LocalFile|RemoteUrl|BotApiFileId|string|array|resource $file      File, URL or Telegram file to upload
     * @param string                             $fileName File name
     * @param callable                           $cb       Callback
     *
     * @return array InputFile constructor
     */
    final public function uploadEncrypted($file, string $fileName = '', ?callable $cb = null, ?\Amp\Cancellation $cancellation = null): array
    {
        return $this->wrapper->getAPI()->uploadEncrypted($file, $fileName, $cb, $cancellation);
    }
    /**
     * Upload file from callable.
     *
     * The callable must accept two parameters: int $offset, int $size
     * The callable must return a string with the contest of the file at the specified offset and size.
     *
     * @param (callable(int, int, ?Cancellation): string) $callable  Callable (offset, length) => data
     * @param integer                                   $size      File size
     * @param ?string                                    $mime      Mime type
     * @param string                                    $fileName  File name
     * @param (callable(float, float, float): void)       $cb        Status callback
     * @param boolean                                   $seekable  Whether chunks can be fetched out of order
     * @param boolean                                   $encrypted Whether to encrypt file for secret chats
     *
     * @return array InputFile constructor
     */
    final public function uploadFromCallable(callable $callable, int $size = 0, ?string $mime = null, string $fileName = '', ?callable $cb = null, bool $seekable = true, bool $encrypted = false, ?\Amp\Cancellation $cancellation = null): array
    {
        return $this->wrapper->getAPI()->uploadFromCallable($callable, $size, $mime, $fileName, $cb, $seekable, $encrypted, $cancellation);
    }
    /**
     * Upload file from stream.
     *
     * @param mixed    $stream    PHP resource or AMPHP async stream
     * @param integer  $size      File size
     * @param string   $mime      Mime type
     * @param string   $fileName  File name
     * @param callable $cb        Callback
     * @param boolean  $encrypted Whether to encrypt file for secret chats
     *
     * @return array InputFile constructor
     */
    final public function uploadFromStream(mixed $stream, int $size = 0, ?string $mime = null, string $fileName = '', ?callable $cb = null, bool $encrypted = false, ?\Amp\Cancellation $cancellation = null): array
    {
        return $this->wrapper->getAPI()->uploadFromStream($stream, $size, $mime, $fileName, $cb, $encrypted, $cancellation);
    }
    /**
     * Reupload telegram file.
     *
     * @param mixed    $media     Telegram file
     * @param callable $cb        Callback
     * @param boolean  $encrypted Whether to encrypt file for secret chats
     *
     * @return array InputFile constructor
     */
    final public function uploadFromTgfile(mixed $media, ?callable $cb = null, bool $encrypted = false, ?\Amp\Cancellation $cancellation = null): array
    {
        return $this->wrapper->getAPI()->uploadFromTgfile($media, $cb, $encrypted, $cancellation);
    }
    /**
     * Upload file from URL.
     *
     * @param  string|FileCallbackInterface $url       URL of file
     * @param  integer                      $size      Size of file
     * @param  string                       $fileName  File name
     * @param  callable                     $cb        Callback
     * @param  boolean                      $encrypted Whether to encrypt file for secret chats
     * @return array                        InputFile constructor
     */
    final public function uploadFromUrl(\danog\MadelineProto\FileCallbackInterface|string $url, int $size = 0, string $fileName = '', ?callable $cb = null, bool $encrypted = false, ?\Amp\Cancellation $cancellation = null): array
    {
        return $this->wrapper->getAPI()->uploadFromUrl($url, $size, $fileName, $cb, $encrypted, $cancellation);
    }
    /**
     * Uploads a gif without actually sending it.
     *
     * Please use named arguments to call this method.
     *
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file                   File to upload: can be a message to reuse media present in a message.
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream|null $thumb                  Optional: Thumbnail to upload
     * @param integer|string|null                                                $peer                   Optional: associate the media to the peer or username.
     * @param string                                                        $caption                Caption of document
     * @param ParseMode                                                     $parseMode              Text parse mode for the caption
     * @param ?callable(float, float, float)                                $callback               Upload callback (percent, speed in mbps, seconds elapsed)
     * @param ?string                                                       $fileName               Optional file name, if absent will be extracted from the passed $file.
     * @param boolean                                                       $spoiler                 Whether the message is a spoiler
     * @param integer|null                                                  $replyToMsgId            ID of message to reply to.
     * @param integer|null                                                  $topMsgId                ID of thread where to send the message.
     * @param array|null                                                    $replyMarkup             Keyboard information.
     * @param integer|string|null                                           $sendAs                 Peer to send the message as.
     * @param integer|null                                                  $scheduleDate            Schedule date.
     * @param boolean                                                       $silent                  Whether to send the message silently, without triggering notifications.
     * @param boolean                                                       $noForwards              Whether to disable forwards for this message.
     * @param boolean                                                       $background              Send this message as background message
     * @param boolean                                                       $clearDraft              Clears the draft field
     * @param boolean                                                       $forceResend             Whether to forcefully resend the file, even if its type and name are the same.
     * @param ?Cancellation                                                  $cancellation            Cancellation.
     *
     */
    final public function uploadGif(\danog\MadelineProto\EventHandler\Message|\danog\MadelineProto\EventHandler\Media|\danog\MadelineProto\LocalFile|\danog\MadelineProto\RemoteUrl|\danog\MadelineProto\BotApiFileId|\Amp\ByteStream\ReadableStream $file, \danog\MadelineProto\EventHandler\Message|\danog\MadelineProto\EventHandler\Media|\danog\MadelineProto\LocalFile|\danog\MadelineProto\RemoteUrl|\danog\MadelineProto\BotApiFileId|\Amp\ByteStream\ReadableStream|null $thumb = null, string|int|null $peer = null, string $caption = '', \danog\MadelineProto\ParseMode $parseMode = \danog\MadelineProto\ParseMode::TEXT, ?callable $callback = null, ?string $fileName = null, bool $spoiler = false, ?int $duration = null, ?int $width = null, ?int $height = null, string $thumbSeek = '00:00:01.000', ?int $replyToMsgId = null, ?int $topMsgId = null, ?array $replyMarkup = null, string|int|null $sendAs = null, ?int $scheduleDate = null, bool $silent = false, bool $noForwards = false, bool $background = false, bool $clearDraft = false, bool $forceResend = false, ?\Amp\Cancellation $cancellation = null): \danog\MadelineProto\EventHandler\Media
    {
        return $this->wrapper->getAPI()->uploadGif($file, $thumb, $peer, $caption, $parseMode, $callback, $fileName, $spoiler, $duration, $width, $height, $thumbSeek, $replyToMsgId, $topMsgId, $replyMarkup, $sendAs, $scheduleDate, $silent, $noForwards, $background, $clearDraft, $forceResend, $cancellation);
    }
    /**
     * Uploads a photo, without sending it.
     *
     * Please use named arguments to call this method.
     *
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file                   File to upload: can be a message to reuse media present in a message.
     * @param string                                                        $caption                Caption of document
     * @param integer|string|null                                           $peer                   Optional: associate the media to the specified peer or username.
     * @param ?callable(float, float, float)                                $callback               Upload callback (percent, speed in mbps, seconds elapsed)
     * @param ?string                                                       $fileName               Optional file name, if absent will be extracted from the passed $file.
     * @param ParseMode                                                     $parseMode              Text parse mode for the caption
     * @param integer|null                                                  $replyToMsgId           ID of message to reply to.
     * @param integer|null                                                  $topMsgId               ID of thread where to send the message.
     * @param array|null                                                    $replyMarkup            Keyboard information.
     * @param integer|null                                                  $sendAs                 Peer to send the message as.
     * @param integer|null                                                  $scheduleDate           Schedule date.
     * @param boolean                                                       $silent                 Whether to send the message silently, without triggering notifications.
     * @param boolean                                                       $background             Send this message as background message
     * @param boolean                                                       $clearDraft             Clears the draft field
     * @param boolean                                                       $updateStickersetsOrder Whether to move used stickersets to top
     * @param boolean                                                       $forceResend            Whether to forcefully resend the file, even if its type and name are the same.
     * @param Cancellation                                                  $cancellation           Cancellation.
     *
     */
    final public function uploadPhoto(\danog\MadelineProto\EventHandler\Message|\danog\MadelineProto\EventHandler\Media|\danog\MadelineProto\LocalFile|\danog\MadelineProto\RemoteUrl|\danog\MadelineProto\BotApiFileId|\Amp\ByteStream\ReadableStream $file, string|int|null $peer = null, string $caption = '', \danog\MadelineProto\ParseMode $parseMode = \danog\MadelineProto\ParseMode::TEXT, ?callable $callback = null, ?string $fileName = null, bool $spoiler = false, ?int $replyToMsgId = null, ?int $topMsgId = null, ?array $replyMarkup = null, string|int|null $sendAs = null, ?int $scheduleDate = null, bool $silent = false, bool $noForwards = false, bool $background = false, bool $clearDraft = false, bool $updateStickersetsOrder = false, bool $forceResend = false, ?\Amp\Cancellation $cancellation = null): \danog\MadelineProto\EventHandler\Media
    {
        return $this->wrapper->getAPI()->uploadPhoto($file, $peer, $caption, $parseMode, $callback, $fileName, $spoiler, $replyToMsgId, $topMsgId, $replyMarkup, $sendAs, $scheduleDate, $silent, $noForwards, $background, $clearDraft, $updateStickersetsOrder, $forceResend, $cancellation);
    }
    /**
     * Uploads a sticker without sending it.
     *
     * Please use named arguments to call this method.
     *
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file                   File to upload: can be a message to reuse media present in a message.
     * @param ?callable(float, float, float)                                $callback               Upload callback (percent, speed in mbps, seconds elapsed)
     * @param ?string                                                       $fileName               Optional file name, if absent will be extracted from the passed $file.
     * @param integer|string|null                                           $peer                   Optional: associate the file to the specified peer or username.
     * @param integer|null                                                  $replyToMsgId           ID of message to reply to.
     * @param integer|null                                                  $topMsgId               ID of thread where to send the message.
     * @param array|null                                                    $replyMarkup            Keyboard information.
     * @param integer|null                                                  $sendAs                 Peer to send the message as.
     * @param integer|null                                                  $scheduleDate           Schedule date.
     * @param boolean                                                       $silent                 Whether to send the message silently, without triggering notifications.
     * @param boolean                                                       $noForwards             Whether to disable forwards for this message.
     * @param boolean                                                       $background             Send this message as background message
     * @param boolean                                                       $clearDraft             Clears the draft field
     * @param boolean                                                       $updateStickersetsOrder Whether to move used stickersets to top
     * @param boolean                                                       $forceResend            Whether to forcefully resend the file, even if its type and name are the same.
     * @param Cancellation                                                  $cancellation           Cancellation.
     *
     */
    final public function uploadSticker(\danog\MadelineProto\EventHandler\Message|\danog\MadelineProto\EventHandler\Media|\danog\MadelineProto\LocalFile|\danog\MadelineProto\RemoteUrl|\danog\MadelineProto\BotApiFileId|\Amp\ByteStream\ReadableStream $file, string $mimeType, string|int|null $peer = null, string $emoji = '', array $stickerSet =  [
        '_' => 'inputStickerSetEmpty',
    ], ?callable $callback = null, ?string $fileName = null, ?int $replyToMsgId = null, ?int $topMsgId = null, ?array $replyMarkup = null, string|int|null $sendAs = null, ?int $scheduleDate = null, bool $silent = false, bool $noForwards = false, bool $background = false, bool $clearDraft = false, bool $updateStickersetsOrder = false, bool $forceResend = false, ?\Amp\Cancellation $cancellation = null): \danog\MadelineProto\EventHandler\Media
    {
        return $this->wrapper->getAPI()->uploadSticker($file, $mimeType, $peer, $emoji, $stickerSet, $callback, $fileName, $replyToMsgId, $topMsgId, $replyMarkup, $sendAs, $scheduleDate, $silent, $noForwards, $background, $clearDraft, $updateStickersetsOrder, $forceResend, $cancellation);
    }
    /**
     * Uploads a video, without actually sending it.
     *
     * Please use named arguments to call this method.
     *
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file                   File to upload: can be a message to reuse media present in a message.
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream|null $thumb                  Optional: Thumbnail to upload
     * @param integer|string|null                                           $peer                   Optional: associate the media to the specified peer or username.
     * @param string                                                        $caption                Caption of document
     * @param ParseMode                                                     $parseMode              Text parse mode for the caption
     * @param ?callable(float, float, float)                                $callback               Upload callback (percent, speed in mbps, seconds elapsed)
     * @param ?string                                                       $fileName               Optional file name, if absent will be extracted from the passed $file.
     * @param boolean                                                       $spoiler                 Whether the message is a spoiler
     * @param boolean                                                       $roundMessage            Whether the message should be round
     * @param boolean                                                       $supportsStreaming        Whether the video supports streaming
     * @param boolean                                                       $noSound                 Whether the video has no sound
     * @param integer|null                                                  $duration                Duration of the video
     * @param integer|null                                                  $width                   Width of the video
     * @param integer|null                                                  $height                  Height of the video
     * @param integer|null                                                  $replyToMsgId            ID of message to reply to.
     * @param integer|null                                                  $topMsgId                ID of thread where to send the message.
     * @param array|null                                                    $replyMarkup             Keyboard information.
     * @param integer|string|null                                           $sendAs                 Peer to send the message as.
     * @param integer|null                                                  $scheduleDate            Schedule date.
     * @param boolean                                                       $silent                  Whether to send the message silently, without triggering notifications.
     * @param boolean                                                       $noForwards              Whether to disable forwards for this message.
     * @param boolean                                                       $background              Send this message as background message
     * @param boolean                                                       $clearDraft              Clears the draft field
     * @param boolean                                                       $forceResend             Whether to forcefully resend the file, even if its type and name are the same.
     * @param Cancellation                                                  $cancellation            Cancellation.
     *
     */
    final public function uploadVideo(\danog\MadelineProto\EventHandler\Message|\danog\MadelineProto\EventHandler\Media|\danog\MadelineProto\LocalFile|\danog\MadelineProto\RemoteUrl|\danog\MadelineProto\BotApiFileId|\Amp\ByteStream\ReadableStream $file, \danog\MadelineProto\EventHandler\Message|\danog\MadelineProto\EventHandler\Media|\danog\MadelineProto\LocalFile|\danog\MadelineProto\RemoteUrl|\danog\MadelineProto\BotApiFileId|\Amp\ByteStream\ReadableStream|null $thumb = null, string|int|null $peer = null, string $caption = '', \danog\MadelineProto\ParseMode $parseMode = \danog\MadelineProto\ParseMode::TEXT, ?callable $callback = null, ?string $fileName = null, string $mimeType = 'video/mp4', bool $spoiler = false, bool $roundMessage = false, bool $supportsStreaming = true, bool $noSound = false, ?int $duration = null, ?int $width = null, ?int $height = null, string $thumbSeek = '00:00:01.000', ?int $replyToMsgId = null, ?int $topMsgId = null, ?array $replyMarkup = null, string|int|null $sendAs = null, ?int $scheduleDate = null, bool $silent = false, bool $noForwards = false, bool $background = false, bool $clearDraft = false, bool $forceResend = false, bool $updateStickersetsOrder = false, ?\Amp\Cancellation $cancellation = null): \danog\MadelineProto\EventHandler\Media
    {
        return $this->wrapper->getAPI()->uploadVideo($file, $thumb, $peer, $caption, $parseMode, $callback, $fileName, $mimeType, $spoiler, $roundMessage, $supportsStreaming, $noSound, $duration, $width, $height, $thumbSeek, $replyToMsgId, $topMsgId, $replyMarkup, $sendAs, $scheduleDate, $silent, $noForwards, $background, $clearDraft, $forceResend, $updateStickersetsOrder, $cancellation);
    }
    /**
     * Uploads a voice without actually sending it to the chat.
     *
     * Please use named arguments to call this method.
     *
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file                   File to upload: can be a message to reuse media present in a message.
     * @param integer|string|null                                                $peer                   Optional: associate the media to the specified peer or username.
     * @param string                                                        $caption                Caption of document
     * @param ParseMode                                                     $parseMode              Text parse mode for the caption
     * @param ?callable(float, float, float)                                $callback               Upload callback (percent, speed in mbps, seconds elapsed)
     * @param ?string                                                       $fileName               Optional file name, if absent will be extracted from the passed $file.
     * @param integer|null                                                  $duration                Duration of the voice
     * @param array|null                                                    $waveform                Waveform of the voice
     * @param integer|null                                                  $replyToMsgId            ID of message to reply to.
     * @param integer|null                                                  $topMsgId                ID of thread where to send the message.
     * @param array|null                                                    $replyMarkup             Keyboard information.
     * @param integer|string|null                                           $sendAs                 Peer to send the message as.
     * @param integer|null                                                  $scheduleDate            Schedule date.
     * @param boolean                                                       $silent                  Whether to send the message silently, without triggering notifications.
     * @param boolean                                                       $noForwards              Whether to disable forwards for this message.
     * @param boolean                                                       $background              Send this message as background message
     * @param boolean                                                       $clearDraft              Clears the draft field
     * @param boolean                                                       $forceResend             Whether to forcefully resend the file, even if its type and name are the same.
     * @param ?Cancellation                                                  $cancellation            Cancellation.
     *
     */
    final public function uploadVoice(string|int $peer, \danog\MadelineProto\EventHandler\Message|\danog\MadelineProto\EventHandler\Media|\danog\MadelineProto\LocalFile|\danog\MadelineProto\RemoteUrl|\danog\MadelineProto\BotApiFileId|\Amp\ByteStream\ReadableStream $file, string $caption = '', \danog\MadelineProto\ParseMode $parseMode = \danog\MadelineProto\ParseMode::TEXT, ?callable $callback = null, ?string $fileName = null, ?int $duration = null, ?array $waveform = null, ?int $replyToMsgId = null, ?int $topMsgId = null, ?array $replyMarkup = null, string|int|null $sendAs = null, ?int $scheduleDate = null, bool $silent = false, bool $noForwards = false, bool $background = false, bool $clearDraft = false, bool $forceResend = false, ?\Amp\Cancellation $cancellation = null): \danog\MadelineProto\EventHandler\Media
    {
        return $this->wrapper->getAPI()->uploadVoice($peer, $file, $caption, $parseMode, $callback, $fileName, $duration, $waveform, $replyToMsgId, $topMsgId, $replyMarkup, $sendAs, $scheduleDate, $silent, $noForwards, $background, $clearDraft, $forceResend, $cancellation);
    }
    /**
     * Perform static analysis on a certain event handler class, to make sure it satisfies some performance requirements.
     *
     * @param class-string<EventHandler> $class Class name
     *
     * @return list<EventHandlerIssue>
     */
    final public static function validateEventHandlerClass(string $class): array
    {
        return \danog\MadelineProto\Tools::validateEventHandlerClass($class);
    }
    /**
     * Mark sponsored message as read.
     *
     * @param int|array                       $peer    Channel ID, or Update, or Message, or Peer.
     * @param string|array{random_id: string} $message Random ID or sponsored message to mark as read.
     */
    final public function viewSponsoredMessage(array|int $peer, array|string $message): bool
    {
        return $this->wrapper->getAPI()->viewSponsoredMessage($peer, $message);
    }
    /**
     * Wrap a media constructor into an abstract Media object.
     */
    final public function wrapMedia(array $media, bool $protected = false): ?\danog\MadelineProto\EventHandler\Media
    {
        return $this->wrapper->getAPI()->wrapMedia($media, $protected);
    }
    /**
     * Wrap a Message constructor into an abstract Message object.
     */
    final public function wrapMessage(array $message, bool $scheduled = false): ?\danog\MadelineProto\EventHandler\AbstractMessage
    {
        return $this->wrapper->getAPI()->wrapMessage($message, $scheduled);
    }
    /**
     * Wrap a Pin constructor into an abstract Pinned object.
     */
    final public function wrapPin(array $message): ?\danog\MadelineProto\EventHandler\Pinned
    {
        return $this->wrapper->getAPI()->wrapPin($message);
    }
    /**
     * Wrap an Update constructor into an abstract Update object.
     */
    final public function wrapUpdate(array $update): ?\danog\MadelineProto\EventHandler\Update
    {
        return $this->wrapper->getAPI()->wrapUpdate($update);
    }
}
{"ok":true,"result":{"420":{"account.deleteAccount":{"2FA_CONFIRM_WAIT_%d":"2FA_CONFIRM_WAIT_%d"},"channels.createChannel":{"ADDRESS_INVALID":"ADDRESS_INVALID"},"upload.getFile":{"FLOOD_PREMIUM_WAIT_%d":"FLOOD_PREMIUM_WAIT_%d"},"channels.deleteMessages":{"FROZEN_METHOD_INVALID":"FROZEN_METHOD_INVALID"},"channels.joinChannel":{"FROZEN_METHOD_INVALID":"FROZEN_METHOD_INVALID"},"channels.searchPosts":{"FROZEN_METHOD_INVALID":"FROZEN_METHOD_INVALID"},"payments.applyGiftCode":{"PREMIUM_SUB_ACTIVE_UNTIL_%d":"PREMIUM_SUB_ACTIVE_UNTIL_%d"},"messages.forwardMessages":{"SLOWMODE_WAIT_%d":"SLOWMODE_WAIT_%d"},"messages.sendInlineBotResult":{"SLOWMODE_WAIT_%d":"SLOWMODE_WAIT_%d"},"messages.sendMedia":{"SLOWMODE_WAIT_%d":"SLOWMODE_WAIT_%d"},"messages.sendMessage":{"SLOWMODE_WAIT_%d":"SLOWMODE_WAIT_%d"},"messages.sendMultiMedia":{"SLOWMODE_WAIT_%d":"SLOWMODE_WAIT_%d"},"account.initTakeoutSession":{"TAKEOUT_INIT_DELAY_%d":"TAKEOUT_INIT_DELAY_%d"}},"400":{"account.updateProfile":{"ABOUT_TOO_LONG":"ABOUT_TOO_LONG","BUSINESS_CONNECTION_INVALID":"BUSINESS_CONNECTION_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","FIRSTNAME_INVALID":"FIRSTNAME_INVALID"},"auth.importBotAuthorization":{"ACCESS_TOKEN_EXPIRED":"ACCESS_TOKEN_EXPIRED","ACCESS_TOKEN_INVALID":"ACCESS_TOKEN_INVALID","API_ID_INVALID":"API_ID_INVALID","API_ID_PUBLISHED_FLOOD":"API_ID_PUBLISHED_FLOOD","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"channels.reportSponsoredMessage":{"AD_EXPIRED":"AD_EXPIRED","CHANNEL_INVALID":"CHANNEL_INVALID","PREMIUM_ACCOUNT_REQUIRED":"PREMIUM_ACCOUNT_REQUIRED"},"channels.createChannel":{"ADDRESS_INVALID":"ADDRESS_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNELS_ADMIN_LOCATED_TOO_MUCH":"CHANNELS_ADMIN_LOCATED_TOO_MUCH","CHANNELS_TOO_MUCH":"CHANNELS_TOO_MUCH","CHAT_ABOUT_TOO_LONG":"CHAT_ABOUT_TOO_LONG","CHAT_TITLE_EMPTY":"CHAT_TITLE_EMPTY","TTL_PERIOD_INVALID":"TTL_PERIOD_INVALID"},"messages.deleteRevokedExportedChatInvites":{"ADMIN_ID_INVALID":"ADMIN_ID_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.getExportedChatInvites":{"ADMIN_ID_INVALID":"ADMIN_ID_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_ID_INVALID":"CHAT_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID"},"channels.editAdmin":{"ADMIN_RANK_EMOJI_NOT_ALLOWED":"ADMIN_RANK_EMOJI_NOT_ALLOWED","ADMIN_RANK_INVALID":"ADMIN_RANK_INVALID","ADMINS_TOO_MUCH":"ADMINS_TOO_MUCH","BOT_CHANNELS_NA":"BOT_CHANNELS_NA","BOT_GROUPS_BLOCKED":"BOT_GROUPS_BLOCKED","BOTS_TOO_MUCH":"BOTS_TOO_MUCH","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_MONOFORUM_UNSUPPORTED":"CHANNEL_MONOFORUM_UNSUPPORTED","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","FRESH_CHANGE_ADMINS_FORBIDDEN":"FRESH_CHANGE_ADMINS_FORBIDDEN","INPUT_USER_DEACTIVATED":"INPUT_USER_DEACTIVATED","MSG_ID_INVALID":"MSG_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID","USER_BLOCKED":"USER_BLOCKED","USER_CREATOR":"USER_CREATOR","USER_ID_INVALID":"USER_ID_INVALID","USER_KICKED":"USER_KICKED","USER_NOT_MUTUAL_CONTACT":"USER_NOT_MUTUAL_CONTACT","USERS_TOO_MUCH":"USERS_TOO_MUCH"},"messages.sendMessage":{"ADMIN_RIGHTS_EMPTY":"ADMIN_RIGHTS_EMPTY","BALANCE_TOO_LOW":"BALANCE_TOO_LOW","BOT_DOMAIN_INVALID":"BOT_DOMAIN_INVALID","BOT_INVALID":"BOT_INVALID","BUSINESS_CONNECTION_INVALID":"BUSINESS_CONNECTION_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","BUSINESS_PEER_INVALID":"BUSINESS_PEER_INVALID","BUSINESS_PEER_USAGE_MISSING":"BUSINESS_PEER_USAGE_MISSING","BUTTON_COPY_TEXT_INVALID":"BUTTON_COPY_TEXT_INVALID","BUTTON_DATA_INVALID":"BUTTON_DATA_INVALID","BUTTON_ID_INVALID":"BUTTON_ID_INVALID","BUTTON_TYPE_INVALID":"BUTTON_TYPE_INVALID","BUTTON_URL_INVALID":"BUTTON_URL_INVALID","BUTTON_USER_INVALID":"BUTTON_USER_INVALID","BUTTON_USER_PRIVACY_RESTRICTED":"BUTTON_USER_PRIVACY_RESTRICTED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_MONOFORUM_UNSUPPORTED":"CHANNEL_MONOFORUM_UNSUPPORTED","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_FORWARDS_RESTRICTED":"CHAT_FORWARDS_RESTRICTED","CHAT_ID_INVALID":"CHAT_ID_INVALID","CHAT_RESTRICTED":"CHAT_RESTRICTED","DOCUMENT_INVALID":"DOCUMENT_INVALID","EFFECT_CHAT_INVALID":"EFFECT_CHAT_INVALID","ENCRYPTION_DECLINED":"ENCRYPTION_DECLINED","ENTITIES_TOO_LONG":"ENTITIES_TOO_LONG","ENTITY_BOUNDS_INVALID":"ENTITY_BOUNDS_INVALID","ENTITY_DATE_FORMAT_INVALID":"ENTITY_DATE_FORMAT_INVALID","ENTITY_DATE_INVALID":"ENTITY_DATE_INVALID","ENTITY_DATE_TOO_LONG":"ENTITY_DATE_TOO_LONG","ENTITY_MENTION_USER_INVALID":"ENTITY_MENTION_USER_INVALID","FROM_MESSAGE_BOT_DISABLED":"FROM_MESSAGE_BOT_DISABLED","INPUT_USER_DEACTIVATED":"INPUT_USER_DEACTIVATED","MESSAGE_EMPTY":"MESSAGE_EMPTY","MESSAGE_TOO_LONG":"MESSAGE_TOO_LONG","MSG_ID_INVALID":"MSG_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID","PEER_TYPES_INVALID":"PEER_TYPES_INVALID","PINNED_DIALOGS_TOO_MUCH":"PINNED_DIALOGS_TOO_MUCH","POLL_OPTION_INVALID":"POLL_OPTION_INVALID","QUICK_REPLIES_BOT_NOT_ALLOWED":"QUICK_REPLIES_BOT_NOT_ALLOWED","QUICK_REPLIES_TOO_MUCH":"QUICK_REPLIES_TOO_MUCH","QUOTE_TEXT_INVALID":"QUOTE_TEXT_INVALID","RANDOM_ID_EMPTY":"RANDOM_ID_EMPTY","REPLY_MARKUP_INVALID":"REPLY_MARKUP_INVALID","REPLY_MARKUP_TOO_LONG":"REPLY_MARKUP_TOO_LONG","REPLY_MESSAGE_ID_INVALID":"REPLY_MESSAGE_ID_INVALID","REPLY_MESSAGES_TOO_MUCH":"REPLY_MESSAGES_TOO_MUCH","REPLY_TO_INVALID":"REPLY_TO_INVALID","REPLY_TO_MONOFORUM_PEER_INVALID":"REPLY_TO_MONOFORUM_PEER_INVALID","REPLY_TO_USER_INVALID":"REPLY_TO_USER_INVALID","SCHEDULE_BOT_NOT_ALLOWED":"SCHEDULE_BOT_NOT_ALLOWED","SCHEDULE_DATE_TOO_LATE":"SCHEDULE_DATE_TOO_LATE","SCHEDULE_STATUS_PRIVATE":"SCHEDULE_STATUS_PRIVATE","SCHEDULE_TOO_MUCH":"SCHEDULE_TOO_MUCH","SEND_AS_PEER_INVALID":"SEND_AS_PEER_INVALID","STORIES_NEVER_CREATED":"STORIES_NEVER_CREATED","STORY_ID_INVALID":"STORY_ID_INVALID","SUGGESTED_POST_AMOUNT_INVALID":"SUGGESTED_POST_AMOUNT_INVALID","SUGGESTED_POST_PEER_INVALID":"SUGGESTED_POST_PEER_INVALID","TOPIC_CLOSED":"TOPIC_CLOSED","TOPIC_DELETED":"TOPIC_DELETED","USER_BANNED_IN_CHANNEL":"USER_BANNED_IN_CHANNEL","USER_BOT_TO_BOT_DISABLED":"USER_BOT_TO_BOT_DISABLED","USER_IS_BLOCKED":"USER_IS_BLOCKED","USER_IS_BOT":"USER_IS_BOT","WC_CONVERT_URL_INVALID":"WC_CONVERT_URL_INVALID","YOU_BLOCKED_USER":"YOU_BLOCKED_USER"},"messages.composeMessageWithAI":{"AI_COMPOSE_TASK_MISSING":"AI_COMPOSE_TASK_MISSING","AICOMPOSE_FLOOD_PREMIUM":"AICOMPOSE_FLOOD_PREMIUM","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","MESSAGE_EMPTY":"MESSAGE_EMPTY"},"aicompose.deleteTone":{"AICOMPOSE_TONE_INVALID":"AICOMPOSE_TONE_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"aicompose.getTone":{"AICOMPOSE_TONE_INVALID":"AICOMPOSE_TONE_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"aicompose.getToneExample":{"AICOMPOSE_TONE_INVALID":"AICOMPOSE_TONE_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"aicompose.saveTone":{"AICOMPOSE_TONE_INVALID":"AICOMPOSE_TONE_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"aicompose.updateTone":{"AICOMPOSE_TONE_INVALID":"AICOMPOSE_TONE_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"aicompose.createTone":{"AICOMPOSE_TONE_TITLE_INVALID":"AICOMPOSE_TONE_TITLE_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"photos.updateProfilePhoto":{"ALBUM_PHOTOS_TOO_MANY":"ALBUM_PHOTOS_TOO_MANY","BOT_FALLBACK_UNSUPPORTED":"BOT_FALLBACK_UNSUPPORTED","FILE_PARTS_INVALID":"FILE_PARTS_INVALID","IMAGE_PROCESS_FAILED":"IMAGE_PROCESS_FAILED","LOCATION_INVALID":"LOCATION_INVALID","PHOTO_CROP_SIZE_SMALL":"PHOTO_CROP_SIZE_SMALL","PHOTO_EXT_INVALID":"PHOTO_EXT_INVALID","PHOTO_ID_INVALID":"PHOTO_ID_INVALID"},"photos.uploadProfilePhoto":{"ALBUM_PHOTOS_TOO_MANY":"ALBUM_PHOTOS_TOO_MANY","BOT_INVALID":"BOT_INVALID","EMOJI_MARKUP_INVALID":"EMOJI_MARKUP_INVALID","FILE_PARTS_INVALID":"FILE_PARTS_INVALID","IMAGE_PROCESS_FAILED":"IMAGE_PROCESS_FAILED","PHOTO_CROP_FILE_MISSING":"PHOTO_CROP_FILE_MISSING","PHOTO_CROP_SIZE_SMALL":"PHOTO_CROP_SIZE_SMALL","PHOTO_EXT_INVALID":"PHOTO_EXT_INVALID","PHOTO_FILE_MISSING":"PHOTO_FILE_MISSING","PHOTO_INVALID":"PHOTO_INVALID","STICKER_MIME_INVALID":"STICKER_MIME_INVALID","VIDEO_FILE_INVALID":"VIDEO_FILE_INVALID"},"auth.exportLoginToken":{"API_ID_INVALID":"API_ID_INVALID","API_ID_PUBLISHED_FLOOD":"API_ID_PUBLISHED_FLOOD","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"auth.importWebTokenAuthorization":{"API_ID_INVALID":"API_ID_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","WEBAUTH_TOKEN_EXPIRED":"WEBAUTH_TOKEN_EXPIRED"},"auth.initPasskeyLogin":{"API_ID_INVALID":"API_ID_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"auth.sendCode":{"API_ID_INVALID":"API_ID_INVALID","API_ID_PUBLISHED_FLOOD":"API_ID_PUBLISHED_FLOOD","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PHONE_NUMBER_APP_SIGNUP_FORBIDDEN":"PHONE_NUMBER_APP_SIGNUP_FORBIDDEN","PHONE_NUMBER_BANNED":"PHONE_NUMBER_BANNED","PHONE_NUMBER_FLOOD":"PHONE_NUMBER_FLOOD","PHONE_NUMBER_INVALID":"PHONE_NUMBER_INVALID","PHONE_PASSWORD_PROTECTED":"PHONE_PASSWORD_PROTECTED","SMS_CODE_CREATE_FAILED":"SMS_CODE_CREATE_FAILED"},"messages.setInlineBotResults":{"ARTICLE_TITLE_EMPTY":"ARTICLE_TITLE_EMPTY","AUDIO_CONTENT_URL_EMPTY":"AUDIO_CONTENT_URL_EMPTY","AUDIO_TITLE_EMPTY":"AUDIO_TITLE_EMPTY","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","BUTTON_DATA_INVALID":"BUTTON_DATA_INVALID","BUTTON_TYPE_INVALID":"BUTTON_TYPE_INVALID","BUTTON_URL_INVALID":"BUTTON_URL_INVALID","DOCUMENT_INVALID":"DOCUMENT_INVALID","FILE_CONTENT_TYPE_INVALID":"FILE_CONTENT_TYPE_INVALID","FILE_TITLE_EMPTY":"FILE_TITLE_EMPTY","GIF_CONTENT_TYPE_INVALID":"GIF_CONTENT_TYPE_INVALID","MEDIA_CAPTION_TOO_LONG":"MEDIA_CAPTION_TOO_LONG","MESSAGE_EMPTY":"MESSAGE_EMPTY","MESSAGE_TOO_LONG":"MESSAGE_TOO_LONG","NEXT_OFFSET_INVALID":"NEXT_OFFSET_INVALID","PEER_TYPES_INVALID":"PEER_TYPES_INVALID","PHOTO_CONTENT_TYPE_INVALID":"PHOTO_CONTENT_TYPE_INVALID","PHOTO_CONTENT_URL_EMPTY":"PHOTO_CONTENT_URL_EMPTY","PHOTO_INVALID":"PHOTO_INVALID","PHOTO_THUMB_URL_EMPTY":"PHOTO_THUMB_URL_EMPTY","QUERY_ID_INVALID":"QUERY_ID_INVALID","REPLY_MARKUP_INVALID":"REPLY_MARKUP_INVALID","RESULT_ID_DUPLICATE":"RESULT_ID_DUPLICATE","RESULT_ID_INVALID":"RESULT_ID_INVALID","RESULT_TYPE_INVALID":"RESULT_TYPE_INVALID","RESULTS_TOO_MUCH":"RESULTS_TOO_MUCH","SEND_MESSAGE_MEDIA_INVALID":"SEND_MESSAGE_MEDIA_INVALID","SEND_MESSAGE_TYPE_INVALID":"SEND_MESSAGE_TYPE_INVALID","START_PARAM_EMPTY":"START_PARAM_EMPTY","START_PARAM_INVALID":"START_PARAM_INVALID","STICKER_DOCUMENT_INVALID":"STICKER_DOCUMENT_INVALID","SWITCH_PM_TEXT_EMPTY":"SWITCH_PM_TEXT_EMPTY","SWITCH_WEBVIEW_URL_INVALID":"SWITCH_WEBVIEW_URL_INVALID","URL_INVALID":"URL_INVALID","USER_BOT_REQUIRED":"USER_BOT_REQUIRED","VIDEO_CONTENT_TYPE_INVALID":"VIDEO_CONTENT_TYPE_INVALID","VIDEO_TITLE_EMPTY":"VIDEO_TITLE_EMPTY","WEBDOCUMENT_INVALID":"WEBDOCUMENT_INVALID","WEBDOCUMENT_MIME_INVALID":"WEBDOCUMENT_MIME_INVALID","WEBDOCUMENT_SIZE_TOO_BIG":"WEBDOCUMENT_SIZE_TOO_BIG","WEBDOCUMENT_URL_EMPTY":"WEBDOCUMENT_URL_EMPTY","WEBDOCUMENT_URL_INVALID":"WEBDOCUMENT_URL_INVALID"},"auth.importAuthorization":{"AUTH_BYTES_INVALID":"AUTH_BYTES_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","USER_ID_INVALID":"USER_ID_INVALID"},"invokeWithLayer":{"AUTH_BYTES_INVALID":"AUTH_BYTES_INVALID","CDN_METHOD_INVALID":"CDN_METHOD_INVALID","CONNECTION_API_ID_INVALID":"CONNECTION_API_ID_INVALID","CONNECTION_LAYER_INVALID":"CONNECTION_LAYER_INVALID","INVITE_HASH_EXPIRED":"INVITE_HASH_EXPIRED"},"auth.acceptLoginToken":{"AUTH_TOKEN_ALREADY_ACCEPTED":"AUTH_TOKEN_ALREADY_ACCEPTED","AUTH_TOKEN_EXCEPTION":"AUTH_TOKEN_EXCEPTION","AUTH_TOKEN_EXPIRED":"AUTH_TOKEN_EXPIRED","AUTH_TOKEN_INVALIDX":"AUTH_TOKEN_INVALIDX","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"auth.importLoginToken":{"AUTH_TOKEN_ALREADY_ACCEPTED":"AUTH_TOKEN_ALREADY_ACCEPTED","AUTH_TOKEN_EXPIRED":"AUTH_TOKEN_EXPIRED","AUTH_TOKEN_INVALID":"AUTH_TOKEN_INVALID","AUTH_TOKEN_INVALIDX":"AUTH_TOKEN_INVALIDX","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.setGlobalPrivacySettings":{"AUTOARCHIVE_NOT_AVAILABLE":"AUTOARCHIVE_NOT_AVAILABLE","BUSINESS_CONNECTION_INVALID":"BUSINESS_CONNECTION_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.sendPaidReaction":{"BALANCE_TOO_LOW":"BALANCE_TOO_LOW","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","MESSAGE_ID_INVALID":"MESSAGE_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID","RANDOM_ID_EMPTY":"RANDOM_ID_EMPTY","RANDOM_ID_EXPIRED":"RANDOM_ID_EXPIRED","REACTIONS_COUNT_INVALID":"REACTIONS_COUNT_INVALID","SEND_AS_PEER_INVALID":"SEND_AS_PEER_INVALID"},"payments.sendStarsForm":{"BALANCE_TOO_LOW":"BALANCE_TOO_LOW","BOT_INVOICE_INVALID":"BOT_INVOICE_INVALID","BUSINESS_CONNECTION_INVALID":"BUSINESS_CONNECTION_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","FORM_EXPIRED":"FORM_EXPIRED","FORM_ID_EMPTY":"FORM_ID_EMPTY","FORM_SUBMIT_DUPLICATE":"FORM_SUBMIT_DUPLICATE","FORM_UNSUPPORTED":"FORM_UNSUPPORTED","GIFT_STARS_INVALID":"GIFT_STARS_INVALID","MEDIA_ALREADY_PAID":"MEDIA_ALREADY_PAID","MONTH_INVALID":"MONTH_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID","PURPOSE_INVALID":"PURPOSE_INVALID","STARGIFT_ALREADY_UPGRADED":"STARGIFT_ALREADY_UPGRADED","STARGIFT_NOT_FOUND":"STARGIFT_NOT_FOUND","STARGIFT_OWNER_INVALID":"STARGIFT_OWNER_INVALID","STARGIFT_SLUG_INVALID":"STARGIFT_SLUG_INVALID","STARGIFT_USAGE_LIMITED":"STARGIFT_USAGE_LIMITED","STARGIFT_USER_USAGE_LIMITED":"STARGIFT_USER_USAGE_LIMITED","TO_ID_INVALID":"TO_ID_INVALID","USER_ID_INVALID":"USER_ID_INVALID"},"payments.getBankCardData":{"BANK_CARD_NUMBER_INVALID":"BANK_CARD_NUMBER_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.editChatDefaultBannedRights":{"BANNED_RIGHTS_INVALID":"BANNED_RIGHTS_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_ID_INVALID":"CHAT_ID_INVALID","CHAT_NOT_MODIFIED":"CHAT_NOT_MODIFIED","PEER_ID_INVALID":"PEER_ID_INVALID","UNTIL_DATE_INVALID":"UNTIL_DATE_INVALID"},"users.suggestBirthday":{"BIRTHDAY_ALREADY":"BIRTHDAY_ALREADY","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","USER_ID_INVALID":"USER_ID_INVALID"},"account.updateBirthday":{"BIRTHDAY_INVALID":"BIRTHDAY_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"stories.applyBoost":{"BOOST_NOT_MODIFIED":"BOOST_NOT_MODIFIED","PEER_ID_INVALID":"PEER_ID_INVALID","PREMIUM_ACCOUNT_REQUIRED":"PREMIUM_ACCOUNT_REQUIRED"},"stories.canApplyBoost":{"BOOST_NOT_MODIFIED":"BOOST_NOT_MODIFIED","PEER_ID_INVALID":"PEER_ID_INVALID","PREMIUM_ACCOUNT_REQUIRED":"PREMIUM_ACCOUNT_REQUIRED"},"payments.getPaymentForm":{"BOOST_PEER_INVALID":"BOOST_PEER_INVALID","BOT_INVOICE_INVALID":"BOT_INVOICE_INVALID","BUSINESS_CONNECTION_INVALID":"BUSINESS_CONNECTION_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","GIFT_MONTHS_INVALID":"GIFT_MONTHS_INVALID","GIFT_STARS_INVALID":"GIFT_STARS_INVALID","INVOICE_INVALID":"INVOICE_INVALID","MESSAGE_ID_INVALID":"MESSAGE_ID_INVALID","MESSAGE_TOO_LONG":"MESSAGE_TOO_LONG","MONTH_INVALID":"MONTH_INVALID","MSG_ID_INVALID":"MSG_ID_INVALID","NO_PAYMENT_NEEDED":"NO_PAYMENT_NEEDED","PEER_ID_INVALID":"PEER_ID_INVALID","PREMIUM_PURPOSE_INVALID":"PREMIUM_PURPOSE_INVALID","SLUG_INVALID":"SLUG_INVALID","STARGIFT_ALREADY_CONVERTED":"STARGIFT_ALREADY_CONVERTED","STARGIFT_ALREADY_REFUNDED":"STARGIFT_ALREADY_REFUNDED","STARGIFT_ALREADY_UPGRADED":"STARGIFT_ALREADY_UPGRADED","STARGIFT_INVALID":"STARGIFT_INVALID","STARGIFT_MESSAGE_INVALID":"STARGIFT_MESSAGE_INVALID","STARGIFT_NOT_FOUND":"STARGIFT_NOT_FOUND","STARGIFT_NOT_OWNER":"STARGIFT_NOT_OWNER","STARGIFT_NOT_UNIQUE":"STARGIFT_NOT_UNIQUE","STARGIFT_OWNER_INVALID":"STARGIFT_OWNER_INVALID","STARGIFT_PEER_INVALID":"STARGIFT_PEER_INVALID","STARGIFT_RESELL_CURRENCY_NOT_ALLOWED":"STARGIFT_RESELL_CURRENCY_NOT_ALLOWED","STARGIFT_RESELL_TOO_EARLY_%d":"STARGIFT_RESELL_TOO_EARLY_%d","STARGIFT_SLUG_INVALID":"STARGIFT_SLUG_INVALID","STARGIFT_TRANSFER_TOO_EARLY_%d":"STARGIFT_TRANSFER_TOO_EARLY_%d","STARGIFT_UPGRADE_UNAVAILABLE":"STARGIFT_UPGRADE_UNAVAILABLE","TO_ID_INVALID":"TO_ID_INVALID","UNTIL_DATE_INVALID":"UNTIL_DATE_INVALID"},"premium.applyBoost":{"BOOSTS_EMPTY":"BOOSTS_EMPTY","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID","SLOTS_EMPTY":"SLOTS_EMPTY"},"channels.updateColor":{"BOOSTS_REQUIRED":"BOOSTS_REQUIRED","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID"},"stories.canSendStory":{"BOOSTS_REQUIRED":"BOOSTS_REQUIRED","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","PEER_ID_INVALID":"PEER_ID_INVALID","PREMIUM_ACCOUNT_REQUIRED":"PREMIUM_ACCOUNT_REQUIRED","STORIES_TOO_MUCH":"STORIES_TOO_MUCH","STORY_SEND_FLOOD_MONTHLY_%d":"STORY_SEND_FLOOD_MONTHLY_%d","STORY_SEND_FLOOD_WEEKLY_%d":"STORY_SEND_FLOOD_WEEKLY_%d"},"stories.sendStory":{"BOOSTS_REQUIRED":"BOOSTS_REQUIRED","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","IMAGE_PROCESS_FAILED":"IMAGE_PROCESS_FAILED","MEDIA_CAPTION_TOO_LONG":"MEDIA_CAPTION_TOO_LONG","MEDIA_EMPTY":"MEDIA_EMPTY","MEDIA_FILE_INVALID":"MEDIA_FILE_INVALID","MEDIA_TYPE_INVALID":"MEDIA_TYPE_INVALID","MEDIA_VIDEO_STORY_MISSING":"MEDIA_VIDEO_STORY_MISSING","PEER_ID_INVALID":"PEER_ID_INVALID","PHOTO_INVALID_DIMENSIONS":"PHOTO_INVALID_DIMENSIONS","PREMIUM_ACCOUNT_REQUIRED":"PREMIUM_ACCOUNT_REQUIRED","REACTION_INVALID":"REACTION_INVALID","STORIES_TOO_MUCH":"STORIES_TOO_MUCH","STORY_PERIOD_INVALID":"STORY_PERIOD_INVALID","VENUE_ID_INVALID":"VENUE_ID_INVALID","VIDEO_DURATION_INVALID":"VIDEO_DURATION_INVALID"},"account.disablePeerConnectedBot":{"BOT_ALREADY_DISABLED":"BOT_ALREADY_DISABLED","BOT_NOT_CONNECTED_YET":"BOT_NOT_CONNECTED_YET","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.getBotApp":{"BOT_APP_BOT_INVALID":"BOT_APP_BOT_INVALID","BOT_APP_INVALID":"BOT_APP_INVALID","BOT_APP_SHORTNAME_INVALID":"BOT_APP_SHORTNAME_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.requestAppWebView":{"BOT_APP_BOT_INVALID":"BOT_APP_BOT_INVALID","BOT_APP_INVALID":"BOT_APP_INVALID","BOT_APP_SHORTNAME_INVALID":"BOT_APP_SHORTNAME_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","MSG_ID_INVALID":"MSG_ID_INVALID","THEME_PARAMS_INVALID":"THEME_PARAMS_INVALID"},"account.updateConnectedBot":{"BOT_BUSINESS_MISSING":"BOT_BUSINESS_MISSING","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","BUSINESS_RECIPIENTS_EMPTY":"BUSINESS_RECIPIENTS_EMPTY"},"bots.setBotCommands":{"BOT_COMMAND_DESCRIPTION_INVALID":"BOT_COMMAND_DESCRIPTION_INVALID","BOT_COMMAND_INVALID":"BOT_COMMAND_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","LANG_CODE_INVALID":"LANG_CODE_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID","USER_BOT_REQUIRED":"USER_BOT_REQUIRED","USER_ID_INVALID":"USER_ID_INVALID"},"bots.createBot":{"BOT_CREATE_LIMIT_EXCEEDED":"BOT_CREATE_LIMIT_EXCEEDED","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","MANAGER_INVALID":"MANAGER_INVALID","MANAGER_PERMISSION_MISSING":"MANAGER_PERMISSION_MISSING","NAME_INVALID":"NAME_INVALID","USERNAME_INVALID":"USERNAME_INVALID","USERNAME_OCCUPIED":"USERNAME_OCCUPIED","USERNAME_SUFFIX_MISSING":"USERNAME_SUFFIX_MISSING"},"messages.editMessage":{"BOT_DOMAIN_INVALID":"BOT_DOMAIN_INVALID","BOT_INVALID":"BOT_INVALID","BUSINESS_CONNECTION_INVALID":"BUSINESS_CONNECTION_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","BUSINESS_PEER_INVALID":"BUSINESS_PEER_INVALID","BUTTON_COPY_TEXT_INVALID":"BUTTON_COPY_TEXT_INVALID","BUTTON_DATA_INVALID":"BUTTON_DATA_INVALID","BUTTON_TYPE_INVALID":"BUTTON_TYPE_INVALID","BUTTON_URL_INVALID":"BUTTON_URL_INVALID","BUTTON_USER_PRIVACY_RESTRICTED":"BUTTON_USER_PRIVACY_RESTRICTED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_FORWARDS_RESTRICTED":"CHAT_FORWARDS_RESTRICTED","DOCUMENT_INVALID":"DOCUMENT_INVALID","ENTITIES_TOO_LONG":"ENTITIES_TOO_LONG","ENTITY_BOUNDS_INVALID":"ENTITY_BOUNDS_INVALID","FILE_PARTS_INVALID":"FILE_PARTS_INVALID","IMAGE_PROCESS_FAILED":"IMAGE_PROCESS_FAILED","INPUT_USER_DEACTIVATED":"INPUT_USER_DEACTIVATED","MEDIA_CAPTION_TOO_LONG":"MEDIA_CAPTION_TOO_LONG","MEDIA_EMPTY":"MEDIA_EMPTY","MEDIA_GROUPED_INVALID":"MEDIA_GROUPED_INVALID","MEDIA_INVALID":"MEDIA_INVALID","MEDIA_NEW_INVALID":"MEDIA_NEW_INVALID","MEDIA_PREV_INVALID":"MEDIA_PREV_INVALID","MEDIA_TTL_INVALID":"MEDIA_TTL_INVALID","MESSAGE_EDIT_TIME_EXPIRED":"MESSAGE_EDIT_TIME_EXPIRED","MESSAGE_EMPTY":"MESSAGE_EMPTY","MESSAGE_ID_INVALID":"MESSAGE_ID_INVALID","MESSAGE_NOT_MODIFIED":"MESSAGE_NOT_MODIFIED","MESSAGE_TOO_LONG":"MESSAGE_TOO_LONG","MSG_ID_INVALID":"MSG_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID","PEER_TYPES_INVALID":"PEER_TYPES_INVALID","PHOTO_EXT_INVALID":"PHOTO_EXT_INVALID","PHOTO_INVALID_DIMENSIONS":"PHOTO_INVALID_DIMENSIONS","PHOTO_SAVE_FILE_INVALID":"PHOTO_SAVE_FILE_INVALID","REPLY_MARKUP_INVALID":"REPLY_MARKUP_INVALID","REPLY_MARKUP_TOO_LONG":"REPLY_MARKUP_TOO_LONG","SCHEDULE_DATE_INVALID":"SCHEDULE_DATE_INVALID","TODO_ITEM_DUPLICATE":"TODO_ITEM_DUPLICATE","TODO_ITEMS_EMPTY":"TODO_ITEMS_EMPTY","USER_BANNED_IN_CHANNEL":"USER_BANNED_IN_CHANNEL","WEBPAGE_NOT_FOUND":"WEBPAGE_NOT_FOUND"},"messages.sendMedia":{"BOT_GAMES_DISABLED":"BOT_GAMES_DISABLED","BOT_PAYMENTS_DISABLED":"BOT_PAYMENTS_DISABLED","BROADCAST_PUBLIC_VOTERS_FORBIDDEN":"BROADCAST_PUBLIC_VOTERS_FORBIDDEN","BUSINESS_CONNECTION_INVALID":"BUSINESS_CONNECTION_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","BUSINESS_PEER_INVALID":"BUSINESS_PEER_INVALID","BUSINESS_PEER_USAGE_MISSING":"BUSINESS_PEER_USAGE_MISSING","BUTTON_COPY_TEXT_INVALID":"BUTTON_COPY_TEXT_INVALID","BUTTON_DATA_INVALID":"BUTTON_DATA_INVALID","BUTTON_POS_INVALID":"BUTTON_POS_INVALID","BUTTON_TYPE_INVALID":"BUTTON_TYPE_INVALID","BUTTON_URL_INVALID":"BUTTON_URL_INVALID","BUTTON_USER_PRIVACY_RESTRICTED":"BUTTON_USER_PRIVACY_RESTRICTED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_FORWARDS_RESTRICTED":"CHAT_FORWARDS_RESTRICTED","CHAT_RESTRICTED":"CHAT_RESTRICTED","CURRENCY_TOTAL_AMOUNT_INVALID":"CURRENCY_TOTAL_AMOUNT_INVALID","DOCUMENT_INVALID":"DOCUMENT_INVALID","EFFECT_CHAT_INVALID":"EFFECT_CHAT_INVALID","EFFECT_ID_INVALID":"EFFECT_ID_INVALID","EMOTICON_INVALID":"EMOTICON_INVALID","ENTITY_BOUNDS_INVALID":"ENTITY_BOUNDS_INVALID","EXTENDED_MEDIA_AMOUNT_INVALID":"EXTENDED_MEDIA_AMOUNT_INVALID","EXTENDED_MEDIA_EMPTY":"EXTENDED_MEDIA_EMPTY","EXTENDED_MEDIA_INVALID":"EXTENDED_MEDIA_INVALID","EXTENDED_MEDIA_PEER_INVALID":"EXTENDED_MEDIA_PEER_INVALID","EXTERNAL_URL_INVALID":"EXTERNAL_URL_INVALID","FILE_PART_LENGTH_INVALID":"FILE_PART_LENGTH_INVALID","FILE_PARTS_INVALID":"FILE_PARTS_INVALID","FILE_REFERENCE_EMPTY":"FILE_REFERENCE_EMPTY","FILE_REFERENCE_EXPIRED":"FILE_REFERENCE_EXPIRED","GAME_BOT_INVALID":"GAME_BOT_INVALID","IMAGE_PROCESS_FAILED":"IMAGE_PROCESS_FAILED","INPUT_FILE_INVALID":"INPUT_FILE_INVALID","INPUT_USER_DEACTIVATED":"INPUT_USER_DEACTIVATED","INVOICE_PAYLOAD_INVALID":"INVOICE_PAYLOAD_INVALID","MD5_CHECKSUM_INVALID":"MD5_CHECKSUM_INVALID","MEDIA_CAPTION_TOO_LONG":"MEDIA_CAPTION_TOO_LONG","MEDIA_EMPTY":"MEDIA_EMPTY","MEDIA_FILE_INVALID":"MEDIA_FILE_INVALID","MEDIA_INVALID":"MEDIA_INVALID","MESSAGE_EMPTY":"MESSAGE_EMPTY","MESSAGE_TOO_LONG":"MESSAGE_TOO_LONG","MSG_ID_INVALID":"MSG_ID_INVALID","PAYMENT_PROVIDER_INVALID":"PAYMENT_PROVIDER_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID","PHOTO_EXT_INVALID":"PHOTO_EXT_INVALID","PHOTO_INVALID_DIMENSIONS":"PHOTO_INVALID_DIMENSIONS","PHOTO_SAVE_FILE_INVALID":"PHOTO_SAVE_FILE_INVALID","POLL_ANSWER_INVALID":"POLL_ANSWER_INVALID","POLL_ANSWERS_INVALID":"POLL_ANSWERS_INVALID","POLL_OPTION_DUPLICATE":"POLL_OPTION_DUPLICATE","POLL_OPTION_INVALID":"POLL_OPTION_INVALID","POLL_QUESTION_INVALID":"POLL_QUESTION_INVALID","QUICK_REPLIES_BOT_NOT_ALLOWED":"QUICK_REPLIES_BOT_NOT_ALLOWED","QUICK_REPLIES_TOO_MUCH":"QUICK_REPLIES_TOO_MUCH","QUIZ_CORRECT_ANSWER_INVALID":"QUIZ_CORRECT_ANSWER_INVALID","QUIZ_CORRECT_ANSWERS_EMPTY":"QUIZ_CORRECT_ANSWERS_EMPTY","QUIZ_CORRECT_ANSWERS_TOO_MUCH":"QUIZ_CORRECT_ANSWERS_TOO_MUCH","QUIZ_MULTIPLE_INVALID":"QUIZ_MULTIPLE_INVALID","REPLY_MARKUP_BUY_EMPTY":"REPLY_MARKUP_BUY_EMPTY","REPLY_MARKUP_GAME_EMPTY":"REPLY_MARKUP_GAME_EMPTY","REPLY_MARKUP_INVALID":"REPLY_MARKUP_INVALID","REPLY_MARKUP_TOO_LONG":"REPLY_MARKUP_TOO_LONG","REPLY_MESSAGE_ID_INVALID":"REPLY_MESSAGE_ID_INVALID","REPLY_MESSAGES_TOO_MUCH":"REPLY_MESSAGES_TOO_MUCH","REPLY_TO_MONOFORUM_PEER_INVALID":"REPLY_TO_MONOFORUM_PEER_INVALID","SCHEDULE_BOT_NOT_ALLOWED":"SCHEDULE_BOT_NOT_ALLOWED","SCHEDULE_DATE_TOO_LATE":"SCHEDULE_DATE_TOO_LATE","SCHEDULE_TOO_MUCH":"SCHEDULE_TOO_MUCH","SEND_AS_PEER_INVALID":"SEND_AS_PEER_INVALID","STARS_INVOICE_INVALID":"STARS_INVOICE_INVALID","STORY_ID_INVALID":"STORY_ID_INVALID","SUBSCRIPTION_EXPORT_MISSING":"SUBSCRIPTION_EXPORT_MISSING","SUGGESTED_POST_PEER_INVALID":"SUGGESTED_POST_PEER_INVALID","TERMS_URL_INVALID":"TERMS_URL_INVALID","TODO_ITEM_DUPLICATE":"TODO_ITEM_DUPLICATE","TODO_ITEMS_EMPTY":"TODO_ITEMS_EMPTY","TODO_ITEMS_TOO_MUCH":"TODO_ITEMS_TOO_MUCH","TOPIC_CLOSED":"TOPIC_CLOSED","TOPIC_DELETED":"TOPIC_DELETED","TTL_MEDIA_INVALID":"TTL_MEDIA_INVALID","USER_BANNED_IN_CHANNEL":"USER_BANNED_IN_CHANNEL","USER_BOT_TO_BOT_DISABLED":"USER_BOT_TO_BOT_DISABLED","USER_IS_BLOCKED":"USER_IS_BLOCKED","USER_IS_BOT":"USER_IS_BOT","VIDEO_CONTENT_TYPE_INVALID":"VIDEO_CONTENT_TYPE_INVALID","VOICE_MESSAGES_FORBIDDEN":"VOICE_MESSAGES_FORBIDDEN","WEBDOCUMENT_MIME_INVALID":"WEBDOCUMENT_MIME_INVALID","WEBPAGE_CURL_FAILED":"WEBPAGE_CURL_FAILED","WEBPAGE_MEDIA_EMPTY":"WEBPAGE_MEDIA_EMPTY","WEBPAGE_NOT_FOUND":"WEBPAGE_NOT_FOUND","WEBPAGE_URL_INVALID":"WEBPAGE_URL_INVALID","YOU_BLOCKED_USER":"YOU_BLOCKED_USER"},"channels.inviteToChannel":{"BOT_GROUPS_BLOCKED":"BOT_GROUPS_BLOCKED","BOTS_TOO_MUCH":"BOTS_TOO_MUCH","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_MONOFORUM_UNSUPPORTED":"CHANNEL_MONOFORUM_UNSUPPORTED","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_INVALID":"CHAT_INVALID","CHAT_MEMBER_ADD_FAILED":"CHAT_MEMBER_ADD_FAILED","INPUT_USER_DEACTIVATED":"INPUT_USER_DEACTIVATED","MSG_ID_INVALID":"MSG_ID_INVALID","USER_BANNED_IN_CHANNEL":"USER_BANNED_IN_CHANNEL","USER_BLOCKED":"USER_BLOCKED","USER_BOT":"USER_BOT","USER_CHANNELS_TOO_MUCH":"USER_CHANNELS_TOO_MUCH","USER_ID_INVALID":"USER_ID_INVALID","USER_KICKED":"USER_KICKED","USER_NOT_MUTUAL_CONTACT":"USER_NOT_MUTUAL_CONTACT","USERS_TOO_MUCH":"USERS_TOO_MUCH"},"messages.addChatUser":{"BOT_GROUPS_BLOCKED":"BOT_GROUPS_BLOCKED","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_ID_INVALID":"CHAT_ID_INVALID","CHAT_INVALID":"CHAT_INVALID","CHAT_MEMBER_ADD_FAILED":"CHAT_MEMBER_ADD_FAILED","INPUT_USER_DEACTIVATED":"INPUT_USER_DEACTIVATED","MSG_ID_INVALID":"MSG_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID","USER_ALREADY_PARTICIPANT":"USER_ALREADY_PARTICIPANT","USER_ID_INVALID":"USER_ID_INVALID","USER_IS_BLOCKED":"USER_IS_BLOCKED","USER_NOT_MUTUAL_CONTACT":"USER_NOT_MUTUAL_CONTACT","USERS_TOO_MUCH":"USERS_TOO_MUCH","YOU_BLOCKED_USER":"YOU_BLOCKED_USER"},"account.confirmBotConnection":{"BOT_ID_INVALID":"BOT_ID_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"bots.getRequestedWebViewButton":{"BOT_ID_INVALID":"BOT_ID_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","WEBAPP_REQ_ID_INVALID":"WEBAPP_REQ_ID_INVALID"},"messages.getInlineBotResults":{"BOT_INLINE_DISABLED":"BOT_INLINE_DISABLED","BOT_INVALID":"BOT_INVALID","BOT_RESPONSE_TIMEOUT":"BOT_RESPONSE_TIMEOUT","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","INPUT_USER_DEACTIVATED":"INPUT_USER_DEACTIVATED","MSG_ID_INVALID":"MSG_ID_INVALID"},"account.acceptAuthorization":{"BOT_INVALID":"BOT_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PUBLIC_KEY_REQUIRED":"PUBLIC_KEY_REQUIRED"},"account.getAuthorizationForm":{"BOT_INVALID":"BOT_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PUBLIC_KEY_REQUIRED":"PUBLIC_KEY_REQUIRED"},"bots.addPreviewMedia":{"BOT_INVALID":"BOT_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"bots.allowSendMessage":{"BOT_INVALID":"BOT_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"bots.canSendMessage":{"BOT_INVALID":"BOT_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"bots.checkDownloadFileParams":{"BOT_INVALID":"BOT_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"bots.deletePreviewMedia":{"BOT_INVALID":"BOT_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"bots.editAccessSettings":{"BOT_INVALID":"BOT_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","USER_BOT_REQUIRED":"USER_BOT_REQUIRED"},"bots.editPreviewMedia":{"BOT_INVALID":"BOT_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"bots.exportBotToken":{"BOT_INVALID":"BOT_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","USER_BOT_REQUIRED":"USER_BOT_REQUIRED"},"bots.getAccessSettings":{"BOT_INVALID":"BOT_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","USER_BOT_REQUIRED":"USER_BOT_REQUIRED"},"bots.getBotInfo":{"BOT_INVALID":"BOT_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","LANG_CODE_INVALID":"LANG_CODE_INVALID","USER_BOT_INVALID":"USER_BOT_INVALID"},"bots.getBotRecommendations":{"BOT_INVALID":"BOT_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"bots.getPreviewInfo":{"BOT_INVALID":"BOT_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"bots.getPreviewMedias":{"BOT_INVALID":"BOT_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"bots.invokeWebViewCustomMethod":{"BOT_INVALID":"BOT_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","DATA_JSON_INVALID":"DATA_JSON_INVALID","METHOD_INVALID":"METHOD_INVALID"},"bots.reorderPreviewMedias":{"BOT_INVALID":"BOT_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"bots.reorderUsernames":{"BOT_INVALID":"BOT_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","USERNAME_NOT_MODIFIED":"USERNAME_NOT_MODIFIED"},"bots.setBotInfo":{"BOT_INVALID":"BOT_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","USER_BOT_INVALID":"USER_BOT_INVALID"},"bots.setCustomVerification":{"BOT_INVALID":"BOT_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"bots.toggleUserEmojiStatusPermission":{"BOT_INVALID":"BOT_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"bots.toggleUsername":{"BOT_INVALID":"BOT_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","USERNAME_NOT_MODIFIED":"USERNAME_NOT_MODIFIED"},"bots.updateStarRefProgram":{"BOT_INVALID":"BOT_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","STARREF_AWAITING_END":"STARREF_AWAITING_END","STARREF_PERMILLE_INVALID":"STARREF_PERMILLE_INVALID","STARREF_PERMILLE_TOO_LOW":"STARREF_PERMILLE_TOO_LOW"},"messages.getAttachMenuBot":{"BOT_INVALID":"BOT_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.prolongWebView":{"BOT_INVALID":"BOT_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.requestMainWebView":{"BOT_INVALID":"BOT_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.requestSimpleWebView":{"BOT_INVALID":"BOT_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","URL_INVALID":"URL_INVALID"},"messages.requestWebView":{"BOT_INVALID":"BOT_INVALID","BOT_WEBVIEW_DISABLED":"BOT_WEBVIEW_DISABLED","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","INPUT_USER_DEACTIVATED":"INPUT_USER_DEACTIVATED","MSG_ID_INVALID":"MSG_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID","SEND_AS_PEER_INVALID":"SEND_AS_PEER_INVALID","THEME_PARAMS_INVALID":"THEME_PARAMS_INVALID","URL_INVALID":"URL_INVALID","YOU_BLOCKED_USER":"YOU_BLOCKED_USER"},"messages.sendWebViewData":{"BOT_INVALID":"BOT_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.startBot":{"BOT_INVALID":"BOT_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","INPUT_USER_DEACTIVATED":"INPUT_USER_DEACTIVATED","MSG_ID_INVALID":"MSG_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID","START_PARAM_EMPTY":"START_PARAM_EMPTY","START_PARAM_INVALID":"START_PARAM_INVALID","START_PARAM_TOO_LONG":"START_PARAM_TOO_LONG"},"messages.toggleBotInAttachMenu":{"BOT_INVALID":"BOT_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.updatePinnedMessage":{"BOT_ONESIDE_NOT_AVAIL":"BOT_ONESIDE_NOT_AVAIL","BUSINESS_CONNECTION_INVALID":"BUSINESS_CONNECTION_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","BUSINESS_PEER_INVALID":"BUSINESS_PEER_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_INVALID":"CHAT_INVALID","CHAT_NOT_MODIFIED":"CHAT_NOT_MODIFIED","INPUT_USER_DEACTIVATED":"INPUT_USER_DEACTIVATED","MESSAGE_ID_INVALID":"MESSAGE_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID","PIN_RESTRICTED":"PIN_RESTRICTED","USER_BANNED_IN_CHANNEL":"USER_BANNED_IN_CHANNEL"},"messages.getBotCallbackAnswer":{"BOT_RESPONSE_TIMEOUT":"BOT_RESPONSE_TIMEOUT","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","DATA_INVALID":"DATA_INVALID","MESSAGE_ID_INVALID":"MESSAGE_ID_INVALID","MSG_ID_INVALID":"MSG_ID_INVALID","PASSWORD_MISSING":"PASSWORD_MISSING","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.setGameScore":{"BOT_SCORE_NOT_MODIFIED":"BOT_SCORE_NOT_MODIFIED","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","MESSAGE_ID_INVALID":"MESSAGE_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID","SCORE_INVALID":"SCORE_INVALID","USER_BOT_REQUIRED":"USER_BOT_REQUIRED"},"channels.setDiscussionGroup":{"BROADCAST_ID_INVALID":"BROADCAST_ID_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","LINK_NOT_MODIFIED":"LINK_NOT_MODIFIED","MEGAGROUP_ID_INVALID":"MEGAGROUP_ID_INVALID","MEGAGROUP_PREHISTORY_HIDDEN":"MEGAGROUP_PREHISTORY_HIDDEN"},"messages.forwardMessages":{"BROADCAST_PUBLIC_VOTERS_FORBIDDEN":"BROADCAST_PUBLIC_VOTERS_FORBIDDEN","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_FORWARDS_RESTRICTED":"CHAT_FORWARDS_RESTRICTED","CHAT_ID_INVALID":"CHAT_ID_INVALID","CHAT_RESTRICTED":"CHAT_RESTRICTED","GROUPED_MEDIA_INVALID":"GROUPED_MEDIA_INVALID","INPUT_USER_DEACTIVATED":"INPUT_USER_DEACTIVATED","MEDIA_EMPTY":"MEDIA_EMPTY","MEDIA_FILE_INVALID":"MEDIA_FILE_INVALID","MESSAGE_ID_INVALID":"MESSAGE_ID_INVALID","MESSAGE_IDS_EMPTY":"MESSAGE_IDS_EMPTY","MSG_ID_INVALID":"MSG_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID","QUICK_REPLIES_BOT_NOT_ALLOWED":"QUICK_REPLIES_BOT_NOT_ALLOWED","QUICK_REPLIES_TOO_MUCH":"QUICK_REPLIES_TOO_MUCH","QUIZ_ANSWER_MISSING":"QUIZ_ANSWER_MISSING","RANDOM_ID_INVALID":"RANDOM_ID_INVALID","REPLY_MESSAGES_TOO_MUCH":"REPLY_MESSAGES_TOO_MUCH","REPLY_TO_MONOFORUM_PEER_INVALID":"REPLY_TO_MONOFORUM_PEER_INVALID","SCHEDULE_BOT_NOT_ALLOWED":"SCHEDULE_BOT_NOT_ALLOWED","SCHEDULE_DATE_TOO_LATE":"SCHEDULE_DATE_TOO_LATE","SCHEDULE_TOO_MUCH":"SCHEDULE_TOO_MUCH","SEND_AS_PEER_INVALID":"SEND_AS_PEER_INVALID","SLOWMODE_MULTI_MSGS_DISABLED":"SLOWMODE_MULTI_MSGS_DISABLED","SUGGESTED_POST_PEER_INVALID":"SUGGESTED_POST_PEER_INVALID","TOPIC_CLOSED":"TOPIC_CLOSED","TOPIC_DELETED":"TOPIC_DELETED","USER_BANNED_IN_CHANNEL":"USER_BANNED_IN_CHANNEL","USER_BOT_TO_BOT_DISABLED":"USER_BOT_TO_BOT_DISABLED","USER_IS_BLOCKED":"USER_IS_BLOCKED","USER_IS_BOT":"USER_IS_BOT","YOU_BLOCKED_USER":"YOU_BLOCKED_USER"},"stats.getBroadcastStats":{"BROADCAST_REQUIRED":"BROADCAST_REQUIRED","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED"},"messages.deleteMessages":{"BUSINESS_CONNECTION_INVALID":"BUSINESS_CONNECTION_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","MESSAGE_ID_INVALID":"MESSAGE_ID_INVALID","SELF_DELETE_RESTRICTED":"SELF_DELETE_RESTRICTED"},"messages.readHistory":{"BUSINESS_CONNECTION_INVALID":"BUSINESS_CONNECTION_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_ID_INVALID":"CHAT_ID_INVALID","MSG_ID_INVALID":"MSG_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.sendMultiMedia":{"BUSINESS_CONNECTION_INVALID":"BUSINESS_CONNECTION_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","BUSINESS_PEER_INVALID":"BUSINESS_PEER_INVALID","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_FORWARDS_RESTRICTED":"CHAT_FORWARDS_RESTRICTED","EFFECT_ID_INVALID":"EFFECT_ID_INVALID","ENTITY_BOUNDS_INVALID":"ENTITY_BOUNDS_INVALID","FILE_REFERENCE_%d_EMPTY":"FILE_REFERENCE_%d_EMPTY","FILE_REFERENCE_%d_EXPIRED":"FILE_REFERENCE_%d_EXPIRED","FILE_REFERENCE_%d_INVALID":"FILE_REFERENCE_%d_INVALID","MEDIA_CAPTION_TOO_LONG":"MEDIA_CAPTION_TOO_LONG","MEDIA_EMPTY":"MEDIA_EMPTY","MEDIA_INVALID":"MEDIA_INVALID","MSG_ID_INVALID":"MSG_ID_INVALID","MULTI_MEDIA_TOO_LONG":"MULTI_MEDIA_TOO_LONG","PEER_ID_INVALID":"PEER_ID_INVALID","QUICK_REPLIES_BOT_NOT_ALLOWED":"QUICK_REPLIES_BOT_NOT_ALLOWED","QUICK_REPLIES_TOO_MUCH":"QUICK_REPLIES_TOO_MUCH","RANDOM_ID_EMPTY":"RANDOM_ID_EMPTY","REPLY_MESSAGES_TOO_MUCH":"REPLY_MESSAGES_TOO_MUCH","REPLY_TO_INVALID":"REPLY_TO_INVALID","SCHEDULE_DATE_TOO_LATE":"SCHEDULE_DATE_TOO_LATE","SCHEDULE_TOO_MUCH":"SCHEDULE_TOO_MUCH","SEND_AS_PEER_INVALID":"SEND_AS_PEER_INVALID","TOPIC_CLOSED":"TOPIC_CLOSED","TOPIC_DELETED":"TOPIC_DELETED","USER_BANNED_IN_CHANNEL":"USER_BANNED_IN_CHANNEL","USER_IS_BLOCKED":"USER_IS_BLOCKED"},"messages.setTyping":{"BUSINESS_CONNECTION_INVALID":"BUSINESS_CONNECTION_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","BUSINESS_PEER_INVALID":"BUSINESS_PEER_INVALID","BUSINESS_PEER_USAGE_MISSING":"BUSINESS_PEER_USAGE_MISSING","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_MONOFORUM_UNSUPPORTED":"CHANNEL_MONOFORUM_UNSUPPORTED","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_ID_INVALID":"CHAT_ID_INVALID","INPUT_USER_DEACTIVATED":"INPUT_USER_DEACTIVATED","MESSAGE_TOO_LONG":"MESSAGE_TOO_LONG","MSG_ID_INVALID":"MSG_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID","TEXTDRAFT_PEER_INVALID":"TEXTDRAFT_PEER_INVALID","USER_BANNED_IN_CHANNEL":"USER_BANNED_IN_CHANNEL","USER_IS_BLOCKED":"USER_IS_BLOCKED","USER_IS_BOT":"USER_IS_BOT"},"payments.convertStarGift":{"BUSINESS_CONNECTION_INVALID":"BUSINESS_CONNECTION_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","MESSAGE_ID_INVALID":"MESSAGE_ID_INVALID","SAVED_ID_EMPTY":"SAVED_ID_EMPTY","STARGIFT_PEER_INVALID":"STARGIFT_PEER_INVALID","USER_ID_INVALID":"USER_ID_INVALID"},"payments.exportInvoice":{"BUSINESS_CONNECTION_INVALID":"BUSINESS_CONNECTION_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CURRENCY_TOTAL_AMOUNT_INVALID":"CURRENCY_TOTAL_AMOUNT_INVALID","INVOICE_PAYLOAD_INVALID":"INVOICE_PAYLOAD_INVALID","MEDIA_INVALID":"MEDIA_INVALID","PAYMENT_PROVIDER_INVALID":"PAYMENT_PROVIDER_INVALID","STARS_INVOICE_INVALID":"STARS_INVOICE_INVALID","USER_BOT_REQUIRED":"USER_BOT_REQUIRED","WEBDOCUMENT_MIME_INVALID":"WEBDOCUMENT_MIME_INVALID","WEBDOCUMENT_URL_EMPTY":"WEBDOCUMENT_URL_EMPTY"},"payments.getSavedStarGifts":{"BUSINESS_CONNECTION_INVALID":"BUSINESS_CONNECTION_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","COLLECTION_ID_INVALID":"COLLECTION_ID_INVALID","MSG_ID_INVALID":"MSG_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID"},"payments.getStarsStatus":{"BUSINESS_CONNECTION_INVALID":"BUSINESS_CONNECTION_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"payments.transferStarGift":{"BUSINESS_CONNECTION_INVALID":"BUSINESS_CONNECTION_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","MESSAGE_ID_INVALID":"MESSAGE_ID_INVALID","PAYMENT_REQUIRED":"PAYMENT_REQUIRED","PEER_ID_INVALID":"PEER_ID_INVALID","SAVED_ID_EMPTY":"SAVED_ID_EMPTY","STARGIFT_NOT_FOUND":"STARGIFT_NOT_FOUND","STARGIFT_NOT_OWNER":"STARGIFT_NOT_OWNER","STARGIFT_NOT_UNIQUE":"STARGIFT_NOT_UNIQUE","STARGIFT_OWNER_INVALID":"STARGIFT_OWNER_INVALID","STARGIFT_PEER_INVALID":"STARGIFT_PEER_INVALID","STARGIFT_TRANSFER_TOO_EARLY_%d":"STARGIFT_TRANSFER_TOO_EARLY_%d"},"payments.upgradeStarGift":{"BUSINESS_CONNECTION_INVALID":"BUSINESS_CONNECTION_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","MESSAGE_ID_INVALID":"MESSAGE_ID_INVALID","PAYMENT_REQUIRED":"PAYMENT_REQUIRED","SAVED_ID_EMPTY":"SAVED_ID_EMPTY","STARGIFT_ALREADY_CONVERTED":"STARGIFT_ALREADY_CONVERTED","STARGIFT_ALREADY_UPGRADED":"STARGIFT_ALREADY_UPGRADED","STARGIFT_UPGRADE_UNAVAILABLE":"STARGIFT_UPGRADE_UNAVAILABLE"},"stories.deleteStories":{"BUSINESS_CONNECTION_INVALID":"BUSINESS_CONNECTION_INVALID","BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID","STORY_ID_EMPTY":"STORY_ID_EMPTY"},"account.cancelPasswordEmail":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","EMAIL_HASH_EXPIRED":"EMAIL_HASH_EXPIRED"},"account.changeAuthorizationSettings":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","HASH_INVALID":"HASH_INVALID"},"account.changePhone":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PHONE_CODE_EMPTY":"PHONE_CODE_EMPTY","PHONE_CODE_EXPIRED":"PHONE_CODE_EXPIRED","PHONE_CODE_INVALID":"PHONE_CODE_INVALID","PHONE_NUMBER_INVALID":"PHONE_NUMBER_INVALID","PHONE_NUMBER_OCCUPIED":"PHONE_NUMBER_OCCUPIED"},"account.checkUsername":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","USERNAME_INVALID":"USERNAME_INVALID","USERNAME_OCCUPIED":"USERNAME_OCCUPIED","USERNAME_PURCHASE_AVAILABLE":"USERNAME_PURCHASE_AVAILABLE"},"account.clearRecentEmojiStatuses":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.confirmPasswordEmail":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CODE_INVALID":"CODE_INVALID","EMAIL_HASH_EXPIRED":"EMAIL_HASH_EXPIRED"},"account.confirmPhone":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CODE_HASH_INVALID":"CODE_HASH_INVALID","PHONE_CODE_EMPTY":"PHONE_CODE_EMPTY"},"account.createBusinessChatLink":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHATLINKS_TOO_MUCH":"CHATLINKS_TOO_MUCH","DOCUMENT_INVALID":"DOCUMENT_INVALID"},"account.createTheme":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","THEME_MIME_INVALID":"THEME_MIME_INVALID","THEME_TITLE_INVALID":"THEME_TITLE_INVALID"},"account.declinePasswordReset":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","RESET_REQUEST_MISSING":"RESET_REQUEST_MISSING"},"account.deleteAutoSaveExceptions":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.deleteBusinessChatLink":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHATLINK_SLUG_EMPTY":"CHATLINK_SLUG_EMPTY","CHATLINK_SLUG_EXPIRED":"CHATLINK_SLUG_EXPIRED"},"account.deletePasskey":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.deleteSecureValue":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.deleteWebBrowserSettingsExceptions":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.editBusinessChatLink":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHATLINK_SLUG_EMPTY":"CHATLINK_SLUG_EMPTY"},"account.finishTakeoutSession":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.getAccountTTL":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.getAllSecureValues":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.getAuthorizations":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.getAutoDownloadSettings":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.getAutoSaveSettings":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.getBotBusinessConnection":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CONNECTION_ID_INVALID":"CONNECTION_ID_INVALID"},"account.getBusinessChatLinks":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.getChannelDefaultEmojiStatuses":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.getChannelRestrictedStatusEmojis":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.getChatThemes":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.getCollectibleEmojiStatuses":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.getConnectedBots":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.getContactSignUpNotification":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.getContentSettings":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.getDefaultBackgroundEmojis":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.getDefaultEmojiStatuses":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.getDefaultGroupPhotoEmojis":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.getDefaultProfilePhotoEmojis":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.getGlobalPrivacySettings":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.getMultiWallPapers":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","WALLPAPER_INVALID":"WALLPAPER_INVALID"},"account.getNotifyExceptions":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.getNotifySettings":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","PEER_ID_INVALID":"PEER_ID_INVALID"},"account.getPaidMessagesRevenue":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PARENT_PEER_INVALID":"PARENT_PEER_INVALID","USER_ID_INVALID":"USER_ID_INVALID"},"account.getPasskeys":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.getPassword":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.getPasswordSettings":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PASSWORD_HASH_INVALID":"PASSWORD_HASH_INVALID"},"account.getPrivacy":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PRIVACY_KEY_INVALID":"PRIVACY_KEY_INVALID"},"account.getReactionsNotifySettings":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.getRecentEmojiStatuses":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.getSavedMusicIds":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.getSavedRingtones":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.getSecureValue":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.getTheme":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","THEME_FORMAT_INVALID":"THEME_FORMAT_INVALID","THEME_INVALID":"THEME_INVALID","THEME_SLUG_INVALID":"THEME_SLUG_INVALID"},"account.getThemes":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.getTmpPassword":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PASSWORD_HASH_INVALID":"PASSWORD_HASH_INVALID","SRP_A_INVALID":"SRP_A_INVALID","TMP_PASSWORD_DISABLED":"TMP_PASSWORD_DISABLED"},"account.getUniqueGiftChatThemes":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.getWallPaper":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","WALLPAPER_INVALID":"WALLPAPER_INVALID"},"account.getWallPapers":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.getWebAuthorizations":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.getWebBrowserSettings":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.initPasskeyRegistration":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.initTakeoutSession":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.installTheme":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.installWallPaper":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","WALLPAPER_INVALID":"WALLPAPER_INVALID"},"account.invalidateSignInCodes":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.registerDevice":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","TOKEN_EMPTY":"TOKEN_EMPTY","TOKEN_INVALID":"TOKEN_INVALID","TOKEN_TYPE_INVALID":"TOKEN_TYPE_INVALID","WEBPUSH_AUTH_INVALID":"WEBPUSH_AUTH_INVALID","WEBPUSH_KEY_INVALID":"WEBPUSH_KEY_INVALID","WEBPUSH_TOKEN_INVALID":"WEBPUSH_TOKEN_INVALID"},"account.registerPasskey":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CREDENTIAL_INVALID":"CREDENTIAL_INVALID","PASSKEY_ORIGIN_MISMATCH":"PASSKEY_ORIGIN_MISMATCH"},"account.reorderUsernames":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","ORDER_INVALID":"ORDER_INVALID","USERNAME_NOT_MODIFIED":"USERNAME_NOT_MODIFIED"},"account.reportPeer":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","PEER_ID_INVALID":"PEER_ID_INVALID"},"account.reportProfilePhoto":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"account.resendPasswordEmail":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","EMAIL_HASH_EXPIRED":"EMAIL_HASH_EXPIRED"},"account.resetNotifySettings":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.resetWallPapers":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.resetWebAuthorization":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","HASH_INVALID":"HASH_INVALID"},"account.resetWebAuthorizations":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.resolveBusinessChatLink":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHATLINK_SLUG_EMPTY":"CHATLINK_SLUG_EMPTY","CHATLINK_SLUG_EXPIRED":"CHATLINK_SLUG_EXPIRED"},"account.saveAutoDownloadSettings":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.saveAutoSaveSettings":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"account.saveMusic":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","DOCUMENT_INVALID":"DOCUMENT_INVALID"},"account.saveRingtone":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","RINGTONE_INVALID":"RINGTONE_INVALID"},"account.saveSecureValue":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PASSWORD_REQUIRED":"PASSWORD_REQUIRED","SECURE_SECRET_REQUIRED":"SECURE_SECRET_REQUIRED"},"account.saveTheme":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","THEME_INVALID":"THEME_INVALID"},"account.saveWallPaper":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","WALLPAPER_INVALID":"WALLPAPER_INVALID"},"account.sendChangePhoneCode":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PHONE_NUMBER_BANNED":"PHONE_NUMBER_BANNED","PHONE_NUMBER_INVALID":"PHONE_NUMBER_INVALID","PHONE_NUMBER_OCCUPIED":"PHONE_NUMBER_OCCUPIED"},"account.sendConfirmPhoneCode":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","HASH_INVALID":"HASH_INVALID"},"account.sendVerifyEmailCode":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","EMAIL_INVALID":"EMAIL_INVALID","EMAIL_NOT_ALLOWED":"EMAIL_NOT_ALLOWED","EMAIL_NOT_SETUP":"EMAIL_NOT_SETUP","PHONE_CODE_EMPTY":"PHONE_CODE_EMPTY","PHONE_HASH_EXPIRED":"PHONE_HASH_EXPIRED","PHONE_NUMBER_INVALID":"PHONE_NUMBER_INVALID"},"account.sendVerifyPhoneCode":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PHONE_NUMBER_INVALID":"PHONE_NUMBER_INVALID"},"account.setAccountTTL":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","TTL_DAYS_INVALID":"TTL_DAYS_INVALID"},"account.setAuthorizationTTL":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","TTL_DAYS_INVALID":"TTL_DAYS_INVALID"},"account.setContactSignUpNotification":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.setContentSettings":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.setMainProfileTab":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.setPrivacy":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PRIVACY_KEY_INVALID":"PRIVACY_KEY_INVALID","PRIVACY_TOO_LONG":"PRIVACY_TOO_LONG","PRIVACY_VALUE_INVALID":"PRIVACY_VALUE_INVALID"},"account.setReactionsNotifySettings":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.toggleConnectedBotPaused":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"account.toggleNoPaidMessagesException":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PARENT_PEER_INVALID":"PARENT_PEER_INVALID","UNSUPPORTED":"UNSUPPORTED","USER_ID_INVALID":"USER_ID_INVALID"},"account.toggleSponsoredMessages":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.toggleUsername":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","USERNAME_INVALID":"USERNAME_INVALID","USERNAME_NOT_MODIFIED":"USERNAME_NOT_MODIFIED","USERNAMES_ACTIVE_TOO_MUCH":"USERNAMES_ACTIVE_TOO_MUCH"},"account.toggleWebBrowserSettingsException":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","URL_INVALID":"URL_INVALID"},"account.unregisterDevice":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","TOKEN_INVALID":"TOKEN_INVALID"},"account.updateBusinessAwayMessage":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.updateBusinessGreetingMessage":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.updateBusinessIntro":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.updateBusinessLocation":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.updateBusinessWorkHours":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","BUSINESS_WORK_HOURS_EMPTY":"BUSINESS_WORK_HOURS_EMPTY","BUSINESS_WORK_HOURS_PERIOD_INVALID":"BUSINESS_WORK_HOURS_PERIOD_INVALID","TIMEZONE_INVALID":"TIMEZONE_INVALID"},"account.updateColor":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","COLOR_INVALID":"COLOR_INVALID","DOCUMENT_INVALID":"DOCUMENT_INVALID"},"account.updateDeviceLocked":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.updateEmojiStatus":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","COLLECTIBLE_INVALID":"COLLECTIBLE_INVALID","DOCUMENT_INVALID":"DOCUMENT_INVALID"},"account.updateNotifySettings":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","MSG_ID_INVALID":"MSG_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID","SETTINGS_INVALID":"SETTINGS_INVALID"},"account.updatePasswordSettings":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","EMAIL_INVALID":"EMAIL_INVALID","EMAIL_UNCONFIRMED":"EMAIL_UNCONFIRMED","EMAIL_UNCONFIRMED_%d":"EMAIL_UNCONFIRMED_%d","NEW_SALT_INVALID":"NEW_SALT_INVALID","NEW_SETTINGS_EMPTY":"NEW_SETTINGS_EMPTY","NEW_SETTINGS_INVALID":"NEW_SETTINGS_INVALID","PASSWORD_HASH_INVALID":"PASSWORD_HASH_INVALID","SRP_ID_INVALID":"SRP_ID_INVALID","SRP_PASSWORD_CHANGED":"SRP_PASSWORD_CHANGED"},"account.updatePersonalChannel":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PUBLIC_BROADCAST_EXPECTED":"PUBLIC_BROADCAST_EXPECTED"},"account.updateStatus":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.updateTheme":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","THEME_INVALID":"THEME_INVALID"},"account.updateWebBrowserSettings":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"account.uploadRingtone":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","RINGTONE_MIME_INVALID":"RINGTONE_MIME_INVALID"},"account.uploadTheme":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","THEME_FILE_INVALID":"THEME_FILE_INVALID","THEME_MIME_INVALID":"THEME_MIME_INVALID"},"account.uploadWallPaper":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","WALLPAPER_FILE_INVALID":"WALLPAPER_FILE_INVALID","WALLPAPER_MIME_INVALID":"WALLPAPER_MIME_INVALID"},"account.verifyEmail":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CODE_INVALID":"CODE_INVALID","EMAIL_INVALID":"EMAIL_INVALID","EMAIL_NOT_ALLOWED":"EMAIL_NOT_ALLOWED","EMAIL_VERIFY_EXPIRED":"EMAIL_VERIFY_EXPIRED","PHONE_CODE_EXPIRED":"PHONE_CODE_EXPIRED","PHONE_NUMBER_INVALID":"PHONE_NUMBER_INVALID"},"account.verifyPhone":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PHONE_CODE_EMPTY":"PHONE_CODE_EMPTY","PHONE_CODE_EXPIRED":"PHONE_CODE_EXPIRED","PHONE_NUMBER_INVALID":"PHONE_NUMBER_INVALID"},"aicompose.getTones":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"auth.bindTempAuthKey":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","ENCRYPTED_MESSAGE_INVALID":"ENCRYPTED_MESSAGE_INVALID","EXPIRES_AT_INVALID":"EXPIRES_AT_INVALID","TEMP_AUTH_KEY_ALREADY_BOUND":"TEMP_AUTH_KEY_ALREADY_BOUND","TEMP_AUTH_KEY_EMPTY":"TEMP_AUTH_KEY_EMPTY"},"auth.cancelCode":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PHONE_CODE_EXPIRED":"PHONE_CODE_EXPIRED","PHONE_NUMBER_INVALID":"PHONE_NUMBER_INVALID"},"auth.checkPaidAuth":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PHONE_NUMBER_INVALID":"PHONE_NUMBER_INVALID"},"auth.checkPassword":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PASSWORD_HASH_INVALID":"PASSWORD_HASH_INVALID","SRP_ID_INVALID":"SRP_ID_INVALID","SRP_PASSWORD_CHANGED":"SRP_PASSWORD_CHANGED"},"auth.checkRecoveryPassword":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CODE_EMPTY":"CODE_EMPTY","PASSWORD_RECOVERY_EXPIRED":"PASSWORD_RECOVERY_EXPIRED"},"auth.exportAuthorization":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","DC_ID_INVALID":"DC_ID_INVALID"},"auth.finishPasskeyLogin":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CREDENTIAL_INVALID":"CREDENTIAL_INVALID","PASSKEY_ORIGIN_MISMATCH":"PASSKEY_ORIGIN_MISMATCH"},"auth.recoverPassword":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CODE_EMPTY":"CODE_EMPTY","NEW_SETTINGS_INVALID":"NEW_SETTINGS_INVALID"},"auth.reportMissingCode":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PHONE_NUMBER_INVALID":"PHONE_NUMBER_INVALID"},"auth.requestFirebaseSms":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PHONE_CODE_EMPTY":"PHONE_CODE_EMPTY","PHONE_NUMBER_INVALID":"PHONE_NUMBER_INVALID"},"auth.requestPasswordRecovery":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PASSWORD_EMPTY":"PASSWORD_EMPTY","PASSWORD_RECOVERY_NA":"PASSWORD_RECOVERY_NA"},"auth.resendCode":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","EMAIL_INSTALL_MISSING":"EMAIL_INSTALL_MISSING","PHONE_CODE_EMPTY":"PHONE_CODE_EMPTY","PHONE_CODE_EXPIRED":"PHONE_CODE_EXPIRED","PHONE_CODE_HASH_EMPTY":"PHONE_CODE_HASH_EMPTY","PHONE_NUMBER_INVALID":"PHONE_NUMBER_INVALID"},"auth.resetLoginEmail":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","EMAIL_INSTALL_MISSING":"EMAIL_INSTALL_MISSING","PHONE_NUMBER_INVALID":"PHONE_NUMBER_INVALID","TASK_ALREADY_EXISTS":"TASK_ALREADY_EXISTS"},"auth.signIn":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PHONE_CODE_EMPTY":"PHONE_CODE_EMPTY","PHONE_CODE_EXPIRED":"PHONE_CODE_EXPIRED","PHONE_CODE_INVALID":"PHONE_CODE_INVALID","PHONE_NUMBER_INVALID":"PHONE_NUMBER_INVALID","PHONE_NUMBER_UNOCCUPIED":"PHONE_NUMBER_UNOCCUPIED"},"auth.signUp":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","FIRSTNAME_INVALID":"FIRSTNAME_INVALID","LASTNAME_INVALID":"LASTNAME_INVALID","PHONE_CODE_EMPTY":"PHONE_CODE_EMPTY","PHONE_CODE_EXPIRED":"PHONE_CODE_EXPIRED","PHONE_CODE_INVALID":"PHONE_CODE_INVALID","PHONE_NUMBER_FLOOD":"PHONE_NUMBER_FLOOD","PHONE_NUMBER_INVALID":"PHONE_NUMBER_INVALID","PHONE_NUMBER_OCCUPIED":"PHONE_NUMBER_OCCUPIED"},"bots.answerWebhookJSONQuery":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","DATA_JSON_INVALID":"DATA_JSON_INVALID","QUERY_ID_INVALID":"QUERY_ID_INVALID","USER_BOT_REQUIRED":"USER_BOT_REQUIRED"},"bots.checkUsername":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","USERNAME_INVALID":"USERNAME_INVALID","USERNAME_OCCUPIED":"USERNAME_OCCUPIED","USERNAME_SUFFIX_MISSING":"USERNAME_SUFFIX_MISSING"},"bots.getAdminedBots":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"bots.getBotCommands":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","USER_BOT_REQUIRED":"USER_BOT_REQUIRED"},"bots.getBotMenuButton":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","USER_BOT_REQUIRED":"USER_BOT_REQUIRED"},"bots.getPopularAppBots":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"bots.requestWebViewButton":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","BUTTON_INVALID":"BUTTON_INVALID","USER_BOT_REQUIRED":"USER_BOT_REQUIRED","USER_ID_INVALID":"USER_ID_INVALID"},"bots.resetBotCommands":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","LANG_CODE_INVALID":"LANG_CODE_INVALID","USER_BOT_REQUIRED":"USER_BOT_REQUIRED"},"bots.sendCustomRequest":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","DATA_JSON_INVALID":"DATA_JSON_INVALID","METHOD_INVALID":"METHOD_INVALID","USER_BOT_REQUIRED":"USER_BOT_REQUIRED"},"bots.setBotBroadcastDefaultAdminRights":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","RIGHTS_NOT_MODIFIED":"RIGHTS_NOT_MODIFIED","USER_BOT_REQUIRED":"USER_BOT_REQUIRED"},"bots.setBotGroupDefaultAdminRights":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","RIGHTS_NOT_MODIFIED":"RIGHTS_NOT_MODIFIED","USER_BOT_REQUIRED":"USER_BOT_REQUIRED"},"bots.setBotMenuButton":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","BUTTON_INVALID":"BUTTON_INVALID","BUTTON_TEXT_INVALID":"BUTTON_TEXT_INVALID","BUTTON_URL_INVALID":"BUTTON_URL_INVALID","USER_BOT_REQUIRED":"USER_BOT_REQUIRED"},"bots.setJoinChatResults":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","USER_BOT_REQUIRED":"USER_BOT_REQUIRED"},"bots.updateUserEmojiStatus":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","USER_BOT_REQUIRED":"USER_BOT_REQUIRED","USER_ID_INVALID":"USER_ID_INVALID"},"channels.checkSearchPostsFlood":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"channels.checkUsername":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHANNELS_ADMIN_PUBLIC_TOO_MUCH":"CHANNELS_ADMIN_PUBLIC_TOO_MUCH","CHAT_ID_INVALID":"CHAT_ID_INVALID","MSG_ID_INVALID":"MSG_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID","USERNAME_INVALID":"USERNAME_INVALID","USERNAME_OCCUPIED":"USERNAME_OCCUPIED","USERNAME_PURCHASE_AVAILABLE":"USERNAME_PURCHASE_AVAILABLE"},"channels.convertToGigagroup":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_ID_INVALID":"CHANNEL_ID_INVALID","CHANNEL_INVALID":"CHANNEL_INVALID","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","FORUM_ENABLED":"FORUM_ENABLED","PARTICIPANTS_TOO_FEW":"PARTICIPANTS_TOO_FEW"},"channels.createForumTopic":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_FORUM_MISSING":"CHANNEL_FORUM_MISSING","CHANNEL_INVALID":"CHANNEL_INVALID","DOCUMENT_INVALID":"DOCUMENT_INVALID","TOPIC_TITLE_EMPTY":"TOPIC_TITLE_EMPTY"},"channels.deactivateAllUsernames":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID"},"channels.deleteChannel":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHANNEL_TOO_LARGE":"CHANNEL_TOO_LARGE","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_NOT_MODIFIED":"CHAT_NOT_MODIFIED"},"channels.deleteHistory":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PARICIPANT_MISSING":"CHANNEL_PARICIPANT_MISSING","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHANNEL_TOO_BIG":"CHANNEL_TOO_BIG","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED"},"channels.deleteMessages":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","MSG_ID_INVALID":"MSG_ID_INVALID"},"channels.deleteParticipantHistory":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","MSG_ID_INVALID":"MSG_ID_INVALID","PARTICIPANT_ID_INVALID":"PARTICIPANT_ID_INVALID"},"channels.deleteTopicHistory":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_FORUM_MISSING":"CHANNEL_FORUM_MISSING","CHANNEL_INVALID":"CHANNEL_INVALID","TOPIC_ID_INVALID":"TOPIC_ID_INVALID"},"channels.editBanned":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","INPUT_USER_DEACTIVATED":"INPUT_USER_DEACTIVATED","MSG_ID_INVALID":"MSG_ID_INVALID","PARTICIPANT_ID_INVALID":"PARTICIPANT_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID","USER_ADMIN_INVALID":"USER_ADMIN_INVALID","USER_ID_INVALID":"USER_ID_INVALID"},"channels.editCreator":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_MONOFORUM_UNSUPPORTED":"CHANNEL_MONOFORUM_UNSUPPORTED","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHANNELS_ADMIN_PUBLIC_TOO_MUCH":"CHANNELS_ADMIN_PUBLIC_TOO_MUCH","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_MEMBER_ADD_FAILED":"CHAT_MEMBER_ADD_FAILED","CHAT_NOT_MODIFIED":"CHAT_NOT_MODIFIED","INPUT_USER_DEACTIVATED":"INPUT_USER_DEACTIVATED","PASSWORD_HASH_INVALID":"PASSWORD_HASH_INVALID","PASSWORD_MISSING":"PASSWORD_MISSING","PASSWORD_TOO_FRESH_%d":"PASSWORD_TOO_FRESH_%d","SESSION_TOO_FRESH_%d":"SESSION_TOO_FRESH_%d","SRP_ID_INVALID":"SRP_ID_INVALID","USER_ID_INVALID":"USER_ID_INVALID","USER_NOT_MUTUAL_CONTACT":"USER_NOT_MUTUAL_CONTACT","USERS_TOO_MUCH":"USERS_TOO_MUCH"},"channels.editForumTopic":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_FORUM_MISSING":"CHANNEL_FORUM_MISSING","CHANNEL_INVALID":"CHANNEL_INVALID","DOCUMENT_INVALID":"DOCUMENT_INVALID","GENERAL_MODIFY_ICON_FORBIDDEN":"GENERAL_MODIFY_ICON_FORBIDDEN","NOGENERAL_HIDE_FORBIDDEN":"NOGENERAL_HIDE_FORBIDDEN","TOPIC_CLOSE_SEPARATELY":"TOPIC_CLOSE_SEPARATELY","TOPIC_HIDE_SEPARATELY":"TOPIC_HIDE_SEPARATELY","TOPIC_ID_INVALID":"TOPIC_ID_INVALID","TOPIC_NOT_MODIFIED":"TOPIC_NOT_MODIFIED"},"channels.editLocation":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_NOT_MODIFIED":"CHAT_NOT_MODIFIED","MEGAGROUP_GEO_REQUIRED":"MEGAGROUP_GEO_REQUIRED","MEGAGROUP_REQUIRED":"MEGAGROUP_REQUIRED"},"channels.editPhoto":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_INVALID":"CHAT_INVALID","CHAT_NOT_MODIFIED":"CHAT_NOT_MODIFIED","FILE_PARTS_INVALID":"FILE_PARTS_INVALID","FILE_REFERENCE_INVALID":"FILE_REFERENCE_INVALID","IMAGE_PROCESS_FAILED":"IMAGE_PROCESS_FAILED","PEER_ID_INVALID":"PEER_ID_INVALID","PHOTO_CROP_SIZE_SMALL":"PHOTO_CROP_SIZE_SMALL","PHOTO_EXT_INVALID":"PHOTO_EXT_INVALID","PHOTO_FILE_MISSING":"PHOTO_FILE_MISSING","PHOTO_INVALID":"PHOTO_INVALID","STICKER_MIME_INVALID":"STICKER_MIME_INVALID"},"channels.editTitle":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_INVALID":"CHAT_INVALID","CHAT_NOT_MODIFIED":"CHAT_NOT_MODIFIED","CHAT_TITLE_EMPTY":"CHAT_TITLE_EMPTY"},"channels.exportMessageLink":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","MESSAGE_ID_INVALID":"MESSAGE_ID_INVALID","MSG_ID_INVALID":"MSG_ID_INVALID"},"channels.getAdminedPublicChannels":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNELS_ADMIN_LOCATED_TOO_MUCH":"CHANNELS_ADMIN_LOCATED_TOO_MUCH","CHANNELS_ADMIN_PUBLIC_TOO_MUCH":"CHANNELS_ADMIN_PUBLIC_TOO_MUCH"},"channels.getAdminLog":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","MSG_ID_INVALID":"MSG_ID_INVALID"},"channels.getChannelRecommendations":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_NOT_MODIFIED":"CHAT_NOT_MODIFIED"},"channels.getChannels":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","MSG_ID_INVALID":"MSG_ID_INVALID","USER_BANNED_IN_CHANNEL":"USER_BANNED_IN_CHANNEL"},"channels.getForumTopics":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_FORUM_MISSING":"CHANNEL_FORUM_MISSING","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_MONOFORUM_UNSUPPORTED":"CHANNEL_MONOFORUM_UNSUPPORTED","CHANNEL_PRIVATE":"CHANNEL_PRIVATE"},"channels.getForumTopicsByID":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_FORUM_MISSING":"CHANNEL_FORUM_MISSING","CHANNEL_INVALID":"CHANNEL_INVALID","TOPICS_EMPTY":"TOPICS_EMPTY"},"channels.getFullChannel":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_NOT_MODIFIED":"CHAT_NOT_MODIFIED","MSG_ID_INVALID":"MSG_ID_INVALID"},"channels.getFutureCreatorAfterLeave":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID"},"channels.getGroupsForDiscussion":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"channels.getInactiveChannels":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"channels.getLeftChannels":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","TAKEOUT_INVALID":"TAKEOUT_INVALID"},"channels.getMessageAuthor":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID"},"channels.getMessages":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_NOT_MODIFIED":"CHAT_NOT_MODIFIED","FROZEN_PARTICIPANT_MISSING":"FROZEN_PARTICIPANT_MISSING","MESSAGE_IDS_EMPTY":"MESSAGE_IDS_EMPTY","MSG_ID_INVALID":"MSG_ID_INVALID","USER_BANNED_IN_CHANNEL":"USER_BANNED_IN_CHANNEL"},"channels.getParticipant":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","MSG_ID_INVALID":"MSG_ID_INVALID","PARTICIPANT_ID_INVALID":"PARTICIPANT_ID_INVALID","USER_ID_INVALID":"USER_ID_INVALID","USER_NOT_PARTICIPANT":"USER_NOT_PARTICIPANT"},"channels.getParticipants":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_MONOFORUM_UNSUPPORTED":"CHANNEL_MONOFORUM_UNSUPPORTED","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","MSG_ID_INVALID":"MSG_ID_INVALID"},"channels.getSendAs":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_ID_INVALID":"CHAT_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID"},"channels.joinChannel":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_MONOFORUM_UNSUPPORTED":"CHANNEL_MONOFORUM_UNSUPPORTED","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHANNELS_TOO_MUCH":"CHANNELS_TOO_MUCH","CHAT_INVALID":"CHAT_INVALID","INVITE_HASH_EMPTY":"INVITE_HASH_EMPTY","INVITE_HASH_EXPIRED":"INVITE_HASH_EXPIRED","INVITE_HASH_INVALID":"INVITE_HASH_INVALID","INVITE_REQUEST_SENT":"INVITE_REQUEST_SENT","MSG_ID_INVALID":"MSG_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID","USER_ALREADY_PARTICIPANT":"USER_ALREADY_PARTICIPANT","USER_BANNED_IN_CHANNEL":"USER_BANNED_IN_CHANNEL","USER_CHANNELS_TOO_MUCH":"USER_CHANNELS_TOO_MUCH","USERS_TOO_MUCH":"USERS_TOO_MUCH"},"channels.leaveChannel":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_INVALID":"CHAT_INVALID","MSG_ID_INVALID":"MSG_ID_INVALID","USER_BANNED_IN_CHANNEL":"USER_BANNED_IN_CHANNEL","USER_CREATOR":"USER_CREATOR","USER_NOT_PARTICIPANT":"USER_NOT_PARTICIPANT"},"channels.readHistory":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","MSG_ID_INVALID":"MSG_ID_INVALID"},"channels.readMessageContents":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","MSG_ID_INVALID":"MSG_ID_INVALID"},"channels.reorderPinnedForumTopics":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID"},"channels.reorderUsernames":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHAT_NOT_MODIFIED":"CHAT_NOT_MODIFIED"},"channels.reportAntiSpamFalsePositive":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID"},"channels.reportSpam":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","INPUT_USER_DEACTIVATED":"INPUT_USER_DEACTIVATED","MSG_ID_INVALID":"MSG_ID_INVALID","USER_ID_INVALID":"USER_ID_INVALID"},"channels.restrictSponsoredMessages":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID"},"channels.searchPosts":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","OFFSET_PEER_ID_INVALID":"OFFSET_PEER_ID_INVALID"},"channels.setBoostsToUnblockRestrictions":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID"},"channels.setEmojiStickers":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID"},"channels.setMainProfileTab":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID"},"channels.setStickers":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHAT_ID_INVALID":"CHAT_ID_INVALID","PARTICIPANTS_TOO_FEW":"PARTICIPANTS_TOO_FEW"},"channels.toggleAntiSpam":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHAT_NOT_MODIFIED":"CHAT_NOT_MODIFIED"},"channels.toggleAutotranslation":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID"},"channels.toggleForum":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_DISCUSSION_UNALLOWED":"CHAT_DISCUSSION_UNALLOWED","CHAT_ID_INVALID":"CHAT_ID_INVALID","CHAT_NOT_MODIFIED":"CHAT_NOT_MODIFIED"},"channels.toggleJoinRequest":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_ID_INVALID":"CHAT_ID_INVALID","CHAT_NOT_MODIFIED":"CHAT_NOT_MODIFIED","CHAT_PUBLIC_REQUIRED":"CHAT_PUBLIC_REQUIRED"},"channels.toggleJoinToSend":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_ID_INVALID":"CHAT_ID_INVALID","CHAT_NOT_MODIFIED":"CHAT_NOT_MODIFIED"},"channels.toggleParticipantsHidden":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_ID_INVALID":"CHAT_ID_INVALID","CHAT_NOT_MODIFIED":"CHAT_NOT_MODIFIED","PARTICIPANTS_TOO_FEW":"PARTICIPANTS_TOO_FEW"},"channels.togglePreHistoryHidden":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_ID_INVALID":"CHAT_ID_INVALID","CHAT_LINK_EXISTS":"CHAT_LINK_EXISTS","CHAT_NOT_MODIFIED":"CHAT_NOT_MODIFIED","FORUM_ENABLED":"FORUM_ENABLED"},"channels.toggleSignatures":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_ID_INVALID":"CHAT_ID_INVALID","CHAT_NOT_MODIFIED":"CHAT_NOT_MODIFIED"},"channels.toggleSlowMode":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_ID_INVALID":"CHAT_ID_INVALID","CHAT_NOT_MODIFIED":"CHAT_NOT_MODIFIED","SECONDS_INVALID":"SECONDS_INVALID"},"channels.toggleUsername":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_NOT_MODIFIED":"CHAT_NOT_MODIFIED","USERNAME_INVALID":"USERNAME_INVALID","USERNAME_NOT_MODIFIED":"USERNAME_NOT_MODIFIED","USERNAMES_ACTIVE_TOO_MUCH":"USERNAMES_ACTIVE_TOO_MUCH"},"channels.toggleViewForumAsMessages":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID"},"channels.updateEmojiStatus":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID"},"channels.updatePaidMessagesPrice":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_MONOFORUM_UNSUPPORTED":"CHANNEL_MONOFORUM_UNSUPPORTED","CHAT_NOT_MODIFIED":"CHAT_NOT_MODIFIED","STARS_AMOUNT_INVALID":"STARS_AMOUNT_INVALID"},"channels.updatePinnedForumTopic":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_FORUM_MISSING":"CHANNEL_FORUM_MISSING","CHANNEL_INVALID":"CHANNEL_INVALID","PINNED_TOO_MUCH":"PINNED_TOO_MUCH","PINNED_TOPIC_NOT_MODIFIED":"PINNED_TOPIC_NOT_MODIFIED","TOPIC_ID_INVALID":"TOPIC_ID_INVALID"},"channels.updateUsername":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHANNELS_ADMIN_PUBLIC_TOO_MUCH":"CHANNELS_ADMIN_PUBLIC_TOO_MUCH","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_NOT_MODIFIED":"CHAT_NOT_MODIFIED","USERNAME_INVALID":"USERNAME_INVALID","USERNAME_NOT_MODIFIED":"USERNAME_NOT_MODIFIED","USERNAME_OCCUPIED":"USERNAME_OCCUPIED","USERNAME_PURCHASE_AVAILABLE":"USERNAME_PURCHASE_AVAILABLE"},"chatlists.checkChatlistInvite":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","INVITE_SLUG_EMPTY":"INVITE_SLUG_EMPTY","INVITE_SLUG_EXPIRED":"INVITE_SLUG_EXPIRED"},"chatlists.deleteExportedInvite":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","FILTER_ID_INVALID":"FILTER_ID_INVALID","FILTER_NOT_SUPPORTED":"FILTER_NOT_SUPPORTED","INVITE_SLUG_EXPIRED":"INVITE_SLUG_EXPIRED","INVITE_SLUG_INVALID":"INVITE_SLUG_INVALID"},"chatlists.editExportedInvite":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","FILTER_ID_INVALID":"FILTER_ID_INVALID","FILTER_NOT_SUPPORTED":"FILTER_NOT_SUPPORTED","INVITE_SLUG_EMPTY":"INVITE_SLUG_EMPTY","INVITE_SLUG_EXPIRED":"INVITE_SLUG_EXPIRED","PEERS_LIST_EMPTY":"PEERS_LIST_EMPTY"},"chatlists.exportChatlistInvite":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHATLISTS_TOO_MUCH":"CHATLISTS_TOO_MUCH","FILTER_ID_INVALID":"FILTER_ID_INVALID","FILTER_NOT_SUPPORTED":"FILTER_NOT_SUPPORTED","INVITES_TOO_MUCH":"INVITES_TOO_MUCH","PEERS_LIST_EMPTY":"PEERS_LIST_EMPTY"},"chatlists.getChatlistUpdates":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","FILTER_ID_INVALID":"FILTER_ID_INVALID","FILTER_NOT_SUPPORTED":"FILTER_NOT_SUPPORTED","INPUT_CHATLIST_INVALID":"INPUT_CHATLIST_INVALID"},"chatlists.getExportedInvites":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","FILTER_ID_INVALID":"FILTER_ID_INVALID"},"chatlists.getLeaveChatlistSuggestions":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","FILTER_ID_INVALID":"FILTER_ID_INVALID","FILTER_NOT_SUPPORTED":"FILTER_NOT_SUPPORTED"},"chatlists.hideChatlistUpdates":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","FILTER_ID_INVALID":"FILTER_ID_INVALID","FILTER_NOT_SUPPORTED":"FILTER_NOT_SUPPORTED"},"chatlists.joinChatlistInvite":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNELS_TOO_MUCH":"CHANNELS_TOO_MUCH","CHATLISTS_TOO_MUCH":"CHATLISTS_TOO_MUCH","FILTER_INCLUDE_EMPTY":"FILTER_INCLUDE_EMPTY","INVITE_SLUG_EMPTY":"INVITE_SLUG_EMPTY","INVITE_SLUG_EXPIRED":"INVITE_SLUG_EXPIRED"},"chatlists.joinChatlistUpdates":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","FILTER_ID_INVALID":"FILTER_ID_INVALID","FILTER_INCLUDE_EMPTY":"FILTER_INCLUDE_EMPTY"},"chatlists.leaveChatlist":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","FILTER_ID_INVALID":"FILTER_ID_INVALID"},"contacts.acceptContact":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CONTACT_ADD_MISSING":"CONTACT_ADD_MISSING","CONTACT_ID_INVALID":"CONTACT_ID_INVALID","CONTACT_REQ_MISSING":"CONTACT_REQ_MISSING","MSG_ID_INVALID":"MSG_ID_INVALID"},"contacts.addContact":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CONTACT_ID_INVALID":"CONTACT_ID_INVALID","CONTACT_NAME_EMPTY":"CONTACT_NAME_EMPTY","MSG_ID_INVALID":"MSG_ID_INVALID"},"contacts.block":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CONTACT_ID_INVALID":"CONTACT_ID_INVALID","INPUT_USER_DEACTIVATED":"INPUT_USER_DEACTIVATED","MSG_ID_INVALID":"MSG_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID"},"contacts.blockFromReplies":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","MSG_ID_INVALID":"MSG_ID_INVALID"},"contacts.deleteByPhones":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"contacts.deleteContacts":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","MSG_ID_INVALID":"MSG_ID_INVALID"},"contacts.editCloseFriends":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"contacts.exportContactToken":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"contacts.getBirthdays":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"contacts.getBlocked":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"contacts.getContactIDs":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"contacts.getContacts":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"contacts.getLocated":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","GEO_POINT_INVALID":"GEO_POINT_INVALID","USERPIC_UPLOAD_REQUIRED":"USERPIC_UPLOAD_REQUIRED"},"contacts.getSaved":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","TAKEOUT_INVALID":"TAKEOUT_INVALID","TAKEOUT_REQUIRED":"TAKEOUT_REQUIRED"},"contacts.getSponsoredPeers":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","SEARCH_QUERY_EMPTY":"SEARCH_QUERY_EMPTY"},"contacts.getStatuses":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"contacts.getTopPeers":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","TYPES_EMPTY":"TYPES_EMPTY"},"contacts.importContacts":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"contacts.importContactToken":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","IMPORT_TOKEN_INVALID":"IMPORT_TOKEN_INVALID"},"contacts.resetSaved":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"contacts.resetTopPeerRating":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"contacts.resolvePhone":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PHONE_NOT_OCCUPIED":"PHONE_NOT_OCCUPIED"},"contacts.resolveUsername":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CONNECTION_LAYER_INVALID":"CONNECTION_LAYER_INVALID","STARREF_EXPIRED":"STARREF_EXPIRED","USERNAME_INVALID":"USERNAME_INVALID","USERNAME_NOT_OCCUPIED":"USERNAME_NOT_OCCUPIED"},"contacts.search":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","QUERY_TOO_SHORT":"QUERY_TOO_SHORT","SEARCH_QUERY_EMPTY":"SEARCH_QUERY_EMPTY"},"contacts.setBlocked":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"contacts.toggleTopPeers":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"contacts.unblock":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CONTACT_ID_INVALID":"CONTACT_ID_INVALID","MSG_ID_INVALID":"MSG_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID"},"contacts.updateContactNote":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CONTACT_ID_INVALID":"CONTACT_ID_INVALID","CONTACT_MISSING":"CONTACT_MISSING"},"folders.editPeerFolders":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_ID_INVALID":"CHAT_ID_INVALID","FOLDER_ID_INVALID":"FOLDER_ID_INVALID"},"fragment.getCollectibleInfo":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","COLLECTIBLE_INVALID":"COLLECTIBLE_INVALID","COLLECTIBLE_NOT_FOUND":"COLLECTIBLE_NOT_FOUND"},"help.acceptTermsOfService":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","DATA_JSON_INVALID":"DATA_JSON_INVALID"},"help.dismissSuggestion":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"help.editUserInfo":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","ENTITY_BOUNDS_INVALID":"ENTITY_BOUNDS_INVALID","USER_INVALID":"USER_INVALID"},"help.getAppConfig":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"help.getAppUpdate":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"help.getCdnConfig":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"help.getConfig":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CONNECTION_API_ID_INVALID":"CONNECTION_API_ID_INVALID","CONNECTION_APP_VERSION_EMPTY":"CONNECTION_APP_VERSION_EMPTY","CONNECTION_LAYER_INVALID":"CONNECTION_LAYER_INVALID","DATA_INVALID":"DATA_INVALID","MSG_ID_INVALID":"MSG_ID_INVALID","USERNAME_INVALID":"USERNAME_INVALID"},"help.getCountriesList":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"help.getDeepLinkInfo":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"help.getInviteText":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"help.getNearestDc":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"help.getPassportConfig":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"help.getPeerColors":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"help.getPeerProfileColors":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"help.getPremiumPromo":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"help.getPromoData":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"help.getRecentMeUrls":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"help.getSupport":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"help.getSupportName":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","USER_INVALID":"USER_INVALID"},"help.getTermsOfServiceUpdate":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"help.getTimezonesList":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"help.getUserInfo":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","USER_INVALID":"USER_INVALID"},"help.hidePromoData":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"help.saveAppLog":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"help.setBotUpdatesStatus":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","USER_BOT_REQUIRED":"USER_BOT_REQUIRED"},"langpack.getDifference":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","LANG_PACK_INVALID":"LANG_PACK_INVALID"},"langpack.getLangPack":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","LANG_CODE_NOT_SUPPORTED":"LANG_CODE_NOT_SUPPORTED","LANG_PACK_INVALID":"LANG_PACK_INVALID","LANGUAGE_INVALID":"LANGUAGE_INVALID"},"langpack.getLanguage":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","LANG_CODE_NOT_SUPPORTED":"LANG_CODE_NOT_SUPPORTED","LANG_PACK_INVALID":"LANG_PACK_INVALID"},"langpack.getLanguages":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","LANG_PACK_INVALID":"LANG_PACK_INVALID"},"langpack.getStrings":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","LANG_CODE_NOT_SUPPORTED":"LANG_CODE_NOT_SUPPORTED","LANG_PACK_INVALID":"LANG_PACK_INVALID"},"messages.acceptEncryption":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHAT_ID_INVALID":"CHAT_ID_INVALID","ENCRYPTION_ALREADY_ACCEPTED":"ENCRYPTION_ALREADY_ACCEPTED","ENCRYPTION_ALREADY_DECLINED":"ENCRYPTION_ALREADY_DECLINED"},"messages.acceptUrlAuth":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.addPollAnswer":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.appendTodoList":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","MESSAGE_ID_INVALID":"MESSAGE_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID","TODO_ITEM_DUPLICATE":"TODO_ITEM_DUPLICATE","TODO_NOT_MODIFIED":"TODO_NOT_MODIFIED"},"messages.checkChatInvite":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","INVITE_HASH_EMPTY":"INVITE_HASH_EMPTY","INVITE_HASH_EXPIRED":"INVITE_HASH_EXPIRED","INVITE_HASH_INVALID":"INVITE_HASH_INVALID"},"messages.checkHistoryImport":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","IMPORT_FORMAT_UNRECOGNIZED":"IMPORT_FORMAT_UNRECOGNIZED"},"messages.checkHistoryImportPeer":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","PEER_ID_INVALID":"PEER_ID_INVALID","USER_NOT_MUTUAL_CONTACT":"USER_NOT_MUTUAL_CONTACT"},"messages.checkQuickReplyShortcut":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.checkUrlAuthMatchCode":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","URL_INVALID":"URL_INVALID"},"messages.clearAllDrafts":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.clearRecentReactions":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.clearRecentStickers":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.clickSponsoredMessage":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.createChat":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHAT_INVALID":"CHAT_INVALID","CHAT_MEMBER_ADD_FAILED":"CHAT_MEMBER_ADD_FAILED","CHAT_TITLE_EMPTY":"CHAT_TITLE_EMPTY","INPUT_USER_DEACTIVATED":"INPUT_USER_DEACTIVATED","TTL_PERIOD_INVALID":"TTL_PERIOD_INVALID","USERS_TOO_FEW":"USERS_TOO_FEW"},"messages.createForumTopic":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_FORUM_MISSING":"CHANNEL_FORUM_MISSING","CHANNEL_INVALID":"CHANNEL_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.declineUrlAuth":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","URL_INVALID":"URL_INVALID"},"messages.deleteChat":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_ID_INVALID":"CHAT_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.deleteChatUser":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_ID_INVALID":"CHAT_ID_INVALID","INPUT_USER_DEACTIVATED":"INPUT_USER_DEACTIVATED","PEER_ID_INVALID":"PEER_ID_INVALID","USER_ID_INVALID":"USER_ID_INVALID","USER_NOT_PARTICIPANT":"USER_NOT_PARTICIPANT"},"messages.deleteExportedChatInvite":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","INVITE_HASH_EXPIRED":"INVITE_HASH_EXPIRED","INVITE_REVOKED_MISSING":"INVITE_REVOKED_MISSING","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.deleteFactCheck":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.deleteHistory":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_ID_INVALID":"CHAT_ID_INVALID","CHAT_REVOKE_DATE_UNSUPPORTED":"CHAT_REVOKE_DATE_UNSUPPORTED","MAX_DATE_INVALID":"MAX_DATE_INVALID","MESSAGE_ID_INVALID":"MESSAGE_ID_INVALID","MIN_DATE_INVALID":"MIN_DATE_INVALID","MSG_ID_INVALID":"MSG_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.deleteParticipantReaction":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.deleteParticipantReactions":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.deletePhoneCallHistory":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.deletePollAnswer":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.deleteQuickReplyMessages":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","SHORTCUT_INVALID":"SHORTCUT_INVALID"},"messages.deleteQuickReplyShortcut":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","SHORTCUT_INVALID":"SHORTCUT_INVALID"},"messages.deleteSavedHistory":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.deleteScheduledMessages":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.deleteTopicHistory":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID"},"messages.discardEncryption":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHAT_ID_EMPTY":"CHAT_ID_EMPTY","ENCRYPTION_ALREADY_ACCEPTED":"ENCRYPTION_ALREADY_ACCEPTED","ENCRYPTION_ALREADY_DECLINED":"ENCRYPTION_ALREADY_DECLINED","ENCRYPTION_ID_INVALID":"ENCRYPTION_ID_INVALID"},"messages.editChatAbout":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_ABOUT_NOT_MODIFIED":"CHAT_ABOUT_NOT_MODIFIED","CHAT_ABOUT_TOO_LONG":"CHAT_ABOUT_TOO_LONG","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_ID_INVALID":"CHAT_ID_INVALID","CHAT_NOT_MODIFIED":"CHAT_NOT_MODIFIED","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.editChatAdmin":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHAT_ID_INVALID":"CHAT_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID","USER_ID_INVALID":"USER_ID_INVALID","USER_NOT_PARTICIPANT":"USER_NOT_PARTICIPANT"},"messages.editChatCreator":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHANNELS_ADMIN_PUBLIC_TOO_MUCH":"CHANNELS_ADMIN_PUBLIC_TOO_MUCH","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_MEMBER_ADD_FAILED":"CHAT_MEMBER_ADD_FAILED","INPUT_USER_DEACTIVATED":"INPUT_USER_DEACTIVATED","PASSWORD_HASH_INVALID":"PASSWORD_HASH_INVALID","PASSWORD_MISSING":"PASSWORD_MISSING","PASSWORD_TOO_FRESH_%d":"PASSWORD_TOO_FRESH_%d","SESSION_TOO_FRESH_%d":"SESSION_TOO_FRESH_%d","SRP_ID_INVALID":"SRP_ID_INVALID","USER_NOT_MUTUAL_CONTACT":"USER_NOT_MUTUAL_CONTACT","USERS_TOO_MUCH":"USERS_TOO_MUCH"},"messages.editChatParticipantRank":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.editChatPhoto":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHAT_ID_INVALID":"CHAT_ID_INVALID","CHAT_NOT_MODIFIED":"CHAT_NOT_MODIFIED","IMAGE_PROCESS_FAILED":"IMAGE_PROCESS_FAILED","PEER_ID_INVALID":"PEER_ID_INVALID","PHOTO_CROP_SIZE_SMALL":"PHOTO_CROP_SIZE_SMALL","PHOTO_EXT_INVALID":"PHOTO_EXT_INVALID","PHOTO_INVALID":"PHOTO_INVALID"},"messages.editChatTitle":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_ID_INVALID":"CHAT_ID_INVALID","CHAT_NOT_MODIFIED":"CHAT_NOT_MODIFIED","CHAT_TITLE_EMPTY":"CHAT_TITLE_EMPTY","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.editExportedChatInvite":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_INVITE_PERMANENT":"CHAT_INVITE_PERMANENT","INVITE_HASH_EXPIRED":"INVITE_HASH_EXPIRED","PEER_ID_INVALID":"PEER_ID_INVALID","USAGE_LIMIT_INVALID":"USAGE_LIMIT_INVALID"},"messages.editFactCheck":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.editForumTopic":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","TOPIC_CLOSE_SEPARATELY":"TOPIC_CLOSE_SEPARATELY","TOPIC_NOT_MODIFIED":"TOPIC_NOT_MODIFIED"},"messages.editInlineBotMessage":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","BUTTON_DATA_INVALID":"BUTTON_DATA_INVALID","ENTITY_BOUNDS_INVALID":"ENTITY_BOUNDS_INVALID","MESSAGE_ID_INVALID":"MESSAGE_ID_INVALID","MESSAGE_NOT_MODIFIED":"MESSAGE_NOT_MODIFIED"},"messages.editQuickReplyShortcut":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","SHORTCUT_INVALID":"SHORTCUT_INVALID"},"messages.exportChatInvite":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_MONOFORUM_UNSUPPORTED":"CHANNEL_MONOFORUM_UNSUPPORTED","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_ID_INVALID":"CHAT_ID_INVALID","EXPIRE_DATE_INVALID":"EXPIRE_DATE_INVALID","INPUT_USER_DEACTIVATED":"INPUT_USER_DEACTIVATED","MSG_ID_INVALID":"MSG_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID","PRICING_CHAT_INVALID":"PRICING_CHAT_INVALID","SUBSCRIPTION_PERIOD_INVALID":"SUBSCRIPTION_PERIOD_INVALID","USAGE_LIMIT_INVALID":"USAGE_LIMIT_INVALID"},"messages.faveSticker":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","STICKER_ID_INVALID":"STICKER_ID_INVALID"},"messages.getAdminsWithInvites":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.getAllDrafts":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.getAllStickers":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.getArchivedStickers":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.getAttachedStickers":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","MEDIA_EMPTY":"MEDIA_EMPTY"},"messages.getAttachMenuBots":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.getAvailableEffects":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.getAvailableReactions":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.getChatInviteImporters":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","INVITE_HASH_EXPIRED":"INVITE_HASH_EXPIRED","PEER_ID_INVALID":"PEER_ID_INVALID","SEARCH_WITH_LINK_NOT_SUPPORTED":"SEARCH_WITH_LINK_NOT_SUPPORTED"},"messages.getChats":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHAT_ID_INVALID":"CHAT_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.getCommonChats":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","MSG_ID_INVALID":"MSG_ID_INVALID","USER_ID_INVALID":"USER_ID_INVALID"},"messages.getCustomEmojiDocuments":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.getDefaultHistoryTTL":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.getDefaultTagReactions":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.getDhConfig":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","RANDOM_LENGTH_INVALID":"RANDOM_LENGTH_INVALID"},"messages.getDialogFilters":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.getDialogs":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHAT_NOT_MODIFIED":"CHAT_NOT_MODIFIED","FOLDER_ID_INVALID":"FOLDER_ID_INVALID","OFFSET_PEER_ID_INVALID":"OFFSET_PEER_ID_INVALID","PINNED_DIALOGS_TOO_MUCH":"PINNED_DIALOGS_TOO_MUCH","TAKEOUT_INVALID":"TAKEOUT_INVALID"},"messages.getDialogUnreadMarks":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.getDiscussionMessage":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","MSG_ID_INVALID":"MSG_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID","TOPIC_ID_INVALID":"TOPIC_ID_INVALID"},"messages.getDocumentByHash":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","SHA256_HASH_INVALID":"SHA256_HASH_INVALID"},"messages.getEmojiGameInfo":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.getEmojiGroups":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.getEmojiKeywords":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.getEmojiKeywordsDifference":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.getEmojiKeywordsLanguages":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.getEmojiProfilePhotoGroups":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.getEmojiStatusGroups":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.getEmojiStickerGroups":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.getEmojiStickers":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.getEmojiURL":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.getExportedChatInvite":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","INVITE_HASH_EXPIRED":"INVITE_HASH_EXPIRED","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.getExtendedMedia":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.getFactCheck":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.getFavedStickers":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.getFeaturedEmojiStickers":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.getFeaturedStickers":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.getForumTopics":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_FORUM_MISSING":"CHANNEL_FORUM_MISSING","CHANNEL_INVALID":"CHANNEL_INVALID"},"messages.getForumTopicsByID":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","TOPICS_EMPTY":"TOPICS_EMPTY"},"messages.getFullChat":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHAT_ID_INVALID":"CHAT_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.getFutureChatCreatorAfterLeave":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_ID_INVALID":"CHANNEL_ID_INVALID"},"messages.getGameHighScores":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","MESSAGE_ID_INVALID":"MESSAGE_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID","USER_BOT_REQUIRED":"USER_BOT_REQUIRED"},"messages.getHistory":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_ID_INVALID":"CHAT_ID_INVALID","CHAT_NOT_MODIFIED":"CHAT_NOT_MODIFIED","FROZEN_PARTICIPANT_MISSING":"FROZEN_PARTICIPANT_MISSING","MSG_ID_INVALID":"MSG_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID","TAKEOUT_INVALID":"TAKEOUT_INVALID"},"messages.getInlineGameHighScores":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","MESSAGE_ID_INVALID":"MESSAGE_ID_INVALID","USER_BOT_REQUIRED":"USER_BOT_REQUIRED"},"messages.getMaskStickers":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.getMessageEditData":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","MESSAGE_ID_INVALID":"MESSAGE_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.getMessageReactionsList":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","MSG_ID_INVALID":"MSG_ID_INVALID"},"messages.getMessageReadParticipants":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHAT_TOO_BIG":"CHAT_TOO_BIG","MSG_ID_INVALID":"MSG_ID_INVALID","MSG_TOO_OLD":"MSG_TOO_OLD","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.getMessages":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","MESSAGE_ID_INVALID":"MESSAGE_ID_INVALID","MSG_ID_INVALID":"MSG_ID_INVALID"},"messages.getMessagesReactions":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","MSG_ID_INVALID":"MSG_ID_INVALID"},"messages.getMessagesViews":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_ID_INVALID":"CHAT_ID_INVALID","CHAT_NOT_MODIFIED":"CHAT_NOT_MODIFIED","MSG_ID_INVALID":"MSG_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.getMyStickers":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.getOldFeaturedStickers":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.getOnlines":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_ID_INVALID":"CHAT_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.getOutboxReadDate":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","MESSAGE_ID_INVALID":"MESSAGE_ID_INVALID","MESSAGE_NOT_READ_YET":"MESSAGE_NOT_READ_YET","MESSAGE_TOO_OLD":"MESSAGE_TOO_OLD","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.getPaidReactionPrivacy":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.getPeerDialogs":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","FROZEN_PARTICIPANT_MISSING":"FROZEN_PARTICIPANT_MISSING","INPUT_PEERS_EMPTY":"INPUT_PEERS_EMPTY","MSG_ID_INVALID":"MSG_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.getPeerSettings":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_MONOFORUM_UNSUPPORTED":"CHANNEL_MONOFORUM_UNSUPPORTED","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","MSG_ID_INVALID":"MSG_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.getPersonalChannelHistory":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","USER_BOT_REQUIRED":"USER_BOT_REQUIRED","USER_ID_INVALID":"USER_ID_INVALID"},"messages.getPinnedDialogs":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","FOLDER_ID_INVALID":"FOLDER_ID_INVALID"},"messages.getPinnedSavedDialogs":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.getPollResults":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","MESSAGE_ID_INVALID":"MESSAGE_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.getPollVotes":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","MSG_ID_INVALID":"MSG_ID_INVALID"},"messages.getPreparedInlineMessage":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","ID_EXPIRED":"ID_EXPIRED","ID_INVALID":"ID_INVALID"},"messages.getQuickReplies":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.getQuickReplyMessages":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","SHORTCUT_INVALID":"SHORTCUT_INVALID"},"messages.getRecentLocations":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.getRecentReactions":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.getRecentStickers":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.getReplies":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","MSG_ID_INVALID":"MSG_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID","TOPIC_ID_INVALID":"TOPIC_ID_INVALID"},"messages.getRichMessage":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","MESSAGE_ID_INVALID":"MESSAGE_ID_INVALID"},"messages.getSavedDialogs":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.getSavedDialogsByID":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.getSavedGifs":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.getSavedHistory":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","PARENT_PEER_INVALID":"PARENT_PEER_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.getSavedReactionTags":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.getScheduledHistory":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.getScheduledMessages":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.getSearchCounters":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.getSearchResultsCalendar":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","FILTER_NOT_SUPPORTED":"FILTER_NOT_SUPPORTED","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.getSearchResultsPositions":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.getSplitRanges":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.getSponsoredMessages":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","MESSAGE_ID_INVALID":"MESSAGE_ID_INVALID","MSG_ID_INVALID":"MSG_ID_INVALID"},"messages.getStickers":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","EMOTICON_EMPTY":"EMOTICON_EMPTY"},"messages.getStickerSet":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","EMOTICON_STICKERPACK_MISSING":"EMOTICON_STICKERPACK_MISSING","STICKERSET_INVALID":"STICKERSET_INVALID"},"messages.getSuggestedDialogFilters":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.getTopReactions":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.getUnreadMentions":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","MSG_ID_INVALID":"MSG_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.getUnreadPollVotes":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.getUnreadReactions":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.getWebPage":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","WC_CONVERT_URL_INVALID":"WC_CONVERT_URL_INVALID"},"messages.getWebPagePreview":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","ENTITY_BOUNDS_INVALID":"ENTITY_BOUNDS_INVALID","MESSAGE_EMPTY":"MESSAGE_EMPTY"},"messages.hideAllChatJoinRequests":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHANNELS_TOO_MUCH":"CHANNELS_TOO_MUCH","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","HIDE_REQUESTER_MISSING":"HIDE_REQUESTER_MISSING","INVITE_HASH_EXPIRED":"INVITE_HASH_EXPIRED","PEER_ID_INVALID":"PEER_ID_INVALID","USER_CHANNELS_TOO_MUCH":"USER_CHANNELS_TOO_MUCH"},"messages.hideChatJoinRequest":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHANNELS_TOO_MUCH":"CHANNELS_TOO_MUCH","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","HIDE_REQUESTER_MISSING":"HIDE_REQUESTER_MISSING","INPUT_USER_DEACTIVATED":"INPUT_USER_DEACTIVATED","MSG_ID_INVALID":"MSG_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID","USER_ALREADY_PARTICIPANT":"USER_ALREADY_PARTICIPANT","USER_CHANNELS_TOO_MUCH":"USER_CHANNELS_TOO_MUCH","USER_ID_INVALID":"USER_ID_INVALID"},"messages.hidePeerSettingsBar":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.importChatInvite":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHANNELS_TOO_MUCH":"CHANNELS_TOO_MUCH","CHAT_INVALID":"CHAT_INVALID","INVITE_HASH_EMPTY":"INVITE_HASH_EMPTY","INVITE_HASH_EXPIRED":"INVITE_HASH_EXPIRED","INVITE_HASH_INVALID":"INVITE_HASH_INVALID","INVITE_REQUEST_SENT":"INVITE_REQUEST_SENT","MSG_ID_INVALID":"MSG_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID","STARS_PAYMENT_REQUIRED":"STARS_PAYMENT_REQUIRED","USER_ALREADY_PARTICIPANT":"USER_ALREADY_PARTICIPANT","USER_CHANNELS_TOO_MUCH":"USER_CHANNELS_TOO_MUCH","USERS_TOO_MUCH":"USERS_TOO_MUCH"},"messages.initHistoryImport":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","IMPORT_FILE_INVALID":"IMPORT_FILE_INVALID","IMPORT_FORMAT_DATE_INVALID":"IMPORT_FORMAT_DATE_INVALID","IMPORT_FORMAT_UNRECOGNIZED":"IMPORT_FORMAT_UNRECOGNIZED","PEER_ID_INVALID":"PEER_ID_INVALID","USER_NOT_MUTUAL_CONTACT":"USER_NOT_MUTUAL_CONTACT"},"messages.installStickerSet":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","STICKERSET_INVALID":"STICKERSET_INVALID"},"messages.markDialogUnread":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.migrateChat":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNELS_TOO_MUCH":"CHANNELS_TOO_MUCH","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_ID_INVALID":"CHAT_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.rateTranscribedAudio":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.readDiscussion":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHAT_ID_INVALID":"CHAT_ID_INVALID","MSG_ID_INVALID":"MSG_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.readEncryptedHistory":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHAT_ID_INVALID":"CHAT_ID_INVALID","MAX_DATE_INVALID":"MAX_DATE_INVALID","MSG_WAIT_FAILED":"MSG_WAIT_FAILED"},"messages.readFeaturedStickers":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.readMentions":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","MSG_ID_INVALID":"MSG_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.readMessageContents":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.readPollVotes":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.readReactions":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.readSavedHistory":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","PARENT_PEER_INVALID":"PARENT_PEER_INVALID"},"messages.receivedMessages":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.receivedQueue":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","MAX_QTS_INVALID":"MAX_QTS_INVALID","MSG_WAIT_FAILED":"MSG_WAIT_FAILED"},"messages.reorderPinnedDialogs":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.reorderPinnedForumTopics":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID"},"messages.reorderPinnedSavedDialogs":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.reorderQuickReplies":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.reorderStickerSets":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.report":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","MESSAGE_REQUIRED":"MESSAGE_REQUIRED","OPTION_INVALID":"OPTION_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.reportEncryptedSpam":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHAT_ID_INVALID":"CHAT_ID_INVALID"},"messages.reportMessagesDelivery":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.reportMusicListen":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.reportReaction":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","MSG_ID_INVALID":"MSG_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID","USER_ID_INVALID":"USER_ID_INVALID"},"messages.reportReadMetrics":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.reportSpam":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","MSG_ID_INVALID":"MSG_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.reportSponsoredMessage":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.requestEncryption":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","DH_G_A_INVALID":"DH_G_A_INVALID","INPUT_USER_DEACTIVATED":"INPUT_USER_DEACTIVATED","RANDOM_ID_DUPLICATE":"RANDOM_ID_DUPLICATE","USER_ID_INVALID":"USER_ID_INVALID"},"messages.requestUrlAuth":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","URL_EXPIRED":"URL_EXPIRED","URL_INVALID":"URL_INVALID"},"messages.saveDefaultSendAs":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID","SEND_AS_PEER_INVALID":"SEND_AS_PEER_INVALID"},"messages.saveDraft":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","ENTITY_BOUNDS_INVALID":"ENTITY_BOUNDS_INVALID","INPUT_USER_DEACTIVATED":"INPUT_USER_DEACTIVATED","MSG_ID_INVALID":"MSG_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.saveGif":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","GIF_ID_INVALID":"GIF_ID_INVALID"},"messages.savePreparedInlineMessage":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","RESULT_ID_INVALID":"RESULT_ID_INVALID","SEND_MESSAGE_GAME_INVALID":"SEND_MESSAGE_GAME_INVALID","USER_BOT_REQUIRED":"USER_BOT_REQUIRED","USER_ID_INVALID":"USER_ID_INVALID"},"messages.saveRecentSticker":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","STICKER_ID_INVALID":"STICKER_ID_INVALID"},"messages.search":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_ID_INVALID":"CHAT_ID_INVALID","FROM_PEER_INVALID":"FROM_PEER_INVALID","INPUT_FILTER_INVALID":"INPUT_FILTER_INVALID","INPUT_USER_DEACTIVATED":"INPUT_USER_DEACTIVATED","MSG_ID_INVALID":"MSG_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID","PEER_ID_NOT_SUPPORTED":"PEER_ID_NOT_SUPPORTED","SEARCH_QUERY_EMPTY":"SEARCH_QUERY_EMPTY","TAKEOUT_INVALID":"TAKEOUT_INVALID","USER_ID_INVALID":"USER_ID_INVALID"},"messages.searchCustomEmoji":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","EMOTICON_EMPTY":"EMOTICON_EMPTY"},"messages.searchEmojiStickerSets":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.searchGlobal":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","FOLDER_ID_INVALID":"FOLDER_ID_INVALID","INPUT_FILTER_INVALID":"INPUT_FILTER_INVALID","SEARCH_QUERY_EMPTY":"SEARCH_QUERY_EMPTY"},"messages.searchSentMedia":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","FILTER_NOT_SUPPORTED":"FILTER_NOT_SUPPORTED"},"messages.searchStickers":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.searchStickerSets":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.sendBotRequestedPeer":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","MESSAGE_ID_INVALID":"MESSAGE_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.sendEncrypted":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHAT_ID_INVALID":"CHAT_ID_INVALID","DATA_INVALID":"DATA_INVALID","DATA_TOO_LONG":"DATA_TOO_LONG","ENCRYPTION_DECLINED":"ENCRYPTION_DECLINED","MSG_WAIT_FAILED":"MSG_WAIT_FAILED"},"messages.sendEncryptedFile":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHAT_ID_INVALID":"CHAT_ID_INVALID","DATA_TOO_LONG":"DATA_TOO_LONG","ENCRYPTION_DECLINED":"ENCRYPTION_DECLINED","FILE_EMTPY":"FILE_EMTPY","MD5_CHECKSUM_INVALID":"MD5_CHECKSUM_INVALID","MSG_WAIT_FAILED":"MSG_WAIT_FAILED"},"messages.sendEncryptedService":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHAT_ID_INVALID":"CHAT_ID_INVALID","DATA_INVALID":"DATA_INVALID","DATA_TOO_LONG":"DATA_TOO_LONG","ENCRYPTION_DECLINED":"ENCRYPTION_DECLINED","ENCRYPTION_ID_INVALID":"ENCRYPTION_ID_INVALID","MSG_WAIT_FAILED":"MSG_WAIT_FAILED","USER_IS_BLOCKED":"USER_IS_BLOCKED"},"messages.sendInlineBotResult":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_RESTRICTED":"CHAT_RESTRICTED","CHAT_SEND_INLINE_FORBIDDEN":"CHAT_SEND_INLINE_FORBIDDEN","ENTITY_BOUNDS_INVALID":"ENTITY_BOUNDS_INVALID","INLINE_RESULT_EXPIRED":"INLINE_RESULT_EXPIRED","INPUT_USER_DEACTIVATED":"INPUT_USER_DEACTIVATED","MEDIA_EMPTY":"MEDIA_EMPTY","MSG_ID_INVALID":"MSG_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID","QUERY_ID_EMPTY":"QUERY_ID_EMPTY","QUICK_REPLIES_TOO_MUCH":"QUICK_REPLIES_TOO_MUCH","REPLY_MESSAGES_TOO_MUCH":"REPLY_MESSAGES_TOO_MUCH","RESULT_ID_EMPTY":"RESULT_ID_EMPTY","RESULT_ID_INVALID":"RESULT_ID_INVALID","SCHEDULE_DATE_TOO_LATE":"SCHEDULE_DATE_TOO_LATE","SCHEDULE_TOO_MUCH":"SCHEDULE_TOO_MUCH","SEND_AS_PEER_INVALID":"SEND_AS_PEER_INVALID","TOPIC_DELETED":"TOPIC_DELETED","USER_BANNED_IN_CHANNEL":"USER_BANNED_IN_CHANNEL","VOICE_MESSAGES_FORBIDDEN":"VOICE_MESSAGES_FORBIDDEN","WEBPAGE_CURL_FAILED":"WEBPAGE_CURL_FAILED","WEBPAGE_MEDIA_EMPTY":"WEBPAGE_MEDIA_EMPTY","YOU_BLOCKED_USER":"YOU_BLOCKED_USER"},"messages.sendQuickReplyMessages":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.sendReaction":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN","CUSTOM_REACTIONS_TOO_MANY":"CUSTOM_REACTIONS_TOO_MANY","DOCUMENT_INVALID":"DOCUMENT_INVALID","MESSAGE_ID_INVALID":"MESSAGE_ID_INVALID","MESSAGE_NOT_MODIFIED":"MESSAGE_NOT_MODIFIED","MSG_ID_INVALID":"MSG_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID","REACTION_EMPTY":"REACTION_EMPTY","REACTION_INVALID":"REACTION_INVALID","REACTIONS_TOO_MANY":"REACTIONS_TOO_MANY","USER_BANNED_IN_CHANNEL":"USER_BANNED_IN_CHANNEL"},"messages.sendScheduledMessages":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","MESSAGE_ID_INVALID":"MESSAGE_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.sendScreenshotNotification":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","INPUT_USER_DEACTIVATED":"INPUT_USER_DEACTIVATED","PEER_ID_INVALID":"PEER_ID_INVALID","REPLY_MESSAGE_ID_INVALID":"REPLY_MESSAGE_ID_INVALID","STORY_ID_INVALID":"STORY_ID_INVALID","YOU_BLOCKED_USER":"YOU_BLOCKED_USER"},"messages.sendVote":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","MESSAGE_ID_INVALID":"MESSAGE_ID_INVALID","MESSAGE_POLL_CLOSED":"MESSAGE_POLL_CLOSED","MSG_ID_INVALID":"MSG_ID_INVALID","OPTION_INVALID":"OPTION_INVALID","OPTIONS_TOO_MUCH":"OPTIONS_TOO_MUCH","PEER_ID_INVALID":"PEER_ID_INVALID","REVOTE_NOT_ALLOWED":"REVOTE_NOT_ALLOWED"},"messages.sendWebViewResultMessage":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","QUERY_ID_INVALID":"QUERY_ID_INVALID","USER_BOT_REQUIRED":"USER_BOT_REQUIRED"},"messages.setBotCallbackAnswer":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","MESSAGE_TOO_LONG":"MESSAGE_TOO_LONG","QUERY_ID_INVALID":"QUERY_ID_INVALID","URL_INVALID":"URL_INVALID","USER_BOT_REQUIRED":"USER_BOT_REQUIRED"},"messages.setBotGuestChatResult":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","USER_BOT_REQUIRED":"USER_BOT_REQUIRED"},"messages.setBotPrecheckoutResults":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","ERROR_TEXT_EMPTY":"ERROR_TEXT_EMPTY","USER_BOT_REQUIRED":"USER_BOT_REQUIRED"},"messages.setBotShippingResults":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","QUERY_ID_INVALID":"QUERY_ID_INVALID","USER_BOT_REQUIRED":"USER_BOT_REQUIRED"},"messages.setChatAvailableReactions":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_NOT_MODIFIED":"CHAT_NOT_MODIFIED","DOCUMENT_INVALID":"DOCUMENT_INVALID","LIMIT_PER_POST_INVALID":"LIMIT_PER_POST_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID","REACTION_INVALID":"REACTION_INVALID"},"messages.setChatTheme":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","EMOJI_INVALID":"EMOJI_INVALID","EMOJI_NOT_MODIFIED":"EMOJI_NOT_MODIFIED","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.setChatWallPaper":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID","WALLPAPER_INVALID":"WALLPAPER_INVALID","WALLPAPER_NOT_FOUND":"WALLPAPER_NOT_FOUND"},"messages.setDefaultHistoryTTL":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","TTL_PERIOD_INVALID":"TTL_PERIOD_INVALID"},"messages.setDefaultReaction":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","REACTION_INVALID":"REACTION_INVALID"},"messages.setEncryptedTyping":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHAT_ID_INVALID":"CHAT_ID_INVALID"},"messages.setHistoryTTL":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_NOT_MODIFIED":"CHAT_NOT_MODIFIED","PEER_ID_INVALID":"PEER_ID_INVALID","TTL_PERIOD_INVALID":"TTL_PERIOD_INVALID"},"messages.setInlineGameScore":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","MESSAGE_ID_INVALID":"MESSAGE_ID_INVALID","USER_BOT_REQUIRED":"USER_BOT_REQUIRED"},"messages.startHistoryImport":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","IMPORT_ID_INVALID":"IMPORT_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.summarizeText":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","INPUT_TEXT_TOO_LONG":"INPUT_TEXT_TOO_LONG","MSG_ID_INVALID":"MSG_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.toggleDialogFilterTags":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.toggleDialogPin":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","PEER_HISTORY_EMPTY":"PEER_HISTORY_EMPTY","PEER_ID_INVALID":"PEER_ID_INVALID","PINNED_DIALOGS_TOO_MUCH":"PINNED_DIALOGS_TOO_MUCH"},"messages.toggleNoForwards":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_NOT_MODIFIED":"CHAT_NOT_MODIFIED","PEER_ID_INVALID":"PEER_ID_INVALID","REQUEST_MSG_EXPIRED":"REQUEST_MSG_EXPIRED"},"messages.togglePaidReactionPrivacy":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID","REACTION_EMPTY":"REACTION_EMPTY"},"messages.togglePeerTranslations":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.toggleSavedDialogPin":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.toggleStickerSets":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.toggleSuggestedPostApproval":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.toggleTodoCompleted":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.transcribeAudio":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","MSG_ID_INVALID":"MSG_ID_INVALID","MSG_VOICE_MISSING":"MSG_VOICE_MISSING","MSG_VOICE_TOO_LONG":"MSG_VOICE_TOO_LONG","PEER_ID_INVALID":"PEER_ID_INVALID","TRANSCRIPTION_FAILED":"TRANSCRIPTION_FAILED"},"messages.translateText":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","INPUT_TEXT_EMPTY":"INPUT_TEXT_EMPTY","INPUT_TEXT_TOO_LONG":"INPUT_TEXT_TOO_LONG","MSG_ID_INVALID":"MSG_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID","TO_LANG_INVALID":"TO_LANG_INVALID","TRANSLATE_REQ_QUOTA_EXCEEDED":"TRANSLATE_REQ_QUOTA_EXCEEDED"},"messages.uninstallStickerSet":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","STICKERSET_INVALID":"STICKERSET_INVALID"},"messages.unpinAllMessages":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_NOT_MODIFIED":"CHAT_NOT_MODIFIED","INPUT_USER_DEACTIVATED":"INPUT_USER_DEACTIVATED","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.updateDialogFilter":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHAT_ID_INVALID":"CHAT_ID_INVALID","CHATLIST_EXCLUDE_INVALID":"CHATLIST_EXCLUDE_INVALID","FILTER_ID_INVALID":"FILTER_ID_INVALID","FILTER_INCLUDE_EMPTY":"FILTER_INCLUDE_EMPTY","FILTER_TITLE_EMPTY":"FILTER_TITLE_EMPTY","MESSAGE_TOO_LONG":"MESSAGE_TOO_LONG","MSG_ID_INVALID":"MSG_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.updateDialogFiltersOrder":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"messages.updatePinnedForumTopic":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID"},"messages.updateSavedReactionTag":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","REACTION_INVALID":"REACTION_INVALID"},"messages.uploadEncryptedFile":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHAT_ID_INVALID":"CHAT_ID_INVALID"},"messages.uploadImportedMedia":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","IMPORT_ID_INVALID":"IMPORT_ID_INVALID","MEDIA_INVALID":"MEDIA_INVALID"},"messages.uploadMedia":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_ID_INVALID":"CHAT_ID_INVALID","CHAT_RESTRICTED":"CHAT_RESTRICTED","FILE_PART_LENGTH_INVALID":"FILE_PART_LENGTH_INVALID","FILE_PARTS_INVALID":"FILE_PARTS_INVALID","IMAGE_PROCESS_FAILED":"IMAGE_PROCESS_FAILED","INPUT_USER_DEACTIVATED":"INPUT_USER_DEACTIVATED","MEDIA_INVALID":"MEDIA_INVALID","MSG_ID_INVALID":"MSG_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID","PHOTO_EXT_INVALID":"PHOTO_EXT_INVALID","PHOTO_INVALID_DIMENSIONS":"PHOTO_INVALID_DIMENSIONS","PHOTO_SAVE_FILE_INVALID":"PHOTO_SAVE_FILE_INVALID","USER_BANNED_IN_CHANNEL":"USER_BANNED_IN_CHANNEL","VOICE_MESSAGES_FORBIDDEN":"VOICE_MESSAGES_FORBIDDEN","WEBPAGE_CURL_FAILED":"WEBPAGE_CURL_FAILED"},"messages.viewSponsoredMessage":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"payments.applyGiftCode":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","GIFT_SLUG_EXPIRED":"GIFT_SLUG_EXPIRED","GIFT_SLUG_INVALID":"GIFT_SLUG_INVALID"},"payments.assignAppStoreTransaction":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","INPUT_PURPOSE_INVALID":"INPUT_PURPOSE_INVALID","RECEIPT_EMPTY":"RECEIPT_EMPTY"},"payments.assignPlayMarketTransaction":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","DATA_JSON_INVALID":"DATA_JSON_INVALID"},"payments.botCancelStarsSubscription":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHARGE_ID_INVALID":"CHARGE_ID_INVALID","USER_ID_INVALID":"USER_ID_INVALID"},"payments.canPurchaseStore":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","INPUT_PURPOSE_INVALID":"INPUT_PURPOSE_INVALID"},"payments.changeStarsSubscription":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"payments.checkCanSendGift":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","STARGIFT_INVALID":"STARGIFT_INVALID"},"payments.checkGiftCode":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","GIFT_SLUG_EXPIRED":"GIFT_SLUG_EXPIRED","GIFT_SLUG_INVALID":"GIFT_SLUG_INVALID"},"payments.clearSavedInfo":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"payments.connectStarRefBot":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"payments.craftStarGift":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","SAVED_ID_EMPTY":"SAVED_ID_EMPTY"},"payments.createStarGiftCollection":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"payments.deleteStarGiftCollection":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"payments.editConnectedStarRefBot":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","STARREF_HASH_REVOKED":"STARREF_HASH_REVOKED"},"payments.fulfillStarsSubscription":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"payments.getConnectedStarRefBot":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"payments.getConnectedStarRefBots":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"payments.getCraftStarGifts":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","STARGIFT_INVALID":"STARGIFT_INVALID"},"payments.getGiveawayInfo":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","MESSAGE_ID_INVALID":"MESSAGE_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID"},"payments.getPaymentReceipt":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","MESSAGE_ID_INVALID":"MESSAGE_ID_INVALID"},"payments.getPremiumGiftCodeOptions":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"payments.getResaleStarGifts":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","STARGIFT_ATTRIBUTE_INVALID":"STARGIFT_ATTRIBUTE_INVALID","STARGIFT_INVALID":"STARGIFT_INVALID"},"payments.getSavedInfo":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"payments.getSavedStarGift":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","SAVED_ID_EMPTY":"SAVED_ID_EMPTY","STARGIFT_OWNER_INVALID":"STARGIFT_OWNER_INVALID","STARGIFT_SLUG_INVALID":"STARGIFT_SLUG_INVALID"},"payments.getStarGiftActiveAuctions":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"payments.getStarGiftAuctionAcquiredGifts":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","STARGIFT_INVALID":"STARGIFT_INVALID"},"payments.getStarGiftAuctionState":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","STARGIFT_INVALID":"STARGIFT_INVALID"},"payments.getStarGiftCollections":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"payments.getStarGifts":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"payments.getStarGiftUpgradeAttributes":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","STARGIFT_INVALID":"STARGIFT_INVALID"},"payments.getStarGiftUpgradePreview":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","STARGIFT_INVALID":"STARGIFT_INVALID","STARGIFT_UPGRADE_UNAVAILABLE":"STARGIFT_UPGRADE_UNAVAILABLE"},"payments.getStarGiftWithdrawalUrl":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PASSWORD_HASH_INVALID":"PASSWORD_HASH_INVALID","PASSWORD_TOO_FRESH_%d":"PASSWORD_TOO_FRESH_%d","SESSION_TOO_FRESH_%d":"SESSION_TOO_FRESH_%d"},"payments.getStarsGiftOptions":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","INPUT_USER_DEACTIVATED":"INPUT_USER_DEACTIVATED","USER_GIFT_UNAVAILABLE":"USER_GIFT_UNAVAILABLE","USER_ID_INVALID":"USER_ID_INVALID"},"payments.getStarsGiveawayOptions":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"payments.getStarsRevenueAdsAccountUrl":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"payments.getStarsRevenueStats":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","PEER_ID_INVALID":"PEER_ID_INVALID"},"payments.getStarsRevenueWithdrawalUrl":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PASSWORD_HASH_INVALID":"PASSWORD_HASH_INVALID","PASSWORD_MISSING":"PASSWORD_MISSING","PASSWORD_TOO_FRESH_%d":"PASSWORD_TOO_FRESH_%d","SESSION_TOO_FRESH_%d":"SESSION_TOO_FRESH_%d"},"payments.getStarsSubscriptions":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"payments.getStarsTopupOptions":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"payments.getStarsTransactions":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","PEER_ID_INVALID":"PEER_ID_INVALID","SUBSCRIPTION_ID_INVALID":"SUBSCRIPTION_ID_INVALID"},"payments.getStarsTransactionsByID":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID","TRANSACTION_ID_INVALID":"TRANSACTION_ID_INVALID"},"payments.getSuggestedStarRefBots":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"payments.getUniqueStarGift":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","STARGIFT_SLUG_INVALID":"STARGIFT_SLUG_INVALID"},"payments.getUniqueStarGiftValueInfo":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","STARGIFT_SLUG_INVALID":"STARGIFT_SLUG_INVALID"},"payments.launchPrepaidGiveaway":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"payments.refundStarsCharge":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHARGE_ALREADY_REFUNDED":"CHARGE_ALREADY_REFUNDED","CHARGE_ID_EMPTY":"CHARGE_ID_EMPTY","USER_BOT_REQUIRED":"USER_BOT_REQUIRED","USER_ID_INVALID":"USER_ID_INVALID"},"payments.reorderStarGiftCollections":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"payments.saveStarGift":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","MESSAGE_ID_INVALID":"MESSAGE_ID_INVALID","SAVED_ID_EMPTY":"SAVED_ID_EMPTY","STARGIFT_OBJECT_INVALID":"STARGIFT_OBJECT_INVALID","STARGIFT_OWNER_INVALID":"STARGIFT_OWNER_INVALID","USER_ID_INVALID":"USER_ID_INVALID"},"payments.sendPaymentForm":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","FORM_UNSUPPORTED":"FORM_UNSUPPORTED","INVOICE_INVALID":"INVOICE_INVALID","MESSAGE_ID_INVALID":"MESSAGE_ID_INVALID","PAYMENT_CREDENTIALS_INVALID":"PAYMENT_CREDENTIALS_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID","TMP_PASSWORD_INVALID":"TMP_PASSWORD_INVALID"},"payments.sendStarGiftOffer":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","INPUT_STARS_AMOUNT_INVALID":"INPUT_STARS_AMOUNT_INVALID","INPUT_STARS_NANOS_INVALID":"INPUT_STARS_NANOS_INVALID","INVOICE_INVALID":"INVOICE_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID","RESELL_STARS_TOO_FEW":"RESELL_STARS_TOO_FEW","RESELL_STARS_TOO_MUCH":"RESELL_STARS_TOO_MUCH","STARGIFT_OFFER_INVALID":"STARGIFT_OFFER_INVALID","STARGIFT_OFFER_NOT_ALLOWED":"STARGIFT_OFFER_NOT_ALLOWED","STARGIFT_SLUG_INVALID":"STARGIFT_SLUG_INVALID"},"payments.toggleChatStarGiftNotifications":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"payments.toggleStarGiftsPinnedToTop":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"payments.updateStarGiftCollection":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"payments.updateStarGiftPrice":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","SAVED_ID_EMPTY":"SAVED_ID_EMPTY","STARGIFT_NOT_FOUND":"STARGIFT_NOT_FOUND"},"payments.validateRequestedInfo":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","MESSAGE_ID_INVALID":"MESSAGE_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID"},"phone.acceptCall":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CALL_ALREADY_ACCEPTED":"CALL_ALREADY_ACCEPTED","CALL_ALREADY_DECLINED":"CALL_ALREADY_DECLINED","CALL_PEER_INVALID":"CALL_PEER_INVALID","CALL_PROTOCOL_FLAGS_INVALID":"CALL_PROTOCOL_FLAGS_INVALID","CALL_PROTOCOL_LAYER_INVALID":"CALL_PROTOCOL_LAYER_INVALID"},"phone.checkGroupCall":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","GROUPCALL_INVALID":"GROUPCALL_INVALID","GROUPCALL_JOIN_MISSING":"GROUPCALL_JOIN_MISSING"},"phone.confirmCall":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CALL_ALREADY_DECLINED":"CALL_ALREADY_DECLINED","CALL_PEER_INVALID":"CALL_PEER_INVALID"},"phone.createConferenceCall":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"phone.createGroupCall":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CREATE_CALL_FAILED":"CREATE_CALL_FAILED","GROUPCALL_ALREADY_DISCARDED":"GROUPCALL_ALREADY_DISCARDED","PEER_ID_INVALID":"PEER_ID_INVALID","SCHEDULE_DATE_INVALID":"SCHEDULE_DATE_INVALID"},"phone.declineConferenceCallInvite":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","MESSAGE_ID_INVALID":"MESSAGE_ID_INVALID"},"phone.deleteConferenceCallParticipants":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","GROUPCALL_INVALID":"GROUPCALL_INVALID"},"phone.deleteGroupCallMessages":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","GROUPCALL_INVALID":"GROUPCALL_INVALID"},"phone.deleteGroupCallParticipantMessages":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","GROUPCALL_INVALID":"GROUPCALL_INVALID"},"phone.discardCall":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CALL_ALREADY_ACCEPTED":"CALL_ALREADY_ACCEPTED","CALL_OCCUPY_FAILED":"CALL_OCCUPY_FAILED","CALL_PEER_INVALID":"CALL_PEER_INVALID"},"phone.discardGroupCall":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","GROUPCALL_ALREADY_DISCARDED":"GROUPCALL_ALREADY_DISCARDED","GROUPCALL_INVALID":"GROUPCALL_INVALID"},"phone.editGroupCallParticipant":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","GROUPCALL_FORBIDDEN":"GROUPCALL_FORBIDDEN","GROUPCALL_INVALID":"GROUPCALL_INVALID","PARTICIPANT_JOIN_MISSING":"PARTICIPANT_JOIN_MISSING","RAISE_HAND_FORBIDDEN":"RAISE_HAND_FORBIDDEN","USER_ID_INVALID":"USER_ID_INVALID","USER_VOLUME_INVALID":"USER_VOLUME_INVALID","VIDEO_PAUSE_FORBIDDEN":"VIDEO_PAUSE_FORBIDDEN","VIDEO_STOP_FORBIDDEN":"VIDEO_STOP_FORBIDDEN"},"phone.editGroupCallTitle":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","GROUPCALL_INVALID":"GROUPCALL_INVALID"},"phone.exportGroupCallInvite":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","GROUPCALL_INVALID":"GROUPCALL_INVALID"},"phone.getCallConfig":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"phone.getGroupCall":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","GROUPCALL_INVALID":"GROUPCALL_INVALID"},"phone.getGroupCallChainBlocks":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","GROUPCALL_INVALID":"GROUPCALL_INVALID"},"phone.getGroupCallJoinAs":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID"},"phone.getGroupCallStars":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","GROUPCALL_INVALID":"GROUPCALL_INVALID"},"phone.getGroupCallStreamChannels":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","GROUPCALL_INVALID":"GROUPCALL_INVALID","GROUPCALL_JOIN_MISSING":"GROUPCALL_JOIN_MISSING"},"phone.getGroupCallStreamRtmpUrl":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","PEER_ID_INVALID":"PEER_ID_INVALID"},"phone.getGroupParticipants":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","GROUPCALL_INVALID":"GROUPCALL_INVALID"},"phone.inviteConferenceCallParticipant":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","GROUPCALL_INVALID":"GROUPCALL_INVALID"},"phone.inviteToGroupCall":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","GROUPCALL_INVALID":"GROUPCALL_INVALID","INVITE_FORBIDDEN_WITH_JOINAS":"INVITE_FORBIDDEN_WITH_JOINAS","USER_ALREADY_INVITED":"USER_ALREADY_INVITED"},"phone.joinGroupCall":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","DATA_JSON_INVALID":"DATA_JSON_INVALID","GROUPCALL_INVALID":"GROUPCALL_INVALID","GROUPCALL_SSRC_DUPLICATE_MUCH":"GROUPCALL_SSRC_DUPLICATE_MUCH","JOIN_AS_PEER_INVALID":"JOIN_AS_PEER_INVALID","PUBLIC_KEY_INVALID":"PUBLIC_KEY_INVALID"},"phone.joinGroupCallPresentation":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","GROUPCALL_INVALID":"GROUPCALL_INVALID","PARTICIPANT_JOIN_MISSING":"PARTICIPANT_JOIN_MISSING"},"phone.leaveGroupCall":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","GROUPCALL_INVALID":"GROUPCALL_INVALID"},"phone.leaveGroupCallPresentation":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","GROUPCALL_INVALID":"GROUPCALL_INVALID"},"phone.receivedCall":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CALL_ALREADY_DECLINED":"CALL_ALREADY_DECLINED","CALL_PEER_INVALID":"CALL_PEER_INVALID"},"phone.requestCall":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CALL_PROTOCOL_FLAGS_INVALID":"CALL_PROTOCOL_FLAGS_INVALID","CALL_PROTOCOL_LAYER_INVALID":"CALL_PROTOCOL_LAYER_INVALID","INPUT_USER_DEACTIVATED":"INPUT_USER_DEACTIVATED","PARTICIPANT_VERSION_OUTDATED":"PARTICIPANT_VERSION_OUTDATED","USER_ID_INVALID":"USER_ID_INVALID","USER_IS_BLOCKED":"USER_IS_BLOCKED"},"phone.saveCallDebug":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CALL_PEER_INVALID":"CALL_PEER_INVALID","DATA_JSON_INVALID":"DATA_JSON_INVALID"},"phone.saveCallLog":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CALL_PEER_INVALID":"CALL_PEER_INVALID"},"phone.saveDefaultGroupCallJoinAs":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","JOIN_AS_PEER_INVALID":"JOIN_AS_PEER_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID"},"phone.saveDefaultSendAs":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","GROUPCALL_INVALID":"GROUPCALL_INVALID"},"phone.sendConferenceCallBroadcast":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","GROUPCALL_INVALID":"GROUPCALL_INVALID"},"phone.sendGroupCallEncryptedMessage":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","GROUPCALL_INVALID":"GROUPCALL_INVALID"},"phone.sendGroupCallMessage":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","GROUPCALL_INVALID":"GROUPCALL_INVALID","GROUPCALL_JOIN_MISSING":"GROUPCALL_JOIN_MISSING"},"phone.sendSignalingData":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CALL_NOT_ACTIVE":"CALL_NOT_ACTIVE","CALL_PEER_INVALID":"CALL_PEER_INVALID"},"phone.setCallRating":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CALL_PEER_INVALID":"CALL_PEER_INVALID"},"phone.startScheduledGroupCall":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","GROUPCALL_INVALID":"GROUPCALL_INVALID"},"phone.toggleGroupCallRecord":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","GROUPCALL_INVALID":"GROUPCALL_INVALID","GROUPCALL_NOT_MODIFIED":"GROUPCALL_NOT_MODIFIED"},"phone.toggleGroupCallSettings":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","GROUPCALL_INVALID":"GROUPCALL_INVALID","GROUPCALL_NOT_MODIFIED":"GROUPCALL_NOT_MODIFIED"},"phone.toggleGroupCallStartSubscription":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","GROUPCALL_INVALID":"GROUPCALL_INVALID"},"photos.deletePhotos":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"photos.getUserPhotos":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","MAX_ID_INVALID":"MAX_ID_INVALID","MSG_ID_INVALID":"MSG_ID_INVALID","USER_ID_INVALID":"USER_ID_INVALID"},"photos.uploadContactProfilePhoto":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CONTACT_MISSING":"CONTACT_MISSING","NEED_ACTION_MISSING":"NEED_ACTION_MISSING","USER_ID_INVALID":"USER_ID_INVALID"},"premium.getBoostsList":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","PEER_ID_INVALID":"PEER_ID_INVALID"},"premium.getBoostsStatus":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","PEER_ID_INVALID":"PEER_ID_INVALID"},"premium.getMyBoosts":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"premium.getUserBoosts":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"smsjobs.finishJob":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","SMSJOB_ID_INVALID":"SMSJOB_ID_INVALID"},"smsjobs.getSmsJob":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","SMSJOB_ID_INVALID":"SMSJOB_ID_INVALID"},"smsjobs.getStatus":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","NOT_JOINED":"NOT_JOINED"},"smsjobs.isEligibleToJoin":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"smsjobs.join":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","NOT_ELIGIBLE":"NOT_ELIGIBLE"},"smsjobs.leave":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","NOT_JOINED":"NOT_JOINED"},"smsjobs.updateSettings":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","NOT_JOINED":"NOT_JOINED"},"stats.getMegagroupStats":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","MEGAGROUP_REQUIRED":"MEGAGROUP_REQUIRED"},"stats.getMessagePublicForwards":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","MESSAGE_ID_INVALID":"MESSAGE_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID"},"stats.getMessageStats":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","MESSAGE_ID_INVALID":"MESSAGE_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID"},"stats.getPollStats":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"stats.getStoryPublicForwards":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"stats.getStoryStats":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID","STORIES_NEVER_CREATED":"STORIES_NEVER_CREATED"},"stats.loadAsyncGraph":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","GRAPH_EXPIRED_RELOAD":"GRAPH_EXPIRED_RELOAD","GRAPH_INVALID_RELOAD":"GRAPH_INVALID_RELOAD","GRAPH_OUTDATED_RELOAD":"GRAPH_OUTDATED_RELOAD"},"stickers.addStickerToSet":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","STICKER_PNG_NOPNG":"STICKER_PNG_NOPNG","STICKER_TGS_NOTGS":"STICKER_TGS_NOTGS","STICKERPACK_STICKERS_TOO_MUCH":"STICKERPACK_STICKERS_TOO_MUCH","STICKERS_TOO_MUCH":"STICKERS_TOO_MUCH","STICKERSET_INVALID":"STICKERSET_INVALID"},"stickers.changeSticker":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","STICKER_INVALID":"STICKER_INVALID"},"stickers.changeStickerPosition":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","STICKER_INVALID":"STICKER_INVALID"},"stickers.checkShortName":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","SHORT_NAME_INVALID":"SHORT_NAME_INVALID","SHORT_NAME_OCCUPIED":"SHORT_NAME_OCCUPIED"},"stickers.createStickerSet":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PACK_SHORT_NAME_INVALID":"PACK_SHORT_NAME_INVALID","PACK_SHORT_NAME_OCCUPIED":"PACK_SHORT_NAME_OCCUPIED","PACK_TITLE_INVALID":"PACK_TITLE_INVALID","PACK_TYPE_INVALID":"PACK_TYPE_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID","STICKER_EMOJI_INVALID":"STICKER_EMOJI_INVALID","STICKER_FILE_INVALID":"STICKER_FILE_INVALID","STICKER_GIF_DIMENSIONS":"STICKER_GIF_DIMENSIONS","STICKER_PNG_DIMENSIONS":"STICKER_PNG_DIMENSIONS","STICKER_PNG_NOPNG":"STICKER_PNG_NOPNG","STICKER_TGS_NODOC":"STICKER_TGS_NODOC","STICKER_TGS_NOTGS":"STICKER_TGS_NOTGS","STICKER_THUMB_PNG_NOPNG":"STICKER_THUMB_PNG_NOPNG","STICKER_THUMB_TGS_NOTGS":"STICKER_THUMB_TGS_NOTGS","STICKER_VIDEO_BIG":"STICKER_VIDEO_BIG","STICKER_VIDEO_NODOC":"STICKER_VIDEO_NODOC","STICKER_VIDEO_NOWEBM":"STICKER_VIDEO_NOWEBM","STICKERS_EMPTY":"STICKERS_EMPTY","USER_ID_INVALID":"USER_ID_INVALID"},"stickers.deleteStickerSet":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","STICKERSET_INVALID":"STICKERSET_INVALID"},"stickers.removeStickerFromSet":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","STICKER_INVALID":"STICKER_INVALID"},"stickers.renameStickerSet":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","STICKERSET_INVALID":"STICKERSET_INVALID"},"stickers.replaceSticker":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","STICKER_INVALID":"STICKER_INVALID"},"stickers.setStickerSetThumb":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","STICKER_THUMB_PNG_NOPNG":"STICKER_THUMB_PNG_NOPNG","STICKER_THUMB_TGS_NOTGS":"STICKER_THUMB_TGS_NOTGS","STICKERSET_INVALID":"STICKERSET_INVALID"},"stickers.suggestShortName":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","TITLE_INVALID":"TITLE_INVALID"},"stories.activateStealthMode":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PREMIUM_ACCOUNT_REQUIRED":"PREMIUM_ACCOUNT_REQUIRED"},"stories.createAlbum":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"stories.deleteAlbum":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"stories.editStory":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID","STORY_NOT_MODIFIED":"STORY_NOT_MODIFIED"},"stories.exportStoryLink":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID","STORY_ID_EMPTY":"STORY_ID_EMPTY","USER_PUBLIC_MISSING":"USER_PUBLIC_MISSING"},"stories.getAlbums":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"stories.getAlbumStories":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"stories.getAllReadPeerStories":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"stories.getAllStories":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"stories.getChatsToSend":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"stories.getPeerMaxIDs":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"stories.getPeerStories":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","MSG_ID_INVALID":"MSG_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID"},"stories.getPinnedStories":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID","USER_ID_INVALID":"USER_ID_INVALID"},"stories.getStoriesArchive":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","PEER_ID_INVALID":"PEER_ID_INVALID"},"stories.getStoriesByID":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","MSG_ID_INVALID":"MSG_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID","STORIES_NEVER_CREATED":"STORIES_NEVER_CREATED","STORY_ID_EMPTY":"STORY_ID_EMPTY"},"stories.getStoriesViews":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","PEER_ID_INVALID":"PEER_ID_INVALID","STORY_ID_EMPTY":"STORY_ID_EMPTY"},"stories.getStoryReactionsList":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"stories.getStoryViewsList":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID","STORY_ID_INVALID":"STORY_ID_INVALID"},"stories.incrementStoryViews":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID","STORY_ID_EMPTY":"STORY_ID_EMPTY"},"stories.readStories":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","MAX_ID_INVALID":"MAX_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID","STORIES_NEVER_CREATED":"STORIES_NEVER_CREATED"},"stories.reorderAlbums":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"stories.report":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"stories.searchPosts":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","HASHTAG_INVALID":"HASHTAG_INVALID"},"stories.sendReaction":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID","REACTION_INVALID":"REACTION_INVALID","STORIES_NEVER_CREATED":"STORIES_NEVER_CREATED","STORY_ID_EMPTY":"STORY_ID_EMPTY","STORY_ID_INVALID":"STORY_ID_INVALID"},"stories.startLive":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_MONOFORUM_UNSUPPORTED":"CHANNEL_MONOFORUM_UNSUPPORTED","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","PEER_ID_INVALID":"PEER_ID_INVALID","STORY_LIVE_ALREADY_%d":"STORY_LIVE_ALREADY_%d"},"stories.toggleAllStoriesHidden":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"stories.togglePeerStoriesHidden":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"stories.togglePinned":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"stories.togglePinnedToTop":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID","STORY_ID_INVALID":"STORY_ID_INVALID"},"stories.updateAlbum":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","PEER_ID_INVALID":"PEER_ID_INVALID"},"updates.getChannelDifference":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_NOT_MODIFIED":"CHAT_NOT_MODIFIED","FROM_MESSAGE_BOT_DISABLED":"FROM_MESSAGE_BOT_DISABLED","FROZEN_PARTICIPANT_MISSING":"FROZEN_PARTICIPANT_MISSING","MSG_ID_INVALID":"MSG_ID_INVALID","PERSISTENT_TIMESTAMP_EMPTY":"PERSISTENT_TIMESTAMP_EMPTY","PERSISTENT_TIMESTAMP_INVALID":"PERSISTENT_TIMESTAMP_INVALID","PINNED_DIALOGS_TOO_MUCH":"PINNED_DIALOGS_TOO_MUCH","RANGES_INVALID":"RANGES_INVALID","USER_BANNED_IN_CHANNEL":"USER_BANNED_IN_CHANNEL"},"updates.getDifference":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CDN_METHOD_INVALID":"CDN_METHOD_INVALID","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_NOT_MODIFIED":"CHAT_NOT_MODIFIED","DATE_EMPTY":"DATE_EMPTY","MSG_ID_INVALID":"MSG_ID_INVALID","PERSISTENT_TIMESTAMP_EMPTY":"PERSISTENT_TIMESTAMP_EMPTY","PERSISTENT_TIMESTAMP_INVALID":"PERSISTENT_TIMESTAMP_INVALID","USER_NOT_PARTICIPANT":"USER_NOT_PARTICIPANT","USERNAME_INVALID":"USERNAME_INVALID"},"updates.getState":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"upload.getCdnFile":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","FILE_TOKEN_INVALID":"FILE_TOKEN_INVALID"},"upload.getCdnFileHashes":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CDN_METHOD_INVALID":"CDN_METHOD_INVALID","FILE_TOKEN_INVALID":"FILE_TOKEN_INVALID","RSA_DECRYPT_FAILED":"RSA_DECRYPT_FAILED"},"upload.getFile":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CDN_METHOD_INVALID":"CDN_METHOD_INVALID","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","FILE_ID_INVALID":"FILE_ID_INVALID","FILE_REFERENCE_EMPTY":"FILE_REFERENCE_EMPTY","FILE_REFERENCE_EXPIRED":"FILE_REFERENCE_EXPIRED","FILE_REFERENCE_INVALID":"FILE_REFERENCE_INVALID","LIMIT_INVALID":"LIMIT_INVALID","LOCATION_INVALID":"LOCATION_INVALID","MSG_ID_INVALID":"MSG_ID_INVALID","OFFSET_INVALID":"OFFSET_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID"},"upload.getFileHashes":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","LOCATION_INVALID":"LOCATION_INVALID"},"upload.getWebFile":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","DOCUMENT_INVALID":"DOCUMENT_INVALID","LOCATION_INVALID":"LOCATION_INVALID"},"upload.reuploadCdnFile":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CDN_METHOD_INVALID":"CDN_METHOD_INVALID","FILE_TOKEN_INVALID":"FILE_TOKEN_INVALID","LOCATION_INVALID":"LOCATION_INVALID","REQUEST_TOKEN_INVALID":"REQUEST_TOKEN_INVALID","RSA_DECRYPT_FAILED":"RSA_DECRYPT_FAILED"},"upload.saveBigFilePart":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","FILE_PART_EMPTY":"FILE_PART_EMPTY","FILE_PART_INVALID":"FILE_PART_INVALID","FILE_PART_SIZE_CHANGED":"FILE_PART_SIZE_CHANGED","FILE_PART_SIZE_INVALID":"FILE_PART_SIZE_INVALID","FILE_PART_TOO_BIG":"FILE_PART_TOO_BIG","FILE_PART_TOO_SMALL":"FILE_PART_TOO_SMALL","FILE_PARTS_INVALID":"FILE_PARTS_INVALID"},"upload.saveFilePart":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","FILE_PART_EMPTY":"FILE_PART_EMPTY","FILE_PART_INVALID":"FILE_PART_INVALID","MSG_ID_INVALID":"MSG_ID_INVALID"},"users.getFullUser":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","MSG_ID_INVALID":"MSG_ID_INVALID","USER_ID_INVALID":"USER_ID_INVALID","USERNAME_OCCUPIED":"USERNAME_OCCUPIED"},"users.getRequirementsToContact":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED"},"users.getSavedMusic":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","USER_ID_INVALID":"USER_ID_INVALID"},"users.getSavedMusicByID":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","USER_ID_INVALID":"USER_ID_INVALID"},"users.getUsers":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_MONOFORUM_UNSUPPORTED":"CHANNEL_MONOFORUM_UNSUPPORTED","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","FROM_MESSAGE_BOT_DISABLED":"FROM_MESSAGE_BOT_DISABLED","MSG_ID_INVALID":"MSG_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID","USER_BANNED_IN_CHANNEL":"USER_BANNED_IN_CHANNEL"},"users.setSecureValueErrors":{"BUSINESS_CONNECTION_NOT_ALLOWED":"BUSINESS_CONNECTION_NOT_ALLOWED","DATA_HASH_SIZE_INVALID":"DATA_HASH_SIZE_INVALID","HASH_SIZE_INVALID":"HASH_SIZE_INVALID","USER_BOT_REQUIRED":"USER_BOT_REQUIRED","USER_ID_INVALID":"USER_ID_INVALID"},"channels.clickSponsoredMessage":{"CHANNEL_INVALID":"CHANNEL_INVALID"},"channels.deleteUserHistory":{"CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","MSG_ID_INVALID":"MSG_ID_INVALID","USER_ID_INVALID":"USER_ID_INVALID"},"channels.editAbout":{"CHANNEL_INVALID":"CHANNEL_INVALID","CHAT_ABOUT_NOT_MODIFIED":"CHAT_ABOUT_NOT_MODIFIED","CHAT_ABOUT_TOO_LONG":"CHAT_ABOUT_TOO_LONG","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED"},"channels.exportInvite":{"CHANNEL_INVALID":"CHANNEL_INVALID","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","INVITE_HASH_EXPIRED":"INVITE_HASH_EXPIRED"},"channels.getSponsoredMessages":{"CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_NOT_MODIFIED":"CHAT_NOT_MODIFIED","MSG_ID_INVALID":"MSG_ID_INVALID"},"channels.toggleInvites":{"CHANNEL_INVALID":"CHANNEL_INVALID","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_NOT_MODIFIED":"CHAT_NOT_MODIFIED"},"channels.updatePinnedMessage":{"CHANNEL_INVALID":"CHANNEL_INVALID","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_ID_INVALID":"CHAT_ID_INVALID","CHAT_NOT_MODIFIED":"CHAT_NOT_MODIFIED"},"channels.viewSponsoredMessage":{"CHANNEL_INVALID":"CHANNEL_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE"},"stats.getBroadcastRevenueStats":{"CHANNEL_INVALID":"CHANNEL_INVALID","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","PEER_ID_INVALID":"PEER_ID_INVALID"},"stats.getBroadcastRevenueTransactions":{"CHANNEL_INVALID":"CHANNEL_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID"},"stories.getBoostersList":{"CHANNEL_INVALID":"CHANNEL_INVALID","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.forwardMessage":{"CHAT_ID_INVALID":"CHAT_ID_INVALID","MESSAGE_ID_INVALID":"MESSAGE_ID_INVALID","PEER_ID_INVALID":"PEER_ID_INVALID","YOU_BLOCKED_USER":"YOU_BLOCKED_USER"},"messages.toggleChatAdmins":{"CHAT_ID_INVALID":"CHAT_ID_INVALID","CHAT_NOT_MODIFIED":"CHAT_NOT_MODIFIED"},"messages.getMessagesReadParticipants":{"CHAT_TOO_BIG":"CHAT_TOO_BIG","MESSAGE_ID_INVALID":"MESSAGE_ID_INVALID"},"initConnection":{"CONNECTION_LAYER_INVALID":"CONNECTION_LAYER_INVALID"},"contacts.deleteContact":{"CONTACT_ID_INVALID":"CONTACT_ID_INVALID"},"contacts.importCard":{"EXPORT_CARD_INVALID":"EXPORT_CARD_INVALID"},"folders.deleteFolder":{"FOLDER_ID_EMPTY":"FOLDER_ID_EMPTY","FOLDER_ID_INVALID":"FOLDER_ID_INVALID"},"phone.discardGroupCallRequest":{"GROUPCALL_ALREADY_DISCARDED":"GROUPCALL_ALREADY_DISCARDED"},"account.resetAuthorization":{"HASH_INVALID":"HASH_INVALID"},"auth.sendInvites":{"MESSAGE_EMPTY":"MESSAGE_EMPTY"},"payments.resolveStarGiftOffer":{"MESSAGE_ID_INVALID":"MESSAGE_ID_INVALID"},"messages.searchGifs":{"METHOD_INVALID":"METHOD_INVALID","SEARCH_QUERY_EMPTY":"SEARCH_QUERY_EMPTY"},"account.resetPassword":{"PASSWORD_EMPTY":"PASSWORD_EMPTY"},"account.checkPassword":{"PASSWORD_HASH_INVALID":"PASSWORD_HASH_INVALID"},"account.deleteAccount":{"PASSWORD_HASH_INVALID":"PASSWORD_HASH_INVALID"},"stats.getBroadcastRevenueWithdrawalUrl":{"PASSWORD_HASH_INVALID":"PASSWORD_HASH_INVALID","PASSWORD_MISSING":"PASSWORD_MISSING","PASSWORD_TOO_FRESH_%d":"PASSWORD_TOO_FRESH_%d","SESSION_TOO_FRESH_%d":"SESSION_TOO_FRESH_%d"},"messages.getStatsURL":{"PEER_ID_INVALID":"PEER_ID_INVALID"},"messages.hideReportSpam":{"PEER_ID_INVALID":"PEER_ID_INVALID"},"stories.getBoostsStatus":{"PEER_ID_INVALID":"PEER_ID_INVALID"},"auth.checkPhone":{"PHONE_NUMBER_BANNED":"PHONE_NUMBER_BANNED","PHONE_NUMBER_INVALID":"PHONE_NUMBER_INVALID"},"payments.canPurchasePremium":{"USER_GIFT_UNAVAILABLE":"USER_GIFT_UNAVAILABLE","USER_ID_INVALID":"USER_ID_INVALID"},"account.addNoPaidMessagesException":{"USER_ID_INVALID":"USER_ID_INVALID"},"payments.getUserStarGifts":{"USER_ID_INVALID":"USER_ID_INVALID"},"stories.getUserStories":{"USER_ID_INVALID":"USER_ID_INVALID"},"account.updateUsername":{"USERNAME_INVALID":"USERNAME_INVALID","USERNAME_NOT_MODIFIED":"USERNAME_NOT_MODIFIED","USERNAME_OCCUPIED":"USERNAME_OCCUPIED","USERNAME_PURCHASE_AVAILABLE":"USERNAME_PURCHASE_AVAILABLE"}},"403":{"account.initPasskeyRegistration":{"ACCESS_DENIED":"ACCESS_DENIED"},"messages.forwardMessages":{"ALLOW_PAYMENT_REQUIRED_%d":"ALLOW_PAYMENT_REQUIRED_%d","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_GUEST_SEND_FORBIDDEN":"CHAT_GUEST_SEND_FORBIDDEN","CHAT_SEND_AUDIOS_FORBIDDEN":"CHAT_SEND_AUDIOS_FORBIDDEN","CHAT_SEND_DOCS_FORBIDDEN":"CHAT_SEND_DOCS_FORBIDDEN","CHAT_SEND_GAME_FORBIDDEN":"CHAT_SEND_GAME_FORBIDDEN","CHAT_SEND_GIFS_FORBIDDEN":"CHAT_SEND_GIFS_FORBIDDEN","CHAT_SEND_INLINE_FORBIDDEN":"CHAT_SEND_INLINE_FORBIDDEN","CHAT_SEND_MEDIA_FORBIDDEN":"CHAT_SEND_MEDIA_FORBIDDEN","CHAT_SEND_PHOTOS_FORBIDDEN":"CHAT_SEND_PHOTOS_FORBIDDEN","CHAT_SEND_PLAIN_FORBIDDEN":"CHAT_SEND_PLAIN_FORBIDDEN","CHAT_SEND_POLL_FORBIDDEN":"CHAT_SEND_POLL_FORBIDDEN","CHAT_SEND_STICKERS_FORBIDDEN":"CHAT_SEND_STICKERS_FORBIDDEN","CHAT_SEND_VIDEOS_FORBIDDEN":"CHAT_SEND_VIDEOS_FORBIDDEN","CHAT_SEND_VOICES_FORBIDDEN":"CHAT_SEND_VOICES_FORBIDDEN","CHAT_SEND_WEBPAGE_FORBIDDEN":"CHAT_SEND_WEBPAGE_FORBIDDEN","CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN","PREMIUM_ACCOUNT_REQUIRED":"PREMIUM_ACCOUNT_REQUIRED","PRIVACY_PREMIUM_REQUIRED":"PRIVACY_PREMIUM_REQUIRED","USER_IS_BLOCKED":"USER_IS_BLOCKED","VOICE_MESSAGES_FORBIDDEN":"VOICE_MESSAGES_FORBIDDEN"},"messages.sendInlineBotResult":{"ALLOW_PAYMENT_REQUIRED_%d":"ALLOW_PAYMENT_REQUIRED_%d","CHAT_GUEST_SEND_FORBIDDEN":"CHAT_GUEST_SEND_FORBIDDEN","CHAT_SEND_AUDIOS_FORBIDDEN":"CHAT_SEND_AUDIOS_FORBIDDEN","CHAT_SEND_GAME_FORBIDDEN":"CHAT_SEND_GAME_FORBIDDEN","CHAT_SEND_GIFS_FORBIDDEN":"CHAT_SEND_GIFS_FORBIDDEN","CHAT_SEND_INLINE_FORBIDDEN":"CHAT_SEND_INLINE_FORBIDDEN","CHAT_SEND_MEDIA_FORBIDDEN":"CHAT_SEND_MEDIA_FORBIDDEN","CHAT_SEND_PHOTOS_FORBIDDEN":"CHAT_SEND_PHOTOS_FORBIDDEN","CHAT_SEND_PLAIN_FORBIDDEN":"CHAT_SEND_PLAIN_FORBIDDEN","CHAT_SEND_STICKERS_FORBIDDEN":"CHAT_SEND_STICKERS_FORBIDDEN","CHAT_SEND_VOICES_FORBIDDEN":"CHAT_SEND_VOICES_FORBIDDEN","CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN","PRIVACY_PREMIUM_REQUIRED":"PRIVACY_PREMIUM_REQUIRED"},"messages.sendMedia":{"ALLOW_PAYMENT_REQUIRED_%d":"ALLOW_PAYMENT_REQUIRED_%d","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_GUEST_SEND_FORBIDDEN":"CHAT_GUEST_SEND_FORBIDDEN","CHAT_SEND_AUDIOS_FORBIDDEN":"CHAT_SEND_AUDIOS_FORBIDDEN","CHAT_SEND_DOCS_FORBIDDEN":"CHAT_SEND_DOCS_FORBIDDEN","CHAT_SEND_GIFS_FORBIDDEN":"CHAT_SEND_GIFS_FORBIDDEN","CHAT_SEND_MEDIA_FORBIDDEN":"CHAT_SEND_MEDIA_FORBIDDEN","CHAT_SEND_PHOTOS_FORBIDDEN":"CHAT_SEND_PHOTOS_FORBIDDEN","CHAT_SEND_PLAIN_FORBIDDEN":"CHAT_SEND_PLAIN_FORBIDDEN","CHAT_SEND_POLL_FORBIDDEN":"CHAT_SEND_POLL_FORBIDDEN","CHAT_SEND_ROUNDVIDEOS_FORBIDDEN":"CHAT_SEND_ROUNDVIDEOS_FORBIDDEN","CHAT_SEND_STICKERS_FORBIDDEN":"CHAT_SEND_STICKERS_FORBIDDEN","CHAT_SEND_VIDEOS_FORBIDDEN":"CHAT_SEND_VIDEOS_FORBIDDEN","CHAT_SEND_VOICES_FORBIDDEN":"CHAT_SEND_VOICES_FORBIDDEN","CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN","PREMIUM_ACCOUNT_REQUIRED":"PREMIUM_ACCOUNT_REQUIRED","PRIVACY_PREMIUM_REQUIRED":"PRIVACY_PREMIUM_REQUIRED","USER_IS_BLOCKED":"USER_IS_BLOCKED"},"messages.sendMessage":{"ALLOW_PAYMENT_REQUIRED_%d":"ALLOW_PAYMENT_REQUIRED_%d","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_GUEST_SEND_FORBIDDEN":"CHAT_GUEST_SEND_FORBIDDEN","CHAT_SEND_PLAIN_FORBIDDEN":"CHAT_SEND_PLAIN_FORBIDDEN","CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN","PREMIUM_ACCOUNT_REQUIRED":"PREMIUM_ACCOUNT_REQUIRED","PRIVACY_PREMIUM_REQUIRED":"PRIVACY_PREMIUM_REQUIRED","USER_IS_BLOCKED":"USER_IS_BLOCKED"},"messages.sendMultiMedia":{"ALLOW_PAYMENT_REQUIRED_%d":"ALLOW_PAYMENT_REQUIRED_%d","CHAT_SEND_MEDIA_FORBIDDEN":"CHAT_SEND_MEDIA_FORBIDDEN","CHAT_SEND_PHOTOS_FORBIDDEN":"CHAT_SEND_PHOTOS_FORBIDDEN","CHAT_SEND_VIDEOS_FORBIDDEN":"CHAT_SEND_VIDEOS_FORBIDDEN","CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN","PRIVACY_PREMIUM_REQUIRED":"PRIVACY_PREMIUM_REQUIRED"},"account.setGlobalPrivacySettings":{"BOT_ACCESS_FORBIDDEN":"BOT_ACCESS_FORBIDDEN","PREMIUM_ACCOUNT_REQUIRED":"PREMIUM_ACCOUNT_REQUIRED"},"messages.deleteMessages":{"BOT_ACCESS_FORBIDDEN":"BOT_ACCESS_FORBIDDEN","MESSAGE_DELETE_FORBIDDEN":"MESSAGE_DELETE_FORBIDDEN"},"payments.getPaymentForm":{"BOT_ACCESS_FORBIDDEN":"BOT_ACCESS_FORBIDDEN","USER_DISALLOWED_STARGIFTS":"USER_DISALLOWED_STARGIFTS"},"payments.getStarsStatus":{"BOT_ACCESS_FORBIDDEN":"BOT_ACCESS_FORBIDDEN"},"payments.sendStarsForm":{"BOT_ACCESS_FORBIDDEN":"BOT_ACCESS_FORBIDDEN"},"stories.deleteStories":{"BOT_ACCESS_FORBIDDEN":"BOT_ACCESS_FORBIDDEN"},"stories.sendStory":{"BOT_ACCESS_FORBIDDEN":"BOT_ACCESS_FORBIDDEN"},"messages.createForumTopic":{"BOT_FORUM_CREATE_FORBIDDEN":"BOT_FORUM_CREATE_FORBIDDEN","PREMIUM_ACCOUNT_REQUIRED":"PREMIUM_ACCOUNT_REQUIRED"},"bots.setJoinChatResults":{"BOT_GUARD_NOT_SUPPORTED":"BOT_GUARD_NOT_SUPPORTED"},"bots.setCustomVerification":{"BOT_VERIFIER_FORBIDDEN":"BOT_VERIFIER_FORBIDDEN"},"messages.getMessageReactionsList":{"BROADCAST_FORBIDDEN":"BROADCAST_FORBIDDEN"},"messages.getPollVotes":{"BROADCAST_FORBIDDEN":"BROADCAST_FORBIDDEN","POLL_VOTE_REQUIRED":"POLL_VOTE_REQUIRED"},"channels.getFullChannel":{"CHANNEL_PUBLIC_GROUP_NA":"CHANNEL_PUBLIC_GROUP_NA"},"channels.leaveChannel":{"CHANNEL_PUBLIC_GROUP_NA":"CHANNEL_PUBLIC_GROUP_NA"},"updates.getChannelDifference":{"CHANNEL_PUBLIC_GROUP_NA":"CHANNEL_PUBLIC_GROUP_NA","CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN"},"messages.deleteFactCheck":{"CHAT_ACTION_FORBIDDEN":"CHAT_ACTION_FORBIDDEN"},"messages.editFactCheck":{"CHAT_ACTION_FORBIDDEN":"CHAT_ACTION_FORBIDDEN"},"channels.editAdmin":{"CHAT_ADMIN_INVITE_REQUIRED":"CHAT_ADMIN_INVITE_REQUIRED","CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN","RIGHT_FORBIDDEN":"RIGHT_FORBIDDEN","USER_CHANNELS_TOO_MUCH":"USER_CHANNELS_TOO_MUCH","USER_NOT_MUTUAL_CONTACT":"USER_NOT_MUTUAL_CONTACT","USER_PRIVACY_RESTRICTED":"USER_PRIVACY_RESTRICTED","USER_RESTRICTED":"USER_RESTRICTED"},"channels.deleteUserHistory":{"CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN"},"channels.editBanned":{"CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN"},"channels.editForumTopic":{"CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED"},"channels.editPhoto":{"CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN"},"channels.editTitle":{"CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN"},"channels.getAdminLog":{"CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN"},"channels.getParticipant":{"CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED"},"channels.getParticipants":{"CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED"},"channels.inviteToChannel":{"CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN","USER_CHANNELS_TOO_MUCH":"USER_CHANNELS_TOO_MUCH","USER_NOT_MUTUAL_CONTACT":"USER_NOT_MUTUAL_CONTACT","USER_PRIVACY_RESTRICTED":"USER_PRIVACY_RESTRICTED"},"channels.updateUsername":{"CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN"},"messages.addChatUser":{"CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN","USER_NOT_MUTUAL_CONTACT":"USER_NOT_MUTUAL_CONTACT","USER_PRIVACY_RESTRICTED":"USER_PRIVACY_RESTRICTED"},"messages.editMessage":{"CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED","CHAT_SEND_GIFS_FORBIDDEN":"CHAT_SEND_GIFS_FORBIDDEN","CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN","INLINE_BOT_REQUIRED":"INLINE_BOT_REQUIRED","MESSAGE_AUTHOR_REQUIRED":"MESSAGE_AUTHOR_REQUIRED"},"messages.migrateChat":{"CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED"},"messages.search":{"CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED"},"stats.getBroadcastStats":{"CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED"},"stats.getMegagroupStats":{"CHAT_ADMIN_REQUIRED":"CHAT_ADMIN_REQUIRED"},"phone.inviteToGroupCall":{"CHAT_TYPE_INVALID":"CHAT_TYPE_INVALID","GROUPCALL_FORBIDDEN":"GROUPCALL_FORBIDDEN","USER_NOT_PARTICIPANT":"USER_NOT_PARTICIPANT"},"channels.convertToGigagroup":{"CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN"},"channels.createForumTopic":{"CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN","PREMIUM_ACCOUNT_REQUIRED":"PREMIUM_ACCOUNT_REQUIRED"},"channels.deleteChannel":{"CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN"},"channels.deleteParticipantHistory":{"CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN"},"channels.deleteTopicHistory":{"CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN"},"channels.editCreator":{"CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN","USER_CHANNELS_TOO_MUCH":"USER_CHANNELS_TOO_MUCH","USER_PRIVACY_RESTRICTED":"USER_PRIVACY_RESTRICTED"},"channels.setDiscussionGroup":{"CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN"},"channels.updatePinnedForumTopic":{"CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN"},"invokeWithLayer":{"CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN"},"messages.editChatAbout":{"CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN"},"messages.editChatDefaultBannedRights":{"CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN"},"messages.editExportedChatInvite":{"CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN","EDIT_BOT_INVITE_FORBIDDEN":"EDIT_BOT_INVITE_FORBIDDEN"},"messages.exportChatInvite":{"CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN"},"messages.getAdminsWithInvites":{"CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN"},"messages.getChatInviteImporters":{"CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN"},"messages.getDialogs":{"CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN"},"messages.getExportedChatInvite":{"CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN"},"messages.getExportedChatInvites":{"CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN"},"messages.getMessageEditData":{"CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN","MESSAGE_AUTHOR_REQUIRED":"MESSAGE_AUTHOR_REQUIRED"},"messages.hideAllChatJoinRequests":{"CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN"},"messages.hideChatJoinRequest":{"CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN","USER_CHANNELS_TOO_MUCH":"USER_CHANNELS_TOO_MUCH"},"messages.requestWebView":{"CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN","PRIVACY_PREMIUM_REQUIRED":"PRIVACY_PREMIUM_REQUIRED"},"messages.saveDraft":{"CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN"},"messages.sendPaidReaction":{"CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN"},"messages.sendReaction":{"CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN","PREMIUM_ACCOUNT_REQUIRED":"PREMIUM_ACCOUNT_REQUIRED","USER_BANNED_IN_CHANNEL":"USER_BANNED_IN_CHANNEL"},"messages.setTyping":{"CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN","GROUPCALL_FORBIDDEN":"GROUPCALL_FORBIDDEN","USER_IS_BLOCKED":"USER_IS_BLOCKED"},"messages.startBot":{"CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN"},"messages.updatePinnedMessage":{"CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN"},"messages.uploadMedia":{"CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN"},"payments.getStarsRevenueAdsAccountUrl":{"CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN"},"phone.joinGroupCall":{"CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN","GROUPCALL_FORBIDDEN":"GROUPCALL_FORBIDDEN"},"updates.getDifference":{"CHAT_WRITE_FORBIDDEN":"CHAT_WRITE_FORBIDDEN"},"phone.startScheduledGroupCall":{"GROUPCALL_ALREADY_STARTED":"GROUPCALL_ALREADY_STARTED"},"phone.toggleGroupCallStartSubscription":{"GROUPCALL_ALREADY_STARTED":"GROUPCALL_ALREADY_STARTED","GROUPCALL_FORBIDDEN":"GROUPCALL_FORBIDDEN"},"phone.toggleGroupCallSettings":{"GROUPCALL_CHANGE_FORBIDDEN":"GROUPCALL_CHANGE_FORBIDDEN","GROUPCALL_FORBIDDEN":"GROUPCALL_FORBIDDEN"},"phone.discardGroupCall":{"GROUPCALL_FORBIDDEN":"GROUPCALL_FORBIDDEN"},"phone.editGroupCallParticipant":{"GROUPCALL_FORBIDDEN":"GROUPCALL_FORBIDDEN"},"phone.editGroupCallTitle":{"GROUPCALL_FORBIDDEN":"GROUPCALL_FORBIDDEN"},"phone.getGroupCall":{"GROUPCALL_FORBIDDEN":"GROUPCALL_FORBIDDEN"},"phone.toggleGroupCallRecord":{"GROUPCALL_FORBIDDEN":"GROUPCALL_FORBIDDEN"},"channels.deleteMessages":{"MESSAGE_DELETE_FORBIDDEN":"MESSAGE_DELETE_FORBIDDEN"},"messages.deleteScheduledMessages":{"MESSAGE_DELETE_FORBIDDEN":"MESSAGE_DELETE_FORBIDDEN"},"smsjobs.isEligibleToJoin":{"NOT_ELIGIBLE":"NOT_ELIGIBLE"},"phone.joinGroupCallPresentation":{"PARTICIPANT_JOIN_MISSING":"PARTICIPANT_JOIN_MISSING"},"payments.getSuggestedStarRefBots":{"PEER_ID_INVALID":"PEER_ID_INVALID"},"account.createBusinessChatLink":{"PREMIUM_ACCOUNT_REQUIRED":"PREMIUM_ACCOUNT_REQUIRED"},"account.editBusinessChatLink":{"PREMIUM_ACCOUNT_REQUIRED":"PREMIUM_ACCOUNT_REQUIRED"},"account.updateColor":{"PREMIUM_ACCOUNT_REQUIRED":"PREMIUM_ACCOUNT_REQUIRED"},"account.updateEmojiStatus":{"PREMIUM_ACCOUNT_REQUIRED":"PREMIUM_ACCOUNT_REQUIRED"},"channels.searchPosts":{"PREMIUM_ACCOUNT_REQUIRED":"PREMIUM_ACCOUNT_REQUIRED"},"messages.checkQuickReplyShortcut":{"PREMIUM_ACCOUNT_REQUIRED":"PREMIUM_ACCOUNT_REQUIRED"},"messages.editQuickReplyShortcut":{"PREMIUM_ACCOUNT_REQUIRED":"PREMIUM_ACCOUNT_REQUIRED"},"messages.reorderQuickReplies":{"PREMIUM_ACCOUNT_REQUIRED":"PREMIUM_ACCOUNT_REQUIRED"},"messages.sendQuickReplyMessages":{"PREMIUM_ACCOUNT_REQUIRED":"PREMIUM_ACCOUNT_REQUIRED"},"messages.toggleDialogFilterTags":{"PREMIUM_ACCOUNT_REQUIRED":"PREMIUM_ACCOUNT_REQUIRED"},"messages.transcribeAudio":{"PREMIUM_ACCOUNT_REQUIRED":"PREMIUM_ACCOUNT_REQUIRED"},"messages.updateSavedReactionTag":{"PREMIUM_ACCOUNT_REQUIRED":"PREMIUM_ACCOUNT_REQUIRED"},"phone.exportGroupCallInvite":{"PUBLIC_CHANNEL_MISSING":"PUBLIC_CHANNEL_MISSING"},"account.setContentSettings":{"SENSITIVE_CHANGE_FORBIDDEN":"SENSITIVE_CHANGE_FORBIDDEN"},"account.finishTakeoutSession":{"TAKEOUT_REQUIRED":"TAKEOUT_REQUIRED"},"channels.getLeftChannels":{"TAKEOUT_REQUIRED":"TAKEOUT_REQUIRED"},"contacts.getSaved":{"TAKEOUT_REQUIRED":"TAKEOUT_REQUIRED"},"messages.setBotGuestChatResult":{"USER_BOT_INVALID":"USER_BOT_INVALID"},"messages.setInlineBotResults":{"USER_BOT_INVALID":"USER_BOT_INVALID"},"messages.editChatCreator":{"USER_CHANNELS_TOO_MUCH":"USER_CHANNELS_TOO_MUCH","USER_PRIVACY_RESTRICTED":"USER_PRIVACY_RESTRICTED"},"messages.sendEncryptedService":{"USER_DELETED":"USER_DELETED","USER_IS_BLOCKED":"USER_IS_BLOCKED"},"help.editUserInfo":{"USER_INVALID":"USER_INVALID"},"help.getSupportName":{"USER_INVALID":"USER_INVALID"},"help.getUserInfo":{"USER_INVALID":"USER_INVALID"},"messages.requestEncryption":{"USER_IS_BLOCKED":"USER_IS_BLOCKED"},"messages.sendEncrypted":{"USER_IS_BLOCKED":"USER_IS_BLOCKED"},"phone.requestCall":{"USER_IS_BLOCKED":"USER_IS_BLOCKED","USER_PRIVACY_RESTRICTED":"USER_PRIVACY_RESTRICTED"},"bots.updateUserEmojiStatus":{"USER_PERMISSION_DENIED":"USER_PERMISSION_DENIED"},"help.getConfig":{"USER_PRIVACY_RESTRICTED":"USER_PRIVACY_RESTRICTED"},"messages.getOutboxReadDate":{"USER_PRIVACY_RESTRICTED":"USER_PRIVACY_RESTRICTED","YOUR_PRIVACY_RESTRICTED":"YOUR_PRIVACY_RESTRICTED"},"channels.createChannel":{"USER_RESTRICTED":"USER_RESTRICTED"},"messages.createChat":{"USER_RESTRICTED":"USER_RESTRICTED"}},"500":{"messages.composeMessageWithAI":{"AICOMPOSE_TIMEOUT":"AICOMPOSE_TIMEOUT"},"auth.checkPassword":{"AUTH_KEY_UNSYNCHRONIZED":"AUTH_KEY_UNSYNCHRONIZED"},"auth.exportLoginToken":{"AUTH_RESTART":"AUTH_RESTART"},"auth.finishPasskeyLogin":{"AUTH_RESTART":"AUTH_RESTART"},"auth.initPasskeyLogin":{"AUTH_RESTART":"AUTH_RESTART"},"auth.sendCode":{"AUTH_RESTART":"AUTH_RESTART","AUTH_RESTART_%d":"AUTH_RESTART_%d"},"auth.signIn":{"AUTH_RESTART":"AUTH_RESTART","SIGN_IN_FAILED":"SIGN_IN_FAILED"},"phone.acceptCall":{"CALL_OCCUPY_FAILED":"CALL_OCCUPY_FAILED"},"phone.discardCall":{"CALL_OCCUPY_FAILED":"CALL_OCCUPY_FAILED"},"upload.reuploadCdnFile":{"CDN_UPLOAD_TIMEOUT":"CDN_UPLOAD_TIMEOUT"},"messages.createChat":{"CHAT_ID_GENERATE_FAILED":"CHAT_ID_GENERATE_FAILED"},"channels.createChannel":{"CHAT_INVALID":"CHAT_INVALID"},"messages.migrateChat":{"CHAT_INVALID":"CHAT_INVALID"},"messages.editMessage":{"MSG_WAIT_FAILED":"MSG_WAIT_FAILED"},"messages.receivedQueue":{"MSG_WAIT_FAILED":"MSG_WAIT_FAILED"},"messages.sendEncrypted":{"MSG_WAIT_FAILED":"MSG_WAIT_FAILED"},"messages.sendEncryptedService":{"MSG_WAIT_FAILED":"MSG_WAIT_FAILED"},"messages.sendMessage":{"MSG_WAIT_FAILED":"MSG_WAIT_FAILED","RANDOM_ID_DUPLICATE":"RANDOM_ID_DUPLICATE"},"messages.requestUrlAuth":{"OAUTH_REQUEST_INVALID":"OAUTH_REQUEST_INVALID"},"updates.getChannelDifference":{"PERSISTENT_TIMESTAMP_OUTDATED":"PERSISTENT_TIMESTAMP_OUTDATED"},"messages.forwardMessages":{"RANDOM_ID_DUPLICATE":"RANDOM_ID_DUPLICATE"},"messages.sendInlineBotResult":{"RANDOM_ID_DUPLICATE":"RANDOM_ID_DUPLICATE","SEND_MEDIA_INVALID":"SEND_MEDIA_INVALID"},"messages.sendMedia":{"RANDOM_ID_DUPLICATE":"RANDOM_ID_DUPLICATE"},"messages.sendMultiMedia":{"RANDOM_ID_DUPLICATE":"RANDOM_ID_DUPLICATE"},"messages.sendScheduledMessages":{"RANDOM_ID_DUPLICATE":"RANDOM_ID_DUPLICATE"},"messages.startBot":{"RANDOM_ID_DUPLICATE":"RANDOM_ID_DUPLICATE"},"phone.requestCall":{"RANDOM_ID_DUPLICATE":"RANDOM_ID_DUPLICATE"},"updates.getDifference":{"RANDOM_ID_DUPLICATE":"RANDOM_ID_DUPLICATE"},"messages.translateText":{"TRANSLATE_REQ_FAILED":"TRANSLATE_REQ_FAILED","TRANSLATION_TIMEOUT":"TRANSLATION_TIMEOUT"}},"406":{"messages.forwardMessages":{"ALLOW_PAYMENT_REQUIRED":"ALLOW_PAYMENT_REQUIRED","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHAT_FORWARDS_RESTRICTED":"CHAT_FORWARDS_RESTRICTED","PAYMENT_UNSUPPORTED":"PAYMENT_UNSUPPORTED","PEER_ID_INVALID":"PEER_ID_INVALID","PRIVACY_PREMIUM_REQUIRED":"PRIVACY_PREMIUM_REQUIRED","TOPIC_CLOSED":"TOPIC_CLOSED","TOPIC_DELETED":"TOPIC_DELETED"},"messages.sendMedia":{"ALLOW_PAYMENT_REQUIRED":"ALLOW_PAYMENT_REQUIRED","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","PEER_ID_INVALID":"PEER_ID_INVALID","TOPIC_CLOSED":"TOPIC_CLOSED","TOPIC_DELETED":"TOPIC_DELETED"},"messages.sendMessage":{"ALLOW_PAYMENT_REQUIRED":"ALLOW_PAYMENT_REQUIRED","CHANNEL_PRIVATE":"CHANNEL_PRIVATE","PAYMENT_UNSUPPORTED":"PAYMENT_UNSUPPORTED","PEER_ID_INVALID":"PEER_ID_INVALID","PRIVACY_PREMIUM_REQUIRED":"PRIVACY_PREMIUM_REQUIRED","TOPIC_CLOSED":"TOPIC_CLOSED","TOPIC_DELETED":"TOPIC_DELETED"},"payments.getPaymentForm":{"API_GIFT_RESTRICTED_UPDATE_APP":"API_GIFT_RESTRICTED_UPDATE_APP","STARGIFT_EXPORT_IN_PROGRESS":"STARGIFT_EXPORT_IN_PROGRESS","STARS_FORM_AMOUNT_MISMATCH":"STARS_FORM_AMOUNT_MISMATCH"},"payments.sendStarsForm":{"API_GIFT_RESTRICTED_UPDATE_APP":"API_GIFT_RESTRICTED_UPDATE_APP","PRECHECKOUT_FAILED":"PRECHECKOUT_FAILED","STARS_FORM_AMOUNT_MISMATCH":"STARS_FORM_AMOUNT_MISMATCH"},"channels.editBanned":{"BANNED_RIGHTS_INVALID":"BANNED_RIGHTS_INVALID","CHANNEL_PRIVATE":"CHANNEL_PRIVATE"},"contacts.getLocated":{"BUSINESS_ADDRESS_ACTIVE":"BUSINESS_ADDRESS_ACTIVE","USERPIC_PRIVACY_REQUIRED":"USERPIC_PRIVACY_REQUIRED","USERPIC_UPLOAD_REQUIRED":"USERPIC_UPLOAD_REQUIRED"},"phone.acceptCall":{"CALL_PROTOCOL_COMPAT_LAYER_INVALID":"CALL_PROTOCOL_COMPAT_LAYER_INVALID"},"channels.deleteChannel":{"CHANNEL_PRIVATE":"CHANNEL_PRIVATE","CHANNEL_TOO_LARGE":"CHANNEL_TOO_LARGE"},"channels.deleteMessages":{"CHANNEL_PRIVATE":"CHANNEL_PRIVATE"},"channels.getAdminLog":{"CHANNEL_PRIVATE":"CHANNEL_PRIVATE"},"channels.getChannels":{"CHANNEL_PRIVATE":"CHANNEL_PRIVATE"},"channels.getFullChannel":{"CHANNEL_PRIVATE":"CHANNEL_PRIVATE"},"channels.getMessages":{"CHANNEL_PRIVATE":"CHANNEL_PRIVATE"},"channels.getParticipant":{"CHANNEL_PRIVATE":"CHANNEL_PRIVATE"},"channels.getParticipants":{"CHANNEL_PRIVATE":"CHANNEL_PRIVATE"},"channels.inviteToChannel":{"CHANNEL_PRIVATE":"CHANNEL_PRIVATE"},"channels.joinChannel":{"CHANNEL_PRIVATE":"CHANNEL_PRIVATE","INVITE_HASH_EXPIRED":"INVITE_HASH_EXPIRED"},"channels.leaveChannel":{"CHANNEL_PRIVATE":"CHANNEL_PRIVATE"},"channels.readHistory":{"CHANNEL_PRIVATE":"CHANNEL_PRIVATE"},"channels.readMessageContents":{"CHANNEL_PRIVATE":"CHANNEL_PRIVATE"},"messages.checkChatInvite":{"CHANNEL_PRIVATE":"CHANNEL_PRIVATE","INVITE_HASH_EXPIRED":"INVITE_HASH_EXPIRED"},"messages.editMessage":{"CHANNEL_PRIVATE":"CHANNEL_PRIVATE","EDIT_MESSAGE_TEMP_RESTRICTED":"EDIT_MESSAGE_TEMP_RESTRICTED"},"messages.getHistory":{"CHANNEL_PRIVATE":"CHANNEL_PRIVATE"},"messages.getInlineBotResults":{"CHANNEL_PRIVATE":"CHANNEL_PRIVATE"},"messages.getMessagesViews":{"CHANNEL_PRIVATE":"CHANNEL_PRIVATE"},"messages.getPeerDialogs":{"CHANNEL_PRIVATE":"CHANNEL_PRIVATE"},"messages.setTyping":{"CHANNEL_PRIVATE":"CHANNEL_PRIVATE","PEER_ID_INVALID":"PEER_ID_INVALID"},"updates.getChannelDifference":{"CHANNEL_PRIVATE":"CHANNEL_PRIVATE"},"upload.getFile":{"FILEREF_UPGRADE_NEEDED":"FILEREF_UPGRADE_NEEDED"},"channels.editAdmin":{"FRESH_CHANGE_ADMINS_FORBIDDEN":"FRESH_CHANGE_ADMINS_FORBIDDEN"},"account.sendChangePhoneCode":{"FRESH_CHANGE_PHONE_FORBIDDEN":"FRESH_CHANGE_PHONE_FORBIDDEN","PHONE_NUMBER_INVALID":"PHONE_NUMBER_INVALID"},"account.initPasskeyRegistration":{"FRESH_RESET_AUTHORISATION_FORBIDDEN":"FRESH_RESET_AUTHORISATION_FORBIDDEN"},"account.resetAuthorization":{"FRESH_RESET_AUTHORISATION_FORBIDDEN":"FRESH_RESET_AUTHORISATION_FORBIDDEN"},"account.setAuthorizationTTL":{"FRESH_RESET_AUTHORISATION_FORBIDDEN":"FRESH_RESET_AUTHORISATION_FORBIDDEN"},"auth.resetAuthorizations":{"FRESH_RESET_AUTHORISATION_FORBIDDEN":"FRESH_RESET_AUTHORISATION_FORBIDDEN"},"invokeWithLayer":{"INVITE_HASH_EXPIRED":"INVITE_HASH_EXPIRED"},"messages.importChatInvite":{"INVITE_HASH_EXPIRED":"INVITE_HASH_EXPIRED"},"messages.sendMultiMedia":{"PEER_ID_INVALID":"PEER_ID_INVALID"},"account.changePhone":{"PHONE_NUMBER_INVALID":"PHONE_NUMBER_INVALID"},"auth.cancelCode":{"PHONE_NUMBER_INVALID":"PHONE_NUMBER_INVALID"},"auth.checkPhone":{"PHONE_NUMBER_INVALID":"PHONE_NUMBER_INVALID"},"auth.resendCode":{"PHONE_NUMBER_INVALID":"PHONE_NUMBER_INVALID","SEND_CODE_UNAVAILABLE":"SEND_CODE_UNAVAILABLE"},"auth.sendCode":{"PHONE_NUMBER_INVALID":"PHONE_NUMBER_INVALID","PHONE_PASSWORD_FLOOD":"PHONE_PASSWORD_FLOOD","UPDATE_APP_TO_LOGIN":"UPDATE_APP_TO_LOGIN"},"auth.signIn":{"PHONE_NUMBER_INVALID":"PHONE_NUMBER_INVALID","UPDATE_APP_TO_LOGIN":"UPDATE_APP_TO_LOGIN"},"auth.signUp":{"PHONE_NUMBER_INVALID":"PHONE_NUMBER_INVALID"},"messages.sendVote":{"POLL_COUNTRY_RESTRICTED":"POLL_COUNTRY_RESTRICTED","POLL_MEMBER_RESTRICTED":"POLL_MEMBER_RESTRICTED"},"payments.canPurchasePremium":{"PREMIUM_CURRENTLY_UNAVAILABLE":"PREMIUM_CURRENTLY_UNAVAILABLE"},"payments.canPurchaseStore":{"PREMIUM_CURRENTLY_UNAVAILABLE":"PREMIUM_CURRENTLY_UNAVAILABLE"},"messages.initHistoryImport":{"PREVIOUS_CHAT_IMPORT_ACTIVE_WAIT_%dMIN":"PREVIOUS_CHAT_IMPORT_ACTIVE_WAIT_%dMIN"},"messages.getStickerSet":{"STICKERSET_INVALID":"STICKERSET_INVALID"},"messages.installStickerSet":{"STICKERSET_INVALID":"STICKERSET_INVALID"},"messages.uninstallStickerSet":{"STICKERSET_INVALID":"STICKERSET_INVALID"},"stickers.addStickerToSet":{"STICKERSET_INVALID":"STICKERSET_INVALID"},"channels.setStickers":{"STICKERSET_OWNER_ANONYMOUS":"STICKERSET_OWNER_ANONYMOUS"},"messages.translateText":{"TRANSLATIONS_DISABLED":"TRANSLATIONS_DISABLED"},"channels.createChannel":{"USER_RESTRICTED":"USER_RESTRICTED"},"messages.createChat":{"USER_RESTRICTED":"USER_RESTRICTED"}},"401":{"account.acceptAuthorization":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.cancelPasswordEmail":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.changeAuthorizationSettings":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.changePhone":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.checkUsername":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.clearRecentEmojiStatuses":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.confirmBotConnection":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.confirmPasswordEmail":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.confirmPhone":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.createBusinessChatLink":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.createTheme":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.declinePasswordReset":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.deleteAutoSaveExceptions":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.deleteBusinessChatLink":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.deletePasskey":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.deleteSecureValue":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.deleteWebBrowserSettingsExceptions":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.disablePeerConnectedBot":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.editBusinessChatLink":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.finishTakeoutSession":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.getAccountTTL":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.getAllSecureValues":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.getAuthorizationForm":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.getAuthorizations":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.getAutoDownloadSettings":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.getAutoSaveSettings":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.getBotBusinessConnection":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.getBusinessChatLinks":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.getChannelDefaultEmojiStatuses":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.getChannelRestrictedStatusEmojis":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.getChatThemes":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.getCollectibleEmojiStatuses":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.getConnectedBots":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.getContactSignUpNotification":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.getContentSettings":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.getDefaultBackgroundEmojis":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.getDefaultEmojiStatuses":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.getDefaultGroupPhotoEmojis":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.getDefaultProfilePhotoEmojis":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.getGlobalPrivacySettings":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.getMultiWallPapers":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.getNotifyExceptions":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.getNotifySettings":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.getPaidMessagesRevenue":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.getPasskeys":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.getPassword":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.getPasswordSettings":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.getPrivacy":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.getReactionsNotifySettings":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.getRecentEmojiStatuses":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.getSavedMusicIds":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.getSavedRingtones":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.getSecureValue":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.getTheme":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.getThemes":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.getTmpPassword":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.getUniqueGiftChatThemes":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.getWallPaper":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.getWallPapers":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.getWebAuthorizations":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.getWebBrowserSettings":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.initPasskeyRegistration":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.initTakeoutSession":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.installTheme":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.installWallPaper":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.invalidateSignInCodes":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.registerDevice":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.registerPasskey":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.reorderUsernames":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.reportPeer":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.reportProfilePhoto":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.resendPasswordEmail":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.resetAuthorization":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.resetNotifySettings":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.resetPassword":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.resetWallPapers":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.resetWebAuthorization":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.resetWebAuthorizations":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.resolveBusinessChatLink":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.saveAutoDownloadSettings":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.saveAutoSaveSettings":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.saveMusic":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.saveRingtone":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.saveSecureValue":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.saveTheme":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.saveWallPaper":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.sendChangePhoneCode":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.sendConfirmPhoneCode":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.sendVerifyPhoneCode":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.setAccountTTL":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.setAuthorizationTTL":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.setContactSignUpNotification":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.setContentSettings":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.setGlobalPrivacySettings":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.setMainProfileTab":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.setPrivacy":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.setReactionsNotifySettings":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.toggleConnectedBotPaused":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.toggleNoPaidMessagesException":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.toggleSponsoredMessages":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.toggleUsername":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.toggleWebBrowserSettingsException":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.unregisterDevice":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.updateBirthday":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.updateBusinessAwayMessage":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.updateBusinessGreetingMessage":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.updateBusinessIntro":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.updateBusinessLocation":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.updateBusinessWorkHours":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.updateColor":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.updateConnectedBot":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.updateDeviceLocked":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.updateEmojiStatus":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.updateNotifySettings":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.updatePasswordSettings":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.updatePersonalChannel":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.updateProfile":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.updateStatus":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.updateTheme":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.updateUsername":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.updateWebBrowserSettings":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.uploadRingtone":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.uploadTheme":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.uploadWallPaper":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"account.verifyPhone":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"aicompose.createTone":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"aicompose.deleteTone":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"aicompose.getTone":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"aicompose.getToneExample":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"aicompose.getTones":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"aicompose.saveTone":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"aicompose.updateTone":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"auth.acceptLoginToken":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"auth.checkPassword":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"auth.checkRecoveryPassword":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"auth.exportAuthorization":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"auth.recoverPassword":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"auth.requestPasswordRecovery":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"auth.resetAuthorizations":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"bots.addPreviewMedia":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"bots.allowSendMessage":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"bots.answerWebhookJSONQuery":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"bots.canSendMessage":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"bots.checkDownloadFileParams":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"bots.checkUsername":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"bots.createBot":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"bots.deletePreviewMedia":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"bots.editAccessSettings":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"bots.editPreviewMedia":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"bots.exportBotToken":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"bots.getAccessSettings":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"bots.getAdminedBots":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"bots.getBotCommands":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"bots.getBotInfo":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"bots.getBotMenuButton":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"bots.getBotRecommendations":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"bots.getPopularAppBots":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"bots.getPreviewInfo":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"bots.getPreviewMedias":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"bots.getRequestedWebViewButton":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"bots.invokeWebViewCustomMethod":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"bots.reorderPreviewMedias":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"bots.reorderUsernames":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"bots.requestWebViewButton":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"bots.resetBotCommands":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"bots.sendCustomRequest":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"bots.setBotBroadcastDefaultAdminRights":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"bots.setBotCommands":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"bots.setBotGroupDefaultAdminRights":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"bots.setBotInfo":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"bots.setBotMenuButton":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"bots.setCustomVerification":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"bots.setJoinChatResults":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"bots.toggleUserEmojiStatusPermission":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"bots.toggleUsername":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"bots.updateStarRefProgram":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"bots.updateUserEmojiStatus":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.checkSearchPostsFlood":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.checkUsername":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.convertToGigagroup":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.createChannel":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.deactivateAllUsernames":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.deleteChannel":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.deleteHistory":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.deleteMessages":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.deleteParticipantHistory":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.editAdmin":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.editBanned":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.editLocation":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.editPhoto":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.editTitle":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.exportMessageLink":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.getAdminedPublicChannels":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.getAdminLog":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.getChannelRecommendations":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.getChannels":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.getFullChannel":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.getGroupsForDiscussion":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.getInactiveChannels":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.getLeftChannels":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.getMessageAuthor":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.getMessages":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.getParticipant":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.getParticipants":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.getSendAs":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.inviteToChannel":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.joinChannel":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.leaveChannel":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.readHistory":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.readMessageContents":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.reorderUsernames":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.reportAntiSpamFalsePositive":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.reportSpam":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.restrictSponsoredMessages":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.searchPosts":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.setBoostsToUnblockRestrictions":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.setDiscussionGroup":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.setEmojiStickers":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.setMainProfileTab":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.setStickers":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.toggleAntiSpam":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.toggleAutotranslation":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.toggleForum":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.toggleJoinRequest":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.toggleJoinToSend":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.toggleParticipantsHidden":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.togglePreHistoryHidden":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.toggleSignatures":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.toggleSlowMode":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.toggleUsername":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.toggleViewForumAsMessages":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.updateColor":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.updateEmojiStatus":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.updatePaidMessagesPrice":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"channels.updateUsername":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"chatlists.checkChatlistInvite":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"chatlists.deleteExportedInvite":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"chatlists.editExportedInvite":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"chatlists.exportChatlistInvite":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"chatlists.getChatlistUpdates":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"chatlists.getExportedInvites":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"chatlists.getLeaveChatlistSuggestions":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"chatlists.hideChatlistUpdates":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"chatlists.joinChatlistInvite":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"chatlists.joinChatlistUpdates":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"chatlists.leaveChatlist":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"contacts.acceptContact":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"contacts.addContact":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"contacts.block":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"contacts.blockFromReplies":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"contacts.deleteByPhones":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"contacts.deleteContacts":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"contacts.editCloseFriends":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"contacts.exportContactToken":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"contacts.getBirthdays":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"contacts.getBlocked":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"contacts.getContactIDs":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"contacts.getContacts":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"contacts.getLocated":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"contacts.getSaved":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"contacts.getSponsoredPeers":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"contacts.getStatuses":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"contacts.getTopPeers":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"contacts.importContacts":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"contacts.importContactToken":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"contacts.resetSaved":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"contacts.resetTopPeerRating":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"contacts.resolvePhone":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"contacts.resolveUsername":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"contacts.search":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"contacts.setBlocked":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"contacts.toggleTopPeers":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"contacts.unblock":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"contacts.updateContactNote":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"folders.editPeerFolders":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"fragment.getCollectibleInfo":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"help.acceptTermsOfService":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"help.dismissSuggestion":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"help.editUserInfo":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"help.getAppUpdate":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"help.getCdnConfig":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"help.getInviteText":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"help.getPassportConfig":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"help.getPeerColors":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"help.getPeerProfileColors":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"help.getPremiumPromo":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"help.getPromoData":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"help.getRecentMeUrls":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"help.getSupport":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"help.getSupportName":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"help.getTermsOfServiceUpdate":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"help.getTimezonesList":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"help.getUserInfo":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"help.hidePromoData":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"help.setBotUpdatesStatus":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.acceptEncryption":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.acceptUrlAuth":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.addChatUser":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.addPollAnswer":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.appendTodoList":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.checkChatInvite":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.checkHistoryImport":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.checkHistoryImportPeer":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.checkQuickReplyShortcut":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.checkUrlAuthMatchCode":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.clearAllDrafts":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.clearRecentReactions":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.clearRecentStickers":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.clickSponsoredMessage":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.composeMessageWithAI":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.createChat":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.createForumTopic":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.declineUrlAuth":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.deleteChat":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.deleteChatUser":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.deleteExportedChatInvite":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.deleteFactCheck":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.deleteHistory":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.deleteMessages":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.deleteParticipantReaction":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.deleteParticipantReactions":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.deletePhoneCallHistory":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.deletePollAnswer":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.deleteQuickReplyMessages":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.deleteQuickReplyShortcut":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.deleteRevokedExportedChatInvites":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.deleteSavedHistory":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.deleteScheduledMessages":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.deleteTopicHistory":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.discardEncryption":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.editChatAbout":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.editChatAdmin":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.editChatCreator":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.editChatDefaultBannedRights":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.editChatParticipantRank":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.editChatPhoto":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.editChatTitle":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.editExportedChatInvite":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.editFactCheck":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.editForumTopic":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.editInlineBotMessage":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.editMessage":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.editQuickReplyShortcut":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.exportChatInvite":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.faveSticker":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.forwardMessages":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getAdminsWithInvites":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getAllDrafts":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getAllStickers":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getArchivedStickers":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getAttachedStickers":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getAttachMenuBot":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getAttachMenuBots":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getAvailableEffects":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getAvailableReactions":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getBotApp":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getBotCallbackAnswer":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getChatInviteImporters":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getChats":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getCommonChats":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getCustomEmojiDocuments":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getDefaultHistoryTTL":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getDefaultTagReactions":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getDhConfig":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getDialogFilters":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getDialogs":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getDialogUnreadMarks":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getDiscussionMessage":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getDocumentByHash":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getEmojiGameInfo":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getEmojiGroups":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getEmojiKeywords":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getEmojiKeywordsDifference":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getEmojiKeywordsLanguages":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getEmojiProfilePhotoGroups":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getEmojiStatusGroups":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getEmojiStickerGroups":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getEmojiStickers":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getEmojiURL":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getExportedChatInvite":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getExportedChatInvites":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getExtendedMedia":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getFactCheck":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getFavedStickers":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getFeaturedEmojiStickers":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getFeaturedStickers":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getForumTopics":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getForumTopicsByID":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getFullChat":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getFutureChatCreatorAfterLeave":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getGameHighScores":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getHistory":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getInlineBotResults":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getInlineGameHighScores":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getMaskStickers":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getMessageEditData":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getMessageReactionsList":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getMessageReadParticipants":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getMessages":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getMessagesReactions":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getMessagesViews":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getMyStickers":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getOldFeaturedStickers":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getOnlines":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getOutboxReadDate":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getPaidReactionPrivacy":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getPeerDialogs":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getPeerSettings":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getPersonalChannelHistory":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getPinnedDialogs":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getPinnedSavedDialogs":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getPollResults":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getPollVotes":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getPreparedInlineMessage":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getQuickReplies":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getQuickReplyMessages":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getRecentLocations":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getRecentReactions":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getRecentStickers":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getReplies":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getRichMessage":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getSavedDialogs":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getSavedDialogsByID":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getSavedGifs":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getSavedHistory":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getSavedReactionTags":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getScheduledHistory":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getScheduledMessages":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getSearchCounters":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getSearchResultsCalendar":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getSearchResultsPositions":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getSplitRanges":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getSponsoredMessages":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getStickers":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getStickerSet":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getSuggestedDialogFilters":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getTopReactions":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getUnreadMentions":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getUnreadPollVotes":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getUnreadReactions":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getWebPage":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.getWebPagePreview":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.hideAllChatJoinRequests":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.hideChatJoinRequest":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.hidePeerSettingsBar":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.importChatInvite":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.initHistoryImport":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.installStickerSet":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.markDialogUnread":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.migrateChat":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.prolongWebView":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.rateTranscribedAudio":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.readDiscussion":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.readEncryptedHistory":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.readFeaturedStickers":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.readHistory":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.readMentions":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.readMessageContents":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.readPollVotes":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.readReactions":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.readSavedHistory":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.receivedMessages":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.receivedQueue":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.reorderPinnedDialogs":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.reorderPinnedForumTopics":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.reorderPinnedSavedDialogs":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.reorderQuickReplies":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.reorderStickerSets":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.report":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.reportEncryptedSpam":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.reportMessagesDelivery":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.reportMusicListen":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.reportReaction":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.reportReadMetrics":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.reportSpam":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.reportSponsoredMessage":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.requestAppWebView":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.requestEncryption":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.requestMainWebView":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.requestSimpleWebView":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.requestUrlAuth":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.requestWebView":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.saveDefaultSendAs":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.saveDraft":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.saveGif":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.savePreparedInlineMessage":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.saveRecentSticker":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.search":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.searchCustomEmoji":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.searchEmojiStickerSets":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.searchGlobal":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.searchSentMedia":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.searchStickers":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.searchStickerSets":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.sendBotRequestedPeer":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.sendEncrypted":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.sendEncryptedFile":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.sendEncryptedService":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.sendInlineBotResult":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.sendMedia":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.sendMessage":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.sendMultiMedia":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.sendPaidReaction":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.sendQuickReplyMessages":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.sendReaction":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.sendScheduledMessages":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.sendScreenshotNotification":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.sendVote":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.sendWebViewData":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.sendWebViewResultMessage":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.setBotCallbackAnswer":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.setBotGuestChatResult":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.setBotPrecheckoutResults":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.setBotShippingResults":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.setChatAvailableReactions":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.setChatTheme":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.setChatWallPaper":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.setDefaultHistoryTTL":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.setDefaultReaction":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.setEncryptedTyping":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.setGameScore":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.setHistoryTTL":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.setInlineBotResults":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.setInlineGameScore":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.setTyping":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.startBot":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.startHistoryImport":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.summarizeText":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.toggleBotInAttachMenu":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.toggleDialogFilterTags":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.toggleDialogPin":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.toggleNoForwards":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.togglePaidReactionPrivacy":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.togglePeerTranslations":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.toggleSavedDialogPin":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.toggleStickerSets":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.toggleSuggestedPostApproval":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.toggleTodoCompleted":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.transcribeAudio":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.translateText":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.uninstallStickerSet":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.unpinAllMessages":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.updateDialogFilter":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.updateDialogFiltersOrder":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.updatePinnedForumTopic":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.updatePinnedMessage":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.updateSavedReactionTag":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.uploadEncryptedFile":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.uploadImportedMedia":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.uploadMedia":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"messages.viewSponsoredMessage":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.applyGiftCode":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.botCancelStarsSubscription":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.changeStarsSubscription":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.checkCanSendGift":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.checkGiftCode":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.clearSavedInfo":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.connectStarRefBot":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.convertStarGift":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.craftStarGift":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.createStarGiftCollection":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.deleteStarGiftCollection":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.editConnectedStarRefBot":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.exportInvoice":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.fulfillStarsSubscription":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.getBankCardData":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.getConnectedStarRefBot":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.getConnectedStarRefBots":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.getCraftStarGifts":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.getGiveawayInfo":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.getPaymentReceipt":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.getPremiumGiftCodeOptions":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.getResaleStarGifts":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.getSavedInfo":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.getSavedStarGift":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.getSavedStarGifts":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.getStarGiftActiveAuctions":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.getStarGiftAuctionAcquiredGifts":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.getStarGiftAuctionState":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.getStarGiftCollections":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.getStarGifts":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.getStarGiftUpgradeAttributes":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.getStarGiftUpgradePreview":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.getStarGiftWithdrawalUrl":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.getStarsGiftOptions":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.getStarsGiveawayOptions":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.getStarsRevenueAdsAccountUrl":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.getStarsRevenueStats":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.getStarsRevenueWithdrawalUrl":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.getStarsStatus":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.getStarsSubscriptions":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.getStarsTopupOptions":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.getStarsTransactions":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.getStarsTransactionsByID":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.getSuggestedStarRefBots":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.getUniqueStarGift":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.getUniqueStarGiftValueInfo":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.launchPrepaidGiveaway":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.refundStarsCharge":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.reorderStarGiftCollections":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.resolveStarGiftOffer":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.saveStarGift":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.sendStarGiftOffer":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.sendStarsForm":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.toggleChatStarGiftNotifications":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.toggleStarGiftsPinnedToTop":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.transferStarGift":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.updateStarGiftCollection":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.updateStarGiftPrice":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.upgradeStarGift":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"payments.validateRequestedInfo":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"phone.acceptCall":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"phone.checkGroupCall":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"phone.confirmCall":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"phone.createConferenceCall":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"phone.createGroupCall":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"phone.declineConferenceCallInvite":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"phone.deleteConferenceCallParticipants":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"phone.deleteGroupCallMessages":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"phone.deleteGroupCallParticipantMessages":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"phone.discardCall":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"phone.discardGroupCall":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"phone.editGroupCallParticipant":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"phone.editGroupCallTitle":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"phone.exportGroupCallInvite":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"phone.getCallConfig":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"phone.getGroupCall":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"phone.getGroupCallChainBlocks":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"phone.getGroupCallJoinAs":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"phone.getGroupCallStars":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"phone.getGroupCallStreamChannels":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"phone.getGroupCallStreamRtmpUrl":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"phone.getGroupParticipants":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"phone.inviteConferenceCallParticipant":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"phone.inviteToGroupCall":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"phone.joinGroupCall":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"phone.joinGroupCallPresentation":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"phone.leaveGroupCall":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"phone.leaveGroupCallPresentation":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"phone.receivedCall":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"phone.requestCall":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"phone.saveCallDebug":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"phone.saveCallLog":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"phone.saveDefaultGroupCallJoinAs":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"phone.saveDefaultSendAs":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"phone.sendConferenceCallBroadcast":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"phone.sendGroupCallEncryptedMessage":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"phone.sendGroupCallMessage":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"phone.sendSignalingData":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"phone.setCallRating":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"phone.startScheduledGroupCall":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"phone.toggleGroupCallRecord":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"phone.toggleGroupCallSettings":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"phone.toggleGroupCallStartSubscription":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"photos.deletePhotos":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"photos.getUserPhotos":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"photos.updateProfilePhoto":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"photos.uploadContactProfilePhoto":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"photos.uploadProfilePhoto":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"premium.applyBoost":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"premium.getBoostsList":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"premium.getBoostsStatus":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"premium.getMyBoosts":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"premium.getUserBoosts":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"smsjobs.finishJob":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"smsjobs.getSmsJob":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"smsjobs.getStatus":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"smsjobs.isEligibleToJoin":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"smsjobs.join":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"smsjobs.leave":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"smsjobs.updateSettings":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stats.getBroadcastStats":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stats.getMegagroupStats":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stats.getMessagePublicForwards":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stats.getMessageStats":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stats.getPollStats":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stats.getStoryPublicForwards":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stats.getStoryStats":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stats.loadAsyncGraph":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stickers.addStickerToSet":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stickers.changeSticker":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stickers.changeStickerPosition":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stickers.checkShortName":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stickers.createStickerSet":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stickers.deleteStickerSet":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stickers.removeStickerFromSet":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stickers.renameStickerSet":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stickers.replaceSticker":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stickers.setStickerSetThumb":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stickers.suggestShortName":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stories.activateStealthMode":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stories.canSendStory":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stories.createAlbum":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stories.deleteAlbum":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stories.deleteStories":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stories.editStory":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stories.exportStoryLink":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stories.getAlbums":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stories.getAlbumStories":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stories.getAllReadPeerStories":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stories.getAllStories":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stories.getChatsToSend":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stories.getPeerMaxIDs":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stories.getPeerStories":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stories.getPinnedStories":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stories.getStoriesArchive":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stories.getStoriesByID":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stories.getStoriesViews":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stories.getStoryReactionsList":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stories.getStoryViewsList":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stories.incrementStoryViews":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stories.readStories":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stories.reorderAlbums":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stories.report":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stories.searchPosts":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stories.sendReaction":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stories.sendStory":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stories.startLive":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stories.toggleAllStoriesHidden":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stories.togglePeerStoriesHidden":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stories.togglePinned":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stories.togglePinnedToTop":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"stories.updateAlbum":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"updates.getChannelDifference":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"updates.getDifference":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"updates.getState":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"upload.getCdnFile":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"upload.getCdnFileHashes":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"upload.getFile":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"upload.getFileHashes":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"upload.getWebFile":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"upload.reuploadCdnFile":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"upload.saveBigFilePart":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"upload.saveFilePart":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"users.getFullUser":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"users.getRequirementsToContact":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"users.getSavedMusic":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"users.getSavedMusicByID":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"users.getUsers":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"users.setSecureValueErrors":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"},"users.suggestBirthday":{"AUTH_KEY_UNREGISTERED":"AUTH_KEY_UNREGISTERED"}},"404":{"upload.getCdnFile":{"METHOD_INVALID":"METHOD_INVALID"},"messages.sendMessage":{"PEER_ID_INVALID":"PEER_ID_INVALID"}},"-503":{"messages.getBotCallbackAnswer":{"Timeout":"Timeout"},"messages.getInlineBotResults":{"Timeout":"Timeout"}}},"human_result":{"2FA_CONFIRM_WAIT_%d":"Since this account is active and protected by a 2FA password, we will delete it in 1 week for security purposes. You can cancel this process at any time, you'll be able to reset your account in %d seconds.","ABOUT_TOO_LONG":"About string too long","ACCESS_DENIED":"The account was deactivated, or is a bot\/service account.","ACCESS_TOKEN_EXPIRED":"Access token expired","ACCESS_TOKEN_INVALID":"Access token invalid","AD_EXPIRED":"The ad has expired (too old or not found).","ADDRESS_INVALID":"The specified geopoint address is invalid","ADMIN_ID_INVALID":"The specified admin ID is invalid","ADMIN_RANK_EMOJI_NOT_ALLOWED":"An admin rank cannot contain emojis","ADMIN_RANK_INVALID":"The specified admin rank is invalid.","ADMIN_RIGHTS_EMPTY":"The chatAdminRights constructor passed in keyboardButtonRequestPeer.peer_type.user_admin_rights has no rights set (i.e. flags is 0).","ADMINS_TOO_MUCH":"There are too many admins","AI_COMPOSE_TASK_MISSING":"No AI task was specified. The caller must provide at least one of: proofread, translate (with a target language), tone, or emojify.","AICOMPOSE_FLOOD_PREMIUM":"You've reached the daily limit of AI text transformations, upgrade to [Telegram Premium](https:\/\/core.telegram.org\/api\/premium) to get **50x** times more AI text transformations per day!","AICOMPOSE_TIMEOUT":"A timeout occurred while composing the message","AICOMPOSE_TONE_INVALID":"The specified tone is invalid","AICOMPOSE_TONE_TITLE_INVALID":"The specified tone title is invalid","ALBUM_PHOTOS_TOO_MANY":"You have uploaded too many profile photos, delete some before retrying.","ALLOW_PAYMENT_REQUIRED":"This peer only accepts [paid messages &raquo;](https:\/\/core.telegram.org\/api\/paid-messages): this error is only emitted for older layers without paid messages support, so the client must be updated in order to use paid messages.  ","ALLOW_PAYMENT_REQUIRED_%d":"This peer charges %d [Telegram Stars](https:\/\/core.telegram.org\/api\/stars) per message, but the `allow_paid_stars` was not set or its value is smaller than %d.","API_GIFT_RESTRICTED_UPDATE_APP":"Please update the app to access the gift API.","API_ID_INVALID":"API ID invalid","API_ID_PUBLISHED_FLOOD":"This API id was published somewhere, you can't use it now","ARTICLE_TITLE_EMPTY":"The title of the article is empty","AUDIO_CONTENT_URL_EMPTY":"The remote URL specified in the content field is empty","AUDIO_TITLE_EMPTY":"An empty audio title was provided.","AUTH_BYTES_INVALID":"The provided authorization is invalid","AUTH_KEY_DUPLICATED":"Concurrent usage of the current session from multiple connections was detected, the current session was invalidated by the server for security reasons!","AUTH_KEY_INVALID":"The specified auth key is invalid","AUTH_KEY_PERM_EMPTY":"The method is unavailable for temporary authorization keys, not bound to a permanent authorization key","AUTH_KEY_UNREGISTERED":"The specified authorization key is not registered in the system (for example, a PFS temporary key has expired)","AUTH_KEY_UNSYNCHRONIZED":"Internal error, please repeat the method call.","AUTH_RESTART":"Restart the authorization process","AUTH_RESTART_%d":"Internal error (debug info %d), please repeat the method call.","AUTH_TOKEN_ALREADY_ACCEPTED":"The specified auth token was already accepted","AUTH_TOKEN_EXCEPTION":"An error occurred while importing the auth token","AUTH_TOKEN_EXPIRED":"The authorization token has expired","AUTH_TOKEN_INVALID":"The specified auth token is invalid","AUTH_TOKEN_INVALIDX":"The specified auth token is invalid","AUTOARCHIVE_NOT_AVAILABLE":"The autoarchive setting is not available at this time: please check the value of the [autoarchive_setting_available field in client config &raquo;](https:\/\/core.telegram.org\/api\/config#client-configuration) before calling this method.","BALANCE_TOO_LOW":"The transaction cannot be completed because the current [Telegram Stars balance](https:\/\/core.telegram.org\/api\/stars) is too low.","BANK_CARD_NUMBER_INVALID":"The specified card number is invalid","BANNED_RIGHTS_INVALID":"You provided some invalid flags in the banned rights","BIRTHDAY_ALREADY":"The target user already has a birthday set","BIRTHDAY_INVALID":"An invalid age was specified, must be between 0 and 150 years.","BOOST_NOT_MODIFIED":"You're already [boosting](https:\/\/core.telegram.org\/api\/boost) the specified channel.","BOOST_PEER_INVALID":"The specified `boost_peer` is invalid","BOOSTS_EMPTY":"No boost slots were specified","BOOSTS_REQUIRED":"The specified channel must first be [boosted by its users](https:\/\/core.telegram.org\/api\/boost) in order to perform this action.","BOT_ACCESS_FORBIDDEN":"The specified method *can* be used over a [business connection](https:\/\/core.telegram.org\/api\/bots\/connected-business-bots) for some operations, but the specified query attempted an operation that is not allowed over a business connection.","BOT_ALREADY_DISABLED":"The connected business bot was already disabled for the specified peer.","BOT_APP_BOT_INVALID":"The bot_id passed in the inputBotAppShortName constructor is invalid.","BOT_APP_INVALID":"The specified bot app is invalid","BOT_APP_SHORTNAME_INVALID":"The specified bot app short name is invalid","BOT_BUSINESS_MISSING":"The specified bot is not a business bot (the [user](https:\/\/core.telegram.org\/constructor\/user).`bot_business` flag is not set).","BOT_CHANNELS_NA":"Bots can't edit admin privileges","BOT_COMMAND_DESCRIPTION_INVALID":"The specified command description is invalid","BOT_COMMAND_INVALID":"The specified command is invalid","BOT_CREATE_LIMIT_EXCEEDED":"The current user already owns the maximum allowed number of owned bots, as specified by [`bots_create_limit_default` &raquo;](https:\/\/core.telegram.org\/api\/config#bots-create-limit-default) and [`bots_create_limit_premium` &raquo;](https:\/\/core.telegram.org\/api\/config#bots-create-limit-premium); if the current user doesn't have Telegram Premium, upgrading to Premium will allow them to create more bots.","BOT_DOMAIN_INVALID":"Bot domain invalid","BOT_FALLBACK_UNSUPPORTED":"The fallback flag can't be set for bots","BOT_FORUM_CREATE_FORBIDDEN":"Since the bot's user.bot_forum_can_manage_topics flag is **not** set, the user cannot create or modify bot forum topics.","BOT_GAMES_DISABLED":"Games can't be sent to channels.","BOT_GROUPS_BLOCKED":"This bot can't be added to groups","BOT_GUARD_NOT_SUPPORTED":"This bot is not designated as a \"join guard\" bot. This method is only available to bots that mediate user joins to chats. ","BOT_ID_INVALID":"The specified bot ID is invalid","BOT_INLINE_DISABLED":"This bot can't be used in inline mode","BOT_INVALID":"This is not a valid bot","BOT_INVOICE_INVALID":"The specified invoice is invalid","BOT_METHOD_INVALID":"The specified method cannot be used by bots.","BOT_NOT_CONNECTED_YET":"No [business bot](https:\/\/core.telegram.org\/api\/business#connected-bots) is connected to the currently logged in user.","BOT_ONESIDE_NOT_AVAIL":"Bots can't pin messages in PM just for themselves","BOT_PAYMENTS_DISABLED":"Please enable bot payments in botfather before calling this method.","BOT_RESPONSE_TIMEOUT":"A timeout occurred while fetching data from the bot","BOT_SCORE_NOT_MODIFIED":"The score wasn't modified","BOT_VERIFIER_FORBIDDEN":"This bot cannot assign [verification icons](https:\/\/core.telegram.org\/api\/bots\/verification)","BOT_WEBVIEW_DISABLED":"A webview cannot be opened in the specified conditions: emitted for example if `from_bot_menu` or `url` are set and `peer` is not the chat with the bot.","BOTS_TOO_MUCH":"There are too many bots in this chat\/channel","BROADCAST_FORBIDDEN":"Channel poll voters and reactions cannot be fetched to prevent deanonymization.","BROADCAST_ID_INVALID":"Broadcast ID invalid","BROADCAST_PUBLIC_VOTERS_FORBIDDEN":"You can't forward polls with public voters","BROADCAST_REQUIRED":"This method can only be called on a channel, please use stats.getMegagroupStats for supergroups","BUSINESS_ADDRESS_ACTIVE":"The user is currently advertising a [Business Location](https:\/\/core.telegram.org\/api\/business#location), the location may only be changed (or removed) using [account.updateBusinessLocation &raquo;](https:\/\/core.telegram.org\/method\/account.updateBusinessLocation).  ","BUSINESS_CONNECTION_INVALID":"The `connection_id` passed to the wrapping [invokeWithBusinessConnection](https:\/\/core.telegram.org\/api\/business) call is invalid.","BUSINESS_CONNECTION_NOT_ALLOWED":"This method was invoked over a business connection using [invokeWithBusinessConnection](https:\/\/core.telegram.org\/api\/business#connected-bots), but either (1) we're a user, and users cannot invoke methods over a business connection; (2) we're a bot, but business mode was disabled in @botfather or (3); we're a bot, but this method cannot be invoked over a business connection.","BUSINESS_PEER_INVALID":"Messages can't be set to the specified peer through the current [business connection](https:\/\/core.telegram.org\/api\/business#connected-bots).","BUSINESS_PEER_USAGE_MISSING":"You cannot send a message to a user through a [business connection](https:\/\/core.telegram.org\/api\/business#connected-bots) if the user hasn't recently contacted us.","BUSINESS_RECIPIENTS_EMPTY":"You didn't set any flag in inputBusinessBotRecipients, thus the bot cannot work with *any* peer.","BUSINESS_WORK_HOURS_EMPTY":"No work hours were specified","BUSINESS_WORK_HOURS_PERIOD_INVALID":"The specified work hours are invalid, see [here &raquo;](https:\/\/core.telegram.org\/api\/business#opening-hours) for the exact requirements","BUTTON_COPY_TEXT_INVALID":"The specified [keyboardButtonCopy](https:\/\/core.telegram.org\/constructor\/keyboardButtonCopy).`copy_text` is invalid.","BUTTON_DATA_INVALID":"The data of one or more of the buttons you provided is invalid","BUTTON_ID_INVALID":"The specified button ID is invalid.","BUTTON_INVALID":"The specified button is invalid","BUTTON_POS_INVALID":"The position of one of the keyboard buttons is invalid (i.e. a Game or Pay button not in the first position, and so on...)","BUTTON_TEXT_INVALID":"The specified button text is invalid","BUTTON_TYPE_INVALID":"The type of one or more of the buttons you provided is invalid","BUTTON_URL_INVALID":"Button URL invalid","BUTTON_USER_INVALID":"The `user_id` passed to inputKeyboardButtonUserProfile is invalid!","BUTTON_USER_PRIVACY_RESTRICTED":"The privacy setting of the user specified in a [inputKeyboardButtonUserProfile](https:\/\/core.telegram.org\/constructor\/inputKeyboardButtonUserProfile) button do not allow creating such a button","CALL_ALREADY_ACCEPTED":"The call was already accepted","CALL_ALREADY_DECLINED":"The call was already declined","CALL_NOT_ACTIVE":"The specified call is not active","CALL_OCCUPY_FAILED":"The call failed because the user is already making another call","CALL_PEER_INVALID":"The provided call peer object is invalid","CALL_PROTOCOL_COMPAT_LAYER_INVALID":"The other side of the call does not support any of the VoIP protocols supported by the local client, as specified by the `protocol.layer` and `protocol.library_versions` fields.","CALL_PROTOCOL_FLAGS_INVALID":"Call protocol flags invalid","CALL_PROTOCOL_LAYER_INVALID":"The specified protocol layer version range is invalid","CDN_METHOD_INVALID":"You can't call this method in a CDN DC","CDN_UPLOAD_TIMEOUT":"A server-side timeout occurred while reuploading the file to the CDN DC","CHANNEL_FORUM_MISSING":"This supergroup is not a forum","CHANNEL_ID_INVALID":"The specified supergroup ID is invalid","CHANNEL_INVALID":"The provided channel is invalid","CHANNEL_MONOFORUM_UNSUPPORTED":"[Monoforums](https:\/\/core.telegram.org\/api\/channel#monoforums) do not support this feature.","CHANNEL_PARICIPANT_MISSING":"The current user is not in the channel","CHANNEL_PRIVATE":"You haven't joined this channel\/supergroup","CHANNEL_PUBLIC_GROUP_NA":"channel\/supergroup not available","CHANNEL_TOO_BIG":"This channel has too many participants (>1000) to be deleted.","CHANNEL_TOO_LARGE":"Channel is too large to be deleted; this error is issued when trying to delete channels with more than 1000 members (subject to change)","CHANNELS_ADMIN_LOCATED_TOO_MUCH":"The user has reached the limit of public geogroups","CHANNELS_ADMIN_PUBLIC_TOO_MUCH":"You're admin of too many public channels, make some channels private to change the username of this channel","CHANNELS_TOO_MUCH":"You have joined too many channels\/supergroups","CHARGE_ALREADY_REFUNDED":"The transaction was already refunded","CHARGE_ID_EMPTY":"The specified charge_id is empty","CHARGE_ID_INVALID":"The specified charge_id is invalid","CHAT_ABOUT_NOT_MODIFIED":"About text has not changed","CHAT_ABOUT_TOO_LONG":"Chat about too long","CHAT_ACTION_FORBIDDEN":"You cannot execute this action","CHAT_ADMIN_INVITE_REQUIRED":"You do not have the rights to do this","CHAT_ADMIN_REQUIRED":"You must be an admin in this chat to do this","CHAT_DISCUSSION_UNALLOWED":"You can't enable forum topics in a discussion group linked to a channel","CHAT_FORBIDDEN":"This chat is not available to the current user","CHAT_FORWARDS_RESTRICTED":"You can't forward messages from a protected chat","CHAT_GUEST_SEND_FORBIDDEN":"You join the discussion group before commenting, see [here &raquo;](https:\/\/core.telegram.org\/api\/discussion#requiring-users-to-join-the-group) for more info.","CHAT_ID_EMPTY":"The provided chat ID is empty","CHAT_ID_GENERATE_FAILED":"Failure while generating the chat ID","CHAT_ID_INVALID":"The provided chat id is invalid","CHAT_INVALID":"Invalid chat","CHAT_INVITE_PERMANENT":"You can't set an expiration date on permanent invite links","CHAT_LINK_EXISTS":"The chat is public, you can't hide the history to new users","CHAT_MEMBER_ADD_FAILED":"Could not add participants.","CHAT_NOT_MODIFIED":"No changes were made to chat information because the new information you passed is identical to the current information.","CHAT_PUBLIC_REQUIRED":"You can only enable join requests in public groups","CHAT_RESTRICTED":"You can't send messages in this chat, you were restricted","CHAT_REVOKE_DATE_UNSUPPORTED":"`min_date` and `max_date` are not available for using with non-user peers.","CHAT_SEND_AUDIOS_FORBIDDEN":"You can't send audio messages in this chat","CHAT_SEND_DOCS_FORBIDDEN":"You can't send documents in this chat","CHAT_SEND_GAME_FORBIDDEN":"You can't send a game to this chat.","CHAT_SEND_GIFS_FORBIDDEN":"You can't send gifs in this chat","CHAT_SEND_INLINE_FORBIDDEN":"You can't send inline messages in this group","CHAT_SEND_MEDIA_FORBIDDEN":"You can't send media in this chat","CHAT_SEND_PHOTOS_FORBIDDEN":"You can't send photos in this chat","CHAT_SEND_PLAIN_FORBIDDEN":"You can't send non-media (text) messages in this chat","CHAT_SEND_POLL_FORBIDDEN":"You can't send polls in this chat","CHAT_SEND_ROUNDVIDEOS_FORBIDDEN":"You can't send round videos to this chat","CHAT_SEND_STICKERS_FORBIDDEN":"You can't send stickers in this chat.","CHAT_SEND_VIDEOS_FORBIDDEN":"You can't send videos in this chat","CHAT_SEND_VOICES_FORBIDDEN":"You can't send voice recordings in this chat","CHAT_SEND_WEBPAGE_FORBIDDEN":"You can't send webpage previews to this chat","CHAT_TITLE_EMPTY":"No chat title provided","CHAT_TOO_BIG":"This method is not available for groups with more than `chat_read_mark_size_threshold` members, [see client configuration &raquo;](https:\/\/core.telegram.org\/api\/config#client-configuration).","CHAT_TYPE_INVALID":"The specified user type is invalid","CHAT_WRITE_FORBIDDEN":"You can't write in this chat","CHATLINK_SLUG_EMPTY":"The specified slug is empty","CHATLINK_SLUG_EXPIRED":"The specified [business chat link](https:\/\/core.telegram.org\/api\/business#business-chat-links) has expired","CHATLINKS_TOO_MUCH":"Too many [business chat links](https:\/\/core.telegram.org\/api\/business#business-chat-links) were created, please delete some older links","CHATLIST_EXCLUDE_INVALID":"The specified `exclude_peers` are invalid","CHATLISTS_TOO_MUCH":"You have created too many folder links, hitting the `chatlist_invites_limit_default`\/`chatlist_invites_limit_premium` [limits &raquo;](https:\/\/core.telegram.org\/api\/config#chatlist-invites-limit-default)","CODE_EMPTY":"The provided code is empty","CODE_HASH_INVALID":"Code hash invalid","CODE_INVALID":"Code invalid","COLLECTIBLE_INVALID":"The specified collectible is invalid","COLLECTIBLE_NOT_FOUND":"The specified collectible could not be found","COLLECTION_ID_INVALID":"The specified collection ID is invalid","COLOR_INVALID":"The specified color palette ID was invalid.","CONNECTION_API_ID_INVALID":"The provided API id is invalid","CONNECTION_APP_VERSION_EMPTY":"App version is empty","CONNECTION_DEVICE_MODEL_EMPTY":"The specified device model is empty","CONNECTION_ID_INVALID":"The specified connection ID is invalid","CONNECTION_LANG_PACK_INVALID":"The specified language pack is empty","CONNECTION_LAYER_INVALID":"Layer invalid","CONNECTION_NOT_INITED":"Please initialize the connection using initConnection before making queries.","CONNECTION_SYSTEM_EMPTY":"The specified system version is empty","CONNECTION_SYSTEM_LANG_CODE_EMPTY":"The specified system language code is empty","CONTACT_ADD_MISSING":"Contact to add is missing","CONTACT_ID_INVALID":"The provided contact ID is invalid","CONTACT_MISSING":"The specified user is not a contact","CONTACT_NAME_EMPTY":"Contact name empty","CONTACT_REQ_MISSING":"Missing contact request","CREATE_CALL_FAILED":"An error occurred while creating the call.","CREDENTIAL_INVALID":"The specified credential is invalid","CURRENCY_TOTAL_AMOUNT_INVALID":"The total amount of all prices is invalid","CUSTOM_REACTIONS_TOO_MANY":"Too many custom reactions were specified.","DATA_HASH_SIZE_INVALID":"The size of the specified secureValueErrorData.data_hash is invalid","DATA_INVALID":"Encrypted data invalid","DATA_JSON_INVALID":"The provided JSON data is invalid","DATA_TOO_LONG":"Data too long","DATE_EMPTY":"Date empty","DC_ID_INVALID":"The provided DC ID is invalid","DH_G_A_INVALID":"g_a invalid","DOCUMENT_INVALID":"The specified document is invalid","EDIT_BOT_INVITE_FORBIDDEN":"Normal users can't edit invites that were created by bots","EDIT_MESSAGE_TEMP_RESTRICTED":"Message editing is temporarily forbidden for this user due to regulatory restrictions.","EFFECT_CHAT_INVALID":"Message [effects](https:\/\/core.telegram.org\/api\/effects) can only be used in private 1-on-1 chats, but the caller tried to send a message with an effect to a group or channel.","EFFECT_ID_INVALID":"The specified effect ID is invalid","EMAIL_HASH_EXPIRED":"Email hash expired","EMAIL_INSTALL_MISSING":"Attempting to send a code to the recovery email, but no email is configured","EMAIL_INVALID":"The specified email is invalid","EMAIL_NOT_ALLOWED":"The specified email cannot be used to complete the operation.","EMAIL_NOT_SETUP":"In order to change the login email with emailVerifyPurposeLoginChange, an existing login email must already be set using emailVerifyPurposeLoginSetup","EMAIL_UNCONFIRMED":"Email unconfirmed","EMAIL_UNCONFIRMED_%d":"The provided email isn't confirmed, %d is the length of the verification code that was just sent to the email: use [account.verifyEmail](https:\/\/core.telegram.org\/method\/account.verifyEmail) to enter the received verification code and enable the recovery email.","EMAIL_VERIFY_EXPIRED":"The verification email has expired","EMOJI_INVALID":"The specified theme emoji is valid","EMOJI_MARKUP_INVALID":"The specified `video_emoji_markup` was invalid","EMOJI_NOT_MODIFIED":"The theme wasn't changed","EMOTICON_EMPTY":"The emoji is empty","EMOTICON_INVALID":"The specified emoji is invalid","EMOTICON_STICKERPACK_MISSING":"inputStickerSetDice.emoji cannot be empty","ENCRYPTED_MESSAGE_INVALID":"Encrypted message invalid","ENCRYPTION_ALREADY_ACCEPTED":"Secret chat already accepted","ENCRYPTION_ALREADY_DECLINED":"The secret chat was already declined","ENCRYPTION_DECLINED":"The secret chat was declined","ENCRYPTION_ID_INVALID":"The provided secret chat ID is invalid","ENTITIES_TOO_LONG":"You provided too many styled message entities","ENTITY_BOUNDS_INVALID":"A specified [entity offset or length](https:\/\/core.telegram.org\/api\/entities#entity-length) is invalid, see [here &raquo;](https:\/\/core.telegram.org\/api\/entities#entity-length) for info on how to properly compute the entity offset\/length","ENTITY_DATE_FORMAT_INVALID":"One of the passed messageEntityFormattedDate objects has an invalid format (i.e. an invalid combination of the format flags)","ENTITY_DATE_INVALID":"One of the passed messageEntityFormattedDate objects has an invalid date: the allowed value ranges from `0` to the current date plus 1098 days (`time()+1098*86400`).","ENTITY_DATE_TOO_LONG":"The maximum text span that can be covered by a date entity is 31 UTF-16 code units if any of the date formatting flags is set, or 127 UTF-16 code units without.  ","ENTITY_MENTION_USER_INVALID":"You mentioned an invalid user","ERROR_TEXT_EMPTY":"The provided error message is empty","EXPIRE_DATE_INVALID":"The specified expiration date is invalid","EXPIRES_AT_INVALID":"The specified `expires_at` timestamp is invalid","EXPORT_CARD_INVALID":"Provided card is invalid","EXTENDED_MEDIA_AMOUNT_INVALID":"The specified `stars_amount` of the passed [inputMediaPaidMedia](https:\/\/core.telegram.org\/constructor\/inputMediaPaidMedia) is invalid","EXTENDED_MEDIA_EMPTY":"The specified extended media is empty","EXTENDED_MEDIA_INVALID":"The specified paid media is invalid.","EXTENDED_MEDIA_PEER_INVALID":"Paid media is not allowed for the target peer.","EXTERNAL_URL_INVALID":"External URL invalid","FILE_CONTENT_TYPE_INVALID":"File content-type is invalid","FILE_EMTPY":"An empty file was provided","FILE_ID_INVALID":"The provided file id is invalid","FILE_MIGRATE_%d":"The file currently being accessed is stored in DC %d, please re-send the query to that DC.","FILE_PART_%d_MISSING":"Part %d of the file is missing from storage. Try repeating the method call to resave the part.","FILE_PART_EMPTY":"The provided file part is empty","FILE_PART_INVALID":"The file part number is invalid","FILE_PART_LENGTH_INVALID":"The length of a file part is invalid","FILE_PART_SIZE_CHANGED":"Provided file part size has changed","FILE_PART_SIZE_INVALID":"The provided file part size is invalid","FILE_PART_TOO_BIG":"The uploaded file part is too big","FILE_PART_TOO_SMALL":"The size of the uploaded file part is too small, please see the documentation for the allowed sizes","FILE_PARTS_INVALID":"The number of file parts is invalid","FILE_REFERENCE_%d_EMPTY":"The file reference of the media file at offset %d in the multi_media array is invalid","FILE_REFERENCE_%d_EXPIRED":"The file reference of the media file at index %d in the passed media array expired, it [must be refreshed as specified in the documentation](https:\/\/core.telegram.org\/api\/file-references). ","FILE_REFERENCE_%d_INVALID":"The [file reference](https:\/\/core.telegram.org\/api\/file-references) of the media file at index %d in the passed media array is invalid.","FILE_REFERENCE_EMPTY":"An empty [file reference](https:\/\/core.telegram.org\/api\/file-references) was specified.","FILE_REFERENCE_EXPIRED":"File reference expired, it must be refetched as described in [the documentation](https:\/\/core.telegram.org\/api\/file-references).","FILE_REFERENCE_INVALID":"The specified [file reference](https:\/\/core.telegram.org\/api\/file-references) is invalid","FILE_TITLE_EMPTY":"An empty file title was specified","FILE_TOKEN_INVALID":"The master DC did not accept the `file_token` (e.g., the token has expired). Continue downloading the file from the master DC using upload.getFile.","FILEREF_UPGRADE_NEEDED":"The client has to be updated in order to support [file references](https:\/\/core.telegram.org\/api\/file-references)","FILTER_ID_INVALID":"The specified filter ID is invalid","FILTER_INCLUDE_EMPTY":"The include_peers vector of the filter is empty","FILTER_NOT_SUPPORTED":"The specified filter cannot be used in this context","FILTER_TITLE_EMPTY":"The title field of the filter is empty","FIRSTNAME_INVALID":"The first name is invalid","FLOOD_PREMIUM_WAIT_%d":"Please wait %d seconds before repeating the action, or purchase a [Telegram Premium subscription](https:\/\/core.telegram.org\/api\/premium) to remove this rate limit.","FLOOD_WAIT_%d":"Please wait %d seconds before repeating the action.","FOLDER_ID_EMPTY":"An empty folder ID was specified","FOLDER_ID_INVALID":"Invalid folder ID","FORM_EXPIRED":"The form was generated more than 10 minutes ago and has expired, please re-generate it using [payments.getPaymentForm](https:\/\/core.telegram.org\/method\/payments.getPaymentForm) and pass the new `form_id`.","FORM_ID_EMPTY":"The specified form ID is empty","FORM_SUBMIT_DUPLICATE":"The same payment form was already submitted.  ","FORM_UNSUPPORTED":"Please update your client","FORUM_ENABLED":"You can't execute the specified action because the group is a [forum](https:\/\/core.telegram.org\/api\/forum), disable forum functionality to continue.","FRESH_CHANGE_ADMINS_FORBIDDEN":"You were just elected admin, you can't add or modify other admins yet","FRESH_CHANGE_PHONE_FORBIDDEN":"You can't change phone number right after logging in, please wait at least 24 hours.","FRESH_RESET_AUTHORISATION_FORBIDDEN":"You can't logout other sessions if less than 24 hours have passed since you logged on the current session","FROM_MESSAGE_BOT_DISABLED":"Bots can't use fromMessage min constructors","FROM_PEER_INVALID":"The specified from_id is invalid","FROZEN_METHOD_INVALID":"The current account is [frozen](https:\/\/core.telegram.org\/api\/auth#frozen-accounts), and thus cannot execute the specified action.","FROZEN_PARTICIPANT_MISSING":"The current account is [frozen](https:\/\/core.telegram.org\/api\/auth#frozen-accounts), and cannot access the specified peer.","GAME_BOT_INVALID":"Bots can't send another bot's game.","GENERAL_MODIFY_ICON_FORBIDDEN":"You can't modify the icon of the \"General\" topic","GEO_POINT_INVALID":"Invalid geoposition provided","GIF_CONTENT_TYPE_INVALID":"GIF content-type invalid","GIF_ID_INVALID":"The provided GIF ID is invalid","GIFT_MONTHS_INVALID":"The value passed in invoice.inputInvoicePremiumGiftStars.months is invalid","GIFT_SLUG_EXPIRED":"The specified gift slug has expired","GIFT_SLUG_INVALID":"The specified slug is invalid.","GIFT_STARS_INVALID":"The specified amount of stars is invalid","GRAPH_EXPIRED_RELOAD":"This graph has expired, please obtain a new graph token","GRAPH_INVALID_RELOAD":"Invalid graph token provided, please reload the stats and provide the updated token","GRAPH_OUTDATED_RELOAD":"The graph is outdated, please get a new async token using stats.getBroadcastStats","GROUPCALL_ALREADY_DISCARDED":"The group call was already discarded","GROUPCALL_ALREADY_STARTED":"The groupcall has already started, you can join directly using [phone.joinGroupCall](https:\/\/core.telegram.org\/method\/phone.joinGroupCall)","GROUPCALL_CHANGE_FORBIDDEN":"You cannot change this group call setting","GROUPCALL_FORBIDDEN":"The specified group call cannot be used in this context","GROUPCALL_INVALID":"The specified group call is invalid","GROUPCALL_JOIN_MISSING":"You haven't joined this group call.","GROUPCALL_NOT_MODIFIED":"Group call settings weren't modified","GROUPCALL_SSRC_DUPLICATE_MUCH":"The app needs to retry joining the group call with a new SSRC value.","GROUPED_MEDIA_INVALID":"Invalid grouped media","HASH_INVALID":"The provided hash is invalid","HASH_SIZE_INVALID":"The size of the specified secureValueError.hash is invalid","HASHTAG_INVALID":"The specified hashtag is invalid","HIDE_REQUESTER_MISSING":"The join request was missing or was already handled.","ID_EXPIRED":"The passed prepared inline message ID has expired","ID_INVALID":"The passed ID is invalid","IMAGE_PROCESS_FAILED":"Failure while processing image","IMPORT_FILE_INVALID":"The specified chat export file is invalid","IMPORT_FORMAT_DATE_INVALID":"The date specified in the import file is invalid.","IMPORT_FORMAT_UNRECOGNIZED":"The specified chat export file was exported from an unsupported chat app","IMPORT_ID_INVALID":"The specified import ID is invalid","IMPORT_TOKEN_INVALID":"The specified token is invalid","INLINE_BOT_REQUIRED":"Only the inline bot can edit message","INLINE_RESULT_EXPIRED":"The inline query expired","INPUT_CHATLIST_INVALID":"The specified folder is invalid","INPUT_CONSTRUCTOR_INVALID":"The specified TL constructor is invalid","INPUT_FETCH_ERROR":"An error occurred while parsing the provided TL constructor","INPUT_FETCH_FAIL":"An error occurred while parsing the provided TL constructor","INPUT_FILE_INVALID":"The specified [InputFile](https:\/\/core.telegram.org\/type\/InputFile) is invalid.","INPUT_FILTER_INVALID":"The specified filter is invalid","INPUT_LAYER_INVALID":"The specified layer is invalid","INPUT_METHOD_INVALID":"The specified method is invalid","INPUT_PEERS_EMPTY":"The specified peer array is empty","INPUT_PURPOSE_INVALID":"The specified payment purpose is invalid","INPUT_REQUEST_TOO_LONG":"The request payload is too long","INPUT_STARS_AMOUNT_INVALID":"The specified offer amount in stars is invalid, see [here &raquo;](https:\/\/core.telegram.org\/api\/gifts#collectible-gift-purchase-offers) for the allowed range","INPUT_STARS_NANOS_INVALID":" The specified offer amount in nanotons is invalid, see [here &raquo;](https:\/\/core.telegram.org\/api\/gifts#collectible-gift-purchase-offers) for the allowed range","INPUT_TEXT_EMPTY":"The specified text is empty","INPUT_TEXT_TOO_LONG":"The specified text is too long","INPUT_USER_DEACTIVATED":"The specified user was deleted","INVITE_FORBIDDEN_WITH_JOINAS":"If the user has anonymously joined a group call as a channel, they can't invite other users to the group call because that would cause deanonymization, because the invite would be sent using the original user ID, not the anonymized channel ID","INVITE_HASH_EMPTY":"The invite hash is empty","INVITE_HASH_EXPIRED":"The invite link has expired","INVITE_HASH_INVALID":"The invite hash is invalid","INVITE_REQUEST_SENT":"You have successfully requested to join this chat or channel","INVITE_REVOKED_MISSING":"The specified invite link was already revoked or is invalid","INVITE_SLUG_EMPTY":"The specified invite slug is empty","INVITE_SLUG_EXPIRED":"The specified chat folder link has expired.","INVITE_SLUG_INVALID":"The specified invitation slug is invalid","INVITES_TOO_MUCH":"The maximum number of per-folder invites specified by the `chatlist_invites_limit_default`\/`chatlist_invites_limit_premium` [client configuration parameters &raquo;](https:\/\/core.telegram.org\/api\/config#chatlist-invites-limit-default) was reached.","INVOICE_INVALID":"The specified invoice is invalid","INVOICE_PAYLOAD_INVALID":"The specified invoice payload is invalid.","JOIN_AS_PEER_INVALID":"The specified peer cannot be used to join a group call","LANG_CODE_INVALID":"The specified language code is invalid","LANG_CODE_NOT_SUPPORTED":"The specified language code is not supported","LANG_PACK_INVALID":"The provided language pack is invalid","LANGUAGE_INVALID":"The specified lang_code is invalid.","LASTNAME_INVALID":"The last name is invalid.","LIMIT_INVALID":"The provided limit is invalid","LIMIT_PER_POST_INVALID":"The specified reactions_limit value is invalid","LINK_NOT_MODIFIED":"Discussion link not modified","LOCATION_INVALID":"The provided location is invalid","MANAGER_INVALID":"The specified manager bot is invalid","MANAGER_PERMISSION_MISSING":"The specified manager bot does not have the [user](https:\/\/core.telegram.org\/constructor\/user).`bot_can_manage_bots` flag set","MAX_DATE_INVALID":"The specified maximum date is invalid.","MAX_ID_INVALID":"The provided max ID is invalid","MAX_QTS_INVALID":"The specified max_qts is invalid","MD5_CHECKSUM_INVALID":"The MD5 checksums do not match","MEDIA_ALREADY_PAID":"You already paid for the specified media","MEDIA_CAPTION_TOO_LONG":"The caption is too long","MEDIA_EMPTY":"The provided media object is invalid","MEDIA_FILE_INVALID":"The specified media file is invalid","MEDIA_GROUPED_INVALID":"You tried to send media of different types in an album.","MEDIA_INVALID":"Media invalid","MEDIA_NEW_INVALID":"The new media is invalid","MEDIA_PREV_INVALID":"Previous media invalid","MEDIA_TTL_INVALID":"The specified media TTL is invalid","MEDIA_TYPE_INVALID":"The specified media type cannot be used in stories","MEDIA_VIDEO_STORY_MISSING":"A non-story video cannot be repubblished as a story (emitted when trying to resend a non-story video as a story using inputDocument).","MEGAGROUP_GEO_REQUIRED":"This method can only be invoked on a geogroup.","MEGAGROUP_ID_INVALID":"Invalid supergroup ID","MEGAGROUP_PREHISTORY_HIDDEN":"Group with hidden history for new members can't be set as discussion groups","MEGAGROUP_REQUIRED":"You can only use this method on a supergroup","MESSAGE_AUTHOR_REQUIRED":"Message author required","MESSAGE_DELETE_FORBIDDEN":"You can't delete one of the messages you tried to delete, most likely because it is a service message.","MESSAGE_EDIT_TIME_EXPIRED":"You can't edit this message anymore, too much time has passed since its creation.","MESSAGE_EMPTY":"The provided message is empty","MESSAGE_ID_INVALID":"The provided message id is invalid","MESSAGE_IDS_EMPTY":"No message ids were provided","MESSAGE_NOT_MODIFIED":"The provided message data is identical to the previous message data, the message wasn't modified","MESSAGE_NOT_READ_YET":"The specified message wasn't read yet.","MESSAGE_POLL_CLOSED":"Poll closed","MESSAGE_REQUIRED":"A non-empty list of IDs must be passed to `id`","MESSAGE_TOO_LONG":"The provided message is too long","MESSAGE_TOO_OLD":"The message is too old, the requested information is not available","METHOD_INVALID":"The specified method is invalid","MIN_DATE_INVALID":"The specified minimum date is invalid","MONTH_INVALID":"The number of months specified in inputInvoicePremiumGiftStars.months is invalid","MSG_ID_INVALID":"Invalid message ID provided","MSG_TOO_OLD":"[`chat_read_mark_expire_period` seconds](https:\/\/core.telegram.org\/api\/config#chat-read-mark-expire-period) have passed since the message was sent, read receipts were deleted","MSG_VOICE_MISSING":"The specified message is not a voice message","MSG_VOICE_TOO_LONG":"The specified voice message is too long to be transcribed","MSG_WAIT_FAILED":"A waiting call returned an error","MSG_WAIT_TIMEOUT":"Spent too much time waiting for a previous query in the invokeAfterMsg request queue, aborting!","MULTI_MEDIA_TOO_LONG":"Too many media files for album","NAME_INVALID":"The specified bot name is invalid","NEED_ACTION_MISSING":"The caller didn't specify a valid action (either save or suggest) for the contact profile photo upload.","NETWORK_MIGRATE_%d":"Your IP address is associated to DC %d, please re-send the query to that DC.","NEW_SALT_INVALID":"The new salt is invalid","NEW_SETTINGS_EMPTY":"No password is set on the current account, and no new password was specified in `new_settings`.","NEW_SETTINGS_INVALID":"The new password settings are invalid.","NEXT_OFFSET_INVALID":"The specified offset is longer than 64 bytes.","NO_PAYMENT_NEEDED":"The upgrade\/transfer of the specified gift was already paid for or is free.","NOGENERAL_HIDE_FORBIDDEN":"Only the \"General\" topic with `id=1` can be hidden.","NOT_ELIGIBLE":"The current user is not eligible to join the Peer-to-Peer Login Program","NOT_JOINED":"The current user hasn't joined the Peer-to-Peer Login Program","OAUTH_REQUEST_INVALID":"The specified OAuth request is invalid","OFFSET_INVALID":"The provided offset is invalid","OFFSET_PEER_ID_INVALID":"The provided offset peer is invalid","OPTION_INVALID":"Invalid option selected","OPTIONS_TOO_MUCH":"Too many options provided","ORDER_INVALID":"The specified username order is invalid","PACK_SHORT_NAME_INVALID":"Short pack name invalid","PACK_SHORT_NAME_OCCUPIED":"A stickerpack with this name already exists","PACK_TITLE_INVALID":"The stickerpack title is invalid","PACK_TYPE_INVALID":"The masks and emojis flags are mutually exclusive","PARENT_PEER_INVALID":"The specified `parent_peer` is invalid.","PARTICIPANT_ID_INVALID":"The specified participant ID is invalid","PARTICIPANT_JOIN_MISSING":"Trying to enable a presentation, when the user hasn't joined the Video Chat with [phone.joinGroupCall](https:\/\/core.telegram.org\/method\/phone.joinGroupCall).","PARTICIPANT_VERSION_OUTDATED":"The other participant does not use an up to date telegram client with support for calls","PARTICIPANTS_TOO_FEW":"Not enough participants","PASSKEY_ORIGIN_MISMATCH":"Third-party clients currently don't support passkeys even when changing the origin","PASSWORD_EMPTY":"The provided password is empty","PASSWORD_HASH_INVALID":"The provided password hash is invalid","PASSWORD_MISSING":"You must [enable 2FA](https:\/\/core.telegram.org\/api\/srp) before executing this operation","PASSWORD_RECOVERY_EXPIRED":"The recovery code has expired","PASSWORD_RECOVERY_NA":"No email was set, can't recover password via email.","PASSWORD_REQUIRED":"A [2FA password](https:\/\/core.telegram.org\/api\/srp) must be configured to use Telegram Passport","PASSWORD_TOO_FRESH_%d":"The password was modified less than 24 hours ago, try again in %d seconds","PAYMENT_CREDENTIALS_INVALID":"The specified payment credentials are invalid","PAYMENT_PROVIDER_INVALID":"The specified payment provider is invalid","PAYMENT_REQUIRED":"Payment is required for this action, see [here &raquo;](https:\/\/core.telegram.org\/api\/gifts) for more info","PAYMENT_UNSUPPORTED":"A detailed description of the error will be received separately as described [here &raquo;](https:\/\/core.telegram.org\/api\/errors#406-not-acceptable)","PEER_FLOOD":"The current account is spamreported, you cannot execute this action, check @spambot for more info.","PEER_HISTORY_EMPTY":"You can't pin an empty chat with a user","PEER_ID_INVALID":"The provided peer id is invalid","PEER_ID_NOT_SUPPORTED":"The provided peer ID is not supported","PEER_TYPES_INVALID":"The passed [keyboardButtonSwitchInline](https:\/\/core.telegram.org\/constructor\/keyboardButtonSwitchInline).`peer_types` field is invalid","PEERS_LIST_EMPTY":"The specified list of peers is empty","PERSISTENT_TIMESTAMP_EMPTY":"Persistent timestamp empty","PERSISTENT_TIMESTAMP_INVALID":"Persistent timestamp invalid","PERSISTENT_TIMESTAMP_OUTDATED":"Channel internal replication issues, try again later (treat this like an RPC_CALL_FAIL)","PHONE_CODE_EMPTY":"phone_code is missing","PHONE_CODE_EXPIRED":"The phone code you provided has expired","PHONE_CODE_HASH_EMPTY":"phone_code_hash is missing","PHONE_CODE_INVALID":"The provided phone code is invalid","PHONE_HASH_EXPIRED":"An invalid or expired `phone_code_hash` was provided.","PHONE_MIGRATE_%d":"Your phone number is associated to DC %d, please re-send the query to that DC.","PHONE_NOT_OCCUPIED":"No user is associated to the specified phone number","PHONE_NUMBER_APP_SIGNUP_FORBIDDEN":"You can't sign up using this app","PHONE_NUMBER_BANNED":"The provided phone number is banned from telegram","PHONE_NUMBER_FLOOD":"You asked for the code too many times.","PHONE_NUMBER_INVALID":"The phone number is invalid","PHONE_NUMBER_OCCUPIED":"The phone number is already in use","PHONE_NUMBER_UNOCCUPIED":"The phone number is not yet being used","PHONE_PASSWORD_FLOOD":"You have tried logging in too many times","PHONE_PASSWORD_PROTECTED":"This phone is password protected","PHOTO_CONTENT_TYPE_INVALID":"Photo mime-type invalid","PHOTO_CONTENT_URL_EMPTY":"Photo URL invalid","PHOTO_CROP_FILE_MISSING":"Photo crop file missing","PHOTO_CROP_SIZE_SMALL":"Photo is too small","PHOTO_EXT_INVALID":"The extension of the photo is invalid","PHOTO_FILE_MISSING":"Profile photo file missing","PHOTO_ID_INVALID":"Photo ID invalid","PHOTO_INVALID":"Photo invalid","PHOTO_INVALID_DIMENSIONS":"The photo dimensions are invalid","PHOTO_SAVE_FILE_INVALID":"Internal issues, try again later","PHOTO_THUMB_URL_EMPTY":"Photo thumbnail URL is empty","PIN_RESTRICTED":"You can't pin messages","PINNED_DIALOGS_TOO_MUCH":"Too many pinned dialogs","PINNED_TOO_MUCH":"There are too many pinned topics, unpin some first","PINNED_TOPIC_NOT_MODIFIED":"The specified topic is already pinned","POLL_ANSWER_INVALID":"One of the poll answers is not acceptable","POLL_ANSWERS_INVALID":"Invalid poll answers were provided","POLL_COUNTRY_RESTRICTED":"Users from the current user's country cannot vote in this [country-restricted poll &raquo;](https:\/\/core.telegram.org\/api\/poll#country-restricted-polls)","POLL_MEMBER_RESTRICTED":"Only channel subscribers can vote in this poll","POLL_OPTION_DUPLICATE":"Duplicate poll options provided","POLL_OPTION_INVALID":"Invalid poll option provided","POLL_QUESTION_INVALID":"One of the poll questions is not acceptable","POLL_VOTE_REQUIRED":"Cast a vote in the poll before calling this method","PRECHECKOUT_FAILED":"Precheckout failed, a detailed and localized description for the error will be emitted via an [updateServiceNotification as specified here &raquo;](https:\/\/core.telegram.org\/api\/errors#406-not-acceptable)","PREMIUM_ACCOUNT_REQUIRED":"A premium account is required to execute this action.","PREMIUM_CURRENTLY_UNAVAILABLE":"You cannot currently purchase a Premium subscription.","PREMIUM_PURPOSE_INVALID":"The specified InputStorePaymentPurpose is invalid","PREMIUM_SUB_ACTIVE_UNTIL_%d":"You already have a premium subscription active until unixtime %d ","PREVIOUS_CHAT_IMPORT_ACTIVE_WAIT_%dMIN":"Import for this chat is already in progress, wait %d minutes before starting a new one.","PRICING_CHAT_INVALID":"The pricing for the [subscription](https:\/\/core.telegram.org\/api\/subscriptions) is invalid, the maximum price is specified in the [`stars_subscription_amount_max` config key &raquo;](https:\/\/core.telegram.org\/api\/config#stars-subscription-amount-max)","PRIVACY_KEY_INVALID":"The privacy key is invalid","PRIVACY_PREMIUM_REQUIRED":"You need a [Telegram Premium subscription](https:\/\/core.telegram.org\/api\/premium) to send a message to this user.","PRIVACY_TOO_LONG":"Too many privacy rules were specified, the current limit is 1000","PRIVACY_VALUE_INVALID":"The specified privacy rule combination is invalid","PUBLIC_BROADCAST_EXPECTED":"`channel` only accepts a channel, but a supergroup was passed","PUBLIC_CHANNEL_MISSING":"You can only export group call invite links for public chats or channels","PUBLIC_KEY_INVALID":"The specified e2e public key is invalid","PUBLIC_KEY_REQUIRED":"A public key is required","PURPOSE_INVALID":"The specified payment purpose is invalid","QUERY_ID_EMPTY":"The query ID is empty","QUERY_ID_INVALID":"The query ID is invalid","QUERY_TOO_SHORT":"The query string is too short","QUICK_REPLIES_BOT_NOT_ALLOWED":"[Quick replies](https:\/\/core.telegram.org\/api\/business#quick-reply-shortcuts) cannot be used by bots.","QUICK_REPLIES_TOO_MUCH":"A maximum of [appConfig.`quick_replies_limit`](https:\/\/core.telegram.org\/api\/config#quick-replies-limit) shortcuts may be created, the limit was reached.","QUIZ_ANSWER_MISSING":"You can forward a quiz while hiding the original author only after choosing an option in the quiz","QUIZ_CORRECT_ANSWER_INVALID":"An invalid value was provided to the correct_answers field","QUIZ_CORRECT_ANSWERS_EMPTY":"No correct quiz answer was specified","QUIZ_CORRECT_ANSWERS_TOO_MUCH":"You specified too many correct answers in a quiz, quizzes can only have one right answer!","QUIZ_MULTIPLE_INVALID":"Quizzes can't have the multiple_choice flag set!","QUOTE_TEXT_INVALID":"The specified `reply_to`.`quote_text` field is invalid.","RAISE_HAND_FORBIDDEN":"You cannot raise your hand","RANDOM_ID_DUPLICATE":"You provided a random ID that was already used","RANDOM_ID_EMPTY":"Random ID empty","RANDOM_ID_EXPIRED":"The specified `random_id` was expired (most likely it didn't follow the required `uint64_t random_id = (time() << 32) | ((uint64_t)random_uint32_t())` format, or the specified time is too far in the past)","RANDOM_ID_INVALID":"A provided random ID is invalid","RANDOM_LENGTH_INVALID":"Random length invalid","RANGES_INVALID":"Invalid range provided","REACTION_EMPTY":"Empty reaction provided","REACTION_INVALID":"The specified reaction is invalid","REACTIONS_COUNT_INVALID":"The specified number of reactions is invalid","REACTIONS_TOO_MANY":"The message already has exactly `reactions_uniq_max` reaction emojis, you can't react with a new emoji, see [the docs for more info &raquo;](https:\/\/core.telegram.org\/api\/config#client-configuration)","RECEIPT_EMPTY":"The specified receipt is empty","REPLY_MARKUP_BUY_EMPTY":"Reply markup for buy button empty","REPLY_MARKUP_GAME_EMPTY":"A game message is being edited, but the newly provided keyboard doesn't have a keyboardButtonGame button.","REPLY_MARKUP_INVALID":"The provided reply markup is invalid","REPLY_MARKUP_TOO_LONG":"The specified reply_markup is too long","REPLY_MESSAGE_ID_INVALID":"The specified reply-to message ID is invalid","REPLY_MESSAGES_TOO_MUCH":"Each shortcut can contain a maximum of [appConfig.`quick_reply_messages_limit`](https:\/\/core.telegram.org\/api\/config#quick-reply-messages-limit) messages, the limit was reached.","REPLY_TO_INVALID":"The specified `reply_to` field is invalid","REPLY_TO_MONOFORUM_PEER_INVALID":"The specified inputReplyToMonoForum.monoforum_peer_id is invalid","REPLY_TO_USER_INVALID":"The replied-to user is invalid","REQUEST_MSG_EXPIRED":"The request specified in request_msg_id has already expired","REQUEST_TOKEN_INVALID":"The master DC did not accept the `request_token` from the CDN DC. Continue downloading the file from the master DC using upload.getFile.","RESELL_STARS_TOO_FEW":"The offered price is too low","RESELL_STARS_TOO_MUCH":"The offered price is too high","RESET_REQUEST_MISSING":"No password reset is in progress","RESULT_ID_DUPLICATE":"You provided a duplicate result ID","RESULT_ID_EMPTY":"Result ID empty","RESULT_ID_INVALID":"One of the specified result IDs is invalid","RESULT_TYPE_INVALID":"Result type invalid","RESULTS_TOO_MUCH":"Too many results were provided","REVOTE_NOT_ALLOWED":"You cannot change your vote","RIGHT_FORBIDDEN":"Your admin rights do not allow you to do this","RIGHTS_NOT_MODIFIED":"The new admin rights are equal to the old rights, no change was made","RINGTONE_INVALID":"The specified ringtone is invalid","RINGTONE_MIME_INVALID":"The MIME type for the ringtone is invalid","RSA_DECRYPT_FAILED":"Internal RSA decryption failed","SAVED_ID_EMPTY":"The passed inputSavedStarGiftChat.saved_id is empty","SCHEDULE_BOT_NOT_ALLOWED":"Bots cannot schedule messages","SCHEDULE_DATE_INVALID":"Invalid schedule date provided","SCHEDULE_DATE_TOO_LATE":"You can't schedule a message this far in the future","SCHEDULE_STATUS_PRIVATE":"Can't schedule until user is online, if the user's last seen timestamp is hidden by their privacy settings.","SCHEDULE_TOO_MUCH":"There are too many scheduled messages","SCORE_INVALID":"The specified game score is invalid","SEARCH_QUERY_EMPTY":"The search query is empty","SEARCH_WITH_LINK_NOT_SUPPORTED":"You cannot provide a search query and an invite link at the same time.","SECONDS_INVALID":"Invalid duration provided","SECURE_SECRET_REQUIRED":"A secure secret is required","SELF_DELETE_RESTRICTED":"Business bots can't delete messages just for the user, `revoke` **must** be set.","SEND_AS_PEER_INVALID":"You can't send messages as the specified peer","SEND_CODE_UNAVAILABLE":"Returned when all available options for this type of number were already used (e.g. flash-call, then SMS, then this error might be returned to trigger a second resend)","SEND_MEDIA_INVALID":"The specified media is invalid","SEND_MESSAGE_GAME_INVALID":"An inputBotInlineMessageGame can only be contained in an inputBotInlineResultGame, not in an inputBotInlineResult\/inputBotInlineResultPhoto\/etc.","SEND_MESSAGE_MEDIA_INVALID":"Invalid media provided","SEND_MESSAGE_TYPE_INVALID":"The message type is invalid","SENSITIVE_CHANGE_FORBIDDEN":"You can't change your sensitive content settings.","SESSION_EXPIRED":"The session has expired","SESSION_PASSWORD_NEEDED":"2FA is enabled, use a password to login","SESSION_REVOKED":"The session was revoked by the user","SESSION_TOO_FRESH_%d":"This session was created less than 24 hours ago, try again in %d seconds","SETTINGS_INVALID":"Invalid settings were provided","SHA256_HASH_INVALID":"The provided SHA256 hash is invalid","SHORT_NAME_INVALID":"The specified short name is invalid","SHORT_NAME_OCCUPIED":"The specified short name is already in use","SHORTCUT_INVALID":"The specified shortcut is invalid","SIGN_IN_FAILED":"Failure while signing in","SLOTS_EMPTY":"The specified slot list is empty","SLOWMODE_MULTI_MSGS_DISABLED":"Slowmode is enabled, you cannot forward multiple messages to this group.","SLOWMODE_WAIT_%d":"Slowmode is enabled in this chat: wait %d seconds before sending another message to this chat.","SLUG_INVALID":"The specified invoice slug is invalid","SMS_CODE_CREATE_FAILED":"An error occurred while creating the SMS code","SMSJOB_ID_INVALID":"The specified job ID is invalid","SRP_A_INVALID":"The specified inputCheckPasswordSRP.A value is invalid","SRP_ID_INVALID":"Invalid SRP ID provided","SRP_PASSWORD_CHANGED":"Password has changed","STARGIFT_ALREADY_CONVERTED":"The specified star gift was already converted to Stars","STARGIFT_ALREADY_REFUNDED":"The specified star gift was already refunded","STARGIFT_ALREADY_UPGRADED":"The specified gift was already upgraded to a collectible gift","STARGIFT_ATTRIBUTE_INVALID":"One of the specified star gift attributes is invalid","STARGIFT_EXPORT_IN_PROGRESS":"A gift export is in progress, a detailed and localized description for the error will be emitted via an [updateServiceNotification as specified here &raquo;](https:\/\/core.telegram.org\/api\/errors#406-not-acceptable)","STARGIFT_INVALID":"The passed gift is invalid","STARGIFT_MESSAGE_INVALID":"The specified inputInvoiceStarGift.message is invalid","STARGIFT_NOT_FOUND":"The specified gift was not found","STARGIFT_NOT_OWNER":"You're not the owner of the gift you trying to transfer","STARGIFT_NOT_UNIQUE":"You can't transfer a non-collectible gift","STARGIFT_OBJECT_INVALID":"The specified star gift object is invalid","STARGIFT_OFFER_INVALID":"The specified offer amount is invalid","STARGIFT_OFFER_NOT_ALLOWED":"You can't send a purchase offer for this gift","STARGIFT_OWNER_INVALID":"You cannot transfer or sell a gift owned by another user","STARGIFT_PEER_INVALID":"The specified inputSavedStarGiftChat.peer is invalid","STARGIFT_RESELL_CURRENCY_NOT_ALLOWED":"You can't buy the gift using the specified currency (i.e. trying to pay in Stars for TON gifts)","STARGIFT_RESELL_TOO_EARLY_%d":"You will be able to resell this gift in %d seconds.","STARGIFT_SLUG_INVALID":"The specified gift slug is invalid","STARGIFT_TRANSFER_TOO_EARLY_%d":"You cannot transfer this gift yet, wait %d seconds","STARGIFT_UPGRADE_UNAVAILABLE":"A received gift can only be upgraded to a collectible gift if the [messageActionStarGift](https:\/\/core.telegram.org\/constructor\/messageActionStarGift)\/[savedStarGift](https:\/\/core.telegram.org\/constructor\/savedStarGift).`can_upgrade` flag is set.","STARGIFT_USAGE_LIMITED":"The gift is sold out","STARGIFT_USER_USAGE_LIMITED":"You've reached the starGift.limited_per_user limit, you can't buy any more gifts of this type.","STARREF_AWAITING_END":"The previous referral program was terminated less than 24 hours ago: further changes can be made after the date specified in userFull.starref_program.end_date","STARREF_EXPIRED":"The specified referral link is invalid","STARREF_HASH_REVOKED":"The specified affiliate link was already revoked","STARREF_PERMILLE_INVALID":"The specified commission_permille is invalid: the minimum and maximum values for this parameter are contained in the [starref_min_commission_permille](https:\/\/core.telegram.org\/api\/config#starref-min-commission-permille) and [starref_max_commission_permille](https:\/\/core.telegram.org\/api\/config#starref-max-commission-permille) client configuration parameters.","STARREF_PERMILLE_TOO_LOW":"The specified commission_permille is too low: the minimum and maximum values for this parameter are contained in the [starref_min_commission_permille](https:\/\/core.telegram.org\/api\/config#starref-min-commission-permille) and [starref_max_commission_permille](https:\/\/core.telegram.org\/api\/config#starref-max-commission-permille) client configuration parameters.","STARS_AMOUNT_INVALID":"The specified amount in stars is invalid","STARS_FORM_AMOUNT_MISMATCH":"The form amount has changed, please fetch the new form using [payments.getPaymentForm](https:\/\/core.telegram.org\/method\/payments.getPaymentForm) and restart the process.","STARS_INVOICE_INVALID":"The specified Telegram Star invoice is invalid","STARS_PAYMENT_REQUIRED":"To import this chat invite link, you must first [pay for the associated Telegram Star subscription &raquo;](https:\/\/core.telegram.org\/api\/subscriptions#channel-subscriptions)","START_PARAM_EMPTY":"The start parameter is empty","START_PARAM_INVALID":"Start parameter invalid","START_PARAM_TOO_LONG":"Start parameter is too long","STATS_MIGRATE_%d":"Channel statistics for the specified channel are stored on DC %d, please re-send the query to that DC.","STICKER_DOCUMENT_INVALID":"The specified sticker document is invalid","STICKER_EMOJI_INVALID":"Sticker emoji invalid","STICKER_FILE_INVALID":"Sticker file invalid","STICKER_GIF_DIMENSIONS":"The specified video sticker has invalid dimensions","STICKER_ID_INVALID":"The provided sticker ID is invalid","STICKER_INVALID":"The provided sticker is invalid","STICKER_MIME_INVALID":"The specified sticker MIME type is invalid","STICKER_PNG_DIMENSIONS":"Sticker png dimensions invalid","STICKER_PNG_NOPNG":"One of the specified stickers is not a valid PNG file","STICKER_TGS_NODOC":"You must send the animated sticker as a document.","STICKER_TGS_NOTGS":"Invalid TGS sticker provided.","STICKER_THUMB_PNG_NOPNG":"Incorrect stickerset thumb file provided, PNG \/ WEBP expected.","STICKER_THUMB_TGS_NOTGS":"Incorrect stickerset TGS thumb file provided.","STICKER_VIDEO_BIG":"The specified video sticker is too big","STICKER_VIDEO_NODOC":"You must send the video sticker as a document.","STICKER_VIDEO_NOWEBM":"The specified video sticker is not in webm format","STICKERPACK_STICKERS_TOO_MUCH":"There are too many stickers in this stickerpack, you can't add any more","STICKERS_EMPTY":"No sticker provided","STICKERS_TOO_MUCH":"There are too many stickers in this stickerpack, you can't add any more","STICKERSET_INVALID":"The provided sticker set is invalid","STICKERSET_NOT_MODIFIED":"The passed stickerset information is equal to the current information","STICKERSET_OWNER_ANONYMOUS":"Provided stickerset can't be installed as group stickerset to prevent admin deanonymization.","STORIES_NEVER_CREATED":"This peer hasn't ever posted any stories.","STORIES_TOO_MUCH":"You have hit the maximum active stories limit as specified by the [`story_expiring_limit_*` client configuration parameters](https:\/\/core.telegram.org\/api\/config#story-expiring-limit-default): you should buy a [Premium](https:\/\/core.telegram.org\/api\/premium) subscription, delete an active story, or wait for the oldest story to expire.","STORY_ID_EMPTY":"You specified no story IDs.","STORY_ID_INVALID":"The specified story ID is invalid","STORY_LIVE_ALREADY_%d":"This peer already has an active live story, and its ID is equal to %d.","STORY_NOT_MODIFIED":"The new story information you passed is equal to the previous story information, thus it wasn't modified.","STORY_PERIOD_INVALID":"The specified story period is invalid for this account.","STORY_SEND_FLOOD_MONTHLY_%d":"You've hit the monthly story limit as specified by the [`stories_sent_monthly_limit_*` client configuration parameters](https:\/\/core.telegram.org\/api\/config#stories-sent-monthly-limit-default): wait %d seconds before posting a new story.","STORY_SEND_FLOOD_WEEKLY_%d":"You've hit the weekly story limit as specified by the [`stories_sent_weekly_limit_*` client configuration parameters](https:\/\/core.telegram.org\/api\/config#stories-sent-weekly-limit-default): wait for %d seconds before posting a new story.","SUBSCRIPTION_EXPORT_MISSING":"You cannot send a [bot subscription invoice](https:\/\/core.telegram.org\/api\/subscriptions#bot-subscriptions) directly, you may only create invoice links using [payments.exportInvoice](https:\/\/core.telegram.org\/method\/payments.exportInvoice)","SUBSCRIPTION_ID_INVALID":"The specified subscription_id is invalid","SUBSCRIPTION_PERIOD_INVALID":"The specified subscription_pricing.period is invalid","SUGGESTED_POST_AMOUNT_INVALID":"The specified price for the suggested post is invalid","SUGGESTED_POST_PEER_INVALID":"You cannot send suggested posts to non-[monoforum](https:\/\/core.telegram.org\/api\/monoforum) peers","SWITCH_PM_TEXT_EMPTY":"The switch_pm.text field was empty","SWITCH_WEBVIEW_URL_INVALID":"The URL specified in switch_webview.url is invalid!","TAKEOUT_INIT_DELAY_%d":"Sorry, for security reasons, you will be able to begin downloading your data in %d seconds. We have notified all your devices about the export request to make sure it's authorized and to give you time to react if it's not.","TAKEOUT_INVALID":"The specified takeout ID is invalid","TAKEOUT_REQUIRED":"A [takeout](https:\/\/core.telegram.org\/api\/takeout) session needs to be initialized first, [see here &raquo; for more info](https:\/\/core.telegram.org\/api\/takeout).","TASK_ALREADY_EXISTS":"An email reset was already requested.","TEMP_AUTH_KEY_ALREADY_BOUND":"The passed temporary key is already bound to another **perm_auth_key_id**.","TEMP_AUTH_KEY_EMPTY":"No temporary auth key provided","TERMS_URL_INVALID":"The specified [invoice](https:\/\/core.telegram.org\/constructor\/invoice).`terms_url` is invalid","TEXTDRAFT_PEER_INVALID":"sendMessageTextDraftAction can only be used in private 1-on-1 chats","THEME_FILE_INVALID":"Invalid theme file provided","THEME_FORMAT_INVALID":"Invalid theme format provided","THEME_INVALID":"Invalid theme provided","THEME_MIME_INVALID":"The theme's MIME type is invalid","THEME_PARAMS_INVALID":"The specified `theme_params` field is invalid.","THEME_SLUG_INVALID":"The specified theme slug is invalid","THEME_TITLE_INVALID":"The specified theme title is invalid","Timeout":"Timeout while fetching data","TIMEZONE_INVALID":"The specified timezone does not exist","TITLE_INVALID":"The specified stickerpack title is invalid","TMP_PASSWORD_DISABLED":"The temporary password is disabled","TMP_PASSWORD_INVALID":"The passed tmp_password is invalid","TO_ID_INVALID":"The specified `to_id` of the passed inputInvoiceStarGiftResale or inputInvoiceStarGiftTransfer is invalid.","TO_LANG_INVALID":"The specified destination language is invalid","TODO_ITEM_DUPLICATE":"Duplicate [checklist items](https:\/\/core.telegram.org\/api\/todo) detected.","TODO_ITEMS_EMPTY":"A checklist was specified, but no [checklist items](https:\/\/core.telegram.org\/api\/todo) were passed.","TODO_ITEMS_TOO_MUCH":"You specified too many todo list items","TODO_NOT_MODIFIED":"No todo items were specified, so no changes were made to the todo list","TOKEN_EMPTY":"The specified token is empty","TOKEN_INVALID":"The provided token is invalid","TOKEN_TYPE_INVALID":"The specified token type is invalid","TOPIC_CLOSE_SEPARATELY":"The `close` flag cannot be provided together with any of the other flags","TOPIC_CLOSED":"This topic was closed, you can't send messages to it anymore","TOPIC_DELETED":"The specified topic was deleted","TOPIC_HIDE_SEPARATELY":"The `hide` flag cannot be provided together with any of the other flags","TOPIC_ID_INVALID":"The specified topic ID is invalid","TOPIC_NOT_MODIFIED":"The updated topic info is equal to the current topic info, nothing was changed","TOPIC_TITLE_EMPTY":"The specified topic title is empty.","TOPICS_EMPTY":"You specified no topic IDs.","TRANSACTION_ID_INVALID":"The specified transaction ID is invalid","TRANSCRIPTION_FAILED":"Audio transcription failed","TRANSLATE_REQ_FAILED":"Translation failed, please try again later.","TRANSLATE_REQ_QUOTA_EXCEEDED":"Translation is currently unavailable due to a temporary server-side lack of resources.","TRANSLATION_TIMEOUT":"A timeout occurred while translating the specified text.","TRANSLATIONS_DISABLED":"Translations are unavailable, a detailed and localized description for the error will be emitted via an [updateServiceNotification as specified here &raquo;](https:\/\/core.telegram.org\/api\/errors#406-not-acceptable)","TTL_DAYS_INVALID":"The provided TTL is invalid","TTL_MEDIA_INVALID":"Invalid media Time To Live was provided","TTL_PERIOD_INVALID":"The specified TTL period is invalid","TYPES_EMPTY":"No top peer type was provided","UNSUPPORTED":"`require_payment` cannot be *set* by users, only by monoforums: users must instead use the [inputPrivacyKeyNoPaidMessages](https:\/\/core.telegram.org\/constructor\/inputPrivacyKeyNoPaidMessages) privacy setting to remove a previously added exemption.","UNTIL_DATE_INVALID":"Invalid until date provided","UPDATE_APP_TO_LOGIN":"Please update to the latest version of MadelineProto to login.","URL_EXPIRED":"The specified OAuth request has expired","URL_INVALID":"Invalid URL provided","USAGE_LIMIT_INVALID":"The specified usage limit is invalid","USER_ADMIN_INVALID":"You're not an admin","USER_ALREADY_INVITED":"You have already invited this user","USER_ALREADY_PARTICIPANT":"The user is already in the group","USER_BANNED_IN_CHANNEL":"You're banned from sending messages in supergroups\/channels","USER_BLOCKED":"User blocked","USER_BOT":"Bots can only be admins in channels.","USER_BOT_INVALID":"User accounts must provide the `bot` method parameter when calling this method. If there is no such method parameter, this method can only be invoked by bot accounts.","USER_BOT_REQUIRED":"This method can only be called by a bot.","USER_BOT_TO_BOT_DISABLED":"Bot-to-bot messaging is disabled because one of the two bots hasn't enabled the Bot to Bot setting in @BotFather.","USER_CHANNELS_TOO_MUCH":"One of the users you tried to add is already in too many channels\/supergroups","USER_CREATOR":"For channels.editAdmin: you've tried to edit the admin rights of the owner, but you're not the owner; for channels.leaveChannel: you can't leave this channel, because you're its creator.","USER_DEACTIVATED":"The current account was deleted by the user","USER_DEACTIVATED_BAN":"The current account was deleted and banned by Telegram's antispam system","USER_DELETED":"You can't send this secret message because the other participant deleted their account.","USER_DISALLOWED_STARGIFTS":"The recipient user has configured restrictions on which categories of star gifts they're willing to accept (unique, limited, or unlimited): the sender attempted to get a payment form for a gift that falls into a category the recipient has blocked.","USER_GIFT_UNAVAILABLE":"Gifts are not available in the current region ([stars_gifts_enabled](https:\/\/core.telegram.org\/api\/config#stars-gifts-enabled) is equal to false)","USER_ID_INVALID":"The provided user ID is invalid","USER_INVALID":"Invalid user provided","USER_IS_BLOCKED":"You were blocked by this user","USER_IS_BOT":"Bots can't send messages to other bots","USER_KICKED":"This user was kicked from this supergroup\/channel","USER_MIGRATE_%d":"Your account is associated to DC %d, please re-send the query to that DC.","USER_NOT_MUTUAL_CONTACT":"The provided user is not a mutual contact","USER_NOT_PARTICIPANT":"You're not a member of this supergroup\/channel","USER_PERMISSION_DENIED":"The user hasn't granted or has revoked the bot's access to change their emoji status using [bots.toggleUserEmojiStatusPermission](https:\/\/core.telegram.org\/method\/bots.toggleUserEmojiStatusPermission)","USER_PRIVACY_RESTRICTED":"The user's privacy settings do not allow you to do this","USER_PUBLIC_MISSING":"Cannot generate a link to stories posted by a peer without a username.","USER_RESTRICTED":"You're spamreported, you can't create channels or chats.","USER_VOLUME_INVALID":"The specified user volume is invalid","USERNAME_INVALID":"The provided username is not valid","USERNAME_NOT_MODIFIED":"The username was not modified","USERNAME_NOT_OCCUPIED":"The provided username is not occupied","USERNAME_OCCUPIED":"The provided username is already occupied","USERNAME_PURCHASE_AVAILABLE":"The specified username can be purchased on https:\/\/fragment.com","USERNAME_SUFFIX_MISSING":"The required `bot` suffix is missing from the passed username","USERNAMES_ACTIVE_TOO_MUCH":"The maximum number of active usernames was reached","USERPIC_PRIVACY_REQUIRED":"You need to disable privacy settings for your profile picture in order to make your geolocation public.","USERPIC_UPLOAD_REQUIRED":"You must have a profile picture to publish your geolocation","USERS_TOO_FEW":"Not enough users (to create a chat, for example)","USERS_TOO_MUCH":"The maximum number of users has been exceeded (to create a chat, for example)","VENUE_ID_INVALID":"The specified venue ID is invalid","VIDEO_CONTENT_TYPE_INVALID":"The video's content type is invalid","VIDEO_DURATION_INVALID":"The duration of the specified video is invalid","VIDEO_FILE_INVALID":"The specified video file is invalid","VIDEO_PAUSE_FORBIDDEN":"You cannot pause the video stream","VIDEO_STOP_FORBIDDEN":"You cannot stop the video stream","VIDEO_TITLE_EMPTY":"The specified video title is empty","VOICE_MESSAGES_FORBIDDEN":"This user's privacy settings forbid you from sending voice messages","WALLPAPER_FILE_INVALID":"The specified wallpaper file is invalid","WALLPAPER_INVALID":"The specified wallpaper is invalid","WALLPAPER_MIME_INVALID":"The specified wallpaper MIME type is invalid","WALLPAPER_NOT_FOUND":"The specified wallpaper could not be found","WC_CONVERT_URL_INVALID":"WC convert URL invalid","WEBAPP_REQ_ID_INVALID":"The specified webapp_req_id is invalid","WEBAUTH_TOKEN_EXPIRED":"The specified auth token has expired","WEBDOCUMENT_INVALID":"Invalid webdocument URL provided","WEBDOCUMENT_MIME_INVALID":"Invalid webdocument mime type provided","WEBDOCUMENT_SIZE_TOO_BIG":"Webdocument is too big!","WEBDOCUMENT_URL_EMPTY":"The passed web document URL is empty","WEBDOCUMENT_URL_INVALID":"The specified webdocument URL is invalid","WEBPAGE_CURL_FAILED":"Failure while fetching the webpage with cURL","WEBPAGE_MEDIA_EMPTY":"Webpage media empty","WEBPAGE_NOT_FOUND":"A preview for the specified webpage `url` could not be generated","WEBPAGE_URL_INVALID":"The specified webpage `url` is invalid","WEBPUSH_AUTH_INVALID":"The specified web push authentication secret is invalid.","WEBPUSH_KEY_INVALID":"The specified web push elliptic curve Diffie-Hellman public key is invalid.","WEBPUSH_TOKEN_INVALID":"The specified web push token is invalid.","YOU_BLOCKED_USER":"You blocked this user","YOUR_PRIVACY_RESTRICTED":"You cannot fetch the read date of this message because you have disallowed other users to do so for *your* messages; to fix, allow other users to see *your* exact last online date OR purchase a [Telegram Premium](https:\/\/core.telegram.org\/api\/premium) subscription."}}<?php

declare(strict_types=1);

/**
 * PTSException module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use Exception;

use const PHP_EOL;

/**
 * Internal error indicating a file redirect.
 *
 * @internal
 */
final class FileRedirect extends Exception
{
    use TL\PrettyException;
    /**
     * @psalm-mutation-free
     */
    public function __toString(): string
    {
        return PTSException::class.($this->message !== '' ? ': ' : '').$this->message.PHP_EOL.'TL Trace:'.PHP_EOL.PHP_EOL.$this->getTLTrace().PHP_EOL;
    }
    public function __construct(public readonly int $dc, string $file = '')
    {
        parent::__construct("Redirect to dc $dc");
        $this->prettifyTL($file);
    }
}
<?php

declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

if (class_exists(VoIPServerConfig::class)) {
    return;
}
/**
 * Manages storage of VoIP server config.
 *
 * @internal
 *
 * @psalm-external-mutation-free
 */
final class VoIPServerConfig
{
    /**
     * The configuration.
     *
     */
    private static array $_config = [];
    /**
     * The default configuration.
     *
     */
    private static array $_configDefault = [];
    /**
     * Update shared call settings.
     *
     * @param array $config The settings
     *
     * @psalm-external-mutation-free
     */
    public static function update(array $config): void
    {
        self::$_config = $config;
    }
    /**
     * Get shared call settings.
     *
     * @return array The settings
     *
     * @psalm-external-mutation-free
     */
    public static function get(): array
    {
        return self::$_config;
    }
    /**
     * Update default shared call settings.
     *
     * @param array $configDefault The settings
     *
     * @psalm-external-mutation-free
     */
    public static function updateDefault(array $configDefault): void
    {
        self::$_configDefault = $configDefault;
    }
    /**
     * Get default shared call settings.
     *
     * @return array The settings
     *
     * @psalm-external-mutation-free
     */
    public static function getDefault(): array
    {
        return self::$_configDefault;
    }
    /**
     * Get final settings.
     *
     * @psalm-external-mutation-free
     */
    public static function getFinal(): array
    {
        return array_merge(self::$_configDefault, self::$_config);
    }
}
<?php

declare(strict_types=1);

/**
 * FileCallback module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

/**
 * File callback interface.
 *
 * @template TT
 *
 * @implements FileCallbackInterface<TT>
 */
final class FileCallback implements FileCallbackInterface
{
    /**
     * Callback.
     *
     * @var callable(float, float, float)
     */
    public readonly mixed $callback;
    /**
     * Construct file callback.
     *
     * @param TT                            $file     File to download/upload
     * @param callable(float, float, float) $callback Callback
     *
     * @psalm-mutation-free
     */
    public function __construct(public readonly mixed $file, callable $callback)
    {
        $this->callback = $callback;
    }
    /**
     * Get file.
     *
     * @return TT
     */
    #[\Override]
    public function getFile(): mixed
    {
        return $this->file;
    }
    #[\Override]
    public function __invoke(float $percent, float $speed, float $time): void
    {
        $callback = $this->callback;
        $callback($percent, $speed, $time);
    }
}
<?php

declare(strict_types=1);

/**
 * NothingInTheSocketException module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use Exception;

final class NothingInTheSocketException extends Exception
{
}
<?php

declare(strict_types=1);

/**
 * Exception module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use const DIRECTORY_SEPARATOR;
use const PHP_EOL;
use const PHP_MAJOR_VERSION;
use const PHP_MINOR_VERSION;
use const PHP_SAPI;

/**
 * Basic exception.
 */
class Exception extends \Exception
{
    use TL\PrettyException;
    /**
     * @psalm-external-mutation-free
     */
    public function __toString(): string
    {
        return $this->file === 'MadelineProto' ? $this->message : '\\danog\\MadelineProto\\Exception'.($this->message !== '' ? ': ' : '').$this->message.' in '.$this->file.':'.$this->line.PHP_EOL.Magic::$revision.PHP_EOL.'TL Trace:'.PHP_EOL.$this->getTLTrace();
    }
    public function __construct($message = null, $code = 0, ?\Throwable $previous = null, $file = null, $line = null)
    {
        $this->prettifyTL();
        if ($file !== null) {
            $this->file = $file;
        }
        if ($line !== null) {
            $this->line = $line;
        }
        parent::__construct($message, $code, $previous);
        if (!str_contains($message, 'socket_accept')
            && $message !== 'Client backtrace'
            && !\in_array(basename($this->file), ['PKCS8.php', 'PSS.php'], true)
        ) {
            Logger::log($message.' in '.basename($this->file).':'.$this->line, Logger::FATAL_ERROR);
        }
    }
    /**
     * Complain about missing extensions.
     *
     * @param string $extensionName Extension name
     */
    public static function extension(string $extensionName): self
    {
        if ($extensionName === 'libtgvoip') {
            $additional = \sprintf(Lang::$current_lang['extensionRequiredInstallWithCustomInstructions'], 'https://voip.madelineproto.xyz');
        } elseif ($extensionName === 'prime') {
            $additional = \sprintf(Lang::$current_lang['extensionRequiredInstallWithCustomInstructions'], 'https://prime.madelineproto.xyz');
        } else {
            $additional = \sprintf(Lang::$current_lang['extensionRequiredInstallWithApt'], 'php'.PHP_MAJOR_VERSION.'.'.PHP_MINOR_VERSION.'-'.$extensionName);
        }
        $message = \sprintf(Lang::$current_lang['extensionRequired'], $extensionName, $additional);
        if (PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg') {
            echo htmlentities($message).'<br>';
        }
        $file = 'MadelineProto';
        $line = 1;
        return new self($message, 0, null, $file, $line);
    }
    /**
     * @internal
     *
     * Error handler
     */
    public static function exceptionErrorHandler($errno = 0, $errstr = null, $errfile = null, $errline = null): bool
    {
        $errfileReplaced = $errfile ?? '';
        // Warnings throw, @ included (it doesn't clear error_reporting() since PHP 8): catch them instead.
        // Only a script that turned error reporting off entirely gets none.
        if (error_reporting() === 0
            || strpos($errstr, 'headers already sent')
            || str_contains($errstr, 'Creation of dynamic property')
            || str_contains($errstr, 'Legacy nullable type detected')
            || str_contains($errstr, '$tdMethods is deprecated')
            || $errfileReplaced && (
                str_contains($errfileReplaced, DIRECTORY_SEPARATOR.'amphp'.DIRECTORY_SEPARATOR)
                || str_contains($errfileReplaced, DIRECTORY_SEPARATOR.'league'.DIRECTORY_SEPARATOR)
                || str_contains($errfileReplaced, DIRECTORY_SEPARATOR.'phpseclib'.DIRECTORY_SEPARATOR)
                || str_contains($errfileReplaced, DIRECTORY_SEPARATOR.'symfony'.DIRECTORY_SEPARATOR)
            )
        ) {
            return false;
        }
        throw new self($errstr, $errno, null, $errfile, $errline);
    }
    /**
     * @internal
     *
     * ExceptionErrorHandler.
     */
    public static function exceptionHandler(\Throwable $exception): void
    {
        $print = static function (string $s): void {
            Logger::log($s, Logger::FATAL_ERROR);
            if (headers_sent()) {
                return;
            }
            http_response_code(500);
            if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
                echo($s.PHP_EOL);
            } else {
                echo(str_replace("\n", "<br>", htmlentities($s)).PHP_EOL);
            }
        };
        if (str_contains($exception->getMessage(), 'Fiber stack protect failed')
            || str_contains($exception->getMessage(), 'Fiber stack allocate failed')
        ) {
            $maps = "?";
            try {
                $maps = '~'.substr_count(file_get_contents('/proc/self/maps'), "\n");
                $pid = getmypid();
                $maps = '~'.substr_count(file_get_contents("/proc/$pid/maps"), "\n");
            } catch (\Throwable) {
            }
            $print(Lang::$current_lang['manualAdminActionRequired']);
            $print(Lang::$current_lang['manualAdminActionRequired']);
            $print(Lang::$current_lang['manualAdminActionRequired']);
            $print(\sprintf(Lang::$current_lang['mmapErrorPart1'], $maps));
            $print(\sprintf(Lang::$current_lang['mmapErrorPart2'], 'echo 262144 | sudo tee /proc/sys/vm/max_map_count'));
            $print(\sprintf(Lang::$current_lang['mmapErrorPart3'], 'echo vm.max_map_count=262144 | sudo tee /etc/sysctl.d/40-madelineproto.conf'));
            $print(Lang::$current_lang['mmapErrorPart4']);
            $print(Lang::$current_lang['manualAdminActionRequired']);
            $print(Lang::$current_lang['manualAdminActionRequired']);
            $print(Lang::$current_lang['manualAdminActionRequired']);
        }
        $print((string) $exception);
        die(1);
    }
}
<?php

declare(strict_types=1);

/**
 * LoggerGetter interface.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

/** @internal */
/** @psalm-mutable */
interface LoggerGetter
{
    /** @psalm-impure */
    public function getLogger(): Logger;
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use danog\MadelineProto\EventHandler\Calls\CallDenialReason;

/**
 * Thrown when a call or group call cannot be started in a chat, because of our own permissions,
 * the other party's privacy settings, or the kind of chat/account involved.
 *
 * Use `canRequestCall()` on a message to check in advance whether a call can be started.
 */
final class CallNotAllowedException extends Exception
{
    /** @internal */
    public function __construct(
        /** Why the call cannot be started. */
        public readonly CallDenialReason $reason
    ) {
        parent::__construct($reason->value);
    }
}
boolFalse#bc799737 = Bool;
boolTrue#997275b5 = Bool;

true#3fedd339 = True;

vector#1cb5c415 {t:Type} # [ t ] = Vector t;

error#c4b9f9bb code:int text:string = Error;

null#56730bcc = Null;

inputPeerEmpty#7f3b18ea = InputPeer;
inputPeerSelf#7da07ec9 = InputPeer;
inputPeerChat#35a95cb9 chat_id:long = InputPeer;
inputPeerUser#dde8a54c user_id:long access_hash:long = InputPeer;
inputPeerChannel#27bcbbfc channel_id:long access_hash:long = InputPeer;
inputPeerUserFromMessage#a87b0a1c peer:InputPeer msg_id:int user_id:long = InputPeer;
inputPeerChannelFromMessage#bd2a0840 peer:InputPeer msg_id:int channel_id:long = InputPeer;

inputUserEmpty#b98886cf = InputUser;
inputUserSelf#f7c1b13f = InputUser;
inputUser#f21158c6 user_id:long access_hash:long = InputUser;
inputUserFromMessage#1da448e2 peer:InputPeer msg_id:int user_id:long = InputUser;

inputPhoneContact#6a1dc4be flags:# client_id:long phone:string first_name:string last_name:string note:flags.0?TextWithEntities = InputContact;

inputFile#f52ff27f id:long parts:int name:string md5_checksum:string = InputFile;
inputFileBig#fa4f0bb5 id:long parts:int name:string = InputFile;
inputFileStoryDocument#62dc8b48 id:InputDocument = InputFile;

inputMediaEmpty#9664f57f = InputMedia;
inputMediaUploadedPhoto#7d8375da flags:# spoiler:flags.2?true live_photo:flags.3?true file:InputFile stickers:flags.0?Vector<InputDocument> ttl_seconds:flags.1?int video:flags.3?InputDocument = InputMedia;
inputMediaPhoto#e3af4434 flags:# spoiler:flags.1?true live_photo:flags.2?true id:InputPhoto ttl_seconds:flags.0?int video:flags.2?InputDocument = InputMedia;
inputMediaGeoPoint#f9c44144 geo_point:InputGeoPoint = InputMedia;
inputMediaContact#f8ab7dfb phone_number:string first_name:string last_name:string vcard:string = InputMedia;
inputMediaUploadedDocument#37c9330 flags:# nosound_video:flags.3?true force_file:flags.4?true spoiler:flags.5?true file:InputFile thumb:flags.2?InputFile mime_type:string attributes:Vector<DocumentAttribute> stickers:flags.0?Vector<InputDocument> video_cover:flags.6?InputPhoto video_timestamp:flags.7?int ttl_seconds:flags.1?int = InputMedia;
inputMediaDocument#a8763ab5 flags:# spoiler:flags.2?true id:InputDocument video_cover:flags.3?InputPhoto video_timestamp:flags.4?int ttl_seconds:flags.0?int query:flags.1?string = InputMedia;
inputMediaVenue#c13d1c11 geo_point:InputGeoPoint title:string address:string provider:string venue_id:string venue_type:string = InputMedia;
inputMediaPhotoExternal#e5bbfe1a flags:# spoiler:flags.1?true url:string ttl_seconds:flags.0?int = InputMedia;
inputMediaDocumentExternal#779600f9 flags:# spoiler:flags.1?true url:string ttl_seconds:flags.0?int video_cover:flags.2?InputPhoto video_timestamp:flags.3?int = InputMedia;
inputMediaGame#d33f43f3 id:InputGame = InputMedia;
inputMediaInvoice#405fef0d flags:# title:string description:string photo:flags.0?InputWebDocument invoice:Invoice payload:bytes provider:flags.3?string provider_data:DataJSON start_param:flags.1?string extended_media:flags.2?InputMedia = InputMedia;
inputMediaGeoLive#971fa843 flags:# stopped:flags.0?true geo_point:InputGeoPoint heading:flags.2?int period:flags.1?int proximity_notification_radius:flags.3?int = InputMedia;
inputMediaPoll#883a4108 flags:# poll:Poll correct_answers:flags.0?Vector<int> attached_media:flags.3?InputMedia solution:flags.1?string solution_entities:flags.1?Vector<MessageEntity> solution_media:flags.2?InputMedia = InputMedia;
inputMediaDice#e66fbf7b emoticon:string = InputMedia;
inputMediaStory#89fdd778 peer:InputPeer id:int = InputMedia;
inputMediaWebPage#c21b8849 flags:# force_large_media:flags.0?true force_small_media:flags.1?true optional:flags.2?true url:string = InputMedia;
inputMediaPaidMedia#c4103386 flags:# stars_amount:long extended_media:Vector<InputMedia> payload:flags.0?string = InputMedia;
inputMediaTodo#9fc55fde todo:TodoList = InputMedia;
inputMediaStakeDice#f3a9244a game_hash:string ton_amount:long client_seed:bytes = InputMedia;

inputChatPhotoEmpty#1ca48f57 = InputChatPhoto;
inputChatUploadedPhoto#bdcdaec0 flags:# file:flags.0?InputFile video:flags.1?InputFile video_start_ts:flags.2?double video_emoji_markup:flags.3?VideoSize = InputChatPhoto;
inputChatPhoto#8953ad37 id:InputPhoto = InputChatPhoto;

inputGeoPointEmpty#e4c123d6 = InputGeoPoint;
inputGeoPoint#48222faf flags:# lat:double long:double accuracy_radius:flags.0?int = InputGeoPoint;

inputPhotoEmpty#1cd7bf0d = InputPhoto;
inputPhoto#3bb3b94a id:long access_hash:long file_reference:bytes = InputPhoto;

inputFileLocation#dfdaabe1 volume_id:long local_id:int secret:long file_reference:bytes = InputFileLocation;
inputEncryptedFileLocation#f5235d55 id:long access_hash:long = InputFileLocation;
inputDocumentFileLocation#bad07584 id:long access_hash:long file_reference:bytes thumb_size:string = InputFileLocation;
inputSecureFileLocation#cbc7ee28 id:long access_hash:long = InputFileLocation;
inputTakeoutFileLocation#29be5899 = InputFileLocation;
inputPhotoFileLocation#40181ffe id:long access_hash:long file_reference:bytes thumb_size:string = InputFileLocation;
inputPhotoLegacyFileLocation#d83466f3 id:long access_hash:long file_reference:bytes volume_id:long local_id:int secret:long = InputFileLocation;
inputPeerPhotoFileLocation#37257e99 flags:# big:flags.0?true peer:InputPeer photo_id:long = InputFileLocation;
inputStickerSetThumb#9d84f3db stickerset:InputStickerSet thumb_version:int = InputFileLocation;
inputGroupCallStream#598a92a flags:# call:InputGroupCall time_ms:long scale:int video_channel:flags.0?int video_quality:flags.0?int = InputFileLocation;

peerUser#59511722 user_id:long = Peer;
peerChat#36c6019a chat_id:long = Peer;
peerChannel#a2a5371e channel_id:long = Peer;

storage.fileUnknown#aa963b05 = storage.FileType;
storage.filePartial#40bc6f52 = storage.FileType;
storage.fileJpeg#7efe0e = storage.FileType;
storage.fileGif#cae1aadf = storage.FileType;
storage.filePng#a4f63c0 = storage.FileType;
storage.filePdf#ae1e508d = storage.FileType;
storage.fileMp3#528a0677 = storage.FileType;
storage.fileMov#4b09ebbc = storage.FileType;
storage.fileMp4#b3cea0e4 = storage.FileType;
storage.fileWebp#1081464c = storage.FileType;

userEmpty#d3bc4b7a id:long = User;
user#b1b8cc83 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true support:flags.23?true scam:flags.24?true apply_min_photo:flags.25?true fake:flags.26?true bot_attach_menu:flags.27?true premium:flags.28?true attach_menu_enabled:flags.29?true flags2:# bot_can_edit:flags2.1?true close_friend:flags2.2?true stories_hidden:flags2.3?true stories_unavailable:flags2.4?true contact_require_premium:flags2.10?true bot_business:flags2.11?true bot_has_main_app:flags2.13?true bot_forum_view:flags2.16?true bot_forum_can_manage_topics:flags2.17?true bot_can_manage_bots:flags2.18?true bot_guestchat:flags2.19?true bot_guard:flags2.20?true id:long access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?Vector<RestrictionReason> bot_inline_placeholder:flags.19?string lang_code:flags.22?string emoji_status:flags.30?EmojiStatus usernames:flags2.0?Vector<Username> stories_max_id:flags2.5?RecentStory color:flags2.8?PeerColor profile_color:flags2.9?PeerColor bot_active_users:flags2.12?int bot_verification_icon:flags2.14?long send_paid_messages_stars:flags2.15?long linked_community_id:flags2.21?long = User;

userProfilePhotoEmpty#4f11bae1 = UserProfilePhoto;
userProfilePhoto#82d1f706 flags:# has_video:flags.0?true personal:flags.2?true photo_id:long stripped_thumb:flags.1?bytes dc_id:int = UserProfilePhoto;

userStatusEmpty#9d05049 = UserStatus;
userStatusOnline#edb93949 expires:int = UserStatus;
userStatusOffline#8c703f was_online:int = UserStatus;
userStatusRecently#7b197dc8 flags:# by_me:flags.0?true = UserStatus;
userStatusLastWeek#541a1d1a flags:# by_me:flags.0?true = UserStatus;
userStatusLastMonth#65899777 flags:# by_me:flags.0?true = UserStatus;

chatEmpty#29562865 id:long = Chat;
chat#41cbf256 flags:# creator:flags.0?true left:flags.2?true deactivated:flags.5?true call_active:flags.23?true call_not_empty:flags.24?true noforwards:flags.25?true id:long title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel admin_rights:flags.14?ChatAdminRights default_banned_rights:flags.18?ChatBannedRights = Chat;
chatForbidden#6592a1a7 id:long title:string = Chat;
channel#7ad15240 flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true call_active:flags.23?true call_not_empty:flags.24?true fake:flags.25?true gigagroup:flags.26?true noforwards:flags.27?true join_to_send:flags.28?true join_request:flags.29?true forum:flags.30?true flags2:# stories_hidden:flags2.1?true stories_hidden_min:flags2.2?true stories_unavailable:flags2.3?true signature_profiles:flags2.12?true autotranslation:flags2.15?true broadcast_messages_allowed:flags2.16?true monoforum:flags2.17?true forum_tabs:flags2.19?true in_news:flags2.21?true id:long access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int restriction_reason:flags.9?Vector<RestrictionReason> admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int usernames:flags2.0?Vector<Username> stories_max_id:flags2.4?RecentStory color:flags2.7?PeerColor profile_color:flags2.8?PeerColor emoji_status:flags2.9?EmojiStatus level:flags2.10?int subscription_until_date:flags2.11?int bot_verification_icon:flags2.13?long send_paid_messages_stars:flags2.14?long linked_monoforum_id:flags2.18?long linked_community_id:flags2.20?long pinned_msg_id:flags2.22?int = Chat;
channelForbidden#17d493d5 flags:# broadcast:flags.5?true megagroup:flags.8?true monoforum:flags.10?true id:long access_hash:long title:string until_date:flags.16?int = Chat;
communityForbidden#fd3cdab8 flags:# id:long access_hash:flags.13?long title:string = Chat;
community#65efe954 flags:# creator:flags.0?true left:flags.2?true min:flags.12?true flags2:# collapsed_in_dialogs:flags2.20?true id:long access_hash:flags.13?long title:string photo:ChatPhoto date:int admin_rights:flags.14?ChatAdminRights default_banned_rights:flags.18?ChatBannedRights = Chat;

chatFull#2633421b flags:# can_set_username:flags.7?true has_scheduled:flags.8?true translations_disabled:flags.19?true has_welcome_messages:flags.21?true id:long about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl_period:flags.14?int groupcall_default_join_as:flags.15?Peer theme_emoticon:flags.16?string requests_pending:flags.17?int recent_requesters:flags.17?Vector<long> available_reactions:flags.18?ChatReactions reactions_limit:flags.20?int = ChatFull;
channelFull#c4c4dd40 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true flags2:# can_delete_channel:flags2.0?true antispam:flags2.1?true participants_hidden:flags2.2?true translations_disabled:flags2.3?true stories_pinned_available:flags2.5?true view_forum_as_messages:flags2.6?true restricted_sponsored:flags2.11?true can_view_revenue:flags2.12?true paid_media_allowed:flags2.14?true can_view_stars_revenue:flags2.15?true paid_reactions_available:flags2.16?true stargifts_available:flags2.19?true paid_messages_available:flags2.20?true has_welcome_messages:flags2.24?true id:long about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?long migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?long location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector<string> groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string requests_pending:flags.28?int recent_requesters:flags.28?Vector<long> default_send_as:flags.29?Peer available_reactions:flags.30?ChatReactions reactions_limit:flags2.13?int stories:flags2.4?PeerStories wallpaper:flags2.7?WallPaper boosts_applied:flags2.8?int boosts_unrestrict:flags2.9?int emojiset:flags2.10?StickerSet bot_verification:flags2.17?BotVerification stargifts_count:flags2.18?int send_paid_messages_stars:flags2.21?long main_tab:flags2.22?ProfileTab guard_bot_id:flags2.23?long share_theme:flags2.25?ShareTheme = ChatFull;
communityFull#cbb7a507 flags:# id:long about:string chat_photo:Photo linked_peers:Vector<CommunityPeer> admins_count:flags.1?int kicked_count:flags.2?int peer_link_requests_pending:flags.0?int = ChatFull;

chatParticipant#38e79fde flags:# user_id:long inviter_id:long date:int rank:flags.0?string = ChatParticipant;
chatParticipantCreator#e1f867b8 flags:# user_id:long rank:flags.0?string = ChatParticipant;
chatParticipantAdmin#360d5d2 flags:# user_id:long inviter_id:long date:int rank:flags.0?string = ChatParticipant;

chatParticipantsForbidden#8763d3e1 flags:# chat_id:long self_participant:flags.0?ChatParticipant = ChatParticipants;
chatParticipants#3cbc93f8 chat_id:long participants:Vector<ChatParticipant> version:int = ChatParticipants;

chatPhotoEmpty#37c1011c = ChatPhoto;
chatPhoto#1c6e1c11 flags:# has_video:flags.0?true photo_id:long stripped_thumb:flags.1?bytes dc_id:int = ChatPhoto;

messageEmpty#90a6ca84 flags:# id:int peer_id:flags.0?Peer = Message;
message#7600b9d3 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true noforwards:flags.26?true invert_media:flags.27?true flags2:# offline:flags2.1?true video_processing_pending:flags2.4?true paid_suggested_post_stars:flags2.8?true paid_suggested_post_ton:flags2.9?true id:int from_id:flags.8?Peer from_boosts_applied:flags.29?int from_rank:flags2.12?string peer_id:Peer saved_peer_id:flags.28?Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?long via_business_bot_id:flags2.0?long guestchat_via_from:flags2.19?Peer reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long reactions:flags.20?MessageReactions restriction_reason:flags.22?Vector<RestrictionReason> ttl_period:flags.25?int quick_reply_shortcut_id:flags.30?int effect:flags2.2?long factcheck:flags2.3?FactCheck report_delivery_until_date:flags2.5?int paid_message_stars:flags2.6?long suggested_post:flags2.7?SuggestedPost schedule_repeat_period:flags2.10?int summary_from_language:flags2.11?string rich_message:flags2.13?RichMessage = Message;
messageService#7a800e0a flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true reactions_are_possible:flags.9?true silent:flags.13?true post:flags.14?true legacy:flags.19?true id:int from_id:flags.8?Peer peer_id:Peer saved_peer_id:flags.28?Peer reply_to:flags.3?MessageReplyHeader date:int action:MessageAction reactions:flags.20?MessageReactions ttl_period:flags.25?int = Message;

messageMediaEmpty#3ded6320 = MessageMedia;
messageMediaPhoto#e216eb63 flags:# spoiler:flags.3?true live_photo:flags.4?true photo:flags.0?Photo ttl_seconds:flags.2?int video:flags.4?Document = MessageMedia;
messageMediaGeo#56e0d474 geo:GeoPoint = MessageMedia;
messageMediaContact#70322949 phone_number:string first_name:string last_name:string vcard:string user_id:long = MessageMedia;
messageMediaUnsupported#9f84f49e = MessageMedia;
messageMediaDocument#52d8ccd9 flags:# nopremium:flags.3?true spoiler:flags.4?true video:flags.6?true round:flags.7?true voice:flags.8?true document:flags.0?Document alt_documents:flags.5?Vector<Document> video_cover:flags.9?Photo video_timestamp:flags.10?int ttl_seconds:flags.2?int = MessageMedia;
messageMediaWebPage#ddf10c3b flags:# force_large_media:flags.0?true force_small_media:flags.1?true manual:flags.3?true safe:flags.4?true webpage:WebPage = MessageMedia;
messageMediaVenue#2ec0533f geo:GeoPoint title:string address:string provider:string venue_id:string venue_type:string = MessageMedia;
messageMediaGame#fdb19008 game:Game = MessageMedia;
messageMediaInvoice#f6a548d3 flags:# shipping_address_requested:flags.1?true test:flags.3?true title:string description:string photo:flags.0?WebDocument receipt_msg_id:flags.2?int currency:string total_amount:long start_param:string extended_media:flags.4?MessageExtendedMedia = MessageMedia;
messageMediaGeoLive#b940c666 flags:# geo:GeoPoint heading:flags.0?int period:int proximity_notification_radius:flags.1?int = MessageMedia;
messageMediaPoll#773f4e66 flags:# poll:Poll results:PollResults attached_media:flags.0?MessageMedia = MessageMedia;
messageMediaDice#8cbec07 flags:# value:int emoticon:string game_outcome:flags.0?messages.EmojiGameOutcome = MessageMedia;
messageMediaStory#68cb6283 flags:# via_mention:flags.1?true peer:Peer id:int story:flags.0?StoryItem = MessageMedia;
messageMediaGiveaway#aa073beb flags:# only_new_subscribers:flags.0?true winners_are_visible:flags.2?true channels:Vector<long> countries_iso2:flags.1?Vector<string> prize_description:flags.3?string quantity:int months:flags.4?int stars:flags.5?long until_date:int = MessageMedia;
messageMediaGiveawayResults#ceaa3ea1 flags:# only_new_subscribers:flags.0?true refunded:flags.2?true channel_id:long additional_peers_count:flags.3?int launch_msg_id:int winners_count:int unclaimed_count:int winners:Vector<long> months:flags.4?int stars:flags.5?long prize_description:flags.1?string until_date:int = MessageMedia;
messageMediaPaidMedia#a8852491 stars_amount:long extended_media:Vector<MessageExtendedMedia> = MessageMedia;
messageMediaToDo#8a53b014 flags:# todo:TodoList completions:flags.0?Vector<TodoCompletion> = MessageMedia;
messageMediaVideoStream#ca5cab89 flags:# rtmp_stream:flags.0?true call:InputGroupCall = MessageMedia;

messageActionEmpty#b6aef7b0 = MessageAction;
messageActionChatCreate#bd47cbad title:string users:Vector<long> = MessageAction;
messageActionChatEditTitle#b5a1ce5a title:string = MessageAction;
messageActionChatEditPhoto#7fcb13a8 photo:Photo = MessageAction;
messageActionChatDeletePhoto#95e3fbef = MessageAction;
messageActionChatAddUser#15cefd00 users:Vector<long> = MessageAction;
messageActionChatDeleteUser#a43f30cc user_id:long = MessageAction;
messageActionChatJoinedByLink#31224c3 inviter_id:long = MessageAction;
messageActionChannelCreate#95d2ac92 title:string = MessageAction;
messageActionChatMigrateTo#e1037f92 channel_id:long = MessageAction;
messageActionChannelMigrateFrom#ea3948e9 title:string chat_id:long = MessageAction;
messageActionPinMessage#94bd38ed = MessageAction;
messageActionHistoryClear#9fbab604 = MessageAction;
messageActionGameScore#92a72876 game_id:long score:int = MessageAction;
messageActionPaymentSentMe#ffa00ccc flags:# recurring_init:flags.2?true recurring_used:flags.3?true currency:string total_amount:long payload:bytes info:flags.0?PaymentRequestedInfo shipping_option_id:flags.1?string charge:PaymentCharge subscription_until_date:flags.4?int = MessageAction;
messageActionPaymentSent#c624b16e flags:# recurring_init:flags.2?true recurring_used:flags.3?true currency:string total_amount:long invoice_slug:flags.0?string subscription_until_date:flags.4?int = MessageAction;
messageActionPhoneCall#80e11a7f flags:# video:flags.2?true call_id:long reason:flags.0?PhoneCallDiscardReason duration:flags.1?int = MessageAction;
messageActionScreenshotTaken#4792929b = MessageAction;
messageActionCustomAction#fae69f56 message:string = MessageAction;
messageActionBotAllowed#c516d679 flags:# attach_menu:flags.1?true from_request:flags.3?true domain:flags.0?string app:flags.2?BotApp = MessageAction;
messageActionSecureValuesSentMe#1b287353 values:Vector<SecureValue> credentials:SecureCredentialsEncrypted = MessageAction;
messageActionSecureValuesSent#d95c6154 types:Vector<SecureValueType> = MessageAction;
messageActionContactSignUp#f3f25f76 = MessageAction;
messageActionGeoProximityReached#98e0d697 from_id:Peer to_id:Peer distance:int = MessageAction;
messageActionGroupCall#7a0d7f42 flags:# call:InputGroupCall duration:flags.0?int = MessageAction;
messageActionInviteToGroupCall#502f92f7 call:InputGroupCall users:Vector<long> = MessageAction;
messageActionSetMessagesTTL#3c134d7b flags:# period:int auto_setting_from:flags.0?long = MessageAction;
messageActionGroupCallScheduled#b3a07661 call:InputGroupCall schedule_date:int = MessageAction;
messageActionSetChatTheme#b91bbd3a theme:ChatTheme = MessageAction;
messageActionChatJoinedByRequest#ebbca3cb = MessageAction;
messageActionWebViewDataSentMe#47dd8079 text:string data:string = MessageAction;
messageActionWebViewDataSent#b4c38cb5 text:string = MessageAction;
messageActionGiftPremium#48e91302 flags:# currency:string amount:long days:int crypto_currency:flags.0?string crypto_amount:flags.0?long message:flags.1?TextWithEntities = MessageAction;
messageActionTopicCreate#d999256 flags:# title_missing:flags.1?true title:string icon_color:int icon_emoji_id:flags.0?long = MessageAction;
messageActionTopicEdit#c0944820 flags:# title:flags.0?string icon_emoji_id:flags.1?long closed:flags.2?Bool hidden:flags.3?Bool = MessageAction;
messageActionSuggestProfilePhoto#57de635e photo:Photo = MessageAction;
messageActionRequestedPeer#31518e9b button_id:int peers:Vector<Peer> = MessageAction;
messageActionSetChatWallPaper#5060a3f4 flags:# same:flags.0?true for_both:flags.1?true wallpaper:WallPaper = MessageAction;
messageActionGiftCode#31c48347 flags:# via_giveaway:flags.0?true unclaimed:flags.5?true boost_peer:flags.1?Peer days:int slug:string currency:flags.2?string amount:flags.2?long crypto_currency:flags.3?string crypto_amount:flags.3?long message:flags.4?TextWithEntities = MessageAction;
messageActionGiveawayLaunch#a80f51e4 flags:# stars:flags.0?long = MessageAction;
messageActionGiveawayResults#87e2f155 flags:# stars:flags.0?true winners_count:int unclaimed_count:int = MessageAction;
messageActionBoostApply#cc02aa6d boosts:int = MessageAction;
messageActionRequestedPeerSentMe#93b31848 button_id:int peers:Vector<RequestedPeer> = MessageAction;
messageActionPaymentRefunded#41b3e202 flags:# peer:Peer currency:string total_amount:long payload:flags.0?bytes charge:PaymentCharge = MessageAction;
messageActionGiftStars#45d5b021 flags:# currency:string amount:long stars:long crypto_currency:flags.0?string crypto_amount:flags.0?long transaction_id:flags.1?string = MessageAction;
messageActionPrizeStars#b00c47a2 flags:# unclaimed:flags.0?true stars:long transaction_id:string boost_peer:Peer giveaway_msg_id:int = MessageAction;
messageActionStarGift#ea2c31d3 flags:# name_hidden:flags.0?true saved:flags.2?true converted:flags.3?true upgraded:flags.5?true refunded:flags.9?true can_upgrade:flags.10?true prepaid_upgrade:flags.13?true upgrade_separate:flags.16?true auction_acquired:flags.17?true gift:StarGift message:flags.1?TextWithEntities convert_stars:flags.4?long upgrade_msg_id:flags.5?int upgrade_stars:flags.8?long from_id:flags.11?Peer peer:flags.12?Peer saved_id:flags.12?long prepaid_upgrade_hash:flags.14?string gift_msg_id:flags.15?int to_id:flags.18?Peer gift_num:flags.19?int = MessageAction;
messageActionStarGiftUnique#7e1c1187 flags:# upgrade:flags.0?true transferred:flags.1?true saved:flags.2?true refunded:flags.5?true prepaid_upgrade:flags.11?true assigned:flags.13?true from_offer:flags.14?true craft:flags.16?true name_hidden:flags.17?true gift:StarGift can_export_at:flags.3?int transfer_stars:flags.4?long from_id:flags.6?Peer peer:flags.7?Peer saved_id:flags.7?long resale_amount:flags.8?StarsAmount can_transfer_at:flags.9?int can_resell_at:flags.10?int drop_original_details_stars:flags.12?long can_craft_at:flags.15?int message:flags.18?TextWithEntities = MessageAction;
messageActionPaidMessagesRefunded#ac1f1fcd count:int stars:long = MessageAction;
messageActionPaidMessagesPrice#84b88578 flags:# broadcast_messages_allowed:flags.0?true stars:long = MessageAction;
messageActionConferenceCall#2ffe2f7a flags:# missed:flags.0?true active:flags.1?true video:flags.4?true call_id:long duration:flags.2?int other_participants:flags.3?Vector<Peer> = MessageAction;
messageActionTodoCompletions#cc7c5c89 completed:Vector<int> incompleted:Vector<int> = MessageAction;
messageActionTodoAppendTasks#c7edbc83 list:Vector<TodoItem> = MessageAction;
messageActionSuggestedPostApproval#ee7a1596 flags:# rejected:flags.0?true balance_too_low:flags.1?true reject_comment:flags.2?string schedule_date:flags.3?int price:flags.4?StarsAmount = MessageAction;
messageActionSuggestedPostSuccess#95ddcf69 price:StarsAmount = MessageAction;
messageActionSuggestedPostRefund#69f916f8 flags:# payer_initiated:flags.0?true = MessageAction;
messageActionGiftTon#a8a3c699 flags:# currency:string amount:long crypto_currency:string crypto_amount:long transaction_id:flags.0?string = MessageAction;
messageActionSuggestBirthday#2c8f2a25 birthday:Birthday = MessageAction;
messageActionStarGiftPurchaseOffer#774278d4 flags:# accepted:flags.0?true declined:flags.1?true gift:StarGift price:StarsAmount expires_at:int = MessageAction;
messageActionStarGiftPurchaseOfferDeclined#73ada76b flags:# expired:flags.0?true gift:StarGift price:StarsAmount = MessageAction;
messageActionNewCreatorPending#b07ed085 new_creator_id:long = MessageAction;
messageActionChangeCreator#e188503b new_creator_id:long = MessageAction;
messageActionNoForwardsToggle#bf7d6572 prev_value:Bool new_value:Bool = MessageAction;
messageActionNoForwardsRequest#3e2793ba flags:# expired:flags.0?true prev_value:Bool new_value:Bool = MessageAction;
messageActionPollAppendAnswer#9da1cd6c answer:PollAnswer = MessageAction;
messageActionPollDeleteAnswer#399674dc answer:PollAnswer = MessageAction;
messageActionManagedBotCreated#16605e3e bot_id:long = MessageAction;
messageActionChangeCommunity#5d20bae8 flags:# community_id:flags.0?long = MessageAction;
messageActionChatJoinedViaCommunity#4a8bfe80 community_id:long = MessageAction;
messageActionGramTransfer#9329ea33 flags:# comment_encrypted:flags.1?true amount:long peer_address:string transaction_id:string comment:flags.0?string = MessageAction;
messageActionWalletTonConnectRequest#69b8c4bf flags:# accepted:flags.2?true declined:flags.3?true session_id:long expires:int topic:flags.0?string trace_id:flags.1?string dapp_name:flags.4?string = MessageAction;
messageActionNoPaidMessagesTokenUsed#c1d2f1c6 = MessageAction;

dialog#fc89f7f3 flags:# pinned:flags.2?true unread_mark:flags.3?true view_forum_as_messages:flags.6?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int unread_poll_votes_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int ttl_period:flags.5?int = Dialog;
dialogFolder#71bd134c flags:# pinned:flags.2?true folder:Folder peer:Peer top_message:int unread_muted_peers_count:int unread_unmuted_peers_count:int unread_muted_messages_count:int unread_unmuted_messages_count:int = Dialog;
dialogCommunity#f78a0973 flags:# pinned:flags.2?true community_id:long notify_settings:PeerNotifySettings = Dialog;

photoEmpty#2331b22d id:long = Photo;
photo#fb197a65 flags:# has_stickers:flags.0?true id:long access_hash:long file_reference:bytes date:int sizes:Vector<PhotoSize> video_sizes:flags.1?Vector<VideoSize> dc_id:int = Photo;

photoSizeEmpty#e17e23c type:string = PhotoSize;
photoSize#75c78e60 type:string w:int h:int size:int = PhotoSize;
photoCachedSize#21e1ad6 type:string w:int h:int bytes:bytes = PhotoSize;
photoStrippedSize#e0b0bc2e type:string bytes:bytes = PhotoSize;
photoSizeProgressive#fa3efb95 type:string w:int h:int sizes:Vector<int> = PhotoSize;
photoPathSize#d8214d41 type:string bytes:bytes = PhotoSize;

geoPointEmpty#1117dd5f = GeoPoint;
geoPoint#b2a2f663 flags:# long:double lat:double access_hash:long accuracy_radius:flags.0?int = GeoPoint;

auth.sentCode#5e002502 flags:# type:auth.SentCodeType phone_code_hash:string next_type:flags.1?auth.CodeType timeout:flags.2?int = auth.SentCode;
auth.sentCodeSuccess#2390fe44 authorization:auth.Authorization = auth.SentCode;
auth.sentCodePaymentRequired#f8827ebf store_product:string phone_code_hash:string support_email_address:string support_email_subject:string premium_days:int currency:string amount:long = auth.SentCode;

auth.authorization#2ea2c0d4 flags:# setup_password_required:flags.1?true otherwise_relogin_days:flags.1?int tmp_sessions:flags.0?int future_auth_token:flags.2?bytes user:User = auth.Authorization;
auth.authorizationSignUpRequired#44747e9a flags:# terms_of_service:flags.0?help.TermsOfService = auth.Authorization;

auth.exportedAuthorization#b434e2b8 id:long bytes:bytes = auth.ExportedAuthorization;

inputNotifyPeer#b8bc5b0c peer:InputPeer = InputNotifyPeer;
inputNotifyUsers#193b4417 = InputNotifyPeer;
inputNotifyChats#4a95e84e = InputNotifyPeer;
inputNotifyBroadcasts#b1db7c7e = InputNotifyPeer;
inputNotifyForumTopic#5c467992 peer:InputPeer top_msg_id:int = InputNotifyPeer;
inputNotifyCommunity#27bb1adc community:InputChannel = InputNotifyPeer;

inputPeerNotifySettings#cacb6ae2 flags:# show_previews:flags.0?Bool silent:flags.1?Bool mute_until:flags.2?int sound:flags.3?NotificationSound stories_muted:flags.6?Bool stories_hide_sender:flags.7?Bool stories_sound:flags.8?NotificationSound = InputPeerNotifySettings;

peerNotifySettings#99622c0c flags:# show_previews:flags.0?Bool silent:flags.1?Bool mute_until:flags.2?int ios_sound:flags.3?NotificationSound android_sound:flags.4?NotificationSound other_sound:flags.5?NotificationSound stories_muted:flags.6?Bool stories_hide_sender:flags.7?Bool stories_ios_sound:flags.8?NotificationSound stories_android_sound:flags.9?NotificationSound stories_other_sound:flags.10?NotificationSound = PeerNotifySettings;

peerSettings#f47741f7 flags:# report_spam:flags.0?true add_contact:flags.1?true block_contact:flags.2?true share_contact:flags.3?true need_contacts_exception:flags.4?true report_geo:flags.5?true autoarchived:flags.7?true invite_members:flags.8?true request_chat_broadcast:flags.10?true business_bot_paused:flags.11?true business_bot_can_reply:flags.12?true geo_distance:flags.6?int request_chat_title:flags.9?string request_chat_date:flags.9?int business_bot_id:flags.13?long business_bot_manage_url:flags.13?string charge_paid_message_stars:flags.14?long registration_month:flags.15?string phone_country:flags.16?string name_change_date:flags.17?int photo_change_date:flags.18?int = PeerSettings;

wallPaper#a437c3ed id:long flags:# creator:flags.0?true default:flags.1?true pattern:flags.3?true dark:flags.4?true access_hash:long slug:string document:Document settings:flags.2?WallPaperSettings = WallPaper;
wallPaperNoFile#e0804116 id:long flags:# default:flags.1?true dark:flags.4?true settings:flags.2?WallPaperSettings = WallPaper;

inputReportReasonSpam#58dbcab8 = ReportReason;
inputReportReasonViolence#1e22c78d = ReportReason;
inputReportReasonPornography#2e59d922 = ReportReason;
inputReportReasonChildAbuse#adf44ee3 = ReportReason;
inputReportReasonOther#c1e4a2b1 = ReportReason;
inputReportReasonCopyright#9b89f93a = ReportReason;
inputReportReasonGeoIrrelevant#dbd4feed = ReportReason;
inputReportReasonFake#f5ddd6e7 = ReportReason;
inputReportReasonIllegalDrugs#a8eb2be = ReportReason;
inputReportReasonPersonalDetails#9ec7863d = ReportReason;

userFull#b90c2afd flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true voice_messages_forbidden:flags.20?true translations_disabled:flags.23?true stories_pinned_available:flags.26?true blocked_my_stories_from:flags.27?true wallpaper_overridden:flags.28?true contact_require_premium:flags.29?true read_dates_private:flags.30?true flags2:# sponsored_enabled:flags2.7?true can_view_revenue:flags2.9?true bot_can_manage_emoji_status:flags2.10?true display_gifts_button:flags2.16?true noforwards_my_enabled:flags2.23?true noforwards_peer_enabled:flags2.24?true unofficial_security_risk:flags2.26?true id:long about:flags.1?string settings:PeerSettings personal_photo:flags.21?Photo profile_photo:flags.2?Photo fallback_photo:flags.22?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int theme:flags.15?ChatTheme private_forward_name:flags.16?string bot_group_admin_rights:flags.17?ChatAdminRights bot_broadcast_admin_rights:flags.18?ChatAdminRights wallpaper:flags.24?WallPaper stories:flags.25?PeerStories business_work_hours:flags2.0?BusinessWorkHours business_location:flags2.1?BusinessLocation business_greeting_message:flags2.2?BusinessGreetingMessage business_away_message:flags2.3?BusinessAwayMessage business_intro:flags2.4?BusinessIntro birthday:flags2.5?Birthday personal_channel_id:flags2.6?long personal_channel_message:flags2.6?int stargifts_count:flags2.8?int starref_program:flags2.11?StarRefProgram bot_verification:flags2.12?BotVerification send_paid_messages_stars:flags2.14?long disallowed_gifts:flags2.15?DisallowedGiftsSettings stars_rating:flags2.17?StarsRating stars_my_pending_rating:flags2.18?StarsRating stars_my_pending_rating_date:flags2.18?int main_tab:flags2.20?ProfileTab saved_music:flags2.21?Document note:flags2.22?TextWithEntities bot_manager_id:flags2.25?long wallet_card:flags2.19?wallet.Card share_theme:flags2.27?ShareTheme = UserFull;

contact#145ade0b user_id:long mutual:Bool = Contact;

importedContact#c13e3c50 user_id:long client_id:long = ImportedContact;

contactStatus#16d9703b user_id:long status:UserStatus = ContactStatus;

contacts.contactsNotModified#b74ba9d2 = contacts.Contacts;
contacts.contacts#eae87e42 contacts:Vector<Contact> saved_count:int users:Vector<User> = contacts.Contacts;

contacts.importedContacts#77d01c3b imported:Vector<ImportedContact> popular_invites:Vector<PopularContact> retry_contacts:Vector<long> users:Vector<User> = contacts.ImportedContacts;

contacts.blocked#ade1591 blocked:Vector<PeerBlocked> chats:Vector<Chat> users:Vector<User> = contacts.Blocked;
contacts.blockedSlice#e1664194 count:int blocked:Vector<PeerBlocked> chats:Vector<Chat> users:Vector<User> = contacts.Blocked;

messages.dialogs#15ba6c40 dialogs:Vector<Dialog> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.Dialogs;
messages.dialogsSlice#71e094f3 count:int dialogs:Vector<Dialog> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.Dialogs;
messages.dialogsNotModified#f0e3e596 count:int = messages.Dialogs;

messages.messages#1d73e7ea messages:Vector<Message> topics:Vector<ForumTopic> chats:Vector<Chat> users:Vector<User> = messages.Messages;
messages.messagesSlice#5f206716 flags:# inexact:flags.1?true count:int next_rate:flags.0?int offset_id_offset:flags.2?int search_flood:flags.3?SearchPostsFlood messages:Vector<Message> topics:Vector<ForumTopic> chats:Vector<Chat> users:Vector<User> = messages.Messages;
messages.channelMessages#c776ba4e flags:# inexact:flags.1?true pts:int count:int offset_id_offset:flags.2?int messages:Vector<Message> topics:Vector<ForumTopic> chats:Vector<Chat> users:Vector<User> = messages.Messages;
messages.messagesNotModified#74535f21 count:int = messages.Messages;

messages.chats#64ff9fd5 chats:Vector<Chat> = messages.Chats;
messages.chatsSlice#9cd81144 count:int chats:Vector<Chat> = messages.Chats;

messages.chatFull#e5d7d19c full_chat:ChatFull chats:Vector<Chat> users:Vector<User> = messages.ChatFull;

messages.affectedHistory#b45c69d1 pts:int pts_count:int offset:int = messages.AffectedHistory;

inputMessagesFilterEmpty#57e2f66c = MessagesFilter;
inputMessagesFilterPhotos#9609a51c = MessagesFilter;
inputMessagesFilterVideo#9fc00e65 = MessagesFilter;
inputMessagesFilterPhotoVideo#56e9f0e4 = MessagesFilter;
inputMessagesFilterDocument#9eddf188 = MessagesFilter;
inputMessagesFilterUrl#7ef0dd87 = MessagesFilter;
inputMessagesFilterGif#ffc86587 = MessagesFilter;
inputMessagesFilterVoice#50f5c392 = MessagesFilter;
inputMessagesFilterMusic#3751b49e = MessagesFilter;
inputMessagesFilterChatPhotos#3a20ecb8 = MessagesFilter;
inputMessagesFilterPhoneCalls#80c99768 flags:# missed:flags.0?true = MessagesFilter;
inputMessagesFilterRoundVoice#7a7c17a4 = MessagesFilter;
inputMessagesFilterRoundVideo#b549da53 = MessagesFilter;
inputMessagesFilterMyMentions#c1f8e69a = MessagesFilter;
inputMessagesFilterGeo#e7026d0d = MessagesFilter;
inputMessagesFilterContacts#e062db83 = MessagesFilter;
inputMessagesFilterPinned#1bb00451 = MessagesFilter;
inputMessagesFilterPoll#fa2bc90a = MessagesFilter;

updateNewMessage#1f2b0afd message:Message pts:int pts_count:int = Update;
updateMessageID#4e90bfd6 id:int random_id:long = Update;
updateDeleteMessages#a20db0e5 messages:Vector<int> pts:int pts_count:int = Update;
updateUserTyping#2a17bf5c flags:# user_id:long top_msg_id:flags.0?int action:SendMessageAction = Update;
updateChatUserTyping#83487af0 chat_id:long from_id:Peer action:SendMessageAction = Update;
updateChatParticipants#7761198 participants:ChatParticipants = Update;
updateUserStatus#e5bdf8de user_id:long status:UserStatus = Update;
updateUserName#a7848924 user_id:long first_name:string last_name:string usernames:Vector<Username> = Update;
updateNewAuthorization#8951abef flags:# unconfirmed:flags.0?true hash:long date:flags.0?int device:flags.0?string location:flags.0?string = Update;
updateNewEncryptedMessage#12bcbd9a message:EncryptedMessage qts:int = Update;
updateEncryptedChatTyping#1710f156 chat_id:int = Update;
updateEncryption#b4a2e88d chat:EncryptedChat date:int = Update;
updateEncryptedMessagesRead#38fe25b7 chat_id:int max_date:int date:int = Update;
updateChatParticipantAdd#3dda5451 chat_id:long user_id:long inviter_id:long date:int version:int = Update;
updateChatParticipantDelete#e32f3d77 chat_id:long user_id:long version:int = Update;
updateDcOptions#8e5e9873 dc_options:Vector<DcOption> = Update;
updateNotifySettings#bec268ef peer:NotifyPeer notify_settings:PeerNotifySettings = Update;
updateServiceNotification#ebe46819 flags:# popup:flags.0?true invert_media:flags.2?true inbox_date:flags.1?int type:string message:string media:MessageMedia entities:Vector<MessageEntity> = Update;
updatePrivacy#ee3b272a key:PrivacyKey rules:Vector<PrivacyRule> = Update;
updateUserPhone#5492a13 user_id:long phone:string = Update;
updateReadHistoryInbox#9e84bc99 flags:# folder_id:flags.0?int peer:Peer top_msg_id:flags.1?int max_id:int still_unread_count:int pts:int pts_count:int = Update;
updateReadHistoryOutbox#2f2f21bf peer:Peer max_id:int pts:int pts_count:int = Update;
updateWebPage#7f891213 webpage:WebPage pts:int pts_count:int = Update;
updateReadMessagesContents#f8227181 flags:# messages:Vector<int> pts:int pts_count:int date:flags.0?int = Update;
updateChannelTooLong#108d941f flags:# channel_id:long pts:flags.0?int = Update;
updateChannel#635b4c09 channel_id:long = Update;
updateNewChannelMessage#62ba04d9 message:Message pts:int pts_count:int = Update;
updateReadChannelInbox#922e6e10 flags:# folder_id:flags.0?int channel_id:long max_id:int still_unread_count:int pts:int = Update;
updateDeleteChannelMessages#c32d5b12 channel_id:long messages:Vector<int> pts:int pts_count:int = Update;
updateChannelMessageViews#f226ac08 channel_id:long id:int views:int = Update;
updateChatParticipantAdmin#d7ca61a2 chat_id:long user_id:long is_admin:Bool version:int = Update;
updateNewStickerSet#688a30aa stickerset:messages.StickerSet = Update;
updateStickerSetsOrder#bb2d201 flags:# masks:flags.0?true emojis:flags.1?true order:Vector<long> = Update;
updateStickerSets#31c24808 flags:# masks:flags.0?true emojis:flags.1?true = Update;
updateSavedGifs#9375341e = Update;
updateBotInlineQuery#496f379c flags:# query_id:long user_id:long query:string geo:flags.0?GeoPoint peer_type:flags.1?InlineQueryPeerType offset:string = Update;
updateBotInlineSend#12f12a07 flags:# user_id:long query:string geo:flags.0?GeoPoint id:string msg_id:flags.1?InputBotInlineMessageID = Update;
updateEditChannelMessage#1b3f4df7 message:Message pts:int pts_count:int = Update;
updateBotCallbackQuery#b9cfc48d flags:# query_id:long user_id:long peer:Peer msg_id:int chat_instance:long data:flags.0?bytes game_short_name:flags.1?string = Update;
updateEditMessage#e40370a3 message:Message pts:int pts_count:int = Update;
updateInlineBotCallbackQuery#691e9052 flags:# query_id:long user_id:long msg_id:InputBotInlineMessageID chat_instance:long data:flags.0?bytes game_short_name:flags.1?string = Update;
updateReadChannelOutbox#b75f99a9 channel_id:long max_id:int = Update;
updateDraftMessage#edfc111e flags:# peer:Peer top_msg_id:flags.0?int saved_peer_id:flags.1?Peer draft:DraftMessage = Update;
updateReadFeaturedStickers#571d2742 = Update;
updateRecentStickers#9a422c20 = Update;
updateConfig#a229dd06 = Update;
updatePtsChanged#3354678f = Update;
updateChannelWebPage#2f2ba99f channel_id:long webpage:WebPage pts:int pts_count:int = Update;
updateDialogPinned#6e6fe51c flags:# pinned:flags.0?true folder_id:flags.1?int peer:DialogPeer = Update;
updatePinnedDialogs#fa0f3ca2 flags:# folder_id:flags.1?int order:flags.0?Vector<DialogPeer> = Update;
updateBotWebhookJSON#8317c0c3 data:DataJSON = Update;
updateBotWebhookJSONQuery#9b9240a6 query_id:long data:DataJSON timeout:int = Update;
updateBotShippingQuery#b5aefd7d query_id:long user_id:long payload:bytes shipping_address:PostAddress = Update;
updateBotPrecheckoutQuery#8caa9a96 flags:# query_id:long user_id:long payload:bytes info:flags.0?PaymentRequestedInfo shipping_option_id:flags.1?string currency:string total_amount:long = Update;
updatePhoneCall#ab0f6b1e phone_call:PhoneCall = Update;
updateLangPackTooLong#46560264 lang_code:string = Update;
updateLangPack#56022f4d difference:LangPackDifference = Update;
updateFavedStickers#e511996d = Update;
updateChannelReadMessagesContents#25f324f7 flags:# channel_id:long top_msg_id:flags.0?int saved_peer_id:flags.1?Peer messages:Vector<int> = Update;
updateContactsReset#7084a7be = Update;
updateChannelAvailableMessages#b23fc698 channel_id:long available_min_id:int = Update;
updateDialogUnreadMark#b658f23e flags:# unread:flags.0?true peer:DialogPeer saved_peer_id:flags.1?Peer = Update;
updateMessagePoll#d64c522b flags:# peer:flags.1?Peer msg_id:flags.1?int top_msg_id:flags.2?int poll_id:long poll:flags.0?Poll results:PollResults = Update;
updateChatDefaultBannedRights#54c01850 peer:Peer default_banned_rights:ChatBannedRights version:int = Update;
updateFolderPeers#19360dc0 folder_peers:Vector<FolderPeer> pts:int pts_count:int = Update;
updatePeerSettings#6a7e7366 peer:Peer settings:PeerSettings = Update;
updatePeerLocated#b4afcfb0 peers:Vector<PeerLocated> = Update;
updateNewScheduledMessage#39a51dfb message:Message = Update;
updateDeleteScheduledMessages#f2a71983 flags:# peer:Peer messages:Vector<int> sent_messages:flags.0?Vector<int> = Update;
updateTheme#8216fba3 theme:Theme = Update;
updateGeoLiveViewed#871fb939 peer:Peer msg_id:int = Update;
updateLoginToken#564fe691 = Update;
updateMessagePollVote#7699f014 poll_id:long peer:Peer options:Vector<bytes> positions:Vector<int> qts:int = Update;
updateDialogFilter#26ffde7d flags:# id:int filter:flags.0?DialogFilter = Update;
updateDialogFilterOrder#a5d72105 order:Vector<int> = Update;
updateDialogFilters#3504914f = Update;
updatePhoneCallSignalingData#2661bf09 phone_call_id:long data:bytes = Update;
updateChannelMessageForwards#d29a27f4 channel_id:long id:int forwards:int = Update;
updateReadChannelDiscussionInbox#d6b19546 flags:# channel_id:long top_msg_id:int read_max_id:int broadcast_id:flags.0?long broadcast_post:flags.0?int = Update;
updateReadChannelDiscussionOutbox#695c9e7c channel_id:long top_msg_id:int read_max_id:int = Update;
updatePeerBlocked#ebe07752 flags:# blocked:flags.0?true blocked_my_stories_from:flags.1?true peer_id:Peer = Update;
updateChannelUserTyping#8c88c923 flags:# channel_id:long top_msg_id:flags.0?int from_id:Peer action:SendMessageAction = Update;
updatePinnedMessages#ed85eab5 flags:# pinned:flags.0?true peer:Peer messages:Vector<int> pts:int pts_count:int = Update;
updatePinnedChannelMessages#5bb98608 flags:# pinned:flags.0?true channel_id:long messages:Vector<int> pts:int pts_count:int = Update;
updateChat#f89a6a4e chat_id:long = Update;
updateGroupCallParticipants#f2ebdb4e call:InputGroupCall participants:Vector<GroupCallParticipant> version:int = Update;
updateGroupCall#9d2216e0 flags:# live_story:flags.2?true peer:flags.1?Peer call:GroupCall = Update;
updatePeerHistoryTTL#bb9bb9a5 flags:# peer:Peer ttl_period:flags.0?int = Update;
updateChatParticipant#d087663a flags:# chat_id:long date:int actor_id:long user_id:long prev_participant:flags.0?ChatParticipant new_participant:flags.1?ChatParticipant invite:flags.2?ExportedChatInvite qts:int = Update;
updateChannelParticipant#985d3abb flags:# via_chatlist:flags.3?true channel_id:long date:int actor_id:long user_id:long prev_participant:flags.0?ChannelParticipant new_participant:flags.1?ChannelParticipant invite:flags.2?ExportedChatInvite qts:int = Update;
updateBotStopped#c4870a49 user_id:long date:int stopped:Bool qts:int = Update;
updateGroupCallConnection#b783982 flags:# presentation:flags.0?true params:DataJSON = Update;
updateBotCommands#4d712f2e peer:Peer bot_id:long commands:Vector<BotCommand> = Update;
updatePendingJoinRequests#7063c3db peer:Peer requests_pending:int recent_requesters:Vector<long> = Update;
updateBotChatInviteRequester#7cb34d79 flags:# peer:Peer date:int user_id:long about:string invite:ExportedChatInvite qts:int query_id:flags.0?long = Update;
updateMessageReactions#1e297bfa flags:# peer:Peer msg_id:int top_msg_id:flags.0?int saved_peer_id:flags.1?Peer reactions:MessageReactions = Update;
updateAttachMenuBots#17b7a20b = Update;
updateWebViewResultSent#1592b79d query_id:long = Update;
updateBotMenuButton#14b85813 bot_id:long button:BotMenuButton = Update;
updateSavedRingtones#74d8be99 = Update;
updateTranscribedAudio#84cd5a flags:# pending:flags.0?true peer:Peer msg_id:int transcription_id:long text:string = Update;
updateReadFeaturedEmojiStickers#fb4c496c = Update;
updateUserEmojiStatus#28373599 user_id:long emoji_status:EmojiStatus = Update;
updateRecentEmojiStatuses#30f443db = Update;
updateRecentReactions#6f7863f4 = Update;
updateMoveStickerSetToTop#86fccf85 flags:# masks:flags.0?true emojis:flags.1?true stickerset:long = Update;
updateMessageExtendedMedia#d5a41724 peer:Peer msg_id:int extended_media:Vector<MessageExtendedMedia> = Update;
updateUser#20529438 user_id:long = Update;
updateAutoSaveSettings#ec05b097 = Update;
updateStory#75b3b798 peer:Peer story:StoryItem = Update;
updateReadStories#f74e932b peer:Peer max_id:int = Update;
updateStoryID#1bf335b9 id:int random_id:long = Update;
updateStoriesStealthMode#2c084dc1 stealth_mode:StoriesStealthMode = Update;
updateSentStoryReaction#7d627683 peer:Peer story_id:int reaction:Reaction = Update;
updateBotChatBoost#904dd49c peer:Peer boost:Boost qts:int = Update;
updateChannelViewForumAsMessages#7b68920 channel_id:long enabled:Bool = Update;
updatePeerWallpaper#ae3f101d flags:# wallpaper_overridden:flags.1?true peer:Peer wallpaper:flags.0?WallPaper = Update;
updateBotMessageReaction#ac21d3ce peer:Peer msg_id:int date:int actor:Peer old_reactions:Vector<Reaction> new_reactions:Vector<Reaction> qts:int = Update;
updateBotMessageReactions#9cb7759 peer:Peer msg_id:int date:int reactions:Vector<ReactionCount> qts:int = Update;
updateSavedDialogPinned#aeaf9e74 flags:# pinned:flags.0?true peer:DialogPeer = Update;
updatePinnedSavedDialogs#686c85a6 flags:# order:flags.0?Vector<DialogPeer> = Update;
updateSavedReactionTags#39c67432 = Update;
updateSmsJob#f16269d4 job_id:string = Update;
updateQuickReplies#f9470ab2 quick_replies:Vector<QuickReply> = Update;
updateNewQuickReply#f53da717 quick_reply:QuickReply = Update;
updateDeleteQuickReply#53e6f1ec shortcut_id:int = Update;
updateQuickReplyMessage#3e050d0f message:Message = Update;
updateDeleteQuickReplyMessages#566fe7cd shortcut_id:int messages:Vector<int> = Update;
updateBotBusinessConnect#8ae5c97a connection:BotBusinessConnection qts:int = Update;
updateBotNewBusinessMessage#9ddb347c flags:# connection_id:string message:Message reply_to_message:flags.0?Message qts:int = Update;
updateBotEditBusinessMessage#7df587c flags:# connection_id:string message:Message reply_to_message:flags.0?Message qts:int = Update;
updateBotDeleteBusinessMessage#a02a982e connection_id:string peer:Peer messages:Vector<int> qts:int = Update;
updateNewStoryReaction#1824e40b story_id:int peer:Peer reaction:Reaction = Update;
updateStarsBalance#4e80a379 balance:StarsAmount = Update;
updateBusinessBotCallbackQuery#1ea2fda7 flags:# query_id:long user_id:long connection_id:string message:Message reply_to_message:flags.2?Message chat_instance:long data:flags.0?bytes = Update;
updateStarsRevenueStatus#a584b019 peer:Peer status:StarsRevenueStatus = Update;
updateBotPurchasedPaidMedia#283bd312 user_id:long payload:string qts:int = Update;
updatePaidReactionPrivacy#8b725fce private:PaidReactionPrivacy = Update;
updateSentPhoneCode#504aa18f sent_code:auth.SentCode = Update;
updateGroupCallChainBlocks#a477288f call:InputGroupCall sub_chain_id:int blocks:Vector<bytes> next_offset:int = Update;
updateReadMonoForumInbox#77b0e372 channel_id:long saved_peer_id:Peer read_max_id:int = Update;
updateReadMonoForumOutbox#a4a79376 channel_id:long saved_peer_id:Peer read_max_id:int = Update;
updateMonoForumNoPaidException#9f812b08 flags:# exception:flags.0?true channel_id:long saved_peer_id:Peer = Update;
updateGroupCallMessage#d8326f0d call:InputGroupCall message:GroupCallMessage = Update;
updateGroupCallEncryptedMessage#c957a766 call:InputGroupCall from_id:Peer encrypted_message:bytes = Update;
updatePinnedForumTopic#683b2c52 flags:# pinned:flags.0?true peer:Peer topic_id:int = Update;
updatePinnedForumTopics#def143d0 flags:# peer:Peer order:flags.0?Vector<int> = Update;
updateDeleteGroupCallMessages#3e85e92c call:InputGroupCall messages:Vector<int> = Update;
updateStarGiftAuctionState#48e246c2 gift_id:long state:StarGiftAuctionState = Update;
updateStarGiftAuctionUserState#dc58f31e gift_id:long user_state:StarGiftAuctionUserState = Update;
updateEmojiGameInfo#fb9c547a info:messages.EmojiGameInfo = Update;
updateStarGiftCraftFail#ac072444 = Update;
updateChatParticipantRank#bd8367b9 chat_id:long user_id:long rank:string version:int = Update;
updateManagedBot#4880ed9a user_id:long bot_id:long qts:int = Update;
updateBotGuestChatQuery#cdd4093d flags:# query_id:long message:Message reference_messages:flags.0?Vector<Message> qts:int = Update;
updateAiComposeTones#8c0f91fb = Update;
updateJoinChatWebViewDecision#bdac7e70 peer:Peer query_id:long result:JoinChatBotResult = Update;
updateNewBotConnection#b22083a6 flags:# confirmed:flags.0?true bot_id:long date:flags.1?int device:flags.1?string location:flags.1?string = Update;
updateWebBrowserSettings#c39a2ade flags:# open_external_browser:flags.0?true display_close_button:flags.1?true = Update;
updateWebBrowserException#140502d1 flags:# delete:flags.1?true open_external_browser:flags.0?Bool exception:WebDomainException = Update;
updateNewEphemeralMessage#20bcbba1 message:EphemeralMessage = Update;
updateDeleteEphemeralMessages#56dbfcf8 peer:Peer ids:Vector<int> = Update;
updateEditEphemeralMessage#4bbb8f01 message:EphemeralMessage = Update;
updateEphemeralBotCallbackQuery#7c1079d6 flags:# query_id:long user_id:long peer:flags.0?Peer msg_id:int data:bytes chat_instance:flags.1?long message:EphemeralMessage = Update;
updateBotStarsSubscription#6c0d8e23 flags:# canceled:flags.0?true payment_failed:flags.1?true restored:flags.2?true user_id:long payload:bytes qts:int = Update;
updateWalletState#6ac3f2aa state:WalletState = Update;
updateSentWalletTransaction#b1434d4a flags:# gasless:flags.0?true msg_hash:string transaction:flags.1?WalletTransaction = Update;
updateWalletGaslessInfo#a8ae1cac flags:# available:flags.0?true left:int reset_at:int min_amount:long relayer_address:string = Update;
updateWalletTonConnectSession#50a38e0e session:TonConnectSession = Update;
updateWalletTonConnectPendingDisconnect#d1ee8199 session_ids:Vector<long> = Update;

updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;

updates.differenceEmpty#5d75a138 date:int seq:int = updates.Difference;
updates.difference#f49ca0 new_messages:Vector<Message> new_encrypted_messages:Vector<EncryptedMessage> other_updates:Vector<Update> chats:Vector<Chat> users:Vector<User> state:updates.State = updates.Difference;
updates.differenceSlice#a8fb1981 new_messages:Vector<Message> new_encrypted_messages:Vector<EncryptedMessage> other_updates:Vector<Update> chats:Vector<Chat> users:Vector<User> intermediate_state:updates.State = updates.Difference;
updates.differenceTooLong#4afe8f6d pts:int = updates.Difference;

updatesTooLong#e317af7e = Updates;
updateShortMessage#313bc7f8 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true id:int user_id:long message:string pts:int pts_count:int date:int fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?long reply_to:flags.3?MessageReplyHeader entities:flags.7?Vector<MessageEntity> ttl_period:flags.25?int = Updates;
updateShortChatMessage#4d6deea5 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true id:int from_id:long chat_id:long message:string pts:int pts_count:int date:int fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?long reply_to:flags.3?MessageReplyHeader entities:flags.7?Vector<MessageEntity> ttl_period:flags.25?int = Updates;
updateShort#78d4dec1 update:Update date:int = Updates;
updatesCombined#725b04c3 updates:Vector<Update> users:Vector<User> chats:Vector<Chat> date:int seq_start:int seq:int = Updates;
updates#74ae4240 updates:Vector<Update> users:Vector<User> chats:Vector<Chat> date:int seq:int = Updates;
updateShortSentMessage#9015e101 flags:# out:flags.1?true id:int pts:int pts_count:int date:int media:flags.9?MessageMedia entities:flags.7?Vector<MessageEntity> ttl_period:flags.25?int = Updates;

photos.photos#8dca6aa5 photos:Vector<Photo> users:Vector<User> = photos.Photos;
photos.photosSlice#15051f54 count:int photos:Vector<Photo> users:Vector<User> = photos.Photos;

photos.photo#20212ca8 photo:Photo users:Vector<User> = photos.Photo;

upload.file#96a18d5 type:storage.FileType mtime:int bytes:bytes = upload.File;
upload.fileCdnRedirect#f18cda44 dc_id:int file_token:bytes encryption_key:bytes encryption_iv:bytes file_hashes:Vector<FileHash> = upload.File;

dcOption#18b7a10d flags:# ipv6:flags.0?true media_only:flags.1?true tcpo_only:flags.2?true cdn:flags.3?true static:flags.4?true this_port_only:flags.5?true id:int ip_address:string port:int secret:flags.10?bytes = DcOption;

config#cc1a241e flags:# default_p2p_contacts:flags.3?true preload_featured_stickers:flags.4?true revoke_pm_inbox:flags.6?true blocked_mode:flags.8?true force_try_ipv6:flags.14?true date:int expires:int test_mode:Bool this_dc:int dc_options:Vector<DcOption> dc_txt_domain_name:string chat_size_max:int megagroup_size_max:int forwarded_count_max:int online_update_period_ms:int offline_blur_timeout_ms:int offline_idle_timeout_ms:int online_cloud_timeout_ms:int notify_cloud_delay_ms:int notify_default_delay_ms:int push_chat_period_ms:int push_chat_limit:int edit_time_limit:int revoke_time_limit:int revoke_pm_time_limit:int rating_e_decay:int stickers_recent_limit:int channels_read_media_period:int tmp_sessions:flags.0?int call_receive_timeout_ms:int call_ring_timeout_ms:int call_connect_timeout_ms:int call_packet_timeout_ms:int me_url_prefix:string autoupdate_url_prefix:flags.7?string gif_search_username:flags.9?string venue_search_username:flags.10?string img_search_username:flags.11?string static_maps_provider:flags.12?string caption_length_max:int message_length_max:int webfile_dc_id:int suggested_lang_code:flags.2?string lang_pack_version:flags.2?int base_lang_pack_version:flags.2?int reactions_default:flags.15?Reaction autologin_token:flags.16?string = Config;

nearestDc#8e1a1775 country:string this_dc:int nearest_dc:int = NearestDc;

help.appUpdate#ccbbce30 flags:# can_not_skip:flags.0?true id:int version:string text:string entities:Vector<MessageEntity> document:flags.1?Document url:flags.2?string sticker:flags.3?Document = help.AppUpdate;
help.noAppUpdate#c45a6536 = help.AppUpdate;

help.inviteText#18cb9f78 message:string = help.InviteText;

encryptedChatEmpty#ab7ec0a0 id:int = EncryptedChat;
encryptedChatWaiting#66b25953 id:int access_hash:long date:int admin_id:long participant_id:long = EncryptedChat;
encryptedChatRequested#48f1d94c flags:# folder_id:flags.0?int id:int access_hash:long date:int admin_id:long participant_id:long g_a:bytes = EncryptedChat;
encryptedChat#61f0d4c7 id:int access_hash:long date:int admin_id:long participant_id:long g_a_or_b:bytes key_fingerprint:long = EncryptedChat;
encryptedChatDiscarded#1e1c7c45 flags:# history_deleted:flags.0?true id:int = EncryptedChat;

inputEncryptedChat#f141b5e1 chat_id:int access_hash:long = InputEncryptedChat;

encryptedFileEmpty#c21f497e = EncryptedFile;
encryptedFile#a8008cd8 id:long access_hash:long size:long dc_id:int key_fingerprint:int = EncryptedFile;

inputEncryptedFileEmpty#1837c364 = InputEncryptedFile;
inputEncryptedFileUploaded#64bd0306 id:long parts:int md5_checksum:string key_fingerprint:int = InputEncryptedFile;
inputEncryptedFile#5a17b5e5 id:long access_hash:long = InputEncryptedFile;
inputEncryptedFileBigUploaded#2dc173c8 id:long parts:int key_fingerprint:int = InputEncryptedFile;

encryptedMessage#ed18c118 random_id:long chat_id:int date:int bytes:bytes file:EncryptedFile = EncryptedMessage;
encryptedMessageService#23734b06 random_id:long chat_id:int date:int bytes:bytes = EncryptedMessage;

messages.dhConfigNotModified#c0e24635 random:bytes = messages.DhConfig;
messages.dhConfig#2c221edd g:int p:bytes version:int random:bytes = messages.DhConfig;

messages.sentEncryptedMessage#560f8935 date:int = messages.SentEncryptedMessage;
messages.sentEncryptedFile#9493ff32 date:int file:EncryptedFile = messages.SentEncryptedMessage;

inputDocumentEmpty#72f0eaae = InputDocument;
inputDocument#1abfb575 id:long access_hash:long file_reference:bytes = InputDocument;

documentEmpty#36f8c871 id:long = Document;
document#8fd4c4d8 flags:# id:long access_hash:long file_reference:bytes date:int mime_type:string size:long thumbs:flags.0?Vector<PhotoSize> video_thumbs:flags.1?Vector<VideoSize> dc_id:int attributes:Vector<DocumentAttribute> = Document;

help.support#17c6b5f6 phone_number:string user:User = help.Support;

notifyPeer#9fd40bd8 peer:Peer = NotifyPeer;
notifyUsers#b4c83b4c = NotifyPeer;
notifyChats#c007cec3 = NotifyPeer;
notifyBroadcasts#d612e8ef = NotifyPeer;
notifyForumTopic#226e6308 peer:Peer top_msg_id:int = NotifyPeer;
notifyCommunity#be376999 community_id:long = NotifyPeer;

sendMessageTypingAction#16bf744e = SendMessageAction;
sendMessageCancelAction#fd5ec8f5 = SendMessageAction;
sendMessageRecordVideoAction#a187d66f = SendMessageAction;
sendMessageUploadVideoAction#e9763aec progress:int = SendMessageAction;
sendMessageRecordAudioAction#d52f73f7 = SendMessageAction;
sendMessageUploadAudioAction#f351d7ab progress:int = SendMessageAction;
sendMessageUploadPhotoAction#d1d34a26 progress:int = SendMessageAction;
sendMessageUploadDocumentAction#aa0cd9e4 progress:int = SendMessageAction;
sendMessageGeoLocationAction#176f8ba1 = SendMessageAction;
sendMessageChooseContactAction#628cbc6f = SendMessageAction;
sendMessageGamePlayAction#dd6a8f48 = SendMessageAction;
sendMessageRecordRoundAction#88f27fbc = SendMessageAction;
sendMessageUploadRoundAction#243e1c66 progress:int = SendMessageAction;
speakingInGroupCallAction#d92c2285 = SendMessageAction;
sendMessageHistoryImportAction#dbda9246 progress:int = SendMessageAction;
sendMessageChooseStickerAction#b05ac6b1 = SendMessageAction;
sendMessageEmojiInteraction#25972bcb emoticon:string msg_id:int interaction:DataJSON = SendMessageAction;
sendMessageEmojiInteractionSeen#b665902e emoticon:string = SendMessageAction;
sendMessageTextDraftAction#3630b85a flags:# can_stop:flags.0?true keep_on_stop:flags.1?true random_id:long text:TextWithEntities = SendMessageAction;
inputSendMessageRichMessageDraftAction#a937c7be flags:# can_stop:flags.0?true keep_on_stop:flags.1?true random_id:long rich_message:InputRichMessage = SendMessageAction;
sendMessageRichMessageDraftAction#52564893 flags:# can_stop:flags.0?true keep_on_stop:flags.1?true random_id:long rich_message:RichMessage = SendMessageAction;
sendMessageStopDraftAction#fbf902b0 random_id:long = SendMessageAction;

contacts.found#b3134d9d my_results:Vector<Peer> results:Vector<Peer> chats:Vector<Chat> users:Vector<User> = contacts.Found;

inputPrivacyKeyStatusTimestamp#4f96cb18 = InputPrivacyKey;
inputPrivacyKeyChatInvite#bdfb0426 = InputPrivacyKey;
inputPrivacyKeyPhoneCall#fabadc5f = InputPrivacyKey;
inputPrivacyKeyPhoneP2P#db9e70d2 = InputPrivacyKey;
inputPrivacyKeyForwards#a4dd4c08 = InputPrivacyKey;
inputPrivacyKeyProfilePhoto#5719bacc = InputPrivacyKey;
inputPrivacyKeyPhoneNumber#352dafa = InputPrivacyKey;
inputPrivacyKeyAddedByPhone#d1219bdd = InputPrivacyKey;
inputPrivacyKeyVoiceMessages#aee69d68 = InputPrivacyKey;
inputPrivacyKeyAbout#3823cc40 = InputPrivacyKey;
inputPrivacyKeyBirthday#d65a11cc = InputPrivacyKey;
inputPrivacyKeyStarGiftsAutoSave#e1732341 = InputPrivacyKey;
inputPrivacyKeyNoPaidMessages#bdc597b4 = InputPrivacyKey;
inputPrivacyKeySavedMusic#4dbe9226 = InputPrivacyKey;

privacyKeyStatusTimestamp#bc2eab30 = PrivacyKey;
privacyKeyChatInvite#500e6dfa = PrivacyKey;
privacyKeyPhoneCall#3d662b7b = PrivacyKey;
privacyKeyPhoneP2P#39491cc8 = PrivacyKey;
privacyKeyForwards#69ec56a3 = PrivacyKey;
privacyKeyProfilePhoto#96151fed = PrivacyKey;
privacyKeyPhoneNumber#d19ae46d = PrivacyKey;
privacyKeyAddedByPhone#42ffd42b = PrivacyKey;
privacyKeyVoiceMessages#697f414 = PrivacyKey;
privacyKeyAbout#a486b761 = PrivacyKey;
privacyKeyBirthday#2000a518 = PrivacyKey;
privacyKeyStarGiftsAutoSave#2ca4fdf8 = PrivacyKey;
privacyKeyNoPaidMessages#17d348d2 = PrivacyKey;
privacyKeySavedMusic#ff7a571b = PrivacyKey;

inputPrivacyValueAllowContacts#d09e07b = InputPrivacyRule;
inputPrivacyValueAllowAll#184b35ce = InputPrivacyRule;
inputPrivacyValueAllowUsers#131cc67f users:Vector<InputUser> = InputPrivacyRule;
inputPrivacyValueDisallowContacts#ba52007 = InputPrivacyRule;
inputPrivacyValueDisallowAll#d66b66c9 = InputPrivacyRule;
inputPrivacyValueDisallowUsers#90110467 users:Vector<InputUser> = InputPrivacyRule;
inputPrivacyValueAllowChatParticipants#840649cf chats:Vector<long> = InputPrivacyRule;
inputPrivacyValueDisallowChatParticipants#e94f0f86 chats:Vector<long> = InputPrivacyRule;
inputPrivacyValueAllowCloseFriends#2f453e49 = InputPrivacyRule;
inputPrivacyValueAllowPremium#77cdc9f1 = InputPrivacyRule;
inputPrivacyValueAllowBots#5a4fcce5 = InputPrivacyRule;
inputPrivacyValueDisallowBots#c4e57915 = InputPrivacyRule;

privacyValueAllowContacts#fffe1bac = PrivacyRule;
privacyValueAllowAll#65427b82 = PrivacyRule;
privacyValueAllowUsers#b8905fb2 users:Vector<long> = PrivacyRule;
privacyValueDisallowContacts#f888fa1a = PrivacyRule;
privacyValueDisallowAll#8b73e763 = PrivacyRule;
privacyValueDisallowUsers#e4621141 users:Vector<long> = PrivacyRule;
privacyValueAllowChatParticipants#6b134e8e chats:Vector<long> = PrivacyRule;
privacyValueDisallowChatParticipants#41c87565 chats:Vector<long> = PrivacyRule;
privacyValueAllowCloseFriends#f7e8d89b = PrivacyRule;
privacyValueAllowPremium#ece9814b = PrivacyRule;
privacyValueAllowBots#21461b5d = PrivacyRule;
privacyValueDisallowBots#f6a5f82f = PrivacyRule;

account.privacyRules#50a04e45 rules:Vector<PrivacyRule> chats:Vector<Chat> users:Vector<User> = account.PrivacyRules;

accountDaysTTL#b8d0afdf days:int = AccountDaysTTL;

documentAttributeImageSize#6c37c15c w:int h:int = DocumentAttribute;
documentAttributeAnimated#11b58939 = DocumentAttribute;
documentAttributeSticker#6319d612 flags:# mask:flags.1?true alt:string stickerset:InputStickerSet mask_coords:flags.0?MaskCoords = DocumentAttribute;
documentAttributeVideo#43c57c48 flags:# round_message:flags.0?true supports_streaming:flags.1?true nosound:flags.3?true duration:double w:int h:int preload_prefix_size:flags.2?int video_start_ts:flags.4?double video_codec:flags.5?string = DocumentAttribute;
documentAttributeAudio#9852f9c6 flags:# voice:flags.10?true duration:int title:flags.0?string performer:flags.1?string waveform:flags.2?bytes = DocumentAttribute;
documentAttributeFilename#15590068 file_name:string = DocumentAttribute;
documentAttributeHasStickers#9801d2f7 = DocumentAttribute;
documentAttributeCustomEmoji#fd149899 flags:# free:flags.0?true text_color:flags.1?true alt:string stickerset:InputStickerSet = DocumentAttribute;

messages.stickersNotModified#f1749a22 = messages.Stickers;
messages.stickers#30a6ec7e hash:long stickers:Vector<Document> = messages.Stickers;

stickerPack#12b299d4 emoticon:string documents:Vector<long> = StickerPack;

messages.allStickersNotModified#e86602c3 = messages.AllStickers;
messages.allStickers#cdbbcebb hash:long sets:Vector<StickerSet> = messages.AllStickers;

messages.affectedMessages#84d19185 pts:int pts_count:int = messages.AffectedMessages;

webPageEmpty#211a1788 flags:# id:long url:flags.0?string = WebPage;
webPagePending#b0d13e47 flags:# id:long url:flags.0?string date:int = WebPage;
webPage#e89c45b2 flags:# has_large_media:flags.13?true video_cover_photo:flags.14?true id:long url:string display_url:string hash:int type:flags.0?string site_name:flags.1?string title:flags.2?string description:flags.3?string photo:flags.4?Photo embed_url:flags.5?string embed_type:flags.5?string embed_width:flags.6?int embed_height:flags.6?int duration:flags.7?int author:flags.8?string document:flags.9?Document cached_page:flags.10?Page attributes:flags.12?Vector<WebPageAttribute> = WebPage;
webPageNotModified#7311ca11 flags:# cached_page_views:flags.0?int = WebPage;

authorization#ad01d61d flags:# current:flags.0?true official_app:flags.1?true password_pending:flags.2?true encrypted_requests_disabled:flags.3?true call_requests_disabled:flags.4?true unconfirmed:flags.5?true hash:long device_model:string platform:string system_version:string api_id:int app_name:string app_version:string date_created:int date_active:int ip:string country:string region:string = Authorization;

account.authorizations#4bff8ea0 authorization_ttl_days:int authorizations:Vector<Authorization> = account.Authorizations;

account.password#957b50fb flags:# has_recovery:flags.0?true has_secure_values:flags.1?true has_password:flags.2?true current_algo:flags.2?PasswordKdfAlgo srp_B:flags.2?bytes srp_id:flags.2?long hint:flags.3?string email_unconfirmed_pattern:flags.4?string new_algo:PasswordKdfAlgo new_secure_algo:SecurePasswordKdfAlgo secure_random:bytes pending_reset_date:flags.5?int login_email_pattern:flags.6?string = account.Password;

account.passwordSettings#9a5c33e5 flags:# email:flags.0?string secure_settings:flags.1?SecureSecretSettings = account.PasswordSettings;

account.passwordInputSettings#c23727c9 flags:# new_algo:flags.0?PasswordKdfAlgo new_password_hash:flags.0?bytes hint:flags.0?string email:flags.1?string new_secure_settings:flags.2?SecureSecretSettings = account.PasswordInputSettings;

auth.passwordRecovery#137948a5 email_pattern:string = auth.PasswordRecovery;

receivedNotifyMessage#a384b779 id:int flags:int = ReceivedNotifyMessage;

chatInviteExported#a22cbd96 flags:# revoked:flags.0?true permanent:flags.5?true request_needed:flags.6?true link:string admin_id:long date:int start_date:flags.4?int expire_date:flags.1?int usage_limit:flags.2?int usage:flags.3?int requested:flags.7?int subscription_expired:flags.10?int title:flags.8?string subscription_pricing:flags.9?StarsSubscriptionPricing = ExportedChatInvite;
chatInvitePublicJoinRequests#ed107ab7 = ExportedChatInvite;

chatInviteAlready#5a686d7c chat:Chat = ChatInvite;
chatInvite#5c9d3702 flags:# channel:flags.0?true broadcast:flags.1?true public:flags.2?true megagroup:flags.3?true request_needed:flags.6?true verified:flags.7?true scam:flags.8?true fake:flags.9?true can_refulfill_subscription:flags.11?true title:string about:flags.5?string photo:Photo participants_count:int participants:flags.4?Vector<User> color:int subscription_pricing:flags.10?StarsSubscriptionPricing subscription_form_id:flags.12?long bot_verification:flags.13?BotVerification = ChatInvite;
chatInvitePeek#61695cb0 chat:Chat expires:int = ChatInvite;

inputStickerSetEmpty#ffb62b95 = InputStickerSet;
inputStickerSetID#9de7a269 id:long access_hash:long = InputStickerSet;
inputStickerSetShortName#861cc8a0 short_name:string = InputStickerSet;
inputStickerSetAnimatedEmoji#28703c8 = InputStickerSet;
inputStickerSetDice#e67f520e emoticon:string = InputStickerSet;
inputStickerSetAnimatedEmojiAnimations#cde3739 = InputStickerSet;
inputStickerSetPremiumGifts#c88b3b02 = InputStickerSet;
inputStickerSetEmojiGenericAnimations#4c4d4ce = InputStickerSet;
inputStickerSetEmojiDefaultStatuses#29d0f5ee = InputStickerSet;
inputStickerSetEmojiDefaultTopicIcons#44c1f8e9 = InputStickerSet;
inputStickerSetEmojiChannelDefaultStatuses#49748553 = InputStickerSet;
inputStickerSetTonGifts#1cf671a0 = InputStickerSet;

stickerSet#2dd14edc flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true emojis:flags.7?true text_color:flags.9?true channel_emoji_status:flags.10?true creator:flags.11?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string thumbs:flags.4?Vector<PhotoSize> thumb_dc_id:flags.4?int thumb_version:flags.4?int thumb_document_id:flags.8?long count:int hash:int = StickerSet;

messages.stickerSet#6e153f16 set:StickerSet packs:Vector<StickerPack> keywords:Vector<StickerKeyword> documents:Vector<Document> = messages.StickerSet;
messages.stickerSetNotModified#d3f924eb = messages.StickerSet;

botCommand#9852d6d2 flags:# ephemeral:flags.0?true command:string description:string = BotCommand;

botInfo#4d8a0299 flags:# has_preview_medias:flags.6?true user_id:flags.0?long description:flags.1?string description_photo:flags.4?Photo description_document:flags.5?Document commands:flags.2?Vector<BotCommand> menu_button:flags.3?BotMenuButton privacy_policy_url:flags.7?string app_settings:flags.8?BotAppSettings verifier_settings:flags.9?BotVerifierSettings = BotInfo;

keyboardButton#2f67a72f flags:# style:flags.10?KeyboardButtonStyle text:string type:ButtonType = KeyboardButton;

keyboardButtonRow#77608b83 buttons:Vector<KeyboardButton> = KeyboardButtonRow;

replyKeyboardHide#a03e5b85 flags:# selective:flags.2?true = ReplyMarkup;
replyKeyboardForceReply#86b40b08 flags:# single_use:flags.1?true selective:flags.2?true placeholder:flags.3?string = ReplyMarkup;
replyKeyboardMarkup#85dd99d1 flags:# resize:flags.0?true single_use:flags.1?true selective:flags.2?true persistent:flags.4?true force_reply:flags.5?true rows:Vector<KeyboardButtonRow> placeholder:flags.3?string = ReplyMarkup;
replyInlineMarkup#b2b15770 flags:# force_reply:flags.5?true rows:Vector<KeyboardInlineButtonRow> = ReplyMarkup;

messageEntityUnknown#bb92ba95 offset:int length:int = MessageEntity;
messageEntityMention#fa04579d offset:int length:int = MessageEntity;
messageEntityHashtag#6f635b0d offset:int length:int = MessageEntity;
messageEntityBotCommand#6cef8ac7 offset:int length:int = MessageEntity;
messageEntityUrl#6ed02538 offset:int length:int = MessageEntity;
messageEntityEmail#64e475c2 offset:int length:int = MessageEntity;
messageEntityBold#bd610bc9 offset:int length:int = MessageEntity;
messageEntityItalic#826f8b60 offset:int length:int = MessageEntity;
messageEntityCode#28a20571 offset:int length:int = MessageEntity;
messageEntityPre#73924be0 offset:int length:int language:string = MessageEntity;
messageEntityTextUrl#76a6d327 offset:int length:int url:string = MessageEntity;
messageEntityMentionName#dc7b1140 offset:int length:int user_id:long = MessageEntity;
inputMessageEntityMentionName#208e68c9 offset:int length:int user_id:InputUser = MessageEntity;
messageEntityPhone#9b69e34b offset:int length:int = MessageEntity;
messageEntityCashtag#4c4e743f offset:int length:int = MessageEntity;
messageEntityUnderline#9c4e7e8b offset:int length:int = MessageEntity;
messageEntityStrike#bf0693d4 offset:int length:int = MessageEntity;
messageEntityBankCard#761e6af4 offset:int length:int = MessageEntity;
messageEntitySpoiler#32ca960f offset:int length:int = MessageEntity;
messageEntityCustomEmoji#c8cf05f8 offset:int length:int document_id:long = MessageEntity;
messageEntityBlockquote#f1ccaaac flags:# collapsed:flags.0?true offset:int length:int = MessageEntity;
messageEntityFormattedDate#904ac7c7 flags:# relative:flags.0?true short_time:flags.1?true long_time:flags.2?true short_date:flags.3?true long_date:flags.4?true day_of_week:flags.5?true offset:int length:int date:int = MessageEntity;
messageEntityDiffInsert#71777116 offset:int length:int = MessageEntity;
messageEntityDiffReplace#c6c1e5a7 offset:int length:int old_text:string = MessageEntity;
messageEntityDiffDelete#652c1c5 offset:int length:int = MessageEntity;
messageEntityTonAddress#fdabbfcd offset:int length:int = MessageEntity;

inputChannelEmpty#ee8c1e86 = InputChannel;
inputChannel#f35aec28 channel_id:long access_hash:long = InputChannel;
inputChannelFromMessage#5b934f9d peer:InputPeer msg_id:int channel_id:long = InputChannel;

contacts.resolvedPeer#7f077ad9 peer:Peer chats:Vector<Chat> users:Vector<User> = contacts.ResolvedPeer;

messageRange#ae30253 min_id:int max_id:int = MessageRange;

updates.channelDifferenceEmpty#3e11affb flags:# final:flags.0?true pts:int timeout:flags.1?int = updates.ChannelDifference;
updates.channelDifferenceTooLong#a4bcc6fe flags:# final:flags.0?true timeout:flags.1?int dialog:Dialog messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = updates.ChannelDifference;
updates.channelDifference#2064674e flags:# final:flags.0?true pts:int timeout:flags.1?int new_messages:Vector<Message> other_updates:Vector<Update> chats:Vector<Chat> users:Vector<User> = updates.ChannelDifference;

channelMessagesFilterEmpty#94d42ee7 = ChannelMessagesFilter;
channelMessagesFilter#cd77d957 flags:# exclude_new_messages:flags.1?true ranges:Vector<MessageRange> = ChannelMessagesFilter;

channelParticipant#1bd54456 flags:# user_id:long date:int subscription_until_date:flags.0?int rank:flags.2?string = ChannelParticipant;
channelParticipantSelf#a9478a1a flags:# via_request:flags.0?true user_id:long inviter_id:long date:int subscription_until_date:flags.1?int rank:flags.2?string = ChannelParticipant;
channelParticipantCreator#2fe601d3 flags:# user_id:long admin_rights:ChatAdminRights rank:flags.0?string = ChannelParticipant;
channelParticipantAdmin#34c3bb53 flags:# can_edit:flags.0?true self:flags.1?true user_id:long inviter_id:flags.1?long promoted_by:long date:int admin_rights:ChatAdminRights rank:flags.2?string = ChannelParticipant;
channelParticipantBanned#d5f0ad91 flags:# left:flags.0?true peer:Peer kicked_by:long date:int banned_rights:ChatBannedRights rank:flags.2?string = ChannelParticipant;
channelParticipantLeft#1b03f006 peer:Peer = ChannelParticipant;

channelParticipantsRecent#de3f3c79 = ChannelParticipantsFilter;
channelParticipantsAdmins#b4608969 = ChannelParticipantsFilter;
channelParticipantsKicked#a3b54985 q:string = ChannelParticipantsFilter;
channelParticipantsBots#b0d1865b = ChannelParticipantsFilter;
channelParticipantsBanned#1427a5e1 q:string = ChannelParticipantsFilter;
channelParticipantsSearch#656ac4b q:string = ChannelParticipantsFilter;
channelParticipantsContacts#bb6ae88d q:string = ChannelParticipantsFilter;
channelParticipantsMentions#e04b5ceb flags:# q:flags.0?string top_msg_id:flags.1?int = ChannelParticipantsFilter;

channels.channelParticipants#9ab0feaf count:int participants:Vector<ChannelParticipant> chats:Vector<Chat> users:Vector<User> = channels.ChannelParticipants;
channels.channelParticipantsNotModified#f0173fe9 = channels.ChannelParticipants;

channels.channelParticipant#dfb80317 participant:ChannelParticipant chats:Vector<Chat> users:Vector<User> = channels.ChannelParticipant;

help.termsOfService#780a0310 flags:# popup:flags.0?true id:DataJSON text:string entities:Vector<MessageEntity> min_age_confirm:flags.1?int = help.TermsOfService;

messages.savedGifsNotModified#e8025ca2 = messages.SavedGifs;
messages.savedGifs#84a02a0d hash:long gifs:Vector<Document> = messages.SavedGifs;

inputBotInlineMessageMediaAuto#3380c786 flags:# invert_media:flags.3?true message:string entities:flags.1?Vector<MessageEntity> reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
inputBotInlineMessageText#3dcd7a87 flags:# no_webpage:flags.0?true invert_media:flags.3?true message:string entities:flags.1?Vector<MessageEntity> reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
inputBotInlineMessageMediaGeo#96929a85 flags:# geo_point:InputGeoPoint heading:flags.0?int period:flags.1?int proximity_notification_radius:flags.3?int reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
inputBotInlineMessageMediaVenue#417bbf11 flags:# geo_point:InputGeoPoint title:string address:string provider:string venue_id:string venue_type:string reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
inputBotInlineMessageMediaContact#a6edbffd flags:# phone_number:string first_name:string last_name:string vcard:string reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
inputBotInlineMessageGame#4b425864 flags:# reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
inputBotInlineMessageMediaInvoice#d7e78225 flags:# title:string description:string photo:flags.0?InputWebDocument invoice:Invoice payload:bytes provider:string provider_data:DataJSON reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
inputBotInlineMessageMediaWebPage#bddcc510 flags:# invert_media:flags.3?true force_large_media:flags.4?true force_small_media:flags.5?true optional:flags.6?true message:string entities:flags.1?Vector<MessageEntity> url:string reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
inputBotInlineMessageRichMessage#b43df56c flags:# reply_markup:flags.2?ReplyMarkup rich_message:InputRichMessage = InputBotInlineMessage;

inputBotInlineResult#88bf9319 flags:# id:string type:string title:flags.1?string description:flags.2?string url:flags.3?string thumb:flags.4?InputWebDocument content:flags.5?InputWebDocument send_message:InputBotInlineMessage = InputBotInlineResult;
inputBotInlineResultPhoto#a8d864a7 id:string type:string photo:InputPhoto send_message:InputBotInlineMessage = InputBotInlineResult;
inputBotInlineResultDocument#fff8fdc4 flags:# id:string type:string title:flags.1?string description:flags.2?string document:InputDocument send_message:InputBotInlineMessage = InputBotInlineResult;
inputBotInlineResultGame#4fa417f2 id:string short_name:string send_message:InputBotInlineMessage = InputBotInlineResult;

botInlineMessageMediaAuto#764cf810 flags:# invert_media:flags.3?true message:string entities:flags.1?Vector<MessageEntity> reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
botInlineMessageText#8c7f65e2 flags:# no_webpage:flags.0?true invert_media:flags.3?true message:string entities:flags.1?Vector<MessageEntity> reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
botInlineMessageMediaGeo#51846fd flags:# geo:GeoPoint heading:flags.0?int period:flags.1?int proximity_notification_radius:flags.3?int reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
botInlineMessageMediaVenue#8a86659c flags:# geo:GeoPoint title:string address:string provider:string venue_id:string venue_type:string reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
botInlineMessageMediaContact#18d1cdc2 flags:# phone_number:string first_name:string last_name:string vcard:string reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
botInlineMessageMediaInvoice#354a9b09 flags:# shipping_address_requested:flags.1?true test:flags.3?true title:string description:string photo:flags.0?WebDocument currency:string total_amount:long reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
botInlineMessageMediaWebPage#809ad9a6 flags:# invert_media:flags.3?true force_large_media:flags.4?true force_small_media:flags.5?true manual:flags.7?true safe:flags.8?true message:string entities:flags.1?Vector<MessageEntity> url:string reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
botInlineMessageRichMessage#a617e7b flags:# reply_markup:flags.2?ReplyMarkup rich_message:RichMessage = BotInlineMessage;

botInlineResult#11965f3a flags:# id:string type:string title:flags.1?string description:flags.2?string url:flags.3?string thumb:flags.4?WebDocument content:flags.5?WebDocument send_message:BotInlineMessage = BotInlineResult;
botInlineMediaResult#17db940b flags:# id:string type:string photo:flags.0?Photo document:flags.1?Document title:flags.2?string description:flags.3?string send_message:BotInlineMessage = BotInlineResult;

messages.botResults#e021f2f6 flags:# gallery:flags.0?true query_id:long next_offset:flags.1?string switch_pm:flags.2?InlineBotSwitchPM switch_webview:flags.3?InlineBotWebView results:Vector<BotInlineResult> cache_time:int users:Vector<User> = messages.BotResults;

exportedMessageLink#5dab1af4 link:string html:string = ExportedMessageLink;

messageFwdHeader#4e4df4bb flags:# imported:flags.7?true saved_out:flags.11?true from_id:flags.0?Peer from_name:flags.5?string date:int channel_post:flags.2?int post_author:flags.3?string saved_from_peer:flags.4?Peer saved_from_msg_id:flags.4?int saved_from_id:flags.8?Peer saved_from_name:flags.9?string saved_date:flags.10?int psa_type:flags.6?string = MessageFwdHeader;

auth.codeTypeSms#72a3158c = auth.CodeType;
auth.codeTypeCall#741cd3e3 = auth.CodeType;
auth.codeTypeFlashCall#226ccefb = auth.CodeType;
auth.codeTypeMissedCall#d61ad6ee = auth.CodeType;
auth.codeTypeFragmentSms#6ed998c = auth.CodeType;

auth.sentCodeTypeApp#3dbb5986 length:int = auth.SentCodeType;
auth.sentCodeTypeSms#c000bba2 length:int = auth.SentCodeType;
auth.sentCodeTypeCall#5353e5a7 length:int = auth.SentCodeType;
auth.sentCodeTypeFlashCall#ab03c6d9 pattern:string = auth.SentCodeType;
auth.sentCodeTypeMissedCall#82006484 prefix:string length:int = auth.SentCodeType;
auth.sentCodeTypeEmailCode#f450f59b flags:# apple_signin_allowed:flags.0?true google_signin_allowed:flags.1?true email_pattern:string length:int reset_available_period:flags.3?int reset_pending_date:flags.4?int = auth.SentCodeType;
auth.sentCodeTypeSetUpEmailRequired#a5491dea flags:# apple_signin_allowed:flags.0?true google_signin_allowed:flags.1?true = auth.SentCodeType;
auth.sentCodeTypeFragmentSms#d9565c39 url:string length:int = auth.SentCodeType;
auth.sentCodeTypeFirebaseSms#9fd736 flags:# nonce:flags.0?bytes play_integrity_project_id:flags.2?long play_integrity_nonce:flags.2?bytes receipt:flags.1?string push_timeout:flags.1?int length:int = auth.SentCodeType;
auth.sentCodeTypeSmsWord#a416ac81 flags:# beginning:flags.0?string = auth.SentCodeType;
auth.sentCodeTypeSmsPhrase#b37794af flags:# beginning:flags.0?string = auth.SentCodeType;

messages.botCallbackAnswer#36585ea4 flags:# alert:flags.1?true has_url:flags.3?true native_ui:flags.4?true message:flags.0?string url:flags.2?string cache_time:int = messages.BotCallbackAnswer;

messages.messageEditData#26b5dde6 flags:# caption:flags.0?true = messages.MessageEditData;

inputBotInlineMessageID#890c3d89 dc_id:int id:long access_hash:long = InputBotInlineMessageID;
inputBotInlineMessageID64#b6d915d7 dc_id:int owner_id:long id:int access_hash:long = InputBotInlineMessageID;

inlineBotSwitchPM#3c20629f text:string start_param:string = InlineBotSwitchPM;

messages.peerDialogs#3371c354 dialogs:Vector<Dialog> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> state:updates.State = messages.PeerDialogs;

topPeer#edcdc05b peer:Peer rating:double = TopPeer;

topPeerCategoryBotsPM#ab661b5b = TopPeerCategory;
topPeerCategoryBotsInline#148677e2 = TopPeerCategory;
topPeerCategoryCorrespondents#637b7ed = TopPeerCategory;
topPeerCategoryGroups#bd17a14a = TopPeerCategory;
topPeerCategoryChannels#161d9628 = TopPeerCategory;
topPeerCategoryPhoneCalls#1e76a78c = TopPeerCategory;
topPeerCategoryForwardUsers#a8406ca9 = TopPeerCategory;
topPeerCategoryForwardChats#fbeec0f0 = TopPeerCategory;
topPeerCategoryBotsApp#fd9e7bec = TopPeerCategory;
topPeerCategoryBotsGuestChat#6c24f3dd = TopPeerCategory;

topPeerCategoryPeers#fb834291 category:TopPeerCategory count:int peers:Vector<TopPeer> = TopPeerCategoryPeers;

contacts.topPeersNotModified#de266ef5 = contacts.TopPeers;
contacts.topPeers#70b772a8 categories:Vector<TopPeerCategoryPeers> chats:Vector<Chat> users:Vector<User> = contacts.TopPeers;
contacts.topPeersDisabled#b52c939d = contacts.TopPeers;

draftMessageEmpty#1b0c841a flags:# date:flags.0?int = DraftMessage;
draftMessage#60fe3294 flags:# no_webpage:flags.1?true invert_media:flags.6?true reply_to:flags.4?InputReplyTo message:string entities:flags.3?Vector<MessageEntity> media:flags.5?InputMedia date:int effect:flags.7?long suggested_post:flags.8?SuggestedPost rich_message:flags.9?RichMessage = DraftMessage;

messages.featuredStickersNotModified#c6dc0c66 count:int = messages.FeaturedStickers;
messages.featuredStickers#be382906 flags:# premium:flags.0?true hash:long count:int sets:Vector<StickerSetCovered> unread:Vector<long> = messages.FeaturedStickers;

messages.recentStickersNotModified#b17f890 = messages.RecentStickers;
messages.recentStickers#88d37c56 hash:long packs:Vector<StickerPack> stickers:Vector<Document> dates:Vector<int> = messages.RecentStickers;

messages.archivedStickers#4fcba9c8 count:int sets:Vector<StickerSetCovered> = messages.ArchivedStickers;

messages.stickerSetInstallResultSuccess#38641628 = messages.StickerSetInstallResult;
messages.stickerSetInstallResultArchive#35e410a8 sets:Vector<StickerSetCovered> = messages.StickerSetInstallResult;

stickerSetCovered#6410a5d2 set:StickerSet cover:Document = StickerSetCovered;
stickerSetMultiCovered#3407e51b set:StickerSet covers:Vector<Document> = StickerSetCovered;
stickerSetFullCovered#40d13c0e set:StickerSet packs:Vector<StickerPack> keywords:Vector<StickerKeyword> documents:Vector<Document> = StickerSetCovered;
stickerSetNoCovered#77b15d1c set:StickerSet = StickerSetCovered;

maskCoords#aed6dbb2 n:int x:double y:double zoom:double = MaskCoords;

inputStickeredMediaPhoto#4a992157 id:InputPhoto = InputStickeredMedia;
inputStickeredMediaDocument#438865b id:InputDocument = InputStickeredMedia;

game#bdf9653b flags:# id:long access_hash:long short_name:string title:string description:string photo:Photo document:flags.0?Document = Game;

inputGameID#32c3e77 id:long access_hash:long = InputGame;
inputGameShortName#c331e80a bot_id:InputUser short_name:string = InputGame;

highScore#73a379eb pos:int user_id:long score:int = HighScore;

messages.highScores#9a3bfd99 scores:Vector<HighScore> users:Vector<User> = messages.HighScores;

textEmpty#dc3d824f = RichText;
textPlain#744694e0 text:string = RichText;
textBold#6724abc4 text:RichText = RichText;
textItalic#d912a59c text:RichText = RichText;
textUnderline#c12622c4 text:RichText = RichText;
textStrike#9bf8bb95 text:RichText = RichText;
textFixed#6c3f19b9 text:RichText = RichText;
textUrl#3c2884c1 text:RichText url:string webpage_id:long = RichText;
textEmail#de5a0dd6 text:RichText email:string = RichText;
textConcat#7e6260d7 texts:Vector<RichText> = RichText;
textSubscript#ed6a8504 text:RichText = RichText;
textSuperscript#c7fb5e01 text:RichText = RichText;
textMarked#34b8621 text:RichText = RichText;
textPhone#1ccb966a text:RichText phone:string = RichText;
textImage#81ccf4f document_id:long w:int h:int = RichText;
textAnchor#35553762 text:RichText name:string = RichText;
textMath#9d2eac97 source:string = RichText;
textCustomEmoji#a26156c0 document_id:long alt:string = RichText;
textSpoiler#4c2a5d62 text:RichText = RichText;
textMention#cd24cf44 text:RichText = RichText;
textHashtag#519524ea text:RichText = RichText;
textBotCommand#2ff29d3 text:RichText = RichText;
textCashtag#7b9e1801 text:RichText = RichText;
textAutoUrl#ac6a83aa text:RichText = RichText;
textAutoEmail#c556a45d text:RichText = RichText;
textAutoPhone#24c26789 text:RichText = RichText;
textBankCard#b956812d text:RichText = RichText;
textMentionName#1a9fbfc text:RichText user_id:long = RichText;
textDate#a5b45e2b flags:# relative:flags.0?true short_time:flags.1?true long_time:flags.2?true short_date:flags.3?true long_date:flags.4?true day_of_week:flags.5?true text:RichText date:int = RichText;
textDiff#9686cb50 text:RichText old_text:RichText = RichText;
textButton#afc79cd6 flags:# text:RichText type:InlineButtonType style:flags.0?RichButtonStyle = RichText;
textTonAddress#3cd2a36a text:RichText = RichText;

pageBlockUnsupported#13567e8a = PageBlock;
pageBlockTitle#70abc3fd text:RichText = PageBlock;
pageBlockSubtitle#8ffa9a1f text:RichText = PageBlock;
pageBlockAuthorDate#baafe5e0 author:RichText published_date:int = PageBlock;
pageBlockHeader#bfd064ec text:RichText = PageBlock;
pageBlockSubheader#f12bb6e1 text:RichText = PageBlock;
pageBlockParagraph#467a0766 text:RichText = PageBlock;
pageBlockPreformatted#c070d93e text:RichText language:string = PageBlock;
pageBlockFooter#48870999 text:RichText = PageBlock;
pageBlockDivider#db20b188 = PageBlock;
pageBlockAnchor#ce0d37b0 name:string = PageBlock;
pageBlockList#e4e88011 items:Vector<PageListItem> = PageBlock;
pageBlockBlockquote#66d1670b flags:# collapsed:flags.0?true text:RichText caption:RichText = PageBlock;
pageBlockPullquote#4f4456d3 text:RichText caption:RichText = PageBlock;
pageBlockPhoto#1759c560 flags:# spoiler:flags.1?true photo_id:long caption:PageCaption url:flags.0?string webpage_id:flags.0?long = PageBlock;
pageBlockVideo#7c8fe7b6 flags:# autoplay:flags.0?true loop:flags.1?true spoiler:flags.2?true video_id:long caption:PageCaption = PageBlock;
pageBlockCover#39f23300 cover:PageBlock = PageBlock;
pageBlockEmbed#a8718dc5 flags:# full_width:flags.0?true allow_scrolling:flags.3?true url:flags.1?string html:flags.2?string poster_photo_id:flags.4?long w:flags.5?int h:flags.5?int caption:PageCaption = PageBlock;
pageBlockEmbedPost#f259a80b url:string webpage_id:long author_photo_id:long author:string date:int blocks:Vector<PageBlock> caption:PageCaption = PageBlock;
pageBlockCollage#65a0fa4d items:Vector<PageBlock> caption:PageCaption = PageBlock;
pageBlockSlideshow#31f9590 items:Vector<PageBlock> caption:PageCaption = PageBlock;
pageBlockChannel#ef1751b5 channel:Chat = PageBlock;
pageBlockAudio#804361ea audio_id:long caption:PageCaption = PageBlock;
pageBlockKicker#1e148390 text:RichText = PageBlock;
pageBlockTable#bf4dea82 flags:# bordered:flags.0?true striped:flags.1?true compact:flags.2?true title:RichText rows:Vector<PageTableRow> = PageBlock;
pageBlockOrderedList#1fd6f6c1 flags:# reversed:flags.2?true items:Vector<PageListOrderedItem> start:flags.0?int type:flags.1?string = PageBlock;
pageBlockDetails#76768bed flags:# open:flags.0?true blocks:Vector<PageBlock> title:RichText = PageBlock;
pageBlockRelatedArticles#16115a96 title:RichText articles:Vector<PageRelatedArticle> = PageBlock;
pageBlockMap#a44f3ef6 geo:GeoPoint zoom:int w:int h:int caption:PageCaption = PageBlock;
pageBlockHeading1#baff072f text:RichText = PageBlock;
pageBlockHeading2#96b2aec text:RichText = PageBlock;
pageBlockHeading3#67e731ad text:RichText = PageBlock;
pageBlockHeading4#b532772b text:RichText = PageBlock;
pageBlockHeading5#dbbe6c6a text:RichText = PageBlock;
pageBlockHeading6#682a41a9 text:RichText = PageBlock;
pageBlockMath#59080c20 source:string = PageBlock;
pageBlockThinking#3c29a3e2 text:RichText = PageBlock;
inputPageBlockMap#574b617f geo:InputGeoPoint zoom:int w:int h:int caption:PageCaption = PageBlock;
pageBlockBlockquoteBlocks#e6e47c4 blocks:Vector<PageBlock> caption:RichText = PageBlock;
pageBlockButtonRow#6d640318 flags:# align_left:flags.0?true align_center:flags.1?true align_right:flags.2?true buttons:Vector<PageButton> = PageBlock;
pageBlockDocument#38fa3ba3 document_id:long caption:PageCaption = PageBlock;

phoneCallDiscardReasonMissed#85e42301 = PhoneCallDiscardReason;
phoneCallDiscardReasonDisconnect#e095c1a0 = PhoneCallDiscardReason;
phoneCallDiscardReasonHangup#57adc690 = PhoneCallDiscardReason;
phoneCallDiscardReasonBusy#faf7e8c9 = PhoneCallDiscardReason;
phoneCallDiscardReasonMigrateConferenceCall#9fbbf1f7 slug:string = PhoneCallDiscardReason;

dataJSON#7d748d04 data:string = DataJSON;

labeledPrice#cb296bf8 label:string amount:long = LabeledPrice;

invoice#49ee584 flags:# test:flags.0?true name_requested:flags.1?true phone_requested:flags.2?true email_requested:flags.3?true shipping_address_requested:flags.4?true flexible:flags.5?true phone_to_provider:flags.6?true email_to_provider:flags.7?true recurring:flags.9?true currency:string prices:Vector<LabeledPrice> max_tip_amount:flags.8?long suggested_tip_amounts:flags.8?Vector<long> terms_url:flags.10?string subscription_period:flags.11?int = Invoice;

paymentCharge#ea02c27e id:string provider_charge_id:string = PaymentCharge;

postAddress#1e8caaeb street_line1:string street_line2:string city:string state:string country_iso2:string post_code:string = PostAddress;

paymentRequestedInfo#909c3f94 flags:# name:flags.0?string phone:flags.1?string email:flags.2?string shipping_address:flags.3?PostAddress = PaymentRequestedInfo;

paymentSavedCredentialsCard#cdc27a1f id:string title:string = PaymentSavedCredentials;

webDocument#1c570ed1 url:string access_hash:long size:int mime_type:string attributes:Vector<DocumentAttribute> = WebDocument;
webDocumentNoProxy#f9c8bcc6 url:string size:int mime_type:string attributes:Vector<DocumentAttribute> = WebDocument;

inputWebDocument#9bed434d url:string size:int mime_type:string attributes:Vector<DocumentAttribute> = InputWebDocument;

inputWebFileLocation#c239d686 url:string access_hash:long = InputWebFileLocation;
inputWebFileGeoPointLocation#9f2221c9 geo_point:InputGeoPoint access_hash:long w:int h:int zoom:int scale:int = InputWebFileLocation;
inputWebFileAudioAlbumThumbLocation#f46fe924 flags:# small:flags.2?true document:flags.0?InputDocument title:flags.1?string performer:flags.1?string = InputWebFileLocation;

upload.webFile#21e753bc size:int mime_type:string file_type:storage.FileType mtime:int bytes:bytes = upload.WebFile;

payments.paymentForm#a0058751 flags:# can_save_credentials:flags.2?true password_missing:flags.3?true form_id:long bot_id:long title:string description:string photo:flags.5?WebDocument invoice:Invoice provider_id:long url:string native_provider:flags.4?string native_params:flags.4?DataJSON additional_methods:flags.6?Vector<PaymentFormMethod> saved_info:flags.0?PaymentRequestedInfo saved_credentials:flags.1?Vector<PaymentSavedCredentials> users:Vector<User> = payments.PaymentForm;
payments.paymentFormStars#7bf6b15c flags:# form_id:long bot_id:long title:string description:string photo:flags.5?WebDocument invoice:Invoice users:Vector<User> = payments.PaymentForm;
payments.paymentFormStarGift#b425cfe1 form_id:long invoice:Invoice = payments.PaymentForm;

payments.validatedRequestedInfo#d1451883 flags:# id:flags.0?string shipping_options:flags.1?Vector<ShippingOption> = payments.ValidatedRequestedInfo;

payments.paymentResult#4e5f810d updates:Updates = payments.PaymentResult;
payments.paymentVerificationNeeded#d8411139 url:string = payments.PaymentResult;

payments.paymentReceipt#70c4fe03 flags:# date:int bot_id:long provider_id:long title:string description:string photo:flags.2?WebDocument invoice:Invoice info:flags.0?PaymentRequestedInfo shipping:flags.1?ShippingOption tip_amount:flags.3?long currency:string total_amount:long credentials_title:string users:Vector<User> = payments.PaymentReceipt;
payments.paymentReceiptStars#dabbf83a flags:# date:int bot_id:long title:string description:string photo:flags.2?WebDocument invoice:Invoice currency:string total_amount:long transaction_id:string users:Vector<User> = payments.PaymentReceipt;

payments.savedInfo#fb8fe43c flags:# has_saved_credentials:flags.1?true saved_info:flags.0?PaymentRequestedInfo = payments.SavedInfo;

inputPaymentCredentialsSaved#c10eb2cf id:string tmp_password:bytes = InputPaymentCredentials;
inputPaymentCredentials#3417d728 flags:# save:flags.0?true data:DataJSON = InputPaymentCredentials;
inputPaymentCredentialsApplePay#aa1c39f payment_data:DataJSON = InputPaymentCredentials;
inputPaymentCredentialsGooglePay#8ac32801 payment_token:DataJSON = InputPaymentCredentials;

account.tmpPassword#db64fd34 tmp_password:bytes valid_until:int = account.TmpPassword;

shippingOption#b6213cdf id:string title:string prices:Vector<LabeledPrice> = ShippingOption;

inputStickerSetItem#32da9e9c flags:# document:InputDocument emoji:string mask_coords:flags.0?MaskCoords keywords:flags.1?string = InputStickerSetItem;

inputPhoneCall#1e36fded id:long access_hash:long = InputPhoneCall;

phoneCallEmpty#5366c915 id:long = PhoneCall;
phoneCallWaiting#c5226f17 flags:# video:flags.6?true id:long access_hash:long date:int admin_id:long participant_id:long protocol:PhoneCallProtocol receive_date:flags.0?int = PhoneCall;
phoneCallRequested#14b0ed0c flags:# video:flags.6?true id:long access_hash:long date:int admin_id:long participant_id:long g_a_hash:bytes protocol:PhoneCallProtocol = PhoneCall;
phoneCallAccepted#3660c311 flags:# video:flags.6?true id:long access_hash:long date:int admin_id:long participant_id:long g_b:bytes protocol:PhoneCallProtocol = PhoneCall;
phoneCall#30535af5 flags:# p2p_allowed:flags.5?true video:flags.6?true conference_supported:flags.8?true id:long access_hash:long date:int admin_id:long participant_id:long g_a_or_b:bytes key_fingerprint:long protocol:PhoneCallProtocol connections:Vector<PhoneConnection> start_date:int custom_parameters:flags.7?DataJSON = PhoneCall;
phoneCallDiscarded#50ca4de1 flags:# need_rating:flags.2?true need_debug:flags.3?true video:flags.6?true id:long reason:flags.0?PhoneCallDiscardReason duration:flags.1?int = PhoneCall;

phoneConnection#9cc123c7 flags:# tcp:flags.0?true id:long ip:string ipv6:string port:int peer_tag:bytes = PhoneConnection;
phoneConnectionWebrtc#635fe375 flags:# turn:flags.0?true stun:flags.1?true id:long ip:string ipv6:string port:int username:string password:string = PhoneConnection;

phoneCallProtocol#fc878fc8 flags:# udp_p2p:flags.0?true udp_reflector:flags.1?true min_layer:int max_layer:int library_versions:Vector<string> = PhoneCallProtocol;

phone.phoneCall#ec82e140 phone_call:PhoneCall users:Vector<User> = phone.PhoneCall;

upload.cdnFileReuploadNeeded#eea8e46e request_token:bytes = upload.CdnFile;
upload.cdnFile#a99fca4f bytes:bytes = upload.CdnFile;

cdnPublicKey#c982eaba dc_id:int public_key:string = CdnPublicKey;

cdnConfig#5725e40a public_keys:Vector<CdnPublicKey> = CdnConfig;

langPackString#cad181f6 key:string value:string = LangPackString;
langPackStringPluralized#6c47ac9f flags:# key:string zero_value:flags.0?string one_value:flags.1?string two_value:flags.2?string few_value:flags.3?string many_value:flags.4?string other_value:string = LangPackString;
langPackStringDeleted#2979eeb2 key:string = LangPackString;

langPackDifference#f385c1f6 lang_code:string from_version:int version:int strings:Vector<LangPackString> = LangPackDifference;

langPackLanguage#eeca5ce3 flags:# official:flags.0?true rtl:flags.2?true beta:flags.3?true name:string native_name:string lang_code:string base_lang_code:flags.1?string plural_code:string strings_count:int translated_count:int translations_url:string = LangPackLanguage;

channelAdminLogEventActionChangeTitle#e6dfb825 prev_value:string new_value:string = ChannelAdminLogEventAction;
channelAdminLogEventActionChangeAbout#55188a2e prev_value:string new_value:string = ChannelAdminLogEventAction;
channelAdminLogEventActionChangeUsername#6a4afc38 prev_value:string new_value:string = ChannelAdminLogEventAction;
channelAdminLogEventActionChangePhoto#434bd2af prev_photo:Photo new_photo:Photo = ChannelAdminLogEventAction;
channelAdminLogEventActionToggleInvites#1b7907ae new_value:Bool = ChannelAdminLogEventAction;
channelAdminLogEventActionToggleSignatures#26ae0971 new_value:Bool = ChannelAdminLogEventAction;
channelAdminLogEventActionUpdatePinned#e9e82c18 message:Message = ChannelAdminLogEventAction;
channelAdminLogEventActionEditMessage#709b2405 prev_message:Message new_message:Message = ChannelAdminLogEventAction;
channelAdminLogEventActionDeleteMessage#42e047bb message:Message = ChannelAdminLogEventAction;
channelAdminLogEventActionParticipantJoin#183040d3 = ChannelAdminLogEventAction;
channelAdminLogEventActionParticipantLeave#f89777f2 = ChannelAdminLogEventAction;
channelAdminLogEventActionParticipantInvite#e31c34d8 participant:ChannelParticipant = ChannelAdminLogEventAction;
channelAdminLogEventActionParticipantToggleBan#e6d83d7e prev_participant:ChannelParticipant new_participant:ChannelParticipant = ChannelAdminLogEventAction;
channelAdminLogEventActionParticipantToggleAdmin#d5676710 prev_participant:ChannelParticipant new_participant:ChannelParticipant = ChannelAdminLogEventAction;
channelAdminLogEventActionChangeStickerSet#b1c3caa7 prev_stickerset:InputStickerSet new_stickerset:InputStickerSet = ChannelAdminLogEventAction;
channelAdminLogEventActionTogglePreHistoryHidden#5f5c95f1 new_value:Bool = ChannelAdminLogEventAction;
channelAdminLogEventActionDefaultBannedRights#2df5fc0a prev_banned_rights:ChatBannedRights new_banned_rights:ChatBannedRights = ChannelAdminLogEventAction;
channelAdminLogEventActionStopPoll#8f079643 message:Message = ChannelAdminLogEventAction;
channelAdminLogEventActionChangeLinkedChat#50c7ac8 prev_value:long new_value:long = ChannelAdminLogEventAction;
channelAdminLogEventActionChangeLocation#e6b76ae prev_value:ChannelLocation new_value:ChannelLocation = ChannelAdminLogEventAction;
channelAdminLogEventActionToggleSlowMode#53909779 prev_value:int new_value:int = ChannelAdminLogEventAction;
channelAdminLogEventActionStartGroupCall#23209745 call:InputGroupCall = ChannelAdminLogEventAction;
channelAdminLogEventActionDiscardGroupCall#db9f9140 call:InputGroupCall = ChannelAdminLogEventAction;
channelAdminLogEventActionParticipantMute#f92424d2 participant:GroupCallParticipant = ChannelAdminLogEventAction;
channelAdminLogEventActionParticipantUnmute#e64429c0 participant:GroupCallParticipant = ChannelAdminLogEventAction;
channelAdminLogEventActionToggleGroupCallSetting#56d6a247 join_muted:Bool = ChannelAdminLogEventAction;
channelAdminLogEventActionParticipantJoinByInvite#fe9fc158 flags:# via_chatlist:flags.0?true invite:ExportedChatInvite = ChannelAdminLogEventAction;
channelAdminLogEventActionExportedInviteDelete#5a50fca4 invite:ExportedChatInvite = ChannelAdminLogEventAction;
channelAdminLogEventActionExportedInviteRevoke#410a134e invite:ExportedChatInvite = ChannelAdminLogEventAction;
channelAdminLogEventActionExportedInviteEdit#e90ebb59 prev_invite:ExportedChatInvite new_invite:ExportedChatInvite = ChannelAdminLogEventAction;
channelAdminLogEventActionParticipantVolume#3e7f6847 participant:GroupCallParticipant = ChannelAdminLogEventAction;
channelAdminLogEventActionChangeHistoryTTL#6e941a38 prev_value:int new_value:int = ChannelAdminLogEventAction;
channelAdminLogEventActionParticipantJoinByRequest#afb6144a invite:ExportedChatInvite approved_by:long = ChannelAdminLogEventAction;
channelAdminLogEventActionToggleNoForwards#cb2ac766 new_value:Bool = ChannelAdminLogEventAction;
channelAdminLogEventActionSendMessage#278f2868 message:Message = ChannelAdminLogEventAction;
channelAdminLogEventActionChangeAvailableReactions#be4e0ef8 prev_value:ChatReactions new_value:ChatReactions = ChannelAdminLogEventAction;
channelAdminLogEventActionChangeUsernames#f04fb3a9 prev_value:Vector<string> new_value:Vector<string> = ChannelAdminLogEventAction;
channelAdminLogEventActionToggleForum#2cc6383 new_value:Bool = ChannelAdminLogEventAction;
channelAdminLogEventActionCreateTopic#58707d28 topic:ForumTopic = ChannelAdminLogEventAction;
channelAdminLogEventActionEditTopic#f06fe208 prev_topic:ForumTopic new_topic:ForumTopic = ChannelAdminLogEventAction;
channelAdminLogEventActionDeleteTopic#ae168909 topic:ForumTopic = ChannelAdminLogEventAction;
channelAdminLogEventActionPinTopic#5d8d353b flags:# prev_topic:flags.0?ForumTopic new_topic:flags.1?ForumTopic = ChannelAdminLogEventAction;
channelAdminLogEventActionToggleAntiSpam#64f36dfc new_value:Bool = ChannelAdminLogEventAction;
channelAdminLogEventActionChangePeerColor#5796e780 prev_value:PeerColor new_value:PeerColor = ChannelAdminLogEventAction;
channelAdminLogEventActionChangeProfilePeerColor#5e477b25 prev_value:PeerColor new_value:PeerColor = ChannelAdminLogEventAction;
channelAdminLogEventActionChangeWallpaper#31bb5d52 prev_value:WallPaper new_value:WallPaper = ChannelAdminLogEventAction;
channelAdminLogEventActionChangeEmojiStatus#3ea9feb1 prev_value:EmojiStatus new_value:EmojiStatus = ChannelAdminLogEventAction;
channelAdminLogEventActionChangeEmojiStickerSet#46d840ab prev_stickerset:InputStickerSet new_stickerset:InputStickerSet = ChannelAdminLogEventAction;
channelAdminLogEventActionToggleSignatureProfiles#60a79c79 new_value:Bool = ChannelAdminLogEventAction;
channelAdminLogEventActionParticipantSubExtend#64642db3 prev_participant:ChannelParticipant new_participant:ChannelParticipant = ChannelAdminLogEventAction;
channelAdminLogEventActionToggleAutotranslation#c517f77e new_value:Bool = ChannelAdminLogEventAction;
channelAdminLogEventActionParticipantEditRank#5806b4ec user_id:long prev_rank:string new_rank:string = ChannelAdminLogEventAction;

channelAdminLogEvent#1fad68cd id:long date:int user_id:long action:ChannelAdminLogEventAction = ChannelAdminLogEvent;

channels.adminLogResults#ed8af74d events:Vector<ChannelAdminLogEvent> chats:Vector<Chat> users:Vector<User> = channels.AdminLogResults;

channelAdminLogEventsFilter#ea107ae4 flags:# join:flags.0?true leave:flags.1?true invite:flags.2?true ban:flags.3?true unban:flags.4?true kick:flags.5?true unkick:flags.6?true promote:flags.7?true demote:flags.8?true info:flags.9?true settings:flags.10?true pinned:flags.11?true edit:flags.12?true delete:flags.13?true group_call:flags.14?true invites:flags.15?true send:flags.16?true forums:flags.17?true sub_extend:flags.18?true edit_rank:flags.19?true = ChannelAdminLogEventsFilter;

popularContact#5ce14175 client_id:long importers:int = PopularContact;

messages.favedStickersNotModified#9e8fa6d3 = messages.FavedStickers;
messages.favedStickers#2cb51097 hash:long packs:Vector<StickerPack> stickers:Vector<Document> = messages.FavedStickers;

recentMeUrlUnknown#46e1d13d url:string = RecentMeUrl;
recentMeUrlUser#b92c09e2 url:string user_id:long = RecentMeUrl;
recentMeUrlChat#b2da71d2 url:string chat_id:long = RecentMeUrl;
recentMeUrlChatInvite#eb49081d url:string chat_invite:ChatInvite = RecentMeUrl;
recentMeUrlStickerSet#bc0a57dc url:string set:StickerSetCovered = RecentMeUrl;

help.recentMeUrls#e0310d7 urls:Vector<RecentMeUrl> chats:Vector<Chat> users:Vector<User> = help.RecentMeUrls;

inputSingleMedia#1cc6e91f flags:# media:InputMedia random_id:long message:string entities:flags.0?Vector<MessageEntity> = InputSingleMedia;

webAuthorization#a6f8f452 hash:long bot_id:long domain:string browser:string platform:string date_created:int date_active:int ip:string region:string = WebAuthorization;

account.webAuthorizations#ed56c9fc authorizations:Vector<WebAuthorization> users:Vector<User> = account.WebAuthorizations;

inputMessageID#a676a322 id:int = InputMessage;
inputMessageReplyTo#bad88395 id:int = InputMessage;
inputMessagePinned#86872538 = InputMessage;
inputMessageCallbackQuery#acfa1a7e id:int query_id:long = InputMessage;

inputDialogPeer#fcaafeb7 peer:InputPeer = InputDialogPeer;
inputDialogPeerFolder#64600527 folder_id:int = InputDialogPeer;
inputDialogPeerCommunity#69ef72c4 community:InputChannel = InputDialogPeer;

dialogPeer#e56dbf05 peer:Peer = DialogPeer;
dialogPeerFolder#514519e2 folder_id:int = DialogPeer;
dialogPeerCommunity#2f65c8e4 community_id:long = DialogPeer;

messages.foundStickerSetsNotModified#d54b65d = messages.FoundStickerSets;
messages.foundStickerSets#8af09dd2 hash:long sets:Vector<StickerSetCovered> = messages.FoundStickerSets;

fileHash#f39b035c offset:long limit:int hash:bytes = FileHash;

inputClientProxy#75588b3f address:string port:int = InputClientProxy;

help.termsOfServiceUpdateEmpty#e3309f7f expires:int = help.TermsOfServiceUpdate;
help.termsOfServiceUpdate#28ecf961 expires:int terms_of_service:help.TermsOfService = help.TermsOfServiceUpdate;

inputSecureFileUploaded#3334b0f0 id:long parts:int md5_checksum:string file_hash:bytes secret:bytes = InputSecureFile;
inputSecureFile#5367e5be id:long access_hash:long = InputSecureFile;

secureFileEmpty#64199744 = SecureFile;
secureFile#7d09c27e id:long access_hash:long size:long dc_id:int date:int file_hash:bytes secret:bytes = SecureFile;

secureData#8aeabec3 data:bytes data_hash:bytes secret:bytes = SecureData;

securePlainPhone#7d6099dd phone:string = SecurePlainData;
securePlainEmail#21ec5a5f email:string = SecurePlainData;

secureValueTypePersonalDetails#9d2a81e3 = SecureValueType;
secureValueTypePassport#3dac6a00 = SecureValueType;
secureValueTypeDriverLicense#6e425c4 = SecureValueType;
secureValueTypeIdentityCard#a0d0744b = SecureValueType;
secureValueTypeInternalPassport#99a48f23 = SecureValueType;
secureValueTypeAddress#cbe31e26 = SecureValueType;
secureValueTypeUtilityBill#fc36954e = SecureValueType;
secureValueTypeBankStatement#89137c0d = SecureValueType;
secureValueTypeRentalAgreement#8b883488 = SecureValueType;
secureValueTypePassportRegistration#99e3806a = SecureValueType;
secureValueTypeTemporaryRegistration#ea02ec33 = SecureValueType;
secureValueTypePhone#b320aadb = SecureValueType;
secureValueTypeEmail#8e3ca7ee = SecureValueType;

secureValue#187fa0ca flags:# type:SecureValueType data:flags.0?SecureData front_side:flags.1?SecureFile reverse_side:flags.2?SecureFile selfie:flags.3?SecureFile translation:flags.6?Vector<SecureFile> files:flags.4?Vector<SecureFile> plain_data:flags.5?SecurePlainData hash:bytes = SecureValue;

inputSecureValue#db21d0a7 flags:# type:SecureValueType data:flags.0?SecureData front_side:flags.1?InputSecureFile reverse_side:flags.2?InputSecureFile selfie:flags.3?InputSecureFile translation:flags.6?Vector<InputSecureFile> files:flags.4?Vector<InputSecureFile> plain_data:flags.5?SecurePlainData = InputSecureValue;

secureValueHash#ed1ecdb0 type:SecureValueType hash:bytes = SecureValueHash;

secureValueErrorData#e8a40bd9 type:SecureValueType data_hash:bytes field:string text:string = SecureValueError;
secureValueErrorFrontSide#be3dfa type:SecureValueType file_hash:bytes text:string = SecureValueError;
secureValueErrorReverseSide#868a2aa5 type:SecureValueType file_hash:bytes text:string = SecureValueError;
secureValueErrorSelfie#e537ced6 type:SecureValueType file_hash:bytes text:string = SecureValueError;
secureValueErrorFile#7a700873 type:SecureValueType file_hash:bytes text:string = SecureValueError;
secureValueErrorFiles#666220e9 type:SecureValueType file_hash:Vector<bytes> text:string = SecureValueError;
secureValueError#869d758f type:SecureValueType hash:bytes text:string = SecureValueError;
secureValueErrorTranslationFile#a1144770 type:SecureValueType file_hash:bytes text:string = SecureValueError;
secureValueErrorTranslationFiles#34636dd8 type:SecureValueType file_hash:Vector<bytes> text:string = SecureValueError;

secureCredentialsEncrypted#33f0ea47 data:bytes hash:bytes secret:bytes = SecureCredentialsEncrypted;

account.authorizationForm#ad2e1cd8 flags:# required_types:Vector<SecureRequiredType> values:Vector<SecureValue> errors:Vector<SecureValueError> users:Vector<User> privacy_policy_url:flags.0?string = account.AuthorizationForm;

account.sentEmailCode#811f854f email_pattern:string length:int = account.SentEmailCode;

help.deepLinkInfoEmpty#66afa166 = help.DeepLinkInfo;
help.deepLinkInfo#6a4ee832 flags:# update_app:flags.0?true message:string entities:flags.1?Vector<MessageEntity> = help.DeepLinkInfo;

savedPhoneContact#1142bd56 phone:string first_name:string last_name:string date:int = SavedContact;

account.takeout#4dba4501 id:long = account.Takeout;

passwordKdfAlgoUnknown#d45ab096 = PasswordKdfAlgo;
passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow#3a912d4a salt1:bytes salt2:bytes g:int p:bytes = PasswordKdfAlgo;

securePasswordKdfAlgoUnknown#4a8537 = SecurePasswordKdfAlgo;
securePasswordKdfAlgoPBKDF2HMACSHA512iter100000#bbf2dda0 salt:bytes = SecurePasswordKdfAlgo;
securePasswordKdfAlgoSHA512#86471d92 salt:bytes = SecurePasswordKdfAlgo;

secureSecretSettings#1527bcac secure_algo:SecurePasswordKdfAlgo secure_secret:bytes secure_secret_id:long = SecureSecretSettings;

inputCheckPasswordEmpty#9880f658 = InputCheckPasswordSRP;
inputCheckPasswordSRP#d27ff082 srp_id:long A:bytes M1:bytes = InputCheckPasswordSRP;

secureRequiredType#829d99da flags:# native_names:flags.0?true selfie_required:flags.1?true translation_required:flags.2?true type:SecureValueType = SecureRequiredType;
secureRequiredTypeOneOf#27477b4 types:Vector<SecureRequiredType> = SecureRequiredType;

help.passportConfigNotModified#bfb9f457 = help.PassportConfig;
help.passportConfig#a098d6af hash:int countries_langs:DataJSON = help.PassportConfig;

inputAppEvent#1d1b1245 time:double type:string peer:long data:JSONValue = InputAppEvent;

jsonObjectValue#c0de1bd9 key:string value:JSONValue = JSONObjectValue;

jsonNull#3f6d7b68 = JSONValue;
jsonBool#c7345e6a value:Bool = JSONValue;
jsonNumber#2be0dfa4 value:double = JSONValue;
jsonString#b71e767a value:string = JSONValue;
jsonArray#f7444763 value:Vector<JSONValue> = JSONValue;
jsonObject#99c1d49d value:Vector<JSONObjectValue> = JSONValue;

pageTableCell#34566b6a flags:# header:flags.0?true align_center:flags.3?true align_right:flags.4?true valign_middle:flags.5?true valign_bottom:flags.6?true text:flags.7?RichText colspan:flags.1?int rowspan:flags.2?int = PageTableCell;

pageTableRow#e0c0c5e5 cells:Vector<PageTableCell> = PageTableRow;

pageCaption#6f747657 text:RichText credit:RichText = PageCaption;

pageListItemText#2f58683c flags:# checkbox:flags.0?true checked:flags.1?true text:RichText = PageListItem;
pageListItemBlocks#63ca67aa flags:# checkbox:flags.0?true checked:flags.1?true blocks:Vector<PageBlock> = PageListItem;

pageListOrderedItemText#15031189 flags:# checkbox:flags.0?true checked:flags.1?true num:flags.2?string text:RichText value:flags.3?int type:flags.4?string = PageListOrderedItem;
pageListOrderedItemBlocks#8ff2d5f0 flags:# checkbox:flags.0?true checked:flags.1?true num:flags.2?string blocks:Vector<PageBlock> value:flags.3?int type:flags.4?string = PageListOrderedItem;

pageRelatedArticle#b390dc08 flags:# url:string webpage_id:long title:flags.0?string description:flags.1?string photo_id:flags.2?long author:flags.3?string published_date:flags.4?int = PageRelatedArticle;

page#98657f0d flags:# part:flags.0?true rtl:flags.1?true v2:flags.2?true url:string blocks:Vector<PageBlock> photos:Vector<Photo> documents:Vector<Document> views:flags.3?int = Page;

help.supportName#8c05f1c9 name:string = help.SupportName;

help.userInfoEmpty#f3ae2eed = help.UserInfo;
help.userInfo#1eb3758 message:string entities:Vector<MessageEntity> author:string date:int = help.UserInfo;

pollAnswer#4b7d786a flags:# text:TextWithEntities option:bytes media:flags.0?MessageMedia added_by:flags.1?Peer date:flags.1?int = PollAnswer;
inputPollAnswer#199fed96 flags:# text:TextWithEntities media:flags.0?InputMedia = PollAnswer;

poll#966e2dbf id:long flags:# closed:flags.0?true public_voters:flags.1?true multiple_choice:flags.2?true quiz:flags.3?true open_answers:flags.6?true revoting_disabled:flags.7?true shuffle_answers:flags.8?true hide_results_until_close:flags.9?true creator:flags.10?true subscribers_only:flags.11?true question:TextWithEntities answers:Vector<PollAnswer> close_period:flags.4?int close_date:flags.5?int countries_iso2:flags.12?Vector<string> hash:long = Poll;

pollAnswerVoters#3645230a flags:# chosen:flags.0?true correct:flags.1?true option:bytes voters:flags.2?int recent_voters:flags.2?Vector<Peer> = PollAnswerVoters;

pollResults#ba7bb15e flags:# min:flags.0?true has_unread_votes:flags.6?true can_view_stats:flags.7?true results:flags.1?Vector<PollAnswerVoters> total_voters:flags.2?int recent_voters:flags.3?Vector<Peer> solution:flags.4?string solution_entities:flags.4?Vector<MessageEntity> solution_media:flags.5?MessageMedia = PollResults;

chatOnlines#f041e250 onlines:int = ChatOnlines;

statsURL#47a971e0 url:string = StatsURL;

chatAdminRights#5fb224d5 flags:# change_info:flags.0?true post_messages:flags.1?true edit_messages:flags.2?true delete_messages:flags.3?true ban_users:flags.4?true invite_users:flags.5?true pin_messages:flags.7?true add_admins:flags.9?true anonymous:flags.10?true manage_call:flags.11?true other:flags.12?true manage_topics:flags.13?true post_stories:flags.14?true edit_stories:flags.15?true delete_stories:flags.16?true manage_direct_messages:flags.17?true manage_ranks:flags.18?true manage_linked_peers:flags.19?true manage_welcome_messages:flags.20?true = ChatAdminRights;

chatBannedRights#9f120418 flags:# view_messages:flags.0?true send_messages:flags.1?true send_media:flags.2?true send_stickers:flags.3?true send_gifs:flags.4?true send_games:flags.5?true send_inline:flags.6?true embed_links:flags.7?true send_polls:flags.8?true change_info:flags.10?true invite_users:flags.15?true pin_messages:flags.17?true manage_topics:flags.18?true send_photos:flags.19?true send_videos:flags.20?true send_roundvideos:flags.21?true send_audios:flags.22?true send_voices:flags.23?true send_docs:flags.24?true send_plain:flags.25?true edit_rank:flags.26?true send_reactions:flags.27?true manage_linked_peers:flags.28?true until_date:int = ChatBannedRights;

inputWallPaper#e630b979 id:long access_hash:long = InputWallPaper;
inputWallPaperSlug#72091c80 slug:string = InputWallPaper;
inputWallPaperNoFile#967a462e id:long = InputWallPaper;

account.wallPapersNotModified#1c199183 = account.WallPapers;
account.wallPapers#cdc3858c hash:long wallpapers:Vector<WallPaper> = account.WallPapers;

codeSettings#ad253d78 flags:# allow_flashcall:flags.0?true current_number:flags.1?true allow_app_hash:flags.4?true allow_missed_call:flags.5?true allow_firebase:flags.7?true unknown_number:flags.9?true logout_tokens:flags.6?Vector<bytes> token:flags.8?string app_sandbox:flags.8?Bool = CodeSettings;

wallPaperSettings#372efcd0 flags:# blur:flags.1?true motion:flags.2?true background_color:flags.0?int second_background_color:flags.4?int third_background_color:flags.5?int fourth_background_color:flags.6?int intensity:flags.3?int rotation:flags.4?int emoticon:flags.7?string = WallPaperSettings;

autoDownloadSettings#baa57628 flags:# disabled:flags.0?true video_preload_large:flags.1?true audio_preload_next:flags.2?true phonecalls_less_data:flags.3?true stories_preload:flags.4?true photo_size_max:int video_size_max:long file_size_max:long video_upload_maxbitrate:int small_queue_active_operations_max:int large_queue_active_operations_max:int = AutoDownloadSettings;

account.autoDownloadSettings#63cacf26 low:AutoDownloadSettings medium:AutoDownloadSettings high:AutoDownloadSettings = account.AutoDownloadSettings;

emojiKeyword#d5b3b9f9 keyword:string emoticons:Vector<string> = EmojiKeyword;
emojiKeywordDeleted#236df622 keyword:string emoticons:Vector<string> = EmojiKeyword;

emojiKeywordsDifference#5cc761bd lang_code:string from_version:int version:int keywords:Vector<EmojiKeyword> = EmojiKeywordsDifference;

emojiURL#a575739d url:string = EmojiURL;

emojiLanguage#b3fb5361 lang_code:string = EmojiLanguage;

folder#ff544e65 flags:# autofill_new_broadcasts:flags.0?true autofill_public_groups:flags.1?true autofill_new_correspondents:flags.2?true id:int title:string photo:flags.3?ChatPhoto = Folder;

inputFolderPeer#fbd2c296 peer:InputPeer folder_id:int = InputFolderPeer;

folderPeer#e9baa668 peer:Peer folder_id:int = FolderPeer;

messages.searchCounter#e844ebff flags:# inexact:flags.1?true filter:MessagesFilter count:int = messages.SearchCounter;

urlAuthResultRequest#bdca6167 flags:# request_write_access:flags.0?true request_phone_number:flags.1?true match_codes_first:flags.5?true is_app:flags.6?true request_wallet:flags.8?true bot:User domain:string browser:flags.2?string platform:flags.2?string ip:flags.2?string region:flags.2?string match_codes:flags.3?Vector<string> user_id_hint:flags.4?long verified_app_name:flags.7?string tc_session_id:flags.9?long tc_client_id:flags.10?string = UrlAuthResult;
urlAuthResultAccepted#623a8fa0 flags:# url:flags.0?string = UrlAuthResult;
urlAuthResultDefault#a9d6db1f = UrlAuthResult;

channelLocationEmpty#bfb5ad8b = ChannelLocation;
channelLocation#209b82db geo_point:GeoPoint address:string = ChannelLocation;

peerLocated#ca461b5d peer:Peer expires:int distance:int = PeerLocated;
peerSelfLocated#f8ec284b expires:int = PeerLocated;

restrictionReason#d072acb4 platform:string reason:string text:string = RestrictionReason;

inputTheme#3c5693e9 id:long access_hash:long = InputTheme;
inputThemeSlug#f5890df1 slug:string = InputTheme;

theme#a00e67d6 flags:# creator:flags.0?true default:flags.1?true for_chat:flags.5?true id:long access_hash:long slug:string title:string document:flags.2?Document settings:flags.3?Vector<ThemeSettings> emoticon:flags.6?string installs_count:flags.4?int = Theme;

account.themesNotModified#f41eb622 = account.Themes;
account.themes#9a3d8c6d hash:long themes:Vector<Theme> = account.Themes;

auth.loginToken#629f1980 expires:int token:bytes = auth.LoginToken;
auth.loginTokenMigrateTo#68e9916 dc_id:int token:bytes = auth.LoginToken;
auth.loginTokenSuccess#390d5c5e authorization:auth.Authorization = auth.LoginToken;

account.contentSettings#57e28221 flags:# sensitive_enabled:flags.0?true sensitive_can_change:flags.1?true = account.ContentSettings;

messages.inactiveChats#a927fec5 dates:Vector<int> chats:Vector<Chat> users:Vector<User> = messages.InactiveChats;

baseThemeClassic#c3a12462 = BaseTheme;
baseThemeDay#fbd81688 = BaseTheme;
baseThemeNight#b7b31ea8 = BaseTheme;
baseThemeTinted#6d5f77ee = BaseTheme;
baseThemeArctic#5b11125a = BaseTheme;

inputThemeSettings#8fde504f flags:# message_colors_animated:flags.2?true base_theme:BaseTheme accent_color:int outbox_accent_color:flags.3?int message_colors:flags.0?Vector<int> wallpaper:flags.1?InputWallPaper wallpaper_settings:flags.1?WallPaperSettings = InputThemeSettings;

themeSettings#fa58b6d4 flags:# message_colors_animated:flags.2?true base_theme:BaseTheme accent_color:int outbox_accent_color:flags.3?int message_colors:flags.0?Vector<int> wallpaper:flags.1?WallPaper = ThemeSettings;

webPageAttributeTheme#54b56617 flags:# documents:flags.0?Vector<Document> settings:flags.1?ThemeSettings = WebPageAttribute;
webPageAttributeStory#2e94c3e7 flags:# peer:Peer id:int story:flags.0?StoryItem = WebPageAttribute;
webPageAttributeStickerSet#50cc03d3 flags:# emojis:flags.0?true text_color:flags.1?true stickers:Vector<Document> = WebPageAttribute;
webPageAttributeUniqueStarGift#cf6f6db8 gift:StarGift = WebPageAttribute;
webPageAttributeStarGiftCollection#31cad303 icons:Vector<Document> = WebPageAttribute;
webPageAttributeStarGiftAuction#1c641c2 gift:StarGift end_date:int = WebPageAttribute;
webPageAttributeAiComposeTone#7781fe18 emoji_id:long = WebPageAttribute;

messages.votesList#4899484e flags:# count:int votes:Vector<MessagePeerVote> chats:Vector<Chat> users:Vector<User> next_offset:flags.0?string = messages.VotesList;

bankCardOpenUrl#f568028a url:string name:string = BankCardOpenUrl;

payments.bankCardData#3e24e573 title:string open_urls:Vector<BankCardOpenUrl> = payments.BankCardData;

dialogFilter#aa472651 flags:# contacts:flags.0?true non_contacts:flags.1?true groups:flags.2?true broadcasts:flags.3?true bots:flags.4?true exclude_muted:flags.11?true exclude_read:flags.12?true exclude_archived:flags.13?true title_noanimate:flags.28?true id:int title:TextWithEntities emoticon:flags.25?string color:flags.27?int pinned_peers:Vector<InputPeer> include_peers:Vector<InputPeer> exclude_peers:Vector<InputPeer> = DialogFilter;
dialogFilterDefault#363293ae = DialogFilter;
dialogFilterChatlist#96537bd7 flags:# has_my_invites:flags.26?true title_noanimate:flags.28?true id:int title:TextWithEntities emoticon:flags.25?string color:flags.27?int pinned_peers:Vector<InputPeer> include_peers:Vector<InputPeer> = DialogFilter;

dialogFilterSuggested#77744d4a filter:DialogFilter description:string = DialogFilterSuggested;

statsDateRangeDays#b637edaf min_date:int max_date:int = StatsDateRangeDays;

statsAbsValueAndPrev#cb43acde current:double previous:double = StatsAbsValueAndPrev;

statsPercentValue#cbce2fe0 part:double total:double = StatsPercentValue;

statsGraphAsync#4a27eb2d token:string = StatsGraph;
statsGraphError#bedc9822 error:string = StatsGraph;
statsGraph#8ea464b6 flags:# json:DataJSON zoom_token:flags.0?string = StatsGraph;

stats.broadcastStats#396ca5fc period:StatsDateRangeDays followers:StatsAbsValueAndPrev views_per_post:StatsAbsValueAndPrev shares_per_post:StatsAbsValueAndPrev reactions_per_post:StatsAbsValueAndPrev views_per_story:StatsAbsValueAndPrev shares_per_story:StatsAbsValueAndPrev reactions_per_story:StatsAbsValueAndPrev enabled_notifications:StatsPercentValue growth_graph:StatsGraph followers_graph:StatsGraph mute_graph:StatsGraph top_hours_graph:StatsGraph interactions_graph:StatsGraph iv_interactions_graph:StatsGraph views_by_source_graph:StatsGraph new_followers_by_source_graph:StatsGraph languages_graph:StatsGraph reactions_by_emotion_graph:StatsGraph story_interactions_graph:StatsGraph story_reactions_by_emotion_graph:StatsGraph recent_posts_interactions:Vector<PostInteractionCounters> = stats.BroadcastStats;

help.promoDataEmpty#98f6ac75 expires:int = help.PromoData;
help.promoData#8a4d87a flags:# proxy:flags.0?true expires:int peer:flags.3?Peer psa_type:flags.1?string psa_message:flags.2?string pending_suggestions:Vector<string> dismissed_suggestions:Vector<string> custom_pending_suggestion:flags.4?PendingSuggestion chats:Vector<Chat> users:Vector<User> = help.PromoData;

videoSize#de33b094 flags:# type:string w:int h:int size:int video_start_ts:flags.0?double = VideoSize;
videoSizeEmojiMarkup#f85c413c emoji_id:long background_colors:Vector<int> = VideoSize;
videoSizeStickerMarkup#da082fe stickerset:InputStickerSet sticker_id:long background_colors:Vector<int> = VideoSize;

statsGroupTopPoster#9d04af9b user_id:long messages:int avg_chars:int = StatsGroupTopPoster;

statsGroupTopAdmin#d7584c87 user_id:long deleted:int kicked:int banned:int = StatsGroupTopAdmin;

statsGroupTopInviter#535f779d user_id:long invitations:int = StatsGroupTopInviter;

stats.megagroupStats#ef7ff916 period:StatsDateRangeDays members:StatsAbsValueAndPrev messages:StatsAbsValueAndPrev viewers:StatsAbsValueAndPrev posters:StatsAbsValueAndPrev growth_graph:StatsGraph members_graph:StatsGraph new_members_by_source_graph:StatsGraph languages_graph:StatsGraph messages_graph:StatsGraph actions_graph:StatsGraph top_hours_graph:StatsGraph weekdays_graph:StatsGraph top_posters:Vector<StatsGroupTopPoster> top_admins:Vector<StatsGroupTopAdmin> top_inviters:Vector<StatsGroupTopInviter> users:Vector<User> = stats.MegagroupStats;

globalPrivacySettings#fe41b34f flags:# archive_and_mute_new_noncontact_peers:flags.0?true keep_archived_unmuted:flags.1?true keep_archived_folders:flags.2?true hide_read_marks:flags.3?true new_noncontact_peers_require_premium:flags.4?true display_gifts_button:flags.7?true noncontact_peers_paid_stars:flags.5?long disallowed_gifts:flags.6?DisallowedGiftsSettings = GlobalPrivacySettings;

help.countryCode#4203c5ef flags:# country_code:string prefixes:flags.0?Vector<string> patterns:flags.1?Vector<string> = help.CountryCode;

help.country#c3878e23 flags:# hidden:flags.0?true iso2:string default_name:string name:flags.1?string country_codes:Vector<help.CountryCode> = help.Country;

help.countriesListNotModified#93cc1f32 = help.CountriesList;
help.countriesList#87d0759e countries:Vector<help.Country> hash:int = help.CountriesList;

messageViews#455b853d flags:# views:flags.0?int forwards:flags.1?int replies:flags.2?MessageReplies = MessageViews;

messages.messageViews#b6c4f543 views:Vector<MessageViews> chats:Vector<Chat> users:Vector<User> = messages.MessageViews;

messages.discussionMessage#a6341782 flags:# messages:Vector<Message> max_id:flags.0?int read_inbox_max_id:flags.1?int read_outbox_max_id:flags.2?int unread_count:int chats:Vector<Chat> users:Vector<User> = messages.DiscussionMessage;

messageReplyHeader#ddb5ab75 flags:# reply_to_scheduled:flags.2?true forum_topic:flags.3?true quote:flags.9?true reply_to_ephemeral:flags.13?true reply_to_msg_id:flags.4?int reply_to_peer_id:flags.0?Peer reply_from:flags.5?MessageFwdHeader reply_media:flags.8?MessageMedia reply_to_top_id:flags.1?int quote_text:flags.6?string quote_entities:flags.7?Vector<MessageEntity> quote_offset:flags.10?int todo_item_id:flags.11?int poll_option:flags.12?bytes quote_rich:flags.14?Vector<RichText> = MessageReplyHeader;
messageReplyStoryHeader#e5af939 peer:Peer story_id:int = MessageReplyHeader;

messageReplies#83d60fc2 flags:# comments:flags.0?true replies:int replies_pts:int recent_repliers:flags.1?Vector<Peer> channel_id:flags.0?long max_id:flags.2?int read_max_id:flags.3?int = MessageReplies;

peerBlocked#e8fd8014 peer_id:Peer date:int = PeerBlocked;

stats.messageStats#7fe91c14 views_graph:StatsGraph reactions_by_emotion_graph:StatsGraph = stats.MessageStats;

groupCallDiscarded#7780bcb4 id:long access_hash:long duration:int = GroupCall;
groupCall#efb2b617 flags:# join_muted:flags.1?true can_change_join_muted:flags.2?true join_date_asc:flags.6?true schedule_start_subscribed:flags.8?true can_start_video:flags.9?true record_video_active:flags.11?true rtmp_stream:flags.12?true listeners_hidden:flags.13?true conference:flags.14?true creator:flags.15?true messages_enabled:flags.17?true can_change_messages_enabled:flags.18?true min:flags.19?true id:long access_hash:long participants_count:int title:flags.3?string stream_dc_id:flags.4?int record_start_date:flags.5?int schedule_date:flags.7?int unmuted_video_count:flags.10?int unmuted_video_limit:int version:int invite_link:flags.16?string send_paid_messages_stars:flags.20?long default_send_as:flags.21?Peer = GroupCall;

inputGroupCall#d8aa840f id:long access_hash:long = InputGroupCall;
inputGroupCallSlug#fe06823f slug:string = InputGroupCall;
inputGroupCallInviteMessage#8c10603f msg_id:int = InputGroupCall;

groupCallParticipant#2a3dc7ac flags:# muted:flags.0?true left:flags.1?true can_self_unmute:flags.2?true just_joined:flags.4?true versioned:flags.5?true min:flags.8?true muted_by_you:flags.9?true volume_by_admin:flags.10?true self:flags.12?true video_joined:flags.15?true peer:Peer date:int active_date:flags.3?int source:int volume:flags.7?int about:flags.11?string raise_hand_rating:flags.13?long video:flags.6?GroupCallParticipantVideo presentation:flags.14?GroupCallParticipantVideo paid_stars_total:flags.16?long = GroupCallParticipant;

phone.groupCall#9e727aad call:GroupCall participants:Vector<GroupCallParticipant> participants_next_offset:string chats:Vector<Chat> users:Vector<User> = phone.GroupCall;

phone.groupParticipants#f47751b6 count:int participants:Vector<GroupCallParticipant> next_offset:string chats:Vector<Chat> users:Vector<User> version:int = phone.GroupParticipants;

inlineQueryPeerTypeSameBotPM#3081ed9d = InlineQueryPeerType;
inlineQueryPeerTypePM#833c0fac = InlineQueryPeerType;
inlineQueryPeerTypeChat#d766c50a = InlineQueryPeerType;
inlineQueryPeerTypeMegagroup#5ec4be43 = InlineQueryPeerType;
inlineQueryPeerTypeBroadcast#6334ee9a = InlineQueryPeerType;
inlineQueryPeerTypeBotPM#e3b2d0c = InlineQueryPeerType;

messages.historyImport#1662af0b id:long = messages.HistoryImport;

messages.historyImportParsed#5e0fb7b9 flags:# pm:flags.0?true group:flags.1?true title:flags.2?string = messages.HistoryImportParsed;

messages.affectedFoundMessages#ef8d3e6c pts:int pts_count:int offset:int messages:Vector<int> = messages.AffectedFoundMessages;

chatInviteImporter#8c5adfd9 flags:# requested:flags.0?true via_chatlist:flags.3?true user_id:long date:int about:flags.2?string approved_by:flags.1?long = ChatInviteImporter;

messages.exportedChatInvites#bdc62dcc count:int invites:Vector<ExportedChatInvite> users:Vector<User> = messages.ExportedChatInvites;

messages.exportedChatInvite#1871be50 invite:ExportedChatInvite users:Vector<User> = messages.ExportedChatInvite;
messages.exportedChatInviteReplaced#222600ef invite:ExportedChatInvite new_invite:ExportedChatInvite users:Vector<User> = messages.ExportedChatInvite;

messages.chatInviteImporters#81b6b00a count:int importers:Vector<ChatInviteImporter> users:Vector<User> = messages.ChatInviteImporters;

chatAdminWithInvites#f2ecef23 admin_id:long invites_count:int revoked_invites_count:int = ChatAdminWithInvites;

messages.chatAdminsWithInvites#b69b72d7 admins:Vector<ChatAdminWithInvites> users:Vector<User> = messages.ChatAdminsWithInvites;

messages.checkedHistoryImportPeer#a24de717 confirm_text:string = messages.CheckedHistoryImportPeer;

phone.joinAsPeers#afe5623f peers:Vector<Peer> chats:Vector<Chat> users:Vector<User> = phone.JoinAsPeers;

phone.exportedGroupCallInvite#204bd158 link:string = phone.ExportedGroupCallInvite;

groupCallParticipantVideoSourceGroup#dcb118b7 semantics:string sources:Vector<int> = GroupCallParticipantVideoSourceGroup;

groupCallParticipantVideo#67753ac8 flags:# paused:flags.0?true endpoint:string source_groups:Vector<GroupCallParticipantVideoSourceGroup> audio_source:flags.1?int = GroupCallParticipantVideo;

stickers.suggestedShortName#85fea03f short_name:string = stickers.SuggestedShortName;

botCommandScopeDefault#2f6cb2ab = BotCommandScope;
botCommandScopeUsers#3c4f04d8 = BotCommandScope;
botCommandScopeChats#6fe1a881 = BotCommandScope;
botCommandScopeChatAdmins#b9aa606a = BotCommandScope;
botCommandScopePeer#db9d897d peer:InputPeer = BotCommandScope;
botCommandScopePeerAdmins#3fd863d1 peer:InputPeer = BotCommandScope;
botCommandScopePeerUser#a1321f3 peer:InputPeer user_id:InputUser = BotCommandScope;

account.resetPasswordFailedWait#e3779861 retry_date:int = account.ResetPasswordResult;
account.resetPasswordRequestedWait#e9effc7d until_date:int = account.ResetPasswordResult;
account.resetPasswordOk#e926d63e = account.ResetPasswordResult;

chatTheme#c3dffc04 emoticon:string = ChatTheme;
chatThemeUniqueGift#3458f9c8 gift:StarGift theme_settings:Vector<ThemeSettings> = ChatTheme;

account.chatThemesNotModified#e011e1c4 = account.ChatThemes;
account.chatThemes#be098173 flags:# hash:long themes:Vector<ChatTheme> chats:Vector<Chat> users:Vector<User> next_offset:flags.0?string = account.ChatThemes;

sponsoredMessage#7dbf8673 flags:# recommended:flags.5?true can_report:flags.12?true random_id:bytes url:string title:string message:string entities:flags.1?Vector<MessageEntity> photo:flags.6?Photo media:flags.14?MessageMedia color:flags.13?PeerColor button_text:string sponsor_info:flags.7?string additional_info:flags.8?string min_display_duration:flags.15?int max_display_duration:flags.15?int = SponsoredMessage;

messages.sponsoredMessages#ffda656d flags:# posts_between:flags.0?int start_delay:flags.1?int between_delay:flags.2?int messages:Vector<SponsoredMessage> chats:Vector<Chat> users:Vector<User> = messages.SponsoredMessages;
messages.sponsoredMessagesEmpty#1839490f = messages.SponsoredMessages;

searchResultsCalendarPeriod#c9b0539f date:int min_msg_id:int max_msg_id:int count:int = SearchResultsCalendarPeriod;

messages.searchResultsCalendar#147ee23c flags:# inexact:flags.0?true count:int min_date:int min_msg_id:int offset_id_offset:flags.1?int periods:Vector<SearchResultsCalendarPeriod> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.SearchResultsCalendar;

searchResultPosition#7f648b67 msg_id:int date:int offset:int = SearchResultsPosition;

messages.searchResultsPositions#53b22baf count:int positions:Vector<SearchResultsPosition> = messages.SearchResultsPositions;

channels.sendAsPeers#f496b0c6 peers:Vector<SendAsPeer> chats:Vector<Chat> users:Vector<User> = channels.SendAsPeers;

users.userFull#3b6d152e full_user:UserFull chats:Vector<Chat> users:Vector<User> = users.UserFull;

messages.peerSettings#6880b94d settings:PeerSettings chats:Vector<Chat> users:Vector<User> = messages.PeerSettings;

auth.loggedOut#c3a2835f flags:# future_auth_token:flags.0?bytes = auth.LoggedOut;

reactionCount#a3d1cb80 flags:# chosen_order:flags.0?int reaction:Reaction count:int = ReactionCount;

messageReactions#a339f0b flags:# min:flags.0?true can_see_list:flags.2?true reactions_as_tags:flags.3?true results:Vector<ReactionCount> recent_reactions:flags.1?Vector<MessagePeerReaction> top_reactors:flags.4?Vector<MessageReactor> = MessageReactions;

messages.messageReactionsList#31bd492d flags:# count:int reactions:Vector<MessagePeerReaction> chats:Vector<Chat> users:Vector<User> next_offset:flags.0?string = messages.MessageReactionsList;

availableReaction#c077ec01 flags:# inactive:flags.0?true premium:flags.2?true reaction:string title:string static_icon:Document appear_animation:Document select_animation:Document activate_animation:Document effect_animation:Document around_animation:flags.1?Document center_icon:flags.1?Document = AvailableReaction;

messages.availableReactionsNotModified#9f071957 = messages.AvailableReactions;
messages.availableReactions#768e3aad hash:int reactions:Vector<AvailableReaction> = messages.AvailableReactions;

messagePeerReaction#8c79b63c flags:# big:flags.0?true unread:flags.1?true my:flags.2?true peer_id:Peer date:int reaction:Reaction = MessagePeerReaction;

groupCallStreamChannel#80eb48af channel:int scale:int last_timestamp_ms:long = GroupCallStreamChannel;

phone.groupCallStreamChannels#d0e482b2 channels:Vector<GroupCallStreamChannel> = phone.GroupCallStreamChannels;

phone.groupCallStreamRtmpUrl#2dbf3432 url:string key:string = phone.GroupCallStreamRtmpUrl;

attachMenuBotIconColor#4576f3f0 name:string color:int = AttachMenuBotIconColor;

attachMenuBotIcon#b2a7386b flags:# name:string icon:Document colors:flags.0?Vector<AttachMenuBotIconColor> = AttachMenuBotIcon;

attachMenuBot#d90d8dfe flags:# inactive:flags.0?true has_settings:flags.1?true request_write_access:flags.2?true show_in_attach_menu:flags.3?true show_in_side_menu:flags.4?true side_menu_disclaimer_needed:flags.5?true bot_id:long short_name:string peer_types:flags.3?Vector<AttachMenuPeerType> icons:Vector<AttachMenuBotIcon> = AttachMenuBot;

attachMenuBotsNotModified#f1d88a5c = AttachMenuBots;
attachMenuBots#3c4301c0 hash:long bots:Vector<AttachMenuBot> users:Vector<User> = AttachMenuBots;

attachMenuBotsBot#93bf667f bot:AttachMenuBot users:Vector<User> = AttachMenuBotsBot;

webViewResultUrl#4d22ff98 flags:# fullsize:flags.1?true fullscreen:flags.2?true same_origin:flags.3?true query_id:flags.0?long url:string = WebViewResult;

webViewMessageSent#c94511c flags:# msg_id:flags.0?InputBotInlineMessageID = WebViewMessageSent;

botMenuButtonDefault#7533a588 = BotMenuButton;
botMenuButtonCommands#4258c205 = BotMenuButton;
botMenuButton#c7b57ce6 text:string url:string = BotMenuButton;

account.savedRingtonesNotModified#fbf6e8b1 = account.SavedRingtones;
account.savedRingtones#c1e92cc5 hash:long ringtones:Vector<Document> = account.SavedRingtones;

notificationSoundDefault#97e8bebe = NotificationSound;
notificationSoundNone#6f0c34df = NotificationSound;
notificationSoundLocal#830b9ae4 title:string data:string = NotificationSound;
notificationSoundRingtone#ff6c8049 id:long = NotificationSound;

account.savedRingtone#b7263f6d = account.SavedRingtone;
account.savedRingtoneConverted#1f307eb7 document:Document = account.SavedRingtone;

attachMenuPeerTypeSameBotPM#7d6be90e = AttachMenuPeerType;
attachMenuPeerTypeBotPM#c32bfa1a = AttachMenuPeerType;
attachMenuPeerTypePM#f146d31f = AttachMenuPeerType;
attachMenuPeerTypeChat#509113f = AttachMenuPeerType;
attachMenuPeerTypeBroadcast#7bfbdefc = AttachMenuPeerType;

inputInvoiceMessage#c5b56859 peer:InputPeer msg_id:int = InputInvoice;
inputInvoiceSlug#c326caef slug:string = InputInvoice;
inputInvoicePremiumGiftCode#98986c0d purpose:InputStorePaymentPurpose option:PremiumGiftCodeOption = InputInvoice;
inputInvoiceStars#65f00ce3 purpose:InputStorePaymentPurpose = InputInvoice;
inputInvoiceChatInviteSubscription#34e793f1 hash:string = InputInvoice;
inputInvoiceStarGift#e8625e92 flags:# hide_name:flags.0?true include_upgrade:flags.2?true peer:InputPeer gift_id:long message:flags.1?TextWithEntities = InputInvoice;
inputInvoiceStarGiftUpgrade#4d818d5d flags:# keep_original_details:flags.0?true stargift:InputSavedStarGift = InputInvoice;
inputInvoiceStarGiftTransfer#4a5f5bd9 stargift:InputSavedStarGift to_id:InputPeer = InputInvoice;
inputInvoicePremiumGiftStars#dabab2ef flags:# user_id:InputUser months:int message:flags.0?TextWithEntities = InputInvoice;
inputInvoiceBusinessBotTransferStars#f4997e42 bot:InputUser stars:long = InputInvoice;
inputInvoiceStarGiftResale#e9b0c658 flags:# ton:flags.0?true show_name:flags.2?true slug:string to_id:InputPeer message:flags.1?TextWithEntities = InputInvoice;
inputInvoiceStarGiftPrepaidUpgrade#9a0b48b8 peer:InputPeer hash:string = InputInvoice;
inputInvoicePremiumAuthCode#3e77f614 purpose:InputStorePaymentPurpose = InputInvoice;
inputInvoiceStarGiftDropOriginalDetails#923d8d1 stargift:InputSavedStarGift = InputInvoice;
inputInvoiceStarGiftAuctionBid#1ecafa10 flags:# hide_name:flags.0?true update_bid:flags.2?true peer:flags.3?InputPeer gift_id:long bid_amount:long message:flags.1?TextWithEntities = InputInvoice;

payments.exportedInvoice#aed0cbd9 url:string = payments.ExportedInvoice;

messages.transcribedAudio#cfb9d957 flags:# pending:flags.0?true transcription_id:long text:string trial_remains_num:flags.1?int trial_remains_until_date:flags.1?int = messages.TranscribedAudio;

help.premiumPromo#5334759c status_text:string status_entities:Vector<MessageEntity> video_sections:Vector<string> videos:Vector<Document> period_options:Vector<PremiumSubscriptionOption> users:Vector<User> = help.PremiumPromo;

inputStorePaymentPremiumSubscription#a6751e66 flags:# restore:flags.0?true upgrade:flags.1?true = InputStorePaymentPurpose;
inputStorePaymentGiftPremium#616f7fe8 user_id:InputUser currency:string amount:long = InputStorePaymentPurpose;
inputStorePaymentPremiumGiftCode#fb790393 flags:# users:Vector<InputUser> boost_peer:flags.0?InputPeer currency:string amount:long message:flags.1?TextWithEntities = InputStorePaymentPurpose;
inputStorePaymentPremiumGiveaway#160544ca flags:# only_new_subscribers:flags.0?true winners_are_visible:flags.3?true boost_peer:InputPeer additional_peers:flags.1?Vector<InputPeer> countries_iso2:flags.2?Vector<string> prize_description:flags.4?string random_id:long until_date:int currency:string amount:long = InputStorePaymentPurpose;
inputStorePaymentStarsTopup#f9a2a6cb flags:# stars:long currency:string amount:long spend_purpose_peer:flags.0?InputPeer = InputStorePaymentPurpose;
inputStorePaymentStarsGift#1d741ef7 user_id:InputUser stars:long currency:string amount:long = InputStorePaymentPurpose;
inputStorePaymentStarsGiveaway#751f08fa flags:# only_new_subscribers:flags.0?true winners_are_visible:flags.3?true stars:long boost_peer:InputPeer additional_peers:flags.1?Vector<InputPeer> countries_iso2:flags.2?Vector<string> prize_description:flags.4?string random_id:long until_date:int currency:string amount:long users:int = InputStorePaymentPurpose;
inputStorePaymentAuthCode#3fc18057 flags:# restore:flags.0?true phone_number:string phone_code_hash:string premium_days:int currency:string amount:long = InputStorePaymentPurpose;

paymentFormMethod#88f8f21b url:string title:string = PaymentFormMethod;

emojiStatusEmpty#2de11aae = EmojiStatus;
emojiStatus#e7ff068a flags:# document_id:long until:flags.0?int = EmojiStatus;
emojiStatusCollectible#7184603b flags:# collectible_id:long document_id:long title:string slug:string pattern_document_id:long center_color:int edge_color:int pattern_color:int text_color:int until:flags.0?int = EmojiStatus;
inputEmojiStatusCollectible#7141dbf flags:# collectible_id:long until:flags.0?int = EmojiStatus;

account.emojiStatusesNotModified#d08ce645 = account.EmojiStatuses;
account.emojiStatuses#90c467d1 hash:long statuses:Vector<EmojiStatus> = account.EmojiStatuses;

reactionEmpty#79f5d419 = Reaction;
reactionEmoji#1b2286b8 emoticon:string = Reaction;
reactionCustomEmoji#8935fc73 document_id:long = Reaction;
reactionPaid#523da4eb = Reaction;

chatReactionsNone#eafc32bc = ChatReactions;
chatReactionsAll#52928bca flags:# allow_custom:flags.0?true = ChatReactions;
chatReactionsSome#661d4037 reactions:Vector<Reaction> = ChatReactions;

messages.reactionsNotModified#b06fdbdf = messages.Reactions;
messages.reactions#eafdf716 hash:long reactions:Vector<Reaction> = messages.Reactions;

emailVerifyPurposeLoginSetup#4345be73 phone_number:string phone_code_hash:string = EmailVerifyPurpose;
emailVerifyPurposeLoginChange#527d22eb = EmailVerifyPurpose;
emailVerifyPurposePassport#bbf51685 = EmailVerifyPurpose;

emailVerificationCode#922e55a9 code:string = EmailVerification;
emailVerificationGoogle#db909ec2 token:string = EmailVerification;
emailVerificationApple#96d074fd token:string = EmailVerification;

account.emailVerified#2b96cd1b email:string = account.EmailVerified;
account.emailVerifiedLogin#e1bb0d61 email:string sent_code:auth.SentCode = account.EmailVerified;

premiumSubscriptionOption#5f2d1df2 flags:# current:flags.1?true can_purchase_upgrade:flags.2?true transaction:flags.3?string months:int currency:string amount:long bot_url:string store_product:flags.0?string = PremiumSubscriptionOption;

sendAsPeer#b81c7034 flags:# premium_required:flags.0?true peer:Peer = SendAsPeer;

messageExtendedMediaPreview#ad628cc8 flags:# w:flags.0?int h:flags.0?int thumb:flags.1?PhotoSize video_duration:flags.2?int = MessageExtendedMedia;
messageExtendedMedia#ee479c64 media:MessageMedia = MessageExtendedMedia;

stickerKeyword#fcfeb29c document_id:long keyword:Vector<string> = StickerKeyword;

username#b4073647 flags:# editable:flags.0?true active:flags.1?true deletable:flags.2?true expired:flags.3?true username:string = Username;

forumTopicDeleted#23f109b id:int = ForumTopic;
forumTopic#fcdad815 flags:# my:flags.1?true closed:flags.2?true pinned:flags.3?true short:flags.5?true hidden:flags.6?true title_missing:flags.7?true id:int date:int peer:Peer title:string icon_color:int icon_emoji_id:flags.0?long top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int unread_poll_votes_count:int from_id:Peer notify_settings:PeerNotifySettings draft:flags.4?DraftMessage = ForumTopic;

messages.forumTopics#367617d3 flags:# order_by_create_date:flags.0?true count:int topics:Vector<ForumTopic> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> pts:int = messages.ForumTopics;

defaultHistoryTTL#43b46b20 period:int = DefaultHistoryTTL;

exportedContactToken#41bf109b url:string expires:int = ExportedContactToken;

requestPeerTypeUser#5f3b8a00 flags:# bot:flags.0?Bool premium:flags.1?Bool = RequestPeerType;
requestPeerTypeChat#c9f06e1b flags:# creator:flags.0?true bot_participant:flags.5?true has_username:flags.3?Bool forum:flags.4?Bool user_admin_rights:flags.1?ChatAdminRights bot_admin_rights:flags.2?ChatAdminRights = RequestPeerType;
requestPeerTypeBroadcast#339bef6c flags:# creator:flags.0?true has_username:flags.3?Bool user_admin_rights:flags.1?ChatAdminRights bot_admin_rights:flags.2?ChatAdminRights = RequestPeerType;
requestPeerTypeCreateBot#3e81e078 flags:# bot_managed:flags.0?true suggested_name:flags.1?string suggested_username:flags.2?string = RequestPeerType;

emojiListNotModified#481eadfa = EmojiList;
emojiList#7a1e11d1 hash:long document_id:Vector<long> = EmojiList;

emojiGroup#7a9abda9 title:string icon_emoji_id:long emoticons:Vector<string> = EmojiGroup;
emojiGroupGreeting#80d26cc7 title:string icon_emoji_id:long emoticons:Vector<string> = EmojiGroup;
emojiGroupPremium#93bcf34 title:string icon_emoji_id:long = EmojiGroup;

messages.emojiGroupsNotModified#6fb4ad87 = messages.EmojiGroups;
messages.emojiGroups#881fb94b hash:int groups:Vector<EmojiGroup> = messages.EmojiGroups;

textWithEntities#751f3146 text:string entities:Vector<MessageEntity> = TextWithEntities;

messages.translateResult#33db32f8 result:Vector<TextWithEntities> = messages.TranslatedText;

autoSaveSettings#c84834ce flags:# photos:flags.0?true videos:flags.1?true video_max_size:flags.2?long = AutoSaveSettings;

autoSaveException#81602d47 peer:Peer settings:AutoSaveSettings = AutoSaveException;

account.autoSaveSettings#4c3e069d users_settings:AutoSaveSettings chats_settings:AutoSaveSettings broadcasts_settings:AutoSaveSettings exceptions:Vector<AutoSaveException> chats:Vector<Chat> users:Vector<User> = account.AutoSaveSettings;

help.appConfigNotModified#7cde641d = help.AppConfig;
help.appConfig#dd18782e hash:int config:JSONValue = help.AppConfig;

inputBotAppID#a920bd7a id:long access_hash:long = InputBotApp;
inputBotAppShortName#908c0407 bot_id:InputUser short_name:string = InputBotApp;

botAppNotModified#5da674b7 = BotApp;
botApp#95fcd1d6 flags:# id:long access_hash:long short_name:string title:string description:string photo:Photo document:flags.0?Document hash:long = BotApp;

messages.botApp#eb50adf5 flags:# inactive:flags.0?true request_write_access:flags.1?true has_settings:flags.2?true app:BotApp = messages.BotApp;

inlineBotWebView#b57295d5 text:string url:string = InlineBotWebView;

readParticipantDate#4a4ff172 user_id:long date:int = ReadParticipantDate;

inputChatlistDialogFilter#f3e0da33 filter_id:int = InputChatlist;

exportedChatlistInvite#c5181ac flags:# title:string url:string peers:Vector<Peer> = ExportedChatlistInvite;

chatlists.exportedChatlistInvite#10e6e3a6 filter:DialogFilter invite:ExportedChatlistInvite = chatlists.ExportedChatlistInvite;

chatlists.exportedInvites#10ab6dc7 invites:Vector<ExportedChatlistInvite> chats:Vector<Chat> users:Vector<User> = chatlists.ExportedInvites;

chatlists.chatlistInviteAlready#fa87f659 filter_id:int missing_peers:Vector<Peer> already_peers:Vector<Peer> chats:Vector<Chat> users:Vector<User> = chatlists.ChatlistInvite;
chatlists.chatlistInvite#f10ece2f flags:# title_noanimate:flags.1?true title:TextWithEntities emoticon:flags.0?string peers:Vector<Peer> chats:Vector<Chat> users:Vector<User> = chatlists.ChatlistInvite;

chatlists.chatlistUpdates#93bd878d missing_peers:Vector<Peer> chats:Vector<Chat> users:Vector<User> = chatlists.ChatlistUpdates;

bots.botInfo#e8a775b0 name:string about:string description:string = bots.BotInfo;

messagePeerVote#b6cc2d5c peer:Peer option:bytes date:int = MessagePeerVote;
messagePeerVoteInputOption#74cda504 peer:Peer date:int = MessagePeerVote;
messagePeerVoteMultiple#4628f6e6 peer:Peer options:Vector<bytes> date:int = MessagePeerVote;

storyViews#8d595cd6 flags:# has_viewers:flags.1?true views_count:int forwards_count:flags.2?int reactions:flags.3?Vector<ReactionCount> reactions_count:flags.4?int recent_viewers:flags.0?Vector<long> = StoryViews;

storyItemDeleted#51e6ee4f id:int = StoryItem;
storyItemSkipped#ffadc913 flags:# close_friends:flags.8?true live:flags.9?true id:int date:int expire_date:int = StoryItem;
storyItem#16a4b93c flags:# pinned:flags.5?true public:flags.7?true close_friends:flags.8?true min:flags.9?true noforwards:flags.10?true edited:flags.11?true contacts:flags.12?true selected_contacts:flags.13?true out:flags.16?true id:int date:int from_id:flags.18?Peer fwd_from:flags.17?StoryFwdHeader expire_date:int caption:flags.0?string entities:flags.1?Vector<MessageEntity> media:MessageMedia media_areas:flags.14?Vector<MediaArea> privacy:flags.2?Vector<PrivacyRule> views:flags.3?StoryViews sent_reaction:flags.15?Reaction albums:flags.19?Vector<int> music:flags.20?Document = StoryItem;

stories.allStoriesNotModified#1158fe3e flags:# state:string stealth_mode:StoriesStealthMode = stories.AllStories;
stories.allStories#6efc5e81 flags:# has_more:flags.0?true count:int state:string peer_stories:Vector<PeerStories> chats:Vector<Chat> users:Vector<User> stealth_mode:StoriesStealthMode = stories.AllStories;

stories.stories#63c3dd0a flags:# count:int stories:Vector<StoryItem> pinned_to_top:flags.0?Vector<int> chats:Vector<Chat> users:Vector<User> = stories.Stories;

storyView#b0bdeac5 flags:# blocked:flags.0?true blocked_my_stories_from:flags.1?true user_id:long date:int reaction:flags.2?Reaction = StoryView;
storyViewPublicForward#9083670b flags:# blocked:flags.0?true blocked_my_stories_from:flags.1?true message:Message = StoryView;
storyViewPublicRepost#bd74cf49 flags:# blocked:flags.0?true blocked_my_stories_from:flags.1?true peer_id:Peer story:StoryItem = StoryView;

stories.storyViewsList#59d78fc5 flags:# count:int views_count:int forwards_count:int reactions_count:int views:Vector<StoryView> chats:Vector<Chat> users:Vector<User> next_offset:flags.0?string = stories.StoryViewsList;

stories.storyViews#de9eed1d views:Vector<StoryViews> users:Vector<User> = stories.StoryViews;

inputReplyToMessage#75c1ad70 flags:# reply_to_msg_id:int top_msg_id:flags.0?int reply_to_peer_id:flags.1?InputPeer quote_text:flags.2?string quote_entities:flags.3?Vector<MessageEntity> quote_offset:flags.4?int monoforum_peer_id:flags.5?InputPeer todo_item_id:flags.6?int poll_option:flags.7?bytes quote_rich:flags.8?Vector<RichText> = InputReplyTo;
inputReplyToStory#5881323a peer:InputPeer story_id:int = InputReplyTo;
inputReplyToMonoForum#69d66c45 monoforum_peer_id:InputPeer = InputReplyTo;
inputReplyToEphemeralMessage#4119b95e id:int = InputReplyTo;

exportedStoryLink#3fc9053b link:string = ExportedStoryLink;

storiesStealthMode#712e27fd flags:# active_until_date:flags.0?int cooldown_until_date:flags.1?int = StoriesStealthMode;

mediaAreaCoordinates#cfc9e002 flags:# x:double y:double w:double h:double rotation:double radius:flags.0?double = MediaAreaCoordinates;

mediaAreaVenue#be82db9c coordinates:MediaAreaCoordinates geo:GeoPoint title:string address:string provider:string venue_id:string venue_type:string = MediaArea;
inputMediaAreaVenue#b282217f coordinates:MediaAreaCoordinates query_id:long result_id:string = MediaArea;
mediaAreaGeoPoint#cad5452d flags:# coordinates:MediaAreaCoordinates geo:GeoPoint address:flags.0?GeoPointAddress = MediaArea;
mediaAreaSuggestedReaction#14455871 flags:# dark:flags.0?true flipped:flags.1?true coordinates:MediaAreaCoordinates reaction:Reaction = MediaArea;
mediaAreaChannelPost#770416af coordinates:MediaAreaCoordinates channel_id:long msg_id:int = MediaArea;
inputMediaAreaChannelPost#2271f2bf coordinates:MediaAreaCoordinates channel:InputChannel msg_id:int = MediaArea;
mediaAreaUrl#37381085 coordinates:MediaAreaCoordinates url:string = MediaArea;
mediaAreaWeather#49a6549c coordinates:MediaAreaCoordinates emoji:string temperature_c:double color:int = MediaArea;
mediaAreaStarGift#5787686d coordinates:MediaAreaCoordinates slug:string = MediaArea;

peerStories#9a35e999 flags:# peer:Peer max_read_id:flags.0?int stories:Vector<StoryItem> = PeerStories;

stories.peerStories#cae68768 stories:PeerStories chats:Vector<Chat> users:Vector<User> = stories.PeerStories;

messages.webPage#fd5e12bd webpage:WebPage chats:Vector<Chat> users:Vector<User> = messages.WebPage;

premiumGiftCodeOption#257e962b flags:# users:int months:int store_product:flags.0?string store_quantity:flags.1?int currency:string amount:long = PremiumGiftCodeOption;

payments.checkedGiftCode#eb983f8f flags:# via_giveaway:flags.2?true from_id:flags.4?Peer giveaway_msg_id:flags.3?int to_id:flags.0?long date:int days:int used_date:flags.1?int chats:Vector<Chat> users:Vector<User> = payments.CheckedGiftCode;

payments.giveawayInfo#4367daa0 flags:# participating:flags.0?true preparing_results:flags.3?true start_date:int joined_too_early_date:flags.1?int admin_disallowed_chat_id:flags.2?long disallowed_country:flags.4?string = payments.GiveawayInfo;
payments.giveawayInfoResults#e175e66f flags:# winner:flags.0?true refunded:flags.1?true start_date:int gift_code_slug:flags.3?string stars_prize:flags.4?long finish_date:int winners_count:int activated_count:flags.2?int = payments.GiveawayInfo;

prepaidGiveaway#b2539d54 id:long months:int quantity:int date:int = PrepaidGiveaway;
prepaidStarsGiveaway#9a9d77e0 id:long stars:long quantity:int boosts:int date:int = PrepaidGiveaway;

boost#4b3e14d6 flags:# gift:flags.1?true giveaway:flags.2?true unclaimed:flags.3?true id:string user_id:flags.0?long giveaway_msg_id:flags.2?int date:int expires:int used_gift_slug:flags.4?string multiplier:flags.5?int stars:flags.6?long = Boost;

premium.boostsList#86f8613c flags:# count:int boosts:Vector<Boost> next_offset:flags.0?string users:Vector<User> = premium.BoostsList;

myBoost#c448415c flags:# slot:int peer:flags.0?Peer date:int expires:int cooldown_until_date:flags.1?int = MyBoost;

premium.myBoosts#9ae228e2 my_boosts:Vector<MyBoost> chats:Vector<Chat> users:Vector<User> = premium.MyBoosts;

premium.boostsStatus#4959427a flags:# my_boost:flags.2?true level:int current_level_boosts:int boosts:int gift_boosts:flags.4?int next_level_boosts:flags.0?int premium_audience:flags.1?StatsPercentValue boost_url:string prepaid_giveaways:flags.3?Vector<PrepaidGiveaway> my_boost_slots:flags.2?Vector<int> = premium.BoostsStatus;

storyFwdHeader#b826e150 flags:# modified:flags.3?true from:flags.0?Peer from_name:flags.1?string story_id:flags.2?int = StoryFwdHeader;

postInteractionCountersMessage#e7058e7f msg_id:int views:int forwards:int reactions:int = PostInteractionCounters;
postInteractionCountersStory#8a480e27 story_id:int views:int forwards:int reactions:int = PostInteractionCounters;

stats.storyStats#50cd067c views_graph:StatsGraph reactions_by_emotion_graph:StatsGraph = stats.StoryStats;

publicForwardMessage#1f2bf4a message:Message = PublicForward;
publicForwardStory#edf3add0 peer:Peer story:StoryItem = PublicForward;

stats.publicForwards#93037e20 flags:# count:int forwards:Vector<PublicForward> next_offset:flags.0?string chats:Vector<Chat> users:Vector<User> = stats.PublicForwards;

peerColor#b54b5acf flags:# color:flags.0?int background_emoji_id:flags.1?long = PeerColor;
peerColorCollectible#b9c0639a flags:# collectible_id:long gift_emoji_id:long background_emoji_id:long accent_color:int colors:Vector<int> dark_accent_color:flags.0?int dark_colors:flags.1?Vector<int> = PeerColor;
inputPeerColorCollectible#b8ea86a9 collectible_id:long = PeerColor;

help.peerColorSet#26219a58 colors:Vector<int> = help.PeerColorSet;
help.peerColorProfileSet#767d61eb palette_colors:Vector<int> bg_colors:Vector<int> story_colors:Vector<int> = help.PeerColorSet;

help.peerColorOption#adec6ebe flags:# hidden:flags.0?true color_id:int colors:flags.1?help.PeerColorSet dark_colors:flags.2?help.PeerColorSet channel_min_level:flags.3?int group_min_level:flags.4?int = help.PeerColorOption;

help.peerColorsNotModified#2ba1f5ce = help.PeerColors;
help.peerColors#f8ed08 hash:int colors:Vector<help.PeerColorOption> = help.PeerColors;

storyReaction#6090d6d5 peer_id:Peer date:int reaction:Reaction = StoryReaction;
storyReactionPublicForward#bbab2643 message:Message = StoryReaction;
storyReactionPublicRepost#cfcd0f13 peer_id:Peer story:StoryItem = StoryReaction;

stories.storyReactionsList#aa5f789c flags:# count:int reactions:Vector<StoryReaction> chats:Vector<Chat> users:Vector<User> next_offset:flags.0?string = stories.StoryReactionsList;

savedDialog#bd87cb6c flags:# pinned:flags.2?true peer:Peer top_message:int = SavedDialog;
monoForumDialog#64407ea7 flags:# unread_mark:flags.3?true nopaid_messages_exception:flags.4?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_reactions_count:int draft:flags.1?DraftMessage = SavedDialog;

messages.savedDialogs#f83ae221 dialogs:Vector<SavedDialog> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.SavedDialogs;
messages.savedDialogsSlice#44ba9dd9 count:int dialogs:Vector<SavedDialog> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.SavedDialogs;
messages.savedDialogsNotModified#c01f6fe8 count:int = messages.SavedDialogs;

savedReactionTag#cb6ff828 flags:# reaction:Reaction title:flags.0?string count:int = SavedReactionTag;

messages.savedReactionTagsNotModified#889b59ef = messages.SavedReactionTags;
messages.savedReactionTags#3259950a tags:Vector<SavedReactionTag> hash:long = messages.SavedReactionTags;

outboxReadDate#3bb842ac date:int = OutboxReadDate;

smsjobs.eligibleToJoin#dc8b44cf terms_url:string monthly_sent_sms:int = smsjobs.EligibilityToJoin;

smsjobs.status#2aee9191 flags:# allow_international:flags.0?true recent_sent:int recent_since:int recent_remains:int total_sent:int total_since:int last_gift_slug:flags.1?string terms_url:string = smsjobs.Status;

smsJob#e6a1eeb8 job_id:string phone_number:string text:string = SmsJob;

businessWeeklyOpen#120b1ab9 start_minute:int end_minute:int = BusinessWeeklyOpen;

businessWorkHours#8c92b098 flags:# open_now:flags.0?true timezone_id:string weekly_open:Vector<BusinessWeeklyOpen> = BusinessWorkHours;

businessLocation#ac5c1af7 flags:# geo_point:flags.0?GeoPoint address:string = BusinessLocation;

inputBusinessRecipients#6f8b32aa flags:# existing_chats:flags.0?true new_chats:flags.1?true contacts:flags.2?true non_contacts:flags.3?true exclude_selected:flags.5?true users:flags.4?Vector<InputUser> = InputBusinessRecipients;

businessRecipients#21108ff7 flags:# existing_chats:flags.0?true new_chats:flags.1?true contacts:flags.2?true non_contacts:flags.3?true exclude_selected:flags.5?true users:flags.4?Vector<long> = BusinessRecipients;

businessAwayMessageScheduleAlways#c9b9e2b9 = BusinessAwayMessageSchedule;
businessAwayMessageScheduleOutsideWorkHours#c3f2f501 = BusinessAwayMessageSchedule;
businessAwayMessageScheduleCustom#cc4d9ecc start_date:int end_date:int = BusinessAwayMessageSchedule;

inputBusinessGreetingMessage#194cb3b shortcut_id:int recipients:InputBusinessRecipients no_activity_days:int = InputBusinessGreetingMessage;

businessGreetingMessage#e519abab shortcut_id:int recipients:BusinessRecipients no_activity_days:int = BusinessGreetingMessage;

inputBusinessAwayMessage#832175e0 flags:# offline_only:flags.0?true shortcut_id:int schedule:BusinessAwayMessageSchedule recipients:InputBusinessRecipients = InputBusinessAwayMessage;

businessAwayMessage#ef156a5c flags:# offline_only:flags.0?true shortcut_id:int schedule:BusinessAwayMessageSchedule recipients:BusinessRecipients = BusinessAwayMessage;

timezone#ff9289f5 id:string name:string utc_offset:int = Timezone;

help.timezonesListNotModified#970708cc = help.TimezonesList;
help.timezonesList#7b74ed71 timezones:Vector<Timezone> hash:int = help.TimezonesList;

quickReply#697102b shortcut_id:int shortcut:string top_message:int count:int = QuickReply;

inputQuickReplyShortcut#24596d41 shortcut:string = InputQuickReplyShortcut;
inputQuickReplyShortcutId#1190cf1 shortcut_id:int = InputQuickReplyShortcut;

messages.quickReplies#c68d6695 quick_replies:Vector<QuickReply> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.QuickReplies;
messages.quickRepliesNotModified#5f91eb5b = messages.QuickReplies;

connectedBot#33ed001 flags:# bot_id:long recipients:BusinessBotRecipients rights:BusinessBotRights device:flags.0?string date:flags.1?int location:flags.2?string = ConnectedBot;

account.connectedBots#17d7f87b connected_bots:Vector<ConnectedBot> users:Vector<User> = account.ConnectedBots;

messages.dialogFilters#2ad93719 flags:# tags_enabled:flags.0?true filters:Vector<DialogFilter> = messages.DialogFilters;

birthday#6c8e1e06 flags:# day:int month:int year:flags.0?int = Birthday;

botBusinessConnection#8f34b2f5 flags:# disabled:flags.1?true connection_id:string user_id:long dc_id:int date:int rights:flags.2?BusinessBotRights = BotBusinessConnection;

inputBusinessIntro#9c469cd flags:# title:string description:string sticker:flags.0?InputDocument = InputBusinessIntro;

businessIntro#5a0a066d flags:# title:string description:string sticker:flags.0?Document = BusinessIntro;

messages.myStickers#faff629d count:int sets:Vector<StickerSetCovered> = messages.MyStickers;

inputCollectibleUsername#e39460a9 username:string = InputCollectible;
inputCollectiblePhone#a2e214a4 phone:string = InputCollectible;

fragment.collectibleInfo#6ebdff91 purchase_date:int currency:string amount:long crypto_currency:string crypto_amount:long url:string = fragment.CollectibleInfo;

inputBusinessBotRecipients#c4e5921e flags:# existing_chats:flags.0?true new_chats:flags.1?true contacts:flags.2?true non_contacts:flags.3?true exclude_selected:flags.5?true users:flags.4?Vector<InputUser> exclude_users:flags.6?Vector<InputUser> = InputBusinessBotRecipients;

businessBotRecipients#b88cf373 flags:# existing_chats:flags.0?true new_chats:flags.1?true contacts:flags.2?true non_contacts:flags.3?true exclude_selected:flags.5?true users:flags.4?Vector<long> exclude_users:flags.6?Vector<long> = BusinessBotRecipients;

contactBirthday#1d998733 contact_id:long birthday:Birthday = ContactBirthday;

contacts.contactBirthdays#114ff30d contacts:Vector<ContactBirthday> users:Vector<User> = contacts.ContactBirthdays;

missingInvitee#628c9224 flags:# premium_would_allow_invite:flags.0?true premium_required_for_pm:flags.1?true user_id:long = MissingInvitee;

messages.invitedUsers#7f5defa6 updates:Updates missing_invitees:Vector<MissingInvitee> = messages.InvitedUsers;

inputBusinessChatLink#11679fa7 flags:# message:string entities:flags.0?Vector<MessageEntity> title:flags.1?string = InputBusinessChatLink;

businessChatLink#b4ae666f flags:# link:string message:string entities:flags.0?Vector<MessageEntity> title:flags.1?string views:int = BusinessChatLink;

account.businessChatLinks#ec43a2d1 links:Vector<BusinessChatLink> chats:Vector<Chat> users:Vector<User> = account.BusinessChatLinks;

account.resolvedBusinessChatLinks#9a23af21 flags:# peer:Peer message:string entities:flags.0?Vector<MessageEntity> chats:Vector<Chat> users:Vector<User> = account.ResolvedBusinessChatLinks;

requestedPeerUser#d62ff46a flags:# user_id:long first_name:flags.0?string last_name:flags.0?string username:flags.1?string photo:flags.2?Photo = RequestedPeer;
requestedPeerChat#7307544f flags:# chat_id:long title:flags.0?string photo:flags.2?Photo = RequestedPeer;
requestedPeerChannel#8ba403e4 flags:# channel_id:long title:flags.0?string username:flags.1?string photo:flags.2?Photo = RequestedPeer;

sponsoredMessageReportOption#430d3150 text:string option:bytes = SponsoredMessageReportOption;

channels.sponsoredMessageReportResultChooseOption#846f9e42 title:string options:Vector<SponsoredMessageReportOption> = channels.SponsoredMessageReportResult;
channels.sponsoredMessageReportResultAdsHidden#3e3bcf2f = channels.SponsoredMessageReportResult;
channels.sponsoredMessageReportResultReported#ad798849 = channels.SponsoredMessageReportResult;

reactionNotificationsFromContacts#bac3a61a = ReactionNotificationsFrom;
reactionNotificationsFromAll#4b9e22a0 = ReactionNotificationsFrom;

reactionsNotifySettings#71e4ea58 flags:# messages_notify_from:flags.0?ReactionNotificationsFrom stories_notify_from:flags.1?ReactionNotificationsFrom poll_votes_notify_from:flags.2?ReactionNotificationsFrom sound:NotificationSound show_previews:Bool = ReactionsNotifySettings;

availableEffect#93c3e27e flags:# premium_required:flags.2?true id:long emoticon:string static_icon_id:flags.0?long effect_sticker_id:long effect_animation_id:flags.1?long = AvailableEffect;

messages.availableEffectsNotModified#d1ed9a5b = messages.AvailableEffects;
messages.availableEffects#bddb616e hash:int effects:Vector<AvailableEffect> documents:Vector<Document> = messages.AvailableEffects;

factCheck#b89bfccf flags:# need_check:flags.0?true country:flags.1?string text:flags.1?TextWithEntities hash:long = FactCheck;

starsTransactionPeerUnsupported#95f2bfe4 = StarsTransactionPeer;
starsTransactionPeerAppStore#b457b375 = StarsTransactionPeer;
starsTransactionPeerPlayMarket#7b560a0b = StarsTransactionPeer;
starsTransactionPeerPremiumBot#250dbaf8 = StarsTransactionPeer;
starsTransactionPeerFragment#e92fd902 = StarsTransactionPeer;
starsTransactionPeer#d80da15d peer:Peer = StarsTransactionPeer;
starsTransactionPeerAds#60682812 = StarsTransactionPeer;
starsTransactionPeerAPI#f9677aad = StarsTransactionPeer;

starsTopupOption#bd915c0 flags:# extended:flags.1?true stars:long store_product:flags.0?string currency:string amount:long = StarsTopupOption;

starsTransaction#13659eb0 flags:# refund:flags.3?true pending:flags.4?true failed:flags.6?true gift:flags.10?true reaction:flags.11?true stargift_upgrade:flags.18?true business_transfer:flags.21?true stargift_resale:flags.22?true posts_search:flags.24?true stargift_prepaid_upgrade:flags.25?true stargift_drop_original_details:flags.26?true phonegroup_message:flags.27?true stargift_auction_bid:flags.28?true offer:flags.29?true id:string amount:StarsAmount date:int peer:StarsTransactionPeer title:flags.0?string description:flags.1?string photo:flags.2?WebDocument transaction_date:flags.5?int transaction_url:flags.5?string bot_payload:flags.7?bytes msg_id:flags.8?int extended_media:flags.9?Vector<MessageMedia> subscription_period:flags.12?int giveaway_post_id:flags.13?int stargift:flags.14?StarGift floodskip_number:flags.15?int starref_commission_permille:flags.16?int starref_peer:flags.17?Peer starref_amount:flags.17?StarsAmount paid_messages:flags.19?int premium_gift_months:flags.20?int ads_proceeds_from_date:flags.23?int ads_proceeds_to_date:flags.23?int = StarsTransaction;

payments.starsStatus#6c9ce8ed flags:# balance:StarsAmount subscriptions:flags.1?Vector<StarsSubscription> subscriptions_next_offset:flags.2?string subscriptions_missing_balance:flags.4?long history:flags.3?Vector<StarsTransaction> next_offset:flags.0?string chats:Vector<Chat> users:Vector<User> = payments.StarsStatus;

foundStory#e87acbc0 peer:Peer story:StoryItem = FoundStory;

stories.foundStories#e2de7737 flags:# count:int stories:Vector<FoundStory> next_offset:flags.0?string chats:Vector<Chat> users:Vector<User> = stories.FoundStories;

geoPointAddress#de4c5d93 flags:# country_iso2:string state:flags.0?string city:flags.1?string street:flags.2?string = GeoPointAddress;

starsRevenueStatus#febe5491 flags:# withdrawal_enabled:flags.0?true current_balance:StarsAmount available_balance:StarsAmount overall_revenue:StarsAmount next_withdrawal_at:flags.1?int = StarsRevenueStatus;

payments.starsRevenueStats#6c207376 flags:# top_hours_graph:flags.0?StatsGraph revenue_graph:StatsGraph status:StarsRevenueStatus usd_rate:double = payments.StarsRevenueStats;

payments.starsRevenueWithdrawalUrl#1dab80b7 url:string = payments.StarsRevenueWithdrawalUrl;

payments.starsRevenueAdsAccountUrl#394e7f21 url:string = payments.StarsRevenueAdsAccountUrl;

inputStarsTransaction#206ae6d1 flags:# refund:flags.0?true id:string = InputStarsTransaction;

starsGiftOption#5e0589f1 flags:# extended:flags.1?true stars:long store_product:flags.0?string currency:string amount:long = StarsGiftOption;

bots.popularAppBots#1991b13b flags:# next_offset:flags.0?string users:Vector<User> = bots.PopularAppBots;

botPreviewMedia#23e91ba3 date:int media:MessageMedia = BotPreviewMedia;

bots.previewInfo#ca71d64 media:Vector<BotPreviewMedia> lang_codes:Vector<string> = bots.PreviewInfo;

starsSubscriptionPricing#5416d58 period:int amount:long = StarsSubscriptionPricing;

starsSubscription#2e6eab1a flags:# canceled:flags.0?true can_refulfill:flags.1?true missing_balance:flags.2?true bot_canceled:flags.7?true id:string peer:Peer until_date:int pricing:StarsSubscriptionPricing chat_invite_hash:flags.3?string title:flags.4?string photo:flags.5?WebDocument invoice_slug:flags.6?string = StarsSubscription;

messageReactor#4ba3a95a flags:# top:flags.0?true my:flags.1?true anonymous:flags.2?true peer_id:flags.3?Peer count:int = MessageReactor;

starsGiveawayOption#94ce852a flags:# extended:flags.0?true default:flags.1?true stars:long yearly_boosts:int store_product:flags.2?string currency:string amount:long winners:Vector<StarsGiveawayWinnersOption> = StarsGiveawayOption;

starsGiveawayWinnersOption#54236209 flags:# default:flags.0?true users:int per_user_stars:long = StarsGiveawayWinnersOption;

starGift#313a9547 flags:# limited:flags.0?true sold_out:flags.1?true birthday:flags.2?true require_premium:flags.7?true limited_per_user:flags.8?true peer_color_available:flags.10?true auction:flags.11?true id:long sticker:Document stars:long availability_remains:flags.0?int availability_total:flags.0?int availability_resale:flags.4?long convert_stars:long first_sale_date:flags.1?int last_sale_date:flags.1?int upgrade_stars:flags.3?long resell_min_stars:flags.4?long title:flags.5?string released_by:flags.6?Peer per_user_total:flags.8?int per_user_remains:flags.8?int locked_until_date:flags.9?int auction_slug:flags.11?string gifts_per_round:flags.11?int auction_start_date:flags.11?int upgrade_variants:flags.12?int background:flags.13?StarGiftBackground = StarGift;
starGiftUnique#85f0a9cd flags:# require_premium:flags.6?true resale_ton_only:flags.7?true theme_available:flags.9?true burned:flags.14?true crafted:flags.15?true id:long gift_id:long title:string slug:string num:int owner_id:flags.0?Peer owner_name:flags.1?string owner_address:flags.2?string attributes:Vector<StarGiftAttribute> availability_issued:int availability_total:int gift_address:flags.3?string resell_amount:flags.4?Vector<StarsAmount> released_by:flags.5?Peer value_amount:flags.8?long value_currency:flags.8?string value_usd_amount:flags.8?long theme_peer:flags.10?Peer peer_color:flags.11?PeerColor host_id:flags.12?Peer offer_min_stars:flags.13?int craft_chance_permille:flags.16?int = StarGift;

payments.starGiftsNotModified#a388a368 = payments.StarGifts;
payments.starGifts#2ed82995 hash:int gifts:Vector<StarGift> chats:Vector<Chat> users:Vector<User> = payments.StarGifts;

messageReportOption#7903e3d9 text:string option:bytes = MessageReportOption;

reportResultChooseOption#f0e4e0b6 title:string options:Vector<MessageReportOption> = ReportResult;
reportResultAddComment#6f09ac31 flags:# optional:flags.0?true option:bytes = ReportResult;
reportResultReported#8db33c4b = ReportResult;

messages.botPreparedInlineMessage#8ecf0511 id:string expire_date:int = messages.BotPreparedInlineMessage;

messages.preparedInlineMessage#ff57708d query_id:long result:BotInlineResult peer_types:Vector<InlineQueryPeerType> cache_time:int users:Vector<User> = messages.PreparedInlineMessage;

botAppSettings#c99b1950 flags:# placeholder_path:flags.0?bytes background_color:flags.1?int background_dark_color:flags.2?int header_color:flags.3?int header_dark_color:flags.4?int = BotAppSettings;

starRefProgram#dd0c66f2 flags:# bot_id:long commission_permille:int duration_months:flags.0?int end_date:flags.1?int daily_revenue_per_user:flags.2?StarsAmount = StarRefProgram;

connectedBotStarRef#19a13f71 flags:# revoked:flags.1?true url:string date:int bot_id:long commission_permille:int duration_months:flags.0?int participants:long revenue:long = ConnectedBotStarRef;

payments.connectedStarRefBots#98d5ea1d count:int connected_bots:Vector<ConnectedBotStarRef> users:Vector<User> = payments.ConnectedStarRefBots;

payments.suggestedStarRefBots#b4d5d859 flags:# count:int suggested_bots:Vector<StarRefProgram> users:Vector<User> next_offset:flags.0?string = payments.SuggestedStarRefBots;

starsAmount#bbb6b4a3 amount:long nanos:int = StarsAmount;
starsTonAmount#74aee3e0 amount:long = StarsAmount;

messages.foundStickersNotModified#6010c534 flags:# next_offset:flags.0?int = messages.FoundStickers;
messages.foundStickers#82c9e290 flags:# next_offset:flags.0?int hash:long stickers:Vector<Document> = messages.FoundStickers;

botVerifierSettings#a12aefff flags:# can_modify_custom_description:flags.1?true icon:long company:string custom_description:flags.0?TextWithEntities = BotVerifierSettings;

botVerification#f72e0ed9 bot_id:long icon:long description:TextWithEntities = BotVerification;

starGiftAttributeModel#565251e2 flags:# crafted:flags.0?true name:string document:Document rarity:StarGiftAttributeRarity = StarGiftAttribute;
starGiftAttributePattern#4e7085ea name:string document:Document rarity:StarGiftAttributeRarity = StarGiftAttribute;
starGiftAttributeBackdrop#9f2504e4 name:string backdrop_id:int center_color:int edge_color:int pattern_color:int text_color:int rarity:StarGiftAttributeRarity = StarGiftAttribute;
starGiftAttributeOriginalDetails#e0bff26c flags:# sender_id:flags.0?Peer recipient_id:Peer date:int message:flags.1?TextWithEntities = StarGiftAttribute;

payments.starGiftUpgradePreview#3de1dfed sample_attributes:Vector<StarGiftAttribute> prices:Vector<StarGiftUpgradePrice> next_prices:Vector<StarGiftUpgradePrice> = payments.StarGiftUpgradePreview;

users.users#62d706b8 users:Vector<User> = users.Users;
users.usersSlice#315a4974 count:int users:Vector<User> = users.Users;

payments.uniqueStarGift#416c56e8 gift:StarGift chats:Vector<Chat> users:Vector<User> = payments.UniqueStarGift;

messages.webPagePreview#8c9a88ac media:MessageMedia chats:Vector<Chat> users:Vector<User> = messages.WebPagePreview;

savedStarGift#41df43fc flags:# name_hidden:flags.0?true unsaved:flags.5?true refunded:flags.9?true can_upgrade:flags.10?true pinned_to_top:flags.12?true upgrade_separate:flags.17?true from_id:flags.1?Peer date:int gift:StarGift message:flags.2?TextWithEntities msg_id:flags.3?int saved_id:flags.11?long convert_stars:flags.4?long upgrade_stars:flags.6?long can_export_at:flags.7?int transfer_stars:flags.8?long can_transfer_at:flags.13?int can_resell_at:flags.14?int collection_id:flags.15?Vector<int> prepaid_upgrade_hash:flags.16?string drop_original_details_stars:flags.18?long gift_num:flags.19?int can_craft_at:flags.20?int = SavedStarGift;

payments.savedStarGifts#95f389b1 flags:# count:int chat_notifications_enabled:flags.1?Bool gifts:Vector<SavedStarGift> next_offset:flags.0?string chats:Vector<Chat> users:Vector<User> = payments.SavedStarGifts;

inputSavedStarGiftUser#69279795 msg_id:int = InputSavedStarGift;
inputSavedStarGiftChat#f101aa7f peer:InputPeer saved_id:long = InputSavedStarGift;
inputSavedStarGiftSlug#2085c238 slug:string = InputSavedStarGift;

payments.starGiftWithdrawalUrl#84aa3a9c url:string = payments.StarGiftWithdrawalUrl;

paidReactionPrivacyDefault#206ad49e = PaidReactionPrivacy;
paidReactionPrivacyAnonymous#1f0c1ad9 = PaidReactionPrivacy;
paidReactionPrivacyPeer#dc6cfcf0 peer:InputPeer = PaidReactionPrivacy;

account.paidMessagesRevenue#1e109708 stars_amount:long = account.PaidMessagesRevenue;

requirementToContactEmpty#50a9839 = RequirementToContact;
requirementToContactPremium#e581e4e9 = RequirementToContact;
requirementToContactPaidMessages#b4f67e93 stars_amount:long = RequirementToContact;

businessBotRights#a0624cf7 flags:# reply:flags.0?true read_messages:flags.1?true delete_sent_messages:flags.2?true delete_received_messages:flags.3?true edit_name:flags.4?true edit_bio:flags.5?true edit_profile_photo:flags.6?true edit_username:flags.7?true view_gifts:flags.8?true sell_gifts:flags.9?true change_gift_settings:flags.10?true transfer_and_upgrade_gifts:flags.11?true transfer_stars:flags.12?true manage_stories:flags.13?true = BusinessBotRights;

disallowedGiftsSettings#71f276c4 flags:# disallow_unlimited_stargifts:flags.0?true disallow_limited_stargifts:flags.1?true disallow_unique_stargifts:flags.2?true disallow_premium_gifts:flags.3?true disallow_stargifts_from_channels:flags.4?true = DisallowedGiftsSettings;

sponsoredPeer#c69708d3 flags:# random_id:bytes peer:Peer sponsor_info:flags.0?string additional_info:flags.1?string = SponsoredPeer;

contacts.sponsoredPeersEmpty#ea32b4b1 = contacts.SponsoredPeers;
contacts.sponsoredPeers#eb032884 peers:Vector<SponsoredPeer> chats:Vector<Chat> users:Vector<User> = contacts.SponsoredPeers;

starGiftAttributeIdModel#48aaae3c document_id:long = StarGiftAttributeId;
starGiftAttributeIdPattern#4a162433 document_id:long = StarGiftAttributeId;
starGiftAttributeIdBackdrop#1f01c757 backdrop_id:int = StarGiftAttributeId;

starGiftAttributeCounter#2eb1b658 attribute:StarGiftAttributeId count:int = StarGiftAttributeCounter;

payments.resaleStarGifts#947a12df flags:# count:int gifts:Vector<StarGift> next_offset:flags.0?string attributes:flags.1?Vector<StarGiftAttribute> attributes_hash:flags.1?long chats:Vector<Chat> counters:flags.2?Vector<StarGiftAttributeCounter> users:Vector<User> = payments.ResaleStarGifts;

stories.canSendStoryCount#c387c04e count_remains:int = stories.CanSendStoryCount;

pendingSuggestion#e7e82e12 suggestion:string title:TextWithEntities description:TextWithEntities url:string = PendingSuggestion;

todoItem#cba9a52f id:int title:TextWithEntities = TodoItem;

todoList#49b92a26 flags:# others_can_append:flags.0?true others_can_complete:flags.1?true title:TextWithEntities list:Vector<TodoItem> = TodoList;

todoCompletion#221bb5e4 id:int completed_by:Peer date:int = TodoCompletion;

suggestedPost#e8e37e5 flags:# accepted:flags.1?true rejected:flags.2?true price:flags.3?StarsAmount schedule_date:flags.0?int = SuggestedPost;

starsRating#1b0e4f07 flags:# level:int current_level_stars:long stars:long next_level_stars:flags.0?long = StarsRating;

starGiftCollection#9d6b13b0 flags:# collection_id:int title:string icon:flags.0?Document gifts_count:int hash:long = StarGiftCollection;

payments.starGiftCollectionsNotModified#a0ba4f17 = payments.StarGiftCollections;
payments.starGiftCollections#8a2932f3 collections:Vector<StarGiftCollection> = payments.StarGiftCollections;

storyAlbum#9325705a flags:# album_id:int title:string icon_photo:flags.0?Photo icon_video:flags.1?Document = StoryAlbum;

stories.albumsNotModified#564edaeb = stories.Albums;
stories.albums#c3987a3a hash:long albums:Vector<StoryAlbum> = stories.Albums;

searchPostsFlood#3e0b5b6a flags:# query_is_free:flags.0?true total_daily:int remains:int wait_till:flags.1?int stars_amount:long = SearchPostsFlood;

payments.uniqueStarGiftValueInfo#512fe446 flags:# last_sale_on_fragment:flags.1?true value_is_average:flags.6?true currency:string value:long initial_sale_date:int initial_sale_stars:long initial_sale_price:long last_sale_date:flags.0?int last_sale_price:flags.0?long floor_price:flags.2?long average_price:flags.3?long listed_count:flags.4?int fragment_listed_count:flags.5?int fragment_listed_url:flags.5?string = payments.UniqueStarGiftValueInfo;

profileTabPosts#b98cd696 = ProfileTab;
profileTabGifts#4d4bd46a = ProfileTab;
profileTabMedia#72c64955 = ProfileTab;
profileTabFiles#ab339c00 = ProfileTab;
profileTabMusic#9f27d26e = ProfileTab;
profileTabVoice#e477092e = ProfileTab;
profileTabLinks#d3656499 = ProfileTab;
profileTabGifs#a2c0f695 = ProfileTab;

users.savedMusicNotModified#e3878aa4 count:int = users.SavedMusic;
users.savedMusic#34a2f297 count:int documents:Vector<Document> = users.SavedMusic;

account.savedMusicIdsNotModified#4fc81d6e = account.SavedMusicIds;
account.savedMusicIds#998d6636 ids:Vector<long> = account.SavedMusicIds;

payments.checkCanSendGiftResultOk#374fa7ad = payments.CheckCanSendGiftResult;
payments.checkCanSendGiftResultFail#d5e58274 reason:TextWithEntities = payments.CheckCanSendGiftResult;

inputChatThemeEmpty#83268483 = InputChatTheme;
inputChatTheme#c93de95c emoticon:string = InputChatTheme;
inputChatThemeUniqueGift#87e5dfe4 slug:string = InputChatTheme;

starGiftUpgradePrice#99ea331d date:int upgrade_stars:long = StarGiftUpgradePrice;

groupCallMessage#1a8afc7e flags:# from_admin:flags.1?true id:int from_id:Peer date:int message:TextWithEntities paid_message_stars:flags.0?long = GroupCallMessage;

groupCallDonor#ee430c85 flags:# top:flags.0?true my:flags.1?true peer_id:flags.3?Peer stars:long = GroupCallDonor;

phone.groupCallStars#9d1dbd26 total_stars:long top_donors:Vector<GroupCallDonor> chats:Vector<Chat> users:Vector<User> = phone.GroupCallStars;

recentStory#711d692d flags:# live:flags.0?true max_id:flags.1?int = RecentStory;

auctionBidLevel#310240cc pos:int amount:long date:int = AuctionBidLevel;

starGiftAuctionStateNotModified#fe333952 = StarGiftAuctionState;
starGiftAuctionState#771a4e66 version:int start_date:int end_date:int min_bid_amount:long bid_levels:Vector<AuctionBidLevel> top_bidders:Vector<long> next_round_at:int last_gift_num:int gifts_left:int current_round:int total_rounds:int rounds:Vector<StarGiftAuctionRound> = StarGiftAuctionState;
starGiftAuctionStateFinished#972dabbf flags:# start_date:int end_date:int average_price:long listed_count:flags.0?int fragment_listed_count:flags.1?int fragment_listed_url:flags.1?string = StarGiftAuctionState;

starGiftAuctionUserState#2eeed1c4 flags:# returned:flags.1?true bid_amount:flags.0?long bid_date:flags.0?int min_bid_amount:flags.0?long bid_peer:flags.0?Peer acquired_count:int = StarGiftAuctionUserState;

payments.starGiftAuctionState#6b39f4ec gift:StarGift state:StarGiftAuctionState user_state:StarGiftAuctionUserState timeout:int users:Vector<User> chats:Vector<Chat> = payments.StarGiftAuctionState;

starGiftAuctionAcquiredGift#42b00348 flags:# name_hidden:flags.0?true peer:Peer date:int bid_amount:long round:int pos:int message:flags.1?TextWithEntities gift_num:flags.2?int = StarGiftAuctionAcquiredGift;

payments.starGiftAuctionAcquiredGifts#7d5bd1f0 gifts:Vector<StarGiftAuctionAcquiredGift> users:Vector<User> chats:Vector<Chat> = payments.StarGiftAuctionAcquiredGifts;

starGiftActiveAuctionState#d31bc45d gift:StarGift state:StarGiftAuctionState user_state:StarGiftAuctionUserState = StarGiftActiveAuctionState;

payments.starGiftActiveAuctionsNotModified#db33dad0 = payments.StarGiftActiveAuctions;
payments.starGiftActiveAuctions#aef6abbc auctions:Vector<StarGiftActiveAuctionState> users:Vector<User> chats:Vector<Chat> = payments.StarGiftActiveAuctions;

inputStarGiftAuction#2e16c98 gift_id:long = InputStarGiftAuction;
inputStarGiftAuctionSlug#7ab58308 slug:string = InputStarGiftAuction;

passkey#98613ebf flags:# id:string name:string date:int software_emoji_id:flags.0?long last_usage_date:flags.1?int = Passkey;

account.passkeys#f8e0aa1c passkeys:Vector<Passkey> = account.Passkeys;

account.passkeyRegistrationOptions#e16b5ce1 options:DataJSON = account.PasskeyRegistrationOptions;

auth.passkeyLoginOptions#e2037789 options:DataJSON = auth.PasskeyLoginOptions;

inputPasskeyResponseRegister#3e63935c client_data:DataJSON attestation_data:bytes = InputPasskeyResponse;
inputPasskeyResponseLogin#c31fc14a client_data:DataJSON authenticator_data:bytes signature:bytes user_handle:string = InputPasskeyResponse;

inputPasskeyCredentialPublicKey#3c27b78f id:string raw_id:string response:InputPasskeyResponse = InputPasskeyCredential;

starGiftBackground#aff56398 center_color:int edge_color:int text_color:int = StarGiftBackground;

starGiftAuctionRound#3aae0528 num:int duration:int = StarGiftAuctionRound;
starGiftAuctionRoundExtendable#aa021e5 num:int duration:int extend_top:int extend_window:int = StarGiftAuctionRound;

payments.starGiftUpgradeAttributes#46c6e36f attributes:Vector<StarGiftAttribute> = payments.StarGiftUpgradeAttributes;

messages.emojiGameOutcome#da2ad647 seed:bytes stake_ton_amount:long ton_amount:long = messages.EmojiGameOutcome;

messages.emojiGameUnavailable#59e65335 = messages.EmojiGameInfo;
messages.emojiGameDiceInfo#44e56023 flags:# game_hash:string prev_stake:long current_streak:int params:Vector<int> plays_left:flags.0?int = messages.EmojiGameInfo;

starGiftAttributeRarity#36437737 permille:int = StarGiftAttributeRarity;
starGiftAttributeRarityUncommon#dbce6389 = StarGiftAttributeRarity;
starGiftAttributeRarityRare#f08d516b = StarGiftAttributeRarity;
starGiftAttributeRarityEpic#78fbf3a8 = StarGiftAttributeRarity;
starGiftAttributeRarityLegendary#cef7e7a8 = StarGiftAttributeRarity;

keyboardButtonStyle#4fdd3430 flags:# bg_primary:flags.0?true bg_danger:flags.1?true bg_success:flags.2?true icon:flags.3?long = KeyboardButtonStyle;

inputMessageReadMetric#402b4495 msg_id:int view_id:long time_in_view_ms:int active_time_in_view_ms:int height_to_viewport_ratio_permille:int seen_range_ratio_permille:int = InputMessageReadMetric;

bots.exportedBotToken#3c60b621 token:string = bots.ExportedBotToken;

bots.requestedButton#f13bbcd7 webapp_req_id:string = bots.RequestedButton;

messages.composedMessageWithAI#90d7adfa flags:# result_text:TextWithEntities diff_text:flags.0?TextWithEntities = messages.ComposedMessageWithAI;

stats.pollStats#2999beed votes_graph:StatsGraph = stats.PollStats;

inputAiComposeToneDefault#1fe9a9bf tone:string = InputAiComposeTone;
inputAiComposeToneID#773c080 id:long access_hash:long = InputAiComposeTone;
inputAiComposeToneSlug#1fa01357 slug:string = InputAiComposeTone;
inputAiComposeToneSingleUse#e0c35af custom_prompt:string = InputAiComposeTone;

aiComposeTone#cff63ea9 flags:# creator:flags.0?true id:long access_hash:long slug:string title:string emoji_id:flags.1?long prompt:flags.4?string installs_count:flags.2?int author_id:flags.3?long example_english:flags.5?AiComposeToneExample = AiComposeTone;
aiComposeToneDefault#9bad6414 tone:string emoji_id:long title:string = AiComposeTone;

aicompose.tonesNotModified#c1f46103 = aicompose.Tones;
aicompose.tones#6c9d0efe hash:long tones:Vector<AiComposeTone> users:Vector<User> = aicompose.Tones;

aiComposeToneExample#f1d628ec from:TextWithEntities to:TextWithEntities = AiComposeToneExample;

bots.accessSettings#dd1fbf93 flags:# restricted:flags.0?true add_users:flags.1?Vector<User> = bots.AccessSettings;

messages.chatInviteJoinResultOk#445663a7 updates:Updates = messages.ChatInviteJoinResult;
messages.chatInviteJoinResultWebView#61ca29d3 bot_id:long query_id:long users:Vector<User> = messages.ChatInviteJoinResult;

joinChatBotResultApproved#ae152a69 = JoinChatBotResult;
joinChatBotResultDeclined#efa0194 = JoinChatBotResult;
joinChatBotResultQueued#98a3a840 = JoinChatBotResult;
joinChatBotResultWebView#d6e3b813 url:string = JoinChatBotResult;

webDomainException#933ca597 flags:# domain:string url:string title:string favicon:flags.0?long = WebDomainException;

account.webBrowserSettingsNotModified#c31c8f4e = account.WebBrowserSettings;
account.webBrowserSettings#79eb8cb3 flags:# open_external_browser:flags.0?true display_close_button:flags.1?true external_exceptions:Vector<WebDomainException> inapp_exceptions:Vector<WebDomainException> hash:long = account.WebBrowserSettings;

inputRichFilePhoto#9b00622b id:string photo:InputPhoto = InputRichFile;
inputRichFileDocument#83281dbd id:string document:InputDocument = InputRichFile;

inputRichMessage#e4c449fc flags:# rtl:flags.0?true noautolink:flags.1?true blocks:Vector<PageBlock> photos:flags.2?Vector<InputPhoto> documents:flags.3?Vector<InputDocument> users:flags.4?Vector<InputUser> = InputRichMessage;
inputRichMessageHTML#dacb836a flags:# rtl:flags.0?true noautolink:flags.1?true html:string files:flags.2?Vector<InputRichFile> = InputRichMessage;
inputRichMessageMarkdown#4b572c flags:# rtl:flags.0?true noautolink:flags.1?true markdown:string files:flags.2?Vector<InputRichFile> = InputRichMessage;

richMessage#baf39d8b flags:# rtl:flags.0?true part:flags.1?true blocks:Vector<PageBlock> photos:Vector<Photo> documents:Vector<Document> = RichMessage;

communityPeer#76141ebd flags:# can_view_history:flags.2?true visible:flags.0?Bool peer:Peer = CommunityPeer;

communityPeerRequest#7beafa85 flags:# visible:flags.0?true peer:Peer requested_by:long date:int = CommunityPeerRequest;

communities.peerLinkRequests#2244afad flags:# total_count:int requests:Vector<CommunityPeerRequest> next_offset:flags.0?string chats:Vector<Chat> users:Vector<User> = communities.PeerLinkRequests;

ephemeralMessage#dd27bee9 flags:# out:flags.0?true welcome_template:flags.5?true invert_media:flags.7?true noforwards:flags.12?true id:int from_id:Peer peer_id:flags.9?Peer receiver_id:long top_msg_id:flags.1?int date:int message:string entities:flags.2?Vector<MessageEntity> media:flags.3?MessageMedia reply_markup:flags.4?ReplyMarkup reply_to:flags.6?MessageReplyHeader rich_message:flags.8?RichMessage chat_instance:flags.10?long anchor_msg_id:flags.11?int = EphemeralMessage;

communities.participantJoinedChats#8d78512a creator_chat_ids:Vector<long> joined_chat_ids:Vector<long> chats:Vector<Chat> users:Vector<User> = communities.ParticipantJoinedChats;

messages.translatedRichMessage#4203998f result:Vector<RichMessage> = messages.TranslatedRichMessage;

messages.composedRichMessageWithAI#4c4537c8 result:RichMessage = messages.ComposedRichMessageWithAI;

buttonTypeDefault#c9dd90e9 = ButtonType;
buttonTypeRequestPhone#df3d36f9 = ButtonType;
buttonTypeRequestGeoLocation#9beee140 = ButtonType;
buttonTypeRequestPoll#aacfff84 flags:# quiz:flags.0?Bool = ButtonType;
buttonTypeRequestPeer#4f58a237 flags:# button_id:int peer_type:RequestPeerType max_quantity:int = ButtonType;
inputButtonTypeRequestPeer#3fe268fe flags:# name_requested:flags.0?true username_requested:flags.1?true photo_requested:flags.2?true button_id:int peer_type:RequestPeerType max_quantity:int = ButtonType;
buttonTypeSimpleWebView#c01a597a url:string = ButtonType;

inlineButtonTypeUrl#eca4f8d4 url:string = InlineButtonType;
inlineButtonTypeUrlAuth#bfd02da2 flags:# fwd_text:flags.0?string url:string button_id:int = InlineButtonType;
inputInlineButtonTypeUrlAuth#9961bcb4 flags:# request_write_access:flags.0?true fwd_text:flags.1?string url:string bot:flags.2?InputUser = InlineButtonType;
inlineButtonTypeWebView#3bcab5b4 url:string = InlineButtonType;
inlineButtonTypeCallback#2955bc38 flags:# requires_password:flags.0?true data:bytes = InlineButtonType;
inlineButtonTypeGame#5cd3709d = InlineButtonType;
inlineButtonTypeBuy#48bad7a5 = InlineButtonType;
inlineButtonTypeSwitchInline#93773ff5 flags:# same_peer:flags.0?true query:string peer_types:flags.1?Vector<InlineQueryPeerType> = InlineButtonType;
inlineButtonTypeUserProfile#3fa33fcf user_id:long = InlineButtonType;
inputInlineButtonTypeUserProfile#53f3ce5a user_id:InputUser = InlineButtonType;
inlineButtonTypeCopy#b41d3272 copy_text:string = InlineButtonType;
inlineButtonTypeDisabled#a438619d = InlineButtonType;

keyboardInlineButton#11c1a322 flags:# style:flags.10?KeyboardButtonStyle text:string type:InlineButtonType = KeyboardInlineButton;

keyboardInlineButtonRow#19420af6 buttons:Vector<KeyboardInlineButton> = KeyboardInlineButtonRow;

richButtonStyle#3c610bd flags:# bg_primary:flags.0?true bg_danger:flags.1?true bg_success:flags.2?true link:flags.3?true = RichButtonStyle;

pageButton#692a5488 flags:# text:RichText type:InlineButtonType style:flags.0?RichButtonStyle = PageButton;

ephemeral.welcomeMessagesNotModified#59ffdb31 = ephemeral.WelcomeMessages;
ephemeral.welcomeMessages#104fc872 hash:long messages:Vector<EphemeralMessage> = ephemeral.WelcomeMessages;

currencyRate#30d9743c currency:string rate:double = CurrencyRate;

payments.currencyRates#bbcce4c2 rates:Vector<CurrencyRate> = payments.CurrencyRates;

toncenter.apiResponse#ac8dfe19 response:DataJSON = toncenter.ApiResponse;

toncenter.streamingUrl#19887601 url:string expires:int = toncenter.StreamingUrl;

onrampMethodAvailability#9ec8c6a8 flags:# available:flags.0?true payment_method:string = OnrampMethodAvailability;

onrampProviderInfo#dc27582 flags:# supports_base_currencies:flags.0?true supports_limits:flags.1?true supports_quote:flags.2?true id:string name:string crypto_currencies:Vector<string> = OnrampProviderInfo;

onrampAvailability#f9c84973 flags:# allowed:flags.0?true buy_allowed:flags.1?true country_code:string state:flags.2?string methods:Vector<OnrampMethodAvailability> = OnrampAvailability;

onrampLimits#7be2677e base_currency:string base_min_amount:string base_max_amount:string crypto_min_amount:string crypto_max_amount:string payment_method:string = OnrampLimits;

onrampQuote#7a8011e9 base_currency:string base_amount:string crypto_currency:string crypto_amount:string crypto_price:string fee_amount:string extra_fee_amount:string network_fee_amount:string total_amount:string payment_method:string expires_date:int = OnrampQuote;

onrampSession#d1e42e24 provider:string session_id:string url:string expires_date:int = OnrampSession;

walletStateEmpty#9cb9b2ec flags:# creating:flags.0?true = WalletState;
walletState#95c5346b flags:# backup_enabled:flags.0?true can_export_phrase:flags.1?true can_enable_backup:flags.2?true address:string public_key:bytes balance:long = WalletState;

walletUserAddress#fe78eecc flags:# user_id:flags.0?long address:string public_key:bytes = WalletUserAddress;

wallet.userAddresses#928e7b55 addresses:Vector<WalletUserAddress> users:Vector<User> = wallet.UserAddresses;

walletTransactionPeerUser#d4ea706d flags:# user_id:long address:string domain:flags.0?string = WalletTransactionPeer;
walletTransactionPeerAddress#62cc1bc flags:# address:string domain:flags.0?string = WalletTransactionPeer;
walletTransactionPeerOnramp#e595186e flags:# address:string domain:flags.0?string provider_name:string = WalletTransactionPeer;
walletTransactionPeerUnsupported#728bed5a = WalletTransactionPeer;
walletTransactionPeerChannel#54e70c31 flags:# channel_id:long address:string domain:flags.0?string = WalletTransactionPeer;
walletTransactionPeerTelegramGift#f4729499 flags:# own_profile:flags.0?true = WalletTransactionPeer;

walletTransaction#952dc143 flags:# incoming:flags.0?true gasless:flags.1?true failed:flags.2?true key_change:flags.5?true comment_encrypted:flags.6?true id:string amount:long fee:long date:int peer:WalletTransactionPeer comment:flags.3?string tx_hash:flags.4?string nft:flags.7?wallet.NftItem = WalletTransaction;

wallet.transactions#4322d5a5 flags:# balance:long transactions:Vector<WalletTransaction> next_offset:flags.0?string chats:Vector<Chat> users:Vector<User> = wallet.Transactions;

wallet.secretPhraseParts#e6d0ef01 token:string dcs:Vector<int> = wallet.SecretPhraseParts;

wallet.encryptedSecretPhrasePart#18e82537 data:bytes = wallet.EncryptedSecretPhrasePart;

wallet.holderDc#f9d612ef dc:int public_key:bytes = wallet.HolderDc;

wallet.proofChallenge#99e41707 payload:string expires:int domain:string = wallet.ProofChallenge;

walletOwnershipProof#60bccb0d timestamp:int signature:bytes = WalletOwnershipProof;

inputWalletNew#63a440dc = InputWalletReplacement;
inputWalletImported#66a66a3b flags:# public_key:bytes anchor_public_key:flags.0?bytes proof:WalletOwnershipProof = InputWalletReplacement;

tonConnectManifest#12229264 flags:# url:string name:string icon:flags.0?WebDocument = TonConnectManifest;

tonConnectSession#126556c6 flags:# pending:flags.0?true closing:flags.1?true closed:flags.2?true id:long dapp_client_id:string client_id:flags.3?string nonce:bytes manifest:flags.4?TonConnectManifest manifest_error:flags.5?int date:int = TonConnectSession;

wallet.tonConnectChallenge#4bc89693 challenge:bytes event_id:long = wallet.TonConnectChallenge;

tonConnectNextEventId#582464e3 event_id:long = TonConnectNextEventId;

tonConnectRequest#a15f8513 flags:# session_id:long msg_id:int body:bytes expires:int topic:flags.0?string trace_id:flags.1?string = TonConnectRequest;

wallet.tonConnectPending#85c0f124 session:TonConnectSession requests:Vector<TonConnectRequest> = wallet.TonConnectPending;

wallet.tonConnectSessions#e1f6896 sessions:Vector<TonConnectSession> = wallet.TonConnectSessions;

wallet.existingBalance#bde90a8d flags:# has_balance:flags.0?true url:string = wallet.ExistingBalance;

wallet.nftAttribute#4c1ef10e trait_type:string value:string = wallet.NftAttribute;

wallet.nftItem#173dfaf3 flags:# collection_address:flags.0?string address:string owner_address:string index:string name:flags.1?string description:flags.2?string image:flags.3?WebDocument image_small:flags.4?WebDocument content_url:flags.5?WebDocument lottie:flags.6?WebDocument attributes:flags.7?Vector<wallet.NftAttribute> extra:flags.8?DataJSON gift:flags.9?StarGift = wallet.NftItem;

wallet.nftItems#794d486f flags:# items:Vector<wallet.NftItem> next_offset:flags.0?string = wallet.NftItems;

auth.firebasePnvIntent#df5ac00c nonce:string digital_credential_payload:string = auth.FirebasePnvIntent;

inputTonConnectOauthSession#629f66ce session_id:long challenge_answer:bytes body:bytes proof:WalletOwnershipProof = InputTonConnectOauthSession;

exportedNoPaidMessagesToken#695dd042 flags:# token:string url:string expires:flags.0?int = ExportedNoPaidMessagesToken;

shareThemeDefault#95ace84f = ShareTheme;
shareThemeEmojiBackdrop#64b334e1 flags:# backdrop_id:flags.0?int emojis:flags.1?Vector<Reaction> = ShareTheme;
shareThemeGift#ebece66d stargift:StarGift = ShareTheme;
inputShareThemeGift#3fb92b5b stargift:InputSavedStarGift = ShareTheme;

shareThemeBackdropOption#45c1d652 backdrop_id:int name:string center_color:int edge_color:int pattern_color:int text_color:int = ShareThemeBackdropOption;

shareThemeBackdropsNotModified#7a3f6dff = ShareThemeBackdrops;
shareThemeBackdrops#aaeadce0 hash:int backdrops:Vector<ShareThemeBackdropOption> = ShareThemeBackdrops;

inputWalletCardSticker#57df45a7 coordinates:MediaAreaCoordinates nft_address:string = InputWalletCardSticker;

wallet.cardSticker#f3010cba coordinates:MediaAreaCoordinates nft:wallet.NftItem = wallet.CardSticker;

wallet.card#8ed043b1 flags:# stickers:flags.0?Vector<wallet.CardSticker> bg_nft_item:flags.1?wallet.NftItem address:string balance:long = wallet.Card;

payments.starGiftDepositPayload#725bb4eb nft_address:string payload:bytes amount:long = payments.StarGiftDepositPayload;

---functions---

invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
invokeAfterMsgs#3dc4b4f0 {X:Type} msg_ids:Vector<long> query:!X = X;
initConnection#c1cd5ea9 {X:Type} flags:# api_id:int device_model:string system_version:string app_version:string system_lang_code:string lang_pack:string lang_code:string proxy:flags.0?InputClientProxy params:flags.1?JSONValue query:!X = X;
invokeWithLayer#da9b0d0d {X:Type} layer:int query:!X = X;
invokeWithoutUpdates#bf9459b7 {X:Type} query:!X = X;
invokeWithMessagesRange#365275f2 {X:Type} range:MessageRange query:!X = X;
invokeWithTakeout#aca9fd2e {X:Type} takeout_id:long query:!X = X;
invokeWithBusinessConnection#dd289f8e {X:Type} connection_id:string query:!X = X;
invokeWithGooglePlayIntegrity#1df92984 {X:Type} nonce:string token:string query:!X = X;
invokeWithApnsSecret#0dae54f8 {X:Type} nonce:string secret:string query:!X = X;
invokeWithReCaptcha#adbb0f94 {X:Type} token:string query:!X = X;

auth.sendCode#a677244f phone_number:string api_id:int api_hash:string settings:CodeSettings = auth.SentCode;
auth.signUp#aac7b717 flags:# no_joined_notifications:flags.0?true phone_number:string phone_code_hash:string first_name:string last_name:string = auth.Authorization;
auth.signIn#8d52a951 flags:# phone_number:string phone_code_hash:string phone_code:flags.0?string email_verification:flags.1?EmailVerification = auth.Authorization;
auth.logOut#3e72ba19 = auth.LoggedOut;
auth.resetAuthorizations#9fab0d1a = Bool;
auth.exportAuthorization#e5bfffcd dc_id:int = auth.ExportedAuthorization;
auth.importAuthorization#a57a7dad id:long bytes:bytes = auth.Authorization;
auth.bindTempAuthKey#cdd42a05 perm_auth_key_id:long nonce:long expires_at:int encrypted_message:bytes = Bool;
auth.importBotAuthorization#67a3ff2c flags:int api_id:int api_hash:string bot_auth_token:string = auth.Authorization;
auth.checkPassword#d18b4d16 password:InputCheckPasswordSRP = auth.Authorization;
auth.requestPasswordRecovery#d897bc66 = auth.PasswordRecovery;
auth.recoverPassword#37096c70 flags:# code:string new_settings:flags.0?account.PasswordInputSettings = auth.Authorization;
auth.resendCode#cae47523 flags:# phone_number:string phone_code_hash:string reason:flags.0?string = auth.SentCode;
auth.cancelCode#1f040578 phone_number:string phone_code_hash:string = Bool;
auth.dropTempAuthKeys#8e48a188 except_auth_keys:Vector<long> = Bool;
auth.exportLoginToken#b7e085fe api_id:int api_hash:string except_ids:Vector<long> = auth.LoginToken;
auth.importLoginToken#95ac5ce4 token:bytes = auth.LoginToken;
auth.acceptLoginToken#e894ad4d token:bytes = Authorization;
auth.checkRecoveryPassword#d36bf79 code:string = Bool;
auth.importWebTokenAuthorization#2db873a9 api_id:int api_hash:string web_auth_token:string = auth.Authorization;
auth.requestFirebaseSms#8e39261e flags:# phone_number:string phone_code_hash:string safety_net_token:flags.0?string play_integrity_token:flags.2?string ios_push_secret:flags.1?string = Bool;
auth.resetLoginEmail#7e960193 phone_number:string phone_code_hash:string = auth.SentCode;
auth.reportMissingCode#cb9deff6 phone_number:string phone_code_hash:string mnc:string = Bool;
auth.checkPaidAuth#56e59f9c phone_number:string phone_code_hash:string form_id:long = auth.SentCode;
auth.initPasskeyLogin#518ad0b7 api_id:int api_hash:string = auth.PasskeyLoginOptions;
auth.finishPasskeyLogin#9857ad07 flags:# credential:InputPasskeyCredential from_dc_id:flags.0?int from_auth_key_id:flags.0?long = auth.Authorization;
auth.cancelWebTokenAuthorization#490dada1 web_auth_token:string = Bool;
auth.initFirebasePnvLogin#644f15c0 flags:# except_ids:flags.0?Vector<long> api_id:int api_hash:string = auth.FirebasePnvIntent;
auth.finishFirebasePnvLogin#2c85094c google_token:string = auth.Authorization;
auth.firebasePnvSignUp#783f6b56 flags:# no_joined_notifications:flags.0?true first_name:string last_name:string = auth.Authorization;

account.registerDevice#ec86017a flags:# no_muted:flags.0?true token_type:int token:string app_sandbox:Bool secret:bytes other_uids:Vector<long> = Bool;
account.unregisterDevice#6a0d3206 token_type:int token:string other_uids:Vector<long> = Bool;
account.updateNotifySettings#84be5b93 peer:InputNotifyPeer settings:InputPeerNotifySettings = Bool;
account.getNotifySettings#12b3ad31 peer:InputNotifyPeer = PeerNotifySettings;
account.resetNotifySettings#db7e1747 = Bool;
account.updateProfile#78515775 flags:# first_name:flags.0?string last_name:flags.1?string about:flags.2?string = User;
account.updateStatus#6628562c offline:Bool = Bool;
account.getWallPapers#7967d36 hash:long = account.WallPapers;
account.reportPeer#c5ba3d86 peer:InputPeer reason:ReportReason message:string = Bool;
account.checkUsername#2714d86c username:string = Bool;
account.updateUsername#3e0bdd7c username:string = User;
account.getPrivacy#dadbc950 key:InputPrivacyKey = account.PrivacyRules;
account.setPrivacy#c9f81ce8 key:InputPrivacyKey rules:Vector<InputPrivacyRule> = account.PrivacyRules;
account.deleteAccount#a2c0cf74 flags:# reason:string password:flags.0?InputCheckPasswordSRP = Bool;
account.getAccountTTL#8fc711d = AccountDaysTTL;
account.setAccountTTL#2442485e ttl:AccountDaysTTL = Bool;
account.sendChangePhoneCode#82574ae5 phone_number:string settings:CodeSettings = auth.SentCode;
account.changePhone#70c32edb phone_number:string phone_code_hash:string phone_code:string = User;
account.updateDeviceLocked#38df3532 period:int = Bool;
account.getAuthorizations#e320c158 = account.Authorizations;
account.resetAuthorization#df77f3bc hash:long = Bool;
account.getPassword#548a30f5 = account.Password;
account.getPasswordSettings#9cd4eaf9 password:InputCheckPasswordSRP = account.PasswordSettings;
account.updatePasswordSettings#a59b102f password:InputCheckPasswordSRP new_settings:account.PasswordInputSettings = Bool;
account.sendConfirmPhoneCode#1b3faa88 hash:string settings:CodeSettings = auth.SentCode;
account.confirmPhone#5f2178c3 phone_code_hash:string phone_code:string = Bool;
account.getTmpPassword#449e0b51 password:InputCheckPasswordSRP period:int = account.TmpPassword;
account.getWebAuthorizations#182e6d6f = account.WebAuthorizations;
account.resetWebAuthorization#2d01b9ef hash:long = Bool;
account.resetWebAuthorizations#682d2594 = Bool;
account.getAllSecureValues#b288bc7d = Vector<SecureValue>;
account.getSecureValue#73665bc2 types:Vector<SecureValueType> = Vector<SecureValue>;
account.saveSecureValue#899fe31d value:InputSecureValue secure_secret_id:long = SecureValue;
account.deleteSecureValue#b880bc4b types:Vector<SecureValueType> = Bool;
account.getAuthorizationForm#a929597a bot_id:long scope:string public_key:string = account.AuthorizationForm;
account.acceptAuthorization#f3ed4c73 bot_id:long scope:string public_key:string value_hashes:Vector<SecureValueHash> credentials:SecureCredentialsEncrypted = Bool;
account.sendVerifyPhoneCode#a5a356f9 phone_number:string settings:CodeSettings = auth.SentCode;
account.verifyPhone#4dd3a7f6 phone_number:string phone_code_hash:string phone_code:string = Bool;
account.sendVerifyEmailCode#98e037bb purpose:EmailVerifyPurpose email:string = account.SentEmailCode;
account.verifyEmail#32da4cf purpose:EmailVerifyPurpose verification:EmailVerification = account.EmailVerified;
account.initTakeoutSession#8ef3eab0 flags:# contacts:flags.0?true message_users:flags.1?true message_chats:flags.2?true message_megagroups:flags.3?true message_channels:flags.4?true files:flags.5?true file_max_size:flags.5?long = account.Takeout;
account.finishTakeoutSession#1d2652ee flags:# success:flags.0?true = Bool;
account.confirmPasswordEmail#8fdf1920 code:string = Bool;
account.resendPasswordEmail#7a7f2a15 = Bool;
account.cancelPasswordEmail#c1cbd5b6 = Bool;
account.getContactSignUpNotification#9f07c728 = Bool;
account.setContactSignUpNotification#cff43f61 silent:Bool = Bool;
account.getNotifyExceptions#53577479 flags:# compare_sound:flags.1?true compare_stories:flags.2?true peer:flags.0?InputNotifyPeer = Updates;
account.getWallPaper#fc8ddbea wallpaper:InputWallPaper = WallPaper;
account.uploadWallPaper#e39a8f03 flags:# for_chat:flags.0?true file:InputFile mime_type:string settings:WallPaperSettings = WallPaper;
account.saveWallPaper#6c5a5b37 wallpaper:InputWallPaper unsave:Bool settings:WallPaperSettings = Bool;
account.installWallPaper#feed5769 wallpaper:InputWallPaper settings:WallPaperSettings = Bool;
account.resetWallPapers#bb3b9804 = Bool;
account.getAutoDownloadSettings#56da0b3f = account.AutoDownloadSettings;
account.saveAutoDownloadSettings#76f36233 flags:# low:flags.0?true high:flags.1?true settings:AutoDownloadSettings = Bool;
account.uploadTheme#1c3db333 flags:# file:InputFile thumb:flags.0?InputFile file_name:string mime_type:string = Document;
account.createTheme#652e4400 flags:# slug:string title:string document:flags.2?InputDocument settings:flags.3?Vector<InputThemeSettings> = Theme;
account.updateTheme#2bf40ccc flags:# format:string theme:InputTheme slug:flags.0?string title:flags.1?string document:flags.2?InputDocument settings:flags.3?Vector<InputThemeSettings> = Theme;
account.saveTheme#f257106c theme:InputTheme unsave:Bool = Bool;
account.installTheme#c727bb3b flags:# dark:flags.0?true theme:flags.1?InputTheme format:flags.2?string base_theme:flags.3?BaseTheme = Bool;
account.getTheme#3a5869ec format:string theme:InputTheme = Theme;
account.getThemes#7206e458 format:string hash:long = account.Themes;
account.setContentSettings#b574b16b flags:# sensitive_enabled:flags.0?true = Bool;
account.getContentSettings#8b9b4dae = account.ContentSettings;
account.getMultiWallPapers#65ad71dc wallpapers:Vector<InputWallPaper> = Vector<WallPaper>;
account.getGlobalPrivacySettings#eb2b4cf6 = GlobalPrivacySettings;
account.setGlobalPrivacySettings#1edaaac2 settings:GlobalPrivacySettings = GlobalPrivacySettings;
account.reportProfilePhoto#fa8cc6f5 peer:InputPeer photo_id:InputPhoto reason:ReportReason message:string = Bool;
account.resetPassword#9308ce1b = account.ResetPasswordResult;
account.declinePasswordReset#4c9409f6 = Bool;
account.getChatThemes#d638de89 hash:long = account.Themes;
account.setAuthorizationTTL#bf899aa0 authorization_ttl_days:int = Bool;
account.changeAuthorizationSettings#40f48462 flags:# confirmed:flags.3?true hash:long encrypted_requests_disabled:flags.0?Bool call_requests_disabled:flags.1?Bool = Bool;
account.getSavedRingtones#e1902288 hash:long = account.SavedRingtones;
account.saveRingtone#3dea5b03 id:InputDocument unsave:Bool = account.SavedRingtone;
account.uploadRingtone#831a83a2 file:InputFile file_name:string mime_type:string = Document;
account.updateEmojiStatus#fbd3de6b emoji_status:EmojiStatus = Bool;
account.getDefaultEmojiStatuses#d6753386 hash:long = account.EmojiStatuses;
account.getRecentEmojiStatuses#f578105 hash:long = account.EmojiStatuses;
account.clearRecentEmojiStatuses#18201aae = Bool;
account.reorderUsernames#ef500eab order:Vector<string> = Bool;
account.toggleUsername#58d6b376 username:string active:Bool = Bool;
account.getDefaultProfilePhotoEmojis#e2750328 hash:long = EmojiList;
account.getDefaultGroupPhotoEmojis#915860ae hash:long = EmojiList;
account.getAutoSaveSettings#adcbbcda = account.AutoSaveSettings;
account.saveAutoSaveSettings#d69b8361 flags:# users:flags.0?true chats:flags.1?true broadcasts:flags.2?true peer:flags.3?InputPeer settings:AutoSaveSettings = Bool;
account.deleteAutoSaveExceptions#53bc0020 = Bool;
account.invalidateSignInCodes#ca8ae8ba codes:Vector<string> = Bool;
account.updateColor#684d214e flags:# for_profile:flags.1?true color:flags.2?PeerColor = Bool;
account.getDefaultBackgroundEmojis#a60ab9ce hash:long = EmojiList;
account.getChannelDefaultEmojiStatuses#7727a7d5 hash:long = account.EmojiStatuses;
account.getChannelRestrictedStatusEmojis#35a9e0d5 hash:long = EmojiList;
account.updateBusinessWorkHours#4b00e066 flags:# business_work_hours:flags.0?BusinessWorkHours = Bool;
account.updateBusinessLocation#9e6b131a flags:# geo_point:flags.1?InputGeoPoint address:flags.0?string = Bool;
account.updateBusinessGreetingMessage#66cdafc4 flags:# message:flags.0?InputBusinessGreetingMessage = Bool;
account.updateBusinessAwayMessage#a26a7fa5 flags:# message:flags.0?InputBusinessAwayMessage = Bool;
account.updateConnectedBot#66a08c7e flags:# deleted:flags.1?true rights:flags.0?BusinessBotRights bot:InputUser recipients:InputBusinessBotRecipients = Updates;
account.getConnectedBots#4ea4c80f = account.ConnectedBots;
account.getBotBusinessConnection#76a86270 connection_id:string = Updates;
account.updateBusinessIntro#a614d034 flags:# intro:flags.0?InputBusinessIntro = Bool;
account.toggleConnectedBotPaused#646e1097 peer:InputPeer paused:Bool = Bool;
account.disablePeerConnectedBot#5e437ed9 peer:InputPeer = Bool;
account.updateBirthday#cc6e0c11 flags:# birthday:flags.0?Birthday = Bool;
account.createBusinessChatLink#8851e68e link:InputBusinessChatLink = BusinessChatLink;
account.editBusinessChatLink#8c3410af slug:string link:InputBusinessChatLink = BusinessChatLink;
account.deleteBusinessChatLink#60073674 slug:string = Bool;
account.getBusinessChatLinks#6f70dde1 = account.BusinessChatLinks;
account.resolveBusinessChatLink#5492e5ee slug:string = account.ResolvedBusinessChatLinks;
account.updatePersonalChannel#d94305e0 channel:InputChannel = Bool;
account.toggleSponsoredMessages#b9d9a38d enabled:Bool = Bool;
account.getReactionsNotifySettings#6dd654c = ReactionsNotifySettings;
account.setReactionsNotifySettings#316ce548 settings:ReactionsNotifySettings = ReactionsNotifySettings;
account.getCollectibleEmojiStatuses#2e7b4543 hash:long = account.EmojiStatuses;
account.getPaidMessagesRevenue#19ba4a67 flags:# parent_peer:flags.0?InputPeer user_id:InputUser = account.PaidMessagesRevenue;
account.toggleNoPaidMessagesException#fe2eda76 flags:# refund_charged:flags.0?true require_payment:flags.2?true parent_peer:flags.1?InputPeer user_id:InputUser = Bool;
account.setMainProfileTab#5dee78b0 tab:ProfileTab = Bool;
account.saveMusic#b26732a9 flags:# unsave:flags.0?true id:InputDocument after_id:flags.1?InputDocument = Bool;
account.getSavedMusicIds#e09d5faf hash:long = account.SavedMusicIds;
account.getUniqueGiftChatThemes#e42ce9c9 offset:string limit:int hash:long = account.ChatThemes;
account.initPasskeyRegistration#429547e8 = account.PasskeyRegistrationOptions;
account.registerPasskey#55b41fd6 credential:InputPasskeyCredential = Passkey;
account.getPasskeys#ea1f0c52 = account.Passkeys;
account.deletePasskey#f5b5563f id:string = Bool;
account.confirmBotConnection#67ed1f68 bot_id:InputUser = Bool;
account.getWebBrowserSettings#56655768 hash:long = account.WebBrowserSettings;
account.updateWebBrowserSettings#9adf82fe flags:# open_external_browser:flags.0?true display_close_button:flags.1?true = account.WebBrowserSettings;
account.toggleWebBrowserSettingsException#60ed4229 flags:# delete:flags.1?true open_external_browser:flags.0?Bool url:string = Updates;
account.deleteWebBrowserSettingsExceptions#86a0765d = account.WebBrowserSettings;

users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
users.getFullUser#b60f5918 id:InputUser = users.UserFull;
users.setSecureValueErrors#90c894b5 id:InputUser errors:Vector<SecureValueError> = Bool;
users.getRequirementsToContact#d89a83a3 id:Vector<InputUser> = Vector<RequirementToContact>;
users.getSavedMusic#788d7fe3 id:InputUser offset:int limit:int hash:long = users.SavedMusic;
users.getSavedMusicByID#7573a4e9 id:InputUser documents:Vector<InputDocument> = users.SavedMusic;
users.suggestBirthday#fc533372 id:InputUser birthday:Birthday = Updates;

contacts.getContactIDs#7adc669d hash:long = Vector<int>;
contacts.getStatuses#c4a353ee = Vector<ContactStatus>;
contacts.getContacts#5dd69e12 hash:long = contacts.Contacts;
contacts.importContacts#2c800be5 contacts:Vector<InputContact> = contacts.ImportedContacts;
contacts.deleteContacts#96a0e00 id:Vector<InputUser> = Updates;
contacts.deleteByPhones#1013fd9e phones:Vector<string> = Bool;
contacts.block#2e2e8734 flags:# my_stories_from:flags.0?true id:InputPeer = Bool;
contacts.unblock#b550d328 flags:# my_stories_from:flags.0?true id:InputPeer = Bool;
contacts.getBlocked#9a868f80 flags:# my_stories_from:flags.0?true offset:int limit:int = contacts.Blocked;
contacts.search#5f58d0f flags:# broadcasts:flags.0?true bots:flags.1?true q:string limit:int = contacts.Found;
contacts.resolveUsername#725afbbc flags:# username:string referer:flags.0?string = contacts.ResolvedPeer;
contacts.getTopPeers#973478b6 flags:# correspondents:flags.0?true bots_pm:flags.1?true bots_inline:flags.2?true phone_calls:flags.3?true forward_users:flags.4?true forward_chats:flags.5?true groups:flags.10?true channels:flags.15?true bots_app:flags.16?true bots_guestchat:flags.17?true offset:int limit:int hash:long = contacts.TopPeers;
contacts.resetTopPeerRating#1ae373ac category:TopPeerCategory peer:InputPeer = Bool;
contacts.resetSaved#879537f1 = Bool;
contacts.getSaved#82f1e39f = Vector<SavedContact>;
contacts.toggleTopPeers#8514bdda enabled:Bool = Bool;
contacts.addContact#d9ba2e54 flags:# add_phone_privacy_exception:flags.0?true id:InputUser first_name:string last_name:string phone:string note:flags.1?TextWithEntities = Updates;
contacts.acceptContact#f831a20f id:InputUser = Updates;
contacts.getLocated#d348bc44 flags:# background:flags.1?true geo_point:InputGeoPoint self_expires:flags.0?int = Updates;
contacts.blockFromReplies#29a8962c flags:# delete_message:flags.0?true delete_history:flags.1?true report_spam:flags.2?true msg_id:int = Updates;
contacts.resolvePhone#8af94344 phone:string = contacts.ResolvedPeer;
contacts.exportContactToken#f8654027 = ExportedContactToken;
contacts.importContactToken#13005788 token:string = User;
contacts.editCloseFriends#ba6705f0 id:Vector<long> = Bool;
contacts.setBlocked#94c65c76 flags:# my_stories_from:flags.0?true id:Vector<InputPeer> limit:int = Bool;
contacts.getBirthdays#daeda864 = contacts.ContactBirthdays;
contacts.getSponsoredPeers#b6c8c393 q:string = contacts.SponsoredPeers;
contacts.updateContactNote#139f63fb id:InputUser note:TextWithEntities = Bool;
contacts.exportNoPaidMessagesToken#83c586ae flags:# revoke:flags.0?true temporary:flags.1?true = ExportedNoPaidMessagesToken;
contacts.importNoPaidMessagesToken#35a7c6ca user_id:InputUser token:string = User;

messages.getMessages#63c66506 id:Vector<InputMessage> = messages.Messages;
messages.getDialogs#a0f4cb4f flags:# exclude_pinned:flags.0?true folder_id:flags.1?int offset_date:int offset_id:int offset_peer:InputPeer limit:int hash:long = messages.Dialogs;
messages.getHistory#4423e6c5 peer:InputPeer offset_id:int offset_date:int add_offset:int limit:int max_id:int min_id:int hash:long = messages.Messages;
messages.search#29ee847a flags:# peer:InputPeer q:string from_id:flags.0?InputPeer saved_peer_id:flags.2?InputPeer saved_reaction:flags.3?Vector<Reaction> top_msg_id:flags.1?int filter:MessagesFilter min_date:int max_date:int offset_id:int add_offset:int limit:int max_id:int min_id:int hash:long = messages.Messages;
messages.readHistory#e306d3a peer:InputPeer max_id:int = messages.AffectedMessages;
messages.deleteHistory#b08f922a flags:# just_clear:flags.0?true revoke:flags.1?true peer:InputPeer max_id:int min_date:flags.2?int max_date:flags.3?int = messages.AffectedHistory;
messages.deleteMessages#e58e95d2 flags:# revoke:flags.0?true id:Vector<int> = messages.AffectedMessages;
messages.receivedMessages#5a954c0 max_id:int = Vector<ReceivedNotifyMessage>;
messages.setTyping#58943ee2 flags:# peer:InputPeer top_msg_id:flags.0?int action:SendMessageAction = Bool;
messages.sendMessage#fef48f62 flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true invert_media:flags.16?true allow_paid_floodskip:flags.19?true peer:InputPeer reply_to:flags.0?InputReplyTo message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int schedule_repeat_period:flags.24?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut effect:flags.18?long allow_paid_stars:flags.21?long suggested_post:flags.22?SuggestedPost rich_message:flags.23?InputRichMessage = Updates;
messages.sendMedia#330e77f flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true invert_media:flags.16?true allow_paid_floodskip:flags.19?true peer:InputPeer reply_to:flags.0?InputReplyTo media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int schedule_repeat_period:flags.24?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut effect:flags.18?long allow_paid_stars:flags.21?long suggested_post:flags.22?SuggestedPost = Updates;
messages.forwardMessages#13704a7c flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true drop_author:flags.11?true drop_media_captions:flags.12?true noforwards:flags.14?true allow_paid_floodskip:flags.19?true from_ephemeral:flags.25?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer top_msg_id:flags.9?int reply_to:flags.22?InputReplyTo schedule_date:flags.10?int schedule_repeat_period:flags.24?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut effect:flags.18?long video_timestamp:flags.20?int allow_paid_stars:flags.21?long suggested_post:flags.23?SuggestedPost = Updates;
messages.reportSpam#cf1592db peer:InputPeer = Bool;
messages.getPeerSettings#efd9a6a2 peer:InputPeer = messages.PeerSettings;
messages.report#fc78af9b peer:InputPeer id:Vector<int> option:bytes message:string = ReportResult;
messages.getChats#49e9528f id:Vector<long> = messages.Chats;
messages.getFullChat#aeb00b34 chat_id:long = messages.ChatFull;
messages.editChatTitle#73783ffd chat_id:long title:string = Updates;
messages.editChatPhoto#35ddd674 chat_id:long photo:InputChatPhoto = Updates;
messages.addChatUser#cbc6d107 chat_id:long user_id:InputUser fwd_limit:int = messages.InvitedUsers;
messages.deleteChatUser#a2185cab flags:# revoke_history:flags.0?true chat_id:long user_id:InputUser = Updates;
messages.createChat#92ceddd4 flags:# users:Vector<InputUser> title:string ttl_period:flags.0?int = messages.InvitedUsers;
messages.getDhConfig#26cf8950 version:int random_length:int = messages.DhConfig;
messages.requestEncryption#f64daf43 user_id:InputUser random_id:int g_a:bytes = EncryptedChat;
messages.acceptEncryption#3dbc0415 peer:InputEncryptedChat g_b:bytes key_fingerprint:long = EncryptedChat;
messages.discardEncryption#f393aea0 flags:# delete_history:flags.0?true chat_id:int = Bool;
messages.setEncryptedTyping#791451ed peer:InputEncryptedChat typing:Bool = Bool;
messages.readEncryptedHistory#7f4b690a peer:InputEncryptedChat max_date:int = Bool;
messages.sendEncrypted#44fa7a15 flags:# silent:flags.0?true peer:InputEncryptedChat random_id:long data:bytes = messages.SentEncryptedMessage;
messages.sendEncryptedFile#5559481d flags:# silent:flags.0?true peer:InputEncryptedChat random_id:long data:bytes file:InputEncryptedFile = messages.SentEncryptedMessage;
messages.sendEncryptedService#32d439a4 peer:InputEncryptedChat random_id:long data:bytes = messages.SentEncryptedMessage;
messages.receivedQueue#55a5bb66 max_qts:int = Vector<long>;
messages.reportEncryptedSpam#4b0c8c0f peer:InputEncryptedChat = Bool;
messages.readMessageContents#36a73f77 id:Vector<int> = messages.AffectedMessages;
messages.getStickers#d5a5d3a1 emoticon:string hash:long = messages.Stickers;
messages.getAllStickers#b8a0a1a8 hash:long = messages.AllStickers;
messages.getWebPagePreview#570d6f6f flags:# message:string entities:flags.3?Vector<MessageEntity> = messages.WebPagePreview;
messages.exportChatInvite#a455de90 flags:# legacy_revoke_permanent:flags.2?true request_needed:flags.3?true peer:InputPeer expire_date:flags.0?int usage_limit:flags.1?int title:flags.4?string subscription_pricing:flags.5?StarsSubscriptionPricing = ExportedChatInvite;
messages.checkChatInvite#3eadb1bb hash:string = ChatInvite;
messages.importChatInvite#de91436e hash:string = messages.ChatInviteJoinResult;
messages.getStickerSet#c8a0ec74 stickerset:InputStickerSet hash:int = messages.StickerSet;
messages.installStickerSet#c78fe460 stickerset:InputStickerSet archived:Bool = messages.StickerSetInstallResult;
messages.uninstallStickerSet#f96e55de stickerset:InputStickerSet = Bool;
messages.startBot#e6df7378 bot:InputUser peer:InputPeer random_id:long start_param:string = Updates;
messages.getMessagesViews#5784d3e1 peer:InputPeer id:Vector<int> increment:Bool = messages.MessageViews;
messages.editChatAdmin#a85bd1c2 chat_id:long user_id:InputUser is_admin:Bool = Bool;
messages.migrateChat#a2875319 chat_id:long = Updates;
messages.searchGlobal#6126a43c flags:# broadcasts_only:flags.1?true groups_only:flags.2?true users_only:flags.3?true folder_id:flags.0?int community:flags.4?InputChannel q:string filter:MessagesFilter min_date:int max_date:int offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages;
messages.reorderStickerSets#78337739 flags:# masks:flags.0?true emojis:flags.1?true order:Vector<long> = Bool;
messages.getDocumentByHash#b1f2061f sha256:bytes size:long mime_type:string = Document;
messages.getSavedGifs#5cf09635 hash:long = messages.SavedGifs;
messages.saveGif#327a30cb id:InputDocument unsave:Bool = Bool;
messages.getInlineBotResults#514e999d flags:# bot:InputUser peer:InputPeer geo_point:flags.0?InputGeoPoint query:string offset:string = messages.BotResults;
messages.setInlineBotResults#bb12a419 flags:# gallery:flags.0?true private:flags.1?true query_id:long results:Vector<InputBotInlineResult> cache_time:int next_offset:flags.2?string switch_pm:flags.3?InlineBotSwitchPM switch_webview:flags.4?InlineBotWebView = Bool;
messages.sendInlineBotResult#c0cf7646 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true hide_via:flags.11?true peer:InputPeer reply_to:flags.0?InputReplyTo random_id:long query_id:long id:string schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut allow_paid_stars:flags.21?long = Updates;
messages.getMessageEditData#fda68d36 peer:InputPeer id:int = messages.MessageEditData;
messages.editMessage#b106e66c flags:# no_webpage:flags.1?true invert_media:flags.16?true peer:InputPeer id:int message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.15?int schedule_repeat_period:flags.18?int quick_reply_shortcut_id:flags.17?int rich_message:flags.23?InputRichMessage = Updates;
messages.editInlineBotMessage#a423bb51 flags:# no_webpage:flags.1?true invert_media:flags.16?true id:InputBotInlineMessageID message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> rich_message:flags.23?InputRichMessage = Bool;
messages.getBotCallbackAnswer#9342ca07 flags:# game:flags.1?true peer:InputPeer msg_id:int data:flags.0?bytes password:flags.2?InputCheckPasswordSRP = messages.BotCallbackAnswer;
messages.setBotCallbackAnswer#d58f130a flags:# alert:flags.1?true query_id:long message:flags.0?string url:flags.2?string cache_time:int = Bool;
messages.getPeerDialogs#e470bcfd peers:Vector<InputDialogPeer> = messages.PeerDialogs;
messages.saveDraft#ad0fa15c flags:# no_webpage:flags.1?true invert_media:flags.6?true reply_to:flags.4?InputReplyTo peer:InputPeer message:string entities:flags.3?Vector<MessageEntity> media:flags.5?InputMedia effect:flags.7?long suggested_post:flags.8?SuggestedPost rich_message:flags.9?InputRichMessage = Bool;
messages.getAllDrafts#6a3f8d65 = Updates;
messages.getFeaturedStickers#64780b14 hash:long = messages.FeaturedStickers;
messages.readFeaturedStickers#5b118126 id:Vector<long> = Bool;
messages.getRecentStickers#9da9403b flags:# attached:flags.0?true hash:long = messages.RecentStickers;
messages.saveRecentSticker#392718f8 flags:# attached:flags.0?true id:InputDocument unsave:Bool = Bool;
messages.clearRecentStickers#8999602d flags:# attached:flags.0?true = Bool;
messages.getArchivedStickers#57f17692 flags:# masks:flags.0?true emojis:flags.1?true offset_id:long limit:int = messages.ArchivedStickers;
messages.getMaskStickers#640f82b8 hash:long = messages.AllStickers;
messages.getAttachedStickers#cc5b67cc media:InputStickeredMedia = Vector<StickerSetCovered>;
messages.setGameScore#8ef8ecc0 flags:# edit_message:flags.0?true force:flags.1?true peer:InputPeer id:int user_id:InputUser score:int = Updates;
messages.setInlineGameScore#15ad9f64 flags:# edit_message:flags.0?true force:flags.1?true id:InputBotInlineMessageID user_id:InputUser score:int = Bool;
messages.getGameHighScores#e822649d peer:InputPeer id:int user_id:InputUser = messages.HighScores;
messages.getInlineGameHighScores#f635e1b id:InputBotInlineMessageID user_id:InputUser = messages.HighScores;
messages.getCommonChats#e40ca104 user_id:InputUser max_id:long limit:int = messages.Chats;
messages.getWebPage#8d9692a3 url:string hash:int = messages.WebPage;
messages.toggleDialogPin#a731e257 flags:# pinned:flags.0?true peer:InputDialogPeer = Bool;
messages.reorderPinnedDialogs#3b1adf37 flags:# force:flags.0?true folder_id:int order:Vector<InputDialogPeer> = Bool;
messages.getPinnedDialogs#d6b94df2 folder_id:int = messages.PeerDialogs;
messages.setBotShippingResults#e5f672fa flags:# query_id:long error:flags.0?string shipping_options:flags.1?Vector<ShippingOption> = Bool;
messages.setBotPrecheckoutResults#9c2dd95 flags:# success:flags.1?true query_id:long error:flags.0?string = Bool;
messages.uploadMedia#14967978 flags:# business_connection_id:flags.0?string peer:InputPeer media:InputMedia = MessageMedia;
messages.sendScreenshotNotification#a1405817 peer:InputPeer reply_to:InputReplyTo random_id:long = Updates;
messages.getFavedStickers#4f1aaa9 hash:long = messages.FavedStickers;
messages.faveSticker#b9ffc55b id:InputDocument unfave:Bool = Bool;
messages.getUnreadMentions#f107e790 flags:# peer:InputPeer top_msg_id:flags.0?int offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
messages.readMentions#36e5bf4d flags:# peer:InputPeer top_msg_id:flags.0?int = messages.AffectedHistory;
messages.getRecentLocations#702a40e0 peer:InputPeer limit:int hash:long = messages.Messages;
messages.sendMultiMedia#1bf89d74 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true invert_media:flags.16?true allow_paid_floodskip:flags.19?true peer:InputPeer reply_to:flags.0?InputReplyTo multi_media:Vector<InputSingleMedia> schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut effect:flags.18?long allow_paid_stars:flags.21?long = Updates;
messages.uploadEncryptedFile#5057c497 peer:InputEncryptedChat file:InputEncryptedFile = EncryptedFile;
messages.searchStickerSets#35705b8a flags:# exclude_featured:flags.0?true q:string hash:long = messages.FoundStickerSets;
messages.getSplitRanges#1cff7e08 = Vector<MessageRange>;
messages.markDialogUnread#8c5006f8 flags:# unread:flags.0?true parent_peer:flags.1?InputPeer peer:InputDialogPeer = Bool;
messages.getDialogUnreadMarks#21202222 flags:# parent_peer:flags.0?InputPeer = Vector<DialogPeer>;
messages.clearAllDrafts#7e58ee9c = Bool;
messages.updatePinnedMessage#d2aaf7ec flags:# silent:flags.0?true unpin:flags.1?true pm_oneside:flags.2?true peer:InputPeer id:int = Updates;
messages.sendVote#10ea6184 peer:InputPeer msg_id:int options:Vector<bytes> = Updates;
messages.getPollResults#eda3e33b peer:InputPeer msg_id:int poll_hash:long = Updates;
messages.getOnlines#6e2be050 peer:InputPeer = ChatOnlines;
messages.editChatAbout#def60797 peer:InputPeer about:string = Bool;
messages.editChatDefaultBannedRights#a5866b41 peer:InputPeer banned_rights:ChatBannedRights = Updates;
messages.getEmojiKeywords#35a0e062 lang_code:string = EmojiKeywordsDifference;
messages.getEmojiKeywordsDifference#1508b6af lang_code:string from_version:int = EmojiKeywordsDifference;
messages.getEmojiKeywordsLanguages#4e9963b2 lang_codes:Vector<string> = Vector<EmojiLanguage>;
messages.getEmojiURL#d5b10c26 lang_code:string = EmojiURL;
messages.getSearchCounters#1bbcf300 flags:# peer:InputPeer saved_peer_id:flags.2?InputPeer top_msg_id:flags.0?int filters:Vector<MessagesFilter> = Vector<messages.SearchCounter>;
messages.requestUrlAuth#894cc99c flags:# peer:flags.1?InputPeer msg_id:flags.1?int button_id:flags.1?int url:flags.2?string in_app_origin:flags.3?string = UrlAuthResult;
messages.acceptUrlAuth#18dd4e92 flags:# write_allowed:flags.0?true share_phone_number:flags.3?true peer:flags.1?InputPeer msg_id:flags.1?int button_id:flags.1?int url:flags.2?string match_code:flags.4?string tonconnect_session:flags.5?InputTonConnectOauthSession = UrlAuthResult;
messages.hidePeerSettingsBar#4facb138 peer:InputPeer = Bool;
messages.getScheduledHistory#f516760b peer:InputPeer hash:long = messages.Messages;
messages.getScheduledMessages#bdbb0464 peer:InputPeer id:Vector<int> = messages.Messages;
messages.sendScheduledMessages#bd38850a peer:InputPeer id:Vector<int> = Updates;
messages.deleteScheduledMessages#59ae2b16 peer:InputPeer id:Vector<int> = Updates;
messages.getPollVotes#b86e380e flags:# peer:InputPeer id:int option:flags.0?bytes offset:flags.1?string limit:int = messages.VotesList;
messages.toggleStickerSets#b5052fea flags:# uninstall:flags.0?true archive:flags.1?true unarchive:flags.2?true stickersets:Vector<InputStickerSet> = Bool;
messages.getDialogFilters#efd48c89 = messages.DialogFilters;
messages.getSuggestedDialogFilters#a29cd42c = Vector<DialogFilterSuggested>;
messages.updateDialogFilter#1ad4a04a flags:# id:int filter:flags.0?DialogFilter = Bool;
messages.updateDialogFiltersOrder#c563c1e4 order:Vector<int> = Bool;
messages.getOldFeaturedStickers#7ed094a1 offset:int limit:int hash:long = messages.FeaturedStickers;
messages.getReplies#22ddd30c peer:InputPeer msg_id:int offset_id:int offset_date:int add_offset:int limit:int max_id:int min_id:int hash:long = messages.Messages;
messages.getDiscussionMessage#446972fd peer:InputPeer msg_id:int = messages.DiscussionMessage;
messages.readDiscussion#f731a9f4 peer:InputPeer msg_id:int read_max_id:int = Bool;
messages.unpinAllMessages#62dd747 flags:# peer:InputPeer top_msg_id:flags.0?int saved_peer_id:flags.1?InputPeer = messages.AffectedHistory;
messages.deleteChat#5bd0ee50 chat_id:long = Bool;
messages.deletePhoneCallHistory#f9cbe409 flags:# revoke:flags.0?true = messages.AffectedFoundMessages;
messages.checkHistoryImport#43fe19f3 import_head:string = messages.HistoryImportParsed;
messages.initHistoryImport#34090c3b peer:InputPeer file:InputFile media_count:int = messages.HistoryImport;
messages.uploadImportedMedia#2a862092 peer:InputPeer import_id:long file_name:string media:InputMedia = MessageMedia;
messages.startHistoryImport#b43df344 peer:InputPeer import_id:long = Bool;
messages.getExportedChatInvites#a2b5a3f6 flags:# revoked:flags.3?true peer:InputPeer admin_id:InputUser offset_date:flags.2?int offset_link:flags.2?string limit:int = messages.ExportedChatInvites;
messages.getExportedChatInvite#73746f5c peer:InputPeer link:string = messages.ExportedChatInvite;
messages.editExportedChatInvite#bdca2f75 flags:# revoked:flags.2?true peer:InputPeer link:string expire_date:flags.0?int usage_limit:flags.1?int request_needed:flags.3?Bool title:flags.4?string = messages.ExportedChatInvite;
messages.deleteRevokedExportedChatInvites#56987bd5 peer:InputPeer admin_id:InputUser = Bool;
messages.deleteExportedChatInvite#d464a42b peer:InputPeer link:string = Bool;
messages.getAdminsWithInvites#3920e6ef peer:InputPeer = messages.ChatAdminsWithInvites;
messages.getChatInviteImporters#df04dd4e flags:# requested:flags.0?true subscription_expired:flags.3?true peer:InputPeer link:flags.1?string q:flags.2?string offset_date:int offset_user:InputUser limit:int = messages.ChatInviteImporters;
messages.setHistoryTTL#b80e5fe4 peer:InputPeer period:int = Updates;
messages.checkHistoryImportPeer#5dc60f03 peer:InputPeer = messages.CheckedHistoryImportPeer;
messages.setChatTheme#81202c9 peer:InputPeer theme:InputChatTheme = Updates;
messages.getMessageReadParticipants#31c1c44f peer:InputPeer msg_id:int = Vector<ReadParticipantDate>;
messages.getSearchResultsCalendar#6aa3f6bd flags:# peer:InputPeer saved_peer_id:flags.2?InputPeer filter:MessagesFilter offset_id:int offset_date:int = messages.SearchResultsCalendar;
messages.getSearchResultsPositions#9c7f2f10 flags:# peer:InputPeer saved_peer_id:flags.2?InputPeer filter:MessagesFilter offset_id:int limit:int = messages.SearchResultsPositions;
messages.hideChatJoinRequest#7fe7e815 flags:# approved:flags.0?true peer:InputPeer user_id:InputUser = Updates;
messages.hideAllChatJoinRequests#e085f4ea flags:# approved:flags.0?true peer:InputPeer link:flags.1?string = Updates;
messages.toggleNoForwards#b2081a35 flags:# peer:InputPeer enabled:Bool request_msg_id:flags.0?int = Updates;
messages.saveDefaultSendAs#ccfddf96 peer:InputPeer send_as:InputPeer = Bool;
messages.sendReaction#d30d78d4 flags:# big:flags.1?true add_to_recent:flags.2?true peer:InputPeer msg_id:int reaction:flags.0?Vector<Reaction> = Updates;
messages.getMessagesReactions#8bba90e6 peer:InputPeer id:Vector<int> = Updates;
messages.getMessageReactionsList#461b3f48 flags:# peer:InputPeer id:int reaction:flags.0?Reaction offset:flags.1?string limit:int = messages.MessageReactionsList;
messages.setChatAvailableReactions#864b2581 flags:# peer:InputPeer available_reactions:ChatReactions reactions_limit:flags.0?int paid_enabled:flags.1?Bool = Updates;
messages.getAvailableReactions#18dea0ac hash:int = messages.AvailableReactions;
messages.setDefaultReaction#4f47a016 reaction:Reaction = Bool;
messages.translateText#a5eec345 flags:# peer:flags.0?InputPeer id:flags.0?Vector<int> text:flags.1?Vector<TextWithEntities> to_lang:string tone:flags.2?string = messages.TranslatedText;
messages.getUnreadReactions#bd7f90ac flags:# peer:InputPeer top_msg_id:flags.0?int saved_peer_id:flags.1?InputPeer offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
messages.readReactions#9ec44f93 flags:# peer:InputPeer top_msg_id:flags.0?int saved_peer_id:flags.1?InputPeer = messages.AffectedHistory;
messages.searchSentMedia#107e31a0 q:string filter:MessagesFilter limit:int = messages.Messages;
messages.getAttachMenuBots#16fcc2cb hash:long = AttachMenuBots;
messages.getAttachMenuBot#77216192 bot:InputUser = AttachMenuBotsBot;
messages.toggleBotInAttachMenu#69f59d69 flags:# write_allowed:flags.0?true bot:InputUser enabled:Bool = Bool;
messages.requestWebView#269dc2c1 flags:# from_bot_menu:flags.4?true silent:flags.5?true compact:flags.7?true fullscreen:flags.8?true peer:InputPeer bot:InputUser url:flags.1?string start_param:flags.3?string theme_params:flags.2?DataJSON platform:string reply_to:flags.0?InputReplyTo send_as:flags.13?InputPeer = WebViewResult;
messages.prolongWebView#b0d81a83 flags:# silent:flags.5?true peer:InputPeer bot:InputUser query_id:long reply_to:flags.0?InputReplyTo send_as:flags.13?InputPeer = Bool;
messages.requestSimpleWebView#413a3e73 flags:# from_switch_webview:flags.1?true from_side_menu:flags.2?true compact:flags.7?true fullscreen:flags.8?true bot:InputUser url:flags.3?string start_param:flags.4?string theme_params:flags.0?DataJSON platform:string = WebViewResult;
messages.sendWebViewResultMessage#a4314f5 bot_query_id:string result:InputBotInlineResult = WebViewMessageSent;
messages.sendWebViewData#dc0242c8 bot:InputUser random_id:long button_text:string data:string = Updates;
messages.transcribeAudio#269e9a49 peer:InputPeer msg_id:int = messages.TranscribedAudio;
messages.rateTranscribedAudio#7f1d072f peer:InputPeer msg_id:int transcription_id:long good:Bool = Bool;
messages.getCustomEmojiDocuments#d9ab0f54 document_id:Vector<long> = Vector<Document>;
messages.getEmojiStickers#fbfca18f hash:long = messages.AllStickers;
messages.getFeaturedEmojiStickers#ecf6736 hash:long = messages.FeaturedStickers;
messages.reportReaction#3f64c076 peer:InputPeer id:int reaction_peer:InputPeer = Bool;
messages.getTopReactions#bb8125ba limit:int hash:long = messages.Reactions;
messages.getRecentReactions#39461db2 limit:int hash:long = messages.Reactions;
messages.clearRecentReactions#9dfeefb4 = Bool;
messages.getExtendedMedia#84f80814 peer:InputPeer id:Vector<int> = Updates;
messages.setDefaultHistoryTTL#9eb51445 period:int = Bool;
messages.getDefaultHistoryTTL#658b7188 = DefaultHistoryTTL;
messages.sendBotRequestedPeer#6c5cf2a7 flags:# peer:InputPeer msg_id:flags.0?int webapp_req_id:flags.1?string button_id:int requested_peers:Vector<InputPeer> = Updates;
messages.getEmojiGroups#7488ce5b hash:int = messages.EmojiGroups;
messages.getEmojiStatusGroups#2ecd56cd hash:int = messages.EmojiGroups;
messages.getEmojiProfilePhotoGroups#21a548f3 hash:int = messages.EmojiGroups;
messages.searchCustomEmoji#2c11c0d7 emoticon:string hash:long = EmojiList;
messages.togglePeerTranslations#e47cb579 flags:# disabled:flags.0?true peer:InputPeer = Bool;
messages.getBotApp#34fdc5c3 app:InputBotApp hash:long = messages.BotApp;
messages.requestAppWebView#53618bce flags:# write_allowed:flags.0?true compact:flags.7?true fullscreen:flags.8?true peer:InputPeer app:InputBotApp start_param:flags.1?string theme_params:flags.2?DataJSON platform:string = WebViewResult;
messages.setChatWallPaper#8ffacae1 flags:# for_both:flags.3?true revert:flags.4?true peer:InputPeer wallpaper:flags.0?InputWallPaper settings:flags.2?WallPaperSettings id:flags.1?int = Updates;
messages.searchEmojiStickerSets#92b4494c flags:# exclude_featured:flags.0?true q:string hash:long = messages.FoundStickerSets;
messages.getSavedDialogs#1e91fc99 flags:# exclude_pinned:flags.0?true parent_peer:flags.1?InputPeer offset_date:int offset_id:int offset_peer:InputPeer limit:int hash:long = messages.SavedDialogs;
messages.getSavedHistory#998ab009 flags:# parent_peer:flags.0?InputPeer peer:InputPeer offset_id:int offset_date:int add_offset:int limit:int max_id:int min_id:int hash:long = messages.Messages;
messages.deleteSavedHistory#4dc5085f flags:# parent_peer:flags.0?InputPeer peer:InputPeer max_id:int min_date:flags.2?int max_date:flags.3?int = messages.AffectedHistory;
messages.getPinnedSavedDialogs#d63d94e0 = messages.SavedDialogs;
messages.toggleSavedDialogPin#ac81bbde flags:# pinned:flags.0?true peer:InputDialogPeer = Bool;
messages.reorderPinnedSavedDialogs#8b716587 flags:# force:flags.0?true order:Vector<InputDialogPeer> = Bool;
messages.getSavedReactionTags#3637e05b flags:# peer:flags.0?InputPeer hash:long = messages.SavedReactionTags;
messages.updateSavedReactionTag#60297dec flags:# reaction:Reaction title:flags.0?string = Bool;
messages.getDefaultTagReactions#bdf93428 hash:long = messages.Reactions;
messages.getOutboxReadDate#8c4bfe5d peer:InputPeer msg_id:int = OutboxReadDate;
messages.getQuickReplies#d483f2a8 hash:long = messages.QuickReplies;
messages.reorderQuickReplies#60331907 order:Vector<int> = Bool;
messages.checkQuickReplyShortcut#f1d0fbd3 shortcut:string = Bool;
messages.editQuickReplyShortcut#5c003cef shortcut_id:int shortcut:string = Bool;
messages.deleteQuickReplyShortcut#3cc04740 shortcut_id:int = Bool;
messages.getQuickReplyMessages#94a495c3 flags:# shortcut_id:int id:flags.0?Vector<int> hash:long = messages.Messages;
messages.sendQuickReplyMessages#6c750de1 peer:InputPeer shortcut_id:int id:Vector<int> random_id:Vector<long> = Updates;
messages.deleteQuickReplyMessages#e105e910 shortcut_id:int id:Vector<int> = Updates;
messages.toggleDialogFilterTags#fd2dda49 enabled:Bool = Bool;
messages.getMyStickers#d0b5e1fc offset_id:long limit:int = messages.MyStickers;
messages.getEmojiStickerGroups#1dd840f5 hash:int = messages.EmojiGroups;
messages.getAvailableEffects#dea20a39 hash:int = messages.AvailableEffects;
messages.editFactCheck#589ee75 peer:InputPeer msg_id:int text:TextWithEntities = Updates;
messages.deleteFactCheck#d1da940c peer:InputPeer msg_id:int = Updates;
messages.getFactCheck#b9cdc5ee peer:InputPeer msg_id:Vector<int> = Vector<FactCheck>;
messages.requestMainWebView#c9e01e7b flags:# compact:flags.7?true fullscreen:flags.8?true peer:InputPeer bot:InputUser start_param:flags.1?string theme_params:flags.0?DataJSON platform:string = WebViewResult;
messages.sendPaidReaction#58bbcb50 flags:# peer:InputPeer msg_id:int count:int random_id:long private:flags.0?PaidReactionPrivacy = Updates;
messages.togglePaidReactionPrivacy#435885b5 peer:InputPeer msg_id:int private:PaidReactionPrivacy = Bool;
messages.getPaidReactionPrivacy#472455aa = Updates;
messages.viewSponsoredMessage#269e3643 random_id:bytes = Bool;
messages.clickSponsoredMessage#8235057e flags:# media:flags.0?true fullscreen:flags.1?true random_id:bytes = Bool;
messages.reportSponsoredMessage#12cbf0c4 random_id:bytes option:bytes = channels.SponsoredMessageReportResult;
messages.getSponsoredMessages#3d6ce850 flags:# peer:InputPeer msg_id:flags.0?int = messages.SponsoredMessages;
messages.savePreparedInlineMessage#f21f7f2f flags:# result:InputBotInlineResult user_id:InputUser peer_types:flags.0?Vector<InlineQueryPeerType> = messages.BotPreparedInlineMessage;
messages.getPreparedInlineMessage#857ebdb8 bot:InputUser id:string = messages.PreparedInlineMessage;
messages.searchStickers#29b1c66a flags:# emojis:flags.0?true q:string emoticon:string lang_code:Vector<string> offset:int limit:int hash:long = messages.FoundStickers;
messages.reportMessagesDelivery#5a6d7395 flags:# push:flags.0?true peer:InputPeer id:Vector<int> = Bool;
messages.getSavedDialogsByID#6f6f9c96 flags:# parent_peer:flags.1?InputPeer ids:Vector<InputPeer> = messages.SavedDialogs;
messages.readSavedHistory#ba4a3b5b parent_peer:InputPeer peer:InputPeer max_id:int = Bool;
messages.toggleTodoCompleted#d3e03124 peer:InputPeer msg_id:int completed:Vector<int> incompleted:Vector<int> = Updates;
messages.appendTodoList#21a61057 peer:InputPeer msg_id:int list:Vector<TodoItem> = Updates;
messages.toggleSuggestedPostApproval#8107455c flags:# reject:flags.1?true peer:InputPeer msg_id:int schedule_date:flags.0?int reject_comment:flags.2?string = Updates;
messages.getForumTopics#3ba47bff flags:# peer:InputPeer q:flags.0?string offset_date:int offset_id:int offset_topic:int limit:int = messages.ForumTopics;
messages.getForumTopicsByID#af0a4a08 peer:InputPeer topics:Vector<int> = messages.ForumTopics;
messages.editForumTopic#cecc1134 flags:# peer:InputPeer topic_id:int title:flags.0?string icon_emoji_id:flags.1?long closed:flags.2?Bool hidden:flags.3?Bool = Updates;
messages.updatePinnedForumTopic#175df251 peer:InputPeer topic_id:int pinned:Bool = Updates;
messages.reorderPinnedForumTopics#e7841f0 flags:# force:flags.0?true peer:InputPeer order:Vector<int> = Updates;
messages.createForumTopic#2f98c3d5 flags:# title_missing:flags.4?true peer:InputPeer title:string icon_color:flags.0?int icon_emoji_id:flags.3?long random_id:long send_as:flags.2?InputPeer = Updates;
messages.deleteTopicHistory#d2816f10 peer:InputPeer top_msg_id:int = messages.AffectedHistory;
messages.getEmojiGameInfo#fb7e8ca7 = messages.EmojiGameInfo;
messages.summarizeText#abbbd346 flags:# peer:InputPeer id:int to_lang:flags.0?string tone:flags.2?string = TextWithEntities;
messages.editChatCreator#f743b857 peer:InputPeer user_id:InputUser password:InputCheckPasswordSRP = Updates;
messages.getFutureChatCreatorAfterLeave#3b7d0ea6 peer:InputPeer = User;
messages.editChatParticipantRank#a00f32b0 peer:InputPeer participant:InputPeer rank:string = Updates;
messages.declineUrlAuth#35436bbc url:string = Bool;
messages.checkUrlAuthMatchCode#c9a47b0b url:string match_code:string = Bool;
messages.composeMessageWithAI#daecc589 flags:# proofread:flags.0?true emojify:flags.3?true text:TextWithEntities translate_to_lang:flags.1?string tone:flags.2?InputAiComposeTone = messages.ComposedMessageWithAI;
messages.reportReadMetrics#4067c5e6 peer:InputPeer metrics:Vector<InputMessageReadMetric> = Bool;
messages.reportMusicListen#ddbcd819 id:InputDocument listened_duration:int = Bool;
messages.addPollAnswer#19bc4b6d peer:InputPeer msg_id:int answer:PollAnswer = Updates;
messages.deletePollAnswer#ac8505a5 peer:InputPeer msg_id:int option:bytes = Updates;
messages.getUnreadPollVotes#43286cf2 flags:# peer:InputPeer top_msg_id:flags.0?int offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
messages.readPollVotes#1720b4d8 flags:# peer:InputPeer top_msg_id:flags.0?int = messages.AffectedHistory;
messages.setBotGuestChatResult#b8f106e3 query_id:long result:InputBotInlineResult = InputBotInlineMessageID;
messages.deleteParticipantReactions#a0b80cf8 peer:InputPeer participant:InputPeer = Bool;
messages.deleteParticipantReaction#e3b7f82c peer:InputPeer msg_id:int participant:InputPeer = Updates;
messages.getPersonalChannelHistory#55fb0996 user_id:InputUser limit:int max_id:int min_id:int hash:long = messages.Messages;
messages.getRichMessage#501569cf peer:InputPeer id:int = messages.Messages;
messages.translateRichMessage#1a542004 flags:# peer:flags.0?InputPeer id:flags.0?Vector<int> text:flags.1?Vector<InputRichMessage> to_lang:string tone:flags.2?string = messages.TranslatedRichMessage;
messages.composeRichMessageWithAI#8d7ae6af flags:# proofread:flags.0?true emojify:flags.3?true text:flags.4?InputRichMessage translate_to_lang:flags.1?string tone:flags.2?InputAiComposeTone = messages.ComposedRichMessageWithAI;
messages.requestChatJoinWebView#ba9ee679 flags:# query_id:long theme_params:flags.0?DataJSON platform:string = WebViewResult;
messages.setShareTheme#698a9b1a peer:InputPeer theme:ShareTheme = Bool;
messages.getShareThemeBackdrops#f4599ef2 hash:int = ShareThemeBackdrops;

updates.getState#edd4882a = updates.State;
updates.getDifference#19c2f763 flags:# pts:int pts_limit:flags.1?int pts_total_limit:flags.0?int date:int qts:int qts_limit:flags.2?int = updates.Difference;
updates.getChannelDifference#3173d78 flags:# force:flags.0?true channel:InputChannel filter:ChannelMessagesFilter pts:int limit:int = updates.ChannelDifference;

photos.updateProfilePhoto#9e82039 flags:# fallback:flags.0?true bot:flags.1?InputUser id:InputPhoto = photos.Photo;
photos.uploadProfilePhoto#388a3b5 flags:# fallback:flags.3?true bot:flags.5?InputUser file:flags.0?InputFile video:flags.1?InputFile video_start_ts:flags.2?double video_emoji_markup:flags.4?VideoSize = photos.Photo;
photos.deletePhotos#87cf7f2f id:Vector<InputPhoto> = Vector<long>;
photos.getUserPhotos#91cd32a8 user_id:InputUser offset:int max_id:long limit:int = photos.Photos;
photos.uploadContactProfilePhoto#e14c4a71 flags:# suggest:flags.3?true save:flags.4?true user_id:InputUser file:flags.0?InputFile video:flags.1?InputFile video_start_ts:flags.2?double video_emoji_markup:flags.5?VideoSize = photos.Photo;

upload.saveFilePart#b304a621 file_id:long file_part:int bytes:bytes = Bool;
upload.getFile#be5335be flags:# precise:flags.0?true cdn_supported:flags.1?true location:InputFileLocation offset:long limit:int = upload.File;
upload.saveBigFilePart#de7b673d file_id:long file_part:int file_total_parts:int bytes:bytes = Bool;
upload.getWebFile#24e6818d location:InputWebFileLocation offset:int limit:int = upload.WebFile;
upload.getCdnFile#395f69da file_token:bytes offset:long limit:int = upload.CdnFile;
upload.reuploadCdnFile#9b2754a8 file_token:bytes request_token:bytes = Vector<FileHash>;
upload.getCdnFileHashes#91dc3f31 file_token:bytes offset:long = Vector<FileHash>;
upload.getFileHashes#9156982a location:InputFileLocation offset:long = Vector<FileHash>;

help.getConfig#c4f9186b = Config;
help.getNearestDc#1fb33026 = NearestDc;
help.getAppUpdate#522d5a7d source:string = help.AppUpdate;
help.getInviteText#4d392343 = help.InviteText;
help.getSupport#9cdf08cd = help.Support;
help.setBotUpdatesStatus#ec22cfcd pending_updates_count:int message:string = Bool;
help.getCdnConfig#52029342 = CdnConfig;
help.getRecentMeUrls#3dc0f114 referer:string = help.RecentMeUrls;
help.getTermsOfServiceUpdate#2ca51fd1 = help.TermsOfServiceUpdate;
help.acceptTermsOfService#ee72f79a id:DataJSON = Bool;
help.getDeepLinkInfo#3fedc75f path:string = help.DeepLinkInfo;
help.getAppConfig#61e3f854 hash:int = help.AppConfig;
help.saveAppLog#6f02f748 events:Vector<InputAppEvent> = Bool;
help.getPassportConfig#c661ad08 hash:int = help.PassportConfig;
help.getSupportName#d360e72c = help.SupportName;
help.getUserInfo#38a08d3 user_id:InputUser = help.UserInfo;
help.editUserInfo#66b91b70 user_id:InputUser message:string entities:Vector<MessageEntity> = help.UserInfo;
help.getPromoData#c0977421 = help.PromoData;
help.hidePromoData#1e251c95 peer:InputPeer = Bool;
help.dismissSuggestion#f50dbaa1 peer:InputPeer suggestion:string = Bool;
help.getCountriesList#735787a8 lang_code:string hash:int = help.CountriesList;
help.getPremiumPromo#b81b93d4 = help.PremiumPromo;
help.getPeerColors#da80f42f hash:int = help.PeerColors;
help.getPeerProfileColors#abcfa9fd hash:int = help.PeerColors;
help.getTimezonesList#49b30240 hash:int = help.TimezonesList;

channels.readHistory#cc104937 channel:InputChannel max_id:int = Bool;
channels.deleteMessages#84c1fd4e channel:InputChannel id:Vector<int> = messages.AffectedMessages;
channels.reportSpam#f44a8315 channel:InputChannel participant:InputPeer id:Vector<int> = Bool;
channels.getMessages#ad8c9a23 channel:InputChannel id:Vector<InputMessage> = messages.Messages;
channels.getParticipants#77ced9d0 channel:InputChannel filter:ChannelParticipantsFilter offset:int limit:int hash:long = channels.ChannelParticipants;
channels.getParticipant#a0ab6cc6 channel:InputChannel participant:InputPeer = channels.ChannelParticipant;
channels.getChannels#a7f6bbb id:Vector<InputChannel> = messages.Chats;
channels.getFullChannel#8736a09 channel:InputChannel = messages.ChatFull;
channels.createChannel#91006707 flags:# broadcast:flags.0?true megagroup:flags.1?true for_import:flags.3?true forum:flags.5?true title:string about:string geo_point:flags.2?InputGeoPoint address:flags.2?string ttl_period:flags.4?int = Updates;
channels.editAdmin#9a98ad68 flags:# channel:InputChannel user_id:InputUser admin_rights:ChatAdminRights rank:flags.0?string = Updates;
channels.editTitle#566decd0 channel:InputChannel title:string = Updates;
channels.editPhoto#f12e57c9 channel:InputChannel photo:InputChatPhoto = Updates;
channels.checkUsername#10e6bd2c channel:InputChannel username:string = Bool;
channels.updateUsername#3514b3de channel:InputChannel username:string = Bool;
channels.joinChannel#7f6a1e22 channel:InputChannel = messages.ChatInviteJoinResult;
channels.leaveChannel#f836aa95 channel:InputChannel = Updates;
channels.inviteToChannel#c9e33d54 channel:InputChannel users:Vector<InputUser> = messages.InvitedUsers;
channels.deleteChannel#c0111fe3 channel:InputChannel = Updates;
channels.exportMessageLink#e63fadeb flags:# grouped:flags.0?true thread:flags.1?true channel:InputChannel id:int = ExportedMessageLink;
channels.toggleSignatures#418d549c flags:# signatures_enabled:flags.0?true profiles_enabled:flags.1?true channel:InputChannel = Updates;
channels.getAdminedPublicChannels#f8b036af flags:# by_location:flags.0?true check_limit:flags.1?true for_personal:flags.2?true for_community_peer:flags.3?true = messages.Chats;
channels.editBanned#96e6cd81 channel:InputChannel participant:InputPeer banned_rights:ChatBannedRights = Updates;
channels.getAdminLog#33ddf480 flags:# channel:InputChannel q:string events_filter:flags.0?ChannelAdminLogEventsFilter admins:flags.1?Vector<InputUser> max_id:long min_id:long limit:int = channels.AdminLogResults;
channels.setStickers#ea8ca4f9 channel:InputChannel stickerset:InputStickerSet = Bool;
channels.readMessageContents#eab5dc38 channel:InputChannel id:Vector<int> = Bool;
channels.deleteHistory#9baa9647 flags:# for_everyone:flags.0?true channel:InputChannel max_id:int = Updates;
channels.togglePreHistoryHidden#eabbb94c channel:InputChannel enabled:Bool = Updates;
channels.getLeftChannels#8341ecc0 offset:int = messages.Chats;
channels.getGroupsForDiscussion#f5dad378 = messages.Chats;
channels.setDiscussionGroup#40582bb2 broadcast:InputChannel group:InputChannel = Bool;
channels.editLocation#58e63f6d channel:InputChannel geo_point:InputGeoPoint address:string = Bool;
channels.toggleSlowMode#edd49ef0 channel:InputChannel seconds:int = Updates;
channels.getInactiveChannels#11e831ee = messages.InactiveChats;
channels.convertToGigagroup#b290c69 channel:InputChannel = Updates;
channels.getSendAs#e785a43f flags:# for_paid_reactions:flags.0?true for_live_stories:flags.1?true peer:InputPeer = channels.SendAsPeers;
channels.deleteParticipantHistory#367544db channel:InputChannel participant:InputPeer = messages.AffectedHistory;
channels.toggleJoinToSend#e4cb9580 channel:InputChannel enabled:Bool = Updates;
channels.toggleJoinRequest#ecc2618 flags:# apply_to_invites:flags.1?true channel:InputChannel enabled:Bool guard_bot:flags.0?InputUser = Updates;
channels.reorderUsernames#b45ced1d channel:InputChannel order:Vector<string> = Bool;
channels.toggleUsername#50f24105 channel:InputChannel username:string active:Bool = Bool;
channels.deactivateAllUsernames#a245dd3 channel:InputChannel = Bool;
channels.toggleForum#3ff75734 channel:InputChannel enabled:Bool tabs:Bool = Updates;
channels.toggleAntiSpam#68f3e4eb channel:InputChannel enabled:Bool = Updates;
channels.reportAntiSpamFalsePositive#a850a693 channel:InputChannel msg_id:int = Bool;
channels.toggleParticipantsHidden#6a6e7854 channel:InputChannel enabled:Bool = Updates;
channels.updateColor#d8aa3671 flags:# for_profile:flags.1?true channel:InputChannel color:flags.2?int background_emoji_id:flags.0?long = Updates;
channels.toggleViewForumAsMessages#9738bb15 channel:InputChannel enabled:Bool = Updates;
channels.getChannelRecommendations#25a71742 flags:# channel:flags.0?InputChannel = messages.Chats;
channels.updateEmojiStatus#f0d3e6a8 channel:InputChannel emoji_status:EmojiStatus = Updates;
channels.setBoostsToUnblockRestrictions#ad399cee channel:InputChannel boosts:int = Updates;
channels.setEmojiStickers#3cd930b7 channel:InputChannel stickerset:InputStickerSet = Bool;
channels.restrictSponsoredMessages#9ae91519 channel:InputChannel restricted:Bool = Updates;
channels.searchPosts#f2c4f24d flags:# hashtag:flags.0?string query:flags.1?string offset_rate:int offset_peer:InputPeer offset_id:int limit:int allow_paid_stars:flags.2?long = messages.Messages;
channels.updatePaidMessagesPrice#4b12327b flags:# broadcast_messages_allowed:flags.0?true channel:InputChannel send_paid_messages_stars:long = Updates;
channels.toggleAutotranslation#167fc0a1 channel:InputChannel enabled:Bool = Updates;
channels.getMessageAuthor#ece2a0e6 channel:InputChannel id:int = User;
channels.checkSearchPostsFlood#22567115 flags:# query:flags.0?string = SearchPostsFlood;
channels.setMainProfileTab#3583fcb1 channel:InputChannel tab:ProfileTab = Bool;

bots.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON;
bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool;
bots.setBotCommands#517165a scope:BotCommandScope lang_code:string commands:Vector<BotCommand> = Bool;
bots.resetBotCommands#3d8de0f9 scope:BotCommandScope lang_code:string = Bool;
bots.getBotCommands#e34c0dd6 scope:BotCommandScope lang_code:string = Vector<BotCommand>;
bots.setBotMenuButton#4504d54f user_id:InputUser button:BotMenuButton = Bool;
bots.getBotMenuButton#9c60eb28 user_id:InputUser = BotMenuButton;
bots.setBotBroadcastDefaultAdminRights#788464e1 admin_rights:ChatAdminRights = Bool;
bots.setBotGroupDefaultAdminRights#925ec9ea admin_rights:ChatAdminRights = Bool;
bots.setBotInfo#10cf3123 flags:# bot:flags.2?InputUser lang_code:string name:flags.3?string about:flags.0?string description:flags.1?string = Bool;
bots.getBotInfo#dcd914fd flags:# bot:flags.0?InputUser lang_code:string = bots.BotInfo;
bots.reorderUsernames#9709b1c2 bot:InputUser order:Vector<string> = Bool;
bots.toggleUsername#53ca973 bot:InputUser username:string active:Bool = Bool;
bots.canSendMessage#1359f4e6 bot:InputUser = Bool;
bots.allowSendMessage#f132e3ef bot:InputUser = Updates;
bots.invokeWebViewCustomMethod#87fc5e7 bot:InputUser custom_method:string params:DataJSON = DataJSON;
bots.getPopularAppBots#c2510192 offset:string limit:int = bots.PopularAppBots;
bots.addPreviewMedia#17aeb75a bot:InputUser lang_code:string media:InputMedia = BotPreviewMedia;
bots.editPreviewMedia#8525606f bot:InputUser lang_code:string media:InputMedia new_media:InputMedia = BotPreviewMedia;
bots.deletePreviewMedia#2d0135b3 bot:InputUser lang_code:string media:Vector<InputMedia> = Bool;
bots.reorderPreviewMedias#b627f3aa bot:InputUser lang_code:string order:Vector<InputMedia> = Bool;
bots.getPreviewInfo#423ab3ad bot:InputUser lang_code:string = bots.PreviewInfo;
bots.getPreviewMedias#a2a5594d bot:InputUser = Vector<BotPreviewMedia>;
bots.updateUserEmojiStatus#ed9f30c5 user_id:InputUser emoji_status:EmojiStatus = Bool;
bots.toggleUserEmojiStatusPermission#6de6392 bot:InputUser enabled:Bool = Bool;
bots.checkDownloadFileParams#50077589 bot:InputUser file_name:string url:string = Bool;
bots.getAdminedBots#b0711d83 = Vector<User>;
bots.updateStarRefProgram#778b5ab3 flags:# bot:InputUser commission_permille:int duration_months:flags.0?int = StarRefProgram;
bots.setCustomVerification#f4946757 flags:# enabled:flags.1?true bot:flags.0?InputUser peer:InputPeer custom_description:flags.2?TextWithEntities = Bool;
bots.getBotRecommendations#a1b70815 bot:InputUser = users.Users;
bots.checkUsername#e3ac4c61 flags:# additional:flags.0?true username:string = Bool;
bots.createBot#e5b17f2b flags:# via_deeplink:flags.0?true name:string username:string manager_id:InputUser = User;
bots.exportBotToken#bd0d99eb bot:InputUser revoke:Bool = bots.ExportedBotToken;
bots.requestWebViewButton#31a2a35e user_id:InputUser button:KeyboardButton = bots.RequestedButton;
bots.getRequestedWebViewButton#bf25b7f3 bot:InputUser webapp_req_id:string = KeyboardButton;
bots.getAccessSettings#213853a3 bot:InputUser = bots.AccessSettings;
bots.editAccessSettings#31813cd8 flags:# restricted:flags.0?true bot:InputUser add_users:flags.1?Vector<InputUser> = Bool;
bots.setJoinChatResults#e71a4810 query_id:long result:JoinChatBotResult = Bool;
bots.addUsername#3501c08a bot:InputUser username:string = Bool;
bots.removeUsername#c704147d bot:InputUser username:string = Bool;

payments.getPaymentForm#37148dbb flags:# invoice:InputInvoice theme_params:flags.0?DataJSON = payments.PaymentForm;
payments.getPaymentReceipt#2478d1cc peer:InputPeer msg_id:int = payments.PaymentReceipt;
payments.validateRequestedInfo#b6c8f12b flags:# save:flags.0?true invoice:InputInvoice info:PaymentRequestedInfo = payments.ValidatedRequestedInfo;
payments.sendPaymentForm#2d03522f flags:# form_id:long invoice:InputInvoice requested_info_id:flags.0?string shipping_option_id:flags.1?string credentials:InputPaymentCredentials tip_amount:flags.2?long = payments.PaymentResult;
payments.getSavedInfo#227d824b = payments.SavedInfo;
payments.clearSavedInfo#d83d70c1 flags:# credentials:flags.0?true info:flags.1?true = Bool;
payments.getBankCardData#2e79d779 number:string = payments.BankCardData;
payments.exportInvoice#f91b065 invoice_media:InputMedia = payments.ExportedInvoice;
payments.assignAppStoreTransaction#80ed747d receipt:bytes purpose:InputStorePaymentPurpose = Updates;
payments.assignPlayMarketTransaction#dffd50d3 receipt:DataJSON purpose:InputStorePaymentPurpose = Updates;
payments.getPremiumGiftCodeOptions#2757ba54 flags:# boost_peer:flags.0?InputPeer = Vector<PremiumGiftCodeOption>;
payments.checkGiftCode#8e51b4c1 slug:string = payments.CheckedGiftCode;
payments.applyGiftCode#f6e26854 slug:string = Updates;
payments.getGiveawayInfo#f4239425 peer:InputPeer msg_id:int = payments.GiveawayInfo;
payments.launchPrepaidGiveaway#5ff58f20 peer:InputPeer giveaway_id:long purpose:InputStorePaymentPurpose = Updates;
payments.getStarsTopupOptions#c00ec7d3 = Vector<StarsTopupOption>;
payments.getStarsStatus#4ea9b3bf flags:# ton:flags.0?true peer:InputPeer = payments.StarsStatus;
payments.getStarsTransactions#69da4557 flags:# inbound:flags.0?true outbound:flags.1?true ascending:flags.2?true ton:flags.4?true subscription_id:flags.3?string peer:InputPeer offset:string limit:int = payments.StarsStatus;
payments.sendStarsForm#7998c914 form_id:long invoice:InputInvoice = payments.PaymentResult;
payments.refundStarsCharge#25ae8f4a user_id:InputUser charge_id:string = Updates;
payments.getStarsRevenueStats#d91ffad6 flags:# dark:flags.0?true ton:flags.1?true peer:InputPeer = payments.StarsRevenueStats;
payments.getStarsRevenueWithdrawalUrl#2433dc92 flags:# ton:flags.0?true peer:InputPeer amount:flags.1?long password:InputCheckPasswordSRP = payments.StarsRevenueWithdrawalUrl;
payments.getStarsRevenueAdsAccountUrl#d1d7efc5 peer:InputPeer = payments.StarsRevenueAdsAccountUrl;
payments.getStarsTransactionsByID#2dca16b8 flags:# ton:flags.0?true peer:InputPeer id:Vector<InputStarsTransaction> = payments.StarsStatus;
payments.getStarsGiftOptions#d3c96bc8 flags:# user_id:flags.0?InputUser = Vector<StarsGiftOption>;
payments.getStarsSubscriptions#32512c5 flags:# missing_balance:flags.0?true peer:InputPeer offset:string = payments.StarsStatus;
payments.changeStarsSubscription#c7770878 flags:# peer:InputPeer subscription_id:string canceled:flags.0?Bool = Bool;
payments.fulfillStarsSubscription#cc5bebb3 peer:InputPeer subscription_id:string = Bool;
payments.getStarsGiveawayOptions#bd1efd3e = Vector<StarsGiveawayOption>;
payments.getStarGifts#c4563590 hash:int = payments.StarGifts;
payments.saveStarGift#2a2a697c flags:# unsave:flags.0?true stargift:InputSavedStarGift = Bool;
payments.convertStarGift#74bf076b stargift:InputSavedStarGift = Bool;
payments.botCancelStarsSubscription#6dfa0622 flags:# restore:flags.0?true user_id:InputUser charge_id:string = Bool;
payments.getConnectedStarRefBots#5869a553 flags:# peer:InputPeer offset_date:flags.2?int offset_link:flags.2?string limit:int = payments.ConnectedStarRefBots;
payments.getConnectedStarRefBot#b7d998f0 peer:InputPeer bot:InputUser = payments.ConnectedStarRefBots;
payments.getSuggestedStarRefBots#d6b48f7 flags:# order_by_revenue:flags.0?true order_by_date:flags.1?true peer:InputPeer offset:string limit:int = payments.SuggestedStarRefBots;
payments.connectStarRefBot#7ed5348a peer:InputPeer bot:InputUser = payments.ConnectedStarRefBots;
payments.editConnectedStarRefBot#e4fca4a3 flags:# revoked:flags.0?true peer:InputPeer link:string = payments.ConnectedStarRefBots;
payments.getStarGiftUpgradePreview#9c9abcb1 gift_id:long = payments.StarGiftUpgradePreview;
payments.upgradeStarGift#aed6e4f5 flags:# keep_original_details:flags.0?true stargift:InputSavedStarGift = Updates;
payments.transferStarGift#7f18176a stargift:InputSavedStarGift to_id:InputPeer = Updates;
payments.getUniqueStarGift#a1974d72 slug:string = payments.UniqueStarGift;
payments.getSavedStarGifts#a319e569 flags:# exclude_unsaved:flags.0?true exclude_saved:flags.1?true exclude_unlimited:flags.2?true exclude_unique:flags.4?true sort_by_value:flags.5?true exclude_upgradable:flags.7?true exclude_unupgradable:flags.8?true peer_color_available:flags.9?true exclude_hosted:flags.10?true peer:InputPeer collection_id:flags.6?int offset:string limit:int = payments.SavedStarGifts;
payments.getSavedStarGift#b455a106 stargift:Vector<InputSavedStarGift> = payments.SavedStarGifts;
payments.getStarGiftWithdrawalUrl#c8ca817b flags:# stargift:InputSavedStarGift tmp_password:bytes = payments.StarGiftWithdrawalUrl;
payments.toggleChatStarGiftNotifications#60eaefa1 flags:# enabled:flags.0?true peer:InputPeer = Bool;
payments.toggleStarGiftsPinnedToTop#1513e7b0 peer:InputPeer stargift:Vector<InputSavedStarGift> = Bool;
payments.canPurchaseStore#4fdc5ea7 purpose:InputStorePaymentPurpose = Bool;
payments.getResaleStarGifts#7a5fa236 flags:# sort_by_price:flags.1?true sort_by_num:flags.2?true for_craft:flags.4?true stars_only:flags.5?true attributes_hash:flags.0?long gift_id:long attributes:flags.3?Vector<StarGiftAttributeId> offset:string limit:int = payments.ResaleStarGifts;
payments.updateStarGiftPrice#edbe6ccb stargift:InputSavedStarGift resell_amount:StarsAmount = Updates;
payments.createStarGiftCollection#1f4a0e87 peer:InputPeer title:string stargift:Vector<InputSavedStarGift> = StarGiftCollection;
payments.updateStarGiftCollection#4fddbee7 flags:# peer:InputPeer collection_id:int title:flags.0?string delete_stargift:flags.1?Vector<InputSavedStarGift> add_stargift:flags.2?Vector<InputSavedStarGift> order:flags.3?Vector<InputSavedStarGift> = StarGiftCollection;
payments.reorderStarGiftCollections#c32af4cc peer:InputPeer order:Vector<int> = Bool;
payments.deleteStarGiftCollection#ad5648e8 peer:InputPeer collection_id:int = Bool;
payments.getStarGiftCollections#981b91dd peer:InputPeer hash:long = payments.StarGiftCollections;
payments.getUniqueStarGiftValueInfo#4365af6b slug:string = payments.UniqueStarGiftValueInfo;
payments.checkCanSendGift#c0c4edc9 gift_id:long = payments.CheckCanSendGiftResult;
payments.getStarGiftAuctionState#5c9ff4d6 auction:InputStarGiftAuction version:int = payments.StarGiftAuctionState;
payments.getStarGiftAuctionAcquiredGifts#6ba2cbec gift_id:long = payments.StarGiftAuctionAcquiredGifts;
payments.getStarGiftActiveAuctions#a5d0514d hash:long = payments.StarGiftActiveAuctions;
payments.resolveStarGiftOffer#e9ce781c flags:# decline:flags.0?true offer_msg_id:int = Updates;
payments.sendStarGiftOffer#8fb86b41 flags:# peer:InputPeer slug:string price:StarsAmount duration:int random_id:long allow_paid_stars:flags.0?long = Updates;
payments.getStarGiftUpgradeAttributes#6d038b58 gift_id:long = payments.StarGiftUpgradeAttributes;
payments.getCraftStarGifts#fd05dd00 gift_id:long offset:string limit:int = payments.SavedStarGifts;
payments.craftStarGift#b0f9684f stargift:Vector<InputSavedStarGift> = Updates;
payments.getCurrencyRates#d35983e8 = payments.CurrencyRates;
payments.getOnrampProviders#3f3e00dc flags:# crypto_currency:flags.0?string = Vector<OnrampProviderInfo>;
payments.getOnrampBaseCurrencies#1c8bedf3 provider:string crypto_currency:string = Vector<string>;
payments.getOnrampAvailability#2ec29951 flags:# provider:string crypto_currency:string base_currency:flags.0?string = OnrampAvailability;
payments.getOnrampLimits#80e851fd flags:# provider:string crypto_currency:string base_currency:string payment_method:flags.0?string = OnrampLimits;
payments.getOnrampQuote#e06645fa flags:# provider:string crypto_currency:string base_currency:string base_amount:flags.0?string crypto_amount:flags.1?string payment_method:flags.2?string = OnrampQuote;
payments.createOnrampSession#a2197cf6 flags:# provider:string crypto_currency:string address:string payment_method:flags.0?string base_currency:flags.1?string base_amount:flags.2?string memo:flags.3?string theme:flags.4?string success_return_url:flags.5?string fail_return_url:flags.6?string crypto_amount:flags.7?string = OnrampSession;
payments.withdrawStarGift#ec2030ae flags:# keep_on_profile:flags.0?true stargift:InputSavedStarGift tmp_password:bytes = Bool;
payments.depositStarGift#c0a2ea9f flags:# hide_sender:flags.0?true peer:InputPeer slug:string = payments.StarGiftDepositPayload;
payments.displayStarGift#4aa5f0fe flags:# peer:flags.0?InputPeer slug:string = Bool;
payments.deleteStarGift#3cdaf8aa stargift:InputSavedStarGift = Bool;

stickers.createStickerSet#9021ab67 flags:# masks:flags.0?true emojis:flags.5?true text_color:flags.6?true user_id:InputUser title:string short_name:string thumb:flags.2?InputDocument stickers:Vector<InputStickerSetItem> software:flags.3?string = messages.StickerSet;
stickers.removeStickerFromSet#f7760f51 sticker:InputDocument = messages.StickerSet;
stickers.changeStickerPosition#ffb6d4ca sticker:InputDocument position:int = messages.StickerSet;
stickers.addStickerToSet#8653febe stickerset:InputStickerSet sticker:InputStickerSetItem = messages.StickerSet;
stickers.setStickerSetThumb#a76a5392 flags:# stickerset:InputStickerSet thumb:flags.0?InputDocument thumb_document_id:flags.1?long = messages.StickerSet;
stickers.checkShortName#284b3639 short_name:string = Bool;
stickers.suggestShortName#4dafc503 title:string = stickers.SuggestedShortName;
stickers.changeSticker#f5537ebc flags:# sticker:InputDocument emoji:flags.0?string mask_coords:flags.1?MaskCoords keywords:flags.2?string = messages.StickerSet;
stickers.renameStickerSet#124b1c00 stickerset:InputStickerSet title:string = messages.StickerSet;
stickers.deleteStickerSet#87704394 stickerset:InputStickerSet = Bool;
stickers.replaceSticker#4696459a sticker:InputDocument new_sticker:InputStickerSetItem = messages.StickerSet;

phone.getCallConfig#55451fa9 = DataJSON;
phone.requestCall#42ff96ed flags:# video:flags.0?true user_id:InputUser random_id:int g_a_hash:bytes protocol:PhoneCallProtocol = phone.PhoneCall;
phone.acceptCall#3bd2b4a0 peer:InputPhoneCall g_b:bytes protocol:PhoneCallProtocol = phone.PhoneCall;
phone.confirmCall#2efe1722 peer:InputPhoneCall g_a:bytes key_fingerprint:long protocol:PhoneCallProtocol = phone.PhoneCall;
phone.receivedCall#17d54f61 peer:InputPhoneCall = Bool;
phone.discardCall#b2cbc1c0 flags:# video:flags.0?true peer:InputPhoneCall duration:int reason:PhoneCallDiscardReason connection_id:long = Updates;
phone.setCallRating#59ead627 flags:# user_initiative:flags.0?true peer:InputPhoneCall rating:int comment:string = Updates;
phone.saveCallDebug#277add7e peer:InputPhoneCall debug:DataJSON = Bool;
phone.sendSignalingData#ff7a9383 peer:InputPhoneCall data:bytes = Bool;
phone.createGroupCall#48cdc6d8 flags:# rtmp_stream:flags.2?true peer:InputPeer random_id:int title:flags.0?string schedule_date:flags.1?int = Updates;
phone.joinGroupCall#8fb53057 flags:# muted:flags.0?true video_stopped:flags.2?true call:InputGroupCall join_as:InputPeer invite_hash:flags.1?string public_key:flags.3?int256 block:flags.3?bytes params:DataJSON = Updates;
phone.leaveGroupCall#500377f9 call:InputGroupCall source:int = Updates;
phone.inviteToGroupCall#7b393160 call:InputGroupCall users:Vector<InputUser> = Updates;
phone.discardGroupCall#7a777135 call:InputGroupCall = Updates;
phone.toggleGroupCallSettings#974392f2 flags:# reset_invite_hash:flags.1?true call:InputGroupCall join_muted:flags.0?Bool messages_enabled:flags.2?Bool send_paid_messages_stars:flags.3?long = Updates;
phone.getGroupCall#41845db call:InputGroupCall limit:int = phone.GroupCall;
phone.getGroupParticipants#c558d8ab call:InputGroupCall ids:Vector<InputPeer> sources:Vector<int> offset:string limit:int = phone.GroupParticipants;
phone.checkGroupCall#b59cf977 call:InputGroupCall sources:Vector<int> = Vector<int>;
phone.toggleGroupCallRecord#f128c708 flags:# start:flags.0?true video:flags.2?true call:InputGroupCall title:flags.1?string video_portrait:flags.2?Bool = Updates;
phone.editGroupCallParticipant#a5273abf flags:# call:InputGroupCall participant:InputPeer muted:flags.0?Bool volume:flags.1?int raise_hand:flags.2?Bool video_stopped:flags.3?Bool video_paused:flags.4?Bool presentation_paused:flags.5?Bool = Updates;
phone.editGroupCallTitle#1ca6ac0a call:InputGroupCall title:string = Updates;
phone.getGroupCallJoinAs#ef7c213a peer:InputPeer = phone.JoinAsPeers;
phone.exportGroupCallInvite#e6aa647f flags:# can_self_unmute:flags.0?true call:InputGroupCall = phone.ExportedGroupCallInvite;
phone.toggleGroupCallStartSubscription#219c34e6 call:InputGroupCall subscribed:Bool = Updates;
phone.startScheduledGroupCall#5680e342 call:InputGroupCall = Updates;
phone.saveDefaultGroupCallJoinAs#575e1f8c peer:InputPeer join_as:InputPeer = Bool;
phone.joinGroupCallPresentation#cbea6bc4 call:InputGroupCall params:DataJSON = Updates;
phone.leaveGroupCallPresentation#1c50d144 call:InputGroupCall = Updates;
phone.getGroupCallStreamChannels#1ab21940 call:InputGroupCall = phone.GroupCallStreamChannels;
phone.getGroupCallStreamRtmpUrl#5af4c73a flags:# live_story:flags.0?true peer:InputPeer revoke:Bool = phone.GroupCallStreamRtmpUrl;
phone.saveCallLog#41248786 peer:InputPhoneCall file:InputFile = Bool;
phone.createConferenceCall#7d0444bb flags:# muted:flags.0?true video_stopped:flags.2?true join:flags.3?true random_id:int public_key:flags.3?int256 block:flags.3?bytes params:flags.3?DataJSON = Updates;
phone.deleteConferenceCallParticipants#8ca60525 flags:# only_left:flags.0?true kick:flags.1?true call:InputGroupCall ids:Vector<long> block:bytes = Updates;
phone.sendConferenceCallBroadcast#c6701900 call:InputGroupCall block:bytes = Updates;
phone.inviteConferenceCallParticipant#bcf22685 flags:# video:flags.0?true call:InputGroupCall user_id:InputUser = Updates;
phone.declineConferenceCallInvite#3c479971 msg_id:int = Updates;
phone.getGroupCallChainBlocks#ee9f88a6 call:InputGroupCall sub_chain_id:int offset:int limit:int = Updates;
phone.sendGroupCallMessage#b1d11410 flags:# call:InputGroupCall random_id:long message:TextWithEntities allow_paid_stars:flags.0?long send_as:flags.1?InputPeer = Updates;
phone.sendGroupCallEncryptedMessage#e5afa56d call:InputGroupCall encrypted_message:bytes = Bool;
phone.deleteGroupCallMessages#f64f54f7 flags:# report_spam:flags.0?true call:InputGroupCall messages:Vector<int> = Updates;
phone.deleteGroupCallParticipantMessages#1dbfeca0 flags:# report_spam:flags.0?true call:InputGroupCall participant:InputPeer = Updates;
phone.getGroupCallStars#6f636302 call:InputGroupCall = phone.GroupCallStars;
phone.saveDefaultSendAs#4167add1 call:InputGroupCall send_as:InputPeer = Bool;

langpack.getLangPack#f2f2330a lang_pack:string lang_code:string = LangPackDifference;
langpack.getStrings#efea3803 lang_pack:string lang_code:string keys:Vector<string> = Vector<LangPackString>;
langpack.getDifference#cd984aa5 lang_pack:string lang_code:string from_version:int = LangPackDifference;
langpack.getLanguages#42c6978f lang_pack:string = Vector<LangPackLanguage>;
langpack.getLanguage#6a596502 lang_pack:string lang_code:string = LangPackLanguage;

folders.editPeerFolders#6847d0ab folder_peers:Vector<InputFolderPeer> = Updates;

stats.getBroadcastStats#ab42441a flags:# dark:flags.0?true channel:InputChannel = stats.BroadcastStats;
stats.loadAsyncGraph#621d5fa0 flags:# token:string x:flags.0?long = StatsGraph;
stats.getMegagroupStats#dcdf8607 flags:# dark:flags.0?true channel:InputChannel = stats.MegagroupStats;
stats.getMessagePublicForwards#5f150144 channel:InputChannel msg_id:int offset:string limit:int = stats.PublicForwards;
stats.getMessageStats#b6e0a3f5 flags:# dark:flags.0?true channel:InputChannel msg_id:int = stats.MessageStats;
stats.getStoryStats#374fef40 flags:# dark:flags.0?true peer:InputPeer id:int = stats.StoryStats;
stats.getStoryPublicForwards#a6437ef6 peer:InputPeer id:int offset:string limit:int = stats.PublicForwards;
stats.getPollStats#c27dfa68 flags:# dark:flags.0?true peer:InputPeer msg_id:int = stats.PollStats;

chatlists.exportChatlistInvite#8472478e chatlist:InputChatlist title:string peers:Vector<InputPeer> = chatlists.ExportedChatlistInvite;
chatlists.deleteExportedInvite#719c5c5e chatlist:InputChatlist slug:string = Bool;
chatlists.editExportedInvite#653db63d flags:# chatlist:InputChatlist slug:string title:flags.1?string peers:flags.2?Vector<InputPeer> = ExportedChatlistInvite;
chatlists.getExportedInvites#ce03da83 chatlist:InputChatlist = chatlists.ExportedInvites;
chatlists.checkChatlistInvite#41c10fff slug:string = chatlists.ChatlistInvite;
chatlists.joinChatlistInvite#a6b1e39a slug:string peers:Vector<InputPeer> = Updates;
chatlists.getChatlistUpdates#89419521 chatlist:InputChatlist = chatlists.ChatlistUpdates;
chatlists.joinChatlistUpdates#e089f8f5 chatlist:InputChatlist peers:Vector<InputPeer> = Updates;
chatlists.hideChatlistUpdates#66e486fb chatlist:InputChatlist = Bool;
chatlists.getLeaveChatlistSuggestions#fdbcd714 chatlist:InputChatlist = Vector<Peer>;
chatlists.leaveChatlist#74fae13a chatlist:InputChatlist peers:Vector<InputPeer> = Updates;

stories.canSendStory#30eb63f0 peer:InputPeer = stories.CanSendStoryCount;
stories.sendStory#8f9e6898 flags:# pinned:flags.2?true noforwards:flags.4?true fwd_modified:flags.7?true peer:InputPeer media:InputMedia media_areas:flags.5?Vector<MediaArea> caption:flags.0?string entities:flags.1?Vector<MessageEntity> privacy_rules:Vector<InputPrivacyRule> random_id:long period:flags.3?int fwd_from_id:flags.6?InputPeer fwd_from_story:flags.6?int albums:flags.8?Vector<int> music:flags.9?InputDocument = Updates;
stories.editStory#2c63a72b flags:# peer:InputPeer id:int media:flags.0?InputMedia media_areas:flags.3?Vector<MediaArea> caption:flags.1?string entities:flags.1?Vector<MessageEntity> privacy_rules:flags.2?Vector<InputPrivacyRule> music:flags.4?InputDocument = Updates;
stories.deleteStories#ae59db5f peer:InputPeer id:Vector<int> = Vector<int>;
stories.togglePinned#9a75a1ef peer:InputPeer id:Vector<int> pinned:Bool = Vector<int>;
stories.getAllStories#eeb0d625 flags:# next:flags.1?true hidden:flags.2?true state:flags.0?string = stories.AllStories;
stories.getPinnedStories#5821a5dc peer:InputPeer offset_id:int limit:int = stories.Stories;
stories.getStoriesArchive#b4352016 peer:InputPeer offset_id:int limit:int = stories.Stories;
stories.getStoriesByID#5774ca74 peer:InputPeer id:Vector<int> = stories.Stories;
stories.toggleAllStoriesHidden#7c2557c4 hidden:Bool = Bool;
stories.readStories#a556dac8 peer:InputPeer max_id:int = Vector<int>;
stories.incrementStoryViews#b2028afb peer:InputPeer id:Vector<int> = Bool;
stories.getStoryViewsList#7ed23c57 flags:# just_contacts:flags.0?true reactions_first:flags.2?true forwards_first:flags.3?true peer:InputPeer q:flags.1?string id:int offset:string limit:int = stories.StoryViewsList;
stories.getStoriesViews#28e16cc8 peer:InputPeer id:Vector<int> = stories.StoryViews;
stories.exportStoryLink#7b8def20 peer:InputPeer id:int = ExportedStoryLink;
stories.report#19d8eb45 peer:InputPeer id:Vector<int> option:bytes message:string = ReportResult;
stories.activateStealthMode#57bbd166 flags:# past:flags.0?true future:flags.1?true = Updates;
stories.sendReaction#7fd736b2 flags:# add_to_recent:flags.0?true peer:InputPeer story_id:int reaction:Reaction = Updates;
stories.getPeerStories#2c4ada50 peer:InputPeer = stories.PeerStories;
stories.getAllReadPeerStories#9b5ae7f9 = Updates;
stories.getPeerMaxIDs#78499170 id:Vector<InputPeer> = Vector<RecentStory>;
stories.getChatsToSend#a56a8b60 = messages.Chats;
stories.togglePeerStoriesHidden#bd0415c4 peer:InputPeer hidden:Bool = Bool;
stories.getStoryReactionsList#b9b2881f flags:# forwards_first:flags.2?true peer:InputPeer id:int reaction:flags.0?Reaction offset:flags.1?string limit:int = stories.StoryReactionsList;
stories.togglePinnedToTop#b297e9b peer:InputPeer id:Vector<int> = Bool;
stories.searchPosts#d1810907 flags:# hashtag:flags.0?string area:flags.1?MediaArea peer:flags.2?InputPeer offset:string limit:int = stories.FoundStories;
stories.createAlbum#a36396e5 peer:InputPeer title:string stories:Vector<int> = StoryAlbum;
stories.updateAlbum#5e5259b6 flags:# peer:InputPeer album_id:int title:flags.0?string delete_stories:flags.1?Vector<int> add_stories:flags.2?Vector<int> order:flags.3?Vector<int> = StoryAlbum;
stories.reorderAlbums#8535fbd9 peer:InputPeer order:Vector<int> = Bool;
stories.deleteAlbum#8d3456d0 peer:InputPeer album_id:int = Bool;
stories.getAlbums#25b3eac7 peer:InputPeer hash:long = stories.Albums;
stories.getAlbumStories#ac806d61 peer:InputPeer album_id:int offset:int limit:int = stories.Stories;
stories.startLive#d069ccde flags:# pinned:flags.2?true noforwards:flags.4?true rtmp_stream:flags.5?true peer:InputPeer caption:flags.0?string entities:flags.1?Vector<MessageEntity> privacy_rules:Vector<InputPrivacyRule> random_id:long messages_enabled:flags.6?Bool send_paid_messages_stars:flags.7?long = Updates;

premium.getBoostsList#60f67660 flags:# gifts:flags.0?true peer:InputPeer offset:string limit:int = premium.BoostsList;
premium.getMyBoosts#be77b4a = premium.MyBoosts;
premium.applyBoost#6b7da746 flags:# slots:flags.0?Vector<int> peer:InputPeer = premium.MyBoosts;
premium.getBoostsStatus#42f1f61 peer:InputPeer = premium.BoostsStatus;
premium.getUserBoosts#39854d1f peer:InputPeer user_id:InputUser = premium.BoostsList;

smsjobs.isEligibleToJoin#edc39d0 = smsjobs.EligibilityToJoin;
smsjobs.join#a74ece2d = Bool;
smsjobs.leave#9898ad73 = Bool;
smsjobs.updateSettings#93fa0bf flags:# allow_international:flags.0?true = Bool;
smsjobs.getStatus#10a698e8 = smsjobs.Status;
smsjobs.getSmsJob#778d902f job_id:string = SmsJob;
smsjobs.finishJob#4f1ebf24 flags:# job_id:string error:flags.0?string = Bool;

fragment.getCollectibleInfo#be1e85ba collectible:InputCollectible = fragment.CollectibleInfo;

aicompose.createTone#4aa83913 flags:# display_author:flags.0?true emoji_id:long title:string prompt:string = AiComposeTone;
aicompose.updateTone#903bcf59 flags:# tone:InputAiComposeTone display_author:flags.0?Bool emoji_id:flags.1?long title:flags.2?string prompt:flags.3?string = AiComposeTone;
aicompose.saveTone#1782cbb1 tone:InputAiComposeTone unsave:Bool = Bool;
aicompose.deleteTone#dd39316a tone:InputAiComposeTone = Bool;
aicompose.getTone#b2e8ba03 tone:InputAiComposeTone = aicompose.Tones;
aicompose.getTones#abd59201 hash:long = aicompose.Tones;
aicompose.getToneExample#d1b4ab14 tone:InputAiComposeTone num:int = AiComposeToneExample;

communities.create#a63859ec flags:# hidden:flags.1?true title:string about:flags.0?string peer:InputPeer = Updates;
communities.togglePeerLink#736dcfea flags:# visible:flags.0?true hidden:flags.1?true deleted:flags.2?true community:InputChannel peer:InputPeer = Bool;
communities.getJoinedCommunities#a663e830 = messages.Chats;
communities.toggleCommunityCollapsedInDialogs#d766e3ea flags:# collapsed:flags.0?true community:InputChannel = Updates;
communities.getPeerLinkRequests#93773344 community:InputChannel offset:string limit:int = communities.PeerLinkRequests;
communities.togglePeerLinkRequestApproval#8c8219a8 flags:# reject:flags.0?true community:InputChannel peer:InputPeer = Bool;
communities.toggleAllPeerLinkRequestApproval#bfe3dd3d flags:# reject:flags.0?true community:InputChannel = Bool;
communities.toggleParticipantBanned#9967ad0f flags:# unban:flags.0?true community:InputChannel participant:InputPeer = Bool;
communities.getParticipantJoinedChats#f87eabab community:InputChannel participant:InputPeer = communities.ParticipantJoinedChats;

ephemeral.sendMessage#ba8d5f35 flags:# invert_media:flags.6?true welcome:flags.7?true anchor:flags.9?true noforwards:flags.10?true peer:flags.8?InputPeer receiver_id:InputUser query_id:flags.0?long message:string entities:flags.1?Vector<MessageEntity> media:flags.2?InputMedia reply_markup:flags.3?ReplyMarkup rich_message:flags.4?InputRichMessage random_id:long reply_to:flags.5?InputReplyTo = Updates;
ephemeral.deleteMessage#92f6e797 flags:# peer:flags.0?InputPeer receiver_id:InputUser id:int = Bool;
ephemeral.reportMessage#8704f2bf peer:InputPeer id:int option:bytes message:string = ReportResult;
ephemeral.getCallbackAnswer#3fa464c8 flags:# peer:InputPeer id:int data:flags.1?bytes = messages.BotCallbackAnswer;
ephemeral.editMessage#cf9c725b flags:# invert_media:flags.5?true welcome:flags.6?true peer:flags.7?InputPeer receiver_id:InputUser id:int message:flags.0?string media:flags.3?InputMedia entities:flags.1?Vector<MessageEntity> reply_markup:flags.2?ReplyMarkup rich_message:flags.4?InputRichMessage = Updates;
ephemeral.deleteWelcomeMessage#e882a9e1 peer:InputPeer id:int = Bool;
ephemeral.deleteAllWelcomeMessages#734f9721 peer:InputPeer = Bool;
ephemeral.getWelcomeMessages#db9ac18d peer:InputPeer hash:long = ephemeral.WelcomeMessages;

toncenter.performApiRequest#8d7bdd61 flags:# post:flags.0?true endpoint:string query:flags.1?string payload:flags.2?string = toncenter.ApiResponse;
toncenter.getStreamingUrl#cdaf63c7 = toncenter.StreamingUrl;

wallet.getState#ab83b33a = WalletState;
wallet.getUserAddresses#5275dfdd flags:# force:flags.0?true id:Vector<InputUser> addresses:Vector<string> = wallet.UserAddresses;
wallet.getTransactions#a8830a83 flags:# inbound:flags.0?true outbound:flags.1?true offset:string limit:int = wallet.Transactions;
wallet.getTransactionsByIDs#811ceab6 id:Vector<string> = wallet.Transactions;
wallet.getTransactionsByMsgHash#a6bb795d msg_hash:Vector<string> = wallet.Transactions;
wallet.exportSecretPhrase#9077c9ac flags:# password:flags.0?InputCheckPasswordSRP = wallet.SecretPhraseParts;
wallet.fetchEncryptedSecretPhrasePart#f53925cf token:string public_key:bytes = wallet.EncryptedSecretPhrasePart;
wallet.replaceWallet#d8c72eec flags:# wallet:InputWalletReplacement password:flags.0?InputCheckPasswordSRP = WalletState;
wallet.disableBackup#b0bf0da flags:# password:flags.0?InputCheckPasswordSRP new_public_key:flags.1?bytes proof:flags.2?WalletOwnershipProof = WalletState;
wallet.getBackupHolderDcs#d179d494 = Vector<wallet.HolderDc>;
wallet.enableBackup#4500c525 flags:# parts:Vector<bytes> new_public_key:flags.1?bytes proof:flags.2?WalletOwnershipProof = WalletState;
wallet.getProofChallenge#2025e697 = wallet.ProofChallenge;
wallet.sendTransfer#d9e5d9e2 flags:# data_normal:bytes data_gasless:flags.0?bytes user_id:InputUser random_id:long = Updates;
wallet.getGaslessInfo#742df0a1 = Updates;
wallet.getExistingWaltBalance#691f4360 = wallet.ExistingBalance;
wallet.getNfts#8c12f3a4 offset:string limit:int = wallet.NftItems;
wallet.tonConnectCreateSession#cc931046 dapp_client_id:string manifest_url:string = TonConnectSession;
wallet.tonConnectRegisterKey#338bcf1c session_id:long client_id:string = wallet.TonConnectChallenge;
wallet.tonConnectSubmitConnectResult#c2c00779 flags:# error:flags.0?true session_id:long challenge_answer:bytes body:bytes trace_id:flags.1?string = Bool;
wallet.tonConnectGetPending#11aee065 flags:# dapp_client_id:flags.0?string session_id:flags.1?long = wallet.TonConnectPending;
wallet.tonConnectClaimRequest#66b57b41 flags:# declined:flags.1?true session_id:long msg_id:int app_request_id:string challenge_answer:flags.0?bytes = Bool;
wallet.tonConnectSubmitResponse#fac67d3c flags:# session_id:long msg_id:int body:bytes trace_id:flags.0?string = Bool;
wallet.tonConnectNextEventId#775d7244 session_id:long = TonConnectNextEventId;
wallet.tonConnectCloseSession#d63f61ce flags:# session_id:long body:flags.0?bytes = Bool;
wallet.tonConnectGetSessions#a7ffb56e = wallet.TonConnectSessions;
wallet.setCardStickers#68509ac flags:# stickers:Vector<InputWalletCardSticker> bg_nft_address:flags.0?string = wallet.Card;

// LAYER 232
<?php

declare(strict_types=1);

/**
 * Connection module handling all connections to a datacenter.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use Amp\DeferredFuture;
use Amp\Future;
use danog\MadelineProto\Loop\Generic\PeriodicLoopInternal;
use danog\MadelineProto\MTProto\ConnectionState;
use danog\MadelineProto\MTProto\Container;
use danog\MadelineProto\MTProto\MTProtoOutgoingMessage;
use danog\MadelineProto\MTProto\NewAuthKey;
use danog\MadelineProto\MTProto\PermAuthKey;
use danog\MadelineProto\MTProto\SpecialMethodType;
use danog\MadelineProto\MTProtoTools\Crypt;
use danog\MadelineProto\Reactive\SimpleSubscriber;
use danog\MadelineProto\RPCError\FloodWaitError;
use danog\MadelineProto\Settings\Connection as ConnectionSettings;
use danog\MadelineProto\Stream\ContextIterator;
use Revolt\EventLoop;
use Webmozart\Assert\Assert;

use function Amp\delay;
use function count;

/**
 * Datacenter connection.
 * @internal
 * @implements SimpleSubscriber<ConnectionState>
 */
final class DataCenterConnection implements SimpleSubscriber
{
    public const READ_WEIGHT = 1;
    public const READ_WEIGHT_MEDIA = 5;
    public const WRITE_WEIGHT = 10;

    /** @deprecated */
    private ?PermAuthKey $permAuthKey;

    /**
     * Promise for connection.
     *
     */
    private Future $connectionsPromise;
    /**
     * Deferred for connection.
     *
     */
    private ?DeferredFuture $connectionsDeferred = null;
    public readonly NewAuthKey $auth;
    /**
     * Connections open to a certain DC.
     *
     * @var array<int, Connection>
     */
    private array $connections = [];
    /**
     * Connection weights.
     *
     * @var array<int, int>
     */
    private array $availableConnections = [];
    /**
     * Connection contexts.
     */
    private ?ContextIterator $ctx = null;
    /**
     * Loop to keep weights at sane value.
     */
    private ?PeriodicLoopInternal $robinLoop = null;
    /**
     * Decrement roundrobin weight by this value if busy reading.
     *
     */
    private int $decRead = 1;
    /**
     * Decrement roundrobin weight by this value if busy writing.
     *
     */
    private int $decWrite = 10;
    /**
     * Backed up messages.
     *
     */
    private array $backup = [];
    /**
     * Whether this socket has to be reconnected.
     *
     */
    private bool $needsReconnect = false;

    public function __construct(private readonly MTProto $API, private readonly int $datacenter)
    {
        $media = DataCenter::isMedia($this->datacenter);
        $this->auth = new NewAuthKey(
            $media,
            $this->API->isCdn($this->datacenter),
            $this->datacenter,
            $this->API->loginState,
            $media ? $this->API->datacenter->getDataCenterConnection(-$this->datacenter)->auth : null
        );
        $this->auth->connectionState->subscribe($this);
    }

    public function importFromLegacy(self $legacy): void
    {
        $this->auth->setAuthKey($legacy->permAuthKey?->getAuthKey());
    }

    /**
     * @psalm-pure
     */
    public function __sleep()
    {
        return ['auth', 'API', 'datacenter'];
    }

    #[\Override]
    public function onSimpleStateChange($state): void
    {
        try {
            $this->initAuthorization($state);
        } catch (SecurityException $e) {
            throw $e;
        } catch (\Throwable $e) {
            throw new SecurityException("An error occurred while handling state transition to {$state->name} in DC {$this->datacenter}: ".$e->getMessage(), 0, $e);
        }
    }

    /**
     * Indicate if this socket needs to be reconnected.
     *
     * @param boolean $needsReconnect Whether the socket has to be reconnected
     *
     * @psalm-external-mutation-free
     */
    public function needReconnect(bool $needsReconnect): void
    {
        $this->needsReconnect = $needsReconnect;
    }
    /**
     * Whether this sockets needs to be reconnected.
     */
    public function shouldReconnect(): bool
    {
        return $this->needsReconnect;
    }
    /**
     * @psalm-mutation-free
     */
    public function getCtxs(): ContextIterator
    {
        \assert($this->ctx !== null);
        return $this->ctx;
    }
    private function initAuthorization(ConnectionState $state): void
    {
        if (!isset($this->connectionsPromise)) {
            $this->API->datacenter->getDataCenterConnection($this->datacenter);
        }
        $logger = $this->API->logger;
        $this->waitGetConnection();
        $connection = $this->getAuthConnection();
        $this->createSession();

        // Skip old states in case of unexpected server-side abort back to the unencrypted state.
        if ($state !== $this->auth->connectionState->getState()) {
            $logger->logger("Skipping outdated auth key transition to {$state->name} in DC {$this->datacenter}", Logger::NOTICE);
            return;
        }
        $logger->logger("Handling auth key transition to {$state->name} in DC {$this->datacenter}", Logger::NOTICE);

        if ($state === ConnectionState::UNENCRYPTED_NO_PERMANENT) {
            Assert::false($this->auth->isMedia);
            Assert::false($this->auth->isCdn);
            $logger->logger(\sprintf('Generating permanent authorization key for DC %s...', $this->datacenter), Logger::NOTICE);
            $connection->createAuthKey(false);
        } elseif ($state === ConnectionState::UNENCRYPTED) {
            $logger->logger(\sprintf('Generating temporary authorization key for DC %s...', $this->datacenter), Logger::NOTICE);
            $connection->createAuthKey(true);
        } elseif ($state === ConnectionState::ENCRYPTED_NOT_BOUND) {
            $expires_in = MTProto::PFS_DURATION;
            for ($retry_id_total = 1; $retry_id_total <= $this->API->settings->getAuth()->getMaxAuthTries(); $retry_id_total++) {
                if ($this->auth->connectionState->getState() !== ConnectionState::ENCRYPTED_NOT_BOUND) {
                    // A binding answered with CONNECTION_NOT_INITED (ResponseHandler): an earlier binding went
                    // through but its answer was lost (e.g. with a session reset), the key is bound.
                    // Or the key was dropped: the next transition takes over either way.
                    $logger->logger("Not binding authorization keys anymore in DC {$this->datacenter}, now in state {$this->auth->connectionState->getState()->name}", Logger::NOTICE);
                    return;
                }
                try {
                    $logger->logger('Binding authorization keys...', Logger::VERBOSE);
                    $nonce = Tools::random(8);
                    $expires_at = time() + $expires_in;
                    $temp_auth_key_id = $this->auth->getTempId();
                    $perm_auth_key_id = $this->auth->getID();
                    $temp_session_id = $connection->session_id;
                    $message_data = ($this->API->getTL()->serializeObject(['type' => ''], ['_' => 'bind_auth_key_inner', 'nonce' => $nonce, 'temp_auth_key_id' => $temp_auth_key_id, 'perm_auth_key_id' => $perm_auth_key_id, 'temp_session_id' => $temp_session_id, 'expires_at' => $expires_at], 'bindTempAuthKey_inner'));
                    $message_id = $connection->msgIdHandler->generateMessageId();
                    $seq_no = 0;
                    $encrypted_data = Tools::random(16).Tools::packSignedLong($message_id).pack('VV', $seq_no, \strlen($message_data)).$message_data;
                    $message_key = substr(sha1($encrypted_data, true), -16);
                    $padding = Tools::random(Tools::posmod(-\strlen($encrypted_data), 16));
                    [$aes_key, $aes_iv] = $this->auth->pfsKdf($message_key);
                    $encrypted_message = $this->auth->getID().$message_key.Crypt::igeEncrypt($encrypted_data.$padding, $aes_key, $aes_iv);
                    $res = $connection->methodCallAsyncRead('auth.bindTempAuthKey', ['perm_auth_key_id' => $perm_auth_key_id, 'nonce' => $nonce, 'expires_at' => $expires_at, 'encrypted_message' => $encrypted_message, 'madelineMsgId' => $message_id, 'specialMethodType' => SpecialMethodType::UNAUTHED_METHOD]);
                    if ($res === true) {
                        $logger->logger("Bound temporary and permanent authorization keys, DC {$this->datacenter}", Logger::NOTICE);
                        $this->auth->bind();
                        return;
                    }
                } catch (SecurityException $e) {
                    $logger->logger('An exception occurred while generating the authorization key: '.$e->getMessage().' Retrying (try number '.$retry_id_total.')...', Logger::WARNING);
                } catch (Exception $e) {
                    $logger->logger('An exception occurred while generating the authorization key: '.$e->getMessage().' Retrying (try number '.$retry_id_total.')...', Logger::WARNING);
                } catch (RPCErrorException $e) {
                    $logger->logger('An RPCErrorException occurred while generating the authorization key: '.$e->getMessage().' Retrying (try number '.$retry_id_total.')...', Logger::WARNING);
                }
            }
            throw new SecurityException('An error occurred while binding temporary and permanent authorization keys.');
        } elseif ($state === ConnectionState::ENCRYPTED_NOT_INITED) {
            $this->API->logger('Writing client info (also executing help.getConfig)...', Logger::NOTICE);
            if ($this->auth->isCdn) {
                $message = $connection->mainPendingOutgoing->peek();
                Assert::notNull($message);
                $method = $message->getSerializedBody();
            } else {
                $method = $this->API->getTL()->serializeMethod(
                    'help.getConfig',
                    []
                );
            }
            $connection->methodCallAsyncRead('invokeWithLayer', [
                'layer' => $this->API->settings->getSchema()->getLayer(),
                'query' => $this->API->getTL()->serializeMethod(
                    'initConnection',
                    [
                        'api_id' => $this->API->settings->getAppInfo()->getApiId(),
                        'api_hash' => $this->API->settings->getAppInfo()->getApiHash(),
                        'device_model' => !$this->auth->isCdn ? $this->API->settings->getAppInfo()->getDeviceModel() : 'n/a',
                        'system_version' => !$this->auth->isCdn ? $this->API->settings->getAppInfo()->getSystemVersion() : 'n/a',
                        'app_version' => $this->API->settings->getAppInfo()->getAppVersion(),
                        'system_lang_code' => $this->API->settings->getAppInfo()->getSystemLangCode(),
                        'lang_code' => $this->API->settings->getAppInfo()->getLangCode(),
                        'lang_pack' => $this->API->settings->getAppInfo()->getLangPack(),
                        'proxy' => $connection->getInputClientProxy(),
                        'query' => $method,
                    ]
                ),
                'specialMethodType' => SpecialMethodType::UNAUTHED_METHOD,
            ]);
            $this->auth->init();
        } elseif ($state === ConnectionState::ENCRYPTED_NOT_AUTHED) {
            Assert::eq($this->API->loginState->getState()->state, API::LOGGED_IN);
            $authed = $this->API->loginState->getState()->authorizedDc;
            Assert::notNull($authed);

            $logger->logger('Trying to copy authorization from DC '.$authed.' to DC '.$this->datacenter);
            $authorized_socket =  $this->API->datacenter->getDataCenterConnection($authed);
            $authorized_socket->waitGetConnection();
            for ($attempt = 1; ;) {
                try {
                    $e = $authorized_socket->getAuthConnection()->methodCallAsyncRead(
                        'auth.exportAuthorization',
                        ['dc_id' => $this->datacenter % 10_000, 'specialMethodType' => SpecialMethodType::USER_RELATED]
                    );
                    $e['specialMethodType'] = SpecialMethodType::UNAUTHED_METHOD;
                    $connection->methodCallAsyncRead('auth.importAuthorization', $e);
                    break;
                } catch (FloodWaitError $error) {
                    // Many processes of the account copying its authorization at once (CI runs creating sessions)
                    // get FLOOD_WAITs longer than the flood timeout: wait them out, as this runs in the transition of
                    // the state of the connection, where an exception kills the process, and the connection can't be
                    // used without the authorization anyway. Not through the floodWaitLimit of the calls: their
                    // timeout (the RPC drop timeout) would expire first.
                    $logger->logger("Got {$error->rpc} copying the authorization to DC {$this->datacenter}, waiting {$error->waitTime} seconds", Logger::WARNING);
                    delay($error->waitTime);
                } catch (RPCErrorException $error) {
                    // A newer export of the same account (another process of the account copying its authorization
                    // at the same time, for example) invalidates the exported bytes: export them again, after a
                    // random delay, so that the processes don't keep invalidating each other's.
                    if ($error->rpc !== 'AUTH_BYTES_INVALID' || $attempt++ === 5) {
                        throw $error;
                    }
                    $logger->logger("The exported authorization for DC {$this->datacenter} was invalidated, exporting it again", Logger::WARNING);
                    delay(random_int(100, 2000) / 1000);
                }
            }
            $this->auth->authorize();
        }

        $logger->logger("Finished auth key transition to {$state->name} in DC {$this->datacenter}", Logger::NOTICE);
    }

    /**
     * Reset MTProto sessions.
     */
    public function resetSession(string $why): void
    {
        foreach ($this->connections as $socket) {
            $socket->resetSession($why);
        }
    }
    /**
     * Create MTProto sessions if needed.
     */
    public function createSession(): void
    {
        foreach ($this->connections as $socket) {
            $socket->createSession();
        }
    }
    /**
     * Has connection context?
     *
     * @psalm-mutation-free
     */
    public function hasCtx(): bool
    {
        return isset($this->ctx);
    }
    /**
     * Connect function.
     *
     * @param int $id Optional connection ID to reconnect
     */
    public function connect(int $id = -1): void
    {
        $media = $this->auth->isMedia || $this->auth->isCdn;
        if ($media) {
            if (!$this->robinLoop) {
                $this->robinLoop = new PeriodicLoopInternal(
                    $this->API,
                    $this->even(...),
                    "robin loop DC {$this->datacenter}",
                    $this->API->getSettings()->getConnection()->getRobinPeriod()
                );
            }
            $this->robinLoop->start();
        }
        $this->decRead = $media ? self::READ_WEIGHT_MEDIA : self::READ_WEIGHT;
        $this->decWrite = self::WRITE_WEIGHT;
        if ($id === -1 || !isset($this->connections[$id])) {
            if ($this->connections) {
                $this->API->logger('Already connected!', Logger::WARNING);
                return;
            }
            $f = new DeferredFuture;
            $this->connectionsPromise = $f->getFuture();
            $this->connectMore(1);
            $f->complete();
            if (isset($this->connectionsDeferred)) {
                $connectionsDeferred = $this->connectionsDeferred;
                $this->connectionsDeferred = null;
                $connectionsDeferred->complete();
            }
            $this->restoreBackup();
        } else {
            $this->availableConnections[$id] = 0;
            $this->connections[$id]->setExtra($this, $this->datacenter, $id);
        }
    }
    /**
     * Connect to the DC using count more sockets.
     *
     * @param integer $count Number of sockets to open
     *
     * @psalm-external-mutation-free
     */
    private function connectMore(int $count): void
    {
        $count += $previousCount = \count($this->connections);
        for ($x = $previousCount; $x < $count; $x++) {
            $connection = new Connection();
            $connection->setExtra($this, $this->datacenter, $x);
            $this->connections[$x] = $connection;
            $this->availableConnections[$x] = 0;
        }
    }
    /**
     * Signal that a connection ID disconnected.
     *
     * @param integer $id Connection ID
     */
    public function signalDisconnect(int $id): void
    {
        $backup = $this->connections[$id]->backupSession();
        $list = '';
        foreach ($backup as $message) {
            $message->unlink();
            $list .= $message->constructor;
            $list .= ', ';
        }
        $this->API->logger("Backed up {$list} from DC {$this->datacenter}.{$id}");
        $this->backup = array_merge($this->backup, $backup);
        unset($this->connections[$id], $this->availableConnections[$id]);
    }
    /**
     * Close all connections to DC.
     */
    public function disconnect(): void
    {
        $this->connectionsDeferred = new DeferredFuture();
        $this->connectionsPromise = $this->connectionsDeferred->getFuture();
        if (!isset($this->ctx)) {
            return;
        }
        $this->API->logger("Disconnecting from shared DC {$this->datacenter}");
        if ($this->robinLoop) {
            $this->robinLoop->stop();
            $this->robinLoop = null;
        }
        $before = \count($this->backup);
        foreach ($this->connections as $connection) {
            $connection->disconnect();
        }
        $count = \count($this->backup) - $before;
        $this->API->logger("Backed up {$count}, added to {$before} existing messages) from DC {$this->datacenter}");
        $this->connections = [];
        $this->availableConnections = [];
    }
    /**
     * Reconnect to DC.
     */
    public function reconnect(): void
    {
        $this->API->logger("Reconnecting shared DC {$this->datacenter}");
        $this->disconnect();
        $this->connect();
    }
    /**
     * Restore backed up messages.
     */
    private function restoreBackup(): void
    {
        $backup = $this->backup;
        $this->backup = [];
        $count = \count($backup);
        $this->API->logger("Restoring {$count} messages to DC {$this->datacenter}");
        /** @var MTProtoOutgoingMessage */
        foreach ($backup as $message) {
            if ($message instanceof Container || $message->hasReply()) {
                continue;
            }
            if (!$message->resetForResend()) {
                continue;
            }
            $message->connection = $connection = $this->getConnection();
            $this->API->logger("Restoring $message to DC {$this->datacenter}");
            EventLoop::queue($connection->sendMessage(...), $message);
        }
    }
    /**
     * Get connection for authorization.
     *
     * @psalm-mutation-free
     */
    private function getAuthConnection(): Connection
    {
        return $this->connections[0];
    }
    /**
     * Check if any connection is available.
     *
     * @param integer $id Connection ID
     *
     * @psalm-mutation-free
     */
    public function hasConnection(int $id = -1): bool|int
    {
        return $id < 0 ? \count($this->connections) : isset($this->connections[$id]);
    }
    /**
     * Get best socket in round robin, asynchronously.
     */
    public function waitGetConnection(): Connection
    {
        if (empty($this->availableConnections)) {
            $this->connectionsPromise->await();
        }
        return $this->getConnection();
    }
    /**
     * Get best socket in round robin.
     *
     * @param integer $id Connection ID, for manual fetching
     *
     * @psalm-external-mutation-free
     */
    public function getConnection(int $id = -1): Connection
    {
        if ($id >= 0) {
            return $this->connections[$id];
        }
        if (\count($this->availableConnections) <= 1) {
            return $this->connections[0];
        }
        $max = max($this->availableConnections);
        $key = array_search($max, $this->availableConnections, true);
        // Decrease to implement round robin
        $this->availableConnections[$key]--;
        return $this->connections[$key];
    }
    /**
     * Even out round robin values.
     */
    public function even(): void
    {
        if (!$this->availableConnections) {
            return;
        }
        $min = min($this->availableConnections);
        if ($min < 50) {
            foreach ($this->availableConnections as &$count) {
                $count += 50;
            }
        } elseif ($min < 100) {
            $max = $this->auth->isMedia || $this->auth->isCdn ? $this->API->getSettings()->getConnection()->getMaxMediaSocketCount() : 1;
            if (\count($this->availableConnections) < $max) {
                $this->connectMore(2);
            } else {
                foreach ($this->availableConnections as &$value) {
                    $value += 1000;
                }
            }
        }
    }
    /**
     * Indicate that one of the sockets is busy reading.
     *
     * @param boolean $reading Whether we're busy reading
     * @param int     $x       Connection ID
     *
     * @psalm-external-mutation-free
     */
    public function reading(bool $reading, int $x): void
    {
        if (!isset($this->availableConnections[$x])) {
            return;
        }
        $this->availableConnections[$x] += $reading ? -$this->decRead : $this->decRead;
    }
    /**
     * Indicate that one of the sockets is busy writing.
     *
     * @param boolean $writing Whether we're busy writing
     * @param int     $x       Connection ID
     *
     * @psalm-external-mutation-free
     */
    public function writing(bool $writing, int $x): void
    {
        if (!isset($this->availableConnections[$x])) {
            return;
        }
        $this->availableConnections[$x] += $writing ? -$this->decWrite : $this->decWrite;
    }
    /**
     * @psalm-external-mutation-free
     */
    public function setCtx(ContextIterator $ctx): void
    {
        $this->ctx = $ctx;
    }
    /**
     * Get main instance.
     */
    public function getExtra(): MTProto
    {
        return $this->API;
    }
    /**
     * Get DC-specific settings.
     *
     * @psalm-mutation-free
     */
    public function getSettings(): ConnectionSettings
    {
        return $this->API->getSettings()->getConnection();
    }
    /**
     * Get global settings.
     *
     * @psalm-mutation-free
     */
    public function getGenericSettings(): Settings
    {
        return $this->API->getSettings();
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use AssertionError;
use danog\Decoder\FileId;
use danog\Decoder\FileIdType;
use danog\MadelineProto\EventHandler\Media;
use danog\MadelineProto\EventHandler\Media\AbstractSticker;
use danog\MadelineProto\EventHandler\Media\Audio;
use danog\MadelineProto\EventHandler\Media\Document;
use danog\MadelineProto\EventHandler\Media\Gif;
use danog\MadelineProto\EventHandler\Media\Photo;
use danog\MadelineProto\EventHandler\Media\RoundVideo;
use danog\MadelineProto\EventHandler\Media\Video;
use danog\MadelineProto\EventHandler\Media\Voice;

/**
 * Indicates a bot API file ID to upload using sendDocument, sendPhoto etc...
 */
final class BotApiFileId
{
    /**
     * @param string  $fileId    The file ID
     * @param int<1, max> $size      The file size
     * @param string  $fileName  The original file name
     * @param bool    $protected Whether the original file is protected
     *
     * @psalm-pure
     */
    public function __construct(
        public readonly string $fileId,
        public readonly int $size,
        public readonly string $fileName,
        public readonly bool $protected
    ) {
        if ($size <= 0) {
            throw new AssertionError("The specified size must be >= 0!");
        }
    }

    /**
     * @internal
     *
     * @return class-string<Media>
     */
    public function getTypeClass(): string
    {
        $f = FileId::fromBotAPI($this->fileId);
        return match ($f->type) {
            FileIdType::PHOTO => Photo::class,
            FileIdType::VOICE => Voice::class,
            FileIdType::VIDEO => Video::class,
            FileIdType::DOCUMENT => Document::class,
            FileIdType::STICKER => AbstractSticker::class,
            FileIdType::VIDEO_NOTE => RoundVideo::class,
            FileIdType::AUDIO => Audio::class,
            FileIdType::ANIMATION => Gif::class,
            default => throw new AssertionError("Cannot use bot API file ID of type ".$f->type->value)
        };
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use Psr\Log\AbstractLogger;
use Psr\Log\InvalidArgumentException;
use Psr\Log\LogLevel;

/**
 * PSR-3 wrapper for MadelineProto's Logger.
 */
final class PsrLogger extends AbstractLogger
{
    private const LEVEL_MAP = [
        LogLevel::EMERGENCY => Logger::LEVEL_FATAL,
        LogLevel::ALERT => Logger::LEVEL_FATAL,
        LogLevel::CRITICAL => Logger::LEVEL_FATAL,
        LogLevel::ERROR => Logger::LEVEL_ERROR,
        LogLevel::WARNING => Logger::LEVEL_WARNING,
        LogLevel::NOTICE => Logger::LEVEL_NOTICE,
        LogLevel::INFO => Logger::LEVEL_VERBOSE,
        LogLevel::DEBUG => Logger::LEVEL_ULTRA_VERBOSE,
    ];
    /**
     * Logger.
     */
    private Logger $logger;
    /**
     * Constructor.
     *
     * @psalm-mutation-free
     */
    public function __construct(Logger $logger)
    {
        $this->logger = $logger;
    }
    /**
     * Logs with an arbitrary level.
     *
     * @param  array<mixed>             $context
     * @throws InvalidArgumentException
     */
    #[\Override]
    public function log($level, $message, array $context = []): void
    {
        $this->logger->logger($message, self::LEVEL_MAP[$level]);
    }
}
<?php declare(strict_types=1);

namespace danog\MadelineProto;

use danog\AsyncOrm\Annotations\OrmMappedArray;
use danog\AsyncOrm\DbAutoProperties;
use danog\AsyncOrm\Driver\MemoryArray as DriverMemoryArray;
use danog\MadelineProto\Db\CachedArray;
use danog\MadelineProto\Db\MemoryArray;
use ReflectionClass;

/** @internal */
trait LegacyMigrator
{
    use DbAutoProperties;

    /** @return list<\ReflectionProperty> */
    private function getDbAutoProperties(): array
    {
        $res = [];
        $closure = function (string $propName): void {
            if (isset($this->{$propName})) {
                if ($this->{$propName} instanceof CachedArray) {
                    unset($this->{$propName});
                } elseif ($this->{$propName} instanceof MemoryArray) {
                    $this->{$propName} = new DriverMemoryArray($this->{$propName}->getArrayCopy());
                }
            }
        };
        foreach ((new ReflectionClass(static::class))->getProperties() as $property) {
            $attr = $property->getAttributes(OrmMappedArray::class);
            if (!$attr) {
                continue;
            }
            $closure->bindTo($this, $property->getDeclaringClass()->getName())($property->getName());
            $res []= $property;
        }
        return $res;
    }
}
<?php

declare(strict_types=1);

/**
 * API module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use Amp\CancelledException;
use Amp\DeferredFuture;
use Amp\Future;
use Amp\Future\UnhandledFutureError;
use Amp\Ipc\Sync\ChannelledSocket;
use Amp\SignalException;
use Amp\TimeoutException;
use danog\MadelineProto\ApiWrappers\Start;
use danog\MadelineProto\Ipc\AlreadyServedException;
use danog\MadelineProto\Ipc\Client;
use danog\MadelineProto\Ipc\Server;
use danog\MadelineProto\Settings\Ipc as SettingsIpc;
use danog\MadelineProto\Settings\Logger as SettingsLogger;
use danog\MadelineProto\Settings\Templates;
use danog\MadelineProto\WebUI\WebUI;
use Revolt\EventLoop;
use Revolt\EventLoop\UncaughtThrowable;
use Throwable;
use Webmozart\Assert\Assert;

use function Amp\async;
use function Amp\Future\await;
use function Amp\Future\awaitFirst;

/**
 * Main API wrapper for MadelineProto.
 */
final class API extends AbstractAPI
{
    /**
     * Release version.
     *
     * @var string
     */
    public const RELEASE = '8.12.0';
    /**
     * We're not logged in.
     *
     * @var int
     */
    public const NOT_LOGGED_IN = 0;
    /**
     * We're waiting for the login code.
     *
     * @var int
     */
    public const WAITING_CODE = 1;
    /**
     * We're waiting for parameters to sign up.
     *
     * @var int
     */
    public const WAITING_SIGNUP = -1;
    /**
     * We're waiting for the 2FA password.
     *
     * @var int
     */
    public const WAITING_PASSWORD = 2;
    /**
     * We're logged in.
     *
     * @var int
     */
    public const LOGGED_IN = 3;
    /**
     * We're logged out, the session will be deleted ASAP.
     *
     * @var int
     */
    public const LOGGED_OUT = 4;
    /**
     * This peer is a user.
     *
     * @var string
     */
    public const PEER_TYPE_USER = 'user';
    /**
     * This peer is a bot.
     *
     * @var string
     */
    public const PEER_TYPE_BOT = 'bot';
    /**
     * This peer is a normal group.
     *
     * @var string
     */
    public const PEER_TYPE_GROUP = 'chat';
    /**
     * This peer is a supergroup.
     *
     * @var string
     */
    public const PEER_TYPE_SUPERGROUP = 'supergroup';
    /**
     * This peer is a channel.
     *
     * @var string
     */
    public const PEER_TYPE_CHANNEL = 'channel';
    /**
     * This peer is a community.
     *
     * @var string
     */
    public const PEER_TYPE_COMMUNITY = 'community';
    /**
     * Whether to generate only peer information.
     */
    public const INFO_TYPE_PEER = 0;
    /**
     * Whether to generate only constructor information.
     */
    public const INFO_TYPE_CONSTRUCTOR = 1;
    /**
     * Whether to generate only ID information.
     */
    public const INFO_TYPE_ID = 2;
    /**
     * Whether to generate all information.
     */
    public const INFO_TYPE_ALL = 3;
    /**
     * Whether to generate all usernames.
     */
    public const INFO_TYPE_USERNAMES = 4;
    /**
     * Whether to generate just type info.
     */
    public const INFO_TYPE_TYPE = 5;

    use Start;
    /**
     * Session paths.
     *
     * @internal
     */
    private SessionPaths $session;

    /**
     * Unlock callback.
     *
     * @var ?callable
     */
    private $unlock = null;

    /**
     * Constructor function.
     *
     * @param string           $session  Session name
     * @param SettingsAbstract $settings Settings
     */
    public function __construct(string $session, ?SettingsAbstract $settings = null)
    {
        Magic::start(light: true);
        $settings ??= new SettingsEmpty;
        // The assets of the web UI (scripts, animations...), requested by its pages from this same script
        if (PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg' && isset($_GET[WebUI::ASSET_PARAMETER])) {
            $templates = match (true) {
                $settings instanceof Settings => $settings->getTemplates(),
                $settings instanceof Templates => $settings,
                default => new Templates,
            };
            if (WebUI::serveAsset($templates)) {
                die;
            }
        }
        $this->session = new SessionPaths($session);
        $this->wrapper = new APIWrapper($this->session);
        $this->exportNamespaces();

        Logger::constructorFromSettings($settings instanceof Settings
            ? $settings->getLogger()
            : ($settings instanceof SettingsLogger ? $settings : new SettingsLogger));

        if ($this->connectToMadelineProto($settings)) {
            if (!$settings instanceof SettingsEmpty) {
                EventLoop::queue($this->updateSettings(...), $settings);
            }
            return; // OK
        }

        if (!$settings instanceof Settings) {
            $newSettings = new Settings;
            $newSettings->merge($settings);
            $settings = $newSettings;
        }

        $appInfo = $settings->getAppInfo();
        if (!$appInfo->hasApiInfo()) {
            if (!$appInfo->getShowPrompt()) {
                throw new Exception("No API ID or API hash was provided, please specify them in the settings!");
            }
            $app = $this->APIStart($settings);
            if (!$app) {
                die();
            }
            $appInfo->setApiId($app['api_id']);
            $appInfo->setApiHash($app['api_hash']);
        }
        // Set without finishing the web setup (in the settings, for example): drop its my.telegram.org login
        $this->session->storeApiSetupState(null);
        $this->wrapper->setAPI(new MTProto($settings, $this->wrapper));
        $this->wrapper->logger('Prompting initial serialization...');
        $this->wrapper->serialize();
        $this->wrapper->logger('Done initial serialization!');
        $this->destruct();
        if (!$this->connectToMadelineProto($settings)) {
            throw new Exception("Could not start IPC server!");
        }

        $this->wrapper->logger(Lang::$current_lang['madelineproto_ready'], Logger::NOTICE);
    }

    /**
     * Reconnect to full instance.
     */
    #[\Override]
    protected function reconnectFull(): bool
    {
        if ($this->wrapper->getAPI() instanceof Client) {
            $this->wrapper->logger('Restarting to full instance...');
            try {
                if (!isset($_GET['MadelineSelfRestart']) && (($this->hasEventHandler()) || !($this->isIpcWorker()))) {
                    $this->wrapper->logger('Restarting to full instance: the bot is already running!');
                    Tools::closeConnection($this->getWebMessage(Lang::$current_lang['botAlreadyRunning']));
                    return false;
                }
                $this->wrapper->logger('Restarting to full instance: stopping IPC server...');
                $this->wrapper->getAPI()->stopIpcServer();
            } catch (SecurityException|SignalException $e) {
                throw $e;
            } catch (Throwable $e) {
                if ($e instanceof UncaughtThrowable) {
                    $e = $e->getPrevious();
                    if ($e instanceof SecurityException || $e instanceof SignalException) {
                        throw $e;
                    }
                }
                $this->wrapper->logger("Restarting to full instance: error $e");
            }
            $this->wrapper->logger('Restarting to full instance: reconnecting...');
            $cancel = new DeferredFuture;
            $cb = function () use ($cancel, &$cb): void {
                [$result] = Serialization::tryConnect($this->session->getIpcPath(), $cancel->getFuture());
                if ($result instanceof ChannelledSocket) {
                    try {
                        if (!$this->wrapper->getAPI() instanceof Client) {
                            $this->wrapper->logger('Restarting to full instance (again): the bot is already running!');
                            $result->disconnect();
                            return;
                        }
                        $this->wrapper->logger('Restarting to full instance (again): sending shutdown signal!');
                        $result->send(Server::SHUTDOWN);
                        $result->disconnect();
                    } catch (SecurityException|SignalException $e) {
                        throw $e;
                    } catch (Throwable $e) {
                        if ($e instanceof UncaughtThrowable) {
                            $e = $e->getPrevious();
                            if ($e instanceof SecurityException || $e instanceof SignalException) {
                                throw $e;
                            }
                        }
                        $this->wrapper->logger("Restarting to full instance: error in stop loop $e");
                    }
                    // Repeated until we get the session, but not right away: it spun as long as a server accepted connections without stopping
                    EventLoop::delay(1.0, static fn () => $cb());
                }
            };
            EventLoop::queue($cb);
            $this->connectToMadelineProto(new SettingsEmpty, true);
            $cancel->complete(new Exception('Connected!'));
        }
        return true;
    }
    /**
     * Connect to MadelineProto.
     *
     * @param SettingsAbstract $settings  Settings
     * @param bool             $forceFull Whether to force full initialization
     */
    protected function connectToMadelineProto(SettingsAbstract $settings, bool $forceFull = false, bool $tryReconnect = true): bool
    {
        if ($settings instanceof SettingsIpc) {
            $forceFull = $forceFull || $settings->getSlow();
        } elseif ($settings instanceof Settings) {
            $forceFull = $forceFull || $settings->getIpc()->getSlow();
        }
        $selfRestart = !$forceFull && isset($_GET['MadelineSelfRestart']) && !Magic::$altervista;
        $forceFull = $forceFull || $selfRestart || Magic::$altervista;

        $future = async(
            Serialization::unserialize(...),
            $this->session,
            $settings,
            $forceFull,
            $selfRestart,
        );
        while (true) {
            try {
                [$unserialized, $this->unlock] = $future->await(Tools::getTimeoutCancellation(30.0, "Timeout during session unserialization!"));
                break;
            } catch (CancelledException $e) {
                if (!$e->getPrevious() instanceof TimeoutException) {
                    throw $e;
                }
                // The lock holder is alive (a lock dies with its process): it's loading the session, which
                // can take a while with a big database, and serves it once it's done, or it's shutting down
                // to let us in. Either way, giving up now would fail a startup that is going well.
                if (Serialization::isLockedByAnotherProcess($this->session)) {
                    Logger::log('The session is still locked by another process (loading or releasing it), waiting...');
                    continue;
                }
                $future->ignore();
                [$unserialized, $this->unlock] = [0, null];
                break;
            }
        }

        if ($unserialized === 0) {
            // Timeout
            throw new Exception(Lang::$current_lang['could_not_connect_to_MadelineProto']);
        } elseif ($unserialized instanceof Throwable) {
            // IPC server error
            throw $unserialized;
        } elseif ($unserialized instanceof ChannelledSocket) {
            if (Magic::$isIpcWorker) {
                // A worker is only useful as the server: as a client, it would wait for a shutdown forever.
                $unserialized->disconnect();
                throw new AlreadyServedException("Another process is already serving the session {$this->session->getSessionDirectoryPath()}");
            }
            // Success, IPC client
            Server::warnIfOverTcp($this->session);
            $this->wrapper->setAPI($client = new Client($unserialized, $this->session, Logger::$default));
            if (\defined('MADELINE_PHAR') && \defined('MADELINE_PHAR_VERSION') && \defined('MADELINE_PHP')) {
                // Short-lived clients (like dl.php) exit before completing the update check, and their phars may be
                // in another directory than the worker's: the worker brings both to the newest release.
                try {
                    $client->syncIpcClientPhar(self::RELEASE, \MADELINE_PHAR, \MADELINE_PHAR_VERSION, \MADELINE_PHP);
                } catch (Throwable $e) {
                    // Workers older than this method
                    Logger::log("Could not sync the phar with the IPC server: $e", Logger::NOTICE);
                }
            }
            return true;
        } elseif ($unserialized) {
            // Success, full session
            $this->wrapper->getAPI()?->unreference();
            $this->wrapper = $unserialized;
            $this->wrapper->setSession($this->session);
            $this->exportNamespaces();
            if ($this->wrapper->getAPI()) {
                unset($unserialized);

                if ($settings instanceof SettingsIpc) {
                    $settings = new SettingsEmpty;
                }
                try {
                    $this->wrapper->getAPI()->wakeup($settings, $this->wrapper);
                } catch (Throwable $e) {
                    // Not saved at shutdown (by serializeAll() or the destructor): the session on disk is the one
                    // that failed to wake up, a half-initialized instance must not overwrite it.
                    $this->wrapper->setAPI(null);
                    throw $e;
                }
                $this->wrapper->logger(Lang::$current_lang['madelineproto_ready'], Logger::NOTICE);
                return true;
            }
        }
        return false;
    }
    /**
     * Wakeup function.
     */
    public function __wakeup(): void
    {
        $this->__construct($this->session->getSessionDirectoryPath());
    }
    /**
     * @psalm-pure
     */
    #[\Override]
    public function __sleep(): array
    {
        return ['session'];
    }
    /**
     * @var array<Future<null>>
     */
    private static array $destructors = [];
    /**
     * @internal
     */
    public static function finalize(): void
    {
        if (self::$destructors) {
            await(self::$destructors);
        }
    }

    private function destruct(int|null $id = null): void
    {
        $this->wrapper->logger('Shutting down MadelineProto ('.static::class.')');
        $this->wrapper->getAPI()?->unreference();
        if (isset($this->wrapper)) {
            $this->wrapper->logger('Prompting final serialization...');
            $this->wrapper->serialize();
            $this->wrapper->logger('Done final serialization!');
        }
        if ($this->unlock) {
            ($this->unlock)();
        }
        if ($id !== null) {
            unset(self::$destructors[$id]);
        }
    }
    /**
     * Destruct function.
     *
     * @internal
     */
    public function __destruct()
    {
        $id = \count(self::$destructors);
        self::$destructors[$id] = async($this->destruct(...), $id);
    }

    /**
     * Start multiple instances of MadelineProto and the event handlers (enables async).
     *
     * @param array<API>                                                   $instances    Instances of madeline
     * @param array<class-string<EventHandler>>|class-string<EventHandler> $eventHandler Event handler(s)
     */
    public static function startAndLoopMulti(array $instances, array|string $eventHandler): void
    {
        if (\is_string($eventHandler)) {
            Assert::classExists($eventHandler);
            $eventHandler::cachePlugins($eventHandler);
            $eventHandler = array_fill_keys(array_keys($instances), $eventHandler);
        } else {
            Assert::notEmpty($eventHandler);
            Assert::allClassExists($eventHandler);
            foreach ($eventHandler as $c) {
                $c::cachePlugins($c);
            }
        }

        $errors = [];
        $started = array_fill_keys(array_keys($instances), false);
        $instanceOne = array_values($instances)[0];

        $prev = EventLoop::getErrorHandler();
        EventLoop::setErrorHandler(
            $cb = static function (Throwable $e) use ($instanceOne, &$errors, &$started, $eventHandler): void {
                if ($e instanceof UnhandledFutureError) {
                    $e = $e->getPrevious();
                }
                if ($e instanceof SecurityException || $e instanceof SignalException) {
                    throw $e;
                }
                if (str_starts_with($e->getMessage(), 'Could not connect to DC ')) {
                    throw $e;
                }
                $t = time();
                $errors = [$t => $errors[$t] ?? 0];
                $errors[$t]++;
                if ($errors[$t] > 10 && array_sum($started) !== \count($eventHandler)) {
                    $instanceOne->wrapper->logger('More than 10 errors in a second and not inited, exiting!', Logger::FATAL_ERROR);
                    return;
                }
                $eOrig = (string) $e;
                $e = Tools::taintEscape((string) $e);
                echo $e;
                $instanceOne->wrapper->logger($eOrig, Logger::FATAL_ERROR);
                $instanceOne->report("Surfaced: $eOrig");
            }
        );

        try {
            $promises = [];
            foreach ($instances as $k => $instance) {
                $instance->start();
                $promises []= async(static function () use ($k, $instance, $eventHandler, &$started): void {
                    $instance->startAndLoopLogic($eventHandler[$k], $started[$k]);
                });
            }
            awaitFirst($promises);
        } finally {
            if (EventLoop::getErrorHandler() === $cb) {
                EventLoop::setErrorHandler($prev);
            }
        }
    }
}
<?php

declare(strict_types=1);

/**
 * TLMethods module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\TL;

/**
 * @internal
 */
final class TLMethods
{
    use TLParams;
    public array $by_id = [];
    public array $by_method = [];
    public array $method_namespace = [];
    /**
     * @psalm-pure
     */
    public function __sleep(): array
    {
        return ['by_id', 'by_method', 'method_namespace'];
    }
    public function add(array $json_dict, string $scheme_type): void
    {
        $this->by_id[$json_dict['id']] = [
            'method' => $json_dict['method'],
            'type' => $json_dict['type'],
            'params' => $json_dict['params'],
            'flags' => [],
            'encrypted' => $scheme_type !== 'mtproto'
                || $json_dict['method'] === 'ping_delay_disconnect'
                || $json_dict['method'] === 'ping'
                || $json_dict['method'] === 'destroy_session'
                || $json_dict['method'] === 'rpc_drop_answer'
                || $json_dict['method'] === 'get_future_salts',
        ];
        if (preg_match('/^(v|V)ector\\<(.*)\\>$/', $json_dict['type'], $matches)) {
            $this->by_id[$json_dict['id']]['type'] = $matches[1] === 'v' ? 'vector' : 'Vector t';
            $this->by_id[$json_dict['id']]['subtype'] = $matches[2];
        }
        $this->by_method[$json_dict['method']] = $json_dict['id'];
        $namespace = explode('.', $json_dict['method']);
        if (isset($namespace[1])) {
            $this->method_namespace[] = [$namespace[0] => $namespace[1]];
        }
        $this->parseParams($json_dict['id'], false, $json_dict['method']);
    }
    /**
     * @psalm-mutation-free
     */
    public function findById(string $id)
    {
        if (isset($this->by_id[$id])) {
            $method = $this->by_id[$id];
            $method['id'] = $id;
            return $method;
        }
        return false;
    }
    /**
     * @psalm-mutation-free
     */
    public function findByMethod(string $method_name)
    {
        if (isset($this->by_method[$method_name])) {
            $method = $this->by_id[$this->by_method[$method_name]];
            $method['id'] = $this->by_method[$method_name];
            return $method;
        }
        return false;
    }
}
<?php namespace danog\MadelineProto\TL;
/** @internal @generated Autogenerated using tools/TL/Builder.php â€” do not edit. */
use danog\MadelineProto\MTProto;
use danog\MadelineProto\Connection;
use danog\MadelineProto\MTProtoTools\PeerDatabase;
use danog\MadelineProto\MTProtoTools\ReferenceDatabase;
use danog\MadelineProto\MTProtoTools\MinDatabase;
use danog\MadelineProto\SecurityException;
use AssertionError;
use danog\MadelineProto\Lang;
final class SecretTLParser {
public function __construct(
            private readonly MTProto $API,
            private readonly Connection $connection,
            private readonly PeerDatabase $peerDatabase,
            private readonly ?ReferenceDatabase $referenceDatabase,
            private readonly ?MinDatabase $minDatabase,
        ) {}
            private  function err(mixed $stream): never {

            fseek($stream, -4, SEEK_CUR);
            throw new AssertionError("Unexpected ID ".bin2hex(fread($stream, 4)));
        
    }
    private  function gzdecode(mixed $stream): mixed {

            $res = fopen('php://memory', 'rw+b');
            fwrite($res, gzdecode(self::deserialize_string($stream)));
            rewind($res);
            return $res;
        
    }
    private  function gzdecode_vector(mixed $stream): mixed {

            $res = fopen('php://memory', 'rw+b');
            fwrite($res, gzdecode(self::deserialize_string($stream)));
            rewind($res);
            return match (stream_get_contents($stream, 4)) {
                'Äµ' => $stream,
                default => self::err($stream)
            };
        
    }
    private  function deserialize_bytes(mixed $stream): mixed {

            
            $l = \ord(stream_get_contents($stream, 1));
            if ($l > 254) {
                throw new Exception(Lang::$current_lang["length_too_big"]);
            }
            if ($l === 254) {
                $l = unpack("V", stream_get_contents($stream, 3).\chr(0))[1];
                $x = stream_get_contents($stream, $l);
                $resto = (-$l) % 4;
                $resto = $resto < 0 ? $resto + 4 : $resto;
                if ($resto > 0) {
                    stream_get_contents($stream, $resto);
                }
            } else {
                $x = $l ? stream_get_contents($stream, $l) : "";
                $resto = (-$l+1) % 4;
                $resto = $resto < 0 ? $resto + 4 : $resto;
                if ($resto > 0) {
                    stream_get_contents($stream, $resto);
                }
            }

            return new Types\Bytes($x);
        
    }
    private  function deserialize_string(mixed $stream): mixed {

            
            $l = \ord(stream_get_contents($stream, 1));
            if ($l > 254) {
                throw new Exception(Lang::$current_lang["length_too_big"]);
            }
            if ($l === 254) {
                $l = unpack("V", stream_get_contents($stream, 3).\chr(0))[1];
                $x = stream_get_contents($stream, $l);
                $resto = (-$l) % 4;
                $resto = $resto < 0 ? $resto + 4 : $resto;
                if ($resto > 0) {
                    stream_get_contents($stream, $resto);
                }
            } else {
                $x = $l ? stream_get_contents($stream, $l) : "";
                $resto = (-$l+1) % 4;
                $resto = $resto < 0 ? $resto + 4 : $resto;
                if ($resto > 0) {
                    stream_get_contents($stream, $resto);
                }
            }

            return $x;
        
    }
    private  function deserialize_waveform(mixed $stream): mixed {

            
            $l = \ord(stream_get_contents($stream, 1));
            if ($l > 254) {
                throw new Exception(Lang::$current_lang["length_too_big"]);
            }
            if ($l === 254) {
                $l = unpack("V", stream_get_contents($stream, 3).\chr(0))[1];
                $x = stream_get_contents($stream, $l);
                $resto = (-$l) % 4;
                $resto = $resto < 0 ? $resto + 4 : $resto;
                if ($resto > 0) {
                    stream_get_contents($stream, $resto);
                }
            } else {
                $x = $l ? stream_get_contents($stream, $l) : "";
                $resto = (-$l+1) % 4;
                $resto = $resto < 0 ? $resto + 4 : $resto;
                if ($resto > 0) {
                    stream_get_contents($stream, $resto);
                }
            }

            return TL::extractWaveform($x);
        
    }
    private  function deserialize_random_bytes(mixed $stream): void {

            $l = \ord(stream_get_contents($stream, 1));
            if ($l > 254) {
                throw new Exception(Lang::$current_lang["length_too_big"]);
            }
            if ($l === 254) {
                $l = unpack("V", stream_get_contents($stream, 3).\chr(0))[1];
                if ($l < 15) {
                    throw new SecurityException("Random_bytes is too small!");
                }
            } else {
                if ($l < 15) {
                    throw new SecurityException("Random_bytes is too small!");
                }
                $l += 1;
            }
            $resto = (-$l) % 4;
            $resto = $resto < 0 ? $resto + 4 : $resto;
            if ($resto > 0) {
                $l += $resto;
            }
            stream_get_contents($stream, $l);
        
    }
    private  function deserialize_type_array_of_int(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= unpack('l', stream_get_contents($stream, 4))[1];
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_long(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= unpack('q', stream_get_contents($stream, 8))[1];
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_double(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= unpack('d', stream_get_contents($stream, 8))[1];
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_strlong(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= stream_get_contents($stream, 8);
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_string(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= self::deserialize_string($stream);
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_bytes(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= self::deserialize_bytes($stream);
                }
                return $result;    
            
    }
    private  function deserialize_type_FileLocation(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'FkY|' => [
'_' => 'fileLocationUnavailable_23',
'volume_id' => unpack('q', stream_get_contents($stream, 8))[1],
'local_id' => unpack('l', stream_get_contents($stream, 4))[1],
'secret' => unpack('q', stream_get_contents($stream, 8))[1],
],
'v�ÖS' => [
'_' => 'fileLocation_23',
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'volume_id' => unpack('q', stream_get_contents($stream, 8))[1],
'local_id' => unpack('l', stream_get_contents($stream, 4))[1],
'secret' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => $this->deserialize_type_FileLocation(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_PhotoSize(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'<â' => [
'_' => 'photoSizeEmpty',
'type' => self::deserialize_string($stream),
],
'`ŽÇu' => [
'_' => 'photoSize',
'type' => self::deserialize_string($stream),
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Ö' => [
'_' => 'photoCachedSize',
'type' => self::deserialize_string($stream),
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'bytes' => self::deserialize_bytes($stream),
],
'.¼°à' => [
'_' => 'photoStrippedSize',
'type' => self::deserialize_string($stream),
'inflated' => new Types\Bytes(Tools::inflateStripped(self::deserialize_string($stream))),
],
'•û>ú' => [
'_' => 'photoSizeProgressive',
'type' => self::deserialize_string($stream),
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'sizes' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'AM!Ø' => [
'_' => 'photoPathSize',
'type' => self::deserialize_string($stream),
'bytes' => self::deserialize_bytes($stream),
],
'¶¿w' => [
'_' => 'photoSize_23',
'type' => self::deserialize_string($stream),
'location' => match (stream_get_contents($stream, 4)) {
'FkY|' => [
'_' => 'fileLocationUnavailable_23',
'volume_id' => unpack('q', stream_get_contents($stream, 8))[1],
'local_id' => unpack('l', stream_get_contents($stream, 4))[1],
'secret' => unpack('q', stream_get_contents($stream, 8))[1],
],
'v�ÖS' => [
'_' => 'fileLocation_23',
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'volume_id' => unpack('q', stream_get_contents($stream, 8))[1],
'local_id' => unpack('l', stream_get_contents($stream, 4))[1],
'secret' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_FileLocation(self::gzdecode($stream)),
default => self::err($stream)
}
,
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
],
'ú4§é' => [
'_' => 'photoCachedSize_23',
'type' => self::deserialize_string($stream),
'location' => match (stream_get_contents($stream, 4)) {
'FkY|' => [
'_' => 'fileLocationUnavailable_23',
'volume_id' => unpack('q', stream_get_contents($stream, 8))[1],
'local_id' => unpack('l', stream_get_contents($stream, 4))[1],
'secret' => unpack('q', stream_get_contents($stream, 8))[1],
],
'v�ÖS' => [
'_' => 'fileLocation_23',
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'volume_id' => unpack('q', stream_get_contents($stream, 8))[1],
'local_id' => unpack('l', stream_get_contents($stream, 4))[1],
'secret' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_FileLocation(self::gzdecode($stream)),
default => self::err($stream)
}
,
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'bytes' => self::deserialize_bytes($stream),
],
'¡Ïr0' => $this->deserialize_type_PhotoSize(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_InputStickerSet(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'•+¶ÿ' => [
'_' => 'inputStickerSetEmpty',
],
'i¢ç�' => [
'_' => 'inputStickerSetID',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
' È†' => [
'_' => 'inputStickerSetShortName',
'short_name' => self::deserialize_string($stream),
],
'È‡' => [
'_' => 'inputStickerSetAnimatedEmoji',
],
'Ræ' => [
'_' => 'inputStickerSetDice',
'emoticon' => self::deserialize_string($stream),
],
'97Þ' => [
'_' => 'inputStickerSetAnimatedEmojiAnimations',
],
';‹È' => [
'_' => 'inputStickerSetPremiumGifts',
],
'ÎÔÄ' => [
'_' => 'inputStickerSetEmojiGenericAnimations',
],
'îõÐ)' => [
'_' => 'inputStickerSetEmojiDefaultStatuses',
],
'éøÁD' => [
'_' => 'inputStickerSetEmojiDefaultTopicIcons',
],
'S…tI' => [
'_' => 'inputStickerSetEmojiChannelDefaultStatuses',
],
'¡Ïr0' => $this->deserialize_type_InputStickerSet(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_MaskCoords(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'²ÛÖ®' => [
'_' => 'maskCoords',
'n' => unpack('l', stream_get_contents($stream, 4))[1],
'x' => unpack('d', stream_get_contents($stream, 8))[1],
'y' => unpack('d', stream_get_contents($stream, 8))[1],
'zoom' => unpack('d', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => $this->deserialize_type_MaskCoords(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_documentAttributeSticker(mixed $stream): mixed {
$tmp = ['_' => 'documentAttributeSticker'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['mask'] = ($flags & 2) !== 0;
$tmp['alt'] = self::deserialize_string($stream);
$tmp['stickerset'] = match (stream_get_contents($stream, 4)) {
'•+¶ÿ' => [
'_' => 'inputStickerSetEmpty',
],
'i¢ç�' => [
'_' => 'inputStickerSetID',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
' È†' => [
'_' => 'inputStickerSetShortName',
'short_name' => self::deserialize_string($stream),
],
'È‡' => [
'_' => 'inputStickerSetAnimatedEmoji',
],
'Ræ' => [
'_' => 'inputStickerSetDice',
'emoticon' => self::deserialize_string($stream),
],
'97Þ' => [
'_' => 'inputStickerSetAnimatedEmojiAnimations',
],
';‹È' => [
'_' => 'inputStickerSetPremiumGifts',
],
'ÎÔÄ' => [
'_' => 'inputStickerSetEmojiGenericAnimations',
],
'îõÐ)' => [
'_' => 'inputStickerSetEmojiDefaultStatuses',
],
'éøÁD' => [
'_' => 'inputStickerSetEmojiDefaultTopicIcons',
],
'S…tI' => [
'_' => 'inputStickerSetEmojiChannelDefaultStatuses',
],
'¡Ïr0' => self::deserialize_type_InputStickerSet(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 1) !== 0) $tmp['mask_coords'] = match (stream_get_contents($stream, 4)) {
'²ÛÖ®' => [
'_' => 'maskCoords',
'n' => unpack('l', stream_get_contents($stream, 4))[1],
'x' => unpack('d', stream_get_contents($stream, 8))[1],
'y' => unpack('d', stream_get_contents($stream, 8))[1],
'zoom' => unpack('d', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_MaskCoords(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_documentAttributeVideo(mixed $stream): mixed {
$tmp = ['_' => 'documentAttributeVideo'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['round_message'] = ($flags & 1) !== 0;
$tmp['supports_streaming'] = ($flags & 2) !== 0;
$tmp['nosound'] = ($flags & 8) !== 0;
$tmp['duration'] = unpack('d', stream_get_contents($stream, 8))[1];
$tmp['w'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['h'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 4) !== 0) $tmp['preload_prefix_size'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_documentAttributeAudio(mixed $stream): mixed {
$tmp = ['_' => 'documentAttributeAudio'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['voice'] = ($flags & 1024) !== 0;
$tmp['duration'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['title'] = self::deserialize_string($stream);
if (($flags & 2) !== 0) $tmp['performer'] = self::deserialize_string($stream);
if (($flags & 4) !== 0) $tmp['waveform'] = self::deserialize_waveform($stream);
return $tmp;

    }
    private  function deserialize_documentAttributeCustomEmoji(mixed $stream): mixed {
$tmp = ['_' => 'documentAttributeCustomEmoji'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['free'] = ($flags & 1) !== 0;
$tmp['text_color'] = ($flags & 2) !== 0;
$tmp['alt'] = self::deserialize_string($stream);
$tmp['stickerset'] = match (stream_get_contents($stream, 4)) {
'•+¶ÿ' => [
'_' => 'inputStickerSetEmpty',
],
'i¢ç�' => [
'_' => 'inputStickerSetID',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
' È†' => [
'_' => 'inputStickerSetShortName',
'short_name' => self::deserialize_string($stream),
],
'È‡' => [
'_' => 'inputStickerSetAnimatedEmoji',
],
'Ræ' => [
'_' => 'inputStickerSetDice',
'emoticon' => self::deserialize_string($stream),
],
'97Þ' => [
'_' => 'inputStickerSetAnimatedEmojiAnimations',
],
';‹È' => [
'_' => 'inputStickerSetPremiumGifts',
],
'ÎÔÄ' => [
'_' => 'inputStickerSetEmojiGenericAnimations',
],
'îõÐ)' => [
'_' => 'inputStickerSetEmojiDefaultStatuses',
],
'éøÁD' => [
'_' => 'inputStickerSetEmojiDefaultTopicIcons',
],
'S…tI' => [
'_' => 'inputStickerSetEmojiChannelDefaultStatuses',
],
'¡Ïr0' => self::deserialize_type_InputStickerSet(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_documentAttributeVideo_66(mixed $stream): mixed {
$tmp = ['_' => 'documentAttributeVideo_66'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['round_message'] = ($flags & 1) !== 0;
$tmp['duration'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['w'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['h'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_array_of_DocumentAttribute(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'\\Á7l' => [
'_' => 'documentAttributeImageSize',
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'9‰µ' => [
'_' => 'documentAttributeAnimated',
],
'Öc' => self::deserialize_documentAttributeSticker($stream),
'Âñ�Ó' => self::deserialize_documentAttributeVideo($stream),
'ÆùR˜' => self::deserialize_documentAttributeAudio($stream),
'h' . "\0" . 'Y' => [
'_' => 'documentAttributeFilename',
'file_name' => self::deserialize_string($stream),
],
'÷Ò˜' => [
'_' => 'documentAttributeHasStickers',
],
'™˜ý' => self::deserialize_documentAttributeCustomEmoji($stream),
'\'W
û' => [
'_' => 'documentAttributeSticker_23',
],
'ËÌY' => [
'_' => 'documentAttributeVideo_23',
'duration' => unpack('l', stream_get_contents($stream, 4))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'åH' => [
'_' => 'documentAttributeAudio_23',
'duration' => unpack('l', stream_get_contents($stream, 4))[1],
],
'cU:' => [
'_' => 'documentAttributeSticker_45',
'alt' => self::deserialize_string($stream),
'stickerset' => match (stream_get_contents($stream, 4)) {
'•+¶ÿ' => [
'_' => 'inputStickerSetEmpty',
],
'i¢ç�' => [
'_' => 'inputStickerSetID',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
' È†' => [
'_' => 'inputStickerSetShortName',
'short_name' => self::deserialize_string($stream),
],
'È‡' => [
'_' => 'inputStickerSetAnimatedEmoji',
],
'Ræ' => [
'_' => 'inputStickerSetDice',
'emoticon' => self::deserialize_string($stream),
],
'97Þ' => [
'_' => 'inputStickerSetAnimatedEmojiAnimations',
],
';‹È' => [
'_' => 'inputStickerSetPremiumGifts',
],
'ÎÔÄ' => [
'_' => 'inputStickerSetEmojiGenericAnimations',
],
'îõÐ)' => [
'_' => 'inputStickerSetEmojiDefaultStatuses',
],
'éøÁD' => [
'_' => 'inputStickerSetEmojiDefaultTopicIcons',
],
'S…tI' => [
'_' => 'inputStickerSetEmojiChannelDefaultStatuses',
],
'¡Ïr0' => self::deserialize_type_InputStickerSet(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'àÒÞ' => [
'_' => 'documentAttributeAudio_45',
'duration' => unpack('l', stream_get_contents($stream, 4))[1],
'title' => self::deserialize_string($stream),
'performer' => self::deserialize_string($stream),
],
'æ,ð' => self::deserialize_documentAttributeVideo_66($stream),
'¡Ïr0' => $this->deserialize_type_DocumentAttribute(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_DecryptedMessageMedia(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'J\\Ÿ' => [
'_' => 'decryptedMessageMediaEmpty_8',
],
'ŒŠy2' => [
'_' => 'decryptedMessageMediaPhoto_8',
'thumb' => self::deserialize_bytes($stream),
'thumb_w' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb_h' => unpack('l', stream_get_contents($stream, 4))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
],
'ónîL' => [
'_' => 'decryptedMessageMediaVideo_8',
'thumb' => self::deserialize_bytes($stream),
'thumb_w' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb_h' => unpack('l', stream_get_contents($stream, 4))[1],
'duration' => unpack('l', stream_get_contents($stream, 4))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
],
'Y
H5' => [
'_' => 'decryptedMessageMediaGeoPoint_8',
'lat' => unpack('d', stream_get_contents($stream, 8))[1],
'long' => unpack('d', stream_get_contents($stream, 8))[1],
],
'—
ŠX' => [
'_' => 'decryptedMessageMediaContact_8',
'phone_number' => self::deserialize_string($stream),
'first_name' => self::deserialize_string($stream),
'last_name' => self::deserialize_string($stream),
'user_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'KC•°' => [
'_' => 'decryptedMessageMediaDocument_8',
'thumb' => self::deserialize_bytes($stream),
'thumb_w' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb_h' => unpack('l', stream_get_contents($stream, 4))[1],
'file_name' => self::deserialize_string($stream),
'mime_type' => self::deserialize_string($stream),
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
],
'�u€`' => [
'_' => 'decryptedMessageMediaAudio_8',
'duration' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
],
']AJR' => [
'_' => 'decryptedMessageMediaVideo_17',
'thumb' => self::deserialize_bytes($stream),
'thumb_w' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb_h' => unpack('l', stream_get_contents($stream, 4))[1],
'duration' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
],
'Ë©àW' => [
'_' => 'decryptedMessageMediaAudio_17',
'duration' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
],
'Ý°•ú' => [
'_' => 'decryptedMessageMediaExternalDocument_23',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb' => match (stream_get_contents($stream, 4)) {
'<â' => [
'_' => 'photoSizeEmpty',
'type' => self::deserialize_string($stream),
],
'`ŽÇu' => [
'_' => 'photoSize',
'type' => self::deserialize_string($stream),
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Ö' => [
'_' => 'photoCachedSize',
'type' => self::deserialize_string($stream),
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'bytes' => self::deserialize_bytes($stream),
],
'.¼°à' => [
'_' => 'photoStrippedSize',
'type' => self::deserialize_string($stream),
'inflated' => new Types\Bytes(Tools::inflateStripped(self::deserialize_string($stream))),
],
'•û>ú' => [
'_' => 'photoSizeProgressive',
'type' => self::deserialize_string($stream),
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'sizes' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'AM!Ø' => [
'_' => 'photoPathSize',
'type' => self::deserialize_string($stream),
'bytes' => self::deserialize_bytes($stream),
],
'¶¿w' => [
'_' => 'photoSize_23',
'type' => self::deserialize_string($stream),
'location' => match (stream_get_contents($stream, 4)) {
'FkY|' => [
'_' => 'fileLocationUnavailable_23',
'volume_id' => unpack('q', stream_get_contents($stream, 8))[1],
'local_id' => unpack('l', stream_get_contents($stream, 4))[1],
'secret' => unpack('q', stream_get_contents($stream, 8))[1],
],
'v�ÖS' => [
'_' => 'fileLocation_23',
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'volume_id' => unpack('q', stream_get_contents($stream, 8))[1],
'local_id' => unpack('l', stream_get_contents($stream, 4))[1],
'secret' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_FileLocation(self::gzdecode($stream)),
default => self::err($stream)
}
,
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
],
'ú4§é' => [
'_' => 'photoCachedSize_23',
'type' => self::deserialize_string($stream),
'location' => match (stream_get_contents($stream, 4)) {
'FkY|' => [
'_' => 'fileLocationUnavailable_23',
'volume_id' => unpack('q', stream_get_contents($stream, 8))[1],
'local_id' => unpack('l', stream_get_contents($stream, 4))[1],
'secret' => unpack('q', stream_get_contents($stream, 8))[1],
],
'v�ÖS' => [
'_' => 'fileLocation_23',
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'volume_id' => unpack('q', stream_get_contents($stream, 8))[1],
'local_id' => unpack('l', stream_get_contents($stream, 4))[1],
'secret' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_FileLocation(self::gzdecode($stream)),
default => self::err($stream)
}
,
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'bytes' => self::deserialize_bytes($stream),
],
'¡Ïr0' => self::deserialize_type_PhotoSize(self::gzdecode($stream)),
default => self::err($stream)
}
,
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'attributes' => self::deserialize_type_array_of_DocumentAttribute(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'x�úñ' => [
'_' => 'decryptedMessageMediaPhoto_45',
'thumb' => self::deserialize_bytes($stream),
'thumb_w' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb_h' => unpack('l', stream_get_contents($stream, 4))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
'caption' => self::deserialize_string($stream),
],
'Œ—' => [
'_' => 'decryptedMessageMediaVideo_45',
'thumb' => self::deserialize_bytes($stream),
'thumb_w' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb_h' => unpack('l', stream_get_contents($stream, 4))[1],
'duration' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
'caption' => self::deserialize_string($stream),
],
'âŠþz' => [
'_' => 'decryptedMessageMediaDocument_45',
'thumb' => self::deserialize_bytes($stream),
'thumb_w' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb_h' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
'attributes' => self::deserialize_type_array_of_DocumentAttribute(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'caption' => self::deserialize_string($stream),
],
'oõŠ' => [
'_' => 'decryptedMessageMediaVenue_45',
'lat' => unpack('d', stream_get_contents($stream, 8))[1],
'long' => unpack('d', stream_get_contents($stream, 8))[1],
'title' => self::deserialize_string($stream),
'address' => self::deserialize_string($stream),
'provider' => self::deserialize_string($stream),
'venue_id' => self::deserialize_string($stream),
],
'Øå' => [
'_' => 'decryptedMessageMediaWebPage_45',
'url' => self::deserialize_string($stream),
],
'‚—½j' => [
'_' => 'decryptedMessageMediaDocument_143',
'thumb' => self::deserialize_bytes($stream),
'thumb_w' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb_h' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'size' => unpack('q', stream_get_contents($stream, 8))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
'attributes' => self::deserialize_type_array_of_DocumentAttribute(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'caption' => self::deserialize_string($stream),
],
'¡Ïr0' => $this->deserialize_type_DecryptedMessageMedia(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_DataJSON(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'¡Ïr0' => $this->deserialize_type_DataJSON(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_SendMessageAction(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'Nt¿' => [
'_' => 'sendMessageTypingAction',
],
'õÈ^ý' => [
'_' => 'sendMessageCancelAction',
],
'oÖ‡¡' => [
'_' => 'sendMessageRecordVideoAction',
],
'ì:vé' => [
'_' => 'sendMessageUploadVideoAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'÷s/Õ' => [
'_' => 'sendMessageRecordAudioAction',
],
'«×Qó' => [
'_' => 'sendMessageUploadAudioAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'&JÓÑ' => [
'_' => 'sendMessageUploadPhotoAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'äÙª' => [
'_' => 'sendMessageUploadDocumentAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡‹o' => [
'_' => 'sendMessageGeoLocationAction',
],
'o¼Œb' => [
'_' => 'sendMessageChooseContactAction',
],
'H�jÝ' => [
'_' => 'sendMessageGamePlayAction',
],
'¼òˆ' => [
'_' => 'sendMessageRecordRoundAction',
],
'f>$' => [
'_' => 'sendMessageUploadRoundAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'…",Ù' => [
'_' => 'speakingInGroupCallAction',
],
'F’ÚÛ' => [
'_' => 'sendMessageHistoryImportAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'±ÆZ°' => [
'_' => 'sendMessageChooseStickerAction',
],
'Ë+—%' => [
'_' => 'sendMessageEmojiInteraction',
'emoticon' => self::deserialize_string($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'interaction' => match (stream_get_contents($stream, 4)) {
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'¡Ïr0' => self::deserialize_type_DataJSON(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'.�e¶' => [
'_' => 'sendMessageEmojiInteractionSeen',
'emoticon' => self::deserialize_string($stream),
],
'÷/’' => [
'_' => 'sendMessageUploadVideoAction_17',
],
'oŠ¬æ' => [
'_' => 'sendMessageUploadAudioAction_17',
],
'<
™' => [
'_' => 'sendMessageUploadPhotoAction_17',
],
'Žé®�' => [
'_' => 'sendMessageUploadDocumentAction_17',
],
'$†q»' => [
'_' => 'sendMessageUploadRoundAction_66',
],
'¡Ïr0' => $this->deserialize_type_SendMessageAction(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_DecryptedMessageAction(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'ì:s¡' => [
'_' => 'decryptedMessageActionSetMessageTTL_8',
'ttl_seconds' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¾@O' => [
'_' => 'decryptedMessageActionReadMessages_8',
'random_ids' => self::deserialize_type_array_of_long(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'Cae' => [
'_' => 'decryptedMessageActionDeleteMessages_8',
'random_ids' => self::deserialize_type_array_of_long(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'uôÁŠ' => [
'_' => 'decryptedMessageActionScreenshotMessages_8',
'random_ids' => self::deserialize_type_array_of_long(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'\\äg' => [
'_' => 'decryptedMessageActionFlushHistory_8',
],
'°Q' => [
'_' => 'decryptedMessageActionResend_17',
'start_seq_no' => unpack('l', stream_get_contents($stream, 4))[1],
'end_seq_no' => unpack('l', stream_get_contents($stream, 4))[1],
],
'ƒˆó' => [
'_' => 'decryptedMessageActionNotifyLayer_17',
'layer' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Av²Ì' => [
'_' => 'decryptedMessageActionTyping_17',
'action' => match (stream_get_contents($stream, 4)) {
'Nt¿' => [
'_' => 'sendMessageTypingAction',
],
'õÈ^ý' => [
'_' => 'sendMessageCancelAction',
],
'oÖ‡¡' => [
'_' => 'sendMessageRecordVideoAction',
],
'ì:vé' => [
'_' => 'sendMessageUploadVideoAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'÷s/Õ' => [
'_' => 'sendMessageRecordAudioAction',
],
'«×Qó' => [
'_' => 'sendMessageUploadAudioAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'&JÓÑ' => [
'_' => 'sendMessageUploadPhotoAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'äÙª' => [
'_' => 'sendMessageUploadDocumentAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡‹o' => [
'_' => 'sendMessageGeoLocationAction',
],
'o¼Œb' => [
'_' => 'sendMessageChooseContactAction',
],
'H�jÝ' => [
'_' => 'sendMessageGamePlayAction',
],
'¼òˆ' => [
'_' => 'sendMessageRecordRoundAction',
],
'f>$' => [
'_' => 'sendMessageUploadRoundAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'…",Ù' => [
'_' => 'speakingInGroupCallAction',
],
'F’ÚÛ' => [
'_' => 'sendMessageHistoryImportAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'±ÆZ°' => [
'_' => 'sendMessageChooseStickerAction',
],
'Ë+—%' => [
'_' => 'sendMessageEmojiInteraction',
'emoticon' => self::deserialize_string($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'interaction' => match (stream_get_contents($stream, 4)) {
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'¡Ïr0' => self::deserialize_type_DataJSON(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'.�e¶' => [
'_' => 'sendMessageEmojiInteractionSeen',
'emoticon' => self::deserialize_string($stream),
],
'÷/’' => [
'_' => 'sendMessageUploadVideoAction_17',
],
'oŠ¬æ' => [
'_' => 'sendMessageUploadAudioAction_17',
],
'<
™' => [
'_' => 'sendMessageUploadPhotoAction_17',
],
'Žé®�' => [
'_' => 'sendMessageUploadDocumentAction_17',
],
'$†q»' => [
'_' => 'sendMessageUploadRoundAction_66',
],
'¡Ïr0' => self::deserialize_type_SendMessageAction(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'aÉó' => [
'_' => 'decryptedMessageActionRequestKey_20',
'exchange_id' => unpack('q', stream_get_contents($stream, 8))[1],
'g_a' => self::deserialize_bytes($stream),
],
'[sáo' => [
'_' => 'decryptedMessageActionAcceptKey_20',
'exchange_id' => unpack('q', stream_get_contents($stream, 8))[1],
'g_b' => self::deserialize_bytes($stream),
'key_fingerprint' => stream_get_contents($stream, 8),
],
'kìÝ' => [
'_' => 'decryptedMessageActionAbortKey_20',
'exchange_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'›.ì' => [
'_' => 'decryptedMessageActionCommitKey_20',
'exchange_id' => unpack('q', stream_get_contents($stream, 8))[1],
'key_fingerprint' => stream_get_contents($stream, 8),
],
'cÝ/¨' => [
'_' => 'decryptedMessageActionNoop_20',
],
'¡Ïr0' => $this->deserialize_type_DecryptedMessageAction(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_messageEntityMentionName(mixed $stream): mixed {
$tmp = [
'_' => 'messageEntityMentionName',
'offset' => unpack('l', stream_get_contents($stream, 4))[1],
'length' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
];
$this->minDatabase?->addPeer($tmp);
return $tmp;

    }
    private  function deserialize_type_InputPeer(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'ê;' => [
'_' => 'inputPeerEmpty',
],
'É~ }' => [
'_' => 'inputPeerSelf',
],
'¹\\©5' => [
'_' => 'inputPeerChat',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'L¥èÝ' => [
'_' => 'inputPeerUser',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ü»¼\'' => [
'_' => 'inputPeerChannel',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'
{¨' => [
'_' => 'inputPeerUserFromMessage',
'peer' => match (stream_get_contents($stream, 4)) {
'ê;' => [
'_' => 'inputPeerEmpty',
],
'É~ }' => [
'_' => 'inputPeerSelf',
],
'¹\\©5' => [
'_' => 'inputPeerChat',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'L¥èÝ' => [
'_' => 'inputPeerUser',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ü»¼\'' => [
'_' => 'inputPeerChannel',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'
{¨' => [
'_' => 'inputPeerUserFromMessage',
'peer' => $this->deserialize_type_InputPeer($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'@*½' => [
'_' => 'inputPeerChannelFromMessage',
'peer' => $this->deserialize_type_InputPeer($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => $this->deserialize_type_InputPeer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'@*½' => [
'_' => 'inputPeerChannelFromMessage',
'peer' => match (stream_get_contents($stream, 4)) {
'ê;' => [
'_' => 'inputPeerEmpty',
],
'É~ }' => [
'_' => 'inputPeerSelf',
],
'¹\\©5' => [
'_' => 'inputPeerChat',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'L¥èÝ' => [
'_' => 'inputPeerUser',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ü»¼\'' => [
'_' => 'inputPeerChannel',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'
{¨' => [
'_' => 'inputPeerUserFromMessage',
'peer' => $this->deserialize_type_InputPeer($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'@*½' => [
'_' => 'inputPeerChannelFromMessage',
'peer' => $this->deserialize_type_InputPeer($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => $this->deserialize_type_InputPeer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => $this->deserialize_type_InputPeer(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_InputUser(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'Ï†ˆ¹' => [
'_' => 'inputUserEmpty',
],
'?±Á÷' => [
'_' => 'inputUserSelf',
],
'ÆXò' => [
'_' => 'inputUser',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'âH¤' => [
'_' => 'inputUserFromMessage',
'peer' => match (stream_get_contents($stream, 4)) {
'ê;' => [
'_' => 'inputPeerEmpty',
],
'É~ }' => [
'_' => 'inputPeerSelf',
],
'¹\\©5' => [
'_' => 'inputPeerChat',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'L¥èÝ' => [
'_' => 'inputPeerUser',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ü»¼\'' => [
'_' => 'inputPeerChannel',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'
{¨' => [
'_' => 'inputPeerUserFromMessage',
'peer' => self::deserialize_type_InputPeer($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'@*½' => [
'_' => 'inputPeerChannelFromMessage',
'peer' => self::deserialize_type_InputPeer($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputPeer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => $this->deserialize_type_InputUser(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_array_of_MessageEntity(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'•º’»' => [
'_' => 'messageEntityUnknown',
'offset' => unpack('l', stream_get_contents($stream, 4))[1],
'length' => unpack('l', stream_get_contents($stream, 4))[1],
],
'�Wú' => [
'_' => 'messageEntityMention',
'offset' => unpack('l', stream_get_contents($stream, 4))[1],
'length' => unpack('l', stream_get_contents($stream, 4))[1],
],
'[co' => [
'_' => 'messageEntityHashtag',
'offset' => unpack('l', stream_get_contents($stream, 4))[1],
'length' => unpack('l', stream_get_contents($stream, 4))[1],
],
'ÇŠïl' => [
'_' => 'messageEntityBotCommand',
'offset' => unpack('l', stream_get_contents($stream, 4))[1],
'length' => unpack('l', stream_get_contents($stream, 4))[1],
],
'8%Ðn' => [
'_' => 'messageEntityUrl',
'offset' => unpack('l', stream_get_contents($stream, 4))[1],
'length' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Âuäd' => [
'_' => 'messageEntityEmail',
'offset' => unpack('l', stream_get_contents($stream, 4))[1],
'length' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Éa½' => [
'_' => 'messageEntityBold',
'offset' => unpack('l', stream_get_contents($stream, 4))[1],
'length' => unpack('l', stream_get_contents($stream, 4))[1],
],
'`‹o‚' => [
'_' => 'messageEntityItalic',
'offset' => unpack('l', stream_get_contents($stream, 4))[1],
'length' => unpack('l', stream_get_contents($stream, 4))[1],
],
'q¢(' => [
'_' => 'messageEntityCode',
'offset' => unpack('l', stream_get_contents($stream, 4))[1],
'length' => unpack('l', stream_get_contents($stream, 4))[1],
],
'àK’s' => [
'_' => 'messageEntityPre',
'offset' => unpack('l', stream_get_contents($stream, 4))[1],
'length' => unpack('l', stream_get_contents($stream, 4))[1],
'language' => self::deserialize_string($stream),
],
'\'Ó¦v' => [
'_' => 'messageEntityTextUrl',
'offset' => unpack('l', stream_get_contents($stream, 4))[1],
'length' => unpack('l', stream_get_contents($stream, 4))[1],
'url' => self::deserialize_string($stream),
],
'@{Ü' => self::deserialize_messageEntityMentionName($stream),
'ÉhŽ ' => [
'_' => 'inputMessageEntityMentionName',
'offset' => unpack('l', stream_get_contents($stream, 4))[1],
'length' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => match (stream_get_contents($stream, 4)) {
'Ï†ˆ¹' => [
'_' => 'inputUserEmpty',
],
'?±Á÷' => [
'_' => 'inputUserSelf',
],
'ÆXò' => [
'_' => 'inputUser',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'âH¤' => [
'_' => 'inputUserFromMessage',
'peer' => match (stream_get_contents($stream, 4)) {
'ê;' => [
'_' => 'inputPeerEmpty',
],
'É~ }' => [
'_' => 'inputPeerSelf',
],
'¹\\©5' => [
'_' => 'inputPeerChat',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'L¥èÝ' => [
'_' => 'inputPeerUser',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ü»¼\'' => [
'_' => 'inputPeerChannel',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'
{¨' => [
'_' => 'inputPeerUserFromMessage',
'peer' => self::deserialize_type_InputPeer($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'@*½' => [
'_' => 'inputPeerChannelFromMessage',
'peer' => self::deserialize_type_InputPeer($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputPeer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputUser(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Kãi›' => [
'_' => 'messageEntityPhone',
'offset' => unpack('l', stream_get_contents($stream, 4))[1],
'length' => unpack('l', stream_get_contents($stream, 4))[1],
],
'?tNL' => [
'_' => 'messageEntityCashtag',
'offset' => unpack('l', stream_get_contents($stream, 4))[1],
'length' => unpack('l', stream_get_contents($stream, 4))[1],
],
'‹~Nœ' => [
'_' => 'messageEntityUnderline',
'offset' => unpack('l', stream_get_contents($stream, 4))[1],
'length' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Ô“¿' => [
'_' => 'messageEntityStrike',
'offset' => unpack('l', stream_get_contents($stream, 4))[1],
'length' => unpack('l', stream_get_contents($stream, 4))[1],
],
'ôjv' => [
'_' => 'messageEntityBankCard',
'offset' => unpack('l', stream_get_contents($stream, 4))[1],
'length' => unpack('l', stream_get_contents($stream, 4))[1],
],
'–Ê2' => [
'_' => 'messageEntitySpoiler',
'offset' => unpack('l', stream_get_contents($stream, 4))[1],
'length' => unpack('l', stream_get_contents($stream, 4))[1],
],
'øÏÈ' => [
'_' => 'messageEntityCustomEmoji',
'offset' => unpack('l', stream_get_contents($stream, 4))[1],
'length' => unpack('l', stream_get_contents($stream, 4))[1],
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'Ðõ' => [
'_' => 'messageEntityBlockquote',
'offset' => unpack('l', stream_get_contents($stream, 4))[1],
'length' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => $this->deserialize_type_MessageEntity(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_decryptedMessage_45(mixed $stream): mixed {
$tmp = ['_' => 'decryptedMessage_45'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['random_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['ttl'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['message'] = self::deserialize_string($stream);
if (($flags & 512) !== 0) $tmp['media'] = match (stream_get_contents($stream, 4)) {
'J\\Ÿ' => [
'_' => 'decryptedMessageMediaEmpty_8',
],
'ŒŠy2' => [
'_' => 'decryptedMessageMediaPhoto_8',
'thumb' => self::deserialize_bytes($stream),
'thumb_w' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb_h' => unpack('l', stream_get_contents($stream, 4))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
],
'ónîL' => [
'_' => 'decryptedMessageMediaVideo_8',
'thumb' => self::deserialize_bytes($stream),
'thumb_w' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb_h' => unpack('l', stream_get_contents($stream, 4))[1],
'duration' => unpack('l', stream_get_contents($stream, 4))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
],
'Y
H5' => [
'_' => 'decryptedMessageMediaGeoPoint_8',
'lat' => unpack('d', stream_get_contents($stream, 8))[1],
'long' => unpack('d', stream_get_contents($stream, 8))[1],
],
'—
ŠX' => [
'_' => 'decryptedMessageMediaContact_8',
'phone_number' => self::deserialize_string($stream),
'first_name' => self::deserialize_string($stream),
'last_name' => self::deserialize_string($stream),
'user_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'KC•°' => [
'_' => 'decryptedMessageMediaDocument_8',
'thumb' => self::deserialize_bytes($stream),
'thumb_w' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb_h' => unpack('l', stream_get_contents($stream, 4))[1],
'file_name' => self::deserialize_string($stream),
'mime_type' => self::deserialize_string($stream),
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
],
'�u€`' => [
'_' => 'decryptedMessageMediaAudio_8',
'duration' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
],
']AJR' => [
'_' => 'decryptedMessageMediaVideo_17',
'thumb' => self::deserialize_bytes($stream),
'thumb_w' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb_h' => unpack('l', stream_get_contents($stream, 4))[1],
'duration' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
],
'Ë©àW' => [
'_' => 'decryptedMessageMediaAudio_17',
'duration' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
],
'Ý°•ú' => [
'_' => 'decryptedMessageMediaExternalDocument_23',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb' => match (stream_get_contents($stream, 4)) {
'<â' => [
'_' => 'photoSizeEmpty',
'type' => self::deserialize_string($stream),
],
'`ŽÇu' => [
'_' => 'photoSize',
'type' => self::deserialize_string($stream),
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Ö' => [
'_' => 'photoCachedSize',
'type' => self::deserialize_string($stream),
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'bytes' => self::deserialize_bytes($stream),
],
'.¼°à' => [
'_' => 'photoStrippedSize',
'type' => self::deserialize_string($stream),
'inflated' => new Types\Bytes(Tools::inflateStripped(self::deserialize_string($stream))),
],
'•û>ú' => [
'_' => 'photoSizeProgressive',
'type' => self::deserialize_string($stream),
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'sizes' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'AM!Ø' => [
'_' => 'photoPathSize',
'type' => self::deserialize_string($stream),
'bytes' => self::deserialize_bytes($stream),
],
'¶¿w' => [
'_' => 'photoSize_23',
'type' => self::deserialize_string($stream),
'location' => match (stream_get_contents($stream, 4)) {
'FkY|' => [
'_' => 'fileLocationUnavailable_23',
'volume_id' => unpack('q', stream_get_contents($stream, 8))[1],
'local_id' => unpack('l', stream_get_contents($stream, 4))[1],
'secret' => unpack('q', stream_get_contents($stream, 8))[1],
],
'v�ÖS' => [
'_' => 'fileLocation_23',
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'volume_id' => unpack('q', stream_get_contents($stream, 8))[1],
'local_id' => unpack('l', stream_get_contents($stream, 4))[1],
'secret' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_FileLocation(self::gzdecode($stream)),
default => self::err($stream)
}
,
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
],
'ú4§é' => [
'_' => 'photoCachedSize_23',
'type' => self::deserialize_string($stream),
'location' => match (stream_get_contents($stream, 4)) {
'FkY|' => [
'_' => 'fileLocationUnavailable_23',
'volume_id' => unpack('q', stream_get_contents($stream, 8))[1],
'local_id' => unpack('l', stream_get_contents($stream, 4))[1],
'secret' => unpack('q', stream_get_contents($stream, 8))[1],
],
'v�ÖS' => [
'_' => 'fileLocation_23',
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'volume_id' => unpack('q', stream_get_contents($stream, 8))[1],
'local_id' => unpack('l', stream_get_contents($stream, 4))[1],
'secret' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_FileLocation(self::gzdecode($stream)),
default => self::err($stream)
}
,
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'bytes' => self::deserialize_bytes($stream),
],
'¡Ïr0' => self::deserialize_type_PhotoSize(self::gzdecode($stream)),
default => self::err($stream)
}
,
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'attributes' => self::deserialize_type_array_of_DocumentAttribute(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'x�úñ' => [
'_' => 'decryptedMessageMediaPhoto_45',
'thumb' => self::deserialize_bytes($stream),
'thumb_w' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb_h' => unpack('l', stream_get_contents($stream, 4))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
'caption' => self::deserialize_string($stream),
],
'Œ—' => [
'_' => 'decryptedMessageMediaVideo_45',
'thumb' => self::deserialize_bytes($stream),
'thumb_w' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb_h' => unpack('l', stream_get_contents($stream, 4))[1],
'duration' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
'caption' => self::deserialize_string($stream),
],
'âŠþz' => [
'_' => 'decryptedMessageMediaDocument_45',
'thumb' => self::deserialize_bytes($stream),
'thumb_w' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb_h' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
'attributes' => self::deserialize_type_array_of_DocumentAttribute(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'caption' => self::deserialize_string($stream),
],
'oõŠ' => [
'_' => 'decryptedMessageMediaVenue_45',
'lat' => unpack('d', stream_get_contents($stream, 8))[1],
'long' => unpack('d', stream_get_contents($stream, 8))[1],
'title' => self::deserialize_string($stream),
'address' => self::deserialize_string($stream),
'provider' => self::deserialize_string($stream),
'venue_id' => self::deserialize_string($stream),
],
'Øå' => [
'_' => 'decryptedMessageMediaWebPage_45',
'url' => self::deserialize_string($stream),
],
'‚—½j' => [
'_' => 'decryptedMessageMediaDocument_143',
'thumb' => self::deserialize_bytes($stream),
'thumb_w' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb_h' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'size' => unpack('q', stream_get_contents($stream, 8))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
'attributes' => self::deserialize_type_array_of_DocumentAttribute(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'caption' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_DecryptedMessageMedia(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 128) !== 0) $tmp['entities'] = self::deserialize_type_array_of_MessageEntity(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 2048) !== 0) $tmp['via_bot_name'] = self::deserialize_string($stream);
if (($flags & 8) !== 0) $tmp['reply_to_random_id'] = unpack('q', stream_get_contents($stream, 8))[1];
return $tmp;

    }
    private  function deserialize_decryptedMessage_73(mixed $stream): mixed {
$tmp = ['_' => 'decryptedMessage_73'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['no_webpage'] = ($flags & 2) !== 0;
$tmp['silent'] = ($flags & 32) !== 0;
$tmp['random_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['ttl'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['message'] = self::deserialize_string($stream);
if (($flags & 512) !== 0) $tmp['media'] = match (stream_get_contents($stream, 4)) {
'J\\Ÿ' => [
'_' => 'decryptedMessageMediaEmpty_8',
],
'ŒŠy2' => [
'_' => 'decryptedMessageMediaPhoto_8',
'thumb' => self::deserialize_bytes($stream),
'thumb_w' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb_h' => unpack('l', stream_get_contents($stream, 4))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
],
'ónîL' => [
'_' => 'decryptedMessageMediaVideo_8',
'thumb' => self::deserialize_bytes($stream),
'thumb_w' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb_h' => unpack('l', stream_get_contents($stream, 4))[1],
'duration' => unpack('l', stream_get_contents($stream, 4))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
],
'Y
H5' => [
'_' => 'decryptedMessageMediaGeoPoint_8',
'lat' => unpack('d', stream_get_contents($stream, 8))[1],
'long' => unpack('d', stream_get_contents($stream, 8))[1],
],
'—
ŠX' => [
'_' => 'decryptedMessageMediaContact_8',
'phone_number' => self::deserialize_string($stream),
'first_name' => self::deserialize_string($stream),
'last_name' => self::deserialize_string($stream),
'user_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'KC•°' => [
'_' => 'decryptedMessageMediaDocument_8',
'thumb' => self::deserialize_bytes($stream),
'thumb_w' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb_h' => unpack('l', stream_get_contents($stream, 4))[1],
'file_name' => self::deserialize_string($stream),
'mime_type' => self::deserialize_string($stream),
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
],
'�u€`' => [
'_' => 'decryptedMessageMediaAudio_8',
'duration' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
],
']AJR' => [
'_' => 'decryptedMessageMediaVideo_17',
'thumb' => self::deserialize_bytes($stream),
'thumb_w' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb_h' => unpack('l', stream_get_contents($stream, 4))[1],
'duration' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
],
'Ë©àW' => [
'_' => 'decryptedMessageMediaAudio_17',
'duration' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
],
'Ý°•ú' => [
'_' => 'decryptedMessageMediaExternalDocument_23',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb' => match (stream_get_contents($stream, 4)) {
'<â' => [
'_' => 'photoSizeEmpty',
'type' => self::deserialize_string($stream),
],
'`ŽÇu' => [
'_' => 'photoSize',
'type' => self::deserialize_string($stream),
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Ö' => [
'_' => 'photoCachedSize',
'type' => self::deserialize_string($stream),
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'bytes' => self::deserialize_bytes($stream),
],
'.¼°à' => [
'_' => 'photoStrippedSize',
'type' => self::deserialize_string($stream),
'inflated' => new Types\Bytes(Tools::inflateStripped(self::deserialize_string($stream))),
],
'•û>ú' => [
'_' => 'photoSizeProgressive',
'type' => self::deserialize_string($stream),
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'sizes' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'AM!Ø' => [
'_' => 'photoPathSize',
'type' => self::deserialize_string($stream),
'bytes' => self::deserialize_bytes($stream),
],
'¶¿w' => [
'_' => 'photoSize_23',
'type' => self::deserialize_string($stream),
'location' => match (stream_get_contents($stream, 4)) {
'FkY|' => [
'_' => 'fileLocationUnavailable_23',
'volume_id' => unpack('q', stream_get_contents($stream, 8))[1],
'local_id' => unpack('l', stream_get_contents($stream, 4))[1],
'secret' => unpack('q', stream_get_contents($stream, 8))[1],
],
'v�ÖS' => [
'_' => 'fileLocation_23',
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'volume_id' => unpack('q', stream_get_contents($stream, 8))[1],
'local_id' => unpack('l', stream_get_contents($stream, 4))[1],
'secret' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_FileLocation(self::gzdecode($stream)),
default => self::err($stream)
}
,
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
],
'ú4§é' => [
'_' => 'photoCachedSize_23',
'type' => self::deserialize_string($stream),
'location' => match (stream_get_contents($stream, 4)) {
'FkY|' => [
'_' => 'fileLocationUnavailable_23',
'volume_id' => unpack('q', stream_get_contents($stream, 8))[1],
'local_id' => unpack('l', stream_get_contents($stream, 4))[1],
'secret' => unpack('q', stream_get_contents($stream, 8))[1],
],
'v�ÖS' => [
'_' => 'fileLocation_23',
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'volume_id' => unpack('q', stream_get_contents($stream, 8))[1],
'local_id' => unpack('l', stream_get_contents($stream, 4))[1],
'secret' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_FileLocation(self::gzdecode($stream)),
default => self::err($stream)
}
,
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'bytes' => self::deserialize_bytes($stream),
],
'¡Ïr0' => self::deserialize_type_PhotoSize(self::gzdecode($stream)),
default => self::err($stream)
}
,
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'attributes' => self::deserialize_type_array_of_DocumentAttribute(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'x�úñ' => [
'_' => 'decryptedMessageMediaPhoto_45',
'thumb' => self::deserialize_bytes($stream),
'thumb_w' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb_h' => unpack('l', stream_get_contents($stream, 4))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
'caption' => self::deserialize_string($stream),
],
'Œ—' => [
'_' => 'decryptedMessageMediaVideo_45',
'thumb' => self::deserialize_bytes($stream),
'thumb_w' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb_h' => unpack('l', stream_get_contents($stream, 4))[1],
'duration' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
'caption' => self::deserialize_string($stream),
],
'âŠþz' => [
'_' => 'decryptedMessageMediaDocument_45',
'thumb' => self::deserialize_bytes($stream),
'thumb_w' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb_h' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
'attributes' => self::deserialize_type_array_of_DocumentAttribute(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'caption' => self::deserialize_string($stream),
],
'oõŠ' => [
'_' => 'decryptedMessageMediaVenue_45',
'lat' => unpack('d', stream_get_contents($stream, 8))[1],
'long' => unpack('d', stream_get_contents($stream, 8))[1],
'title' => self::deserialize_string($stream),
'address' => self::deserialize_string($stream),
'provider' => self::deserialize_string($stream),
'venue_id' => self::deserialize_string($stream),
],
'Øå' => [
'_' => 'decryptedMessageMediaWebPage_45',
'url' => self::deserialize_string($stream),
],
'‚—½j' => [
'_' => 'decryptedMessageMediaDocument_143',
'thumb' => self::deserialize_bytes($stream),
'thumb_w' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb_h' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'size' => unpack('q', stream_get_contents($stream, 8))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
'attributes' => self::deserialize_type_array_of_DocumentAttribute(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'caption' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_DecryptedMessageMedia(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 128) !== 0) $tmp['entities'] = self::deserialize_type_array_of_MessageEntity(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 2048) !== 0) $tmp['via_bot_name'] = self::deserialize_string($stream);
if (($flags & 8) !== 0) $tmp['reply_to_random_id'] = unpack('q', stream_get_contents($stream, 8))[1];
if (($flags & 131072) !== 0) $tmp['grouped_id'] = unpack('q', stream_get_contents($stream, 8))[1];
return $tmp;

    }
    public  function deserialize_type_Object(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'O�' => [
'_' => 'decryptedMessage_8',
'random_id' => unpack('q', stream_get_contents($stream, 8))[1],
'random_bytes' => self::deserialize_random_bytes($stream),
'message' => self::deserialize_string($stream),
'media' => match (stream_get_contents($stream, 4)) {
'J\\Ÿ' => [
'_' => 'decryptedMessageMediaEmpty_8',
],
'ŒŠy2' => [
'_' => 'decryptedMessageMediaPhoto_8',
'thumb' => self::deserialize_bytes($stream),
'thumb_w' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb_h' => unpack('l', stream_get_contents($stream, 4))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
],
'ónîL' => [
'_' => 'decryptedMessageMediaVideo_8',
'thumb' => self::deserialize_bytes($stream),
'thumb_w' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb_h' => unpack('l', stream_get_contents($stream, 4))[1],
'duration' => unpack('l', stream_get_contents($stream, 4))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
],
'Y
H5' => [
'_' => 'decryptedMessageMediaGeoPoint_8',
'lat' => unpack('d', stream_get_contents($stream, 8))[1],
'long' => unpack('d', stream_get_contents($stream, 8))[1],
],
'—
ŠX' => [
'_' => 'decryptedMessageMediaContact_8',
'phone_number' => self::deserialize_string($stream),
'first_name' => self::deserialize_string($stream),
'last_name' => self::deserialize_string($stream),
'user_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'KC•°' => [
'_' => 'decryptedMessageMediaDocument_8',
'thumb' => self::deserialize_bytes($stream),
'thumb_w' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb_h' => unpack('l', stream_get_contents($stream, 4))[1],
'file_name' => self::deserialize_string($stream),
'mime_type' => self::deserialize_string($stream),
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
],
'�u€`' => [
'_' => 'decryptedMessageMediaAudio_8',
'duration' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
],
']AJR' => [
'_' => 'decryptedMessageMediaVideo_17',
'thumb' => self::deserialize_bytes($stream),
'thumb_w' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb_h' => unpack('l', stream_get_contents($stream, 4))[1],
'duration' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
],
'Ë©àW' => [
'_' => 'decryptedMessageMediaAudio_17',
'duration' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
],
'Ý°•ú' => [
'_' => 'decryptedMessageMediaExternalDocument_23',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb' => match (stream_get_contents($stream, 4)) {
'<â' => [
'_' => 'photoSizeEmpty',
'type' => self::deserialize_string($stream),
],
'`ŽÇu' => [
'_' => 'photoSize',
'type' => self::deserialize_string($stream),
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Ö' => [
'_' => 'photoCachedSize',
'type' => self::deserialize_string($stream),
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'bytes' => self::deserialize_bytes($stream),
],
'.¼°à' => [
'_' => 'photoStrippedSize',
'type' => self::deserialize_string($stream),
'inflated' => new Types\Bytes(Tools::inflateStripped(self::deserialize_string($stream))),
],
'•û>ú' => [
'_' => 'photoSizeProgressive',
'type' => self::deserialize_string($stream),
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'sizes' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'AM!Ø' => [
'_' => 'photoPathSize',
'type' => self::deserialize_string($stream),
'bytes' => self::deserialize_bytes($stream),
],
'¶¿w' => [
'_' => 'photoSize_23',
'type' => self::deserialize_string($stream),
'location' => match (stream_get_contents($stream, 4)) {
'FkY|' => [
'_' => 'fileLocationUnavailable_23',
'volume_id' => unpack('q', stream_get_contents($stream, 8))[1],
'local_id' => unpack('l', stream_get_contents($stream, 4))[1],
'secret' => unpack('q', stream_get_contents($stream, 8))[1],
],
'v�ÖS' => [
'_' => 'fileLocation_23',
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'volume_id' => unpack('q', stream_get_contents($stream, 8))[1],
'local_id' => unpack('l', stream_get_contents($stream, 4))[1],
'secret' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_FileLocation(self::gzdecode($stream)),
default => self::err($stream)
}
,
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
],
'ú4§é' => [
'_' => 'photoCachedSize_23',
'type' => self::deserialize_string($stream),
'location' => match (stream_get_contents($stream, 4)) {
'FkY|' => [
'_' => 'fileLocationUnavailable_23',
'volume_id' => unpack('q', stream_get_contents($stream, 8))[1],
'local_id' => unpack('l', stream_get_contents($stream, 4))[1],
'secret' => unpack('q', stream_get_contents($stream, 8))[1],
],
'v�ÖS' => [
'_' => 'fileLocation_23',
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'volume_id' => unpack('q', stream_get_contents($stream, 8))[1],
'local_id' => unpack('l', stream_get_contents($stream, 4))[1],
'secret' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_FileLocation(self::gzdecode($stream)),
default => self::err($stream)
}
,
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'bytes' => self::deserialize_bytes($stream),
],
'¡Ïr0' => self::deserialize_type_PhotoSize(self::gzdecode($stream)),
default => self::err($stream)
}
,
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'attributes' => self::deserialize_type_array_of_DocumentAttribute(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'x�úñ' => [
'_' => 'decryptedMessageMediaPhoto_45',
'thumb' => self::deserialize_bytes($stream),
'thumb_w' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb_h' => unpack('l', stream_get_contents($stream, 4))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
'caption' => self::deserialize_string($stream),
],
'Œ—' => [
'_' => 'decryptedMessageMediaVideo_45',
'thumb' => self::deserialize_bytes($stream),
'thumb_w' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb_h' => unpack('l', stream_get_contents($stream, 4))[1],
'duration' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
'caption' => self::deserialize_string($stream),
],
'âŠþz' => [
'_' => 'decryptedMessageMediaDocument_45',
'thumb' => self::deserialize_bytes($stream),
'thumb_w' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb_h' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
'attributes' => self::deserialize_type_array_of_DocumentAttribute(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'caption' => self::deserialize_string($stream),
],
'oõŠ' => [
'_' => 'decryptedMessageMediaVenue_45',
'lat' => unpack('d', stream_get_contents($stream, 8))[1],
'long' => unpack('d', stream_get_contents($stream, 8))[1],
'title' => self::deserialize_string($stream),
'address' => self::deserialize_string($stream),
'provider' => self::deserialize_string($stream),
'venue_id' => self::deserialize_string($stream),
],
'Øå' => [
'_' => 'decryptedMessageMediaWebPage_45',
'url' => self::deserialize_string($stream),
],
'‚—½j' => [
'_' => 'decryptedMessageMediaDocument_143',
'thumb' => self::deserialize_bytes($stream),
'thumb_w' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb_h' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'size' => unpack('q', stream_get_contents($stream, 8))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
'attributes' => self::deserialize_type_array_of_DocumentAttribute(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'caption' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_DecryptedMessageMedia(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'}2Hª' => [
'_' => 'decryptedMessageService_8',
'random_id' => unpack('q', stream_get_contents($stream, 8))[1],
'random_bytes' => self::deserialize_random_bytes($stream),
'action' => match (stream_get_contents($stream, 4)) {
'ì:s¡' => [
'_' => 'decryptedMessageActionSetMessageTTL_8',
'ttl_seconds' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¾@O' => [
'_' => 'decryptedMessageActionReadMessages_8',
'random_ids' => self::deserialize_type_array_of_long(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'Cae' => [
'_' => 'decryptedMessageActionDeleteMessages_8',
'random_ids' => self::deserialize_type_array_of_long(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'uôÁŠ' => [
'_' => 'decryptedMessageActionScreenshotMessages_8',
'random_ids' => self::deserialize_type_array_of_long(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'\\äg' => [
'_' => 'decryptedMessageActionFlushHistory_8',
],
'°Q' => [
'_' => 'decryptedMessageActionResend_17',
'start_seq_no' => unpack('l', stream_get_contents($stream, 4))[1],
'end_seq_no' => unpack('l', stream_get_contents($stream, 4))[1],
],
'ƒˆó' => [
'_' => 'decryptedMessageActionNotifyLayer_17',
'layer' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Av²Ì' => [
'_' => 'decryptedMessageActionTyping_17',
'action' => match (stream_get_contents($stream, 4)) {
'Nt¿' => [
'_' => 'sendMessageTypingAction',
],
'õÈ^ý' => [
'_' => 'sendMessageCancelAction',
],
'oÖ‡¡' => [
'_' => 'sendMessageRecordVideoAction',
],
'ì:vé' => [
'_' => 'sendMessageUploadVideoAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'÷s/Õ' => [
'_' => 'sendMessageRecordAudioAction',
],
'«×Qó' => [
'_' => 'sendMessageUploadAudioAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'&JÓÑ' => [
'_' => 'sendMessageUploadPhotoAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'äÙª' => [
'_' => 'sendMessageUploadDocumentAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡‹o' => [
'_' => 'sendMessageGeoLocationAction',
],
'o¼Œb' => [
'_' => 'sendMessageChooseContactAction',
],
'H�jÝ' => [
'_' => 'sendMessageGamePlayAction',
],
'¼òˆ' => [
'_' => 'sendMessageRecordRoundAction',
],
'f>$' => [
'_' => 'sendMessageUploadRoundAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'…",Ù' => [
'_' => 'speakingInGroupCallAction',
],
'F’ÚÛ' => [
'_' => 'sendMessageHistoryImportAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'±ÆZ°' => [
'_' => 'sendMessageChooseStickerAction',
],
'Ë+—%' => [
'_' => 'sendMessageEmojiInteraction',
'emoticon' => self::deserialize_string($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'interaction' => match (stream_get_contents($stream, 4)) {
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'¡Ïr0' => self::deserialize_type_DataJSON(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'.�e¶' => [
'_' => 'sendMessageEmojiInteractionSeen',
'emoticon' => self::deserialize_string($stream),
],
'÷/’' => [
'_' => 'sendMessageUploadVideoAction_17',
],
'oŠ¬æ' => [
'_' => 'sendMessageUploadAudioAction_17',
],
'<
™' => [
'_' => 'sendMessageUploadPhotoAction_17',
],
'Žé®�' => [
'_' => 'sendMessageUploadDocumentAction_17',
],
'$†q»' => [
'_' => 'sendMessageUploadRoundAction_66',
],
'¡Ïr0' => self::deserialize_type_SendMessageAction(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'aÉó' => [
'_' => 'decryptedMessageActionRequestKey_20',
'exchange_id' => unpack('q', stream_get_contents($stream, 8))[1],
'g_a' => self::deserialize_bytes($stream),
],
'[sáo' => [
'_' => 'decryptedMessageActionAcceptKey_20',
'exchange_id' => unpack('q', stream_get_contents($stream, 8))[1],
'g_b' => self::deserialize_bytes($stream),
'key_fingerprint' => stream_get_contents($stream, 8),
],
'kìÝ' => [
'_' => 'decryptedMessageActionAbortKey_20',
'exchange_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'›.ì' => [
'_' => 'decryptedMessageActionCommitKey_20',
'exchange_id' => unpack('q', stream_get_contents($stream, 8))[1],
'key_fingerprint' => stream_get_contents($stream, 8),
],
'cÝ/¨' => [
'_' => 'decryptedMessageActionNoop_20',
],
'¡Ïr0' => self::deserialize_type_DecryptedMessageAction(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'J\\Ÿ' => [
'_' => 'decryptedMessageMediaEmpty_8',
],
'ŒŠy2' => [
'_' => 'decryptedMessageMediaPhoto_8',
'thumb' => self::deserialize_bytes($stream),
'thumb_w' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb_h' => unpack('l', stream_get_contents($stream, 4))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
],
'ónîL' => [
'_' => 'decryptedMessageMediaVideo_8',
'thumb' => self::deserialize_bytes($stream),
'thumb_w' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb_h' => unpack('l', stream_get_contents($stream, 4))[1],
'duration' => unpack('l', stream_get_contents($stream, 4))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
],
'Y
H5' => [
'_' => 'decryptedMessageMediaGeoPoint_8',
'lat' => unpack('d', stream_get_contents($stream, 8))[1],
'long' => unpack('d', stream_get_contents($stream, 8))[1],
],
'—
ŠX' => [
'_' => 'decryptedMessageMediaContact_8',
'phone_number' => self::deserialize_string($stream),
'first_name' => self::deserialize_string($stream),
'last_name' => self::deserialize_string($stream),
'user_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'KC•°' => [
'_' => 'decryptedMessageMediaDocument_8',
'thumb' => self::deserialize_bytes($stream),
'thumb_w' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb_h' => unpack('l', stream_get_contents($stream, 4))[1],
'file_name' => self::deserialize_string($stream),
'mime_type' => self::deserialize_string($stream),
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
],
'�u€`' => [
'_' => 'decryptedMessageMediaAudio_8',
'duration' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
],
'x8M ' => [
'_' => 'decryptedMessage_17',
'random_id' => unpack('q', stream_get_contents($stream, 8))[1],
'ttl' => unpack('l', stream_get_contents($stream, 4))[1],
'message' => self::deserialize_string($stream),
'media' => match (stream_get_contents($stream, 4)) {
'J\\Ÿ' => [
'_' => 'decryptedMessageMediaEmpty_8',
],
'ŒŠy2' => [
'_' => 'decryptedMessageMediaPhoto_8',
'thumb' => self::deserialize_bytes($stream),
'thumb_w' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb_h' => unpack('l', stream_get_contents($stream, 4))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
],
'ónîL' => [
'_' => 'decryptedMessageMediaVideo_8',
'thumb' => self::deserialize_bytes($stream),
'thumb_w' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb_h' => unpack('l', stream_get_contents($stream, 4))[1],
'duration' => unpack('l', stream_get_contents($stream, 4))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
],
'Y
H5' => [
'_' => 'decryptedMessageMediaGeoPoint_8',
'lat' => unpack('d', stream_get_contents($stream, 8))[1],
'long' => unpack('d', stream_get_contents($stream, 8))[1],
],
'—
ŠX' => [
'_' => 'decryptedMessageMediaContact_8',
'phone_number' => self::deserialize_string($stream),
'first_name' => self::deserialize_string($stream),
'last_name' => self::deserialize_string($stream),
'user_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'KC•°' => [
'_' => 'decryptedMessageMediaDocument_8',
'thumb' => self::deserialize_bytes($stream),
'thumb_w' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb_h' => unpack('l', stream_get_contents($stream, 4))[1],
'file_name' => self::deserialize_string($stream),
'mime_type' => self::deserialize_string($stream),
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
],
'�u€`' => [
'_' => 'decryptedMessageMediaAudio_8',
'duration' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
],
']AJR' => [
'_' => 'decryptedMessageMediaVideo_17',
'thumb' => self::deserialize_bytes($stream),
'thumb_w' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb_h' => unpack('l', stream_get_contents($stream, 4))[1],
'duration' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
],
'Ë©àW' => [
'_' => 'decryptedMessageMediaAudio_17',
'duration' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
],
'Ý°•ú' => [
'_' => 'decryptedMessageMediaExternalDocument_23',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb' => match (stream_get_contents($stream, 4)) {
'<â' => [
'_' => 'photoSizeEmpty',
'type' => self::deserialize_string($stream),
],
'`ŽÇu' => [
'_' => 'photoSize',
'type' => self::deserialize_string($stream),
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Ö' => [
'_' => 'photoCachedSize',
'type' => self::deserialize_string($stream),
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'bytes' => self::deserialize_bytes($stream),
],
'.¼°à' => [
'_' => 'photoStrippedSize',
'type' => self::deserialize_string($stream),
'inflated' => new Types\Bytes(Tools::inflateStripped(self::deserialize_string($stream))),
],
'•û>ú' => [
'_' => 'photoSizeProgressive',
'type' => self::deserialize_string($stream),
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'sizes' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'AM!Ø' => [
'_' => 'photoPathSize',
'type' => self::deserialize_string($stream),
'bytes' => self::deserialize_bytes($stream),
],
'¶¿w' => [
'_' => 'photoSize_23',
'type' => self::deserialize_string($stream),
'location' => match (stream_get_contents($stream, 4)) {
'FkY|' => [
'_' => 'fileLocationUnavailable_23',
'volume_id' => unpack('q', stream_get_contents($stream, 8))[1],
'local_id' => unpack('l', stream_get_contents($stream, 4))[1],
'secret' => unpack('q', stream_get_contents($stream, 8))[1],
],
'v�ÖS' => [
'_' => 'fileLocation_23',
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'volume_id' => unpack('q', stream_get_contents($stream, 8))[1],
'local_id' => unpack('l', stream_get_contents($stream, 4))[1],
'secret' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_FileLocation(self::gzdecode($stream)),
default => self::err($stream)
}
,
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
],
'ú4§é' => [
'_' => 'photoCachedSize_23',
'type' => self::deserialize_string($stream),
'location' => match (stream_get_contents($stream, 4)) {
'FkY|' => [
'_' => 'fileLocationUnavailable_23',
'volume_id' => unpack('q', stream_get_contents($stream, 8))[1],
'local_id' => unpack('l', stream_get_contents($stream, 4))[1],
'secret' => unpack('q', stream_get_contents($stream, 8))[1],
],
'v�ÖS' => [
'_' => 'fileLocation_23',
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'volume_id' => unpack('q', stream_get_contents($stream, 8))[1],
'local_id' => unpack('l', stream_get_contents($stream, 4))[1],
'secret' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_FileLocation(self::gzdecode($stream)),
default => self::err($stream)
}
,
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'bytes' => self::deserialize_bytes($stream),
],
'¡Ïr0' => self::deserialize_type_PhotoSize(self::gzdecode($stream)),
default => self::err($stream)
}
,
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'attributes' => self::deserialize_type_array_of_DocumentAttribute(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'x�úñ' => [
'_' => 'decryptedMessageMediaPhoto_45',
'thumb' => self::deserialize_bytes($stream),
'thumb_w' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb_h' => unpack('l', stream_get_contents($stream, 4))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
'caption' => self::deserialize_string($stream),
],
'Œ—' => [
'_' => 'decryptedMessageMediaVideo_45',
'thumb' => self::deserialize_bytes($stream),
'thumb_w' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb_h' => unpack('l', stream_get_contents($stream, 4))[1],
'duration' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
'caption' => self::deserialize_string($stream),
],
'âŠþz' => [
'_' => 'decryptedMessageMediaDocument_45',
'thumb' => self::deserialize_bytes($stream),
'thumb_w' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb_h' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
'attributes' => self::deserialize_type_array_of_DocumentAttribute(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'caption' => self::deserialize_string($stream),
],
'oõŠ' => [
'_' => 'decryptedMessageMediaVenue_45',
'lat' => unpack('d', stream_get_contents($stream, 8))[1],
'long' => unpack('d', stream_get_contents($stream, 8))[1],
'title' => self::deserialize_string($stream),
'address' => self::deserialize_string($stream),
'provider' => self::deserialize_string($stream),
'venue_id' => self::deserialize_string($stream),
],
'Øå' => [
'_' => 'decryptedMessageMediaWebPage_45',
'url' => self::deserialize_string($stream),
],
'‚—½j' => [
'_' => 'decryptedMessageMediaDocument_143',
'thumb' => self::deserialize_bytes($stream),
'thumb_w' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb_h' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'size' => unpack('q', stream_get_contents($stream, 8))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
'attributes' => self::deserialize_type_array_of_DocumentAttribute(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'caption' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_DecryptedMessageMedia(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'`As' => [
'_' => 'decryptedMessageService_17',
'random_id' => unpack('q', stream_get_contents($stream, 8))[1],
'action' => match (stream_get_contents($stream, 4)) {
'ì:s¡' => [
'_' => 'decryptedMessageActionSetMessageTTL_8',
'ttl_seconds' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¾@O' => [
'_' => 'decryptedMessageActionReadMessages_8',
'random_ids' => self::deserialize_type_array_of_long(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'Cae' => [
'_' => 'decryptedMessageActionDeleteMessages_8',
'random_ids' => self::deserialize_type_array_of_long(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'uôÁŠ' => [
'_' => 'decryptedMessageActionScreenshotMessages_8',
'random_ids' => self::deserialize_type_array_of_long(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'\\äg' => [
'_' => 'decryptedMessageActionFlushHistory_8',
],
'°Q' => [
'_' => 'decryptedMessageActionResend_17',
'start_seq_no' => unpack('l', stream_get_contents($stream, 4))[1],
'end_seq_no' => unpack('l', stream_get_contents($stream, 4))[1],
],
'ƒˆó' => [
'_' => 'decryptedMessageActionNotifyLayer_17',
'layer' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Av²Ì' => [
'_' => 'decryptedMessageActionTyping_17',
'action' => match (stream_get_contents($stream, 4)) {
'Nt¿' => [
'_' => 'sendMessageTypingAction',
],
'õÈ^ý' => [
'_' => 'sendMessageCancelAction',
],
'oÖ‡¡' => [
'_' => 'sendMessageRecordVideoAction',
],
'ì:vé' => [
'_' => 'sendMessageUploadVideoAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'÷s/Õ' => [
'_' => 'sendMessageRecordAudioAction',
],
'«×Qó' => [
'_' => 'sendMessageUploadAudioAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'&JÓÑ' => [
'_' => 'sendMessageUploadPhotoAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'äÙª' => [
'_' => 'sendMessageUploadDocumentAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡‹o' => [
'_' => 'sendMessageGeoLocationAction',
],
'o¼Œb' => [
'_' => 'sendMessageChooseContactAction',
],
'H�jÝ' => [
'_' => 'sendMessageGamePlayAction',
],
'¼òˆ' => [
'_' => 'sendMessageRecordRoundAction',
],
'f>$' => [
'_' => 'sendMessageUploadRoundAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'…",Ù' => [
'_' => 'speakingInGroupCallAction',
],
'F’ÚÛ' => [
'_' => 'sendMessageHistoryImportAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'±ÆZ°' => [
'_' => 'sendMessageChooseStickerAction',
],
'Ë+—%' => [
'_' => 'sendMessageEmojiInteraction',
'emoticon' => self::deserialize_string($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'interaction' => match (stream_get_contents($stream, 4)) {
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'¡Ïr0' => self::deserialize_type_DataJSON(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'.�e¶' => [
'_' => 'sendMessageEmojiInteractionSeen',
'emoticon' => self::deserialize_string($stream),
],
'÷/’' => [
'_' => 'sendMessageUploadVideoAction_17',
],
'oŠ¬æ' => [
'_' => 'sendMessageUploadAudioAction_17',
],
'<
™' => [
'_' => 'sendMessageUploadPhotoAction_17',
],
'Žé®�' => [
'_' => 'sendMessageUploadDocumentAction_17',
],
'$†q»' => [
'_' => 'sendMessageUploadRoundAction_66',
],
'¡Ïr0' => self::deserialize_type_SendMessageAction(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'aÉó' => [
'_' => 'decryptedMessageActionRequestKey_20',
'exchange_id' => unpack('q', stream_get_contents($stream, 8))[1],
'g_a' => self::deserialize_bytes($stream),
],
'[sáo' => [
'_' => 'decryptedMessageActionAcceptKey_20',
'exchange_id' => unpack('q', stream_get_contents($stream, 8))[1],
'g_b' => self::deserialize_bytes($stream),
'key_fingerprint' => stream_get_contents($stream, 8),
],
'kìÝ' => [
'_' => 'decryptedMessageActionAbortKey_20',
'exchange_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'›.ì' => [
'_' => 'decryptedMessageActionCommitKey_20',
'exchange_id' => unpack('q', stream_get_contents($stream, 8))[1],
'key_fingerprint' => stream_get_contents($stream, 8),
],
'cÝ/¨' => [
'_' => 'decryptedMessageActionNoop_20',
],
'¡Ïr0' => self::deserialize_type_DecryptedMessageAction(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
']AJR' => [
'_' => 'decryptedMessageMediaVideo_17',
'thumb' => self::deserialize_bytes($stream),
'thumb_w' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb_h' => unpack('l', stream_get_contents($stream, 4))[1],
'duration' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
],
'Ë©àW' => [
'_' => 'decryptedMessageMediaAudio_17',
'duration' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
],
'Ý°•ú' => [
'_' => 'decryptedMessageMediaExternalDocument_23',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb' => match (stream_get_contents($stream, 4)) {
'<â' => [
'_' => 'photoSizeEmpty',
'type' => self::deserialize_string($stream),
],
'`ŽÇu' => [
'_' => 'photoSize',
'type' => self::deserialize_string($stream),
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Ö' => [
'_' => 'photoCachedSize',
'type' => self::deserialize_string($stream),
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'bytes' => self::deserialize_bytes($stream),
],
'.¼°à' => [
'_' => 'photoStrippedSize',
'type' => self::deserialize_string($stream),
'inflated' => new Types\Bytes(Tools::inflateStripped(self::deserialize_string($stream))),
],
'•û>ú' => [
'_' => 'photoSizeProgressive',
'type' => self::deserialize_string($stream),
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'sizes' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'AM!Ø' => [
'_' => 'photoPathSize',
'type' => self::deserialize_string($stream),
'bytes' => self::deserialize_bytes($stream),
],
'¶¿w' => [
'_' => 'photoSize_23',
'type' => self::deserialize_string($stream),
'location' => match (stream_get_contents($stream, 4)) {
'FkY|' => [
'_' => 'fileLocationUnavailable_23',
'volume_id' => unpack('q', stream_get_contents($stream, 8))[1],
'local_id' => unpack('l', stream_get_contents($stream, 4))[1],
'secret' => unpack('q', stream_get_contents($stream, 8))[1],
],
'v�ÖS' => [
'_' => 'fileLocation_23',
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'volume_id' => unpack('q', stream_get_contents($stream, 8))[1],
'local_id' => unpack('l', stream_get_contents($stream, 4))[1],
'secret' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_FileLocation(self::gzdecode($stream)),
default => self::err($stream)
}
,
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
],
'ú4§é' => [
'_' => 'photoCachedSize_23',
'type' => self::deserialize_string($stream),
'location' => match (stream_get_contents($stream, 4)) {
'FkY|' => [
'_' => 'fileLocationUnavailable_23',
'volume_id' => unpack('q', stream_get_contents($stream, 8))[1],
'local_id' => unpack('l', stream_get_contents($stream, 4))[1],
'secret' => unpack('q', stream_get_contents($stream, 8))[1],
],
'v�ÖS' => [
'_' => 'fileLocation_23',
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'volume_id' => unpack('q', stream_get_contents($stream, 8))[1],
'local_id' => unpack('l', stream_get_contents($stream, 4))[1],
'secret' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_FileLocation(self::gzdecode($stream)),
default => self::err($stream)
}
,
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'bytes' => self::deserialize_bytes($stream),
],
'¡Ïr0' => self::deserialize_type_PhotoSize(self::gzdecode($stream)),
default => self::err($stream)
}
,
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'attributes' => self::deserialize_type_array_of_DocumentAttribute(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'Þ‘°6' => self::deserialize_decryptedMessage_45($stream),
'x�úñ' => [
'_' => 'decryptedMessageMediaPhoto_45',
'thumb' => self::deserialize_bytes($stream),
'thumb_w' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb_h' => unpack('l', stream_get_contents($stream, 4))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
'caption' => self::deserialize_string($stream),
],
'Œ—' => [
'_' => 'decryptedMessageMediaVideo_45',
'thumb' => self::deserialize_bytes($stream),
'thumb_w' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb_h' => unpack('l', stream_get_contents($stream, 4))[1],
'duration' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
'caption' => self::deserialize_string($stream),
],
'âŠþz' => [
'_' => 'decryptedMessageMediaDocument_45',
'thumb' => self::deserialize_bytes($stream),
'thumb_w' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb_h' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
'attributes' => self::deserialize_type_array_of_DocumentAttribute(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'caption' => self::deserialize_string($stream),
],
'oõŠ' => [
'_' => 'decryptedMessageMediaVenue_45',
'lat' => unpack('d', stream_get_contents($stream, 8))[1],
'long' => unpack('d', stream_get_contents($stream, 8))[1],
'title' => self::deserialize_string($stream),
'address' => self::deserialize_string($stream),
'provider' => self::deserialize_string($stream),
'venue_id' => self::deserialize_string($stream),
],
'Øå' => [
'_' => 'decryptedMessageMediaWebPage_45',
'url' => self::deserialize_string($stream),
],
'tFÌ‘' => self::deserialize_decryptedMessage_73($stream),
'‚—½j' => [
'_' => 'decryptedMessageMediaDocument_143',
'thumb' => self::deserialize_bytes($stream),
'thumb_w' => unpack('l', stream_get_contents($stream, 4))[1],
'thumb_h' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'size' => unpack('q', stream_get_contents($stream, 8))[1],
'key' => self::deserialize_bytes($stream),
'iv' => self::deserialize_bytes($stream),
'attributes' => self::deserialize_type_array_of_DocumentAttribute(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'caption' => self::deserialize_string($stream),
],
'¡Ïr0' => $this->deserialize_type_Object(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
}
<?php

declare(strict_types=1);

/**
 * BotAPIFiles module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\TL\Conversion;

use danog\Decoder\FileId;
use danog\Decoder\FileIdType;
use danog\Decoder\PhotoSizeSource\PhotoSizeSourceDialogPhoto;
use danog\Decoder\PhotoSizeSource\PhotoSizeSourceDialogPhotoBig;
use danog\Decoder\PhotoSizeSource\PhotoSizeSourceLegacy;
use danog\Decoder\PhotoSizeSource\PhotoSizeSourceStickersetThumbnail;
use danog\Decoder\PhotoSizeSource\PhotoSizeSourceThumbnail;
use danog\DialogId\DialogId;
use danog\MadelineProto\API;
use danog\MadelineProto\EventHandler\Media\InlineThumbnail;
use danog\MadelineProto\Lang;

/**
 * @internal
 */
trait BotAPIFiles
{
    private function photosizeToBotAPI($photoSize, $photo, $thumbnail = false): array
    {
        $photoSizeSource = new PhotoSizeSourceThumbnail(
            thumbType: $photoSize['type'],
            thumbFileType: $photo['_'] === 'photo' ? FileIdType::PHOTO : FileIdType::THUMBNAIL
        );
        $fileId = new FileId(
            id: $photo['id'] ?? 0,
            type: $photo['_'] === 'photo' ? FileIdType::PHOTO : FileIdType::THUMBNAIL,
            accessHash: $photo['access_hash'] ?? 0,
            fileReference: $photo['file_reference'] === null
                ? null
                : (string) $photo['file_reference'],
            dcId: $photo['dc_id'] ?? 0,
            localId: $photoSize['location']['local_id'] ?? null,
            volumeId: $photoSize['location']['volume_id'] ?? null,
            photoSizeSource: $photoSizeSource
        );

        return [
            'file_id' => (string) $fileId,
            'file_unique_id' => $fileId->getUniqueBotAPI(),
            'width' => $photoSize['w'],
            'height' => $photoSize['h'],
            'file_size' => $photoSize['size'] ?? (isset($photoSize['sizes']) ? end($photoSize['sizes']) : \strlen((string) $photoSize['bytes'])),
            'mime_type' => 'image/jpeg',
            'file_name' => isset($photoSize['location']) ? $photoSize['location']['volume_id'].'_'.$photoSize['location']['local_id'].'.jpg' : $photo['id'].'.jpg',
        ];
    }
    /**
     * Convert the profile photo of a peer (the big one, as downloaded by getDownloadInfo()) to a bot API file.
     *
     * @param array $peer User, chat or channel constructor, or updateUserPhoto
     *
     * @return array{file_id: string, file_unique_id: string}
     */
    private function profilePhotoToBotAPI(array $peer): array
    {
        $fileId = new FileId(
            id: $peer['photo']['photo_id'],
            type: FileIdType::PROFILE_PHOTO,
            accessHash: 0,
            dcId: $peer['photo']['dc_id'],
            photoSizeSource: new PhotoSizeSourceDialogPhotoBig(
                dialogId: $this->getIdInternal($peer) ?? 0,
                dialogAccessHash: $peer['access_hash'] ?? 0,
            ),
        );

        return [
            'file_id' => (string) $fileId,
            'file_unique_id' => $fileId->getUniqueBotAPI(),
        ];
    }
    /**
     * Unpack bot API file ID.
     *
     * @param  string $fileId Bot API file ID
     * @return array  Unpacked file ID
     */
    public static function unpackFileId(string $fileId): array
    {
        if (InlineThumbnail::isFileId($fileId)) {
            throw new Exception('The file ID of an inline thumbnail can only be used by the session that generated it');
        }
        $fileId = FileId::fromBotAPI($fileId);

        if (!\in_array($fileId->version, [2, 4], true)) {
            throw new Exception("Invalid bot API file ID version {$fileId->version}");
        }

        $photoSize = $fileId->photoSizeSource;

        $res = null;
        switch ($fileId->type) {
            case FileIdType::PROFILE_PHOTO:
                /**
                 * @var PhotoSizeSourceDialogPhoto $photoSize
                 */
                $dialogId = $photoSize->dialogId;
                $photo = ['dc_id' => $fileId->dcId, 'photo_id' => $fileId->id];
                if (DialogId::isUser($dialogId)) {
                    $res['User'] = ['_' => 'user', 'id' => $dialogId, 'access_hash' => $photoSize->dialogAccessHash, 'photo' => ['_' => 'userProfilePhoto'] + $photo, 'min' => true];
                    $peer = ['_' => 'inputPeerUser', 'user_id' => $dialogId, 'access_hash' => $photoSize->dialogAccessHash];
                } elseif (DialogId::isChat($dialogId)) {
                    $res['Chat'] = ['_' => 'chat', 'id' => $dialogId, 'photo' => ['_' => 'chatPhoto'] + $photo, 'min' => true];
                    $peer = ['_' => 'inputPeerChat', 'chat_id' => DialogId::toChatId($dialogId)];
                } else {
                    $res['Chat'] = ['_' => 'channel', 'id' => $dialogId, 'access_hash' => $photoSize->dialogAccessHash, 'photo' => ['_' => 'chatPhoto'] + $photo, 'min' => true];
                    $peer = ['_' => 'inputPeerChannel', 'channel_id' => DialogId::toSupergroupOrChannelId($dialogId), 'access_hash' => $photoSize->dialogAccessHash];
                }
                // With the access hash of the file ID: the peer may not be in the database.
                $res['InputFileLocation'] = [
                    '_' => 'inputPeerPhotoFileLocation',
                    'big' => !$photoSize->isSmallDialogPhoto(),
                    'peer' => $peer,
                    'photo_id' => $fileId->id,
                ];
                $res['name'] = $dialogId.'_'.($photoSize->isSmallDialogPhoto() ? 'small' : 'big').'_'.$fileId->dcId;
                $res['ext'] = '.jpg';
                $res['mime'] = 'image/jpeg';
                return $res;
            case FileIdType::THUMBNAIL:
                /**
                 * @var PhotoSizeSourceThumbnail $photoSize
                 */
                $res['InputFileLocation'] = [
                    '_' => $photoSize->thumbFileType === FileIdType::THUMBNAIL ? 'inputDocumentFileLocation' : 'inputPhotoFileLocation',
                    'id' => $fileId->id,
                    'access_hash' => $fileId->accessHash,
                    'file_reference' => $fileId->fileReference,
                    'thumb_size' => $photoSize->thumbType,
                    'dc_id' => $fileId->dcId,
                ];
                $res['name'] = $fileId->id.'_'.$photoSize->thumbType;
                $res['ext'] = '.jpg';
                $res['mime'] = 'image/jpeg';
                $res['InputMedia'] = [
                    '_' => $photoSize->thumbFileType === FileIdType::THUMBNAIL ? 'inputMediaDocument' : 'inputMediaPhoto',
                    'id' => [
                        '_' => $photoSize->thumbFileType === FileIdType::THUMBNAIL ? 'inputDocument' : 'inputPhoto',
                        'id' => $fileId->id,
                        'access_hash' => $fileId->accessHash,
                        'file_reference' => $fileId->fileReference,
                    ],
                ];
                if ($res['InputMedia']['id']['_'] === 'inputPhoto') {
                    $res['InputMedia']['id']['sizes'] = [
                        [
                            '_' => 'photoSize',
                            'type' => $photoSize->thumbType,
                        ],
                    ];
                }
                return $res;
            case FileIdType::PHOTO:
                $constructor = [
                    '_' => 'photo',
                    'id' => $fileId->id,
                    'access_hash' => $fileId->accessHash,
                    'file_reference' => $fileId->fileReference,
                    'dc_id' => $fileId->dcId,
                    'sizes' => [],
                ];
                $constructor['sizes'][] = [
                    '_' => 'photoSize',
                    'type' => $photoSize instanceof PhotoSizeSourceThumbnail ? $photoSize->thumbType : '',
                    'location' => [
                        '_' => $photoSize instanceof PhotoSizeSourceLegacy ? 'fileLocation' : 'fileLocationToBeDeprecated',
                        'dc_id' => $fileId->dcId,
                        'local_id' => $fileId->localId,
                        'volume_id' => $fileId->volumeId,
                        'secret' => $photoSize instanceof PhotoSizeSourceLegacy ? $photoSize->secret : '',
                    ],
                ];
                $res['MessageMedia'] = [
                    '_' => 'messageMediaPhoto',
                    'photo' => $constructor,
                    'caption' => '',
                ];
                return $res;
            case FileIdType::VOICE:
                $attribute = [
                    '_' => 'documentAttributeAudio',
                    'voice' => true,
                ];
                break;
            case FileIdType::VIDEO:
                $attribute = [
                    '_' => 'documentAttributeVideo',
                    'round_message' => false,
                ];
                break;
            case FileIdType::DOCUMENT:
                $attribute = [];
                break;
            case FileIdType::STICKER:
                $attribute = [
                    '_' => 'documentAttributeSticker',
                    'alt' => '',
                ];
                if ($photoSize instanceof PhotoSizeSourceStickersetThumbnail) {
                    $attribute['stickerset'] = [
                        '_' => 'inputStickerSetID',
                        'id' => $photoSize->stickerSetId,
                        'access_hash' => $photoSize->stickerSetAccessHash,
                    ];
                }
                break;
            case FileIdType::AUDIO:
                $attribute = ['_' => 'documentAttributeAudio', 'voice' => false];
                break;
            case FileIdType::ANIMATION:
                $attribute = ['_' => 'documentAttributeAnimated'];
                break;
            case FileIdType::VIDEO_NOTE:
                $attribute = ['_' => 'documentAttributeVideo', 'round_message' => true];
                break;
            default:
                throw new Exception(\sprintf(Lang::$current_lang['file_type_invalid'], $fileId->type->name));
        }

        $constructor = [
            '_' => 'document',
            'id' => $fileId->id,
            'access_hash' => $fileId->accessHash,
            'file_reference' => $fileId->fileReference,
            'dc_id' => $fileId->dcId,
            'mime_type' => 'application/octet-stream',
            'attributes' => $attribute ? [$attribute] : [],
        ];
        $res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $constructor, 'caption' => ''];
        return $res;
    }
}
<?php

declare(strict_types=1);

/**
 * BotAPI module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\TL\Conversion;

use danog\Decoder\FileId;
use danog\Decoder\FileIdType;
use danog\MadelineProto\EventHandler\Message\Entities\MessageEntity;
use danog\MadelineProto\Logger;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\StrTools;
use danog\MadelineProto\Tools;
use Throwable;

/**
 * @internal
 */
trait BotAPI
{
    /**
     * @psalm-pure
     */
    private function htmlEntityDecode(string $stuff): string
    {
        return html_entity_decode(preg_replace('#< *br */? *>#', "\n", $stuff));
    }
    /**
     * Convert the rows of a bot API keyboard: keyboardButtonRow/keyboardButton with a ButtonType for reply
     * keyboards, keyboardInlineButtonRow/keyboardInlineButton with an InlineButtonType for inline ones.
     *
     * @return list<array{_: string, buttons: list<array{_: string, text: string, type?: array, style?: array}>}>
     */
    private function parseButtons(array $rows, bool $inline): array
    {
        $newrows = [];
        $key = 0;
        foreach ($rows as $row) {
            $newrows[$key] = ['_' => $inline ? 'keyboardInlineButtonRow' : 'keyboardButtonRow', 'buttons' => []];
            $button_key = 0;
            foreach ($row as $button) {
                $text = $button['text'];
                $newrows[$key]['buttons'][$button_key] = [
                    '_' => $inline ? 'keyboardInlineButton' : 'keyboardButton',
                    'text' => $button['text'],
                ];
                if (!$inline) {
                    $newrows[$key]['buttons'][$button_key]['type']['_'] = 'buttonTypeDefault';
                }
                if (isset($button['style']) || isset($button['icon_custom_emoji_id'])) {
                    $newrows[$key]['buttons'][$button_key]['style']['_'] = 'keyboardButtonStyle';
                    $bgKey = match ($button['style'] ?? null) {
                        'primary' => 'bg_primary',
                        'danger'  => 'bg_danger',
                        'success' => 'bg_success',
                        default   => null,
                    };
                    if ($bgKey !== null) {
                        $newrows[$key]['buttons'][$button_key]['style'][$bgKey] = true;
                    }
                    if (isset($button['icon_custom_emoji_id'])) {
                        $newrows[$key]['buttons'][$button_key]['style']['icon'] = $button['icon_custom_emoji_id'];
                    }
                }
                if (isset($button['url'])) {
                    if (str_starts_with($button['url'], 'tg://user?id=')) {
                        $newrows[$key]['buttons'][$button_key]['type']['_'] = 'inputInlineButtonTypeUserProfile';
                        $newrows[$key]['buttons'][$button_key]['type']['user_id'] = str_replace(
                            'tg://user?id=',
                            '',
                            $button['url']
                        );
                    } else {
                        $newrows[$key]['buttons'][$button_key]['type']['_'] = 'inlineButtonTypeUrl';
                        $newrows[$key]['buttons'][$button_key]['type']['url'] = $button['url'];
                    }
                } elseif (isset($button['pay'])) {
                    $newrows[$key]['buttons'][$button_key]['type']['_'] = 'inlineButtonTypeBuy';
                } elseif (isset($button['callback_data'])) {
                    $newrows[$key]['buttons'][$button_key]['type']['_'] = 'inlineButtonTypeCallback';
                    $newrows[$key]['buttons'][$button_key]['type']['data'] = (string) $button['callback_data'];
                } elseif (isset($button['login_url'])) {
                    $button = $button['login_url'];
                    $newrows[$key]['buttons'][$button_key]['type']['_'] = 'inputInlineButtonTypeUrlAuth';
                    $newrows[$key]['buttons'][$button_key]['type']['request_write_access'] = $button['request_write_access'] ?? false;
                    if (isset($button['forward_text'])) {
                        $newrows[$key]['buttons'][$button_key]['type']['fwd_text'] = $button['forward_text'];
                    }
                    $newrows[$key]['buttons'][$button_key]['type']['url'] = $button['url'];
                    if (isset($button['bot_username'])) {
                        $newrows[$key]['buttons'][$button_key]['type']['bot'] = $button['bot_username'];
                    }
                } elseif (isset($button['web_app'])) {
                    $newrows[$key]['buttons'][$button_key]['type']['_'] = $inline
                        ? 'inlineButtonTypeWebView'
                        : 'buttonTypeSimpleWebView';
                    $newrows[$key]['buttons'][$button_key]['type']['url'] = $button['web_app']['url'];
                } elseif (isset($button['switch_inline_query'])) {
                    $newrows[$key]['buttons'][$button_key]['type']['_'] = 'inlineButtonTypeSwitchInline';
                    $newrows[$key]['buttons'][$button_key]['type']['same_peer'] = false;
                    $newrows[$key]['buttons'][$button_key]['type']['query'] = $button['switch_inline_query'];
                } elseif (isset($button['switch_inline_query_current_chat'])) {
                    $newrows[$key]['buttons'][$button_key]['type']['_'] = 'inlineButtonTypeSwitchInline';
                    $newrows[$key]['buttons'][$button_key]['type']['same_peer'] = true;
                    $newrows[$key]['buttons'][$button_key]['type']['query'] = $button['switch_inline_query_current_chat'] ?? '';
                } elseif (isset($button['switch_inline_query_chosen_chat'])) {
                    $button = $button['switch_inline_query_chosen_chat'];
                    $newrows[$key]['buttons'][$button_key]['type']['_'] = 'inlineButtonTypeSwitchInline';
                    $newrows[$key]['buttons'][$button_key]['type']['same_peer'] = false;
                    $newrows[$key]['buttons'][$button_key]['type']['query'] = $button['query'] ?? '';
                    $peer_types = [];
                    if ($button['allow_user_chats'] ?? false) {
                        $peer_types []= ['_' => 'inlineQueryPeerTypePM'];
                    }
                    if ($button['allow_bot_chats'] ?? false) {
                        $peer_types []= ['_' => 'inlineQueryPeerTypeBotPM'];
                    }
                    if ($button['allow_group_chats'] ?? false) {
                        $peer_types []= ['_' => 'inlineQueryPeerTypeChat'];
                        $peer_types []= ['_' => 'inlineQueryPeerTypeMegagroup'];
                    }
                    if ($button['allow_channel_chats'] ?? false) {
                        $peer_types []= ['_' => 'inlineQueryPeerTypeBroadcast'];
                    }
                    if (!empty($peer_types)) {
                        $newrows[$key]['buttons'][$button_key]['type']['peer_types'] = $peer_types;
                    }
                } elseif (isset($button['callback_game'])) {
                    $newrows[$key]['buttons'][$button_key]['type']['_'] = 'inlineButtonTypeGame';
                } elseif (isset($button['request_contact']) && $button['request_contact']) {
                    $newrows[$key]['buttons'][$button_key]['type']['_'] = 'buttonTypeRequestPhone';
                } elseif (isset($button['request_location']) && $button['request_location']) {
                    $newrows[$key]['buttons'][$button_key]['type']['_'] = 'buttonTypeRequestGeoLocation';
                } elseif (isset($button['copy_text'])) {
                    $newrows[$key]['buttons'][$button_key]['type']['_'] = 'inlineButtonTypeCopy';
                    $newrows[$key]['buttons'][$button_key]['type']['copy_text'] = $button['copy_text']['text'];
                } elseif (isset($button['request_poll'])) {
                    $newrows[$key]['buttons'][$button_key]['type']['_'] = 'buttonTypeRequestPoll';
                    // Without a type, any poll can be created.
                    if (isset($button['request_poll']['type'])) {
                        $newrows[$key]['buttons'][$button_key]['type']['quiz'] = $button['request_poll']['type'] === 'quiz';
                    }
                }
                if (!isset($newrows[$key]['buttons'][$button_key]['type'])) {
                    throw new Exception("The inline keyboard button \"$text\" has no action (url, callback_data, ...)");
                }
                $button_key++;
            }
            $key++;
        }
        return $newrows;
    }
    private function parseReplyMarkup($markup)
    {
        if (isset($markup['force_reply']) && $markup['force_reply']) {
            $markup['_'] = 'replyKeyboardForceReply';
            unset($markup['force_reply']);
        }
        if (isset($markup['remove_keyboard']) && $markup['remove_keyboard']) {
            $markup['_'] = 'replyKeyboardHide';
            unset($markup['remove_keyboard']);
        }
        if (isset($markup['keyboard'])) {
            $markup['_'] = 'replyKeyboardMarkup';
            if (isset($markup['resize_keyboard'])) {
                $markup['resize'] = $markup['resize_keyboard'];
                unset($markup['resize_keyboard']);
            }
            if (isset($markup['one_time_keyboard'])) {
                $markup['single_use'] = $markup['one_time_keyboard'];
                unset($markup['one_time_keyboard']);
            }
            $markup['rows'] = $this->parseButtons($markup['keyboard'], false);
            unset($markup['keyboard']);
        }
        if (isset($markup['inline_keyboard'])) {
            $markup['_'] = 'replyInlineMarkup';
            $markup['rows'] = $this->parseButtons($markup['inline_keyboard'], true);
            unset($markup['inline_keyboard']);
        }
        return $markup;
    }
    /**
     * Convert MTProto parameters to bot API parameters.
     *
     * @param array $data Data
     */
    public function MTProtoToBotAPI(array $data): array
    {
        $newd = [];
        if (!isset($data['_'])) {
            foreach ($data as $key => $element) {
                $newd[$key] = $this->MTProtoToBotAPI($element);
            }
            return $newd;
        }
        $res = null;
        switch ($data['_']) {
            case 'storyItem':
                return $this->MTProtoToBotAPI($data['media']);
            case 'updates':
            case 'updatesCombined':
            case 'updateShort':
            case 'updateShortSentMessage':
            case 'updateShortMessage':
            case 'updateShortChatMessage':
                $data = $this->extractMessageUpdate($data);
                // no break
            case 'updateNewChannelMessage':
            case 'updateNewMessage':
                $data = $data['message'];
                // no break
            case 'message':
                $newd['message_id'] = $data['id'];
                $newd['date'] = $data['date'];
                $newd['text'] = $data['message'];
                $newd['post'] = $data['post'];
                $newd['silent'] = $data['silent'];
                if (isset($data['from_id'])) {
                    $newd['from'] = ($this->getPwrChat($data['from_id'], false));
                }
                $newd['chat'] = ($this->getPwrChat($data['peer_id'], false));
                if (isset($data['entities'])) {
                    $newd['entities'] = ($this->MTProtoToBotAPI($data['entities']));
                }
                if (isset($data['views'])) {
                    $newd['views'] = $data['views'];
                }
                if (isset($data['edit_date'])) {
                    $newd['edit_date'] = $data['edit_date'];
                }
                if (isset($data['via_bot_id'])) {
                    $newd['via_bot'] = ($this->getPwrChat($data['via_bot_id'], false));
                }
                if (isset($data['fwd_from']['from_id'])) {
                    $newd['forward_from'] = ($this->getPwrChat($data['fwd_from']['from_id'], false));
                }
                if (isset($data['fwd_from']) && $data['fwd_from'] < 0) {
                    try {
                        $newd['forward_from_chat'] = $this->getPwrChat($data['fwd_from'], false);
                    } catch (Throwable $e) {
                    }
                }
                if (isset($data['fwd_from']['date'])) {
                    $newd['forward_date'] = $data['fwd_from']['date'];
                }
                if (isset($data['fwd_from']['channel_post'])) {
                    $newd['forward_from_message_id'] = $data['fwd_from']['channel_post'];
                }
                if (isset($data['media']) && $data['media']['_'] !== 'messageMediaWebPage') {
                    $newd = array_merge($newd, $this->MTProtoToBotAPI($data['media']));
                }
                return $newd;
            case 'messageEntityMentionName':
            case 'inputMessageEntityMentionName':
                unset($data['_']);
                $data['type'] = 'text_mention';
                $data['user'] = ($this->getPwrChat($data['user_id'], false));
                unset($data['user_id']);
                return $data;
            case 'photo':
                $data = ['photo' => $data];
                // no break
            case 'messageMediaPhoto':
                if (isset($data['caption'])) {
                    $res['caption'] = $data['caption'];
                }
                $res['photo'] = [];
                foreach ($data['photo']['sizes'] as $key => $photo) {
                    if (\in_array($photo['_'], ['photoCachedSize', 'photoSize', 'photoSizeProgressive'], true)) {
                        $res['photo'][$key] = $this->photosizeToBotAPI($photo, $data['photo']);
                    }
                }
                return $res;
            case 'messageMediaEmpty':
                return [];
            case 'decryptedMessageMediaExternalDocument':
                $data = ['document' => $data];
                // no break
            case 'messageMediaDocument':
                $type_name = 'document';
                $res = [];
                if (isset($data['document']['thumbs']) && $data['document']['thumbs'] && \in_array(end($data['document']['thumbs'])['_'], ['photoCachedSize', 'photoSize', 'photoSizeProgressive'], true)) {
                    $res['thumb'] = $this->photosizeToBotAPI(end($data['document']['thumbs']), $data['document'], true);
                }
                foreach ($data['document']['attributes'] as $attribute) {
                    switch ($attribute['_']) {
                        case 'documentAttributeFilename':
                            $pathinfo = pathinfo($attribute['file_name']);
                            $res['ext'] = isset($pathinfo['extension']) ? '.'.$pathinfo['extension'] : '';
                            $res['file_name'] = $pathinfo['filename'];
                            break;
                        case 'documentAttributeAudio':
                            $audio = $attribute;
                            // Round videos may also have an audio attribute, with the waveform.
                            if ($type_name !== 'video_note') {
                                $type_name = $attribute['voice'] ? 'voice' :'audio';
                            }
                            $res['duration'] = $attribute['duration'];
                            if (isset($attribute['performer'])) {
                                $res['performer'] = $attribute['performer'];
                            }
                            if (isset($attribute['title'])) {
                                $res['title'] = $attribute['title'];
                            }
                            if (isset($attribute['waveform'])) {
                                $res['waveform'] = $attribute['waveform'];
                            }
                            break;
                        case 'documentAttributeVideo':
                            $type_name = $attribute['round_message'] ? 'video_note' : 'video';
                            $res['width'] = $attribute['w'];
                            $res['height'] = $attribute['h'];
                            $res['duration'] = $attribute['duration'];
                            break;
                        case 'documentAttributeImageSize':
                            $res['width'] = $attribute['w'];
                            $res['height'] = $attribute['h'];
                            break;
                        case 'documentAttributeAnimated':
                            $type_name = 'animation';
                            $res['animated'] = true;
                            break;
                        case 'documentAttributeHasStickers':
                            $res['has_stickers'] = true;
                            break;
                        case 'documentAttributeSticker':
                            $type_name = 'sticker';
                            $res['mask'] = $attribute['mask'] ?? false;
                            $res['emoji'] = $attribute['alt'];
                            $res['sticker_set'] = $attribute['stickerset'];
                            if (isset($attribute['mask_coords'])) {
                                $res['mask_coords'] = $attribute['mask_coords'];
                            }
                            break;
                    }
                }
                if (isset($audio) && isset($audio['title']) && !isset($res['file_name'])) {
                    $res['file_name'] = $audio['title'];
                    if (isset($audio['performer'])) {
                        $res['file_name'] .= ' - '.$audio['performer'];
                    }
                }
                if (!isset($res['file_name'])) {
                    $res['file_name'] = $data['document']['access_hash'];
                }
                $res['file_name'] .= '_'.$data['document']['id'];
                if (isset($res['ext'])) {
                    $res['file_name'] .= $res['ext'];
                    unset($res['ext']);
                } else {
                    $res['file_name'] .= Tools::getExtensionFromMime($data['document']['mime_type']);
                }
                $res['file_size'] = $data['document']['size'];
                $res['mime_type'] = $data['document']['mime_type'];

                $fileId = new FileId(
                    id: $data['document']['id'],
                    accessHash: $data['document']['access_hash'],
                    fileReference: $data['document']['file_reference'] === null
                        ? null
                        : (string) $data['document']['file_reference'],
                    dcId: $data['document']['dc_id'],
                    type: FileIdType::from($type_name)
                );

                $res['file_id'] = (string) $fileId;
                $res['file_unique_id'] = $fileId->getUniqueBotAPI();
                return [$type_name => $res, 'caption' => $data['caption'] ?? ''];
            case 'decryptedMessageMediaAudio':
            case 'decryptedMessageMediaPhoto':
            case 'decryptedMessageMediaVideo':
            case 'decryptedMessageMediaDocument':
                $data = $data['file'];
                // no break
            case 'encryptedFile':
                $fileId = new FileId(
                    id: $data['id'],
                    accessHash: $data['access_hash'],
                    fileReference: null,
                    dcId: $data['dc_id'],
                    type: FileIdType::ENCRYPTED
                );

                $res = [
                    'file_id' => (string) $fileId,
                    'file_unique_id' => $fileId->getUniqueBotAPI(),
                    'file_size' => $data['size'],
                    'mime_type' => 'application/octet-stream',
                ];
                return ['encrypted' => $res];
            default:
                return self::MTProtoEntityToBotAPI($data);
        }
    }
    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public static function MTProtoEntityToBotAPI(array $data): array
    {
        return MessageEntity::fromRawEntity($data)->toBotAPI();
    }
    /**
     * Convert bot API parameters to MTProto parameters.
     *
     * @param array $arguments Arguments
     */
    public function botAPIToMTProto(array $arguments): array
    {
        foreach (self::BOTAPI_PARAMS_CONVERSION as $bot => $mtproto) {
            if (isset($arguments[$bot]) && !isset($arguments[$mtproto])) {
                $arguments[$mtproto] = $arguments[$bot];
                //unset($arguments[$bot]);
            }
        }
        if (isset($arguments['reply_markup'])) {
            $arguments['reply_markup'] = $this->parseReplyMarkup($arguments['reply_markup']);
        }
        if (isset($arguments['results']) && \is_array($arguments['results'])) {
            foreach ($arguments['results'] as &$result) {
                if (isset($result['send_message']['reply_markup'])) {
                    $result['send_message']['reply_markup'] = $this->parseReplyMarkup($result['send_message']['reply_markup']);
                }
            }
            unset($result);
        }
        if (isset($arguments['parse_mode'])) {
            $arguments = $this->parseMode($arguments);
        }
        return $arguments;
    }
    /**
     * Convert markdown and HTML messages.
     *
     * @param array $arguments Arguments
     * @internal
     */
    public static function parseMode(array $arguments): array
    {
        $key = isset($arguments['caption']) ? 'caption' : 'message';
        if (($arguments[$key] ?? '') === '' || !isset($arguments['parse_mode'])) {
            return $arguments;
        }
        if (!(\is_string($arguments[$key]) || \is_object($arguments[$key]) && method_exists($arguments[$key], '__toString'))) {
            throw new Exception('Messages can only be strings');
        }
        if ($arguments['parse_mode'] instanceof \danog\MadelineProto\ParseMode) {
            $arguments['parse_mode'] = $arguments['parse_mode']->value;
        }
        if (isset($arguments['parse_mode']['_'])) {
            $arguments['parse_mode'] = str_replace('textParseMode', '', $arguments['parse_mode']['_']);
        }
        if (stripos($arguments['parse_mode'], 'markdown') !== false) {
            $entities = StrTools::markdownToMessageEntities($arguments[$key]);
            $arguments[$key] = $entities->message;
            $arguments['entities'] = array_merge($arguments['entities'] ?? [], $entities->entities);
            unset($arguments['parse_mode']);
        } elseif (stripos($arguments['parse_mode'], 'html') !== false) {
            $entities = StrTools::htmlToMessageEntities($arguments[$key]);
            $arguments[$key] = $entities->message;
            $arguments['entities'] = array_merge($arguments['entities'] ?? [], $entities->entities);
            unset($arguments['parse_mode']);
        }
        return $arguments;
    }

    /**
     * Split too long message into chunks.
     *
     * @param array $args Arguments
     * @internal
     */
    public function splitToChunks(array $args): array
    {
        $args = $this->parseMode($args);
        $args['entities'] ??= [];

        // UTF-8 length, not UTF-16
        $max_length = isset($args['media']) ? $this->config['caption_length_max'] : $this->config['message_length_max'];
        $cur_len = 0;
        $cur = '';
        $multiple_args_base = array_merge($args, ['entities' => [], 'parse_mode' => 'text', 'message' => '']);
        unset($multiple_args_base['message']);
        $multiple_args = [];
        foreach (explode("\n", $args['message']) as $word) {
            foreach (mb_str_split($word."\n", $max_length, 'UTF-8') as $vv) {
                $len = mb_strlen($vv, 'UTF-8');
                if ($cur_len + $len <= $max_length) {
                    $cur .= $vv;
                    $cur_len += $len;
                } else {
                    if (trim($cur) !== '') {
                        $multiple_args[] = [
                            ...$multiple_args_base,
                            'message' => $cur,
                        ];
                    }
                    $cur = $vv;
                    $cur_len = $len;
                }
            }
        }
        if (trim($cur) !== '') {
            $multiple_args[] = [
                ...$multiple_args_base,
                'message' => $cur,
            ];
        }

        $i = 0;
        $offset = 0;
        for ($k = 0; $k < \count($args['entities']); $k++) {
            $entity = $args['entities'][$k];
            if ($entity instanceof MessageEntity) {
                $entity = $entity->toMTProto();
            }
            do {
                while ($entity['offset'] > $offset + StrTools::mbStrlen($multiple_args[$i]['message'])) {
                    $offset += StrTools::mbStrlen($multiple_args[$i]['message']);
                    $i++;
                }
                $entity['offset'] -= $offset;
                if ($entity['offset'] + $entity['length'] > StrTools::mbStrlen($multiple_args[$i]['message'])) {
                    $newentity = $entity;
                    $newentity['length'] = $entity['length'] - (StrTools::mbStrlen($multiple_args[$i]['message']) - $entity['offset']);
                    $entity['length'] = StrTools::mbStrlen($multiple_args[$i]['message']) - $entity['offset'];
                    $offset += $entity['length'];
                    $newentity['offset'] = $offset;
                    $orig = $multiple_args[$i]['message'];
                    $trimmed = rtrim($orig);
                    $diff = StrTools::mbStrlen($orig) - StrTools::mbStrlen($trimmed);
                    $entity['length'] -= $diff;
                    $multiple_args[$i]['message'] = $trimmed;
                    $multiple_args[$i]['entities'][] = $entity;
                    $i++;
                    $entity = $newentity;
                    continue;
                }
                $multiple_args[$i]['entities'][] = $entity;
                break;
            } while (true);
        }
        $total = 0;
        foreach ($multiple_args as $args) {
            if (\count($args['entities']) > self::MAX_ENTITY_LENGTH) {
                $total += \count($args['entities']) - self::MAX_ENTITY_LENGTH;
            }
            $c = 0;
            foreach ($args['entities'] as $entity) {
                if (isset($entity['url'])) {
                    $c += \strlen($entity['url']);
                }
            }
            if ($c >= self::MAX_ENTITY_SIZE) {
                $this->logger->logger('Entity size limit possibly exceeded, you may get an error indicating that the entities are too long. Reduce the number of entities and/or size of the URLs used.', Logger::FATAL_ERROR);
            }
        }
        if ($total) {
            $this->logger->logger("Too many entities, {$total} entities will be truncated", Logger::FATAL_ERROR);
        }
        return $multiple_args;
    }
}
<?php

declare(strict_types=1);

/**
 * TD module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\TL\Conversion;

use danog\DialogId\DialogId;
use danog\MadelineProto\Lang;

/**
 * @internal
 */
trait TD
{
    /**
     * Convert tdcli parameters to tdcli.
     *
     * @param mixed $params Params
     * @param array $key    Key
     */
    public function tdcliToTd(&$params, ?array $key = null): array
    {
        if (!\is_array($params)) {
            return $params;
        }
        if (!isset($params['ID'])) {
            array_walk($params, [$this, 'tdcliToTd']);
            return $params;
        }
        foreach ($params as $key => $value) {
            $value = $this->tdcliToTd($value);
            if (preg_match('/_$/', $key)) {
                $params[preg_replace('/_$/', '', $key)] = $value;
                unset($params[$key]);
            }
        }
        $params['_'] = lcfirst($params['ID']);
        unset($params['ID']);
        return $params;
    }
    /**
     * Convert TD to MTProto parameters.
     *
     * @param array $params Parameters
     */
    public function tdToMTProto(array $params): array
    {
        $newparams = ['_' => self::TD_REVERSE[$params['_']]];
        foreach (self::TD_PARAMS_CONVERSION[$newparams['_']] as $td => $mtproto) {
            switch (end($mtproto)) {
                case 'choose_message_content':
                    switch ($params[$td]['_']) {
                        case 'inputMessageText':
                            $params[$td]['_'] = 'messages.sendMessage';
                            if (isset($params['disable_web_page_preview'])) {
                                $newparams['no_webpage'] = $params[$td]['disable_web_page_preview'];
                            }
                            $newparams = array_merge($params[$td], $newparams);
                            break;
                        default:
                            throw new Exception(Lang::$current_lang['non_text_conversion']);
                    }
                    break;
                default:
                    $newparams[$mtproto[0]] = $params[$td] ?? null;
                    if (\is_array($newparams[$mtproto[0]])) {
                        $newparams[$mtproto[0]] = ($this->MTProtoToTd($newparams[$mtproto[0]]));
                    }
            }
        }
        return $newparams;
    }
    /**
     * MTProto to TDCLI params.
     *
     * @param mixed $params Params
     */
    public function MTProtoToTdcli(mixed $params): array
    {
        return $this->tdToTdcli($this->MTProtoToTd($params));
    }
    /**
     * MTProto to TD params.
     *
     * @param mixed $params Params
     */
    public function MTProtoToTd(mixed &$params): array
    {
        if (!\is_array($params)) {
            return $params;
        }
        if (!isset($params['_'])) {
            array_walk($params, [$this, 'mtprotoToTd']);
            return $params;
        }
        $newparams = ['_' => $params['_']];
        if (\in_array($params['_'], self::TD_IGNORE, true)) {
            return $params;
        }
        foreach (self::TD_PARAMS_CONVERSION[$params['_']] as $td => $mtproto) {
            if (\is_string($mtproto)) {
                $newparams[$td] = $mtproto;
            } else {
                switch (end($mtproto)) {
                    case 'choose_chat_id_from_botapi':
                        $newparams[$td] = ($this->getInfo($params[$mtproto[0]]))['bot_api_id'] == $this->authorization['user']['id'] ? $this->getIdInternal($params['from_id']) : ($this->getInfo($params[$mtproto[0]]))['bot_api_id'];
                        break;
                    case 'choose_incoming_or_sent':
                        $newparams[$td] = ['_' => $params['out'] ? 'messageIsSuccessfullySent' : 'messageIsIncoming'];
                        break;
                    case 'choose_can_edit':
                        $newparams[$td] = !isset($params['fwd_from']) && $params['out'];
                        break;
                    case 'choose_can_delete':
                        $newparams[$td] = $params['out'];
                        break;
                    case 'choose_forward_info':
                        if (isset($params['fwd_from'])) {
                            $newparams[$td] = ['_' => 'messageForwardedFromUser'];
                            if (DialogId::isSupergroupOrChannelOrMonoforum($params['fwd_from'])) {
                                $newparams[$td] = ['_' => 'messageForwardedPost', 'chat_id' => $params['fwd_from']];
                            }
                            $newparams[$td]['date'] = $params['fwd_from']['date'];
                            if (isset($params['fwd_from']['channel_post'])) {
                                $newparams[$td]['channel_post'] = $params['fwd_from']['channel_post'];
                            }
                            if (isset($params['fwd_from']['from_id'])) {
                                $newparams[$td]['sender_user_id'] = $this->getIdInternal($params['fwd_from']['from_id']);
                            }
                        } else {
                            $newparams[$td] = null;
                        }
                        break;
                    case 'choose_ttl':
                        $newparams[$td] = $params['ttl'] ?? 0;
                        break;
                    case 'choose_ttl_expires_in':
                        $newparams[$td] = $newparams['ttl'] - microtime(true);
                        break;
                    case 'choose_message_content':
                        if ($params['message'] !== '') {
                            $newparams[$td] = ['_' => 'messageText', 'text' => $params['message']];
                            if (isset($params['media']['_']) && $params['media']['_'] === 'messageMediaWebPage') {
                                $newparams[$td]['web_page'] = ($this->MTProtoToTd($params['media']['webpage']));
                            }
                            if (isset($params['entities'])) {
                                $newparams[$td]['entities'] = $params['entities'];
                            }
                        } else {
                            throw new Exception(Lang::$current_lang['non_text_conversion']);
                        }
                        break;
                    default:
                        if (isset($mtproto[1])) {
                            $newparams[$td] = $params[$mtproto[0]][$mtproto[1]] ?? null;
                        } else {
                            $newparams[$td] = $params[$mtproto[0]] ?? null;
                        }
                        if (\is_array($newparams[$td])) {
                            $newparams[$td] = ($this->MTProtoToTd($newparams[$td]));
                        }
                }
            }
        }
        return $newparams;
    }
    /**
     * Convert TD parameters to tdcli.
     *
     * @param mixed $params Parameters
     *
     * @psalm-mutation-free
     */
    public function tdToTdcli(mixed $params): array
    {
        if (!\is_array($params)) {
            return $params;
        }
        $newparams = [];
        foreach ($params as $key => $value) {
            if ($key === '_') {
                $newparams['ID'] = ucfirst($value);
            } else {
                if (!is_numeric($key) && !str_ends_with($key, '_')) {
                    $key = $key.'_';
                }
                $newparams[$key] = $this->tdToTdcli($value);
            }
        }
        return $newparams;
    }
}
<?php

declare(strict_types=1);

/**
 * Exception module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\TL\Conversion;

use danog\MadelineProto\Magic;
use danog\MadelineProto\TL\PrettyException;

use const PHP_EOL;
use const PHP_SAPI;

/**
 * TL conversion exception.
 */
final class Exception extends \Exception
{
    use PrettyException;
    /**
     * @psalm-external-mutation-free
     */
    public function __toString(): string
    {
        $result = static::class.($this->message !== '' ? ': ' : '').$this->message.PHP_EOL.Magic::$revision.PHP_EOL.'TL Trace:'.PHP_EOL.PHP_EOL.$this->getTLTrace().PHP_EOL;
        if (PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg') {
            $result = str_replace(PHP_EOL, '<br>'.PHP_EOL, $result);
        }
        return $result;
    }
    public function __construct($message, $file = '')
    {
        parent::__construct($message);
        $this->prettifyTL($file);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\TL\Conversion;

use danog\MadelineProto\Magic;
use finfo;

use const FILEINFO_MIME_TYPE;

/**
 * Manages generation of extensions for files.
 */
abstract class Extension
{
    public const ALL_MIMES = ['webp' => [0 => 'image/webp'], 'png' => [0 => 'image/png', 1 => 'image/x-png'], 'bmp' => [0 => 'image/bmp', 1 => 'image/x-bmp', 2 => 'image/x-bitmap', 3 => 'image/x-xbitmap', 4 => 'image/x-win-bitmap', 5 => 'image/x-windows-bmp', 6 => 'image/ms-bmp', 7 => 'image/x-ms-bmp', 8 => 'application/bmp', 9 => 'application/x-bmp', 10 => 'application/x-win-bitmap'], 'gif' => [0 => 'image/gif'], 'jpeg' => [0 => 'image/jpeg', 1 => 'image/pjpeg'], 'xspf' => [0 => 'application/xspf+xml'], 'vlc' => [0 => 'application/videolan'], 'wmv' => [0 => 'video/x-ms-wmv', 1 => 'video/x-ms-asf'], 'au' => [0 => 'audio/x-au'], 'ac3' => [0 => 'audio/ac3'], 'flac' => [0 => 'audio/x-flac'], 'ogg' => [0 => 'audio/ogg', 1 => 'video/ogg', 2 => 'application/ogg'], 'kmz' => [0 => 'application/vnd.google-earth.kmz'], 'kml' => [0 => 'application/vnd.google-earth.kml+xml'], 'rtx' => [0 => 'text/richtext'], 'rtf' => [0 => 'text/rtf'], 'jar' => [0 => 'application/java-archive', 1 => 'application/x-java-application', 2 => 'application/x-jar'], 'zip' => [0 => 'application/x-zip', 1 => 'application/zip', 2 => 'application/x-zip-compressed', 3 => 'application/s-compressed', 4 => 'multipart/x-zip'], '7zip' => [0 => 'application/x-compressed'], 'xml' => [0 => 'application/xml', 1 => 'text/xml'], 'svg' => [0 => 'image/svg+xml'], '3g2' => [0 => 'video/3gpp2'], '3gp' => [0 => 'video/3gp', 1 => 'video/3gpp'], 'mp4' => [0 => 'video/mp4'], 'm4a' => [0 => 'audio/x-m4a'], 'f4v' => [0 => 'video/x-f4v'], 'flv' => [0 => 'video/x-flv'], 'webm' => [0 => 'video/webm'], 'aac' => [0 => 'audio/x-acc'], 'm4u' => [0 => 'application/vnd.mpegurl'], 'pdf' => [0 => 'application/pdf', 1 => 'application/octet-stream'], 'pptx' => [0 => 'application/vnd.openxmlformats-officedocument.presentationml.presentation'], 'ppt' => [0 => 'application/powerpoint', 1 => 'application/vnd.ms-powerpoint', 2 => 'application/vnd.ms-office', 3 => 'application/msword'], 'docx' => [0 => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'], 'xlsx' => [0 => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 1 => 'application/vnd.ms-excel'], 'xl' => [0 => 'application/excel'], 'xls' => [0 => 'application/msexcel', 1 => 'application/x-msexcel', 2 => 'application/x-ms-excel', 3 => 'application/x-excel', 4 => 'application/x-dos_ms_excel', 5 => 'application/xls', 6 => 'application/x-xls'], 'xsl' => [0 => 'text/xsl'], 'mpeg' => [0 => 'video/mpeg'], 'mov' => [0 => 'video/quicktime'], 'avi' => [0 => 'video/x-msvideo', 1 => 'video/msvideo', 2 => 'video/avi', 3 => 'application/x-troff-msvideo'], 'movie' => [0 => 'video/x-sgi-movie'], 'log' => [0 => 'text/x-log'], 'txt' => [0 => 'text/plain'], 'css' => [0 => 'text/css'], 'html' => [0 => 'text/html'], 'wav' => [0 => 'audio/x-wav', 1 => 'audio/wave', 2 => 'audio/wav'], 'xhtml' => [0 => 'application/xhtml+xml'], 'tar' => [0 => 'application/x-tar'], 'tgz' => [0 => 'application/x-gzip-compressed'], 'psd' => [0 => 'application/x-photoshop', 1 => 'image/vnd.adobe.photoshop'], 'exe' => [0 => 'application/x-msdownload'], 'js' => [0 => 'application/x-javascript'], 'mp3' => [0 => 'audio/mpeg', 1 => 'audio/mpg', 2 => 'audio/mpeg3', 3 => 'audio/mp3'], 'rar' => [0 => 'application/x-rar', 1 => 'application/rar', 2 => 'application/x-rar-compressed'], 'gzip' => [0 => 'application/x-gzip'], 'hqx' => [0 => 'application/mac-binhex40', 1 => 'application/mac-binhex', 2 => 'application/x-binhex40', 3 => 'application/x-mac-binhex40'], 'cpt' => [0 => 'application/mac-compactpro'], 'bin' => [0 => 'application/macbinary', 1 => 'application/mac-binary', 2 => 'application/x-binary', 3 => 'application/x-macbinary'], 'oda' => [0 => 'application/oda'], 'ai' => [0 => 'application/postscript'], 'smil' => [0 => 'application/smil'], 'mif' => [0 => 'application/vnd.mif'], 'wbxml' => [0 => 'application/wbxml'], 'wmlc' => [0 => 'application/wmlc'], 'dcr' => [0 => 'application/x-director'], 'dvi' => [0 => 'application/x-dvi'], 'gtar' => [0 => 'application/x-gtar'], 'php' => [0 => 'application/x-httpd-php', 1 => 'application/php', 2 => 'application/x-php', 3 => 'text/php', 4 => 'text/x-php', 5 => 'application/x-httpd-php-source'], 'swf' => [0 => 'application/x-shockwave-flash'], 'sit' => [0 => 'application/x-stuffit'], 'z' => [0 => 'application/x-compress'], 'mid' => [0 => 'audio/midi'], 'aif' => [0 => 'audio/x-aiff', 1 => 'audio/aiff'], 'ram' => [0 => 'audio/x-pn-realaudio'], 'rpm' => [0 => 'audio/x-pn-realaudio-plugin'], 'ra' => [0 => 'audio/x-realaudio'], 'rv' => [0 => 'video/vnd.rn-realvideo'], 'jp2' => [0 => 'image/jp2', 1 => 'video/mj2', 2 => 'image/jpx', 3 => 'image/jpm'], 'tiff' => [0 => 'image/tiff'], 'eml' => [0 => 'message/rfc822'], 'pem' => [0 => 'application/x-x509-user-cert', 1 => 'application/x-pem-file'], 'p10' => [0 => 'application/x-pkcs10', 1 => 'application/pkcs10'], 'p12' => [0 => 'application/x-pkcs12'], 'p7a' => [0 => 'application/x-pkcs7-signature'], 'p7c' => [0 => 'application/pkcs7-mime', 1 => 'application/x-pkcs7-mime'], 'p7r' => [0 => 'application/x-pkcs7-certreqresp'], 'p7s' => [0 => 'application/pkcs7-signature'], 'crt' => [0 => 'application/x-x509-ca-cert', 1 => 'application/pkix-cert'], 'crl' => [0 => 'application/pkix-crl', 1 => 'application/pkcs-crl'], 'pgp' => [0 => 'application/pgp'], 'gpg' => [0 => 'application/gpg-keys'], 'rsa' => [0 => 'application/x-pkcs7'], 'ics' => [0 => 'text/calendar'], 'zsh' => [0 => 'text/x-scriptzsh'], 'cdr' => [0 => 'application/cdr', 1 => 'application/coreldraw', 2 => 'application/x-cdr', 3 => 'application/x-coreldraw', 4 => 'image/cdr', 5 => 'image/x-cdr', 6 => 'zz-application/zz-winassoc-cdr'], 'wma' => [0 => 'audio/x-ms-wma'], 'vcf' => [0 => 'text/x-vcard'], 'srt' => [0 => 'text/srt'], 'vtt' => [0 => 'text/vtt'], 'ico' => [0 => 'image/x-icon', 1 => 'image/x-ico', 2 => 'image/vnd.microsoft.icon'], 'csv' => [0 => 'text/x-comma-separated-values', 1 => 'text/comma-separated-values', 2 => 'application/vnd.msexcel'], 'json' => [0 => 'application/json', 1 => 'text/json']];
    /**
     * Get mime type from file extension.
     *
     * @param string $extension File extension
     * @param string $default   Default mime type
     *
     * @psalm-pure
     */
    public static function getMimeFromExtension(string $extension, string $default): string
    {
        $ext = ltrim($extension, '.');
        if (isset(self::ALL_MIMES[$ext])) {
            return self::ALL_MIMES[$ext][0];
        }
        return $default;
    }
    /**
     * Get extension from mime type.
     *
     * @param string $mime MIME type
     *
     * @psalm-external-mutation-free
     */
    public static function getExtensionFromMime(string $mime): string
    {
        return Magic::$allMimes[$mime] ?? '';
    }
    /**
     * Get extension from file location.
     *
     * @param mixed  $location File location
     * @param string $default  Default extension
     *
     * @psalm-pure
     */
    public static function getExtensionFromLocation(mixed $location, string $default): string
    {
        return $default;
    }
    /**
     * Get mime type of file.
     *
     * @param string $file File
     */
    public static function getMimeFromFile(string $file): string
    {
        $finfo = new finfo(FILEINFO_MIME_TYPE);
        return $finfo->file($file);
    }
    /**
     * Get mime type from buffer.
     *
     * @param string $buffer Buffer
     */
    public static function getMimeFromBuffer(string $buffer): string
    {
        $finfo = new finfo(FILEINFO_MIME_TYPE);
        return $finfo->buffer($buffer);
    }
}
<?php

declare(strict_types=1);

/**
 * TL module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\TL;

use AssertionError;
use danog\DialogId\DialogId;
use danog\MadelineProto\EventHandler\Message\Entities\MessageEntity;
use danog\MadelineProto\Lang;
use danog\MadelineProto\Logger;
use danog\MadelineProto\Magic;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\MTProto\MTProtoOutgoingMessage;
use danog\MadelineProto\SecurityException;
use danog\MadelineProto\Settings\TLSchema;
use danog\MadelineProto\TL\Types\Button;
use danog\MadelineProto\TL\Types\Bytes;
use danog\MadelineProto\Tools;

use const STR_PAD_LEFT;

/**
 * @psalm-import-type TBeforeMethodResponseDeserialization from TLCallback
 * @psalm-import-type TAfterMethodResponseDeserialization from TLCallback
 *
 * @psalm-import-type TBeforeConstructorSerialization from TLCallback
 * @psalm-import-type TBeforeConstructorDeserialization from TLCallback
 * @psalm-import-type TAfterConstructorDeserialization from TLCallback
 * @psalm-import-type TTypeMismatch from TLCallback
 *
 * TL serialization.
 *
 * @internal
 */
final class TL implements TLInterface
{
    /**
     * Highest available secret chat layer version.
     *
     */
    private int $secretLayer = -1;
    /**
     * Constructors.
     *
     */
    private TLConstructors $constructors;
    /**
     * Methods.
     *
     */
    private TLMethods $methods;
    /**
     * Descriptions.
     *
     */
    private array $tdDescriptions;

    /** @var array<string, list<TBeforeMethodResponseDeserialization>> */
    public array $beforeMethodResponseDeserialization;

    /** @var array<string, list<TAfterMethodResponseDeserialization>> */
    public array $afterMethodResponseDeserialization;

    /** @var array<string, TBeforeConstructorSerialization> */
    public array $beforeConstructorSerialization;
    /** @var array<string, list<TBeforeConstructorDeserialization>> */
    public array $beforeConstructorDeserialization;
    /** @var array<string, list<TAfterConstructorDeserialization>> */
    public array $afterConstructorDeserialization;

    /** @var array<string, TTypeMismatch> */
    public array $typeMismatch;

    /**
     * API instance.
     */
    private ?MTProto $API = null;
    /**
     * @psalm-pure
     */
    public function __sleep()
    {
        return [
            'secretLayer',
            'constructors',
            'methods',
            'tdDescriptions',
            'API',
        ];
    }
    /**
     * Constructor function.
     *
     * @param MTProto $API API instance
     *
     * @psalm-mutation-free
     */
    public function __construct(?MTProto $API = null)
    {
        if ($API) {
            $this->API = $API;
        }
    }
    /**
     * Get secret chat layer version.
     */
    #[\Override]
    public function getSecretLayer(): int
    {
        return $this->secretLayer;
    }
    /**
     * Get constructors.
     */
    #[\Override]
    public function getConstructors(): TLConstructors
    {
        return $this->constructors;
    }
    /**
     * Get methods.
     */
    #[\Override]
    public function getMethods(): TLMethods
    {
        return $this->methods;
    }
    /**
     * Get TL descriptions.
     */
    #[\Override]
    public function getDescriptions(): array
    {
        return $this->tdDescriptions;
    }
    /**
     * Get TL descriptions.
     */
    public function &getDescriptionsRef(): array
    {
        return $this->tdDescriptions;
    }
    /**
     * Initialize TL parser.
     *
     * @param TLSchema         $files   Scheme files
     * @param list<TLCallback> $objects TL Callback objects
     */
    public function init(TLSchema $files, array $objects = []): void
    {
        //$this->API?->logger?->logger('Loading TL schemes...', Logger::VERBOSE);
        $this->updateCallbacks($objects);
        $this->constructors = new TLConstructors();
        $this->methods = new TLMethods();
        $this->tdDescriptions = ['types' => [], 'constructors' => [], 'methods' => []];
        foreach (array_filter([
            'api' => $files->getAPISchema(),
            'mtproto' => $files->getMTProtoSchema(),
            'secret' => $files->getSecretSchema(),
            ...$files->getOther(),
        ]) as $scheme_type => $file) {
            //$this->API?->logger?->logger(sprintf(Lang::$current_lang['file_parsing'], basename($file)), Logger::VERBOSE);
            $filec = file_get_contents(Tools::absolute($file));
            $TL_dict = json_decode($filec, true);
            if ($TL_dict === null) {
                $TL_dict = $this->toJson($filec, $scheme_type);
            }
            foreach ($TL_dict['constructors'] as $key => $value) {
                $id = $TL_dict['constructors'][$key]['id'];
                if (!is_numeric($id)) {
                    throw new AssertionError("ID isn't numeric!");
                }
                $TL_dict['constructors'][$key]['id'] = Tools::packSignedInt((int) $id);
            }
            foreach ($TL_dict['methods'] as $key => $value) {
                $id = $TL_dict['methods'][$key]['id'];
                if (!is_numeric($id)) {
                    throw new AssertionError("ID isn't numeric!");
                }
                $TL_dict['methods'][$key]['id'] = Tools::packSignedInt((int) $id);
            }

            if (empty($TL_dict) || empty($TL_dict['constructors']) || !isset($TL_dict['methods'])) {
                throw new Exception(Lang::$current_lang['src_file_invalid'].$file);
            }
            //$this->API?->logger?->logger('Translating objects...', Logger::ULTRA_VERBOSE);
            foreach ($TL_dict['constructors'] as $elem) {
                if ($scheme_type === 'secret') {
                    $this->secretLayer = max($this->secretLayer, $elem['layer']);
                }
                $this->constructors->add($elem, $scheme_type);
            }
            //$this->API?->logger?->logger('Translating methods...', Logger::ULTRA_VERBOSE);
            foreach ($TL_dict['methods'] as $elem) {
                $this->methods->add($elem, $scheme_type);
                if ($scheme_type === 'secret') {
                    $this->secretLayer = max($this->secretLayer, $elem['layer']);
                }
            }
        }
        if (isset($files->getOther()['td'])) {
            foreach ($this->constructors->by_id as $id => $data) {
                $name = $data['predicate'];
                if ($this->constructors->findById($id) === false) {
                    unset($this->tdDescriptions['constructors'][$name]);
                } else {
                    if (!\count($this->tdDescriptions['constructors'][$name]['params'])) {
                        continue;
                    }
                    foreach ($this->tdDescriptions['constructors'][$name]['params'] as $k => $param) {
                        $this->tdDescriptions['constructors'][$name]['params'][$k] = str_replace('nullable', 'optional', $param);
                    }
                }
            }
            foreach ($this->methods->by_id as $id => $data) {
                $name = $data['method'];
                if ($this->methods->findById($id) === false) {
                    unset($this->tdDescriptions['methods'][$name]);
                } else {
                    foreach ($this->tdDescriptions['methods'][$name]['params'] as $k => $param) {
                        $this->tdDescriptions['constructors'][$name]['params'][$k] = str_replace('nullable', 'optional', $param);
                    }
                }
            }
        }
        $files->upgrade();
    }
    /**
     * @return array{
     *      constructors: list<array{id: numeric-string, predicate: string, params: list<array{name: string, type: string}>, type: string, layer?: int}>,
     *      methods: list<array{id: numeric-string, method: string, params: list<array{name: string, type: string}>, type: string, layer?: int}>
     * }
     *
     * @psalm-external-mutation-free
     */
    public function toJson(string $filec, ?string $scheme_type = null): array
    {
        $TL_dict = ['constructors' => [], 'methods' => []];
        $type = 'constructors';
        $layer = null;
        $tl_file = explode("\n", $filec);
        $key = 0;
        $e = null;
        $class = null;
        $dparams = [];
        $lineBuf = '';
        foreach ($tl_file as $line) {
            $line = rtrim($line);
            if (preg_match('|^//@|', $line)) {
                $list = explode(' @', str_replace('//', ' ', $line));
                foreach ($list as $elem) {
                    if ($elem === '') {
                        continue;
                    }
                    $elem = explode(' ', $elem, 2);
                    if ($elem[0] === 'class') {
                        $elem = explode(' ', $elem[1], 2);
                        $class = $elem[0];
                        continue;
                    }
                    if ($elem[0] === 'description') {
                        if (!\is_null($class)) {
                            $this->tdDescriptions['types'][$class] = $elem[1];
                            $class = null;
                        } else {
                            $e = $elem[1];
                        }
                        continue;
                    }
                    if ($elem[0] === 'param_description') {
                        $elem[0] = 'description';
                    }
                    $dparams[$elem[0]] = $elem[1];
                }
                continue;
            }
            $line = preg_replace(['|//.*|', '|^\\s+$|'], '', $line);
            if ($line === '') {
                continue;
            }
            if ($line === '---functions---') {
                $type = 'methods';
                continue;
            }
            if ($line === '---types---') {
                $type = 'constructors';
                continue;
            }
            if (preg_match('|^===(\\d*)===|', $line, $matches)) {
                $layer = (int) $matches[1];
                continue;
            }
            if (str_starts_with($line, 'vector#')) {
                $TL_dict[$type][]= [
                    "id" => "481674261",
                    "predicate" => "vector",
                    "params" => [],
                    "type" => "Vector t",
                ];
                continue;
            }
            if (str_contains($line, ' ?= ')) {
                continue;
            }
            $line = preg_replace(['/[(]([\\w\\.]+) ([\\w\\.]+)[)]/', '/\\s+/'], ['$1<$2>', ' '], $line);
            if (!str_contains($line, ';')) {
                $lineBuf .= $line;
                continue;
            } elseif ($lineBuf) {
                $lineBuf .= $line;
                $line = $lineBuf;
                $lineBuf = '';
            }
            $name = preg_replace(['/#.*/', '/\\s.*/'], '', $line);
            if (\in_array($name, ['bytes', 'int128', 'int256', 'int512', 'int', 'long', 'double', 'string', 'bytes', 'object', 'function'], true)) {
                continue;
            }
            if (\in_array($scheme_type, ['ton_api', 'lite_api'], true)) {
                $clean = preg_replace(['/;/', '/#[a-f0-9]+ /', '/ [a-zA-Z0-9_]+\\:flags\\.[0-9]+\\?true/', '/[<]/', '/[>]/', '/  /', '/^ /', '/ $/', '/{/', '/}/'], ['', ' ', '', ' ', ' ', ' ', '', '', '', ''], $line);
            } else {
                $clean = preg_replace(['/:bytes /', '/;/', '/#[a-f0-9]+ /', '/ [a-zA-Z0-9_]+\\:flags\\.[0-9]+\\?true/', '/[<]/', '/[>]/', '/  /', '/^ /', '/ $/', '/\\?bytes /', '/{/', '/}/'], [':string ', '', ' ', '', ' ', ' ', ' ', '', '', '?string ', '', ''], $line);
            }
            $id = hash('crc32b', $clean);
            if (preg_match('/^[^\\s]+#([a-f0-9]*)/i', $line, $matches)) {
                $nid = str_pad($matches[1], 8, '0', STR_PAD_LEFT);
                /*if ($id !== $nid) {
                    $this->API?->logger?->logger(\sprintf('CRC32 mismatch (%s, %s) for %s', $id, $nid, $line), Logger::ERROR);
                }*/
                $id = $nid;
            }
            if (!\is_null($e)) {
                $this->tdDescriptions[$type][$name] = ['description' => $e, 'params' => $dparams];
                $e = null;
                $dparams = [];
            }
            $key = \count($TL_dict[$type]);
            $TL_dict[$type][$key]['id'] = (string) Tools::unpackSignedInt(strrev(hex2bin($id)));
            $TL_dict[$type][$key][$type === 'constructors' ? 'predicate' : 'method'] = $name;
            $TL_dict[$type][$key]['params'] = [];
            $TL_dict[$type][$key]['type'] = preg_replace(['/.+\\s+=\\s+/', '/;/'], '', $line);
            if ($layer !== null) {
                $TL_dict[$type][$key]['layer'] = $layer;
            }
            if ($name !== 'vector' && $TL_dict[$type][$key]['type'] !== 'Vector t') {
                foreach (explode(' ', preg_replace(['/^[^\\s]+\\s/', '/=\\s[^\\s]+/', '/\\s$/'], '', $line)) as $param) {
                    if ($param === '') {
                        continue;
                    }
                    if ($param[0] === '{') {
                        continue;
                    }
                    if ($param === '#') {
                        continue;
                    }
                    $explode = explode(':', $param);
                    $TL_dict[$type][$key]['params'][] = ['name' => $explode[0], 'type' => $explode[1]];
                }
            }
        }

        return $TL_dict;
    }
    /**
     * Get TL namespaces.
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function getMethodNamespaces(): array
    {
        $res = [];
        foreach ($this->methods->method_namespace as $pair) {
            $a = key($pair);
            $res[$a] = $a;
        }
        return $res;
    }
    /**
     * Get namespaced methods (method => namespace).
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function getMethodsNamespaced(): array
    {
        return $this->methods->method_namespace;
    }
    /**
     * Update TL callbacks.
     *
     * @param list<TLCallback> $callbacks TL callbacks
     */
    public function updateCallbacks(array $callbacks): void
    {
        $this->beforeMethodResponseDeserialization = array_merge_recursive(...array_map(
            static fn (TLCallback $t) => $t->getMethodBeforeResponseDeserializationCallbacks(),
            $callbacks
        ));
        $this->afterMethodResponseDeserialization = array_merge_recursive(...array_map(
            static fn (TLCallback $t) => $t->getMethodAfterResponseDeserializationCallbacks(),
            $callbacks
        ));

        $this->beforeConstructorSerialization = array_merge(...array_map(
            static fn (TLCallback $t) => $t->getConstructorBeforeSerializationCallbacks(),
            $callbacks
        ));
        $this->beforeConstructorDeserialization = array_merge_recursive(...array_map(
            static fn (TLCallback $t) => $t->getConstructorBeforeDeserializationCallbacks(),
            $callbacks
        ));
        $this->afterConstructorDeserialization = array_merge_recursive(...array_map(
            static fn (TLCallback $t) => $t->getConstructorAfterDeserializationCallbacks(),
            $callbacks
        ));

        $this->typeMismatch = array_merge(...array_map(
            static fn (TLCallback $t) => $t->getTypeMismatchCallbacks(),
            $callbacks
        ));
    }
    /**
     * Deserialize bool.
     *
     * @param string $id Constructor ID
     *
     * @psalm-external-mutation-free
     */
    private function deserializeBool(string $id): bool
    {
        $tl_elem = $this->constructors->findById($id);
        if ($tl_elem === false) {
            throw new Exception(Lang::$current_lang['bool_error']);
        }
        return $tl_elem['predicate'] === 'boolTrue';
    }
    /**
     * Serialize TL object.
     *
     * @param array   $type   TL type definition
     * @param mixed   $object Object to serialize
     * @param string  $ctx    Context
     * @param integer $layer  Layer version
     */
    #[\Override]
    public function serializeObject(array $type, mixed $object, string|int $ctx, int $layer = -1)
    {
        switch ($type['type']) {
            case 'int':
                if (!is_numeric($object)) {
                    throw new Exception(Lang::$current_lang['not_numeric']);
                }
                return Tools::packSignedInt((int) $object);
            case '#':
                if (!\is_int($object)) {
                    throw new Exception(Lang::$current_lang['not_numeric']);
                }
                return Tools::packUnsignedInt($object);
            case 'strlong':
                return $object;
            case 'long':
                if (\is_object($object)) {
                    return str_pad(strrev($object->toBytes()), 8, \chr(0));
                }
                if (\is_string($object) && \strlen($object) === 8) {
                    return $object;
                }
                if (\is_string($object) && \strlen($object) === 9 && $object[0] === 'a') {
                    return substr($object, 1);
                }
                if (\is_array($object) && $type['name'] === 'hash') {
                    /** @psalm-suppress MixedArgumentTypeCoercion Typechecks are done inside */
                    return Tools::genVectorHash($object);
                }
                if (\is_array($object) && \count($object) === 2) {
                    return pack('l2', ...$object); // For bot API on 32bit
                }
                if (!is_numeric($object)) {
                    throw new Exception(Lang::$current_lang['not_numeric']);
                }
                return Tools::packSignedLong((int) $object);
            case 'int128':
                if (\strlen($object) !== 16) {
                    $object = base64_decode($object, true);
                    if (\strlen($object) !== 16) {
                        throw new Exception(Lang::$current_lang['long_not_16']);
                    }
                }
                return (string) $object;
            case 'int256':
                if (\strlen($object) !== 32) {
                    $object = base64_decode($object, true);
                    if (\strlen($object) !== 32) {
                        throw new Exception(Lang::$current_lang['long_not_32']);
                    }
                }
                return (string) $object;
            case 'int512':
                if (\strlen($object) !== 64) {
                    $object = base64_decode($object, true);
                    if (\strlen($object) !== 64) {
                        throw new Exception(Lang::$current_lang['long_not_64']);
                    }
                }
                return (string) $object;
            case 'double':
                return Tools::packDouble(\is_int($object) ? (float) $object : $object);
            case 'string':
                if ($object instanceof Bytes || \is_int($object) || \is_float($object)) {
                    $object = (string) $object;
                }
                if (!\is_string($object)) {
                    throw new Exception(Lang::$current_lang['string_required']);
                }
                $l = \strlen($object);
                $concat = '';
                if ($l <= 253) {
                    $concat .= \chr($l);
                    $concat .= $object;
                    $concat .= pack('@'.Tools::posmod(-$l - 1, 4));
                } else {
                    $concat .= \chr(254);
                    $concat .= substr(Tools::packSignedInt($l), 0, 3);
                    $concat .= $object;
                    $concat .= pack('@'.Tools::posmod(-$l, 4));
                }
                return $concat;
            case 'bytes':
                if (\is_array($object) && isset($object['_']) && $object['_'] === 'bytes') {
                    $object = base64_decode($object['bytes'], true);
                }
                if ($object instanceof Bytes || \is_int($object) || \is_float($object)) {
                    $object = (string) $object;
                }
                if (!\is_string($object)) {
                    throw new Exception(Lang::$current_lang['string_required']);
                }
                $l = \strlen($object);
                $concat = '';
                if ($l <= 253) {
                    $concat .= \chr($l);
                    $concat .= $object;
                    $concat .= pack('@'.Tools::posmod(-$l - 1, 4));
                } else {
                    $concat .= \chr(254);
                    $concat .= substr(Tools::packSignedInt($l), 0, 3);
                    $concat .= $object;
                    $concat .= pack('@'.Tools::posmod(-$l, 4));
                }
                return $concat;
            case 'waveform':
                if (\is_array($object) && isset($object['_']) && $object['_'] === 'bytes') {
                    $object = base64_decode($object['bytes'], true);
                }
                if (\is_array($object)) {
                    $object = self::compressWaveform($object);
                }
                if ($object instanceof Bytes) {
                    $object = (string) $object;
                }
                if (!\is_string($object)) {
                    throw new Exception(Lang::$current_lang['string_required']);
                }
                $l = \strlen($object);
                $concat = '';
                if ($l <= 253) {
                    $concat .= \chr($l);
                    $concat .= $object;
                    $concat .= pack('@'.Tools::posmod(-$l - 1, 4));
                } else {
                    $concat .= \chr(254);
                    $concat .= substr(Tools::packSignedInt($l), 0, 3);
                    $concat .= $object;
                    $concat .= pack('@'.Tools::posmod(-$l, 4));
                }
                return $concat;
            case 'Bool':
                return $this->constructors->findByPredicate((bool) $object ? 'boolTrue' : 'boolFalse')['id'];
            case 'true':
                return;
            case '!X':
                return $object;
            case 'Vector t':
                if (!\is_array($object)) {
                    throw new Exception(Lang::$current_lang['array_invalid']);
                }
                if (isset($object['_'])) {
                    throw new Exception(\sprintf(Lang::$current_lang['array_invalid'], $type['subtype']));
                }
                $concat = $this->constructors->findByPredicate('vector')['id'];
                $concat .= Tools::packUnsignedInt(\count($object));
                foreach ($object as $k => $current_object) {
                    $concat .= ($this->serializeObject(['type' => $type['subtype']], $current_object, $k, $layer));
                }
                return $concat;
            case 'vector':
                if (!\is_array($object)) {
                    throw new Exception(Lang::$current_lang['array_invalid']);
                }
                $concat = Tools::packUnsignedInt(\count($object));
                foreach ($object as $k => $current_object) {
                    $concat .= ($this->serializeObject(['type' => $type['subtype']], $current_object, $k, $layer));
                }
                return $concat;
            case 'Object':
                if (\is_string($object)) {
                    return $object;
                }
        }
        $object = $this->convertTypeMismatch($type['type'], $object);
        if (!isset($object['_'])) {
            $constructorData = $this->constructors->findByPredicate($type['type'], $layer);
            if ($constructorData === false) {
                throw new Exception(Lang::$current_lang['predicate_not_set']);
            }
            $object['_'] = $constructorData['predicate'];
        }
        if (isset($this->beforeConstructorSerialization[$object['_']])) {
            $object = $this->beforeConstructorSerialization[$object['_']]($object);
        }
        $predicate = $object['_'];
        $constructorData = $this->constructors->findByPredicate($predicate, $layer);
        if ($constructorData === false) {
            //$this->API->logger($object, Logger::FATAL_ERROR);
            throw new Exception(\sprintf(Lang::$current_lang['type_extract_error'], $predicate));
        }
        if ($bare = $type['type'] != '' && $type['type'][0] === '%') {
            $type['type'] = substr($type['type'], 1);
        }
        if ($predicate === $type['type']) {
            $bare = true;
        }
        if ($predicate === 'messageEntityMentionName') {
            $constructorData = $this->constructors->findByPredicate('inputMessageEntityMentionName');
        }
        $concat = $bare ? '' : $constructorData['id'];
        return $concat.($this->serializeParams($constructorData, $object, '', $layer));
    }
    /**
     * Convert a value passed for an object of a TL type to a constructor of that type, as done when serializing it
     * (a MessageMedia passed as an InputMedia, a peer ID passed as an InputPeer...).
     *
     * Returns other values unchanged.
     *
     * @internal
     */
    #[\Override]
    public function convertTypeMismatch(string $type, mixed $object): mixed
    {
        if ($object instanceof MessageEntity) {
            return $object->toMTProto();
        }
        if ($type === 'InputMessage' && !\is_array($object)) {
            return ['_' => 'inputMessageID', 'id' => $object];
        }
        if ($type === 'TextWithEntities' && !\is_array($object)) {
            return ['_' => 'textWithEntities', 'text' => $object];
        }
        if (isset($this->typeMismatch[$type]) && (!\is_array($object) || isset($object['_']) && $this->constructors->findByPredicate($object['_'])['type'] !== $type)) {
            $object = $this->typeMismatch[$type]($object);
            if (!isset($object['_'])) {
                if (!isset($object[$type])) {
                    throw new \danog\MadelineProto\Exception(\sprintf(Lang::$current_lang['could_not_convert_object'], $type));
                }
                $object = $object[$type];
            }
        }
        return $object;
    }
    /**
     * Serialize method.
     *
     * @param string $method    Method name
     * @param mixed  $arguments Arguments
     */
    #[\Override]
    public function serializeMethod(string $method, mixed $arguments)
    {
        $tl = $this->methods->findByMethod($method);
        if ($tl === false) {
            throw new Exception(Lang::$current_lang['method_not_found'].$method);
        }
        return $tl['id'].$this->serializeParams($tl, $arguments, $method, -1);
    }
    /**
     * Whether a `DataJSON` argument was already passed as a dataJSON constructor, instead of as the
     * decoded value we would have to encode ourselves.
     *
     * @psalm-pure
     */
    private static function isDataJSON(mixed $value): bool
    {
        return \is_array($value) && ($value['_'] ?? null) === 'dataJSON';
    }
    /**
     * Serialize parameters.
     *
     * @param array   $tl    TL object definition
     * @param string  $ctx   Context
     * @param integer $layer Layer
     */
    private function serializeParams(array $tl, array|Button $arguments, string|int $ctx, int $layer)
    {
        $serialized = '';
        $arguments = $arguments instanceof Button ? $arguments->jsonSerialize() : $arguments;
        // The bot-API argument conversion is only relevant to the full API; a schema-only TL instance
        // (e.g. the E2E conference block codec) has no API and serializes already-MTProto arguments.
        $arguments = $this->API?->botAPIToMTProto($arguments) ?? $arguments;
        foreach ($tl['flags'] as [
            'flag' => $flag,
            'name' => $name,
            'type' => $type,
            'pow' => $pow
        ]) {
            $arguments[$flag] ??= 0;
            if ($type === 'true') {
                if (isset($arguments[$name]) && $arguments[$name]) {
                    $arguments[$flag] |= $pow;
                } elseif ($arguments[$flag] & $pow) {
                    throw new Exception(Lang::$current_lang['params_missing'].' '.$name);
                } else {
                    $arguments[$flag] &= ~$pow;
                }
            } else {
                if (isset($arguments[$name])) {
                    $arguments[$flag] |= $pow;
                } elseif ($arguments[$flag] & $pow) {
                    throw new Exception(Lang::$current_lang['params_missing'].' '.$name);
                } else {
                    $arguments[$flag] &= ~$pow;
                }
            }
        }
        foreach ($tl['params'] as $current_argument) {
            $name = $current_argument['name'];
            $type = $current_argument['type'];
            if (!isset($arguments[$name])) {
                if (isset($current_argument['pow']) && ($type === 'true' || ($arguments[$current_argument['flag']] & $current_argument['pow']) === 0)) {
                    //$this->API->logger('Skipping '.$name.' of type '.$current_argument['type');
                    continue;
                }
                if ($name === 'random_bytes') {
                    $serialized .= $this->serializeObject(['type' => 'bytes'], Tools::random(15 + 4 * Tools::randomInt(modulus: 3)), 'random_bytes');
                    continue;
                }
                if ($name === 'random_id') {
                    switch ($type) {
                        case 'long':
                            $serialized .= Tools::random(8);
                            continue 2;
                        case 'int':
                            $serialized .= Tools::random(4);
                            continue 2;
                        case 'Vector t':
                            if (isset($arguments['id'])) {
                                $serialized .= $this->constructors->findByPredicate('vector')['id'];
                                $serialized .= Tools::packUnsignedInt(\count($arguments['id']));
                                $serialized .= Tools::random(8 * \count($arguments['id']));
                                continue 2;
                            }
                    }
                }
                if ($type === 'long') {
                    $serialized .= "\0\0\0\0\0\0\0\0";
                    continue;
                }
                if ($type === 'double') {
                    $serialized .= "\0\0\0\0\0\0\0\0";
                    continue;
                }
                if ($tl['type'] === 'InputMedia' && $name === 'mime_type') {
                    $serialized .= ($this->serializeObject($current_argument, $arguments['file']['mime_type'], $name, $layer));
                    continue;
                }
                if (\in_array($type, ['bytes', 'string', 'int'], true)) {
                    $serialized .= "\0\0\0\0";
                    continue;
                }
                if (($id = $this->constructors->findByPredicate(lcfirst($type).'Empty', $tl['layer'] ?? -1)) && $id['type'] === $type) {
                    $serialized .= $id['id'];
                    continue;
                }
                if (($id = $this->constructors->findByPredicate('input'.$type.'Empty', $tl['layer'] ?? -1)) && $id['type'] === $type) {
                    $serialized .= $id['id'];
                    continue;
                }
                switch ($type) {
                    case 'Vector t':
                    case 'vector':
                        $value = [];
                        break;
                    case 'Bool':
                        $value = false;
                        break;
                    case 'DataJSON':
                    case '%DataJSON':
                        $value = null;
                        break;
                    case '#':
                        $value = 0;
                        break;
                    case 'strlong':
                        $value = str_repeat("\0", 8);
                        break;
                    default:
                        if ($this->API->getSettings()->getSchema()->getFuzzMode()) {
                            $value = $this->constructors->findByType($type);
                            if ($value === false) {
                                throw new Exception(\sprintf(Lang::$current_lang['type_extract_error'], $type));
                            }
                            $value = ['_' => $value['predicate']];
                        } else {
                            throw new Exception(Lang::$current_lang['params_missing'].' '.$name);
                        }
                }
            } else {
                $value = $arguments[$name];
            }
            if (\in_array($type, ['DataJSON', '%DataJSON'], true) && !self::isDataJSON($value)) {
                $value = ['_' => 'dataJSON', 'data' => json_encode($value)];
            }
            if (isset($current_argument['subtype']) && \in_array($current_argument['subtype'], ['DataJSON', '%DataJSON'], true)) {
                array_walk($value, static function (&$arg): void {
                    if (!self::isDataJSON($arg)) {
                        $arg = ['_' => 'dataJSON', 'data' => json_encode($arg)];
                    }
                });
            }
            if ($type === 'InputFile' && (!\is_array($value) || !(isset($value['_']) && $this->constructors->findByPredicate($value['_'])['type'] === 'InputFile'))) {
                $value = $this->API->upload($value, cancellation: $arguments['cancellation'] ?? null);
                $arguments[$name] = $value;
            }
            if ($type === 'InputEncryptedFile' && (!\is_array($value) || !(isset($value['_']) && $this->constructors->findByPredicate($value['_'])['type'] === 'InputEncryptedFile'))) {
                $value = $this->API->uploadEncrypted($value, cancellation: $arguments['cancellation'] ?? null);
                $arguments[$name] = $value;
            }
            if ($type === 'InputEncryptedChat' && (!\is_array($value) || isset($value['_']) && $this->constructors->findByPredicate($value['_'])['type'] !== $type)) {
                $value = $this->API->getSecretChatController($value)->inputChat;
                $arguments[$name] = $value;
            }
            $serialized .= ($this->serializeObject($current_argument, $value, $name, $layer));
        }
        return $serialized;
    }
    /**
     * Get length of TL payload.
     *
     * @param resource|string $stream Stream
     * @param array           $type   Type identifier
     */
    #[\Override]
    public function getLength($stream, array $type = ['type' => '', 'connection' => null, 'encrypted' => false]): int
    {
        if (\is_string($stream)) {
            $res = fopen('php://memory', 'rw+b');
            fwrite($res, $stream);
            fseek($res, 0);
            $stream = $res;
        } elseif (!\is_resource($stream)) {
            throw new Exception(Lang::$current_lang['stream_handle_invalid']);
        }
        $this->deserialize($stream, $type);
        return ftell($stream);
    }

    /**
     * Extracts a waveform.
     *
     * @internal Don't use this manually.
     */
    public static function extractWaveform(string $x): array
    {
        $values = array_pad(array_values(unpack('C*', $x)), 63, 0);

        $result = array_fill(0, 100, 0);
        $bitPos = 0;
        foreach ($result as &$value) {
            $start = $bitPos & 7;
            $bytePos = $bitPos >> 3;
            $value = $values[$bytePos] >> $start;
            if ($start > 3) {
                $value |= $values[$bytePos+1] << (8 - $start);
            }
            $value &= 31;

            $bitPos += 5;
        }
        return $result;
    }
    /**
     * Compresses a waveform.
     *
     * @internal Don't use this manually, just pass an array of integers to $attribute['waveform'].
     *
     * @psalm-external-mutation-free
     */
    public static function compressWaveform(array $x): string
    {
        if (\count($x) !== 100) {
            throw new Exception(Lang::$current_lang['waveform_must_have_100_values']);
        }
        $values = array_fill(0, 63, 0);
        $bitPos = 0;
        foreach ($x as $value) {
            if (!\is_int($value) || $value < 0 || $value > 31) {
                throw new Exception(Lang::$current_lang['waveform_value']);
            }
            $start = $bitPos & 7;
            $bytePos = $bitPos >> 3;
            $values[$bytePos] |= ($value << $start) & 0xFF;
            if ($start > 3) {
                $values[$bytePos+1] |= $value >> (8 - $start);
            }
            $bitPos += 5;
        }
        return pack('C63', ...$values);
    }
    /**
     * Deserialize TL object.
     *
     * @param string|resource $stream Stream
     * @param array           $type   Type identifier
     */
    #[\Override]
    public function deserialize($stream, array $type)
    {
        if (\is_string($stream)) {
            $res = fopen('php://memory', 'rw+b');
            fwrite($res, $stream);
            fseek($res, 0);
            $stream = $res;
        } elseif (!\is_resource($stream)) {
            throw new Exception(Lang::$current_lang['stream_handle_invalid']);
        }
        switch ($type['type']) {
            case 'Bool':
                return $this->deserializeBool(stream_get_contents($stream, 4));
            case 'int':
                return Tools::unpackSignedInt(stream_get_contents($stream, 4));
            case '#':
                return unpack('V', stream_get_contents($stream, 4))[1];
            case 'strlong':
                return stream_get_contents($stream, 8);
            case 'long':
                return Tools::unpackSignedLong(stream_get_contents($stream, 8));
            case 'double':
                return Tools::unpackDouble(stream_get_contents($stream, 8));
            case 'int128':
                return stream_get_contents($stream, 16);
            case 'int256':
                return stream_get_contents($stream, 32);
            case 'int512':
                return stream_get_contents($stream, 64);
            case 'waveform':
            case 'string':
            case 'bytes':
                $l = \ord(stream_get_contents($stream, 1));
                if ($l > 254) {
                    throw new Exception(Lang::$current_lang['length_too_big']);
                }
                if ($l === 254) {
                    $long_len = unpack('V', stream_get_contents($stream, 3).\chr(0))[1];
                    $x = stream_get_contents($stream, $long_len);
                    $resto = Tools::posmod(-$long_len, 4);
                    if ($resto > 0) {
                        stream_get_contents($stream, $resto);
                    }
                } else {
                    $x = $l ? stream_get_contents($stream, $l) : '';
                    $resto = Tools::posmod(-($l + 1), 4);
                    if ($resto > 0) {
                        stream_get_contents($stream, $resto);
                    }
                }
                if (!\is_string($x)) {
                    throw new Exception("Generated value isn't a string");
                }
                if ($type['type'] === 'bytes') {
                    return new Types\Bytes($x);
                }
                if ($type['type'] === 'waveform') {
                    return self::extractWaveform($x);
                }
                return $x;
            case 'Vector t':
                $id = stream_get_contents($stream, 4);
                $constructorData = $this->constructors->findById($id);
                if ($constructorData === false) {
                    $constructorData = $this->methods->findById($id);
                    $constructorData['predicate'] = 'method_'.$constructorData['method'];
                }
                if ($constructorData === false) {
                    throw new Exception(\sprintf(Lang::$current_lang['type_extract_error_id'], $type['type'], bin2hex(strrev($id))));
                }
                switch ($constructorData['predicate']) {
                    case 'gzip_packed':
                        return $this->deserialize(
                            gzdecode(
                                (string) $this->deserialize(
                                    $stream,
                                    ['type' => 'bytes', 'connection' => $type['connection'], 'encrypted' => $type['encrypted']],
                                ),
                            ),
                            ['type' => '', 'connection' => $type['connection'], 'encrypted' => $type['encrypted']],
                        );
                    case 'Vector t':
                    case 'vector':
                        break;
                    default:
                        throw new Exception('Invalid vector constructor: '.$constructorData['predicate']);
                }
                // no break
            case 'vector':
                $count = unpack('V', stream_get_contents($stream, 4))[1];
                $result = [];
                $type['type'] = $type['subtype'];
                for ($i = 0; $i < $count; $i++) {
                    $result []= $this->deserialize($stream, $type);
                }
                return $result;
        }
        if ($type['type'] != '' && $type['type'][0] === '%') {
            $checkType = substr($type['type'], 1);
            $constructorData = $this->constructors->findByType($checkType);
            if ($constructorData === false) {
                throw new Exception(Lang::$current_lang['constructor_not_found'].$checkType);
            }
        } else {
            $constructorData = $this->constructors->findByPredicate($type['type']);
            if ($constructorData === false) {
                $id = stream_get_contents($stream, 4);
                $constructorData = $this->constructors->findById($id);
                if ($constructorData === false) {
                    $constructorData = $this->methods->findById($id);
                    if ($constructorData === false) {
                        throw new Exception(\sprintf(Lang::$current_lang['type_extract_error_id'], $type['type'], bin2hex(strrev($id))));
                    }
                    $constructorData['predicate'] = 'method_'.$constructorData['method'];
                }
            }
        }
        if ($constructorData['predicate'] === 'gzip_packed') {
            if (!isset($type['subtype'])) {
                $type['subtype'] = '';
            }
            $x = gzdecode(
                (string) $this->deserialize(
                    $stream,
                    ['type' => 'bytes'],
                ),
            );
            return $this->deserialize(
                $x,
                ['type' => '', 'connection' => $type['connection'], 'subtype' => $type['subtype'], 'encrypted' => $type['encrypted']],
            );
        }
        if ($constructorData['type'] === 'Vector t') {
            $constructorData['connection'] = $type['connection'];
            $constructorData['encrypted'] = $type['encrypted'];
            $constructorData['subtype'] = $type['subtype'] ?? '';
            $constructorData['type'] = 'vector';
            return $this->deserialize($stream, $constructorData);
        }
        if ($constructorData['predicate'] === 'boolTrue') {
            return true;
        }
        if ($constructorData['predicate'] === 'boolFalse') {
            return false;
        }
        $x = ['_' => $constructorData['predicate']];
        if ($type['encrypted'] && isset($this->beforeConstructorDeserialization[$x['_']])) {
            foreach ($this->beforeConstructorDeserialization[$x['_']] as $callback) {
                $callback($x['_']);
            }
        }
        foreach ($constructorData['params'] as $arg) {
            if (isset($arg['pow'])) {
                switch ($arg['type']) {
                    case 'true':
                        $x[$arg['name']] = ($x[$arg['flag']] & $arg['pow']) !== 0;
                        continue 2;
                    default:
                        if (($x[$arg['flag']] & $arg['pow']) === 0) {
                            continue 2;
                        }
                }
            }
            if ($x['_'] === 'rpc_result' && $arg['name'] === 'result' && $type['encrypted'] && isset($type['connection']->new_outgoing[$x['req_msg_id']])) {
                /** @var MTProtoOutgoingMessage */
                $message = $type['connection']->new_outgoing[$x['req_msg_id']];
                foreach ($this->beforeMethodResponseDeserialization[$message->constructor] ?? [] as $callback) {
                    $callback($message->constructor);
                }
                if ($message->subtype) {
                    $arg['subtype'] = $message->subtype;
                }
            }
            $arg['connection'] = $type['connection'];
            $arg['encrypted'] = $type['encrypted'];
            $x[$arg['name']] = $this->deserialize($stream, $arg);
            if ($arg['name'] === 'chat_id' && $arg['type'] === 'long') {
                $x['chat_id'] = -$x['chat_id'];
            }
        }
        if (isset($x['migrated_from_chat_id'])) {
            $x['migrated_from_chat_id'] = -$x['migrated_from_chat_id'];
        }

        if (isset($x['channel_id'])) {
            $x['channel_id'] = $x['channel_id'] > Magic::MAX_CHANNEL_ID
                ? DialogId::fromMonoforumId($x['channel_id'])
                : DialogId::fromSupergroupOrChannelId($x['channel_id']);
        } elseif (isset($x['community_id'])) {
            $x['community_id'] = $x['community_id'] > Magic::MAX_CHANNEL_ID
                ? DialogId::fromMonoforumId($x['community_id'])
                : DialogId::fromSupergroupOrChannelId($x['community_id']);
        } elseif (isset($x['random_bytes'])) {
            if (\strlen((string) $x['random_bytes']) < 15) {
                throw new SecurityException('Random_bytes is too small!');
            }
            unset($x['random_bytes']);
        } elseif ($x['_'] === 'channel'
            || $x['_'] === 'channelForbidden'
            || $x['_'] === 'channelFull'
            || $x['_'] === 'communityFull'
            || $x['_'] === 'community'
            || $x['_'] === 'communityForbidden'
        ) {
            $x['id'] = $x['id'] > Magic::MAX_CHANNEL_ID
                ? DialogId::fromMonoforumId($x['id'])
                : DialogId::fromSupergroupOrChannelId($x['id']);
            if (isset($x['linked_monoforum_id'])) {
                $x['linked_monoforum_id'] = $x['linked_monoforum_id'] > Magic::MAX_CHANNEL_ID
                    ? DialogId::fromMonoforumId($x['linked_monoforum_id'])
                    : DialogId::fromSupergroupOrChannelId($x['linked_monoforum_id']);
            }
            if (isset($x['linked_community_id'])) {
                $x['linked_community_id'] = $x['linked_community_id'] > Magic::MAX_CHANNEL_ID
                    ? DialogId::fromMonoforumId($x['linked_community_id'])
                    : DialogId::fromSupergroupOrChannelId($x['linked_community_id']);
            }
        } elseif ($x['_'] === 'chat'
            || $x['_'] === 'chatForbidden'
            || $x['_'] === 'chatFull'
        ) {
            $x['id'] = -$x['id'];
        }

        if ($x['_'] === 'dataJSON') {
            return json_decode($x['data'], true);
        } elseif ($constructorData['type'] === 'JSONValue') {
            switch ($x['_']) {
                case 'jsonNull':
                    return;
                case 'jsonObject':
                    $res = [];
                    foreach ($x['value'] as $pair) {
                        $res[$pair['key']] = $pair['value'];
                    }
                    return $res;
                default:
                    return $x['value'];
            }
        } elseif ($x['_'] === 'photoStrippedSize') {
            $x['inflated'] = new Types\Bytes(Tools::inflateStripped((string) $x['bytes']));
        }
        if ($type['encrypted']) {
            if (isset($this->afterConstructorDeserialization[$x['_']])) {
                foreach ($this->afterConstructorDeserialization[$x['_']] as $callback) {
                    $callback($x, $type['connection']);
                }
            } elseif ($x['_'] === 'rpc_result'
                && isset($type['connection']->new_outgoing[$x['req_msg_id']])
                && isset($this->afterMethodResponseDeserialization[$type['connection']->new_outgoing[$x['req_msg_id']]->constructor])) {
                $msg = $type['connection']->new_outgoing[$x['req_msg_id']];
                foreach ($this->afterMethodResponseDeserialization[$msg->constructor] as $callback) {
                    $callback($msg, $x['result']);
                }
            }
        }
        /** @psalm-suppress InvalidArgument */
        if ($x['_'] === 'peerUser') {
            $x = $x['user_id'];
        } elseif ($x['_'] === 'peerChat') {
            $x = $x['chat_id'];
        } elseif ($x['_'] === 'peerChannel') {
            $x = $x['channel_id'];
        } elseif ($x['_'] === 'user') {
            unset($x['flags'], $x['flags2'], $x['access_hash']);
        } elseif ($x['_'] === 'channel'
            || $x['_'] === 'channelForbidden'
            || $x['_'] === 'channelFull'
            || $x['_'] === 'communityFull'
            || $x['_'] === 'community'
            || $x['_'] === 'communityForbidden'
        ) {
            unset($x['flags'], $x['flags2'], $x['access_hash']);
        } elseif ($x['_'] === 'chat'
            || $x['_'] === 'chatForbidden'
            || $x['_'] === 'chatFull'
        ) {
            unset($x['flags']);
        } else {
            unset($x['flags'], $x['flags2']);
        }
        return $x;
    }
}
<?php

declare(strict_types=1);

/**
 * TL module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\TL;

/** @psalm-mutable */
interface TLInterface
{
    /**
     * Get constructors.
     * @psalm-impure
     */
    public function getConstructors(): TLConstructors;
    /**
     * Get methods.
     * @psalm-impure
     */
    public function getMethods(): TLMethods;
    /**
     * Get descriptions.
     *
     * @psalm-impure
     */
    public function getDescriptions(): array;
    /**
     * Get TL namespaces.
     * @psalm-impure
     */
    public function getMethodNamespaces(): array;
    /**
     * Get namespaced methods (method => namespace).
     * @psalm-impure
     */
    public function getMethodsNamespaced(): array;
    /**
     * Serialize TL object.
     *
     * @param array   $type   TL type definition
     * @param mixed   $object Object to serialize
     * @param string  $ctx    Context
     * @param integer $layer  Layer version
     * @psalm-impure
     */
    public function serializeObject(array $type, mixed $object, string|int $ctx, int $layer = -1);
    /**
     * Serialize method.
     *
     * @param string $method    Method name
     * @param mixed  $arguments Arguments
     * @psalm-impure
     */
    public function serializeMethod(string $method, mixed $arguments);
    /**
     * Convert a value passed for an object of a TL type to a constructor of that type, as done when serializing it.
     *
     * @psalm-impure
     */
    public function convertTypeMismatch(string $type, mixed $object): mixed;
    /**
     * Get length of TL payload.
     *
     * @param resource|string $stream Stream
     * @param array           $type   Type identifier
     * @psalm-impure
     */
    public function getLength($stream, array $type = ['type' => '', 'connection' => null, 'encrypted' => false]): int;
    /**
     * Deserialize TL object.
     *
     * @param string|resource $stream Stream
     * @param array           $type   Type identifier
     * @psalm-impure
     */
    public function deserialize($stream, array $type);

    /**
     * Get secret chat layer version.
     * @psalm-impure
     */
    public function getSecretLayer(): int;
}
<?php namespace danog\MadelineProto\TL;
/** @internal @generated Autogenerated using tools/TL/Builder.php â€” do not edit. */
use danog\MadelineProto\MTProto;
use danog\MadelineProto\Connection;
use danog\MadelineProto\MTProtoTools\PeerDatabase;
use danog\MadelineProto\MTProtoTools\ReferenceDatabase;
use danog\MadelineProto\MTProtoTools\MinDatabase;
use danog\MadelineProto\SecurityException;
use AssertionError;
use danog\MadelineProto\Lang;
final class TLParser {
public function __construct(
            private readonly MTProto $API,
            private readonly Connection $connection,
            private readonly PeerDatabase $peerDatabase,
            private readonly ?ReferenceDatabase $referenceDatabase,
            private readonly ?MinDatabase $minDatabase,
        ) {}
            private  function err(mixed $stream): never {

            fseek($stream, -4, SEEK_CUR);
            throw new AssertionError("Unexpected ID ".bin2hex(fread($stream, 4)));
        
    }
    private  function gzdecode(mixed $stream): mixed {

            $res = fopen('php://memory', 'rw+b');
            fwrite($res, gzdecode(self::deserialize_string($stream)));
            rewind($res);
            return $res;
        
    }
    private  function gzdecode_vector(mixed $stream): mixed {

            $res = fopen('php://memory', 'rw+b');
            fwrite($res, gzdecode(self::deserialize_string($stream)));
            rewind($res);
            return match (stream_get_contents($stream, 4)) {
                'Äµ' => $stream,
                default => self::err($stream)
            };
        
    }
    private  function deserialize_bytes(mixed $stream): mixed {

            
            $l = \ord(stream_get_contents($stream, 1));
            if ($l > 254) {
                throw new Exception(Lang::$current_lang["length_too_big"]);
            }
            if ($l === 254) {
                $l = unpack("V", stream_get_contents($stream, 3).\chr(0))[1];
                $x = stream_get_contents($stream, $l);
                $resto = (-$l) % 4;
                $resto = $resto < 0 ? $resto + 4 : $resto;
                if ($resto > 0) {
                    stream_get_contents($stream, $resto);
                }
            } else {
                $x = $l ? stream_get_contents($stream, $l) : "";
                $resto = (-$l+1) % 4;
                $resto = $resto < 0 ? $resto + 4 : $resto;
                if ($resto > 0) {
                    stream_get_contents($stream, $resto);
                }
            }

            return new Types\Bytes($x);
        
    }
    private  function deserialize_string(mixed $stream): mixed {

            
            $l = \ord(stream_get_contents($stream, 1));
            if ($l > 254) {
                throw new Exception(Lang::$current_lang["length_too_big"]);
            }
            if ($l === 254) {
                $l = unpack("V", stream_get_contents($stream, 3).\chr(0))[1];
                $x = stream_get_contents($stream, $l);
                $resto = (-$l) % 4;
                $resto = $resto < 0 ? $resto + 4 : $resto;
                if ($resto > 0) {
                    stream_get_contents($stream, $resto);
                }
            } else {
                $x = $l ? stream_get_contents($stream, $l) : "";
                $resto = (-$l+1) % 4;
                $resto = $resto < 0 ? $resto + 4 : $resto;
                if ($resto > 0) {
                    stream_get_contents($stream, $resto);
                }
            }

            return $x;
        
    }
    private  function deserialize_waveform(mixed $stream): mixed {

            
            $l = \ord(stream_get_contents($stream, 1));
            if ($l > 254) {
                throw new Exception(Lang::$current_lang["length_too_big"]);
            }
            if ($l === 254) {
                $l = unpack("V", stream_get_contents($stream, 3).\chr(0))[1];
                $x = stream_get_contents($stream, $l);
                $resto = (-$l) % 4;
                $resto = $resto < 0 ? $resto + 4 : $resto;
                if ($resto > 0) {
                    stream_get_contents($stream, $resto);
                }
            } else {
                $x = $l ? stream_get_contents($stream, $l) : "";
                $resto = (-$l+1) % 4;
                $resto = $resto < 0 ? $resto + 4 : $resto;
                if ($resto > 0) {
                    stream_get_contents($stream, $resto);
                }
            }

            return TL::extractWaveform($x);
        
    }
    private  function deserialize_random_bytes(mixed $stream): void {

            $l = \ord(stream_get_contents($stream, 1));
            if ($l > 254) {
                throw new Exception(Lang::$current_lang["length_too_big"]);
            }
            if ($l === 254) {
                $l = unpack("V", stream_get_contents($stream, 3).\chr(0))[1];
                if ($l < 15) {
                    throw new SecurityException("Random_bytes is too small!");
                }
            } else {
                if ($l < 15) {
                    throw new SecurityException("Random_bytes is too small!");
                }
                $l += 1;
            }
            $resto = (-$l) % 4;
            $resto = $resto < 0 ? $resto + 4 : $resto;
            if ($resto > 0) {
                $l += $resto;
            }
            stream_get_contents($stream, $l);
        
    }
    private  function deserialize_type_array_of_int(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= unpack('l', stream_get_contents($stream, 4))[1];
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_long(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= unpack('q', stream_get_contents($stream, 8))[1];
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_double(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= unpack('d', stream_get_contents($stream, 8))[1];
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_strlong(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= stream_get_contents($stream, 8);
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_string(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= self::deserialize_string($stream);
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_bytes(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= self::deserialize_bytes($stream);
                }
                return $result;    
            
    }
    private  function deserialize_peerUser(mixed $stream): mixed {
$tmp = unpack('q', stream_get_contents($stream, 8))[1];
$this->minDatabase?->addPeer($tmp);
return $tmp;

    }
    private  function deserialize_peerChannel(mixed $stream): mixed {
$tmp = -1000000000000 - unpack('q', stream_get_contents($stream, 8))[1];
$this->minDatabase?->addPeer($tmp);
return $tmp;

    }
    private  function deserialize_type_Peer(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => $this->deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_messageEmpty(mixed $stream): mixed {
$tmp = ['_' => 'messageEmpty'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['id'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['peer_id'] = match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_messageFwdHeader(mixed $stream): mixed {
$tmp = ['_' => 'messageFwdHeader'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['imported'] = ($flags & 128) !== 0;
$tmp['saved_out'] = ($flags & 2048) !== 0;
if (($flags & 1) !== 0) $tmp['from_id'] = match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 32) !== 0) $tmp['from_name'] = self::deserialize_string($stream);
$tmp['date'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 4) !== 0) $tmp['channel_post'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 8) !== 0) $tmp['post_author'] = self::deserialize_string($stream);
if (($flags & 16) !== 0) $tmp['saved_from_peer'] = match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 16) !== 0) $tmp['saved_from_msg_id'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 256) !== 0) $tmp['saved_from_id'] = match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 512) !== 0) $tmp['saved_from_name'] = self::deserialize_string($stream);
if (($flags & 1024) !== 0) $tmp['saved_date'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 64) !== 0) $tmp['psa_type'] = self::deserialize_string($stream);
$this->minDatabase?->addPeer($tmp);
return $tmp;

    }
    private  function deserialize_type_MessageFwdHeader(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'»ôMN' => self::deserialize_messageFwdHeader($stream),
'¡Ïr0' => $this->deserialize_type_MessageFwdHeader(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_array_of_PhotoSize(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'<â' => [
'_' => 'photoSizeEmpty',
'type' => self::deserialize_string($stream),
],
'`ŽÇu' => [
'_' => 'photoSize',
'type' => self::deserialize_string($stream),
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Ö' => [
'_' => 'photoCachedSize',
'type' => self::deserialize_string($stream),
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'bytes' => self::deserialize_bytes($stream),
],
'.¼°à' => [
'_' => 'photoStrippedSize',
'type' => self::deserialize_string($stream),
'inflated' => new Types\Bytes(Tools::inflateStripped(self::deserialize_string($stream))),
],
'•û>ú' => [
'_' => 'photoSizeProgressive',
'type' => self::deserialize_string($stream),
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'sizes' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'AM!Ø' => [
'_' => 'photoPathSize',
'type' => self::deserialize_string($stream),
'bytes' => self::deserialize_bytes($stream),
],
'¡Ïr0' => $this->deserialize_type_PhotoSize(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_videoSize(mixed $stream): mixed {
$tmp = ['_' => 'videoSize'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['type'] = self::deserialize_string($stream);
$tmp['w'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['h'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['size'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['video_start_ts'] = unpack('d', stream_get_contents($stream, 8))[1];
return $tmp;

    }
    private  function deserialize_type_InputStickerSet(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'•+¶ÿ' => [
'_' => 'inputStickerSetEmpty',
],
'i¢ç�' => [
'_' => 'inputStickerSetID',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
' È†' => [
'_' => 'inputStickerSetShortName',
'short_name' => self::deserialize_string($stream),
],
'È‡' => [
'_' => 'inputStickerSetAnimatedEmoji',
],
'Ræ' => [
'_' => 'inputStickerSetDice',
'emoticon' => self::deserialize_string($stream),
],
'97Þ' => [
'_' => 'inputStickerSetAnimatedEmojiAnimations',
],
';‹È' => [
'_' => 'inputStickerSetPremiumGifts',
],
'ÎÔÄ' => [
'_' => 'inputStickerSetEmojiGenericAnimations',
],
'îõÐ)' => [
'_' => 'inputStickerSetEmojiDefaultStatuses',
],
'éøÁD' => [
'_' => 'inputStickerSetEmojiDefaultTopicIcons',
],
'S…tI' => [
'_' => 'inputStickerSetEmojiChannelDefaultStatuses',
],
'¡Ïr0' => $this->deserialize_type_InputStickerSet(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_array_of_VideoSize(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'”°3Þ' => self::deserialize_videoSize($stream),
'<A\\ø' => [
'_' => 'videoSizeEmojiMarkup',
'emoji_id' => unpack('q', stream_get_contents($stream, 8))[1],
'background_colors' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'þ‚ ' => [
'_' => 'videoSizeStickerMarkup',
'stickerset' => match (stream_get_contents($stream, 4)) {
'•+¶ÿ' => [
'_' => 'inputStickerSetEmpty',
],
'i¢ç�' => [
'_' => 'inputStickerSetID',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
' È†' => [
'_' => 'inputStickerSetShortName',
'short_name' => self::deserialize_string($stream),
],
'È‡' => [
'_' => 'inputStickerSetAnimatedEmoji',
],
'Ræ' => [
'_' => 'inputStickerSetDice',
'emoticon' => self::deserialize_string($stream),
],
'97Þ' => [
'_' => 'inputStickerSetAnimatedEmojiAnimations',
],
';‹È' => [
'_' => 'inputStickerSetPremiumGifts',
],
'ÎÔÄ' => [
'_' => 'inputStickerSetEmojiGenericAnimations',
],
'îõÐ)' => [
'_' => 'inputStickerSetEmojiDefaultStatuses',
],
'éøÁD' => [
'_' => 'inputStickerSetEmojiDefaultTopicIcons',
],
'S…tI' => [
'_' => 'inputStickerSetEmojiChannelDefaultStatuses',
],
'¡Ïr0' => self::deserialize_type_InputStickerSet(self::gzdecode($stream)),
default => self::err($stream)
}
,
'sticker_id' => unpack('q', stream_get_contents($stream, 8))[1],
'background_colors' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => $this->deserialize_type_VideoSize(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_photo(mixed $stream): mixed {
$tmp = ['_' => 'photo'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['has_stickers'] = ($flags & 1) !== 0;
$tmp['id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['access_hash'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['file_reference'] = self::deserialize_bytes($stream);
$tmp['date'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['sizes'] = self::deserialize_type_array_of_PhotoSize(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 2) !== 0) $tmp['video_sizes'] = self::deserialize_type_array_of_VideoSize(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['dc_id'] = unpack('l', stream_get_contents($stream, 4))[1];
$this->referenceDatabase?->addReference($tmp);
return $tmp;

    }
    private  function deserialize_type_Photo(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'-²1#' => [
'_' => 'photoEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ezû' => self::deserialize_photo($stream),
'¡Ïr0' => $this->deserialize_type_Photo(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_messageMediaPhoto(mixed $stream): mixed {
$tmp = ['_' => 'messageMediaPhoto'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['spoiler'] = ($flags & 8) !== 0;
if (($flags & 1) !== 0) $tmp['photo'] = match (stream_get_contents($stream, 4)) {
'-²1#' => [
'_' => 'photoEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ezû' => self::deserialize_photo($stream),
'¡Ïr0' => self::deserialize_type_Photo(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 4) !== 0) $tmp['ttl_seconds'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_geoPoint(mixed $stream): mixed {
$tmp = ['_' => 'geoPoint'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['long'] = unpack('d', stream_get_contents($stream, 8))[1];
$tmp['lat'] = unpack('d', stream_get_contents($stream, 8))[1];
$tmp['access_hash'] = unpack('q', stream_get_contents($stream, 8))[1];
if (($flags & 1) !== 0) $tmp['accuracy_radius'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_GeoPoint(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => $this->deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_MaskCoords(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'²ÛÖ®' => [
'_' => 'maskCoords',
'n' => unpack('l', stream_get_contents($stream, 4))[1],
'x' => unpack('d', stream_get_contents($stream, 8))[1],
'y' => unpack('d', stream_get_contents($stream, 8))[1],
'zoom' => unpack('d', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => $this->deserialize_type_MaskCoords(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_documentAttributeSticker(mixed $stream): mixed {
$tmp = ['_' => 'documentAttributeSticker'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['mask'] = ($flags & 2) !== 0;
$tmp['alt'] = self::deserialize_string($stream);
$tmp['stickerset'] = match (stream_get_contents($stream, 4)) {
'•+¶ÿ' => [
'_' => 'inputStickerSetEmpty',
],
'i¢ç�' => [
'_' => 'inputStickerSetID',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
' È†' => [
'_' => 'inputStickerSetShortName',
'short_name' => self::deserialize_string($stream),
],
'È‡' => [
'_' => 'inputStickerSetAnimatedEmoji',
],
'Ræ' => [
'_' => 'inputStickerSetDice',
'emoticon' => self::deserialize_string($stream),
],
'97Þ' => [
'_' => 'inputStickerSetAnimatedEmojiAnimations',
],
';‹È' => [
'_' => 'inputStickerSetPremiumGifts',
],
'ÎÔÄ' => [
'_' => 'inputStickerSetEmojiGenericAnimations',
],
'îõÐ)' => [
'_' => 'inputStickerSetEmojiDefaultStatuses',
],
'éøÁD' => [
'_' => 'inputStickerSetEmojiDefaultTopicIcons',
],
'S…tI' => [
'_' => 'inputStickerSetEmojiChannelDefaultStatuses',
],
'¡Ïr0' => self::deserialize_type_InputStickerSet(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 1) !== 0) $tmp['mask_coords'] = match (stream_get_contents($stream, 4)) {
'²ÛÖ®' => [
'_' => 'maskCoords',
'n' => unpack('l', stream_get_contents($stream, 4))[1],
'x' => unpack('d', stream_get_contents($stream, 8))[1],
'y' => unpack('d', stream_get_contents($stream, 8))[1],
'zoom' => unpack('d', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_MaskCoords(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_documentAttributeVideo(mixed $stream): mixed {
$tmp = ['_' => 'documentAttributeVideo'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['round_message'] = ($flags & 1) !== 0;
$tmp['supports_streaming'] = ($flags & 2) !== 0;
$tmp['nosound'] = ($flags & 8) !== 0;
$tmp['duration'] = unpack('d', stream_get_contents($stream, 8))[1];
$tmp['w'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['h'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 4) !== 0) $tmp['preload_prefix_size'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_documentAttributeAudio(mixed $stream): mixed {
$tmp = ['_' => 'documentAttributeAudio'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['voice'] = ($flags & 1024) !== 0;
$tmp['duration'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['title'] = self::deserialize_string($stream);
if (($flags & 2) !== 0) $tmp['performer'] = self::deserialize_string($stream);
if (($flags & 4) !== 0) $tmp['waveform'] = self::deserialize_waveform($stream);
return $tmp;

    }
    private  function deserialize_documentAttributeCustomEmoji(mixed $stream): mixed {
$tmp = ['_' => 'documentAttributeCustomEmoji'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['free'] = ($flags & 1) !== 0;
$tmp['text_color'] = ($flags & 2) !== 0;
$tmp['alt'] = self::deserialize_string($stream);
$tmp['stickerset'] = match (stream_get_contents($stream, 4)) {
'•+¶ÿ' => [
'_' => 'inputStickerSetEmpty',
],
'i¢ç�' => [
'_' => 'inputStickerSetID',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
' È†' => [
'_' => 'inputStickerSetShortName',
'short_name' => self::deserialize_string($stream),
],
'È‡' => [
'_' => 'inputStickerSetAnimatedEmoji',
],
'Ræ' => [
'_' => 'inputStickerSetDice',
'emoticon' => self::deserialize_string($stream),
],
'97Þ' => [
'_' => 'inputStickerSetAnimatedEmojiAnimations',
],
';‹È' => [
'_' => 'inputStickerSetPremiumGifts',
],
'ÎÔÄ' => [
'_' => 'inputStickerSetEmojiGenericAnimations',
],
'îõÐ)' => [
'_' => 'inputStickerSetEmojiDefaultStatuses',
],
'éøÁD' => [
'_' => 'inputStickerSetEmojiDefaultTopicIcons',
],
'S…tI' => [
'_' => 'inputStickerSetEmojiChannelDefaultStatuses',
],
'¡Ïr0' => self::deserialize_type_InputStickerSet(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_type_array_of_DocumentAttribute(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'\\Á7l' => [
'_' => 'documentAttributeImageSize',
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'9‰µ' => [
'_' => 'documentAttributeAnimated',
],
'Öc' => self::deserialize_documentAttributeSticker($stream),
'Âñ�Ó' => self::deserialize_documentAttributeVideo($stream),
'ÆùR˜' => self::deserialize_documentAttributeAudio($stream),
'h' . "\0" . 'Y' => [
'_' => 'documentAttributeFilename',
'file_name' => self::deserialize_string($stream),
],
'÷Ò˜' => [
'_' => 'documentAttributeHasStickers',
],
'™˜ý' => self::deserialize_documentAttributeCustomEmoji($stream),
'¡Ïr0' => $this->deserialize_type_DocumentAttribute(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_document(mixed $stream): mixed {
$this->referenceDatabase?->addOriginContext('document');
$tmp = ['_' => 'document'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['access_hash'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['file_reference'] = self::deserialize_bytes($stream);
$tmp['date'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['mime_type'] = self::deserialize_string($stream);
$tmp['size'] = unpack('q', stream_get_contents($stream, 8))[1];
if (($flags & 1) !== 0) $tmp['thumbs'] = self::deserialize_type_array_of_PhotoSize(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 2) !== 0) $tmp['video_thumbs'] = self::deserialize_type_array_of_VideoSize(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['dc_id'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['attributes'] = self::deserialize_type_array_of_DocumentAttribute(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$this->referenceDatabase?->addReference($tmp);
$this->referenceDatabase?->addOrigin($tmp);
return $tmp;

    }
    private  function deserialize_type_Document(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'qÈø6' => [
'_' => 'documentEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ØÄÔ�' => self::deserialize_document($stream),
'¡Ïr0' => $this->deserialize_type_Document(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_messageMediaDocument(mixed $stream): mixed {
$tmp = ['_' => 'messageMediaDocument'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['nopremium'] = ($flags & 8) !== 0;
$tmp['spoiler'] = ($flags & 16) !== 0;
$tmp['video'] = ($flags & 64) !== 0;
$tmp['round'] = ($flags & 128) !== 0;
$tmp['voice'] = ($flags & 256) !== 0;
if (($flags & 1) !== 0) $tmp['document'] = match (stream_get_contents($stream, 4)) {
'qÈø6' => [
'_' => 'documentEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ØÄÔ�' => self::deserialize_document($stream),
'¡Ïr0' => self::deserialize_type_Document(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 32) !== 0) $tmp['alt_document'] = match (stream_get_contents($stream, 4)) {
'qÈø6' => [
'_' => 'documentEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ØÄÔ�' => self::deserialize_document($stream),
'¡Ïr0' => self::deserialize_type_Document(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 4) !== 0) $tmp['ttl_seconds'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_webPageEmpty(mixed $stream): mixed {
$tmp = ['_' => 'webPageEmpty'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['id'] = unpack('q', stream_get_contents($stream, 8))[1];
if (($flags & 1) !== 0) $tmp['url'] = self::deserialize_string($stream);
return $tmp;

    }
    private  function deserialize_webPagePending(mixed $stream): mixed {
$tmp = ['_' => 'webPagePending'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['id'] = unpack('q', stream_get_contents($stream, 8))[1];
if (($flags & 1) !== 0) $tmp['url'] = self::deserialize_string($stream);
$tmp['date'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_array_of_RichText(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => $this->deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => $this->deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => $this->deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => $this->deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => $this->deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => $this->deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => $this->deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => $this->deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => $this->deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => $this->deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => $this->deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => $this->deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => $this->deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => $this->deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_RichText(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => $this->deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => $this->deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => $this->deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => $this->deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => $this->deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => $this->deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => $this->deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => $this->deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => $this->deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => $this->deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => $this->deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => $this->deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => $this->deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => $this->deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => $this->deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => $this->deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => $this->deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => $this->deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => $this->deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => $this->deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => $this->deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => $this->deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => $this->deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => $this->deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => $this->deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => $this->deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => $this->deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => $this->deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => $this->deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => $this->deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => $this->deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => $this->deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => $this->deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => $this->deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => $this->deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => $this->deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => $this->deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => $this->deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => $this->deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'•»ø›' => [
'_' => 'textStrike',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => $this->deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => $this->deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => $this->deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => $this->deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => $this->deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => $this->deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => $this->deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => $this->deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => $this->deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => $this->deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => $this->deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => $this->deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => $this->deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¹?l' => [
'_' => 'textFixed',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => $this->deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => $this->deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => $this->deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => $this->deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => $this->deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => $this->deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => $this->deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => $this->deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => $this->deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => $this->deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => $this->deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => $this->deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => $this->deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Á„(<' => [
'_' => 'textUrl',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => $this->deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => $this->deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => $this->deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => $this->deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => $this->deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => $this->deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => $this->deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => $this->deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => $this->deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => $this->deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => $this->deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => $this->deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => $this->deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => $this->deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => $this->deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => $this->deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => $this->deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => $this->deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => $this->deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => $this->deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => $this->deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => $this->deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => $this->deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => $this->deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => $this->deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => $this->deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => $this->deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => $this->deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => $this->deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => $this->deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => $this->deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => $this->deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => $this->deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => $this->deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => $this->deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => $this->deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => $this->deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => $this->deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => $this->deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => $this->deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => $this->deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => $this->deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => $this->deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => $this->deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => $this->deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => $this->deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => $this->deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => $this->deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => $this->deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => $this->deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => $this->deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => $this->deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'!†K' => [
'_' => 'textMarked',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => $this->deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => $this->deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => $this->deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => $this->deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => $this->deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => $this->deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => $this->deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => $this->deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => $this->deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => $this->deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => $this->deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => $this->deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => $this->deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'j–Ë' => [
'_' => 'textPhone',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => $this->deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => $this->deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => $this->deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => $this->deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => $this->deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => $this->deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => $this->deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => $this->deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => $this->deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => $this->deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => $this->deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => $this->deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => $this->deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => $this->deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => $this->deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => $this->deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => $this->deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => $this->deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => $this->deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => $this->deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => $this->deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => $this->deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => $this->deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => $this->deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => $this->deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => $this->deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => $this->deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_array_of_PageListItem(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'Í¶/¹' => [
'_' => 'pageListItemText',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'üsà%' => [
'_' => 'pageListItemBlocks',
'blocks' => $this->deserialize_type_array_of_PageBlock(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => $this->deserialize_type_PageListItem(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_PageCaption(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'Wvto' => [
'_' => 'pageCaption',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
'credit' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => $this->deserialize_type_PageCaption(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_pageBlockPhoto(mixed $stream): mixed {
$tmp = ['_' => 'pageBlockPhoto'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['photo_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['caption'] = match (stream_get_contents($stream, 4)) {
'Wvto' => [
'_' => 'pageCaption',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
'credit' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_PageCaption(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 1) !== 0) $tmp['url'] = self::deserialize_string($stream);
if (($flags & 1) !== 0) $tmp['webpage_id'] = unpack('q', stream_get_contents($stream, 8))[1];
return $tmp;

    }
    private  function deserialize_pageBlockVideo(mixed $stream): mixed {
$tmp = ['_' => 'pageBlockVideo'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['autoplay'] = ($flags & 1) !== 0;
$tmp['loop'] = ($flags & 2) !== 0;
$tmp['video_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['caption'] = match (stream_get_contents($stream, 4)) {
'Wvto' => [
'_' => 'pageCaption',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
'credit' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_PageCaption(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_pageBlockEmbed(mixed $stream): mixed {
$tmp = ['_' => 'pageBlockEmbed'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['full_width'] = ($flags & 1) !== 0;
$tmp['allow_scrolling'] = ($flags & 8) !== 0;
if (($flags & 2) !== 0) $tmp['url'] = self::deserialize_string($stream);
if (($flags & 4) !== 0) $tmp['html'] = self::deserialize_string($stream);
if (($flags & 16) !== 0) $tmp['poster_photo_id'] = unpack('q', stream_get_contents($stream, 8))[1];
if (($flags & 32) !== 0) $tmp['w'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 32) !== 0) $tmp['h'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['caption'] = match (stream_get_contents($stream, 4)) {
'Wvto' => [
'_' => 'pageCaption',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
'credit' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_PageCaption(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_chatEmpty(mixed $stream): mixed {
$tmp = [
'_' => 'chatEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
];
$this->peerDatabase->addChat($tmp);
return $tmp;

    }
    private  function deserialize_chatPhoto(mixed $stream): mixed {
$tmp = ['_' => 'chatPhoto'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['has_video'] = ($flags & 1) !== 0;
$tmp['photo_id'] = unpack('q', stream_get_contents($stream, 8))[1];
if (($flags & 2) !== 0) $tmp['stripped_thumb'] = self::deserialize_bytes($stream);
$tmp['dc_id'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_ChatPhoto(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'Á7' => [
'_' => 'chatPhotoEmpty',
],
'n' => self::deserialize_chatPhoto($stream),
'¡Ïr0' => $this->deserialize_type_ChatPhoto(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_InputPeer(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'ê;' => [
'_' => 'inputPeerEmpty',
],
'É~ }' => [
'_' => 'inputPeerSelf',
],
'¹\\©5' => [
'_' => 'inputPeerChat',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'L¥èÝ' => [
'_' => 'inputPeerUser',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ü»¼\'' => [
'_' => 'inputPeerChannel',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'
{¨' => [
'_' => 'inputPeerUserFromMessage',
'peer' => match (stream_get_contents($stream, 4)) {
'ê;' => [
'_' => 'inputPeerEmpty',
],
'É~ }' => [
'_' => 'inputPeerSelf',
],
'¹\\©5' => [
'_' => 'inputPeerChat',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'L¥èÝ' => [
'_' => 'inputPeerUser',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ü»¼\'' => [
'_' => 'inputPeerChannel',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'
{¨' => [
'_' => 'inputPeerUserFromMessage',
'peer' => $this->deserialize_type_InputPeer($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'@*½' => [
'_' => 'inputPeerChannelFromMessage',
'peer' => $this->deserialize_type_InputPeer($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => $this->deserialize_type_InputPeer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'@*½' => [
'_' => 'inputPeerChannelFromMessage',
'peer' => match (stream_get_contents($stream, 4)) {
'ê;' => [
'_' => 'inputPeerEmpty',
],
'É~ }' => [
'_' => 'inputPeerSelf',
],
'¹\\©5' => [
'_' => 'inputPeerChat',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'L¥èÝ' => [
'_' => 'inputPeerUser',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ü»¼\'' => [
'_' => 'inputPeerChannel',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'
{¨' => [
'_' => 'inputPeerUserFromMessage',
'peer' => $this->deserialize_type_InputPeer($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'@*½' => [
'_' => 'inputPeerChannelFromMessage',
'peer' => $this->deserialize_type_InputPeer($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => $this->deserialize_type_InputPeer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => $this->deserialize_type_InputPeer(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_InputChannel(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'†Œî' => [
'_' => 'inputChannelEmpty',
],
'(ìZó' => [
'_' => 'inputChannel',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'�O“[' => [
'_' => 'inputChannelFromMessage',
'peer' => match (stream_get_contents($stream, 4)) {
'ê;' => [
'_' => 'inputPeerEmpty',
],
'É~ }' => [
'_' => 'inputPeerSelf',
],
'¹\\©5' => [
'_' => 'inputPeerChat',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'L¥èÝ' => [
'_' => 'inputPeerUser',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ü»¼\'' => [
'_' => 'inputPeerChannel',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'
{¨' => [
'_' => 'inputPeerUserFromMessage',
'peer' => self::deserialize_type_InputPeer($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'@*½' => [
'_' => 'inputPeerChannelFromMessage',
'peer' => self::deserialize_type_InputPeer($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputPeer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => $this->deserialize_type_InputChannel(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_chatAdminRights(mixed $stream): mixed {
$tmp = ['_' => 'chatAdminRights'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['change_info'] = ($flags & 1) !== 0;
$tmp['post_messages'] = ($flags & 2) !== 0;
$tmp['edit_messages'] = ($flags & 4) !== 0;
$tmp['delete_messages'] = ($flags & 8) !== 0;
$tmp['ban_users'] = ($flags & 16) !== 0;
$tmp['invite_users'] = ($flags & 32) !== 0;
$tmp['pin_messages'] = ($flags & 128) !== 0;
$tmp['add_admins'] = ($flags & 512) !== 0;
$tmp['anonymous'] = ($flags & 1024) !== 0;
$tmp['manage_call'] = ($flags & 2048) !== 0;
$tmp['other'] = ($flags & 4096) !== 0;
$tmp['manage_topics'] = ($flags & 8192) !== 0;
$tmp['post_stories'] = ($flags & 16384) !== 0;
$tmp['edit_stories'] = ($flags & 32768) !== 0;
$tmp['delete_stories'] = ($flags & 65536) !== 0;
return $tmp;

    }
    private  function deserialize_type_ChatAdminRights(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'Õ$²_' => self::deserialize_chatAdminRights($stream),
'¡Ïr0' => $this->deserialize_type_ChatAdminRights(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_chatBannedRights(mixed $stream): mixed {
$tmp = ['_' => 'chatBannedRights'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['view_messages'] = ($flags & 1) !== 0;
$tmp['send_messages'] = ($flags & 2) !== 0;
$tmp['send_media'] = ($flags & 4) !== 0;
$tmp['send_stickers'] = ($flags & 8) !== 0;
$tmp['send_gifs'] = ($flags & 16) !== 0;
$tmp['send_games'] = ($flags & 32) !== 0;
$tmp['send_inline'] = ($flags & 64) !== 0;
$tmp['embed_links'] = ($flags & 128) !== 0;
$tmp['send_polls'] = ($flags & 256) !== 0;
$tmp['change_info'] = ($flags & 1024) !== 0;
$tmp['invite_users'] = ($flags & 32768) !== 0;
$tmp['pin_messages'] = ($flags & 131072) !== 0;
$tmp['manage_topics'] = ($flags & 262144) !== 0;
$tmp['send_photos'] = ($flags & 524288) !== 0;
$tmp['send_videos'] = ($flags & 1048576) !== 0;
$tmp['send_roundvideos'] = ($flags & 2097152) !== 0;
$tmp['send_audios'] = ($flags & 4194304) !== 0;
$tmp['send_voices'] = ($flags & 8388608) !== 0;
$tmp['send_docs'] = ($flags & 16777216) !== 0;
$tmp['send_plain'] = ($flags & 33554432) !== 0;
$tmp['until_date'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_ChatBannedRights(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'Ÿ' => self::deserialize_chatBannedRights($stream),
'¡Ïr0' => $this->deserialize_type_ChatBannedRights(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_chat(mixed $stream): mixed {
$this->referenceDatabase?->addOriginContext('chat');
$tmp = ['_' => 'chat'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['creator'] = ($flags & 1) !== 0;
$tmp['left'] = ($flags & 4) !== 0;
$tmp['deactivated'] = ($flags & 32) !== 0;
$tmp['call_active'] = ($flags & 8388608) !== 0;
$tmp['call_not_empty'] = ($flags & 16777216) !== 0;
$tmp['noforwards'] = ($flags & 33554432) !== 0;
$tmp['id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['title'] = self::deserialize_string($stream);
$tmp['photo'] = match (stream_get_contents($stream, 4)) {
'Á7' => [
'_' => 'chatPhotoEmpty',
],
'n' => self::deserialize_chatPhoto($stream),
'¡Ïr0' => self::deserialize_type_ChatPhoto(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['participants_count'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['date'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['version'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 64) !== 0) $tmp['migrated_to'] = match (stream_get_contents($stream, 4)) {
'†Œî' => [
'_' => 'inputChannelEmpty',
],
'(ìZó' => [
'_' => 'inputChannel',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'�O“[' => [
'_' => 'inputChannelFromMessage',
'peer' => match (stream_get_contents($stream, 4)) {
'ê;' => [
'_' => 'inputPeerEmpty',
],
'É~ }' => [
'_' => 'inputPeerSelf',
],
'¹\\©5' => [
'_' => 'inputPeerChat',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'L¥èÝ' => [
'_' => 'inputPeerUser',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ü»¼\'' => [
'_' => 'inputPeerChannel',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'
{¨' => [
'_' => 'inputPeerUserFromMessage',
'peer' => self::deserialize_type_InputPeer($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'@*½' => [
'_' => 'inputPeerChannelFromMessage',
'peer' => self::deserialize_type_InputPeer($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputPeer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputChannel(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 16384) !== 0) $tmp['admin_rights'] = match (stream_get_contents($stream, 4)) {
'Õ$²_' => self::deserialize_chatAdminRights($stream),
'¡Ïr0' => self::deserialize_type_ChatAdminRights(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 262144) !== 0) $tmp['default_banned_rights'] = match (stream_get_contents($stream, 4)) {
'Ÿ' => self::deserialize_chatBannedRights($stream),
'¡Ïr0' => self::deserialize_type_ChatBannedRights(self::gzdecode($stream)),
default => self::err($stream)
}
;
$this->referenceDatabase?->addOrigin($tmp);
$this->peerDatabase->addChat($tmp);
return $tmp;

    }
    private  function deserialize_chatForbidden(mixed $stream): mixed {
$tmp = [
'_' => 'chatForbidden',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'title' => self::deserialize_string($stream),
];
$this->peerDatabase->addChat($tmp);
return $tmp;

    }
    private  function deserialize_type_array_of_RestrictionReason(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'´¬rÐ' => [
'_' => 'restrictionReason',
'platform' => self::deserialize_string($stream),
'reason' => self::deserialize_string($stream),
'text' => self::deserialize_string($stream),
],
'¡Ïr0' => $this->deserialize_type_RestrictionReason(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_username(mixed $stream): mixed {
$tmp = ['_' => 'username'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['editable'] = ($flags & 1) !== 0;
$tmp['active'] = ($flags & 2) !== 0;
$tmp['username'] = self::deserialize_string($stream);
return $tmp;

    }
    private  function deserialize_type_array_of_Username(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'G6´' => self::deserialize_username($stream),
'¡Ïr0' => $this->deserialize_type_Username(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_peerColor(mixed $stream): mixed {
$tmp = ['_' => 'peerColor'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['color'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 2) !== 0) $tmp['background_emoji_id'] = unpack('q', stream_get_contents($stream, 8))[1];
return $tmp;

    }
    private  function deserialize_type_PeerColor(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'ÏZKµ' => self::deserialize_peerColor($stream),
'¡Ïr0' => $this->deserialize_type_PeerColor(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_EmojiStatus(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'®á-' => [
'_' => 'emojiStatusEmpty',
],
'�a›’' => [
'_' => 'emojiStatus',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'Ç¨0ú' => [
'_' => 'emojiStatusUntil',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'until' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => $this->deserialize_type_EmojiStatus(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_channel(mixed $stream): mixed {
$this->referenceDatabase?->addOriginContext('channel');
$tmp = ['_' => 'channel'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['creator'] = ($flags & 1) !== 0;
$tmp['left'] = ($flags & 4) !== 0;
$tmp['broadcast'] = ($flags & 32) !== 0;
$tmp['verified'] = ($flags & 128) !== 0;
$tmp['megagroup'] = ($flags & 256) !== 0;
$tmp['restricted'] = ($flags & 512) !== 0;
$tmp['signatures'] = ($flags & 2048) !== 0;
$tmp['min'] = ($flags & 4096) !== 0;
$tmp['scam'] = ($flags & 524288) !== 0;
$tmp['has_link'] = ($flags & 1048576) !== 0;
$tmp['has_geo'] = ($flags & 2097152) !== 0;
$tmp['slowmode_enabled'] = ($flags & 4194304) !== 0;
$tmp['call_active'] = ($flags & 8388608) !== 0;
$tmp['call_not_empty'] = ($flags & 16777216) !== 0;
$tmp['fake'] = ($flags & 33554432) !== 0;
$tmp['gigagroup'] = ($flags & 67108864) !== 0;
$tmp['noforwards'] = ($flags & 134217728) !== 0;
$tmp['join_to_send'] = ($flags & 268435456) !== 0;
$tmp['join_request'] = ($flags & 536870912) !== 0;
$tmp['forum'] = ($flags & 1073741824) !== 0;
$flags2 = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['stories_hidden'] = ($flags2 & 2) !== 0;
$tmp['stories_hidden_min'] = ($flags2 & 4) !== 0;
$tmp['stories_unavailable'] = ($flags2 & 8) !== 0;
$tmp['id'] = unpack('q', stream_get_contents($stream, 8))[1];
if (($flags & 8192) !== 0) $tmp['access_hash'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['title'] = self::deserialize_string($stream);
if (($flags & 64) !== 0) $tmp['username'] = self::deserialize_string($stream);
$tmp['photo'] = match (stream_get_contents($stream, 4)) {
'Á7' => [
'_' => 'chatPhotoEmpty',
],
'n' => self::deserialize_chatPhoto($stream),
'¡Ïr0' => self::deserialize_type_ChatPhoto(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['date'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 512) !== 0) $tmp['restriction_reason'] = self::deserialize_type_array_of_RestrictionReason(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 16384) !== 0) $tmp['admin_rights'] = match (stream_get_contents($stream, 4)) {
'Õ$²_' => self::deserialize_chatAdminRights($stream),
'¡Ïr0' => self::deserialize_type_ChatAdminRights(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 32768) !== 0) $tmp['banned_rights'] = match (stream_get_contents($stream, 4)) {
'Ÿ' => self::deserialize_chatBannedRights($stream),
'¡Ïr0' => self::deserialize_type_ChatBannedRights(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 262144) !== 0) $tmp['default_banned_rights'] = match (stream_get_contents($stream, 4)) {
'Ÿ' => self::deserialize_chatBannedRights($stream),
'¡Ïr0' => self::deserialize_type_ChatBannedRights(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 131072) !== 0) $tmp['participants_count'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags2 & 1) !== 0) $tmp['usernames'] = self::deserialize_type_array_of_Username(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags2 & 16) !== 0) $tmp['stories_max_id'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags2 & 128) !== 0) $tmp['color'] = match (stream_get_contents($stream, 4)) {
'ÏZKµ' => self::deserialize_peerColor($stream),
'¡Ïr0' => self::deserialize_type_PeerColor(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags2 & 256) !== 0) $tmp['profile_color'] = match (stream_get_contents($stream, 4)) {
'ÏZKµ' => self::deserialize_peerColor($stream),
'¡Ïr0' => self::deserialize_type_PeerColor(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags2 & 512) !== 0) $tmp['emoji_status'] = match (stream_get_contents($stream, 4)) {
'®á-' => [
'_' => 'emojiStatusEmpty',
],
'�a›’' => [
'_' => 'emojiStatus',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'Ç¨0ú' => [
'_' => 'emojiStatusUntil',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'until' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_EmojiStatus(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags2 & 1024) !== 0) $tmp['level'] = unpack('l', stream_get_contents($stream, 4))[1];
$this->referenceDatabase?->addOrigin($tmp);
$this->peerDatabase->addChat($tmp);
return $tmp;

    }
    private  function deserialize_channelForbidden(mixed $stream): mixed {
$tmp = ['_' => 'channelForbidden'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['broadcast'] = ($flags & 32) !== 0;
$tmp['megagroup'] = ($flags & 256) !== 0;
$tmp['id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['access_hash'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['title'] = self::deserialize_string($stream);
if (($flags & 65536) !== 0) $tmp['until_date'] = unpack('l', stream_get_contents($stream, 4))[1];
$this->peerDatabase->addChat($tmp);
return $tmp;

    }
    private  function deserialize_type_Chat(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'e(V)' => self::deserialize_chatEmpty($stream),
'VòËA' => self::deserialize_chat($stream),
'§¡’e' => self::deserialize_chatForbidden($stream),
'�ü­
' => self::deserialize_channel($stream),
'Õ“Ô' => self::deserialize_channelForbidden($stream),
'¡Ïr0' => $this->deserialize_type_Chat(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_pageTableCell(mixed $stream): mixed {
$tmp = ['_' => 'pageTableCell'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['header'] = ($flags & 1) !== 0;
$tmp['align_center'] = ($flags & 8) !== 0;
$tmp['align_right'] = ($flags & 16) !== 0;
$tmp['valign_middle'] = ($flags & 32) !== 0;
$tmp['valign_bottom'] = ($flags & 64) !== 0;
if (($flags & 128) !== 0) $tmp['text'] = match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 2) !== 0) $tmp['colspan'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 4) !== 0) $tmp['rowspan'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_array_of_PageTableCell(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'jkV4' => self::deserialize_pageTableCell($stream),
'¡Ïr0' => $this->deserialize_type_PageTableCell(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_PageTableRow(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'åÅÀà' => [
'_' => 'pageTableRow',
'cells' => self::deserialize_type_array_of_PageTableCell(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => $this->deserialize_type_PageTableRow(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_pageBlockTable(mixed $stream): mixed {
$tmp = ['_' => 'pageBlockTable'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['bordered'] = ($flags & 1) !== 0;
$tmp['striped'] = ($flags & 2) !== 0;
$tmp['title'] = match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['rows'] = self::deserialize_type_array_of_PageTableRow(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
return $tmp;

    }
    private  function deserialize_type_array_of_PageListOrderedItem(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'G€^' => [
'_' => 'pageListOrderedItemText',
'num' => self::deserialize_string($stream),
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'6‰Ý˜' => [
'_' => 'pageListOrderedItemBlocks',
'num' => self::deserialize_string($stream),
'blocks' => $this->deserialize_type_array_of_PageBlock(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => $this->deserialize_type_PageListOrderedItem(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_pageBlockDetails(mixed $stream): mixed {
$tmp = ['_' => 'pageBlockDetails'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['open'] = ($flags & 1) !== 0;
$tmp['blocks'] = $this->deserialize_type_array_of_PageBlock(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['title'] = match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_pageRelatedArticle(mixed $stream): mixed {
$tmp = ['_' => 'pageRelatedArticle'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['url'] = self::deserialize_string($stream);
$tmp['webpage_id'] = unpack('q', stream_get_contents($stream, 8))[1];
if (($flags & 1) !== 0) $tmp['title'] = self::deserialize_string($stream);
if (($flags & 2) !== 0) $tmp['description'] = self::deserialize_string($stream);
if (($flags & 4) !== 0) $tmp['photo_id'] = unpack('q', stream_get_contents($stream, 8))[1];
if (($flags & 8) !== 0) $tmp['author'] = self::deserialize_string($stream);
if (($flags & 16) !== 0) $tmp['published_date'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_array_of_PageRelatedArticle(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'Ü�³' => self::deserialize_pageRelatedArticle($stream),
'¡Ïr0' => $this->deserialize_type_PageRelatedArticle(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_PageBlock(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'Š~V' => [
'_' => 'pageBlockUnsupported',
],
'ýÃ«p' => [
'_' => 'pageBlockTitle',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'šú�' => [
'_' => 'pageBlockSubtitle',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'àå¯º' => [
'_' => 'pageBlockAuthorDate',
'author' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
'published_date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'ìdÐ¿' => [
'_' => 'pageBlockHeader',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'á¶+ñ' => [
'_' => 'pageBlockSubheader',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'fzF' => [
'_' => 'pageBlockParagraph',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'>ÙpÀ' => [
'_' => 'pageBlockPreformatted',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
'language' => self::deserialize_string($stream),
],
'™	‡H' => [
'_' => 'pageBlockFooter',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ˆ± Û' => [
'_' => 'pageBlockDivider',
],
'°7Î' => [
'_' => 'pageBlockAnchor',
'name' => self::deserialize_string($stream),
],
'€èä' => [
'_' => 'pageBlockList',
'items' => self::deserialize_type_array_of_PageListItem(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'&|=&' => [
'_' => 'pageBlockBlockquote',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
'caption' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ÓVDO' => [
'_' => 'pageBlockPullquote',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
'caption' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'`ÅY' => self::deserialize_pageBlockPhoto($stream),
'¶ç�|' => self::deserialize_pageBlockVideo($stream),
'' . "\0" . '3ò9' => [
'_' => 'pageBlockCover',
'cover' => match (stream_get_contents($stream, 4)) {
'Š~V' => [
'_' => 'pageBlockUnsupported',
],
'ýÃ«p' => [
'_' => 'pageBlockTitle',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'šú�' => [
'_' => 'pageBlockSubtitle',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'àå¯º' => [
'_' => 'pageBlockAuthorDate',
'author' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
'published_date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'ìdÐ¿' => [
'_' => 'pageBlockHeader',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'á¶+ñ' => [
'_' => 'pageBlockSubheader',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'fzF' => [
'_' => 'pageBlockParagraph',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'>ÙpÀ' => [
'_' => 'pageBlockPreformatted',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
'language' => self::deserialize_string($stream),
],
'™	‡H' => [
'_' => 'pageBlockFooter',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ˆ± Û' => [
'_' => 'pageBlockDivider',
],
'°7Î' => [
'_' => 'pageBlockAnchor',
'name' => self::deserialize_string($stream),
],
'€èä' => [
'_' => 'pageBlockList',
'items' => self::deserialize_type_array_of_PageListItem(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'&|=&' => [
'_' => 'pageBlockBlockquote',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
'caption' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ÓVDO' => [
'_' => 'pageBlockPullquote',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
'caption' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'`ÅY' => self::deserialize_pageBlockPhoto($stream),
'¶ç�|' => self::deserialize_pageBlockVideo($stream),
'' . "\0" . '3ò9' => [
'_' => 'pageBlockCover',
'cover' => $this->deserialize_type_PageBlock($stream),
],
'Å�q¨' => self::deserialize_pageBlockEmbed($stream),
'¨Yò' => [
'_' => 'pageBlockEmbedPost',
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
'author_photo_id' => unpack('q', stream_get_contents($stream, 8))[1],
'author' => self::deserialize_string($stream),
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'blocks' => $this->deserialize_type_array_of_PageBlock(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'caption' => match (stream_get_contents($stream, 4)) {
'Wvto' => [
'_' => 'pageCaption',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
'credit' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_PageCaption(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Mú e' => [
'_' => 'pageBlockCollage',
'items' => $this->deserialize_type_array_of_PageBlock(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'caption' => match (stream_get_contents($stream, 4)) {
'Wvto' => [
'_' => 'pageCaption',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
'credit' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_PageCaption(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'�•' => [
'_' => 'pageBlockSlideshow',
'items' => $this->deserialize_type_array_of_PageBlock(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'caption' => match (stream_get_contents($stream, 4)) {
'Wvto' => [
'_' => 'pageCaption',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
'credit' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_PageCaption(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'µQï' => [
'_' => 'pageBlockChannel',
'channel' => match (stream_get_contents($stream, 4)) {
'e(V)' => self::deserialize_chatEmpty($stream),
'VòËA' => self::deserialize_chat($stream),
'§¡’e' => self::deserialize_chatForbidden($stream),
'�ü­
' => self::deserialize_channel($stream),
'Õ“Ô' => self::deserialize_channelForbidden($stream),
'¡Ïr0' => self::deserialize_type_Chat(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'êaC€' => [
'_' => 'pageBlockAudio',
'audio_id' => unpack('q', stream_get_contents($stream, 8))[1],
'caption' => match (stream_get_contents($stream, 4)) {
'Wvto' => [
'_' => 'pageCaption',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
'credit' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_PageCaption(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'�ƒ' => [
'_' => 'pageBlockKicker',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'‚êM¿' => self::deserialize_pageBlockTable($stream),
'ááŠš' => [
'_' => 'pageBlockOrderedList',
'items' => self::deserialize_type_array_of_PageListOrderedItem(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'í‹vv' => self::deserialize_pageBlockDetails($stream),
'–Z' => [
'_' => 'pageBlockRelatedArticles',
'title' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
'articles' => self::deserialize_type_array_of_PageRelatedArticle(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ö>O¤' => [
'_' => 'pageBlockMap',
'geo' => match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => self::deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
,
'zoom' => unpack('l', stream_get_contents($stream, 4))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'caption' => match (stream_get_contents($stream, 4)) {
'Wvto' => [
'_' => 'pageCaption',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
'credit' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_PageCaption(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => $this->deserialize_type_PageBlock(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Å�q¨' => self::deserialize_pageBlockEmbed($stream),
'¨Yò' => [
'_' => 'pageBlockEmbedPost',
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
'author_photo_id' => unpack('q', stream_get_contents($stream, 8))[1],
'author' => self::deserialize_string($stream),
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'blocks' => $this->deserialize_type_array_of_PageBlock(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'caption' => match (stream_get_contents($stream, 4)) {
'Wvto' => [
'_' => 'pageCaption',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
'credit' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_PageCaption(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Mú e' => [
'_' => 'pageBlockCollage',
'items' => $this->deserialize_type_array_of_PageBlock(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'caption' => match (stream_get_contents($stream, 4)) {
'Wvto' => [
'_' => 'pageCaption',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
'credit' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_PageCaption(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'�•' => [
'_' => 'pageBlockSlideshow',
'items' => $this->deserialize_type_array_of_PageBlock(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'caption' => match (stream_get_contents($stream, 4)) {
'Wvto' => [
'_' => 'pageCaption',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
'credit' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_PageCaption(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'µQï' => [
'_' => 'pageBlockChannel',
'channel' => match (stream_get_contents($stream, 4)) {
'e(V)' => self::deserialize_chatEmpty($stream),
'VòËA' => self::deserialize_chat($stream),
'§¡’e' => self::deserialize_chatForbidden($stream),
'�ü­
' => self::deserialize_channel($stream),
'Õ“Ô' => self::deserialize_channelForbidden($stream),
'¡Ïr0' => self::deserialize_type_Chat(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'êaC€' => [
'_' => 'pageBlockAudio',
'audio_id' => unpack('q', stream_get_contents($stream, 8))[1],
'caption' => match (stream_get_contents($stream, 4)) {
'Wvto' => [
'_' => 'pageCaption',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
'credit' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_PageCaption(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'�ƒ' => [
'_' => 'pageBlockKicker',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'‚êM¿' => self::deserialize_pageBlockTable($stream),
'ááŠš' => [
'_' => 'pageBlockOrderedList',
'items' => self::deserialize_type_array_of_PageListOrderedItem(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'í‹vv' => self::deserialize_pageBlockDetails($stream),
'–Z' => [
'_' => 'pageBlockRelatedArticles',
'title' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
'articles' => self::deserialize_type_array_of_PageRelatedArticle(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ö>O¤' => [
'_' => 'pageBlockMap',
'geo' => match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => self::deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
,
'zoom' => unpack('l', stream_get_contents($stream, 4))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'caption' => match (stream_get_contents($stream, 4)) {
'Wvto' => [
'_' => 'pageCaption',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
'credit' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_PageCaption(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => $this->deserialize_type_PageBlock(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_array_of_PageBlock(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'Š~V' => [
'_' => 'pageBlockUnsupported',
],
'ýÃ«p' => [
'_' => 'pageBlockTitle',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'šú�' => [
'_' => 'pageBlockSubtitle',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'àå¯º' => [
'_' => 'pageBlockAuthorDate',
'author' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
'published_date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'ìdÐ¿' => [
'_' => 'pageBlockHeader',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'á¶+ñ' => [
'_' => 'pageBlockSubheader',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'fzF' => [
'_' => 'pageBlockParagraph',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'>ÙpÀ' => [
'_' => 'pageBlockPreformatted',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
'language' => self::deserialize_string($stream),
],
'™	‡H' => [
'_' => 'pageBlockFooter',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ˆ± Û' => [
'_' => 'pageBlockDivider',
],
'°7Î' => [
'_' => 'pageBlockAnchor',
'name' => self::deserialize_string($stream),
],
'€èä' => [
'_' => 'pageBlockList',
'items' => self::deserialize_type_array_of_PageListItem(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'&|=&' => [
'_' => 'pageBlockBlockquote',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
'caption' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ÓVDO' => [
'_' => 'pageBlockPullquote',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
'caption' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'`ÅY' => self::deserialize_pageBlockPhoto($stream),
'¶ç�|' => self::deserialize_pageBlockVideo($stream),
'' . "\0" . '3ò9' => [
'_' => 'pageBlockCover',
'cover' => match (stream_get_contents($stream, 4)) {
'Š~V' => [
'_' => 'pageBlockUnsupported',
],
'ýÃ«p' => [
'_' => 'pageBlockTitle',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'šú�' => [
'_' => 'pageBlockSubtitle',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'àå¯º' => [
'_' => 'pageBlockAuthorDate',
'author' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
'published_date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'ìdÐ¿' => [
'_' => 'pageBlockHeader',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'á¶+ñ' => [
'_' => 'pageBlockSubheader',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'fzF' => [
'_' => 'pageBlockParagraph',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'>ÙpÀ' => [
'_' => 'pageBlockPreformatted',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
'language' => self::deserialize_string($stream),
],
'™	‡H' => [
'_' => 'pageBlockFooter',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ˆ± Û' => [
'_' => 'pageBlockDivider',
],
'°7Î' => [
'_' => 'pageBlockAnchor',
'name' => self::deserialize_string($stream),
],
'€èä' => [
'_' => 'pageBlockList',
'items' => self::deserialize_type_array_of_PageListItem(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'&|=&' => [
'_' => 'pageBlockBlockquote',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
'caption' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ÓVDO' => [
'_' => 'pageBlockPullquote',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
'caption' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'`ÅY' => self::deserialize_pageBlockPhoto($stream),
'¶ç�|' => self::deserialize_pageBlockVideo($stream),
'' . "\0" . '3ò9' => [
'_' => 'pageBlockCover',
'cover' => self::deserialize_type_PageBlock($stream),
],
'Å�q¨' => self::deserialize_pageBlockEmbed($stream),
'¨Yò' => [
'_' => 'pageBlockEmbedPost',
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
'author_photo_id' => unpack('q', stream_get_contents($stream, 8))[1],
'author' => self::deserialize_string($stream),
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'blocks' => $this->deserialize_type_array_of_PageBlock(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'caption' => match (stream_get_contents($stream, 4)) {
'Wvto' => [
'_' => 'pageCaption',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
'credit' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_PageCaption(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Mú e' => [
'_' => 'pageBlockCollage',
'items' => $this->deserialize_type_array_of_PageBlock(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'caption' => match (stream_get_contents($stream, 4)) {
'Wvto' => [
'_' => 'pageCaption',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
'credit' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_PageCaption(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'�•' => [
'_' => 'pageBlockSlideshow',
'items' => $this->deserialize_type_array_of_PageBlock(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'caption' => match (stream_get_contents($stream, 4)) {
'Wvto' => [
'_' => 'pageCaption',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
'credit' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_PageCaption(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'µQï' => [
'_' => 'pageBlockChannel',
'channel' => match (stream_get_contents($stream, 4)) {
'e(V)' => self::deserialize_chatEmpty($stream),
'VòËA' => self::deserialize_chat($stream),
'§¡’e' => self::deserialize_chatForbidden($stream),
'�ü­
' => self::deserialize_channel($stream),
'Õ“Ô' => self::deserialize_channelForbidden($stream),
'¡Ïr0' => self::deserialize_type_Chat(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'êaC€' => [
'_' => 'pageBlockAudio',
'audio_id' => unpack('q', stream_get_contents($stream, 8))[1],
'caption' => match (stream_get_contents($stream, 4)) {
'Wvto' => [
'_' => 'pageCaption',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
'credit' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_PageCaption(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'�ƒ' => [
'_' => 'pageBlockKicker',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'‚êM¿' => self::deserialize_pageBlockTable($stream),
'ááŠš' => [
'_' => 'pageBlockOrderedList',
'items' => self::deserialize_type_array_of_PageListOrderedItem(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'í‹vv' => self::deserialize_pageBlockDetails($stream),
'–Z' => [
'_' => 'pageBlockRelatedArticles',
'title' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
'articles' => self::deserialize_type_array_of_PageRelatedArticle(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ö>O¤' => [
'_' => 'pageBlockMap',
'geo' => match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => self::deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
,
'zoom' => unpack('l', stream_get_contents($stream, 4))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'caption' => match (stream_get_contents($stream, 4)) {
'Wvto' => [
'_' => 'pageCaption',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
'credit' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_PageCaption(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_PageBlock(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Å�q¨' => self::deserialize_pageBlockEmbed($stream),
'¨Yò' => [
'_' => 'pageBlockEmbedPost',
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
'author_photo_id' => unpack('q', stream_get_contents($stream, 8))[1],
'author' => self::deserialize_string($stream),
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'blocks' => $this->deserialize_type_array_of_PageBlock(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'caption' => match (stream_get_contents($stream, 4)) {
'Wvto' => [
'_' => 'pageCaption',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
'credit' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_PageCaption(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Mú e' => [
'_' => 'pageBlockCollage',
'items' => $this->deserialize_type_array_of_PageBlock(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'caption' => match (stream_get_contents($stream, 4)) {
'Wvto' => [
'_' => 'pageCaption',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
'credit' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_PageCaption(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'�•' => [
'_' => 'pageBlockSlideshow',
'items' => $this->deserialize_type_array_of_PageBlock(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'caption' => match (stream_get_contents($stream, 4)) {
'Wvto' => [
'_' => 'pageCaption',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
'credit' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_PageCaption(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'µQï' => [
'_' => 'pageBlockChannel',
'channel' => match (stream_get_contents($stream, 4)) {
'e(V)' => self::deserialize_chatEmpty($stream),
'VòËA' => self::deserialize_chat($stream),
'§¡’e' => self::deserialize_chatForbidden($stream),
'�ü­
' => self::deserialize_channel($stream),
'Õ“Ô' => self::deserialize_channelForbidden($stream),
'¡Ïr0' => self::deserialize_type_Chat(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'êaC€' => [
'_' => 'pageBlockAudio',
'audio_id' => unpack('q', stream_get_contents($stream, 8))[1],
'caption' => match (stream_get_contents($stream, 4)) {
'Wvto' => [
'_' => 'pageCaption',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
'credit' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_PageCaption(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'�ƒ' => [
'_' => 'pageBlockKicker',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'‚êM¿' => self::deserialize_pageBlockTable($stream),
'ááŠš' => [
'_' => 'pageBlockOrderedList',
'items' => self::deserialize_type_array_of_PageListOrderedItem(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'í‹vv' => self::deserialize_pageBlockDetails($stream),
'–Z' => [
'_' => 'pageBlockRelatedArticles',
'title' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
'articles' => self::deserialize_type_array_of_PageRelatedArticle(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ö>O¤' => [
'_' => 'pageBlockMap',
'geo' => match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => self::deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
,
'zoom' => unpack('l', stream_get_contents($stream, 4))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'caption' => match (stream_get_contents($stream, 4)) {
'Wvto' => [
'_' => 'pageCaption',
'text' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
'credit' => match (stream_get_contents($stream, 4)) {
'O‚=Ü' => [
'_' => 'textEmpty',
],
'à”Ft' => [
'_' => 'textPlain',
'text' => self::deserialize_string($stream),
],
'Ä«$g' => [
'_' => 'textBold',
'text' => self::deserialize_type_RichText($stream),
],
'œ¥Ù' => [
'_' => 'textItalic',
'text' => self::deserialize_type_RichText($stream),
],
'Ä"&Á' => [
'_' => 'textUnderline',
'text' => self::deserialize_type_RichText($stream),
],
'•»ø›' => [
'_' => 'textStrike',
'text' => self::deserialize_type_RichText($stream),
],
'¹?l' => [
'_' => 'textFixed',
'text' => self::deserialize_type_RichText($stream),
],
'Á„(<' => [
'_' => 'textUrl',
'text' => self::deserialize_type_RichText($stream),
'url' => self::deserialize_string($stream),
'webpage_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÖZÞ' => [
'_' => 'textEmail',
'text' => self::deserialize_type_RichText($stream),
'email' => self::deserialize_string($stream),
],
'×`b~' => [
'_' => 'textConcat',
'texts' => self::deserialize_type_array_of_RichText(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'…jí' => [
'_' => 'textSubscript',
'text' => self::deserialize_type_RichText($stream),
],
'^ûÇ' => [
'_' => 'textSuperscript',
'text' => self::deserialize_type_RichText($stream),
],
'!†K' => [
'_' => 'textMarked',
'text' => self::deserialize_type_RichText($stream),
],
'j–Ë' => [
'_' => 'textPhone',
'text' => self::deserialize_type_RichText($stream),
'phone' => self::deserialize_string($stream),
],
'OÏ' => [
'_' => 'textImage',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
],
'b7U5' => [
'_' => 'textAnchor',
'text' => self::deserialize_type_RichText($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_RichText(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_PageCaption(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_PageBlock(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_Photo(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'-²1#' => [
'_' => 'photoEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ezû' => self::deserialize_photo($stream),
'¡Ïr0' => self::deserialize_type_Photo(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_Document(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'qÈø6' => [
'_' => 'documentEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ØÄÔ�' => self::deserialize_document($stream),
'¡Ïr0' => self::deserialize_type_Document(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_page(mixed $stream): mixed {
$tmp = ['_' => 'page'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['part'] = ($flags & 1) !== 0;
$tmp['rtl'] = ($flags & 2) !== 0;
$tmp['v2'] = ($flags & 4) !== 0;
$tmp['url'] = self::deserialize_string($stream);
$tmp['blocks'] = self::deserialize_type_array_of_PageBlock(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['photos'] = self::deserialize_type_array_of_Photo(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['documents'] = self::deserialize_type_array_of_Document(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 8) !== 0) $tmp['views'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_Page(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'e˜' => self::deserialize_page($stream),
'¡Ïr0' => $this->deserialize_type_Page(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_BaseTheme(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'b$¡Ã' => [
'_' => 'baseThemeClassic',
],
'ˆØû' => [
'_' => 'baseThemeDay',
],
'¨³·' => [
'_' => 'baseThemeNight',
],
'îw_m' => [
'_' => 'baseThemeTinted',
],
'Z[' => [
'_' => 'baseThemeArctic',
],
'¡Ïr0' => $this->deserialize_type_BaseTheme(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_wallPaperSettings(mixed $stream): mixed {
$tmp = ['_' => 'wallPaperSettings'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['blur'] = ($flags & 2) !== 0;
$tmp['motion'] = ($flags & 4) !== 0;
if (($flags & 1) !== 0) $tmp['background_color'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 16) !== 0) $tmp['second_background_color'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 32) !== 0) $tmp['third_background_color'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 64) !== 0) $tmp['fourth_background_color'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 8) !== 0) $tmp['intensity'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 16) !== 0) $tmp['rotation'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 128) !== 0) $tmp['emoticon'] = self::deserialize_string($stream);
return $tmp;

    }
    private  function deserialize_type_WallPaperSettings(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'Ðü.7' => self::deserialize_wallPaperSettings($stream),
'¡Ïr0' => $this->deserialize_type_WallPaperSettings(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_wallPaper(mixed $stream): mixed {
$this->referenceDatabase?->addOriginContext('wallPaper');
$tmp = ['_' => 'wallPaper'];
$tmp['id'] = unpack('q', stream_get_contents($stream, 8))[1];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['creator'] = ($flags & 1) !== 0;
$tmp['default'] = ($flags & 2) !== 0;
$tmp['pattern'] = ($flags & 8) !== 0;
$tmp['dark'] = ($flags & 16) !== 0;
$tmp['access_hash'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['slug'] = self::deserialize_string($stream);
$tmp['document'] = match (stream_get_contents($stream, 4)) {
'qÈø6' => [
'_' => 'documentEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ØÄÔ�' => self::deserialize_document($stream),
'¡Ïr0' => self::deserialize_type_Document(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 4) !== 0) $tmp['settings'] = match (stream_get_contents($stream, 4)) {
'Ðü.7' => self::deserialize_wallPaperSettings($stream),
'¡Ïr0' => self::deserialize_type_WallPaperSettings(self::gzdecode($stream)),
default => self::err($stream)
}
;
$this->referenceDatabase?->addOrigin($tmp);
return $tmp;

    }
    private  function deserialize_wallPaperNoFile(mixed $stream): mixed {
$tmp = ['_' => 'wallPaperNoFile'];
$tmp['id'] = unpack('q', stream_get_contents($stream, 8))[1];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['default'] = ($flags & 2) !== 0;
$tmp['dark'] = ($flags & 16) !== 0;
if (($flags & 4) !== 0) $tmp['settings'] = match (stream_get_contents($stream, 4)) {
'Ðü.7' => self::deserialize_wallPaperSettings($stream),
'¡Ïr0' => self::deserialize_type_WallPaperSettings(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_type_WallPaper(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'íÃ7¤' => self::deserialize_wallPaper($stream),
'A€à' => self::deserialize_wallPaperNoFile($stream),
'¡Ïr0' => $this->deserialize_type_WallPaper(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_themeSettings(mixed $stream): mixed {
$tmp = ['_' => 'themeSettings'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['message_colors_animated'] = ($flags & 4) !== 0;
$tmp['base_theme'] = match (stream_get_contents($stream, 4)) {
'b$¡Ã' => [
'_' => 'baseThemeClassic',
],
'ˆØû' => [
'_' => 'baseThemeDay',
],
'¨³·' => [
'_' => 'baseThemeNight',
],
'îw_m' => [
'_' => 'baseThemeTinted',
],
'Z[' => [
'_' => 'baseThemeArctic',
],
'¡Ïr0' => self::deserialize_type_BaseTheme(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['accent_color'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 8) !== 0) $tmp['outbox_accent_color'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['message_colors'] = self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 2) !== 0) $tmp['wallpaper'] = match (stream_get_contents($stream, 4)) {
'íÃ7¤' => self::deserialize_wallPaper($stream),
'A€à' => self::deserialize_wallPaperNoFile($stream),
'¡Ïr0' => self::deserialize_type_WallPaper(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_type_ThemeSettings(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'Ô¶Xú' => self::deserialize_themeSettings($stream),
'¡Ïr0' => $this->deserialize_type_ThemeSettings(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_webPageAttributeTheme(mixed $stream): mixed {
$tmp = ['_' => 'webPageAttributeTheme'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['documents'] = self::deserialize_type_array_of_Document(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 2) !== 0) $tmp['settings'] = match (stream_get_contents($stream, 4)) {
'Ô¶Xú' => self::deserialize_themeSettings($stream),
'¡Ïr0' => self::deserialize_type_ThemeSettings(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_storyItemSkipped(mixed $stream): mixed {
$tmp = ['_' => 'storyItemSkipped'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['close_friends'] = ($flags & 256) !== 0;
$tmp['id'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['date'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['expire_date'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_storyFwdHeader(mixed $stream): mixed {
$tmp = ['_' => 'storyFwdHeader'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['modified'] = ($flags & 8) !== 0;
if (($flags & 1) !== 0) $tmp['from'] = match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 2) !== 0) $tmp['from_name'] = self::deserialize_string($stream);
if (($flags & 4) !== 0) $tmp['story_id'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_StoryFwdHeader(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'Pá&¸' => self::deserialize_storyFwdHeader($stream),
'¡Ïr0' => $this->deserialize_type_StoryFwdHeader(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_messageEntityMentionName(mixed $stream): mixed {
$tmp = [
'_' => 'messageEntityMentionName',
'offset' => unpack('l', stream_get_contents($stream, 4))[1],
'length' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
];
$this->minDatabase?->addPeer($tmp);
return $tmp;

    }
    private  function deserialize_type_InputUser(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'Ï†ˆ¹' => [
'_' => 'inputUserEmpty',
],
'?±Á÷' => [
'_' => 'inputUserSelf',
],
'ÆXò' => [
'_' => 'inputUser',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'âH¤' => [
'_' => 'inputUserFromMessage',
'peer' => match (stream_get_contents($stream, 4)) {
'ê;' => [
'_' => 'inputPeerEmpty',
],
'É~ }' => [
'_' => 'inputPeerSelf',
],
'¹\\©5' => [
'_' => 'inputPeerChat',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'L¥èÝ' => [
'_' => 'inputPeerUser',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ü»¼\'' => [
'_' => 'inputPeerChannel',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'
{¨' => [
'_' => 'inputPeerUserFromMessage',
'peer' => self::deserialize_type_InputPeer($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'@*½' => [
'_' => 'inputPeerChannelFromMessage',
'peer' => self::deserialize_type_InputPeer($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputPeer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => $this->deserialize_type_InputUser(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_array_of_MessageEntity(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'•º’»' => [
'_' => 'messageEntityUnknown',
'offset' => unpack('l', stream_get_contents($stream, 4))[1],
'length' => unpack('l', stream_get_contents($stream, 4))[1],
],
'�Wú' => [
'_' => 'messageEntityMention',
'offset' => unpack('l', stream_get_contents($stream, 4))[1],
'length' => unpack('l', stream_get_contents($stream, 4))[1],
],
'[co' => [
'_' => 'messageEntityHashtag',
'offset' => unpack('l', stream_get_contents($stream, 4))[1],
'length' => unpack('l', stream_get_contents($stream, 4))[1],
],
'ÇŠïl' => [
'_' => 'messageEntityBotCommand',
'offset' => unpack('l', stream_get_contents($stream, 4))[1],
'length' => unpack('l', stream_get_contents($stream, 4))[1],
],
'8%Ðn' => [
'_' => 'messageEntityUrl',
'offset' => unpack('l', stream_get_contents($stream, 4))[1],
'length' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Âuäd' => [
'_' => 'messageEntityEmail',
'offset' => unpack('l', stream_get_contents($stream, 4))[1],
'length' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Éa½' => [
'_' => 'messageEntityBold',
'offset' => unpack('l', stream_get_contents($stream, 4))[1],
'length' => unpack('l', stream_get_contents($stream, 4))[1],
],
'`‹o‚' => [
'_' => 'messageEntityItalic',
'offset' => unpack('l', stream_get_contents($stream, 4))[1],
'length' => unpack('l', stream_get_contents($stream, 4))[1],
],
'q¢(' => [
'_' => 'messageEntityCode',
'offset' => unpack('l', stream_get_contents($stream, 4))[1],
'length' => unpack('l', stream_get_contents($stream, 4))[1],
],
'àK’s' => [
'_' => 'messageEntityPre',
'offset' => unpack('l', stream_get_contents($stream, 4))[1],
'length' => unpack('l', stream_get_contents($stream, 4))[1],
'language' => self::deserialize_string($stream),
],
'\'Ó¦v' => [
'_' => 'messageEntityTextUrl',
'offset' => unpack('l', stream_get_contents($stream, 4))[1],
'length' => unpack('l', stream_get_contents($stream, 4))[1],
'url' => self::deserialize_string($stream),
],
'@{Ü' => self::deserialize_messageEntityMentionName($stream),
'ÉhŽ ' => [
'_' => 'inputMessageEntityMentionName',
'offset' => unpack('l', stream_get_contents($stream, 4))[1],
'length' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => match (stream_get_contents($stream, 4)) {
'Ï†ˆ¹' => [
'_' => 'inputUserEmpty',
],
'?±Á÷' => [
'_' => 'inputUserSelf',
],
'ÆXò' => [
'_' => 'inputUser',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'âH¤' => [
'_' => 'inputUserFromMessage',
'peer' => match (stream_get_contents($stream, 4)) {
'ê;' => [
'_' => 'inputPeerEmpty',
],
'É~ }' => [
'_' => 'inputPeerSelf',
],
'¹\\©5' => [
'_' => 'inputPeerChat',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'L¥èÝ' => [
'_' => 'inputPeerUser',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ü»¼\'' => [
'_' => 'inputPeerChannel',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'
{¨' => [
'_' => 'inputPeerUserFromMessage',
'peer' => self::deserialize_type_InputPeer($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'@*½' => [
'_' => 'inputPeerChannelFromMessage',
'peer' => self::deserialize_type_InputPeer($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputPeer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputUser(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Kãi›' => [
'_' => 'messageEntityPhone',
'offset' => unpack('l', stream_get_contents($stream, 4))[1],
'length' => unpack('l', stream_get_contents($stream, 4))[1],
],
'?tNL' => [
'_' => 'messageEntityCashtag',
'offset' => unpack('l', stream_get_contents($stream, 4))[1],
'length' => unpack('l', stream_get_contents($stream, 4))[1],
],
'‹~Nœ' => [
'_' => 'messageEntityUnderline',
'offset' => unpack('l', stream_get_contents($stream, 4))[1],
'length' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Ô“¿' => [
'_' => 'messageEntityStrike',
'offset' => unpack('l', stream_get_contents($stream, 4))[1],
'length' => unpack('l', stream_get_contents($stream, 4))[1],
],
'ôjv' => [
'_' => 'messageEntityBankCard',
'offset' => unpack('l', stream_get_contents($stream, 4))[1],
'length' => unpack('l', stream_get_contents($stream, 4))[1],
],
'–Ê2' => [
'_' => 'messageEntitySpoiler',
'offset' => unpack('l', stream_get_contents($stream, 4))[1],
'length' => unpack('l', stream_get_contents($stream, 4))[1],
],
'øÏÈ' => [
'_' => 'messageEntityCustomEmoji',
'offset' => unpack('l', stream_get_contents($stream, 4))[1],
'length' => unpack('l', stream_get_contents($stream, 4))[1],
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'Ðõ' => [
'_' => 'messageEntityBlockquote',
'offset' => unpack('l', stream_get_contents($stream, 4))[1],
'length' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => $this->deserialize_type_MessageEntity(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_webPage(mixed $stream): mixed {
$tmp = ['_' => 'webPage'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['has_large_media'] = ($flags & 8192) !== 0;
$tmp['id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['url'] = self::deserialize_string($stream);
$tmp['display_url'] = self::deserialize_string($stream);
$tmp['hash'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['type'] = self::deserialize_string($stream);
if (($flags & 2) !== 0) $tmp['site_name'] = self::deserialize_string($stream);
if (($flags & 4) !== 0) $tmp['title'] = self::deserialize_string($stream);
if (($flags & 8) !== 0) $tmp['description'] = self::deserialize_string($stream);
if (($flags & 16) !== 0) $tmp['photo'] = match (stream_get_contents($stream, 4)) {
'-²1#' => [
'_' => 'photoEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ezû' => self::deserialize_photo($stream),
'¡Ïr0' => self::deserialize_type_Photo(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 32) !== 0) $tmp['embed_url'] = self::deserialize_string($stream);
if (($flags & 32) !== 0) $tmp['embed_type'] = self::deserialize_string($stream);
if (($flags & 64) !== 0) $tmp['embed_width'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 64) !== 0) $tmp['embed_height'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 128) !== 0) $tmp['duration'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 256) !== 0) $tmp['author'] = self::deserialize_string($stream);
if (($flags & 512) !== 0) $tmp['document'] = match (stream_get_contents($stream, 4)) {
'qÈø6' => [
'_' => 'documentEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ØÄÔ�' => self::deserialize_document($stream),
'¡Ïr0' => self::deserialize_type_Document(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 1024) !== 0) $tmp['cached_page'] = match (stream_get_contents($stream, 4)) {
'e˜' => self::deserialize_page($stream),
'¡Ïr0' => self::deserialize_type_Page(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 4096) !== 0) $tmp['attributes'] = $this->deserialize_type_array_of_WebPageAttribute(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
return $tmp;

    }
    private  function deserialize_webPageNotModified(mixed $stream): mixed {
$tmp = ['_' => 'webPageNotModified'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['cached_page_views'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_messageMediaWebPage(mixed $stream): mixed {
$tmp = ['_' => 'messageMediaWebPage'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['force_large_media'] = ($flags & 1) !== 0;
$tmp['force_small_media'] = ($flags & 2) !== 0;
$tmp['manual'] = ($flags & 8) !== 0;
$tmp['safe'] = ($flags & 16) !== 0;
$tmp['webpage'] = match (stream_get_contents($stream, 4)) {
'ˆ!' => self::deserialize_webPageEmpty($stream),
'G>Ñ°' => self::deserialize_webPagePending($stream),
'²Eœè' => self::deserialize_webPage($stream),
'Ês' => self::deserialize_webPageNotModified($stream),
'¡Ïr0' => $this->deserialize_type_WebPage(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_game(mixed $stream): mixed {
$tmp = ['_' => 'game'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['access_hash'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['short_name'] = self::deserialize_string($stream);
$tmp['title'] = self::deserialize_string($stream);
$tmp['description'] = self::deserialize_string($stream);
$tmp['photo'] = match (stream_get_contents($stream, 4)) {
'-²1#' => [
'_' => 'photoEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ezû' => self::deserialize_photo($stream),
'¡Ïr0' => self::deserialize_type_Photo(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 1) !== 0) $tmp['document'] = match (stream_get_contents($stream, 4)) {
'qÈø6' => [
'_' => 'documentEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ØÄÔ�' => self::deserialize_document($stream),
'¡Ïr0' => self::deserialize_type_Document(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_type_Game(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
';eù½' => self::deserialize_game($stream),
'¡Ïr0' => $this->deserialize_type_Game(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_WebDocument(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'ÑW' => [
'_' => 'webDocument',
'url' => self::deserialize_string($stream),
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'attributes' => self::deserialize_type_array_of_DocumentAttribute(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'Æ¼Èù' => [
'_' => 'webDocumentNoProxy',
'url' => self::deserialize_string($stream),
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'attributes' => self::deserialize_type_array_of_DocumentAttribute(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => $this->deserialize_type_WebDocument(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_PhotoSize(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'<â' => [
'_' => 'photoSizeEmpty',
'type' => self::deserialize_string($stream),
],
'`ŽÇu' => [
'_' => 'photoSize',
'type' => self::deserialize_string($stream),
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Ö' => [
'_' => 'photoCachedSize',
'type' => self::deserialize_string($stream),
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'bytes' => self::deserialize_bytes($stream),
],
'.¼°à' => [
'_' => 'photoStrippedSize',
'type' => self::deserialize_string($stream),
'inflated' => new Types\Bytes(Tools::inflateStripped(self::deserialize_string($stream))),
],
'•û>ú' => [
'_' => 'photoSizeProgressive',
'type' => self::deserialize_string($stream),
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'sizes' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'AM!Ø' => [
'_' => 'photoPathSize',
'type' => self::deserialize_string($stream),
'bytes' => self::deserialize_bytes($stream),
],
'¡Ïr0' => $this->deserialize_type_PhotoSize(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_messageExtendedMediaPreview(mixed $stream): mixed {
$tmp = ['_' => 'messageExtendedMediaPreview'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['w'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['h'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 2) !== 0) $tmp['thumb'] = match (stream_get_contents($stream, 4)) {
'<â' => [
'_' => 'photoSizeEmpty',
'type' => self::deserialize_string($stream),
],
'`ŽÇu' => [
'_' => 'photoSize',
'type' => self::deserialize_string($stream),
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Ö' => [
'_' => 'photoCachedSize',
'type' => self::deserialize_string($stream),
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'bytes' => self::deserialize_bytes($stream),
],
'.¼°à' => [
'_' => 'photoStrippedSize',
'type' => self::deserialize_string($stream),
'inflated' => new Types\Bytes(Tools::inflateStripped(self::deserialize_string($stream))),
],
'•û>ú' => [
'_' => 'photoSizeProgressive',
'type' => self::deserialize_string($stream),
'w' => unpack('l', stream_get_contents($stream, 4))[1],
'h' => unpack('l', stream_get_contents($stream, 4))[1],
'sizes' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'AM!Ø' => [
'_' => 'photoPathSize',
'type' => self::deserialize_string($stream),
'bytes' => self::deserialize_bytes($stream),
],
'¡Ïr0' => self::deserialize_type_PhotoSize(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 4) !== 0) $tmp['video_duration'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_MessageExtendedMedia(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'ÈŒb­' => self::deserialize_messageExtendedMediaPreview($stream),
'dœGî' => [
'_' => 'messageExtendedMedia',
'media' => $this->deserialize_type_MessageMedia($stream),
],
'¡Ïr0' => $this->deserialize_type_MessageExtendedMedia(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_messageMediaInvoice(mixed $stream): mixed {
$tmp = ['_' => 'messageMediaInvoice'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['shipping_address_requested'] = ($flags & 2) !== 0;
$tmp['test'] = ($flags & 8) !== 0;
$tmp['title'] = self::deserialize_string($stream);
$tmp['description'] = self::deserialize_string($stream);
if (($flags & 1) !== 0) $tmp['photo'] = match (stream_get_contents($stream, 4)) {
'ÑW' => [
'_' => 'webDocument',
'url' => self::deserialize_string($stream),
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'attributes' => self::deserialize_type_array_of_DocumentAttribute(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'Æ¼Èù' => [
'_' => 'webDocumentNoProxy',
'url' => self::deserialize_string($stream),
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'attributes' => self::deserialize_type_array_of_DocumentAttribute(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => self::deserialize_type_WebDocument(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 4) !== 0) $tmp['receipt_msg_id'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['currency'] = self::deserialize_string($stream);
$tmp['total_amount'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['start_param'] = self::deserialize_string($stream);
if (($flags & 16) !== 0) $tmp['extended_media'] = match (stream_get_contents($stream, 4)) {
'ÈŒb­' => self::deserialize_messageExtendedMediaPreview($stream),
'dœGî' => [
'_' => 'messageExtendedMedia',
'media' => $this->deserialize_type_MessageMedia($stream),
],
'¡Ïr0' => self::deserialize_type_MessageExtendedMedia(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_messageMediaGeoLive(mixed $stream): mixed {
$tmp = ['_' => 'messageMediaGeoLive'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['geo'] = match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => self::deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 1) !== 0) $tmp['heading'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['period'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 2) !== 0) $tmp['proximity_notification_radius'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_array_of_PollAnswer(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'éÂ©l' => [
'_' => 'pollAnswer',
'text' => self::deserialize_string($stream),
'option' => self::deserialize_bytes($stream),
],
'¡Ïr0' => $this->deserialize_type_PollAnswer(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_poll(mixed $stream): mixed {
$tmp = ['_' => 'poll'];
$tmp['id'] = unpack('q', stream_get_contents($stream, 8))[1];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['closed'] = ($flags & 1) !== 0;
$tmp['public_voters'] = ($flags & 2) !== 0;
$tmp['multiple_choice'] = ($flags & 4) !== 0;
$tmp['quiz'] = ($flags & 8) !== 0;
$tmp['question'] = self::deserialize_string($stream);
$tmp['answers'] = self::deserialize_type_array_of_PollAnswer(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 16) !== 0) $tmp['close_period'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 32) !== 0) $tmp['close_date'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_Poll(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'a�á†' => self::deserialize_poll($stream),
'¡Ïr0' => $this->deserialize_type_Poll(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_pollAnswerVoters(mixed $stream): mixed {
$tmp = ['_' => 'pollAnswerVoters'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['chosen'] = ($flags & 1) !== 0;
$tmp['correct'] = ($flags & 2) !== 0;
$tmp['option'] = self::deserialize_bytes($stream);
$tmp['voters'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_array_of_PollAnswerVoters(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'ÒÚm;' => self::deserialize_pollAnswerVoters($stream),
'¡Ïr0' => $this->deserialize_type_PollAnswerVoters(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_Peer(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_pollResults(mixed $stream): mixed {
$tmp = ['_' => 'pollResults'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['min'] = ($flags & 1) !== 0;
if (($flags & 2) !== 0) $tmp['results'] = self::deserialize_type_array_of_PollAnswerVoters(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 4) !== 0) $tmp['total_voters'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 8) !== 0) $tmp['recent_voters'] = self::deserialize_type_array_of_Peer(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 16) !== 0) $tmp['solution'] = self::deserialize_string($stream);
if (($flags & 16) !== 0) $tmp['solution_entities'] = self::deserialize_type_array_of_MessageEntity(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
return $tmp;

    }
    private  function deserialize_type_PollResults(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
' $ßz' => self::deserialize_pollResults($stream),
'¡Ïr0' => $this->deserialize_type_PollResults(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_MediaAreaCoordinates(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'NêÑ' => [
'_' => 'mediaAreaCoordinates',
'x' => unpack('d', stream_get_contents($stream, 8))[1],
'y' => unpack('d', stream_get_contents($stream, 8))[1],
'w' => unpack('d', stream_get_contents($stream, 8))[1],
'h' => unpack('d', stream_get_contents($stream, 8))[1],
'rotation' => unpack('d', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => $this->deserialize_type_MediaAreaCoordinates(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_Reaction(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'Ôõy' => [
'_' => 'reactionEmpty',
],
'¸†"' => [
'_' => 'reactionEmoji',
'emoticon' => self::deserialize_string($stream),
],
'sü5‰' => [
'_' => 'reactionCustomEmoji',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => $this->deserialize_type_Reaction(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_mediaAreaSuggestedReaction(mixed $stream): mixed {
$tmp = ['_' => 'mediaAreaSuggestedReaction'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['dark'] = ($flags & 1) !== 0;
$tmp['flipped'] = ($flags & 2) !== 0;
$tmp['coordinates'] = match (stream_get_contents($stream, 4)) {
'NêÑ' => [
'_' => 'mediaAreaCoordinates',
'x' => unpack('d', stream_get_contents($stream, 8))[1],
'y' => unpack('d', stream_get_contents($stream, 8))[1],
'w' => unpack('d', stream_get_contents($stream, 8))[1],
'h' => unpack('d', stream_get_contents($stream, 8))[1],
'rotation' => unpack('d', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_MediaAreaCoordinates(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['reaction'] = match (stream_get_contents($stream, 4)) {
'Ôõy' => [
'_' => 'reactionEmpty',
],
'¸†"' => [
'_' => 'reactionEmoji',
'emoticon' => self::deserialize_string($stream),
],
'sü5‰' => [
'_' => 'reactionCustomEmoji',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_Reaction(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_type_array_of_MediaArea(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'œÛ‚¾' => [
'_' => 'mediaAreaVenue',
'coordinates' => match (stream_get_contents($stream, 4)) {
'NêÑ' => [
'_' => 'mediaAreaCoordinates',
'x' => unpack('d', stream_get_contents($stream, 8))[1],
'y' => unpack('d', stream_get_contents($stream, 8))[1],
'w' => unpack('d', stream_get_contents($stream, 8))[1],
'h' => unpack('d', stream_get_contents($stream, 8))[1],
'rotation' => unpack('d', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_MediaAreaCoordinates(self::gzdecode($stream)),
default => self::err($stream)
}
,
'geo' => match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => self::deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
,
'title' => self::deserialize_string($stream),
'address' => self::deserialize_string($stream),
'provider' => self::deserialize_string($stream),
'venue_id' => self::deserialize_string($stream),
'venue_type' => self::deserialize_string($stream),
],
'!‚²' => [
'_' => 'inputMediaAreaVenue',
'coordinates' => match (stream_get_contents($stream, 4)) {
'NêÑ' => [
'_' => 'mediaAreaCoordinates',
'x' => unpack('d', stream_get_contents($stream, 8))[1],
'y' => unpack('d', stream_get_contents($stream, 8))[1],
'w' => unpack('d', stream_get_contents($stream, 8))[1],
'h' => unpack('d', stream_get_contents($stream, 8))[1],
'rotation' => unpack('d', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_MediaAreaCoordinates(self::gzdecode($stream)),
default => self::err($stream)
}
,
'query_id' => unpack('q', stream_get_contents($stream, 8))[1],
'result_id' => self::deserialize_string($stream),
],
'";‹ß' => [
'_' => 'mediaAreaGeoPoint',
'coordinates' => match (stream_get_contents($stream, 4)) {
'NêÑ' => [
'_' => 'mediaAreaCoordinates',
'x' => unpack('d', stream_get_contents($stream, 8))[1],
'y' => unpack('d', stream_get_contents($stream, 8))[1],
'w' => unpack('d', stream_get_contents($stream, 8))[1],
'h' => unpack('d', stream_get_contents($stream, 8))[1],
'rotation' => unpack('d', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_MediaAreaCoordinates(self::gzdecode($stream)),
default => self::err($stream)
}
,
'geo' => match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => self::deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'qXE' => self::deserialize_mediaAreaSuggestedReaction($stream),
'¯w' => [
'_' => 'mediaAreaChannelPost',
'coordinates' => match (stream_get_contents($stream, 4)) {
'NêÑ' => [
'_' => 'mediaAreaCoordinates',
'x' => unpack('d', stream_get_contents($stream, 8))[1],
'y' => unpack('d', stream_get_contents($stream, 8))[1],
'w' => unpack('d', stream_get_contents($stream, 8))[1],
'h' => unpack('d', stream_get_contents($stream, 8))[1],
'rotation' => unpack('d', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_MediaAreaCoordinates(self::gzdecode($stream)),
default => self::err($stream)
}
,
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¿òq"' => [
'_' => 'inputMediaAreaChannelPost',
'coordinates' => match (stream_get_contents($stream, 4)) {
'NêÑ' => [
'_' => 'mediaAreaCoordinates',
'x' => unpack('d', stream_get_contents($stream, 8))[1],
'y' => unpack('d', stream_get_contents($stream, 8))[1],
'w' => unpack('d', stream_get_contents($stream, 8))[1],
'h' => unpack('d', stream_get_contents($stream, 8))[1],
'rotation' => unpack('d', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_MediaAreaCoordinates(self::gzdecode($stream)),
default => self::err($stream)
}
,
'channel' => match (stream_get_contents($stream, 4)) {
'†Œî' => [
'_' => 'inputChannelEmpty',
],
'(ìZó' => [
'_' => 'inputChannel',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'�O“[' => [
'_' => 'inputChannelFromMessage',
'peer' => match (stream_get_contents($stream, 4)) {
'ê;' => [
'_' => 'inputPeerEmpty',
],
'É~ }' => [
'_' => 'inputPeerSelf',
],
'¹\\©5' => [
'_' => 'inputPeerChat',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'L¥èÝ' => [
'_' => 'inputPeerUser',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ü»¼\'' => [
'_' => 'inputPeerChannel',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'
{¨' => [
'_' => 'inputPeerUserFromMessage',
'peer' => self::deserialize_type_InputPeer($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'@*½' => [
'_' => 'inputPeerChannelFromMessage',
'peer' => self::deserialize_type_InputPeer($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputPeer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputChannel(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => $this->deserialize_type_MediaArea(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_PrivacyRule(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'¬þÿ' => [
'_' => 'privacyValueAllowContacts',
],
'‚{Be' => [
'_' => 'privacyValueAllowAll',
],
'²_�¸' => [
'_' => 'privacyValueAllowUsers',
'users' => self::deserialize_type_array_of_long(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'úˆø' => [
'_' => 'privacyValueDisallowContacts',
],
'cçs‹' => [
'_' => 'privacyValueDisallowAll',
],
'Abä' => [
'_' => 'privacyValueDisallowUsers',
'users' => self::deserialize_type_array_of_long(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ŽNk' => [
'_' => 'privacyValueAllowChatParticipants',
'chats' => self::deserialize_type_array_of_long(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'euÈA' => [
'_' => 'privacyValueDisallowChatParticipants',
'chats' => self::deserialize_type_array_of_long(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'›Øè÷' => [
'_' => 'privacyValueAllowCloseFriends',
],
'¡Ïr0' => $this->deserialize_type_PrivacyRule(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_reactionCount(mixed $stream): mixed {
$tmp = ['_' => 'reactionCount'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['chosen_order'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['reaction'] = match (stream_get_contents($stream, 4)) {
'Ôõy' => [
'_' => 'reactionEmpty',
],
'¸†"' => [
'_' => 'reactionEmoji',
'emoticon' => self::deserialize_string($stream),
],
'sü5‰' => [
'_' => 'reactionCustomEmoji',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_Reaction(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['count'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_array_of_ReactionCount(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'€ËÑ£' => self::deserialize_reactionCount($stream),
'¡Ïr0' => $this->deserialize_type_ReactionCount(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_storyViews(mixed $stream): mixed {
$tmp = ['_' => 'storyViews'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['has_viewers'] = ($flags & 2) !== 0;
$tmp['views_count'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 4) !== 0) $tmp['forwards_count'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 8) !== 0) $tmp['reactions'] = self::deserialize_type_array_of_ReactionCount(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 16) !== 0) $tmp['reactions_count'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['recent_viewers'] = self::deserialize_type_array_of_long(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
return $tmp;

    }
    private  function deserialize_type_StoryViews(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'Ö\\Y�' => self::deserialize_storyViews($stream),
'¡Ïr0' => $this->deserialize_type_StoryViews(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_storyItem(mixed $stream): mixed {
$tmp = ['_' => 'storyItem'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['pinned'] = ($flags & 32) !== 0;
$tmp['public'] = ($flags & 128) !== 0;
$tmp['close_friends'] = ($flags & 256) !== 0;
$tmp['min'] = ($flags & 512) !== 0;
$tmp['noforwards'] = ($flags & 1024) !== 0;
$tmp['edited'] = ($flags & 2048) !== 0;
$tmp['contacts'] = ($flags & 4096) !== 0;
$tmp['selected_contacts'] = ($flags & 8192) !== 0;
$tmp['out'] = ($flags & 65536) !== 0;
$tmp['id'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['date'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 262144) !== 0) $tmp['from_id'] = match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 131072) !== 0) $tmp['fwd_from'] = match (stream_get_contents($stream, 4)) {
'Pá&¸' => self::deserialize_storyFwdHeader($stream),
'¡Ïr0' => self::deserialize_type_StoryFwdHeader(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['expire_date'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['caption'] = self::deserialize_string($stream);
if (($flags & 2) !== 0) $tmp['entities'] = self::deserialize_type_array_of_MessageEntity(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['media'] = $this->deserialize_type_MessageMedia($stream);
if (($flags & 16384) !== 0) $tmp['media_areas'] = self::deserialize_type_array_of_MediaArea(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 4) !== 0) $tmp['privacy'] = self::deserialize_type_array_of_PrivacyRule(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 8) !== 0) $tmp['views'] = match (stream_get_contents($stream, 4)) {
'Ö\\Y�' => self::deserialize_storyViews($stream),
'¡Ïr0' => self::deserialize_type_StoryViews(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 32768) !== 0) $tmp['sent_reaction'] = match (stream_get_contents($stream, 4)) {
'Ôõy' => [
'_' => 'reactionEmpty',
],
'¸†"' => [
'_' => 'reactionEmoji',
'emoticon' => self::deserialize_string($stream),
],
'sü5‰' => [
'_' => 'reactionCustomEmoji',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_Reaction(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_messageMediaStory(mixed $stream): mixed {
$tmp = ['_' => 'messageMediaStory'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['via_mention'] = ($flags & 2) !== 0;
$tmp['peer'] = match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['id'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['story'] = match (stream_get_contents($stream, 4)) {
'OîæQ' => [
'_' => 'storyItemDeleted',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'É­ÿ' => self::deserialize_storyItemSkipped($stream),
'$j²y' => self::deserialize_storyItem($stream),
'¡Ïr0' => $this->deserialize_type_StoryItem(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_messageMediaGiveaway(mixed $stream): mixed {
$tmp = ['_' => 'messageMediaGiveaway'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['only_new_subscribers'] = ($flags & 1) !== 0;
$tmp['winners_are_visible'] = ($flags & 4) !== 0;
$tmp['channels'] = self::deserialize_type_array_of_long(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 2) !== 0) $tmp['countries_iso2'] = self::deserialize_type_array_of_string(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 8) !== 0) $tmp['prize_description'] = self::deserialize_string($stream);
$tmp['quantity'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['months'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['until_date'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_messageMediaGiveawayResults(mixed $stream): mixed {
$tmp = ['_' => 'messageMediaGiveawayResults'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['only_new_subscribers'] = ($flags & 1) !== 0;
$tmp['refunded'] = ($flags & 4) !== 0;
$tmp['channel_id'] = unpack('q', stream_get_contents($stream, 8))[1];
if (($flags & 8) !== 0) $tmp['additional_peers_count'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['launch_msg_id'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['winners_count'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['unclaimed_count'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['winners'] = self::deserialize_type_array_of_long(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['months'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 2) !== 0) $tmp['prize_description'] = self::deserialize_string($stream);
$tmp['until_date'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_StoryItem(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'OîæQ' => [
'_' => 'storyItemDeleted',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'É­ÿ' => self::deserialize_storyItemSkipped($stream),
'$j²y' => self::deserialize_storyItem($stream),
'¡Ïr0' => $this->deserialize_type_StoryItem(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_webPageAttributeStory(mixed $stream): mixed {
$tmp = ['_' => 'webPageAttributeStory'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['peer'] = match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['id'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['story'] = match (stream_get_contents($stream, 4)) {
'OîæQ' => [
'_' => 'storyItemDeleted',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'É­ÿ' => self::deserialize_storyItemSkipped($stream),
'$j²y' => self::deserialize_storyItem($stream),
'¡Ïr0' => self::deserialize_type_StoryItem(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_type_array_of_WebPageAttribute(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'fµT' => self::deserialize_webPageAttributeTheme($stream),
'çÃ”.' => self::deserialize_webPageAttributeStory($stream),
'¡Ïr0' => $this->deserialize_type_WebPageAttribute(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_WebPage(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'ˆ!' => self::deserialize_webPageEmpty($stream),
'G>Ñ°' => self::deserialize_webPagePending($stream),
'²Eœè' => self::deserialize_webPage($stream),
'Ês' => self::deserialize_webPageNotModified($stream),
'¡Ïr0' => $this->deserialize_type_WebPage(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_MessageMedia(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
' cí=' => [
'_' => 'messageMediaEmpty',
],
'×PQi' => self::deserialize_messageMediaPhoto($stream),
'tÔàV' => [
'_' => 'messageMediaGeo',
'geo' => match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => self::deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'I)2p' => [
'_' => 'messageMediaContact',
'phone_number' => self::deserialize_string($stream),
'first_name' => self::deserialize_string($stream),
'last_name' => self::deserialize_string($stream),
'vcard' => self::deserialize_string($stream),
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'žô„Ÿ' => [
'_' => 'messageMediaUnsupported',
],
'-×ôL' => self::deserialize_messageMediaDocument($stream),
';ñÝ' => self::deserialize_messageMediaWebPage($stream),
'?SÀ.' => [
'_' => 'messageMediaVenue',
'geo' => match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => self::deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
,
'title' => self::deserialize_string($stream),
'address' => self::deserialize_string($stream),
'provider' => self::deserialize_string($stream),
'venue_id' => self::deserialize_string($stream),
'venue_type' => self::deserialize_string($stream),
],
'�±ý' => [
'_' => 'messageMediaGame',
'game' => match (stream_get_contents($stream, 4)) {
';eù½' => self::deserialize_game($stream),
'¡Ïr0' => self::deserialize_type_Game(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ÓH¥ö' => self::deserialize_messageMediaInvoice($stream),
'fÆ@¹' => self::deserialize_messageMediaGeoLive($stream),
'˜çÖK' => [
'_' => 'messageMediaPoll',
'poll' => match (stream_get_contents($stream, 4)) {
'a�á†' => self::deserialize_poll($stream),
'¡Ïr0' => self::deserialize_type_Poll(self::gzdecode($stream)),
default => self::err($stream)
}
,
'results' => match (stream_get_contents($stream, 4)) {
' $ßz' => self::deserialize_pollResults($stream),
'¡Ïr0' => self::deserialize_type_PollResults(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'‹å~?' => [
'_' => 'messageMediaDice',
'value' => unpack('l', stream_get_contents($stream, 4))[1],
'emoticon' => self::deserialize_string($stream),
],
'ƒbËh' => self::deserialize_messageMediaStory($stream),
'°…­Ú' => self::deserialize_messageMediaGiveaway($stream),
'h™Æ' => self::deserialize_messageMediaGiveawayResults($stream),
'¡Ïr0' => $this->deserialize_type_MessageMedia(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_messageReplyHeader(mixed $stream): mixed {
$tmp = ['_' => 'messageReplyHeader'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['reply_to_scheduled'] = ($flags & 4) !== 0;
$tmp['forum_topic'] = ($flags & 8) !== 0;
$tmp['quote'] = ($flags & 512) !== 0;
if (($flags & 16) !== 0) $tmp['reply_to_msg_id'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['reply_to_peer_id'] = match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 32) !== 0) $tmp['reply_from'] = match (stream_get_contents($stream, 4)) {
'»ôMN' => self::deserialize_messageFwdHeader($stream),
'¡Ïr0' => self::deserialize_type_MessageFwdHeader(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 256) !== 0) $tmp['reply_media'] = match (stream_get_contents($stream, 4)) {
' cí=' => [
'_' => 'messageMediaEmpty',
],
'×PQi' => self::deserialize_messageMediaPhoto($stream),
'tÔàV' => [
'_' => 'messageMediaGeo',
'geo' => match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => self::deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'I)2p' => [
'_' => 'messageMediaContact',
'phone_number' => self::deserialize_string($stream),
'first_name' => self::deserialize_string($stream),
'last_name' => self::deserialize_string($stream),
'vcard' => self::deserialize_string($stream),
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'žô„Ÿ' => [
'_' => 'messageMediaUnsupported',
],
'-×ôL' => self::deserialize_messageMediaDocument($stream),
';ñÝ' => self::deserialize_messageMediaWebPage($stream),
'?SÀ.' => [
'_' => 'messageMediaVenue',
'geo' => match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => self::deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
,
'title' => self::deserialize_string($stream),
'address' => self::deserialize_string($stream),
'provider' => self::deserialize_string($stream),
'venue_id' => self::deserialize_string($stream),
'venue_type' => self::deserialize_string($stream),
],
'�±ý' => [
'_' => 'messageMediaGame',
'game' => match (stream_get_contents($stream, 4)) {
';eù½' => self::deserialize_game($stream),
'¡Ïr0' => self::deserialize_type_Game(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ÓH¥ö' => self::deserialize_messageMediaInvoice($stream),
'fÆ@¹' => self::deserialize_messageMediaGeoLive($stream),
'˜çÖK' => [
'_' => 'messageMediaPoll',
'poll' => match (stream_get_contents($stream, 4)) {
'a�á†' => self::deserialize_poll($stream),
'¡Ïr0' => self::deserialize_type_Poll(self::gzdecode($stream)),
default => self::err($stream)
}
,
'results' => match (stream_get_contents($stream, 4)) {
' $ßz' => self::deserialize_pollResults($stream),
'¡Ïr0' => self::deserialize_type_PollResults(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'‹å~?' => [
'_' => 'messageMediaDice',
'value' => unpack('l', stream_get_contents($stream, 4))[1],
'emoticon' => self::deserialize_string($stream),
],
'ƒbËh' => self::deserialize_messageMediaStory($stream),
'°…­Ú' => self::deserialize_messageMediaGiveaway($stream),
'h™Æ' => self::deserialize_messageMediaGiveawayResults($stream),
'¡Ïr0' => self::deserialize_type_MessageMedia(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 2) !== 0) $tmp['reply_to_top_id'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 64) !== 0) $tmp['quote_text'] = self::deserialize_string($stream);
if (($flags & 128) !== 0) $tmp['quote_entities'] = self::deserialize_type_array_of_MessageEntity(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 1024) !== 0) $tmp['quote_offset'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_MessageReplyHeader(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'Û	¼¯' => self::deserialize_messageReplyHeader($stream),
'9ùZ' => [
'_' => 'messageReplyStoryHeader',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'story_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => $this->deserialize_type_MessageReplyHeader(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_replyKeyboardHide(mixed $stream): mixed {
$tmp = ['_' => 'replyKeyboardHide'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['selective'] = ($flags & 4) !== 0;
return $tmp;

    }
    private  function deserialize_replyKeyboardForceReply(mixed $stream): mixed {
$tmp = ['_' => 'replyKeyboardForceReply'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['single_use'] = ($flags & 2) !== 0;
$tmp['selective'] = ($flags & 4) !== 0;
if (($flags & 8) !== 0) $tmp['placeholder'] = self::deserialize_string($stream);
return $tmp;

    }
    private  function deserialize_keyboardButtonCallback(mixed $stream): mixed {
$tmp = ['_' => 'keyboardButtonCallback'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['requires_password'] = ($flags & 1) !== 0;
$tmp['text'] = self::deserialize_string($stream);
$tmp['data'] = self::deserialize_bytes($stream);
return $tmp;

    }
    private  function deserialize_type_array_of_InlineQueryPeerType(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'�í�0' => [
'_' => 'inlineQueryPeerTypeSameBotPM',
],
'¬<ƒ' => [
'_' => 'inlineQueryPeerTypePM',
],
'
Åf×' => [
'_' => 'inlineQueryPeerTypeChat',
],
'C¾Ä^' => [
'_' => 'inlineQueryPeerTypeMegagroup',
],
'šî4c' => [
'_' => 'inlineQueryPeerTypeBroadcast',
],
'-;' => [
'_' => 'inlineQueryPeerTypeBotPM',
],
'¡Ïr0' => $this->deserialize_type_InlineQueryPeerType(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_keyboardButtonSwitchInline(mixed $stream): mixed {
$tmp = ['_' => 'keyboardButtonSwitchInline'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['same_peer'] = ($flags & 1) !== 0;
$tmp['text'] = self::deserialize_string($stream);
$tmp['query'] = self::deserialize_string($stream);
if (($flags & 2) !== 0) $tmp['peer_types'] = self::deserialize_type_array_of_InlineQueryPeerType(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
return $tmp;

    }
    private  function deserialize_keyboardButtonUrlAuth(mixed $stream): mixed {
$tmp = ['_' => 'keyboardButtonUrlAuth'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['text'] = self::deserialize_string($stream);
if (($flags & 1) !== 0) $tmp['fwd_text'] = self::deserialize_string($stream);
$tmp['url'] = self::deserialize_string($stream);
$tmp['button_id'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_inputKeyboardButtonUrlAuth(mixed $stream): mixed {
$tmp = ['_' => 'inputKeyboardButtonUrlAuth'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['request_write_access'] = ($flags & 1) !== 0;
$tmp['text'] = self::deserialize_string($stream);
if (($flags & 2) !== 0) $tmp['fwd_text'] = self::deserialize_string($stream);
$tmp['url'] = self::deserialize_string($stream);
$tmp['bot'] = match (stream_get_contents($stream, 4)) {
'Ï†ˆ¹' => [
'_' => 'inputUserEmpty',
],
'?±Á÷' => [
'_' => 'inputUserSelf',
],
'ÆXò' => [
'_' => 'inputUser',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'âH¤' => [
'_' => 'inputUserFromMessage',
'peer' => match (stream_get_contents($stream, 4)) {
'ê;' => [
'_' => 'inputPeerEmpty',
],
'É~ }' => [
'_' => 'inputPeerSelf',
],
'¹\\©5' => [
'_' => 'inputPeerChat',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'L¥èÝ' => [
'_' => 'inputPeerUser',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ü»¼\'' => [
'_' => 'inputPeerChannel',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'
{¨' => [
'_' => 'inputPeerUserFromMessage',
'peer' => self::deserialize_type_InputPeer($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'@*½' => [
'_' => 'inputPeerChannelFromMessage',
'peer' => self::deserialize_type_InputPeer($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputPeer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputUser(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_keyboardButtonRequestPoll(mixed $stream): mixed {
$tmp = ['_' => 'keyboardButtonRequestPoll'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['quiz'] = match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) };
$tmp['text'] = self::deserialize_string($stream);
return $tmp;

    }
    private  function deserialize_requestPeerTypeUser(mixed $stream): mixed {
$tmp = ['_' => 'requestPeerTypeUser'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['bot'] = match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) };
if (($flags & 2) !== 0) $tmp['premium'] = match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) };
return $tmp;

    }
    private  function deserialize_requestPeerTypeChat(mixed $stream): mixed {
$tmp = ['_' => 'requestPeerTypeChat'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['creator'] = ($flags & 1) !== 0;
$tmp['bot_participant'] = ($flags & 32) !== 0;
if (($flags & 8) !== 0) $tmp['has_username'] = match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) };
if (($flags & 16) !== 0) $tmp['forum'] = match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) };
if (($flags & 2) !== 0) $tmp['user_admin_rights'] = match (stream_get_contents($stream, 4)) {
'Õ$²_' => self::deserialize_chatAdminRights($stream),
'¡Ïr0' => self::deserialize_type_ChatAdminRights(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 4) !== 0) $tmp['bot_admin_rights'] = match (stream_get_contents($stream, 4)) {
'Õ$²_' => self::deserialize_chatAdminRights($stream),
'¡Ïr0' => self::deserialize_type_ChatAdminRights(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_requestPeerTypeBroadcast(mixed $stream): mixed {
$tmp = ['_' => 'requestPeerTypeBroadcast'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['creator'] = ($flags & 1) !== 0;
if (($flags & 8) !== 0) $tmp['has_username'] = match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) };
if (($flags & 2) !== 0) $tmp['user_admin_rights'] = match (stream_get_contents($stream, 4)) {
'Õ$²_' => self::deserialize_chatAdminRights($stream),
'¡Ïr0' => self::deserialize_type_ChatAdminRights(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 4) !== 0) $tmp['bot_admin_rights'] = match (stream_get_contents($stream, 4)) {
'Õ$²_' => self::deserialize_chatAdminRights($stream),
'¡Ïr0' => self::deserialize_type_ChatAdminRights(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_type_RequestPeerType(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'' . "\0" . 'Š;_' => self::deserialize_requestPeerTypeUser($stream),
'nðÉ' => self::deserialize_requestPeerTypeChat($stream),
'lï›3' => self::deserialize_requestPeerTypeBroadcast($stream),
'¡Ïr0' => $this->deserialize_type_RequestPeerType(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_array_of_KeyboardButton(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'€Hú¢' => [
'_' => 'keyboardButton',
'text' => self::deserialize_string($stream),
],
'ÿŠ%' => [
'_' => 'keyboardButtonUrl',
'text' => self::deserialize_string($stream),
'url' => self::deserialize_string($stream),
],
'kÛ»5' => self::deserialize_keyboardButtonCallback($stream),
')lj±' => [
'_' => 'keyboardButtonRequestPhone',
'text' => self::deserialize_string($stream),
],
'?kyü' => [
'_' => 'keyboardButtonRequestGeoLocation',
'text' => self::deserialize_string($stream),
],
'µû¹“' => self::deserialize_keyboardButtonSwitchInline($stream),
'ÏôP' => [
'_' => 'keyboardButtonGame',
'text' => self::deserialize_string($stream),
],
'»?Ù¯' => [
'_' => 'keyboardButtonBuy',
'text' => self::deserialize_string($stream),
],
')�·' => self::deserialize_keyboardButtonUrlAuth($stream),
'Ô.Ð' => self::deserialize_inputKeyboardButtonUrlAuth($stream),
']QÇ»' => self::deserialize_keyboardButtonRequestPoll($stream),
'{ˆé' => [
'_' => 'inputKeyboardButtonUserProfile',
'text' => self::deserialize_string($stream),
'user_id' => match (stream_get_contents($stream, 4)) {
'Ï†ˆ¹' => [
'_' => 'inputUserEmpty',
],
'?±Á÷' => [
'_' => 'inputUserSelf',
],
'ÆXò' => [
'_' => 'inputUser',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'âH¤' => [
'_' => 'inputUserFromMessage',
'peer' => match (stream_get_contents($stream, 4)) {
'ê;' => [
'_' => 'inputPeerEmpty',
],
'É~ }' => [
'_' => 'inputPeerSelf',
],
'¹\\©5' => [
'_' => 'inputPeerChat',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'L¥èÝ' => [
'_' => 'inputPeerUser',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ü»¼\'' => [
'_' => 'inputPeerChannel',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'
{¨' => [
'_' => 'inputPeerUserFromMessage',
'peer' => self::deserialize_type_InputPeer($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'@*½' => [
'_' => 'inputPeerChannelFromMessage',
'peer' => self::deserialize_type_InputPeer($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputPeer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputUser(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Á`†0' => [
'_' => 'keyboardButtonUserProfile',
'text' => self::deserialize_string($stream),
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'0rv' => [
'_' => 'keyboardButtonWebView',
'text' => self::deserialize_string($stream),
'url' => self::deserialize_string($stream),
],
'\\PÀ ' => [
'_' => 'keyboardButtonSimpleWebView',
'text' => self::deserialize_string($stream),
'url' => self::deserialize_string($stream),
],
'Ø¿×S' => [
'_' => 'keyboardButtonRequestPeer',
'text' => self::deserialize_string($stream),
'button_id' => unpack('l', stream_get_contents($stream, 4))[1],
'peer_type' => match (stream_get_contents($stream, 4)) {
'' . "\0" . 'Š;_' => self::deserialize_requestPeerTypeUser($stream),
'nðÉ' => self::deserialize_requestPeerTypeChat($stream),
'lï›3' => self::deserialize_requestPeerTypeBroadcast($stream),
'¡Ïr0' => self::deserialize_type_RequestPeerType(self::gzdecode($stream)),
default => self::err($stream)
}
,
'max_quantity' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => $this->deserialize_type_KeyboardButton(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_KeyboardButtonRow(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'ƒ‹`w' => [
'_' => 'keyboardButtonRow',
'buttons' => self::deserialize_type_array_of_KeyboardButton(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => $this->deserialize_type_KeyboardButtonRow(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_replyKeyboardMarkup(mixed $stream): mixed {
$tmp = ['_' => 'replyKeyboardMarkup'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['resize'] = ($flags & 1) !== 0;
$tmp['single_use'] = ($flags & 2) !== 0;
$tmp['selective'] = ($flags & 4) !== 0;
$tmp['persistent'] = ($flags & 16) !== 0;
$tmp['rows'] = self::deserialize_type_array_of_KeyboardButtonRow(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 8) !== 0) $tmp['placeholder'] = self::deserialize_string($stream);
return $tmp;

    }
    private  function deserialize_type_ReplyMarkup(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'…[> ' => self::deserialize_replyKeyboardHide($stream),
'´†' => self::deserialize_replyKeyboardForceReply($stream),
'Ñ™Ý…' => self::deserialize_replyKeyboardMarkup($stream),
'T£H' => [
'_' => 'replyInlineMarkup',
'rows' => self::deserialize_type_array_of_KeyboardButtonRow(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => $this->deserialize_type_ReplyMarkup(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_messageReplies(mixed $stream): mixed {
$tmp = ['_' => 'messageReplies'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['comments'] = ($flags & 1) !== 0;
$tmp['replies'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['replies_pts'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 2) !== 0) $tmp['recent_repliers'] = self::deserialize_type_array_of_Peer(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 1) !== 0) $tmp['channel_id'] = unpack('q', stream_get_contents($stream, 8))[1];
if (($flags & 4) !== 0) $tmp['max_id'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 8) !== 0) $tmp['read_max_id'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_MessageReplies(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'ÂÖƒ' => self::deserialize_messageReplies($stream),
'¡Ïr0' => $this->deserialize_type_MessageReplies(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_messagePeerReaction(mixed $stream): mixed {
$tmp = ['_' => 'messagePeerReaction'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['big'] = ($flags & 1) !== 0;
$tmp['unread'] = ($flags & 2) !== 0;
$tmp['my'] = ($flags & 4) !== 0;
$tmp['peer_id'] = match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['date'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['reaction'] = match (stream_get_contents($stream, 4)) {
'Ôõy' => [
'_' => 'reactionEmpty',
],
'¸†"' => [
'_' => 'reactionEmoji',
'emoticon' => self::deserialize_string($stream),
],
'sü5‰' => [
'_' => 'reactionCustomEmoji',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_Reaction(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_type_array_of_MessagePeerReaction(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'<¶yŒ' => self::deserialize_messagePeerReaction($stream),
'¡Ïr0' => $this->deserialize_type_MessagePeerReaction(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_messageReactions(mixed $stream): mixed {
$tmp = ['_' => 'messageReactions'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['min'] = ($flags & 1) !== 0;
$tmp['can_see_list'] = ($flags & 4) !== 0;
$tmp['reactions_as_tags'] = ($flags & 8) !== 0;
$tmp['results'] = self::deserialize_type_array_of_ReactionCount(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 2) !== 0) $tmp['recent_reactions'] = self::deserialize_type_array_of_MessagePeerReaction(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
return $tmp;

    }
    private  function deserialize_type_MessageReactions(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'y”+O' => self::deserialize_messageReactions($stream),
'¡Ïr0' => $this->deserialize_type_MessageReactions(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_message(mixed $stream): mixed {
$this->referenceDatabase?->addOriginContext('message');
$this->minDatabase?->addOriginContext('message');
$tmp = ['_' => 'message'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['out'] = ($flags & 2) !== 0;
$tmp['mentioned'] = ($flags & 16) !== 0;
$tmp['media_unread'] = ($flags & 32) !== 0;
$tmp['silent'] = ($flags & 8192) !== 0;
$tmp['post'] = ($flags & 16384) !== 0;
$tmp['from_scheduled'] = ($flags & 262144) !== 0;
$tmp['legacy'] = ($flags & 524288) !== 0;
$tmp['edit_hide'] = ($flags & 2097152) !== 0;
$tmp['pinned'] = ($flags & 16777216) !== 0;
$tmp['noforwards'] = ($flags & 67108864) !== 0;
$tmp['invert_media'] = ($flags & 134217728) !== 0;
$tmp['id'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 256) !== 0) $tmp['from_id'] = match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 536870912) !== 0) $tmp['from_boosts_applied'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['peer_id'] = match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 268435456) !== 0) $tmp['saved_peer_id'] = match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 4) !== 0) $tmp['fwd_from'] = match (stream_get_contents($stream, 4)) {
'»ôMN' => self::deserialize_messageFwdHeader($stream),
'¡Ïr0' => self::deserialize_type_MessageFwdHeader(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 2048) !== 0) $tmp['via_bot_id'] = unpack('q', stream_get_contents($stream, 8))[1];
if (($flags & 8) !== 0) $tmp['reply_to'] = match (stream_get_contents($stream, 4)) {
'Û	¼¯' => self::deserialize_messageReplyHeader($stream),
'9ùZ' => [
'_' => 'messageReplyStoryHeader',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'story_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_MessageReplyHeader(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['date'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['message'] = self::deserialize_string($stream);
if (($flags & 512) !== 0) $tmp['media'] = match (stream_get_contents($stream, 4)) {
' cí=' => [
'_' => 'messageMediaEmpty',
],
'×PQi' => self::deserialize_messageMediaPhoto($stream),
'tÔàV' => [
'_' => 'messageMediaGeo',
'geo' => match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => self::deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'I)2p' => [
'_' => 'messageMediaContact',
'phone_number' => self::deserialize_string($stream),
'first_name' => self::deserialize_string($stream),
'last_name' => self::deserialize_string($stream),
'vcard' => self::deserialize_string($stream),
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'žô„Ÿ' => [
'_' => 'messageMediaUnsupported',
],
'-×ôL' => self::deserialize_messageMediaDocument($stream),
';ñÝ' => self::deserialize_messageMediaWebPage($stream),
'?SÀ.' => [
'_' => 'messageMediaVenue',
'geo' => match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => self::deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
,
'title' => self::deserialize_string($stream),
'address' => self::deserialize_string($stream),
'provider' => self::deserialize_string($stream),
'venue_id' => self::deserialize_string($stream),
'venue_type' => self::deserialize_string($stream),
],
'�±ý' => [
'_' => 'messageMediaGame',
'game' => match (stream_get_contents($stream, 4)) {
';eù½' => self::deserialize_game($stream),
'¡Ïr0' => self::deserialize_type_Game(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ÓH¥ö' => self::deserialize_messageMediaInvoice($stream),
'fÆ@¹' => self::deserialize_messageMediaGeoLive($stream),
'˜çÖK' => [
'_' => 'messageMediaPoll',
'poll' => match (stream_get_contents($stream, 4)) {
'a�á†' => self::deserialize_poll($stream),
'¡Ïr0' => self::deserialize_type_Poll(self::gzdecode($stream)),
default => self::err($stream)
}
,
'results' => match (stream_get_contents($stream, 4)) {
' $ßz' => self::deserialize_pollResults($stream),
'¡Ïr0' => self::deserialize_type_PollResults(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'‹å~?' => [
'_' => 'messageMediaDice',
'value' => unpack('l', stream_get_contents($stream, 4))[1],
'emoticon' => self::deserialize_string($stream),
],
'ƒbËh' => self::deserialize_messageMediaStory($stream),
'°…­Ú' => self::deserialize_messageMediaGiveaway($stream),
'h™Æ' => self::deserialize_messageMediaGiveawayResults($stream),
'¡Ïr0' => self::deserialize_type_MessageMedia(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 64) !== 0) $tmp['reply_markup'] = match (stream_get_contents($stream, 4)) {
'…[> ' => self::deserialize_replyKeyboardHide($stream),
'´†' => self::deserialize_replyKeyboardForceReply($stream),
'Ñ™Ý…' => self::deserialize_replyKeyboardMarkup($stream),
'T£H' => [
'_' => 'replyInlineMarkup',
'rows' => self::deserialize_type_array_of_KeyboardButtonRow(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => self::deserialize_type_ReplyMarkup(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 128) !== 0) $tmp['entities'] = self::deserialize_type_array_of_MessageEntity(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 1024) !== 0) $tmp['views'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 1024) !== 0) $tmp['forwards'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 8388608) !== 0) $tmp['replies'] = match (stream_get_contents($stream, 4)) {
'ÂÖƒ' => self::deserialize_messageReplies($stream),
'¡Ïr0' => self::deserialize_type_MessageReplies(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 32768) !== 0) $tmp['edit_date'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 65536) !== 0) $tmp['post_author'] = self::deserialize_string($stream);
if (($flags & 131072) !== 0) $tmp['grouped_id'] = unpack('q', stream_get_contents($stream, 8))[1];
if (($flags & 1048576) !== 0) $tmp['reactions'] = match (stream_get_contents($stream, 4)) {
'y”+O' => self::deserialize_messageReactions($stream),
'¡Ïr0' => self::deserialize_type_MessageReactions(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 4194304) !== 0) $tmp['restriction_reason'] = self::deserialize_type_array_of_RestrictionReason(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 33554432) !== 0) $tmp['ttl_period'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 1073741824) !== 0) $tmp['quick_reply_shortcut_id'] = unpack('l', stream_get_contents($stream, 4))[1];
$this->referenceDatabase?->addOrigin($tmp);
$this->minDatabase?->addOrigin($tmp);
return $tmp;

    }
    private  function deserialize_messageActionChatCreate(mixed $stream): mixed {
$tmp = [
'_' => 'messageActionChatCreate',
'title' => self::deserialize_string($stream),
'users' => self::deserialize_type_array_of_long(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
];
$this->minDatabase?->addPeer($tmp);
return $tmp;

    }
    private  function deserialize_messageActionChatAddUser(mixed $stream): mixed {
$tmp = [
'_' => 'messageActionChatAddUser',
'users' => self::deserialize_type_array_of_long(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
];
$this->minDatabase?->addPeer($tmp);
return $tmp;

    }
    private  function deserialize_messageActionChatDeleteUser(mixed $stream): mixed {
$tmp = [
'_' => 'messageActionChatDeleteUser',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
];
$this->minDatabase?->addPeer($tmp);
return $tmp;

    }
    private  function deserialize_messageActionChatJoinedByLink(mixed $stream): mixed {
$tmp = [
'_' => 'messageActionChatJoinedByLink',
'inviter_id' => unpack('q', stream_get_contents($stream, 8))[1],
];
$this->minDatabase?->addPeer($tmp);
return $tmp;

    }
    private  function deserialize_type_PostAddress(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'ëªŒ' => [
'_' => 'postAddress',
'street_line1' => self::deserialize_string($stream),
'street_line2' => self::deserialize_string($stream),
'city' => self::deserialize_string($stream),
'state' => self::deserialize_string($stream),
'country_iso2' => self::deserialize_string($stream),
'post_code' => self::deserialize_string($stream),
],
'¡Ïr0' => $this->deserialize_type_PostAddress(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_paymentRequestedInfo(mixed $stream): mixed {
$tmp = ['_' => 'paymentRequestedInfo'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['name'] = self::deserialize_string($stream);
if (($flags & 2) !== 0) $tmp['phone'] = self::deserialize_string($stream);
if (($flags & 4) !== 0) $tmp['email'] = self::deserialize_string($stream);
if (($flags & 8) !== 0) $tmp['shipping_address'] = match (stream_get_contents($stream, 4)) {
'ëªŒ' => [
'_' => 'postAddress',
'street_line1' => self::deserialize_string($stream),
'street_line2' => self::deserialize_string($stream),
'city' => self::deserialize_string($stream),
'state' => self::deserialize_string($stream),
'country_iso2' => self::deserialize_string($stream),
'post_code' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_PostAddress(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_type_PaymentRequestedInfo(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'”?œ�' => self::deserialize_paymentRequestedInfo($stream),
'¡Ïr0' => $this->deserialize_type_PaymentRequestedInfo(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_PaymentCharge(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'~Âê' => [
'_' => 'paymentCharge',
'id' => self::deserialize_string($stream),
'provider_charge_id' => self::deserialize_string($stream),
],
'¡Ïr0' => $this->deserialize_type_PaymentCharge(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_messageActionPaymentSentMe(mixed $stream): mixed {
$tmp = ['_' => 'messageActionPaymentSentMe'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['recurring_init'] = ($flags & 4) !== 0;
$tmp['recurring_used'] = ($flags & 8) !== 0;
$tmp['currency'] = self::deserialize_string($stream);
$tmp['total_amount'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['payload'] = self::deserialize_bytes($stream);
if (($flags & 1) !== 0) $tmp['info'] = match (stream_get_contents($stream, 4)) {
'”?œ�' => self::deserialize_paymentRequestedInfo($stream),
'¡Ïr0' => self::deserialize_type_PaymentRequestedInfo(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 2) !== 0) $tmp['shipping_option_id'] = self::deserialize_string($stream);
$tmp['charge'] = match (stream_get_contents($stream, 4)) {
'~Âê' => [
'_' => 'paymentCharge',
'id' => self::deserialize_string($stream),
'provider_charge_id' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_PaymentCharge(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_messageActionPaymentSent(mixed $stream): mixed {
$tmp = ['_' => 'messageActionPaymentSent'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['recurring_init'] = ($flags & 4) !== 0;
$tmp['recurring_used'] = ($flags & 8) !== 0;
$tmp['currency'] = self::deserialize_string($stream);
$tmp['total_amount'] = unpack('q', stream_get_contents($stream, 8))[1];
if (($flags & 1) !== 0) $tmp['invoice_slug'] = self::deserialize_string($stream);
return $tmp;

    }
    private  function deserialize_type_PhoneCallDiscardReason(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'#ä…' => [
'_' => 'phoneCallDiscardReasonMissed',
],
' Á•à' => [
'_' => 'phoneCallDiscardReasonDisconnect',
],
'�Æ­W' => [
'_' => 'phoneCallDiscardReasonHangup',
],
'Éè÷ú' => [
'_' => 'phoneCallDiscardReasonBusy',
],
'¡Ïr0' => $this->deserialize_type_PhoneCallDiscardReason(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_messageActionPhoneCall(mixed $stream): mixed {
$tmp = ['_' => 'messageActionPhoneCall'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['video'] = ($flags & 4) !== 0;
$tmp['call_id'] = unpack('q', stream_get_contents($stream, 8))[1];
if (($flags & 1) !== 0) $tmp['reason'] = match (stream_get_contents($stream, 4)) {
'#ä…' => [
'_' => 'phoneCallDiscardReasonMissed',
],
' Á•à' => [
'_' => 'phoneCallDiscardReasonDisconnect',
],
'�Æ­W' => [
'_' => 'phoneCallDiscardReasonHangup',
],
'Éè÷ú' => [
'_' => 'phoneCallDiscardReasonBusy',
],
'¡Ïr0' => self::deserialize_type_PhoneCallDiscardReason(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 2) !== 0) $tmp['duration'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_botApp(mixed $stream): mixed {
$tmp = ['_' => 'botApp'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['access_hash'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['short_name'] = self::deserialize_string($stream);
$tmp['title'] = self::deserialize_string($stream);
$tmp['description'] = self::deserialize_string($stream);
$tmp['photo'] = match (stream_get_contents($stream, 4)) {
'-²1#' => [
'_' => 'photoEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ezû' => self::deserialize_photo($stream),
'¡Ïr0' => self::deserialize_type_Photo(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 1) !== 0) $tmp['document'] = match (stream_get_contents($stream, 4)) {
'qÈø6' => [
'_' => 'documentEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ØÄÔ�' => self::deserialize_document($stream),
'¡Ïr0' => self::deserialize_type_Document(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['hash'] = unpack('q', stream_get_contents($stream, 8))[1];
return $tmp;

    }
    private  function deserialize_type_BotApp(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'·t¦]' => [
'_' => 'botAppNotModified',
],
'ÖÑü•' => self::deserialize_botApp($stream),
'¡Ïr0' => $this->deserialize_type_BotApp(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_messageActionBotAllowed(mixed $stream): mixed {
$tmp = ['_' => 'messageActionBotAllowed'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['attach_menu'] = ($flags & 2) !== 0;
$tmp['from_request'] = ($flags & 8) !== 0;
if (($flags & 1) !== 0) $tmp['domain'] = self::deserialize_string($stream);
if (($flags & 4) !== 0) $tmp['app'] = match (stream_get_contents($stream, 4)) {
'·t¦]' => [
'_' => 'botAppNotModified',
],
'ÖÑü•' => self::deserialize_botApp($stream),
'¡Ïr0' => self::deserialize_type_BotApp(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_type_SecureValueType(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'ã�*�' => [
'_' => 'secureValueTypePersonalDetails',
],
'' . "\0" . 'j¬=' => [
'_' => 'secureValueTypePassport',
],
'Ä%ä' => [
'_' => 'secureValueTypeDriverLicense',
],
'KtÐ ' => [
'_' => 'secureValueTypeIdentityCard',
],
'#�¤™' => [
'_' => 'secureValueTypeInternalPassport',
],
'&ãË' => [
'_' => 'secureValueTypeAddress',
],
'N•6ü' => [
'_' => 'secureValueTypeUtilityBill',
],
'|‰' => [
'_' => 'secureValueTypeBankStatement',
],
'ˆ4ˆ‹' => [
'_' => 'secureValueTypeRentalAgreement',
],
'j€ã™' => [
'_' => 'secureValueTypePassportRegistration',
],
'3ìê' => [
'_' => 'secureValueTypeTemporaryRegistration',
],
'Ûª ³' => [
'_' => 'secureValueTypePhone',
],
'î§<Ž' => [
'_' => 'secureValueTypeEmail',
],
'¡Ïr0' => $this->deserialize_type_SecureValueType(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_SecureData(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'Ã¾êŠ' => [
'_' => 'secureData',
'data' => self::deserialize_bytes($stream),
'data_hash' => self::deserialize_bytes($stream),
'secret' => self::deserialize_bytes($stream),
],
'¡Ïr0' => $this->deserialize_type_SecureData(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_SecureFile(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'D—d' => [
'_' => 'secureFileEmpty',
],
'~Â	}' => [
'_' => 'secureFile',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'size' => unpack('q', stream_get_contents($stream, 8))[1],
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'file_hash' => self::deserialize_bytes($stream),
'secret' => self::deserialize_bytes($stream),
],
'¡Ïr0' => $this->deserialize_type_SecureFile(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_array_of_SecureFile(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'D—d' => [
'_' => 'secureFileEmpty',
],
'~Â	}' => [
'_' => 'secureFile',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'size' => unpack('q', stream_get_contents($stream, 8))[1],
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'file_hash' => self::deserialize_bytes($stream),
'secret' => self::deserialize_bytes($stream),
],
'¡Ïr0' => self::deserialize_type_SecureFile(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_SecurePlainData(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'Ý™`}' => [
'_' => 'securePlainPhone',
'phone' => self::deserialize_string($stream),
],
'_Zì!' => [
'_' => 'securePlainEmail',
'email' => self::deserialize_string($stream),
],
'¡Ïr0' => $this->deserialize_type_SecurePlainData(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_secureValue(mixed $stream): mixed {
$tmp = ['_' => 'secureValue'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['type'] = match (stream_get_contents($stream, 4)) {
'ã�*�' => [
'_' => 'secureValueTypePersonalDetails',
],
'' . "\0" . 'j¬=' => [
'_' => 'secureValueTypePassport',
],
'Ä%ä' => [
'_' => 'secureValueTypeDriverLicense',
],
'KtÐ ' => [
'_' => 'secureValueTypeIdentityCard',
],
'#�¤™' => [
'_' => 'secureValueTypeInternalPassport',
],
'&ãË' => [
'_' => 'secureValueTypeAddress',
],
'N•6ü' => [
'_' => 'secureValueTypeUtilityBill',
],
'|‰' => [
'_' => 'secureValueTypeBankStatement',
],
'ˆ4ˆ‹' => [
'_' => 'secureValueTypeRentalAgreement',
],
'j€ã™' => [
'_' => 'secureValueTypePassportRegistration',
],
'3ìê' => [
'_' => 'secureValueTypeTemporaryRegistration',
],
'Ûª ³' => [
'_' => 'secureValueTypePhone',
],
'î§<Ž' => [
'_' => 'secureValueTypeEmail',
],
'¡Ïr0' => self::deserialize_type_SecureValueType(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 1) !== 0) $tmp['data'] = match (stream_get_contents($stream, 4)) {
'Ã¾êŠ' => [
'_' => 'secureData',
'data' => self::deserialize_bytes($stream),
'data_hash' => self::deserialize_bytes($stream),
'secret' => self::deserialize_bytes($stream),
],
'¡Ïr0' => self::deserialize_type_SecureData(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 2) !== 0) $tmp['front_side'] = match (stream_get_contents($stream, 4)) {
'D—d' => [
'_' => 'secureFileEmpty',
],
'~Â	}' => [
'_' => 'secureFile',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'size' => unpack('q', stream_get_contents($stream, 8))[1],
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'file_hash' => self::deserialize_bytes($stream),
'secret' => self::deserialize_bytes($stream),
],
'¡Ïr0' => self::deserialize_type_SecureFile(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 4) !== 0) $tmp['reverse_side'] = match (stream_get_contents($stream, 4)) {
'D—d' => [
'_' => 'secureFileEmpty',
],
'~Â	}' => [
'_' => 'secureFile',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'size' => unpack('q', stream_get_contents($stream, 8))[1],
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'file_hash' => self::deserialize_bytes($stream),
'secret' => self::deserialize_bytes($stream),
],
'¡Ïr0' => self::deserialize_type_SecureFile(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 8) !== 0) $tmp['selfie'] = match (stream_get_contents($stream, 4)) {
'D—d' => [
'_' => 'secureFileEmpty',
],
'~Â	}' => [
'_' => 'secureFile',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'size' => unpack('q', stream_get_contents($stream, 8))[1],
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'file_hash' => self::deserialize_bytes($stream),
'secret' => self::deserialize_bytes($stream),
],
'¡Ïr0' => self::deserialize_type_SecureFile(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 64) !== 0) $tmp['translation'] = self::deserialize_type_array_of_SecureFile(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 16) !== 0) $tmp['files'] = self::deserialize_type_array_of_SecureFile(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 32) !== 0) $tmp['plain_data'] = match (stream_get_contents($stream, 4)) {
'Ý™`}' => [
'_' => 'securePlainPhone',
'phone' => self::deserialize_string($stream),
],
'_Zì!' => [
'_' => 'securePlainEmail',
'email' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_SecurePlainData(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['hash'] = self::deserialize_bytes($stream);
return $tmp;

    }
    private  function deserialize_type_array_of_SecureValue(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'Ê ' => self::deserialize_secureValue($stream),
'¡Ïr0' => $this->deserialize_type_SecureValue(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_SecureCredentialsEncrypted(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'Gêð3' => [
'_' => 'secureCredentialsEncrypted',
'data' => self::deserialize_bytes($stream),
'hash' => self::deserialize_bytes($stream),
'secret' => self::deserialize_bytes($stream),
],
'¡Ïr0' => $this->deserialize_type_SecureCredentialsEncrypted(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_array_of_SecureValueType(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'ã�*�' => [
'_' => 'secureValueTypePersonalDetails',
],
'' . "\0" . 'j¬=' => [
'_' => 'secureValueTypePassport',
],
'Ä%ä' => [
'_' => 'secureValueTypeDriverLicense',
],
'KtÐ ' => [
'_' => 'secureValueTypeIdentityCard',
],
'#�¤™' => [
'_' => 'secureValueTypeInternalPassport',
],
'&ãË' => [
'_' => 'secureValueTypeAddress',
],
'N•6ü' => [
'_' => 'secureValueTypeUtilityBill',
],
'|‰' => [
'_' => 'secureValueTypeBankStatement',
],
'ˆ4ˆ‹' => [
'_' => 'secureValueTypeRentalAgreement',
],
'j€ã™' => [
'_' => 'secureValueTypePassportRegistration',
],
'3ìê' => [
'_' => 'secureValueTypeTemporaryRegistration',
],
'Ûª ³' => [
'_' => 'secureValueTypePhone',
],
'î§<Ž' => [
'_' => 'secureValueTypeEmail',
],
'¡Ïr0' => self::deserialize_type_SecureValueType(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_InputGroupCall(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'„ªØ' => [
'_' => 'inputGroupCall',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => $this->deserialize_type_InputGroupCall(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_messageActionGroupCall(mixed $stream): mixed {
$tmp = ['_' => 'messageActionGroupCall'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['call'] = match (stream_get_contents($stream, 4)) {
'„ªØ' => [
'_' => 'inputGroupCall',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputGroupCall(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 1) !== 0) $tmp['duration'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_messageActionSetMessagesTTL(mixed $stream): mixed {
$tmp = ['_' => 'messageActionSetMessagesTTL'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['period'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['auto_setting_from'] = unpack('q', stream_get_contents($stream, 8))[1];
return $tmp;

    }
    private  function deserialize_messageActionGiftPremium(mixed $stream): mixed {
$tmp = ['_' => 'messageActionGiftPremium'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['currency'] = self::deserialize_string($stream);
$tmp['amount'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['months'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['crypto_currency'] = self::deserialize_string($stream);
if (($flags & 1) !== 0) $tmp['crypto_amount'] = unpack('q', stream_get_contents($stream, 8))[1];
return $tmp;

    }
    private  function deserialize_messageActionTopicCreate(mixed $stream): mixed {
$tmp = ['_' => 'messageActionTopicCreate'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['title'] = self::deserialize_string($stream);
$tmp['icon_color'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['icon_emoji_id'] = unpack('q', stream_get_contents($stream, 8))[1];
return $tmp;

    }
    private  function deserialize_messageActionTopicEdit(mixed $stream): mixed {
$tmp = ['_' => 'messageActionTopicEdit'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['title'] = self::deserialize_string($stream);
if (($flags & 2) !== 0) $tmp['icon_emoji_id'] = unpack('q', stream_get_contents($stream, 8))[1];
if (($flags & 4) !== 0) $tmp['closed'] = match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) };
if (($flags & 8) !== 0) $tmp['hidden'] = match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) };
return $tmp;

    }
    private  function deserialize_messageActionSetChatWallPaper(mixed $stream): mixed {
$tmp = ['_' => 'messageActionSetChatWallPaper'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['same'] = ($flags & 1) !== 0;
$tmp['for_both'] = ($flags & 2) !== 0;
$tmp['wallpaper'] = match (stream_get_contents($stream, 4)) {
'íÃ7¤' => self::deserialize_wallPaper($stream),
'A€à' => self::deserialize_wallPaperNoFile($stream),
'¡Ïr0' => self::deserialize_type_WallPaper(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_messageActionGiftCode(mixed $stream): mixed {
$tmp = ['_' => 'messageActionGiftCode'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['via_giveaway'] = ($flags & 1) !== 0;
$tmp['unclaimed'] = ($flags & 4) !== 0;
if (($flags & 2) !== 0) $tmp['boost_peer'] = match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['months'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['slug'] = self::deserialize_string($stream);
if (($flags & 4) !== 0) $tmp['currency'] = self::deserialize_string($stream);
if (($flags & 4) !== 0) $tmp['amount'] = unpack('q', stream_get_contents($stream, 8))[1];
if (($flags & 8) !== 0) $tmp['crypto_currency'] = self::deserialize_string($stream);
if (($flags & 8) !== 0) $tmp['crypto_amount'] = unpack('q', stream_get_contents($stream, 8))[1];
return $tmp;

    }
    private  function deserialize_type_MessageAction(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'°÷®¶' => [
'_' => 'messageActionEmpty',
],
'­ËG½' => self::deserialize_messageActionChatCreate($stream),
'ZÎ¡µ' => [
'_' => 'messageActionChatEditTitle',
'title' => self::deserialize_string($stream),
],
'¨Ë' => [
'_' => 'messageActionChatEditPhoto',
'photo' => match (stream_get_contents($stream, 4)) {
'-²1#' => [
'_' => 'photoEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ezû' => self::deserialize_photo($stream),
'¡Ïr0' => self::deserialize_type_Photo(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ïûã•' => [
'_' => 'messageActionChatDeletePhoto',
],
'' . "\0" . 'ýÎ' => self::deserialize_messageActionChatAddUser($stream),
'Ì0?¤' => self::deserialize_messageActionChatDeleteUser($stream),
'Ã$' => self::deserialize_messageActionChatJoinedByLink($stream),
'’¬Ò•' => [
'_' => 'messageActionChannelCreate',
'title' => self::deserialize_string($stream),
],
'’á' => [
'_' => 'messageActionChatMigrateTo',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'éH9ê' => [
'_' => 'messageActionChannelMigrateFrom',
'title' => self::deserialize_string($stream),
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'í8½”' => [
'_' => 'messageActionPinMessage',
],
'¶ºŸ' => [
'_' => 'messageActionHistoryClear',
],
'v(§’' => [
'_' => 'messageActionGameScore',
'game_id' => unpack('q', stream_get_contents($stream, 8))[1],
'score' => unpack('l', stream_get_contents($stream, 4))[1],
],
'\'³1�' => self::deserialize_messageActionPaymentSentMe($stream),
'V?–' => self::deserialize_messageActionPaymentSent($stream),
'á€' => self::deserialize_messageActionPhoneCall($stream),
'›’’G' => [
'_' => 'messageActionScreenshotTaken',
],
'VŸæú' => [
'_' => 'messageActionCustomAction',
'message' => self::deserialize_string($stream),
],
'yÖÅ' => self::deserialize_messageActionBotAllowed($stream),
'Ss(' => [
'_' => 'messageActionSecureValuesSentMe',
'values' => self::deserialize_type_array_of_SecureValue(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'credentials' => match (stream_get_contents($stream, 4)) {
'Gêð3' => [
'_' => 'secureCredentialsEncrypted',
'data' => self::deserialize_bytes($stream),
'hash' => self::deserialize_bytes($stream),
'secret' => self::deserialize_bytes($stream),
],
'¡Ïr0' => self::deserialize_type_SecureCredentialsEncrypted(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Ta\\Ù' => [
'_' => 'messageActionSecureValuesSent',
'types' => self::deserialize_type_array_of_SecureValueType(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'v_òó' => [
'_' => 'messageActionContactSignUp',
],
'—Öà˜' => [
'_' => 'messageActionGeoProximityReached',
'from_id' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'to_id' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'distance' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Bz' => self::deserialize_messageActionGroupCall($stream),
'÷’/P' => [
'_' => 'messageActionInviteToGroupCall',
'call' => match (stream_get_contents($stream, 4)) {
'„ªØ' => [
'_' => 'inputGroupCall',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputGroupCall(self::gzdecode($stream)),
default => self::err($stream)
}
,
'users' => self::deserialize_type_array_of_long(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'{M<' => self::deserialize_messageActionSetMessagesTTL($stream),
'av ³' => [
'_' => 'messageActionGroupCallScheduled',
'call' => match (stream_get_contents($stream, 4)) {
'„ªØ' => [
'_' => 'inputGroupCall',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputGroupCall(self::gzdecode($stream)),
default => self::err($stream)
}
,
'schedule_date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Ecxª' => [
'_' => 'messageActionSetChatTheme',
'emoticon' => self::deserialize_string($stream),
],
'Ë£¼ë' => [
'_' => 'messageActionChatJoinedByRequest',
],
'y€ÝG' => [
'_' => 'messageActionWebViewDataSentMe',
'text' => self::deserialize_string($stream),
'data' => self::deserialize_string($stream),
],
'µŒÃ´' => [
'_' => 'messageActionWebViewDataSent',
'text' => self::deserialize_string($stream),
],
'ìj=È' => self::deserialize_messageActionGiftPremium($stream),
'V’™' => self::deserialize_messageActionTopicCreate($stream),
' H”À' => self::deserialize_messageActionTopicEdit($stream),
'^cÞW' => [
'_' => 'messageActionSuggestProfilePhoto',
'photo' => match (stream_get_contents($stream, 4)) {
'-²1#' => [
'_' => 'photoEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ezû' => self::deserialize_photo($stream),
'¡Ïr0' => self::deserialize_type_Photo(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'›ŽQ1' => [
'_' => 'messageActionRequestedPeer',
'button_id' => unpack('l', stream_get_contents($stream, 4))[1],
'peers' => self::deserialize_type_array_of_Peer(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ô£`P' => self::deserialize_messageActionSetChatWallPaper($stream),
'	.Œg' => self::deserialize_messageActionGiftCode($stream),
'í©+3' => [
'_' => 'messageActionGiveawayLaunch',
],
'Å­Ÿ*' => [
'_' => 'messageActionGiveawayResults',
'winners_count' => unpack('l', stream_get_contents($stream, 4))[1],
'unclaimed_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'mªÌ' => [
'_' => 'messageActionBoostApply',
'boosts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => $this->deserialize_type_MessageAction(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_messageService(mixed $stream): mixed {
$this->referenceDatabase?->addOriginContext('messageService');
$this->minDatabase?->addOriginContext('messageService');
$tmp = ['_' => 'messageService'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['out'] = ($flags & 2) !== 0;
$tmp['mentioned'] = ($flags & 16) !== 0;
$tmp['media_unread'] = ($flags & 32) !== 0;
$tmp['silent'] = ($flags & 8192) !== 0;
$tmp['post'] = ($flags & 16384) !== 0;
$tmp['legacy'] = ($flags & 524288) !== 0;
$tmp['id'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 256) !== 0) $tmp['from_id'] = match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['peer_id'] = match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 8) !== 0) $tmp['reply_to'] = match (stream_get_contents($stream, 4)) {
'Û	¼¯' => self::deserialize_messageReplyHeader($stream),
'9ùZ' => [
'_' => 'messageReplyStoryHeader',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'story_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_MessageReplyHeader(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['date'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['action'] = match (stream_get_contents($stream, 4)) {
'°÷®¶' => [
'_' => 'messageActionEmpty',
],
'­ËG½' => self::deserialize_messageActionChatCreate($stream),
'ZÎ¡µ' => [
'_' => 'messageActionChatEditTitle',
'title' => self::deserialize_string($stream),
],
'¨Ë' => [
'_' => 'messageActionChatEditPhoto',
'photo' => match (stream_get_contents($stream, 4)) {
'-²1#' => [
'_' => 'photoEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ezû' => self::deserialize_photo($stream),
'¡Ïr0' => self::deserialize_type_Photo(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ïûã•' => [
'_' => 'messageActionChatDeletePhoto',
],
'' . "\0" . 'ýÎ' => self::deserialize_messageActionChatAddUser($stream),
'Ì0?¤' => self::deserialize_messageActionChatDeleteUser($stream),
'Ã$' => self::deserialize_messageActionChatJoinedByLink($stream),
'’¬Ò•' => [
'_' => 'messageActionChannelCreate',
'title' => self::deserialize_string($stream),
],
'’á' => [
'_' => 'messageActionChatMigrateTo',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'éH9ê' => [
'_' => 'messageActionChannelMigrateFrom',
'title' => self::deserialize_string($stream),
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'í8½”' => [
'_' => 'messageActionPinMessage',
],
'¶ºŸ' => [
'_' => 'messageActionHistoryClear',
],
'v(§’' => [
'_' => 'messageActionGameScore',
'game_id' => unpack('q', stream_get_contents($stream, 8))[1],
'score' => unpack('l', stream_get_contents($stream, 4))[1],
],
'\'³1�' => self::deserialize_messageActionPaymentSentMe($stream),
'V?–' => self::deserialize_messageActionPaymentSent($stream),
'á€' => self::deserialize_messageActionPhoneCall($stream),
'›’’G' => [
'_' => 'messageActionScreenshotTaken',
],
'VŸæú' => [
'_' => 'messageActionCustomAction',
'message' => self::deserialize_string($stream),
],
'yÖÅ' => self::deserialize_messageActionBotAllowed($stream),
'Ss(' => [
'_' => 'messageActionSecureValuesSentMe',
'values' => self::deserialize_type_array_of_SecureValue(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'credentials' => match (stream_get_contents($stream, 4)) {
'Gêð3' => [
'_' => 'secureCredentialsEncrypted',
'data' => self::deserialize_bytes($stream),
'hash' => self::deserialize_bytes($stream),
'secret' => self::deserialize_bytes($stream),
],
'¡Ïr0' => self::deserialize_type_SecureCredentialsEncrypted(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Ta\\Ù' => [
'_' => 'messageActionSecureValuesSent',
'types' => self::deserialize_type_array_of_SecureValueType(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'v_òó' => [
'_' => 'messageActionContactSignUp',
],
'—Öà˜' => [
'_' => 'messageActionGeoProximityReached',
'from_id' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'to_id' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'distance' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Bz' => self::deserialize_messageActionGroupCall($stream),
'÷’/P' => [
'_' => 'messageActionInviteToGroupCall',
'call' => match (stream_get_contents($stream, 4)) {
'„ªØ' => [
'_' => 'inputGroupCall',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputGroupCall(self::gzdecode($stream)),
default => self::err($stream)
}
,
'users' => self::deserialize_type_array_of_long(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'{M<' => self::deserialize_messageActionSetMessagesTTL($stream),
'av ³' => [
'_' => 'messageActionGroupCallScheduled',
'call' => match (stream_get_contents($stream, 4)) {
'„ªØ' => [
'_' => 'inputGroupCall',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputGroupCall(self::gzdecode($stream)),
default => self::err($stream)
}
,
'schedule_date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Ecxª' => [
'_' => 'messageActionSetChatTheme',
'emoticon' => self::deserialize_string($stream),
],
'Ë£¼ë' => [
'_' => 'messageActionChatJoinedByRequest',
],
'y€ÝG' => [
'_' => 'messageActionWebViewDataSentMe',
'text' => self::deserialize_string($stream),
'data' => self::deserialize_string($stream),
],
'µŒÃ´' => [
'_' => 'messageActionWebViewDataSent',
'text' => self::deserialize_string($stream),
],
'ìj=È' => self::deserialize_messageActionGiftPremium($stream),
'V’™' => self::deserialize_messageActionTopicCreate($stream),
' H”À' => self::deserialize_messageActionTopicEdit($stream),
'^cÞW' => [
'_' => 'messageActionSuggestProfilePhoto',
'photo' => match (stream_get_contents($stream, 4)) {
'-²1#' => [
'_' => 'photoEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ezû' => self::deserialize_photo($stream),
'¡Ïr0' => self::deserialize_type_Photo(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'›ŽQ1' => [
'_' => 'messageActionRequestedPeer',
'button_id' => unpack('l', stream_get_contents($stream, 4))[1],
'peers' => self::deserialize_type_array_of_Peer(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ô£`P' => self::deserialize_messageActionSetChatWallPaper($stream),
'	.Œg' => self::deserialize_messageActionGiftCode($stream),
'í©+3' => [
'_' => 'messageActionGiveawayLaunch',
],
'Å­Ÿ*' => [
'_' => 'messageActionGiveawayResults',
'winners_count' => unpack('l', stream_get_contents($stream, 4))[1],
'unclaimed_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'mªÌ' => [
'_' => 'messageActionBoostApply',
'boosts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_MessageAction(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 33554432) !== 0) $tmp['ttl_period'] = unpack('l', stream_get_contents($stream, 4))[1];
$this->referenceDatabase?->addOrigin($tmp);
$this->minDatabase?->addOrigin($tmp);
return $tmp;

    }
    private  function deserialize_type_Message(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => $this->deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_DataJSON(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'¡Ïr0' => $this->deserialize_type_DataJSON(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_SendMessageAction(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'Nt¿' => [
'_' => 'sendMessageTypingAction',
],
'õÈ^ý' => [
'_' => 'sendMessageCancelAction',
],
'oÖ‡¡' => [
'_' => 'sendMessageRecordVideoAction',
],
'ì:vé' => [
'_' => 'sendMessageUploadVideoAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'÷s/Õ' => [
'_' => 'sendMessageRecordAudioAction',
],
'«×Qó' => [
'_' => 'sendMessageUploadAudioAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'&JÓÑ' => [
'_' => 'sendMessageUploadPhotoAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'äÙª' => [
'_' => 'sendMessageUploadDocumentAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡‹o' => [
'_' => 'sendMessageGeoLocationAction',
],
'o¼Œb' => [
'_' => 'sendMessageChooseContactAction',
],
'H�jÝ' => [
'_' => 'sendMessageGamePlayAction',
],
'¼òˆ' => [
'_' => 'sendMessageRecordRoundAction',
],
'f>$' => [
'_' => 'sendMessageUploadRoundAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'…",Ù' => [
'_' => 'speakingInGroupCallAction',
],
'F’ÚÛ' => [
'_' => 'sendMessageHistoryImportAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'±ÆZ°' => [
'_' => 'sendMessageChooseStickerAction',
],
'Ë+—%' => [
'_' => 'sendMessageEmojiInteraction',
'emoticon' => self::deserialize_string($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'interaction' => match (stream_get_contents($stream, 4)) {
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'¡Ïr0' => self::deserialize_type_DataJSON(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'.�e¶' => [
'_' => 'sendMessageEmojiInteractionSeen',
'emoticon' => self::deserialize_string($stream),
],
'¡Ïr0' => $this->deserialize_type_SendMessageAction(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_ChatParticipant(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'@-À' => [
'_' => 'chatParticipant',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'inviter_id' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'äÎkä' => [
'_' => 'chatParticipantCreator',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'[?“ ' => [
'_' => 'chatParticipantAdmin',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'inviter_id' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => $this->deserialize_type_ChatParticipant(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_chatParticipantsForbidden(mixed $stream): mixed {
$tmp = ['_' => 'chatParticipantsForbidden'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['chat_id'] = unpack('q', stream_get_contents($stream, 8))[1];
if (($flags & 1) !== 0) $tmp['self_participant'] = match (stream_get_contents($stream, 4)) {
'@-À' => [
'_' => 'chatParticipant',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'inviter_id' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'äÎkä' => [
'_' => 'chatParticipantCreator',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'[?“ ' => [
'_' => 'chatParticipantAdmin',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'inviter_id' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_ChatParticipant(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_type_array_of_ChatParticipant(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'@-À' => [
'_' => 'chatParticipant',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'inviter_id' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'äÎkä' => [
'_' => 'chatParticipantCreator',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'[?“ ' => [
'_' => 'chatParticipantAdmin',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'inviter_id' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_ChatParticipant(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_ChatParticipants(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'áÓc‡' => self::deserialize_chatParticipantsForbidden($stream),
'ø“¼<' => [
'_' => 'chatParticipants',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'participants' => self::deserialize_type_array_of_ChatParticipant(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => $this->deserialize_type_ChatParticipants(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_userStatusRecently(mixed $stream): mixed {
$tmp = ['_' => 'userStatusRecently'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['by_me'] = ($flags & 1) !== 0;
return $tmp;

    }
    private  function deserialize_userStatusLastWeek(mixed $stream): mixed {
$tmp = ['_' => 'userStatusLastWeek'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['by_me'] = ($flags & 1) !== 0;
return $tmp;

    }
    private  function deserialize_userStatusLastMonth(mixed $stream): mixed {
$tmp = ['_' => 'userStatusLastMonth'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['by_me'] = ($flags & 1) !== 0;
return $tmp;

    }
    private  function deserialize_type_UserStatus(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'IPÐ	' => [
'_' => 'userStatusEmpty',
],
'I9¹í' => [
'_' => 'userStatusOnline',
'expires' => unpack('l', stream_get_contents($stream, 4))[1],
],
'?pŒ' . "\0" . '' => [
'_' => 'userStatusOffline',
'was_online' => unpack('l', stream_get_contents($stream, 4))[1],
],
'È}{' => self::deserialize_userStatusRecently($stream),
'T' => self::deserialize_userStatusLastWeek($stream),
'w—‰e' => self::deserialize_userStatusLastMonth($stream),
'¡Ïr0' => $this->deserialize_type_UserStatus(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_updateNewAuthorization(mixed $stream): mixed {
$tmp = ['_' => 'updateNewAuthorization'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['unconfirmed'] = ($flags & 1) !== 0;
$tmp['hash'] = unpack('q', stream_get_contents($stream, 8))[1];
if (($flags & 1) !== 0) $tmp['date'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['device'] = self::deserialize_string($stream);
if (($flags & 1) !== 0) $tmp['location'] = self::deserialize_string($stream);
return $tmp;

    }
    private  function deserialize_type_EncryptedFile(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'~IÂ' => [
'_' => 'encryptedFileEmpty',
],
'ØŒ' . "\0" . '¨' => [
'_' => 'encryptedFile',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'size' => unpack('q', stream_get_contents($stream, 8))[1],
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'key_fingerprint' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => $this->deserialize_type_EncryptedFile(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_EncryptedMessage(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'Áí' => [
'_' => 'encryptedMessage',
'random_id' => unpack('q', stream_get_contents($stream, 8))[1],
'chat_id' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'bytes' => self::deserialize_bytes($stream),
'file' => match (stream_get_contents($stream, 4)) {
'~IÂ' => [
'_' => 'encryptedFileEmpty',
],
'ØŒ' . "\0" . '¨' => [
'_' => 'encryptedFile',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'size' => unpack('q', stream_get_contents($stream, 8))[1],
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'key_fingerprint' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_EncryptedFile(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Ks#' => [
'_' => 'encryptedMessageService',
'random_id' => unpack('q', stream_get_contents($stream, 8))[1],
'chat_id' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'bytes' => self::deserialize_bytes($stream),
],
'¡Ïr0' => $this->deserialize_type_EncryptedMessage(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_encryptedChatRequested(mixed $stream): mixed {
$tmp = ['_' => 'encryptedChatRequested'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['folder_id'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['id'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['access_hash'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['date'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['admin_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['participant_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['g_a'] = self::deserialize_bytes($stream);
return $tmp;

    }
    private  function deserialize_encryptedChatDiscarded(mixed $stream): mixed {
$tmp = ['_' => 'encryptedChatDiscarded'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['history_deleted'] = ($flags & 1) !== 0;
$tmp['id'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_EncryptedChat(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
' À~«' => [
'_' => 'encryptedChatEmpty',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'SY²f' => [
'_' => 'encryptedChatWaiting',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'admin_id' => unpack('q', stream_get_contents($stream, 8))[1],
'participant_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'LÙñH' => self::deserialize_encryptedChatRequested($stream),
'ÇÔða' => [
'_' => 'encryptedChat',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'admin_id' => unpack('q', stream_get_contents($stream, 8))[1],
'participant_id' => unpack('q', stream_get_contents($stream, 8))[1],
'g_a_or_b' => self::deserialize_bytes($stream),
'key_fingerprint' => stream_get_contents($stream, 8),
],
'E|' => self::deserialize_encryptedChatDiscarded($stream),
'¡Ïr0' => $this->deserialize_type_EncryptedChat(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_dcOption(mixed $stream): mixed {
$tmp = ['_' => 'dcOption'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['ipv6'] = ($flags & 1) !== 0;
$tmp['media_only'] = ($flags & 2) !== 0;
$tmp['tcpo_only'] = ($flags & 4) !== 0;
$tmp['cdn'] = ($flags & 8) !== 0;
$tmp['static'] = ($flags & 16) !== 0;
$tmp['this_port_only'] = ($flags & 32) !== 0;
$tmp['id'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['ip_address'] = self::deserialize_string($stream);
$tmp['port'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 1024) !== 0) $tmp['secret'] = self::deserialize_string($stream);
return $tmp;

    }
    private  function deserialize_type_array_of_DcOption(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'¡·' => self::deserialize_dcOption($stream),
'¡Ïr0' => $this->deserialize_type_DcOption(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_NotifyPeer(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'ØÔŸ' => [
'_' => 'notifyPeer',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'L;È´' => [
'_' => 'notifyUsers',
],
'ÃÎÀ' => [
'_' => 'notifyChats',
],
'ïèÖ' => [
'_' => 'notifyBroadcasts',
],
'cn"' => [
'_' => 'notifyForumTopic',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'top_msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => $this->deserialize_type_NotifyPeer(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_NotificationSound(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'¾¾è—' => [
'_' => 'notificationSoundDefault',
],
'ß4o' => [
'_' => 'notificationSoundNone',
],
'äšƒ' => [
'_' => 'notificationSoundLocal',
'title' => self::deserialize_string($stream),
'data' => self::deserialize_string($stream),
],
'I€lÿ' => [
'_' => 'notificationSoundRingtone',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => $this->deserialize_type_NotificationSound(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_peerNotifySettings(mixed $stream): mixed {
$tmp = ['_' => 'peerNotifySettings'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['show_previews'] = match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) };
if (($flags & 2) !== 0) $tmp['silent'] = match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) };
if (($flags & 4) !== 0) $tmp['mute_until'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 8) !== 0) $tmp['ios_sound'] = match (stream_get_contents($stream, 4)) {
'¾¾è—' => [
'_' => 'notificationSoundDefault',
],
'ß4o' => [
'_' => 'notificationSoundNone',
],
'äšƒ' => [
'_' => 'notificationSoundLocal',
'title' => self::deserialize_string($stream),
'data' => self::deserialize_string($stream),
],
'I€lÿ' => [
'_' => 'notificationSoundRingtone',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_NotificationSound(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 16) !== 0) $tmp['android_sound'] = match (stream_get_contents($stream, 4)) {
'¾¾è—' => [
'_' => 'notificationSoundDefault',
],
'ß4o' => [
'_' => 'notificationSoundNone',
],
'äšƒ' => [
'_' => 'notificationSoundLocal',
'title' => self::deserialize_string($stream),
'data' => self::deserialize_string($stream),
],
'I€lÿ' => [
'_' => 'notificationSoundRingtone',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_NotificationSound(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 32) !== 0) $tmp['other_sound'] = match (stream_get_contents($stream, 4)) {
'¾¾è—' => [
'_' => 'notificationSoundDefault',
],
'ß4o' => [
'_' => 'notificationSoundNone',
],
'äšƒ' => [
'_' => 'notificationSoundLocal',
'title' => self::deserialize_string($stream),
'data' => self::deserialize_string($stream),
],
'I€lÿ' => [
'_' => 'notificationSoundRingtone',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_NotificationSound(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 64) !== 0) $tmp['stories_muted'] = match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) };
if (($flags & 128) !== 0) $tmp['stories_hide_sender'] = match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) };
if (($flags & 256) !== 0) $tmp['stories_ios_sound'] = match (stream_get_contents($stream, 4)) {
'¾¾è—' => [
'_' => 'notificationSoundDefault',
],
'ß4o' => [
'_' => 'notificationSoundNone',
],
'äšƒ' => [
'_' => 'notificationSoundLocal',
'title' => self::deserialize_string($stream),
'data' => self::deserialize_string($stream),
],
'I€lÿ' => [
'_' => 'notificationSoundRingtone',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_NotificationSound(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 512) !== 0) $tmp['stories_android_sound'] = match (stream_get_contents($stream, 4)) {
'¾¾è—' => [
'_' => 'notificationSoundDefault',
],
'ß4o' => [
'_' => 'notificationSoundNone',
],
'äšƒ' => [
'_' => 'notificationSoundLocal',
'title' => self::deserialize_string($stream),
'data' => self::deserialize_string($stream),
],
'I€lÿ' => [
'_' => 'notificationSoundRingtone',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_NotificationSound(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 1024) !== 0) $tmp['stories_other_sound'] = match (stream_get_contents($stream, 4)) {
'¾¾è—' => [
'_' => 'notificationSoundDefault',
],
'ß4o' => [
'_' => 'notificationSoundNone',
],
'äšƒ' => [
'_' => 'notificationSoundLocal',
'title' => self::deserialize_string($stream),
'data' => self::deserialize_string($stream),
],
'I€lÿ' => [
'_' => 'notificationSoundRingtone',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_NotificationSound(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_type_PeerNotifySettings(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
',b™' => self::deserialize_peerNotifySettings($stream),
'¡Ïr0' => $this->deserialize_type_PeerNotifySettings(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_updateServiceNotification(mixed $stream): mixed {
$tmp = ['_' => 'updateServiceNotification'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['popup'] = ($flags & 1) !== 0;
$tmp['invert_media'] = ($flags & 4) !== 0;
if (($flags & 2) !== 0) $tmp['inbox_date'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['type'] = self::deserialize_string($stream);
$tmp['message'] = self::deserialize_string($stream);
$tmp['media'] = match (stream_get_contents($stream, 4)) {
' cí=' => [
'_' => 'messageMediaEmpty',
],
'×PQi' => self::deserialize_messageMediaPhoto($stream),
'tÔàV' => [
'_' => 'messageMediaGeo',
'geo' => match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => self::deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'I)2p' => [
'_' => 'messageMediaContact',
'phone_number' => self::deserialize_string($stream),
'first_name' => self::deserialize_string($stream),
'last_name' => self::deserialize_string($stream),
'vcard' => self::deserialize_string($stream),
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'žô„Ÿ' => [
'_' => 'messageMediaUnsupported',
],
'-×ôL' => self::deserialize_messageMediaDocument($stream),
';ñÝ' => self::deserialize_messageMediaWebPage($stream),
'?SÀ.' => [
'_' => 'messageMediaVenue',
'geo' => match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => self::deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
,
'title' => self::deserialize_string($stream),
'address' => self::deserialize_string($stream),
'provider' => self::deserialize_string($stream),
'venue_id' => self::deserialize_string($stream),
'venue_type' => self::deserialize_string($stream),
],
'�±ý' => [
'_' => 'messageMediaGame',
'game' => match (stream_get_contents($stream, 4)) {
';eù½' => self::deserialize_game($stream),
'¡Ïr0' => self::deserialize_type_Game(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ÓH¥ö' => self::deserialize_messageMediaInvoice($stream),
'fÆ@¹' => self::deserialize_messageMediaGeoLive($stream),
'˜çÖK' => [
'_' => 'messageMediaPoll',
'poll' => match (stream_get_contents($stream, 4)) {
'a�á†' => self::deserialize_poll($stream),
'¡Ïr0' => self::deserialize_type_Poll(self::gzdecode($stream)),
default => self::err($stream)
}
,
'results' => match (stream_get_contents($stream, 4)) {
' $ßz' => self::deserialize_pollResults($stream),
'¡Ïr0' => self::deserialize_type_PollResults(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'‹å~?' => [
'_' => 'messageMediaDice',
'value' => unpack('l', stream_get_contents($stream, 4))[1],
'emoticon' => self::deserialize_string($stream),
],
'ƒbËh' => self::deserialize_messageMediaStory($stream),
'°…­Ú' => self::deserialize_messageMediaGiveaway($stream),
'h™Æ' => self::deserialize_messageMediaGiveawayResults($stream),
'¡Ïr0' => self::deserialize_type_MessageMedia(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['entities'] = self::deserialize_type_array_of_MessageEntity(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
return $tmp;

    }
    private  function deserialize_type_PrivacyKey(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'0«.¼' => [
'_' => 'privacyKeyStatusTimestamp',
],
'úmP' => [
'_' => 'privacyKeyChatInvite',
],
'{+f=' => [
'_' => 'privacyKeyPhoneCall',
],
'ÈI9' => [
'_' => 'privacyKeyPhoneP2P',
],
'£Vìi' => [
'_' => 'privacyKeyForwards',
],
'í–' => [
'_' => 'privacyKeyProfilePhoto',
],
'mäšÑ' => [
'_' => 'privacyKeyPhoneNumber',
],
'+ÔÿB' => [
'_' => 'privacyKeyAddedByPhone',
],
'ô—' => [
'_' => 'privacyKeyVoiceMessages',
],
'a·†¤' => [
'_' => 'privacyKeyAbout',
],
'¡Ïr0' => $this->deserialize_type_PrivacyKey(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_updateReadHistoryInbox(mixed $stream): mixed {
$tmp = ['_' => 'updateReadHistoryInbox'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['folder_id'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['peer'] = match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['max_id'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['still_unread_count'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['pts'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['pts_count'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_updateReadMessagesContents(mixed $stream): mixed {
$tmp = ['_' => 'updateReadMessagesContents'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['messages'] = self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['pts'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['pts_count'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['date'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_updateChannelTooLong(mixed $stream): mixed {
$tmp = ['_' => 'updateChannelTooLong'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['channel_id'] = unpack('q', stream_get_contents($stream, 8))[1];
if (($flags & 1) !== 0) $tmp['pts'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_updateReadChannelInbox(mixed $stream): mixed {
$tmp = ['_' => 'updateReadChannelInbox'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['folder_id'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['channel_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['max_id'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['still_unread_count'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['pts'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_stickerSet(mixed $stream): mixed {
$tmp = ['_' => 'stickerSet'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['archived'] = ($flags & 2) !== 0;
$tmp['official'] = ($flags & 4) !== 0;
$tmp['masks'] = ($flags & 8) !== 0;
$tmp['animated'] = ($flags & 32) !== 0;
$tmp['videos'] = ($flags & 64) !== 0;
$tmp['emojis'] = ($flags & 128) !== 0;
$tmp['text_color'] = ($flags & 512) !== 0;
$tmp['channel_emoji_status'] = ($flags & 1024) !== 0;
if (($flags & 1) !== 0) $tmp['installed_date'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['access_hash'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['title'] = self::deserialize_string($stream);
$tmp['short_name'] = self::deserialize_string($stream);
if (($flags & 16) !== 0) $tmp['thumbs'] = self::deserialize_type_array_of_PhotoSize(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 16) !== 0) $tmp['thumb_dc_id'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 16) !== 0) $tmp['thumb_version'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 256) !== 0) $tmp['thumb_document_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['count'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['hash'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_StickerSet(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'ÜNÑ-' => self::deserialize_stickerSet($stream),
'¡Ïr0' => $this->deserialize_type_StickerSet(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_array_of_StickerPack(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'Ô™²' => [
'_' => 'stickerPack',
'emoticon' => self::deserialize_string($stream),
'documents' => self::deserialize_type_array_of_long(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => $this->deserialize_type_StickerPack(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_StickerKeyword(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'œ²þü' => [
'_' => 'stickerKeyword',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'keyword' => self::deserialize_type_array_of_string(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => $this->deserialize_type_StickerKeyword(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_messages___stickerSet(mixed $stream): mixed {
$this->referenceDatabase?->addOriginContext('messages.stickerSet');
$tmp = [
'_' => 'messages.stickerSet',
'set' => match (stream_get_contents($stream, 4)) {
'ÜNÑ-' => self::deserialize_stickerSet($stream),
'¡Ïr0' => self::deserialize_type_StickerSet(self::gzdecode($stream)),
default => self::err($stream)
}
,
'packs' => self::deserialize_type_array_of_StickerPack(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'keywords' => self::deserialize_type_array_of_StickerKeyword(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'documents' => self::deserialize_type_array_of_Document(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
];
$this->referenceDatabase?->addOrigin($tmp);
return $tmp;

    }
    private  function deserialize_type_messages___StickerSet(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'?n' => self::deserialize_messages___stickerSet($stream),
'ë$ùÓ' => [
'_' => 'messages.stickerSetNotModified',
],
'¡Ïr0' => $this->deserialize_type_messages___StickerSet(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_updateStickerSetsOrder(mixed $stream): mixed {
$tmp = ['_' => 'updateStickerSetsOrder'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['masks'] = ($flags & 1) !== 0;
$tmp['emojis'] = ($flags & 2) !== 0;
$tmp['order'] = self::deserialize_type_array_of_long(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
return $tmp;

    }
    private  function deserialize_updateStickerSets(mixed $stream): mixed {
$tmp = ['_' => 'updateStickerSets'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['masks'] = ($flags & 1) !== 0;
$tmp['emojis'] = ($flags & 2) !== 0;
return $tmp;

    }
    private  function deserialize_type_InlineQueryPeerType(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'�í�0' => [
'_' => 'inlineQueryPeerTypeSameBotPM',
],
'¬<ƒ' => [
'_' => 'inlineQueryPeerTypePM',
],
'
Åf×' => [
'_' => 'inlineQueryPeerTypeChat',
],
'C¾Ä^' => [
'_' => 'inlineQueryPeerTypeMegagroup',
],
'šî4c' => [
'_' => 'inlineQueryPeerTypeBroadcast',
],
'-;' => [
'_' => 'inlineQueryPeerTypeBotPM',
],
'¡Ïr0' => $this->deserialize_type_InlineQueryPeerType(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_updateBotInlineQuery(mixed $stream): mixed {
$tmp = ['_' => 'updateBotInlineQuery'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['query_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['user_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['query'] = self::deserialize_string($stream);
if (($flags & 1) !== 0) $tmp['geo'] = match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => self::deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 2) !== 0) $tmp['peer_type'] = match (stream_get_contents($stream, 4)) {
'�í�0' => [
'_' => 'inlineQueryPeerTypeSameBotPM',
],
'¬<ƒ' => [
'_' => 'inlineQueryPeerTypePM',
],
'
Åf×' => [
'_' => 'inlineQueryPeerTypeChat',
],
'C¾Ä^' => [
'_' => 'inlineQueryPeerTypeMegagroup',
],
'šî4c' => [
'_' => 'inlineQueryPeerTypeBroadcast',
],
'-;' => [
'_' => 'inlineQueryPeerTypeBotPM',
],
'¡Ïr0' => self::deserialize_type_InlineQueryPeerType(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['offset'] = self::deserialize_string($stream);
return $tmp;

    }
    private  function deserialize_type_InputBotInlineMessageID(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'‰=‰' => [
'_' => 'inputBotInlineMessageID',
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'×Ù¶' => [
'_' => 'inputBotInlineMessageID64',
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'owner_id' => unpack('q', stream_get_contents($stream, 8))[1],
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => $this->deserialize_type_InputBotInlineMessageID(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_updateBotInlineSend(mixed $stream): mixed {
$tmp = ['_' => 'updateBotInlineSend'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['user_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['query'] = self::deserialize_string($stream);
if (($flags & 1) !== 0) $tmp['geo'] = match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => self::deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['id'] = self::deserialize_string($stream);
if (($flags & 2) !== 0) $tmp['msg_id'] = match (stream_get_contents($stream, 4)) {
'‰=‰' => [
'_' => 'inputBotInlineMessageID',
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'×Ù¶' => [
'_' => 'inputBotInlineMessageID64',
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'owner_id' => unpack('q', stream_get_contents($stream, 8))[1],
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputBotInlineMessageID(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_updateBotCallbackQuery(mixed $stream): mixed {
$tmp = ['_' => 'updateBotCallbackQuery'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['query_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['user_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['peer'] = match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['msg_id'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['chat_instance'] = unpack('q', stream_get_contents($stream, 8))[1];
if (($flags & 1) !== 0) $tmp['data'] = self::deserialize_bytes($stream);
if (($flags & 2) !== 0) $tmp['game_short_name'] = self::deserialize_string($stream);
return $tmp;

    }
    private  function deserialize_updateInlineBotCallbackQuery(mixed $stream): mixed {
$tmp = ['_' => 'updateInlineBotCallbackQuery'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['query_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['user_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['msg_id'] = match (stream_get_contents($stream, 4)) {
'‰=‰' => [
'_' => 'inputBotInlineMessageID',
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'×Ù¶' => [
'_' => 'inputBotInlineMessageID64',
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'owner_id' => unpack('q', stream_get_contents($stream, 8))[1],
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputBotInlineMessageID(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['chat_instance'] = unpack('q', stream_get_contents($stream, 8))[1];
if (($flags & 1) !== 0) $tmp['data'] = self::deserialize_bytes($stream);
if (($flags & 2) !== 0) $tmp['game_short_name'] = self::deserialize_string($stream);
return $tmp;

    }
    private  function deserialize_draftMessageEmpty(mixed $stream): mixed {
$tmp = ['_' => 'draftMessageEmpty'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['date'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_inputReplyToMessage(mixed $stream): mixed {
$tmp = ['_' => 'inputReplyToMessage'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['reply_to_msg_id'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['top_msg_id'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 2) !== 0) $tmp['reply_to_peer_id'] = match (stream_get_contents($stream, 4)) {
'ê;' => [
'_' => 'inputPeerEmpty',
],
'É~ }' => [
'_' => 'inputPeerSelf',
],
'¹\\©5' => [
'_' => 'inputPeerChat',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'L¥èÝ' => [
'_' => 'inputPeerUser',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ü»¼\'' => [
'_' => 'inputPeerChannel',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'
{¨' => [
'_' => 'inputPeerUserFromMessage',
'peer' => self::deserialize_type_InputPeer($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'@*½' => [
'_' => 'inputPeerChannelFromMessage',
'peer' => self::deserialize_type_InputPeer($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputPeer(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 4) !== 0) $tmp['quote_text'] = self::deserialize_string($stream);
if (($flags & 8) !== 0) $tmp['quote_entities'] = self::deserialize_type_array_of_MessageEntity(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 16) !== 0) $tmp['quote_offset'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_InputReplyTo(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'ÕöÀ"' => self::deserialize_inputReplyToMessage($stream),
':2�X' => [
'_' => 'inputReplyToStory',
'peer' => match (stream_get_contents($stream, 4)) {
'ê;' => [
'_' => 'inputPeerEmpty',
],
'É~ }' => [
'_' => 'inputPeerSelf',
],
'¹\\©5' => [
'_' => 'inputPeerChat',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'L¥èÝ' => [
'_' => 'inputPeerUser',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ü»¼\'' => [
'_' => 'inputPeerChannel',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'
{¨' => [
'_' => 'inputPeerUserFromMessage',
'peer' => self::deserialize_type_InputPeer($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'@*½' => [
'_' => 'inputPeerChannelFromMessage',
'peer' => self::deserialize_type_InputPeer($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputPeer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'story_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => $this->deserialize_type_InputReplyTo(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_InputFile(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'ò/õ' => [
'_' => 'inputFile',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'parts' => unpack('l', stream_get_contents($stream, 4))[1],
'name' => self::deserialize_string($stream),
'md5_checksum' => self::deserialize_string($stream),
],
'µOú' => [
'_' => 'inputFileBig',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'parts' => unpack('l', stream_get_contents($stream, 4))[1],
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => $this->deserialize_type_InputFile(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_array_of_InputDocument(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'®êðr' => [
'_' => 'inputDocumentEmpty',
],
'uµ¿' => [
'_' => 'inputDocument',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'file_reference' => self::deserialize_bytes($stream),
],
'¡Ïr0' => $this->deserialize_type_InputDocument(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_inputMediaUploadedPhoto(mixed $stream): mixed {
$tmp = ['_' => 'inputMediaUploadedPhoto'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['spoiler'] = ($flags & 4) !== 0;
$tmp['file'] = match (stream_get_contents($stream, 4)) {
'ò/õ' => [
'_' => 'inputFile',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'parts' => unpack('l', stream_get_contents($stream, 4))[1],
'name' => self::deserialize_string($stream),
'md5_checksum' => self::deserialize_string($stream),
],
'µOú' => [
'_' => 'inputFileBig',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'parts' => unpack('l', stream_get_contents($stream, 4))[1],
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_InputFile(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 1) !== 0) $tmp['stickers'] = self::deserialize_type_array_of_InputDocument(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 2) !== 0) $tmp['ttl_seconds'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_InputPhoto(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'¿×' => [
'_' => 'inputPhotoEmpty',
],
'J¹³;' => [
'_' => 'inputPhoto',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'file_reference' => self::deserialize_bytes($stream),
],
'¡Ïr0' => $this->deserialize_type_InputPhoto(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_inputMediaPhoto(mixed $stream): mixed {
$tmp = ['_' => 'inputMediaPhoto'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['spoiler'] = ($flags & 2) !== 0;
$tmp['id'] = match (stream_get_contents($stream, 4)) {
'¿×' => [
'_' => 'inputPhotoEmpty',
],
'J¹³;' => [
'_' => 'inputPhoto',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'file_reference' => self::deserialize_bytes($stream),
],
'¡Ïr0' => self::deserialize_type_InputPhoto(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 1) !== 0) $tmp['ttl_seconds'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_inputGeoPoint(mixed $stream): mixed {
$tmp = ['_' => 'inputGeoPoint'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['lat'] = unpack('d', stream_get_contents($stream, 8))[1];
$tmp['long'] = unpack('d', stream_get_contents($stream, 8))[1];
if (($flags & 1) !== 0) $tmp['accuracy_radius'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_InputGeoPoint(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'Ö#Áä' => [
'_' => 'inputGeoPointEmpty',
],
'¯/"H' => self::deserialize_inputGeoPoint($stream),
'¡Ïr0' => $this->deserialize_type_InputGeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_inputMediaUploadedDocument(mixed $stream): mixed {
$tmp = ['_' => 'inputMediaUploadedDocument'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['nosound_video'] = ($flags & 8) !== 0;
$tmp['force_file'] = ($flags & 16) !== 0;
$tmp['spoiler'] = ($flags & 32) !== 0;
$tmp['file'] = match (stream_get_contents($stream, 4)) {
'ò/õ' => [
'_' => 'inputFile',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'parts' => unpack('l', stream_get_contents($stream, 4))[1],
'name' => self::deserialize_string($stream),
'md5_checksum' => self::deserialize_string($stream),
],
'µOú' => [
'_' => 'inputFileBig',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'parts' => unpack('l', stream_get_contents($stream, 4))[1],
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_InputFile(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 4) !== 0) $tmp['thumb'] = match (stream_get_contents($stream, 4)) {
'ò/õ' => [
'_' => 'inputFile',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'parts' => unpack('l', stream_get_contents($stream, 4))[1],
'name' => self::deserialize_string($stream),
'md5_checksum' => self::deserialize_string($stream),
],
'µOú' => [
'_' => 'inputFileBig',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'parts' => unpack('l', stream_get_contents($stream, 4))[1],
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_InputFile(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['mime_type'] = self::deserialize_string($stream);
$tmp['attributes'] = self::deserialize_type_array_of_DocumentAttribute(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 1) !== 0) $tmp['stickers'] = self::deserialize_type_array_of_InputDocument(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 2) !== 0) $tmp['ttl_seconds'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_InputDocument(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'®êðr' => [
'_' => 'inputDocumentEmpty',
],
'uµ¿' => [
'_' => 'inputDocument',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'file_reference' => self::deserialize_bytes($stream),
],
'¡Ïr0' => $this->deserialize_type_InputDocument(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_inputMediaDocument(mixed $stream): mixed {
$tmp = ['_' => 'inputMediaDocument'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['spoiler'] = ($flags & 4) !== 0;
$tmp['id'] = match (stream_get_contents($stream, 4)) {
'®êðr' => [
'_' => 'inputDocumentEmpty',
],
'uµ¿' => [
'_' => 'inputDocument',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'file_reference' => self::deserialize_bytes($stream),
],
'¡Ïr0' => self::deserialize_type_InputDocument(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 1) !== 0) $tmp['ttl_seconds'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 2) !== 0) $tmp['query'] = self::deserialize_string($stream);
return $tmp;

    }
    private  function deserialize_inputMediaPhotoExternal(mixed $stream): mixed {
$tmp = ['_' => 'inputMediaPhotoExternal'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['spoiler'] = ($flags & 2) !== 0;
$tmp['url'] = self::deserialize_string($stream);
if (($flags & 1) !== 0) $tmp['ttl_seconds'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_inputMediaDocumentExternal(mixed $stream): mixed {
$tmp = ['_' => 'inputMediaDocumentExternal'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['spoiler'] = ($flags & 2) !== 0;
$tmp['url'] = self::deserialize_string($stream);
if (($flags & 1) !== 0) $tmp['ttl_seconds'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_InputGame(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'w>,' => [
'_' => 'inputGameID',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'
è1Ã' => [
'_' => 'inputGameShortName',
'bot_id' => match (stream_get_contents($stream, 4)) {
'Ï†ˆ¹' => [
'_' => 'inputUserEmpty',
],
'?±Á÷' => [
'_' => 'inputUserSelf',
],
'ÆXò' => [
'_' => 'inputUser',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'âH¤' => [
'_' => 'inputUserFromMessage',
'peer' => match (stream_get_contents($stream, 4)) {
'ê;' => [
'_' => 'inputPeerEmpty',
],
'É~ }' => [
'_' => 'inputPeerSelf',
],
'¹\\©5' => [
'_' => 'inputPeerChat',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'L¥èÝ' => [
'_' => 'inputPeerUser',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ü»¼\'' => [
'_' => 'inputPeerChannel',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'
{¨' => [
'_' => 'inputPeerUserFromMessage',
'peer' => self::deserialize_type_InputPeer($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'@*½' => [
'_' => 'inputPeerChannelFromMessage',
'peer' => self::deserialize_type_InputPeer($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputPeer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputUser(self::gzdecode($stream)),
default => self::err($stream)
}
,
'short_name' => self::deserialize_string($stream),
],
'¡Ïr0' => $this->deserialize_type_InputGame(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_InputWebDocument(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'MCí›' => [
'_' => 'inputWebDocument',
'url' => self::deserialize_string($stream),
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'attributes' => self::deserialize_type_array_of_DocumentAttribute(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => $this->deserialize_type_InputWebDocument(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_array_of_LabeledPrice(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'øk)Ë' => [
'_' => 'labeledPrice',
'label' => self::deserialize_string($stream),
'amount' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => $this->deserialize_type_LabeledPrice(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_invoice(mixed $stream): mixed {
$tmp = ['_' => 'invoice'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['test'] = ($flags & 1) !== 0;
$tmp['name_requested'] = ($flags & 2) !== 0;
$tmp['phone_requested'] = ($flags & 4) !== 0;
$tmp['email_requested'] = ($flags & 8) !== 0;
$tmp['shipping_address_requested'] = ($flags & 16) !== 0;
$tmp['flexible'] = ($flags & 32) !== 0;
$tmp['phone_to_provider'] = ($flags & 64) !== 0;
$tmp['email_to_provider'] = ($flags & 128) !== 0;
$tmp['recurring'] = ($flags & 512) !== 0;
$tmp['currency'] = self::deserialize_string($stream);
$tmp['prices'] = self::deserialize_type_array_of_LabeledPrice(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 256) !== 0) $tmp['max_tip_amount'] = unpack('q', stream_get_contents($stream, 8))[1];
if (($flags & 256) !== 0) $tmp['suggested_tip_amounts'] = self::deserialize_type_array_of_long(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 1024) !== 0) $tmp['terms_url'] = self::deserialize_string($stream);
return $tmp;

    }
    private  function deserialize_type_Invoice(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'Z¹]' => self::deserialize_invoice($stream),
'¡Ïr0' => $this->deserialize_type_Invoice(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_inputMediaInvoice(mixed $stream): mixed {
$tmp = ['_' => 'inputMediaInvoice'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['title'] = self::deserialize_string($stream);
$tmp['description'] = self::deserialize_string($stream);
if (($flags & 1) !== 0) $tmp['photo'] = match (stream_get_contents($stream, 4)) {
'MCí›' => [
'_' => 'inputWebDocument',
'url' => self::deserialize_string($stream),
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'attributes' => self::deserialize_type_array_of_DocumentAttribute(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => self::deserialize_type_InputWebDocument(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['invoice'] = match (stream_get_contents($stream, 4)) {
'Z¹]' => self::deserialize_invoice($stream),
'¡Ïr0' => self::deserialize_type_Invoice(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['payload'] = self::deserialize_bytes($stream);
$tmp['provider'] = self::deserialize_string($stream);
$tmp['provider_data'] = match (stream_get_contents($stream, 4)) {
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'¡Ïr0' => self::deserialize_type_DataJSON(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 2) !== 0) $tmp['start_param'] = self::deserialize_string($stream);
if (($flags & 4) !== 0) $tmp['extended_media'] = $this->deserialize_type_InputMedia($stream);
return $tmp;

    }
    private  function deserialize_inputMediaGeoLive(mixed $stream): mixed {
$tmp = ['_' => 'inputMediaGeoLive'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['stopped'] = ($flags & 1) !== 0;
$tmp['geo_point'] = match (stream_get_contents($stream, 4)) {
'Ö#Áä' => [
'_' => 'inputGeoPointEmpty',
],
'¯/"H' => self::deserialize_inputGeoPoint($stream),
'¡Ïr0' => self::deserialize_type_InputGeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 4) !== 0) $tmp['heading'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 2) !== 0) $tmp['period'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 8) !== 0) $tmp['proximity_notification_radius'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_inputMediaPoll(mixed $stream): mixed {
$tmp = ['_' => 'inputMediaPoll'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['poll'] = match (stream_get_contents($stream, 4)) {
'a�á†' => self::deserialize_poll($stream),
'¡Ïr0' => self::deserialize_type_Poll(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 1) !== 0) $tmp['correct_answers'] = self::deserialize_type_array_of_bytes(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 2) !== 0) $tmp['solution'] = self::deserialize_string($stream);
if (($flags & 2) !== 0) $tmp['solution_entities'] = self::deserialize_type_array_of_MessageEntity(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
return $tmp;

    }
    private  function deserialize_inputMediaWebPage(mixed $stream): mixed {
$tmp = ['_' => 'inputMediaWebPage'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['force_large_media'] = ($flags & 1) !== 0;
$tmp['force_small_media'] = ($flags & 2) !== 0;
$tmp['optional'] = ($flags & 4) !== 0;
$tmp['url'] = self::deserialize_string($stream);
return $tmp;

    }
    private  function deserialize_type_InputMedia(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'õd–' => [
'_' => 'inputMediaEmpty',
],
'}(' => self::deserialize_inputMediaUploadedPhoto($stream),
'5º³' => self::deserialize_inputMediaPhoto($stream),
'DAÄù' => [
'_' => 'inputMediaGeoPoint',
'geo_point' => match (stream_get_contents($stream, 4)) {
'Ö#Áä' => [
'_' => 'inputGeoPointEmpty',
],
'¯/"H' => self::deserialize_inputGeoPoint($stream),
'¡Ïr0' => self::deserialize_type_InputGeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'û}«ø' => [
'_' => 'inputMediaContact',
'phone_number' => self::deserialize_string($stream),
'first_name' => self::deserialize_string($stream),
'last_name' => self::deserialize_string($stream),
'vcard' => self::deserialize_string($stream),
],
'ÁÆ8[' => self::deserialize_inputMediaUploadedDocument($stream),
'X0G3' => self::deserialize_inputMediaDocument($stream),
'=Á' => [
'_' => 'inputMediaVenue',
'geo_point' => match (stream_get_contents($stream, 4)) {
'Ö#Áä' => [
'_' => 'inputGeoPointEmpty',
],
'¯/"H' => self::deserialize_inputGeoPoint($stream),
'¡Ïr0' => self::deserialize_type_InputGeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
,
'title' => self::deserialize_string($stream),
'address' => self::deserialize_string($stream),
'provider' => self::deserialize_string($stream),
'venue_id' => self::deserialize_string($stream),
'venue_type' => self::deserialize_string($stream),
],
'þ»å' => self::deserialize_inputMediaPhotoExternal($stream),
'™ÜRû' => self::deserialize_inputMediaDocumentExternal($stream),
'óC?Ó' => [
'_' => 'inputMediaGame',
'id' => match (stream_get_contents($stream, 4)) {
'w>,' => [
'_' => 'inputGameID',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'
è1Ã' => [
'_' => 'inputGameShortName',
'bot_id' => match (stream_get_contents($stream, 4)) {
'Ï†ˆ¹' => [
'_' => 'inputUserEmpty',
],
'?±Á÷' => [
'_' => 'inputUserSelf',
],
'ÆXò' => [
'_' => 'inputUser',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'âH¤' => [
'_' => 'inputUserFromMessage',
'peer' => match (stream_get_contents($stream, 4)) {
'ê;' => [
'_' => 'inputPeerEmpty',
],
'É~ }' => [
'_' => 'inputPeerSelf',
],
'¹\\©5' => [
'_' => 'inputPeerChat',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'L¥èÝ' => [
'_' => 'inputPeerUser',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ü»¼\'' => [
'_' => 'inputPeerChannel',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'
{¨' => [
'_' => 'inputPeerUserFromMessage',
'peer' => self::deserialize_type_InputPeer($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'@*½' => [
'_' => 'inputPeerChannelFromMessage',
'peer' => self::deserialize_type_InputPeer($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputPeer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputUser(self::gzdecode($stream)),
default => self::err($stream)
}
,
'short_name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_InputGame(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Õ¦µŽ' => self::deserialize_inputMediaInvoice($stream),
'C¨—' => self::deserialize_inputMediaGeoLive($stream),
'ñå”' => self::deserialize_inputMediaPoll($stream),
'{¿oæ' => [
'_' => 'inputMediaDice',
'emoticon' => self::deserialize_string($stream),
],
'x×ý‰' => [
'_' => 'inputMediaStory',
'peer' => match (stream_get_contents($stream, 4)) {
'ê;' => [
'_' => 'inputPeerEmpty',
],
'É~ }' => [
'_' => 'inputPeerSelf',
],
'¹\\©5' => [
'_' => 'inputPeerChat',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'L¥èÝ' => [
'_' => 'inputPeerUser',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ü»¼\'' => [
'_' => 'inputPeerChannel',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'
{¨' => [
'_' => 'inputPeerUserFromMessage',
'peer' => self::deserialize_type_InputPeer($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'@*½' => [
'_' => 'inputPeerChannelFromMessage',
'peer' => self::deserialize_type_InputPeer($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputPeer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'IˆÂ' => self::deserialize_inputMediaWebPage($stream),
'¡Ïr0' => $this->deserialize_type_InputMedia(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_draftMessage(mixed $stream): mixed {
$tmp = ['_' => 'draftMessage'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['no_webpage'] = ($flags & 2) !== 0;
$tmp['invert_media'] = ($flags & 64) !== 0;
if (($flags & 16) !== 0) $tmp['reply_to'] = match (stream_get_contents($stream, 4)) {
'ÕöÀ"' => self::deserialize_inputReplyToMessage($stream),
':2�X' => [
'_' => 'inputReplyToStory',
'peer' => match (stream_get_contents($stream, 4)) {
'ê;' => [
'_' => 'inputPeerEmpty',
],
'É~ }' => [
'_' => 'inputPeerSelf',
],
'¹\\©5' => [
'_' => 'inputPeerChat',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'L¥èÝ' => [
'_' => 'inputPeerUser',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ü»¼\'' => [
'_' => 'inputPeerChannel',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'
{¨' => [
'_' => 'inputPeerUserFromMessage',
'peer' => self::deserialize_type_InputPeer($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'@*½' => [
'_' => 'inputPeerChannelFromMessage',
'peer' => self::deserialize_type_InputPeer($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputPeer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'story_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_InputReplyTo(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['message'] = self::deserialize_string($stream);
if (($flags & 8) !== 0) $tmp['entities'] = self::deserialize_type_array_of_MessageEntity(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 32) !== 0) $tmp['media'] = match (stream_get_contents($stream, 4)) {
'õd–' => [
'_' => 'inputMediaEmpty',
],
'}(' => self::deserialize_inputMediaUploadedPhoto($stream),
'5º³' => self::deserialize_inputMediaPhoto($stream),
'DAÄù' => [
'_' => 'inputMediaGeoPoint',
'geo_point' => match (stream_get_contents($stream, 4)) {
'Ö#Áä' => [
'_' => 'inputGeoPointEmpty',
],
'¯/"H' => self::deserialize_inputGeoPoint($stream),
'¡Ïr0' => self::deserialize_type_InputGeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'û}«ø' => [
'_' => 'inputMediaContact',
'phone_number' => self::deserialize_string($stream),
'first_name' => self::deserialize_string($stream),
'last_name' => self::deserialize_string($stream),
'vcard' => self::deserialize_string($stream),
],
'ÁÆ8[' => self::deserialize_inputMediaUploadedDocument($stream),
'X0G3' => self::deserialize_inputMediaDocument($stream),
'=Á' => [
'_' => 'inputMediaVenue',
'geo_point' => match (stream_get_contents($stream, 4)) {
'Ö#Áä' => [
'_' => 'inputGeoPointEmpty',
],
'¯/"H' => self::deserialize_inputGeoPoint($stream),
'¡Ïr0' => self::deserialize_type_InputGeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
,
'title' => self::deserialize_string($stream),
'address' => self::deserialize_string($stream),
'provider' => self::deserialize_string($stream),
'venue_id' => self::deserialize_string($stream),
'venue_type' => self::deserialize_string($stream),
],
'þ»å' => self::deserialize_inputMediaPhotoExternal($stream),
'™ÜRû' => self::deserialize_inputMediaDocumentExternal($stream),
'óC?Ó' => [
'_' => 'inputMediaGame',
'id' => match (stream_get_contents($stream, 4)) {
'w>,' => [
'_' => 'inputGameID',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'
è1Ã' => [
'_' => 'inputGameShortName',
'bot_id' => match (stream_get_contents($stream, 4)) {
'Ï†ˆ¹' => [
'_' => 'inputUserEmpty',
],
'?±Á÷' => [
'_' => 'inputUserSelf',
],
'ÆXò' => [
'_' => 'inputUser',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'âH¤' => [
'_' => 'inputUserFromMessage',
'peer' => match (stream_get_contents($stream, 4)) {
'ê;' => [
'_' => 'inputPeerEmpty',
],
'É~ }' => [
'_' => 'inputPeerSelf',
],
'¹\\©5' => [
'_' => 'inputPeerChat',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'L¥èÝ' => [
'_' => 'inputPeerUser',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ü»¼\'' => [
'_' => 'inputPeerChannel',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'
{¨' => [
'_' => 'inputPeerUserFromMessage',
'peer' => self::deserialize_type_InputPeer($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'@*½' => [
'_' => 'inputPeerChannelFromMessage',
'peer' => self::deserialize_type_InputPeer($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputPeer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputUser(self::gzdecode($stream)),
default => self::err($stream)
}
,
'short_name' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_InputGame(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Õ¦µŽ' => self::deserialize_inputMediaInvoice($stream),
'C¨—' => self::deserialize_inputMediaGeoLive($stream),
'ñå”' => self::deserialize_inputMediaPoll($stream),
'{¿oæ' => [
'_' => 'inputMediaDice',
'emoticon' => self::deserialize_string($stream),
],
'x×ý‰' => [
'_' => 'inputMediaStory',
'peer' => match (stream_get_contents($stream, 4)) {
'ê;' => [
'_' => 'inputPeerEmpty',
],
'É~ }' => [
'_' => 'inputPeerSelf',
],
'¹\\©5' => [
'_' => 'inputPeerChat',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'L¥èÝ' => [
'_' => 'inputPeerUser',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ü»¼\'' => [
'_' => 'inputPeerChannel',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'
{¨' => [
'_' => 'inputPeerUserFromMessage',
'peer' => self::deserialize_type_InputPeer($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'@*½' => [
'_' => 'inputPeerChannelFromMessage',
'peer' => self::deserialize_type_InputPeer($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputPeer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'IˆÂ' => self::deserialize_inputMediaWebPage($stream),
'¡Ïr0' => self::deserialize_type_InputMedia(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['date'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_DraftMessage(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'„' => self::deserialize_draftMessageEmpty($stream),
'ï÷Ì?' => self::deserialize_draftMessage($stream),
'¡Ïr0' => $this->deserialize_type_DraftMessage(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_updateDraftMessage(mixed $stream): mixed {
$tmp = ['_' => 'updateDraftMessage'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['peer'] = match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 1) !== 0) $tmp['top_msg_id'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['draft'] = match (stream_get_contents($stream, 4)) {
'„' => self::deserialize_draftMessageEmpty($stream),
'ï÷Ì?' => self::deserialize_draftMessage($stream),
'¡Ïr0' => self::deserialize_type_DraftMessage(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_type_DialogPeer(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'¿må' => [
'_' => 'dialogPeer',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'âEQ' => [
'_' => 'dialogPeerFolder',
'folder_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => $this->deserialize_type_DialogPeer(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_updateDialogPinned(mixed $stream): mixed {
$tmp = ['_' => 'updateDialogPinned'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['pinned'] = ($flags & 1) !== 0;
if (($flags & 2) !== 0) $tmp['folder_id'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['peer'] = match (stream_get_contents($stream, 4)) {
'¿må' => [
'_' => 'dialogPeer',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'âEQ' => [
'_' => 'dialogPeerFolder',
'folder_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_DialogPeer(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_type_array_of_DialogPeer(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'¿må' => [
'_' => 'dialogPeer',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'âEQ' => [
'_' => 'dialogPeerFolder',
'folder_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_DialogPeer(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_updatePinnedDialogs(mixed $stream): mixed {
$tmp = ['_' => 'updatePinnedDialogs'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
if (($flags & 2) !== 0) $tmp['folder_id'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['order'] = self::deserialize_type_array_of_DialogPeer(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
return $tmp;

    }
    private  function deserialize_updateBotPrecheckoutQuery(mixed $stream): mixed {
$tmp = ['_' => 'updateBotPrecheckoutQuery'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['query_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['user_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['payload'] = self::deserialize_bytes($stream);
if (($flags & 1) !== 0) $tmp['info'] = match (stream_get_contents($stream, 4)) {
'”?œ�' => self::deserialize_paymentRequestedInfo($stream),
'¡Ïr0' => self::deserialize_type_PaymentRequestedInfo(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 2) !== 0) $tmp['shipping_option_id'] = self::deserialize_string($stream);
$tmp['currency'] = self::deserialize_string($stream);
$tmp['total_amount'] = unpack('q', stream_get_contents($stream, 8))[1];
return $tmp;

    }
    private  function deserialize_phoneCallProtocol(mixed $stream): mixed {
$tmp = ['_' => 'phoneCallProtocol'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['udp_p2p'] = ($flags & 1) !== 0;
$tmp['udp_reflector'] = ($flags & 2) !== 0;
$tmp['min_layer'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['max_layer'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['library_versions'] = self::deserialize_type_array_of_string(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
return $tmp;

    }
    private  function deserialize_type_PhoneCallProtocol(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'È�‡ü' => self::deserialize_phoneCallProtocol($stream),
'¡Ïr0' => $this->deserialize_type_PhoneCallProtocol(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_phoneCallWaiting(mixed $stream): mixed {
$tmp = ['_' => 'phoneCallWaiting'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['video'] = ($flags & 64) !== 0;
$tmp['id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['access_hash'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['date'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['admin_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['participant_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['protocol'] = match (stream_get_contents($stream, 4)) {
'È�‡ü' => self::deserialize_phoneCallProtocol($stream),
'¡Ïr0' => self::deserialize_type_PhoneCallProtocol(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 1) !== 0) $tmp['receive_date'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_phoneCallRequested(mixed $stream): mixed {
$tmp = ['_' => 'phoneCallRequested'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['video'] = ($flags & 64) !== 0;
$tmp['id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['access_hash'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['date'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['admin_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['participant_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['g_a_hash'] = self::deserialize_bytes($stream);
$tmp['protocol'] = match (stream_get_contents($stream, 4)) {
'È�‡ü' => self::deserialize_phoneCallProtocol($stream),
'¡Ïr0' => self::deserialize_type_PhoneCallProtocol(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_phoneCallAccepted(mixed $stream): mixed {
$tmp = ['_' => 'phoneCallAccepted'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['video'] = ($flags & 64) !== 0;
$tmp['id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['access_hash'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['date'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['admin_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['participant_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['g_b'] = self::deserialize_bytes($stream);
$tmp['protocol'] = match (stream_get_contents($stream, 4)) {
'È�‡ü' => self::deserialize_phoneCallProtocol($stream),
'¡Ïr0' => self::deserialize_type_PhoneCallProtocol(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_phoneConnection(mixed $stream): mixed {
$tmp = ['_' => 'phoneConnection'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['tcp'] = ($flags & 1) !== 0;
$tmp['id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['ip'] = self::deserialize_string($stream);
$tmp['ipv6'] = self::deserialize_string($stream);
$tmp['port'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['peer_tag'] = self::deserialize_string($stream);
return $tmp;

    }
    private  function deserialize_phoneConnectionWebrtc(mixed $stream): mixed {
$tmp = ['_' => 'phoneConnectionWebrtc'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['turn'] = ($flags & 1) !== 0;
$tmp['stun'] = ($flags & 2) !== 0;
$tmp['id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['ip'] = self::deserialize_string($stream);
$tmp['ipv6'] = self::deserialize_string($stream);
$tmp['port'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['username'] = self::deserialize_string($stream);
$tmp['password'] = self::deserialize_string($stream);
return $tmp;

    }
    private  function deserialize_type_array_of_PhoneConnection(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'Ç#Áœ' => self::deserialize_phoneConnection($stream),
'uã_c' => self::deserialize_phoneConnectionWebrtc($stream),
'¡Ïr0' => $this->deserialize_type_PhoneConnection(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_phoneCall(mixed $stream): mixed {
$tmp = ['_' => 'phoneCall'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['p2p_allowed'] = ($flags & 32) !== 0;
$tmp['video'] = ($flags & 64) !== 0;
$tmp['id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['access_hash'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['date'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['admin_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['participant_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['g_a_or_b'] = self::deserialize_bytes($stream);
$tmp['key_fingerprint'] = stream_get_contents($stream, 8);
$tmp['protocol'] = match (stream_get_contents($stream, 4)) {
'È�‡ü' => self::deserialize_phoneCallProtocol($stream),
'¡Ïr0' => self::deserialize_type_PhoneCallProtocol(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['connections'] = self::deserialize_type_array_of_PhoneConnection(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['start_date'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_phoneCallDiscarded(mixed $stream): mixed {
$tmp = ['_' => 'phoneCallDiscarded'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['need_rating'] = ($flags & 4) !== 0;
$tmp['need_debug'] = ($flags & 8) !== 0;
$tmp['video'] = ($flags & 64) !== 0;
$tmp['id'] = unpack('q', stream_get_contents($stream, 8))[1];
if (($flags & 1) !== 0) $tmp['reason'] = match (stream_get_contents($stream, 4)) {
'#ä…' => [
'_' => 'phoneCallDiscardReasonMissed',
],
' Á•à' => [
'_' => 'phoneCallDiscardReasonDisconnect',
],
'�Æ­W' => [
'_' => 'phoneCallDiscardReasonHangup',
],
'Éè÷ú' => [
'_' => 'phoneCallDiscardReasonBusy',
],
'¡Ïr0' => self::deserialize_type_PhoneCallDiscardReason(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 2) !== 0) $tmp['duration'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_PhoneCall(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'ÉfS' => [
'_' => 'phoneCallEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'o"Å' => self::deserialize_phoneCallWaiting($stream),
'í°' => self::deserialize_phoneCallRequested($stream),
'Ã`6' => self::deserialize_phoneCallAccepted($stream),
'g|–' => self::deserialize_phoneCall($stream),
'áMÊP' => self::deserialize_phoneCallDiscarded($stream),
'¡Ïr0' => $this->deserialize_type_PhoneCall(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_langPackStringPluralized(mixed $stream): mixed {
$tmp = ['_' => 'langPackStringPluralized'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['key'] = self::deserialize_string($stream);
if (($flags & 1) !== 0) $tmp['zero_value'] = self::deserialize_string($stream);
if (($flags & 2) !== 0) $tmp['one_value'] = self::deserialize_string($stream);
if (($flags & 4) !== 0) $tmp['two_value'] = self::deserialize_string($stream);
if (($flags & 8) !== 0) $tmp['few_value'] = self::deserialize_string($stream);
if (($flags & 16) !== 0) $tmp['many_value'] = self::deserialize_string($stream);
$tmp['other_value'] = self::deserialize_string($stream);
return $tmp;

    }
    private  function deserialize_type_array_of_LangPackString(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'ö�ÑÊ' => [
'_' => 'langPackString',
'key' => self::deserialize_string($stream),
'value' => self::deserialize_string($stream),
],
'Ÿ¬Gl' => self::deserialize_langPackStringPluralized($stream),
'²îy)' => [
'_' => 'langPackStringDeleted',
'key' => self::deserialize_string($stream),
],
'¡Ïr0' => $this->deserialize_type_LangPackString(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_LangPackDifference(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'öÁ…ó' => [
'_' => 'langPackDifference',
'lang_code' => self::deserialize_string($stream),
'from_version' => unpack('l', stream_get_contents($stream, 4))[1],
'version' => unpack('l', stream_get_contents($stream, 4))[1],
'strings' => self::deserialize_type_array_of_LangPackString(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => $this->deserialize_type_LangPackDifference(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_updateChannelReadMessagesContents(mixed $stream): mixed {
$tmp = ['_' => 'updateChannelReadMessagesContents'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['channel_id'] = unpack('q', stream_get_contents($stream, 8))[1];
if (($flags & 1) !== 0) $tmp['top_msg_id'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['messages'] = self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
return $tmp;

    }
    private  function deserialize_updateDialogUnreadMark(mixed $stream): mixed {
$tmp = ['_' => 'updateDialogUnreadMark'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['unread'] = ($flags & 1) !== 0;
$tmp['peer'] = match (stream_get_contents($stream, 4)) {
'¿må' => [
'_' => 'dialogPeer',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'âEQ' => [
'_' => 'dialogPeerFolder',
'folder_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_DialogPeer(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_updateMessagePoll(mixed $stream): mixed {
$tmp = ['_' => 'updateMessagePoll'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['poll_id'] = unpack('q', stream_get_contents($stream, 8))[1];
if (($flags & 1) !== 0) $tmp['poll'] = match (stream_get_contents($stream, 4)) {
'a�á†' => self::deserialize_poll($stream),
'¡Ïr0' => self::deserialize_type_Poll(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['results'] = match (stream_get_contents($stream, 4)) {
' $ßz' => self::deserialize_pollResults($stream),
'¡Ïr0' => self::deserialize_type_PollResults(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_type_array_of_FolderPeer(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'h¦ºé' => [
'_' => 'folderPeer',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'folder_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => $this->deserialize_type_FolderPeer(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_peerSettings(mixed $stream): mixed {
$tmp = ['_' => 'peerSettings'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['report_spam'] = ($flags & 1) !== 0;
$tmp['add_contact'] = ($flags & 2) !== 0;
$tmp['block_contact'] = ($flags & 4) !== 0;
$tmp['share_contact'] = ($flags & 8) !== 0;
$tmp['need_contacts_exception'] = ($flags & 16) !== 0;
$tmp['report_geo'] = ($flags & 32) !== 0;
$tmp['autoarchived'] = ($flags & 128) !== 0;
$tmp['invite_members'] = ($flags & 256) !== 0;
$tmp['request_chat_broadcast'] = ($flags & 1024) !== 0;
if (($flags & 64) !== 0) $tmp['geo_distance'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 512) !== 0) $tmp['request_chat_title'] = self::deserialize_string($stream);
if (($flags & 512) !== 0) $tmp['request_chat_date'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_PeerSettings(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'¥' => self::deserialize_peerSettings($stream),
'¡Ïr0' => $this->deserialize_type_PeerSettings(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_array_of_PeerLocated(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
']FÊ' => [
'_' => 'peerLocated',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'expires' => unpack('l', stream_get_contents($stream, 4))[1],
'distance' => unpack('l', stream_get_contents($stream, 4))[1],
],
'K(ìø' => [
'_' => 'peerSelfLocated',
'expires' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => $this->deserialize_type_PeerLocated(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_ThemeSettings(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'Ô¶Xú' => self::deserialize_themeSettings($stream),
'¡Ïr0' => self::deserialize_type_ThemeSettings(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_theme(mixed $stream): mixed {
$tmp = ['_' => 'theme'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['creator'] = ($flags & 1) !== 0;
$tmp['default'] = ($flags & 2) !== 0;
$tmp['for_chat'] = ($flags & 32) !== 0;
$tmp['id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['access_hash'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['slug'] = self::deserialize_string($stream);
$tmp['title'] = self::deserialize_string($stream);
if (($flags & 4) !== 0) $tmp['document'] = match (stream_get_contents($stream, 4)) {
'qÈø6' => [
'_' => 'documentEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ØÄÔ�' => self::deserialize_document($stream),
'¡Ïr0' => self::deserialize_type_Document(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 8) !== 0) $tmp['settings'] = self::deserialize_type_array_of_ThemeSettings(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 64) !== 0) $tmp['emoticon'] = self::deserialize_string($stream);
if (($flags & 16) !== 0) $tmp['installs_count'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_Theme(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'Ög ' => self::deserialize_theme($stream),
'¡Ïr0' => $this->deserialize_type_Theme(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_array_of_InputPeer(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'ê;' => [
'_' => 'inputPeerEmpty',
],
'É~ }' => [
'_' => 'inputPeerSelf',
],
'¹\\©5' => [
'_' => 'inputPeerChat',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'L¥èÝ' => [
'_' => 'inputPeerUser',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ü»¼\'' => [
'_' => 'inputPeerChannel',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'
{¨' => [
'_' => 'inputPeerUserFromMessage',
'peer' => match (stream_get_contents($stream, 4)) {
'ê;' => [
'_' => 'inputPeerEmpty',
],
'É~ }' => [
'_' => 'inputPeerSelf',
],
'¹\\©5' => [
'_' => 'inputPeerChat',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'L¥èÝ' => [
'_' => 'inputPeerUser',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ü»¼\'' => [
'_' => 'inputPeerChannel',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'
{¨' => [
'_' => 'inputPeerUserFromMessage',
'peer' => self::deserialize_type_InputPeer($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'@*½' => [
'_' => 'inputPeerChannelFromMessage',
'peer' => self::deserialize_type_InputPeer($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputPeer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'@*½' => [
'_' => 'inputPeerChannelFromMessage',
'peer' => match (stream_get_contents($stream, 4)) {
'ê;' => [
'_' => 'inputPeerEmpty',
],
'É~ }' => [
'_' => 'inputPeerSelf',
],
'¹\\©5' => [
'_' => 'inputPeerChat',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'L¥èÝ' => [
'_' => 'inputPeerUser',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ü»¼\'' => [
'_' => 'inputPeerChannel',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'
{¨' => [
'_' => 'inputPeerUserFromMessage',
'peer' => self::deserialize_type_InputPeer($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'@*½' => [
'_' => 'inputPeerChannelFromMessage',
'peer' => self::deserialize_type_InputPeer($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputPeer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputPeer(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_dialogFilter(mixed $stream): mixed {
$tmp = ['_' => 'dialogFilter'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['contacts'] = ($flags & 1) !== 0;
$tmp['non_contacts'] = ($flags & 2) !== 0;
$tmp['groups'] = ($flags & 4) !== 0;
$tmp['broadcasts'] = ($flags & 8) !== 0;
$tmp['bots'] = ($flags & 16) !== 0;
$tmp['exclude_muted'] = ($flags & 2048) !== 0;
$tmp['exclude_read'] = ($flags & 4096) !== 0;
$tmp['exclude_archived'] = ($flags & 8192) !== 0;
$tmp['id'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['title'] = self::deserialize_string($stream);
if (($flags & 33554432) !== 0) $tmp['emoticon'] = self::deserialize_string($stream);
if (($flags & 134217728) !== 0) $tmp['color'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['pinned_peers'] = self::deserialize_type_array_of_InputPeer(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['include_peers'] = self::deserialize_type_array_of_InputPeer(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['exclude_peers'] = self::deserialize_type_array_of_InputPeer(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
return $tmp;

    }
    private  function deserialize_dialogFilterChatlist(mixed $stream): mixed {
$tmp = ['_' => 'dialogFilterChatlist'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['has_my_invites'] = ($flags & 67108864) !== 0;
$tmp['id'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['title'] = self::deserialize_string($stream);
if (($flags & 33554432) !== 0) $tmp['emoticon'] = self::deserialize_string($stream);
if (($flags & 134217728) !== 0) $tmp['color'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['pinned_peers'] = self::deserialize_type_array_of_InputPeer(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['include_peers'] = self::deserialize_type_array_of_InputPeer(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
return $tmp;

    }
    private  function deserialize_type_DialogFilter(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
';Rµ_' => self::deserialize_dialogFilter($stream),
'®“26' => [
'_' => 'dialogFilterDefault',
],
'¤ŽâŸ' => self::deserialize_dialogFilterChatlist($stream),
'¡Ïr0' => $this->deserialize_type_DialogFilter(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_updateDialogFilter(mixed $stream): mixed {
$tmp = ['_' => 'updateDialogFilter'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['id'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['filter'] = match (stream_get_contents($stream, 4)) {
';Rµ_' => self::deserialize_dialogFilter($stream),
'®“26' => [
'_' => 'dialogFilterDefault',
],
'¤ŽâŸ' => self::deserialize_dialogFilterChatlist($stream),
'¡Ïr0' => self::deserialize_type_DialogFilter(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_updateReadChannelDiscussionInbox(mixed $stream): mixed {
$tmp = ['_' => 'updateReadChannelDiscussionInbox'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['channel_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['top_msg_id'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['read_max_id'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['broadcast_id'] = unpack('q', stream_get_contents($stream, 8))[1];
if (($flags & 1) !== 0) $tmp['broadcast_post'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_updatePeerBlocked(mixed $stream): mixed {
$tmp = ['_' => 'updatePeerBlocked'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['blocked'] = ($flags & 1) !== 0;
$tmp['blocked_my_stories_from'] = ($flags & 2) !== 0;
$tmp['peer_id'] = match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_updateChannelUserTyping(mixed $stream): mixed {
$tmp = ['_' => 'updateChannelUserTyping'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['channel_id'] = unpack('q', stream_get_contents($stream, 8))[1];
if (($flags & 1) !== 0) $tmp['top_msg_id'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['from_id'] = match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['action'] = match (stream_get_contents($stream, 4)) {
'Nt¿' => [
'_' => 'sendMessageTypingAction',
],
'õÈ^ý' => [
'_' => 'sendMessageCancelAction',
],
'oÖ‡¡' => [
'_' => 'sendMessageRecordVideoAction',
],
'ì:vé' => [
'_' => 'sendMessageUploadVideoAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'÷s/Õ' => [
'_' => 'sendMessageRecordAudioAction',
],
'«×Qó' => [
'_' => 'sendMessageUploadAudioAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'&JÓÑ' => [
'_' => 'sendMessageUploadPhotoAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'äÙª' => [
'_' => 'sendMessageUploadDocumentAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡‹o' => [
'_' => 'sendMessageGeoLocationAction',
],
'o¼Œb' => [
'_' => 'sendMessageChooseContactAction',
],
'H�jÝ' => [
'_' => 'sendMessageGamePlayAction',
],
'¼òˆ' => [
'_' => 'sendMessageRecordRoundAction',
],
'f>$' => [
'_' => 'sendMessageUploadRoundAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'…",Ù' => [
'_' => 'speakingInGroupCallAction',
],
'F’ÚÛ' => [
'_' => 'sendMessageHistoryImportAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'±ÆZ°' => [
'_' => 'sendMessageChooseStickerAction',
],
'Ë+—%' => [
'_' => 'sendMessageEmojiInteraction',
'emoticon' => self::deserialize_string($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'interaction' => match (stream_get_contents($stream, 4)) {
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'¡Ïr0' => self::deserialize_type_DataJSON(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'.�e¶' => [
'_' => 'sendMessageEmojiInteractionSeen',
'emoticon' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_SendMessageAction(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_updatePinnedMessages(mixed $stream): mixed {
$tmp = ['_' => 'updatePinnedMessages'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['pinned'] = ($flags & 1) !== 0;
$tmp['peer'] = match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['messages'] = self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['pts'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['pts_count'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_updatePinnedChannelMessages(mixed $stream): mixed {
$tmp = ['_' => 'updatePinnedChannelMessages'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['pinned'] = ($flags & 1) !== 0;
$tmp['channel_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['messages'] = self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['pts'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['pts_count'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_array_of_GroupCallParticipantVideoSourceGroup(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'·±Ü' => [
'_' => 'groupCallParticipantVideoSourceGroup',
'semantics' => self::deserialize_string($stream),
'sources' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => $this->deserialize_type_GroupCallParticipantVideoSourceGroup(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_groupCallParticipantVideo(mixed $stream): mixed {
$tmp = ['_' => 'groupCallParticipantVideo'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['paused'] = ($flags & 1) !== 0;
$tmp['endpoint'] = self::deserialize_string($stream);
$tmp['source_groups'] = self::deserialize_type_array_of_GroupCallParticipantVideoSourceGroup(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 2) !== 0) $tmp['audio_source'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_GroupCallParticipantVideo(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'È:ug' => self::deserialize_groupCallParticipantVideo($stream),
'¡Ïr0' => $this->deserialize_type_GroupCallParticipantVideo(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_groupCallParticipant(mixed $stream): mixed {
$tmp = ['_' => 'groupCallParticipant'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['muted'] = ($flags & 1) !== 0;
$tmp['left'] = ($flags & 2) !== 0;
$tmp['can_self_unmute'] = ($flags & 4) !== 0;
$tmp['just_joined'] = ($flags & 16) !== 0;
$tmp['versioned'] = ($flags & 32) !== 0;
$tmp['min'] = ($flags & 256) !== 0;
$tmp['muted_by_you'] = ($flags & 512) !== 0;
$tmp['volume_by_admin'] = ($flags & 1024) !== 0;
$tmp['self'] = ($flags & 4096) !== 0;
$tmp['video_joined'] = ($flags & 32768) !== 0;
$tmp['peer'] = match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['date'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 8) !== 0) $tmp['active_date'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['source'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 128) !== 0) $tmp['volume'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 2048) !== 0) $tmp['about'] = self::deserialize_string($stream);
if (($flags & 8192) !== 0) $tmp['raise_hand_rating'] = unpack('q', stream_get_contents($stream, 8))[1];
if (($flags & 64) !== 0) $tmp['video'] = match (stream_get_contents($stream, 4)) {
'È:ug' => self::deserialize_groupCallParticipantVideo($stream),
'¡Ïr0' => self::deserialize_type_GroupCallParticipantVideo(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 16384) !== 0) $tmp['presentation'] = match (stream_get_contents($stream, 4)) {
'È:ug' => self::deserialize_groupCallParticipantVideo($stream),
'¡Ïr0' => self::deserialize_type_GroupCallParticipantVideo(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_type_array_of_GroupCallParticipant(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'þ6¦ë' => self::deserialize_groupCallParticipant($stream),
'¡Ïr0' => $this->deserialize_type_GroupCallParticipant(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_groupCall(mixed $stream): mixed {
$tmp = ['_' => 'groupCall'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['join_muted'] = ($flags & 2) !== 0;
$tmp['can_change_join_muted'] = ($flags & 4) !== 0;
$tmp['join_date_asc'] = ($flags & 64) !== 0;
$tmp['schedule_start_subscribed'] = ($flags & 256) !== 0;
$tmp['can_start_video'] = ($flags & 512) !== 0;
$tmp['record_video_active'] = ($flags & 2048) !== 0;
$tmp['rtmp_stream'] = ($flags & 4096) !== 0;
$tmp['listeners_hidden'] = ($flags & 8192) !== 0;
$tmp['id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['access_hash'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['participants_count'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 8) !== 0) $tmp['title'] = self::deserialize_string($stream);
if (($flags & 16) !== 0) $tmp['stream_dc_id'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 32) !== 0) $tmp['record_start_date'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 128) !== 0) $tmp['schedule_date'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 1024) !== 0) $tmp['unmuted_video_count'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['unmuted_video_limit'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['version'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_GroupCall(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'´¼€w' => [
'_' => 'groupCallDiscarded',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'duration' => unpack('l', stream_get_contents($stream, 4))[1],
],
'e—Õ' => self::deserialize_groupCall($stream),
'¡Ïr0' => $this->deserialize_type_GroupCall(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_updatePeerHistoryTTL(mixed $stream): mixed {
$tmp = ['_' => 'updatePeerHistoryTTL'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['peer'] = match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 1) !== 0) $tmp['ttl_period'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_chatInviteExported(mixed $stream): mixed {
$tmp = ['_' => 'chatInviteExported'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['revoked'] = ($flags & 1) !== 0;
$tmp['permanent'] = ($flags & 32) !== 0;
$tmp['request_needed'] = ($flags & 64) !== 0;
$tmp['link'] = self::deserialize_string($stream);
$tmp['admin_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['date'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 16) !== 0) $tmp['start_date'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 2) !== 0) $tmp['expire_date'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 4) !== 0) $tmp['usage_limit'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 8) !== 0) $tmp['usage'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 128) !== 0) $tmp['requested'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 256) !== 0) $tmp['title'] = self::deserialize_string($stream);
return $tmp;

    }
    private  function deserialize_type_ExportedChatInvite(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'¨´
' => self::deserialize_chatInviteExported($stream),
'·zí' => [
'_' => 'chatInvitePublicJoinRequests',
],
'¡Ïr0' => $this->deserialize_type_ExportedChatInvite(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_updateChatParticipant(mixed $stream): mixed {
$tmp = ['_' => 'updateChatParticipant'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['chat_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['date'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['actor_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['user_id'] = unpack('q', stream_get_contents($stream, 8))[1];
if (($flags & 1) !== 0) $tmp['prev_participant'] = match (stream_get_contents($stream, 4)) {
'@-À' => [
'_' => 'chatParticipant',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'inviter_id' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'äÎkä' => [
'_' => 'chatParticipantCreator',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'[?“ ' => [
'_' => 'chatParticipantAdmin',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'inviter_id' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_ChatParticipant(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 2) !== 0) $tmp['new_participant'] = match (stream_get_contents($stream, 4)) {
'@-À' => [
'_' => 'chatParticipant',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'inviter_id' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'äÎkä' => [
'_' => 'chatParticipantCreator',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'[?“ ' => [
'_' => 'chatParticipantAdmin',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'inviter_id' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_ChatParticipant(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 4) !== 0) $tmp['invite'] = match (stream_get_contents($stream, 4)) {
'¨´
' => self::deserialize_chatInviteExported($stream),
'·zí' => [
'_' => 'chatInvitePublicJoinRequests',
],
'¡Ïr0' => self::deserialize_type_ExportedChatInvite(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['qts'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_channelParticipantSelf(mixed $stream): mixed {
$tmp = ['_' => 'channelParticipantSelf'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['via_request'] = ($flags & 1) !== 0;
$tmp['user_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['inviter_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['date'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_channelParticipantCreator(mixed $stream): mixed {
$tmp = ['_' => 'channelParticipantCreator'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['user_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['admin_rights'] = match (stream_get_contents($stream, 4)) {
'Õ$²_' => self::deserialize_chatAdminRights($stream),
'¡Ïr0' => self::deserialize_type_ChatAdminRights(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 1) !== 0) $tmp['rank'] = self::deserialize_string($stream);
return $tmp;

    }
    private  function deserialize_channelParticipantAdmin(mixed $stream): mixed {
$tmp = ['_' => 'channelParticipantAdmin'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['can_edit'] = ($flags & 1) !== 0;
$tmp['self'] = ($flags & 2) !== 0;
$tmp['user_id'] = unpack('q', stream_get_contents($stream, 8))[1];
if (($flags & 2) !== 0) $tmp['inviter_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['promoted_by'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['date'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['admin_rights'] = match (stream_get_contents($stream, 4)) {
'Õ$²_' => self::deserialize_chatAdminRights($stream),
'¡Ïr0' => self::deserialize_type_ChatAdminRights(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 4) !== 0) $tmp['rank'] = self::deserialize_string($stream);
return $tmp;

    }
    private  function deserialize_channelParticipantBanned(mixed $stream): mixed {
$tmp = ['_' => 'channelParticipantBanned'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['left'] = ($flags & 1) !== 0;
$tmp['peer'] = match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['kicked_by'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['date'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['banned_rights'] = match (stream_get_contents($stream, 4)) {
'Ÿ' => self::deserialize_chatBannedRights($stream),
'¡Ïr0' => self::deserialize_type_ChatBannedRights(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_type_ChannelParticipant(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'ÀÀ' => [
'_' => 'channelParticipant',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'§¿¨5' => self::deserialize_channelParticipantSelf($stream),
'Óæ/' => self::deserialize_channelParticipantCreator($stream),
'S»Ã4' => self::deserialize_channelParticipantAdmin($stream),
'Nøm' => self::deserialize_channelParticipantBanned($stream),
'ð' => [
'_' => 'channelParticipantLeft',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => $this->deserialize_type_ChannelParticipant(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_updateChannelParticipant(mixed $stream): mixed {
$tmp = ['_' => 'updateChannelParticipant'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['via_chatlist'] = ($flags & 8) !== 0;
$tmp['channel_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['date'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['actor_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['user_id'] = unpack('q', stream_get_contents($stream, 8))[1];
if (($flags & 1) !== 0) $tmp['prev_participant'] = match (stream_get_contents($stream, 4)) {
'ÀÀ' => [
'_' => 'channelParticipant',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'§¿¨5' => self::deserialize_channelParticipantSelf($stream),
'Óæ/' => self::deserialize_channelParticipantCreator($stream),
'S»Ã4' => self::deserialize_channelParticipantAdmin($stream),
'Nøm' => self::deserialize_channelParticipantBanned($stream),
'ð' => [
'_' => 'channelParticipantLeft',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_ChannelParticipant(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 2) !== 0) $tmp['new_participant'] = match (stream_get_contents($stream, 4)) {
'ÀÀ' => [
'_' => 'channelParticipant',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'§¿¨5' => self::deserialize_channelParticipantSelf($stream),
'Óæ/' => self::deserialize_channelParticipantCreator($stream),
'S»Ã4' => self::deserialize_channelParticipantAdmin($stream),
'Nøm' => self::deserialize_channelParticipantBanned($stream),
'ð' => [
'_' => 'channelParticipantLeft',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_ChannelParticipant(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 4) !== 0) $tmp['invite'] = match (stream_get_contents($stream, 4)) {
'¨´
' => self::deserialize_chatInviteExported($stream),
'·zí' => [
'_' => 'chatInvitePublicJoinRequests',
],
'¡Ïr0' => self::deserialize_type_ExportedChatInvite(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['qts'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_updateGroupCallConnection(mixed $stream): mixed {
$tmp = ['_' => 'updateGroupCallConnection'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['presentation'] = ($flags & 1) !== 0;
$tmp['params'] = match (stream_get_contents($stream, 4)) {
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'¡Ïr0' => self::deserialize_type_DataJSON(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_type_array_of_BotCommand(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'ÇÈzÂ' => [
'_' => 'botCommand',
'command' => self::deserialize_string($stream),
'description' => self::deserialize_string($stream),
],
'¡Ïr0' => $this->deserialize_type_BotCommand(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_updateMessageReactions(mixed $stream): mixed {
$tmp = ['_' => 'updateMessageReactions'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['peer'] = match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['msg_id'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['top_msg_id'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['reactions'] = match (stream_get_contents($stream, 4)) {
'y”+O' => self::deserialize_messageReactions($stream),
'¡Ïr0' => self::deserialize_type_MessageReactions(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_type_BotMenuButton(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'ˆ¥3u' => [
'_' => 'botMenuButtonDefault',
],
'ÂXB' => [
'_' => 'botMenuButtonCommands',
],
'æ|µÇ' => [
'_' => 'botMenuButton',
'text' => self::deserialize_string($stream),
'url' => self::deserialize_string($stream),
],
'¡Ïr0' => $this->deserialize_type_BotMenuButton(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_updateTranscribedAudio(mixed $stream): mixed {
$tmp = ['_' => 'updateTranscribedAudio'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['pending'] = ($flags & 1) !== 0;
$tmp['peer'] = match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['msg_id'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['transcription_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['text'] = self::deserialize_string($stream);
return $tmp;

    }
    private  function deserialize_updateMoveStickerSetToTop(mixed $stream): mixed {
$tmp = ['_' => 'updateMoveStickerSetToTop'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['masks'] = ($flags & 1) !== 0;
$tmp['emojis'] = ($flags & 2) !== 0;
$tmp['stickerset'] = unpack('q', stream_get_contents($stream, 8))[1];
return $tmp;

    }
    private  function deserialize_updateChannelPinnedTopic(mixed $stream): mixed {
$tmp = ['_' => 'updateChannelPinnedTopic'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['pinned'] = ($flags & 1) !== 0;
$tmp['channel_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['topic_id'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_updateChannelPinnedTopics(mixed $stream): mixed {
$tmp = ['_' => 'updateChannelPinnedTopics'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['channel_id'] = unpack('q', stream_get_contents($stream, 8))[1];
if (($flags & 1) !== 0) $tmp['order'] = self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
return $tmp;

    }
    private  function deserialize_storiesStealthMode(mixed $stream): mixed {
$tmp = ['_' => 'storiesStealthMode'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['active_until_date'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 2) !== 0) $tmp['cooldown_until_date'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_StoriesStealthMode(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'ý\'.q' => self::deserialize_storiesStealthMode($stream),
'¡Ïr0' => $this->deserialize_type_StoriesStealthMode(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_boost(mixed $stream): mixed {
$tmp = ['_' => 'boost'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['gift'] = ($flags & 2) !== 0;
$tmp['giveaway'] = ($flags & 4) !== 0;
$tmp['unclaimed'] = ($flags & 8) !== 0;
$tmp['id'] = self::deserialize_string($stream);
if (($flags & 1) !== 0) $tmp['user_id'] = unpack('q', stream_get_contents($stream, 8))[1];
if (($flags & 4) !== 0) $tmp['giveaway_msg_id'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['date'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['expires'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 16) !== 0) $tmp['used_gift_slug'] = self::deserialize_string($stream);
if (($flags & 32) !== 0) $tmp['multiplier'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_Boost(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'qŒ*' => self::deserialize_boost($stream),
'¡Ïr0' => $this->deserialize_type_Boost(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_updatePeerWallpaper(mixed $stream): mixed {
$tmp = ['_' => 'updatePeerWallpaper'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['wallpaper_overridden'] = ($flags & 2) !== 0;
$tmp['peer'] = match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 1) !== 0) $tmp['wallpaper'] = match (stream_get_contents($stream, 4)) {
'íÃ7¤' => self::deserialize_wallPaper($stream),
'A€à' => self::deserialize_wallPaperNoFile($stream),
'¡Ïr0' => self::deserialize_type_WallPaper(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_type_array_of_Reaction(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'Ôõy' => [
'_' => 'reactionEmpty',
],
'¸†"' => [
'_' => 'reactionEmoji',
'emoticon' => self::deserialize_string($stream),
],
'sü5‰' => [
'_' => 'reactionCustomEmoji',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_Reaction(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_updateSavedDialogPinned(mixed $stream): mixed {
$tmp = ['_' => 'updateSavedDialogPinned'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['pinned'] = ($flags & 1) !== 0;
$tmp['peer'] = match (stream_get_contents($stream, 4)) {
'¿må' => [
'_' => 'dialogPeer',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'âEQ' => [
'_' => 'dialogPeerFolder',
'folder_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_DialogPeer(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_updatePinnedSavedDialogs(mixed $stream): mixed {
$tmp = ['_' => 'updatePinnedSavedDialogs'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['order'] = self::deserialize_type_array_of_DialogPeer(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
return $tmp;

    }
    private  function deserialize_type_array_of_QuickReply(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'+—' => [
'_' => 'quickReply',
'shortcut_id' => unpack('l', stream_get_contents($stream, 4))[1],
'shortcut' => self::deserialize_string($stream),
'top_message' => unpack('l', stream_get_contents($stream, 4))[1],
'count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => $this->deserialize_type_QuickReply(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_QuickReply(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'+—' => [
'_' => 'quickReply',
'shortcut_id' => unpack('l', stream_get_contents($stream, 4))[1],
'shortcut' => self::deserialize_string($stream),
'top_message' => unpack('l', stream_get_contents($stream, 4))[1],
'count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => $this->deserialize_type_QuickReply(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_auth___sentCodeTypeEmailCode(mixed $stream): mixed {
$tmp = ['_' => 'auth.sentCodeTypeEmailCode'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['apple_signin_allowed'] = ($flags & 1) !== 0;
$tmp['google_signin_allowed'] = ($flags & 2) !== 0;
$tmp['email_pattern'] = self::deserialize_string($stream);
$tmp['length'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 8) !== 0) $tmp['reset_available_period'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 16) !== 0) $tmp['reset_pending_date'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_auth___sentCodeTypeSetUpEmailRequired(mixed $stream): mixed {
$tmp = ['_' => 'auth.sentCodeTypeSetUpEmailRequired'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['apple_signin_allowed'] = ($flags & 1) !== 0;
$tmp['google_signin_allowed'] = ($flags & 2) !== 0;
return $tmp;

    }
    private  function deserialize_auth___sentCodeTypeFirebaseSms(mixed $stream): mixed {
$tmp = ['_' => 'auth.sentCodeTypeFirebaseSms'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['nonce'] = self::deserialize_bytes($stream);
if (($flags & 2) !== 0) $tmp['receipt'] = self::deserialize_string($stream);
if (($flags & 2) !== 0) $tmp['push_timeout'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['length'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_auth___SentCodeType(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'†Y»=' => [
'_' => 'auth.sentCodeTypeApp',
'length' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¢»' . "\0" . 'À' => [
'_' => 'auth.sentCodeTypeSms',
'length' => unpack('l', stream_get_contents($stream, 4))[1],
],
'§åSS' => [
'_' => 'auth.sentCodeTypeCall',
'length' => unpack('l', stream_get_contents($stream, 4))[1],
],
'ÙÆ«' => [
'_' => 'auth.sentCodeTypeFlashCall',
'pattern' => self::deserialize_string($stream),
],
'„d' . "\0" . '‚' => [
'_' => 'auth.sentCodeTypeMissedCall',
'prefix' => self::deserialize_string($stream),
'length' => unpack('l', stream_get_contents($stream, 4))[1],
],
'›õPô' => self::deserialize_auth___sentCodeTypeEmailCode($stream),
'êI¥' => self::deserialize_auth___sentCodeTypeSetUpEmailRequired($stream),
'9\\VÙ' => [
'_' => 'auth.sentCodeTypeFragmentSms',
'url' => self::deserialize_string($stream),
'length' => unpack('l', stream_get_contents($stream, 4))[1],
],
'2{å' => self::deserialize_auth___sentCodeTypeFirebaseSms($stream),
'¡Ïr0' => $this->deserialize_type_auth___SentCodeType(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_auth___CodeType(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'Œ£r' => [
'_' => 'auth.codeTypeSms',
],
'ãÓt' => [
'_' => 'auth.codeTypeCall',
],
'ûÎl"' => [
'_' => 'auth.codeTypeFlashCall',
],
'îÖÖ' => [
'_' => 'auth.codeTypeMissedCall',
],
'Œ™í' => [
'_' => 'auth.codeTypeFragmentSms',
],
'¡Ïr0' => $this->deserialize_type_auth___CodeType(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_auth___sentCode(mixed $stream): mixed {
$tmp = ['_' => 'auth.sentCode'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['type'] = match (stream_get_contents($stream, 4)) {
'†Y»=' => [
'_' => 'auth.sentCodeTypeApp',
'length' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¢»' . "\0" . 'À' => [
'_' => 'auth.sentCodeTypeSms',
'length' => unpack('l', stream_get_contents($stream, 4))[1],
],
'§åSS' => [
'_' => 'auth.sentCodeTypeCall',
'length' => unpack('l', stream_get_contents($stream, 4))[1],
],
'ÙÆ«' => [
'_' => 'auth.sentCodeTypeFlashCall',
'pattern' => self::deserialize_string($stream),
],
'„d' . "\0" . '‚' => [
'_' => 'auth.sentCodeTypeMissedCall',
'prefix' => self::deserialize_string($stream),
'length' => unpack('l', stream_get_contents($stream, 4))[1],
],
'›õPô' => self::deserialize_auth___sentCodeTypeEmailCode($stream),
'êI¥' => self::deserialize_auth___sentCodeTypeSetUpEmailRequired($stream),
'9\\VÙ' => [
'_' => 'auth.sentCodeTypeFragmentSms',
'url' => self::deserialize_string($stream),
'length' => unpack('l', stream_get_contents($stream, 4))[1],
],
'2{å' => self::deserialize_auth___sentCodeTypeFirebaseSms($stream),
'¡Ïr0' => self::deserialize_type_auth___SentCodeType(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['phone_code_hash'] = self::deserialize_string($stream);
if (($flags & 2) !== 0) $tmp['next_type'] = match (stream_get_contents($stream, 4)) {
'Œ£r' => [
'_' => 'auth.codeTypeSms',
],
'ãÓt' => [
'_' => 'auth.codeTypeCall',
],
'ûÎl"' => [
'_' => 'auth.codeTypeFlashCall',
],
'îÖÖ' => [
'_' => 'auth.codeTypeMissedCall',
],
'Œ™í' => [
'_' => 'auth.codeTypeFragmentSms',
],
'¡Ïr0' => self::deserialize_type_auth___CodeType(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 4) !== 0) $tmp['timeout'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_userEmpty(mixed $stream): mixed {
$tmp = [
'_' => 'userEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
];
$this->peerDatabase->addUser($tmp);
return $tmp;

    }
    private  function deserialize_userProfilePhoto(mixed $stream): mixed {
$tmp = ['_' => 'userProfilePhoto'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['has_video'] = ($flags & 1) !== 0;
$tmp['personal'] = ($flags & 4) !== 0;
$tmp['photo_id'] = unpack('q', stream_get_contents($stream, 8))[1];
if (($flags & 2) !== 0) $tmp['stripped_thumb'] = self::deserialize_bytes($stream);
$tmp['dc_id'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_UserProfilePhoto(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'áºO' => [
'_' => 'userProfilePhotoEmpty',
],
'÷Ñ‚' => self::deserialize_userProfilePhoto($stream),
'¡Ïr0' => $this->deserialize_type_UserProfilePhoto(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_user(mixed $stream): mixed {
$this->referenceDatabase?->addOriginContext('user');
$tmp = ['_' => 'user'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['self'] = ($flags & 1024) !== 0;
$tmp['contact'] = ($flags & 2048) !== 0;
$tmp['mutual_contact'] = ($flags & 4096) !== 0;
$tmp['deleted'] = ($flags & 8192) !== 0;
$tmp['bot'] = ($flags & 16384) !== 0;
$tmp['bot_chat_history'] = ($flags & 32768) !== 0;
$tmp['bot_nochats'] = ($flags & 65536) !== 0;
$tmp['verified'] = ($flags & 131072) !== 0;
$tmp['restricted'] = ($flags & 262144) !== 0;
$tmp['min'] = ($flags & 1048576) !== 0;
$tmp['bot_inline_geo'] = ($flags & 2097152) !== 0;
$tmp['support'] = ($flags & 8388608) !== 0;
$tmp['scam'] = ($flags & 16777216) !== 0;
$tmp['apply_min_photo'] = ($flags & 33554432) !== 0;
$tmp['fake'] = ($flags & 67108864) !== 0;
$tmp['bot_attach_menu'] = ($flags & 134217728) !== 0;
$tmp['premium'] = ($flags & 268435456) !== 0;
$tmp['attach_menu_enabled'] = ($flags & 536870912) !== 0;
$flags2 = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['bot_can_edit'] = ($flags2 & 2) !== 0;
$tmp['close_friend'] = ($flags2 & 4) !== 0;
$tmp['stories_hidden'] = ($flags2 & 8) !== 0;
$tmp['stories_unavailable'] = ($flags2 & 16) !== 0;
$tmp['contact_require_premium'] = ($flags2 & 1024) !== 0;
$tmp['id'] = unpack('q', stream_get_contents($stream, 8))[1];
if (($flags & 1) !== 0) $tmp['access_hash'] = unpack('q', stream_get_contents($stream, 8))[1];
if (($flags & 2) !== 0) $tmp['first_name'] = self::deserialize_string($stream);
if (($flags & 4) !== 0) $tmp['last_name'] = self::deserialize_string($stream);
if (($flags & 8) !== 0) $tmp['username'] = self::deserialize_string($stream);
if (($flags & 16) !== 0) $tmp['phone'] = self::deserialize_string($stream);
if (($flags & 32) !== 0) $tmp['photo'] = match (stream_get_contents($stream, 4)) {
'áºO' => [
'_' => 'userProfilePhotoEmpty',
],
'÷Ñ‚' => self::deserialize_userProfilePhoto($stream),
'¡Ïr0' => self::deserialize_type_UserProfilePhoto(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 64) !== 0) $tmp['status'] = match (stream_get_contents($stream, 4)) {
'IPÐ	' => [
'_' => 'userStatusEmpty',
],
'I9¹í' => [
'_' => 'userStatusOnline',
'expires' => unpack('l', stream_get_contents($stream, 4))[1],
],
'?pŒ' . "\0" . '' => [
'_' => 'userStatusOffline',
'was_online' => unpack('l', stream_get_contents($stream, 4))[1],
],
'È}{' => self::deserialize_userStatusRecently($stream),
'T' => self::deserialize_userStatusLastWeek($stream),
'w—‰e' => self::deserialize_userStatusLastMonth($stream),
'¡Ïr0' => self::deserialize_type_UserStatus(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 16384) !== 0) $tmp['bot_info_version'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 262144) !== 0) $tmp['restriction_reason'] = self::deserialize_type_array_of_RestrictionReason(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 524288) !== 0) $tmp['bot_inline_placeholder'] = self::deserialize_string($stream);
if (($flags & 4194304) !== 0) $tmp['lang_code'] = self::deserialize_string($stream);
if (($flags & 1073741824) !== 0) $tmp['emoji_status'] = match (stream_get_contents($stream, 4)) {
'®á-' => [
'_' => 'emojiStatusEmpty',
],
'�a›’' => [
'_' => 'emojiStatus',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'Ç¨0ú' => [
'_' => 'emojiStatusUntil',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'until' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_EmojiStatus(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags2 & 1) !== 0) $tmp['usernames'] = self::deserialize_type_array_of_Username(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags2 & 32) !== 0) $tmp['stories_max_id'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags2 & 256) !== 0) $tmp['color'] = match (stream_get_contents($stream, 4)) {
'ÏZKµ' => self::deserialize_peerColor($stream),
'¡Ïr0' => self::deserialize_type_PeerColor(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags2 & 512) !== 0) $tmp['profile_color'] = match (stream_get_contents($stream, 4)) {
'ÏZKµ' => self::deserialize_peerColor($stream),
'¡Ïr0' => self::deserialize_type_PeerColor(self::gzdecode($stream)),
default => self::err($stream)
}
;
$this->referenceDatabase?->addOrigin($tmp);
$this->peerDatabase->addUser($tmp);
return $tmp;

    }
    private  function deserialize_type_User(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'zK¼Ó' => self::deserialize_userEmpty($stream),
'8D\\!' => self::deserialize_user($stream),
'¡Ïr0' => $this->deserialize_type_User(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_auth___authorization(mixed $stream): mixed {
$tmp = ['_' => 'auth.authorization'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['setup_password_required'] = ($flags & 2) !== 0;
if (($flags & 2) !== 0) $tmp['otherwise_relogin_days'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['tmp_sessions'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 4) !== 0) $tmp['future_auth_token'] = self::deserialize_bytes($stream);
$tmp['user'] = match (stream_get_contents($stream, 4)) {
'zK¼Ó' => self::deserialize_userEmpty($stream),
'8D\\!' => self::deserialize_user($stream),
'¡Ïr0' => self::deserialize_type_User(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_help___termsOfService(mixed $stream): mixed {
$tmp = ['_' => 'help.termsOfService'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['popup'] = ($flags & 1) !== 0;
$tmp['id'] = match (stream_get_contents($stream, 4)) {
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'¡Ïr0' => self::deserialize_type_DataJSON(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['text'] = self::deserialize_string($stream);
$tmp['entities'] = self::deserialize_type_array_of_MessageEntity(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 2) !== 0) $tmp['min_age_confirm'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_help___TermsOfService(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'
x' => self::deserialize_help___termsOfService($stream),
'¡Ïr0' => $this->deserialize_type_help___TermsOfService(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_auth___authorizationSignUpRequired(mixed $stream): mixed {
$tmp = ['_' => 'auth.authorizationSignUpRequired'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['terms_of_service'] = match (stream_get_contents($stream, 4)) {
'
x' => self::deserialize_help___termsOfService($stream),
'¡Ïr0' => self::deserialize_type_help___TermsOfService(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_type_auth___Authorization(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'ÔÀ¢.' => self::deserialize_auth___authorization($stream),
'š~tD' => self::deserialize_auth___authorizationSignUpRequired($stream),
'¡Ïr0' => $this->deserialize_type_auth___Authorization(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_auth___loggedOut(mixed $stream): mixed {
$tmp = ['_' => 'auth.loggedOut'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['future_auth_token'] = self::deserialize_bytes($stream);
return $tmp;

    }
    private  function deserialize_authorization(mixed $stream): mixed {
$tmp = ['_' => 'authorization'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['current'] = ($flags & 1) !== 0;
$tmp['official_app'] = ($flags & 2) !== 0;
$tmp['password_pending'] = ($flags & 4) !== 0;
$tmp['encrypted_requests_disabled'] = ($flags & 8) !== 0;
$tmp['call_requests_disabled'] = ($flags & 16) !== 0;
$tmp['unconfirmed'] = ($flags & 32) !== 0;
$tmp['hash'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['device_model'] = self::deserialize_string($stream);
$tmp['platform'] = self::deserialize_string($stream);
$tmp['system_version'] = self::deserialize_string($stream);
$tmp['api_id'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['app_name'] = self::deserialize_string($stream);
$tmp['app_version'] = self::deserialize_string($stream);
$tmp['date_created'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['date_active'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['ip'] = self::deserialize_string($stream);
$tmp['country'] = self::deserialize_string($stream);
$tmp['region'] = self::deserialize_string($stream);
return $tmp;

    }
    private  function deserialize_type_array_of_WallPaper(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'íÃ7¤' => self::deserialize_wallPaper($stream),
'A€à' => self::deserialize_wallPaperNoFile($stream),
'¡Ïr0' => self::deserialize_type_WallPaper(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_Chat(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'e(V)' => self::deserialize_chatEmpty($stream),
'VòËA' => self::deserialize_chat($stream),
'§¡’e' => self::deserialize_chatForbidden($stream),
'�ü­
' => self::deserialize_channel($stream),
'Õ“Ô' => self::deserialize_channelForbidden($stream),
'¡Ïr0' => self::deserialize_type_Chat(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_User(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'zK¼Ó' => self::deserialize_userEmpty($stream),
'8D\\!' => self::deserialize_user($stream),
'¡Ïr0' => self::deserialize_type_User(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_Authorization(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'Ö­' => self::deserialize_authorization($stream),
'¡Ïr0' => $this->deserialize_type_Authorization(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_PasswordKdfAlgo(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'–°ZÔ' => [
'_' => 'passwordKdfAlgoUnknown',
],
'J-‘:' => [
'_' => 'passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow',
'salt1' => self::deserialize_bytes($stream),
'salt2' => self::deserialize_bytes($stream),
'g' => unpack('l', stream_get_contents($stream, 4))[1],
'p' => self::deserialize_bytes($stream),
],
'¡Ïr0' => $this->deserialize_type_PasswordKdfAlgo(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_SecurePasswordKdfAlgo(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'7…J' . "\0" . '' => [
'_' => 'securePasswordKdfAlgoUnknown',
],
' Ýò»' => [
'_' => 'securePasswordKdfAlgoPBKDF2HMACSHA512iter100000',
'salt' => self::deserialize_bytes($stream),
],
'’G†' => [
'_' => 'securePasswordKdfAlgoSHA512',
'salt' => self::deserialize_bytes($stream),
],
'¡Ïr0' => $this->deserialize_type_SecurePasswordKdfAlgo(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_account___password(mixed $stream): mixed {
$tmp = ['_' => 'account.password'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['has_recovery'] = ($flags & 1) !== 0;
$tmp['has_secure_values'] = ($flags & 2) !== 0;
$tmp['has_password'] = ($flags & 4) !== 0;
if (($flags & 4) !== 0) $tmp['current_algo'] = match (stream_get_contents($stream, 4)) {
'–°ZÔ' => [
'_' => 'passwordKdfAlgoUnknown',
],
'J-‘:' => [
'_' => 'passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow',
'salt1' => self::deserialize_bytes($stream),
'salt2' => self::deserialize_bytes($stream),
'g' => unpack('l', stream_get_contents($stream, 4))[1],
'p' => self::deserialize_bytes($stream),
],
'¡Ïr0' => self::deserialize_type_PasswordKdfAlgo(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 4) !== 0) $tmp['srp_B'] = self::deserialize_bytes($stream);
if (($flags & 4) !== 0) $tmp['srp_id'] = unpack('q', stream_get_contents($stream, 8))[1];
if (($flags & 8) !== 0) $tmp['hint'] = self::deserialize_string($stream);
if (($flags & 16) !== 0) $tmp['email_unconfirmed_pattern'] = self::deserialize_string($stream);
$tmp['new_algo'] = match (stream_get_contents($stream, 4)) {
'–°ZÔ' => [
'_' => 'passwordKdfAlgoUnknown',
],
'J-‘:' => [
'_' => 'passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow',
'salt1' => self::deserialize_bytes($stream),
'salt2' => self::deserialize_bytes($stream),
'g' => unpack('l', stream_get_contents($stream, 4))[1],
'p' => self::deserialize_bytes($stream),
],
'¡Ïr0' => self::deserialize_type_PasswordKdfAlgo(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['new_secure_algo'] = match (stream_get_contents($stream, 4)) {
'7…J' . "\0" . '' => [
'_' => 'securePasswordKdfAlgoUnknown',
],
' Ýò»' => [
'_' => 'securePasswordKdfAlgoPBKDF2HMACSHA512iter100000',
'salt' => self::deserialize_bytes($stream),
],
'’G†' => [
'_' => 'securePasswordKdfAlgoSHA512',
'salt' => self::deserialize_bytes($stream),
],
'¡Ïr0' => self::deserialize_type_SecurePasswordKdfAlgo(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['secure_random'] = self::deserialize_bytes($stream);
if (($flags & 32) !== 0) $tmp['pending_reset_date'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 64) !== 0) $tmp['login_email_pattern'] = self::deserialize_string($stream);
return $tmp;

    }
    private  function deserialize_type_SecureSecretSettings(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'¬¼\'' => [
'_' => 'secureSecretSettings',
'secure_algo' => match (stream_get_contents($stream, 4)) {
'7…J' . "\0" . '' => [
'_' => 'securePasswordKdfAlgoUnknown',
],
' Ýò»' => [
'_' => 'securePasswordKdfAlgoPBKDF2HMACSHA512iter100000',
'salt' => self::deserialize_bytes($stream),
],
'’G†' => [
'_' => 'securePasswordKdfAlgoSHA512',
'salt' => self::deserialize_bytes($stream),
],
'¡Ïr0' => self::deserialize_type_SecurePasswordKdfAlgo(self::gzdecode($stream)),
default => self::err($stream)
}
,
'secure_secret' => self::deserialize_bytes($stream),
'secure_secret_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => $this->deserialize_type_SecureSecretSettings(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_account___passwordSettings(mixed $stream): mixed {
$tmp = ['_' => 'account.passwordSettings'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['email'] = self::deserialize_string($stream);
if (($flags & 2) !== 0) $tmp['secure_settings'] = match (stream_get_contents($stream, 4)) {
'¬¼\'' => [
'_' => 'secureSecretSettings',
'secure_algo' => match (stream_get_contents($stream, 4)) {
'7…J' . "\0" . '' => [
'_' => 'securePasswordKdfAlgoUnknown',
],
' Ýò»' => [
'_' => 'securePasswordKdfAlgoPBKDF2HMACSHA512iter100000',
'salt' => self::deserialize_bytes($stream),
],
'’G†' => [
'_' => 'securePasswordKdfAlgoSHA512',
'salt' => self::deserialize_bytes($stream),
],
'¡Ïr0' => self::deserialize_type_SecurePasswordKdfAlgo(self::gzdecode($stream)),
default => self::err($stream)
}
,
'secure_secret' => self::deserialize_bytes($stream),
'secure_secret_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_SecureSecretSettings(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_type_array_of_WebAuthorization(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'Rôø¦' => [
'_' => 'webAuthorization',
'hash' => unpack('q', stream_get_contents($stream, 8))[1],
'bot_id' => unpack('q', stream_get_contents($stream, 8))[1],
'domain' => self::deserialize_string($stream),
'browser' => self::deserialize_string($stream),
'platform' => self::deserialize_string($stream),
'date_created' => unpack('l', stream_get_contents($stream, 4))[1],
'date_active' => unpack('l', stream_get_contents($stream, 4))[1],
'ip' => self::deserialize_string($stream),
'region' => self::deserialize_string($stream),
],
'¡Ïr0' => $this->deserialize_type_WebAuthorization(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_secureRequiredType(mixed $stream): mixed {
$tmp = ['_' => 'secureRequiredType'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['native_names'] = ($flags & 1) !== 0;
$tmp['selfie_required'] = ($flags & 2) !== 0;
$tmp['translation_required'] = ($flags & 4) !== 0;
$tmp['type'] = match (stream_get_contents($stream, 4)) {
'ã�*�' => [
'_' => 'secureValueTypePersonalDetails',
],
'' . "\0" . 'j¬=' => [
'_' => 'secureValueTypePassport',
],
'Ä%ä' => [
'_' => 'secureValueTypeDriverLicense',
],
'KtÐ ' => [
'_' => 'secureValueTypeIdentityCard',
],
'#�¤™' => [
'_' => 'secureValueTypeInternalPassport',
],
'&ãË' => [
'_' => 'secureValueTypeAddress',
],
'N•6ü' => [
'_' => 'secureValueTypeUtilityBill',
],
'|‰' => [
'_' => 'secureValueTypeBankStatement',
],
'ˆ4ˆ‹' => [
'_' => 'secureValueTypeRentalAgreement',
],
'j€ã™' => [
'_' => 'secureValueTypePassportRegistration',
],
'3ìê' => [
'_' => 'secureValueTypeTemporaryRegistration',
],
'Ûª ³' => [
'_' => 'secureValueTypePhone',
],
'î§<Ž' => [
'_' => 'secureValueTypeEmail',
],
'¡Ïr0' => self::deserialize_type_SecureValueType(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_type_array_of_SecureRequiredType(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'Ú™�‚' => self::deserialize_secureRequiredType($stream),
'´wt' => [
'_' => 'secureRequiredTypeOneOf',
'types' => $this->deserialize_type_array_of_SecureRequiredType(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => $this->deserialize_type_SecureRequiredType(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_SecureValueError(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'Ù¤è' => [
'_' => 'secureValueErrorData',
'type' => match (stream_get_contents($stream, 4)) {
'ã�*�' => [
'_' => 'secureValueTypePersonalDetails',
],
'' . "\0" . 'j¬=' => [
'_' => 'secureValueTypePassport',
],
'Ä%ä' => [
'_' => 'secureValueTypeDriverLicense',
],
'KtÐ ' => [
'_' => 'secureValueTypeIdentityCard',
],
'#�¤™' => [
'_' => 'secureValueTypeInternalPassport',
],
'&ãË' => [
'_' => 'secureValueTypeAddress',
],
'N•6ü' => [
'_' => 'secureValueTypeUtilityBill',
],
'|‰' => [
'_' => 'secureValueTypeBankStatement',
],
'ˆ4ˆ‹' => [
'_' => 'secureValueTypeRentalAgreement',
],
'j€ã™' => [
'_' => 'secureValueTypePassportRegistration',
],
'3ìê' => [
'_' => 'secureValueTypeTemporaryRegistration',
],
'Ûª ³' => [
'_' => 'secureValueTypePhone',
],
'î§<Ž' => [
'_' => 'secureValueTypeEmail',
],
'¡Ïr0' => self::deserialize_type_SecureValueType(self::gzdecode($stream)),
default => self::err($stream)
}
,
'data_hash' => self::deserialize_bytes($stream),
'field' => self::deserialize_string($stream),
'text' => self::deserialize_string($stream),
],
'ú=¾' . "\0" . '' => [
'_' => 'secureValueErrorFrontSide',
'type' => match (stream_get_contents($stream, 4)) {
'ã�*�' => [
'_' => 'secureValueTypePersonalDetails',
],
'' . "\0" . 'j¬=' => [
'_' => 'secureValueTypePassport',
],
'Ä%ä' => [
'_' => 'secureValueTypeDriverLicense',
],
'KtÐ ' => [
'_' => 'secureValueTypeIdentityCard',
],
'#�¤™' => [
'_' => 'secureValueTypeInternalPassport',
],
'&ãË' => [
'_' => 'secureValueTypeAddress',
],
'N•6ü' => [
'_' => 'secureValueTypeUtilityBill',
],
'|‰' => [
'_' => 'secureValueTypeBankStatement',
],
'ˆ4ˆ‹' => [
'_' => 'secureValueTypeRentalAgreement',
],
'j€ã™' => [
'_' => 'secureValueTypePassportRegistration',
],
'3ìê' => [
'_' => 'secureValueTypeTemporaryRegistration',
],
'Ûª ³' => [
'_' => 'secureValueTypePhone',
],
'î§<Ž' => [
'_' => 'secureValueTypeEmail',
],
'¡Ïr0' => self::deserialize_type_SecureValueType(self::gzdecode($stream)),
default => self::err($stream)
}
,
'file_hash' => self::deserialize_bytes($stream),
'text' => self::deserialize_string($stream),
],
'¥*Š†' => [
'_' => 'secureValueErrorReverseSide',
'type' => match (stream_get_contents($stream, 4)) {
'ã�*�' => [
'_' => 'secureValueTypePersonalDetails',
],
'' . "\0" . 'j¬=' => [
'_' => 'secureValueTypePassport',
],
'Ä%ä' => [
'_' => 'secureValueTypeDriverLicense',
],
'KtÐ ' => [
'_' => 'secureValueTypeIdentityCard',
],
'#�¤™' => [
'_' => 'secureValueTypeInternalPassport',
],
'&ãË' => [
'_' => 'secureValueTypeAddress',
],
'N•6ü' => [
'_' => 'secureValueTypeUtilityBill',
],
'|‰' => [
'_' => 'secureValueTypeBankStatement',
],
'ˆ4ˆ‹' => [
'_' => 'secureValueTypeRentalAgreement',
],
'j€ã™' => [
'_' => 'secureValueTypePassportRegistration',
],
'3ìê' => [
'_' => 'secureValueTypeTemporaryRegistration',
],
'Ûª ³' => [
'_' => 'secureValueTypePhone',
],
'î§<Ž' => [
'_' => 'secureValueTypeEmail',
],
'¡Ïr0' => self::deserialize_type_SecureValueType(self::gzdecode($stream)),
default => self::err($stream)
}
,
'file_hash' => self::deserialize_bytes($stream),
'text' => self::deserialize_string($stream),
],
'ÖÎ7å' => [
'_' => 'secureValueErrorSelfie',
'type' => match (stream_get_contents($stream, 4)) {
'ã�*�' => [
'_' => 'secureValueTypePersonalDetails',
],
'' . "\0" . 'j¬=' => [
'_' => 'secureValueTypePassport',
],
'Ä%ä' => [
'_' => 'secureValueTypeDriverLicense',
],
'KtÐ ' => [
'_' => 'secureValueTypeIdentityCard',
],
'#�¤™' => [
'_' => 'secureValueTypeInternalPassport',
],
'&ãË' => [
'_' => 'secureValueTypeAddress',
],
'N•6ü' => [
'_' => 'secureValueTypeUtilityBill',
],
'|‰' => [
'_' => 'secureValueTypeBankStatement',
],
'ˆ4ˆ‹' => [
'_' => 'secureValueTypeRentalAgreement',
],
'j€ã™' => [
'_' => 'secureValueTypePassportRegistration',
],
'3ìê' => [
'_' => 'secureValueTypeTemporaryRegistration',
],
'Ûª ³' => [
'_' => 'secureValueTypePhone',
],
'î§<Ž' => [
'_' => 'secureValueTypeEmail',
],
'¡Ïr0' => self::deserialize_type_SecureValueType(self::gzdecode($stream)),
default => self::err($stream)
}
,
'file_hash' => self::deserialize_bytes($stream),
'text' => self::deserialize_string($stream),
],
'spz' => [
'_' => 'secureValueErrorFile',
'type' => match (stream_get_contents($stream, 4)) {
'ã�*�' => [
'_' => 'secureValueTypePersonalDetails',
],
'' . "\0" . 'j¬=' => [
'_' => 'secureValueTypePassport',
],
'Ä%ä' => [
'_' => 'secureValueTypeDriverLicense',
],
'KtÐ ' => [
'_' => 'secureValueTypeIdentityCard',
],
'#�¤™' => [
'_' => 'secureValueTypeInternalPassport',
],
'&ãË' => [
'_' => 'secureValueTypeAddress',
],
'N•6ü' => [
'_' => 'secureValueTypeUtilityBill',
],
'|‰' => [
'_' => 'secureValueTypeBankStatement',
],
'ˆ4ˆ‹' => [
'_' => 'secureValueTypeRentalAgreement',
],
'j€ã™' => [
'_' => 'secureValueTypePassportRegistration',
],
'3ìê' => [
'_' => 'secureValueTypeTemporaryRegistration',
],
'Ûª ³' => [
'_' => 'secureValueTypePhone',
],
'î§<Ž' => [
'_' => 'secureValueTypeEmail',
],
'¡Ïr0' => self::deserialize_type_SecureValueType(self::gzdecode($stream)),
default => self::err($stream)
}
,
'file_hash' => self::deserialize_bytes($stream),
'text' => self::deserialize_string($stream),
],
'é bf' => [
'_' => 'secureValueErrorFiles',
'type' => match (stream_get_contents($stream, 4)) {
'ã�*�' => [
'_' => 'secureValueTypePersonalDetails',
],
'' . "\0" . 'j¬=' => [
'_' => 'secureValueTypePassport',
],
'Ä%ä' => [
'_' => 'secureValueTypeDriverLicense',
],
'KtÐ ' => [
'_' => 'secureValueTypeIdentityCard',
],
'#�¤™' => [
'_' => 'secureValueTypeInternalPassport',
],
'&ãË' => [
'_' => 'secureValueTypeAddress',
],
'N•6ü' => [
'_' => 'secureValueTypeUtilityBill',
],
'|‰' => [
'_' => 'secureValueTypeBankStatement',
],
'ˆ4ˆ‹' => [
'_' => 'secureValueTypeRentalAgreement',
],
'j€ã™' => [
'_' => 'secureValueTypePassportRegistration',
],
'3ìê' => [
'_' => 'secureValueTypeTemporaryRegistration',
],
'Ûª ³' => [
'_' => 'secureValueTypePhone',
],
'î§<Ž' => [
'_' => 'secureValueTypeEmail',
],
'¡Ïr0' => self::deserialize_type_SecureValueType(self::gzdecode($stream)),
default => self::err($stream)
}
,
'file_hash' => self::deserialize_type_array_of_bytes(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'text' => self::deserialize_string($stream),
],
'�u�†' => [
'_' => 'secureValueError',
'type' => match (stream_get_contents($stream, 4)) {
'ã�*�' => [
'_' => 'secureValueTypePersonalDetails',
],
'' . "\0" . 'j¬=' => [
'_' => 'secureValueTypePassport',
],
'Ä%ä' => [
'_' => 'secureValueTypeDriverLicense',
],
'KtÐ ' => [
'_' => 'secureValueTypeIdentityCard',
],
'#�¤™' => [
'_' => 'secureValueTypeInternalPassport',
],
'&ãË' => [
'_' => 'secureValueTypeAddress',
],
'N•6ü' => [
'_' => 'secureValueTypeUtilityBill',
],
'|‰' => [
'_' => 'secureValueTypeBankStatement',
],
'ˆ4ˆ‹' => [
'_' => 'secureValueTypeRentalAgreement',
],
'j€ã™' => [
'_' => 'secureValueTypePassportRegistration',
],
'3ìê' => [
'_' => 'secureValueTypeTemporaryRegistration',
],
'Ûª ³' => [
'_' => 'secureValueTypePhone',
],
'î§<Ž' => [
'_' => 'secureValueTypeEmail',
],
'¡Ïr0' => self::deserialize_type_SecureValueType(self::gzdecode($stream)),
default => self::err($stream)
}
,
'hash' => self::deserialize_bytes($stream),
'text' => self::deserialize_string($stream),
],
'pG¡' => [
'_' => 'secureValueErrorTranslationFile',
'type' => match (stream_get_contents($stream, 4)) {
'ã�*�' => [
'_' => 'secureValueTypePersonalDetails',
],
'' . "\0" . 'j¬=' => [
'_' => 'secureValueTypePassport',
],
'Ä%ä' => [
'_' => 'secureValueTypeDriverLicense',
],
'KtÐ ' => [
'_' => 'secureValueTypeIdentityCard',
],
'#�¤™' => [
'_' => 'secureValueTypeInternalPassport',
],
'&ãË' => [
'_' => 'secureValueTypeAddress',
],
'N•6ü' => [
'_' => 'secureValueTypeUtilityBill',
],
'|‰' => [
'_' => 'secureValueTypeBankStatement',
],
'ˆ4ˆ‹' => [
'_' => 'secureValueTypeRentalAgreement',
],
'j€ã™' => [
'_' => 'secureValueTypePassportRegistration',
],
'3ìê' => [
'_' => 'secureValueTypeTemporaryRegistration',
],
'Ûª ³' => [
'_' => 'secureValueTypePhone',
],
'î§<Ž' => [
'_' => 'secureValueTypeEmail',
],
'¡Ïr0' => self::deserialize_type_SecureValueType(self::gzdecode($stream)),
default => self::err($stream)
}
,
'file_hash' => self::deserialize_bytes($stream),
'text' => self::deserialize_string($stream),
],
'Ømc4' => [
'_' => 'secureValueErrorTranslationFiles',
'type' => match (stream_get_contents($stream, 4)) {
'ã�*�' => [
'_' => 'secureValueTypePersonalDetails',
],
'' . "\0" . 'j¬=' => [
'_' => 'secureValueTypePassport',
],
'Ä%ä' => [
'_' => 'secureValueTypeDriverLicense',
],
'KtÐ ' => [
'_' => 'secureValueTypeIdentityCard',
],
'#�¤™' => [
'_' => 'secureValueTypeInternalPassport',
],
'&ãË' => [
'_' => 'secureValueTypeAddress',
],
'N•6ü' => [
'_' => 'secureValueTypeUtilityBill',
],
'|‰' => [
'_' => 'secureValueTypeBankStatement',
],
'ˆ4ˆ‹' => [
'_' => 'secureValueTypeRentalAgreement',
],
'j€ã™' => [
'_' => 'secureValueTypePassportRegistration',
],
'3ìê' => [
'_' => 'secureValueTypeTemporaryRegistration',
],
'Ûª ³' => [
'_' => 'secureValueTypePhone',
],
'î§<Ž' => [
'_' => 'secureValueTypeEmail',
],
'¡Ïr0' => self::deserialize_type_SecureValueType(self::gzdecode($stream)),
default => self::err($stream)
}
,
'file_hash' => self::deserialize_type_array_of_bytes(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'text' => self::deserialize_string($stream),
],
'¡Ïr0' => $this->deserialize_type_SecureValueError(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_account___authorizationForm(mixed $stream): mixed {
$tmp = ['_' => 'account.authorizationForm'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['required_types'] = self::deserialize_type_array_of_SecureRequiredType(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['values'] = self::deserialize_type_array_of_SecureValue(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['errors'] = self::deserialize_type_array_of_SecureValueError(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['users'] = self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 1) !== 0) $tmp['privacy_policy_url'] = self::deserialize_string($stream);
return $tmp;

    }
    private  function deserialize_type_auth___SentCode(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'%' . "\0" . '^' => self::deserialize_auth___sentCode($stream),
'Dþ�#' => [
'_' => 'auth.sentCodeSuccess',
'authorization' => match (stream_get_contents($stream, 4)) {
'ÔÀ¢.' => self::deserialize_auth___authorization($stream),
'š~tD' => self::deserialize_auth___authorizationSignUpRequired($stream),
'¡Ïr0' => self::deserialize_type_auth___Authorization(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => $this->deserialize_type_auth___SentCode(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_updateShortMessage(mixed $stream): mixed {
$tmp = ['_' => 'updateShortMessage'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['out'] = ($flags & 2) !== 0;
$tmp['mentioned'] = ($flags & 16) !== 0;
$tmp['media_unread'] = ($flags & 32) !== 0;
$tmp['silent'] = ($flags & 8192) !== 0;
$tmp['id'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['user_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['message'] = self::deserialize_string($stream);
$tmp['pts'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['pts_count'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['date'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 4) !== 0) $tmp['fwd_from'] = match (stream_get_contents($stream, 4)) {
'»ôMN' => self::deserialize_messageFwdHeader($stream),
'¡Ïr0' => self::deserialize_type_MessageFwdHeader(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 2048) !== 0) $tmp['via_bot_id'] = unpack('q', stream_get_contents($stream, 8))[1];
if (($flags & 8) !== 0) $tmp['reply_to'] = match (stream_get_contents($stream, 4)) {
'Û	¼¯' => self::deserialize_messageReplyHeader($stream),
'9ùZ' => [
'_' => 'messageReplyStoryHeader',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'story_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_MessageReplyHeader(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 128) !== 0) $tmp['entities'] = self::deserialize_type_array_of_MessageEntity(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 33554432) !== 0) $tmp['ttl_period'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_updateShortChatMessage(mixed $stream): mixed {
$tmp = ['_' => 'updateShortChatMessage'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['out'] = ($flags & 2) !== 0;
$tmp['mentioned'] = ($flags & 16) !== 0;
$tmp['media_unread'] = ($flags & 32) !== 0;
$tmp['silent'] = ($flags & 8192) !== 0;
$tmp['id'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['from_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['chat_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['message'] = self::deserialize_string($stream);
$tmp['pts'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['pts_count'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['date'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 4) !== 0) $tmp['fwd_from'] = match (stream_get_contents($stream, 4)) {
'»ôMN' => self::deserialize_messageFwdHeader($stream),
'¡Ïr0' => self::deserialize_type_MessageFwdHeader(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 2048) !== 0) $tmp['via_bot_id'] = unpack('q', stream_get_contents($stream, 8))[1];
if (($flags & 8) !== 0) $tmp['reply_to'] = match (stream_get_contents($stream, 4)) {
'Û	¼¯' => self::deserialize_messageReplyHeader($stream),
'9ùZ' => [
'_' => 'messageReplyStoryHeader',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'story_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_MessageReplyHeader(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 128) !== 0) $tmp['entities'] = self::deserialize_type_array_of_MessageEntity(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 33554432) !== 0) $tmp['ttl_period'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_Update(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'ý
+' => [
'_' => 'updateNewMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Ö¿�N' => [
'_' => 'updateMessageID',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'random_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'å°¢' => [
'_' => 'updateDeleteMessages',
'messages' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'…À' => [
'_' => 'updateUserTyping',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'action' => match (stream_get_contents($stream, 4)) {
'Nt¿' => [
'_' => 'sendMessageTypingAction',
],
'õÈ^ý' => [
'_' => 'sendMessageCancelAction',
],
'oÖ‡¡' => [
'_' => 'sendMessageRecordVideoAction',
],
'ì:vé' => [
'_' => 'sendMessageUploadVideoAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'÷s/Õ' => [
'_' => 'sendMessageRecordAudioAction',
],
'«×Qó' => [
'_' => 'sendMessageUploadAudioAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'&JÓÑ' => [
'_' => 'sendMessageUploadPhotoAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'äÙª' => [
'_' => 'sendMessageUploadDocumentAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡‹o' => [
'_' => 'sendMessageGeoLocationAction',
],
'o¼Œb' => [
'_' => 'sendMessageChooseContactAction',
],
'H�jÝ' => [
'_' => 'sendMessageGamePlayAction',
],
'¼òˆ' => [
'_' => 'sendMessageRecordRoundAction',
],
'f>$' => [
'_' => 'sendMessageUploadRoundAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'…",Ù' => [
'_' => 'speakingInGroupCallAction',
],
'F’ÚÛ' => [
'_' => 'sendMessageHistoryImportAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'±ÆZ°' => [
'_' => 'sendMessageChooseStickerAction',
],
'Ë+—%' => [
'_' => 'sendMessageEmojiInteraction',
'emoticon' => self::deserialize_string($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'interaction' => match (stream_get_contents($stream, 4)) {
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'¡Ïr0' => self::deserialize_type_DataJSON(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'.�e¶' => [
'_' => 'sendMessageEmojiInteractionSeen',
'emoticon' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_SendMessageAction(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ðzHƒ' => [
'_' => 'updateChatUserTyping',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'from_id' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'action' => match (stream_get_contents($stream, 4)) {
'Nt¿' => [
'_' => 'sendMessageTypingAction',
],
'õÈ^ý' => [
'_' => 'sendMessageCancelAction',
],
'oÖ‡¡' => [
'_' => 'sendMessageRecordVideoAction',
],
'ì:vé' => [
'_' => 'sendMessageUploadVideoAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'÷s/Õ' => [
'_' => 'sendMessageRecordAudioAction',
],
'«×Qó' => [
'_' => 'sendMessageUploadAudioAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'&JÓÑ' => [
'_' => 'sendMessageUploadPhotoAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'äÙª' => [
'_' => 'sendMessageUploadDocumentAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡‹o' => [
'_' => 'sendMessageGeoLocationAction',
],
'o¼Œb' => [
'_' => 'sendMessageChooseContactAction',
],
'H�jÝ' => [
'_' => 'sendMessageGamePlayAction',
],
'¼òˆ' => [
'_' => 'sendMessageRecordRoundAction',
],
'f>$' => [
'_' => 'sendMessageUploadRoundAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'…",Ù' => [
'_' => 'speakingInGroupCallAction',
],
'F’ÚÛ' => [
'_' => 'sendMessageHistoryImportAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'±ÆZ°' => [
'_' => 'sendMessageChooseStickerAction',
],
'Ë+—%' => [
'_' => 'sendMessageEmojiInteraction',
'emoticon' => self::deserialize_string($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'interaction' => match (stream_get_contents($stream, 4)) {
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'¡Ïr0' => self::deserialize_type_DataJSON(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'.�e¶' => [
'_' => 'sendMessageEmojiInteractionSeen',
'emoticon' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_SendMessageAction(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'˜v' => [
'_' => 'updateChatParticipants',
'participants' => match (stream_get_contents($stream, 4)) {
'áÓc‡' => self::deserialize_chatParticipantsForbidden($stream),
'ø“¼<' => [
'_' => 'chatParticipants',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'participants' => self::deserialize_type_array_of_ChatParticipant(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_ChatParticipants(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Þø½å' => [
'_' => 'updateUserStatus',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'status' => match (stream_get_contents($stream, 4)) {
'IPÐ	' => [
'_' => 'userStatusEmpty',
],
'I9¹í' => [
'_' => 'userStatusOnline',
'expires' => unpack('l', stream_get_contents($stream, 4))[1],
],
'?pŒ' . "\0" . '' => [
'_' => 'userStatusOffline',
'was_online' => unpack('l', stream_get_contents($stream, 4))[1],
],
'È}{' => self::deserialize_userStatusRecently($stream),
'T' => self::deserialize_userStatusLastWeek($stream),
'w—‰e' => self::deserialize_userStatusLastMonth($stream),
'¡Ïr0' => self::deserialize_type_UserStatus(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'$‰„§' => [
'_' => 'updateUserName',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'first_name' => self::deserialize_string($stream),
'last_name' => self::deserialize_string($stream),
'usernames' => self::deserialize_type_array_of_Username(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ï«Q‰' => self::deserialize_updateNewAuthorization($stream),
'š½¼' => [
'_' => 'updateNewEncryptedMessage',
'message' => match (stream_get_contents($stream, 4)) {
'Áí' => [
'_' => 'encryptedMessage',
'random_id' => unpack('q', stream_get_contents($stream, 8))[1],
'chat_id' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'bytes' => self::deserialize_bytes($stream),
'file' => match (stream_get_contents($stream, 4)) {
'~IÂ' => [
'_' => 'encryptedFileEmpty',
],
'ØŒ' . "\0" . '¨' => [
'_' => 'encryptedFile',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'size' => unpack('q', stream_get_contents($stream, 8))[1],
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'key_fingerprint' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_EncryptedFile(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Ks#' => [
'_' => 'encryptedMessageService',
'random_id' => unpack('q', stream_get_contents($stream, 8))[1],
'chat_id' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'bytes' => self::deserialize_bytes($stream),
],
'¡Ïr0' => self::deserialize_type_EncryptedMessage(self::gzdecode($stream)),
default => self::err($stream)
}
,
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Vñ' => [
'_' => 'updateEncryptedChatTyping',
'chat_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'�è¢´' => [
'_' => 'updateEncryption',
'chat' => match (stream_get_contents($stream, 4)) {
' À~«' => [
'_' => 'encryptedChatEmpty',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'SY²f' => [
'_' => 'encryptedChatWaiting',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'admin_id' => unpack('q', stream_get_contents($stream, 8))[1],
'participant_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'LÙñH' => self::deserialize_encryptedChatRequested($stream),
'ÇÔða' => [
'_' => 'encryptedChat',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'admin_id' => unpack('q', stream_get_contents($stream, 8))[1],
'participant_id' => unpack('q', stream_get_contents($stream, 8))[1],
'g_a_or_b' => self::deserialize_bytes($stream),
'key_fingerprint' => stream_get_contents($stream, 8),
],
'E|' => self::deserialize_encryptedChatDiscarded($stream),
'¡Ïr0' => self::deserialize_type_EncryptedChat(self::gzdecode($stream)),
default => self::err($stream)
}
,
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'·%þ8' => [
'_' => 'updateEncryptedMessagesRead',
'chat_id' => unpack('l', stream_get_contents($stream, 4))[1],
'max_date' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'QTÚ=' => [
'_' => 'updateChatParticipantAdd',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'inviter_id' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
'w=/ã' => [
'_' => 'updateChatParticipantDelete',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
's˜^Ž' => [
'_' => 'updateDcOptions',
'dc_options' => self::deserialize_type_array_of_DcOption(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ïhÂ¾' => [
'_' => 'updateNotifySettings',
'peer' => match (stream_get_contents($stream, 4)) {
'ØÔŸ' => [
'_' => 'notifyPeer',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'L;È´' => [
'_' => 'notifyUsers',
],
'ÃÎÀ' => [
'_' => 'notifyChats',
],
'ïèÖ' => [
'_' => 'notifyBroadcasts',
],
'cn"' => [
'_' => 'notifyForumTopic',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'top_msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_NotifyPeer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'notify_settings' => match (stream_get_contents($stream, 4)) {
',b™' => self::deserialize_peerNotifySettings($stream),
'¡Ïr0' => self::deserialize_type_PeerNotifySettings(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'häë' => self::deserialize_updateServiceNotification($stream),
'*\';î' => [
'_' => 'updatePrivacy',
'key' => match (stream_get_contents($stream, 4)) {
'0«.¼' => [
'_' => 'privacyKeyStatusTimestamp',
],
'úmP' => [
'_' => 'privacyKeyChatInvite',
],
'{+f=' => [
'_' => 'privacyKeyPhoneCall',
],
'ÈI9' => [
'_' => 'privacyKeyPhoneP2P',
],
'£Vìi' => [
'_' => 'privacyKeyForwards',
],
'í–' => [
'_' => 'privacyKeyProfilePhoto',
],
'mäšÑ' => [
'_' => 'privacyKeyPhoneNumber',
],
'+ÔÿB' => [
'_' => 'privacyKeyAddedByPhone',
],
'ô—' => [
'_' => 'privacyKeyVoiceMessages',
],
'a·†¤' => [
'_' => 'privacyKeyAbout',
],
'¡Ïr0' => self::deserialize_type_PrivacyKey(self::gzdecode($stream)),
default => self::err($stream)
}
,
'rules' => self::deserialize_type_array_of_PrivacyRule(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'*I' => [
'_' => 'updateUserPhone',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'phone' => self::deserialize_string($stream),
],
'ßO—œ' => self::deserialize_updateReadHistoryInbox($stream),
'¿!//' => [
'_' => 'updateReadHistoryOutbox',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'max_id' => unpack('l', stream_get_contents($stream, 4))[1],
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'‰' => [
'_' => 'updateWebPage',
'webpage' => match (stream_get_contents($stream, 4)) {
'ˆ!' => self::deserialize_webPageEmpty($stream),
'G>Ñ°' => self::deserialize_webPagePending($stream),
'²Eœè' => self::deserialize_webPage($stream),
'Ês' => self::deserialize_webPageNotModified($stream),
'¡Ïr0' => self::deserialize_type_WebPage(self::gzdecode($stream)),
default => self::err($stream)
}
,
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'�q"ø' => self::deserialize_updateReadMessagesContents($stream),
'”�' => self::deserialize_updateChannelTooLong($stream),
'	L[c' => [
'_' => 'updateChannel',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'Ùºb' => [
'_' => 'updateNewChannelMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'n.’' => self::deserialize_updateReadChannelInbox($stream),
'[-Ã' => [
'_' => 'updateDeleteChannelMessages',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'messages' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¬&ò' => [
'_' => 'updateChannelMessageViews',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'views' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¢aÊ×' => [
'_' => 'updateChatParticipantAdmin',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'is_admin' => match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) },
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
'ª0Šh' => [
'_' => 'updateNewStickerSet',
'stickerset' => match (stream_get_contents($stream, 4)) {
'?n' => self::deserialize_messages___stickerSet($stream),
'ë$ùÓ' => [
'_' => 'messages.stickerSetNotModified',
],
'¡Ïr0' => self::deserialize_type_messages___StickerSet(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Ò²' => self::deserialize_updateStickerSetsOrder($stream),
'HÂ1' => self::deserialize_updateStickerSets($stream),
'4u“' => [
'_' => 'updateSavedGifs',
],
'œ7oI' => self::deserialize_updateBotInlineQuery($stream),
'*ñ' => self::deserialize_updateBotInlineSend($stream),
'÷M?' => [
'_' => 'updateEditChannelMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'�ÄÏ¹' => self::deserialize_updateBotCallbackQuery($stream),
'£pä' => [
'_' => 'updateEditMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'R�i' => self::deserialize_updateInlineBotCallbackQuery($stream),
'©™_·' => [
'_' => 'updateReadChannelOutbox',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'max_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'mìI' => self::deserialize_updateDraftMessage($stream),
'B\'W' => [
'_' => 'updateReadFeaturedStickers',
],
' ,Bš' => [
'_' => 'updateRecentStickers',
],
'Ý)¢' => [
'_' => 'updateConfig',
],
'�gT3' => [
'_' => 'updatePtsChanged',
],
'Ÿ©+/' => [
'_' => 'updateChannelWebPage',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'webpage' => match (stream_get_contents($stream, 4)) {
'ˆ!' => self::deserialize_webPageEmpty($stream),
'G>Ñ°' => self::deserialize_webPagePending($stream),
'²Eœè' => self::deserialize_webPage($stream),
'Ês' => self::deserialize_webPageNotModified($stream),
'¡Ïr0' => self::deserialize_type_WebPage(self::gzdecode($stream)),
default => self::err($stream)
}
,
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'åon' => self::deserialize_updateDialogPinned($stream),
'¢<ú' => self::deserialize_updatePinnedDialogs($stream),
'ÃÀƒ' => [
'_' => 'updateBotWebhookJSON',
'data' => match (stream_get_contents($stream, 4)) {
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'¡Ïr0' => self::deserialize_type_DataJSON(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¦@’›' => [
'_' => 'updateBotWebhookJSONQuery',
'query_id' => unpack('q', stream_get_contents($stream, 8))[1],
'data' => match (stream_get_contents($stream, 4)) {
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'¡Ïr0' => self::deserialize_type_DataJSON(self::gzdecode($stream)),
default => self::err($stream)
}
,
'timeout' => unpack('l', stream_get_contents($stream, 4))[1],
],
'}ý®µ' => [
'_' => 'updateBotShippingQuery',
'query_id' => unpack('q', stream_get_contents($stream, 8))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'payload' => self::deserialize_bytes($stream),
'shipping_address' => match (stream_get_contents($stream, 4)) {
'ëªŒ' => [
'_' => 'postAddress',
'street_line1' => self::deserialize_string($stream),
'street_line2' => self::deserialize_string($stream),
'city' => self::deserialize_string($stream),
'state' => self::deserialize_string($stream),
'country_iso2' => self::deserialize_string($stream),
'post_code' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_PostAddress(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'–šªŒ' => self::deserialize_updateBotPrecheckoutQuery($stream),
'k«' => [
'_' => 'updatePhoneCall',
'phone_call' => match (stream_get_contents($stream, 4)) {
'ÉfS' => [
'_' => 'phoneCallEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'o"Å' => self::deserialize_phoneCallWaiting($stream),
'í°' => self::deserialize_phoneCallRequested($stream),
'Ã`6' => self::deserialize_phoneCallAccepted($stream),
'g|–' => self::deserialize_phoneCall($stream),
'áMÊP' => self::deserialize_phoneCallDiscarded($stream),
'¡Ïr0' => self::deserialize_type_PhoneCall(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'dVF' => [
'_' => 'updateLangPackTooLong',
'lang_code' => self::deserialize_string($stream),
],
'M/V' => [
'_' => 'updateLangPack',
'difference' => match (stream_get_contents($stream, 4)) {
'öÁ…ó' => [
'_' => 'langPackDifference',
'lang_code' => self::deserialize_string($stream),
'from_version' => unpack('l', stream_get_contents($stream, 4))[1],
'version' => unpack('l', stream_get_contents($stream, 4))[1],
'strings' => self::deserialize_type_array_of_LangPackString(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => self::deserialize_type_LangPackDifference(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'm™å' => [
'_' => 'updateFavedStickers',
],
'])ê' => self::deserialize_updateChannelReadMessagesContents($stream),
'¾§„p' => [
'_' => 'updateContactsReset',
],
'˜Æ?²' => [
'_' => 'updateChannelAvailableMessages',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'available_min_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'ÃYdá' => self::deserialize_updateDialogUnreadMark($stream),
'{e¡¬' => self::deserialize_updateMessagePoll($stream),
'PÀT' => [
'_' => 'updateChatDefaultBannedRights',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'default_banned_rights' => match (stream_get_contents($stream, 4)) {
'Ÿ' => self::deserialize_chatBannedRights($stream),
'¡Ïr0' => self::deserialize_type_ChatBannedRights(self::gzdecode($stream)),
default => self::err($stream)
}
,
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
'À6' => [
'_' => 'updateFolderPeers',
'folder_peers' => self::deserialize_type_array_of_FolderPeer(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'fs~j' => [
'_' => 'updatePeerSettings',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'settings' => match (stream_get_contents($stream, 4)) {
'¥' => self::deserialize_peerSettings($stream),
'¡Ïr0' => self::deserialize_type_PeerSettings(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'°Ï¯´' => [
'_' => 'updatePeerLocated',
'peers' => self::deserialize_type_array_of_PeerLocated(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'û¥9' => [
'_' => 'updateNewScheduledMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'îl†�' => [
'_' => 'updateDeleteScheduledMessages',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'messages' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'£û‚' => [
'_' => 'updateTheme',
'theme' => match (stream_get_contents($stream, 4)) {
'Ög ' => self::deserialize_theme($stream),
'¡Ïr0' => self::deserialize_type_Theme(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'9¹‡' => [
'_' => 'updateGeoLiveViewed',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'‘æOV' => [
'_' => 'updateLoginToken',
],
'wô$' => [
'_' => 'updateMessagePollVote',
'poll_id' => unpack('q', stream_get_contents($stream, 8))[1],
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'options' => self::deserialize_type_array_of_bytes(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'}Þÿ&' => self::deserialize_updateDialogFilter($stream),
'!×¥' => [
'_' => 'updateDialogFilterOrder',
'order' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'O‘5' => [
'_' => 'updateDialogFilters',
],
'	¿a&' => [
'_' => 'updatePhoneCallSignalingData',
'phone_call_id' => unpack('q', stream_get_contents($stream, 8))[1],
'data' => self::deserialize_bytes($stream),
],
'ô\'šÒ' => [
'_' => 'updateChannelMessageForwards',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'forwards' => unpack('l', stream_get_contents($stream, 4))[1],
],
'F•±Ö' => self::deserialize_updateReadChannelDiscussionInbox($stream),
'|ž\\i' => [
'_' => 'updateReadChannelDiscussionOutbox',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'top_msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'read_max_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Rwàë' => self::deserialize_updatePeerBlocked($stream),
'#ÉˆŒ' => self::deserialize_updateChannelUserTyping($stream),
'µê…í' => self::deserialize_updatePinnedMessages($stream),
'†¹[' => self::deserialize_updatePinnedChannelMessages($stream),
'Njšø' => [
'_' => 'updateChat',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'NÛëò' => [
'_' => 'updateGroupCallParticipants',
'call' => match (stream_get_contents($stream, 4)) {
'„ªØ' => [
'_' => 'inputGroupCall',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputGroupCall(self::gzdecode($stream)),
default => self::err($stream)
}
,
'participants' => self::deserialize_type_array_of_GroupCallParticipant(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
'' . "\0" . 'E²' => [
'_' => 'updateGroupCall',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'call' => match (stream_get_contents($stream, 4)) {
'´¼€w' => [
'_' => 'groupCallDiscarded',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'duration' => unpack('l', stream_get_contents($stream, 4))[1],
],
'e—Õ' => self::deserialize_groupCall($stream),
'¡Ïr0' => self::deserialize_type_GroupCall(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¥¹›»' => self::deserialize_updatePeerHistoryTTL($stream),
':f‡Ð' => self::deserialize_updateChatParticipant($stream),
'»:]˜' => self::deserialize_updateChannelParticipant($stream),
'I
‡Ä' => [
'_' => 'updateBotStopped',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'stopped' => match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) },
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'‚9x' => self::deserialize_updateGroupCallConnection($stream),
'./qM' => [
'_' => 'updateBotCommands',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'bot_id' => unpack('q', stream_get_contents($stream, 8))[1],
'commands' => self::deserialize_type_array_of_BotCommand(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ÛÃcp' => [
'_' => 'updatePendingJoinRequests',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'requests_pending' => unpack('l', stream_get_contents($stream, 4))[1],
'recent_requesters' => self::deserialize_type_array_of_long(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'†©ß' => [
'_' => 'updateBotChatInviteRequester',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'about' => self::deserialize_string($stream),
'invite' => match (stream_get_contents($stream, 4)) {
'¨´
' => self::deserialize_chatInviteExported($stream),
'·zí' => [
'_' => 'chatInvitePublicJoinRequests',
],
'¡Ïr0' => self::deserialize_type_ExportedChatInvite(self::gzdecode($stream)),
default => self::err($stream)
}
,
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¸<^' => self::deserialize_updateMessageReactions($stream),
'¢·' => [
'_' => 'updateAttachMenuBots',
],
'�·’' => [
'_' => 'updateWebViewResultSent',
'query_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'X¸' => [
'_' => 'updateBotMenuButton',
'bot_id' => unpack('q', stream_get_contents($stream, 8))[1],
'button' => match (stream_get_contents($stream, 4)) {
'ˆ¥3u' => [
'_' => 'botMenuButtonDefault',
],
'ÂXB' => [
'_' => 'botMenuButtonCommands',
],
'æ|µÇ' => [
'_' => 'botMenuButton',
'text' => self::deserialize_string($stream),
'url' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_BotMenuButton(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'™¾Øt' => [
'_' => 'updateSavedRingtones',
],
'ZÍ„' . "\0" . '' => self::deserialize_updateTranscribedAudio($stream),
'lILû' => [
'_' => 'updateReadFeaturedEmojiStickers',
],
'™57(' => [
'_' => 'updateUserEmojiStatus',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'emoji_status' => match (stream_get_contents($stream, 4)) {
'®á-' => [
'_' => 'emojiStatusEmpty',
],
'�a›’' => [
'_' => 'emojiStatus',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'Ç¨0ú' => [
'_' => 'emojiStatusUntil',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'until' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_EmojiStatus(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ÛCô0' => [
'_' => 'updateRecentEmojiStatuses',
],
'ôcxo' => [
'_' => 'updateRecentReactions',
],
'…Ïü†' => self::deserialize_updateMoveStickerSetToTop($stream),
'Œ©sZ' => [
'_' => 'updateMessageExtendedMedia',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'extended_media' => match (stream_get_contents($stream, 4)) {
'ÈŒb­' => self::deserialize_messageExtendedMediaPreview($stream),
'dœGî' => [
'_' => 'messageExtendedMedia',
'media' => match (stream_get_contents($stream, 4)) {
' cí=' => [
'_' => 'messageMediaEmpty',
],
'×PQi' => self::deserialize_messageMediaPhoto($stream),
'tÔàV' => [
'_' => 'messageMediaGeo',
'geo' => match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => self::deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'I)2p' => [
'_' => 'messageMediaContact',
'phone_number' => self::deserialize_string($stream),
'first_name' => self::deserialize_string($stream),
'last_name' => self::deserialize_string($stream),
'vcard' => self::deserialize_string($stream),
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'žô„Ÿ' => [
'_' => 'messageMediaUnsupported',
],
'-×ôL' => self::deserialize_messageMediaDocument($stream),
';ñÝ' => self::deserialize_messageMediaWebPage($stream),
'?SÀ.' => [
'_' => 'messageMediaVenue',
'geo' => match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => self::deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
,
'title' => self::deserialize_string($stream),
'address' => self::deserialize_string($stream),
'provider' => self::deserialize_string($stream),
'venue_id' => self::deserialize_string($stream),
'venue_type' => self::deserialize_string($stream),
],
'�±ý' => [
'_' => 'messageMediaGame',
'game' => match (stream_get_contents($stream, 4)) {
';eù½' => self::deserialize_game($stream),
'¡Ïr0' => self::deserialize_type_Game(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ÓH¥ö' => self::deserialize_messageMediaInvoice($stream),
'fÆ@¹' => self::deserialize_messageMediaGeoLive($stream),
'˜çÖK' => [
'_' => 'messageMediaPoll',
'poll' => match (stream_get_contents($stream, 4)) {
'a�á†' => self::deserialize_poll($stream),
'¡Ïr0' => self::deserialize_type_Poll(self::gzdecode($stream)),
default => self::err($stream)
}
,
'results' => match (stream_get_contents($stream, 4)) {
' $ßz' => self::deserialize_pollResults($stream),
'¡Ïr0' => self::deserialize_type_PollResults(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'‹å~?' => [
'_' => 'messageMediaDice',
'value' => unpack('l', stream_get_contents($stream, 4))[1],
'emoticon' => self::deserialize_string($stream),
],
'ƒbËh' => self::deserialize_messageMediaStory($stream),
'°…­Ú' => self::deserialize_messageMediaGiveaway($stream),
'h™Æ' => self::deserialize_messageMediaGiveawayResults($stream),
'¡Ïr0' => self::deserialize_type_MessageMedia(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_MessageExtendedMedia(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ãû.' => self::deserialize_updateChannelPinnedTopic($stream),
'†þ' => self::deserialize_updateChannelPinnedTopics($stream),
'8”R ' => [
'_' => 'updateUser',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'—°ì' => [
'_' => 'updateAutoSaveSettings',
],
'ÖŠðÌ' => [
'_' => 'updateGroupInvitePrivacyForbidden',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'˜·³u' => [
'_' => 'updateStory',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'story' => match (stream_get_contents($stream, 4)) {
'OîæQ' => [
'_' => 'storyItemDeleted',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'É­ÿ' => self::deserialize_storyItemSkipped($stream),
'$j²y' => self::deserialize_storyItem($stream),
'¡Ïr0' => self::deserialize_type_StoryItem(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'+“N÷' => [
'_' => 'updateReadStories',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'max_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¹5ó' => [
'_' => 'updateStoryID',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'random_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÁM,' => [
'_' => 'updateStoriesStealthMode',
'stealth_mode' => match (stream_get_contents($stream, 4)) {
'ý\'.q' => self::deserialize_storiesStealthMode($stream),
'¡Ïr0' => self::deserialize_type_StoriesStealthMode(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ƒvb}' => [
'_' => 'updateSentStoryReaction',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'story_id' => unpack('l', stream_get_contents($stream, 4))[1],
'reaction' => match (stream_get_contents($stream, 4)) {
'Ôõy' => [
'_' => 'reactionEmpty',
],
'¸†"' => [
'_' => 'reactionEmoji',
'emoticon' => self::deserialize_string($stream),
],
'sü5‰' => [
'_' => 'reactionCustomEmoji',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_Reaction(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'œÔM�' => [
'_' => 'updateBotChatBoost',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'boost' => match (stream_get_contents($stream, 4)) {
'qŒ*' => self::deserialize_boost($stream),
'¡Ïr0' => self::deserialize_type_Boost(self::gzdecode($stream)),
default => self::err($stream)
}
,
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
' ‰¶' => [
'_' => 'updateChannelViewForumAsMessages',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'enabled' => match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) },
],
'?®' => self::deserialize_updatePeerWallpaper($stream),
'ÎÓ!¬' => [
'_' => 'updateBotMessageReaction',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'actor' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'old_reactions' => self::deserialize_type_array_of_Reaction(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'new_reactions' => self::deserialize_type_array_of_Reaction(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'YwË	' => [
'_' => 'updateBotMessageReactions',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'reactions' => self::deserialize_type_array_of_ReactionCount(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'tž¯®' => self::deserialize_updateSavedDialogPinned($stream),
'¦…lh' => self::deserialize_updatePinnedSavedDialogs($stream),
'2tÆ9' => [
'_' => 'updateSavedReactionTags',
],
'Ôibñ' => [
'_' => 'updateSmsJob',
'job_id' => self::deserialize_string($stream),
],
'²
Gù' => [
'_' => 'updateQuickReplies',
'quick_replies' => self::deserialize_type_array_of_QuickReply(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'§=õ' => [
'_' => 'updateNewQuickReply',
'quick_reply' => match (stream_get_contents($stream, 4)) {
'+—' => [
'_' => 'quickReply',
'shortcut_id' => unpack('l', stream_get_contents($stream, 4))[1],
'shortcut' => self::deserialize_string($stream),
'top_message' => unpack('l', stream_get_contents($stream, 4))[1],
'count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_QuickReply(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ìñæS' => [
'_' => 'updateDeleteQuickReply',
'shortcut_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'>' => [
'_' => 'updateQuickReplyMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ÍçoV' => [
'_' => 'updateDeleteQuickReplyMessages',
'shortcut_id' => unpack('l', stream_get_contents($stream, 4))[1],
'messages' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => $this->deserialize_type_Update(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_array_of_Update(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'ý
+' => [
'_' => 'updateNewMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Ö¿�N' => [
'_' => 'updateMessageID',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'random_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'å°¢' => [
'_' => 'updateDeleteMessages',
'messages' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'…À' => [
'_' => 'updateUserTyping',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'action' => match (stream_get_contents($stream, 4)) {
'Nt¿' => [
'_' => 'sendMessageTypingAction',
],
'õÈ^ý' => [
'_' => 'sendMessageCancelAction',
],
'oÖ‡¡' => [
'_' => 'sendMessageRecordVideoAction',
],
'ì:vé' => [
'_' => 'sendMessageUploadVideoAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'÷s/Õ' => [
'_' => 'sendMessageRecordAudioAction',
],
'«×Qó' => [
'_' => 'sendMessageUploadAudioAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'&JÓÑ' => [
'_' => 'sendMessageUploadPhotoAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'äÙª' => [
'_' => 'sendMessageUploadDocumentAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡‹o' => [
'_' => 'sendMessageGeoLocationAction',
],
'o¼Œb' => [
'_' => 'sendMessageChooseContactAction',
],
'H�jÝ' => [
'_' => 'sendMessageGamePlayAction',
],
'¼òˆ' => [
'_' => 'sendMessageRecordRoundAction',
],
'f>$' => [
'_' => 'sendMessageUploadRoundAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'…",Ù' => [
'_' => 'speakingInGroupCallAction',
],
'F’ÚÛ' => [
'_' => 'sendMessageHistoryImportAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'±ÆZ°' => [
'_' => 'sendMessageChooseStickerAction',
],
'Ë+—%' => [
'_' => 'sendMessageEmojiInteraction',
'emoticon' => self::deserialize_string($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'interaction' => match (stream_get_contents($stream, 4)) {
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'¡Ïr0' => self::deserialize_type_DataJSON(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'.�e¶' => [
'_' => 'sendMessageEmojiInteractionSeen',
'emoticon' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_SendMessageAction(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ðzHƒ' => [
'_' => 'updateChatUserTyping',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'from_id' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'action' => match (stream_get_contents($stream, 4)) {
'Nt¿' => [
'_' => 'sendMessageTypingAction',
],
'õÈ^ý' => [
'_' => 'sendMessageCancelAction',
],
'oÖ‡¡' => [
'_' => 'sendMessageRecordVideoAction',
],
'ì:vé' => [
'_' => 'sendMessageUploadVideoAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'÷s/Õ' => [
'_' => 'sendMessageRecordAudioAction',
],
'«×Qó' => [
'_' => 'sendMessageUploadAudioAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'&JÓÑ' => [
'_' => 'sendMessageUploadPhotoAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'äÙª' => [
'_' => 'sendMessageUploadDocumentAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡‹o' => [
'_' => 'sendMessageGeoLocationAction',
],
'o¼Œb' => [
'_' => 'sendMessageChooseContactAction',
],
'H�jÝ' => [
'_' => 'sendMessageGamePlayAction',
],
'¼òˆ' => [
'_' => 'sendMessageRecordRoundAction',
],
'f>$' => [
'_' => 'sendMessageUploadRoundAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'…",Ù' => [
'_' => 'speakingInGroupCallAction',
],
'F’ÚÛ' => [
'_' => 'sendMessageHistoryImportAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'±ÆZ°' => [
'_' => 'sendMessageChooseStickerAction',
],
'Ë+—%' => [
'_' => 'sendMessageEmojiInteraction',
'emoticon' => self::deserialize_string($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'interaction' => match (stream_get_contents($stream, 4)) {
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'¡Ïr0' => self::deserialize_type_DataJSON(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'.�e¶' => [
'_' => 'sendMessageEmojiInteractionSeen',
'emoticon' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_SendMessageAction(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'˜v' => [
'_' => 'updateChatParticipants',
'participants' => match (stream_get_contents($stream, 4)) {
'áÓc‡' => self::deserialize_chatParticipantsForbidden($stream),
'ø“¼<' => [
'_' => 'chatParticipants',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'participants' => self::deserialize_type_array_of_ChatParticipant(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_ChatParticipants(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Þø½å' => [
'_' => 'updateUserStatus',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'status' => match (stream_get_contents($stream, 4)) {
'IPÐ	' => [
'_' => 'userStatusEmpty',
],
'I9¹í' => [
'_' => 'userStatusOnline',
'expires' => unpack('l', stream_get_contents($stream, 4))[1],
],
'?pŒ' . "\0" . '' => [
'_' => 'userStatusOffline',
'was_online' => unpack('l', stream_get_contents($stream, 4))[1],
],
'È}{' => self::deserialize_userStatusRecently($stream),
'T' => self::deserialize_userStatusLastWeek($stream),
'w—‰e' => self::deserialize_userStatusLastMonth($stream),
'¡Ïr0' => self::deserialize_type_UserStatus(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'$‰„§' => [
'_' => 'updateUserName',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'first_name' => self::deserialize_string($stream),
'last_name' => self::deserialize_string($stream),
'usernames' => self::deserialize_type_array_of_Username(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ï«Q‰' => self::deserialize_updateNewAuthorization($stream),
'š½¼' => [
'_' => 'updateNewEncryptedMessage',
'message' => match (stream_get_contents($stream, 4)) {
'Áí' => [
'_' => 'encryptedMessage',
'random_id' => unpack('q', stream_get_contents($stream, 8))[1],
'chat_id' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'bytes' => self::deserialize_bytes($stream),
'file' => match (stream_get_contents($stream, 4)) {
'~IÂ' => [
'_' => 'encryptedFileEmpty',
],
'ØŒ' . "\0" . '¨' => [
'_' => 'encryptedFile',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'size' => unpack('q', stream_get_contents($stream, 8))[1],
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'key_fingerprint' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_EncryptedFile(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Ks#' => [
'_' => 'encryptedMessageService',
'random_id' => unpack('q', stream_get_contents($stream, 8))[1],
'chat_id' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'bytes' => self::deserialize_bytes($stream),
],
'¡Ïr0' => self::deserialize_type_EncryptedMessage(self::gzdecode($stream)),
default => self::err($stream)
}
,
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Vñ' => [
'_' => 'updateEncryptedChatTyping',
'chat_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'�è¢´' => [
'_' => 'updateEncryption',
'chat' => match (stream_get_contents($stream, 4)) {
' À~«' => [
'_' => 'encryptedChatEmpty',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'SY²f' => [
'_' => 'encryptedChatWaiting',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'admin_id' => unpack('q', stream_get_contents($stream, 8))[1],
'participant_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'LÙñH' => self::deserialize_encryptedChatRequested($stream),
'ÇÔða' => [
'_' => 'encryptedChat',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'admin_id' => unpack('q', stream_get_contents($stream, 8))[1],
'participant_id' => unpack('q', stream_get_contents($stream, 8))[1],
'g_a_or_b' => self::deserialize_bytes($stream),
'key_fingerprint' => stream_get_contents($stream, 8),
],
'E|' => self::deserialize_encryptedChatDiscarded($stream),
'¡Ïr0' => self::deserialize_type_EncryptedChat(self::gzdecode($stream)),
default => self::err($stream)
}
,
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'·%þ8' => [
'_' => 'updateEncryptedMessagesRead',
'chat_id' => unpack('l', stream_get_contents($stream, 4))[1],
'max_date' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'QTÚ=' => [
'_' => 'updateChatParticipantAdd',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'inviter_id' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
'w=/ã' => [
'_' => 'updateChatParticipantDelete',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
's˜^Ž' => [
'_' => 'updateDcOptions',
'dc_options' => self::deserialize_type_array_of_DcOption(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ïhÂ¾' => [
'_' => 'updateNotifySettings',
'peer' => match (stream_get_contents($stream, 4)) {
'ØÔŸ' => [
'_' => 'notifyPeer',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'L;È´' => [
'_' => 'notifyUsers',
],
'ÃÎÀ' => [
'_' => 'notifyChats',
],
'ïèÖ' => [
'_' => 'notifyBroadcasts',
],
'cn"' => [
'_' => 'notifyForumTopic',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'top_msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_NotifyPeer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'notify_settings' => match (stream_get_contents($stream, 4)) {
',b™' => self::deserialize_peerNotifySettings($stream),
'¡Ïr0' => self::deserialize_type_PeerNotifySettings(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'häë' => self::deserialize_updateServiceNotification($stream),
'*\';î' => [
'_' => 'updatePrivacy',
'key' => match (stream_get_contents($stream, 4)) {
'0«.¼' => [
'_' => 'privacyKeyStatusTimestamp',
],
'úmP' => [
'_' => 'privacyKeyChatInvite',
],
'{+f=' => [
'_' => 'privacyKeyPhoneCall',
],
'ÈI9' => [
'_' => 'privacyKeyPhoneP2P',
],
'£Vìi' => [
'_' => 'privacyKeyForwards',
],
'í–' => [
'_' => 'privacyKeyProfilePhoto',
],
'mäšÑ' => [
'_' => 'privacyKeyPhoneNumber',
],
'+ÔÿB' => [
'_' => 'privacyKeyAddedByPhone',
],
'ô—' => [
'_' => 'privacyKeyVoiceMessages',
],
'a·†¤' => [
'_' => 'privacyKeyAbout',
],
'¡Ïr0' => self::deserialize_type_PrivacyKey(self::gzdecode($stream)),
default => self::err($stream)
}
,
'rules' => self::deserialize_type_array_of_PrivacyRule(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'*I' => [
'_' => 'updateUserPhone',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'phone' => self::deserialize_string($stream),
],
'ßO—œ' => self::deserialize_updateReadHistoryInbox($stream),
'¿!//' => [
'_' => 'updateReadHistoryOutbox',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'max_id' => unpack('l', stream_get_contents($stream, 4))[1],
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'‰' => [
'_' => 'updateWebPage',
'webpage' => match (stream_get_contents($stream, 4)) {
'ˆ!' => self::deserialize_webPageEmpty($stream),
'G>Ñ°' => self::deserialize_webPagePending($stream),
'²Eœè' => self::deserialize_webPage($stream),
'Ês' => self::deserialize_webPageNotModified($stream),
'¡Ïr0' => self::deserialize_type_WebPage(self::gzdecode($stream)),
default => self::err($stream)
}
,
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'�q"ø' => self::deserialize_updateReadMessagesContents($stream),
'”�' => self::deserialize_updateChannelTooLong($stream),
'	L[c' => [
'_' => 'updateChannel',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'Ùºb' => [
'_' => 'updateNewChannelMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'n.’' => self::deserialize_updateReadChannelInbox($stream),
'[-Ã' => [
'_' => 'updateDeleteChannelMessages',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'messages' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¬&ò' => [
'_' => 'updateChannelMessageViews',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'views' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¢aÊ×' => [
'_' => 'updateChatParticipantAdmin',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'is_admin' => match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) },
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
'ª0Šh' => [
'_' => 'updateNewStickerSet',
'stickerset' => match (stream_get_contents($stream, 4)) {
'?n' => self::deserialize_messages___stickerSet($stream),
'ë$ùÓ' => [
'_' => 'messages.stickerSetNotModified',
],
'¡Ïr0' => self::deserialize_type_messages___StickerSet(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Ò²' => self::deserialize_updateStickerSetsOrder($stream),
'HÂ1' => self::deserialize_updateStickerSets($stream),
'4u“' => [
'_' => 'updateSavedGifs',
],
'œ7oI' => self::deserialize_updateBotInlineQuery($stream),
'*ñ' => self::deserialize_updateBotInlineSend($stream),
'÷M?' => [
'_' => 'updateEditChannelMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'�ÄÏ¹' => self::deserialize_updateBotCallbackQuery($stream),
'£pä' => [
'_' => 'updateEditMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'R�i' => self::deserialize_updateInlineBotCallbackQuery($stream),
'©™_·' => [
'_' => 'updateReadChannelOutbox',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'max_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'mìI' => self::deserialize_updateDraftMessage($stream),
'B\'W' => [
'_' => 'updateReadFeaturedStickers',
],
' ,Bš' => [
'_' => 'updateRecentStickers',
],
'Ý)¢' => [
'_' => 'updateConfig',
],
'�gT3' => [
'_' => 'updatePtsChanged',
],
'Ÿ©+/' => [
'_' => 'updateChannelWebPage',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'webpage' => match (stream_get_contents($stream, 4)) {
'ˆ!' => self::deserialize_webPageEmpty($stream),
'G>Ñ°' => self::deserialize_webPagePending($stream),
'²Eœè' => self::deserialize_webPage($stream),
'Ês' => self::deserialize_webPageNotModified($stream),
'¡Ïr0' => self::deserialize_type_WebPage(self::gzdecode($stream)),
default => self::err($stream)
}
,
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'åon' => self::deserialize_updateDialogPinned($stream),
'¢<ú' => self::deserialize_updatePinnedDialogs($stream),
'ÃÀƒ' => [
'_' => 'updateBotWebhookJSON',
'data' => match (stream_get_contents($stream, 4)) {
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'¡Ïr0' => self::deserialize_type_DataJSON(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¦@’›' => [
'_' => 'updateBotWebhookJSONQuery',
'query_id' => unpack('q', stream_get_contents($stream, 8))[1],
'data' => match (stream_get_contents($stream, 4)) {
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'¡Ïr0' => self::deserialize_type_DataJSON(self::gzdecode($stream)),
default => self::err($stream)
}
,
'timeout' => unpack('l', stream_get_contents($stream, 4))[1],
],
'}ý®µ' => [
'_' => 'updateBotShippingQuery',
'query_id' => unpack('q', stream_get_contents($stream, 8))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'payload' => self::deserialize_bytes($stream),
'shipping_address' => match (stream_get_contents($stream, 4)) {
'ëªŒ' => [
'_' => 'postAddress',
'street_line1' => self::deserialize_string($stream),
'street_line2' => self::deserialize_string($stream),
'city' => self::deserialize_string($stream),
'state' => self::deserialize_string($stream),
'country_iso2' => self::deserialize_string($stream),
'post_code' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_PostAddress(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'–šªŒ' => self::deserialize_updateBotPrecheckoutQuery($stream),
'k«' => [
'_' => 'updatePhoneCall',
'phone_call' => match (stream_get_contents($stream, 4)) {
'ÉfS' => [
'_' => 'phoneCallEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'o"Å' => self::deserialize_phoneCallWaiting($stream),
'í°' => self::deserialize_phoneCallRequested($stream),
'Ã`6' => self::deserialize_phoneCallAccepted($stream),
'g|–' => self::deserialize_phoneCall($stream),
'áMÊP' => self::deserialize_phoneCallDiscarded($stream),
'¡Ïr0' => self::deserialize_type_PhoneCall(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'dVF' => [
'_' => 'updateLangPackTooLong',
'lang_code' => self::deserialize_string($stream),
],
'M/V' => [
'_' => 'updateLangPack',
'difference' => match (stream_get_contents($stream, 4)) {
'öÁ…ó' => [
'_' => 'langPackDifference',
'lang_code' => self::deserialize_string($stream),
'from_version' => unpack('l', stream_get_contents($stream, 4))[1],
'version' => unpack('l', stream_get_contents($stream, 4))[1],
'strings' => self::deserialize_type_array_of_LangPackString(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => self::deserialize_type_LangPackDifference(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'm™å' => [
'_' => 'updateFavedStickers',
],
'])ê' => self::deserialize_updateChannelReadMessagesContents($stream),
'¾§„p' => [
'_' => 'updateContactsReset',
],
'˜Æ?²' => [
'_' => 'updateChannelAvailableMessages',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'available_min_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'ÃYdá' => self::deserialize_updateDialogUnreadMark($stream),
'{e¡¬' => self::deserialize_updateMessagePoll($stream),
'PÀT' => [
'_' => 'updateChatDefaultBannedRights',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'default_banned_rights' => match (stream_get_contents($stream, 4)) {
'Ÿ' => self::deserialize_chatBannedRights($stream),
'¡Ïr0' => self::deserialize_type_ChatBannedRights(self::gzdecode($stream)),
default => self::err($stream)
}
,
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
'À6' => [
'_' => 'updateFolderPeers',
'folder_peers' => self::deserialize_type_array_of_FolderPeer(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'fs~j' => [
'_' => 'updatePeerSettings',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'settings' => match (stream_get_contents($stream, 4)) {
'¥' => self::deserialize_peerSettings($stream),
'¡Ïr0' => self::deserialize_type_PeerSettings(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'°Ï¯´' => [
'_' => 'updatePeerLocated',
'peers' => self::deserialize_type_array_of_PeerLocated(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'û¥9' => [
'_' => 'updateNewScheduledMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'îl†�' => [
'_' => 'updateDeleteScheduledMessages',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'messages' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'£û‚' => [
'_' => 'updateTheme',
'theme' => match (stream_get_contents($stream, 4)) {
'Ög ' => self::deserialize_theme($stream),
'¡Ïr0' => self::deserialize_type_Theme(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'9¹‡' => [
'_' => 'updateGeoLiveViewed',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'‘æOV' => [
'_' => 'updateLoginToken',
],
'wô$' => [
'_' => 'updateMessagePollVote',
'poll_id' => unpack('q', stream_get_contents($stream, 8))[1],
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'options' => self::deserialize_type_array_of_bytes(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'}Þÿ&' => self::deserialize_updateDialogFilter($stream),
'!×¥' => [
'_' => 'updateDialogFilterOrder',
'order' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'O‘5' => [
'_' => 'updateDialogFilters',
],
'	¿a&' => [
'_' => 'updatePhoneCallSignalingData',
'phone_call_id' => unpack('q', stream_get_contents($stream, 8))[1],
'data' => self::deserialize_bytes($stream),
],
'ô\'šÒ' => [
'_' => 'updateChannelMessageForwards',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'forwards' => unpack('l', stream_get_contents($stream, 4))[1],
],
'F•±Ö' => self::deserialize_updateReadChannelDiscussionInbox($stream),
'|ž\\i' => [
'_' => 'updateReadChannelDiscussionOutbox',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'top_msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'read_max_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Rwàë' => self::deserialize_updatePeerBlocked($stream),
'#ÉˆŒ' => self::deserialize_updateChannelUserTyping($stream),
'µê…í' => self::deserialize_updatePinnedMessages($stream),
'†¹[' => self::deserialize_updatePinnedChannelMessages($stream),
'Njšø' => [
'_' => 'updateChat',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'NÛëò' => [
'_' => 'updateGroupCallParticipants',
'call' => match (stream_get_contents($stream, 4)) {
'„ªØ' => [
'_' => 'inputGroupCall',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputGroupCall(self::gzdecode($stream)),
default => self::err($stream)
}
,
'participants' => self::deserialize_type_array_of_GroupCallParticipant(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
'' . "\0" . 'E²' => [
'_' => 'updateGroupCall',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'call' => match (stream_get_contents($stream, 4)) {
'´¼€w' => [
'_' => 'groupCallDiscarded',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'duration' => unpack('l', stream_get_contents($stream, 4))[1],
],
'e—Õ' => self::deserialize_groupCall($stream),
'¡Ïr0' => self::deserialize_type_GroupCall(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¥¹›»' => self::deserialize_updatePeerHistoryTTL($stream),
':f‡Ð' => self::deserialize_updateChatParticipant($stream),
'»:]˜' => self::deserialize_updateChannelParticipant($stream),
'I
‡Ä' => [
'_' => 'updateBotStopped',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'stopped' => match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) },
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'‚9x' => self::deserialize_updateGroupCallConnection($stream),
'./qM' => [
'_' => 'updateBotCommands',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'bot_id' => unpack('q', stream_get_contents($stream, 8))[1],
'commands' => self::deserialize_type_array_of_BotCommand(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ÛÃcp' => [
'_' => 'updatePendingJoinRequests',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'requests_pending' => unpack('l', stream_get_contents($stream, 4))[1],
'recent_requesters' => self::deserialize_type_array_of_long(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'†©ß' => [
'_' => 'updateBotChatInviteRequester',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'about' => self::deserialize_string($stream),
'invite' => match (stream_get_contents($stream, 4)) {
'¨´
' => self::deserialize_chatInviteExported($stream),
'·zí' => [
'_' => 'chatInvitePublicJoinRequests',
],
'¡Ïr0' => self::deserialize_type_ExportedChatInvite(self::gzdecode($stream)),
default => self::err($stream)
}
,
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¸<^' => self::deserialize_updateMessageReactions($stream),
'¢·' => [
'_' => 'updateAttachMenuBots',
],
'�·’' => [
'_' => 'updateWebViewResultSent',
'query_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'X¸' => [
'_' => 'updateBotMenuButton',
'bot_id' => unpack('q', stream_get_contents($stream, 8))[1],
'button' => match (stream_get_contents($stream, 4)) {
'ˆ¥3u' => [
'_' => 'botMenuButtonDefault',
],
'ÂXB' => [
'_' => 'botMenuButtonCommands',
],
'æ|µÇ' => [
'_' => 'botMenuButton',
'text' => self::deserialize_string($stream),
'url' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_BotMenuButton(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'™¾Øt' => [
'_' => 'updateSavedRingtones',
],
'ZÍ„' . "\0" . '' => self::deserialize_updateTranscribedAudio($stream),
'lILû' => [
'_' => 'updateReadFeaturedEmojiStickers',
],
'™57(' => [
'_' => 'updateUserEmojiStatus',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'emoji_status' => match (stream_get_contents($stream, 4)) {
'®á-' => [
'_' => 'emojiStatusEmpty',
],
'�a›’' => [
'_' => 'emojiStatus',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'Ç¨0ú' => [
'_' => 'emojiStatusUntil',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'until' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_EmojiStatus(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ÛCô0' => [
'_' => 'updateRecentEmojiStatuses',
],
'ôcxo' => [
'_' => 'updateRecentReactions',
],
'…Ïü†' => self::deserialize_updateMoveStickerSetToTop($stream),
'Œ©sZ' => [
'_' => 'updateMessageExtendedMedia',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'extended_media' => match (stream_get_contents($stream, 4)) {
'ÈŒb­' => self::deserialize_messageExtendedMediaPreview($stream),
'dœGî' => [
'_' => 'messageExtendedMedia',
'media' => match (stream_get_contents($stream, 4)) {
' cí=' => [
'_' => 'messageMediaEmpty',
],
'×PQi' => self::deserialize_messageMediaPhoto($stream),
'tÔàV' => [
'_' => 'messageMediaGeo',
'geo' => match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => self::deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'I)2p' => [
'_' => 'messageMediaContact',
'phone_number' => self::deserialize_string($stream),
'first_name' => self::deserialize_string($stream),
'last_name' => self::deserialize_string($stream),
'vcard' => self::deserialize_string($stream),
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'žô„Ÿ' => [
'_' => 'messageMediaUnsupported',
],
'-×ôL' => self::deserialize_messageMediaDocument($stream),
';ñÝ' => self::deserialize_messageMediaWebPage($stream),
'?SÀ.' => [
'_' => 'messageMediaVenue',
'geo' => match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => self::deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
,
'title' => self::deserialize_string($stream),
'address' => self::deserialize_string($stream),
'provider' => self::deserialize_string($stream),
'venue_id' => self::deserialize_string($stream),
'venue_type' => self::deserialize_string($stream),
],
'�±ý' => [
'_' => 'messageMediaGame',
'game' => match (stream_get_contents($stream, 4)) {
';eù½' => self::deserialize_game($stream),
'¡Ïr0' => self::deserialize_type_Game(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ÓH¥ö' => self::deserialize_messageMediaInvoice($stream),
'fÆ@¹' => self::deserialize_messageMediaGeoLive($stream),
'˜çÖK' => [
'_' => 'messageMediaPoll',
'poll' => match (stream_get_contents($stream, 4)) {
'a�á†' => self::deserialize_poll($stream),
'¡Ïr0' => self::deserialize_type_Poll(self::gzdecode($stream)),
default => self::err($stream)
}
,
'results' => match (stream_get_contents($stream, 4)) {
' $ßz' => self::deserialize_pollResults($stream),
'¡Ïr0' => self::deserialize_type_PollResults(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'‹å~?' => [
'_' => 'messageMediaDice',
'value' => unpack('l', stream_get_contents($stream, 4))[1],
'emoticon' => self::deserialize_string($stream),
],
'ƒbËh' => self::deserialize_messageMediaStory($stream),
'°…­Ú' => self::deserialize_messageMediaGiveaway($stream),
'h™Æ' => self::deserialize_messageMediaGiveawayResults($stream),
'¡Ïr0' => self::deserialize_type_MessageMedia(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_MessageExtendedMedia(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ãû.' => self::deserialize_updateChannelPinnedTopic($stream),
'†þ' => self::deserialize_updateChannelPinnedTopics($stream),
'8”R ' => [
'_' => 'updateUser',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'—°ì' => [
'_' => 'updateAutoSaveSettings',
],
'ÖŠðÌ' => [
'_' => 'updateGroupInvitePrivacyForbidden',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'˜·³u' => [
'_' => 'updateStory',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'story' => match (stream_get_contents($stream, 4)) {
'OîæQ' => [
'_' => 'storyItemDeleted',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'É­ÿ' => self::deserialize_storyItemSkipped($stream),
'$j²y' => self::deserialize_storyItem($stream),
'¡Ïr0' => self::deserialize_type_StoryItem(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'+“N÷' => [
'_' => 'updateReadStories',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'max_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¹5ó' => [
'_' => 'updateStoryID',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'random_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÁM,' => [
'_' => 'updateStoriesStealthMode',
'stealth_mode' => match (stream_get_contents($stream, 4)) {
'ý\'.q' => self::deserialize_storiesStealthMode($stream),
'¡Ïr0' => self::deserialize_type_StoriesStealthMode(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ƒvb}' => [
'_' => 'updateSentStoryReaction',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'story_id' => unpack('l', stream_get_contents($stream, 4))[1],
'reaction' => match (stream_get_contents($stream, 4)) {
'Ôõy' => [
'_' => 'reactionEmpty',
],
'¸†"' => [
'_' => 'reactionEmoji',
'emoticon' => self::deserialize_string($stream),
],
'sü5‰' => [
'_' => 'reactionCustomEmoji',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_Reaction(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'œÔM�' => [
'_' => 'updateBotChatBoost',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'boost' => match (stream_get_contents($stream, 4)) {
'qŒ*' => self::deserialize_boost($stream),
'¡Ïr0' => self::deserialize_type_Boost(self::gzdecode($stream)),
default => self::err($stream)
}
,
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
' ‰¶' => [
'_' => 'updateChannelViewForumAsMessages',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'enabled' => match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) },
],
'?®' => self::deserialize_updatePeerWallpaper($stream),
'ÎÓ!¬' => [
'_' => 'updateBotMessageReaction',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'actor' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'old_reactions' => self::deserialize_type_array_of_Reaction(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'new_reactions' => self::deserialize_type_array_of_Reaction(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'YwË	' => [
'_' => 'updateBotMessageReactions',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'reactions' => self::deserialize_type_array_of_ReactionCount(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'tž¯®' => self::deserialize_updateSavedDialogPinned($stream),
'¦…lh' => self::deserialize_updatePinnedSavedDialogs($stream),
'2tÆ9' => [
'_' => 'updateSavedReactionTags',
],
'Ôibñ' => [
'_' => 'updateSmsJob',
'job_id' => self::deserialize_string($stream),
],
'²
Gù' => [
'_' => 'updateQuickReplies',
'quick_replies' => self::deserialize_type_array_of_QuickReply(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'§=õ' => [
'_' => 'updateNewQuickReply',
'quick_reply' => match (stream_get_contents($stream, 4)) {
'+—' => [
'_' => 'quickReply',
'shortcut_id' => unpack('l', stream_get_contents($stream, 4))[1],
'shortcut' => self::deserialize_string($stream),
'top_message' => unpack('l', stream_get_contents($stream, 4))[1],
'count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_QuickReply(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ìñæS' => [
'_' => 'updateDeleteQuickReply',
'shortcut_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'>' => [
'_' => 'updateQuickReplyMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ÍçoV' => [
'_' => 'updateDeleteQuickReplyMessages',
'shortcut_id' => unpack('l', stream_get_contents($stream, 4))[1],
'messages' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => self::deserialize_type_Update(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_updateShortSentMessage(mixed $stream): mixed {
$tmp = ['_' => 'updateShortSentMessage'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['out'] = ($flags & 2) !== 0;
$tmp['id'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['pts'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['pts_count'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['date'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 512) !== 0) $tmp['media'] = match (stream_get_contents($stream, 4)) {
' cí=' => [
'_' => 'messageMediaEmpty',
],
'×PQi' => self::deserialize_messageMediaPhoto($stream),
'tÔàV' => [
'_' => 'messageMediaGeo',
'geo' => match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => self::deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'I)2p' => [
'_' => 'messageMediaContact',
'phone_number' => self::deserialize_string($stream),
'first_name' => self::deserialize_string($stream),
'last_name' => self::deserialize_string($stream),
'vcard' => self::deserialize_string($stream),
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'žô„Ÿ' => [
'_' => 'messageMediaUnsupported',
],
'-×ôL' => self::deserialize_messageMediaDocument($stream),
';ñÝ' => self::deserialize_messageMediaWebPage($stream),
'?SÀ.' => [
'_' => 'messageMediaVenue',
'geo' => match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => self::deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
,
'title' => self::deserialize_string($stream),
'address' => self::deserialize_string($stream),
'provider' => self::deserialize_string($stream),
'venue_id' => self::deserialize_string($stream),
'venue_type' => self::deserialize_string($stream),
],
'�±ý' => [
'_' => 'messageMediaGame',
'game' => match (stream_get_contents($stream, 4)) {
';eù½' => self::deserialize_game($stream),
'¡Ïr0' => self::deserialize_type_Game(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ÓH¥ö' => self::deserialize_messageMediaInvoice($stream),
'fÆ@¹' => self::deserialize_messageMediaGeoLive($stream),
'˜çÖK' => [
'_' => 'messageMediaPoll',
'poll' => match (stream_get_contents($stream, 4)) {
'a�á†' => self::deserialize_poll($stream),
'¡Ïr0' => self::deserialize_type_Poll(self::gzdecode($stream)),
default => self::err($stream)
}
,
'results' => match (stream_get_contents($stream, 4)) {
' $ßz' => self::deserialize_pollResults($stream),
'¡Ïr0' => self::deserialize_type_PollResults(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'‹å~?' => [
'_' => 'messageMediaDice',
'value' => unpack('l', stream_get_contents($stream, 4))[1],
'emoticon' => self::deserialize_string($stream),
],
'ƒbËh' => self::deserialize_messageMediaStory($stream),
'°…­Ú' => self::deserialize_messageMediaGiveaway($stream),
'h™Æ' => self::deserialize_messageMediaGiveawayResults($stream),
'¡Ïr0' => self::deserialize_type_MessageMedia(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 128) !== 0) $tmp['entities'] = self::deserialize_type_array_of_MessageEntity(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 33554432) !== 0) $tmp['ttl_period'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_autoDownloadSettings(mixed $stream): mixed {
$tmp = ['_' => 'autoDownloadSettings'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['disabled'] = ($flags & 1) !== 0;
$tmp['video_preload_large'] = ($flags & 2) !== 0;
$tmp['audio_preload_next'] = ($flags & 4) !== 0;
$tmp['phonecalls_less_data'] = ($flags & 8) !== 0;
$tmp['stories_preload'] = ($flags & 16) !== 0;
$tmp['photo_size_max'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['video_size_max'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['file_size_max'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['video_upload_maxbitrate'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['small_queue_active_operations_max'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['large_queue_active_operations_max'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_AutoDownloadSettings(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'(v¥º' => self::deserialize_autoDownloadSettings($stream),
'¡Ïr0' => $this->deserialize_type_AutoDownloadSettings(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_array_of_Theme(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'Ög ' => self::deserialize_theme($stream),
'¡Ïr0' => self::deserialize_type_Theme(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_account___contentSettings(mixed $stream): mixed {
$tmp = ['_' => 'account.contentSettings'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['sensitive_enabled'] = ($flags & 1) !== 0;
$tmp['sensitive_can_change'] = ($flags & 2) !== 0;
return $tmp;

    }
    private  function deserialize_globalPrivacySettings(mixed $stream): mixed {
$tmp = ['_' => 'globalPrivacySettings'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['archive_and_mute_new_noncontact_peers'] = ($flags & 1) !== 0;
$tmp['keep_archived_unmuted'] = ($flags & 2) !== 0;
$tmp['keep_archived_folders'] = ($flags & 4) !== 0;
$tmp['hide_read_marks'] = ($flags & 8) !== 0;
$tmp['new_noncontact_peers_require_premium'] = ($flags & 16) !== 0;
return $tmp;

    }
    private  function deserialize_type_array_of_EmojiStatus(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'®á-' => [
'_' => 'emojiStatusEmpty',
],
'�a›’' => [
'_' => 'emojiStatus',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'Ç¨0ú' => [
'_' => 'emojiStatusUntil',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'until' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_EmojiStatus(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_autoSaveSettings(mixed $stream): mixed {
$tmp = ['_' => 'autoSaveSettings'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['photos'] = ($flags & 1) !== 0;
$tmp['videos'] = ($flags & 2) !== 0;
if (($flags & 4) !== 0) $tmp['video_max_size'] = unpack('q', stream_get_contents($stream, 8))[1];
return $tmp;

    }
    private  function deserialize_type_AutoSaveSettings(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'Î4HÈ' => self::deserialize_autoSaveSettings($stream),
'¡Ïr0' => $this->deserialize_type_AutoSaveSettings(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_array_of_AutoSaveException(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'G-`�' => [
'_' => 'autoSaveException',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'settings' => match (stream_get_contents($stream, 4)) {
'Î4HÈ' => self::deserialize_autoSaveSettings($stream),
'¡Ïr0' => self::deserialize_type_AutoSaveSettings(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => $this->deserialize_type_AutoSaveException(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_businessRecipients(mixed $stream): mixed {
$tmp = ['_' => 'businessRecipients'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['existing_chats'] = ($flags & 1) !== 0;
$tmp['new_chats'] = ($flags & 2) !== 0;
$tmp['contacts'] = ($flags & 4) !== 0;
$tmp['non_contacts'] = ($flags & 8) !== 0;
$tmp['exclude_selected'] = ($flags & 32) !== 0;
if (($flags & 16) !== 0) $tmp['users'] = self::deserialize_type_array_of_long(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
return $tmp;

    }
    private  function deserialize_type_BusinessRecipients(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'÷�!' => self::deserialize_businessRecipients($stream),
'¡Ïr0' => $this->deserialize_type_BusinessRecipients(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_connectedBot(mixed $stream): mixed {
$tmp = ['_' => 'connectedBot'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['can_reply'] = ($flags & 1) !== 0;
$tmp['bot_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['recipients'] = match (stream_get_contents($stream, 4)) {
'÷�!' => self::deserialize_businessRecipients($stream),
'¡Ïr0' => self::deserialize_type_BusinessRecipients(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_type_array_of_ConnectedBot(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'ç™éç' => self::deserialize_connectedBot($stream),
'¡Ïr0' => $this->deserialize_type_ConnectedBot(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_botInfo(mixed $stream): mixed {
$tmp = ['_' => 'botInfo'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['user_id'] = unpack('q', stream_get_contents($stream, 8))[1];
if (($flags & 2) !== 0) $tmp['description'] = self::deserialize_string($stream);
if (($flags & 16) !== 0) $tmp['description_photo'] = match (stream_get_contents($stream, 4)) {
'-²1#' => [
'_' => 'photoEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ezû' => self::deserialize_photo($stream),
'¡Ïr0' => self::deserialize_type_Photo(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 32) !== 0) $tmp['description_document'] = match (stream_get_contents($stream, 4)) {
'qÈø6' => [
'_' => 'documentEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ØÄÔ�' => self::deserialize_document($stream),
'¡Ïr0' => self::deserialize_type_Document(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 4) !== 0) $tmp['commands'] = self::deserialize_type_array_of_BotCommand(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 8) !== 0) $tmp['menu_button'] = match (stream_get_contents($stream, 4)) {
'ˆ¥3u' => [
'_' => 'botMenuButtonDefault',
],
'ÂXB' => [
'_' => 'botMenuButtonCommands',
],
'æ|µÇ' => [
'_' => 'botMenuButton',
'text' => self::deserialize_string($stream),
'url' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_BotMenuButton(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_type_BotInfo(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'W0�' => self::deserialize_botInfo($stream),
'¡Ïr0' => $this->deserialize_type_BotInfo(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_premiumGiftOption(mixed $stream): mixed {
$tmp = ['_' => 'premiumGiftOption'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['months'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['currency'] = self::deserialize_string($stream);
$tmp['amount'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['bot_url'] = self::deserialize_string($stream);
if (($flags & 1) !== 0) $tmp['store_product'] = self::deserialize_string($stream);
return $tmp;

    }
    private  function deserialize_type_array_of_PremiumGiftOption(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'CÃt' => self::deserialize_premiumGiftOption($stream),
'¡Ïr0' => $this->deserialize_type_PremiumGiftOption(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_StoryItem(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'OîæQ' => [
'_' => 'storyItemDeleted',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'É­ÿ' => self::deserialize_storyItemSkipped($stream),
'$j²y' => self::deserialize_storyItem($stream),
'¡Ïr0' => self::deserialize_type_StoryItem(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_peerStories(mixed $stream): mixed {
$tmp = ['_' => 'peerStories'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['peer'] = match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 1) !== 0) $tmp['max_read_id'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['stories'] = self::deserialize_type_array_of_StoryItem(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
return $tmp;

    }
    private  function deserialize_type_PeerStories(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'™é5š' => self::deserialize_peerStories($stream),
'¡Ïr0' => $this->deserialize_type_PeerStories(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_array_of_BusinessWeeklyOpen(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'¹' => [
'_' => 'businessWeeklyOpen',
'start_minute' => unpack('l', stream_get_contents($stream, 4))[1],
'end_minute' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => $this->deserialize_type_BusinessWeeklyOpen(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_businessWorkHours(mixed $stream): mixed {
$tmp = ['_' => 'businessWorkHours'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['open_now'] = ($flags & 1) !== 0;
$tmp['timezone_id'] = self::deserialize_string($stream);
$tmp['weekly_open'] = self::deserialize_type_array_of_BusinessWeeklyOpen(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
return $tmp;

    }
    private  function deserialize_type_BusinessWorkHours(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'˜°’Œ' => self::deserialize_businessWorkHours($stream),
'¡Ïr0' => $this->deserialize_type_BusinessWorkHours(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_businessLocation(mixed $stream): mixed {
$tmp = ['_' => 'businessLocation'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['geo_point'] = match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => self::deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['address'] = self::deserialize_string($stream);
return $tmp;

    }
    private  function deserialize_type_BusinessLocation(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'÷\\¬' => self::deserialize_businessLocation($stream),
'¡Ïr0' => $this->deserialize_type_BusinessLocation(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_BusinessGreetingMessage(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'««å' => [
'_' => 'businessGreetingMessage',
'shortcut_id' => unpack('l', stream_get_contents($stream, 4))[1],
'recipients' => match (stream_get_contents($stream, 4)) {
'÷�!' => self::deserialize_businessRecipients($stream),
'¡Ïr0' => self::deserialize_type_BusinessRecipients(self::gzdecode($stream)),
default => self::err($stream)
}
,
'no_activity_days' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => $this->deserialize_type_BusinessGreetingMessage(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_BusinessAwayMessageSchedule(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'¹â¹É' => [
'_' => 'businessAwayMessageScheduleAlways',
],
'õòÃ' => [
'_' => 'businessAwayMessageScheduleOutsideWorkHours',
],
'ÌžMÌ' => [
'_' => 'businessAwayMessageScheduleCustom',
'start_date' => unpack('l', stream_get_contents($stream, 4))[1],
'end_date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => $this->deserialize_type_BusinessAwayMessageSchedule(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_businessAwayMessage(mixed $stream): mixed {
$tmp = ['_' => 'businessAwayMessage'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['offline_only'] = ($flags & 1) !== 0;
$tmp['shortcut_id'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['schedule'] = match (stream_get_contents($stream, 4)) {
'¹â¹É' => [
'_' => 'businessAwayMessageScheduleAlways',
],
'õòÃ' => [
'_' => 'businessAwayMessageScheduleOutsideWorkHours',
],
'ÌžMÌ' => [
'_' => 'businessAwayMessageScheduleCustom',
'start_date' => unpack('l', stream_get_contents($stream, 4))[1],
'end_date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_BusinessAwayMessageSchedule(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['recipients'] = match (stream_get_contents($stream, 4)) {
'÷�!' => self::deserialize_businessRecipients($stream),
'¡Ïr0' => self::deserialize_type_BusinessRecipients(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_type_BusinessAwayMessage(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'\\jï' => self::deserialize_businessAwayMessage($stream),
'¡Ïr0' => $this->deserialize_type_BusinessAwayMessage(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_userFull(mixed $stream): mixed {
$this->referenceDatabase?->addOriginContext('userFull');
$tmp = ['_' => 'userFull'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['blocked'] = ($flags & 1) !== 0;
$tmp['phone_calls_available'] = ($flags & 16) !== 0;
$tmp['phone_calls_private'] = ($flags & 32) !== 0;
$tmp['can_pin_message'] = ($flags & 128) !== 0;
$tmp['has_scheduled'] = ($flags & 4096) !== 0;
$tmp['video_calls_available'] = ($flags & 8192) !== 0;
$tmp['voice_messages_forbidden'] = ($flags & 1048576) !== 0;
$tmp['translations_disabled'] = ($flags & 8388608) !== 0;
$tmp['stories_pinned_available'] = ($flags & 67108864) !== 0;
$tmp['blocked_my_stories_from'] = ($flags & 134217728) !== 0;
$tmp['wallpaper_overridden'] = ($flags & 268435456) !== 0;
$tmp['contact_require_premium'] = ($flags & 536870912) !== 0;
$tmp['read_dates_private'] = ($flags & 1073741824) !== 0;
$flags2 = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['id'] = unpack('q', stream_get_contents($stream, 8))[1];
if (($flags & 2) !== 0) $tmp['about'] = self::deserialize_string($stream);
$tmp['settings'] = match (stream_get_contents($stream, 4)) {
'¥' => self::deserialize_peerSettings($stream),
'¡Ïr0' => self::deserialize_type_PeerSettings(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 2097152) !== 0) $tmp['personal_photo'] = match (stream_get_contents($stream, 4)) {
'-²1#' => [
'_' => 'photoEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ezû' => self::deserialize_photo($stream),
'¡Ïr0' => self::deserialize_type_Photo(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 4) !== 0) $tmp['profile_photo'] = match (stream_get_contents($stream, 4)) {
'-²1#' => [
'_' => 'photoEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ezû' => self::deserialize_photo($stream),
'¡Ïr0' => self::deserialize_type_Photo(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 4194304) !== 0) $tmp['fallback_photo'] = match (stream_get_contents($stream, 4)) {
'-²1#' => [
'_' => 'photoEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ezû' => self::deserialize_photo($stream),
'¡Ïr0' => self::deserialize_type_Photo(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['notify_settings'] = match (stream_get_contents($stream, 4)) {
',b™' => self::deserialize_peerNotifySettings($stream),
'¡Ïr0' => self::deserialize_type_PeerNotifySettings(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 8) !== 0) $tmp['bot_info'] = match (stream_get_contents($stream, 4)) {
'W0�' => self::deserialize_botInfo($stream),
'¡Ïr0' => self::deserialize_type_BotInfo(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 64) !== 0) $tmp['pinned_msg_id'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['common_chats_count'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 2048) !== 0) $tmp['folder_id'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 16384) !== 0) $tmp['ttl_period'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 32768) !== 0) $tmp['theme_emoticon'] = self::deserialize_string($stream);
if (($flags & 65536) !== 0) $tmp['private_forward_name'] = self::deserialize_string($stream);
if (($flags & 131072) !== 0) $tmp['bot_group_admin_rights'] = match (stream_get_contents($stream, 4)) {
'Õ$²_' => self::deserialize_chatAdminRights($stream),
'¡Ïr0' => self::deserialize_type_ChatAdminRights(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 262144) !== 0) $tmp['bot_broadcast_admin_rights'] = match (stream_get_contents($stream, 4)) {
'Õ$²_' => self::deserialize_chatAdminRights($stream),
'¡Ïr0' => self::deserialize_type_ChatAdminRights(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 524288) !== 0) $tmp['premium_gifts'] = self::deserialize_type_array_of_PremiumGiftOption(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 16777216) !== 0) $tmp['wallpaper'] = match (stream_get_contents($stream, 4)) {
'íÃ7¤' => self::deserialize_wallPaper($stream),
'A€à' => self::deserialize_wallPaperNoFile($stream),
'¡Ïr0' => self::deserialize_type_WallPaper(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 33554432) !== 0) $tmp['stories'] = match (stream_get_contents($stream, 4)) {
'™é5š' => self::deserialize_peerStories($stream),
'¡Ïr0' => self::deserialize_type_PeerStories(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags2 & 1) !== 0) $tmp['business_work_hours'] = match (stream_get_contents($stream, 4)) {
'˜°’Œ' => self::deserialize_businessWorkHours($stream),
'¡Ïr0' => self::deserialize_type_BusinessWorkHours(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags2 & 2) !== 0) $tmp['business_location'] = match (stream_get_contents($stream, 4)) {
'÷\\¬' => self::deserialize_businessLocation($stream),
'¡Ïr0' => self::deserialize_type_BusinessLocation(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags2 & 4) !== 0) $tmp['business_greeting_message'] = match (stream_get_contents($stream, 4)) {
'««å' => [
'_' => 'businessGreetingMessage',
'shortcut_id' => unpack('l', stream_get_contents($stream, 4))[1],
'recipients' => match (stream_get_contents($stream, 4)) {
'÷�!' => self::deserialize_businessRecipients($stream),
'¡Ïr0' => self::deserialize_type_BusinessRecipients(self::gzdecode($stream)),
default => self::err($stream)
}
,
'no_activity_days' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_BusinessGreetingMessage(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags2 & 8) !== 0) $tmp['business_away_message'] = match (stream_get_contents($stream, 4)) {
'\\jï' => self::deserialize_businessAwayMessage($stream),
'¡Ïr0' => self::deserialize_type_BusinessAwayMessage(self::gzdecode($stream)),
default => self::err($stream)
}
;
$this->referenceDatabase?->addOrigin($tmp);
$this->peerDatabase->addFullChat($tmp);
return $tmp;

    }
    private  function deserialize_type_UserFull(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'…>ÿ"' => self::deserialize_userFull($stream),
'¡Ïr0' => $this->deserialize_type_UserFull(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_array_of_Contact(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'ÞZ' => [
'_' => 'contact',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'mutual' => match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) },
],
'¡Ïr0' => $this->deserialize_type_Contact(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_ImportedContact(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'P<>Á' => [
'_' => 'importedContact',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'client_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => $this->deserialize_type_ImportedContact(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_PopularContact(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'uAá\\' => [
'_' => 'popularContact',
'client_id' => unpack('q', stream_get_contents($stream, 8))[1],
'importers' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => $this->deserialize_type_PopularContact(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_PeerBlocked(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'€ýè' => [
'_' => 'peerBlocked',
'peer_id' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => $this->deserialize_type_PeerBlocked(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_TopPeerCategory(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'[f«' => [
'_' => 'topPeerCategoryBotsPM',
],
'âw†' => [
'_' => 'topPeerCategoryBotsInline',
],
'í·7' => [
'_' => 'topPeerCategoryCorrespondents',
],
'J¡½' => [
'_' => 'topPeerCategoryGroups',
],
'(–' => [
'_' => 'topPeerCategoryChannels',
],
'Œ§v' => [
'_' => 'topPeerCategoryPhoneCalls',
],
'©l@¨' => [
'_' => 'topPeerCategoryForwardUsers',
],
'ðÀîû' => [
'_' => 'topPeerCategoryForwardChats',
],
'¡Ïr0' => $this->deserialize_type_TopPeerCategory(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_array_of_TopPeer(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'[ÀÍí' => [
'_' => 'topPeer',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'rating' => unpack('d', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => $this->deserialize_type_TopPeer(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_TopPeerCategoryPeers(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'‘Bƒû' => [
'_' => 'topPeerCategoryPeers',
'category' => match (stream_get_contents($stream, 4)) {
'[f«' => [
'_' => 'topPeerCategoryBotsPM',
],
'âw†' => [
'_' => 'topPeerCategoryBotsInline',
],
'í·7' => [
'_' => 'topPeerCategoryCorrespondents',
],
'J¡½' => [
'_' => 'topPeerCategoryGroups',
],
'(–' => [
'_' => 'topPeerCategoryChannels',
],
'Œ§v' => [
'_' => 'topPeerCategoryPhoneCalls',
],
'©l@¨' => [
'_' => 'topPeerCategoryForwardUsers',
],
'ðÀîû' => [
'_' => 'topPeerCategoryForwardChats',
],
'¡Ïr0' => self::deserialize_type_TopPeerCategory(self::gzdecode($stream)),
default => self::err($stream)
}
,
'count' => unpack('l', stream_get_contents($stream, 4))[1],
'peers' => self::deserialize_type_array_of_TopPeer(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => $this->deserialize_type_TopPeerCategoryPeers(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_Message(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_messages___messagesSlice(mixed $stream): mixed {
$tmp = ['_' => 'messages.messagesSlice'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['inexact'] = ($flags & 2) !== 0;
$tmp['count'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['next_rate'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 4) !== 0) $tmp['offset_id_offset'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['messages'] = self::deserialize_type_array_of_Message(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['chats'] = self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['users'] = self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
return $tmp;

    }
    private  function deserialize_forumTopic(mixed $stream): mixed {
$tmp = ['_' => 'forumTopic'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['my'] = ($flags & 2) !== 0;
$tmp['closed'] = ($flags & 4) !== 0;
$tmp['pinned'] = ($flags & 8) !== 0;
$tmp['short'] = ($flags & 32) !== 0;
$tmp['hidden'] = ($flags & 64) !== 0;
$tmp['id'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['date'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['title'] = self::deserialize_string($stream);
$tmp['icon_color'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['icon_emoji_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['top_message'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['read_inbox_max_id'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['read_outbox_max_id'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['unread_count'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['unread_mentions_count'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['unread_reactions_count'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['from_id'] = match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['notify_settings'] = match (stream_get_contents($stream, 4)) {
',b™' => self::deserialize_peerNotifySettings($stream),
'¡Ïr0' => self::deserialize_type_PeerNotifySettings(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 16) !== 0) $tmp['draft'] = match (stream_get_contents($stream, 4)) {
'„' => self::deserialize_draftMessageEmpty($stream),
'ï÷Ì?' => self::deserialize_draftMessage($stream),
'¡Ïr0' => self::deserialize_type_DraftMessage(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_type_array_of_ForumTopic(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'›?' => [
'_' => 'forumTopicDeleted',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'©pq' => self::deserialize_forumTopic($stream),
'¡Ïr0' => $this->deserialize_type_ForumTopic(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_messages___channelMessages(mixed $stream): mixed {
$tmp = ['_' => 'messages.channelMessages'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['inexact'] = ($flags & 2) !== 0;
$tmp['pts'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['count'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 4) !== 0) $tmp['offset_id_offset'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['messages'] = self::deserialize_type_array_of_Message(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['topics'] = self::deserialize_type_array_of_ForumTopic(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['chats'] = self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['users'] = self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
return $tmp;

    }
    private  function deserialize_dialog(mixed $stream): mixed {
$tmp = ['_' => 'dialog'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['pinned'] = ($flags & 4) !== 0;
$tmp['unread_mark'] = ($flags & 8) !== 0;
$tmp['view_forum_as_messages'] = ($flags & 64) !== 0;
$tmp['peer'] = match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['top_message'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['read_inbox_max_id'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['read_outbox_max_id'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['unread_count'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['unread_mentions_count'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['unread_reactions_count'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['notify_settings'] = match (stream_get_contents($stream, 4)) {
',b™' => self::deserialize_peerNotifySettings($stream),
'¡Ïr0' => self::deserialize_type_PeerNotifySettings(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 1) !== 0) $tmp['pts'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 2) !== 0) $tmp['draft'] = match (stream_get_contents($stream, 4)) {
'„' => self::deserialize_draftMessageEmpty($stream),
'ï÷Ì?' => self::deserialize_draftMessage($stream),
'¡Ïr0' => self::deserialize_type_DraftMessage(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 16) !== 0) $tmp['folder_id'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 32) !== 0) $tmp['ttl_period'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_folder(mixed $stream): mixed {
$tmp = ['_' => 'folder'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['autofill_new_broadcasts'] = ($flags & 1) !== 0;
$tmp['autofill_public_groups'] = ($flags & 2) !== 0;
$tmp['autofill_new_correspondents'] = ($flags & 4) !== 0;
$tmp['id'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['title'] = self::deserialize_string($stream);
if (($flags & 8) !== 0) $tmp['photo'] = match (stream_get_contents($stream, 4)) {
'Á7' => [
'_' => 'chatPhotoEmpty',
],
'n' => self::deserialize_chatPhoto($stream),
'¡Ïr0' => self::deserialize_type_ChatPhoto(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_type_Folder(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'eNTÿ' => self::deserialize_folder($stream),
'¡Ïr0' => $this->deserialize_type_Folder(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_dialogFolder(mixed $stream): mixed {
$tmp = ['_' => 'dialogFolder'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['pinned'] = ($flags & 4) !== 0;
$tmp['folder'] = match (stream_get_contents($stream, 4)) {
'eNTÿ' => self::deserialize_folder($stream),
'¡Ïr0' => self::deserialize_type_Folder(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['peer'] = match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['top_message'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['unread_muted_peers_count'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['unread_unmuted_peers_count'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['unread_muted_messages_count'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['unread_unmuted_messages_count'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_array_of_Dialog(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'ÆŠÕ' => self::deserialize_dialog($stream),
'L½q' => self::deserialize_dialogFolder($stream),
'¡Ïr0' => $this->deserialize_type_Dialog(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_BotInfo(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'W0�' => self::deserialize_botInfo($stream),
'¡Ïr0' => self::deserialize_type_BotInfo(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_chatReactionsAll(mixed $stream): mixed {
$tmp = ['_' => 'chatReactionsAll'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['allow_custom'] = ($flags & 1) !== 0;
return $tmp;

    }
    private  function deserialize_type_ChatReactions(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'¼2üê' => [
'_' => 'chatReactionsNone',
],
'Ê‹’R' => self::deserialize_chatReactionsAll($stream),
'7@f' => [
'_' => 'chatReactionsSome',
'reactions' => self::deserialize_type_array_of_Reaction(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => $this->deserialize_type_ChatReactions(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_chatFull(mixed $stream): mixed {
$this->referenceDatabase?->addOriginContext('chatFull');
$tmp = ['_' => 'chatFull'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['can_set_username'] = ($flags & 128) !== 0;
$tmp['has_scheduled'] = ($flags & 256) !== 0;
$tmp['translations_disabled'] = ($flags & 524288) !== 0;
$tmp['id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['about'] = self::deserialize_string($stream);
$tmp['participants'] = match (stream_get_contents($stream, 4)) {
'áÓc‡' => self::deserialize_chatParticipantsForbidden($stream),
'ø“¼<' => [
'_' => 'chatParticipants',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'participants' => self::deserialize_type_array_of_ChatParticipant(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_ChatParticipants(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 4) !== 0) $tmp['chat_photo'] = match (stream_get_contents($stream, 4)) {
'-²1#' => [
'_' => 'photoEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ezû' => self::deserialize_photo($stream),
'¡Ïr0' => self::deserialize_type_Photo(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['notify_settings'] = match (stream_get_contents($stream, 4)) {
',b™' => self::deserialize_peerNotifySettings($stream),
'¡Ïr0' => self::deserialize_type_PeerNotifySettings(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 8192) !== 0) $tmp['exported_invite'] = match (stream_get_contents($stream, 4)) {
'¨´
' => self::deserialize_chatInviteExported($stream),
'·zí' => [
'_' => 'chatInvitePublicJoinRequests',
],
'¡Ïr0' => self::deserialize_type_ExportedChatInvite(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 8) !== 0) $tmp['bot_info'] = self::deserialize_type_array_of_BotInfo(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 64) !== 0) $tmp['pinned_msg_id'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 2048) !== 0) $tmp['folder_id'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 4096) !== 0) $tmp['call'] = match (stream_get_contents($stream, 4)) {
'„ªØ' => [
'_' => 'inputGroupCall',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputGroupCall(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 16384) !== 0) $tmp['ttl_period'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 32768) !== 0) $tmp['groupcall_default_join_as'] = match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 65536) !== 0) $tmp['theme_emoticon'] = self::deserialize_string($stream);
if (($flags & 131072) !== 0) $tmp['requests_pending'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 131072) !== 0) $tmp['recent_requesters'] = self::deserialize_type_array_of_long(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 262144) !== 0) $tmp['available_reactions'] = match (stream_get_contents($stream, 4)) {
'¼2üê' => [
'_' => 'chatReactionsNone',
],
'Ê‹’R' => self::deserialize_chatReactionsAll($stream),
'7@f' => [
'_' => 'chatReactionsSome',
'reactions' => self::deserialize_type_array_of_Reaction(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => self::deserialize_type_ChatReactions(self::gzdecode($stream)),
default => self::err($stream)
}
;
$this->referenceDatabase?->addOrigin($tmp);
$this->peerDatabase->addFullChat($tmp);
return $tmp;

    }
    private  function deserialize_type_ChannelLocation(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'‹­µ¿' => [
'_' => 'channelLocationEmpty',
],
'Û‚› ' => [
'_' => 'channelLocation',
'geo_point' => match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => self::deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
,
'address' => self::deserialize_string($stream),
],
'¡Ïr0' => $this->deserialize_type_ChannelLocation(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_channelFull(mixed $stream): mixed {
$this->referenceDatabase?->addOriginContext('channelFull');
$tmp = ['_' => 'channelFull'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['can_view_participants'] = ($flags & 8) !== 0;
$tmp['can_set_username'] = ($flags & 64) !== 0;
$tmp['can_set_stickers'] = ($flags & 128) !== 0;
$tmp['hidden_prehistory'] = ($flags & 1024) !== 0;
$tmp['can_set_location'] = ($flags & 65536) !== 0;
$tmp['has_scheduled'] = ($flags & 524288) !== 0;
$tmp['can_view_stats'] = ($flags & 1048576) !== 0;
$tmp['blocked'] = ($flags & 4194304) !== 0;
$flags2 = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['can_delete_channel'] = ($flags2 & 1) !== 0;
$tmp['antispam'] = ($flags2 & 2) !== 0;
$tmp['participants_hidden'] = ($flags2 & 4) !== 0;
$tmp['translations_disabled'] = ($flags2 & 8) !== 0;
$tmp['stories_pinned_available'] = ($flags2 & 32) !== 0;
$tmp['view_forum_as_messages'] = ($flags2 & 64) !== 0;
$tmp['id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['about'] = self::deserialize_string($stream);
if (($flags & 1) !== 0) $tmp['participants_count'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 2) !== 0) $tmp['admins_count'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 4) !== 0) $tmp['kicked_count'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 4) !== 0) $tmp['banned_count'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 8192) !== 0) $tmp['online_count'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['read_inbox_max_id'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['read_outbox_max_id'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['unread_count'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['chat_photo'] = match (stream_get_contents($stream, 4)) {
'-²1#' => [
'_' => 'photoEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ezû' => self::deserialize_photo($stream),
'¡Ïr0' => self::deserialize_type_Photo(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['notify_settings'] = match (stream_get_contents($stream, 4)) {
',b™' => self::deserialize_peerNotifySettings($stream),
'¡Ïr0' => self::deserialize_type_PeerNotifySettings(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 8388608) !== 0) $tmp['exported_invite'] = match (stream_get_contents($stream, 4)) {
'¨´
' => self::deserialize_chatInviteExported($stream),
'·zí' => [
'_' => 'chatInvitePublicJoinRequests',
],
'¡Ïr0' => self::deserialize_type_ExportedChatInvite(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['bot_info'] = self::deserialize_type_array_of_BotInfo(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 16) !== 0) $tmp['migrated_from_chat_id'] = unpack('q', stream_get_contents($stream, 8))[1];
if (($flags & 16) !== 0) $tmp['migrated_from_max_id'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 32) !== 0) $tmp['pinned_msg_id'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 256) !== 0) $tmp['stickerset'] = match (stream_get_contents($stream, 4)) {
'ÜNÑ-' => self::deserialize_stickerSet($stream),
'¡Ïr0' => self::deserialize_type_StickerSet(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 512) !== 0) $tmp['available_min_id'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 2048) !== 0) $tmp['folder_id'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 16384) !== 0) $tmp['linked_chat_id'] = unpack('q', stream_get_contents($stream, 8))[1];
if (($flags & 32768) !== 0) $tmp['location'] = match (stream_get_contents($stream, 4)) {
'‹­µ¿' => [
'_' => 'channelLocationEmpty',
],
'Û‚› ' => [
'_' => 'channelLocation',
'geo_point' => match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => self::deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
,
'address' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_ChannelLocation(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 131072) !== 0) $tmp['slowmode_seconds'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 262144) !== 0) $tmp['slowmode_next_send_date'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 4096) !== 0) $tmp['stats_dc'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['pts'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 2097152) !== 0) $tmp['call'] = match (stream_get_contents($stream, 4)) {
'„ªØ' => [
'_' => 'inputGroupCall',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputGroupCall(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 16777216) !== 0) $tmp['ttl_period'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 33554432) !== 0) $tmp['pending_suggestions'] = self::deserialize_type_array_of_string(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 67108864) !== 0) $tmp['groupcall_default_join_as'] = match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 134217728) !== 0) $tmp['theme_emoticon'] = self::deserialize_string($stream);
if (($flags & 268435456) !== 0) $tmp['requests_pending'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 268435456) !== 0) $tmp['recent_requesters'] = self::deserialize_type_array_of_long(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 536870912) !== 0) $tmp['default_send_as'] = match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 1073741824) !== 0) $tmp['available_reactions'] = match (stream_get_contents($stream, 4)) {
'¼2üê' => [
'_' => 'chatReactionsNone',
],
'Ê‹’R' => self::deserialize_chatReactionsAll($stream),
'7@f' => [
'_' => 'chatReactionsSome',
'reactions' => self::deserialize_type_array_of_Reaction(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => self::deserialize_type_ChatReactions(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags2 & 16) !== 0) $tmp['stories'] = match (stream_get_contents($stream, 4)) {
'™é5š' => self::deserialize_peerStories($stream),
'¡Ïr0' => self::deserialize_type_PeerStories(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags2 & 128) !== 0) $tmp['wallpaper'] = match (stream_get_contents($stream, 4)) {
'íÃ7¤' => self::deserialize_wallPaper($stream),
'A€à' => self::deserialize_wallPaperNoFile($stream),
'¡Ïr0' => self::deserialize_type_WallPaper(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags2 & 256) !== 0) $tmp['boosts_applied'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags2 & 512) !== 0) $tmp['boosts_unrestrict'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags2 & 1024) !== 0) $tmp['emojiset'] = match (stream_get_contents($stream, 4)) {
'ÜNÑ-' => self::deserialize_stickerSet($stream),
'¡Ïr0' => self::deserialize_type_StickerSet(self::gzdecode($stream)),
default => self::err($stream)
}
;
$this->referenceDatabase?->addOrigin($tmp);
$this->peerDatabase->addFullChat($tmp);
return $tmp;

    }
    private  function deserialize_type_ChatFull(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'8ÓÉ' => self::deserialize_chatFull($stream),
'§TÀD' => self::deserialize_channelFull($stream),
'¡Ïr0' => $this->deserialize_type_ChatFull(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_array_of_StickerSet(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'ÜNÑ-' => self::deserialize_stickerSet($stream),
'¡Ïr0' => self::deserialize_type_StickerSet(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_chatInvite(mixed $stream): mixed {
$tmp = ['_' => 'chatInvite'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['channel'] = ($flags & 1) !== 0;
$tmp['broadcast'] = ($flags & 2) !== 0;
$tmp['public'] = ($flags & 4) !== 0;
$tmp['megagroup'] = ($flags & 8) !== 0;
$tmp['request_needed'] = ($flags & 64) !== 0;
$tmp['verified'] = ($flags & 128) !== 0;
$tmp['scam'] = ($flags & 256) !== 0;
$tmp['fake'] = ($flags & 512) !== 0;
$tmp['title'] = self::deserialize_string($stream);
if (($flags & 32) !== 0) $tmp['about'] = self::deserialize_string($stream);
$tmp['photo'] = match (stream_get_contents($stream, 4)) {
'-²1#' => [
'_' => 'photoEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ezû' => self::deserialize_photo($stream),
'¡Ïr0' => self::deserialize_type_Photo(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['participants_count'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 16) !== 0) $tmp['participants'] = self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['color'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_array_of_StickerSetCovered(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'Ò¥d' => [
'_' => 'stickerSetCovered',
'set' => match (stream_get_contents($stream, 4)) {
'ÜNÑ-' => self::deserialize_stickerSet($stream),
'¡Ïr0' => self::deserialize_type_StickerSet(self::gzdecode($stream)),
default => self::err($stream)
}
,
'cover' => match (stream_get_contents($stream, 4)) {
'qÈø6' => [
'_' => 'documentEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ØÄÔ�' => self::deserialize_document($stream),
'¡Ïr0' => self::deserialize_type_Document(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'å4' => [
'_' => 'stickerSetMultiCovered',
'set' => match (stream_get_contents($stream, 4)) {
'ÜNÑ-' => self::deserialize_stickerSet($stream),
'¡Ïr0' => self::deserialize_type_StickerSet(self::gzdecode($stream)),
default => self::err($stream)
}
,
'covers' => self::deserialize_type_array_of_Document(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'<Ñ@' => [
'_' => 'stickerSetFullCovered',
'set' => match (stream_get_contents($stream, 4)) {
'ÜNÑ-' => self::deserialize_stickerSet($stream),
'¡Ïr0' => self::deserialize_type_StickerSet(self::gzdecode($stream)),
default => self::err($stream)
}
,
'packs' => self::deserialize_type_array_of_StickerPack(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'keywords' => self::deserialize_type_array_of_StickerKeyword(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'documents' => self::deserialize_type_array_of_Document(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
']±w' => [
'_' => 'stickerSetNoCovered',
'set' => match (stream_get_contents($stream, 4)) {
'ÜNÑ-' => self::deserialize_stickerSet($stream),
'¡Ïr0' => self::deserialize_type_StickerSet(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => $this->deserialize_type_StickerSetCovered(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_messageViews(mixed $stream): mixed {
$tmp = ['_' => 'messageViews'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['views'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 2) !== 0) $tmp['forwards'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 4) !== 0) $tmp['replies'] = match (stream_get_contents($stream, 4)) {
'ÂÖƒ' => self::deserialize_messageReplies($stream),
'¡Ïr0' => self::deserialize_type_MessageReplies(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_type_array_of_MessageViews(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'=…[E' => self::deserialize_messageViews($stream),
'¡Ïr0' => $this->deserialize_type_MessageViews(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_messages___savedGifs(mixed $stream): mixed {
$this->referenceDatabase?->addOriginContext('messages.savedGifs');
$tmp = [
'_' => 'messages.savedGifs',
'hash' => unpack('q', stream_get_contents($stream, 8))[1],
'gifs' => self::deserialize_type_array_of_Document(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
];
$this->referenceDatabase?->addOrigin($tmp);
return $tmp;

    }
    private  function deserialize_type_InlineBotSwitchPM(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'Ÿb <' => [
'_' => 'inlineBotSwitchPM',
'text' => self::deserialize_string($stream),
'start_param' => self::deserialize_string($stream),
],
'¡Ïr0' => $this->deserialize_type_InlineBotSwitchPM(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_InlineBotWebView(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'Õ•rµ' => [
'_' => 'inlineBotWebView',
'text' => self::deserialize_string($stream),
'url' => self::deserialize_string($stream),
],
'¡Ïr0' => $this->deserialize_type_InlineBotWebView(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_botInlineMessageMediaAuto(mixed $stream): mixed {
$tmp = ['_' => 'botInlineMessageMediaAuto'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['invert_media'] = ($flags & 8) !== 0;
$tmp['message'] = self::deserialize_string($stream);
if (($flags & 2) !== 0) $tmp['entities'] = self::deserialize_type_array_of_MessageEntity(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 4) !== 0) $tmp['reply_markup'] = match (stream_get_contents($stream, 4)) {
'…[> ' => self::deserialize_replyKeyboardHide($stream),
'´†' => self::deserialize_replyKeyboardForceReply($stream),
'Ñ™Ý…' => self::deserialize_replyKeyboardMarkup($stream),
'T£H' => [
'_' => 'replyInlineMarkup',
'rows' => self::deserialize_type_array_of_KeyboardButtonRow(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => self::deserialize_type_ReplyMarkup(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_botInlineMessageText(mixed $stream): mixed {
$tmp = ['_' => 'botInlineMessageText'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['no_webpage'] = ($flags & 1) !== 0;
$tmp['invert_media'] = ($flags & 8) !== 0;
$tmp['message'] = self::deserialize_string($stream);
if (($flags & 2) !== 0) $tmp['entities'] = self::deserialize_type_array_of_MessageEntity(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 4) !== 0) $tmp['reply_markup'] = match (stream_get_contents($stream, 4)) {
'…[> ' => self::deserialize_replyKeyboardHide($stream),
'´†' => self::deserialize_replyKeyboardForceReply($stream),
'Ñ™Ý…' => self::deserialize_replyKeyboardMarkup($stream),
'T£H' => [
'_' => 'replyInlineMarkup',
'rows' => self::deserialize_type_array_of_KeyboardButtonRow(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => self::deserialize_type_ReplyMarkup(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_botInlineMessageMediaGeo(mixed $stream): mixed {
$tmp = ['_' => 'botInlineMessageMediaGeo'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['geo'] = match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => self::deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 1) !== 0) $tmp['heading'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 2) !== 0) $tmp['period'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 8) !== 0) $tmp['proximity_notification_radius'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 4) !== 0) $tmp['reply_markup'] = match (stream_get_contents($stream, 4)) {
'…[> ' => self::deserialize_replyKeyboardHide($stream),
'´†' => self::deserialize_replyKeyboardForceReply($stream),
'Ñ™Ý…' => self::deserialize_replyKeyboardMarkup($stream),
'T£H' => [
'_' => 'replyInlineMarkup',
'rows' => self::deserialize_type_array_of_KeyboardButtonRow(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => self::deserialize_type_ReplyMarkup(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_botInlineMessageMediaVenue(mixed $stream): mixed {
$tmp = ['_' => 'botInlineMessageMediaVenue'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['geo'] = match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => self::deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['title'] = self::deserialize_string($stream);
$tmp['address'] = self::deserialize_string($stream);
$tmp['provider'] = self::deserialize_string($stream);
$tmp['venue_id'] = self::deserialize_string($stream);
$tmp['venue_type'] = self::deserialize_string($stream);
if (($flags & 4) !== 0) $tmp['reply_markup'] = match (stream_get_contents($stream, 4)) {
'…[> ' => self::deserialize_replyKeyboardHide($stream),
'´†' => self::deserialize_replyKeyboardForceReply($stream),
'Ñ™Ý…' => self::deserialize_replyKeyboardMarkup($stream),
'T£H' => [
'_' => 'replyInlineMarkup',
'rows' => self::deserialize_type_array_of_KeyboardButtonRow(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => self::deserialize_type_ReplyMarkup(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_botInlineMessageMediaContact(mixed $stream): mixed {
$tmp = ['_' => 'botInlineMessageMediaContact'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['phone_number'] = self::deserialize_string($stream);
$tmp['first_name'] = self::deserialize_string($stream);
$tmp['last_name'] = self::deserialize_string($stream);
$tmp['vcard'] = self::deserialize_string($stream);
if (($flags & 4) !== 0) $tmp['reply_markup'] = match (stream_get_contents($stream, 4)) {
'…[> ' => self::deserialize_replyKeyboardHide($stream),
'´†' => self::deserialize_replyKeyboardForceReply($stream),
'Ñ™Ý…' => self::deserialize_replyKeyboardMarkup($stream),
'T£H' => [
'_' => 'replyInlineMarkup',
'rows' => self::deserialize_type_array_of_KeyboardButtonRow(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => self::deserialize_type_ReplyMarkup(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_botInlineMessageMediaInvoice(mixed $stream): mixed {
$tmp = ['_' => 'botInlineMessageMediaInvoice'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['shipping_address_requested'] = ($flags & 2) !== 0;
$tmp['test'] = ($flags & 8) !== 0;
$tmp['title'] = self::deserialize_string($stream);
$tmp['description'] = self::deserialize_string($stream);
if (($flags & 1) !== 0) $tmp['photo'] = match (stream_get_contents($stream, 4)) {
'ÑW' => [
'_' => 'webDocument',
'url' => self::deserialize_string($stream),
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'attributes' => self::deserialize_type_array_of_DocumentAttribute(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'Æ¼Èù' => [
'_' => 'webDocumentNoProxy',
'url' => self::deserialize_string($stream),
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'attributes' => self::deserialize_type_array_of_DocumentAttribute(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => self::deserialize_type_WebDocument(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['currency'] = self::deserialize_string($stream);
$tmp['total_amount'] = unpack('q', stream_get_contents($stream, 8))[1];
if (($flags & 4) !== 0) $tmp['reply_markup'] = match (stream_get_contents($stream, 4)) {
'…[> ' => self::deserialize_replyKeyboardHide($stream),
'´†' => self::deserialize_replyKeyboardForceReply($stream),
'Ñ™Ý…' => self::deserialize_replyKeyboardMarkup($stream),
'T£H' => [
'_' => 'replyInlineMarkup',
'rows' => self::deserialize_type_array_of_KeyboardButtonRow(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => self::deserialize_type_ReplyMarkup(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_botInlineMessageMediaWebPage(mixed $stream): mixed {
$tmp = ['_' => 'botInlineMessageMediaWebPage'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['invert_media'] = ($flags & 8) !== 0;
$tmp['force_large_media'] = ($flags & 16) !== 0;
$tmp['force_small_media'] = ($flags & 32) !== 0;
$tmp['manual'] = ($flags & 128) !== 0;
$tmp['safe'] = ($flags & 256) !== 0;
$tmp['message'] = self::deserialize_string($stream);
if (($flags & 2) !== 0) $tmp['entities'] = self::deserialize_type_array_of_MessageEntity(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['url'] = self::deserialize_string($stream);
if (($flags & 4) !== 0) $tmp['reply_markup'] = match (stream_get_contents($stream, 4)) {
'…[> ' => self::deserialize_replyKeyboardHide($stream),
'´†' => self::deserialize_replyKeyboardForceReply($stream),
'Ñ™Ý…' => self::deserialize_replyKeyboardMarkup($stream),
'T£H' => [
'_' => 'replyInlineMarkup',
'rows' => self::deserialize_type_array_of_KeyboardButtonRow(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => self::deserialize_type_ReplyMarkup(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_type_BotInlineMessage(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'øLv' => self::deserialize_botInlineMessageMediaAuto($stream),
'âeŒ' => self::deserialize_botInlineMessageText($stream),
'ýF' => self::deserialize_botInlineMessageMediaGeo($stream),
'œe†Š' => self::deserialize_botInlineMessageMediaVenue($stream),
'ÂÍÑ' => self::deserialize_botInlineMessageMediaContact($stream),
'	›J5' => self::deserialize_botInlineMessageMediaInvoice($stream),
'¦Ùš€' => self::deserialize_botInlineMessageMediaWebPage($stream),
'¡Ïr0' => $this->deserialize_type_BotInlineMessage(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_botInlineResult(mixed $stream): mixed {
$tmp = ['_' => 'botInlineResult'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['id'] = self::deserialize_string($stream);
$tmp['type'] = self::deserialize_string($stream);
if (($flags & 2) !== 0) $tmp['title'] = self::deserialize_string($stream);
if (($flags & 4) !== 0) $tmp['description'] = self::deserialize_string($stream);
if (($flags & 8) !== 0) $tmp['url'] = self::deserialize_string($stream);
if (($flags & 16) !== 0) $tmp['thumb'] = match (stream_get_contents($stream, 4)) {
'ÑW' => [
'_' => 'webDocument',
'url' => self::deserialize_string($stream),
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'attributes' => self::deserialize_type_array_of_DocumentAttribute(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'Æ¼Èù' => [
'_' => 'webDocumentNoProxy',
'url' => self::deserialize_string($stream),
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'attributes' => self::deserialize_type_array_of_DocumentAttribute(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => self::deserialize_type_WebDocument(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 32) !== 0) $tmp['content'] = match (stream_get_contents($stream, 4)) {
'ÑW' => [
'_' => 'webDocument',
'url' => self::deserialize_string($stream),
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'attributes' => self::deserialize_type_array_of_DocumentAttribute(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'Æ¼Èù' => [
'_' => 'webDocumentNoProxy',
'url' => self::deserialize_string($stream),
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'attributes' => self::deserialize_type_array_of_DocumentAttribute(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => self::deserialize_type_WebDocument(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['send_message'] = match (stream_get_contents($stream, 4)) {
'øLv' => self::deserialize_botInlineMessageMediaAuto($stream),
'âeŒ' => self::deserialize_botInlineMessageText($stream),
'ýF' => self::deserialize_botInlineMessageMediaGeo($stream),
'œe†Š' => self::deserialize_botInlineMessageMediaVenue($stream),
'ÂÍÑ' => self::deserialize_botInlineMessageMediaContact($stream),
'	›J5' => self::deserialize_botInlineMessageMediaInvoice($stream),
'¦Ùš€' => self::deserialize_botInlineMessageMediaWebPage($stream),
'¡Ïr0' => self::deserialize_type_BotInlineMessage(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_botInlineMediaResult(mixed $stream): mixed {
$tmp = ['_' => 'botInlineMediaResult'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['id'] = self::deserialize_string($stream);
$tmp['type'] = self::deserialize_string($stream);
if (($flags & 1) !== 0) $tmp['photo'] = match (stream_get_contents($stream, 4)) {
'-²1#' => [
'_' => 'photoEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ezû' => self::deserialize_photo($stream),
'¡Ïr0' => self::deserialize_type_Photo(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 2) !== 0) $tmp['document'] = match (stream_get_contents($stream, 4)) {
'qÈø6' => [
'_' => 'documentEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ØÄÔ�' => self::deserialize_document($stream),
'¡Ïr0' => self::deserialize_type_Document(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 4) !== 0) $tmp['title'] = self::deserialize_string($stream);
if (($flags & 8) !== 0) $tmp['description'] = self::deserialize_string($stream);
$tmp['send_message'] = match (stream_get_contents($stream, 4)) {
'øLv' => self::deserialize_botInlineMessageMediaAuto($stream),
'âeŒ' => self::deserialize_botInlineMessageText($stream),
'ýF' => self::deserialize_botInlineMessageMediaGeo($stream),
'œe†Š' => self::deserialize_botInlineMessageMediaVenue($stream),
'ÂÍÑ' => self::deserialize_botInlineMessageMediaContact($stream),
'	›J5' => self::deserialize_botInlineMessageMediaInvoice($stream),
'¦Ùš€' => self::deserialize_botInlineMessageMediaWebPage($stream),
'¡Ïr0' => self::deserialize_type_BotInlineMessage(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_type_array_of_BotInlineResult(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
':_–' => self::deserialize_botInlineResult($stream),
'”Û' => self::deserialize_botInlineMediaResult($stream),
'¡Ïr0' => $this->deserialize_type_BotInlineResult(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_messages___botResults(mixed $stream): mixed {
$tmp = ['_' => 'messages.botResults'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['gallery'] = ($flags & 1) !== 0;
$tmp['query_id'] = unpack('q', stream_get_contents($stream, 8))[1];
if (($flags & 2) !== 0) $tmp['next_offset'] = self::deserialize_string($stream);
if (($flags & 4) !== 0) $tmp['switch_pm'] = match (stream_get_contents($stream, 4)) {
'Ÿb <' => [
'_' => 'inlineBotSwitchPM',
'text' => self::deserialize_string($stream),
'start_param' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_InlineBotSwitchPM(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 8) !== 0) $tmp['switch_webview'] = match (stream_get_contents($stream, 4)) {
'Õ•rµ' => [
'_' => 'inlineBotWebView',
'text' => self::deserialize_string($stream),
'url' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_InlineBotWebView(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['results'] = self::deserialize_type_array_of_BotInlineResult(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['cache_time'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['users'] = self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
return $tmp;

    }
    private  function deserialize_messages___messageEditData(mixed $stream): mixed {
$tmp = ['_' => 'messages.messageEditData'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['caption'] = ($flags & 1) !== 0;
return $tmp;

    }
    private  function deserialize_messages___botCallbackAnswer(mixed $stream): mixed {
$tmp = ['_' => 'messages.botCallbackAnswer'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['alert'] = ($flags & 2) !== 0;
$tmp['has_url'] = ($flags & 8) !== 0;
$tmp['native_ui'] = ($flags & 16) !== 0;
if (($flags & 1) !== 0) $tmp['message'] = self::deserialize_string($stream);
if (($flags & 4) !== 0) $tmp['url'] = self::deserialize_string($stream);
$tmp['cache_time'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_updates___State(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'>*l¥' => [
'_' => 'updates.state',
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'seq' => unpack('l', stream_get_contents($stream, 4))[1],
'unread_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => $this->deserialize_type_updates___State(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_messages___featuredStickers(mixed $stream): mixed {
$tmp = ['_' => 'messages.featuredStickers'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['premium'] = ($flags & 1) !== 0;
$tmp['hash'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['count'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['sets'] = self::deserialize_type_array_of_StickerSetCovered(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['unread'] = self::deserialize_type_array_of_long(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
return $tmp;

    }
    private  function deserialize_messages___recentStickers(mixed $stream): mixed {
$this->referenceDatabase?->addOriginContext('messages.recentStickers');
$tmp = [
'_' => 'messages.recentStickers',
'hash' => unpack('q', stream_get_contents($stream, 8))[1],
'packs' => self::deserialize_type_array_of_StickerPack(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'stickers' => self::deserialize_type_array_of_Document(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'dates' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
];
$this->referenceDatabase?->addOrigin($tmp);
return $tmp;

    }
    private  function deserialize_type_array_of_HighScore(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'ëy£s' => [
'_' => 'highScore',
'pos' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'score' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => $this->deserialize_type_HighScore(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_messages___favedStickers(mixed $stream): mixed {
$this->referenceDatabase?->addOriginContext('messages.favedStickers');
$tmp = [
'_' => 'messages.favedStickers',
'hash' => unpack('q', stream_get_contents($stream, 8))[1],
'packs' => self::deserialize_type_array_of_StickerPack(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'stickers' => self::deserialize_type_array_of_Document(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
];
$this->referenceDatabase?->addOrigin($tmp);
return $tmp;

    }
    private  function deserialize_type_array_of_EmojiKeyword(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'ù¹³Õ' => [
'_' => 'emojiKeyword',
'keyword' => self::deserialize_string($stream),
'emoticons' => self::deserialize_type_array_of_string(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'"öm#' => [
'_' => 'emojiKeywordDeleted',
'keyword' => self::deserialize_string($stream),
'emoticons' => self::deserialize_type_array_of_string(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => $this->deserialize_type_EmojiKeyword(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_urlAuthResultRequest(mixed $stream): mixed {
$tmp = ['_' => 'urlAuthResultRequest'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['request_write_access'] = ($flags & 1) !== 0;
$tmp['bot'] = match (stream_get_contents($stream, 4)) {
'zK¼Ó' => self::deserialize_userEmpty($stream),
'8D\\!' => self::deserialize_user($stream),
'¡Ïr0' => self::deserialize_type_User(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['domain'] = self::deserialize_string($stream);
return $tmp;

    }
    private  function deserialize_type_array_of_MessagePeerVote(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'\\-Ì¶' => [
'_' => 'messagePeerVote',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'option' => self::deserialize_bytes($stream),
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¥Ít' => [
'_' => 'messagePeerVoteInputOption',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'æö(F' => [
'_' => 'messagePeerVoteMultiple',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'options' => self::deserialize_type_array_of_bytes(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => $this->deserialize_type_MessagePeerVote(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_messages___votesList(mixed $stream): mixed {
$tmp = ['_' => 'messages.votesList'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['count'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['votes'] = self::deserialize_type_array_of_MessagePeerVote(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['chats'] = self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['users'] = self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 1) !== 0) $tmp['next_offset'] = self::deserialize_string($stream);
return $tmp;

    }
    private  function deserialize_type_array_of_DialogFilter(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
';Rµ_' => self::deserialize_dialogFilter($stream),
'®“26' => [
'_' => 'dialogFilterDefault',
],
'¤ŽâŸ' => self::deserialize_dialogFilterChatlist($stream),
'¡Ïr0' => self::deserialize_type_DialogFilter(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_messages___dialogFilters(mixed $stream): mixed {
$tmp = ['_' => 'messages.dialogFilters'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['tags_enabled'] = ($flags & 1) !== 0;
$tmp['filters'] = self::deserialize_type_array_of_DialogFilter(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
return $tmp;

    }
    private  function deserialize_messages___discussionMessage(mixed $stream): mixed {
$tmp = ['_' => 'messages.discussionMessage'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['messages'] = self::deserialize_type_array_of_Message(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 1) !== 0) $tmp['max_id'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 2) !== 0) $tmp['read_inbox_max_id'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 4) !== 0) $tmp['read_outbox_max_id'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['unread_count'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['chats'] = self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['users'] = self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
return $tmp;

    }
    private  function deserialize_messages___historyImportParsed(mixed $stream): mixed {
$tmp = ['_' => 'messages.historyImportParsed'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['pm'] = ($flags & 1) !== 0;
$tmp['group'] = ($flags & 2) !== 0;
if (($flags & 4) !== 0) $tmp['title'] = self::deserialize_string($stream);
return $tmp;

    }
    private  function deserialize_type_array_of_ExportedChatInvite(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'¨´
' => self::deserialize_chatInviteExported($stream),
'·zí' => [
'_' => 'chatInvitePublicJoinRequests',
],
'¡Ïr0' => self::deserialize_type_ExportedChatInvite(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_ChatAdminWithInvites(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'#ïìò' => [
'_' => 'chatAdminWithInvites',
'admin_id' => unpack('q', stream_get_contents($stream, 8))[1],
'invites_count' => unpack('l', stream_get_contents($stream, 4))[1],
'revoked_invites_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => $this->deserialize_type_ChatAdminWithInvites(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_chatInviteImporter(mixed $stream): mixed {
$tmp = ['_' => 'chatInviteImporter'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['requested'] = ($flags & 1) !== 0;
$tmp['via_chatlist'] = ($flags & 8) !== 0;
$tmp['user_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['date'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 4) !== 0) $tmp['about'] = self::deserialize_string($stream);
if (($flags & 2) !== 0) $tmp['approved_by'] = unpack('q', stream_get_contents($stream, 8))[1];
return $tmp;

    }
    private  function deserialize_type_array_of_ChatInviteImporter(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'ÙßZŒ' => self::deserialize_chatInviteImporter($stream),
'¡Ïr0' => $this->deserialize_type_ChatInviteImporter(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_SearchResultsCalendarPeriod(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'ŸS°É' => [
'_' => 'searchResultsCalendarPeriod',
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'min_msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'max_msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => $this->deserialize_type_SearchResultsCalendarPeriod(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_messages___searchResultsCalendar(mixed $stream): mixed {
$tmp = ['_' => 'messages.searchResultsCalendar'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['inexact'] = ($flags & 1) !== 0;
$tmp['count'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['min_date'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['min_msg_id'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 2) !== 0) $tmp['offset_id_offset'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['periods'] = self::deserialize_type_array_of_SearchResultsCalendarPeriod(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['messages'] = self::deserialize_type_array_of_Message(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['chats'] = self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['users'] = self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
return $tmp;

    }
    private  function deserialize_type_array_of_SearchResultsPosition(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'g‹d' => [
'_' => 'searchResultPosition',
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'offset' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => $this->deserialize_type_SearchResultsPosition(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_messages___messageReactionsList(mixed $stream): mixed {
$tmp = ['_' => 'messages.messageReactionsList'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['count'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['reactions'] = self::deserialize_type_array_of_MessagePeerReaction(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['chats'] = self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['users'] = self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 1) !== 0) $tmp['next_offset'] = self::deserialize_string($stream);
return $tmp;

    }
    private  function deserialize_availableReaction(mixed $stream): mixed {
$tmp = ['_' => 'availableReaction'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['inactive'] = ($flags & 1) !== 0;
$tmp['premium'] = ($flags & 4) !== 0;
$tmp['reaction'] = self::deserialize_string($stream);
$tmp['title'] = self::deserialize_string($stream);
$tmp['static_icon'] = match (stream_get_contents($stream, 4)) {
'qÈø6' => [
'_' => 'documentEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ØÄÔ�' => self::deserialize_document($stream),
'¡Ïr0' => self::deserialize_type_Document(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['appear_animation'] = match (stream_get_contents($stream, 4)) {
'qÈø6' => [
'_' => 'documentEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ØÄÔ�' => self::deserialize_document($stream),
'¡Ïr0' => self::deserialize_type_Document(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['select_animation'] = match (stream_get_contents($stream, 4)) {
'qÈø6' => [
'_' => 'documentEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ØÄÔ�' => self::deserialize_document($stream),
'¡Ïr0' => self::deserialize_type_Document(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['activate_animation'] = match (stream_get_contents($stream, 4)) {
'qÈø6' => [
'_' => 'documentEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ØÄÔ�' => self::deserialize_document($stream),
'¡Ïr0' => self::deserialize_type_Document(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['effect_animation'] = match (stream_get_contents($stream, 4)) {
'qÈø6' => [
'_' => 'documentEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ØÄÔ�' => self::deserialize_document($stream),
'¡Ïr0' => self::deserialize_type_Document(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 2) !== 0) $tmp['around_animation'] = match (stream_get_contents($stream, 4)) {
'qÈø6' => [
'_' => 'documentEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ØÄÔ�' => self::deserialize_document($stream),
'¡Ïr0' => self::deserialize_type_Document(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 2) !== 0) $tmp['center_icon'] = match (stream_get_contents($stream, 4)) {
'qÈø6' => [
'_' => 'documentEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ØÄÔ�' => self::deserialize_document($stream),
'¡Ïr0' => self::deserialize_type_Document(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_type_array_of_AvailableReaction(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'ìwÀ' => self::deserialize_availableReaction($stream),
'¡Ïr0' => $this->deserialize_type_AvailableReaction(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_TextWithEntities(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'F1u' => [
'_' => 'textWithEntities',
'text' => self::deserialize_string($stream),
'entities' => self::deserialize_type_array_of_MessageEntity(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => $this->deserialize_type_TextWithEntities(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_AttachMenuPeerType(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'ék}' => [
'_' => 'attachMenuPeerTypeSameBotPM',
],
'ú+Ã' => [
'_' => 'attachMenuPeerTypeBotPM',
],
'ÓFñ' => [
'_' => 'attachMenuPeerTypePM',
],
'?	' => [
'_' => 'attachMenuPeerTypeChat',
],
'üÞû{' => [
'_' => 'attachMenuPeerTypeBroadcast',
],
'¡Ïr0' => $this->deserialize_type_AttachMenuPeerType(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_AttachMenuBotIconColor(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'ðóvE' => [
'_' => 'attachMenuBotIconColor',
'name' => self::deserialize_string($stream),
'color' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => $this->deserialize_type_AttachMenuBotIconColor(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_attachMenuBotIcon(mixed $stream): mixed {
$tmp = ['_' => 'attachMenuBotIcon'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['name'] = self::deserialize_string($stream);
$tmp['icon'] = match (stream_get_contents($stream, 4)) {
'qÈø6' => [
'_' => 'documentEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ØÄÔ�' => self::deserialize_document($stream),
'¡Ïr0' => self::deserialize_type_Document(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 1) !== 0) $tmp['colors'] = self::deserialize_type_array_of_AttachMenuBotIconColor(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
return $tmp;

    }
    private  function deserialize_type_array_of_AttachMenuBotIcon(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'k8§²' => self::deserialize_attachMenuBotIcon($stream),
'¡Ïr0' => $this->deserialize_type_AttachMenuBotIcon(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_attachMenuBot(mixed $stream): mixed {
$tmp = ['_' => 'attachMenuBot'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['inactive'] = ($flags & 1) !== 0;
$tmp['has_settings'] = ($flags & 2) !== 0;
$tmp['request_write_access'] = ($flags & 4) !== 0;
$tmp['show_in_attach_menu'] = ($flags & 8) !== 0;
$tmp['show_in_side_menu'] = ($flags & 16) !== 0;
$tmp['side_menu_disclaimer_needed'] = ($flags & 32) !== 0;
$tmp['bot_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['short_name'] = self::deserialize_string($stream);
if (($flags & 8) !== 0) $tmp['peer_types'] = self::deserialize_type_array_of_AttachMenuPeerType(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['icons'] = self::deserialize_type_array_of_AttachMenuBotIcon(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
return $tmp;

    }
    private  function deserialize_type_array_of_AttachMenuBot(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'þ�Ù' => self::deserialize_attachMenuBot($stream),
'¡Ïr0' => $this->deserialize_type_AttachMenuBot(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_AttachMenuBot(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'þ�Ù' => self::deserialize_attachMenuBot($stream),
'¡Ïr0' => $this->deserialize_type_AttachMenuBot(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_webViewMessageSent(mixed $stream): mixed {
$tmp = ['_' => 'webViewMessageSent'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['msg_id'] = match (stream_get_contents($stream, 4)) {
'‰=‰' => [
'_' => 'inputBotInlineMessageID',
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'×Ù¶' => [
'_' => 'inputBotInlineMessageID64',
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'owner_id' => unpack('q', stream_get_contents($stream, 8))[1],
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputBotInlineMessageID(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_messages___transcribedAudio(mixed $stream): mixed {
$tmp = ['_' => 'messages.transcribedAudio'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['pending'] = ($flags & 1) !== 0;
$tmp['transcription_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['text'] = self::deserialize_string($stream);
if (($flags & 2) !== 0) $tmp['trial_remains_num'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 2) !== 0) $tmp['trial_remains_until_date'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_array_of_EmojiGroup(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'©½šz' => [
'_' => 'emojiGroup',
'title' => self::deserialize_string($stream),
'icon_emoji_id' => unpack('q', stream_get_contents($stream, 8))[1],
'emoticons' => self::deserialize_type_array_of_string(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => $this->deserialize_type_EmojiGroup(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_messages___botApp(mixed $stream): mixed {
$tmp = ['_' => 'messages.botApp'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['inactive'] = ($flags & 1) !== 0;
$tmp['request_write_access'] = ($flags & 2) !== 0;
$tmp['has_settings'] = ($flags & 4) !== 0;
$tmp['app'] = match (stream_get_contents($stream, 4)) {
'·t¦]' => [
'_' => 'botAppNotModified',
],
'ÖÑü•' => self::deserialize_botApp($stream),
'¡Ïr0' => self::deserialize_type_BotApp(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_savedDialog(mixed $stream): mixed {
$tmp = ['_' => 'savedDialog'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['pinned'] = ($flags & 4) !== 0;
$tmp['peer'] = match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['top_message'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_array_of_SavedDialog(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'lË‡½' => self::deserialize_savedDialog($stream),
'¡Ïr0' => $this->deserialize_type_SavedDialog(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_savedReactionTag(mixed $stream): mixed {
$tmp = ['_' => 'savedReactionTag'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['reaction'] = match (stream_get_contents($stream, 4)) {
'Ôõy' => [
'_' => 'reactionEmpty',
],
'¸†"' => [
'_' => 'reactionEmoji',
'emoticon' => self::deserialize_string($stream),
],
'sü5‰' => [
'_' => 'reactionCustomEmoji',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_Reaction(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 1) !== 0) $tmp['title'] = self::deserialize_string($stream);
$tmp['count'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_array_of_SavedReactionTag(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'(øoË' => self::deserialize_savedReactionTag($stream),
'¡Ïr0' => $this->deserialize_type_SavedReactionTag(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_EncryptedMessage(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'Áí' => [
'_' => 'encryptedMessage',
'random_id' => unpack('q', stream_get_contents($stream, 8))[1],
'chat_id' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'bytes' => self::deserialize_bytes($stream),
'file' => match (stream_get_contents($stream, 4)) {
'~IÂ' => [
'_' => 'encryptedFileEmpty',
],
'ØŒ' . "\0" . '¨' => [
'_' => 'encryptedFile',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'size' => unpack('q', stream_get_contents($stream, 8))[1],
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'key_fingerprint' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_EncryptedFile(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Ks#' => [
'_' => 'encryptedMessageService',
'random_id' => unpack('q', stream_get_contents($stream, 8))[1],
'chat_id' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'bytes' => self::deserialize_bytes($stream),
],
'¡Ïr0' => self::deserialize_type_EncryptedMessage(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_updates___channelDifferenceEmpty(mixed $stream): mixed {
$tmp = ['_' => 'updates.channelDifferenceEmpty'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['final'] = ($flags & 1) !== 0;
$tmp['pts'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 2) !== 0) $tmp['timeout'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_Dialog(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'ÆŠÕ' => self::deserialize_dialog($stream),
'L½q' => self::deserialize_dialogFolder($stream),
'¡Ïr0' => $this->deserialize_type_Dialog(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_updates___channelDifferenceTooLong(mixed $stream): mixed {
$tmp = ['_' => 'updates.channelDifferenceTooLong'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['final'] = ($flags & 1) !== 0;
if (($flags & 2) !== 0) $tmp['timeout'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['dialog'] = match (stream_get_contents($stream, 4)) {
'ÆŠÕ' => self::deserialize_dialog($stream),
'L½q' => self::deserialize_dialogFolder($stream),
'¡Ïr0' => self::deserialize_type_Dialog(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['messages'] = self::deserialize_type_array_of_Message(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['chats'] = self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['users'] = self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
return $tmp;

    }
    private  function deserialize_updates___channelDifference(mixed $stream): mixed {
$tmp = ['_' => 'updates.channelDifference'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['final'] = ($flags & 1) !== 0;
$tmp['pts'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 2) !== 0) $tmp['timeout'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['new_messages'] = self::deserialize_type_array_of_Message(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['other_updates'] = self::deserialize_type_array_of_Update(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['chats'] = self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['users'] = self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
return $tmp;

    }
    private  function deserialize_type_storage___FileType(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
';–ª' => [
'_' => 'storage.fileUnknown',
],
'Ro¼@' => [
'_' => 'storage.filePartial',
],
'þ~' . "\0" . '' => [
'_' => 'storage.fileJpeg',
],
'ßªáÊ' => [
'_' => 'storage.fileGif',
],
'ÀcO
' => [
'_' => 'storage.filePng',
],
'�P®' => [
'_' => 'storage.filePdf',
],
'wŠR' => [
'_' => 'storage.fileMp3',
],
'¼ë	K' => [
'_' => 'storage.fileMov',
],
'ä Î³' => [
'_' => 'storage.fileMp4',
],
'LF�' => [
'_' => 'storage.fileWebp',
],
'¡Ïr0' => $this->deserialize_type_storage___FileType(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_array_of_FileHash(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'\\›ó' => [
'_' => 'fileHash',
'offset' => unpack('q', stream_get_contents($stream, 8))[1],
'limit' => unpack('l', stream_get_contents($stream, 4))[1],
'hash' => self::deserialize_bytes($stream),
],
'¡Ïr0' => $this->deserialize_type_FileHash(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_config(mixed $stream): mixed {
$tmp = ['_' => 'config'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['default_p2p_contacts'] = ($flags & 8) !== 0;
$tmp['preload_featured_stickers'] = ($flags & 16) !== 0;
$tmp['revoke_pm_inbox'] = ($flags & 64) !== 0;
$tmp['blocked_mode'] = ($flags & 256) !== 0;
$tmp['force_try_ipv6'] = ($flags & 16384) !== 0;
$tmp['date'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['expires'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['test_mode'] = match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) };
$tmp['this_dc'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['dc_options'] = self::deserialize_type_array_of_DcOption(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['dc_txt_domain_name'] = self::deserialize_string($stream);
$tmp['chat_size_max'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['megagroup_size_max'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['forwarded_count_max'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['online_update_period_ms'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['offline_blur_timeout_ms'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['offline_idle_timeout_ms'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['online_cloud_timeout_ms'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['notify_cloud_delay_ms'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['notify_default_delay_ms'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['push_chat_period_ms'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['push_chat_limit'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['edit_time_limit'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['revoke_time_limit'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['revoke_pm_time_limit'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['rating_e_decay'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['stickers_recent_limit'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['channels_read_media_period'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['tmp_sessions'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['call_receive_timeout_ms'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['call_ring_timeout_ms'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['call_connect_timeout_ms'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['call_packet_timeout_ms'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['me_url_prefix'] = self::deserialize_string($stream);
if (($flags & 128) !== 0) $tmp['autoupdate_url_prefix'] = self::deserialize_string($stream);
if (($flags & 512) !== 0) $tmp['gif_search_username'] = self::deserialize_string($stream);
if (($flags & 1024) !== 0) $tmp['venue_search_username'] = self::deserialize_string($stream);
if (($flags & 2048) !== 0) $tmp['img_search_username'] = self::deserialize_string($stream);
if (($flags & 4096) !== 0) $tmp['static_maps_provider'] = self::deserialize_string($stream);
$tmp['caption_length_max'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['message_length_max'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['webfile_dc_id'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 4) !== 0) $tmp['suggested_lang_code'] = self::deserialize_string($stream);
if (($flags & 4) !== 0) $tmp['lang_pack_version'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 4) !== 0) $tmp['base_lang_pack_version'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 32768) !== 0) $tmp['reactions_default'] = match (stream_get_contents($stream, 4)) {
'Ôõy' => [
'_' => 'reactionEmpty',
],
'¸†"' => [
'_' => 'reactionEmoji',
'emoticon' => self::deserialize_string($stream),
],
'sü5‰' => [
'_' => 'reactionCustomEmoji',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_Reaction(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 65536) !== 0) $tmp['autologin_token'] = self::deserialize_string($stream);
$this->API->populateConfig($tmp);
return $tmp;

    }
    private  function deserialize_help___appUpdate(mixed $stream): mixed {
$tmp = ['_' => 'help.appUpdate'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['can_not_skip'] = ($flags & 1) !== 0;
$tmp['id'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['version'] = self::deserialize_string($stream);
$tmp['text'] = self::deserialize_string($stream);
$tmp['entities'] = self::deserialize_type_array_of_MessageEntity(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 2) !== 0) $tmp['document'] = match (stream_get_contents($stream, 4)) {
'qÈø6' => [
'_' => 'documentEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ØÄÔ�' => self::deserialize_document($stream),
'¡Ïr0' => self::deserialize_type_Document(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 4) !== 0) $tmp['url'] = self::deserialize_string($stream);
if (($flags & 8) !== 0) $tmp['sticker'] = match (stream_get_contents($stream, 4)) {
'qÈø6' => [
'_' => 'documentEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ØÄÔ�' => self::deserialize_document($stream),
'¡Ïr0' => self::deserialize_type_Document(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_help___support(mixed $stream): mixed {
$tmp = [
'_' => 'help.support',
'phone_number' => self::deserialize_string($stream),
'user' => match (stream_get_contents($stream, 4)) {
'zK¼Ó' => self::deserialize_userEmpty($stream),
'8D\\!' => self::deserialize_user($stream),
'¡Ïr0' => self::deserialize_type_User(self::gzdecode($stream)),
default => self::err($stream)
}
,
];
$this->API->populateSupportUser($tmp);
return $tmp;

    }
    private  function deserialize_type_array_of_CdnPublicKey(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'ºê‚É' => [
'_' => 'cdnPublicKey',
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'public_key' => self::deserialize_string($stream),
],
'¡Ïr0' => $this->deserialize_type_CdnPublicKey(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_ChatInvite(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'|mhZ' => [
'_' => 'chatInviteAlready',
'chat' => match (stream_get_contents($stream, 4)) {
'e(V)' => self::deserialize_chatEmpty($stream),
'VòËA' => self::deserialize_chat($stream),
'§¡’e' => self::deserialize_chatForbidden($stream),
'�ü­
' => self::deserialize_channel($stream),
'Õ“Ô' => self::deserialize_channelForbidden($stream),
'¡Ïr0' => self::deserialize_type_Chat(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'@ìàÍ' => self::deserialize_chatInvite($stream),
'°\\ia' => [
'_' => 'chatInvitePeek',
'chat' => match (stream_get_contents($stream, 4)) {
'e(V)' => self::deserialize_chatEmpty($stream),
'VòËA' => self::deserialize_chat($stream),
'§¡’e' => self::deserialize_chatForbidden($stream),
'�ü­
' => self::deserialize_channel($stream),
'Õ“Ô' => self::deserialize_channelForbidden($stream),
'¡Ïr0' => self::deserialize_type_Chat(self::gzdecode($stream)),
default => self::err($stream)
}
,
'expires' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => $this->deserialize_type_ChatInvite(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_StickerSetCovered(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'Ò¥d' => [
'_' => 'stickerSetCovered',
'set' => match (stream_get_contents($stream, 4)) {
'ÜNÑ-' => self::deserialize_stickerSet($stream),
'¡Ïr0' => self::deserialize_type_StickerSet(self::gzdecode($stream)),
default => self::err($stream)
}
,
'cover' => match (stream_get_contents($stream, 4)) {
'qÈø6' => [
'_' => 'documentEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ØÄÔ�' => self::deserialize_document($stream),
'¡Ïr0' => self::deserialize_type_Document(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'å4' => [
'_' => 'stickerSetMultiCovered',
'set' => match (stream_get_contents($stream, 4)) {
'ÜNÑ-' => self::deserialize_stickerSet($stream),
'¡Ïr0' => self::deserialize_type_StickerSet(self::gzdecode($stream)),
default => self::err($stream)
}
,
'covers' => self::deserialize_type_array_of_Document(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'<Ñ@' => [
'_' => 'stickerSetFullCovered',
'set' => match (stream_get_contents($stream, 4)) {
'ÜNÑ-' => self::deserialize_stickerSet($stream),
'¡Ïr0' => self::deserialize_type_StickerSet(self::gzdecode($stream)),
default => self::err($stream)
}
,
'packs' => self::deserialize_type_array_of_StickerPack(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'keywords' => self::deserialize_type_array_of_StickerKeyword(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'documents' => self::deserialize_type_array_of_Document(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
']±w' => [
'_' => 'stickerSetNoCovered',
'set' => match (stream_get_contents($stream, 4)) {
'ÜNÑ-' => self::deserialize_stickerSet($stream),
'¡Ïr0' => self::deserialize_type_StickerSet(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => $this->deserialize_type_StickerSetCovered(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_array_of_RecentMeUrl(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'=ÑáF' => [
'_' => 'recentMeUrlUnknown',
'url' => self::deserialize_string($stream),
],
'â	,¹' => [
'_' => 'recentMeUrlUser',
'url' => self::deserialize_string($stream),
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÒqÚ²' => [
'_' => 'recentMeUrlChat',
'url' => self::deserialize_string($stream),
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'Ië' => [
'_' => 'recentMeUrlChatInvite',
'url' => self::deserialize_string($stream),
'chat_invite' => match (stream_get_contents($stream, 4)) {
'|mhZ' => [
'_' => 'chatInviteAlready',
'chat' => match (stream_get_contents($stream, 4)) {
'e(V)' => self::deserialize_chatEmpty($stream),
'VòËA' => self::deserialize_chat($stream),
'§¡’e' => self::deserialize_chatForbidden($stream),
'�ü­
' => self::deserialize_channel($stream),
'Õ“Ô' => self::deserialize_channelForbidden($stream),
'¡Ïr0' => self::deserialize_type_Chat(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'@ìàÍ' => self::deserialize_chatInvite($stream),
'°\\ia' => [
'_' => 'chatInvitePeek',
'chat' => match (stream_get_contents($stream, 4)) {
'e(V)' => self::deserialize_chatEmpty($stream),
'VòËA' => self::deserialize_chat($stream),
'§¡’e' => self::deserialize_chatForbidden($stream),
'�ü­
' => self::deserialize_channel($stream),
'Õ“Ô' => self::deserialize_channelForbidden($stream),
'¡Ïr0' => self::deserialize_type_Chat(self::gzdecode($stream)),
default => self::err($stream)
}
,
'expires' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_ChatInvite(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ÜW
¼' => [
'_' => 'recentMeUrlStickerSet',
'url' => self::deserialize_string($stream),
'set' => match (stream_get_contents($stream, 4)) {
'Ò¥d' => [
'_' => 'stickerSetCovered',
'set' => match (stream_get_contents($stream, 4)) {
'ÜNÑ-' => self::deserialize_stickerSet($stream),
'¡Ïr0' => self::deserialize_type_StickerSet(self::gzdecode($stream)),
default => self::err($stream)
}
,
'cover' => match (stream_get_contents($stream, 4)) {
'qÈø6' => [
'_' => 'documentEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ØÄÔ�' => self::deserialize_document($stream),
'¡Ïr0' => self::deserialize_type_Document(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'å4' => [
'_' => 'stickerSetMultiCovered',
'set' => match (stream_get_contents($stream, 4)) {
'ÜNÑ-' => self::deserialize_stickerSet($stream),
'¡Ïr0' => self::deserialize_type_StickerSet(self::gzdecode($stream)),
default => self::err($stream)
}
,
'covers' => self::deserialize_type_array_of_Document(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'<Ñ@' => [
'_' => 'stickerSetFullCovered',
'set' => match (stream_get_contents($stream, 4)) {
'ÜNÑ-' => self::deserialize_stickerSet($stream),
'¡Ïr0' => self::deserialize_type_StickerSet(self::gzdecode($stream)),
default => self::err($stream)
}
,
'packs' => self::deserialize_type_array_of_StickerPack(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'keywords' => self::deserialize_type_array_of_StickerKeyword(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'documents' => self::deserialize_type_array_of_Document(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
']±w' => [
'_' => 'stickerSetNoCovered',
'set' => match (stream_get_contents($stream, 4)) {
'ÜNÑ-' => self::deserialize_stickerSet($stream),
'¡Ïr0' => self::deserialize_type_StickerSet(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_StickerSetCovered(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => $this->deserialize_type_RecentMeUrl(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_help___deepLinkInfo(mixed $stream): mixed {
$tmp = ['_' => 'help.deepLinkInfo'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['update_app'] = ($flags & 1) !== 0;
$tmp['message'] = self::deserialize_string($stream);
if (($flags & 2) !== 0) $tmp['entities'] = self::deserialize_type_array_of_MessageEntity(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
return $tmp;

    }
    private  function deserialize_type_array_of_JSONObjectValue(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result[self::deserialize_string($stream)] = match (stream_get_contents($stream, 4)) {
'h{m?' => null,
'j^4Ç' => match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) },
'¤ßà+' => unpack('d', stream_get_contents($stream, 8))[1],
'zv·' => self::deserialize_string($stream),
'cGD÷' => $this->deserialize_type_array_of_JSONValue(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'�ÔÁ™' => $this->deserialize_type_array_of_JSONObjectValue(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'¡Ïr0' => $this->deserialize_type_JSONValue(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private static function deserialize_type_array_of_JSONValue(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'h{m?' => null,
'j^4Ç' => match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) },
'¤ßà+' => unpack('d', stream_get_contents($stream, 8))[1],
'zv·' => self::deserialize_string($stream),
'cGD÷' => $this->deserialize_type_array_of_JSONValue(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'�ÔÁ™' => self::deserialize_type_array_of_JSONObjectValue(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'¡Ïr0' => $this->deserialize_type_JSONValue(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private static function deserialize_type_JSONValue(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'h{m?' => null,
'j^4Ç' => match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) },
'¤ßà+' => unpack('d', stream_get_contents($stream, 8))[1],
'zv·' => self::deserialize_string($stream),
'cGD÷' => $this->deserialize_type_array_of_JSONValue(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'�ÔÁ™' => self::deserialize_type_array_of_JSONObjectValue(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'¡Ïr0' => $this->deserialize_type_JSONValue(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_help___promoData(mixed $stream): mixed {
$tmp = ['_' => 'help.promoData'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['proxy'] = ($flags & 1) !== 0;
$tmp['expires'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['peer'] = match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['chats'] = self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['users'] = self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 2) !== 0) $tmp['psa_type'] = self::deserialize_string($stream);
if (($flags & 4) !== 0) $tmp['psa_message'] = self::deserialize_string($stream);
return $tmp;

    }
    private  function deserialize_help___countryCode(mixed $stream): mixed {
$tmp = ['_' => 'help.countryCode'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['country_code'] = self::deserialize_string($stream);
if (($flags & 1) !== 0) $tmp['prefixes'] = self::deserialize_type_array_of_string(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 2) !== 0) $tmp['patterns'] = self::deserialize_type_array_of_string(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
return $tmp;

    }
    private  function deserialize_type_array_of_help___CountryCode(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'ïÅB' => self::deserialize_help___countryCode($stream),
'¡Ïr0' => $this->deserialize_type_help___CountryCode(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_help___country(mixed $stream): mixed {
$tmp = ['_' => 'help.country'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['hidden'] = ($flags & 1) !== 0;
$tmp['iso2'] = self::deserialize_string($stream);
$tmp['default_name'] = self::deserialize_string($stream);
if (($flags & 2) !== 0) $tmp['name'] = self::deserialize_string($stream);
$tmp['country_codes'] = self::deserialize_type_array_of_help___CountryCode(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
return $tmp;

    }
    private  function deserialize_type_array_of_help___Country(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'#Ž‡Ã' => self::deserialize_help___country($stream),
'¡Ïr0' => $this->deserialize_type_help___Country(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_premiumSubscriptionOption(mixed $stream): mixed {
$tmp = ['_' => 'premiumSubscriptionOption'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['current'] = ($flags & 2) !== 0;
$tmp['can_purchase_upgrade'] = ($flags & 4) !== 0;
if (($flags & 8) !== 0) $tmp['transaction'] = self::deserialize_string($stream);
$tmp['months'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['currency'] = self::deserialize_string($stream);
$tmp['amount'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['bot_url'] = self::deserialize_string($stream);
if (($flags & 1) !== 0) $tmp['store_product'] = self::deserialize_string($stream);
return $tmp;

    }
    private  function deserialize_type_array_of_PremiumSubscriptionOption(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'ò-_' => self::deserialize_premiumSubscriptionOption($stream),
'¡Ïr0' => $this->deserialize_type_PremiumSubscriptionOption(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_help___PeerColorSet(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'Xš!&' => [
'_' => 'help.peerColorSet',
'colors' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ëa}v' => [
'_' => 'help.peerColorProfileSet',
'palette_colors' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'bg_colors' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'story_colors' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => $this->deserialize_type_help___PeerColorSet(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_help___peerColorOption(mixed $stream): mixed {
$tmp = ['_' => 'help.peerColorOption'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['hidden'] = ($flags & 1) !== 0;
$tmp['color_id'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 2) !== 0) $tmp['colors'] = match (stream_get_contents($stream, 4)) {
'Xš!&' => [
'_' => 'help.peerColorSet',
'colors' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ëa}v' => [
'_' => 'help.peerColorProfileSet',
'palette_colors' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'bg_colors' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'story_colors' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => self::deserialize_type_help___PeerColorSet(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 4) !== 0) $tmp['dark_colors'] = match (stream_get_contents($stream, 4)) {
'Xš!&' => [
'_' => 'help.peerColorSet',
'colors' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ëa}v' => [
'_' => 'help.peerColorProfileSet',
'palette_colors' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'bg_colors' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'story_colors' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => self::deserialize_type_help___PeerColorSet(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 8) !== 0) $tmp['channel_min_level'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 16) !== 0) $tmp['group_min_level'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_array_of_help___PeerColorOption(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'¾nì­' => self::deserialize_help___peerColorOption($stream),
'¡Ïr0' => $this->deserialize_type_help___PeerColorOption(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_Timezone(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'õ‰’ÿ' => [
'_' => 'timezone',
'id' => self::deserialize_string($stream),
'name' => self::deserialize_string($stream),
'utc_offset' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => $this->deserialize_type_Timezone(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_ChannelParticipant(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'ÀÀ' => [
'_' => 'channelParticipant',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'§¿¨5' => self::deserialize_channelParticipantSelf($stream),
'Óæ/' => self::deserialize_channelParticipantCreator($stream),
'S»Ã4' => self::deserialize_channelParticipantAdmin($stream),
'Nøm' => self::deserialize_channelParticipantBanned($stream),
'ð' => [
'_' => 'channelParticipantLeft',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_ChannelParticipant(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_GroupCallParticipant(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'þ6¦ë' => self::deserialize_groupCallParticipant($stream),
'¡Ïr0' => $this->deserialize_type_GroupCallParticipant(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_channelAdminLogEventActionParticipantJoinByInvite(mixed $stream): mixed {
$tmp = ['_' => 'channelAdminLogEventActionParticipantJoinByInvite'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['via_chatlist'] = ($flags & 1) !== 0;
$tmp['invite'] = match (stream_get_contents($stream, 4)) {
'¨´
' => self::deserialize_chatInviteExported($stream),
'·zí' => [
'_' => 'chatInvitePublicJoinRequests',
],
'¡Ïr0' => self::deserialize_type_ExportedChatInvite(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_type_ForumTopic(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'›?' => [
'_' => 'forumTopicDeleted',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'©pq' => self::deserialize_forumTopic($stream),
'¡Ïr0' => $this->deserialize_type_ForumTopic(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_channelAdminLogEventActionPinTopic(mixed $stream): mixed {
$tmp = ['_' => 'channelAdminLogEventActionPinTopic'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['prev_topic'] = match (stream_get_contents($stream, 4)) {
'›?' => [
'_' => 'forumTopicDeleted',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'©pq' => self::deserialize_forumTopic($stream),
'¡Ïr0' => self::deserialize_type_ForumTopic(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 2) !== 0) $tmp['new_topic'] = match (stream_get_contents($stream, 4)) {
'›?' => [
'_' => 'forumTopicDeleted',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'©pq' => self::deserialize_forumTopic($stream),
'¡Ïr0' => self::deserialize_type_ForumTopic(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_type_ChannelAdminLogEventAction(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'%¸ßæ' => [
'_' => 'channelAdminLogEventActionChangeTitle',
'prev_value' => self::deserialize_string($stream),
'new_value' => self::deserialize_string($stream),
],
'.ŠU' => [
'_' => 'channelAdminLogEventActionChangeAbout',
'prev_value' => self::deserialize_string($stream),
'new_value' => self::deserialize_string($stream),
],
'8üJj' => [
'_' => 'channelAdminLogEventActionChangeUsername',
'prev_value' => self::deserialize_string($stream),
'new_value' => self::deserialize_string($stream),
],
'¯ÒKC' => [
'_' => 'channelAdminLogEventActionChangePhoto',
'prev_photo' => match (stream_get_contents($stream, 4)) {
'-²1#' => [
'_' => 'photoEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ezû' => self::deserialize_photo($stream),
'¡Ïr0' => self::deserialize_type_Photo(self::gzdecode($stream)),
default => self::err($stream)
}
,
'new_photo' => match (stream_get_contents($stream, 4)) {
'-²1#' => [
'_' => 'photoEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ezû' => self::deserialize_photo($stream),
'¡Ïr0' => self::deserialize_type_Photo(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'®y' => [
'_' => 'channelAdminLogEventActionToggleInvites',
'new_value' => match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) },
],
'q	®&' => [
'_' => 'channelAdminLogEventActionToggleSignatures',
'new_value' => match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) },
],
',èé' => [
'_' => 'channelAdminLogEventActionUpdatePinned',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'$›p' => [
'_' => 'channelAdminLogEventActionEditMessage',
'prev_message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
'new_message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'»GàB' => [
'_' => 'channelAdminLogEventActionDeleteMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Ó@0' => [
'_' => 'channelAdminLogEventActionParticipantJoin',
],
'òw—ø' => [
'_' => 'channelAdminLogEventActionParticipantLeave',
],
'Ø4ã' => [
'_' => 'channelAdminLogEventActionParticipantInvite',
'participant' => match (stream_get_contents($stream, 4)) {
'ÀÀ' => [
'_' => 'channelParticipant',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'§¿¨5' => self::deserialize_channelParticipantSelf($stream),
'Óæ/' => self::deserialize_channelParticipantCreator($stream),
'S»Ã4' => self::deserialize_channelParticipantAdmin($stream),
'Nøm' => self::deserialize_channelParticipantBanned($stream),
'ð' => [
'_' => 'channelParticipantLeft',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_ChannelParticipant(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'~=Øæ' => [
'_' => 'channelAdminLogEventActionParticipantToggleBan',
'prev_participant' => match (stream_get_contents($stream, 4)) {
'ÀÀ' => [
'_' => 'channelParticipant',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'§¿¨5' => self::deserialize_channelParticipantSelf($stream),
'Óæ/' => self::deserialize_channelParticipantCreator($stream),
'S»Ã4' => self::deserialize_channelParticipantAdmin($stream),
'Nøm' => self::deserialize_channelParticipantBanned($stream),
'ð' => [
'_' => 'channelParticipantLeft',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_ChannelParticipant(self::gzdecode($stream)),
default => self::err($stream)
}
,
'new_participant' => match (stream_get_contents($stream, 4)) {
'ÀÀ' => [
'_' => 'channelParticipant',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'§¿¨5' => self::deserialize_channelParticipantSelf($stream),
'Óæ/' => self::deserialize_channelParticipantCreator($stream),
'S»Ã4' => self::deserialize_channelParticipantAdmin($stream),
'Nøm' => self::deserialize_channelParticipantBanned($stream),
'ð' => [
'_' => 'channelParticipantLeft',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_ChannelParticipant(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ggÕ' => [
'_' => 'channelAdminLogEventActionParticipantToggleAdmin',
'prev_participant' => match (stream_get_contents($stream, 4)) {
'ÀÀ' => [
'_' => 'channelParticipant',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'§¿¨5' => self::deserialize_channelParticipantSelf($stream),
'Óæ/' => self::deserialize_channelParticipantCreator($stream),
'S»Ã4' => self::deserialize_channelParticipantAdmin($stream),
'Nøm' => self::deserialize_channelParticipantBanned($stream),
'ð' => [
'_' => 'channelParticipantLeft',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_ChannelParticipant(self::gzdecode($stream)),
default => self::err($stream)
}
,
'new_participant' => match (stream_get_contents($stream, 4)) {
'ÀÀ' => [
'_' => 'channelParticipant',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'§¿¨5' => self::deserialize_channelParticipantSelf($stream),
'Óæ/' => self::deserialize_channelParticipantCreator($stream),
'S»Ã4' => self::deserialize_channelParticipantAdmin($stream),
'Nøm' => self::deserialize_channelParticipantBanned($stream),
'ð' => [
'_' => 'channelParticipantLeft',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_ChannelParticipant(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'§ÊÃ±' => [
'_' => 'channelAdminLogEventActionChangeStickerSet',
'prev_stickerset' => match (stream_get_contents($stream, 4)) {
'•+¶ÿ' => [
'_' => 'inputStickerSetEmpty',
],
'i¢ç�' => [
'_' => 'inputStickerSetID',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
' È†' => [
'_' => 'inputStickerSetShortName',
'short_name' => self::deserialize_string($stream),
],
'È‡' => [
'_' => 'inputStickerSetAnimatedEmoji',
],
'Ræ' => [
'_' => 'inputStickerSetDice',
'emoticon' => self::deserialize_string($stream),
],
'97Þ' => [
'_' => 'inputStickerSetAnimatedEmojiAnimations',
],
';‹È' => [
'_' => 'inputStickerSetPremiumGifts',
],
'ÎÔÄ' => [
'_' => 'inputStickerSetEmojiGenericAnimations',
],
'îõÐ)' => [
'_' => 'inputStickerSetEmojiDefaultStatuses',
],
'éøÁD' => [
'_' => 'inputStickerSetEmojiDefaultTopicIcons',
],
'S…tI' => [
'_' => 'inputStickerSetEmojiChannelDefaultStatuses',
],
'¡Ïr0' => self::deserialize_type_InputStickerSet(self::gzdecode($stream)),
default => self::err($stream)
}
,
'new_stickerset' => match (stream_get_contents($stream, 4)) {
'•+¶ÿ' => [
'_' => 'inputStickerSetEmpty',
],
'i¢ç�' => [
'_' => 'inputStickerSetID',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
' È†' => [
'_' => 'inputStickerSetShortName',
'short_name' => self::deserialize_string($stream),
],
'È‡' => [
'_' => 'inputStickerSetAnimatedEmoji',
],
'Ræ' => [
'_' => 'inputStickerSetDice',
'emoticon' => self::deserialize_string($stream),
],
'97Þ' => [
'_' => 'inputStickerSetAnimatedEmojiAnimations',
],
';‹È' => [
'_' => 'inputStickerSetPremiumGifts',
],
'ÎÔÄ' => [
'_' => 'inputStickerSetEmojiGenericAnimations',
],
'îõÐ)' => [
'_' => 'inputStickerSetEmojiDefaultStatuses',
],
'éøÁD' => [
'_' => 'inputStickerSetEmojiDefaultTopicIcons',
],
'S…tI' => [
'_' => 'inputStickerSetEmojiChannelDefaultStatuses',
],
'¡Ïr0' => self::deserialize_type_InputStickerSet(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ñ•\\_' => [
'_' => 'channelAdminLogEventActionTogglePreHistoryHidden',
'new_value' => match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) },
],
'
üõ-' => [
'_' => 'channelAdminLogEventActionDefaultBannedRights',
'prev_banned_rights' => match (stream_get_contents($stream, 4)) {
'Ÿ' => self::deserialize_chatBannedRights($stream),
'¡Ïr0' => self::deserialize_type_ChatBannedRights(self::gzdecode($stream)),
default => self::err($stream)
}
,
'new_banned_rights' => match (stream_get_contents($stream, 4)) {
'Ÿ' => self::deserialize_chatBannedRights($stream),
'¡Ïr0' => self::deserialize_type_ChatBannedRights(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'C–�' => [
'_' => 'channelAdminLogEventActionStopPoll',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Èz' => [
'_' => 'channelAdminLogEventActionChangeLinkedChat',
'prev_value' => unpack('q', stream_get_contents($stream, 8))[1],
'new_value' => unpack('q', stream_get_contents($stream, 8))[1],
],
'®vk' => [
'_' => 'channelAdminLogEventActionChangeLocation',
'prev_value' => match (stream_get_contents($stream, 4)) {
'‹­µ¿' => [
'_' => 'channelLocationEmpty',
],
'Û‚› ' => [
'_' => 'channelLocation',
'geo_point' => match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => self::deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
,
'address' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_ChannelLocation(self::gzdecode($stream)),
default => self::err($stream)
}
,
'new_value' => match (stream_get_contents($stream, 4)) {
'‹­µ¿' => [
'_' => 'channelLocationEmpty',
],
'Û‚› ' => [
'_' => 'channelLocation',
'geo_point' => match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => self::deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
,
'address' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_ChannelLocation(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'y—�S' => [
'_' => 'channelAdminLogEventActionToggleSlowMode',
'prev_value' => unpack('l', stream_get_contents($stream, 4))[1],
'new_value' => unpack('l', stream_get_contents($stream, 4))[1],
],
'E— #' => [
'_' => 'channelAdminLogEventActionStartGroupCall',
'call' => match (stream_get_contents($stream, 4)) {
'„ªØ' => [
'_' => 'inputGroupCall',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputGroupCall(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'@‘ŸÛ' => [
'_' => 'channelAdminLogEventActionDiscardGroupCall',
'call' => match (stream_get_contents($stream, 4)) {
'„ªØ' => [
'_' => 'inputGroupCall',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputGroupCall(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Ò$$ù' => [
'_' => 'channelAdminLogEventActionParticipantMute',
'participant' => match (stream_get_contents($stream, 4)) {
'þ6¦ë' => self::deserialize_groupCallParticipant($stream),
'¡Ïr0' => self::deserialize_type_GroupCallParticipant(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'À)Dæ' => [
'_' => 'channelAdminLogEventActionParticipantUnmute',
'participant' => match (stream_get_contents($stream, 4)) {
'þ6¦ë' => self::deserialize_groupCallParticipant($stream),
'¡Ïr0' => self::deserialize_type_GroupCallParticipant(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'G¢ÖV' => [
'_' => 'channelAdminLogEventActionToggleGroupCallSetting',
'join_muted' => match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) },
],
'XÁŸþ' => self::deserialize_channelAdminLogEventActionParticipantJoinByInvite($stream),
'¤üPZ' => [
'_' => 'channelAdminLogEventActionExportedInviteDelete',
'invite' => match (stream_get_contents($stream, 4)) {
'¨´
' => self::deserialize_chatInviteExported($stream),
'·zí' => [
'_' => 'chatInvitePublicJoinRequests',
],
'¡Ïr0' => self::deserialize_type_ExportedChatInvite(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'N
A' => [
'_' => 'channelAdminLogEventActionExportedInviteRevoke',
'invite' => match (stream_get_contents($stream, 4)) {
'¨´
' => self::deserialize_chatInviteExported($stream),
'·zí' => [
'_' => 'chatInvitePublicJoinRequests',
],
'¡Ïr0' => self::deserialize_type_ExportedChatInvite(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Y»é' => [
'_' => 'channelAdminLogEventActionExportedInviteEdit',
'prev_invite' => match (stream_get_contents($stream, 4)) {
'¨´
' => self::deserialize_chatInviteExported($stream),
'·zí' => [
'_' => 'chatInvitePublicJoinRequests',
],
'¡Ïr0' => self::deserialize_type_ExportedChatInvite(self::gzdecode($stream)),
default => self::err($stream)
}
,
'new_invite' => match (stream_get_contents($stream, 4)) {
'¨´
' => self::deserialize_chatInviteExported($stream),
'·zí' => [
'_' => 'chatInvitePublicJoinRequests',
],
'¡Ïr0' => self::deserialize_type_ExportedChatInvite(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Gh>' => [
'_' => 'channelAdminLogEventActionParticipantVolume',
'participant' => match (stream_get_contents($stream, 4)) {
'þ6¦ë' => self::deserialize_groupCallParticipant($stream),
'¡Ïr0' => self::deserialize_type_GroupCallParticipant(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'8”n' => [
'_' => 'channelAdminLogEventActionChangeHistoryTTL',
'prev_value' => unpack('l', stream_get_contents($stream, 4))[1],
'new_value' => unpack('l', stream_get_contents($stream, 4))[1],
],
'J¶¯' => [
'_' => 'channelAdminLogEventActionParticipantJoinByRequest',
'invite' => match (stream_get_contents($stream, 4)) {
'¨´
' => self::deserialize_chatInviteExported($stream),
'·zí' => [
'_' => 'chatInvitePublicJoinRequests',
],
'¡Ïr0' => self::deserialize_type_ExportedChatInvite(self::gzdecode($stream)),
default => self::err($stream)
}
,
'approved_by' => unpack('q', stream_get_contents($stream, 8))[1],
],
'fÇ*Ë' => [
'_' => 'channelAdminLogEventActionToggleNoForwards',
'new_value' => match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) },
],
'h(�\'' => [
'_' => 'channelAdminLogEventActionSendMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'øN¾' => [
'_' => 'channelAdminLogEventActionChangeAvailableReactions',
'prev_value' => match (stream_get_contents($stream, 4)) {
'¼2üê' => [
'_' => 'chatReactionsNone',
],
'Ê‹’R' => self::deserialize_chatReactionsAll($stream),
'7@f' => [
'_' => 'chatReactionsSome',
'reactions' => self::deserialize_type_array_of_Reaction(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => self::deserialize_type_ChatReactions(self::gzdecode($stream)),
default => self::err($stream)
}
,
'new_value' => match (stream_get_contents($stream, 4)) {
'¼2üê' => [
'_' => 'chatReactionsNone',
],
'Ê‹’R' => self::deserialize_chatReactionsAll($stream),
'7@f' => [
'_' => 'chatReactionsSome',
'reactions' => self::deserialize_type_array_of_Reaction(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => self::deserialize_type_ChatReactions(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'©³Oð' => [
'_' => 'channelAdminLogEventActionChangeUsernames',
'prev_value' => self::deserialize_type_array_of_string(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'new_value' => self::deserialize_type_array_of_string(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ƒcÌ' => [
'_' => 'channelAdminLogEventActionToggleForum',
'new_value' => match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) },
],
'(}pX' => [
'_' => 'channelAdminLogEventActionCreateTopic',
'topic' => match (stream_get_contents($stream, 4)) {
'›?' => [
'_' => 'forumTopicDeleted',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'©pq' => self::deserialize_forumTopic($stream),
'¡Ïr0' => self::deserialize_type_ForumTopic(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'âoð' => [
'_' => 'channelAdminLogEventActionEditTopic',
'prev_topic' => match (stream_get_contents($stream, 4)) {
'›?' => [
'_' => 'forumTopicDeleted',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'©pq' => self::deserialize_forumTopic($stream),
'¡Ïr0' => self::deserialize_type_ForumTopic(self::gzdecode($stream)),
default => self::err($stream)
}
,
'new_topic' => match (stream_get_contents($stream, 4)) {
'›?' => [
'_' => 'forumTopicDeleted',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'©pq' => self::deserialize_forumTopic($stream),
'¡Ïr0' => self::deserialize_type_ForumTopic(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'	‰®' => [
'_' => 'channelAdminLogEventActionDeleteTopic',
'topic' => match (stream_get_contents($stream, 4)) {
'›?' => [
'_' => 'forumTopicDeleted',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'©pq' => self::deserialize_forumTopic($stream),
'¡Ïr0' => self::deserialize_type_ForumTopic(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
';5�]' => self::deserialize_channelAdminLogEventActionPinTopic($stream),
'ümód' => [
'_' => 'channelAdminLogEventActionToggleAntiSpam',
'new_value' => match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) },
],
'€ç–W' => [
'_' => 'channelAdminLogEventActionChangePeerColor',
'prev_value' => match (stream_get_contents($stream, 4)) {
'ÏZKµ' => self::deserialize_peerColor($stream),
'¡Ïr0' => self::deserialize_type_PeerColor(self::gzdecode($stream)),
default => self::err($stream)
}
,
'new_value' => match (stream_get_contents($stream, 4)) {
'ÏZKµ' => self::deserialize_peerColor($stream),
'¡Ïr0' => self::deserialize_type_PeerColor(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'%{G^' => [
'_' => 'channelAdminLogEventActionChangeProfilePeerColor',
'prev_value' => match (stream_get_contents($stream, 4)) {
'ÏZKµ' => self::deserialize_peerColor($stream),
'¡Ïr0' => self::deserialize_type_PeerColor(self::gzdecode($stream)),
default => self::err($stream)
}
,
'new_value' => match (stream_get_contents($stream, 4)) {
'ÏZKµ' => self::deserialize_peerColor($stream),
'¡Ïr0' => self::deserialize_type_PeerColor(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'R]»1' => [
'_' => 'channelAdminLogEventActionChangeWallpaper',
'prev_value' => match (stream_get_contents($stream, 4)) {
'íÃ7¤' => self::deserialize_wallPaper($stream),
'A€à' => self::deserialize_wallPaperNoFile($stream),
'¡Ïr0' => self::deserialize_type_WallPaper(self::gzdecode($stream)),
default => self::err($stream)
}
,
'new_value' => match (stream_get_contents($stream, 4)) {
'íÃ7¤' => self::deserialize_wallPaper($stream),
'A€à' => self::deserialize_wallPaperNoFile($stream),
'¡Ïr0' => self::deserialize_type_WallPaper(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'±þ©>' => [
'_' => 'channelAdminLogEventActionChangeEmojiStatus',
'prev_value' => match (stream_get_contents($stream, 4)) {
'®á-' => [
'_' => 'emojiStatusEmpty',
],
'�a›’' => [
'_' => 'emojiStatus',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'Ç¨0ú' => [
'_' => 'emojiStatusUntil',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'until' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_EmojiStatus(self::gzdecode($stream)),
default => self::err($stream)
}
,
'new_value' => match (stream_get_contents($stream, 4)) {
'®á-' => [
'_' => 'emojiStatusEmpty',
],
'�a›’' => [
'_' => 'emojiStatus',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'Ç¨0ú' => [
'_' => 'emojiStatusUntil',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'until' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_EmojiStatus(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'«@ØF' => [
'_' => 'channelAdminLogEventActionChangeEmojiStickerSet',
'prev_stickerset' => match (stream_get_contents($stream, 4)) {
'•+¶ÿ' => [
'_' => 'inputStickerSetEmpty',
],
'i¢ç�' => [
'_' => 'inputStickerSetID',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
' È†' => [
'_' => 'inputStickerSetShortName',
'short_name' => self::deserialize_string($stream),
],
'È‡' => [
'_' => 'inputStickerSetAnimatedEmoji',
],
'Ræ' => [
'_' => 'inputStickerSetDice',
'emoticon' => self::deserialize_string($stream),
],
'97Þ' => [
'_' => 'inputStickerSetAnimatedEmojiAnimations',
],
';‹È' => [
'_' => 'inputStickerSetPremiumGifts',
],
'ÎÔÄ' => [
'_' => 'inputStickerSetEmojiGenericAnimations',
],
'îõÐ)' => [
'_' => 'inputStickerSetEmojiDefaultStatuses',
],
'éøÁD' => [
'_' => 'inputStickerSetEmojiDefaultTopicIcons',
],
'S…tI' => [
'_' => 'inputStickerSetEmojiChannelDefaultStatuses',
],
'¡Ïr0' => self::deserialize_type_InputStickerSet(self::gzdecode($stream)),
default => self::err($stream)
}
,
'new_stickerset' => match (stream_get_contents($stream, 4)) {
'•+¶ÿ' => [
'_' => 'inputStickerSetEmpty',
],
'i¢ç�' => [
'_' => 'inputStickerSetID',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
' È†' => [
'_' => 'inputStickerSetShortName',
'short_name' => self::deserialize_string($stream),
],
'È‡' => [
'_' => 'inputStickerSetAnimatedEmoji',
],
'Ræ' => [
'_' => 'inputStickerSetDice',
'emoticon' => self::deserialize_string($stream),
],
'97Þ' => [
'_' => 'inputStickerSetAnimatedEmojiAnimations',
],
';‹È' => [
'_' => 'inputStickerSetPremiumGifts',
],
'ÎÔÄ' => [
'_' => 'inputStickerSetEmojiGenericAnimations',
],
'îõÐ)' => [
'_' => 'inputStickerSetEmojiDefaultStatuses',
],
'éøÁD' => [
'_' => 'inputStickerSetEmojiDefaultTopicIcons',
],
'S…tI' => [
'_' => 'inputStickerSetEmojiChannelDefaultStatuses',
],
'¡Ïr0' => self::deserialize_type_InputStickerSet(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => $this->deserialize_type_ChannelAdminLogEventAction(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_array_of_ChannelAdminLogEvent(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'Íh­' => [
'_' => 'channelAdminLogEvent',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'action' => match (stream_get_contents($stream, 4)) {
'%¸ßæ' => [
'_' => 'channelAdminLogEventActionChangeTitle',
'prev_value' => self::deserialize_string($stream),
'new_value' => self::deserialize_string($stream),
],
'.ŠU' => [
'_' => 'channelAdminLogEventActionChangeAbout',
'prev_value' => self::deserialize_string($stream),
'new_value' => self::deserialize_string($stream),
],
'8üJj' => [
'_' => 'channelAdminLogEventActionChangeUsername',
'prev_value' => self::deserialize_string($stream),
'new_value' => self::deserialize_string($stream),
],
'¯ÒKC' => [
'_' => 'channelAdminLogEventActionChangePhoto',
'prev_photo' => match (stream_get_contents($stream, 4)) {
'-²1#' => [
'_' => 'photoEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ezû' => self::deserialize_photo($stream),
'¡Ïr0' => self::deserialize_type_Photo(self::gzdecode($stream)),
default => self::err($stream)
}
,
'new_photo' => match (stream_get_contents($stream, 4)) {
'-²1#' => [
'_' => 'photoEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ezû' => self::deserialize_photo($stream),
'¡Ïr0' => self::deserialize_type_Photo(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'®y' => [
'_' => 'channelAdminLogEventActionToggleInvites',
'new_value' => match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) },
],
'q	®&' => [
'_' => 'channelAdminLogEventActionToggleSignatures',
'new_value' => match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) },
],
',èé' => [
'_' => 'channelAdminLogEventActionUpdatePinned',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'$›p' => [
'_' => 'channelAdminLogEventActionEditMessage',
'prev_message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
'new_message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'»GàB' => [
'_' => 'channelAdminLogEventActionDeleteMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Ó@0' => [
'_' => 'channelAdminLogEventActionParticipantJoin',
],
'òw—ø' => [
'_' => 'channelAdminLogEventActionParticipantLeave',
],
'Ø4ã' => [
'_' => 'channelAdminLogEventActionParticipantInvite',
'participant' => match (stream_get_contents($stream, 4)) {
'ÀÀ' => [
'_' => 'channelParticipant',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'§¿¨5' => self::deserialize_channelParticipantSelf($stream),
'Óæ/' => self::deserialize_channelParticipantCreator($stream),
'S»Ã4' => self::deserialize_channelParticipantAdmin($stream),
'Nøm' => self::deserialize_channelParticipantBanned($stream),
'ð' => [
'_' => 'channelParticipantLeft',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_ChannelParticipant(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'~=Øæ' => [
'_' => 'channelAdminLogEventActionParticipantToggleBan',
'prev_participant' => match (stream_get_contents($stream, 4)) {
'ÀÀ' => [
'_' => 'channelParticipant',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'§¿¨5' => self::deserialize_channelParticipantSelf($stream),
'Óæ/' => self::deserialize_channelParticipantCreator($stream),
'S»Ã4' => self::deserialize_channelParticipantAdmin($stream),
'Nøm' => self::deserialize_channelParticipantBanned($stream),
'ð' => [
'_' => 'channelParticipantLeft',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_ChannelParticipant(self::gzdecode($stream)),
default => self::err($stream)
}
,
'new_participant' => match (stream_get_contents($stream, 4)) {
'ÀÀ' => [
'_' => 'channelParticipant',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'§¿¨5' => self::deserialize_channelParticipantSelf($stream),
'Óæ/' => self::deserialize_channelParticipantCreator($stream),
'S»Ã4' => self::deserialize_channelParticipantAdmin($stream),
'Nøm' => self::deserialize_channelParticipantBanned($stream),
'ð' => [
'_' => 'channelParticipantLeft',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_ChannelParticipant(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ggÕ' => [
'_' => 'channelAdminLogEventActionParticipantToggleAdmin',
'prev_participant' => match (stream_get_contents($stream, 4)) {
'ÀÀ' => [
'_' => 'channelParticipant',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'§¿¨5' => self::deserialize_channelParticipantSelf($stream),
'Óæ/' => self::deserialize_channelParticipantCreator($stream),
'S»Ã4' => self::deserialize_channelParticipantAdmin($stream),
'Nøm' => self::deserialize_channelParticipantBanned($stream),
'ð' => [
'_' => 'channelParticipantLeft',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_ChannelParticipant(self::gzdecode($stream)),
default => self::err($stream)
}
,
'new_participant' => match (stream_get_contents($stream, 4)) {
'ÀÀ' => [
'_' => 'channelParticipant',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'§¿¨5' => self::deserialize_channelParticipantSelf($stream),
'Óæ/' => self::deserialize_channelParticipantCreator($stream),
'S»Ã4' => self::deserialize_channelParticipantAdmin($stream),
'Nøm' => self::deserialize_channelParticipantBanned($stream),
'ð' => [
'_' => 'channelParticipantLeft',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_ChannelParticipant(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'§ÊÃ±' => [
'_' => 'channelAdminLogEventActionChangeStickerSet',
'prev_stickerset' => match (stream_get_contents($stream, 4)) {
'•+¶ÿ' => [
'_' => 'inputStickerSetEmpty',
],
'i¢ç�' => [
'_' => 'inputStickerSetID',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
' È†' => [
'_' => 'inputStickerSetShortName',
'short_name' => self::deserialize_string($stream),
],
'È‡' => [
'_' => 'inputStickerSetAnimatedEmoji',
],
'Ræ' => [
'_' => 'inputStickerSetDice',
'emoticon' => self::deserialize_string($stream),
],
'97Þ' => [
'_' => 'inputStickerSetAnimatedEmojiAnimations',
],
';‹È' => [
'_' => 'inputStickerSetPremiumGifts',
],
'ÎÔÄ' => [
'_' => 'inputStickerSetEmojiGenericAnimations',
],
'îõÐ)' => [
'_' => 'inputStickerSetEmojiDefaultStatuses',
],
'éøÁD' => [
'_' => 'inputStickerSetEmojiDefaultTopicIcons',
],
'S…tI' => [
'_' => 'inputStickerSetEmojiChannelDefaultStatuses',
],
'¡Ïr0' => self::deserialize_type_InputStickerSet(self::gzdecode($stream)),
default => self::err($stream)
}
,
'new_stickerset' => match (stream_get_contents($stream, 4)) {
'•+¶ÿ' => [
'_' => 'inputStickerSetEmpty',
],
'i¢ç�' => [
'_' => 'inputStickerSetID',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
' È†' => [
'_' => 'inputStickerSetShortName',
'short_name' => self::deserialize_string($stream),
],
'È‡' => [
'_' => 'inputStickerSetAnimatedEmoji',
],
'Ræ' => [
'_' => 'inputStickerSetDice',
'emoticon' => self::deserialize_string($stream),
],
'97Þ' => [
'_' => 'inputStickerSetAnimatedEmojiAnimations',
],
';‹È' => [
'_' => 'inputStickerSetPremiumGifts',
],
'ÎÔÄ' => [
'_' => 'inputStickerSetEmojiGenericAnimations',
],
'îõÐ)' => [
'_' => 'inputStickerSetEmojiDefaultStatuses',
],
'éøÁD' => [
'_' => 'inputStickerSetEmojiDefaultTopicIcons',
],
'S…tI' => [
'_' => 'inputStickerSetEmojiChannelDefaultStatuses',
],
'¡Ïr0' => self::deserialize_type_InputStickerSet(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ñ•\\_' => [
'_' => 'channelAdminLogEventActionTogglePreHistoryHidden',
'new_value' => match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) },
],
'
üõ-' => [
'_' => 'channelAdminLogEventActionDefaultBannedRights',
'prev_banned_rights' => match (stream_get_contents($stream, 4)) {
'Ÿ' => self::deserialize_chatBannedRights($stream),
'¡Ïr0' => self::deserialize_type_ChatBannedRights(self::gzdecode($stream)),
default => self::err($stream)
}
,
'new_banned_rights' => match (stream_get_contents($stream, 4)) {
'Ÿ' => self::deserialize_chatBannedRights($stream),
'¡Ïr0' => self::deserialize_type_ChatBannedRights(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'C–�' => [
'_' => 'channelAdminLogEventActionStopPoll',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Èz' => [
'_' => 'channelAdminLogEventActionChangeLinkedChat',
'prev_value' => unpack('q', stream_get_contents($stream, 8))[1],
'new_value' => unpack('q', stream_get_contents($stream, 8))[1],
],
'®vk' => [
'_' => 'channelAdminLogEventActionChangeLocation',
'prev_value' => match (stream_get_contents($stream, 4)) {
'‹­µ¿' => [
'_' => 'channelLocationEmpty',
],
'Û‚› ' => [
'_' => 'channelLocation',
'geo_point' => match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => self::deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
,
'address' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_ChannelLocation(self::gzdecode($stream)),
default => self::err($stream)
}
,
'new_value' => match (stream_get_contents($stream, 4)) {
'‹­µ¿' => [
'_' => 'channelLocationEmpty',
],
'Û‚› ' => [
'_' => 'channelLocation',
'geo_point' => match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => self::deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
,
'address' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_ChannelLocation(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'y—�S' => [
'_' => 'channelAdminLogEventActionToggleSlowMode',
'prev_value' => unpack('l', stream_get_contents($stream, 4))[1],
'new_value' => unpack('l', stream_get_contents($stream, 4))[1],
],
'E— #' => [
'_' => 'channelAdminLogEventActionStartGroupCall',
'call' => match (stream_get_contents($stream, 4)) {
'„ªØ' => [
'_' => 'inputGroupCall',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputGroupCall(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'@‘ŸÛ' => [
'_' => 'channelAdminLogEventActionDiscardGroupCall',
'call' => match (stream_get_contents($stream, 4)) {
'„ªØ' => [
'_' => 'inputGroupCall',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputGroupCall(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Ò$$ù' => [
'_' => 'channelAdminLogEventActionParticipantMute',
'participant' => match (stream_get_contents($stream, 4)) {
'þ6¦ë' => self::deserialize_groupCallParticipant($stream),
'¡Ïr0' => self::deserialize_type_GroupCallParticipant(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'À)Dæ' => [
'_' => 'channelAdminLogEventActionParticipantUnmute',
'participant' => match (stream_get_contents($stream, 4)) {
'þ6¦ë' => self::deserialize_groupCallParticipant($stream),
'¡Ïr0' => self::deserialize_type_GroupCallParticipant(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'G¢ÖV' => [
'_' => 'channelAdminLogEventActionToggleGroupCallSetting',
'join_muted' => match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) },
],
'XÁŸþ' => self::deserialize_channelAdminLogEventActionParticipantJoinByInvite($stream),
'¤üPZ' => [
'_' => 'channelAdminLogEventActionExportedInviteDelete',
'invite' => match (stream_get_contents($stream, 4)) {
'¨´
' => self::deserialize_chatInviteExported($stream),
'·zí' => [
'_' => 'chatInvitePublicJoinRequests',
],
'¡Ïr0' => self::deserialize_type_ExportedChatInvite(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'N
A' => [
'_' => 'channelAdminLogEventActionExportedInviteRevoke',
'invite' => match (stream_get_contents($stream, 4)) {
'¨´
' => self::deserialize_chatInviteExported($stream),
'·zí' => [
'_' => 'chatInvitePublicJoinRequests',
],
'¡Ïr0' => self::deserialize_type_ExportedChatInvite(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Y»é' => [
'_' => 'channelAdminLogEventActionExportedInviteEdit',
'prev_invite' => match (stream_get_contents($stream, 4)) {
'¨´
' => self::deserialize_chatInviteExported($stream),
'·zí' => [
'_' => 'chatInvitePublicJoinRequests',
],
'¡Ïr0' => self::deserialize_type_ExportedChatInvite(self::gzdecode($stream)),
default => self::err($stream)
}
,
'new_invite' => match (stream_get_contents($stream, 4)) {
'¨´
' => self::deserialize_chatInviteExported($stream),
'·zí' => [
'_' => 'chatInvitePublicJoinRequests',
],
'¡Ïr0' => self::deserialize_type_ExportedChatInvite(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Gh>' => [
'_' => 'channelAdminLogEventActionParticipantVolume',
'participant' => match (stream_get_contents($stream, 4)) {
'þ6¦ë' => self::deserialize_groupCallParticipant($stream),
'¡Ïr0' => self::deserialize_type_GroupCallParticipant(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'8”n' => [
'_' => 'channelAdminLogEventActionChangeHistoryTTL',
'prev_value' => unpack('l', stream_get_contents($stream, 4))[1],
'new_value' => unpack('l', stream_get_contents($stream, 4))[1],
],
'J¶¯' => [
'_' => 'channelAdminLogEventActionParticipantJoinByRequest',
'invite' => match (stream_get_contents($stream, 4)) {
'¨´
' => self::deserialize_chatInviteExported($stream),
'·zí' => [
'_' => 'chatInvitePublicJoinRequests',
],
'¡Ïr0' => self::deserialize_type_ExportedChatInvite(self::gzdecode($stream)),
default => self::err($stream)
}
,
'approved_by' => unpack('q', stream_get_contents($stream, 8))[1],
],
'fÇ*Ë' => [
'_' => 'channelAdminLogEventActionToggleNoForwards',
'new_value' => match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) },
],
'h(�\'' => [
'_' => 'channelAdminLogEventActionSendMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'øN¾' => [
'_' => 'channelAdminLogEventActionChangeAvailableReactions',
'prev_value' => match (stream_get_contents($stream, 4)) {
'¼2üê' => [
'_' => 'chatReactionsNone',
],
'Ê‹’R' => self::deserialize_chatReactionsAll($stream),
'7@f' => [
'_' => 'chatReactionsSome',
'reactions' => self::deserialize_type_array_of_Reaction(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => self::deserialize_type_ChatReactions(self::gzdecode($stream)),
default => self::err($stream)
}
,
'new_value' => match (stream_get_contents($stream, 4)) {
'¼2üê' => [
'_' => 'chatReactionsNone',
],
'Ê‹’R' => self::deserialize_chatReactionsAll($stream),
'7@f' => [
'_' => 'chatReactionsSome',
'reactions' => self::deserialize_type_array_of_Reaction(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => self::deserialize_type_ChatReactions(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'©³Oð' => [
'_' => 'channelAdminLogEventActionChangeUsernames',
'prev_value' => self::deserialize_type_array_of_string(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'new_value' => self::deserialize_type_array_of_string(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ƒcÌ' => [
'_' => 'channelAdminLogEventActionToggleForum',
'new_value' => match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) },
],
'(}pX' => [
'_' => 'channelAdminLogEventActionCreateTopic',
'topic' => match (stream_get_contents($stream, 4)) {
'›?' => [
'_' => 'forumTopicDeleted',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'©pq' => self::deserialize_forumTopic($stream),
'¡Ïr0' => self::deserialize_type_ForumTopic(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'âoð' => [
'_' => 'channelAdminLogEventActionEditTopic',
'prev_topic' => match (stream_get_contents($stream, 4)) {
'›?' => [
'_' => 'forumTopicDeleted',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'©pq' => self::deserialize_forumTopic($stream),
'¡Ïr0' => self::deserialize_type_ForumTopic(self::gzdecode($stream)),
default => self::err($stream)
}
,
'new_topic' => match (stream_get_contents($stream, 4)) {
'›?' => [
'_' => 'forumTopicDeleted',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'©pq' => self::deserialize_forumTopic($stream),
'¡Ïr0' => self::deserialize_type_ForumTopic(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'	‰®' => [
'_' => 'channelAdminLogEventActionDeleteTopic',
'topic' => match (stream_get_contents($stream, 4)) {
'›?' => [
'_' => 'forumTopicDeleted',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'©pq' => self::deserialize_forumTopic($stream),
'¡Ïr0' => self::deserialize_type_ForumTopic(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
';5�]' => self::deserialize_channelAdminLogEventActionPinTopic($stream),
'ümód' => [
'_' => 'channelAdminLogEventActionToggleAntiSpam',
'new_value' => match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) },
],
'€ç–W' => [
'_' => 'channelAdminLogEventActionChangePeerColor',
'prev_value' => match (stream_get_contents($stream, 4)) {
'ÏZKµ' => self::deserialize_peerColor($stream),
'¡Ïr0' => self::deserialize_type_PeerColor(self::gzdecode($stream)),
default => self::err($stream)
}
,
'new_value' => match (stream_get_contents($stream, 4)) {
'ÏZKµ' => self::deserialize_peerColor($stream),
'¡Ïr0' => self::deserialize_type_PeerColor(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'%{G^' => [
'_' => 'channelAdminLogEventActionChangeProfilePeerColor',
'prev_value' => match (stream_get_contents($stream, 4)) {
'ÏZKµ' => self::deserialize_peerColor($stream),
'¡Ïr0' => self::deserialize_type_PeerColor(self::gzdecode($stream)),
default => self::err($stream)
}
,
'new_value' => match (stream_get_contents($stream, 4)) {
'ÏZKµ' => self::deserialize_peerColor($stream),
'¡Ïr0' => self::deserialize_type_PeerColor(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'R]»1' => [
'_' => 'channelAdminLogEventActionChangeWallpaper',
'prev_value' => match (stream_get_contents($stream, 4)) {
'íÃ7¤' => self::deserialize_wallPaper($stream),
'A€à' => self::deserialize_wallPaperNoFile($stream),
'¡Ïr0' => self::deserialize_type_WallPaper(self::gzdecode($stream)),
default => self::err($stream)
}
,
'new_value' => match (stream_get_contents($stream, 4)) {
'íÃ7¤' => self::deserialize_wallPaper($stream),
'A€à' => self::deserialize_wallPaperNoFile($stream),
'¡Ïr0' => self::deserialize_type_WallPaper(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'±þ©>' => [
'_' => 'channelAdminLogEventActionChangeEmojiStatus',
'prev_value' => match (stream_get_contents($stream, 4)) {
'®á-' => [
'_' => 'emojiStatusEmpty',
],
'�a›’' => [
'_' => 'emojiStatus',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'Ç¨0ú' => [
'_' => 'emojiStatusUntil',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'until' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_EmojiStatus(self::gzdecode($stream)),
default => self::err($stream)
}
,
'new_value' => match (stream_get_contents($stream, 4)) {
'®á-' => [
'_' => 'emojiStatusEmpty',
],
'�a›’' => [
'_' => 'emojiStatus',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'Ç¨0ú' => [
'_' => 'emojiStatusUntil',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'until' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_EmojiStatus(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'«@ØF' => [
'_' => 'channelAdminLogEventActionChangeEmojiStickerSet',
'prev_stickerset' => match (stream_get_contents($stream, 4)) {
'•+¶ÿ' => [
'_' => 'inputStickerSetEmpty',
],
'i¢ç�' => [
'_' => 'inputStickerSetID',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
' È†' => [
'_' => 'inputStickerSetShortName',
'short_name' => self::deserialize_string($stream),
],
'È‡' => [
'_' => 'inputStickerSetAnimatedEmoji',
],
'Ræ' => [
'_' => 'inputStickerSetDice',
'emoticon' => self::deserialize_string($stream),
],
'97Þ' => [
'_' => 'inputStickerSetAnimatedEmojiAnimations',
],
';‹È' => [
'_' => 'inputStickerSetPremiumGifts',
],
'ÎÔÄ' => [
'_' => 'inputStickerSetEmojiGenericAnimations',
],
'îõÐ)' => [
'_' => 'inputStickerSetEmojiDefaultStatuses',
],
'éøÁD' => [
'_' => 'inputStickerSetEmojiDefaultTopicIcons',
],
'S…tI' => [
'_' => 'inputStickerSetEmojiChannelDefaultStatuses',
],
'¡Ïr0' => self::deserialize_type_InputStickerSet(self::gzdecode($stream)),
default => self::err($stream)
}
,
'new_stickerset' => match (stream_get_contents($stream, 4)) {
'•+¶ÿ' => [
'_' => 'inputStickerSetEmpty',
],
'i¢ç�' => [
'_' => 'inputStickerSetID',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
' È†' => [
'_' => 'inputStickerSetShortName',
'short_name' => self::deserialize_string($stream),
],
'È‡' => [
'_' => 'inputStickerSetAnimatedEmoji',
],
'Ræ' => [
'_' => 'inputStickerSetDice',
'emoticon' => self::deserialize_string($stream),
],
'97Þ' => [
'_' => 'inputStickerSetAnimatedEmojiAnimations',
],
';‹È' => [
'_' => 'inputStickerSetPremiumGifts',
],
'ÎÔÄ' => [
'_' => 'inputStickerSetEmojiGenericAnimations',
],
'îõÐ)' => [
'_' => 'inputStickerSetEmojiDefaultStatuses',
],
'éøÁD' => [
'_' => 'inputStickerSetEmojiDefaultTopicIcons',
],
'S…tI' => [
'_' => 'inputStickerSetEmojiChannelDefaultStatuses',
],
'¡Ïr0' => self::deserialize_type_InputStickerSet(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_ChannelAdminLogEventAction(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => $this->deserialize_type_ChannelAdminLogEvent(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_sponsoredWebPage(mixed $stream): mixed {
$tmp = ['_' => 'sponsoredWebPage'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['url'] = self::deserialize_string($stream);
$tmp['site_name'] = self::deserialize_string($stream);
if (($flags & 1) !== 0) $tmp['photo'] = match (stream_get_contents($stream, 4)) {
'-²1#' => [
'_' => 'photoEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ezû' => self::deserialize_photo($stream),
'¡Ïr0' => self::deserialize_type_Photo(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_type_SponsoredWebPage(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'cì¸=' => self::deserialize_sponsoredWebPage($stream),
'¡Ïr0' => $this->deserialize_type_SponsoredWebPage(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_sponsoredMessage(mixed $stream): mixed {
$tmp = ['_' => 'sponsoredMessage'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['recommended'] = ($flags & 32) !== 0;
$tmp['show_peer_photo'] = ($flags & 64) !== 0;
$tmp['random_id'] = self::deserialize_bytes($stream);
if (($flags & 8) !== 0) $tmp['from_id'] = match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 16) !== 0) $tmp['chat_invite'] = match (stream_get_contents($stream, 4)) {
'|mhZ' => [
'_' => 'chatInviteAlready',
'chat' => match (stream_get_contents($stream, 4)) {
'e(V)' => self::deserialize_chatEmpty($stream),
'VòËA' => self::deserialize_chat($stream),
'§¡’e' => self::deserialize_chatForbidden($stream),
'�ü­
' => self::deserialize_channel($stream),
'Õ“Ô' => self::deserialize_channelForbidden($stream),
'¡Ïr0' => self::deserialize_type_Chat(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'@ìàÍ' => self::deserialize_chatInvite($stream),
'°\\ia' => [
'_' => 'chatInvitePeek',
'chat' => match (stream_get_contents($stream, 4)) {
'e(V)' => self::deserialize_chatEmpty($stream),
'VòËA' => self::deserialize_chat($stream),
'§¡’e' => self::deserialize_chatForbidden($stream),
'�ü­
' => self::deserialize_channel($stream),
'Õ“Ô' => self::deserialize_channelForbidden($stream),
'¡Ïr0' => self::deserialize_type_Chat(self::gzdecode($stream)),
default => self::err($stream)
}
,
'expires' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_ChatInvite(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 16) !== 0) $tmp['chat_invite_hash'] = self::deserialize_string($stream);
if (($flags & 4) !== 0) $tmp['channel_post'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['start_param'] = self::deserialize_string($stream);
if (($flags & 512) !== 0) $tmp['webpage'] = match (stream_get_contents($stream, 4)) {
'cì¸=' => self::deserialize_sponsoredWebPage($stream),
'¡Ïr0' => self::deserialize_type_SponsoredWebPage(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 1024) !== 0) $tmp['app'] = match (stream_get_contents($stream, 4)) {
'·t¦]' => [
'_' => 'botAppNotModified',
],
'ÖÑü•' => self::deserialize_botApp($stream),
'¡Ïr0' => self::deserialize_type_BotApp(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['message'] = self::deserialize_string($stream);
if (($flags & 2) !== 0) $tmp['entities'] = self::deserialize_type_array_of_MessageEntity(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 2048) !== 0) $tmp['button_text'] = self::deserialize_string($stream);
if (($flags & 128) !== 0) $tmp['sponsor_info'] = self::deserialize_string($stream);
if (($flags & 256) !== 0) $tmp['additional_info'] = self::deserialize_string($stream);
return $tmp;

    }
    private  function deserialize_type_array_of_SponsoredMessage(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'÷ƒSí' => self::deserialize_sponsoredMessage($stream),
'¡Ïr0' => $this->deserialize_type_SponsoredMessage(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_messages___sponsoredMessages(mixed $stream): mixed {
$tmp = ['_' => 'messages.sponsoredMessages'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['posts_between'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['messages'] = self::deserialize_type_array_of_SponsoredMessage(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['chats'] = self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['users'] = self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
return $tmp;

    }
    private  function deserialize_sendAsPeer(mixed $stream): mixed {
$tmp = ['_' => 'sendAsPeer'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['premium_required'] = ($flags & 1) !== 0;
$tmp['peer'] = match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_type_array_of_SendAsPeer(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'4p¸' => self::deserialize_sendAsPeer($stream),
'¡Ïr0' => $this->deserialize_type_SendAsPeer(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_messages___forumTopics(mixed $stream): mixed {
$tmp = ['_' => 'messages.forumTopics'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['order_by_create_date'] = ($flags & 1) !== 0;
$tmp['count'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['topics'] = self::deserialize_type_array_of_ForumTopic(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['messages'] = self::deserialize_type_array_of_Message(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['chats'] = self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['users'] = self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['pts'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_array_of_PaymentFormMethod(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'òøˆ' => [
'_' => 'paymentFormMethod',
'url' => self::deserialize_string($stream),
'title' => self::deserialize_string($stream),
],
'¡Ïr0' => $this->deserialize_type_PaymentFormMethod(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_PaymentSavedCredentials(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'zÂÍ' => [
'_' => 'paymentSavedCredentialsCard',
'id' => self::deserialize_string($stream),
'title' => self::deserialize_string($stream),
],
'¡Ïr0' => $this->deserialize_type_PaymentSavedCredentials(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_payments___paymentForm(mixed $stream): mixed {
$tmp = ['_' => 'payments.paymentForm'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['can_save_credentials'] = ($flags & 4) !== 0;
$tmp['password_missing'] = ($flags & 8) !== 0;
$tmp['form_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['bot_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['title'] = self::deserialize_string($stream);
$tmp['description'] = self::deserialize_string($stream);
if (($flags & 32) !== 0) $tmp['photo'] = match (stream_get_contents($stream, 4)) {
'ÑW' => [
'_' => 'webDocument',
'url' => self::deserialize_string($stream),
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'attributes' => self::deserialize_type_array_of_DocumentAttribute(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'Æ¼Èù' => [
'_' => 'webDocumentNoProxy',
'url' => self::deserialize_string($stream),
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'attributes' => self::deserialize_type_array_of_DocumentAttribute(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => self::deserialize_type_WebDocument(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['invoice'] = match (stream_get_contents($stream, 4)) {
'Z¹]' => self::deserialize_invoice($stream),
'¡Ïr0' => self::deserialize_type_Invoice(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['provider_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['url'] = self::deserialize_string($stream);
if (($flags & 16) !== 0) $tmp['native_provider'] = self::deserialize_string($stream);
if (($flags & 16) !== 0) $tmp['native_params'] = match (stream_get_contents($stream, 4)) {
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'¡Ïr0' => self::deserialize_type_DataJSON(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 64) !== 0) $tmp['additional_methods'] = self::deserialize_type_array_of_PaymentFormMethod(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 1) !== 0) $tmp['saved_info'] = match (stream_get_contents($stream, 4)) {
'”?œ�' => self::deserialize_paymentRequestedInfo($stream),
'¡Ïr0' => self::deserialize_type_PaymentRequestedInfo(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 2) !== 0) $tmp['saved_credentials'] = self::deserialize_type_array_of_PaymentSavedCredentials(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['users'] = self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
return $tmp;

    }
    private  function deserialize_type_ShippingOption(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'ß<!¶' => [
'_' => 'shippingOption',
'id' => self::deserialize_string($stream),
'title' => self::deserialize_string($stream),
'prices' => self::deserialize_type_array_of_LabeledPrice(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => $this->deserialize_type_ShippingOption(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_payments___paymentReceipt(mixed $stream): mixed {
$tmp = ['_' => 'payments.paymentReceipt'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['date'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['bot_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['provider_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['title'] = self::deserialize_string($stream);
$tmp['description'] = self::deserialize_string($stream);
if (($flags & 4) !== 0) $tmp['photo'] = match (stream_get_contents($stream, 4)) {
'ÑW' => [
'_' => 'webDocument',
'url' => self::deserialize_string($stream),
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'attributes' => self::deserialize_type_array_of_DocumentAttribute(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'Æ¼Èù' => [
'_' => 'webDocumentNoProxy',
'url' => self::deserialize_string($stream),
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'attributes' => self::deserialize_type_array_of_DocumentAttribute(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => self::deserialize_type_WebDocument(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['invoice'] = match (stream_get_contents($stream, 4)) {
'Z¹]' => self::deserialize_invoice($stream),
'¡Ïr0' => self::deserialize_type_Invoice(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 1) !== 0) $tmp['info'] = match (stream_get_contents($stream, 4)) {
'”?œ�' => self::deserialize_paymentRequestedInfo($stream),
'¡Ïr0' => self::deserialize_type_PaymentRequestedInfo(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 2) !== 0) $tmp['shipping'] = match (stream_get_contents($stream, 4)) {
'ß<!¶' => [
'_' => 'shippingOption',
'id' => self::deserialize_string($stream),
'title' => self::deserialize_string($stream),
'prices' => self::deserialize_type_array_of_LabeledPrice(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => self::deserialize_type_ShippingOption(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 8) !== 0) $tmp['tip_amount'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['currency'] = self::deserialize_string($stream);
$tmp['total_amount'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['credentials_title'] = self::deserialize_string($stream);
$tmp['users'] = self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
return $tmp;

    }
    private  function deserialize_type_array_of_ShippingOption(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'ß<!¶' => [
'_' => 'shippingOption',
'id' => self::deserialize_string($stream),
'title' => self::deserialize_string($stream),
'prices' => self::deserialize_type_array_of_LabeledPrice(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => self::deserialize_type_ShippingOption(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_payments___validatedRequestedInfo(mixed $stream): mixed {
$tmp = ['_' => 'payments.validatedRequestedInfo'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['id'] = self::deserialize_string($stream);
if (($flags & 2) !== 0) $tmp['shipping_options'] = self::deserialize_type_array_of_ShippingOption(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
return $tmp;

    }
    private  function deserialize_type_Updates(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'~¯ã' => [
'_' => 'updatesTooLong',
],
'øÇ;1' => self::deserialize_updateShortMessage($stream),
'¥îmM' => self::deserialize_updateShortChatMessage($stream),
'ÁÞÔx' => [
'_' => 'updateShort',
'update' => match (stream_get_contents($stream, 4)) {
'ý
+' => [
'_' => 'updateNewMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Ö¿�N' => [
'_' => 'updateMessageID',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'random_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'å°¢' => [
'_' => 'updateDeleteMessages',
'messages' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'…À' => [
'_' => 'updateUserTyping',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'action' => match (stream_get_contents($stream, 4)) {
'Nt¿' => [
'_' => 'sendMessageTypingAction',
],
'õÈ^ý' => [
'_' => 'sendMessageCancelAction',
],
'oÖ‡¡' => [
'_' => 'sendMessageRecordVideoAction',
],
'ì:vé' => [
'_' => 'sendMessageUploadVideoAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'÷s/Õ' => [
'_' => 'sendMessageRecordAudioAction',
],
'«×Qó' => [
'_' => 'sendMessageUploadAudioAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'&JÓÑ' => [
'_' => 'sendMessageUploadPhotoAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'äÙª' => [
'_' => 'sendMessageUploadDocumentAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡‹o' => [
'_' => 'sendMessageGeoLocationAction',
],
'o¼Œb' => [
'_' => 'sendMessageChooseContactAction',
],
'H�jÝ' => [
'_' => 'sendMessageGamePlayAction',
],
'¼òˆ' => [
'_' => 'sendMessageRecordRoundAction',
],
'f>$' => [
'_' => 'sendMessageUploadRoundAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'…",Ù' => [
'_' => 'speakingInGroupCallAction',
],
'F’ÚÛ' => [
'_' => 'sendMessageHistoryImportAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'±ÆZ°' => [
'_' => 'sendMessageChooseStickerAction',
],
'Ë+—%' => [
'_' => 'sendMessageEmojiInteraction',
'emoticon' => self::deserialize_string($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'interaction' => match (stream_get_contents($stream, 4)) {
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'¡Ïr0' => self::deserialize_type_DataJSON(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'.�e¶' => [
'_' => 'sendMessageEmojiInteractionSeen',
'emoticon' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_SendMessageAction(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ðzHƒ' => [
'_' => 'updateChatUserTyping',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'from_id' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'action' => match (stream_get_contents($stream, 4)) {
'Nt¿' => [
'_' => 'sendMessageTypingAction',
],
'õÈ^ý' => [
'_' => 'sendMessageCancelAction',
],
'oÖ‡¡' => [
'_' => 'sendMessageRecordVideoAction',
],
'ì:vé' => [
'_' => 'sendMessageUploadVideoAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'÷s/Õ' => [
'_' => 'sendMessageRecordAudioAction',
],
'«×Qó' => [
'_' => 'sendMessageUploadAudioAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'&JÓÑ' => [
'_' => 'sendMessageUploadPhotoAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'äÙª' => [
'_' => 'sendMessageUploadDocumentAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡‹o' => [
'_' => 'sendMessageGeoLocationAction',
],
'o¼Œb' => [
'_' => 'sendMessageChooseContactAction',
],
'H�jÝ' => [
'_' => 'sendMessageGamePlayAction',
],
'¼òˆ' => [
'_' => 'sendMessageRecordRoundAction',
],
'f>$' => [
'_' => 'sendMessageUploadRoundAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'…",Ù' => [
'_' => 'speakingInGroupCallAction',
],
'F’ÚÛ' => [
'_' => 'sendMessageHistoryImportAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'±ÆZ°' => [
'_' => 'sendMessageChooseStickerAction',
],
'Ë+—%' => [
'_' => 'sendMessageEmojiInteraction',
'emoticon' => self::deserialize_string($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'interaction' => match (stream_get_contents($stream, 4)) {
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'¡Ïr0' => self::deserialize_type_DataJSON(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'.�e¶' => [
'_' => 'sendMessageEmojiInteractionSeen',
'emoticon' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_SendMessageAction(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'˜v' => [
'_' => 'updateChatParticipants',
'participants' => match (stream_get_contents($stream, 4)) {
'áÓc‡' => self::deserialize_chatParticipantsForbidden($stream),
'ø“¼<' => [
'_' => 'chatParticipants',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'participants' => self::deserialize_type_array_of_ChatParticipant(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_ChatParticipants(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Þø½å' => [
'_' => 'updateUserStatus',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'status' => match (stream_get_contents($stream, 4)) {
'IPÐ	' => [
'_' => 'userStatusEmpty',
],
'I9¹í' => [
'_' => 'userStatusOnline',
'expires' => unpack('l', stream_get_contents($stream, 4))[1],
],
'?pŒ' . "\0" . '' => [
'_' => 'userStatusOffline',
'was_online' => unpack('l', stream_get_contents($stream, 4))[1],
],
'È}{' => self::deserialize_userStatusRecently($stream),
'T' => self::deserialize_userStatusLastWeek($stream),
'w—‰e' => self::deserialize_userStatusLastMonth($stream),
'¡Ïr0' => self::deserialize_type_UserStatus(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'$‰„§' => [
'_' => 'updateUserName',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'first_name' => self::deserialize_string($stream),
'last_name' => self::deserialize_string($stream),
'usernames' => self::deserialize_type_array_of_Username(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ï«Q‰' => self::deserialize_updateNewAuthorization($stream),
'š½¼' => [
'_' => 'updateNewEncryptedMessage',
'message' => match (stream_get_contents($stream, 4)) {
'Áí' => [
'_' => 'encryptedMessage',
'random_id' => unpack('q', stream_get_contents($stream, 8))[1],
'chat_id' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'bytes' => self::deserialize_bytes($stream),
'file' => match (stream_get_contents($stream, 4)) {
'~IÂ' => [
'_' => 'encryptedFileEmpty',
],
'ØŒ' . "\0" . '¨' => [
'_' => 'encryptedFile',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'size' => unpack('q', stream_get_contents($stream, 8))[1],
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'key_fingerprint' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_EncryptedFile(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Ks#' => [
'_' => 'encryptedMessageService',
'random_id' => unpack('q', stream_get_contents($stream, 8))[1],
'chat_id' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'bytes' => self::deserialize_bytes($stream),
],
'¡Ïr0' => self::deserialize_type_EncryptedMessage(self::gzdecode($stream)),
default => self::err($stream)
}
,
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Vñ' => [
'_' => 'updateEncryptedChatTyping',
'chat_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'�è¢´' => [
'_' => 'updateEncryption',
'chat' => match (stream_get_contents($stream, 4)) {
' À~«' => [
'_' => 'encryptedChatEmpty',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'SY²f' => [
'_' => 'encryptedChatWaiting',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'admin_id' => unpack('q', stream_get_contents($stream, 8))[1],
'participant_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'LÙñH' => self::deserialize_encryptedChatRequested($stream),
'ÇÔða' => [
'_' => 'encryptedChat',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'admin_id' => unpack('q', stream_get_contents($stream, 8))[1],
'participant_id' => unpack('q', stream_get_contents($stream, 8))[1],
'g_a_or_b' => self::deserialize_bytes($stream),
'key_fingerprint' => stream_get_contents($stream, 8),
],
'E|' => self::deserialize_encryptedChatDiscarded($stream),
'¡Ïr0' => self::deserialize_type_EncryptedChat(self::gzdecode($stream)),
default => self::err($stream)
}
,
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'·%þ8' => [
'_' => 'updateEncryptedMessagesRead',
'chat_id' => unpack('l', stream_get_contents($stream, 4))[1],
'max_date' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'QTÚ=' => [
'_' => 'updateChatParticipantAdd',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'inviter_id' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
'w=/ã' => [
'_' => 'updateChatParticipantDelete',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
's˜^Ž' => [
'_' => 'updateDcOptions',
'dc_options' => self::deserialize_type_array_of_DcOption(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ïhÂ¾' => [
'_' => 'updateNotifySettings',
'peer' => match (stream_get_contents($stream, 4)) {
'ØÔŸ' => [
'_' => 'notifyPeer',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'L;È´' => [
'_' => 'notifyUsers',
],
'ÃÎÀ' => [
'_' => 'notifyChats',
],
'ïèÖ' => [
'_' => 'notifyBroadcasts',
],
'cn"' => [
'_' => 'notifyForumTopic',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'top_msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_NotifyPeer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'notify_settings' => match (stream_get_contents($stream, 4)) {
',b™' => self::deserialize_peerNotifySettings($stream),
'¡Ïr0' => self::deserialize_type_PeerNotifySettings(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'häë' => self::deserialize_updateServiceNotification($stream),
'*\';î' => [
'_' => 'updatePrivacy',
'key' => match (stream_get_contents($stream, 4)) {
'0«.¼' => [
'_' => 'privacyKeyStatusTimestamp',
],
'úmP' => [
'_' => 'privacyKeyChatInvite',
],
'{+f=' => [
'_' => 'privacyKeyPhoneCall',
],
'ÈI9' => [
'_' => 'privacyKeyPhoneP2P',
],
'£Vìi' => [
'_' => 'privacyKeyForwards',
],
'í–' => [
'_' => 'privacyKeyProfilePhoto',
],
'mäšÑ' => [
'_' => 'privacyKeyPhoneNumber',
],
'+ÔÿB' => [
'_' => 'privacyKeyAddedByPhone',
],
'ô—' => [
'_' => 'privacyKeyVoiceMessages',
],
'a·†¤' => [
'_' => 'privacyKeyAbout',
],
'¡Ïr0' => self::deserialize_type_PrivacyKey(self::gzdecode($stream)),
default => self::err($stream)
}
,
'rules' => self::deserialize_type_array_of_PrivacyRule(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'*I' => [
'_' => 'updateUserPhone',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'phone' => self::deserialize_string($stream),
],
'ßO—œ' => self::deserialize_updateReadHistoryInbox($stream),
'¿!//' => [
'_' => 'updateReadHistoryOutbox',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'max_id' => unpack('l', stream_get_contents($stream, 4))[1],
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'‰' => [
'_' => 'updateWebPage',
'webpage' => match (stream_get_contents($stream, 4)) {
'ˆ!' => self::deserialize_webPageEmpty($stream),
'G>Ñ°' => self::deserialize_webPagePending($stream),
'²Eœè' => self::deserialize_webPage($stream),
'Ês' => self::deserialize_webPageNotModified($stream),
'¡Ïr0' => self::deserialize_type_WebPage(self::gzdecode($stream)),
default => self::err($stream)
}
,
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'�q"ø' => self::deserialize_updateReadMessagesContents($stream),
'”�' => self::deserialize_updateChannelTooLong($stream),
'	L[c' => [
'_' => 'updateChannel',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'Ùºb' => [
'_' => 'updateNewChannelMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'n.’' => self::deserialize_updateReadChannelInbox($stream),
'[-Ã' => [
'_' => 'updateDeleteChannelMessages',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'messages' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¬&ò' => [
'_' => 'updateChannelMessageViews',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'views' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¢aÊ×' => [
'_' => 'updateChatParticipantAdmin',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'is_admin' => match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) },
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
'ª0Šh' => [
'_' => 'updateNewStickerSet',
'stickerset' => match (stream_get_contents($stream, 4)) {
'?n' => self::deserialize_messages___stickerSet($stream),
'ë$ùÓ' => [
'_' => 'messages.stickerSetNotModified',
],
'¡Ïr0' => self::deserialize_type_messages___StickerSet(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Ò²' => self::deserialize_updateStickerSetsOrder($stream),
'HÂ1' => self::deserialize_updateStickerSets($stream),
'4u“' => [
'_' => 'updateSavedGifs',
],
'œ7oI' => self::deserialize_updateBotInlineQuery($stream),
'*ñ' => self::deserialize_updateBotInlineSend($stream),
'÷M?' => [
'_' => 'updateEditChannelMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'�ÄÏ¹' => self::deserialize_updateBotCallbackQuery($stream),
'£pä' => [
'_' => 'updateEditMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'R�i' => self::deserialize_updateInlineBotCallbackQuery($stream),
'©™_·' => [
'_' => 'updateReadChannelOutbox',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'max_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'mìI' => self::deserialize_updateDraftMessage($stream),
'B\'W' => [
'_' => 'updateReadFeaturedStickers',
],
' ,Bš' => [
'_' => 'updateRecentStickers',
],
'Ý)¢' => [
'_' => 'updateConfig',
],
'�gT3' => [
'_' => 'updatePtsChanged',
],
'Ÿ©+/' => [
'_' => 'updateChannelWebPage',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'webpage' => match (stream_get_contents($stream, 4)) {
'ˆ!' => self::deserialize_webPageEmpty($stream),
'G>Ñ°' => self::deserialize_webPagePending($stream),
'²Eœè' => self::deserialize_webPage($stream),
'Ês' => self::deserialize_webPageNotModified($stream),
'¡Ïr0' => self::deserialize_type_WebPage(self::gzdecode($stream)),
default => self::err($stream)
}
,
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'åon' => self::deserialize_updateDialogPinned($stream),
'¢<ú' => self::deserialize_updatePinnedDialogs($stream),
'ÃÀƒ' => [
'_' => 'updateBotWebhookJSON',
'data' => match (stream_get_contents($stream, 4)) {
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'¡Ïr0' => self::deserialize_type_DataJSON(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¦@’›' => [
'_' => 'updateBotWebhookJSONQuery',
'query_id' => unpack('q', stream_get_contents($stream, 8))[1],
'data' => match (stream_get_contents($stream, 4)) {
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'¡Ïr0' => self::deserialize_type_DataJSON(self::gzdecode($stream)),
default => self::err($stream)
}
,
'timeout' => unpack('l', stream_get_contents($stream, 4))[1],
],
'}ý®µ' => [
'_' => 'updateBotShippingQuery',
'query_id' => unpack('q', stream_get_contents($stream, 8))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'payload' => self::deserialize_bytes($stream),
'shipping_address' => match (stream_get_contents($stream, 4)) {
'ëªŒ' => [
'_' => 'postAddress',
'street_line1' => self::deserialize_string($stream),
'street_line2' => self::deserialize_string($stream),
'city' => self::deserialize_string($stream),
'state' => self::deserialize_string($stream),
'country_iso2' => self::deserialize_string($stream),
'post_code' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_PostAddress(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'–šªŒ' => self::deserialize_updateBotPrecheckoutQuery($stream),
'k«' => [
'_' => 'updatePhoneCall',
'phone_call' => match (stream_get_contents($stream, 4)) {
'ÉfS' => [
'_' => 'phoneCallEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'o"Å' => self::deserialize_phoneCallWaiting($stream),
'í°' => self::deserialize_phoneCallRequested($stream),
'Ã`6' => self::deserialize_phoneCallAccepted($stream),
'g|–' => self::deserialize_phoneCall($stream),
'áMÊP' => self::deserialize_phoneCallDiscarded($stream),
'¡Ïr0' => self::deserialize_type_PhoneCall(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'dVF' => [
'_' => 'updateLangPackTooLong',
'lang_code' => self::deserialize_string($stream),
],
'M/V' => [
'_' => 'updateLangPack',
'difference' => match (stream_get_contents($stream, 4)) {
'öÁ…ó' => [
'_' => 'langPackDifference',
'lang_code' => self::deserialize_string($stream),
'from_version' => unpack('l', stream_get_contents($stream, 4))[1],
'version' => unpack('l', stream_get_contents($stream, 4))[1],
'strings' => self::deserialize_type_array_of_LangPackString(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => self::deserialize_type_LangPackDifference(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'm™å' => [
'_' => 'updateFavedStickers',
],
'])ê' => self::deserialize_updateChannelReadMessagesContents($stream),
'¾§„p' => [
'_' => 'updateContactsReset',
],
'˜Æ?²' => [
'_' => 'updateChannelAvailableMessages',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'available_min_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'ÃYdá' => self::deserialize_updateDialogUnreadMark($stream),
'{e¡¬' => self::deserialize_updateMessagePoll($stream),
'PÀT' => [
'_' => 'updateChatDefaultBannedRights',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'default_banned_rights' => match (stream_get_contents($stream, 4)) {
'Ÿ' => self::deserialize_chatBannedRights($stream),
'¡Ïr0' => self::deserialize_type_ChatBannedRights(self::gzdecode($stream)),
default => self::err($stream)
}
,
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
'À6' => [
'_' => 'updateFolderPeers',
'folder_peers' => self::deserialize_type_array_of_FolderPeer(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'fs~j' => [
'_' => 'updatePeerSettings',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'settings' => match (stream_get_contents($stream, 4)) {
'¥' => self::deserialize_peerSettings($stream),
'¡Ïr0' => self::deserialize_type_PeerSettings(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'°Ï¯´' => [
'_' => 'updatePeerLocated',
'peers' => self::deserialize_type_array_of_PeerLocated(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'û¥9' => [
'_' => 'updateNewScheduledMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'îl†�' => [
'_' => 'updateDeleteScheduledMessages',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'messages' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'£û‚' => [
'_' => 'updateTheme',
'theme' => match (stream_get_contents($stream, 4)) {
'Ög ' => self::deserialize_theme($stream),
'¡Ïr0' => self::deserialize_type_Theme(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'9¹‡' => [
'_' => 'updateGeoLiveViewed',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'‘æOV' => [
'_' => 'updateLoginToken',
],
'wô$' => [
'_' => 'updateMessagePollVote',
'poll_id' => unpack('q', stream_get_contents($stream, 8))[1],
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'options' => self::deserialize_type_array_of_bytes(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'}Þÿ&' => self::deserialize_updateDialogFilter($stream),
'!×¥' => [
'_' => 'updateDialogFilterOrder',
'order' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'O‘5' => [
'_' => 'updateDialogFilters',
],
'	¿a&' => [
'_' => 'updatePhoneCallSignalingData',
'phone_call_id' => unpack('q', stream_get_contents($stream, 8))[1],
'data' => self::deserialize_bytes($stream),
],
'ô\'šÒ' => [
'_' => 'updateChannelMessageForwards',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'forwards' => unpack('l', stream_get_contents($stream, 4))[1],
],
'F•±Ö' => self::deserialize_updateReadChannelDiscussionInbox($stream),
'|ž\\i' => [
'_' => 'updateReadChannelDiscussionOutbox',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'top_msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'read_max_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Rwàë' => self::deserialize_updatePeerBlocked($stream),
'#ÉˆŒ' => self::deserialize_updateChannelUserTyping($stream),
'µê…í' => self::deserialize_updatePinnedMessages($stream),
'†¹[' => self::deserialize_updatePinnedChannelMessages($stream),
'Njšø' => [
'_' => 'updateChat',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'NÛëò' => [
'_' => 'updateGroupCallParticipants',
'call' => match (stream_get_contents($stream, 4)) {
'„ªØ' => [
'_' => 'inputGroupCall',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputGroupCall(self::gzdecode($stream)),
default => self::err($stream)
}
,
'participants' => self::deserialize_type_array_of_GroupCallParticipant(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
'' . "\0" . 'E²' => [
'_' => 'updateGroupCall',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'call' => match (stream_get_contents($stream, 4)) {
'´¼€w' => [
'_' => 'groupCallDiscarded',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'duration' => unpack('l', stream_get_contents($stream, 4))[1],
],
'e—Õ' => self::deserialize_groupCall($stream),
'¡Ïr0' => self::deserialize_type_GroupCall(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¥¹›»' => self::deserialize_updatePeerHistoryTTL($stream),
':f‡Ð' => self::deserialize_updateChatParticipant($stream),
'»:]˜' => self::deserialize_updateChannelParticipant($stream),
'I
‡Ä' => [
'_' => 'updateBotStopped',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'stopped' => match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) },
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'‚9x' => self::deserialize_updateGroupCallConnection($stream),
'./qM' => [
'_' => 'updateBotCommands',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'bot_id' => unpack('q', stream_get_contents($stream, 8))[1],
'commands' => self::deserialize_type_array_of_BotCommand(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ÛÃcp' => [
'_' => 'updatePendingJoinRequests',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'requests_pending' => unpack('l', stream_get_contents($stream, 4))[1],
'recent_requesters' => self::deserialize_type_array_of_long(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'†©ß' => [
'_' => 'updateBotChatInviteRequester',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'about' => self::deserialize_string($stream),
'invite' => match (stream_get_contents($stream, 4)) {
'¨´
' => self::deserialize_chatInviteExported($stream),
'·zí' => [
'_' => 'chatInvitePublicJoinRequests',
],
'¡Ïr0' => self::deserialize_type_ExportedChatInvite(self::gzdecode($stream)),
default => self::err($stream)
}
,
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¸<^' => self::deserialize_updateMessageReactions($stream),
'¢·' => [
'_' => 'updateAttachMenuBots',
],
'�·’' => [
'_' => 'updateWebViewResultSent',
'query_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'X¸' => [
'_' => 'updateBotMenuButton',
'bot_id' => unpack('q', stream_get_contents($stream, 8))[1],
'button' => match (stream_get_contents($stream, 4)) {
'ˆ¥3u' => [
'_' => 'botMenuButtonDefault',
],
'ÂXB' => [
'_' => 'botMenuButtonCommands',
],
'æ|µÇ' => [
'_' => 'botMenuButton',
'text' => self::deserialize_string($stream),
'url' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_BotMenuButton(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'™¾Øt' => [
'_' => 'updateSavedRingtones',
],
'ZÍ„' . "\0" . '' => self::deserialize_updateTranscribedAudio($stream),
'lILû' => [
'_' => 'updateReadFeaturedEmojiStickers',
],
'™57(' => [
'_' => 'updateUserEmojiStatus',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'emoji_status' => match (stream_get_contents($stream, 4)) {
'®á-' => [
'_' => 'emojiStatusEmpty',
],
'�a›’' => [
'_' => 'emojiStatus',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'Ç¨0ú' => [
'_' => 'emojiStatusUntil',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'until' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_EmojiStatus(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ÛCô0' => [
'_' => 'updateRecentEmojiStatuses',
],
'ôcxo' => [
'_' => 'updateRecentReactions',
],
'…Ïü†' => self::deserialize_updateMoveStickerSetToTop($stream),
'Œ©sZ' => [
'_' => 'updateMessageExtendedMedia',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'extended_media' => match (stream_get_contents($stream, 4)) {
'ÈŒb­' => self::deserialize_messageExtendedMediaPreview($stream),
'dœGî' => [
'_' => 'messageExtendedMedia',
'media' => match (stream_get_contents($stream, 4)) {
' cí=' => [
'_' => 'messageMediaEmpty',
],
'×PQi' => self::deserialize_messageMediaPhoto($stream),
'tÔàV' => [
'_' => 'messageMediaGeo',
'geo' => match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => self::deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'I)2p' => [
'_' => 'messageMediaContact',
'phone_number' => self::deserialize_string($stream),
'first_name' => self::deserialize_string($stream),
'last_name' => self::deserialize_string($stream),
'vcard' => self::deserialize_string($stream),
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'žô„Ÿ' => [
'_' => 'messageMediaUnsupported',
],
'-×ôL' => self::deserialize_messageMediaDocument($stream),
';ñÝ' => self::deserialize_messageMediaWebPage($stream),
'?SÀ.' => [
'_' => 'messageMediaVenue',
'geo' => match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => self::deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
,
'title' => self::deserialize_string($stream),
'address' => self::deserialize_string($stream),
'provider' => self::deserialize_string($stream),
'venue_id' => self::deserialize_string($stream),
'venue_type' => self::deserialize_string($stream),
],
'�±ý' => [
'_' => 'messageMediaGame',
'game' => match (stream_get_contents($stream, 4)) {
';eù½' => self::deserialize_game($stream),
'¡Ïr0' => self::deserialize_type_Game(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ÓH¥ö' => self::deserialize_messageMediaInvoice($stream),
'fÆ@¹' => self::deserialize_messageMediaGeoLive($stream),
'˜çÖK' => [
'_' => 'messageMediaPoll',
'poll' => match (stream_get_contents($stream, 4)) {
'a�á†' => self::deserialize_poll($stream),
'¡Ïr0' => self::deserialize_type_Poll(self::gzdecode($stream)),
default => self::err($stream)
}
,
'results' => match (stream_get_contents($stream, 4)) {
' $ßz' => self::deserialize_pollResults($stream),
'¡Ïr0' => self::deserialize_type_PollResults(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'‹å~?' => [
'_' => 'messageMediaDice',
'value' => unpack('l', stream_get_contents($stream, 4))[1],
'emoticon' => self::deserialize_string($stream),
],
'ƒbËh' => self::deserialize_messageMediaStory($stream),
'°…­Ú' => self::deserialize_messageMediaGiveaway($stream),
'h™Æ' => self::deserialize_messageMediaGiveawayResults($stream),
'¡Ïr0' => self::deserialize_type_MessageMedia(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_MessageExtendedMedia(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ãû.' => self::deserialize_updateChannelPinnedTopic($stream),
'†þ' => self::deserialize_updateChannelPinnedTopics($stream),
'8”R ' => [
'_' => 'updateUser',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'—°ì' => [
'_' => 'updateAutoSaveSettings',
],
'ÖŠðÌ' => [
'_' => 'updateGroupInvitePrivacyForbidden',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'˜·³u' => [
'_' => 'updateStory',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'story' => match (stream_get_contents($stream, 4)) {
'OîæQ' => [
'_' => 'storyItemDeleted',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'É­ÿ' => self::deserialize_storyItemSkipped($stream),
'$j²y' => self::deserialize_storyItem($stream),
'¡Ïr0' => self::deserialize_type_StoryItem(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'+“N÷' => [
'_' => 'updateReadStories',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'max_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¹5ó' => [
'_' => 'updateStoryID',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'random_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÁM,' => [
'_' => 'updateStoriesStealthMode',
'stealth_mode' => match (stream_get_contents($stream, 4)) {
'ý\'.q' => self::deserialize_storiesStealthMode($stream),
'¡Ïr0' => self::deserialize_type_StoriesStealthMode(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ƒvb}' => [
'_' => 'updateSentStoryReaction',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'story_id' => unpack('l', stream_get_contents($stream, 4))[1],
'reaction' => match (stream_get_contents($stream, 4)) {
'Ôõy' => [
'_' => 'reactionEmpty',
],
'¸†"' => [
'_' => 'reactionEmoji',
'emoticon' => self::deserialize_string($stream),
],
'sü5‰' => [
'_' => 'reactionCustomEmoji',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_Reaction(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'œÔM�' => [
'_' => 'updateBotChatBoost',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'boost' => match (stream_get_contents($stream, 4)) {
'qŒ*' => self::deserialize_boost($stream),
'¡Ïr0' => self::deserialize_type_Boost(self::gzdecode($stream)),
default => self::err($stream)
}
,
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
' ‰¶' => [
'_' => 'updateChannelViewForumAsMessages',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'enabled' => match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) },
],
'?®' => self::deserialize_updatePeerWallpaper($stream),
'ÎÓ!¬' => [
'_' => 'updateBotMessageReaction',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'actor' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'old_reactions' => self::deserialize_type_array_of_Reaction(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'new_reactions' => self::deserialize_type_array_of_Reaction(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'YwË	' => [
'_' => 'updateBotMessageReactions',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'reactions' => self::deserialize_type_array_of_ReactionCount(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'tž¯®' => self::deserialize_updateSavedDialogPinned($stream),
'¦…lh' => self::deserialize_updatePinnedSavedDialogs($stream),
'2tÆ9' => [
'_' => 'updateSavedReactionTags',
],
'Ôibñ' => [
'_' => 'updateSmsJob',
'job_id' => self::deserialize_string($stream),
],
'²
Gù' => [
'_' => 'updateQuickReplies',
'quick_replies' => self::deserialize_type_array_of_QuickReply(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'§=õ' => [
'_' => 'updateNewQuickReply',
'quick_reply' => match (stream_get_contents($stream, 4)) {
'+—' => [
'_' => 'quickReply',
'shortcut_id' => unpack('l', stream_get_contents($stream, 4))[1],
'shortcut' => self::deserialize_string($stream),
'top_message' => unpack('l', stream_get_contents($stream, 4))[1],
'count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_QuickReply(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ìñæS' => [
'_' => 'updateDeleteQuickReply',
'shortcut_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'>' => [
'_' => 'updateQuickReplyMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ÍçoV' => [
'_' => 'updateDeleteQuickReplyMessages',
'shortcut_id' => unpack('l', stream_get_contents($stream, 4))[1],
'messages' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => self::deserialize_type_Update(self::gzdecode($stream)),
default => self::err($stream)
}
,
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Ã[r' => [
'_' => 'updatesCombined',
'updates' => self::deserialize_type_array_of_Update(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'seq_start' => unpack('l', stream_get_contents($stream, 4))[1],
'seq' => unpack('l', stream_get_contents($stream, 4))[1],
],
'@B®t' => [
'_' => 'updates',
'updates' => self::deserialize_type_array_of_Update(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'seq' => unpack('l', stream_get_contents($stream, 4))[1],
],
'á�' => self::deserialize_updateShortSentMessage($stream),
'¡Ïr0' => $this->deserialize_type_Updates(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_payments___savedInfo(mixed $stream): mixed {
$tmp = ['_' => 'payments.savedInfo'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['has_saved_credentials'] = ($flags & 2) !== 0;
if (($flags & 1) !== 0) $tmp['saved_info'] = match (stream_get_contents($stream, 4)) {
'”?œ�' => self::deserialize_paymentRequestedInfo($stream),
'¡Ïr0' => self::deserialize_type_PaymentRequestedInfo(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_type_array_of_BankCardOpenUrl(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'Šhõ' => [
'_' => 'bankCardOpenUrl',
'url' => self::deserialize_string($stream),
'name' => self::deserialize_string($stream),
],
'¡Ïr0' => $this->deserialize_type_BankCardOpenUrl(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_payments___checkedGiftCode(mixed $stream): mixed {
$tmp = ['_' => 'payments.checkedGiftCode'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['via_giveaway'] = ($flags & 4) !== 0;
if (($flags & 16) !== 0) $tmp['from_id'] = match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 8) !== 0) $tmp['giveaway_msg_id'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['to_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['date'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['months'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 2) !== 0) $tmp['used_date'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['chats'] = self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['users'] = self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
return $tmp;

    }
    private  function deserialize_payments___giveawayInfo(mixed $stream): mixed {
$tmp = ['_' => 'payments.giveawayInfo'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['participating'] = ($flags & 1) !== 0;
$tmp['preparing_results'] = ($flags & 8) !== 0;
$tmp['start_date'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 2) !== 0) $tmp['joined_too_early_date'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 4) !== 0) $tmp['admin_disallowed_chat_id'] = unpack('q', stream_get_contents($stream, 8))[1];
if (($flags & 16) !== 0) $tmp['disallowed_country'] = self::deserialize_string($stream);
return $tmp;

    }
    private  function deserialize_payments___giveawayInfoResults(mixed $stream): mixed {
$tmp = ['_' => 'payments.giveawayInfoResults'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['winner'] = ($flags & 1) !== 0;
$tmp['refunded'] = ($flags & 2) !== 0;
$tmp['start_date'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['gift_code_slug'] = self::deserialize_string($stream);
$tmp['finish_date'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['winners_count'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['activated_count'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_array_of_GroupCallStreamChannel(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'¯Hë€' => [
'_' => 'groupCallStreamChannel',
'channel' => unpack('l', stream_get_contents($stream, 4))[1],
'scale' => unpack('l', stream_get_contents($stream, 4))[1],
'last_timestamp_ms' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => $this->deserialize_type_GroupCallStreamChannel(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_langPackLanguage(mixed $stream): mixed {
$tmp = ['_' => 'langPackLanguage'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['official'] = ($flags & 1) !== 0;
$tmp['rtl'] = ($flags & 4) !== 0;
$tmp['beta'] = ($flags & 8) !== 0;
$tmp['name'] = self::deserialize_string($stream);
$tmp['native_name'] = self::deserialize_string($stream);
$tmp['lang_code'] = self::deserialize_string($stream);
if (($flags & 2) !== 0) $tmp['base_lang_code'] = self::deserialize_string($stream);
$tmp['plural_code'] = self::deserialize_string($stream);
$tmp['strings_count'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['translated_count'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['translations_url'] = self::deserialize_string($stream);
return $tmp;

    }
    private  function deserialize_type_StatsDateRangeDays(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'¯í7¶' => [
'_' => 'statsDateRangeDays',
'min_date' => unpack('l', stream_get_contents($stream, 4))[1],
'max_date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => $this->deserialize_type_StatsDateRangeDays(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_StatsAbsValueAndPrev(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'Þ¬CË' => [
'_' => 'statsAbsValueAndPrev',
'current' => unpack('d', stream_get_contents($stream, 8))[1],
'previous' => unpack('d', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => $this->deserialize_type_StatsAbsValueAndPrev(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_StatsPercentValue(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'à/ÎË' => [
'_' => 'statsPercentValue',
'part' => unpack('d', stream_get_contents($stream, 8))[1],
'total' => unpack('d', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => $this->deserialize_type_StatsPercentValue(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_statsGraph(mixed $stream): mixed {
$tmp = ['_' => 'statsGraph'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['json'] = match (stream_get_contents($stream, 4)) {
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'¡Ïr0' => self::deserialize_type_DataJSON(self::gzdecode($stream)),
default => self::err($stream)
}
;
if (($flags & 1) !== 0) $tmp['zoom_token'] = self::deserialize_string($stream);
return $tmp;

    }
    private  function deserialize_type_StatsGraph(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => $this->deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_array_of_PostInteractionCounters(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'Žç' => [
'_' => 'postInteractionCountersMessage',
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'views' => unpack('l', stream_get_contents($stream, 4))[1],
'forwards' => unpack('l', stream_get_contents($stream, 4))[1],
'reactions' => unpack('l', stream_get_contents($stream, 4))[1],
],
'\'HŠ' => [
'_' => 'postInteractionCountersStory',
'story_id' => unpack('l', stream_get_contents($stream, 4))[1],
'views' => unpack('l', stream_get_contents($stream, 4))[1],
'forwards' => unpack('l', stream_get_contents($stream, 4))[1],
'reactions' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => $this->deserialize_type_PostInteractionCounters(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_StatsGroupTopPoster(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'›¯�' => [
'_' => 'statsGroupTopPoster',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'messages' => unpack('l', stream_get_contents($stream, 4))[1],
'avg_chars' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => $this->deserialize_type_StatsGroupTopPoster(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_StatsGroupTopAdmin(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'‡LX×' => [
'_' => 'statsGroupTopAdmin',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'deleted' => unpack('l', stream_get_contents($stream, 4))[1],
'kicked' => unpack('l', stream_get_contents($stream, 4))[1],
'banned' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => $this->deserialize_type_StatsGroupTopAdmin(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_StatsGroupTopInviter(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'�w_S' => [
'_' => 'statsGroupTopInviter',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'invitations' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => $this->deserialize_type_StatsGroupTopInviter(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_PublicForward(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'J¿ò' => [
'_' => 'publicForwardMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Ð­óí' => [
'_' => 'publicForwardStory',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'story' => match (stream_get_contents($stream, 4)) {
'OîæQ' => [
'_' => 'storyItemDeleted',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'É­ÿ' => self::deserialize_storyItemSkipped($stream),
'$j²y' => self::deserialize_storyItem($stream),
'¡Ïr0' => self::deserialize_type_StoryItem(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => $this->deserialize_type_PublicForward(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_stats___publicForwards(mixed $stream): mixed {
$tmp = ['_' => 'stats.publicForwards'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['count'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['forwards'] = self::deserialize_type_array_of_PublicForward(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 1) !== 0) $tmp['next_offset'] = self::deserialize_string($stream);
$tmp['chats'] = self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['users'] = self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
return $tmp;

    }
    private  function deserialize_type_ExportedChatlistInvite(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'¬�Q' => [
'_' => 'exportedChatlistInvite',
'flags' => unpack('V', stream_get_contents($stream, 4))[1],
'title' => self::deserialize_string($stream),
'url' => self::deserialize_string($stream),
'peers' => self::deserialize_type_array_of_Peer(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => $this->deserialize_type_ExportedChatlistInvite(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_type_array_of_ExportedChatlistInvite(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'¬�Q' => [
'_' => 'exportedChatlistInvite',
'flags' => unpack('V', stream_get_contents($stream, 4))[1],
'title' => self::deserialize_string($stream),
'url' => self::deserialize_string($stream),
'peers' => self::deserialize_type_array_of_Peer(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => self::deserialize_type_ExportedChatlistInvite(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_chatlists___chatlistInvite(mixed $stream): mixed {
$tmp = ['_' => 'chatlists.chatlistInvite'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['title'] = self::deserialize_string($stream);
if (($flags & 1) !== 0) $tmp['emoticon'] = self::deserialize_string($stream);
$tmp['peers'] = self::deserialize_type_array_of_Peer(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['chats'] = self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['users'] = self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
return $tmp;

    }
    private  function deserialize_type_array_of_PeerStories(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'™é5š' => self::deserialize_peerStories($stream),
'¡Ïr0' => self::deserialize_type_PeerStories(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_stories___allStories(mixed $stream): mixed {
$tmp = ['_' => 'stories.allStories'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['has_more'] = ($flags & 1) !== 0;
$tmp['count'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['state'] = self::deserialize_string($stream);
$tmp['peer_stories'] = self::deserialize_type_array_of_PeerStories(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['chats'] = self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['users'] = self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['stealth_mode'] = match (stream_get_contents($stream, 4)) {
'ý\'.q' => self::deserialize_storiesStealthMode($stream),
'¡Ïr0' => self::deserialize_type_StoriesStealthMode(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_storyView(mixed $stream): mixed {
$tmp = ['_' => 'storyView'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['blocked'] = ($flags & 1) !== 0;
$tmp['blocked_my_stories_from'] = ($flags & 2) !== 0;
$tmp['user_id'] = unpack('q', stream_get_contents($stream, 8))[1];
$tmp['date'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 4) !== 0) $tmp['reaction'] = match (stream_get_contents($stream, 4)) {
'Ôõy' => [
'_' => 'reactionEmpty',
],
'¸†"' => [
'_' => 'reactionEmoji',
'emoticon' => self::deserialize_string($stream),
],
'sü5‰' => [
'_' => 'reactionCustomEmoji',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_Reaction(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_storyViewPublicForward(mixed $stream): mixed {
$tmp = ['_' => 'storyViewPublicForward'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['blocked'] = ($flags & 1) !== 0;
$tmp['blocked_my_stories_from'] = ($flags & 2) !== 0;
$tmp['message'] = match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_storyViewPublicRepost(mixed $stream): mixed {
$tmp = ['_' => 'storyViewPublicRepost'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['blocked'] = ($flags & 1) !== 0;
$tmp['blocked_my_stories_from'] = ($flags & 2) !== 0;
$tmp['peer_id'] = match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['story'] = match (stream_get_contents($stream, 4)) {
'OîæQ' => [
'_' => 'storyItemDeleted',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'É­ÿ' => self::deserialize_storyItemSkipped($stream),
'$j²y' => self::deserialize_storyItem($stream),
'¡Ïr0' => self::deserialize_type_StoryItem(self::gzdecode($stream)),
default => self::err($stream)
}
;
return $tmp;

    }
    private  function deserialize_type_array_of_StoryView(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'Åê½°' => self::deserialize_storyView($stream),
'gƒ�' => self::deserialize_storyViewPublicForward($stream),
'IÏt½' => self::deserialize_storyViewPublicRepost($stream),
'¡Ïr0' => $this->deserialize_type_StoryView(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_stories___storyViewsList(mixed $stream): mixed {
$tmp = ['_' => 'stories.storyViewsList'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['count'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['views_count'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['forwards_count'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['reactions_count'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['views'] = self::deserialize_type_array_of_StoryView(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['chats'] = self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['users'] = self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 1) !== 0) $tmp['next_offset'] = self::deserialize_string($stream);
return $tmp;

    }
    private  function deserialize_type_array_of_StoryViews(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'Ö\\Y�' => self::deserialize_storyViews($stream),
'¡Ïr0' => self::deserialize_type_StoryViews(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_StoryReaction(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'ÕÖ�`' => [
'_' => 'storyReaction',
'peer_id' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'reaction' => match (stream_get_contents($stream, 4)) {
'Ôõy' => [
'_' => 'reactionEmpty',
],
'¸†"' => [
'_' => 'reactionEmoji',
'emoticon' => self::deserialize_string($stream),
],
'sü5‰' => [
'_' => 'reactionCustomEmoji',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_Reaction(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'C&«»' => [
'_' => 'storyReactionPublicForward',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ÍÏ' => [
'_' => 'storyReactionPublicRepost',
'peer_id' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'story' => match (stream_get_contents($stream, 4)) {
'OîæQ' => [
'_' => 'storyItemDeleted',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'É­ÿ' => self::deserialize_storyItemSkipped($stream),
'$j²y' => self::deserialize_storyItem($stream),
'¡Ïr0' => self::deserialize_type_StoryItem(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => $this->deserialize_type_StoryReaction(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_stories___storyReactionsList(mixed $stream): mixed {
$tmp = ['_' => 'stories.storyReactionsList'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['count'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['reactions'] = self::deserialize_type_array_of_StoryReaction(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['chats'] = self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
$tmp['users'] = self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 1) !== 0) $tmp['next_offset'] = self::deserialize_string($stream);
return $tmp;

    }
    private  function deserialize_type_array_of_Boost(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'qŒ*' => self::deserialize_boost($stream),
'¡Ïr0' => self::deserialize_type_Boost(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_premium___boostsList(mixed $stream): mixed {
$tmp = ['_' => 'premium.boostsList'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['count'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['boosts'] = self::deserialize_type_array_of_Boost(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 1) !== 0) $tmp['next_offset'] = self::deserialize_string($stream);
$tmp['users'] = self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
return $tmp;

    }
    private  function deserialize_myBoost(mixed $stream): mixed {
$tmp = ['_' => 'myBoost'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['slot'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['peer'] = match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['date'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['expires'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 2) !== 0) $tmp['cooldown_until_date'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_array_of_MyBoost(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'\\AHÄ' => self::deserialize_myBoost($stream),
'¡Ïr0' => $this->deserialize_type_MyBoost(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_PrepaidGiveaway(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'T�S²' => [
'_' => 'prepaidGiveaway',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'months' => unpack('l', stream_get_contents($stream, 4))[1],
'quantity' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => $this->deserialize_type_PrepaidGiveaway(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_premium___boostsStatus(mixed $stream): mixed {
$tmp = ['_' => 'premium.boostsStatus'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['my_boost'] = ($flags & 4) !== 0;
$tmp['level'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['current_level_boosts'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['boosts'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 16) !== 0) $tmp['gift_boosts'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['next_level_boosts'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 2) !== 0) $tmp['premium_audience'] = match (stream_get_contents($stream, 4)) {
'à/ÎË' => [
'_' => 'statsPercentValue',
'part' => unpack('d', stream_get_contents($stream, 8))[1],
'total' => unpack('d', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_StatsPercentValue(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['boost_url'] = self::deserialize_string($stream);
if (($flags & 8) !== 0) $tmp['prepaid_giveaways'] = self::deserialize_type_array_of_PrepaidGiveaway(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
if (($flags & 4) !== 0) $tmp['my_boost_slots'] = self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            });
return $tmp;

    }
    private  function deserialize_smsjobs___status(mixed $stream): mixed {
$tmp = ['_' => 'smsjobs.status'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['allow_international'] = ($flags & 1) !== 0;
$tmp['recent_sent'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['recent_since'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['recent_remains'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['total_sent'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['total_since'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 2) !== 0) $tmp['last_gift_slug'] = self::deserialize_string($stream);
$tmp['terms_url'] = self::deserialize_string($stream);
return $tmp;

    }
    private  function deserialize_type_array_of_Bool(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'7—y¼' => [
'_' => 'boolFalse',
],
'µur™' => [
'_' => 'boolTrue',
],
'¡Ïr0' => $this->deserialize_type_Bool(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_ContactStatus(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
';pÙ' => [
'_' => 'contactStatus',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'status' => match (stream_get_contents($stream, 4)) {
'IPÐ	' => [
'_' => 'userStatusEmpty',
],
'I9¹í' => [
'_' => 'userStatusOnline',
'expires' => unpack('l', stream_get_contents($stream, 4))[1],
],
'?pŒ' . "\0" . '' => [
'_' => 'userStatusOffline',
'was_online' => unpack('l', stream_get_contents($stream, 4))[1],
],
'È}{' => self::deserialize_userStatusRecently($stream),
'T' => self::deserialize_userStatusLastWeek($stream),
'w—‰e' => self::deserialize_userStatusLastMonth($stream),
'¡Ïr0' => self::deserialize_type_UserStatus(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => $this->deserialize_type_ContactStatus(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_SavedContact(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'V½B' => [
'_' => 'savedPhoneContact',
'phone' => self::deserialize_string($stream),
'first_name' => self::deserialize_string($stream),
'last_name' => self::deserialize_string($stream),
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => $this->deserialize_type_SavedContact(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_ReceivedNotifyMessage(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'y·„£' => [
'_' => 'receivedNotifyMessage',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'flags' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => $this->deserialize_type_ReceivedNotifyMessage(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_MessageRange(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'Sã
' => [
'_' => 'messageRange',
'min_id' => unpack('l', stream_get_contents($stream, 4))[1],
'max_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => $this->deserialize_type_MessageRange(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_EmojiLanguage(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'aSû³' => [
'_' => 'emojiLanguage',
'lang_code' => self::deserialize_string($stream),
],
'¡Ïr0' => $this->deserialize_type_EmojiLanguage(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_inputMessagesFilterPhoneCalls(mixed $stream): mixed {
$tmp = ['_' => 'inputMessagesFilterPhoneCalls'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['missed'] = ($flags & 1) !== 0;
return $tmp;

    }
    private  function deserialize_type_MessagesFilter(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'löâW' => [
'_' => 'inputMessagesFilterEmpty',
],
'¥	–' => [
'_' => 'inputMessagesFilterPhotos',
],
'eÀŸ' => [
'_' => 'inputMessagesFilterVideo',
],
'äðéV' => [
'_' => 'inputMessagesFilterPhotoVideo',
],
'ˆñÝž' => [
'_' => 'inputMessagesFilterDocument',
],
'‡Ýð~' => [
'_' => 'inputMessagesFilterUrl',
],
'‡eÈÿ' => [
'_' => 'inputMessagesFilterGif',
],
'’ÃõP' => [
'_' => 'inputMessagesFilterVoice',
],
'ž´Q7' => [
'_' => 'inputMessagesFilterMusic',
],
'¸ì :' => [
'_' => 'inputMessagesFilterChatPhotos',
],
'h—É€' => self::deserialize_inputMessagesFilterPhoneCalls($stream),
'¤|z' => [
'_' => 'inputMessagesFilterRoundVoice',
],
'SÚIµ' => [
'_' => 'inputMessagesFilterRoundVideo',
],
'šæøÁ' => [
'_' => 'inputMessagesFilterMyMentions',
],
'mç' => [
'_' => 'inputMessagesFilterGeo',
],
'ƒÛbà' => [
'_' => 'inputMessagesFilterContacts',
],
'Q°' => [
'_' => 'inputMessagesFilterPinned',
],
'¡Ïr0' => $this->deserialize_type_MessagesFilter(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    private  function deserialize_messages___searchCounter(mixed $stream): mixed {
$tmp = ['_' => 'messages.searchCounter'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['inexact'] = ($flags & 2) !== 0;
$tmp['filter'] = match (stream_get_contents($stream, 4)) {
'löâW' => [
'_' => 'inputMessagesFilterEmpty',
],
'¥	–' => [
'_' => 'inputMessagesFilterPhotos',
],
'eÀŸ' => [
'_' => 'inputMessagesFilterVideo',
],
'äðéV' => [
'_' => 'inputMessagesFilterPhotoVideo',
],
'ˆñÝž' => [
'_' => 'inputMessagesFilterDocument',
],
'‡Ýð~' => [
'_' => 'inputMessagesFilterUrl',
],
'‡eÈÿ' => [
'_' => 'inputMessagesFilterGif',
],
'’ÃõP' => [
'_' => 'inputMessagesFilterVoice',
],
'ž´Q7' => [
'_' => 'inputMessagesFilterMusic',
],
'¸ì :' => [
'_' => 'inputMessagesFilterChatPhotos',
],
'h—É€' => self::deserialize_inputMessagesFilterPhoneCalls($stream),
'¤|z' => [
'_' => 'inputMessagesFilterRoundVoice',
],
'SÚIµ' => [
'_' => 'inputMessagesFilterRoundVideo',
],
'šæøÁ' => [
'_' => 'inputMessagesFilterMyMentions',
],
'mç' => [
'_' => 'inputMessagesFilterGeo',
],
'ƒÛbà' => [
'_' => 'inputMessagesFilterContacts',
],
'Q°' => [
'_' => 'inputMessagesFilterPinned',
],
'¡Ïr0' => self::deserialize_type_MessagesFilter(self::gzdecode($stream)),
default => self::err($stream)
}
;
$tmp['count'] = unpack('l', stream_get_contents($stream, 4))[1];
return $tmp;

    }
    private  function deserialize_type_array_of_messages___SearchCounter(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'ÿëDè' => self::deserialize_messages___searchCounter($stream),
'¡Ïr0' => $this->deserialize_type_messages___SearchCounter(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_DialogFilterSuggested(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'JMtw' => [
'_' => 'dialogFilterSuggested',
'filter' => match (stream_get_contents($stream, 4)) {
';Rµ_' => self::deserialize_dialogFilter($stream),
'®“26' => [
'_' => 'dialogFilterDefault',
],
'¤ŽâŸ' => self::deserialize_dialogFilterChatlist($stream),
'¡Ïr0' => self::deserialize_type_DialogFilter(self::gzdecode($stream)),
default => self::err($stream)
}
,
'description' => self::deserialize_string($stream),
],
'¡Ïr0' => $this->deserialize_type_DialogFilterSuggested(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_ReadParticipantDate(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'rñOJ' => [
'_' => 'readParticipantDate',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => $this->deserialize_type_ReadParticipantDate(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_premiumGiftCodeOption(mixed $stream): mixed {
$tmp = ['_' => 'premiumGiftCodeOption'];
$flags = unpack('V', stream_get_contents($stream, 4))[1];
$tmp['users'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['months'] = unpack('l', stream_get_contents($stream, 4))[1];
if (($flags & 1) !== 0) $tmp['store_product'] = self::deserialize_string($stream);
if (($flags & 2) !== 0) $tmp['store_quantity'] = unpack('l', stream_get_contents($stream, 4))[1];
$tmp['currency'] = self::deserialize_string($stream);
$tmp['amount'] = unpack('q', stream_get_contents($stream, 8))[1];
return $tmp;

    }
    private  function deserialize_type_array_of_PremiumGiftCodeOption(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'+–~%' => self::deserialize_premiumGiftCodeOption($stream),
'¡Ïr0' => $this->deserialize_type_PremiumGiftCodeOption(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_LangPackLanguage(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= match (stream_get_contents($stream, 4)) {
'ã\\Êî' => self::deserialize_langPackLanguage($stream),
'¡Ïr0' => $this->deserialize_type_LangPackLanguage(self::gzdecode($stream)),
default => self::err($stream)
}
;
                }
                return $result;    
            
    }
    private  function deserialize_type_MethodResult(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'%' . "\0" . '^' => self::deserialize_auth___sentCode($stream),
'Dþ�#' => [
'_' => 'auth.sentCodeSuccess',
'authorization' => match (stream_get_contents($stream, 4)) {
'ÔÀ¢.' => self::deserialize_auth___authorization($stream),
'š~tD' => self::deserialize_auth___authorizationSignUpRequired($stream),
'¡Ïr0' => self::deserialize_type_auth___Authorization(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ÔÀ¢.' => self::deserialize_auth___authorization($stream),
'š~tD' => self::deserialize_auth___authorizationSignUpRequired($stream),
'_ƒ¢Ã' => self::deserialize_auth___loggedOut($stream),
'7—y¼' => [
'_' => 'boolFalse',
],
'µur™' => [
'_' => 'boolTrue',
],
'¸â4´' => [
'_' => 'auth.exportedAuthorization',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'bytes' => self::deserialize_bytes($stream),
],
'¥Hy' => [
'_' => 'auth.passwordRecovery',
'email_pattern' => self::deserialize_string($stream),
],
'€Ÿb' => [
'_' => 'auth.loginToken',
'expires' => unpack('l', stream_get_contents($stream, 4))[1],
'token' => self::deserialize_bytes($stream),
],
'™Ž' => [
'_' => 'auth.loginTokenMigrateTo',
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'token' => self::deserialize_bytes($stream),
],
'^\\9' => [
'_' => 'auth.loginTokenSuccess',
'authorization' => match (stream_get_contents($stream, 4)) {
'ÔÀ¢.' => self::deserialize_auth___authorization($stream),
'š~tD' => self::deserialize_auth___authorizationSignUpRequired($stream),
'¡Ïr0' => self::deserialize_type_auth___Authorization(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Ö­' => self::deserialize_authorization($stream),
',b™' => self::deserialize_peerNotifySettings($stream),
'zK¼Ó' => self::deserialize_userEmpty($stream),
'8D\\!' => self::deserialize_user($stream),
'ƒ‘' => [
'_' => 'account.wallPapersNotModified',
],
'Œ…ÃÍ' => [
'_' => 'account.wallPapers',
'hash' => unpack('q', stream_get_contents($stream, 8))[1],
'wallpapers' => self::deserialize_type_array_of_WallPaper(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'EN P' => [
'_' => 'account.privacyRules',
'rules' => self::deserialize_type_array_of_PrivacyRule(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ß¯Ð¸' => [
'_' => 'accountDaysTTL',
'days' => unpack('l', stream_get_contents($stream, 4))[1],
],
' ŽÿK' => [
'_' => 'account.authorizations',
'authorization_ttl_days' => unpack('l', stream_get_contents($stream, 4))[1],
'authorizations' => self::deserialize_type_array_of_Authorization(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ûP{•' => self::deserialize_account___password($stream),
'å3\\š' => self::deserialize_account___passwordSettings($stream),
'4ýdÛ' => [
'_' => 'account.tmpPassword',
'tmp_password' => self::deserialize_bytes($stream),
'valid_until' => unpack('l', stream_get_contents($stream, 4))[1],
],
'üÉVí' => [
'_' => 'account.webAuthorizations',
'authorizations' => self::deserialize_type_array_of_WebAuthorization(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'Ê ' => self::deserialize_secureValue($stream),
'Ø.­' => self::deserialize_account___authorizationForm($stream),
'O…�' => [
'_' => 'account.sentEmailCode',
'email_pattern' => self::deserialize_string($stream),
'length' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Í–+' => [
'_' => 'account.emailVerified',
'email' => self::deserialize_string($stream),
],
'a»á' => [
'_' => 'account.emailVerifiedLogin',
'email' => self::deserialize_string($stream),
'sent_code' => match (stream_get_contents($stream, 4)) {
'%' . "\0" . '^' => self::deserialize_auth___sentCode($stream),
'Dþ�#' => [
'_' => 'auth.sentCodeSuccess',
'authorization' => match (stream_get_contents($stream, 4)) {
'ÔÀ¢.' => self::deserialize_auth___authorization($stream),
'š~tD' => self::deserialize_auth___authorizationSignUpRequired($stream),
'¡Ïr0' => self::deserialize_type_auth___Authorization(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_auth___SentCode(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'EºM' => [
'_' => 'account.takeout',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'~¯ã' => [
'_' => 'updatesTooLong',
],
'øÇ;1' => self::deserialize_updateShortMessage($stream),
'¥îmM' => self::deserialize_updateShortChatMessage($stream),
'ÁÞÔx' => [
'_' => 'updateShort',
'update' => match (stream_get_contents($stream, 4)) {
'ý
+' => [
'_' => 'updateNewMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Ö¿�N' => [
'_' => 'updateMessageID',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'random_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'å°¢' => [
'_' => 'updateDeleteMessages',
'messages' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'…À' => [
'_' => 'updateUserTyping',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'action' => match (stream_get_contents($stream, 4)) {
'Nt¿' => [
'_' => 'sendMessageTypingAction',
],
'õÈ^ý' => [
'_' => 'sendMessageCancelAction',
],
'oÖ‡¡' => [
'_' => 'sendMessageRecordVideoAction',
],
'ì:vé' => [
'_' => 'sendMessageUploadVideoAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'÷s/Õ' => [
'_' => 'sendMessageRecordAudioAction',
],
'«×Qó' => [
'_' => 'sendMessageUploadAudioAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'&JÓÑ' => [
'_' => 'sendMessageUploadPhotoAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'äÙª' => [
'_' => 'sendMessageUploadDocumentAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡‹o' => [
'_' => 'sendMessageGeoLocationAction',
],
'o¼Œb' => [
'_' => 'sendMessageChooseContactAction',
],
'H�jÝ' => [
'_' => 'sendMessageGamePlayAction',
],
'¼òˆ' => [
'_' => 'sendMessageRecordRoundAction',
],
'f>$' => [
'_' => 'sendMessageUploadRoundAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'…",Ù' => [
'_' => 'speakingInGroupCallAction',
],
'F’ÚÛ' => [
'_' => 'sendMessageHistoryImportAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'±ÆZ°' => [
'_' => 'sendMessageChooseStickerAction',
],
'Ë+—%' => [
'_' => 'sendMessageEmojiInteraction',
'emoticon' => self::deserialize_string($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'interaction' => match (stream_get_contents($stream, 4)) {
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'¡Ïr0' => self::deserialize_type_DataJSON(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'.�e¶' => [
'_' => 'sendMessageEmojiInteractionSeen',
'emoticon' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_SendMessageAction(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ðzHƒ' => [
'_' => 'updateChatUserTyping',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'from_id' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'action' => match (stream_get_contents($stream, 4)) {
'Nt¿' => [
'_' => 'sendMessageTypingAction',
],
'õÈ^ý' => [
'_' => 'sendMessageCancelAction',
],
'oÖ‡¡' => [
'_' => 'sendMessageRecordVideoAction',
],
'ì:vé' => [
'_' => 'sendMessageUploadVideoAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'÷s/Õ' => [
'_' => 'sendMessageRecordAudioAction',
],
'«×Qó' => [
'_' => 'sendMessageUploadAudioAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'&JÓÑ' => [
'_' => 'sendMessageUploadPhotoAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'äÙª' => [
'_' => 'sendMessageUploadDocumentAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡‹o' => [
'_' => 'sendMessageGeoLocationAction',
],
'o¼Œb' => [
'_' => 'sendMessageChooseContactAction',
],
'H�jÝ' => [
'_' => 'sendMessageGamePlayAction',
],
'¼òˆ' => [
'_' => 'sendMessageRecordRoundAction',
],
'f>$' => [
'_' => 'sendMessageUploadRoundAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'…",Ù' => [
'_' => 'speakingInGroupCallAction',
],
'F’ÚÛ' => [
'_' => 'sendMessageHistoryImportAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'±ÆZ°' => [
'_' => 'sendMessageChooseStickerAction',
],
'Ë+—%' => [
'_' => 'sendMessageEmojiInteraction',
'emoticon' => self::deserialize_string($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'interaction' => match (stream_get_contents($stream, 4)) {
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'¡Ïr0' => self::deserialize_type_DataJSON(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'.�e¶' => [
'_' => 'sendMessageEmojiInteractionSeen',
'emoticon' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_SendMessageAction(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'˜v' => [
'_' => 'updateChatParticipants',
'participants' => match (stream_get_contents($stream, 4)) {
'áÓc‡' => self::deserialize_chatParticipantsForbidden($stream),
'ø“¼<' => [
'_' => 'chatParticipants',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'participants' => self::deserialize_type_array_of_ChatParticipant(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_ChatParticipants(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Þø½å' => [
'_' => 'updateUserStatus',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'status' => match (stream_get_contents($stream, 4)) {
'IPÐ	' => [
'_' => 'userStatusEmpty',
],
'I9¹í' => [
'_' => 'userStatusOnline',
'expires' => unpack('l', stream_get_contents($stream, 4))[1],
],
'?pŒ' . "\0" . '' => [
'_' => 'userStatusOffline',
'was_online' => unpack('l', stream_get_contents($stream, 4))[1],
],
'È}{' => self::deserialize_userStatusRecently($stream),
'T' => self::deserialize_userStatusLastWeek($stream),
'w—‰e' => self::deserialize_userStatusLastMonth($stream),
'¡Ïr0' => self::deserialize_type_UserStatus(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'$‰„§' => [
'_' => 'updateUserName',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'first_name' => self::deserialize_string($stream),
'last_name' => self::deserialize_string($stream),
'usernames' => self::deserialize_type_array_of_Username(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ï«Q‰' => self::deserialize_updateNewAuthorization($stream),
'š½¼' => [
'_' => 'updateNewEncryptedMessage',
'message' => match (stream_get_contents($stream, 4)) {
'Áí' => [
'_' => 'encryptedMessage',
'random_id' => unpack('q', stream_get_contents($stream, 8))[1],
'chat_id' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'bytes' => self::deserialize_bytes($stream),
'file' => match (stream_get_contents($stream, 4)) {
'~IÂ' => [
'_' => 'encryptedFileEmpty',
],
'ØŒ' . "\0" . '¨' => [
'_' => 'encryptedFile',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'size' => unpack('q', stream_get_contents($stream, 8))[1],
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'key_fingerprint' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_EncryptedFile(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Ks#' => [
'_' => 'encryptedMessageService',
'random_id' => unpack('q', stream_get_contents($stream, 8))[1],
'chat_id' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'bytes' => self::deserialize_bytes($stream),
],
'¡Ïr0' => self::deserialize_type_EncryptedMessage(self::gzdecode($stream)),
default => self::err($stream)
}
,
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Vñ' => [
'_' => 'updateEncryptedChatTyping',
'chat_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'�è¢´' => [
'_' => 'updateEncryption',
'chat' => match (stream_get_contents($stream, 4)) {
' À~«' => [
'_' => 'encryptedChatEmpty',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'SY²f' => [
'_' => 'encryptedChatWaiting',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'admin_id' => unpack('q', stream_get_contents($stream, 8))[1],
'participant_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'LÙñH' => self::deserialize_encryptedChatRequested($stream),
'ÇÔða' => [
'_' => 'encryptedChat',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'admin_id' => unpack('q', stream_get_contents($stream, 8))[1],
'participant_id' => unpack('q', stream_get_contents($stream, 8))[1],
'g_a_or_b' => self::deserialize_bytes($stream),
'key_fingerprint' => stream_get_contents($stream, 8),
],
'E|' => self::deserialize_encryptedChatDiscarded($stream),
'¡Ïr0' => self::deserialize_type_EncryptedChat(self::gzdecode($stream)),
default => self::err($stream)
}
,
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'·%þ8' => [
'_' => 'updateEncryptedMessagesRead',
'chat_id' => unpack('l', stream_get_contents($stream, 4))[1],
'max_date' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'QTÚ=' => [
'_' => 'updateChatParticipantAdd',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'inviter_id' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
'w=/ã' => [
'_' => 'updateChatParticipantDelete',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
's˜^Ž' => [
'_' => 'updateDcOptions',
'dc_options' => self::deserialize_type_array_of_DcOption(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ïhÂ¾' => [
'_' => 'updateNotifySettings',
'peer' => match (stream_get_contents($stream, 4)) {
'ØÔŸ' => [
'_' => 'notifyPeer',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'L;È´' => [
'_' => 'notifyUsers',
],
'ÃÎÀ' => [
'_' => 'notifyChats',
],
'ïèÖ' => [
'_' => 'notifyBroadcasts',
],
'cn"' => [
'_' => 'notifyForumTopic',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'top_msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_NotifyPeer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'notify_settings' => match (stream_get_contents($stream, 4)) {
',b™' => self::deserialize_peerNotifySettings($stream),
'¡Ïr0' => self::deserialize_type_PeerNotifySettings(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'häë' => self::deserialize_updateServiceNotification($stream),
'*\';î' => [
'_' => 'updatePrivacy',
'key' => match (stream_get_contents($stream, 4)) {
'0«.¼' => [
'_' => 'privacyKeyStatusTimestamp',
],
'úmP' => [
'_' => 'privacyKeyChatInvite',
],
'{+f=' => [
'_' => 'privacyKeyPhoneCall',
],
'ÈI9' => [
'_' => 'privacyKeyPhoneP2P',
],
'£Vìi' => [
'_' => 'privacyKeyForwards',
],
'í–' => [
'_' => 'privacyKeyProfilePhoto',
],
'mäšÑ' => [
'_' => 'privacyKeyPhoneNumber',
],
'+ÔÿB' => [
'_' => 'privacyKeyAddedByPhone',
],
'ô—' => [
'_' => 'privacyKeyVoiceMessages',
],
'a·†¤' => [
'_' => 'privacyKeyAbout',
],
'¡Ïr0' => self::deserialize_type_PrivacyKey(self::gzdecode($stream)),
default => self::err($stream)
}
,
'rules' => self::deserialize_type_array_of_PrivacyRule(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'*I' => [
'_' => 'updateUserPhone',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'phone' => self::deserialize_string($stream),
],
'ßO—œ' => self::deserialize_updateReadHistoryInbox($stream),
'¿!//' => [
'_' => 'updateReadHistoryOutbox',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'max_id' => unpack('l', stream_get_contents($stream, 4))[1],
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'‰' => [
'_' => 'updateWebPage',
'webpage' => match (stream_get_contents($stream, 4)) {
'ˆ!' => self::deserialize_webPageEmpty($stream),
'G>Ñ°' => self::deserialize_webPagePending($stream),
'²Eœè' => self::deserialize_webPage($stream),
'Ês' => self::deserialize_webPageNotModified($stream),
'¡Ïr0' => self::deserialize_type_WebPage(self::gzdecode($stream)),
default => self::err($stream)
}
,
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'�q"ø' => self::deserialize_updateReadMessagesContents($stream),
'”�' => self::deserialize_updateChannelTooLong($stream),
'	L[c' => [
'_' => 'updateChannel',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'Ùºb' => [
'_' => 'updateNewChannelMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'n.’' => self::deserialize_updateReadChannelInbox($stream),
'[-Ã' => [
'_' => 'updateDeleteChannelMessages',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'messages' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¬&ò' => [
'_' => 'updateChannelMessageViews',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'views' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¢aÊ×' => [
'_' => 'updateChatParticipantAdmin',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'is_admin' => match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) },
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
'ª0Šh' => [
'_' => 'updateNewStickerSet',
'stickerset' => match (stream_get_contents($stream, 4)) {
'?n' => self::deserialize_messages___stickerSet($stream),
'ë$ùÓ' => [
'_' => 'messages.stickerSetNotModified',
],
'¡Ïr0' => self::deserialize_type_messages___StickerSet(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Ò²' => self::deserialize_updateStickerSetsOrder($stream),
'HÂ1' => self::deserialize_updateStickerSets($stream),
'4u“' => [
'_' => 'updateSavedGifs',
],
'œ7oI' => self::deserialize_updateBotInlineQuery($stream),
'*ñ' => self::deserialize_updateBotInlineSend($stream),
'÷M?' => [
'_' => 'updateEditChannelMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'�ÄÏ¹' => self::deserialize_updateBotCallbackQuery($stream),
'£pä' => [
'_' => 'updateEditMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'R�i' => self::deserialize_updateInlineBotCallbackQuery($stream),
'©™_·' => [
'_' => 'updateReadChannelOutbox',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'max_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'mìI' => self::deserialize_updateDraftMessage($stream),
'B\'W' => [
'_' => 'updateReadFeaturedStickers',
],
' ,Bš' => [
'_' => 'updateRecentStickers',
],
'Ý)¢' => [
'_' => 'updateConfig',
],
'�gT3' => [
'_' => 'updatePtsChanged',
],
'Ÿ©+/' => [
'_' => 'updateChannelWebPage',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'webpage' => match (stream_get_contents($stream, 4)) {
'ˆ!' => self::deserialize_webPageEmpty($stream),
'G>Ñ°' => self::deserialize_webPagePending($stream),
'²Eœè' => self::deserialize_webPage($stream),
'Ês' => self::deserialize_webPageNotModified($stream),
'¡Ïr0' => self::deserialize_type_WebPage(self::gzdecode($stream)),
default => self::err($stream)
}
,
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'åon' => self::deserialize_updateDialogPinned($stream),
'¢<ú' => self::deserialize_updatePinnedDialogs($stream),
'ÃÀƒ' => [
'_' => 'updateBotWebhookJSON',
'data' => match (stream_get_contents($stream, 4)) {
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'¡Ïr0' => self::deserialize_type_DataJSON(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¦@’›' => [
'_' => 'updateBotWebhookJSONQuery',
'query_id' => unpack('q', stream_get_contents($stream, 8))[1],
'data' => match (stream_get_contents($stream, 4)) {
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'¡Ïr0' => self::deserialize_type_DataJSON(self::gzdecode($stream)),
default => self::err($stream)
}
,
'timeout' => unpack('l', stream_get_contents($stream, 4))[1],
],
'}ý®µ' => [
'_' => 'updateBotShippingQuery',
'query_id' => unpack('q', stream_get_contents($stream, 8))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'payload' => self::deserialize_bytes($stream),
'shipping_address' => match (stream_get_contents($stream, 4)) {
'ëªŒ' => [
'_' => 'postAddress',
'street_line1' => self::deserialize_string($stream),
'street_line2' => self::deserialize_string($stream),
'city' => self::deserialize_string($stream),
'state' => self::deserialize_string($stream),
'country_iso2' => self::deserialize_string($stream),
'post_code' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_PostAddress(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'–šªŒ' => self::deserialize_updateBotPrecheckoutQuery($stream),
'k«' => [
'_' => 'updatePhoneCall',
'phone_call' => match (stream_get_contents($stream, 4)) {
'ÉfS' => [
'_' => 'phoneCallEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'o"Å' => self::deserialize_phoneCallWaiting($stream),
'í°' => self::deserialize_phoneCallRequested($stream),
'Ã`6' => self::deserialize_phoneCallAccepted($stream),
'g|–' => self::deserialize_phoneCall($stream),
'áMÊP' => self::deserialize_phoneCallDiscarded($stream),
'¡Ïr0' => self::deserialize_type_PhoneCall(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'dVF' => [
'_' => 'updateLangPackTooLong',
'lang_code' => self::deserialize_string($stream),
],
'M/V' => [
'_' => 'updateLangPack',
'difference' => match (stream_get_contents($stream, 4)) {
'öÁ…ó' => [
'_' => 'langPackDifference',
'lang_code' => self::deserialize_string($stream),
'from_version' => unpack('l', stream_get_contents($stream, 4))[1],
'version' => unpack('l', stream_get_contents($stream, 4))[1],
'strings' => self::deserialize_type_array_of_LangPackString(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => self::deserialize_type_LangPackDifference(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'm™å' => [
'_' => 'updateFavedStickers',
],
'])ê' => self::deserialize_updateChannelReadMessagesContents($stream),
'¾§„p' => [
'_' => 'updateContactsReset',
],
'˜Æ?²' => [
'_' => 'updateChannelAvailableMessages',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'available_min_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'ÃYdá' => self::deserialize_updateDialogUnreadMark($stream),
'{e¡¬' => self::deserialize_updateMessagePoll($stream),
'PÀT' => [
'_' => 'updateChatDefaultBannedRights',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'default_banned_rights' => match (stream_get_contents($stream, 4)) {
'Ÿ' => self::deserialize_chatBannedRights($stream),
'¡Ïr0' => self::deserialize_type_ChatBannedRights(self::gzdecode($stream)),
default => self::err($stream)
}
,
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
'À6' => [
'_' => 'updateFolderPeers',
'folder_peers' => self::deserialize_type_array_of_FolderPeer(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'fs~j' => [
'_' => 'updatePeerSettings',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'settings' => match (stream_get_contents($stream, 4)) {
'¥' => self::deserialize_peerSettings($stream),
'¡Ïr0' => self::deserialize_type_PeerSettings(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'°Ï¯´' => [
'_' => 'updatePeerLocated',
'peers' => self::deserialize_type_array_of_PeerLocated(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'û¥9' => [
'_' => 'updateNewScheduledMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'îl†�' => [
'_' => 'updateDeleteScheduledMessages',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'messages' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'£û‚' => [
'_' => 'updateTheme',
'theme' => match (stream_get_contents($stream, 4)) {
'Ög ' => self::deserialize_theme($stream),
'¡Ïr0' => self::deserialize_type_Theme(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'9¹‡' => [
'_' => 'updateGeoLiveViewed',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'‘æOV' => [
'_' => 'updateLoginToken',
],
'wô$' => [
'_' => 'updateMessagePollVote',
'poll_id' => unpack('q', stream_get_contents($stream, 8))[1],
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'options' => self::deserialize_type_array_of_bytes(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'}Þÿ&' => self::deserialize_updateDialogFilter($stream),
'!×¥' => [
'_' => 'updateDialogFilterOrder',
'order' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'O‘5' => [
'_' => 'updateDialogFilters',
],
'	¿a&' => [
'_' => 'updatePhoneCallSignalingData',
'phone_call_id' => unpack('q', stream_get_contents($stream, 8))[1],
'data' => self::deserialize_bytes($stream),
],
'ô\'šÒ' => [
'_' => 'updateChannelMessageForwards',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'forwards' => unpack('l', stream_get_contents($stream, 4))[1],
],
'F•±Ö' => self::deserialize_updateReadChannelDiscussionInbox($stream),
'|ž\\i' => [
'_' => 'updateReadChannelDiscussionOutbox',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'top_msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'read_max_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Rwàë' => self::deserialize_updatePeerBlocked($stream),
'#ÉˆŒ' => self::deserialize_updateChannelUserTyping($stream),
'µê…í' => self::deserialize_updatePinnedMessages($stream),
'†¹[' => self::deserialize_updatePinnedChannelMessages($stream),
'Njšø' => [
'_' => 'updateChat',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'NÛëò' => [
'_' => 'updateGroupCallParticipants',
'call' => match (stream_get_contents($stream, 4)) {
'„ªØ' => [
'_' => 'inputGroupCall',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputGroupCall(self::gzdecode($stream)),
default => self::err($stream)
}
,
'participants' => self::deserialize_type_array_of_GroupCallParticipant(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
'' . "\0" . 'E²' => [
'_' => 'updateGroupCall',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'call' => match (stream_get_contents($stream, 4)) {
'´¼€w' => [
'_' => 'groupCallDiscarded',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'duration' => unpack('l', stream_get_contents($stream, 4))[1],
],
'e—Õ' => self::deserialize_groupCall($stream),
'¡Ïr0' => self::deserialize_type_GroupCall(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¥¹›»' => self::deserialize_updatePeerHistoryTTL($stream),
':f‡Ð' => self::deserialize_updateChatParticipant($stream),
'»:]˜' => self::deserialize_updateChannelParticipant($stream),
'I
‡Ä' => [
'_' => 'updateBotStopped',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'stopped' => match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) },
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'‚9x' => self::deserialize_updateGroupCallConnection($stream),
'./qM' => [
'_' => 'updateBotCommands',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'bot_id' => unpack('q', stream_get_contents($stream, 8))[1],
'commands' => self::deserialize_type_array_of_BotCommand(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ÛÃcp' => [
'_' => 'updatePendingJoinRequests',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'requests_pending' => unpack('l', stream_get_contents($stream, 4))[1],
'recent_requesters' => self::deserialize_type_array_of_long(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'†©ß' => [
'_' => 'updateBotChatInviteRequester',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'about' => self::deserialize_string($stream),
'invite' => match (stream_get_contents($stream, 4)) {
'¨´
' => self::deserialize_chatInviteExported($stream),
'·zí' => [
'_' => 'chatInvitePublicJoinRequests',
],
'¡Ïr0' => self::deserialize_type_ExportedChatInvite(self::gzdecode($stream)),
default => self::err($stream)
}
,
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¸<^' => self::deserialize_updateMessageReactions($stream),
'¢·' => [
'_' => 'updateAttachMenuBots',
],
'�·’' => [
'_' => 'updateWebViewResultSent',
'query_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'X¸' => [
'_' => 'updateBotMenuButton',
'bot_id' => unpack('q', stream_get_contents($stream, 8))[1],
'button' => match (stream_get_contents($stream, 4)) {
'ˆ¥3u' => [
'_' => 'botMenuButtonDefault',
],
'ÂXB' => [
'_' => 'botMenuButtonCommands',
],
'æ|µÇ' => [
'_' => 'botMenuButton',
'text' => self::deserialize_string($stream),
'url' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_BotMenuButton(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'™¾Øt' => [
'_' => 'updateSavedRingtones',
],
'ZÍ„' . "\0" . '' => self::deserialize_updateTranscribedAudio($stream),
'lILû' => [
'_' => 'updateReadFeaturedEmojiStickers',
],
'™57(' => [
'_' => 'updateUserEmojiStatus',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'emoji_status' => match (stream_get_contents($stream, 4)) {
'®á-' => [
'_' => 'emojiStatusEmpty',
],
'�a›’' => [
'_' => 'emojiStatus',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'Ç¨0ú' => [
'_' => 'emojiStatusUntil',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'until' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_EmojiStatus(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ÛCô0' => [
'_' => 'updateRecentEmojiStatuses',
],
'ôcxo' => [
'_' => 'updateRecentReactions',
],
'…Ïü†' => self::deserialize_updateMoveStickerSetToTop($stream),
'Œ©sZ' => [
'_' => 'updateMessageExtendedMedia',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'extended_media' => match (stream_get_contents($stream, 4)) {
'ÈŒb­' => self::deserialize_messageExtendedMediaPreview($stream),
'dœGî' => [
'_' => 'messageExtendedMedia',
'media' => match (stream_get_contents($stream, 4)) {
' cí=' => [
'_' => 'messageMediaEmpty',
],
'×PQi' => self::deserialize_messageMediaPhoto($stream),
'tÔàV' => [
'_' => 'messageMediaGeo',
'geo' => match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => self::deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'I)2p' => [
'_' => 'messageMediaContact',
'phone_number' => self::deserialize_string($stream),
'first_name' => self::deserialize_string($stream),
'last_name' => self::deserialize_string($stream),
'vcard' => self::deserialize_string($stream),
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'žô„Ÿ' => [
'_' => 'messageMediaUnsupported',
],
'-×ôL' => self::deserialize_messageMediaDocument($stream),
';ñÝ' => self::deserialize_messageMediaWebPage($stream),
'?SÀ.' => [
'_' => 'messageMediaVenue',
'geo' => match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => self::deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
,
'title' => self::deserialize_string($stream),
'address' => self::deserialize_string($stream),
'provider' => self::deserialize_string($stream),
'venue_id' => self::deserialize_string($stream),
'venue_type' => self::deserialize_string($stream),
],
'�±ý' => [
'_' => 'messageMediaGame',
'game' => match (stream_get_contents($stream, 4)) {
';eù½' => self::deserialize_game($stream),
'¡Ïr0' => self::deserialize_type_Game(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ÓH¥ö' => self::deserialize_messageMediaInvoice($stream),
'fÆ@¹' => self::deserialize_messageMediaGeoLive($stream),
'˜çÖK' => [
'_' => 'messageMediaPoll',
'poll' => match (stream_get_contents($stream, 4)) {
'a�á†' => self::deserialize_poll($stream),
'¡Ïr0' => self::deserialize_type_Poll(self::gzdecode($stream)),
default => self::err($stream)
}
,
'results' => match (stream_get_contents($stream, 4)) {
' $ßz' => self::deserialize_pollResults($stream),
'¡Ïr0' => self::deserialize_type_PollResults(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'‹å~?' => [
'_' => 'messageMediaDice',
'value' => unpack('l', stream_get_contents($stream, 4))[1],
'emoticon' => self::deserialize_string($stream),
],
'ƒbËh' => self::deserialize_messageMediaStory($stream),
'°…­Ú' => self::deserialize_messageMediaGiveaway($stream),
'h™Æ' => self::deserialize_messageMediaGiveawayResults($stream),
'¡Ïr0' => self::deserialize_type_MessageMedia(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_MessageExtendedMedia(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ãû.' => self::deserialize_updateChannelPinnedTopic($stream),
'†þ' => self::deserialize_updateChannelPinnedTopics($stream),
'8”R ' => [
'_' => 'updateUser',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'—°ì' => [
'_' => 'updateAutoSaveSettings',
],
'ÖŠðÌ' => [
'_' => 'updateGroupInvitePrivacyForbidden',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'˜·³u' => [
'_' => 'updateStory',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'story' => match (stream_get_contents($stream, 4)) {
'OîæQ' => [
'_' => 'storyItemDeleted',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'É­ÿ' => self::deserialize_storyItemSkipped($stream),
'$j²y' => self::deserialize_storyItem($stream),
'¡Ïr0' => self::deserialize_type_StoryItem(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'+“N÷' => [
'_' => 'updateReadStories',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'max_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¹5ó' => [
'_' => 'updateStoryID',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'random_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÁM,' => [
'_' => 'updateStoriesStealthMode',
'stealth_mode' => match (stream_get_contents($stream, 4)) {
'ý\'.q' => self::deserialize_storiesStealthMode($stream),
'¡Ïr0' => self::deserialize_type_StoriesStealthMode(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ƒvb}' => [
'_' => 'updateSentStoryReaction',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'story_id' => unpack('l', stream_get_contents($stream, 4))[1],
'reaction' => match (stream_get_contents($stream, 4)) {
'Ôõy' => [
'_' => 'reactionEmpty',
],
'¸†"' => [
'_' => 'reactionEmoji',
'emoticon' => self::deserialize_string($stream),
],
'sü5‰' => [
'_' => 'reactionCustomEmoji',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_Reaction(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'œÔM�' => [
'_' => 'updateBotChatBoost',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'boost' => match (stream_get_contents($stream, 4)) {
'qŒ*' => self::deserialize_boost($stream),
'¡Ïr0' => self::deserialize_type_Boost(self::gzdecode($stream)),
default => self::err($stream)
}
,
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
' ‰¶' => [
'_' => 'updateChannelViewForumAsMessages',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'enabled' => match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) },
],
'?®' => self::deserialize_updatePeerWallpaper($stream),
'ÎÓ!¬' => [
'_' => 'updateBotMessageReaction',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'actor' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'old_reactions' => self::deserialize_type_array_of_Reaction(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'new_reactions' => self::deserialize_type_array_of_Reaction(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'YwË	' => [
'_' => 'updateBotMessageReactions',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'reactions' => self::deserialize_type_array_of_ReactionCount(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'tž¯®' => self::deserialize_updateSavedDialogPinned($stream),
'¦…lh' => self::deserialize_updatePinnedSavedDialogs($stream),
'2tÆ9' => [
'_' => 'updateSavedReactionTags',
],
'Ôibñ' => [
'_' => 'updateSmsJob',
'job_id' => self::deserialize_string($stream),
],
'²
Gù' => [
'_' => 'updateQuickReplies',
'quick_replies' => self::deserialize_type_array_of_QuickReply(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'§=õ' => [
'_' => 'updateNewQuickReply',
'quick_reply' => match (stream_get_contents($stream, 4)) {
'+—' => [
'_' => 'quickReply',
'shortcut_id' => unpack('l', stream_get_contents($stream, 4))[1],
'shortcut' => self::deserialize_string($stream),
'top_message' => unpack('l', stream_get_contents($stream, 4))[1],
'count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_QuickReply(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ìñæS' => [
'_' => 'updateDeleteQuickReply',
'shortcut_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'>' => [
'_' => 'updateQuickReplyMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ÍçoV' => [
'_' => 'updateDeleteQuickReplyMessages',
'shortcut_id' => unpack('l', stream_get_contents($stream, 4))[1],
'messages' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => self::deserialize_type_Update(self::gzdecode($stream)),
default => self::err($stream)
}
,
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Ã[r' => [
'_' => 'updatesCombined',
'updates' => self::deserialize_type_array_of_Update(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'seq_start' => unpack('l', stream_get_contents($stream, 4))[1],
'seq' => unpack('l', stream_get_contents($stream, 4))[1],
],
'@B®t' => [
'_' => 'updates',
'updates' => self::deserialize_type_array_of_Update(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'seq' => unpack('l', stream_get_contents($stream, 4))[1],
],
'á�' => self::deserialize_updateShortSentMessage($stream),
'íÃ7¤' => self::deserialize_wallPaper($stream),
'A€à' => self::deserialize_wallPaperNoFile($stream),
'&ÏÊc' => [
'_' => 'account.autoDownloadSettings',
'low' => match (stream_get_contents($stream, 4)) {
'(v¥º' => self::deserialize_autoDownloadSettings($stream),
'¡Ïr0' => self::deserialize_type_AutoDownloadSettings(self::gzdecode($stream)),
default => self::err($stream)
}
,
'medium' => match (stream_get_contents($stream, 4)) {
'(v¥º' => self::deserialize_autoDownloadSettings($stream),
'¡Ïr0' => self::deserialize_type_AutoDownloadSettings(self::gzdecode($stream)),
default => self::err($stream)
}
,
'high' => match (stream_get_contents($stream, 4)) {
'(v¥º' => self::deserialize_autoDownloadSettings($stream),
'¡Ïr0' => self::deserialize_type_AutoDownloadSettings(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'qÈø6' => [
'_' => 'documentEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ØÄÔ�' => self::deserialize_document($stream),
'Ög ' => self::deserialize_theme($stream),
'"¶ô' => [
'_' => 'account.themesNotModified',
],
'mŒ=š' => [
'_' => 'account.themes',
'hash' => unpack('q', stream_get_contents($stream, 8))[1],
'themes' => self::deserialize_type_array_of_Theme(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'!‚âW' => self::deserialize_account___contentSettings($stream),
'ËLLs' => self::deserialize_globalPrivacySettings($stream),
'a˜wã' => [
'_' => 'account.resetPasswordFailedWait',
'retry_date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'}üïé' => [
'_' => 'account.resetPasswordRequestedWait',
'until_date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'>Ö&é' => [
'_' => 'account.resetPasswordOk',
],
'±èöû' => [
'_' => 'account.savedRingtonesNotModified',
],
'Å,éÁ' => [
'_' => 'account.savedRingtones',
'hash' => unpack('q', stream_get_contents($stream, 8))[1],
'ringtones' => self::deserialize_type_array_of_Document(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'm?&·' => [
'_' => 'account.savedRingtone',
],
'·~0' => [
'_' => 'account.savedRingtoneConverted',
'document' => match (stream_get_contents($stream, 4)) {
'qÈø6' => [
'_' => 'documentEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ØÄÔ�' => self::deserialize_document($stream),
'¡Ïr0' => self::deserialize_type_Document(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'EæŒÐ' => [
'_' => 'account.emojiStatusesNotModified',
],
'ÑgÄ�' => [
'_' => 'account.emojiStatuses',
'hash' => unpack('q', stream_get_contents($stream, 8))[1],
'statuses' => self::deserialize_type_array_of_EmojiStatus(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ú­H' => [
'_' => 'emojiListNotModified',
],
'Ñz' => [
'_' => 'emojiList',
'hash' => unpack('q', stream_get_contents($stream, 8))[1],
'document_id' => self::deserialize_type_array_of_long(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'�>L' => [
'_' => 'account.autoSaveSettings',
'users_settings' => match (stream_get_contents($stream, 4)) {
'Î4HÈ' => self::deserialize_autoSaveSettings($stream),
'¡Ïr0' => self::deserialize_type_AutoSaveSettings(self::gzdecode($stream)),
default => self::err($stream)
}
,
'chats_settings' => match (stream_get_contents($stream, 4)) {
'Î4HÈ' => self::deserialize_autoSaveSettings($stream),
'¡Ïr0' => self::deserialize_type_AutoSaveSettings(self::gzdecode($stream)),
default => self::err($stream)
}
,
'broadcasts_settings' => match (stream_get_contents($stream, 4)) {
'Î4HÈ' => self::deserialize_autoSaveSettings($stream),
'¡Ïr0' => self::deserialize_type_AutoSaveSettings(self::gzdecode($stream)),
default => self::err($stream)
}
,
'exceptions' => self::deserialize_type_array_of_AutoSaveException(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'{ø×' => [
'_' => 'account.connectedBots',
'connected_bots' => self::deserialize_type_array_of_ConnectedBot(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'.m;' => [
'_' => 'users.userFull',
'full_user' => match (stream_get_contents($stream, 4)) {
'…>ÿ"' => self::deserialize_userFull($stream),
'¡Ïr0' => self::deserialize_type_UserFull(self::gzdecode($stream)),
default => self::err($stream)
}
,
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'Ò©K·' => [
'_' => 'contacts.contactsNotModified',
],
'B~èê' => [
'_' => 'contacts.contacts',
'contacts' => self::deserialize_type_array_of_Contact(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'saved_count' => unpack('l', stream_get_contents($stream, 4))[1],
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
';Ðw' => [
'_' => 'contacts.importedContacts',
'imported' => self::deserialize_type_array_of_ImportedContact(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'popular_invites' => self::deserialize_type_array_of_PopularContact(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'retry_contacts' => self::deserialize_type_array_of_long(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'‘Þ
' => [
'_' => 'contacts.blocked',
'blocked' => self::deserialize_type_array_of_PeerBlocked(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'”Afá' => [
'_' => 'contacts.blockedSlice',
'count' => unpack('l', stream_get_contents($stream, 4))[1],
'blocked' => self::deserialize_type_array_of_PeerBlocked(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'�M³' => [
'_' => 'contacts.found',
'my_results' => self::deserialize_type_array_of_Peer(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'results' => self::deserialize_type_array_of_Peer(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'Ùz' => [
'_' => 'contacts.resolvedPeer',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'õn&Þ' => [
'_' => 'contacts.topPeersNotModified',
],
'¨r·p' => [
'_' => 'contacts.topPeers',
'categories' => self::deserialize_type_array_of_TopPeerCategoryPeers(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'�“,µ' => [
'_' => 'contacts.topPeersDisabled',
],
'›¿A' => [
'_' => 'exportedContactToken',
'url' => self::deserialize_string($stream),
'expires' => unpack('l', stream_get_contents($stream, 4))[1],
],
'‡ŽqŒ' => [
'_' => 'messages.messages',
'messages' => self::deserialize_type_array_of_Message(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'^hT:' => self::deserialize_messages___messagesSlice($stream),
'NºvÇ' => self::deserialize_messages___channelMessages($stream),
'!_St' => [
'_' => 'messages.messagesNotModified',
'count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'@lº' => [
'_' => 'messages.dialogs',
'dialogs' => self::deserialize_type_array_of_Dialog(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'messages' => self::deserialize_type_array_of_Message(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ó”àq' => [
'_' => 'messages.dialogsSlice',
'count' => unpack('l', stream_get_contents($stream, 4))[1],
'dialogs' => self::deserialize_type_array_of_Dialog(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'messages' => self::deserialize_type_array_of_Message(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'–åãð' => [
'_' => 'messages.dialogsNotModified',
'count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'…‘Ñ„' => [
'_' => 'messages.affectedMessages',
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Ñi\\´' => [
'_' => 'messages.affectedHistory',
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
'offset' => unpack('l', stream_get_contents($stream, 4))[1],
],
'M¹€h' => [
'_' => 'messages.peerSettings',
'settings' => match (stream_get_contents($stream, 4)) {
'¥' => self::deserialize_peerSettings($stream),
'¡Ïr0' => self::deserialize_type_PeerSettings(self::gzdecode($stream)),
default => self::err($stream)
}
,
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ÕŸÿd' => [
'_' => 'messages.chats',
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'DØœ' => [
'_' => 'messages.chatsSlice',
'count' => unpack('l', stream_get_contents($stream, 4))[1],
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'œÑ×å' => [
'_' => 'messages.chatFull',
'full_chat' => match (stream_get_contents($stream, 4)) {
'8ÓÉ' => self::deserialize_chatFull($stream),
'§TÀD' => self::deserialize_channelFull($stream),
'¡Ïr0' => self::deserialize_type_ChatFull(self::gzdecode($stream)),
default => self::err($stream)
}
,
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'5FâÀ' => [
'_' => 'messages.dhConfigNotModified',
'random' => self::deserialize_bytes($stream),
],
'Ý",' => [
'_' => 'messages.dhConfig',
'g' => unpack('l', stream_get_contents($stream, 4))[1],
'p' => self::deserialize_bytes($stream),
'version' => unpack('l', stream_get_contents($stream, 4))[1],
'random' => self::deserialize_bytes($stream),
],
' À~«' => [
'_' => 'encryptedChatEmpty',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'SY²f' => [
'_' => 'encryptedChatWaiting',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'admin_id' => unpack('q', stream_get_contents($stream, 8))[1],
'participant_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'LÙñH' => self::deserialize_encryptedChatRequested($stream),
'ÇÔða' => [
'_' => 'encryptedChat',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'admin_id' => unpack('q', stream_get_contents($stream, 8))[1],
'participant_id' => unpack('q', stream_get_contents($stream, 8))[1],
'g_a_or_b' => self::deserialize_bytes($stream),
'key_fingerprint' => stream_get_contents($stream, 8),
],
'E|' => self::deserialize_encryptedChatDiscarded($stream),
'5‰V' => [
'_' => 'messages.sentEncryptedMessage',
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'2ÿ“”' => [
'_' => 'messages.sentEncryptedFile',
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'file' => match (stream_get_contents($stream, 4)) {
'~IÂ' => [
'_' => 'encryptedFileEmpty',
],
'ØŒ' . "\0" . '¨' => [
'_' => 'encryptedFile',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'size' => unpack('q', stream_get_contents($stream, 8))[1],
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'key_fingerprint' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_EncryptedFile(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'"štñ' => [
'_' => 'messages.stickersNotModified',
],
'~ì¦0' => [
'_' => 'messages.stickers',
'hash' => unpack('q', stream_get_contents($stream, 8))[1],
'stickers' => self::deserialize_type_array_of_Document(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'Ãfè' => [
'_' => 'messages.allStickersNotModified',
],
'»Î»Í' => [
'_' => 'messages.allStickers',
'hash' => unpack('q', stream_get_contents($stream, 8))[1],
'sets' => self::deserialize_type_array_of_StickerSet(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
' cí=' => [
'_' => 'messageMediaEmpty',
],
'×PQi' => self::deserialize_messageMediaPhoto($stream),
'tÔàV' => [
'_' => 'messageMediaGeo',
'geo' => match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => self::deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'I)2p' => [
'_' => 'messageMediaContact',
'phone_number' => self::deserialize_string($stream),
'first_name' => self::deserialize_string($stream),
'last_name' => self::deserialize_string($stream),
'vcard' => self::deserialize_string($stream),
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'žô„Ÿ' => [
'_' => 'messageMediaUnsupported',
],
'-×ôL' => self::deserialize_messageMediaDocument($stream),
';ñÝ' => self::deserialize_messageMediaWebPage($stream),
'?SÀ.' => [
'_' => 'messageMediaVenue',
'geo' => match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => self::deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
,
'title' => self::deserialize_string($stream),
'address' => self::deserialize_string($stream),
'provider' => self::deserialize_string($stream),
'venue_id' => self::deserialize_string($stream),
'venue_type' => self::deserialize_string($stream),
],
'�±ý' => [
'_' => 'messageMediaGame',
'game' => match (stream_get_contents($stream, 4)) {
';eù½' => self::deserialize_game($stream),
'¡Ïr0' => self::deserialize_type_Game(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ÓH¥ö' => self::deserialize_messageMediaInvoice($stream),
'fÆ@¹' => self::deserialize_messageMediaGeoLive($stream),
'˜çÖK' => [
'_' => 'messageMediaPoll',
'poll' => match (stream_get_contents($stream, 4)) {
'a�á†' => self::deserialize_poll($stream),
'¡Ïr0' => self::deserialize_type_Poll(self::gzdecode($stream)),
default => self::err($stream)
}
,
'results' => match (stream_get_contents($stream, 4)) {
' $ßz' => self::deserialize_pollResults($stream),
'¡Ïr0' => self::deserialize_type_PollResults(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'‹å~?' => [
'_' => 'messageMediaDice',
'value' => unpack('l', stream_get_contents($stream, 4))[1],
'emoticon' => self::deserialize_string($stream),
],
'ƒbËh' => self::deserialize_messageMediaStory($stream),
'°…­Ú' => self::deserialize_messageMediaGiveaway($stream),
'h™Æ' => self::deserialize_messageMediaGiveawayResults($stream),
'¨´
' => self::deserialize_chatInviteExported($stream),
'·zí' => [
'_' => 'chatInvitePublicJoinRequests',
],
'|mhZ' => [
'_' => 'chatInviteAlready',
'chat' => match (stream_get_contents($stream, 4)) {
'e(V)' => self::deserialize_chatEmpty($stream),
'VòËA' => self::deserialize_chat($stream),
'§¡’e' => self::deserialize_chatForbidden($stream),
'�ü­
' => self::deserialize_channel($stream),
'Õ“Ô' => self::deserialize_channelForbidden($stream),
'¡Ïr0' => self::deserialize_type_Chat(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'@ìàÍ' => self::deserialize_chatInvite($stream),
'°\\ia' => [
'_' => 'chatInvitePeek',
'chat' => match (stream_get_contents($stream, 4)) {
'e(V)' => self::deserialize_chatEmpty($stream),
'VòËA' => self::deserialize_chat($stream),
'§¡’e' => self::deserialize_chatForbidden($stream),
'�ü­
' => self::deserialize_channel($stream),
'Õ“Ô' => self::deserialize_channelForbidden($stream),
'¡Ïr0' => self::deserialize_type_Chat(self::gzdecode($stream)),
default => self::err($stream)
}
,
'expires' => unpack('l', stream_get_contents($stream, 4))[1],
],
'?n' => self::deserialize_messages___stickerSet($stream),
'ë$ùÓ' => [
'_' => 'messages.stickerSetNotModified',
],
'(d8' => [
'_' => 'messages.stickerSetInstallResultSuccess',
],
'¨ä5' => [
'_' => 'messages.stickerSetInstallResultArchive',
'sets' => self::deserialize_type_array_of_StickerSetCovered(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'CõÄ¶' => [
'_' => 'messages.messageViews',
'views' => self::deserialize_type_array_of_MessageViews(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¢\\è' => [
'_' => 'messages.savedGifsNotModified',
],
'* „' => self::deserialize_messages___savedGifs($stream),
'öò!à' => self::deserialize_messages___botResults($stream),
'æÝµ&' => self::deserialize_messages___messageEditData($stream),
'¤^X6' => self::deserialize_messages___botCallbackAnswer($stream),
'TÃq3' => [
'_' => 'messages.peerDialogs',
'dialogs' => self::deserialize_type_array_of_Dialog(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'messages' => self::deserialize_type_array_of_Message(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'state' => match (stream_get_contents($stream, 4)) {
'>*l¥' => [
'_' => 'updates.state',
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'seq' => unpack('l', stream_get_contents($stream, 4))[1],
'unread_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_updates___State(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'fÜÆ' => [
'_' => 'messages.featuredStickersNotModified',
'count' => unpack('l', stream_get_contents($stream, 4))[1],
],
')8¾' => self::deserialize_messages___featuredStickers($stream),
'�ø' => [
'_' => 'messages.recentStickersNotModified',
],
'V|Óˆ' => self::deserialize_messages___recentStickers($stream),
'È©ËO' => [
'_' => 'messages.archivedStickers',
'count' => unpack('l', stream_get_contents($stream, 4))[1],
'sets' => self::deserialize_type_array_of_StickerSetCovered(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'™ý;š' => [
'_' => 'messages.highScores',
'scores' => self::deserialize_type_array_of_HighScore(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'½^ý' => [
'_' => 'messages.webPage',
'webpage' => match (stream_get_contents($stream, 4)) {
'ˆ!' => self::deserialize_webPageEmpty($stream),
'G>Ñ°' => self::deserialize_webPagePending($stream),
'²Eœè' => self::deserialize_webPage($stream),
'Ês' => self::deserialize_webPageNotModified($stream),
'¡Ïr0' => self::deserialize_type_WebPage(self::gzdecode($stream)),
default => self::err($stream)
}
,
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'Ó¦�ž' => [
'_' => 'messages.favedStickersNotModified',
],
'—µ,' => self::deserialize_messages___favedStickers($stream),
'~IÂ' => [
'_' => 'encryptedFileEmpty',
],
'ØŒ' . "\0" . '¨' => [
'_' => 'encryptedFile',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'size' => unpack('q', stream_get_contents($stream, 8))[1],
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'key_fingerprint' => unpack('l', stream_get_contents($stream, 4))[1],
],
']¶T' => [
'_' => 'messages.foundStickerSetsNotModified',
],
'Ò�ðŠ' => [
'_' => 'messages.foundStickerSets',
'hash' => unpack('q', stream_get_contents($stream, 8))[1],
'sets' => self::deserialize_type_array_of_StickerSetCovered(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'PâAð' => [
'_' => 'chatOnlines',
'onlines' => unpack('l', stream_get_contents($stream, 4))[1],
],
'½aÇ\\' => [
'_' => 'emojiKeywordsDifference',
'lang_code' => self::deserialize_string($stream),
'from_version' => unpack('l', stream_get_contents($stream, 4))[1],
'version' => unpack('l', stream_get_contents($stream, 4))[1],
'keywords' => self::deserialize_type_array_of_EmojiKeyword(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'�su¥' => [
'_' => 'emojiURL',
'url' => self::deserialize_string($stream),
],
':Ó’' => self::deserialize_urlAuthResultRequest($stream),
'NŒ�' => [
'_' => 'urlAuthResultAccepted',
'url' => self::deserialize_string($stream),
],
'ÛÖ©' => [
'_' => 'urlAuthResultDefault',
],
'NH™H' => self::deserialize_messages___votesList($stream),
'7Ù*' => self::deserialize_messages___dialogFilters($stream),
'‚4¦' => self::deserialize_messages___discussionMessage($stream),
'l>�ï' => [
'_' => 'messages.affectedFoundMessages',
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
'offset' => unpack('l', stream_get_contents($stream, 4))[1],
'messages' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¹·^' => self::deserialize_messages___historyImportParsed($stream),
'¯b' => [
'_' => 'messages.historyImport',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'Ì-Æ½' => [
'_' => 'messages.exportedChatInvites',
'count' => unpack('l', stream_get_contents($stream, 4))[1],
'invites' => self::deserialize_type_array_of_ExportedChatInvite(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'P¾q' => [
'_' => 'messages.exportedChatInvite',
'invite' => match (stream_get_contents($stream, 4)) {
'¨´
' => self::deserialize_chatInviteExported($stream),
'·zí' => [
'_' => 'chatInvitePublicJoinRequests',
],
'¡Ïr0' => self::deserialize_type_ExportedChatInvite(self::gzdecode($stream)),
default => self::err($stream)
}
,
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ï' . "\0" . '&"' => [
'_' => 'messages.exportedChatInviteReplaced',
'invite' => match (stream_get_contents($stream, 4)) {
'¨´
' => self::deserialize_chatInviteExported($stream),
'·zí' => [
'_' => 'chatInvitePublicJoinRequests',
],
'¡Ïr0' => self::deserialize_type_ExportedChatInvite(self::gzdecode($stream)),
default => self::err($stream)
}
,
'new_invite' => match (stream_get_contents($stream, 4)) {
'¨´
' => self::deserialize_chatInviteExported($stream),
'·zí' => [
'_' => 'chatInvitePublicJoinRequests',
],
'¡Ïr0' => self::deserialize_type_ExportedChatInvite(self::gzdecode($stream)),
default => self::err($stream)
}
,
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'×r›¶' => [
'_' => 'messages.chatAdminsWithInvites',
'admins' => self::deserialize_type_array_of_ChatAdminWithInvites(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'
°¶�' => [
'_' => 'messages.chatInviteImporters',
'count' => unpack('l', stream_get_contents($stream, 4))[1],
'importers' => self::deserialize_type_array_of_ChatInviteImporter(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'çM¢' => [
'_' => 'messages.checkedHistoryImportPeer',
'confirm_text' => self::deserialize_string($stream),
],
'<â~' => self::deserialize_messages___searchResultsCalendar($stream),
'¯+²S' => [
'_' => 'messages.searchResultsPositions',
'count' => unpack('l', stream_get_contents($stream, 4))[1],
'positions' => self::deserialize_type_array_of_SearchResultsPosition(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'-I½1' => self::deserialize_messages___messageReactionsList($stream),
'WŸ' => [
'_' => 'messages.availableReactionsNotModified',
],
'­:Žv' => [
'_' => 'messages.availableReactions',
'hash' => unpack('l', stream_get_contents($stream, 4))[1],
'reactions' => self::deserialize_type_array_of_AvailableReaction(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ø2Û3' => [
'_' => 'messages.translateResult',
'result' => self::deserialize_type_array_of_TextWithEntities(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'\\ŠØñ' => [
'_' => 'attachMenuBotsNotModified',
],
'ÀC<' => [
'_' => 'attachMenuBots',
'hash' => unpack('q', stream_get_contents($stream, 8))[1],
'bots' => self::deserialize_type_array_of_AttachMenuBot(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'f¿“' => [
'_' => 'attachMenuBotsBot',
'bot' => match (stream_get_contents($stream, 4)) {
'þ�Ù' => self::deserialize_attachMenuBot($stream),
'¡Ïr0' => self::deserialize_type_AttachMenuBot(self::gzdecode($stream)),
default => self::err($stream)
}
,
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'|U' => [
'_' => 'webViewResultUrl',
'query_id' => unpack('q', stream_get_contents($stream, 8))[1],
'url' => self::deserialize_string($stream),
],
'»v/ˆ' => [
'_' => 'simpleWebViewResultUrl',
'url' => self::deserialize_string($stream),
],
'Q”' => self::deserialize_webViewMessageSent($stream),
'WÙ¹Ï' => self::deserialize_messages___transcribedAudio($stream),
'ßÛo°' => [
'_' => 'messages.reactionsNotModified',
],
'÷ýê' => [
'_' => 'messages.reactions',
'hash' => unpack('q', stream_get_contents($stream, 8))[1],
'reactions' => self::deserialize_type_array_of_Reaction(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
' k´C' => [
'_' => 'defaultHistoryTTL',
'period' => unpack('l', stream_get_contents($stream, 4))[1],
],
'‡­´o' => [
'_' => 'messages.emojiGroupsNotModified',
],
'K¹ˆ' => [
'_' => 'messages.emojiGroups',
'hash' => unpack('l', stream_get_contents($stream, 4))[1],
'groups' => self::deserialize_type_array_of_EmojiGroup(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'õ­Pë' => self::deserialize_messages___botApp($stream),
'O<' => [
'_' => 'appWebViewResultUrl',
'url' => self::deserialize_string($stream),
],
'!â:ø' => [
'_' => 'messages.savedDialogs',
'dialogs' => self::deserialize_type_array_of_SavedDialog(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'messages' => self::deserialize_type_array_of_Message(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'Ù�ºD' => [
'_' => 'messages.savedDialogsSlice',
'count' => unpack('l', stream_get_contents($stream, 4))[1],
'dialogs' => self::deserialize_type_array_of_SavedDialog(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'messages' => self::deserialize_type_array_of_Message(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'èoÀ' => [
'_' => 'messages.savedDialogsNotModified',
'count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'ïY›ˆ' => [
'_' => 'messages.savedReactionTagsNotModified',
],
'
•Y2' => [
'_' => 'messages.savedReactionTags',
'tags' => self::deserialize_type_array_of_SavedReactionTag(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¬B¸;' => [
'_' => 'outboxReadDate',
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'•f�Æ' => [
'_' => 'messages.quickReplies',
'quick_replies' => self::deserialize_type_array_of_QuickReply(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'messages' => self::deserialize_type_array_of_Message(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'[ë‘_' => [
'_' => 'messages.quickRepliesNotModified',
],
'>*l¥' => [
'_' => 'updates.state',
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'seq' => unpack('l', stream_get_contents($stream, 4))[1],
'unread_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'8¡u]' => [
'_' => 'updates.differenceEmpty',
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'seq' => unpack('l', stream_get_contents($stream, 4))[1],
],
' œô' . "\0" . '' => [
'_' => 'updates.difference',
'new_messages' => self::deserialize_type_array_of_Message(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'new_encrypted_messages' => self::deserialize_type_array_of_EncryptedMessage(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'other_updates' => self::deserialize_type_array_of_Update(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'state' => match (stream_get_contents($stream, 4)) {
'>*l¥' => [
'_' => 'updates.state',
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'seq' => unpack('l', stream_get_contents($stream, 4))[1],
'unread_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_updates___State(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'�û¨' => [
'_' => 'updates.differenceSlice',
'new_messages' => self::deserialize_type_array_of_Message(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'new_encrypted_messages' => self::deserialize_type_array_of_EncryptedMessage(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'other_updates' => self::deserialize_type_array_of_Update(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'intermediate_state' => match (stream_get_contents($stream, 4)) {
'>*l¥' => [
'_' => 'updates.state',
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'seq' => unpack('l', stream_get_contents($stream, 4))[1],
'unread_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_updates___State(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'm�þJ' => [
'_' => 'updates.differenceTooLong',
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'û¯>' => self::deserialize_updates___channelDifferenceEmpty($stream),
'þÆ¼¤' => self::deserialize_updates___channelDifferenceTooLong($stream),
'Ngd ' => self::deserialize_updates___channelDifference($stream),
'¨,! ' => [
'_' => 'photos.photo',
'photo' => match (stream_get_contents($stream, 4)) {
'-²1#' => [
'_' => 'photoEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ezû' => self::deserialize_photo($stream),
'¡Ïr0' => self::deserialize_type_Photo(self::gzdecode($stream)),
default => self::err($stream)
}
,
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¥jÊ�' => [
'_' => 'photos.photos',
'photos' => self::deserialize_type_array_of_Photo(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'T' => [
'_' => 'photos.photosSlice',
'count' => unpack('l', stream_get_contents($stream, 4))[1],
'photos' => self::deserialize_type_array_of_Photo(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'Õj	' => [
'_' => 'upload.file',
'type' => match (stream_get_contents($stream, 4)) {
';–ª' => [
'_' => 'storage.fileUnknown',
],
'Ro¼@' => [
'_' => 'storage.filePartial',
],
'þ~' . "\0" . '' => [
'_' => 'storage.fileJpeg',
],
'ßªáÊ' => [
'_' => 'storage.fileGif',
],
'ÀcO
' => [
'_' => 'storage.filePng',
],
'�P®' => [
'_' => 'storage.filePdf',
],
'wŠR' => [
'_' => 'storage.fileMp3',
],
'¼ë	K' => [
'_' => 'storage.fileMov',
],
'ä Î³' => [
'_' => 'storage.fileMp4',
],
'LF�' => [
'_' => 'storage.fileWebp',
],
'¡Ïr0' => self::deserialize_type_storage___FileType(self::gzdecode($stream)),
default => self::err($stream)
}
,
'mtime' => unpack('l', stream_get_contents($stream, 4))[1],
'bytes' => self::deserialize_bytes($stream),
],
'DÚŒñ' => [
'_' => 'upload.fileCdnRedirect',
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'file_token' => self::deserialize_string($stream),
'encryption_key' => self::deserialize_string($stream),
'encryption_iv' => self::deserialize_string($stream),
'file_hashes' => self::deserialize_type_array_of_FileHash(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¼Sç!' => [
'_' => 'upload.webFile',
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'file_type' => match (stream_get_contents($stream, 4)) {
';–ª' => [
'_' => 'storage.fileUnknown',
],
'Ro¼@' => [
'_' => 'storage.filePartial',
],
'þ~' . "\0" . '' => [
'_' => 'storage.fileJpeg',
],
'ßªáÊ' => [
'_' => 'storage.fileGif',
],
'ÀcO
' => [
'_' => 'storage.filePng',
],
'�P®' => [
'_' => 'storage.filePdf',
],
'wŠR' => [
'_' => 'storage.fileMp3',
],
'¼ë	K' => [
'_' => 'storage.fileMov',
],
'ä Î³' => [
'_' => 'storage.fileMp4',
],
'LF�' => [
'_' => 'storage.fileWebp',
],
'¡Ïr0' => self::deserialize_type_storage___FileType(self::gzdecode($stream)),
default => self::err($stream)
}
,
'mtime' => unpack('l', stream_get_contents($stream, 4))[1],
'bytes' => self::deserialize_bytes($stream),
],
'nä¨î' => [
'_' => 'upload.cdnFileReuploadNeeded',
'request_token' => self::deserialize_bytes($stream),
],
'OÊŸ©' => [
'_' => 'upload.cdnFile',
'bytes' => self::deserialize_bytes($stream),
],
'$Ì' => self::deserialize_config($stream),
'uŽ' => [
'_' => 'nearestDc',
'country' => self::deserialize_string($stream),
'this_dc' => unpack('l', stream_get_contents($stream, 4))[1],
'nearest_dc' => unpack('l', stream_get_contents($stream, 4))[1],
],
'0Î»Ì' => self::deserialize_help___appUpdate($stream),
'6eZÄ' => [
'_' => 'help.noAppUpdate',
],
'xŸË' => [
'_' => 'help.inviteText',
'message' => self::deserialize_string($stream),
],
'öµÆ' => self::deserialize_help___support($stream),
'
ä%W' => [
'_' => 'cdnConfig',
'public_keys' => self::deserialize_type_array_of_CdnPublicKey(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'×' => [
'_' => 'help.recentMeUrls',
'urls' => self::deserialize_type_array_of_RecentMeUrl(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'Ÿ0ã' => [
'_' => 'help.termsOfServiceUpdateEmpty',
'expires' => unpack('l', stream_get_contents($stream, 4))[1],
],
'aùì(' => [
'_' => 'help.termsOfServiceUpdate',
'expires' => unpack('l', stream_get_contents($stream, 4))[1],
'terms_of_service' => match (stream_get_contents($stream, 4)) {
'
x' => self::deserialize_help___termsOfService($stream),
'¡Ïr0' => self::deserialize_type_help___TermsOfService(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'f¡¯f' => [
'_' => 'help.deepLinkInfoEmpty',
],
'2èNj' => self::deserialize_help___deepLinkInfo($stream),
'dÞ|' => [
'_' => 'help.appConfigNotModified',
],
'.xÝ' => [
'_' => 'help.appConfig',
'hash' => unpack('l', stream_get_contents($stream, 4))[1],
'config' => match (stream_get_contents($stream, 4)) {
'h{m?' => null,
'j^4Ç' => match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) },
'¤ßà+' => unpack('d', stream_get_contents($stream, 8))[1],
'zv·' => self::deserialize_string($stream),
'cGD÷' => $this->deserialize_type_array_of_JSONValue(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'�ÔÁ™' => self::deserialize_type_array_of_JSONObjectValue(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'¡Ïr0' => $this->deserialize_type_JSONValue(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Wô¹¿' => [
'_' => 'help.passportConfigNotModified',
],
'¯Ö˜ ' => [
'_' => 'help.passportConfig',
'hash' => unpack('l', stream_get_contents($stream, 4))[1],
'countries_langs' => match (stream_get_contents($stream, 4)) {
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'¡Ïr0' => self::deserialize_type_DataJSON(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ÉñŒ' => [
'_' => 'help.supportName',
'name' => self::deserialize_string($stream),
],
'í.®ó' => [
'_' => 'help.userInfoEmpty',
],
'X7ë' => [
'_' => 'help.userInfo',
'message' => self::deserialize_string($stream),
'entities' => self::deserialize_type_array_of_MessageEntity(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'author' => self::deserialize_string($stream),
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'u¬ö˜' => [
'_' => 'help.promoDataEmpty',
'expires' => unpack('l', stream_get_contents($stream, 4))[1],
],
'?y9Œ' => self::deserialize_help___promoData($stream),
'2Ì“' => [
'_' => 'help.countriesListNotModified',
],
'žuÐ‡' => [
'_' => 'help.countriesList',
'countries' => self::deserialize_type_array_of_help___Country(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'hash' => unpack('l', stream_get_contents($stream, 4))[1],
],
'œu4S' => [
'_' => 'help.premiumPromo',
'status_text' => self::deserialize_string($stream),
'status_entities' => self::deserialize_type_array_of_MessageEntity(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'video_sections' => self::deserialize_type_array_of_string(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'videos' => self::deserialize_type_array_of_Document(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'period_options' => self::deserialize_type_array_of_PremiumSubscriptionOption(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'Îõ¡+' => [
'_' => 'help.peerColorsNotModified',
],
'íø' . "\0" . '' => [
'_' => 'help.peerColors',
'hash' => unpack('l', stream_get_contents($stream, 4))[1],
'colors' => self::deserialize_type_array_of_help___PeerColorOption(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'Ì—' => [
'_' => 'help.timezonesListNotModified',
],
'qít{' => [
'_' => 'help.timezonesList',
'timezones' => self::deserialize_type_array_of_Timezone(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'hash' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¯þ°š' => [
'_' => 'channels.channelParticipants',
'count' => unpack('l', stream_get_contents($stream, 4))[1],
'participants' => self::deserialize_type_array_of_ChannelParticipant(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'é?ð' => [
'_' => 'channels.channelParticipantsNotModified',
],
'¸ß' => [
'_' => 'channels.channelParticipant',
'participant' => match (stream_get_contents($stream, 4)) {
'ÀÀ' => [
'_' => 'channelParticipant',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'§¿¨5' => self::deserialize_channelParticipantSelf($stream),
'Óæ/' => self::deserialize_channelParticipantCreator($stream),
'S»Ã4' => self::deserialize_channelParticipantAdmin($stream),
'Nøm' => self::deserialize_channelParticipantBanned($stream),
'ð' => [
'_' => 'channelParticipantLeft',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_ChannelParticipant(self::gzdecode($stream)),
default => self::err($stream)
}
,
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ô«]' => [
'_' => 'exportedMessageLink',
'link' => self::deserialize_string($stream),
'html' => self::deserialize_string($stream),
],
'M÷Ší' => [
'_' => 'channels.adminLogResults',
'events' => self::deserialize_type_array_of_ChannelAdminLogEvent(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'Åþ\'©' => [
'_' => 'messages.inactiveChats',
'dates' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'‡îÉ' => self::deserialize_messages___sponsoredMessages($stream),
'I9' => [
'_' => 'messages.sponsoredMessagesEmpty',
],
'Æ°–ô' => [
'_' => 'channels.sendAsPeers',
'peers' => self::deserialize_type_array_of_SendAsPeer(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'Óv6' => self::deserialize_messages___forumTopics($stream),
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'ˆ¥3u' => [
'_' => 'botMenuButtonDefault',
],
'ÂXB' => [
'_' => 'botMenuButtonCommands',
],
'æ|µÇ' => [
'_' => 'botMenuButton',
'text' => self::deserialize_string($stream),
'url' => self::deserialize_string($stream),
],
'°u§è' => [
'_' => 'bots.botInfo',
'name' => self::deserialize_string($stream),
'about' => self::deserialize_string($stream),
'description' => self::deserialize_string($stream),
],
'Q‡ ' => self::deserialize_payments___paymentForm($stream),
'þÄp' => self::deserialize_payments___paymentReceipt($stream),
'ƒEÑ' => self::deserialize_payments___validatedRequestedInfo($stream),
'�_N' => [
'_' => 'payments.paymentResult',
'updates' => match (stream_get_contents($stream, 4)) {
'~¯ã' => [
'_' => 'updatesTooLong',
],
'øÇ;1' => self::deserialize_updateShortMessage($stream),
'¥îmM' => self::deserialize_updateShortChatMessage($stream),
'ÁÞÔx' => [
'_' => 'updateShort',
'update' => match (stream_get_contents($stream, 4)) {
'ý
+' => [
'_' => 'updateNewMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Ö¿�N' => [
'_' => 'updateMessageID',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'random_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'å°¢' => [
'_' => 'updateDeleteMessages',
'messages' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'…À' => [
'_' => 'updateUserTyping',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'action' => match (stream_get_contents($stream, 4)) {
'Nt¿' => [
'_' => 'sendMessageTypingAction',
],
'õÈ^ý' => [
'_' => 'sendMessageCancelAction',
],
'oÖ‡¡' => [
'_' => 'sendMessageRecordVideoAction',
],
'ì:vé' => [
'_' => 'sendMessageUploadVideoAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'÷s/Õ' => [
'_' => 'sendMessageRecordAudioAction',
],
'«×Qó' => [
'_' => 'sendMessageUploadAudioAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'&JÓÑ' => [
'_' => 'sendMessageUploadPhotoAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'äÙª' => [
'_' => 'sendMessageUploadDocumentAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡‹o' => [
'_' => 'sendMessageGeoLocationAction',
],
'o¼Œb' => [
'_' => 'sendMessageChooseContactAction',
],
'H�jÝ' => [
'_' => 'sendMessageGamePlayAction',
],
'¼òˆ' => [
'_' => 'sendMessageRecordRoundAction',
],
'f>$' => [
'_' => 'sendMessageUploadRoundAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'…",Ù' => [
'_' => 'speakingInGroupCallAction',
],
'F’ÚÛ' => [
'_' => 'sendMessageHistoryImportAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'±ÆZ°' => [
'_' => 'sendMessageChooseStickerAction',
],
'Ë+—%' => [
'_' => 'sendMessageEmojiInteraction',
'emoticon' => self::deserialize_string($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'interaction' => match (stream_get_contents($stream, 4)) {
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'¡Ïr0' => self::deserialize_type_DataJSON(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'.�e¶' => [
'_' => 'sendMessageEmojiInteractionSeen',
'emoticon' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_SendMessageAction(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ðzHƒ' => [
'_' => 'updateChatUserTyping',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'from_id' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'action' => match (stream_get_contents($stream, 4)) {
'Nt¿' => [
'_' => 'sendMessageTypingAction',
],
'õÈ^ý' => [
'_' => 'sendMessageCancelAction',
],
'oÖ‡¡' => [
'_' => 'sendMessageRecordVideoAction',
],
'ì:vé' => [
'_' => 'sendMessageUploadVideoAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'÷s/Õ' => [
'_' => 'sendMessageRecordAudioAction',
],
'«×Qó' => [
'_' => 'sendMessageUploadAudioAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'&JÓÑ' => [
'_' => 'sendMessageUploadPhotoAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'äÙª' => [
'_' => 'sendMessageUploadDocumentAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡‹o' => [
'_' => 'sendMessageGeoLocationAction',
],
'o¼Œb' => [
'_' => 'sendMessageChooseContactAction',
],
'H�jÝ' => [
'_' => 'sendMessageGamePlayAction',
],
'¼òˆ' => [
'_' => 'sendMessageRecordRoundAction',
],
'f>$' => [
'_' => 'sendMessageUploadRoundAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'…",Ù' => [
'_' => 'speakingInGroupCallAction',
],
'F’ÚÛ' => [
'_' => 'sendMessageHistoryImportAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'±ÆZ°' => [
'_' => 'sendMessageChooseStickerAction',
],
'Ë+—%' => [
'_' => 'sendMessageEmojiInteraction',
'emoticon' => self::deserialize_string($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'interaction' => match (stream_get_contents($stream, 4)) {
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'¡Ïr0' => self::deserialize_type_DataJSON(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'.�e¶' => [
'_' => 'sendMessageEmojiInteractionSeen',
'emoticon' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_SendMessageAction(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'˜v' => [
'_' => 'updateChatParticipants',
'participants' => match (stream_get_contents($stream, 4)) {
'áÓc‡' => self::deserialize_chatParticipantsForbidden($stream),
'ø“¼<' => [
'_' => 'chatParticipants',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'participants' => self::deserialize_type_array_of_ChatParticipant(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_ChatParticipants(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Þø½å' => [
'_' => 'updateUserStatus',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'status' => match (stream_get_contents($stream, 4)) {
'IPÐ	' => [
'_' => 'userStatusEmpty',
],
'I9¹í' => [
'_' => 'userStatusOnline',
'expires' => unpack('l', stream_get_contents($stream, 4))[1],
],
'?pŒ' . "\0" . '' => [
'_' => 'userStatusOffline',
'was_online' => unpack('l', stream_get_contents($stream, 4))[1],
],
'È}{' => self::deserialize_userStatusRecently($stream),
'T' => self::deserialize_userStatusLastWeek($stream),
'w—‰e' => self::deserialize_userStatusLastMonth($stream),
'¡Ïr0' => self::deserialize_type_UserStatus(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'$‰„§' => [
'_' => 'updateUserName',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'first_name' => self::deserialize_string($stream),
'last_name' => self::deserialize_string($stream),
'usernames' => self::deserialize_type_array_of_Username(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ï«Q‰' => self::deserialize_updateNewAuthorization($stream),
'š½¼' => [
'_' => 'updateNewEncryptedMessage',
'message' => match (stream_get_contents($stream, 4)) {
'Áí' => [
'_' => 'encryptedMessage',
'random_id' => unpack('q', stream_get_contents($stream, 8))[1],
'chat_id' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'bytes' => self::deserialize_bytes($stream),
'file' => match (stream_get_contents($stream, 4)) {
'~IÂ' => [
'_' => 'encryptedFileEmpty',
],
'ØŒ' . "\0" . '¨' => [
'_' => 'encryptedFile',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'size' => unpack('q', stream_get_contents($stream, 8))[1],
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'key_fingerprint' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_EncryptedFile(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Ks#' => [
'_' => 'encryptedMessageService',
'random_id' => unpack('q', stream_get_contents($stream, 8))[1],
'chat_id' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'bytes' => self::deserialize_bytes($stream),
],
'¡Ïr0' => self::deserialize_type_EncryptedMessage(self::gzdecode($stream)),
default => self::err($stream)
}
,
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Vñ' => [
'_' => 'updateEncryptedChatTyping',
'chat_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'�è¢´' => [
'_' => 'updateEncryption',
'chat' => match (stream_get_contents($stream, 4)) {
' À~«' => [
'_' => 'encryptedChatEmpty',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'SY²f' => [
'_' => 'encryptedChatWaiting',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'admin_id' => unpack('q', stream_get_contents($stream, 8))[1],
'participant_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'LÙñH' => self::deserialize_encryptedChatRequested($stream),
'ÇÔða' => [
'_' => 'encryptedChat',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'admin_id' => unpack('q', stream_get_contents($stream, 8))[1],
'participant_id' => unpack('q', stream_get_contents($stream, 8))[1],
'g_a_or_b' => self::deserialize_bytes($stream),
'key_fingerprint' => stream_get_contents($stream, 8),
],
'E|' => self::deserialize_encryptedChatDiscarded($stream),
'¡Ïr0' => self::deserialize_type_EncryptedChat(self::gzdecode($stream)),
default => self::err($stream)
}
,
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'·%þ8' => [
'_' => 'updateEncryptedMessagesRead',
'chat_id' => unpack('l', stream_get_contents($stream, 4))[1],
'max_date' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'QTÚ=' => [
'_' => 'updateChatParticipantAdd',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'inviter_id' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
'w=/ã' => [
'_' => 'updateChatParticipantDelete',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
's˜^Ž' => [
'_' => 'updateDcOptions',
'dc_options' => self::deserialize_type_array_of_DcOption(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ïhÂ¾' => [
'_' => 'updateNotifySettings',
'peer' => match (stream_get_contents($stream, 4)) {
'ØÔŸ' => [
'_' => 'notifyPeer',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'L;È´' => [
'_' => 'notifyUsers',
],
'ÃÎÀ' => [
'_' => 'notifyChats',
],
'ïèÖ' => [
'_' => 'notifyBroadcasts',
],
'cn"' => [
'_' => 'notifyForumTopic',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'top_msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_NotifyPeer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'notify_settings' => match (stream_get_contents($stream, 4)) {
',b™' => self::deserialize_peerNotifySettings($stream),
'¡Ïr0' => self::deserialize_type_PeerNotifySettings(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'häë' => self::deserialize_updateServiceNotification($stream),
'*\';î' => [
'_' => 'updatePrivacy',
'key' => match (stream_get_contents($stream, 4)) {
'0«.¼' => [
'_' => 'privacyKeyStatusTimestamp',
],
'úmP' => [
'_' => 'privacyKeyChatInvite',
],
'{+f=' => [
'_' => 'privacyKeyPhoneCall',
],
'ÈI9' => [
'_' => 'privacyKeyPhoneP2P',
],
'£Vìi' => [
'_' => 'privacyKeyForwards',
],
'í–' => [
'_' => 'privacyKeyProfilePhoto',
],
'mäšÑ' => [
'_' => 'privacyKeyPhoneNumber',
],
'+ÔÿB' => [
'_' => 'privacyKeyAddedByPhone',
],
'ô—' => [
'_' => 'privacyKeyVoiceMessages',
],
'a·†¤' => [
'_' => 'privacyKeyAbout',
],
'¡Ïr0' => self::deserialize_type_PrivacyKey(self::gzdecode($stream)),
default => self::err($stream)
}
,
'rules' => self::deserialize_type_array_of_PrivacyRule(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'*I' => [
'_' => 'updateUserPhone',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'phone' => self::deserialize_string($stream),
],
'ßO—œ' => self::deserialize_updateReadHistoryInbox($stream),
'¿!//' => [
'_' => 'updateReadHistoryOutbox',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'max_id' => unpack('l', stream_get_contents($stream, 4))[1],
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'‰' => [
'_' => 'updateWebPage',
'webpage' => match (stream_get_contents($stream, 4)) {
'ˆ!' => self::deserialize_webPageEmpty($stream),
'G>Ñ°' => self::deserialize_webPagePending($stream),
'²Eœè' => self::deserialize_webPage($stream),
'Ês' => self::deserialize_webPageNotModified($stream),
'¡Ïr0' => self::deserialize_type_WebPage(self::gzdecode($stream)),
default => self::err($stream)
}
,
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'�q"ø' => self::deserialize_updateReadMessagesContents($stream),
'”�' => self::deserialize_updateChannelTooLong($stream),
'	L[c' => [
'_' => 'updateChannel',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'Ùºb' => [
'_' => 'updateNewChannelMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'n.’' => self::deserialize_updateReadChannelInbox($stream),
'[-Ã' => [
'_' => 'updateDeleteChannelMessages',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'messages' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¬&ò' => [
'_' => 'updateChannelMessageViews',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'views' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¢aÊ×' => [
'_' => 'updateChatParticipantAdmin',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'is_admin' => match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) },
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
'ª0Šh' => [
'_' => 'updateNewStickerSet',
'stickerset' => match (stream_get_contents($stream, 4)) {
'?n' => self::deserialize_messages___stickerSet($stream),
'ë$ùÓ' => [
'_' => 'messages.stickerSetNotModified',
],
'¡Ïr0' => self::deserialize_type_messages___StickerSet(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Ò²' => self::deserialize_updateStickerSetsOrder($stream),
'HÂ1' => self::deserialize_updateStickerSets($stream),
'4u“' => [
'_' => 'updateSavedGifs',
],
'œ7oI' => self::deserialize_updateBotInlineQuery($stream),
'*ñ' => self::deserialize_updateBotInlineSend($stream),
'÷M?' => [
'_' => 'updateEditChannelMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'�ÄÏ¹' => self::deserialize_updateBotCallbackQuery($stream),
'£pä' => [
'_' => 'updateEditMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'R�i' => self::deserialize_updateInlineBotCallbackQuery($stream),
'©™_·' => [
'_' => 'updateReadChannelOutbox',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'max_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'mìI' => self::deserialize_updateDraftMessage($stream),
'B\'W' => [
'_' => 'updateReadFeaturedStickers',
],
' ,Bš' => [
'_' => 'updateRecentStickers',
],
'Ý)¢' => [
'_' => 'updateConfig',
],
'�gT3' => [
'_' => 'updatePtsChanged',
],
'Ÿ©+/' => [
'_' => 'updateChannelWebPage',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'webpage' => match (stream_get_contents($stream, 4)) {
'ˆ!' => self::deserialize_webPageEmpty($stream),
'G>Ñ°' => self::deserialize_webPagePending($stream),
'²Eœè' => self::deserialize_webPage($stream),
'Ês' => self::deserialize_webPageNotModified($stream),
'¡Ïr0' => self::deserialize_type_WebPage(self::gzdecode($stream)),
default => self::err($stream)
}
,
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'åon' => self::deserialize_updateDialogPinned($stream),
'¢<ú' => self::deserialize_updatePinnedDialogs($stream),
'ÃÀƒ' => [
'_' => 'updateBotWebhookJSON',
'data' => match (stream_get_contents($stream, 4)) {
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'¡Ïr0' => self::deserialize_type_DataJSON(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¦@’›' => [
'_' => 'updateBotWebhookJSONQuery',
'query_id' => unpack('q', stream_get_contents($stream, 8))[1],
'data' => match (stream_get_contents($stream, 4)) {
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'¡Ïr0' => self::deserialize_type_DataJSON(self::gzdecode($stream)),
default => self::err($stream)
}
,
'timeout' => unpack('l', stream_get_contents($stream, 4))[1],
],
'}ý®µ' => [
'_' => 'updateBotShippingQuery',
'query_id' => unpack('q', stream_get_contents($stream, 8))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'payload' => self::deserialize_bytes($stream),
'shipping_address' => match (stream_get_contents($stream, 4)) {
'ëªŒ' => [
'_' => 'postAddress',
'street_line1' => self::deserialize_string($stream),
'street_line2' => self::deserialize_string($stream),
'city' => self::deserialize_string($stream),
'state' => self::deserialize_string($stream),
'country_iso2' => self::deserialize_string($stream),
'post_code' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_PostAddress(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'–šªŒ' => self::deserialize_updateBotPrecheckoutQuery($stream),
'k«' => [
'_' => 'updatePhoneCall',
'phone_call' => match (stream_get_contents($stream, 4)) {
'ÉfS' => [
'_' => 'phoneCallEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'o"Å' => self::deserialize_phoneCallWaiting($stream),
'í°' => self::deserialize_phoneCallRequested($stream),
'Ã`6' => self::deserialize_phoneCallAccepted($stream),
'g|–' => self::deserialize_phoneCall($stream),
'áMÊP' => self::deserialize_phoneCallDiscarded($stream),
'¡Ïr0' => self::deserialize_type_PhoneCall(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'dVF' => [
'_' => 'updateLangPackTooLong',
'lang_code' => self::deserialize_string($stream),
],
'M/V' => [
'_' => 'updateLangPack',
'difference' => match (stream_get_contents($stream, 4)) {
'öÁ…ó' => [
'_' => 'langPackDifference',
'lang_code' => self::deserialize_string($stream),
'from_version' => unpack('l', stream_get_contents($stream, 4))[1],
'version' => unpack('l', stream_get_contents($stream, 4))[1],
'strings' => self::deserialize_type_array_of_LangPackString(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => self::deserialize_type_LangPackDifference(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'm™å' => [
'_' => 'updateFavedStickers',
],
'])ê' => self::deserialize_updateChannelReadMessagesContents($stream),
'¾§„p' => [
'_' => 'updateContactsReset',
],
'˜Æ?²' => [
'_' => 'updateChannelAvailableMessages',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'available_min_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'ÃYdá' => self::deserialize_updateDialogUnreadMark($stream),
'{e¡¬' => self::deserialize_updateMessagePoll($stream),
'PÀT' => [
'_' => 'updateChatDefaultBannedRights',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'default_banned_rights' => match (stream_get_contents($stream, 4)) {
'Ÿ' => self::deserialize_chatBannedRights($stream),
'¡Ïr0' => self::deserialize_type_ChatBannedRights(self::gzdecode($stream)),
default => self::err($stream)
}
,
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
'À6' => [
'_' => 'updateFolderPeers',
'folder_peers' => self::deserialize_type_array_of_FolderPeer(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'fs~j' => [
'_' => 'updatePeerSettings',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'settings' => match (stream_get_contents($stream, 4)) {
'¥' => self::deserialize_peerSettings($stream),
'¡Ïr0' => self::deserialize_type_PeerSettings(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'°Ï¯´' => [
'_' => 'updatePeerLocated',
'peers' => self::deserialize_type_array_of_PeerLocated(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'û¥9' => [
'_' => 'updateNewScheduledMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'îl†�' => [
'_' => 'updateDeleteScheduledMessages',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'messages' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'£û‚' => [
'_' => 'updateTheme',
'theme' => match (stream_get_contents($stream, 4)) {
'Ög ' => self::deserialize_theme($stream),
'¡Ïr0' => self::deserialize_type_Theme(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'9¹‡' => [
'_' => 'updateGeoLiveViewed',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'‘æOV' => [
'_' => 'updateLoginToken',
],
'wô$' => [
'_' => 'updateMessagePollVote',
'poll_id' => unpack('q', stream_get_contents($stream, 8))[1],
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'options' => self::deserialize_type_array_of_bytes(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'}Þÿ&' => self::deserialize_updateDialogFilter($stream),
'!×¥' => [
'_' => 'updateDialogFilterOrder',
'order' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'O‘5' => [
'_' => 'updateDialogFilters',
],
'	¿a&' => [
'_' => 'updatePhoneCallSignalingData',
'phone_call_id' => unpack('q', stream_get_contents($stream, 8))[1],
'data' => self::deserialize_bytes($stream),
],
'ô\'šÒ' => [
'_' => 'updateChannelMessageForwards',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'forwards' => unpack('l', stream_get_contents($stream, 4))[1],
],
'F•±Ö' => self::deserialize_updateReadChannelDiscussionInbox($stream),
'|ž\\i' => [
'_' => 'updateReadChannelDiscussionOutbox',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'top_msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'read_max_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Rwàë' => self::deserialize_updatePeerBlocked($stream),
'#ÉˆŒ' => self::deserialize_updateChannelUserTyping($stream),
'µê…í' => self::deserialize_updatePinnedMessages($stream),
'†¹[' => self::deserialize_updatePinnedChannelMessages($stream),
'Njšø' => [
'_' => 'updateChat',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'NÛëò' => [
'_' => 'updateGroupCallParticipants',
'call' => match (stream_get_contents($stream, 4)) {
'„ªØ' => [
'_' => 'inputGroupCall',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputGroupCall(self::gzdecode($stream)),
default => self::err($stream)
}
,
'participants' => self::deserialize_type_array_of_GroupCallParticipant(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
'' . "\0" . 'E²' => [
'_' => 'updateGroupCall',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'call' => match (stream_get_contents($stream, 4)) {
'´¼€w' => [
'_' => 'groupCallDiscarded',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'duration' => unpack('l', stream_get_contents($stream, 4))[1],
],
'e—Õ' => self::deserialize_groupCall($stream),
'¡Ïr0' => self::deserialize_type_GroupCall(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¥¹›»' => self::deserialize_updatePeerHistoryTTL($stream),
':f‡Ð' => self::deserialize_updateChatParticipant($stream),
'»:]˜' => self::deserialize_updateChannelParticipant($stream),
'I
‡Ä' => [
'_' => 'updateBotStopped',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'stopped' => match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) },
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'‚9x' => self::deserialize_updateGroupCallConnection($stream),
'./qM' => [
'_' => 'updateBotCommands',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'bot_id' => unpack('q', stream_get_contents($stream, 8))[1],
'commands' => self::deserialize_type_array_of_BotCommand(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ÛÃcp' => [
'_' => 'updatePendingJoinRequests',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'requests_pending' => unpack('l', stream_get_contents($stream, 4))[1],
'recent_requesters' => self::deserialize_type_array_of_long(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'†©ß' => [
'_' => 'updateBotChatInviteRequester',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'about' => self::deserialize_string($stream),
'invite' => match (stream_get_contents($stream, 4)) {
'¨´
' => self::deserialize_chatInviteExported($stream),
'·zí' => [
'_' => 'chatInvitePublicJoinRequests',
],
'¡Ïr0' => self::deserialize_type_ExportedChatInvite(self::gzdecode($stream)),
default => self::err($stream)
}
,
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¸<^' => self::deserialize_updateMessageReactions($stream),
'¢·' => [
'_' => 'updateAttachMenuBots',
],
'�·’' => [
'_' => 'updateWebViewResultSent',
'query_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'X¸' => [
'_' => 'updateBotMenuButton',
'bot_id' => unpack('q', stream_get_contents($stream, 8))[1],
'button' => match (stream_get_contents($stream, 4)) {
'ˆ¥3u' => [
'_' => 'botMenuButtonDefault',
],
'ÂXB' => [
'_' => 'botMenuButtonCommands',
],
'æ|µÇ' => [
'_' => 'botMenuButton',
'text' => self::deserialize_string($stream),
'url' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_BotMenuButton(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'™¾Øt' => [
'_' => 'updateSavedRingtones',
],
'ZÍ„' . "\0" . '' => self::deserialize_updateTranscribedAudio($stream),
'lILû' => [
'_' => 'updateReadFeaturedEmojiStickers',
],
'™57(' => [
'_' => 'updateUserEmojiStatus',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'emoji_status' => match (stream_get_contents($stream, 4)) {
'®á-' => [
'_' => 'emojiStatusEmpty',
],
'�a›’' => [
'_' => 'emojiStatus',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'Ç¨0ú' => [
'_' => 'emojiStatusUntil',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'until' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_EmojiStatus(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ÛCô0' => [
'_' => 'updateRecentEmojiStatuses',
],
'ôcxo' => [
'_' => 'updateRecentReactions',
],
'…Ïü†' => self::deserialize_updateMoveStickerSetToTop($stream),
'Œ©sZ' => [
'_' => 'updateMessageExtendedMedia',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'extended_media' => match (stream_get_contents($stream, 4)) {
'ÈŒb­' => self::deserialize_messageExtendedMediaPreview($stream),
'dœGî' => [
'_' => 'messageExtendedMedia',
'media' => match (stream_get_contents($stream, 4)) {
' cí=' => [
'_' => 'messageMediaEmpty',
],
'×PQi' => self::deserialize_messageMediaPhoto($stream),
'tÔàV' => [
'_' => 'messageMediaGeo',
'geo' => match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => self::deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'I)2p' => [
'_' => 'messageMediaContact',
'phone_number' => self::deserialize_string($stream),
'first_name' => self::deserialize_string($stream),
'last_name' => self::deserialize_string($stream),
'vcard' => self::deserialize_string($stream),
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'žô„Ÿ' => [
'_' => 'messageMediaUnsupported',
],
'-×ôL' => self::deserialize_messageMediaDocument($stream),
';ñÝ' => self::deserialize_messageMediaWebPage($stream),
'?SÀ.' => [
'_' => 'messageMediaVenue',
'geo' => match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => self::deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
,
'title' => self::deserialize_string($stream),
'address' => self::deserialize_string($stream),
'provider' => self::deserialize_string($stream),
'venue_id' => self::deserialize_string($stream),
'venue_type' => self::deserialize_string($stream),
],
'�±ý' => [
'_' => 'messageMediaGame',
'game' => match (stream_get_contents($stream, 4)) {
';eù½' => self::deserialize_game($stream),
'¡Ïr0' => self::deserialize_type_Game(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ÓH¥ö' => self::deserialize_messageMediaInvoice($stream),
'fÆ@¹' => self::deserialize_messageMediaGeoLive($stream),
'˜çÖK' => [
'_' => 'messageMediaPoll',
'poll' => match (stream_get_contents($stream, 4)) {
'a�á†' => self::deserialize_poll($stream),
'¡Ïr0' => self::deserialize_type_Poll(self::gzdecode($stream)),
default => self::err($stream)
}
,
'results' => match (stream_get_contents($stream, 4)) {
' $ßz' => self::deserialize_pollResults($stream),
'¡Ïr0' => self::deserialize_type_PollResults(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'‹å~?' => [
'_' => 'messageMediaDice',
'value' => unpack('l', stream_get_contents($stream, 4))[1],
'emoticon' => self::deserialize_string($stream),
],
'ƒbËh' => self::deserialize_messageMediaStory($stream),
'°…­Ú' => self::deserialize_messageMediaGiveaway($stream),
'h™Æ' => self::deserialize_messageMediaGiveawayResults($stream),
'¡Ïr0' => self::deserialize_type_MessageMedia(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_MessageExtendedMedia(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ãû.' => self::deserialize_updateChannelPinnedTopic($stream),
'†þ' => self::deserialize_updateChannelPinnedTopics($stream),
'8”R ' => [
'_' => 'updateUser',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'—°ì' => [
'_' => 'updateAutoSaveSettings',
],
'ÖŠðÌ' => [
'_' => 'updateGroupInvitePrivacyForbidden',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'˜·³u' => [
'_' => 'updateStory',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'story' => match (stream_get_contents($stream, 4)) {
'OîæQ' => [
'_' => 'storyItemDeleted',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'É­ÿ' => self::deserialize_storyItemSkipped($stream),
'$j²y' => self::deserialize_storyItem($stream),
'¡Ïr0' => self::deserialize_type_StoryItem(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'+“N÷' => [
'_' => 'updateReadStories',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'max_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¹5ó' => [
'_' => 'updateStoryID',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'random_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÁM,' => [
'_' => 'updateStoriesStealthMode',
'stealth_mode' => match (stream_get_contents($stream, 4)) {
'ý\'.q' => self::deserialize_storiesStealthMode($stream),
'¡Ïr0' => self::deserialize_type_StoriesStealthMode(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ƒvb}' => [
'_' => 'updateSentStoryReaction',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'story_id' => unpack('l', stream_get_contents($stream, 4))[1],
'reaction' => match (stream_get_contents($stream, 4)) {
'Ôõy' => [
'_' => 'reactionEmpty',
],
'¸†"' => [
'_' => 'reactionEmoji',
'emoticon' => self::deserialize_string($stream),
],
'sü5‰' => [
'_' => 'reactionCustomEmoji',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_Reaction(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'œÔM�' => [
'_' => 'updateBotChatBoost',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'boost' => match (stream_get_contents($stream, 4)) {
'qŒ*' => self::deserialize_boost($stream),
'¡Ïr0' => self::deserialize_type_Boost(self::gzdecode($stream)),
default => self::err($stream)
}
,
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
' ‰¶' => [
'_' => 'updateChannelViewForumAsMessages',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'enabled' => match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) },
],
'?®' => self::deserialize_updatePeerWallpaper($stream),
'ÎÓ!¬' => [
'_' => 'updateBotMessageReaction',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'actor' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'old_reactions' => self::deserialize_type_array_of_Reaction(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'new_reactions' => self::deserialize_type_array_of_Reaction(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'YwË	' => [
'_' => 'updateBotMessageReactions',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'reactions' => self::deserialize_type_array_of_ReactionCount(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'tž¯®' => self::deserialize_updateSavedDialogPinned($stream),
'¦…lh' => self::deserialize_updatePinnedSavedDialogs($stream),
'2tÆ9' => [
'_' => 'updateSavedReactionTags',
],
'Ôibñ' => [
'_' => 'updateSmsJob',
'job_id' => self::deserialize_string($stream),
],
'²
Gù' => [
'_' => 'updateQuickReplies',
'quick_replies' => self::deserialize_type_array_of_QuickReply(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'§=õ' => [
'_' => 'updateNewQuickReply',
'quick_reply' => match (stream_get_contents($stream, 4)) {
'+—' => [
'_' => 'quickReply',
'shortcut_id' => unpack('l', stream_get_contents($stream, 4))[1],
'shortcut' => self::deserialize_string($stream),
'top_message' => unpack('l', stream_get_contents($stream, 4))[1],
'count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_QuickReply(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ìñæS' => [
'_' => 'updateDeleteQuickReply',
'shortcut_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'>' => [
'_' => 'updateQuickReplyMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ÍçoV' => [
'_' => 'updateDeleteQuickReplyMessages',
'shortcut_id' => unpack('l', stream_get_contents($stream, 4))[1],
'messages' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => self::deserialize_type_Update(self::gzdecode($stream)),
default => self::err($stream)
}
,
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Ã[r' => [
'_' => 'updatesCombined',
'updates' => self::deserialize_type_array_of_Update(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'seq_start' => unpack('l', stream_get_contents($stream, 4))[1],
'seq' => unpack('l', stream_get_contents($stream, 4))[1],
],
'@B®t' => [
'_' => 'updates',
'updates' => self::deserialize_type_array_of_Update(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'seq' => unpack('l', stream_get_contents($stream, 4))[1],
],
'á�' => self::deserialize_updateShortSentMessage($stream),
'¡Ïr0' => self::deserialize_type_Updates(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'9AØ' => [
'_' => 'payments.paymentVerificationNeeded',
'url' => self::deserialize_string($stream),
],
'<ä�û' => self::deserialize_payments___savedInfo($stream),
'så$>' => [
'_' => 'payments.bankCardData',
'title' => self::deserialize_string($stream),
'open_urls' => self::deserialize_type_array_of_BankCardOpenUrl(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ÙËÐ®' => [
'_' => 'payments.exportedInvoice',
'url' => self::deserialize_string($stream),
],
'–J(' => self::deserialize_payments___checkedGiftCode($stream),
' ÚgC' => self::deserialize_payments___giveawayInfo($stream),
'pUÍ' . "\0" . '' => self::deserialize_payments___giveawayInfoResults($stream),
'? þ…' => [
'_' => 'stickers.suggestedShortName',
'short_name' => self::deserialize_string($stream),
],
'@á‚ì' => [
'_' => 'phone.phoneCall',
'phone_call' => match (stream_get_contents($stream, 4)) {
'ÉfS' => [
'_' => 'phoneCallEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'o"Å' => self::deserialize_phoneCallWaiting($stream),
'í°' => self::deserialize_phoneCallRequested($stream),
'Ã`6' => self::deserialize_phoneCallAccepted($stream),
'g|–' => self::deserialize_phoneCall($stream),
'áMÊP' => self::deserialize_phoneCallDiscarded($stream),
'¡Ïr0' => self::deserialize_type_PhoneCall(self::gzdecode($stream)),
default => self::err($stream)
}
,
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'­zrž' => [
'_' => 'phone.groupCall',
'call' => match (stream_get_contents($stream, 4)) {
'´¼€w' => [
'_' => 'groupCallDiscarded',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'duration' => unpack('l', stream_get_contents($stream, 4))[1],
],
'e—Õ' => self::deserialize_groupCall($stream),
'¡Ïr0' => self::deserialize_type_GroupCall(self::gzdecode($stream)),
default => self::err($stream)
}
,
'participants' => self::deserialize_type_array_of_GroupCallParticipant(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'participants_next_offset' => self::deserialize_string($stream),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¶Qwô' => [
'_' => 'phone.groupParticipants',
'count' => unpack('l', stream_get_contents($stream, 4))[1],
'participants' => self::deserialize_type_array_of_GroupCallParticipant(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'next_offset' => self::deserialize_string($stream),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
'?bå¯' => [
'_' => 'phone.joinAsPeers',
'peers' => self::deserialize_type_array_of_Peer(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'XÑK ' => [
'_' => 'phone.exportedGroupCallInvite',
'link' => self::deserialize_string($stream),
],
'²‚äÐ' => [
'_' => 'phone.groupCallStreamChannels',
'channels' => self::deserialize_type_array_of_GroupCallStreamChannel(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'24¿-' => [
'_' => 'phone.groupCallStreamRtmpUrl',
'url' => self::deserialize_string($stream),
'key' => self::deserialize_string($stream),
],
'öÁ…ó' => [
'_' => 'langPackDifference',
'lang_code' => self::deserialize_string($stream),
'from_version' => unpack('l', stream_get_contents($stream, 4))[1],
'version' => unpack('l', stream_get_contents($stream, 4))[1],
'strings' => self::deserialize_type_array_of_LangPackString(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ã\\Êî' => self::deserialize_langPackLanguage($stream),
'ü¥l9' => [
'_' => 'stats.broadcastStats',
'period' => match (stream_get_contents($stream, 4)) {
'¯í7¶' => [
'_' => 'statsDateRangeDays',
'min_date' => unpack('l', stream_get_contents($stream, 4))[1],
'max_date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_StatsDateRangeDays(self::gzdecode($stream)),
default => self::err($stream)
}
,
'followers' => match (stream_get_contents($stream, 4)) {
'Þ¬CË' => [
'_' => 'statsAbsValueAndPrev',
'current' => unpack('d', stream_get_contents($stream, 8))[1],
'previous' => unpack('d', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_StatsAbsValueAndPrev(self::gzdecode($stream)),
default => self::err($stream)
}
,
'views_per_post' => match (stream_get_contents($stream, 4)) {
'Þ¬CË' => [
'_' => 'statsAbsValueAndPrev',
'current' => unpack('d', stream_get_contents($stream, 8))[1],
'previous' => unpack('d', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_StatsAbsValueAndPrev(self::gzdecode($stream)),
default => self::err($stream)
}
,
'shares_per_post' => match (stream_get_contents($stream, 4)) {
'Þ¬CË' => [
'_' => 'statsAbsValueAndPrev',
'current' => unpack('d', stream_get_contents($stream, 8))[1],
'previous' => unpack('d', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_StatsAbsValueAndPrev(self::gzdecode($stream)),
default => self::err($stream)
}
,
'reactions_per_post' => match (stream_get_contents($stream, 4)) {
'Þ¬CË' => [
'_' => 'statsAbsValueAndPrev',
'current' => unpack('d', stream_get_contents($stream, 8))[1],
'previous' => unpack('d', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_StatsAbsValueAndPrev(self::gzdecode($stream)),
default => self::err($stream)
}
,
'views_per_story' => match (stream_get_contents($stream, 4)) {
'Þ¬CË' => [
'_' => 'statsAbsValueAndPrev',
'current' => unpack('d', stream_get_contents($stream, 8))[1],
'previous' => unpack('d', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_StatsAbsValueAndPrev(self::gzdecode($stream)),
default => self::err($stream)
}
,
'shares_per_story' => match (stream_get_contents($stream, 4)) {
'Þ¬CË' => [
'_' => 'statsAbsValueAndPrev',
'current' => unpack('d', stream_get_contents($stream, 8))[1],
'previous' => unpack('d', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_StatsAbsValueAndPrev(self::gzdecode($stream)),
default => self::err($stream)
}
,
'reactions_per_story' => match (stream_get_contents($stream, 4)) {
'Þ¬CË' => [
'_' => 'statsAbsValueAndPrev',
'current' => unpack('d', stream_get_contents($stream, 8))[1],
'previous' => unpack('d', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_StatsAbsValueAndPrev(self::gzdecode($stream)),
default => self::err($stream)
}
,
'enabled_notifications' => match (stream_get_contents($stream, 4)) {
'à/ÎË' => [
'_' => 'statsPercentValue',
'part' => unpack('d', stream_get_contents($stream, 8))[1],
'total' => unpack('d', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_StatsPercentValue(self::gzdecode($stream)),
default => self::err($stream)
}
,
'growth_graph' => match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => self::deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
,
'followers_graph' => match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => self::deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
,
'mute_graph' => match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => self::deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
,
'top_hours_graph' => match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => self::deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
,
'interactions_graph' => match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => self::deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
,
'iv_interactions_graph' => match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => self::deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
,
'views_by_source_graph' => match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => self::deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
,
'new_followers_by_source_graph' => match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => self::deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
,
'languages_graph' => match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => self::deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
,
'reactions_by_emotion_graph' => match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => self::deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
,
'story_interactions_graph' => match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => self::deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
,
'story_reactions_by_emotion_graph' => match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => self::deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
,
'recent_posts_interactions' => self::deserialize_type_array_of_PostInteractionCounters(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'ùï' => [
'_' => 'stats.megagroupStats',
'period' => match (stream_get_contents($stream, 4)) {
'¯í7¶' => [
'_' => 'statsDateRangeDays',
'min_date' => unpack('l', stream_get_contents($stream, 4))[1],
'max_date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_StatsDateRangeDays(self::gzdecode($stream)),
default => self::err($stream)
}
,
'members' => match (stream_get_contents($stream, 4)) {
'Þ¬CË' => [
'_' => 'statsAbsValueAndPrev',
'current' => unpack('d', stream_get_contents($stream, 8))[1],
'previous' => unpack('d', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_StatsAbsValueAndPrev(self::gzdecode($stream)),
default => self::err($stream)
}
,
'messages' => match (stream_get_contents($stream, 4)) {
'Þ¬CË' => [
'_' => 'statsAbsValueAndPrev',
'current' => unpack('d', stream_get_contents($stream, 8))[1],
'previous' => unpack('d', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_StatsAbsValueAndPrev(self::gzdecode($stream)),
default => self::err($stream)
}
,
'viewers' => match (stream_get_contents($stream, 4)) {
'Þ¬CË' => [
'_' => 'statsAbsValueAndPrev',
'current' => unpack('d', stream_get_contents($stream, 8))[1],
'previous' => unpack('d', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_StatsAbsValueAndPrev(self::gzdecode($stream)),
default => self::err($stream)
}
,
'posters' => match (stream_get_contents($stream, 4)) {
'Þ¬CË' => [
'_' => 'statsAbsValueAndPrev',
'current' => unpack('d', stream_get_contents($stream, 8))[1],
'previous' => unpack('d', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_StatsAbsValueAndPrev(self::gzdecode($stream)),
default => self::err($stream)
}
,
'growth_graph' => match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => self::deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
,
'members_graph' => match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => self::deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
,
'new_members_by_source_graph' => match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => self::deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
,
'languages_graph' => match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => self::deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
,
'messages_graph' => match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => self::deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
,
'actions_graph' => match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => self::deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
,
'top_hours_graph' => match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => self::deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
,
'weekdays_graph' => match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => self::deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
,
'top_posters' => self::deserialize_type_array_of_StatsGroupTopPoster(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'top_admins' => self::deserialize_type_array_of_StatsGroupTopAdmin(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'top_inviters' => self::deserialize_type_array_of_StatsGroupTopInviter(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
' ~“' => self::deserialize_stats___publicForwards($stream),
'é' => [
'_' => 'stats.messageStats',
'views_graph' => match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => self::deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
,
'reactions_by_emotion_graph' => match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => self::deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'|ÍP' => [
'_' => 'stats.storyStats',
'views_graph' => match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => self::deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
,
'reactions_by_emotion_graph' => match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => self::deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¦ãæ' => [
'_' => 'chatlists.exportedChatlistInvite',
'filter' => match (stream_get_contents($stream, 4)) {
';Rµ_' => self::deserialize_dialogFilter($stream),
'®“26' => [
'_' => 'dialogFilterDefault',
],
'¤ŽâŸ' => self::deserialize_dialogFilterChatlist($stream),
'¡Ïr0' => self::deserialize_type_DialogFilter(self::gzdecode($stream)),
default => self::err($stream)
}
,
'invite' => match (stream_get_contents($stream, 4)) {
'¬�Q' => [
'_' => 'exportedChatlistInvite',
'flags' => unpack('V', stream_get_contents($stream, 4))[1],
'title' => self::deserialize_string($stream),
'url' => self::deserialize_string($stream),
'peers' => self::deserialize_type_array_of_Peer(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => self::deserialize_type_ExportedChatlistInvite(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¬�Q' => [
'_' => 'exportedChatlistInvite',
'flags' => unpack('V', stream_get_contents($stream, 4))[1],
'title' => self::deserialize_string($stream),
'url' => self::deserialize_string($stream),
'peers' => self::deserialize_type_array_of_Peer(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'Çm«' => [
'_' => 'chatlists.exportedInvites',
'invites' => self::deserialize_type_array_of_ExportedChatlistInvite(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'Yö‡ú' => [
'_' => 'chatlists.chatlistInviteAlready',
'filter_id' => unpack('l', stream_get_contents($stream, 4))[1],
'missing_peers' => self::deserialize_type_array_of_Peer(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'already_peers' => self::deserialize_type_array_of_Peer(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'�ƒÍ' => self::deserialize_chatlists___chatlistInvite($stream),
'�‡½“' => [
'_' => 'chatlists.chatlistUpdates',
'missing_peers' => self::deserialize_type_array_of_Peer(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'>þX' => [
'_' => 'stories.allStoriesNotModified',
'flags' => unpack('V', stream_get_contents($stream, 4))[1],
'state' => self::deserialize_string($stream),
'stealth_mode' => match (stream_get_contents($stream, 4)) {
'ý\'.q' => self::deserialize_storiesStealthMode($stream),
'¡Ïr0' => self::deserialize_type_StoriesStealthMode(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'�^ün' => self::deserialize_stories___allStories($stream),
'ÈÃØ]' => [
'_' => 'stories.stories',
'count' => unpack('l', stream_get_contents($stream, 4))[1],
'stories' => self::deserialize_type_array_of_StoryItem(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'Å�×Y' => self::deserialize_stories___storyViewsList($stream),
'ížÞ' => [
'_' => 'stories.storyViews',
'views' => self::deserialize_type_array_of_StoryViews(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
';É?' => [
'_' => 'exportedStoryLink',
'link' => self::deserialize_string($stream),
],
'h‡æÊ' => [
'_' => 'stories.peerStories',
'stories' => match (stream_get_contents($stream, 4)) {
'™é5š' => self::deserialize_peerStories($stream),
'¡Ïr0' => self::deserialize_type_PeerStories(self::gzdecode($stream)),
default => self::err($stream)
}
,
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'œx_ª' => self::deserialize_stories___storyReactionsList($stream),
'<aø†' => self::deserialize_premium___boostsList($stream),
'â(âš' => [
'_' => 'premium.myBoosts',
'my_boosts' => self::deserialize_type_array_of_MyBoost(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'zBYI' => self::deserialize_premium___boostsStatus($stream),
'ÏD‹Ü' => [
'_' => 'smsjobs.eligibleToJoin',
'terms_url' => self::deserialize_string($stream),
'monthly_sent_sms' => unpack('l', stream_get_contents($stream, 4))[1],
],
'‘‘î*' => self::deserialize_smsjobs___status($stream),
'¸î¡æ' => [
'_' => 'smsJob',
'job_id' => self::deserialize_string($stream),
'phone_number' => self::deserialize_string($stream),
'text' => self::deserialize_string($stream),
],
'¡Ïr0' => $this->deserialize_type_MethodResult(self::gzdecode($stream), $method),
'Äµ' => match ($method) {
'account.getAllSecureValues' => self::deserialize_type_array_of_SecureValue(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'account.getSecureValue' => self::deserialize_type_array_of_SecureValue(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'account.getMultiWallPapers' => self::deserialize_type_array_of_WallPaper(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users.getUsers' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users.getIsPremiumRequiredToContact' => self::deserialize_type_array_of_Bool(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'contacts.getContactIDs' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'contacts.getStatuses' => self::deserialize_type_array_of_ContactStatus(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'contacts.getSaved' => self::deserialize_type_array_of_SavedContact(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'messages.receivedMessages' => self::deserialize_type_array_of_ReceivedNotifyMessage(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'messages.receivedQueue' => self::deserialize_type_array_of_long(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'messages.getAttachedStickers' => self::deserialize_type_array_of_StickerSetCovered(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'messages.getSplitRanges' => self::deserialize_type_array_of_MessageRange(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'messages.getDialogUnreadMarks' => self::deserialize_type_array_of_DialogPeer(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'messages.getEmojiKeywordsLanguages' => self::deserialize_type_array_of_EmojiLanguage(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'messages.getSearchCounters' => self::deserialize_type_array_of_messages___SearchCounter(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'messages.getSuggestedDialogFilters' => self::deserialize_type_array_of_DialogFilterSuggested(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'messages.getMessageReadParticipants' => self::deserialize_type_array_of_ReadParticipantDate(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'messages.getCustomEmojiDocuments' => self::deserialize_type_array_of_Document(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'photos.deletePhotos' => self::deserialize_type_array_of_long(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'upload.reuploadCdnFile' => self::deserialize_type_array_of_FileHash(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'upload.getCdnFileHashes' => self::deserialize_type_array_of_FileHash(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'upload.getFileHashes' => self::deserialize_type_array_of_FileHash(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'bots.getBotCommands' => self::deserialize_type_array_of_BotCommand(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'payments.getPremiumGiftCodeOptions' => self::deserialize_type_array_of_PremiumGiftCodeOption(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'phone.checkGroupCall' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'langpack.getStrings' => self::deserialize_type_array_of_LangPackString(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'langpack.getLanguages' => self::deserialize_type_array_of_LangPackLanguage(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chatlists.getLeaveChatlistSuggestions' => self::deserialize_type_array_of_Peer(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'stories.deleteStories' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'stories.togglePinned' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'stories.readStories' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'stories.getPeerMaxIDs' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
},
default => self::err($stream)
}
;
    }
    private  function deserialize_rpc_result(mixed $stream): mixed {
$tmp = ['_' => 'rpc_result', 'req_msg_id' => $id = unpack('q', stream_get_contents($stream, 8))[1]];
$message = $this->connection->outgoing_messages[$id];
            $method = $message->constructor;
            if (isset($this->beforeMethodResponseDeserialization[$method])) {
                foreach ($this->beforeMethodResponseDeserialization[$method] as $callback) {
                    $callback($method);
                }
            }
            $tmp["result"] = match (stream_get_contents($stream, 4)) {
'%' . "\0" . '^' => self::deserialize_auth___sentCode($stream),
'Dþ�#' => [
'_' => 'auth.sentCodeSuccess',
'authorization' => match (stream_get_contents($stream, 4)) {
'ÔÀ¢.' => self::deserialize_auth___authorization($stream),
'š~tD' => self::deserialize_auth___authorizationSignUpRequired($stream),
'¡Ïr0' => self::deserialize_type_auth___Authorization(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ÔÀ¢.' => self::deserialize_auth___authorization($stream),
'š~tD' => self::deserialize_auth___authorizationSignUpRequired($stream),
'_ƒ¢Ã' => self::deserialize_auth___loggedOut($stream),
'7—y¼' => [
'_' => 'boolFalse',
],
'µur™' => [
'_' => 'boolTrue',
],
'¸â4´' => [
'_' => 'auth.exportedAuthorization',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'bytes' => self::deserialize_bytes($stream),
],
'¥Hy' => [
'_' => 'auth.passwordRecovery',
'email_pattern' => self::deserialize_string($stream),
],
'€Ÿb' => [
'_' => 'auth.loginToken',
'expires' => unpack('l', stream_get_contents($stream, 4))[1],
'token' => self::deserialize_bytes($stream),
],
'™Ž' => [
'_' => 'auth.loginTokenMigrateTo',
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'token' => self::deserialize_bytes($stream),
],
'^\\9' => [
'_' => 'auth.loginTokenSuccess',
'authorization' => match (stream_get_contents($stream, 4)) {
'ÔÀ¢.' => self::deserialize_auth___authorization($stream),
'š~tD' => self::deserialize_auth___authorizationSignUpRequired($stream),
'¡Ïr0' => self::deserialize_type_auth___Authorization(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Ö­' => self::deserialize_authorization($stream),
',b™' => self::deserialize_peerNotifySettings($stream),
'zK¼Ó' => self::deserialize_userEmpty($stream),
'8D\\!' => self::deserialize_user($stream),
'ƒ‘' => [
'_' => 'account.wallPapersNotModified',
],
'Œ…ÃÍ' => [
'_' => 'account.wallPapers',
'hash' => unpack('q', stream_get_contents($stream, 8))[1],
'wallpapers' => self::deserialize_type_array_of_WallPaper(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'EN P' => [
'_' => 'account.privacyRules',
'rules' => self::deserialize_type_array_of_PrivacyRule(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ß¯Ð¸' => [
'_' => 'accountDaysTTL',
'days' => unpack('l', stream_get_contents($stream, 4))[1],
],
' ŽÿK' => [
'_' => 'account.authorizations',
'authorization_ttl_days' => unpack('l', stream_get_contents($stream, 4))[1],
'authorizations' => self::deserialize_type_array_of_Authorization(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ûP{•' => self::deserialize_account___password($stream),
'å3\\š' => self::deserialize_account___passwordSettings($stream),
'4ýdÛ' => [
'_' => 'account.tmpPassword',
'tmp_password' => self::deserialize_bytes($stream),
'valid_until' => unpack('l', stream_get_contents($stream, 4))[1],
],
'üÉVí' => [
'_' => 'account.webAuthorizations',
'authorizations' => self::deserialize_type_array_of_WebAuthorization(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'Ê ' => self::deserialize_secureValue($stream),
'Ø.­' => self::deserialize_account___authorizationForm($stream),
'O…�' => [
'_' => 'account.sentEmailCode',
'email_pattern' => self::deserialize_string($stream),
'length' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Í–+' => [
'_' => 'account.emailVerified',
'email' => self::deserialize_string($stream),
],
'a»á' => [
'_' => 'account.emailVerifiedLogin',
'email' => self::deserialize_string($stream),
'sent_code' => match (stream_get_contents($stream, 4)) {
'%' . "\0" . '^' => self::deserialize_auth___sentCode($stream),
'Dþ�#' => [
'_' => 'auth.sentCodeSuccess',
'authorization' => match (stream_get_contents($stream, 4)) {
'ÔÀ¢.' => self::deserialize_auth___authorization($stream),
'š~tD' => self::deserialize_auth___authorizationSignUpRequired($stream),
'¡Ïr0' => self::deserialize_type_auth___Authorization(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_auth___SentCode(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'EºM' => [
'_' => 'account.takeout',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'~¯ã' => [
'_' => 'updatesTooLong',
],
'øÇ;1' => self::deserialize_updateShortMessage($stream),
'¥îmM' => self::deserialize_updateShortChatMessage($stream),
'ÁÞÔx' => [
'_' => 'updateShort',
'update' => match (stream_get_contents($stream, 4)) {
'ý
+' => [
'_' => 'updateNewMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Ö¿�N' => [
'_' => 'updateMessageID',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'random_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'å°¢' => [
'_' => 'updateDeleteMessages',
'messages' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'…À' => [
'_' => 'updateUserTyping',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'action' => match (stream_get_contents($stream, 4)) {
'Nt¿' => [
'_' => 'sendMessageTypingAction',
],
'õÈ^ý' => [
'_' => 'sendMessageCancelAction',
],
'oÖ‡¡' => [
'_' => 'sendMessageRecordVideoAction',
],
'ì:vé' => [
'_' => 'sendMessageUploadVideoAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'÷s/Õ' => [
'_' => 'sendMessageRecordAudioAction',
],
'«×Qó' => [
'_' => 'sendMessageUploadAudioAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'&JÓÑ' => [
'_' => 'sendMessageUploadPhotoAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'äÙª' => [
'_' => 'sendMessageUploadDocumentAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡‹o' => [
'_' => 'sendMessageGeoLocationAction',
],
'o¼Œb' => [
'_' => 'sendMessageChooseContactAction',
],
'H�jÝ' => [
'_' => 'sendMessageGamePlayAction',
],
'¼òˆ' => [
'_' => 'sendMessageRecordRoundAction',
],
'f>$' => [
'_' => 'sendMessageUploadRoundAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'…",Ù' => [
'_' => 'speakingInGroupCallAction',
],
'F’ÚÛ' => [
'_' => 'sendMessageHistoryImportAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'±ÆZ°' => [
'_' => 'sendMessageChooseStickerAction',
],
'Ë+—%' => [
'_' => 'sendMessageEmojiInteraction',
'emoticon' => self::deserialize_string($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'interaction' => match (stream_get_contents($stream, 4)) {
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'¡Ïr0' => self::deserialize_type_DataJSON(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'.�e¶' => [
'_' => 'sendMessageEmojiInteractionSeen',
'emoticon' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_SendMessageAction(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ðzHƒ' => [
'_' => 'updateChatUserTyping',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'from_id' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'action' => match (stream_get_contents($stream, 4)) {
'Nt¿' => [
'_' => 'sendMessageTypingAction',
],
'õÈ^ý' => [
'_' => 'sendMessageCancelAction',
],
'oÖ‡¡' => [
'_' => 'sendMessageRecordVideoAction',
],
'ì:vé' => [
'_' => 'sendMessageUploadVideoAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'÷s/Õ' => [
'_' => 'sendMessageRecordAudioAction',
],
'«×Qó' => [
'_' => 'sendMessageUploadAudioAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'&JÓÑ' => [
'_' => 'sendMessageUploadPhotoAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'äÙª' => [
'_' => 'sendMessageUploadDocumentAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡‹o' => [
'_' => 'sendMessageGeoLocationAction',
],
'o¼Œb' => [
'_' => 'sendMessageChooseContactAction',
],
'H�jÝ' => [
'_' => 'sendMessageGamePlayAction',
],
'¼òˆ' => [
'_' => 'sendMessageRecordRoundAction',
],
'f>$' => [
'_' => 'sendMessageUploadRoundAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'…",Ù' => [
'_' => 'speakingInGroupCallAction',
],
'F’ÚÛ' => [
'_' => 'sendMessageHistoryImportAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'±ÆZ°' => [
'_' => 'sendMessageChooseStickerAction',
],
'Ë+—%' => [
'_' => 'sendMessageEmojiInteraction',
'emoticon' => self::deserialize_string($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'interaction' => match (stream_get_contents($stream, 4)) {
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'¡Ïr0' => self::deserialize_type_DataJSON(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'.�e¶' => [
'_' => 'sendMessageEmojiInteractionSeen',
'emoticon' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_SendMessageAction(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'˜v' => [
'_' => 'updateChatParticipants',
'participants' => match (stream_get_contents($stream, 4)) {
'áÓc‡' => self::deserialize_chatParticipantsForbidden($stream),
'ø“¼<' => [
'_' => 'chatParticipants',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'participants' => self::deserialize_type_array_of_ChatParticipant(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_ChatParticipants(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Þø½å' => [
'_' => 'updateUserStatus',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'status' => match (stream_get_contents($stream, 4)) {
'IPÐ	' => [
'_' => 'userStatusEmpty',
],
'I9¹í' => [
'_' => 'userStatusOnline',
'expires' => unpack('l', stream_get_contents($stream, 4))[1],
],
'?pŒ' . "\0" . '' => [
'_' => 'userStatusOffline',
'was_online' => unpack('l', stream_get_contents($stream, 4))[1],
],
'È}{' => self::deserialize_userStatusRecently($stream),
'T' => self::deserialize_userStatusLastWeek($stream),
'w—‰e' => self::deserialize_userStatusLastMonth($stream),
'¡Ïr0' => self::deserialize_type_UserStatus(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'$‰„§' => [
'_' => 'updateUserName',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'first_name' => self::deserialize_string($stream),
'last_name' => self::deserialize_string($stream),
'usernames' => self::deserialize_type_array_of_Username(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ï«Q‰' => self::deserialize_updateNewAuthorization($stream),
'š½¼' => [
'_' => 'updateNewEncryptedMessage',
'message' => match (stream_get_contents($stream, 4)) {
'Áí' => [
'_' => 'encryptedMessage',
'random_id' => unpack('q', stream_get_contents($stream, 8))[1],
'chat_id' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'bytes' => self::deserialize_bytes($stream),
'file' => match (stream_get_contents($stream, 4)) {
'~IÂ' => [
'_' => 'encryptedFileEmpty',
],
'ØŒ' . "\0" . '¨' => [
'_' => 'encryptedFile',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'size' => unpack('q', stream_get_contents($stream, 8))[1],
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'key_fingerprint' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_EncryptedFile(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Ks#' => [
'_' => 'encryptedMessageService',
'random_id' => unpack('q', stream_get_contents($stream, 8))[1],
'chat_id' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'bytes' => self::deserialize_bytes($stream),
],
'¡Ïr0' => self::deserialize_type_EncryptedMessage(self::gzdecode($stream)),
default => self::err($stream)
}
,
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Vñ' => [
'_' => 'updateEncryptedChatTyping',
'chat_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'�è¢´' => [
'_' => 'updateEncryption',
'chat' => match (stream_get_contents($stream, 4)) {
' À~«' => [
'_' => 'encryptedChatEmpty',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'SY²f' => [
'_' => 'encryptedChatWaiting',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'admin_id' => unpack('q', stream_get_contents($stream, 8))[1],
'participant_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'LÙñH' => self::deserialize_encryptedChatRequested($stream),
'ÇÔða' => [
'_' => 'encryptedChat',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'admin_id' => unpack('q', stream_get_contents($stream, 8))[1],
'participant_id' => unpack('q', stream_get_contents($stream, 8))[1],
'g_a_or_b' => self::deserialize_bytes($stream),
'key_fingerprint' => stream_get_contents($stream, 8),
],
'E|' => self::deserialize_encryptedChatDiscarded($stream),
'¡Ïr0' => self::deserialize_type_EncryptedChat(self::gzdecode($stream)),
default => self::err($stream)
}
,
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'·%þ8' => [
'_' => 'updateEncryptedMessagesRead',
'chat_id' => unpack('l', stream_get_contents($stream, 4))[1],
'max_date' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'QTÚ=' => [
'_' => 'updateChatParticipantAdd',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'inviter_id' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
'w=/ã' => [
'_' => 'updateChatParticipantDelete',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
's˜^Ž' => [
'_' => 'updateDcOptions',
'dc_options' => self::deserialize_type_array_of_DcOption(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ïhÂ¾' => [
'_' => 'updateNotifySettings',
'peer' => match (stream_get_contents($stream, 4)) {
'ØÔŸ' => [
'_' => 'notifyPeer',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'L;È´' => [
'_' => 'notifyUsers',
],
'ÃÎÀ' => [
'_' => 'notifyChats',
],
'ïèÖ' => [
'_' => 'notifyBroadcasts',
],
'cn"' => [
'_' => 'notifyForumTopic',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'top_msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_NotifyPeer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'notify_settings' => match (stream_get_contents($stream, 4)) {
',b™' => self::deserialize_peerNotifySettings($stream),
'¡Ïr0' => self::deserialize_type_PeerNotifySettings(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'häë' => self::deserialize_updateServiceNotification($stream),
'*\';î' => [
'_' => 'updatePrivacy',
'key' => match (stream_get_contents($stream, 4)) {
'0«.¼' => [
'_' => 'privacyKeyStatusTimestamp',
],
'úmP' => [
'_' => 'privacyKeyChatInvite',
],
'{+f=' => [
'_' => 'privacyKeyPhoneCall',
],
'ÈI9' => [
'_' => 'privacyKeyPhoneP2P',
],
'£Vìi' => [
'_' => 'privacyKeyForwards',
],
'í–' => [
'_' => 'privacyKeyProfilePhoto',
],
'mäšÑ' => [
'_' => 'privacyKeyPhoneNumber',
],
'+ÔÿB' => [
'_' => 'privacyKeyAddedByPhone',
],
'ô—' => [
'_' => 'privacyKeyVoiceMessages',
],
'a·†¤' => [
'_' => 'privacyKeyAbout',
],
'¡Ïr0' => self::deserialize_type_PrivacyKey(self::gzdecode($stream)),
default => self::err($stream)
}
,
'rules' => self::deserialize_type_array_of_PrivacyRule(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'*I' => [
'_' => 'updateUserPhone',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'phone' => self::deserialize_string($stream),
],
'ßO—œ' => self::deserialize_updateReadHistoryInbox($stream),
'¿!//' => [
'_' => 'updateReadHistoryOutbox',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'max_id' => unpack('l', stream_get_contents($stream, 4))[1],
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'‰' => [
'_' => 'updateWebPage',
'webpage' => match (stream_get_contents($stream, 4)) {
'ˆ!' => self::deserialize_webPageEmpty($stream),
'G>Ñ°' => self::deserialize_webPagePending($stream),
'²Eœè' => self::deserialize_webPage($stream),
'Ês' => self::deserialize_webPageNotModified($stream),
'¡Ïr0' => self::deserialize_type_WebPage(self::gzdecode($stream)),
default => self::err($stream)
}
,
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'�q"ø' => self::deserialize_updateReadMessagesContents($stream),
'”�' => self::deserialize_updateChannelTooLong($stream),
'	L[c' => [
'_' => 'updateChannel',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'Ùºb' => [
'_' => 'updateNewChannelMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'n.’' => self::deserialize_updateReadChannelInbox($stream),
'[-Ã' => [
'_' => 'updateDeleteChannelMessages',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'messages' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¬&ò' => [
'_' => 'updateChannelMessageViews',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'views' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¢aÊ×' => [
'_' => 'updateChatParticipantAdmin',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'is_admin' => match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) },
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
'ª0Šh' => [
'_' => 'updateNewStickerSet',
'stickerset' => match (stream_get_contents($stream, 4)) {
'?n' => self::deserialize_messages___stickerSet($stream),
'ë$ùÓ' => [
'_' => 'messages.stickerSetNotModified',
],
'¡Ïr0' => self::deserialize_type_messages___StickerSet(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Ò²' => self::deserialize_updateStickerSetsOrder($stream),
'HÂ1' => self::deserialize_updateStickerSets($stream),
'4u“' => [
'_' => 'updateSavedGifs',
],
'œ7oI' => self::deserialize_updateBotInlineQuery($stream),
'*ñ' => self::deserialize_updateBotInlineSend($stream),
'÷M?' => [
'_' => 'updateEditChannelMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'�ÄÏ¹' => self::deserialize_updateBotCallbackQuery($stream),
'£pä' => [
'_' => 'updateEditMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'R�i' => self::deserialize_updateInlineBotCallbackQuery($stream),
'©™_·' => [
'_' => 'updateReadChannelOutbox',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'max_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'mìI' => self::deserialize_updateDraftMessage($stream),
'B\'W' => [
'_' => 'updateReadFeaturedStickers',
],
' ,Bš' => [
'_' => 'updateRecentStickers',
],
'Ý)¢' => [
'_' => 'updateConfig',
],
'�gT3' => [
'_' => 'updatePtsChanged',
],
'Ÿ©+/' => [
'_' => 'updateChannelWebPage',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'webpage' => match (stream_get_contents($stream, 4)) {
'ˆ!' => self::deserialize_webPageEmpty($stream),
'G>Ñ°' => self::deserialize_webPagePending($stream),
'²Eœè' => self::deserialize_webPage($stream),
'Ês' => self::deserialize_webPageNotModified($stream),
'¡Ïr0' => self::deserialize_type_WebPage(self::gzdecode($stream)),
default => self::err($stream)
}
,
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'åon' => self::deserialize_updateDialogPinned($stream),
'¢<ú' => self::deserialize_updatePinnedDialogs($stream),
'ÃÀƒ' => [
'_' => 'updateBotWebhookJSON',
'data' => match (stream_get_contents($stream, 4)) {
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'¡Ïr0' => self::deserialize_type_DataJSON(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¦@’›' => [
'_' => 'updateBotWebhookJSONQuery',
'query_id' => unpack('q', stream_get_contents($stream, 8))[1],
'data' => match (stream_get_contents($stream, 4)) {
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'¡Ïr0' => self::deserialize_type_DataJSON(self::gzdecode($stream)),
default => self::err($stream)
}
,
'timeout' => unpack('l', stream_get_contents($stream, 4))[1],
],
'}ý®µ' => [
'_' => 'updateBotShippingQuery',
'query_id' => unpack('q', stream_get_contents($stream, 8))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'payload' => self::deserialize_bytes($stream),
'shipping_address' => match (stream_get_contents($stream, 4)) {
'ëªŒ' => [
'_' => 'postAddress',
'street_line1' => self::deserialize_string($stream),
'street_line2' => self::deserialize_string($stream),
'city' => self::deserialize_string($stream),
'state' => self::deserialize_string($stream),
'country_iso2' => self::deserialize_string($stream),
'post_code' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_PostAddress(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'–šªŒ' => self::deserialize_updateBotPrecheckoutQuery($stream),
'k«' => [
'_' => 'updatePhoneCall',
'phone_call' => match (stream_get_contents($stream, 4)) {
'ÉfS' => [
'_' => 'phoneCallEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'o"Å' => self::deserialize_phoneCallWaiting($stream),
'í°' => self::deserialize_phoneCallRequested($stream),
'Ã`6' => self::deserialize_phoneCallAccepted($stream),
'g|–' => self::deserialize_phoneCall($stream),
'áMÊP' => self::deserialize_phoneCallDiscarded($stream),
'¡Ïr0' => self::deserialize_type_PhoneCall(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'dVF' => [
'_' => 'updateLangPackTooLong',
'lang_code' => self::deserialize_string($stream),
],
'M/V' => [
'_' => 'updateLangPack',
'difference' => match (stream_get_contents($stream, 4)) {
'öÁ…ó' => [
'_' => 'langPackDifference',
'lang_code' => self::deserialize_string($stream),
'from_version' => unpack('l', stream_get_contents($stream, 4))[1],
'version' => unpack('l', stream_get_contents($stream, 4))[1],
'strings' => self::deserialize_type_array_of_LangPackString(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => self::deserialize_type_LangPackDifference(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'm™å' => [
'_' => 'updateFavedStickers',
],
'])ê' => self::deserialize_updateChannelReadMessagesContents($stream),
'¾§„p' => [
'_' => 'updateContactsReset',
],
'˜Æ?²' => [
'_' => 'updateChannelAvailableMessages',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'available_min_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'ÃYdá' => self::deserialize_updateDialogUnreadMark($stream),
'{e¡¬' => self::deserialize_updateMessagePoll($stream),
'PÀT' => [
'_' => 'updateChatDefaultBannedRights',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'default_banned_rights' => match (stream_get_contents($stream, 4)) {
'Ÿ' => self::deserialize_chatBannedRights($stream),
'¡Ïr0' => self::deserialize_type_ChatBannedRights(self::gzdecode($stream)),
default => self::err($stream)
}
,
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
'À6' => [
'_' => 'updateFolderPeers',
'folder_peers' => self::deserialize_type_array_of_FolderPeer(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'fs~j' => [
'_' => 'updatePeerSettings',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'settings' => match (stream_get_contents($stream, 4)) {
'¥' => self::deserialize_peerSettings($stream),
'¡Ïr0' => self::deserialize_type_PeerSettings(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'°Ï¯´' => [
'_' => 'updatePeerLocated',
'peers' => self::deserialize_type_array_of_PeerLocated(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'û¥9' => [
'_' => 'updateNewScheduledMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'îl†�' => [
'_' => 'updateDeleteScheduledMessages',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'messages' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'£û‚' => [
'_' => 'updateTheme',
'theme' => match (stream_get_contents($stream, 4)) {
'Ög ' => self::deserialize_theme($stream),
'¡Ïr0' => self::deserialize_type_Theme(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'9¹‡' => [
'_' => 'updateGeoLiveViewed',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'‘æOV' => [
'_' => 'updateLoginToken',
],
'wô$' => [
'_' => 'updateMessagePollVote',
'poll_id' => unpack('q', stream_get_contents($stream, 8))[1],
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'options' => self::deserialize_type_array_of_bytes(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'}Þÿ&' => self::deserialize_updateDialogFilter($stream),
'!×¥' => [
'_' => 'updateDialogFilterOrder',
'order' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'O‘5' => [
'_' => 'updateDialogFilters',
],
'	¿a&' => [
'_' => 'updatePhoneCallSignalingData',
'phone_call_id' => unpack('q', stream_get_contents($stream, 8))[1],
'data' => self::deserialize_bytes($stream),
],
'ô\'šÒ' => [
'_' => 'updateChannelMessageForwards',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'forwards' => unpack('l', stream_get_contents($stream, 4))[1],
],
'F•±Ö' => self::deserialize_updateReadChannelDiscussionInbox($stream),
'|ž\\i' => [
'_' => 'updateReadChannelDiscussionOutbox',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'top_msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'read_max_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Rwàë' => self::deserialize_updatePeerBlocked($stream),
'#ÉˆŒ' => self::deserialize_updateChannelUserTyping($stream),
'µê…í' => self::deserialize_updatePinnedMessages($stream),
'†¹[' => self::deserialize_updatePinnedChannelMessages($stream),
'Njšø' => [
'_' => 'updateChat',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'NÛëò' => [
'_' => 'updateGroupCallParticipants',
'call' => match (stream_get_contents($stream, 4)) {
'„ªØ' => [
'_' => 'inputGroupCall',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputGroupCall(self::gzdecode($stream)),
default => self::err($stream)
}
,
'participants' => self::deserialize_type_array_of_GroupCallParticipant(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
'' . "\0" . 'E²' => [
'_' => 'updateGroupCall',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'call' => match (stream_get_contents($stream, 4)) {
'´¼€w' => [
'_' => 'groupCallDiscarded',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'duration' => unpack('l', stream_get_contents($stream, 4))[1],
],
'e—Õ' => self::deserialize_groupCall($stream),
'¡Ïr0' => self::deserialize_type_GroupCall(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¥¹›»' => self::deserialize_updatePeerHistoryTTL($stream),
':f‡Ð' => self::deserialize_updateChatParticipant($stream),
'»:]˜' => self::deserialize_updateChannelParticipant($stream),
'I
‡Ä' => [
'_' => 'updateBotStopped',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'stopped' => match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) },
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'‚9x' => self::deserialize_updateGroupCallConnection($stream),
'./qM' => [
'_' => 'updateBotCommands',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'bot_id' => unpack('q', stream_get_contents($stream, 8))[1],
'commands' => self::deserialize_type_array_of_BotCommand(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ÛÃcp' => [
'_' => 'updatePendingJoinRequests',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'requests_pending' => unpack('l', stream_get_contents($stream, 4))[1],
'recent_requesters' => self::deserialize_type_array_of_long(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'†©ß' => [
'_' => 'updateBotChatInviteRequester',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'about' => self::deserialize_string($stream),
'invite' => match (stream_get_contents($stream, 4)) {
'¨´
' => self::deserialize_chatInviteExported($stream),
'·zí' => [
'_' => 'chatInvitePublicJoinRequests',
],
'¡Ïr0' => self::deserialize_type_ExportedChatInvite(self::gzdecode($stream)),
default => self::err($stream)
}
,
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¸<^' => self::deserialize_updateMessageReactions($stream),
'¢·' => [
'_' => 'updateAttachMenuBots',
],
'�·’' => [
'_' => 'updateWebViewResultSent',
'query_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'X¸' => [
'_' => 'updateBotMenuButton',
'bot_id' => unpack('q', stream_get_contents($stream, 8))[1],
'button' => match (stream_get_contents($stream, 4)) {
'ˆ¥3u' => [
'_' => 'botMenuButtonDefault',
],
'ÂXB' => [
'_' => 'botMenuButtonCommands',
],
'æ|µÇ' => [
'_' => 'botMenuButton',
'text' => self::deserialize_string($stream),
'url' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_BotMenuButton(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'™¾Øt' => [
'_' => 'updateSavedRingtones',
],
'ZÍ„' . "\0" . '' => self::deserialize_updateTranscribedAudio($stream),
'lILû' => [
'_' => 'updateReadFeaturedEmojiStickers',
],
'™57(' => [
'_' => 'updateUserEmojiStatus',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'emoji_status' => match (stream_get_contents($stream, 4)) {
'®á-' => [
'_' => 'emojiStatusEmpty',
],
'�a›’' => [
'_' => 'emojiStatus',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'Ç¨0ú' => [
'_' => 'emojiStatusUntil',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'until' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_EmojiStatus(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ÛCô0' => [
'_' => 'updateRecentEmojiStatuses',
],
'ôcxo' => [
'_' => 'updateRecentReactions',
],
'…Ïü†' => self::deserialize_updateMoveStickerSetToTop($stream),
'Œ©sZ' => [
'_' => 'updateMessageExtendedMedia',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'extended_media' => match (stream_get_contents($stream, 4)) {
'ÈŒb­' => self::deserialize_messageExtendedMediaPreview($stream),
'dœGî' => [
'_' => 'messageExtendedMedia',
'media' => match (stream_get_contents($stream, 4)) {
' cí=' => [
'_' => 'messageMediaEmpty',
],
'×PQi' => self::deserialize_messageMediaPhoto($stream),
'tÔàV' => [
'_' => 'messageMediaGeo',
'geo' => match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => self::deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'I)2p' => [
'_' => 'messageMediaContact',
'phone_number' => self::deserialize_string($stream),
'first_name' => self::deserialize_string($stream),
'last_name' => self::deserialize_string($stream),
'vcard' => self::deserialize_string($stream),
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'žô„Ÿ' => [
'_' => 'messageMediaUnsupported',
],
'-×ôL' => self::deserialize_messageMediaDocument($stream),
';ñÝ' => self::deserialize_messageMediaWebPage($stream),
'?SÀ.' => [
'_' => 'messageMediaVenue',
'geo' => match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => self::deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
,
'title' => self::deserialize_string($stream),
'address' => self::deserialize_string($stream),
'provider' => self::deserialize_string($stream),
'venue_id' => self::deserialize_string($stream),
'venue_type' => self::deserialize_string($stream),
],
'�±ý' => [
'_' => 'messageMediaGame',
'game' => match (stream_get_contents($stream, 4)) {
';eù½' => self::deserialize_game($stream),
'¡Ïr0' => self::deserialize_type_Game(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ÓH¥ö' => self::deserialize_messageMediaInvoice($stream),
'fÆ@¹' => self::deserialize_messageMediaGeoLive($stream),
'˜çÖK' => [
'_' => 'messageMediaPoll',
'poll' => match (stream_get_contents($stream, 4)) {
'a�á†' => self::deserialize_poll($stream),
'¡Ïr0' => self::deserialize_type_Poll(self::gzdecode($stream)),
default => self::err($stream)
}
,
'results' => match (stream_get_contents($stream, 4)) {
' $ßz' => self::deserialize_pollResults($stream),
'¡Ïr0' => self::deserialize_type_PollResults(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'‹å~?' => [
'_' => 'messageMediaDice',
'value' => unpack('l', stream_get_contents($stream, 4))[1],
'emoticon' => self::deserialize_string($stream),
],
'ƒbËh' => self::deserialize_messageMediaStory($stream),
'°…­Ú' => self::deserialize_messageMediaGiveaway($stream),
'h™Æ' => self::deserialize_messageMediaGiveawayResults($stream),
'¡Ïr0' => self::deserialize_type_MessageMedia(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_MessageExtendedMedia(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ãû.' => self::deserialize_updateChannelPinnedTopic($stream),
'†þ' => self::deserialize_updateChannelPinnedTopics($stream),
'8”R ' => [
'_' => 'updateUser',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'—°ì' => [
'_' => 'updateAutoSaveSettings',
],
'ÖŠðÌ' => [
'_' => 'updateGroupInvitePrivacyForbidden',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'˜·³u' => [
'_' => 'updateStory',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'story' => match (stream_get_contents($stream, 4)) {
'OîæQ' => [
'_' => 'storyItemDeleted',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'É­ÿ' => self::deserialize_storyItemSkipped($stream),
'$j²y' => self::deserialize_storyItem($stream),
'¡Ïr0' => self::deserialize_type_StoryItem(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'+“N÷' => [
'_' => 'updateReadStories',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'max_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¹5ó' => [
'_' => 'updateStoryID',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'random_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÁM,' => [
'_' => 'updateStoriesStealthMode',
'stealth_mode' => match (stream_get_contents($stream, 4)) {
'ý\'.q' => self::deserialize_storiesStealthMode($stream),
'¡Ïr0' => self::deserialize_type_StoriesStealthMode(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ƒvb}' => [
'_' => 'updateSentStoryReaction',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'story_id' => unpack('l', stream_get_contents($stream, 4))[1],
'reaction' => match (stream_get_contents($stream, 4)) {
'Ôõy' => [
'_' => 'reactionEmpty',
],
'¸†"' => [
'_' => 'reactionEmoji',
'emoticon' => self::deserialize_string($stream),
],
'sü5‰' => [
'_' => 'reactionCustomEmoji',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_Reaction(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'œÔM�' => [
'_' => 'updateBotChatBoost',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'boost' => match (stream_get_contents($stream, 4)) {
'qŒ*' => self::deserialize_boost($stream),
'¡Ïr0' => self::deserialize_type_Boost(self::gzdecode($stream)),
default => self::err($stream)
}
,
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
' ‰¶' => [
'_' => 'updateChannelViewForumAsMessages',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'enabled' => match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) },
],
'?®' => self::deserialize_updatePeerWallpaper($stream),
'ÎÓ!¬' => [
'_' => 'updateBotMessageReaction',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'actor' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'old_reactions' => self::deserialize_type_array_of_Reaction(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'new_reactions' => self::deserialize_type_array_of_Reaction(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'YwË	' => [
'_' => 'updateBotMessageReactions',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'reactions' => self::deserialize_type_array_of_ReactionCount(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'tž¯®' => self::deserialize_updateSavedDialogPinned($stream),
'¦…lh' => self::deserialize_updatePinnedSavedDialogs($stream),
'2tÆ9' => [
'_' => 'updateSavedReactionTags',
],
'Ôibñ' => [
'_' => 'updateSmsJob',
'job_id' => self::deserialize_string($stream),
],
'²
Gù' => [
'_' => 'updateQuickReplies',
'quick_replies' => self::deserialize_type_array_of_QuickReply(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'§=õ' => [
'_' => 'updateNewQuickReply',
'quick_reply' => match (stream_get_contents($stream, 4)) {
'+—' => [
'_' => 'quickReply',
'shortcut_id' => unpack('l', stream_get_contents($stream, 4))[1],
'shortcut' => self::deserialize_string($stream),
'top_message' => unpack('l', stream_get_contents($stream, 4))[1],
'count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_QuickReply(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ìñæS' => [
'_' => 'updateDeleteQuickReply',
'shortcut_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'>' => [
'_' => 'updateQuickReplyMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ÍçoV' => [
'_' => 'updateDeleteQuickReplyMessages',
'shortcut_id' => unpack('l', stream_get_contents($stream, 4))[1],
'messages' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => self::deserialize_type_Update(self::gzdecode($stream)),
default => self::err($stream)
}
,
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Ã[r' => [
'_' => 'updatesCombined',
'updates' => self::deserialize_type_array_of_Update(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'seq_start' => unpack('l', stream_get_contents($stream, 4))[1],
'seq' => unpack('l', stream_get_contents($stream, 4))[1],
],
'@B®t' => [
'_' => 'updates',
'updates' => self::deserialize_type_array_of_Update(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'seq' => unpack('l', stream_get_contents($stream, 4))[1],
],
'á�' => self::deserialize_updateShortSentMessage($stream),
'íÃ7¤' => self::deserialize_wallPaper($stream),
'A€à' => self::deserialize_wallPaperNoFile($stream),
'&ÏÊc' => [
'_' => 'account.autoDownloadSettings',
'low' => match (stream_get_contents($stream, 4)) {
'(v¥º' => self::deserialize_autoDownloadSettings($stream),
'¡Ïr0' => self::deserialize_type_AutoDownloadSettings(self::gzdecode($stream)),
default => self::err($stream)
}
,
'medium' => match (stream_get_contents($stream, 4)) {
'(v¥º' => self::deserialize_autoDownloadSettings($stream),
'¡Ïr0' => self::deserialize_type_AutoDownloadSettings(self::gzdecode($stream)),
default => self::err($stream)
}
,
'high' => match (stream_get_contents($stream, 4)) {
'(v¥º' => self::deserialize_autoDownloadSettings($stream),
'¡Ïr0' => self::deserialize_type_AutoDownloadSettings(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'qÈø6' => [
'_' => 'documentEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ØÄÔ�' => self::deserialize_document($stream),
'Ög ' => self::deserialize_theme($stream),
'"¶ô' => [
'_' => 'account.themesNotModified',
],
'mŒ=š' => [
'_' => 'account.themes',
'hash' => unpack('q', stream_get_contents($stream, 8))[1],
'themes' => self::deserialize_type_array_of_Theme(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'!‚âW' => self::deserialize_account___contentSettings($stream),
'ËLLs' => self::deserialize_globalPrivacySettings($stream),
'a˜wã' => [
'_' => 'account.resetPasswordFailedWait',
'retry_date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'}üïé' => [
'_' => 'account.resetPasswordRequestedWait',
'until_date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'>Ö&é' => [
'_' => 'account.resetPasswordOk',
],
'±èöû' => [
'_' => 'account.savedRingtonesNotModified',
],
'Å,éÁ' => [
'_' => 'account.savedRingtones',
'hash' => unpack('q', stream_get_contents($stream, 8))[1],
'ringtones' => self::deserialize_type_array_of_Document(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'm?&·' => [
'_' => 'account.savedRingtone',
],
'·~0' => [
'_' => 'account.savedRingtoneConverted',
'document' => match (stream_get_contents($stream, 4)) {
'qÈø6' => [
'_' => 'documentEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ØÄÔ�' => self::deserialize_document($stream),
'¡Ïr0' => self::deserialize_type_Document(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'EæŒÐ' => [
'_' => 'account.emojiStatusesNotModified',
],
'ÑgÄ�' => [
'_' => 'account.emojiStatuses',
'hash' => unpack('q', stream_get_contents($stream, 8))[1],
'statuses' => self::deserialize_type_array_of_EmojiStatus(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ú­H' => [
'_' => 'emojiListNotModified',
],
'Ñz' => [
'_' => 'emojiList',
'hash' => unpack('q', stream_get_contents($stream, 8))[1],
'document_id' => self::deserialize_type_array_of_long(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'�>L' => [
'_' => 'account.autoSaveSettings',
'users_settings' => match (stream_get_contents($stream, 4)) {
'Î4HÈ' => self::deserialize_autoSaveSettings($stream),
'¡Ïr0' => self::deserialize_type_AutoSaveSettings(self::gzdecode($stream)),
default => self::err($stream)
}
,
'chats_settings' => match (stream_get_contents($stream, 4)) {
'Î4HÈ' => self::deserialize_autoSaveSettings($stream),
'¡Ïr0' => self::deserialize_type_AutoSaveSettings(self::gzdecode($stream)),
default => self::err($stream)
}
,
'broadcasts_settings' => match (stream_get_contents($stream, 4)) {
'Î4HÈ' => self::deserialize_autoSaveSettings($stream),
'¡Ïr0' => self::deserialize_type_AutoSaveSettings(self::gzdecode($stream)),
default => self::err($stream)
}
,
'exceptions' => self::deserialize_type_array_of_AutoSaveException(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'{ø×' => [
'_' => 'account.connectedBots',
'connected_bots' => self::deserialize_type_array_of_ConnectedBot(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'.m;' => [
'_' => 'users.userFull',
'full_user' => match (stream_get_contents($stream, 4)) {
'…>ÿ"' => self::deserialize_userFull($stream),
'¡Ïr0' => self::deserialize_type_UserFull(self::gzdecode($stream)),
default => self::err($stream)
}
,
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'Ò©K·' => [
'_' => 'contacts.contactsNotModified',
],
'B~èê' => [
'_' => 'contacts.contacts',
'contacts' => self::deserialize_type_array_of_Contact(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'saved_count' => unpack('l', stream_get_contents($stream, 4))[1],
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
';Ðw' => [
'_' => 'contacts.importedContacts',
'imported' => self::deserialize_type_array_of_ImportedContact(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'popular_invites' => self::deserialize_type_array_of_PopularContact(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'retry_contacts' => self::deserialize_type_array_of_long(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'‘Þ
' => [
'_' => 'contacts.blocked',
'blocked' => self::deserialize_type_array_of_PeerBlocked(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'”Afá' => [
'_' => 'contacts.blockedSlice',
'count' => unpack('l', stream_get_contents($stream, 4))[1],
'blocked' => self::deserialize_type_array_of_PeerBlocked(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'�M³' => [
'_' => 'contacts.found',
'my_results' => self::deserialize_type_array_of_Peer(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'results' => self::deserialize_type_array_of_Peer(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'Ùz' => [
'_' => 'contacts.resolvedPeer',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'õn&Þ' => [
'_' => 'contacts.topPeersNotModified',
],
'¨r·p' => [
'_' => 'contacts.topPeers',
'categories' => self::deserialize_type_array_of_TopPeerCategoryPeers(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'�“,µ' => [
'_' => 'contacts.topPeersDisabled',
],
'›¿A' => [
'_' => 'exportedContactToken',
'url' => self::deserialize_string($stream),
'expires' => unpack('l', stream_get_contents($stream, 4))[1],
],
'‡ŽqŒ' => [
'_' => 'messages.messages',
'messages' => self::deserialize_type_array_of_Message(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'^hT:' => self::deserialize_messages___messagesSlice($stream),
'NºvÇ' => self::deserialize_messages___channelMessages($stream),
'!_St' => [
'_' => 'messages.messagesNotModified',
'count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'@lº' => [
'_' => 'messages.dialogs',
'dialogs' => self::deserialize_type_array_of_Dialog(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'messages' => self::deserialize_type_array_of_Message(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ó”àq' => [
'_' => 'messages.dialogsSlice',
'count' => unpack('l', stream_get_contents($stream, 4))[1],
'dialogs' => self::deserialize_type_array_of_Dialog(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'messages' => self::deserialize_type_array_of_Message(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'–åãð' => [
'_' => 'messages.dialogsNotModified',
'count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'…‘Ñ„' => [
'_' => 'messages.affectedMessages',
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Ñi\\´' => [
'_' => 'messages.affectedHistory',
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
'offset' => unpack('l', stream_get_contents($stream, 4))[1],
],
'M¹€h' => [
'_' => 'messages.peerSettings',
'settings' => match (stream_get_contents($stream, 4)) {
'¥' => self::deserialize_peerSettings($stream),
'¡Ïr0' => self::deserialize_type_PeerSettings(self::gzdecode($stream)),
default => self::err($stream)
}
,
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ÕŸÿd' => [
'_' => 'messages.chats',
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'DØœ' => [
'_' => 'messages.chatsSlice',
'count' => unpack('l', stream_get_contents($stream, 4))[1],
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'œÑ×å' => [
'_' => 'messages.chatFull',
'full_chat' => match (stream_get_contents($stream, 4)) {
'8ÓÉ' => self::deserialize_chatFull($stream),
'§TÀD' => self::deserialize_channelFull($stream),
'¡Ïr0' => self::deserialize_type_ChatFull(self::gzdecode($stream)),
default => self::err($stream)
}
,
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'5FâÀ' => [
'_' => 'messages.dhConfigNotModified',
'random' => self::deserialize_bytes($stream),
],
'Ý",' => [
'_' => 'messages.dhConfig',
'g' => unpack('l', stream_get_contents($stream, 4))[1],
'p' => self::deserialize_bytes($stream),
'version' => unpack('l', stream_get_contents($stream, 4))[1],
'random' => self::deserialize_bytes($stream),
],
' À~«' => [
'_' => 'encryptedChatEmpty',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'SY²f' => [
'_' => 'encryptedChatWaiting',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'admin_id' => unpack('q', stream_get_contents($stream, 8))[1],
'participant_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'LÙñH' => self::deserialize_encryptedChatRequested($stream),
'ÇÔða' => [
'_' => 'encryptedChat',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'admin_id' => unpack('q', stream_get_contents($stream, 8))[1],
'participant_id' => unpack('q', stream_get_contents($stream, 8))[1],
'g_a_or_b' => self::deserialize_bytes($stream),
'key_fingerprint' => stream_get_contents($stream, 8),
],
'E|' => self::deserialize_encryptedChatDiscarded($stream),
'5‰V' => [
'_' => 'messages.sentEncryptedMessage',
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'2ÿ“”' => [
'_' => 'messages.sentEncryptedFile',
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'file' => match (stream_get_contents($stream, 4)) {
'~IÂ' => [
'_' => 'encryptedFileEmpty',
],
'ØŒ' . "\0" . '¨' => [
'_' => 'encryptedFile',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'size' => unpack('q', stream_get_contents($stream, 8))[1],
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'key_fingerprint' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_EncryptedFile(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'"štñ' => [
'_' => 'messages.stickersNotModified',
],
'~ì¦0' => [
'_' => 'messages.stickers',
'hash' => unpack('q', stream_get_contents($stream, 8))[1],
'stickers' => self::deserialize_type_array_of_Document(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'Ãfè' => [
'_' => 'messages.allStickersNotModified',
],
'»Î»Í' => [
'_' => 'messages.allStickers',
'hash' => unpack('q', stream_get_contents($stream, 8))[1],
'sets' => self::deserialize_type_array_of_StickerSet(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
' cí=' => [
'_' => 'messageMediaEmpty',
],
'×PQi' => self::deserialize_messageMediaPhoto($stream),
'tÔàV' => [
'_' => 'messageMediaGeo',
'geo' => match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => self::deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'I)2p' => [
'_' => 'messageMediaContact',
'phone_number' => self::deserialize_string($stream),
'first_name' => self::deserialize_string($stream),
'last_name' => self::deserialize_string($stream),
'vcard' => self::deserialize_string($stream),
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'žô„Ÿ' => [
'_' => 'messageMediaUnsupported',
],
'-×ôL' => self::deserialize_messageMediaDocument($stream),
';ñÝ' => self::deserialize_messageMediaWebPage($stream),
'?SÀ.' => [
'_' => 'messageMediaVenue',
'geo' => match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => self::deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
,
'title' => self::deserialize_string($stream),
'address' => self::deserialize_string($stream),
'provider' => self::deserialize_string($stream),
'venue_id' => self::deserialize_string($stream),
'venue_type' => self::deserialize_string($stream),
],
'�±ý' => [
'_' => 'messageMediaGame',
'game' => match (stream_get_contents($stream, 4)) {
';eù½' => self::deserialize_game($stream),
'¡Ïr0' => self::deserialize_type_Game(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ÓH¥ö' => self::deserialize_messageMediaInvoice($stream),
'fÆ@¹' => self::deserialize_messageMediaGeoLive($stream),
'˜çÖK' => [
'_' => 'messageMediaPoll',
'poll' => match (stream_get_contents($stream, 4)) {
'a�á†' => self::deserialize_poll($stream),
'¡Ïr0' => self::deserialize_type_Poll(self::gzdecode($stream)),
default => self::err($stream)
}
,
'results' => match (stream_get_contents($stream, 4)) {
' $ßz' => self::deserialize_pollResults($stream),
'¡Ïr0' => self::deserialize_type_PollResults(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'‹å~?' => [
'_' => 'messageMediaDice',
'value' => unpack('l', stream_get_contents($stream, 4))[1],
'emoticon' => self::deserialize_string($stream),
],
'ƒbËh' => self::deserialize_messageMediaStory($stream),
'°…­Ú' => self::deserialize_messageMediaGiveaway($stream),
'h™Æ' => self::deserialize_messageMediaGiveawayResults($stream),
'¨´
' => self::deserialize_chatInviteExported($stream),
'·zí' => [
'_' => 'chatInvitePublicJoinRequests',
],
'|mhZ' => [
'_' => 'chatInviteAlready',
'chat' => match (stream_get_contents($stream, 4)) {
'e(V)' => self::deserialize_chatEmpty($stream),
'VòËA' => self::deserialize_chat($stream),
'§¡’e' => self::deserialize_chatForbidden($stream),
'�ü­
' => self::deserialize_channel($stream),
'Õ“Ô' => self::deserialize_channelForbidden($stream),
'¡Ïr0' => self::deserialize_type_Chat(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'@ìàÍ' => self::deserialize_chatInvite($stream),
'°\\ia' => [
'_' => 'chatInvitePeek',
'chat' => match (stream_get_contents($stream, 4)) {
'e(V)' => self::deserialize_chatEmpty($stream),
'VòËA' => self::deserialize_chat($stream),
'§¡’e' => self::deserialize_chatForbidden($stream),
'�ü­
' => self::deserialize_channel($stream),
'Õ“Ô' => self::deserialize_channelForbidden($stream),
'¡Ïr0' => self::deserialize_type_Chat(self::gzdecode($stream)),
default => self::err($stream)
}
,
'expires' => unpack('l', stream_get_contents($stream, 4))[1],
],
'?n' => self::deserialize_messages___stickerSet($stream),
'ë$ùÓ' => [
'_' => 'messages.stickerSetNotModified',
],
'(d8' => [
'_' => 'messages.stickerSetInstallResultSuccess',
],
'¨ä5' => [
'_' => 'messages.stickerSetInstallResultArchive',
'sets' => self::deserialize_type_array_of_StickerSetCovered(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'CõÄ¶' => [
'_' => 'messages.messageViews',
'views' => self::deserialize_type_array_of_MessageViews(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¢\\è' => [
'_' => 'messages.savedGifsNotModified',
],
'* „' => self::deserialize_messages___savedGifs($stream),
'öò!à' => self::deserialize_messages___botResults($stream),
'æÝµ&' => self::deserialize_messages___messageEditData($stream),
'¤^X6' => self::deserialize_messages___botCallbackAnswer($stream),
'TÃq3' => [
'_' => 'messages.peerDialogs',
'dialogs' => self::deserialize_type_array_of_Dialog(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'messages' => self::deserialize_type_array_of_Message(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'state' => match (stream_get_contents($stream, 4)) {
'>*l¥' => [
'_' => 'updates.state',
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'seq' => unpack('l', stream_get_contents($stream, 4))[1],
'unread_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_updates___State(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'fÜÆ' => [
'_' => 'messages.featuredStickersNotModified',
'count' => unpack('l', stream_get_contents($stream, 4))[1],
],
')8¾' => self::deserialize_messages___featuredStickers($stream),
'�ø' => [
'_' => 'messages.recentStickersNotModified',
],
'V|Óˆ' => self::deserialize_messages___recentStickers($stream),
'È©ËO' => [
'_' => 'messages.archivedStickers',
'count' => unpack('l', stream_get_contents($stream, 4))[1],
'sets' => self::deserialize_type_array_of_StickerSetCovered(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'™ý;š' => [
'_' => 'messages.highScores',
'scores' => self::deserialize_type_array_of_HighScore(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'½^ý' => [
'_' => 'messages.webPage',
'webpage' => match (stream_get_contents($stream, 4)) {
'ˆ!' => self::deserialize_webPageEmpty($stream),
'G>Ñ°' => self::deserialize_webPagePending($stream),
'²Eœè' => self::deserialize_webPage($stream),
'Ês' => self::deserialize_webPageNotModified($stream),
'¡Ïr0' => self::deserialize_type_WebPage(self::gzdecode($stream)),
default => self::err($stream)
}
,
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'Ó¦�ž' => [
'_' => 'messages.favedStickersNotModified',
],
'—µ,' => self::deserialize_messages___favedStickers($stream),
'~IÂ' => [
'_' => 'encryptedFileEmpty',
],
'ØŒ' . "\0" . '¨' => [
'_' => 'encryptedFile',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'size' => unpack('q', stream_get_contents($stream, 8))[1],
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'key_fingerprint' => unpack('l', stream_get_contents($stream, 4))[1],
],
']¶T' => [
'_' => 'messages.foundStickerSetsNotModified',
],
'Ò�ðŠ' => [
'_' => 'messages.foundStickerSets',
'hash' => unpack('q', stream_get_contents($stream, 8))[1],
'sets' => self::deserialize_type_array_of_StickerSetCovered(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'PâAð' => [
'_' => 'chatOnlines',
'onlines' => unpack('l', stream_get_contents($stream, 4))[1],
],
'½aÇ\\' => [
'_' => 'emojiKeywordsDifference',
'lang_code' => self::deserialize_string($stream),
'from_version' => unpack('l', stream_get_contents($stream, 4))[1],
'version' => unpack('l', stream_get_contents($stream, 4))[1],
'keywords' => self::deserialize_type_array_of_EmojiKeyword(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'�su¥' => [
'_' => 'emojiURL',
'url' => self::deserialize_string($stream),
],
':Ó’' => self::deserialize_urlAuthResultRequest($stream),
'NŒ�' => [
'_' => 'urlAuthResultAccepted',
'url' => self::deserialize_string($stream),
],
'ÛÖ©' => [
'_' => 'urlAuthResultDefault',
],
'NH™H' => self::deserialize_messages___votesList($stream),
'7Ù*' => self::deserialize_messages___dialogFilters($stream),
'‚4¦' => self::deserialize_messages___discussionMessage($stream),
'l>�ï' => [
'_' => 'messages.affectedFoundMessages',
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
'offset' => unpack('l', stream_get_contents($stream, 4))[1],
'messages' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¹·^' => self::deserialize_messages___historyImportParsed($stream),
'¯b' => [
'_' => 'messages.historyImport',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'Ì-Æ½' => [
'_' => 'messages.exportedChatInvites',
'count' => unpack('l', stream_get_contents($stream, 4))[1],
'invites' => self::deserialize_type_array_of_ExportedChatInvite(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'P¾q' => [
'_' => 'messages.exportedChatInvite',
'invite' => match (stream_get_contents($stream, 4)) {
'¨´
' => self::deserialize_chatInviteExported($stream),
'·zí' => [
'_' => 'chatInvitePublicJoinRequests',
],
'¡Ïr0' => self::deserialize_type_ExportedChatInvite(self::gzdecode($stream)),
default => self::err($stream)
}
,
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ï' . "\0" . '&"' => [
'_' => 'messages.exportedChatInviteReplaced',
'invite' => match (stream_get_contents($stream, 4)) {
'¨´
' => self::deserialize_chatInviteExported($stream),
'·zí' => [
'_' => 'chatInvitePublicJoinRequests',
],
'¡Ïr0' => self::deserialize_type_ExportedChatInvite(self::gzdecode($stream)),
default => self::err($stream)
}
,
'new_invite' => match (stream_get_contents($stream, 4)) {
'¨´
' => self::deserialize_chatInviteExported($stream),
'·zí' => [
'_' => 'chatInvitePublicJoinRequests',
],
'¡Ïr0' => self::deserialize_type_ExportedChatInvite(self::gzdecode($stream)),
default => self::err($stream)
}
,
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'×r›¶' => [
'_' => 'messages.chatAdminsWithInvites',
'admins' => self::deserialize_type_array_of_ChatAdminWithInvites(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'
°¶�' => [
'_' => 'messages.chatInviteImporters',
'count' => unpack('l', stream_get_contents($stream, 4))[1],
'importers' => self::deserialize_type_array_of_ChatInviteImporter(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'çM¢' => [
'_' => 'messages.checkedHistoryImportPeer',
'confirm_text' => self::deserialize_string($stream),
],
'<â~' => self::deserialize_messages___searchResultsCalendar($stream),
'¯+²S' => [
'_' => 'messages.searchResultsPositions',
'count' => unpack('l', stream_get_contents($stream, 4))[1],
'positions' => self::deserialize_type_array_of_SearchResultsPosition(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'-I½1' => self::deserialize_messages___messageReactionsList($stream),
'WŸ' => [
'_' => 'messages.availableReactionsNotModified',
],
'­:Žv' => [
'_' => 'messages.availableReactions',
'hash' => unpack('l', stream_get_contents($stream, 4))[1],
'reactions' => self::deserialize_type_array_of_AvailableReaction(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ø2Û3' => [
'_' => 'messages.translateResult',
'result' => self::deserialize_type_array_of_TextWithEntities(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'\\ŠØñ' => [
'_' => 'attachMenuBotsNotModified',
],
'ÀC<' => [
'_' => 'attachMenuBots',
'hash' => unpack('q', stream_get_contents($stream, 8))[1],
'bots' => self::deserialize_type_array_of_AttachMenuBot(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'f¿“' => [
'_' => 'attachMenuBotsBot',
'bot' => match (stream_get_contents($stream, 4)) {
'þ�Ù' => self::deserialize_attachMenuBot($stream),
'¡Ïr0' => self::deserialize_type_AttachMenuBot(self::gzdecode($stream)),
default => self::err($stream)
}
,
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'|U' => [
'_' => 'webViewResultUrl',
'query_id' => unpack('q', stream_get_contents($stream, 8))[1],
'url' => self::deserialize_string($stream),
],
'»v/ˆ' => [
'_' => 'simpleWebViewResultUrl',
'url' => self::deserialize_string($stream),
],
'Q”' => self::deserialize_webViewMessageSent($stream),
'WÙ¹Ï' => self::deserialize_messages___transcribedAudio($stream),
'ßÛo°' => [
'_' => 'messages.reactionsNotModified',
],
'÷ýê' => [
'_' => 'messages.reactions',
'hash' => unpack('q', stream_get_contents($stream, 8))[1],
'reactions' => self::deserialize_type_array_of_Reaction(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
' k´C' => [
'_' => 'defaultHistoryTTL',
'period' => unpack('l', stream_get_contents($stream, 4))[1],
],
'‡­´o' => [
'_' => 'messages.emojiGroupsNotModified',
],
'K¹ˆ' => [
'_' => 'messages.emojiGroups',
'hash' => unpack('l', stream_get_contents($stream, 4))[1],
'groups' => self::deserialize_type_array_of_EmojiGroup(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'õ­Pë' => self::deserialize_messages___botApp($stream),
'O<' => [
'_' => 'appWebViewResultUrl',
'url' => self::deserialize_string($stream),
],
'!â:ø' => [
'_' => 'messages.savedDialogs',
'dialogs' => self::deserialize_type_array_of_SavedDialog(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'messages' => self::deserialize_type_array_of_Message(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'Ù�ºD' => [
'_' => 'messages.savedDialogsSlice',
'count' => unpack('l', stream_get_contents($stream, 4))[1],
'dialogs' => self::deserialize_type_array_of_SavedDialog(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'messages' => self::deserialize_type_array_of_Message(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'èoÀ' => [
'_' => 'messages.savedDialogsNotModified',
'count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'ïY›ˆ' => [
'_' => 'messages.savedReactionTagsNotModified',
],
'
•Y2' => [
'_' => 'messages.savedReactionTags',
'tags' => self::deserialize_type_array_of_SavedReactionTag(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¬B¸;' => [
'_' => 'outboxReadDate',
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'•f�Æ' => [
'_' => 'messages.quickReplies',
'quick_replies' => self::deserialize_type_array_of_QuickReply(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'messages' => self::deserialize_type_array_of_Message(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'[ë‘_' => [
'_' => 'messages.quickRepliesNotModified',
],
'>*l¥' => [
'_' => 'updates.state',
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'seq' => unpack('l', stream_get_contents($stream, 4))[1],
'unread_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'8¡u]' => [
'_' => 'updates.differenceEmpty',
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'seq' => unpack('l', stream_get_contents($stream, 4))[1],
],
' œô' . "\0" . '' => [
'_' => 'updates.difference',
'new_messages' => self::deserialize_type_array_of_Message(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'new_encrypted_messages' => self::deserialize_type_array_of_EncryptedMessage(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'other_updates' => self::deserialize_type_array_of_Update(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'state' => match (stream_get_contents($stream, 4)) {
'>*l¥' => [
'_' => 'updates.state',
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'seq' => unpack('l', stream_get_contents($stream, 4))[1],
'unread_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_updates___State(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'�û¨' => [
'_' => 'updates.differenceSlice',
'new_messages' => self::deserialize_type_array_of_Message(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'new_encrypted_messages' => self::deserialize_type_array_of_EncryptedMessage(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'other_updates' => self::deserialize_type_array_of_Update(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'intermediate_state' => match (stream_get_contents($stream, 4)) {
'>*l¥' => [
'_' => 'updates.state',
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'seq' => unpack('l', stream_get_contents($stream, 4))[1],
'unread_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_updates___State(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'm�þJ' => [
'_' => 'updates.differenceTooLong',
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'û¯>' => self::deserialize_updates___channelDifferenceEmpty($stream),
'þÆ¼¤' => self::deserialize_updates___channelDifferenceTooLong($stream),
'Ngd ' => self::deserialize_updates___channelDifference($stream),
'¨,! ' => [
'_' => 'photos.photo',
'photo' => match (stream_get_contents($stream, 4)) {
'-²1#' => [
'_' => 'photoEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ezû' => self::deserialize_photo($stream),
'¡Ïr0' => self::deserialize_type_Photo(self::gzdecode($stream)),
default => self::err($stream)
}
,
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¥jÊ�' => [
'_' => 'photos.photos',
'photos' => self::deserialize_type_array_of_Photo(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'T' => [
'_' => 'photos.photosSlice',
'count' => unpack('l', stream_get_contents($stream, 4))[1],
'photos' => self::deserialize_type_array_of_Photo(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'Õj	' => [
'_' => 'upload.file',
'type' => match (stream_get_contents($stream, 4)) {
';–ª' => [
'_' => 'storage.fileUnknown',
],
'Ro¼@' => [
'_' => 'storage.filePartial',
],
'þ~' . "\0" . '' => [
'_' => 'storage.fileJpeg',
],
'ßªáÊ' => [
'_' => 'storage.fileGif',
],
'ÀcO
' => [
'_' => 'storage.filePng',
],
'�P®' => [
'_' => 'storage.filePdf',
],
'wŠR' => [
'_' => 'storage.fileMp3',
],
'¼ë	K' => [
'_' => 'storage.fileMov',
],
'ä Î³' => [
'_' => 'storage.fileMp4',
],
'LF�' => [
'_' => 'storage.fileWebp',
],
'¡Ïr0' => self::deserialize_type_storage___FileType(self::gzdecode($stream)),
default => self::err($stream)
}
,
'mtime' => unpack('l', stream_get_contents($stream, 4))[1],
'bytes' => self::deserialize_bytes($stream),
],
'DÚŒñ' => [
'_' => 'upload.fileCdnRedirect',
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'file_token' => self::deserialize_string($stream),
'encryption_key' => self::deserialize_string($stream),
'encryption_iv' => self::deserialize_string($stream),
'file_hashes' => self::deserialize_type_array_of_FileHash(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¼Sç!' => [
'_' => 'upload.webFile',
'size' => unpack('l', stream_get_contents($stream, 4))[1],
'mime_type' => self::deserialize_string($stream),
'file_type' => match (stream_get_contents($stream, 4)) {
';–ª' => [
'_' => 'storage.fileUnknown',
],
'Ro¼@' => [
'_' => 'storage.filePartial',
],
'þ~' . "\0" . '' => [
'_' => 'storage.fileJpeg',
],
'ßªáÊ' => [
'_' => 'storage.fileGif',
],
'ÀcO
' => [
'_' => 'storage.filePng',
],
'�P®' => [
'_' => 'storage.filePdf',
],
'wŠR' => [
'_' => 'storage.fileMp3',
],
'¼ë	K' => [
'_' => 'storage.fileMov',
],
'ä Î³' => [
'_' => 'storage.fileMp4',
],
'LF�' => [
'_' => 'storage.fileWebp',
],
'¡Ïr0' => self::deserialize_type_storage___FileType(self::gzdecode($stream)),
default => self::err($stream)
}
,
'mtime' => unpack('l', stream_get_contents($stream, 4))[1],
'bytes' => self::deserialize_bytes($stream),
],
'nä¨î' => [
'_' => 'upload.cdnFileReuploadNeeded',
'request_token' => self::deserialize_bytes($stream),
],
'OÊŸ©' => [
'_' => 'upload.cdnFile',
'bytes' => self::deserialize_bytes($stream),
],
'$Ì' => self::deserialize_config($stream),
'uŽ' => [
'_' => 'nearestDc',
'country' => self::deserialize_string($stream),
'this_dc' => unpack('l', stream_get_contents($stream, 4))[1],
'nearest_dc' => unpack('l', stream_get_contents($stream, 4))[1],
],
'0Î»Ì' => self::deserialize_help___appUpdate($stream),
'6eZÄ' => [
'_' => 'help.noAppUpdate',
],
'xŸË' => [
'_' => 'help.inviteText',
'message' => self::deserialize_string($stream),
],
'öµÆ' => self::deserialize_help___support($stream),
'
ä%W' => [
'_' => 'cdnConfig',
'public_keys' => self::deserialize_type_array_of_CdnPublicKey(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'×' => [
'_' => 'help.recentMeUrls',
'urls' => self::deserialize_type_array_of_RecentMeUrl(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'Ÿ0ã' => [
'_' => 'help.termsOfServiceUpdateEmpty',
'expires' => unpack('l', stream_get_contents($stream, 4))[1],
],
'aùì(' => [
'_' => 'help.termsOfServiceUpdate',
'expires' => unpack('l', stream_get_contents($stream, 4))[1],
'terms_of_service' => match (stream_get_contents($stream, 4)) {
'
x' => self::deserialize_help___termsOfService($stream),
'¡Ïr0' => self::deserialize_type_help___TermsOfService(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'f¡¯f' => [
'_' => 'help.deepLinkInfoEmpty',
],
'2èNj' => self::deserialize_help___deepLinkInfo($stream),
'dÞ|' => [
'_' => 'help.appConfigNotModified',
],
'.xÝ' => [
'_' => 'help.appConfig',
'hash' => unpack('l', stream_get_contents($stream, 4))[1],
'config' => match (stream_get_contents($stream, 4)) {
'h{m?' => null,
'j^4Ç' => match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) },
'¤ßà+' => unpack('d', stream_get_contents($stream, 8))[1],
'zv·' => self::deserialize_string($stream),
'cGD÷' => $this->deserialize_type_array_of_JSONValue(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'�ÔÁ™' => self::deserialize_type_array_of_JSONObjectValue(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'¡Ïr0' => $this->deserialize_type_JSONValue(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Wô¹¿' => [
'_' => 'help.passportConfigNotModified',
],
'¯Ö˜ ' => [
'_' => 'help.passportConfig',
'hash' => unpack('l', stream_get_contents($stream, 4))[1],
'countries_langs' => match (stream_get_contents($stream, 4)) {
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'¡Ïr0' => self::deserialize_type_DataJSON(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ÉñŒ' => [
'_' => 'help.supportName',
'name' => self::deserialize_string($stream),
],
'í.®ó' => [
'_' => 'help.userInfoEmpty',
],
'X7ë' => [
'_' => 'help.userInfo',
'message' => self::deserialize_string($stream),
'entities' => self::deserialize_type_array_of_MessageEntity(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'author' => self::deserialize_string($stream),
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'u¬ö˜' => [
'_' => 'help.promoDataEmpty',
'expires' => unpack('l', stream_get_contents($stream, 4))[1],
],
'?y9Œ' => self::deserialize_help___promoData($stream),
'2Ì“' => [
'_' => 'help.countriesListNotModified',
],
'žuÐ‡' => [
'_' => 'help.countriesList',
'countries' => self::deserialize_type_array_of_help___Country(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'hash' => unpack('l', stream_get_contents($stream, 4))[1],
],
'œu4S' => [
'_' => 'help.premiumPromo',
'status_text' => self::deserialize_string($stream),
'status_entities' => self::deserialize_type_array_of_MessageEntity(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'video_sections' => self::deserialize_type_array_of_string(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'videos' => self::deserialize_type_array_of_Document(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'period_options' => self::deserialize_type_array_of_PremiumSubscriptionOption(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'Îõ¡+' => [
'_' => 'help.peerColorsNotModified',
],
'íø' . "\0" . '' => [
'_' => 'help.peerColors',
'hash' => unpack('l', stream_get_contents($stream, 4))[1],
'colors' => self::deserialize_type_array_of_help___PeerColorOption(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'Ì—' => [
'_' => 'help.timezonesListNotModified',
],
'qít{' => [
'_' => 'help.timezonesList',
'timezones' => self::deserialize_type_array_of_Timezone(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'hash' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¯þ°š' => [
'_' => 'channels.channelParticipants',
'count' => unpack('l', stream_get_contents($stream, 4))[1],
'participants' => self::deserialize_type_array_of_ChannelParticipant(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'é?ð' => [
'_' => 'channels.channelParticipantsNotModified',
],
'¸ß' => [
'_' => 'channels.channelParticipant',
'participant' => match (stream_get_contents($stream, 4)) {
'ÀÀ' => [
'_' => 'channelParticipant',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'§¿¨5' => self::deserialize_channelParticipantSelf($stream),
'Óæ/' => self::deserialize_channelParticipantCreator($stream),
'S»Ã4' => self::deserialize_channelParticipantAdmin($stream),
'Nøm' => self::deserialize_channelParticipantBanned($stream),
'ð' => [
'_' => 'channelParticipantLeft',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_ChannelParticipant(self::gzdecode($stream)),
default => self::err($stream)
}
,
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ô«]' => [
'_' => 'exportedMessageLink',
'link' => self::deserialize_string($stream),
'html' => self::deserialize_string($stream),
],
'M÷Ší' => [
'_' => 'channels.adminLogResults',
'events' => self::deserialize_type_array_of_ChannelAdminLogEvent(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'Åþ\'©' => [
'_' => 'messages.inactiveChats',
'dates' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'‡îÉ' => self::deserialize_messages___sponsoredMessages($stream),
'I9' => [
'_' => 'messages.sponsoredMessagesEmpty',
],
'Æ°–ô' => [
'_' => 'channels.sendAsPeers',
'peers' => self::deserialize_type_array_of_SendAsPeer(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'Óv6' => self::deserialize_messages___forumTopics($stream),
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'ˆ¥3u' => [
'_' => 'botMenuButtonDefault',
],
'ÂXB' => [
'_' => 'botMenuButtonCommands',
],
'æ|µÇ' => [
'_' => 'botMenuButton',
'text' => self::deserialize_string($stream),
'url' => self::deserialize_string($stream),
],
'°u§è' => [
'_' => 'bots.botInfo',
'name' => self::deserialize_string($stream),
'about' => self::deserialize_string($stream),
'description' => self::deserialize_string($stream),
],
'Q‡ ' => self::deserialize_payments___paymentForm($stream),
'þÄp' => self::deserialize_payments___paymentReceipt($stream),
'ƒEÑ' => self::deserialize_payments___validatedRequestedInfo($stream),
'�_N' => [
'_' => 'payments.paymentResult',
'updates' => match (stream_get_contents($stream, 4)) {
'~¯ã' => [
'_' => 'updatesTooLong',
],
'øÇ;1' => self::deserialize_updateShortMessage($stream),
'¥îmM' => self::deserialize_updateShortChatMessage($stream),
'ÁÞÔx' => [
'_' => 'updateShort',
'update' => match (stream_get_contents($stream, 4)) {
'ý
+' => [
'_' => 'updateNewMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Ö¿�N' => [
'_' => 'updateMessageID',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'random_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'å°¢' => [
'_' => 'updateDeleteMessages',
'messages' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'…À' => [
'_' => 'updateUserTyping',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'action' => match (stream_get_contents($stream, 4)) {
'Nt¿' => [
'_' => 'sendMessageTypingAction',
],
'õÈ^ý' => [
'_' => 'sendMessageCancelAction',
],
'oÖ‡¡' => [
'_' => 'sendMessageRecordVideoAction',
],
'ì:vé' => [
'_' => 'sendMessageUploadVideoAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'÷s/Õ' => [
'_' => 'sendMessageRecordAudioAction',
],
'«×Qó' => [
'_' => 'sendMessageUploadAudioAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'&JÓÑ' => [
'_' => 'sendMessageUploadPhotoAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'äÙª' => [
'_' => 'sendMessageUploadDocumentAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡‹o' => [
'_' => 'sendMessageGeoLocationAction',
],
'o¼Œb' => [
'_' => 'sendMessageChooseContactAction',
],
'H�jÝ' => [
'_' => 'sendMessageGamePlayAction',
],
'¼òˆ' => [
'_' => 'sendMessageRecordRoundAction',
],
'f>$' => [
'_' => 'sendMessageUploadRoundAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'…",Ù' => [
'_' => 'speakingInGroupCallAction',
],
'F’ÚÛ' => [
'_' => 'sendMessageHistoryImportAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'±ÆZ°' => [
'_' => 'sendMessageChooseStickerAction',
],
'Ë+—%' => [
'_' => 'sendMessageEmojiInteraction',
'emoticon' => self::deserialize_string($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'interaction' => match (stream_get_contents($stream, 4)) {
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'¡Ïr0' => self::deserialize_type_DataJSON(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'.�e¶' => [
'_' => 'sendMessageEmojiInteractionSeen',
'emoticon' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_SendMessageAction(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ðzHƒ' => [
'_' => 'updateChatUserTyping',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'from_id' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'action' => match (stream_get_contents($stream, 4)) {
'Nt¿' => [
'_' => 'sendMessageTypingAction',
],
'õÈ^ý' => [
'_' => 'sendMessageCancelAction',
],
'oÖ‡¡' => [
'_' => 'sendMessageRecordVideoAction',
],
'ì:vé' => [
'_' => 'sendMessageUploadVideoAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'÷s/Õ' => [
'_' => 'sendMessageRecordAudioAction',
],
'«×Qó' => [
'_' => 'sendMessageUploadAudioAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'&JÓÑ' => [
'_' => 'sendMessageUploadPhotoAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'äÙª' => [
'_' => 'sendMessageUploadDocumentAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡‹o' => [
'_' => 'sendMessageGeoLocationAction',
],
'o¼Œb' => [
'_' => 'sendMessageChooseContactAction',
],
'H�jÝ' => [
'_' => 'sendMessageGamePlayAction',
],
'¼òˆ' => [
'_' => 'sendMessageRecordRoundAction',
],
'f>$' => [
'_' => 'sendMessageUploadRoundAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'…",Ù' => [
'_' => 'speakingInGroupCallAction',
],
'F’ÚÛ' => [
'_' => 'sendMessageHistoryImportAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'±ÆZ°' => [
'_' => 'sendMessageChooseStickerAction',
],
'Ë+—%' => [
'_' => 'sendMessageEmojiInteraction',
'emoticon' => self::deserialize_string($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'interaction' => match (stream_get_contents($stream, 4)) {
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'¡Ïr0' => self::deserialize_type_DataJSON(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'.�e¶' => [
'_' => 'sendMessageEmojiInteractionSeen',
'emoticon' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_SendMessageAction(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'˜v' => [
'_' => 'updateChatParticipants',
'participants' => match (stream_get_contents($stream, 4)) {
'áÓc‡' => self::deserialize_chatParticipantsForbidden($stream),
'ø“¼<' => [
'_' => 'chatParticipants',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'participants' => self::deserialize_type_array_of_ChatParticipant(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_ChatParticipants(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Þø½å' => [
'_' => 'updateUserStatus',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'status' => match (stream_get_contents($stream, 4)) {
'IPÐ	' => [
'_' => 'userStatusEmpty',
],
'I9¹í' => [
'_' => 'userStatusOnline',
'expires' => unpack('l', stream_get_contents($stream, 4))[1],
],
'?pŒ' . "\0" . '' => [
'_' => 'userStatusOffline',
'was_online' => unpack('l', stream_get_contents($stream, 4))[1],
],
'È}{' => self::deserialize_userStatusRecently($stream),
'T' => self::deserialize_userStatusLastWeek($stream),
'w—‰e' => self::deserialize_userStatusLastMonth($stream),
'¡Ïr0' => self::deserialize_type_UserStatus(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'$‰„§' => [
'_' => 'updateUserName',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'first_name' => self::deserialize_string($stream),
'last_name' => self::deserialize_string($stream),
'usernames' => self::deserialize_type_array_of_Username(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ï«Q‰' => self::deserialize_updateNewAuthorization($stream),
'š½¼' => [
'_' => 'updateNewEncryptedMessage',
'message' => match (stream_get_contents($stream, 4)) {
'Áí' => [
'_' => 'encryptedMessage',
'random_id' => unpack('q', stream_get_contents($stream, 8))[1],
'chat_id' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'bytes' => self::deserialize_bytes($stream),
'file' => match (stream_get_contents($stream, 4)) {
'~IÂ' => [
'_' => 'encryptedFileEmpty',
],
'ØŒ' . "\0" . '¨' => [
'_' => 'encryptedFile',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'size' => unpack('q', stream_get_contents($stream, 8))[1],
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'key_fingerprint' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_EncryptedFile(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Ks#' => [
'_' => 'encryptedMessageService',
'random_id' => unpack('q', stream_get_contents($stream, 8))[1],
'chat_id' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'bytes' => self::deserialize_bytes($stream),
],
'¡Ïr0' => self::deserialize_type_EncryptedMessage(self::gzdecode($stream)),
default => self::err($stream)
}
,
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Vñ' => [
'_' => 'updateEncryptedChatTyping',
'chat_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'�è¢´' => [
'_' => 'updateEncryption',
'chat' => match (stream_get_contents($stream, 4)) {
' À~«' => [
'_' => 'encryptedChatEmpty',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'SY²f' => [
'_' => 'encryptedChatWaiting',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'admin_id' => unpack('q', stream_get_contents($stream, 8))[1],
'participant_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'LÙñH' => self::deserialize_encryptedChatRequested($stream),
'ÇÔða' => [
'_' => 'encryptedChat',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'admin_id' => unpack('q', stream_get_contents($stream, 8))[1],
'participant_id' => unpack('q', stream_get_contents($stream, 8))[1],
'g_a_or_b' => self::deserialize_bytes($stream),
'key_fingerprint' => stream_get_contents($stream, 8),
],
'E|' => self::deserialize_encryptedChatDiscarded($stream),
'¡Ïr0' => self::deserialize_type_EncryptedChat(self::gzdecode($stream)),
default => self::err($stream)
}
,
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'·%þ8' => [
'_' => 'updateEncryptedMessagesRead',
'chat_id' => unpack('l', stream_get_contents($stream, 4))[1],
'max_date' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'QTÚ=' => [
'_' => 'updateChatParticipantAdd',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'inviter_id' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
'w=/ã' => [
'_' => 'updateChatParticipantDelete',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
's˜^Ž' => [
'_' => 'updateDcOptions',
'dc_options' => self::deserialize_type_array_of_DcOption(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ïhÂ¾' => [
'_' => 'updateNotifySettings',
'peer' => match (stream_get_contents($stream, 4)) {
'ØÔŸ' => [
'_' => 'notifyPeer',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'L;È´' => [
'_' => 'notifyUsers',
],
'ÃÎÀ' => [
'_' => 'notifyChats',
],
'ïèÖ' => [
'_' => 'notifyBroadcasts',
],
'cn"' => [
'_' => 'notifyForumTopic',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'top_msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_NotifyPeer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'notify_settings' => match (stream_get_contents($stream, 4)) {
',b™' => self::deserialize_peerNotifySettings($stream),
'¡Ïr0' => self::deserialize_type_PeerNotifySettings(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'häë' => self::deserialize_updateServiceNotification($stream),
'*\';î' => [
'_' => 'updatePrivacy',
'key' => match (stream_get_contents($stream, 4)) {
'0«.¼' => [
'_' => 'privacyKeyStatusTimestamp',
],
'úmP' => [
'_' => 'privacyKeyChatInvite',
],
'{+f=' => [
'_' => 'privacyKeyPhoneCall',
],
'ÈI9' => [
'_' => 'privacyKeyPhoneP2P',
],
'£Vìi' => [
'_' => 'privacyKeyForwards',
],
'í–' => [
'_' => 'privacyKeyProfilePhoto',
],
'mäšÑ' => [
'_' => 'privacyKeyPhoneNumber',
],
'+ÔÿB' => [
'_' => 'privacyKeyAddedByPhone',
],
'ô—' => [
'_' => 'privacyKeyVoiceMessages',
],
'a·†¤' => [
'_' => 'privacyKeyAbout',
],
'¡Ïr0' => self::deserialize_type_PrivacyKey(self::gzdecode($stream)),
default => self::err($stream)
}
,
'rules' => self::deserialize_type_array_of_PrivacyRule(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'*I' => [
'_' => 'updateUserPhone',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'phone' => self::deserialize_string($stream),
],
'ßO—œ' => self::deserialize_updateReadHistoryInbox($stream),
'¿!//' => [
'_' => 'updateReadHistoryOutbox',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'max_id' => unpack('l', stream_get_contents($stream, 4))[1],
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'‰' => [
'_' => 'updateWebPage',
'webpage' => match (stream_get_contents($stream, 4)) {
'ˆ!' => self::deserialize_webPageEmpty($stream),
'G>Ñ°' => self::deserialize_webPagePending($stream),
'²Eœè' => self::deserialize_webPage($stream),
'Ês' => self::deserialize_webPageNotModified($stream),
'¡Ïr0' => self::deserialize_type_WebPage(self::gzdecode($stream)),
default => self::err($stream)
}
,
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'�q"ø' => self::deserialize_updateReadMessagesContents($stream),
'”�' => self::deserialize_updateChannelTooLong($stream),
'	L[c' => [
'_' => 'updateChannel',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'Ùºb' => [
'_' => 'updateNewChannelMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'n.’' => self::deserialize_updateReadChannelInbox($stream),
'[-Ã' => [
'_' => 'updateDeleteChannelMessages',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'messages' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¬&ò' => [
'_' => 'updateChannelMessageViews',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'views' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¢aÊ×' => [
'_' => 'updateChatParticipantAdmin',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'is_admin' => match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) },
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
'ª0Šh' => [
'_' => 'updateNewStickerSet',
'stickerset' => match (stream_get_contents($stream, 4)) {
'?n' => self::deserialize_messages___stickerSet($stream),
'ë$ùÓ' => [
'_' => 'messages.stickerSetNotModified',
],
'¡Ïr0' => self::deserialize_type_messages___StickerSet(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Ò²' => self::deserialize_updateStickerSetsOrder($stream),
'HÂ1' => self::deserialize_updateStickerSets($stream),
'4u“' => [
'_' => 'updateSavedGifs',
],
'œ7oI' => self::deserialize_updateBotInlineQuery($stream),
'*ñ' => self::deserialize_updateBotInlineSend($stream),
'÷M?' => [
'_' => 'updateEditChannelMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'�ÄÏ¹' => self::deserialize_updateBotCallbackQuery($stream),
'£pä' => [
'_' => 'updateEditMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'R�i' => self::deserialize_updateInlineBotCallbackQuery($stream),
'©™_·' => [
'_' => 'updateReadChannelOutbox',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'max_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'mìI' => self::deserialize_updateDraftMessage($stream),
'B\'W' => [
'_' => 'updateReadFeaturedStickers',
],
' ,Bš' => [
'_' => 'updateRecentStickers',
],
'Ý)¢' => [
'_' => 'updateConfig',
],
'�gT3' => [
'_' => 'updatePtsChanged',
],
'Ÿ©+/' => [
'_' => 'updateChannelWebPage',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'webpage' => match (stream_get_contents($stream, 4)) {
'ˆ!' => self::deserialize_webPageEmpty($stream),
'G>Ñ°' => self::deserialize_webPagePending($stream),
'²Eœè' => self::deserialize_webPage($stream),
'Ês' => self::deserialize_webPageNotModified($stream),
'¡Ïr0' => self::deserialize_type_WebPage(self::gzdecode($stream)),
default => self::err($stream)
}
,
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'åon' => self::deserialize_updateDialogPinned($stream),
'¢<ú' => self::deserialize_updatePinnedDialogs($stream),
'ÃÀƒ' => [
'_' => 'updateBotWebhookJSON',
'data' => match (stream_get_contents($stream, 4)) {
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'¡Ïr0' => self::deserialize_type_DataJSON(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¦@’›' => [
'_' => 'updateBotWebhookJSONQuery',
'query_id' => unpack('q', stream_get_contents($stream, 8))[1],
'data' => match (stream_get_contents($stream, 4)) {
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'¡Ïr0' => self::deserialize_type_DataJSON(self::gzdecode($stream)),
default => self::err($stream)
}
,
'timeout' => unpack('l', stream_get_contents($stream, 4))[1],
],
'}ý®µ' => [
'_' => 'updateBotShippingQuery',
'query_id' => unpack('q', stream_get_contents($stream, 8))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'payload' => self::deserialize_bytes($stream),
'shipping_address' => match (stream_get_contents($stream, 4)) {
'ëªŒ' => [
'_' => 'postAddress',
'street_line1' => self::deserialize_string($stream),
'street_line2' => self::deserialize_string($stream),
'city' => self::deserialize_string($stream),
'state' => self::deserialize_string($stream),
'country_iso2' => self::deserialize_string($stream),
'post_code' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_PostAddress(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'–šªŒ' => self::deserialize_updateBotPrecheckoutQuery($stream),
'k«' => [
'_' => 'updatePhoneCall',
'phone_call' => match (stream_get_contents($stream, 4)) {
'ÉfS' => [
'_' => 'phoneCallEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'o"Å' => self::deserialize_phoneCallWaiting($stream),
'í°' => self::deserialize_phoneCallRequested($stream),
'Ã`6' => self::deserialize_phoneCallAccepted($stream),
'g|–' => self::deserialize_phoneCall($stream),
'áMÊP' => self::deserialize_phoneCallDiscarded($stream),
'¡Ïr0' => self::deserialize_type_PhoneCall(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'dVF' => [
'_' => 'updateLangPackTooLong',
'lang_code' => self::deserialize_string($stream),
],
'M/V' => [
'_' => 'updateLangPack',
'difference' => match (stream_get_contents($stream, 4)) {
'öÁ…ó' => [
'_' => 'langPackDifference',
'lang_code' => self::deserialize_string($stream),
'from_version' => unpack('l', stream_get_contents($stream, 4))[1],
'version' => unpack('l', stream_get_contents($stream, 4))[1],
'strings' => self::deserialize_type_array_of_LangPackString(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => self::deserialize_type_LangPackDifference(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'm™å' => [
'_' => 'updateFavedStickers',
],
'])ê' => self::deserialize_updateChannelReadMessagesContents($stream),
'¾§„p' => [
'_' => 'updateContactsReset',
],
'˜Æ?²' => [
'_' => 'updateChannelAvailableMessages',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'available_min_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'ÃYdá' => self::deserialize_updateDialogUnreadMark($stream),
'{e¡¬' => self::deserialize_updateMessagePoll($stream),
'PÀT' => [
'_' => 'updateChatDefaultBannedRights',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'default_banned_rights' => match (stream_get_contents($stream, 4)) {
'Ÿ' => self::deserialize_chatBannedRights($stream),
'¡Ïr0' => self::deserialize_type_ChatBannedRights(self::gzdecode($stream)),
default => self::err($stream)
}
,
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
'À6' => [
'_' => 'updateFolderPeers',
'folder_peers' => self::deserialize_type_array_of_FolderPeer(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'fs~j' => [
'_' => 'updatePeerSettings',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'settings' => match (stream_get_contents($stream, 4)) {
'¥' => self::deserialize_peerSettings($stream),
'¡Ïr0' => self::deserialize_type_PeerSettings(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'°Ï¯´' => [
'_' => 'updatePeerLocated',
'peers' => self::deserialize_type_array_of_PeerLocated(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'û¥9' => [
'_' => 'updateNewScheduledMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'îl†�' => [
'_' => 'updateDeleteScheduledMessages',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'messages' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'£û‚' => [
'_' => 'updateTheme',
'theme' => match (stream_get_contents($stream, 4)) {
'Ög ' => self::deserialize_theme($stream),
'¡Ïr0' => self::deserialize_type_Theme(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'9¹‡' => [
'_' => 'updateGeoLiveViewed',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'‘æOV' => [
'_' => 'updateLoginToken',
],
'wô$' => [
'_' => 'updateMessagePollVote',
'poll_id' => unpack('q', stream_get_contents($stream, 8))[1],
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'options' => self::deserialize_type_array_of_bytes(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'}Þÿ&' => self::deserialize_updateDialogFilter($stream),
'!×¥' => [
'_' => 'updateDialogFilterOrder',
'order' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'O‘5' => [
'_' => 'updateDialogFilters',
],
'	¿a&' => [
'_' => 'updatePhoneCallSignalingData',
'phone_call_id' => unpack('q', stream_get_contents($stream, 8))[1],
'data' => self::deserialize_bytes($stream),
],
'ô\'šÒ' => [
'_' => 'updateChannelMessageForwards',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'forwards' => unpack('l', stream_get_contents($stream, 4))[1],
],
'F•±Ö' => self::deserialize_updateReadChannelDiscussionInbox($stream),
'|ž\\i' => [
'_' => 'updateReadChannelDiscussionOutbox',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'top_msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'read_max_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Rwàë' => self::deserialize_updatePeerBlocked($stream),
'#ÉˆŒ' => self::deserialize_updateChannelUserTyping($stream),
'µê…í' => self::deserialize_updatePinnedMessages($stream),
'†¹[' => self::deserialize_updatePinnedChannelMessages($stream),
'Njšø' => [
'_' => 'updateChat',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'NÛëò' => [
'_' => 'updateGroupCallParticipants',
'call' => match (stream_get_contents($stream, 4)) {
'„ªØ' => [
'_' => 'inputGroupCall',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputGroupCall(self::gzdecode($stream)),
default => self::err($stream)
}
,
'participants' => self::deserialize_type_array_of_GroupCallParticipant(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
'' . "\0" . 'E²' => [
'_' => 'updateGroupCall',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'call' => match (stream_get_contents($stream, 4)) {
'´¼€w' => [
'_' => 'groupCallDiscarded',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'duration' => unpack('l', stream_get_contents($stream, 4))[1],
],
'e—Õ' => self::deserialize_groupCall($stream),
'¡Ïr0' => self::deserialize_type_GroupCall(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¥¹›»' => self::deserialize_updatePeerHistoryTTL($stream),
':f‡Ð' => self::deserialize_updateChatParticipant($stream),
'»:]˜' => self::deserialize_updateChannelParticipant($stream),
'I
‡Ä' => [
'_' => 'updateBotStopped',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'stopped' => match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) },
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'‚9x' => self::deserialize_updateGroupCallConnection($stream),
'./qM' => [
'_' => 'updateBotCommands',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'bot_id' => unpack('q', stream_get_contents($stream, 8))[1],
'commands' => self::deserialize_type_array_of_BotCommand(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ÛÃcp' => [
'_' => 'updatePendingJoinRequests',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'requests_pending' => unpack('l', stream_get_contents($stream, 4))[1],
'recent_requesters' => self::deserialize_type_array_of_long(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'†©ß' => [
'_' => 'updateBotChatInviteRequester',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'about' => self::deserialize_string($stream),
'invite' => match (stream_get_contents($stream, 4)) {
'¨´
' => self::deserialize_chatInviteExported($stream),
'·zí' => [
'_' => 'chatInvitePublicJoinRequests',
],
'¡Ïr0' => self::deserialize_type_ExportedChatInvite(self::gzdecode($stream)),
default => self::err($stream)
}
,
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¸<^' => self::deserialize_updateMessageReactions($stream),
'¢·' => [
'_' => 'updateAttachMenuBots',
],
'�·’' => [
'_' => 'updateWebViewResultSent',
'query_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'X¸' => [
'_' => 'updateBotMenuButton',
'bot_id' => unpack('q', stream_get_contents($stream, 8))[1],
'button' => match (stream_get_contents($stream, 4)) {
'ˆ¥3u' => [
'_' => 'botMenuButtonDefault',
],
'ÂXB' => [
'_' => 'botMenuButtonCommands',
],
'æ|µÇ' => [
'_' => 'botMenuButton',
'text' => self::deserialize_string($stream),
'url' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_BotMenuButton(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'™¾Øt' => [
'_' => 'updateSavedRingtones',
],
'ZÍ„' . "\0" . '' => self::deserialize_updateTranscribedAudio($stream),
'lILû' => [
'_' => 'updateReadFeaturedEmojiStickers',
],
'™57(' => [
'_' => 'updateUserEmojiStatus',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'emoji_status' => match (stream_get_contents($stream, 4)) {
'®á-' => [
'_' => 'emojiStatusEmpty',
],
'�a›’' => [
'_' => 'emojiStatus',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'Ç¨0ú' => [
'_' => 'emojiStatusUntil',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'until' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_EmojiStatus(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ÛCô0' => [
'_' => 'updateRecentEmojiStatuses',
],
'ôcxo' => [
'_' => 'updateRecentReactions',
],
'…Ïü†' => self::deserialize_updateMoveStickerSetToTop($stream),
'Œ©sZ' => [
'_' => 'updateMessageExtendedMedia',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'extended_media' => match (stream_get_contents($stream, 4)) {
'ÈŒb­' => self::deserialize_messageExtendedMediaPreview($stream),
'dœGî' => [
'_' => 'messageExtendedMedia',
'media' => match (stream_get_contents($stream, 4)) {
' cí=' => [
'_' => 'messageMediaEmpty',
],
'×PQi' => self::deserialize_messageMediaPhoto($stream),
'tÔàV' => [
'_' => 'messageMediaGeo',
'geo' => match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => self::deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'I)2p' => [
'_' => 'messageMediaContact',
'phone_number' => self::deserialize_string($stream),
'first_name' => self::deserialize_string($stream),
'last_name' => self::deserialize_string($stream),
'vcard' => self::deserialize_string($stream),
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'žô„Ÿ' => [
'_' => 'messageMediaUnsupported',
],
'-×ôL' => self::deserialize_messageMediaDocument($stream),
';ñÝ' => self::deserialize_messageMediaWebPage($stream),
'?SÀ.' => [
'_' => 'messageMediaVenue',
'geo' => match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => self::deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
,
'title' => self::deserialize_string($stream),
'address' => self::deserialize_string($stream),
'provider' => self::deserialize_string($stream),
'venue_id' => self::deserialize_string($stream),
'venue_type' => self::deserialize_string($stream),
],
'�±ý' => [
'_' => 'messageMediaGame',
'game' => match (stream_get_contents($stream, 4)) {
';eù½' => self::deserialize_game($stream),
'¡Ïr0' => self::deserialize_type_Game(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ÓH¥ö' => self::deserialize_messageMediaInvoice($stream),
'fÆ@¹' => self::deserialize_messageMediaGeoLive($stream),
'˜çÖK' => [
'_' => 'messageMediaPoll',
'poll' => match (stream_get_contents($stream, 4)) {
'a�á†' => self::deserialize_poll($stream),
'¡Ïr0' => self::deserialize_type_Poll(self::gzdecode($stream)),
default => self::err($stream)
}
,
'results' => match (stream_get_contents($stream, 4)) {
' $ßz' => self::deserialize_pollResults($stream),
'¡Ïr0' => self::deserialize_type_PollResults(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'‹å~?' => [
'_' => 'messageMediaDice',
'value' => unpack('l', stream_get_contents($stream, 4))[1],
'emoticon' => self::deserialize_string($stream),
],
'ƒbËh' => self::deserialize_messageMediaStory($stream),
'°…­Ú' => self::deserialize_messageMediaGiveaway($stream),
'h™Æ' => self::deserialize_messageMediaGiveawayResults($stream),
'¡Ïr0' => self::deserialize_type_MessageMedia(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_MessageExtendedMedia(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ãû.' => self::deserialize_updateChannelPinnedTopic($stream),
'†þ' => self::deserialize_updateChannelPinnedTopics($stream),
'8”R ' => [
'_' => 'updateUser',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'—°ì' => [
'_' => 'updateAutoSaveSettings',
],
'ÖŠðÌ' => [
'_' => 'updateGroupInvitePrivacyForbidden',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'˜·³u' => [
'_' => 'updateStory',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'story' => match (stream_get_contents($stream, 4)) {
'OîæQ' => [
'_' => 'storyItemDeleted',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'É­ÿ' => self::deserialize_storyItemSkipped($stream),
'$j²y' => self::deserialize_storyItem($stream),
'¡Ïr0' => self::deserialize_type_StoryItem(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'+“N÷' => [
'_' => 'updateReadStories',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'max_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¹5ó' => [
'_' => 'updateStoryID',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'random_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÁM,' => [
'_' => 'updateStoriesStealthMode',
'stealth_mode' => match (stream_get_contents($stream, 4)) {
'ý\'.q' => self::deserialize_storiesStealthMode($stream),
'¡Ïr0' => self::deserialize_type_StoriesStealthMode(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ƒvb}' => [
'_' => 'updateSentStoryReaction',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'story_id' => unpack('l', stream_get_contents($stream, 4))[1],
'reaction' => match (stream_get_contents($stream, 4)) {
'Ôõy' => [
'_' => 'reactionEmpty',
],
'¸†"' => [
'_' => 'reactionEmoji',
'emoticon' => self::deserialize_string($stream),
],
'sü5‰' => [
'_' => 'reactionCustomEmoji',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_Reaction(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'œÔM�' => [
'_' => 'updateBotChatBoost',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'boost' => match (stream_get_contents($stream, 4)) {
'qŒ*' => self::deserialize_boost($stream),
'¡Ïr0' => self::deserialize_type_Boost(self::gzdecode($stream)),
default => self::err($stream)
}
,
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
' ‰¶' => [
'_' => 'updateChannelViewForumAsMessages',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'enabled' => match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) },
],
'?®' => self::deserialize_updatePeerWallpaper($stream),
'ÎÓ!¬' => [
'_' => 'updateBotMessageReaction',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'actor' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'old_reactions' => self::deserialize_type_array_of_Reaction(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'new_reactions' => self::deserialize_type_array_of_Reaction(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'YwË	' => [
'_' => 'updateBotMessageReactions',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'reactions' => self::deserialize_type_array_of_ReactionCount(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'tž¯®' => self::deserialize_updateSavedDialogPinned($stream),
'¦…lh' => self::deserialize_updatePinnedSavedDialogs($stream),
'2tÆ9' => [
'_' => 'updateSavedReactionTags',
],
'Ôibñ' => [
'_' => 'updateSmsJob',
'job_id' => self::deserialize_string($stream),
],
'²
Gù' => [
'_' => 'updateQuickReplies',
'quick_replies' => self::deserialize_type_array_of_QuickReply(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'§=õ' => [
'_' => 'updateNewQuickReply',
'quick_reply' => match (stream_get_contents($stream, 4)) {
'+—' => [
'_' => 'quickReply',
'shortcut_id' => unpack('l', stream_get_contents($stream, 4))[1],
'shortcut' => self::deserialize_string($stream),
'top_message' => unpack('l', stream_get_contents($stream, 4))[1],
'count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_QuickReply(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ìñæS' => [
'_' => 'updateDeleteQuickReply',
'shortcut_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'>' => [
'_' => 'updateQuickReplyMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ÍçoV' => [
'_' => 'updateDeleteQuickReplyMessages',
'shortcut_id' => unpack('l', stream_get_contents($stream, 4))[1],
'messages' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => self::deserialize_type_Update(self::gzdecode($stream)),
default => self::err($stream)
}
,
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Ã[r' => [
'_' => 'updatesCombined',
'updates' => self::deserialize_type_array_of_Update(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'seq_start' => unpack('l', stream_get_contents($stream, 4))[1],
'seq' => unpack('l', stream_get_contents($stream, 4))[1],
],
'@B®t' => [
'_' => 'updates',
'updates' => self::deserialize_type_array_of_Update(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'seq' => unpack('l', stream_get_contents($stream, 4))[1],
],
'á�' => self::deserialize_updateShortSentMessage($stream),
'¡Ïr0' => self::deserialize_type_Updates(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'9AØ' => [
'_' => 'payments.paymentVerificationNeeded',
'url' => self::deserialize_string($stream),
],
'<ä�û' => self::deserialize_payments___savedInfo($stream),
'så$>' => [
'_' => 'payments.bankCardData',
'title' => self::deserialize_string($stream),
'open_urls' => self::deserialize_type_array_of_BankCardOpenUrl(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ÙËÐ®' => [
'_' => 'payments.exportedInvoice',
'url' => self::deserialize_string($stream),
],
'–J(' => self::deserialize_payments___checkedGiftCode($stream),
' ÚgC' => self::deserialize_payments___giveawayInfo($stream),
'pUÍ' . "\0" . '' => self::deserialize_payments___giveawayInfoResults($stream),
'? þ…' => [
'_' => 'stickers.suggestedShortName',
'short_name' => self::deserialize_string($stream),
],
'@á‚ì' => [
'_' => 'phone.phoneCall',
'phone_call' => match (stream_get_contents($stream, 4)) {
'ÉfS' => [
'_' => 'phoneCallEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'o"Å' => self::deserialize_phoneCallWaiting($stream),
'í°' => self::deserialize_phoneCallRequested($stream),
'Ã`6' => self::deserialize_phoneCallAccepted($stream),
'g|–' => self::deserialize_phoneCall($stream),
'áMÊP' => self::deserialize_phoneCallDiscarded($stream),
'¡Ïr0' => self::deserialize_type_PhoneCall(self::gzdecode($stream)),
default => self::err($stream)
}
,
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'­zrž' => [
'_' => 'phone.groupCall',
'call' => match (stream_get_contents($stream, 4)) {
'´¼€w' => [
'_' => 'groupCallDiscarded',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'duration' => unpack('l', stream_get_contents($stream, 4))[1],
],
'e—Õ' => self::deserialize_groupCall($stream),
'¡Ïr0' => self::deserialize_type_GroupCall(self::gzdecode($stream)),
default => self::err($stream)
}
,
'participants' => self::deserialize_type_array_of_GroupCallParticipant(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'participants_next_offset' => self::deserialize_string($stream),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¶Qwô' => [
'_' => 'phone.groupParticipants',
'count' => unpack('l', stream_get_contents($stream, 4))[1],
'participants' => self::deserialize_type_array_of_GroupCallParticipant(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'next_offset' => self::deserialize_string($stream),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
'?bå¯' => [
'_' => 'phone.joinAsPeers',
'peers' => self::deserialize_type_array_of_Peer(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'XÑK ' => [
'_' => 'phone.exportedGroupCallInvite',
'link' => self::deserialize_string($stream),
],
'²‚äÐ' => [
'_' => 'phone.groupCallStreamChannels',
'channels' => self::deserialize_type_array_of_GroupCallStreamChannel(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'24¿-' => [
'_' => 'phone.groupCallStreamRtmpUrl',
'url' => self::deserialize_string($stream),
'key' => self::deserialize_string($stream),
],
'öÁ…ó' => [
'_' => 'langPackDifference',
'lang_code' => self::deserialize_string($stream),
'from_version' => unpack('l', stream_get_contents($stream, 4))[1],
'version' => unpack('l', stream_get_contents($stream, 4))[1],
'strings' => self::deserialize_type_array_of_LangPackString(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ã\\Êî' => self::deserialize_langPackLanguage($stream),
'ü¥l9' => [
'_' => 'stats.broadcastStats',
'period' => match (stream_get_contents($stream, 4)) {
'¯í7¶' => [
'_' => 'statsDateRangeDays',
'min_date' => unpack('l', stream_get_contents($stream, 4))[1],
'max_date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_StatsDateRangeDays(self::gzdecode($stream)),
default => self::err($stream)
}
,
'followers' => match (stream_get_contents($stream, 4)) {
'Þ¬CË' => [
'_' => 'statsAbsValueAndPrev',
'current' => unpack('d', stream_get_contents($stream, 8))[1],
'previous' => unpack('d', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_StatsAbsValueAndPrev(self::gzdecode($stream)),
default => self::err($stream)
}
,
'views_per_post' => match (stream_get_contents($stream, 4)) {
'Þ¬CË' => [
'_' => 'statsAbsValueAndPrev',
'current' => unpack('d', stream_get_contents($stream, 8))[1],
'previous' => unpack('d', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_StatsAbsValueAndPrev(self::gzdecode($stream)),
default => self::err($stream)
}
,
'shares_per_post' => match (stream_get_contents($stream, 4)) {
'Þ¬CË' => [
'_' => 'statsAbsValueAndPrev',
'current' => unpack('d', stream_get_contents($stream, 8))[1],
'previous' => unpack('d', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_StatsAbsValueAndPrev(self::gzdecode($stream)),
default => self::err($stream)
}
,
'reactions_per_post' => match (stream_get_contents($stream, 4)) {
'Þ¬CË' => [
'_' => 'statsAbsValueAndPrev',
'current' => unpack('d', stream_get_contents($stream, 8))[1],
'previous' => unpack('d', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_StatsAbsValueAndPrev(self::gzdecode($stream)),
default => self::err($stream)
}
,
'views_per_story' => match (stream_get_contents($stream, 4)) {
'Þ¬CË' => [
'_' => 'statsAbsValueAndPrev',
'current' => unpack('d', stream_get_contents($stream, 8))[1],
'previous' => unpack('d', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_StatsAbsValueAndPrev(self::gzdecode($stream)),
default => self::err($stream)
}
,
'shares_per_story' => match (stream_get_contents($stream, 4)) {
'Þ¬CË' => [
'_' => 'statsAbsValueAndPrev',
'current' => unpack('d', stream_get_contents($stream, 8))[1],
'previous' => unpack('d', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_StatsAbsValueAndPrev(self::gzdecode($stream)),
default => self::err($stream)
}
,
'reactions_per_story' => match (stream_get_contents($stream, 4)) {
'Þ¬CË' => [
'_' => 'statsAbsValueAndPrev',
'current' => unpack('d', stream_get_contents($stream, 8))[1],
'previous' => unpack('d', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_StatsAbsValueAndPrev(self::gzdecode($stream)),
default => self::err($stream)
}
,
'enabled_notifications' => match (stream_get_contents($stream, 4)) {
'à/ÎË' => [
'_' => 'statsPercentValue',
'part' => unpack('d', stream_get_contents($stream, 8))[1],
'total' => unpack('d', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_StatsPercentValue(self::gzdecode($stream)),
default => self::err($stream)
}
,
'growth_graph' => match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => self::deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
,
'followers_graph' => match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => self::deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
,
'mute_graph' => match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => self::deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
,
'top_hours_graph' => match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => self::deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
,
'interactions_graph' => match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => self::deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
,
'iv_interactions_graph' => match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => self::deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
,
'views_by_source_graph' => match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => self::deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
,
'new_followers_by_source_graph' => match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => self::deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
,
'languages_graph' => match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => self::deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
,
'reactions_by_emotion_graph' => match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => self::deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
,
'story_interactions_graph' => match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => self::deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
,
'story_reactions_by_emotion_graph' => match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => self::deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
,
'recent_posts_interactions' => self::deserialize_type_array_of_PostInteractionCounters(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'ùï' => [
'_' => 'stats.megagroupStats',
'period' => match (stream_get_contents($stream, 4)) {
'¯í7¶' => [
'_' => 'statsDateRangeDays',
'min_date' => unpack('l', stream_get_contents($stream, 4))[1],
'max_date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_StatsDateRangeDays(self::gzdecode($stream)),
default => self::err($stream)
}
,
'members' => match (stream_get_contents($stream, 4)) {
'Þ¬CË' => [
'_' => 'statsAbsValueAndPrev',
'current' => unpack('d', stream_get_contents($stream, 8))[1],
'previous' => unpack('d', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_StatsAbsValueAndPrev(self::gzdecode($stream)),
default => self::err($stream)
}
,
'messages' => match (stream_get_contents($stream, 4)) {
'Þ¬CË' => [
'_' => 'statsAbsValueAndPrev',
'current' => unpack('d', stream_get_contents($stream, 8))[1],
'previous' => unpack('d', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_StatsAbsValueAndPrev(self::gzdecode($stream)),
default => self::err($stream)
}
,
'viewers' => match (stream_get_contents($stream, 4)) {
'Þ¬CË' => [
'_' => 'statsAbsValueAndPrev',
'current' => unpack('d', stream_get_contents($stream, 8))[1],
'previous' => unpack('d', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_StatsAbsValueAndPrev(self::gzdecode($stream)),
default => self::err($stream)
}
,
'posters' => match (stream_get_contents($stream, 4)) {
'Þ¬CË' => [
'_' => 'statsAbsValueAndPrev',
'current' => unpack('d', stream_get_contents($stream, 8))[1],
'previous' => unpack('d', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_StatsAbsValueAndPrev(self::gzdecode($stream)),
default => self::err($stream)
}
,
'growth_graph' => match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => self::deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
,
'members_graph' => match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => self::deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
,
'new_members_by_source_graph' => match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => self::deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
,
'languages_graph' => match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => self::deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
,
'messages_graph' => match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => self::deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
,
'actions_graph' => match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => self::deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
,
'top_hours_graph' => match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => self::deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
,
'weekdays_graph' => match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => self::deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
,
'top_posters' => self::deserialize_type_array_of_StatsGroupTopPoster(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'top_admins' => self::deserialize_type_array_of_StatsGroupTopAdmin(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'top_inviters' => self::deserialize_type_array_of_StatsGroupTopInviter(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
' ~“' => self::deserialize_stats___publicForwards($stream),
'é' => [
'_' => 'stats.messageStats',
'views_graph' => match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => self::deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
,
'reactions_by_emotion_graph' => match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => self::deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'|ÍP' => [
'_' => 'stats.storyStats',
'views_graph' => match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => self::deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
,
'reactions_by_emotion_graph' => match (stream_get_contents($stream, 4)) {
'-ë\'J' => [
'_' => 'statsGraphAsync',
'token' => self::deserialize_string($stream),
],
'"˜Ü¾' => [
'_' => 'statsGraphError',
'error' => self::deserialize_string($stream),
],
'¶d¤Ž' => self::deserialize_statsGraph($stream),
'¡Ïr0' => self::deserialize_type_StatsGraph(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¦ãæ' => [
'_' => 'chatlists.exportedChatlistInvite',
'filter' => match (stream_get_contents($stream, 4)) {
';Rµ_' => self::deserialize_dialogFilter($stream),
'®“26' => [
'_' => 'dialogFilterDefault',
],
'¤ŽâŸ' => self::deserialize_dialogFilterChatlist($stream),
'¡Ïr0' => self::deserialize_type_DialogFilter(self::gzdecode($stream)),
default => self::err($stream)
}
,
'invite' => match (stream_get_contents($stream, 4)) {
'¬�Q' => [
'_' => 'exportedChatlistInvite',
'flags' => unpack('V', stream_get_contents($stream, 4))[1],
'title' => self::deserialize_string($stream),
'url' => self::deserialize_string($stream),
'peers' => self::deserialize_type_array_of_Peer(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => self::deserialize_type_ExportedChatlistInvite(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¬�Q' => [
'_' => 'exportedChatlistInvite',
'flags' => unpack('V', stream_get_contents($stream, 4))[1],
'title' => self::deserialize_string($stream),
'url' => self::deserialize_string($stream),
'peers' => self::deserialize_type_array_of_Peer(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'Çm«' => [
'_' => 'chatlists.exportedInvites',
'invites' => self::deserialize_type_array_of_ExportedChatlistInvite(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'Yö‡ú' => [
'_' => 'chatlists.chatlistInviteAlready',
'filter_id' => unpack('l', stream_get_contents($stream, 4))[1],
'missing_peers' => self::deserialize_type_array_of_Peer(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'already_peers' => self::deserialize_type_array_of_Peer(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'�ƒÍ' => self::deserialize_chatlists___chatlistInvite($stream),
'�‡½“' => [
'_' => 'chatlists.chatlistUpdates',
'missing_peers' => self::deserialize_type_array_of_Peer(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'>þX' => [
'_' => 'stories.allStoriesNotModified',
'flags' => unpack('V', stream_get_contents($stream, 4))[1],
'state' => self::deserialize_string($stream),
'stealth_mode' => match (stream_get_contents($stream, 4)) {
'ý\'.q' => self::deserialize_storiesStealthMode($stream),
'¡Ïr0' => self::deserialize_type_StoriesStealthMode(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'�^ün' => self::deserialize_stories___allStories($stream),
'ÈÃØ]' => [
'_' => 'stories.stories',
'count' => unpack('l', stream_get_contents($stream, 4))[1],
'stories' => self::deserialize_type_array_of_StoryItem(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'Å�×Y' => self::deserialize_stories___storyViewsList($stream),
'ížÞ' => [
'_' => 'stories.storyViews',
'views' => self::deserialize_type_array_of_StoryViews(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
';É?' => [
'_' => 'exportedStoryLink',
'link' => self::deserialize_string($stream),
],
'h‡æÊ' => [
'_' => 'stories.peerStories',
'stories' => match (stream_get_contents($stream, 4)) {
'™é5š' => self::deserialize_peerStories($stream),
'¡Ïr0' => self::deserialize_type_PeerStories(self::gzdecode($stream)),
default => self::err($stream)
}
,
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'œx_ª' => self::deserialize_stories___storyReactionsList($stream),
'<aø†' => self::deserialize_premium___boostsList($stream),
'â(âš' => [
'_' => 'premium.myBoosts',
'my_boosts' => self::deserialize_type_array_of_MyBoost(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chats' => self::deserialize_type_array_of_Chat(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'zBYI' => self::deserialize_premium___boostsStatus($stream),
'ÏD‹Ü' => [
'_' => 'smsjobs.eligibleToJoin',
'terms_url' => self::deserialize_string($stream),
'monthly_sent_sms' => unpack('l', stream_get_contents($stream, 4))[1],
],
'‘‘î*' => self::deserialize_smsjobs___status($stream),
'¸î¡æ' => [
'_' => 'smsJob',
'job_id' => self::deserialize_string($stream),
'phone_number' => self::deserialize_string($stream),
'text' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_MethodResult(self::gzdecode($stream), $method),
'Äµ' => match ($method) {
'account.getAllSecureValues' => self::deserialize_type_array_of_SecureValue(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'account.getSecureValue' => self::deserialize_type_array_of_SecureValue(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'account.getMultiWallPapers' => self::deserialize_type_array_of_WallPaper(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users.getUsers' => self::deserialize_type_array_of_User(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'users.getIsPremiumRequiredToContact' => self::deserialize_type_array_of_Bool(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'contacts.getContactIDs' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'contacts.getStatuses' => self::deserialize_type_array_of_ContactStatus(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'contacts.getSaved' => self::deserialize_type_array_of_SavedContact(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'messages.receivedMessages' => self::deserialize_type_array_of_ReceivedNotifyMessage(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'messages.receivedQueue' => self::deserialize_type_array_of_long(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'messages.getAttachedStickers' => self::deserialize_type_array_of_StickerSetCovered(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'messages.getSplitRanges' => self::deserialize_type_array_of_MessageRange(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'messages.getDialogUnreadMarks' => self::deserialize_type_array_of_DialogPeer(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'messages.getEmojiKeywordsLanguages' => self::deserialize_type_array_of_EmojiLanguage(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'messages.getSearchCounters' => self::deserialize_type_array_of_messages___SearchCounter(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'messages.getSuggestedDialogFilters' => self::deserialize_type_array_of_DialogFilterSuggested(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'messages.getMessageReadParticipants' => self::deserialize_type_array_of_ReadParticipantDate(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'messages.getCustomEmojiDocuments' => self::deserialize_type_array_of_Document(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'photos.deletePhotos' => self::deserialize_type_array_of_long(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'upload.reuploadCdnFile' => self::deserialize_type_array_of_FileHash(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'upload.getCdnFileHashes' => self::deserialize_type_array_of_FileHash(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'upload.getFileHashes' => self::deserialize_type_array_of_FileHash(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'bots.getBotCommands' => self::deserialize_type_array_of_BotCommand(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'payments.getPremiumGiftCodeOptions' => self::deserialize_type_array_of_PremiumGiftCodeOption(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'phone.checkGroupCall' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'langpack.getStrings' => self::deserialize_type_array_of_LangPackString(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'langpack.getLanguages' => self::deserialize_type_array_of_LangPackLanguage(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'chatlists.getLeaveChatlistSuggestions' => self::deserialize_type_array_of_Peer(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'stories.deleteStories' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'stories.togglePinned' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'stories.readStories' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'stories.getPeerMaxIDs' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
},
default => self::err($stream)
}
;
            if (isset($this->afterMethodResponseDeserialization[$method])) {
                foreach ($this->afterMethodResponseDeserialization[$method] as $callback) {
                    $callback($tmp);
                }
            }
            return $tmp;

    }
    private  function deserialize_type_array_of_future_salt(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= [
'_' => 'future_salt',
'valid_since' => unpack('l', stream_get_contents($stream, 4))[1],
'valid_until' => unpack('l', stream_get_contents($stream, 4))[1],
'salt' => unpack('q', stream_get_contents($stream, 8))[1],
];
                }
                return $result;    
            
    }
    private  function deserialize_type_array_of_MTmessage(mixed $stream): array {

                $result = [];
                for ($x = unpack("V", stream_get_contents($stream, 4))[1]; $x > 0; --$x) {    
                    $result []= [
'_' => 'MTmessage',
'msg_id' => unpack('q', stream_get_contents($stream, 8))[1],
'seqno' => unpack('l', stream_get_contents($stream, 4))[1],
'bytes' => unpack('l', stream_get_contents($stream, 4))[1],
'body' => match (stream_get_contents($stream, 4)) {
'¡Ïr0' => $this->deserialize_type_Object(self::gzdecode($stream)),
default => self::err($stream)
}
,
];
                }
                return $result;    
            
    }
    private  function deserialize_type_MTMessage(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'å¸[' => [
'_' => 'MTmessage',
'msg_id' => unpack('q', stream_get_contents($stream, 8))[1],
'seqno' => unpack('l', stream_get_contents($stream, 4))[1],
'bytes' => unpack('l', stream_get_contents($stream, 4))[1],
'body' => match (stream_get_contents($stream, 4)) {
'¡Ïr0' => $this->deserialize_type_Object(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => $this->deserialize_type_MTMessage(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
    public  function deserialize_type_Object(mixed $stream): mixed {
return match (stream_get_contents($stream, 4)) {
'ý
+' => [
'_' => 'updateNewMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Ö¿�N' => [
'_' => 'updateMessageID',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'random_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'å°¢' => [
'_' => 'updateDeleteMessages',
'messages' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'…À' => [
'_' => 'updateUserTyping',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'action' => match (stream_get_contents($stream, 4)) {
'Nt¿' => [
'_' => 'sendMessageTypingAction',
],
'õÈ^ý' => [
'_' => 'sendMessageCancelAction',
],
'oÖ‡¡' => [
'_' => 'sendMessageRecordVideoAction',
],
'ì:vé' => [
'_' => 'sendMessageUploadVideoAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'÷s/Õ' => [
'_' => 'sendMessageRecordAudioAction',
],
'«×Qó' => [
'_' => 'sendMessageUploadAudioAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'&JÓÑ' => [
'_' => 'sendMessageUploadPhotoAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'äÙª' => [
'_' => 'sendMessageUploadDocumentAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡‹o' => [
'_' => 'sendMessageGeoLocationAction',
],
'o¼Œb' => [
'_' => 'sendMessageChooseContactAction',
],
'H�jÝ' => [
'_' => 'sendMessageGamePlayAction',
],
'¼òˆ' => [
'_' => 'sendMessageRecordRoundAction',
],
'f>$' => [
'_' => 'sendMessageUploadRoundAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'…",Ù' => [
'_' => 'speakingInGroupCallAction',
],
'F’ÚÛ' => [
'_' => 'sendMessageHistoryImportAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'±ÆZ°' => [
'_' => 'sendMessageChooseStickerAction',
],
'Ë+—%' => [
'_' => 'sendMessageEmojiInteraction',
'emoticon' => self::deserialize_string($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'interaction' => match (stream_get_contents($stream, 4)) {
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'¡Ïr0' => self::deserialize_type_DataJSON(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'.�e¶' => [
'_' => 'sendMessageEmojiInteractionSeen',
'emoticon' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_SendMessageAction(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ðzHƒ' => [
'_' => 'updateChatUserTyping',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'from_id' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'action' => match (stream_get_contents($stream, 4)) {
'Nt¿' => [
'_' => 'sendMessageTypingAction',
],
'õÈ^ý' => [
'_' => 'sendMessageCancelAction',
],
'oÖ‡¡' => [
'_' => 'sendMessageRecordVideoAction',
],
'ì:vé' => [
'_' => 'sendMessageUploadVideoAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'÷s/Õ' => [
'_' => 'sendMessageRecordAudioAction',
],
'«×Qó' => [
'_' => 'sendMessageUploadAudioAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'&JÓÑ' => [
'_' => 'sendMessageUploadPhotoAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'äÙª' => [
'_' => 'sendMessageUploadDocumentAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡‹o' => [
'_' => 'sendMessageGeoLocationAction',
],
'o¼Œb' => [
'_' => 'sendMessageChooseContactAction',
],
'H�jÝ' => [
'_' => 'sendMessageGamePlayAction',
],
'¼òˆ' => [
'_' => 'sendMessageRecordRoundAction',
],
'f>$' => [
'_' => 'sendMessageUploadRoundAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'…",Ù' => [
'_' => 'speakingInGroupCallAction',
],
'F’ÚÛ' => [
'_' => 'sendMessageHistoryImportAction',
'progress' => unpack('l', stream_get_contents($stream, 4))[1],
],
'±ÆZ°' => [
'_' => 'sendMessageChooseStickerAction',
],
'Ë+—%' => [
'_' => 'sendMessageEmojiInteraction',
'emoticon' => self::deserialize_string($stream),
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'interaction' => match (stream_get_contents($stream, 4)) {
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'¡Ïr0' => self::deserialize_type_DataJSON(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'.�e¶' => [
'_' => 'sendMessageEmojiInteractionSeen',
'emoticon' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_SendMessageAction(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'˜v' => [
'_' => 'updateChatParticipants',
'participants' => match (stream_get_contents($stream, 4)) {
'áÓc‡' => self::deserialize_chatParticipantsForbidden($stream),
'ø“¼<' => [
'_' => 'chatParticipants',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'participants' => self::deserialize_type_array_of_ChatParticipant(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_ChatParticipants(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Þø½å' => [
'_' => 'updateUserStatus',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'status' => match (stream_get_contents($stream, 4)) {
'IPÐ	' => [
'_' => 'userStatusEmpty',
],
'I9¹í' => [
'_' => 'userStatusOnline',
'expires' => unpack('l', stream_get_contents($stream, 4))[1],
],
'?pŒ' . "\0" . '' => [
'_' => 'userStatusOffline',
'was_online' => unpack('l', stream_get_contents($stream, 4))[1],
],
'È}{' => self::deserialize_userStatusRecently($stream),
'T' => self::deserialize_userStatusLastWeek($stream),
'w—‰e' => self::deserialize_userStatusLastMonth($stream),
'¡Ïr0' => self::deserialize_type_UserStatus(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'$‰„§' => [
'_' => 'updateUserName',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'first_name' => self::deserialize_string($stream),
'last_name' => self::deserialize_string($stream),
'usernames' => self::deserialize_type_array_of_Username(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ï«Q‰' => self::deserialize_updateNewAuthorization($stream),
'š½¼' => [
'_' => 'updateNewEncryptedMessage',
'message' => match (stream_get_contents($stream, 4)) {
'Áí' => [
'_' => 'encryptedMessage',
'random_id' => unpack('q', stream_get_contents($stream, 8))[1],
'chat_id' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'bytes' => self::deserialize_bytes($stream),
'file' => match (stream_get_contents($stream, 4)) {
'~IÂ' => [
'_' => 'encryptedFileEmpty',
],
'ØŒ' . "\0" . '¨' => [
'_' => 'encryptedFile',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'size' => unpack('q', stream_get_contents($stream, 8))[1],
'dc_id' => unpack('l', stream_get_contents($stream, 4))[1],
'key_fingerprint' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_EncryptedFile(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Ks#' => [
'_' => 'encryptedMessageService',
'random_id' => unpack('q', stream_get_contents($stream, 8))[1],
'chat_id' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'bytes' => self::deserialize_bytes($stream),
],
'¡Ïr0' => self::deserialize_type_EncryptedMessage(self::gzdecode($stream)),
default => self::err($stream)
}
,
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Vñ' => [
'_' => 'updateEncryptedChatTyping',
'chat_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'�è¢´' => [
'_' => 'updateEncryption',
'chat' => match (stream_get_contents($stream, 4)) {
' À~«' => [
'_' => 'encryptedChatEmpty',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'SY²f' => [
'_' => 'encryptedChatWaiting',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'admin_id' => unpack('q', stream_get_contents($stream, 8))[1],
'participant_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'LÙñH' => self::deserialize_encryptedChatRequested($stream),
'ÇÔða' => [
'_' => 'encryptedChat',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'admin_id' => unpack('q', stream_get_contents($stream, 8))[1],
'participant_id' => unpack('q', stream_get_contents($stream, 8))[1],
'g_a_or_b' => self::deserialize_bytes($stream),
'key_fingerprint' => stream_get_contents($stream, 8),
],
'E|' => self::deserialize_encryptedChatDiscarded($stream),
'¡Ïr0' => self::deserialize_type_EncryptedChat(self::gzdecode($stream)),
default => self::err($stream)
}
,
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'·%þ8' => [
'_' => 'updateEncryptedMessagesRead',
'chat_id' => unpack('l', stream_get_contents($stream, 4))[1],
'max_date' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
],
'QTÚ=' => [
'_' => 'updateChatParticipantAdd',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'inviter_id' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
'w=/ã' => [
'_' => 'updateChatParticipantDelete',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
's˜^Ž' => [
'_' => 'updateDcOptions',
'dc_options' => self::deserialize_type_array_of_DcOption(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ïhÂ¾' => [
'_' => 'updateNotifySettings',
'peer' => match (stream_get_contents($stream, 4)) {
'ØÔŸ' => [
'_' => 'notifyPeer',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'L;È´' => [
'_' => 'notifyUsers',
],
'ÃÎÀ' => [
'_' => 'notifyChats',
],
'ïèÖ' => [
'_' => 'notifyBroadcasts',
],
'cn"' => [
'_' => 'notifyForumTopic',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'top_msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_NotifyPeer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'notify_settings' => match (stream_get_contents($stream, 4)) {
',b™' => self::deserialize_peerNotifySettings($stream),
'¡Ïr0' => self::deserialize_type_PeerNotifySettings(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'häë' => self::deserialize_updateServiceNotification($stream),
'*\';î' => [
'_' => 'updatePrivacy',
'key' => match (stream_get_contents($stream, 4)) {
'0«.¼' => [
'_' => 'privacyKeyStatusTimestamp',
],
'úmP' => [
'_' => 'privacyKeyChatInvite',
],
'{+f=' => [
'_' => 'privacyKeyPhoneCall',
],
'ÈI9' => [
'_' => 'privacyKeyPhoneP2P',
],
'£Vìi' => [
'_' => 'privacyKeyForwards',
],
'í–' => [
'_' => 'privacyKeyProfilePhoto',
],
'mäšÑ' => [
'_' => 'privacyKeyPhoneNumber',
],
'+ÔÿB' => [
'_' => 'privacyKeyAddedByPhone',
],
'ô—' => [
'_' => 'privacyKeyVoiceMessages',
],
'a·†¤' => [
'_' => 'privacyKeyAbout',
],
'¡Ïr0' => self::deserialize_type_PrivacyKey(self::gzdecode($stream)),
default => self::err($stream)
}
,
'rules' => self::deserialize_type_array_of_PrivacyRule(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'*I' => [
'_' => 'updateUserPhone',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'phone' => self::deserialize_string($stream),
],
'ßO—œ' => self::deserialize_updateReadHistoryInbox($stream),
'¿!//' => [
'_' => 'updateReadHistoryOutbox',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'max_id' => unpack('l', stream_get_contents($stream, 4))[1],
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'‰' => [
'_' => 'updateWebPage',
'webpage' => match (stream_get_contents($stream, 4)) {
'ˆ!' => self::deserialize_webPageEmpty($stream),
'G>Ñ°' => self::deserialize_webPagePending($stream),
'²Eœè' => self::deserialize_webPage($stream),
'Ês' => self::deserialize_webPageNotModified($stream),
'¡Ïr0' => self::deserialize_type_WebPage(self::gzdecode($stream)),
default => self::err($stream)
}
,
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'�q"ø' => self::deserialize_updateReadMessagesContents($stream),
'”�' => self::deserialize_updateChannelTooLong($stream),
'	L[c' => [
'_' => 'updateChannel',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'Ùºb' => [
'_' => 'updateNewChannelMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'n.’' => self::deserialize_updateReadChannelInbox($stream),
'[-Ã' => [
'_' => 'updateDeleteChannelMessages',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'messages' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¬&ò' => [
'_' => 'updateChannelMessageViews',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'views' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¢aÊ×' => [
'_' => 'updateChatParticipantAdmin',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'is_admin' => match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) },
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
'ª0Šh' => [
'_' => 'updateNewStickerSet',
'stickerset' => match (stream_get_contents($stream, 4)) {
'?n' => self::deserialize_messages___stickerSet($stream),
'ë$ùÓ' => [
'_' => 'messages.stickerSetNotModified',
],
'¡Ïr0' => self::deserialize_type_messages___StickerSet(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Ò²' => self::deserialize_updateStickerSetsOrder($stream),
'HÂ1' => self::deserialize_updateStickerSets($stream),
'4u“' => [
'_' => 'updateSavedGifs',
],
'œ7oI' => self::deserialize_updateBotInlineQuery($stream),
'*ñ' => self::deserialize_updateBotInlineSend($stream),
'÷M?' => [
'_' => 'updateEditChannelMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'�ÄÏ¹' => self::deserialize_updateBotCallbackQuery($stream),
'£pä' => [
'_' => 'updateEditMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'R�i' => self::deserialize_updateInlineBotCallbackQuery($stream),
'©™_·' => [
'_' => 'updateReadChannelOutbox',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'max_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'mìI' => self::deserialize_updateDraftMessage($stream),
'B\'W' => [
'_' => 'updateReadFeaturedStickers',
],
' ,Bš' => [
'_' => 'updateRecentStickers',
],
'Ý)¢' => [
'_' => 'updateConfig',
],
'�gT3' => [
'_' => 'updatePtsChanged',
],
'Ÿ©+/' => [
'_' => 'updateChannelWebPage',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'webpage' => match (stream_get_contents($stream, 4)) {
'ˆ!' => self::deserialize_webPageEmpty($stream),
'G>Ñ°' => self::deserialize_webPagePending($stream),
'²Eœè' => self::deserialize_webPage($stream),
'Ês' => self::deserialize_webPageNotModified($stream),
'¡Ïr0' => self::deserialize_type_WebPage(self::gzdecode($stream)),
default => self::err($stream)
}
,
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'åon' => self::deserialize_updateDialogPinned($stream),
'¢<ú' => self::deserialize_updatePinnedDialogs($stream),
'ÃÀƒ' => [
'_' => 'updateBotWebhookJSON',
'data' => match (stream_get_contents($stream, 4)) {
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'¡Ïr0' => self::deserialize_type_DataJSON(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¦@’›' => [
'_' => 'updateBotWebhookJSONQuery',
'query_id' => unpack('q', stream_get_contents($stream, 8))[1],
'data' => match (stream_get_contents($stream, 4)) {
'�t}' => json_decode(self::deserialize_string($stream), true, 512, \JSON_THROW_ON_ERROR),
'¡Ïr0' => self::deserialize_type_DataJSON(self::gzdecode($stream)),
default => self::err($stream)
}
,
'timeout' => unpack('l', stream_get_contents($stream, 4))[1],
],
'}ý®µ' => [
'_' => 'updateBotShippingQuery',
'query_id' => unpack('q', stream_get_contents($stream, 8))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'payload' => self::deserialize_bytes($stream),
'shipping_address' => match (stream_get_contents($stream, 4)) {
'ëªŒ' => [
'_' => 'postAddress',
'street_line1' => self::deserialize_string($stream),
'street_line2' => self::deserialize_string($stream),
'city' => self::deserialize_string($stream),
'state' => self::deserialize_string($stream),
'country_iso2' => self::deserialize_string($stream),
'post_code' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_PostAddress(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'–šªŒ' => self::deserialize_updateBotPrecheckoutQuery($stream),
'k«' => [
'_' => 'updatePhoneCall',
'phone_call' => match (stream_get_contents($stream, 4)) {
'ÉfS' => [
'_' => 'phoneCallEmpty',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'o"Å' => self::deserialize_phoneCallWaiting($stream),
'í°' => self::deserialize_phoneCallRequested($stream),
'Ã`6' => self::deserialize_phoneCallAccepted($stream),
'g|–' => self::deserialize_phoneCall($stream),
'áMÊP' => self::deserialize_phoneCallDiscarded($stream),
'¡Ïr0' => self::deserialize_type_PhoneCall(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'dVF' => [
'_' => 'updateLangPackTooLong',
'lang_code' => self::deserialize_string($stream),
],
'M/V' => [
'_' => 'updateLangPack',
'difference' => match (stream_get_contents($stream, 4)) {
'öÁ…ó' => [
'_' => 'langPackDifference',
'lang_code' => self::deserialize_string($stream),
'from_version' => unpack('l', stream_get_contents($stream, 4))[1],
'version' => unpack('l', stream_get_contents($stream, 4))[1],
'strings' => self::deserialize_type_array_of_LangPackString(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'¡Ïr0' => self::deserialize_type_LangPackDifference(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'm™å' => [
'_' => 'updateFavedStickers',
],
'])ê' => self::deserialize_updateChannelReadMessagesContents($stream),
'¾§„p' => [
'_' => 'updateContactsReset',
],
'˜Æ?²' => [
'_' => 'updateChannelAvailableMessages',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'available_min_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'ÃYdá' => self::deserialize_updateDialogUnreadMark($stream),
'{e¡¬' => self::deserialize_updateMessagePoll($stream),
'PÀT' => [
'_' => 'updateChatDefaultBannedRights',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'default_banned_rights' => match (stream_get_contents($stream, 4)) {
'Ÿ' => self::deserialize_chatBannedRights($stream),
'¡Ïr0' => self::deserialize_type_ChatBannedRights(self::gzdecode($stream)),
default => self::err($stream)
}
,
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
'À6' => [
'_' => 'updateFolderPeers',
'folder_peers' => self::deserialize_type_array_of_FolderPeer(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'pts' => unpack('l', stream_get_contents($stream, 4))[1],
'pts_count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'fs~j' => [
'_' => 'updatePeerSettings',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'settings' => match (stream_get_contents($stream, 4)) {
'¥' => self::deserialize_peerSettings($stream),
'¡Ïr0' => self::deserialize_type_PeerSettings(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'°Ï¯´' => [
'_' => 'updatePeerLocated',
'peers' => self::deserialize_type_array_of_PeerLocated(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'û¥9' => [
'_' => 'updateNewScheduledMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'îl†�' => [
'_' => 'updateDeleteScheduledMessages',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'messages' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'£û‚' => [
'_' => 'updateTheme',
'theme' => match (stream_get_contents($stream, 4)) {
'Ög ' => self::deserialize_theme($stream),
'¡Ïr0' => self::deserialize_type_Theme(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'9¹‡' => [
'_' => 'updateGeoLiveViewed',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'‘æOV' => [
'_' => 'updateLoginToken',
],
'wô$' => [
'_' => 'updateMessagePollVote',
'poll_id' => unpack('q', stream_get_contents($stream, 8))[1],
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'options' => self::deserialize_type_array_of_bytes(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'}Þÿ&' => self::deserialize_updateDialogFilter($stream),
'!×¥' => [
'_' => 'updateDialogFilterOrder',
'order' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'O‘5' => [
'_' => 'updateDialogFilters',
],
'	¿a&' => [
'_' => 'updatePhoneCallSignalingData',
'phone_call_id' => unpack('q', stream_get_contents($stream, 8))[1],
'data' => self::deserialize_bytes($stream),
],
'ô\'šÒ' => [
'_' => 'updateChannelMessageForwards',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'forwards' => unpack('l', stream_get_contents($stream, 4))[1],
],
'F•±Ö' => self::deserialize_updateReadChannelDiscussionInbox($stream),
'|ž\\i' => [
'_' => 'updateReadChannelDiscussionOutbox',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'top_msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'read_max_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'Rwàë' => self::deserialize_updatePeerBlocked($stream),
'#ÉˆŒ' => self::deserialize_updateChannelUserTyping($stream),
'µê…í' => self::deserialize_updatePinnedMessages($stream),
'†¹[' => self::deserialize_updatePinnedChannelMessages($stream),
'Njšø' => [
'_' => 'updateChat',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'NÛëò' => [
'_' => 'updateGroupCallParticipants',
'call' => match (stream_get_contents($stream, 4)) {
'„ªØ' => [
'_' => 'inputGroupCall',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_InputGroupCall(self::gzdecode($stream)),
default => self::err($stream)
}
,
'participants' => self::deserialize_type_array_of_GroupCallParticipant(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'version' => unpack('l', stream_get_contents($stream, 4))[1],
],
'' . "\0" . 'E²' => [
'_' => 'updateGroupCall',
'chat_id' => unpack('q', stream_get_contents($stream, 8))[1],
'call' => match (stream_get_contents($stream, 4)) {
'´¼€w' => [
'_' => 'groupCallDiscarded',
'id' => unpack('q', stream_get_contents($stream, 8))[1],
'access_hash' => unpack('q', stream_get_contents($stream, 8))[1],
'duration' => unpack('l', stream_get_contents($stream, 4))[1],
],
'e—Õ' => self::deserialize_groupCall($stream),
'¡Ïr0' => self::deserialize_type_GroupCall(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¥¹›»' => self::deserialize_updatePeerHistoryTTL($stream),
':f‡Ð' => self::deserialize_updateChatParticipant($stream),
'»:]˜' => self::deserialize_updateChannelParticipant($stream),
'I
‡Ä' => [
'_' => 'updateBotStopped',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'stopped' => match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) },
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'‚9x' => self::deserialize_updateGroupCallConnection($stream),
'./qM' => [
'_' => 'updateBotCommands',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'bot_id' => unpack('q', stream_get_contents($stream, 8))[1],
'commands' => self::deserialize_type_array_of_BotCommand(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'ÛÃcp' => [
'_' => 'updatePendingJoinRequests',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'requests_pending' => unpack('l', stream_get_contents($stream, 4))[1],
'recent_requesters' => self::deserialize_type_array_of_long(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'†©ß' => [
'_' => 'updateBotChatInviteRequester',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'about' => self::deserialize_string($stream),
'invite' => match (stream_get_contents($stream, 4)) {
'¨´
' => self::deserialize_chatInviteExported($stream),
'·zí' => [
'_' => 'chatInvitePublicJoinRequests',
],
'¡Ïr0' => self::deserialize_type_ExportedChatInvite(self::gzdecode($stream)),
default => self::err($stream)
}
,
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¸<^' => self::deserialize_updateMessageReactions($stream),
'¢·' => [
'_' => 'updateAttachMenuBots',
],
'�·’' => [
'_' => 'updateWebViewResultSent',
'query_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'X¸' => [
'_' => 'updateBotMenuButton',
'bot_id' => unpack('q', stream_get_contents($stream, 8))[1],
'button' => match (stream_get_contents($stream, 4)) {
'ˆ¥3u' => [
'_' => 'botMenuButtonDefault',
],
'ÂXB' => [
'_' => 'botMenuButtonCommands',
],
'æ|µÇ' => [
'_' => 'botMenuButton',
'text' => self::deserialize_string($stream),
'url' => self::deserialize_string($stream),
],
'¡Ïr0' => self::deserialize_type_BotMenuButton(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'™¾Øt' => [
'_' => 'updateSavedRingtones',
],
'ZÍ„' . "\0" . '' => self::deserialize_updateTranscribedAudio($stream),
'lILû' => [
'_' => 'updateReadFeaturedEmojiStickers',
],
'™57(' => [
'_' => 'updateUserEmojiStatus',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
'emoji_status' => match (stream_get_contents($stream, 4)) {
'®á-' => [
'_' => 'emojiStatusEmpty',
],
'�a›’' => [
'_' => 'emojiStatus',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'Ç¨0ú' => [
'_' => 'emojiStatusUntil',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
'until' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_EmojiStatus(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ÛCô0' => [
'_' => 'updateRecentEmojiStatuses',
],
'ôcxo' => [
'_' => 'updateRecentReactions',
],
'…Ïü†' => self::deserialize_updateMoveStickerSetToTop($stream),
'Œ©sZ' => [
'_' => 'updateMessageExtendedMedia',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'extended_media' => match (stream_get_contents($stream, 4)) {
'ÈŒb­' => self::deserialize_messageExtendedMediaPreview($stream),
'dœGî' => [
'_' => 'messageExtendedMedia',
'media' => match (stream_get_contents($stream, 4)) {
' cí=' => [
'_' => 'messageMediaEmpty',
],
'×PQi' => self::deserialize_messageMediaPhoto($stream),
'tÔàV' => [
'_' => 'messageMediaGeo',
'geo' => match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => self::deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'I)2p' => [
'_' => 'messageMediaContact',
'phone_number' => self::deserialize_string($stream),
'first_name' => self::deserialize_string($stream),
'last_name' => self::deserialize_string($stream),
'vcard' => self::deserialize_string($stream),
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'žô„Ÿ' => [
'_' => 'messageMediaUnsupported',
],
'-×ôL' => self::deserialize_messageMediaDocument($stream),
';ñÝ' => self::deserialize_messageMediaWebPage($stream),
'?SÀ.' => [
'_' => 'messageMediaVenue',
'geo' => match (stream_get_contents($stream, 4)) {
'_Ý' => [
'_' => 'geoPointEmpty',
],
'cö¢²' => self::deserialize_geoPoint($stream),
'¡Ïr0' => self::deserialize_type_GeoPoint(self::gzdecode($stream)),
default => self::err($stream)
}
,
'title' => self::deserialize_string($stream),
'address' => self::deserialize_string($stream),
'provider' => self::deserialize_string($stream),
'venue_id' => self::deserialize_string($stream),
'venue_type' => self::deserialize_string($stream),
],
'�±ý' => [
'_' => 'messageMediaGame',
'game' => match (stream_get_contents($stream, 4)) {
';eù½' => self::deserialize_game($stream),
'¡Ïr0' => self::deserialize_type_Game(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ÓH¥ö' => self::deserialize_messageMediaInvoice($stream),
'fÆ@¹' => self::deserialize_messageMediaGeoLive($stream),
'˜çÖK' => [
'_' => 'messageMediaPoll',
'poll' => match (stream_get_contents($stream, 4)) {
'a�á†' => self::deserialize_poll($stream),
'¡Ïr0' => self::deserialize_type_Poll(self::gzdecode($stream)),
default => self::err($stream)
}
,
'results' => match (stream_get_contents($stream, 4)) {
' $ßz' => self::deserialize_pollResults($stream),
'¡Ïr0' => self::deserialize_type_PollResults(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'‹å~?' => [
'_' => 'messageMediaDice',
'value' => unpack('l', stream_get_contents($stream, 4))[1],
'emoticon' => self::deserialize_string($stream),
],
'ƒbËh' => self::deserialize_messageMediaStory($stream),
'°…­Ú' => self::deserialize_messageMediaGiveaway($stream),
'h™Æ' => self::deserialize_messageMediaGiveawayResults($stream),
'¡Ïr0' => self::deserialize_type_MessageMedia(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_MessageExtendedMedia(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ãû.' => self::deserialize_updateChannelPinnedTopic($stream),
'†þ' => self::deserialize_updateChannelPinnedTopics($stream),
'8”R ' => [
'_' => 'updateUser',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'—°ì' => [
'_' => 'updateAutoSaveSettings',
],
'ÖŠðÌ' => [
'_' => 'updateGroupInvitePrivacyForbidden',
'user_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'˜·³u' => [
'_' => 'updateStory',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'story' => match (stream_get_contents($stream, 4)) {
'OîæQ' => [
'_' => 'storyItemDeleted',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'É­ÿ' => self::deserialize_storyItemSkipped($stream),
'$j²y' => self::deserialize_storyItem($stream),
'¡Ïr0' => self::deserialize_type_StoryItem(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'+“N÷' => [
'_' => 'updateReadStories',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'max_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¹5ó' => [
'_' => 'updateStoryID',
'id' => unpack('l', stream_get_contents($stream, 4))[1],
'random_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÁM,' => [
'_' => 'updateStoriesStealthMode',
'stealth_mode' => match (stream_get_contents($stream, 4)) {
'ý\'.q' => self::deserialize_storiesStealthMode($stream),
'¡Ïr0' => self::deserialize_type_StoriesStealthMode(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ƒvb}' => [
'_' => 'updateSentStoryReaction',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'story_id' => unpack('l', stream_get_contents($stream, 4))[1],
'reaction' => match (stream_get_contents($stream, 4)) {
'Ôõy' => [
'_' => 'reactionEmpty',
],
'¸†"' => [
'_' => 'reactionEmoji',
'emoticon' => self::deserialize_string($stream),
],
'sü5‰' => [
'_' => 'reactionCustomEmoji',
'document_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'¡Ïr0' => self::deserialize_type_Reaction(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'œÔM�' => [
'_' => 'updateBotChatBoost',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'boost' => match (stream_get_contents($stream, 4)) {
'qŒ*' => self::deserialize_boost($stream),
'¡Ïr0' => self::deserialize_type_Boost(self::gzdecode($stream)),
default => self::err($stream)
}
,
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
' ‰¶' => [
'_' => 'updateChannelViewForumAsMessages',
'channel_id' => unpack('q', stream_get_contents($stream, 8))[1],
'enabled' => match (stream_get_contents($stream, 4)) {'µur™' => true,'7—y¼' => false, default => self::err($stream) },
],
'?®' => self::deserialize_updatePeerWallpaper($stream),
'ÎÓ!¬' => [
'_' => 'updateBotMessageReaction',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'actor' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'old_reactions' => self::deserialize_type_array_of_Reaction(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'new_reactions' => self::deserialize_type_array_of_Reaction(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'YwË	' => [
'_' => 'updateBotMessageReactions',
'peer' => match (stream_get_contents($stream, 4)) {
'"QY' => self::deserialize_peerUser($stream),
'šÆ6' => -unpack('q', stream_get_contents($stream, 8))[1],
'7¥¢' => self::deserialize_peerChannel($stream),
'¡Ïr0' => self::deserialize_type_Peer(self::gzdecode($stream)),
default => self::err($stream)
}
,
'msg_id' => unpack('l', stream_get_contents($stream, 4))[1],
'date' => unpack('l', stream_get_contents($stream, 4))[1],
'reactions' => self::deserialize_type_array_of_ReactionCount(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'qts' => unpack('l', stream_get_contents($stream, 4))[1],
],
'tž¯®' => self::deserialize_updateSavedDialogPinned($stream),
'¦…lh' => self::deserialize_updatePinnedSavedDialogs($stream),
'2tÆ9' => [
'_' => 'updateSavedReactionTags',
],
'Ôibñ' => [
'_' => 'updateSmsJob',
'job_id' => self::deserialize_string($stream),
],
'²
Gù' => [
'_' => 'updateQuickReplies',
'quick_replies' => self::deserialize_type_array_of_QuickReply(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'§=õ' => [
'_' => 'updateNewQuickReply',
'quick_reply' => match (stream_get_contents($stream, 4)) {
'+—' => [
'_' => 'quickReply',
'shortcut_id' => unpack('l', stream_get_contents($stream, 4))[1],
'shortcut' => self::deserialize_string($stream),
'top_message' => unpack('l', stream_get_contents($stream, 4))[1],
'count' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => self::deserialize_type_QuickReply(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ìñæS' => [
'_' => 'updateDeleteQuickReply',
'shortcut_id' => unpack('l', stream_get_contents($stream, 4))[1],
],
'>' => [
'_' => 'updateQuickReplyMessage',
'message' => match (stream_get_contents($stream, 4)) {
'„Ê¦�' => self::deserialize_messageEmpty($stream),
'ü~l¦' => self::deserialize_message($stream),
'bX+' => self::deserialize_messageService($stream),
'¡Ïr0' => self::deserialize_type_Message(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'ÍçoV' => [
'_' => 'updateDeleteQuickReplyMessages',
'shortcut_id' => unpack('l', stream_get_contents($stream, 4))[1],
'messages' => self::deserialize_type_array_of_int(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'c$' => [
'_' => 'resPQ',
'nonce' => stream_get_contents($stream, 16),
'server_nonce' => stream_get_contents($stream, 16),
'pq' => self::deserialize_string($stream),
'server_public_key_fingerprints' => self::deserialize_type_array_of_strlong(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'Äµ' => [
'_' => 'vector',
],
'•_õ©' => [
'_' => 'p_q_inner_data_dc',
'pq' => self::deserialize_string($stream),
'p' => self::deserialize_string($stream),
'q' => self::deserialize_string($stream),
'nonce' => stream_get_contents($stream, 16),
'server_nonce' => stream_get_contents($stream, 16),
'new_nonce' => stream_get_contents($stream, 32),
'dc' => unpack('l', stream_get_contents($stream, 4))[1],
],
'ˆßýV' => [
'_' => 'p_q_inner_data_temp_dc',
'pq' => self::deserialize_string($stream),
'p' => self::deserialize_string($stream),
'q' => self::deserialize_string($stream),
'nonce' => stream_get_contents($stream, 16),
'server_nonce' => stream_get_contents($stream, 16),
'new_nonce' => stream_get_contents($stream, 32),
'dc' => unpack('l', stream_get_contents($stream, 4))[1],
'expires_in' => unpack('l', stream_get_contents($stream, 4))[1],
],
']Ëy' => [
'_' => 'server_DH_params_fail',
'nonce' => stream_get_contents($stream, 16),
'server_nonce' => stream_get_contents($stream, 16),
'new_nonce_hash' => stream_get_contents($stream, 16),
],
'\\èÐ' => [
'_' => 'server_DH_params_ok',
'nonce' => stream_get_contents($stream, 16),
'server_nonce' => stream_get_contents($stream, 16),
'encrypted_answer' => self::deserialize_string($stream),
],
'º‰µ' => [
'_' => 'server_DH_inner_data',
'nonce' => stream_get_contents($stream, 16),
'server_nonce' => stream_get_contents($stream, 16),
'g' => unpack('l', stream_get_contents($stream, 4))[1],
'dh_prime' => self::deserialize_string($stream),
'g_a' => self::deserialize_string($stream),
'server_time' => unpack('l', stream_get_contents($stream, 4))[1],
],
'T¶Cf' => [
'_' => 'client_DH_inner_data',
'nonce' => stream_get_contents($stream, 16),
'server_nonce' => stream_get_contents($stream, 16),
'retry_id' => unpack('q', stream_get_contents($stream, 8))[1],
'g_b' => self::deserialize_string($stream),
],
'4÷Ë;' => [
'_' => 'dh_gen_ok',
'nonce' => stream_get_contents($stream, 16),
'server_nonce' => stream_get_contents($stream, 16),
'new_nonce_hash1' => stream_get_contents($stream, 16),
],
'¹ÜF' => [
'_' => 'dh_gen_retry',
'nonce' => stream_get_contents($stream, 16),
'server_nonce' => stream_get_contents($stream, 16),
'new_nonce_hash2' => stream_get_contents($stream, 16),
],
'®�¦' => [
'_' => 'dh_gen_fail',
'nonce' => stream_get_contents($stream, 16),
'server_nonce' => stream_get_contents($stream, 16),
'new_nonce_hash3' => stream_get_contents($stream, 16),
],
'e÷£u' => [
'_' => 'bind_auth_key_inner',
'nonce' => unpack('q', stream_get_contents($stream, 8))[1],
'temp_auth_key_id' => unpack('q', stream_get_contents($stream, 8))[1],
'perm_auth_key_id' => unpack('q', stream_get_contents($stream, 8))[1],
'temp_session_id' => unpack('q', stream_get_contents($stream, 8))[1],
'expires_at' => unpack('l', stream_get_contents($stream, 4))[1],
],
'm\\ó' => self::deserialize_rpc_result($stream),
'nÓ*^' => [
'_' => 'rpc_answer_unknown',
],
'†åxÍ' => [
'_' => 'rpc_answer_dropped_running',
],
'·Ø:¤' => [
'_' => 'rpc_answer_dropped',
'msg_id' => unpack('q', stream_get_contents($stream, 8))[1],
'seq_no' => unpack('l', stream_get_contents($stream, 4))[1],
'bytes' => unpack('l', stream_get_contents($stream, 4))[1],
],
'ÜÙI	' => [
'_' => 'future_salt',
'valid_since' => unpack('l', stream_get_contents($stream, 4))[1],
'valid_until' => unpack('l', stream_get_contents($stream, 4))[1],
'salt' => unpack('q', stream_get_contents($stream, 8))[1],
],
'•P®' => [
'_' => 'future_salts',
'req_msg_id' => unpack('q', stream_get_contents($stream, 8))[1],
'now' => unpack('l', stream_get_contents($stream, 4))[1],
'salts' => self::deserialize_type_array_of_future_salt($stream),
],
'Åsw4' => [
'_' => 'pong',
'msg_id' => unpack('q', stream_get_contents($stream, 8))[1],
'ping_id' => stream_get_contents($stream, 8),
],
'üE â' => [
'_' => 'destroy_session_ok',
'session_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'ÉPÓb' => [
'_' => 'destroy_session_none',
'session_id' => unpack('q', stream_get_contents($stream, 8))[1],
],
'	Âž' => [
'_' => 'new_session_created',
'first_msg_id' => unpack('q', stream_get_contents($stream, 8))[1],
'unique_id' => unpack('q', stream_get_contents($stream, 8))[1],
'server_salt' => stream_get_contents($stream, 8),
],
'Üøñs' => [
'_' => 'msg_container',
'messages' => self::deserialize_type_array_of_MTmessage($stream),
],
'²F`à' => [
'_' => 'msg_copy',
'orig_message' => match (stream_get_contents($stream, 4)) {
'å¸[' => [
'_' => 'MTmessage',
'msg_id' => unpack('q', stream_get_contents($stream, 8))[1],
'seqno' => unpack('l', stream_get_contents($stream, 4))[1],
'bytes' => unpack('l', stream_get_contents($stream, 4))[1],
'body' => match (stream_get_contents($stream, 4)) {
'¡Ïr0' => $this->deserialize_type_Object(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'¡Ïr0' => self::deserialize_type_MTMessage(self::gzdecode($stream)),
default => self::err($stream)
}
,
],
'Y´Öb' => [
'_' => 'msgs_ack',
'msg_ids' => self::deserialize_type_array_of_long(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'øï§' => [
'_' => 'bad_msg_notification',
'bad_msg_id' => unpack('q', stream_get_contents($stream, 8))[1],
'bad_msg_seqno' => unpack('l', stream_get_contents($stream, 4))[1],
'error_code' => unpack('l', stream_get_contents($stream, 4))[1],
],
'{D«í' => [
'_' => 'bad_server_salt',
'bad_msg_id' => unpack('q', stream_get_contents($stream, 8))[1],
'bad_msg_seqno' => unpack('l', stream_get_contents($stream, 4))[1],
'error_code' => unpack('l', stream_get_contents($stream, 4))[1],
'new_server_salt' => stream_get_contents($stream, 8),
],
'ëº†' => [
'_' => 'msg_resend_ans_req',
'msg_ids' => self::deserialize_type_array_of_long(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'†}' => [
'_' => 'msg_resend_req',
'msg_ids' => self::deserialize_type_array_of_long(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'RûiÚ' => [
'_' => 'msgs_state_req',
'msg_ids' => self::deserialize_type_array_of_long(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
],
'}µÞ' => [
'_' => 'msgs_state_info',
'req_msg_id' => unpack('q', stream_get_contents($stream, 8))[1],
'info' => self::deserialize_string($stream),
],
'1ÑÀŒ' => [
'_' => 'msgs_all_info',
'msg_ids' => self::deserialize_type_array_of_long(match(stream_get_contents($stream, 4)) {    
                'Äµ' => $stream,    
                '¡Ïr0' => self::gzdecode_vector($stream)
            }),
'info' => self::deserialize_string($stream),
],
'Æ>m\'' => [
'_' => 'msg_detailed_info',
'msg_id' => unpack('q', stream_get_contents($stream, 8))[1],
'answer_msg_id' => unpack('q', stream_get_contents($stream, 8))[1],
'bytes' => unpack('l', stream_get_contents($stream, 4))[1],
'status' => unpack('l', stream_get_contents($stream, 4))[1],
],
'ß¶�€' => [
'_' => 'msg_new_detailed_info',
'answer_msg_id' => unpack('q', stream_get_contents($stream, 8))[1],
'bytes' => unpack('l', stream_get_contents($stream, 4))[1],
'status' => unpack('l', stream_get_contents($stream, 4))[1],
],
'vËz' => [
'_' => 'rsa_public_key',
'n' => self::deserialize_string($stream),
'e' => self::deserialize_string($stream),
],
'Ÿ5™’' => [
'_' => 'http_wait',
'max_delay' => unpack('l', stream_get_contents($stream, 4))[1],
'wait_after' => unpack('l', stream_get_contents($stream, 4))[1],
'max_wait' => unpack('l', stream_get_contents($stream, 4))[1],
],
'¡Ïr0' => $this->deserialize_type_Object(self::gzdecode($stream)),
default => self::err($stream)
}
;
    }
}
<?php

declare(strict_types=1);

/**
 * TLConstructors module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\TL;

/**
 * @internal
 */
final class TLConstructors
{
    use TLParams;
    public array $by_id = [];
    public array $by_predicate_and_layer = [];
    public array $layers = [];
    /**
     * @psalm-pure
     */
    public function __sleep()
    {
        return ['by_predicate_and_layer', 'by_id', 'layers'];
    }
    public function add(array $json_dict, string $scheme_type): void
    {
        if (isset($this->by_id[$json_dict['id']]) && (!isset($this->by_id[$json_dict['id']]['layer']) || $this->by_id[$json_dict['id']]['layer'] > $json_dict['layer'])) {
            return;
        }
        $predicate = ($scheme_type === 'mtproto' && $json_dict['predicate'] === 'message' ? 'MT' : '').$json_dict['predicate'];
        $this->by_id[$json_dict['id']] = [
            'predicate' => $predicate,
            'params' => $json_dict['params'],
            'flags' => [],
            'type' => ($scheme_type === 'mtproto' && $json_dict['type'] === 'Message' ? 'MT' : '').$json_dict['type'],
            'encrypted' => $scheme_type !== 'mtproto',
        ];
        if ($scheme_type === 'secret') {
            $this->by_id[$json_dict['id']]['layer'] = $json_dict['layer'];
            $this->layers[$json_dict['layer']] = $json_dict['layer'];
            ksort($this->layers);
        } else {
            $json_dict['layer'] = '';
        }
        $this->by_predicate_and_layer[$predicate.$json_dict['layer']] = $json_dict['id'];
        $this->parseParams($json_dict['id'], $scheme_type === 'mtproto', $json_dict['predicate']);
    }
    /**
     * @psalm-mutation-free
     */
    public function findByType(string $type)
    {
        foreach ($this->by_id as $id => $constructor) {
            if ($constructor['type'] === $type) {
                $constructor['id'] = $id;
                return $constructor;
            }
        }
        return false;
    }
    /**
     * @psalm-mutation-free
     */
    public function findByPredicate(string $predicate, int $layer = -1)
    {
        if ($layer !== -1) {
            $chosenid = null;
            foreach ($this->layers as $alayer) {
                if ($alayer <= $layer) {
                    if (isset($this->by_predicate_and_layer[$predicate.$alayer])) {
                        $chosenid = $this->by_predicate_and_layer[$predicate.$alayer];
                    }
                } elseif (!isset($chosenid)) {
                    if (isset($this->by_predicate_and_layer[$predicate.$alayer])) {
                        $chosenid = $this->by_predicate_and_layer[$predicate.$alayer];
                    }
                }
            }
            if (!isset($chosenid)) {
                return $this->findByPredicate($predicate);
            }
            $constructor = $this->by_id[$chosenid];
            $constructor['id'] = $chosenid;
            return $constructor;
        }
        if (isset($this->by_predicate_and_layer[$predicate])) {
            $constructor = $this->by_id[$this->by_predicate_and_layer[$predicate]];
            $constructor['id'] = $this->by_predicate_and_layer[$predicate];
            return $constructor;
        }
        return false;
    }
    /**
     * Find constructor by ID.
     *
     * @param string $id Constructor ID
     *
     * @psalm-mutation-free
     */
    public function findById(string $id): array|false
    {
        if (isset($this->by_id[$id])) {
            $constructor = $this->by_id[$id];
            $constructor['id'] = $id;
            return $constructor;
        }
        return false;
    }
}
<?php

declare(strict_types=1);

/**
 * Button module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\TL\Types;

use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\Ipc\IpcCapable;
use danog\MadelineProto\MTProto;
use JsonSerializable;

/**
 * Clickable button.
 */
final class Button extends IpcCapable implements JsonSerializable
{
    /** Button label */
    public readonly string $label;
    /**
     * Constructor function.
     *
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(
        MTProto $API,
        protected readonly Message $message,
        protected readonly array $button
    ) {
        parent::__construct($API);
        $this->label = $button['text'];
    }
    /**
     * Click on button.
     *
     * @param boolean $donotwait Whether to wait for the result of the method
     */
    public function click(bool $donotwait = true)
    {
        // Since layer 229, what a button does is its type (a ButtonType or an InlineButtonType).
        $type = $this->button['type'] ?? [];
        return match ($this->button['_'].'/'.($type['_'] ?? '')) {
            default => false,
            'keyboardButton/buttonTypeDefault' => $this->message->reply($this->label),
            'keyboardInlineButton/inlineButtonTypeUrl' => $type['url'],
            'keyboardInlineButton/inlineButtonTypeCallback' => $this->getClient()->clickInternal($donotwait, 'messages.getBotCallbackAnswer', ['peer' => $this->message->chatId, 'msg_id' => $this->message->id, 'data' => $type['data']]),
            'keyboardInlineButton/inlineButtonTypeGame' => $this->getClient()->clickInternal($donotwait, 'messages.getBotCallbackAnswer', ['peer' => $this->message->chatId, 'msg_id' => $this->message->id, 'game' => true]),
        };
    }
    /**
     * Serialize button.
     */
    #[\Override]
    public function jsonSerialize(): array
    {
        return $this->button;
    }
}
<?php

declare(strict_types=1);

/**
 * Bytes module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\TL\Types;

use ArrayAccess;
use AssertionError;
use JsonSerializable;

/**
 * Bytes wrapper.
 *
 * Cast this object to a string ((string) $bytes) to obtain the inner bytes.
 *
 * @implements ArrayAccess<int, string>
 */
final class Bytes implements JsonSerializable, ArrayAccess
{
    /**
     * Constructor function.
     *
     * @param string $bytes Contents
     *
     * @psalm-mutation-free
     */
    public function __construct(private readonly string $bytes)
    {
    }
    /**
     * Sleep function.
     *
     * @psalm-pure
     */
    public function __sleep(): array
    {
        return ['bytes'];
    }
    /**
     * Cast bytes to string.
     */
    public function __toString(): string
    {
        return $this->bytes;
    }
    /**
     * Obtain values for JSON-encoding.
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function jsonSerialize(): array
    {
        return ['_' => 'bytes', 'bytes' => base64_encode($this->bytes)];
    }
    /**
     * Set char at offset.
     *
     * @param integer|null $offset Offset
     * @param string       $value  Char
     *
     * @psalm-pure
     */
    #[\Override]
    public function offsetSet(mixed $offset, mixed $value): void
    {
        throw new AssertionError("Cannot modify nested bytes!");
    }
    /**
     * Get char at offset.
     *
     * @param integer $offset Name
     *
     * @return string
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function offsetGet(mixed $offset): mixed
    {
        return $this->bytes[$offset];
    }
    /**
     * Unset char at offset.
     *
     * @param integer $offset Offset
     *
     * @psalm-pure
     */
    #[\Override]
    public function offsetUnset(mixed $offset): void
    {
        throw new AssertionError("Cannot modify nested bytes!");
    }
    /**
     * Check if char at offset exists.
     *
     * @param integer $offset Offset
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function offsetExists(mixed $offset): bool
    {
        return isset($this->bytes[$offset]);
    }
}
<?php

declare(strict_types=1);

/**
 * Login QR code.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\TL\Types;

use Amp\Cancellation;
use Amp\CancelledException;
use Amp\CompositeCancellation;
use Amp\DeferredFuture;
use AssertionError;
use BaconQrCode\Renderer\Image\SvgImageBackEnd;
use BaconQrCode\Renderer\ImageRenderer;
use BaconQrCode\Renderer\PlainTextRenderer;
use BaconQrCode\Renderer\RendererStyle\RendererStyle;
use BaconQrCode\Writer;
use danog\MadelineProto\Ipc\IpcCapable;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\Tools;
use JsonSerializable;

/**
 * Represents a login QR code.
 */
final class LoginQrCode extends IpcCapable implements JsonSerializable
{
    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(
        MTProto $API,
        /** @var non-empty-string The [QR code login link](https://core.telegram.org/api/links#qr-code-login-links) */
        public readonly string $link,
        /** @var positive-int The expiry date of the link */
        public readonly int $expiry
    ) {
        parent::__construct($API);
    }

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function jsonSerialize(): mixed
    {
        return [
            'link' => $this->link,
            'expiry' => $this->expiry,
        ];
    }

    /**
     * Returns true if the QR code has expired and a new one should be fetched.
     */
    public function isExpired(): bool
    {
        return $this->expiry <= time();
    }

    /**
     * Returns the number of seconds until the QR code expires.
     *
     * @return non-negative-int
     */
    public function expiresIn(): int
    {
        return max(0, $this->expiry - time());
    }

    public function getExpirationCancellation(): Cancellation
    {
        return Tools::getTimeoutCancellation((float) $this->expiresIn(), "The QR code expired!");
    }

    public function getLoginCancellation(): Cancellation
    {
        return $this->getClient()->getQrLoginCancellation();
    }

    /**
     * Waits for the user to login or for the QR code to expire.
     *
     * If the user logins, null is returned.
     *
     * If the QR code expires, the new QR code is returned.
     *
     * If cancellation is requested externally through $cancellation, a CancelledException is thrown.
     *
     * @throws CancelledException
     *
     * @param Cancellation|null $customCancellation Optional additional cancellation
     */
    public function waitForLoginOrQrCodeExpiration(?Cancellation $customCancellation = null): ?self
    {
        $expire = $this->getExpirationCancellation();
        if ($customCancellation) {
            $cancellation = new CompositeCancellation($expire, $customCancellation);
        } else {
            $cancellation = $expire;
        }
        $login = $this->getLoginCancellation();
        $cancellation = new CompositeCancellation($login, $cancellation);
        try {
            (new DeferredFuture)->getFuture()->await($cancellation);
        } catch (CancelledException) {
            $customCancellation?->throwIfRequested();
            return $this->getClient()->qrLogin();
        }
        throw new AssertionError("Unreachable!");
    }

    /**
     * Render and return SVG version of QR code.
     */
    public function getQRSvg(int $size = 400, int $margin = 4): string
    {
        $writer = new Writer(new ImageRenderer(
            new RendererStyle($size, $margin),
            new SvgImageBackEnd
        ));
        return $writer->writeString($this->link);
    }

    /**
     * Render and return plain text version of QR code.
     *
     * @param non-negative-int $margin Text margin
     */
    public function getQRText(int $margin = 2): string
    {
        $writer = new Writer(new PlainTextRenderer($margin));
        return $writer->writeString($this->link);
    }
}
<?php

declare(strict_types=1);

/**
 * Exception module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\TL;

use danog\MadelineProto\Magic;

use const PHP_EOL;
use const PHP_SAPI;

/**
 * TL deserialization exception.
 */
final class Exception extends \Exception
{
    use PrettyException;
    /**
     * @psalm-external-mutation-free
     */
    public function __toString(): string
    {
        $result = static::class.($this->message !== '' ? ': ' : '').$this->message.PHP_EOL.Magic::$revision.PHP_EOL.'TL Trace:'.PHP_EOL.PHP_EOL.$this->getTLTrace().PHP_EOL;
        if (PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg') {
            $result = str_replace(PHP_EOL, '<br>'.PHP_EOL, $result);
        }
        return $result;
    }
    public function __construct($message, $file = '')
    {
        parent::__construct($message);
        $this->prettifyTL($file);
    }
}
<?php

declare(strict_types=1);

/**
 * TLParams module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\TL;

/**
 * @internal
 */
trait TLParams
{
    public function parseParams(string $key, bool $mtproto, string $predicate): void
    {
        foreach ($this->by_id[$key]['params'] as $kkey => $param) {
            if (preg_match('/([^.]+)\\.(\\d+)\\?(.+)/', $param['type'], $matches)) {
                $param['flag'] = $matches[1];
                $param['pow'] = 2** (int) $matches[2];
                $param['type'] = $matches[3];
            }
            if (preg_match('/^(v|V)ector\\<(.*)\\>$/', $param['type'], $matches)) {
                $param['type'] = $matches[1] === 'v' ? 'vector' : 'Vector t';
                $param['subtype'] = $matches[2];
                $param['subtype'] = ($mtproto && $param['subtype'] === 'Message' ? 'MT' : '').$param['subtype'];
                $param['subtype'] = $mtproto && $param['subtype'] === '%Message' ? '%MTMessage' : $param['subtype'];
            }
            $param['type'] = ($mtproto && $param['type'] === 'Message' ? 'MT' : '').$param['type'];
            $param['type'] = $mtproto && $param['type'] === '%Message' ? '%MTMessage' : $param['type'];

            if (\in_array($param['name'], ['key_fingerprint', 'server_salt', 'new_server_salt', 'ping_id'], true) && $param['type'] === 'long') {
                $param['type'] = 'strlong';
            } elseif (\in_array($param['name'], ['peer_tag', 'file_token', 'cdn_key', 'cdn_iv', 'encryption_key', 'encryption_iv'], true)) {
                $param['type'] = 'string';
            } elseif ($param['name'] === 'server_public_key_fingerprints') {
                $param['subtype'] = 'strlong';
            }

            if ($predicate === 'dcOption' && $param['name'] === 'secret') {
                $param['type'] = 'string';
            }

            if ($predicate === 'documentAttributeAudio' && $param['name'] === 'waveform') {
                $param['type'] = 'waveform';
            }

            $this->by_id[$key]['params'][$kkey] = $param;
            if (isset($param['pow'])) {
                $this->by_id[$key]['flags'][$kkey] = $param;
            }
        }
    }
}
<?php

declare(strict_types=1);

/**
 * PrettyException module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\TL;

use const PHP_EOL;
use const PHP_SAPI;

/**
 * Handle async stack traces.
 */
trait PrettyException
{
    /**
     * TL trace.
     *
     */
    public string $tlTrace = '';
    /**
     * Method name.
     *
     */
    private string $method = '';
    /**
     * Whether the TL trace was updated.
     *
     */
    private bool $updated = false;
    /**
     * Get TL trace.
     */
    public function getTLTrace(): string
    {
        return $this->tlTrace;
    }
    /**
     * Generate async trace.
     *
     * @internal
     *
     * @param string $init  Method name
     * @param array  $trace Async trace
     */
    public function prettifyTL(string $init = '', ?array $trace = null): void
    {
        $this->method = $init;
        $previous_trace = $this->tlTrace;
        $this->tlTrace = '';
        $eol = PHP_EOL;
        if (PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg') {
            $eol = '<br>'.PHP_EOL;
        }
        $tl = false;
        foreach (array_reverse($trace ?? $this->getTrace()) as $k => $frame) {
            if (isset($frame['function']) && \in_array($frame['function'], ['serializeParams', 'serializeObject'], true)) {
                if (($frame['args'][2] ?? '') !== '') {
                    $this->tlTrace .= $tl ? "['".$frame['args'][2]."']" : "While serializing:  \t".$frame['args'][2];
                    $tl = true;
                }
            } else {
                if ($tl) {
                    $this->tlTrace .= $eol;
                }
                if (isset($frame['function']) && ($frame['function'] === 'handle_rpc_error' && $k === \count($this->getTrace()) - 1) || $frame['function'] === 'unserialize') {
                    continue;
                }
                $this->tlTrace .= isset($frame['file']) ? str_pad(basename($frame['file']).'('.$frame['line'].'):', 20)."\t" : '';
                $this->tlTrace .= isset($frame['function']) ? $frame['function'].'(' : '';
                $this->tlTrace .= isset($frame['args']) ? substr(json_encode($frame['args']) ?: '', 1, -1) : '';
                $this->tlTrace .= ')';
                $this->tlTrace .= $eol;
                $tl = false;
            }
        }
        $this->tlTrace .= $init !== '' ? "['".$init."']" : '';
        $this->tlTrace = implode($eol, array_reverse(explode($eol, $this->tlTrace)));
        if ($previous_trace) {
            $this->tlTrace .= $eol.$eol;
            $this->tlTrace .= "Previous TL trace:{$eol}";
            $this->tlTrace .= $previous_trace;
        }
    }
}
<?php

declare(strict_types=1);

/**
 * TL callback module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\TL;

use danog\MadelineProto\Connection;
use danog\MadelineProto\MTProto\MTProtoOutgoingMessage;

/**
 * @psalm-type TBeforeMethodResponseDeserialization=Closure(string): void
 * @psalm-type TAfterMethodResponseDeserialization=Closure(MTProtoOutgoingMessage, array): void
 *
 * @psalm-type TBeforeConstructorSerialization=Closure(array): mixed
 * @psalm-type TBeforeConstructorDeserialization=Closure(string): void
 * @psalm-type TAfterConstructorDeserialization=Closure(array, Connection): void
 * @psalm-type TTypeMismatch=Closure(array): mixed
 *
 * @internal Interface for managing TL serialization callbacks.
 * @psalm-mutable
 */
interface TLCallback
{
    /**
     * Called right before deserialization of the result of a method starts.
     *
     * Pass only the method name, will return void
     *
     * @return array<string, list<TBeforeMethodResponseDeserialization>>
     * @psalm-impure
     */
    public function getMethodBeforeResponseDeserializationCallbacks(): array;
    /**
     * Called after deserialization of the result of a method.
     *
     * Pass the method name and response, will return void
     *
     * @return array<string, list<TAfterMethodResponseDeserialization>>
     * @psalm-impure
     */
    public function getMethodAfterResponseDeserializationCallbacks(): array;
    /**
     * Called right before serialization of constructor.
     *
     * Passed the constructor, will return a modified version.
     *
     * @return array<string, TBeforeConstructorSerialization>
     * @psalm-impure
     */
    public function getConstructorBeforeSerializationCallbacks(): array;
    /**
     * Called right before deserialization of constructor.
     *
     * Pass only the constructor name, will return void
     *
     * @return array<string, list<TBeforeConstructorDeserialization>>
     * @psalm-impure
     */
    public function getConstructorBeforeDeserializationCallbacks(): array;
    /**
     * Called right after deserialization of constructor.
     *
     * Pass the deserialized constructor, will return void
     *
     * @return array<string, list<TAfterConstructorDeserialization>>
     * @psalm-impure
     */
    public function getConstructorAfterDeserializationCallbacks(): array;
    /**
     * Called if constructors of the specified type cannot be serialized.
     *
     * Passed the unserializable constructor,
     * will try to convert it to an constructor of the proper type.
     *
     * @return array<string, TTypeMismatch>
     * @psalm-impure
     */
    public function getTypeMismatchCallbacks(): array;
}
<?php

declare(strict_types=1);

/**
 * Internal loop trait.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Loop;

use danog\MadelineProto\API;
use danog\MadelineProto\MTProto;

/**
 * @internal
 */
trait InternalLoop
{
    use LoggerLoop;

    /**
     * API instance.
     */
    protected MTProto $API;
    /**
     * Constructor.
     *
     * @param MTProto $API API instance
     */
    public function __construct(MTProto $API)
    {
        $this->API = $API;
    }
}
<?php

declare(strict_types=1);

/**
 * Update feeder loop.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Loop\Generic;

use Closure;
use danog\Loop\PeriodicLoop;
use danog\MadelineProto\Loop\InternalLoop;
use danog\MadelineProto\MTProto;

/**
 * {@inheritDoc}
 *
 * @internal For internal use
 */
final class PeriodicLoopInternal extends PeriodicLoop
{
    use InternalLoop {
        __construct as private init;
    }
    /**
     * Constructor.
     *
     * @param MTProto  $API      API instance
     * @param Closure $callable Method
     * @param string   $name     Loop name
     * @param int|null $interval Interval
     */
    public function __construct(MTProto $API, Closure $callable, string $name, ?int $interval)
    {
        $this->init($API);
        parent::__construct(static fn () => $callable(), $name, $interval);
    }
}
<?php

declare(strict_types=1);

/**
 * Loop logging trait.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Loop;

use danog\Loop\Loop;
use danog\MadelineProto\Logger;
use danog\MadelineProto\Tgcalls\CallControllerInterface;

/**
 * @internal
 */
abstract class VoIPLoop extends Loop
{
    /**
     * @psalm-mutation-free
     */
    public function __construct(
        protected CallControllerInterface $instance,
    ) {
    }
    /**
     * Signal that loop has started.
     */
    #[\Override]
    protected function startedLoop(): void
    {
        $this->instance->log("Entered $this");
        parent::startedLoop();
    }

    /**
     * Signal that loop has exited.
     */
    #[\Override]
    protected function exitedLoop(): void
    {
        $this->instance->log("Exited $this");
        parent::exitedLoop();
    }

    /**
     * Report pause, can be overriden for logging.
     *
     * @param float $timeout Pause duration, 0 = forever
     */
    #[\Override]
    protected function reportPause(float $timeout): void
    {
        if ($timeout) {
            $this->instance->log(
                "Pausing $this for $timeout",
                Logger::ULTRA_VERBOSE,
            );
        } else {
            $this->instance->log(
                "Pausing $this until resume is called",
                Logger::ULTRA_VERBOSE,
            );
        }
    }

    /**
     * Get loop name.
     * @psalm-mutation-free
     */
    abstract public function __toString(): string;
}
<?php

declare(strict_types=1);

/**
 * Loop logging trait.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Loop;

use danog\MadelineProto\Logger;

/**
 * @internal
 */
trait LoggerLoop
{
    private bool $logPauses = true;

    /**
     * Report pause, can be overriden for logging.
     *
     * @param float $timeout Pause duration, 0 = forever
     *
     * @psalm-external-mutation-free
     */
    protected function reportPause(float $timeout): void
    {
        $timeout = $timeout ? "for $timeout" : "until resume";
        $this->API->logger("Pausing $this $timeout...", Logger::ULTRA_VERBOSE);
    }

    /**
     * Signal that loop was started.
     *
     * @psalm-external-mutation-free
     */
    protected function startedLoop(): void
    {
        $this->API->logger("Started $this!", Logger::ULTRA_VERBOSE);
    }
    /**
     * Signal that loop has exited.
     *
     * @psalm-external-mutation-free
     */
    protected function exitedLoop(): void
    {
        $this->API->logger("Exited $this!", Logger::ULTRA_VERBOSE);
    }
    /**
     * Get loop name.
     */
    abstract public function __toString(): string;
}
<?php

declare(strict_types=1);

/**
 * Update loop.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Loop\Update;

use Amp\CancelledException;
use Amp\TimeoutException;
use danog\Loop\Loop;
use danog\MadelineProto\API;
use danog\MadelineProto\Exception;
use danog\MadelineProto\Logger;
use danog\MadelineProto\Loop\InternalLoop;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\MTProto\LoginState;
use danog\MadelineProto\MTProto\SpecialMethodType;
use danog\MadelineProto\PeerNotInDbException;
use danog\MadelineProto\PTSException;
use danog\MadelineProto\Reactive\SimpleSubscriber;
use danog\MadelineProto\RPCError\ChannelInvalidError;
use danog\MadelineProto\RPCError\ChannelPrivateError;
use danog\MadelineProto\RPCError\ChatForbiddenError;
use danog\MadelineProto\RPCError\TimeoutError;
use danog\MadelineProto\RPCError\UserBannedInChannelError;

use Revolt\EventLoop;
use function Amp\delay;

/**
 * Update loop.
 *
 * @internal
 *
 * @author Daniil Gentili <daniil@daniil.it>
 *
 * @implements SimpleSubscriber<LoginState>
 */
final class UpdateLoop extends Loop implements SimpleSubscriber
{
    use InternalLoop {
        __construct as private init;
    }
    /**
     * Main loop ID.
     */
    public const GENERIC = 0;

    private const DEFAULT_TIMEOUT = 10.0;

    /**
     * Feed loop.
     */
    private ?FeedLoop $feeder = null;
    private ?int $authorizedDc = null;
    /**
     * Constructor.
     */
    public function __construct(MTProto $API, private int $channelId)
    {
        $this->init($API);
        $API->loginState->subscribe($this);
    }
    /**
     * @psalm-pure
     */
    public function __sleep(): array
    {
        return ['channelId', 'API', 'feeder', 'authorizedDc'];
    }
    #[\Override]
    public function onSimpleStateChange($state): void
    {
        if ($state->state !== API::LOGGED_IN) {
            $this->authorizedDc = null;
            return;
        }
        if (null !== $this->authorizedDc = $state->authorizedDc) {
            if ($this->isRunning()) {
                $this->resume(true);
            } else {
                $this->start();
            }
        }
    }

    /**
     * Main loop.
     */
    #[\Override]
    public function loop(): ?float
    {
        if ($this->authorizedDc === null) {
            return self::PAUSE;
        }
        $this->API->feeders[$this->channelId] ??= new FeedLoop($this->API, $this->channelId);
        $this->API->updaters[$this->channelId] ??= new UpdateLoop($this->API, $this->channelId);

        $this->feeder = $this->API->feeders[$this->channelId];
        $state = $this->channelId === self::GENERIC ? $this->API->loadUpdateState() : $this->API->loadChannelState($this->channelId);

        $result = [];
        while (true) {
            if ($this->channelId) {
                $this->API->logger('Resumed and fetching '.$this->channelId.' difference...', Logger::ULTRA_VERBOSE);
                if ($state->pts() <= 1) {
                    $limit = 10;
                } elseif ($this->API->authorization['user']['bot']) {
                    $limit = 100000;
                } else {
                    $limit = 100;
                }
                $request_pts = $state->pts();
                try {
                    $difference = $this->API->methodCallAsyncRead('updates.getChannelDifference', ['channel' => $this->channelId, 'filter' => ['_' => 'channelMessagesFilterEmpty'], 'pts' => $request_pts, 'limit' => $limit, 'force' => true, 'floodWaitLimit' => 86400]);
                } catch (ChannelPrivateError|ChatForbiddenError|ChannelInvalidError|UserBannedInChannelError) {
                    $this->feeder->stop();
                    unset($this->API->updaters[$this->channelId], $this->API->feeders[$this->channelId]);
                    $this->API->getChannelStates()->remove($this->channelId);
                    $this->API->logger("Channel private, exiting {$this}");
                    return self::STOP;
                } catch (TimeoutError $e) {
                    delay(1.0);
                    continue;
                } catch (PeerNotInDbException) {
                    $this->feeder->stop();
                    $this->API->getChannelStates()->remove($this->channelId);
                    unset($this->API->updaters[$this->channelId], $this->API->feeders[$this->channelId]);
                    $this->API->logger("Channel private, exiting {$this}");
                    return self::STOP;
                } catch (PTSException $e) {
                    $this->feeder->stop();
                    $this->API->getChannelStates()->remove($this->channelId);
                    unset($this->API->updaters[$this->channelId], $this->API->feeders[$this->channelId]);
                    $this->API->logger("Got PTS exception, exiting update loop for $this: $e", Logger::FATAL_ERROR);
                    return self::STOP;
                } catch (CancelledException $e) {
                    if (!$e->getPrevious() instanceof TimeoutException) {
                        throw $e;
                    }
                    EventLoop::queue($this->API->report(...), "Network issues detected, please check logs!");
                    delay(1.0);
                    continue;
                }
                $timeout = min(self::DEFAULT_TIMEOUT, $difference['timeout'] ?? self::DEFAULT_TIMEOUT);
                $this->API->logger('Got '.$difference['_'], Logger::ULTRA_VERBOSE);
                switch ($difference['_']) {
                    case 'updates.channelDifferenceEmpty':
                        $state->update($difference);
                        unset($difference);
                        break 2;
                    case 'updates.channelDifference':
                        if ($request_pts >= $difference['pts'] && $request_pts > 1) {
                            $this->API->logger("The PTS ({$difference['pts']}) I got with getDifference is smaller than the PTS I requested ".$state->pts().', using '.($state->pts() + 1), Logger::VERBOSE);
                            $difference['pts'] = $request_pts + 1;
                        }
                        $result += ($this->feeder->feed($difference['other_updates']));
                        $state->update($difference);
                        if ($difference['new_messages']) {
                            $result[$this->channelId] = true;
                        }
                        $this->feeder->saveMessages($difference['new_messages']);
                        if (!$difference['final']) {
                            unset($difference);
                            break;
                        }
                        unset($difference);
                        break 2;
                    case 'updates.channelDifferenceTooLong':
                        if (isset($difference['dialog']['pts'])) {
                            $difference['pts'] = $difference['dialog']['pts'];
                        }
                        $state->update($difference);
                        if ($difference['messages']) {
                            $result[$this->channelId] = true;
                        }
                        $this->feeder->saveMessages($difference['messages']);
                        unset($difference);
                        break;
                    default:
                        throw new Exception('Unrecognized update difference received: '.var_export($difference, true));
                }
            } else {
                $this->API->logger('Resumed and fetching normal difference...', Logger::ULTRA_VERBOSE);
                do {
                    try {
                        $difference = $this->API->methodCallAsyncRead('updates.getDifference', ['pts' => $state->pts(), 'date' => $state->date(), 'qts' => $state->qts(), 'specialMethodType' => SpecialMethodType::USER_RELATED], $this->authorizedDc);
                        break;
                    } catch (TimeoutError) {
                        delay(1.0);
                    } catch (CancelledException $e) {
                        if (!$e->getPrevious() instanceof TimeoutException) {
                            throw $e;
                        }
                        EventLoop::queue($this->API->report(...), "Network issues detected, please check logs!");
                    }
                } while (true);
                $this->API->logger('Got '.$difference['_'], Logger::ULTRA_VERBOSE);
                $timeout = self::DEFAULT_TIMEOUT;
                switch ($difference['_']) {
                    case 'updates.differenceEmpty':
                        $state->update($difference);
                        unset($difference);
                        break 2;
                    case 'updates.difference':
                        $state->qts($difference['state']['qts']);
                        foreach ($difference['new_encrypted_messages'] as &$encrypted) {
                            $encrypted = ['_' => 'updateNewEncryptedMessage', 'message' => $encrypted];
                        }
                        $result += ($this->feeder->feed($difference['other_updates']));
                        $result += ($this->feeder->feed($difference['new_encrypted_messages']));
                        $state->update($difference['state']);
                        if ($difference['new_messages']) {
                            $result[$this->channelId] = true;
                        }
                        $this->feeder->saveMessages($difference['new_messages']);
                        unset($difference);
                        break 2;
                    case 'updates.differenceSlice':
                        $state->qts($difference['intermediate_state']['qts']);
                        foreach ($difference['new_encrypted_messages'] as &$encrypted) {
                            $encrypted = ['_' => 'updateNewEncryptedMessage', 'message' => $encrypted];
                        }
                        $result += ($this->feeder->feed($difference['other_updates']));
                        $result += ($this->feeder->feed($difference['new_encrypted_messages']));
                        $state->update($difference['intermediate_state']);
                        if ($difference['new_messages']) {
                            $result[$this->channelId] = true;
                        }
                        $this->feeder->saveMessages($difference['new_messages']);
                        unset($difference);
                        break;
                    case 'updates.differenceTooLong':
                        $state->update($difference);
                        unset($difference);
                        break;
                    default:
                        throw new Exception('Unrecognized update difference received: '.var_export($difference, true));
                }
            }
        }
        $this->API->logger("Finished parsing updates in {$this}, now resuming feeders", Logger::ULTRA_VERBOSE);
        foreach ($result as $channelId => $_) {
            ($this->API->feeders[$channelId] ?? null)?->resume();
        }
        $this->API->logger("Finished parsing updates in {$this}, pausing for $timeout seconds", Logger::ULTRA_VERBOSE);

        return $timeout;
    }
    /**
     * Get loop name.
     *
     * @psalm-mutation-free
     */
    public function __toString(): string
    {
        return $this->channelId ?
            "getUpdate loop channel {$this->channelId}" :
            'getUpdate loop generic';
    }
}
<?php

declare(strict_types=1);

/**
 * Update feeder loop.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Loop\Update;

use danog\Loop\Loop;
use danog\MadelineProto\API;
use danog\MadelineProto\AsyncTools;
use danog\MadelineProto\Logger;
use danog\MadelineProto\Loop\InternalLoop;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\MTProto\LoginState;
use danog\MadelineProto\MTProtoTools\UpdatesState;
use danog\MadelineProto\Reactive\SimpleSubscriber;

/**
 * Update feed loop.
 *
 * @internal
 *
 * @author Daniil Gentili <daniil@daniil.it>
 *
 * @implements SimpleSubscriber<LoginState>
 */
final class FeedLoop extends Loop implements SimpleSubscriber
{
    use InternalLoop {
        __construct as private init;
    }
    /**
     * Main loop ID.
     */
    public const GENERIC = 0;
    /**
     * Incoming updates array.
     */
    private array $incomingUpdates = [];
    /**
     * Parsed updates array.
     */
    private array $parsedUpdates = [];
    /**
     * Update loop.
     */
    private ?UpdateLoop $updater = null;
    /**
     * Update state.
     */
    private ?UpdatesState $state = null;
    private bool $mustPause;
    /**
     * Constructor.
     */
    public function __construct(MTProto $API, private int $channelId = 0)
    {
        $this->init($API);
        $API->loginState->subscribe($this);
    }
    /**
     * @psalm-pure
     */
    public function __sleep(): array
    {
        return ['incomingUpdates', 'parsedUpdates', 'updater', 'API', 'state', 'channelId'];
    }
    #[\Override]
    public function onSimpleStateChange($state): void
    {
        $this->mustPause = $state->state !== API::LOGGED_IN;
        if (!$this->mustPause) {
            if ($this->isRunning()) {
                $this->resume(true);
            } else {
                $this->start();
            }
        }
    }
    /**
     * Main loop.
     */
    #[\Override]
    public function loop(): ?float
    {
        if ($this->mustPause) {
            return self::PAUSE;
        }
        $this->updater = $this->API->updaters[$this->channelId];
        $this->state = $this->channelId === self::GENERIC ? $this->API->loadUpdateState() : $this->API->loadChannelState($this->channelId);

        $this->API->logger("Resumed {$this}");
        while ($this->incomingUpdates) {
            $updates = $this->incomingUpdates;
            $this->incomingUpdates = [];
            $this->parse($updates);
            $updates = null;
        }
        while ($this->parsedUpdates) {
            $parsedUpdates = $this->parsedUpdates;
            $this->parsedUpdates = [];
            foreach ($parsedUpdates as $update) {
                try {
                    $this->API->saveUpdate($update);
                } catch (\Throwable $e) {
                    AsyncTools::rethrow($e);
                }
            }
            $parsedUpdates = null;
        }
        return self::PAUSE;
    }
    public function parse(array $updates): void
    {
        reset($updates);
        while ($updates) {
            $key = key($updates);
            $update = $updates[$key];
            unset($updates[$key]);
            if ($update['_'] === 'updateChannelTooLong') {
                $this->API->logger('Got channel too long update, getting difference...', Logger::VERBOSE);
                $this->updater->resume();
                continue;
            }
            if (isset($update['pts'], $update['pts_count'])) {
                $logger = /**
                 * @psalm-external-mutation-free
                 */
                function ($msg) use ($update): void {
                    $pts_count = $update['pts_count'];
                    $mid = $update['message']['id'] ?? '-';
                    $mypts = $this->state->pts();
                    $computed = $mypts + $pts_count;
                    $this->API->logger("{$msg}. My pts: {$mypts}, remote pts: {$update['pts']}, computed pts: {$computed}, msg id: {$mid}, channel id: {$this->channelId}", Logger::ULTRA_VERBOSE);
                };
                $result = $this->state->checkPts($update);
                if ($result < 0) {
                    $logger('PTS duplicate');
                    continue;
                }
                if ($result > 0) {
                    $logger('PTS hole');
                    //$this->updater->setLimit($this->state->pts() + $result);
                    $this->updater->resume();
                    //$updates = array_merge($this->incomingUpdates, $updates);
                    //$this->incomingUpdates = [];
                    continue;
                }
                if (isset($update['message']['id'], $update['message']['peer_id']) && !\in_array($update['_'], ['updateEditMessage', 'updateEditChannelMessage', 'updateMessageID'], true)) {
                    if (!$this->API->checkMsgId($update['message'])) {
                        $logger('MSGID duplicate');
                        continue;
                    }
                }
                $logger('PTS OK');
                $this->state->pts($update['pts']);
            }

            $this->parsedUpdates[] = $update;
        }
    }
    public function feed(array $updates)
    {
        $result = [];
        foreach ($updates as $update) {
            $result[$this->feedSingle($update)] = true;
        }
        return $result;
    }
    public function feedSingle(array $update)
    {
        $channelId = self::GENERIC;
        switch ($update['_']) {
            case 'updateNewChannelMessage':
            case 'updateEditChannelMessage':
                $channelId = $update['message']['peer_id'];
                break;
            case 'updateChannelWebPage':
            case 'updateDeleteChannelMessages':
                $channelId = $update['channel_id'];
                break;
            case 'updateChannelTooLong':
            case 'updateChannel':
                $channelId = $update['channel_id'] ?? self::GENERIC;
                if (!isset($update['pts'])) {
                    $update['pts'] = 1;
                }
                break;
        }
        if ($channelId && !$this->API->getChannelStates()->has($channelId)) {
            $this->API->loadChannelState($channelId, $update);
            if (!isset($this->API->feeders[$channelId])) {
                $this->API->feeders[$channelId] = new self($this->API, $channelId);
            }
            if (!isset($this->API->updaters[$channelId])) {
                $this->API->updaters[$channelId] = new UpdateLoop($this->API, $channelId);
            }
            $this->API->feeders[$channelId]->start();
            $this->API->updaters[$channelId]->start();
        }
        switch ($update['_']) {
            case 'updateNewMessage':
            case 'updateEditMessage':
            case 'updateNewChannelMessage':
            case 'updateEditChannelMessage':
                $to = false;
                $from = false;
                $via_bot = false;
                $entities = false;
                if ($update['message']['_'] !== 'messageEmpty' && (
                    (
                        $from = isset($update['message']['from_id'])
                        && !($this->API->peerIsset($update['message']['from_id']))
                    ) || (
                        $to = !($this->API->peerIsset($update['message']['peer_id']))
                    )
                        || (
                            $via_bot = isset($update['message']['via_bot_id'])
                            && !($this->API->peerIsset($update['message']['via_bot_id']))
                        ) || (
                            $entities = isset($update['message']['entities'])
                            && !($this->API->entitiesPeerIsset($update['message']['entities']))
                        )
                )
                ) {
                    $log = '';
                    if ($from) {
                        $from_id = $this->API->getIdInternal($update['message']['from_id']);
                        $log .= "from_id {$from_id}, ";
                    }
                    if ($to) {
                        $log .= 'peer_id '.json_encode($update['message']['peer_id']).', ';
                    }
                    if ($via_bot) {
                        $log .= "via_bot {$update['message']['via_bot_id']}, ";
                    }
                    if ($entities) {
                        $log .= 'entities '.json_encode($update['message']['entities']).', ';
                    }
                    $this->API->logger("Not enough data: for message update {$log}, getting difference...", Logger::VERBOSE);
                    $update = ['_' => 'updateChannelTooLong'];
                    if ($channelId && $to) {
                        $channelId = self::GENERIC;
                    }
                }
                break;
            default:
                if ($channelId && !$this->API->peerIsset($channelId)) {
                    $this->API->logger('Skipping update, I do not have the channel id '.$channelId, Logger::ERROR);
                    return false;
                }
                break;
        }
        if ($channelId !== $this->channelId) {
            if (isset($this->API->feeders[$channelId])) {
                return $this->API->feeders[$channelId]->feedSingle($update);
            } elseif ($this->channelId) {
                return $this->API->feeders[self::GENERIC]->feedSingle($update);
            }
        }
        $this->API->logger('Was fed an update of type '.$update['_']." in {$this}...", Logger::ULTRA_VERBOSE);
        if ($update['_'] === 'updateLoginToken') {
            $this->API->saveUpdate($update);
            return $this->channelId;
        }
        $this->incomingUpdates[] = $update;
        return $this->channelId;
    }
    public function saveMessages($messages): void
    {
        foreach ($messages as $message) {
            if (!$this->API->checkMsgId($message)) {
                $this->API->logger("MSGID duplicate ({$message['id']}) in {$this}");
                continue;
            }
            if ($message['_'] !== 'messageEmpty') {
                $this->API->logger('Getdiff fed me message of type '.$message['_']." in {$this}...", Logger::VERBOSE);
            }
            $this->parsedUpdates[] = ['_' => $this->channelId === self::GENERIC ? 'updateNewMessage' : 'updateNewChannelMessage', 'message' => $message, 'pts' => -1, 'pts_count' => -1];
        }
    }
    /**
     * @psalm-mutation-free
     */
    public function __toString(): string
    {
        return !$this->channelId ? 'update feed loop generic' : "update feed loop channel {$this->channelId}";
    }
}
<?php

declare(strict_types=1);

/**
 * Update feeder loop.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Loop\Update;

use danog\Loop\Loop;
use danog\MadelineProto\API;
use danog\MadelineProto\Logger;
use danog\MadelineProto\Loop\InternalLoop;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\MTProto\LoginState;
use danog\MadelineProto\MTProtoTools\UpdatesState;
use danog\MadelineProto\Reactive\SimpleSubscriber;

use function Amp\delay;

/**
 * update feed loop.
 *
 * @internal
 *
 * @author Daniil Gentili <daniil@daniil.it>
 *
 * @implements SimpleSubscriber<LoginState>
 */
final class SeqLoop extends Loop implements SimpleSubscriber
{
    use InternalLoop {
        __construct as private init;
    }
    /**
     * Incoming updates.
     */
    private array $incomingUpdates = [];
    /**
     * Update feeder.
     */
    private ?FeedLoop $feeder = null;
    /**
     * Pending updates.
     */
    private array $pendingWakeups = [];
    /**
     * State.
     */
    private ?UpdatesState $state = null;
    private bool $mustPause;
    /**
     * Constructor.
     */
    public function __construct(MTProto $API)
    {
        $this->init($API);
        $API->loginState->subscribe($this);
    }
    /**
     * @psalm-pure
     */
    public function __sleep(): array
    {
        return ['incomingUpdates', 'feeder', 'API', 'state'];
    }
    #[\Override]
    public function onSimpleStateChange($state): void
    {
        $this->mustPause = $state->state !== API::LOGGED_IN;
        if (!$this->mustPause) {
            if ($this->isRunning()) {
                $this->resume(true);
            } else {
                $this->start();
            }
        }
    }
    /**
     * Main loop.
     */
    #[\Override]
    public function loop(): ?float
    {
        if ($this->mustPause) {
            return self::PAUSE;
        }
        $this->feeder = $this->API->feeders[FeedLoop::GENERIC];
        $this->state = $this->API->loadUpdateState();
        $this->API->logger("Resumed $this!", Logger::LEVEL_ULTRA_VERBOSE);
        while ($this->incomingUpdates) {
            $updates = $this->incomingUpdates;
            $this->incomingUpdates = [];
            $this->parse($updates);
            $updates = null;
        }
        while ($this->pendingWakeups) {
            reset($this->pendingWakeups);
            $channelId = key($this->pendingWakeups);
            unset($this->pendingWakeups[$channelId]);
            if (isset($this->API->feeders[$channelId])) {
                $this->API->feeders[$channelId]->resume();
            }
        }
        return self::PAUSE;
    }
    public function parse(array $updates): void
    {
        reset($updates);
        while ($updates) {
            $options = [];
            $key = key($updates);
            $update = $updates[$key];
            unset($updates[$key]);
            $options = $update['options'];
            $seq_start = $options['seq_start'];
            $seq_end = $options['seq_end'];
            $result = $this->state->checkSeq($seq_start);
            if ($result > 0) {
                $this->API->logger('Seq hole. seq_start: '.$seq_start.' != cur seq: '.($this->state->seq() + 1), Logger::ERROR);
                //delay(1);
                //if (!$this->incomingUpdates) {
                $this->API->updaters[UpdateLoop::GENERIC]->resume();
                //}
                //$this->incomingUpdates = array_merge($this->incomingUpdates, [$update], $updates);
                continue;
            }
            if ($result < 0) {
                $this->API->logger('Seq too old. seq_start: '.$seq_start.' != cur seq: '.($this->state->seq() + 1), Logger::ERROR);
                continue;
            }
            $this->state->seq($seq_end);
            if (isset($options['date'])) {
                $this->state->date($options['date']);
            }
            $this->save($update);
        }
    }
    /**
     * @param array<(array|mixed)> $updates
     *
     * @psalm-external-mutation-free
     */
    public function feed(array $updates): void
    {
        $this->API->logger('Was fed updates of type '.$updates['_'].'...', Logger::VERBOSE);
        $this->incomingUpdates[] = $updates;
    }
    /**
     * @param array{updates: array} $updates
     */
    public function save(array $updates): void
    {
        $this->pendingWakeups += ($this->feeder->feed($updates['updates']));
    }
    /**
     * @param array<true> $wakeups
     *
     * @psalm-external-mutation-free
     */
    public function addPendingWakeups(array $wakeups): void
    {
        $this->pendingWakeups += $wakeups;
    }
    /**
     * @psalm-pure
     */
    public function __toString(): string
    {
        return 'update seq loop';
    }
}
<?php

declare(strict_types=1);

/**
 * RPC call status check loop.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Loop\Connection;

use danog\Loop\Loop;
use danog\MadelineProto\Connection;

/**
 * Message cleanup loop.
 *
 * @internal
 *
 * @author Daniil Gentili <daniil@daniil.it>
 */
final class CleanupLoop extends Loop
{
    use Common {
        __construct as initCommon;
    }

    public function __construct(Connection $connection)
    {
        $this->initCommon($connection);
        $this->logPauses = false;
    }

    /**
     * Main loop.
     */
    #[\Override]
    protected function loop(): ?float
    {
        $this->connection->msgIdHandler?->cleanup();
        return 10.0;
    }
    /**
     * Loop name.
     *
     * @psalm-mutation-free
     */
    public function __toString(): string
    {
        return "cleanup loop in DC {$this->datacenter}";
    }
}
<?php

declare(strict_types=1);

/**
 * Socket write loop.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Loop\Connection;

use Amp\ByteStream\StreamException;
use Amp\CancelledException;
use Amp\DeferredFuture;
use danog\Loop\Loop;
use danog\MadelineProto\Connection;
use danog\MadelineProto\Logger;
use danog\MadelineProto\MTProto\ConnectionState;
use danog\MadelineProto\MTProto\Container;
use danog\MadelineProto\MTProto\LinkedList;
use danog\MadelineProto\MTProto\MTProtoOutgoingMessage;
use danog\MadelineProto\MTProtoTools\Crypt;
use danog\MadelineProto\Reactive\EphemeralSubscriber;
use danog\MadelineProto\Reactive\Subscriber;
use danog\MadelineProto\Tools;
use Revolt\EventLoop;
use WeakMap;

/**
 * Socket write loop.
 *
 * @internal
 *
 * @author Daniil Gentili <daniil@daniil.it>
 *
 * @implements Subscriber<ConnectionState>
 */
final class WriteLoop extends Loop implements Subscriber, EphemeralSubscriber
{
    private const MAX_COUNT = 1020;
    private const MAX_SIZE = 1 << 15;
    private const LONG_POLL_TIMEOUT = 30.0;
    private const LONG_POLL_TIMEOUT_MS = 30_000;
    public const MAX_IDS = 8192;

    use Common {
        __construct as init2;
    }

    private readonly bool $isHttp;

    private int $resendTimeout;

    private LinkedList $queue;
    private ?ConnectionState $pendingState = null;

    /**
     * Constructor function.
     */
    public function __construct(Connection $connection)
    {
        $this->init2($connection);
        $this->resendTimeout = $this->API->getSettings()->getRpc()->getRpcResendTimeout();
        $connection->getState()->subscribe($this);
    }

    /**
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function onAttach($initState): void
    {
        $this->pendingState = $initState;
    }

    #[\Override]
    public function onStateChange($prevState, $state): void
    {
        $this->pendingState = $state;
        $this->resume();
    }
    /**
     * Main loop.
     */
    #[\Override]
    public function loop(): ?float
    {
        while (true) {
            if ($this->connection->shouldReconnect()) {
                $this->API->logger("Exiting $this because connection is old");
                return self::STOP;
            }
            if ($this->pendingState !== null) {
                $this->queue = match ($this->pendingState) {
                    ConnectionState::UNENCRYPTED,
                    ConnectionState::UNENCRYPTED_NO_PERMANENT,
                    ConnectionState::UNENCRYPTED_MEDIA_WAITING_MAIN => $this->connection->unencryptedPendingOutgoing,
                    ConnectionState::ENCRYPTED,
                    ConnectionState::ENCRYPTED_NOT_AUTHED_NO_LOGIN => $this->connection->mainPendingOutgoing,
                    default => $this->connection->uninitedPendingOutgoing
                };
                $this->pendingState = null;
            }
            $this->connection->writing(true);
            try {
                if ($this->queue === $this->connection->unencryptedPendingOutgoing) {
                    $this->unencryptedWriteLoop();
                } else {
                    $this->encryptedWriteLoop();
                }
                if ($this->pendingState === null) {
                    $this->API->logger("No messages, pausing in $this...", Logger::ULTRA_VERBOSE);
                    return self::LONG_POLL_TIMEOUT;
                }
            } catch (StreamException $e) {
                if ($this->connection->shouldReconnect()) {
                    $this->API->logger("Stopping $this because we have to reconnect");
                    return self::STOP;
                }
                EventLoop::queue(function () use ($e): void {
                    $this->API->logger($e);
                    $this->API->logger("Got nothing in the socket in DC {$this->datacenter}, reconnecting...", Logger::ERROR);
                    $this->connection->reconnect();
                });
                $this->API->logger("Stopping $this");
                return self::STOP;
            } catch (\Throwable $e) {
                $this->API->logger("Exiting $this due to $e", Logger::FATAL_ERROR);
                return self::STOP;
            } finally {
                $this->connection->writing(false);
            }
        }
    }
    public function unencryptedWriteLoop(): void
    {
        if (0 !== ($queue = $this->queue->check_queue)->count()) {
            $this->queue->check_queue = new WeakMap;
            foreach ($queue as $msg => $_) {
                // TODO: wait for actual re-queueing
                $this->connection->methodRecall($msg);
            }
        }
        while ($message = $this->queue->peek()) {
            $this->API->logger("Sending $message as unencrypted message to DC $this->datacenter", Logger::ULTRA_VERBOSE);
            $serialized = $message->getSerializedBody();

            $message_id = $message->getMsgId() ?? $this->connection->msgIdHandler->generateMessageId();
            $length = \strlen($serialized);
            $pad_length = -$length & 15;
            $pad_length += 16 * Tools::randomInt(modulus: 16);
            $pad = Tools::random($pad_length);
            $buffer = $this->connection->stream->getWriteBuffer($total_len = 8 + 8 + 4 + $pad_length + $length);
            $buffer->bufferWrite("\0\0\0\0\0\0\0\0".Tools::packSignedLong($message_id).Tools::packUnsignedInt($length).$serialized.$pad);
            $this->connection->httpSent();
            $this->connection->outgoingBytesCtr?->incBy($total_len);

            $this->API->logger("Sent $message as unencrypted message to DC $this->datacenter!", Logger::ULTRA_VERBOSE);

            $message->setMsgId($message_id);
            $message->sent();
        }
    }
    public function encryptedWriteLoop(): void
    {
        while ($this->pendingState === null &&
            (
                !$this->queue->isEmpty()
                || $this->connection->ack_queue
                || $this->queue->check_queue->count()
            )
        ) {
            $this->API->logger("Resuming write loop in DC $this->datacenter", Logger::ULTRA_VERBOSE);
            if (0 !== ($check = $this->queue->check_queue)->count()) {
                $this->queue->check_queue = new WeakMap;
                $deferred = new DeferredFuture();
                $deferred->getFuture()->catch(function (\Throwable $e): void {
                    $this->API->logger("Got exception in check loop for DC {$this->datacenter}");
                    $this->API->logger((string) $e);
                });

                $list = '';
                $msgIds = [];
                $arr = [];
                foreach ($check as $msg => $_) {
                    if ($msg->hasReply()) {
                        continue;
                    }
                    $id = $msg->getMsgId();
                    if ($id === null) {
                        $this->API->logger("$msg has no ID, cannot request status!", Logger::ERROR);
                        continue;
                    }
                    $msgIds[] = $id;
                    $list .= $msg.', ';
                    $arr[] = $msg;
                }
                if ($msgIds) {
                    $this->API->logger("Still missing {$list} on DC {$this->datacenter}, sending state request", Logger::ERROR);
                    $stream = $this->connection->stream;
                    $this->connection->objectCallAsync('msgs_state_req', [
                        'msg_ids' => $msgIds,
                        'cancellation' => new \Amp\TimeoutCancellation(
                            self::LONG_POLL_TIMEOUT,
                            "Timeout while waiting for msgs_state_req response on DC {$this->datacenter}"
                        ),
                    ], $deferred);
                    $deferred->getFuture()->map(function (array|\Closure $result) use ($arr, $stream): void {
                        if (\is_callable($result)) {
                            try {
                                $result();
                            } catch (CancelledException $e) {
                                // The server always answers state requests right away: the connection is dead,
                                // like a half-open TCP connection silently dropped along the way (by a NAT, for
                                // example), on which nothing errors out and nothing ever arrives. Reconnect,
                                // resending the pending messages, unless it was already replaced.
                                if ($this->connection->stream === $stream && $stream !== null) {
                                    $this->API->logger("Got no answer to the state request in DC {$this->datacenter}, reconnecting...", Logger::ERROR);
                                    $this->connection->reconnect();
                                }
                            } catch (\Throwable $e) {
                                $this->API->logger("Got exception in check loop for DC {$this->datacenter}");
                                $this->API->logger((string) $e);
                            }
                            return;
                        }
                        try {
                            foreach (str_split($result['info']) as $key => $chr) {
                                $message = $arr[$key];
                                if ($message->hasReply()) {
                                    $this->API->logger("Already got response for and forgot about message $message");
                                    $this->connection->ack_queue[] = $message->getMsgId();
                                    continue;
                                }
                                $chr = \ord($chr);
                                switch ($chr & 7) {
                                    case 0:
                                        $this->API->logger("Wrong message status 0 for $message", Logger::FATAL_ERROR);
                                        break;
                                    case 1:
                                    case 2:
                                    case 3:
                                        if ($message->constructor === 'msgs_state_req') {
                                            $message->reply(null);
                                            break;
                                        }
                                        $this->API->logger("Message $message not received by server, resending...", Logger::ERROR);
                                        $this->connection->methodRecall($message);
                                        break;
                                    case 4:
                                        if ($chr & 128) {
                                            $this->API->logger("Message $message received by server and was already sent.", Logger::ERROR);
                                        } elseif ($chr & 64) {
                                            $this->API->logger("Message $message received by server and was already processed.", Logger::ERROR);
                                        } elseif ($chr & 32) {
                                            if ($message->getSent() + $this->resendTimeout < hrtime(true)) {
                                                if (!$message->cancellation?->isRequested()) {
                                                    $this->API->logger("Message $message received by server and is being processed for way too long, resending request...", Logger::ERROR);
                                                    $this->connection->methodRecall($message);
                                                }
                                            } else {
                                                $this->API->logger("Message $message received by server and is being processed, waiting...", Logger::ERROR);
                                            }
                                        } else {
                                            $this->API->logger("Message $message received by server, waiting...", Logger::ERROR);
                                        }
                                        break;
                                }
                            }
                        } catch (\Throwable $e) {
                            $this->API->logger("Got exception in check loop for DC {$this->datacenter}");
                            $this->API->logger((string) $e);
                        }
                    });
                }
            }

            $messages = [];
            $MTmessages = [];

            $total_length = 0;
            $count = 0;

            $has_seq = false;

            $has_state = false;
            $has_resend = false;
            $has_content_related = false;

            $message = $this->queue;
            while (($message = $message->prev) instanceof MTProtoOutgoingMessage) {
                $constructor = $message->constructor;
                if ($constructor === 'msgs_state_req') {
                    if ($has_state) {
                        $this->API->logger("Already have a state request queued for the current container in DC {$this->datacenter}");
                        continue;
                    }
                    $has_state = true;
                }
                if ($constructor === 'msg_resend_req') {
                    if ($has_resend) {
                        continue;
                    }
                    $has_resend = true;
                }

                $body_length = \strlen($message->getSerializedBody());
                $actual_length = $body_length + 32;
                if ($total_length && $total_length + $actual_length > 32760 || $count >= self::MAX_COUNT) {
                    $this->API->logger('Length overflow, postponing part of payload', Logger::ULTRA_VERBOSE);
                    break;
                }
                if ($message->hasSeqNo()) {
                    $has_seq = true;
                }

                $message_id = $message->getMsgId() ?? $this->connection->msgIdHandler->generateMessageId();
                $this->API->logger("Sending $message as encrypted message with id $message_id to DC $this->datacenter", Logger::ULTRA_VERBOSE);
                $MTmessage = [
                    '_' => 'MTmessage',
                    'msg_id' => $message_id,
                    'body' => $message->getSerializedBody(),
                    'seqno' => $message->getSeqNo() ?? $this->connection->generateOutSeqNo($message->contentRelated),
                ];

                $body_length = \strlen($MTmessage['body']);
                $actual_length = $body_length + 32;
                if ($total_length && $total_length + $actual_length > 32760) {
                    $this->API->logger('Length overflow, postponing part of payload', Logger::ULTRA_VERBOSE);
                    break;
                }
                $count++;
                $total_length += $actual_length;
                $MTmessage['bytes'] = $body_length;
                $MTmessages[] = $MTmessage;
                $messages[] = $message;

                $has_content_related = $has_content_related || $message->hasPromise();

                $message->setSeqNo($MTmessage['seqno'])
                        ->setMsgId($MTmessage['msg_id']);
            }
            $MTmessage = null;

            $acks = \array_slice($this->connection->ack_queue, 0, self::MAX_COUNT);
            if ($ackCount = \count($acks)) {
                $this->API->logger('Adding msgs_ack', Logger::ULTRA_VERBOSE);

                $body = $this->API->getTL()->serializeObject(['type' => ''], ['_' => 'msgs_ack', 'msg_ids' => $acks], 'msgs_ack');
                $MTmessages[]= [
                    '_' => 'MTmessage',
                    'msg_id' => $this->connection->msgIdHandler->generateMessageId(),
                    'body' => $body,
                    'seqno' => $this->connection->generateOutSeqNo(false),
                    'bytes' => \strlen($body),
                ];
                $count++;
                unset($body);
            }
            if ($this->connection->isHttp()) {
                $this->API->logger('Adding http_wait', Logger::ULTRA_VERBOSE);
                $body = $this->API->getTL()->serializeObject(['type' => ''], ['_' => 'http_wait', 'max_wait' => 30000, 'wait_after' => 0, 'max_delay' => 0], 'http_wait');
                $MTmessages []= [
                    '_' => 'MTmessage',
                    'msg_id' => $this->connection->msgIdHandler->generateMessageId(),
                    'body' => $body,
                    'seqno' => $this->connection->generateOutSeqNo(true),
                    'bytes' => \strlen($body),
                ];
                $count++;
                unset($body);
            }

            if ($count > 1 || $has_seq) {
                $message_id = $this->connection->msgIdHandler->generateMessageId();
                $this->API->logger("Wrapping in msg_container ({$count} messages of total size {$total_length}, id $message_id) as encrypted message for DC {$this->datacenter}", Logger::ULTRA_VERBOSE);
                $ct = new Container(
                    $this->connection,
                    $messages,
                    $acks,
                );
                if ($has_content_related) {
                    $messages []= $ct;
                }
                $this->connection->outgoingCtr?->inc();
                $message_data = $this->API->getTL()->serializeObject(['type' => ''], ['_' => 'msg_container', 'messages' => $MTmessages], 'container');
                $message_data_length = \strlen($message_data);
                $seq_no = $this->connection->generateOutSeqNo(false);
                $ct->setMsgId($message_id);
                $ct->setSeqNo($seq_no);
            } elseif ($count) {
                $message = $MTmessages[0];
                $message_data = $message['body'];
                $message_data_length = $message['bytes'];
                $message_id = $message['msg_id'];
                $seq_no = $message['seqno'];
            } else {
                $msg = "NO MESSAGE SENT in $this, ";
                $msg .= $this->queue->isEmpty() ? "messages: true, " : "messages: false, ";
                $msg .= $this->connection->ack_queue ? "ack: true, " : "ack: false, ";
                $msg .= $this->queue->check_queue->count() ? "queue: true" : "check queue: false";
                $this->API->logger($msg, Logger::ULTRA_VERBOSE);
                continue;
            }
            unset($MTmessages);

            $auth = $this->shared->auth;

            $plaintext = $auth->getServerSalt().$this->connection->session_id.Tools::packSignedLong($message_id).pack('VV', $seq_no, $message_data_length).$message_data;
            $padding = Tools::posmod(-\strlen($plaintext), 16);
            if ($padding < 12) {
                $padding += 16;
            }
            $padding = Tools::random($padding);
            $message_key_large = hash('sha256', $auth->getTempAuthKeyForHash().$plaintext.$padding, true);
            $message_key = substr($message_key_large, 8, 16);
            //$ack = unpack('V', substr($message_key_large, 0, 4))[1] | (1 << 31);
            [$aes_key, $aes_iv] = Crypt::kdf($message_key, $auth->getTempAuthKey());
            $message = $auth->getTempID().$message_key.Crypt::igeEncrypt($plaintext.$padding, $aes_key, $aes_iv);
            $buffer = $this->connection->stream->getWriteBuffer($total_len = \strlen($message));
            $buffer->bufferWrite($message);
            $this->connection->httpSent();
            $this->connection->outgoingBytesCtr?->incBy($total_len);
            $this->API->logger("Sent encrypted payload to DC {$this->datacenter}", Logger::ULTRA_VERBOSE);

            if ($ackCount) {
                $this->connection->ack_queue = \array_slice($this->connection->ack_queue, $ackCount);
            }

            foreach ($messages as $message) {
                $message->sent();
            }
        }
    }
    /**
     * Get loop name.
     *
     * @psalm-mutation-free
     */
    public function __toString(): string
    {
        return "write loop in DC {$this->datacenter}";
    }
}
<?php

declare(strict_types=1);

/**
 * Socket read loop.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Loop\Connection;

use Amp\ByteStream\PendingReadError;
use Amp\ByteStream\StreamException;
use Amp\Websocket\WebsocketClosedException;
use danog\Loop\Loop;
use danog\MadelineProto\API;
use danog\MadelineProto\Logger;
use danog\MadelineProto\MTProto\MTProtoIncomingMessage;
use danog\MadelineProto\MTProtoTools\Crypt;
use danog\MadelineProto\NothingInTheSocketException;
use danog\MadelineProto\SecurityException;
use danog\MadelineProto\Tools;
use danog\MadelineProto\TransportError;
use Error;
use Revolt\EventLoop;

use function Amp\delay;
use function substr;

/**
 * Socket read loop.
 *
 * @internal
 *
 * @author Daniil Gentili <daniil@daniil.it>
 */
final class ReadLoop extends Loop
{
    use Common;
    /** rpc_result#f35c6d01, little-endian. */
    private const RPC_RESULT = "\x01\x6d\x5c\xf3";
    /** msg_container#73f1f8dc, little-endian. */
    private const MSG_CONTAINER = "\xdc\xf8\xf1\x73";
    /**
     * Main loop.
     */
    #[\Override]
    protected function loop(): ?float
    {
        try {
            $this->API->logger("Reading in $this...", Logger::ULTRA_VERBOSE);
            $error = $this->readMessage();
            $this->API->logger("Finished reading in $this!", Logger::ULTRA_VERBOSE);
        } catch (NothingInTheSocketException|StreamException|PendingReadError|Error $e) {
            if ($this->connection->shouldReconnect()) {
                $this->API->logger("Stopping $this due to reconnect...", Logger::ERROR);
                return self::STOP;
            }
            EventLoop::queue(function () use ($e): void {
                if ($e instanceof NothingInTheSocketException
                    && !$this->connection->unencrypted_new_outgoing
                    && !$this->connection->new_outgoing
                    && $this->connection->getShared()->auth->isMedia
                    && !$this->connection->isWriting()
                ) {
                    $this->API->logger("Got NothingInTheSocketException in DC {$this->datacenter}, disconnecting because we have nothing to do...", Logger::ERROR);
                    $this->connection->disconnect(true);
                } else {
                    $this->API->logger($e, Logger::ERROR);
                    $this->API->logger("Got exception in DC {$this->datacenter}, reconnecting...", Logger::ERROR);
                    $this->connection->reconnect();
                }
            });
            return self::STOP;
        } catch (SecurityException $e) {
            $this->connection->resetSession("security exception {$e->getMessage()}");
            $this->API->logger($e, Logger::ERROR);
            $this->API->logger("Got security exception in DC {$this->datacenter}, reconnecting...", Logger::ERROR);
            // Not from this fiber: reconnecting restarts this loop, which waits for this loop() to return.
            EventLoop::queue($this->connection->reconnect(...));
            return self::STOP;
        }
        if (\is_int($error)) {
            EventLoop::queue(function () use ($error): void {
                if ($error === -404) {
                    if ($this->shared->auth->getTempAuthKey() !== null) {
                        $this->API->logger("WARNING: Resetting auth key in DC {$this->datacenter}...", Logger::WARNING);
                        $this->shared->auth->setTempAuthKey(null, null);
                        $this->shared->resetSession("-404");
                        foreach ($this->connection->new_outgoing as $message) {
                            $message->resetSent();
                        }
                        foreach ($this->connection->unencrypted_new_outgoing as $message) {
                            $message->resetSent();
                        }
                        $this->shared->reconnect();
                    } else {
                        $this->connection->reconnect();
                    }
                } elseif ($error === -1) {
                    $this->API->logger("WARNING: Got quick ack from DC {$this->datacenter}", Logger::WARNING);
                    $this->connection->reconnect();
                } elseif ($error === 0) {
                    $this->API->logger("Got NOOP from DC {$this->datacenter}", Logger::WARNING);
                    $this->connection->reconnect();
                } elseif ($error === -429) {
                    $this->API->logger("Got -429 from DC {$this->datacenter}", Logger::WARNING);
                    delay(3);
                    $this->connection->reconnect();
                } else {
                    $this->connection->reconnect();
                    throw new TransportError($error);
                }
            });
            $this->API->logger("Stopping $this due to $error...", Logger::ERROR);
            return self::STOP;
        }
        $this->connection->httpReceived();
        return self::CONTINUE;
    }
    public function readMessage(): ?int
    {
        if ($this->connection->shouldReconnect()) {
            $this->API->logger('Not reading because connection is old');
            throw new NothingInTheSocketException();
        }
        try {
            $buffer = $this->connection->stream->getReadBuffer($payload_length);
        } catch (WebsocketClosedException $e) {
            $this->API->logger($e->getReason());
            if (str_starts_with($e->getReason(), '       ')) {
                $payload = -((int) substr($e->getReason(), 7));
                $this->API->logger("Received {$payload} from DC ".$this->datacenter, Logger::ERROR);
                return $payload;
            }
            throw $e;
        }
        /** @var int $payload_length */
        if ($payload_length & (1 << 31)) {
            $this->API->logger("Received quick ACK $payload_length from DC ".$this->datacenter, Logger::ULTRA_VERBOSE);
            $this->connection->incomingBytesCtr?->incBy(4);
            return null;
        }
        $this->connection->incomingBytesCtr?->incBy(4+$payload_length);
        if ($payload_length <= 16) {
            $code = Tools::unpackSignedInt($buffer->bufferRead(4));
            if ($code === -1 && $payload_length >= 8) {
                $ack = unpack('V', $buffer->bufferRead(4))[1];
                $this->API->logger("Received quick ACK $ack (padded) from DC ".$this->datacenter, Logger::ULTRA_VERBOSE);
                if ($payload_length > 8) {
                    $buffer->bufferRead($payload_length-8);
                }
                return null;
            }
            if ($payload_length > 4) {
                $buffer->bufferRead($payload_length-4);
            }
            $this->API->logger("Received {$code} from DC ".$this->datacenter, Logger::ULTRA_VERBOSE);
            return $code;
        }
        $this->connection->reading(true);
        try {
            $seq_no = null;
            $auth_key_id = $buffer->bufferRead(8);
            $auth = $this->shared->auth;
            if ($unencrypted = $auth_key_id === "\0\0\0\0\0\0\0\0") {
                if ($this->shared->auth->connectionState->getState()->isEncrypted()) {
                    // The late answer to an unencrypted request of the key exchange that was sent again by its
                    // check timer (MTProtoOutgoingMessage::check) and was answered twice: the key is already
                    // generated, nothing unencrypted is accepted anymore, drop it.
                    $this->API->logger("Dropping unencrypted message received in DC {$this->datacenter} after generating the auth key", Logger::WARNING);
                    $buffer->bufferRead($payload_length - 8);
                    return null;
                }
                $message_id = Tools::unpackSignedLong($buffer->bufferRead(8));
                $this->connection->msgIdHandler->checkIncomingMessageId($message_id, false);
                $message_length = unpack('V', $buffer->bufferRead(4))[1];
                $message_data = $buffer->bufferRead($message_length);
                $left = $payload_length - $message_length - 4 - 8 - 8;
                if ($left) {
                    $this->API->logger('Padded unencrypted message', Logger::ULTRA_VERBOSE);
                    if ($left < (-$message_length & 15)) {
                        $this->API->logger('Protocol padded unencrypted message', Logger::ULTRA_VERBOSE);
                    }
                    $this->connection->incomingBytesCtr?->incBy($left);
                    $buffer->bufferRead($left);
                }
            } elseif ($auth_key_id === $auth->getTempID()) {
                $message_key = $buffer->bufferRead(16);
                [$aes_key, $aes_iv] = Crypt::kdf($message_key, $auth->getTempAuthKey(), false);
                $payload_length -= 24;
                $left = $payload_length & 15;
                $payload_length -= $left;
                $decrypted_data = Crypt::igeDecrypt($buffer->bufferRead($payload_length), $aes_key, $aes_iv);
                if ($left) {
                    $this->connection->incomingBytesCtr?->incBy($left);
                    $buffer->bufferRead($left);
                }
                if ($message_key != substr(hash('sha256', substr($auth->getTempAuthKey(), 96, 32).$decrypted_data, true), 8, 16)) {
                    throw new SecurityException('msg_key mismatch');
                }
                /*
                                $server_salt = substr($decrypted_data, 0, 8);
                                if ($server_salt != $this->shared->getTempAuthKey()->getServerSalt()) {
                                $this->API->logger('WARNING: Server salt mismatch (my server salt '.$this->shared->getTempAuthKey()->getServerSalt().' is not equal to server server salt '.$server_salt.').', Logger::WARNING);
                                }
                */
                $session_id = substr($decrypted_data, 8, 8);
                if ($session_id !== $this->connection->session_id) {
                    $this->API->logger('Session ID mismatch', Logger::FATAL_ERROR);
                    $this->connection->resetSession("session ID mismatch");
                    throw new NothingInTheSocketException();
                }
                $message_id = Tools::unpackSignedLong(substr($decrypted_data, 16, 8));
                $this->connection->msgIdHandler->checkIncomingMessageId($message_id, false);
                $seq_no = unpack('V', substr($decrypted_data, 24, 4))[1];
                $message_data_length = unpack('V', substr($decrypted_data, 28, 4))[1];
                if ($message_data_length > \strlen($decrypted_data)) {
                    throw new SecurityException('message_data_length is too big');
                }
                if (\strlen($decrypted_data) - 32 - $message_data_length < 12) {
                    throw new SecurityException('padding is too small');
                }
                if (\strlen($decrypted_data) - 32 - $message_data_length > 1024) {
                    throw new SecurityException('padding is too big');
                }
                if ($message_data_length < 0) {
                    throw new SecurityException('message_data_length not positive');
                }
                if ($message_data_length % 4 != 0) {
                    throw new SecurityException('message_data_length not divisible by 4');
                }
                $message_data = substr($decrypted_data, 32, $message_data_length);
            } else {
                $this->API->logger('Got unknown auth_key id', Logger::ERROR);
                return -404;
            }
            $this->API->logger('Received payload from DC '.$this->datacenter, Logger::ULTRA_VERBOSE);

            try {
                $deserialized = $this->deserialize($message_data, $message_id, !$unencrypted);
                if ($deserialized === null) {
                    return null;
                }
            } finally {
                $this->API->minDatabase->reset();
            }

            $message = new MTProtoIncomingMessage(
                $this->connection,
                $deserialized,
                $message_id,
                $unencrypted,
                false
            );
            if (isset($seq_no)) {
                $message->setSeqNo($seq_no);
            }

            $this->connection->wakeupHandler($message);
            $this->connection->incomingCtr?->inc();
        } finally {
            $this->connection->reading(false);
        }
        return null;
    }
    /**
     * Deserialize a message, or as much of it as possible if it contains constructors missing from the schema.
     *
     * An undeserializable result fails its request with the deserialization error: had the read loop exited, the
     * request would be resent after reconnecting, and get the same result, forever.
     * The other undeserializable messages are acknowledged (so they aren't resent) and dropped,
     * the rest of their container is still handled.
     *
     * @return ?array The message, null if it was dropped.
     */
    private function deserialize(string $data, int $messageId, bool $encrypted, bool $fromContainer = false): ?array
    {
        try {
            return $this->API->getTL()->deserialize($data, ['type' => '', 'connection' => $this->connection, 'encrypted' => $encrypted]);
        } catch (\Throwable $e) {
            Logger::log('Error during deserializing message (base64): ' .  base64_encode($data), Logger::ERROR);
            if (!$encrypted) {
                $this->API->report("Schema issues, please report this to @danog_community: ".API::RELEASE.", $e");
                throw $e;
            }
        }
        $constructor = substr($data, 0, 4);
        if ($constructor === self::RPC_RESULT && \strlen($data) >= 12) {
            $this->API->report("Schema issues, please report this to @danog_community: ".API::RELEASE.", $e");
            return ['_' => 'rpc_result', 'req_msg_id' => Tools::unpackSignedLong(substr($data, 4, 8)), 'result' => $e];
        }
        if ($constructor === self::MSG_CONTAINER && !$fromContainer && \strlen($data) >= 8) {
            $messages = [];
            $count = unpack('V', substr($data, 4, 4))[1];
            $offset = 8;
            for ($i = 0; $i < $count && \strlen($data) >= $offset + 16; $i++) {
                $innerId = Tools::unpackSignedLong(substr($data, $offset, 8));
                $seqno = unpack('V', substr($data, $offset + 8, 4))[1];
                $length = unpack('V', substr($data, $offset + 12, 4))[1];
                $body = $this->deserialize(substr($data, $offset + 16, $length), $innerId, $encrypted, true);
                $offset += 16 + $length;
                if ($body !== null) {
                    $messages []= ['_' => 'message', 'msg_id' => $innerId, 'seqno' => $seqno, 'bytes' => $length, 'body' => $body];
                }
            }
            return ['_' => 'msg_container', 'messages' => $messages];
        }
        $this->API->report("Schema issues, please report this to @danog_community: ".API::RELEASE.", $e");
        $this->API->logger("Dropping undeserializable message $messageId from DC {$this->datacenter}", Logger::ERROR);
        $this->connection->ack_queue []= $messageId;
        return null;
    }
    /**
     * Get loop name.
     *
     * @psalm-mutation-free
     */
    public function __toString(): string
    {
        return "read loop in DC {$this->datacenter}";
    }
}
<?php

declare(strict_types=1);

/**
 * Common abstract class for all connection loops.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Loop\Connection;

use danog\MadelineProto\Connection;
use danog\MadelineProto\DataCenterConnection;
use danog\MadelineProto\Loop\InternalLoop;

/**
 * RPC call status check loop.
 *
 * @internal
 *
 * @author Daniil Gentili <daniil@daniil.it>
 */
trait Common
{
    use InternalLoop {
        __construct as private init;
    }
    /**
     * Connection instance.
     */
    private Connection $connection;
    /**
     * DC ID.
     */
    private string $datacenter;
    /**
     * DataCenterConnection instance.
     */
    private DataCenterConnection $shared;
    /**
     * Network-related timeouts.
     */
    private int $timeout;
    /**
     * Network-related timeouts.
     */
    private float $timeoutSeconds;
    /**
     * Constructor function.
     */
    public function __construct(Connection $connection)
    {
        $this->init($connection->getExtra());
        $this->connection = $connection;
        $this->datacenter = $connection->getDatacenterID();
        $this->shared = $connection->getShared();
        $this->timeoutSeconds = $this->shared->getSettings()->getTimeout();
        $this->timeout = (int) ($this->timeoutSeconds * 1_000_000_000.0);
    }
}
<?php

declare(strict_types=1);

/**
 * Ping loop.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Loop\Connection;

use danog\Loop\Loop;
use danog\MadelineProto\Connection;
use danog\MadelineProto\MTProto\ConnectionState;
use danog\MadelineProto\Reactive\EphemeralSubscriber;
use danog\MadelineProto\Reactive\SimpleSubscriber;
use Revolt\EventLoop;
use Throwable;

/**
 * Ping loop.
 *
 * @internal
 *
 * @author Daniil Gentili <daniil@daniil.it>
 *
 * @implements SimpleSubscriber<ConnectionState>
 */
final class PingLoop extends Loop implements SimpleSubscriber, EphemeralSubscriber
{
    use Common {
        __construct as constructCommon;
    }
    private int $timeoutDisconnect;
    private bool $mustPause;
    /**
     * Constructor function.
     */
    public function __construct(Connection $connection)
    {
        $this->constructCommon($connection);
        $this->timeout = $timeout = $this->shared->getSettings()->getPingInterval();
        $this->timeoutDisconnect = $timeout + 15;
        $this->timeoutSeconds = (float) $this->timeout;
        $connection->getShared()->auth->connectionState->subscribe($this);
    }
    #[\Override]
    public function onSimpleStateChange($state): void
    {
        if ($state === ConnectionState::ENCRYPTED) {
            $this->mustPause = false;
            if ($this->isRunning()) {
                $this->resume(true);
            } else {
                $this->start();
            }
        } else {
            $this->mustPause = true;
        }
    }

    /**
     * Main loop.
     */
    #[\Override]
    public function loop(): ?float
    {
        if ($this->mustPause) {
            return self::PAUSE;
        }

        EventLoop::queue(function (): void {
            try {
                $this->connection->methodCallAsyncRead('ping_delay_disconnect', ['ping_id' => random_bytes(8), 'disconnect_delay' => $this->timeoutDisconnect]);
            } catch (Throwable $e) {
                $this->API->logger("Error while pinging DC {$this->datacenter}");
                $this->API->logger((string) $e);
            }
        });
        return $this->timeoutSeconds;
    }
    /**
     * Get loop name.
     *
     * @psalm-mutation-free
     */
    public function __toString(): string
    {
        return "Ping loop in DC {$this->datacenter}";
    }
}
<?php

declare(strict_types=1);

/**
 * Internal loop trait.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Loop;

use danog\MadelineProto\InternalDoc;

/**
 * API loop trait.
 * @internal
 */
trait APILoop
{
    use LoggerLoop;

    /**
     * API instance.
     */
    protected InternalDoc $API;
    /**
     * Constructor.
     *
     * @param InternalDoc $API API instance
     */
    public function __construct(InternalDoc $API)
    {
        $this->API = $API;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Loop\VoIP;

use Amp\ByteStream\ReadableStream;
use danog\MadelineProto\ResumableStream;
use Webmozart\Assert\Assert;

/**
 * A resumable stream queued for playback.
 *
 * It is serialized as soon as it is queued, before anything reads it, so that it can be reopened from
 * the start of the item any number of times: to resume it after a restart, or to loop it as a hold file.
 *
 * The first play reads the queued stream itself, keeping its progress callback and cancellation, which
 * are not serialized.
 *
 * @internal
 */
final class StreamSnapshot
{
    private readonly string $serialized;
    private ?ReadableStream $stream;

    public function __construct(ReadableStream&ResumableStream $stream)
    {
        $this->serialized = serialize($stream);
        $this->stream = $stream;
    }

    /**
     * Get a new stream, positioned where the snapshotted one was when it was queued.
     */
    public function open(): ReadableStream
    {
        if ($this->stream !== null) {
            $stream = $this->stream;
            $this->stream = null;
            return $stream;
        }

        $stream = unserialize($this->serialized);
        Assert::isInstanceOf($stream, ReadableStream::class);
        return $stream;
    }

    /**
     * @psalm-mutation-free
     *
     * @return array{serialized: string}
     */
    public function __serialize(): array
    {
        return ['serialized' => $this->serialized];
    }

    /**
     * @param array{serialized: string} $data
     */
    public function __unserialize(array $data): void
    {
        $this->serialized = $data['serialized'];
        $this->stream = null;
    }
}
<?php

declare(strict_types=1);

/**
 * Internal loop trait.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Loop\VoIP;

use Amp\ByteStream\Pipe;
use Amp\ByteStream\ReadableIterableStream;
use Amp\ByteStream\ReadableStream;
use Amp\Cancellation;
use Amp\CancelledException;
use Amp\DeferredCancellation;
use Amp\DeferredFuture;
use AssertionError;
use danog\MadelineProto\GroupCall\Rtmp\FlvTags;
use danog\MadelineProto\LocalFile;
use danog\MadelineProto\Logger;
use danog\MadelineProto\Loop\VoIPLoop;
use danog\MadelineProto\Matroska;
use danog\MadelineProto\Mp4;
use danog\MadelineProto\Ogg;
use danog\MadelineProto\RemoteUrl;
use danog\MadelineProto\RestoresDeclaredProperties;
use danog\MadelineProto\ResumableStream;
use danog\MadelineProto\Tgcalls\CallControllerInterface;
use danog\MadelineProto\Tgcalls\H264Framing;
use danog\MadelineProto\Tgcalls\VideoCodecObserver;
use danog\MadelineProto\Tools;
use Revolt\EventLoop;
use SplQueue;
use Throwable;
use Webrtc\Codecs\Codec;

/**
 * The single disc jockey of a call: it plays a playlist of files, streaming both their audio and
 * their video.
 *
 * There is exactly one playback mechanism, and it is the same for audio and for video; only the
 * demuxer differs per file:
 *
 * - A WebM/Matroska file (it begins with the EBML header) is demuxed by {@see Matroska}: its already
 *   encoded video and OPUS audio frames are handed to the RTP senders untouched, keeping the
 *   timestamps they had in the file, which is what keeps the two in sync.
 * - An OGG OPUS file is demuxed by {@see Ogg}: its packets are handed to the tracks untouched, each
 *   paced by its own duration. Anything else is converted to OGG OPUS on the fly by ffmpeg.
 *   The legacy libtgvoip engine can only send 60ms packets, so for it ({@see self::setFixedOggGrid()})
 *   only MadelineProto's own OGG OPUS files are played as-is, and the others are converted too.
 *
 * Nothing is decoded or re-encoded for pre-encoded input, so playback needs no codec library (and
 * therefore no FFI). Frames are **streamed**, not buffered whole: the reader reads only as far ahead
 * of playback as configured ({@see \danog\MadelineProto\Settings\VoIP::setPlaybackLookahead()}, just
 * the next frame by default) and blocks until the tracks drain it, so memory stays bounded however
 * long the file is. The frames read ahead are not saved: playback resumes exactly from the first one
 * that was not played across a serialize â†’ destruct â†’ unserialize cycle (for a seekable source â€” a
 * raw stream or an on-the-fly conversion replays the current item instead).
 *
 * The whole playlist API â€” {@see self::play()}, {@see self::skip()}, {@see self::playOnHold()},
 * pausing, {@see self::getCurrent()} â€” applies to every file regardless of what media it carries.
 *
 * @internal
 */
final class DjLoop extends VoIPLoop
{
    use RestoresDeclaredProperties;
    /** VP8 and every other video codec on the wire use a 90kHz RTP clock. */
    public const VIDEO_CLOCK_RATE = 90000;
    /** OPUS always uses a 48kHz RTP clock. */
    public const AUDIO_CLOCK_RATE = 48000;
    /**
     * How far ahead of the look-ahead the reader may go to find the next frame of a track that has
     * none queued, see {@see self::throttle()}: muxers interleave the frames of the tracks of a file by
     * timestamp, so that's enough for any file but badly interleaved ones.
     */
    private const MAX_INTERLEAVING_MS = 1000;

    /**
     * The video codecs we can transmit, as `Matroska CodecID => SDP encoding name`.
     *
     * This is exactly the set a Telegram group call carries; see {@see \danog\MadelineProto\Tgcalls\GroupSdp}
     * for the payload types the SFU pairs them with.
     */
    public const VIDEO_CODECS = [
        'V_VP8' => 'VP8',
        'V_VP9' => 'VP9',
        'V_AV1' => 'AV1',
        'V_MPEG4/ISO/AVC' => 'H264',
        'V_MPEGH/ISO/HEVC' => 'H265',
    ];

    /** The audio codecs we can transmit, as `Matroska CodecID => SDP encoding name`. */
    public const AUDIO_CODECS = ['A_OPUS' => 'opus'];

    /** The video codecs of an RTMP livestream: the RTMP ingest of Telegram only accepts H.264. */
    public const RTMP_VIDEO_CODECS = ['V_MPEG4/ISO/AVC' => 'H264'];
    /** The audio codecs of an RTMP livestream: the RTMP ingest of Telegram only accepts AAC (any Matroska CodecID of it). */
    public const RTMP_AUDIO_CODECS = [
        'A_AAC' => 'aac',
        'A_AAC/MPEG2/MAIN' => 'aac',
        'A_AAC/MPEG2/LC' => 'aac',
        'A_AAC/MPEG2/LC/SBR' => 'aac',
        'A_AAC/MPEG2/SSR' => 'aac',
        'A_AAC/MPEG4/MAIN' => 'aac',
        'A_AAC/MPEG4/LC' => 'aac',
        'A_AAC/MPEG4/LC/SBR' => 'aac',
        'A_AAC/MPEG4/SSR' => 'aac',
        'A_AAC/MPEG4/LTP' => 'aac',
    ];

    /**
     * How a Matroska/WebM file begins: the EBML header, or â€” for a headerless file, as some muxers
     * and {@see Matroska} itself tolerate â€” the Segment element straight away.
     */
    private const MATROSKA_MAGICS = ["\x1A\x45\xDF\xA3", "\x18\x53\x80\x67"];
    /** OGG capture pattern: how an OGG file begins. */
    private const OGG_MAGIC = 'OggS';

    /* -------------------------------------------------------------------- *
     *  Playlist.
     * -------------------------------------------------------------------- */

    /** @var list<LocalFile|RemoteUrl|ReadableStream|StreamSnapshot> */
    private array $inputFiles = [];
    /** @var array<LocalFile|RemoteUrl|ReadableStream|StreamSnapshot> */
    private array $holdFiles = [];
    private int $holdIndex = 0;
    private bool $playingHold = false;
    private bool $pause = false;

    /* -------------------------------------------------------------------- *
     *  Bounded look-ahead queues, drained by the tracks.
     *
     *  They are not serialized: the current file is resumed from the position of its first frame
     *  that was not played yet, instead. Every queued frame carries its `position` in the file
     *  (see {@see self::trackResume()}), null if the file can't be resumed.
     * -------------------------------------------------------------------- */

    /** @var SplQueue<array{data: string, position: ?list{int, int}, file: int}> OPUS packets of an OGG file. */
    private SplQueue $oggQueue;
    /** Total duration of the packets in {@see self::$oggQueue}, in 48kHz samples. */
    private int $oggQueueSamples = 0;
    /** Whether OGG files must be MadelineProto's own 60ms ones, others being converted (for the legacy libtgvoip engine). */
    private bool $fixedOggGrid = false;
    /**
     * @var SplQueue<array{data: string, timestamp: int, position: ?list{int, int}, file: int}> OPUS frames of a WebM file (48kHz ts).
     *
     * The `file` of a queued frame is the {@see self::$generation} of the file it was read from: the
     * tracks hold a frame until its presentation time, so the next file may already be queued when
     * they get to it, and they must re-anchor their clocks right where it begins.
     */
    private SplQueue $webmAudioQueue;
    /** @var SplQueue<array{data: string, timestamp: int, keyframe: bool, position: ?list{int, int}, file: int}> Video frames (90kHz ts). */
    private SplQueue $videoQueue;

    /** Completed by a track when it drains a queue, to release the reader from back-pressure. */
    private ?DeferredFuture $drainDeferred = null;
    /** Awaited by {@see self::pullPacket()} and {@see self::awaitFrame()} until a frame is queued. */
    private ?DeferredFuture $packetDeferred = null;
    /** Whether the current WebM file has audio for the audio track, see {@see self::isStarving()}. */
    private bool $currentHasAudio = false;
    /** Whether the current WebM file has video for the video track, see {@see self::isStarving()}. */
    private bool $currentHasVideo = false;

    /* -------------------------------------------------------------------- *
     *  Current file + its byte-offset resume state.
     * -------------------------------------------------------------------- */

    private LocalFile|RemoteUrl|ReadableStream|StreamSnapshot|null $currentFile = null;
    private LocalFile|RemoteUrl|string|null $currentDesc = null;
    /** 'ogg', 'matroska' or 'mp4', or null when nothing is playing. */
    private ?string $currentKind = null;
    /** Whether the current file can be resumed from a byte offset (a seekable, non-converted source). */
    private bool $currentResumable = false;

    /** Byte offset of the container unit (OGG page / MKV cluster) to resume the current file from. */
    private int $resumeOffset = 0;
    /** How many frames of the resume unit were already produced, and so must be skipped on resume. */
    private int $resumeSkip = 0;
    /** Byte offset of the unit the last produced frame came from, used to keep {@see self::$resumeSkip}. */
    private int $resumeUnitOffset = -1;
    /**
     * Position of the last played audio and video frame of the files whose frames are queued, by
     * {@see self::$generation}, see {@see self::trackResume()}.
     *
     * The audio and video frames of a WebM file are interleaved, but played separately: when resuming
     * from the first frame that was not played, the frames of the other kind that were already played
     * are skipped, see {@see self::$resumePlayed}.
     *
     * @var array<int, array{audio?: list{int, int}, video?: list{int, int}}>
     */
    private array $playedPositions = [];
    /**
     * Position of the last played audio and video frame of the file being resumed.
     *
     * @var array{audio?: list{int, int}, video?: list{int, int}}
     */
    private array $resumePlayed = [];
    /**
     * The files read before the current one, oldest first, while some of their frames may still be
     * queued, to resume them from the first one that was not played: see {@see self::fileState()}.
     *
     * @var list<array<string, mixed>>
     */
    private array $previous = [];
    /** The Matroska track list, needed to resume a WebM file past its (once-only) header. */
    private array $resumeTracks = [];
    /** The Matroska timestamp scale, needed to resume a WebM file. */
    private int $resumeTimestampScale = 0;

    /** Highest audio/video timestamp buffered so far, in milliseconds, for back-pressure. */
    private int $bufferedUntilMs = 0;
    /** How far playback has progressed, in milliseconds, reported by the tracks. */
    private int $playbackMs = 0;
    /** Whether the current file's demuxer has been fully read. */
    private bool $finishedCurrent = true;

    /* -------------------------------------------------------------------- *
     *  Video codec negotiation.
     * -------------------------------------------------------------------- */

    private ?VideoCodecObserver $videoObserver = null;
    private ?string $videoCodec = null;
    /** SDP fmtp parameters (profile/level/etc.) of the current video file, derived from its bitstream. */
    private array $videoParameters = [];
    /** Whether {@see self::$videoParameters} is still a tentative value awaiting the first keyframe. */
    private bool $videoProfileFromFrame = false;
    private ?H264Framing $framing = null;
    /** The track number of the AAC audio of the current file, in RTMP mode. */
    private ?int $aacTrack = null;
    /** The AudioSpecificConfig of {@see self::$aacTrack}, for the ADTS headers of its frames. */
    private ?string $aacConfig = null;
    /** Whether a video codec was announced and still owes the observer a stop notification. */
    private bool $webmVideoAnnounced = false;

    /* -------------------------------------------------------------------- *
     *  Transient reader control (never serialized).
     * -------------------------------------------------------------------- */

    private bool $readerRunning = false;
    /** Bumped to make the running reader abandon the current file (skip/stop/discard), and for every file. */
    private int $generation = 0;
    private ?DeferredCancellation $readerCancel = null;

    /**
     * @param bool $videoOnly When true, the audio of played files is dropped and only their video is
     *                        queued â€” used for a presentation/screencast stream, which carries no audio.
     * @param bool $rtmp      When true, the files are played for an RTMP livestream (see
     *                        {@see \danog\MadelineProto\GroupCall\Rtmp\RtmpPublisher}): only H.264 and AAC,
     *                        whose frames are queued with an ADTS header, in MP4 or Matroska files.
     */
    public function __construct(CallControllerInterface $instance, private bool $videoOnly = false, private bool $rtmp = false)
    {
        parent::__construct($instance);
        $this->oggQueue = new SplQueue;
        $this->webmAudioQueue = new SplQueue;
        $this->videoQueue = new SplQueue;
    }

    /**
     * Register the observer notified when the outgoing video codec changes or video playback stops.
     *
     * Set by the WebRTC engine ({@see \danog\MadelineProto\Tgcalls\Controller} or
     * {@see \danog\MadelineProto\Tgcalls\GroupConnection}) once it exists, since this loop is created
     * before it.
     *
     * @psalm-external-mutation-free
     */
    public function setVideoCodecObserver(VideoCodecObserver $observer): void
    {
        $this->videoObserver = $observer;
        // The reader is started by play() and may open the file and detect its video codec before
        // the connection â€” and hence this observer â€” exists. When that happens the onVideoCodec
        // announcement in selectTracks() is dropped (the observer was still null), the codec is
        // never preferred, and the peer decodes our pre-encoded frames as whatever codec sits first
        // in the default table (VP8). Replay the announcement here so the codec is preferred in the
        // very first offer. It is queued so it runs after the connection has finished constructing
        // its transceivers, and before the initial offer it queues itself.
        if ($this->videoCodec !== null) {
            $codec = $this->videoCodec;
            $parameters = $this->videoParameters;
            EventLoop::queue(static fn () => $observer->onVideoCodec($codec, $parameters));
        }
    }

    /**
     * The state needed to resume the current file.
     *
     * @return array<string, mixed>
     */
    private function fileState(): array
    {
        return [
            'generation' => $this->generation,
            'file' => $this->currentFile,
            'desc' => $this->currentDesc,
            'kind' => $this->currentKind,
            'hold' => $this->playingHold,
            'resumeOffset' => $this->resumeOffset,
            'resumeSkip' => $this->resumeSkip,
            'resumeUnitOffset' => $this->resumeUnitOffset,
            'resumeTracks' => $this->resumeTracks,
            'resumeTimestampScale' => $this->resumeTimestampScale,
            'videoCodec' => $this->videoCodec,
            'videoParameters' => $this->videoParameters,
            'videoProfileFromFrame' => $this->videoProfileFromFrame,
            'framing' => $this->framing,
            'aacTrack' => $this->aacTrack,
            'aacConfig' => $this->aacConfig,
        ];
    }

    public function __serialize(): array
    {
        // The queued frames are not saved: a seekable, non-converted file is resumed from the first of its
        // frames that was not played. A raw stream or an on-the-fly conversion replays its item from the
        // start (files) or is dropped (streams).
        $inputFiles = $this->inputFiles;
        $holdIndex = $this->holdIndex;
        $state = null;
        $position = null;
        foreach ($this->previous as $k => $previous) {
            if (($position = $this->firstUnplayedPosition($previous['generation'])) !== null) {
                // The end of a previous file was not played yet: resume it, followed by the files after
                // it, whose frames come after its own.
                $state = $previous;
                $next = array_column(\array_slice($this->previous, $k + 1), 'file');
                if ($this->currentFile !== null) {
                    if ($this->playingHold) {
                        $holdIndex--;
                    } else {
                        $next[] = $this->currentFile;
                    }
                }
                array_unshift($inputFiles, ...$next);
                break;
            }
        }
        if ($state === null && $this->currentFile !== null && !$this->finishedCurrent) {
            if ($this->currentResumable) {
                $state = $this->fileState();
                $position = $this->firstUnplayedPosition($this->generation);
            } elseif (!$this->currentFile instanceof ReadableStream) {
                array_unshift($inputFiles, $this->currentFile);
            }
        }
        // Without a position, every frame read so far was played: resume right after the last one.
        if ($state !== null && $position !== null) {
            [$state['resumeOffset'], $state['resumeSkip']] = $position;
            $state['resumeUnitOffset'] = $position[0];
        }
        return [
            'instance' => $this->instance,
            'pause' => $this->pause,
            'inputFiles' => array_values(array_filter($inputFiles, static fn ($v) => !$v instanceof ReadableStream)),
            'holdFiles' => array_values(array_filter($this->holdFiles, static fn ($v) => !$v instanceof ReadableStream)),
            'holdIndex' => $holdIndex,
            'playingHold' => $state['hold'] ?? false,
            'fixedOggGrid' => $this->fixedOggGrid,
            'currentFile' => $state['file'] ?? null,
            'currentDesc' => $state['desc'] ?? null,
            'currentKind' => $state['kind'] ?? null,
            'currentResumable' => $state !== null,
            'finishedCurrent' => $state === null,
            'resumeOffset' => $state['resumeOffset'] ?? 0,
            'resumeSkip' => $state['resumeSkip'] ?? 0,
            'resumeUnitOffset' => $state['resumeUnitOffset'] ?? -1,
            'resumePlayed' => $state !== null ? $this->playedPositions[$state['generation']] ?? [] : [],
            'resumeTracks' => $state['resumeTracks'] ?? [],
            'resumeTimestampScale' => $state['resumeTimestampScale'] ?? 0,
            // Nothing is buffered once resumed: playback restarts from the first frame read again.
            'bufferedUntilMs' => 0,
            'playbackMs' => 0,
            'videoObserver' => $this->videoObserver,
            'videoCodec' => $state !== null ? $state['videoCodec'] : $this->videoCodec,
            'videoParameters' => $state !== null ? $state['videoParameters'] : $this->videoParameters,
            'videoProfileFromFrame' => $state !== null ? $state['videoProfileFromFrame'] : $this->videoProfileFromFrame,
            'videoOnly' => $this->videoOnly,
            'framing' => $state !== null ? $state['framing'] : $this->framing,
            'aacTrack' => $state !== null ? $state['aacTrack'] : $this->aacTrack,
            'aacConfig' => $state !== null ? $state['aacConfig'] : $this->aacConfig,
            'rtmp' => $this->rtmp,
            'webmVideoAnnounced' => $this->webmVideoAnnounced,
            // Kept (not reset) so a file opened after a restart never reuses the ID of one whose
            // frames the tracks still remember: see the `file` of the queued frames.
            'generation' => $this->generation,
        ];
    }

    public function __unserialize(array $data): void
    {
        $this->restoreDeclaredProperties($data);
        // Older versions saved the queued frames: they are dropped (along with at most a couple of seconds of playback).
        $this->oggQueue = new SplQueue;
        $this->oggQueueSamples = 0;
        $this->webmAudioQueue = new SplQueue;
        $this->videoQueue = new SplQueue;
        // Not saved by older versions.
        $this->videoOnly ??= false;
        $this->rtmp ??= false;
        // Do NOT restart the reader here: its loop reads the call's state, which is only restored once
        // the whole call graph has finished deserializing. Starting it now could run it
        // mid-deserialization (if a nested resume suspends the fiber) and dereference not-yet-restored
        // state. The call's deserializer calls {@see self::resume()} once the graph is whole.
        $this->readerRunning = false;
    }

    /**
     * Restart the demuxer/reader task after a serialize/unserialize cycle, resuming the current file
     * (from its byte offset) or the playlist where it left off. Called by the call's deserializer once
     * the whole call graph is restored; idempotent via the readerRunning guard. (Named to avoid the
     * base Loop::resume().).
     */
    public function resumeReader(): void
    {
        $this->startReader();
    }

    public function discard(): void
    {
        $this->generation++;
        $this->readerCancel?->cancel();
        $this->oggQueue = new SplQueue;
        $this->oggQueueSamples = 0;
        $this->webmAudioQueue = new SplQueue;
        $this->videoQueue = new SplQueue;
        $this->previous = [];
        $this->packetDeferred?->complete(false);
        $this->packetDeferred = null;
    }

    #[\Override]
    protected function loop(): ?float
    {
        // The playlist is driven by a dedicated reader task (see startReader()); this loop only
        // needs to exist for the base class and to stop with the call.
        if ($this->instance->isCallEnded()) {
            return self::STOP;
        }
        return self::PAUSE;
    }

    /* ==================================================================== *
     *  Playlist API.
     * ==================================================================== */

    /**
     * Play a file, transmitting its audio and, if it carries a transmittable one, its video.
     */
    public function play(LocalFile|RemoteUrl|ReadableStream $file): void
    {
        $this->inputFiles[] = self::snapshot($file);
        if ($this->playingHold) {
            $this->playingHold = false;
            $this->skip();
        }
        $this->startReader();
    }

    /**
     * Skip to the next file in the playlist.
     */
    public function skip(): void
    {
        $this->generation++;
        $this->readerCancel?->cancel();
        $this->finishedCurrent = true;
        $this->startReader();
    }

    /**
     * Stop playing all files, clear the main and the hold playlist, and stop any video.
     */
    public function stopPlaying(): void
    {
        $this->inputFiles = [];
        $this->holdFiles = [];
        $this->playingHold = false;
        $this->previous = [];
        $this->skip();
        $this->announceVideoStopped();
    }

    /**
     * @psalm-external-mutation-free
     */
    public function pausePlaying(): void
    {
        $this->pause = true;
    }

    /**
     * @psalm-external-mutation-free
     */
    public function resumePlaying(): void
    {
        $this->pause = false;
    }

    /**
     * Whether playback is paused.
     *
     * @psalm-mutation-free
     */
    public function isAudioPaused(): bool
    {
        return $this->pause;
    }

    /**
     * Get info about what is currently being played: a description string for a stream, otherwise
     * the related {@see LocalFile} or {@see RemoteUrl}.
     *
     * @psalm-mutation-free
     */
    public function getCurrent(): LocalFile|RemoteUrl|string|null
    {
        return $this->currentDesc;
    }

    /**
     * Files to play on hold: they replace the hold file being played (if any) right away.
     */
    public function playOnHold(LocalFile|RemoteUrl|ReadableStream ...$files): void
    {
        $this->holdFiles = array_map(self::snapshot(...), $files);
        $this->holdIndex = 0;
        if ($this->playingHold) {
            $this->playingHold = false;
            $this->skip();
            return;
        }
        $this->startReader();
    }

    /* ==================================================================== *
     *  Track-facing API.
     * ==================================================================== */

    /**
     * Only play MadelineProto's own OGG OPUS files as-is, converting the others: for the legacy
     * libtgvoip engine, which can only send 60ms packets.
     */
    public function setFixedOggGrid(): void
    {
        $this->fixedOggGrid = true;
    }

    /**
     * Pull the next OPUS packet of an OGG file, waiting for one if the playlist is not exhausted.
     */
    public function pullPacket(): ?string
    {
        while (true) {
            $packet = $this->tryPullPacket();
            if ($packet !== null) {
                return $packet;
            }
            if ($this->instance->isCallEnded() || !$this->hasPendingOggWork()) {
                return null;
            }
            $this->packetDeferred ??= new DeferredFuture;
            if (!$this->packetDeferred->getFuture()->await()) {
                return null;
            }
        }
    }

    /**
     * Wait until a frame is queued, or for at most $timeout seconds: for a track whose queue ran dry.
     */
    public function awaitFrame(float $timeout): void
    {
        $this->packetDeferred ??= new DeferredFuture;
        $future = $this->packetDeferred->getFuture();
        $timeoutCancellation = new DeferredCancellation;
        $timer = EventLoop::delay($timeout, static fn () => $timeoutCancellation->cancel());
        try {
            $future->await($timeoutCancellation->getCancellation());
        } catch (CancelledException) {
        } finally {
            EventLoop::cancel($timer);
        }
    }

    /**
     * Pull the next OPUS packet of an OGG file if one is immediately available.
     *
     * Used by the WebRTC playback track, which is polled from a callback that must never suspend.
     */
    public function tryPullPacket(): ?string
    {
        if ($this->pause || $this->oggQueue->isEmpty()) {
            return null;
        }
        /** @var array{data: string, position: ?list{int, int}, file: int} $packet */
        $packet = $this->oggQueue->dequeue();
        $this->oggQueueSamples -= Ogg::packetDuration($packet['data']);
        $this->releaseReader();
        return $packet['data'];
    }

    public function hasWebmAudio(): bool
    {
        return !$this->webmAudioQueue->isEmpty();
    }

    /**
     * Pull the next OPUS frame of the WebM file being played, or null if none is buffered.
     *
     * @return array{data: string, timestamp: int, file: int}|null
     */
    public function pullWebmAudio(): ?array
    {
        if ($this->webmAudioQueue->isEmpty()) {
            return null;
        }
        /** @var array{data: string, timestamp: int, position: ?list{int, int}, file: int} $frame */
        $frame = $this->webmAudioQueue->dequeue();
        $this->markPlayed('audio', $frame);
        $this->setPlaybackPosition((int) ($frame['timestamp'] * 1000 / self::AUDIO_CLOCK_RATE), $frame['file']);
        $this->releaseReader();
        return $frame;
    }

    /**
     * @return array{data: string, timestamp: int, keyframe: bool, file: int}|null
     */
    public function pullVideo(): ?array
    {
        if ($this->videoQueue->isEmpty()) {
            return null;
        }
        /** @var array{data: string, timestamp: int, keyframe: bool, position: ?list{int, int}, file: int} $frame */
        $frame = $this->videoQueue->dequeue();
        $this->markPlayed('video', $frame);
        $this->releaseReader();
        return $frame;
    }

    public function hasVideo(): bool
    {
        return !$this->videoQueue->isEmpty();
    }

    /**
     * Record that a frame of the current file was played.
     *
     * @param 'audio'|'video' $kind
     * @param array{position: ?list{int, int}, file: int, ...} $frame
     */
    private function markPlayed(string $kind, array $frame): void
    {
        if ($frame['position'] !== null) {
            $this->playedPositions[$frame['file']][$kind] = $frame['position'];
        }
    }

    /**
     * The position of the first queued frame of a file, i.e. the first one that was not played yet.
     *
     * @param int $generation The {@see self::$generation} of the file.
     *
     * @return ?list{int, int}
     */
    private function firstUnplayedPosition(int $generation): ?array
    {
        $first = null;
        foreach ([$this->oggQueue, $this->webmAudioQueue, $this->videoQueue] as $queue) {
            foreach ($queue as $frame) {
                if ($frame['position'] !== null && $frame['file'] === $generation) {
                    if ($first === null || $frame['position'] < $first) {
                        $first = $frame['position'];
                    }
                    // The rest of the queue comes after it.
                    break;
                }
            }
        }
        return $first;
    }

    /**
     * Whether a WebM file is currently being played, even if a queue momentarily ran dry because the
     * reader is behind.
     */
    public function isWebmPlaying(): bool
    {
        return $this->playsFrames() && !$this->isExhausted();
    }

    /**
     * Whether the WebM file was fully read and both of its queues are drained.
     *
     * Firing the observer's stop notification exactly once here keeps the video-stop plumbing in the
     * one place that knows a file has truly finished. Not when another file follows, though: the
     * stop would make the other participants drop our video, only to pick it up again (or not) a
     * moment later; the next file announces its own video, or the lack of it, when it starts.
     */
    public function isExhausted(): bool
    {
        $exhausted = !$this->playsFrames()
            || ($this->finishedCurrent && $this->webmAudioQueue->isEmpty() && $this->videoQueue->isEmpty());
        if ($exhausted && $this->webmVideoAnnounced && $this->inputFiles === [] && $this->holdFiles === []) {
            $this->announceVideoStopped();
        }
        return $exhausted;
    }

    /**
     * Report how far playback has progressed, in milliseconds.
     *
     * @param ?int $file The `file` of the frame that was played: the playback of the frames of a
     *                   previous file doesn't count for the current one.
     */
    public function setPlaybackPosition(int $milliseconds, ?int $file = null): void
    {
        if ($file === null || $file === $this->generation) {
            $this->playbackMs = max($this->playbackMs, $milliseconds);
        }
        $this->releaseReader();
    }

    /**
     * The SDP encoding name of the video we are currently transmitting, if any.
     */
    public function getVideoCodec(): ?string
    {
        return $this->videoCodec;
    }

    /**
     * The SDP fmtp parameters (profile/level/tier/â€¦) of the current video, derived from its bitstream.
     *
     * @return array<string, string>
     */
    public function getVideoParameters(): array
    {
        return $this->videoParameters;
    }

    /* ==================================================================== *
     *  Reader: streams the current file into the bounded queues.
     * ==================================================================== */

    private function startReader(): void
    {
        if ($this->readerRunning) {
            return;
        }
        $this->readerRunning = true;
        // The call-ended check lives in the (deferred) reader loop, not here: startReader() can run
        // from __unserialize before PrivateCallController has restored its state, so it must not touch it.
        EventLoop::queue($this->readerLoop(...));
    }

    private function readerLoop(): void
    {
        try {
            while (!$this->instance->isCallEnded()) {
                if ($this->currentFile === null && !$this->openNextFile()) {
                    if ($this->holdFiles === []) {
                        // Nothing to play: exit. play()/playOnHold()/skip() restart the reader.
                        return;
                    }
                    // Hold files only start once the queued frames of the last file have played out
                    // (openNextFile() refuses to open one on top of them): wait for the tracks to drain
                    // the queues and try again, rather than exiting and never playing the hold files.
                    Tools::sleep(0.1);
                    continue;
                }
                $generation = $this->generation + 1;
                $played = $this->streamCurrentFile();
                // Keep the files while their frames are queued, to resume them if the session is saved before they are played.
                $this->previous = array_values(array_filter(
                    $this->previous,
                    fn (array $previous): bool => $this->firstUnplayedPosition($previous['generation']) !== null,
                ));
                if ($this->currentResumable && $this->generation === $generation) {
                    $this->previous[] = $this->fileState();
                }
                // Only the played positions of those files are still needed.
                $this->playedPositions = array_intersect_key($this->playedPositions, array_flip(array_column($this->previous, 'generation')));
                $this->currentFile = null;
                $this->currentKind = null;
                $this->currentDesc = null;
                if (!$played && $this->playingHold) {
                    // Hold files are played in a loop: don't retry one that keeps failing in a tight loop.
                    Tools::sleep(1);
                }
            }
        } catch (Throwable $e) {
            $this->instance->log("DJ reader stopped in {$this}: $e", Logger::ERROR);
        } finally {
            $this->readerRunning = false;
        }
    }

    /**
     * Pick the next file to play from the playlist, or a hold file when the playlist is drained.
     */
    private function openNextFile(): bool
    {
        if ($this->inputFiles) {
            $this->setCurrent(array_shift($this->inputFiles), resuming: false);
            return true;
        }
        if ($this->holdFiles !== [] && $this->oggQueue->isEmpty() && $this->webmAudioQueue->isEmpty() && $this->videoQueue->isEmpty()) {
            $this->playingHold = true;
            $this->setCurrent($this->holdFiles[($this->holdIndex++) % \count($this->holdFiles)], resuming: false);
            return true;
        }
        return false;
    }

    /**
     * @psalm-external-mutation-free
     */
    private function setCurrent(LocalFile|RemoteUrl|ReadableStream|StreamSnapshot $file, bool $resuming): void
    {
        $this->currentFile = $file;
        $this->currentDesc = $file instanceof ReadableStream || $file instanceof StreamSnapshot ? 'stream '.spl_object_id($file) : $file;
        $this->finishedCurrent = false;
        if (!$resuming) {
            // Until the new file is probed, it can't be resumed.
            $this->currentResumable = false;
            $this->resumeOffset = 0;
            $this->resumeSkip = 0;
            $this->resumeUnitOffset = -1;
            $this->resumeTracks = [];
            $this->resumeTimestampScale = 0;
            $this->resumePlayed = [];
            $this->bufferedUntilMs = 0;
            $this->playbackMs = 0;
            $this->currentKind = null;
        }
    }

    /**
     * @return bool Whether the file could be played (even if it was skipped).
     */
    private function streamCurrentFile(): bool
    {
        $generation = ++$this->generation;
        $this->readerCancel = new DeferredCancellation;
        $cancellation = $this->readerCancel->getCancellation();
        $item = $this->currentFile;
        \assert($item !== null);
        $resuming = $this->currentKind !== null; // set only when resuming from __unserialize
        $file = null;
        try {
            // A snapshotted resumable stream is reopened from the start of the item on every play.
            $file = $item instanceof StreamSnapshot ? $item->open() : $item;
            [$kind, $source, $convert] = $this->prepareSource($file, $cancellation, $resuming);
            $this->currentKind = $kind;
            if ($this->rtmp && $kind === 'ogg') {
                throw new AssertionError(
                    'An RTMP livestream can only play H.264 video and AAC audio, in an MP4 or Matroska file: '
                    .$this->describe($item).' is not one, please convert it, e.g. with ffmpeg -i input -c:v libx264 -c:a aac -movflags +faststart output.mp4'
                );
            }
            if ($kind === 'ogg') {
                // An OGG file has no video: stop the video a previous file left announced.
                $this->announceVideoStopped();
            }
            // A resumable stream can be reopened and skipped forward, like a file; MP4 files are only
            // played from their start.
            $this->currentResumable = $kind !== 'mp4' && !$convert && (!$source instanceof ReadableStream || $item instanceof StreamSnapshot);
            if ($kind === 'matroska') {
                $this->streamMatroska($source, $file, $generation, $cancellation, $resuming);
            } elseif ($kind === 'mp4') {
                $this->streamMp4($source, $generation, $cancellation);
            } else {
                $this->streamOgg($source, $file, $convert, $generation, $cancellation, $resuming);
            }
        } catch (CancelledException) {
            // Skipped or discarded: the next file (if any) starts on the next loop turn.
        } catch (Throwable $e) {
            $this->instance->log("Could not play {$this->describe($item)} in {$this}: $e", Logger::ERROR);
            return false;
        } finally {
            if ($item instanceof StreamSnapshot && $file instanceof ReadableStream) {
                // Stop the download, if the stream was skipped before its end.
                $file->close();
            }
            if ($generation === $this->generation) {
                $this->finishedCurrent = true;
                $this->wakePacketWaiters();
            }
        }
        return true;
    }

    /**
     * Decide which demuxer a file needs and hand back a source to read it from.
     *
     * A seekable file is probed and re-opened; a raw stream is peeked at (its first chunk is put
     * back) so it can be classified without a rewind it does not support.
     *
     * @return array{string, LocalFile|RemoteUrl|ReadableStream, bool} kind, source, needsConversion
     */
    private function prepareSource(LocalFile|RemoteUrl|ReadableStream $file, ?Cancellation $cancellation, bool $resuming): array
    {
        if ($resuming) {
            return [(string) $this->currentKind, $file, false];
        }
        if ($file instanceof ReadableStream) {
            $first = $file->read($cancellation) ?? '';
            $source = new ReadableIterableStream((static function () use ($first, $file, $cancellation) {
                if ($first !== '') {
                    yield $first;
                }
                while (($chunk = $file->read($cancellation)) !== null) {
                    yield $chunk;
                }
            })());
            foreach (self::MATROSKA_MAGICS as $magic) {
                if (str_starts_with($first, $magic)) {
                    return ['matroska', $source, false];
                }
            }
            // The first box of an MP4 file is its ftyp (or styp, or straight its moov).
            if (\in_array(substr($first, 4, 4), ['ftyp', 'styp', 'moov'], true)) {
                return ['mp4', $source, false];
            }
            if (str_starts_with($first, self::OGG_MAGIC)) {
                // A stream can't be probed for MadelineProto's own encoding.
                return ['ogg', $source, $this->fixedOggGrid];
            }
            return ['ogg', $source, true];
        }
        try {
            $probe = new Matroska($file, $cancellation);
            if ($probe->tracks !== []) {
                return ['matroska', $file, false];
            }
        } catch (CancelledException $e) {
            throw $e;
        } catch (Throwable) {
            // Not a Matroska container.
        }
        try {
            $probe = new Mp4($file, $cancellation);
            if ($probe->tracks !== []) {
                return ['mp4', $file, false];
            }
        } catch (CancelledException $e) {
            throw $e;
        } catch (Throwable $e) {
            if ($e->getMessage() === Mp4::MOOV_AT_END) {
                throw $e;
            }
            // Not an MP4 file.
        }
        try {
            $ogg = new Ogg($file, $cancellation);
            if (!$this->fixedOggGrid || \in_array('MADELINE_ENCODER_V=1', $ogg->comments, true)) {
                return ['ogg', $file, false];
            }
        } catch (CancelledException $e) {
            throw $e;
        } catch (Throwable) {
            // Not an OGG OPUS file.
        }
        return ['ogg', $file, true];
    }

    private function streamOgg(
        LocalFile|RemoteUrl|ReadableStream $source,
        LocalFile|RemoteUrl|ReadableStream $file,
        bool $convert,
        int $generation,
        ?Cancellation $cancellation,
        bool $resuming
    ): void {
        if ($resuming) {
            $ogg = new Ogg($file, $cancellation, $this->resumeOffset);
        } elseif ($convert) {
            if (!Tools::canConvertOgg()) {
                throw new AssertionError($this->fixedOggGrid
                    ? 'The passed file was not generated by MadelineProto or @libtgvoipbot, please pre-convert it using @libtgvoip bot or install FFI and ffmpeg to perform realtime conversion!'
                    : 'The passed file is not a WebM/Matroska or OGG OPUS file, please convert it (see https://docs.madelineproto.xyz/docs/CALLS.html#supported-formats) or install FFI and ffmpeg to perform realtime conversion!');
            }
            // Convert to OGG OPUS on the fly; the resulting pipe is not seekable, so it is not resumable.
            $pipe = new Pipe(4096);
            EventLoop::queue(static function () use ($source, $pipe, $cancellation): void {
                try {
                    Ogg::convert($source, $pipe->getSink(), $cancellation);
                } catch (CancelledException) {
                } finally {
                    EventLoop::queue($pipe->getSink()->close(...));
                }
            });
            $ogg = new Ogg($pipe->getSource());
        } else {
            $ogg = new Ogg($source, $cancellation);
        }
        $skip = $resuming ? $this->resumeSkip : 0;
        foreach ($ogg->opusPackets as $packet) {
            if ($generation !== $this->generation) {
                return;
            }
            $unit = $ogg->pageOffset;
            if ($skip > 0 && $unit === $this->resumeUnitOffset) {
                $skip--;
                continue;
            }
            $this->oggQueue->enqueue([
                'data' => $packet,
                'position' => $this->trackResume($unit),
                'file' => $generation,
            ]);
            $samples = Ogg::packetDuration($packet);
            $this->oggQueueSamples += $samples;
            $this->bufferedUntilMs += intdiv($samples, 48);
            $this->wakePacketWaiters();
            $this->throttle($generation, $cancellation);
        }
    }

    private function streamMatroska(
        LocalFile|RemoteUrl|ReadableStream $source,
        LocalFile|RemoteUrl|ReadableStream $file,
        int $generation,
        ?Cancellation $cancellation,
        bool $resuming
    ): void {
        if ($resuming) {
            $matroska = new Matroska($file, $cancellation, $this->resumeOffset, $this->resumeTracks, $this->resumeTimestampScale);
            if (($this->videoCodec === 'H264' || $this->videoCodec === 'H265') && $this->framing === null) {
                foreach ($this->resumeTracks as $track) {
                    if (($track['type'] ?? 0) === Matroska::TRACK_TYPE_VIDEO) {
                        $this->framing = new H264Framing($track['private'], $this->videoCodec === 'H265');
                        break;
                    }
                }
            }
        } else {
            $matroska = new Matroska($source, $cancellation);
            $this->resumeTracks = $matroska->tracks;
            $this->resumeTimestampScale = $matroska->getTimestampScale();
            $this->selectTracks($matroska);
        }
        $this->currentHasVideo = $this->videoCodec !== null;
        $this->currentHasAudio = false;
        if (!$this->videoOnly) {
            foreach ($matroska->tracks as $track) {
                if (isset($this->audioCodecs()[$track['codec']])) {
                    $this->currentHasAudio = true;
                }
            }
        }
        $skip = $resuming ? $this->resumeSkip : 0;
        foreach ($matroska->frames as $frame) {
            if ($generation !== $this->generation) {
                return;
            }
            $unit = $matroska->unitOffset;
            if ($skip > 0 && $unit === $this->resumeUnitOffset) {
                $skip--;
                continue;
            }
            // Dropped frames are counted too, as the skip above counts all frames of the unit.
            $position = $this->trackResume($unit);
            $played = $this->resumePlayed[isset($this->audioCodecs()[$frame['codec']]) ? 'audio' : 'video'] ?? null;
            if ($resuming && $position !== null && $played !== null && $position <= $played) {
                // Played before the restart, while frames of the other kind before it weren't.
                continue;
            }
            if ($this->pushFrame($frame, $position)) {
                $this->wakePacketWaiters();
                $this->throttle($generation, $cancellation);
            }
        }
    }

    /**
     * Read an MP4 file, from its start: unlike a Matroska one, it's not resumed from where it was.
     */
    private function streamMp4(LocalFile|RemoteUrl|ReadableStream $source, int $generation, ?Cancellation $cancellation): void
    {
        $mp4 = new Mp4($source, $cancellation);
        $this->resumeTracks = [];
        $this->selectTracks($mp4);
        $this->currentHasVideo = $this->videoCodec !== null;
        $this->currentHasAudio = false;
        if (!$this->videoOnly) {
            foreach ($mp4->tracks as $track) {
                if (isset($this->audioCodecs()[$track['codec']])) {
                    $this->currentHasAudio = true;
                }
            }
        }
        foreach ($mp4->frames as $frame) {
            if ($generation !== $this->generation) {
                return;
            }
            if ($this->pushFrame($frame, null)) {
                $this->wakePacketWaiters();
                $this->throttle($generation, $cancellation);
            }
        }
    }

    /**
     * Enqueue one demuxed WebM frame; returns whether it was kept (an untransmittable frame is not).
     *
     * @param array{track: int, codec: string, type: int, data: string, timestamp: int, keyframe: bool} $frame
     * @param ?list{int, int} $position
     */
    private function pushFrame(array $frame, ?array $position): bool
    {
        $timestampMs = $frame['timestamp'];
        $this->bufferedUntilMs = max($this->bufferedUntilMs, $timestampMs);
        if (isset($this->audioCodecs()[$frame['codec']])) {
            // A presentation/screencast stream transmits video only; drop its audio.
            if ($this->videoOnly) {
                return false;
            }
            $data = $frame['data'];
            if ($this->rtmp) {
                // The audio track picked in selectTracks(), self-describing with an ADTS header.
                if ($frame['track'] !== $this->aacTrack || $this->aacConfig === null) {
                    return false;
                }
                $data = FlvTags::adtsHeader($this->aacConfig, \strlen($data)).$data;
            }
            $this->webmAudioQueue->enqueue([
                'data' => $data,
                'timestamp' => (int) ($timestampMs * self::AUDIO_CLOCK_RATE / 1000),
                'position' => $position,
                'file' => $this->generation,
            ]);
            return true;
        }
        if ($this->videoCodec === null || ($this->videoCodecs()[$frame['codec']] ?? null) !== $this->videoCodec) {
            return false;
        }
        // Confirm the VP9 profile from the first real keyframe (no configuration record was stored);
        // re-announce only if it differs from the tentative profile 0 advertised in selectTracks().
        if ($this->videoProfileFromFrame && $frame['keyframe']) {
            $this->videoProfileFromFrame = false;
            $parameters = Codec::fmtpFromBitstream('video/'.$this->videoCodec, '', $frame['data']);
            if ($parameters !== [] && $parameters !== $this->videoParameters) {
                $this->videoParameters = $parameters;
                $this->videoObserver?->onVideoCodec($this->videoCodec, $parameters);
            }
        }
        $this->videoQueue->enqueue([
            'data' => $this->framing?->convert($frame['data'], $frame['keyframe']) ?? $frame['data'],
            'timestamp' => (int) ($timestampMs * self::VIDEO_CLOCK_RATE / 1000),
            'keyframe' => $frame['keyframe'],
            'position' => $position,
            'file' => $this->generation,
        ]);
        return true;
    }

    /**
     * Record the resume point right after the frame that was just produced.
     *
     * @return ?list{int, int} The position of the frame: the byte offset of its unit (OGG page / MKV
     *                         cluster) and how many frames of the unit come before it, i.e. the resume
     *                         point right before it. Null if the file can't be resumed.
     *
     * @psalm-external-mutation-free
     */
    private function trackResume(int $unit): ?array
    {
        if (!$this->currentResumable) {
            return null;
        }
        if ($unit !== $this->resumeUnitOffset) {
            $this->resumeUnitOffset = $unit;
            $this->resumeSkip = 0;
        }
        $position = [$unit, $this->resumeSkip];
        $this->resumeSkip++;
        $this->resumeOffset = $unit;
        return $position;
    }

    /**
     * Block the reader while the look-ahead buffer is full, so memory stays bounded.
     *
     * Never while a track has nothing to play, though: the next frame of every track must be read
     * before it's due, whatever the look-ahead. Unless the file is badly interleaved (or one of its
     * tracks ended early): then up to {@see self::MAX_INTERLEAVING_MS} more are read.
     */
    private function throttle(int $generation, ?Cancellation $cancellation): void
    {
        $lookahead = $this->instance->getVoIPSettings()->getPlaybackLookahead();
        while (($ahead = $this->bufferedAheadMs()) > $lookahead
            && ($ahead > $lookahead + self::MAX_INTERLEAVING_MS || !$this->isStarving())
        ) {
            if ($generation !== $this->generation || $this->instance->isCallEnded()) {
                return;
            }
            $this->drainDeferred ??= new DeferredFuture;
            try {
                $this->drainDeferred->getFuture()->await($cancellation);
            } catch (CancelledException) {
                return;
            }
        }
    }

    /**
     * Whether a track has no frame queued, while the current file has frames for it.
     */
    private function isStarving(): bool
    {
        if ($this->playsFrames()) {
            return ($this->currentHasAudio && $this->webmAudioQueue->isEmpty())
                || ($this->currentHasVideo && $this->videoQueue->isEmpty());
        }
        return $this->oggQueue->isEmpty();
    }

    private function bufferedAheadMs(): int
    {
        if ($this->playsFrames()) {
            return $this->bufferedUntilMs - $this->playbackMs;
        }
        return intdiv($this->oggQueueSamples, 48);
    }

    /** Release the reader from back-pressure once a track has drained a queue. */
    private function releaseReader(): void
    {
        if ($this->drainDeferred !== null) {
            $deferred = $this->drainDeferred;
            $this->drainDeferred = null;
            $deferred->complete();
        }
    }

    /** Wake anything blocked in {@see self::pullPacket()}. */
    private function wakePacketWaiters(): void
    {
        if ($this->packetDeferred !== null) {
            $deferred = $this->packetDeferred;
            $this->packetDeferred = null;
            $deferred->complete(true);
        }
    }

    private function hasPendingOggWork(): bool
    {
        return !$this->oggQueue->isEmpty()
            || (!$this->playsFrames() && !$this->finishedCurrent)
            || $this->inputFiles !== []
            || (!$this->playingHold && $this->holdFiles !== []);
    }

    private function announceVideoStopped(): void
    {
        if ($this->webmVideoAnnounced) {
            $this->webmVideoAnnounced = false;
            $this->videoObserver?->onVideoStopped();
        }
    }

    /**
     * Work out what of a WebM file we can transmit, pin the video codec and warn about the rest.
     */
    private function selectTracks(Matroska|Mp4 $matroska): void
    {
        $this->videoCodec = null;
        $this->videoParameters = [];
        $this->videoProfileFromFrame = false;
        $this->framing = null;
        $this->aacTrack = null;
        $this->aacConfig = null;
        if ($this->rtmp) {
            $this->selectRtmpAudio($matroska);
        }
        foreach ($matroska->tracks as $track) {
            if ($this->videoCodec !== null || !isset($this->videoCodecs()[$track['codec']])) {
                continue;
            }
            $this->videoCodec = $this->videoCodecs()[$track['codec']];
            $private = $track['private'] ?? '';
            if ($this->videoCodec === 'H264' || $this->videoCodec === 'H265') {
                $this->framing = new H264Framing($private, $this->videoCodec === 'H265');
            }
            // Advertise the file's real profile/level/tier instead of the generic fallback.
            $this->videoParameters = Codec::fmtpFromBitstream('video/'.$this->videoCodec, $private);
            // VP9 in WebM usually carries no configuration record; its profile is in every frame.
            // Advertise the common profile 0 for now and confirm it from the first keyframe.
            if ($this->videoParameters === [] && $this->videoCodec === 'VP9') {
                $this->videoParameters = ['profile-id' => '0'];
                $this->videoProfileFromFrame = true;
            }
        }
        $this->warnAboutDroppedTracks($matroska, Matroska::TRACK_TYPE_VIDEO, $this->videoCodecs());
        $this->warnAboutDroppedTracks($matroska, Matroska::TRACK_TYPE_AUDIO, $this->audioCodecs());
        if ($this->videoCodec !== null) {
            // The transport has to be renegotiated before the first frame goes out, or the peer
            // would decode it as whatever codec the previous file used.
            $this->webmVideoAnnounced = true;
            $this->videoObserver?->onVideoCodec($this->videoCodec, $this->videoParameters);
        } else {
            // No video in this file: stop the video a previous file left announced.
            $this->announceVideoStopped();
        }
    }

    /**
     * Pick the AAC track of a file to play for an RTMP livestream, refusing files with tracks it can't carry.
     *
     * @throws AssertionError If the file has audio or video an RTMP livestream can't carry, or neither.
     */
    private function selectRtmpAudio(Matroska|Mp4 $demuxer): void
    {
        $unsupported = [];
        $hasVideo = false;
        $hasH264 = false;
        $hasAudio = false;
        foreach ($demuxer->tracks as $number => $track) {
            if ($track['type'] === Matroska::TRACK_TYPE_VIDEO) {
                $hasVideo = true;
                if (isset(self::RTMP_VIDEO_CODECS[$track['codec']])) {
                    $hasH264 = true;
                } else {
                    $unsupported[] = $track['codec'];
                }
            } elseif ($track['type'] === Matroska::TRACK_TYPE_AUDIO) {
                $hasAudio = true;
                if (!isset(self::RTMP_AUDIO_CODECS[$track['codec']])) {
                    $unsupported[] = $track['codec'];
                } elseif ($this->aacTrack === null) {
                    if (($track['private'] ?? '') === '') {
                        throw new AssertionError("The AAC audio of the file played in $this has no AudioSpecificConfig (CodecPrivate), please remux it, e.g. with ffmpeg -i input -c copy output.mp4");
                    }
                    // Fails right away if ADTS can't carry its configuration.
                    FlvTags::adtsHeader($track['private'], 0);
                    $this->aacTrack = $number;
                    $this->aacConfig = $track['private'];
                }
            }
        }
        // Other tracks next to the ones we can play are only dropped, see warnAboutDroppedTracks().
        if (($hasVideo && !$hasH264) || ($hasAudio && $this->aacTrack === null) || (!$hasH264 && $this->aacTrack === null)) {
            throw new AssertionError(
                "An RTMP livestream can only play H.264 video and AAC audio, but the file played in $this has "
                .($unsupported !== [] ? implode(', ', $unsupported) : 'neither')
                .', please convert it, e.g. with ffmpeg -i input -c:v libx264 -c:a aac -movflags +faststart output.mp4'
            );
        }
    }

    /**
     * Whether the current file is a container of audio and video frames (rather than an OGG file).
     *
     * @psalm-mutation-free
     */
    private function playsFrames(): bool
    {
        return $this->currentKind === 'matroska' || $this->currentKind === 'mp4';
    }

    /**
     * The video codecs that can be played, as `Matroska CodecID => SDP encoding name`.
     *
     * @return array<string, string>
     *
     * @psalm-mutation-free
     */
    private function videoCodecs(): array
    {
        return $this->rtmp ? self::RTMP_VIDEO_CODECS : self::VIDEO_CODECS;
    }

    /**
     * The audio codecs that can be played, as `Matroska CodecID => SDP encoding name`.
     *
     * @return array<string, string>
     *
     * @psalm-mutation-free
     */
    private function audioCodecs(): array
    {
        return $this->rtmp ? self::RTMP_AUDIO_CODECS : self::AUDIO_CODECS;
    }

    /**
     * @param array<string, string> $supported
     */
    private function warnAboutDroppedTracks(Matroska|Mp4 $matroska, int $kind, array $supported): void
    {
        $found = [];
        foreach ($matroska->tracks as $track) {
            if ($track['type'] === $kind && !isset($supported[$track['codec']])) {
                $found[] = $track['codec'];
            } elseif ($track['type'] === $kind) {
                return;
            }
        }
        if ($found === []) {
            return;
        }
        $name = $kind === Matroska::TRACK_TYPE_VIDEO ? 'video' : 'audio';
        $this->instance->log(
            "The $name of the file played in {$this} will not be transmitted: it is ".
            implode(', ', $found).', and frames are fed to RTP exactly as the container stores them, '.
            'so a Telegram call can only carry '.implode(', ', array_keys($supported)).'.',
            Logger::WARNING
        );
    }

    /**
     * Resumable streams are snapshotted as soon as they are queued, so they survive a restart and can
     * be replayed (hold files loop); other streams can only be read once, and don't survive a restart.
     */
    private static function snapshot(LocalFile|RemoteUrl|ReadableStream $file): LocalFile|RemoteUrl|ReadableStream|StreamSnapshot
    {
        return $file instanceof ReadableStream && $file instanceof ResumableStream ? new StreamSnapshot($file) : $file;
    }

    /**
     * @psalm-pure
     */
    private function describe(LocalFile|RemoteUrl|ReadableStream|StreamSnapshot $file): string
    {
        return match (true) {
            $file instanceof LocalFile => $file->file,
            $file instanceof RemoteUrl => $file->url,
            default => 'stream '.spl_object_id($file),
        };
    }

    /**
     * @psalm-mutation-free
     */
    public function __toString(): string
    {
        return "DJ loop {$this->instance}";
    }
}
<?php

declare(strict_types=1);

/**
 * Update feeder loop.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Loop\Secret;

use danog\Loop\Loop;
use danog\MadelineProto\API;
use danog\MadelineProto\Loop\InternalLoop;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\Reactive\SimpleSubscriber;
use danog\MadelineProto\SecretChats\SecretChatController;
use danog\MadelineProto\SecurityException;

/**
 * Secret feed loop.
 *
 * @internal
 *
 * @author Daniil Gentili <daniil@daniil.it>
 *
 * @implements SimpleSubscriber<LoginState>
 */
final class SecretFeedLoop extends Loop implements SimpleSubscriber
{
    use InternalLoop {
        __construct as private init;
    }
    /**
     * Incoming secret updates array.
     */
    private array $incomingUpdates = [];
    private bool $mustPause;
    /**
     * Constructor.
     *
     * @param MTProto $API      API instance
     */
    public function __construct(MTProto $API, private readonly SecretChatController $secretChat)
    {
        $this->init($API);
        $API->loginState->subscribe($this);
    }
    /**
     * @psalm-pure
     */
    public function __sleep()
    {
        return ['API', 'secretChat', 'incomingUpdates'];
    }
    public function __wakeup(): void
    {
        if (!isset($this->API->logger)) {
            $this->API->setupLogger();
        }
        $this->init($this->API);
    }

    #[\Override]
    public function onSimpleStateChange($state): void
    {
        $this->mustPause = $state->state !== API::LOGGED_IN;
        if (!$this->mustPause) {
            if ($this->isRunning()) {
                $this->resume(true);
            } else {
                $this->start();
            }
        }
    }

    /**
     * Main loop.
     */
    #[\Override]
    public function loop(): ?float
    {
        if ($this->mustPause) {
            return self::PAUSE;
        }
        $this->API->logger("Resumed {$this}");
        while ($this->incomingUpdates) {
            $updates = $this->incomingUpdates;
            $this->incomingUpdates = [];
            foreach ($updates as $update) {
                try {
                    $this->secretChat->handleEncryptedUpdate($update);
                } catch (SecurityException $e) {
                    $this->API->logger("Secret chat deleted, exiting $this...");
                    throw $e;
                }
            }
            $updates = null;
        }
        return self::PAUSE;
    }
    /**
     * Feed incoming update to loop.
     */
    public function feed(array $update): void
    {
        $this->incomingUpdates []= $update;
        $this->resume();
    }
    /**
     * @psalm-mutation-free
     */
    public function __toString(): string
    {
        return "secret chat feed loop {$this->secretChat->id}";
    }
}
<?php declare(strict_types=1);
/**
 * Lang module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link      https://docs.madelineproto.xyz MadelineProto documentation
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto;

/** @internal */
final class Lang
{
    public const PERCENTAGES =  [
        'ar' => 99,
        'ckb' => 99,
        'de' => 99,
        'en' => 100,
        'es' => 99,
        'fa' => 99,
        'fr' => 99,
        'he' => 99,
        'hi' => 99,
        'id' => 99,
        'it' => 99,
        'ja' => 99,
        'ko' => 99,
        'pl' => 99,
        'pt' => 99,
        'ru' => 99,
        'tr' => 99,
        'uk' => 99,
        'uz' => 99,
        'vi' => 99,
        'zh' => 99,
    ];

    public static int $currentPercentage = 100;

    public static array $lang =  [
        'ar' =>
         [
             '2fa_uncalled' => 'Ù„Ø³Øª Ù�ÙŠ Ø§Ù†ØªØ¸Ø§Ø± ÙƒÙ„Ù…Ø© Ø§Ù„Ù…Ø±ÙˆØ±! ÙŠÙ�Ø±Ø¬Ù‰ Ø§Ø³ØªØ¯Ø¹Ø§Ø¡ Ø§Ù„Ø¯Ø§Ù„ØªÙŠÙ† phoneLogin ÙˆcompletePhoneLogin Ø£ÙˆÙ„Ù‹Ø§!',
             'accepting_call' => 'Ø¬Ø§Ø±Ù� Ù‚Ø¨ÙˆÙ„ Ø§Ù„Ù…ÙƒØ§Ù„Ù…Ø© Ù…Ù† %s...',
             'account_banned' => '!!!!!!! ØªØ­Ø°ÙŠØ± !!!!!!!
Ø¹Ù„Ù‘Ù‚ Ù†Ø¸Ø§Ù… Ù…Ù†Ø¹ Ø§Ù„Ø¥ØºØ±Ø§Ù‚ (flood) Ù�ÙŠ Telegram Ù‡Ø°Ø§ Ø§Ù„Ø­Ø³Ø§Ø¨.
Ù„Ù„Ù…ØªØ§Ø¨Ø¹Ø©ØŒ ÙŠÙ„Ø²Ù… Ø§Ù„ØªØ­Ù‚Ù‚ Ø§Ù„ÙŠØ¯ÙˆÙŠ.
Ø£Ø±Ø³Ù„ Ø¨Ø±ÙŠØ¯Ù‹Ø§ Ø¥Ù„ÙƒØªØ±ÙˆÙ†ÙŠÙ‹Ø§ Ø¥Ù„Ù‰ recover@telegram.org ØªØ·Ù„Ø¨ Ù�ÙŠÙ‡ Ø±Ù�Ø¹ Ø§Ù„Ø­Ø¸Ø± Ø¹Ù† Ø±Ù‚Ù… Ø§Ù„Ù‡Ø§ØªÙ� %sØŒ ÙˆØµÙ�Ù� Ø¨Ø§Ø®ØªØµØ§Ø± Ù…Ø§ Ø³ØªÙ�Ø¹Ù„Ù‡ Ø¨Ù‡Ø°Ø§ Ø§Ù„Ø±Ù‚Ù….
Ø«Ù… Ø³Ø¬Ù‘Ù„ Ø§Ù„Ø¯Ø®ÙˆÙ„ Ù…Ø±Ø© Ø£Ø®Ø±Ù‰.
Ø¥Ø°Ø§ Ø­Ø°Ù�Øª Ù‡Ø°Ø§ Ø§Ù„Ø­Ø³Ø§Ø¨ Ø¹Ù…Ø¯Ù‹Ø§ØŒ Ù�ØªØ¬Ø§Ù‡Ù„ Ù‡Ø°Ù‡ Ø§Ù„Ø±Ø³Ø§Ù„Ø©.',
             'already_loggedIn' => 'Ù†Ø³Ø®Ø© MadelineProto Ù‡Ø°Ù‡ Ù…Ø³Ø¬Ù‘Ù„Ø© Ø§Ù„Ø¯Ø®ÙˆÙ„ Ø¨Ø§Ù„Ù�Ø¹Ù„!',
             'apiAppDescLabelWeb' => 'Ø§Ù„ÙˆØµÙ�',
             'apiAppInstructionsAuto0' => 'Ø£Ø¯Ø®Ù„ Ø§Ø³Ù… Ø§Ù„ØªØ·Ø¨ÙŠÙ‚ØŒ ÙŠÙ…ÙƒÙ† Ø£Ù† ÙŠÙƒÙˆÙ† Ø£ÙŠ Ø´ÙŠØ¡: ',
             'apiAppInstructionsAuto1' => 'Ø£Ø¯Ø®Ù„ Ø§Ù„Ø§Ø³Ù… Ø§Ù„Ù…Ø®ØªØµØ± Ù„Ù„ØªØ·Ø¨ÙŠÙ‚ØŒ Ø£Ø­Ø±Ù� ÙˆØ£Ø±Ù‚Ø§Ù…ØŒ Ù…Ù† 5 Ø¥Ù„Ù‰ 32 Ø­Ø±Ù�Ù‹Ø§: ',
             'apiAppInstructionsAuto2' => 'Ø£Ø¯Ø®Ù„ Ø¹Ù†ÙˆØ§Ù† URL Ù„Ù„ØªØ·Ø¨ÙŠÙ‚/Ø§Ù„Ù…ÙˆÙ‚Ø¹ØŒ Ø£Ùˆ t.me/yourusername: ',
             'apiAppInstructionsAuto3' => 'Ø£Ø¯Ø®Ù„ Ù…Ù†ØµØ© Ø§Ù„ØªØ·Ø¨ÙŠÙ‚: ',
             'apiAppInstructionsAuto4' => 'ØµÙ�Ù� ØªØ·Ø¨ÙŠÙ‚Ùƒ: ',
             'apiAppInstructionsAutoTypeOther' => 'Ø£Ø®Ø±Ù‰ (Ø­Ø¯Ù‘Ø¯Ù‡Ø§ Ù�ÙŠ Ø§Ù„ÙˆØµÙ�)',
             'apiAppInstructionsManual0' => 'Ø§Ø³Ù… ØªØ·Ø¨ÙŠÙ‚ÙƒØŒ ÙŠÙ…ÙƒÙ† Ø£Ù† ÙŠÙƒÙˆÙ† Ø£ÙŠ Ø´ÙŠØ¡',
             'apiAppInstructionsManual1' => 'Ø§Ù„Ø§Ø³Ù… Ø§Ù„Ù…Ø®ØªØµØ± Ù„ØªØ·Ø¨ÙŠÙ‚ÙƒØŒ Ø£Ø­Ø±Ù� ÙˆØ£Ø±Ù‚Ø§Ù…ØŒ Ù…Ù† 5 Ø¥Ù„Ù‰ 32 Ø­Ø±Ù�Ù‹Ø§',
             'apiAppInstructionsManual2' => 'Ø¹Ù†ÙˆØ§Ù† URL Ù„ØªØ·Ø¨ÙŠÙ‚Ùƒ/Ù…ÙˆÙ‚Ø¹ÙƒØŒ Ø£Ùˆ t.me/yourusername',
             'apiAppInstructionsManual3' => 'Ø£ÙŠ Ø´ÙŠØ¡',
             'apiAppInstructionsManual4' => 'ØµÙ�Ù� ØªØ·Ø¨ÙŠÙ‚Ùƒ Ù‡Ù†Ø§',
             'apiAppShortNameLabelWeb' => 'Ø§Ù„Ø§Ø³Ù… Ø§Ù„Ù…Ø®ØªØµØ±',
             'apiAppTitleLabelWeb' => 'Ø§Ø³Ù… Ø§Ù„ØªØ·Ø¨ÙŠÙ‚',
             'apiAppUrlLabelWeb' => 'Ø¹Ù†ÙˆØ§Ù† URL',
             'apiAppWeb' => 'Ø£Ø¯Ø®Ù„ Ù…Ø¹Ù„ÙˆÙ…Ø§Øª API',
             'apiAutoCodeLabelWeb' => 'Ø±Ù…Ø² ØªØ³Ø¬ÙŠÙ„ Ø§Ù„Ø¯Ø®ÙˆÙ„',
             'apiAutoCodeLeadWeb' => 'Ø£Ø±Ø³Ù„ my.telegram.org Ø±Ù…Ø² ØªØ³Ø¬ÙŠÙ„ Ø¯Ø®ÙˆÙ„ Ø¥Ù„Ù‰ ØªØ·Ø¨ÙŠÙ‚ Telegram Ù„Ø¯ÙŠÙƒØŒ Ù�ÙŠ Ø§Ù„Ù…Ø­Ø§Ø¯Ø«Ø© Ù…Ø¹ Telegram (ÙˆÙ„ÙŠØ³ Ø¹Ø¨Ø± SMS).',
             'apiAutoCodeTitleWeb' => 'Ø£Ø¯Ø®Ù„ Ø§Ù„Ø±Ù…Ø² Ø§Ù„ÙˆØ§Ø±Ø¯ Ù…Ù† Telegram',
             'apiAutoErrorTitle' => 'Ø£Ø¹Ø§Ø¯ my.telegram.org Ø®Ø·Ø£Ù‹',
             'apiAutoFallbackManual' => 'Ø£Ø¯Ø®Ù„ API ID Ùˆhash ÙŠØ¯ÙˆÙŠÙ‹Ø§ Ø¨Ø¯Ù„Ù‹Ø§ Ù…Ù† Ø°Ù„Ùƒ.',
             'apiAutoPhoneHintWeb' => 'ÙŠØ±Ø³Ù„ my.telegram.org Ø±Ù…Ø² ØªØ³Ø¬ÙŠÙ„ Ø§Ù„Ø¯Ø®ÙˆÙ„ Ø¥Ù„Ù‰ ØªØ·Ø¨ÙŠÙ‚ Telegram Ù„Ø¯ÙŠÙƒØŒ ÙˆÙ„ÙŠØ³ Ø¹Ø¨Ø± SMS.',
             'apiAutoPrompt0' => 'Ø£Ø¯Ø®Ù„ Ø±Ù‚Ù… Ù‡Ø§ØªÙ� Ù…Ø³Ø¬Ù‘Ù„Ù‹Ø§ Ù…Ø³Ø¨Ù‚Ù‹Ø§ Ù�ÙŠ Telegram: ',
             'apiAutoPrompt1' => 'Ø£Ø¯Ø®Ù„ Ø±Ù…Ø² Ø§Ù„ØªØ­Ù‚Ù‚ Ø§Ù„Ø°ÙŠ ØªÙ„Ù‚ÙŠØªÙ‡ Ù�ÙŠ Telegram: ',
             'apiAutoWeb' => 'Ø£Ø¯Ø®Ù„ Ø±Ù‚Ù… Ù‡Ø§ØªÙ� <b>Ù…Ø³Ø¬Ù‘Ù„Ù‹Ø§ Ù…Ø³Ø¨Ù‚Ù‹Ø§</b> Ù�ÙŠ Telegram Ù„Ù„Ø­ØµÙˆÙ„ Ø¹Ù„Ù‰ API ID',
             'apiChooseAutoWeb' => 'Ø§Ù„Ø­ØµÙˆÙ„ Ø¹Ù„ÙŠÙ‡Ù…Ø§ ØªÙ„Ù‚Ø§Ø¦ÙŠÙ‹Ø§',
             'apiChooseManualAuto' => 'Ù‡Ù„ ØªØ±ÙŠØ¯ Ø§Ù„Ø­ØµÙˆÙ„ Ø¹Ù„Ù‰ API ID Ùˆhash ØªÙ„Ù‚Ø§Ø¦ÙŠÙ‹Ø§ Ù…Ù† my.telegram.org (a)ØŒ Ø£Ù… Ø¥Ø¯Ø®Ø§Ù„Ù‡Ù…Ø§ ÙŠØ¯ÙˆÙŠÙ‹Ø§ (m)ØŸ',
             'apiChooseManualAutoTip' => 'Ù„Ø§Ø­Ø¸ Ø£Ù†Ù‡ ÙŠÙ…ÙƒÙ†Ùƒ Ø£ÙŠØ¶Ù‹Ø§ ØªÙˆÙ�ÙŠØ± API ID/hash Ù…Ø¨Ø§Ø´Ø±Ø©Ù‹ Ù�ÙŠ Ø§Ù„Ø´ÙŠÙ�Ø±Ø© Ø¨Ø§Ø³ØªØ®Ø¯Ø§Ù… Ø§Ù„Ø¥Ø¹Ø¯Ø§Ø¯Ø§Øª: %s',
             'apiChooseManualAutoTipWeb' => 'Ù„Ø§Ø­Ø¸ Ø£Ù†Ù‡ ÙŠÙ…ÙƒÙ†Ùƒ Ø£ÙŠØ¶Ù‹Ø§ ØªÙˆÙ�ÙŠØ± API ID/hash Ù…Ø¨Ø§Ø´Ø±Ø©Ù‹ Ù�ÙŠ Ø§Ù„Ø´ÙŠÙ�Ø±Ø© Ø¨Ø§Ø³ØªØ®Ø¯Ø§Ù… <a target="_blank" href="%s">Ø§Ù„Ø¥Ø¹Ø¯Ø§Ø¯Ø§Øª</a>.',
             'apiChooseManualWeb' => 'Ù„Ø¯ÙŠ API ID Ø¨Ø§Ù„Ù�Ø¹Ù„',
             'apiChoosePrompt' => 'Ø§Ø®ØªÙŠØ§Ø±Ùƒ (m/a): ',
             'apiCreateErrorTitle' => 'Ù„Ù… ÙŠÙ�Ù†Ø´Ø¦ my.telegram.org Ø§Ù„ØªØ·Ø¨ÙŠÙ‚',
             'apiCreateLeadWeb' => 'Ù„Ø§ ÙŠÙ…Ù„Ùƒ Ù‡Ø°Ø§ Ø§Ù„Ø­Ø³Ø§Ø¨ Ø£ÙŠ ØªØ·Ø¨ÙŠÙ‚ Ø¹Ù„Ù‰ my.telegram.org Ø¨Ø¹Ø¯. ØªØ¸Ù‡Ø± Ù‡Ø°Ù‡ Ø§Ù„ØªÙ�Ø§ØµÙŠÙ„ Ù„Ùƒ Ù�Ù‚Ø·ØŒ ÙˆÙŠÙ…ÙƒÙ† ØªØºÙŠÙŠØ±Ù‡Ø§ Ù„Ø§Ø­Ù‚Ù‹Ø§.',
             'apiCreateTitleWeb' => 'Ø£Ù†Ø´Ø¦ ØªØ·Ø¨ÙŠÙ‚Ùƒ',
             'apiCreateVpsWarning' => 'Ù„Ø§ ÙŠØ³Ù…Ø­ Telegram Ø¨Ø¥Ù†Ø´Ø§Ø¡ ØªØ·Ø¨ÙŠÙ‚Ø§Øª Ø¬Ø¯ÙŠØ¯Ø© Ø¹Ù„Ù‰ my.telegram.org Ù…Ù† Ù†Ø·Ø§Ù‚Ø§Øª IP Ø§Ù„Ø®Ø§ØµØ© Ø¨Ø®ÙˆØ§Ø¯Ù… VPS ÙˆØ´Ø¨ÙƒØ§Øª VPN ÙˆÙ…Ø±Ø§ÙƒØ² Ø§Ù„Ø¨ÙŠØ§Ù†Ø§Øª Ø§Ù„Ø£Ø®Ø±Ù‰ØŒ ÙˆÙ‚Ø¯ Ø£Ù�Ø±Ø³Ù„ Ø§Ù„Ø·Ù„Ø¨ Ù…Ù† Ø¹Ù†ÙˆØ§Ù† IP Ù„Ù„Ø¬Ù‡Ø§Ø² Ø§Ù„Ø°ÙŠ ÙŠØ´ØºÙ‘Ù„ MadelineProto. Ø£Ù†Ø´Ø¦ Ø§Ù„ØªØ·Ø¨ÙŠÙ‚ ÙŠØ¯ÙˆÙŠÙ‹Ø§ Ø¹Ù„Ù‰ https://my.telegram.org Ù…Ù† Ø§ØªØµØ§Ù„ Ù…Ù†Ø²Ù„ÙŠ (Ù…Ø«Ù„ Ù‡Ø§ØªÙ�Ùƒ Ø¹Ø¨Ø± Ø¨ÙŠØ§Ù†Ø§Øª Ø§Ù„Ø¬ÙˆØ§Ù„)ØŒ Ø«Ù… Ø£Ø¯Ø®Ù„ API ID Ùˆhash.',
             'apiCreateWeb' => 'Ø¥Ù†Ø´Ø§Ø¡ Ø§Ù„ØªØ·Ø¨ÙŠÙ‚',
             'apiError' => 'Ø®Ø·Ø£: %s. Ø­Ø§ÙˆÙ„ Ù…Ø±Ø© Ø£Ø®Ø±Ù‰.',
             'apiInvalidWeb' => 'API ID Ø±Ù‚Ù…ØŒ ÙˆAPI hash Ø·ÙˆÙ„Ù‡ 32 Ø­Ø±Ù�Ù‹Ø§: Ø§Ù†Ø³Ø®Ù‡Ù…Ø§ Ù…Ø±Ø© Ø£Ø®Ø±Ù‰ Ù…Ù† my.telegram.org.',
             'apiManualInstructions0' => 'Ø³Ø¬Ù‘Ù„ Ø§Ù„Ø¯Ø®ÙˆÙ„ Ø¥Ù„Ù‰ https://my.telegram.org',
             'apiManualInstructions1' => 'Ø§Ù†ØªÙ‚Ù„ Ø¥Ù„Ù‰ API development tools',
             'apiManualInstructions2' => 'Ø§Ù†Ù‚Ø± Ø¹Ù„Ù‰ create application',
             'apiManualPrompt0' => 'Ø£Ø¯Ø®Ù„ API ID Ø§Ù„Ø®Ø§Øµ Ø¨Ùƒ: ',
             'apiManualPrompt1' => 'Ø£Ø¯Ø®Ù„ API hash Ø§Ù„Ø®Ø§Øµ Ø¨Ùƒ: ',
             'apiManualWeb' => 'Ø£Ø¯Ø®Ù„ API ID ÙˆAPI hash Ø§Ù„Ø®Ø§ØµÙŠÙ† Ø¨Ùƒ',
             'apiParamsError' => 'Ù„Ù… ØªÙ‚Ø¯Ù‘Ù… Ø¬Ù…ÙŠØ¹ Ø§Ù„Ù…Ø¹Ø§Ù…Ù„Ø§Øª Ø§Ù„Ù…Ø·Ù„ÙˆØ¨Ø©!',
             'apiSaveWeb' => 'Ø­Ù�Ø¸ ÙˆÙ…ØªØ§Ø¨Ø¹Ø©',
             'apiSendCodeWeb' => 'Ø¥Ø±Ø³Ø§Ù„ Ø§Ù„Ø±Ù…Ø²',
             'apiSetupLeadWeb' => 'ÙŠØ­ØªØ§Ø¬ ÙƒÙ„ ØªØ·Ø¨ÙŠÙ‚ Telegram Ø¥Ù„Ù‰ API ID Ùˆhash. ÙŠÙ…ÙƒÙ† Ù„Ù€ MadelineProto Ø§Ù„Ø­ØµÙˆÙ„ Ø¹Ù„ÙŠÙ‡Ù…Ø§ Ù„Ùƒ Ù…Ù† my.telegram.orgØŒ Ø¨Ø§Ø³ØªØ®Ø¯Ø§Ù… Ø±Ù‚Ù… Ù‡Ø§ØªÙ� Ø­Ø³Ø§Ø¨ Telegram Ù…ÙˆØ¬ÙˆØ¯.',
             'apiSetupTitleWeb' => 'Ø§Ø­ØµÙ„ Ø¹Ù„Ù‰ API ID Ùˆhash',
             'apiStartOverWeb' => 'Ø§Ù„Ø¨Ø¯Ø¡ Ù…Ù† Ø¬Ø¯ÙŠØ¯',
             'api_not_set' => 'ÙŠØ¬Ø¨ ØªÙˆÙ�ÙŠØ± api key Ùˆapi idØŒ Ø§Ø­ØµÙ„ Ø¹Ù„ÙŠÙ‡Ù…Ø§ Ù…Ù† my.telegram.org',
             'array_invalid' => 'Ù„Ù… ØªÙ‚Ø¯Ù‘Ù… Ù…ØµÙ�ÙˆÙ�Ø© ØµØ§Ù„Ø­Ø©',
             'baseDirLimitation' => 'ØªÙ… Ø¶Ø¨Ø· Ù‚ÙŠØ¯ basedir: Ù‚Ø¯ ÙŠØ¤Ø«Ø± Ø°Ù„Ùƒ Ø¹Ù„Ù‰ Ø§Ù„Ø£Ø¯Ø§Ø¡ ÙˆÙŠØ³Ø¨Ø¨ Ø¨Ø¹Ø¶ Ø§Ù„Ù…Ø´ÙƒÙ„Ø§ØªØŒ ÙŠÙ�Ø±Ø¬Ù‰ ØªØ¹Ø·ÙŠÙ„Ù‡ Ø¥Ù† Ø£Ù…ÙƒÙ†!',
             'bool_error' => 'ØªØ¹Ø°Ù‘Ø± Ø§Ø³ØªØ®Ø±Ø§Ø¬ Ø§Ù„Ù‚ÙŠÙ…Ø© Ø§Ù„Ù…Ù†Ø·Ù‚ÙŠØ© (boolean)',
             'botAlreadyRunning' => 'Ø§Ù„Ø¨ÙˆØª Ù‚ÙŠØ¯ Ø§Ù„ØªØ´ØºÙŠÙ„ Ø¨Ø§Ù„Ù�Ø¹Ù„!',
             'botapi_conversion_error' => 'Ù„Ø§ ÙŠÙ…ÙƒÙ† ØªØ­ÙˆÙŠÙ„ %s Ø¥Ù„Ù‰ ÙƒØ§Ø¦Ù† bot API',
             'call_already_accepted' => 'Ø§Ù„Ù…ÙƒØ§Ù„Ù…Ø© %s Ù…Ù‚Ø¨ÙˆÙ„Ø© Ø¨Ø§Ù„Ù�Ø¹Ù„',
             'call_already_declined' => 'Ø§Ù„Ù…ÙƒØ§Ù„Ù…Ø© %s Ù…Ø±Ù�ÙˆØ¶Ø© Ø¨Ø§Ù„Ù�Ø¹Ù„',
             'call_completing' => 'Ø¬Ø§Ø±Ù� Ø¥ÙƒÙ…Ø§Ù„ Ø¥Ù†Ø´Ø§Ø¡ Ø§Ù„Ù…ÙƒØ§Ù„Ù…Ø© Ù…Ù† %s...',
             'call_confirming' => 'Ø¬Ø§Ø±Ù� ØªØ£ÙƒÙŠØ¯ Ø§Ù„Ù…ÙƒØ§Ù„Ù…Ø© Ù…Ù† %s...',
             'call_discarding' => 'Ø¬Ø§Ø±Ù� Ø¥Ù†Ù‡Ø§Ø¡ Ø§Ù„Ù…ÙƒØ§Ù„Ù…Ø© %s...',
             'call_error_1' => 'ØªØ¹Ø°Ù‘Ø± Ø§Ù„Ø¹Ø«ÙˆØ± Ø¹Ù„Ù‰ Ø§Ù„Ù…ÙƒØ§Ù„Ù…Ø© %s ÙˆÙ‚Ø¨ÙˆÙ„Ù‡Ø§',
             'call_error_2' => 'ØªØ¹Ø°Ù‘Ø± Ø§Ù„Ø¹Ø«ÙˆØ± Ø¹Ù„Ù‰ Ø§Ù„Ù…ÙƒØ§Ù„Ù…Ø© %s ÙˆØªØ£ÙƒÙŠØ¯Ù‡Ø§',
             'call_error_3' => 'ØªØ¹Ø°Ù‘Ø± Ø§Ù„Ø¹Ø«ÙˆØ± Ø¹Ù„Ù‰ Ø§Ù„Ù…ÙƒØ§Ù„Ù…Ø© %s ÙˆØ¥ÙƒÙ…Ø§Ù„ Ø¥Ù†Ø´Ø§Ø¦Ù‡Ø§',
             'cli_need_dl.php_link' => 'ÙŠÙ�Ø±Ø¬Ù‰ ØªØ­Ø¯ÙŠØ¯ Ø¹Ù†ÙˆØ§Ù† URL Ù„Ø³ÙƒØ±Ø¨Øª Ø§Ù„ØªÙ†Ø²ÙŠÙ„ Ø¹Ù†Ø¯ Ø§Ø³ØªØ®Ø¯Ø§Ù… getDownloadLink Ø¹Ø¨Ø± CLI!',
             'constructor_not_found' => 'Ù„Ù… ÙŠØªÙ… Ø§Ù„Ø¹Ø«ÙˆØ± Ø¹Ù„Ù‰ constructor Ù„Ù„Ù†ÙˆØ¹: ',
             'could_not_connect_to_MadelineProto' => 'ØªØ¹Ø°Ù‘Ø± Ø§Ù„Ø§ØªØµØ§Ù„ Ø¨Ù€ MadelineProtoØŒ ÙŠÙ�Ø±Ø¬Ù‰ ØªÙ�Ø¹ÙŠÙ„ proc_open ÙˆØ¥Ø²Ø§Ù„Ø© Ù‚ÙŠÙˆØ¯ open_basedir Ø£Ùˆ ØªØ¹Ø·ÙŠÙ„ Ø¥Ø¹Ø§Ø¯Ø© ÙƒØªØ§Ø¨Ø© Ø§Ù„Ù…Ø³Ø§Ø±Ø§Øª Ù�ÙŠ Ø®Ø§Ø¯Ù… Ø§Ù„ÙˆÙŠØ¨ Ù„Ø¥ØµÙ„Ø§Ø­ Ø°Ù„Ùƒ! Ø¥Ø°Ø§ ÙƒÙ†Øª Ù‚Ø¯ Ù�Ø¹Ù„Øª Ø°Ù„Ùƒ Ø¨Ø§Ù„Ù�Ø¹Ù„ØŒ Ù�ØªØ£ÙƒØ¯ Ù…Ù† Ø£Ù† Ø¥ØµØ¯Ø§Ø± CLI Ù…Ù† PHP Ù…Ø·Ø§Ø¨Ù‚ ØªÙ…Ø§Ù…Ù‹Ø§ Ù„Ø¥ØµØ¯Ø§Ø± Ø§Ù„ÙˆÙŠØ¨ (Ù†Ù�Ø³ Ø§Ù„Ø¥ØµØ¯Ø§Ø± ÙˆØ§Ù„Ø§Ù…ØªØ¯Ø§Ø¯Ø§Øª ÙˆÙ…Ø§ Ø¥Ù„Ù‰ Ø°Ù„Ùƒ)ØŒ ÙˆØ±Ø§Ø¬Ø¹ Ù…Ù„Ù� MadelineProto.log Ù„Ù…Ø²ÙŠØ¯ Ù…Ù† Ø§Ù„Ù…Ø¹Ù„ÙˆÙ…Ø§Øª Ø­ÙˆÙ„ Ø§Ù„Ø®Ø·Ø£ Ø§Ù„Ø°ÙŠ Ù…Ù†Ø¹ Ø®Ø§Ø¯Ù… IPC Ù…Ù† Ø§Ù„Ø¨Ø¯Ø¡.',
             'could_not_convert_object' => 'ØªØ¹Ø°Ù‘Ø± ØªØ­ÙˆÙŠÙ„ ÙƒØ§Ø¦Ù† Ù…Ù† Ø§Ù„Ù†ÙˆØ¹ %s',
             'deserialization_error' => 'Ø­Ø¯Ø« Ø®Ø·Ø£ Ø£Ø«Ù†Ø§Ø¡ Ø¥Ù„ØºØ§Ø¡ Ø§Ù„ØªØ³Ù„Ø³Ù„ (deserialization)',
             'dl.php_check_logs_make_sure_session_running' => 'Ø¥Ù…Ø§ Ø£Ù† Ø¨ÙˆØª MadelineProto EventHandler Ø§Ù„Ù…Ø±ØªØ¨Ø· Ø£Ùˆ Ø®Ø§Ø¯Ù… MadelineProto IPC ØºÙŠØ± Ù…ØªØµÙ„ØŒ ÙŠÙ�Ø±Ø¬Ù‰ Ø§Ù„ØªØ­Ù‚Ù‚ Ù…Ù† Ø§Ù„Ø³Ø¬Ù„Ø§Øª ÙˆØ§Ù„ØªØ£ÙƒØ¯ Ù…Ù† ØªØ´ØºÙŠÙ„ ÙˆØ§Ø­Ø¯ Ù…Ù†Ù‡Ù…Ø§ Ø¹Ù„Ù‰ Ø§Ù„Ø£Ù‚Ù„!',
             'dl.php_powered_by_madelineproto' => 'Ø®Ø§Ø¯Ù… ØªÙ†Ø²ÙŠÙ„ Ù…Ù„Ù�Ø§Øª Telegram (Ø­ØªÙ‰ 4 ØºÙŠØºØ§Ø¨Ø§ÙŠØª)ØŒ Ù…Ø¯Ø¹ÙˆÙ… Ø¨ÙˆØ§Ø³Ø·Ø© <a href="https://docs.madelineproto.xyz" target="_blank">MadelineProto</a>!<br>Ø§Ù†Ù‚Ø± <a href="https://docs.madelineproto.xyz/docs/FILES.html#getting-a-download-link" target="_blank">Ù‡Ù†Ø§</a> Ù„Ù…Ø²ÙŠØ¯ Ù…Ù† Ø§Ù„Ù…Ø¹Ù„ÙˆÙ…Ø§Øª Ø­ÙˆÙ„ ÙƒÙŠÙ�ÙŠØ© Ø¥Ø¹Ø¯Ø§Ø¯ Ø®Ø§Ø¯Ù… ØªÙ†Ø²ÙŠÙ„ Ù…Ù„Ù�Ø§Øª Telegram Ø§Ù„Ø®Ø§Øµ Ø¨Ùƒ!',
             'do_not_delete_MadelineProto.log' => 'ÙŠØ¬Ø¨ Ø¹Ø¯Ù… Ø­Ø°Ù� Ù…Ù„Ù� MadelineProto.log Ø£Ø¨Ø¯Ù‹Ø§ØŒ ÙŠÙ�Ø±Ø¬Ù‰ Ø¨Ø¯Ù„Ù‹Ø§ Ù…Ù† Ø°Ù„Ùƒ ØªØ¹ÙŠÙŠÙ† Ø­Ø¯ Ø£Ù‚ØµÙ‰ Ù…Ø®ØµØµ Ù„Ù„Ø­Ø¬Ù… Ù�ÙŠ Ø§Ù„Ø¥Ø¹Ø¯Ø§Ø¯Ø§Øª!',
             'do_not_remove_MadelineProto.log_phar' => 'ÙŠÙ�Ø±Ø¬Ù‰ Ø¹Ø¯Ù… Ø­Ø°Ù� madeline.phar Ø£Ùˆ madeline.phpØŒ ÙˆØ¥Ù„Ø§ Ø³ÙŠØªØ¹Ø·Ù„ MadelineProto. Ø¥Ø°Ø§ ÙˆØ§Ø¬Ù‡Øª Ø£ÙŠ Ù…Ø´ÙƒÙ„Ø© Ù…Ø¹ MadelineProtoØŒ Ù�Ø£Ø¨Ù„Øº Ø¹Ù†Ù‡Ø§ Ø¹Ù„Ù‰ https://github.com/danog/MadelineProto Ø£Ùˆ https://t.me/pwrtelegramgroup',
             'do_not_use_blocking_class' => 'Ù„Ø£Ø³Ø¨Ø§Ø¨ ØªØªØ¹Ù„Ù‚ Ø¨Ø§Ù„Ø£Ø¯Ø§Ø¡ØŒ Ù„Ø§ ÙŠØ¬ÙˆØ² Ù„Ù„Ù…Ø¹Ø§Ù„Ø¬Ø§Øª Ø§Ø³ØªØ®Ø¯Ø§Ù… Ø§Ù„ØµÙ†Ù� Ø§Ù„Ø­Ø§Ø¬Ø¨ (ØºÙŠØ± async) %sØŒ ÙŠÙ�Ø±Ø¬Ù‰ Ø§Ø³ØªØ®Ø¯Ø§Ù… %s Ø¨Ø¯Ù„Ù‹Ø§ Ù…Ù†Ù‡',
             'do_not_use_blocking_function' => 'Ù„Ø£Ø³Ø¨Ø§Ø¨ ØªØªØ¹Ù„Ù‚ Ø¨Ø§Ù„Ø£Ø¯Ø§Ø¡ØŒ Ù„Ø§ ÙŠØ¬ÙˆØ² Ù„Ù…Ø¹Ø§Ù„Ø¬Ø§Øª Ø§Ù„Ø£Ø­Ø¯Ø§Ø« Ø§Ø³ØªØ®Ø¯Ø§Ù… Ø§Ù„Ø¯Ø§Ù„Ø© Ø§Ù„Ø­Ø§Ø¬Ø¨Ø© Ø§Ù„Ù…ØªØ²Ø§Ù…Ù†Ø© (ØºÙŠØ± async) %sØŒ ÙŠÙ�Ø±Ø¬Ù‰ Ø§Ø³ØªØ®Ø¯Ø§Ù… %s Ø¨Ø¯Ù„Ù‹Ø§ Ù…Ù†Ù‡Ø§',
             'do_not_use_deprecated_function' => 'Ø§Ù„Ø¯Ø§Ù„Ø© %s Ù…Ù‡Ù…Ù„Ø© (deprecated)ØŒ ÙŠÙ�Ø±Ø¬Ù‰ Ø§Ø³ØªØ®Ø¯Ø§Ù… %s Ø¨Ø¯Ù„Ù‹Ø§ Ù…Ù†Ù‡Ø§',
             'do_not_use_non_root_require_in_event_handler' => 'Ù„Ø£Ø³Ø¨Ø§Ø¨ ØªØªØ¹Ù„Ù‚ Ø¨Ø§Ù„Ø£Ø¯Ø§Ø¡ØŒ ÙŠØ¬Ø¨ Ø¹Ø¯Ù… Ø§Ø³ØªØ®Ø¯Ø§Ù… require Ø£Ùˆ include Ø¯Ø§Ø®Ù„ ØµÙ†Ù� Ù…Ø¹Ø§Ù„Ø¬ Ø§Ù„Ø£Ø­Ø¯Ø§Ø«ØŒ ÙŠÙ�Ø³Ù…Ø­ Ù�Ù‚Ø· Ø¨Ù€ require Ø¹Ù„Ù‰ Ù…Ø³ØªÙˆÙ‰ Ø§Ù„Ø¬Ø°Ø±.',
             'do_not_use_yield' => 'Ù„Ø§ ÙŠØªØ·Ù„Ø¨ MadelineProto 8 Ø§Ø³ØªØ®Ø¯Ø§Ù… yield Ù�ÙŠ Ø§Ù„Ø¯ÙˆØ§Ù„ ØºÙŠØ± Ø§Ù„Ù…ØªØ²Ø§Ù…Ù†Ø© ÙˆÙ„Ø§ ÙŠØ¯Ø¹Ù…Ù‡ØŒ ÙŠØ¬Ø¨ Ø¹Ù„ÙŠÙƒ Ø¥Ø²Ø§Ù„Ø© Ø¬Ù…ÙŠØ¹ ÙƒÙ„Ù…Ø§Øª yield Ø§Ù„Ù…Ù�ØªØ§Ø­ÙŠØ© Ø§Ù„Ù…Ø³ØªØ®Ø¯Ù…Ø© Ø³Ø§Ø¨Ù‚Ù‹Ø§ Ù„Ø§Ø³ØªØ¯Ø¹Ø§Ø¡Ø§Øª Ø§Ù„Ø¯ÙˆØ§Ù„ ØºÙŠØ± Ø§Ù„Ù…ØªØ²Ø§Ù…Ù†Ø©',
             'done' => 'ØªÙ…!',
             'encode_double_error' => 'ØªØ¹Ø°Ù‘Ø± ØªØ±Ù…ÙŠØ² double Ø¨Ø´ÙƒÙ„ ØµØ­ÙŠØ­',
             'extensionRecommended' => 'ØªØ­Ø°ÙŠØ±: Ø§Ù„Ø§Ù…ØªØ¯Ø§Ø¯ %s ØºÙŠØ± Ù…Ø«Ø¨Ù‘ØªØŒ ÙŠÙ�Ø±Ø¬Ù‰ ØªØ«Ø¨ÙŠØªÙ‡ Ù„ØªØ³Ø±ÙŠØ¹ MadelineProto!',
             'extensionRequired' => 'ÙŠØªØ·Ù„Ø¨ MadelineProto Ø§Ù„Ø§Ù…ØªØ¯Ø§Ø¯ %s Ù„ÙŠØ¹Ù…Ù„. %s',
             'extensionRequiredInstallWithApt' => 'Ø¬Ø±Ù‘Ø¨ ØªÙ†Ù�ÙŠØ° sudo apt-get install %s.',
             'extensionRequiredInstallWithCustomInstructions' => 'Ø§ØªØ¨Ø¹ Ø§Ù„ØªØ¹Ù„ÙŠÙ…Ø§Øª Ø¹Ù„Ù‰ %s Ù„ØªØ«Ø¨ÙŠØªÙ‡.',
             'file_not_exist' => 'Ø§Ù„Ù…Ù„Ù� ØºÙŠØ± Ù…ÙˆØ¬ÙˆØ¯',
             'file_parsing' => 'Ø¬Ø§Ø±Ù� ØªØ­Ù„ÙŠÙ„ %s...',
             'file_type_invalid' => 'ØªÙ… Ø§ÙƒØªØ´Ø§Ù� Ù†ÙˆØ¹ Ù…Ù„Ù� ØºÙŠØ± ØµØ§Ù„Ø­ (%s)',
             'fingerprint_invalid' => 'Ø¨ØµÙ…Ø© Ø§Ù„Ù…Ù�ØªØ§Ø­ ØºÙŠØ± ØµØ§Ù„Ø­Ø©!',
             'go' => 'Ù…ØªØ§Ø¨Ø¹Ø©',
             'invalid_dl.php_session' => '%s Ù„ÙŠØ³ Ø³ÙƒØ±Ø¨Øª ØªÙ†Ø²ÙŠÙ„ ØµØ§Ù„Ø­Ù‹Ø§ Ù„Ø£Ù† Ù…Ø¹Ø±Ù‘Ù� Ø¬Ù„Ø³ØªÙ‡ Ù…Ø®ØªÙ„Ù� (Ø§Ù„Ù…ØªÙˆÙ‚Ø¹ %sØŒ Ø§Ù„Ù�Ø¹Ù„ÙŠ %s)',
             'ipc_tcp_warning' => 'UNIX sockets are not available, so the IPC server of the session listens on TCP (127.0.0.1) instead: any program running on this machine can connect to it, and control the session. On Windows 10 1803+, enable the sockets extension (extension=sockets in php.ini) to use UNIX sockets.',
             'length_too_big' => 'Ø§Ù„Ø·ÙˆÙ„ ÙƒØ¨ÙŠØ± Ø¬Ø¯Ù‹Ø§',
             'loginBot' => 'Ø£Ø¯Ø®Ù„ ØªÙˆÙƒÙ† Ø§Ù„Ø¨ÙˆØª: ',
             'loginBotTokenHintWeb' => 'Ø§Ù„ØªÙˆÙƒÙ† Ø§Ù„Ø°ÙŠ Ø£Ø¹Ø·Ø§Ùƒ Ø¥ÙŠØ§Ù‡ @BotFather Ø¹Ù†Ø¯ Ø¥Ù†Ø´Ø§Ø¡ Ø§Ù„Ø¨ÙˆØª.',
             'loginBotTokenWeb' => 'ØªÙˆÙƒÙ† Ø§Ù„Ø¨ÙˆØª',
             'loginChoosePromptWeb' => 'Ù‡Ù„ ØªØ±ÙŠØ¯ ØªØ³Ø¬ÙŠÙ„ Ø§Ù„Ø¯Ø®ÙˆÙ„ ÙƒÙ…Ø³ØªØ®Ø¯Ù… Ø£Ù… ÙƒØ¨ÙˆØªØŸ',
             'loginCodeLeadWeb' => 'Ø£Ø±Ø³Ù„ Telegram Ø±Ù…Ø² ØªØ³Ø¬ÙŠÙ„ Ø§Ù„Ø¯Ø®ÙˆÙ„ Ø¥Ù„Ù‰ ØªØ·Ø¨ÙŠÙ‚ Telegram Ù„Ø¯ÙŠÙƒØŒ Ø£Ùˆ Ø¹Ø¨Ø± SMS Ø¥Ù† Ù„Ù… ØªÙƒÙ† Ù…Ø³Ø¬Ù‘Ù„Ù‹Ø§ Ø§Ù„Ø¯Ø®ÙˆÙ„ Ù�ÙŠ Ø£ÙŠ Ù…ÙƒØ§Ù† Ø¢Ø®Ø±.',
             'loginCodeTitleWeb' => 'Ø£Ø¯Ø®Ù„ Ø±Ù…Ø² ØªØ³Ø¬ÙŠÙ„ Ø§Ù„Ø¯Ø®ÙˆÙ„',
             'loginLeadWeb' => 'Ø§Ø®ØªØ± Ø§Ù„Ø­Ø³Ø§Ø¨ Ø£Ùˆ Ø§Ù„Ø¨ÙˆØª Ø§Ù„Ø°ÙŠ Ø³ØªØ¹Ù…Ù„ Ø¨Ù‡ Ù†Ø³Ø®Ø© MadelineProto Ù‡Ø°Ù‡.',
             'loginManual' => 'Ø¨Ø¯Ù„Ù‹Ø§ Ù…Ù† Ø°Ù„ÙƒØŒ ÙŠÙ…ÙƒÙ†Ùƒ Ø£ÙŠØ¶Ù‹Ø§ Ø¥Ø¯Ø®Ø§Ù„ ØªÙˆÙƒÙ† Ø¨ÙˆØª Ø£Ùˆ Ø±Ù‚Ù… Ù‡Ø§ØªÙ� Ù„ØªØ³Ø¬ÙŠÙ„ Ø§Ù„Ø¯Ø®ÙˆÙ„ ÙŠØ¯ÙˆÙŠÙ‹Ø§: ',
             'loginNoCode' => 'Ù„Ù… ØªÙ�Ø¯Ø®Ù„ Ø±Ù…Ø² Ø§Ù„ØªØ­Ù‚Ù‚!',
             'loginNoName' => 'Ù„Ù… ØªÙ�Ø¯Ø®Ù„ Ø§Ù„Ø§Ø³Ù… Ø§Ù„Ø£ÙˆÙ„!',
             'loginNoPass' => 'Ù„Ù… ØªÙ�Ø¯Ø®Ù„ ÙƒÙ„Ù…Ø© Ø§Ù„Ù…Ø±ÙˆØ±!',
             'loginOptionBot' => 'Ø¨ÙˆØª',
             'loginOptionUser' => 'Ù…Ø³ØªØ®Ø¯Ù…',
             'loginPasswordLabelWeb' => 'ÙƒÙ„Ù…Ø© Ø§Ù„Ù…Ø±ÙˆØ±',
             'loginPasswordLeadWeb' => 'Ù‡Ø°Ø§ Ø§Ù„Ø­Ø³Ø§Ø¨ Ù…Ø­Ù…ÙŠ Ø¨ÙƒÙ„Ù…Ø© Ù…Ø±ÙˆØ±ØŒ Ù…Ù�Ø¹ÙŠÙ‘Ù†Ø© Ù�ÙŠ Ø§Ù„Ø¥Ø¹Ø¯Ø§Ø¯Ø§Øª > Ø§Ù„Ø®ØµÙˆØµÙŠØ© ÙˆØ§Ù„Ø£Ù…Ø§Ù† > Ø§Ù„ØªØ­Ù‚Ù‚ Ø¨Ø®Ø·ÙˆØªÙŠÙ†.',
             'loginPasswordTitleWeb' => 'Ø§Ù„ØªØ­Ù‚Ù‚ Ø¨Ø®Ø·ÙˆØªÙŠÙ†',
             'loginQr' => 'Ø§Ù�ØªØ­ Telegram Ø¹Ù„Ù‰ Ù‡Ø§ØªÙ�ÙƒØŒ ÙˆØ§Ù†ØªÙ‚Ù„ Ø¥Ù„Ù‰ Ø§Ù„Ø¥Ø¹Ø¯Ø§Ø¯Ø§Øª > Ø§Ù„Ø£Ø¬Ù‡Ø²Ø© > Ø±Ø¨Ø· Ø¬Ù‡Ø§Ø² Ø³Ø·Ø­ Ø§Ù„Ù…ÙƒØªØ¨ØŒ Ø«Ù… Ø§Ù…Ø³Ø­ Ø±Ù…Ø² QR Ø£Ø¹Ù„Ø§Ù‡ Ù„ØªØ³Ø¬ÙŠÙ„ Ø§Ù„Ø¯Ø®ÙˆÙ„ ØªÙ„Ù‚Ø§Ø¦ÙŠÙ‹Ø§.',
             'loginQrCodeExpired' => 'Ø§Ù†ØªÙ‡Øª ØµÙ„Ø§Ø­ÙŠØ© Ø±Ù…Ø² QRØŒ Ø¬Ø§Ø±Ù� Ø¥Ù†Ø´Ø§Ø¡ Ø±Ù…Ø² Ø¬Ø¯ÙŠØ¯...',
             'loginQrCodeSuccessful' => 'ØªÙ… ØªØ³Ø¬ÙŠÙ„ Ø§Ù„Ø¯Ø®ÙˆÙ„ Ø¨Ø±Ù…Ø² QR Ø¨Ù†Ø¬Ø§Ø­!',
             'loginTitleWeb' => 'ØªØ³Ø¬ÙŠÙ„ Ø§Ù„Ø¯Ø®ÙˆÙ„ Ø¥Ù„Ù‰ Telegram',
             'loginTypeWeb' => 'ØªØ³Ø¬ÙŠÙ„ Ø§Ù„Ø¯Ø®ÙˆÙ„ ÙƒÙ€',
             'loginUser' => 'Ø£Ø¯Ø®Ù„ Ø±Ù‚Ù… Ù‡Ø§ØªÙ�Ùƒ: ',
             'loginUserCode' => 'Ø£Ø¯Ø®Ù„ Ø§Ù„Ø±Ù…Ø²: ',
             'loginUserPass' => 'Ø£Ø¯Ø®Ù„ ÙƒÙ„Ù…Ø© Ø§Ù„Ù…Ø±ÙˆØ± (Ø§Ù„ØªÙ„Ù…ÙŠØ­: %s): ',
             'loginUserPassHint' => 'Ø§Ù„ØªÙ„Ù…ÙŠØ­: %s',
             'loginUserPassWeb' => 'Ø£Ø¯Ø®Ù„ ÙƒÙ„Ù…Ø© Ø§Ù„Ù…Ø±ÙˆØ±: ',
             'loginUserPhoneCodeWeb' => 'Ø§Ù„Ø±Ù…Ø²',
             'loginUserPhoneHintWeb' => 'Ù…Ø¹ Ø±Ù…Ø² Ø§Ù„Ø¯ÙˆÙ„Ø©ØŒ Ù…Ø«Ù„ +39 123 456 7890.',
             'loginUserPhoneWeb' => 'Ø±Ù‚Ù… Ø§Ù„Ù‡Ø§ØªÙ�',
             'loginWebQr' => 'ÙŠÙ…ÙƒÙ†Ùƒ Ø£ÙŠØ¶Ù‹Ø§ ØªØ³Ø¬ÙŠÙ„ Ø§Ù„Ø¯Ø®ÙˆÙ„ ØªÙ„Ù‚Ø§Ø¦ÙŠÙ‹Ø§ Ø¨Ù�ØªØ­ Telegram Ø¹Ù„Ù‰ Ù‡Ø§ØªÙ�ÙƒØŒ ÙˆØ§Ù„Ø§Ù†ØªÙ‚Ø§Ù„ Ø¥Ù„Ù‰ Ø§Ù„Ø¥Ø¹Ø¯Ø§Ø¯Ø§Øª > Ø§Ù„Ø£Ø¬Ù‡Ø²Ø© > Ø±Ø¨Ø· Ø¬Ù‡Ø§Ø² Ø³Ø·Ø­ Ø§Ù„Ù…ÙƒØªØ¨ØŒ Ø«Ù… Ù…Ø³Ø­ Ø±Ù…Ø² QR Ø§Ù„ØªØ§Ù„ÙŠ:',
             'loginWebQr1' => 'Ø§Ù�ØªØ­ Telegram Ø¹Ù„Ù‰ Ù‡Ø§ØªÙ�Ùƒ',
             'loginWebQr2' => 'Ø§Ù†ØªÙ‚Ù„ Ø¥Ù„Ù‰ Ø§Ù„Ø¥Ø¹Ø¯Ø§Ø¯Ø§Øª > Ø§Ù„Ø£Ø¬Ù‡Ø²Ø© > Ø±Ø¨Ø· Ø¬Ù‡Ø§Ø² Ø³Ø·Ø­ Ø§Ù„Ù…ÙƒØªØ¨',
             'loginWebQr3' => 'ÙˆØ¬Ù‘Ù‡ Ù‡Ø§ØªÙ�Ùƒ Ù†Ø­Ùˆ Ù‡Ø°Ù‡ Ø§Ù„Ø´Ø§Ø´Ø© Ù„ØªØ£ÙƒÙŠØ¯ ØªØ³Ø¬ÙŠÙ„ Ø§Ù„Ø¯Ø®ÙˆÙ„',
             'login_2fa_enabled' => 'Ø§Ù„Ù…ØµØ§Ø¯Ù‚Ø© Ø§Ù„Ø«Ù†Ø§Ø¦ÙŠØ© (2FA) Ù…Ù�Ø¹Ù‘Ù„Ø©ØŒ Ø³ÙŠØªØ¹ÙŠÙ‘Ù† Ø¹Ù„ÙŠÙƒ Ø§Ø³ØªØ¯Ø¹Ø§Ø¡ Ø§Ù„Ø¯Ø§Ù„Ø© complete2falogin...',
             'login_auth_key' => 'Ø¬Ø§Ø±Ù� ØªØ³Ø¬ÙŠÙ„ Ø§Ù„Ø¯Ø®ÙˆÙ„ Ø¨Ø§Ø³ØªØ®Ø¯Ø§Ù… Ù…Ù�ØªØ§Ø­ Ø§Ù„ØªÙ�ÙˆÙŠØ¶ (auth key)...',
             'login_bot' => 'Ø¬Ø§Ø±Ù� ØªØ³Ø¬ÙŠÙ„ Ø§Ù„Ø¯Ø®ÙˆÙ„ ÙƒØ¨ÙˆØª...',
             'login_code_sending' => 'Ø¬Ø§Ø±Ù� Ø¥Ø±Ø³Ø§Ù„ Ø§Ù„Ø±Ù…Ø²...',
             'login_code_sent' => 'ØªÙ… Ø¥Ø±Ø³Ø§Ù„ Ø§Ù„Ø±Ù…Ø² Ø¨Ù†Ø¬Ø§Ø­! Ø¨Ø¹Ø¯ Ø§Ø³ØªÙ„Ø§Ù… Ø§Ù„Ø±Ù…Ø²ØŒ Ø§Ø³ØªØ®Ø¯Ù… Ø§Ù„Ø¯Ø§Ù„Ø© completePhoneLogin.',
             'login_code_uncalled' => 'Ù„Ø³Øª Ù�ÙŠ Ø§Ù†ØªØ¸Ø§Ø± Ø§Ù„Ø±Ù…Ø²! ÙŠÙ�Ø±Ø¬Ù‰ Ø§Ø³ØªØ¯Ø¹Ø§Ø¡ Ø§Ù„Ø¯Ø§Ù„Ø© phoneLogin Ø£ÙˆÙ„Ù‹Ø§',
             'login_need_signup' => 'Ù„Ù… ÙŠØªÙ… Ø¥Ù†Ø´Ø§Ø¡ Ø­Ø³Ø§Ø¨ Ù„Ù‡Ø°Ø§ Ø§Ù„Ø±Ù‚Ù…ØŒ Ø³ÙŠØªØ¹ÙŠÙ‘Ù† Ø¹Ù„ÙŠÙƒ Ø§Ø³ØªØ¯Ø¹Ø§Ø¡ Ø§Ù„Ø¯Ø§Ù„Ø© completeSignup...',
             'login_ok' => 'ØªÙ… ØªØ³Ø¬ÙŠÙ„ Ø§Ù„Ø¯Ø®ÙˆÙ„ Ø¨Ù†Ø¬Ø§Ø­!',
             'login_user' => 'Ø¬Ø§Ø±Ù� ØªØ³Ø¬ÙŠÙ„ Ø§Ù„Ø¯Ø®ÙˆÙ„ ÙƒÙ…Ø³ØªØ®Ø¯Ù… Ø¹Ø§Ø¯ÙŠ...',
             'logout_ok' => 'ØªÙ… ØªØ³Ø¬ÙŠÙ„ Ø§Ù„Ø®Ø±ÙˆØ¬ Ø¨Ù†Ø¬Ø§Ø­!',
             'long_not_16' => 'Ø·ÙˆÙ„ Ø§Ù„Ù‚ÙŠÙ…Ø© Ø§Ù„Ù…Ø¹Ø·Ø§Ø© Ù„ÙŠØ³ 16 Ø¨Ø§ÙŠØª',
             'long_not_32' => 'Ø·ÙˆÙ„ Ø§Ù„Ù‚ÙŠÙ…Ø© Ø§Ù„Ù…Ø¹Ø·Ø§Ø© Ù„ÙŠØ³ 32 Ø¨Ø§ÙŠØª',
             'long_not_64' => 'Ø·ÙˆÙ„ Ø§Ù„Ù‚ÙŠÙ…Ø© Ø§Ù„Ù…Ø¹Ø·Ø§Ø© Ù„ÙŠØ³ 64 Ø¨Ø§ÙŠØª',
             'madelineproto_ready' => 'MadelineProto Ø¬Ø§Ù‡Ø²!',
             'manualAdminActionRequired' => '!!!!!!!!! ÙŠÙ„Ø²Ù… ØªØ¯Ø®Ù„ ÙŠØ¯ÙˆÙŠ Ù…Ù† Ù…Ø³Ø¤ÙˆÙ„ Ø§Ù„Ù†Ø¸Ø§Ù… !!!!!!!!!',
             'method_not_found' => 'ØªØ¹Ø°Ù‘Ø± Ø§Ù„Ø¹Ø«ÙˆØ± Ø¹Ù„Ù‰ Ø§Ù„Ø¯Ø§Ù„Ø©: ',
             'mmapErrorPart1' => 'ØªÙ… Ø¨Ù„ÙˆØº Ø§Ù„Ø­Ø¯ Ø§Ù„Ø£Ù‚ØµÙ‰ Ù„Ø¹Ø¯Ø¯ Ù…Ù†Ø§Ø·Ù‚ Ø§Ù„Ø°Ø§ÙƒØ±Ø© Ø§Ù„Ù…Ø¹ÙŠÙ‘Ù†Ø© (mmap) (%s): ÙŠÙ�Ø±Ø¬Ù‰ Ø²ÙŠØ§Ø¯Ø© Ø¥Ø¹Ø¯Ø§Ø¯ Ø§Ù„Ù†ÙˆØ§Ø© vm.max_map_count Ø¥Ù„Ù‰ 262144 Ù„Ø¥ØµÙ„Ø§Ø­ Ø°Ù„Ùƒ.',
             'mmapErrorPart2' => 'Ù„Ù„Ø¥ØµÙ„Ø§Ø­ØŒ Ù†Ù�Ù‘Ø° Ø§Ù„Ø£Ù…Ø± Ø§Ù„ØªØ§Ù„ÙŠ Ø¨ØµÙ„Ø§Ø­ÙŠØ§Øª root: %s',
             'mmapErrorPart3' => 'Ù„Ù„Ø¥Ø¨Ù‚Ø§Ø¡ Ø¹Ù„Ù‰ Ø§Ù„ØªØºÙŠÙŠØ± Ø¨Ø¹Ø¯ Ø¥Ø¹Ø§Ø¯Ø© Ø§Ù„ØªØ´ØºÙŠÙ„: %s',
             'mmapErrorPart4' => 'Ø¹Ù„Ù‰ Windows ÙˆWSLØŒ Ù‚Ø¯ ØªØ³Ø§Ø¹Ø¯ Ø²ÙŠØ§Ø¯Ø© Ø­Ø¬Ù… Ù…Ù„Ù� Ø§Ù„ØµÙ�Ø­Ø§Øª (pagefile)Ø› ÙŠÙ�Ø±Ø¬Ù‰ Ø§Ù„Ø§Ù†ØªÙ‚Ø§Ù„ Ø¥Ù„Ù‰ Linux Ø£ØµÙ„ÙŠ Ø¥Ø°Ø§ Ø§Ø³ØªÙ…Ø±Øª Ø§Ù„Ù…Ø´ÙƒÙ„Ø©.',
             'must_have_declare_types' => 'Ù„Ø£Ø³Ø¨Ø§Ø¨ ØªØªØ¹Ù„Ù‚ Ø¨Ø§Ù„Ø£Ø¯Ø§Ø¡ØŒ ÙŠØ¬Ø¨ Ø£Ù† ØªÙƒÙˆÙ† Ø§Ù„Ø¹Ø¨Ø§Ø±Ø© Ø§Ù„Ø£ÙˆÙ„Ù‰ Ù�ÙŠ Ù…Ù„Ù� Ù…Ø¹Ø§Ù„Ø¬ Ø§Ù„Ø£Ø­Ø¯Ø§Ø« "declare(strict_types=1);"',
             'nearest_dc' => 'Ù†Ø­Ù† Ù�ÙŠ %sØŒ ÙˆØ£Ù‚Ø±Ø¨ DC Ù‡Ùˆ %d.',
             'need_dl.php' => 'ØªØ¹Ø°Ù‘Ø± Ø¥Ù†Ø´Ø§Ø¡ Ø³ÙƒØ±Ø¨Øª Ø§Ù„ØªÙ†Ø²ÙŠÙ„ Ø§Ù„Ø§Ù�ØªØ±Ø§Ø¶ÙŠ (%s)ØŒ ÙŠÙ�Ø±Ø¬Ù‰ Ø¥Ù†Ø´Ø§Ø¡ Ù…Ù„Ù� dl.php Ø¨Ø§Ù„Ù…Ø­ØªÙˆÙ‰ Ø§Ù„ØªØ§Ù„ÙŠ: %s ÙˆØªÙ…Ø±ÙŠØ± Ø¹Ù†ÙˆØ§Ù† URL Ø§Ù„Ø®Ø§Øµ Ø¨Ù‡ Ø¥Ù„Ù‰ Ø§Ù„Ù…Ø¹Ø§Ù…Ù„ Ø§Ù„Ø«Ø§Ù†ÙŠ Ù„Ù€ getDownloadLink',
             'noReportPeers' => 'ØªØ­Ø°ÙŠØ±: Ù„Ù… ÙŠØªÙ… ØªØ¹ÙŠÙŠÙ† Ø£ÙŠ Ø¬Ù‡Ø§Øª Ù„Ø§Ø³ØªÙ„Ø§Ù… Ø§Ù„ØªÙ‚Ø§Ø±ÙŠØ± (report peers)ØŒ ÙŠÙ�Ø±Ø¬Ù‰ Ø¥Ø¶Ø§Ù�Ø© Ø§Ù„Ø¯Ø§Ù„Ø© Ø§Ù„ØªØ§Ù„ÙŠØ© Ø¥Ù„Ù‰ Ù…Ø¹Ø§Ù„Ø¬ Ø§Ù„Ø£Ø­Ø¯Ø§Ø« Ø§Ù„Ø®Ø§Øµ Ø¨Ùƒ',
             'non_text_conversion' => 'Ù„Ø§ ÙŠÙ…ÙƒÙ† ØªØ­ÙˆÙŠÙ„ Ø§Ù„Ø±Ø³Ø§Ø¦Ù„ ØºÙŠØ± Ø§Ù„Ù†ØµÙŠØ© Ø¨Ø¹Ø¯!',
             'not_loggedIn' => 'Ù„Ù… ÙŠØªÙ… ØªØ³Ø¬ÙŠÙ„ Ø§Ù„Ø¯Ø®ÙˆÙ„!',
             'not_numeric' => 'Ø§Ù„Ù‚ÙŠÙ…Ø© Ø§Ù„Ù…Ø¹Ø·Ø§Ø© Ù„ÙŠØ³Øª Ø±Ù‚Ù…ÙŠØ©',
             'params_missing' => 'Ù…Ø¹Ø§Ù…Ù„ Ù…Ø·Ù„ÙˆØ¨ Ù…Ù�Ù‚ÙˆØ¯',
             'peer_not_in_db' => 'Ù‡Ø°Ø§ Ø§Ù„Ù€ peer ØºÙŠØ± Ù…ÙˆØ¬ÙˆØ¯ Ù�ÙŠ Ù‚Ø§Ø¹Ø¯Ø© Ø¨ÙŠØ§Ù†Ø§Øª Ø§Ù„Ù€ peers Ø§Ù„Ø¯Ø§Ø®Ù„ÙŠØ©',
             'plugin_path_does_not_exist' => 'Ù…Ø³Ø§Ø± Ø§Ù„Ø¥Ø¶Ø§Ù�Ø© %s ØºÙŠØ± Ù…ÙˆØ¬ÙˆØ¯!',
             'plugins_do_not_use_require' => 'Ù„Ø£Ø³Ø¨Ø§Ø¨ ØªØªØ¹Ù„Ù‚ Ø¨Ø§Ù„Ø£Ø¯Ø§Ø¡ØŒ Ù„Ø§ ÙŠÙ…ÙƒÙ† Ù„Ù„Ø¥Ø¶Ø§Ù�Ø§Øª ØªØ¶Ù…ÙŠÙ† (include Ø£Ùˆ require) Ù…Ù„Ù�Ø§Øª Ø£Ø®Ø±Ù‰ Ù…ÙˆØ¬ÙˆØ¯Ø© Ù�ÙŠ Ù…Ø¬Ù„Ø¯ Ø§Ù„Ø¥Ø¶Ø§Ù�Ø§Øª ØªÙ„Ù‚Ø§Ø¦ÙŠÙ‹Ø§ Ø¥Ù„Ø§ Ø¹Ø¨Ø± ØªØ´ØºÙŠÙ„ Ø§Ù„Ù…Ø­Ù…Ù‘Ù„ Ø§Ù„ØªÙ„Ù‚Ø§Ø¦ÙŠ PSR-4 (ÙˆÙ„ÙŠØ³ Ø¨Ø§Ø³ØªØ¯Ø¹Ø§Ø¡ require() ÙŠØ¯ÙˆÙŠÙ‹Ø§).',
             'plugins_must_have_exactly_one_class' => 'ÙŠØ¬Ø¨ Ø£Ù† ØªØ¹Ø±Ù‘Ù� Ø§Ù„Ø¥Ø¶Ø§Ù�Ø© ØµÙ†Ù�Ù‹Ø§ ÙˆØ§Ø­Ø¯Ù‹Ø§ Ø¨Ø§Ù„Ø¶Ø¨Ø·! Ù„ØªØ¹Ø±ÙŠÙ� Ø¹Ø¯Ø© Ø£ØµÙ†Ø§Ù� Ø£Ùˆ ÙˆØ§Ø¬Ù‡Ø§Øª Ø£Ùˆ traitsØŒ Ø£Ù†Ø´Ø¦ Ù…Ù„Ù�Ø§Øª Ù…Ù†Ù�ØµÙ„Ø©ØŒ ÙˆØ³ÙŠØ­Ù…Ù‘Ù„Ù‡Ø§ MadelineProto ØªÙ„Ù‚Ø§Ø¦ÙŠÙ‹Ø§.',
             'predicate_not_set' => 'Ù„Ù… ÙŠØªÙ… ØªØ¹ÙŠÙŠÙ† Ø§Ù„Ù€ predicate (Ø§Ù„Ù‚ÙŠÙ…Ø© ØªØ­Øª _)!',
             'recommend_not_use_filesystem_function' => 'Ù„Ø§ ÙŠÙ�Ù†ØµØ­ Ø¨Ø§Ø³ØªØ®Ø¯Ø§Ù… Ø§Ù„Ø¯Ø§Ù„Ø© %sØŒ Ù„Ø£Ù† Ø§Ù„ÙˆØµÙˆÙ„ Ø¥Ù„Ù‰ Ù†Ø¸Ø§Ù… Ø§Ù„Ù…Ù„Ù�Ø§Øª Ø£Ø«Ù†Ø§Ø¡ Ù…Ø¹Ø§Ù„Ø¬Ø© Ø§Ù„ØªØ­Ø¯ÙŠØ«Ø§Øª Ø³ÙŠØ¨Ø·Ø¦ Ø§Ù„Ø¨ÙˆØª Ø§Ù„Ø®Ø§Øµ Ø¨ÙƒØŒ ÙŠÙ�Ø±Ø¬Ù‰ Ù…Ø±Ø§Ø¬Ø¹Ø© https://docs.madelineproto.xyz/docs/UPDATES.html#avoiding-the-use-of-filesystem-functions Ù„Ù„Ø§Ø·Ù„Ø§Ø¹ Ø¹Ù„Ù‰ Ù‚Ø§Ø¦Ù…Ø© Ø¨Ø§Ù„Ø·Ø±Ù‚ Ø§Ù„Ø¨Ø¯ÙŠÙ„Ø© Ù„ØªØ®Ø²ÙŠÙ† Ø§Ù„Ø¨ÙŠØ§Ù†Ø§Øª Ø§Ù„ØªÙŠ Ù„Ù† ØªØ¨Ø·Ø¦ Ø§Ù„Ø¨ÙˆØª!',
             'rpc_tg_error' => 'Ø£Ø¹Ø§Ø¯ Telegram Ø®Ø·Ø£ RPC: %s (%s)ØŒ Ø¨Ø³Ø¨Ø¨ %s:%s%sØªØªØ¨Ø¹ TL:',
             'sec_peer_not_in_db' => 'Ù‡Ø°Ø§ Ø§Ù„Ù€ peer Ø§Ù„Ø³Ø±ÙŠ ØºÙŠØ± Ù…ÙˆØ¬ÙˆØ¯ Ù�ÙŠ Ù‚Ø§Ø¹Ø¯Ø© Ø¨ÙŠØ§Ù†Ø§Øª Ø§Ù„Ù€ peers Ø§Ù„Ø¯Ø§Ø®Ù„ÙŠØ©',
             'secret_chat_skipping' => 'Ø§Ù„Ù…Ø­Ø§Ø¯Ø«Ø© Ø§Ù„Ø³Ø±ÙŠØ© %s ØºÙŠØ± Ù…ÙˆØ¬ÙˆØ¯Ø© Ù�ÙŠ Ù‚Ø§Ø¹Ø¯Ø© Ø§Ù„Ø¨ÙŠØ§Ù†Ø§ØªØŒ Ø¬Ø§Ø±Ù� ØªØ®Ø·ÙŠ Ø§Ù„Ø±Ø³Ø§Ù„Ø©...',
             'serialization_ofd' => 'Ø§Ù„ØªØ³Ù„Ø³Ù„ (serialization) Ù‚Ø¯ÙŠÙ…ØŒ Ø¬Ø§Ø±Ù� Ø¥Ø¹Ø§Ø¯Ø© Ø¨Ù†Ø§Ø¡ Ø§Ù„ÙƒØ§Ø¦Ù†!',
             'session_corrupted' => 'Ø§Ù„Ø¬Ù„Ø³Ø© ØªØ§Ù„Ù�Ø©!',
             'signing_up' => 'Ø¬Ø§Ø±Ù� Ø§Ù„ØªØ³Ø¬ÙŠÙ„ ÙƒÙ…Ø³ØªØ®Ø¯Ù… Ø¹Ø§Ø¯ÙŠ...',
             'signupFirstName' => 'Ø£Ø¯Ø®Ù„ Ø§Ø³Ù…Ùƒ Ø§Ù„Ø£ÙˆÙ„: ',
             'signupFirstNameWeb' => 'Ø§Ù„Ø§Ø³Ù… Ø§Ù„Ø£ÙˆÙ„',
             'signupLastName' => 'Ø£Ø¯Ø®Ù„ Ø§Ø³Ù… Ø§Ù„Ø¹Ø§Ø¦Ù„Ø© (ÙŠÙ…ÙƒÙ† ØªØ±ÙƒÙ‡ Ù�Ø§Ø±ØºÙ‹Ø§): ',
             'signupLastNameWeb' => 'Ø§Ø³Ù… Ø§Ù„Ø¹Ø§Ø¦Ù„Ø©',
             'signupLeadWeb' => 'Ø±Ù‚Ù… Ø§Ù„Ù‡Ø§ØªÙ� Ù‡Ø°Ø§ ØºÙŠØ± Ù…Ø³Ø¬Ù‘Ù„ Ù�ÙŠ Telegram Ø¨Ø¹Ø¯: Ø§Ø®ØªØ± Ø§Ø³Ù… Ø§Ù„Ø­Ø³Ø§Ø¨ Ø§Ù„Ø¬Ø¯ÙŠØ¯.',
             'signupTitleWeb' => 'Ø£Ù†Ø´Ø¦ Ø­Ø³Ø§Ø¨Ùƒ',
             'signupWeb' => 'ÙŠÙ�Ø±Ø¬Ù‰ Ø§Ù„ØªØ³Ø¬ÙŠÙ„',
             'signup_ok' => 'ØªÙ… Ø§Ù„ØªØ³Ø¬ÙŠÙ„ Ø¨Ù†Ø¬Ø§Ø­!',
             'signup_uncalled' => 'Ù„Ø³Øª Ù�ÙŠ Ø§Ù†ØªØ¸Ø§Ø± Ø§Ù„ØªØ³Ø¬ÙŠÙ„! ÙŠÙ�Ø±Ø¬Ù‰ Ø§Ø³ØªØ¯Ø¹Ø§Ø¡ Ø§Ù„Ø¯Ø§Ù„ØªÙŠÙ† phoneLogin ÙˆcompletePhoneLogin Ø£ÙˆÙ„Ù‹Ø§!',
             'src_file_invalid' => 'ØªÙ… ØªÙ‚Ø¯ÙŠÙ… Ù…Ù„Ù� Ù…ØµØ¯Ø± ØºÙŠØ± ØµØ§Ù„Ø­: ',
             'static_analysis_minor' => 'ØªÙ…Øª Ù…ØµØ§Ø¯Ù�Ø© Ù…Ø´ÙƒÙ„Ø© Ø¨Ø³ÙŠØ·Ø© Ø£Ø«Ù†Ø§Ø¡ Ø§Ù„ØªØ­Ù„ÙŠÙ„ Ø§Ù„Ø«Ø§Ø¨Øª Ù„Ù€ %s: %s',
             'static_analysis_severe' => 'ØªÙ…Øª Ù…ØµØ§Ø¯Ù�Ø© Ù…Ø´ÙƒÙ„Ø© Ø®Ø·ÙŠØ±Ø© Ø£Ø«Ù†Ø§Ø¡ Ø§Ù„ØªØ­Ù„ÙŠÙ„ Ø§Ù„Ø«Ø§Ø¨Øª Ù„Ù€ %s: %s',
             'stream_handle_invalid' => 'ØªÙ… ØªÙ‚Ø¯ÙŠÙ… Ù…Ù‚Ø¨Ø¶ ØªØ¯Ù�Ù‚ (stream handle) ØºÙŠØ± ØµØ§Ù„Ø­.',
             'string_required' => 'ÙƒØ§Ù† Ù…Ù† Ø§Ù„Ù…ØªÙˆÙ‚Ø¹ Ø³Ù„Ø³Ù„Ø© Ù†ØµÙŠØ© (string)!',
             'translate_madelineproto_cli' => 'ÙŠÙ…ÙƒÙ† ØªØ±Ø¬Ù…Ø© MadelineProto Ø¥Ù„Ù‰ Ù„ØºØªÙƒ (ØªÙ‚Ø¯Ù‘Ù… Ø§Ù„ØªØ±Ø¬Ù…Ø© Ø§Ù„Ø­Ø§Ù„ÙŠ: %d%%)ØŒ Ø§Ù†ØªÙ‚Ù„ Ø¥Ù„Ù‰ https://github.com/danog/MadelineProto/tree/v8/langs Ù„Ù„Ù…Ø³Ø§Ù‡Ù…Ø© Ù�ÙŠ Ø§Ù„ØªØ±Ø¬Ù…Ø©!',
             'translate_madelineproto_web' => 'ÙŠÙ…ÙƒÙ† ØªØ±Ø¬Ù…Ø© MadelineProto Ø¥Ù„Ù‰ Ù„ØºØªÙƒ (ØªÙ‚Ø¯Ù‘Ù… Ø§Ù„ØªØ±Ø¬Ù…Ø© Ø§Ù„Ø­Ø§Ù„ÙŠ: %d%%)ØŒ Ø§Ù†Ù‚Ø± <a href="https://github.com/danog/MadelineProto/tree/v8/langs" target="_blank">Ù‡Ù†Ø§ Ù„Ù„Ù…Ø³Ø§Ù‡Ù…Ø© Ù�ÙŠ Ø§Ù„ØªØ±Ø¬Ù…Ø©!</a>',
             'tweb.ACCESS_TOKEN_EXPIRED' => 'ØªÙ… Ø¥Ø¨Ø·Ø§Ù„ ØªÙˆÙƒÙ† Ø§Ù„Ø¨ÙˆØª Ù‡Ø°Ø§',
             'tweb.ACCESS_TOKEN_INVALID' => 'ØªÙˆÙƒÙ† Ø§Ù„Ø¨ÙˆØª ØºÙŠØ± ØµØ§Ù„Ø­',
             'tweb.AccDescr.LoginCode' => 'Ø±Ù…Ø² ØªØ³Ø¬ÙŠÙ„ Ø§Ù„Ø¯Ø®ÙˆÙ„',
             'tweb.Close' => 'Ø¥ØºÙ„Ø§Ù‚',
             'tweb.Code' => 'Ø§Ù„Ø±Ù…Ø²',
             'tweb.Contacts.PhoneNumber.Placeholder' => 'Ø±Ù‚Ù… Ø§Ù„Ù‡Ø§ØªÙ�',
             'tweb.Country' => 'Ø§Ù„Ø¯ÙˆÙ„Ø©',
             'tweb.DarkMode' => 'Ø§Ù„ÙˆØ¶Ø¹ Ø§Ù„Ø¯Ø§ÙƒÙ†',
             'tweb.Error.AnError' => 'Ø­Ø¯Ø« Ø®Ø·Ø£. ÙŠÙ�Ø±Ø¬Ù‰ Ø§Ù„Ù…Ø­Ø§ÙˆÙ„Ø© Ù…Ø±Ø© Ø£Ø®Ø±Ù‰ Ù„Ø§Ø­Ù‚Ù‹Ø§.',
             'tweb.FIRSTNAME_INVALID' => 'Ø§Ù„Ø§Ø³Ù… Ø§Ù„Ø£ÙˆÙ„ ØºÙŠØ± ØµØ§Ù„Ø­',
             'tweb.FirstName' => 'Ø§Ù„Ø§Ø³Ù… Ø§Ù„Ø£ÙˆÙ„ (Ù…Ø·Ù„ÙˆØ¨)',
             'tweb.LASTNAME_INVALID' => 'Ø§Ø³Ù… Ø§Ù„Ø¹Ø§Ø¦Ù„Ø© ØºÙŠØ± ØµØ§Ù„Ø­',
             'tweb.LastName' => 'Ø§Ø³Ù… Ø§Ù„Ø¹Ø§Ø¦Ù„Ø© (Ø§Ø®ØªÙŠØ§Ø±ÙŠ)',
             'tweb.Login.Bot' => 'ØªØ³Ø¬ÙŠÙ„ Ø§Ù„Ø¯Ø®ÙˆÙ„ ÙƒØ¨ÙˆØª >',
             'tweb.Login.Bot.Cancel' => 'ØªØ³Ø¬ÙŠÙ„ Ø§Ù„Ø¯Ø®ÙˆÙ„ ÙƒÙ…Ø³ØªØ®Ø¯Ù… >',
             'tweb.Login.Bot.Subtitle' => 'Ø£Ø¯Ø®Ù„ Ø§Ù„ØªÙˆÙƒÙ† Ø§Ù„Ø°ÙŠ Ø£Ø¹Ø·Ø§Ùƒ Ø¥ÙŠØ§Ù‡ @BotFather
Ø¹Ù†Ø¯ Ø¥Ù†Ø´Ø§Ø¡ Ø§Ù„Ø¨ÙˆØª.',
             'tweb.Login.Bot.Title' => 'ØªØ³Ø¬ÙŠÙ„ Ø§Ù„Ø¯Ø®ÙˆÙ„ ÙƒØ¨ÙˆØª',
             'tweb.Login.Bot.Token' => 'ØªÙˆÙƒÙ† Ø§Ù„Ø¨ÙˆØª',
             'tweb.Login.Code.SecretPhrase' => 'Ø§Ù„Ø¹Ø¨Ø§Ø±Ø© Ø§Ù„Ø³Ø±ÙŠØ©',
             'tweb.Login.Code.SecretWord' => 'Ø§Ù„ÙƒÙ„Ù…Ø© Ø§Ù„Ø³Ø±ÙŠØ©',
             'tweb.Login.Code.SentCall' => 'Ø³Ù†ØªØµÙ„ Ø¨Ùƒ ÙˆÙ†Ù…Ù„ÙŠ Ø¹Ù„ÙŠÙƒ
Ø§Ù„Ø±Ù…Ø².',
             'tweb.Login.Code.SentEmail' => 'ÙŠÙ�Ø±Ø¬Ù‰ Ø§Ù„ØªØ­Ù‚Ù‚ Ù…Ù† Ø¨Ø±ÙŠØ¯Ùƒ Ø§Ù„Ø¥Ù„ÙƒØªØ±ÙˆÙ†ÙŠ **%s** (Ù„Ø§ ØªÙ†Ø³ÙŽ Ù…Ø¬Ù„Ø¯ Ø§Ù„Ø±Ø³Ø§Ø¦Ù„ ØºÙŠØ± Ø§Ù„Ù…Ø±ØºÙˆØ¨ Ù�ÙŠÙ‡Ø§) ÙˆØ¥Ø¯Ø®Ø§Ù„ Ø§Ù„Ø±Ù…Ø² Ø§Ù„Ø°ÙŠ Ø£Ø±Ø³Ù„Ù†Ø§Ù‡ Ø¥Ù„ÙŠÙƒ Ù„Ù„ØªÙˆ.',
             'tweb.Login.Code.SentInApp' => 'Ù„Ù‚Ø¯ Ø£Ø±Ø³Ù„Ù†Ø§ Ø¥Ù„ÙŠÙƒ Ø±Ø³Ø§Ù„Ø© Ù�ÙŠ Telegram
ØªØ­ØªÙˆÙŠ Ø¹Ù„Ù‰ Ø§Ù„Ø±Ù…Ø².',
             'tweb.Login.Code.SentMissedCall' => 'Ø®Ù„Ø§Ù„ Ø§Ù„Ø«ÙˆØ§Ù†ÙŠ Ø§Ù„Ù‚Ù„ÙŠÙ„Ø© Ø§Ù„Ù‚Ø§Ø¯Ù…Ø© Ø³ØªØªÙ„Ù‚Ù‰
Ù…ÙƒØ§Ù„Ù…Ø© Ù‚ØµÙŠØ±Ø© Ù…Ù† **%s**. Ù„Ø§ ØªÙ�Ø¬Ø¨ Ø¹Ù„ÙŠÙ‡Ø§ â€”
Ù�Ù‚Ø· Ø£Ø¯Ø®Ù„ Ø§Ù„Ø£Ø±Ù‚Ø§Ù… Ø§Ù„Ø£Ø®ÙŠØ±Ø© Ù…Ù† Ù‡Ø°Ø§ Ø§Ù„Ø±Ù‚Ù….',
             'tweb.Login.Code.SentSms' => 'Ù„Ù‚Ø¯ Ø£Ø±Ø³Ù„Ù†Ø§ Ø¥Ù„ÙŠÙƒ Ø±Ø³Ø§Ù„Ø© SMS
ØªØ­ØªÙˆÙŠ Ø¹Ù„Ù‰ Ø§Ù„Ø±Ù…Ø².',
             'tweb.Login.Code.SentSmsPhrase' => 'Ù„Ù‚Ø¯ Ø£Ø±Ø³Ù„Ù†Ø§ Ø±Ø³Ø§Ù„Ø© SMS ØªØ­ØªÙˆÙŠ Ø¹Ù„Ù‰ Ø¹Ø¨Ø§Ø±Ø© Ø³Ø±ÙŠØ©
Ø¥Ù„Ù‰ Ù‡Ø§ØªÙ�Ùƒ **%s**.',
             'tweb.Login.Code.SentSmsPhraseBeginning' => 'Ù„Ù‚Ø¯ Ø£Ø±Ø³Ù„Ù†Ø§ Ø±Ø³Ø§Ù„Ø© SMS ØªØ­ØªÙˆÙŠ Ø¹Ù„Ù‰ Ø¹Ø¨Ø§Ø±Ø© Ø³Ø±ÙŠØ© ØªØ¨Ø¯Ø£
Ø¨Ù€ "**%2$s**" Ø¥Ù„Ù‰ Ù‡Ø§ØªÙ�Ùƒ **%1$s**.',
             'tweb.Login.Code.SentSmsWord' => 'Ù„Ù‚Ø¯ Ø£Ø±Ø³Ù„Ù†Ø§ Ø±Ø³Ø§Ù„Ø© SMS ØªØ­ØªÙˆÙŠ Ø¹Ù„Ù‰ ÙƒÙ„Ù…Ø© Ø³Ø±ÙŠØ©
Ø¥Ù„Ù‰ Ù‡Ø§ØªÙ�Ùƒ **%s**.',
             'tweb.Login.Code.SentSmsWordBeginning' => 'Ù„Ù‚Ø¯ Ø£Ø±Ø³Ù„Ù†Ø§ Ø±Ø³Ø§Ù„Ø© SMS ØªØ­ØªÙˆÙŠ Ø¹Ù„Ù‰ ÙƒÙ„Ù…Ø© Ø³Ø±ÙŠØ© ØªØ¨Ø¯Ø£
Ø¨Ù€ "**%2$s**" Ø¥Ù„Ù‰ Ù‡Ø§ØªÙ�Ùƒ **%1$s**.',
             'tweb.Login.Code.SentUnknown' => 'ÙŠÙ�Ø±Ø¬Ù‰ Ø§Ù„Ø¨Ø­Ø« Ù�ÙŠ ÙƒÙ„ Ù…ÙƒØ§Ù†
Ø¹Ù† Ø§Ù„Ø±Ù…Ø² (Ø§Ù„Ù†ÙˆØ¹: %s)',
             'tweb.Login.Code.WordBeginningInvalid' => 'ÙƒÙ„Ù…Ø© ØºÙŠØ± ØµØ­ÙŠØ­Ø©',
             'tweb.Login.Code.WordInvalid' => 'ØºÙŠØ± ØµØ­ÙŠØ­ØŒ ÙŠÙ�Ø±Ø¬Ù‰ Ø§Ù„Ù…Ø­Ø§ÙˆÙ„Ø© Ù…Ø±Ø© Ø£Ø®Ø±Ù‰',
             'tweb.Login.Next' => 'Ø§Ù„ØªØ§Ù„ÙŠ',
             'tweb.Login.Password.Subtitle' => 'Ø­Ø³Ø§Ø¨Ùƒ Ù…Ø­Ù…ÙŠ
Ø¨ÙƒÙ„Ù…Ø© Ù…Ø±ÙˆØ± Ø¥Ø¶Ø§Ù�ÙŠØ©',
             'tweb.Login.Password.Title' => 'Ø£Ø¯Ø®Ù„ ÙƒÙ„Ù…Ø© Ø§Ù„Ù…Ø±ÙˆØ±',
             'tweb.Login.PhoneLabelInvalid' => 'Ø±Ù‚Ù… Ø§Ù„Ù‡Ø§ØªÙ� ØºÙŠØ± ØµØ§Ù„Ø­',
             'tweb.Login.QR.Cancel' => 'ØªØ³Ø¬ÙŠÙ„ Ø§Ù„Ø¯Ø®ÙˆÙ„ Ø¨Ø±Ù‚Ù… Ø§Ù„Ù‡Ø§ØªÙ� >',
             'tweb.Login.QR.Help1' => 'Ø§Ù�ØªØ­ Telegram Ø¹Ù„Ù‰ Ù‡Ø§ØªÙ�Ùƒ',
             'tweb.Login.QR.Help2' => 'Ø§Ù†ØªÙ‚Ù„ Ø¥Ù„Ù‰ **Ø§Ù„Ø¥Ø¹Ø¯Ø§Ø¯Ø§Øª** > **Ø§Ù„Ø£Ø¬Ù‡Ø²Ø©** > **Ø¥Ø¶Ø§Ù�Ø© Ø¬Ù‡Ø§Ø²**',
             'tweb.Login.QR.Help3' => 'ÙˆØ¬Ù‘Ù‡ Ù‡Ø§ØªÙ�Ùƒ Ù†Ø­Ùˆ Ù‡Ø°Ù‡ Ø§Ù„Ø´Ø§Ø´Ø© Ù„ØªØ£ÙƒÙŠØ¯ ØªØ³Ø¬ÙŠÙ„ Ø§Ù„Ø¯Ø®ÙˆÙ„',
             'tweb.Login.QR.Login' => 'ØªØ³Ø¬ÙŠÙ„ Ø§Ù„Ø¯Ø®ÙˆÙ„ Ø¨Ø±Ù…Ø² QR >',
             'tweb.Login.QR.Subtitle' => 'Ø§Ù…Ø³Ø­ Ø§Ù„Ø±Ù…Ø² Ø¨Ø§Ø³ØªØ®Ø¯Ø§Ù… ØªØ·Ø¨ÙŠÙ‚ Telegram Ø¹Ù„Ù‰ Ù‡Ø§ØªÙ�Ùƒ',
             'tweb.Login.QR.Title' => 'ØªØ³Ø¬ÙŠÙ„ Ø§Ù„Ø¯Ø®ÙˆÙ„ Ø¨Ø±Ù…Ø² QR',
             'tweb.Login.Register.Subtitle' => 'Ø£Ø¯Ø®Ù„ Ø§Ø³Ù…Ùƒ
Ù„Ø¥Ù†Ø´Ø§Ø¡ Ø­Ø³Ø§Ø¨Ùƒ',
             'tweb.Login.StartText' => 'ÙŠÙ�Ø±Ø¬Ù‰ ØªØ£ÙƒÙŠØ¯ Ø±Ù…Ø² Ø¯ÙˆÙ„ØªÙƒ
ÙˆØ¥Ø¯Ø®Ø§Ù„ Ø±Ù‚Ù… Ù‡Ø§ØªÙ�Ùƒ.',
             'tweb.Login.Title' => 'ØªØ³Ø¬ÙŠÙ„ Ø§Ù„Ø¯Ø®ÙˆÙ„ Ø¥Ù„Ù‰ MadelineProto',
             'tweb.LoginPassword' => 'ÙƒÙ„Ù…Ø© Ø§Ù„Ù…Ø±ÙˆØ±',
             'tweb.Optional' => 'Ø§Ø®ØªÙŠØ§Ø±ÙŠ',
             'tweb.PASSWORD_HASH_INVALID' => 'ÙƒÙ„Ù…Ø© Ø§Ù„Ù…Ø±ÙˆØ± ØºÙŠØ± ØµØ­ÙŠØ­Ø©',
             'tweb.PHONE_CODE_EMPTY' => 'Ø±Ù…Ø² ØºÙŠØ± ØµØ§Ù„Ø­',
             'tweb.PHONE_CODE_EXPIRED' => 'Ø§Ù†ØªÙ‡Øª ØµÙ„Ø§Ø­ÙŠØ© Ø§Ù„Ø±Ù…Ø²',
             'tweb.PHONE_CODE_INVALID' => 'Ø±Ù…Ø² ØºÙŠØ± ØµØ§Ù„Ø­',
             'tweb.PHONE_NUMBER_BANNED' => 'Ø±Ù‚Ù… Ø§Ù„Ù‡Ø§ØªÙ� Ù‡Ø°Ø§ Ù…Ø­Ø¸ÙˆØ±',
             'tweb.PHONE_NUMBER_FLOOD' => 'Ù…Ø­Ø§ÙˆÙ„Ø§Øª ÙƒØ«ÙŠØ±Ø© Ø¬Ø¯Ù‹Ø§ØŒ Ø­Ø§ÙˆÙ„ Ù…Ø±Ø© Ø£Ø®Ø±Ù‰ Ù„Ø§Ø­Ù‚Ù‹Ø§',
             'tweb.PHONE_NUMBER_INVALID' => 'Ø±Ù‚Ù… Ø§Ù„Ù‡Ø§ØªÙ� ØºÙŠØ± ØµØ§Ù„Ø­',
             'tweb.PHONE_PASSWORD_FLOOD' => 'Ù…Ø­Ø§ÙˆÙ„Ø§Øª ÙƒØ«ÙŠØ±Ø© Ø¬Ø¯Ù‹Ø§ØŒ Ø­Ø§ÙˆÙ„ Ù…Ø±Ø© Ø£Ø®Ø±Ù‰ Ù„Ø§Ø­Ù‚Ù‹Ø§',
             'tweb.PhoneNumber.Code.Fragment.Info' => 'Ø§Ø­ØµÙ„ Ø¹Ù„Ù‰ Ø§Ù„Ø±Ù…Ø² Ù…Ù† Ù‚Ø³Ù… Ø§Ù„Ø£Ø±Ù‚Ø§Ù… Ø¹Ù„Ù‰ [Fragment]()',
             'tweb.PleaseWait' => 'ÙŠÙ�Ø±Ø¬Ù‰ Ø§Ù„Ø§Ù†ØªØ¸Ø§Ø±...',
             'tweb.StartMessaging' => 'Ø§Ø¨Ø¯Ø£ Ø§Ù„Ù…Ø±Ø§Ø³Ù„Ø©',
             'tweb.YourName' => 'Ø§Ø³Ù…Ùƒ',
             'type_extract_error' => 'ØªØ¹Ø°Ù‘Ø± Ø§Ø³ØªØ®Ø±Ø§Ø¬ Ø§Ù„Ù†ÙˆØ¹ "%s"ØŒ ÙŠØ¬Ø¨ Ø¹Ù„ÙŠÙƒ ØªØ­Ø¯ÙŠØ« MadelineProto!',
             'type_extract_error_id' => 'ØªØ¹Ø°Ù‘Ø± Ø§Ø³ØªØ®Ø±Ø§Ø¬ Ø§Ù„Ù†ÙˆØ¹: %s Ø¨Ø§Ù„Ù…Ø¹Ø±Ù‘Ù� %sØŒ ÙŠØ¬Ø¨ Ø¹Ù„ÙŠÙƒ ØªØ­Ø¯ÙŠØ« MadelineProto!',
             'update_madelineproto' => 'Ø£Ù†Øª ØªØ³ØªØ®Ø¯Ù… Ø¥ØµØ¯Ø§Ø±Ù‹Ø§ Ù‚Ø¯ÙŠÙ…Ù‹Ø§ Ù…Ù† MadelineProtoØŒ ÙˆØ§Ù„ØªØ­Ø¯ÙŠØ« Ù…Ø·Ù„ÙˆØ¨: Ø§Ù„Ø¥ØµØ¯Ø§Ø± Ø§Ù„Ø­Ø§Ù„ÙŠ Ù‡Ùˆ %sØŒ Ù„ÙƒÙ† Ø£Ø­Ø¯Ø« Ø¥ØµØ¯Ø§Ø± Ù…Ø¹ Ø§Ù„Ø¹Ø¯ÙŠØ¯ Ù…Ù† Ø¥ØµÙ„Ø§Ø­Ø§Øª Ø§Ù„Ø£Ø®Ø·Ø§Ø¡ ÙˆØ§Ù„Ù…ÙŠØ²Ø§Øª Ø§Ù„Ø¬Ø¯ÙŠØ¯Ø© Ù‡Ùˆ %s!',
             'value_bigger_than_2147483647' => 'Ø§Ù„Ù‚ÙŠÙ…Ø© Ø§Ù„Ù…Ù‚Ø¯Ù‘Ù…Ø© %s Ø£ÙƒØ¨Ø± Ù…Ù† 2147483647',
             'value_bigger_than_4294967296' => 'Ø§Ù„Ù‚ÙŠÙ…Ø© Ø§Ù„Ù…Ù‚Ø¯Ù‘Ù…Ø© %s Ø£ÙƒØ¨Ø± Ù…Ù† 4294967296',
             'value_bigger_than_9223372036854775807' => 'Ø§Ù„Ù‚ÙŠÙ…Ø© Ø§Ù„Ù…Ù‚Ø¯Ù‘Ù…Ø© %s Ø£ÙƒØ¨Ø± Ù…Ù† 9223372036854775807',
             'value_smaller_than_0' => 'Ø§Ù„Ù‚ÙŠÙ…Ø© Ø§Ù„Ù…Ù‚Ø¯Ù‘Ù…Ø© %s Ø£ØµØºØ± Ù…Ù† 0',
             'value_smaller_than_2147483648' => 'Ø§Ù„Ù‚ÙŠÙ…Ø© Ø§Ù„Ù…Ù‚Ø¯Ù‘Ù…Ø© %s Ø£ØµØºØ± Ù…Ù† -2147483648',
             'value_smaller_than_9223372036854775808' => 'Ø§Ù„Ù‚ÙŠÙ…Ø© Ø§Ù„Ù…Ù‚Ø¯Ù‘Ù…Ø© %s Ø£ØµØºØ± Ù…Ù† -9223372036854775808',
             'waveform_must_have_100_values' => 'ÙŠØ¬Ø¨ Ø£Ù† ØªØ­ØªÙˆÙŠ Ù…ØµÙ�ÙˆÙ�Ø© waveform Ø¹Ù„Ù‰ 100 Ù‚ÙŠÙ…Ø©!',
             'waveform_value' => 'ÙŠØ¬Ø¨ Ø£Ù† ØªÙƒÙˆÙ† Ù‚ÙŠÙ…Ø© waveform Ø¨ÙŠÙ† 0 Ùˆ31!',
             'webContinue' => 'Ù…ØªØ§Ø¨Ø¹Ø©',
             'webDocs' => 'Ø§Ù„ØªÙˆØ«ÙŠÙ‚',
             'webDoneLead' => 'ÙŠÙˆØ§ØµÙ„ MadelineProto Ø§Ù„Ø¹Ù…Ù„ Ø¹Ù„Ù‰ Ø§Ù„Ø®Ø§Ø¯Ù…: ÙŠÙ…ÙƒÙ†Ùƒ Ø¥ØºÙ„Ø§Ù‚ Ù‡Ø°Ù‡ Ø§Ù„ØµÙ�Ø­Ø©.',
             'webError' => '%s. ØªØ­Ù‚Ù‘Ù‚ Ù…Ù†Ù‡ ÙˆØ­Ø§ÙˆÙ„ Ù…Ø±Ø© Ø£Ø®Ø±Ù‰.',
             'webFieldRequired' => 'Ø§Ù…Ù„Ø£ Ù‡Ø°Ø§ Ø§Ù„Ø­Ù‚Ù„.',
             'webHidePassword' => 'Ø¥Ø®Ù�Ø§Ø¡',
             'webLogin' => 'ØªØ³Ø¬ÙŠÙ„ Ø§Ù„Ø¯Ø®ÙˆÙ„',
             'webOptional' => 'Ø§Ø®ØªÙŠØ§Ø±ÙŠ',
             'webPhoneInvalid' => 'Ø£Ø¯Ø®Ù„ Ø±Ù‚Ù… Ø§Ù„Ù‡Ø§ØªÙ� Ù…Ø¹ Ø±Ù…Ø² Ø§Ù„Ø¯ÙˆÙ„Ø©ØŒ Ù…Ø«Ù„ +39 123 456 7890.',
             'webProgress' => 'ØªÙ‚Ø¯Ù‘Ù… Ø§Ù„Ø¥Ø¹Ø¯Ø§Ø¯',
             'webQrTitle' => 'ØªØ³Ø¬ÙŠÙ„ Ø§Ù„Ø¯Ø®ÙˆÙ„ Ø¨Ø±Ù…Ø² QR',
             'webQrWaiting' => 'Ù�ÙŠ Ø§Ù†ØªØ¸Ø§Ø± Ø§Ù„Ù…Ø³Ø­â€¦',
             'webShowPassword' => 'Ø¥Ø¸Ù‡Ø§Ø±',
             'webStepAccount' => 'Ø§Ù„Ø­Ø³Ø§Ø¨',
             'webStepApp' => 'API ID Ùˆhash',
             'webStepDone' => 'Ù‚ÙŠØ¯ Ø§Ù„ØªØ´ØºÙŠÙ„',
             'webStepVerify' => 'Ø§Ù„ØªØ­Ù‚Ù‚',
         ],
        'ckb' =>
         [
             '2fa_uncalled' => 'Ú†Ø§ÙˆÛ•Ú•ÛŽÛŒ Ù¾Ø§Ø³Û†Ø±Ø¯Û•Ú©Û• Ù†Ø§Ú©Û•Ù…! ØªÚ©Ø§ÛŒÛ• Ø³Û•Ø±Û•ØªØ§ Ø¨Ø§Ù†Ú¯ÛŒ Ø´ÛŽÙˆØ§Ø²Û•Ú©Ø§Ù†ÛŒ phoneLogin Ùˆ completePhoneLogin Ø¨Ú©Û•!',
             'accepting_call' => 'ÙˆÛ•Ø±Ú¯Ø±ØªÙ†ÛŒ Ù¾Û•ÛŒÙˆÛ•Ù†Ø¯ÛŒ Ù„Û• %s...',
             'account_banned' => '!!!!!!! Ø¦Ø§Ú¯Ø§Ø¯Ø§Ø±ÛŒ !!!!!!!
Ø³ÛŒØ³ØªÛ•Ù…ÛŒ Ú•ÛŽÚ¯Ø±ÛŒÚ©Ø±Ø¯Ù† Ù„Û• flood ÛŒ ØªÛŽÙ„ÛŽÚ¯Ø±Ø§Ù… Ø¦Û•Ù… Ø¦Û•Ú©Ø§ÙˆÙ†ØªÛ•ÛŒ Ú•Ø§Ú¯Ø±ØªÙˆÙˆÛ•.
Ø¨Û† Ø¨Û•Ø±Ø¯Û•ÙˆØ§Ù…Ø¨ÙˆÙˆÙ†ØŒ Ù¾Ø´ØªÚ•Ø§Ø³ØªÚ©Ø±Ø¯Ù†Û•ÙˆÛ•ÛŒ Ø¯Û•Ø³ØªÛŒ Ù¾ÛŽÙˆÛŒØ³ØªÛ•.
Ø¦ÛŒÙ…Û•ÛŒÚµÛŽÚ© Ø¨Ù†ÛŽØ±Û• Ø¨Û† recover@telegram.org Ùˆ Ø¯Ø§ÙˆØ§ÛŒ Ù„Ø§Ø¨Ø±Ø¯Ù†ÛŒ Ø¨Ø§Ù†Ø¯ÛŒ Ú˜Ù…Ø§Ø±Û•ÛŒ ØªÛ•Ù„Û•Ù�Û†Ù†ÛŒ %s Ø¨Ú©Û•ØŒ Ùˆ Ø¨Û• Ú©ÙˆØ±ØªÛŒ Ø¨Ø§Ø³ÛŒ Ø¦Û•ÙˆÛ• Ø¨Ú©Û• Ú©Û• Ú†ÛŒ Ø¨Û•Ù… Ú˜Ù…Ø§Ø±Û• ØªÛ•Ù„Û•Ù�Û†Ù†Û• Ø¯Û•Ú©Û•ÛŒØª.
Ù¾Ø§Ø´Ø§Ù† Ø¯ÙˆÙˆØ¨Ø§Ø±Û• Ø¨Ú†Û† Ú˜ÙˆÙˆØ±Û•ÙˆÛ•.
Ø¦Û•Ú¯Û•Ø± Ø¨Û• Ø¦Û•Ù†Ù‚Û•Ø³Øª Ø¦Û•Ù… Ø¦Û•Ú©Ø§ÙˆÙ†ØªÛ•Øª Ø³Ú•ÛŒÙˆÛ•ØªÛ•ÙˆÛ•ØŒ Ø¦Û•Ù… Ù†Ø§Ù…Û•ÛŒÛ• Ù¾Ø´ØªÚ¯ÙˆÛŽ Ø¨Ø®Û•.',
             'already_loggedIn' => 'Ø¦Û•Ù… Ù†Ù…ÙˆÙˆÙ†Û•ÛŒÛ•ÛŒ MadelineProto Ù¾ÛŽØ´ØªØ± Ú†ÙˆÙˆÛ•ØªÛ• Ú˜ÙˆÙˆØ±Û•ÙˆÛ•!',
             'apiAppDescLabelWeb' => 'ÙˆÛ•Ø³Ù�',
             'apiAppInstructionsAuto0' => 'Ù†Ø§ÙˆÛŒ Ø¦Û•Ù¾Û•Ú©Û• Ø¨Ù†ÙˆÙˆØ³Û•ØŒ Ø¯Û•ØªÙˆØ§Ù†ÛŽØª Ù‡Û•Ø± Ø´ØªÛŽÚ© Ø¨ÛŽØª: ',
             'apiAppInstructionsAuto1' => 'Ù†Ø§ÙˆÛŒ Ú©ÙˆØ±ØªÛŒ Ø¦Û•Ù¾Û•Ú©Û• Ø¨Ù†ÙˆÙˆØ³Û•ØŒ Ø¦Û•Ù„Ù�ÙˆØ¨ÛŽÛŒ Ú˜Ù…Ø§Ø±Û•ÛŒÛŒØŒ Ù¥-Ù£Ù¢ Ù¾ÛŒØª: ',
             'apiAppInstructionsAuto2' => 'URL ÛŒ Ø¦Û•Ù¾/Ù…Ø§ÚµÙ¾Û•Ú•Û•Ú©Û• Ø¯Ø§Ø¨Ù†ÛŽØŒ ÛŒØ§Ù† t.me/yourusername: ',
             'apiAppInstructionsAuto3' => 'Ù¾Ù„Ø§ØªÙ�Û†Ø±Ù…ÛŒ Ø¦Û•Ù¾Û•Ú©Û• Ø¨Ù†ÙˆÙˆØ³Û•: ',
             'apiAppInstructionsAuto4' => 'Ø¨Ø§Ø³ÛŒ Ø¦Û•Ù¾Û•Ú©Û•Øª Ø¨Ú©Û•: ',
             'apiAppInstructionsAutoTypeOther' => 'Ø´ØªÛŒ ØªØ± (Ù„Û• ÙˆÛ•Ø³Ù�Ø¯Ø§ Ø¯ÛŒØ§Ø±ÛŒ Ø¨Ú©Û•)',
             'apiAppInstructionsManual0' => 'Ù†Ø§ÙˆÛŒ Ø¦Û•Ù¾Û•Ú©Û•Øª Ø¯Û•ØªÙˆØ§Ù†ÛŽØª Ù‡Û•Ø± Ø´ØªÛŽÚ© Ø¨ÛŽØª',
             'apiAppInstructionsManual1' => 'Ù†Ø§ÙˆÛŒ Ú©ÙˆØ±ØªÛŒ Ø¦Û•Ù¾Û•Ú©Û•ØªØŒ Ø¦Û•Ù„Ù�ÙˆØ¨ÛŽÛŒ Ú˜Ù…Ø§Ø±Û•ÛŒÛŒØŒ Ù¥-Ù£Ù¢ Ù¾ÛŒØª',
             'apiAppInstructionsManual2' => 'URL ÛŒ Ø¦Û•Ù¾/Ù…Ø§ÚµÙ¾Û•Ú•Û•Ú©Û•ØªØŒ ÛŒØ§Ù† t.me/yourusername',
             'apiAppInstructionsManual3' => 'Ù‡Û•Ø± Ø´ØªÛŽÚ©',
             'apiAppInstructionsManual4' => 'Ù„ÛŽØ±Û•Ø¯Ø§ Ø¨Ø§Ø³ÛŒ Ø¦Û•Ù¾Û•Ú©Û•Øª Ø¨Ú©Û•',
             'apiAppShortNameLabelWeb' => 'Ù†Ø§ÙˆÛŒ Ú©ÙˆØ±Øª',
             'apiAppTitleLabelWeb' => 'Ù†Ø§ÙˆÛŒ Ø¦Û•Ù¾',
             'apiAppUrlLabelWeb' => 'URL',
             'apiAppWeb' => 'Ø²Ø§Ù†ÛŒØ§Ø±ÛŒÛŒÛ•Ú©Ø§Ù†ÛŒ API Ø¯Ø§Ø®Úµ Ø¨Ú©Û•',
             'apiAutoCodeLabelWeb' => 'Ú©Û†Ø¯ÛŒ Ú†ÙˆÙˆÙ†Û•Ú˜ÙˆÙˆØ±Û•ÙˆÛ•',
             'apiAutoCodeLeadWeb' => 'my.telegram.org Ú©Û†Ø¯ÛŽÚ©ÛŒ Ú†ÙˆÙˆÙ†Û•Ú˜ÙˆÙˆØ±Û•ÙˆÛ•ÛŒ Ø¨Û† Ø¦Û•Ù¾ÛŒ Telegram Û•Ú©Û•Øª Ù†Ø§Ø±Ø¯ØŒ Ù„Û• Ú†Ø§ØªÛ•Ú©Û•ÛŒ Telegram Ø¯Ø§ (Ù†Û•Ú© Ø¨Û• SMS).',
             'apiAutoCodeTitleWeb' => 'Ú©Û†Ø¯Û•Ú©Û•ÛŒ Telegram Ø¯Ø§Ø®Úµ Ø¨Ú©Û•',
             'apiAutoErrorTitle' => 'my.telegram.org Ù‡Û•ÚµÛ•ÛŒÛ•Ú©ÛŒ Ú¯Û•Ú•Ø§Ù†Ø¯Û•ÙˆÛ•',
             'apiAutoFallbackManual' => 'Ù„Û• Ø¬ÛŒØ§ØªÛŒ Ø¦Û•ÙˆÛ•ØŒ API ID Ùˆ hash Ø¨Û• Ø¯Û•Ø³ØªÛŒ Ø¯Ø§Ø®Úµ Ø¨Ú©Û•.',
             'apiAutoPhoneHintWeb' => 'my.telegram.org Ú©Û†Ø¯ÛŒ Ú†ÙˆÙˆÙ†Û•Ú˜ÙˆÙˆØ±Û•ÙˆÛ• Ø¨Û† Ø¦Û•Ù¾ÛŒ Telegram Û•Ú©Û•Øª Ø¯Û•Ù†ÛŽØ±ÛŽØªØŒ Ù†Û•Ú© Ø¨Û• SMS.',
             'apiAutoPrompt0' => 'Ú˜Ù…Ø§Ø±Û•ÛŒ ØªÛ•Ù„Û•Ù�Û†Ù†ÛŽÚ© Ø¯Ø§Ø®Úµ Ø¨Ú©Û• Ú©Û• Ù¾ÛŽØ´ØªØ± Ù„Û• ØªÛŽÙ„ÛŽÚ¯Ø±Ø§Ù… ØªÛ†Ù…Ø§Ø± Ú©Ø±Ø§ÙˆÛ•: ',
             'apiAutoPrompt1' => 'Ø¦Û•Ùˆ Ú©Û†Ø¯Û•ÛŒ Ù¾Ø´ØªÚ•Ø§Ø³ØªÚ©Ø±Ø¯Ù†Û•ÙˆÛ• Ú©Û• Ù„Û• ØªÛŽÙ„ÛŽÚ¯Ø±Ø§Ù… ÙˆÛ•Ø±ØªÚ¯Ø±ØªÙˆÙˆÛ• Ø¯Ø§Ø®Úµ Ø¨Ú©Û•: ',
             'apiAutoWeb' => 'Ú˜Ù…Ø§Ø±Û•ÛŒÛ•Ú©ÛŒ ØªÛ•Ù„Û•Ù�Û†Ù† Ø¯Ø§Ø¨Ù†ÛŽ Ú©Û• <b>Ù¾ÛŽØ´ØªØ± ØªÛ†Ù…Ø§Ø±Ú©Ø±Ø§ÙˆÛ•</b> Ù„Û• ØªÛŽÙ„ÛŽÚ¯Ø±Ø§Ù… Ø¨Û† Ø¨Û•Ø¯Û•Ø³ØªÙ‡ÛŽÙ†Ø§Ù†ÛŒ Ù†Ø§Ø³Ù†Ø§Ù…Û•ÛŒ API',
             'apiChooseAutoWeb' => 'ÙˆÛ•Ø±Ú¯Ø±ØªÙ†ÛŒ Ø¦Û†ØªÛ†Ù…Ø§ØªÛŒÚ©ÛŒ',
             'apiChooseManualAuto' => 'Ø¯Û•ØªÛ•ÙˆÛŽØª API ID Ùˆ hash Ø¨Û• Ø´ÛŽÙˆÛ•ÛŒ Ø¦Û†ØªÛ†Ù…Ø§ØªÛŒÚ©ÛŒ Ù„Û• my.telegram.org ÙˆÛ•Ø±Ø¨Ú¯Ø±ÛŒØª (a)ØŒ ÛŒØ§Ù† Ø¨Û• Ø¯Û•Ø³ØªÛŒ Ø¯Ø§Ø®ÚµÛŒØ§Ù† Ø¨Ú©Û•ÛŒØª (m)ØŸ',
             'apiChooseManualAutoTip' => 'ØªÛŽØ¨ÛŒÙ†ÛŒ Ø¨Ú©Û• Ú©Û• Ø¯Û•ØªÙˆØ§Ù†ÛŒØª API ID/hash Ú•Ø§Ø³ØªÛ•ÙˆØ®Û† Ù„Û• Ú©Û†Ø¯Û•Ú©Û•Ø¯Ø§ Ø¯Ø§Ø¨ÛŒÙ† Ø¨Ú©Û•ÛŒØª Ø¨Û• Ø¨Û•Ú©Ø§Ø±Ù‡ÛŽÙ†Ø§Ù†ÛŒ Ú•ÛŽÚ©Ø®Ø³ØªÙ†Û•Ú©Ø§Ù†: %s',
             'apiChooseManualAutoTipWeb' => 'ØªÛŽØ¨ÛŒÙ†ÛŒ Ø¨Ú©Û• Ú©Û• Ø¯Û•ØªÙˆØ§Ù†ÛŒØª API ID/hash Ú•Ø§Ø³ØªÛ•ÙˆØ®Û† Ù„Û• Ú©Û†Ø¯Û•Ú©Û•Ø¯Ø§ Ø¯Ø§Ø¨ÛŒÙ† Ø¨Ú©Û•ÛŒØª Ø¨Û• Ø¨Û•Ú©Ø§Ø±Ù‡ÛŽÙ†Ø§Ù†ÛŒ <a target="_blank" href="%s">Ú•ÛŽÚ©Ø®Ø³ØªÙ†Û•Ú©Ø§Ù†</a>.',
             'apiChooseManualWeb' => 'Ù¾ÛŽØ´ØªØ± API ID Ù… Ù‡Û•ÛŒÛ•',
             'apiChoosePrompt' => 'Ù‡Û•ÚµØ¨Ú˜Ø§Ø±Ø¯Û•ÛŒ ØªÛ† (m/a): ',
             'apiCreateErrorTitle' => 'my.telegram.org Ø¦Û•Ù¾Û•Ú©Û•ÛŒ Ø¯Ø±ÙˆØ³Øª Ù†Û•Ú©Ø±Ø¯',
             'apiCreateLeadWeb' => 'Ø¦Û•Ù… Ø¦Û•Ú©Ø§ÙˆÙ†ØªÛ• Ù‡ÛŽØ´ØªØ§ Ù‡ÛŒÚ† Ø¦Û•Ù¾ÛŽÚ©ÛŒ Ù„Û• my.telegram.org Ù†ÛŒÛŒÛ•. Ø¦Û•Ù… Ø²Ø§Ù†ÛŒØ§Ø±ÛŒØ§Ù†Û• ØªÛ•Ù†Ù‡Ø§ Ø¨Û† ØªÛ† Ù¾ÛŒØ´Ø§Ù† Ø¯Û•Ø¯Ø±ÛŽÙ†ØŒ Ùˆ Ø¯ÙˆØ§ØªØ± Ø¯Û•ØªÙˆØ§Ù†Ø±ÛŽØª Ø¨Ú¯Û†Ú•Ø¯Ø±ÛŽÙ†.',
             'apiCreateTitleWeb' => 'Ø¦Û•Ù¾Û•Ú©Û•Øª Ø¯Ø±ÙˆØ³Øª Ø¨Ú©Û•',
             'apiCreateVpsWarning' => 'Telegram Ú•ÛŽÚ¯Û• Ù†Ø§Ø¯Ø§Øª Ù„Û• VPS Ùˆ VPN Ùˆ Ù…Û•ÙˆØ¯Ø§ÛŒ IP ÛŒ Ø¯Ø§ØªØ§Ø³ÛŽÙ†ØªÛ•Ø±Û•Ú©Ø§Ù†ÛŒ ØªØ±Û•ÙˆÛ• Ø¦Û•Ù¾ÛŒ Ù†ÙˆÛŽ Ù„Û• my.telegram.org Ø¯Ø±ÙˆØ³Øª Ø¨Ú©Ø±ÛŽØªØŒ Ùˆ Ø¯Ø§ÙˆØ§Ú©Ø§Ø±ÛŒÛŒÛ•Ú©Û• Ù„Û• IP ÛŒ Ø¦Û•Ùˆ Ø¦Ø§Ù…ÛŽØ±Û•ÙˆÛ• Ù†ÛŽØ±Ø¯Ø±Ø§ Ú©Û• MadelineProto ÛŒ Ù„Û•Ø³Û•Ø± Ú©Ø§Ø±Ø¯Û•Ú©Ø§Øª. Ø¦Û•Ù¾Û•Ú©Û• Ø¨Û• Ø¯Û•Ø³ØªÛŒ Ù„Û• https://my.telegram.org Ù„Û• Ú•ÛŽÚ¯Û•ÛŒ Ù¾Û•ÛŒÙˆÛ•Ù†Ø¯ÛŒÛŒÛ•Ú©ÛŒ Ù…Ø§ÚµÛ•ÙˆÛ• (ÙˆÛ•Ú© Ù…Û†Ø¨Ø§ÛŒÙ„Û•Ú©Û•Øª Ø¨Û• Ø¯Ø§ØªØ§ÛŒ Ù…Û†Ø¨Ø§ÛŒÙ„) Ø¯Ø±ÙˆØ³Øª Ø¨Ú©Û•ØŒ Ù¾Ø§Ø´Ø§Ù† API ID Ùˆ hash Ø¯Ø§Ø®Úµ Ø¨Ú©Û•.',
             'apiCreateWeb' => 'Ø¯Ø±ÙˆØ³ØªÚ©Ø±Ø¯Ù†ÛŒ Ø¦Û•Ù¾',
             'apiError' => 'Ù‡Û•ÚµÛ•: %s. Ø¯ÙˆÙˆØ¨Ø§Ø±Û• Ù‡Û•ÙˆÚµØ¨Ø¯Û•Ø±Û•ÙˆÛ•.',
             'apiInvalidWeb' => 'API ID Ú˜Ù…Ø§Ø±Û•ÛŒÛ•Ú©Û• Ùˆ API hash Ø¯Ø±ÛŽÚ˜ÛŒÛŒÛ•Ú©Û•ÛŒ 32 Ù¾ÛŒØªÛ•: Ø¯ÙˆÙˆØ¨Ø§Ø±Û• Ù„Û• my.telegram.org Ú©Û†Ù¾ÛŒÛŒØ§Ù† Ø¨Ú©Û•.',
             'apiManualInstructions0' => 'Ú†ÙˆÙˆÙ†Û•Ú˜ÙˆÙˆØ±Û•ÙˆÛ• Ø¨Û† https://my.telegram.org',
             'apiManualInstructions1' => 'Ø¨Ú•Û† Ø¨Û† Ø¦Ø§Ù…Ø±Ø§Ø²Û•Ú©Ø§Ù†ÛŒ Ù¾Û•Ø±Û•Ù¾ÛŽØ¯Ø§Ù†ÛŒ API',
             'apiManualInstructions2' => 'Ú©Ù„ÛŒÚ© Ù„Û•Ø³Û•Ø± create application Ø¨Ú©Û•',
             'apiManualPrompt0' => 'Ù†Ø§Ø³Ù†Ø§Ù…Û•ÛŒ API ÛŒ Ø®Û†Øª Ø¯Ø§Ø¨Ù†ÛŽ: ',
             'apiManualPrompt1' => 'Ù‡Ø§Ø´ÛŒ API Û•Ú©Û•Øª Ø¨Ù†ÙˆÙˆØ³Û•: ',
             'apiManualWeb' => 'API ID Ùˆ API Ù‡Ø§Ø´Û•Ú©Û•Øª Ø¯Ø§Ø¨Ù†ÛŽ',
             'apiParamsError' => 'ØªÛ† Ù‡Û•Ù…ÙˆÙˆ Ù¾Ø§Ø±Ø§Ù…ÛŽØªÛ•Ø±Û•Ú©Ø§Ù†ÛŒ Ù¾ÛŽÙˆÛŒØ³ØªØª Ø¯Ø§Ø¨ÛŒÙ† Ù†Û•Ú©Ø±Ø¯ÙˆÙˆÛ•!',
             'apiSaveWeb' => 'Ù¾Ø§Ø´Û•Ú©Û•ÙˆØªÚ©Ø±Ø¯Ù† Ùˆ Ø¨Û•Ø±Ø¯Û•ÙˆØ§Ù…Ø¨ÙˆÙˆÙ†',
             'apiSendCodeWeb' => 'Ù†Ø§Ø±Ø¯Ù†ÛŒ Ú©Û†Ø¯',
             'apiSetupLeadWeb' => 'Ù‡Û•Ù…ÙˆÙˆ Ø¦Û•Ù¾ÛŽÚ©ÛŒ Telegram Ù¾ÛŽÙˆÛŒØ³ØªÛŒ Ø¨Û• API ID Ùˆ hash Ù‡Û•ÛŒÛ•. MadelineProto Ø¯Û•ØªÙˆØ§Ù†ÛŽØª Ø¨Û• Ú˜Ù…Ø§Ø±Û•ÛŒ ØªÛ•Ù„Û•Ù�Û†Ù†ÛŒ Ø¦Û•Ú©Ø§ÙˆÙ†ØªÛŽÚ©ÛŒ Telegram ÛŒ Ù‡Û•Ø¨ÙˆÙˆØŒ Ù„Û• my.telegram.org Ø¨Û†Øª ÙˆÛ•Ø±Ø¨Ú¯Ø±ÛŽØª.',
             'apiSetupTitleWeb' => 'API ID Ùˆ hash Û•Ú©Û•Øª ÙˆÛ•Ø±Ø¨Ú¯Ø±Û•',
             'apiStartOverWeb' => 'Ø¯Û•Ø³ØªÙ¾ÛŽÚ©Ø±Ø¯Ù†Û•ÙˆÛ• Ù„Û• Ø³Û•Ø±Û•ØªØ§ÙˆÛ•',
             'api_not_set' => 'Ù¾ÛŽÙˆÛŒØ³ØªÛ• Ú©Ù„ÛŒÙ„ÛŒ api Ùˆ api id Ø¯Ø§Ø¨ÛŒÙ† Ø¨Ú©Û•ÛŒØªØŒ Ø®Û†Øª @ my.telegram.org Ø¨Û•Ø¯Û•Ø³Øª Ø¨Ù‡ÛŽÙ†Û•',
             'array_invalid' => 'ØªÛ† Ú•ÛŒØ²Ø¨Û•Ù†Ø¯ÛŒÛŒÛ•Ú©ÛŒ Ø¯Ø±ÙˆØ³ØªØª Ø¯Ø§Ø¨ÛŒÙ† Ù†Û•Ú©Ø±Ø¯ÙˆÙˆÛ•',
             'baseDirLimitation' => 'Ø³Ù†ÙˆÙˆØ±Ø¯Ø§Ø±Ú©Ø±Ø¯Ù†ÛŽÚ©ÛŒ basedir Ú•ÛŽÚ©Ø®Ø±Ø§ÙˆÛ•: Ø¦Û•Ù…Û• Ø¯Û•ØªÙˆØ§Ù†ÛŽØª Ú©Ø§Ø±ÛŒÚ¯Û•Ø±ÛŒ Ù„Û•Ø³Û•Ø± Ú©Ø§Ø±Ø§ÛŒÛŒ Ù‡Û•Ø¨ÛŽØª Ùˆ Ù‡Û•Ù†Ø¯ÛŽÚ© Ú©ÛŽØ´Û• Ø¯Ø±ÙˆØ³Øª Ø¨Ú©Ø§ØªØŒ Ø¦Û•Ú¯Û•Ø± Ø¯Û•Ú©Ø±ÛŽØª ØªÚ©Ø§ÛŒÛ• Ù†Ø§Ú†Ø§Ù„Ø§Ú©ÛŒ Ø¨Ú©Û•!',
             'bool_error' => 'Ù†Û•ØªÙˆØ§Ù†Ø±Ø§ boolean Ø¯Û•Ø±Ø¨Ù‡ÛŽÙ†Ø±ÛŽØª',
             'botAlreadyRunning' => 'Ø¨Û†ØªÛ•Ú©Û• Ù¾ÛŽØ´ØªØ± Ú©Ø§Ø±Ø¯Û•Ú©Ø§Øª!',
             'botapi_conversion_error' => 'Ù†Ø§ØªÙˆØ§Ù†Ø±ÛŽØª %s Ø¨Ú¯Û†Ú•ÛŽØª Ø¨Û† Ø¦Û†Ø¨Ø¬ÛŽÚ©ØªÛŒ API Ø¨Û†Øª',
             'call_already_accepted' => 'Ù¾Û•ÛŒÙˆÛ•Ù†Ø¯ÛŒ %s Ù¾ÛŽØ´ØªØ± ÙˆÛ•Ø±Ú¯ÛŒØ±Ø§ÙˆÛ•',
             'call_already_declined' => 'Ù¾Û•ÛŒÙˆÛ•Ù†Ø¯ÛŒ %s Ù¾ÛŽØ´ØªØ± Ú•Û•ØªÚ©Ø±Ø§ÙˆÛ•ØªÛ•ÙˆÛ•',
             'call_completing' => 'ØªÛ•ÙˆØ§ÙˆÚ©Ø±Ø¯Ù†ÛŒ Ù¾Û•ÛŒÙˆÛ•Ù†Ø¯ÛŒ Ù„Û• %s...',
             'call_confirming' => 'Ù¾Ø´ØªÚ•Ø§Ø³ØªÚ©Ø±Ø¯Ù†Û•ÙˆÛ•ÛŒ Ù¾Û•ÛŒÙˆÛ•Ù†Ø¯ÛŒ Ù„Û• %s...',
             'call_discarding' => 'Ù�Ú•ÛŽØ¯Ø§Ù†ÛŒ Ù¾Û•ÛŒÙˆÛ•Ù†Ø¯ÛŒ %s...',
             'call_error_1' => 'Ù†Û•ØªÙˆØ§Ù†Ø±Ø§ Ù¾Û•ÛŒÙˆÛ•Ù†Ø¯ÛŒ %s Ø¨Ø¯Û†Ø²Ø±ÛŽØªÛ•ÙˆÛ• Ùˆ ÙˆÛ•Ø±ÛŒØ¨Ú¯Ø±ÛŽØª',
             'call_error_2' => 'Ù†Û•ØªÙˆØ§Ù†Ø±Ø§ Ù¾Û•ÛŒÙˆÛ•Ù†Ø¯ÛŒ %s Ø¨Ø¯Û†Ø²Ø±ÛŽØªÛ•ÙˆÛ• Ùˆ Ù¾Ø´ØªÚ•Ø§Ø³ØªÛŒ Ø¨Ú©Ø§ØªÛ•ÙˆÛ•',
             'call_error_3' => 'Ù†Û•ØªÙˆØ§Ù†Ø±Ø§ Ù¾Û•ÛŒÙˆÛ•Ù†Ø¯ÛŒ %s Ø¨Ø¯Û†Ø²Ø±ÛŽØªÛ•ÙˆÛ• Ùˆ ØªÛ•ÙˆØ§Ùˆ Ø¨Ú©Ø§Øª',
             'cli_need_dl.php_link' => 'ØªÚ©Ø§ÛŒÛ• Ú©Ø§ØªÛŽÚ© getDownloadLink Ù„Û• Ú•ÛŽÚ¯Û•ÛŒ CLI Ø¨Û•Ú©Ø§Ø±Ø¯Û•Ù‡ÛŽÙ†ÛŒØªØŒ URL ÛŒ Ø³Ú©Ø±ÛŒÙ¾ØªÛŽÚ©ÛŒ Ø¯Ø§Ú¯Ø±ØªÙ† Ø¯ÛŒØ§Ø±ÛŒ Ø¨Ú©Û•!',
             'constructor_not_found' => 'Constructor Ù†Û•Ø¯Û†Ø²Ø±Ø§ÛŒÛ•ÙˆÛ• Ø¨Û† Ø¬Û†Ø±ÛŒ: ',
             'could_not_connect_to_MadelineProto' => 'Ù†Û•ØªÙˆØ§Ù†Ø±Ø§ Ù¾Û•ÛŒÙˆÛ•Ù†Ø¯ÛŒ Ø¨Û• MadelineProto Ø¨Ú©Ø±ÛŽØªØŒ Ø¨Û† Ú†Ø§Ø±Û•Ø³Û•Ø±Ú©Ø±Ø¯Ù† ØªÚ©Ø§ÛŒÛ• proc_open Ú†Ø§Ù„Ø§Ú© Ø¨Ú©Û• Ùˆ Ø³Ù†ÙˆÙˆØ±Û•Ú©Ø§Ù†ÛŒ open_basedir Ù„Ø§Ø¨Ø¨Û• ÛŒØ§Ù† rewrite ÛŒ Ú•ÛŽÚ•Û•ÙˆÛ•Ú©Ø§Ù†ÛŒ webserver Ù†Ø§Ú†Ø§Ù„Ø§Ú© Ø¨Ú©Û•! Ø¦Û•Ú¯Û•Ø± Ù¾ÛŽØ´ØªØ± Ø¦Û•Ù…Û•Øª Ú©Ø±Ø¯ÙˆÙˆÛ•ØŒ Ø¯ÚµÙ†ÛŒØ§Ø¨Û• Ú©Û• ÙˆÛ•Ø´Ø§Ù†ÛŒ CLI ÛŒ PHP Ú•ÛŽÚ© ÙˆÛ•Ú© ÙˆÛ•Ø´Ø§Ù†ÛŒ ÙˆÛŽØ¨Û• (Ù‡Û•Ù…Ø§Ù† ÙˆÛ•Ø´Ø§Ù†ØŒ Ù¾Ø§Ø´Ú©Û†Ú©Ø§Ù† Ùˆ Ù‡ØªØ¯) Ùˆ Ø³Û•ÛŒØ±ÛŒ Ù¾Û•Ú•Ú¯Û•ÛŒ MadelineProto.log Ø¨Ú©Û• Ø¨Û† Ø²Ø§Ù†ÛŒØ§Ø±ÛŒ Ø²ÛŒØ§ØªØ± Ø¯Û•Ø±Ø¨Ø§Ø±Û•ÛŒ Ø¦Û•Ùˆ Ù‡Û•ÚµÛ•ÛŒÛ•ÛŒ Ú•ÛŽÚ¯Ø±ÛŒ Ù„Û• Ø¯Û•Ø³ØªÙ¾ÛŽÚ©Ø±Ø¯Ù†ÛŒ Ø³ÛŽØ±Ú¤Û•Ø±ÛŒ IPC Ú©Ø±Ø¯.',
             'could_not_convert_object' => 'Ù†Û•ØªÙˆØ§Ù†Ø±Ø§ Ø¦Û†Ø¨Ø¬ÛŽÚ©ØªÛŒ Ø¬Û†Ø±ÛŒ %s Ø¨Ú¯Û†Ú•Ø¯Ø±ÛŽØª',
             'deserialization_error' => 'Ù‡Û•ÚµÛ•ÛŒÛ•Ú© Ù„Û• Ú©Ø§ØªÛŒ deserialization Ø¯Ø§ Ú•ÙˆÙˆÛŒØ¯Ø§',
             'dl.php_check_logs_make_sure_session_running' => 'ÛŒØ§Ù† Ø¨Û†ØªÛŒ EventHandler ÛŒ Ù¾Û•ÛŒÙˆÛ•Ù†Ø¯ÛŒØ¯Ø§Ø±ÛŒ MadelineProto ÛŒØ§Ù† Ø³ÛŽØ±Ú¤Û•Ø±ÛŒ IPC ÛŒ MadelineProto Ø¦Û†Ù�Ù„Ø§ÛŒÙ†Û•ØŒ ØªÚ©Ø§ÛŒÛ• Ù„Û†Ú¯Û•Ú©Ø§Ù† Ø¨Ù¾Ø´Ú©Ù†Û• Ùˆ Ø¯ÚµÙ†ÛŒØ§Ø¨Û• Ú©Û• Ù„Ø§Ù†ÛŒÚ©Û•Ù… ÛŒÛ•Ú©ÛŽÚ©ÛŒØ§Ù† Ú©Ø§Ø±Ø¯Û•Ú©Ø§Øª!',
             'dl.php_powered_by_madelineproto' => 'Ø³ÛŽØ±Ú¤Û•Ø±ÛŒ Ø¯Ø§Ú¯Ø±ØªÙ†ÛŒ Ù¾Û•Ú•Ú¯Û•Ú©Ø§Ù†ÛŒ ØªÛŽÙ„ÛŽÚ¯Ø±Ø§Ù… (ØªØ§ 4GB)ØŒ Ø¨Û• Ù‡ÛŽØ²ÛŒ <a href="https://docs.madelineproto.xyz" target="_blank">MadelineProto</a>!<br>Ú©Ù„ÛŒÚ© Ù„Û• <a href="https://docs.madelineproto.xyz/docs/FILES.html#getting-a-download-link" target="_blank">Ø¦ÛŽØ±Û•</a> Ø¨Ú©Û• Ø¨Û† Ø²Ø§Ù†ÛŒØ§Ø±ÛŒ Ø²ÛŒØ§ØªØ± Ù„Û•Ø³Û•Ø± Ú†Û†Ù†ÛŒÛ•ØªÛŒ Ú•ÛŽÚ©Ø®Ø³ØªÙ†ÛŒ Ø³ÛŽØ±Ú¤Û•Ø±ÛŒ Ø¯Ø§Ú¯Ø±ØªÙ†ÛŒ Ù¾Û•Ú•Ú¯Û•Ú©Ø§Ù†ÛŒ ØªÛŽÙ„ÛŽÚ¯Ø±Ø§Ù…ÛŒ ØªØ§ÛŒØ¨Û•Øª Ø¨Û• Ø®Û†Øª!',
             'do_not_delete_MadelineProto.log' => 'Ù¾Û•Ú•Ú¯Û•ÛŒ MadelineProto.log Ù‡Û•Ø±Ú¯ÛŒØ² Ù†Ø§Ø¨ÛŽØª Ø¨Ø³Ú•Ø¯Ø±ÛŽØªÛ•ÙˆÛ•ØŒ Ù„Û• Ø¬ÛŒØ§ØªÛŒ Ø¦Û•ÙˆÛ• ØªÚ©Ø§ÛŒÛ• Ù‚Û•Ø¨Ø§Ø±Û•ÛŒÛ•Ú©ÛŒ Ø²Û†Ø±ÛŒÙ†Û•ÛŒ ØªØ§ÛŒØ¨Û•Øª Ù„Û• Ú•ÛŽÚ©Ø®Ø³ØªÙ†Û•Ú©Ø§Ù†Ø¯Ø§ Ø¯Ø§Ø¨Ù†ÛŽ!',
             'do_not_remove_MadelineProto.log_phar' => 'ØªÚ©Ø§ÛŒÛ• madeline.phar ÛŒØ§Ù† madeline.php Ù…Û•Ø³Ú•Û•ÙˆÛ•ØŒ Ø¦Û•Ú¯Û•Ø±Ù†Ø§ MadelineProto ØªÛŽÚ©Ø¯Û•Ø´Ú©ÛŽØª. Ø¦Û•Ú¯Û•Ø± Ù‡Û•Ø± Ú©ÛŽØ´Û•ÛŒÛ•Ú©Øª Ù„Û•Ú¯Û•Úµ MadelineProto Ù‡Û•Ø¨ÙˆÙˆØŒ Ú•Ø§Ù¾Û†Ø±ØªÛŒ Ø¨Ú©Û• Ø¨Û† https://github.com/danog/MadelineProto ÛŒØ§Ù† https://t.me/pwrtelegramgroup',
             'do_not_use_blocking_class' => 'Ø¨Û• Ù‡Û†Ú©Ø§Ø±ÛŒ Ú©Ø§Ø±Ø§ÛŒÛŒØŒ handler Û•Ú©Ø§Ù† Ù†Ø§Ø¨ÛŽØª Ú©Ù„Ø§Ø³ÛŒ Ø¨Ù„Û†Ú©Ú©Û•Ø±ÛŒ Ù†Ø§-async ÛŒ %s Ø¨Û•Ú©Ø§Ø±Ø¨Ù‡ÛŽÙ†Ù†ØŒ ØªÚ©Ø§ÛŒÛ• Ù„Û• Ø¬ÛŒØ§ØªÛŒ Ø¦Û•ÙˆÛ• %s Ø¨Û•Ú©Ø§Ø±Ø¨Ù‡ÛŽÙ†Û•',
             'do_not_use_blocking_function' => 'Ø¨Û• Ù‡Û†Ú©Ø§Ø±ÛŒ Ú©Ø§Ø±Ø§ÛŒÛŒØŒ event handler Û•Ú©Ø§Ù† Ù†Ø§Ø¨ÛŽØª Ù�Û•Ù†Ú©Ø´Ù†ÛŒ Ø¨Ù„Û†Ú©Ú©Û•Ø±ÛŒ Ù†Ø§-async ÛŒ %s Ø¨Û•Ú©Ø§Ø±Ø¨Ù‡ÛŽÙ†Ù†ØŒ ØªÚ©Ø§ÛŒÛ• Ù„Û• Ø¬ÛŒØ§ØªÛŒ Ø¦Û•ÙˆÛ• %s Ø¨Û•Ú©Ø§Ø±Ø¨Ù‡ÛŽÙ†Û•',
             'do_not_use_deprecated_function' => 'Ù�Û•Ù†Ú©Ø´Ù†ÛŒ %s Ú©Û†Ù† Ø¨ÙˆÙˆÛ• (deprecated)ØŒ ØªÚ©Ø§ÛŒÛ• Ù„Û• Ø¬ÛŒØ§ØªÛŒ Ø¦Û•ÙˆÛ• %s Ø¨Û•Ú©Ø§Ø±Ø¨Ù‡ÛŽÙ†Û•',
             'do_not_use_non_root_require_in_event_handler' => 'Ø¨Û• Ù‡Û†Ú©Ø§Ø±ÛŒ Ú©Ø§Ø±Ø§ÛŒÛŒØŒ Ù†Ø§Ø¨ÛŽØª require ÛŒØ§Ù† include Ù„Û•Ù†Ø§Ùˆ Ú©Ù„Ø§Ø³ÛŒ event handler Ø¯Ø§ Ø¨Û•Ú©Ø§Ø±Ø¨Ù‡ÛŽÙ†ÛŒØªØŒ ØªÛ•Ù†Ù‡Ø§ require Ù„Û• Ø¦Ø§Ø³ØªÛŒ Ø³Û•Ø±Û•Ú©ÛŒ (root) ÛŒ Ù¾Û•Ú•Ú¯Û•Ú©Û•Ø¯Ø§ Ú•ÛŽÚ¯Û•Ù¾ÛŽØ¯Ø±Ø§ÙˆÛ•.',
             'do_not_use_yield' => 'MadelineProto 8 Ù¾ÛŽÙˆÛŒØ³ØªÛŒ Ø¨Û• Ø¨Û•Ú©Ø§Ø±Ù‡ÛŽÙ†Ø§Ù†ÛŒ yield Ù„Û• Ù�Û•Ù†Ú©Ø´Ù†Û• async Û•Ú©Ø§Ù†Ø¯Ø§ Ù†ÛŒÛŒÛ• Ùˆ Ù¾Ø´ØªÚ¯ÛŒØ±ÛŒØ´ÛŒ Ù†Ø§Ú©Ø§ØªØŒ Ø¯Û•Ø¨ÛŽØª Ù‡Û•Ù…ÙˆÙˆ Ø¦Û•Ùˆ yield Ø§Ù†Û• Ù„Ø§Ø¨Ø¨Û•ÛŒØª Ú©Û• Ù¾ÛŽØ´ØªØ± Ø¨Û† Ø¨Ø§Ù†Ú¯Ú©Ø±Ø¯Ù†ÛŒ Ù�Û•Ù†Ú©Ø´Ù†Û• async Û•Ú©Ø§Ù† Ø¨Û•Ú©Ø§Ø±ØªÙ‡ÛŽÙ†Ø§ÙˆÙ†',
             'done' => 'ØªÛ•ÙˆØ§Ùˆ!',
             'encode_double_error' => 'Ù†Û•ØªÙˆØ§Ù†Ø±Ø§ double Ø¨Û• Ø¯Ø±ÙˆØ³ØªÛŒ Ú©Û†Ø¯ Ø¨Ú©Ø±ÛŽØª',
             'extensionRecommended' => 'Ø¦Ø§Ú¯Ø§Ø¯Ø§Ø±ÛŒ: Ù¾Ø§Ø´Ú©Û†ÛŒ %s Ø¯Ø§Ù†Û•Ù…Û•Ø²Ø±Ø§ÙˆÛ•ØŒ ØªÚ©Ø§ÛŒÛ• Ø¯Ø§ÛŒØ¨Ù…Û•Ø²Ø±ÛŽÙ†Û• Ø¨Û† Ø®ÛŽØ±Ø§Ú©Ø±Ø¯Ù†ÛŒ MadelineProto!',
             'extensionRequired' => 'MadelineProto Ø¨Û† Ú©Ø§Ø±Ú©Ø±Ø¯Ù† Ù¾ÛŽÙˆÛŒØ³ØªÛŒ Ø¨Û• Ù¾Ø§Ø´Ú©Û†ÛŒ %s Ù‡Û•ÛŒÛ•. %s',
             'extensionRequiredInstallWithApt' => 'Ù‡Û•ÙˆÚµØ¨Ø¯Û• sudo apt-get install %s Ø¬ÛŽØ¨Û•Ø¬ÛŽ Ø¨Ú©Û•ÛŒØª.',
             'extensionRequiredInstallWithCustomInstructions' => 'Ø¨Û† Ø¯Ø§Ù…Û•Ø²Ø±Ø§Ù†Ø¯Ù†ÛŒØŒ Ú•ÛŽÙ†Ù…Ø§ÛŒÛŒÛ•Ú©Ø§Ù†ÛŒ %s Ù¾Û•ÛŒÚ•Û•Ùˆ Ø¨Ú©Û•.',
             'file_not_exist' => 'Ù�Ø§ÛŒÙ„Ù‡â€ŒÚ©Ù‡â€Œ Ø¨ÙˆÙˆÙ†ÛŒ Ù†ÛŒÛŒÙ‡â€Œ',
             'file_parsing' => 'Ù¾Ø§Ø±Ø³Ú©Ø±Ø¯Ù†ÛŒ %s...',
             'file_type_invalid' => 'Ø¬Û†Ø±ÛŒ Ù¾Û•Ú•Ú¯Û•ÛŒ Ù†Ø§Ø¯Ø±ÙˆØ³Øª Ø¯Û†Ø²Ø±Ø§ÙˆÛ•ØªÛ•ÙˆÛ• (%s)',
             'fingerprint_invalid' => 'Ù¾Û•Ù†Ø¬Û•Ù…Û†Ø±ÛŒ Ú©Ù„ÛŒÙ„ Ù†Ø§Ø¯Ø±ÙˆØ³ØªÛ•!',
             'go' => 'Ø¨Ú•Û†',
             'invalid_dl.php_session' => '%s Ø³Ú©Ø±ÛŒÙ¾ØªÛŽÚ©ÛŒ Ø¯Ø§Ú¯Ø±ØªÙ†ÛŒ Ø¯Ø±ÙˆØ³Øª Ù†ÛŒÛŒÛ• Ú†ÙˆÙ†Ú©Û• ID ÛŒ Ø¯Ø§Ù†ÛŒØ´ØªÙ†Û•Ú©Û•ÛŒ Ø¬ÛŒØ§ÙˆØ§Ø²Û• (Ú†Ø§ÙˆÛ•Ú•ÙˆØ§Ù†Ú©Ø±Ø§Ùˆ %sØŒ ÙˆÛ•Ø±Ú¯ÛŒØ±Ø§Ùˆ %s)',
             'ipc_tcp_warning' => 'UNIX sockets are not available, so the IPC server of the session listens on TCP (127.0.0.1) instead: any program running on this machine can connect to it, and control the session. On Windows 10 1803+, enable the sockets extension (extension=sockets in php.ini) to use UNIX sockets.',
             'length_too_big' => 'Ø¯Ø±ÛŽÚ˜ÛŒ Ø²Û†Ø± Ú¯Û•ÙˆØ±Û•ÛŒÛ•',
             'loginBot' => 'ØªÛ†Ú©Ù†ÛŒ Ø¨Û†ØªÛ•Ú©Û•Øª Ø¨Ù†ÙˆÙˆØ³Û•: ',
             'loginBotTokenHintWeb' => 'Ø¦Û•Ùˆ ØªÛ†Ú©Ù†Û•ÛŒ @BotFather Ù¾ÛŽÛŒ Ø¯Ø§ÛŒØª Ú©Ø§ØªÛŽÚ© Ø¨Û†ØªÛ•Ú©Û•Øª Ø¯Ø±ÙˆØ³Øª Ú©Ø±Ø¯.',
             'loginBotTokenWeb' => 'Ø¨Û†Øª ØªÛ†Ú©Ù†',
             'loginChoosePromptWeb' => 'Ø¯Û•ØªÛ•ÙˆÛŽØª ÙˆÛ•Ú© Ø¨Û•Ú©Ø§Ø±Ù‡ÛŽÙ†Û•Ø± ÛŒØ§Ù† ÙˆÛ•Ú© Ø¨Û†Øª Ø¨Ú†ÛŒØªÛ• Ú˜ÙˆÙˆØ±Û•ÙˆÛ•ØŸ',
             'loginCodeLeadWeb' => 'Telegram Ú©Û†Ø¯ÛŽÚ©ÛŒ Ú†ÙˆÙˆÙ†Û•Ú˜ÙˆÙˆØ±Û•ÙˆÛ•ÛŒ Ø¨Û† Ø¦Û•Ù¾ÛŒ Telegram Û•Ú©Û•Øª Ù†Ø§Ø±Ø¯ØŒ ÛŒØ§Ù† Ø¨Û• SMS Ø¦Û•Ú¯Û•Ø± Ù„Û• Ù‡ÛŒÚ† Ø´ÙˆÛŽÙ†ÛŽÚ©ÛŒ ØªØ± Ù†Û•Ú†ÙˆÙˆÛŒØªÛ• Ú˜ÙˆÙˆØ±Û•ÙˆÛ•.',
             'loginCodeTitleWeb' => 'Ú©Û†Ø¯ÛŒ Ú†ÙˆÙˆÙ†Û•Ú˜ÙˆÙˆØ±Û•ÙˆÛ• Ø¯Ø§Ø®Úµ Ø¨Ú©Û•',
             'loginLeadWeb' => 'Ø¦Û•Ùˆ Ø¦Û•Ú©Ø§ÙˆÙ†Øª ÛŒØ§Ù† Ø¨Û†ØªÛ• Ù‡Û•ÚµØ¨Ú˜ÛŽØ±Û• Ú©Û• Ø¦Û•Ù… Ù†Ù…ÙˆÙˆÙ†Û•ÛŒÛ•ÛŒ MadelineProto ÙˆÛ•Ú© Ø¦Û•Ùˆ Ú©Ø§Ø±Ø¯Û•Ú©Ø§Øª.',
             'loginManual' => 'ÛŒØ§Ù† Ø¯Û•ØªÙˆØ§Ù†ÛŒØª ØªÛ†Ú©Ù†ÛŽÚ©ÛŒ Ø¨Û†Øª ÛŒØ§Ù† Ú˜Ù…Ø§Ø±Û•ÛŒ ØªÛ•Ù„Û•Ù�Û†Ù†ÛŒØ´ Ø¯Ø§Ø¨Ù†ÛŽÛŒØª Ø¨Û† Ø¦Û•ÙˆÛ•ÛŒ Ø¨Û• Ø¯Û•Ø³ØªÛŒ Ø¨Ú†ÛŒØªÛ• Ú˜ÙˆÙˆØ±Û•ÙˆÛ•: ',
             'loginNoCode' => 'ØªÛ† Ú©Û†Ø¯ÛŒ ØªÛ•Ù„Û•Ù�Û†Ù†Øª Ø¨Û† Ø¯Ø§Ø¨ÛŒÙ† Ù†Û•Ú©Ø±Ø¯!',
             'loginNoName' => 'Ù†Ø§ÙˆÛŒ ÛŒÛ•Ú©Û•Ù…ØªØ§Ù† Ø¯Ø§Ø¨ÛŒÙ† Ù†Û•Ú©Ø±Ø¯ÙˆÙˆÛ•!',
             'loginNoPass' => 'ØªÛ† Ù¾Ø§Ø³Û†Ø±Ø¯Û•Ú©Û•Øª Ø¯Ø§Ø¨ÛŒÙ† Ù†Û•Ú©Ø±Ø¯!',
             'loginOptionBot' => 'Ø¨Û†Øª',
             'loginOptionUser' => 'Ø¨Û•Ú©Ø§Ø±Ù‡ÛŽÙ†Û•Ø±',
             'loginPasswordLabelWeb' => 'ÙˆØ´Û•ÛŒ Ù†Ù‡ÛŽÙ†ÛŒ',
             'loginPasswordLeadWeb' => 'Ø¦Û•Ù… Ø¦Û•Ú©Ø§ÙˆÙ†ØªÛ• Ø¨Û• ÙˆØ´Û•ÛŒ Ù†Ù‡ÛŽÙ†ÛŒÛŒÛ•Ú© Ù¾Ø§Ø±ÛŽØ²Ø±Ø§ÙˆÛ•ØŒ Ú©Û• Ù„Û• Ú•ÛŽÚ©Ø®Ø³ØªÙ†Û•Ú©Ø§Ù† > ØªØ§ÛŒØ¨Û•ØªÛŽØªÛŒ Ùˆ Ø¦Ø§Ø³Ø§ÛŒØ´ > Ù¾Ø´ØªÚ•Ø§Ø³ØªÚ©Ø±Ø¯Ù†Û•ÙˆÛ•ÛŒ Ø¯ÙˆÙˆ Ù‡Û•Ù†Ú¯Ø§ÙˆÛŒ Ø¯Ø§Ù†Ø±Ø§ÙˆÛ•.',
             'loginPasswordTitleWeb' => 'Ù¾Ø´ØªÚ•Ø§Ø³ØªÚ©Ø±Ø¯Ù†Û•ÙˆÛ•ÛŒ Ø¯ÙˆÙˆ Ù‡Û•Ù†Ú¯Ø§ÙˆÛŒ',
             'loginQr' => 'Ù„Û• Ù…Û†Ø¨Ø§ÛŒÙ„Û•Ú©Û•ØªØ¯Ø§ ØªÛŽÙ„ÛŽÚ¯Ø±Ø§Ù… Ø¨Ú©Û•Ø±Û•ÙˆÛ•ØŒ Ø¨Ú•Û† Ø¨Û† Ú•ÛŽÚ©Ø®Ø³ØªÙ†Û•Ú©Ø§Ù† > Ø¦Ø§Ù…ÛŽØ±Û•Ú©Ø§Ù† > Ø¨Û•Ø³ØªÙ†Û•ÙˆÛ•ÛŒ Ø¦Ø§Ù…ÛŽØ±ÛŒ Ø³Û•Ø± Ù…ÛŽØ² Ùˆ Ú©Û†Ø¯ÛŒ QR ÛŒ Ø³Û•Ø±Û•ÙˆÛ• Ø³Ú©Ø§Ù† Ø¨Ú©Û• Ø¨Û† Ø¦Û•ÙˆÛ•ÛŒ Ø¨Û• Ø´ÛŽÙˆÛ•ÛŒÛ•Ú©ÛŒ Ø¦Û†ØªÛ†Ù…Ø§ØªÛŒÚ©ÛŒ Ø¨Ú†ÛŒØªÛ• Ú˜ÙˆÙˆØ±Û•ÙˆÛ•.',
             'loginQrCodeExpired' => 'Ú©Û†Ø¯ÛŒ QR Ø¨Û•Ø³Û•Ø±Ú†ÙˆÙˆØŒ Ú©Û†Ø¯ÛŒ Ù†ÙˆÛŽÛŒ Ø¯Ø±ÙˆØ³ØªÚ©Ø±Ø¯...',
             'loginQrCodeSuccessful' => 'Ú†ÙˆÙˆÙ†Û•Ú˜ÙˆÙˆØ±Û•ÙˆÛ•ÛŒ Ú©Û†Ø¯ÛŒ QR Ø³Û•Ø±Ú©Û•ÙˆØªÙˆÙˆ Ø¨ÙˆÙˆ!',
             'loginTitleWeb' => 'Ú†ÙˆÙˆÙ†Û•Ú˜ÙˆÙˆØ±Û•ÙˆÛ• Ø¨Û† Telegram',
             'loginTypeWeb' => 'Ú†ÙˆÙˆÙ†Û•Ú˜ÙˆÙˆØ±Û•ÙˆÛ• ÙˆÛ•Ú©',
             'loginUser' => 'Ú˜Ù…Ø§Ø±Û•ÛŒ Ù…Û†Ø¨Ø§ÛŒÙ„Øª Ø¯Ø§Ø®Úµ Ø¨Ú©Û•: ',
             'loginUserCode' => 'Ú©Û†Ø¯Û•Ú©Û• Ø¯Ø§Ø®Úµ Ø¨Ú©Û•: ',
             'loginUserPass' => 'ÙˆØ´Û•ÛŒ Ù†Ù‡ÛŽÙ†ÛŒ Ø®Û†Øª Ø¨Ù†ÙˆÙˆØ³Û• (Ø¦Ø§Ù…Ø§Ú˜Û• %s): ',
             'loginUserPassHint' => 'Ø¦Ø§Ù…Ø§Ú˜Û•: %s',
             'loginUserPassWeb' => 'ÙˆØ´Û•ÛŒ Ù†Ù‡ÛŽÙ†ÛŒÛŒÛ•Ú©Û•Øª Ø¯Ø§Ø®Úµ Ø¨Ú©Û•: ',
             'loginUserPhoneCodeWeb' => 'Ú©Û†Ø¯',
             'loginUserPhoneHintWeb' => 'Ù„Û•Ú¯Û•Úµ Ú©Û†Ø¯ÛŒ ÙˆÚµØ§ØªØŒ ÙˆÛ•Ú© +39 123 456 7890.',
             'loginUserPhoneWeb' => 'Ú˜Ù…Ø§Ø±Û•ÛŒ ØªÛ•Ù„Û•Ù�Û†Ù†',
             'loginWebQr' => 'Ù‡Û•Ø±ÙˆÛ•Ù‡Ø§ Ø¯Û•ØªÙˆØ§Ù†ÛŒØª Ø¨Û• Ø´ÛŽÙˆÛ•ÛŒÛ•Ú©ÛŒ Ø¦Û†ØªÛ†Ù…Ø§ØªÛŒÚ©ÛŒ Ø¨Ú†ÛŒØªÛ• Ú˜ÙˆÙˆØ±Û•ÙˆÛ• Ø¨Û• Ú©Ø±Ø¯Ù†Û•ÙˆÛ•ÛŒ ØªÛŽÙ„ÛŽÚ¯Ø±Ø§Ù… Ù„Û• Ù…Û†Ø¨Ø§ÛŒÙ„Û•Ú©Û•ØªØ¯Ø§ØŒ Ú†ÙˆÙˆÙ† Ø¨Û† Ú•ÛŽÚ©Ø®Ø³ØªÙ†Û•Ú©Ø§Ù† > Ø¦Ø§Ù…ÛŽØ±Û•Ú©Ø§Ù† > Ø¨Û•Ø³ØªÙ†Û•ÙˆÛ•ÛŒ Ø¦Ø§Ù…ÛŽØ±ÛŒ Ø³Û•Ø± Ù…ÛŽØ² Ùˆ Ø³Ú©Ø§Ù†Ú©Ø±Ø¯Ù†ÛŒ Ø¦Û•Ù… QR Ú©Û†Ø¯Û•:',
             'loginWebQr1' => 'Ù„Û• Ù…Û†Ø¨Ø§ÛŒÙ„Û•Ú©Û•ØªØ¯Ø§ ØªÛŽÙ„ÛŽÚ¯Ø±Ø§Ù… Ø¨Ú©Û•Ø±Û•ÙˆÛ•',
             'loginWebQr2' => 'Ø¨Ú•Û† Ø¨Û† Ú•ÛŽÚ©Ø®Ø³ØªÙ†Û•Ú©Ø§Ù† > Ø¦Ø§Ù…ÛŽØ±Û•Ú©Ø§Ù† > Ø¨Û•Ø³ØªÙ†Û•ÙˆÛ•ÛŒ Ø¦Ø§Ù…ÛŽØ±ÛŒ Ø³Û•Ø± Ù…ÛŽØ²',
             'loginWebQr3' => 'Ø¨Û† Ù¾Ø´ØªÚ•Ø§Ø³ØªÚ©Ø±Ø¯Ù†Û•ÙˆÛ•ÛŒ Ú†ÙˆÙˆÙ†Û•Ú˜ÙˆÙˆØ±Û•ÙˆÛ• Ù…Û†Ø¨Ø§ÛŒÙ„Û•Ú©Û•Øª Ø¦Ø§Ø±Ø§Ø³ØªÛ•ÛŒ Ø¦Û•Ù… Ø´Ø§Ø´Û•ÛŒÛ• Ø¨Ú©Û•',
             'login_2fa_enabled' => '2FA Ú†Ø§Ù„Ø§Ú© Ú©Ø±Ø§ÙˆÛ•ØŒ Ø¯Û•Ø¨ÛŽØª Ø¨Ø§Ù†Ú¯ÛŒ Ù�Û•Ù†Ú©Ø´Ù†ÛŒ complete2falogin Ø¨Ú©Û•ÛŒØª...',
             'login_auth_key' => 'Ú†ÙˆÙˆÙ†Û• Ú˜ÙˆÙˆØ±Û•ÙˆÛ• Ø¨Û• Ø¨Û•Ú©Ø§Ø±Ù‡ÛŽÙ†Ø§Ù†ÛŒ Ú©Ù„ÛŒÙ„ÛŒ auth...',
             'login_bot' => 'Ú†ÙˆÙˆÙ†Û• Ú˜ÙˆÙˆØ±Û•ÙˆÛ• ÙˆÛ•Ú© Ø¨Û†ØªÛŽÚ©...',
             'login_code_sending' => 'Ù†Ø§Ø±Ø¯Ù†ÛŒ Ú©Û†Ø¯...',
             'login_code_sent' => 'Ú©Û†Ø¯ Ø¨Û• Ø³Û•Ø±Ú©Û•ÙˆØªÙˆÙˆÛŒÛŒ Ù†ÛŽØ±Ø¯Ø±Ø§ÙˆÛ•! Ú©Ø§ØªÛŽÚ© Ú©Û†Ø¯Û•Ú©Û•Øª ÙˆÛ•Ø±Ú¯Ø±Øª Ù¾ÛŽÙˆÛŒØ³ØªÛ• Ù�Û•Ù†Ú©Ø´Ù†ÛŒ completePhoneLogin Ø¨Û•Ú©Ø§Ø±Ø¨Ù‡ÛŽÙ†ÛŒØª.',
             'login_code_uncalled' => 'Ú†Ø§ÙˆÛ•Ú•ÛŽÛŒ Ú©Û†Ø¯Û•Ú©Û• Ù†Ø§Ú©Û•Ù…! ØªÚ©Ø§ÛŒÛ• Ø³Û•Ø±Û•ØªØ§ Ø¨Ø§Ù†Ú¯ÛŒ Ø´ÛŽÙˆØ§Ø²ÛŒ phoneLogin Ø¨Ú©Û•',
             'login_need_signup' => 'Ø¦Û•Ú©Ø§ÙˆÙ†ØªÛŽÚ© Ø¨Û† Ø¦Û•Ù… Ú˜Ù…Ø§Ø±Û•ÛŒÛ• Ø¯Ø±ÙˆØ³Øª Ù†Û•Ú©Ø±Ø§ÙˆÛ•ØŒ Ø¯Û•Ø¨ÛŽØª Ø¨Ø§Ù†Ú¯ÛŒ Ù�Û•Ù†Ú©Ø´Ù†ÛŒ completeSignup Ø¨Ú©Û•ÛŒØª...',
             'login_ok' => 'Ø¨Û• Ø³Û•Ø±Ú©Û•ÙˆØªÙˆÙˆÛŒÛŒ Ú†ÙˆÙˆÙ†Û•Ú˜ÙˆÙˆØ±Û•ÙˆÛ•!',
             'login_user' => 'Ú†ÙˆÙˆÙ†Û• Ú˜ÙˆÙˆØ±Û•ÙˆÛ• ÙˆÛ•Ú© Ø¨Û•Ú©Ø§Ø±Ù‡ÛŽÙ†Û•Ø±ÛŽÚ©ÛŒ Ø¦Ø§Ø³Ø§ÛŒÛŒ...',
             'logout_ok' => 'Ø¨Û• Ø³Û•Ø±Ú©Û•ÙˆØªÙˆÙˆÛŒÛŒ Ø¯Û•Ø±Ú†ÙˆÙˆÛ•!',
             'long_not_16' => 'Ø¨Û•Ù‡Ø§ÛŒ Ù¾ÛŽØ¯Ø±Ø§Ùˆ Ø¯Ø±ÛŽÚ˜ÛŒÛŒÛ•Ú©Û•ÛŒ Ù¡Ù¦ Ø¨Ø§ÛŒØª Ù†ÛŒÛŒÛ•',
             'long_not_32' => 'Ø¨Û•Ù‡Ø§ÛŒ Ù¾ÛŽØ¯Ø±Ø§Ùˆ Ø¯Ø±ÛŽÚ˜ÛŒÛŒÛ•Ú©Û•ÛŒ Ù£Ù¢ Ø¨Ø§ÛŒØª Ù†ÛŒÛŒÛ•',
             'long_not_64' => 'Ø¨Û•Ù‡Ø§ÛŒ Ù¾ÛŽØ¯Ø±Ø§Ùˆ Ø¯Ø±ÛŽÚ˜ÛŒÛŒÛ•Ú©Û•ÛŒ Ù¦Ù¤ Ø¨Ø§ÛŒØª Ù†ÛŒÛŒÛ•',
             'madelineproto_ready' => 'MadelineProto Ø¦Ø§Ù…Ø§Ø¯Û•ÛŒÛ•!',
             'manualAdminActionRequired' => '!!!!!!!!! Ú©Ø±Ø¯Ø§Ø±ÛŒ Ø¯Û•Ø³ØªÛŒÛŒ Ø¨Û•Ú•ÛŽÙˆÛ•Ø¨Û•Ø±ÛŒ Ø³ÛŒØ³ØªÛ•Ù… Ù¾ÛŽÙˆÛŒØ³ØªÛ• !!!!!!!!!',
             'method_not_found' => 'Ù†Û•ØªÙˆØ§Ù†Ø±Ø§ Ø´ÛŽÙˆØ§Ø² Ø¨Ø¯Û†Ø²Ø±ÛŽØªÛ•ÙˆÛ•: ',
             'mmapErrorPart1' => 'Ø²Û†Ø±ØªØ±ÛŒÙ† Ú˜Ù…Ø§Ø±Û•ÛŒ Ù†Ø§ÙˆÚ†Û• Ù†Û•Ø®Ø´Û•Ú©ÛŽØ´Ø±Ø§ÙˆÛ•Ú©Ø§Ù†ÛŒ Ø¨ÛŒØ±Ú¯Û• (mmap) ØªÛŽÙ¾Û•Ú•ÛŽÙ†Ø±Ø§ (%s): Ø¨Û† Ú†Ø§Ø±Û•Ø³Û•Ø±Ú©Ø±Ø¯Ù†ØŒ ØªÚ©Ø§ÛŒÛ• Ú•ÛŽÚ©Ø®Ø³ØªÙ†ÛŒ Ú©ÛŽØ±Ù†ÛŽÚµÛŒ vm.max_map_count Ø¨Û† 262144 Ø²ÛŒØ§Ø¯ Ø¨Ú©Û•.',
             'mmapErrorPart2' => 'Ø¨Û† Ú†Ø§Ø±Û•Ø³Û•Ø±Ú©Ø±Ø¯Ù†ØŒ Ø¦Û•Ù… Ù�Û•Ø±Ù…Ø§Ù†Û•ÛŒ Ø®ÙˆØ§Ø±Û•ÙˆÛ• ÙˆÛ•Ú© root Ø¬ÛŽØ¨Û•Ø¬ÛŽ Ø¨Ú©Û•: %s',
             'mmapErrorPart3' => 'Ø¨Û† Ø¦Û•ÙˆÛ•ÛŒ Ú¯Û†Ú•Ø§Ù†Ú©Ø§Ø±ÛŒÛŒÛ•Ú©Û• Ø¯ÙˆØ§ÛŒ Ø¯ÙˆÙˆØ¨Ø§Ø±Û• Ù‡Û•ÚµÚ©Ø±Ø¯Ù†Û•ÙˆÛ•ÛŒ Ø³ÛŒØ³ØªÛ•Ù… Ø¨Ù…ÛŽÙ†ÛŽØªÛ•ÙˆÛ•: %s',
             'mmapErrorPart4' => 'Ù„Û• Windows Ùˆ WSL Ø¯Ø§ØŒ Ú¯Û•ÙˆØ±Û•Ú©Ø±Ø¯Ù†ÛŒ Ù‚Û•Ø¨Ø§Ø±Û•ÛŒ pagefile Ù„Û•ÙˆØ§Ù†Û•ÛŒÛ• ÛŒØ§Ø±Ù…Û•ØªÛŒØ¯Û•Ø± Ø¨ÛŽØªØ› Ø¦Û•Ú¯Û•Ø± Ú©ÛŽØ´Û•Ú©Û• Ø¨Û•Ø±Ø¯Û•ÙˆØ§Ù… Ø¨ÙˆÙˆØŒ ØªÚ©Ø§ÛŒÛ• Ø¨Ú¯Û†Ú•Û• Ø¨Û† Linux ÛŒ Ú•Ø§Ø³ØªÛ•ÙˆØ®Û† (native).',
             'must_have_declare_types' => 'Ø¨Û• Ù‡Û†Ú©Ø§Ø±ÛŒ Ú©Ø§Ø±Ø§ÛŒÛŒØŒ ÛŒÛ•Ú©Û•Ù… Ú•Ø³ØªÛ•ÛŒ Ù¾Û•Ú•Ú¯Û•ÛŒ event handler Ø¯Û•Ø¨ÛŽØª "declare(strict_types=1);" Ø¨ÛŽØª',
             'nearest_dc' => 'Ø¦ÛŽÙ…Û• Ù„Û• %s Ø¯Ø§ÛŒÙ†ØŒ Ù†Ø²ÛŒÚ©ØªØ±ÛŒÙ† DC %d Û•.',
             'need_dl.php' => 'Ù†Û•ØªÙˆØ§Ù†Ø±Ø§ Ø³Ú©Ø±ÛŒÙ¾ØªÛŒ Ø¯Ø§Ú¯Ø±ØªÙ†ÛŒ Ø¨Ù†Û•Ú•Û•ØªÛŒ Ø¯Ø±ÙˆØ³Øª Ø¨Ú©Ø±ÛŽØª (%s)ØŒ ØªÚ©Ø§ÛŒÛ• Ù¾Û•Ú•Ú¯Û•ÛŒÛ•Ú©ÛŒ dl.php Ø¨Û•Ù… Ù†Ø§ÙˆÛ•Ú•Û†Ú©Û•ÛŒ Ø®ÙˆØ§Ø±Û•ÙˆÛ• Ø¯Ø±ÙˆØ³Øª Ø¨Ú©Û•: %s Ùˆ URL Û•Ú©Û•ÛŒ ÙˆÛ•Ú© Ù¾Ø§Ø±Ø§Ù…ÛŽØªØ±ÛŒ Ø¯ÙˆÙˆÛ•Ù… Ø¨Ø¯Û• Ø¨Û• getDownloadLink',
             'noReportPeers' => 'Ø¦Ø§Ú¯Ø§Ø¯Ø§Ø±ÛŒ: Ù‡ÛŒÚ† peer ÛŽÚ©ÛŒ Ú•Ø§Ù¾Û†Ø±Øª Ø¯Ø§Ù†Û•Ù†Ø±Ø§ÙˆÛ•ØŒ ØªÚ©Ø§ÛŒÛ• Ø¦Û•Ù… Ø´ÛŽÙˆØ§Ø²Û•ÛŒ Ø®ÙˆØ§Ø±Û•ÙˆÛ• Ø¨Û† event handler Û•Ú©Û•Øª Ø²ÛŒØ§Ø¯ Ø¨Ú©Û•',
             'non_text_conversion' => 'Ù‡ÛŽØ´ØªØ§ Ù†Ø§ØªÙˆØ§Ù†Ø±ÛŽØª Ù†Ø§Ù…Û• Ù†Ø§Ø¯Û•Ù‚ÛŒÛŒÛ•Ú©Ø§Ù† Ø¨Ú¯Û†Ú•Ø¯Ø±ÛŽÙ†!',
             'not_loggedIn' => 'Ù…Ù† Ù†Û•Ú†ÙˆÙˆÙ…Û•ØªÛ• Ú˜ÙˆÙˆØ±Û•ÙˆÛ•!',
             'not_numeric' => 'Ø¨Û•Ù‡Ø§ÛŒ Ù¾ÛŽØ¯Ø±Ø§Ùˆ Ú˜Ù…Ø§Ø±Û•ÛŒÛŒ Ù†ÛŒÛŒÛ•',
             'params_missing' => 'Ù¾Ø§Ø±Ø§Ù…ÛŽØªØ±ÛŒ Ù¾ÛŽÙˆÛŒØ³Øª Ù†Û•Ù…Ø§ÙˆÛ•',
             'peer_not_in_db' => 'Ø¦Û•Ù… Ù‡Ø§ÙˆØªØ§ÛŒÛ• Ù„Û• Ø¨Ù†Ú©Û•Ø¯Ø±Ø§ÙˆÛ•ÛŒ Ù†Ø§ÙˆØ®Û†ÛŒÛŒ Ù‡Ø§ÙˆØªØ§Ø¯Ø§ Ø¦Ø§Ù…Ø§Ø¯Û• Ù†ÛŒÛŒÛ•',
             'plugin_path_does_not_exist' => 'Ú•ÛŽÚ•Û•ÙˆÛŒ Ù¾Ù„Û•Ú¯ÛŒÙ†ÛŒ %s Ø¨ÙˆÙˆÙ†ÛŒ Ù†ÛŒÛŒÛ•!',
             'plugins_do_not_use_require' => 'Ø¨Û• Ù‡Û†Ú©Ø§Ø±ÛŒ Ú©Ø§Ø±Ø§ÛŒÛŒØŒ Ù¾Ù„Û•Ú¯ÛŒÙ†Û•Ú©Ø§Ù† ØªÛ•Ù†Ù‡Ø§ Ø¯Û•ØªÙˆØ§Ù†Ù† Ù¾Û•Ú•Ú¯Û•Ú©Ø§Ù†ÛŒ ØªØ±ÛŒ Ù†Ø§Ùˆ Ù�Û†ÚµØ¯Û•Ø±ÛŒ Ù¾Ù„Û•Ú¯ÛŒÙ†Û•Ú©Ø§Ù† Ø¨Û• Ø´ÛŽÙˆÛ•ÛŒ Ø¦Û†ØªÛ†Ù…Ø§ØªÛŒÚ©ÛŒ include ÛŒØ§Ù† require Ø¨Ú©Û•Ù† Ù„Û• Ú•ÛŽÚ¯Û•ÛŒ autoloader ÛŒ PSR-4 Û•ÙˆÛ• (Ù†Û•Ú© Ø¨Û• require() Ú©Ø±Ø¯Ù†ÛŒ Ø¯Û•Ø³ØªÛŒ).',
             'plugins_must_have_exactly_one_class' => 'Ù¾Ù„Û•Ú¯ÛŒÙ† Ø¯Û•Ø¨ÛŽØª Ú•ÛŽÚ© ÛŒÛ•Ú© Ú©Ù„Ø§Ø³ Ù¾ÛŽÙ†Ø§Ø³Û• Ø¨Ú©Ø§Øª! Ø¨Û† Ù¾ÛŽÙ†Ø§Ø³Û•Ú©Ø±Ø¯Ù†ÛŒ Ú†Û•Ù†Ø¯ Ú©Ù„Ø§Ø³ØŒ interface ÛŒØ§Ù† traitØŒ Ù¾Û•Ú•Ú¯Û•ÛŒ Ø¬ÛŒØ§ÙˆØ§Ø² Ø¯Ø±ÙˆØ³Øª Ø¨Ú©Û•ØŒ MadelineProto Ø¨Û• Ø´ÛŽÙˆÛ•ÛŒ Ø¦Û†ØªÛ†Ù…Ø§ØªÛŒÚ©ÛŒ autoload ÛŒØ§Ù† Ø¯Û•Ú©Ø§Øª.',
             'predicate_not_set' => 'Predicate (Ø¨Û•Ù‡Ø§ÛŒ Ú˜ÛŽØ± _) Ø¯Ø§Ù†Û•Ù†Ø±Ø§Ø¨ÙˆÙˆ!',
             'recommend_not_use_filesystem_function' => 'Ø¨Û•Ú©Ø§Ø±Ù‡ÛŽÙ†Ø§Ù†ÛŒ Ù�Û•Ù†Ú©Ø´Ù†ÛŒ %s Ù¾ÛŽØ´Ù†ÛŒØ§Ø± Ù†Ø§Ú©Ø±ÛŽØªØŒ Ú†ÙˆÙ†Ú©Û• Ø¯Û•Ø³ØªÚ¯Û•ÛŒØ´ØªÙ† Ø¨Û• Ø³ÛŒØ³ØªÛ•Ù…ÛŒ Ù¾Û•Ú•Ú¯Û•Ú©Ø§Ù† Ù„Û• Ú©Ø§ØªÛŒ Ù…Ø§Ù…Û•ÚµÛ•Ú©Ø±Ø¯Ù† Ù„Û•Ú¯Û•Úµ update Û•Ú©Ø§Ù†Ø¯Ø§ Ø¨Û†ØªÛ•Ú©Û•Øª Ø®Ø§Ùˆ Ø¯Û•Ú©Ø§ØªÛ•ÙˆÛ•ØŒ ØªÚ©Ø§ÛŒÛ• Ø³Û•ÛŒØ±ÛŒ https://docs.madelineproto.xyz/docs/UPDATES.html#avoiding-the-use-of-filesystem-functions Ø¨Ú©Û• Ø¨Û† Ù„ÛŒØ³ØªÛŽÚ©ÛŒ Ú•ÛŽÚ¯Û• Ø¬ÛŽÚ¯Ø±Û•ÙˆÛ•Ú©Ø§Ù†ÛŒ Ù‡Û•ÚµÚ¯Ø±ØªÙ†ÛŒ Ø¯Ø§ØªØ§ Ú©Û• Ø¨Û†ØªÛ•Ú©Û•Øª Ø®Ø§Ùˆ Ù†Ø§Ú©Û•Ù†Û•ÙˆÛ•!',
             'rpc_tg_error' => 'ØªÛŽÙ„ÛŽÚ¯Ø±Ø§Ù… Ù‡Û•ÚµÛ•ÛŒÛ•Ú©ÛŒ RPC ÛŒ Ú¯Û•Ú•Ø§Ù†Ø¯Û•ÙˆÛ•: %s (%s)ØŒ Ø¨Û•Ù‡Û†ÛŒ %s:%s%sØ´ÙˆÛŽÙ†Ù¾ÛŽÛŒ TL:',
             'sec_peer_not_in_db' => 'Ø¦Û•Ù… Ù‡Ø§ÙˆØªØ§ Ù†Ù‡ÛŽÙ†ÛŒÛŒÛ• Ù„Û• Ø¨Ù†Ú©Û•Ø¯Ø±Ø§ÙˆÛ•ÛŒ Ù†Ø§ÙˆØ®Û†ÛŒÛŒ Ù‡Ø§ÙˆØªØ§Ø¯Ø§ Ø¦Ø§Ù…Ø§Ø¯Û• Ù†ÛŒÛŒÛ•',
             'secret_chat_skipping' => 'Ù…Ù† Ú†Ø§ØªÛŒ Ù†Ù‡ÛŽÙ†ÛŒ %sÙ… Ù„Û• Ø¨Ù†Ú©Û•Ø¯Ø±Ø§ÙˆÛ•Ø¯Ø§ Ù†ÛŒÛŒÛ•ØŒ Ù†Ø§Ù…Û•Ú©Û•Ù… Ø¨Û•Ø¬ÛŽÙ‡ÛŽØ´ØªÙˆÙˆÛ•...',
             'serialization_ofd' => 'Ø²Ù†Ø¬ÛŒØ±Û•ÛŒÛŒÚ©Ø±Ø¯Ù† Ø¨Û•Ø³Û•Ø±Ú†ÙˆÙˆÛ•ØŒ Ø¯ÙˆÙˆØ¨Ø§Ø±Û• Ø¨Ù†ÛŒØ§ØªÙ†Ø§Ù†Û•ÙˆÛ•ÛŒ Ø¦Û†Ø¨Ø¬ÛŽÚ©ØªÛŒ!',
             'session_corrupted' => 'Ø¯Ø§Ù†ÛŒØ´ØªÙ†Û•Ú©Û• ØªÛŽÚ©Ú†ÙˆÙˆÛ•!',
             'signing_up' => 'Ù†Ø§Ùˆ ØªÛ†Ù…Ø§Ø±Ú©Ø±Ø¯Ù† ÙˆÛ•Ú© Ø¨Û•Ú©Ø§Ø±Ù‡ÛŽÙ†Û•Ø±ÛŽÚ©ÛŒ Ø¦Ø§Ø³Ø§ÛŒÛŒ...',
             'signupFirstName' => 'Ù†Ø§ÙˆÛŒ ÛŒÛ•Ú©Û•Ù…ÛŒ Ø®Û†Øª Ø¨Ù†ÙˆÙˆØ³Û•: ',
             'signupFirstNameWeb' => 'Ù†Ø§ÙˆÛŒ ÛŒÛ•Ú©Û•Ù…',
             'signupLastName' => 'Ù†Ø§ÙˆÛŒ Ú©Û†ØªØ§ÛŒÛŒ Ø®Û†Øª Ø¨Ù†ÙˆÙˆØ³Û• (Ø¯Û•ØªÙˆØ§Ù†ÛŽØª Ø¨Û•ØªØ§Úµ Ø¨ÛŽØª): ',
             'signupLastNameWeb' => 'Ù†Ø§ÙˆÛŒ Ú©Û†ØªØ§ÛŒÛŒ',
             'signupLeadWeb' => 'Ø¦Û•Ù… Ú˜Ù…Ø§Ø±Û• ØªÛ•Ù„Û•Ù�Û†Ù†Û• Ù‡ÛŽØ´ØªØ§ Ù„Û• Telegram Ø¯Ø§ Ù†ÛŒÛŒÛ•: Ù†Ø§ÙˆÛŒ Ø¦Û•Ú©Ø§ÙˆÙ†ØªÛ• Ù†ÙˆÛŽÛŒÛ•Ú©Û• Ù‡Û•ÚµØ¨Ú˜ÛŽØ±Û•.',
             'signupTitleWeb' => 'Ø¦Û•Ú©Ø§ÙˆÙ†ØªÛ•Ú©Û•Øª Ø¯Ø±ÙˆØ³Øª Ø¨Ú©Û•',
             'signupWeb' => 'ØªÚ©Ø§ÛŒÛ• Ù†Ø§ÙˆØª ØªÛ†Ù…Ø§Ø± Ø¨Ú©Û•',
             'signup_ok' => 'Ø¨Û• Ø³Û•Ø±Ú©Û•ÙˆØªÙˆÙˆÛŒÛŒ Ù†Ø§ÙˆØª ØªÛ†Ù…Ø§Ø± Ú©Ø±Ø¯!',
             'signup_uncalled' => 'Ú†Ø§ÙˆÛ•Ú•ÛŽÛŒ Ù†Ø§Ùˆ ØªÛ†Ù…Ø§Ø±Ú©Ø±Ø¯Ù† Ù†Ø§Ú©Û•Ù…! ØªÚ©Ø§ÛŒÛ• Ø³Û•Ø±Û•ØªØ§ Ø¨Ø§Ù†Ú¯ÛŒ Ø´ÛŽÙˆØ§Ø²Û•Ú©Ø§Ù†ÛŒ phoneLogin Ùˆ completePhoneLogin Ø¨Ú©Û•!',
             'src_file_invalid' => 'Ù¾Û•Ú•Ú¯Û•ÛŒ Ø³Û•Ø±Ú†Ø§ÙˆÛ•ÛŒ Ù†Ø§Ø¯Ø±ÙˆØ³Øª Ø¯Ø§Ø¨ÛŒÙ† Ú©Ø±Ø§: ',
             'static_analysis_minor' => 'Ù„Û• Ú©Ø§ØªÛŒ Ø´ÛŒÚ©Ø§Ø±ÛŒ Ø³ØªØ§ØªÛŒÚ©ÛŒ %s Ø¯Ø§ Ú©ÛŽØ´Û•ÛŒÛ•Ú©ÛŒ Ø¨Ú†ÙˆÙˆÚ© Ø¯Û†Ø²Ø±Ø§ÛŒÛ•ÙˆÛ•: %s',
             'static_analysis_severe' => 'Ù„Û• Ú©Ø§ØªÛŒ Ø´ÛŒÚ©Ø§Ø±ÛŒ Ø³ØªØ§ØªÛŒÚ©ÛŒ %s Ø¯Ø§ Ú©ÛŽØ´Û•ÛŒÛ•Ú©ÛŒ Ú¯Û•ÙˆØ±Û• Ø¯Û†Ø²Ø±Ø§ÛŒÛ•ÙˆÛ•: %s',
             'stream_handle_invalid' => 'Ø¯Û•Ø³ØªÛ•ÛŒÛ•Ú©ÛŒ Ù†Ø§Ø¯Ø±ÙˆØ³ØªÛŒ Ø³ØªØ±ÛŽÙ… Ø¯Ø§Ø¨ÛŒÙ† Ú©Ø±Ø§Ø¨ÙˆÙˆ.',
             'string_required' => 'Ú†Ø§ÙˆÛ•Ú•ÛŽÛŒ string Ø¯Û•Ú©Ø±Ø§!',
             'translate_madelineproto_cli' => 'MadelineProto Ø¯Û•ØªÙˆØ§Ù†Ø±ÛŽØª Ø¨Û† Ø²Ù…Ø§Ù†Û•Ú©Û•Øª ÙˆÛ•Ø±Ø¨Ú¯ÛŽÚ•Ø¯Ø±ÛŽØª (Ú•ÛŽÚ˜Û•ÛŒ Ø¦ÛŽØ³ØªØ§ÛŒ ÙˆÛ•Ø±Ú¯ÛŽÚ•Ø§Ù†: %d%%)ØŒ Ø¨Ú•Û† Ø¨Û† https://github.com/danog/MadelineProto/tree/v8/langs Ø¨Û† Ø¨Û•Ø´Ø¯Ø§Ø±ÛŒÚ©Ø±Ø¯Ù† Ù„Û• ÙˆÛ•Ø±Ú¯ÛŽÚ•Ø§Ù†Û•Ú©Û•!',
             'translate_madelineproto_web' => 'MadelineProto Ø¯Û•ØªÙˆØ§Ù†Ø±ÛŽØª Ø¨Û† Ø²Ù…Ø§Ù†Û•Ú©Û•Øª ÙˆÛ•Ø±Ø¨Ú¯ÛŽÚ•Ø¯Ø±ÛŽØª (Ú•ÛŽÚ˜Û•ÛŒ Ø¦ÛŽØ³ØªØ§ÛŒ ÙˆÛ•Ø±Ú¯ÛŽÚ•Ø§Ù†: %d%%)ØŒ <a href="https://github.com/danog/MadelineProto/tree/v8/langs" target="_blank">Ú©Ù„ÛŒÚ© Ù„ÛŽØ±Û• Ø¨Ú©Û• Ø¨Û† Ø¨Û•Ø´Ø¯Ø§Ø±ÛŒÚ©Ø±Ø¯Ù† Ù„Û• ÙˆÛ•Ø±Ú¯ÛŽÚ•Ø§Ù†Û•Ú©Û•!</a>',
             'tweb.ACCESS_TOKEN_EXPIRED' => 'Ø¦Û•Ù… ØªÛ†Ú©Ù†ÛŒ Ø¨Û†ØªÛ• Ù‡Û•ÚµÙˆÛ•Ø´ÛŽÙ†Ø±Ø§ÙˆÛ•ØªÛ•ÙˆÛ•',
             'tweb.ACCESS_TOKEN_INVALID' => 'ØªÛ†Ú©Ù†ÛŒ Ø¨Û†Øª Ù†Ø§Ø¯Ø±ÙˆØ³ØªÛ•',
             'tweb.AccDescr.LoginCode' => 'Ú©Û†Ø¯ÛŒ Ú†ÙˆÙˆÙ†Û•Ú˜ÙˆÙˆØ±Û•ÙˆÛ•',
             'tweb.Close' => 'Ø¯Ø§Ø®Ø³ØªÙ†',
             'tweb.Code' => 'Ú©Û†Ø¯',
             'tweb.Contacts.PhoneNumber.Placeholder' => 'Ú˜Ù…Ø§Ø±Û•ÛŒ ØªÛ•Ù„Û•Ù�Û†Ù†',
             'tweb.Country' => 'ÙˆÚµØ§Øª',
             'tweb.DarkMode' => 'Ø¯Û†Ø®ÛŒ ØªØ§Ø±ÛŒÚ©',
             'tweb.Error.AnError' => 'Ù‡Û•ÚµÛ•ÛŒÛ•Ú© Ú•ÙˆÙˆÛŒØ¯Ø§. ØªÚ©Ø§ÛŒÛ• Ø¯ÙˆØ§ØªØ± Ø¯ÙˆÙˆØ¨Ø§Ø±Û• Ù‡Û•ÙˆÚµØ¨Ø¯Û•Ø±Û•ÙˆÛ•.',
             'tweb.FIRSTNAME_INVALID' => 'Ù†Ø§ÙˆÛŒ ÛŒÛ•Ú©Û•Ù… Ù†Ø§Ø¯Ø±ÙˆØ³ØªÛ•',
             'tweb.FirstName' => 'Ù†Ø§ÙˆÛŒ ÛŒÛ•Ú©Û•Ù… (Ù¾ÛŽÙˆÛŒØ³ØªÛ•)',
             'tweb.LASTNAME_INVALID' => 'Ù†Ø§ÙˆÛŒ Ú©Û†ØªØ§ÛŒÛŒ Ù†Ø§Ø¯Ø±ÙˆØ³ØªÛ•',
             'tweb.LastName' => 'Ù†Ø§ÙˆÛŒ Ú©Û†ØªØ§ÛŒÛŒ (Ø¦Ø§Ø±Û•Ø²ÙˆÙˆÙ…Û•Ù†Ø¯Ø§Ù†Û•)',
             'tweb.Login.Bot' => 'Ú†ÙˆÙˆÙ†Û•Ú˜ÙˆÙˆØ±Û•ÙˆÛ• ÙˆÛ•Ú© Ø¨Û†Øª >',
             'tweb.Login.Bot.Cancel' => 'Ú†ÙˆÙˆÙ†Û•Ú˜ÙˆÙˆØ±Û•ÙˆÛ• ÙˆÛ•Ú© Ø¨Û•Ú©Ø§Ø±Ù‡ÛŽÙ†Û•Ø± >',
             'tweb.Login.Bot.Subtitle' => 'Ø¦Û•Ùˆ ØªÛ†Ú©Ù†Û• Ø¯Ø§Ø®Úµ Ø¨Ú©Û• Ú©Û• @BotFather Ù¾ÛŽÛŒ Ø¯Ø§ÛŒØª
Ú©Ø§ØªÛŽÚ© Ø¨Û†ØªÛ•Ú©Û•Øª Ø¯Ø±ÙˆØ³Øª Ú©Ø±Ø¯.',
             'tweb.Login.Bot.Title' => 'Ú†ÙˆÙˆÙ†Û•Ú˜ÙˆÙˆØ±Û•ÙˆÛ• ÙˆÛ•Ú© Ø¨Û†Øª',
             'tweb.Login.Bot.Token' => 'ØªÛ†Ú©Ù†ÛŒ Ø¨Û†Øª',
             'tweb.Login.Code.SecretPhrase' => 'Ø¯Û•Ø³ØªÛ•ÙˆØ§Ú˜Û•ÛŒ Ø´Ø§Ø±Ø§ÙˆÛ•',
             'tweb.Login.Code.SecretWord' => 'ÙˆØ´Û•ÛŒ Ø´Ø§Ø±Ø§ÙˆÛ•',
             'tweb.Login.Code.SentCall' => 'Ù¾Û•ÛŒÙˆÛ•Ù†Ø¯ÛŒØª Ù¾ÛŽÙˆÛ• Ø¯Û•Ú©Û•ÛŒÙ† Ùˆ
Ú©Û†Ø¯Û•Ú©Û•Øª Ø¨Û† Ø¯Û•Ø®ÙˆÛŽÙ†ÛŒÙ†Û•ÙˆÛ•.',
             'tweb.Login.Code.SentEmail' => 'ØªÚ©Ø§ÛŒÛ• Ø¦ÛŒÙ…Û•ÛŒÚµÛ•Ú©Û•Øª **%s** Ø¨Ù¾Ø´Ú©Ù†Û• (Ù�Û†ÚµØ¯Û•Ø±ÛŒ Ø³Ù¾Ø§Ù… Ù„Û•Ø¨ÛŒØ± Ù…Û•Ú©Û•) Ùˆ Ø¦Û•Ùˆ Ú©Û†Ø¯Û•ÛŒ Ø¦ÛŽØ³ØªØ§ Ø¨Û†Ù…Ø§Ù† Ù†Ø§Ø±Ø¯ÛŒØª Ø¯Ø§Ø®Úµ Ø¨Ú©Û•.',
             'tweb.Login.Code.SentInApp' => 'Ù†Ø§Ù…Û•ÛŒÛ•Ú©Ù…Ø§Ù† Ù„Û• Telegram Ø¨Û† Ù†Ø§Ø±Ø¯ÛŒØª
Ú©Û• Ú©Û†Ø¯Û•Ú©Û•ÛŒ ØªÛŽØ¯Ø§ÛŒÛ•.',
             'tweb.Login.Code.SentMissedCall' => 'Ù„Û• Ú†Û•Ù†Ø¯ Ú†Ø±Ú©Û•ÛŒ Ø¯Ø§Ù‡Ø§ØªÙˆÙˆØ¯Ø§
Ù¾Û•ÛŒÙˆÛ•Ù†Ø¯ÛŒÛŒÛ•Ú©ÛŒ Ú©ÙˆØ±Øª Ù„Û• **%s** ÙˆÛ•Ø±Ø¯Û•Ú¯Ø±ÛŒØª. ÙˆÛ•ÚµØ§Ù…ÛŒ Ù…Û•Ø¯Û•Ø±Û•ÙˆÛ• â€”
ØªÛ•Ù†Ù‡Ø§ Ø¯ÙˆØ§ÛŒÛŒÙ† Ú˜Ù…Ø§Ø±Û•Ú©Ø§Ù†ÛŒ Ø¦Û•Ùˆ Ú˜Ù…Ø§Ø±Û•ÛŒÛ• Ø¯Ø§Ø®Úµ Ø¨Ú©Û•.',
             'tweb.Login.Code.SentSms' => 'SMS ÛŽÚ©Ù…Ø§Ù† Ø¨Û† Ù†Ø§Ø±Ø¯ÛŒØª
Ú©Û• Ú©Û†Ø¯Û•Ú©Û•ÛŒ ØªÛŽØ¯Ø§ÛŒÛ•.',
             'tweb.Login.Code.SentSmsPhrase' => 'SMS ÛŽÚ©Ù…Ø§Ù† Ú©Û• Ø¯Û•Ø³ØªÛ•ÙˆØ§Ú˜Û•ÛŒÛ•Ú©ÛŒ Ø´Ø§Ø±Ø§ÙˆÛ•ÛŒ ØªÛŽØ¯Ø§ÛŒÛ•
Ø¨Û† ØªÛ•Ù„Û•Ù�Û†Ù†Û•Ú©Û•Øª **%s** Ù†Ø§Ø±Ø¯.',
             'tweb.Login.Code.SentSmsPhraseBeginning' => 'SMS ÛŽÚ©Ù…Ø§Ù† Ú©Û• Ø¯Û•Ø³ØªÛ•ÙˆØ§Ú˜Û•ÛŒÛ•Ú©ÛŒ Ø´Ø§Ø±Ø§ÙˆÛ•ÛŒ ØªÛŽØ¯Ø§ÛŒÛ• Ùˆ Ø¨Û•
"**%2$s**" Ø¯Û•Ø³Øª Ù¾ÛŽØ¯Û•Ú©Ø§ØªØŒ Ø¨Û† ØªÛ•Ù„Û•Ù�Û†Ù†Û•Ú©Û•Øª **%1$s** Ù†Ø§Ø±Ø¯.',
             'tweb.Login.Code.SentSmsWord' => 'SMS ÛŽÚ©Ù…Ø§Ù† Ú©Û• ÙˆØ´Û•ÛŒÛ•Ú©ÛŒ Ø´Ø§Ø±Ø§ÙˆÛ•ÛŒ ØªÛŽØ¯Ø§ÛŒÛ•
Ø¨Û† ØªÛ•Ù„Û•Ù�Û†Ù†Û•Ú©Û•Øª **%s** Ù†Ø§Ø±Ø¯.',
             'tweb.Login.Code.SentSmsWordBeginning' => 'SMS ÛŽÚ©Ù…Ø§Ù† Ú©Û• ÙˆØ´Û•ÛŒÛ•Ú©ÛŒ Ø´Ø§Ø±Ø§ÙˆÛ•ÛŒ ØªÛŽØ¯Ø§ÛŒÛ• Ùˆ Ø¨Û•
"**%2$s**" Ø¯Û•Ø³Øª Ù¾ÛŽØ¯Û•Ú©Ø§ØªØŒ Ø¨Û† ØªÛ•Ù„Û•Ù�Û†Ù†Û•Ú©Û•Øª **%1$s** Ù†Ø§Ø±Ø¯.',
             'tweb.Login.Code.SentUnknown' => 'ØªÚ©Ø§ÛŒÛ• Ù‡Û•Ù…ÙˆÙˆ Ø´ÙˆÛŽÙ†ÛŽÚ© Ø¨Ù¾Ø´Ú©Ù†Û•
Ø¨Û† Ø¯Û†Ø²ÛŒÙ†Û•ÙˆÛ•ÛŒ Ú©Û†Ø¯Û•Ú©Û• (Ø¬Û†Ø±: %s)',
             'tweb.Login.Code.WordBeginningInvalid' => 'ÙˆØ´Û•ÛŒ Ù‡Û•ÚµÛ•',
             'tweb.Login.Code.WordInvalid' => 'Ù‡Û•ÚµÛ•ÛŒÛ•ØŒ ØªÚ©Ø§ÛŒÛ• Ø¯ÙˆÙˆØ¨Ø§Ø±Û• Ù‡Û•ÙˆÚµØ¨Ø¯Û•Ø±Û•ÙˆÛ•',
             'tweb.Login.Next' => 'Ø¯ÙˆØ§ØªØ±',
             'tweb.Login.Password.Subtitle' => 'Ø¦Û•Ú©Ø§ÙˆÙ†ØªÛ•Ú©Û•Øª Ø¨Û• ÙˆØ´Û•ÛŒÛ•Ú©ÛŒ Ù†Ù‡ÛŽÙ†ÛŒ
Ø²ÛŒØ§Ø¯Û• Ù¾Ø§Ø±ÛŽØ²Ø±Ø§ÙˆÛ•',
             'tweb.Login.Password.Title' => 'ÙˆØ´Û•ÛŒ Ù†Ù‡ÛŽÙ†ÛŒÛŒÛ•Ú©Û•Øª Ø¯Ø§Ø®Úµ Ø¨Ú©Û•',
             'tweb.Login.PhoneLabelInvalid' => 'Ú˜Ù…Ø§Ø±Û•ÛŒ ØªÛ•Ù„Û•Ù�Û†Ù† Ù†Ø§Ø¯Ø±ÙˆØ³ØªÛ•',
             'tweb.Login.QR.Cancel' => 'Ú†ÙˆÙˆÙ†Û•Ú˜ÙˆÙˆØ±Û•ÙˆÛ• Ø¨Û• Ú˜Ù…Ø§Ø±Û•ÛŒ ØªÛ•Ù„Û•Ù�Û†Ù† >',
             'tweb.Login.QR.Help1' => 'Ù„Û• Ù…Û†Ø¨Ø§ÛŒÙ„Û•Ú©Û•ØªØ¯Ø§ Telegram Ø¨Ú©Û•Ø±Û•ÙˆÛ•',
             'tweb.Login.QR.Help2' => 'Ø¨Ú•Û† Ø¨Û† **Ú•ÛŽÚ©Ø®Ø³ØªÙ†Û•Ú©Ø§Ù†** > **Ø¦Ø§Ù…ÛŽØ±Û•Ú©Ø§Ù†** > **Ø²ÛŒØ§Ø¯Ú©Ø±Ø¯Ù†ÛŒ Ø¦Ø§Ù…ÛŽØ±**',
             'tweb.Login.QR.Help3' => 'Ø¨Û† Ù¾Ø´ØªÚ•Ø§Ø³ØªÚ©Ø±Ø¯Ù†Û•ÙˆÛ•ÛŒ Ú†ÙˆÙˆÙ†Û•Ú˜ÙˆÙˆØ±Û•ÙˆÛ• Ù…Û†Ø¨Ø§ÛŒÙ„Û•Ú©Û•Øª Ø¦Ø§Ø±Ø§Ø³ØªÛ•ÛŒ Ø¦Û•Ù… Ø´Ø§Ø´Û•ÛŒÛ• Ø¨Ú©Û•',
             'tweb.Login.QR.Login' => 'Ú†ÙˆÙˆÙ†Û•Ú˜ÙˆÙˆØ±Û•ÙˆÛ• Ø¨Û• Ú©Û†Ø¯ÛŒ QR >',
             'tweb.Login.QR.Subtitle' => 'Ø¨Û• Ø¦Û•Ù¾ÛŒ Telegram ÛŒ Ù…Û†Ø¨Ø§ÛŒÙ„Û•Ú©Û•Øª Ø³Ú©Ø§Ù†ÛŒ Ø¨Ú©Û•',
             'tweb.Login.QR.Title' => 'Ú†ÙˆÙˆÙ†Û•Ú˜ÙˆÙˆØ±Û•ÙˆÛ• Ø¨Û• Ú©Û†Ø¯ÛŒ QR',
             'tweb.Login.Register.Subtitle' => 'Ù†Ø§ÙˆÛ•Ú©Û•Øª Ø¯Ø§Ø®Úµ Ø¨Ú©Û•
Ø¨Û† Ø¯Ø±ÙˆØ³ØªÚ©Ø±Ø¯Ù†ÛŒ Ø¦Û•Ú©Ø§ÙˆÙ†ØªÛ•Ú©Û•Øª',
             'tweb.Login.StartText' => 'ØªÚ©Ø§ÛŒÛ• Ú©Û†Ø¯ÛŒ ÙˆÚµØ§ØªÛ•Ú©Û•Øª Ù¾Ø´ØªÚ•Ø§Ø³Øª Ø¨Ú©Û•Ø±Û•ÙˆÛ•
Ùˆ Ú˜Ù…Ø§Ø±Û•ÛŒ ØªÛ•Ù„Û•Ù�Û†Ù†Û•Ú©Û•Øª Ø¯Ø§Ø®Úµ Ø¨Ú©Û•.',
             'tweb.Login.Title' => 'Ú†ÙˆÙˆÙ†Û•Ú˜ÙˆÙˆØ±Û•ÙˆÛ• Ø¨Û† MadelineProto',
             'tweb.LoginPassword' => 'ÙˆØ´Û•ÛŒ Ù†Ù‡ÛŽÙ†ÛŒ',
             'tweb.Optional' => 'Ø¦Ø§Ø±Û•Ø²ÙˆÙˆÙ…Û•Ù†Ø¯Ø§Ù†Û•',
             'tweb.PASSWORD_HASH_INVALID' => 'ÙˆØ´Û•ÛŒ Ù†Ù‡ÛŽÙ†ÛŒ Ù‡Û•ÚµÛ•ÛŒÛ•',
             'tweb.PHONE_CODE_EMPTY' => 'Ú©Û†Ø¯Û•Ú©Û• Ù†Ø§Ø¯Ø±ÙˆØ³ØªÛ•',
             'tweb.PHONE_CODE_EXPIRED' => 'Ú©Û†Ø¯Û•Ú©Û• Ø¨Û•Ø³Û•Ø±Ú†ÙˆÙˆÛ•',
             'tweb.PHONE_CODE_INVALID' => 'Ú©Û†Ø¯Û•Ú©Û• Ù†Ø§Ø¯Ø±ÙˆØ³ØªÛ•',
             'tweb.PHONE_NUMBER_BANNED' => 'Ø¦Û•Ù… Ú˜Ù…Ø§Ø±Û• ØªÛ•Ù„Û•Ù�Û†Ù†Û• Ù‚Û•Ø¯Û•ØºÛ• Ú©Ø±Ø§ÙˆÛ•',
             'tweb.PHONE_NUMBER_FLOOD' => 'Ù‡Û•ÙˆÚµØ¯Ø§Ù†ÛŒ Ø²Û†Ø±ØŒ Ø¯ÙˆØ§ØªØ± Ø¯ÙˆÙˆØ¨Ø§Ø±Û• Ù‡Û•ÙˆÚµØ¨Ø¯Û•Ø±Û•ÙˆÛ•',
             'tweb.PHONE_NUMBER_INVALID' => 'Ú˜Ù…Ø§Ø±Û•ÛŒ ØªÛ•Ù„Û•Ù�Û†Ù† Ù†Ø§Ø¯Ø±ÙˆØ³ØªÛ•',
             'tweb.PHONE_PASSWORD_FLOOD' => 'Ù‡Û•ÙˆÚµØ¯Ø§Ù†ÛŒ Ø²Û†Ø±ØŒ Ø¯ÙˆØ§ØªØ± Ø¯ÙˆÙˆØ¨Ø§Ø±Û• Ù‡Û•ÙˆÚµØ¨Ø¯Û•Ø±Û•ÙˆÛ•',
             'tweb.PhoneNumber.Code.Fragment.Info' => 'Ú©Û†Ø¯Û•Ú©Û• Ù„Û• Ø¨Û•Ø´ÛŒ Ú˜Ù…Ø§Ø±Û•Ú©Ø§Ù†ÛŒ [Fragment]() ÙˆÛ•Ø±Ø¨Ú¯Ø±Û•',
             'tweb.PleaseWait' => 'ØªÚ©Ø§ÛŒÛ• Ú†Ø§ÙˆÛ•Ú•ÛŽ Ø¨Ú©Û•...',
             'tweb.StartMessaging' => 'Ø¯Û•Ø³ØªÙ¾ÛŽÚ©Ø±Ø¯Ù†ÛŒ Ù†Ø§Ù…Û•Ù†Ø§Ø±Ø¯Ù†',
             'tweb.YourName' => 'Ù†Ø§ÙˆÛ•Ú©Û•Øª',
             'type_extract_error' => 'Ù†Û•ØªÙˆØ§Ù†Ø±Ø§ Ø¬Û†Ø±ÛŒ "%s" Ø¯Û•Ø±Ø¨Ù‡ÛŽÙ†Ø±ÛŽØªØŒ Ù¾ÛŽÙˆÛŒØ³ØªÛ• MadelineProto Ù†ÙˆÛŽ Ø¨Ú©Û•ÛŒØªÛ•ÙˆÛ•!',
             'type_extract_error_id' => 'Ù†Û•ØªÙˆØ§Ù†Ø±Ø§ Ø¬Û†Ø±ÛŒ: %s Ø¨Û• id %s Ø¯Û•Ø±Ø¨Ù‡ÛŽÙ†Ø±ÛŽØªØŒ Ù¾ÛŽÙˆÛŒØ³ØªÛ• MadelineProto Ù†ÙˆÛŽ Ø¨Ú©Û•ÛŒØªÛ•ÙˆÛ•!',
             'update_madelineproto' => 'ØªÛ† ÙˆÛ•Ø´Ø§Ù†ÛŽÚ©ÛŒ Ú©Û†Ù†ÛŒ MadelineProto Ø¨Û•Ú©Ø§Ø±Ø¯Û•Ù‡ÛŽÙ†ÛŒØªØŒ Ù†ÙˆÛŽÚ©Ø±Ø¯Ù†Û•ÙˆÛ• Ù¾ÛŽÙˆÛŒØ³ØªÛ•: Ø¦ÛŽØ³ØªØ§ ÙˆÛ•Ø´Ø§Ù†ÛŒ %s Ú©Ø§Ø±Ø¯Û•Ú©Ø§ØªØŒ Ø¨Û•ÚµØ§Ù… Ø¯ÙˆØ§ÛŒÛŒÙ† ÙˆÛ•Ø´Ø§Ù† Ú©Û• Ú†Û•Ù†Ø¯ÛŒÙ† Ú†Ø§Ú©Ú©Ø±Ø¯Ù†Û•ÙˆÛ•ÛŒ Ù‡Û•ÚµÛ• Ùˆ ØªØ§ÛŒØ¨Û•ØªÙ…Û•Ù†Ø¯ÛŒ Ù†ÙˆÛŽÛŒ ØªÛŽØ¯Ø§ÛŒÛ• %s Û•!',
             'value_bigger_than_2147483647' => 'Ø¨Û•Ù‡Ø§ÛŒ Ø¯Ø§Ø¨ÛŒÙ†Ú©Ø±Ø§Ùˆ %s Ú¯Û•ÙˆØ±Û•ØªØ±Û• Ù„Û• 2147483647',
             'value_bigger_than_4294967296' => 'Ø¨Û•Ù‡Ø§ÛŒ Ø¯Ø§Ø¨ÛŒÙ†Ú©Ø±Ø§Ùˆ %s Ú¯Û•ÙˆØ±Û•ØªØ±Û• Ù„Û• 4294967296',
             'value_bigger_than_9223372036854775807' => 'Ø¨Û•Ù‡Ø§ÛŒ Ø¯Ø§Ø¨ÛŒÙ†Ú©Ø±Ø§Ùˆ %s Ú¯Û•ÙˆØ±Û•ØªØ±Û• Ù„Û• 9223372036854775807',
             'value_smaller_than_0' => 'Ø¨Û•Ù‡Ø§ÛŒ Ø¯Ø§Ø¨ÛŒÙ†Ú©Ø±Ø§Ùˆ %s Ø¨Ú†ÙˆÙˆÚ©ØªØ±Û• Ù„Û• 0',
             'value_smaller_than_2147483648' => 'Ø¨Û•Ù‡Ø§ÛŒ Ø¯Ø§Ø¨ÛŒÙ†Ú©Ø±Ø§Ùˆ %s Ø¨Ú†ÙˆÙˆÚ©ØªØ±Û• Ù„Û• -2147483648',
             'value_smaller_than_9223372036854775808' => 'Ø¨Û•Ù‡Ø§ÛŒ Ø¯Ø§Ø¨ÛŒÙ†Ú©Ø±Ø§Ùˆ %s Ø¨Ú†ÙˆÙˆÚ©ØªØ±Û• Ù„Û• -9223372036854775808',
             'waveform_must_have_100_values' => 'Ú•ÛŒØ²Ø¨Û•Ù†Ø¯ÛŒÛŒÛ•Ú©ÛŒ waveform Ø¯Û•Ø¨ÛŽØª 100 Ø¨Û•Ù‡Ø§ÛŒ Ù‡Û•Ø¨ÛŽØª!',
             'waveform_value' => 'Ø¨Û•Ù‡Ø§ÛŒ waveform Ø¯Û•Ø¨ÛŽØª Ù„Û• Ù†ÛŽÙˆØ§Ù† 0 Ùˆ 31 Ø¯Ø§ Ø¨ÛŽØª!',
             'webContinue' => 'Ø¨Û•Ø±Ø¯Û•ÙˆØ§Ù…Ø¨ÙˆÙˆÙ†',
             'webDocs' => 'Ø¨Û•ÚµÚ¯Û•Ù†Ø§Ù…Û•Ú©Ø§Ù†',
             'webDoneLead' => 'MadelineProto Ù„Û•Ø³Û•Ø± Ø³ÛŽØ±Ú¤Û•Ø±Û•Ú©Û• Ø¨Û•Ø±Ø¯Û•ÙˆØ§Ù… Ú©Ø§Ø±Ø¯Û•Ú©Ø§Øª: Ø¯Û•ØªÙˆØ§Ù†ÛŒØª Ø¦Û•Ù… Ù¾Û•Ú•Û•ÛŒÛ• Ø¯Ø§Ø¨Ø®Û•ÛŒØª.',
             'webError' => '%s. Ø¨ÛŒÙ¾Ø´Ú©Ù†Û• Ùˆ Ø¯ÙˆÙˆØ¨Ø§Ø±Û• Ù‡Û•ÙˆÚµØ¨Ø¯Û•Ø±Û•ÙˆÛ•.',
             'webFieldRequired' => 'Ø¦Û•Ù… Ø®Ø§Ù†Û•ÛŒÛ• Ù¾Ú• Ø¨Ú©Û•Ø±Û•ÙˆÛ•.',
             'webHidePassword' => 'Ø´Ø§Ø±Ø¯Ù†Û•ÙˆÛ•',
             'webLogin' => 'Ú†ÙˆÙˆÙ†Û•Ú˜ÙˆÙˆØ±Û•ÙˆÛ•',
             'webOptional' => 'Ø¦Ø§Ø±Û•Ø²ÙˆÙˆÙ…Û•Ù†Ø¯Ø§Ù†Û•',
             'webPhoneInvalid' => 'Ú˜Ù…Ø§Ø±Û•ÛŒ ØªÛ•Ù„Û•Ù�Û†Ù† Ù„Û•Ú¯Û•Úµ Ú©Û†Ø¯ÛŒ ÙˆÚµØ§ØªÛ•Ú©Û•ÛŒ Ø¯Ø§Ø®Úµ Ø¨Ú©Û•ØŒ ÙˆÛ•Ú© +39 123 456 7890.',
             'webProgress' => 'Ù¾ÛŽØ´Ú©Û•ÙˆØªÙ†ÛŒ Ú•ÛŽÚ©Ø®Ø³ØªÙ†',
             'webQrTitle' => 'Ú†ÙˆÙˆÙ†Û•Ú˜ÙˆÙˆØ±Û•ÙˆÛ• Ø¨Û• Ú©Û†Ø¯ÛŒ QR',
             'webQrWaiting' => 'Ú†Ø§ÙˆÛ•Ú•ÛŽÛŒ Ø³Ú©Ø§Ù†Ú©Ø±Ø¯Ù†â€¦',
             'webShowPassword' => 'Ù¾ÛŒØ´Ø§Ù†Ø¯Ø§Ù†',
             'webStepAccount' => 'Ø¦Û•Ú©Ø§ÙˆÙ†Øª',
             'webStepApp' => 'API ID Ùˆ hash',
             'webStepDone' => 'Ú©Ø§Ø±Ø¯Û•Ú©Ø§Øª',
             'webStepVerify' => 'Ù¾Ø´ØªÚ•Ø§Ø³ØªÚ©Ø±Ø¯Ù†Û•ÙˆÛ•',
         ],
        'de' =>
         [
             '2fa_uncalled' => 'Ich warte nicht auf das Passwort! Bitte rufen Sie zuerst die Methoden phoneLogin und completePhoneLogin auf!',
             'accepting_call' => 'Anruf von %s wird angenommen...',
             'account_banned' => '!!!!!!! WARNUNG !!!!!!!
Das Flood-Schutzsystem von Telegram hat dieses Konto gesperrt.
Um fortzufahren, ist eine manuelle Verifizierung erforderlich.
Senden Sie eine E-Mail an recover@telegram.org, in der Sie um die Entsperrung der Telefonnummer %s bitten, und beschreiben Sie kurz, was Sie mit dieser Telefonnummer tun werden.
Melden Sie sich dann erneut an.
Wenn Sie dieses Konto absichtlich gelÃ¶scht haben, ignorieren Sie diese Nachricht.',
             'already_loggedIn' => 'Diese MadelineProto-Instanz ist bereits angemeldet!',
             'apiAppDescLabelWeb' => 'Beschreibung',
             'apiAppInstructionsAuto0' => 'Geben Sie den Namen der App ein, beliebig: ',
             'apiAppInstructionsAuto1' => 'Geben Sie den Kurznamen der App ein, alphanumerisch, 5-32 Zeichen: ',
             'apiAppInstructionsAuto2' => 'Geben Sie die URL der App/Website oder t.me/yourusername ein: ',
             'apiAppInstructionsAuto3' => 'Geben Sie die Plattform der App ein: ',
             'apiAppInstructionsAuto4' => 'Beschreiben Sie Ihre App: ',
             'apiAppInstructionsAutoTypeOther' => 'Andere (in der Beschreibung angeben)',
             'apiAppInstructionsManual0' => 'der Name Ihrer App, beliebig',
             'apiAppInstructionsManual1' => 'der Kurzname Ihrer App, alphanumerisch, 5-32 Zeichen',
             'apiAppInstructionsManual2' => 'die URL Ihrer App/Website oder t.me/yourusername',
             'apiAppInstructionsManual3' => 'beliebig',
             'apiAppInstructionsManual4' => 'Beschreiben Sie hier Ihre App',
             'apiAppShortNameLabelWeb' => 'Kurzname',
             'apiAppTitleLabelWeb' => 'App-Name',
             'apiAppUrlLabelWeb' => 'URL',
             'apiAppWeb' => 'API-Informationen eingeben',
             'apiAutoCodeLabelWeb' => 'Anmeldecode',
             'apiAutoCodeLeadWeb' => 'my.telegram.org hat einen Anmeldecode an Ihre Telegram-App gesendet, im Chat mit Telegram (nicht per SMS).',
             'apiAutoCodeTitleWeb' => 'Code aus Telegram eingeben',
             'apiAutoErrorTitle' => 'my.telegram.org hat einen Fehler zurÃ¼ckgegeben',
             'apiAutoFallbackManual' => 'Geben Sie API-ID und Hash stattdessen manuell ein.',
             'apiAutoPhoneHintWeb' => 'my.telegram.org sendet den Anmeldecode an Ihre Telegram-App, nicht per SMS.',
             'apiAutoPrompt0' => 'Geben Sie eine Telefonnummer ein, die bereits bei Telegram registriert ist: ',
             'apiAutoPrompt1' => 'Geben Sie den BestÃ¤tigungscode ein, den Sie in Telegram erhalten haben: ',
             'apiAutoWeb' => 'Geben Sie eine Telefonnummer ein, die <b>bereits bei Telegram registriert</b> ist, um die API-ID zu erhalten',
             'apiChooseAutoWeb' => 'Automatisch abrufen',
             'apiChooseManualAuto' => 'MÃ¶chten Sie API-ID und Hash automatisch von my.telegram.org abrufen (a) oder manuell eingeben (m)?',
             'apiChooseManualAutoTip' => 'Beachten Sie, dass Sie API-ID/Hash auch direkt im Code Ã¼ber die Einstellungen angeben kÃ¶nnen: %s',
             'apiChooseManualAutoTipWeb' => 'Beachten Sie, dass Sie API-ID/Hash auch direkt im Code Ã¼ber die <a target="_blank" href="%s">Einstellungen</a> angeben kÃ¶nnen.',
             'apiChooseManualWeb' => 'Ich habe bereits eine API-ID',
             'apiChoosePrompt' => 'Ihre Wahl (m/a): ',
             'apiCreateErrorTitle' => 'my.telegram.org hat die App nicht erstellt',
             'apiCreateLeadWeb' => 'Dieses Konto hat noch keine App auf my.telegram.org. Diese Angaben sind nur fÃ¼r Sie sichtbar und kÃ¶nnen spÃ¤ter geÃ¤ndert werden.',
             'apiCreateTitleWeb' => 'App erstellen',
             'apiCreateVpsWarning' => 'Telegram erlaubt es VPS-, VPN- und anderen Rechenzentrums-IP-Bereichen nicht, neue Apps auf my.telegram.org zu erstellen, und die Anfrage kam von der IP des Rechners, auf dem MadelineProto lÃ¤uft. Erstellen Sie die App manuell unter https://my.telegram.org Ã¼ber einen privaten Internetanschluss (z. B. Ihr Telefon mit mobilen Daten) und geben Sie dann API-ID und Hash ein.',
             'apiCreateWeb' => 'App erstellen',
             'apiError' => 'FEHLER: %s. Versuchen Sie es erneut.',
             'apiInvalidWeb' => 'Die API-ID ist eine Zahl und der API-Hash ist 32 Zeichen lang: Kopieren Sie beide erneut von my.telegram.org.',
             'apiManualInstructions0' => 'Melden Sie sich bei https://my.telegram.org an',
             'apiManualInstructions1' => 'Gehen Sie zu â€žAPI development toolsâ€œ',
             'apiManualInstructions2' => 'Klicken Sie auf â€žCreate applicationâ€œ',
             'apiManualPrompt0' => 'Geben Sie Ihre API-ID ein: ',
             'apiManualPrompt1' => 'Geben Sie Ihren API-Hash ein: ',
             'apiManualWeb' => 'Geben Sie Ihre API-ID und Ihren API-Hash ein',
             'apiParamsError' => 'Sie haben nicht alle erforderlichen Parameter angegeben!',
             'apiSaveWeb' => 'Speichern und fortfahren',
             'apiSendCodeWeb' => 'Code senden',
             'apiSetupLeadWeb' => 'Jede Telegram-App benÃ¶tigt eine API-ID und einen Hash. MadelineProto kann sie mit der Telefonnummer eines bestehenden Telegram-Kontos fÃ¼r Sie von my.telegram.org abrufen.',
             'apiSetupTitleWeb' => 'API-ID und Hash abrufen',
             'apiStartOverWeb' => 'Neu beginnen',
             'api_not_set' => 'Sie mÃ¼ssen einen API-SchlÃ¼ssel und eine API-ID angeben, holen Sie sich Ihre eigenen unter my.telegram.org',
             'array_invalid' => 'Sie haben kein gÃ¼ltiges Array angegeben',
             'baseDirLimitation' => 'Eine basedir-BeschrÃ¤nkung ist konfiguriert: Dies kann die Leistung beeintrÃ¤chtigen und Probleme verursachen, bitte deaktivieren Sie sie nach MÃ¶glichkeit!',
             'bool_error' => 'Boolescher Wert konnte nicht extrahiert werden',
             'botAlreadyRunning' => 'Der Bot lÃ¤uft bereits!',
             'botapi_conversion_error' => '%s kann nicht in ein Bot-API-Objekt konvertiert werden',
             'call_already_accepted' => 'Anruf %s wurde bereits angenommen',
             'call_already_declined' => 'Anruf %s wurde bereits abgelehnt',
             'call_completing' => 'Anruf von %s wird abgeschlossen...',
             'call_confirming' => 'Anruf von %s wird bestÃ¤tigt...',
             'call_discarding' => 'Anruf %s wird beendet...',
             'call_error_1' => 'Anruf %s konnte nicht gefunden und angenommen werden',
             'call_error_2' => 'Anruf %s konnte nicht gefunden und bestÃ¤tigt werden',
             'call_error_3' => 'Anruf %s konnte nicht gefunden und abgeschlossen werden',
             'cli_need_dl.php_link' => 'Bitte geben Sie die URL eines Download-Skripts an, wenn Sie getDownloadLink Ã¼ber die CLI verwenden!',
             'constructor_not_found' => 'Konstruktor nicht gefunden fÃ¼r Typ: ',
             'could_not_connect_to_MadelineProto' => 'Verbindung zu MadelineProto nicht mÃ¶glich, bitte aktivieren Sie zur Behebung proc_open und entfernen Sie open_basedir-BeschrÃ¤nkungen oder deaktivieren Sie Pfad-Rewrites des Webservers! Falls Sie das bereits getan haben, stellen Sie sicher, dass die CLI-Version von PHP genau der Web-Version entspricht (gleiche Version, Erweiterungen usw.), und prÃ¼fen Sie die Datei MadelineProto.log auf weitere Informationen zu dem Fehler, der den Start des IPC-Servers verhindert hat.',
             'could_not_convert_object' => 'Objekt vom Typ %s konnte nicht konvertiert werden',
             'deserialization_error' => 'Bei der Deserialisierung ist ein Fehler aufgetreten',
             'dl.php_check_logs_make_sure_session_running' => 'Entweder der zugehÃ¶rige MadelineProto-EventHandler-Bot oder der MadelineProto-IPC-Server ist offline, bitte prÃ¼fen Sie die Logs und stellen Sie sicher, dass mindestens einer davon lÃ¤uft!',
             'dl.php_powered_by_madelineproto' => 'Telegram-Datei-Downloadserver (bis zu 4 GB), betrieben mit <a href="https://docs.madelineproto.xyz" target="_blank">MadelineProto</a>!<br>Klicken Sie <a href="https://docs.madelineproto.xyz/docs/FILES.html#getting-a-download-link" target="_blank">hier</a>, um zu erfahren, wie Sie Ihren eigenen Telegram-Datei-Downloadserver einrichten!',
             'do_not_delete_MadelineProto.log' => 'die Datei MadelineProto.log darf niemals gelÃ¶scht werden, bitte legen Sie stattdessen in den Einstellungen eine benutzerdefinierte MaximalgrÃ¶ÃŸe fest!',
             'do_not_remove_MadelineProto.log_phar' => 'Bitte entfernen Sie madeline.phar oder madeline.php nicht, sonst stÃ¼rzt MadelineProto ab. Wenn Sie ein Problem mit MadelineProto haben, melden Sie es unter https://github.com/danog/MadelineProto oder https://t.me/pwrtelegramgroup',
             'do_not_use_blocking_class' => 'aus Performance-GrÃ¼nden dÃ¼rfen Handler die nicht asynchrone, blockierende Klasse %s nicht verwenden, bitte verwenden Sie stattdessen %s',
             'do_not_use_blocking_function' => 'aus Performance-GrÃ¼nden dÃ¼rfen Event-Handler die nicht asynchrone, blockierende Funktion %s nicht verwenden, bitte verwenden Sie stattdessen %s',
             'do_not_use_deprecated_function' => 'die Funktion %s ist veraltet, bitte verwenden Sie stattdessen %s',
             'do_not_use_non_root_require_in_event_handler' => 'aus Performance-GrÃ¼nden dÃ¼rfen Sie require oder include nicht innerhalb einer Event-Handler-Klasse verwenden, nur requires auf oberster Ebene sind erlaubt.',
             'do_not_use_yield' => 'MadelineProto 8 benÃ¶tigt und unterstÃ¼tzt kein yield in asynchronen Funktionen, Sie mÃ¼ssen alle yield-SchlÃ¼sselwÃ¶rter entfernen, die zuvor fÃ¼r asynchrone Funktionsaufrufe verwendet wurden',
             'done' => 'Fertig!',
             'encode_double_error' => 'Double konnte nicht korrekt kodiert werden',
             'extensionRecommended' => 'Warnung: Die Erweiterung %s ist nicht installiert, bitte installieren Sie sie, um MadelineProto zu beschleunigen!',
             'extensionRequired' => 'MadelineProto benÃ¶tigt zur AusfÃ¼hrung die Erweiterung %s. %s',
             'extensionRequiredInstallWithApt' => 'Versuchen Sie, sudo apt-get install %s auszufÃ¼hren.',
             'extensionRequiredInstallWithCustomInstructions' => 'Folgen Sie den Anweisungen unter %s, um sie zu installieren.',
             'file_not_exist' => 'Die Datei existiert nicht',
             'file_parsing' => '%s wird geparst...',
             'file_type_invalid' => 'UngÃ¼ltiger Dateityp erkannt (%s)',
             'fingerprint_invalid' => 'UngÃ¼ltiger SchlÃ¼ssel-Fingerprint!',
             'go' => 'Los',
             'invalid_dl.php_session' => '%s ist kein gÃ¼ltiges Download-Skript, da seine Sitzungs-ID abweicht (erwartet %s, erhalten %s)',
             'ipc_tcp_warning' => 'UNIX sockets are not available, so the IPC server of the session listens on TCP (127.0.0.1) instead: any program running on this machine can connect to it, and control the session. On Windows 10 1803+, enable the sockets extension (extension=sockets in php.ini) to use UNIX sockets.',
             'length_too_big' => 'Die LÃ¤nge ist zu groÃŸ',
             'loginBot' => 'Geben Sie Ihr Bot-Token ein: ',
             'loginBotTokenHintWeb' => 'Das Token, das Ihnen @BotFather beim Erstellen des Bots gegeben hat.',
             'loginBotTokenWeb' => 'Bot-Token',
             'loginChoosePromptWeb' => 'MÃ¶chten Sie sich als Benutzer oder als Bot anmelden?',
             'loginCodeLeadWeb' => 'Telegram hat einen Anmeldecode an Ihre Telegram-App gesendet, oder per SMS, falls Sie nirgendwo sonst angemeldet sind.',
             'loginCodeTitleWeb' => 'Anmeldecode eingeben',
             'loginLeadWeb' => 'WÃ¤hlen Sie das Konto oder den Bot, als der diese MadelineProto-Instanz laufen soll.',
             'loginManual' => 'Alternativ kÃ¶nnen Sie auch ein Bot-Token oder eine Telefonnummer eingeben, um sich manuell anzumelden: ',
             'loginNoCode' => 'Sie haben keinen BestÃ¤tigungscode angegeben!',
             'loginNoName' => 'Sie haben keinen Vornamen angegeben!',
             'loginNoPass' => 'Sie haben kein Passwort angegeben!',
             'loginOptionBot' => 'Bot',
             'loginOptionUser' => 'Benutzer',
             'loginPasswordLabelWeb' => 'Passwort',
             'loginPasswordLeadWeb' => 'Dieses Konto ist durch ein Passwort geschÃ¼tzt, das unter Einstellungen > PrivatsphÃ¤re und Sicherheit > Zweistufige BestÃ¤tigung festgelegt wurde.',
             'loginPasswordTitleWeb' => 'Zweistufige BestÃ¤tigung',
             'loginQr' => 'Ã–ffnen Sie Telegram auf Ihrem Telefon, gehen Sie zu Einstellungen > GerÃ¤te > Desktop-GerÃ¤t verknÃ¼pfen und scannen Sie den obigen QR-Code, um sich automatisch anzumelden.',
             'loginQrCodeExpired' => 'Der QR-Code ist abgelaufen, ein neuer wird generiert...',
             'loginQrCodeSuccessful' => 'Anmeldung per QR-Code erfolgreich!',
             'loginTitleWeb' => 'Bei Telegram anmelden',
             'loginTypeWeb' => 'Anmelden als',
             'loginUser' => 'Geben Sie Ihre Telefonnummer ein: ',
             'loginUserCode' => 'Geben Sie den Code ein: ',
             'loginUserPass' => 'Geben Sie Ihr Passwort ein (Hinweis: %s): ',
             'loginUserPassHint' => 'Hinweis: %s',
             'loginUserPassWeb' => 'Geben Sie Ihr Passwort ein: ',
             'loginUserPhoneCodeWeb' => 'Code',
             'loginUserPhoneHintWeb' => 'Mit LÃ¤ndervorwahl, z. B. +39 123 456 7890.',
             'loginUserPhoneWeb' => 'Telefonnummer',
             'loginWebQr' => 'Sie kÃ¶nnen sich auch automatisch anmelden, indem Sie Telegram auf Ihrem Telefon Ã¶ffnen, zu Einstellungen > GerÃ¤te > Desktop-GerÃ¤t verknÃ¼pfen gehen und den folgenden QR-Code scannen:',
             'loginWebQr1' => 'Ã–ffnen Sie Telegram auf Ihrem Telefon',
             'loginWebQr2' => 'Gehen Sie zu Einstellungen > GerÃ¤te > Desktop-GerÃ¤t verknÃ¼pfen',
             'loginWebQr3' => 'Richten Sie Ihr Telefon auf diesen Bildschirm, um die Anmeldung zu bestÃ¤tigen',
             'login_2fa_enabled' => '2FA ist aktiviert, Sie mÃ¼ssen die Funktion complete2falogin aufrufen...',
             'login_auth_key' => 'Anmeldung mit Auth-Key...',
             'login_bot' => 'Anmeldung als Bot...',
             'login_code_sending' => 'Code wird gesendet...',
             'login_code_sent' => 'Code erfolgreich gesendet! Sobald Sie den Code erhalten haben, sollten Sie die Funktion completePhoneLogin verwenden.',
             'login_code_uncalled' => 'Ich warte nicht auf den Code! Bitte rufen Sie zuerst die Methode phoneLogin auf',
             'login_need_signup' => 'FÃ¼r diese Nummer wurde noch kein Konto erstellt, Sie mÃ¼ssen die Funktion completeSignup aufrufen...',
             'login_ok' => 'Erfolgreich angemeldet!',
             'login_user' => 'Anmeldung als normaler Benutzer...',
             'logout_ok' => 'Erfolgreich abgemeldet!',
             'long_not_16' => 'Der angegebene Wert ist nicht 16 Bytes lang',
             'long_not_32' => 'Der angegebene Wert ist nicht 32 Bytes lang',
             'long_not_64' => 'Der angegebene Wert ist nicht 64 Bytes lang',
             'madelineproto_ready' => 'MadelineProto ist bereit!',
             'manualAdminActionRequired' => '!!!!!!!!! MANUELLER EINGRIFF DES SYSTEMADMINISTRATORS ERFORDERLICH !!!!!!!!!',
             'method_not_found' => 'Methode nicht gefunden: ',
             'mmapErrorPart1' => 'Die maximale Anzahl speicherabgebildeter Bereiche (mmap) wurde erreicht (%s): ErhÃ¶hen Sie zur Behebung den Kernel-Parameter vm.max_map_count auf 262144.',
             'mmapErrorPart2' => 'FÃ¼hren Sie zur Behebung den folgenden Befehl als root aus: %s',
             'mmapErrorPart3' => 'Um die Ã„nderung Ã¼ber Neustarts hinweg beizubehalten: %s',
             'mmapErrorPart4' => 'Unter Windows und WSL kann eine VergrÃ¶ÃŸerung der Auslagerungsdatei helfen; bitte wechseln Sie zu nativem Linux, falls das Problem weiterhin besteht.',
             'must_have_declare_types' => 'aus Performance-GrÃ¼nden muss die erste Anweisung einer Event-Handler-Datei "declare(strict_types=1);" sein',
             'nearest_dc' => 'Wir sind in %s, das nÃ¤chstgelegene DC ist %d.',
             'need_dl.php' => 'Das Standard-Download-Skript konnte nicht generiert werden (%s), bitte erstellen Sie eine Datei dl.php mit folgendem Inhalt: %s und Ã¼bergeben Sie ihre URL als zweiten Parameter an getDownloadLink',
             'noReportPeers' => 'Warnung: Es sind keine Report-Peers festgelegt, bitte fÃ¼gen Sie Ihrem Event-Handler die folgende Methode hinzu',
             'non_text_conversion' => 'Nicht-Text-Nachrichten kÃ¶nnen noch nicht konvertiert werden!',
             'not_loggedIn' => 'Ich bin nicht angemeldet!',
             'not_numeric' => 'Der angegebene Wert ist nicht numerisch',
             'params_missing' => 'Erforderlicher Parameter fehlt',
             'peer_not_in_db' => 'Dieser Peer ist nicht in der internen Peer-Datenbank vorhanden',
             'plugin_path_does_not_exist' => 'Der Plugin-Pfad %s existiert nicht!',
             'plugins_do_not_use_require' => 'aus Performance-GrÃ¼nden kÃ¶nnen Plugins andere Dateien im Plugin-Ordner nur automatisch Ã¼ber den PSR-4-Autoloader einbinden (nicht durch manuelles require()).',
             'plugins_must_have_exactly_one_class' => 'ein Plugin muss genau eine Klasse definieren! Um mehrere Klassen, Interfaces oder Traits zu definieren, erstellen Sie separate Dateien, sie werden von MadelineProto automatisch geladen.',
             'predicate_not_set' => 'PrÃ¤dikat (Wert unter _) wurde nicht gesetzt!',
             'recommend_not_use_filesystem_function' => 'die Verwendung der Funktion %s wird nicht empfohlen, da Dateisystemzugriffe wÃ¤hrend der Verarbeitung von Updates Ihren Bot verlangsamen. Unter https://docs.madelineproto.xyz/docs/UPDATES.html#avoiding-the-use-of-filesystem-functions finden Sie alternative MÃ¶glichkeiten, Daten zu speichern, die Ihren Bot nicht verlangsamen!',
             'rpc_tg_error' => 'Telegram hat einen RPC-Fehler zurÃ¼ckgegeben: %s (%s), verursacht durch %s:%s%sTL-Trace:',
             'sec_peer_not_in_db' => 'Dieser geheime Peer ist nicht in der internen Peer-Datenbank vorhanden',
             'secret_chat_skipping' => 'Der geheime Chat %s ist nicht in der Datenbank vorhanden, Nachricht wird Ã¼bersprungen...',
             'serialization_ofd' => 'Die Serialisierung ist veraltet, das Objekt wird neu aufgebaut!',
             'session_corrupted' => 'Die Sitzung ist beschÃ¤digt!',
             'signing_up' => 'Registrierung als normaler Benutzer...',
             'signupFirstName' => 'Geben Sie Ihren Vornamen ein: ',
             'signupFirstNameWeb' => 'Vorname',
             'signupLastName' => 'Geben Sie Ihren Nachnamen ein (kann leer bleiben): ',
             'signupLastNameWeb' => 'Nachname',
             'signupLeadWeb' => 'Diese Telefonnummer ist noch nicht bei Telegram: WÃ¤hlen Sie den Namen des neuen Kontos.',
             'signupTitleWeb' => 'Konto erstellen',
             'signupWeb' => 'Bitte registrieren Sie sich',
             'signup_ok' => 'Erfolgreich registriert!',
             'signup_uncalled' => 'Ich warte nicht auf eine Registrierung! Bitte rufen Sie zuerst die Methoden phoneLogin und completePhoneLogin auf!',
             'src_file_invalid' => 'UngÃ¼ltige Quelldatei angegeben: ',
             'static_analysis_minor' => 'Bei der statischen Analyse von %s wurde ein geringfÃ¼giges Problem festgestellt: %s',
             'static_analysis_severe' => 'Bei der statischen Analyse von %s wurde ein schwerwiegendes Problem festgestellt: %s',
             'stream_handle_invalid' => 'Es wurde ein ungÃ¼ltiges Stream-Handle angegeben.',
             'string_required' => 'Ein String wurde erwartet!',
             'translate_madelineproto_cli' => 'MadelineProto kann in Ihre Sprache Ã¼bersetzt werden (aktueller Ãœbersetzungsfortschritt: %d%%), besuchen Sie https://github.com/danog/MadelineProto/tree/v8/langs, um zur Ãœbersetzung beizutragen!',
             'translate_madelineproto_web' => 'MadelineProto kann in Ihre Sprache Ã¼bersetzt werden (aktueller Ãœbersetzungsfortschritt: %d%%), klicken Sie <a href="https://github.com/danog/MadelineProto/tree/v8/langs" target="_blank">hier, um zur Ãœbersetzung beizutragen!</a>',
             'tweb.ACCESS_TOKEN_EXPIRED' => 'Dieses Bot-Token wurde widerrufen',
             'tweb.ACCESS_TOKEN_INVALID' => 'UngÃ¼ltiges Bot-Token',
             'tweb.AccDescr.LoginCode' => 'Anmeldecode',
             'tweb.Close' => 'SchlieÃŸen',
             'tweb.Code' => 'Code',
             'tweb.Contacts.PhoneNumber.Placeholder' => 'Telefonnummer',
             'tweb.Country' => 'Land',
             'tweb.DarkMode' => 'Dunkler Modus',
             'tweb.Error.AnError' => 'Ein Fehler ist aufgetreten. Bitte versuchen Sie es spÃ¤ter erneut.',
             'tweb.FIRSTNAME_INVALID' => 'UngÃ¼ltiger Vorname',
             'tweb.FirstName' => 'Vorname (erforderlich)',
             'tweb.LASTNAME_INVALID' => 'UngÃ¼ltiger Nachname',
             'tweb.LastName' => 'Nachname (optional)',
             'tweb.Login.Bot' => 'Als Bot anmelden >',
             'tweb.Login.Bot.Cancel' => 'Als Benutzer anmelden >',
             'tweb.Login.Bot.Subtitle' => 'Geben Sie das Token ein, das Ihnen @BotFather
beim Erstellen des Bots gegeben hat.',
             'tweb.Login.Bot.Title' => 'Als Bot anmelden',
             'tweb.Login.Bot.Token' => 'Bot-Token',
             'tweb.Login.Code.SecretPhrase' => 'Geheimer Satz',
             'tweb.Login.Code.SecretWord' => 'Geheimes Wort',
             'tweb.Login.Code.SentCall' => 'Wir rufen Sie an und diktieren
Ihnen den Code.',
             'tweb.Login.Code.SentEmail' => 'Bitte prÃ¼fen Sie Ihre E-Mails unter **%s** (auch den Spam-Ordner) und geben Sie den Code ein, den wir Ihnen gerade gesendet haben.',
             'tweb.Login.Code.SentInApp' => 'Wir haben Ihnen eine Nachricht in Telegram
mit dem Code gesendet.',
             'tweb.Login.Code.SentMissedCall' => 'In den nÃ¤chsten Sekunden erhalten Sie
einen kurzen Anruf von **%s**. Nehmen Sie nicht ab â€“
geben Sie einfach die letzten Ziffern dieser Nummer ein.',
             'tweb.Login.Code.SentSms' => 'Wir haben Ihnen eine SMS
mit dem Code gesendet.',
             'tweb.Login.Code.SentSmsPhrase' => 'Wir haben eine SMS mit einem geheimen Satz
an Ihre Nummer **%s** gesendet.',
             'tweb.Login.Code.SentSmsPhraseBeginning' => 'Wir haben eine SMS mit einem geheimen Satz, der
mit â€ž**%2$s**â€œ beginnt, an Ihre Nummer **%1$s** gesendet.',
             'tweb.Login.Code.SentSmsWord' => 'Wir haben eine SMS mit einem geheimen Wort
an Ihre Nummer **%s** gesendet.',
             'tweb.Login.Code.SentSmsWordBeginning' => 'Wir haben eine SMS mit einem geheimen Wort, das
mit â€ž**%2$s**â€œ beginnt, an Ihre Nummer **%1$s** gesendet.',
             'tweb.Login.Code.SentUnknown' => 'Bitte suchen Sie Ã¼berall
nach einem Code (Typ: %s)',
             'tweb.Login.Code.WordBeginningInvalid' => 'Falsches Wort',
             'tweb.Login.Code.WordInvalid' => 'Falsch, bitte versuchen Sie es erneut',
             'tweb.Login.Next' => 'Weiter',
             'tweb.Login.Password.Subtitle' => 'Ihr Konto ist durch ein
zusÃ¤tzliches Passwort geschÃ¼tzt',
             'tweb.Login.Password.Title' => 'Passwort eingeben',
             'tweb.Login.PhoneLabelInvalid' => 'UngÃ¼ltige Telefonnummer',
             'tweb.Login.QR.Cancel' => 'Mit Telefonnummer anmelden >',
             'tweb.Login.QR.Help1' => 'Ã–ffnen Sie Telegram auf Ihrem Telefon',
             'tweb.Login.QR.Help2' => 'Gehen Sie zu **Einstellungen** > **GerÃ¤te** > **GerÃ¤t hinzufÃ¼gen**',
             'tweb.Login.QR.Help3' => 'Richten Sie Ihr Telefon auf diesen Bildschirm, um die Anmeldung zu bestÃ¤tigen',
             'tweb.Login.QR.Login' => 'Mit QR-Code anmelden >',
             'tweb.Login.QR.Subtitle' => 'Mit der Telegram-App auf Ihrem Telefon scannen',
             'tweb.Login.QR.Title' => 'Mit QR-Code anmelden',
             'tweb.Login.Register.Subtitle' => 'Geben Sie Ihren Namen ein,
um Ihr Konto zu erstellen',
             'tweb.Login.StartText' => 'Bitte bestÃ¤tigen Sie Ihre LÃ¤ndervorwahl
und geben Sie Ihre Telefonnummer ein.',
             'tweb.Login.Title' => 'Bei MadelineProto anmelden',
             'tweb.LoginPassword' => 'Passwort',
             'tweb.Optional' => 'optional',
             'tweb.PASSWORD_HASH_INVALID' => 'Falsches Passwort',
             'tweb.PHONE_CODE_EMPTY' => 'UngÃ¼ltiger Code',
             'tweb.PHONE_CODE_EXPIRED' => 'Code abgelaufen',
             'tweb.PHONE_CODE_INVALID' => 'UngÃ¼ltiger Code',
             'tweb.PHONE_NUMBER_BANNED' => 'Diese Telefonnummer ist gesperrt',
             'tweb.PHONE_NUMBER_FLOOD' => 'Zu viele Versuche, bitte spÃ¤ter erneut versuchen',
             'tweb.PHONE_NUMBER_INVALID' => 'UngÃ¼ltige Telefonnummer',
             'tweb.PHONE_PASSWORD_FLOOD' => 'Zu viele Versuche, bitte spÃ¤ter erneut versuchen',
             'tweb.PhoneNumber.Code.Fragment.Info' => 'Den Code finden Sie im Bereich â€žNumbersâ€œ auf [Fragment]()',
             'tweb.PleaseWait' => 'Bitte warten...',
             'tweb.StartMessaging' => 'Loslegen',
             'tweb.YourName' => 'Ihr Name',
             'type_extract_error' => 'Typ "%s" konnte nicht extrahiert werden, Sie sollten MadelineProto aktualisieren!',
             'type_extract_error_id' => 'Typ %s mit ID %s konnte nicht extrahiert werden, Sie sollten MadelineProto aktualisieren!',
             'update_madelineproto' => 'Sie verwenden eine alte Version von MadelineProto, ein Update ist erforderlich: Aktuell lÃ¤uft %s, aber die neueste Version mit zahlreichen Fehlerbehebungen und neuen Funktionen ist %s!',
             'value_bigger_than_2147483647' => 'Der angegebene Wert %s ist grÃ¶ÃŸer als 2147483647',
             'value_bigger_than_4294967296' => 'Der angegebene Wert %s ist grÃ¶ÃŸer als 4294967296',
             'value_bigger_than_9223372036854775807' => 'Der angegebene Wert %s ist grÃ¶ÃŸer als 9223372036854775807',
             'value_smaller_than_0' => 'Der angegebene Wert %s ist kleiner als 0',
             'value_smaller_than_2147483648' => 'Der angegebene Wert %s ist kleiner als -2147483648',
             'value_smaller_than_9223372036854775808' => 'Der angegebene Wert %s ist kleiner als -9223372036854775808',
             'waveform_must_have_100_values' => 'Ein Waveform-Array muss 100 Werte enthalten!',
             'waveform_value' => 'Ein Waveform-Wert muss zwischen 0 und 31 liegen!',
             'webContinue' => 'Weiter',
             'webDocs' => 'Dokumentation',
             'webDoneLead' => 'MadelineProto lÃ¤uft auf dem Server weiter: Sie kÃ¶nnen diese Seite schlieÃŸen.',
             'webError' => '%s. Bitte prÃ¼fen Sie die Eingabe und versuchen Sie es erneut.',
             'webFieldRequired' => 'Bitte fÃ¼llen Sie dieses Feld aus.',
             'webHidePassword' => 'Verbergen',
             'webLogin' => 'Anmelden',
             'webOptional' => 'optional',
             'webPhoneInvalid' => 'Geben Sie die Telefonnummer mit LÃ¤ndervorwahl ein, z. B. +39 123 456 7890.',
             'webProgress' => 'Einrichtungsfortschritt',
             'webQrTitle' => 'Mit QR-Code anmelden',
             'webQrWaiting' => 'Warten auf den Scanâ€¦',
             'webShowPassword' => 'Anzeigen',
             'webStepAccount' => 'Konto',
             'webStepApp' => 'API-ID und Hash',
             'webStepDone' => 'LÃ¤uft',
             'webStepVerify' => 'BestÃ¤tigung',
         ],
        'en' =>
         [
             '2fa_uncalled' => 'I\'m not waiting for the password! Please call the phoneLogin and the completePhoneLogin methods first!',
             'accepting_call' => 'Accepting call from %s...',
             'account_banned' => '!!!!!!! WARNING !!!!!!!
Telegram\'s flood prevention system suspended this account.
To continue, manual verification is required.
Send an email to recover@telegram.org, asking to unban the phone number %s, and shortly describe what will you do with this phone number.
Then login again.
If you intentionally deleted this account, ignore this message.',
             'already_loggedIn' => 'This instance of MadelineProto is already logged in!',
             'apiAppDescLabelWeb' => 'Description',
             'apiAppInstructionsAuto0' => 'Enter the app\'s name, can be anything: ',
             'apiAppInstructionsAuto1' => 'Enter the app\'s short name, alphanumeric, 5-32 characters: ',
             'apiAppInstructionsAuto2' => 'Enter the app/website\'s URL, or t.me/yourusername: ',
             'apiAppInstructionsAuto3' => 'Enter the app platform: ',
             'apiAppInstructionsAuto4' => 'Describe your app: ',
             'apiAppInstructionsAutoTypeOther' => 'Other (specify in description)',
             'apiAppInstructionsManual0' => 'your app\'s name, can be anything',
             'apiAppInstructionsManual1' => 'your app\'s short name, alphanumeric, 5-32 characters',
             'apiAppInstructionsManual2' => 'your app/website\'s URL, or t.me/yourusername',
             'apiAppInstructionsManual3' => 'anything',
             'apiAppInstructionsManual4' => 'Describe your app here',
             'apiAppShortNameLabelWeb' => 'Short name',
             'apiAppTitleLabelWeb' => 'App title',
             'apiAppUrlLabelWeb' => 'URL',
             'apiAppWeb' => 'Enter API information',
             'apiAutoCodeLabelWeb' => 'Login code',
             'apiAutoCodeLeadWeb' => 'my.telegram.org sent a login code to your Telegram app, in the chat with Telegram (not by SMS).',
             'apiAutoCodeTitleWeb' => 'Enter the code from Telegram',
             'apiAutoErrorTitle' => 'my.telegram.org returned an error',
             'apiAutoFallbackManual' => 'Enter the API ID and hash manually instead.',
             'apiAutoPhoneHintWeb' => 'my.telegram.org sends the login code to your Telegram app, not by SMS.',
             'apiAutoPrompt0' => 'Enter a phone number that is already registered on Telegram: ',
             'apiAutoPrompt1' => 'Enter the verification code you received in Telegram: ',
             'apiAutoWeb' => 'Enter a phone number that is <b>already registered</b> on telegram to get the API ID',
             'apiChooseAutoWeb' => 'Get them automatically',
             'apiChooseManualAuto' => 'Do you want to get the API ID and hash automatically from my.telegram.org (a), or enter them manually (m)?',
             'apiChooseManualAutoTip' => 'Note that you can also provide the API ID/hash directly in the code using the settings: %s',
             'apiChooseManualAutoTipWeb' => 'Note that you can also provide the API ID/hash directly in the code using the <a target="_blank" href="%s">settings</a>.',
             'apiChooseManualWeb' => 'I already have an API ID',
             'apiChoosePrompt' => 'Your choice (m/a): ',
             'apiCreateErrorTitle' => 'my.telegram.org didn\'t create the app',
             'apiCreateLeadWeb' => 'This account has no app on my.telegram.org yet. These details are only shown to you, and can be changed later.',
             'apiCreateTitleWeb' => 'Create your app',
             'apiCreateVpsWarning' => 'Telegram doesn\'t let VPS, VPN and other datacenter IP ranges create new apps on my.telegram.org, and the request was made from the IP of the machine running MadelineProto. Create the app manually at https://my.telegram.org from a residential connection (like your phone on mobile data), then enter the API ID and hash.',
             'apiCreateWeb' => 'Create app',
             'apiError' => 'ERROR: %s. Try again.',
             'apiInvalidWeb' => 'The API ID is a number, and the API hash is 32 characters long: copy them again from my.telegram.org.',
             'apiManualInstructions0' => 'Login to https://my.telegram.org',
             'apiManualInstructions1' => 'Go to API development tools',
             'apiManualInstructions2' => 'Click on create application',
             'apiManualPrompt0' => 'Enter your API ID: ',
             'apiManualPrompt1' => 'Enter your API hash: ',
             'apiManualWeb' => 'Enter your API ID and API hash',
             'apiParamsError' => 'You didn\'t provide all of the required parameters!',
             'apiSaveWeb' => 'Save and continue',
             'apiSendCodeWeb' => 'Send code',
             'apiSetupLeadWeb' => 'Every Telegram app needs an API ID and hash. MadelineProto can get them for you from my.telegram.org, with the phone number of an existing Telegram account.',
             'apiSetupTitleWeb' => 'Get your API ID and hash',
             'apiStartOverWeb' => 'Start over',
             'api_not_set' => 'You must provide an api key and an api id, get your own @ my.telegram.org',
             'array_invalid' => 'You didn\'t provide a valid array',
             'baseDirLimitation' => 'A basedir limitation is configured: this can impact performance and cause some issues, please disable it if possible!',
             'bool_error' => 'Could not extract boolean',
             'botAlreadyRunning' => 'The bot is already running!',
             'botapi_conversion_error' => 'Can\'t convert %s to a bot API object',
             'call_already_accepted' => 'Call %s already accepted',
             'call_already_declined' => 'Call %s already declined',
             'call_completing' => 'Completing call from %s...',
             'call_confirming' => 'Confirming call from %s...',
             'call_discarding' => 'Discarding call %s...',
             'call_error_1' => 'Could not find and accept call %s',
             'call_error_2' => 'Could not find and confirm call %s',
             'call_error_3' => 'Could not find and complete call %s',
             'cli_need_dl.php_link' => 'Please specify a download script URL when using getDownloadLink via CLI!',
             'constructor_not_found' => 'Constructor not found for type: ',
             'could_not_connect_to_MadelineProto' => 'Could not connect to MadelineProto, please enable proc_open and remove open_basedir restrictions or disable webserver path rewrites to fix! If you already did that, make sure the CLI version of PHP is exactly the same as the web version (same version, extensions, et cetera) and check out the MadelineProto.log file for more info about the error that prevented the IPC server from starting.',
             'could_not_convert_object' => 'Could not convert object of type %s',
             'deserialization_error' => 'An error occurred on deserialization',
             'dl.php_check_logs_make_sure_session_running' => 'Either the associated MadelineProto EventHandler bot or the MadelineProto IPC server are offline, please check logs and make sure at least one of them is running!',
             'dl.php_powered_by_madelineproto' => 'Telegram file download server (up to 4GB), powered by <a href="https://docs.madelineproto.xyz" target="_blank">MadelineProto</a>!<br>Click <a href="https://docs.madelineproto.xyz/docs/FILES.html#getting-a-download-link" target="_blank">here</a> for more info on how to setup your very own Telegram file download server!',
             'do_not_delete_MadelineProto.log' => 'the MadelineProto.log file must never be deleted, please set a custom max size in the settings, instead!',
             'do_not_remove_MadelineProto.log_phar' => 'Please do not remove madeline.phar or madeline.php, or else MadelineProto will crash. If you have any problem with MadelineProto, report it to https://github.com/danog/MadelineProto or https://t.me/pwrtelegramgroup',
             'do_not_use_blocking_class' => 'for performance reasons, handlers may not use the non-async blocking class %s, please use %s, instead',
             'do_not_use_blocking_function' => 'for performance reasons, event handlers may not use the non-async blocking function %s, please use %s, instead',
             'do_not_use_deprecated_function' => 'the %s function is deprecated, please use %s, instead',
             'do_not_use_non_root_require_in_event_handler' => 'for performance reasons, you must not use require or include inside of an event handler class, only root-level requires are allowed.',
             'do_not_use_yield' => 'MadelineProto 8 does not require or support the use of yield in async functions, you must remove all yield keywords previously used for async function calls',
             'done' => 'Done!',
             'encode_double_error' => 'Could not properly encode double',
             'extensionRecommended' => 'Warning: the %s extension is not installed, please install it to speed up MadelineProto!',
             'extensionRequired' => 'MadelineProto requires the %s extension to run. %s',
             'extensionRequiredInstallWithApt' => 'Try running sudo apt-get install %s.',
             'extensionRequiredInstallWithCustomInstructions' => 'Follow the instructions at %s to install it.',
             'file_not_exist' => 'File does not exist',
             'file_parsing' => 'Parsing %s...',
             'file_type_invalid' => 'Invalid file type detected (%s)',
             'fingerprint_invalid' => 'Invalid key fingerprint!',
             'go' => 'Go',
             'invalid_dl.php_session' => '%s is not a valid download script because its session ID is different (expected %s, got %s)',
             'ipc_tcp_warning' => 'UNIX sockets are not available, so the IPC server of the session listens on TCP (127.0.0.1) instead: any program running on this machine can connect to it, and control the session. On Windows 10 1803+, enable the sockets extension (extension=sockets in php.ini) to use UNIX sockets.',
             'length_too_big' => 'Length is too big',
             'loginBot' => 'Enter your bot token: ',
             'loginBotTokenHintWeb' => 'The token @BotFather gave you when you created the bot.',
             'loginBotTokenWeb' => 'Bot token',
             'loginChoosePromptWeb' => 'Do you want to login as a user or as a bot?',
             'loginCodeLeadWeb' => 'Telegram sent a login code to your Telegram app, or by SMS if you aren\'t logged in anywhere else.',
             'loginCodeTitleWeb' => 'Enter the login code',
             'loginLeadWeb' => 'Choose the account or the bot this MadelineProto instance will run as.',
             'loginManual' => 'Alternatively, you can also enter a bot token or phone number to login manually: ',
             'loginNoCode' => 'You didn\'t provide a phone code!',
             'loginNoName' => 'You didn\'t provide the first name!',
             'loginNoPass' => 'You didn\'t provide the password!',
             'loginOptionBot' => 'Bot',
             'loginOptionUser' => 'User',
             'loginPasswordLabelWeb' => 'Password',
             'loginPasswordLeadWeb' => 'This account is protected by a password, set in Settings > Privacy and Security > Two-Step Verification.',
             'loginPasswordTitleWeb' => 'Two-step verification',
             'loginQr' => 'Open Telegram on your phone, go to Settings > Devices > Link Desktop Device and scan the above QR code to login automatically.',
             'loginQrCodeExpired' => 'The QR code expired, generating a new one...',
             'loginQrCodeSuccessful' => 'QR code login successful!',
             'loginTitleWeb' => 'Log in to Telegram',
             'loginTypeWeb' => 'Log in as',
             'loginUser' => 'Enter your phone number: ',
             'loginUserCode' => 'Enter the code: ',
             'loginUserPass' => 'Enter your password (hint %s): ',
             'loginUserPassHint' => 'Hint: %s',
             'loginUserPassWeb' => 'Enter your password: ',
             'loginUserPhoneCodeWeb' => 'Code',
             'loginUserPhoneHintWeb' => 'With the country code, like +39 123 456 7890.',
             'loginUserPhoneWeb' => 'Phone number',
             'loginWebQr' => 'You can also login automatically by opening Telegram on your phone, going to Settings > Devices > Link Desktop Device and scanning the following QR code:',
             'loginWebQr1' => 'Open Telegram on your phone',
             'loginWebQr2' => 'Go to Settings > Devices > Link Desktop Device',
             'loginWebQr3' => 'Point your phone at this screen to confirm login',
             'login_2fa_enabled' => '2FA enabled, you will have to call the complete2falogin function...',
             'login_auth_key' => 'Logging in using auth key...',
             'login_bot' => 'Logging in as a bot...',
             'login_code_sending' => 'Sending code...',
             'login_code_sent' => 'Code sent successfully! Once you receive the code you should use the completePhoneLogin function.',
             'login_code_uncalled' => 'I\'m not waiting for the code! Please call the phoneLogin method first',
             'login_need_signup' => 'An account has not been created for this number, you will have to call the completeSignup function...',
             'login_ok' => 'Logged in successfully!',
             'login_user' => 'Logging in as a normal user...',
             'logout_ok' => 'Logged out successfully!',
             'long_not_16' => 'Given value is not 16 bytes long',
             'long_not_32' => 'Given value is not 32 bytes long',
             'long_not_64' => 'Given value is not 64 bytes long',
             'madelineproto_ready' => 'MadelineProto is ready!',
             'manualAdminActionRequired' => '!!!!!!!!! MANUAL SYSTEM ADMIN ACTION REQUIRED !!!!!!!!!',
             'method_not_found' => 'Could not find method: ',
             'mmapErrorPart1' => 'The maximum number of memory mapped (mmap) regions was reached (%s): please increase the vm.max_map_count kernel config to 262144 to fix.',
             'mmapErrorPart2' => 'To fix, run the following command as root: %s',
             'mmapErrorPart3' => 'To persist the change across reboots: %s',
             'mmapErrorPart4' => 'On Windows and WSL, increasing the size of the pagefile might help; please switch to native Linux if the issue persists.',
             'must_have_declare_types' => 'for performance reasons, the first statement of an event handler file must be "declare(strict_types=1);"',
             'nearest_dc' => 'We\'re in %s, nearest DC is %d.',
             'need_dl.php' => 'Could not generate default download script (%s), please create a dl.php file with the following content: %s and pass its URL to the second parameter of getDownloadLink',
             'noReportPeers' => 'Warning: no report peers are set, please add the following method to your event handler',
             'non_text_conversion' => 'Can\'t convert non text messages yet!',
             'not_loggedIn' => 'I\'m not logged in!',
             'not_numeric' => 'Given value isn\'t numeric',
             'params_missing' => 'Missing required parameter',
             'peer_not_in_db' => 'This peer is not present in the internal peer database',
             'plugin_path_does_not_exist' => 'Plugin path %s does not exist!',
             'plugins_do_not_use_require' => 'for performance reasons, plugins can only automatically include or require other files present in the plugins folder by triggering the PSR-4 autoloader (not by manually require()\'ing them).',
             'plugins_must_have_exactly_one_class' => 'a plugin must define exactly one class! To define multiple classes, interfaces or traits, create separate files, they will be autoloaded by MadelineProto automatically.',
             'predicate_not_set' => 'Predicate (value under _) was not set!',
             'recommend_not_use_filesystem_function' => 'usage of the %s function is not recommended, because accessing the filesystem during update handling will slow down your bot, please see https://docs.madelineproto.xyz/docs/UPDATES.html#avoiding-the-use-of-filesystem-functions for a list of alternative ways to store data that will not slow down your bot!',
             'rpc_tg_error' => 'Telegram returned an RPC error: %s (%s), caused by %s:%s%sTL trace:',
             'sec_peer_not_in_db' => 'This secret peer is not present in the internal peer database',
             'secret_chat_skipping' => 'I do not have the secret chat %s in the database, skipping message...',
             'serialization_ofd' => 'Serialization is out of date, reconstructing object!',
             'session_corrupted' => 'The session is corrupted!',
             'signing_up' => 'Signing up as a normal user...',
             'signupFirstName' => 'Enter your first name: ',
             'signupFirstNameWeb' => 'First name',
             'signupLastName' => 'Enter your last name (can be empty): ',
             'signupLastNameWeb' => 'Last name',
             'signupLeadWeb' => 'This phone number isn\'t on Telegram yet: choose the name of the new account.',
             'signupTitleWeb' => 'Create your account',
             'signupWeb' => 'Sign up please',
             'signup_ok' => 'Signed up in successfully!',
             'signup_uncalled' => 'I\'m not waiting to signup! Please call the phoneLogin and the completePhoneLogin methods first!',
             'src_file_invalid' => 'Invalid source file was provided: ',
             'static_analysis_minor' => 'A minor issue was encountered during static analysis of %s: %s',
             'static_analysis_severe' => 'A severe issue was encountered during static analysis of %s: %s',
             'stream_handle_invalid' => 'An invalid stream handle was provided.',
             'string_required' => 'A string was expected!',
             'translate_madelineproto_cli' => 'MadelineProto can be translated in your language (current translation progress: %d%%), go to https://github.com/danog/MadelineProto/tree/v8/langs to contribute with the translation!',
             'translate_madelineproto_web' => 'MadelineProto can be translated in your language (current translation progress: %d%%), click <a href="https://github.com/danog/MadelineProto/tree/v8/langs" target="_blank">here to contribute with the translation!</a>',
             'tweb.ACCESS_TOKEN_EXPIRED' => 'This bot token was revoked',
             'tweb.ACCESS_TOKEN_INVALID' => 'Invalid bot token',
             'tweb.AccDescr.LoginCode' => 'Login code',
             'tweb.Close' => 'Close',
             'tweb.Code' => 'Code',
             'tweb.Contacts.PhoneNumber.Placeholder' => 'Phone Number',
             'tweb.Country' => 'Country',
             'tweb.DarkMode' => 'Dark Mode',
             'tweb.Error.AnError' => 'An error occurred. Please try again later.',
             'tweb.FIRSTNAME_INVALID' => 'Invalid first name',
             'tweb.FirstName' => 'First name (required)',
             'tweb.LASTNAME_INVALID' => 'Invalid last name',
             'tweb.LastName' => 'Last name (optional)',
             'tweb.Login.Bot' => 'Log in as a bot >',
             'tweb.Login.Bot.Cancel' => 'Log in as a user >',
             'tweb.Login.Bot.Subtitle' => 'Enter the token @BotFather gave you
when you created the bot.',
             'tweb.Login.Bot.Title' => 'Log in as a Bot',
             'tweb.Login.Bot.Token' => 'Bot Token',
             'tweb.Login.Code.SecretPhrase' => 'Secret phrase',
             'tweb.Login.Code.SecretWord' => 'Secret word',
             'tweb.Login.Code.SentCall' => 'We will call you and dictate
the code.',
             'tweb.Login.Code.SentEmail' => 'Please check your email **%s** (don\'t forget the spam folder) and enter the code we just sent you.',
             'tweb.Login.Code.SentInApp' => 'We have sent you a message in Telegram
with the code.',
             'tweb.Login.Code.SentMissedCall' => 'Within the next few seconds you should receive
a short call from **%s**. Don\'t answer it â€”
just enter the last digits of that number.',
             'tweb.Login.Code.SentSms' => 'We have sent you an SMS
with the code.',
             'tweb.Login.Code.SentSmsPhrase' => 'We\'ve sent an SMS with a secret phrase
to your phone **%s**.',
             'tweb.Login.Code.SentSmsPhraseBeginning' => 'We\'ve sent an SMS with a secret phrase that starts
with "**%2$s**" to your phone **%1$s**.',
             'tweb.Login.Code.SentSmsWord' => 'We\'ve sent an SMS with a secret word
to your phone **%s**.',
             'tweb.Login.Code.SentSmsWordBeginning' => 'We\'ve sent an SMS with a secret word that starts
with "**%2$s**" to your phone **%1$s**.',
             'tweb.Login.Code.SentUnknown' => 'Please check everything
for a code (type: %s)',
             'tweb.Login.Code.WordBeginningInvalid' => 'Incorrect word',
             'tweb.Login.Code.WordInvalid' => 'Incorrect, please try again',
             'tweb.Login.Next' => 'Next',
             'tweb.Login.Password.Subtitle' => 'Your account is protected with
an additional password',
             'tweb.Login.Password.Title' => 'Enter Your Password',
             'tweb.Login.PhoneLabelInvalid' => 'Phone Number Invalid',
             'tweb.Login.QR.Cancel' => 'Log in by phone number >',
             'tweb.Login.QR.Help1' => 'Open Telegram on your phone',
             'tweb.Login.QR.Help2' => 'Go to **Settings** > **Devices** > **Add Device**',
             'tweb.Login.QR.Help3' => 'Point your phone at this screen to confirm login',
             'tweb.Login.QR.Login' => 'Log in by QR Code >',
             'tweb.Login.QR.Subtitle' => 'Scan with Telegram app on your phone',
             'tweb.Login.QR.Title' => 'Log in by QR Code',
             'tweb.Login.Register.Subtitle' => 'Enter your name
to create your account',
             'tweb.Login.StartText' => 'Please confirm your country code
and enter your phone number.',
             'tweb.Login.Title' => 'Sign in to MadelineProto',
             'tweb.LoginPassword' => 'Password',
             'tweb.Optional' => 'optional',
             'tweb.PASSWORD_HASH_INVALID' => 'Incorrect password',
             'tweb.PHONE_CODE_EMPTY' => 'Invalid code',
             'tweb.PHONE_CODE_EXPIRED' => 'Code expired',
             'tweb.PHONE_CODE_INVALID' => 'Invalid code',
             'tweb.PHONE_NUMBER_BANNED' => 'This phone number is banned',
             'tweb.PHONE_NUMBER_FLOOD' => 'Too many attempts, try again later',
             'tweb.PHONE_NUMBER_INVALID' => 'Phone Number Invalid',
             'tweb.PHONE_PASSWORD_FLOOD' => 'Too many attempts, try again later',
             'tweb.PhoneNumber.Code.Fragment.Info' => 'Get the code in the Numbers section on [Fragment]()',
             'tweb.PleaseWait' => 'Please wait...',
             'tweb.StartMessaging' => 'Start Messaging',
             'tweb.YourName' => 'Your Name',
             'type_extract_error' => 'Could not extract type "%s", you should update MadelineProto!',
             'type_extract_error_id' => 'Could not extract type: %s with id %s, you should update MadelineProto!',
             'update_madelineproto' => 'You\'re running an old version of MadelineProto, an update is required: currently running %s, but the latest version with multiple bugfixes and new features is %s!',
             'value_bigger_than_2147483647' => 'Provided value %s is bigger than 2147483647',
             'value_bigger_than_4294967296' => 'Provided value %s is bigger than 4294967296',
             'value_bigger_than_9223372036854775807' => 'Provided value %s is bigger than 9223372036854775807',
             'value_smaller_than_0' => 'Provided value %s is smaller than 0',
             'value_smaller_than_2147483648' => 'Provided value %s is smaller than -2147483648',
             'value_smaller_than_9223372036854775808' => 'Provided value %s is smaller than -9223372036854775808',
             'waveform_must_have_100_values' => 'A waveform array must have 100 values!',
             'waveform_value' => 'A waveform value must be between 0 and 31!',
             'webContinue' => 'Continue',
             'webDocs' => 'Documentation',
             'webDoneLead' => 'MadelineProto keeps running on the server: you can close this page.',
             'webError' => '%s. Check it and try again.',
             'webFieldRequired' => 'Fill in this field.',
             'webHidePassword' => 'Hide',
             'webLogin' => 'Log in',
             'webOptional' => 'optional',
             'webPhoneInvalid' => 'Enter the phone number with its country code, like +39 123 456 7890.',
             'webProgress' => 'Setup progress',
             'webQrTitle' => 'Log in with a QR code',
             'webQrWaiting' => 'Waiting for the scanâ€¦',
             'webShowPassword' => 'Show',
             'webStepAccount' => 'Account',
             'webStepApp' => 'API ID and hash',
             'webStepDone' => 'Running',
             'webStepVerify' => 'Verification',
         ],
        'es' =>
         [
             '2fa_uncalled' => 'Â¡No se estÃ¡ esperando la contraseÃ±a! Llame primero a los mÃ©todos phoneLogin y completePhoneLogin.',
             'accepting_call' => 'Aceptando la llamada de %s...',
             'account_banned' => '!!!!!!! ADVERTENCIA !!!!!!!
El sistema de prevenciÃ³n de flood de Telegram ha suspendido esta cuenta.
Para continuar, se requiere una verificaciÃ³n manual.
EnvÃ­e un correo electrÃ³nico a recover@telegram.org solicitando el desbloqueo del nÃºmero de telÃ©fono %s y describa brevemente quÃ© harÃ¡ con este nÃºmero de telÃ©fono.
A continuaciÃ³n, vuelva a iniciar sesiÃ³n.
Si eliminÃ³ esta cuenta intencionadamente, ignore este mensaje.',
             'already_loggedIn' => 'Â¡Esta instancia de MadelineProto ya ha iniciado sesiÃ³n!',
             'apiAppDescLabelWeb' => 'DescripciÃ³n',
             'apiAppInstructionsAuto0' => 'Introduzca el nombre de la aplicaciÃ³n, puede ser cualquiera: ',
             'apiAppInstructionsAuto1' => 'Introduzca el nombre corto de la aplicaciÃ³n, alfanumÃ©rico, de 5 a 32 caracteres: ',
             'apiAppInstructionsAuto2' => 'Introduzca la URL de la aplicaciÃ³n/sitio web, o t.me/yourusername: ',
             'apiAppInstructionsAuto3' => 'Introduzca la plataforma de la aplicaciÃ³n: ',
             'apiAppInstructionsAuto4' => 'Describa su aplicaciÃ³n: ',
             'apiAppInstructionsAutoTypeOther' => 'Otra (especifÃ­quela en la descripciÃ³n)',
             'apiAppInstructionsManual0' => 'el nombre de su aplicaciÃ³n, puede ser cualquiera',
             'apiAppInstructionsManual1' => 'el nombre corto de su aplicaciÃ³n, alfanumÃ©rico, de 5 a 32 caracteres',
             'apiAppInstructionsManual2' => 'la URL de su aplicaciÃ³n/sitio web, o t.me/yourusername',
             'apiAppInstructionsManual3' => 'cualquiera',
             'apiAppInstructionsManual4' => 'Describa aquÃ­ su aplicaciÃ³n',
             'apiAppShortNameLabelWeb' => 'Nombre corto',
             'apiAppTitleLabelWeb' => 'Nombre de la aplicaciÃ³n',
             'apiAppUrlLabelWeb' => 'URL',
             'apiAppWeb' => 'Introduzca la informaciÃ³n de la API',
             'apiAutoCodeLabelWeb' => 'CÃ³digo de inicio de sesiÃ³n',
             'apiAutoCodeLeadWeb' => 'my.telegram.org ha enviado un cÃ³digo de inicio de sesiÃ³n a su aplicaciÃ³n de Telegram, en el chat con Telegram (no por SMS).',
             'apiAutoCodeTitleWeb' => 'Introduzca el cÃ³digo de Telegram',
             'apiAutoErrorTitle' => 'my.telegram.org ha devuelto un error',
             'apiAutoFallbackManual' => 'Introduzca el API ID y el hash manualmente.',
             'apiAutoPhoneHintWeb' => 'my.telegram.org envÃ­a el cÃ³digo de inicio de sesiÃ³n a su aplicaciÃ³n de Telegram, no por SMS.',
             'apiAutoPrompt0' => 'Introduzca un nÃºmero de telÃ©fono ya registrado en Telegram: ',
             'apiAutoPrompt1' => 'Introduzca el cÃ³digo de verificaciÃ³n que ha recibido en Telegram: ',
             'apiAutoWeb' => 'Introduzca un nÃºmero de telÃ©fono <b>ya registrado</b> en Telegram para obtener el API ID',
             'apiChooseAutoWeb' => 'Obtenerlos automÃ¡ticamente',
             'apiChooseManualAuto' => 'Â¿Desea obtener el API ID y el hash automÃ¡ticamente desde my.telegram.org (a) o introducirlos manualmente (m)?',
             'apiChooseManualAutoTip' => 'Tenga en cuenta que tambiÃ©n puede proporcionar el API ID/hash directamente en el cÃ³digo mediante la configuraciÃ³n: %s',
             'apiChooseManualAutoTipWeb' => 'Tenga en cuenta que tambiÃ©n puede proporcionar el API ID/hash directamente en el cÃ³digo mediante la <a target="_blank" href="%s">configuraciÃ³n</a>.',
             'apiChooseManualWeb' => 'Ya tengo un API ID',
             'apiChoosePrompt' => 'Su elecciÃ³n (m/a): ',
             'apiCreateErrorTitle' => 'my.telegram.org no ha creado la aplicaciÃ³n',
             'apiCreateLeadWeb' => 'Esta cuenta aÃºn no tiene ninguna aplicaciÃ³n en my.telegram.org. Estos datos solo los verÃ¡ usted y podrÃ¡ cambiarlos mÃ¡s adelante.',
             'apiCreateTitleWeb' => 'Cree su aplicaciÃ³n',
             'apiCreateVpsWarning' => 'Telegram no permite crear nuevas aplicaciones en my.telegram.org desde rangos de IP de VPS, VPN u otros centros de datos, y la solicitud se hizo desde la IP de la mÃ¡quina que ejecuta MadelineProto. Cree la aplicaciÃ³n manualmente en https://my.telegram.org desde una conexiÃ³n residencial (como su telÃ©fono con datos mÃ³viles) y luego introduzca el API ID y el hash.',
             'apiCreateWeb' => 'Crear aplicaciÃ³n',
             'apiError' => 'ERROR: %s. IntÃ©ntelo de nuevo.',
             'apiInvalidWeb' => 'El API ID es un nÃºmero y el API hash tiene 32 caracteres: cÃ³pielos de nuevo desde my.telegram.org.',
             'apiManualInstructions0' => 'Inicie sesiÃ³n en https://my.telegram.org',
             'apiManualInstructions1' => 'Vaya a "API development tools"',
             'apiManualInstructions2' => 'Haga clic en "Create application"',
             'apiManualPrompt0' => 'Introduzca su API ID: ',
             'apiManualPrompt1' => 'Introduzca su API hash: ',
             'apiManualWeb' => 'Introduzca su API ID y su API hash',
             'apiParamsError' => 'Â¡No ha proporcionado todos los parÃ¡metros obligatorios!',
             'apiSaveWeb' => 'Guardar y continuar',
             'apiSendCodeWeb' => 'Enviar cÃ³digo',
             'apiSetupLeadWeb' => 'Toda aplicaciÃ³n de Telegram necesita un API ID y un hash. MadelineProto puede obtenerlos por usted desde my.telegram.org, con el nÃºmero de telÃ©fono de una cuenta de Telegram existente.',
             'apiSetupTitleWeb' => 'Obtenga su API ID y su hash',
             'apiStartOverWeb' => 'Empezar de nuevo',
             'api_not_set' => 'Debe proporcionar una clave de API y un ID de API, obtenga los suyos en my.telegram.org',
             'array_invalid' => 'No ha proporcionado un array vÃ¡lido',
             'baseDirLimitation' => 'Hay configurada una limitaciÃ³n de basedir: puede afectar al rendimiento y causar algunos problemas, desactÃ­vela si es posible.',
             'bool_error' => 'No se pudo extraer el booleano',
             'botAlreadyRunning' => 'Â¡El bot ya estÃ¡ en ejecuciÃ³n!',
             'botapi_conversion_error' => 'No se puede convertir %s en un objeto de la bot API',
             'call_already_accepted' => 'La llamada %s ya ha sido aceptada',
             'call_already_declined' => 'La llamada %s ya ha sido rechazada',
             'call_completing' => 'Completando la llamada de %s...',
             'call_confirming' => 'Confirmando la llamada de %s...',
             'call_discarding' => 'Descartando la llamada %s...',
             'call_error_1' => 'No se pudo encontrar y aceptar la llamada %s',
             'call_error_2' => 'No se pudo encontrar y confirmar la llamada %s',
             'call_error_3' => 'No se pudo encontrar y completar la llamada %s',
             'cli_need_dl.php_link' => 'Â¡Especifique la URL de un script de descarga al usar getDownloadLink desde la CLI!',
             'constructor_not_found' => 'No se encontrÃ³ el constructor para el tipo: ',
             'could_not_connect_to_MadelineProto' => 'No se pudo conectar con MadelineProto: para solucionarlo, habilite proc_open y elimine las restricciones de open_basedir, o desactive las reescrituras de rutas del servidor web. Si ya lo ha hecho, asegÃºrese de que la versiÃ³n CLI de PHP sea exactamente la misma que la versiÃ³n web (misma versiÃ³n, extensiones, etc.) y consulte el archivo MadelineProto.log para obtener mÃ¡s informaciÃ³n sobre el error que impidiÃ³ que se iniciara el servidor IPC.',
             'could_not_convert_object' => 'No se pudo convertir el objeto de tipo %s',
             'deserialization_error' => 'Se produjo un error durante la deserializaciÃ³n',
             'dl.php_check_logs_make_sure_session_running' => 'El bot EventHandler de MadelineProto asociado o el servidor IPC de MadelineProto estÃ¡n desconectados, Â¡revise los registros y asegÃºrese de que al menos uno de ellos estÃ© en ejecuciÃ³n!',
             'dl.php_powered_by_madelineproto' => 'Â¡Servidor de descarga de archivos de Telegram (hasta 4 GB) basado en <a href="https://docs.madelineproto.xyz" target="_blank">MadelineProto</a>!<br>Haga clic <a href="https://docs.madelineproto.xyz/docs/FILES.html#getting-a-download-link" target="_blank">aquÃ­</a> para obtener mÃ¡s informaciÃ³n sobre cÃ³mo configurar su propio servidor de descarga de archivos de Telegram.',
             'do_not_delete_MadelineProto.log' => 'el archivo MadelineProto.log nunca debe eliminarse, Â¡en su lugar, establezca un tamaÃ±o mÃ¡ximo personalizado en la configuraciÃ³n!',
             'do_not_remove_MadelineProto.log_phar' => 'No elimine madeline.phar ni madeline.php, o MadelineProto fallarÃ¡. Si tiene algÃºn problema con MadelineProto, notifÃ­quelo en https://github.com/danog/MadelineProto o en https://t.me/pwrtelegramgroup',
             'do_not_use_blocking_class' => 'por motivos de rendimiento, los gestores no pueden usar la clase bloqueante no asÃ­ncrona %s, utilice %s en su lugar',
             'do_not_use_blocking_function' => 'por motivos de rendimiento, los gestores de eventos no pueden usar la funciÃ³n bloqueante no asÃ­ncrona %s, utilice %s en su lugar',
             'do_not_use_deprecated_function' => 'la funciÃ³n %s estÃ¡ obsoleta, utilice %s en su lugar',
             'do_not_use_non_root_require_in_event_handler' => 'por motivos de rendimiento, no se debe usar require ni include dentro de una clase de gestor de eventos, solo se permiten los require de nivel raÃ­z.',
             'do_not_use_yield' => 'MadelineProto 8 no requiere ni admite el uso de yield en funciones asÃ­ncronas, debe eliminar todas las palabras clave yield usadas anteriormente en las llamadas a funciones asÃ­ncronas',
             'done' => 'Â¡Listo!',
             'encode_double_error' => 'No se pudo codificar correctamente el valor double',
             'extensionRecommended' => 'Advertencia: la extensiÃ³n %s no estÃ¡ instalada, Â¡instÃ¡lela para acelerar MadelineProto!',
             'extensionRequired' => 'MadelineProto requiere la extensiÃ³n %s para funcionar. %s',
             'extensionRequiredInstallWithApt' => 'Intente ejecutar sudo apt-get install %s.',
             'extensionRequiredInstallWithCustomInstructions' => 'Siga las instrucciones de %s para instalarla.',
             'file_not_exist' => 'El archivo no existe',
             'file_parsing' => 'Analizando %s...',
             'file_type_invalid' => 'Se detectÃ³ un tipo de archivo no vÃ¡lido (%s)',
             'fingerprint_invalid' => 'Â¡Huella digital de la clave no vÃ¡lida!',
             'go' => 'Ir',
             'invalid_dl.php_session' => '%s no es un script de descarga vÃ¡lido porque su ID de sesiÃ³n es diferente (se esperaba %s, se obtuvo %s)',
             'ipc_tcp_warning' => 'UNIX sockets are not available, so the IPC server of the session listens on TCP (127.0.0.1) instead: any program running on this machine can connect to it, and control the session. On Windows 10 1803+, enable the sockets extension (extension=sockets in php.ini) to use UNIX sockets.',
             'length_too_big' => 'La longitud es demasiado grande',
             'loginBot' => 'Introduzca el token de su bot: ',
             'loginBotTokenHintWeb' => 'El token que le dio @BotFather al crear el bot.',
             'loginBotTokenWeb' => 'Token del bot',
             'loginChoosePromptWeb' => 'Â¿Desea iniciar sesiÃ³n como usuario o como bot?',
             'loginCodeLeadWeb' => 'Telegram ha enviado un cÃ³digo de inicio de sesiÃ³n a su aplicaciÃ³n de Telegram, o por SMS si no tiene la sesiÃ³n iniciada en ningÃºn otro sitio.',
             'loginCodeTitleWeb' => 'Introduzca el cÃ³digo de inicio de sesiÃ³n',
             'loginLeadWeb' => 'Elija la cuenta o el bot con el que se ejecutarÃ¡ esta instancia de MadelineProto.',
             'loginManual' => 'TambiÃ©n puede introducir un token de bot o un nÃºmero de telÃ©fono para iniciar sesiÃ³n manualmente: ',
             'loginNoCode' => 'Â¡No ha proporcionado el cÃ³digo de verificaciÃ³n!',
             'loginNoName' => 'Â¡No ha proporcionado el nombre!',
             'loginNoPass' => 'Â¡No ha proporcionado la contraseÃ±a!',
             'loginOptionBot' => 'Bot',
             'loginOptionUser' => 'Usuario',
             'loginPasswordLabelWeb' => 'ContraseÃ±a',
             'loginPasswordLeadWeb' => 'Esta cuenta estÃ¡ protegida con una contraseÃ±a, configurada en Ajustes > Privacidad y seguridad > VerificaciÃ³n en dos pasos.',
             'loginPasswordTitleWeb' => 'VerificaciÃ³n en dos pasos',
             'loginQr' => 'Abra Telegram en su telÃ©fono, vaya a Ajustes > Dispositivos > Vincular dispositivo de escritorio y escanee el cÃ³digo QR anterior para iniciar sesiÃ³n automÃ¡ticamente.',
             'loginQrCodeExpired' => 'El cÃ³digo QR ha caducado, generando uno nuevo...',
             'loginQrCodeSuccessful' => 'Â¡Inicio de sesiÃ³n con cÃ³digo QR completado!',
             'loginTitleWeb' => 'Iniciar sesiÃ³n en Telegram',
             'loginTypeWeb' => 'Iniciar sesiÃ³n como',
             'loginUser' => 'Introduzca su nÃºmero de telÃ©fono: ',
             'loginUserCode' => 'Introduzca el cÃ³digo: ',
             'loginUserPass' => 'Introduzca su contraseÃ±a (pista: %s): ',
             'loginUserPassHint' => 'Pista: %s',
             'loginUserPassWeb' => 'Introduzca su contraseÃ±a: ',
             'loginUserPhoneCodeWeb' => 'CÃ³digo',
             'loginUserPhoneHintWeb' => 'Con el prefijo del paÃ­s, por ejemplo +39 123 456 7890.',
             'loginUserPhoneWeb' => 'NÃºmero de telÃ©fono',
             'loginWebQr' => 'TambiÃ©n puede iniciar sesiÃ³n automÃ¡ticamente abriendo Telegram en su telÃ©fono, yendo a Ajustes > Dispositivos > Vincular dispositivo de escritorio y escaneando el siguiente cÃ³digo QR:',
             'loginWebQr1' => 'Abra Telegram en su telÃ©fono',
             'loginWebQr2' => 'Vaya a Ajustes > Dispositivos > Vincular dispositivo de escritorio',
             'loginWebQr3' => 'Apunte su telÃ©fono a esta pantalla para confirmar el inicio de sesiÃ³n',
             'login_2fa_enabled' => 'La 2FA estÃ¡ activada, deberÃ¡ llamar a la funciÃ³n complete2falogin...',
             'login_auth_key' => 'Iniciando sesiÃ³n con la clave de autorizaciÃ³n...',
             'login_bot' => 'Iniciando sesiÃ³n como bot...',
             'login_code_sending' => 'Enviando el cÃ³digo...',
             'login_code_sent' => 'Â¡CÃ³digo enviado correctamente! Cuando reciba el cÃ³digo, utilice la funciÃ³n completePhoneLogin.',
             'login_code_uncalled' => 'Â¡No se estÃ¡ esperando ningÃºn cÃ³digo! Llame primero al mÃ©todo phoneLogin',
             'login_need_signup' => 'No se ha creado ninguna cuenta para este nÃºmero, deberÃ¡ llamar a la funciÃ³n completeSignup...',
             'login_ok' => 'Â¡SesiÃ³n iniciada correctamente!',
             'login_user' => 'Iniciando sesiÃ³n como usuario normal...',
             'logout_ok' => 'Â¡SesiÃ³n cerrada correctamente!',
             'long_not_16' => 'El valor proporcionado no tiene una longitud de 16 bytes',
             'long_not_32' => 'El valor proporcionado no tiene una longitud de 32 bytes',
             'long_not_64' => 'El valor proporcionado no tiene una longitud de 64 bytes',
             'madelineproto_ready' => 'Â¡MadelineProto estÃ¡ listo!',
             'manualAdminActionRequired' => '!!!!!!!!! SE REQUIERE UNA ACCIÃ“N MANUAL DEL ADMINISTRADOR DEL SISTEMA !!!!!!!!!',
             'method_not_found' => 'No se encontrÃ³ el mÃ©todo: ',
             'mmapErrorPart1' => 'Se ha alcanzado el nÃºmero mÃ¡ximo de regiones de memoria mapeada (mmap) (%s): para solucionarlo, aumente el parÃ¡metro del kernel vm.max_map_count a 262144.',
             'mmapErrorPart2' => 'Para solucionarlo, ejecute el siguiente comando como root: %s',
             'mmapErrorPart3' => 'Para que el cambio se mantenga tras los reinicios: %s',
             'mmapErrorPart4' => 'En Windows y WSL, aumentar el tamaÃ±o del archivo de paginaciÃ³n puede ayudar; si el problema persiste, cambie a Linux nativo.',
             'must_have_declare_types' => 'por motivos de rendimiento, la primera instrucciÃ³n de un archivo de gestor de eventos debe ser "declare(strict_types=1);"',
             'nearest_dc' => 'Estamos en %s, el DC mÃ¡s cercano es %d.',
             'need_dl.php' => 'No se pudo generar el script de descarga predeterminado (%s), cree un archivo dl.php con el siguiente contenido: %s y pase su URL como segundo parÃ¡metro de getDownloadLink',
             'noReportPeers' => 'Advertencia: no se ha configurado ningÃºn peer para los informes de errores, aÃ±ada el siguiente mÃ©todo a su gestor de eventos',
             'non_text_conversion' => 'Â¡TodavÃ­a no se pueden convertir mensajes que no sean de texto!',
             'not_loggedIn' => 'Â¡No se ha iniciado sesiÃ³n!',
             'not_numeric' => 'El valor proporcionado no es numÃ©rico',
             'params_missing' => 'Falta un parÃ¡metro obligatorio',
             'peer_not_in_db' => 'Este peer no estÃ¡ presente en la base de datos interna de peers',
             'plugin_path_does_not_exist' => 'Â¡La ruta del plugin %s no existe!',
             'plugins_do_not_use_require' => 'por motivos de rendimiento, los plugins solo pueden incluir o requerir automÃ¡ticamente otros archivos presentes en la carpeta de plugins a travÃ©s del autoloader PSR-4 (no mediante require() manuales).',
             'plugins_must_have_exactly_one_class' => 'Â¡un plugin debe definir exactamente una clase! Para definir varias clases, interfaces o traits, cree archivos separados: MadelineProto los cargarÃ¡ automÃ¡ticamente.',
             'predicate_not_set' => 'Â¡No se ha establecido el predicado (el valor de _)!',
             'recommend_not_use_filesystem_function' => 'no se recomienda usar la funciÃ³n %s, porque acceder al sistema de archivos durante la gestiÃ³n de las actualizaciones ralentizarÃ¡ su bot; consulte https://docs.madelineproto.xyz/docs/UPDATES.html#avoiding-the-use-of-filesystem-functions para ver una lista de formas alternativas de almacenar datos que no ralentizarÃ¡n su bot.',
             'rpc_tg_error' => 'Telegram devolviÃ³ un error RPC: %s (%s), causado por %s:%s%sTraza TL:',
             'sec_peer_not_in_db' => 'Este peer secreto no estÃ¡ presente en la base de datos interna de peers',
             'secret_chat_skipping' => 'El chat secreto %s no estÃ¡ en la base de datos, se omite el mensaje...',
             'serialization_ofd' => 'La serializaciÃ³n estÃ¡ desactualizada, Â¡reconstruyendo el objeto!',
             'session_corrupted' => 'Â¡La sesiÃ³n estÃ¡ daÃ±ada!',
             'signing_up' => 'RegistrÃ¡ndose como usuario normal...',
             'signupFirstName' => 'Introduzca su nombre: ',
             'signupFirstNameWeb' => 'Nombre',
             'signupLastName' => 'Introduzca sus apellidos (puede dejarse vacÃ­o): ',
             'signupLastNameWeb' => 'Apellidos',
             'signupLeadWeb' => 'Este nÃºmero de telÃ©fono aÃºn no estÃ¡ en Telegram: elija el nombre de la nueva cuenta.',
             'signupTitleWeb' => 'Cree su cuenta',
             'signupWeb' => 'RegÃ­strese',
             'signup_ok' => 'Â¡Registro completado correctamente!',
             'signup_uncalled' => 'Â¡No se estÃ¡ esperando ningÃºn registro! Llame primero a los mÃ©todos phoneLogin y completePhoneLogin.',
             'src_file_invalid' => 'Se proporcionÃ³ un archivo de origen no vÃ¡lido: ',
             'static_analysis_minor' => 'Se encontrÃ³ un problema menor durante el anÃ¡lisis estÃ¡tico de %s: %s',
             'static_analysis_severe' => 'Se encontrÃ³ un problema grave durante el anÃ¡lisis estÃ¡tico de %s: %s',
             'stream_handle_invalid' => 'Se proporcionÃ³ un descriptor de flujo no vÃ¡lido.',
             'string_required' => 'Â¡Se esperaba una cadena!',
             'translate_madelineproto_cli' => 'MadelineProto puede traducirse a su idioma (progreso actual de la traducciÃ³n: %d%%), visite https://github.com/danog/MadelineProto/tree/v8/langs para contribuir a la traducciÃ³n.',
             'translate_madelineproto_web' => 'MadelineProto puede traducirse a su idioma (progreso actual de la traducciÃ³n: %d%%), haga clic <a href="https://github.com/danog/MadelineProto/tree/v8/langs" target="_blank">aquÃ­ para contribuir a la traducciÃ³n.</a>',
             'tweb.ACCESS_TOKEN_EXPIRED' => 'Este token del bot ha sido revocado',
             'tweb.ACCESS_TOKEN_INVALID' => 'Token del bot no vÃ¡lido',
             'tweb.AccDescr.LoginCode' => 'CÃ³digo de inicio de sesiÃ³n',
             'tweb.Close' => 'Cerrar',
             'tweb.Code' => 'CÃ³digo',
             'tweb.Contacts.PhoneNumber.Placeholder' => 'NÃºmero de telÃ©fono',
             'tweb.Country' => 'PaÃ­s',
             'tweb.DarkMode' => 'Modo oscuro',
             'tweb.Error.AnError' => 'Se ha producido un error. IntÃ©ntelo de nuevo mÃ¡s tarde.',
             'tweb.FIRSTNAME_INVALID' => 'Nombre no vÃ¡lido',
             'tweb.FirstName' => 'Nombre (obligatorio)',
             'tweb.LASTNAME_INVALID' => 'Apellidos no vÃ¡lidos',
             'tweb.LastName' => 'Apellidos (opcional)',
             'tweb.Login.Bot' => 'Iniciar sesiÃ³n como bot >',
             'tweb.Login.Bot.Cancel' => 'Iniciar sesiÃ³n como usuario >',
             'tweb.Login.Bot.Subtitle' => 'Introduzca el token que le dio @BotFather
al crear el bot.',
             'tweb.Login.Bot.Title' => 'Iniciar sesiÃ³n como bot',
             'tweb.Login.Bot.Token' => 'Token del bot',
             'tweb.Login.Code.SecretPhrase' => 'Frase secreta',
             'tweb.Login.Code.SecretWord' => 'Palabra secreta',
             'tweb.Login.Code.SentCall' => 'Le llamaremos para dictarle
el cÃ³digo.',
             'tweb.Login.Code.SentEmail' => 'Revise su correo **%s** (no olvide la carpeta de spam) e introduzca el cÃ³digo que le acabamos de enviar.',
             'tweb.Login.Code.SentInApp' => 'Le hemos enviado un mensaje en Telegram
con el cÃ³digo.',
             'tweb.Login.Code.SentMissedCall' => 'En los prÃ³ximos segundos recibirÃ¡
una breve llamada de **%s**. No la conteste:
solo introduzca los Ãºltimos dÃ­gitos de ese nÃºmero.',
             'tweb.Login.Code.SentSms' => 'Le hemos enviado un SMS
con el cÃ³digo.',
             'tweb.Login.Code.SentSmsPhrase' => 'Hemos enviado un SMS con una frase secreta
a su telÃ©fono **%s**.',
             'tweb.Login.Code.SentSmsPhraseBeginning' => 'Hemos enviado un SMS con una frase secreta que empieza
por "**%2$s**" a su telÃ©fono **%1$s**.',
             'tweb.Login.Code.SentSmsWord' => 'Hemos enviado un SMS con una palabra secreta
a su telÃ©fono **%s**.',
             'tweb.Login.Code.SentSmsWordBeginning' => 'Hemos enviado un SMS con una palabra secreta que empieza
por "**%2$s**" a su telÃ©fono **%1$s**.',
             'tweb.Login.Code.SentUnknown' => 'Busque un cÃ³digo
en todas partes (tipo: %s)',
             'tweb.Login.Code.WordBeginningInvalid' => 'Palabra incorrecta',
             'tweb.Login.Code.WordInvalid' => 'Incorrecto, intÃ©ntelo de nuevo',
             'tweb.Login.Next' => 'Siguiente',
             'tweb.Login.Password.Subtitle' => 'Su cuenta estÃ¡ protegida con
una contraseÃ±a adicional',
             'tweb.Login.Password.Title' => 'Introduzca su contraseÃ±a',
             'tweb.Login.PhoneLabelInvalid' => 'NÃºmero de telÃ©fono no vÃ¡lido',
             'tweb.Login.QR.Cancel' => 'Iniciar sesiÃ³n con nÃºmero de telÃ©fono >',
             'tweb.Login.QR.Help1' => 'Abra Telegram en su telÃ©fono',
             'tweb.Login.QR.Help2' => 'Vaya a **Ajustes** > **Dispositivos** > **Vincular dispositivo**',
             'tweb.Login.QR.Help3' => 'Apunte su telÃ©fono a esta pantalla para confirmar el inicio de sesiÃ³n',
             'tweb.Login.QR.Login' => 'Iniciar sesiÃ³n con cÃ³digo QR >',
             'tweb.Login.QR.Subtitle' => 'Escanee con la aplicaciÃ³n de Telegram de su telÃ©fono',
             'tweb.Login.QR.Title' => 'Iniciar sesiÃ³n con cÃ³digo QR',
             'tweb.Login.Register.Subtitle' => 'Introduzca su nombre
para crear su cuenta',
             'tweb.Login.StartText' => 'Confirme el cÃ³digo de su paÃ­s
e introduzca su nÃºmero de telÃ©fono.',
             'tweb.Login.Title' => 'Iniciar sesiÃ³n en MadelineProto',
             'tweb.LoginPassword' => 'ContraseÃ±a',
             'tweb.Optional' => 'opcional',
             'tweb.PASSWORD_HASH_INVALID' => 'ContraseÃ±a incorrecta',
             'tweb.PHONE_CODE_EMPTY' => 'CÃ³digo no vÃ¡lido',
             'tweb.PHONE_CODE_EXPIRED' => 'El cÃ³digo ha caducado',
             'tweb.PHONE_CODE_INVALID' => 'CÃ³digo no vÃ¡lido',
             'tweb.PHONE_NUMBER_BANNED' => 'Este nÃºmero de telÃ©fono estÃ¡ bloqueado',
             'tweb.PHONE_NUMBER_FLOOD' => 'Demasiados intentos, intÃ©ntelo mÃ¡s tarde',
             'tweb.PHONE_NUMBER_INVALID' => 'NÃºmero de telÃ©fono no vÃ¡lido',
             'tweb.PHONE_PASSWORD_FLOOD' => 'Demasiados intentos, intÃ©ntelo mÃ¡s tarde',
             'tweb.PhoneNumber.Code.Fragment.Info' => 'Obtenga el cÃ³digo en la secciÃ³n Numbers de [Fragment]()',
             'tweb.PleaseWait' => 'Espere, por favor...',
             'tweb.StartMessaging' => 'Empezar a chatear',
             'tweb.YourName' => 'Su nombre',
             'type_extract_error' => 'No se pudo extraer el tipo "%s", Â¡actualice MadelineProto!',
             'type_extract_error_id' => 'No se pudo extraer el tipo: %s con id %s, Â¡actualice MadelineProto!',
             'update_madelineproto' => 'EstÃ¡ ejecutando una versiÃ³n antigua de MadelineProto y es necesario actualizarla: la versiÃ³n actual es %s, Â¡pero la Ãºltima versiÃ³n, con numerosas correcciones de errores y nuevas funciones, es %s!',
             'value_bigger_than_2147483647' => 'El valor proporcionado %s es mayor que 2147483647',
             'value_bigger_than_4294967296' => 'El valor proporcionado %s es mayor que 4294967296',
             'value_bigger_than_9223372036854775807' => 'El valor proporcionado %s es mayor que 9223372036854775807',
             'value_smaller_than_0' => 'El valor proporcionado %s es menor que 0',
             'value_smaller_than_2147483648' => 'El valor proporcionado %s es menor que -2147483648',
             'value_smaller_than_9223372036854775808' => 'El valor proporcionado %s es menor que -9223372036854775808',
             'waveform_must_have_100_values' => 'Â¡Un array de forma de onda debe tener 100 valores!',
             'waveform_value' => 'Â¡Un valor de forma de onda debe estar entre 0 y 31!',
             'webContinue' => 'Continuar',
             'webDocs' => 'DocumentaciÃ³n',
             'webDoneLead' => 'MadelineProto sigue ejecutÃ¡ndose en el servidor: puede cerrar esta pÃ¡gina.',
             'webError' => '%s. CompruÃ©belo e intÃ©ntelo de nuevo.',
             'webFieldRequired' => 'Rellene este campo.',
             'webHidePassword' => 'Ocultar',
             'webLogin' => 'Iniciar sesiÃ³n',
             'webOptional' => 'opcional',
             'webPhoneInvalid' => 'Introduzca el nÃºmero de telÃ©fono con el prefijo del paÃ­s, por ejemplo +39 123 456 7890.',
             'webProgress' => 'Progreso de la configuraciÃ³n',
             'webQrTitle' => 'Iniciar sesiÃ³n con un cÃ³digo QR',
             'webQrWaiting' => 'Esperando el escaneoâ€¦',
             'webShowPassword' => 'Mostrar',
             'webStepAccount' => 'Cuenta',
             'webStepApp' => 'API ID y hash',
             'webStepDone' => 'En ejecuciÃ³n',
             'webStepVerify' => 'VerificaciÃ³n',
         ],
        'fa' =>
         [
             '2fa_uncalled' => 'Ù…Ù† Ù…Ù†ØªØ¸Ø± Ú©Ù„Ù…Ù‡ Ø¹Ø¨ÙˆØ± Ù†ÛŒØ³ØªÙ…! Ù„Ø·Ù�Ø§ Ø§ÙˆÙ„ ØªÙˆØ§Ø¨Ø¹ phoneLogin Ùˆ completePhoneLogin Ø±Ø§ ØµØ¯Ø§ Ø¨Ø²Ù†ÛŒØ¯!',
             'accepting_call' => 'Ø¯Ø± Ø­Ø§Ù„ Ù¾Ø°ÛŒØ±Ø´ ØªÙ…Ø§Ø³ Ø§Ø² Ø·Ø±Ù� %s...',
             'account_banned' => '!!!!!!! Ø§Ø®Ø·Ø§Ø± !!!!!!!
Ø³ÛŒØ³ØªÙ… Ø¬Ù„ÙˆÚ¯ÛŒØ±ÛŒ Ø§Ø² Ù�Ù„ÙˆØ¯ ØªÙ„Ú¯Ø±Ø§Ù…ØŒ Ø§ÛŒÙ† Ø­Ø³Ø§Ø¨ Ø±Ø§ Ù…Ø¹Ù„Ù‚ Ú©Ø±Ø¯Ù‡.
Ø¨Ø±Ø§ÛŒ Ø§Ø¯Ø§Ù…Ù‡ØŒ ØªØ§ÛŒÛŒØ¯ Ø¯Ø³ØªÛŒ Ù†ÛŒØ§Ø² Ø§Ø³Øª.
ÛŒÚ© Ø§ÛŒÙ…ÛŒÙ„ Ø¨Ù‡ recover@telegram.org Ø¨Ù�Ø±Ø³ØªÛŒØ¯ØŒ Ùˆ Ø§Ø² Ø¢Ù†Ù‡Ø§ Ø¢Ø²Ø§Ø¯Ø³Ø§Ø²ÛŒ Ø´Ù…Ø§Ø±Ù‡ %s Ø±Ø§ Ø¬ÙˆÛŒØ§ Ø¨Ø§Ø´ÛŒØ¯ØŒ Ùˆ Ú©ÙˆØªØ§Ù‡ ØªÙˆØ¶ÛŒØ­ Ø¯Ù‡ÛŒØ¯ Ú©Ù‡ Ú†Ù‡ Ú©Ø§Ø±ÛŒ Ø¨Ø§ Ø§ÛŒÙ† Ø´Ù…Ø§Ø±Ù‡ Ø§Ù†Ø¬Ø§Ù… Ø®ÙˆØ§Ù‡ÛŒØ¯ Ø¯Ø§Ø¯.
Ø³Ù¾Ø³ Ø¯ÙˆØ¨Ø§Ø±Ù‡ ÙˆØ§Ø±Ø¯ Ø´ÙˆÛŒØ¯.
Ø§Ú¯Ø± Ø§Ø² Ù‚ØµØ¯ Ø§ÛŒÙ† Ø­Ø³Ø§Ø¨ Ø±Ø§ Ø­Ø°Ù� Ú©Ø±Ø¯Ù‡â€ŒØ§ÛŒØ¯ØŒ Ø§ÛŒÙ† Ù¾ÛŒØ§Ù… Ø±Ø§ Ù†Ø§Ø¯ÛŒØ¯Ù‡ Ø¨Ú¯ÛŒØ±ÛŒØ¯.',
             'already_loggedIn' => 'Ù†Ù…ÙˆÙ†Ù‡â€ŒÛŒ MadelineProto Ø§Ø² Ù‚Ø¨Ù„ ÙˆØ§Ø±Ø¯ Ø´Ø¯Ù‡!',
             'apiAppDescLabelWeb' => 'ØªÙˆØ¶ÛŒØ­Ø§Øª',
             'apiAppInstructionsAuto0' => 'Ù†Ø§Ù… Ø¨Ø±Ù†Ø§Ù…Ù‡ Ø®ÙˆØ¯ Ø±Ø§ ÙˆØ§Ø±Ø¯ Ú©Ù†ÛŒØ¯ØŒ Ù…ÛŒØªÙˆØ§Ù†Ø¯ Ù‡Ø±Ú†ÛŒØ²ÛŒ Ø¨Ø§Ø´Ø¯ ',
             'apiAppInstructionsAuto1' => 'Ù†Ø§Ù… Ù…Ø®Ù�Ù� Ø¨Ø±Ù†Ø§Ù…Ù‡â€ŒØªØ§Ù† Ø±Ø§ ÙˆØ§Ø±Ø¯ Ú©Ù†ÛŒØ¯ØŒ 5 ØªØ§ 32 Ú©Ø§Ø±Ø§Ú©ØªØ± Ø§Ù„Ù�Ø¨Ø§ Ø§Ø¹Ø¯Ø§Ø¯: ',
             'apiAppInstructionsAuto2' => 'Ù„ÛŒÙ†Ú© Ø¨Ø±Ù†Ø§Ù…Ù‡/ÙˆØ¨Ø³Ø§ÛŒØª Ø±Ø§ ÙˆØ§Ø±Ø¯ Ú©Ù†ÛŒØ¯ØŒ ÛŒØ§ t.me/yourusername: ',
             'apiAppInstructionsAuto3' => 'Ø³Ú©ÙˆÛŒ (Ù¾Ù„ØªÙ�Ø±Ù…) Ø¨Ø±Ù†Ø§Ù…Ù‡ Ø±Ø§ ÙˆØ§Ø±Ø¯ Ú©Ù†ÛŒØ¯: ',
             'apiAppInstructionsAuto4' => 'Ø¨Ø±Ù†Ø§Ù…Ù‡â€ŒØªØ§Ù† Ø±Ø§ ØªÙˆØµÛŒÙ� Ú©Ù†ÛŒØ¯: ',
             'apiAppInstructionsAutoTypeOther' => 'Ù…Ø§Ø¨Ù‚ÛŒ (Ø¯Ø± ØªÙˆØ¶ÛŒØ­Ø§Øª Ù…Ø´Ø®Øµ Ú©Ù†ÛŒØ¯)',
             'apiAppInstructionsManual0' => 'Ø§Ø³Ù… Ø§Ù¾Ù„ÛŒÚ©ÛŒØ´Ù† Ø´Ù…Ø§ØŒ Ù…ÛŒâ€ŒØªÙˆØ§Ù†Ø¯ Ù‡Ø±Ú†ÛŒØ²ÛŒ Ø¨Ø§Ø´Ø¯',
             'apiAppInstructionsManual1' => 'Ø§Ø³Ù… Ú©ÙˆØªØ§Ù‡ Ø§Ù¾Ù„ÛŒÚ©ÛŒØ´Ù† Ø´Ù…Ø§ØŒ 5 ØªØ§ 32 Ú©Ø§Ø±Ø§Ú©ØªØ±ØŒ Ø­Ø±ÙˆÙ� Ùˆ Ø§Ø¹Ø¯Ø§Ø¯ Ø§Ù†Ú¯Ù„ÛŒØ³ÛŒ',
             'apiAppInstructionsManual2' => 'Ù„ÛŒÙ†Ú© Ø§Ù¾Ù„ÛŒÚ©ÛŒØ´Ù†/ÙˆØ¨Ø³Ø§ÛŒØª Ø´Ù…Ø§ØŒ ÛŒØ§ t.me/yourusername',
             'apiAppInstructionsManual3' => 'Ù‡Ø±Ú†ÛŒØ²ÛŒ',
             'apiAppInstructionsManual4' => 'Ø§Ù¾Ù„ÛŒÚ©ÛŒØ´Ù† Ø®ÙˆØ¯ Ø±Ø§ Ø§ÛŒÙ†Ø¬Ø§ ØªÙˆØµÛŒÙ� Ú©Ù†ÛŒØ¯',
             'apiAppShortNameLabelWeb' => 'Ù†Ø§Ù… Ú©ÙˆØªØ§Ù‡',
             'apiAppTitleLabelWeb' => 'Ù†Ø§Ù… Ø¨Ø±Ù†Ø§Ù…Ù‡',
             'apiAppUrlLabelWeb' => 'Ù„ÛŒÙ†Ú©',
             'apiAppWeb' => 'Ø§Ø·Ù„Ø§Ø¹Ø§Øª API Ø±Ø§ ÙˆØ§Ø±Ø¯ Ú©Ù†ÛŒØ¯',
             'apiAutoCodeLabelWeb' => 'Ú©Ø¯ ÙˆØ±ÙˆØ¯',
             'apiAutoCodeLeadWeb' => 'my.telegram.org ÛŒÚ© Ú©Ø¯ ÙˆØ±ÙˆØ¯ Ø¨Ù‡ Ø¨Ø±Ù†Ø§Ù…Ù‡ Telegram Ø´Ù…Ø§ØŒ Ø¯Ø± Ú¯Ù�ØªÚ¯Ùˆ Ø¨Ø§ Telegram Ù�Ø±Ø³ØªØ§Ø¯ (Ù†Ù‡ Ø¨Ø§ Ù¾ÛŒØ§Ù…Ú©).',
             'apiAutoCodeTitleWeb' => 'Ú©Ø¯ Ø¯Ø±ÛŒØ§Ù�ØªÛŒ Ø§Ø² Telegram Ø±Ø§ ÙˆØ§Ø±Ø¯ Ú©Ù†ÛŒØ¯',
             'apiAutoErrorTitle' => 'my.telegram.org Ø®Ø·Ø§ Ø¨Ø±Ú¯Ø±Ø¯Ø§Ù†Ø¯',
             'apiAutoFallbackManual' => 'Ø¨Ù‡â€ŒØ¬Ø§ÛŒ Ø¢Ù†ØŒ API ID Ùˆ API Hash Ø±Ø§ Ø¯Ø³ØªÛŒ ÙˆØ§Ø±Ø¯ Ú©Ù†ÛŒØ¯.',
             'apiAutoPhoneHintWeb' => 'my.telegram.org Ú©Ø¯ ÙˆØ±ÙˆØ¯ Ø±Ø§ Ø¨Ù‡ Ø¨Ø±Ù†Ø§Ù…Ù‡ Telegram Ø´Ù…Ø§ Ù…ÛŒâ€ŒÙ�Ø±Ø³ØªØ¯ØŒ Ù†Ù‡ Ø¨Ø§ Ù¾ÛŒØ§Ù…Ú©.',
             'apiAutoPrompt0' => 'Ø´Ù…Ø§Ø±Ù‡â€ŒØ§ÛŒ Ø±Ø§ ÙˆØ§Ø±Ø¯ Ú©Ù†ÛŒØ¯ Ú©Ù‡ Ø§Ø² Ù‚Ø¨Ù„ Ø¯Ø± ØªÙ„Ú¯Ø±Ø§Ù… Ø«Ø¨Øªâ€ŒÙ†Ø§Ù… Ú©Ø±Ø¯Ù‡ Ø¨Ø§Ø´Ø¯: ',
             'apiAutoPrompt1' => 'Ú©Ø¯ ØªØ§ÛŒÛŒØ¯ÛŒ Ø±Ø§ Ú©Ù‡ Ø¯Ø± ØªÙ„Ú¯Ø±Ø§Ù… Ø¯Ø±ÛŒØ§Ù�Øª Ú©Ø±Ø¯ÛŒØ¯ØŒ ÙˆØ§Ø±Ø¯ Ú©Ù†ÛŒØ¯ ',
             'apiAutoWeb' => 'Ø¨Ø±Ø§ÛŒ Ø¯Ø±ÛŒØ§Ù�Øª API ID Ø´Ù…Ø§Ø±Ù‡â€ŒØ§ÛŒ Ø±Ø§ ÙˆØ§Ø±Ø¯ Ú©Ù†ÛŒØ¯ Ú©Ù‡ Ø§Ø² Ù‚Ø¨Ù„ Ø¯Ø± ØªÙ„Ú¯Ø±Ø§Ù… <b>Ø«Ø¨Øªâ€ŒÙ†Ø§Ù…</b> Ú©Ø±Ø¯Ù‡ Ø¨Ø§Ø´Ø¯',
             'apiChooseAutoWeb' => 'Ø¯Ø±ÛŒØ§Ù�Øª Ø®ÙˆØ¯Ú©Ø§Ø±',
             'apiChooseManualAuto' => 'Ù…ÛŒâ€ŒØ®ÙˆØ§Ù‡ÛŒØ¯ API ID Ùˆ API Hash Ø±Ø§ Ø¨Ù‡â€ŒØµÙˆØ±Øª Ø®ÙˆØ¯Ú©Ø§Ø± Ø§Ø² my.telegram.org Ø¯Ø±ÛŒØ§Ù�Øª Ú©Ù†ÛŒØ¯ (a)ØŒ ÛŒØ§ Ø¢Ù†â€ŒÙ‡Ø§ Ø±Ø§ Ø¯Ø³ØªÛŒ ÙˆØ§Ø±Ø¯ Ú©Ù†ÛŒØ¯ (m)ØŸ',
             'apiChooseManualAutoTip' => 'Ø¯Ø± Ù†Ø¸Ø± Ø¯Ø§Ø´ØªÙ‡ Ø¨Ø§Ø´ÛŒØ¯ Ú©Ù‡ Ù…ÛŒâ€ŒØªÙˆØ§Ù†ÛŒØ¯ API ID Ùˆ API Hash Ø±Ø§ Ø¨Ù‡ Ø·ÙˆØ± Ù…Ø³ØªÙ‚ÛŒÙ… Ø¯Ø§Ø®Ù„ Ú©Ø¯ØªØ§Ù† Ø¨Ø§ Ø§Ø³ØªÙ�Ø§Ø¯Ù‡ Ø§Ø² ØªÙ†Ø¸ÛŒÙ…Ø§Øª ÙˆØ§Ø±Ø¯ Ú©Ù†ÛŒØ¯. %s',
             'apiChooseManualAutoTipWeb' => 'Ø¯Ø± Ù†Ø¸Ø± Ø¨Ø§Ø´ÛŒØ¯ Ú©Ù‡ Ù…ÛŒâ€ŒØªÙˆØ§Ù†ÛŒØ¯ API ID Ùˆ API Hash Ø±Ø§ Ø¨Ù‡ ØµÙˆØ±Øª Ù…Ø³ØªÙ‚ÛŒÙ… Ø¯Ø§Ø®Ù„ Ú©Ø¯ØªØ§Ù† Ø¨Ø§ Ø§Ø³ØªÙ�Ø§Ø¯Ù‡ Ø§Ø² <a target="_blank" href="%s">ØªÙ†Ø¸ÛŒÙ…Ø§Øª</a> ÙˆØ§Ø±Ø¯ Ú©Ù†ÛŒØ¯.',
             'apiChooseManualWeb' => 'Ø§Ø² Ù‚Ø¨Ù„ API ID Ø¯Ø§Ø±Ù…',
             'apiChoosePrompt' => 'Ø§Ù†ØªØ®Ø§Ø¨ Ø´Ù…Ø§ (m/a): ',
             'apiCreateErrorTitle' => 'my.telegram.org Ø¨Ø±Ù†Ø§Ù…Ù‡ Ø±Ø§ Ù†Ø³Ø§Ø®Øª',
             'apiCreateLeadWeb' => 'Ø§ÛŒÙ† Ø­Ø³Ø§Ø¨ Ù‡Ù†ÙˆØ² Ù‡ÛŒÚ† Ø¨Ø±Ù†Ø§Ù…Ù‡â€ŒØ§ÛŒ Ø¯Ø± my.telegram.org Ù†Ø¯Ø§Ø±Ø¯. Ø§ÛŒÙ† Ø§Ø·Ù„Ø§Ø¹Ø§Øª Ù�Ù‚Ø· Ø¨Ù‡ Ø´Ù…Ø§ Ù†Ù…Ø§ÛŒØ´ Ø¯Ø§Ø¯Ù‡ Ù…ÛŒâ€ŒØ´ÙˆØ¯ Ùˆ Ø¨Ø¹Ø¯Ø§Ù‹ Ù‚Ø§Ø¨Ù„ ØªØºÛŒÛŒØ± Ø§Ø³Øª.',
             'apiCreateTitleWeb' => 'Ø¨Ø±Ù†Ø§Ù…Ù‡ Ø®ÙˆØ¯ Ø±Ø§ Ø¨Ø³Ø§Ø²ÛŒØ¯',
             'apiCreateVpsWarning' => 'Telegram Ø§Ø¬Ø§Ø²Ù‡ Ù†Ù…ÛŒâ€ŒØ¯Ù‡Ø¯ Ø§Ø² IPÙ‡Ø§ÛŒ VPSØŒ VPN Ùˆ Ø¯ÛŒÚ¯Ø± Ù…Ø­Ø¯ÙˆØ¯Ù‡â€ŒÙ‡Ø§ÛŒ IP Ø¯ÛŒØªØ§Ø³Ù†ØªØ±Ù‡Ø§ Ø¨Ø±Ù†Ø§Ù…Ù‡ Ø¬Ø¯ÛŒØ¯ Ø¯Ø± my.telegram.org Ø³Ø§Ø®ØªÙ‡ Ø´ÙˆØ¯ØŒ Ùˆ Ø§ÛŒÙ† Ø¯Ø±Ø®ÙˆØ§Ø³Øª Ø§Ø² IP Ø¯Ø³ØªÚ¯Ø§Ù‡ÛŒ Ø§Ø±Ø³Ø§Ù„ Ø´Ø¯ Ú©Ù‡ MadelineProto Ø±Ø§ Ø§Ø¬Ø±Ø§ Ù…ÛŒâ€ŒÚ©Ù†Ø¯. Ø¨Ø±Ù†Ø§Ù…Ù‡ Ø±Ø§ Ø¨Ù‡â€ŒØµÙˆØ±Øª Ø¯Ø³ØªÛŒ Ø¯Ø± https://my.telegram.org Ø¨Ø§ ÛŒÚ© Ø§ØªØµØ§Ù„ Ø®Ø§Ù†Ú¯ÛŒ (Ù…Ø«Ù„Ø§Ù‹ Ú¯ÙˆØ´ÛŒ Ø®ÙˆØ¯ Ø¨Ø§ Ø§ÛŒÙ†ØªØ±Ù†Øª Ù‡Ù…Ø±Ø§Ù‡) Ø¨Ø³Ø§Ø²ÛŒØ¯ØŒ Ø³Ù¾Ø³ API ID Ùˆ API Hash Ø±Ø§ ÙˆØ§Ø±Ø¯ Ú©Ù†ÛŒØ¯.',
             'apiCreateWeb' => 'Ø³Ø§Ø®Øª Ø¨Ø±Ù†Ø§Ù…Ù‡',
             'apiError' => 'Ø§Ø±ÙˆØ±: %s. Ø¯ÙˆØ¨Ø§Ø±Ù‡ Ø§Ù…ØªØ­Ø§Ù† Ú©Ù†ÛŒØ¯.',
             'apiInvalidWeb' => 'API ID ÛŒÚ© Ø¹Ø¯Ø¯ Ø§Ø³Øª Ùˆ API Hash Û³Û² Ú©Ø§Ø±Ø§Ú©ØªØ± Ø¯Ø§Ø±Ø¯: Ø¢Ù†â€ŒÙ‡Ø§ Ø±Ø§ Ø¯ÙˆØ¨Ø§Ø±Ù‡ Ø§Ø² my.telegram.org Ú©Ù¾ÛŒ Ú©Ù†ÛŒØ¯.',
             'apiManualInstructions0' => 'ÙˆØ§Ø±Ø¯ Ø­Ø³Ø§Ø¨ Ø®ÙˆØ¯ Ø¯Ø± https://my.telegram.org Ø´ÙˆÛŒØ¯',
             'apiManualInstructions1' => 'Ø¨Ù‡ Ø§Ø¨Ø²Ø§Ø±Ù‡Ø§ÛŒ ØªÙˆØ³Ø¹Ù‡ API Ø¨Ø±ÙˆÛŒØ¯',
             'apiManualInstructions2' => 'Ø±ÙˆÛŒ create application Ú©Ù„ÛŒÚ© Ú©Ù†ÛŒØ¯',
             'apiManualPrompt0' => 'API ID Ø®ÙˆØ¯ Ø±Ø§ ÙˆØ§Ø±Ø¯ Ú©Ù†ÛŒØ¯: ',
             'apiManualPrompt1' => 'API Hash Ø®ÙˆØ¯ Ø±Ø§ ÙˆØ§Ø±Ø¯ Ú©Ù†ÛŒØ¯: ',
             'apiManualWeb' => 'API ID Ùˆ API HASH Ø®ÙˆØ¯ Ø±Ø§ ÙˆØ§Ø±Ø¯ Ú©Ù†ÛŒØ¯',
             'apiParamsError' => 'Ø´Ù…Ø§ ØªÙ…Ø§Ù… ÙˆØ±ÙˆØ¯ÛŒâ€ŒÙ‡Ø§ÛŒ Ù…ÙˆØ±Ø¯Ù†ÛŒØ§Ø² Ø±Ø§ ÙˆØ§Ø±Ø¯ Ù†Ú©Ø±Ø¯ÛŒØ¯!',
             'apiSaveWeb' => 'Ø°Ø®ÛŒØ±Ù‡ Ùˆ Ø§Ø¯Ø§Ù…Ù‡',
             'apiSendCodeWeb' => 'Ø§Ø±Ø³Ø§Ù„ Ú©Ø¯',
             'apiSetupLeadWeb' => 'Ù‡Ø± Ø¨Ø±Ù†Ø§Ù…Ù‡ Telegram Ø¨Ù‡ ÛŒÚ© API ID Ùˆ API Hash Ù†ÛŒØ§Ø² Ø¯Ø§Ø±Ø¯. MadelineProto Ù…ÛŒâ€ŒØªÙˆØ§Ù†Ø¯ Ø¢Ù†â€ŒÙ‡Ø§ Ø±Ø§ Ø¨Ø§ Ø´Ù…Ø§Ø±Ù‡ ØªÙ„Ù�Ù† ÛŒÚ© Ø­Ø³Ø§Ø¨ Telegram Ù…ÙˆØ¬ÙˆØ¯ØŒ Ø§Ø² my.telegram.org Ø¨Ø±Ø§ÛŒØªØ§Ù† Ø¯Ø±ÛŒØ§Ù�Øª Ú©Ù†Ø¯.',
             'apiSetupTitleWeb' => 'API ID Ùˆ API Hash Ø®ÙˆØ¯ Ø±Ø§ Ø¯Ø±ÛŒØ§Ù�Øª Ú©Ù†ÛŒØ¯',
             'apiStartOverWeb' => 'Ø´Ø±ÙˆØ¹ Ø¯ÙˆØ¨Ø§Ø±Ù‡',
             'api_not_set' => 'Ø´Ù…Ø§ Ø¨Ø§ÛŒØ¯ ÛŒÚ© api key Ùˆ ÛŒÚ© api id ÙˆØ§Ø±Ø¯ Ú©Ù†ÛŒØ¯ØŒ @ Ø®ÙˆØªØ§Ù† Ø±Ø§ Ø§Ø² my.telegram.org Ø¨Ú¯ÛŒØ±ÛŒØ¯',
             'array_invalid' => 'Ø´Ù…Ø§ ÛŒÚ© Ø¢Ø±Ø§ÛŒÙ‡ Ù…Ø¹ØªØ¨Ø± ÙˆØ§Ø±Ø¯ Ù†Ú©Ø±Ø¯ÛŒØ¯',
             'baseDirLimitation' => 'ÛŒÚ© Ù…Ø­Ø¯ÙˆØ¯ÛŒØª basedir Ù¾ÛŒÚ©Ø±Ø¨Ù†Ø¯ÛŒ Ø´Ø¯Ù‡ Ø§Ø³Øª: Ø§ÛŒÙ† Ù…ÛŒØªÙˆØ§Ù†Ø¯ Ø±ÙˆÛŒ Ù¾Ø±Ù�ÙˆØ±Ù…Ù†Ø³ ØªØ§Ø«ÛŒØ± Ø¨Ú¯Ø°Ø§Ø±Ø¯ Ùˆ Ø¨Ø§Ø¹Ø« Ø§ÛŒØ¬Ø§Ø¯ Ù…Ø´Ú©Ù„Ø§Øª Ø´ÙˆØ¯ØŒ Ù„Ø·Ù�Ø§ Ø¯Ø± ØµÙˆØ±Øª Ø§Ù…Ú©Ø§Ù† Ø¢Ù† Ø±Ø§ ØºÛŒØ±Ù�Ø¹Ø§Ù„ Ú©Ù†ÛŒØ¯!',
             'bool_error' => 'Ù†Ù…ÛŒâ€ŒØªÙˆØ§Ù†â€Œ boolean Ø±Ø§ Ø§Ø³ØªØ®Ø±Ø§Ø¬ Ú©Ø±Ø¯',
             'botAlreadyRunning' => 'Ø±Ø¨Ø§Øª Ù‡Ù… Ø§Ú©Ù†ÙˆÙ† Ø¯Ø± Ø­Ø§Ù„ Ø§Ø¬Ø±Ø§ Ø§Ø³Øª!',
             'botapi_conversion_error' => 'Ù†Ù…ÛŒØªÙˆØ§Ù† %s Ø±Ø§ Ø¨Ù‡ Ø´ÛŒ Ø±Ø¨Ø§Øª API ØªØ¨Ø¯ÛŒÙ„ Ú©Ø±Ø¯',
             'call_already_accepted' => 'ØªÙ…Ø§Ø³ %s Ø§Ø² Ù‚Ø¨Ù„ Ù¾Ø°ÛŒØ±Ù�ØªÙ‡ Ø´Ø¯Ù‡',
             'call_already_declined' => 'ØªÙ…Ø§Ø³ %s Ø§Ø² Ù‚Ø¨Ù„ Ø±Ø¯ Ø´Ø¯Ù‡',
             'call_completing' => 'Ø¯Ø± Ø­Ø§Ù„ Ú©Ø§Ù…Ù„ Ú©Ø±Ø¯Ù† ØªÙ…Ø§Ø³ Ø§Ø² Ø·Ø±Ù� %s...',
             'call_confirming' => 'Ø¯Ø± Ø­Ø§Ù„ ØªØ§ÛŒÛŒØ¯ ØªÙ…Ø§Ø³ Ø§Ø² Ø·Ø±Ù� %s...',
             'call_discarding' => 'Ø¯Ø±Ø­Ø§Ù„ ØµØ±Ù� Ù†Ø¸Ø± Ú©Ø±Ø¯Ù† Ø§Ø² ØªÙ…Ø§Ø³ %s...',
             'call_error_1' => 'Ù†Ù…ÛŒâ€ŒØªÙˆØ§Ù† ØªÙ…Ø§Ø³ %s Ø±Ø§ Ù¾ÛŒØ¯Ø§ Ú©Ø±Ø¯ Ùˆ Ù¾Ø°ÛŒØ±Ù�Øª',
             'call_error_2' => 'Ù†Ù…ÛŒâ€ŒØªÙˆØ§Ù† ØªÙ…Ø§Ø³ %s Ø±Ø§ Ù¾ÛŒØ¯Ø§ Ùˆ ØªØ§ÛŒÛŒØ¯ Ú©Ø±Ø¯',
             'call_error_3' => 'Ù†Ù…ÛŒØªÙˆØ§Ù† ØªÙ…Ø§Ø³ %s Ø±Ø§ ÛŒØ§Ù�Øª Ùˆ Ú©Ø§Ù…Ù„ Ú©Ø±Ø¯',
             'cli_need_dl.php_link' => 'Ù„Ø·Ù�Ø§ Ù‡Ù†Ú¯Ø§Ù… Ø§Ø³ØªÙ�Ø§Ø¯Ù‡ Ø§Ø² getDownloadLink Ø±ÙˆÛŒ CLI (Ù…Ø­ÛŒØ· ØªØ±Ù…ÛŒÙ†Ø§Ù„)ØŒ Ù„ÛŒÙ†Ú© Ø§Ø³Ú©Ø±ÛŒÙ¾Øª Ø¯Ø§Ù†Ù„ÙˆØ¯ Ø±Ø§ Ù…Ø´Ø®Øµ Ú©Ù†ÛŒØ¯!',
             'constructor_not_found' => 'Ø³Ø§Ø²Ù†Ø¯Ù‡ Ø¨Ø±Ø§ÛŒ Ø§ÛŒÙ† Ù†ÙˆØ¹ Ù¾ÛŒØ¯Ø§ Ù†Ø´Ø¯: ',
             'could_not_connect_to_MadelineProto' => 'Ù†Ù…ÛŒ ØªÙˆØ§Ù† Ø¨Ù‡ Ù…Ø¯Ù„ÛŒÙ†â€ŒÙ¾Ø±ÙˆØªÙˆ Ù…ØªØµÙ„ Ø´Ø¯ØŒ Ù„Ø·Ù�Ø§Ù‹ proc_open Ø±Ø§ Ù�Ø¹Ø§Ù„ Ú©Ù†ÛŒØ¯ Ùˆ Ù…Ø­Ø¯ÙˆØ¯ÛŒØª open_basedir Ø±Ø§ Ø¨Ø±Ø¯Ø§Ø±ÛŒØ¯ØŒ ÛŒØ§ Ø¨Ø§Ø²Ù†ÙˆÛŒØ³ÛŒ Ù…Ø³ÛŒØ± ÙˆØ¨ Ø³Ø±ÙˆØ± Ø±Ø§ ØºÛŒØ±Ù�Ø¹Ø§Ù„ Ú©Ù†ÛŒØ¯ ØªØ§ Ù…Ø´Ú©Ù„ Ø±Ù�Ø¹ Ø´ÙˆØ¯! Ø§Ú¯Ø± Ø§ÛŒÙ† Ú©Ø§Ø± Ø±Ø§ Ú©Ø±Ø¯ÛŒØ¯ØŒ Ù…Ø·Ù…Ø¦Ù† Ø´ÙˆÛŒØ¯ Ú©Ù‡ Ù†Ø³Ø®Ù‡ PHP Ø¯Ø± Ú©Ø§Ù…Ù†Ø¯Ù„Ø§ÛŒÙ† Ùˆ ÙˆØ¨Ø³Ø±ÙˆØ± Ø¯Ù‚ÛŒÙ‚Ø§ ÛŒÚ©ÛŒ Ø§Ø³Øª (ÙˆØ±Ú˜Ù† ÛŒÚ©Ø³Ø§Ù†ØŒ Ø§Ù�Ø²ÙˆÙ†Ù‡â€ŒÙ‡Ø§ÛŒ ÛŒÚ©Ø³Ø§Ù† Ùˆ...) Ùˆ Ø¨Ø±Ø§ÛŒ Ú©Ø³Ø¨ Ø§Ø·Ù„Ø§Ø¹Ø§Øª Ø¨ÛŒØ´ØªØ± Ø±Ø§Ø¬Ø¨ Ø§Ø±ÙˆØ±ÛŒ Ú©Ù‡ Ù…Ø§Ù†Ø¹ Ø´Ø±ÙˆØ¹ IPC server Ù…ÛŒØ´ÙˆØ¯ØŒ Ù�Ø§ÛŒÙ„ MadelineProto.log Ø±Ø§ Ø¨Ø±Ø±Ø³ÛŒ Ú©Ù†ÛŒØ¯.',
             'could_not_convert_object' => 'Ù†Ù…ÛŒØªÙˆØ§Ù† Ù…Ù‚Ø¯Ø§Ø± Ø¢Ø¨Ø¬Ú©Øª ØªØ§ÛŒÙ¾ %s Ø±Ø§ ØªØ¨Ø¯ÛŒÙ„ Ú©Ø±Ø¯',
             'deserialization_error' => 'Ù…Ø´Ú©Ù„ÛŒ Ø¯Ø± Ø³Ø±ÛŒØ§Ù„â€ŒØ²Ø¯Ø§ÛŒÛŒ Ù¾ÛŒØ´ Ø¢Ù…Ø¯',
             'dl.php_check_logs_make_sure_session_running' => 'Ù‡Ù… Ø±Ø¨Ø§Øª Ø§ÛŒÙˆÙ†Øªâ€ŒÙ‡Ù†Ø¯Ù„Ø± Ù…Ø¯Ù„ÛŒÙ†â€ŒÙ¾Ø±ÙˆØªÙˆ Ù…Ø±ØªØ¨Ø· Ùˆ Ù‡Ù… Ø³Ø±ÙˆØ± IPC Ù…Ø¯Ù„ÛŒÙ†â€ŒÙ¾Ø±ÙˆØªÙˆ Ø¢Ù�Ù„Ø§ÛŒÙ† Ù‡Ø³ØªÙ†Ø¯ØŒ Ù„Ø·Ù�Ø§ Ù„Ø§Ú¯â€ŒÙ‡Ø§ Ø±Ø§ Ø¨Ø±Ø±Ø³ÛŒ Ú©Ù†ÛŒØ¯ Ùˆ Ù…Ø·Ù…Ø¦Ù† Ø´ÙˆÛŒØ¯ Ø­Ø¯Ø§Ù‚Ù„ ÛŒÚ©ÛŒ Ø§Ø² Ø¢Ù†Ù‡Ø§ Ø¯Ø± Ø­Ø§Ù„ Ø§Ø¬Ø±Ø§Ø³Øª!',
             'dl.php_powered_by_madelineproto' => 'Ø³Ø±ÙˆØ± Ø¯Ø§Ù†Ù„ÙˆØ¯ Ù�Ø§ÛŒÙ„ ØªÙ„Ú¯Ø±Ø§Ù… (ØªØ§ Ø­Ø¯Ø§Ú©Ø«Ø± 4 Ú¯ÛŒÚ¯Ø§Ø¨Ø§ÛŒØª)ØŒ Ù‚Ø¯Ø±Øª Ú¯Ø±Ù�ØªÙ‡ Ø§Ø² <a href="https://docs.madelineproto.xyz" target="_blank">Ù…Ø¯Ù„ÛŒÙ†â€ŒÙ¾Ø±ÙˆØªÙˆ</a>!<br>Ø¨Ø±Ø§ÛŒ Ø§Ø·Ù„Ø§Ø¹Ø§Øª Ø¨ÛŒØ´ØªØ± Ø¯Ø± Ù…ÙˆØ±Ø¯ Ø·Ø±ÛŒÙ‚Ù‡ Ø§Ø¬Ø±Ø§ÛŒ Ø³Ø±ÙˆØ± Ø¯Ø§Ù†Ù„ÙˆØ¯ ØªÙ„Ú¯Ø±Ø§Ù… Ø´Ø®ØµÛŒ Ø®ÙˆØ¯ <a href="https://docs.madelineproto.xyz/docs/FILES.html#getting-a-download-link" target="_blank">Ø§ÛŒÙ†Ø¬Ø§</a> Ú©Ù„ÛŒÚ© Ú©Ù†ÛŒØ¯!',
             'do_not_delete_MadelineProto.log' => 'Ù�Ø§ÛŒÙ„ MadelineProto.log Ù‡ÛŒÚ†ÙˆÙ‚Øª Ù†Ø¨Ø§ÛŒØ¯ Ø­Ø°Ù� Ø´ÙˆØ¯ØŒ Ù„Ø·Ù�Ø§ Ø¨Ù‡ Ø¬Ø§ÛŒ Ø­Ø°Ù�ØŒ Ø¨ÛŒØ´ØªØ±ÛŒÙ† Ø§Ù†Ø¯Ø§Ø²Ù‡ Ø¯Ù„Ø®ÙˆØ§Ù‡ Ø±Ø§ Ø¯Ø± ØªÙ†Ø¸ÛŒÙ…Ø§Øª Ù…Ø´Ø®Øµ Ú©Ù†ÛŒØ¯!',
             'do_not_remove_MadelineProto.log_phar' => 'Ù„Ø·Ù�Ø§ madeline.phar ÛŒØ§ madeline.php Ø±Ø§ Ø­Ø°Ù� Ù†Ú©Ù†ÛŒØ¯ØŒ Ø¯Ø± ØºÛŒØ±Ø§ÛŒÙ†ØµÙˆØ±Øª Ù…Ø¯Ù„ÛŒÙ†â€ŒÙ¾Ø±ÙˆØªÙˆ crash Ø®ÙˆØ§Ù‡Ø¯ Ú©Ø±Ø¯. Ø¯Ø± ØµÙˆØ±Øª Ø¯Ø§Ø´ØªÙ† Ù‡Ø±Ú¯ÙˆÙ†Ù‡ Ù…Ø´Ú©Ù„ Ø¨Ø§ Ù…Ø¯Ù„ÛŒÙ†â€ŒÙ¾Ø±ÙˆØªÙˆØŒ Ø¢Ù† Ø±Ø§ Ø¨Ù‡ https://github.com/danog/MadelineProto ÛŒØ§ https://t.me/pwrtelegramgroup Ú¯Ø²Ø§Ø±Ø´ Ú©Ù†ÛŒØ¯',
             'do_not_use_blocking_class' => 'Ø¨Ù‡ Ø¯Ù„Ø§ÛŒÙ„ Ø¹Ù…Ù„Ú©Ø±Ø¯ÛŒ Ùˆ Ø§Ø¬Ø±Ø§ÛŒÛŒØŒ Ù‡Ù†Ø¯Ù„Ø±Ù‡Ø§ Ù†Ø¨Ø§ÛŒØ¯ Ø§Ø² Ú©Ù„Ø³ %s Ú©Ù‡ async Ù†ÛŒØ³Øª Ø§Ø³ØªÙ�Ø§Ø¯Ù‡ Ú©Ù†Ù†Ø¯ØŒ Ø¨Ù‡ Ø¬Ø§ÛŒ Ø¢Ù† Ø§Ø² %s Ø§Ø³ØªÙ�Ø§Ø¯Ù‡ Ú©Ù†ÛŒØ¯',
             'do_not_use_blocking_function' => 'Ø¨Ù‡ Ø¯Ù„Ø§ÛŒÙ„ Ø¹Ù…Ù„Ú©Ø±Ø¯ÛŒ Ùˆ Ø§Ø¬Ø±Ø§ÛŒÛŒØŒ Ø§ÛŒÙˆÙ†Øªâ€ŒÙ‡Ù†Ø¯Ù„Ø±Ù‡Ø§ Ù†Ø¨Ø§ÛŒØ¯ Ø§Ø² Ù�Ø§Ù†Ú©Ø´Ù† %s Ú©Ù‡ async Ù†ÛŒØ³Øª Ø§Ø³ØªÙ�Ø§Ø¯Ù‡ Ú©Ù†Ù†Ø¯ØŒ Ù„Ø·Ù�Ø§ Ø¨Ù‡ Ø¬Ø§ÛŒ Ø¢Ù† Ø§Ø² %s Ø§Ø³ØªÙ�Ø§Ø¯Ù‡ Ú©Ù†ÛŒØ¯',
             'do_not_use_deprecated_function' => 'ØªØ§Ø¨Ø¹ %s Ù…Ù†Ø³ÙˆØ® Ø´Ø¯Ù‡ØŒ Ù„Ø·Ù�Ø§ Ø¨Ù‡ Ø¬Ø§ÛŒ Ø¢Ù† Ø§Ø² %s Ø§Ø³ØªÙ�Ø§Ø¯Ù‡ Ú©Ù†ÛŒØ¯',
             'do_not_use_non_root_require_in_event_handler' => 'Ø¨Ù‡ Ø¯Ù„Ø§ÛŒÙ„ Ø¹Ù…Ù„Ú©Ø±Ø¯ÛŒ Ùˆ Ø§Ø¬Ø±Ø§ÛŒÛŒØŒ Ø´Ù…Ø§ Ù†Ø¨Ø§ÛŒØ¯ Ø§Ø² require ÛŒØ§ include Ø¯Ø§Ø®Ù„ Ú©Ù„Ø³ Ø§ÛŒÙˆÙ†Øªâ€ŒÙ‡Ù†Ø¯Ù„Ø± Ø§Ø³ØªÙ�Ø§Ø¯Ù‡ Ú©Ù†ÛŒØ¯ØŒ Ù�Ù‚Ø· require Ù‡Ø§ÛŒ Ø³Ø·Ø­ root Ù…Ø¬Ø§Ø² Ù‡Ø³ØªÙ†Ø¯.',
             'do_not_use_yield' => 'Ù…Ø¯Ù„ÛŒÙ†â€ŒÙ¾Ø±ÙˆØªÙˆ Ù†Ø³Ø®Ù‡ 8 Ù†ÛŒØ§Ø²ÛŒ Ø¨Ù‡ yield Ø¯Ø± ØªÙˆØ§Ø¨Ø¹ async Ù†Ø¯Ø§Ø±Ø¯ Ùˆ Ø­ØªÛŒ Ø¢Ù† Ø±Ø§ Ù¾Ø´ØªÛŒØ¨Ø§Ù†ÛŒ Ù‡Ù… Ù†Ù…ÛŒÚ©Ù†Ø¯ØŒ Ø´Ù…Ø§ Ø¨Ø§ÛŒØ¯ ØªÙ…Ø§Ù… Ú©Ù„Ù…Ø§Øª Ú©Ù„ÛŒØ¯ÛŒ yield Ø±Ø§ Ú©Ù‡ Ù‚Ø¨Ù„Ø§ Ø¯Ø± Ø§Ø¬Ø±Ø§ÛŒ ØªÙˆØ§Ø¨Ø¹ async Ø¨Ù‡ Ú©Ø§Ø± Ù…ÛŒØ±Ù�ØªÙ†Ø¯ Ø­Ø°Ù� Ú©Ù†ÛŒØ¯',
             'done' => 'Ø§Ù†Ø¬Ø§Ù… Ø´Ø¯!',
             'encode_double_error' => 'Ù†Ù…ÛŒâ€ŒØªÙˆØ§Ù† Ø¨Ù‡ Ø¯Ø±Ø³ØªÛŒ double Ø±Ø§ Ø±Ù…Ø²Ù†Ú¯Ø§Ø±ÛŒ (Ø§Ù†Ú©Ø¯) Ú©Ø±Ø¯',
             'extensionRecommended' => 'Ù‡Ø´Ø¯Ø§Ø±: Ø§Ù�Ø²ÙˆÙ†Ù‡ %s Ù†ØµØ¨ Ù†Ø´Ø¯Ù‡ Ø§Ø³Øª. Ù„Ø·Ù�Ø§ Ø¨Ø±Ø§ÛŒ Ø§Ù�Ø²Ø§ÛŒØ´ Ø³Ø±Ø¹Øª Ù…Ø¯Ù„ÛŒÙ†â€ŒÙ¾Ø±ÙˆØªÙˆ Ø¢Ù† Ø±Ø§ Ù†ØµØ¨ Ú©Ù†ÛŒØ¯!',
             'extensionRequired' => 'Ù…Ø¯Ù„ÛŒÙ† Ø¨Ø±Ø§ÛŒ Ø§Ø¬Ø±Ø§ Ù†ÛŒØ§Ø² Ø¨Ù‡ Ø§Ù�Ø²ÙˆÙ†Ù‡ %s Ø¯Ø§Ø±Ø¯. %s',
             'extensionRequiredInstallWithApt' => 'Ø§Ø¬Ø±Ø§ÛŒ Ú©Ø§Ù…Ù†Ø¯ sudo apt-get install %s Ø±Ø§ Ø§Ù…ØªØ­Ø§Ù† Ú©Ù†ÛŒØ¯.',
             'extensionRequiredInstallWithCustomInstructions' => 'Ø¨Ø±Ø§ÛŒ Ù†ØµØ¨ Ø¢Ù†ØŒ Ø¯Ø³ØªÙˆØ±Ø§Ù„Ø¹Ù…Ù„ Ù‡Ø§ Ø±Ø§ Ø¯Ø± %s Ø¯Ù†Ø¨Ø§Ù„ Ú©Ù†ÛŒØ¯.',
             'file_not_exist' => 'Ù�Ø§ÛŒÙ„ ÙˆØ¬ÙˆØ¯ Ù†Ø¯Ø§Ø±Ø¯',
             'file_parsing' => 'Ø¯Ø±Ø­Ø§Ù„ ØªØ¬Ø²ÛŒÙ‡ %s...',
             'file_type_invalid' => 'Ù†ÙˆØ¹ Ù�Ø§ÛŒÙ„ Ù†Ø§Ù…Ø¹ØªØ¨Ø± Ø´Ù†Ø§Ø³Ø§ÛŒÛŒ Ø´Ø¯ (%s)',
             'fingerprint_invalid' => 'Ø§Ø«Ø±Ø§Ù†Ú¯Ø´Øª Ú©Ù„ÛŒØ¯ Ù†Ø§Ù…Ø¹ØªØ¨Ø± Ø§Ø³Øª!',
             'go' => 'Ø¨Ø±Ùˆ',
             'invalid_dl.php_session' => '%s ÛŒÚ© Ø§Ø³Ú©Ø±ÛŒÙ¾Øª Ø¯Ø§Ù†Ù„ÙˆØ¯ Ø¯Ø±Ø³Øª Ùˆ Ù…Ø¹ØªØ¨Ø± Ù†ÛŒØ³Øª Ú†Ø±Ø§ Ú©Ù‡ Ø³Ø´Ù†â€ŒØ¢ÛŒØ¯ÛŒ Ù…ØªÙ�Ø§ÙˆØª Ø§Ø³Øª (Ø§Ù†ØªØ¸Ø§Ø± Ø§ÛŒÙ† Ø³Ø´Ù†â€ŒØ¢ÛŒØ¯ÛŒ ÙˆØ¬ÙˆØ¯ Ø¯Ø§Ø´Øª: %sØŒ Ø§Ù…Ø§ Ø§ÛŒÙ† Ø¯Ø±ÛŒØ§Ù�Øª Ø´Ø¯: %s)',
             'ipc_tcp_warning' => 'UNIX sockets are not available, so the IPC server of the session listens on TCP (127.0.0.1) instead: any program running on this machine can connect to it, and control the session. On Windows 10 1803+, enable the sockets extension (extension=sockets in php.ini) to use UNIX sockets.',
             'length_too_big' => 'Ø·ÙˆÙ„ Ø¨Ø³ÛŒØ§Ø± Ø¨Ø²Ø±Ú¯ Ø§Ø³Øª',
             'loginBot' => 'ØªÙˆÚ©Ù† Ø±Ø¨Ø§Øªâ€ŒØªØ§Ù† Ø±Ø§ ÙˆØ§Ø±Ø¯ Ú©Ù†ÛŒØ¯: ',
             'loginBotTokenHintWeb' => 'ØªÙˆÚ©Ù†ÛŒ Ú©Ù‡ @BotFather Ù‡Ù†Ú¯Ø§Ù… Ø³Ø§Ø®Øª Ø±Ø¨Ø§Øª Ø¨Ù‡ Ø´Ù…Ø§ Ø¯Ø§Ø¯.',
             'loginBotTokenWeb' => 'ØªÙˆÚ©Ù† Ø±Ø¨Ø§Øª',
             'loginChoosePromptWeb' => 'Ù…ÛŒØ®ÙˆØ§Ù‡ÛŒØ¯ Ø¨Ù‡ Ø¹Ù†ÙˆØ§Ù† ÛŒÚ© Ú©Ø§Ø±Ø¨Ø± ÛŒØ§ Ø¨Ù‡ Ø¹Ù†ÙˆØ§Ù† ÛŒÚ© Ø±Ø¨Ø§ØªØŒ ÙˆØ§Ø±Ø¯ Ø´ÙˆÛŒØ¯ØŸ',
             'loginCodeLeadWeb' => 'Telegram ÛŒÚ© Ú©Ø¯ ÙˆØ±ÙˆØ¯ Ø¨Ù‡ Ø¨Ø±Ù†Ø§Ù…Ù‡ Telegram Ø´Ù…Ø§ Ù�Ø±Ø³ØªØ§Ø¯ØŒ ÛŒØ§ Ø§Ú¯Ø± Ø¬Ø§ÛŒ Ø¯ÛŒÚ¯Ø±ÛŒ ÙˆØ§Ø±Ø¯ Ù†Ø´Ø¯Ù‡ Ø¨Ø§Ø´ÛŒØ¯ØŒ Ø¨Ø§ Ù¾ÛŒØ§Ù…Ú©.',
             'loginCodeTitleWeb' => 'Ú©Ø¯ ÙˆØ±ÙˆØ¯ Ø±Ø§ ÙˆØ§Ø±Ø¯ Ú©Ù†ÛŒØ¯',
             'loginLeadWeb' => 'Ø­Ø³Ø§Ø¨ ÛŒØ§ Ø±Ø¨Ø§ØªÛŒ Ø±Ø§ Ø§Ù†ØªØ®Ø§Ø¨ Ú©Ù†ÛŒØ¯ Ú©Ù‡ Ø§ÛŒÙ† Ù†Ù…ÙˆÙ†Ù‡ MadelineProto Ø¨Ø§ Ø¢Ù† Ø§Ø¬Ø±Ø§ Ø®ÙˆØ§Ù‡Ø¯ Ø´Ø¯.',
             'loginManual' => 'Ù‡Ù…Ú†Ù†ÛŒÙ† Ø§Ø² Ø³ÙˆÛŒ Ø¯ÛŒÚ¯Ø±ØŒ Ù…ÛŒØªÙˆØ§Ù†ÛŒØ¯ ØªÙˆÚ©Ù† Ø±Ø¨Ø§Øª ÛŒØ§ Ø´Ù…Ø§Ø±Ù‡ ØªÙ„Ù�Ù† Ø®ÙˆØ¯ Ø±Ø§ Ø¨Ø±Ø§ÛŒ ÙˆØ±ÙˆØ¯ Ø®ÙˆØ¯Ú©Ø§Ø± ÙˆØ§Ø±Ø¯ Ú©Ù†ÛŒØ¯: ',
             'loginNoCode' => 'Ø´Ù…Ø§ Ú©Ø¯ Ø´Ù…Ø§Ø±Ù‡ Ø±Ø§ ÙˆØ§Ø±Ø¯ Ù†Ú©Ø±Ø¯ÛŒØ¯!',
             'loginNoName' => 'Ø´Ù…Ø§ Ù†Ø§Ù… Ø±Ø§ ÙˆØ§Ø±Ø¯ Ù†Ú©Ø±Ø¯ÛŒØ¯!',
             'loginNoPass' => 'Ø´Ù…Ø§ Ú©Ù„Ù…Ù‡ Ø¹Ø¨ÙˆØ± Ø±Ø§ ÙˆØ§Ø±Ø¯ Ù†Ú©Ø±Ø¯ÛŒØ¯!',
             'loginOptionBot' => 'Ø±Ø¨Ø§Øª',
             'loginOptionUser' => 'Ú©Ø§Ø±Ø¨Ø±',
             'loginPasswordLabelWeb' => 'Ø±Ù…Ø² Ø¹Ø¨ÙˆØ±',
             'loginPasswordLeadWeb' => 'Ø§ÛŒÙ† Ø­Ø³Ø§Ø¨ Ø¨Ø§ Ø±Ù…Ø² Ø¹Ø¨ÙˆØ±ÛŒ Ù…Ø­Ø§Ù�Ø¸Øª Ù…ÛŒâ€ŒØ´ÙˆØ¯ Ú©Ù‡ Ø¯Ø± ØªÙ†Ø¸ÛŒÙ…Ø§Øª > Ø­Ø±ÛŒÙ… Ø®ØµÙˆØµÛŒ Ùˆ Ø§Ù…Ù†ÛŒØª > ØªØ£ÛŒÛŒØ¯ Ø¯Ùˆ Ù…Ø±Ø­Ù„Ù‡â€ŒØ§ÛŒ ØªØ¹ÛŒÛŒÙ† Ø´Ø¯Ù‡ Ø§Ø³Øª.',
             'loginPasswordTitleWeb' => 'ØªØ£ÛŒÛŒØ¯ Ø¯Ùˆ Ù…Ø±Ø­Ù„Ù‡â€ŒØ§ÛŒ',
             'loginQr' => 'Ø¨Ø±Ø§ÛŒ ÙˆØ±ÙˆØ¯ Ø®ÙˆØ¯Ú©Ø§Ø± QR Code Ø¨Ø§Ù„Ø§ Ø±Ø§ Ø§Ø³Ú©Ù† Ú©Ù†ÛŒØ¯.',
             'loginQrCodeExpired' => 'QR Code Ù…Ù†Ù‚Ø¶ÛŒ Ø´Ø¯ØŒ Ø¯Ø± Ø­Ø§Ù„ Ø³Ø§Ø®Øª QR code Ø¬Ø¯ÛŒØ¯...',
             'loginQrCodeSuccessful' => 'ÙˆØ±ÙˆØ¯ Ø¨Ø§ QR code Ø¨Ø§ Ù…ÙˆÙ�Ù‚ÛŒØª Ø§Ù†Ø¬Ø§Ù… Ø´Ø¯!',
             'loginTitleWeb' => 'ÙˆØ±ÙˆØ¯ Ø¨Ù‡ Telegram',
             'loginTypeWeb' => 'ÙˆØ±ÙˆØ¯ Ø¨Ù‡ Ø¹Ù†ÙˆØ§Ù†',
             'loginUser' => 'Ø´Ù…Ø§Ø±Ù‡ ØªÙ„Ù�Ù†â€ŒØªØ§Ù† Ø±Ø§ ÙˆØ§Ø±Ø¯ Ú©Ù†ÛŒØ¯: ',
             'loginUserCode' => 'Ú©Ø¯ Ø±Ø§ ÙˆØ§Ø±Ø¯ Ú©Ù†ÛŒØ¯: ',
             'loginUserPass' => 'Ø±Ù…Ø² Ø®ÙˆØ¯ Ø±Ø§ ÙˆØ§Ø±Ø¯ Ú©Ù†ÛŒØ¯ (Ø§Ø´Ø§Ø±Ù‡ %s): ',
             'loginUserPassHint' => 'Ø§Ø´Ø§Ø±Ù‡: %s',
             'loginUserPassWeb' => 'Ø±Ù…Ø² Ø®ÙˆØ¯ Ø±Ø§ ÙˆØ§Ø±Ø¯ Ú©Ù†ÛŒØ¯: ',
             'loginUserPhoneCodeWeb' => 'Ú©Ø¯',
             'loginUserPhoneHintWeb' => 'Ù‡Ù…Ø±Ø§Ù‡ Ø¨Ø§ Ú©Ø¯ Ú©Ø´ÙˆØ±ØŒ Ù…Ø§Ù†Ù†Ø¯ +39 123 456 7890.',
             'loginUserPhoneWeb' => 'Ø´Ù…Ø§Ø±Ù‡ ØªÙ„Ù�Ù†',
             'loginWebQr' => 'Ù‡Ù…Ú†Ù†ÛŒÙ† Ù…ÛŒØªÙˆØ§Ù†ÛŒØ¯ Ø¨Ù‡ ØµÙˆØ±Øª Ø®ÙˆØ¯Ú©Ø§Ø± Ø¨Ø§ Ø§Ø³Ú©Ù† QR Code Ù¾Ø§ÛŒÛŒÙ† ÙˆØ§Ø±Ø¯ Ø´ÙˆÛŒØ¯:',
             'loginWebQr1' => 'ØªÙ„Ú¯Ø±Ø§Ù… Ø±Ø§ Ø¯Ø± Ù…ÙˆØ¨Ø§ÛŒÙ„ Ø®ÙˆØ¯ Ø¨Ø§Ø² Ú©Ù†ÛŒØ¯',
             'loginWebQr2' => 'Ø¨Ø±ÙˆÛŒØ¯ Ø¨Ù‡ ØªÙ†Ø¸ÛŒÙ…Ø§Øª > Ø¯Ø³ØªÚ¯Ø§Ù‡â€ŒÙ‡Ø§ > Ø§ØªØµØ§Ù„ Ø¯Ø³ØªÚ¯Ø§Ù‡ Ø¯Ø³Ú©ØªØ§Ù¾',
             'loginWebQr3' => 'Ú¯ÙˆØ´ÛŒ Ø®ÙˆØ¯ Ø±Ø§ Ø¨Ù‡ Ø³Ù…Øª Ø§ÛŒÙ† ØµÙ�Ø­Ù‡ Ø¨Ú¯ÛŒØ±ÛŒØ¯ ØªØ§ ÙˆØ±ÙˆØ¯ Ø±Ø§ ØªØ§ÛŒÛŒØ¯ Ú©Ù†ÛŒØ¯',
             'login_2fa_enabled' => 'Ø§Ø­Ø±Ø§Ø² Ù‡ÙˆÛŒØª Ø¯ÙˆØ¹Ø§Ù…Ù„ÛŒ Ù�Ø¹Ø§Ù„ Ø§Ø³ØªØŒ Ø´Ù…Ø§ Ø¨Ø§ÛŒØ¯ ØªØ§Ø¨Ø¹ complete2falogin Ø±Ø§ ØµØ¯Ø§ Ø¨Ø²Ù†ÛŒØ¯...',
             'login_auth_key' => 'Ø¯Ø±Ø­Ø§Ù„ ÙˆØ±ÙˆØ¯ Ø¨Ø§ Ø§Ø³ØªÙ�Ø§Ø¯Ù‡ Ø§Ø² Ú©Ù„ÛŒØ¯ Ù‡ÙˆÛŒØªÛŒ...',
             'login_bot' => 'Ø¯Ø± Ø­Ø§Ù„ ÙˆØ±ÙˆØ¯ Ø¨Ù‡ Ø¹Ù†ÙˆØ§Ù† ÛŒÚ© Ø±Ø¨Ø§Øª...',
             'login_code_sending' => 'Ø¯Ø± Ø­Ø§Ù„ Ø§Ø±Ø³Ø§Ù„ Ú©Ø¯...',
             'login_code_sent' => 'Ú©Ø¯ Ø¨Ø§ Ù…ÙˆÙ�Ù‚ÛŒØª Ø§Ø±Ø³Ø§Ù„ Ø´Ø¯! Ø²Ù…Ø§Ù†ÛŒ Ú©Ù‡ Ú©Ø¯ Ø±Ø§ Ø¯Ø±ÛŒØ§Ù�Øª Ú©Ø±Ø¯ÛŒØ¯ Ø¨Ø§ÛŒØ¯ Ø§Ø² ØªØ§Ø¨Ø¹ completePhoneLogin Ø§Ø³ØªÙ�Ø§Ø¯Ù‡ Ú©Ù†ÛŒØ¯.',
             'login_code_uncalled' => 'Ù…Ù† Ù…Ù†ØªØ¸Ø± Ú©Ø¯ Ù†ÛŒØ³ØªÙ…! Ù„Ø·Ù�Ø§ Ø§ÙˆÙ„ ØªØ§Ø¨Ø¹ phoneLogin Ø±Ø§ ØµØ¯Ø§ Ø¨Ø²Ù†ÛŒØ¯',
             'login_need_signup' => 'Ø­Ø³Ø§Ø¨ÛŒ Ø¨Ø±Ø§ÛŒ Ø§ÛŒÙ† Ø´Ù…Ø§Ø±Ù‡ Ø³Ø§Ø®ØªÙ‡ Ù†Ø´Ø¯Ù‡â€ŒØ§Ø³ØªØŒ Ø´Ù…Ø§ Ø¨Ø§ÛŒØ¯ ØªØ§Ø¨Ø¹ completeSignup Ø±Ø§ ØµØ¯Ø§ Ø¨Ø²Ù†ÛŒØ¯...',
             'login_ok' => 'Ø¨Ø§ Ù…ÙˆÙ�Ù‚ÛŒØª ÙˆØ§Ø±Ø¯ Ø´Ø¯!',
             'login_user' => 'Ø¯Ø± Ø­Ø§Ù„ ÙˆØ±ÙˆØ¯ Ø¨Ù‡ Ø¹Ù†ÙˆØ§Ù† ÛŒÚ© Ú©Ø§Ø±Ø¨Ø± Ø¹Ø§Ø¯ÛŒ...',
             'logout_ok' => 'Ø¨Ø§ Ù…ÙˆÙ�Ù‚ÛŒØª Ø®Ø§Ø±Ø¬ Ø´Ø¯!',
             'long_not_16' => 'Ù…Ù‚Ø¯Ø§Ø± ÙˆØ±ÙˆØ¯ÛŒ Ø¨Ù‡ Ø·ÙˆÙ„ 16 Ø¨Ø§ÛŒØª Ù†ÛŒØ³Øª',
             'long_not_32' => 'Ù…Ù‚Ø¯Ø§Ø± ÙˆØ±ÙˆØ¯ÛŒ Ø¨Ù‡ Ø·ÙˆÙ„ 32 Ø¨Ø§ÛŒØª Ù†ÛŒØ³Øª',
             'long_not_64' => 'Ù…Ù‚Ø¯Ø§Ø± ÙˆØ±ÙˆØ¯ÛŒ Ø¨Ù‡ Ø·ÙˆÙ„ 64 Ø¨Ø§ÛŒØª Ù†ÛŒØ³Øª',
             'madelineproto_ready' => 'MadelineProto Ø¢Ù…Ø§Ø¯Ù‡ Ø§Ø³Øª!',
             'manualAdminActionRequired' => '!!!!!!!!! Ù�Ø¹Ø§Ù„ÛŒØª Ø¯Ø³ØªÛŒ Ø§Ø¯Ù…ÛŒÙ† Ø³ÛŒØ³ØªÙ… Ù†ÛŒØ§Ø² Ø§Ø³Øª !!!!!!!!!',
             'method_not_found' => 'ØªØ§Ø¨Ø¹ Ù¾ÛŒØ¯Ø§ Ù†Ø´Ø¯: ',
             'mmapErrorPart1' => 'Ø¨ÛŒØ´ØªØ±ÛŒÙ† Ù…Ù‚Ø¯Ø§Ø± Ø­Ø§Ù�Ø¸Ù‡ ØªØ®ØµÛŒØµ Ø¯Ø§Ø¯Ù‡ Ø´Ø¯Ù‡ (mmap ) Ù¾Ø± Ø´Ø¯Ù‡ Ø§Ø³Øª (%s ) : Ù„Ø·Ù�Ø§ Ø§Ø² Ø¯Ø§Ø®Ù„ Ú©Ø§Ù†Ù�ÛŒÚ¯ Ù‡Ø§ÛŒ Ù…Ø±Ø¨ÙˆØ· Ø¨Ù‡ Ú©Ø±Ù†Ù„ Ù…Ù‚Ø¯Ø§Ø± vm.max_map_count Ø±Ø§ Ø¨Ù‡ Ù…Ù‚Ø¯Ø§Ø± 262144 Ø§Ù�Ø²Ø§ÛŒØ´ Ø¯Ù‡ÛŒØ¯ØªØ§ Ù…Ø´Ú©Ù„ Ø±Ù�Ø¹ Ú¯Ø±Ø¯Ø¯.',
             'mmapErrorPart2' => 'Ø¨Ø±Ø§ÛŒ Ø­Ù„ Ú©Ø±Ø¯Ù† Ù…Ø´Ú©Ù„ØŒ Ú©Ø§Ù…Ù†Ø¯ Ù…Ù‚Ø§Ø¨Ù„ Ø±Ø§ Ø¨Ø§ Ø¯Ø³ØªØ±Ø³ÛŒ root Ø§Ø¬Ø±Ø§ Ú©Ù†ÛŒØ¯: %s',
             'mmapErrorPart3' => 'Ø¨Ø±Ø§ÛŒ ØªØ¯Ø§ÙˆÙ… ØªØºÛŒÛŒØ± Ø¯Ø± Ø·ÙˆÙ„ Ø±Ø§Ù‡â€ŒØ§Ù†Ø¯Ø§Ø²ÛŒ Ø¯ÙˆØ¨Ø§Ø±Ù‡: %s',
             'mmapErrorPart4' => 'Ø±ÙˆÛŒ ÙˆÛŒÙ†Ø¯ÙˆØ² Ùˆ WSLØŒ Ø§Ù�Ø²Ø§ÛŒØ´ Ø³Ø§ÛŒØ² pagefile Ù…Ù…Ú©Ù† Ø§Ø³Øª Ú©Ù…Ú© Ú©Ù†Ø¯Ø› Ø§Ú¯Ø± Ù…Ø´Ú©Ù„ Ù¾Ø§Ù�Ø´Ø§Ø±ÛŒ Ú©Ø±Ø¯ Ù„Ø·Ù�Ø§ Ø³ÛŒØ³ØªÙ…â€ŒØ¹Ø§Ù…Ù„ Ø®ÙˆØ¯ Ø±Ø§ Ø¨Ù‡ Ù„ÛŒÙ†ÙˆÚ©Ø³ ØªØºÛŒÛŒØ± Ø¯Ù‡ÛŒØ¯.',
             'must_have_declare_types' => 'Ø¨Ù‡ Ø¯Ù„Ø§ÛŒÙ„ Ø¹Ù…Ù„Ú©Ø±Ø¯ÛŒ Ùˆ Ø§Ø¬Ø±Ø§ÛŒÛŒØŒ Ø§ÙˆÙ„ÛŒÙ† Ø¯Ø³ØªÙˆØ± Ù�Ø§ÛŒÙ„ Ø§ÛŒÙˆÙ†Øªâ€ŒÙ‡Ù†Ø¯Ù„Ø± Ø¨Ø§ÛŒØ¯ Ø¯Ø³ØªÙˆØ± Ù…Ù‚Ø§Ø¨Ù„ Ø¨Ø§Ø´Ø¯: "declare(strict_types=1);"',
             'nearest_dc' => 'Ù…Ø§ Ø¯Ø± %s Ù‡Ø³ØªÛŒÙ…ØŒ Ù†Ø²Ø¯ÛŒÚ© ØªØ±ÛŒÙ† Ø¯ÛŒØªØ§Ø³Ù†ØªØ± %d Ø§Ø³Øª.',
             'need_dl.php' => 'Ø§Ø³Ú©Ø±ÛŒÙ¾Øª Ø¯Ø§Ù†Ù„ÙˆØ¯ Ù¾ÛŒØ´Ù�Ø±Ø¶ ØªÙˆÙ„ÛŒØ¯ Ù†Ø´Ø¯ (%s)ØŒ Ù„Ø·Ù�Ø§ ÛŒÚ© Ù�Ø§ÛŒÙ„ dl.php Ø¨Ø³ÛŒØ§Ø²ÛŒØ¯ Ùˆ Ø§ÛŒÙ† Ù…Ø­ØªÙˆØ§ Ø±Ø§ Ø¯Ø± Ø¢Ù† Ù‚Ø±Ø§Ø± Ø¯Ù‡ÛŒØ¯: %s Ø³Ù¾Ø³ Ù„ÛŒÙ†Ú© Ø¢Ù† Ù�Ø§ÛŒÙ„ Ø±Ø§ Ø¨Ù‡ ÙˆØ±ÙˆØ¯ÛŒ Ø¯ÙˆÙ… getDownloadLink Ø¨Ø¯Ù‡ÛŒØ¯',
             'noReportPeers' => 'Ù‡Ø´Ø¯Ø§Ø±: Ù‡ÛŒÚ† peerÛŒ Ø¨Ù‡ Ø¹Ù†ÙˆØ§Ù† peer Ú¯Ø²Ø§Ø±Ø´Ø§Øª ØªÙ†Ø¸ÛŒÙ… Ù†Ø´Ø¯Ù‡ØŒ Ù„Ø·Ù�Ø§ Ù…ØªÙˆØ¯ Ø²ÛŒØ± Ø±Ø§ Ø¨Ù‡ Ø§ÛŒÙˆÙ†Øª Ù‡Ù†Ø¯Ù„Ø± Ø§Ø¶Ø§Ù�Ù‡ Ú©Ù†ÛŒØ¯',
             'non_text_conversion' => 'Ù�Ø¹Ù„Ø§ Ù†Ù…ÛŒâ€ŒØªÙˆØ§Ù† Ù¾ÛŒØ§Ù…â€ŒÙ‡Ø§ÛŒ ØºÛŒØ±Ù…ØªÙ†ÛŒ Ø±Ø§ ØªØ¨Ø¯ÛŒÙ„ Ú©Ø±Ø¯!',
             'not_loggedIn' => 'Ù…Ù† ÙˆØ§Ø±Ø¯ Ù†Ø´Ø¯Ù…!',
             'not_numeric' => 'Ù…Ù‚Ø¯Ø§Ø± ÙˆØ±ÙˆØ¯ÛŒ Ø¹Ø¯Ø¯ÛŒ Ù†ÛŒØ³Øª',
             'params_missing' => 'ÙˆØ±ÙˆØ¯ÛŒ Ù…ÙˆØ±Ø¯Ù†ÛŒØ§Ø² ÛŒØ§Ù�Øª Ù†Ø´Ø¯',
             'peer_not_in_db' => 'Ø§ÛŒÙ† peer Ø¯Ø± Ù¾Ø§ÛŒÚ¯Ø§Ù‡ Ø¯Ø§Ø¯Ù‡ (Ø¯ÛŒØªØ§Ø¨ÛŒØ³) peer ÙˆØ¬ÙˆØ¯ Ù†Ø¯Ø§Ø±Ø¯',
             'plugin_path_does_not_exist' => 'Ù…Ø³ÛŒØ± Ù¾Ù„Ø§Ú¯ÛŒÙ† %s ÙˆØ¬ÙˆØ¯ Ù†Ø¯Ø§Ø±Ø¯!',
             'plugins_do_not_use_require' => 'Ø¨Ù‡ Ø¯Ù„Ø§ÛŒÙ„ Ø¹Ù…Ù„Ú©Ø±Ø¯ÛŒ Ùˆ Ø§Ø¬Ø±Ø§ÛŒÛŒØŒ Ù¾Ù„Ø§Ú¯ÛŒÙ†â€ŒÙ‡Ø§ Ù�Ù‚Ø· Ù…ÛŒØªÙˆØ§Ù†Ù†Ø¯ Ù�Ø§ÛŒÙ„ Ù‡Ø§ÛŒ Ø¯ÛŒÚ¯Ø± Ø±Ø§ Ú©Ù‡ Ø¯Ø± Ù¾ÙˆØ´Ù‡ Ù¾Ù„Ø§Ú¯ÛŒÙ† Ù‡Ø§Ø³Øª Ø¨Ø§ Ø§Ø³ØªÙ�Ø§Ø¯Ù‡ Ø§Ø² Ø±Ø§Ù‡â€ŒØ§Ù†Ø¯Ø§Ø²ÛŒ Ø¨Ø§Ø±Ú¯Ø°Ø§Ø± Ø®ÙˆØ¯Ú©Ø§Ø± PSR-4ØŒ Ø¨Ù‡ ØµÙˆØ±Øª Ø®ÙˆØ¯Ú©Ø§Ø± include ÛŒØ§ require Ú©Ù†Ù†Ø¯ (Ø¨Ø¯ÙˆÙ† require Ú©Ø±Ø¯Ù† Ø¯Ø³ØªÛŒ Ø¢Ù†Ù‡Ø§).',
             'plugins_must_have_exactly_one_class' => 'ÛŒÚ© Ù¾Ù„Ø§Ú¯ÛŒÙ† Ø¨Ø§ÛŒØ¯ Ø¯Ù‚ÛŒÙ‚Ø§ ÛŒÚ© Ú©Ù„Ø³ Ø±Ø§ ØªØ¹Ø±ÛŒÙ� Ú©Ù†Ø¯! Ø¨Ø±Ø§ÛŒ ØªØ¹Ø±ÛŒÙ� Ú†Ù†Ø¯ Ú©Ù„Ø³ØŒ Ø§ÛŒÙ†ØªØ±Ù�ÛŒØ³ ÛŒØ§ ØªØ±ÛŒØªØŒ Ù�Ø§ÛŒÙ„â€ŒÙ‡Ø§ÛŒ Ø¬Ø¯Ø§Ú¯Ø§Ù†Ù‡ Ø¨Ø³Ø§Ø²ÛŒØ¯ØŒ Ø¢Ù†Ù‡Ø§ ØªÙˆØ³Ø· Ù…Ø¯Ù„ÛŒÙ†â€ŒÙ¾Ø±ÙˆØªÙˆ Ø¨Ù‡ ØµÙˆØ±Øª Ø®ÙˆØ¯Ú©Ø§Ø± Ø¨Ø§Ø±Ú¯Ø°Ø§Ø±ÛŒ Ø®ÙˆØ§Ù‡Ù†Ø¯ Ø´Ø¯.',
             'predicate_not_set' => 'Ù…Ø³ØªÙ†Ø¯ (Ù…Ù‚Ø¯Ø§Ø± ØªØ­Øª _) ØªÙ†Ø¸ÛŒÙ… Ù†Ø´Ø¯Ù‡!',
             'recommend_not_use_filesystem_function' => 'Ø§Ø³ØªÙ�Ø§Ø¯Ù‡ Ø§Ø² ØªØ§Ø¨Ø¹ %s Ù¾ÛŒØ´Ù†Ù‡Ø§Ø¯ Ù†Ù…ÛŒØ´ÙˆØ¯ØŒ Ú†Ø±Ø§Ú©Ù‡ Ø¯Ø³ØªØ±Ø³ÛŒ Ùˆ Ú©Ø§Ø± Ø¨Ø§ Ù�Ø§ÛŒÙ„ Ù‡Ø§ÛŒ Ø³ÛŒØ³ØªÙ…ÛŒ Ù…ÙˆÙ‚Ø¹ Ù¾Ø±Ø¯Ø§Ø²Ø´ Ø¢Ù¾Ø¯ÛŒØª Ø¨Ø§Ø¹Ø« Ú©Ù… Ø´Ø¯Ù† Ø³Ø±Ø¹Øª Ø±Ø¨Ø§Øª Ø´Ù…Ø§ Ù…ÛŒØ´ÙˆØ¯ØŒ Ù„Ø·Ù�Ø§ ØµÙ�Ø­Ù‡ https://docs.madelineproto.xyz/docs/UPDATES.html#avoiding-the-use-of-filesystem-functions Ø±Ø§ Ø¨Ø¨ÛŒÙ†ÛŒØ¯ ØªØ§ Ø¨Ø§ Ø±Ø§Ù‡â€ŒÙ‡Ø§ÛŒ Ø¬Ø§ÛŒÚ¯Ø²ÛŒÙ† Ø¨Ø±Ø§ÛŒ Ø°Ø®ÛŒØ±Ù‡ Ø§Ø·Ù„Ø§Ø¹Ø§Øª Ú©Ù‡ Ø¨Ø§Ø¹Ø« Ú©Ø§Ù‡Ø´ Ø³Ø±Ø¹Øª Ø±Ø¨Ø§Øª Ø´Ù…Ø§ Ù†Ù…ÛŒØ´ÙˆÙ†Ø¯ Ø¢Ø´Ù†Ø§ Ø´ÙˆÛŒØ¯!',
             'rpc_tg_error' => 'ØªÙ„Ú¯Ø±Ø§Ù… ÛŒÚ© Ø®Ø·Ø§ÛŒ RPC Ø¨Ø±Ú¯Ø±Ø¯Ø§Ù†Ø¯: %s (%s), Ù†Ø§Ø´ÛŒ Ø§Ø² %s:%s%sTL Ø±Ø¯:',
             'sec_peer_not_in_db' => 'Ø§ÛŒÙ† peer Ù…Ø®Ù�ÛŒ Ø¯Ø± Ù¾Ø§ÛŒÚ¯Ø§Ù‡ Ø¯Ø§Ø¯Ù‡ (Ø¯ÛŒØªØ§Ø¨ÛŒØ³) Ø¯Ø§Ø®Ù„ÛŒ peer ÙˆØ¬ÙˆØ¯ Ù†Ø¯Ø§Ø±Ø¯',
             'secret_chat_skipping' => 'Ù…Ù† Ú†Øª Ù…Ø®Ù�ÛŒ (Ø³Ú©Ø±Øª) %s Ø±Ø§ Ø¯Ø± Ù¾Ø§ÛŒÚ¯Ø§Ù‡ Ø¯Ø§Ø¯Ù‡ (Ø¯ÛŒØªØ§Ø¨ÛŒØ³) Ù†Ø¯Ø§Ø±Ù…ØŒ Ø¯Ø±Ø­Ø§Ù„ Ø±Ø¯ Ø´Ø¯Ù† Ø§Ø² Ù¾ÛŒØ§Ù…...',
             'serialization_ofd' => 'Ù…Ø±ØªØ¨ Ø³Ø§Ø²ÛŒ (Ø³Ø±ÛŒØ§Ù„Ø§ÛŒØ²) Ù…Ù†Ù‚Ø¶ÛŒ Ø´Ø¯Ù‡ØŒ Ø¯Ø±Ø­Ø§Ù„ Ø¨Ø§Ø²Ø³Ø§Ø²ÛŒ Ø´ÛŒ!',
             'session_corrupted' => 'Ø³Ø´Ù† Ø´Ù…Ø§ Ø®Ø±Ø§Ø¨ Ø§Ø³Øª!',
             'signing_up' => 'Ø¯Ø± Ø­Ø§Ù„ Ø«Ø¨Øªâ€ŒÙ†Ø§Ù… Ø¨Ù‡ Ø¹Ù†ÙˆØ§Ù† ÛŒÚ© Ú©Ø§Ø±Ø¨Ø± Ø¹Ø§Ø¯ÛŒ...',
             'signupFirstName' => 'Ù†Ø§Ù… Ø®ÙˆØ¯ Ø±Ø§ ÙˆØ§Ø±Ø¯ Ú©Ù†ÛŒØ¯: ',
             'signupFirstNameWeb' => 'Ù†Ø§Ù…',
             'signupLastName' => 'Ù†Ø§Ù… Ø®Ø§Ù†ÙˆØ§Ø¯Ú¯ÛŒ Ø®ÙˆØ¯ Ø±Ø§ ÙˆØ§Ø±Ø¯ Ú©Ù†ÛŒØ¯ (Ù…ÛŒâ€ŒØªÙˆØ§Ù†Ø¯ Ø®Ø§Ù„ÛŒ Ø¨Ø§Ø´Ø¯): ',
             'signupLastNameWeb' => 'Ù†Ø§Ù… Ø®Ø§Ù†ÙˆØ§Ø¯Ú¯ÛŒ',
             'signupLeadWeb' => 'Ø§ÛŒÙ† Ø´Ù…Ø§Ø±Ù‡ ØªÙ„Ù�Ù† Ù‡Ù†ÙˆØ² Ø¯Ø± Telegram Ø«Ø¨Øª Ù†Ø´Ø¯Ù‡ Ø§Ø³Øª: Ù†Ø§Ù… Ø­Ø³Ø§Ø¨ Ø¬Ø¯ÛŒØ¯ Ø±Ø§ Ø§Ù†ØªØ®Ø§Ø¨ Ú©Ù†ÛŒØ¯.',
             'signupTitleWeb' => 'Ø­Ø³Ø§Ø¨ Ø®ÙˆØ¯ Ø±Ø§ Ø¨Ø³Ø§Ø²ÛŒØ¯',
             'signupWeb' => 'Ù„Ø·Ù�Ø§ Ø«Ø¨Øªâ€ŒÙ†Ø§Ù… Ú©Ù†ÛŒØ¯',
             'signup_ok' => 'Ø¨Ø§ Ù…ÙˆÙ�Ù‚ÛŒØª Ø«Ø¨Øªâ€ŒÙ†Ø§Ù… Ø´Ø¯!',
             'signup_uncalled' => 'Ù…Ù† Ù…Ù†ØªØ¸Ø± Ø³Ø§Ø®Øª Ø­Ø³Ø§Ø¨ Ù†ÛŒØ³ØªÙ…! Ù„Ø·Ù�Ø§ Ø§ÙˆÙ„ ØªÙˆØ§Ø¨Ø¹ phoneLogin Ùˆ completePhoneLogin Ø±Ø§ ØµØ¯Ø§ Ø¨Ø²Ù†ÛŒØ¯!',
             'src_file_invalid' => 'Ù…Ù†Ø¨Ø¹ Ù�Ø§ÛŒÙ„ Ù†Ø§Ù…Ø¹ØªØ¨Ø±ÛŒ Ù„Ø­Ø§Ø¸ Ú¯Ø±Ø¯ÛŒØ¯Ù‡: ',
             'static_analysis_minor' => 'ÛŒÚ© Ù…Ø´Ú©Ù„ Ø¬Ø²Ø¦ÛŒ Ø¯Ø± Ø·ÙˆÙ„ Ø¨Ø±Ø±Ø³ÛŒ Ø§Ø³ØªØ§ØªÛŒÚ© %s Ø±Ø® Ø¯Ø§Ø¯: %s',
             'static_analysis_severe' => 'ÛŒÚ© Ù…Ø´Ú©Ù„ Ø³Ù…Øª Ø³Ø±ÙˆØ± Ø¯Ø± Ø·ÙˆÙ„ Ø¨Ø±Ø±Ø³ÛŒ Ø§Ø³ØªØ§ØªÛŒÚ© %s Ø±Ø® Ø¯Ø§Ø¯: %s',
             'stream_handle_invalid' => 'ÛŒÚ© Ú©Ù†ØªØ±Ù„â€ŒÚ©Ù†Ù†Ø¯Ù‡ Ø¬Ø±ÛŒØ§Ù† (Ø§Ø³ØªØ±ÛŒÙ… Ù‡Ù†Ø¯Ù„) Ù†Ø§Ù…Ø¹ØªØ¨Ø± Ù„Ø­Ø§Ø¸ Ú¯Ø±Ø¯ÛŒØ¯Ù‡.',
             'string_required' => 'Ù…Ù‚Ø¯Ø§Ø± Ù…ÙˆØ±Ø¯ Ø§Ù†ØªØ¸Ø§Ø± Ø±Ø´ØªÙ‡ Ø§Ø³Øª!',
             'translate_madelineproto_cli' => 'Ù…Ø¯Ù„ÛŒÙ†â€ŒÙ¾Ø±ÙˆØªÙˆ Ø±Ø§ Ù…ÛŒâ€ŒØªÙˆØ§Ù† Ø¨Ù‡ Ø²Ø¨Ø§Ù† Ø´Ù…Ø§ ØªØ±Ø¬Ù…Ù‡ Ú©Ø±Ø¯ (Ù¾ÛŒØ´Ø±Ù�Øª ØªØ±Ø¬Ù…Ù‡ Ù�Ø¹Ù„ÛŒ: %d%%)ØŒ Ø¨Ø±Ø§ÛŒ Ù…Ø´Ø§Ø±Ú©Øª Ø¯Ø± ØªØ±Ø¬Ù…Ù‡ Ø¨Ù‡ https://github.com/danog/MadelineProto/tree/v8/langs Ø¨Ø±ÙˆÛŒØ¯!',
             'translate_madelineproto_web' => 'Ù…Ø¯Ù„ÛŒÙ†â€ŒÙ¾Ø±ÙˆØªÙˆ Ø±Ø§ Ù…ÛŒâ€ŒØªÙˆØ§Ù† Ø¨Ù‡ Ø²Ø¨Ø§Ù† Ø´Ù…Ø§ ØªØ±Ø¬Ù…Ù‡ Ú©Ø±Ø¯ (Ù¾ÛŒØ´Ø±Ù�Øª ØªØ±Ø¬Ù…Ù‡ Ù�Ø¹Ù„ÛŒ: %d%%)ØŒ Ø¨Ø±Ø§ÛŒ Ù…Ø´Ø§Ø±Ú©Øª Ø¯Ø± ØªØ±Ø¬Ù…Ù‡ <a href="https://github.com/danog/MadelineProto/tree/v8/langs" target="_blank">Ø§ÛŒÙ†Ø¬Ø§ Ø±Ø§ Ú©Ù„ÛŒÚ© Ú©Ù†ÛŒØ¯!</a >',
             'tweb.ACCESS_TOKEN_EXPIRED' => 'Ø§ÛŒÙ† ØªÙˆÚ©Ù† Ø±Ø¨Ø§Øª Ø¨Ø§Ø·Ù„ Ø´Ø¯Ù‡ Ø§Ø³Øª',
             'tweb.ACCESS_TOKEN_INVALID' => 'ØªÙˆÚ©Ù† Ø±Ø¨Ø§Øª Ù†Ø§Ù…Ø¹ØªØ¨Ø± Ø§Ø³Øª',
             'tweb.AccDescr.LoginCode' => 'Ú©Ø¯ ÙˆØ±ÙˆØ¯',
             'tweb.Close' => 'Ø¨Ø³ØªÙ†',
             'tweb.Code' => 'Ú©Ø¯',
             'tweb.Contacts.PhoneNumber.Placeholder' => 'Ø´Ù…Ø§Ø±Ù‡ ØªÙ„Ù�Ù†',
             'tweb.Country' => 'Ú©Ø´ÙˆØ±',
             'tweb.DarkMode' => 'Ø­Ø§Ù„Øª ØªØ§Ø±ÛŒÚ©',
             'tweb.Error.AnError' => 'Ø®Ø·Ø§ÛŒÛŒ Ø±Ø® Ø¯Ø§Ø¯. Ù„Ø·Ù�Ø§Ù‹ Ø¨Ø¹Ø¯Ø§Ù‹ Ø¯ÙˆØ¨Ø§Ø±Ù‡ Ø§Ù…ØªØ­Ø§Ù† Ú©Ù†ÛŒØ¯.',
             'tweb.FIRSTNAME_INVALID' => 'Ù†Ø§Ù… Ù†Ø§Ù…Ø¹ØªØ¨Ø± Ø§Ø³Øª',
             'tweb.FirstName' => 'Ù†Ø§Ù… (Ø§Ù„Ø²Ø§Ù…ÛŒ)',
             'tweb.LASTNAME_INVALID' => 'Ù†Ø§Ù… Ø®Ø§Ù†ÙˆØ§Ø¯Ú¯ÛŒ Ù†Ø§Ù…Ø¹ØªØ¨Ø± Ø§Ø³Øª',
             'tweb.LastName' => 'Ù†Ø§Ù… Ø®Ø§Ù†ÙˆØ§Ø¯Ú¯ÛŒ (Ø§Ø®ØªÛŒØ§Ø±ÛŒ)',
             'tweb.Login.Bot' => 'ÙˆØ±ÙˆØ¯ Ø¨Ù‡ Ø¹Ù†ÙˆØ§Ù† Ø±Ø¨Ø§Øª >',
             'tweb.Login.Bot.Cancel' => 'ÙˆØ±ÙˆØ¯ Ø¨Ù‡ Ø¹Ù†ÙˆØ§Ù† Ú©Ø§Ø±Ø¨Ø± >',
             'tweb.Login.Bot.Subtitle' => 'ØªÙˆÚ©Ù†ÛŒ Ø±Ø§ Ú©Ù‡ @BotFather Ù‡Ù†Ú¯Ø§Ù… Ø³Ø§Ø®Øª Ø±Ø¨Ø§Øª
Ø¨Ù‡ Ø´Ù…Ø§ Ø¯Ø§Ø¯ ÙˆØ§Ø±Ø¯ Ú©Ù†ÛŒØ¯.',
             'tweb.Login.Bot.Title' => 'ÙˆØ±ÙˆØ¯ Ø¨Ù‡ Ø¹Ù†ÙˆØ§Ù† Ø±Ø¨Ø§Øª',
             'tweb.Login.Bot.Token' => 'ØªÙˆÚ©Ù† Ø±Ø¨Ø§Øª',
             'tweb.Login.Code.SecretPhrase' => 'Ø¹Ø¨Ø§Ø±Øª Ù…Ø®Ù�ÛŒ',
             'tweb.Login.Code.SecretWord' => 'Ú©Ù„Ù…Ù‡ Ù…Ø®Ù�ÛŒ',
             'tweb.Login.Code.SentCall' => 'Ø¨Ø§ Ø´Ù…Ø§ ØªÙ…Ø§Ø³ Ù…ÛŒâ€ŒÚ¯ÛŒØ±ÛŒÙ… Ùˆ
Ú©Ø¯ Ø±Ø§ Ø¨Ø±Ø§ÛŒØªØ§Ù† Ù…ÛŒâ€ŒØ®ÙˆØ§Ù†ÛŒÙ….',
             'tweb.Login.Code.SentEmail' => 'Ù„Ø·Ù�Ø§Ù‹ Ø§ÛŒÙ…ÛŒÙ„ Ø®ÙˆØ¯ **%s** Ø±Ø§ Ø¨Ø±Ø±Ø³ÛŒ Ú©Ù†ÛŒØ¯ (Ù¾ÙˆØ´Ù‡ Ø§Ø³Ù¾Ù… Ø±Ø§ Ù�Ø±Ø§Ù…ÙˆØ´ Ù†Ú©Ù†ÛŒØ¯) Ùˆ Ú©Ø¯ÛŒ Ø±Ø§ Ú©Ù‡ Ù‡Ù…ÛŒÙ† Ø­Ø§Ù„Ø§ Ù�Ø±Ø³ØªØ§Ø¯ÛŒÙ… ÙˆØ§Ø±Ø¯ Ú©Ù†ÛŒØ¯.',
             'tweb.Login.Code.SentInApp' => 'Ú©Ø¯ Ø±Ø§ Ø¯Ø± Ù¾ÛŒØ§Ù…ÛŒ Ø¯Ø± Telegram
Ø¨Ø±Ø§ÛŒØªØ§Ù† Ù�Ø±Ø³ØªØ§Ø¯ÛŒÙ….',
             'tweb.Login.Code.SentMissedCall' => 'ØªØ§ Ú†Ù†Ø¯ Ø«Ø§Ù†ÛŒÙ‡ Ø¯ÛŒÚ¯Ø± ÛŒÚ© ØªÙ…Ø§Ø³ Ú©ÙˆØªØ§Ù‡
Ø§Ø² **%s** Ø¯Ø±ÛŒØ§Ù�Øª Ù…ÛŒâ€ŒÚ©Ù†ÛŒØ¯. Ø¨Ù‡ Ø¢Ù† Ù¾Ø§Ø³Ø® Ù†Ø¯Ù‡ÛŒØ¯ â€”
Ù�Ù‚Ø· Ø§Ø±Ù‚Ø§Ù… Ø¢Ø®Ø± Ø¢Ù† Ø´Ù…Ø§Ø±Ù‡ Ø±Ø§ ÙˆØ§Ø±Ø¯ Ú©Ù†ÛŒØ¯.',
             'tweb.Login.Code.SentSms' => 'Ú©Ø¯ Ø±Ø§ Ø¨Ø§ Ù¾ÛŒØ§Ù…Ú©
Ø¨Ø±Ø§ÛŒØªØ§Ù† Ù�Ø±Ø³ØªØ§Ø¯ÛŒÙ….',
             'tweb.Login.Code.SentSmsPhrase' => 'ÛŒÚ© Ù¾ÛŒØ§Ù…Ú© Ø­Ø§ÙˆÛŒ Ø¹Ø¨Ø§Ø±Øª Ù…Ø®Ù�ÛŒ
Ø¨Ù‡ Ø´Ù…Ø§Ø±Ù‡ **%s** Ù�Ø±Ø³ØªØ§Ø¯ÛŒÙ….',
             'tweb.Login.Code.SentSmsPhraseBeginning' => 'ÛŒÚ© Ù¾ÛŒØ§Ù…Ú© Ø­Ø§ÙˆÛŒ Ø¹Ø¨Ø§Ø±Øª Ù…Ø®Ù�ÛŒ Ú©Ù‡ Ø¨Ø§
"**%2$s**" Ø´Ø±ÙˆØ¹ Ù…ÛŒâ€ŒØ´ÙˆØ¯ Ø¨Ù‡ Ø´Ù…Ø§Ø±Ù‡ **%1$s** Ù�Ø±Ø³ØªØ§Ø¯ÛŒÙ….',
             'tweb.Login.Code.SentSmsWord' => 'ÛŒÚ© Ù¾ÛŒØ§Ù…Ú© Ø­Ø§ÙˆÛŒ Ú©Ù„Ù…Ù‡ Ù…Ø®Ù�ÛŒ
Ø¨Ù‡ Ø´Ù…Ø§Ø±Ù‡ **%s** Ù�Ø±Ø³ØªØ§Ø¯ÛŒÙ….',
             'tweb.Login.Code.SentSmsWordBeginning' => 'ÛŒÚ© Ù¾ÛŒØ§Ù…Ú© Ø­Ø§ÙˆÛŒ Ú©Ù„Ù…Ù‡ Ù…Ø®Ù�ÛŒ Ú©Ù‡ Ø¨Ø§
"**%2$s**" Ø´Ø±ÙˆØ¹ Ù…ÛŒâ€ŒØ´ÙˆØ¯ Ø¨Ù‡ Ø´Ù…Ø§Ø±Ù‡ **%1$s** Ù�Ø±Ø³ØªØ§Ø¯ÛŒÙ….',
             'tweb.Login.Code.SentUnknown' => 'Ù„Ø·Ù�Ø§Ù‹ Ù‡Ù…Ù‡â€ŒØ¬Ø§ Ø±Ø§ Ø¨Ø±Ø§ÛŒ ÛŒØ§Ù�ØªÙ†
Ú©Ø¯ Ø¨Ø±Ø±Ø³ÛŒ Ú©Ù†ÛŒØ¯ (Ù†ÙˆØ¹: %s)',
             'tweb.Login.Code.WordBeginningInvalid' => 'Ú©Ù„Ù…Ù‡ Ù†Ø§Ø¯Ø±Ø³Øª',
             'tweb.Login.Code.WordInvalid' => 'Ù†Ø§Ø¯Ø±Ø³Øª Ø§Ø³ØªØŒ Ù„Ø·Ù�Ø§Ù‹ Ø¯ÙˆØ¨Ø§Ø±Ù‡ Ø§Ù…ØªØ­Ø§Ù† Ú©Ù†ÛŒØ¯',
             'tweb.Login.Next' => 'Ø¨Ø¹Ø¯ÛŒ',
             'tweb.Login.Password.Subtitle' => 'Ø­Ø³Ø§Ø¨ Ø´Ù…Ø§ Ø¨Ø§ ÛŒÚ© Ø±Ù…Ø² Ø¹Ø¨ÙˆØ±
Ø§Ø¶Ø§Ù�ÛŒ Ù…Ø­Ø§Ù�Ø¸Øª Ù…ÛŒâ€ŒØ´ÙˆØ¯',
             'tweb.Login.Password.Title' => 'Ø±Ù…Ø² Ø¹Ø¨ÙˆØ± Ø®ÙˆØ¯ Ø±Ø§ ÙˆØ§Ø±Ø¯ Ú©Ù†ÛŒØ¯',
             'tweb.Login.PhoneLabelInvalid' => 'Ø´Ù…Ø§Ø±Ù‡ ØªÙ„Ù�Ù† Ù†Ø§Ù…Ø¹ØªØ¨Ø± Ø§Ø³Øª',
             'tweb.Login.QR.Cancel' => 'ÙˆØ±ÙˆØ¯ Ø¨Ø§ Ø´Ù…Ø§Ø±Ù‡ ØªÙ„Ù�Ù† >',
             'tweb.Login.QR.Help1' => 'Telegram Ø±Ø§ Ø¯Ø± Ú¯ÙˆØ´ÛŒ Ø®ÙˆØ¯ Ø¨Ø§Ø² Ú©Ù†ÛŒØ¯',
             'tweb.Login.QR.Help2' => 'Ø¨Ù‡ **ØªÙ†Ø¸ÛŒÙ…Ø§Øª** > **Ø¯Ø³ØªÚ¯Ø§Ù‡â€ŒÙ‡Ø§** > **Ø§Ù�Ø²ÙˆØ¯Ù† Ø¯Ø³ØªÚ¯Ø§Ù‡** Ø¨Ø±ÙˆÛŒØ¯',
             'tweb.Login.QR.Help3' => 'Ú¯ÙˆØ´ÛŒ Ø®ÙˆØ¯ Ø±Ø§ Ø¨Ù‡ Ø³Ù…Øª Ø§ÛŒÙ† ØµÙ�Ø­Ù‡ Ø¨Ú¯ÛŒØ±ÛŒØ¯ ØªØ§ ÙˆØ±ÙˆØ¯ Ø±Ø§ ØªØ£ÛŒÛŒØ¯ Ú©Ù†ÛŒØ¯',
             'tweb.Login.QR.Login' => 'ÙˆØ±ÙˆØ¯ Ø¨Ø§ QR Code >',
             'tweb.Login.QR.Subtitle' => 'Ø¨Ø§ Ø¨Ø±Ù†Ø§Ù…Ù‡ Telegram Ø±ÙˆÛŒ Ú¯ÙˆØ´ÛŒ Ø®ÙˆØ¯ Ø§Ø³Ú©Ù† Ú©Ù†ÛŒØ¯',
             'tweb.Login.QR.Title' => 'ÙˆØ±ÙˆØ¯ Ø¨Ø§ QR Code',
             'tweb.Login.Register.Subtitle' => 'Ø¨Ø±Ø§ÛŒ Ø³Ø§Ø®Øª Ø­Ø³Ø§Ø¨
Ù†Ø§Ù… Ø®ÙˆØ¯ Ø±Ø§ ÙˆØ§Ø±Ø¯ Ú©Ù†ÛŒØ¯',
             'tweb.Login.StartText' => 'Ù„Ø·Ù�Ø§Ù‹ Ú©Ø¯ Ú©Ø´ÙˆØ± Ø®ÙˆØ¯ Ø±Ø§ ØªØ£ÛŒÛŒØ¯ Ú©Ù†ÛŒØ¯
Ùˆ Ø´Ù…Ø§Ø±Ù‡ ØªÙ„Ù�Ù† Ø®ÙˆØ¯ Ø±Ø§ ÙˆØ§Ø±Ø¯ Ú©Ù†ÛŒØ¯.',
             'tweb.Login.Title' => 'ÙˆØ±ÙˆØ¯ Ø¨Ù‡ MadelineProto',
             'tweb.LoginPassword' => 'Ø±Ù…Ø² Ø¹Ø¨ÙˆØ±',
             'tweb.Optional' => 'Ø§Ø®ØªÛŒØ§Ø±ÛŒ',
             'tweb.PASSWORD_HASH_INVALID' => 'Ø±Ù…Ø² Ø¹Ø¨ÙˆØ± Ù†Ø§Ø¯Ø±Ø³Øª Ø§Ø³Øª',
             'tweb.PHONE_CODE_EMPTY' => 'Ú©Ø¯ Ù†Ø§Ù…Ø¹ØªØ¨Ø± Ø§Ø³Øª',
             'tweb.PHONE_CODE_EXPIRED' => 'Ú©Ø¯ Ù…Ù†Ù‚Ø¶ÛŒ Ø´Ø¯Ù‡ Ø§Ø³Øª',
             'tweb.PHONE_CODE_INVALID' => 'Ú©Ø¯ Ù†Ø§Ù…Ø¹ØªØ¨Ø± Ø§Ø³Øª',
             'tweb.PHONE_NUMBER_BANNED' => 'Ø§ÛŒÙ† Ø´Ù…Ø§Ø±Ù‡ ØªÙ„Ù�Ù† Ù…Ø³Ø¯ÙˆØ¯ Ø´Ø¯Ù‡ Ø§Ø³Øª',
             'tweb.PHONE_NUMBER_FLOOD' => 'ØªÙ„Ø§Ø´â€ŒÙ‡Ø§ÛŒ Ø¨ÛŒØ´ Ø§Ø² Ø­Ø¯ØŒ Ø¨Ø¹Ø¯Ø§Ù‹ Ø¯ÙˆØ¨Ø§Ø±Ù‡ Ø§Ù…ØªØ­Ø§Ù† Ú©Ù†ÛŒØ¯',
             'tweb.PHONE_NUMBER_INVALID' => 'Ø´Ù…Ø§Ø±Ù‡ ØªÙ„Ù�Ù† Ù†Ø§Ù…Ø¹ØªØ¨Ø± Ø§Ø³Øª',
             'tweb.PHONE_PASSWORD_FLOOD' => 'ØªÙ„Ø§Ø´â€ŒÙ‡Ø§ÛŒ Ø¨ÛŒØ´ Ø§Ø² Ø­Ø¯ØŒ Ø¨Ø¹Ø¯Ø§Ù‹ Ø¯ÙˆØ¨Ø§Ø±Ù‡ Ø§Ù…ØªØ­Ø§Ù† Ú©Ù†ÛŒØ¯',
             'tweb.PhoneNumber.Code.Fragment.Info' => 'Ú©Ø¯ Ø±Ø§ Ø§Ø² Ø¨Ø®Ø´ Ø´Ù…Ø§Ø±Ù‡â€ŒÙ‡Ø§ Ø¯Ø± [Fragment]() Ø¯Ø±ÛŒØ§Ù�Øª Ú©Ù†ÛŒØ¯',
             'tweb.PleaseWait' => 'Ù„Ø·Ù�Ø§Ù‹ ØµØ¨Ø± Ú©Ù†ÛŒØ¯...',
             'tweb.StartMessaging' => 'Ø´Ø±ÙˆØ¹ Ù¾ÛŒØ§Ù…â€ŒØ±Ø³Ø§Ù†ÛŒ',
             'tweb.YourName' => 'Ù†Ø§Ù… Ø´Ù…Ø§',
             'type_extract_error' => 'Ù†Ù…ÛŒâ€ŒØªÙˆØ§Ù† Ù†ÙˆØ¹ (ØªØ§ÛŒÙ¾) "%s" Ø±Ø§ Ø§Ø³ØªØ®Ø±Ø§Ø¬ Ú©Ø±Ø¯ØŒ Ø´Ù…Ø§ Ø¨Ø§ÛŒØ¯ MadelineProto Ø±Ø§ Ø¨Ù‡ Ø±ÙˆØ² Ø±Ø³Ø§Ù†ÛŒ Ú©Ù†ÛŒØ¯!',
             'type_extract_error_id' => 'Ù†Ù…ÛŒâ€ŒØªÙˆØ§Ù† Ù†ÙˆØ¹: %s Ø±Ø§ Ø¨Ø§ Ø§ÛŒØ¯ÛŒ %s Ø§Ø³ØªØ®Ø±Ø§Ø¬ Ú©Ø±Ø¯ØŒ Ø´Ù…Ø§ Ø¨Ø§ÛŒØ¯ MadelineProto Ø±Ø§ Ø¨Ù‡ Ø±ÙˆØ² Ø±Ø³Ø§Ù†ÛŒ Ú©Ù†ÛŒØ¯!',
             'update_madelineproto' => 'Ø´Ù…Ø§ Ù†Ø³Ø®Ù‡ Ù‚Ø¯ÛŒÙ…ÛŒ MadelineProto Ø±Ø§ Ø§Ø¬Ø±Ø§ Ù…ÛŒâ€ŒÚ©Ù†ÛŒØ¯ØŒ Ø³ÙˆØ±Ø³ Ø´Ù…Ø§ Ù†ÛŒØ§Ø² Ø¨Ù‡ Ø¨Ø±ÙˆØ²Ø±Ø³Ø§Ù†ÛŒ Ø¯Ø§Ø±Ø¯: Ø¯Ø± Ø­Ø§Ù„ Ø­Ø§Ø¶Ø± %s Ø¯Ø± Ø­Ø§Ù„ Ø§Ø¬Ø±Ø§ Ø§Ø³ØªØŒ Ø§Ù…Ø§ Ø¢Ø®Ø±ÛŒÙ† Ù†Ø³Ø®Ù‡ Ø¨Ø§ Ú†Ù†Ø¯ÛŒÙ† Ø±Ù�Ø¹ Ø§Ø´Ú©Ø§Ù„ Ùˆ ÙˆÛŒÚ˜Ú¯ÛŒâ€ŒÙ‡Ø§ÛŒ Ø¬Ø¯ÛŒØ¯ %s Ø§Ø³Øª!',
             'value_bigger_than_2147483647' => 'Ù…Ù‚Ø¯Ø§Ø± Ø¯Ø§Ø¯Ù‡ Ø´Ø¯Ù‡â€ŒÛŒ %s Ø¨ÛŒØ´ØªØ± Ø§Ø² 2147483647 Ø§Ø³Øª',
             'value_bigger_than_4294967296' => 'Ù…Ù‚Ø¯Ø§Ø± Ø¯Ø§Ø¯Ù‡ Ø´Ø¯Ù‡â€ŒÛŒ %s Ø¨ÛŒØ´ØªØ± Ø§Ø² 4294967296 Ø§Ø³Øª',
             'value_bigger_than_9223372036854775807' => 'Ù…Ù‚Ø¯Ø§Ø± Ø¯Ø§Ø¯Ù‡ Ø´Ø¯Ù‡â€ŒÛŒ %s Ø¨ÛŒØ´ØªØ± Ø§Ø² 9223372036854775807 Ø§Ø³Øª',
             'value_smaller_than_0' => 'Ù…Ù‚Ø¯Ø§Ø± Ø¯Ø§Ø¯Ù‡ Ø´Ø¯Ù‡â€ŒÛŒ %s Ú©Ù…ØªØ± Ø§Ø² 0 Ø§Ø³Øª',
             'value_smaller_than_2147483648' => 'Ù…Ù‚Ø¯Ø§Ø± Ø¯Ø§Ø¯Ù‡ Ø´Ø¯Ù‡â€ŒÛŒ %s Ú©Ù…ØªØ± Ø§Ø² -2147483648 Ø§Ø³Øª',
             'value_smaller_than_9223372036854775808' => 'Ù…Ù‚Ø¯Ø§Ø± Ø¯Ø§Ø¯Ù‡ Ø´Ø¯Ù‡â€ŒÛŒ %s Ú©Ù…ØªØ± Ø§Ø² -9223372036854775808 Ø§Ø³Øª',
             'waveform_must_have_100_values' => 'Ø¢Ø±Ø§ÛŒÙ‡ waveformØ¨Ø§ÛŒØ¯ 100 Ù…Ù‚Ø¯Ø§Ø± Ø¯Ø§Ø´ØªÙ‡ Ø¨Ø§Ø´Ø¯(Ø³Ø§ÛŒØ² Ù…ÙˆØ±Ø¯ Ø§Ù†ØªØ¸Ø§Ø± 100 Ø§Ø³Øª ) !',
             'waveform_value' => 'Ù…Ù‚Ø¯Ø§Ø± Ø¹Ø¯Ø¯ÛŒwaveform Ø¨Ø§ÛŒØ¯ Ø¨ÛŒÙ† 0 ØªØ§ 31 Ø¨Ø§Ø´Ø¯!',
             'webContinue' => 'Ø§Ø¯Ø§Ù…Ù‡',
             'webDocs' => 'Ù…Ø³ØªÙ†Ø¯Ø§Øª',
             'webDoneLead' => 'MadelineProto Ø±ÙˆÛŒ Ø³Ø±ÙˆØ± Ø¨Ù‡ Ø§Ø¬Ø±Ø§ Ø§Ø¯Ø§Ù…Ù‡ Ù…ÛŒâ€ŒØ¯Ù‡Ø¯: Ù…ÛŒâ€ŒØªÙˆØ§Ù†ÛŒØ¯ Ø§ÛŒÙ† ØµÙ�Ø­Ù‡ Ø±Ø§ Ø¨Ø¨Ù†Ø¯ÛŒØ¯.',
             'webError' => '%s. Ø¢Ù† Ø±Ø§ Ø¨Ø±Ø±Ø³ÛŒ Ú©Ù†ÛŒØ¯ Ùˆ Ø¯ÙˆØ¨Ø§Ø±Ù‡ Ø§Ù…ØªØ­Ø§Ù† Ú©Ù†ÛŒØ¯.',
             'webFieldRequired' => 'Ø§ÛŒÙ† Ù�ÛŒÙ„Ø¯ Ø±Ø§ Ù¾Ø± Ú©Ù†ÛŒØ¯.',
             'webHidePassword' => 'Ù¾Ù†Ù‡Ø§Ù† Ú©Ø±Ø¯Ù†',
             'webLogin' => 'ÙˆØ±ÙˆØ¯',
             'webOptional' => 'Ø§Ø®ØªÛŒØ§Ø±ÛŒ',
             'webPhoneInvalid' => 'Ø´Ù…Ø§Ø±Ù‡ ØªÙ„Ù�Ù† Ø±Ø§ Ù‡Ù…Ø±Ø§Ù‡ Ø¨Ø§ Ú©Ø¯ Ú©Ø´ÙˆØ± ÙˆØ§Ø±Ø¯ Ú©Ù†ÛŒØ¯ØŒ Ù…Ø§Ù†Ù†Ø¯ +39 123 456 7890.',
             'webProgress' => 'Ù¾ÛŒØ´Ø±Ù�Øª Ø±Ø§Ù‡â€ŒØ§Ù†Ø¯Ø§Ø²ÛŒ',
             'webQrTitle' => 'ÙˆØ±ÙˆØ¯ Ø¨Ø§ QR Code',
             'webQrWaiting' => 'Ø¯Ø± Ø§Ù†ØªØ¸Ø§Ø± Ø§Ø³Ú©Ù†â€¦',
             'webShowPassword' => 'Ù†Ù…Ø§ÛŒØ´',
             'webStepAccount' => 'Ø­Ø³Ø§Ø¨',
             'webStepApp' => 'API ID Ùˆ API Hash',
             'webStepDone' => 'Ø¯Ø± Ø­Ø§Ù„ Ø§Ø¬Ø±Ø§',
             'webStepVerify' => 'ØªØ£ÛŒÛŒØ¯',
         ],
        'fr' =>
         [
             '2fa_uncalled' => 'Je n\'attends pas le mot de passe! Veuillez d\'abord appeler les mÃ©thodes phoneLogin et completePhoneLogin!',
             'accepting_call' => 'Acceptation de l\'appel de %s...',
             'account_banned' => '!!!!!!! AVERTISSEMENT !!!!!!!
Le systÃ¨me anti-flood de Telegram a suspendu ce compte.
Pour continuer, une vÃ©rification manuelle est requise.
Envoyez un e-mail Ã  recover@telegram.org pour demander le dÃ©bannissement du numÃ©ro de tÃ©lÃ©phone %s, en dÃ©crivant briÃ¨vement ce que vous ferez avec ce numÃ©ro.
Puis connectez-vous Ã  nouveau.
Si vous avez supprimÃ© ce compte intentionnellement, ignorez ce message.',
             'already_loggedIn' => 'Cette instance de MadelineProto est dÃ©jÃ  connectÃ©e!',
             'apiAppDescLabelWeb' => 'Description',
             'apiAppInstructionsAuto0' => 'Entrez le nom de l\'application, peut Ãªtre n\'importe quoi: ',
             'apiAppInstructionsAuto1' => 'Entrez le nom court de l\'application, alphanumÃ©rique, 5-32 caractÃ¨res: ',
             'apiAppInstructionsAuto2' => 'Entrez l\'URL de l\'application/du site web, ou t.me/yourusername: ',
             'apiAppInstructionsAuto3' => 'Entrez la plateforme de l\'application: ',
             'apiAppInstructionsAuto4' => 'DÃ©crivez votre application: ',
             'apiAppInstructionsAutoTypeOther' => 'Autre (Ã  prÃ©ciser dans la description)',
             'apiAppInstructionsManual0' => 'le nom de votre application, peut Ãªtre n\'importe quoi',
             'apiAppInstructionsManual1' => 'le nom court de votre application, alphanumÃ©rique, 5-32 caractÃ¨res',
             'apiAppInstructionsManual2' => 'l\'URL de votre application/site web, ou t.me/yourusername',
             'apiAppInstructionsManual3' => 'n\'importe quoi',
             'apiAppInstructionsManual4' => 'DÃ©crivez votre application ici',
             'apiAppShortNameLabelWeb' => 'Nom court',
             'apiAppTitleLabelWeb' => 'Nom de l\'application',
             'apiAppUrlLabelWeb' => 'URL',
             'apiAppWeb' => 'Entrez les informations de l\'API',
             'apiAutoCodeLabelWeb' => 'Code de connexion',
             'apiAutoCodeLeadWeb' => 'my.telegram.org a envoyÃ© un code de connexion Ã  votre application Telegram, dans la discussion avec Telegram (pas par SMS).',
             'apiAutoCodeTitleWeb' => 'Entrez le code reÃ§u dans Telegram',
             'apiAutoErrorTitle' => 'my.telegram.org a renvoyÃ© une erreur',
             'apiAutoFallbackManual' => 'Saisissez plutÃ´t l\'API ID et le hash manuellement.',
             'apiAutoPhoneHintWeb' => 'my.telegram.org envoie le code de connexion Ã  votre application Telegram, pas par SMS.',
             'apiAutoPrompt0' => 'Entrez un numÃ©ro de tÃ©lÃ©phone dÃ©jÃ  enregistrÃ© sur Telegram: ',
             'apiAutoPrompt1' => 'Saisissez le code de vÃ©rification que vous avez reÃ§u dans Telegram : ',
             'apiAutoWeb' => 'Entrez un numÃ©ro de tÃ©lÃ©phone <b>dÃ©jÃ  enregistrÃ©</b> sur Telegram pour obtenir l\'API ID',
             'apiChooseAutoWeb' => 'Les obtenir automatiquement',
             'apiChooseManualAuto' => 'Voulez-vous obtenir l\'API ID et le hash automatiquement depuis my.telegram.org (a), ou les saisir manuellement (m) ?',
             'apiChooseManualAutoTip' => 'Notez que vous pouvez Ã©galement fournir l\'API ID/hash directement dans le code via les paramÃ¨tres: %s',
             'apiChooseManualAutoTipWeb' => 'Notez que vous pouvez Ã©galement fournir l\'API ID/hash directement dans le code via les <a target="_blank" href="%s">paramÃ¨tres</a>.',
             'apiChooseManualWeb' => 'J\'ai dÃ©jÃ  un API ID',
             'apiChoosePrompt' => 'Votre choix (m/a): ',
             'apiCreateErrorTitle' => 'my.telegram.org n\'a pas crÃ©Ã© l\'application',
             'apiCreateLeadWeb' => 'Ce compte n\'a pas encore d\'application sur my.telegram.org. Ces informations ne sont visibles que par vous et pourront Ãªtre modifiÃ©es plus tard.',
             'apiCreateTitleWeb' => 'CrÃ©ez votre application',
             'apiCreateVpsWarning' => 'Telegram ne permet pas aux plages d\'IP de VPS, de VPN et autres centres de donnÃ©es de crÃ©er de nouvelles applications sur my.telegram.org, et la requÃªte a Ã©tÃ© faite depuis l\'IP de la machine qui exÃ©cute MadelineProto. CrÃ©ez l\'application manuellement sur https://my.telegram.org depuis une connexion rÃ©sidentielle (comme votre tÃ©lÃ©phone en donnÃ©es mobiles), puis saisissez l\'API ID et le hash.',
             'apiCreateWeb' => 'CrÃ©er l\'application',
             'apiError' => 'ERREUR: %s. Essayez de nouveau.',
             'apiInvalidWeb' => 'L\'API ID est un nombre et l\'API hash fait 32 caractÃ¨res : copiez-les Ã  nouveau depuis my.telegram.org.',
             'apiManualInstructions0' => 'Connectez-vous Ã  https://my.telegram.org',
             'apiManualInstructions1' => 'Allez dans Â« API development tools Â»',
             'apiManualInstructions2' => 'Cliquez sur Â« Create application Â»',
             'apiManualPrompt0' => 'Entrez votre API ID: ',
             'apiManualPrompt1' => 'Entrez votre API hash: ',
             'apiManualWeb' => 'Entrez votre API ID et votre API hash',
             'apiParamsError' => 'Vous n\'avez pas fourni tous les paramÃ¨tres requis!',
             'apiSaveWeb' => 'Enregistrer et continuer',
             'apiSendCodeWeb' => 'Envoyer le code',
             'apiSetupLeadWeb' => 'Chaque application Telegram a besoin d\'un API ID et d\'un hash. MadelineProto peut les obtenir pour vous depuis my.telegram.org, avec le numÃ©ro de tÃ©lÃ©phone d\'un compte Telegram existant.',
             'apiSetupTitleWeb' => 'Obtenez votre API ID et votre hash',
             'apiStartOverWeb' => 'Recommencer',
             'api_not_set' => 'Vous devez fournir une clÃ© API et un ID API, obtenez les vÃ´tres sur my.telegram.org',
             'array_invalid' => 'Vous n\'avez pas fourni de tableau valide',
             'baseDirLimitation' => 'Une restriction basedir est configurÃ©e: cela peut affecter les performances et causer des problÃ¨mes, veuillez la dÃ©sactiver si possible!',
             'bool_error' => 'Impossible d\'extraire le boolÃ©en',
             'botAlreadyRunning' => 'Le bot est dÃ©jÃ  en cours d\'exÃ©cution!',
             'botapi_conversion_error' => 'Impossible de convertir %s en objet de la bot API',
             'call_already_accepted' => 'Appel %s dÃ©jÃ  acceptÃ©',
             'call_already_declined' => 'Appel %s dÃ©jÃ  refusÃ©',
             'call_completing' => 'Finalisation de l\'appel de %s...',
             'call_confirming' => 'Confirmation de l\'appel de %s...',
             'call_discarding' => 'Abandon de l\'appel %s...',
             'call_error_1' => 'Impossible de trouver et d\'accepter l\'appel %s',
             'call_error_2' => 'Impossible de trouver et de confirmer l\'appel %s',
             'call_error_3' => 'Impossible de trouver et de finaliser l\'appel %s',
             'cli_need_dl.php_link' => 'Veuillez spÃ©cifier l\'URL d\'un script de tÃ©lÃ©chargement lorsque vous utilisez getDownloadLink en CLI!',
             'constructor_not_found' => 'Constructeur introuvable pour le type: ',
             'could_not_connect_to_MadelineProto' => 'Impossible de se connecter Ã  MadelineProto, veuillez activer proc_open et supprimer les restrictions open_basedir ou dÃ©sactiver la rÃ©Ã©criture des chemins du serveur web pour corriger le problÃ¨me! Si c\'est dÃ©jÃ  fait, assurez-vous que la version CLI de PHP est exactement la mÃªme que la version web (mÃªme version, mÃªmes extensions, etc.) et consultez le fichier MadelineProto.log pour plus d\'informations sur l\'erreur qui a empÃªchÃ© le dÃ©marrage du serveur IPC.',
             'could_not_convert_object' => 'Impossible de convertir l\'objet de type %s',
             'deserialization_error' => 'Une erreur s\'est produite lors de la dÃ©sÃ©rialisation',
             'dl.php_check_logs_make_sure_session_running' => 'Le bot EventHandler MadelineProto associÃ© ou le serveur IPC MadelineProto est hors ligne, veuillez vÃ©rifier les logs et vous assurer qu\'au moins l\'un d\'eux est en cours d\'exÃ©cution!',
             'dl.php_powered_by_madelineproto' => 'Serveur de tÃ©lÃ©chargement de fichiers Telegram (jusqu\'Ã  4 Go), propulsÃ© par <a href="https://docs.madelineproto.xyz" target="_blank">MadelineProto</a>!<br>Cliquez <a href="https://docs.madelineproto.xyz/docs/FILES.html#getting-a-download-link" target="_blank">ici</a> pour savoir comment configurer votre propre serveur de tÃ©lÃ©chargement de fichiers Telegram!',
             'do_not_delete_MadelineProto.log' => 'le fichier MadelineProto.log ne doit jamais Ãªtre supprimÃ©, veuillez plutÃ´t dÃ©finir une taille maximale personnalisÃ©e dans les paramÃ¨tres!',
             'do_not_remove_MadelineProto.log_phar' => 'Veuillez ne pas supprimer madeline.phar ou madeline.php, sinon MadelineProto plantera. Si vous rencontrez un problÃ¨me avec MadelineProto, signalez-le sur https://github.com/danog/MadelineProto ou https://t.me/pwrtelegramgroup',
             'do_not_use_blocking_class' => 'pour des raisons de performance, les gestionnaires ne peuvent pas utiliser la classe bloquante non asynchrone %s, veuillez plutÃ´t utiliser %s',
             'do_not_use_blocking_function' => 'pour des raisons de performance, les gestionnaires d\'Ã©vÃ©nements ne peuvent pas utiliser la fonction bloquante non asynchrone %s, veuillez plutÃ´t utiliser %s',
             'do_not_use_deprecated_function' => 'la fonction %s est obsolÃ¨te, veuillez plutÃ´t utiliser %s',
             'do_not_use_non_root_require_in_event_handler' => 'pour des raisons de performance, vous ne devez pas utiliser require ou include Ã  l\'intÃ©rieur d\'une classe de gestionnaire d\'Ã©vÃ©nements, seuls les require au niveau racine sont autorisÃ©s.',
             'do_not_use_yield' => 'MadelineProto 8 ne nÃ©cessite pas et ne prend pas en charge l\'utilisation de yield dans les fonctions asynchrones, vous devez supprimer tous les mots-clÃ©s yield utilisÃ©s auparavant pour les appels de fonctions asynchrones',
             'done' => 'TerminÃ©!',
             'encode_double_error' => 'Impossible d\'encoder correctement le double',
             'extensionRecommended' => 'Avertissement: l\'extension %s n\'est pas installÃ©e, veuillez l\'installer pour accÃ©lÃ©rer MadelineProto!',
             'extensionRequired' => 'MadelineProto nÃ©cessite l\'extension %s pour fonctionner. %s',
             'extensionRequiredInstallWithApt' => 'Essayez d\'exÃ©cuter sudo apt-get install %s.',
             'extensionRequiredInstallWithCustomInstructions' => 'Suivez les instructions sur %s pour l\'installer.',
             'file_not_exist' => 'Le fichier n\'existe pas',
             'file_parsing' => 'Analyse de %s...',
             'file_type_invalid' => 'Type de fichier invalide dÃ©tectÃ© (%s)',
             'fingerprint_invalid' => 'Empreinte de clÃ© invalide!',
             'go' => 'Valider',
             'invalid_dl.php_session' => '%s n\'est pas un script de tÃ©lÃ©chargement valide car son ID de session est diffÃ©rent (attendu %s, obtenu %s)',
             'ipc_tcp_warning' => 'UNIX sockets are not available, so the IPC server of the session listens on TCP (127.0.0.1) instead: any program running on this machine can connect to it, and control the session. On Windows 10 1803+, enable the sockets extension (extension=sockets in php.ini) to use UNIX sockets.',
             'length_too_big' => 'La longueur est trop grande',
             'loginBot' => 'Entrez le token de votre bot: ',
             'loginBotTokenHintWeb' => 'Le token que @BotFather vous a donnÃ© lors de la crÃ©ation du bot.',
             'loginBotTokenWeb' => 'Token du bot',
             'loginChoosePromptWeb' => 'Voulez-vous vous connecter en tant qu\'utilisateur ou en tant que bot?',
             'loginCodeLeadWeb' => 'Telegram a envoyÃ© un code de connexion Ã  votre application Telegram, ou par SMS si vous n\'Ãªtes connectÃ© nulle part ailleurs.',
             'loginCodeTitleWeb' => 'Entrez le code de connexion',
             'loginLeadWeb' => 'Choisissez le compte ou le bot sous lequel cette instance de MadelineProto fonctionnera.',
             'loginManual' => 'Vous pouvez aussi entrer un token de bot ou un numÃ©ro de tÃ©lÃ©phone pour vous connecter manuellement: ',
             'loginNoCode' => 'Vous n\'avez pas fourni de code de vÃ©rification!',
             'loginNoName' => 'Vous n\'avez pas fourni le prÃ©nom!',
             'loginNoPass' => 'Vous n\'avez pas fourni le mot de passe!',
             'loginOptionBot' => 'Bot',
             'loginOptionUser' => 'Utilisateur',
             'loginPasswordLabelWeb' => 'Mot de passe',
             'loginPasswordLeadWeb' => 'Ce compte est protÃ©gÃ© par un mot de passe, dÃ©fini dans ParamÃ¨tres > ConfidentialitÃ© et sÃ©curitÃ© > Validation en deux Ã©tapes.',
             'loginPasswordTitleWeb' => 'Validation en deux Ã©tapes',
             'loginQr' => 'Ouvrez Telegram sur votre tÃ©lÃ©phone, allez dans ParamÃ¨tres > Appareils > Lier un appareil de bureau et scannez le code QR ci-dessus pour vous connecter automatiquement.',
             'loginQrCodeExpired' => 'Le code QR a expirÃ©, gÃ©nÃ©ration d\'un nouveau code...',
             'loginQrCodeSuccessful' => 'Connexion par code QR rÃ©ussie!',
             'loginTitleWeb' => 'Connexion Ã  Telegram',
             'loginTypeWeb' => 'Se connecter en tant que',
             'loginUser' => 'Entrez votre numÃ©ro de tÃ©lÃ©phone: ',
             'loginUserCode' => 'Entrez le code: ',
             'loginUserPass' => 'Entrez votre mot de passe (Indice %s): ',
             'loginUserPassHint' => 'Indice: %s',
             'loginUserPassWeb' => 'Entrez votre mot de passe: ',
             'loginUserPhoneCodeWeb' => 'Code',
             'loginUserPhoneHintWeb' => 'Avec l\'indicatif du pays, par exemple +39 123 456 7890.',
             'loginUserPhoneWeb' => 'NumÃ©ro de tÃ©lÃ©phone',
             'loginWebQr' => 'Vous pouvez Ã©galement vous connecter automatiquement en ouvrant Telegram sur votre tÃ©lÃ©phone, en allant dans ParamÃ¨tres > Appareils > Lier un appareil de bureau et en scannant le code QR suivant:',
             'loginWebQr1' => 'Ouvrez Telegram sur votre tÃ©lÃ©phone',
             'loginWebQr2' => 'Allez dans ParamÃ¨tres > Appareils > Lier un appareil de bureau',
             'loginWebQr3' => 'Pointez votre tÃ©lÃ©phone vers cet Ã©cran pour confirmer la connexion',
             'login_2fa_enabled' => '2FA activÃ©, vous devrez appeler la fonction complete2falogin...',
             'login_auth_key' => 'Connexion avec la clÃ© d\'autorisation...',
             'login_bot' => 'Connexion en tant que bot...',
             'login_code_sending' => 'Envoi du code...',
             'login_code_sent' => 'Code envoyÃ© avec succÃ¨s! Une fois que vous avez reÃ§u le code, vous devez utiliser la fonction completePhoneLogin.',
             'login_code_uncalled' => 'Je n\'attends pas de code! Veuillez d\'abord appeler la mÃ©thode phoneLogin',
             'login_need_signup' => 'Aucun compte n\'a Ã©tÃ© crÃ©Ã© pour ce numÃ©ro, vous devrez appeler la fonction completeSignup...',
             'login_ok' => 'ConnectÃ© avec succÃ¨s!',
             'login_user' => 'Connexion en tant qu\'utilisateur normal...',
             'logout_ok' => 'DÃ©connectÃ© avec succÃ¨s!',
             'long_not_16' => 'La valeur donnÃ©e ne fait pas 16 octets',
             'long_not_32' => 'La valeur donnÃ©e ne fait pas 32 octets',
             'long_not_64' => 'La valeur donnÃ©e ne fait pas 64 octets',
             'madelineproto_ready' => 'MadelineProto est prÃªt!',
             'manualAdminActionRequired' => '!!!!!!!!! INTERVENTION MANUELLE DE L\'ADMINISTRATEUR SYSTÃˆME REQUISE !!!!!!!!!',
             'method_not_found' => 'MÃ©thode introuvable: ',
             'mmapErrorPart1' => 'Le nombre maximal de rÃ©gions mappÃ©es en mÃ©moire (mmap) a Ã©tÃ© atteint (%s): pour corriger le problÃ¨me, veuillez augmenter le paramÃ¨tre du noyau vm.max_map_count Ã  262144.',
             'mmapErrorPart2' => 'Pour corriger le problÃ¨me, exÃ©cutez la commande suivante en tant que root: %s',
             'mmapErrorPart3' => 'Pour conserver la modification aprÃ¨s un redÃ©marrage: %s',
             'mmapErrorPart4' => 'Sous Windows et WSL, augmenter la taille du fichier d\'Ã©change peut aider; veuillez passer Ã  un Linux natif si le problÃ¨me persiste.',
             'must_have_declare_types' => 'pour des raisons de performance, la premiÃ¨re instruction d\'un fichier de gestionnaire d\'Ã©vÃ©nements doit Ãªtre "declare(strict_types=1);"',
             'nearest_dc' => 'Nous sommes en %s, le DC le plus proche est %d.',
             'need_dl.php' => 'Impossible de gÃ©nÃ©rer le script de tÃ©lÃ©chargement par dÃ©faut (%s), veuillez crÃ©er un fichier dl.php avec le contenu suivant: %s et passer son URL comme deuxiÃ¨me paramÃ¨tre de getDownloadLink',
             'noReportPeers' => 'Avertissement: aucun destinataire des rapports (report peers) n\'est dÃ©fini, veuillez ajouter la mÃ©thode suivante Ã  votre gestionnaire d\'Ã©vÃ©nements',
             'non_text_conversion' => 'Impossible de convertir des messages non textuels pour le moment!',
             'not_loggedIn' => 'Je ne suis pas connectÃ©!',
             'not_numeric' => 'La valeur donnÃ©e n\'est pas numÃ©rique',
             'params_missing' => 'ParamÃ¨tre requis manquant',
             'peer_not_in_db' => 'Ce peer n\'est pas prÃ©sent dans la base de donnÃ©es interne des peers',
             'plugin_path_does_not_exist' => 'Le chemin du plugin %s n\'existe pas!',
             'plugins_do_not_use_require' => 'pour des raisons de performance, les plugins ne peuvent inclure ou requÃ©rir automatiquement d\'autres fichiers prÃ©sents dans le dossier des plugins qu\'en dÃ©clenchant l\'autoloader PSR-4 (et non en les incluant manuellement avec require()).',
             'plugins_must_have_exactly_one_class' => 'un plugin doit dÃ©finir exactement une classe! Pour dÃ©finir plusieurs classes, interfaces ou traits, crÃ©ez des fichiers sÃ©parÃ©s, ils seront chargÃ©s automatiquement par MadelineProto.',
             'predicate_not_set' => 'Le prÃ©dicat (valeur sous _) n\'a pas Ã©tÃ© dÃ©fini!',
             'recommend_not_use_filesystem_function' => 'l\'utilisation de la fonction %s n\'est pas recommandÃ©e, car accÃ©der au systÃ¨me de fichiers pendant le traitement des mises Ã  jour ralentira votre bot, consultez https://docs.madelineproto.xyz/docs/UPDATES.html#avoiding-the-use-of-filesystem-functions pour une liste de mÃ©thodes alternatives de stockage des donnÃ©es qui ne ralentiront pas votre bot!',
             'rpc_tg_error' => 'Telegram a renvoyÃ© une erreur RPC: %s (%s), causÃ©e par %s:%s%sTrace TL:',
             'sec_peer_not_in_db' => 'Ce peer secret n\'est pas prÃ©sent dans la base de donnÃ©es interne des peers',
             'secret_chat_skipping' => 'Je n\'ai pas le chat secret %s dans la base de donnÃ©es, message ignorÃ©...',
             'serialization_ofd' => 'La sÃ©rialisation est obsolÃ¨te, reconstruction de l\'objet!',
             'session_corrupted' => 'La session est corrompue!',
             'signing_up' => 'Inscription en tant qu\'utilisateur normal...',
             'signupFirstName' => 'Entrez votre prÃ©nom: ',
             'signupFirstNameWeb' => 'PrÃ©nom',
             'signupLastName' => 'Entrez votre nom de famille (peut Ãªtre vide): ',
             'signupLastNameWeb' => 'Nom de famille',
             'signupLeadWeb' => 'Ce numÃ©ro de tÃ©lÃ©phone n\'est pas encore sur Telegram : choisissez le nom du nouveau compte.',
             'signupTitleWeb' => 'CrÃ©ez votre compte',
             'signupWeb' => 'Inscrivez-vous s\'il vous plaÃ®t',
             'signup_ok' => 'Inscription rÃ©ussie!',
             'signup_uncalled' => 'Je n\'attends pas d\'inscription! Veuillez d\'abord appeler les mÃ©thodes phoneLogin et completePhoneLogin!',
             'src_file_invalid' => 'Un fichier source invalide a Ã©tÃ© fourni: ',
             'static_analysis_minor' => 'Un problÃ¨me mineur a Ã©tÃ© dÃ©tectÃ© lors de l\'analyse statique de %s: %s',
             'static_analysis_severe' => 'Un problÃ¨me grave a Ã©tÃ© dÃ©tectÃ© lors de l\'analyse statique de %s: %s',
             'stream_handle_invalid' => 'Un handle de flux invalide a Ã©tÃ© fourni.',
             'string_required' => 'Une chaÃ®ne Ã©tait attendue!',
             'translate_madelineproto_cli' => 'MadelineProto peut Ãªtre traduit dans votre langue (progression actuelle de la traduction: %d%%), rendez-vous sur https://github.com/danog/MadelineProto/tree/v8/langs pour contribuer Ã  la traduction!',
             'translate_madelineproto_web' => 'MadelineProto peut Ãªtre traduit dans votre langue (progression actuelle de la traduction: %d%%), cliquez <a href="https://github.com/danog/MadelineProto/tree/v8/langs" target="_blank">ici pour contribuer Ã  la traduction!</a>',
             'tweb.ACCESS_TOKEN_EXPIRED' => 'Ce token de bot a Ã©tÃ© rÃ©voquÃ©',
             'tweb.ACCESS_TOKEN_INVALID' => 'Token de bot invalide',
             'tweb.AccDescr.LoginCode' => 'Code de connexion',
             'tweb.Close' => 'Fermer',
             'tweb.Code' => 'Code',
             'tweb.Contacts.PhoneNumber.Placeholder' => 'NumÃ©ro de tÃ©lÃ©phone',
             'tweb.Country' => 'Pays',
             'tweb.DarkMode' => 'Mode sombre',
             'tweb.Error.AnError' => 'Une erreur s\'est produite. Veuillez rÃ©essayer plus tard.',
             'tweb.FIRSTNAME_INVALID' => 'PrÃ©nom invalide',
             'tweb.FirstName' => 'PrÃ©nom (obligatoire)',
             'tweb.LASTNAME_INVALID' => 'Nom invalide',
             'tweb.LastName' => 'Nom (facultatif)',
             'tweb.Login.Bot' => 'Se connecter en tant que bot >',
             'tweb.Login.Bot.Cancel' => 'Se connecter en tant qu\'utilisateur >',
             'tweb.Login.Bot.Subtitle' => 'Entrez le token que @BotFather vous a donnÃ©
lors de la crÃ©ation du bot.',
             'tweb.Login.Bot.Title' => 'Connexion en tant que bot',
             'tweb.Login.Bot.Token' => 'Token du bot',
             'tweb.Login.Code.SecretPhrase' => 'Phrase secrÃ¨te',
             'tweb.Login.Code.SecretWord' => 'Mot secret',
             'tweb.Login.Code.SentCall' => 'Nous allons vous appeler pour vous dicter
le code.',
             'tweb.Login.Code.SentEmail' => 'Veuillez consulter votre e-mail **%s** (sans oublier le dossier des spams) et entrer le code que nous venons de vous envoyer.',
             'tweb.Login.Code.SentInApp' => 'Nous vous avons envoyÃ© un message dans Telegram
avec le code.',
             'tweb.Login.Code.SentMissedCall' => 'Dans les prochaines secondes, vous allez recevoir
un bref appel de **%s**. Ne rÃ©pondez pas :
entrez simplement les derniers chiffres de ce numÃ©ro.',
             'tweb.Login.Code.SentSms' => 'Nous vous avons envoyÃ© un SMS
avec le code.',
             'tweb.Login.Code.SentSmsPhrase' => 'Nous avons envoyÃ© un SMS avec une phrase secrÃ¨te
Ã  votre numÃ©ro **%s**.',
             'tweb.Login.Code.SentSmsPhraseBeginning' => 'Nous avons envoyÃ© un SMS avec une phrase secrÃ¨te commenÃ§ant
par Â« **%2$s** Â» Ã  votre numÃ©ro **%1$s**.',
             'tweb.Login.Code.SentSmsWord' => 'Nous avons envoyÃ© un SMS avec un mot secret
Ã  votre numÃ©ro **%s**.',
             'tweb.Login.Code.SentSmsWordBeginning' => 'Nous avons envoyÃ© un SMS avec un mot secret commenÃ§ant
par Â« **%2$s** Â» Ã  votre numÃ©ro **%1$s**.',
             'tweb.Login.Code.SentUnknown' => 'Veuillez chercher un code
partout (type : %s)',
             'tweb.Login.Code.WordBeginningInvalid' => 'Mot incorrect',
             'tweb.Login.Code.WordInvalid' => 'Incorrect, veuillez rÃ©essayer',
             'tweb.Login.Next' => 'Suivant',
             'tweb.Login.Password.Subtitle' => 'Votre compte est protÃ©gÃ© par
un mot de passe supplÃ©mentaire',
             'tweb.Login.Password.Title' => 'Entrez votre mot de passe',
             'tweb.Login.PhoneLabelInvalid' => 'NumÃ©ro de tÃ©lÃ©phone invalide',
             'tweb.Login.QR.Cancel' => 'Connexion par numÃ©ro de tÃ©lÃ©phone >',
             'tweb.Login.QR.Help1' => 'Ouvrez Telegram sur votre tÃ©lÃ©phone',
             'tweb.Login.QR.Help2' => 'Allez dans **ParamÃ¨tres** > **Appareils** > **Ajouter un appareil**',
             'tweb.Login.QR.Help3' => 'Pointez votre tÃ©lÃ©phone vers cet Ã©cran pour confirmer la connexion',
             'tweb.Login.QR.Login' => 'Connexion par code QR >',
             'tweb.Login.QR.Subtitle' => 'Scannez avec l\'application Telegram sur votre tÃ©lÃ©phone',
             'tweb.Login.QR.Title' => 'Connexion par code QR',
             'tweb.Login.Register.Subtitle' => 'Entrez votre nom
pour crÃ©er votre compte',
             'tweb.Login.StartText' => 'Veuillez confirmer l\'indicatif de votre pays
et entrer votre numÃ©ro de tÃ©lÃ©phone.',
             'tweb.Login.Title' => 'Connexion Ã  MadelineProto',
             'tweb.LoginPassword' => 'Mot de passe',
             'tweb.Optional' => 'facultatif',
             'tweb.PASSWORD_HASH_INVALID' => 'Mot de passe incorrect',
             'tweb.PHONE_CODE_EMPTY' => 'Code invalide',
             'tweb.PHONE_CODE_EXPIRED' => 'Code expirÃ©',
             'tweb.PHONE_CODE_INVALID' => 'Code invalide',
             'tweb.PHONE_NUMBER_BANNED' => 'Ce numÃ©ro de tÃ©lÃ©phone est banni',
             'tweb.PHONE_NUMBER_FLOOD' => 'Trop de tentatives, rÃ©essayez plus tard',
             'tweb.PHONE_NUMBER_INVALID' => 'NumÃ©ro de tÃ©lÃ©phone invalide',
             'tweb.PHONE_PASSWORD_FLOOD' => 'Trop de tentatives, rÃ©essayez plus tard',
             'tweb.PhoneNumber.Code.Fragment.Info' => 'Obtenez le code dans la section Numbers sur [Fragment]()',
             'tweb.PleaseWait' => 'Veuillez patienter...',
             'tweb.StartMessaging' => 'Commencer',
             'tweb.YourName' => 'Votre nom',
             'type_extract_error' => 'Impossible d\'extraire le type "%s", vous devriez mettre Ã  jour MadelineProto!',
             'type_extract_error_id' => 'Impossible d\'extraire le type %s avec l\'id %s, vous devriez mettre Ã  jour MadelineProto!',
             'update_madelineproto' => 'Vous utilisez une ancienne version de MadelineProto, une mise Ã  jour est requise: vous utilisez actuellement la version %s, mais la derniÃ¨re version, avec de nombreuses corrections de bugs et nouvelles fonctionnalitÃ©s, est la %s!',
             'value_bigger_than_2147483647' => 'La valeur fournie %s est supÃ©rieure Ã  2147483647',
             'value_bigger_than_4294967296' => 'La valeur fournie %s est supÃ©rieure Ã  4294967296',
             'value_bigger_than_9223372036854775807' => 'La valeur fournie %s est supÃ©rieure Ã  9223372036854775807',
             'value_smaller_than_0' => 'La valeur fournie %s est infÃ©rieure Ã  0',
             'value_smaller_than_2147483648' => 'La valeur fournie %s est infÃ©rieure Ã  -2147483648',
             'value_smaller_than_9223372036854775808' => 'La valeur fournie %s est infÃ©rieure Ã  -9223372036854775808',
             'waveform_must_have_100_values' => 'Un tableau de forme d\'onde doit contenir 100 valeurs!',
             'waveform_value' => 'Une valeur de forme d\'onde doit Ãªtre comprise entre 0 et 31!',
             'webContinue' => 'Continuer',
             'webDocs' => 'Documentation',
             'webDoneLead' => 'MadelineProto continue de tourner sur le serveur : vous pouvez fermer cette page.',
             'webError' => '%s. VÃ©rifiez et rÃ©essayez.',
             'webFieldRequired' => 'Veuillez remplir ce champ.',
             'webHidePassword' => 'Masquer',
             'webLogin' => 'Se connecter',
             'webOptional' => 'facultatif',
             'webPhoneInvalid' => 'Entrez le numÃ©ro de tÃ©lÃ©phone avec l\'indicatif du pays, par exemple +39 123 456 7890.',
             'webProgress' => 'Progression de la configuration',
             'webQrTitle' => 'Se connecter avec un code QR',
             'webQrWaiting' => 'En attente du scanâ€¦',
             'webShowPassword' => 'Afficher',
             'webStepAccount' => 'Compte',
             'webStepApp' => 'API ID et hash',
             'webStepDone' => 'En cours d\'exÃ©cution',
             'webStepVerify' => 'VÃ©rification',
         ],
        'he' =>
         [
             '2fa_uncalled' => '×�× ×™ ×œ×� ×ž×—×›×” ×œ×¡×™×¡×ž×�! ×¢×œ×™×š ×œ×”×©×ª×ž×© ×‘×¤×•× ×§×¦×™×•×ª phoneLogin ×•- completePhoneLogin ×§×•×“×�!',
             'accepting_call' => '×ž×§×‘×œ ×©×™×—×” ×ž %s...',
             'account_banned' => '!!!!!!! ×�×–×”×¨×”!!!!!!!
×ž×¢×¨×›×ª ×ž× ×™×¢×ª ×”×¦×¤×•×ª ×©×œ ×˜×œ×’×¨×� ×”×©×¢×ª×” ×�×ª ×”×—×©×‘×•×Ÿ ×”×–×”.
×›×“×™ ×œ×”×ž×©×™×š, × ×“×¨×© ×�×™×ž×•×ª ×™×“× ×™.
×©×œ×— ×�×™×ž×™×™×œ ×œ×›×ª×•×‘×ª recover@telegram.org, ×‘×‘×§×©×” ×œ×‘×˜×œ ×�×ª ×”×—×¡×™×ž×” ×©×œ ×ž×¡×¤×¨ ×”×˜×œ×¤×•×Ÿ %s, ×•×ª×�×¨ ×‘×§×¦×¨×” ×ž×” ×ª×¢×©×” ×¢×� ×ž×¡×¤×¨ ×”×˜×œ×¤×•×Ÿ ×”×–×”.
×œ×�×—×¨ ×ž×›×Ÿ ×”×ª×—×‘×¨ ×©×•×‘.
×�×� ×ž×—×§×ª ×�×ª ×”×—×©×‘×•×Ÿ ×”×–×” ×‘×›×•×•× ×”, ×”×ª×¢×œ×� ×ž×”×•×“×¢×” ×–×•.',
             'already_loggedIn' => '×ž×•×¤×¢ ×–×” ×©×œ MadelineProto ×›×‘×¨ ×ž×—×•×‘×¨!',
             'apiAppDescLabelWeb' => '×ª×™×�×•×¨',
             'apiAppInstructionsAuto0' => '×”×–×Ÿ ×�×ª ×©×� ×”×�×¤×œ×™×§×¦×™×” ×©×œ×š, ×™×›×•×œ ×œ×”×™×•×ª ×›×œ ×©×�: ',
             'apiAppInstructionsAuto1' => '×”×–×Ÿ ×©×� ×§×¦×¨ ×œ×�×¤×œ×™×§×¦×™×” ×©×œ×š, ×�×•×ª×™×•×ª ×‘×�× ×’×œ×™×ª ×‘×œ×‘×“, 5-32 ×ª×•×•×™×�: ',
             'apiAppInstructionsAuto2' => '×”×–×Ÿ ×�×ª ×”×§×™×©×•×¨ ×œ×�×¤×œ×™×§×¦×™×”/×œ×�×ª×¨ ×©×œ×š ×�×• t.me/yourusername: ',
             'apiAppInstructionsAuto3' => '×”×–×Ÿ ×�×ª ×¤×œ×˜×•×¤×¨×ž×ª ×”×�×¤×œ×™×§×¦×™×”: ',
             'apiAppInstructionsAuto4' => '×ª×�×¨ ×�×ª ×”×�×¤×œ×™×§×¦×™×” ×©×œ×š: ',
             'apiAppInstructionsAutoTypeOther' => '×�×—×¨ (×¤×¨×˜ ×‘×ª×™×�×•×¨)',
             'apiAppInstructionsManual0' => '×©×� ×”×�×¤×œ×™×§×¦×™×” ×©×œ×š, ×™×›×•×œ ×œ×”×™×•×ª ×›×œ ×©×�',
             'apiAppInstructionsManual1' => '×©×� ×§×¦×¨ ×œ×�×¤×œ×™×§×¦×™×” ×©×œ×š, ×�×•×ª×™×•×ª ×‘×�× ×’×œ×™×ª ×‘×œ×‘×“, 5-32 ×ª×•×•×™×�',
             'apiAppInstructionsManual2' => '×§×™×©×•×¨ ×œ×�×¤×œ×™×§×¦×™×”/×œ×�×ª×¨ ×©×œ×š ×�×• t.me/yourusername',
             'apiAppInstructionsManual3' => '×”×›×œ',
             'apiAppInstructionsManual4' => '×ª×�×¨ ×�×ª ×”×�×¤×œ×™×§×¦×™×” ×©×œ×š ×¤×”',
             'apiAppShortNameLabelWeb' => '×©×� ×§×¦×¨',
             'apiAppTitleLabelWeb' => '×©×� ×”×�×¤×œ×™×§×¦×™×”',
             'apiAppUrlLabelWeb' => '×›×ª×•×‘×ª URL',
             'apiAppWeb' => '×”×–×Ÿ ×�×ª ×¤×¨×˜×™ ×” API',
             'apiAutoCodeLabelWeb' => '×§×•×“ ×›× ×™×¡×”',
             'apiAutoCodeLeadWeb' => 'my.telegram.org ×©×œ×— ×§×•×“ ×›× ×™×¡×” ×œ×�×¤×œ×™×§×¦×™×™×ª Telegram ×©×œ×š, ×‘×¦\'×�×˜ ×¢×� Telegram (×œ×� ×‘-SMS).',
             'apiAutoCodeTitleWeb' => '×”×–×Ÿ ×�×ª ×”×§×•×“ ×ž-Telegram',
             'apiAutoErrorTitle' => 'my.telegram.org ×”×—×–×™×¨ ×©×’×™×�×”',
             'apiAutoFallbackManual' => '×‘×ž×§×•×� ×–×�×ª, ×”×–×Ÿ ×�×ª ×”-API ID ×•×”-hash ×™×“× ×™×ª.',
             'apiAutoPhoneHintWeb' => 'my.telegram.org ×©×•×œ×— ×�×ª ×§×•×“ ×”×›× ×™×¡×” ×œ×�×¤×œ×™×§×¦×™×™×ª Telegram ×©×œ×š, ×œ×� ×‘-SMS.',
             'apiAutoPrompt0' => '×”×–×Ÿ ×�×ª ×ž×¡×¤×¨ ×”×˜×œ×¤×•×Ÿ ×©×›×‘×¨ ×¨×©×•×� ×‘×˜×œ×’×¨×�: ',
             'apiAutoPrompt1' => '×”×–×Ÿ ×�×ª ×§×•×“ ×”×�×™×ž×•×ª ×©×§×™×‘×œ×ª ×‘×�×¤×œ×™×§×¦×™×ª ×˜×’×œ×¨×�: ',
             'apiAutoWeb' => '×”×–×Ÿ ×�×ª ×ž×¡×¤×¨ ×”×˜×œ×¤×•×Ÿ <b>×©×›×‘×¨ ×¨×©×•×�</b> ×‘×˜×œ×’×¨×� ×›×“×™ ×œ×§×‘×œ ×�×ª ×” API ID',
             'apiChooseAutoWeb' => '×§×‘×œ×” ×�×•×˜×•×ž×˜×™×ª',
             'apiChooseManualAuto' => '×”×�×� ×œ×§×‘×œ ×�×ª ×”-API ID ×•×”-hash ×�×•×˜×•×ž×˜×™×ª ×ž-my.telegram.org (a), ×�×• ×œ×”×–×™×Ÿ ×�×•×ª×� ×™×“× ×™×ª (m)?',
             'apiChooseManualAutoTip' => '×©×™×� ×œ×‘ ×©×�×ª×” ×™×›×•×œ ×’×� ×œ×¡×¤×§ ×�×ª ×ž×–×”×” ×”-API/hash ×™×©×™×¨×•×ª ×‘×§×•×“ ×‘×�×ž×¦×¢×•×ª ×”×”×’×“×¨×•×ª: %s',
             'apiChooseManualAutoTipWeb' => '×©×™×� ×œ×‘ ×©×�×ª×” ×™×›×•×œ ×’×� ×œ×¡×¤×§ ×�×ª ×ž×–×”×” ×”-API/hash ×™×©×™×¨×•×ª ×‘×§×•×“ ×‘×�×ž×¦×¢×•×ª ×”- <a target="_blank" href="%s">×”×’×“×¨×•×ª</a>.',
             'apiChooseManualWeb' => '×›×‘×¨ ×™×© ×œ×™ API ID',
             'apiChoosePrompt' => '×‘×—×™×¨×ª×š (m/a): ',
             'apiCreateErrorTitle' => 'my.telegram.org ×œ×� ×™×¦×¨ ×�×ª ×”×�×¤×œ×™×§×¦×™×”',
             'apiCreateLeadWeb' => '×œ×—×©×‘×•×Ÿ ×”×–×” ×¢×“×™×™×Ÿ ×�×™×Ÿ ×�×¤×œ×™×§×¦×™×” ×‘-my.telegram.org. ×”×¤×¨×˜×™×� ×”×�×œ×” ×ž×•×¦×’×™×� ×¨×§ ×œ×š, ×•×�×¤×©×¨ ×œ×©× ×•×ª ×�×•×ª×� ×‘×”×ž×©×š.',
             'apiCreateTitleWeb' => '×™×¦×™×¨×ª ×”×�×¤×œ×™×§×¦×™×” ×©×œ×š',
             'apiCreateVpsWarning' => 'Telegram ×œ×� ×ž×�×¤×©×¨ ×œ×™×¦×•×¨ ×�×¤×œ×™×§×¦×™×•×ª ×—×“×©×•×ª ×‘-my.telegram.org ×ž×˜×•×•×—×™ IP ×©×œ VPS, VPN ×•×ž×¨×›×–×™ × ×ª×•× ×™×� ×�×—×¨×™×�, ×•×”×‘×§×©×” × ×©×œ×—×” ×ž×”-IP ×©×œ ×”×ž×—×©×‘ ×©×ž×¨×™×¥ ×�×ª MadelineProto. ×¦×•×¨ ×�×ª ×”×�×¤×œ×™×§×¦×™×” ×™×“× ×™×ª ×‘-https://my.telegram.org ×ž×—×™×‘×•×¨ ×‘×™×ª×™ (×œ×ž×©×œ ×ž×”×˜×œ×¤×•×Ÿ ×©×œ×š ×¢×� ×’×œ×™×©×” ×¡×œ×•×œ×¨×™×ª), ×•×�×– ×”×–×Ÿ ×�×ª ×”-API ID ×•×”-hash.',
             'apiCreateWeb' => '×™×¦×™×¨×ª ×�×¤×œ×™×§×¦×™×”',
             'apiError' => '×©×’×™×�×”: %s. × ×¡×” ×©× ×™×ª.',
             'apiInvalidWeb' => '×”-API ID ×”×•×� ×ž×¡×¤×¨, ×•×”-API hash ×‘×�×•×¨×š 32 ×ª×•×•×™×�: ×”×¢×ª×§ ×�×•×ª×� ×©×•×‘ ×ž-my.telegram.org.',
             'apiManualInstructions0' => '×”×ª×—×‘×¨ ×œ https://my.telegram.org',
             'apiManualInstructions1' => '×œ×š ×œ API development tools',
             'apiManualInstructions2' => '×œ×—×¥ ×¢×œ create application',
             'apiManualPrompt0' => '×”×–×Ÿ ×�×ª ×” API ID ×©×œ×š: ',
             'apiManualPrompt1' => '×”×–×Ÿ ×�×ª ×” API Hash ×©×œ×š: ',
             'apiManualWeb' => '×”×–×Ÿ ×�×ª API ID ×• API hash',
             'apiParamsError' => '×œ×� ×¡×™×¤×§×ª ×�×ª ×›×œ ×”×¤×¨×ž×˜×¨×™×� ×”× ×“×¨×©×™×�!',
             'apiSaveWeb' => '×©×ž×™×¨×” ×•×”×ž×©×š',
             'apiSendCodeWeb' => '×©×œ×™×—×ª ×§×•×“',
             'apiSetupLeadWeb' => '×›×œ ×�×¤×œ×™×§×¦×™×™×ª Telegram ×¦×¨×™×›×” API ID ×•-hash. MadelineProto ×™×›×•×œ ×œ×§×‘×œ ×�×•×ª×� ×¢×‘×•×¨×š ×ž-my.telegram.org, ×‘×¢×–×¨×ª ×ž×¡×¤×¨ ×”×˜×œ×¤×•×Ÿ ×©×œ ×—×©×‘×•×Ÿ Telegram ×§×™×™×�.',
             'apiSetupTitleWeb' => '×§×‘×œ×ª API ID ×•-hash',
             'apiStartOverWeb' => '×”×ª×—×œ×” ×ž×—×“×©',
             'api_not_set' => '×¢×œ×™×š ×œ×”×–×™×Ÿ api key ×•- api id ×ž×©×œ×š, ×§×‘×œ ×�×—×“ ×‘×§×™×©×•×¨ @ my.telegram.org',
             'array_invalid' => '×œ×� ×¡×™×¤×§×ª ×ž×¢×¨×š ×—×•×§×™',
             'baseDirLimitation' => '×ž×’×‘×œ×ª ×ª×™×§×™×™×ª ×‘×¡×™×¡ ×ž×•×’×“×¨×ª: ×–×” ×™×›×•×œ ×œ×”×©×¤×™×¢ ×¢×œ ×”×‘×™×¦×•×¢×™×� ×•×œ×’×¨×•×� ×œ×‘×¢×™×•×ª ×ž×¡×•×™×ž×•×ª, ×�× ×� ×”×©×‘×ª ×�×•×ª×” ×�×� ×�×¤×©×¨!',
             'bool_error' => '×œ×� × ×™×ª×Ÿ ×”×™×” ×œ×—×œ×¥ ×‘×•×œ×™×�× ×™',
             'botAlreadyRunning' => '×”×‘×•×˜ ×›×‘×¨ ×¤×•×¢×œ!',
             'botapi_conversion_error' => '×œ×� × ×™×ª×Ÿ ×œ×”×ž×™×¨ ×�×ª %s ×œ×�×•×‘×™×™×§×˜ bot API',
             'call_already_accepted' => '×©×™×—×” %s ×›×‘×¨ ×”×ª×§×‘×œ×”',
             'call_already_declined' => '×©×™×—×” %s ×›×‘×¨ × ×“×—×ª×”',
             'call_completing' => '×ž×©×œ×™×� ×©×™×—×” ×ž %s...',
             'call_confirming' => '×�×™×©×•×¨ ×©×™×—×” ×ž %s...',
             'call_discarding' => '×‘×™×˜×•×œ ×©×™×—×” %s...',
             'call_error_1' => '×œ×� × ×™×ª×Ÿ ×”×™×” ×œ×ž×¦×•×� ×•×œ×§×‘×œ ×�×ª ×”×©×™×—×” %s',
             'call_error_2' => '×œ×� × ×™×ª×Ÿ ×”×™×” ×œ×ž×¦×•×� ×•×œ×�×©×¨ ×�×ª ×”×©×™×—×” %s',
             'call_error_3' => '×œ×� × ×™×ª×Ÿ ×”×™×” ×œ×ž×¦×•×� ×•×œ×”×©×œ×™×� ×�×ª ×”×©×™×—×” %s',
             'cli_need_dl.php_link' => '×�× ×� ×¦×™×™×Ÿ ×›×ª×•×‘×ª ×�×ª×¨ ×©×œ ×¡×§×¨×™×¤×˜ ×”×•×¨×“×” ×‘×¢×ª ×©×™×ž×•×© ×‘-getDownloadLink ×“×¨×š CLI!',
             'constructor_not_found' => '×”×‘× ×�×™ ×œ×� × ×ž×¦×� ×œ×¡×•×’: ',
             'could_not_connect_to_MadelineProto' => '×œ×� × ×™×ª×Ÿ ×œ×”×ª×—×‘×¨ ×œ-MadelineProto, ×�× ×� ×”×¤×¢×œ ×�×ª proc_open ×•×”×¡×¨ ×”×’×‘×œ×•×ª open_basedir ×�×• ×”×©×‘×ª ×©×›×ª×•×‘ ×©×œ × ×ª×™×‘ ×©×¨×ª ×�×™× ×˜×¨× ×˜ ×›×“×™ ×œ×ª×§×Ÿ! ×�×� ×›×‘×¨ ×¢×©×™×ª ×–×�×ª, ×•×“×� ×©×’×¨×¡×ª ×”-CLI ×©×œ PHP ×–×”×” ×‘×“×™×•×§ ×œ×’×¨×¡×ª ×”×�×™× ×˜×¨× ×˜ (×�×•×ª×” ×’×¨×¡×”, ×”×¨×—×‘×•×ª ×•×›×•\') ×•×¢×™×™×Ÿ ×‘×§×•×‘×¥ MadelineProto.log ×œ×§×‘×œ×ª ×ž×™×“×¢ × ×•×¡×£ ×¢×œ ×”×©×’×™×�×” ×©×ž× ×¢×” ×ž×©×¨×ª ×”-IPC ×œ×”×ª×—×™×œ.',
             'could_not_convert_object' => '×œ×� × ×™×ª×Ÿ ×œ×”×ž×™×¨ ×�×•×‘×™×™×§×˜ ×ž×¡×•×’ %s',
             'deserialization_error' => '×�×™×¨×¢×” ×©×’×™×�×” ×‘×¢×ª ×“×”-×¡×™×“×¨×™×�×œ×™×–×¦×™×”',
             'dl.php_check_logs_make_sure_session_running' => '×”×‘×•×˜ ×©×œ MadelineProto EventHandler ×�×• ×©×¨×ª ×”-IPC ×©×œ MadelineProto ×�×™× ×� ×ž×§×•×•× ×™×�, ×�× ×� ×‘×“×•×§ ×™×•×ž× ×™×� ×•×•×“×� ×©×œ×¤×—×•×ª ×�×—×“ ×ž×”×� ×¤×•×¢×œ!',
             'dl.php_powered_by_madelineproto' => '×©×¨×ª ×”×•×¨×“×ª ×§×‘×¦×™ Telegram (×¢×“ 4GB), ×ž×•×¤×¢×œ ×¢×œ ×™×“×™ <a href="https://docs.madelineproto.xyz" target="_blank">MadelineProto</a>!<br>Click <a href="https://docs.madelineproto.xyz/docs/FILES.html#getting-a-download-link" target="_blank">here</a> ×œ×ž×™×“×¢ × ×•×¡×£ ×¢×œ ×�×™×š ×œ×”×’×“×™×¨ ×©×¨×ª ×”×•×¨×“×ª ×§×‘×¦×™×� ×ž×©×œ×š ×‘×˜×œ×’×¨×�!',
             'do_not_delete_MadelineProto.log' => '×�×¡×•×¨ ×œ×ž×—×•×§ ×�×ª ×”×§×•×‘×¥ MadelineProto.log, ×�× ×� ×”×’×“×¨ ×’×•×“×œ ×ž×§×¡×™×ž×œ×™ ×ž×•×ª×�×� ×�×™×©×™×ª ×‘×”×’×“×¨×•×ª, ×‘×ž×§×•×� ×–×�×ª!',
             'do_not_remove_MadelineProto.log_phar' => '× ×� ×œ×� ×œ×”×¡×™×¨ madeline.phar ×�×• madeline.php, ×�×—×¨×ª MadelineProto ×ª×§×¨×•×¡. ×�×� ×™×© ×œ×š ×‘×¢×™×” ×›×œ×©×”×™ ×¢×� MadelineProto, ×“×•×•×— ×¢×œ ×–×” ×�×œ https://github.com/danog/MadelineProto ×�×• https://t.me/pwrtelegramgroup',
             'do_not_use_blocking_class' => '×ž×˜×¢×ž×™ ×‘×™×¦×•×¢×™×�, ×ž×˜×¤×œ×™×� ×�×™× ×� ×¨×©×�×™×� ×œ×”×©×ª×ž×© ×‘×ž×—×œ×§×” ×”×—×•×¡×ž×ª ×œ×œ×� ×�×¡×™× ×›×¨×•×Ÿ %s, ×�× ×� ×”×©×ª×ž×© ×‘-%s, ×‘×ž×§×•×� ×–×�×ª',
             'do_not_use_blocking_function' => '×ž×˜×¢×ž×™ ×‘×™×¦×•×¢×™×�, ×™×™×ª×›×Ÿ ×©×ž×˜×¤×œ×™ ×�×™×¨×•×¢×™×� ×œ×� ×™×©×ª×ž×©×• ×‘×¤×•× ×§×¦×™×™×ª ×”×—×¡×™×ž×” ×”×œ×�-×�×¡×™× ×›×¨×•× ×™×ª %s, ×�× ×� ×”×©×ª×ž×© ×‘-%s, ×‘×ž×§×•×� ×–×�×ª',
             'do_not_use_deprecated_function' => '×”×¤×•× ×§×¦×™×” %s ×”×•×¦×�×” ×ž×©×™×ž×•×©, ×�× ×� ×”×©×ª×ž×© ×‘-%s ×‘×ž×§×•×� ×–×�×ª',
             'do_not_use_non_root_require_in_event_handler' => '×ž×¡×™×‘×•×ª ×‘×™×¦×•×¢×™×�, ×�×¡×•×¨ ×œ×”×©×ª×ž×© ×‘-require ×�×• ×œ×›×œ×•×œ ×‘×ª×•×š ×ž×—×œ×§×” ×©×œ ×ž×˜×¤×œ ×‘×�×™×¨×•×¢×™×�, ×¨×§ ×“×¨×™×©×•×ª ×‘×¨×ž×ª ×”×©×•×¨×© ×ž×•×ª×¨×•×ª.',
             'do_not_use_yield' => 'MadelineProto 8 ×�×™× ×• ×“×•×¨×© ×�×• ×ª×•×ž×š ×‘×©×™×ž×•×© ×‘×ª×©×•×�×” ×‘×¤×•× ×§×¦×™×•×ª ×�×¡×™× ×›×¨×•×Ÿ, ×¢×œ×™×š ×œ×”×¡×™×¨ ×�×ª ×›×œ ×ž×™×œ×•×ª ×”×ž×¤×ª×— ×©×œ ×ª×©×•×�×” ×©×©×™×ž×©×• ×‘×¢×‘×¨ ×¢×‘×•×¨ ×§×¨×™×�×•×ª ×¤×•× ×§×¦×™×•×ª ×�×¡×™× ×›×¨×•×Ÿ',
             'done' => '×‘×•×¦×¢!',
             'encode_double_error' => '×œ×� × ×™×ª×Ÿ ×”×™×” ×œ×§×•×“×“ ×›×¤×•×œ ×›×¨×�×•×™',
             'extensionRecommended' => '×�×–×”×¨×”: ×”×ª×•×¡×£ %s ×�×™× ×• ×ž×•×ª×§×Ÿ, ×�× ×� ×”×ª×§×Ÿ ×�×•×ª×• ×›×“×™ ×œ×”×�×™×¥ ×�×ª MadelineProto!',
             'extensionRequired' => 'MadelineProto ×“×•×¨×©×ª ×�×ª ×”×ª×•×¡×£ %s ×›×“×™ ×œ×¤×¢×•×œ. %s',
             'extensionRequiredInstallWithApt' => '× ×¡×” ×œ×”×¤×¢×™×œ ×�×ª sudo apt-get install %s.',
             'extensionRequiredInstallWithCustomInstructions' => '×¢×§×•×‘ ×�×—×¨ ×”×”×•×¨×�×•×ª ×‘-%s ×›×“×™ ×œ×”×ª×§×™×Ÿ ×�×•×ª×•.',
             'file_not_exist' => '×§×•×‘×¥ ×œ×� ×§×™×™×�',
             'file_parsing' => '×ž×ž×™×¨ %s...',
             'file_type_invalid' => '×–×•×”×” ×¡×•×’ ×§×•×‘×¥ ×œ×� ×—×•×§×™ (%s)',
             'fingerprint_invalid' => '×˜×‘×™×¢×ª ×�×¦×‘×¢ ×œ×� ×—×•×§×™×ª ×©×œ ×ž×¤×ª×—!',
             'go' => '×§×“×™×ž×”',
             'invalid_dl.php_session' => '%s ×�×™× ×• ×¡×§×¨×™×¤×˜ ×”×•×¨×“×” ×—×•×§×™ ×ž×›×™×•×•×Ÿ ×©×ž×–×”×” ×”×”×¤×¢×œ×” ×©×œ×• ×©×•× ×” (×¦×¤×™ %s, ×§×™×‘×œ %s)',
             'ipc_tcp_warning' => 'UNIX sockets are not available, so the IPC server of the session listens on TCP (127.0.0.1) instead: any program running on this machine can connect to it, and control the session. On Windows 10 1803+, enable the sockets extension (extension=sockets in php.ini) to use UNIX sockets.',
             'length_too_big' => '×”×�×•×¨×š ×’×“×•×œ ×ž×“×™',
             'loginBot' => '×”×–×Ÿ ×�×ª ×”×˜×•×§×Ÿ ×©×œ ×”×‘×•×˜ ×©×œ×š: ',
             'loginBotTokenHintWeb' => '×”×˜×•×§×Ÿ ×©-@BotFather × ×ª×Ÿ ×œ×š ×›×©×™×¦×¨×ª ×�×ª ×”×‘×•×˜.',
             'loginBotTokenWeb' => '×˜×•×§×Ÿ ×”×‘×•×˜',
             'loginChoosePromptWeb' => '×”×�×� ×‘×¨×¦×•× ×š ×œ×”×ª×—×‘×¨ ×›×ž×©×ª×ž×© ×�×• ×›×‘×•×˜?',
             'loginCodeLeadWeb' => 'Telegram ×©×œ×— ×§×•×“ ×›× ×™×¡×” ×œ×�×¤×œ×™×§×¦×™×™×ª Telegram ×©×œ×š, ×�×• ×‘-SMS ×�×� ×�×™× ×š ×ž×—×•×‘×¨ ×‘×©×•×� ×ž×§×•×� ×�×—×¨.',
             'loginCodeTitleWeb' => '×”×–×Ÿ ×�×ª ×§×•×“ ×”×›× ×™×¡×”',
             'loginLeadWeb' => '×‘×—×¨ ×�×ª ×”×—×©×‘×•×Ÿ ×�×• ×”×‘×•×˜ ×©×‘×©×ž×• ×™×¤×¢×œ ×ž×•×¤×¢ MadelineProto ×”×–×”.',
             'loginManual' => '×œ×—×œ×•×¤×™×Ÿ, ×�×ª×” ×™×›×•×œ ×’×� ×œ×”×–×™×Ÿ ×˜×•×¨×Ÿ ×©×œ ×‘×•×˜ ×�×• ×ž×¡×¤×¨ ×˜×œ×¤×•×Ÿ ×›×“×™ ×œ×”×ª×—×‘×¨ ×‘×�×•×¤×Ÿ ×™×“× ×™: ',
             'loginNoCode' => '×œ×� ×”×–× ×ª ×§×•×“ ×˜×œ×¤×•×Ÿ!',
             'loginNoName' => '×œ×� ×”×–× ×”×ª ×�×ª ×”×©×� ×”×¤×¨×˜×™!',
             'loginNoPass' => '×œ×� ×”×–× ×ª ×�×ª ×”×¡×™×¡×ž×”!',
             'loginOptionBot' => '×‘×•×˜',
             'loginOptionUser' => '×ž×©×ª×ž×©',
             'loginPasswordLabelWeb' => '×¡×™×¡×ž×”',
             'loginPasswordLeadWeb' => '×”×—×©×‘×•×Ÿ ×”×–×” ×ž×•×’×Ÿ ×‘×¡×™×¡×ž×”, ×©×”×•×’×“×¨×” ×‘×”×’×“×¨×•×ª > ×¤×¨×˜×™×•×ª ×•×�×‘×˜×—×” > ×�×™×ž×•×ª ×“×•-×©×œ×‘×™.',
             'loginPasswordTitleWeb' => '×�×™×ž×•×ª ×“×•-×©×œ×‘×™',
             'loginQr' => '×¤×ª×— ×�×ª Telegram ×‘×˜×œ×¤×•×Ÿ ×©×œ×š, ×¢×‘×•×¨ ×�×œ ×”×’×“×¨×•×ª > ×ž×›×©×™×¨×™×� > ×§×™×©×•×¨ ×ž×›×©×™×¨ ×©×•×œ×—×Ÿ ×¢×‘×•×“×” ×•×¡×¨×•×§ ×�×ª ×§×•×“ ×”-QR ×©×œ×ž×¢×œ×” ×›×“×™ ×œ×”×ª×—×‘×¨ ×�×•×˜×•×ž×˜×™×ª.',
             'loginQrCodeExpired' => '×¤×’ ×ª×•×§×¤×• ×©×œ ×§×•×“ ×”-QR, ×ª×™×¦×¨×• ×§×•×“ ×—×“×©...',
             'loginQrCodeSuccessful' => '×”×ª×—×‘×¨×•×ª ×“×¨×š ×§×•×“ QR ×‘×•×¦×¢×” ×‘×”×¦×œ×—×”!',
             'loginTitleWeb' => '×›× ×™×¡×” ×œ-Telegram',
             'loginTypeWeb' => '×›× ×™×¡×” ×‘×ª×•×¨',
             'loginUser' => '×”×–×Ÿ ×�×ª ×ž×¡×¤×¨ ×”×˜×œ×¤×•×Ÿ ×©×œ×š: ',
             'loginUserCode' => '×”×–×Ÿ ×�×ª ×”×§×•×“: ',
             'loginUserPass' => '×”×–×Ÿ ×�×ª ×”×¡×™×¡×ž×� ×©×œ×š (×¨×ž×– %s): ',
             'loginUserPassHint' => '×¨×ž×–: %s',
             'loginUserPassWeb' => '×”×–×Ÿ ×�×ª ×”×¡×™×¡×ž×� ×©×œ×š: ',
             'loginUserPhoneCodeWeb' => '×§×•×“',
             'loginUserPhoneHintWeb' => '×¢×� ×§×™×“×•×ž×ª ×”×ž×“×™× ×”, ×œ×ž×©×œ +39 123 456 7890.',
             'loginUserPhoneWeb' => '×ž×¡×¤×¨ ×˜×œ×¤×•×Ÿ',
             'loginWebQr' => '×�×ª×” ×™×›×•×œ ×’×� ×œ×”×ª×—×‘×¨ ×�×•×˜×•×ž×˜×™×ª ×¢×œ ×™×“×™ ×¤×ª×™×—×ª Telegram ×‘×˜×œ×¤×•×Ÿ ×©×œ×š, ×ž×¢×‘×¨ ×œ×”×’×“×¨×•×ª > ×ž×›×©×™×¨×™×� > ×§×™×©×•×¨ ×ž×›×©×™×¨ ×©×•×œ×—×Ÿ ×¢×‘×•×“×” ×•×¡×¨×™×§×ª ×§×•×“ ×”-QR ×”×‘×�:',
             'loginWebQr1' => '×¤×ª×— ×�×ª ×˜×œ×’×¨×� ×‘×˜×œ×¤×•×Ÿ ×©×œ×š',
             'loginWebQr2' => '×¢×‘×•×¨ ×�×œ ×”×’×“×¨×•×ª > ×ž×›×©×™×¨×™×� > ×§×™×©×•×¨ ×ž×›×©×™×¨ ×©×•×œ×—×Ÿ ×¢×‘×•×“×”',
             'loginWebQr3' => '×›×•×•×Ÿ ×�×ª ×”×˜×œ×¤×•×Ÿ ×©×œ×š ×œ×ž×¡×š ×–×” ×›×“×™ ×œ×�×©×¨ ×�×ª ×”×›× ×™×¡×”',
             'login_2fa_enabled' => '×�×™×ž×•×ª ×“×• ×©×œ×‘×™ ×ž×•×¤×¢×œ, ×¢×œ×™×š ×œ×”×©×ª×ž×© ×‘×¤×•× ×§×¦×™×™×ª complete2falogin...',
             'login_auth_key' => '×ž×ª×—×‘×¨ ×‘×�×ž×¦×¢×•×ª ×ž×¤×ª×— ×�×™×©×•×¨...',
             'login_bot' => '×ž×ª×—×‘×¨ ×›×‘×•×˜...',
             'login_code_sending' => '×©×•×œ×— ×§×•×“...',
             'login_code_sent' => '×”×§×•×“ × ×©×œ×— ×‘×”×¦×œ×—×”! ×œ×�×—×¨ ×©×ª×§×‘×œ ×�×ª ×”×§×•×“, ×¢×œ×™×š ×œ×”×©×ª×ž×© ×‘×¤×•× ×§×¦×™×™×ª completePhoneLogin.',
             'login_code_uncalled' => '×�× ×™ ×œ×� ×ž×—×›×” ×œ×§×•×“! ×¢×œ×™×š ×œ×”×©×ª×ž×© ×‘×¤×•× ×§×¦×™×™×ª phoneLogin ×§×•×“×�',
             'login_need_signup' => '×œ×� × ×•×¦×¨ ×—×©×‘×•×Ÿ ×¢×‘×•×¨ ×ž×¡×¤×¨ ×–×”, ×¢×œ×™×š ×œ×”×©×ª×ž×© ×‘×¤×•× ×§×¦×™×™×ª completeSignup...',
             'login_ok' => '×”×ª×—×‘×¨×ª ×‘×”×¦×œ×—×”!',
             'login_user' => '×ž×ª×—×‘×¨ ×›×ž×©×ª×ž×© ×¨×’×™×œ...',
             'logout_ok' => '×”×ª× ×ª×§ ×‘×”×¦×œ×—×”!',
             'long_not_16' => '×”×¢×¨×š ×”× ×ª×•×Ÿ ×�×™× ×• ×‘×�×•×¨×š 16 ×‘×ª×™×�',
             'long_not_32' => '×”×¢×¨×š ×”× ×ª×•×Ÿ ×�×™× ×• ×‘×�×•×¨×š 32 ×‘×ª×™×�',
             'long_not_64' => '×”×¢×¨×š ×”× ×ª×•×Ÿ ×�×™× ×• ×‘×�×•×¨×š 64 ×‘×ª×™×�',
             'madelineproto_ready' => 'MadelineProto ×ž×•×›×Ÿ!',
             'manualAdminActionRequired' => '!!!!!!!!! × ×“×¨×©×ª ×¤×¢×•×œ×” ×™×“× ×™×ª ×©×œ ×ž× ×”×œ ×ž×¢×¨×›×ª !!!!!!!!!',
             'method_not_found' => '×”×¤×•× ×§×¦×™×” ×œ×� × ×ž×¦×�: ',
             'mmapErrorPart1' => '×”×ž×¡×¤×¨ ×”×ž×¨×‘×™ ×©×œ ×�×–×•×¨×™×� ×ž×ž×•×¤×™ ×–×™×›×¨×•×Ÿ (mmap) ×”×•×©×’ (%s): ×”×’×“×œ ×�×ª ×ª×¦×•×¨×ª ×œ×™×‘×ª vm.max_map_count ×œ-262144 ×›×“×™ ×œ×ª×§×Ÿ.',
             'mmapErrorPart2' => '×›×“×™ ×œ×ª×§×Ÿ, ×”×¤×¢×œ ×�×ª ×”×¤×§×•×“×” ×”×‘×�×”: %s (×‘×ª×•×¨ root)',
             'mmapErrorPart3' => '×›×“×™ ×œ×”×—×™×œ ×�×ª ×”×©×™× ×•×™ ×œ×�×—×¨ ×�×ª×—×•×œ ×ž×—×“×©: %s',
             'mmapErrorPart4' => '×‘-Windows ×•×‘-WSL, ×”×’×“×œ×ª ×’×•×“×œ ×§×•×‘×¥ ×”×“×£ ×¢×©×•×™×” ×œ×¢×–×•×¨; ×�× ×� ×¢×‘×•×¨ ×œ×œ×™× ×•×§×¡ ×ž×§×•×¨×™×ª ×�×� ×”×‘×¢×™×” × ×ž×©×›×ª.',
             'must_have_declare_types' => '×ž×¡×™×‘×•×ª ×‘×™×¦×•×¢×™×�, ×”×”×¦×”×¨×” ×”×¨×�×©×•× ×” ×©×œ ×§×•×‘×¥ ×ž×˜×¤×œ ×‘×�×™×¨×•×¢ ×—×™×™×‘×ª ×œ×”×™×•×ª "declare(strict_types=1);"',
             'nearest_dc' => '×�× ×—× ×• ×‘ %s, ×—×•×•×ª ×©×¨×ª×™ ×” DC ×”×§×¨×•×‘×” ×”×™×� %d.',
             'need_dl.php' => '×œ×� × ×™×ª×Ÿ ×œ×™×¦×•×¨ ×¡×§×¨×™×¤×˜ ×”×•×¨×“×” ×›×‘×¨×™×¨×ª ×ž×—×“×œ (%s), ×�× ×� ×¦×•×¨ ×§×•×‘×¥ dl.php ×¢×� ×”×ª×•×›×Ÿ ×”×‘×�: %s ×•×”×¢×‘×™×¨ ×�×ª ×›×ª×•×‘×ª ×”×�×ª×¨ ×©×œ×• ×œ×¤×¨×ž×˜×¨ ×”×©× ×™ ×©×œ getDownloadLink',
             'noReportPeers' => '×�×–×”×¨×”: ×œ×� ×ž×•×’×“×¨×™×� ×¢×ž×™×ª×™×� ×œ×“×™×•×•×—, ×�× ×� ×”×•×¡×£ ×�×ª ×”×©×™×˜×” ×”×‘×�×” ×œ×ž×˜×¤×œ ×‘×�×™×¨×•×¢×™×� ×©×œ×š',
             'non_text_conversion' => '×¢×“×™×™×Ÿ ×œ×� × ×™×ª×Ÿ ×œ×”×ž×™×¨ ×”×•×“×¢×•×ª ×©×�×™× ×Ÿ ×˜×§×¡×˜!',
             'not_loggedIn' => '×�× ×™ ×œ×� ×ž×—×•×‘×¨!',
             'not_numeric' => '×”×¢×¨×š ×”× ×ª×•×Ÿ ×�×™× ×• ×ž×¡×¤×¨×™',
             'params_missing' => '×—×¡×¨ ×¤×¨×ž×˜×¨ × ×“×¨×©',
             'peer_not_in_db' => '×¢×ž×™×ª ×–×” ×œ×� ×§×™×™×� ×‘×ž×¡×“ ×”× ×ª×•× ×™×� ×”×¤× ×™×ž×™ ×©×œ ×¢×ž×™×ª×™×�',
             'plugin_path_does_not_exist' => '× ×ª×™×‘ ×”×ª×•×¡×£ %s ×�×™× ×• ×§×™×™×�!',
             'plugins_do_not_use_require' => '×ž×˜×¢×ž×™ ×‘×™×¦×•×¢×™×�, ×ª×•×¡×¤×™×� ×™×›×•×œ×™×� ×œ×›×œ×•×œ ×�×• ×œ×“×¨×•×© ×�×•×˜×•×ž×˜×™×ª ×§×‘×¦×™×� ×�×—×¨×™×� ×”× ×ž×¦×�×™×� ×‘×ª×™×§×™×™×ª ×”×ª×•×¡×¤×™×� ×¢×œ ×™×“×™ ×”×¤×¢×œ×ª ×”×˜×¢×™× ×” ×”×�×•×˜×•×ž×˜×™×ª ×©×œ PSR-4 (×œ×� ×¢×œ ×™×“×™ ×”×¤×¢×œ×ª×� ×™×“× ×™×ª ×©×œ require()\'ing them).',
             'plugins_must_have_exactly_one_class' => '×ª×•×¡×£ ×—×™×™×‘ ×œ×”×’×“×™×¨ ×‘×“×™×•×§ ×ž×—×œ×§×” ×�×—×ª! ×›×“×™ ×œ×”×’×“×™×¨ ×ž×¡×¤×¨ ×ž×—×œ×§×•×ª, ×ž×ž×©×§×™×� ×�×• ×ª×›×•× ×•×ª, ×¦×•×¨ ×§×‘×¦×™×� × ×¤×¨×“×™×�, ×”×� ×™×™×˜×¢× ×• ×�×•×˜×•×ž×˜×™×ª ×¢×œ ×™×“×™ MadelineProto ×‘×�×•×¤×Ÿ ×�×•×˜×•×ž×˜×™.',
             'predicate_not_set' => '×¤×¨×“×™×§×˜ (×¢×¨×š ×ª×—×ª _) ×œ×� ×”×•×’×“×¨!',
             'recommend_not_use_filesystem_function' => '×”×©×™×ž×•×© ×‘×¤×•× ×§×¦×™×” %s ×�×™× ×• ×ž×•×ž×œ×¥, ×ž×›×™×•×•×Ÿ ×©×’×™×©×” ×œ×ž×¢×¨×›×ª ×”×§×‘×¦×™×� ×‘×ž×”×œ×š ×˜×™×¤×•×œ ×‘×¢×“×›×•× ×™×� ×ª×�×˜ ×�×ª ×”×‘×•×˜ ×©×œ×š, ×�× ×� ×¨×�×” https://docs.madelineproto.xyz/docs/UPDATES.html#avoiding-the-use-of-filesystem-functions ×¢×‘×•×¨ ×¨×©×™×ž×” ×©×œ ×“×¨×›×™×� ×—×œ×•×¤×™×•×ª ×œ×�×—×¡×•×Ÿ × ×ª×•× ×™×� ×©×œ×� ×™×�×˜ ×�×ª ×”×‘×•×˜ ×©×œ×š!',
             'rpc_tg_error' => '×˜×œ×’×¨×� ×”×—×–×™×¨×” ×©×’×™×�×ª RPC: %s (%s), × ×ª×¤×¡ ×¢"×™ %s:%s%s×ž×¢×§×‘ TL:',
             'sec_peer_not_in_db' => '×¢×ž×™×ª ×¡×•×“×™ ×–×” ×�×™× ×• ×§×™×™×� ×‘×ž×¡×“ ×”× ×ª×•× ×™×� ×”×¤× ×™×ž×™ ×©×œ ×¢×ž×™×ª×™×�',
             'secret_chat_skipping' => '×�×™×Ÿ ×œ×™ ×�×ª ×”×¦\'×�×˜ ×”×¡×•×“×™ %s ×‘×ž×¡×“ ×”× ×ª×•× ×™×�, ×“×™×œ×•×’ ×¢×œ ×”×•×“×¢×”...',
             'serialization_ofd' => '×”×¡×™×“×¨×” ×œ×� ×ž×¢×•×“×›× ×ª, ×ž×©×—×–×¨×ª ×�×•×‘×™×™×§×˜!',
             'session_corrupted' => '×”×¡×©×Ÿ ×¤×’×•×�!',
             'signing_up' => '× ×¨×©×� ×›×ž×©×ª×ž×© ×¨×’×™×œ...',
             'signupFirstName' => '×”×–×Ÿ ×�×ª ×”×©×� ×”×¤×¨×˜×™ ×©×œ×š: ',
             'signupFirstNameWeb' => '×©×� ×¤×¨×˜×™',
             'signupLastName' => '×”×–×Ÿ ×�×ª ×©×� ×”×ž×©×¤×—×” ×©×œ×š (×™×›×•×œ ×œ×”×™×•×ª ×¨×™×§): ',
             'signupLastNameWeb' => '×©×� ×ž×©×¤×—×”',
             'signupLeadWeb' => '×ž×¡×¤×¨ ×”×˜×œ×¤×•×Ÿ ×”×–×” ×¢×“×™×™×Ÿ ×œ×� ×¨×©×•×� ×‘-Telegram: ×‘×—×¨ ×�×ª ×”×©×� ×©×œ ×”×—×©×‘×•×Ÿ ×”×—×“×©.',
             'signupTitleWeb' => '×™×¦×™×¨×ª ×”×—×©×‘×•×Ÿ ×©×œ×š',
             'signupWeb' => '×”×™×¨×©×� ×‘×‘×§×©×”',
             'signup_ok' => '× ×¨×©×ž×ª ×‘×”×¦×œ×—×”!',
             'signup_uncalled' => '×�× ×™ ×œ×� ×ž×—×›×” ×œ×”×™×¨×©×�! ×¢×œ×™×š ×œ×”×©×ª×ž×© ×‘×¤×•× ×§×¦×™×•×ª phoneLogin ×•- completePhoneLogin ×§×•×“×�!',
             'src_file_invalid' => '×¡×•×¤×§ ×§×•×‘×¥ ×ž×§×•×¨ ×œ×� ×—×•×§×™: ',
             'static_analysis_minor' => '× ×ª×§×œ×” ×‘×‘×¢×™×” ×§×œ×” ×‘×ž×”×œ×š × ×™×ª×•×— ×¡×˜×˜×™ ×©×œ %s: %s',
             'static_analysis_severe' => '× ×ª×§×œ×” ×‘×‘×¢×™×” ×—×ž×•×¨×” ×‘×ž×”×œ×š × ×™×ª×•×— ×¡×˜×˜×™ ×©×œ %s: %s',
             'stream_handle_invalid' => '×¡×•×¤×§ stream handle ×œ×� ×—×•×§×™.',
             'string_required' => '×”×ž×—×¨×•×–×ª ×”×™×™×ª×” ×¦×¤×•×™×”!',
             'translate_madelineproto_cli' => '× ×™×ª×Ÿ ×œ×ª×¨×’×� ×�×ª MadelineProto ×‘×©×¤×” ×©×œ×š (×”×ª×§×“×ž×•×ª ×”×ª×¨×’×•×� ×”× ×•×›×—×™×ª: %d%%), ×¢×‘×•×¨ ×�×œ https://github.com/danog/MadelineProto/tree/v8/langs ×›×“×™ ×œ×ª×¨×•×� ×¢×� ×”×ª×¨×’×•×�!',
             'translate_madelineproto_web' => '× ×™×ª×Ÿ ×œ×ª×¨×’×� ×�×ª MadelineProto ×‘×©×¤×” ×©×œ×š (×”×ª×§×“×ž×•×ª ×”×ª×¨×’×•×� ×”× ×•×›×—×™×ª: %d%%), ×œ×—×¥ <a href="https://github.com/danog/MadelineProto/tree/v8/langs" target="_blank">×›×�×Ÿ ×›×“×™ ×œ×ª×¨×•×� ×¢×� ×”×ª×¨×’×•×�!</a>',
             'tweb.ACCESS_TOKEN_EXPIRED' => '×˜×•×§×Ÿ ×”×‘×•×˜ ×”×–×” ×‘×•×˜×œ',
             'tweb.ACCESS_TOKEN_INVALID' => '×˜×•×§×Ÿ ×‘×•×˜ ×œ×� ×ª×§×™×Ÿ',
             'tweb.AccDescr.LoginCode' => '×§×•×“ ×›× ×™×¡×”',
             'tweb.Close' => '×¡×’×™×¨×”',
             'tweb.Code' => '×§×•×“',
             'tweb.Contacts.PhoneNumber.Placeholder' => '×ž×¡×¤×¨ ×˜×œ×¤×•×Ÿ',
             'tweb.Country' => '×ž×“×™× ×”',
             'tweb.DarkMode' => '×ž×¦×‘ ×›×”×”',
             'tweb.Error.AnError' => '×�×™×¨×¢×” ×©×’×™×�×”. × ×� ×œ× ×¡×•×ª ×©×•×‘ ×ž×�×•×—×¨ ×™×•×ª×¨.',
             'tweb.FIRSTNAME_INVALID' => '×©×� ×¤×¨×˜×™ ×œ×� ×ª×§×™×Ÿ',
             'tweb.FirstName' => '×©×� ×¤×¨×˜×™ (×—×•×‘×”)',
             'tweb.LASTNAME_INVALID' => '×©×� ×ž×©×¤×—×” ×œ×� ×ª×§×™×Ÿ',
             'tweb.LastName' => '×©×� ×ž×©×¤×—×” (×�×•×¤×¦×™×•× ×œ×™)',
             'tweb.Login.Bot' => '×›× ×™×¡×” ×‘×ª×•×¨ ×‘×•×˜ >',
             'tweb.Login.Bot.Cancel' => '×›× ×™×¡×” ×‘×ª×•×¨ ×ž×©×ª×ž×© >',
             'tweb.Login.Bot.Subtitle' => '×”×–×Ÿ ×�×ª ×”×˜×•×§×Ÿ ×©-@BotFather × ×ª×Ÿ ×œ×š
×›×©×™×¦×¨×ª ×�×ª ×”×‘×•×˜.',
             'tweb.Login.Bot.Title' => '×›× ×™×¡×” ×‘×ª×•×¨ ×‘×•×˜',
             'tweb.Login.Bot.Token' => '×˜×•×§×Ÿ ×”×‘×•×˜',
             'tweb.Login.Code.SecretPhrase' => '×‘×™×˜×•×™ ×¡×•×“×™',
             'tweb.Login.Code.SecretWord' => '×ž×™×œ×” ×¡×•×“×™×ª',
             'tweb.Login.Code.SentCall' => '× ×ª×§×©×¨ ×�×œ×™×š ×•× ×§×¨×™×� ×œ×š
×�×ª ×”×§×•×“.',
             'tweb.Login.Code.SentEmail' => '× ×� ×œ×‘×“×•×§ ×�×ª ×”×�×™×ž×™×™×œ ×©×œ×š **%s** (×�×œ ×ª×©×›×— ×�×ª ×ª×™×§×™×™×ª ×”×¡×¤×�×�) ×•×œ×”×–×™×Ÿ ×�×ª ×”×§×•×“ ×©×©×œ×—× ×• ×œ×š ×–×” ×¢×ª×”.',
             'tweb.Login.Code.SentInApp' => '×©×œ×—× ×• ×œ×š ×”×•×“×¢×” ×‘-Telegram
×¢×� ×”×§×•×“.',
             'tweb.Login.Code.SentMissedCall' => '×‘×©× ×™×•×ª ×”×§×¨×•×‘×•×ª ×ª×§×‘×œ
×©×™×—×” ×§×¦×¨×” ×ž-**%s**. ×�×œ ×ª×¢× ×” ×œ×” â€”
×¤×©×•×˜ ×”×–×Ÿ ×�×ª ×”×¡×¤×¨×•×ª ×”×�×—×¨×•× ×•×ª ×©×œ ×”×ž×¡×¤×¨ ×”×–×”.',
             'tweb.Login.Code.SentSms' => '×©×œ×—× ×• ×œ×š SMS
×¢×� ×”×§×•×“.',
             'tweb.Login.Code.SentSmsPhrase' => '×©×œ×—× ×• SMS ×¢×� ×‘×™×˜×•×™ ×¡×•×“×™
×œ×˜×œ×¤×•×Ÿ ×©×œ×š **%s**.',
             'tweb.Login.Code.SentSmsPhraseBeginning' => '×©×œ×—× ×• SMS ×¢×� ×‘×™×˜×•×™ ×¡×•×“×™ ×©×ž×ª×—×™×œ
×‘-"**%2$s**" ×œ×˜×œ×¤×•×Ÿ ×©×œ×š **%1$s**.',
             'tweb.Login.Code.SentSmsWord' => '×©×œ×—× ×• SMS ×¢×� ×ž×™×œ×” ×¡×•×“×™×ª
×œ×˜×œ×¤×•×Ÿ ×©×œ×š **%s**.',
             'tweb.Login.Code.SentSmsWordBeginning' => '×©×œ×—× ×• SMS ×¢×� ×ž×™×œ×” ×¡×•×“×™×ª ×©×ž×ª×—×™×œ×”
×‘-"**%2$s**" ×œ×˜×œ×¤×•×Ÿ ×©×œ×š **%1$s**.',
             'tweb.Login.Code.SentUnknown' => '× ×� ×œ×—×¤×© ×‘×›×œ ×ž×§×•×�
×�×ª ×”×§×•×“ (×¡×•×’: %s)',
             'tweb.Login.Code.WordBeginningInvalid' => '×ž×™×œ×” ×©×’×•×™×”',
             'tweb.Login.Code.WordInvalid' => '×©×’×•×™, × ×� ×œ× ×¡×•×ª ×©×•×‘',
             'tweb.Login.Next' => '×”×‘×�',
             'tweb.Login.Password.Subtitle' => '×”×—×©×‘×•×Ÿ ×©×œ×š ×ž×•×’×Ÿ
×‘×¡×™×¡×ž×” × ×•×¡×¤×ª',
             'tweb.Login.Password.Title' => '×”×–×Ÿ ×�×ª ×”×¡×™×¡×ž×” ×©×œ×š',
             'tweb.Login.PhoneLabelInvalid' => '×ž×¡×¤×¨ ×˜×œ×¤×•×Ÿ ×œ×� ×ª×§×™×Ÿ',
             'tweb.Login.QR.Cancel' => '×›× ×™×¡×” ×‘×�×ž×¦×¢×•×ª ×ž×¡×¤×¨ ×˜×œ×¤×•×Ÿ >',
             'tweb.Login.QR.Help1' => '×¤×ª×— ×�×ª Telegram ×‘×˜×œ×¤×•×Ÿ ×©×œ×š',
             'tweb.Login.QR.Help2' => '×¢×‘×•×¨ ×�×œ **×”×’×“×¨×•×ª** > **×ž×›×©×™×¨×™×�** > **×”×•×¡×¤×ª ×ž×›×©×™×¨**',
             'tweb.Login.QR.Help3' => '×›×•×•×Ÿ ×�×ª ×”×˜×œ×¤×•×Ÿ ×©×œ×š ×œ×ž×¡×š ×–×” ×›×“×™ ×œ×�×©×¨ ×�×ª ×”×›× ×™×¡×”',
             'tweb.Login.QR.Login' => '×›× ×™×¡×” ×‘×�×ž×¦×¢×•×ª ×§×•×“ QR >',
             'tweb.Login.QR.Subtitle' => '×¡×¨×•×§ ×‘×�×ž×¦×¢×•×ª ×�×¤×œ×™×§×¦×™×™×ª Telegram ×‘×˜×œ×¤×•×Ÿ ×©×œ×š',
             'tweb.Login.QR.Title' => '×›× ×™×¡×” ×‘×�×ž×¦×¢×•×ª ×§×•×“ QR',
             'tweb.Login.Register.Subtitle' => '×”×–×Ÿ ×�×ª ×©×ž×š
×›×“×™ ×œ×™×¦×•×¨ ×�×ª ×”×—×©×‘×•×Ÿ ×©×œ×š',
             'tweb.Login.StartText' => '× ×� ×œ×�×©×¨ ×�×ª ×§×™×“×•×ž×ª ×”×ž×“×™× ×”
×•×œ×”×–×™×Ÿ ×�×ª ×ž×¡×¤×¨ ×”×˜×œ×¤×•×Ÿ ×©×œ×š.',
             'tweb.Login.Title' => '×›× ×™×¡×” ×œ-MadelineProto',
             'tweb.LoginPassword' => '×¡×™×¡×ž×”',
             'tweb.Optional' => '×�×•×¤×¦×™×•× ×œ×™',
             'tweb.PASSWORD_HASH_INVALID' => '×¡×™×¡×ž×” ×©×’×•×™×”',
             'tweb.PHONE_CODE_EMPTY' => '×§×•×“ ×©×’×•×™',
             'tweb.PHONE_CODE_EXPIRED' => '×¤×’ ×ª×•×§×£ ×”×§×•×“',
             'tweb.PHONE_CODE_INVALID' => '×§×•×“ ×©×’×•×™',
             'tweb.PHONE_NUMBER_BANNED' => '×ž×¡×¤×¨ ×”×˜×œ×¤×•×Ÿ ×”×–×” ×—×¡×•×�',
             'tweb.PHONE_NUMBER_FLOOD' => '×™×•×ª×¨ ×ž×“×™ × ×™×¡×™×•× ×•×ª, × ×¡×” ×©×•×‘ ×ž×�×•×—×¨ ×™×•×ª×¨',
             'tweb.PHONE_NUMBER_INVALID' => '×ž×¡×¤×¨ ×˜×œ×¤×•×Ÿ ×œ×� ×ª×§×™×Ÿ',
             'tweb.PHONE_PASSWORD_FLOOD' => '×™×•×ª×¨ ×ž×“×™ × ×™×¡×™×•× ×•×ª, × ×¡×” ×©×•×‘ ×ž×�×•×—×¨ ×™×•×ª×¨',
             'tweb.PhoneNumber.Code.Fragment.Info' => '×§×‘×œ ×�×ª ×”×§×•×“ ×‘×�×–×•×¨ ×”×ž×¡×¤×¨×™×� ×‘-[Fragment]()',
             'tweb.PleaseWait' => '× ×� ×œ×”×ž×ª×™×Ÿ...',
             'tweb.StartMessaging' => '×”×ª×—×œ ×œ×©×œ×•×— ×”×•×“×¢×•×ª',
             'tweb.YourName' => '×”×©×� ×©×œ×š',
             'type_extract_error' => '×œ×� × ×™×ª×Ÿ ×”×™×” ×œ×—×œ×¥ ×�×ª ×”×¡×•×’ "%s", × ×“×¨×© ×œ×¢×“×›×Ÿ ×�×ª MadelineProto!',
             'type_extract_error_id' => '×œ×� × ×™×ª×Ÿ ×”×™×” ×œ×—×œ×¥ ×�×ª ×”×¡×•×’: %s ×¢×� ×ž×–×”×” id %s, × ×“×¨×© ×œ×¢×“×›×Ÿ ×�×ª MadelineProto!',
             'update_madelineproto' => '×�×ª×” ×ž×¤×¢×™×œ ×’×¨×¡×” ×™×©× ×” ×©×œ MadelineProto, × ×“×¨×© ×¢×“×›×•×Ÿ: ×¤×•×¢×œ ×›×¢×ª %s, ×�×š ×”×’×¨×¡×” ×”×�×—×¨×•× ×” ×¢×� ×ž×¡×¤×¨ ×ª×™×§×•× ×™ ×‘×�×’×™×� ×•×ª×›×•× ×•×ª ×—×“×©×•×ª ×”×™×� %s!',
             'value_bigger_than_2147483647' => '×”×¢×¨×š ×©×”×ª×§×‘×œ %s ×’×“×•×œ ×ž 2147483647',
             'value_bigger_than_4294967296' => '×”×¢×¨×š ×©×”×ª×§×‘×œ %s ×’×“×•×œ ×ž 4294967296',
             'value_bigger_than_9223372036854775807' => '×”×¢×¨×š ×©×”×ª×§×‘×œ %s ×’×“×•×œ ×ž 9223372036854775807',
             'value_smaller_than_0' => '×”×¢×¨×š ×©×”×ª×§×‘×œ %s ×§×˜×Ÿ ×ž 0',
             'value_smaller_than_2147483648' => '×”×¢×¨×š ×©×”×ª×§×‘×œ %s ×§×˜×Ÿ ×ž -2147483648',
             'value_smaller_than_9223372036854775808' => '×”×¢×¨×š ×©×”×ª×§×‘×œ %s ×§×˜×Ÿ ×ž -9223372036854775808',
             'waveform_must_have_100_values' => '×ž×¢×¨×š ×¦×•×¨×•×ª ×’×œ(waveform ) ×—×™×™×‘ ×œ×”×™×•×ª 100 ×¢×¨×›×™×�!',
             'waveform_value' => '×¢×¨×š waveform ×—×™×™×‘ ×œ×”×™×•×ª ×‘×™×Ÿ 0 ×œ-31!',
             'webContinue' => '×”×ž×©×š',
             'webDocs' => '×ª×™×¢×•×“',
             'webDoneLead' => 'MadelineProto ×ž×ž×©×™×š ×œ×¤×¢×•×œ ×‘×©×¨×ª: ×�×¤×©×¨ ×œ×¡×’×•×¨ ×�×ª ×”×“×£ ×”×–×”.',
             'webError' => '%s. ×‘×“×•×§ ×•× ×¡×” ×©×•×‘.',
             'webFieldRequired' => '×™×© ×œ×ž×œ×� ×�×ª ×”×©×“×” ×”×–×”.',
             'webHidePassword' => '×”×¡×ª×¨',
             'webLogin' => '×›× ×™×¡×”',
             'webOptional' => '×�×•×¤×¦×™×•× ×œ×™',
             'webPhoneInvalid' => '×”×–×Ÿ ×�×ª ×ž×¡×¤×¨ ×”×˜×œ×¤×•×Ÿ ×¢×� ×§×™×“×•×ž×ª ×”×ž×“×™× ×”, ×œ×ž×©×œ +39 123 456 7890.',
             'webProgress' => '×”×ª×§×“×ž×•×ª ×”×”×’×“×¨×”',
             'webQrTitle' => '×›× ×™×¡×” ×‘×�×ž×¦×¢×•×ª ×§×•×“ QR',
             'webQrWaiting' => '×ž×ž×ª×™×Ÿ ×œ×¡×¨×™×§×”â€¦',
             'webShowPassword' => '×”×¦×’',
             'webStepAccount' => '×—×©×‘×•×Ÿ',
             'webStepApp' => 'API ID ×•-hash',
             'webStepDone' => '×¤×•×¢×œ',
             'webStepVerify' => '×�×™×ž×•×ª',
         ],
        'hi' =>
         [
             '2fa_uncalled' => 'à¤®à¥ˆà¤‚ à¤ªà¤¾à¤¸à¤µà¤°à¥�à¤¡ à¤•à¥€ à¤ªà¥�à¤°à¤¤à¥€à¤•à¥�à¤·à¤¾ à¤¨à¤¹à¥€à¤‚ à¤•à¤° à¤°à¤¹à¤¾ à¤¹à¥‚à¤�! à¤•à¥ƒà¤ªà¤¯à¤¾ à¤ªà¤¹à¤²à¥‡ phoneLogin à¤”à¤° completePhoneLogin à¤®à¥‡à¤¥à¤¡ à¤•à¥‰à¤² à¤•à¤°à¥‡à¤‚!',
             'accepting_call' => '%s à¤•à¥€ à¤•à¥‰à¤² à¤¸à¥�à¤µà¥€à¤•à¤¾à¤° à¤•à¥€ à¤œà¤¾ à¤°à¤¹à¥€ à¤¹à¥ˆ...',
             'account_banned' => '!!!!!!! à¤šà¥‡à¤¤à¤¾à¤µà¤¨à¥€ !!!!!!!
Telegram à¤•à¥€ à¤«à¤¼à¥�à¤²à¤¡ à¤°à¥‹à¤•à¤¥à¤¾à¤® à¤ªà¥�à¤°à¤£à¤¾à¤²à¥€ à¤¨à¥‡ à¤‡à¤¸ à¤…à¤•à¤¾à¤‰à¤‚à¤Ÿ à¤•à¥‹ à¤¨à¤¿à¤²à¤‚à¤¬à¤¿à¤¤ à¤•à¤° à¤¦à¤¿à¤¯à¤¾ à¤¹à¥ˆà¥¤
à¤œà¤¾à¤°à¥€ à¤°à¤–à¤¨à¥‡ à¤•à¥‡ à¤²à¤¿à¤� à¤®à¥ˆà¤¨à¥�à¤¯à¥�à¤…à¤² à¤µà¥‡à¤°à¤¿à¤«à¤¼à¤¿à¤•à¥‡à¤¶à¤¨ à¤†à¤µà¤¶à¥�à¤¯à¤• à¤¹à¥ˆà¥¤
recover@telegram.org à¤ªà¤° à¤�à¤• à¤ˆà¤®à¥‡à¤² à¤­à¥‡à¤œà¥‡à¤‚, à¤œà¤¿à¤¸à¤®à¥‡à¤‚ à¤«à¤¼à¥‹à¤¨ à¤¨à¤‚à¤¬à¤° %s à¤•à¥‹ à¤…à¤¨à¤¬à¥ˆà¤¨ à¤•à¤°à¤¨à¥‡ à¤•à¤¾ à¤…à¤¨à¥�à¤°à¥‹à¤§ à¤•à¤°à¥‡à¤‚, à¤”à¤° à¤¸à¤‚à¤•à¥�à¤·à¥‡à¤ª à¤®à¥‡à¤‚ à¤¬à¤¤à¤¾à¤�à¤� à¤•à¤¿ à¤†à¤ª à¤‡à¤¸ à¤«à¤¼à¥‹à¤¨ à¤¨à¤‚à¤¬à¤° à¤•à¥‡ à¤¸à¤¾à¤¥ à¤•à¥�à¤¯à¤¾ à¤•à¤°à¥‡à¤‚à¤—à¥‡à¥¤
à¤«à¤¿à¤° à¤¦à¥‹à¤¬à¤¾à¤°à¤¾ à¤²à¥‰à¤—à¤¿à¤¨ à¤•à¤°à¥‡à¤‚à¥¤
à¤¯à¤¦à¤¿ à¤†à¤ªà¤¨à¥‡ à¤œà¤¾à¤¨à¤¬à¥‚à¤�à¤•à¤° à¤¯à¤¹ à¤…à¤•à¤¾à¤‰à¤‚à¤Ÿ à¤¡à¤¿à¤²à¥€à¤Ÿ à¤•à¤¿à¤¯à¤¾ à¤¹à¥ˆ, à¤¤à¥‹ à¤‡à¤¸ à¤¸à¤‚à¤¦à¥‡à¤¶ à¤•à¥‹ à¤…à¤¨à¤¦à¥‡à¤–à¤¾ à¤•à¤°à¥‡à¤‚à¥¤',
             'already_loggedIn' => 'MadelineProto à¤•à¤¾ à¤¯à¤¹ à¤‡à¤‚à¤¸à¥�à¤Ÿà¥‡à¤‚à¤¸ à¤ªà¤¹à¤²à¥‡ à¤¸à¥‡ à¤²à¥‰à¤— à¤‡à¤¨ à¤¹à¥ˆ!',
             'apiAppDescLabelWeb' => 'à¤µà¤¿à¤µà¤°à¤£',
             'apiAppInstructionsAuto0' => 'à¤�à¤ª à¤•à¤¾ à¤¨à¤¾à¤® à¤¦à¤°à¥�à¤œ à¤•à¤°à¥‡à¤‚, à¤•à¥�à¤› à¤­à¥€ à¤¹à¥‹ à¤¸à¤•à¤¤à¤¾ à¤¹à¥ˆ: ',
             'apiAppInstructionsAuto1' => 'à¤�à¤ª à¤•à¤¾ à¤›à¥‹à¤Ÿà¤¾ à¤¨à¤¾à¤® à¤¦à¤°à¥�à¤œ à¤•à¤°à¥‡à¤‚, à¤•à¥‡à¤µà¤² à¤…à¤•à¥�à¤·à¤° à¤”à¤° à¤…à¤‚à¤•, 5-32 à¤µà¤°à¥�à¤£: ',
             'apiAppInstructionsAuto2' => 'à¤�à¤ª/à¤µà¥‡à¤¬à¤¸à¤¾à¤‡à¤Ÿ à¤•à¤¾ URL à¤¦à¤°à¥�à¤œ à¤•à¤°à¥‡à¤‚, à¤¯à¤¾ t.me/yourusername: ',
             'apiAppInstructionsAuto3' => 'à¤�à¤ª à¤•à¤¾ à¤ªà¥�à¤²à¥‡à¤Ÿà¤«à¤¼à¥‰à¤°à¥�à¤® à¤¦à¤°à¥�à¤œ à¤•à¤°à¥‡à¤‚: ',
             'apiAppInstructionsAuto4' => 'à¤…à¤ªà¤¨à¥‡ à¤�à¤ª à¤•à¤¾ à¤µà¤¿à¤µà¤°à¤£ à¤¦à¥‡à¤‚: ',
             'apiAppInstructionsAutoTypeOther' => 'à¤…à¤¨à¥�à¤¯ (à¤µà¤¿à¤µà¤°à¤£ à¤®à¥‡à¤‚ à¤¬à¤¤à¤¾à¤�à¤�)',
             'apiAppInstructionsManual0' => 'à¤†à¤ªà¤•à¥‡ à¤�à¤ª à¤•à¤¾ à¤¨à¤¾à¤®, à¤•à¥�à¤› à¤­à¥€ à¤¹à¥‹ à¤¸à¤•à¤¤à¤¾ à¤¹à¥ˆ',
             'apiAppInstructionsManual1' => 'à¤†à¤ªà¤•à¥‡ à¤�à¤ª à¤•à¤¾ à¤›à¥‹à¤Ÿà¤¾ à¤¨à¤¾à¤®, à¤•à¥‡à¤µà¤² à¤…à¤•à¥�à¤·à¤° à¤”à¤° à¤…à¤‚à¤•, 5-32 à¤µà¤°à¥�à¤£',
             'apiAppInstructionsManual2' => 'à¤†à¤ªà¤•à¥‡ à¤�à¤ª/à¤µà¥‡à¤¬à¤¸à¤¾à¤‡à¤Ÿ à¤•à¤¾ URL, à¤¯à¤¾ t.me/yourusername',
             'apiAppInstructionsManual3' => 'à¤•à¥�à¤› à¤­à¥€',
             'apiAppInstructionsManual4' => 'à¤¯à¤¹à¤¾à¤� à¤…à¤ªà¤¨à¥‡ à¤�à¤ª à¤•à¤¾ à¤µà¤¿à¤µà¤°à¤£ à¤¦à¥‡à¤‚',
             'apiAppShortNameLabelWeb' => 'à¤›à¥‹à¤Ÿà¤¾ à¤¨à¤¾à¤®',
             'apiAppTitleLabelWeb' => 'à¤�à¤ª à¤•à¤¾ à¤¨à¤¾à¤®',
             'apiAppUrlLabelWeb' => 'URL',
             'apiAppWeb' => 'API à¤œà¤¾à¤¨à¤•à¤¾à¤°à¥€ à¤¦à¤°à¥�à¤œ à¤•à¤°à¥‡à¤‚',
             'apiAutoCodeLabelWeb' => 'à¤²à¥‰à¤—à¤¿à¤¨ à¤•à¥‹à¤¡',
             'apiAutoCodeLeadWeb' => 'my.telegram.org à¤¨à¥‡ à¤†à¤ªà¤•à¥‡ Telegram à¤�à¤ª à¤®à¥‡à¤‚, Telegram à¤µà¤¾à¤²à¥€ à¤šà¥ˆà¤Ÿ à¤®à¥‡à¤‚ à¤�à¤• à¤²à¥‰à¤—à¤¿à¤¨ à¤•à¥‹à¤¡ à¤­à¥‡à¤œà¤¾ à¤¹à¥ˆ (SMS à¤¸à¥‡ à¤¨à¤¹à¥€à¤‚)à¥¤',
             'apiAutoCodeTitleWeb' => 'Telegram à¤¸à¥‡ à¤®à¤¿à¤²à¤¾ à¤•à¥‹à¤¡ à¤¦à¤°à¥�à¤œ à¤•à¤°à¥‡à¤‚',
             'apiAutoErrorTitle' => 'my.telegram.org à¤¨à¥‡ à¤�à¤• à¤¤à¥�à¤°à¥�à¤Ÿà¤¿ à¤²à¥Œà¤Ÿà¤¾à¤ˆ',
             'apiAutoFallbackManual' => 'à¤‡à¤¸à¤•à¥‡ à¤¬à¤œà¤¾à¤¯ API ID à¤”à¤° hash à¤®à¥ˆà¤¨à¥�à¤¯à¥�à¤…à¤² à¤°à¥‚à¤ª à¤¸à¥‡ à¤¦à¤°à¥�à¤œ à¤•à¤°à¥‡à¤‚à¥¤',
             'apiAutoPhoneHintWeb' => 'my.telegram.org à¤²à¥‰à¤—à¤¿à¤¨ à¤•à¥‹à¤¡ SMS à¤¸à¥‡ à¤¨à¤¹à¥€à¤‚, à¤¬à¤²à¥�à¤•à¤¿ à¤†à¤ªà¤•à¥‡ Telegram à¤�à¤ª à¤ªà¤° à¤­à¥‡à¤œà¤¤à¤¾ à¤¹à¥ˆà¥¤',
             'apiAutoPrompt0' => 'à¤µà¤¹ à¤«à¤¼à¥‹à¤¨ à¤¨à¤‚à¤¬à¤° à¤¦à¤°à¥�à¤œ à¤•à¤°à¥‡à¤‚ à¤œà¥‹ Telegram à¤ªà¤° à¤ªà¤¹à¤²à¥‡ à¤¸à¥‡ à¤°à¤œà¤¿à¤¸à¥�à¤Ÿà¤°à¥�à¤¡ à¤¹à¥ˆ: ',
             'apiAutoPrompt1' => 'Telegram à¤®à¥‡à¤‚ à¤ªà¥�à¤°à¤¾à¤ªà¥�à¤¤ à¤µà¥‡à¤°à¤¿à¤«à¤¼à¤¿à¤•à¥‡à¤¶à¤¨ à¤•à¥‹à¤¡ à¤¦à¤°à¥�à¤œ à¤•à¤°à¥‡à¤‚: ',
             'apiAutoWeb' => 'API ID à¤ªà¥�à¤°à¤¾à¤ªà¥�à¤¤ à¤•à¤°à¤¨à¥‡ à¤•à¥‡ à¤²à¤¿à¤� à¤µà¤¹ à¤«à¤¼à¥‹à¤¨ à¤¨à¤‚à¤¬à¤° à¤¦à¤°à¥�à¤œ à¤•à¤°à¥‡à¤‚ à¤œà¥‹ Telegram à¤ªà¤° <b>à¤ªà¤¹à¤²à¥‡ à¤¸à¥‡ à¤°à¤œà¤¿à¤¸à¥�à¤Ÿà¤°à¥�à¤¡</b> à¤¹à¥ˆ',
             'apiChooseAutoWeb' => 'à¤…à¤ªà¤¨à¥‡-à¤†à¤ª à¤ªà¥�à¤°à¤¾à¤ªà¥�à¤¤ à¤•à¤°à¥‡à¤‚',
             'apiChooseManualAuto' => 'à¤•à¥�à¤¯à¤¾ à¤†à¤ª API ID à¤”à¤° hash à¤•à¥‹ my.telegram.org à¤¸à¥‡ à¤…à¤ªà¤¨à¥‡-à¤†à¤ª à¤ªà¥�à¤°à¤¾à¤ªà¥�à¤¤ à¤•à¤°à¤¨à¤¾ à¤šà¤¾à¤¹à¤¤à¥‡ à¤¹à¥ˆà¤‚ (a), à¤¯à¤¾ à¤‰à¤¨à¥�à¤¹à¥‡à¤‚ à¤®à¥ˆà¤¨à¥�à¤¯à¥�à¤…à¤² à¤°à¥‚à¤ª à¤¸à¥‡ à¤¦à¤°à¥�à¤œ à¤•à¤°à¤¨à¤¾ à¤šà¤¾à¤¹à¤¤à¥‡ à¤¹à¥ˆà¤‚ (m)?',
             'apiChooseManualAutoTip' => 'à¤§à¥�à¤¯à¤¾à¤¨ à¤¦à¥‡à¤‚ à¤•à¤¿ à¤†à¤ª à¤¸à¥‡à¤Ÿà¤¿à¤‚à¤—à¥�à¤¸ à¤•à¤¾ à¤‰à¤ªà¤¯à¥‹à¤— à¤•à¤°à¤•à¥‡ API ID/hash à¤¸à¥€à¤§à¥‡ à¤•à¥‹à¤¡ à¤®à¥‡à¤‚ à¤­à¥€ à¤¦à¥‡ à¤¸à¤•à¤¤à¥‡ à¤¹à¥ˆà¤‚: %s',
             'apiChooseManualAutoTipWeb' => 'à¤§à¥�à¤¯à¤¾à¤¨ à¤¦à¥‡à¤‚ à¤•à¤¿ à¤†à¤ª <a target="_blank" href="%s">à¤¸à¥‡à¤Ÿà¤¿à¤‚à¤—à¥�à¤¸</a> à¤•à¤¾ à¤‰à¤ªà¤¯à¥‹à¤— à¤•à¤°à¤•à¥‡ API ID/hash à¤¸à¥€à¤§à¥‡ à¤•à¥‹à¤¡ à¤®à¥‡à¤‚ à¤­à¥€ à¤¦à¥‡ à¤¸à¤•à¤¤à¥‡ à¤¹à¥ˆà¤‚à¥¤',
             'apiChooseManualWeb' => 'à¤®à¥‡à¤°à¥‡ à¤ªà¤¾à¤¸ à¤ªà¤¹à¤²à¥‡ à¤¸à¥‡ API ID à¤¹à¥ˆ',
             'apiChoosePrompt' => 'à¤†à¤ªà¤•à¤¾ à¤µà¤¿à¤•à¤²à¥�à¤ª (m/a): ',
             'apiCreateErrorTitle' => 'my.telegram.org à¤¨à¥‡ à¤�à¤ª à¤¨à¤¹à¥€à¤‚ à¤¬à¤¨à¤¾à¤¯à¤¾',
             'apiCreateLeadWeb' => 'à¤‡à¤¸ à¤…à¤•à¤¾à¤‰à¤‚à¤Ÿ à¤•à¤¾ my.telegram.org à¤ªà¤° à¤…à¤­à¥€ à¤•à¥‹à¤ˆ à¤�à¤ª à¤¨à¤¹à¥€à¤‚ à¤¹à¥ˆà¥¤ à¤¯à¥‡ à¤œà¤¾à¤¨à¤•à¤¾à¤°à¥€ à¤•à¥‡à¤µà¤² à¤†à¤ªà¤•à¥‹ à¤¦à¤¿à¤–à¤¾à¤ˆ à¤¦à¥‡à¤¤à¥€ à¤¹à¥ˆ à¤”à¤° à¤¬à¤¾à¤¦ à¤®à¥‡à¤‚ à¤¬à¤¦à¤²à¥€ à¤œà¤¾ à¤¸à¤•à¤¤à¥€ à¤¹à¥ˆà¥¤',
             'apiCreateTitleWeb' => 'à¤…à¤ªà¤¨à¤¾ à¤�à¤ª à¤¬à¤¨à¤¾à¤�à¤�',
             'apiCreateVpsWarning' => 'Telegram, VPS, VPN à¤”à¤° à¤…à¤¨à¥�à¤¯ à¤¡à¥‡à¤Ÿà¤¾ à¤¸à¥‡à¤‚à¤Ÿà¤° IP à¤°à¥‡à¤‚à¤œ à¤¸à¥‡ my.telegram.org à¤ªà¤° à¤¨à¤� à¤�à¤ª à¤¬à¤¨à¤¾à¤¨à¥‡ à¤•à¥€ à¤…à¤¨à¥�à¤®à¤¤à¤¿ à¤¨à¤¹à¥€à¤‚ à¤¦à¥‡à¤¤à¤¾, à¤”à¤° à¤¯à¤¹ à¤…à¤¨à¥�à¤°à¥‹à¤§ MadelineProto à¤šà¤²à¤¾ à¤°à¤¹à¥€ à¤®à¤¶à¥€à¤¨ à¤•à¥‡ IP à¤¸à¥‡ à¤­à¥‡à¤œà¤¾ à¤—à¤¯à¤¾ à¤¥à¤¾à¥¤ à¤•à¤¿à¤¸à¥€ à¤˜à¤°à¥‡à¤²à¥‚ à¤•à¤¨à¥‡à¤•à¥�à¤¶à¤¨ (à¤œà¥ˆà¤¸à¥‡ à¤®à¥‹à¤¬à¤¾à¤‡à¤² à¤¡à¥‡à¤Ÿà¤¾ à¤ªà¤° à¤†à¤ªà¤•à¤¾ à¤«à¤¼à¥‹à¤¨) à¤¸à¥‡ https://my.telegram.org à¤ªà¤° à¤®à¥ˆà¤¨à¥�à¤¯à¥�à¤…à¤² à¤°à¥‚à¤ª à¤¸à¥‡ à¤�à¤ª à¤¬à¤¨à¤¾à¤�à¤�, à¤«à¤¿à¤° API ID à¤”à¤° hash à¤¦à¤°à¥�à¤œ à¤•à¤°à¥‡à¤‚à¥¤',
             'apiCreateWeb' => 'à¤�à¤ª à¤¬à¤¨à¤¾à¤�à¤�',
             'apiError' => 'à¤¤à¥�à¤°à¥�à¤Ÿà¤¿: %sà¥¤ à¤«à¤¿à¤° à¤¸à¥‡ à¤ªà¥�à¤°à¤¯à¤¾à¤¸ à¤•à¤°à¥‡à¤‚à¥¤',
             'apiInvalidWeb' => 'API ID à¤�à¤• à¤¸à¤‚à¤–à¥�à¤¯à¤¾ à¤¹à¥‹à¤¤à¥€ à¤¹à¥ˆ à¤”à¤° API hash 32 à¤µà¤°à¥�à¤£à¥‹à¤‚ à¤•à¤¾ à¤¹à¥‹à¤¤à¤¾ à¤¹à¥ˆ: à¤‡à¤¨à¥�à¤¹à¥‡à¤‚ my.telegram.org à¤¸à¥‡ à¤«à¤¿à¤° à¤¸à¥‡ à¤•à¥‰à¤ªà¥€ à¤•à¤°à¥‡à¤‚à¥¤',
             'apiManualInstructions0' => 'https://my.telegram.org à¤ªà¤° à¤²à¥‰à¤—à¤¿à¤¨ à¤•à¤°à¥‡à¤‚',
             'apiManualInstructions1' => 'API development tools à¤ªà¤° à¤œà¤¾à¤�à¤�',
             'apiManualInstructions2' => 'create application à¤ªà¤° à¤•à¥�à¤²à¤¿à¤• à¤•à¤°à¥‡à¤‚',
             'apiManualPrompt0' => 'à¤…à¤ªà¤¨à¤¾ API ID à¤¦à¤°à¥�à¤œ à¤•à¤°à¥‡à¤‚: ',
             'apiManualPrompt1' => 'à¤…à¤ªà¤¨à¤¾ API hash à¤¦à¤°à¥�à¤œ à¤•à¤°à¥‡à¤‚: ',
             'apiManualWeb' => 'à¤…à¤ªà¤¨à¤¾ API ID à¤”à¤° API hash à¤¦à¤°à¥�à¤œ à¤•à¤°à¥‡à¤‚',
             'apiParamsError' => 'à¤†à¤ªà¤¨à¥‡ à¤¸à¤­à¥€ à¤†à¤µà¤¶à¥�à¤¯à¤• à¤ªà¥ˆà¤°à¤¾à¤®à¥€à¤Ÿà¤° à¤¨à¤¹à¥€à¤‚ à¤¦à¤¿à¤�!',
             'apiSaveWeb' => 'à¤¸à¤¹à¥‡à¤œà¥‡à¤‚ à¤”à¤° à¤œà¤¾à¤°à¥€ à¤°à¤–à¥‡à¤‚',
             'apiSendCodeWeb' => 'à¤•à¥‹à¤¡ à¤­à¥‡à¤œà¥‡à¤‚',
             'apiSetupLeadWeb' => 'à¤¹à¤° Telegram à¤�à¤ª à¤•à¥‹ à¤�à¤• API ID à¤”à¤° hash à¤•à¥€ à¤œà¤¼à¤°à¥‚à¤°à¤¤ à¤¹à¥‹à¤¤à¥€ à¤¹à¥ˆà¥¤ MadelineProto à¤•à¤¿à¤¸à¥€ à¤®à¥Œà¤œà¥‚à¤¦à¤¾ Telegram à¤…à¤•à¤¾à¤‰à¤‚à¤Ÿ à¤•à¥‡ à¤«à¤¼à¥‹à¤¨ à¤¨à¤‚à¤¬à¤° à¤¸à¥‡ à¤‡à¤¨à¥�à¤¹à¥‡à¤‚ à¤†à¤ªà¤•à¥‡ à¤²à¤¿à¤� my.telegram.org à¤¸à¥‡ à¤ªà¥�à¤°à¤¾à¤ªà¥�à¤¤ à¤•à¤° à¤¸à¤•à¤¤à¤¾ à¤¹à¥ˆà¥¤',
             'apiSetupTitleWeb' => 'à¤…à¤ªà¤¨à¤¾ API ID à¤”à¤° hash à¤ªà¥�à¤°à¤¾à¤ªà¥�à¤¤ à¤•à¤°à¥‡à¤‚',
             'apiStartOverWeb' => 'à¤«à¤¿à¤° à¤¸à¥‡ à¤¶à¥�à¤°à¥‚ à¤•à¤°à¥‡à¤‚',
             'api_not_set' => 'à¤†à¤ªà¤•à¥‹ api key à¤”à¤° api id à¤¦à¥‡à¤¨à¥€ à¤¹à¥‹à¤—à¥€, à¤…à¤ªà¤¨à¥€ à¤–à¥�à¤¦ à¤•à¥€ my.telegram.org à¤ªà¤° à¤ªà¥�à¤°à¤¾à¤ªà¥�à¤¤ à¤•à¤°à¥‡à¤‚',
             'array_invalid' => 'à¤†à¤ªà¤¨à¥‡ à¤�à¤• à¤®à¤¾à¤¨à¥�à¤¯ array à¤¨à¤¹à¥€à¤‚ à¤¦à¤¿à¤¯à¤¾',
             'baseDirLimitation' => 'basedir à¤ªà¥�à¤°à¤¤à¤¿à¤¬à¤‚à¤§ à¤•à¥‰à¤¨à¥�à¤«à¤¼à¤¿à¤—à¤° à¤•à¤¿à¤¯à¤¾ à¤—à¤¯à¤¾ à¤¹à¥ˆ: à¤‡à¤¸à¤¸à¥‡ à¤ªà¤°à¤«à¤¼à¥‰à¤°à¥�à¤®à¥‡à¤‚à¤¸ à¤ªà¥�à¤°à¤­à¤¾à¤µà¤¿à¤¤ à¤¹à¥‹ à¤¸à¤•à¤¤à¥€ à¤¹à¥ˆ à¤”à¤° à¤•à¥�à¤› à¤¸à¤®à¤¸à¥�à¤¯à¤¾à¤�à¤� à¤¹à¥‹ à¤¸à¤•à¤¤à¥€ à¤¹à¥ˆà¤‚, à¤•à¥ƒà¤ªà¤¯à¤¾ à¤¸à¤‚à¤­à¤µ à¤¹à¥‹ à¤¤à¥‹ à¤‡à¤¸à¥‡ à¤…à¤•à¥�à¤·à¤® à¤•à¤°à¥‡à¤‚!',
             'bool_error' => 'boolean à¤¨à¤¹à¥€à¤‚ à¤¨à¤¿à¤•à¤¾à¤²à¤¾ à¤œà¤¾ à¤¸à¤•à¤¾',
             'botAlreadyRunning' => 'à¤¬à¥‰à¤Ÿ à¤ªà¤¹à¤²à¥‡ à¤¸à¥‡ à¤šà¤² à¤°à¤¹à¤¾ à¤¹à¥ˆ!',
             'botapi_conversion_error' => '%s à¤•à¥‹ bot API à¤‘à¤¬à¥�à¤œà¥‡à¤•à¥�à¤Ÿ à¤®à¥‡à¤‚ à¤•à¤¨à¥�à¤µà¤°à¥�à¤Ÿ à¤¨à¤¹à¥€à¤‚ à¤•à¤¿à¤¯à¤¾ à¤œà¤¾ à¤¸à¤•à¤¤à¤¾',
             'call_already_accepted' => 'à¤•à¥‰à¤² %s à¤ªà¤¹à¤²à¥‡ à¤¹à¥€ à¤¸à¥�à¤µà¥€à¤•à¤¾à¤° à¤•à¥€ à¤œà¤¾ à¤šà¥�à¤•à¥€ à¤¹à¥ˆ',
             'call_already_declined' => 'à¤•à¥‰à¤² %s à¤ªà¤¹à¤²à¥‡ à¤¹à¥€ à¤…à¤¸à¥�à¤µà¥€à¤•à¤¾à¤° à¤•à¥€ à¤œà¤¾ à¤šà¥�à¤•à¥€ à¤¹à¥ˆ',
             'call_completing' => '%s à¤•à¥€ à¤•à¥‰à¤² à¤ªà¥‚à¤°à¥€ à¤•à¥€ à¤œà¤¾ à¤°à¤¹à¥€ à¤¹à¥ˆ...',
             'call_confirming' => '%s à¤•à¥€ à¤•à¥‰à¤² à¤•à¥€ à¤ªà¥�à¤·à¥�à¤Ÿà¤¿ à¤•à¥€ à¤œà¤¾ à¤°à¤¹à¥€ à¤¹à¥ˆ...',
             'call_discarding' => 'à¤•à¥‰à¤² %s à¤¸à¤®à¤¾à¤ªà¥�à¤¤ à¤•à¥€ à¤œà¤¾ à¤°à¤¹à¥€ à¤¹à¥ˆ...',
             'call_error_1' => 'à¤•à¥‰à¤² %s à¤•à¥‹ à¤¢à¥‚à¤�à¤¢à¤•à¤° à¤¸à¥�à¤µà¥€à¤•à¤¾à¤° à¤¨à¤¹à¥€à¤‚ à¤•à¤¿à¤¯à¤¾ à¤œà¤¾ à¤¸à¤•à¤¾',
             'call_error_2' => 'à¤•à¥‰à¤² %s à¤•à¥‹ à¤¢à¥‚à¤�à¤¢à¤•à¤° à¤‰à¤¸à¤•à¥€ à¤ªà¥�à¤·à¥�à¤Ÿà¤¿ à¤¨à¤¹à¥€à¤‚ à¤•à¥€ à¤œà¤¾ à¤¸à¤•à¥€',
             'call_error_3' => 'à¤•à¥‰à¤² %s à¤•à¥‹ à¤¢à¥‚à¤�à¤¢à¤•à¤° à¤ªà¥‚à¤°à¤¾ à¤¨à¤¹à¥€à¤‚ à¤•à¤¿à¤¯à¤¾ à¤œà¤¾ à¤¸à¤•à¤¾',
             'cli_need_dl.php_link' => 'CLI à¤•à¥‡ à¤®à¤¾à¤§à¥�à¤¯à¤® à¤¸à¥‡ getDownloadLink à¤•à¤¾ à¤‰à¤ªà¤¯à¥‹à¤— à¤•à¤°à¤¤à¥‡ à¤¸à¤®à¤¯ à¤•à¥ƒà¤ªà¤¯à¤¾ à¤¡à¤¾à¤‰à¤¨à¤²à¥‹à¤¡ à¤¸à¥�à¤•à¥�à¤°à¤¿à¤ªà¥�à¤Ÿ à¤•à¤¾ URL à¤¨à¤¿à¤°à¥�à¤¦à¤¿à¤·à¥�à¤Ÿ à¤•à¤°à¥‡à¤‚!',
             'constructor_not_found' => 'à¤‡à¤¸ à¤Ÿà¤¾à¤‡à¤ª à¤•à¥‡ à¤²à¤¿à¤� constructor à¤¨à¤¹à¥€à¤‚ à¤®à¤¿à¤²à¤¾: ',
             'could_not_connect_to_MadelineProto' => 'MadelineProto à¤¸à¥‡ à¤•à¤¨à¥‡à¤•à¥�à¤Ÿ à¤¨à¤¹à¥€à¤‚ à¤•à¤¿à¤¯à¤¾ à¤œà¤¾ à¤¸à¤•à¤¾, à¤‡à¤¸à¥‡ à¤ à¥€à¤• à¤•à¤°à¤¨à¥‡ à¤•à¥‡ à¤²à¤¿à¤� à¤•à¥ƒà¤ªà¤¯à¤¾ proc_open à¤¸à¤•à¥�à¤·à¤® à¤•à¤°à¥‡à¤‚ à¤”à¤° open_basedir à¤ªà¥�à¤°à¤¤à¤¿à¤¬à¤‚à¤§ à¤¹à¤Ÿà¤¾à¤�à¤� à¤¯à¤¾ à¤µà¥‡à¤¬à¤¸à¤°à¥�à¤µà¤° à¤ªà¤¾à¤¥ à¤°à¥€à¤°à¤¾à¤‡à¤Ÿ à¤…à¤•à¥�à¤·à¤® à¤•à¤°à¥‡à¤‚! à¤¯à¤¦à¤¿ à¤†à¤ª à¤¯à¤¹ à¤ªà¤¹à¤²à¥‡ à¤¹à¥€ à¤•à¤° à¤šà¥�à¤•à¥‡ à¤¹à¥ˆà¤‚, à¤¤à¥‹ à¤¸à¥�à¤¨à¤¿à¤¶à¥�à¤šà¤¿à¤¤ à¤•à¤°à¥‡à¤‚ à¤•à¤¿ PHP à¤•à¤¾ CLI à¤¸à¤‚à¤¸à¥�à¤•à¤°à¤£ à¤µà¥‡à¤¬ à¤¸à¤‚à¤¸à¥�à¤•à¤°à¤£ à¤•à¥‡ à¤¬à¤¿à¤²à¥�à¤•à¥�à¤² à¤¸à¤®à¤¾à¤¨ à¤¹à¥ˆ (à¤¸à¤®à¤¾à¤¨ à¤¸à¤‚à¤¸à¥�à¤•à¤°à¤£, à¤�à¤•à¥�à¤¸à¤Ÿà¥‡à¤‚à¤¶à¤¨ à¤†à¤¦à¤¿) à¤”à¤° à¤‰à¤¸ à¤¤à¥�à¤°à¥�à¤Ÿà¤¿ à¤•à¥‡ à¤¬à¤¾à¤°à¥‡ à¤®à¥‡à¤‚ à¤…à¤§à¤¿à¤• à¤œà¤¾à¤¨à¤•à¤¾à¤°à¥€ à¤•à¥‡ à¤²à¤¿à¤� MadelineProto.log à¤«à¤¼à¤¾à¤‡à¤² à¤¦à¥‡à¤–à¥‡à¤‚ à¤œà¤¿à¤¸à¤¨à¥‡ IPC à¤¸à¤°à¥�à¤µà¤° à¤•à¥‹ à¤¶à¥�à¤°à¥‚ à¤¹à¥‹à¤¨à¥‡ à¤¸à¥‡ à¤°à¥‹à¤•à¤¾à¥¤',
             'could_not_convert_object' => '%s à¤Ÿà¤¾à¤‡à¤ª à¤•à¥‡ à¤‘à¤¬à¥�à¤œà¥‡à¤•à¥�à¤Ÿ à¤•à¥‹ à¤•à¤¨à¥�à¤µà¤°à¥�à¤Ÿ à¤¨à¤¹à¥€à¤‚ à¤•à¤¿à¤¯à¤¾ à¤œà¤¾ à¤¸à¤•à¤¾',
             'deserialization_error' => 'à¤¡à¥€à¤¸à¥€à¤°à¤¿à¤¯à¤²à¤¾à¤‡à¤œà¤¼à¥‡à¤¶à¤¨ à¤•à¥‡ à¤¦à¥Œà¤°à¤¾à¤¨ à¤�à¤• à¤¤à¥�à¤°à¥�à¤Ÿà¤¿ à¤¹à¥�à¤ˆ',
             'dl.php_check_logs_make_sure_session_running' => 'à¤¯à¤¾ à¤¤à¥‹ à¤¸à¤‚à¤¬à¤‚à¤§à¤¿à¤¤ MadelineProto EventHandler à¤¬à¥‰à¤Ÿ à¤¯à¤¾ MadelineProto IPC à¤¸à¤°à¥�à¤µà¤° à¤‘à¤«à¤¼à¤²à¤¾à¤‡à¤¨ à¤¹à¥ˆ, à¤•à¥ƒà¤ªà¤¯à¤¾ à¤²à¥‰à¤— à¤œà¤¾à¤�à¤šà¥‡à¤‚ à¤”à¤° à¤¸à¥�à¤¨à¤¿à¤¶à¥�à¤šà¤¿à¤¤ à¤•à¤°à¥‡à¤‚ à¤•à¤¿ à¤‰à¤¨à¤®à¥‡à¤‚ à¤¸à¥‡ à¤•à¤® à¤¸à¥‡ à¤•à¤® à¤�à¤• à¤šà¤² à¤°à¤¹à¤¾ à¤¹à¥‹!',
             'dl.php_powered_by_madelineproto' => 'Telegram à¤«à¤¼à¤¾à¤‡à¤² à¤¡à¤¾à¤‰à¤¨à¤²à¥‹à¤¡ à¤¸à¤°à¥�à¤µà¤° (4GB à¤¤à¤•), <a href="https://docs.madelineproto.xyz" target="_blank">MadelineProto</a> à¤¦à¥�à¤µà¤¾à¤°à¤¾ à¤¸à¤‚à¤šà¤¾à¤²à¤¿à¤¤!<br>à¤…à¤ªà¤¨à¤¾ à¤–à¥�à¤¦ à¤•à¤¾ Telegram à¤«à¤¼à¤¾à¤‡à¤² à¤¡à¤¾à¤‰à¤¨à¤²à¥‹à¤¡ à¤¸à¤°à¥�à¤µà¤° à¤¸à¥‡à¤Ÿ à¤…à¤ª à¤•à¤°à¤¨à¥‡ à¤•à¥‡ à¤¬à¤¾à¤°à¥‡ à¤®à¥‡à¤‚ à¤…à¤§à¤¿à¤• à¤œà¤¾à¤¨à¤•à¤¾à¤°à¥€ à¤•à¥‡ à¤²à¤¿à¤� <a href="https://docs.madelineproto.xyz/docs/FILES.html#getting-a-download-link" target="_blank">à¤¯à¤¹à¤¾à¤�</a> à¤•à¥�à¤²à¤¿à¤• à¤•à¤°à¥‡à¤‚!',
             'do_not_delete_MadelineProto.log' => 'MadelineProto.log à¤«à¤¼à¤¾à¤‡à¤² à¤•à¥‹ à¤•à¤­à¥€ à¤­à¥€ à¤¡à¤¿à¤²à¥€à¤Ÿ à¤¨à¤¹à¥€à¤‚ à¤•à¤¿à¤¯à¤¾ à¤œà¤¾à¤¨à¤¾ à¤šà¤¾à¤¹à¤¿à¤�, à¤‡à¤¸à¤•à¥‡ à¤¬à¤œà¤¾à¤¯ à¤•à¥ƒà¤ªà¤¯à¤¾ à¤¸à¥‡à¤Ÿà¤¿à¤‚à¤—à¥�à¤¸ à¤®à¥‡à¤‚ à¤�à¤• à¤•à¤¸à¥�à¤Ÿà¤® à¤…à¤§à¤¿à¤•à¤¤à¤® à¤†à¤•à¤¾à¤° à¤¸à¥‡à¤Ÿ à¤•à¤°à¥‡à¤‚!',
             'do_not_remove_MadelineProto.log_phar' => 'à¤•à¥ƒà¤ªà¤¯à¤¾ madeline.phar à¤¯à¤¾ madeline.php à¤•à¥‹ à¤¨ à¤¹à¤Ÿà¤¾à¤�à¤�, à¤…à¤¨à¥�à¤¯à¤¥à¤¾ MadelineProto à¤•à¥�à¤°à¥ˆà¤¶ à¤¹à¥‹ à¤œà¤¾à¤�à¤—à¤¾à¥¤ à¤¯à¤¦à¤¿ à¤†à¤ªà¤•à¥‹ MadelineProto à¤•à¥‡ à¤¸à¤¾à¤¥ à¤•à¥‹à¤ˆ à¤¸à¤®à¤¸à¥�à¤¯à¤¾ à¤¹à¥ˆ, à¤¤à¥‹ à¤‰à¤¸à¥‡ https://github.com/danog/MadelineProto à¤¯à¤¾ https://t.me/pwrtelegramgroup à¤ªà¤° à¤°à¤¿à¤ªà¥‹à¤°à¥�à¤Ÿ à¤•à¤°à¥‡à¤‚',
             'do_not_use_blocking_class' => 'à¤ªà¤°à¤«à¤¼à¥‰à¤°à¥�à¤®à¥‡à¤‚à¤¸ à¤•à¤¾à¤°à¤£à¥‹à¤‚ à¤¸à¥‡, à¤¹à¥ˆà¤‚à¤¡à¤²à¤° à¤¨à¥‰à¤¨-async à¤¬à¥�à¤²à¥‰à¤•à¤¿à¤‚à¤— à¤•à¥�à¤²à¤¾à¤¸ %s à¤•à¤¾ à¤‰à¤ªà¤¯à¥‹à¤— à¤¨à¤¹à¥€à¤‚ à¤•à¤° à¤¸à¤•à¤¤à¥‡, à¤•à¥ƒà¤ªà¤¯à¤¾ à¤‡à¤¸à¤•à¥‡ à¤¬à¤œà¤¾à¤¯ %s à¤•à¤¾ à¤‰à¤ªà¤¯à¥‹à¤— à¤•à¤°à¥‡à¤‚',
             'do_not_use_blocking_function' => 'à¤ªà¤°à¤«à¤¼à¥‰à¤°à¥�à¤®à¥‡à¤‚à¤¸ à¤•à¤¾à¤°à¤£à¥‹à¤‚ à¤¸à¥‡, à¤‡à¤µà¥‡à¤‚à¤Ÿ à¤¹à¥ˆà¤‚à¤¡à¤²à¤° à¤¨à¥‰à¤¨-async à¤¬à¥�à¤²à¥‰à¤•à¤¿à¤‚à¤— à¤«à¤¼à¤‚à¤•à¥�à¤¶à¤¨ %s à¤•à¤¾ à¤‰à¤ªà¤¯à¥‹à¤— à¤¨à¤¹à¥€à¤‚ à¤•à¤° à¤¸à¤•à¤¤à¥‡, à¤•à¥ƒà¤ªà¤¯à¤¾ à¤‡à¤¸à¤•à¥‡ à¤¬à¤œà¤¾à¤¯ %s à¤•à¤¾ à¤‰à¤ªà¤¯à¥‹à¤— à¤•à¤°à¥‡à¤‚',
             'do_not_use_deprecated_function' => '%s à¤«à¤¼à¤‚à¤•à¥�à¤¶à¤¨ à¤…à¤ªà¥�à¤°à¤šà¤²à¤¿à¤¤ (deprecated) à¤¹à¥ˆ, à¤•à¥ƒà¤ªà¤¯à¤¾ à¤‡à¤¸à¤•à¥‡ à¤¬à¤œà¤¾à¤¯ %s à¤•à¤¾ à¤‰à¤ªà¤¯à¥‹à¤— à¤•à¤°à¥‡à¤‚',
             'do_not_use_non_root_require_in_event_handler' => 'à¤ªà¤°à¤«à¤¼à¥‰à¤°à¥�à¤®à¥‡à¤‚à¤¸ à¤•à¤¾à¤°à¤£à¥‹à¤‚ à¤¸à¥‡, à¤†à¤ªà¤•à¥‹ à¤‡à¤µà¥‡à¤‚à¤Ÿ à¤¹à¥ˆà¤‚à¤¡à¤²à¤° à¤•à¥�à¤²à¤¾à¤¸ à¤•à¥‡ à¤…à¤‚à¤¦à¤° require à¤¯à¤¾ include à¤•à¤¾ à¤‰à¤ªà¤¯à¥‹à¤— à¤¨à¤¹à¥€à¤‚ à¤•à¤°à¤¨à¤¾ à¤šà¤¾à¤¹à¤¿à¤�, à¤•à¥‡à¤µà¤² à¤°à¥‚à¤Ÿ-à¤²à¥‡à¤µà¤² require à¤•à¥€ à¤…à¤¨à¥�à¤®à¤¤à¤¿ à¤¹à¥ˆà¥¤',
             'do_not_use_yield' => 'MadelineProto 8 à¤•à¥‹ async à¤«à¤¼à¤‚à¤•à¥�à¤¶à¤¨à¥‹à¤‚ à¤®à¥‡à¤‚ yield à¤•à¥‡ à¤‰à¤ªà¤¯à¥‹à¤— à¤•à¥€ à¤¨ à¤¤à¥‹ à¤†à¤µà¤¶à¥�à¤¯à¤•à¤¤à¤¾ à¤¹à¥ˆ à¤”à¤° à¤¨ à¤¹à¥€ à¤¯à¤¹ à¤‡à¤¸à¤•à¤¾ à¤¸à¤®à¤°à¥�à¤¥à¤¨ à¤•à¤°à¤¤à¤¾ à¤¹à¥ˆ, à¤†à¤ªà¤•à¥‹ async à¤«à¤¼à¤‚à¤•à¥�à¤¶à¤¨ à¤•à¥‰à¤² à¤•à¥‡ à¤²à¤¿à¤� à¤ªà¤¹à¤²à¥‡ à¤‰à¤ªà¤¯à¥‹à¤— à¤•à¤¿à¤� à¤—à¤� à¤¸à¤­à¥€ yield à¤•à¥€à¤µà¤°à¥�à¤¡ à¤¹à¤Ÿà¤¾à¤¨à¥‡ à¤¹à¥‹à¤‚à¤—à¥‡',
             'done' => 'à¤¹à¥‹ à¤—à¤¯à¤¾!',
             'encode_double_error' => 'double à¤•à¥‹ à¤ à¥€à¤• à¤¸à¥‡ à¤�à¤¨à¤•à¥‹à¤¡ à¤¨à¤¹à¥€à¤‚ à¤•à¤¿à¤¯à¤¾ à¤œà¤¾ à¤¸à¤•à¤¾',
             'extensionRecommended' => 'à¤šà¥‡à¤¤à¤¾à¤µà¤¨à¥€: %s à¤�à¤•à¥�à¤¸à¤Ÿà¥‡à¤‚à¤¶à¤¨ à¤‡à¤‚à¤¸à¥�à¤Ÿà¥‰à¤² à¤¨à¤¹à¥€à¤‚ à¤¹à¥ˆ, à¤•à¥ƒà¤ªà¤¯à¤¾ MadelineProto à¤•à¥€ à¤—à¤¤à¤¿ à¤¬à¤¢à¤¼à¤¾à¤¨à¥‡ à¤•à¥‡ à¤²à¤¿à¤� à¤‡à¤¸à¥‡ à¤‡à¤‚à¤¸à¥�à¤Ÿà¥‰à¤² à¤•à¤°à¥‡à¤‚!',
             'extensionRequired' => 'MadelineProto à¤•à¥‹ à¤šà¤²à¤¨à¥‡ à¤•à¥‡ à¤²à¤¿à¤� %s à¤�à¤•à¥�à¤¸à¤Ÿà¥‡à¤‚à¤¶à¤¨ à¤•à¥€ à¤†à¤µà¤¶à¥�à¤¯à¤•à¤¤à¤¾ à¤¹à¥ˆà¥¤ %s',
             'extensionRequiredInstallWithApt' => 'sudo apt-get install %s à¤šà¤²à¤¾à¤•à¤° à¤¦à¥‡à¤–à¥‡à¤‚à¥¤',
             'extensionRequiredInstallWithCustomInstructions' => 'à¤‡à¤¸à¥‡ à¤‡à¤‚à¤¸à¥�à¤Ÿà¥‰à¤² à¤•à¤°à¤¨à¥‡ à¤•à¥‡ à¤²à¤¿à¤� %s à¤ªà¤° à¤¦à¤¿à¤� à¤—à¤� à¤¨à¤¿à¤°à¥�à¤¦à¥‡à¤¶à¥‹à¤‚ à¤•à¤¾ à¤ªà¤¾à¤²à¤¨ à¤•à¤°à¥‡à¤‚à¥¤',
             'file_not_exist' => 'à¤«à¤¼à¤¾à¤‡à¤² à¤®à¥Œà¤œà¥‚à¤¦ à¤¨à¤¹à¥€à¤‚ à¤¹à¥ˆ',
             'file_parsing' => '%s à¤ªà¤¾à¤°à¥�à¤¸ à¤•à¤¿à¤¯à¤¾ à¤œà¤¾ à¤°à¤¹à¤¾ à¤¹à¥ˆ...',
             'file_type_invalid' => 'à¤…à¤®à¤¾à¤¨à¥�à¤¯ à¤«à¤¼à¤¾à¤‡à¤² à¤Ÿà¤¾à¤‡à¤ª à¤•à¤¾ à¤ªà¤¤à¤¾ à¤šà¤²à¤¾ (%s)',
             'fingerprint_invalid' => 'à¤…à¤®à¤¾à¤¨à¥�à¤¯ à¤•à¥�à¤‚à¤œà¥€ à¤«à¤¼à¤¿à¤‚à¤—à¤°à¤ªà¥�à¤°à¤¿à¤‚à¤Ÿ!',
             'go' => 'à¤†à¤—à¥‡ à¤¬à¤¢à¤¼à¥‡à¤‚',
             'invalid_dl.php_session' => '%s à¤�à¤• à¤®à¤¾à¤¨à¥�à¤¯ à¤¡à¤¾à¤‰à¤¨à¤²à¥‹à¤¡ à¤¸à¥�à¤•à¥�à¤°à¤¿à¤ªà¥�à¤Ÿ à¤¨à¤¹à¥€à¤‚ à¤¹à¥ˆ à¤•à¥�à¤¯à¥‹à¤‚à¤•à¤¿ à¤‡à¤¸à¤•à¤¾ à¤¸à¥‡à¤¶à¤¨ ID à¤…à¤²à¤— à¤¹à¥ˆ (à¤…à¤ªà¥‡à¤•à¥�à¤·à¤¿à¤¤ %s, à¤ªà¥�à¤°à¤¾à¤ªà¥�à¤¤ %s)',
             'ipc_tcp_warning' => 'UNIX sockets are not available, so the IPC server of the session listens on TCP (127.0.0.1) instead: any program running on this machine can connect to it, and control the session. On Windows 10 1803+, enable the sockets extension (extension=sockets in php.ini) to use UNIX sockets.',
             'length_too_big' => 'à¤²à¤‚à¤¬à¤¾à¤ˆ à¤¬à¤¹à¥�à¤¤ à¤…à¤§à¤¿à¤• à¤¹à¥ˆ',
             'loginBot' => 'à¤…à¤ªà¤¨à¤¾ à¤¬à¥‰à¤Ÿ à¤Ÿà¥‹à¤•à¤¨ à¤¦à¤°à¥�à¤œ à¤•à¤°à¥‡à¤‚: ',
             'loginBotTokenHintWeb' => 'à¤¬à¥‰à¤Ÿ à¤¬à¤¨à¤¾à¤¤à¥‡ à¤¸à¤®à¤¯ @BotFather à¤¦à¥�à¤µà¤¾à¤°à¤¾ à¤¦à¤¿à¤¯à¤¾ à¤—à¤¯à¤¾ à¤Ÿà¥‹à¤•à¤¨à¥¤',
             'loginBotTokenWeb' => 'à¤¬à¥‰à¤Ÿ à¤Ÿà¥‹à¤•à¤¨',
             'loginChoosePromptWeb' => 'à¤•à¥�à¤¯à¤¾ à¤†à¤ª à¤¯à¥‚à¤œà¤¼à¤° à¤•à¥‡ à¤°à¥‚à¤ª à¤®à¥‡à¤‚ à¤²à¥‰à¤—à¤¿à¤¨ à¤•à¤°à¤¨à¤¾ à¤šà¤¾à¤¹à¤¤à¥‡ à¤¹à¥ˆà¤‚ à¤¯à¤¾ à¤¬à¥‰à¤Ÿ à¤•à¥‡ à¤°à¥‚à¤ª à¤®à¥‡à¤‚?',
             'loginCodeLeadWeb' => 'Telegram à¤¨à¥‡ à¤†à¤ªà¤•à¥‡ Telegram à¤�à¤ª à¤ªà¤° à¤�à¤• à¤²à¥‰à¤—à¤¿à¤¨ à¤•à¥‹à¤¡ à¤­à¥‡à¤œà¤¾ à¤¹à¥ˆ, à¤¯à¤¾ à¤¯à¤¦à¤¿ à¤†à¤ª à¤•à¤¹à¥€à¤‚ à¤”à¤° à¤²à¥‰à¤— à¤‡à¤¨ à¤¨à¤¹à¥€à¤‚ à¤¹à¥ˆà¤‚ à¤¤à¥‹ SMS à¤¸à¥‡à¥¤',
             'loginCodeTitleWeb' => 'à¤²à¥‰à¤—à¤¿à¤¨ à¤•à¥‹à¤¡ à¤¦à¤°à¥�à¤œ à¤•à¤°à¥‡à¤‚',
             'loginLeadWeb' => 'à¤µà¤¹ à¤…à¤•à¤¾à¤‰à¤‚à¤Ÿ à¤¯à¤¾ à¤¬à¥‰à¤Ÿ à¤šà¥�à¤¨à¥‡à¤‚ à¤œà¤¿à¤¸à¤•à¥‡ à¤°à¥‚à¤ª à¤®à¥‡à¤‚ à¤¯à¤¹ MadelineProto à¤‡à¤‚à¤¸à¥�à¤Ÿà¥‡à¤‚à¤¸ à¤šà¤²à¥‡à¤—à¤¾à¥¤',
             'loginManual' => 'à¤µà¥ˆà¤•à¤²à¥�à¤ªà¤¿à¤• à¤°à¥‚à¤ª à¤¸à¥‡, à¤†à¤ª à¤®à¥ˆà¤¨à¥�à¤¯à¥�à¤…à¤² à¤°à¥‚à¤ª à¤¸à¥‡ à¤²à¥‰à¤—à¤¿à¤¨ à¤•à¤°à¤¨à¥‡ à¤•à¥‡ à¤²à¤¿à¤� à¤¬à¥‰à¤Ÿ à¤Ÿà¥‹à¤•à¤¨ à¤¯à¤¾ à¤«à¤¼à¥‹à¤¨ à¤¨à¤‚à¤¬à¤° à¤­à¥€ à¤¦à¤°à¥�à¤œ à¤•à¤° à¤¸à¤•à¤¤à¥‡ à¤¹à¥ˆà¤‚: ',
             'loginNoCode' => 'à¤†à¤ªà¤¨à¥‡ à¤«à¤¼à¥‹à¤¨ à¤•à¥‹à¤¡ à¤¨à¤¹à¥€à¤‚ à¤¦à¤¿à¤¯à¤¾!',
             'loginNoName' => 'à¤†à¤ªà¤¨à¥‡ à¤ªà¤¹à¤²à¤¾ à¤¨à¤¾à¤® à¤¨à¤¹à¥€à¤‚ à¤¦à¤¿à¤¯à¤¾!',
             'loginNoPass' => 'à¤†à¤ªà¤¨à¥‡ à¤ªà¤¾à¤¸à¤µà¤°à¥�à¤¡ à¤¨à¤¹à¥€à¤‚ à¤¦à¤¿à¤¯à¤¾!',
             'loginOptionBot' => 'à¤¬à¥‰à¤Ÿ',
             'loginOptionUser' => 'à¤¯à¥‚à¤œà¤¼à¤°',
             'loginPasswordLabelWeb' => 'à¤ªà¤¾à¤¸à¤µà¤°à¥�à¤¡',
             'loginPasswordLeadWeb' => 'à¤¯à¤¹ à¤…à¤•à¤¾à¤‰à¤‚à¤Ÿ à¤�à¤• à¤ªà¤¾à¤¸à¤µà¤°à¥�à¤¡ à¤¸à¥‡ à¤¸à¥�à¤°à¤•à¥�à¤·à¤¿à¤¤ à¤¹à¥ˆ, à¤œà¥‹ Settings > Privacy and Security > Two-Step Verification à¤®à¥‡à¤‚ à¤¸à¥‡à¤Ÿ à¤•à¤¿à¤¯à¤¾ à¤—à¤¯à¤¾ à¤¹à¥ˆà¥¤',
             'loginPasswordTitleWeb' => 'à¤Ÿà¥‚-à¤¸à¥�à¤Ÿà¥‡à¤ª à¤µà¥‡à¤°à¤¿à¤«à¤¼à¤¿à¤•à¥‡à¤¶à¤¨',
             'loginQr' => 'à¤…à¤ªà¤¨à¥‡ à¤«à¤¼à¥‹à¤¨ à¤ªà¤° Telegram à¤–à¥‹à¤²à¥‡à¤‚, Settings > Devices > Link Desktop Device à¤ªà¤° à¤œà¤¾à¤�à¤� à¤”à¤° à¤…à¤ªà¤¨à¥‡-à¤†à¤ª à¤²à¥‰à¤—à¤¿à¤¨ à¤•à¤°à¤¨à¥‡ à¤•à¥‡ à¤²à¤¿à¤� à¤Šà¤ªà¤° à¤¦à¤¿à¤¯à¤¾ à¤—à¤¯à¤¾ QR à¤•à¥‹à¤¡ à¤¸à¥�à¤•à¥ˆà¤¨ à¤•à¤°à¥‡à¤‚à¥¤',
             'loginQrCodeExpired' => 'QR à¤•à¥‹à¤¡ à¤•à¥€ à¤¸à¤®à¤¯-à¤¸à¥€à¤®à¤¾ à¤¸à¤®à¤¾à¤ªà¥�à¤¤ à¤¹à¥‹ à¤—à¤ˆ, à¤¨à¤¯à¤¾ à¤•à¥‹à¤¡ à¤¬à¤¨à¤¾à¤¯à¤¾ à¤œà¤¾ à¤°à¤¹à¤¾ à¤¹à¥ˆ...',
             'loginQrCodeSuccessful' => 'QR à¤•à¥‹à¤¡ à¤¸à¥‡ à¤²à¥‰à¤—à¤¿à¤¨ à¤¸à¤«à¤² à¤°à¤¹à¤¾!',
             'loginTitleWeb' => 'Telegram à¤®à¥‡à¤‚ à¤²à¥‰à¤—à¤¿à¤¨ à¤•à¤°à¥‡à¤‚',
             'loginTypeWeb' => 'à¤‡à¤¸ à¤°à¥‚à¤ª à¤®à¥‡à¤‚ à¤²à¥‰à¤—à¤¿à¤¨ à¤•à¤°à¥‡à¤‚',
             'loginUser' => 'à¤…à¤ªà¤¨à¤¾ à¤«à¤¼à¥‹à¤¨ à¤¨à¤‚à¤¬à¤° à¤¦à¤°à¥�à¤œ à¤•à¤°à¥‡à¤‚: ',
             'loginUserCode' => 'à¤•à¥‹à¤¡ à¤¦à¤°à¥�à¤œ à¤•à¤°à¥‡à¤‚: ',
             'loginUserPass' => 'à¤…à¤ªà¤¨à¤¾ à¤ªà¤¾à¤¸à¤µà¤°à¥�à¤¡ à¤¦à¤°à¥�à¤œ à¤•à¤°à¥‡à¤‚ (à¤¸à¤‚à¤•à¥‡à¤¤ %s): ',
             'loginUserPassHint' => 'à¤¸à¤‚à¤•à¥‡à¤¤: %s',
             'loginUserPassWeb' => 'à¤…à¤ªà¤¨à¤¾ à¤ªà¤¾à¤¸à¤µà¤°à¥�à¤¡ à¤¦à¤°à¥�à¤œ à¤•à¤°à¥‡à¤‚: ',
             'loginUserPhoneCodeWeb' => 'à¤•à¥‹à¤¡',
             'loginUserPhoneHintWeb' => 'à¤¦à¥‡à¤¶ à¤•à¥‹à¤¡ à¤•à¥‡ à¤¸à¤¾à¤¥, à¤œà¥ˆà¤¸à¥‡ +39 123 456 7890à¥¤',
             'loginUserPhoneWeb' => 'à¤«à¤¼à¥‹à¤¨ à¤¨à¤‚à¤¬à¤°',
             'loginWebQr' => 'à¤†à¤ª à¤…à¤ªà¤¨à¥‡ à¤«à¤¼à¥‹à¤¨ à¤ªà¤° Telegram à¤–à¥‹à¤²à¤•à¤°, Settings > Devices > Link Desktop Device à¤ªà¤° à¤œà¤¾à¤•à¤° à¤”à¤° à¤¨à¤¿à¤®à¥�à¤¨à¤²à¤¿à¤–à¤¿à¤¤ QR à¤•à¥‹à¤¡ à¤¸à¥�à¤•à¥ˆà¤¨ à¤•à¤°à¤•à¥‡ à¤…à¤ªà¤¨à¥‡-à¤†à¤ª à¤­à¥€ à¤²à¥‰à¤—à¤¿à¤¨ à¤•à¤° à¤¸à¤•à¤¤à¥‡ à¤¹à¥ˆà¤‚:',
             'loginWebQr1' => 'à¤…à¤ªà¤¨à¥‡ à¤«à¤¼à¥‹à¤¨ à¤ªà¤° Telegram à¤–à¥‹à¤²à¥‡à¤‚',
             'loginWebQr2' => 'Settings > Devices > Link Desktop Device à¤ªà¤° à¤œà¤¾à¤�à¤�',
             'loginWebQr3' => 'à¤²à¥‰à¤—à¤¿à¤¨ à¤•à¥€ à¤ªà¥�à¤·à¥�à¤Ÿà¤¿ à¤•à¤°à¤¨à¥‡ à¤•à¥‡ à¤²à¤¿à¤� à¤…à¤ªà¤¨à¥‡ à¤«à¤¼à¥‹à¤¨ à¤•à¥‹ à¤‡à¤¸ à¤¸à¥�à¤•à¥�à¤°à¥€à¤¨ à¤•à¥€ à¤“à¤° à¤•à¤°à¥‡à¤‚',
             'login_2fa_enabled' => '2FA à¤¸à¤•à¥�à¤·à¤® à¤¹à¥ˆ, à¤†à¤ªà¤•à¥‹ complete2falogin à¤«à¤¼à¤‚à¤•à¥�à¤¶à¤¨ à¤•à¥‰à¤² à¤•à¤°à¤¨à¤¾ à¤¹à¥‹à¤—à¤¾...',
             'login_auth_key' => 'auth key à¤•à¤¾ à¤‰à¤ªà¤¯à¥‹à¤— à¤•à¤°à¤•à¥‡ à¤²à¥‰à¤— à¤‡à¤¨ à¤•à¤¿à¤¯à¤¾ à¤œà¤¾ à¤°à¤¹à¤¾ à¤¹à¥ˆ...',
             'login_bot' => 'à¤¬à¥‰à¤Ÿ à¤•à¥‡ à¤°à¥‚à¤ª à¤®à¥‡à¤‚ à¤²à¥‰à¤— à¤‡à¤¨ à¤•à¤¿à¤¯à¤¾ à¤œà¤¾ à¤°à¤¹à¤¾ à¤¹à¥ˆ...',
             'login_code_sending' => 'à¤•à¥‹à¤¡ à¤­à¥‡à¤œà¤¾ à¤œà¤¾ à¤°à¤¹à¤¾ à¤¹à¥ˆ...',
             'login_code_sent' => 'à¤•à¥‹à¤¡ à¤¸à¤«à¤²à¤¤à¤¾à¤ªà¥‚à¤°à¥�à¤µà¤• à¤­à¥‡à¤œ à¤¦à¤¿à¤¯à¤¾ à¤—à¤¯à¤¾! à¤•à¥‹à¤¡ à¤ªà¥�à¤°à¤¾à¤ªà¥�à¤¤ à¤¹à¥‹à¤¨à¥‡ à¤•à¥‡ à¤¬à¤¾à¤¦ à¤†à¤ªà¤•à¥‹ completePhoneLogin à¤«à¤¼à¤‚à¤•à¥�à¤¶à¤¨ à¤•à¤¾ à¤‰à¤ªà¤¯à¥‹à¤— à¤•à¤°à¤¨à¤¾ à¤šà¤¾à¤¹à¤¿à¤�à¥¤',
             'login_code_uncalled' => 'à¤®à¥ˆà¤‚ à¤•à¥‹à¤¡ à¤•à¥€ à¤ªà¥�à¤°à¤¤à¥€à¤•à¥�à¤·à¤¾ à¤¨à¤¹à¥€à¤‚ à¤•à¤° à¤°à¤¹à¤¾ à¤¹à¥‚à¤�! à¤•à¥ƒà¤ªà¤¯à¤¾ à¤ªà¤¹à¤²à¥‡ phoneLogin à¤®à¥‡à¤¥à¤¡ à¤•à¥‰à¤² à¤•à¤°à¥‡à¤‚',
             'login_need_signup' => 'à¤‡à¤¸ à¤¨à¤‚à¤¬à¤° à¤•à¥‡ à¤²à¤¿à¤� à¤•à¥‹à¤ˆ à¤…à¤•à¤¾à¤‰à¤‚à¤Ÿ à¤¨à¤¹à¥€à¤‚ à¤¬à¤¨à¤¾à¤¯à¤¾ à¤—à¤¯à¤¾ à¤¹à¥ˆ, à¤†à¤ªà¤•à¥‹ completeSignup à¤«à¤¼à¤‚à¤•à¥�à¤¶à¤¨ à¤•à¥‰à¤² à¤•à¤°à¤¨à¤¾ à¤¹à¥‹à¤—à¤¾...',
             'login_ok' => 'à¤¸à¤«à¤²à¤¤à¤¾à¤ªà¥‚à¤°à¥�à¤µà¤• à¤²à¥‰à¤— à¤‡à¤¨ à¤¹à¥‹ à¤—à¤¯à¤¾!',
             'login_user' => 'à¤¸à¤¾à¤®à¤¾à¤¨à¥�à¤¯ à¤¯à¥‚à¤œà¤¼à¤° à¤•à¥‡ à¤°à¥‚à¤ª à¤®à¥‡à¤‚ à¤²à¥‰à¤— à¤‡à¤¨ à¤•à¤¿à¤¯à¤¾ à¤œà¤¾ à¤°à¤¹à¤¾ à¤¹à¥ˆ...',
             'logout_ok' => 'à¤¸à¤«à¤²à¤¤à¤¾à¤ªà¥‚à¤°à¥�à¤µà¤• à¤²à¥‰à¤— à¤†à¤‰à¤Ÿ à¤¹à¥‹ à¤—à¤¯à¤¾!',
             'long_not_16' => 'à¤¦à¤¿à¤¯à¤¾ à¤—à¤¯à¤¾ à¤®à¤¾à¤¨ 16 à¤¬à¤¾à¤‡à¤Ÿ à¤²à¤‚à¤¬à¤¾ à¤¨à¤¹à¥€à¤‚ à¤¹à¥ˆ',
             'long_not_32' => 'à¤¦à¤¿à¤¯à¤¾ à¤—à¤¯à¤¾ à¤®à¤¾à¤¨ 32 à¤¬à¤¾à¤‡à¤Ÿ à¤²à¤‚à¤¬à¤¾ à¤¨à¤¹à¥€à¤‚ à¤¹à¥ˆ',
             'long_not_64' => 'à¤¦à¤¿à¤¯à¤¾ à¤—à¤¯à¤¾ à¤®à¤¾à¤¨ 64 à¤¬à¤¾à¤‡à¤Ÿ à¤²à¤‚à¤¬à¤¾ à¤¨à¤¹à¥€à¤‚ à¤¹à¥ˆ',
             'madelineproto_ready' => 'MadelineProto à¤¤à¥ˆà¤¯à¤¾à¤° à¤¹à¥ˆ!',
             'manualAdminActionRequired' => '!!!!!!!!! à¤¸à¤¿à¤¸à¥�à¤Ÿà¤® à¤�à¤¡à¤®à¤¿à¤¨ à¤¦à¥�à¤µà¤¾à¤°à¤¾ à¤®à¥ˆà¤¨à¥�à¤¯à¥�à¤…à¤² à¤•à¤¾à¤°à¥�à¤°à¤µà¤¾à¤ˆ à¤†à¤µà¤¶à¥�à¤¯à¤• à¤¹à¥ˆ !!!!!!!!!',
             'method_not_found' => 'à¤®à¥‡à¤¥à¤¡ à¤¨à¤¹à¥€à¤‚ à¤®à¤¿à¤²à¤¾: ',
             'mmapErrorPart1' => 'à¤®à¥‡à¤®à¥‹à¤°à¥€ à¤®à¥ˆà¤ªà¥�à¤¡ (mmap) à¤•à¥�à¤·à¥‡à¤¤à¥�à¤°à¥‹à¤‚ à¤•à¥€ à¤…à¤§à¤¿à¤•à¤¤à¤® à¤¸à¤‚à¤–à¥�à¤¯à¤¾ à¤ªà¥‚à¤°à¥€ à¤¹à¥‹ à¤—à¤ˆ à¤¹à¥ˆ (%s): à¤‡à¤¸à¥‡ à¤ à¥€à¤• à¤•à¤°à¤¨à¥‡ à¤•à¥‡ à¤²à¤¿à¤� à¤•à¥ƒà¤ªà¤¯à¤¾ à¤•à¤°à¥�à¤¨à¥‡à¤² à¤•à¥‰à¤¨à¥�à¤«à¤¼à¤¿à¤— vm.max_map_count à¤•à¥‹ à¤¬à¤¢à¤¼à¤¾à¤•à¤° 262144 à¤•à¤°à¥‡à¤‚à¥¤',
             'mmapErrorPart2' => 'à¤ à¥€à¤• à¤•à¤°à¤¨à¥‡ à¤•à¥‡ à¤²à¤¿à¤�, à¤¨à¤¿à¤®à¥�à¤¨à¤²à¤¿à¤–à¤¿à¤¤ à¤•à¤®à¤¾à¤‚à¤¡ à¤•à¥‹ root à¤•à¥‡ à¤°à¥‚à¤ª à¤®à¥‡à¤‚ à¤šà¤²à¤¾à¤�à¤�: %s',
             'mmapErrorPart3' => 'à¤°à¥€à¤¬à¥‚à¤Ÿ à¤•à¥‡ à¤¬à¤¾à¤¦ à¤­à¥€ à¤¬à¤¦à¤²à¤¾à¤µ à¤¬à¤¨à¤¾à¤� à¤°à¤–à¤¨à¥‡ à¤•à¥‡ à¤²à¤¿à¤�: %s',
             'mmapErrorPart4' => 'Windows à¤”à¤° WSL à¤ªà¤°, pagefile à¤•à¤¾ à¤†à¤•à¤¾à¤° à¤¬à¤¢à¤¼à¤¾à¤¨à¥‡ à¤¸à¥‡ à¤®à¤¦à¤¦ à¤®à¤¿à¤² à¤¸à¤•à¤¤à¥€ à¤¹à¥ˆ; à¤¯à¤¦à¤¿ à¤¸à¤®à¤¸à¥�à¤¯à¤¾ à¤¬à¤¨à¥€ à¤°à¤¹à¤¤à¥€ à¤¹à¥ˆ à¤¤à¥‹ à¤•à¥ƒà¤ªà¤¯à¤¾ à¤¨à¥‡à¤Ÿà¤¿à¤µ Linux à¤ªà¤° à¤¸à¥�à¤µà¤¿à¤š à¤•à¤°à¥‡à¤‚à¥¤',
             'must_have_declare_types' => 'à¤ªà¤°à¤«à¤¼à¥‰à¤°à¥�à¤®à¥‡à¤‚à¤¸ à¤•à¤¾à¤°à¤£à¥‹à¤‚ à¤¸à¥‡, à¤‡à¤µà¥‡à¤‚à¤Ÿ à¤¹à¥ˆà¤‚à¤¡à¤²à¤° à¤«à¤¼à¤¾à¤‡à¤² à¤•à¤¾ à¤ªà¤¹à¤²à¤¾ à¤¸à¥�à¤Ÿà¥‡à¤Ÿà¤®à¥‡à¤‚à¤Ÿ "declare(strict_types=1);" à¤¹à¥‹à¤¨à¤¾ à¤šà¤¾à¤¹à¤¿à¤�',
             'nearest_dc' => 'à¤¹à¤® %s à¤®à¥‡à¤‚ à¤¹à¥ˆà¤‚, à¤¨à¤¿à¤•à¤Ÿà¤¤à¤® DC %d à¤¹à¥ˆà¥¤',
             'need_dl.php' => 'à¤¡à¤¿à¤«à¤¼à¥‰à¤²à¥�à¤Ÿ à¤¡à¤¾à¤‰à¤¨à¤²à¥‹à¤¡ à¤¸à¥�à¤•à¥�à¤°à¤¿à¤ªà¥�à¤Ÿ à¤¨à¤¹à¥€à¤‚ à¤¬à¤¨à¤¾à¤ˆ à¤œà¤¾ à¤¸à¤•à¥€ (%s), à¤•à¥ƒà¤ªà¤¯à¤¾ à¤¨à¤¿à¤®à¥�à¤¨à¤²à¤¿à¤–à¤¿à¤¤ à¤¸à¤¾à¤®à¤—à¥�à¤°à¥€ à¤•à¥‡ à¤¸à¤¾à¤¥ à¤�à¤• dl.php à¤«à¤¼à¤¾à¤‡à¤² à¤¬à¤¨à¤¾à¤�à¤�: %s à¤”à¤° à¤‰à¤¸à¤•à¤¾ URL getDownloadLink à¤•à¥‡ à¤¦à¥‚à¤¸à¤°à¥‡ à¤ªà¥ˆà¤°à¤¾à¤®à¥€à¤Ÿà¤° à¤®à¥‡à¤‚ à¤ªà¤¾à¤¸ à¤•à¤°à¥‡à¤‚',
             'noReportPeers' => 'à¤šà¥‡à¤¤à¤¾à¤µà¤¨à¥€: à¤•à¥‹à¤ˆ à¤°à¤¿à¤ªà¥‹à¤°à¥�à¤Ÿ à¤ªà¥€à¤¯à¤° à¤¸à¥‡à¤Ÿ à¤¨à¤¹à¥€à¤‚ à¤¹à¥ˆ, à¤•à¥ƒà¤ªà¤¯à¤¾ à¤…à¤ªà¤¨à¥‡ à¤‡à¤µà¥‡à¤‚à¤Ÿ à¤¹à¥ˆà¤‚à¤¡à¤²à¤° à¤®à¥‡à¤‚ à¤¨à¤¿à¤®à¥�à¤¨à¤²à¤¿à¤–à¤¿à¤¤ à¤®à¥‡à¤¥à¤¡ à¤œà¥‹à¤¡à¤¼à¥‡à¤‚',
             'non_text_conversion' => 'à¤—à¥ˆà¤°-à¤Ÿà¥‡à¤•à¥�à¤¸à¥�à¤Ÿ à¤¸à¤‚à¤¦à¥‡à¤¶à¥‹à¤‚ à¤•à¥‹ à¤…à¤­à¥€ à¤•à¤¨à¥�à¤µà¤°à¥�à¤Ÿ à¤¨à¤¹à¥€à¤‚ à¤•à¤¿à¤¯à¤¾ à¤œà¤¾ à¤¸à¤•à¤¤à¤¾!',
             'not_loggedIn' => 'à¤®à¥ˆà¤‚ à¤²à¥‰à¤— à¤‡à¤¨ à¤¨à¤¹à¥€à¤‚ à¤¹à¥‚à¤�!',
             'not_numeric' => 'à¤¦à¤¿à¤¯à¤¾ à¤—à¤¯à¤¾ à¤®à¤¾à¤¨ à¤¸à¤‚à¤–à¥�à¤¯à¤¾à¤¤à¥�à¤®à¤• à¤¨à¤¹à¥€à¤‚ à¤¹à¥ˆ',
             'params_missing' => 'à¤†à¤µà¤¶à¥�à¤¯à¤• à¤ªà¥ˆà¤°à¤¾à¤®à¥€à¤Ÿà¤° à¤®à¥Œà¤œà¥‚à¤¦ à¤¨à¤¹à¥€à¤‚ à¤¹à¥ˆ',
             'peer_not_in_db' => 'à¤¯à¤¹ à¤ªà¥€à¤¯à¤° à¤†à¤‚à¤¤à¤°à¤¿à¤• à¤ªà¥€à¤¯à¤° à¤¡à¥‡à¤Ÿà¤¾à¤¬à¥‡à¤¸ à¤®à¥‡à¤‚ à¤®à¥Œà¤œà¥‚à¤¦ à¤¨à¤¹à¥€à¤‚ à¤¹à¥ˆ',
             'plugin_path_does_not_exist' => 'à¤ªà¥�à¤²à¤—à¤‡à¤¨ à¤ªà¤¾à¤¥ %s à¤®à¥Œà¤œà¥‚à¤¦ à¤¨à¤¹à¥€à¤‚ à¤¹à¥ˆ!',
             'plugins_do_not_use_require' => 'à¤ªà¤°à¤«à¤¼à¥‰à¤°à¥�à¤®à¥‡à¤‚à¤¸ à¤•à¤¾à¤°à¤£à¥‹à¤‚ à¤¸à¥‡, à¤ªà¥�à¤²à¤—à¤‡à¤¨ plugins à¤«à¤¼à¥‹à¤²à¥�à¤¡à¤° à¤®à¥‡à¤‚ à¤®à¥Œà¤œà¥‚à¤¦ à¤…à¤¨à¥�à¤¯ à¤«à¤¼à¤¾à¤‡à¤²à¥‹à¤‚ à¤•à¥‹ à¤•à¥‡à¤µà¤² PSR-4 autoloader à¤•à¥‹ à¤Ÿà¥�à¤°à¤¿à¤—à¤° à¤•à¤°à¤•à¥‡ à¤¹à¥€ à¤…à¤ªà¤¨à¥‡-à¤†à¤ª include à¤¯à¤¾ require à¤•à¤° à¤¸à¤•à¤¤à¥‡ à¤¹à¥ˆà¤‚ (à¤‰à¤¨à¥�à¤¹à¥‡à¤‚ à¤®à¥ˆà¤¨à¥�à¤¯à¥�à¤…à¤² à¤°à¥‚à¤ª à¤¸à¥‡ require() à¤•à¤°à¤•à¥‡ à¤¨à¤¹à¥€à¤‚)à¥¤',
             'plugins_must_have_exactly_one_class' => 'à¤�à¤• à¤ªà¥�à¤²à¤—à¤‡à¤¨ à¤•à¥‹ à¤ à¥€à¤• à¤�à¤• à¤•à¥�à¤²à¤¾à¤¸ à¤ªà¤°à¤¿à¤­à¤¾à¤·à¤¿à¤¤ à¤•à¤°à¤¨à¥€ à¤šà¤¾à¤¹à¤¿à¤�! à¤•à¤ˆ à¤•à¥�à¤²à¤¾à¤¸, interface à¤¯à¤¾ trait à¤ªà¤°à¤¿à¤­à¤¾à¤·à¤¿à¤¤ à¤•à¤°à¤¨à¥‡ à¤•à¥‡ à¤²à¤¿à¤� à¤…à¤²à¤—-à¤…à¤²à¤— à¤«à¤¼à¤¾à¤‡à¤²à¥‡à¤‚ à¤¬à¤¨à¤¾à¤�à¤�, MadelineProto à¤‰à¤¨à¥�à¤¹à¥‡à¤‚ à¤…à¤ªà¤¨à¥‡-à¤†à¤ª autoload à¤•à¤° à¤²à¥‡à¤—à¤¾à¥¤',
             'predicate_not_set' => 'Predicate (_ à¤•à¥‡ à¤…à¤‚à¤¤à¤°à¥�à¤—à¤¤ à¤®à¤¾à¤¨) à¤¸à¥‡à¤Ÿ à¤¨à¤¹à¥€à¤‚ à¤•à¤¿à¤¯à¤¾ à¤—à¤¯à¤¾!',
             'recommend_not_use_filesystem_function' => '%s à¤«à¤¼à¤‚à¤•à¥�à¤¶à¤¨ à¤•à¤¾ à¤‰à¤ªà¤¯à¥‹à¤— à¤…à¤¨à¥�à¤¶à¤‚à¤¸à¤¿à¤¤ à¤¨à¤¹à¥€à¤‚ à¤¹à¥ˆ, à¤•à¥�à¤¯à¥‹à¤‚à¤•à¤¿ à¤…à¤ªà¤¡à¥‡à¤Ÿ à¤¹à¥ˆà¤‚à¤¡à¤² à¤•à¤°à¤¤à¥‡ à¤¸à¤®à¤¯ à¤«à¤¼à¤¾à¤‡à¤²à¤¸à¤¿à¤¸à¥�à¤Ÿà¤® à¤�à¤•à¥�à¤¸à¥‡à¤¸ à¤•à¤°à¤¨à¥‡ à¤¸à¥‡ à¤†à¤ªà¤•à¤¾ à¤¬à¥‰à¤Ÿ à¤§à¥€à¤®à¤¾ à¤¹à¥‹ à¤œà¤¾à¤�à¤—à¤¾, à¤¡à¥‡à¤Ÿà¤¾ à¤¸à¥�à¤Ÿà¥‹à¤° à¤•à¤°à¤¨à¥‡ à¤•à¥‡ à¤�à¤¸à¥‡ à¤µà¥ˆà¤•à¤²à¥�à¤ªà¤¿à¤• à¤¤à¤°à¥€à¤•à¥‹à¤‚ à¤•à¥€ à¤¸à¥‚à¤šà¥€ à¤•à¥‡ à¤²à¤¿à¤� à¤œà¥‹ à¤†à¤ªà¤•à¥‡ à¤¬à¥‰à¤Ÿ à¤•à¥‹ à¤§à¥€à¤®à¤¾ à¤¨à¤¹à¥€à¤‚ à¤•à¤°à¥‡à¤‚à¤—à¥‡, à¤•à¥ƒà¤ªà¤¯à¤¾ https://docs.madelineproto.xyz/docs/UPDATES.html#avoiding-the-use-of-filesystem-functions à¤¦à¥‡à¤–à¥‡à¤‚!',
             'rpc_tg_error' => 'Telegram à¤¨à¥‡ à¤�à¤• RPC à¤¤à¥�à¤°à¥�à¤Ÿà¤¿ à¤²à¥Œà¤Ÿà¤¾à¤ˆ: %s (%s), à¤œà¥‹ %s:%s à¤•à¥‡ à¤•à¤¾à¤°à¤£ à¤¹à¥�à¤ˆ%sTL à¤Ÿà¥�à¤°à¥‡à¤¸:',
             'sec_peer_not_in_db' => 'à¤¯à¤¹ à¤¸à¥€à¤•à¥�à¤°à¥‡à¤Ÿ à¤ªà¥€à¤¯à¤° à¤†à¤‚à¤¤à¤°à¤¿à¤• à¤ªà¥€à¤¯à¤° à¤¡à¥‡à¤Ÿà¤¾à¤¬à¥‡à¤¸ à¤®à¥‡à¤‚ à¤®à¥Œà¤œà¥‚à¤¦ à¤¨à¤¹à¥€à¤‚ à¤¹à¥ˆ',
             'secret_chat_skipping' => 'à¤¡à¥‡à¤Ÿà¤¾à¤¬à¥‡à¤¸ à¤®à¥‡à¤‚ à¤¸à¥€à¤•à¥�à¤°à¥‡à¤Ÿ à¤šà¥ˆà¤Ÿ %s à¤®à¥Œà¤œà¥‚à¤¦ à¤¨à¤¹à¥€à¤‚ à¤¹à¥ˆ, à¤¸à¤‚à¤¦à¥‡à¤¶ à¤›à¥‹à¤¡à¤¼à¤¾ à¤œà¤¾ à¤°à¤¹à¤¾ à¤¹à¥ˆ...',
             'serialization_ofd' => 'à¤¸à¥€à¤°à¤¿à¤¯à¤²à¤¾à¤‡à¤œà¤¼à¥‡à¤¶à¤¨ à¤ªà¥�à¤°à¤¾à¤¨à¤¾ à¤¹à¥‹ à¤—à¤¯à¤¾ à¤¹à¥ˆ, à¤‘à¤¬à¥�à¤œà¥‡à¤•à¥�à¤Ÿ à¤•à¥‹ à¤«à¤¿à¤° à¤¸à¥‡ à¤¬à¤¨à¤¾à¤¯à¤¾ à¤œà¤¾ à¤°à¤¹à¤¾ à¤¹à¥ˆ!',
             'session_corrupted' => 'à¤¸à¥‡à¤¶à¤¨ à¤¦à¥‚à¤·à¤¿à¤¤ à¤¹à¥‹ à¤—à¤¯à¤¾ à¤¹à¥ˆ!',
             'signing_up' => 'à¤¸à¤¾à¤®à¤¾à¤¨à¥�à¤¯ à¤¯à¥‚à¤œà¤¼à¤° à¤•à¥‡ à¤°à¥‚à¤ª à¤®à¥‡à¤‚ à¤¸à¤¾à¤‡à¤¨ à¤…à¤ª à¤•à¤¿à¤¯à¤¾ à¤œà¤¾ à¤°à¤¹à¤¾ à¤¹à¥ˆ...',
             'signupFirstName' => 'à¤…à¤ªà¤¨à¤¾ à¤ªà¤¹à¤²à¤¾ à¤¨à¤¾à¤® à¤¦à¤°à¥�à¤œ à¤•à¤°à¥‡à¤‚: ',
             'signupFirstNameWeb' => 'à¤ªà¤¹à¤²à¤¾ à¤¨à¤¾à¤®',
             'signupLastName' => 'à¤…à¤ªà¤¨à¤¾ à¤‰à¤ªà¤¨à¤¾à¤® à¤¦à¤°à¥�à¤œ à¤•à¤°à¥‡à¤‚ (à¤–à¤¾à¤²à¥€ à¤›à¥‹à¤¡à¤¼ à¤¸à¤•à¤¤à¥‡ à¤¹à¥ˆà¤‚): ',
             'signupLastNameWeb' => 'à¤‰à¤ªà¤¨à¤¾à¤®',
             'signupLeadWeb' => 'à¤¯à¤¹ à¤«à¤¼à¥‹à¤¨ à¤¨à¤‚à¤¬à¤° à¤…à¤­à¥€ Telegram à¤ªà¤° à¤¨à¤¹à¥€à¤‚ à¤¹à¥ˆ: à¤¨à¤� à¤…à¤•à¤¾à¤‰à¤‚à¤Ÿ à¤•à¤¾ à¤¨à¤¾à¤® à¤šà¥�à¤¨à¥‡à¤‚à¥¤',
             'signupTitleWeb' => 'à¤…à¤ªà¤¨à¤¾ à¤…à¤•à¤¾à¤‰à¤‚à¤Ÿ à¤¬à¤¨à¤¾à¤�à¤�',
             'signupWeb' => 'à¤•à¥ƒà¤ªà¤¯à¤¾ à¤¸à¤¾à¤‡à¤¨ à¤…à¤ª à¤•à¤°à¥‡à¤‚',
             'signup_ok' => 'à¤¸à¤«à¤²à¤¤à¤¾à¤ªà¥‚à¤°à¥�à¤µà¤• à¤¸à¤¾à¤‡à¤¨ à¤…à¤ª à¤¹à¥‹ à¤—à¤¯à¤¾!',
             'signup_uncalled' => 'à¤®à¥ˆà¤‚ à¤¸à¤¾à¤‡à¤¨ à¤…à¤ª à¤•à¥€ à¤ªà¥�à¤°à¤¤à¥€à¤•à¥�à¤·à¤¾ à¤¨à¤¹à¥€à¤‚ à¤•à¤° à¤°à¤¹à¤¾ à¤¹à¥‚à¤�! à¤•à¥ƒà¤ªà¤¯à¤¾ à¤ªà¤¹à¤²à¥‡ phoneLogin à¤”à¤° completePhoneLogin à¤®à¥‡à¤¥à¤¡ à¤•à¥‰à¤² à¤•à¤°à¥‡à¤‚!',
             'src_file_invalid' => 'à¤…à¤®à¤¾à¤¨à¥�à¤¯ à¤¸à¥‹à¤°à¥�à¤¸ à¤«à¤¼à¤¾à¤‡à¤² à¤¦à¥€ à¤—à¤ˆ: ',
             'static_analysis_minor' => '%s à¤•à¥‡ à¤¸à¥�à¤Ÿà¥ˆà¤Ÿà¤¿à¤• à¤�à¤¨à¤¾à¤²à¤¿à¤¸à¤¿à¤¸ à¤•à¥‡ à¤¦à¥Œà¤°à¤¾à¤¨ à¤�à¤• à¤›à¥‹à¤Ÿà¥€ à¤¸à¤®à¤¸à¥�à¤¯à¤¾ à¤®à¤¿à¤²à¥€: %s',
             'static_analysis_severe' => '%s à¤•à¥‡ à¤¸à¥�à¤Ÿà¥ˆà¤Ÿà¤¿à¤• à¤�à¤¨à¤¾à¤²à¤¿à¤¸à¤¿à¤¸ à¤•à¥‡ à¤¦à¥Œà¤°à¤¾à¤¨ à¤�à¤• à¤—à¤‚à¤­à¥€à¤° à¤¸à¤®à¤¸à¥�à¤¯à¤¾ à¤®à¤¿à¤²à¥€: %s',
             'stream_handle_invalid' => 'à¤�à¤• à¤…à¤®à¤¾à¤¨à¥�à¤¯ à¤¸à¥�à¤Ÿà¥�à¤°à¥€à¤® à¤¹à¥ˆà¤‚à¤¡à¤² à¤¦à¤¿à¤¯à¤¾ à¤—à¤¯à¤¾à¥¤',
             'string_required' => 'à¤�à¤• string à¤…à¤ªà¥‡à¤•à¥�à¤·à¤¿à¤¤ à¤¥à¥€!',
             'translate_madelineproto_cli' => 'MadelineProto à¤•à¤¾ à¤†à¤ªà¤•à¥€ à¤­à¤¾à¤·à¤¾ à¤®à¥‡à¤‚ à¤…à¤¨à¥�à¤µà¤¾à¤¦ à¤•à¤¿à¤¯à¤¾ à¤œà¤¾ à¤¸à¤•à¤¤à¤¾ à¤¹à¥ˆ (à¤µà¤°à¥�à¤¤à¤®à¤¾à¤¨ à¤…à¤¨à¥�à¤µà¤¾à¤¦ à¤ªà¥�à¤°à¤—à¤¤à¤¿: %d%%), à¤…à¤¨à¥�à¤µà¤¾à¤¦ à¤®à¥‡à¤‚ à¤¯à¥‹à¤—à¤¦à¤¾à¤¨ à¤¦à¥‡à¤¨à¥‡ à¤•à¥‡ à¤²à¤¿à¤� https://github.com/danog/MadelineProto/tree/v8/langs à¤ªà¤° à¤œà¤¾à¤�à¤�!',
             'translate_madelineproto_web' => 'MadelineProto à¤•à¤¾ à¤†à¤ªà¤•à¥€ à¤­à¤¾à¤·à¤¾ à¤®à¥‡à¤‚ à¤…à¤¨à¥�à¤µà¤¾à¤¦ à¤•à¤¿à¤¯à¤¾ à¤œà¤¾ à¤¸à¤•à¤¤à¤¾ à¤¹à¥ˆ (à¤µà¤°à¥�à¤¤à¤®à¤¾à¤¨ à¤…à¤¨à¥�à¤µà¤¾à¤¦ à¤ªà¥�à¤°à¤—à¤¤à¤¿: %d%%), <a href="https://github.com/danog/MadelineProto/tree/v8/langs" target="_blank">à¤…à¤¨à¥�à¤µà¤¾à¤¦ à¤®à¥‡à¤‚ à¤¯à¥‹à¤—à¤¦à¤¾à¤¨ à¤¦à¥‡à¤¨à¥‡ à¤•à¥‡ à¤²à¤¿à¤� à¤¯à¤¹à¤¾à¤� à¤•à¥�à¤²à¤¿à¤• à¤•à¤°à¥‡à¤‚!</a>',
             'tweb.ACCESS_TOKEN_EXPIRED' => 'à¤¯à¤¹ à¤¬à¥‰à¤Ÿ à¤Ÿà¥‹à¤•à¤¨ à¤°à¤¦à¥�à¤¦ à¤•à¤° à¤¦à¤¿à¤¯à¤¾ à¤—à¤¯à¤¾ à¤¹à¥ˆ',
             'tweb.ACCESS_TOKEN_INVALID' => 'à¤…à¤®à¤¾à¤¨à¥�à¤¯ à¤¬à¥‰à¤Ÿ à¤Ÿà¥‹à¤•à¤¨',
             'tweb.AccDescr.LoginCode' => 'à¤²à¥‰à¤—à¤¿à¤¨ à¤•à¥‹à¤¡',
             'tweb.Close' => 'à¤¬à¤‚à¤¦ à¤•à¤°à¥‡à¤‚',
             'tweb.Code' => 'à¤•à¥‹à¤¡',
             'tweb.Contacts.PhoneNumber.Placeholder' => 'à¤«à¤¼à¥‹à¤¨ à¤¨à¤‚à¤¬à¤°',
             'tweb.Country' => 'à¤¦à¥‡à¤¶',
             'tweb.DarkMode' => 'à¤¡à¤¾à¤°à¥�à¤• à¤®à¥‹à¤¡',
             'tweb.Error.AnError' => 'à¤�à¤• à¤¤à¥�à¤°à¥�à¤Ÿà¤¿ à¤¹à¥�à¤ˆà¥¤ à¤•à¥ƒà¤ªà¤¯à¤¾ à¤¬à¤¾à¤¦ à¤®à¥‡à¤‚ à¤«à¤¿à¤° à¤¸à¥‡ à¤ªà¥�à¤°à¤¯à¤¾à¤¸ à¤•à¤°à¥‡à¤‚à¥¤',
             'tweb.FIRSTNAME_INVALID' => 'à¤…à¤®à¤¾à¤¨à¥�à¤¯ à¤ªà¤¹à¤²à¤¾ à¤¨à¤¾à¤®',
             'tweb.FirstName' => 'à¤ªà¤¹à¤²à¤¾ à¤¨à¤¾à¤® (à¤†à¤µà¤¶à¥�à¤¯à¤•)',
             'tweb.LASTNAME_INVALID' => 'à¤…à¤®à¤¾à¤¨à¥�à¤¯ à¤‰à¤ªà¤¨à¤¾à¤®',
             'tweb.LastName' => 'à¤‰à¤ªà¤¨à¤¾à¤® (à¤µà¥ˆà¤•à¤²à¥�à¤ªà¤¿à¤•)',
             'tweb.Login.Bot' => 'à¤¬à¥‰à¤Ÿ à¤•à¥‡ à¤°à¥‚à¤ª à¤®à¥‡à¤‚ à¤²à¥‰à¤—à¤¿à¤¨ à¤•à¤°à¥‡à¤‚ >',
             'tweb.Login.Bot.Cancel' => 'à¤¯à¥‚à¤œà¤¼à¤° à¤•à¥‡ à¤°à¥‚à¤ª à¤®à¥‡à¤‚ à¤²à¥‰à¤—à¤¿à¤¨ à¤•à¤°à¥‡à¤‚ >',
             'tweb.Login.Bot.Subtitle' => 'à¤¬à¥‰à¤Ÿ à¤¬à¤¨à¤¾à¤¤à¥‡ à¤¸à¤®à¤¯ @BotFather à¤¦à¥�à¤µà¤¾à¤°à¤¾
à¤¦à¤¿à¤¯à¤¾ à¤—à¤¯à¤¾ à¤Ÿà¥‹à¤•à¤¨ à¤¦à¤°à¥�à¤œ à¤•à¤°à¥‡à¤‚à¥¤',
             'tweb.Login.Bot.Title' => 'à¤¬à¥‰à¤Ÿ à¤•à¥‡ à¤°à¥‚à¤ª à¤®à¥‡à¤‚ à¤²à¥‰à¤—à¤¿à¤¨ à¤•à¤°à¥‡à¤‚',
             'tweb.Login.Bot.Token' => 'à¤¬à¥‰à¤Ÿ à¤Ÿà¥‹à¤•à¤¨',
             'tweb.Login.Code.SecretPhrase' => 'à¤—à¥�à¤ªà¥�à¤¤ à¤µà¤¾à¤•à¥�à¤¯à¤¾à¤‚à¤¶',
             'tweb.Login.Code.SecretWord' => 'à¤—à¥�à¤ªà¥�à¤¤ à¤¶à¤¬à¥�à¤¦',
             'tweb.Login.Code.SentCall' => 'à¤¹à¤® à¤†à¤ªà¤•à¥‹ à¤•à¥‰à¤² à¤•à¤°à¤•à¥‡
à¤•à¥‹à¤¡ à¤¬à¤¤à¤¾à¤�à¤�à¤—à¥‡à¥¤',
             'tweb.Login.Code.SentEmail' => 'à¤•à¥ƒà¤ªà¤¯à¤¾ à¤…à¤ªà¤¨à¤¾ à¤ˆà¤®à¥‡à¤² **%s** à¤œà¤¾à¤�à¤šà¥‡à¤‚ (à¤¸à¥�à¤ªà¥ˆà¤® à¤«à¤¼à¥‹à¤²à¥�à¤¡à¤° à¤¦à¥‡à¤–à¤¨à¤¾ à¤¨ à¤­à¥‚à¤²à¥‡à¤‚) à¤”à¤° à¤¹à¤®à¤¾à¤°à¥‡ à¤¦à¥�à¤µà¤¾à¤°à¤¾ à¤…à¤­à¥€ à¤­à¥‡à¤œà¤¾ à¤—à¤¯à¤¾ à¤•à¥‹à¤¡ à¤¦à¤°à¥�à¤œ à¤•à¤°à¥‡à¤‚à¥¤',
             'tweb.Login.Code.SentInApp' => 'à¤¹à¤®à¤¨à¥‡ à¤†à¤ªà¤•à¥‹ Telegram à¤®à¥‡à¤‚ à¤•à¥‹à¤¡ à¤•à¥‡ à¤¸à¤¾à¤¥
à¤�à¤• à¤¸à¤‚à¤¦à¥‡à¤¶ à¤­à¥‡à¤œà¤¾ à¤¹à¥ˆà¥¤',
             'tweb.Login.Code.SentMissedCall' => 'à¤…à¤—à¤²à¥‡ à¤•à¥�à¤› à¤¸à¥‡à¤•à¤‚à¤¡ à¤®à¥‡à¤‚ à¤†à¤ªà¤•à¥‹ **%s** à¤¸à¥‡
à¤�à¤• à¤›à¥‹à¤Ÿà¥€ à¤•à¥‰à¤² à¤†à¤�à¤—à¥€à¥¤ à¤‡à¤¸à¤•à¤¾ à¤œà¤µà¤¾à¤¬ à¤¨ à¤¦à¥‡à¤‚ â€”
à¤¬à¤¸ à¤‰à¤¸ à¤¨à¤‚à¤¬à¤° à¤•à¥‡ à¤†à¤–à¤¼à¤¿à¤°à¥€ à¤…à¤‚à¤• à¤¦à¤°à¥�à¤œ à¤•à¤°à¥‡à¤‚à¥¤',
             'tweb.Login.Code.SentSms' => 'à¤¹à¤®à¤¨à¥‡ à¤†à¤ªà¤•à¥‹ à¤•à¥‹à¤¡ à¤•à¥‡ à¤¸à¤¾à¤¥
à¤�à¤• SMS à¤­à¥‡à¤œà¤¾ à¤¹à¥ˆà¥¤',
             'tweb.Login.Code.SentSmsPhrase' => 'à¤¹à¤®à¤¨à¥‡ à¤†à¤ªà¤•à¥‡ à¤«à¤¼à¥‹à¤¨ **%s** à¤ªà¤°
à¤�à¤• à¤—à¥�à¤ªà¥�à¤¤ à¤µà¤¾à¤•à¥�à¤¯à¤¾à¤‚à¤¶ à¤•à¥‡ à¤¸à¤¾à¤¥ SMS à¤­à¥‡à¤œà¤¾ à¤¹à¥ˆà¥¤',
             'tweb.Login.Code.SentSmsPhraseBeginning' => 'à¤¹à¤®à¤¨à¥‡ à¤†à¤ªà¤•à¥‡ à¤«à¤¼à¥‹à¤¨ **%1$s** à¤ªà¤° à¤�à¤• à¤—à¥�à¤ªà¥�à¤¤ à¤µà¤¾à¤•à¥�à¤¯à¤¾à¤‚à¤¶ à¤•à¥‡ à¤¸à¤¾à¤¥ SMS à¤­à¥‡à¤œà¤¾ à¤¹à¥ˆ,
à¤œà¥‹ "**%2$s**" à¤¸à¥‡ à¤¶à¥�à¤°à¥‚ à¤¹à¥‹à¤¤à¤¾ à¤¹à¥ˆà¥¤',
             'tweb.Login.Code.SentSmsWord' => 'à¤¹à¤®à¤¨à¥‡ à¤†à¤ªà¤•à¥‡ à¤«à¤¼à¥‹à¤¨ **%s** à¤ªà¤°
à¤�à¤• à¤—à¥�à¤ªà¥�à¤¤ à¤¶à¤¬à¥�à¤¦ à¤•à¥‡ à¤¸à¤¾à¤¥ SMS à¤­à¥‡à¤œà¤¾ à¤¹à¥ˆà¥¤',
             'tweb.Login.Code.SentSmsWordBeginning' => 'à¤¹à¤®à¤¨à¥‡ à¤†à¤ªà¤•à¥‡ à¤«à¤¼à¥‹à¤¨ **%1$s** à¤ªà¤° à¤�à¤• à¤—à¥�à¤ªà¥�à¤¤ à¤¶à¤¬à¥�à¤¦ à¤•à¥‡ à¤¸à¤¾à¤¥ SMS à¤­à¥‡à¤œà¤¾ à¤¹à¥ˆ,
à¤œà¥‹ "**%2$s**" à¤¸à¥‡ à¤¶à¥�à¤°à¥‚ à¤¹à¥‹à¤¤à¤¾ à¤¹à¥ˆà¥¤',
             'tweb.Login.Code.SentUnknown' => 'à¤•à¥ƒà¤ªà¤¯à¤¾ à¤¹à¤° à¤œà¤—à¤¹ à¤•à¥‹à¤¡
à¤¦à¥‡à¤–à¥‡à¤‚ (à¤ªà¥�à¤°à¤•à¤¾à¤°: %s)',
             'tweb.Login.Code.WordBeginningInvalid' => 'à¤—à¤²à¤¤ à¤¶à¤¬à¥�à¤¦',
             'tweb.Login.Code.WordInvalid' => 'à¤—à¤²à¤¤ à¤¹à¥ˆ, à¤•à¥ƒà¤ªà¤¯à¤¾ à¤«à¤¿à¤° à¤¸à¥‡ à¤ªà¥�à¤°à¤¯à¤¾à¤¸ à¤•à¤°à¥‡à¤‚',
             'tweb.Login.Next' => 'à¤†à¤—à¥‡',
             'tweb.Login.Password.Subtitle' => 'à¤†à¤ªà¤•à¤¾ à¤…à¤•à¤¾à¤‰à¤‚à¤Ÿ à¤�à¤• à¤…à¤¤à¤¿à¤°à¤¿à¤•à¥�à¤¤ à¤ªà¤¾à¤¸à¤µà¤°à¥�à¤¡
à¤¸à¥‡ à¤¸à¥�à¤°à¤•à¥�à¤·à¤¿à¤¤ à¤¹à¥ˆ',
             'tweb.Login.Password.Title' => 'à¤…à¤ªà¤¨à¤¾ à¤ªà¤¾à¤¸à¤µà¤°à¥�à¤¡ à¤¦à¤°à¥�à¤œ à¤•à¤°à¥‡à¤‚',
             'tweb.Login.PhoneLabelInvalid' => 'à¤…à¤®à¤¾à¤¨à¥�à¤¯ à¤«à¤¼à¥‹à¤¨ à¤¨à¤‚à¤¬à¤°',
             'tweb.Login.QR.Cancel' => 'à¤«à¤¼à¥‹à¤¨ à¤¨à¤‚à¤¬à¤° à¤¸à¥‡ à¤²à¥‰à¤—à¤¿à¤¨ à¤•à¤°à¥‡à¤‚ >',
             'tweb.Login.QR.Help1' => 'à¤…à¤ªà¤¨à¥‡ à¤«à¤¼à¥‹à¤¨ à¤ªà¤° Telegram à¤–à¥‹à¤²à¥‡à¤‚',
             'tweb.Login.QR.Help2' => '**Settings** > **Devices** > **Add Device** à¤ªà¤° à¤œà¤¾à¤�à¤�',
             'tweb.Login.QR.Help3' => 'à¤²à¥‰à¤—à¤¿à¤¨ à¤•à¥€ à¤ªà¥�à¤·à¥�à¤Ÿà¤¿ à¤•à¤°à¤¨à¥‡ à¤•à¥‡ à¤²à¤¿à¤� à¤…à¤ªà¤¨à¥‡ à¤«à¤¼à¥‹à¤¨ à¤•à¥‹ à¤‡à¤¸ à¤¸à¥�à¤•à¥�à¤°à¥€à¤¨ à¤•à¥€ à¤“à¤° à¤•à¤°à¥‡à¤‚',
             'tweb.Login.QR.Login' => 'QR à¤•à¥‹à¤¡ à¤¸à¥‡ à¤²à¥‰à¤—à¤¿à¤¨ à¤•à¤°à¥‡à¤‚ >',
             'tweb.Login.QR.Subtitle' => 'à¤…à¤ªà¤¨à¥‡ à¤«à¤¼à¥‹à¤¨ à¤ªà¤° Telegram à¤�à¤ª à¤¸à¥‡ à¤¸à¥�à¤•à¥ˆà¤¨ à¤•à¤°à¥‡à¤‚',
             'tweb.Login.QR.Title' => 'QR à¤•à¥‹à¤¡ à¤¸à¥‡ à¤²à¥‰à¤—à¤¿à¤¨ à¤•à¤°à¥‡à¤‚',
             'tweb.Login.Register.Subtitle' => 'à¤…à¤ªà¤¨à¤¾ à¤…à¤•à¤¾à¤‰à¤‚à¤Ÿ à¤¬à¤¨à¤¾à¤¨à¥‡ à¤•à¥‡ à¤²à¤¿à¤�
à¤…à¤ªà¤¨à¤¾ à¤¨à¤¾à¤® à¤¦à¤°à¥�à¤œ à¤•à¤°à¥‡à¤‚',
             'tweb.Login.StartText' => 'à¤•à¥ƒà¤ªà¤¯à¤¾ à¤…à¤ªà¤¨à¥‡ à¤¦à¥‡à¤¶ à¤•à¥‹à¤¡ à¤•à¥€ à¤ªà¥�à¤·à¥�à¤Ÿà¤¿ à¤•à¤°à¥‡à¤‚
à¤”à¤° à¤…à¤ªà¤¨à¤¾ à¤«à¤¼à¥‹à¤¨ à¤¨à¤‚à¤¬à¤° à¤¦à¤°à¥�à¤œ à¤•à¤°à¥‡à¤‚à¥¤',
             'tweb.Login.Title' => 'MadelineProto à¤®à¥‡à¤‚ à¤¸à¤¾à¤‡à¤¨ à¤‡à¤¨ à¤•à¤°à¥‡à¤‚',
             'tweb.LoginPassword' => 'à¤ªà¤¾à¤¸à¤µà¤°à¥�à¤¡',
             'tweb.Optional' => 'à¤µà¥ˆà¤•à¤²à¥�à¤ªà¤¿à¤•',
             'tweb.PASSWORD_HASH_INVALID' => 'à¤—à¤²à¤¤ à¤ªà¤¾à¤¸à¤µà¤°à¥�à¤¡',
             'tweb.PHONE_CODE_EMPTY' => 'à¤…à¤®à¤¾à¤¨à¥�à¤¯ à¤•à¥‹à¤¡',
             'tweb.PHONE_CODE_EXPIRED' => 'à¤•à¥‹à¤¡ à¤•à¥€ à¤¸à¤®à¤¯-à¤¸à¥€à¤®à¤¾ à¤¸à¤®à¤¾à¤ªà¥�à¤¤ à¤¹à¥‹ à¤—à¤ˆ',
             'tweb.PHONE_CODE_INVALID' => 'à¤…à¤®à¤¾à¤¨à¥�à¤¯ à¤•à¥‹à¤¡',
             'tweb.PHONE_NUMBER_BANNED' => 'à¤¯à¤¹ à¤«à¤¼à¥‹à¤¨ à¤¨à¤‚à¤¬à¤° à¤ªà¥�à¤°à¤¤à¤¿à¤¬à¤‚à¤§à¤¿à¤¤ à¤¹à¥ˆ',
             'tweb.PHONE_NUMBER_FLOOD' => 'à¤¬à¤¹à¥�à¤¤ à¤…à¤§à¤¿à¤• à¤ªà¥�à¤°à¤¯à¤¾à¤¸, à¤¬à¤¾à¤¦ à¤®à¥‡à¤‚ à¤«à¤¿à¤° à¤¸à¥‡ à¤ªà¥�à¤°à¤¯à¤¾à¤¸ à¤•à¤°à¥‡à¤‚',
             'tweb.PHONE_NUMBER_INVALID' => 'à¤…à¤®à¤¾à¤¨à¥�à¤¯ à¤«à¤¼à¥‹à¤¨ à¤¨à¤‚à¤¬à¤°',
             'tweb.PHONE_PASSWORD_FLOOD' => 'à¤¬à¤¹à¥�à¤¤ à¤…à¤§à¤¿à¤• à¤ªà¥�à¤°à¤¯à¤¾à¤¸, à¤¬à¤¾à¤¦ à¤®à¥‡à¤‚ à¤«à¤¿à¤° à¤¸à¥‡ à¤ªà¥�à¤°à¤¯à¤¾à¤¸ à¤•à¤°à¥‡à¤‚',
             'tweb.PhoneNumber.Code.Fragment.Info' => '[Fragment]() à¤ªà¤° Numbers à¤¸à¥‡à¤•à¥�à¤¶à¤¨ à¤®à¥‡à¤‚ à¤•à¥‹à¤¡ à¤ªà¥�à¤°à¤¾à¤ªà¥�à¤¤ à¤•à¤°à¥‡à¤‚',
             'tweb.PleaseWait' => 'à¤•à¥ƒà¤ªà¤¯à¤¾ à¤ªà¥�à¤°à¤¤à¥€à¤•à¥�à¤·à¤¾ à¤•à¤°à¥‡à¤‚...',
             'tweb.StartMessaging' => 'à¤®à¥ˆà¤¸à¥‡à¤œà¤¿à¤‚à¤— à¤¶à¥�à¤°à¥‚ à¤•à¤°à¥‡à¤‚',
             'tweb.YourName' => 'à¤†à¤ªà¤•à¤¾ à¤¨à¤¾à¤®',
             'type_extract_error' => 'à¤Ÿà¤¾à¤‡à¤ª "%s" à¤¨à¤¹à¥€à¤‚ à¤¨à¤¿à¤•à¤¾à¤²à¤¾ à¤œà¤¾ à¤¸à¤•à¤¾, à¤†à¤ªà¤•à¥‹ MadelineProto à¤…à¤ªà¤¡à¥‡à¤Ÿ à¤•à¤°à¤¨à¤¾ à¤šà¤¾à¤¹à¤¿à¤�!',
             'type_extract_error_id' => 'à¤Ÿà¤¾à¤‡à¤ª à¤¨à¤¹à¥€à¤‚ à¤¨à¤¿à¤•à¤¾à¤²à¤¾ à¤œà¤¾ à¤¸à¤•à¤¾: %s (id %s), à¤†à¤ªà¤•à¥‹ MadelineProto à¤…à¤ªà¤¡à¥‡à¤Ÿ à¤•à¤°à¤¨à¤¾ à¤šà¤¾à¤¹à¤¿à¤�!',
             'update_madelineproto' => 'à¤†à¤ª MadelineProto à¤•à¤¾ à¤ªà¥�à¤°à¤¾à¤¨à¤¾ à¤¸à¤‚à¤¸à¥�à¤•à¤°à¤£ à¤šà¤²à¤¾ à¤°à¤¹à¥‡ à¤¹à¥ˆà¤‚, à¤…à¤ªà¤¡à¥‡à¤Ÿ à¤†à¤µà¤¶à¥�à¤¯à¤• à¤¹à¥ˆ: à¤…à¤­à¥€ %s à¤šà¤² à¤°à¤¹à¤¾ à¤¹à¥ˆ, à¤²à¥‡à¤•à¤¿à¤¨ à¤•à¤ˆ à¤¬à¤— à¤«à¤¼à¤¿à¤•à¥�à¤¸ à¤”à¤° à¤¨à¤ˆ à¤¸à¥�à¤µà¤¿à¤§à¤¾à¤“à¤‚ à¤µà¤¾à¤²à¤¾ à¤¨à¤µà¥€à¤¨à¤¤à¤® à¤¸à¤‚à¤¸à¥�à¤•à¤°à¤£ %s à¤¹à¥ˆ!',
             'value_bigger_than_2147483647' => 'à¤¦à¤¿à¤¯à¤¾ à¤—à¤¯à¤¾ à¤®à¤¾à¤¨ %s, 2147483647 à¤¸à¥‡ à¤¬à¤¡à¤¼à¤¾ à¤¹à¥ˆ',
             'value_bigger_than_4294967296' => 'à¤¦à¤¿à¤¯à¤¾ à¤—à¤¯à¤¾ à¤®à¤¾à¤¨ %s, 4294967296 à¤¸à¥‡ à¤¬à¤¡à¤¼à¤¾ à¤¹à¥ˆ',
             'value_bigger_than_9223372036854775807' => 'à¤¦à¤¿à¤¯à¤¾ à¤—à¤¯à¤¾ à¤®à¤¾à¤¨ %s, 9223372036854775807 à¤¸à¥‡ à¤¬à¤¡à¤¼à¤¾ à¤¹à¥ˆ',
             'value_smaller_than_0' => 'à¤¦à¤¿à¤¯à¤¾ à¤—à¤¯à¤¾ à¤®à¤¾à¤¨ %s, 0 à¤¸à¥‡ à¤›à¥‹à¤Ÿà¤¾ à¤¹à¥ˆ',
             'value_smaller_than_2147483648' => 'à¤¦à¤¿à¤¯à¤¾ à¤—à¤¯à¤¾ à¤®à¤¾à¤¨ %s, -2147483648 à¤¸à¥‡ à¤›à¥‹à¤Ÿà¤¾ à¤¹à¥ˆ',
             'value_smaller_than_9223372036854775808' => 'à¤¦à¤¿à¤¯à¤¾ à¤—à¤¯à¤¾ à¤®à¤¾à¤¨ %s, -9223372036854775808 à¤¸à¥‡ à¤›à¥‹à¤Ÿà¤¾ à¤¹à¥ˆ',
             'waveform_must_have_100_values' => 'waveform array à¤®à¥‡à¤‚ 100 à¤®à¤¾à¤¨ à¤¹à¥‹à¤¨à¥‡ à¤šà¤¾à¤¹à¤¿à¤�!',
             'waveform_value' => 'waveform à¤•à¤¾ à¤®à¤¾à¤¨ 0 à¤”à¤° 31 à¤•à¥‡ à¤¬à¥€à¤š à¤¹à¥‹à¤¨à¤¾ à¤šà¤¾à¤¹à¤¿à¤�!',
             'webContinue' => 'à¤œà¤¾à¤°à¥€ à¤°à¤–à¥‡à¤‚',
             'webDocs' => 'à¤¡à¥‰à¤•à¥�à¤¯à¥‚à¤®à¥‡à¤‚à¤Ÿà¥‡à¤¶à¤¨',
             'webDoneLead' => 'MadelineProto à¤¸à¤°à¥�à¤µà¤° à¤ªà¤° à¤šà¤²à¤¤à¤¾ à¤°à¤¹à¥‡à¤—à¤¾: à¤†à¤ª à¤¯à¤¹ à¤ªà¥‡à¤œ à¤¬à¤‚à¤¦ à¤•à¤° à¤¸à¤•à¤¤à¥‡ à¤¹à¥ˆà¤‚à¥¤',
             'webError' => '%sà¥¤ à¤‡à¤¸à¥‡ à¤œà¤¾à¤�à¤šà¥‡à¤‚ à¤”à¤° à¤«à¤¿à¤° à¤¸à¥‡ à¤ªà¥�à¤°à¤¯à¤¾à¤¸ à¤•à¤°à¥‡à¤‚à¥¤',
             'webFieldRequired' => 'à¤¯à¤¹ à¤«à¤¼à¥€à¤²à¥�à¤¡ à¤­à¤°à¥‡à¤‚à¥¤',
             'webHidePassword' => 'à¤›à¤¿à¤ªà¤¾à¤�à¤�',
             'webLogin' => 'à¤²à¥‰à¤—à¤¿à¤¨ à¤•à¤°à¥‡à¤‚',
             'webOptional' => 'à¤µà¥ˆà¤•à¤²à¥�à¤ªà¤¿à¤•',
             'webPhoneInvalid' => 'à¤¦à¥‡à¤¶ à¤•à¥‹à¤¡ à¤•à¥‡ à¤¸à¤¾à¤¥ à¤«à¤¼à¥‹à¤¨ à¤¨à¤‚à¤¬à¤° à¤¦à¤°à¥�à¤œ à¤•à¤°à¥‡à¤‚, à¤œà¥ˆà¤¸à¥‡ +39 123 456 7890à¥¤',
             'webProgress' => 'à¤¸à¥‡à¤Ÿà¤…à¤ª à¤•à¥€ à¤ªà¥�à¤°à¤—à¤¤à¤¿',
             'webQrTitle' => 'QR à¤•à¥‹à¤¡ à¤¸à¥‡ à¤²à¥‰à¤—à¤¿à¤¨ à¤•à¤°à¥‡à¤‚',
             'webQrWaiting' => 'à¤¸à¥�à¤•à¥ˆà¤¨ à¤•à¥€ à¤ªà¥�à¤°à¤¤à¥€à¤•à¥�à¤·à¤¾ à¤•à¥€ à¤œà¤¾ à¤°à¤¹à¥€ à¤¹à¥ˆâ€¦',
             'webShowPassword' => 'à¤¦à¤¿à¤–à¤¾à¤�à¤�',
             'webStepAccount' => 'à¤…à¤•à¤¾à¤‰à¤‚à¤Ÿ',
             'webStepApp' => 'API ID à¤”à¤° hash',
             'webStepDone' => 'à¤šà¤² à¤°à¤¹à¤¾ à¤¹à¥ˆ',
             'webStepVerify' => 'à¤µà¥‡à¤°à¤¿à¤«à¤¼à¤¿à¤•à¥‡à¤¶à¤¨',
         ],
        'id' =>
         [
             '2fa_uncalled' => 'Saya tidak sedang menunggu kata sandi! Harap panggil metode phoneLogin dan completePhoneLogin terlebih dahulu!',
             'accepting_call' => 'Menerima panggilan dari %s...',
             'account_banned' => '!!!!!!! PERINGATAN !!!!!!!
Sistem pencegahan flood Telegram telah menangguhkan akun ini.
Untuk melanjutkan, diperlukan verifikasi manual.
Kirim email ke recover@telegram.org, minta agar blokir nomor telepon %s dibuka, dan jelaskan secara singkat apa yang akan Anda lakukan dengan nomor telepon ini.
Kemudian login kembali.
Jika Anda sengaja menghapus akun ini, abaikan pesan ini.',
             'already_loggedIn' => 'Instance MadelineProto ini sudah login!',
             'apiAppDescLabelWeb' => 'Deskripsi',
             'apiAppInstructionsAuto0' => 'Masukkan nama aplikasi, bebas: ',
             'apiAppInstructionsAuto1' => 'Masukkan nama pendek aplikasi, alfanumerik, 5-32 karakter: ',
             'apiAppInstructionsAuto2' => 'Masukkan URL aplikasi/situs web, atau t.me/yourusername: ',
             'apiAppInstructionsAuto3' => 'Masukkan platform aplikasi: ',
             'apiAppInstructionsAuto4' => 'Jelaskan aplikasi Anda: ',
             'apiAppInstructionsAutoTypeOther' => 'Lainnya (jelaskan di deskripsi)',
             'apiAppInstructionsManual0' => 'nama aplikasi Anda, bebas',
             'apiAppInstructionsManual1' => 'nama pendek aplikasi Anda, alfanumerik, 5-32 karakter',
             'apiAppInstructionsManual2' => 'URL aplikasi/situs web Anda, atau t.me/yourusername',
             'apiAppInstructionsManual3' => 'bebas',
             'apiAppInstructionsManual4' => 'Jelaskan aplikasi Anda di sini',
             'apiAppShortNameLabelWeb' => 'Nama pendek',
             'apiAppTitleLabelWeb' => 'Nama aplikasi',
             'apiAppUrlLabelWeb' => 'URL',
             'apiAppWeb' => 'Masukkan informasi API',
             'apiAutoCodeLabelWeb' => 'Kode login',
             'apiAutoCodeLeadWeb' => 'my.telegram.org telah mengirimkan kode login ke aplikasi Telegram Anda, di obrolan dengan Telegram (bukan melalui SMS).',
             'apiAutoCodeTitleWeb' => 'Masukkan kode dari Telegram',
             'apiAutoErrorTitle' => 'my.telegram.org mengembalikan kesalahan',
             'apiAutoFallbackManual' => 'Sebagai gantinya, masukkan API ID dan hash secara manual.',
             'apiAutoPhoneHintWeb' => 'my.telegram.org mengirimkan kode login ke aplikasi Telegram Anda, bukan melalui SMS.',
             'apiAutoPrompt0' => 'Masukkan nomor telepon yang sudah terdaftar di Telegram: ',
             'apiAutoPrompt1' => 'Masukkan kode verifikasi yang Anda terima di Telegram: ',
             'apiAutoWeb' => 'Masukkan nomor telepon yang <b>sudah terdaftar</b> di Telegram untuk mendapatkan API ID',
             'apiChooseAutoWeb' => 'Dapatkan secara otomatis',
             'apiChooseManualAuto' => 'Apakah Anda ingin mendapatkan API ID dan hash secara otomatis dari my.telegram.org (a), atau memasukkannya secara manual (m)?',
             'apiChooseManualAutoTip' => 'Perhatikan bahwa Anda juga dapat memberikan API ID/hash langsung di kode menggunakan pengaturan: %s',
             'apiChooseManualAutoTipWeb' => 'Perhatikan bahwa Anda juga dapat memberikan API ID/hash langsung di kode menggunakan <a target="_blank" href="%s">pengaturan</a>.',
             'apiChooseManualWeb' => 'Saya sudah punya API ID',
             'apiChoosePrompt' => 'Pilihan Anda (m/a): ',
             'apiCreateErrorTitle' => 'my.telegram.org tidak membuat aplikasi',
             'apiCreateLeadWeb' => 'Akun ini belum memiliki aplikasi di my.telegram.org. Detail ini hanya ditampilkan kepada Anda, dan dapat diubah nanti.',
             'apiCreateTitleWeb' => 'Buat aplikasi Anda',
             'apiCreateVpsWarning' => 'Telegram tidak mengizinkan rentang IP VPS, VPN, dan pusat data lainnya membuat aplikasi baru di my.telegram.org, dan permintaan ini dibuat dari IP mesin yang menjalankan MadelineProto. Buat aplikasi secara manual di https://my.telegram.org dari koneksi rumahan (misalnya ponsel Anda dengan data seluler), lalu masukkan API ID dan hash.',
             'apiCreateWeb' => 'Buat aplikasi',
             'apiError' => 'KESALAHAN: %s. Silakan coba lagi.',
             'apiInvalidWeb' => 'API ID berupa angka, dan API hash terdiri dari 32 karakter: salin kembali keduanya dari my.telegram.org.',
             'apiManualInstructions0' => 'Login ke https://my.telegram.org',
             'apiManualInstructions1' => 'Buka API development tools',
             'apiManualInstructions2' => 'Klik create application',
             'apiManualPrompt0' => 'Masukkan API ID Anda: ',
             'apiManualPrompt1' => 'Masukkan API hash Anda: ',
             'apiManualWeb' => 'Masukkan API ID dan API hash Anda',
             'apiParamsError' => 'Anda tidak memberikan semua parameter yang diperlukan!',
             'apiSaveWeb' => 'Simpan dan lanjutkan',
             'apiSendCodeWeb' => 'Kirim kode',
             'apiSetupLeadWeb' => 'Setiap aplikasi Telegram memerlukan API ID dan hash. MadelineProto dapat mengambilnya untuk Anda dari my.telegram.org, menggunakan nomor telepon akun Telegram yang sudah ada.',
             'apiSetupTitleWeb' => 'Dapatkan API ID dan hash Anda',
             'apiStartOverWeb' => 'Mulai dari awal',
             'api_not_set' => 'Anda harus memberikan api key dan api id, dapatkan milik Anda sendiri di my.telegram.org',
             'array_invalid' => 'Anda tidak memberikan array yang valid',
             'baseDirLimitation' => 'Pembatasan basedir sedang dikonfigurasi: hal ini dapat memengaruhi kinerja dan menyebabkan beberapa masalah, harap nonaktifkan jika memungkinkan!',
             'bool_error' => 'Tidak dapat mengekstrak boolean',
             'botAlreadyRunning' => 'Bot sudah berjalan!',
             'botapi_conversion_error' => 'Tidak dapat mengonversi %s menjadi objek bot API',
             'call_already_accepted' => 'Panggilan %s sudah diterima',
             'call_already_declined' => 'Panggilan %s sudah ditolak',
             'call_completing' => 'Menyelesaikan panggilan dari %s...',
             'call_confirming' => 'Mengonfirmasi panggilan dari %s...',
             'call_discarding' => 'Mengakhiri panggilan %s...',
             'call_error_1' => 'Tidak dapat menemukan dan menerima panggilan %s',
             'call_error_2' => 'Tidak dapat menemukan dan mengonfirmasi panggilan %s',
             'call_error_3' => 'Tidak dapat menemukan dan menyelesaikan panggilan %s',
             'cli_need_dl.php_link' => 'Harap tentukan URL skrip unduhan saat menggunakan getDownloadLink melalui CLI!',
             'constructor_not_found' => 'Constructor tidak ditemukan untuk tipe: ',
             'could_not_connect_to_MadelineProto' => 'Tidak dapat terhubung ke MadelineProto, harap aktifkan proc_open dan hapus pembatasan open_basedir atau nonaktifkan path rewrite webserver untuk memperbaikinya! Jika Anda sudah melakukannya, pastikan versi CLI PHP sama persis dengan versi web (versi, ekstensi, dan lain-lain yang sama) dan periksa file MadelineProto.log untuk info lebih lanjut tentang kesalahan yang mencegah server IPC berjalan.',
             'could_not_convert_object' => 'Tidak dapat mengonversi objek bertipe %s',
             'deserialization_error' => 'Terjadi kesalahan saat deserialisasi',
             'dl.php_check_logs_make_sure_session_running' => 'Bot EventHandler MadelineProto terkait atau server IPC MadelineProto sedang offline, harap periksa log dan pastikan setidaknya salah satunya berjalan!',
             'dl.php_powered_by_madelineproto' => 'Server unduhan file Telegram (hingga 4GB), diberdayakan oleh <a href="https://docs.madelineproto.xyz" target="_blank">MadelineProto</a>!<br>Klik <a href="https://docs.madelineproto.xyz/docs/FILES.html#getting-a-download-link" target="_blank">di sini</a> untuk info lebih lanjut tentang cara menyiapkan server unduhan file Telegram Anda sendiri!',
             'do_not_delete_MadelineProto.log' => 'file MadelineProto.log tidak boleh dihapus, sebagai gantinya atur ukuran maksimum kustom di pengaturan!',
             'do_not_remove_MadelineProto.log_phar' => 'Jangan hapus madeline.phar atau madeline.php, karena MadelineProto akan crash. Jika Anda mengalami masalah dengan MadelineProto, laporkan ke https://github.com/danog/MadelineProto atau https://t.me/pwrtelegramgroup',
             'do_not_use_blocking_class' => 'demi alasan kinerja, handler tidak boleh menggunakan kelas blocking non-async %s, sebagai gantinya gunakan %s',
             'do_not_use_blocking_function' => 'demi alasan kinerja, event handler tidak boleh menggunakan fungsi blocking non-async %s, sebagai gantinya gunakan %s',
             'do_not_use_deprecated_function' => 'fungsi %s sudah usang (deprecated), sebagai gantinya gunakan %s',
             'do_not_use_non_root_require_in_event_handler' => 'demi alasan kinerja, Anda tidak boleh menggunakan require atau include di dalam kelas event handler, hanya require di level root yang diizinkan.',
             'do_not_use_yield' => 'MadelineProto 8 tidak memerlukan maupun mendukung penggunaan yield dalam fungsi async, Anda harus menghapus semua kata kunci yield yang sebelumnya digunakan untuk pemanggilan fungsi async',
             'done' => 'Selesai!',
             'encode_double_error' => 'Tidak dapat mengenkode double dengan benar',
             'extensionRecommended' => 'Peringatan: ekstensi %s tidak terpasang, harap pasang untuk mempercepat MadelineProto!',
             'extensionRequired' => 'MadelineProto memerlukan ekstensi %s untuk berjalan. %s',
             'extensionRequiredInstallWithApt' => 'Coba jalankan sudo apt-get install %s.',
             'extensionRequiredInstallWithCustomInstructions' => 'Ikuti petunjuk di %s untuk memasangnya.',
             'file_not_exist' => 'File tidak ada',
             'file_parsing' => 'Mem-parsing %s...',
             'file_type_invalid' => 'Terdeteksi tipe file tidak valid (%s)',
             'fingerprint_invalid' => 'Fingerprint kunci tidak valid!',
             'go' => 'Lanjut',
             'invalid_dl.php_session' => '%s bukan skrip unduhan yang valid karena ID sesinya berbeda (diharapkan %s, didapat %s)',
             'ipc_tcp_warning' => 'UNIX sockets are not available, so the IPC server of the session listens on TCP (127.0.0.1) instead: any program running on this machine can connect to it, and control the session. On Windows 10 1803+, enable the sockets extension (extension=sockets in php.ini) to use UNIX sockets.',
             'length_too_big' => 'Panjang terlalu besar',
             'loginBot' => 'Masukkan token bot Anda: ',
             'loginBotTokenHintWeb' => 'Token yang diberikan @BotFather saat Anda membuat bot.',
             'loginBotTokenWeb' => 'Token bot',
             'loginChoosePromptWeb' => 'Apakah Anda ingin login sebagai pengguna atau sebagai bot?',
             'loginCodeLeadWeb' => 'Telegram telah mengirimkan kode login ke aplikasi Telegram Anda, atau melalui SMS jika Anda tidak login di tempat lain.',
             'loginCodeTitleWeb' => 'Masukkan kode login',
             'loginLeadWeb' => 'Pilih akun atau bot yang akan digunakan untuk menjalankan instance MadelineProto ini.',
             'loginManual' => 'Atau, Anda juga dapat memasukkan token bot atau nomor telepon untuk login secara manual: ',
             'loginNoCode' => 'Anda tidak memberikan kode telepon!',
             'loginNoName' => 'Anda tidak memberikan nama depan!',
             'loginNoPass' => 'Anda tidak memberikan kata sandi!',
             'loginOptionBot' => 'Bot',
             'loginOptionUser' => 'Pengguna',
             'loginPasswordLabelWeb' => 'Kata sandi',
             'loginPasswordLeadWeb' => 'Akun ini dilindungi kata sandi, yang diatur di Pengaturan > Privasi dan Keamanan > Verifikasi Dua Langkah.',
             'loginPasswordTitleWeb' => 'Verifikasi dua langkah',
             'loginQr' => 'Buka Telegram di ponsel Anda, masuk ke Pengaturan > Perangkat > Tautkan Perangkat Desktop, lalu pindai kode QR di atas untuk login secara otomatis.',
             'loginQrCodeExpired' => 'Kode QR kedaluwarsa, membuat kode baru...',
             'loginQrCodeSuccessful' => 'Login dengan kode QR berhasil!',
             'loginTitleWeb' => 'Login ke Telegram',
             'loginTypeWeb' => 'Login sebagai',
             'loginUser' => 'Masukkan nomor telepon Anda: ',
             'loginUserCode' => 'Masukkan kode: ',
             'loginUserPass' => 'Masukkan kata sandi Anda (petunjuk %s): ',
             'loginUserPassHint' => 'Petunjuk: %s',
             'loginUserPassWeb' => 'Masukkan kata sandi Anda: ',
             'loginUserPhoneCodeWeb' => 'Kode',
             'loginUserPhoneHintWeb' => 'Dengan kode negara, misalnya +39 123 456 7890.',
             'loginUserPhoneWeb' => 'Nomor telepon',
             'loginWebQr' => 'Anda juga dapat login secara otomatis dengan membuka Telegram di ponsel Anda, masuk ke Pengaturan > Perangkat > Tautkan Perangkat Desktop, lalu memindai kode QR berikut:',
             'loginWebQr1' => 'Buka Telegram di ponsel Anda',
             'loginWebQr2' => 'Masuk ke Pengaturan > Perangkat > Tautkan Perangkat Desktop',
             'loginWebQr3' => 'Arahkan ponsel Anda ke layar ini untuk mengonfirmasi login',
             'login_2fa_enabled' => '2FA aktif, Anda harus memanggil fungsi complete2falogin...',
             'login_auth_key' => 'Login menggunakan auth key...',
             'login_bot' => 'Login sebagai bot...',
             'login_code_sending' => 'Mengirim kode...',
             'login_code_sent' => 'Kode berhasil dikirim! Setelah menerima kode, Anda harus menggunakan fungsi completePhoneLogin.',
             'login_code_uncalled' => 'Saya tidak sedang menunggu kode! Harap panggil metode phoneLogin terlebih dahulu',
             'login_need_signup' => 'Belum ada akun yang dibuat untuk nomor ini, Anda harus memanggil fungsi completeSignup...',
             'login_ok' => 'Berhasil login!',
             'login_user' => 'Login sebagai pengguna biasa...',
             'logout_ok' => 'Berhasil logout!',
             'long_not_16' => 'Panjang nilai yang diberikan bukan 16 byte',
             'long_not_32' => 'Panjang nilai yang diberikan bukan 32 byte',
             'long_not_64' => 'Panjang nilai yang diberikan bukan 64 byte',
             'madelineproto_ready' => 'MadelineProto siap!',
             'manualAdminActionRequired' => '!!!!!!!!! DIPERLUKAN TINDAKAN MANUAL DARI ADMIN SISTEM !!!!!!!!!',
             'method_not_found' => 'Tidak dapat menemukan metode: ',
             'mmapErrorPart1' => 'Jumlah maksimum region memori yang dipetakan (mmap) telah tercapai (%s): harap naikkan konfigurasi kernel vm.max_map_count menjadi 262144 untuk memperbaikinya.',
             'mmapErrorPart2' => 'Untuk memperbaikinya, jalankan perintah berikut sebagai root: %s',
             'mmapErrorPart3' => 'Agar perubahan tetap berlaku setelah reboot: %s',
             'mmapErrorPart4' => 'Di Windows dan WSL, memperbesar ukuran pagefile mungkin dapat membantu; harap beralih ke Linux native jika masalah berlanjut.',
             'must_have_declare_types' => 'demi alasan kinerja, pernyataan pertama dari file event handler harus "declare(strict_types=1);"',
             'nearest_dc' => 'Kami berada di %s, DC terdekat adalah %d.',
             'need_dl.php' => 'Tidak dapat membuat skrip unduhan default (%s), harap buat file dl.php dengan konten berikut: %s dan berikan URL-nya ke parameter kedua getDownloadLink',
             'noReportPeers' => 'Peringatan: tidak ada peer pelaporan yang diatur, harap tambahkan metode berikut ke event handler Anda',
             'non_text_conversion' => 'Belum dapat mengonversi pesan non-teks!',
             'not_loggedIn' => 'Saya belum login!',
             'not_numeric' => 'Nilai yang diberikan bukan numerik',
             'params_missing' => 'Parameter wajib tidak ada',
             'peer_not_in_db' => 'Peer ini tidak ada di database peer internal',
             'plugin_path_does_not_exist' => 'Path plugin %s tidak ada!',
             'plugins_do_not_use_require' => 'demi alasan kinerja, plugin hanya dapat meng-include atau me-require file lain yang ada di folder plugins secara otomatis dengan memicu autoloader PSR-4 (bukan dengan require() secara manual).',
             'plugins_must_have_exactly_one_class' => 'sebuah plugin harus mendefinisikan tepat satu kelas! Untuk mendefinisikan beberapa kelas, interface, atau trait, buat file terpisah, file-file tersebut akan dimuat otomatis oleh MadelineProto.',
             'predicate_not_set' => 'Predicate (nilai di bawah _) tidak diatur!',
             'recommend_not_use_filesystem_function' => 'penggunaan fungsi %s tidak disarankan, karena mengakses filesystem saat menangani update akan memperlambat bot Anda, silakan lihat https://docs.madelineproto.xyz/docs/UPDATES.html#avoiding-the-use-of-filesystem-functions untuk daftar cara alternatif menyimpan data yang tidak akan memperlambat bot Anda!',
             'rpc_tg_error' => 'Telegram mengembalikan galat RPC: %s (%s), disebabkan oleh %s:%s%sJejak TL:',
             'sec_peer_not_in_db' => 'Peer rahasia ini tidak ada di database peer internal',
             'secret_chat_skipping' => 'Saya tidak memiliki obrolan rahasia %s di database, melewati pesan...',
             'serialization_ofd' => 'Serialisasi sudah usang, merekonstruksi objek!',
             'session_corrupted' => 'Sesi rusak!',
             'signing_up' => 'Mendaftar sebagai pengguna biasa...',
             'signupFirstName' => 'Masukkan nama depan Anda: ',
             'signupFirstNameWeb' => 'Nama depan',
             'signupLastName' => 'Masukkan nama belakang Anda (boleh kosong): ',
             'signupLastNameWeb' => 'Nama belakang',
             'signupLeadWeb' => 'Nomor telepon ini belum terdaftar di Telegram: pilih nama untuk akun baru.',
             'signupTitleWeb' => 'Buat akun Anda',
             'signupWeb' => 'Silakan mendaftar',
             'signup_ok' => 'Berhasil mendaftar!',
             'signup_uncalled' => 'Saya tidak sedang menunggu pendaftaran! Harap panggil metode phoneLogin dan completePhoneLogin terlebih dahulu!',
             'src_file_invalid' => 'File sumber yang diberikan tidak valid: ',
             'static_analysis_minor' => 'Ditemukan masalah kecil saat analisis statis %s: %s',
             'static_analysis_severe' => 'Ditemukan masalah serius saat analisis statis %s: %s',
             'stream_handle_invalid' => 'Stream handle yang diberikan tidak valid.',
             'string_required' => 'Diharapkan sebuah string!',
             'translate_madelineproto_cli' => 'MadelineProto dapat diterjemahkan ke bahasa Anda (progres terjemahan saat ini: %d%%), kunjungi https://github.com/danog/MadelineProto/tree/v8/langs untuk berkontribusi pada terjemahan!',
             'translate_madelineproto_web' => 'MadelineProto dapat diterjemahkan ke bahasa Anda (progres terjemahan saat ini: %d%%), klik <a href="https://github.com/danog/MadelineProto/tree/v8/langs" target="_blank">di sini untuk berkontribusi pada terjemahan!</a>',
             'tweb.ACCESS_TOKEN_EXPIRED' => 'Token bot ini telah dicabut',
             'tweb.ACCESS_TOKEN_INVALID' => 'Token bot tidak valid',
             'tweb.AccDescr.LoginCode' => 'Kode login',
             'tweb.Close' => 'Tutup',
             'tweb.Code' => 'Kode',
             'tweb.Contacts.PhoneNumber.Placeholder' => 'Nomor telepon',
             'tweb.Country' => 'Negara',
             'tweb.DarkMode' => 'Mode Gelap',
             'tweb.Error.AnError' => 'Terjadi kesalahan. Silakan coba lagi nanti.',
             'tweb.FIRSTNAME_INVALID' => 'Nama depan tidak valid',
             'tweb.FirstName' => 'Nama depan (wajib)',
             'tweb.LASTNAME_INVALID' => 'Nama belakang tidak valid',
             'tweb.LastName' => 'Nama belakang (opsional)',
             'tweb.Login.Bot' => 'Login sebagai bot >',
             'tweb.Login.Bot.Cancel' => 'Login sebagai pengguna >',
             'tweb.Login.Bot.Subtitle' => 'Masukkan token yang diberikan @BotFather
saat Anda membuat bot.',
             'tweb.Login.Bot.Title' => 'Login sebagai bot',
             'tweb.Login.Bot.Token' => 'Token bot',
             'tweb.Login.Code.SecretPhrase' => 'Frasa rahasia',
             'tweb.Login.Code.SecretWord' => 'Kata rahasia',
             'tweb.Login.Code.SentCall' => 'Kami akan menelepon Anda dan
menyebutkan kodenya.',
             'tweb.Login.Code.SentEmail' => 'Silakan periksa email Anda **%s** (jangan lupa folder spam) dan masukkan kode yang baru saja kami kirimkan.',
             'tweb.Login.Code.SentInApp' => 'Kami telah mengirimkan pesan di Telegram
berisi kode kepada Anda.',
             'tweb.Login.Code.SentMissedCall' => 'Dalam beberapa detik, Anda akan menerima
panggilan singkat dari **%s**. Jangan dijawab â€”
cukup masukkan digit terakhir dari nomor tersebut.',
             'tweb.Login.Code.SentSms' => 'Kami telah mengirimkan SMS
berisi kode kepada Anda.',
             'tweb.Login.Code.SentSmsPhrase' => 'Kami telah mengirimkan SMS berisi frasa rahasia
ke ponsel Anda **%s**.',
             'tweb.Login.Code.SentSmsPhraseBeginning' => 'Kami telah mengirimkan SMS berisi frasa rahasia yang diawali
dengan "**%2$s**" ke ponsel Anda **%1$s**.',
             'tweb.Login.Code.SentSmsWord' => 'Kami telah mengirimkan SMS berisi kata rahasia
ke ponsel Anda **%s**.',
             'tweb.Login.Code.SentSmsWordBeginning' => 'Kami telah mengirimkan SMS berisi kata rahasia yang diawali
dengan "**%2$s**" ke ponsel Anda **%1$s**.',
             'tweb.Login.Code.SentUnknown' => 'Silakan periksa semuanya
untuk menemukan kode (tipe: %s)',
             'tweb.Login.Code.WordBeginningInvalid' => 'Kata salah',
             'tweb.Login.Code.WordInvalid' => 'Salah, silakan coba lagi',
             'tweb.Login.Next' => 'Berikutnya',
             'tweb.Login.Password.Subtitle' => 'Akun Anda dilindungi dengan
kata sandi tambahan',
             'tweb.Login.Password.Title' => 'Masukkan kata sandi Anda',
             'tweb.Login.PhoneLabelInvalid' => 'Nomor telepon tidak valid',
             'tweb.Login.QR.Cancel' => 'Login dengan nomor telepon >',
             'tweb.Login.QR.Help1' => 'Buka Telegram di ponsel Anda',
             'tweb.Login.QR.Help2' => 'Masuk ke **Pengaturan** > **Perangkat** > **Tambah Perangkat**',
             'tweb.Login.QR.Help3' => 'Arahkan ponsel Anda ke layar ini untuk mengonfirmasi login',
             'tweb.Login.QR.Login' => 'Login dengan kode QR >',
             'tweb.Login.QR.Subtitle' => 'Pindai dengan aplikasi Telegram di ponsel Anda',
             'tweb.Login.QR.Title' => 'Login dengan kode QR',
             'tweb.Login.Register.Subtitle' => 'Masukkan nama Anda
untuk membuat akun',
             'tweb.Login.StartText' => 'Silakan konfirmasi kode negara Anda
dan masukkan nomor telepon Anda.',
             'tweb.Login.Title' => 'Login ke MadelineProto',
             'tweb.LoginPassword' => 'Kata sandi',
             'tweb.Optional' => 'opsional',
             'tweb.PASSWORD_HASH_INVALID' => 'Kata sandi salah',
             'tweb.PHONE_CODE_EMPTY' => 'Kode tidak valid',
             'tweb.PHONE_CODE_EXPIRED' => 'Kode kedaluwarsa',
             'tweb.PHONE_CODE_INVALID' => 'Kode tidak valid',
             'tweb.PHONE_NUMBER_BANNED' => 'Nomor telepon ini diblokir',
             'tweb.PHONE_NUMBER_FLOOD' => 'Terlalu banyak percobaan, coba lagi nanti',
             'tweb.PHONE_NUMBER_INVALID' => 'Nomor telepon tidak valid',
             'tweb.PHONE_PASSWORD_FLOOD' => 'Terlalu banyak percobaan, coba lagi nanti',
             'tweb.PhoneNumber.Code.Fragment.Info' => 'Dapatkan kode di bagian Numbers di [Fragment]()',
             'tweb.PleaseWait' => 'Mohon tunggu...',
             'tweb.StartMessaging' => 'Mulai Berkirim Pesan',
             'tweb.YourName' => 'Nama Anda',
             'type_extract_error' => 'Tidak dapat mengekstrak tipe "%s", Anda harus memperbarui MadelineProto!',
             'type_extract_error_id' => 'Tidak dapat mengekstrak tipe: %s dengan id %s, Anda harus memperbarui MadelineProto!',
             'update_madelineproto' => 'Anda menjalankan versi lama MadelineProto, pembaruan diperlukan: versi saat ini %s, sedangkan versi terbaru dengan banyak perbaikan bug dan fitur baru adalah %s!',
             'value_bigger_than_2147483647' => 'Nilai yang diberikan %s lebih besar dari 2147483647',
             'value_bigger_than_4294967296' => 'Nilai yang diberikan %s lebih besar dari 4294967296',
             'value_bigger_than_9223372036854775807' => 'Nilai yang diberikan %s lebih besar dari 9223372036854775807',
             'value_smaller_than_0' => 'Nilai yang diberikan %s lebih kecil dari 0',
             'value_smaller_than_2147483648' => 'Nilai yang diberikan %s lebih kecil dari -2147483648',
             'value_smaller_than_9223372036854775808' => 'Nilai yang diberikan %s lebih kecil dari -9223372036854775808',
             'waveform_must_have_100_values' => 'Array waveform harus memiliki 100 nilai!',
             'waveform_value' => 'Nilai waveform harus antara 0 dan 31!',
             'webContinue' => 'Lanjutkan',
             'webDocs' => 'Dokumentasi',
             'webDoneLead' => 'MadelineProto tetap berjalan di server: Anda dapat menutup halaman ini.',
             'webError' => '%s. Periksa dan coba lagi.',
             'webFieldRequired' => 'Isi kolom ini.',
             'webHidePassword' => 'Sembunyikan',
             'webLogin' => 'Login',
             'webOptional' => 'opsional',
             'webPhoneInvalid' => 'Masukkan nomor telepon beserta kode negaranya, misalnya +39 123 456 7890.',
             'webProgress' => 'Progres penyiapan',
             'webQrTitle' => 'Login dengan kode QR',
             'webQrWaiting' => 'Menunggu pemindaianâ€¦',
             'webShowPassword' => 'Tampilkan',
             'webStepAccount' => 'Akun',
             'webStepApp' => 'API ID dan hash',
             'webStepDone' => 'Berjalan',
             'webStepVerify' => 'Verifikasi',
         ],
        'it' =>
         [
             '2fa_uncalled' => 'Non sto aspettando la password, chiama prima le funzioni phoneLogin e completePhoneLogin!',
             'accepting_call' => 'Sto accettando una chiamata da %s...',
             'account_banned' => '!!!!!!! ATTENZIONE !!!!!!!
Il sistema antispam di telegram ha sospeso questo account.
Per continuare, Ã¨ richiesta una verifica manuale.
Mandare un\'email in inglese a recover@telegram.org, chiedendo di sbannare il numero di telefono %s, spiegando brevemente cosa si ha intenzione di fare con l\'account.
Poi rieseguite nuovamente il login.
Ignorare questo messaggio se la sessione Ã¨ stata resettata intenzionalmente.',
             'already_loggedIn' => 'Questa istanza di MadelineProto Ã¨ giÃ  loggata!',
             'apiAppDescLabelWeb' => 'Descrizione',
             'apiAppInstructionsAuto0' => 'Inserisci il nome dell\'app, puÃ² essere qualsiasi cosa: ',
             'apiAppInstructionsAuto1' => 'Inserisci il nome ridotto dell\'app, caratteri alfanumerici: ',
             'apiAppInstructionsAuto2' => 'Inserisci il sito internet dell\'app, oppure t.me/username: ',
             'apiAppInstructionsAuto3' => 'Inserisci la piattaforma dell\'app: ',
             'apiAppInstructionsAuto4' => 'Descrivi la tua app: ',
             'apiAppInstructionsAutoTypeOther' => 'Altro (specificare nella descrizione)',
             'apiAppInstructionsManual0' => 'titolo dell\'app, puÃ² essere qualsiasi cosa',
             'apiAppInstructionsManual1' => 'il nome ridotto dell\'app, caratteri alfanumerici',
             'apiAppInstructionsManual2' => 'L\'indirizzo del tuo sito, oppure t.me/username',
             'apiAppInstructionsManual3' => 'Qualsiasi',
             'apiAppInstructionsManual4' => 'Descrivi la tua app',
             'apiAppShortNameLabelWeb' => 'Nome breve',
             'apiAppTitleLabelWeb' => 'Titolo dell\'app',
             'apiAppUrlLabelWeb' => 'URL',
             'apiAppWeb' => 'Inserire informazioni API',
             'apiAutoCodeLabelWeb' => 'Codice di accesso',
             'apiAutoCodeLeadWeb' => 'my.telegram.org ha inviato un codice di accesso alla tua app Telegram, nella chat con Telegram (non via SMS).',
             'apiAutoCodeTitleWeb' => 'Inserisci il codice ricevuto su Telegram',
             'apiAutoErrorTitle' => 'my.telegram.org ha restituito un errore',
             'apiAutoFallbackManual' => 'Inserisci invece API ID e hash manualmente.',
             'apiAutoPhoneHintWeb' => 'my.telegram.org invia il codice di accesso alla tua app Telegram, non via SMS.',
             'apiAutoPrompt0' => 'Inserisci un numero di telefono che Ã¨ giÃ  registrato su Telegram: ',
             'apiAutoPrompt1' => 'Inserisci il codice di verifica che hai ricevuto su Telegram: ',
             'apiAutoWeb' => 'Inserisci un numero di telefono <b>gi&agrave; registrato su Telegram</b> per ottenere l&apos;API ID',
             'apiChooseAutoWeb' => 'Ottienili automaticamente',
             'apiChooseManualAuto' => 'Vuoi ottenere API ID e hash automaticamente da my.telegram.org (a) o inserirli manualmente (m)?',
             'apiChooseManualAutoTip' => 'Nota che puoi anche fornire i parametri API direttamente nelle impostazioni: %s',
             'apiChooseManualAutoTipWeb' => 'Nota che puoi anche fornire i parametri API direttamente nelle <a target="_blank" href="%s">impostazioni</a>.',
             'apiChooseManualWeb' => 'Ho giÃ  un API ID',
             'apiChoosePrompt' => 'La tua scelta (m/a): ',
             'apiCreateErrorTitle' => 'my.telegram.org non ha creato l\'app',
             'apiCreateLeadWeb' => 'Questo account non ha ancora un\'app su my.telegram.org. Questi dati sono visibili solo a te e potrai modificarli in seguito.',
             'apiCreateTitleWeb' => 'Crea la tua app',
             'apiCreateVpsWarning' => 'Telegram non permette di creare nuove app su my.telegram.org da indirizzi IP di VPS, VPN e altri datacenter, e la richiesta Ã¨ partita dall\'IP della macchina su cui gira MadelineProto. Crea l\'app manualmente su https://my.telegram.org da una connessione domestica (ad esempio dal telefono con i dati mobili), poi inserisci API ID e hash.',
             'apiCreateWeb' => 'Crea app',
             'apiError' => 'ERRORE: %s. Prova ancora.',
             'apiInvalidWeb' => 'L\'API ID Ã¨ un numero e l\'API hash Ã¨ lungo 32 caratteri: copiali di nuovo da my.telegram.org.',
             'apiManualInstructions0' => 'Effettua il login su https://my.telegram.org',
             'apiManualInstructions1' => 'Vai su API development tools',
             'apiManualInstructions2' => 'Clicca su create application',
             'apiManualPrompt0' => 'Inserisci il tuo API ID: ',
             'apiManualPrompt1' => 'Inserisci il tuo API hash: ',
             'apiManualWeb' => 'Inserisci il tuo API ID e API hash',
             'apiParamsError' => 'Non hai fornito tutti i parametri richiesti!',
             'apiSaveWeb' => 'Salva e continua',
             'apiSendCodeWeb' => 'Invia codice',
             'apiSetupLeadWeb' => 'Ogni app Telegram ha bisogno di un API ID e di un hash. MadelineProto puÃ² ottenerli per te da my.telegram.org, usando il numero di telefono di un account Telegram giÃ  esistente.',
             'apiSetupTitleWeb' => 'Ottieni API ID e hash',
             'apiStartOverWeb' => 'Ricomincia',
             'api_not_set' => 'Devi specificare una chiave ed un ID API, ottienili su https://my.telegram.org',
             'array_invalid' => 'Il valore fornito non Ã¨ un array',
             'baseDirLimitation' => 'Ãˆ presente una limitazione basedir: questo puÃ² impattare negativamente sulle performance e causare vari problemi, si prega di disabilitare le limitazioni basedir se possibile!',
             'bool_error' => 'Non sono riuscito ad estrarre un booleano',
             'botAlreadyRunning' => 'Il bot Ã¨ giÃ  in esecuzione!',
             'botapi_conversion_error' => 'Non sono risucito a convertire %s in un oggetto bot API',
             'call_already_accepted' => 'La chiamata %s Ã¨ giÃ  stata accettata',
             'call_already_declined' => 'La chiamata %s Ã¨ giÃ  stata annullata',
             'call_completing' => 'Sto completando una chiamata da %s...',
             'call_confirming' => 'Sto confermando una chiamata da %s...',
             'call_discarding' => 'Sto rifiutando la chiamata %s...',
             'call_error_1' => 'Impossibile trovare ed accettare la chiamata %s',
             'call_error_2' => 'Impossibile trovare e confermare la chiamata %s',
             'call_error_3' => 'Impossibile trovare e completare la chiamata %s',
             'cli_need_dl.php_link' => 'Ãˆ necessario specificare l\'URL dello script di scaricamente per usare getDownloadLink via CLI!',
             'constructor_not_found' => 'Costruttore non trovato per tipo: ',
             'could_not_connect_to_MadelineProto' => 'Non Ã¨ stato possibile collegarsi a MadelineProto, per favore abilitare la funzione proc_open e rimuovere ogni limitazione open_basedir o disabilitare le rewrite lato server per fixare! Se l\'avete giÃ  fatto, assicuratevi che la versione PHP CLI Ã¨ la stessa versione di PHP web (apache/php-fpm) (stessa versione, estensioni, eccetera) e controllate il file MadelineProto.log per piÃ¹ informazioni sull\'errore che ha impedito l\'avvio del server IPC.',
             'could_not_convert_object' => 'Non Ã¨ stato possibile convertire l\'oggetto di tipo %s',
             'deserialization_error' => 'C\'Ã¨ stato un errore durante la deserializzazione',
             'dl.php_check_logs_make_sure_session_running' => 'Sia l\'event handler associato al bot che il server IPC della sessione sono offline, controllare i log e assicurarsi che almeno uno dei due sia online!',
             'dl.php_powered_by_madelineproto' => 'Server di scaricamento file Telegram (fino a 4GB), creato usando <a href="https://docs.madelineproto.xyz" target="_blank">MadelineProto</a>!<br>Clicca <a href="https://docs.madelineproto.xyz/docs/FILES.html#getting-a-download-link" target="_blank">qui</a> per configurare il tuo personale server di scaricamento file Telegram!',
             'do_not_delete_MadelineProto.log' => 'il file MadelineProto.log non deve essere mai eliminato, per favore imposta una dimensione massima nelle impostazioni invece di eliminarlo!',
             'do_not_remove_MadelineProto.log_phar' => 'Per favore non rimuovere madeline.phar o madeline.php, o MadelineProto crasherÃ . Se hai problemi con MadelineProto, segnalali su https://github.com/danog/MadelineProto o https://t.me/pwrtelegramgroup',
             'do_not_use_blocking_class' => 'per motivi di performance, gli event handler non possono usare la classe bloccante non-asincrona %s, per favore usa %s',
             'do_not_use_blocking_function' => 'per motivi di performance, gli event handler non possono usare la funzione bloccante non asincrona %s, per favore usa %s',
             'do_not_use_deprecated_function' => 'la funzione %s Ã¨ deprecata, per favore usa %s',
             'do_not_use_non_root_require_in_event_handler' => 'per motivi di performance, non Ã¨ possibile usare require o include dentro l\'event handler, Ã¨ possibile includere file solo all\'esterno della classe.',
             'do_not_use_yield' => 'MadelineProto 8 non richede e non supporta l\'uso della parola chiave yield in funzioni asincrono, Ã¨ necessario rimuovere tutte gli yield usati in precedenza per chiamate a funzioni asincrone',
             'done' => 'Fatto!',
             'encode_double_error' => 'Non sono riuscito a codificare il numero a virgola mobile fornito',
             'extensionRecommended' => 'Attenzione: l\'estensione %s non Ã¨ installata, si prega di installarla per velocizzare MadelineProto!',
             'extensionRequired' => 'MadelineProto richiede l\'estensione %s. %s',
             'extensionRequiredInstallWithApt' => 'Provate ad avviare sudo apt-get install %s.',
             'extensionRequiredInstallWithCustomInstructions' => 'Seguire le istruzioni su %s per installarla.',
             'file_not_exist' => 'Il file specificato non esiste',
             'file_parsing' => 'Leggendo %s...',
             'file_type_invalid' => 'Ãˆ stato rilevato un tipo di file non valido (%s)',
             'fingerprint_invalid' => 'fingerprint della chiave non valido!',
             'go' => 'Vai',
             'invalid_dl.php_session' => '%s non Ã¨ uno script di scaricamento valido perchÃ© l\'ID della sessione non Ã¨ corretto (mi aspettavo %s, ho ricevuto %s)',
             'ipc_tcp_warning' => 'UNIX sockets are not available, so the IPC server of the session listens on TCP (127.0.0.1) instead: any program running on this machine can connect to it, and control the session. On Windows 10 1803+, enable the sockets extension (extension=sockets in php.ini) to use UNIX sockets.',
             'length_too_big' => 'Il valore fornito Ã¨ troppo lungo',
             'loginBot' => 'Inserisci il tuo bot token: ',
             'loginBotTokenHintWeb' => 'Il token che ti ha dato @BotFather quando hai creato il bot.',
             'loginBotTokenWeb' => 'Token del bot',
             'loginChoosePromptWeb' => 'Vuoi effettuare il login come utente o come bot?',
             'loginCodeLeadWeb' => 'Telegram ha inviato un codice di accesso alla tua app Telegram, oppure via SMS se non hai effettuato l\'accesso da nessun\'altra parte.',
             'loginCodeTitleWeb' => 'Inserisci il codice di accesso',
             'loginLeadWeb' => 'Scegli l\'account o il bot con cui funzionerÃ  questa istanza di MadelineProto.',
             'loginManual' => 'In alternativa, puoi inserire un bot token o un numero di telefono per effettuare il login manualmente: ',
             'loginNoCode' => 'Non hai fornito un codice di verifica!',
             'loginNoName' => 'Non hai fornito un nome!',
             'loginNoPass' => 'Non hai fornito la password!',
             'loginOptionBot' => 'Bot',
             'loginOptionUser' => 'Utente',
             'loginPasswordLabelWeb' => 'Password',
             'loginPasswordLeadWeb' => 'Questo account Ã¨ protetto da una password, impostata in Impostazioni > Privacy e sicurezza > Verifica in due passaggi.',
             'loginPasswordTitleWeb' => 'Verifica in due passaggi',
             'loginQr' => 'Apri Telegram sul tuo telefono, vai in Impostazioni > Dispositivi > Collega dispositivo desktop e scansiona il codice QR per fare il login automaticamente.',
             'loginQrCodeExpired' => 'Il codice QR Ã¨ scaduto, generazione di un nuovo codice QR in corso...',
             'loginQrCodeSuccessful' => 'Il login con codice QR Ã¨ stato effettuato correttamente!',
             'loginTitleWeb' => 'Accedi a Telegram',
             'loginTypeWeb' => 'Accedi come',
             'loginUser' => 'Inserisci il tuo numero di telefono: ',
             'loginUserCode' => 'Inserisci il codice di verifica: ',
             'loginUserPass' => 'Inserisci la tua password (suggerimento %s): ',
             'loginUserPassHint' => 'Suggerimento: %s',
             'loginUserPassWeb' => 'Inserisci la tua password: ',
             'loginUserPhoneCodeWeb' => 'Codice di verifica',
             'loginUserPhoneHintWeb' => 'Con il prefisso internazionale, ad esempio +39 123 456 7890.',
             'loginUserPhoneWeb' => 'Numero di telefono',
             'loginWebQr' => 'Puoi anche effettuare il login automaticamente aprendo Telegram sul telefono, andando in Impostazioni > Dispositivi > Collega dispositivo desktop e scansionando il seguente codice QR:',
             'loginWebQr1' => 'Apri Telegram sul tuo telefono',
             'loginWebQr2' => 'Vai su Impostazioni > Dispositivi > Collega dispositivo desktop',
             'loginWebQr3' => 'Inquadra il codice sullo schermo per completare il login',
             'login_2fa_enabled' => 'L\'autenticazione a due fattori Ã¨ abilitata, dovrai chiamare il metodo complete2falogin...',
             'login_auth_key' => 'Sto facendo il login con la chiave di autorizzazione...',
             'login_bot' => 'Sto eseguendo il login come bot...',
             'login_code_sending' => 'Sto inviando il codice...',
             'login_code_sent' => 'Il codice Ã¨ stato inviato correttamente! Una volta ricevuto il codice dovrai usare la funzione completePhoneLogin.',
             'login_code_uncalled' => 'Non sto aspettando il codice, usa prima la funzione phoneLogin!',
             'login_need_signup' => 'Questo numero non Ã¨ registrato su telegram, dovrai chiamare la funzione completeSignup...',
             'login_ok' => 'Il login Ã¨ stato eseguito correttamente!',
             'login_user' => 'Sto eseguendo il login come utente normale...',
             'logout_ok' => 'Il logout Ã¨ stato eseguito correttamente!',
             'long_not_16' => 'Il valore fornito non Ã¨ lungo 16 byte',
             'long_not_32' => 'Il valore fornito non Ã¨ lungo 32 byte',
             'long_not_64' => 'Il valore fornito non Ã¨ lungo 64 byte',
             'madelineproto_ready' => 'MadelineProto Ã¨ pronto!',
             'manualAdminActionRequired' => '!!!!!!!!! Ãˆ NECESSARIO L\'INTERVENTO DELL\'AMMINISTRATORE DI SISTEMA !!!!!!!!!',
             'method_not_found' => 'Impossibile trovare il seguente metodo: ',
             'mmapErrorPart1' => 'Ãˆ stato raggiunto il limite massimo di regioni di memoria mappate (mmap, %s): Ã¨ necessario incrementare il parametro di configurazione del kernel vm.max_map_count a 262144 per correggere l\'errore.',
             'mmapErrorPart2' => 'Per correggere l\'errore, avviare il seguente comando come root: %s',
             'mmapErrorPart3' => 'Per correggere l\'errore in modo permanente, anche dopo un riavvio: %s',
             'mmapErrorPart4' => 'Su Windows e WSL, potrebbe aiutare l\'incremento della dimensione del file di paginazione; passare a Linux nativo se il problema persiste.',
             'must_have_declare_types' => 'per motivi di performance, la prima riga del file contenente l\'event handler deve essere uguale a "declare(strict_types=1);"',
             'nearest_dc' => 'Siamo in %s, il DC piÃ¹ vicino Ã¨ %d.',
             'need_dl.php' => 'Impossibile generare lo script di scaricamento di default (%s), Ã¨ necessario creare un file dl.php con il seguente contenuto: %s e passare il suo URL al secondo parametro di getDownloadLink',
             'noReportPeers' => 'Attenzione: non Ã¨ stato impostato alcun peer per la segnalazione errori, per favore aggiungi il seguente metodo all\'event handler',
             'non_text_conversion' => 'Non posso ancora convertire messaggi media!',
             'not_loggedIn' => 'Non ho ancora fatto il login!',
             'not_numeric' => 'Il valore fornito non Ã¨ numerico',
             'params_missing' => 'Non hai fornito un parametro obbligatorio, rileggi la documentazione API',
             'peer_not_in_db' => 'Questo utente/gruppo/canale non Ã¨ presente nel database interno MadelineProto',
             'plugin_path_does_not_exist' => 'La cartella dei plugin %s non esiste!',
             'plugins_do_not_use_require' => 'per motivi di performance, i plugin possono solo includere altri file presenti nella cartella plugins usando il meccanismo di autocaricamento PSR-4 (non usando require o include).',
             'plugins_must_have_exactly_one_class' => 'un plugin deve definire esattamente una classe! Per definire piÃ¹ classi, interfacce o trait, si prega di creare file separati, che verranno caricati da MadelineProto automaticamente.',
             'predicate_not_set' => 'Il predicato (valore sotto chiave _, esempio [\'_\' => \'inputPeer\']) non Ã¨ impostato!',
             'recommend_not_use_filesystem_function' => 'l\'uso della funzione %s non Ã¨ racommandato, perchÃ© l\'accesso al filesystem durante la gestione degli update rallenterÃ  il bot, si prega di leggere https://docs.madelineproto.xyz/docs/UPDATES.html#avoiding-the-use-of-filesystem-functions per una lista di modi alternativi per memorizzare informazioni in modi che non rallenteranno il tuo bot!',
             'rpc_tg_error' => 'Telegram ha ritornato un errore RPC: %s (%s), causato da %s:%s%sTL trace:',
             'sec_peer_not_in_db' => 'La chat segreta non Ã¨ presente nel database interno MadelineProto',
             'secret_chat_skipping' => 'Non ho la chat segreta %s nel database, ignorando messaggio...',
             'serialization_ofd' => 'La serializzazione non Ã¨ aggiornata, reistanziamento dell\'oggetto in corso!',
             'session_corrupted' => 'La sessione si Ã¨ corrotta!',
             'signing_up' => 'Mi sto registrando su telegram come utente normale...',
             'signupFirstName' => 'Inserisci il tuo nome: ',
             'signupFirstNameWeb' => 'Nome',
             'signupLastName' => 'Inserisci il tuo cognome: ',
             'signupLastNameWeb' => 'Cognome',
             'signupLeadWeb' => 'Questo numero di telefono non Ã¨ ancora su Telegram: scegli il nome del nuovo account.',
             'signupTitleWeb' => 'Crea il tuo account',
             'signupWeb' => 'Registrazione',
             'signup_ok' => 'Mi sono registrato su Telegram!',
             'signup_uncalled' => 'Chiama prima le funzioni phoneLogin e completePhoneLogin!',
             'src_file_invalid' => 'Ãˆ stato fornito un file sorgente non valido: ',
             'static_analysis_minor' => 'Ãˆ stato riscontrato un problema minore durante l\'analisi statica di %s: %s',
             'static_analysis_severe' => 'Ãˆ stato riscontrato un problema grave durante l\'analisi statica di %s: %s',
             'stream_handle_invalid' => 'Il valore fornito non Ã¨ uno stream.',
             'string_required' => 'Ãˆ richiesta una stringa!',
             'translate_madelineproto_cli' => 'MadelineProto puÃ² essere tradotto nella tua lingua (stato attuale della traduzione: %d%%), vai su https://github.com/danog/MadelineProto/tree/v8/langs per contribuire alla traduzione!',
             'translate_madelineproto_web' => 'MadelineProto pu&ograve; essere tradotto nella tua lingua (stato attuale della traduzione: %d%%), cliccate  <a href="https://github.com/danog/MadelineProto/tree/v8/langs" target="_blank">qui per contribuire alla traduzione!</a>',
             'tweb.ACCESS_TOKEN_EXPIRED' => 'Questo token del bot Ã¨ stato revocato',
             'tweb.ACCESS_TOKEN_INVALID' => 'Token del bot non valido',
             'tweb.AccDescr.LoginCode' => 'Codice di accesso',
             'tweb.Close' => 'Chiudi',
             'tweb.Code' => 'Codice',
             'tweb.Contacts.PhoneNumber.Placeholder' => 'Numero di telefono',
             'tweb.Country' => 'Paese',
             'tweb.DarkMode' => 'ModalitÃ  scura',
             'tweb.Error.AnError' => 'Si Ã¨ verificato un errore. Riprova piÃ¹ tardi.',
             'tweb.FIRSTNAME_INVALID' => 'Nome non valido',
             'tweb.FirstName' => 'Nome (obbligatorio)',
             'tweb.LASTNAME_INVALID' => 'Cognome non valido',
             'tweb.LastName' => 'Cognome (facoltativo)',
             'tweb.Login.Bot' => 'Accedi come bot >',
             'tweb.Login.Bot.Cancel' => 'Accedi come utente >',
             'tweb.Login.Bot.Subtitle' => 'Inserisci il token che ti ha dato @BotFather
quando hai creato il bot.',
             'tweb.Login.Bot.Title' => 'Accedi come bot',
             'tweb.Login.Bot.Token' => 'Token del bot',
             'tweb.Login.Code.SecretPhrase' => 'Frase segreta',
             'tweb.Login.Code.SecretWord' => 'Parola segreta',
             'tweb.Login.Code.SentCall' => 'Ti chiameremo per dettarti
il codice.',
             'tweb.Login.Code.SentEmail' => 'Controlla la tua email **%s** (non dimenticare la cartella spam) e inserisci il codice che ti abbiamo appena inviato.',
             'tweb.Login.Code.SentInApp' => 'Ti abbiamo inviato un messaggio su Telegram
con il codice.',
             'tweb.Login.Code.SentMissedCall' => 'Entro pochi secondi riceverai
una breve chiamata da **%s**. Non rispondere:
inserisci solo le ultime cifre di quel numero.',
             'tweb.Login.Code.SentSms' => 'Ti abbiamo inviato un SMS
con il codice.',
             'tweb.Login.Code.SentSmsPhrase' => 'Abbiamo inviato un SMS con una frase segreta
al tuo numero **%s**.',
             'tweb.Login.Code.SentSmsPhraseBeginning' => 'Abbiamo inviato un SMS con una frase segreta che inizia
con "**%2$s**" al tuo numero **%1$s**.',
             'tweb.Login.Code.SentSmsWord' => 'Abbiamo inviato un SMS con una parola segreta
al tuo numero **%s**.',
             'tweb.Login.Code.SentSmsWordBeginning' => 'Abbiamo inviato un SMS con una parola segreta che inizia
con "**%2$s**" al tuo numero **%1$s**.',
             'tweb.Login.Code.SentUnknown' => 'Cerca un codice
ovunque (tipo: %s)',
             'tweb.Login.Code.WordBeginningInvalid' => 'Parola non corretta',
             'tweb.Login.Code.WordInvalid' => 'Non corretta, riprova',
             'tweb.Login.Next' => 'Avanti',
             'tweb.Login.Password.Subtitle' => 'Il tuo account Ã¨ protetto
da una password aggiuntiva',
             'tweb.Login.Password.Title' => 'Inserisci la tua password',
             'tweb.Login.PhoneLabelInvalid' => 'Numero di telefono non valido',
             'tweb.Login.QR.Cancel' => 'Accedi con il numero di telefono >',
             'tweb.Login.QR.Help1' => 'Apri Telegram sul tuo telefono',
             'tweb.Login.QR.Help2' => 'Vai su **Impostazioni** > **Dispositivi** > **Aggiungi dispositivo**',
             'tweb.Login.QR.Help3' => 'Inquadra questo schermo con il telefono per confermare l\'accesso',
             'tweb.Login.QR.Login' => 'Accedi con codice QR >',
             'tweb.Login.QR.Subtitle' => 'Scansiona con l\'app Telegram sul tuo telefono',
             'tweb.Login.QR.Title' => 'Accedi con codice QR',
             'tweb.Login.Register.Subtitle' => 'Inserisci il tuo nome
per creare il tuo account',
             'tweb.Login.StartText' => 'Conferma il prefisso del tuo paese
e inserisci il tuo numero di telefono.',
             'tweb.Login.Title' => 'Accedi a MadelineProto',
             'tweb.LoginPassword' => 'Password',
             'tweb.Optional' => 'facoltativo',
             'tweb.PASSWORD_HASH_INVALID' => 'Password errata',
             'tweb.PHONE_CODE_EMPTY' => 'Codice non valido',
             'tweb.PHONE_CODE_EXPIRED' => 'Codice scaduto',
             'tweb.PHONE_CODE_INVALID' => 'Codice non valido',
             'tweb.PHONE_NUMBER_BANNED' => 'Questo numero di telefono Ã¨ stato bannato',
             'tweb.PHONE_NUMBER_FLOOD' => 'Troppi tentativi, riprova piÃ¹ tardi',
             'tweb.PHONE_NUMBER_INVALID' => 'Numero di telefono non valido',
             'tweb.PHONE_PASSWORD_FLOOD' => 'Troppi tentativi, riprova piÃ¹ tardi',
             'tweb.PhoneNumber.Code.Fragment.Info' => 'Ottieni il codice nella sezione Numbers su [Fragment]()',
             'tweb.PleaseWait' => 'Attendi...',
             'tweb.StartMessaging' => 'Inizia a messaggiare',
             'tweb.YourName' => 'Il tuo nome',
             'type_extract_error' => 'Impossibile estrarre il tipo "%s", dovresti aggiornare MadelineProto!',
             'type_extract_error_id' => 'Non sono riuscito ad estrarre il tipo %s con ID %s, dovresti aggiornare MadelineProto!',
             'update_madelineproto' => 'Stai usando una vecchia versione di MadelineProto, Ã¨ necessario un aggiornamento: la versione che stai usando adesso Ã¨ %s, ma l\'ultima versione con tutti gli ultimi bugfix e tante nuove funzionalitÃ  Ã¨ %s!',
             'value_bigger_than_2147483647' => 'Il valore fornito (%s) Ã¨ maggiore di 2147483647',
             'value_bigger_than_4294967296' => 'Il valore fornito (%s) Ã¨ maggiore di 4294967296',
             'value_bigger_than_9223372036854775807' => 'Il valore fornito (%s) Ã¨ maggiore di 9223372036854775807',
             'value_smaller_than_0' => 'Il valore fornito (%s) Ã¨ minore di 0',
             'value_smaller_than_2147483648' => 'Il valore fornito (%s) Ã¨ minore di -2147483648',
             'value_smaller_than_9223372036854775808' => 'Il valore fornito (%s) Ã¨ minore di -9223372036854775808',
             'waveform_must_have_100_values' => 'Una forma d\'onda deve essere un array contenente 100 valori!',
             'waveform_value' => 'Un singolo valore di una forma d\'onda deve essere compreso tra 0 e 31!',
             'webContinue' => 'Continua',
             'webDocs' => 'Documentazione',
             'webDoneLead' => 'MadelineProto continua a funzionare sul server: puoi chiudere questa pagina.',
             'webError' => '%s. Controlla e riprova.',
             'webFieldRequired' => 'Compila questo campo.',
             'webHidePassword' => 'Nascondi',
             'webLogin' => 'Accedi',
             'webOptional' => 'facoltativo',
             'webPhoneInvalid' => 'Inserisci il numero di telefono con il prefisso internazionale, ad esempio +39 123 456 7890.',
             'webProgress' => 'Avanzamento della configurazione',
             'webQrTitle' => 'Accedi con un codice QR',
             'webQrWaiting' => 'In attesa della scansioneâ€¦',
             'webShowPassword' => 'Mostra',
             'webStepAccount' => 'Account',
             'webStepApp' => 'API ID e hash',
             'webStepDone' => 'In esecuzione',
             'webStepVerify' => 'Verifica',
         ],
        'ja' =>
         [
             '2fa_uncalled' => 'ãƒ‘ã‚¹ãƒ¯ãƒ¼ãƒ‰ã‚’å¾…æ©Ÿã�—ã�¦ã�„ã�¾ã�›ã‚“ï¼�å…ˆã�« phoneLogin ã�¨ completePhoneLogin ãƒ¡ã‚½ãƒƒãƒ‰ã‚’å‘¼ã�³å‡ºã�—ã�¦ã��ã� ã�•ã�„ï¼�',
             'accepting_call' => '%s ã�‹ã‚‰ã�®é€šè©±ã�«å¿œç­”ã�—ã�¦ã�„ã�¾ã�™...',
             'account_banned' => '!!!!!!! è­¦å‘Š !!!!!!!
Telegram ã�®ãƒ•ãƒ©ãƒƒãƒ‰é˜²æ­¢ã‚·ã‚¹ãƒ†ãƒ ã�«ã‚ˆã�£ã�¦ã€�ã�“ã�®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã�¯å�œæ­¢ã�•ã‚Œã�¾ã�—ã�Ÿã€‚
ç¶šè¡Œã�™ã‚‹ã�«ã�¯æ‰‹å‹•ã�§ã�®èª�è¨¼ã�Œå¿…è¦�ã�§ã�™ã€‚
recover@telegram.org ã�«ãƒ¡ãƒ¼ãƒ«ã‚’é€�ã‚Šã€�é›»è©±ç•ªå�· %s ã�®å�œæ­¢è§£é™¤ã‚’ä¾�é ¼ã�™ã‚‹ã�¨ã�¨ã‚‚ã�«ã€�ã�“ã�®é›»è©±ç•ªå�·ã�§ä½•ã‚’ã�™ã‚‹ã�®ã�‹ã‚’ç°¡å�˜ã�«èª¬æ˜Žã�—ã�¦ã��ã� ã�•ã�„ã€‚
ã��ã�®å¾Œã€�ã‚‚ã�†ä¸€åº¦ãƒ­ã‚°ã‚¤ãƒ³ã�—ã�¦ã��ã� ã�•ã�„ã€‚
ã�“ã�®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’æ„�å›³çš„ã�«å‰Šé™¤ã�—ã�Ÿå ´å�ˆã�¯ã€�ã�“ã�®ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã‚’ç„¡è¦–ã�—ã�¦ã��ã� ã�•ã�„ã€‚',
             'already_loggedIn' => 'ã�“ã�® MadelineProto ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã�¯ã�™ã�§ã�«ãƒ­ã‚°ã‚¤ãƒ³ã�—ã�¦ã�„ã�¾ã�™ï¼�',
             'apiAppDescLabelWeb' => 'èª¬æ˜Ž',
             'apiAppInstructionsAuto0' => 'ã‚¢ãƒ—ãƒªã�®å��å‰�ã‚’å…¥åŠ›ã�—ã�¦ã��ã� ã�•ã�„ (ä»»æ„�): ',
             'apiAppInstructionsAuto1' => 'ã‚¢ãƒ—ãƒªã�®çŸ­ã�„å��å‰�ã‚’å…¥åŠ›ã�—ã�¦ã��ã� ã�•ã�„ (è‹±æ•°å­—ã€�5ã€œ32 æ–‡å­—): ',
             'apiAppInstructionsAuto2' => 'ã‚¢ãƒ—ãƒª/ã‚¦ã‚§ãƒ–ã‚µã‚¤ãƒˆã�® URLã€�ã�¾ã�Ÿã�¯ t.me/yourusername ã‚’å…¥åŠ›ã�—ã�¦ã��ã� ã�•ã�„: ',
             'apiAppInstructionsAuto3' => 'ã‚¢ãƒ—ãƒªã�®ãƒ—ãƒ©ãƒƒãƒˆãƒ•ã‚©ãƒ¼ãƒ ã‚’å…¥åŠ›ã�—ã�¦ã��ã� ã�•ã�„: ',
             'apiAppInstructionsAuto4' => 'ã‚¢ãƒ—ãƒªã�®èª¬æ˜Žã‚’å…¥åŠ›ã�—ã�¦ã��ã� ã�•ã�„: ',
             'apiAppInstructionsAutoTypeOther' => 'ã��ã�®ä»– (èª¬æ˜Žæ¬„ã�«è¨˜å…¥)',
             'apiAppInstructionsManual0' => 'ã‚¢ãƒ—ãƒªã�®å��å‰� (ä»»æ„�)',
             'apiAppInstructionsManual1' => 'ã‚¢ãƒ—ãƒªã�®çŸ­ã�„å��å‰� (è‹±æ•°å­—ã€�5ã€œ32 æ–‡å­—)',
             'apiAppInstructionsManual2' => 'ã‚¢ãƒ—ãƒª/ã‚¦ã‚§ãƒ–ã‚µã‚¤ãƒˆã�® URLã€�ã�¾ã�Ÿã�¯ t.me/yourusername',
             'apiAppInstructionsManual3' => 'ä»»æ„�',
             'apiAppInstructionsManual4' => 'ã�“ã�“ã�«ã‚¢ãƒ—ãƒªã�®èª¬æ˜Žã‚’å…¥åŠ›ã�—ã�¾ã�™',
             'apiAppShortNameLabelWeb' => 'çŸ­ã�„å��å‰�',
             'apiAppTitleLabelWeb' => 'ã‚¢ãƒ—ãƒªå��',
             'apiAppUrlLabelWeb' => 'URL',
             'apiAppWeb' => 'API æƒ…å ±ã‚’å…¥åŠ›ã�—ã�¦ã��ã� ã�•ã�„',
             'apiAutoCodeLabelWeb' => 'ãƒ­ã‚°ã‚¤ãƒ³ã‚³ãƒ¼ãƒ‰',
             'apiAutoCodeLeadWeb' => 'my.telegram.org ã�Œ Telegram ã‚¢ãƒ—ãƒªã�®ã€ŒTelegramã€�ã�¨ã�®ãƒ�ãƒ£ãƒƒãƒˆã�«ãƒ­ã‚°ã‚¤ãƒ³ã‚³ãƒ¼ãƒ‰ã‚’é€�ä¿¡ã�—ã�¾ã�—ã�Ÿ (SMS ã�§ã�¯ã�‚ã‚Šã�¾ã�›ã‚“)ã€‚',
             'apiAutoCodeTitleWeb' => 'Telegram ã�§å�—ã�‘å�–ã�£ã�Ÿã‚³ãƒ¼ãƒ‰ã‚’å…¥åŠ›',
             'apiAutoErrorTitle' => 'my.telegram.org ã�Œã‚¨ãƒ©ãƒ¼ã‚’è¿”ã�—ã�¾ã�—ã�Ÿ',
             'apiAutoFallbackManual' => 'ä»£ã‚�ã‚Šã�« API ID ã�¨ hash ã‚’æ‰‹å‹•ã�§å…¥åŠ›ã�—ã�¦ã��ã� ã�•ã�„ã€‚',
             'apiAutoPhoneHintWeb' => 'my.telegram.org ã�¯ãƒ­ã‚°ã‚¤ãƒ³ã‚³ãƒ¼ãƒ‰ã‚’ SMS ã�§ã�¯ã�ªã�� Telegram ã‚¢ãƒ—ãƒªã�«é€�ä¿¡ã�—ã�¾ã�™ã€‚',
             'apiAutoPrompt0' => 'Telegram ã�«ç™»éŒ²æ¸ˆã�¿ã�®é›»è©±ç•ªå�·ã‚’å…¥åŠ›ã�—ã�¦ã��ã� ã�•ã�„: ',
             'apiAutoPrompt1' => 'Telegram ã�§å�—ã�‘å�–ã�£ã�Ÿèª�è¨¼ã‚³ãƒ¼ãƒ‰ã‚’å…¥åŠ›ã�—ã�¦ã��ã� ã�•ã�„: ',
             'apiAutoWeb' => 'API ID ã‚’å�–å¾—ã�™ã‚‹ã�«ã�¯ã€�Telegram ã�«<b>ç™»éŒ²æ¸ˆã�¿</b>ã�®é›»è©±ç•ªå�·ã‚’å…¥åŠ›ã�—ã�¦ã��ã� ã�•ã�„',
             'apiChooseAutoWeb' => 'è‡ªå‹•ã�§å�–å¾—ã�™ã‚‹',
             'apiChooseManualAuto' => 'API ID ã�¨ hash ã‚’ my.telegram.org ã�‹ã‚‰è‡ªå‹•ã�§å�–å¾—ã�—ã�¾ã�™ã�‹ (a)ã€�ã��ã‚Œã�¨ã‚‚æ‰‹å‹•ã�§å…¥åŠ›ã�—ã�¾ã�™ã�‹ (m)ï¼Ÿ',
             'apiChooseManualAutoTip' => 'API ID/hash ã�¯ã€�è¨­å®šã‚’ä½¿ã�£ã�¦ã‚³ãƒ¼ãƒ‰å†…ã�§ç›´æŽ¥æŒ‡å®šã�™ã‚‹ã�“ã�¨ã‚‚ã�§ã��ã�¾ã�™: %s',
             'apiChooseManualAutoTipWeb' => 'API ID/hash ã�¯ã€�<a target="_blank" href="%s">è¨­å®š</a>ã‚’ä½¿ã�£ã�¦ã‚³ãƒ¼ãƒ‰å†…ã�§ç›´æŽ¥æŒ‡å®šã�™ã‚‹ã�“ã�¨ã‚‚ã�§ã��ã�¾ã�™ã€‚',
             'apiChooseManualWeb' => 'API ID ã‚’æŒ�ã�£ã�¦ã�„ã�¾ã�™',
             'apiChoosePrompt' => 'é�¸æŠžã�—ã�¦ã��ã� ã�•ã�„ (m/a): ',
             'apiCreateErrorTitle' => 'my.telegram.org ã�§ã‚¢ãƒ—ãƒªã‚’ä½œæˆ�ã�§ã��ã�¾ã�›ã‚“ã�§ã�—ã�Ÿ',
             'apiCreateLeadWeb' => 'ã�“ã�®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã�«ã�¯ my.telegram.org ã�®ã‚¢ãƒ—ãƒªã�Œã�¾ã� ã�‚ã‚Šã�¾ã�›ã‚“ã€‚ä»¥ä¸‹ã�®æƒ…å ±ã�¯ã�‚ã�ªã�Ÿã�«ã�®ã�¿è¡¨ç¤ºã�•ã‚Œã€�å¾Œã�‹ã‚‰å¤‰æ›´ã�§ã��ã�¾ã�™ã€‚',
             'apiCreateTitleWeb' => 'ã‚¢ãƒ—ãƒªã‚’ä½œæˆ�',
             'apiCreateVpsWarning' => 'Telegram ã�§ã�¯ã€�VPSã€�VPN ã�ªã�©ã�®ãƒ‡ãƒ¼ã‚¿ã‚»ãƒ³ã‚¿ãƒ¼ã�® IP ç¯„å›²ã�‹ã‚‰ my.telegram.org ã�§æ–°ã�—ã�„ã‚¢ãƒ—ãƒªã‚’ä½œæˆ�ã�™ã‚‹ã�“ã�¨ã�¯ã�§ã��ã�¾ã�›ã‚“ã€‚ã�“ã�®ãƒªã‚¯ã‚¨ã‚¹ãƒˆã�¯ MadelineProto ã‚’å®Ÿè¡Œã�—ã�¦ã�„ã‚‹ãƒžã‚·ãƒ³ã�® IP ã�‹ã‚‰é€�ä¿¡ã�•ã‚Œã�¾ã�—ã�Ÿã€‚å®¶åº­ç”¨å›žç·š (ãƒ¢ãƒ�ã‚¤ãƒ«ãƒ‡ãƒ¼ã‚¿é€šä¿¡ä¸­ã�®ã‚¹ãƒžãƒ¼ãƒˆãƒ•ã‚©ãƒ³ã�ªã�©) ã�‹ã‚‰ https://my.telegram.org ã�§æ‰‹å‹•ã�§ã‚¢ãƒ—ãƒªã‚’ä½œæˆ�ã�—ã€�API ID ã�¨ hash ã‚’å…¥åŠ›ã�—ã�¦ã��ã� ã�•ã�„ã€‚',
             'apiCreateWeb' => 'ã‚¢ãƒ—ãƒªã‚’ä½œæˆ�',
             'apiError' => 'ã‚¨ãƒ©ãƒ¼: %sã€‚ã‚‚ã�†ä¸€åº¦ã�Šè©¦ã�—ã��ã� ã�•ã�„ã€‚',
             'apiInvalidWeb' => 'API ID ã�¯æ•°å­—ã�§ã€�API hash ã�¯ 32 æ–‡å­—ã�§ã�™ã€‚my.telegram.org ã�‹ã‚‰ã‚‚ã�†ä¸€åº¦ã‚³ãƒ”ãƒ¼ã�—ã�¦ã��ã� ã�•ã�„ã€‚',
             'apiManualInstructions0' => 'https://my.telegram.org ã�«ãƒ­ã‚°ã‚¤ãƒ³ã�—ã�¾ã�™',
             'apiManualInstructions1' => 'API development tools ã�«ç§»å‹•ã�—ã�¾ã�™',
             'apiManualInstructions2' => 'create application ã‚’ã‚¯ãƒªãƒƒã‚¯ã�—ã�¾ã�™',
             'apiManualPrompt0' => 'API ID ã‚’å…¥åŠ›ã�—ã�¦ã��ã� ã�•ã�„: ',
             'apiManualPrompt1' => 'API hash ã‚’å…¥åŠ›ã�—ã�¦ã��ã� ã�•ã�„: ',
             'apiManualWeb' => 'API ID ã�¨ API hash ã‚’å…¥åŠ›ã�—ã�¦ã��ã� ã�•ã�„',
             'apiParamsError' => 'å¿…é ˆãƒ‘ãƒ©ãƒ¡ãƒ¼ã‚¿ãƒ¼ã�Œã�™ã�¹ã�¦æŒ‡å®šã�•ã‚Œã�¦ã�„ã�¾ã�›ã‚“ï¼�',
             'apiSaveWeb' => 'ä¿�å­˜ã�—ã�¦ç¶šè¡Œ',
             'apiSendCodeWeb' => 'ã‚³ãƒ¼ãƒ‰ã‚’é€�ä¿¡',
             'apiSetupLeadWeb' => 'ã�™ã�¹ã�¦ã�® Telegram ã‚¢ãƒ—ãƒªã�«ã�¯ API ID ã�¨ hash ã�Œå¿…è¦�ã�§ã�™ã€‚æ—¢å­˜ã�® Telegram ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã�®é›»è©±ç•ªå�·ã‚’ä½¿ã�£ã�¦ã€�MadelineProto ã�Œ my.telegram.org ã�‹ã‚‰å�–å¾—ã�§ã��ã�¾ã�™ã€‚',
             'apiSetupTitleWeb' => 'API ID ã�¨ hash ã‚’å�–å¾—',
             'apiStartOverWeb' => 'æœ€åˆ�ã�‹ã‚‰ã‚„ã‚Šç›´ã�™',
             'api_not_set' => 'API ã‚­ãƒ¼ã�¨ API ID ã‚’æŒ‡å®šã�™ã‚‹å¿…è¦�ã�Œã�‚ã‚Šã�¾ã�™ã€‚my.telegram.org ã�§å�–å¾—ã�—ã�¦ã��ã� ã�•ã�„',
             'array_invalid' => 'æœ‰åŠ¹ã�ªé…�åˆ—ã�ŒæŒ‡å®šã�•ã‚Œã�¦ã�„ã�¾ã�›ã‚“',
             'baseDirLimitation' => 'basedir åˆ¶é™�ã�Œè¨­å®šã�•ã‚Œã�¦ã�„ã�¾ã�™ã€‚ãƒ‘ãƒ•ã‚©ãƒ¼ãƒžãƒ³ã‚¹ã�«å½±éŸ¿ã�—ã€�å•�é¡Œã�®åŽŸå› ã�«ã�ªã‚‹å�¯èƒ½æ€§ã�Œã�‚ã‚‹ã�Ÿã‚�ã€�å�¯èƒ½ã�§ã�‚ã‚Œã�°ç„¡åŠ¹ã�«ã�—ã�¦ã��ã� ã�•ã�„ã€‚',
             'bool_error' => 'ãƒ–ãƒ¼ãƒ«å€¤ã‚’æŠ½å‡ºã�§ã��ã�¾ã�›ã‚“ã�§ã�—ã�Ÿ',
             'botAlreadyRunning' => 'ãƒœãƒƒãƒˆã�¯ã�™ã�§ã�«å®Ÿè¡Œä¸­ã�§ã�™ï¼�',
             'botapi_conversion_error' => '%s ã‚’ Bot API ã‚ªãƒ–ã‚¸ã‚§ã‚¯ãƒˆã�«å¤‰æ�›ã�§ã��ã�¾ã�›ã‚“',
             'call_already_accepted' => 'é€šè©± %s ã�¯ã�™ã�§ã�«å¿œç­”æ¸ˆã�¿ã�§ã�™',
             'call_already_declined' => 'é€šè©± %s ã�¯ã�™ã�§ã�«æ‹’å�¦ã�•ã‚Œã�¦ã�„ã�¾ã�™',
             'call_completing' => '%s ã�‹ã‚‰ã�®é€šè©±ã‚’å®Œäº†ã�—ã�¦ã�„ã�¾ã�™...',
             'call_confirming' => '%s ã�‹ã‚‰ã�®é€šè©±ã‚’ç¢ºèª�ã�—ã�¦ã�„ã�¾ã�™...',
             'call_discarding' => 'é€šè©± %s ã‚’ç ´æ£„ã�—ã�¦ã�„ã�¾ã�™...',
             'call_error_1' => 'é€šè©± %s ã�Œè¦‹ã�¤ã�‹ã‚‰ã�šã€�å¿œç­”ã�§ã��ã�¾ã�›ã‚“ã�§ã�—ã�Ÿ',
             'call_error_2' => 'é€šè©± %s ã�Œè¦‹ã�¤ã�‹ã‚‰ã�šã€�ç¢ºèª�ã�§ã��ã�¾ã�›ã‚“ã�§ã�—ã�Ÿ',
             'call_error_3' => 'é€šè©± %s ã�Œè¦‹ã�¤ã�‹ã‚‰ã�šã€�å®Œäº†ã�§ã��ã�¾ã�›ã‚“ã�§ã�—ã�Ÿ',
             'cli_need_dl.php_link' => 'CLI ã�‹ã‚‰ getDownloadLink ã‚’ä½¿ç”¨ã�™ã‚‹å ´å�ˆã�¯ã€�ãƒ€ã‚¦ãƒ³ãƒ­ãƒ¼ãƒ‰ã‚¹ã‚¯ãƒªãƒ—ãƒˆã�® URL ã‚’æŒ‡å®šã�—ã�¦ã��ã� ã�•ã�„ï¼�',
             'constructor_not_found' => 'æ¬¡ã�®åž‹ã�®ã‚³ãƒ³ã‚¹ãƒˆãƒ©ã‚¯ã‚¿ãƒ¼ã�Œè¦‹ã�¤ã�‹ã‚Šã�¾ã�›ã‚“: ',
             'could_not_connect_to_MadelineProto' => 'MadelineProto ã�«æŽ¥ç¶šã�§ã��ã�¾ã�›ã‚“ã�§ã�—ã�Ÿã€‚ä¿®æ­£ã�™ã‚‹ã�«ã�¯ã€�proc_open ã‚’æœ‰åŠ¹ã�«ã�—ã�¦ open_basedir ã�®åˆ¶é™�ã‚’è§£é™¤ã�™ã‚‹ã�‹ã€�Web ã‚µãƒ¼ãƒ�ãƒ¼ã�®ãƒ‘ã‚¹æ›¸ã��æ�›ã�ˆã‚’ç„¡åŠ¹ã�«ã�—ã�¦ã��ã� ã�•ã�„ï¼�ã�™ã�§ã�«å¯¾å¿œæ¸ˆã�¿ã�®å ´å�ˆã�¯ã€�CLI ç‰ˆã�® PHP ã�Œ Web ç‰ˆã�¨ã�¾ã�£ã�Ÿã��å�Œã�˜ (ãƒ�ãƒ¼ã‚¸ãƒ§ãƒ³ã€�æ‹¡å¼µæ©Ÿèƒ½ã�ªã�©) ã�§ã�‚ã‚‹ã�“ã�¨ã‚’ç¢ºèª�ã�—ã€�IPC ã‚µãƒ¼ãƒ�ãƒ¼ã�®èµ·å‹•ã‚’å¦¨ã�’ã�Ÿã‚¨ãƒ©ãƒ¼ã�®è©³ç´°ã�«ã�¤ã�„ã�¦ MadelineProto.log ãƒ•ã‚¡ã‚¤ãƒ«ã‚’ç¢ºèª�ã�—ã�¦ã��ã� ã�•ã�„ã€‚',
             'could_not_convert_object' => 'åž‹ %s ã�®ã‚ªãƒ–ã‚¸ã‚§ã‚¯ãƒˆã‚’å¤‰æ�›ã�§ã��ã�¾ã�›ã‚“ã�§ã�—ã�Ÿ',
             'deserialization_error' => 'ãƒ‡ã‚·ãƒªã‚¢ãƒ©ã‚¤ã‚ºä¸­ã�«ã‚¨ãƒ©ãƒ¼ã�Œç™ºç”Ÿã�—ã�¾ã�—ã�Ÿ',
             'dl.php_check_logs_make_sure_session_running' => 'é–¢é€£ä»˜ã�‘ã‚‰ã‚Œã�Ÿ MadelineProto EventHandler ãƒœãƒƒãƒˆã�‹ MadelineProto IPC ã‚µãƒ¼ãƒ�ãƒ¼ã�®ã�„ã�šã‚Œã�‹ã�Œã‚ªãƒ•ãƒ©ã‚¤ãƒ³ã�§ã�™ã€‚ãƒ­ã‚°ã‚’ç¢ºèª�ã�—ã€�å°‘ã�ªã��ã�¨ã‚‚ä¸€æ–¹ã�Œå®Ÿè¡Œä¸­ã�§ã�‚ã‚‹ã�“ã�¨ã‚’ç¢ºèª�ã�—ã�¦ã��ã� ã�•ã�„ï¼�',
             'dl.php_powered_by_madelineproto' => 'Telegram ãƒ•ã‚¡ã‚¤ãƒ«ãƒ€ã‚¦ãƒ³ãƒ­ãƒ¼ãƒ‰ã‚µãƒ¼ãƒ�ãƒ¼ (æœ€å¤§ 4GB)ã€�powered by <a href="https://docs.madelineproto.xyz" target="_blank">MadelineProto</a>ï¼�<br>ç‹¬è‡ªã�® Telegram ãƒ•ã‚¡ã‚¤ãƒ«ãƒ€ã‚¦ãƒ³ãƒ­ãƒ¼ãƒ‰ã‚µãƒ¼ãƒ�ãƒ¼ã‚’æ§‹ç¯‰ã�™ã‚‹æ–¹æ³•ã�®è©³ç´°ã�¯<a href="https://docs.madelineproto.xyz/docs/FILES.html#getting-a-download-link" target="_blank">ã�“ã�¡ã‚‰</a>ã‚’ã‚¯ãƒªãƒƒã‚¯ã�—ã�¦ã��ã� ã�•ã�„ï¼�',
             'do_not_delete_MadelineProto.log' => 'MadelineProto.log ãƒ•ã‚¡ã‚¤ãƒ«ã�¯çµ¶å¯¾ã�«å‰Šé™¤ã�—ã�ªã�„ã�§ã��ã� ã�•ã�„ã€‚ä»£ã‚�ã‚Šã�«ã€�è¨­å®šã�§ã‚«ã‚¹ã‚¿ãƒ ã�®æœ€å¤§ã‚µã‚¤ã‚ºã‚’æŒ‡å®šã�—ã�¦ã��ã� ã�•ã�„ï¼�',
             'do_not_remove_MadelineProto.log_phar' => 'madeline.phar ã‚„ madeline.php ã‚’å‰Šé™¤ã�—ã�ªã�„ã�§ã��ã� ã�•ã�„ã€‚å‰Šé™¤ã�™ã‚‹ã�¨ MadelineProto ã�Œã‚¯ãƒ©ãƒƒã‚·ãƒ¥ã�—ã�¾ã�™ã€‚MadelineProto ã�§å•�é¡Œã�Œç™ºç”Ÿã�—ã�Ÿå ´å�ˆã�¯ã€�https://github.com/danog/MadelineProto ã�¾ã�Ÿã�¯ https://t.me/pwrtelegramgroup ã�«å ±å‘Šã�—ã�¦ã��ã� ã�•ã�„',
             'do_not_use_blocking_class' => 'ãƒ‘ãƒ•ã‚©ãƒ¼ãƒžãƒ³ã‚¹ä¸Šã�®ç�†ç”±ã�‹ã‚‰ã€�ãƒ�ãƒ³ãƒ‰ãƒ©ãƒ¼ã�§ã�¯é�žå�ŒæœŸã�§ã�ªã�„ãƒ–ãƒ­ãƒƒã‚­ãƒ³ã‚°ã‚¯ãƒ©ã‚¹ %s ã‚’ä½¿ç”¨ã�§ã��ã�¾ã�›ã‚“ã€‚ä»£ã‚�ã‚Šã�« %s ã‚’ä½¿ç”¨ã�—ã�¦ã��ã� ã�•ã�„',
             'do_not_use_blocking_function' => 'ãƒ‘ãƒ•ã‚©ãƒ¼ãƒžãƒ³ã‚¹ä¸Šã�®ç�†ç”±ã�‹ã‚‰ã€�ã‚¤ãƒ™ãƒ³ãƒˆãƒ�ãƒ³ãƒ‰ãƒ©ãƒ¼ã�§ã�¯é�žå�ŒæœŸã�§ã�ªã�„ãƒ–ãƒ­ãƒƒã‚­ãƒ³ã‚°é–¢æ•° %s ã‚’ä½¿ç”¨ã�§ã��ã�¾ã�›ã‚“ã€‚ä»£ã‚�ã‚Šã�« %s ã‚’ä½¿ç”¨ã�—ã�¦ã��ã� ã�•ã�„',
             'do_not_use_deprecated_function' => '%s é–¢æ•°ã�¯é�žæŽ¨å¥¨ã�§ã�™ã€‚ä»£ã‚�ã‚Šã�« %s ã‚’ä½¿ç”¨ã�—ã�¦ã��ã� ã�•ã�„',
             'do_not_use_non_root_require_in_event_handler' => 'ãƒ‘ãƒ•ã‚©ãƒ¼ãƒžãƒ³ã‚¹ä¸Šã�®ç�†ç”±ã�‹ã‚‰ã€�ã‚¤ãƒ™ãƒ³ãƒˆãƒ�ãƒ³ãƒ‰ãƒ©ãƒ¼ã‚¯ãƒ©ã‚¹ã�®å†…éƒ¨ã�§ require ã‚„ include ã‚’ä½¿ç”¨ã�—ã�¦ã�¯ã�„ã�‘ã�¾ã�›ã‚“ã€‚ãƒ«ãƒ¼ãƒˆãƒ¬ãƒ™ãƒ«ã�® require ã�®ã�¿ä½¿ç”¨ã�§ã��ã�¾ã�™ã€‚',
             'do_not_use_yield' => 'MadelineProto 8 ã�§ã�¯é�žå�ŒæœŸé–¢æ•°ã�§ã�® yield ã�®ä½¿ç”¨ã�¯ä¸�è¦�ã�§ã€�ã‚µãƒ�ãƒ¼ãƒˆã‚‚ã�•ã‚Œã�¦ã�„ã�¾ã�›ã‚“ã€‚é�žå�ŒæœŸé–¢æ•°ã�®å‘¼ã�³å‡ºã�—ã�«ä½¿ç”¨ã�—ã�¦ã�„ã�Ÿ yield ã‚­ãƒ¼ãƒ¯ãƒ¼ãƒ‰ã‚’ã�™ã�¹ã�¦å‰Šé™¤ã�—ã�¦ã��ã� ã�•ã�„',
             'done' => 'å®Œäº†ã�—ã�¾ã�—ã�Ÿï¼�',
             'encode_double_error' => 'double ã‚’æ­£ã�—ã��ã‚¨ãƒ³ã‚³ãƒ¼ãƒ‰ã�§ã��ã�¾ã�›ã‚“ã�§ã�—ã�Ÿ',
             'extensionRecommended' => 'è­¦å‘Š: %s æ‹¡å¼µæ©Ÿèƒ½ã�Œã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã�•ã‚Œã�¦ã�„ã�¾ã�›ã‚“ã€‚MadelineProto ã‚’é«˜é€ŸåŒ–ã�™ã‚‹ã�Ÿã‚�ã�«ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã�—ã�¦ã��ã� ã�•ã�„ï¼�',
             'extensionRequired' => 'MadelineProto ã�®å®Ÿè¡Œã�«ã�¯ %s æ‹¡å¼µæ©Ÿèƒ½ã�Œå¿…è¦�ã�§ã�™ã€‚%s',
             'extensionRequiredInstallWithApt' => 'sudo apt-get install %s ã‚’å®Ÿè¡Œã�—ã�¦ã�¿ã�¦ã��ã� ã�•ã�„ã€‚',
             'extensionRequiredInstallWithCustomInstructions' => '%s ã�®æ‰‹é †ã�«å¾“ã�£ã�¦ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã�—ã�¦ã��ã� ã�•ã�„ã€‚',
             'file_not_exist' => 'ãƒ•ã‚¡ã‚¤ãƒ«ã�Œå­˜åœ¨ã�—ã�¾ã�›ã‚“',
             'file_parsing' => '%s ã‚’è§£æž�ã�—ã�¦ã�„ã�¾ã�™...',
             'file_type_invalid' => 'ç„¡åŠ¹ã�ªãƒ•ã‚¡ã‚¤ãƒ«å½¢å¼�ã�Œæ¤œå‡ºã�•ã‚Œã�¾ã�—ã�Ÿ (%s)',
             'fingerprint_invalid' => 'ã‚­ãƒ¼ã�®ãƒ•ã‚£ãƒ³ã‚¬ãƒ¼ãƒ—ãƒªãƒ³ãƒˆã�Œç„¡åŠ¹ã�§ã�™ï¼�',
             'go' => 'å®Ÿè¡Œ',
             'invalid_dl.php_session' => '%s ã�¯ã‚»ãƒƒã‚·ãƒ§ãƒ³ ID ã�Œç•°ã�ªã‚‹ã�Ÿã‚�ã€�æœ‰åŠ¹ã�ªãƒ€ã‚¦ãƒ³ãƒ­ãƒ¼ãƒ‰ã‚¹ã‚¯ãƒªãƒ—ãƒˆã�§ã�¯ã�‚ã‚Šã�¾ã�›ã‚“ (æƒ³å®š: %sã€�å®Ÿéš›: %s)',
             'ipc_tcp_warning' => 'UNIX sockets are not available, so the IPC server of the session listens on TCP (127.0.0.1) instead: any program running on this machine can connect to it, and control the session. On Windows 10 1803+, enable the sockets extension (extension=sockets in php.ini) to use UNIX sockets.',
             'length_too_big' => 'é•·ã�•ã�Œå¤§ã��ã�™ã�Žã�¾ã�™',
             'loginBot' => 'ãƒœãƒƒãƒˆãƒˆãƒ¼ã‚¯ãƒ³ã‚’å…¥åŠ›ã�—ã�¦ã��ã� ã�•ã�„: ',
             'loginBotTokenHintWeb' => 'ãƒœãƒƒãƒˆã�®ä½œæˆ�æ™‚ã�« @BotFather ã�‹ã‚‰å�—ã�‘å�–ã�£ã�Ÿãƒˆãƒ¼ã‚¯ãƒ³ã�§ã�™ã€‚',
             'loginBotTokenWeb' => 'ãƒœãƒƒãƒˆãƒˆãƒ¼ã‚¯ãƒ³',
             'loginChoosePromptWeb' => 'ãƒ¦ãƒ¼ã‚¶ãƒ¼ã�¨ãƒœãƒƒãƒˆã�®ã�©ã�¡ã‚‰ã�§ãƒ­ã‚°ã‚¤ãƒ³ã�—ã�¾ã�™ã�‹ï¼Ÿ',
             'loginCodeLeadWeb' => 'Telegram ã�Œãƒ­ã‚°ã‚¤ãƒ³ã‚³ãƒ¼ãƒ‰ã‚’ Telegram ã‚¢ãƒ—ãƒªã�«é€�ä¿¡ã�—ã�¾ã�—ã�Ÿã€‚ä»–ã�®ç«¯æœ«ã�§ãƒ­ã‚°ã‚¤ãƒ³ã�—ã�¦ã�„ã�ªã�„å ´å�ˆã�¯ SMS ã�§é€�ä¿¡ã�•ã‚Œã�¾ã�™ã€‚',
             'loginCodeTitleWeb' => 'ãƒ­ã‚°ã‚¤ãƒ³ã‚³ãƒ¼ãƒ‰ã‚’å…¥åŠ›',
             'loginLeadWeb' => 'ã�“ã�® MadelineProto ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã�§ä½¿ç”¨ã�™ã‚‹ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã�¾ã�Ÿã�¯ãƒœãƒƒãƒˆã‚’é�¸æŠžã�—ã�¦ã��ã� ã�•ã�„ã€‚',
             'loginManual' => 'ã�¾ã�Ÿã�¯ã€�ãƒœãƒƒãƒˆãƒˆãƒ¼ã‚¯ãƒ³ã�‹é›»è©±ç•ªå�·ã‚’å…¥åŠ›ã�—ã�¦æ‰‹å‹•ã�§ãƒ­ã‚°ã‚¤ãƒ³ã�™ã‚‹ã�“ã�¨ã‚‚ã�§ã��ã�¾ã�™: ',
             'loginNoCode' => 'é›»è©±èª�è¨¼ã‚³ãƒ¼ãƒ‰ã�Œå…¥åŠ›ã�•ã‚Œã�¦ã�„ã�¾ã�›ã‚“ï¼�',
             'loginNoName' => 'å��ã�Œå…¥åŠ›ã�•ã‚Œã�¦ã�„ã�¾ã�›ã‚“ï¼�',
             'loginNoPass' => 'ãƒ‘ã‚¹ãƒ¯ãƒ¼ãƒ‰ã�Œå…¥åŠ›ã�•ã‚Œã�¦ã�„ã�¾ã�›ã‚“ï¼�',
             'loginOptionBot' => 'ãƒœãƒƒãƒˆ',
             'loginOptionUser' => 'ãƒ¦ãƒ¼ã‚¶ãƒ¼',
             'loginPasswordLabelWeb' => 'ãƒ‘ã‚¹ãƒ¯ãƒ¼ãƒ‰',
             'loginPasswordLeadWeb' => 'ã�“ã�®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã�¯ã€�[è¨­å®š] > [ãƒ—ãƒ©ã‚¤ãƒ�ã‚·ãƒ¼ã�¨ã‚»ã‚­ãƒ¥ãƒªãƒ†ã‚£] > [2 æ®µéšŽèª�è¨¼] ã�§è¨­å®šã�•ã‚Œã�Ÿãƒ‘ã‚¹ãƒ¯ãƒ¼ãƒ‰ã�§ä¿�è­·ã�•ã‚Œã�¦ã�„ã�¾ã�™ã€‚',
             'loginPasswordTitleWeb' => '2 æ®µéšŽèª�è¨¼',
             'loginQr' => 'ã‚¹ãƒžãƒ¼ãƒˆãƒ•ã‚©ãƒ³ã�§ Telegram ã‚’é–‹ã��ã€�[è¨­å®š] > [ãƒ‡ãƒ�ã‚¤ã‚¹] > [ãƒ‡ã‚¹ã‚¯ãƒˆãƒƒãƒ—ç«¯æœ«ã‚’ãƒªãƒ³ã‚¯] ã�«é€²ã‚“ã�§ä¸Šã�® QR ã‚³ãƒ¼ãƒ‰ã‚’ã‚¹ã‚­ãƒ£ãƒ³ã�™ã‚‹ã�¨ã€�è‡ªå‹•ã�§ãƒ­ã‚°ã‚¤ãƒ³ã�§ã��ã�¾ã�™ã€‚',
             'loginQrCodeExpired' => 'QR ã‚³ãƒ¼ãƒ‰ã�®æœ‰åŠ¹æœŸé™�ã�Œåˆ‡ã‚Œã�¾ã�—ã�Ÿã€‚æ–°ã�—ã�„ã‚³ãƒ¼ãƒ‰ã‚’ç”Ÿæˆ�ã�—ã�¦ã�„ã�¾ã�™...',
             'loginQrCodeSuccessful' => 'QR ã‚³ãƒ¼ãƒ‰ã�§ã�®ãƒ­ã‚°ã‚¤ãƒ³ã�«æˆ�åŠŸã�—ã�¾ã�—ã�Ÿï¼�',
             'loginTitleWeb' => 'Telegram ã�«ãƒ­ã‚°ã‚¤ãƒ³',
             'loginTypeWeb' => 'ãƒ­ã‚°ã‚¤ãƒ³ã�®ç¨®é¡ž',
             'loginUser' => 'é›»è©±ç•ªå�·ã‚’å…¥åŠ›ã�—ã�¦ã��ã� ã�•ã�„: ',
             'loginUserCode' => 'ã‚³ãƒ¼ãƒ‰ã‚’å…¥åŠ›ã�—ã�¦ã��ã� ã�•ã�„: ',
             'loginUserPass' => 'ãƒ‘ã‚¹ãƒ¯ãƒ¼ãƒ‰ã‚’å…¥åŠ›ã�—ã�¦ã��ã� ã�•ã�„ (ãƒ’ãƒ³ãƒˆ: %s): ',
             'loginUserPassHint' => 'ãƒ’ãƒ³ãƒˆ: %s',
             'loginUserPassWeb' => 'ãƒ‘ã‚¹ãƒ¯ãƒ¼ãƒ‰ã‚’å…¥åŠ›ã�—ã�¦ã��ã� ã�•ã�„: ',
             'loginUserPhoneCodeWeb' => 'ã‚³ãƒ¼ãƒ‰',
             'loginUserPhoneHintWeb' => 'å›½ç•ªå�·ã‚’å�«ã‚�ã�¦å…¥åŠ›ã�—ã�¦ã��ã� ã�•ã�„ (ä¾‹: +39 123 456 7890)ã€‚',
             'loginUserPhoneWeb' => 'é›»è©±ç•ªå�·',
             'loginWebQr' => 'ã‚¹ãƒžãƒ¼ãƒˆãƒ•ã‚©ãƒ³ã�§ Telegram ã‚’é–‹ã��ã€�[è¨­å®š] > [ãƒ‡ãƒ�ã‚¤ã‚¹] > [ãƒ‡ã‚¹ã‚¯ãƒˆãƒƒãƒ—ç«¯æœ«ã‚’ãƒªãƒ³ã‚¯] ã�«é€²ã‚“ã�§æ¬¡ã�® QR ã‚³ãƒ¼ãƒ‰ã‚’ã‚¹ã‚­ãƒ£ãƒ³ã�™ã‚‹ã�¨ã€�è‡ªå‹•ã�§ãƒ­ã‚°ã‚¤ãƒ³ã�™ã‚‹ã�“ã�¨ã‚‚ã�§ã��ã�¾ã�™:',
             'loginWebQr1' => 'ã‚¹ãƒžãƒ¼ãƒˆãƒ•ã‚©ãƒ³ã�§ Telegram ã‚’é–‹ã��ã�¾ã�™',
             'loginWebQr2' => '[è¨­å®š] > [ãƒ‡ãƒ�ã‚¤ã‚¹] > [ãƒ‡ã‚¹ã‚¯ãƒˆãƒƒãƒ—ç«¯æœ«ã‚’ãƒªãƒ³ã‚¯] ã�«é€²ã�¿ã�¾ã�™',
             'loginWebQr3' => 'ã‚¹ãƒžãƒ¼ãƒˆãƒ•ã‚©ãƒ³ã‚’ã�“ã�®ç”»é�¢ã�«å�‘ã�‘ã�¦ãƒ­ã‚°ã‚¤ãƒ³ã‚’ç¢ºèª�ã�—ã�¾ã�™',
             'login_2fa_enabled' => '2 æ®µéšŽèª�è¨¼ã�Œæœ‰åŠ¹ã�«ã�ªã�£ã�¦ã�„ã�¾ã�™ã€‚complete2falogin é–¢æ•°ã‚’å‘¼ã�³å‡ºã�™å¿…è¦�ã�Œã�‚ã‚Šã�¾ã�™...',
             'login_auth_key' => 'èª�è¨¼ã‚­ãƒ¼ã‚’ä½¿ç”¨ã�—ã�¦ãƒ­ã‚°ã‚¤ãƒ³ã�—ã�¦ã�„ã�¾ã�™...',
             'login_bot' => 'ãƒœãƒƒãƒˆã�¨ã�—ã�¦ãƒ­ã‚°ã‚¤ãƒ³ã�—ã�¦ã�„ã�¾ã�™...',
             'login_code_sending' => 'ã‚³ãƒ¼ãƒ‰ã‚’é€�ä¿¡ã�—ã�¦ã�„ã�¾ã�™...',
             'login_code_sent' => 'ã‚³ãƒ¼ãƒ‰ã‚’é€�ä¿¡ã�—ã�¾ã�—ã�Ÿï¼�ã‚³ãƒ¼ãƒ‰ã‚’å�—ã�‘å�–ã�£ã�Ÿã‚‰ completePhoneLogin é–¢æ•°ã‚’ä½¿ç”¨ã�—ã�¦ã��ã� ã�•ã�„ã€‚',
             'login_code_uncalled' => 'ã‚³ãƒ¼ãƒ‰ã‚’å¾…æ©Ÿã�—ã�¦ã�„ã�¾ã�›ã‚“ï¼�å…ˆã�« phoneLogin ãƒ¡ã‚½ãƒƒãƒ‰ã‚’å‘¼ã�³å‡ºã�—ã�¦ã��ã� ã�•ã�„',
             'login_need_signup' => 'ã�“ã�®ç•ªå�·ã�®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã�¯ã�¾ã� ä½œæˆ�ã�•ã‚Œã�¦ã�„ã�¾ã�›ã‚“ã€‚completeSignup é–¢æ•°ã‚’å‘¼ã�³å‡ºã�™å¿…è¦�ã�Œã�‚ã‚Šã�¾ã�™...',
             'login_ok' => 'ãƒ­ã‚°ã‚¤ãƒ³ã�—ã�¾ã�—ã�Ÿï¼�',
             'login_user' => 'é€šå¸¸ã�®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã�¨ã�—ã�¦ãƒ­ã‚°ã‚¤ãƒ³ã�—ã�¦ã�„ã�¾ã�™...',
             'logout_ok' => 'ãƒ­ã‚°ã‚¢ã‚¦ãƒˆã�—ã�¾ã�—ã�Ÿï¼�',
             'long_not_16' => 'æŒ‡å®šã�•ã‚Œã�Ÿå€¤ã�®é•·ã�•ã�Œ 16 ãƒ�ã‚¤ãƒˆã�§ã�¯ã�‚ã‚Šã�¾ã�›ã‚“',
             'long_not_32' => 'æŒ‡å®šã�•ã‚Œã�Ÿå€¤ã�®é•·ã�•ã�Œ 32 ãƒ�ã‚¤ãƒˆã�§ã�¯ã�‚ã‚Šã�¾ã�›ã‚“',
             'long_not_64' => 'æŒ‡å®šã�•ã‚Œã�Ÿå€¤ã�®é•·ã�•ã�Œ 64 ãƒ�ã‚¤ãƒˆã�§ã�¯ã�‚ã‚Šã�¾ã�›ã‚“',
             'madelineproto_ready' => 'MadelineProto ã�®æº–å‚™ã�Œã�§ã��ã�¾ã�—ã�Ÿï¼�',
             'manualAdminActionRequired' => '!!!!!!!!! ã‚·ã‚¹ãƒ†ãƒ ç®¡ç�†è€…ã�«ã‚ˆã‚‹æ‰‹å‹•å¯¾å¿œã�Œå¿…è¦�ã�§ã�™ !!!!!!!!!',
             'method_not_found' => 'ãƒ¡ã‚½ãƒƒãƒ‰ã�Œè¦‹ã�¤ã�‹ã‚Šã�¾ã�›ã‚“ã�§ã�—ã�Ÿ: ',
             'mmapErrorPart1' => 'ãƒ¡ãƒ¢ãƒªãƒžãƒƒãƒ— (mmap) é ˜åŸŸã�®æœ€å¤§æ•°ã�«é�”ã�—ã�¾ã�—ã�Ÿ (%s)ã€‚ä¿®æ­£ã�™ã‚‹ã�«ã�¯ã€�ã‚«ãƒ¼ãƒ�ãƒ«è¨­å®š vm.max_map_count ã‚’ 262144 ã�«å¢—ã‚„ã�—ã�¦ã��ã� ã�•ã�„ã€‚',
             'mmapErrorPart2' => 'ä¿®æ­£ã�™ã‚‹ã�«ã�¯ã€�root ã�¨ã�—ã�¦æ¬¡ã�®ã‚³ãƒžãƒ³ãƒ‰ã‚’å®Ÿè¡Œã�—ã�¦ã��ã� ã�•ã�„: %s',
             'mmapErrorPart3' => 'å†�èµ·å‹•å¾Œã‚‚å¤‰æ›´ã‚’ç¶­æŒ�ã�™ã‚‹ã�«ã�¯: %s',
             'mmapErrorPart4' => 'Windows ã�Šã‚ˆã�³ WSL ã�§ã�¯ã€�ãƒšãƒ¼ã‚¸ãƒ•ã‚¡ã‚¤ãƒ«ã�®ã‚µã‚¤ã‚ºã‚’å¢—ã‚„ã�™ã�¨æ”¹å–„ã�™ã‚‹å ´å�ˆã�Œã�‚ã‚Šã�¾ã�™ã€‚å•�é¡Œã�Œè§£æ±ºã�—ã�ªã�„å ´å�ˆã�¯ã€�ãƒ�ã‚¤ãƒ†ã‚£ãƒ–ã�® Linux ã�«åˆ‡ã‚Šæ›¿ã�ˆã�¦ã��ã� ã�•ã�„ã€‚',
             'must_have_declare_types' => 'ãƒ‘ãƒ•ã‚©ãƒ¼ãƒžãƒ³ã‚¹ä¸Šã�®ç�†ç”±ã�‹ã‚‰ã€�ã‚¤ãƒ™ãƒ³ãƒˆãƒ�ãƒ³ãƒ‰ãƒ©ãƒ¼ãƒ•ã‚¡ã‚¤ãƒ«ã�®æœ€åˆ�ã�®æ–‡ã�¯ "declare(strict_types=1);" ã�§ã�ªã�‘ã‚Œã�°ã�ªã‚Šã�¾ã�›ã‚“',
             'nearest_dc' => 'ç�¾åœ¨åœ°ã�¯ %sã€�æœ€å¯„ã‚Šã�® DC ã�¯ %d ã�§ã�™ã€‚',
             'need_dl.php' => 'ãƒ‡ãƒ•ã‚©ãƒ«ãƒˆã�®ãƒ€ã‚¦ãƒ³ãƒ­ãƒ¼ãƒ‰ã‚¹ã‚¯ãƒªãƒ—ãƒˆã‚’ç”Ÿæˆ�ã�§ã��ã�¾ã�›ã‚“ã�§ã�—ã�Ÿ (%s)ã€‚æ¬¡ã�®å†…å®¹ã�§ dl.php ãƒ•ã‚¡ã‚¤ãƒ«ã‚’ä½œæˆ�ã�—: %s ã��ã�® URL ã‚’ getDownloadLink ã�®ç¬¬ 2 å¼•æ•°ã�«æ¸¡ã�—ã�¦ã��ã� ã�•ã�„',
             'noReportPeers' => 'è­¦å‘Š: ãƒ¬ãƒ�ãƒ¼ãƒˆå…ˆã�®ãƒ”ã‚¢ã�Œè¨­å®šã�•ã‚Œã�¦ã�„ã�¾ã�›ã‚“ã€‚ã‚¤ãƒ™ãƒ³ãƒˆãƒ�ãƒ³ãƒ‰ãƒ©ãƒ¼ã�«æ¬¡ã�®ãƒ¡ã‚½ãƒƒãƒ‰ã‚’è¿½åŠ ã�—ã�¦ã��ã� ã�•ã�„',
             'non_text_conversion' => 'ãƒ†ã‚­ã‚¹ãƒˆä»¥å¤–ã�®ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã�¯ã�¾ã� å¤‰æ�›ã�§ã��ã�¾ã�›ã‚“ï¼�',
             'not_loggedIn' => 'ãƒ­ã‚°ã‚¤ãƒ³ã�—ã�¦ã�„ã�¾ã�›ã‚“ï¼�',
             'not_numeric' => 'æŒ‡å®šã�•ã‚Œã�Ÿå€¤ã�¯æ•°å€¤ã�§ã�¯ã�‚ã‚Šã�¾ã�›ã‚“',
             'params_missing' => 'å¿…é ˆãƒ‘ãƒ©ãƒ¡ãƒ¼ã‚¿ãƒ¼ã�Œã�‚ã‚Šã�¾ã�›ã‚“',
             'peer_not_in_db' => 'ã�“ã�®ãƒ”ã‚¢ã�¯å†…éƒ¨ã�®ãƒ”ã‚¢ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã�«å­˜åœ¨ã�—ã�¾ã�›ã‚“',
             'plugin_path_does_not_exist' => 'ãƒ—ãƒ©ã‚°ã‚¤ãƒ³ã�®ãƒ‘ã‚¹ %s ã�Œå­˜åœ¨ã�—ã�¾ã�›ã‚“ï¼�',
             'plugins_do_not_use_require' => 'ãƒ‘ãƒ•ã‚©ãƒ¼ãƒžãƒ³ã‚¹ä¸Šã�®ç�†ç”±ã�‹ã‚‰ã€�ãƒ—ãƒ©ã‚°ã‚¤ãƒ³ã�Œ plugins ãƒ•ã‚©ãƒ«ãƒ€ãƒ¼å†…ã�®ä»–ã�®ãƒ•ã‚¡ã‚¤ãƒ«ã‚’èª­ã�¿è¾¼ã‚�ã‚‹ã�®ã�¯ã€�PSR-4 ã‚ªãƒ¼ãƒˆãƒ­ãƒ¼ãƒ€ãƒ¼ã�«ã‚ˆã‚‹è‡ªå‹•çš„ã�ª include/require ã�®ã�¿ã�§ã�™ (æ‰‹å‹•ã�§ require() ã�™ã‚‹ã�“ã�¨ã�¯ã�§ã��ã�¾ã�›ã‚“)ã€‚',
             'plugins_must_have_exactly_one_class' => 'ãƒ—ãƒ©ã‚°ã‚¤ãƒ³ã�¯ã‚¯ãƒ©ã‚¹ã‚’ 1 ã�¤ã� ã�‘å®šç¾©ã�™ã‚‹å¿…è¦�ã�Œã�‚ã‚Šã�¾ã�™ï¼�è¤‡æ•°ã�®ã‚¯ãƒ©ã‚¹ã€�ã‚¤ãƒ³ã‚¿ãƒ¼ãƒ•ã‚§ã‚¤ã‚¹ã€�ãƒˆãƒ¬ã‚¤ãƒˆã‚’å®šç¾©ã�™ã‚‹ã�«ã�¯åˆ¥ã€…ã�®ãƒ•ã‚¡ã‚¤ãƒ«ã‚’ä½œæˆ�ã�—ã�¦ã��ã� ã�•ã�„ã€‚MadelineProto ã�Œè‡ªå‹•çš„ã�«ã‚ªãƒ¼ãƒˆãƒ­ãƒ¼ãƒ‰ã�—ã�¾ã�™ã€‚',
             'predicate_not_set' => 'è¿°èªž (_ ã�®å€¤) ã�Œè¨­å®šã�•ã‚Œã�¦ã�„ã�¾ã�›ã‚“ï¼�',
             'recommend_not_use_filesystem_function' => '%s é–¢æ•°ã�®ä½¿ç”¨ã�¯æŽ¨å¥¨ã�•ã‚Œã�¾ã�›ã‚“ã€‚ã‚¢ãƒƒãƒ—ãƒ‡ãƒ¼ãƒˆã�®å‡¦ç�†ä¸­ã�«ãƒ•ã‚¡ã‚¤ãƒ«ã‚·ã‚¹ãƒ†ãƒ ã�«ã‚¢ã‚¯ã‚»ã‚¹ã�™ã‚‹ã�¨ãƒœãƒƒãƒˆã�Œé�…ã��ã�ªã‚‹ã�Ÿã‚�ã�§ã�™ã€‚ãƒœãƒƒãƒˆã‚’é�…ã��ã�—ã�ªã�„ãƒ‡ãƒ¼ã‚¿ã�®ä¿�å­˜æ–¹æ³•ã�«ã�¤ã�„ã�¦ã�¯ https://docs.madelineproto.xyz/docs/UPDATES.html#avoiding-the-use-of-filesystem-functions ã‚’å�‚ç…§ã�—ã�¦ã��ã� ã�•ã�„ï¼�',
             'rpc_tg_error' => 'Telegram ã�Œ RPC ã‚¨ãƒ©ãƒ¼ã‚’è¿”ã�—ã�¾ã�—ã�Ÿ: %s (%s)ã€�ç™ºç”Ÿç®‡æ‰€ %s:%s%sTL ãƒˆãƒ¬ãƒ¼ã‚¹:',
             'sec_peer_not_in_db' => 'ã�“ã�®ã‚·ãƒ¼ã‚¯ãƒ¬ãƒƒãƒˆãƒ”ã‚¢ã�¯å†…éƒ¨ã�®ãƒ”ã‚¢ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã�«å­˜åœ¨ã�—ã�¾ã�›ã‚“',
             'secret_chat_skipping' => 'ã‚·ãƒ¼ã‚¯ãƒ¬ãƒƒãƒˆãƒ�ãƒ£ãƒƒãƒˆ %s ã�Œãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã�«ã�ªã�„ã�Ÿã‚�ã€�ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã‚’ã‚¹ã‚­ãƒƒãƒ—ã�—ã�¾ã�™...',
             'serialization_ofd' => 'ã‚·ãƒªã‚¢ãƒ©ã‚¤ã‚ºå½¢å¼�ã�Œå�¤ã�„ã�Ÿã‚�ã€�ã‚ªãƒ–ã‚¸ã‚§ã‚¯ãƒˆã‚’å†�æ§‹ç¯‰ã�—ã�¦ã�„ã�¾ã�™ï¼�',
             'session_corrupted' => 'ã‚»ãƒƒã‚·ãƒ§ãƒ³ã�Œç ´æ��ã�—ã�¦ã�„ã�¾ã�™ï¼�',
             'signing_up' => 'é€šå¸¸ã�®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã�¨ã�—ã�¦ç™»éŒ²ã�—ã�¦ã�„ã�¾ã�™...',
             'signupFirstName' => 'å��ã‚’å…¥åŠ›ã�—ã�¦ã��ã� ã�•ã�„: ',
             'signupFirstNameWeb' => 'å��',
             'signupLastName' => 'å§“ã‚’å…¥åŠ›ã�—ã�¦ã��ã� ã�•ã�„ (ç©ºæ¬„å�¯): ',
             'signupLastNameWeb' => 'å§“',
             'signupLeadWeb' => 'ã�“ã�®é›»è©±ç•ªå�·ã�¯ã�¾ã�  Telegram ã�«ç™»éŒ²ã�•ã‚Œã�¦ã�„ã�¾ã�›ã‚“ã€‚æ–°ã�—ã�„ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã�®å��å‰�ã‚’å…¥åŠ›ã�—ã�¦ã��ã� ã�•ã�„ã€‚',
             'signupTitleWeb' => 'ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’ä½œæˆ�',
             'signupWeb' => 'ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’ç™»éŒ²ã�—ã�¦ã��ã� ã�•ã�„',
             'signup_ok' => 'ç™»éŒ²ã�—ã�¾ã�—ã�Ÿï¼�',
             'signup_uncalled' => 'ç™»éŒ²ã‚’å¾…æ©Ÿã�—ã�¦ã�„ã�¾ã�›ã‚“ï¼�å…ˆã�« phoneLogin ã�¨ completePhoneLogin ãƒ¡ã‚½ãƒƒãƒ‰ã‚’å‘¼ã�³å‡ºã�—ã�¦ã��ã� ã�•ã�„ï¼�',
             'src_file_invalid' => 'ç„¡åŠ¹ã�ªã‚½ãƒ¼ã‚¹ãƒ•ã‚¡ã‚¤ãƒ«ã�ŒæŒ‡å®šã�•ã‚Œã�¾ã�—ã�Ÿ: ',
             'static_analysis_minor' => '%s ã�®é�™çš„è§£æž�ä¸­ã�«è»½å¾®ã�ªå•�é¡Œã�Œè¦‹ã�¤ã�‹ã‚Šã�¾ã�—ã�Ÿ: %s',
             'static_analysis_severe' => '%s ã�®é�™çš„è§£æž�ä¸­ã�«é‡�å¤§ã�ªå•�é¡Œã�Œè¦‹ã�¤ã�‹ã‚Šã�¾ã�—ã�Ÿ: %s',
             'stream_handle_invalid' => 'ç„¡åŠ¹ã�ªã‚¹ãƒˆãƒªãƒ¼ãƒ ãƒ�ãƒ³ãƒ‰ãƒ«ã�ŒæŒ‡å®šã�•ã‚Œã�¾ã�—ã�Ÿã€‚',
             'string_required' => 'æ–‡å­—åˆ—ã�Œå¿…è¦�ã�§ã�™ï¼�',
             'translate_madelineproto_cli' => 'MadelineProto ã�¯ã�‚ã�ªã�Ÿã�®è¨€èªžã�«ç¿»è¨³ã�§ã��ã�¾ã�™ (ç�¾åœ¨ã�®ç¿»è¨³é€²æ�—: %d%%)ã€‚ç¿»è¨³ã�«å�”åŠ›ã�™ã‚‹ã�«ã�¯ https://github.com/danog/MadelineProto/tree/v8/langs ã�«ã‚¢ã‚¯ã‚»ã‚¹ã�—ã�¦ã��ã� ã�•ã�„ï¼�',
             'translate_madelineproto_web' => 'MadelineProto ã�¯ã�‚ã�ªã�Ÿã�®è¨€èªžã�«ç¿»è¨³ã�§ã��ã�¾ã�™ (ç�¾åœ¨ã�®ç¿»è¨³é€²æ�—: %d%%)ã€‚<a href="https://github.com/danog/MadelineProto/tree/v8/langs" target="_blank">ã�“ã�¡ã‚‰ã‚’ã‚¯ãƒªãƒƒã‚¯ã�—ã�¦ç¿»è¨³ã�«å�”åŠ›ã�—ã�¦ã��ã� ã�•ã�„ï¼�</a>',
             'tweb.ACCESS_TOKEN_EXPIRED' => 'ã�“ã�®ãƒœãƒƒãƒˆãƒˆãƒ¼ã‚¯ãƒ³ã�¯ç„¡åŠ¹åŒ–ã�•ã‚Œã�¦ã�„ã�¾ã�™',
             'tweb.ACCESS_TOKEN_INVALID' => 'ç„¡åŠ¹ã�ªãƒœãƒƒãƒˆãƒˆãƒ¼ã‚¯ãƒ³ã�§ã�™',
             'tweb.AccDescr.LoginCode' => 'ãƒ­ã‚°ã‚¤ãƒ³ã‚³ãƒ¼ãƒ‰',
             'tweb.Close' => 'é–‰ã�˜ã‚‹',
             'tweb.Code' => 'ã‚³ãƒ¼ãƒ‰',
             'tweb.Contacts.PhoneNumber.Placeholder' => 'é›»è©±ç•ªå�·',
             'tweb.Country' => 'å›½',
             'tweb.DarkMode' => 'ãƒ€ãƒ¼ã‚¯ãƒ¢ãƒ¼ãƒ‰',
             'tweb.Error.AnError' => 'ã‚¨ãƒ©ãƒ¼ã�Œç™ºç”Ÿã�—ã�¾ã�—ã�Ÿã€‚ã�—ã�°ã‚‰ã��ã�—ã�¦ã�‹ã‚‰ã‚‚ã�†ä¸€åº¦ã�Šè©¦ã�—ã��ã� ã�•ã�„ã€‚',
             'tweb.FIRSTNAME_INVALID' => 'ç„¡åŠ¹ã�ªå��ã�§ã�™',
             'tweb.FirstName' => 'å�� (å¿…é ˆ)',
             'tweb.LASTNAME_INVALID' => 'ç„¡åŠ¹ã�ªå§“ã�§ã�™',
             'tweb.LastName' => 'å§“ (ä»»æ„�)',
             'tweb.Login.Bot' => 'ãƒœãƒƒãƒˆã�¨ã�—ã�¦ãƒ­ã‚°ã‚¤ãƒ³ >',
             'tweb.Login.Bot.Cancel' => 'ãƒ¦ãƒ¼ã‚¶ãƒ¼ã�¨ã�—ã�¦ãƒ­ã‚°ã‚¤ãƒ³ >',
             'tweb.Login.Bot.Subtitle' => 'ãƒœãƒƒãƒˆã�®ä½œæˆ�æ™‚ã�« @BotFather ã�‹ã‚‰
å�—ã�‘å�–ã�£ã�Ÿãƒˆãƒ¼ã‚¯ãƒ³ã‚’å…¥åŠ›ã�—ã�¦ã��ã� ã�•ã�„ã€‚',
             'tweb.Login.Bot.Title' => 'ãƒœãƒƒãƒˆã�¨ã�—ã�¦ãƒ­ã‚°ã‚¤ãƒ³',
             'tweb.Login.Bot.Token' => 'ãƒœãƒƒãƒˆãƒˆãƒ¼ã‚¯ãƒ³',
             'tweb.Login.Code.SecretPhrase' => 'ç§˜å¯†ã�®ãƒ•ãƒ¬ãƒ¼ã‚º',
             'tweb.Login.Code.SecretWord' => 'ç§˜å¯†ã�®å�˜èªž',
             'tweb.Login.Code.SentCall' => 'ã�Šé›»è©±ã�§ã‚³ãƒ¼ãƒ‰ã‚’
ã�Šä¼�ã�ˆã�—ã�¾ã�™ã€‚',
             'tweb.Login.Code.SentEmail' => 'ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ **%s** ã‚’ç¢ºèª�ã�— (è¿·æƒ‘ãƒ¡ãƒ¼ãƒ«ãƒ•ã‚©ãƒ«ãƒ€ãƒ¼ã‚‚ã�”ç¢ºèª�ã��ã� ã�•ã�„)ã€�é€�ä¿¡ã�—ã�Ÿã‚³ãƒ¼ãƒ‰ã‚’å…¥åŠ›ã�—ã�¦ã��ã� ã�•ã�„ã€‚',
             'tweb.Login.Code.SentInApp' => 'ã‚³ãƒ¼ãƒ‰ã‚’è¨˜è¼‰ã�—ã�Ÿãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã‚’
Telegram ã�§é€�ä¿¡ã�—ã�¾ã�—ã�Ÿã€‚',
             'tweb.Login.Code.SentMissedCall' => 'æ•°ç§’ä»¥å†…ã�« **%s** ã�‹ã‚‰
çŸ­ã�„ç�€ä¿¡ã�Œã�‚ã‚Šã�¾ã�™ã€‚å¿œç­”ã�›ã�šã�«ã€�
ã��ã�®ç•ªå�·ã�®æœ«å°¾ã�®æ•°å­—ã‚’å…¥åŠ›ã�—ã�¦ã��ã� ã�•ã�„ã€‚',
             'tweb.Login.Code.SentSms' => 'ã‚³ãƒ¼ãƒ‰ã‚’è¨˜è¼‰ã�—ã�Ÿ SMS ã‚’
é€�ä¿¡ã�—ã�¾ã�—ã�Ÿã€‚',
             'tweb.Login.Code.SentSmsPhrase' => 'é›»è©±ç•ªå�· **%s** ã�«
ç§˜å¯†ã�®ãƒ•ãƒ¬ãƒ¼ã‚ºã‚’è¨˜è¼‰ã�—ã�Ÿ SMS ã‚’é€�ä¿¡ã�—ã�¾ã�—ã�Ÿã€‚',
             'tweb.Login.Code.SentSmsPhraseBeginning' => 'é›»è©±ç•ªå�· **%1$s** ã�«ã€�ã€Œ**%2$s**ã€�ã�§å§‹ã�¾ã‚‹
ç§˜å¯†ã�®ãƒ•ãƒ¬ãƒ¼ã‚ºã‚’è¨˜è¼‰ã�—ã�Ÿ SMS ã‚’é€�ä¿¡ã�—ã�¾ã�—ã�Ÿã€‚',
             'tweb.Login.Code.SentSmsWord' => 'é›»è©±ç•ªå�· **%s** ã�«
ç§˜å¯†ã�®å�˜èªžã‚’è¨˜è¼‰ã�—ã�Ÿ SMS ã‚’é€�ä¿¡ã�—ã�¾ã�—ã�Ÿã€‚',
             'tweb.Login.Code.SentSmsWordBeginning' => 'é›»è©±ç•ªå�· **%1$s** ã�«ã€�ã€Œ**%2$s**ã€�ã�§å§‹ã�¾ã‚‹
ç§˜å¯†ã�®å�˜èªžã‚’è¨˜è¼‰ã�—ã�Ÿ SMS ã‚’é€�ä¿¡ã�—ã�¾ã�—ã�Ÿã€‚',
             'tweb.Login.Code.SentUnknown' => 'ã�‚ã‚‰ã‚†ã‚‹å ´æ‰€ã�§ã‚³ãƒ¼ãƒ‰ã‚’
ã�”ç¢ºèª�ã��ã� ã�•ã�„ (ç¨®é¡ž: %s)',
             'tweb.Login.Code.WordBeginningInvalid' => 'å�˜èªžã�Œæ­£ã�—ã��ã�‚ã‚Šã�¾ã�›ã‚“',
             'tweb.Login.Code.WordInvalid' => 'æ­£ã�—ã��ã�‚ã‚Šã�¾ã�›ã‚“ã€‚ã‚‚ã�†ä¸€åº¦ã�Šè©¦ã�—ã��ã� ã�•ã�„',
             'tweb.Login.Next' => 'æ¬¡ã�¸',
             'tweb.Login.Password.Subtitle' => 'ã�“ã�®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã�¯è¿½åŠ ã�®ãƒ‘ã‚¹ãƒ¯ãƒ¼ãƒ‰ã�§
ä¿�è­·ã�•ã‚Œã�¦ã�„ã�¾ã�™',
             'tweb.Login.Password.Title' => 'ãƒ‘ã‚¹ãƒ¯ãƒ¼ãƒ‰ã‚’å…¥åŠ›',
             'tweb.Login.PhoneLabelInvalid' => 'ç„¡åŠ¹ã�ªé›»è©±ç•ªå�·',
             'tweb.Login.QR.Cancel' => 'é›»è©±ç•ªå�·ã�§ãƒ­ã‚°ã‚¤ãƒ³ >',
             'tweb.Login.QR.Help1' => 'ã‚¹ãƒžãƒ¼ãƒˆãƒ•ã‚©ãƒ³ã�§ Telegram ã‚’é–‹ã��ã�¾ã�™',
             'tweb.Login.QR.Help2' => '**[è¨­å®š]** > **[ãƒ‡ãƒ�ã‚¤ã‚¹]** > **[ãƒ‡ãƒ�ã‚¤ã‚¹ã‚’è¿½åŠ ]** ã�«é€²ã�¿ã�¾ã�™',
             'tweb.Login.QR.Help3' => 'ã‚¹ãƒžãƒ¼ãƒˆãƒ•ã‚©ãƒ³ã‚’ã�“ã�®ç”»é�¢ã�«å�‘ã�‘ã�¦ãƒ­ã‚°ã‚¤ãƒ³ã‚’ç¢ºèª�ã�—ã�¾ã�™',
             'tweb.Login.QR.Login' => 'QR ã‚³ãƒ¼ãƒ‰ã�§ãƒ­ã‚°ã‚¤ãƒ³ >',
             'tweb.Login.QR.Subtitle' => 'ã‚¹ãƒžãƒ¼ãƒˆãƒ•ã‚©ãƒ³ã�® Telegram ã‚¢ãƒ—ãƒªã�§ã‚¹ã‚­ãƒ£ãƒ³ã�—ã�¦ã��ã� ã�•ã�„',
             'tweb.Login.QR.Title' => 'QR ã‚³ãƒ¼ãƒ‰ã�§ãƒ­ã‚°ã‚¤ãƒ³',
             'tweb.Login.Register.Subtitle' => 'ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’ä½œæˆ�ã�™ã‚‹ã�«ã�¯
å��å‰�ã‚’å…¥åŠ›ã�—ã�¦ã��ã� ã�•ã�„',
             'tweb.Login.StartText' => 'å›½ç•ªå�·ã‚’ç¢ºèª�ã�—ã�¦
é›»è©±ç•ªå�·ã‚’å…¥åŠ›ã�—ã�¦ã��ã� ã�•ã�„ã€‚',
             'tweb.Login.Title' => 'MadelineProto ã�«ãƒ­ã‚°ã‚¤ãƒ³',
             'tweb.LoginPassword' => 'ãƒ‘ã‚¹ãƒ¯ãƒ¼ãƒ‰',
             'tweb.Optional' => 'ä»»æ„�',
             'tweb.PASSWORD_HASH_INVALID' => 'ãƒ‘ã‚¹ãƒ¯ãƒ¼ãƒ‰ã�Œæ­£ã�—ã��ã�‚ã‚Šã�¾ã�›ã‚“',
             'tweb.PHONE_CODE_EMPTY' => 'ç„¡åŠ¹ã�ªã‚³ãƒ¼ãƒ‰ã�§ã�™',
             'tweb.PHONE_CODE_EXPIRED' => 'ã‚³ãƒ¼ãƒ‰ã�®æœ‰åŠ¹æœŸé™�ã�Œåˆ‡ã‚Œã�¾ã�—ã�Ÿ',
             'tweb.PHONE_CODE_INVALID' => 'ç„¡åŠ¹ã�ªã‚³ãƒ¼ãƒ‰ã�§ã�™',
             'tweb.PHONE_NUMBER_BANNED' => 'ã�“ã�®é›»è©±ç•ªå�·ã�¯åˆ©ç”¨å�œæ­¢ã�•ã‚Œã�¦ã�„ã�¾ã�™',
             'tweb.PHONE_NUMBER_FLOOD' => 'è©¦è¡Œå›žæ•°ã�Œå¤šã�™ã�Žã�¾ã�™ã€‚ã�—ã�°ã‚‰ã��ã�—ã�¦ã�‹ã‚‰ã‚‚ã�†ä¸€åº¦ã�Šè©¦ã�—ã��ã� ã�•ã�„',
             'tweb.PHONE_NUMBER_INVALID' => 'ç„¡åŠ¹ã�ªé›»è©±ç•ªå�·ã�§ã�™',
             'tweb.PHONE_PASSWORD_FLOOD' => 'è©¦è¡Œå›žæ•°ã�Œå¤šã�™ã�Žã�¾ã�™ã€‚ã�—ã�°ã‚‰ã��ã�—ã�¦ã�‹ã‚‰ã‚‚ã�†ä¸€åº¦ã�Šè©¦ã�—ã��ã� ã�•ã�„',
             'tweb.PhoneNumber.Code.Fragment.Info' => '[Fragment]() ã�® Numbers ã‚»ã‚¯ã‚·ãƒ§ãƒ³ã�§ã‚³ãƒ¼ãƒ‰ã‚’ç¢ºèª�ã�—ã�¦ã��ã� ã�•ã�„',
             'tweb.PleaseWait' => 'ã�Šå¾…ã�¡ã��ã� ã�•ã�„...',
             'tweb.StartMessaging' => 'ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã‚’å§‹ã‚�ã‚‹',
             'tweb.YourName' => 'å��å‰�',
             'type_extract_error' => 'åž‹ "%s" ã‚’æŠ½å‡ºã�§ã��ã�¾ã�›ã‚“ã�§ã�—ã�Ÿã€‚MadelineProto ã‚’ã‚¢ãƒƒãƒ—ãƒ‡ãƒ¼ãƒˆã�—ã�¦ã��ã� ã�•ã�„ï¼�',
             'type_extract_error_id' => 'åž‹ %s (ID %s) ã‚’æŠ½å‡ºã�§ã��ã�¾ã�›ã‚“ã�§ã�—ã�Ÿã€‚MadelineProto ã‚’ã‚¢ãƒƒãƒ—ãƒ‡ãƒ¼ãƒˆã�—ã�¦ã��ã� ã�•ã�„ï¼�',
             'update_madelineproto' => 'å�¤ã�„ãƒ�ãƒ¼ã‚¸ãƒ§ãƒ³ã�® MadelineProto ã‚’å®Ÿè¡Œã�—ã�¦ã�„ã‚‹ã�Ÿã‚�ã€�ã‚¢ãƒƒãƒ—ãƒ‡ãƒ¼ãƒˆã�Œå¿…è¦�ã�§ã�™ã€‚ç�¾åœ¨ã�®ãƒ�ãƒ¼ã‚¸ãƒ§ãƒ³ã�¯ %s ã�§ã�™ã�Œã€�å¤šæ•°ã�®ãƒ�ã‚°ä¿®æ­£ã�¨æ–°æ©Ÿèƒ½ã‚’å�«ã‚€æœ€æ–°ãƒ�ãƒ¼ã‚¸ãƒ§ãƒ³ã�¯ %s ã�§ã�™ï¼�',
             'value_bigger_than_2147483647' => 'æŒ‡å®šã�•ã‚Œã�Ÿå€¤ %s ã�Œ 2147483647 ã‚ˆã‚Šå¤§ã��ã�„ã�§ã�™',
             'value_bigger_than_4294967296' => 'æŒ‡å®šã�•ã‚Œã�Ÿå€¤ %s ã�Œ 4294967296 ã‚ˆã‚Šå¤§ã��ã�„ã�§ã�™',
             'value_bigger_than_9223372036854775807' => 'æŒ‡å®šã�•ã‚Œã�Ÿå€¤ %s ã�Œ 9223372036854775807 ã‚ˆã‚Šå¤§ã��ã�„ã�§ã�™',
             'value_smaller_than_0' => 'æŒ‡å®šã�•ã‚Œã�Ÿå€¤ %s ã�Œ 0 ã‚ˆã‚Šå°�ã�•ã�„ã�§ã�™',
             'value_smaller_than_2147483648' => 'æŒ‡å®šã�•ã‚Œã�Ÿå€¤ %s ã�Œ -2147483648 ã‚ˆã‚Šå°�ã�•ã�„ã�§ã�™',
             'value_smaller_than_9223372036854775808' => 'æŒ‡å®šã�•ã‚Œã�Ÿå€¤ %s ã�Œ -9223372036854775808 ã‚ˆã‚Šå°�ã�•ã�„ã�§ã�™',
             'waveform_must_have_100_values' => 'æ³¢å½¢ã�®é…�åˆ—ã�«ã�¯ 100 å€‹ã�®å€¤ã�Œå¿…è¦�ã�§ã�™ï¼�',
             'waveform_value' => 'æ³¢å½¢ã�®å€¤ã�¯ 0 ã�‹ã‚‰ 31 ã�®ç¯„å›²ã�§ã�ªã�‘ã‚Œã�°ã�ªã‚Šã�¾ã�›ã‚“ï¼�',
             'webContinue' => 'ç¶šè¡Œ',
             'webDocs' => 'ãƒ‰ã‚­ãƒ¥ãƒ¡ãƒ³ãƒˆ',
             'webDoneLead' => 'MadelineProto ã�¯ã‚µãƒ¼ãƒ�ãƒ¼ä¸Šã�§å®Ÿè¡Œã‚’ç¶šã�‘ã�¦ã�„ã�¾ã�™ã€‚ã�“ã�®ãƒšãƒ¼ã‚¸ã�¯é–‰ã�˜ã�¦ã‚‚ã�‹ã�¾ã�„ã�¾ã�›ã‚“ã€‚',
             'webError' => '%sã€‚ç¢ºèª�ã�—ã�¦ã‚‚ã�†ä¸€åº¦ã�Šè©¦ã�—ã��ã� ã�•ã�„ã€‚',
             'webFieldRequired' => 'ã�“ã�®é …ç›®ã‚’å…¥åŠ›ã�—ã�¦ã��ã� ã�•ã�„ã€‚',
             'webHidePassword' => 'é�žè¡¨ç¤º',
             'webLogin' => 'ãƒ­ã‚°ã‚¤ãƒ³',
             'webOptional' => 'ä»»æ„�',
             'webPhoneInvalid' => 'å›½ç•ªå�·ã‚’å�«ã‚�ã�¦é›»è©±ç•ªå�·ã‚’å…¥åŠ›ã�—ã�¦ã��ã� ã�•ã�„ (ä¾‹: +39 123 456 7890)ã€‚',
             'webProgress' => 'ã‚»ãƒƒãƒˆã‚¢ãƒƒãƒ—ã�®é€²æ�—',
             'webQrTitle' => 'QR ã‚³ãƒ¼ãƒ‰ã�§ãƒ­ã‚°ã‚¤ãƒ³',
             'webQrWaiting' => 'ã‚¹ã‚­ãƒ£ãƒ³ã‚’å¾…ã�£ã�¦ã�„ã�¾ã�™â€¦',
             'webShowPassword' => 'è¡¨ç¤º',
             'webStepAccount' => 'ã‚¢ã‚«ã‚¦ãƒ³ãƒˆ',
             'webStepApp' => 'API ID ã�¨ hash',
             'webStepDone' => 'å®Ÿè¡Œä¸­',
             'webStepVerify' => 'èª�è¨¼',
         ],
        'ko' =>
         [
             '2fa_uncalled' => 'ë¹„ë°€ë²ˆí˜¸ë¥¼ ê¸°ë‹¤ë¦¬ê³  ìžˆì§€ ì•ŠìŠµë‹ˆë‹¤! ë¨¼ì € phoneLogin ë°� completePhoneLogin ë©”ì„œë“œë¥¼ í˜¸ì¶œí•˜ì‹­ì‹œì˜¤!',
             'accepting_call' => '%sì�˜ í†µí™”ë¥¼ ìˆ˜ë�½í•˜ëŠ” ì¤‘...',
             'account_banned' => '!!!!!!! ê²½ê³  !!!!!!!
Telegramì�˜ í”ŒëŸ¬ë“œ ë°©ì§€ ì‹œìŠ¤í…œì�´ ì�´ ê³„ì •ì�„ ì •ì§€í–ˆìŠµë‹ˆë‹¤.
ê³„ì†�í•˜ë ¤ë©´ ìˆ˜ë�™ ì�¸ì¦�ì�´ í•„ìš”í•©ë‹ˆë‹¤.
recover@telegram.orgë¡œ ì�´ë©”ì�¼ì�„ ë³´ë‚´ ì „í™”ë²ˆí˜¸ %sì�˜ ì •ì§€ í•´ì œë¥¼ ìš”ì²­í•˜ê³ , ì�´ ì „í™”ë²ˆí˜¸ë¡œ ë¬´ì—‡ì�„ í•  ê²ƒì�¸ì§€ ê°„ë‹¨ížˆ ì„¤ëª…í•˜ì‹­ì‹œì˜¤.
ê·¸ëŸ° ë‹¤ì�Œ ë‹¤ì‹œ ë¡œê·¸ì�¸í•˜ì‹­ì‹œì˜¤.
ì�´ ê³„ì •ì�„ ì�˜ë�„ì �ìœ¼ë¡œ ì‚­ì œí•œ ê²½ìš° ì�´ ë©”ì‹œì§€ë¥¼ ë¬´ì‹œí•˜ì‹­ì‹œì˜¤.',
             'already_loggedIn' => 'ì�´ MadelineProto ì�¸ìŠ¤í„´ìŠ¤ëŠ” ì�´ë¯¸ ë¡œê·¸ì�¸ë�˜ì–´ ìžˆìŠµë‹ˆë‹¤!',
             'apiAppDescLabelWeb' => 'ì„¤ëª…',
             'apiAppInstructionsAuto0' => 'ì•± ì�´ë¦„ì�„ ìž…ë ¥í•˜ì‹­ì‹œì˜¤(ì•„ë¬´ ì�´ë¦„ì�´ë‚˜ ê°€ëŠ¥): ',
             'apiAppInstructionsAuto1' => 'ì•±ì�˜ ì§§ì�€ ì�´ë¦„ì�„ ìž…ë ¥í•˜ì‹­ì‹œì˜¤(ì˜�ë¬¸ìž�ì™€ ìˆ«ìž�, 5~32ìž�): ',
             'apiAppInstructionsAuto2' => 'ì•±/ì›¹ì‚¬ì�´íŠ¸ URL ë˜�ëŠ” t.me/yourusernameì�„ ìž…ë ¥í•˜ì‹­ì‹œì˜¤: ',
             'apiAppInstructionsAuto3' => 'ì•± í”Œëž«í�¼ì�„ ìž…ë ¥í•˜ì‹­ì‹œì˜¤: ',
             'apiAppInstructionsAuto4' => 'ì•± ì„¤ëª…ì�„ ìž…ë ¥í•˜ì‹­ì‹œì˜¤: ',
             'apiAppInstructionsAutoTypeOther' => 'ê¸°íƒ€(ì„¤ëª…ì—� ëª…ì‹œ)',
             'apiAppInstructionsManual0' => 'ì•± ì�´ë¦„, ì•„ë¬´ ì�´ë¦„ì�´ë‚˜ ê°€ëŠ¥',
             'apiAppInstructionsManual1' => 'ì•±ì�˜ ì§§ì�€ ì�´ë¦„, ì˜�ë¬¸ìž�ì™€ ìˆ«ìž�, 5~32ìž�',
             'apiAppInstructionsManual2' => 'ì•±/ì›¹ì‚¬ì�´íŠ¸ URL ë˜�ëŠ” t.me/yourusername',
             'apiAppInstructionsManual3' => 'ì•„ë¬´ ê°’ì�´ë‚˜ ê°€ëŠ¥',
             'apiAppInstructionsManual4' => 'ì—¬ê¸°ì—� ì•± ì„¤ëª…ì�„ ìž…ë ¥í•©ë‹ˆë‹¤',
             'apiAppShortNameLabelWeb' => 'ì§§ì�€ ì�´ë¦„',
             'apiAppTitleLabelWeb' => 'ì•± ì�´ë¦„',
             'apiAppUrlLabelWeb' => 'URL',
             'apiAppWeb' => 'API ì •ë³´ë¥¼ ìž…ë ¥í•˜ì‹­ì‹œì˜¤',
             'apiAutoCodeLabelWeb' => 'ë¡œê·¸ì�¸ ì½”ë“œ',
             'apiAutoCodeLeadWeb' => 'my.telegram.orgê°€ Telegram ì•±ì�˜ Telegram ì±„íŒ…ìœ¼ë¡œ ë¡œê·¸ì�¸ ì½”ë“œë¥¼ ë³´ëƒˆìŠµë‹ˆë‹¤(SMS ì•„ë‹˜).',
             'apiAutoCodeTitleWeb' => 'Telegramì—�ì„œ ë°›ì�€ ì½”ë“œ ìž…ë ¥',
             'apiAutoErrorTitle' => 'my.telegram.orgê°€ ì˜¤ë¥˜ë¥¼ ë°˜í™˜í–ˆìŠµë‹ˆë‹¤',
             'apiAutoFallbackManual' => 'ëŒ€ì‹  API IDì™€ hashë¥¼ ì§�ì ‘ ìž…ë ¥í•˜ì‹­ì‹œì˜¤.',
             'apiAutoPhoneHintWeb' => 'my.telegram.orgëŠ” ë¡œê·¸ì�¸ ì½”ë“œë¥¼ SMSê°€ ì•„ë‹Œ Telegram ì•±ìœ¼ë¡œ ë³´ëƒ…ë‹ˆë‹¤.',
             'apiAutoPrompt0' => 'Telegramì—� ì�´ë¯¸ ë“±ë¡�ë�œ ì „í™”ë²ˆí˜¸ë¥¼ ìž…ë ¥í•˜ì‹­ì‹œì˜¤: ',
             'apiAutoPrompt1' => 'Telegramì—�ì„œ ë°›ì�€ ì�¸ì¦� ì½”ë“œë¥¼ ìž…ë ¥í•˜ì‹­ì‹œì˜¤: ',
             'apiAutoWeb' => 'API IDë¥¼ ë°›ìœ¼ë ¤ë©´ Telegramì—� <b>ì�´ë¯¸ ë“±ë¡�ë�œ</b> ì „í™”ë²ˆí˜¸ë¥¼ ìž…ë ¥í•˜ì‹­ì‹œì˜¤',
             'apiChooseAutoWeb' => 'ìž�ë�™ìœ¼ë¡œ ë°›ê¸°',
             'apiChooseManualAuto' => 'my.telegram.orgì—�ì„œ API IDì™€ hashë¥¼ ìž�ë�™ìœ¼ë¡œ ë°›ìœ¼ì‹œê² ìŠµë‹ˆê¹Œ(a), ì•„ë‹ˆë©´ ì§�ì ‘ ìž…ë ¥í•˜ì‹œê² ìŠµë‹ˆê¹Œ(m)?',
             'apiChooseManualAutoTip' => 'ì„¤ì •ì�„ ì‚¬ìš©í•˜ì—¬ ì½”ë“œì—�ì„œ API ID/hashë¥¼ ì§�ì ‘ ì§€ì •í•  ìˆ˜ë�„ ìžˆìŠµë‹ˆë‹¤: %s',
             'apiChooseManualAutoTipWeb' => '<a target="_blank" href="%s">ì„¤ì •</a>ì�„ ì‚¬ìš©í•˜ì—¬ ì½”ë“œì—�ì„œ API ID/hashë¥¼ ì§�ì ‘ ì§€ì •í•  ìˆ˜ë�„ ìžˆìŠµë‹ˆë‹¤.',
             'apiChooseManualWeb' => 'ì�´ë¯¸ API IDê°€ ìžˆìŠµë‹ˆë‹¤',
             'apiChoosePrompt' => 'ì„ íƒ�í•˜ì‹­ì‹œì˜¤ (m/a): ',
             'apiCreateErrorTitle' => 'my.telegram.orgì—�ì„œ ì•±ì�„ ë§Œë“¤ì§€ ëª»í–ˆìŠµë‹ˆë‹¤',
             'apiCreateLeadWeb' => 'ì�´ ê³„ì •ì—�ëŠ” ì•„ì§� my.telegram.org ì•±ì�´ ì—†ìŠµë‹ˆë‹¤. ì•„ëž˜ ì •ë³´ëŠ” ë³¸ì�¸ì—�ê²Œë§Œ í‘œì‹œë�˜ë©° ë‚˜ì¤‘ì—� ë³€ê²½í•  ìˆ˜ ìžˆìŠµë‹ˆë‹¤.',
             'apiCreateTitleWeb' => 'ì•± ë§Œë“¤ê¸°',
             'apiCreateVpsWarning' => 'Telegramì�€ VPS, VPN ë°� ê¸°íƒ€ ë�°ì�´í„° ì„¼í„° IP ëŒ€ì—­ì—�ì„œ my.telegram.orgì�˜ ìƒˆ ì•±ì�„ ë§Œë“œëŠ” ê²ƒì�„ í—ˆìš©í•˜ì§€ ì•Šìœ¼ë©°, ì�´ ìš”ì²­ì�€ MadelineProtoë¥¼ ì‹¤í–‰í•˜ëŠ” ë¨¸ì‹ ì�˜ IPì—�ì„œ ì „ì†¡ë�˜ì—ˆìŠµë‹ˆë‹¤. ê°€ì •ìš© íšŒì„ (ì˜ˆ: ëª¨ë°”ì�¼ ë�°ì�´í„°ë¥¼ ì‚¬ìš©í•˜ëŠ” íœ´ëŒ€í�°)ì—�ì„œ https://my.telegram.orgì—� ì ‘ì†�í•´ ì§�ì ‘ ì•±ì�„ ë§Œë“  ë‹¤ì�Œ API IDì™€ hashë¥¼ ìž…ë ¥í•˜ì‹­ì‹œì˜¤.',
             'apiCreateWeb' => 'ì•± ë§Œë“¤ê¸°',
             'apiError' => 'ì˜¤ë¥˜: %s. ë‹¤ì‹œ ì‹œë�„í•˜ì‹­ì‹œì˜¤.',
             'apiInvalidWeb' => 'API IDëŠ” ìˆ«ìž�ì�´ê³  API hashëŠ” 32ìž�ìž…ë‹ˆë‹¤. my.telegram.orgì—�ì„œ ë‹¤ì‹œ ë³µì‚¬í•˜ì‹­ì‹œì˜¤.',
             'apiManualInstructions0' => 'https://my.telegram.orgì—� ë¡œê·¸ì�¸í•©ë‹ˆë‹¤',
             'apiManualInstructions1' => 'API development toolsë¡œ ì�´ë�™í•©ë‹ˆë‹¤',
             'apiManualInstructions2' => 'create applicationì�„ í�´ë¦­í•©ë‹ˆë‹¤',
             'apiManualPrompt0' => 'API IDë¥¼ ìž…ë ¥í•˜ì‹­ì‹œì˜¤: ',
             'apiManualPrompt1' => 'API hashë¥¼ ìž…ë ¥í•˜ì‹­ì‹œì˜¤: ',
             'apiManualWeb' => 'API IDì™€ API hashë¥¼ ìž…ë ¥í•˜ì‹­ì‹œì˜¤',
             'apiParamsError' => 'í•„ìˆ˜ ë§¤ê°œë³€ìˆ˜ë¥¼ ëª¨ë‘� ìž…ë ¥í•˜ì§€ ì•Šì•˜ìŠµë‹ˆë‹¤!',
             'apiSaveWeb' => 'ì €ìž¥í•˜ê³  ê³„ì†�',
             'apiSendCodeWeb' => 'ì½”ë“œ ë³´ë‚´ê¸°',
             'apiSetupLeadWeb' => 'ëª¨ë“  Telegram ì•±ì—�ëŠ” API IDì™€ hashê°€ í•„ìš”í•©ë‹ˆë‹¤. ê¸°ì¡´ Telegram ê³„ì •ì�˜ ì „í™”ë²ˆí˜¸ë¡œ MadelineProtoê°€ my.telegram.orgì—�ì„œ ëŒ€ì‹  ë°›ì•„ì˜¬ ìˆ˜ ìžˆìŠµë‹ˆë‹¤.',
             'apiSetupTitleWeb' => 'API IDì™€ hash ë°›ê¸°',
             'apiStartOverWeb' => 'ì²˜ì�Œë¶€í„° ë‹¤ì‹œ',
             'api_not_set' => 'API í‚¤ì™€ API IDë¥¼ ìž…ë ¥í•´ì•¼ í•©ë‹ˆë‹¤. my.telegram.orgì—�ì„œ ë°œê¸‰ë°›ìœ¼ì‹­ì‹œì˜¤',
             'array_invalid' => 'ìœ íš¨í•œ ë°°ì—´ì�„ ìž…ë ¥í•˜ì§€ ì•Šì•˜ìŠµë‹ˆë‹¤',
             'baseDirLimitation' => 'basedir ì œí•œì�´ ì„¤ì •ë�˜ì–´ ìžˆìŠµë‹ˆë‹¤. ì„±ëŠ¥ì—� ì˜�í–¥ì�„ ì£¼ê³  ë¬¸ì œë¥¼ ì�¼ìœ¼í‚¬ ìˆ˜ ìžˆìœ¼ë¯€ë¡œ ê°€ëŠ¥í•˜ë©´ ë¹„í™œì„±í™”í•˜ì‹­ì‹œì˜¤!',
             'bool_error' => 'ë¶ˆë¦¬ì–¸ ê°’ì�„ ì¶”ì¶œí•  ìˆ˜ ì—†ìŠµë‹ˆë‹¤',
             'botAlreadyRunning' => 'ë´‡ì�´ ì�´ë¯¸ ì‹¤í–‰ ì¤‘ìž…ë‹ˆë‹¤!',
             'botapi_conversion_error' => '%sì�„(ë¥¼) Bot API ê°�ì²´ë¡œ ë³€í™˜í•  ìˆ˜ ì—†ìŠµë‹ˆë‹¤',
             'call_already_accepted' => 'í†µí™” %sì�€(ëŠ”) ì�´ë¯¸ ìˆ˜ë�½ë�˜ì—ˆìŠµë‹ˆë‹¤',
             'call_already_declined' => 'í†µí™” %sì�€(ëŠ”) ì�´ë¯¸ ê±°ì ˆë�˜ì—ˆìŠµë‹ˆë‹¤',
             'call_completing' => '%sì�˜ í†µí™”ë¥¼ ì™„ë£Œí•˜ëŠ” ì¤‘...',
             'call_confirming' => '%sì�˜ í†µí™”ë¥¼ í™•ì�¸í•˜ëŠ” ì¤‘...',
             'call_discarding' => 'í†µí™” %sì�„(ë¥¼) ì¢…ë£Œí•˜ëŠ” ì¤‘...',
             'call_error_1' => 'í†µí™” %sì�„(ë¥¼) ì°¾ì•„ ìˆ˜ë�½í•  ìˆ˜ ì—†ìŠµë‹ˆë‹¤',
             'call_error_2' => 'í†µí™” %sì�„(ë¥¼) ì°¾ì•„ í™•ì�¸í•  ìˆ˜ ì—†ìŠµë‹ˆë‹¤',
             'call_error_3' => 'í†µí™” %sì�„(ë¥¼) ì°¾ì•„ ì™„ë£Œí•  ìˆ˜ ì—†ìŠµë‹ˆë‹¤',
             'cli_need_dl.php_link' => 'CLIì—�ì„œ getDownloadLinkë¥¼ ì‚¬ìš©í•  ë•ŒëŠ” ë‹¤ìš´ë¡œë“œ ìŠ¤í�¬ë¦½íŠ¸ URLì�„ ì§€ì •í•˜ì‹­ì‹œì˜¤!',
             'constructor_not_found' => 'ë‹¤ì�Œ íƒ€ìž…ì�˜ ìƒ�ì„±ìž�ë¥¼ ì°¾ì�„ ìˆ˜ ì—†ìŠµë‹ˆë‹¤: ',
             'could_not_connect_to_MadelineProto' => 'MadelineProtoì—� ì—°ê²°í•  ìˆ˜ ì—†ìŠµë‹ˆë‹¤. í•´ê²°í•˜ë ¤ë©´ proc_openì�„ í™œì„±í™”í•˜ê³  open_basedir ì œí•œì�„ ì œê±°í•˜ê±°ë‚˜ ì›¹ ì„œë²„ì�˜ ê²½ë¡œ ìž¬ìž‘ì„±ì�„ ë¹„í™œì„±í™”í•˜ì‹­ì‹œì˜¤! ì�´ë¯¸ ê·¸ë ‡ê²Œ í–ˆë‹¤ë©´ CLI ë²„ì „ì�˜ PHPê°€ ì›¹ ë²„ì „ê³¼ ì •í™•ížˆ ê°™ì�€ì§€(ë²„ì „, í™•ìž¥ ë“±) í™•ì�¸í•˜ê³ , IPC ì„œë²„ì�˜ ì‹œìž‘ì�„ ë§‰ì�€ ì˜¤ë¥˜ì—� ëŒ€í•œ ìž�ì„¸í•œ ë‚´ìš©ì�€ MadelineProto.log íŒŒì�¼ì�„ í™•ì�¸í•˜ì‹­ì‹œì˜¤.',
             'could_not_convert_object' => '%s íƒ€ìž…ì�˜ ê°�ì²´ë¥¼ ë³€í™˜í•  ìˆ˜ ì—†ìŠµë‹ˆë‹¤',
             'deserialization_error' => 'ì—­ì§�ë ¬í™” ì¤‘ ì˜¤ë¥˜ê°€ ë°œìƒ�í–ˆìŠµë‹ˆë‹¤',
             'dl.php_check_logs_make_sure_session_running' => 'ì—°ê²°ë�œ MadelineProto EventHandler ë´‡ ë˜�ëŠ” MadelineProto IPC ì„œë²„ê°€ ì˜¤í”„ë�¼ì�¸ìž…ë‹ˆë‹¤. ë¡œê·¸ë¥¼ í™•ì�¸í•˜ê³  ë‘˜ ì¤‘ í•˜ë‚˜ ì�´ìƒ�ì�´ ì‹¤í–‰ ì¤‘ì�¸ì§€ í™•ì�¸í•˜ì‹­ì‹œì˜¤!',
             'dl.php_powered_by_madelineproto' => 'Telegram íŒŒì�¼ ë‹¤ìš´ë¡œë“œ ì„œë²„(ìµœëŒ€ 4GB), powered by <a href="https://docs.madelineproto.xyz" target="_blank">MadelineProto</a>!<br>ë‚˜ë§Œì�˜ Telegram íŒŒì�¼ ë‹¤ìš´ë¡œë“œ ì„œë²„ë¥¼ ì„¤ì •í•˜ëŠ” ë°©ë²•ì—� ëŒ€í•œ ìž�ì„¸í•œ ë‚´ìš©ì�€ <a href="https://docs.madelineproto.xyz/docs/FILES.html#getting-a-download-link" target="_blank">ì—¬ê¸°</a>ë¥¼ í�´ë¦­í•˜ì‹­ì‹œì˜¤!',
             'do_not_delete_MadelineProto.log' => 'MadelineProto.log íŒŒì�¼ì�€ ì ˆëŒ€ ì‚­ì œí•˜ë©´ ì•ˆ ë�©ë‹ˆë‹¤. ëŒ€ì‹  ì„¤ì •ì—�ì„œ ì‚¬ìš©ìž� ì§€ì • ìµœëŒ€ í�¬ê¸°ë¥¼ ì„¤ì •í•˜ì‹­ì‹œì˜¤!',
             'do_not_remove_MadelineProto.log_phar' => 'madeline.phar ë˜�ëŠ” madeline.phpë¥¼ ì‚­ì œí•˜ì§€ ë§ˆì‹­ì‹œì˜¤. ì‚­ì œí•˜ë©´ MadelineProtoê°€ ë¹„ì •ìƒ� ì¢…ë£Œë�©ë‹ˆë‹¤. MadelineProtoì—� ë¬¸ì œê°€ ìžˆìœ¼ë©´ https://github.com/danog/MadelineProto ë˜�ëŠ” https://t.me/pwrtelegramgroupì—� ë³´ê³ í•˜ì‹­ì‹œì˜¤',
             'do_not_use_blocking_class' => 'ì„±ëŠ¥ìƒ�ì�˜ ì�´ìœ ë¡œ í•¸ë“¤ëŸ¬ì—�ì„œëŠ” ë¹„ë�™ê¸°ê°€ ì•„ë‹Œ ë¸”ë¡œí‚¹ í�´ëž˜ìŠ¤ %sì�„(ë¥¼) ì‚¬ìš©í•  ìˆ˜ ì—†ìŠµë‹ˆë‹¤. ëŒ€ì‹  %sì�„(ë¥¼) ì‚¬ìš©í•˜ì‹­ì‹œì˜¤',
             'do_not_use_blocking_function' => 'ì„±ëŠ¥ìƒ�ì�˜ ì�´ìœ ë¡œ ì�´ë²¤íŠ¸ í•¸ë“¤ëŸ¬ì—�ì„œëŠ” ë¹„ë�™ê¸°ê°€ ì•„ë‹Œ ë¸”ë¡œí‚¹ í•¨ìˆ˜ %sì�„(ë¥¼) ì‚¬ìš©í•  ìˆ˜ ì—†ìŠµë‹ˆë‹¤. ëŒ€ì‹  %sì�„(ë¥¼) ì‚¬ìš©í•˜ì‹­ì‹œì˜¤',
             'do_not_use_deprecated_function' => '%s í•¨ìˆ˜ëŠ” ë�” ì�´ìƒ� ì‚¬ìš©ë�˜ì§€ ì•ŠìŠµë‹ˆë‹¤. ëŒ€ì‹  %sì�„(ë¥¼) ì‚¬ìš©í•˜ì‹­ì‹œì˜¤',
             'do_not_use_non_root_require_in_event_handler' => 'ì„±ëŠ¥ìƒ�ì�˜ ì�´ìœ ë¡œ ì�´ë²¤íŠ¸ í•¸ë“¤ëŸ¬ í�´ëž˜ìŠ¤ ë‚´ë¶€ì—�ì„œ require ë˜�ëŠ” includeë¥¼ ì‚¬ìš©í•˜ë©´ ì•ˆ ë�©ë‹ˆë‹¤. ë£¨íŠ¸ ìˆ˜ì¤€ì�˜ requireë§Œ í—ˆìš©ë�©ë‹ˆë‹¤.',
             'do_not_use_yield' => 'MadelineProto 8ì—�ì„œëŠ” ë¹„ë�™ê¸° í•¨ìˆ˜ì—�ì„œ yieldë¥¼ ì‚¬ìš©í•  í•„ìš”ê°€ ì—†ìœ¼ë©° ì§€ì›�í•˜ì§€ë�„ ì•ŠìŠµë‹ˆë‹¤. ì�´ì „ì—� ë¹„ë�™ê¸° í•¨ìˆ˜ í˜¸ì¶œì—� ì‚¬ìš©í•œ yield í‚¤ì›Œë“œë¥¼ ëª¨ë‘� ì œê±°í•˜ì‹­ì‹œì˜¤',
             'done' => 'ì™„ë£Œë�˜ì—ˆìŠµë‹ˆë‹¤!',
             'encode_double_error' => 'doubleì�„ ì˜¬ë°”ë¥´ê²Œ ì�¸ì½”ë”©í•  ìˆ˜ ì—†ìŠµë‹ˆë‹¤',
             'extensionRecommended' => 'ê²½ê³ : %s í™•ìž¥ì�´ ì„¤ì¹˜ë�˜ì–´ ìžˆì§€ ì•ŠìŠµë‹ˆë‹¤. MadelineProtoì�˜ ì†�ë�„ë¥¼ ë†’ì�´ë ¤ë©´ ì„¤ì¹˜í•˜ì‹­ì‹œì˜¤!',
             'extensionRequired' => 'MadelineProtoë¥¼ ì‹¤í–‰í•˜ë ¤ë©´ %s í™•ìž¥ì�´ í•„ìš”í•©ë‹ˆë‹¤. %s',
             'extensionRequiredInstallWithApt' => 'sudo apt-get install %s ëª…ë ¹ì�„ ì‹¤í–‰í•´ ë³´ì‹­ì‹œì˜¤.',
             'extensionRequiredInstallWithCustomInstructions' => '%sì�˜ ì•ˆë‚´ì—� ë”°ë�¼ ì„¤ì¹˜í•˜ì‹­ì‹œì˜¤.',
             'file_not_exist' => 'íŒŒì�¼ì�´ ì¡´ìž¬í•˜ì§€ ì•ŠìŠµë‹ˆë‹¤',
             'file_parsing' => '%sì�„(ë¥¼) íŒŒì‹±í•˜ëŠ” ì¤‘...',
             'file_type_invalid' => 'ìœ íš¨í•˜ì§€ ì•Šì�€ íŒŒì�¼ í˜•ì‹�ì�´ ê°�ì§€ë�˜ì—ˆìŠµë‹ˆë‹¤(%s)',
             'fingerprint_invalid' => 'í‚¤ ì§€ë¬¸ì�´ ìœ íš¨í•˜ì§€ ì•ŠìŠµë‹ˆë‹¤!',
             'go' => 'ì‹¤í–‰',
             'invalid_dl.php_session' => '%sì�€(ëŠ”) ì„¸ì…˜ IDê°€ ë‹¬ë�¼ ìœ íš¨í•œ ë‹¤ìš´ë¡œë“œ ìŠ¤í�¬ë¦½íŠ¸ê°€ ì•„ë‹™ë‹ˆë‹¤(ì˜ˆìƒ�: %s, ì‹¤ì œ: %s)',
             'ipc_tcp_warning' => 'UNIX sockets are not available, so the IPC server of the session listens on TCP (127.0.0.1) instead: any program running on this machine can connect to it, and control the session. On Windows 10 1803+, enable the sockets extension (extension=sockets in php.ini) to use UNIX sockets.',
             'length_too_big' => 'ê¸¸ì�´ê°€ ë„ˆë¬´ ê¹�ë‹ˆë‹¤',
             'loginBot' => 'ë´‡ í† í�°ì�„ ìž…ë ¥í•˜ì‹­ì‹œì˜¤: ',
             'loginBotTokenHintWeb' => 'ë´‡ì�„ ë§Œë“¤ ë•Œ @BotFatherê°€ ì•Œë ¤ ì¤€ í† í�°ìž…ë‹ˆë‹¤.',
             'loginBotTokenWeb' => 'ë´‡ í† í�°',
             'loginChoosePromptWeb' => 'ì‚¬ìš©ìž�ì™€ ë´‡ ì¤‘ ì–´ëŠ� ê²ƒìœ¼ë¡œ ë¡œê·¸ì�¸í•˜ì‹œê² ìŠµë‹ˆê¹Œ?',
             'loginCodeLeadWeb' => 'Telegramì�´ Telegram ì•±ìœ¼ë¡œ ë¡œê·¸ì�¸ ì½”ë“œë¥¼ ë³´ëƒˆìŠµë‹ˆë‹¤. ë‹¤ë¥¸ ê³³ì—� ë¡œê·¸ì�¸ë�˜ì–´ ìžˆì§€ ì•Šë‹¤ë©´ SMSë¡œ ë³´ëƒ…ë‹ˆë‹¤.',
             'loginCodeTitleWeb' => 'ë¡œê·¸ì�¸ ì½”ë“œ ìž…ë ¥',
             'loginLeadWeb' => 'ì�´ MadelineProto ì�¸ìŠ¤í„´ìŠ¤ê°€ ì‚¬ìš©í•  ê³„ì • ë˜�ëŠ” ë´‡ì�„ ì„ íƒ�í•˜ì‹­ì‹œì˜¤.',
             'loginManual' => 'ë˜�ëŠ” ë´‡ í† í�°ì�´ë‚˜ ì „í™”ë²ˆí˜¸ë¥¼ ìž…ë ¥í•˜ì—¬ ìˆ˜ë�™ìœ¼ë¡œ ë¡œê·¸ì�¸í•  ìˆ˜ë�„ ìžˆìŠµë‹ˆë‹¤: ',
             'loginNoCode' => 'ì „í™” ì�¸ì¦� ì½”ë“œë¥¼ ìž…ë ¥í•˜ì§€ ì•Šì•˜ìŠµë‹ˆë‹¤!',
             'loginNoName' => 'ì�´ë¦„ì�„ ìž…ë ¥í•˜ì§€ ì•Šì•˜ìŠµë‹ˆë‹¤!',
             'loginNoPass' => 'ë¹„ë°€ë²ˆí˜¸ë¥¼ ìž…ë ¥í•˜ì§€ ì•Šì•˜ìŠµë‹ˆë‹¤!',
             'loginOptionBot' => 'ë´‡',
             'loginOptionUser' => 'ì‚¬ìš©ìž�',
             'loginPasswordLabelWeb' => 'ë¹„ë°€ë²ˆí˜¸',
             'loginPasswordLeadWeb' => 'ì�´ ê³„ì •ì�€ ì„¤ì • > ê°œì�¸ì •ë³´ ë°� ë³´ì•ˆ > 2ë‹¨ê³„ ì�¸ì¦�ì—�ì„œ ì„¤ì •í•œ ë¹„ë°€ë²ˆí˜¸ë¡œ ë³´í˜¸ë�˜ì–´ ìžˆìŠµë‹ˆë‹¤.',
             'loginPasswordTitleWeb' => '2ë‹¨ê³„ ì�¸ì¦�',
             'loginQr' => 'íœ´ëŒ€í�°ì—�ì„œ Telegramì�„ ì—´ê³  ì„¤ì • > ê¸°ê¸° > ë�°ìŠ¤í�¬í†± ê¸°ê¸° ì—°ê²°ë¡œ ì�´ë�™í•˜ì—¬ ìœ„ì�˜ QR ì½”ë“œë¥¼ ìŠ¤ìº”í•˜ë©´ ìž�ë�™ìœ¼ë¡œ ë¡œê·¸ì�¸ë�©ë‹ˆë‹¤.',
             'loginQrCodeExpired' => 'QR ì½”ë“œê°€ ë§Œë£Œë�˜ì—ˆìŠµë‹ˆë‹¤. ìƒˆ ì½”ë“œë¥¼ ìƒ�ì„±í•˜ëŠ” ì¤‘...',
             'loginQrCodeSuccessful' => 'QR ì½”ë“œ ë¡œê·¸ì�¸ì—� ì„±ê³µí–ˆìŠµë‹ˆë‹¤!',
             'loginTitleWeb' => 'Telegramì—� ë¡œê·¸ì�¸',
             'loginTypeWeb' => 'ë¡œê·¸ì�¸ ìœ í˜•',
             'loginUser' => 'ì „í™”ë²ˆí˜¸ë¥¼ ìž…ë ¥í•˜ì‹­ì‹œì˜¤: ',
             'loginUserCode' => 'ì½”ë“œë¥¼ ìž…ë ¥í•˜ì‹­ì‹œì˜¤: ',
             'loginUserPass' => 'ë¹„ë°€ë²ˆí˜¸ë¥¼ ìž…ë ¥í•˜ì‹­ì‹œì˜¤(ížŒíŠ¸: %s): ',
             'loginUserPassHint' => 'ížŒíŠ¸: %s',
             'loginUserPassWeb' => 'ë¹„ë°€ë²ˆí˜¸ë¥¼ ìž…ë ¥í•˜ì‹­ì‹œì˜¤: ',
             'loginUserPhoneCodeWeb' => 'ì½”ë“œ',
             'loginUserPhoneHintWeb' => 'êµ­ê°€ ì½”ë“œë¥¼ í�¬í•¨í•˜ì—¬ ìž…ë ¥í•˜ì‹­ì‹œì˜¤(ì˜ˆ: +39 123 456 7890).',
             'loginUserPhoneWeb' => 'ì „í™”ë²ˆí˜¸',
             'loginWebQr' => 'íœ´ëŒ€í�°ì—�ì„œ Telegramì�„ ì—´ê³  ì„¤ì • > ê¸°ê¸° > ë�°ìŠ¤í�¬í†± ê¸°ê¸° ì—°ê²°ë¡œ ì�´ë�™í•˜ì—¬ ë‹¤ì�Œ QR ì½”ë“œë¥¼ ìŠ¤ìº”í•˜ë©´ ìž�ë�™ìœ¼ë¡œ ë¡œê·¸ì�¸í•  ìˆ˜ë�„ ìžˆìŠµë‹ˆë‹¤:',
             'loginWebQr1' => 'íœ´ëŒ€í�°ì—�ì„œ Telegramì�„ ì—½ë‹ˆë‹¤',
             'loginWebQr2' => 'ì„¤ì • > ê¸°ê¸° > ë�°ìŠ¤í�¬í†± ê¸°ê¸° ì—°ê²°ë¡œ ì�´ë�™í•©ë‹ˆë‹¤',
             'loginWebQr3' => 'íœ´ëŒ€í�°ì�„ ì�´ í™”ë©´ì—� ë¹„ì¶”ì–´ ë¡œê·¸ì�¸ì�„ í™•ì�¸í•©ë‹ˆë‹¤',
             'login_2fa_enabled' => '2ë‹¨ê³„ ì�¸ì¦�ì�´ í™œì„±í™”ë�˜ì–´ ìžˆìŠµë‹ˆë‹¤. complete2falogin í•¨ìˆ˜ë¥¼ í˜¸ì¶œí•´ì•¼ í•©ë‹ˆë‹¤...',
             'login_auth_key' => 'ì�¸ì¦� í‚¤ë¡œ ë¡œê·¸ì�¸í•˜ëŠ” ì¤‘...',
             'login_bot' => 'ë´‡ìœ¼ë¡œ ë¡œê·¸ì�¸í•˜ëŠ” ì¤‘...',
             'login_code_sending' => 'ì½”ë“œë¥¼ ë³´ë‚´ëŠ” ì¤‘...',
             'login_code_sent' => 'ì½”ë“œë¥¼ ë³´ëƒˆìŠµë‹ˆë‹¤! ì½”ë“œë¥¼ ë°›ìœ¼ë©´ completePhoneLogin í•¨ìˆ˜ë¥¼ ì‚¬ìš©í•˜ì‹­ì‹œì˜¤.',
             'login_code_uncalled' => 'ì½”ë“œë¥¼ ê¸°ë‹¤ë¦¬ê³  ìžˆì§€ ì•ŠìŠµë‹ˆë‹¤! ë¨¼ì € phoneLogin ë©”ì„œë“œë¥¼ í˜¸ì¶œí•˜ì‹­ì‹œì˜¤',
             'login_need_signup' => 'ì�´ ë²ˆí˜¸ë¡œ ìƒ�ì„±ë�œ ê³„ì •ì�´ ì—†ìŠµë‹ˆë‹¤. completeSignup í•¨ìˆ˜ë¥¼ í˜¸ì¶œí•´ì•¼ í•©ë‹ˆë‹¤...',
             'login_ok' => 'ë¡œê·¸ì�¸ë�˜ì—ˆìŠµë‹ˆë‹¤!',
             'login_user' => 'ì�¼ë°˜ ì‚¬ìš©ìž�ë¡œ ë¡œê·¸ì�¸í•˜ëŠ” ì¤‘...',
             'logout_ok' => 'ë¡œê·¸ì•„ì›ƒë�˜ì—ˆìŠµë‹ˆë‹¤!',
             'long_not_16' => 'ìž…ë ¥í•œ ê°’ì�˜ ê¸¸ì�´ê°€ 16ë°”ì�´íŠ¸ê°€ ì•„ë‹™ë‹ˆë‹¤',
             'long_not_32' => 'ìž…ë ¥í•œ ê°’ì�˜ ê¸¸ì�´ê°€ 32ë°”ì�´íŠ¸ê°€ ì•„ë‹™ë‹ˆë‹¤',
             'long_not_64' => 'ìž…ë ¥í•œ ê°’ì�˜ ê¸¸ì�´ê°€ 64ë°”ì�´íŠ¸ê°€ ì•„ë‹™ë‹ˆë‹¤',
             'madelineproto_ready' => 'MadelineProtoê°€ ì¤€ë¹„ë�˜ì—ˆìŠµë‹ˆë‹¤!',
             'manualAdminActionRequired' => '!!!!!!!!! ì‹œìŠ¤í…œ ê´€ë¦¬ìž�ì�˜ ìˆ˜ë�™ ì¡°ì¹˜ê°€ í•„ìš”í•©ë‹ˆë‹¤ !!!!!!!!!',
             'method_not_found' => 'ë©”ì„œë“œë¥¼ ì°¾ì�„ ìˆ˜ ì—†ìŠµë‹ˆë‹¤: ',
             'mmapErrorPart1' => 'ë©”ëª¨ë¦¬ ë§¤í•‘(mmap) ì˜�ì—­ì�˜ ìµœëŒ€ ê°œìˆ˜ì—� ë�„ë‹¬í–ˆìŠµë‹ˆë‹¤(%s). í•´ê²°í•˜ë ¤ë©´ ì»¤ë„� ì„¤ì • vm.max_map_countë¥¼ 262144ë¡œ ëŠ˜ë¦¬ì‹­ì‹œì˜¤.',
             'mmapErrorPart2' => 'í•´ê²°í•˜ë ¤ë©´ root ê¶Œí•œìœ¼ë¡œ ë‹¤ì�Œ ëª…ë ¹ì�„ ì‹¤í–‰í•˜ì‹­ì‹œì˜¤: %s',
             'mmapErrorPart3' => 'ìž¬ë¶€íŒ… í›„ì—�ë�„ ë³€ê²½ ì‚¬í•­ì�„ ìœ ì§€í•˜ë ¤ë©´: %s',
             'mmapErrorPart4' => 'Windows ë°� WSLì—�ì„œëŠ” íŽ˜ì�´ì§€ íŒŒì�¼ í�¬ê¸°ë¥¼ ëŠ˜ë¦¬ë©´ ë�„ì›€ì�´ ë�  ìˆ˜ ìžˆìŠµë‹ˆë‹¤. ë¬¸ì œê°€ ê³„ì†�ë�˜ë©´ ë„¤ì�´í‹°ë¸Œ Linuxë¡œ ì „í™˜í•˜ì‹­ì‹œì˜¤.',
             'must_have_declare_types' => 'ì„±ëŠ¥ìƒ�ì�˜ ì�´ìœ ë¡œ ì�´ë²¤íŠ¸ í•¸ë“¤ëŸ¬ íŒŒì�¼ì�˜ ì²« ë²ˆì§¸ ë¬¸ì�€ "declare(strict_types=1);"ì�´ì–´ì•¼ í•©ë‹ˆë‹¤',
             'nearest_dc' => 'í˜„ìž¬ ìœ„ì¹˜ëŠ” %sì�´ë©°, ê°€ìž¥ ê°€ê¹Œìš´ DCëŠ” %dìž…ë‹ˆë‹¤.',
             'need_dl.php' => 'ê¸°ë³¸ ë‹¤ìš´ë¡œë“œ ìŠ¤í�¬ë¦½íŠ¸ë¥¼ ìƒ�ì„±í•  ìˆ˜ ì—†ìŠµë‹ˆë‹¤(%s). ë‹¤ì�Œ ë‚´ìš©ìœ¼ë¡œ dl.php íŒŒì�¼ì�„ ë§Œë“¤ê³ : %s í•´ë‹¹ URLì�„ getDownloadLinkì�˜ ë‘� ë²ˆì§¸ ë§¤ê°œë³€ìˆ˜ë¡œ ì „ë‹¬í•˜ì‹­ì‹œì˜¤',
             'noReportPeers' => 'ê²½ê³ : ë³´ê³  ëŒ€ìƒ� í”¼ì–´ê°€ ì„¤ì •ë�˜ì§€ ì•Šì•˜ìŠµë‹ˆë‹¤. ì�´ë²¤íŠ¸ í•¸ë“¤ëŸ¬ì—� ë‹¤ì�Œ ë©”ì„œë“œë¥¼ ì¶”ê°€í•˜ì‹­ì‹œì˜¤',
             'non_text_conversion' => 'í…�ìŠ¤íŠ¸ê°€ ì•„ë‹Œ ë©”ì‹œì§€ëŠ” ì•„ì§� ë³€í™˜í•  ìˆ˜ ì—†ìŠµë‹ˆë‹¤!',
             'not_loggedIn' => 'ë¡œê·¸ì�¸ë�˜ì–´ ìžˆì§€ ì•ŠìŠµë‹ˆë‹¤!',
             'not_numeric' => 'ìž…ë ¥í•œ ê°’ì�´ ìˆ«ìž�ê°€ ì•„ë‹™ë‹ˆë‹¤',
             'params_missing' => 'í•„ìˆ˜ ë§¤ê°œë³€ìˆ˜ê°€ ì—†ìŠµë‹ˆë‹¤',
             'peer_not_in_db' => 'ì�´ í”¼ì–´ëŠ” ë‚´ë¶€ í”¼ì–´ ë�°ì�´í„°ë² ì�´ìŠ¤ì—� ì—†ìŠµë‹ˆë‹¤',
             'plugin_path_does_not_exist' => 'í”ŒëŸ¬ê·¸ì�¸ ê²½ë¡œ %sì�´(ê°€) ì¡´ìž¬í•˜ì§€ ì•ŠìŠµë‹ˆë‹¤!',
             'plugins_do_not_use_require' => 'ì„±ëŠ¥ìƒ�ì�˜ ì�´ìœ ë¡œ í”ŒëŸ¬ê·¸ì�¸ì�€ PSR-4 ì˜¤í† ë¡œë�”ë¥¼ í†µí•´ì„œë§Œ plugins í�´ë�”ì�˜ ë‹¤ë¥¸ íŒŒì�¼ì�„ ìž�ë�™ìœ¼ë¡œ include ë˜�ëŠ” requireí•  ìˆ˜ ìžˆìŠµë‹ˆë‹¤(ì§�ì ‘ require()í•  ìˆ˜ ì—†ìŠµë‹ˆë‹¤).',
             'plugins_must_have_exactly_one_class' => 'í”ŒëŸ¬ê·¸ì�¸ì�€ ì •í™•ížˆ í•˜ë‚˜ì�˜ í�´ëž˜ìŠ¤ë§Œ ì •ì�˜í•´ì•¼ í•©ë‹ˆë‹¤! ì—¬ëŸ¬ í�´ëž˜ìŠ¤, ì�¸í„°íŽ˜ì�´ìŠ¤ ë˜�ëŠ” íŠ¸ë ˆì�´íŠ¸ë¥¼ ì •ì�˜í•˜ë ¤ë©´ ë³„ë�„ì�˜ íŒŒì�¼ì�„ ë§Œë“œì‹­ì‹œì˜¤. MadelineProtoê°€ ìž�ë�™ìœ¼ë¡œ ì˜¤í† ë¡œë“œí•©ë‹ˆë‹¤.',
             'predicate_not_set' => 'ìˆ ì–´(_ ì•„ëž˜ì�˜ ê°’)ê°€ ì„¤ì •ë�˜ì§€ ì•Šì•˜ìŠµë‹ˆë‹¤!',
             'recommend_not_use_filesystem_function' => '%s í•¨ìˆ˜ëŠ” ì‚¬ìš©í•˜ì§€ ì•ŠëŠ” ê²ƒì�´ ì¢‹ìŠµë‹ˆë‹¤. ì—…ë�°ì�´íŠ¸ë¥¼ ì²˜ë¦¬í•˜ëŠ” ë�™ì•ˆ íŒŒì�¼ ì‹œìŠ¤í…œì—� ì ‘ê·¼í•˜ë©´ ë´‡ì�´ ëŠ�ë ¤ì§€ê¸° ë•Œë¬¸ìž…ë‹ˆë‹¤. ë´‡ì�„ ëŠ�ë¦¬ê²Œ í•˜ì§€ ì•ŠëŠ” ë�°ì�´í„° ì €ìž¥ ë°©ë²•ì�€ https://docs.madelineproto.xyz/docs/UPDATES.html#avoiding-the-use-of-filesystem-functions ì�„ ì°¸ì¡°í•˜ì‹­ì‹œì˜¤!',
             'rpc_tg_error' => 'Telegramì�´ RPC ì˜¤ë¥˜ë¥¼ ë°˜í™˜í–ˆìŠµë‹ˆë‹¤: %s (%s), ë°œìƒ� ìœ„ì¹˜ %s:%s%sTL ì¶”ì �:',
             'sec_peer_not_in_db' => 'ì�´ ë¹„ë°€ í”¼ì–´ëŠ” ë‚´ë¶€ í”¼ì–´ ë�°ì�´í„°ë² ì�´ìŠ¤ì—� ì—†ìŠµë‹ˆë‹¤',
             'secret_chat_skipping' => 'ë�°ì�´í„°ë² ì�´ìŠ¤ì—� ë¹„ë°€ ì±„íŒ… %sì�´(ê°€) ì—†ìœ¼ë¯€ë¡œ ë©”ì‹œì§€ë¥¼ ê±´ë„ˆëœ�ë‹ˆë‹¤...',
             'serialization_ofd' => 'ì§�ë ¬í™” í˜•ì‹�ì�´ ì˜¤ëž˜ë�˜ì–´ ê°�ì²´ë¥¼ ìž¬êµ¬ì„±í•˜ëŠ” ì¤‘ìž…ë‹ˆë‹¤!',
             'session_corrupted' => 'ì„¸ì…˜ì�´ ì†�ìƒ�ë�˜ì—ˆìŠµë‹ˆë‹¤!',
             'signing_up' => 'ì�¼ë°˜ ì‚¬ìš©ìž�ë¡œ ê°€ìž…í•˜ëŠ” ì¤‘...',
             'signupFirstName' => 'ì�´ë¦„ì�„ ìž…ë ¥í•˜ì‹­ì‹œì˜¤: ',
             'signupFirstNameWeb' => 'ì�´ë¦„',
             'signupLastName' => 'ì„±ì�„ ìž…ë ¥í•˜ì‹­ì‹œì˜¤(ë¹„ì›Œ ë‘˜ ìˆ˜ ìžˆì�Œ): ',
             'signupLastNameWeb' => 'ì„±',
             'signupLeadWeb' => 'ì�´ ì „í™”ë²ˆí˜¸ëŠ” ì•„ì§� Telegramì—� ê°€ìž…ë�˜ì–´ ìžˆì§€ ì•ŠìŠµë‹ˆë‹¤. ìƒˆ ê³„ì •ì�˜ ì�´ë¦„ì�„ ìž…ë ¥í•˜ì‹­ì‹œì˜¤.',
             'signupTitleWeb' => 'ê³„ì • ë§Œë“¤ê¸°',
             'signupWeb' => 'ê°€ìž…í•˜ì‹­ì‹œì˜¤',
             'signup_ok' => 'ê°€ìž…ë�˜ì—ˆìŠµë‹ˆë‹¤!',
             'signup_uncalled' => 'ê°€ìž…ì�„ ê¸°ë‹¤ë¦¬ê³  ìžˆì§€ ì•ŠìŠµë‹ˆë‹¤! ë¨¼ì € phoneLogin ë°� completePhoneLogin ë©”ì„œë“œë¥¼ í˜¸ì¶œí•˜ì‹­ì‹œì˜¤!',
             'src_file_invalid' => 'ìœ íš¨í•˜ì§€ ì•Šì�€ ì†ŒìŠ¤ íŒŒì�¼ì�´ ìž…ë ¥ë�˜ì—ˆìŠµë‹ˆë‹¤: ',
             'static_analysis_minor' => '%sì�˜ ì •ì � ë¶„ì„� ì¤‘ ê²½ë¯¸í•œ ë¬¸ì œê°€ ë°œê²¬ë�˜ì—ˆìŠµë‹ˆë‹¤: %s',
             'static_analysis_severe' => '%sì�˜ ì •ì � ë¶„ì„� ì¤‘ ì‹¬ê°�í•œ ë¬¸ì œê°€ ë°œê²¬ë�˜ì—ˆìŠµë‹ˆë‹¤: %s',
             'stream_handle_invalid' => 'ìœ íš¨í•˜ì§€ ì•Šì�€ ìŠ¤íŠ¸ë¦¼ í•¸ë“¤ì�´ ìž…ë ¥ë�˜ì—ˆìŠµë‹ˆë‹¤.',
             'string_required' => 'ë¬¸ìž�ì—´ì�´ í•„ìš”í•©ë‹ˆë‹¤!',
             'translate_madelineproto_cli' => 'MadelineProtoë¥¼ ì—¬ëŸ¬ë¶„ì�˜ ì–¸ì–´ë¡œ ë²ˆì—­í•  ìˆ˜ ìžˆìŠµë‹ˆë‹¤(í˜„ìž¬ ë²ˆì—­ ì§„í–‰ë¥ : %d%%). ë²ˆì—­ì—� ì°¸ì—¬í•˜ë ¤ë©´ https://github.com/danog/MadelineProto/tree/v8/langs ì—� ë°©ë¬¸í•˜ì‹­ì‹œì˜¤!',
             'translate_madelineproto_web' => 'MadelineProtoë¥¼ ì—¬ëŸ¬ë¶„ì�˜ ì–¸ì–´ë¡œ ë²ˆì—­í•  ìˆ˜ ìžˆìŠµë‹ˆë‹¤(í˜„ìž¬ ë²ˆì—­ ì§„í–‰ë¥ : %d%%). <a href="https://github.com/danog/MadelineProto/tree/v8/langs" target="_blank">ì—¬ê¸°ë¥¼ í�´ë¦­í•˜ì—¬ ë²ˆì—­ì—� ì°¸ì—¬í•˜ì‹­ì‹œì˜¤!</a>',
             'tweb.ACCESS_TOKEN_EXPIRED' => 'ì�´ ë´‡ í† í�°ì�€ í��ê¸°ë�˜ì—ˆìŠµë‹ˆë‹¤',
             'tweb.ACCESS_TOKEN_INVALID' => 'ìž˜ëª»ë�œ ë´‡ í† í�°',
             'tweb.AccDescr.LoginCode' => 'ë¡œê·¸ì�¸ ì½”ë“œ',
             'tweb.Close' => 'ë‹«ê¸°',
             'tweb.Code' => 'ì½”ë“œ',
             'tweb.Contacts.PhoneNumber.Placeholder' => 'ì „í™”ë²ˆí˜¸',
             'tweb.Country' => 'êµ­ê°€',
             'tweb.DarkMode' => 'ë‹¤í�¬ ëª¨ë“œ',
             'tweb.Error.AnError' => 'ì˜¤ë¥˜ê°€ ë°œìƒ�í–ˆìŠµë‹ˆë‹¤. ë‚˜ì¤‘ì—� ë‹¤ì‹œ ì‹œë�„í•˜ì‹­ì‹œì˜¤.',
             'tweb.FIRSTNAME_INVALID' => 'ìž˜ëª»ë�œ ì�´ë¦„',
             'tweb.FirstName' => 'ì�´ë¦„(í•„ìˆ˜)',
             'tweb.LASTNAME_INVALID' => 'ìž˜ëª»ë�œ ì„±',
             'tweb.LastName' => 'ì„±(ì„ íƒ� ì‚¬í•­)',
             'tweb.Login.Bot' => 'ë´‡ìœ¼ë¡œ ë¡œê·¸ì�¸ >',
             'tweb.Login.Bot.Cancel' => 'ì‚¬ìš©ìž�ë¡œ ë¡œê·¸ì�¸ >',
             'tweb.Login.Bot.Subtitle' => 'ë´‡ì�„ ë§Œë“¤ ë•Œ @BotFatherê°€ ì•Œë ¤ ì¤€
í† í�°ì�„ ìž…ë ¥í•˜ì‹­ì‹œì˜¤.',
             'tweb.Login.Bot.Title' => 'ë´‡ìœ¼ë¡œ ë¡œê·¸ì�¸',
             'tweb.Login.Bot.Token' => 'ë´‡ í† í�°',
             'tweb.Login.Code.SecretPhrase' => 'ë¹„ë°€ ë¬¸êµ¬',
             'tweb.Login.Code.SecretWord' => 'ë¹„ë°€ ë‹¨ì–´',
             'tweb.Login.Code.SentCall' => 'ì „í™”ë¡œ ì½”ë“œë¥¼
ë¶ˆëŸ¬ ë“œë¦½ë‹ˆë‹¤.',
             'tweb.Login.Code.SentEmail' => 'ì�´ë©”ì�¼ **%s**ì�„(ë¥¼) í™•ì�¸í•˜ê³ (ìŠ¤íŒ¸í•¨ë�„ í™•ì�¸í•˜ì‹­ì‹œì˜¤) ë°©ê¸ˆ ë³´ë‚¸ ì½”ë“œë¥¼ ìž…ë ¥í•˜ì‹­ì‹œì˜¤.',
             'tweb.Login.Code.SentInApp' => 'ì½”ë“œê°€ ë‹´ê¸´ ë©”ì‹œì§€ë¥¼
Telegramìœ¼ë¡œ ë³´ëƒˆìŠµë‹ˆë‹¤.',
             'tweb.Login.Code.SentMissedCall' => 'ëª‡ ì´ˆ ì•ˆì—� **%s**ì—�ì„œ
ì§§ì�€ ì „í™”ê°€ ê±¸ë ¤ ì˜µë‹ˆë‹¤. ë°›ì§€ ë§�ê³ 
ê·¸ ë²ˆí˜¸ì�˜ ë§ˆì§€ë§‰ ìˆ«ìž�ë¥¼ ìž…ë ¥í•˜ì‹­ì‹œì˜¤.',
             'tweb.Login.Code.SentSms' => 'ì½”ë“œê°€ ë‹´ê¸´ SMSë¥¼
ë³´ëƒˆìŠµë‹ˆë‹¤.',
             'tweb.Login.Code.SentSmsPhrase' => 'ì „í™”ë²ˆí˜¸ **%s**(ìœ¼)ë¡œ
ë¹„ë°€ ë¬¸êµ¬ê°€ ë‹´ê¸´ SMSë¥¼ ë³´ëƒˆìŠµë‹ˆë‹¤.',
             'tweb.Login.Code.SentSmsPhraseBeginning' => 'ì „í™”ë²ˆí˜¸ **%1$s**(ìœ¼)ë¡œ "**%2$s**"(ìœ¼)ë¡œ ì‹œìž‘í•˜ëŠ”
ë¹„ë°€ ë¬¸êµ¬ê°€ ë‹´ê¸´ SMSë¥¼ ë³´ëƒˆìŠµë‹ˆë‹¤.',
             'tweb.Login.Code.SentSmsWord' => 'ì „í™”ë²ˆí˜¸ **%s**(ìœ¼)ë¡œ
ë¹„ë°€ ë‹¨ì–´ê°€ ë‹´ê¸´ SMSë¥¼ ë³´ëƒˆìŠµë‹ˆë‹¤.',
             'tweb.Login.Code.SentSmsWordBeginning' => 'ì „í™”ë²ˆí˜¸ **%1$s**(ìœ¼)ë¡œ "**%2$s**"(ìœ¼)ë¡œ ì‹œìž‘í•˜ëŠ”
ë¹„ë°€ ë‹¨ì–´ê°€ ë‹´ê¸´ SMSë¥¼ ë³´ëƒˆìŠµë‹ˆë‹¤.',
             'tweb.Login.Code.SentUnknown' => 'ëª¨ë“  ê³³ì—�ì„œ ì½”ë“œë¥¼
í™•ì�¸í•˜ì‹­ì‹œì˜¤(ìœ í˜•: %s)',
             'tweb.Login.Code.WordBeginningInvalid' => 'ìž˜ëª»ë�œ ë‹¨ì–´',
             'tweb.Login.Code.WordInvalid' => 'ì˜¬ë°”ë¥´ì§€ ì•ŠìŠµë‹ˆë‹¤. ë‹¤ì‹œ ì‹œë�„í•˜ì‹­ì‹œì˜¤',
             'tweb.Login.Next' => 'ë‹¤ì�Œ',
             'tweb.Login.Password.Subtitle' => 'ì�´ ê³„ì •ì�€ ì¶”ê°€ ë¹„ë°€ë²ˆí˜¸ë¡œ
ë³´í˜¸ë�˜ì–´ ìžˆìŠµë‹ˆë‹¤',
             'tweb.Login.Password.Title' => 'ë¹„ë°€ë²ˆí˜¸ ìž…ë ¥',
             'tweb.Login.PhoneLabelInvalid' => 'ìž˜ëª»ë�œ ì „í™”ë²ˆí˜¸',
             'tweb.Login.QR.Cancel' => 'ì „í™”ë²ˆí˜¸ë¡œ ë¡œê·¸ì�¸ >',
             'tweb.Login.QR.Help1' => 'íœ´ëŒ€í�°ì—�ì„œ Telegramì�„ ì—½ë‹ˆë‹¤',
             'tweb.Login.QR.Help2' => '**ì„¤ì •** > **ê¸°ê¸°** > **ê¸°ê¸° ì¶”ê°€**ë¡œ ì�´ë�™í•©ë‹ˆë‹¤',
             'tweb.Login.QR.Help3' => 'íœ´ëŒ€í�°ì�„ ì�´ í™”ë©´ì—� ë¹„ì¶”ì–´ ë¡œê·¸ì�¸ì�„ í™•ì�¸í•©ë‹ˆë‹¤',
             'tweb.Login.QR.Login' => 'QR ì½”ë“œë¡œ ë¡œê·¸ì�¸ >',
             'tweb.Login.QR.Subtitle' => 'íœ´ëŒ€í�°ì�˜ Telegram ì•±ìœ¼ë¡œ ìŠ¤ìº”í•˜ì‹­ì‹œì˜¤',
             'tweb.Login.QR.Title' => 'QR ì½”ë“œë¡œ ë¡œê·¸ì�¸',
             'tweb.Login.Register.Subtitle' => 'ê³„ì •ì�„ ë§Œë“¤ë ¤ë©´
ì�´ë¦„ì�„ ìž…ë ¥í•˜ì‹­ì‹œì˜¤',
             'tweb.Login.StartText' => 'êµ­ê°€ ì½”ë“œë¥¼ í™•ì�¸í•˜ê³ 
ì „í™”ë²ˆí˜¸ë¥¼ ìž…ë ¥í•˜ì‹­ì‹œì˜¤.',
             'tweb.Login.Title' => 'MadelineProtoì—� ë¡œê·¸ì�¸',
             'tweb.LoginPassword' => 'ë¹„ë°€ë²ˆí˜¸',
             'tweb.Optional' => 'ì„ íƒ� ì‚¬í•­',
             'tweb.PASSWORD_HASH_INVALID' => 'ë¹„ë°€ë²ˆí˜¸ê°€ ì˜¬ë°”ë¥´ì§€ ì•ŠìŠµë‹ˆë‹¤',
             'tweb.PHONE_CODE_EMPTY' => 'ìž˜ëª»ë�œ ì½”ë“œ',
             'tweb.PHONE_CODE_EXPIRED' => 'ì½”ë“œê°€ ë§Œë£Œë�˜ì—ˆìŠµë‹ˆë‹¤',
             'tweb.PHONE_CODE_INVALID' => 'ìž˜ëª»ë�œ ì½”ë“œ',
             'tweb.PHONE_NUMBER_BANNED' => 'ì�´ ì „í™”ë²ˆí˜¸ëŠ” ì°¨ë‹¨ë�˜ì—ˆìŠµë‹ˆë‹¤',
             'tweb.PHONE_NUMBER_FLOOD' => 'ì‹œë�„ íšŸìˆ˜ê°€ ë„ˆë¬´ ë§ŽìŠµë‹ˆë‹¤. ë‚˜ì¤‘ì—� ë‹¤ì‹œ ì‹œë�„í•˜ì‹­ì‹œì˜¤',
             'tweb.PHONE_NUMBER_INVALID' => 'ìž˜ëª»ë�œ ì „í™”ë²ˆí˜¸',
             'tweb.PHONE_PASSWORD_FLOOD' => 'ì‹œë�„ íšŸìˆ˜ê°€ ë„ˆë¬´ ë§ŽìŠµë‹ˆë‹¤. ë‚˜ì¤‘ì—� ë‹¤ì‹œ ì‹œë�„í•˜ì‹­ì‹œì˜¤',
             'tweb.PhoneNumber.Code.Fragment.Info' => '[Fragment]()ì�˜ Numbers ì„¹ì…˜ì—�ì„œ ì½”ë“œë¥¼ ë°›ìœ¼ì‹­ì‹œì˜¤',
             'tweb.PleaseWait' => 'ìž ì‹œ ê¸°ë‹¤ë ¤ ì£¼ì‹­ì‹œì˜¤...',
             'tweb.StartMessaging' => 'ë©”ì‹œì§€ ì‹œìž‘í•˜ê¸°',
             'tweb.YourName' => 'ì�´ë¦„',
             'type_extract_error' => 'íƒ€ìž… "%s"ì�„(ë¥¼) ì¶”ì¶œí•  ìˆ˜ ì—†ìŠµë‹ˆë‹¤. MadelineProtoë¥¼ ì—…ë�°ì�´íŠ¸í•˜ì‹­ì‹œì˜¤!',
             'type_extract_error_id' => 'íƒ€ìž… %s(ID %s)ì�„(ë¥¼) ì¶”ì¶œí•  ìˆ˜ ì—†ìŠµë‹ˆë‹¤. MadelineProtoë¥¼ ì—…ë�°ì�´íŠ¸í•˜ì‹­ì‹œì˜¤!',
             'update_madelineproto' => 'ì˜¤ëž˜ë�œ ë²„ì „ì�˜ MadelineProtoë¥¼ ì‹¤í–‰ ì¤‘ì�´ë¯€ë¡œ ì—…ë�°ì�´íŠ¸ê°€ í•„ìš”í•©ë‹ˆë‹¤. í˜„ìž¬ ì‹¤í–‰ ì¤‘ì�¸ ë²„ì „ì�€ %sì�´ì§€ë§Œ, ì—¬ëŸ¬ ë²„ê·¸ ìˆ˜ì •ê³¼ ìƒˆ ê¸°ëŠ¥ì�´ í�¬í•¨ë�œ ìµœì‹  ë²„ì „ì�€ %sìž…ë‹ˆë‹¤!',
             'value_bigger_than_2147483647' => 'ìž…ë ¥í•œ ê°’ %sì�´(ê°€) 2147483647ë³´ë‹¤ í�½ë‹ˆë‹¤',
             'value_bigger_than_4294967296' => 'ìž…ë ¥í•œ ê°’ %sì�´(ê°€) 4294967296ë³´ë‹¤ í�½ë‹ˆë‹¤',
             'value_bigger_than_9223372036854775807' => 'ìž…ë ¥í•œ ê°’ %sì�´(ê°€) 9223372036854775807ë³´ë‹¤ í�½ë‹ˆë‹¤',
             'value_smaller_than_0' => 'ìž…ë ¥í•œ ê°’ %sì�´(ê°€) 0ë³´ë‹¤ ìž‘ìŠµë‹ˆë‹¤',
             'value_smaller_than_2147483648' => 'ìž…ë ¥í•œ ê°’ %sì�´(ê°€) -2147483648ë³´ë‹¤ ìž‘ìŠµë‹ˆë‹¤',
             'value_smaller_than_9223372036854775808' => 'ìž…ë ¥í•œ ê°’ %sì�´(ê°€) -9223372036854775808ë³´ë‹¤ ìž‘ìŠµë‹ˆë‹¤',
             'waveform_must_have_100_values' => 'íŒŒí˜• ë°°ì—´ì—�ëŠ” 100ê°œì�˜ ê°’ì�´ ìžˆì–´ì•¼ í•©ë‹ˆë‹¤!',
             'waveform_value' => 'íŒŒí˜• ê°’ì�€ 0ì—�ì„œ 31 ì‚¬ì�´ì—¬ì•¼ í•©ë‹ˆë‹¤!',
             'webContinue' => 'ê³„ì†�',
             'webDocs' => 'ë¬¸ì„œ',
             'webDoneLead' => 'MadelineProtoê°€ ì„œë²„ì—�ì„œ ê³„ì†� ì‹¤í–‰ ì¤‘ìž…ë‹ˆë‹¤. ì�´ íŽ˜ì�´ì§€ë¥¼ ë‹«ì•„ë�„ ë�©ë‹ˆë‹¤.',
             'webError' => '%s. í™•ì�¸ í›„ ë‹¤ì‹œ ì‹œë�„í•˜ì‹­ì‹œì˜¤.',
             'webFieldRequired' => 'ì�´ í•­ëª©ì�„ ìž…ë ¥í•˜ì‹­ì‹œì˜¤.',
             'webHidePassword' => 'ìˆ¨ê¸°ê¸°',
             'webLogin' => 'ë¡œê·¸ì�¸',
             'webOptional' => 'ì„ íƒ� ì‚¬í•­',
             'webPhoneInvalid' => 'êµ­ê°€ ì½”ë“œë¥¼ í�¬í•¨í•œ ì „í™”ë²ˆí˜¸ë¥¼ ìž…ë ¥í•˜ì‹­ì‹œì˜¤(ì˜ˆ: +39 123 456 7890).',
             'webProgress' => 'ì„¤ì • ì§„í–‰ ìƒ�í™©',
             'webQrTitle' => 'QR ì½”ë“œë¡œ ë¡œê·¸ì�¸',
             'webQrWaiting' => 'ìŠ¤ìº”ì�„ ê¸°ë‹¤ë¦¬ëŠ” ì¤‘â€¦',
             'webShowPassword' => 'í‘œì‹œ',
             'webStepAccount' => 'ê³„ì •',
             'webStepApp' => 'API ID ë°� hash',
             'webStepDone' => 'ì‹¤í–‰ ì¤‘',
             'webStepVerify' => 'ì�¸ì¦�',
         ],
        'pl' =>
         [
             '2fa_uncalled' => 'Nie oczekujÄ™ na hasÅ‚o! Najpierw wywoÅ‚aj metody phoneLogin i completePhoneLogin!',
             'accepting_call' => 'Odbieranie poÅ‚Ä…czenia od %s...',
             'account_banned' => '!!!!!!! OSTRZEÅ»ENIE !!!!!!!
System ochrony przed floodem Telegrama zawiesiÅ‚ to konto.
Aby kontynuowaÄ‡, wymagana jest rÄ™czna weryfikacja.
WyÅ›lij e-mail na adres recover@telegram.org z proÅ›bÄ… o odblokowanie numeru telefonu %s i krÃ³tko opisz, do czego bÄ™dzie uÅ¼ywany ten numer.
NastÄ™pnie zaloguj siÄ™ ponownie.
JeÅ›li konto zostaÅ‚o celowo usuniÄ™te, zignoruj tÄ™ wiadomoÅ›Ä‡.',
             'already_loggedIn' => 'Ta instancja MadelineProto jest juÅ¼ zalogowana!',
             'apiAppDescLabelWeb' => 'Opis',
             'apiAppInstructionsAuto0' => 'WprowadÅº nazwÄ™ aplikacji (dowolnÄ…): ',
             'apiAppInstructionsAuto1' => 'WprowadÅº krÃ³tkÄ… nazwÄ™ aplikacji (alfanumerycznÄ…, 5-32 znaki): ',
             'apiAppInstructionsAuto2' => 'WprowadÅº adres URL aplikacji/strony internetowej lub t.me/yourusername: ',
             'apiAppInstructionsAuto3' => 'WprowadÅº platformÄ™ aplikacji: ',
             'apiAppInstructionsAuto4' => 'Opisz swojÄ… aplikacjÄ™: ',
             'apiAppInstructionsAutoTypeOther' => 'Inna (okreÅ›l w opisie)',
             'apiAppInstructionsManual0' => 'nazwa aplikacji, dowolna',
             'apiAppInstructionsManual1' => 'krÃ³tka nazwa aplikacji, alfanumeryczna, 5-32 znaki',
             'apiAppInstructionsManual2' => 'adres URL aplikacji/strony internetowej lub t.me/yourusername',
             'apiAppInstructionsManual3' => 'cokolwiek',
             'apiAppInstructionsManual4' => 'Opisz tutaj swojÄ… aplikacjÄ™',
             'apiAppShortNameLabelWeb' => 'KrÃ³tka nazwa',
             'apiAppTitleLabelWeb' => 'Nazwa aplikacji',
             'apiAppUrlLabelWeb' => 'URL',
             'apiAppWeb' => 'WprowadÅº informacje o API',
             'apiAutoCodeLabelWeb' => 'Kod logowania',
             'apiAutoCodeLeadWeb' => 'my.telegram.org wysÅ‚aÅ‚ kod logowania do Twojej aplikacji Telegram, na czacie z Telegramem (nie SMS-em).',
             'apiAutoCodeTitleWeb' => 'WprowadÅº kod z Telegrama',
             'apiAutoErrorTitle' => 'my.telegram.org zwrÃ³ciÅ‚ bÅ‚Ä…d',
             'apiAutoFallbackManual' => 'Zamiast tego wprowadÅº API ID i API hash rÄ™cznie.',
             'apiAutoPhoneHintWeb' => 'my.telegram.org wysyÅ‚a kod logowania do aplikacji Telegram, a nie SMS-em.',
             'apiAutoPrompt0' => 'WprowadÅº numer telefonu, ktÃ³ry jest juÅ¼ zarejestrowany w Telegramie: ',
             'apiAutoPrompt1' => 'WprowadÅº kod weryfikacyjny otrzymany w Telegramie: ',
             'apiAutoWeb' => 'WprowadÅº numer telefonu, ktÃ³ry jest <b>juÅ¼ zarejestrowany</b> w Telegramie, aby uzyskaÄ‡ API ID',
             'apiChooseAutoWeb' => 'Pobierz automatycznie',
             'apiChooseManualAuto' => 'Czy chcesz pobraÄ‡ API ID i API hash automatycznie z my.telegram.org (a), czy wprowadziÄ‡ je rÄ™cznie (m)?',
             'apiChooseManualAutoTip' => 'PamiÄ™taj, Å¼e API ID/hash moÅ¼esz teÅ¼ podaÄ‡ bezpoÅ›rednio w kodzie za pomocÄ… ustawieÅ„: %s',
             'apiChooseManualAutoTipWeb' => 'PamiÄ™taj, Å¼e API ID/hash moÅ¼esz teÅ¼ podaÄ‡ bezpoÅ›rednio w kodzie za pomocÄ… <a target="_blank" href="%s">ustawieÅ„</a>.',
             'apiChooseManualWeb' => 'Mam juÅ¼ API ID',
             'apiChoosePrompt' => 'TwÃ³j wybÃ³r (m/a): ',
             'apiCreateErrorTitle' => 'my.telegram.org nie utworzyÅ‚ aplikacji',
             'apiCreateLeadWeb' => 'To konto nie ma jeszcze aplikacji na my.telegram.org. Te dane widzisz tylko Ty i moÅ¼esz je pÃ³Åºniej zmieniÄ‡.',
             'apiCreateTitleWeb' => 'UtwÃ³rz aplikacjÄ™',
             'apiCreateVpsWarning' => 'Telegram nie pozwala na tworzenie nowych aplikacji na my.telegram.org z adresÃ³w IP serwerÃ³w VPS, sieci VPN i innych centrÃ³w danych, a Å¼Ä…danie zostaÅ‚o wysÅ‚ane z adresu IP maszyny, na ktÃ³rej dziaÅ‚a MadelineProto. UtwÃ³rz aplikacjÄ™ rÄ™cznie na https://my.telegram.org z poÅ‚Ä…czenia domowego (np. z telefonu przez dane komÃ³rkowe), a nastÄ™pnie wprowadÅº API ID i API hash.',
             'apiCreateWeb' => 'UtwÃ³rz aplikacjÄ™',
             'apiError' => 'BÅ�Ä„D: %s. SprÃ³buj ponownie.',
             'apiInvalidWeb' => 'API ID to liczba, a API hash ma 32 znaki: skopiuj je ponownie z my.telegram.org.',
             'apiManualInstructions0' => 'Zaloguj siÄ™ na https://my.telegram.org',
             'apiManualInstructions1' => 'PrzejdÅº do â€žAPI development toolsâ€�',
             'apiManualInstructions2' => 'Kliknij â€žCreate applicationâ€�',
             'apiManualPrompt0' => 'WprowadÅº swoje API ID: ',
             'apiManualPrompt1' => 'WprowadÅº swÃ³j API hash: ',
             'apiManualWeb' => 'WprowadÅº swoje API ID i API hash',
             'apiParamsError' => 'Nie podano wszystkich wymaganych parametrÃ³w!',
             'apiSaveWeb' => 'Zapisz i kontynuuj',
             'apiSendCodeWeb' => 'WyÅ›lij kod',
             'apiSetupLeadWeb' => 'KaÅ¼da aplikacja Telegrama potrzebuje API ID i API hash. MadelineProto moÅ¼e pobraÄ‡ je za Ciebie z my.telegram.org, uÅ¼ywajÄ…c numeru telefonu istniejÄ…cego konta Telegram.',
             'apiSetupTitleWeb' => 'Uzyskaj API ID i API hash',
             'apiStartOverWeb' => 'Zacznij od nowa',
             'api_not_set' => 'Musisz podaÄ‡ klucz API i API ID, uzyskaj wÅ‚asne na my.telegram.org',
             'array_invalid' => 'Nie podano prawidÅ‚owej tablicy',
             'baseDirLimitation' => 'Skonfigurowane jest ograniczenie katalogu bazowego: moÅ¼e to wpÅ‚ynÄ…Ä‡ na wydajnoÅ›Ä‡ i spowodowaÄ‡ pewne problemy, wyÅ‚Ä…cz je, jeÅ›li to moÅ¼liwe!',
             'bool_error' => 'Nie udaÅ‚o siÄ™ wyodrÄ™bniÄ‡ wartoÅ›ci logicznej',
             'botAlreadyRunning' => 'Bot jest juÅ¼ uruchomiony!',
             'botapi_conversion_error' => 'Nie moÅ¼na przekonwertowaÄ‡ %s na obiekt Bot API',
             'call_already_accepted' => 'PoÅ‚Ä…czenie %s zostaÅ‚o juÅ¼ odebrane',
             'call_already_declined' => 'PoÅ‚Ä…czenie %s zostaÅ‚o juÅ¼ odrzucone',
             'call_completing' => 'Finalizowanie poÅ‚Ä…czenia od %s...',
             'call_confirming' => 'Potwierdzanie poÅ‚Ä…czenia od %s...',
             'call_discarding' => 'KoÅ„czenie poÅ‚Ä…czenia %s...',
             'call_error_1' => 'Nie udaÅ‚o siÄ™ znaleÅºÄ‡ i odebraÄ‡ poÅ‚Ä…czenia %s',
             'call_error_2' => 'Nie udaÅ‚o siÄ™ znaleÅºÄ‡ i potwierdziÄ‡ poÅ‚Ä…czenia %s',
             'call_error_3' => 'Nie udaÅ‚o siÄ™ znaleÅºÄ‡ i sfinalizowaÄ‡ poÅ‚Ä…czenia %s',
             'cli_need_dl.php_link' => 'Podaj adres URL skryptu pobierania, gdy uÅ¼ywasz getDownloadLink z poziomu CLI!',
             'constructor_not_found' => 'Nie znaleziono konstruktora dla typu: ',
             'could_not_connect_to_MadelineProto' => 'Nie udaÅ‚o siÄ™ poÅ‚Ä…czyÄ‡ z MadelineProto, aby to naprawiÄ‡, wÅ‚Ä…cz proc_open i usuÅ„ ograniczenia open_basedir lub wyÅ‚Ä…cz przepisywanie Å›cieÅ¼ek na serwerze WWW! JeÅ›li zostaÅ‚o to juÅ¼ zrobione, upewnij siÄ™, Å¼e wersja CLI PHP jest dokÅ‚adnie taka sama jak wersja webowa (ta sama wersja, rozszerzenia itd.), i sprawdÅº plik MadelineProto.log, aby uzyskaÄ‡ wiÄ™cej informacji o bÅ‚Ä™dzie, ktÃ³ry uniemoÅ¼liwiÅ‚ uruchomienie serwera IPC.',
             'could_not_convert_object' => 'Nie udaÅ‚o siÄ™ przekonwertowaÄ‡ obiektu typu %s',
             'deserialization_error' => 'WystÄ…piÅ‚ bÅ‚Ä…d podczas deserializacji',
             'dl.php_check_logs_make_sure_session_running' => 'PowiÄ…zany bot EventHandler MadelineProto lub serwer IPC MadelineProto jest offline, sprawdÅº logi i upewnij siÄ™, Å¼e co najmniej jeden z nich dziaÅ‚a!',
             'dl.php_powered_by_madelineproto' => 'Serwer pobierania plikÃ³w z Telegrama (do 4 GB), oparty na <a href="https://docs.madelineproto.xyz" target="_blank">MadelineProto</a>!<br>Kliknij <a href="https://docs.madelineproto.xyz/docs/FILES.html#getting-a-download-link" target="_blank">tutaj</a>, aby dowiedzieÄ‡ siÄ™, jak skonfigurowaÄ‡ wÅ‚asny serwer pobierania plikÃ³w z Telegrama!',
             'do_not_delete_MadelineProto.log' => 'pliku MadelineProto.log nigdy nie wolno usuwaÄ‡, zamiast tego ustaw w ustawieniach niestandardowy rozmiar maksymalny!',
             'do_not_remove_MadelineProto.log_phar' => 'Nie usuwaj madeline.phar ani madeline.php, w przeciwnym razie MadelineProto ulegnie awarii. JeÅ›li masz jakikolwiek problem z MadelineProto, zgÅ‚oÅ› go na https://github.com/danog/MadelineProto lub https://t.me/pwrtelegramgroup',
             'do_not_use_blocking_class' => 'ze wzglÄ™du na wydajnoÅ›Ä‡ handlery nie mogÄ… uÅ¼ywaÄ‡ nieasynchronicznej, blokujÄ…cej klasy %s, zamiast niej uÅ¼yj %s',
             'do_not_use_blocking_function' => 'ze wzglÄ™du na wydajnoÅ›Ä‡ event handlery nie mogÄ… uÅ¼ywaÄ‡ nieasynchronicznej, blokujÄ…cej funkcji %s, zamiast niej uÅ¼yj %s',
             'do_not_use_deprecated_function' => 'funkcja %s jest przestarzaÅ‚a, zamiast niej uÅ¼yj %s',
             'do_not_use_non_root_require_in_event_handler' => 'ze wzglÄ™du na wydajnoÅ›Ä‡ nie wolno uÅ¼ywaÄ‡ require ani include wewnÄ…trz klasy event handlera, dozwolone sÄ… tylko require na najwyÅ¼szym poziomie.',
             'do_not_use_yield' => 'MadelineProto 8 nie wymaga ani nie obsÅ‚uguje uÅ¼ywania yield w funkcjach asynchronicznych, musisz usunÄ…Ä‡ wszystkie sÅ‚owa kluczowe yield uÅ¼ywane wczeÅ›niej przy wywoÅ‚aniach funkcji asynchronicznych',
             'done' => 'Gotowe!',
             'encode_double_error' => 'Nie udaÅ‚o siÄ™ poprawnie zakodowaÄ‡ wartoÅ›ci double',
             'extensionRecommended' => 'OstrzeÅ¼enie: rozszerzenie %s nie jest zainstalowane, zainstaluj je, aby przyspieszyÄ‡ MadelineProto!',
             'extensionRequired' => 'MadelineProto wymaga do dziaÅ‚ania rozszerzenia %s. %s',
             'extensionRequiredInstallWithApt' => 'SprÃ³buj uruchomiÄ‡ sudo apt-get install %s.',
             'extensionRequiredInstallWithCustomInstructions' => 'Aby je zainstalowaÄ‡, postÄ™puj zgodnie z instrukcjami na %s.',
             'file_not_exist' => 'Plik nie istnieje',
             'file_parsing' => 'Parsowanie %s...',
             'file_type_invalid' => 'Wykryto nieprawidÅ‚owy typ pliku (%s)',
             'fingerprint_invalid' => 'NieprawidÅ‚owy odcisk klucza!',
             'go' => 'Dalej',
             'invalid_dl.php_session' => '%s nie jest prawidÅ‚owym skryptem pobierania, poniewaÅ¼ jego ID sesji jest inne (oczekiwano %s, otrzymano %s)',
             'ipc_tcp_warning' => 'UNIX sockets are not available, so the IPC server of the session listens on TCP (127.0.0.1) instead: any program running on this machine can connect to it, and control the session. On Windows 10 1803+, enable the sockets extension (extension=sockets in php.ini) to use UNIX sockets.',
             'length_too_big' => 'DÅ‚ugoÅ›Ä‡ jest zbyt duÅ¼a',
             'loginBot' => 'WprowadÅº token bota: ',
             'loginBotTokenHintWeb' => 'Token, ktÃ³ry otrzymaÅ‚eÅ› od @BotFather podczas tworzenia bota.',
             'loginBotTokenWeb' => 'Token bota',
             'loginChoosePromptWeb' => 'Czy chcesz zalogowaÄ‡ siÄ™ jako uÅ¼ytkownik, czy jako bot?',
             'loginCodeLeadWeb' => 'Telegram wysÅ‚aÅ‚ kod logowania do Twojej aplikacji Telegram lub SMS-em, jeÅ›li nie jesteÅ› zalogowany nigdzie indziej.',
             'loginCodeTitleWeb' => 'WprowadÅº kod logowania',
             'loginLeadWeb' => 'Wybierz konto lub bota, jako ktÃ³ry bÄ™dzie dziaÅ‚aÄ‡ ta instancja MadelineProto.',
             'loginManual' => 'MoÅ¼esz teÅ¼ wprowadziÄ‡ token bota lub numer telefonu, aby zalogowaÄ‡ siÄ™ rÄ™cznie: ',
             'loginNoCode' => 'Nie podano kodu weryfikacyjnego!',
             'loginNoName' => 'Nie podano imienia!',
             'loginNoPass' => 'Nie podano hasÅ‚a!',
             'loginOptionBot' => 'Bot',
             'loginOptionUser' => 'UÅ¼ytkownik',
             'loginPasswordLabelWeb' => 'HasÅ‚o',
             'loginPasswordLeadWeb' => 'To konto jest chronione hasÅ‚em ustawionym w Ustawienia > PrywatnoÅ›Ä‡ i bezpieczeÅ„stwo > Weryfikacja dwuetapowa.',
             'loginPasswordTitleWeb' => 'Weryfikacja dwuetapowa',
             'loginQr' => 'OtwÃ³rz Telegram na telefonie, przejdÅº do Ustawienia > UrzÄ…dzenia > PoÅ‚Ä…cz urzÄ…dzenie desktopowe i zeskanuj powyÅ¼szy kod QR, aby zalogowaÄ‡ siÄ™ automatycznie.',
             'loginQrCodeExpired' => 'Kod QR wygasÅ‚, generowanie nowego...',
             'loginQrCodeSuccessful' => 'Logowanie kodem QR powiodÅ‚o siÄ™!',
             'loginTitleWeb' => 'Zaloguj siÄ™ do Telegrama',
             'loginTypeWeb' => 'Zaloguj jako',
             'loginUser' => 'WprowadÅº swÃ³j numer telefonu: ',
             'loginUserCode' => 'WprowadÅº kod: ',
             'loginUserPass' => 'WprowadÅº hasÅ‚o (podpowiedÅº: %s): ',
             'loginUserPassHint' => 'PodpowiedÅº: %s',
             'loginUserPassWeb' => 'WprowadÅº hasÅ‚o: ',
             'loginUserPhoneCodeWeb' => 'Kod',
             'loginUserPhoneHintWeb' => 'Z numerem kierunkowym kraju, np. +39 123 456 7890.',
             'loginUserPhoneWeb' => 'Numer telefonu',
             'loginWebQr' => 'MoÅ¼esz teÅ¼ zalogowaÄ‡ siÄ™ automatycznie: otwÃ³rz Telegram na telefonie, przejdÅº do Ustawienia > UrzÄ…dzenia > PoÅ‚Ä…cz urzÄ…dzenie desktopowe i zeskanuj poniÅ¼szy kod QR:',
             'loginWebQr1' => 'OtwÃ³rz Telegram na telefonie',
             'loginWebQr2' => 'PrzejdÅº do Ustawienia > UrzÄ…dzenia > PoÅ‚Ä…cz urzÄ…dzenie desktopowe',
             'loginWebQr3' => 'Skieruj telefon na ten ekran, aby potwierdziÄ‡ logowanie',
             'login_2fa_enabled' => 'WÅ‚Ä…czono 2FA, musisz wywoÅ‚aÄ‡ funkcjÄ™ complete2falogin...',
             'login_auth_key' => 'Logowanie za pomocÄ… klucza autoryzacji...',
             'login_bot' => 'Logowanie jako bot...',
             'login_code_sending' => 'WysyÅ‚anie kodu...',
             'login_code_sent' => 'Kod zostaÅ‚ wysÅ‚any! Po otrzymaniu kodu uÅ¼yj funkcji completePhoneLogin.',
             'login_code_uncalled' => 'Nie oczekujÄ™ na kod! Najpierw wywoÅ‚aj metodÄ™ phoneLogin',
             'login_need_signup' => 'Dla tego numeru nie utworzono konta, musisz wywoÅ‚aÄ‡ funkcjÄ™ completeSignup...',
             'login_ok' => 'Zalogowano pomyÅ›lnie!',
             'login_user' => 'Logowanie jako zwykÅ‚y uÅ¼ytkownik...',
             'logout_ok' => 'Wylogowano pomyÅ›lnie!',
             'long_not_16' => 'Podana wartoÅ›Ä‡ nie ma dÅ‚ugoÅ›ci 16 bajtÃ³w',
             'long_not_32' => 'Podana wartoÅ›Ä‡ nie ma dÅ‚ugoÅ›ci 32 bajtÃ³w',
             'long_not_64' => 'Podana wartoÅ›Ä‡ nie ma dÅ‚ugoÅ›ci 64 bajtÃ³w',
             'madelineproto_ready' => 'MadelineProto jest gotowe!',
             'manualAdminActionRequired' => '!!!!!!!!! WYMAGANA RÄ˜CZNA INTERWENCJA ADMINISTRATORA SYSTEMU !!!!!!!!!',
             'method_not_found' => 'Nie znaleziono metody: ',
             'mmapErrorPart1' => 'OsiÄ…gniÄ™to maksymalnÄ… liczbÄ™ regionÃ³w pamiÄ™ci mapowanej (mmap) (%s): aby to naprawiÄ‡, zwiÄ™ksz parametr jÄ…dra vm.max_map_count do 262144.',
             'mmapErrorPart2' => 'Aby to naprawiÄ‡, uruchom jako root nastÄ™pujÄ…ce polecenie: %s',
             'mmapErrorPart3' => 'Aby zmiana zostaÅ‚a zachowana po ponownym uruchomieniu: %s',
             'mmapErrorPart4' => 'W systemie Windows i WSL moÅ¼e pomÃ³c zwiÄ™kszenie rozmiaru pliku stronicowania; jeÅ›li problem nie ustÄ…pi, przejdÅº na natywny system Linux.',
             'must_have_declare_types' => 'ze wzglÄ™du na wydajnoÅ›Ä‡ pierwszÄ… instrukcjÄ… pliku event handlera musi byÄ‡ "declare(strict_types=1);"',
             'nearest_dc' => 'JesteÅ›my w %s, najbliÅ¼sze DC to %d.',
             'need_dl.php' => 'Nie udaÅ‚o siÄ™ wygenerowaÄ‡ domyÅ›lnego skryptu pobierania (%s), utwÃ³rz plik dl.php o nastÄ™pujÄ…cej zawartoÅ›ci: %s i przekaÅ¼ jego adres URL jako drugi parametr getDownloadLink',
             'noReportPeers' => 'OstrzeÅ¼enie: nie ustawiono odbiorcÃ³w raportÃ³w (report peers), dodaj nastÄ™pujÄ…cÄ… metodÄ™ do swojego event handlera',
             'non_text_conversion' => 'Konwersja wiadomoÅ›ci innych niÅ¼ tekstowe nie jest jeszcze obsÅ‚ugiwana!',
             'not_loggedIn' => 'Nie jestem zalogowany!',
             'not_numeric' => 'Podana wartoÅ›Ä‡ nie jest liczbÄ…',
             'params_missing' => 'Brak wymaganego parametru',
             'peer_not_in_db' => 'Tego peera nie ma w wewnÄ™trznej bazie danych peerÃ³w',
             'plugin_path_does_not_exist' => 'ÅšcieÅ¼ka pluginu %s nie istnieje!',
             'plugins_do_not_use_require' => 'ze wzglÄ™du na wydajnoÅ›Ä‡ pluginy mogÄ… automatycznie doÅ‚Ä…czaÄ‡ inne pliki z folderu pluginÃ³w wyÅ‚Ä…cznie za poÅ›rednictwem autoloadera PSR-4 (a nie przez rÄ™czne require()).',
             'plugins_must_have_exactly_one_class' => 'plugin musi definiowaÄ‡ dokÅ‚adnie jednÄ… klasÄ™! Aby zdefiniowaÄ‡ wiele klas, interfejsÃ³w lub traitÃ³w, utwÃ³rz osobne pliki, MadelineProto zaÅ‚aduje je automatycznie.',
             'predicate_not_set' => 'Predykat (wartoÅ›Ä‡ pod _) nie zostaÅ‚ ustawiony!',
             'recommend_not_use_filesystem_function' => 'uÅ¼ywanie funkcji %s nie jest zalecane, poniewaÅ¼ dostÄ™p do systemu plikÃ³w podczas obsÅ‚ugi aktualizacji spowolni bota; na stronie https://docs.madelineproto.xyz/docs/UPDATES.html#avoiding-the-use-of-filesystem-functions znajdziesz listÄ™ alternatywnych sposobÃ³w przechowywania danych, ktÃ³re nie spowalniajÄ… bota!',
             'rpc_tg_error' => 'Telegram zwrÃ³ciÅ‚ bÅ‚Ä…d RPC: %s (%s), spowodowany przez %s:%s%sÅšlad TL:',
             'sec_peer_not_in_db' => 'Tego tajnego peera nie ma w wewnÄ™trznej bazie danych peerÃ³w',
             'secret_chat_skipping' => 'Nie mam tajnego czatu %s w bazie danych, pomijanie wiadomoÅ›ci...',
             'serialization_ofd' => 'Serializacja jest nieaktualna, odtwarzanie obiektu!',
             'session_corrupted' => 'Sesja jest uszkodzona!',
             'signing_up' => 'Rejestracja jako zwykÅ‚y uÅ¼ytkownik...',
             'signupFirstName' => 'WprowadÅº imiÄ™: ',
             'signupFirstNameWeb' => 'ImiÄ™',
             'signupLastName' => 'WprowadÅº nazwisko (moÅ¼e byÄ‡ puste): ',
             'signupLastNameWeb' => 'Nazwisko',
             'signupLeadWeb' => 'Tego numeru telefonu nie ma jeszcze w Telegramie: wybierz nazwÄ™ nowego konta.',
             'signupTitleWeb' => 'UtwÃ³rz konto',
             'signupWeb' => 'Zarejestruj siÄ™',
             'signup_ok' => 'Zarejestrowano pomyÅ›lnie!',
             'signup_uncalled' => 'Nie oczekujÄ™ na rejestracjÄ™! Najpierw wywoÅ‚aj metody phoneLogin i completePhoneLogin!',
             'src_file_invalid' => 'Podano nieprawidÅ‚owy plik ÅºrÃ³dÅ‚owy: ',
             'static_analysis_minor' => 'Podczas analizy statycznej %s napotkano drobny problem: %s',
             'static_analysis_severe' => 'Podczas analizy statycznej %s napotkano powaÅ¼ny problem: %s',
             'stream_handle_invalid' => 'Podano nieprawidÅ‚owy uchwyt strumienia.',
             'string_required' => 'Oczekiwano ciÄ…gu znakÃ³w!',
             'translate_madelineproto_cli' => 'MadelineProto moÅ¼na przetÅ‚umaczyÄ‡ na TwÃ³j jÄ™zyk (obecny postÄ™p tÅ‚umaczenia: %d%%), przejdÅº na https://github.com/danog/MadelineProto/tree/v8/langs, aby pomÃ³c w tÅ‚umaczeniu!',
             'translate_madelineproto_web' => 'MadelineProto moÅ¼na przetÅ‚umaczyÄ‡ na TwÃ³j jÄ™zyk (obecny postÄ™p tÅ‚umaczenia: %d%%), kliknij <a href="https://github.com/danog/MadelineProto/tree/v8/langs" target="_blank">tutaj, aby pomÃ³c w tÅ‚umaczeniu!</a>',
             'tweb.ACCESS_TOKEN_EXPIRED' => 'Ten token bota zostaÅ‚ uniewaÅ¼niony',
             'tweb.ACCESS_TOKEN_INVALID' => 'NieprawidÅ‚owy token bota',
             'tweb.AccDescr.LoginCode' => 'Kod logowania',
             'tweb.Close' => 'Zamknij',
             'tweb.Code' => 'Kod',
             'tweb.Contacts.PhoneNumber.Placeholder' => 'Numer telefonu',
             'tweb.Country' => 'Kraj',
             'tweb.DarkMode' => 'Tryb ciemny',
             'tweb.Error.AnError' => 'WystÄ…piÅ‚ bÅ‚Ä…d. SprÃ³buj ponownie pÃ³Åºniej.',
             'tweb.FIRSTNAME_INVALID' => 'NieprawidÅ‚owe imiÄ™',
             'tweb.FirstName' => 'ImiÄ™ (wymagane)',
             'tweb.LASTNAME_INVALID' => 'NieprawidÅ‚owe nazwisko',
             'tweb.LastName' => 'Nazwisko (opcjonalnie)',
             'tweb.Login.Bot' => 'Zaloguj siÄ™ jako bot >',
             'tweb.Login.Bot.Cancel' => 'Zaloguj siÄ™ jako uÅ¼ytkownik >',
             'tweb.Login.Bot.Subtitle' => 'WprowadÅº token, ktÃ³ry otrzymaÅ‚eÅ› od @BotFather
podczas tworzenia bota.',
             'tweb.Login.Bot.Title' => 'Zaloguj siÄ™ jako bot',
             'tweb.Login.Bot.Token' => 'Token bota',
             'tweb.Login.Code.SecretPhrase' => 'Tajna fraza',
             'tweb.Login.Code.SecretWord' => 'Tajne sÅ‚owo',
             'tweb.Login.Code.SentCall' => 'Zadzwonimy do Ciebie i podyktujemy
kod.',
             'tweb.Login.Code.SentEmail' => 'SprawdÅº swojÄ… skrzynkÄ™ **%s** (nie zapomnij o folderze spam) i wprowadÅº kod, ktÃ³ry wÅ‚aÅ›nie wysÅ‚aliÅ›my.',
             'tweb.Login.Code.SentInApp' => 'WysÅ‚aliÅ›my Ci wiadomoÅ›Ä‡ w Telegramie
z kodem.',
             'tweb.Login.Code.SentMissedCall' => 'W ciÄ…gu kilku sekund powinieneÅ› otrzymaÄ‡
krÃ³tkie poÅ‚Ä…czenie z numeru **%s**. Nie odbieraj â€”
po prostu wpisz ostatnie cyfry tego numeru.',
             'tweb.Login.Code.SentSms' => 'WysÅ‚aliÅ›my Ci SMS
z kodem.',
             'tweb.Login.Code.SentSmsPhrase' => 'WysÅ‚aliÅ›my SMS z tajnÄ… frazÄ…
na TwÃ³j telefon **%s**.',
             'tweb.Login.Code.SentSmsPhraseBeginning' => 'WysÅ‚aliÅ›my SMS z tajnÄ… frazÄ… zaczynajÄ…cÄ… siÄ™
od â€ž**%2$s**â€� na TwÃ³j telefon **%1$s**.',
             'tweb.Login.Code.SentSmsWord' => 'WysÅ‚aliÅ›my SMS z tajnym sÅ‚owem
na TwÃ³j telefon **%s**.',
             'tweb.Login.Code.SentSmsWordBeginning' => 'WysÅ‚aliÅ›my SMS z tajnym sÅ‚owem zaczynajÄ…cym siÄ™
od â€ž**%2$s**â€� na TwÃ³j telefon **%1$s**.',
             'tweb.Login.Code.SentUnknown' => 'Poszukaj kodu
wszÄ™dzie (typ: %s)',
             'tweb.Login.Code.WordBeginningInvalid' => 'Niepoprawne sÅ‚owo',
             'tweb.Login.Code.WordInvalid' => 'Niepoprawnie, sprÃ³buj ponownie',
             'tweb.Login.Next' => 'Dalej',
             'tweb.Login.Password.Subtitle' => 'Twoje konto jest chronione
dodatkowym hasÅ‚em',
             'tweb.Login.Password.Title' => 'WprowadÅº hasÅ‚o',
             'tweb.Login.PhoneLabelInvalid' => 'NieprawidÅ‚owy numer telefonu',
             'tweb.Login.QR.Cancel' => 'Zaloguj siÄ™ numerem telefonu >',
             'tweb.Login.QR.Help1' => 'OtwÃ³rz Telegram na telefonie',
             'tweb.Login.QR.Help2' => 'PrzejdÅº do **Ustawienia** > **UrzÄ…dzenia** > **PoÅ‚Ä…cz urzÄ…dzenie desktopowe**',
             'tweb.Login.QR.Help3' => 'Skieruj telefon na ten ekran, aby potwierdziÄ‡ logowanie',
             'tweb.Login.QR.Login' => 'Zaloguj siÄ™ kodem QR >',
             'tweb.Login.QR.Subtitle' => 'Zeskanuj aplikacjÄ… Telegram na telefonie',
             'tweb.Login.QR.Title' => 'Zaloguj siÄ™ kodem QR',
             'tweb.Login.Register.Subtitle' => 'WprowadÅº swoje imiÄ™,
aby utworzyÄ‡ konto',
             'tweb.Login.StartText' => 'PotwierdÅº kod kraju
i wprowadÅº swÃ³j numer telefonu.',
             'tweb.Login.Title' => 'Zaloguj siÄ™ do MadelineProto',
             'tweb.LoginPassword' => 'HasÅ‚o',
             'tweb.Optional' => 'opcjonalnie',
             'tweb.PASSWORD_HASH_INVALID' => 'NieprawidÅ‚owe hasÅ‚o',
             'tweb.PHONE_CODE_EMPTY' => 'NieprawidÅ‚owy kod',
             'tweb.PHONE_CODE_EXPIRED' => 'Kod wygasÅ‚',
             'tweb.PHONE_CODE_INVALID' => 'NieprawidÅ‚owy kod',
             'tweb.PHONE_NUMBER_BANNED' => 'Ten numer telefonu jest zablokowany',
             'tweb.PHONE_NUMBER_FLOOD' => 'Zbyt wiele prÃ³b, sprÃ³buj ponownie pÃ³Åºniej',
             'tweb.PHONE_NUMBER_INVALID' => 'NieprawidÅ‚owy numer telefonu',
             'tweb.PHONE_PASSWORD_FLOOD' => 'Zbyt wiele prÃ³b, sprÃ³buj ponownie pÃ³Åºniej',
             'tweb.PhoneNumber.Code.Fragment.Info' => 'Odbierz kod w sekcji Numbers na [Fragment]()',
             'tweb.PleaseWait' => 'ProszÄ™ czekaÄ‡...',
             'tweb.StartMessaging' => 'Zacznij pisaÄ‡',
             'tweb.YourName' => 'Twoje imiÄ™',
             'type_extract_error' => 'Nie udaÅ‚o siÄ™ wyodrÄ™bniÄ‡ typu "%s", zaktualizuj MadelineProto!',
             'type_extract_error_id' => 'Nie udaÅ‚o siÄ™ wyodrÄ™bniÄ‡ typu %s o id %s, zaktualizuj MadelineProto!',
             'update_madelineproto' => 'UÅ¼ywasz starej wersji MadelineProto, wymagana jest aktualizacja: obecnie uruchomiona jest wersja %s, a najnowsza wersja z wieloma poprawkami bÅ‚Ä™dÃ³w i nowymi funkcjami to %s!',
             'value_bigger_than_2147483647' => 'Podana wartoÅ›Ä‡ %s jest wiÄ™ksza niÅ¼ 2147483647',
             'value_bigger_than_4294967296' => 'Podana wartoÅ›Ä‡ %s jest wiÄ™ksza niÅ¼ 4294967296',
             'value_bigger_than_9223372036854775807' => 'Podana wartoÅ›Ä‡ %s jest wiÄ™ksza niÅ¼ 9223372036854775807',
             'value_smaller_than_0' => 'Podana wartoÅ›Ä‡ %s jest mniejsza niÅ¼ 0',
             'value_smaller_than_2147483648' => 'Podana wartoÅ›Ä‡ %s jest mniejsza niÅ¼ -2147483648',
             'value_smaller_than_9223372036854775808' => 'Podana wartoÅ›Ä‡ %s jest mniejsza niÅ¼ -9223372036854775808',
             'waveform_must_have_100_values' => 'Tablica ksztaÅ‚tu fali (waveform) musi zawieraÄ‡ 100 wartoÅ›ci!',
             'waveform_value' => 'WartoÅ›Ä‡ ksztaÅ‚tu fali (waveform) musi mieÅ›ciÄ‡ siÄ™ w zakresie od 0 do 31!',
             'webContinue' => 'Kontynuuj',
             'webDocs' => 'Dokumentacja',
             'webDoneLead' => 'MadelineProto nadal dziaÅ‚a na serwerze: moÅ¼esz zamknÄ…Ä‡ tÄ™ stronÄ™.',
             'webError' => '%s. SprawdÅº i sprÃ³buj ponownie.',
             'webFieldRequired' => 'WypeÅ‚nij to pole.',
             'webHidePassword' => 'Ukryj',
             'webLogin' => 'Zaloguj siÄ™',
             'webOptional' => 'opcjonalnie',
             'webPhoneInvalid' => 'WprowadÅº numer telefonu z numerem kierunkowym kraju, np. +39 123 456 7890.',
             'webProgress' => 'PostÄ™p konfiguracji',
             'webQrTitle' => 'Zaloguj siÄ™ kodem QR',
             'webQrWaiting' => 'Oczekiwanie na zeskanowanieâ€¦',
             'webShowPassword' => 'PokaÅ¼',
             'webStepAccount' => 'Konto',
             'webStepApp' => 'API ID i API hash',
             'webStepDone' => 'DziaÅ‚a',
             'webStepVerify' => 'Weryfikacja',
         ],
        'pt' =>
         [
             '2fa_uncalled' => 'NÃ£o estou esperando pela senha! Por favor, chame primeiro os mÃ©todos phoneLogin e completePhoneLogin!',
             'accepting_call' => 'Aceitando chamada de %s...',
             'account_banned' => '!!!!!!! AVISO !!!!!!!
O sistema de prevenÃ§Ã£o de flood do Telegram suspendeu esta conta.
Para continuar, Ã© necessÃ¡ria uma verificaÃ§Ã£o manual.
Envie um e-mail para recover@telegram.org pedindo o desbanimento do nÃºmero de telefone %s e descreva brevemente o que vocÃª farÃ¡ com este nÃºmero.
Depois, faÃ§a login novamente.
Se vocÃª excluiu esta conta intencionalmente, ignore esta mensagem.',
             'already_loggedIn' => 'Esta instÃ¢ncia do MadelineProto jÃ¡ estÃ¡ conectada!',
             'apiAppDescLabelWeb' => 'DescriÃ§Ã£o',
             'apiAppInstructionsAuto0' => 'Digite o nome do app, pode ser qualquer coisa: ',
             'apiAppInstructionsAuto1' => 'Digite o nome do seu aplicativo, alfanumÃ©rico, de 5 a 32 caracteres: ',
             'apiAppInstructionsAuto2' => 'Digite a URL do seu aplicativo/site ou t.me/seuusuÃ¡rio: ',
             'apiAppInstructionsAuto3' => 'Digite a plataforma do aplicativo: ',
             'apiAppInstructionsAuto4' => 'Descreva seu aplicativo: ',
             'apiAppInstructionsAutoTypeOther' => 'Outro (especifique na descriÃ§Ã£o)',
             'apiAppInstructionsManual0' => 'nome do seu aplicativo, pode ser qualquer coisa',
             'apiAppInstructionsManual1' => 'o nome abreviado do seu aplicativo, alfanumÃ©rico, de 5 a 32 caracteres',
             'apiAppInstructionsManual2' => 'URL do seu aplicativo/site ou t.me/yourusername',
             'apiAppInstructionsManual3' => 'qualquer coisa',
             'apiAppInstructionsManual4' => 'Descreva seu aplicativo aqui',
             'apiAppShortNameLabelWeb' => 'Nome abreviado',
             'apiAppTitleLabelWeb' => 'Nome do aplicativo',
             'apiAppUrlLabelWeb' => 'URL',
             'apiAppWeb' => 'Insira as informaÃ§Ãµes da API',
             'apiAutoCodeLabelWeb' => 'CÃ³digo de login',
             'apiAutoCodeLeadWeb' => 'O my.telegram.org enviou um cÃ³digo de login para o seu aplicativo do Telegram, no chat com o Telegram (nÃ£o por SMS).',
             'apiAutoCodeTitleWeb' => 'Digite o cÃ³digo do Telegram',
             'apiAutoErrorTitle' => 'O my.telegram.org retornou um erro',
             'apiAutoFallbackManual' => 'Em vez disso, insira o API ID e o API hash manualmente.',
             'apiAutoPhoneHintWeb' => 'O my.telegram.org envia o cÃ³digo de login para o seu aplicativo do Telegram, nÃ£o por SMS.',
             'apiAutoPrompt0' => 'Digite um nÃºmero de telefone que jÃ¡ esteja registrado no Telegram: ',
             'apiAutoPrompt1' => 'Digite o cÃ³digo de verificaÃ§Ã£o recebido pelo Telegram: ',
             'apiAutoWeb' => 'Insira um nÃºmero de telefone que <b>jÃ¡ esteja registrado</b> no Telegram para obter o ID da API',
             'apiChooseAutoWeb' => 'Obter automaticamente',
             'apiChooseManualAuto' => 'Deseja obter o API ID e o API hash automaticamente de my.telegram.org (a) ou inseri-los manualmente (m)?',
             'apiChooseManualAutoTip' => 'Observe que vocÃª tambÃ©m pode fornecer o ID/hash da API diretamente no cÃ³digo usando as configuraÃ§Ãµes: %s',
             'apiChooseManualAutoTipWeb' => 'Observe que vocÃª tambÃ©m pode fornecer o ID/hash da API diretamente no cÃ³digo usando as <a target="_blank" href="%s">configuraÃ§Ãµes</a>.',
             'apiChooseManualWeb' => 'JÃ¡ tenho um API ID',
             'apiChoosePrompt' => 'Sua escolha (m/a): ',
             'apiCreateErrorTitle' => 'O my.telegram.org nÃ£o criou o aplicativo',
             'apiCreateLeadWeb' => 'Esta conta ainda nÃ£o tem nenhum aplicativo em my.telegram.org. Estes dados sÃ£o exibidos apenas para vocÃª e podem ser alterados depois.',
             'apiCreateTitleWeb' => 'Crie seu aplicativo',
             'apiCreateVpsWarning' => 'O Telegram nÃ£o permite que IPs de VPS, VPN e outros datacenters criem novos aplicativos em my.telegram.org, e a solicitaÃ§Ã£o foi feita a partir do IP da mÃ¡quina que executa o MadelineProto. Crie o aplicativo manualmente em https://my.telegram.org usando uma conexÃ£o residencial (como o seu celular nos dados mÃ³veis) e depois insira o API ID e o API hash.',
             'apiCreateWeb' => 'Criar aplicativo',
             'apiError' => 'ERRO: %s. Tente novamente.',
             'apiInvalidWeb' => 'O API ID Ã© um nÃºmero e o API hash tem 32 caracteres: copie-os novamente de my.telegram.org.',
             'apiManualInstructions0' => 'FaÃ§a login em https://my.telegram.org',
             'apiManualInstructions1' => 'Ir para API development tools',
             'apiManualInstructions2' => 'Clique em criar aplicativo',
             'apiManualPrompt0' => 'Insira seu API ID: ',
             'apiManualPrompt1' => 'Insira seu API hash: ',
             'apiManualWeb' => 'Insira seu API ID e API hash',
             'apiParamsError' => 'VocÃª nÃ£o forneceu todos os parÃ¢metros necessÃ¡rios!',
             'apiSaveWeb' => 'Salvar e continuar',
             'apiSendCodeWeb' => 'Enviar cÃ³digo',
             'apiSetupLeadWeb' => 'Todo aplicativo do Telegram precisa de um API ID e um API hash. O MadelineProto pode obtÃª-los para vocÃª em my.telegram.org, com o nÃºmero de telefone de uma conta do Telegram jÃ¡ existente.',
             'apiSetupTitleWeb' => 'Obtenha seu API ID e API hash',
             'apiStartOverWeb' => 'RecomeÃ§ar',
             'api_not_set' => 'VocÃª deve fornecer uma chave de API e um ID de API, vocÃª pode obter em @ my.telegram.org',
             'array_invalid' => 'VocÃª nÃ£o forneceu um array vÃ¡lido',
             'baseDirLimitation' => 'Uma limitaÃ§Ã£o de basedir estÃ¡ configurada: isso pode afetar o desempenho e causar alguns problemas, desative-a se possÃ­vel!',
             'bool_error' => 'NÃ£o foi possÃ­vel extrair um valor booleano',
             'botAlreadyRunning' => 'O bot jÃ¡ estÃ¡ em execuÃ§Ã£o!',
             'botapi_conversion_error' => 'NÃ£o Ã© possÃ­vel converter %s em um objeto da API de bots',
             'call_already_accepted' => 'Chamada %s jÃ¡ aceita',
             'call_already_declined' => 'Chamada %s jÃ¡ recusada',
             'call_completing' => 'Completando chamada de %s...',
             'call_confirming' => 'Confirmando chamada de %s...',
             'call_discarding' => 'Descartando chamada %s...',
             'call_error_1' => 'NÃ£o foi possÃ­vel encontrar e aceitar a chamada %s',
             'call_error_2' => 'NÃ£o foi possÃ­vel encontrar e confirmar a chamada %s',
             'call_error_3' => 'NÃ£o foi possÃ­vel encontrar e completar a chamada %s',
             'cli_need_dl.php_link' => 'Especifique a URL de um script de download ao usar getDownloadLink pela CLI!',
             'constructor_not_found' => 'Construtor nÃ£o encontrado para o tipo: ',
             'could_not_connect_to_MadelineProto' => 'NÃ£o foi possÃ­vel conectar ao MadelineProto. Para corrigir, habilite proc_open e remova as restriÃ§Ãµes de open_basedir ou desative as reescritas de caminho do servidor web! Se vocÃª jÃ¡ fez isso, certifique-se de que a versÃ£o CLI do PHP seja exatamente igual Ã  versÃ£o web (mesma versÃ£o, extensÃµes etc.) e consulte o arquivo MadelineProto.log para mais informaÃ§Ãµes sobre o erro que impediu a inicializaÃ§Ã£o do servidor IPC.',
             'could_not_convert_object' => 'NÃ£o foi possÃ­vel converter o objeto do tipo %s',
             'deserialization_error' => 'Ocorreu um erro na deserializaÃ§Ã£o',
             'dl.php_check_logs_make_sure_session_running' => 'O bot EventHandler do MadelineProto associado ou o servidor IPC do MadelineProto estÃ¡ offline; verifique os logs e certifique-se de que pelo menos um deles esteja em execuÃ§Ã£o!',
             'dl.php_powered_by_madelineproto' => 'Servidor de download de arquivos do Telegram (atÃ© 4GB), desenvolvido com <a href="https://docs.madelineproto.xyz" target="_blank">MadelineProto</a>!<br>Clique <a href="https://docs.madelineproto.xyz/docs/FILES.html#getting-a-download-link" target="_blank">aqui</a> para mais informaÃ§Ãµes sobre como configurar seu prÃ³prio servidor de download de arquivos do Telegram!',
             'do_not_delete_MadelineProto.log' => 'o arquivo MadelineProto.log nunca deve ser excluÃ­do; em vez disso, defina um tamanho mÃ¡ximo personalizado nas configuraÃ§Ãµes!',
             'do_not_remove_MadelineProto.log_phar' => 'NÃ£o remova madeline.phar nem madeline.php, ou o MadelineProto irÃ¡ travar. Se tiver algum problema com o MadelineProto, reporte-o em https://github.com/danog/MadelineProto ou https://t.me/pwrtelegramgroup',
             'do_not_use_blocking_class' => 'por motivos de desempenho, os handlers nÃ£o podem usar a classe bloqueante nÃ£o assÃ­ncrona %s; use %s em vez disso',
             'do_not_use_blocking_function' => 'por motivos de desempenho, os event handlers nÃ£o podem usar a funÃ§Ã£o bloqueante nÃ£o assÃ­ncrona %s; use %s em vez disso',
             'do_not_use_deprecated_function' => 'a funÃ§Ã£o %s estÃ¡ obsoleta; use %s em vez disso',
             'do_not_use_non_root_require_in_event_handler' => 'por motivos de desempenho, vocÃª nÃ£o deve usar require ou include dentro de uma classe de event handler; somente requires no nÃ­vel raiz sÃ£o permitidos.',
             'do_not_use_yield' => 'O MadelineProto 8 nÃ£o requer nem suporta o uso de yield em funÃ§Ãµes assÃ­ncronas; vocÃª deve remover todas as palavras-chave yield usadas anteriormente para chamadas de funÃ§Ãµes assÃ­ncronas',
             'done' => 'ConcluÃ­do!',
             'encode_double_error' => 'NÃ£o foi possÃ­vel codificar o nÃºmero decimal corretamente',
             'extensionRecommended' => 'Aviso: a extensÃ£o %s nÃ£o estÃ¡ instalada, instale-a para acelerar o MadelineProto!',
             'extensionRequired' => 'MadelineProto requer a extensÃ£o %s para ser executada. %s',
             'extensionRequiredInstallWithApt' => 'Tente executar sudo apt-get install %s.',
             'extensionRequiredInstallWithCustomInstructions' => 'Siga as instruÃ§Ãµes em %s para instalÃ¡-lo.',
             'file_not_exist' => 'Arquivo nÃ£o existe',
             'file_parsing' => 'Processando %s...',
             'file_type_invalid' => 'Tipo de arquivo invÃ¡lido detectado (%s)',
             'fingerprint_invalid' => 'ImpressÃ£o digital da chave invÃ¡lida!',
             'go' => 'Pronto',
             'invalid_dl.php_session' => '%s nÃ£o Ã© um script de download vÃ¡lido porque seu ID de sessÃ£o Ã© diferente (esperado %s, recebido %s)',
             'ipc_tcp_warning' => 'UNIX sockets are not available, so the IPC server of the session listens on TCP (127.0.0.1) instead: any program running on this machine can connect to it, and control the session. On Windows 10 1803+, enable the sockets extension (extension=sockets in php.ini) to use UNIX sockets.',
             'length_too_big' => 'O comprimento Ã© muito grande',
             'loginBot' => 'Digite o token do seu bot: ',
             'loginBotTokenHintWeb' => 'O token que o @BotFather forneceu quando vocÃª criou o bot.',
             'loginBotTokenWeb' => 'Token do bot',
             'loginChoosePromptWeb' => 'Deseja fazer login como UsuÃ¡rio ou como Bot?',
             'loginCodeLeadWeb' => 'O Telegram enviou um cÃ³digo de login para o seu aplicativo do Telegram, ou por SMS se vocÃª nÃ£o estiver conectado em nenhum outro lugar.',
             'loginCodeTitleWeb' => 'Digite o cÃ³digo de login',
             'loginLeadWeb' => 'Escolha a conta ou o bot com que esta instÃ¢ncia do MadelineProto serÃ¡ executada.',
             'loginManual' => 'Alternativamente, vocÃª tambÃ©m pode inserir um token de bot ou nÃºmero de telefone para fazer login manualmente: ',
             'loginNoCode' => 'VocÃª nÃ£o forneceu o cÃ³digo do telefone!',
             'loginNoName' => 'VocÃª nÃ£o forneceu o primeiro nome!',
             'loginNoPass' => 'VocÃª nÃ£o forneceu a senha!',
             'loginOptionBot' => 'Bot',
             'loginOptionUser' => 'UsuÃ¡rio',
             'loginPasswordLabelWeb' => 'Senha',
             'loginPasswordLeadWeb' => 'Esta conta estÃ¡ protegida por uma senha, definida em ConfiguraÃ§Ãµes > Privacidade e SeguranÃ§a > VerificaÃ§Ã£o em Duas Etapas.',
             'loginPasswordTitleWeb' => 'VerificaÃ§Ã£o em duas etapas',
             'loginQr' => 'Abra o Telegram no seu telefone, vÃ¡ para ConfiguraÃ§Ãµes > Dispositivos > Conectar Desktop e escaneie o QR code acima para fazer login automaticamente.',
             'loginQrCodeExpired' => 'O QR code expirou, gerando um novo...',
             'loginQrCodeSuccessful' => 'Login efetuado com sucesso, utilizando QR code!',
             'loginTitleWeb' => 'Entrar no Telegram',
             'loginTypeWeb' => 'Entrar como',
             'loginUser' => 'Digite seu nÃºmero de telefone: ',
             'loginUserCode' => 'Digite o cÃ³digo: ',
             'loginUserPass' => 'Digite sua senha (dica %s): ',
             'loginUserPassHint' => 'Dica: %s',
             'loginUserPassWeb' => 'Digite sua senha: ',
             'loginUserPhoneCodeWeb' => 'CÃ³digo',
             'loginUserPhoneHintWeb' => 'Com o cÃ³digo do paÃ­s, por exemplo +39 123 456 7890.',
             'loginUserPhoneWeb' => 'NÃºmero de telefone',
             'loginWebQr' => 'VocÃª tambÃ©m pode fazer login automaticamente abrindo o Telegram no seu telefone, acessando ConfiguraÃ§Ãµes > Dispositivos > Conectar Desktop e digitalizando o seguinte cÃ³digo QR:',
             'loginWebQr1' => 'Abra o Telegram no seu telefone',
             'loginWebQr2' => 'VÃ¡ para ConfiguraÃ§Ãµes > Dispositivos > Conectar Desktop',
             'loginWebQr3' => 'Aponte seu telefone para tela para confirmar o login',
             'login_2fa_enabled' => 'AutenticaÃ§Ã£o de dois fatores ativada, vocÃª terÃ¡ que chamar a funÃ§Ã£o complete2falogin...',
             'login_auth_key' => 'Conectando-se usando a chave de autenticaÃ§Ã£o...',
             'login_bot' => 'Conectando-se como um bot...',
             'login_code_sending' => 'Enviando cÃ³digo...',
             'login_code_sent' => 'CÃ³digo enviado com sucesso! Assim que receber o cÃ³digo, vocÃª deve usar a funÃ§Ã£o completePhoneLogin.',
             'login_code_uncalled' => 'NÃ£o estou esperando pelo cÃ³digo! Por favor, chame primeiro o mÃ©todo phoneLogin',
             'login_need_signup' => 'Uma conta nÃ£o foi criada para este nÃºmero, vocÃª terÃ¡ que chamar a funÃ§Ã£o completeSignup...',
             'login_ok' => 'Conectado com sucesso!',
             'login_user' => 'Conectando-se como um usuÃ¡rio normal...',
             'logout_ok' => 'Desconectado com sucesso!',
             'long_not_16' => 'O valor fornecido nÃ£o tem 16 bytes de comprimento',
             'long_not_32' => 'O valor fornecido nÃ£o tem 32 bytes de comprimento',
             'long_not_64' => 'O valor fornecido nÃ£o tem 64 bytes de comprimento',
             'madelineproto_ready' => 'MadelineProto estÃ¡ pronto!',
             'manualAdminActionRequired' => '!!!!!!!!! AÃ‡ÃƒO MANUAL DO ADMINISTRADOR DO SISTEMA NECESSÃ�RIA !!!!!!!!',
             'method_not_found' => 'NÃ£o foi possÃ­vel encontrar o mÃ©todo: ',
             'mmapErrorPart1' => 'O nÃºmero mÃ¡ximo de regiÃµes mapeadas de memÃ³ria (mmap) foi atingido (%s): aumente a configuraÃ§Ã£o do kernel vm.max_map_count para 262144 para corrigir.',
             'mmapErrorPart2' => 'Para corrigir, execute o seguinte comando como root: %s',
             'mmapErrorPart3' => 'Para manter a alteraÃ§Ã£o apÃ³s reinicializaÃ§Ãµes: %s',
             'mmapErrorPart4' => 'No Windows e no WSL, aumentar o tamanho do arquivo de paginaÃ§Ã£o pode ajudar; mude para o Linux nativo se o problema persistir.',
             'must_have_declare_types' => 'por motivos de desempenho, a primeira instruÃ§Ã£o de um arquivo de event handler deve ser "declare(strict_types=1);"',
             'nearest_dc' => 'Estamos em %s, o DC mais prÃ³ximo Ã© %d.',
             'need_dl.php' => 'NÃ£o foi possÃ­vel gerar o script de download padrÃ£o (%s); crie um arquivo dl.php com o seguinte conteÃºdo: %s e passe sua URL como segundo parÃ¢metro de getDownloadLink',
             'noReportPeers' => 'Aviso: nenhum peer de relatÃ³rio estÃ¡ definido, adicione o seguinte mÃ©todo ao seu event handler',
             'non_text_conversion' => 'Ainda nÃ£o Ã© possÃ­vel converter mensagens que nÃ£o sejam de texto!',
             'not_loggedIn' => 'NÃ£o estou conectado!',
             'not_numeric' => 'O valor fornecido nÃ£o Ã© numÃ©rico',
             'params_missing' => 'ParÃ¢metro obrigatÃ³rio ausente',
             'peer_not_in_db' => 'Este peer nÃ£o estÃ¡ presente no banco de dados interno de peers',
             'plugin_path_does_not_exist' => 'O caminho do plugin %s nÃ£o existe!',
             'plugins_do_not_use_require' => 'por motivos de desempenho, os plugins sÃ³ podem incluir automaticamente (include ou require) outros arquivos presentes na pasta de plugins por meio do autoloader PSR-4 (e nÃ£o usando require() manualmente).',
             'plugins_must_have_exactly_one_class' => 'um plugin deve definir exatamente uma classe! Para definir vÃ¡rias classes, interfaces ou traits, crie arquivos separados; eles serÃ£o carregados automaticamente pelo MadelineProto.',
             'predicate_not_set' => 'Predicado (valor sob _) nÃ£o foi definido!',
             'recommend_not_use_filesystem_function' => 'o uso da funÃ§Ã£o %s nÃ£o Ã© recomendado, pois acessar o sistema de arquivos durante o processamento de updates deixarÃ¡ seu bot mais lento. Consulte https://docs.madelineproto.xyz/docs/UPDATES.html#avoiding-the-use-of-filesystem-functions para ver formas alternativas de armazenar dados que nÃ£o deixarÃ£o seu bot mais lento!',
             'rpc_tg_error' => 'O Telegram retornou um erro RPC: %s (%s), causado por %s:%s%sTL trace:',
             'sec_peer_not_in_db' => 'Este peer secreto nÃ£o estÃ¡ presente no banco de dados interno de peers',
             'secret_chat_skipping' => 'NÃ£o tenho o chat secreto %s no banco de dados, ignorando a mensagem...',
             'serialization_ofd' => 'A serializaÃ§Ã£o estÃ¡ desatualizada, reconstruindo o objeto!',
             'session_corrupted' => 'A sessÃ£o estÃ¡ corrompida!',
             'signing_up' => 'Cadastrando-se como um usuÃ¡rio normal...',
             'signupFirstName' => 'Digite seu primeiro nome: ',
             'signupFirstNameWeb' => 'Primeiro nome',
             'signupLastName' => 'Digite seu sobrenome (pode ficar em branco): ',
             'signupLastNameWeb' => 'Sobrenome',
             'signupLeadWeb' => 'Este nÃºmero de telefone ainda nÃ£o estÃ¡ no Telegram: escolha o nome da nova conta.',
             'signupTitleWeb' => 'Crie sua conta',
             'signupWeb' => 'FaÃ§a seu cadastro, por favor',
             'signup_ok' => 'Cadastro realizado com sucesso!',
             'signup_uncalled' => 'NÃ£o estou esperando para me cadastrar! Por favor, chame primeiro os mÃ©todos phoneLogin e completePhoneLogin!',
             'src_file_invalid' => 'Arquivo de origem invÃ¡lido foi fornecido: ',
             'static_analysis_minor' => 'Um problema menor foi encontrado durante a anÃ¡lise estÃ¡tica de %s: %s',
             'static_analysis_severe' => 'Um problema grave foi encontrado durante a anÃ¡lise estÃ¡tica de %s: %s',
             'stream_handle_invalid' => 'Um identificador de fluxo invÃ¡lido foi fornecido.',
             'string_required' => 'Uma string era esperada!',
             'translate_madelineproto_cli' => 'MadelineProto pode ser traduzido para o seu idioma (progresso atual da traduÃ§Ã£o: %d%%), acesse https://github.com/danog/MadelineProto/tree/v8/langs para contribuir com a traduÃ§Ã£o!',
             'translate_madelineproto_web' => 'MadelineProto pode ser traduzido para o seu idioma (progresso atual da traduÃ§Ã£o: %d%%), clique <a href="https://github.com/danog/MadelineProto/tree/v8/langs" target="_blank">aqui para contribuir com a traduÃ§Ã£o!</a>',
             'tweb.ACCESS_TOKEN_EXPIRED' => 'Este token do bot foi revogado',
             'tweb.ACCESS_TOKEN_INVALID' => 'Token do bot invÃ¡lido',
             'tweb.AccDescr.LoginCode' => 'CÃ³digo de login',
             'tweb.Close' => 'Fechar',
             'tweb.Code' => 'CÃ³digo',
             'tweb.Contacts.PhoneNumber.Placeholder' => 'NÃºmero de telefone',
             'tweb.Country' => 'PaÃ­s',
             'tweb.DarkMode' => 'Modo escuro',
             'tweb.Error.AnError' => 'Ocorreu um erro. Tente novamente mais tarde.',
             'tweb.FIRSTNAME_INVALID' => 'Nome invÃ¡lido',
             'tweb.FirstName' => 'Nome (obrigatÃ³rio)',
             'tweb.LASTNAME_INVALID' => 'Sobrenome invÃ¡lido',
             'tweb.LastName' => 'Sobrenome (opcional)',
             'tweb.Login.Bot' => 'Entrar como bot >',
             'tweb.Login.Bot.Cancel' => 'Entrar como usuÃ¡rio >',
             'tweb.Login.Bot.Subtitle' => 'Digite o token que o @BotFather forneceu
quando vocÃª criou o bot.',
             'tweb.Login.Bot.Title' => 'Entrar como bot',
             'tweb.Login.Bot.Token' => 'Token do bot',
             'tweb.Login.Code.SecretPhrase' => 'Frase secreta',
             'tweb.Login.Code.SecretWord' => 'Palavra secreta',
             'tweb.Login.Code.SentCall' => 'Vamos ligar para vocÃª e ditar
o cÃ³digo.',
             'tweb.Login.Code.SentEmail' => 'Verifique seu e-mail **%s** (nÃ£o se esqueÃ§a da pasta de spam) e digite o cÃ³digo que acabamos de enviar.',
             'tweb.Login.Code.SentInApp' => 'Enviamos uma mensagem no Telegram
com o cÃ³digo.',
             'tweb.Login.Code.SentMissedCall' => 'Nos prÃ³ximos segundos, vocÃª deve receber
uma ligaÃ§Ã£o curta de **%s**. NÃ£o atenda â€”
apenas digite os Ãºltimos dÃ­gitos desse nÃºmero.',
             'tweb.Login.Code.SentSms' => 'Enviamos um SMS
com o cÃ³digo.',
             'tweb.Login.Code.SentSmsPhrase' => 'Enviamos um SMS com uma frase secreta
para o seu telefone **%s**.',
             'tweb.Login.Code.SentSmsPhraseBeginning' => 'Enviamos um SMS com uma frase secreta que comeÃ§a
com "**%2$s**" para o seu telefone **%1$s**.',
             'tweb.Login.Code.SentSmsWord' => 'Enviamos um SMS com uma palavra secreta
para o seu telefone **%s**.',
             'tweb.Login.Code.SentSmsWordBeginning' => 'Enviamos um SMS com uma palavra secreta que comeÃ§a
com "**%2$s**" para o seu telefone **%1$s**.',
             'tweb.Login.Code.SentUnknown' => 'Procure o cÃ³digo
em todos os lugares (tipo: %s)',
             'tweb.Login.Code.WordBeginningInvalid' => 'Palavra incorreta',
             'tweb.Login.Code.WordInvalid' => 'Incorreto, tente novamente',
             'tweb.Login.Next' => 'AvanÃ§ar',
             'tweb.Login.Password.Subtitle' => 'Sua conta estÃ¡ protegida com
uma senha adicional',
             'tweb.Login.Password.Title' => 'Digite sua senha',
             'tweb.Login.PhoneLabelInvalid' => 'NÃºmero de telefone invÃ¡lido',
             'tweb.Login.QR.Cancel' => 'Entrar com nÃºmero de telefone >',
             'tweb.Login.QR.Help1' => 'Abra o Telegram no seu celular',
             'tweb.Login.QR.Help2' => 'VÃ¡ para **ConfiguraÃ§Ãµes** > **Dispositivos** > **Conectar Desktop**',
             'tweb.Login.QR.Help3' => 'Aponte seu celular para esta tela para confirmar o login',
             'tweb.Login.QR.Login' => 'Entrar com QR code >',
             'tweb.Login.QR.Subtitle' => 'Escaneie com o app do Telegram no seu celular',
             'tweb.Login.QR.Title' => 'Entrar com QR code',
             'tweb.Login.Register.Subtitle' => 'Digite seu nome
para criar sua conta',
             'tweb.Login.StartText' => 'Confirme o cÃ³digo do seu paÃ­s
e digite seu nÃºmero de telefone.',
             'tweb.Login.Title' => 'Entrar no MadelineProto',
             'tweb.LoginPassword' => 'Senha',
             'tweb.Optional' => 'opcional',
             'tweb.PASSWORD_HASH_INVALID' => 'Senha incorreta',
             'tweb.PHONE_CODE_EMPTY' => 'CÃ³digo invÃ¡lido',
             'tweb.PHONE_CODE_EXPIRED' => 'CÃ³digo expirado',
             'tweb.PHONE_CODE_INVALID' => 'CÃ³digo invÃ¡lido',
             'tweb.PHONE_NUMBER_BANNED' => 'Este nÃºmero de telefone estÃ¡ banido',
             'tweb.PHONE_NUMBER_FLOOD' => 'Muitas tentativas, tente novamente mais tarde',
             'tweb.PHONE_NUMBER_INVALID' => 'NÃºmero de telefone invÃ¡lido',
             'tweb.PHONE_PASSWORD_FLOOD' => 'Muitas tentativas, tente novamente mais tarde',
             'tweb.PhoneNumber.Code.Fragment.Info' => 'Obtenha o cÃ³digo na seÃ§Ã£o NÃºmeros do [Fragment]()',
             'tweb.PleaseWait' => 'Aguarde...',
             'tweb.StartMessaging' => 'ComeÃ§ar a conversar',
             'tweb.YourName' => 'Seu nome',
             'type_extract_error' => 'NÃ£o foi possÃ­vel extrair o tipo "%s", vocÃª deve atualizar o MadelineProto!',
             'type_extract_error_id' => 'NÃ£o foi possÃ­vel extrair o tipo: %s com id %s, vocÃª deve atualizar o MadelineProto!',
             'update_madelineproto' => 'VocÃª estÃ¡ executando uma versÃ£o antiga do MadelineProto, uma atualizaÃ§Ã£o Ã© necessÃ¡ria: atualmente rodando %s, mas a versÃ£o mais recente com vÃ¡rias correÃ§Ãµes de bugs e novos recursos Ã© %s!',
             'value_bigger_than_2147483647' => 'O valor fornecido %s Ã© maior que 2147483647',
             'value_bigger_than_4294967296' => 'O valor fornecido %s Ã© maior que 4294967296',
             'value_bigger_than_9223372036854775807' => 'O valor fornecido %s Ã© maior que 9223372036854775807',
             'value_smaller_than_0' => 'O valor fornecido %s Ã© menor que 0',
             'value_smaller_than_2147483648' => 'O valor fornecido %s Ã© menor que -2147483648',
             'value_smaller_than_9223372036854775808' => 'O valor fornecido %s Ã© menor que -9223372036854775808',
             'waveform_must_have_100_values' => 'Um array de waveform deve ter 100 valores!',
             'waveform_value' => 'Um valor de waveform deve estar entre 0 e 31!',
             'webContinue' => 'Continuar',
             'webDocs' => 'DocumentaÃ§Ã£o',
             'webDoneLead' => 'O MadelineProto continua em execuÃ§Ã£o no servidor: vocÃª pode fechar esta pÃ¡gina.',
             'webError' => '%s. Verifique e tente novamente.',
             'webFieldRequired' => 'Preencha este campo.',
             'webHidePassword' => 'Ocultar',
             'webLogin' => 'Entrar',
             'webOptional' => 'opcional',
             'webPhoneInvalid' => 'Digite o nÃºmero de telefone com o cÃ³digo do paÃ­s, por exemplo +39 123 456 7890.',
             'webProgress' => 'Progresso da configuraÃ§Ã£o',
             'webQrTitle' => 'Entrar com um QR code',
             'webQrWaiting' => 'Aguardando a leituraâ€¦',
             'webShowPassword' => 'Mostrar',
             'webStepAccount' => 'Conta',
             'webStepApp' => 'API ID e API hash',
             'webStepDone' => 'Em execuÃ§Ã£o',
             'webStepVerify' => 'VerificaÃ§Ã£o',
         ],
        'ru' =>
         [
             '2fa_uncalled' => 'Ð¯ Ð½Ðµ Ð¶Ð´Ñƒ Ñ�ÐµÐ¹Ñ‡Ð°Ñ� Ð¿Ð°Ñ€Ð¾Ð»Ñ�! Ð’Ñ‹Ð·Ð¾Ð²Ð¸Ñ‚Ðµ Ñ�Ð½Ð°Ñ‡Ð°Ð»Ð° Ð¼ÐµÑ‚Ð¾Ð´Ñ‹ phoneLogin Ð¸ completePhoneLogin!',
             'accepting_call' => 'ÐŸÑ€Ð¸Ð½Ð¸Ð¼Ð°ÑŽ Ð·Ð²Ð¾Ð½Ð¾Ðº Ð¾Ñ‚ %s...',
             'account_banned' => '!!!!!!! Ð’Ð�Ð˜ÐœÐ�Ð�Ð˜Ð• !!!!!!!
Ð¡Ð¸Ñ�Ñ‚ÐµÐ¼Ð° Ð·Ð°Ñ‰Ð¸Ñ‚Ñ‹ Ð¾Ñ‚ Ñ„Ð»ÑƒÐ´Ð° Telegram Ð·Ð°Ð±Ð»Ð¾ÐºÐ¸Ñ€Ð¾Ð²Ð°Ð»Ð° Ñ�Ñ‚Ð¾Ñ‚ Ð°ÐºÐºÐ°ÑƒÐ½Ñ‚.
Ð”Ð»Ñ� Ð¿Ñ€Ð¾Ð´Ð¾Ð»Ð¶ÐµÐ½Ð¸Ñ� Ð½ÐµÐ¾Ð±Ñ…Ð¾Ð´Ð¸Ð¼Ð° Ñ€ÑƒÑ‡Ð½Ð°Ñ� Ð¿Ñ€Ð¾Ð²ÐµÑ€ÐºÐ°.
ÐžÑ‚Ð¿Ñ€Ð°Ð²ÑŒÑ‚Ðµ Ð¿Ð¸Ñ�ÑŒÐ¼Ð¾ Ð½Ð° recover@telegram.org Ñ� Ð¿Ñ€Ð¾Ñ�ÑŒÐ±Ð¾Ð¹ Ñ€Ð°Ð·Ð±Ð»Ð¾ÐºÐ¸Ñ€Ð¾Ð²Ð°Ñ‚ÑŒ Ð½Ð¾Ð¼ÐµÑ€ Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½Ð° %s Ð¸ ÐºÑ€Ð°Ñ‚ÐºÐ¾ Ð¾Ð¿Ð¸ÑˆÐ¸Ñ‚Ðµ, Ð´Ð»Ñ� Ñ‡ÐµÐ³Ð¾ Ð²Ñ‹ Ð±ÑƒÐ´ÐµÑ‚Ðµ Ð¸Ñ�Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÑŒ Ñ�Ñ‚Ð¾Ñ‚ Ð½Ð¾Ð¼ÐµÑ€.
Ð—Ð°Ñ‚ÐµÐ¼ Ð²Ð¾Ð¹Ð´Ð¸Ñ‚Ðµ Ñ�Ð½Ð¾Ð²Ð°.
Ð•Ñ�Ð»Ð¸ Ð²Ñ‹ ÑƒÐ´Ð°Ð»Ð¸Ð»Ð¸ Ñ�Ñ‚Ð¾Ñ‚ Ð°ÐºÐºÐ°ÑƒÐ½Ñ‚ Ð½Ð°Ð¼ÐµÑ€ÐµÐ½Ð½Ð¾, Ð¿Ñ€Ð¾Ð¸Ð³Ð½Ð¾Ñ€Ð¸Ñ€ÑƒÐ¹Ñ‚Ðµ Ñ�Ñ‚Ð¾ Ñ�Ð¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ðµ.',
             'already_loggedIn' => 'Ð­Ñ‚Ð¾Ñ‚ Ð¸Ð½Ñ�Ñ‚Ð°Ð½Ñ� MadelineProto ÑƒÐ¶Ðµ Ð·Ð°Ð»Ð¾Ð³Ð¸Ð½ÐµÐ½!',
             'apiAppDescLabelWeb' => 'ÐžÐ¿Ð¸Ñ�Ð°Ð½Ð¸Ðµ',
             'apiAppInstructionsAuto0' => 'Ð’Ð²ÐµÐ´Ð¸Ñ‚Ðµ Ð¸Ð¼Ñ� Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ�, Ð¼Ð¾Ð¶ÐµÑ‚ Ð±Ñ‹Ñ‚ÑŒ Ñ‡Ñ‚Ð¾ ÑƒÐ³Ð¾Ð´Ð½Ð¾: ',
             'apiAppInstructionsAuto1' => 'Ð’Ð²ÐµÐ´Ð¸Ñ‚Ðµ ÐºÐ¾Ñ€Ð¾Ñ‚ÐºÐ¾Ðµ Ð¸Ð¼Ñ� Ð²Ð°ÑˆÐµÐ³Ð¾ Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ�, Ð»Ð°Ñ‚Ð¸Ð½Ð¸Ñ†Ð° Ð¸ Ñ†Ð¸Ñ„Ñ€Ñ‹, 5-32 Ñ�Ð¸Ð¼Ð²Ð¾Ð»Ð°: ',
             'apiAppInstructionsAuto2' => 'Ð’Ð²ÐµÐ´Ð¸Ñ‚Ðµ Ñ�Ñ�Ñ‹Ð»ÐºÑƒ Ð½Ð° Ñ�Ð°Ð¹Ñ‚ Ð²Ð°ÑˆÐµÐ³Ð¾ Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ�, Ð¸Ð»Ð¸ t.me/yourusername: ',
             'apiAppInstructionsAuto3' => 'Ð’Ð²ÐµÐ´Ð¸Ñ‚Ðµ Ð¿Ð»Ð°Ñ‚Ñ„Ð¾Ñ€Ð¼Ñƒ Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ�: ',
             'apiAppInstructionsAuto4' => 'ÐžÐ¿Ð¸ÑˆÐ¸Ñ‚Ðµ Ð²Ð°ÑˆÐµ Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ðµ: ',
             'apiAppInstructionsAutoTypeOther' => 'Ð”Ñ€ÑƒÐ³Ð¾Ðµ (ÑƒÐºÐ°Ð¶Ð¸Ñ‚Ðµ Ð² Ð¾Ð¿Ð¸Ñ�Ð°Ð½Ð¸Ð¸)',
             'apiAppInstructionsManual0' => 'Ð˜Ð¼Ñ� Ð²Ð°ÑˆÐµÐ³Ð¾ Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ�, Ñ‡Ñ‚Ð¾ ÑƒÐ³Ð¾Ð´Ð½Ð¾',
             'apiAppInstructionsManual1' => 'ÐºÐ¾Ñ€Ð¾Ñ‚ÐºÐ¾Ðµ Ð¸Ð¼Ñ� Ð²Ð°ÑˆÐµÐ³Ð¾ Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ�, Ð»Ð°Ñ‚Ð¸Ð½Ð¸Ñ†Ð° Ð¸ Ñ†Ð¸Ñ„Ñ€Ñ‹, 5-32 Ñ�Ð¸Ð¼Ð²Ð¾Ð»Ð°',
             'apiAppInstructionsManual2' => 'Ð¡Ñ�Ñ‹Ð»ÐºÐ° Ð½Ð° Ñ�Ð°Ð¹Ñ‚ Ð²Ð°ÑˆÐµÐ³Ð¾ Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ�, Ð¸Ð»Ð¸ t.me/username',
             'apiAppInstructionsManual3' => 'Ñ‡Ñ‚Ð¾ ÑƒÐ³Ð¾Ð´Ð½Ð¾',
             'apiAppInstructionsManual4' => 'ÐžÐ¿Ð¸ÑˆÐ¸Ñ‚Ðµ Ð²Ð°ÑˆÐµ Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ðµ Ð·Ð´ÐµÑ�ÑŒ',
             'apiAppShortNameLabelWeb' => 'ÐšÐ¾Ñ€Ð¾Ñ‚ÐºÐ¾Ðµ Ð¸Ð¼Ñ�',
             'apiAppTitleLabelWeb' => 'Ð�Ð°Ð·Ð²Ð°Ð½Ð¸Ðµ Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ�',
             'apiAppUrlLabelWeb' => 'URL',
             'apiAppWeb' => 'Ð’Ð²ÐµÐ´Ð¸Ñ‚Ðµ API Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸ÑŽ',
             'apiAutoCodeLabelWeb' => 'ÐšÐ¾Ð´ Ð²Ñ…Ð¾Ð´Ð°',
             'apiAutoCodeLeadWeb' => 'my.telegram.org Ð¾Ñ‚Ð¿Ñ€Ð°Ð²Ð¸Ð» ÐºÐ¾Ð´ Ð²Ñ…Ð¾Ð´Ð° Ð² Ð²Ð°ÑˆÐµ Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ðµ Telegram, Ð² Ñ‡Ð°Ñ‚ Ñ� Telegram (Ð½Ðµ Ð¿Ð¾ SMS).',
             'apiAutoCodeTitleWeb' => 'Ð’Ð²ÐµÐ´Ð¸Ñ‚Ðµ ÐºÐ¾Ð´ Ð¸Ð· Telegram',
             'apiAutoErrorTitle' => 'my.telegram.org Ð²ÐµÑ€Ð½ÑƒÐ» Ð¾ÑˆÐ¸Ð±ÐºÑƒ',
             'apiAutoFallbackManual' => 'Ð’Ð¼ÐµÑ�Ñ‚Ð¾ Ñ�Ñ‚Ð¾Ð³Ð¾ Ð²Ð²ÐµÐ´Ð¸Ñ‚Ðµ API ID Ð¸ API hash Ð²Ñ€ÑƒÑ‡Ð½ÑƒÑŽ.',
             'apiAutoPhoneHintWeb' => 'my.telegram.org Ð¾Ñ‚Ð¿Ñ€Ð°Ð²Ð»Ñ�ÐµÑ‚ ÐºÐ¾Ð´ Ð²Ñ…Ð¾Ð´Ð° Ð² Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ðµ Telegram, Ð° Ð½Ðµ Ð¿Ð¾ SMS.',
             'apiAutoPrompt0' => 'Ð’Ð²ÐµÐ´Ð¸Ñ‚Ðµ Ð½Ð¾Ð¼ÐµÑ€ Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½Ð°, ÐºÐ¾Ñ‚Ð¾Ñ€Ñ‹Ð¹ ÑƒÐ¶Ðµ Ð·Ð°Ñ€ÐµÐ³Ð¸Ñ�Ñ‚Ñ€Ð¸Ñ€Ð¾Ð²Ð°Ð½ Ð² Telegram: ',
             'apiAutoPrompt1' => 'Ð’Ð²ÐµÐ´Ð¸Ñ‚Ðµ ÐºÐ¾Ð´ Ð¿Ñ€Ð¾Ð²ÐµÑ€ÐºÐ¸, ÐºÐ¾Ñ‚Ð¾Ñ€Ñ‹Ð¹ Ð²Ñ‹ Ð¿Ð¾Ð»ÑƒÑ‡Ð¸Ð»Ð¸ Ð² Telegram: ',
             'apiAutoWeb' => 'Ð’Ð²ÐµÐ´Ð¸Ñ‚Ðµ Ð½Ð¾Ð¼ÐµÑ€ Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½Ð°, ÐºÐ¾Ñ‚Ð¾Ñ€Ñ‹Ð¹ <b>ÑƒÐ¶Ðµ Ð·Ð°Ñ€ÐµÐ³Ð¸Ñ�Ñ‚Ñ€Ð¸Ñ€Ð¾Ð²Ð°Ð½</b> Ð² Telegram, Ñ‡Ñ‚Ð¾Ð±Ñ‹ Ð¿Ð¾Ð»ÑƒÑ‡Ð¸Ñ‚ÑŒ API ID',
             'apiChooseAutoWeb' => 'ÐŸÐ¾Ð»ÑƒÑ‡Ð¸Ñ‚ÑŒ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑ�ÐºÐ¸',
             'apiChooseManualAuto' => 'ÐŸÐ¾Ð»ÑƒÑ‡Ð¸Ñ‚ÑŒ API ID Ð¸ API hash Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑ�ÐºÐ¸ Ñ� my.telegram.org (a) Ð¸Ð»Ð¸ Ð²Ð²ÐµÑ�Ñ‚Ð¸ Ð¸Ñ… Ð²Ñ€ÑƒÑ‡Ð½ÑƒÑŽ (m)?',
             'apiChooseManualAutoTip' => 'Ð¢Ð°ÐºÐ¶Ðµ Ð¼Ð¾Ð¶Ð½Ð¾ ÑƒÐºÐ°Ð·Ð°Ñ‚ÑŒ API ID/hash Ñ�Ñ€Ð°Ð·Ñƒ Ð² ÐºÐ¾Ð´Ðµ Ð¸Ñ�Ð¿Ð¾Ð»ÑŒÐ·ÑƒÑ� Ð½Ð°Ñ�Ñ‚Ñ€Ð¾Ð¹ÐºÐ¸: %s',
             'apiChooseManualAutoTipWeb' => 'Ð¢Ð°ÐºÐ¶Ðµ Ð¼Ð¾Ð¶Ð½Ð¾ ÑƒÐºÐ°Ð·Ð°Ñ‚ÑŒ API ID/hash Ñ�Ñ€Ð°Ð·Ñƒ Ð² ÐºÐ¾Ð´Ðµ Ñ� Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ <a target="_blank" href="%s">Ð½Ð°Ñ�Ñ‚Ñ€Ð¾ÐµÐº</a>.',
             'apiChooseManualWeb' => 'Ð£ Ð¼ÐµÐ½Ñ� ÑƒÐ¶Ðµ ÐµÑ�Ñ‚ÑŒ API ID',
             'apiChoosePrompt' => 'Ð’Ð°Ñˆ Ð²Ñ‹Ð±Ð¾Ñ€ (m/a): ',
             'apiCreateErrorTitle' => 'my.telegram.org Ð½Ðµ Ñ�Ð¾Ð·Ð´Ð°Ð» Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ðµ',
             'apiCreateLeadWeb' => 'Ð£ Ñ�Ñ‚Ð¾Ð³Ð¾ Ð°ÐºÐºÐ°ÑƒÐ½Ñ‚Ð° Ð¿Ð¾ÐºÐ° Ð½ÐµÑ‚ Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ� Ð½Ð° my.telegram.org. Ð­Ñ‚Ð¸ Ð´Ð°Ð½Ð½Ñ‹Ðµ Ð²Ð¸Ð´Ð½Ñ‹ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ Ð²Ð°Ð¼, Ð¸ Ð¸Ñ… Ð¼Ð¾Ð¶Ð½Ð¾ Ð¸Ð·Ð¼ÐµÐ½Ð¸Ñ‚ÑŒ Ð¿Ð¾Ð·Ð¶Ðµ.',
             'apiCreateTitleWeb' => 'Ð¡Ð¾Ð·Ð´Ð°Ð¹Ñ‚Ðµ Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ðµ',
             'apiCreateVpsWarning' => 'Telegram Ð½Ðµ Ð¿Ð¾Ð·Ð²Ð¾Ð»Ñ�ÐµÑ‚ Ñ�Ð¾Ð·Ð´Ð°Ð²Ð°Ñ‚ÑŒ Ð½Ð¾Ð²Ñ‹Ðµ Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ� Ð½Ð° my.telegram.org Ñ� IP-Ð°Ð´Ñ€ÐµÑ�Ð¾Ð² VPS, VPN Ð¸ Ð´Ñ€ÑƒÐ³Ð¸Ñ… Ð´Ð°Ñ‚Ð°-Ñ†ÐµÐ½Ñ‚Ñ€Ð¾Ð², Ð° Ð·Ð°Ð¿Ñ€Ð¾Ñ� Ð±Ñ‹Ð» Ð¾Ñ‚Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½ Ñ� IP Ð¼Ð°ÑˆÐ¸Ð½Ñ‹, Ð½Ð° ÐºÐ¾Ñ‚Ð¾Ñ€Ð¾Ð¹ Ñ€Ð°Ð±Ð¾Ñ‚Ð°ÐµÑ‚ MadelineProto. Ð¡Ð¾Ð·Ð´Ð°Ð¹Ñ‚Ðµ Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ðµ Ð²Ñ€ÑƒÑ‡Ð½ÑƒÑŽ Ð½Ð° https://my.telegram.org Ñ� Ð´Ð¾Ð¼Ð°ÑˆÐ½ÐµÐ³Ð¾ Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ� (Ð½Ð°Ð¿Ñ€Ð¸Ð¼ÐµÑ€, Ñ� Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½Ð° Ñ‡ÐµÑ€ÐµÐ· Ð¼Ð¾Ð±Ð¸Ð»ÑŒÐ½Ñ‹Ð¹ Ð¸Ð½Ñ‚ÐµÑ€Ð½ÐµÑ‚), Ð·Ð°Ñ‚ÐµÐ¼ Ð²Ð²ÐµÐ´Ð¸Ñ‚Ðµ API ID Ð¸ API hash.',
             'apiCreateWeb' => 'Ð¡Ð¾Ð·Ð´Ð°Ñ‚ÑŒ Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ðµ',
             'apiError' => 'ÐžÐ¨Ð˜Ð‘ÐšÐ�: %s. ÐŸÐ¾Ð¿Ñ€Ð¾Ð±ÑƒÐ¹Ñ‚Ðµ Ñ�Ð½Ð¾Ð²Ð°.',
             'apiInvalidWeb' => 'API ID â€” Ñ�Ñ‚Ð¾ Ñ‡Ð¸Ñ�Ð»Ð¾, Ð° API hash Ñ�Ð¾Ñ�Ñ‚Ð¾Ð¸Ñ‚ Ð¸Ð· 32 Ñ�Ð¸Ð¼Ð²Ð¾Ð»Ð¾Ð²: Ñ�ÐºÐ¾Ð¿Ð¸Ñ€ÑƒÐ¹Ñ‚Ðµ Ð¸Ñ… ÐµÑ‰Ñ‘ Ñ€Ð°Ð· Ñ� my.telegram.org.',
             'apiManualInstructions0' => 'Ð’Ð¾Ð¹Ð´Ð¸Ñ‚Ðµ Ð² https://my.telegram.org',
             'apiManualInstructions1' => 'Ð�Ð°Ð¶Ð¼Ð¸Ñ‚Ðµ Ð½Ð° "API development tools"',
             'apiManualInstructions2' => 'Ð’Ñ‹Ð±ÐµÑ€Ð¸Ñ‚Ðµ "create application"',
             'apiManualPrompt0' => 'Ð’Ð²ÐµÐ´Ð¸Ñ‚Ðµ Ð²Ð°Ñˆ API ID: ',
             'apiManualPrompt1' => 'Ð’Ð²ÐµÐ´Ð¸Ñ‚Ðµ Ð²Ð°Ñˆ API hash: ',
             'apiManualWeb' => 'Ð’Ð²ÐµÐ´Ð¸Ñ‚Ðµ Ð²Ð°Ñˆ API ID Ð¸ API hash',
             'apiParamsError' => 'Ð’Ñ‹ Ð½Ðµ Ð·Ð°Ð¿Ð¾Ð»Ð½Ð¸Ð»Ð¸ Ð²Ñ�Ðµ Ð¾Ð±Ñ�Ð·Ð°Ñ‚ÐµÐ»ÑŒÐ½Ñ‹Ðµ Ð¿Ð°Ñ€Ð°Ð¼ÐµÑ‚Ñ€Ñ‹!',
             'apiSaveWeb' => 'Ð¡Ð¾Ñ…Ñ€Ð°Ð½Ð¸Ñ‚ÑŒ Ð¸ Ð¿Ñ€Ð¾Ð´Ð¾Ð»Ð¶Ð¸Ñ‚ÑŒ',
             'apiSendCodeWeb' => 'ÐžÑ‚Ð¿Ñ€Ð°Ð²Ð¸Ñ‚ÑŒ ÐºÐ¾Ð´',
             'apiSetupLeadWeb' => 'ÐšÐ°Ð¶Ð´Ð¾Ð¼Ñƒ Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸ÑŽ Telegram Ð½ÑƒÐ¶Ð½Ñ‹ API ID Ð¸ API hash. MadelineProto Ð¼Ð¾Ð¶ÐµÑ‚ Ð¿Ð¾Ð»ÑƒÑ‡Ð¸Ñ‚ÑŒ Ð¸Ñ… Ð·Ð° Ð²Ð°Ñ� Ñ� my.telegram.org Ð¿Ð¾ Ð½Ð¾Ð¼ÐµÑ€Ñƒ Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½Ð° Ñ�ÑƒÑ‰ÐµÑ�Ñ‚Ð²ÑƒÑŽÑ‰ÐµÐ³Ð¾ Ð°ÐºÐºÐ°ÑƒÐ½Ñ‚Ð° Telegram.',
             'apiSetupTitleWeb' => 'ÐŸÐ¾Ð»ÑƒÑ‡Ð¸Ñ‚Ðµ API ID Ð¸ API hash',
             'apiStartOverWeb' => 'Ð�Ð°Ñ‡Ð°Ñ‚ÑŒ Ð·Ð°Ð½Ð¾Ð²Ð¾',
             'api_not_set' => 'Ð�ÐµÐ¾Ð±Ñ…Ð¾Ð´Ð¸Ð¼Ð¾ ÑƒÐºÐ°Ð·Ð°Ñ‚ÑŒ api key Ð¸ api id, Ð¿Ð¾Ð»ÑƒÑ‡Ð¸Ñ‚Ðµ Ñ�Ð²Ð¾Ð¸ Ð½Ð° my.telegram.org',
             'array_invalid' => 'Ð’Ñ‹ ÑƒÐºÐ°Ð·Ð°Ð»Ð¸ Ð½ÐµÐ²Ð°Ð»Ð¸Ð´Ð½Ñ‹Ð¹ Ð¼Ð°Ñ�Ñ�Ð¸Ð²',
             'baseDirLimitation' => 'Ð�Ð°Ñ�Ñ‚Ñ€Ð¾Ð¹ÐºÐ° basedir Ñ�ÐºÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð°: Ñ�Ñ‚Ð¾ Ð¼Ð¾Ð¶ÐµÑ‚ Ð¿Ð¾Ð²Ð»Ð¸Ñ�Ñ‚ÑŒ Ð½Ð° Ð¿Ñ€Ð¾Ð¸Ð·Ð²Ð¾Ð´Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ñ�Ñ‚ÑŒ Ð¸ Ð²Ñ‹Ð·Ð²Ð°Ñ‚ÑŒ Ð½ÐµÐºÐ¾Ñ‚Ð¾Ñ€Ñ‹Ðµ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼Ñ‹, Ð¾Ñ‚ÐºÐ»ÑŽÑ‡Ð¸Ñ‚Ðµ ÐµÑ‘ ÐµÑ�Ð»Ð¸ Ñ�Ñ‚Ð¾ Ð²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾!',
             'bool_error' => 'Ð�ÐµÐ²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾ Ñ�Ñ‡Ð¸Ñ‚Ð°Ñ‚ÑŒ boolean',
             'botAlreadyRunning' => 'Ð‘Ð¾Ñ‚ ÑƒÐ¶Ðµ Ð·Ð°Ð¿ÑƒÑ‰ÐµÐ½!',
             'botapi_conversion_error' => 'Ð�ÐµÐ²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾ Ñ�ÐºÐ¾Ð½Ð²ÐµÑ€Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ñ‚ÑŒ %s Ð² Ð¾Ð±ÑŠÐµÐºÑ‚ Bot API',
             'call_already_accepted' => 'Ð—Ð²Ð¾Ð½Ð¾Ðº %s ÑƒÐ¶Ðµ Ð±Ñ‹Ð» Ð¿Ñ€Ð¸Ð½Ñ�Ñ‚',
             'call_already_declined' => 'Ð—Ð²Ð¾Ð½Ð¾Ðº %s ÑƒÐ¶Ðµ Ð±Ñ‹Ð» Ð¾Ñ‚Ð²ÐµÑ€Ð³Ð½ÑƒÑ‚',
             'call_completing' => 'Ð¤Ð¸Ð½Ð°Ð»Ð¸Ð·Ð¸Ñ€ÑƒÑŽ Ð·Ð²Ð¾Ð½Ð¾Ðº Ð¾Ñ‚ %s...',
             'call_confirming' => 'ÐŸÐ¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´Ð°ÑŽ Ð·Ð²Ð¾Ð½Ð¾Ðº Ð¾Ñ‚ %s...',
             'call_discarding' => 'ÐžÑ‚Ð²ÐµÑ€Ð³Ð°ÑŽ Ð·Ð²Ð¾Ð½Ð¾Ðº %s...',
             'call_error_1' => 'Ð�Ðµ Ñ�Ð¼Ð¾Ð³ Ð½Ð°Ð¹Ñ‚Ð¸ Ð¸ Ð¿Ñ€Ð¸Ð½Ñ�Ñ‚ÑŒ Ð·Ð²Ð¾Ð½Ð¾Ðº %s',
             'call_error_2' => 'Ð�ÐµÐ²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾ Ð½Ð°Ð¹Ñ‚Ð¸ Ð¸ Ð¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¸Ñ‚ÑŒ Ð·Ð²Ð¾Ð½Ð¾Ðº %s',
             'call_error_3' => 'Ð�ÐµÐ²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾ Ð½Ð°Ð¹Ñ‚Ð¸ Ð¸ Ð·Ð°Ð²ÐµÑ€ÑˆÐ¸Ñ‚ÑŒ Ð·Ð²Ð¾Ð½Ð¾Ðº %s',
             'cli_need_dl.php_link' => 'ÐŸÐ¾Ð¶Ð°Ð»ÑƒÐ¹Ñ�Ñ‚Ð°, ÑƒÐºÐ°Ð¶Ð¸Ñ‚Ðµ URL Ñ�ÐºÑ€Ð¸Ð¿Ñ‚Ð° Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸ Ð¿Ñ€Ð¸ Ð¸Ñ�Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ð¸ getDownloadLink Ñ‡ÐµÑ€ÐµÐ· CLI!',
             'constructor_not_found' => 'Ð�Ðµ Ð½Ð°Ð¹Ð´ÐµÐ½ ÐºÐ¾Ð½Ñ�Ñ‚Ñ€ÑƒÐºÑ‚Ð¾Ñ€ Ð´Ð»Ñ� Ñ‚Ð¸Ð¿Ð°: ',
             'could_not_connect_to_MadelineProto' => 'Ð�Ðµ ÑƒÐ´Ð°Ð»Ð¾Ñ�ÑŒ Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡Ð¸Ñ‚ÑŒÑ�Ñ� Ðº MadelineProto, Ñ‡Ñ‚Ð¾Ð±Ñ‹ Ð¸Ñ�Ð¿Ñ€Ð°Ð²Ð¸Ñ‚ÑŒ, Ð²ÐºÐ»ÑŽÑ‡Ð¸Ñ‚Ðµ proc_open Ð¸ ÑƒÐ±ÐµÑ€Ð¸Ñ‚Ðµ Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ� open_basedir Ð¸Ð»Ð¸ Ð¾Ñ‚ÐºÐ»ÑŽÑ‡Ð¸Ñ‚Ðµ Ð¿ÐµÑ€ÐµÐ·Ð°Ð¿Ð¸Ñ�ÑŒ Ð¿ÑƒÑ‚ÐµÐ¹ Ð²ÐµÐ±-Ñ�ÐµÑ€Ð²ÐµÑ€Ð°! Ð•Ñ�Ð»Ð¸ Ð²Ñ‹ ÑƒÐ¶Ðµ Ñ�Ñ‚Ð¾ Ñ�Ð´ÐµÐ»Ð°Ð»Ð¸, ÑƒÐ±ÐµÐ´Ð¸Ñ‚ÐµÑ�ÑŒ, Ñ‡Ñ‚Ð¾ CLI Ð²ÐµÑ€Ñ�Ð¸Ñ� PHP Ñ‚Ð¾Ñ‡Ð½Ð¾ Ñ‚Ð°ÐºÐ°Ñ� Ð¶Ðµ, ÐºÐ°Ðº Ð²ÐµÐ±-Ð²ÐµÑ€Ñ�Ð¸Ñ� (Ñ‚Ð° Ð¶Ðµ Ð²ÐµÑ€Ñ�Ð¸Ñ�, Ñ€Ð°Ñ�ÑˆÐ¸Ñ€ÐµÐ½Ð¸Ñ� Ð¸ Ñ‚.Ð´.), Ð¸ Ð¿Ð¾Ñ�Ð¼Ð¾Ñ‚Ñ€Ð¸Ñ‚Ðµ Ñ„Ð°Ð¹Ð» MadelineProto.log, Ñ‡Ñ‚Ð¾Ð±Ñ‹ ÑƒÐ·Ð½Ð°Ñ‚ÑŒ Ð¿Ð¾Ð´Ñ€Ð¾Ð±Ð½ÐµÐµ Ð¾Ð± Ð¾ÑˆÐ¸Ð±ÐºÐµ, Ð¸Ð·-Ð·Ð° ÐºÐ¾Ñ‚Ð¾Ñ€Ð¾Ð¹ Ð½Ðµ Ð·Ð°Ð¿ÑƒÑ�Ñ‚Ð¸Ð»Ñ�Ñ� IPC Ñ�ÐµÑ€Ð²ÐµÑ€.',
             'could_not_convert_object' => 'Ð�ÐµÐ²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾ Ñ�ÐºÐ¾Ð½Ð²ÐµÑ€Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ñ‚ÑŒ Ð¾Ð±ÑŠÐµÐºÑ‚ Ñ‚Ð¸Ð¿Ð° %s',
             'deserialization_error' => 'ÐŸÑ€Ð¾Ð¸Ð·Ð¾ÑˆÐ»Ð° Ð¾ÑˆÐ¸Ð±ÐºÐ° Ð¿Ñ€Ð¸ Ð´ÐµÑ�ÐµÑ€Ð¸Ð°Ð»Ð¸Ð·Ð°Ñ†Ð¸Ð¸',
             'dl.php_check_logs_make_sure_session_running' => 'Ð¡Ð²Ñ�Ð·Ð°Ð½Ð½Ñ‹Ð¹ Ð±Ð¾Ñ‚ EventHandler MadelineProto Ð¸Ð»Ð¸ IPC Ñ�ÐµÑ€Ð²ÐµÑ€ MadelineProto Ð½Ðµ Ð·Ð°Ð¿ÑƒÑ‰ÐµÐ½, Ð¿Ð¾Ð¶Ð°Ð»ÑƒÐ¹Ñ�Ñ‚Ð°, Ð¿Ñ€Ð¾Ð²ÐµÑ€ÑŒÑ‚Ðµ Ð»Ð¾Ð³Ð¸ Ð¸ ÑƒÐ±ÐµÐ´Ð¸Ñ‚ÐµÑ�ÑŒ, Ñ‡Ñ‚Ð¾ Ð·Ð°Ð¿ÑƒÑ‰ÐµÐ½ Ñ…Ð¾Ñ‚Ñ� Ð±Ñ‹ Ð¾Ð´Ð¸Ð½ Ð¸Ð· Ð½Ð¸Ñ…!',
             'dl.php_powered_by_madelineproto' => 'Ð¡ÐµÑ€Ð²ÐµÑ€ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸ Ñ„Ð°Ð¹Ð»Ð¾Ð² Telegram (Ð´Ð¾ 4GB) Ð½Ð° Ð±Ð°Ð·Ðµ <a href="https://docs.madelineproto.xyz" target="_blank">MadelineProto</a>!<br>Ð�Ð°Ð¶Ð¼Ð¸Ñ‚Ðµ <a href="https://docs.madelineproto.xyz/docs/FILES.html#getting-a-download-link" target="_blank">Ñ�ÑŽÐ´Ð°</a>, Ñ‡Ñ‚Ð¾Ð±Ñ‹ ÑƒÐ·Ð½Ð°Ñ‚ÑŒ, ÐºÐ°Ðº Ð½Ð°Ñ�Ñ‚Ñ€Ð¾Ð¸Ñ‚ÑŒ Ñ�Ð²Ð¾Ð¹ Ñ�Ð¾Ð±Ñ�Ñ‚Ð²ÐµÐ½Ð½Ñ‹Ð¹ Ñ�ÐµÑ€Ð²ÐµÑ€ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸ Ñ„Ð°Ð¹Ð»Ð¾Ð² Telegram!',
             'do_not_delete_MadelineProto.log' => 'Ñ„Ð°Ð¹Ð» MadelineProto.log Ð½Ð¸ÐºÐ¾Ð³Ð´Ð° Ð½ÐµÐ»ÑŒÐ·Ñ� ÑƒÐ´Ð°Ð»Ñ�Ñ‚ÑŒ, Ð²Ð¼ÐµÑ�Ñ‚Ð¾ Ñ�Ñ‚Ð¾Ð³Ð¾ ÑƒÐºÐ°Ð¶Ð¸Ñ‚Ðµ Ñ�Ð²Ð¾Ð¹ Ð¼Ð°ÐºÑ�Ð¸Ð¼Ð°Ð»ÑŒÐ½Ñ‹Ð¹ Ñ€Ð°Ð·Ð¼ÐµÑ€ Ð² Ð½Ð°Ñ�Ñ‚Ñ€Ð¾Ð¹ÐºÐ°Ñ…!',
             'do_not_remove_MadelineProto.log_phar' => 'ÐŸÐ¾Ð¶Ð°Ð»ÑƒÐ¹Ñ�Ñ‚Ð°, Ð½Ðµ ÑƒÐ´Ð°Ð»Ñ�Ð¹Ñ‚Ðµ madeline.phar Ð¸Ð»Ð¸ madeline.php, Ð¸Ð½Ð°Ñ‡Ðµ MadelineProto ÑƒÐ¿Ð°Ð´Ñ‘Ñ‚. Ð•Ñ�Ð»Ð¸ Ñƒ Ð²Ð°Ñ� Ð²Ð¾Ð·Ð½Ð¸ÐºÐ»Ð¸ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼Ñ‹ Ñ� MadelineProto, Ñ�Ð¾Ð¾Ð±Ñ‰Ð¸Ñ‚Ðµ Ð¾ Ð½Ð¸Ñ… Ð½Ð° https://github.com/danog/MadelineProto Ð¸Ð»Ð¸ Ð² https://t.me/pwrtelegramgroup',
             'do_not_use_blocking_class' => 'Ð¿Ð¾ Ñ�Ð¾Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ�Ð¼ Ð¿Ñ€Ð¾Ð¸Ð·Ð²Ð¾Ð´Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ñ�Ñ‚Ð¸ handler\'Ñ‹ Ð½Ðµ Ð´Ð¾Ð»Ð¶Ð½Ñ‹ Ð¸Ñ�Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÑŒ Ð½ÐµÐ°Ñ�Ð¸Ð½Ñ…Ñ€Ð¾Ð½Ð½Ñ‹Ð¹ Ð±Ð»Ð¾ÐºÐ¸Ñ€ÑƒÑŽÑ‰Ð¸Ð¹ ÐºÐ»Ð°Ñ�Ñ� %s, Ð¸Ñ�Ð¿Ð¾Ð»ÑŒÐ·ÑƒÐ¹Ñ‚Ðµ Ð²Ð¼ÐµÑ�Ñ‚Ð¾ Ð½ÐµÐ³Ð¾ %s',
             'do_not_use_blocking_function' => 'Ð¿Ð¾ Ñ�Ð¾Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ�Ð¼ Ð¿Ñ€Ð¾Ð¸Ð·Ð²Ð¾Ð´Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ñ�Ñ‚Ð¸ event handler\'Ñ‹ Ð½Ðµ Ð´Ð¾Ð»Ð¶Ð½Ñ‹ Ð¸Ñ�Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÑŒ Ð½ÐµÐ°Ñ�Ð¸Ð½Ñ…Ñ€Ð¾Ð½Ð½ÑƒÑŽ Ð±Ð»Ð¾ÐºÐ¸Ñ€ÑƒÑŽÑ‰ÑƒÑŽ Ñ„ÑƒÐ½ÐºÑ†Ð¸ÑŽ %s, Ð¸Ñ�Ð¿Ð¾Ð»ÑŒÐ·ÑƒÐ¹Ñ‚Ðµ Ð²Ð¼ÐµÑ�Ñ‚Ð¾ Ð½ÐµÑ‘ %s',
             'do_not_use_deprecated_function' => 'Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ� %s ÑƒÑ�Ñ‚Ð°Ñ€ÐµÐ»Ð°, Ð¸Ñ�Ð¿Ð¾Ð»ÑŒÐ·ÑƒÐ¹Ñ‚Ðµ Ð²Ð¼ÐµÑ�Ñ‚Ð¾ Ð½ÐµÑ‘ %s',
             'do_not_use_non_root_require_in_event_handler' => 'Ð¿Ð¾ Ñ�Ð¾Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ�Ð¼ Ð¿Ñ€Ð¾Ð¸Ð·Ð²Ð¾Ð´Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ñ�Ñ‚Ð¸ Ð½ÐµÐ»ÑŒÐ·Ñ� Ð¸Ñ�Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÑŒ require Ð¸Ð»Ð¸ include Ð²Ð½ÑƒÑ‚Ñ€Ð¸ ÐºÐ»Ð°Ñ�Ñ�Ð° event handler\'Ð°, Ñ€Ð°Ð·Ñ€ÐµÑˆÐµÐ½Ñ‹ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ require Ð½Ð° Ð²ÐµÑ€Ñ…Ð½ÐµÐ¼ ÑƒÑ€Ð¾Ð²Ð½Ðµ Ñ„Ð°Ð¹Ð»Ð°.',
             'do_not_use_yield' => 'MadelineProto 8 Ð½Ðµ Ñ‚Ñ€ÐµÐ±ÑƒÐµÑ‚ Ð¸ Ð½Ðµ Ð¿Ð¾Ð´Ð´ÐµÑ€Ð¶Ð¸Ð²Ð°ÐµÑ‚ Ð¸Ñ�Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ðµ yield Ð² Ð°Ñ�Ð¸Ð½Ñ…Ñ€Ð¾Ð½Ð½Ñ‹Ñ… Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ�Ñ…, Ð½ÐµÐ¾Ð±Ñ…Ð¾Ð´Ð¸Ð¼Ð¾ ÑƒÐ´Ð°Ð»Ð¸Ñ‚ÑŒ Ð²Ñ�Ðµ ÐºÐ»ÑŽÑ‡ÐµÐ²Ñ‹Ðµ Ñ�Ð»Ð¾Ð²Ð° yield, ÐºÐ¾Ñ‚Ð¾Ñ€Ñ‹Ðµ Ñ€Ð°Ð½ÐµÐµ Ð¸Ñ�Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð»Ð¸Ñ�ÑŒ Ð´Ð»Ñ� Ð²Ñ‹Ð·Ð¾Ð²Ð° Ð°Ñ�Ð¸Ð½Ñ…Ñ€Ð¾Ð½Ð½Ñ‹Ñ… Ñ„ÑƒÐ½ÐºÑ†Ð¸Ð¹',
             'done' => 'Ð“Ð¾Ñ‚Ð¾Ð²Ð¾!',
             'encode_double_error' => 'Ð�ÐµÐ²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾ ÐºÐ¾Ñ€Ñ€ÐµÐºÑ‚Ð½Ð¾ Ð·Ð°ÐºÐ¾Ð´Ð¸Ñ€Ð¾Ð²Ð°Ñ‚ÑŒ double',
             'extensionRecommended' => 'Ð’Ð½Ð¸Ð¼Ð°Ð½Ð¸Ðµ: Ñ€Ð°Ñ�ÑˆÐ¸Ñ€ÐµÐ½Ð¸Ðµ %s Ð½Ðµ ÑƒÑ�Ñ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð¾, Ð¿Ñ€Ð¾Ñ�ÑŒÐ±Ð° ÐµÐ³Ð¾ ÑƒÑ�Ñ‚Ð°Ð½Ð¾Ð²Ð¸Ñ‚ÑŒ Ð´Ð»Ñ� ÑƒÑ�ÐºÐ¾Ñ€ÐµÐ½Ð¸Ñ� MadelineProto!',
             'extensionRequired' => 'MadelineProto Ð½ÑƒÐ¶Ð´Ð°ÐµÑ‚Ñ�Ñ� Ð² Ñ€Ð°Ñ�ÑˆÐ¸Ñ€ÐµÐ½Ð¸Ð¸ %s. %s',
             'extensionRequiredInstallWithApt' => 'ÐŸÐ¾Ð¿Ñ€Ð¾Ð±ÑƒÐ¹Ñ‚Ðµ Ð·Ð°Ð¿ÑƒÑ�Ñ‚Ð¸Ñ‚ÑŒ sudo apt-get install %s.',
             'extensionRequiredInstallWithCustomInstructions' => 'Ð¡Ð»ÐµÐ´ÑƒÐ¹Ñ‚Ðµ Ð¸Ð½Ñ�Ñ‚Ñ€ÑƒÐºÑ†Ð¸Ñ�Ð¼ Ð² %s Ð´Ð»Ñ� ÑƒÑ�Ñ‚Ð°Ð½Ð¾Ð²ÐºÐ¸.',
             'file_not_exist' => 'Ð¤Ð°Ð¹Ð» Ð½Ðµ Ñ�ÑƒÑ‰ÐµÑ�Ñ‚Ð²ÑƒÐµÑ‚',
             'file_parsing' => 'Ð§Ñ‚ÐµÐ½Ð¸Ðµ %s...',
             'file_type_invalid' => 'ÐžÐ±Ð½Ð°Ñ€ÑƒÐ¶ÐµÐ½ Ð½ÐµÐ²Ð°Ð»Ð¸Ð´Ð½Ñ‹Ð¹ Ñ‚Ð¸Ð¿ Ñ„Ð°Ð¹Ð»Ð° (%s)',
             'fingerprint_invalid' => 'Ð�ÐµÐ²Ð°Ð»Ð¸Ð´Ð½Ñ‹Ð¹ Ñ„Ð¸Ð½Ð³ÐµÑ€Ð¿Ñ€Ð¸Ð½Ñ‚ ÐºÐ»ÑŽÑ‡Ð°!',
             'go' => 'ÐžÐš',
             'invalid_dl.php_session' => '%s Ð½Ðµ Ñ�Ð²Ð»Ñ�ÐµÑ‚Ñ�Ñ� Ð²Ð°Ð»Ð¸Ð´Ð½Ñ‹Ð¼ Ñ�ÐºÑ€Ð¸Ð¿Ñ‚Ð¾Ð¼ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸, Ñ‚Ð°Ðº ÐºÐ°Ðº ÐµÐ³Ð¾ ID Ñ�ÐµÑ�Ñ�Ð¸Ð¸ Ð¾Ñ‚Ð»Ð¸Ñ‡Ð°ÐµÑ‚Ñ�Ñ� (Ð¾Ð¶Ð¸Ð´Ð°Ð»Ñ�Ñ� %s, Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½ %s)',
             'ipc_tcp_warning' => 'UNIX sockets are not available, so the IPC server of the session listens on TCP (127.0.0.1) instead: any program running on this machine can connect to it, and control the session. On Windows 10 1803+, enable the sockets extension (extension=sockets in php.ini) to use UNIX sockets.',
             'length_too_big' => 'Ð”Ð»Ð¸Ð½Ð° Ñ�Ð»Ð¸ÑˆÐºÐ¾Ð¼ Ð±Ð¾Ð»ÑŒÑˆÐ°Ñ�',
             'loginBot' => 'Ð’Ð²ÐµÐ´Ð¸Ñ‚Ðµ Ñ‚Ð¾ÐºÐµÐ½ Ð±Ð¾Ñ‚Ð°: ',
             'loginBotTokenHintWeb' => 'Ð¢Ð¾ÐºÐµÐ½, ÐºÐ¾Ñ‚Ð¾Ñ€Ñ‹Ð¹ Ð²Ñ‹Ð´Ð°Ð» @BotFather Ð¿Ñ€Ð¸ Ñ�Ð¾Ð·Ð´Ð°Ð½Ð¸Ð¸ Ð±Ð¾Ñ‚Ð°.',
             'loginBotTokenWeb' => 'Ð¢Ð¾ÐºÐµÐ½ Ð±Ð¾Ñ‚Ð°',
             'loginChoosePromptWeb' => 'Ð�Ð²Ñ‚Ð¾Ñ€Ð¸Ð·Ð¾Ð²Ð°Ñ‚ÑŒÑ�Ñ� ÐºÐ°Ðº Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»ÑŒ Ð¸Ð»Ð¸ ÐºÐ°Ðº Ð±Ð¾Ñ‚?',
             'loginCodeLeadWeb' => 'Telegram Ð¾Ñ‚Ð¿Ñ€Ð°Ð²Ð¸Ð» ÐºÐ¾Ð´ Ð²Ñ…Ð¾Ð´Ð° Ð² Ð²Ð°ÑˆÐµ Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ðµ Telegram Ð¸Ð»Ð¸ Ð¿Ð¾ SMS, ÐµÑ�Ð»Ð¸ Ð²Ñ‹ Ð±Ð¾Ð»ÑŒÑˆÐµ Ð½Ð¸Ð³Ð´Ðµ Ð½Ðµ Ð°Ð²Ñ‚Ð¾Ñ€Ð¸Ð·Ð¾Ð²Ð°Ð½Ñ‹.',
             'loginCodeTitleWeb' => 'Ð’Ð²ÐµÐ´Ð¸Ñ‚Ðµ ÐºÐ¾Ð´ Ð²Ñ…Ð¾Ð´Ð°',
             'loginLeadWeb' => 'Ð’Ñ‹Ð±ÐµÑ€Ð¸Ñ‚Ðµ Ð°ÐºÐºÐ°ÑƒÐ½Ñ‚ Ð¸Ð»Ð¸ Ð±Ð¾Ñ‚Ð°, Ð¾Ñ‚ Ð¸Ð¼ÐµÐ½Ð¸ ÐºÐ¾Ñ‚Ð¾Ñ€Ð¾Ð³Ð¾ Ð±ÑƒÐ´ÐµÑ‚ Ñ€Ð°Ð±Ð¾Ñ‚Ð°Ñ‚ÑŒ Ñ�Ñ‚Ð¾Ñ‚ Ñ�ÐºÐ·ÐµÐ¼Ð¿Ð»Ñ�Ñ€ MadelineProto.',
             'loginManual' => 'Ð’Ñ‹ Ñ‚Ð°ÐºÐ¶Ðµ Ð¼Ð¾Ð¶ÐµÑ‚Ðµ Ð²Ð²ÐµÑ�Ñ‚Ð¸ Ñ‚Ð¾ÐºÐµÐ½ Ð±Ð¾Ñ‚Ð° Ð¸Ð»Ð¸ Ð½Ð¾Ð¼ÐµÑ€ Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½Ð° Ñ‡Ñ‚Ð¾Ð±Ñ‹ Ð²Ð¾Ð¹Ñ‚Ð¸ Ð²Ñ€ÑƒÑ‡Ð½ÑƒÑŽ: ',
             'loginNoCode' => 'Ð’Ñ‹ Ð½Ðµ Ð²Ð²ÐµÐ»Ð¸ ÐºÐ¾Ð´!',
             'loginNoName' => 'Ð’Ñ‹ Ð½Ðµ Ð²Ð²ÐµÐ»Ð¸ Ð¸Ð¼Ñ�!',
             'loginNoPass' => 'Ð’Ñ‹ Ð½Ðµ Ð²Ð²ÐµÐ»Ð¸ Ð¿Ð°Ñ€Ð¾Ð»ÑŒ!',
             'loginOptionBot' => 'Ð‘Ð¾Ñ‚',
             'loginOptionUser' => 'ÐŸÐ¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»ÑŒ',
             'loginPasswordLabelWeb' => 'ÐŸÐ°Ñ€Ð¾Ð»ÑŒ',
             'loginPasswordLeadWeb' => 'Ð­Ñ‚Ð¾Ñ‚ Ð°ÐºÐºÐ°ÑƒÐ½Ñ‚ Ð·Ð°Ñ‰Ð¸Ñ‰Ñ‘Ð½ Ð¿Ð°Ñ€Ð¾Ð»ÐµÐ¼, Ð·Ð°Ð´Ð°Ð½Ð½Ñ‹Ð¼ Ð² Ñ€Ð°Ð·Ð´ÐµÐ»Ðµ Ð�Ð°Ñ�Ñ‚Ñ€Ð¾Ð¹ÐºÐ¸ > ÐšÐ¾Ð½Ñ„Ð¸Ð´ÐµÐ½Ñ†Ð¸Ð°Ð»ÑŒÐ½Ð¾Ñ�Ñ‚ÑŒ > ÐžÐ±Ð»Ð°Ñ‡Ð½Ñ‹Ð¹ Ð¿Ð°Ñ€Ð¾Ð»ÑŒ.',
             'loginPasswordTitleWeb' => 'Ð”Ð²ÑƒÑ…Ñ�Ñ‚Ð°Ð¿Ð½Ð°Ñ� Ð°ÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ñ�',
             'loginQr' => 'ÐžÑ‚ÐºÑ€Ð¾Ð¹Ñ‚Ðµ Telegram Ð½Ð° Ñ�Ð¼Ð°Ñ€Ñ‚Ñ„Ð¾Ð½Ðµ, Ð¿ÐµÑ€ÐµÐ¹Ð´Ð¸Ñ‚Ðµ Ð² Ð�Ð°Ñ�Ñ‚Ñ€Ð¾Ð¹ÐºÐ¸ > Ð£Ñ�Ñ‚Ñ€Ð¾Ð¹Ñ�Ñ‚Ð²Ð° > ÐŸÐ¾Ð´ÐºÐ»ÑŽÑ‡Ð¸Ñ‚ÑŒ ÑƒÑ�Ñ‚Ñ€Ð¾Ð¹Ñ�Ñ‚Ð²Ð¾ Ð¸ Ð¾Ñ‚Ñ�ÐºÐ°Ð½Ð¸Ñ€ÑƒÐ¹Ñ‚Ðµ QR ÐºÐ¾Ð´ Ð²Ñ‹ÑˆÐµ, Ñ‡Ñ‚Ð¾Ð±Ñ‹ Ð²Ð¾Ð¹Ñ‚Ð¸ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑ�ÐºÐ¸.',
             'loginQrCodeExpired' => 'QR ÐºÐ¾Ð´ Ð±Ð¾Ð»ÑŒÑˆÐµ Ð½Ðµ Ð´ÐµÐ¹Ñ�Ñ‚Ð²ÑƒÐµÑ‚, Ð³ÐµÐ½ÐµÑ€Ð¸Ñ€ÑƒÐµÐ¼ Ð½Ð¾Ð²Ñ‹Ð¹...',
             'loginQrCodeSuccessful' => 'Ð’Ñ…Ð¾Ð´ Ð¿Ð¾ QR ÐºÐ¾Ð´Ñƒ Ð¿Ñ€Ð¾ÑˆÐµÐ» ÑƒÑ�Ð¿ÐµÑˆÐ½Ð¾!',
             'loginTitleWeb' => 'Ð’Ñ…Ð¾Ð´ Ð² Telegram',
             'loginTypeWeb' => 'Ð’Ð¾Ð¹Ñ‚Ð¸ ÐºÐ°Ðº',
             'loginUser' => 'Ð�Ð¾Ð¼ÐµÑ€ Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½Ð°: ',
             'loginUserCode' => 'Ð’Ð²ÐµÐ´Ð¸Ñ‚Ðµ ÐºÐ¾Ð´: ',
             'loginUserPass' => 'Ð’Ð²ÐµÐ´Ð¸Ñ‚Ðµ Ð²Ð°Ñˆ Ð¿Ð°Ñ€Ð¾Ð»ÑŒ (Ð¿Ð¾Ð´Ñ�ÐºÐ°Ð·ÐºÐ° %s): ',
             'loginUserPassHint' => 'ÐŸÐ¾Ð´Ñ�ÐºÐ°Ð·ÐºÐ°: %s',
             'loginUserPassWeb' => 'Ð’Ð²ÐµÐ´Ð¸Ñ‚Ðµ Ð²Ð°Ñˆ Ð¿Ð°Ñ€Ð¾Ð»ÑŒ: ',
             'loginUserPhoneCodeWeb' => 'ÐšÐ¾Ð´',
             'loginUserPhoneHintWeb' => 'Ð¡ ÐºÐ¾Ð´Ð¾Ð¼ Ñ�Ñ‚Ñ€Ð°Ð½Ñ‹, Ð½Ð°Ð¿Ñ€Ð¸Ð¼ÐµÑ€ +39 123 456 7890.',
             'loginUserPhoneWeb' => 'Ð�Ð¾Ð¼ÐµÑ€ Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½Ð°',
             'loginWebQr' => 'Ð’Ñ‹ Ñ‚Ð°ÐºÐ¶Ðµ Ð¼Ð¾Ð¶ÐµÑ‚Ðµ Ð²Ð¾Ð¹Ñ‚Ð¸ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑ�ÐºÐ¸: Ð¾Ñ‚ÐºÑ€Ð¾Ð¹Ñ‚Ðµ Telegram Ð½Ð° Ñ�Ð¼Ð°Ñ€Ñ‚Ñ„Ð¾Ð½Ðµ, Ð¿ÐµÑ€ÐµÐ¹Ð´Ð¸Ñ‚Ðµ Ð² Ð�Ð°Ñ�Ñ‚Ñ€Ð¾Ð¹ÐºÐ¸ > Ð£Ñ�Ñ‚Ñ€Ð¾Ð¹Ñ�Ñ‚Ð²Ð° > ÐŸÐ¾Ð´ÐºÐ»ÑŽÑ‡Ð¸Ñ‚ÑŒ ÑƒÑ�Ñ‚Ñ€Ð¾Ð¹Ñ�Ñ‚Ð²Ð¾ Ð¸ Ð¾Ñ‚Ñ�ÐºÐ°Ð½Ð¸Ñ€ÑƒÐ¹Ñ‚Ðµ Ñ�Ñ‚Ð¾Ñ‚ QR ÐºÐ¾Ð´:',
             'loginWebQr1' => 'ÐžÑ‚ÐºÑ€Ð¾Ð¹Ñ‚Ðµ Telegram Ð² Ð²Ð°ÑˆÐµÐ¼ Ñ�Ð¼Ð°Ñ€Ñ‚Ñ„Ð¾Ð½Ðµ',
             'loginWebQr2' => 'ÐžÑ‚ÐºÑ€Ð¾Ð¹Ñ‚Ðµ Ð�Ð°Ñ�Ñ‚Ñ€Ð¾Ð¹ÐºÐ¸ > Ð£Ñ�Ñ‚Ñ€Ð¾Ð¹Ñ�Ñ‚Ð²Ð° > ÐŸÐ¾Ð´ÐºÐ»ÑŽÑ‡Ð¸Ñ‚ÑŒ ÑƒÑ�Ñ‚Ñ€Ð¾Ð¹Ñ�Ñ‚Ð²Ð¾',
             'loginWebQr3' => 'Ð�Ð°Ð²ÐµÐ´Ð¸Ñ‚Ðµ Ð²Ð°Ñˆ Ñ�Ð¼Ð°Ñ€Ñ‚Ñ„Ð¾Ð½ Ð½Ð° Ñ�Ñ‚Ð¾Ñ‚ Ñ�ÐºÑ€Ð°Ð½ Ñ‡Ñ‚Ð¾Ð±Ñ‹ Ð¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¸Ñ‚ÑŒ Ð²Ñ…Ð¾Ð´',
             'login_2fa_enabled' => '2FA Ð°ÐºÑ‚Ð¸Ð²Ð½Ð¾, Ð²Ð°Ð¼ Ð½ÐµÐ¾Ð±Ñ…Ð¾Ð´Ð¸Ð¼Ð¾ Ñ�Ð½Ð°Ñ‡Ð°Ð»Ð° Ð²Ñ‹Ð·Ð²Ð°Ñ‚ÑŒ Ñ„ÑƒÐ½ÐºÑ†Ð¸ÑŽ complete2falogin...',
             'login_auth_key' => 'Ð’Ñ…Ð¾Ð´ Ñ� Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ ÐºÐ»ÑŽÑ‡Ð° Ð°Ð²Ñ‚Ð¾Ñ€Ð¸Ð·Ð°Ñ†Ð¸Ð¸...',
             'login_bot' => 'Ð’Ñ…Ð¾Ð¶Ñƒ ÐºÐ°Ðº Ð±Ð¾Ñ‚...',
             'login_code_sending' => 'ÐžÑ‚Ð¿Ñ€Ð°Ð²ÐºÐ° ÐºÐ¾Ð´Ð°...',
             'login_code_sent' => 'ÐšÐ¾Ð´ ÑƒÑ�Ð¿ÐµÑˆÐ½Ð¾ Ð¾Ñ‚Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½! ÐšÐ°Ðº Ñ‚Ð¾Ð»ÑŒÐºÐ¾ Ð’Ñ‹ Ð¿Ð¾Ð»ÑƒÑ‡Ð¸Ñ‚Ðµ ÐºÐ¾Ð´ Ð’Ð°Ð¼ Ð½ÑƒÐ¶Ð½Ð¾ Ð¸Ñ�Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÑŒ Ñ„ÑƒÐ½ÐºÑ†Ð¸ÑŽ completePhoneLogin.',
             'login_code_uncalled' => 'Ð¯ Ð½Ðµ Ð¾Ð¶Ð¸Ð´Ð°ÑŽ ÐºÐ¾Ð´! ÐŸÐ¾Ð¶Ð°Ð»ÑƒÐ¹Ñ�Ñ‚Ð° Ñ�Ð½Ð°Ñ‡Ð°Ð»Ð° Ð²Ñ‹Ð·Ð¾Ð²Ð¸Ñ‚Ðµ Ð¼ÐµÑ‚Ð¾Ð´ phoneLogin',
             'login_need_signup' => 'Ð”Ð»Ñ� Ñ�Ñ‚Ð¾Ð³Ð¾ Ð½Ð¾Ð¼ÐµÑ€Ð° Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½Ð° Ð½Ðµ Ñ�Ð¾Ð·Ð´Ð°Ð½Ð° ÑƒÑ‡Ñ‘Ñ‚Ð½Ð°Ñ� Ð·Ð°Ð¿Ð¸Ñ�ÑŒ, Ð²Ð°Ð¼ Ð½ÐµÐ¾Ð±Ñ…Ð¾Ð´Ð¸Ð¼Ð¾ Ð²Ñ‹Ð·Ð²Ð°Ñ‚ÑŒ Ñ„ÑƒÐ½ÐºÑ†Ð¸ÑŽ completeSignup...',
             'login_ok' => 'ÐœÑ‹ Ð·Ð°Ð»Ð¾Ð³Ð¸Ð½ÐµÐ½Ñ‹!',
             'login_user' => 'Ð’Ñ…Ð¾Ð¶Ñƒ ÐºÐ°Ðº Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»ÑŒ...',
             'logout_ok' => 'Ð’Ñ‹Ñ…Ð¾Ð´ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½ ÑƒÑ�Ð¿ÐµÑˆÐ½Ð¾!',
             'long_not_16' => 'Ð”Ð»Ð¸Ð½Ð° Ð´Ð°Ð½Ð½Ð¾Ð³Ð¾ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ� Ð½Ðµ Ñ€Ð°Ð²Ð½Ð° 16 Ð±Ð°Ð¹Ñ‚Ð°Ð¼',
             'long_not_32' => 'Ð”Ð»Ð¸Ð½Ð° Ð´Ð°Ð½Ð½Ð¾Ð³Ð¾ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ� Ð½Ðµ Ñ€Ð°Ð²Ð½Ð° 32 Ð±Ð°Ð¹Ñ‚Ð°Ð¼',
             'long_not_64' => 'Ð”Ð»Ð¸Ð½Ð° Ð´Ð°Ð½Ð½Ð¾Ð³Ð¾ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ� Ð½Ðµ Ñ€Ð°Ð²Ð½Ð° 64 Ð±Ð°Ð¹Ñ‚Ð°Ð¼',
             'madelineproto_ready' => 'MadelineProto Ð³Ð¾Ñ‚Ð¾Ð²!',
             'manualAdminActionRequired' => '!!!!!!!!! Ð�Ð•ÐžÐ‘Ð¥ÐžÐ”Ð˜ÐœÐ« Ð Ð£Ð§Ð�Ð«Ð• Ð”Ð•Ð™Ð¡Ð¢Ð’Ð˜Ð¯ Ð¡Ð˜Ð¡Ð¢Ð•ÐœÐ�ÐžÐ“Ðž Ð�Ð”ÐœÐ˜Ð�Ð˜Ð¡Ð¢Ð Ð�Ð¢ÐžÐ Ð� !!!!!!!!!',
             'method_not_found' => 'Ð�ÐµÐ²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾ Ð½Ð°Ð¹Ñ‚Ð¸ Ð¼ÐµÑ‚Ð¾Ð´: ',
             'mmapErrorPart1' => 'ÐŸÑ€ÐµÐ²Ñ‹ÑˆÐµÐ½Ð¾ Ð¼Ð°ÐºÑ�Ð¸Ð¼Ð°Ð»ÑŒÐ½Ð¾Ðµ ÐºÐ¾Ð»Ð¸Ñ‡ÐµÑ�Ñ‚Ð²Ð¾ Ð¼Ð°Ð¿Ð¿Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð½Ñ‹Ñ… Ð·Ð¾Ð½ Ð¿Ð°Ð¼Ñ�Ñ‚Ð¸ (%s, Ñ�Ñ‚Ð¾ Ð�Ð• Ð¿Ð¾Ñ‚Ñ€ÐµÐ±Ð»ÐµÐ½Ð¸Ðµ Ð¿Ð°Ð¼Ñ�Ñ‚Ð¸, Ð° mmap): Ð¿Ð¾Ð¶Ð°Ð»ÑƒÐ¹Ñ�Ñ‚Ð° ÑƒÐ²ÐµÐ»Ð¸Ñ‡ÑŒÑ‚Ðµ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ðµ Ð½Ð°Ñ�Ñ‚Ñ€Ð¾Ð¹ÐºÐ¸ Ñ�Ð´Ñ€Ð° vm.max_map_count Ð´Ð¾ 262144, Ñ‡Ñ‚Ð¾Ð±Ñ‹ Ð¸Ñ�Ð¿Ñ€Ð°Ð²Ð¸Ñ‚ÑŒ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼Ñƒ.',
             'mmapErrorPart2' => 'Ð§Ñ‚Ð¾Ð±Ñ‹ Ð¸Ñ�Ð¿Ñ€Ð°Ð²Ð¸Ñ‚ÑŒ, Ð½ÐµÐ¾Ð±Ñ…Ð¾Ð´Ð¸Ð¼Ð¾ Ð·Ð°Ð¿ÑƒÑ�Ñ‚Ð¸Ñ‚ÑŒ Ñ�Ñ‚Ñƒ ÐºÐ¾Ð¼Ð°Ð½Ð´Ñƒ ÐºÐ°Ðº root: %s',
             'mmapErrorPart3' => 'Ð§Ñ‚Ð¾Ð±Ñ‹ Ñ�Ð¾Ñ…Ñ€Ð°Ð½Ð¸Ñ‚ÑŒ Ð½Ð°Ñ�Ñ‚Ñ€Ð¾Ð¹ÐºÐ¸ Ð´Ð°Ð¶Ðµ Ð¿Ð¾Ñ�Ð»Ðµ Ð¿ÐµÑ€ÐµÐ·Ð°Ð¿ÑƒÑ�ÐºÐ°: %s',
             'mmapErrorPart4' => 'Ð�Ð° Windows Ð¸ WSL, Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼Ð° Ð¼Ð¾Ð¶ÐµÑ‚ Ð¸Ñ�Ð¿Ð°Ñ€Ð¸Ñ‚ÑŒÑ�Ñ� Ð¿Ñ€Ð¸ ÑƒÐ²ÐµÐ»Ð¸Ñ‡ÐµÐ½Ð¸Ð¸ Ñ€Ð°Ð·Ð¼ÐµÑ€Ð° page file; Ð¿Ð¾Ð¶Ð°Ð»ÑƒÐ¹Ñ�Ñ‚Ð° Ð¿ÐµÑ€ÐµÐ¹Ð´Ð¸Ñ‚Ðµ Ð½Ð° Ð½Ð°Ñ‚Ð¸Ð²Ð½Ñ‹Ð¹ Linux ÐµÑ�Ð»Ð¸ Ñ�Ñ‚Ð¾ Ð½Ðµ Ð¸Ñ�Ð¿Ñ€Ð°Ð²Ð¸Ñ‚ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼Ñƒ.',
             'must_have_declare_types' => 'Ð¿Ð¾ Ñ�Ð¾Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ�Ð¼ Ð¿Ñ€Ð¾Ð¸Ð·Ð²Ð¾Ð´Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ñ�Ñ‚Ð¸ Ð¿ÐµÑ€Ð²Ð¾Ð¹ Ð¸Ð½Ñ�Ñ‚Ñ€ÑƒÐºÑ†Ð¸ÐµÐ¹ Ñ„Ð°Ð¹Ð»Ð° event handler\'Ð° Ð´Ð¾Ð»Ð¶Ð½Ð° Ð±Ñ‹Ñ‚ÑŒ "declare(strict_types=1);"',
             'nearest_dc' => 'Ð’Ñ‹ Ð² %s, Ð±Ð»Ð¸Ð¶Ð°Ð¹ÑˆÐ¸Ð¹ Ð”Ð¦: %d.',
             'need_dl.php' => 'Ð�Ðµ ÑƒÐ´Ð°Ð»Ð¾Ñ�ÑŒ Ñ�Ð³ÐµÐ½ÐµÑ€Ð¸Ñ€Ð¾Ð²Ð°Ñ‚ÑŒ Ñ�ÐºÑ€Ð¸Ð¿Ñ‚ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸ Ð¿Ð¾ ÑƒÐ¼Ð¾Ð»Ñ‡Ð°Ð½Ð¸ÑŽ (%s), Ð¿Ð¾Ð¶Ð°Ð»ÑƒÐ¹Ñ�Ñ‚Ð°, Ñ�Ð¾Ð·Ð´Ð°Ð¹Ñ‚Ðµ Ñ„Ð°Ð¹Ð» dl.php Ñ�Ð¾ Ñ�Ð»ÐµÐ´ÑƒÑŽÑ‰Ð¸Ð¼ Ñ�Ð¾Ð´ÐµÑ€Ð¶Ð¸Ð¼Ñ‹Ð¼: %s Ð¸ Ð¿ÐµÑ€ÐµÐ´Ð°Ð¹Ñ‚Ðµ ÐµÐ³Ð¾ URL Ð²Ñ‚Ð¾Ñ€Ñ‹Ð¼ Ð¿Ð°Ñ€Ð°Ð¼ÐµÑ‚Ñ€Ð¾Ð¼ getDownloadLink',
             'noReportPeers' => 'Ð’Ð½Ð¸Ð¼Ð°Ð½Ð¸Ðµ: Ð½Ðµ Ð½Ð°Ñ�Ñ‚Ñ€Ð¾ÐµÐ½ Ð½Ð¸ Ð¾Ð´Ð¸Ð½ ÑŽÐ·ÐµÑ€ Ð´Ð»Ñ� Ñ�Ð¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ� Ð¾ÑˆÐ¸Ð±Ð¾Ðº, Ð¿Ð¾Ð¶Ð°Ð»ÑƒÐ¹Ñ�Ñ‚Ð° Ð´Ð¾Ð±Ð°Ð²ÑŒÑ‚Ðµ Ñ�Ñ‚Ð¾Ñ‚ Ð¼ÐµÑ‚Ð¾Ð´ Ð² Event Handler',
             'non_text_conversion' => 'ÐšÐ¾Ð½Ð²ÐµÑ€Ñ‚Ð°Ñ†Ð¸Ñ� Ð½ÐµÑ‚ÐµÐºÑ�Ñ‚Ð¾Ð²Ñ‹Ñ… Ñ�Ð¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ð¹ Ð¿Ð¾ÐºÐ° Ð½Ðµ Ð¿Ð¾Ð´Ð´ÐµÑ€Ð¶Ð¸Ð²Ð°ÐµÑ‚Ñ�Ñ�!',
             'not_loggedIn' => 'Ð¯ Ð½Ðµ Ð²Ð¾ÑˆÑ‘Ð» Ð² Ñ�Ð¸Ñ�Ñ‚ÐµÐ¼Ñƒ!',
             'not_numeric' => 'Ð”Ð°Ð½Ð½Ð¾Ðµ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ðµ Ð½Ðµ Ñ�Ð²Ð»Ñ�ÐµÑ‚Ñ�Ñ� Ñ‡Ð¸Ñ�Ð»Ð¾Ð¼',
             'params_missing' => 'Ð�Ðµ Ñ…Ð²Ð°Ñ‚Ð°ÐµÑ‚ Ð¾Ð±Ñ�Ð·Ð°Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ð³Ð¾ Ð¿Ð°Ñ€Ð°Ð¼ÐµÑ‚Ñ€Ð°',
             'peer_not_in_db' => 'Ð­Ñ‚Ð¾Ñ‚ peer Ð½Ðµ Ð½Ð°Ñ…Ð¾Ð´Ð¸Ñ‚Ñ�Ñ� Ð²Ð¾ Ð²Ð½ÑƒÑ‚Ñ€ÐµÐ½Ð½ÐµÐ¹ Ð±Ð°Ð·Ðµ',
             'plugin_path_does_not_exist' => 'ÐŸÑƒÑ‚ÑŒ Ðº Ð¿Ð»Ð°Ð³Ð¸Ð½Ñƒ %s Ð½Ðµ Ñ�ÑƒÑ‰ÐµÑ�Ñ‚Ð²ÑƒÐµÑ‚!',
             'plugins_do_not_use_require' => 'Ð¿Ð¾ Ñ�Ð¾Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ�Ð¼ Ð¿Ñ€Ð¾Ð¸Ð·Ð²Ð¾Ð´Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ñ�Ñ‚Ð¸ Ð¿Ð»Ð°Ð³Ð¸Ð½Ñ‹ Ð¼Ð¾Ð³ÑƒÑ‚ Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡Ð°Ñ‚ÑŒ (include Ð¸Ð»Ð¸ require) Ð´Ñ€ÑƒÐ³Ð¸Ðµ Ñ„Ð°Ð¹Ð»Ñ‹ Ð¸Ð· Ð¿Ð°Ð¿ÐºÐ¸ Ð¿Ð»Ð°Ð³Ð¸Ð½Ð¾Ð² Ñ‚Ð¾Ð»ÑŒÐºÐ¾ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑ�ÐºÐ¸, Ñ‡ÐµÑ€ÐµÐ· Ð°Ð²Ñ‚Ð¾Ð·Ð°Ð³Ñ€ÑƒÐ·Ñ‡Ð¸Ðº PSR-4 (Ð° Ð½Ðµ Ð²Ñ€ÑƒÑ‡Ð½ÑƒÑŽ Ñ‡ÐµÑ€ÐµÐ· require()).',
             'plugins_must_have_exactly_one_class' => 'Ð¿Ð»Ð°Ð³Ð¸Ð½ Ð´Ð¾Ð»Ð¶ÐµÐ½ Ð¾Ð¿Ñ€ÐµÐ´ÐµÐ»Ñ�Ñ‚ÑŒ Ñ€Ð¾Ð²Ð½Ð¾ Ð¾Ð´Ð¸Ð½ ÐºÐ»Ð°Ñ�Ñ�! Ð§Ñ‚Ð¾Ð±Ñ‹ Ð¾Ð¿Ñ€ÐµÐ´ÐµÐ»Ð¸Ñ‚ÑŒ Ð½ÐµÑ�ÐºÐ¾Ð»ÑŒÐºÐ¾ ÐºÐ»Ð°Ñ�Ñ�Ð¾Ð², Ð¸Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ�Ð¾Ð² Ð¸Ð»Ð¸ Ñ‚Ñ€ÐµÐ¹Ñ‚Ð¾Ð², Ñ�Ð¾Ð·Ð´Ð°Ð¹Ñ‚Ðµ Ð¾Ñ‚Ð´ÐµÐ»ÑŒÐ½Ñ‹Ðµ Ñ„Ð°Ð¹Ð»Ñ‹, MadelineProto Ð·Ð°Ð³Ñ€ÑƒÐ·Ð¸Ñ‚ Ð¸Ñ… Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑ�ÐºÐ¸.',
             'predicate_not_set' => 'ÐŸÑ€ÐµÐ´Ð¸ÐºÐ°Ñ‚ (Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ðµ Ð¿Ð¾Ð´ ÐºÐ»ÑŽÑ‡Ð¾Ð¼ _) Ð½Ðµ Ð±Ñ‹Ð» ÑƒÐºÐ°Ð·Ð°Ð½!',
             'recommend_not_use_filesystem_function' => 'Ð½Ðµ Ñ€ÐµÐºÐ¾Ð¼ÐµÐ½Ð´ÑƒÐµÑ‚Ñ�Ñ� Ð¸Ñ�Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÑŒ Ñ„ÑƒÐ½ÐºÑ†Ð¸ÑŽ %s, Ñ‚Ð°Ðº ÐºÐ°Ðº Ð¾Ð±Ñ€Ð°Ñ‰ÐµÐ½Ð¸Ðµ Ðº Ñ„Ð°Ð¹Ð»Ð¾Ð²Ð¾Ð¹ Ñ�Ð¸Ñ�Ñ‚ÐµÐ¼Ðµ Ð²Ð¾ Ð²Ñ€ÐµÐ¼Ñ� Ð¾Ð±Ñ€Ð°Ð±Ð¾Ñ‚ÐºÐ¸ Ð°Ð¿Ð´ÐµÐ¹Ñ‚Ð¾Ð² Ð·Ð°Ð¼ÐµÐ´Ð»Ð¸Ñ‚ Ð²Ð°ÑˆÐµÐ³Ð¾ Ð±Ð¾Ñ‚Ð°, Ñ�Ð¿Ð¸Ñ�Ð¾Ðº Ð°Ð»ÑŒÑ‚ÐµÑ€Ð½Ð°Ñ‚Ð¸Ð²Ð½Ñ‹Ñ… Ñ�Ð¿Ð¾Ñ�Ð¾Ð±Ð¾Ð² Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ� Ð´Ð°Ð½Ð½Ñ‹Ñ…, ÐºÐ¾Ñ‚Ð¾Ñ€Ñ‹Ðµ Ð½Ðµ Ð·Ð°Ð¼ÐµÐ´Ð»Ñ�Ñ‚ Ð²Ð°ÑˆÐµÐ³Ð¾ Ð±Ð¾Ñ‚Ð°, Ñ�Ð¼Ð¾Ñ‚Ñ€Ð¸Ñ‚Ðµ Ð½Ð° https://docs.madelineproto.xyz/docs/UPDATES.html#avoiding-the-use-of-filesystem-functions!',
             'rpc_tg_error' => 'Ð¢ÐµÐ»ÐµÐ³Ñ€Ð°Ð¼ Ð²Ð¾Ð·Ð²Ñ€Ð°Ñ‚Ð¸Ð» RPC Ð¾ÑˆÐ¸Ð±ÐºÑƒ: %s (%s), Ð¸Ð·-Ð·Ð° %s:%s%sTL trace:',
             'sec_peer_not_in_db' => 'Ð­Ñ‚Ð¾Ð³Ð¾ Ñ�ÐµÐºÑ€ÐµÑ‚Ð½Ð¾Ð³Ð¾ Ñ‡Ð°Ñ‚Ð° Ð½ÐµÑ‚ Ð²Ð¾ Ð²Ð½ÑƒÑ‚Ñ€ÐµÐ½Ð½ÐµÐ¹ Ð±Ð°Ð·Ðµ',
             'secret_chat_skipping' => 'Ð¡ÐµÐºÑ€ÐµÑ‚Ð½Ð¾Ð³Ð¾ Ñ‡Ð°Ñ‚Ð° %s Ð½ÐµÑ‚ Ð² Ð±Ð°Ð·Ðµ, Ð¿Ñ€Ð¾Ð¿ÑƒÑ�ÐºÐ°ÑŽ Ñ�Ð¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ðµ...',
             'serialization_ofd' => 'Ð¡ÐµÑ€Ð¸Ð°Ð»Ð¸Ð·Ð°Ñ†Ð¸Ñ� ÑƒÑ�Ñ‚Ð°Ñ€ÐµÐ»Ð°, Ð¾Ð±ÑŠÐµÐºÑ‚ Ñ€ÐµÐºÐ¾Ð½Ñ�Ñ‚Ñ€ÑƒÐ¸Ñ€ÑƒÐµÑ‚Ñ�Ñ�!',
             'session_corrupted' => 'Ð¡ÐµÑ�Ñ�Ð¸Ñ� Ð¿Ð¾Ð²Ñ€ÐµÐ¶Ð´ÐµÐ½Ð°!',
             'signing_up' => 'Ð ÐµÐ³Ð¸Ñ�Ñ‚Ñ€Ð°Ñ†Ð¸Ñ� Ð² ÐºÐ°Ñ‡ÐµÑ�Ñ‚Ð²Ðµ Ð¾Ð±Ñ‹Ñ‡Ð½Ð¾Ð³Ð¾ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ�...',
             'signupFirstName' => 'Ð’Ð²ÐµÐ´Ð¸Ñ‚Ðµ Ð²Ð°ÑˆÐµ Ð¸Ð¼Ñ�: ',
             'signupFirstNameWeb' => 'Ð˜Ð¼Ñ�',
             'signupLastName' => 'Ð’Ð²ÐµÐ´Ð¸Ñ‚Ðµ Ð²Ð°ÑˆÑƒ Ñ„Ð°Ð¼Ð¸Ð»Ð¸ÑŽ (Ð¼Ð¾Ð¶ÐµÑ‚ Ð±Ñ‹Ñ‚ÑŒ Ð¿ÑƒÑ�Ñ‚Ñ‹Ð¼): ',
             'signupLastNameWeb' => 'Ð¤Ð°Ð¼Ð¸Ð»Ð¸Ñ�',
             'signupLeadWeb' => 'Ð­Ñ‚Ð¾Ñ‚ Ð½Ð¾Ð¼ÐµÑ€ Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½Ð° ÐµÑ‰Ñ‘ Ð½Ðµ Ð·Ð°Ñ€ÐµÐ³Ð¸Ñ�Ñ‚Ñ€Ð¸Ñ€Ð¾Ð²Ð°Ð½ Ð² Telegram: Ð²Ñ‹Ð±ÐµÑ€Ð¸Ñ‚Ðµ Ð¸Ð¼Ñ� Ð½Ð¾Ð²Ð¾Ð³Ð¾ Ð°ÐºÐºÐ°ÑƒÐ½Ñ‚Ð°.',
             'signupTitleWeb' => 'Ð¡Ð¾Ð·Ð´Ð°Ð¹Ñ‚Ðµ Ð°ÐºÐºÐ°ÑƒÐ½Ñ‚',
             'signupWeb' => 'Ð—Ð°Ñ€ÐµÐ³Ð¸Ñ�Ñ‚Ñ€Ð¸Ñ€ÑƒÐ¹Ñ‚ÐµÑ�ÑŒ, Ð¿Ð¾Ð¶Ð°Ð»ÑƒÐ¹Ñ�Ñ‚Ð°',
             'signup_ok' => 'Ð ÐµÐ³Ð¸Ñ�Ñ‚Ñ€Ð°Ñ†Ð¸Ñ� Ð¿Ñ€Ð¾ÑˆÐ»Ð° ÑƒÑ�Ð¿ÐµÑˆÐ½Ð¾!',
             'signup_uncalled' => 'Ð¯ Ð½Ðµ Ð¾Ð¶Ð¸Ð´Ð°ÑŽ Ñ€ÐµÐ³Ð¸Ñ�Ñ‚Ñ€Ð°Ñ†Ð¸Ð¸! ÐŸÐ¾Ð¶Ð°Ð»ÑƒÐ¹Ñ�Ñ‚Ð°, Ð²Ñ‹Ð·Ð¾Ð²Ð¸Ñ‚Ðµ Ñ�Ð½Ð°Ñ‡Ð°Ð»Ð° Ð¼ÐµÑ‚Ð¾Ð´Ñ‹ phoneLogin Ð¸ completePhoneLogin!',
             'src_file_invalid' => 'Ð�ÐµÐ²Ð°Ð»Ð¸Ð´Ð½Ñ‹Ð¹ Ñ„Ð°Ð¹Ð»: ',
             'static_analysis_minor' => 'ÐŸÑ€Ð¸ Ñ�Ñ‚Ð°Ñ‚Ð¸Ñ‡ÐµÑ�ÐºÐ¾Ð¼ Ð°Ð½Ð°Ð»Ð¸Ð·Ðµ %s Ð¾Ð±Ð½Ð°Ñ€ÑƒÐ¶ÐµÐ½Ð° Ð½ÐµÐ·Ð½Ð°Ñ‡Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð°Ñ� Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼Ð°: %s',
             'static_analysis_severe' => 'ÐŸÑ€Ð¸ Ñ�Ñ‚Ð°Ñ‚Ð¸Ñ‡ÐµÑ�ÐºÐ¾Ð¼ Ð°Ð½Ð°Ð»Ð¸Ð·Ðµ %s Ð¾Ð±Ð½Ð°Ñ€ÑƒÐ¶ÐµÐ½Ð° Ñ�ÐµÑ€ÑŒÑ‘Ð·Ð½Ð°Ñ� Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼Ð°: %s',
             'stream_handle_invalid' => 'Ð˜Ñ�Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½ Ð½ÐµÐ²Ð°Ð»Ð¸Ð´Ð½Ñ‹Ð¹ Ñ�Ñ‚Ñ€Ð¸Ð¼.',
             'string_required' => 'ÐžÐ¶Ð¸Ð´Ð°Ð»Ð°Ñ�ÑŒ Ñ�Ñ‚Ñ€Ð¾ÐºÐ°!',
             'translate_madelineproto_cli' => 'MadelineProto Ð¼Ð¾Ð¶Ð½Ð¾ Ð»Ð¾ÐºÐ°Ð»Ð¸Ð·Ð¸Ñ€Ð¾Ð²Ð°Ñ‚ÑŒ Ð½Ð° Ð²Ð°Ñˆ Ñ�Ð·Ñ‹Ðº (Ð½Ð° Ð´Ð°Ð½Ð½Ñ‹Ð¹ Ð¼Ð¾Ð¼ÐµÐ½Ñ‚ Ð¿ÐµÑ€ÐµÐ²ÐµÐ´ÐµÐ½Ð¾: %d%%), Ð¾Ñ‚ÐºÑ€Ð¾Ð¹Ñ‚Ðµ https://github.com/danog/MadelineProto/tree/v8/langs Ñ‡Ñ‚Ð¾Ð±Ñ‹ Ð¿Ð¾Ð¼Ð¾Ñ‡ÑŒ Ñ� Ð¿ÐµÑ€ÐµÐ²Ð¾Ð´Ð¾Ð¼!',
             'translate_madelineproto_web' => 'MadelineProto Ð¼Ð¾Ð¶Ð½Ð¾ Ð»Ð¾ÐºÐ°Ð»Ð¸Ð·Ð¸Ñ€Ð¾Ð²Ð°Ñ‚ÑŒ Ð½Ð° Ð²Ð°Ñˆ Ñ�Ð·Ñ‹Ðº (Ð½Ð° Ð´Ð°Ð½Ð½Ñ‹Ð¹ Ð¼Ð¾Ð¼ÐµÐ½Ñ‚ Ð¿ÐµÑ€ÐµÐ²ÐµÐ´ÐµÐ½Ð¾: %d%%), ÐºÐ»Ð¸ÐºÐ½Ð¸Ñ‚Ðµ <a href="https://github.com/danog/MadelineProto/tree/v8/langs" target="_blank">Ñ�ÑŽÐ´Ð° Ñ‡Ñ‚Ð¾Ð±Ñ‹ Ð¿Ð¾Ð¼Ð¾Ñ‡ÑŒ Ñ� Ð¿ÐµÑ€ÐµÐ²Ð¾Ð´Ð¾Ð¼!</a>',
             'tweb.ACCESS_TOKEN_EXPIRED' => 'Ð­Ñ‚Ð¾Ñ‚ Ñ‚Ð¾ÐºÐµÐ½ Ð±Ð¾Ñ‚Ð° Ð±Ñ‹Ð» Ð¾Ñ‚Ð¾Ð·Ð²Ð°Ð½',
             'tweb.ACCESS_TOKEN_INVALID' => 'Ð�ÐµÐ²ÐµÑ€Ð½Ñ‹Ð¹ Ñ‚Ð¾ÐºÐµÐ½ Ð±Ð¾Ñ‚Ð°',
             'tweb.AccDescr.LoginCode' => 'ÐšÐ¾Ð´ Ð²Ñ…Ð¾Ð´Ð°',
             'tweb.Close' => 'Ð—Ð°ÐºÑ€Ñ‹Ñ‚ÑŒ',
             'tweb.Code' => 'ÐšÐ¾Ð´',
             'tweb.Contacts.PhoneNumber.Placeholder' => 'Ð�Ð¾Ð¼ÐµÑ€ Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½Ð°',
             'tweb.Country' => 'Ð¡Ñ‚Ñ€Ð°Ð½Ð°',
             'tweb.DarkMode' => 'Ð¢Ñ‘Ð¼Ð½Ð°Ñ� Ñ‚ÐµÐ¼Ð°',
             'tweb.Error.AnError' => 'ÐŸÑ€Ð¾Ð¸Ð·Ð¾ÑˆÐ»Ð° Ð¾ÑˆÐ¸Ð±ÐºÐ°. ÐŸÐ¾Ð¿Ñ€Ð¾Ð±ÑƒÐ¹Ñ‚Ðµ Ð¿Ð¾Ð·Ð¶Ðµ.',
             'tweb.FIRSTNAME_INVALID' => 'Ð�ÐµÐ´Ð¾Ð¿ÑƒÑ�Ñ‚Ð¸Ð¼Ð¾Ðµ Ð¸Ð¼Ñ�',
             'tweb.FirstName' => 'Ð˜Ð¼Ñ� (Ð¾Ð±Ñ�Ð·Ð°Ñ‚ÐµÐ»ÑŒÐ½Ð¾)',
             'tweb.LASTNAME_INVALID' => 'Ð�ÐµÐ´Ð¾Ð¿ÑƒÑ�Ñ‚Ð¸Ð¼Ð°Ñ� Ñ„Ð°Ð¼Ð¸Ð»Ð¸Ñ�',
             'tweb.LastName' => 'Ð¤Ð°Ð¼Ð¸Ð»Ð¸Ñ� (Ð½ÐµÐ¾Ð±Ñ�Ð·Ð°Ñ‚ÐµÐ»ÑŒÐ½Ð¾)',
             'tweb.Login.Bot' => 'Ð’Ð¾Ð¹Ñ‚Ð¸ ÐºÐ°Ðº Ð±Ð¾Ñ‚ >',
             'tweb.Login.Bot.Cancel' => 'Ð’Ð¾Ð¹Ñ‚Ð¸ ÐºÐ°Ðº Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»ÑŒ >',
             'tweb.Login.Bot.Subtitle' => 'Ð’Ð²ÐµÐ´Ð¸Ñ‚Ðµ Ñ‚Ð¾ÐºÐµÐ½, ÐºÐ¾Ñ‚Ð¾Ñ€Ñ‹Ð¹ Ð²Ñ‹Ð´Ð°Ð» @BotFather
Ð¿Ñ€Ð¸ Ñ�Ð¾Ð·Ð´Ð°Ð½Ð¸Ð¸ Ð±Ð¾Ñ‚Ð°.',
             'tweb.Login.Bot.Title' => 'Ð’Ñ…Ð¾Ð´ ÐºÐ°Ðº Ð±Ð¾Ñ‚',
             'tweb.Login.Bot.Token' => 'Ð¢Ð¾ÐºÐµÐ½ Ð±Ð¾Ñ‚Ð°',
             'tweb.Login.Code.SecretPhrase' => 'Ð¡ÐµÐºÑ€ÐµÑ‚Ð½Ð°Ñ� Ñ„Ñ€Ð°Ð·Ð°',
             'tweb.Login.Code.SecretWord' => 'Ð¡ÐµÐºÑ€ÐµÑ‚Ð½Ð¾Ðµ Ñ�Ð»Ð¾Ð²Ð¾',
             'tweb.Login.Code.SentCall' => 'ÐœÑ‹ Ð¿Ð¾Ð·Ð²Ð¾Ð½Ð¸Ð¼ Ð²Ð°Ð¼ Ð¸ Ð¿Ñ€Ð¾Ð´Ð¸ÐºÑ‚ÑƒÐµÐ¼
ÐºÐ¾Ð´.',
             'tweb.Login.Code.SentEmail' => 'ÐŸÑ€Ð¾Ð²ÐµÑ€ÑŒÑ‚Ðµ Ð¿Ð¾Ñ‡Ñ‚Ñƒ **%s** (Ð½Ðµ Ð·Ð°Ð±ÑƒÐ´ÑŒÑ‚Ðµ Ð¿Ñ€Ð¾ Ð¿Ð°Ð¿ÐºÑƒ Â«Ð¡Ð¿Ð°Ð¼Â») Ð¸ Ð²Ð²ÐµÐ´Ð¸Ñ‚Ðµ ÐºÐ¾Ð´, ÐºÐ¾Ñ‚Ð¾Ñ€Ñ‹Ð¹ Ð¼Ñ‹ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ Ñ‡Ñ‚Ð¾ Ð¾Ñ‚Ð¿Ñ€Ð°Ð²Ð¸Ð»Ð¸.',
             'tweb.Login.Code.SentInApp' => 'ÐœÑ‹ Ð¾Ñ‚Ð¿Ñ€Ð°Ð²Ð¸Ð»Ð¸ Ð²Ð°Ð¼ Ñ�Ð¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ðµ Ð² Telegram
Ñ� ÐºÐ¾Ð´Ð¾Ð¼.',
             'tweb.Login.Code.SentMissedCall' => 'Ð’ Ñ‚ÐµÑ‡ÐµÐ½Ð¸Ðµ Ð½ÐµÑ�ÐºÐ¾Ð»ÑŒÐºÐ¸Ñ… Ñ�ÐµÐºÑƒÐ½Ð´ Ð²Ð°Ð¼ Ð¿Ð¾Ñ�Ñ‚ÑƒÐ¿Ð¸Ñ‚
ÐºÐ¾Ñ€Ð¾Ñ‚ÐºÐ¸Ð¹ Ð·Ð²Ð¾Ð½Ð¾Ðº Ñ� Ð½Ð¾Ð¼ÐµÑ€Ð° **%s**. Ð�Ðµ Ð¾Ñ‚Ð²ÐµÑ‡Ð°Ð¹Ñ‚Ðµ â€”
Ð¿Ñ€Ð¾Ñ�Ñ‚Ð¾ Ð²Ð²ÐµÐ´Ð¸Ñ‚Ðµ Ð¿Ð¾Ñ�Ð»ÐµÐ´Ð½Ð¸Ðµ Ñ†Ð¸Ñ„Ñ€Ñ‹ Ñ�Ñ‚Ð¾Ð³Ð¾ Ð½Ð¾Ð¼ÐµÑ€Ð°.',
             'tweb.Login.Code.SentSms' => 'ÐœÑ‹ Ð¾Ñ‚Ð¿Ñ€Ð°Ð²Ð¸Ð»Ð¸ Ð²Ð°Ð¼ SMS
Ñ� ÐºÐ¾Ð´Ð¾Ð¼.',
             'tweb.Login.Code.SentSmsPhrase' => 'ÐœÑ‹ Ð¾Ñ‚Ð¿Ñ€Ð°Ð²Ð¸Ð»Ð¸ SMS Ñ� Ñ�ÐµÐºÑ€ÐµÑ‚Ð½Ð¾Ð¹ Ñ„Ñ€Ð°Ð·Ð¾Ð¹
Ð½Ð° Ð²Ð°Ñˆ Ð½Ð¾Ð¼ÐµÑ€ **%s**.',
             'tweb.Login.Code.SentSmsPhraseBeginning' => 'ÐœÑ‹ Ð¾Ñ‚Ð¿Ñ€Ð°Ð²Ð¸Ð»Ð¸ SMS Ñ� Ñ�ÐµÐºÑ€ÐµÑ‚Ð½Ð¾Ð¹ Ñ„Ñ€Ð°Ð·Ð¾Ð¹, ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ� Ð½Ð°Ñ‡Ð¸Ð½Ð°ÐµÑ‚Ñ�Ñ�
Ñ� Â«**%2$s**Â», Ð½Ð° Ð²Ð°Ñˆ Ð½Ð¾Ð¼ÐµÑ€ **%1$s**.',
             'tweb.Login.Code.SentSmsWord' => 'ÐœÑ‹ Ð¾Ñ‚Ð¿Ñ€Ð°Ð²Ð¸Ð»Ð¸ SMS Ñ� Ñ�ÐµÐºÑ€ÐµÑ‚Ð½Ñ‹Ð¼ Ñ�Ð»Ð¾Ð²Ð¾Ð¼
Ð½Ð° Ð²Ð°Ñˆ Ð½Ð¾Ð¼ÐµÑ€ **%s**.',
             'tweb.Login.Code.SentSmsWordBeginning' => 'ÐœÑ‹ Ð¾Ñ‚Ð¿Ñ€Ð°Ð²Ð¸Ð»Ð¸ SMS Ñ� Ñ�ÐµÐºÑ€ÐµÑ‚Ð½Ñ‹Ð¼ Ñ�Ð»Ð¾Ð²Ð¾Ð¼, ÐºÐ¾Ñ‚Ð¾Ñ€Ð¾Ðµ Ð½Ð°Ñ‡Ð¸Ð½Ð°ÐµÑ‚Ñ�Ñ�
Ñ� Â«**%2$s**Â», Ð½Ð° Ð²Ð°Ñˆ Ð½Ð¾Ð¼ÐµÑ€ **%1$s**.',
             'tweb.Login.Code.SentUnknown' => 'ÐŸÐ¾Ð¸Ñ‰Ð¸Ñ‚Ðµ ÐºÐ¾Ð´
Ð²ÐµÐ·Ð´Ðµ (Ñ‚Ð¸Ð¿: %s)',
             'tweb.Login.Code.WordBeginningInvalid' => 'Ð�ÐµÐ²ÐµÑ€Ð½Ð¾Ðµ Ñ�Ð»Ð¾Ð²Ð¾',
             'tweb.Login.Code.WordInvalid' => 'Ð�ÐµÐ²ÐµÑ€Ð½Ð¾, Ð¿Ð¾Ð¿Ñ€Ð¾Ð±ÑƒÐ¹Ñ‚Ðµ ÐµÑ‰Ñ‘ Ñ€Ð°Ð·',
             'tweb.Login.Next' => 'Ð”Ð°Ð»ÐµÐµ',
             'tweb.Login.Password.Subtitle' => 'Ð’Ð°Ñˆ Ð°ÐºÐºÐ°ÑƒÐ½Ñ‚ Ð·Ð°Ñ‰Ð¸Ñ‰Ñ‘Ð½
Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ñ‹Ð¼ Ð¿Ð°Ñ€Ð¾Ð»ÐµÐ¼',
             'tweb.Login.Password.Title' => 'Ð’Ð²ÐµÐ´Ð¸Ñ‚Ðµ Ð¿Ð°Ñ€Ð¾Ð»ÑŒ',
             'tweb.Login.PhoneLabelInvalid' => 'Ð�ÐµÐ²ÐµÑ€Ð½Ñ‹Ð¹ Ð½Ð¾Ð¼ÐµÑ€ Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½Ð°',
             'tweb.Login.QR.Cancel' => 'Ð’Ñ…Ð¾Ð´ Ð¿Ð¾ Ð½Ð¾Ð¼ÐµÑ€Ñƒ Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½Ð° >',
             'tweb.Login.QR.Help1' => 'ÐžÑ‚ÐºÑ€Ð¾Ð¹Ñ‚Ðµ Telegram Ð½Ð° Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½Ðµ',
             'tweb.Login.QR.Help2' => 'ÐŸÐµÑ€ÐµÐ¹Ð´Ð¸Ñ‚Ðµ Ð² **Ð�Ð°Ñ�Ñ‚Ñ€Ð¾Ð¹ÐºÐ¸** > **Ð£Ñ�Ñ‚Ñ€Ð¾Ð¹Ñ�Ñ‚Ð²Ð°** > **ÐŸÐ¾Ð´ÐºÐ»ÑŽÑ‡Ð¸Ñ‚ÑŒ ÑƒÑ�Ñ‚Ñ€Ð¾Ð¹Ñ�Ñ‚Ð²Ð¾**',
             'tweb.Login.QR.Help3' => 'Ð�Ð°Ð²ÐµÐ´Ð¸Ñ‚Ðµ Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½ Ð½Ð° Ñ�Ñ‚Ð¾Ñ‚ Ñ�ÐºÑ€Ð°Ð½, Ñ‡Ñ‚Ð¾Ð±Ñ‹ Ð¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¸Ñ‚ÑŒ Ð²Ñ…Ð¾Ð´',
             'tweb.Login.QR.Login' => 'Ð’Ñ…Ð¾Ð´ Ð¿Ð¾ QR-ÐºÐ¾Ð´Ñƒ >',
             'tweb.Login.QR.Subtitle' => 'ÐžÑ‚Ñ�ÐºÐ°Ð½Ð¸Ñ€ÑƒÐ¹Ñ‚Ðµ Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸ÐµÐ¼ Telegram Ð½Ð° Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½Ðµ',
             'tweb.Login.QR.Title' => 'Ð’Ñ…Ð¾Ð´ Ð¿Ð¾ QR-ÐºÐ¾Ð´Ñƒ',
             'tweb.Login.Register.Subtitle' => 'Ð’Ð²ÐµÐ´Ð¸Ñ‚Ðµ Ñ�Ð²Ð¾Ñ‘ Ð¸Ð¼Ñ�,
Ñ‡Ñ‚Ð¾Ð±Ñ‹ Ñ�Ð¾Ð·Ð´Ð°Ñ‚ÑŒ Ð°ÐºÐºÐ°ÑƒÐ½Ñ‚',
             'tweb.Login.StartText' => 'ÐŸÐ¾Ð¶Ð°Ð»ÑƒÐ¹Ñ�Ñ‚Ð°, Ð¿Ñ€Ð¾Ð²ÐµÑ€ÑŒÑ‚Ðµ ÐºÐ¾Ð´ Ñ�Ñ‚Ñ€Ð°Ð½Ñ‹
Ð¸ Ð²Ð²ÐµÐ´Ð¸Ñ‚Ðµ Ñ�Ð²Ð¾Ð¹ Ð½Ð¾Ð¼ÐµÑ€ Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½Ð°.',
             'tweb.Login.Title' => 'Ð’Ñ…Ð¾Ð´ Ð² MadelineProto',
             'tweb.LoginPassword' => 'ÐŸÐ°Ñ€Ð¾Ð»ÑŒ',
             'tweb.Optional' => 'Ð½ÐµÐ¾Ð±Ñ�Ð·Ð°Ñ‚ÐµÐ»ÑŒÐ½Ð¾',
             'tweb.PASSWORD_HASH_INVALID' => 'Ð�ÐµÐ²ÐµÑ€Ð½Ñ‹Ð¹ Ð¿Ð°Ñ€Ð¾Ð»ÑŒ',
             'tweb.PHONE_CODE_EMPTY' => 'Ð�ÐµÐ²ÐµÑ€Ð½Ñ‹Ð¹ ÐºÐ¾Ð´',
             'tweb.PHONE_CODE_EXPIRED' => 'Ð¡Ñ€Ð¾Ðº Ð´ÐµÐ¹Ñ�Ñ‚Ð²Ð¸Ñ� ÐºÐ¾Ð´Ð° Ð¸Ñ�Ñ‚Ñ‘Ðº',
             'tweb.PHONE_CODE_INVALID' => 'Ð�ÐµÐ²ÐµÑ€Ð½Ñ‹Ð¹ ÐºÐ¾Ð´',
             'tweb.PHONE_NUMBER_BANNED' => 'Ð­Ñ‚Ð¾Ñ‚ Ð½Ð¾Ð¼ÐµÑ€ Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½Ð° Ð·Ð°Ð±Ð»Ð¾ÐºÐ¸Ñ€Ð¾Ð²Ð°Ð½',
             'tweb.PHONE_NUMBER_FLOOD' => 'Ð¡Ð»Ð¸ÑˆÐºÐ¾Ð¼ Ð¼Ð½Ð¾Ð³Ð¾ Ð¿Ð¾Ð¿Ñ‹Ñ‚Ð¾Ðº, Ð¿Ð¾Ð¿Ñ€Ð¾Ð±ÑƒÐ¹Ñ‚Ðµ Ð¿Ð¾Ð·Ð¶Ðµ',
             'tweb.PHONE_NUMBER_INVALID' => 'Ð�ÐµÐ²ÐµÑ€Ð½Ñ‹Ð¹ Ð½Ð¾Ð¼ÐµÑ€ Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½Ð°',
             'tweb.PHONE_PASSWORD_FLOOD' => 'Ð¡Ð»Ð¸ÑˆÐºÐ¾Ð¼ Ð¼Ð½Ð¾Ð³Ð¾ Ð¿Ð¾Ð¿Ñ‹Ñ‚Ð¾Ðº, Ð¿Ð¾Ð¿Ñ€Ð¾Ð±ÑƒÐ¹Ñ‚Ðµ Ð¿Ð¾Ð·Ð¶Ðµ',
             'tweb.PhoneNumber.Code.Fragment.Info' => 'ÐŸÐ¾Ð»ÑƒÑ‡Ð¸Ñ‚Ðµ ÐºÐ¾Ð´ Ð² Ñ€Ð°Ð·Ð´ÐµÐ»Ðµ Numbers Ð½Ð° [Fragment]()',
             'tweb.PleaseWait' => 'ÐŸÐ¾Ð¶Ð°Ð»ÑƒÐ¹Ñ�Ñ‚Ð°, Ð¿Ð¾Ð´Ð¾Ð¶Ð´Ð¸Ñ‚Ðµ...',
             'tweb.StartMessaging' => 'Ð�Ð°Ñ‡Ð°Ñ‚ÑŒ Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ðµ',
             'tweb.YourName' => 'Ð’Ð°ÑˆÐµ Ð¸Ð¼Ñ�',
             'type_extract_error' => 'Ð�ÐµÐ²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾ Ñ€Ð°Ñ�Ð¿Ð°ÐºÐ¾Ð²Ð°Ñ‚ÑŒ Ñ‚Ð¸Ð¿ "%s", Ð½ÐµÐ¾Ð±Ñ…Ð¾Ð´Ð¸Ð¼Ð¾ Ð¾Ð±Ð½Ð¾Ð²Ð¸Ñ‚ÑŒ MadelineProto!',
             'type_extract_error_id' => 'Ð�ÐµÐ²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾ Ñ€Ð°Ñ�Ð¿Ð°ÐºÐ¾Ð²Ð°Ñ‚ÑŒ Ñ‚Ð¸Ð¿ %s Ñ� id %s, Ð½ÐµÐ¾Ð±Ñ…Ð¾Ð´Ð¸Ð¼Ð¾ Ð¾Ð±Ð½Ð¾Ð²Ð¸Ñ‚ÑŒ MadelineProto!',
             'update_madelineproto' => 'Ð’Ñ‹ Ð¸Ñ�Ð¿Ð¾Ð»ÑŒÐ·ÑƒÐµÑ‚Ðµ Ñ�Ñ‚Ð°Ñ€ÑƒÑŽ Ð²ÐµÑ€Ñ�Ð¸ÑŽ MadelineProto, Ð½ÐµÐ¾Ð±Ñ…Ð¾Ð´Ð¸Ð¼Ð¾ Ð¾Ð±Ð½Ð¾Ð²Ð¸Ñ‚ÑŒÑ�Ñ�: Ñ�ÐµÐ¹Ñ‡Ð°Ñ� Ð¸Ñ�Ð¿Ð¾Ð»ÑŒÐ·ÑƒÐµÑ‚Ñ�Ñ� Ð²ÐµÑ€Ñ�Ð¸Ñ� %s, Ð½Ð¾ Ð¿Ð¾Ñ�Ð»ÐµÐ´Ð½Ñ�Ñ� Ð²ÐµÑ€Ñ�Ð¸Ñ� Ñ�Ð¾ Ð¼Ð½Ð¾Ð³Ð¸Ð¼Ð¸ Ð¸Ñ�Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ�Ð¼Ð¸ Ð¸ Ð½Ð¾Ð²Ñ‹Ð¼ Ñ„ÑƒÐ½ÐºÑ†Ð¸Ð¾Ð½Ð°Ð»Ð¾Ð¼ - %s!',
             'value_bigger_than_2147483647' => 'Ð—Ð½Ð°Ñ‡ÐµÐ½Ð¸Ðµ %s Ð±Ð¾Ð»ÑŒÑˆÐµ Ñ‡ÐµÐ¼ 2147483647',
             'value_bigger_than_4294967296' => 'Ð—Ð½Ð°Ñ‡ÐµÐ½Ð¸Ðµ %s Ð±Ð¾Ð»ÑŒÑˆÐµ Ñ‡ÐµÐ¼ 4294967296',
             'value_bigger_than_9223372036854775807' => 'Ð—Ð½Ð°Ñ‡ÐµÐ½Ð¸Ðµ %s Ð±Ð¾Ð»ÑŒÑˆÐµ Ñ‡ÐµÐ¼ 9223372036854775807',
             'value_smaller_than_0' => 'Ð—Ð½Ð°Ñ‡ÐµÐ½Ð¸Ðµ %s Ð¼ÐµÐ½ÑŒÑˆÐµ Ð½ÑƒÐ»Ñ�',
             'value_smaller_than_2147483648' => 'Ð—Ð½Ð°Ñ‡ÐµÐ½Ð¸Ðµ %s Ð¼ÐµÐ½ÑŒÑˆÐµ Ñ‡ÐµÐ¼ -2147483648',
             'value_smaller_than_9223372036854775808' => 'Ð—Ð½Ð°Ñ‡ÐµÐ½Ð¸Ðµ %s Ð¼ÐµÐ½ÑŒÑˆÐµ Ñ‡ÐµÐ¼ -9223372036854775808',
             'waveform_must_have_100_values' => 'ÐœÐ°Ñ�Ñ�Ð¸Ð² waveform Ð´Ð¾Ð»Ð¶ÐµÐ½ Ð¸Ð¼ÐµÑ‚ÑŒ 100 Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ð¹!',
             'waveform_value' => 'Ð—Ð½Ð°Ñ‡ÐµÐ½Ð¸Ðµ waveform Ð´Ð¾Ð»Ð¶Ð½Ð¾ Ð±Ñ‹Ñ‚ÑŒ Ð¼ÐµÐ¶Ð´Ñƒ 0 Ð¸ 31!',
             'webContinue' => 'ÐŸÑ€Ð¾Ð´Ð¾Ð»Ð¶Ð¸Ñ‚ÑŒ',
             'webDocs' => 'Ð”Ð¾ÐºÑƒÐ¼ÐµÐ½Ñ‚Ð°Ñ†Ð¸Ñ�',
             'webDoneLead' => 'MadelineProto Ð¿Ñ€Ð¾Ð´Ð¾Ð»Ð¶Ð°ÐµÑ‚ Ñ€Ð°Ð±Ð¾Ñ‚Ð°Ñ‚ÑŒ Ð½Ð° Ñ�ÐµÑ€Ð²ÐµÑ€Ðµ: Ñ�Ñ‚Ñƒ Ñ�Ñ‚Ñ€Ð°Ð½Ð¸Ñ†Ñƒ Ð¼Ð¾Ð¶Ð½Ð¾ Ð·Ð°ÐºÑ€Ñ‹Ñ‚ÑŒ.',
             'webError' => '%s. ÐŸÑ€Ð¾Ð²ÐµÑ€ÑŒÑ‚Ðµ Ð¸ Ð¿Ð¾Ð¿Ñ€Ð¾Ð±ÑƒÐ¹Ñ‚Ðµ Ñ�Ð½Ð¾Ð²Ð°.',
             'webFieldRequired' => 'Ð—Ð°Ð¿Ð¾Ð»Ð½Ð¸Ñ‚Ðµ Ñ�Ñ‚Ð¾ Ð¿Ð¾Ð»Ðµ.',
             'webHidePassword' => 'Ð¡ÐºÑ€Ñ‹Ñ‚ÑŒ',
             'webLogin' => 'Ð’Ð¾Ð¹Ñ‚Ð¸',
             'webOptional' => 'Ð½ÐµÐ¾Ð±Ñ�Ð·Ð°Ñ‚ÐµÐ»ÑŒÐ½Ð¾',
             'webPhoneInvalid' => 'Ð’Ð²ÐµÐ´Ð¸Ñ‚Ðµ Ð½Ð¾Ð¼ÐµÑ€ Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½Ð° Ñ� ÐºÐ¾Ð´Ð¾Ð¼ Ñ�Ñ‚Ñ€Ð°Ð½Ñ‹, Ð½Ð°Ð¿Ñ€Ð¸Ð¼ÐµÑ€ +39 123 456 7890.',
             'webProgress' => 'Ð¥Ð¾Ð´ Ð½Ð°Ñ�Ñ‚Ñ€Ð¾Ð¹ÐºÐ¸',
             'webQrTitle' => 'Ð’Ñ…Ð¾Ð´ Ð¿Ð¾ QR-ÐºÐ¾Ð´Ñƒ',
             'webQrWaiting' => 'ÐžÐ¶Ð¸Ð´Ð°Ð½Ð¸Ðµ Ñ�ÐºÐ°Ð½Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ�â€¦',
             'webShowPassword' => 'ÐŸÐ¾ÐºÐ°Ð·Ð°Ñ‚ÑŒ',
             'webStepAccount' => 'Ð�ÐºÐºÐ°ÑƒÐ½Ñ‚',
             'webStepApp' => 'API ID Ð¸ API hash',
             'webStepDone' => 'Ð Ð°Ð±Ð¾Ñ‚Ð°ÐµÑ‚',
             'webStepVerify' => 'ÐŸÐ¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´ÐµÐ½Ð¸Ðµ',
         ],
        'tr' =>
         [
             '2fa_uncalled' => 'Parola beklemiyorum! LÃ¼tfen Ã¶nce phoneLogin ve completePhoneLogin metotlarÄ±nÄ± Ã§aÄŸÄ±rÄ±n!',
             'accepting_call' => '%s tarafÄ±ndan gelen arama kabul ediliyor...',
             'account_banned' => '!!!!!!! UYARI !!!!!!!
Telegram\'Ä±n flood Ã¶nleme sistemi bu hesabÄ± askÄ±ya aldÄ±.
Devam etmek iÃ§in manuel doÄŸrulama gerekiyor.
recover@telegram.org adresine %s telefon numarasÄ±nÄ±n engelinin kaldÄ±rÄ±lmasÄ±nÄ± isteyen bir e-posta gÃ¶nderin ve bu telefon numarasÄ±yla ne yapacaÄŸÄ±nÄ±zÄ± kÄ±saca aÃ§Ä±klayÄ±n.
ArdÄ±ndan tekrar oturum aÃ§Ä±n.
Bu hesabÄ± bilerek sildiyseniz bu mesajÄ± dikkate almayÄ±n.',
             'already_loggedIn' => 'Bu MadelineProto Ã¶rneÄŸinde zaten oturum aÃ§Ä±lmÄ±ÅŸ!',
             'apiAppDescLabelWeb' => 'AÃ§Ä±klama',
             'apiAppInstructionsAuto0' => 'UygulamanÄ±n adÄ±nÄ± girin, herhangi bir ÅŸey olabilir: ',
             'apiAppInstructionsAuto1' => 'UygulamanÄ±n kÄ±sa adÄ±nÄ± girin, alfanÃ¼merik, 5-32 karakter: ',
             'apiAppInstructionsAuto2' => 'UygulamanÄ±n/web sitesinin URL\'sini veya t.me/yourusername girin: ',
             'apiAppInstructionsAuto3' => 'UygulamanÄ±n platformunu girin: ',
             'apiAppInstructionsAuto4' => 'UygulamanÄ±zÄ± aÃ§Ä±klayÄ±n: ',
             'apiAppInstructionsAutoTypeOther' => 'DiÄŸer (aÃ§Ä±klamada belirtin)',
             'apiAppInstructionsManual0' => 'uygulamanÄ±zÄ±n adÄ±, herhangi bir ÅŸey olabilir',
             'apiAppInstructionsManual1' => 'uygulamanÄ±zÄ±n kÄ±sa adÄ±, alfanÃ¼merik, 5-32 karakter',
             'apiAppInstructionsManual2' => 'uygulamanÄ±zÄ±n/web sitenizin URL\'si veya t.me/yourusername',
             'apiAppInstructionsManual3' => 'herhangi bir ÅŸey',
             'apiAppInstructionsManual4' => 'UygulamanÄ±zÄ± burada aÃ§Ä±klayÄ±n',
             'apiAppShortNameLabelWeb' => 'KÄ±sa ad',
             'apiAppTitleLabelWeb' => 'Uygulama adÄ±',
             'apiAppUrlLabelWeb' => 'URL',
             'apiAppWeb' => 'API bilgilerini girin',
             'apiAutoCodeLabelWeb' => 'GiriÅŸ kodu',
             'apiAutoCodeLeadWeb' => 'my.telegram.org, Telegram uygulamanÄ±za Telegram sohbetinde bir giriÅŸ kodu gÃ¶nderdi (SMS ile deÄŸil).',
             'apiAutoCodeTitleWeb' => 'Telegram\'dan gelen kodu girin',
             'apiAutoErrorTitle' => 'my.telegram.org bir hata dÃ¶ndÃ¼rdÃ¼',
             'apiAutoFallbackManual' => 'Bunun yerine API ID ve hash bilgilerini manuel olarak girin.',
             'apiAutoPhoneHintWeb' => 'my.telegram.org giriÅŸ kodunu SMS ile deÄŸil, Telegram uygulamanÄ±za gÃ¶nderir.',
             'apiAutoPrompt0' => 'Telegram\'a zaten kayÄ±tlÄ± bir telefon numarasÄ± girin: ',
             'apiAutoPrompt1' => 'Telegram\'da aldÄ±ÄŸÄ±nÄ±z doÄŸrulama kodunu girin: ',
             'apiAutoWeb' => 'API ID\'yi almak iÃ§in Telegram\'a <b>zaten kayÄ±tlÄ±</b> bir telefon numarasÄ± girin',
             'apiChooseAutoWeb' => 'Otomatik olarak al',
             'apiChooseManualAuto' => 'API ID ve hash bilgilerini my.telegram.org adresinden otomatik olarak mÄ± almak istersiniz (a), yoksa manuel olarak mÄ± girmek istersiniz (m)?',
             'apiChooseManualAutoTip' => 'API ID/hash bilgisini ayarlar aracÄ±lÄ±ÄŸÄ±yla doÄŸrudan kodda da saÄŸlayabileceÄŸinizi unutmayÄ±n: %s',
             'apiChooseManualAutoTipWeb' => 'API ID/hash bilgisini <a target="_blank" href="%s">ayarlar</a> aracÄ±lÄ±ÄŸÄ±yla doÄŸrudan kodda da saÄŸlayabileceÄŸinizi unutmayÄ±n.',
             'apiChooseManualWeb' => 'Zaten bir API ID\'m var',
             'apiChoosePrompt' => 'SeÃ§iminiz (m/a): ',
             'apiCreateErrorTitle' => 'my.telegram.org uygulamayÄ± oluÅŸturmadÄ±',
             'apiCreateLeadWeb' => 'Bu hesabÄ±n my.telegram.org Ã¼zerinde henÃ¼z bir uygulamasÄ± yok. Bu bilgiler yalnÄ±zca size gÃ¶sterilir ve daha sonra deÄŸiÅŸtirilebilir.',
             'apiCreateTitleWeb' => 'UygulamanÄ±zÄ± oluÅŸturun',
             'apiCreateVpsWarning' => 'Telegram, VPS, VPN ve diÄŸer veri merkezi IP aralÄ±klarÄ±nÄ±n my.telegram.org Ã¼zerinde yeni uygulama oluÅŸturmasÄ±na izin vermez ve istek, MadelineProto\'yu Ã§alÄ±ÅŸtÄ±ran makinenin IP adresinden yapÄ±ldÄ±. UygulamayÄ± bir ev baÄŸlantÄ±sÄ±ndan (Ã¶rneÄŸin mobil veri kullanan telefonunuzdan) https://my.telegram.org adresinde manuel olarak oluÅŸturun, ardÄ±ndan API ID ve hash bilgilerini girin.',
             'apiCreateWeb' => 'Uygulama oluÅŸtur',
             'apiError' => 'HATA: %s. Tekrar deneyin.',
             'apiInvalidWeb' => 'API ID bir sayÄ±dÄ±r, API hash ise 32 karakter uzunluÄŸundadÄ±r: bunlarÄ± my.telegram.org adresinden yeniden kopyalayÄ±n.',
             'apiManualInstructions0' => 'https://my.telegram.org adresinde oturum aÃ§Ä±n',
             'apiManualInstructions1' => '"API development tools" bÃ¶lÃ¼mÃ¼ne gidin',
             'apiManualInstructions2' => '"create application" seÃ§eneÄŸine tÄ±klayÄ±n',
             'apiManualPrompt0' => 'API ID\'nizi girin: ',
             'apiManualPrompt1' => 'API hash\'inizi girin: ',
             'apiManualWeb' => 'API ID ve API hash bilgilerinizi girin',
             'apiParamsError' => 'Gerekli parametrelerin tÃ¼mÃ¼nÃ¼ saÄŸlamadÄ±nÄ±z!',
             'apiSaveWeb' => 'Kaydet ve devam et',
             'apiSendCodeWeb' => 'Kod gÃ¶nder',
             'apiSetupLeadWeb' => 'Her Telegram uygulamasÄ±nÄ±n bir API ID ve hash bilgisine ihtiyacÄ± vardÄ±r. MadelineProto, mevcut bir Telegram hesabÄ±nÄ±n telefon numarasÄ±yla bunlarÄ± sizin iÃ§in my.telegram.org adresinden alabilir.',
             'apiSetupTitleWeb' => 'API ID ve hash bilgilerinizi alÄ±n',
             'apiStartOverWeb' => 'BaÅŸtan baÅŸla',
             'api_not_set' => 'Bir api key ve api id saÄŸlamanÄ±z gerekir, kendinizinkini my.telegram.org adresinden alÄ±n',
             'array_invalid' => 'GeÃ§erli bir dizi saÄŸlamadÄ±nÄ±z',
             'baseDirLimitation' => 'Bir basedir kÄ±sÄ±tlamasÄ± yapÄ±landÄ±rÄ±lmÄ±ÅŸ: bu, performansÄ± etkileyebilir ve bazÄ± sorunlara yol aÃ§abilir, mÃ¼mkÃ¼nse lÃ¼tfen devre dÄ±ÅŸÄ± bÄ±rakÄ±n!',
             'bool_error' => 'Boolean deÄŸer Ã§Ä±karÄ±lamadÄ±',
             'botAlreadyRunning' => 'Bot zaten Ã§alÄ±ÅŸÄ±yor!',
             'botapi_conversion_error' => '%s bir bot API nesnesine dÃ¶nÃ¼ÅŸtÃ¼rÃ¼lemiyor',
             'call_already_accepted' => '%s aramasÄ± zaten kabul edildi',
             'call_already_declined' => '%s aramasÄ± zaten reddedildi',
             'call_completing' => '%s tarafÄ±ndan gelen aramanÄ±n kurulumu tamamlanÄ±yor...',
             'call_confirming' => '%s tarafÄ±ndan gelen arama onaylanÄ±yor...',
             'call_discarding' => '%s aramasÄ± sonlandÄ±rÄ±lÄ±yor...',
             'call_error_1' => '%s aramasÄ± bulunup kabul edilemedi',
             'call_error_2' => '%s aramasÄ± bulunup onaylanamadÄ±',
             'call_error_3' => '%s aramasÄ± bulunup kurulumu tamamlanamadÄ±',
             'cli_need_dl.php_link' => 'getDownloadLink\'i CLI Ã¼zerinden kullanÄ±rken lÃ¼tfen bir indirme betiÄŸi URL\'si belirtin!',
             'constructor_not_found' => 'Åžu tÃ¼r iÃ§in constructor bulunamadÄ±: ',
             'could_not_connect_to_MadelineProto' => 'MadelineProto\'ya baÄŸlanÄ±lamadÄ±, dÃ¼zeltmek iÃ§in lÃ¼tfen proc_open\'Ä± etkinleÅŸtirin ve open_basedir kÄ±sÄ±tlamalarÄ±nÄ± kaldÄ±rÄ±n ya da web sunucusunun yol yeniden yazma (rewrite) Ã¶zelliÄŸini devre dÄ±ÅŸÄ± bÄ±rakÄ±n! Bunu zaten yaptÄ±ysanÄ±z, PHP\'nin CLI sÃ¼rÃ¼mÃ¼nÃ¼n web sÃ¼rÃ¼mÃ¼yle tamamen aynÄ± olduÄŸundan (aynÄ± sÃ¼rÃ¼m, eklentiler vb.) emin olun ve IPC sunucusunun baÅŸlamasÄ±nÄ± engelleyen hata hakkÄ±nda daha fazla bilgi iÃ§in MadelineProto.log dosyasÄ±na bakÄ±n.',
             'could_not_convert_object' => '%s tÃ¼rÃ¼ndeki nesne dÃ¶nÃ¼ÅŸtÃ¼rÃ¼lemedi',
             'deserialization_error' => 'SerileÅŸtirmeyi Ã§Ã¶zme sÄ±rasÄ±nda bir hata oluÅŸtu',
             'dl.php_check_logs_make_sure_session_running' => 'Ä°liÅŸkili MadelineProto EventHandler botu veya MadelineProto IPC sunucusu Ã§evrimdÄ±ÅŸÄ±, lÃ¼tfen gÃ¼nlÃ¼kleri kontrol edin ve en az birinin Ã§alÄ±ÅŸtÄ±ÄŸÄ±ndan emin olun!',
             'dl.php_powered_by_madelineproto' => '<a href="https://docs.madelineproto.xyz" target="_blank">MadelineProto</a> tarafÄ±ndan desteklenen Telegram dosya indirme sunucusu (4 GB\'a kadar)!<br>Kendi Telegram dosya indirme sunucunuzu nasÄ±l kuracaÄŸÄ±nÄ±z hakkÄ±nda daha fazla bilgi iÃ§in <a href="https://docs.madelineproto.xyz/docs/FILES.html#getting-a-download-link" target="_blank">buraya</a> tÄ±klayÄ±n!',
             'do_not_delete_MadelineProto.log' => 'MadelineProto.log dosyasÄ± asla silinmemelidir, bunun yerine lÃ¼tfen ayarlarda Ã¶zel bir maksimum boyut belirleyin!',
             'do_not_remove_MadelineProto.log_phar' => 'LÃ¼tfen madeline.phar veya madeline.php dosyalarÄ±nÄ± silmeyin, aksi halde MadelineProto Ã§Ã¶ker. MadelineProto ile ilgili bir sorununuz varsa https://github.com/danog/MadelineProto veya https://t.me/pwrtelegramgroup adresine bildirin',
             'do_not_use_blocking_class' => 'performans nedenleriyle, iÅŸleyiciler async olmayan, engelleyici %s sÄ±nÄ±fÄ±nÄ± kullanamaz, lÃ¼tfen bunun yerine %s kullanÄ±n',
             'do_not_use_blocking_function' => 'performans nedenleriyle, olay iÅŸleyiciler async olmayan, engelleyici %s fonksiyonunu kullanamaz, lÃ¼tfen bunun yerine %s kullanÄ±n',
             'do_not_use_deprecated_function' => '%s fonksiyonu kullanÄ±mdan kaldÄ±rÄ±lmÄ±ÅŸtÄ±r, lÃ¼tfen bunun yerine %s kullanÄ±n',
             'do_not_use_non_root_require_in_event_handler' => 'performans nedenleriyle, bir olay iÅŸleyici sÄ±nÄ±fÄ±nÄ±n iÃ§inde require veya include kullanmamalÄ±sÄ±nÄ±z, yalnÄ±zca kÃ¶k dÃ¼zeydeki require\'lara izin verilir.',
             'do_not_use_yield' => 'MadelineProto 8, async fonksiyonlarda yield kullanÄ±mÄ±nÄ± gerektirmez ve desteklemez; daha Ã¶nce async fonksiyon Ã§aÄŸrÄ±larÄ± iÃ§in kullanÄ±lan tÃ¼m yield anahtar sÃ¶zcÃ¼klerini kaldÄ±rmalÄ±sÄ±nÄ±z',
             'done' => 'TamamlandÄ±!',
             'encode_double_error' => 'double deÄŸeri dÃ¼zgÃ¼n kodlanamadÄ±',
             'extensionRecommended' => 'UyarÄ±: %s eklentisi kurulu deÄŸil, MadelineProto\'yu hÄ±zlandÄ±rmak iÃ§in lÃ¼tfen kurun!',
             'extensionRequired' => 'MadelineProto\'nun Ã§alÄ±ÅŸmasÄ± iÃ§in %s eklentisi gereklidir. %s',
             'extensionRequiredInstallWithApt' => 'sudo apt-get install %s komutunu Ã§alÄ±ÅŸtÄ±rmayÄ± deneyin.',
             'extensionRequiredInstallWithCustomInstructions' => 'Kurmak iÃ§in %s adresindeki talimatlarÄ± izleyin.',
             'file_not_exist' => 'Dosya mevcut deÄŸil',
             'file_parsing' => '%s ayrÄ±ÅŸtÄ±rÄ±lÄ±yor...',
             'file_type_invalid' => 'GeÃ§ersiz dosya tÃ¼rÃ¼ algÄ±landÄ± (%s)',
             'fingerprint_invalid' => 'GeÃ§ersiz anahtar parmak izi!',
             'go' => 'Devam',
             'invalid_dl.php_session' => '%s geÃ§erli bir indirme betiÄŸi deÄŸil, Ã§Ã¼nkÃ¼ oturum kimliÄŸi farklÄ± (beklenen %s, alÄ±nan %s)',
             'ipc_tcp_warning' => 'UNIX sockets are not available, so the IPC server of the session listens on TCP (127.0.0.1) instead: any program running on this machine can connect to it, and control the session. On Windows 10 1803+, enable the sockets extension (extension=sockets in php.ini) to use UNIX sockets.',
             'length_too_big' => 'Uzunluk Ã§ok bÃ¼yÃ¼k',
             'loginBot' => 'Bot token\'Ä±nÄ±zÄ± girin: ',
             'loginBotTokenHintWeb' => 'Botu oluÅŸturduÄŸunuzda @BotFather\'Ä±n size verdiÄŸi token.',
             'loginBotTokenWeb' => 'Bot token\'Ä±',
             'loginChoosePromptWeb' => 'KullanÄ±cÄ± olarak mÄ± yoksa bot olarak mÄ± oturum aÃ§mak istiyorsunuz?',
             'loginCodeLeadWeb' => 'Telegram, Telegram uygulamanÄ±za bir giriÅŸ kodu gÃ¶nderdi; baÅŸka hiÃ§bir yerde oturum aÃ§madÄ±ysanÄ±z kod SMS ile gÃ¶nderilir.',
             'loginCodeTitleWeb' => 'GiriÅŸ kodunu girin',
             'loginLeadWeb' => 'Bu MadelineProto Ã¶rneÄŸinin hangi hesap veya bot olarak Ã§alÄ±ÅŸacaÄŸÄ±nÄ± seÃ§in.',
             'loginManual' => 'Alternatif olarak, manuel olarak oturum aÃ§mak iÃ§in bir bot token\'Ä± veya telefon numarasÄ± da girebilirsiniz: ',
             'loginNoCode' => 'DoÄŸrulama kodunu girmediniz!',
             'loginNoName' => 'AdÄ± girmediniz!',
             'loginNoPass' => 'ParolayÄ± girmediniz!',
             'loginOptionBot' => 'Bot',
             'loginOptionUser' => 'KullanÄ±cÄ±',
             'loginPasswordLabelWeb' => 'Parola',
             'loginPasswordLeadWeb' => 'Bu hesap, Ayarlar > Gizlilik ve GÃ¼venlik > Ä°ki AdÄ±mlÄ± DoÄŸrulama bÃ¶lÃ¼mÃ¼nde belirlenen bir parolayla korunuyor.',
             'loginPasswordTitleWeb' => 'Ä°ki adÄ±mlÄ± doÄŸrulama',
             'loginQr' => 'Otomatik olarak oturum aÃ§mak iÃ§in telefonunuzda Telegram\'Ä± aÃ§Ä±n, Ayarlar > Cihazlar > MasaÃ¼stÃ¼ CihazÄ± BaÄŸla bÃ¶lÃ¼mÃ¼ne gidin ve yukarÄ±daki QR kodunu tarayÄ±n.',
             'loginQrCodeExpired' => 'QR kodunun sÃ¼resi doldu, yenisi oluÅŸturuluyor...',
             'loginQrCodeSuccessful' => 'QR kodu ile oturum aÃ§ma baÅŸarÄ±lÄ±!',
             'loginTitleWeb' => 'Telegram\'da oturum aÃ§Ä±n',
             'loginTypeWeb' => 'Åžu olarak oturum aÃ§',
             'loginUser' => 'Telefon numaranÄ±zÄ± girin: ',
             'loginUserCode' => 'Kodu girin: ',
             'loginUserPass' => 'ParolanÄ±zÄ± girin (ipucu: %s): ',
             'loginUserPassHint' => 'Ä°pucu: %s',
             'loginUserPassWeb' => 'ParolanÄ±zÄ± girin: ',
             'loginUserPhoneCodeWeb' => 'Kod',
             'loginUserPhoneHintWeb' => 'Ãœlke koduyla birlikte, Ã¶rneÄŸin +39 123 456 7890.',
             'loginUserPhoneWeb' => 'Telefon numarasÄ±',
             'loginWebQr' => 'Telefonunuzda Telegram\'Ä± aÃ§Ä±p Ayarlar > Cihazlar > MasaÃ¼stÃ¼ CihazÄ± BaÄŸla bÃ¶lÃ¼mÃ¼ne giderek aÅŸaÄŸÄ±daki QR kodunu tarayarak da otomatik olarak oturum aÃ§abilirsiniz:',
             'loginWebQr1' => 'Telefonunuzda Telegram\'Ä± aÃ§Ä±n',
             'loginWebQr2' => 'Ayarlar > Cihazlar > MasaÃ¼stÃ¼ CihazÄ± BaÄŸla bÃ¶lÃ¼mÃ¼ne gidin',
             'loginWebQr3' => 'Oturum aÃ§mayÄ± onaylamak iÃ§in telefonunuzu bu ekrana doÄŸrultun',
             'login_2fa_enabled' => '2FA etkin, complete2falogin fonksiyonunu Ã§aÄŸÄ±rmanÄ±z gerekecek...',
             'login_auth_key' => 'Yetkilendirme anahtarÄ± (auth key) kullanÄ±larak oturum aÃ§Ä±lÄ±yor...',
             'login_bot' => 'Bot olarak oturum aÃ§Ä±lÄ±yor...',
             'login_code_sending' => 'Kod gÃ¶nderiliyor...',
             'login_code_sent' => 'Kod baÅŸarÄ±yla gÃ¶nderildi! Kodu aldÄ±ÄŸÄ±nÄ±zda completePhoneLogin fonksiyonunu kullanmalÄ±sÄ±nÄ±z.',
             'login_code_uncalled' => 'Kod beklemiyorum! LÃ¼tfen Ã¶nce phoneLogin metodunu Ã§aÄŸÄ±rÄ±n',
             'login_need_signup' => 'Bu numara iÃ§in bir hesap oluÅŸturulmamÄ±ÅŸ, completeSignup fonksiyonunu Ã§aÄŸÄ±rmanÄ±z gerekecek...',
             'login_ok' => 'Oturum baÅŸarÄ±yla aÃ§Ä±ldÄ±!',
             'login_user' => 'Normal kullanÄ±cÄ± olarak oturum aÃ§Ä±lÄ±yor...',
             'logout_ok' => 'Oturum baÅŸarÄ±yla kapatÄ±ldÄ±!',
             'long_not_16' => 'Verilen deÄŸer 16 bayt uzunluÄŸunda deÄŸil',
             'long_not_32' => 'Verilen deÄŸer 32 bayt uzunluÄŸunda deÄŸil',
             'long_not_64' => 'Verilen deÄŸer 64 bayt uzunluÄŸunda deÄŸil',
             'madelineproto_ready' => 'MadelineProto hazÄ±r!',
             'manualAdminActionRequired' => '!!!!!!!!! MANUEL SÄ°STEM YÃ–NETÄ°CÄ°SÄ° MÃœDAHALESÄ° GEREKLÄ° !!!!!!!!!',
             'method_not_found' => 'Metot bulunamadÄ±: ',
             'mmapErrorPart1' => 'BelleÄŸe eÅŸlenmiÅŸ (mmap) bÃ¶lge sayÄ±sÄ±nÄ±n Ã¼st sÄ±nÄ±rÄ±na ulaÅŸÄ±ldÄ± (%s): dÃ¼zeltmek iÃ§in lÃ¼tfen vm.max_map_count Ã§ekirdek ayarÄ±nÄ± 262144 deÄŸerine yÃ¼kseltin.',
             'mmapErrorPart2' => 'DÃ¼zeltmek iÃ§in aÅŸaÄŸÄ±daki komutu root olarak Ã§alÄ±ÅŸtÄ±rÄ±n: %s',
             'mmapErrorPart3' => 'DeÄŸiÅŸikliÄŸin yeniden baÅŸlatmalardan sonra da kalÄ±cÄ± olmasÄ± iÃ§in: %s',
             'mmapErrorPart4' => 'Windows ve WSL\'de sayfa dosyasÄ±nÄ±n (pagefile) boyutunu artÄ±rmak yardÄ±mcÄ± olabilir; sorun devam ederse lÃ¼tfen yerel (native) Linux\'a geÃ§in.',
             'must_have_declare_types' => 'performans nedenleriyle, bir olay iÅŸleyici dosyasÄ±nÄ±n ilk ifadesi "declare(strict_types=1);" olmalÄ±dÄ±r',
             'nearest_dc' => 'Konumumuz: %s, en yakÄ±n DC: %d.',
             'need_dl.php' => 'VarsayÄ±lan indirme betiÄŸi oluÅŸturulamadÄ± (%s), lÃ¼tfen aÅŸaÄŸÄ±daki iÃ§eriÄŸe sahip bir dl.php dosyasÄ± oluÅŸturun: %s ve URL\'sini getDownloadLink\'in ikinci parametresine iletin',
             'noReportPeers' => 'UyarÄ±: hiÃ§bir rapor alÄ±cÄ±sÄ± (report peers) ayarlanmamÄ±ÅŸ, lÃ¼tfen olay iÅŸleyicinize aÅŸaÄŸÄ±daki metodu ekleyin',
             'non_text_conversion' => 'Metin dÄ±ÅŸÄ± mesajlar henÃ¼z dÃ¶nÃ¼ÅŸtÃ¼rÃ¼lemiyor!',
             'not_loggedIn' => 'Oturum aÃ§Ä±lmamÄ±ÅŸ!',
             'not_numeric' => 'Verilen deÄŸer sayÄ±sal deÄŸil',
             'params_missing' => 'Gerekli parametre eksik',
             'peer_not_in_db' => 'Bu peer, dahili peer veritabanÄ±nda bulunmuyor',
             'plugin_path_does_not_exist' => '%s eklenti yolu mevcut deÄŸil!',
             'plugins_do_not_use_require' => 'performans nedenleriyle, eklentiler eklenti klasÃ¶rÃ¼ndeki diÄŸer dosyalarÄ± yalnÄ±zca PSR-4 otomatik yÃ¼kleyicisini tetikleyerek otomatik olarak include veya require edebilir (require() ile elle dahil ederek deÄŸil).',
             'plugins_must_have_exactly_one_class' => 'bir eklenti tam olarak bir sÄ±nÄ±f tanÄ±mlamalÄ±dÄ±r! Birden fazla sÄ±nÄ±f, arayÃ¼z veya trait tanÄ±mlamak iÃ§in ayrÄ± dosyalar oluÅŸturun; bunlar MadelineProto tarafÄ±ndan otomatik olarak yÃ¼klenecektir.',
             'predicate_not_set' => 'Predicate (_ altÄ±ndaki deÄŸer) ayarlanmadÄ±!',
             'recommend_not_use_filesystem_function' => '%s fonksiyonunun kullanÄ±lmasÄ± Ã¶nerilmez, Ã§Ã¼nkÃ¼ gÃ¼ncellemeler iÅŸlenirken dosya sistemine eriÅŸmek botunuzu yavaÅŸlatÄ±r; botunuzu yavaÅŸlatmayan alternatif veri saklama yÃ¶ntemlerinin listesi iÃ§in lÃ¼tfen https://docs.madelineproto.xyz/docs/UPDATES.html#avoiding-the-use-of-filesystem-functions adresine bakÄ±n!',
             'rpc_tg_error' => 'Telegram bir RPC hatasÄ± dÃ¶ndÃ¼rdÃ¼: %s (%s), kaynaÄŸÄ±: %s:%s%sTL izi:',
             'sec_peer_not_in_db' => 'Bu gizli peer, dahili peer veritabanÄ±nda bulunmuyor',
             'secret_chat_skipping' => '%s gizli sohbeti veritabanÄ±nda yok, mesaj atlanÄ±yor...',
             'serialization_ofd' => 'SerileÅŸtirme gÃ¼ncel deÄŸil, nesne yeniden oluÅŸturuluyor!',
             'session_corrupted' => 'Oturum bozuk!',
             'signing_up' => 'Normal kullanÄ±cÄ± olarak kaydolunuyor...',
             'signupFirstName' => 'AdÄ±nÄ±zÄ± girin: ',
             'signupFirstNameWeb' => 'Ad',
             'signupLastName' => 'SoyadÄ±nÄ±zÄ± girin (boÅŸ bÄ±rakÄ±labilir): ',
             'signupLastNameWeb' => 'Soyad',
             'signupLeadWeb' => 'Bu telefon numarasÄ± henÃ¼z Telegram\'da deÄŸil: yeni hesabÄ±n adÄ±nÄ± seÃ§in.',
             'signupTitleWeb' => 'HesabÄ±nÄ±zÄ± oluÅŸturun',
             'signupWeb' => 'LÃ¼tfen kaydolun',
             'signup_ok' => 'KayÄ±t baÅŸarÄ±lÄ±!',
             'signup_uncalled' => 'KayÄ±t beklemiyorum! LÃ¼tfen Ã¶nce phoneLogin ve completePhoneLogin metotlarÄ±nÄ± Ã§aÄŸÄ±rÄ±n!',
             'src_file_invalid' => 'GeÃ§ersiz kaynak dosyasÄ± saÄŸlandÄ±: ',
             'static_analysis_minor' => '%s statik analizi sÄ±rasÄ±nda kÃ¼Ã§Ã¼k bir sorunla karÅŸÄ±laÅŸÄ±ldÄ±: %s',
             'static_analysis_severe' => '%s statik analizi sÄ±rasÄ±nda ciddi bir sorunla karÅŸÄ±laÅŸÄ±ldÄ±: %s',
             'stream_handle_invalid' => 'GeÃ§ersiz bir akÄ±ÅŸ tanÄ±tÄ±cÄ±sÄ± (stream handle) saÄŸlandÄ±.',
             'string_required' => 'Bir dize (string) bekleniyordu!',
             'translate_madelineproto_cli' => 'MadelineProto sizin dilinize Ã§evrilebilir (mevcut Ã§eviri ilerlemesi: %%%d), Ã§eviriye katkÄ±da bulunmak iÃ§in https://github.com/danog/MadelineProto/tree/v8/langs adresine gidin!',
             'translate_madelineproto_web' => 'MadelineProto sizin dilinize Ã§evrilebilir (mevcut Ã§eviri ilerlemesi: %%%d), Ã§eviriye katkÄ±da bulunmak iÃ§in <a href="https://github.com/danog/MadelineProto/tree/v8/langs" target="_blank">buraya tÄ±klayÄ±n!</a>',
             'tweb.ACCESS_TOKEN_EXPIRED' => 'Bu bot token\'Ä± iptal edildi',
             'tweb.ACCESS_TOKEN_INVALID' => 'GeÃ§ersiz bot token\'Ä±',
             'tweb.AccDescr.LoginCode' => 'GiriÅŸ kodu',
             'tweb.Close' => 'Kapat',
             'tweb.Code' => 'Kod',
             'tweb.Contacts.PhoneNumber.Placeholder' => 'Telefon numarasÄ±',
             'tweb.Country' => 'Ãœlke',
             'tweb.DarkMode' => 'KaranlÄ±k mod',
             'tweb.Error.AnError' => 'Bir hata oluÅŸtu. LÃ¼tfen daha sonra tekrar deneyin.',
             'tweb.FIRSTNAME_INVALID' => 'GeÃ§ersiz ad',
             'tweb.FirstName' => 'Ad (zorunlu)',
             'tweb.LASTNAME_INVALID' => 'GeÃ§ersiz soyad',
             'tweb.LastName' => 'Soyad (isteÄŸe baÄŸlÄ±)',
             'tweb.Login.Bot' => 'Bot olarak oturum aÃ§ >',
             'tweb.Login.Bot.Cancel' => 'KullanÄ±cÄ± olarak oturum aÃ§ >',
             'tweb.Login.Bot.Subtitle' => 'Botu oluÅŸturduÄŸunuzda @BotFather\'Ä±n
size verdiÄŸi token\'Ä± girin.',
             'tweb.Login.Bot.Title' => 'Bot olarak oturum aÃ§Ä±n',
             'tweb.Login.Bot.Token' => 'Bot token\'Ä±',
             'tweb.Login.Code.SecretPhrase' => 'Gizli ifade',
             'tweb.Login.Code.SecretWord' => 'Gizli kelime',
             'tweb.Login.Code.SentCall' => 'Sizi arayÄ±p kodu
sesli olarak ileteceÄŸiz.',
             'tweb.Login.Code.SentEmail' => 'LÃ¼tfen **%s** e-posta adresinizi kontrol edin (spam klasÃ¶rÃ¼nÃ¼ unutmayÄ±n) ve az Ã¶nce gÃ¶nderdiÄŸimiz kodu girin.',
             'tweb.Login.Code.SentInApp' => 'Size Telegram\'da kodu iÃ§eren
bir mesaj gÃ¶nderdik.',
             'tweb.Login.Code.SentMissedCall' => 'BirkaÃ§ saniye iÃ§inde **%s** numarasÄ±ndan
kÄ±sa bir arama alacaksÄ±nÄ±z. AramayÄ± yanÄ±tlamayÄ±n,
yalnÄ±zca o numaranÄ±n son hanelerini girin.',
             'tweb.Login.Code.SentSms' => 'Size kodu iÃ§eren
bir SMS gÃ¶nderdik.',
             'tweb.Login.Code.SentSmsPhrase' => '**%s** numaralÄ± telefonunuza
gizli ifadeyi iÃ§eren bir SMS gÃ¶nderdik.',
             'tweb.Login.Code.SentSmsPhraseBeginning' => '**%1$s** numaralÄ± telefonunuza "**%2$s**" ile
baÅŸlayan gizli bir ifade iÃ§eren SMS gÃ¶nderdik.',
             'tweb.Login.Code.SentSmsWord' => '**%s** numaralÄ± telefonunuza
gizli kelimeyi iÃ§eren bir SMS gÃ¶nderdik.',
             'tweb.Login.Code.SentSmsWordBeginning' => '**%1$s** numaralÄ± telefonunuza "**%2$s**" ile
baÅŸlayan gizli bir kelime iÃ§eren SMS gÃ¶nderdik.',
             'tweb.Login.Code.SentUnknown' => 'LÃ¼tfen kod iÃ§in her yeri
kontrol edin (tÃ¼r: %s)',
             'tweb.Login.Code.WordBeginningInvalid' => 'YanlÄ±ÅŸ kelime',
             'tweb.Login.Code.WordInvalid' => 'YanlÄ±ÅŸ, lÃ¼tfen tekrar deneyin',
             'tweb.Login.Next' => 'Ä°leri',
             'tweb.Login.Password.Subtitle' => 'HesabÄ±nÄ±z ek bir parola
ile korunuyor',
             'tweb.Login.Password.Title' => 'ParolanÄ±zÄ± girin',
             'tweb.Login.PhoneLabelInvalid' => 'GeÃ§ersiz telefon numarasÄ±',
             'tweb.Login.QR.Cancel' => 'Telefon numarasÄ±yla oturum aÃ§ >',
             'tweb.Login.QR.Help1' => 'Telefonunuzda Telegram\'Ä± aÃ§Ä±n',
             'tweb.Login.QR.Help2' => '**Ayarlar** > **Cihazlar** > **Cihaz Ekle** bÃ¶lÃ¼mÃ¼ne gidin',
             'tweb.Login.QR.Help3' => 'Oturum aÃ§mayÄ± onaylamak iÃ§in telefonunuzu bu ekrana doÄŸrultun',
             'tweb.Login.QR.Login' => 'QR kodu ile oturum aÃ§ >',
             'tweb.Login.QR.Subtitle' => 'Telefonunuzdaki Telegram uygulamasÄ±yla tarayÄ±n',
             'tweb.Login.QR.Title' => 'QR kodu ile oturum aÃ§Ä±n',
             'tweb.Login.Register.Subtitle' => 'HesabÄ±nÄ±zÄ± oluÅŸturmak iÃ§in
adÄ±nÄ±zÄ± girin',
             'tweb.Login.StartText' => 'LÃ¼tfen Ã¼lke kodunuzu onaylayÄ±n
ve telefon numaranÄ±zÄ± girin.',
             'tweb.Login.Title' => 'MadelineProto\'da oturum aÃ§Ä±n',
             'tweb.LoginPassword' => 'Parola',
             'tweb.Optional' => 'isteÄŸe baÄŸlÄ±',
             'tweb.PASSWORD_HASH_INVALID' => 'YanlÄ±ÅŸ parola',
             'tweb.PHONE_CODE_EMPTY' => 'GeÃ§ersiz kod',
             'tweb.PHONE_CODE_EXPIRED' => 'Kodun sÃ¼resi doldu',
             'tweb.PHONE_CODE_INVALID' => 'GeÃ§ersiz kod',
             'tweb.PHONE_NUMBER_BANNED' => 'Bu telefon numarasÄ± yasaklanmÄ±ÅŸ',
             'tweb.PHONE_NUMBER_FLOOD' => 'Ã‡ok fazla deneme yapÄ±ldÄ±, daha sonra tekrar deneyin',
             'tweb.PHONE_NUMBER_INVALID' => 'GeÃ§ersiz telefon numarasÄ±',
             'tweb.PHONE_PASSWORD_FLOOD' => 'Ã‡ok fazla deneme yapÄ±ldÄ±, daha sonra tekrar deneyin',
             'tweb.PhoneNumber.Code.Fragment.Info' => 'Kodu [Fragment]() Ã¼zerindeki Numaralar bÃ¶lÃ¼mÃ¼nden alÄ±n',
             'tweb.PleaseWait' => 'LÃ¼tfen bekleyin...',
             'tweb.StartMessaging' => 'MesajlaÅŸmaya baÅŸla',
             'tweb.YourName' => 'AdÄ±nÄ±z',
             'type_extract_error' => '"%s" tÃ¼rÃ¼ Ã§Ä±karÄ±lamadÄ±, MadelineProto\'yu gÃ¼ncellemelisiniz!',
             'type_extract_error_id' => 'TÃ¼r Ã§Ä±karÄ±lamadÄ±: %s, id %s; MadelineProto\'yu gÃ¼ncellemelisiniz!',
             'update_madelineproto' => 'MadelineProto\'nun eski bir sÃ¼rÃ¼mÃ¼nÃ¼ kullanÄ±yorsunuz, gÃ¼ncelleme gerekli: ÅŸu anda %s sÃ¼rÃ¼mÃ¼ Ã§alÄ±ÅŸÄ±yor, ancak Ã§ok sayÄ±da hata dÃ¼zeltmesi ve yeni Ã¶zellik iÃ§eren en son sÃ¼rÃ¼m %s!',
             'value_bigger_than_2147483647' => 'SaÄŸlanan %s deÄŸeri 2147483647\'den bÃ¼yÃ¼k',
             'value_bigger_than_4294967296' => 'SaÄŸlanan %s deÄŸeri 4294967296\'dan bÃ¼yÃ¼k',
             'value_bigger_than_9223372036854775807' => 'SaÄŸlanan %s deÄŸeri 9223372036854775807\'den bÃ¼yÃ¼k',
             'value_smaller_than_0' => 'SaÄŸlanan %s deÄŸeri 0\'dan kÃ¼Ã§Ã¼k',
             'value_smaller_than_2147483648' => 'SaÄŸlanan %s deÄŸeri -2147483648\'den kÃ¼Ã§Ã¼k',
             'value_smaller_than_9223372036854775808' => 'SaÄŸlanan %s deÄŸeri -9223372036854775808\'den kÃ¼Ã§Ã¼k',
             'waveform_must_have_100_values' => 'Bir waveform dizisi 100 deÄŸer iÃ§ermelidir!',
             'waveform_value' => 'Bir waveform deÄŸeri 0 ile 31 arasÄ±nda olmalÄ±dÄ±r!',
             'webContinue' => 'Devam',
             'webDocs' => 'Belgeler',
             'webDoneLead' => 'MadelineProto sunucuda Ã§alÄ±ÅŸmaya devam ediyor: bu sayfayÄ± kapatabilirsiniz.',
             'webError' => '%s. Kontrol edip tekrar deneyin.',
             'webFieldRequired' => 'Bu alanÄ± doldurun.',
             'webHidePassword' => 'Gizle',
             'webLogin' => 'Oturum aÃ§',
             'webOptional' => 'isteÄŸe baÄŸlÄ±',
             'webPhoneInvalid' => 'Telefon numarasÄ±nÄ± Ã¼lke koduyla birlikte girin, Ã¶rneÄŸin +39 123 456 7890.',
             'webProgress' => 'Kurulum ilerlemesi',
             'webQrTitle' => 'QR kodu ile oturum aÃ§Ä±n',
             'webQrWaiting' => 'TaranmasÄ± bekleniyorâ€¦',
             'webShowPassword' => 'GÃ¶ster',
             'webStepAccount' => 'Hesap',
             'webStepApp' => 'API ID ve hash',
             'webStepDone' => 'Ã‡alÄ±ÅŸÄ±yor',
             'webStepVerify' => 'DoÄŸrulama',
         ],
        'uk' =>
         [
             '2fa_uncalled' => 'Ð¯ Ð½Ðµ Ð¾Ñ‡Ñ–ÐºÑƒÑŽ Ð¿Ð°Ñ€Ð¾Ð»Ñ�! Ð¡Ð¿Ð¾Ñ‡Ð°Ñ‚ÐºÑƒ Ð²Ð¸ÐºÐ»Ð¸Ñ‡Ñ‚Ðµ Ð¼ÐµÑ‚Ð¾Ð´Ð¸ phoneLogin Ñ– completePhoneLogin!',
             'accepting_call' => 'ÐŸÑ€Ð¸Ð¹Ð½Ñ�Ñ‚Ñ‚Ñ� Ð´Ð·Ð²Ñ–Ð½ÐºÐ° Ð²Ñ–Ð´ %s...',
             'account_banned' => '!!!!!!! ÐŸÐžÐŸÐ•Ð Ð•Ð”Ð–Ð•Ð�Ð�Ð¯ !!!!!!!
Ð¡Ð¸Ñ�Ñ‚ÐµÐ¼Ð° Ð·Ð°Ñ…Ð¸Ñ�Ñ‚Ñƒ Ð²Ñ–Ð´ Ñ„Ð»ÑƒÐ´Ñƒ Telegram Ð¿Ñ€Ð¸Ð·ÑƒÐ¿Ð¸Ð½Ð¸Ð»Ð° Ñ†ÐµÐ¹ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¸Ð¹ Ð·Ð°Ð¿Ð¸Ñ�.
Ð©Ð¾Ð± Ð¿Ñ€Ð¾Ð´Ð¾Ð²Ð¶Ð¸Ñ‚Ð¸, Ð¿Ð¾Ñ‚Ñ€Ñ–Ð±Ð½Ð° Ñ€ÑƒÑ‡Ð½Ð° Ð¿ÐµÑ€ÐµÐ²Ñ–Ñ€ÐºÐ°.
Ð�Ð°Ð´Ñ–ÑˆÐ»Ñ–Ñ‚ÑŒ Ð»Ð¸Ñ�Ñ‚ Ð½Ð° recover@telegram.org Ñ–Ð· Ð¿Ñ€Ð¾Ñ…Ð°Ð½Ð½Ñ�Ð¼ Ñ€Ð¾Ð·Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ñ‚Ð¸ Ð½Ð¾Ð¼ÐµÑ€ Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½Ñƒ %s Ñ– ÐºÐ¾Ñ€Ð¾Ñ‚ÐºÐ¾ Ð¾Ð¿Ð¸ÑˆÑ–Ñ‚ÑŒ, Ñ�Ðº Ð²Ð¸ Ð²Ð¸ÐºÐ¾Ñ€Ð¸Ñ�Ñ‚Ð¾Ð²ÑƒÐ²Ð°Ñ‚Ð¸Ð¼ÐµÑ‚Ðµ Ñ†ÐµÐ¹ Ð½Ð¾Ð¼ÐµÑ€.
ÐŸÐ¾Ñ‚Ñ–Ð¼ ÑƒÐ²Ñ–Ð¹Ð´Ñ–Ñ‚ÑŒ Ð·Ð½Ð¾Ð²Ñƒ.
Ð¯ÐºÑ‰Ð¾ Ð²Ð¸ Ð½Ð°Ð²Ð¼Ð¸Ñ�Ð½Ð¾ Ð²Ð¸Ð´Ð°Ð»Ð¸Ð»Ð¸ Ñ†ÐµÐ¹ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¸Ð¹ Ð·Ð°Ð¿Ð¸Ñ�, Ð¿Ñ€Ð¾Ñ–Ð³Ð½Ð¾Ñ€ÑƒÐ¹Ñ‚Ðµ Ñ†Ðµ Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ�.',
             'already_loggedIn' => 'Ð£ Ñ†ÑŒÐ¾Ð¼Ñƒ ÐµÐºÐ·ÐµÐ¼Ð¿Ð»Ñ�Ñ€Ñ– MadelineProto Ð²Ð¶Ðµ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð¾ Ð²Ñ…Ñ–Ð´!',
             'apiAppDescLabelWeb' => 'ÐžÐ¿Ð¸Ñ�',
             'apiAppInstructionsAuto0' => 'Ð’Ð²ÐµÐ´Ñ–Ñ‚ÑŒ Ð½Ð°Ð·Ð²Ñƒ Ð·Ð°Ñ�Ñ‚Ð¾Ñ�ÑƒÐ½ÐºÑƒ, Ð¼Ð¾Ð¶Ðµ Ð±ÑƒÑ‚Ð¸ Ð±ÑƒÐ´ÑŒ-Ñ�ÐºÐ¾ÑŽ: ',
             'apiAppInstructionsAuto1' => 'Ð’Ð²ÐµÐ´Ñ–Ñ‚ÑŒ ÐºÐ¾Ñ€Ð¾Ñ‚ÐºÑƒ Ð½Ð°Ð·Ð²Ñƒ Ð·Ð°Ñ�Ñ‚Ð¾Ñ�ÑƒÐ½ÐºÑƒ, Ð»Ñ–Ñ‚ÐµÑ€Ð¸ Ð¹ Ñ†Ð¸Ñ„Ñ€Ð¸, 5-32 Ñ�Ð¸Ð¼Ð²Ð¾Ð»Ð¸: ',
             'apiAppInstructionsAuto2' => 'Ð’Ð²ÐµÐ´Ñ–Ñ‚ÑŒ URL Ð·Ð°Ñ�Ñ‚Ð¾Ñ�ÑƒÐ½ÐºÑƒ/Ð²ÐµÐ±Ñ�Ð°Ð¹Ñ‚Ñƒ Ð°Ð±Ð¾ t.me/yourusername: ',
             'apiAppInstructionsAuto3' => 'Ð’Ð²ÐµÐ´Ñ–Ñ‚ÑŒ Ð¿Ð»Ð°Ñ‚Ñ„Ð¾Ñ€Ð¼Ñƒ Ð·Ð°Ñ�Ñ‚Ð¾Ñ�ÑƒÐ½ÐºÑƒ: ',
             'apiAppInstructionsAuto4' => 'ÐžÐ¿Ð¸ÑˆÑ–Ñ‚ÑŒ Ñ�Ð²Ñ–Ð¹ Ð·Ð°Ñ�Ñ‚Ð¾Ñ�ÑƒÐ½Ð¾Ðº: ',
             'apiAppInstructionsAutoTypeOther' => 'Ð†Ð½ÑˆÐµ (ÑƒÑ‚Ð¾Ñ‡Ð½Ñ–Ñ‚ÑŒ Ð² Ð¾Ð¿Ð¸Ñ�Ñ–)',
             'apiAppInstructionsManual0' => 'Ð½Ð°Ð·Ð²Ð° Ð²Ð°ÑˆÐ¾Ð³Ð¾ Ð·Ð°Ñ�Ñ‚Ð¾Ñ�ÑƒÐ½ÐºÑƒ, Ð¼Ð¾Ð¶Ðµ Ð±ÑƒÑ‚Ð¸ Ð±ÑƒÐ´ÑŒ-Ñ�ÐºÐ¾ÑŽ',
             'apiAppInstructionsManual1' => 'ÐºÐ¾Ñ€Ð¾Ñ‚ÐºÐ° Ð½Ð°Ð·Ð²Ð° Ð²Ð°ÑˆÐ¾Ð³Ð¾ Ð·Ð°Ñ�Ñ‚Ð¾Ñ�ÑƒÐ½ÐºÑƒ, Ð»Ñ–Ñ‚ÐµÑ€Ð¸ Ð¹ Ñ†Ð¸Ñ„Ñ€Ð¸, 5-32 Ñ�Ð¸Ð¼Ð²Ð¾Ð»Ð¸',
             'apiAppInstructionsManual2' => 'URL Ð²Ð°ÑˆÐ¾Ð³Ð¾ Ð·Ð°Ñ�Ñ‚Ð¾Ñ�ÑƒÐ½ÐºÑƒ/Ð²ÐµÐ±Ñ�Ð°Ð¹Ñ‚Ñƒ Ð°Ð±Ð¾ t.me/yourusername',
             'apiAppInstructionsManual3' => 'Ð±ÑƒÐ´ÑŒ-Ñ‰Ð¾',
             'apiAppInstructionsManual4' => 'ÐžÐ¿Ð¸ÑˆÑ–Ñ‚ÑŒ Ñ‚ÑƒÑ‚ Ñ�Ð²Ñ–Ð¹ Ð·Ð°Ñ�Ñ‚Ð¾Ñ�ÑƒÐ½Ð¾Ðº',
             'apiAppShortNameLabelWeb' => 'ÐšÐ¾Ñ€Ð¾Ñ‚ÐºÐ° Ð½Ð°Ð·Ð²Ð°',
             'apiAppTitleLabelWeb' => 'Ð�Ð°Ð·Ð²Ð° Ð·Ð°Ñ�Ñ‚Ð¾Ñ�ÑƒÐ½ÐºÑƒ',
             'apiAppUrlLabelWeb' => 'URL',
             'apiAppWeb' => 'Ð’Ð²ÐµÐ´Ñ–Ñ‚ÑŒ Ñ–Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–ÑŽ Ð¿Ñ€Ð¾ API',
             'apiAutoCodeLabelWeb' => 'ÐšÐ¾Ð´ Ð²Ñ…Ð¾Ð´Ñƒ',
             'apiAutoCodeLeadWeb' => 'my.telegram.org Ð½Ð°Ð´Ñ–Ñ�Ð»Ð°Ð² ÐºÐ¾Ð´ Ð²Ñ…Ð¾Ð´Ñƒ Ñƒ Ð²Ð°Ñˆ Ð·Ð°Ñ�Ñ‚Ð¾Ñ�ÑƒÐ½Ð¾Ðº Telegram, Ñƒ Ñ‡Ð°Ñ‚ Ñ–Ð· Telegram (Ð½Ðµ SMS).',
             'apiAutoCodeTitleWeb' => 'Ð’Ð²ÐµÐ´Ñ–Ñ‚ÑŒ ÐºÐ¾Ð´ Ñ–Ð· Telegram',
             'apiAutoErrorTitle' => 'my.telegram.org Ð¿Ð¾Ð²ÐµÑ€Ð½ÑƒÐ² Ð¿Ð¾Ð¼Ð¸Ð»ÐºÑƒ',
             'apiAutoFallbackManual' => 'Ð�Ð°Ñ‚Ð¾Ð¼Ñ–Ñ�Ñ‚ÑŒ Ð²Ð²ÐµÐ´Ñ–Ñ‚ÑŒ API ID Ñ‚Ð° API hash Ð²Ñ€ÑƒÑ‡Ð½Ñƒ.',
             'apiAutoPhoneHintWeb' => 'my.telegram.org Ð½Ð°Ð´Ñ�Ð¸Ð»Ð°Ñ” ÐºÐ¾Ð´ Ð²Ñ…Ð¾Ð´Ñƒ Ð² Ð·Ð°Ñ�Ñ‚Ð¾Ñ�ÑƒÐ½Ð¾Ðº Telegram, Ð° Ð½Ðµ SMS.',
             'apiAutoPrompt0' => 'Ð’Ð²ÐµÐ´Ñ–Ñ‚ÑŒ Ð½Ð¾Ð¼ÐµÑ€ Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½Ñƒ, ÑƒÐ¶Ðµ Ð·Ð°Ñ€ÐµÑ”Ñ�Ñ‚Ñ€Ð¾Ð²Ð°Ð½Ð¸Ð¹ Ñƒ Telegram: ',
             'apiAutoPrompt1' => 'Ð’Ð²ÐµÐ´Ñ–Ñ‚ÑŒ ÐºÐ¾Ð´ Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ�, Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð¸Ð¹ Ñƒ Telegram: ',
             'apiAutoWeb' => 'Ð’Ð²ÐµÐ´Ñ–Ñ‚ÑŒ Ð½Ð¾Ð¼ÐµÑ€ Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½Ñƒ, <b>ÑƒÐ¶Ðµ Ð·Ð°Ñ€ÐµÑ”Ñ�Ñ‚Ñ€Ð¾Ð²Ð°Ð½Ð¸Ð¹</b> Ñƒ Telegram, Ñ‰Ð¾Ð± Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ñ‚Ð¸ API ID',
             'apiChooseAutoWeb' => 'ÐžÑ‚Ñ€Ð¸Ð¼Ð°Ñ‚Ð¸ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡Ð½Ð¾',
             'apiChooseManualAuto' => 'ÐžÑ‚Ñ€Ð¸Ð¼Ð°Ñ‚Ð¸ API ID Ñ‚Ð° API hash Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡Ð½Ð¾ Ð· my.telegram.org (a) Ñ‡Ð¸ Ð²Ð²ÐµÑ�Ñ‚Ð¸ Ñ—Ñ… Ð²Ñ€ÑƒÑ‡Ð½Ñƒ (m)?',
             'apiChooseManualAutoTip' => 'Ð—Ð²ÐµÑ€Ð½Ñ–Ñ‚ÑŒ ÑƒÐ²Ð°Ð³Ñƒ, Ñ‰Ð¾ API ID/hash Ñ‚Ð°ÐºÐ¾Ð¶ Ð¼Ð¾Ð¶Ð½Ð° Ð²ÐºÐ°Ð·Ð°Ñ‚Ð¸ Ð±ÐµÐ·Ð¿Ð¾Ñ�ÐµÑ€ÐµÐ´Ð½ÑŒÐ¾ Ð² ÐºÐ¾Ð´Ñ– Ñ‡ÐµÑ€ÐµÐ· Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ�: %s',
             'apiChooseManualAutoTipWeb' => 'Ð—Ð²ÐµÑ€Ð½Ñ–Ñ‚ÑŒ ÑƒÐ²Ð°Ð³Ñƒ, Ñ‰Ð¾ API ID/hash Ñ‚Ð°ÐºÐ¾Ð¶ Ð¼Ð¾Ð¶Ð½Ð° Ð²ÐºÐ°Ð·Ð°Ñ‚Ð¸ Ð±ÐµÐ·Ð¿Ð¾Ñ�ÐµÑ€ÐµÐ´Ð½ÑŒÐ¾ Ð² ÐºÐ¾Ð´Ñ– Ñ‡ÐµÑ€ÐµÐ· <a target="_blank" href="%s">Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ�</a>.',
             'apiChooseManualWeb' => 'Ð£ Ð¼ÐµÐ½Ðµ Ð²Ð¶Ðµ Ñ” API ID',
             'apiChoosePrompt' => 'Ð’Ð°Ñˆ Ð²Ð¸Ð±Ñ–Ñ€ (m/a): ',
             'apiCreateErrorTitle' => 'my.telegram.org Ð½Ðµ Ñ�Ñ‚Ð²Ð¾Ñ€Ð¸Ð² Ð·Ð°Ñ�Ñ‚Ð¾Ñ�ÑƒÐ½Ð¾Ðº',
             'apiCreateLeadWeb' => 'Ð£ Ñ†ÑŒÐ¾Ð³Ð¾ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¾Ð³Ð¾ Ð·Ð°Ð¿Ð¸Ñ�Ñƒ Ñ‰Ðµ Ð½ÐµÐ¼Ð°Ñ” Ð·Ð°Ñ�Ñ‚Ð¾Ñ�ÑƒÐ½ÐºÑƒ Ð½Ð° my.telegram.org. Ð¦Ñ– Ð´Ð°Ð½Ñ– Ð±Ð°Ñ‡Ð¸Ñ‚Ðµ Ð»Ð¸ÑˆÐµ Ð²Ð¸, Ñ– Ñ—Ñ… Ð¼Ð¾Ð¶Ð½Ð° Ð·Ð¼Ñ–Ð½Ð¸Ñ‚Ð¸ Ð¿Ñ–Ð·Ð½Ñ–ÑˆÐµ.',
             'apiCreateTitleWeb' => 'Ð¡Ñ‚Ð²Ð¾Ñ€Ñ–Ñ‚ÑŒ Ð·Ð°Ñ�Ñ‚Ð¾Ñ�ÑƒÐ½Ð¾Ðº',
             'apiCreateVpsWarning' => 'Telegram Ð½Ðµ Ð´Ð¾Ð·Ð²Ð¾Ð»Ñ�Ñ” Ñ�Ñ‚Ð²Ð¾Ñ€ÑŽÐ²Ð°Ñ‚Ð¸ Ð½Ð¾Ð²Ñ– Ð·Ð°Ñ�Ñ‚Ð¾Ñ�ÑƒÐ½ÐºÐ¸ Ð½Ð° my.telegram.org Ð· IP-Ð°Ð´Ñ€ÐµÑ� VPS, VPN Ñ‚Ð° Ñ–Ð½ÑˆÐ¸Ñ… Ð´Ð°Ñ‚Ð°-Ñ†ÐµÐ½Ñ‚Ñ€Ñ–Ð², Ð° Ð·Ð°Ð¿Ð¸Ñ‚ Ð±ÑƒÐ»Ð¾ Ð½Ð°Ð´Ñ–Ñ�Ð»Ð°Ð½Ð¾ Ð· IP Ð¼Ð°ÑˆÐ¸Ð½Ð¸, Ð½Ð° Ñ�ÐºÑ–Ð¹ Ð¿Ñ€Ð°Ñ†ÑŽÑ” MadelineProto. Ð¡Ñ‚Ð²Ð¾Ñ€Ñ–Ñ‚ÑŒ Ð·Ð°Ñ�Ñ‚Ð¾Ñ�ÑƒÐ½Ð¾Ðº Ð²Ñ€ÑƒÑ‡Ð½Ñƒ Ð½Ð° https://my.telegram.org Ð· Ð´Ð¾Ð¼Ð°ÑˆÐ½ÑŒÐ¾Ð³Ð¾ Ð¿Ñ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ� (Ð½Ð°Ð¿Ñ€Ð¸ÐºÐ»Ð°Ð´, Ð· Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½Ð° Ñ‡ÐµÑ€ÐµÐ· Ð¼Ð¾Ð±Ñ–Ð»ÑŒÐ½Ð¸Ð¹ Ñ–Ð½Ñ‚ÐµÑ€Ð½ÐµÑ‚), Ð° Ð¿Ð¾Ñ‚Ñ–Ð¼ Ð²Ð²ÐµÐ´Ñ–Ñ‚ÑŒ API ID Ñ‚Ð° API hash.',
             'apiCreateWeb' => 'Ð¡Ñ‚Ð²Ð¾Ñ€Ð¸Ñ‚Ð¸ Ð·Ð°Ñ�Ñ‚Ð¾Ñ�ÑƒÐ½Ð¾Ðº',
             'apiError' => 'ÐŸÐžÐœÐ˜Ð›ÐšÐ�: %s. Ð¡Ð¿Ñ€Ð¾Ð±ÑƒÐ¹Ñ‚Ðµ Ñ‰Ðµ Ñ€Ð°Ð·.',
             'apiInvalidWeb' => 'API ID â€” Ñ†Ðµ Ñ‡Ð¸Ñ�Ð»Ð¾, Ð° API hash Ñ�ÐºÐ»Ð°Ð´Ð°Ñ”Ñ‚ÑŒÑ�Ñ� Ð· 32 Ñ�Ð¸Ð¼Ð²Ð¾Ð»Ñ–Ð²: Ñ�ÐºÐ¾Ð¿Ñ–ÑŽÐ¹Ñ‚Ðµ Ñ—Ñ… Ñ‰Ðµ Ñ€Ð°Ð· Ð· my.telegram.org.',
             'apiManualInstructions0' => 'Ð£Ð²Ñ–Ð¹Ð´Ñ–Ñ‚ÑŒ Ð½Ð° https://my.telegram.org',
             'apiManualInstructions1' => 'ÐŸÐµÑ€ÐµÐ¹Ð´Ñ–Ñ‚ÑŒ Ð´Ð¾ Ñ€Ð¾Ð·Ð´Ñ–Ð»Ñƒ Â«API development toolsÂ»',
             'apiManualInstructions2' => 'Ð�Ð°Ñ‚Ð¸Ñ�Ð½Ñ–Ñ‚ÑŒ Â«create applicationÂ»',
             'apiManualPrompt0' => 'Ð’Ð²ÐµÐ´Ñ–Ñ‚ÑŒ Ñ�Ð²Ñ–Ð¹ API ID: ',
             'apiManualPrompt1' => 'Ð’Ð²ÐµÐ´Ñ–Ñ‚ÑŒ Ñ�Ð²Ñ–Ð¹ API hash: ',
             'apiManualWeb' => 'Ð’Ð²ÐµÐ´Ñ–Ñ‚ÑŒ Ñ�Ð²Ð¾Ñ— API ID Ñ‚Ð° API hash',
             'apiParamsError' => 'Ð’Ð¸ Ð²ÐºÐ°Ð·Ð°Ð»Ð¸ Ð½Ðµ Ð²Ñ�Ñ– Ð¾Ð±Ð¾Ð²\'Ñ�Ð·ÐºÐ¾Ð²Ñ– Ð¿Ð°Ñ€Ð°Ð¼ÐµÑ‚Ñ€Ð¸!',
             'apiSaveWeb' => 'Ð—Ð±ÐµÑ€ÐµÐ³Ñ‚Ð¸ Ð¹ Ð¿Ñ€Ð¾Ð´Ð¾Ð²Ð¶Ð¸Ñ‚Ð¸',
             'apiSendCodeWeb' => 'Ð�Ð°Ð´Ñ–Ñ�Ð»Ð°Ñ‚Ð¸ ÐºÐ¾Ð´',
             'apiSetupLeadWeb' => 'ÐšÐ¾Ð¶Ð½Ð¾Ð¼Ñƒ Ð·Ð°Ñ�Ñ‚Ð¾Ñ�ÑƒÐ½ÐºÑƒ Telegram Ð¿Ð¾Ñ‚Ñ€Ñ–Ð±Ð½Ñ– API ID Ñ‚Ð° API hash. MadelineProto Ð¼Ð¾Ð¶Ðµ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ñ‚Ð¸ Ñ—Ñ… Ð´Ð»Ñ� Ð²Ð°Ñ� Ð· my.telegram.org Ð·Ð° Ð½Ð¾Ð¼ÐµÑ€Ð¾Ð¼ Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½Ñƒ Ð½Ð°Ñ�Ð²Ð½Ð¾Ð³Ð¾ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¾Ð³Ð¾ Ð·Ð°Ð¿Ð¸Ñ�Ñƒ Telegram.',
             'apiSetupTitleWeb' => 'ÐžÑ‚Ñ€Ð¸Ð¼Ð°Ð¹Ñ‚Ðµ API ID Ñ‚Ð° API hash',
             'apiStartOverWeb' => 'ÐŸÐ¾Ñ‡Ð°Ñ‚Ð¸ Ñ�Ð¿Ð¾Ñ‡Ð°Ñ‚ÐºÑƒ',
             'api_not_set' => 'ÐŸÐ¾Ñ‚Ñ€Ñ–Ð±Ð½Ð¾ Ð²ÐºÐ°Ð·Ð°Ñ‚Ð¸ api key Ñ‚Ð° api id, Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð¹Ñ‚Ðµ Ð²Ð»Ð°Ñ�Ð½Ñ– Ð½Ð° my.telegram.org',
             'array_invalid' => 'Ð’Ð¸ Ð½Ðµ Ð¿ÐµÑ€ÐµÐ´Ð°Ð»Ð¸ Ð´Ñ–Ð¹Ñ�Ð½Ð¸Ð¹ Ð¼Ð°Ñ�Ð¸Ð²',
             'baseDirLimitation' => 'Ð�Ð°Ð»Ð°ÑˆÑ‚Ð¾Ð²Ð°Ð½Ð¾ Ð¾Ð±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ� basedir: Ñ†Ðµ Ð¼Ð¾Ð¶Ðµ Ð²Ð¿Ð»Ð¸Ð½ÑƒÑ‚Ð¸ Ð½Ð° Ð¿Ñ€Ð¾Ð´ÑƒÐºÑ‚Ð¸Ð²Ð½Ñ–Ñ�Ñ‚ÑŒ Ñ– Ñ�Ð¿Ñ€Ð¸Ñ‡Ð¸Ð½Ð¸Ñ‚Ð¸ Ð¿ÐµÐ²Ð½Ñ– Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼Ð¸, Ð²Ð¸Ð¼ÐºÐ½Ñ–Ñ‚ÑŒ Ð¹Ð¾Ð³Ð¾, Ñ�ÐºÑ‰Ð¾ Ð¼Ð¾Ð¶Ð»Ð¸Ð²Ð¾!',
             'bool_error' => 'Ð�Ðµ Ð²Ð´Ð°Ð»Ð¾Ñ�Ñ� Ð²Ð¸Ð´Ð¾Ð±ÑƒÑ‚Ð¸ Ð±ÑƒÐ»ÐµÐ²Ðµ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ�',
             'botAlreadyRunning' => 'Ð‘Ð¾Ñ‚ ÑƒÐ¶Ðµ Ð·Ð°Ð¿ÑƒÑ‰ÐµÐ½Ð¾!',
             'botapi_conversion_error' => 'Ð�Ðµ Ð²Ð´Ð°Ñ”Ñ‚ÑŒÑ�Ñ� Ð¿ÐµÑ€ÐµÑ‚Ð²Ð¾Ñ€Ð¸Ñ‚Ð¸ %s Ð½Ð° Ð¾Ð±\'Ñ”ÐºÑ‚ bot API',
             'call_already_accepted' => 'Ð”Ð·Ð²Ñ–Ð½Ð¾Ðº %s ÑƒÐ¶Ðµ Ð¿Ñ€Ð¸Ð¹Ð½Ñ�Ñ‚Ð¾',
             'call_already_declined' => 'Ð”Ð·Ð²Ñ–Ð½Ð¾Ðº %s ÑƒÐ¶Ðµ Ð²Ñ–Ð´Ñ…Ð¸Ð»ÐµÐ½Ð¾',
             'call_completing' => 'Ð—Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ� Ð²Ñ�Ñ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ� Ð´Ð·Ð²Ñ–Ð½ÐºÐ° Ð²Ñ–Ð´ %s...',
             'call_confirming' => 'ÐŸÑ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ� Ð´Ð·Ð²Ñ–Ð½ÐºÐ° Ð²Ñ–Ð´ %s...',
             'call_discarding' => 'Ð—Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ� Ð´Ð·Ð²Ñ–Ð½ÐºÐ° %s...',
             'call_error_1' => 'Ð�Ðµ Ð²Ð´Ð°Ð»Ð¾Ñ�Ñ� Ð·Ð½Ð°Ð¹Ñ‚Ð¸ Ð¹ Ð¿Ñ€Ð¸Ð¹Ð½Ñ�Ñ‚Ð¸ Ð´Ð·Ð²Ñ–Ð½Ð¾Ðº %s',
             'call_error_2' => 'Ð�Ðµ Ð²Ð´Ð°Ð»Ð¾Ñ�Ñ� Ð·Ð½Ð°Ð¹Ñ‚Ð¸ Ð¹ Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¸Ñ‚Ð¸ Ð´Ð·Ð²Ñ–Ð½Ð¾Ðº %s',
             'call_error_3' => 'Ð�Ðµ Ð²Ð´Ð°Ð»Ð¾Ñ�Ñ� Ð·Ð½Ð°Ð¹Ñ‚Ð¸ Ð´Ð·Ð²Ñ–Ð½Ð¾Ðº %s Ñ– Ð·Ð°Ð²ÐµÑ€ÑˆÐ¸Ñ‚Ð¸ Ð¹Ð¾Ð³Ð¾ Ð²Ñ�Ñ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ�',
             'cli_need_dl.php_link' => 'ÐŸÑ–Ð´ Ñ‡Ð°Ñ� Ð²Ð¸ÐºÐ¾Ñ€Ð¸Ñ�Ñ‚Ð°Ð½Ð½Ñ� getDownloadLink Ñ‡ÐµÑ€ÐµÐ· CLI Ð²ÐºÐ°Ð¶Ñ–Ñ‚ÑŒ URL Ñ�ÐºÑ€Ð¸Ð¿Ñ‚Ñƒ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ�!',
             'constructor_not_found' => 'Ð�Ðµ Ð·Ð½Ð°Ð¹Ð´ÐµÐ½Ð¾ ÐºÐ¾Ð½Ñ�Ñ‚Ñ€ÑƒÐºÑ‚Ð¾Ñ€ Ð´Ð»Ñ� Ñ‚Ð¸Ð¿Ñƒ: ',
             'could_not_connect_to_MadelineProto' => 'Ð�Ðµ Ð²Ð´Ð°Ð»Ð¾Ñ�Ñ� Ð¿Ñ–Ð´ÐºÐ»ÑŽÑ‡Ð¸Ñ‚Ð¸Ñ�Ñ� Ð´Ð¾ MadelineProto: Ñ‰Ð¾Ð± Ð²Ð¸Ð¿Ñ€Ð°Ð²Ð¸Ñ‚Ð¸ Ñ†Ðµ, ÑƒÐ²Ñ–Ð¼ÐºÐ½Ñ–Ñ‚ÑŒ proc_open Ñ– Ð·Ð½Ñ–Ð¼Ñ–Ñ‚ÑŒ Ð¾Ð±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ� open_basedir Ð°Ð±Ð¾ Ð²Ð¸Ð¼ÐºÐ½Ñ–Ñ‚ÑŒ Ð¿ÐµÑ€ÐµÐ·Ð°Ð¿Ð¸Ñ� ÑˆÐ»Ñ�Ñ…Ñ–Ð² Ð²ÐµÐ±Ñ�ÐµÑ€Ð²ÐµÑ€Ð°! Ð¯ÐºÑ‰Ð¾ Ð²Ð¸ Ð²Ð¶Ðµ Ñ†Ðµ Ð·Ñ€Ð¾Ð±Ð¸Ð»Ð¸, Ð¿ÐµÑ€ÐµÐºÐ¾Ð½Ð°Ð¹Ñ‚ÐµÑ�Ñ�, Ñ‰Ð¾ CLI-Ð²ÐµÑ€Ñ�Ñ–Ñ� PHP Ñ‚Ð¾Ñ‡Ð½Ð¾ Ñ‚Ð°ÐºÐ° Ñ�Ð°Ð¼Ð°, Ñ�Ðº Ð²ÐµÐ±Ð²ÐµÑ€Ñ�Ñ–Ñ� (Ñ‚Ð° Ñ�Ð°Ð¼Ð° Ð²ÐµÑ€Ñ�Ñ–Ñ�, Ñ€Ð¾Ð·ÑˆÐ¸Ñ€ÐµÐ½Ð½Ñ� Ñ‚Ð¾Ñ‰Ð¾), Ñ– Ð¿ÐµÑ€ÐµÐ³Ð»Ñ�Ð½ÑŒÑ‚Ðµ Ñ„Ð°Ð¹Ð» MadelineProto.log, Ñ‰Ð¾Ð± Ð´Ñ–Ð·Ð½Ð°Ñ‚Ð¸Ñ�Ñ� Ð±Ñ–Ð»ÑŒÑˆÐµ Ð¿Ñ€Ð¾ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÑƒ, Ñ‡ÐµÑ€ÐµÐ· Ñ�ÐºÑƒ Ð½Ðµ Ð²Ð´Ð°Ð»Ð¾Ñ�Ñ� Ð·Ð°Ð¿ÑƒÑ�Ñ‚Ð¸Ñ‚Ð¸ IPC-Ñ�ÐµÑ€Ð²ÐµÑ€.',
             'could_not_convert_object' => 'Ð�Ðµ Ð²Ð´Ð°Ð»Ð¾Ñ�Ñ� Ð¿ÐµÑ€ÐµÑ‚Ð²Ð¾Ñ€Ð¸Ñ‚Ð¸ Ð¾Ð±\'Ñ”ÐºÑ‚ Ñ‚Ð¸Ð¿Ñƒ %s',
             'deserialization_error' => 'ÐŸÑ–Ð´ Ñ‡Ð°Ñ� Ð´ÐµÑ�ÐµÑ€Ñ–Ð°Ð»Ñ–Ð·Ð°Ñ†Ñ–Ñ— Ñ�Ñ‚Ð°Ð»Ð°Ñ�Ñ� Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ°',
             'dl.php_check_logs_make_sure_session_running' => 'ÐŸÐ¾Ð²\'Ñ�Ð·Ð°Ð½Ð¸Ð¹ Ð±Ð¾Ñ‚ MadelineProto EventHandler Ð°Ð±Ð¾ IPC-Ñ�ÐµÑ€Ð²ÐµÑ€ MadelineProto Ð½Ðµ Ð¿Ñ€Ð°Ñ†ÑŽÑŽÑ‚ÑŒ, Ð¿ÐµÑ€ÐµÐ²Ñ–Ñ€Ñ‚Ðµ Ð¶ÑƒÑ€Ð½Ð°Ð»Ð¸ Ð¹ Ð¿ÐµÑ€ÐµÐºÐ¾Ð½Ð°Ð¹Ñ‚ÐµÑ�Ñ�, Ñ‰Ð¾ Ñ…Ð¾Ñ‡Ð° Ð± Ð¾Ð´Ð¸Ð½ Ñ–Ð· Ð½Ð¸Ñ… Ð·Ð°Ð¿ÑƒÑ‰ÐµÐ½Ð¾!',
             'dl.php_powered_by_madelineproto' => 'Ð¡ÐµÑ€Ð²ÐµÑ€ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ� Ñ„Ð°Ð¹Ð»Ñ–Ð² Telegram (Ð´Ð¾ 4 Ð“Ð‘) Ð½Ð° Ð±Ð°Ð·Ñ– <a href="https://docs.madelineproto.xyz" target="_blank">MadelineProto</a>!<br>Ð�Ð°Ñ‚Ð¸Ñ�Ð½Ñ–Ñ‚ÑŒ <a href="https://docs.madelineproto.xyz/docs/FILES.html#getting-a-download-link" target="_blank">Ñ‚ÑƒÑ‚</a>, Ñ‰Ð¾Ð± Ð´Ñ–Ð·Ð½Ð°Ñ‚Ð¸Ñ�Ñ�, Ñ�Ðº Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ñ‚Ð¸ Ð²Ð»Ð°Ñ�Ð½Ð¸Ð¹ Ñ�ÐµÑ€Ð²ÐµÑ€ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ� Ñ„Ð°Ð¹Ð»Ñ–Ð² Telegram!',
             'do_not_delete_MadelineProto.log' => 'Ñ„Ð°Ð¹Ð» MadelineProto.log Ð½Ñ–ÐºÐ¾Ð»Ð¸ Ð½Ðµ Ð¼Ð¾Ð¶Ð½Ð° Ð²Ð¸Ð´Ð°Ð»Ñ�Ñ‚Ð¸, Ð½Ð°Ñ‚Ð¾Ð¼Ñ–Ñ�Ñ‚ÑŒ Ð·Ð°Ð´Ð°Ð¹Ñ‚Ðµ Ð²Ð»Ð°Ñ�Ð½Ð¸Ð¹ Ð¼Ð°ÐºÑ�Ð¸Ð¼Ð°Ð»ÑŒÐ½Ð¸Ð¹ Ñ€Ð¾Ð·Ð¼Ñ–Ñ€ Ñƒ Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ�Ñ…!',
             'do_not_remove_MadelineProto.log_phar' => 'Ð�Ðµ Ð²Ð¸Ð´Ð°Ð»Ñ�Ð¹Ñ‚Ðµ madeline.phar Ð°Ð±Ð¾ madeline.php, Ñ–Ð½Ð°ÐºÑˆÐµ MadelineProto Ð°Ð²Ð°Ñ€Ñ–Ð¹Ð½Ð¾ Ð·Ð°Ð²ÐµÑ€ÑˆÐ¸Ñ‚ÑŒ Ñ€Ð¾Ð±Ð¾Ñ‚Ñƒ. Ð¯ÐºÑ‰Ð¾ Ñƒ Ð²Ð°Ñ� Ð²Ð¸Ð½Ð¸ÐºÐ»Ð¸ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼Ð¸ Ð· MadelineProto, Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ñ‚Ðµ Ð¿Ñ€Ð¾ Ð½Ð¸Ñ… Ð½Ð° https://github.com/danog/MadelineProto Ð°Ð±Ð¾ https://t.me/pwrtelegramgroup',
             'do_not_use_blocking_class' => 'Ð· Ð¼Ñ–Ñ€ÐºÑƒÐ²Ð°Ð½ÑŒ Ð¿Ñ€Ð¾Ð´ÑƒÐºÑ‚Ð¸Ð²Ð½Ð¾Ñ�Ñ‚Ñ– Ð¾Ð±Ñ€Ð¾Ð±Ð½Ð¸ÐºÐ¸ Ð½Ðµ Ð¼Ð¾Ð¶ÑƒÑ‚ÑŒ Ð²Ð¸ÐºÐ¾Ñ€Ð¸Ñ�Ñ‚Ð¾Ð²ÑƒÐ²Ð°Ñ‚Ð¸ Ð½ÐµÐ°Ñ�Ð¸Ð½Ñ…Ñ€Ð¾Ð½Ð½Ð¸Ð¹ Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð»ÑŒÐ½Ð¸Ð¹ ÐºÐ»Ð°Ñ� %s, Ð½Ð°Ñ‚Ð¾Ð¼Ñ–Ñ�Ñ‚ÑŒ Ð²Ð¸ÐºÐ¾Ñ€Ð¸Ñ�Ñ‚Ð¾Ð²ÑƒÐ¹Ñ‚Ðµ %s',
             'do_not_use_blocking_function' => 'Ð· Ð¼Ñ–Ñ€ÐºÑƒÐ²Ð°Ð½ÑŒ Ð¿Ñ€Ð¾Ð´ÑƒÐºÑ‚Ð¸Ð²Ð½Ð¾Ñ�Ñ‚Ñ– Ð¾Ð±Ñ€Ð¾Ð±Ð½Ð¸ÐºÐ¸ Ð¿Ð¾Ð´Ñ–Ð¹ Ð½Ðµ Ð¼Ð¾Ð¶ÑƒÑ‚ÑŒ Ð²Ð¸ÐºÐ¾Ñ€Ð¸Ñ�Ñ‚Ð¾Ð²ÑƒÐ²Ð°Ñ‚Ð¸ Ð½ÐµÐ°Ñ�Ð¸Ð½Ñ…Ñ€Ð¾Ð½Ð½Ñƒ Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð»ÑŒÐ½Ñƒ Ñ„ÑƒÐ½ÐºÑ†Ñ–ÑŽ %s, Ð½Ð°Ñ‚Ð¾Ð¼Ñ–Ñ�Ñ‚ÑŒ Ð²Ð¸ÐºÐ¾Ñ€Ð¸Ñ�Ñ‚Ð¾Ð²ÑƒÐ¹Ñ‚Ðµ %s',
             'do_not_use_deprecated_function' => 'Ñ„ÑƒÐ½ÐºÑ†Ñ–Ñ� %s Ð·Ð°Ñ�Ñ‚Ð°Ñ€Ñ–Ð»Ð°, Ð½Ð°Ñ‚Ð¾Ð¼Ñ–Ñ�Ñ‚ÑŒ Ð²Ð¸ÐºÐ¾Ñ€Ð¸Ñ�Ñ‚Ð¾Ð²ÑƒÐ¹Ñ‚Ðµ %s',
             'do_not_use_non_root_require_in_event_handler' => 'Ð· Ð¼Ñ–Ñ€ÐºÑƒÐ²Ð°Ð½ÑŒ Ð¿Ñ€Ð¾Ð´ÑƒÐºÑ‚Ð¸Ð²Ð½Ð¾Ñ�Ñ‚Ñ– Ð½Ðµ Ð¼Ð¾Ð¶Ð½Ð° Ð²Ð¸ÐºÐ¾Ñ€Ð¸Ñ�Ñ‚Ð¾Ð²ÑƒÐ²Ð°Ñ‚Ð¸ require Ð°Ð±Ð¾ include Ð²Ñ�ÐµÑ€ÐµÐ´Ð¸Ð½Ñ– ÐºÐ»Ð°Ñ�Ñƒ Ð¾Ð±Ñ€Ð¾Ð±Ð½Ð¸ÐºÐ° Ð¿Ð¾Ð´Ñ–Ð¹, Ð´Ð¾Ð·Ð²Ð¾Ð»ÐµÐ½Ð¾ Ð»Ð¸ÑˆÐµ require Ð½Ð° ÐºÐ¾Ñ€ÐµÐ½ÐµÐ²Ð¾Ð¼Ñƒ Ñ€Ñ–Ð²Ð½Ñ–.',
             'do_not_use_yield' => 'MadelineProto 8 Ð½Ðµ Ð¿Ð¾Ñ‚Ñ€ÐµÐ±ÑƒÑ” Ð¹ Ð½Ðµ Ð¿Ñ–Ð´Ñ‚Ñ€Ð¸Ð¼ÑƒÑ” Ð²Ð¸ÐºÐ¾Ñ€Ð¸Ñ�Ñ‚Ð°Ð½Ð½Ñ� yield Ð² Ð°Ñ�Ð¸Ð½Ñ…Ñ€Ð¾Ð½Ð½Ð¸Ñ… Ñ„ÑƒÐ½ÐºÑ†Ñ–Ñ�Ñ…, Ð²Ð¸Ð»ÑƒÑ‡Ñ–Ñ‚ÑŒ ÑƒÑ�Ñ– ÐºÐ»ÑŽÑ‡Ð¾Ð²Ñ– Ñ�Ð»Ð¾Ð²Ð° yield, Ñ�ÐºÑ– Ñ€Ð°Ð½Ñ–ÑˆÐµ Ð²Ð¸ÐºÐ¾Ñ€Ð¸Ñ�Ñ‚Ð¾Ð²ÑƒÐ²Ð°Ð»Ð¸Ñ�Ñ� Ð´Ð»Ñ� Ð²Ð¸ÐºÐ»Ð¸ÐºÑ–Ð² Ð°Ñ�Ð¸Ð½Ñ…Ñ€Ð¾Ð½Ð½Ð¸Ñ… Ñ„ÑƒÐ½ÐºÑ†Ñ–Ð¹',
             'done' => 'Ð“Ð¾Ñ‚Ð¾Ð²Ð¾!',
             'encode_double_error' => 'Ð�Ðµ Ð²Ð´Ð°Ð»Ð¾Ñ�Ñ� Ð¿Ñ€Ð°Ð²Ð¸Ð»ÑŒÐ½Ð¾ Ð·Ð°ÐºÐ¾Ð´ÑƒÐ²Ð°Ñ‚Ð¸ double',
             'extensionRecommended' => 'ÐŸÐ¾Ð¿ÐµÑ€ÐµÐ´Ð¶ÐµÐ½Ð½Ñ�: Ñ€Ð¾Ð·ÑˆÐ¸Ñ€ÐµÐ½Ð½Ñ� %s Ð½Ðµ Ð²Ñ�Ñ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð¾, ÑƒÑ�Ñ‚Ð°Ð½Ð¾Ð²Ñ–Ñ‚ÑŒ Ð¹Ð¾Ð³Ð¾, Ñ‰Ð¾Ð± Ð¿Ñ€Ð¸ÑˆÐ²Ð¸Ð´ÑˆÐ¸Ñ‚Ð¸ MadelineProto!',
             'extensionRequired' => 'Ð”Ð»Ñ� Ñ€Ð¾Ð±Ð¾Ñ‚Ð¸ MadelineProto Ð¿Ð¾Ñ‚Ñ€Ñ–Ð±Ð½Ðµ Ñ€Ð¾Ð·ÑˆÐ¸Ñ€ÐµÐ½Ð½Ñ� %s. %s',
             'extensionRequiredInstallWithApt' => 'Ð¡Ð¿Ñ€Ð¾Ð±ÑƒÐ¹Ñ‚Ðµ Ð²Ð¸ÐºÐ¾Ð½Ð°Ñ‚Ð¸ sudo apt-get install %s.',
             'extensionRequiredInstallWithCustomInstructions' => 'Ð©Ð¾Ð± ÑƒÑ�Ñ‚Ð°Ð½Ð¾Ð²Ð¸Ñ‚Ð¸ Ð¹Ð¾Ð³Ð¾, Ð´Ð¾Ñ‚Ñ€Ð¸Ð¼ÑƒÐ¹Ñ‚ÐµÑ�Ñ� Ñ–Ð½Ñ�Ñ‚Ñ€ÑƒÐºÑ†Ñ–Ð¹ Ð·Ð° Ð°Ð´Ñ€ÐµÑ�Ð¾ÑŽ %s.',
             'file_not_exist' => 'Ð¤Ð°Ð¹Ð» Ð½Ðµ Ñ–Ñ�Ð½ÑƒÑ”',
             'file_parsing' => 'Ð Ð¾Ð·Ð±Ñ–Ñ€ %s...',
             'file_type_invalid' => 'Ð’Ð¸Ñ�Ð²Ð»ÐµÐ½Ð¾ Ð½ÐµÐ´Ñ–Ð¹Ñ�Ð½Ð¸Ð¹ Ñ‚Ð¸Ð¿ Ñ„Ð°Ð¹Ð»Ñƒ (%s)',
             'fingerprint_invalid' => 'Ð�ÐµÐ´Ñ–Ð¹Ñ�Ð½Ð¸Ð¹ Ð²Ñ–Ð´Ð±Ð¸Ñ‚Ð¾Ðº ÐºÐ»ÑŽÑ‡Ð°!',
             'go' => 'Ð”Ð°Ð»Ñ–',
             'invalid_dl.php_session' => '%s Ð½Ðµ Ñ” Ð´Ñ–Ð¹Ñ�Ð½Ð¸Ð¼ Ñ�ÐºÑ€Ð¸Ð¿Ñ‚Ð¾Ð¼ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ�, Ð¾Ñ�ÐºÑ–Ð»ÑŒÐºÐ¸ Ð¹Ð¾Ð³Ð¾ ID Ñ�ÐµÑ�Ñ–Ñ— Ð²Ñ–Ð´Ñ€Ñ–Ð·Ð½Ñ�Ñ”Ñ‚ÑŒÑ�Ñ� (Ð¾Ñ‡Ñ–ÐºÑƒÐ²Ð°Ð²Ñ�Ñ� %s, Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð¾ %s)',
             'ipc_tcp_warning' => 'UNIX sockets are not available, so the IPC server of the session listens on TCP (127.0.0.1) instead: any program running on this machine can connect to it, and control the session. On Windows 10 1803+, enable the sockets extension (extension=sockets in php.ini) to use UNIX sockets.',
             'length_too_big' => 'Ð—Ð°Ð²ÐµÐ»Ð¸ÐºÐ° Ð´Ð¾Ð²Ð¶Ð¸Ð½Ð°',
             'loginBot' => 'Ð’Ð²ÐµÐ´Ñ–Ñ‚ÑŒ Ñ‚Ð¾ÐºÐµÐ½ Ð±Ð¾Ñ‚Ð°: ',
             'loginBotTokenHintWeb' => 'Ð¢Ð¾ÐºÐµÐ½, Ñ�ÐºÐ¸Ð¹ Ð²Ð¸Ð´Ð°Ð² @BotFather Ð¿Ñ–Ð´ Ñ‡Ð°Ñ� Ñ�Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ� Ð±Ð¾Ñ‚Ð°.',
             'loginBotTokenWeb' => 'Ð¢Ð¾ÐºÐµÐ½ Ð±Ð¾Ñ‚Ð°',
             'loginChoosePromptWeb' => 'Ð£Ð²Ñ–Ð¹Ñ‚Ð¸ Ñ�Ðº ÐºÐ¾Ñ€Ð¸Ñ�Ñ‚ÑƒÐ²Ð°Ñ‡ Ñ‡Ð¸ Ñ�Ðº Ð±Ð¾Ñ‚?',
             'loginCodeLeadWeb' => 'Telegram Ð½Ð°Ð´Ñ–Ñ�Ð»Ð°Ð² ÐºÐ¾Ð´ Ð²Ñ…Ð¾Ð´Ñƒ Ñƒ Ð²Ð°Ñˆ Ð·Ð°Ñ�Ñ‚Ð¾Ñ�ÑƒÐ½Ð¾Ðº Telegram Ð°Ð±Ð¾ SMS, Ñ�ÐºÑ‰Ð¾ Ð²Ð¸ Ð½Ñ–Ð´Ðµ Ð±Ñ–Ð»ÑŒÑˆÐµ Ð½Ðµ Ð²Ð²Ñ–Ð¹ÑˆÐ»Ð¸.',
             'loginCodeTitleWeb' => 'Ð’Ð²ÐµÐ´Ñ–Ñ‚ÑŒ ÐºÐ¾Ð´ Ð²Ñ…Ð¾Ð´Ñƒ',
             'loginLeadWeb' => 'Ð’Ð¸Ð±ÐµÑ€Ñ–Ñ‚ÑŒ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¸Ð¹ Ð·Ð°Ð¿Ð¸Ñ� Ð°Ð±Ð¾ Ð±Ð¾Ñ‚Ð°, Ð²Ñ–Ð´ Ñ–Ð¼ÐµÐ½Ñ– Ñ�ÐºÐ¾Ð³Ð¾ Ð¿Ñ€Ð°Ñ†ÑŽÐ²Ð°Ñ‚Ð¸Ð¼Ðµ Ñ†ÐµÐ¹ ÐµÐºÐ·ÐµÐ¼Ð¿Ð»Ñ�Ñ€ MadelineProto.',
             'loginManual' => 'Ð�Ð±Ð¾ Ð¶ Ð²Ð²ÐµÐ´Ñ–Ñ‚ÑŒ Ñ‚Ð¾ÐºÐµÐ½ Ð±Ð¾Ñ‚Ð° Ñ‡Ð¸ Ð½Ð¾Ð¼ÐµÑ€ Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½Ñƒ, Ñ‰Ð¾Ð± ÑƒÐ²Ñ–Ð¹Ñ‚Ð¸ Ð²Ñ€ÑƒÑ‡Ð½Ñƒ: ',
             'loginNoCode' => 'Ð’Ð¸ Ð½Ðµ Ð²ÐºÐ°Ð·Ð°Ð»Ð¸ ÐºÐ¾Ð´ Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ�!',
             'loginNoName' => 'Ð’Ð¸ Ð½Ðµ Ð²ÐºÐ°Ð·Ð°Ð»Ð¸ Ñ–Ð¼\'Ñ�!',
             'loginNoPass' => 'Ð’Ð¸ Ð½Ðµ Ð²ÐºÐ°Ð·Ð°Ð»Ð¸ Ð¿Ð°Ñ€Ð¾Ð»ÑŒ!',
             'loginOptionBot' => 'Ð‘Ð¾Ñ‚',
             'loginOptionUser' => 'ÐšÐ¾Ñ€Ð¸Ñ�Ñ‚ÑƒÐ²Ð°Ñ‡',
             'loginPasswordLabelWeb' => 'ÐŸÐ°Ñ€Ð¾Ð»ÑŒ',
             'loginPasswordLeadWeb' => 'Ð¦ÐµÐ¹ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¸Ð¹ Ð·Ð°Ð¿Ð¸Ñ� Ð·Ð°Ñ…Ð¸Ñ‰ÐµÐ½Ð¾ Ð¿Ð°Ñ€Ð¾Ð»ÐµÐ¼, Ð·Ð°Ð´Ð°Ð½Ð¸Ð¼ Ñƒ Ñ€Ð¾Ð·Ð´Ñ–Ð»Ñ– Ð�Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ� > ÐŸÑ€Ð¸Ð²Ð°Ñ‚Ð½Ñ–Ñ�Ñ‚ÑŒ Ñ– Ð±ÐµÐ·Ð¿ÐµÐºÐ° > Ð”Ð²Ð¾ÐµÑ‚Ð°Ð¿Ð½Ð° Ð¿ÐµÑ€ÐµÐ²Ñ–Ñ€ÐºÐ°.',
             'loginPasswordTitleWeb' => 'Ð”Ð²Ð¾ÐµÑ‚Ð°Ð¿Ð½Ð° Ð¿ÐµÑ€ÐµÐ²Ñ–Ñ€ÐºÐ°',
             'loginQr' => 'Ð’Ñ–Ð´ÐºÑ€Ð¸Ð¹Ñ‚Ðµ Telegram Ð½Ð° Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½Ñ–, Ð¿ÐµÑ€ÐµÐ¹Ð´Ñ–Ñ‚ÑŒ Ñƒ Ð�Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ� > ÐŸÑ€Ð¸Ñ�Ñ‚Ñ€Ð¾Ñ— > ÐŸÑ–Ð´ÐºÐ»ÑŽÑ‡Ð¸Ñ‚Ð¸ Ð¿Ñ€Ð¸Ñ�Ñ‚Ñ€Ñ–Ð¹ Ñ– Ð²Ñ–Ð´Ñ�ÐºÐ°Ð½ÑƒÐ¹Ñ‚Ðµ QR-ÐºÐ¾Ð´ Ð²Ð¸Ñ‰Ðµ, Ñ‰Ð¾Ð± ÑƒÐ²Ñ–Ð¹Ñ‚Ð¸ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡Ð½Ð¾.',
             'loginQrCodeExpired' => 'Ð¢ÐµÑ€Ð¼Ñ–Ð½ Ð´Ñ–Ñ— QR-ÐºÐ¾Ð´Ñƒ Ð¼Ð¸Ð½ÑƒÐ², Ð³ÐµÐ½ÐµÑ€ÑƒÑ”Ñ‚ÑŒÑ�Ñ� Ð½Ð¾Ð²Ð¸Ð¹...',
             'loginQrCodeSuccessful' => 'Ð’Ñ…Ñ–Ð´ Ð·Ð° QR-ÐºÐ¾Ð´Ð¾Ð¼ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð¾ ÑƒÑ�Ð¿Ñ–ÑˆÐ½Ð¾!',
             'loginTitleWeb' => 'Ð’Ñ…Ñ–Ð´ Ñƒ Telegram',
             'loginTypeWeb' => 'Ð£Ð²Ñ–Ð¹Ñ‚Ð¸ Ñ�Ðº',
             'loginUser' => 'Ð’Ð²ÐµÐ´Ñ–Ñ‚ÑŒ Ñ�Ð²Ñ–Ð¹ Ð½Ð¾Ð¼ÐµÑ€ Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½Ñƒ: ',
             'loginUserCode' => 'Ð’Ð²ÐµÐ´Ñ–Ñ‚ÑŒ ÐºÐ¾Ð´: ',
             'loginUserPass' => 'Ð’Ð²ÐµÐ´Ñ–Ñ‚ÑŒ Ñ�Ð²Ñ–Ð¹ Ð¿Ð°Ñ€Ð¾Ð»ÑŒ (Ð¿Ñ–Ð´ÐºÐ°Ð·ÐºÐ°: %s): ',
             'loginUserPassHint' => 'ÐŸÑ–Ð´ÐºÐ°Ð·ÐºÐ°: %s',
             'loginUserPassWeb' => 'Ð’Ð²ÐµÐ´Ñ–Ñ‚ÑŒ Ñ�Ð²Ñ–Ð¹ Ð¿Ð°Ñ€Ð¾Ð»ÑŒ: ',
             'loginUserPhoneCodeWeb' => 'ÐšÐ¾Ð´',
             'loginUserPhoneHintWeb' => 'Ð— ÐºÐ¾Ð´Ð¾Ð¼ ÐºÑ€Ð°Ñ—Ð½Ð¸, Ð½Ð°Ð¿Ñ€Ð¸ÐºÐ»Ð°Ð´ +39 123 456 7890.',
             'loginUserPhoneWeb' => 'Ð�Ð¾Ð¼ÐµÑ€ Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½Ñƒ',
             'loginWebQr' => 'Ð¢Ð°ÐºÐ¾Ð¶ Ð¼Ð¾Ð¶Ð½Ð° ÑƒÐ²Ñ–Ð¹Ñ‚Ð¸ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡Ð½Ð¾: Ð²Ñ–Ð´ÐºÑ€Ð¸Ð¹Ñ‚Ðµ Telegram Ð½Ð° Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½Ñ–, Ð¿ÐµÑ€ÐµÐ¹Ð´Ñ–Ñ‚ÑŒ Ñƒ Ð�Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ� > ÐŸÑ€Ð¸Ñ�Ñ‚Ñ€Ð¾Ñ— > ÐŸÑ–Ð´ÐºÐ»ÑŽÑ‡Ð¸Ñ‚Ð¸ Ð¿Ñ€Ð¸Ñ�Ñ‚Ñ€Ñ–Ð¹ Ñ– Ð²Ñ–Ð´Ñ�ÐºÐ°Ð½ÑƒÐ¹Ñ‚Ðµ Ñ†ÐµÐ¹ QR-ÐºÐ¾Ð´:',
             'loginWebQr1' => 'Ð’Ñ–Ð´ÐºÑ€Ð¸Ð¹Ñ‚Ðµ Telegram Ð½Ð° Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½Ñ–',
             'loginWebQr2' => 'ÐŸÐµÑ€ÐµÐ¹Ð´Ñ–Ñ‚ÑŒ Ñƒ Ð�Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ� > ÐŸÑ€Ð¸Ñ�Ñ‚Ñ€Ð¾Ñ— > ÐŸÑ–Ð´ÐºÐ»ÑŽÑ‡Ð¸Ñ‚Ð¸ Ð¿Ñ€Ð¸Ñ�Ñ‚Ñ€Ñ–Ð¹',
             'loginWebQr3' => 'Ð�Ð°Ð²ÐµÐ´Ñ–Ñ‚ÑŒ Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½ Ð½Ð° Ñ†ÐµÐ¹ ÐµÐºÑ€Ð°Ð½, Ñ‰Ð¾Ð± Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¸Ñ‚Ð¸ Ð²Ñ…Ñ–Ð´',
             'login_2fa_enabled' => 'Ð£Ð²Ñ–Ð¼ÐºÐ½ÐµÐ½Ð¾ 2FA, Ð¿Ð¾Ñ‚Ñ€Ñ–Ð±Ð½Ð¾ Ð±ÑƒÐ´Ðµ Ð²Ð¸ÐºÐ»Ð¸ÐºÐ°Ñ‚Ð¸ Ñ„ÑƒÐ½ÐºÑ†Ñ–ÑŽ complete2falogin...',
             'login_auth_key' => 'Ð’Ñ…Ñ–Ð´ Ð·Ð° Ð´Ð¾Ð¿Ð¾Ð¼Ð¾Ð³Ð¾ÑŽ ÐºÐ»ÑŽÑ‡Ð° Ð°Ð²Ñ‚Ð¾Ñ€Ð¸Ð·Ð°Ñ†Ñ–Ñ—...',
             'login_bot' => 'Ð’Ñ…Ñ–Ð´ Ñ�Ðº Ð±Ð¾Ñ‚...',
             'login_code_sending' => 'Ð�Ð°Ð´Ñ�Ð¸Ð»Ð°Ð½Ð½Ñ� ÐºÐ¾Ð´Ñƒ...',
             'login_code_sent' => 'ÐšÐ¾Ð´ ÑƒÑ�Ð¿Ñ–ÑˆÐ½Ð¾ Ð½Ð°Ð´Ñ–Ñ�Ð»Ð°Ð½Ð¾! ÐžÑ‚Ñ€Ð¸Ð¼Ð°Ð²ÑˆÐ¸ ÐºÐ¾Ð´, Ñ�ÐºÐ¾Ñ€Ð¸Ñ�Ñ‚Ð°Ð¹Ñ‚ÐµÑ�Ñ� Ñ„ÑƒÐ½ÐºÑ†Ñ–Ñ”ÑŽ completePhoneLogin.',
             'login_code_uncalled' => 'Ð¯ Ð½Ðµ Ð¾Ñ‡Ñ–ÐºÑƒÑŽ ÐºÐ¾Ð´Ñƒ! Ð¡Ð¿Ð¾Ñ‡Ð°Ñ‚ÐºÑƒ Ð²Ð¸ÐºÐ»Ð¸Ñ‡Ñ‚Ðµ Ð¼ÐµÑ‚Ð¾Ð´ phoneLogin',
             'login_need_signup' => 'Ð”Ð»Ñ� Ñ†ÑŒÐ¾Ð³Ð¾ Ð½Ð¾Ð¼ÐµÑ€Ð° Ð½Ðµ Ñ�Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð¾ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¸Ð¹ Ð·Ð°Ð¿Ð¸Ñ�, Ð¿Ð¾Ñ‚Ñ€Ñ–Ð±Ð½Ð¾ Ð±ÑƒÐ´Ðµ Ð²Ð¸ÐºÐ»Ð¸ÐºÐ°Ñ‚Ð¸ Ñ„ÑƒÐ½ÐºÑ†Ñ–ÑŽ completeSignup...',
             'login_ok' => 'Ð’Ñ…Ñ–Ð´ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð¾ ÑƒÑ�Ð¿Ñ–ÑˆÐ½Ð¾!',
             'login_user' => 'Ð’Ñ…Ñ–Ð´ Ñ�Ðº Ð·Ð²Ð¸Ñ‡Ð°Ð¹Ð½Ð¸Ð¹ ÐºÐ¾Ñ€Ð¸Ñ�Ñ‚ÑƒÐ²Ð°Ñ‡...',
             'logout_ok' => 'Ð’Ð¸Ñ…Ñ–Ð´ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð¾ ÑƒÑ�Ð¿Ñ–ÑˆÐ½Ð¾!',
             'long_not_16' => 'Ð”Ð¾Ð²Ð¶Ð¸Ð½Ð° Ð¿ÐµÑ€ÐµÐ´Ð°Ð½Ð¾Ð³Ð¾ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ� Ð½Ðµ Ð´Ð¾Ñ€Ñ–Ð²Ð½ÑŽÑ” 16 Ð±Ð°Ð¹Ñ‚Ð°Ð¼',
             'long_not_32' => 'Ð”Ð¾Ð²Ð¶Ð¸Ð½Ð° Ð¿ÐµÑ€ÐµÐ´Ð°Ð½Ð¾Ð³Ð¾ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ� Ð½Ðµ Ð´Ð¾Ñ€Ñ–Ð²Ð½ÑŽÑ” 32 Ð±Ð°Ð¹Ñ‚Ð°Ð¼',
             'long_not_64' => 'Ð”Ð¾Ð²Ð¶Ð¸Ð½Ð° Ð¿ÐµÑ€ÐµÐ´Ð°Ð½Ð¾Ð³Ð¾ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ� Ð½Ðµ Ð´Ð¾Ñ€Ñ–Ð²Ð½ÑŽÑ” 64 Ð±Ð°Ð¹Ñ‚Ð°Ð¼',
             'madelineproto_ready' => 'MadelineProto Ð³Ð¾Ñ‚Ð¾Ð²Ð¸Ð¹ Ð´Ð¾ Ñ€Ð¾Ð±Ð¾Ñ‚Ð¸!',
             'manualAdminActionRequired' => '!!!!!!!!! ÐŸÐžÐ¢Ð Ð†Ð‘Ð�Ð• Ð’Ð¢Ð Ð£Ð§Ð�Ð�Ð�Ð¯ Ð¡Ð˜Ð¡Ð¢Ð•ÐœÐ�ÐžÐ“Ðž Ð�Ð”ÐœÐ†Ð�Ð†Ð¡Ð¢Ð Ð�Ð¢ÐžÐ Ð� !!!!!!!!!',
             'method_not_found' => 'Ð�Ðµ Ð²Ð´Ð°Ð»Ð¾Ñ�Ñ� Ð·Ð½Ð°Ð¹Ñ‚Ð¸ Ð¼ÐµÑ‚Ð¾Ð´: ',
             'mmapErrorPart1' => 'Ð”Ð¾Ñ�Ñ�Ð³Ð½ÑƒÑ‚Ð¾ Ð¼Ð°ÐºÑ�Ð¸Ð¼Ð°Ð»ÑŒÐ½Ð¾Ñ— ÐºÑ–Ð»ÑŒÐºÐ¾Ñ�Ñ‚Ñ– Ð²Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ… Ñƒ Ð¿Ð°Ð¼\'Ñ�Ñ‚ÑŒ Ð¾Ð±Ð»Ð°Ñ�Ñ‚ÐµÐ¹ (mmap) (%s): Ñ‰Ð¾Ð± Ð²Ð¸Ð¿Ñ€Ð°Ð²Ð¸Ñ‚Ð¸ Ñ†Ðµ, Ð·Ð±Ñ–Ð»ÑŒÑˆÑ‚Ðµ Ð¿Ð°Ñ€Ð°Ð¼ÐµÑ‚Ñ€ Ñ�Ð´Ñ€Ð° vm.max_map_count Ð´Ð¾ 262144.',
             'mmapErrorPart2' => 'Ð©Ð¾Ð± Ð²Ð¸Ð¿Ñ€Ð°Ð²Ð¸Ñ‚Ð¸, Ð²Ð¸ÐºÐ¾Ð½Ð°Ð¹Ñ‚Ðµ Ð²Ñ–Ð´ Ñ–Ð¼ÐµÐ½Ñ– root Ñ‚Ð°ÐºÑƒ ÐºÐ¾Ð¼Ð°Ð½Ð´Ñƒ: %s',
             'mmapErrorPart3' => 'Ð©Ð¾Ð± Ð·Ð¼Ñ–Ð½Ð° Ð·Ð±ÐµÑ€Ñ–Ð³Ð°Ð»Ð°Ñ�Ñ� Ð¿Ñ–Ñ�Ð»Ñ� Ð¿ÐµÑ€ÐµÐ·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ�: %s',
             'mmapErrorPart4' => 'Ð£ Windows Ñ– WSL Ð¼Ð¾Ð¶Ðµ Ð´Ð¾Ð¿Ð¾Ð¼Ð¾Ð³Ñ‚Ð¸ Ð·Ð±Ñ–Ð»ÑŒÑˆÐµÐ½Ð½Ñ� Ñ„Ð°Ð¹Ð»Ñƒ Ð¿Ñ–Ð´ÐºÐ°Ñ‡ÐºÐ¸; Ñ�ÐºÑ‰Ð¾ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼Ð° Ð½Ðµ Ð·Ð½Ð¸ÐºÐ½Ðµ, Ð¿ÐµÑ€ÐµÐ¹Ð´Ñ–Ñ‚ÑŒ Ð½Ð° Ð½Ð°Ñ‚Ð¸Ð²Ð½Ð¸Ð¹ Linux.',
             'must_have_declare_types' => 'Ð· Ð¼Ñ–Ñ€ÐºÑƒÐ²Ð°Ð½ÑŒ Ð¿Ñ€Ð¾Ð´ÑƒÐºÑ‚Ð¸Ð²Ð½Ð¾Ñ�Ñ‚Ñ– Ð¿ÐµÑ€ÑˆÐ¾ÑŽ Ñ–Ð½Ñ�Ñ‚Ñ€ÑƒÐºÑ†Ñ–Ñ”ÑŽ Ñ„Ð°Ð¹Ð»Ñƒ Ð¾Ð±Ñ€Ð¾Ð±Ð½Ð¸ÐºÐ° Ð¿Ð¾Ð´Ñ–Ð¹ Ð¼Ð°Ñ” Ð±ÑƒÑ‚Ð¸ "declare(strict_types=1);"',
             'nearest_dc' => 'Ð�Ð°ÑˆÐµ Ñ€Ð¾Ð·Ñ‚Ð°ÑˆÑƒÐ²Ð°Ð½Ð½Ñ�: %s, Ð½Ð°Ð¹Ð±Ð»Ð¸Ð¶Ñ‡Ð¸Ð¹ DC: %d.',
             'need_dl.php' => 'Ð�Ðµ Ð²Ð´Ð°Ð»Ð¾Ñ�Ñ� Ð·Ð³ÐµÐ½ÐµÑ€ÑƒÐ²Ð°Ñ‚Ð¸ Ñ�Ñ‚Ð°Ð½Ð´Ð°Ñ€Ñ‚Ð½Ð¸Ð¹ Ñ�ÐºÑ€Ð¸Ð¿Ñ‚ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ� (%s), Ñ�Ñ‚Ð²Ð¾Ñ€Ñ–Ñ‚ÑŒ Ñ„Ð°Ð¹Ð» dl.php Ð· Ñ‚Ð°ÐºÐ¸Ð¼ Ð²Ð¼Ñ–Ñ�Ñ‚Ð¾Ð¼: %s Ñ– Ð¿ÐµÑ€ÐµÐ´Ð°Ð¹Ñ‚Ðµ Ð¹Ð¾Ð³Ð¾ URL Ð´Ñ€ÑƒÐ³Ð¸Ð¼ Ð¿Ð°Ñ€Ð°Ð¼ÐµÑ‚Ñ€Ð¾Ð¼ getDownloadLink',
             'noReportPeers' => 'ÐŸÐ¾Ð¿ÐµÑ€ÐµÐ´Ð¶ÐµÐ½Ð½Ñ�: Ð½Ðµ Ð·Ð°Ð´Ð°Ð½Ð¾ Ð¾Ð´ÐµÑ€Ð¶ÑƒÐ²Ð°Ñ‡Ñ–Ð² Ð·Ð²Ñ–Ñ‚Ñ–Ð² (report peers), Ð´Ð¾Ð´Ð°Ð¹Ñ‚Ðµ Ð´Ð¾ Ñ�Ð²Ð¾Ð³Ð¾ Ð¾Ð±Ñ€Ð¾Ð±Ð½Ð¸ÐºÐ° Ð¿Ð¾Ð´Ñ–Ð¹ Ñ‚Ð°ÐºÐ¸Ð¹ Ð¼ÐµÑ‚Ð¾Ð´',
             'non_text_conversion' => 'ÐŸÐµÑ€ÐµÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ� Ð½ÐµÑ‚ÐµÐºÑ�Ñ‚Ð¾Ð²Ð¸Ñ… Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½ÑŒ Ð¿Ð¾ÐºÐ¸ Ð½Ðµ Ð¿Ñ–Ð´Ñ‚Ñ€Ð¸Ð¼ÑƒÑ”Ñ‚ÑŒÑ�Ñ�!',
             'not_loggedIn' => 'Ð’Ñ…Ñ–Ð´ Ð½Ðµ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð¾!',
             'not_numeric' => 'ÐŸÐµÑ€ÐµÐ´Ð°Ð½Ðµ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ� Ð½Ðµ Ñ” Ñ‡Ð¸Ñ�Ð»Ð¾Ð²Ð¸Ð¼',
             'params_missing' => 'Ð’Ñ–Ð´Ñ�ÑƒÑ‚Ð½Ñ–Ð¹ Ð¾Ð±Ð¾Ð²\'Ñ�Ð·ÐºÐ¾Ð²Ð¸Ð¹ Ð¿Ð°Ñ€Ð°Ð¼ÐµÑ‚Ñ€',
             'peer_not_in_db' => 'Ð¦ÑŒÐ¾Ð³Ð¾ Ñ�Ð¿Ñ–Ð²Ñ€Ð¾Ð·Ð¼Ð¾Ð²Ð½Ð¸ÐºÐ° (peer) Ð½ÐµÐ¼Ð°Ñ” Ñƒ Ð²Ð½ÑƒÑ‚Ñ€Ñ–ÑˆÐ½Ñ–Ð¹ Ð±Ð°Ð·Ñ– Ð´Ð°Ð½Ð¸Ñ… Ñ�Ð¿Ñ–Ð²Ñ€Ð¾Ð·Ð¼Ð¾Ð²Ð½Ð¸ÐºÑ–Ð²',
             'plugin_path_does_not_exist' => 'Ð¨Ð»Ñ�Ñ… Ð´Ð¾ Ð¿Ð»Ð°Ð³Ñ–Ð½Ð° %s Ð½Ðµ Ñ–Ñ�Ð½ÑƒÑ”!',
             'plugins_do_not_use_require' => 'Ð· Ð¼Ñ–Ñ€ÐºÑƒÐ²Ð°Ð½ÑŒ Ð¿Ñ€Ð¾Ð´ÑƒÐºÑ‚Ð¸Ð²Ð½Ð¾Ñ�Ñ‚Ñ– Ð¿Ð»Ð°Ð³Ñ–Ð½Ð¸ Ð¼Ð¾Ð¶ÑƒÑ‚ÑŒ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡Ð½Ð¾ Ð¿Ñ–Ð´ÐºÐ»ÑŽÑ‡Ð°Ñ‚Ð¸ (include Ð°Ð±Ð¾ require) Ñ–Ð½ÑˆÑ– Ñ„Ð°Ð¹Ð»Ð¸ Ð· Ñ‚ÐµÐºÐ¸ Ð¿Ð»Ð°Ð³Ñ–Ð½Ñ–Ð² Ð»Ð¸ÑˆÐµ Ñ‡ÐµÑ€ÐµÐ· Ð°Ð²Ñ‚Ð¾Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÑƒÐ²Ð°Ñ‡ PSR-4 (Ð° Ð½Ðµ Ð²Ñ€ÑƒÑ‡Ð½Ñƒ Ñ‡ÐµÑ€ÐµÐ· require()).',
             'plugins_must_have_exactly_one_class' => 'Ð¿Ð»Ð°Ð³Ñ–Ð½ Ð¼Ð°Ñ” Ð²Ð¸Ð·Ð½Ð°Ñ‡Ð°Ñ‚Ð¸ Ñ€Ñ–Ð²Ð½Ð¾ Ð¾Ð´Ð¸Ð½ ÐºÐ»Ð°Ñ�! Ð©Ð¾Ð± Ð²Ð¸Ð·Ð½Ð°Ñ‡Ð¸Ñ‚Ð¸ ÐºÑ–Ð»ÑŒÐºÐ° ÐºÐ»Ð°Ñ�Ñ–Ð², Ñ–Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ�Ñ–Ð² Ð°Ð±Ð¾ Ñ‚Ñ€ÐµÐ¹Ñ‚Ñ–Ð², Ñ�Ñ‚Ð²Ð¾Ñ€Ñ–Ñ‚ÑŒ Ð¾ÐºÑ€ÐµÐ¼Ñ– Ñ„Ð°Ð¹Ð»Ð¸, MadelineProto Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶Ð¸Ñ‚ÑŒ Ñ—Ñ… Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡Ð½Ð¾.',
             'predicate_not_set' => 'ÐŸÑ€ÐµÐ´Ð¸ÐºÐ°Ñ‚ (Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ� Ð² _) Ð½Ðµ Ð·Ð°Ð´Ð°Ð½Ð¾!',
             'recommend_not_use_filesystem_function' => 'Ð²Ð¸ÐºÐ¾Ñ€Ð¸Ñ�Ñ‚Ð¾Ð²ÑƒÐ²Ð°Ñ‚Ð¸ Ñ„ÑƒÐ½ÐºÑ†Ñ–ÑŽ %s Ð½Ðµ Ñ€ÐµÐºÐ¾Ð¼ÐµÐ½Ð´ÑƒÑ”Ñ‚ÑŒÑ�Ñ�, Ð¾Ñ�ÐºÑ–Ð»ÑŒÐºÐ¸ Ð´Ð¾Ñ�Ñ‚ÑƒÐ¿ Ð´Ð¾ Ñ„Ð°Ð¹Ð»Ð¾Ð²Ð¾Ñ— Ñ�Ð¸Ñ�Ñ‚ÐµÐ¼Ð¸ Ð¿Ñ–Ð´ Ñ‡Ð°Ñ� Ð¾Ð±Ñ€Ð¾Ð±ÐºÐ¸ Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½ÑŒ Ñ�Ð¿Ð¾Ð²Ñ–Ð»ÑŒÐ½ÑŽÑ” Ð²Ð°ÑˆÐ¾Ð³Ð¾ Ð±Ð¾Ñ‚Ð°; Ð¿ÐµÑ€ÐµÐ»Ñ–Ðº Ð°Ð»ÑŒÑ‚ÐµÑ€Ð½Ð°Ñ‚Ð¸Ð²Ð½Ð¸Ñ… Ñ�Ð¿Ð¾Ñ�Ð¾Ð±Ñ–Ð² Ð·Ð±ÐµÑ€Ñ–Ð³Ð°Ð½Ð½Ñ� Ð´Ð°Ð½Ð¸Ñ…, Ñ�ÐºÑ– Ð½Ðµ Ñ�Ð¿Ð¾Ð²Ñ–Ð»ÑŒÐ½ÑŽÑŽÑ‚ÑŒ Ð±Ð¾Ñ‚Ð°, Ð´Ð¸Ð². Ð½Ð° https://docs.madelineproto.xyz/docs/UPDATES.html#avoiding-the-use-of-filesystem-functions!',
             'rpc_tg_error' => 'Telegram Ð¿Ð¾Ð²ÐµÑ€Ð½ÑƒÐ² Ð¿Ð¾Ð¼Ð¸Ð»ÐºÑƒ RPC: %s (%s), Ñ�Ð¿Ñ€Ð¸Ñ‡Ð¸Ð½ÐµÐ½Ñƒ Ð² %s:%s%sTL-Ñ‚Ñ€Ð°Ñ�ÑƒÐ²Ð°Ð½Ð½Ñ�:',
             'sec_peer_not_in_db' => 'Ð¦ÑŒÐ¾Ð³Ð¾ Ñ�ÐµÐºÑ€ÐµÑ‚Ð½Ð¾Ð³Ð¾ Ñ�Ð¿Ñ–Ð²Ñ€Ð¾Ð·Ð¼Ð¾Ð²Ð½Ð¸ÐºÐ° Ð½ÐµÐ¼Ð°Ñ” Ñƒ Ð²Ð½ÑƒÑ‚Ñ€Ñ–ÑˆÐ½Ñ–Ð¹ Ð±Ð°Ð·Ñ– Ð´Ð°Ð½Ð¸Ñ… Ñ�Ð¿Ñ–Ð²Ñ€Ð¾Ð·Ð¼Ð¾Ð²Ð½Ð¸ÐºÑ–Ð²',
             'secret_chat_skipping' => 'Ð¡ÐµÐºÑ€ÐµÑ‚Ð½Ð¾Ð³Ð¾ Ñ‡Ð°Ñ‚Ñƒ %s Ð½ÐµÐ¼Ð°Ñ” Ð² Ð±Ð°Ð·Ñ– Ð´Ð°Ð½Ð¸Ñ…, Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ� Ð¿Ñ€Ð¾Ð¿ÑƒÑ�ÐºÐ°Ñ”Ñ‚ÑŒÑ�Ñ�...',
             'serialization_ofd' => 'Ð¡ÐµÑ€Ñ–Ð°Ð»Ñ–Ð·Ð°Ñ†Ñ–Ñ� Ð·Ð°Ñ�Ñ‚Ð°Ñ€Ñ–Ð»Ð°, Ð¾Ð±\'Ñ”ÐºÑ‚ Ñ€ÐµÐºÐ¾Ð½Ñ�Ñ‚Ñ€ÑƒÑŽÑ”Ñ‚ÑŒÑ�Ñ�!',
             'session_corrupted' => 'Ð¡ÐµÑ�Ñ–ÑŽ Ð¿Ð¾ÑˆÐºÐ¾Ð´Ð¶ÐµÐ½Ð¾!',
             'signing_up' => 'Ð ÐµÑ”Ñ�Ñ‚Ñ€Ð°Ñ†Ñ–Ñ� Ñ�Ðº Ð·Ð²Ð¸Ñ‡Ð°Ð¹Ð½Ð¸Ð¹ ÐºÐ¾Ñ€Ð¸Ñ�Ñ‚ÑƒÐ²Ð°Ñ‡...',
             'signupFirstName' => 'Ð’Ð²ÐµÐ´Ñ–Ñ‚ÑŒ Ñ�Ð²Ð¾Ñ” Ñ–Ð¼\'Ñ�: ',
             'signupFirstNameWeb' => 'Ð†Ð¼\'Ñ�',
             'signupLastName' => 'Ð’Ð²ÐµÐ´Ñ–Ñ‚ÑŒ Ñ�Ð²Ð¾Ñ” Ð¿Ñ€Ñ–Ð·Ð²Ð¸Ñ‰Ðµ (Ð¼Ð¾Ð¶Ð½Ð° Ð·Ð°Ð»Ð¸ÑˆÐ¸Ñ‚Ð¸ Ð¿Ð¾Ñ€Ð¾Ð¶Ð½Ñ–Ð¼): ',
             'signupLastNameWeb' => 'ÐŸÑ€Ñ–Ð·Ð²Ð¸Ñ‰Ðµ',
             'signupLeadWeb' => 'Ð¦ÑŒÐ¾Ð³Ð¾ Ð½Ð¾Ð¼ÐµÑ€Ð° Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½Ñƒ Ñ‰Ðµ Ð½ÐµÐ¼Ð°Ñ” Ð² Telegram: Ð²Ð¸Ð±ÐµÑ€Ñ–Ñ‚ÑŒ Ñ–Ð¼\'Ñ� Ð½Ð¾Ð²Ð¾Ð³Ð¾ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¾Ð³Ð¾ Ð·Ð°Ð¿Ð¸Ñ�Ñƒ.',
             'signupTitleWeb' => 'Ð¡Ñ‚Ð²Ð¾Ñ€Ñ–Ñ‚ÑŒ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¸Ð¹ Ð·Ð°Ð¿Ð¸Ñ�',
             'signupWeb' => 'Ð—Ð°Ñ€ÐµÑ”Ñ�Ñ‚Ñ€ÑƒÐ¹Ñ‚ÐµÑ�Ñ�',
             'signup_ok' => 'Ð ÐµÑ”Ñ�Ñ‚Ñ€Ð°Ñ†Ñ–ÑŽ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð¾ ÑƒÑ�Ð¿Ñ–ÑˆÐ½Ð¾!',
             'signup_uncalled' => 'Ð¯ Ð½Ðµ Ð¾Ñ‡Ñ–ÐºÑƒÑŽ Ñ€ÐµÑ”Ñ�Ñ‚Ñ€Ð°Ñ†Ñ–Ñ—! Ð¡Ð¿Ð¾Ñ‡Ð°Ñ‚ÐºÑƒ Ð²Ð¸ÐºÐ»Ð¸Ñ‡Ñ‚Ðµ Ð¼ÐµÑ‚Ð¾Ð´Ð¸ phoneLogin Ñ– completePhoneLogin!',
             'src_file_invalid' => 'ÐŸÐµÑ€ÐµÐ´Ð°Ð½Ð¾ Ð½ÐµÐ´Ñ–Ð¹Ñ�Ð½Ð¸Ð¹ Ð²Ð¸Ñ…Ñ–Ð´Ð½Ð¸Ð¹ Ñ„Ð°Ð¹Ð»: ',
             'static_analysis_minor' => 'ÐŸÑ–Ð´ Ñ‡Ð°Ñ� Ñ�Ñ‚Ð°Ñ‚Ð¸Ñ‡Ð½Ð¾Ð³Ð¾ Ð°Ð½Ð°Ð»Ñ–Ð·Ñƒ %s Ð²Ð¸Ñ�Ð²Ð»ÐµÐ½Ð¾ Ð½ÐµÐ·Ð½Ð°Ñ‡Ð½Ñƒ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼Ñƒ: %s',
             'static_analysis_severe' => 'ÐŸÑ–Ð´ Ñ‡Ð°Ñ� Ñ�Ñ‚Ð°Ñ‚Ð¸Ñ‡Ð½Ð¾Ð³Ð¾ Ð°Ð½Ð°Ð»Ñ–Ð·Ñƒ %s Ð²Ð¸Ñ�Ð²Ð»ÐµÐ½Ð¾ Ñ�ÐµÑ€Ð¹Ð¾Ð·Ð½Ñƒ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼Ñƒ: %s',
             'stream_handle_invalid' => 'ÐŸÐµÑ€ÐµÐ´Ð°Ð½Ð¾ Ð½ÐµÐ´Ñ–Ð¹Ñ�Ð½Ð¸Ð¹ Ð´ÐµÑ�ÐºÑ€Ð¸Ð¿Ñ‚Ð¾Ñ€ Ð¿Ð¾Ñ‚Ð¾ÐºÑƒ.',
             'string_required' => 'ÐžÑ‡Ñ–ÐºÑƒÐ²Ð°Ð²Ñ�Ñ� Ñ€Ñ�Ð´Ð¾Ðº!',
             'translate_madelineproto_cli' => 'MadelineProto Ð¼Ð¾Ð¶Ð½Ð° Ð¿ÐµÑ€ÐµÐºÐ»Ð°Ñ�Ñ‚Ð¸ Ð²Ð°ÑˆÐ¾ÑŽ Ð¼Ð¾Ð²Ð¾ÑŽ (Ð¿Ð¾Ñ‚Ð¾Ñ‡Ð½Ð¸Ð¹ Ð¿Ñ€Ð¾Ð³Ñ€ÐµÑ� Ð¿ÐµÑ€ÐµÐºÐ»Ð°Ð´Ñƒ: %d%%), Ð¿ÐµÑ€ÐµÐ¹Ð´Ñ–Ñ‚ÑŒ Ð½Ð° https://github.com/danog/MadelineProto/tree/v8/langs, Ñ‰Ð¾Ð± Ð´Ð¾Ð»ÑƒÑ‡Ð¸Ñ‚Ð¸Ñ�Ñ� Ð´Ð¾ Ð¿ÐµÑ€ÐµÐºÐ»Ð°Ð´Ñƒ!',
             'translate_madelineproto_web' => 'MadelineProto Ð¼Ð¾Ð¶Ð½Ð° Ð¿ÐµÑ€ÐµÐºÐ»Ð°Ñ�Ñ‚Ð¸ Ð²Ð°ÑˆÐ¾ÑŽ Ð¼Ð¾Ð²Ð¾ÑŽ (Ð¿Ð¾Ñ‚Ð¾Ñ‡Ð½Ð¸Ð¹ Ð¿Ñ€Ð¾Ð³Ñ€ÐµÑ� Ð¿ÐµÑ€ÐµÐºÐ»Ð°Ð´Ñƒ: %d%%), Ð½Ð°Ñ‚Ð¸Ñ�Ð½Ñ–Ñ‚ÑŒ <a href="https://github.com/danog/MadelineProto/tree/v8/langs" target="_blank">Ñ‚ÑƒÑ‚, Ñ‰Ð¾Ð± Ð´Ð¾Ð»ÑƒÑ‡Ð¸Ñ‚Ð¸Ñ�Ñ� Ð´Ð¾ Ð¿ÐµÑ€ÐµÐºÐ»Ð°Ð´Ñƒ!</a>',
             'tweb.ACCESS_TOKEN_EXPIRED' => 'Ð¦ÐµÐ¹ Ñ‚Ð¾ÐºÐµÐ½ Ð±Ð¾Ñ‚Ð° Ð²Ñ–Ð´ÐºÐ»Ð¸ÐºÐ°Ð½Ð¾',
             'tweb.ACCESS_TOKEN_INVALID' => 'Ð�ÐµÐ¿Ñ€Ð°Ð²Ð¸Ð»ÑŒÐ½Ð¸Ð¹ Ñ‚Ð¾ÐºÐµÐ½ Ð±Ð¾Ñ‚Ð°',
             'tweb.AccDescr.LoginCode' => 'ÐšÐ¾Ð´ Ð²Ñ…Ð¾Ð´Ñƒ',
             'tweb.Close' => 'Ð—Ð°ÐºÑ€Ð¸Ñ‚Ð¸',
             'tweb.Code' => 'ÐšÐ¾Ð´',
             'tweb.Contacts.PhoneNumber.Placeholder' => 'Ð�Ð¾Ð¼ÐµÑ€ Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½Ñƒ',
             'tweb.Country' => 'ÐšÑ€Ð°Ñ—Ð½Ð°',
             'tweb.DarkMode' => 'Ð¢ÐµÐ¼Ð½Ð° Ñ‚ÐµÐ¼Ð°',
             'tweb.Error.AnError' => 'Ð¡Ñ‚Ð°Ð»Ð°Ñ�Ñ� Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ°. Ð¡Ð¿Ñ€Ð¾Ð±ÑƒÐ¹Ñ‚Ðµ Ð¿Ñ–Ð·Ð½Ñ–ÑˆÐµ.',
             'tweb.FIRSTNAME_INVALID' => 'Ð�ÐµÐ¿Ñ€Ð¸Ð¿ÑƒÑ�Ñ‚Ð¸Ð¼Ðµ Ñ–Ð¼\'Ñ�',
             'tweb.FirstName' => 'Ð†Ð¼\'Ñ� (Ð¾Ð±Ð¾Ð²\'Ñ�Ð·ÐºÐ¾Ð²Ð¾)',
             'tweb.LASTNAME_INVALID' => 'Ð�ÐµÐ¿Ñ€Ð¸Ð¿ÑƒÑ�Ñ‚Ð¸Ð¼Ðµ Ð¿Ñ€Ñ–Ð·Ð²Ð¸Ñ‰Ðµ',
             'tweb.LastName' => 'ÐŸÑ€Ñ–Ð·Ð²Ð¸Ñ‰Ðµ (Ð½ÐµÐ¾Ð±Ð¾Ð²\'Ñ�Ð·ÐºÐ¾Ð²Ð¾)',
             'tweb.Login.Bot' => 'Ð£Ð²Ñ–Ð¹Ñ‚Ð¸ Ñ�Ðº Ð±Ð¾Ñ‚ >',
             'tweb.Login.Bot.Cancel' => 'Ð£Ð²Ñ–Ð¹Ñ‚Ð¸ Ñ�Ðº ÐºÐ¾Ñ€Ð¸Ñ�Ñ‚ÑƒÐ²Ð°Ñ‡ >',
             'tweb.Login.Bot.Subtitle' => 'Ð’Ð²ÐµÐ´Ñ–Ñ‚ÑŒ Ñ‚Ð¾ÐºÐµÐ½, Ñ�ÐºÐ¸Ð¹ Ð²Ð¸Ð´Ð°Ð² @BotFather
Ð¿Ñ–Ð´ Ñ‡Ð°Ñ� Ñ�Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ� Ð±Ð¾Ñ‚Ð°.',
             'tweb.Login.Bot.Title' => 'Ð’Ñ…Ñ–Ð´ Ñ�Ðº Ð±Ð¾Ñ‚',
             'tweb.Login.Bot.Token' => 'Ð¢Ð¾ÐºÐµÐ½ Ð±Ð¾Ñ‚Ð°',
             'tweb.Login.Code.SecretPhrase' => 'Ð¡ÐµÐºÑ€ÐµÑ‚Ð½Ð° Ñ„Ñ€Ð°Ð·Ð°',
             'tweb.Login.Code.SecretWord' => 'Ð¡ÐµÐºÑ€ÐµÑ‚Ð½Ðµ Ñ�Ð»Ð¾Ð²Ð¾',
             'tweb.Login.Code.SentCall' => 'ÐœÐ¸ Ð·Ð°Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½ÑƒÑ”Ð¼Ð¾ Ð²Ð°Ð¼ Ñ– Ð¿Ñ€Ð¾Ð´Ð¸ÐºÑ‚ÑƒÑ”Ð¼Ð¾
ÐºÐ¾Ð´.',
             'tweb.Login.Code.SentEmail' => 'ÐŸÐµÑ€ÐµÐ²Ñ–Ñ€Ñ‚Ðµ Ð¿Ð¾ÑˆÑ‚Ñƒ **%s** (Ð½Ðµ Ð·Ð°Ð±ÑƒÐ´ÑŒÑ‚Ðµ Ð¿Ñ€Ð¾ Ñ‚ÐµÐºÑƒ Â«Ð¡Ð¿Ð°Ð¼Â») Ñ– Ð²Ð²ÐµÐ´Ñ–Ñ‚ÑŒ ÐºÐ¾Ð´, Ñ�ÐºÐ¸Ð¹ Ð¼Ð¸ Ñ‰Ð¾Ð¹Ð½Ð¾ Ð½Ð°Ð´Ñ–Ñ�Ð»Ð°Ð»Ð¸.',
             'tweb.Login.Code.SentInApp' => 'ÐœÐ¸ Ð½Ð°Ð´Ñ–Ñ�Ð»Ð°Ð»Ð¸ Ð²Ð°Ð¼ Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ� Ð² Telegram
Ð· ÐºÐ¾Ð´Ð¾Ð¼.',
             'tweb.Login.Code.SentMissedCall' => 'ÐŸÑ€Ð¾Ñ‚Ñ�Ð³Ð¾Ð¼ ÐºÑ–Ð»ÑŒÐºÐ¾Ñ… Ñ�ÐµÐºÑƒÐ½Ð´ Ð²Ð°Ð¼ Ð½Ð°Ð´Ñ–Ð¹Ð´Ðµ
ÐºÐ¾Ñ€Ð¾Ñ‚ÐºÐ¸Ð¹ Ð´Ð·Ð²Ñ–Ð½Ð¾Ðº Ð· Ð½Ð¾Ð¼ÐµÑ€Ð° **%s**. Ð�Ðµ Ð²Ñ–Ð´Ð¿Ð¾Ð²Ñ–Ð´Ð°Ð¹Ñ‚Ðµ â€”
Ð¿Ñ€Ð¾Ñ�Ñ‚Ð¾ Ð²Ð²ÐµÐ´Ñ–Ñ‚ÑŒ Ð¾Ñ�Ñ‚Ð°Ð½Ð½Ñ– Ñ†Ð¸Ñ„Ñ€Ð¸ Ñ†ÑŒÐ¾Ð³Ð¾ Ð½Ð¾Ð¼ÐµÑ€Ð°.',
             'tweb.Login.Code.SentSms' => 'ÐœÐ¸ Ð½Ð°Ð´Ñ–Ñ�Ð»Ð°Ð»Ð¸ Ð²Ð°Ð¼ SMS
Ð· ÐºÐ¾Ð´Ð¾Ð¼.',
             'tweb.Login.Code.SentSmsPhrase' => 'ÐœÐ¸ Ð½Ð°Ð´Ñ–Ñ�Ð»Ð°Ð»Ð¸ SMS Ñ–Ð· Ñ�ÐµÐºÑ€ÐµÑ‚Ð½Ð¾ÑŽ Ñ„Ñ€Ð°Ð·Ð¾ÑŽ
Ð½Ð° Ð²Ð°Ñˆ Ð½Ð¾Ð¼ÐµÑ€ **%s**.',
             'tweb.Login.Code.SentSmsPhraseBeginning' => 'ÐœÐ¸ Ð½Ð°Ð´Ñ–Ñ�Ð»Ð°Ð»Ð¸ SMS Ñ–Ð· Ñ�ÐµÐºÑ€ÐµÑ‚Ð½Ð¾ÑŽ Ñ„Ñ€Ð°Ð·Ð¾ÑŽ, Ñ�ÐºÐ° Ð¿Ð¾Ñ‡Ð¸Ð½Ð°Ñ”Ñ‚ÑŒÑ�Ñ�
Ð· Â«**%2$s**Â», Ð½Ð° Ð²Ð°Ñˆ Ð½Ð¾Ð¼ÐµÑ€ **%1$s**.',
             'tweb.Login.Code.SentSmsWord' => 'ÐœÐ¸ Ð½Ð°Ð´Ñ–Ñ�Ð»Ð°Ð»Ð¸ SMS Ñ–Ð· Ñ�ÐµÐºÑ€ÐµÑ‚Ð½Ð¸Ð¼ Ñ�Ð»Ð¾Ð²Ð¾Ð¼
Ð½Ð° Ð²Ð°Ñˆ Ð½Ð¾Ð¼ÐµÑ€ **%s**.',
             'tweb.Login.Code.SentSmsWordBeginning' => 'ÐœÐ¸ Ð½Ð°Ð´Ñ–Ñ�Ð»Ð°Ð»Ð¸ SMS Ñ–Ð· Ñ�ÐµÐºÑ€ÐµÑ‚Ð½Ð¸Ð¼ Ñ�Ð»Ð¾Ð²Ð¾Ð¼, Ñ�ÐºÐµ Ð¿Ð¾Ñ‡Ð¸Ð½Ð°Ñ”Ñ‚ÑŒÑ�Ñ�
Ð· Â«**%2$s**Â», Ð½Ð° Ð²Ð°Ñˆ Ð½Ð¾Ð¼ÐµÑ€ **%1$s**.',
             'tweb.Login.Code.SentUnknown' => 'ÐŸÐ¾ÑˆÑƒÐºÐ°Ð¹Ñ‚Ðµ ÐºÐ¾Ð´
ÑƒÑ�ÑŽÐ´Ð¸ (Ñ‚Ð¸Ð¿: %s)',
             'tweb.Login.Code.WordBeginningInvalid' => 'Ð�ÐµÐ¿Ñ€Ð°Ð²Ð¸Ð»ÑŒÐ½Ðµ Ñ�Ð»Ð¾Ð²Ð¾',
             'tweb.Login.Code.WordInvalid' => 'Ð�ÐµÐ¿Ñ€Ð°Ð²Ð¸Ð»ÑŒÐ½Ð¾, Ñ�Ð¿Ñ€Ð¾Ð±ÑƒÐ¹Ñ‚Ðµ Ñ‰Ðµ Ñ€Ð°Ð·',
             'tweb.Login.Next' => 'Ð”Ð°Ð»Ñ–',
             'tweb.Login.Password.Subtitle' => 'Ð’Ð°Ñˆ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¸Ð¹ Ð·Ð°Ð¿Ð¸Ñ� Ð·Ð°Ñ…Ð¸Ñ‰ÐµÐ½Ð¾
Ð´Ð¾Ð´Ð°Ñ‚ÐºÐ¾Ð²Ð¸Ð¼ Ð¿Ð°Ñ€Ð¾Ð»ÐµÐ¼',
             'tweb.Login.Password.Title' => 'Ð’Ð²ÐµÐ´Ñ–Ñ‚ÑŒ Ð¿Ð°Ñ€Ð¾Ð»ÑŒ',
             'tweb.Login.PhoneLabelInvalid' => 'Ð�ÐµÐ¿Ñ€Ð°Ð²Ð¸Ð»ÑŒÐ½Ð¸Ð¹ Ð½Ð¾Ð¼ÐµÑ€ Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½Ñƒ',
             'tweb.Login.QR.Cancel' => 'Ð’Ñ…Ñ–Ð´ Ð·Ð° Ð½Ð¾Ð¼ÐµÑ€Ð¾Ð¼ Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½Ñƒ >',
             'tweb.Login.QR.Help1' => 'Ð’Ñ–Ð´ÐºÑ€Ð¸Ð¹Ñ‚Ðµ Telegram Ð½Ð° Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½Ñ–',
             'tweb.Login.QR.Help2' => 'ÐŸÐµÑ€ÐµÐ¹Ð´Ñ–Ñ‚ÑŒ Ñƒ **Ð�Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ�** > **ÐŸÑ€Ð¸Ñ�Ñ‚Ñ€Ð¾Ñ—** > **ÐŸÑ–Ð´ÐºÐ»ÑŽÑ‡Ð¸Ñ‚Ð¸ Ð¿Ñ€Ð¸Ñ�Ñ‚Ñ€Ñ–Ð¹**',
             'tweb.Login.QR.Help3' => 'Ð�Ð°Ð²ÐµÐ´Ñ–Ñ‚ÑŒ Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½ Ð½Ð° Ñ†ÐµÐ¹ ÐµÐºÑ€Ð°Ð½, Ñ‰Ð¾Ð± Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¸Ñ‚Ð¸ Ð²Ñ…Ñ–Ð´',
             'tweb.Login.QR.Login' => 'Ð’Ñ…Ñ–Ð´ Ð·Ð° QR-ÐºÐ¾Ð´Ð¾Ð¼ >',
             'tweb.Login.QR.Subtitle' => 'Ð’Ñ–Ð´Ñ�ÐºÐ°Ð½ÑƒÐ¹Ñ‚Ðµ Ð·Ð°Ñ�Ñ‚Ð¾Ñ�ÑƒÐ½ÐºÐ¾Ð¼ Telegram Ð½Ð° Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½Ñ–',
             'tweb.Login.QR.Title' => 'Ð’Ñ…Ñ–Ð´ Ð·Ð° QR-ÐºÐ¾Ð´Ð¾Ð¼',
             'tweb.Login.Register.Subtitle' => 'Ð’Ð²ÐµÐ´Ñ–Ñ‚ÑŒ Ñ�Ð²Ð¾Ñ” Ñ–Ð¼\'Ñ�,
Ñ‰Ð¾Ð± Ñ�Ñ‚Ð²Ð¾Ñ€Ð¸Ñ‚Ð¸ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¸Ð¹ Ð·Ð°Ð¿Ð¸Ñ�',
             'tweb.Login.StartText' => 'ÐŸÐµÑ€ÐµÐ²Ñ–Ñ€Ñ‚Ðµ ÐºÐ¾Ð´ ÐºÑ€Ð°Ñ—Ð½Ð¸
Ñ– Ð²Ð²ÐµÐ´Ñ–Ñ‚ÑŒ Ñ�Ð²Ñ–Ð¹ Ð½Ð¾Ð¼ÐµÑ€ Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½Ñƒ.',
             'tweb.Login.Title' => 'Ð’Ñ…Ñ–Ð´ Ñƒ MadelineProto',
             'tweb.LoginPassword' => 'ÐŸÐ°Ñ€Ð¾Ð»ÑŒ',
             'tweb.Optional' => 'Ð½ÐµÐ¾Ð±Ð¾Ð²\'Ñ�Ð·ÐºÐ¾Ð²Ð¾',
             'tweb.PASSWORD_HASH_INVALID' => 'Ð�ÐµÐ¿Ñ€Ð°Ð²Ð¸Ð»ÑŒÐ½Ð¸Ð¹ Ð¿Ð°Ñ€Ð¾Ð»ÑŒ',
             'tweb.PHONE_CODE_EMPTY' => 'Ð�ÐµÐ¿Ñ€Ð°Ð²Ð¸Ð»ÑŒÐ½Ð¸Ð¹ ÐºÐ¾Ð´',
             'tweb.PHONE_CODE_EXPIRED' => 'Ð¢ÐµÑ€Ð¼Ñ–Ð½ Ð´Ñ–Ñ— ÐºÐ¾Ð´Ñƒ Ð¼Ð¸Ð½ÑƒÐ²',
             'tweb.PHONE_CODE_INVALID' => 'Ð�ÐµÐ¿Ñ€Ð°Ð²Ð¸Ð»ÑŒÐ½Ð¸Ð¹ ÐºÐ¾Ð´',
             'tweb.PHONE_NUMBER_BANNED' => 'Ð¦ÐµÐ¹ Ð½Ð¾Ð¼ÐµÑ€ Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½Ñƒ Ð·Ð°Ð±Ð»Ð¾ÐºÐ¾Ð²Ð°Ð½Ð¾',
             'tweb.PHONE_NUMBER_FLOOD' => 'Ð—Ð°Ð±Ð°Ð³Ð°Ñ‚Ð¾ Ñ�Ð¿Ñ€Ð¾Ð±, Ñ�Ð¿Ñ€Ð¾Ð±ÑƒÐ¹Ñ‚Ðµ Ð¿Ñ–Ð·Ð½Ñ–ÑˆÐµ',
             'tweb.PHONE_NUMBER_INVALID' => 'Ð�ÐµÐ¿Ñ€Ð°Ð²Ð¸Ð»ÑŒÐ½Ð¸Ð¹ Ð½Ð¾Ð¼ÐµÑ€ Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½Ñƒ',
             'tweb.PHONE_PASSWORD_FLOOD' => 'Ð—Ð°Ð±Ð°Ð³Ð°Ñ‚Ð¾ Ñ�Ð¿Ñ€Ð¾Ð±, Ñ�Ð¿Ñ€Ð¾Ð±ÑƒÐ¹Ñ‚Ðµ Ð¿Ñ–Ð·Ð½Ñ–ÑˆÐµ',
             'tweb.PhoneNumber.Code.Fragment.Info' => 'ÐžÑ‚Ñ€Ð¸Ð¼Ð°Ð¹Ñ‚Ðµ ÐºÐ¾Ð´ Ñƒ Ñ€Ð¾Ð·Ð´Ñ–Ð»Ñ– Numbers Ð½Ð° [Fragment]()',
             'tweb.PleaseWait' => 'Ð—Ð°Ñ‡ÐµÐºÐ°Ð¹Ñ‚Ðµ...',
             'tweb.StartMessaging' => 'ÐŸÐ¾Ñ‡Ð°Ñ‚Ð¸ Ñ�Ð¿Ñ–Ð»ÐºÑƒÐ²Ð°Ð½Ð½Ñ�',
             'tweb.YourName' => 'Ð’Ð°ÑˆÐµ Ñ–Ð¼\'Ñ�',
             'type_extract_error' => 'Ð�Ðµ Ð²Ð´Ð°Ð»Ð¾Ñ�Ñ� Ð²Ð¸Ð´Ð¾Ð±ÑƒÑ‚Ð¸ Ñ‚Ð¸Ð¿ "%s", Ð¾Ð½Ð¾Ð²Ñ–Ñ‚ÑŒ MadelineProto!',
             'type_extract_error_id' => 'Ð�Ðµ Ð²Ð´Ð°Ð»Ð¾Ñ�Ñ� Ð²Ð¸Ð´Ð¾Ð±ÑƒÑ‚Ð¸ Ñ‚Ð¸Ð¿: %s Ð· id %s, Ð¾Ð½Ð¾Ð²Ñ–Ñ‚ÑŒ MadelineProto!',
             'update_madelineproto' => 'Ð’Ð¸ Ð²Ð¸ÐºÐ¾Ñ€Ð¸Ñ�Ñ‚Ð¾Ð²ÑƒÑ”Ñ‚Ðµ Ñ�Ñ‚Ð°Ñ€Ñƒ Ð²ÐµÑ€Ñ�Ñ–ÑŽ MadelineProto, Ð¿Ð¾Ñ‚Ñ€Ñ–Ð±Ð½Ðµ Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ�: Ð¿Ð¾Ñ‚Ð¾Ñ‡Ð½Ð° Ð²ÐµÑ€Ñ�Ñ–Ñ� â€” %s, Ð° Ð¾Ñ�Ñ‚Ð°Ð½Ð½Ñ� Ð²ÐµÑ€Ñ�Ñ–Ñ� Ð· Ñ‡Ð¸Ñ�Ð»ÐµÐ½Ð½Ð¸Ð¼Ð¸ Ð²Ð¸Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð½Ñ�Ð¼Ð¸ Ð¿Ð¾Ð¼Ð¸Ð»Ð¾Ðº Ñ– Ð½Ð¾Ð²Ð¸Ð¼Ð¸ Ñ„ÑƒÐ½ÐºÑ†Ñ–Ñ�Ð¼Ð¸ â€” %s!',
             'value_bigger_than_2147483647' => 'ÐŸÐµÑ€ÐµÐ´Ð°Ð½Ðµ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ� %s Ð±Ñ–Ð»ÑŒÑˆÐµ Ð·Ð° 2147483647',
             'value_bigger_than_4294967296' => 'ÐŸÐµÑ€ÐµÐ´Ð°Ð½Ðµ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ� %s Ð±Ñ–Ð»ÑŒÑˆÐµ Ð·Ð° 4294967296',
             'value_bigger_than_9223372036854775807' => 'ÐŸÐµÑ€ÐµÐ´Ð°Ð½Ðµ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ� %s Ð±Ñ–Ð»ÑŒÑˆÐµ Ð·Ð° 9223372036854775807',
             'value_smaller_than_0' => 'ÐŸÐµÑ€ÐµÐ´Ð°Ð½Ðµ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ� %s Ð¼ÐµÐ½ÑˆÐµ Ð·Ð° 0',
             'value_smaller_than_2147483648' => 'ÐŸÐµÑ€ÐµÐ´Ð°Ð½Ðµ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ� %s Ð¼ÐµÐ½ÑˆÐµ Ð·Ð° -2147483648',
             'value_smaller_than_9223372036854775808' => 'ÐŸÐµÑ€ÐµÐ´Ð°Ð½Ðµ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ� %s Ð¼ÐµÐ½ÑˆÐµ Ð·Ð° -9223372036854775808',
             'waveform_must_have_100_values' => 'ÐœÐ°Ñ�Ð¸Ð² waveform Ð¼Ð°Ñ” Ð¼Ñ–Ñ�Ñ‚Ð¸Ñ‚Ð¸ 100 Ð·Ð½Ð°Ñ‡ÐµÐ½ÑŒ!',
             'waveform_value' => 'Ð—Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ� waveform Ð¼Ð°Ñ” Ð±ÑƒÑ‚Ð¸ Ð²Ñ–Ð´ 0 Ð´Ð¾ 31!',
             'webContinue' => 'ÐŸÑ€Ð¾Ð´Ð¾Ð²Ð¶Ð¸Ñ‚Ð¸',
             'webDocs' => 'Ð”Ð¾ÐºÑƒÐ¼ÐµÐ½Ñ‚Ð°Ñ†Ñ–Ñ�',
             'webDoneLead' => 'MadelineProto Ð¿Ñ€Ð¾Ð´Ð¾Ð²Ð¶ÑƒÑ” Ð¿Ñ€Ð°Ñ†ÑŽÐ²Ð°Ñ‚Ð¸ Ð½Ð° Ñ�ÐµÑ€Ð²ÐµÑ€Ñ–: Ñ†ÑŽ Ñ�Ñ‚Ð¾Ñ€Ñ–Ð½ÐºÑƒ Ð¼Ð¾Ð¶Ð½Ð° Ð·Ð°ÐºÑ€Ð¸Ñ‚Ð¸.',
             'webError' => '%s. ÐŸÐµÑ€ÐµÐ²Ñ–Ñ€Ñ‚Ðµ Ð¹ Ñ�Ð¿Ñ€Ð¾Ð±ÑƒÐ¹Ñ‚Ðµ Ñ‰Ðµ Ñ€Ð°Ð·.',
             'webFieldRequired' => 'Ð—Ð°Ð¿Ð¾Ð²Ð½Ñ–Ñ‚ÑŒ Ñ†Ðµ Ð¿Ð¾Ð»Ðµ.',
             'webHidePassword' => 'ÐŸÑ€Ð¸Ñ…Ð¾Ð²Ð°Ñ‚Ð¸',
             'webLogin' => 'Ð£Ð²Ñ–Ð¹Ñ‚Ð¸',
             'webOptional' => 'Ð½ÐµÐ¾Ð±Ð¾Ð²\'Ñ�Ð·ÐºÐ¾Ð²Ð¾',
             'webPhoneInvalid' => 'Ð’Ð²ÐµÐ´Ñ–Ñ‚ÑŒ Ð½Ð¾Ð¼ÐµÑ€ Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½Ñƒ Ð· ÐºÐ¾Ð´Ð¾Ð¼ ÐºÑ€Ð°Ñ—Ð½Ð¸, Ð½Ð°Ð¿Ñ€Ð¸ÐºÐ»Ð°Ð´ +39 123 456 7890.',
             'webProgress' => 'ÐŸÐµÑ€ÐµÐ±Ñ–Ð³ Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ�',
             'webQrTitle' => 'Ð’Ñ…Ñ–Ð´ Ð·Ð° QR-ÐºÐ¾Ð´Ð¾Ð¼',
             'webQrWaiting' => 'ÐžÑ‡Ñ–ÐºÑƒÐ²Ð°Ð½Ð½Ñ� Ñ�ÐºÐ°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ�â€¦',
             'webShowPassword' => 'ÐŸÐ¾ÐºÐ°Ð·Ð°Ñ‚Ð¸',
             'webStepAccount' => 'ÐžÐ±Ð»Ñ–ÐºÐ¾Ð²Ð¸Ð¹ Ð·Ð°Ð¿Ð¸Ñ�',
             'webStepApp' => 'API ID Ñ‚Ð° API hash',
             'webStepDone' => 'ÐŸÑ€Ð°Ñ†ÑŽÑ”',
             'webStepVerify' => 'ÐŸÑ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ�',
         ],
        'uz' =>
         [
             '2fa_uncalled' => 'Men parolni kutmayapman! Iltimos, birinchi navbatda phoneLogin va completePhoneLogin metodlarini ishga tushuring!',
             'accepting_call' => '%s qoâ€˜ngâ€˜irogâ€˜i qabul qilinmoqda...',
             'account_banned' => '!!!!!!! OGOHLANTIRISH!!!!!!
Telegramâ€™ning suv toshqini oldini olish tizimi ushbu akkauntni toâ€˜xtatib qoâ€˜ydi.
Davom etish uchun qoâ€˜lda tekshirish kerak.
%s telefon raqamini blokdan chiqarishni soâ€˜rab, recover@telegram.org manziliga xat yuboring va bu telefon raqami bilan nima qilishingizni qisqacha tasvirlab bering.
Keyin yana tizimga kiring.
Agar siz ushbu hisobni ataylab o\'chirib tashlagan bo\'lsangiz, bu xabarni e\'tiborsiz qoldiring.',
             'already_loggedIn' => 'MadelineProto-ning bu nusxasi allaqachon tizimga kirgan!',
             'apiAppDescLabelWeb' => 'Tavsif',
             'apiAppInstructionsAuto0' => 'Ilovangiz nomini kiriting: ',
             'apiAppInstructionsAuto1' => 'Ilovani qisqacha nomini kiriting, harflar va raqamlardan iborat 5-32 belgilar orasida ',
             'apiAppInstructionsAuto2' => 'Ilova yoki vebsay manzilini kiriting, yoki t.me/havola ',
             'apiAppInstructionsAuto3' => 'Ilova platformasini kiriting: ',
             'apiAppInstructionsAuto4' => 'Ilovangizni izohlang: ',
             'apiAppInstructionsAutoTypeOther' => 'Boshqa (izohda ko\'rsatiladi)',
             'apiAppInstructionsManual0' => 'sizning ilovangizni nomi, u istalgan ko\'rinishda bo\'lishi mumkin',
             'apiAppInstructionsManual1' => 'Ilovangizni qisqacha nomi, harflar va raqamlardan iborat 5-32 belgilar orasida',
             'apiAppInstructionsManual2' => 'Ilova yoki vebsaytingiz manzili, yoki t.me/havola',
             'apiAppInstructionsManual3' => 'istalgan narsa',
             'apiAppInstructionsManual4' => 'Ilovangizni izoh bering',
             'apiAppShortNameLabelWeb' => 'Qisqa nom',
             'apiAppTitleLabelWeb' => 'Ilova nomi',
             'apiAppUrlLabelWeb' => 'URL',
             'apiAppWeb' => 'API ma\'lumotlarini kiriting',
             'apiAutoCodeLabelWeb' => 'Kirish kodi',
             'apiAutoCodeLeadWeb' => 'my.telegram.org Telegram ilovangizga, Telegram bilan chatga kirish kodini yubordi (SMS orqali emas).',
             'apiAutoCodeTitleWeb' => 'Telegramdan kelgan kodni kiriting',
             'apiAutoErrorTitle' => 'my.telegram.org xatolik qaytardi',
             'apiAutoFallbackManual' => 'Buning oÊ»rniga API ID va hashni qoÊ»lda kiriting.',
             'apiAutoPhoneHintWeb' => 'my.telegram.org kirish kodini SMS orqali emas, Telegram ilovangizga yuboradi.',
             'apiAutoPrompt0' => 'Telegram ro\'yhatdan o\'tkazilgan telefon raqamingizni kiriting ',
             'apiAutoPrompt1' => 'Telegramdan olgan tasdiqlash kodingizni kiriting ',
             'apiAutoWeb' => 'API ID olish uchun telegramdan oldin <b>ro\'yhatdan o\'tkazilgan</b> telefon raqamingizni kiriting',
             'apiChooseAutoWeb' => 'Avtomatik olish',
             'apiChooseManualAuto' => 'API ID va hashni my.telegram.org saytidan avtomatik olishni (a) yoki ularni qoÊ»lda kiritishni (m) xohlaysizmi?',
             'apiChooseManualAutoTip' => 'Shuni esda tutingki, siz API ID/hashni to\'g\'ridan-to\'g\'ri kodda sozlamalardan foydalanib ko\'rsatishingiz mumkin: %s',
             'apiChooseManualAutoTipWeb' => 'Shuni esda tutingki, siz API identifikatorini/xesh-ni to\'g\'ridan-to\'g\'ri kodda <a target="_blank" href="%s">sozlamalardan</a> foydalanib ko\'rsatishingiz mumkin.',
             'apiChooseManualWeb' => 'Menda API ID allaqachon bor',
             'apiChoosePrompt' => 'Sizning tanlovingiz (q/a): ',
             'apiCreateErrorTitle' => 'my.telegram.org ilovani yaratmadi',
             'apiCreateLeadWeb' => 'Bu akkauntda my.telegram.org saytida hali ilova yoÊ»q. Bu maÊ¼lumotlar faqat sizga koÊ»rsatiladi va keyinroq ularni oÊ»zgartirish mumkin.',
             'apiCreateTitleWeb' => 'Ilovangizni yarating',
             'apiCreateVpsWarning' => 'Telegram VPS, VPN va boshqa maÊ¼lumotlar markazlari IP diapazonlaridan my.telegram.org saytida yangi ilova yaratishga ruxsat bermaydi, soÊ»rov esa MadelineProto ishlayotgan mashinaning IP manzilidan yuborildi. Ilovani uy internetidan (masalan, mobil internetdagi telefoningizdan) https://my.telegram.org saytida qoÊ»lda yarating, soÊ»ngra API ID va hashni kiriting.',
             'apiCreateWeb' => 'Ilova yaratish',
             'apiError' => 'Xatolik: %s. Qayta urinib ko\'ring.',
             'apiInvalidWeb' => 'API ID raqamdan iborat, API hash esa 32 belgidan iborat: ularni my.telegram.org saytidan qaytadan nusxalang.',
             'apiManualInstructions0' => 'https://my.telegram.org ga kirish',
             'apiManualInstructions1' => 'API yaratish asboblariga o\'tish',
             'apiManualInstructions2' => 'Ilova yaratishga bosing',
             'apiManualPrompt0' => 'API ID qiymatingizni kiriting ',
             'apiManualPrompt1' => 'API hash qiymatingizni kiriting ',
             'apiManualWeb' => 'API ID va API hash qiymatlaringizni kiriting',
             'apiParamsError' => 'Siz barcha kerakli parametrlarni taqdim etmadingiz!',
             'apiSaveWeb' => 'Saqlash va davom etish',
             'apiSendCodeWeb' => 'Kodni yuborish',
             'apiSetupLeadWeb' => 'Har bir Telegram ilovasiga API ID va hash kerak. MadelineProto ularni mavjud Telegram akkauntining telefon raqami yordamida my.telegram.org saytidan siz uchun olib berishi mumkin.',
             'apiSetupTitleWeb' => 'API ID va hashni oling',
             'apiStartOverWeb' => 'Boshidan boshlash',
             'api_not_set' => 'Siz api kaliti va api ID taqdim etishingiz kerak, o\'zingiz @ my.telegram.org manzilidan oling',
             'array_invalid' => 'Siz toÊ»gÊ»ri massiv taqdim qilmadingiz',
             'baseDirLimitation' => 'Basedir cheklovi sozlangan: bu ishlashga taÊ¼sir qilishi va baÊ¼zi muammolarni keltirib chiqarishi mumkin, iloji boÊ»lsa, uni oÊ»chirib qoÊ»ying!',
             'bool_error' => 'Mantiqiy natija chiqarib bo\'lmadi',
             'botAlreadyRunning' => 'Bot allaqachon ishlayapti!',
             'botapi_conversion_error' => '%s bot API obyektiga aylantirib boâ€˜lmadi',
             'call_already_accepted' => '%s chaqiruvi allaqachon qabul qilingan',
             'call_already_declined' => '%s qoâ€˜ngâ€˜iroqlari allaqachon rad etilgan',
             'call_completing' => '%s qoâ€˜ngâ€˜irogâ€˜i tugallanmoqda...',
             'call_confirming' => '%sdan qoâ€˜ngâ€˜iroq tasdiqlanmoqda...',
             'call_discarding' => '%s qoâ€˜ngâ€˜irogâ€˜i bekor qilinmoqda...',
             'call_error_1' => '%s qoâ€˜ngâ€˜irogâ€˜ini topib, qabul qilib boâ€˜lmadi',
             'call_error_2' => '%s qoâ€˜ngâ€˜irogâ€˜i topilmadi va tasdiqlanmadi',
             'call_error_3' => '%s qoâ€˜ngâ€˜irogâ€˜ini topib boâ€˜lmadi',
             'cli_need_dl.php_link' => 'CLI orqali getDownloadLink dan foydalanganda yuklab olish skripti URL manzilini ko\'rsating!',
             'constructor_not_found' => 'Ushbu tur uchun konstruktor topilmadi: ',
             'could_not_connect_to_MadelineProto' => 'MadelineProto-ga ulanib bo\'lmadi, iltimos, proc_open-ni yoqing va open_basedir cheklovlarini olib tashlang yoki tuzatish uchun veb-server yo\'lini qayta yozishni o\'chirib qo\'ying! Agar siz buni allaqachon qilgan bo\'lsangiz, PHP ning CLI versiyasi veb-versiyasi bilan bir xil ekanligiga ishonch hosil qiling (xuddi shu versiya, kengaytmalar va boshqalar) va IPC serverini ishga tushirishga to\'sqinlik qilgan xato haqida ko\'proq ma\'lumot olish uchun MadelineProto.log faylini tekshiring.',
             'could_not_convert_object' => '%s turdagi obyektni aylantirib boâ€˜lmadi',
             'deserialization_error' => 'Seriyadan chiqarishda xatolik yuz berdi',
             'dl.php_check_logs_make_sure_session_running' => 'MadelineProto EventHandler boti yoki MadelineProto IPC serveri oflayn, iltimos jurnallarni tekshiring va ulardan kamida bittasi ishlayotganiga ishonch hosil qiling!',
             'dl.php_powered_by_madelineproto' => '<a href="https://docs.madelineproto.xyz" target="_blank">MadelineProto</a> tomonidan quvvatlangan Telegram fayl yuklab olish serveri (4 GB gacha)!<br>O\'zingizning Telegram fayl yuklab olish serveringizni sozlash haqida batafsil ma\'lumot olish uchun <a href="https://docs.madelineproto.xyz/docs/FILES.html#getting-a-download-link" target="_blank">bu yerni</a> bosing!',
             'do_not_delete_MadelineProto.log' => 'MadelineProto.log fayli hech qachon o\'chirilmasligi kerak, o\'rniga sozlamalarda maxsus maksimal o\'lchamni o\'rnating!',
             'do_not_remove_MadelineProto.log_phar' => 'Iltimos, madeline.phar yoki madeline.php ni olib tashlamang, aks holda MadelineProto ishlamay qoladi. Agar sizda MadelineProto bilan bog\'liq muammo bo\'lsa, uni https://github.com/danog/MadelineProto yoki https://t.me/pwrtelegramgroup manziliga xabar qiling',
             'do_not_use_blocking_class' => 'ishlash sabablariga ko\'ra, ishlov beruvchilar asinxron bo\'lmagan bloklash sinfi %s dan foydalanmasligi mumkin, o\'rniga %s dan foydalaning',
             'do_not_use_blocking_function' => 'samaradorlik sabablariga koâ€˜ra, hodisa ishlov beruvchilari %s asinxron boâ€˜lmagan blokirovka funksiyasidan foydalanmasligi mumkin, oâ€˜rniga %s dan foydalaning',
             'do_not_use_deprecated_function' => '%s funksiyasi eskirgan, oâ€˜rniga %s dan foydalaning',
             'do_not_use_non_root_require_in_event_handler' => 'ishlash sabablariga ko\'ra, voqea ishlov beruvchisi sinfiga talab yoki qo\'shmaslik kerak, faqat ildiz darajasidagi talablarga ruxsat beriladi.',
             'do_not_use_yield' => 'MadelineProto 8 asinx funksiyalarida yield dan foydalanishni talab qilmaydi yoki qoâ€˜llab-quvvatlamaydi, avval asinxron funksiya chaqiruvlari uchun ishlatilgan barcha yield kalit soâ€˜zlarini olib tashlashingiz kerak',
             'done' => 'Bajarildi!',
             'encode_double_error' => 'Doubleni toÊ»gÊ»ri kodlab boÊ»lmadi',
             'extensionRecommended' => 'Ogohlantirish: %s kengaytmasi oÊ»rnatilmagan, MadelineProto tezligini oshirish uchun uni oÊ»rnating!',
             'extensionRequired' => 'MadelineProto ishlashi uchun %s kengaytmasi talab qilinadi. %s',
             'extensionRequiredInstallWithApt' => 'Sudo apt-get install %s ni ishga tushirib koâ€˜ring.',
             'extensionRequiredInstallWithCustomInstructions' => 'Uni oÊ»rnatish uchun %s koÊ»rsatmalariga amal qiling.',
             'file_not_exist' => 'Fayl mavjud emas',
             'file_parsing' => '%s tahlil qilinmoqda...',
             'file_type_invalid' => 'Yaroqsiz fayl turi aniqlandi (%s)',
             'fingerprint_invalid' => 'Barmoq izi yaroqsiz!',
             'go' => 'Boshlash',
             'invalid_dl.php_session' => '%s yuklab olish skripti notoâ€˜gâ€˜ri, chunki uning seans identifikatori boshqacha (kutilgan %s, %s oldi)',
             'ipc_tcp_warning' => 'UNIX sockets are not available, so the IPC server of the session listens on TCP (127.0.0.1) instead: any program running on this machine can connect to it, and control the session. On Windows 10 1803+, enable the sockets extension (extension=sockets in php.ini) to use UNIX sockets.',
             'length_too_big' => 'Uzunlik juda katta',
             'loginBot' => 'Bot tokeningizni kiriting ',
             'loginBotTokenHintWeb' => 'Botni yaratganingizda @BotFather bergan token.',
             'loginBotTokenWeb' => 'Bot tokeni',
             'loginChoosePromptWeb' => 'Foydalanuvchi sifatida yoki bot sifatida kirmoqchimisiz?',
             'loginCodeLeadWeb' => 'Telegram kirish kodini Telegram ilovangizga yubordi, agar boshqa joyda kirmagan boÊ»lsangiz, SMS orqali yuboriladi.',
             'loginCodeTitleWeb' => 'Kirish kodini kiriting',
             'loginLeadWeb' => 'Ushbu MadelineProto nusxasi qaysi akkaunt yoki bot sifatida ishlashini tanlang.',
             'loginManual' => 'Shu bilan bir qatorda, qo\'lda kirish uchun bot tokenini yoki telefon raqamini ham kiritishingiz mumkin: ',
             'loginNoCode' => 'Telefon raqamingiz kodini kiritmadingiz!',
             'loginNoName' => 'Ismingizni kiritmadingiz!',
             'loginNoPass' => 'Parolingizni kiritmadingiz!',
             'loginOptionBot' => 'Bot',
             'loginOptionUser' => 'Foydalanuvchi',
             'loginPasswordLabelWeb' => 'Parol',
             'loginPasswordLeadWeb' => 'Bu akkaunt Sozlamalar > Maxfiylik va xavfsizlik > Ikki bosqichli tasdiqlash boÊ»limida oÊ»rnatilgan parol bilan himoyalangan.',
             'loginPasswordTitleWeb' => 'Ikki bosqichli tasdiqlash',
             'loginQr' => 'Telefoningizda Telegram-ni oching, Sozlamalar > Qurilmalar > Ish stoli qurilmasini ulash-ga oâ€˜ting va avtomatik kirish uchun yuqoridagi QR kodni skanerlang.',
             'loginQrCodeExpired' => 'QR kod eskirdi, yangisini yaratish...',
             'loginQrCodeSuccessful' => 'QR kod orqali muvaffaqiyatli kirish amalga oshirildi!',
             'loginTitleWeb' => 'Telegramga kirish',
             'loginTypeWeb' => 'Kim sifatida kirish',
             'loginUser' => 'Telefon raqamingizni kiriting ',
             'loginUserCode' => 'Kodni kiriting: ',
             'loginUserPass' => 'Parolingizni kiriting (eslatma %s): ',
             'loginUserPassHint' => 'Eslatma: %s',
             'loginUserPassWeb' => 'Parolingizni kiriting: ',
             'loginUserPhoneCodeWeb' => 'Kod',
             'loginUserPhoneHintWeb' => 'Davlat kodi bilan, masalan +39 123 456 7890.',
             'loginUserPhoneWeb' => 'Telefon raqam',
             'loginWebQr' => 'Shuningdek, siz telefoningizda Telegramâ€™ni ochib, Sozlamalar > Qurilmalar > Ish stoli qurilmasini bogâ€˜lash boâ€˜limiga oâ€˜tib, quyidagi QR kodni skanerlash orqali ham avtomatik tarzda kirishingiz mumkin:',
             'loginWebQr1' => 'Telegram dasturini telefoningizda oching',
             'loginWebQr2' => 'Sozlamalar > Uskunalar > Yangi qurilma ulashga o\'ting',
             'loginWebQr3' => 'Kirishni tasdiqlash uchun telefoningizni ushbu ekranga yo\'naltiring',
             'login_2fa_enabled' => '2FA faollashtirilgan, siz complete2falogin funksiyasini chaqirishingiz kerak bo\'ladi...',
             'login_auth_key' => 'auth keydan foydalanib kirish...',
             'login_bot' => 'Bot ko\'rinishida kirish...',
             'login_code_sending' => 'Kod yuborilmoqda...',
             'login_code_sent' => 'Kod muvaffaqiyatli yuborildi! Kodni olganingizdan so\'ng siz to\'liq PhoneLogin funksiyasidan foydalanishingiz kerak.',
             'login_code_uncalled' => 'Men kodni kutmayapman! Iltimos, avval phoneLogin funsksiyasini ishga tushuring',
             'login_need_signup' => 'Bu raqam uchun hisob yaratilmagan, siz completeSignup funksiyasidan foydalanishingiz kerak boÊ»ladi...',
             'login_ok' => 'Kirish muvaffaqiyatli amalga oshirildi!',
             'login_user' => 'Odatiy foydalanuvchi kabi kirish...',
             'logout_ok' => 'Chiqish muvaffaqiyatli amalga oshirildi!',
             'long_not_16' => 'Berilgan qiymat 16 bayt emas',
             'long_not_32' => 'Berilgan qiymat 32 bayt emas',
             'long_not_64' => 'Berilgan qiymat 64 bayt emas',
             'madelineproto_ready' => 'MadelineProto tayyor!',
             'manualAdminActionRequired' => '!!!!!!!! QO\'L QUVVAT TIZIMI ADMIN HARAKATI TALAB QILINADI !!!!!!!!!!!',
             'method_not_found' => 'Metodni topib bo\'lmadi: ',
             'mmapErrorPart1' => 'Xotira xaritalangan (mmap) mintaqalarining maksimal soniga erishildi (%s): tuzatish uchun vm.max_map_count yadro konfiguratsiyasini 262144 ga oshiring.',
             'mmapErrorPart2' => 'Tuzatish uchun quyidagi buyruqni root sifatida ishga tushiring: %s',
             'mmapErrorPart3' => 'Qayta yuklashda oÊ»zgarishlarni davom ettirish uchun: %s',
             'mmapErrorPart4' => 'Windows va WSL da sahifa fayli hajmini oshirish yordam berishi mumkin; muammo davom etsa, mahalliy Linuxga o\'ting.',
             'must_have_declare_types' => 'ishlash sabablariga ko\'ra, voqea ishlov beruvchi faylining birinchi bayonoti "declare(strict_types=1);" bo\'lishi kerak',
             'nearest_dc' => 'Biz %s ichidamiz, eng yaqin shahar %d.',
             'need_dl.php' => 'Standart yuklab olish skriptini (%s) yaratib boâ€˜lmadi, iltimos, quyidagi tarkibga ega dl.php faylini yarating: %s va uning URL manzilini getDownloadLink ikkinchi parametriga oâ€˜tkazing',
             'noReportPeers' => 'Ogohlantirish: hech qanday hisobot tengdoshlari o\'rnatilmagan, iltimos, voqea ishlov beruvchingizga quyidagi usulni qo\'shing',
             'non_text_conversion' => 'Matnli boÊ»lmagan xabarlarni oÊ»zgartirib boÊ»lmaydi!',
             'not_loggedIn' => 'Kirish amalga oshirilmagan!',
             'not_numeric' => 'Kiritilgan qiymat raqamlar emas',
             'params_missing' => 'Talab etiladigan parametr kiritilmagan',
             'peer_not_in_db' => 'Bu obyekt ichki ma\'lumotlar bazasida mavjud emas',
             'plugin_path_does_not_exist' => '%s plagin yoÊ»li mavjud emas!',
             'plugins_do_not_use_require' => 'ishlash sabablariga ko\'ra, plaginlar faqat PSR-4 avtomatik yuklagichini ishga tushirish orqali plaginlar jildida mavjud bo\'lgan boshqa fayllarni avtomatik ravishda kiritishi yoki talab qilishi mumkin (ularni qo\'lda talab qilish orqali emas).',
             'plugins_must_have_exactly_one_class' => 'plagin aynan bitta sinfni belgilashi kerak! Bir nechta sinflarni, interfeyslarni yoki xususiyatlarni aniqlash uchun alohida fayllar yarating, ular MadelineProto tomonidan avtomatik ravishda yuklanadi.',
             'predicate_not_set' => 'Predikat (_ ostidagi qiymat) belgilanmagan!',
             'recommend_not_use_filesystem_function' => '%s funksiyasidan foydalanish tavsiya etilmaydi, chunki yangilanishni qayta ishlash jarayonida fayl tizimiga kirish botni sekinlashtiradi, botni sekinlashtirmaydigan maÊ¼lumotlarni saqlashning muqobil usullari roÊ»yxatini https://docs.madelineproto.xyz/docs/UPDATES.html#avoiding-the-use-of-filesystem-functions sahifasiga oÊ»ting!',
             'rpc_tg_error' => 'Telegram RPC xatosini qaytardi: %s (%s), %s:%s%s TL izidan kelib chiqqan:',
             'sec_peer_not_in_db' => 'Ushbu maxfiy obyekt ichki obyektlar bazasida mavjud emas',
             'secret_chat_skipping' => 'Menda %s maxfiy chati maÊ¼lumotlar bazasida yoÊ»q, xabar oÊ»tkazib yuborilmoqda...',
             'serialization_ofd' => 'Seriyalash eskirgan, ob\'ekt qayta tiklanmoqda!',
             'session_corrupted' => 'Sessiya buzilgan!',
             'signing_up' => 'Oddiy foydalanuvchi sifatida roÊ»yxatdan oÊ»tish...',
             'signupFirstName' => 'Ismingizni kiriting: ',
             'signupFirstNameWeb' => 'Ism',
             'signupLastName' => 'Familiyangizni kiriting (bo\'sh qoldirish mumkin): ',
             'signupLastNameWeb' => 'Familiya',
             'signupLeadWeb' => 'Bu telefon raqami hali Telegramda yoÊ»q: yangi akkaunt uchun ism tanlang.',
             'signupTitleWeb' => 'Akkauntingizni yarating',
             'signupWeb' => 'Iltimos, ro\'yhatdan o\'ting',
             'signup_ok' => 'Muvaffaqiyatli ro\'yxatdan o\'tildi!',
             'signup_uncalled' => 'Men ro\'yxatdan o\'tishni kutmayman! Iltimos, birinchi navbatda phoneLogin va completePhoneLogin metodlarini ishga tushuring!',
             'src_file_invalid' => 'Yaroqsiz manba fayl taqdim etildi: ',
             'static_analysis_minor' => '%s: %s ni statik tahlil qilishda kichik muammoga duch keldi',
             'static_analysis_severe' => '%s: %s ni statik tahlil qilishda jiddiy muammoga duch keldi',
             'stream_handle_invalid' => 'Yaroqsiz oqim tutqichi taqdim etildi.',
             'string_required' => 'Bir qator kutilgan edi!',
             'translate_madelineproto_cli' => 'MadelineProto o\'z tilingizga tarjima qilinishi mumkin (hozirgi tarjima jarayoni: %d%%), tarjimaga hissa qo\'shish uchun https://github.com/danog/MadelineProto/tree/v8/langs saytiga o\'ting!',
             'translate_madelineproto_web' => 'MadelineProto oÊ»z tilingizga tarjima qilinishi mumkin (hozirgi tarjima jarayoni: %d%%), tarjimaga hissa qoÊ»shish uchun <a href="https://github.com/danog/MadelineProto/tree/v8/langs" target="_blank">bu yerni bosing!</a>',
             'tweb.ACCESS_TOKEN_EXPIRED' => 'Bu bot tokeni bekor qilingan',
             'tweb.ACCESS_TOKEN_INVALID' => 'Bot tokeni notoÊ»gÊ»ri',
             'tweb.AccDescr.LoginCode' => 'Kirish kodi',
             'tweb.Close' => 'Yopish',
             'tweb.Code' => 'Kod',
             'tweb.Contacts.PhoneNumber.Placeholder' => 'Telefon raqami',
             'tweb.Country' => 'Davlat',
             'tweb.DarkMode' => 'Tungi rejim',
             'tweb.Error.AnError' => 'Xatolik yuz berdi. Keyinroq qayta urinib koÊ»ring.',
             'tweb.FIRSTNAME_INVALID' => 'Ism notoÊ»gÊ»ri',
             'tweb.FirstName' => 'Ism (majburiy)',
             'tweb.LASTNAME_INVALID' => 'Familiya notoÊ»gÊ»ri',
             'tweb.LastName' => 'Familiya (ixtiyoriy)',
             'tweb.Login.Bot' => 'Bot sifatida kirish >',
             'tweb.Login.Bot.Cancel' => 'Foydalanuvchi sifatida kirish >',
             'tweb.Login.Bot.Subtitle' => 'Botni yaratganingizda @BotFather
bergan tokenni kiriting.',
             'tweb.Login.Bot.Title' => 'Bot sifatida kirish',
             'tweb.Login.Bot.Token' => 'Bot tokeni',
             'tweb.Login.Code.SecretPhrase' => 'Maxfiy ibora',
             'tweb.Login.Code.SecretWord' => 'Maxfiy soÊ»z',
             'tweb.Login.Code.SentCall' => 'Sizga qoÊ»ngÊ»iroq qilib,
kodni aytib beramiz.',
             'tweb.Login.Code.SentEmail' => 'Iltimos, **%s** elektron pochtangizni tekshiring (spam jildini ham unutmang) va hozirgina yuborgan kodimizni kiriting.',
             'tweb.Login.Code.SentInApp' => 'Sizga Telegramda kod yozilgan
xabar yubordik.',
             'tweb.Login.Code.SentMissedCall' => 'Bir necha soniya ichida **%s** raqamidan
qisqa qoÊ»ngÊ»iroq kelishi kerak. Javob bermang â€”
shunchaki shu raqamning oxirgi raqamlarini kiriting.',
             'tweb.Login.Code.SentSms' => 'Sizga kod yozilgan
SMS yubordik.',
             'tweb.Login.Code.SentSmsPhrase' => '**%s** telefon raqamingizga
maxfiy ibora yozilgan SMS yubordik.',
             'tweb.Login.Code.SentSmsPhraseBeginning' => '**%1$s** telefon raqamingizga "**%2$s**" bilan
boshlanadigan maxfiy ibora yozilgan SMS yubordik.',
             'tweb.Login.Code.SentSmsWord' => '**%s** telefon raqamingizga
maxfiy soÊ»z yozilgan SMS yubordik.',
             'tweb.Login.Code.SentSmsWordBeginning' => '**%1$s** telefon raqamingizga "**%2$s**" bilan
boshlanadigan maxfiy soÊ»z yozilgan SMS yubordik.',
             'tweb.Login.Code.SentUnknown' => 'Iltimos, kodni hamma joydan
qidirib koÊ»ring (turi: %s)',
             'tweb.Login.Code.WordBeginningInvalid' => 'NotoÊ»gÊ»ri soÊ»z',
             'tweb.Login.Code.WordInvalid' => 'NotoÊ»gÊ»ri, qayta urinib koÊ»ring',
             'tweb.Login.Next' => 'Keyingi',
             'tweb.Login.Password.Subtitle' => 'Akkauntingiz qoÊ»shimcha
parol bilan himoyalangan',
             'tweb.Login.Password.Title' => 'Parolingizni kiriting',
             'tweb.Login.PhoneLabelInvalid' => 'Telefon raqami notoÊ»gÊ»ri',
             'tweb.Login.QR.Cancel' => 'Telefon raqami orqali kirish >',
             'tweb.Login.QR.Help1' => 'Telefoningizda Telegramni oching',
             'tweb.Login.QR.Help2' => '**Sozlamalar** > **Qurilmalar** > **Qurilma qoÊ»shish** boÊ»limiga oÊ»ting',
             'tweb.Login.QR.Help3' => 'Kirishni tasdiqlash uchun telefoningizni ushbu ekranga yoÊ»naltiring',
             'tweb.Login.QR.Login' => 'QR kod orqali kirish >',
             'tweb.Login.QR.Subtitle' => 'Telefoningizdagi Telegram ilovasi bilan skanerlang',
             'tweb.Login.QR.Title' => 'QR kod orqali kirish',
             'tweb.Login.Register.Subtitle' => 'Akkaunt yaratish uchun
ismingizni kiriting',
             'tweb.Login.StartText' => 'Iltimos, davlat kodini tasdiqlang
va telefon raqamingizni kiriting.',
             'tweb.Login.Title' => 'MadelineProtoâ€™ga kirish',
             'tweb.LoginPassword' => 'Parol',
             'tweb.Optional' => 'ixtiyoriy',
             'tweb.PASSWORD_HASH_INVALID' => 'Parol notoÊ»gÊ»ri',
             'tweb.PHONE_CODE_EMPTY' => 'Kod notoÊ»gÊ»ri',
             'tweb.PHONE_CODE_EXPIRED' => 'Kod muddati tugagan',
             'tweb.PHONE_CODE_INVALID' => 'Kod notoÊ»gÊ»ri',
             'tweb.PHONE_NUMBER_BANNED' => 'Bu telefon raqami bloklangan',
             'tweb.PHONE_NUMBER_FLOOD' => 'Urinishlar juda koÊ»p, keyinroq qayta urinib koÊ»ring',
             'tweb.PHONE_NUMBER_INVALID' => 'Telefon raqami notoÊ»gÊ»ri',
             'tweb.PHONE_PASSWORD_FLOOD' => 'Urinishlar juda koÊ»p, keyinroq qayta urinib koÊ»ring',
             'tweb.PhoneNumber.Code.Fragment.Info' => 'Kodni [Fragment]() saytidagi Raqamlar boÊ»limidan oling',
             'tweb.PleaseWait' => 'Iltimos, kuting...',
             'tweb.StartMessaging' => 'Yozishmani boshlash',
             'tweb.YourName' => 'Ismingiz',
             'type_extract_error' => 'â€œ%sâ€� turini chiqarib boâ€˜lmadi, siz MadelineProtoni yangilashingiz kerak!',
             'type_extract_error_id' => 'Turni ajratib boâ€˜lmadi: %s identifikatori %s, siz MadelineProtoni yangilashingiz kerak!',
             'update_madelineproto' => 'Siz MadelineProtoâ€™ning eski versiyasini ishga tushiryapsiz, yangilash talab qilinadi: hozirda %s ishlayapti, lekin bir nechta xatoliklar tuzatilgan va yangi funksiyalarga ega soâ€˜nggi versiya %s!',
             'value_bigger_than_2147483647' => 'Taqdim etilgan %s qiymati 2147483647 dan katta',
             'value_bigger_than_4294967296' => 'Taqdim etilgan %s qiymati 4294967296 dan katta',
             'value_bigger_than_9223372036854775807' => 'Taqdim etilgan %s qiymati 9223372036854775807 dan katta',
             'value_smaller_than_0' => 'Taqdim etilgan %s qiymati 0 dan kichik',
             'value_smaller_than_2147483648' => 'Taqdim etilgan %s qiymati -2147483648dan kichik',
             'value_smaller_than_9223372036854775808' => 'Taqdim etilgan %s qiymati -9223372036854775808 dan kichik',
             'waveform_must_have_100_values' => 'To\'lqin shakli qatori 100 ta qiymatga ega bo\'lishi kerak!',
             'waveform_value' => 'To\'lqin shakli qiymati 0 dan 31 gacha bo\'lishi kerak!',
             'webContinue' => 'Davom etish',
             'webDocs' => 'Hujjatlar',
             'webDoneLead' => 'MadelineProto serverda ishlashda davom etmoqda: bu sahifani yopishingiz mumkin.',
             'webError' => '%s. Tekshirib, qayta urinib koÊ»ring.',
             'webFieldRequired' => 'Bu maydonni toÊ»ldiring.',
             'webHidePassword' => 'Yashirish',
             'webLogin' => 'Kirish',
             'webOptional' => 'ixtiyoriy',
             'webPhoneInvalid' => 'Telefon raqamini davlat kodi bilan kiriting, masalan +39 123 456 7890.',
             'webProgress' => 'Sozlash jarayoni',
             'webQrTitle' => 'QR kod orqali kirish',
             'webQrWaiting' => 'Skanerlash kutilmoqdaâ€¦',
             'webShowPassword' => 'KoÊ»rsatish',
             'webStepAccount' => 'Akkaunt',
             'webStepApp' => 'API ID va hash',
             'webStepDone' => 'Ishlamoqda',
             'webStepVerify' => 'Tasdiqlash',
         ],
        'vi' =>
         [
             '2fa_uncalled' => 'TÃ´i khÃ´ng Ä‘ang chá»� máº­t kháº©u! Vui lÃ²ng gá»�i cÃ¡c phÆ°Æ¡ng thá»©c phoneLogin vÃ  completePhoneLogin trÆ°á»›c!',
             'accepting_call' => 'Ä�ang cháº¥p nháº­n cuá»™c gá»�i tá»« %s...',
             'account_banned' => '!!!!!!! Cáº¢NH BÃ�O !!!!!!!
Há»‡ thá»‘ng chá»‘ng flood cá»§a Telegram Ä‘Ã£ táº¡m khÃ³a tÃ i khoáº£n nÃ y.
Ä�á»ƒ tiáº¿p tá»¥c, cáº§n xÃ¡c minh thá»§ cÃ´ng.
HÃ£y gá»­i email Ä‘áº¿n recover@telegram.org, yÃªu cáº§u gá»¡ cháº·n sá»‘ Ä‘iá»‡n thoáº¡i %s, vÃ  mÃ´ táº£ ngáº¯n gá»�n báº¡n sáº½ lÃ m gÃ¬ vá»›i sá»‘ Ä‘iá»‡n thoáº¡i nÃ y.
Sau Ä‘Ã³ Ä‘Äƒng nháº­p láº¡i.
Náº¿u báº¡n cá»‘ Ã½ xÃ³a tÃ i khoáº£n nÃ y, hÃ£y bá»� qua thÃ´ng bÃ¡o nÃ y.',
             'already_loggedIn' => 'Instance MadelineProto nÃ y Ä‘Ã£ Ä‘Äƒng nháº­p rá»“i!',
             'apiAppDescLabelWeb' => 'MÃ´ táº£',
             'apiAppInstructionsAuto0' => 'Nháº­p tÃªn á»©ng dá»¥ng, Ä‘áº·t tÃ¹y Ã½: ',
             'apiAppInstructionsAuto1' => 'Nháº­p tÃªn ngáº¯n cá»§a á»©ng dá»¥ng, chá»‰ gá»“m chá»¯ vÃ  sá»‘, 5-32 kÃ½ tá»±: ',
             'apiAppInstructionsAuto2' => 'Nháº­p URL cá»§a á»©ng dá»¥ng/trang web, hoáº·c t.me/yourusername: ',
             'apiAppInstructionsAuto3' => 'Nháº­p ná»�n táº£ng cá»§a á»©ng dá»¥ng: ',
             'apiAppInstructionsAuto4' => 'MÃ´ táº£ á»©ng dá»¥ng cá»§a báº¡n: ',
             'apiAppInstructionsAutoTypeOther' => 'KhÃ¡c (ghi rÃµ trong pháº§n mÃ´ táº£)',
             'apiAppInstructionsManual0' => 'tÃªn á»©ng dá»¥ng cá»§a báº¡n, Ä‘áº·t tÃ¹y Ã½',
             'apiAppInstructionsManual1' => 'tÃªn ngáº¯n cá»§a á»©ng dá»¥ng, chá»‰ gá»“m chá»¯ vÃ  sá»‘, 5-32 kÃ½ tá»±',
             'apiAppInstructionsManual2' => 'URL cá»§a á»©ng dá»¥ng/trang web cá»§a báº¡n, hoáº·c t.me/yourusername',
             'apiAppInstructionsManual3' => 'tÃ¹y Ã½',
             'apiAppInstructionsManual4' => 'MÃ´ táº£ á»©ng dá»¥ng cá»§a báº¡n táº¡i Ä‘Ã¢y',
             'apiAppShortNameLabelWeb' => 'TÃªn ngáº¯n',
             'apiAppTitleLabelWeb' => 'TÃªn á»©ng dá»¥ng',
             'apiAppUrlLabelWeb' => 'URL',
             'apiAppWeb' => 'Nháº­p thÃ´ng tin API',
             'apiAutoCodeLabelWeb' => 'MÃ£ Ä‘Äƒng nháº­p',
             'apiAutoCodeLeadWeb' => 'my.telegram.org Ä‘Ã£ gá»­i mÃ£ Ä‘Äƒng nháº­p Ä‘áº¿n á»©ng dá»¥ng Telegram cá»§a báº¡n, trong cuá»™c trÃ² chuyá»‡n vá»›i Telegram (khÃ´ng pháº£i qua SMS).',
             'apiAutoCodeTitleWeb' => 'Nháº­p mÃ£ tá»« Telegram',
             'apiAutoErrorTitle' => 'my.telegram.org Ä‘Ã£ tráº£ vá»� lá»—i',
             'apiAutoFallbackManual' => 'Thay vÃ o Ä‘Ã³, hÃ£y nháº­p API ID vÃ  hash thá»§ cÃ´ng.',
             'apiAutoPhoneHintWeb' => 'my.telegram.org gá»­i mÃ£ Ä‘Äƒng nháº­p Ä‘áº¿n á»©ng dá»¥ng Telegram cá»§a báº¡n, khÃ´ng pháº£i qua SMS.',
             'apiAutoPrompt0' => 'Nháº­p sá»‘ Ä‘iá»‡n thoáº¡i Ä‘Ã£ Ä‘Æ°á»£c Ä‘Äƒng kÃ½ trÃªn Telegram: ',
             'apiAutoPrompt1' => 'Nháº­p mÃ£ xÃ¡c minh báº¡n nháº­n Ä‘Æ°á»£c trong Telegram: ',
             'apiAutoWeb' => 'Nháº­p sá»‘ Ä‘iá»‡n thoáº¡i <b>Ä‘Ã£ Ä‘Æ°á»£c Ä‘Äƒng kÃ½</b> trÃªn Telegram Ä‘á»ƒ láº¥y API ID',
             'apiChooseAutoWeb' => 'Láº¥y tá»± Ä‘á»™ng',
             'apiChooseManualAuto' => 'Báº¡n muá»‘n láº¥y API ID vÃ  hash tá»± Ä‘á»™ng tá»« my.telegram.org (a), hay tá»± nháº­p thá»§ cÃ´ng (m)?',
             'apiChooseManualAutoTip' => 'LÆ°u Ã½ ráº±ng báº¡n cÅ©ng cÃ³ thá»ƒ cung cáº¥p API ID/hash trá»±c tiáº¿p trong code báº±ng pháº§n cÃ i Ä‘áº·t: %s',
             'apiChooseManualAutoTipWeb' => 'LÆ°u Ã½ ráº±ng báº¡n cÅ©ng cÃ³ thá»ƒ cung cáº¥p API ID/hash trá»±c tiáº¿p trong code báº±ng pháº§n <a target="_blank" href="%s">cÃ i Ä‘áº·t</a>.',
             'apiChooseManualWeb' => 'TÃ´i Ä‘Ã£ cÃ³ API ID',
             'apiChoosePrompt' => 'Lá»±a chá»�n cá»§a báº¡n (m/a): ',
             'apiCreateErrorTitle' => 'my.telegram.org khÃ´ng táº¡o Ä‘Æ°á»£c á»©ng dá»¥ng',
             'apiCreateLeadWeb' => 'TÃ i khoáº£n nÃ y chÆ°a cÃ³ á»©ng dá»¥ng nÃ o trÃªn my.telegram.org. CÃ¡c thÃ´ng tin nÃ y chá»‰ hiá»ƒn thá»‹ vá»›i báº¡n vÃ  cÃ³ thá»ƒ thay Ä‘á»•i sau.',
             'apiCreateTitleWeb' => 'Táº¡o á»©ng dá»¥ng cá»§a báº¡n',
             'apiCreateVpsWarning' => 'Telegram khÃ´ng cho phÃ©p cÃ¡c dáº£i IP cá»§a VPS, VPN vÃ  cÃ¡c trung tÃ¢m dá»¯ liá»‡u khÃ¡c táº¡o á»©ng dá»¥ng má»›i trÃªn my.telegram.org, vÃ  yÃªu cáº§u nÃ y Ä‘Æ°á»£c gá»­i tá»« IP cá»§a mÃ¡y Ä‘ang cháº¡y MadelineProto. HÃ£y táº¡o á»©ng dá»¥ng thá»§ cÃ´ng táº¡i https://my.telegram.org tá»« má»™t káº¿t ná»‘i gia Ä‘Ã¬nh (vÃ­ dá»¥ Ä‘iá»‡n thoáº¡i cá»§a báº¡n dÃ¹ng dá»¯ liá»‡u di Ä‘á»™ng), sau Ä‘Ã³ nháº­p API ID vÃ  hash.',
             'apiCreateWeb' => 'Táº¡o á»©ng dá»¥ng',
             'apiError' => 'Lá»–I: %s. Vui lÃ²ng thá»­ láº¡i.',
             'apiInvalidWeb' => 'API ID lÃ  má»™t sá»‘, cÃ²n API hash dÃ i 32 kÃ½ tá»±: hÃ£y sao chÃ©p láº¡i chÃºng tá»« my.telegram.org.',
             'apiManualInstructions0' => 'Ä�Äƒng nháº­p vÃ o https://my.telegram.org',
             'apiManualInstructions1' => 'VÃ o API development tools',
             'apiManualInstructions2' => 'Nháº¥n vÃ o create application',
             'apiManualPrompt0' => 'Nháº­p API ID cá»§a báº¡n: ',
             'apiManualPrompt1' => 'Nháº­p API hash cá»§a báº¡n: ',
             'apiManualWeb' => 'Nháº­p API ID vÃ  API hash cá»§a báº¡n',
             'apiParamsError' => 'Báº¡n chÆ°a cung cáº¥p Ä‘á»§ cÃ¡c tham sá»‘ báº¯t buá»™c!',
             'apiSaveWeb' => 'LÆ°u vÃ  tiáº¿p tá»¥c',
             'apiSendCodeWeb' => 'Gá»­i mÃ£',
             'apiSetupLeadWeb' => 'Má»�i á»©ng dá»¥ng Telegram Ä‘á»�u cáº§n API ID vÃ  hash. MadelineProto cÃ³ thá»ƒ láº¥y chÃºng giÃºp báº¡n tá»« my.telegram.org, báº±ng sá»‘ Ä‘iá»‡n thoáº¡i cá»§a má»™t tÃ i khoáº£n Telegram hiá»‡n cÃ³.',
             'apiSetupTitleWeb' => 'Láº¥y API ID vÃ  hash cá»§a báº¡n',
             'apiStartOverWeb' => 'Báº¯t Ä‘áº§u láº¡i',
             'api_not_set' => 'Báº¡n pháº£i cung cáº¥p api key vÃ  api id, hÃ£y láº¥y cá»§a riÃªng báº¡n táº¡i my.telegram.org',
             'array_invalid' => 'Báº¡n chÆ°a cung cáº¥p má»™t máº£ng há»£p lá»‡',
             'baseDirLimitation' => 'Ä�Ã£ cáº¥u hÃ¬nh giá»›i háº¡n basedir: Ä‘iá»�u nÃ y cÃ³ thá»ƒ áº£nh hÆ°á»Ÿng Ä‘áº¿n hiá»‡u nÄƒng vÃ  gÃ¢y ra má»™t sá»‘ váº¥n Ä‘á»�, vui lÃ²ng táº¯t nÃ³ náº¿u cÃ³ thá»ƒ!',
             'bool_error' => 'KhÃ´ng thá»ƒ trÃ­ch xuáº¥t giÃ¡ trá»‹ boolean',
             'botAlreadyRunning' => 'Bot Ä‘ang cháº¡y rá»“i!',
             'botapi_conversion_error' => 'KhÃ´ng thá»ƒ chuyá»ƒn Ä‘á»•i %s thÃ nh Ä‘á»‘i tÆ°á»£ng bot API',
             'call_already_accepted' => 'Cuá»™c gá»�i %s Ä‘Ã£ Ä‘Æ°á»£c cháº¥p nháº­n',
             'call_already_declined' => 'Cuá»™c gá»�i %s Ä‘Ã£ bá»‹ tá»« chá»‘i',
             'call_completing' => 'Ä�ang hoÃ n táº¥t cuá»™c gá»�i tá»« %s...',
             'call_confirming' => 'Ä�ang xÃ¡c nháº­n cuá»™c gá»�i tá»« %s...',
             'call_discarding' => 'Ä�ang káº¿t thÃºc cuá»™c gá»�i %s...',
             'call_error_1' => 'KhÃ´ng thá»ƒ tÃ¬m vÃ  cháº¥p nháº­n cuá»™c gá»�i %s',
             'call_error_2' => 'KhÃ´ng thá»ƒ tÃ¬m vÃ  xÃ¡c nháº­n cuá»™c gá»�i %s',
             'call_error_3' => 'KhÃ´ng thá»ƒ tÃ¬m vÃ  hoÃ n táº¥t cuá»™c gá»�i %s',
             'cli_need_dl.php_link' => 'Vui lÃ²ng chá»‰ Ä‘á»‹nh URL cá»§a script táº£i xuá»‘ng khi sá»­ dá»¥ng getDownloadLink qua CLI!',
             'constructor_not_found' => 'KhÃ´ng tÃ¬m tháº¥y constructor cho kiá»ƒu: ',
             'could_not_connect_to_MadelineProto' => 'KhÃ´ng thá»ƒ káº¿t ná»‘i Ä‘áº¿n MadelineProto, vui lÃ²ng báº­t proc_open vÃ  gá»¡ bá»� giá»›i háº¡n open_basedir hoáº·c táº¯t tÃ­nh nÄƒng rewrite Ä‘Æ°á»�ng dáº«n cá»§a webserver Ä‘á»ƒ kháº¯c phá»¥c! Náº¿u báº¡n Ä‘Ã£ lÃ m váº­y, hÃ£y Ä‘áº£m báº£o phiÃªn báº£n CLI cá»§a PHP giá»‘ng há»‡t phiÃªn báº£n web (cÃ¹ng phiÃªn báº£n, extension, v.v.) vÃ  xem tá»‡p MadelineProto.log Ä‘á»ƒ biáº¿t thÃªm thÃ´ng tin vá»� lá»—i Ä‘Ã£ ngÄƒn mÃ¡y chá»§ IPC khá»Ÿi Ä‘á»™ng.',
             'could_not_convert_object' => 'KhÃ´ng thá»ƒ chuyá»ƒn Ä‘á»•i Ä‘á»‘i tÆ°á»£ng kiá»ƒu %s',
             'deserialization_error' => 'Ä�Ã£ xáº£y ra lá»—i khi giáº£i tuáº§n tá»± hÃ³a',
             'dl.php_check_logs_make_sure_session_running' => 'Bot EventHandler MadelineProto liÃªn quan hoáº·c mÃ¡y chá»§ IPC MadelineProto Ä‘ang ngoáº¡i tuyáº¿n, vui lÃ²ng kiá»ƒm tra log vÃ  Ä‘áº£m báº£o Ã­t nháº¥t má»™t trong sá»‘ chÃºng Ä‘ang cháº¡y!',
             'dl.php_powered_by_madelineproto' => 'MÃ¡y chá»§ táº£i xuá»‘ng tá»‡p Telegram (tá»‘i Ä‘a 4GB), Ä‘Æ°á»£c cung cáº¥p bá»Ÿi <a href="https://docs.madelineproto.xyz" target="_blank">MadelineProto</a>!<br>Nháº¥n vÃ o <a href="https://docs.madelineproto.xyz/docs/FILES.html#getting-a-download-link" target="_blank">Ä‘Ã¢y</a> Ä‘á»ƒ biáº¿t thÃªm thÃ´ng tin vá»� cÃ¡ch thiáº¿t láº­p mÃ¡y chá»§ táº£i xuá»‘ng tá»‡p Telegram cá»§a riÃªng báº¡n!',
             'do_not_delete_MadelineProto.log' => 'tuyá»‡t Ä‘á»‘i khÃ´ng Ä‘Æ°á»£c xÃ³a tá»‡p MadelineProto.log, thay vÃ o Ä‘Ã³ hÃ£y Ä‘áº·t kÃ­ch thÆ°á»›c tá»‘i Ä‘a tÃ¹y chá»‰nh trong pháº§n cÃ i Ä‘áº·t!',
             'do_not_remove_MadelineProto.log_phar' => 'Vui lÃ²ng khÃ´ng xÃ³a madeline.phar hoáº·c madeline.php, náº¿u khÃ´ng MadelineProto sáº½ bá»‹ crash. Náº¿u báº¡n gáº·p báº¥t ká»³ váº¥n Ä‘á»� nÃ o vá»›i MadelineProto, hÃ£y bÃ¡o cÃ¡o táº¡i https://github.com/danog/MadelineProto hoáº·c https://t.me/pwrtelegramgroup',
             'do_not_use_blocking_class' => 'vÃ¬ lÃ½ do hiá»‡u nÄƒng, handler khÃ´ng Ä‘Æ°á»£c sá»­ dá»¥ng lá»›p blocking khÃ´ng async %s, thay vÃ o Ä‘Ã³ hÃ£y sá»­ dá»¥ng %s',
             'do_not_use_blocking_function' => 'vÃ¬ lÃ½ do hiá»‡u nÄƒng, event handler khÃ´ng Ä‘Æ°á»£c sá»­ dá»¥ng hÃ m blocking khÃ´ng async %s, thay vÃ o Ä‘Ã³ hÃ£y sá»­ dá»¥ng %s',
             'do_not_use_deprecated_function' => 'hÃ m %s Ä‘Ã£ lá»—i thá»�i (deprecated), thay vÃ o Ä‘Ã³ hÃ£y sá»­ dá»¥ng %s',
             'do_not_use_non_root_require_in_event_handler' => 'vÃ¬ lÃ½ do hiá»‡u nÄƒng, báº¡n khÃ´ng Ä‘Æ°á»£c sá»­ dá»¥ng require hoáº·c include bÃªn trong lá»›p event handler, chá»‰ cho phÃ©p require á»Ÿ cáº¥p gá»‘c.',
             'do_not_use_yield' => 'MadelineProto 8 khÃ´ng yÃªu cáº§u cÅ©ng khÃ´ng há»— trá»£ viá»‡c sá»­ dá»¥ng yield trong cÃ¡c hÃ m async, báº¡n pháº£i xÃ³a táº¥t cáº£ cÃ¡c tá»« khÃ³a yield Ä‘Ã£ dÃ¹ng trÆ°á»›c Ä‘Ã¢y cho cÃ¡c lá»�i gá»�i hÃ m async',
             'done' => 'Xong!',
             'encode_double_error' => 'KhÃ´ng thá»ƒ mÃ£ hÃ³a double Ä‘Ãºng cÃ¡ch',
             'extensionRecommended' => 'Cáº£nh bÃ¡o: extension %s chÆ°a Ä‘Æ°á»£c cÃ i Ä‘áº·t, vui lÃ²ng cÃ i Ä‘áº·t Ä‘á»ƒ tÄƒng tá»‘c MadelineProto!',
             'extensionRequired' => 'MadelineProto cáº§n extension %s Ä‘á»ƒ cháº¡y. %s',
             'extensionRequiredInstallWithApt' => 'HÃ£y thá»­ cháº¡y sudo apt-get install %s.',
             'extensionRequiredInstallWithCustomInstructions' => 'LÃ m theo hÆ°á»›ng dáº«n táº¡i %s Ä‘á»ƒ cÃ i Ä‘áº·t.',
             'file_not_exist' => 'Tá»‡p khÃ´ng tá»“n táº¡i',
             'file_parsing' => 'Ä�ang phÃ¢n tÃ­ch %s...',
             'file_type_invalid' => 'PhÃ¡t hiá»‡n loáº¡i tá»‡p khÃ´ng há»£p lá»‡ (%s)',
             'fingerprint_invalid' => 'Fingerprint cá»§a khÃ³a khÃ´ng há»£p lá»‡!',
             'go' => 'Tiáº¿p tá»¥c',
             'invalid_dl.php_session' => '%s khÃ´ng pháº£i lÃ  script táº£i xuá»‘ng há»£p lá»‡ vÃ¬ ID phiÃªn cá»§a nÃ³ khÃ¡c (mong Ä‘á»£i %s, nháº­n Ä‘Æ°á»£c %s)',
             'ipc_tcp_warning' => 'UNIX sockets are not available, so the IPC server of the session listens on TCP (127.0.0.1) instead: any program running on this machine can connect to it, and control the session. On Windows 10 1803+, enable the sockets extension (extension=sockets in php.ini) to use UNIX sockets.',
             'length_too_big' => 'Ä�á»™ dÃ i quÃ¡ lá»›n',
             'loginBot' => 'Nháº­p token bot cá»§a báº¡n: ',
             'loginBotTokenHintWeb' => 'Token mÃ  @BotFather Ä‘Ã£ cung cáº¥p khi báº¡n táº¡o bot.',
             'loginBotTokenWeb' => 'Token bot',
             'loginChoosePromptWeb' => 'Báº¡n muá»‘n Ä‘Äƒng nháº­p vá»›i tÆ° cÃ¡ch ngÆ°á»�i dÃ¹ng hay bot?',
             'loginCodeLeadWeb' => 'Telegram Ä‘Ã£ gá»­i mÃ£ Ä‘Äƒng nháº­p Ä‘áº¿n á»©ng dá»¥ng Telegram cá»§a báº¡n, hoáº·c qua SMS náº¿u báº¡n chÆ°a Ä‘Äƒng nháº­p á»Ÿ nÆ¡i nÃ o khÃ¡c.',
             'loginCodeTitleWeb' => 'Nháº­p mÃ£ Ä‘Äƒng nháº­p',
             'loginLeadWeb' => 'Chá»�n tÃ i khoáº£n hoáº·c bot sáº½ Ä‘Æ°á»£c dÃ¹ng Ä‘á»ƒ cháº¡y phiÃªn báº£n MadelineProto nÃ y.',
             'loginManual' => 'NgoÃ i ra, báº¡n cÅ©ng cÃ³ thá»ƒ nháº­p token bot hoáº·c sá»‘ Ä‘iá»‡n thoáº¡i Ä‘á»ƒ Ä‘Äƒng nháº­p thá»§ cÃ´ng: ',
             'loginNoCode' => 'Báº¡n chÆ°a cung cáº¥p mÃ£ Ä‘iá»‡n thoáº¡i!',
             'loginNoName' => 'Báº¡n chÆ°a cung cáº¥p tÃªn!',
             'loginNoPass' => 'Báº¡n chÆ°a cung cáº¥p máº­t kháº©u!',
             'loginOptionBot' => 'Bot',
             'loginOptionUser' => 'NgÆ°á»�i dÃ¹ng',
             'loginPasswordLabelWeb' => 'Máº­t kháº©u',
             'loginPasswordLeadWeb' => 'TÃ i khoáº£n nÃ y Ä‘Æ°á»£c báº£o vá»‡ báº±ng máº­t kháº©u, Ä‘Æ°á»£c Ä‘áº·t trong CÃ i Ä‘áº·t > Quyá»�n riÃªng tÆ° vÃ  báº£o máº­t > XÃ¡c minh hai bÆ°á»›c.',
             'loginPasswordTitleWeb' => 'XÃ¡c minh hai bÆ°á»›c',
             'loginQr' => 'Má»Ÿ Telegram trÃªn Ä‘iá»‡n thoáº¡i, vÃ o CÃ i Ä‘áº·t > Thiáº¿t bá»‹ > LiÃªn káº¿t thiáº¿t bá»‹ mÃ¡y tÃ­nh vÃ  quÃ©t mÃ£ QR á»Ÿ trÃªn Ä‘á»ƒ Ä‘Äƒng nháº­p tá»± Ä‘á»™ng.',
             'loginQrCodeExpired' => 'MÃ£ QR Ä‘Ã£ háº¿t háº¡n, Ä‘ang táº¡o mÃ£ má»›i...',
             'loginQrCodeSuccessful' => 'Ä�Äƒng nháº­p báº±ng mÃ£ QR thÃ nh cÃ´ng!',
             'loginTitleWeb' => 'Ä�Äƒng nháº­p vÃ o Telegram',
             'loginTypeWeb' => 'Ä�Äƒng nháº­p vá»›i tÆ° cÃ¡ch',
             'loginUser' => 'Nháº­p sá»‘ Ä‘iá»‡n thoáº¡i cá»§a báº¡n: ',
             'loginUserCode' => 'Nháº­p mÃ£: ',
             'loginUserPass' => 'Nháº­p máº­t kháº©u cá»§a báº¡n (gá»£i Ã½ %s): ',
             'loginUserPassHint' => 'Gá»£i Ã½: %s',
             'loginUserPassWeb' => 'Nháº­p máº­t kháº©u cá»§a báº¡n: ',
             'loginUserPhoneCodeWeb' => 'MÃ£',
             'loginUserPhoneHintWeb' => 'KÃ¨m mÃ£ quá»‘c gia, vÃ­ dá»¥ +39 123 456 7890.',
             'loginUserPhoneWeb' => 'Sá»‘ Ä‘iá»‡n thoáº¡i',
             'loginWebQr' => 'Báº¡n cÅ©ng cÃ³ thá»ƒ Ä‘Äƒng nháº­p tá»± Ä‘á»™ng báº±ng cÃ¡ch má»Ÿ Telegram trÃªn Ä‘iá»‡n thoáº¡i, vÃ o CÃ i Ä‘áº·t > Thiáº¿t bá»‹ > LiÃªn káº¿t thiáº¿t bá»‹ mÃ¡y tÃ­nh vÃ  quÃ©t mÃ£ QR sau:',
             'loginWebQr1' => 'Má»Ÿ Telegram trÃªn Ä‘iá»‡n thoáº¡i cá»§a báº¡n',
             'loginWebQr2' => 'VÃ o CÃ i Ä‘áº·t > Thiáº¿t bá»‹ > LiÃªn káº¿t thiáº¿t bá»‹ mÃ¡y tÃ­nh',
             'loginWebQr3' => 'HÆ°á»›ng Ä‘iá»‡n thoáº¡i vÃ o mÃ n hÃ¬nh nÃ y Ä‘á»ƒ xÃ¡c nháº­n Ä‘Äƒng nháº­p',
             'login_2fa_enabled' => '2FA Ä‘Ã£ Ä‘Æ°á»£c báº­t, báº¡n sáº½ pháº£i gá»�i hÃ m complete2falogin...',
             'login_auth_key' => 'Ä�ang Ä‘Äƒng nháº­p báº±ng auth key...',
             'login_bot' => 'Ä�ang Ä‘Äƒng nháº­p vá»›i tÆ° cÃ¡ch bot...',
             'login_code_sending' => 'Ä�ang gá»­i mÃ£...',
             'login_code_sent' => 'Ä�Ã£ gá»­i mÃ£ thÃ nh cÃ´ng! Khi nháº­n Ä‘Æ°á»£c mÃ£, báº¡n cáº§n sá»­ dá»¥ng hÃ m completePhoneLogin.',
             'login_code_uncalled' => 'TÃ´i khÃ´ng Ä‘ang chá»� mÃ£! Vui lÃ²ng gá»�i phÆ°Æ¡ng thá»©c phoneLogin trÆ°á»›c',
             'login_need_signup' => 'ChÆ°a cÃ³ tÃ i khoáº£n nÃ o Ä‘Æ°á»£c táº¡o cho sá»‘ nÃ y, báº¡n sáº½ pháº£i gá»�i hÃ m completeSignup...',
             'login_ok' => 'Ä�Äƒng nháº­p thÃ nh cÃ´ng!',
             'login_user' => 'Ä�ang Ä‘Äƒng nháº­p vá»›i tÆ° cÃ¡ch ngÆ°á»�i dÃ¹ng thÃ´ng thÆ°á»�ng...',
             'logout_ok' => 'Ä�Äƒng xuáº¥t thÃ nh cÃ´ng!',
             'long_not_16' => 'GiÃ¡ trá»‹ Ä‘Ã£ cho khÃ´ng dÃ i 16 byte',
             'long_not_32' => 'GiÃ¡ trá»‹ Ä‘Ã£ cho khÃ´ng dÃ i 32 byte',
             'long_not_64' => 'GiÃ¡ trá»‹ Ä‘Ã£ cho khÃ´ng dÃ i 64 byte',
             'madelineproto_ready' => 'MadelineProto Ä‘Ã£ sáºµn sÃ ng!',
             'manualAdminActionRequired' => '!!!!!!!!! Cáº¦N QUáº¢N TRá»Š VIÃŠN Há»† THá»�NG THAO TÃ�C THá»¦ CÃ”NG !!!!!!!!!',
             'method_not_found' => 'KhÃ´ng tÃ¬m tháº¥y phÆ°Æ¡ng thá»©c: ',
             'mmapErrorPart1' => 'Ä�Ã£ Ä‘áº¡t Ä‘áº¿n sá»‘ lÆ°á»£ng tá»‘i Ä‘a vÃ¹ng bá»™ nhá»› Ã¡nh xáº¡ (mmap) (%s): vui lÃ²ng tÄƒng cáº¥u hÃ¬nh kernel vm.max_map_count lÃªn 262144 Ä‘á»ƒ kháº¯c phá»¥c.',
             'mmapErrorPart2' => 'Ä�á»ƒ kháº¯c phá»¥c, hÃ£y cháº¡y lá»‡nh sau vá»›i quyá»�n root: %s',
             'mmapErrorPart3' => 'Ä�á»ƒ giá»¯ thay Ä‘á»•i sau khi khá»Ÿi Ä‘á»™ng láº¡i: %s',
             'mmapErrorPart4' => 'TrÃªn Windows vÃ  WSL, tÄƒng kÃ­ch thÆ°á»›c pagefile cÃ³ thá»ƒ giÃºp Ã­ch; vui lÃ²ng chuyá»ƒn sang Linux gá»‘c náº¿u sá»± cá»‘ váº«n tiáº¿p diá»…n.',
             'must_have_declare_types' => 'vÃ¬ lÃ½ do hiá»‡u nÄƒng, cÃ¢u lá»‡nh Ä‘áº§u tiÃªn cá»§a tá»‡p event handler pháº£i lÃ  "declare(strict_types=1);"',
             'nearest_dc' => 'ChÃºng ta Ä‘ang á»Ÿ %s, DC gáº§n nháº¥t lÃ  %d.',
             'need_dl.php' => 'KhÃ´ng thá»ƒ táº¡o script táº£i xuá»‘ng máº·c Ä‘á»‹nh (%s), vui lÃ²ng táº¡o tá»‡p dl.php vá»›i ná»™i dung sau: %s vÃ  truyá»�n URL cá»§a nÃ³ vÃ o tham sá»‘ thá»© hai cá»§a getDownloadLink',
             'noReportPeers' => 'Cáº£nh bÃ¡o: chÆ°a thiáº¿t láº­p peer nháº­n bÃ¡o cÃ¡o nÃ o, vui lÃ²ng thÃªm phÆ°Æ¡ng thá»©c sau vÃ o event handler cá»§a báº¡n',
             'non_text_conversion' => 'ChÆ°a thá»ƒ chuyá»ƒn Ä‘á»•i tin nháº¯n khÃ´ng pháº£i vÄƒn báº£n!',
             'not_loggedIn' => 'TÃ´i chÆ°a Ä‘Äƒng nháº­p!',
             'not_numeric' => 'GiÃ¡ trá»‹ Ä‘Ã£ cho khÃ´ng pháº£i lÃ  sá»‘',
             'params_missing' => 'Thiáº¿u tham sá»‘ báº¯t buá»™c',
             'peer_not_in_db' => 'Peer nÃ y khÃ´ng cÃ³ trong cÆ¡ sá»Ÿ dá»¯ liá»‡u peer ná»™i bá»™',
             'plugin_path_does_not_exist' => 'Ä�Æ°á»�ng dáº«n plugin %s khÃ´ng tá»“n táº¡i!',
             'plugins_do_not_use_require' => 'vÃ¬ lÃ½ do hiá»‡u nÄƒng, plugin chá»‰ cÃ³ thá»ƒ tá»± Ä‘á»™ng include hoáº·c require cÃ¡c tá»‡p khÃ¡c cÃ³ trong thÆ° má»¥c plugins báº±ng cÃ¡ch kÃ­ch hoáº¡t autoloader PSR-4 (khÃ´ng pháº£i báº±ng cÃ¡ch require() thá»§ cÃ´ng).',
             'plugins_must_have_exactly_one_class' => 'má»™t plugin pháº£i Ä‘á»‹nh nghÄ©a chÃ­nh xÃ¡c má»™t lá»›p! Ä�á»ƒ Ä‘á»‹nh nghÄ©a nhiá»�u lá»›p, interface hoáº·c trait, hÃ£y táº¡o cÃ¡c tá»‡p riÃªng biá»‡t, chÃºng sáº½ Ä‘Æ°á»£c MadelineProto tá»± Ä‘á»™ng náº¡p.',
             'predicate_not_set' => 'Predicate (giÃ¡ trá»‹ trong _) chÆ°a Ä‘Æ°á»£c thiáº¿t láº­p!',
             'recommend_not_use_filesystem_function' => 'khÃ´ng khuyáº¿n khÃ­ch sá»­ dá»¥ng hÃ m %s, vÃ¬ truy cáº­p há»‡ thá»‘ng tá»‡p trong khi xá»­ lÃ½ update sáº½ lÃ m cháº­m bot cá»§a báº¡n, vui lÃ²ng xem https://docs.madelineproto.xyz/docs/UPDATES.html#avoiding-the-use-of-filesystem-functions Ä‘á»ƒ biáº¿t danh sÃ¡ch cÃ¡c cÃ¡ch lÆ°u trá»¯ dá»¯ liá»‡u thay tháº¿ khÃ´ng lÃ m cháº­m bot cá»§a báº¡n!',
             'rpc_tg_error' => 'Telegram tráº£ vá»� lá»—i RPC: %s (%s), gÃ¢y ra bá»Ÿi %s:%s%sTruy váº¿t TL:',
             'sec_peer_not_in_db' => 'Peer bÃ­ máº­t nÃ y khÃ´ng cÃ³ trong cÆ¡ sá»Ÿ dá»¯ liá»‡u peer ná»™i bá»™',
             'secret_chat_skipping' => 'TÃ´i khÃ´ng cÃ³ cuá»™c trÃ² chuyá»‡n bÃ­ máº­t %s trong cÆ¡ sá»Ÿ dá»¯ liá»‡u, bá»� qua tin nháº¯n...',
             'serialization_ofd' => 'Dá»¯ liá»‡u tuáº§n tá»± hÃ³a Ä‘Ã£ lá»—i thá»�i, Ä‘ang tÃ¡i táº¡o Ä‘á»‘i tÆ°á»£ng!',
             'session_corrupted' => 'PhiÃªn bá»‹ há»�ng!',
             'signing_up' => 'Ä�ang Ä‘Äƒng kÃ½ vá»›i tÆ° cÃ¡ch ngÆ°á»�i dÃ¹ng thÃ´ng thÆ°á»�ng...',
             'signupFirstName' => 'Nháº­p tÃªn cá»§a báº¡n: ',
             'signupFirstNameWeb' => 'TÃªn',
             'signupLastName' => 'Nháº­p há»� cá»§a báº¡n (cÃ³ thá»ƒ Ä‘á»ƒ trá»‘ng): ',
             'signupLastNameWeb' => 'Há»�',
             'signupLeadWeb' => 'Sá»‘ Ä‘iá»‡n thoáº¡i nÃ y chÆ°a cÃ³ trÃªn Telegram: hÃ£y chá»�n tÃªn cho tÃ i khoáº£n má»›i.',
             'signupTitleWeb' => 'Táº¡o tÃ i khoáº£n cá»§a báº¡n',
             'signupWeb' => 'Vui lÃ²ng Ä‘Äƒng kÃ½',
             'signup_ok' => 'Ä�Äƒng kÃ½ thÃ nh cÃ´ng!',
             'signup_uncalled' => 'TÃ´i khÃ´ng Ä‘ang chá»� Ä‘Äƒng kÃ½! Vui lÃ²ng gá»�i cÃ¡c phÆ°Æ¡ng thá»©c phoneLogin vÃ  completePhoneLogin trÆ°á»›c!',
             'src_file_invalid' => 'Tá»‡p nguá»“n Ä‘Æ°á»£c cung cáº¥p khÃ´ng há»£p lá»‡: ',
             'static_analysis_minor' => 'PhÃ¡t hiá»‡n má»™t váº¥n Ä‘á»� nhá»� trong quÃ¡ trÃ¬nh phÃ¢n tÃ­ch tÄ©nh %s: %s',
             'static_analysis_severe' => 'PhÃ¡t hiá»‡n má»™t váº¥n Ä‘á»� nghiÃªm trá»�ng trong quÃ¡ trÃ¬nh phÃ¢n tÃ­ch tÄ©nh %s: %s',
             'stream_handle_invalid' => 'Stream handle Ä‘Æ°á»£c cung cáº¥p khÃ´ng há»£p lá»‡.',
             'string_required' => 'YÃªu cáº§u má»™t chuá»—i (string)!',
             'translate_madelineproto_cli' => 'MadelineProto cÃ³ thá»ƒ Ä‘Æ°á»£c dá»‹ch sang ngÃ´n ngá»¯ cá»§a báº¡n (tiáº¿n Ä‘á»™ dá»‹ch hiá»‡n táº¡i: %d%%), hÃ£y truy cáº­p https://github.com/danog/MadelineProto/tree/v8/langs Ä‘á»ƒ Ä‘Ã³ng gÃ³p báº£n dá»‹ch!',
             'translate_madelineproto_web' => 'MadelineProto cÃ³ thá»ƒ Ä‘Æ°á»£c dá»‹ch sang ngÃ´n ngá»¯ cá»§a báº¡n (tiáº¿n Ä‘á»™ dá»‹ch hiá»‡n táº¡i: %d%%), nháº¥n vÃ o <a href="https://github.com/danog/MadelineProto/tree/v8/langs" target="_blank">Ä‘Ã¢y Ä‘á»ƒ Ä‘Ã³ng gÃ³p báº£n dá»‹ch!</a>',
             'tweb.ACCESS_TOKEN_EXPIRED' => 'Token bot nÃ y Ä‘Ã£ bá»‹ thu há»“i',
             'tweb.ACCESS_TOKEN_INVALID' => 'Token bot khÃ´ng há»£p lá»‡',
             'tweb.AccDescr.LoginCode' => 'MÃ£ Ä‘Äƒng nháº­p',
             'tweb.Close' => 'Ä�Ã³ng',
             'tweb.Code' => 'MÃ£',
             'tweb.Contacts.PhoneNumber.Placeholder' => 'Sá»‘ Ä‘iá»‡n thoáº¡i',
             'tweb.Country' => 'Quá»‘c gia',
             'tweb.DarkMode' => 'Cháº¿ Ä‘á»™ tá»‘i',
             'tweb.Error.AnError' => 'Ä�Ã£ xáº£y ra lá»—i. Vui lÃ²ng thá»­ láº¡i sau.',
             'tweb.FIRSTNAME_INVALID' => 'TÃªn khÃ´ng há»£p lá»‡',
             'tweb.FirstName' => 'TÃªn (báº¯t buá»™c)',
             'tweb.LASTNAME_INVALID' => 'Há»� khÃ´ng há»£p lá»‡',
             'tweb.LastName' => 'Há»� (khÃ´ng báº¯t buá»™c)',
             'tweb.Login.Bot' => 'Ä�Äƒng nháº­p vá»›i tÆ° cÃ¡ch bot >',
             'tweb.Login.Bot.Cancel' => 'Ä�Äƒng nháº­p vá»›i tÆ° cÃ¡ch ngÆ°á»�i dÃ¹ng >',
             'tweb.Login.Bot.Subtitle' => 'Nháº­p token mÃ  @BotFather Ä‘Ã£ cung cáº¥p
khi báº¡n táº¡o bot.',
             'tweb.Login.Bot.Title' => 'Ä�Äƒng nháº­p vá»›i tÆ° cÃ¡ch bot',
             'tweb.Login.Bot.Token' => 'Token bot',
             'tweb.Login.Code.SecretPhrase' => 'Cá»¥m tá»« bÃ­ máº­t',
             'tweb.Login.Code.SecretWord' => 'Tá»« bÃ­ máº­t',
             'tweb.Login.Code.SentCall' => 'ChÃºng tÃ´i sáº½ gá»�i cho báº¡n vÃ  Ä‘á»�c
mÃ£.',
             'tweb.Login.Code.SentEmail' => 'Vui lÃ²ng kiá»ƒm tra email **%s** cá»§a báº¡n (Ä‘á»«ng quÃªn thÆ° má»¥c spam) vÃ  nháº­p mÃ£ chÃºng tÃ´i vá»«a gá»­i.',
             'tweb.Login.Code.SentInApp' => 'ChÃºng tÃ´i Ä‘Ã£ gá»­i cho báº¡n má»™t tin nháº¯n
trÃªn Telegram chá»©a mÃ£.',
             'tweb.Login.Code.SentMissedCall' => 'Trong vÃ i giÃ¢y tá»›i, báº¡n sáº½ nháº­n Ä‘Æ°á»£c
má»™t cuá»™c gá»�i ngáº¯n tá»« **%s**. Ä�á»«ng tráº£ lá»�i â€”
chá»‰ cáº§n nháº­p cÃ¡c chá»¯ sá»‘ cuá»‘i cá»§a sá»‘ Ä‘Ã³.',
             'tweb.Login.Code.SentSms' => 'ChÃºng tÃ´i Ä‘Ã£ gá»­i cho báº¡n má»™t SMS
chá»©a mÃ£.',
             'tweb.Login.Code.SentSmsPhrase' => 'ChÃºng tÃ´i Ä‘Ã£ gá»­i má»™t SMS chá»©a cá»¥m tá»« bÃ­ máº­t
Ä‘áº¿n sá»‘ Ä‘iá»‡n thoáº¡i **%s** cá»§a báº¡n.',
             'tweb.Login.Code.SentSmsPhraseBeginning' => 'ChÃºng tÃ´i Ä‘Ã£ gá»­i má»™t SMS chá»©a cá»¥m tá»« bÃ­ máº­t báº¯t Ä‘áº§u
báº±ng "**%2$s**" Ä‘áº¿n sá»‘ Ä‘iá»‡n thoáº¡i **%1$s** cá»§a báº¡n.',
             'tweb.Login.Code.SentSmsWord' => 'ChÃºng tÃ´i Ä‘Ã£ gá»­i má»™t SMS chá»©a tá»« bÃ­ máº­t
Ä‘áº¿n sá»‘ Ä‘iá»‡n thoáº¡i **%s** cá»§a báº¡n.',
             'tweb.Login.Code.SentSmsWordBeginning' => 'ChÃºng tÃ´i Ä‘Ã£ gá»­i má»™t SMS chá»©a tá»« bÃ­ máº­t báº¯t Ä‘áº§u
báº±ng "**%2$s**" Ä‘áº¿n sá»‘ Ä‘iá»‡n thoáº¡i **%1$s** cá»§a báº¡n.',
             'tweb.Login.Code.SentUnknown' => 'Vui lÃ²ng kiá»ƒm tra má»�i nÆ¡i
Ä‘á»ƒ tÃ¬m mÃ£ (loáº¡i: %s)',
             'tweb.Login.Code.WordBeginningInvalid' => 'Tá»« khÃ´ng Ä‘Ãºng',
             'tweb.Login.Code.WordInvalid' => 'KhÃ´ng Ä‘Ãºng, vui lÃ²ng thá»­ láº¡i',
             'tweb.Login.Next' => 'Tiáº¿p',
             'tweb.Login.Password.Subtitle' => 'TÃ i khoáº£n cá»§a báº¡n Ä‘Æ°á»£c báº£o vá»‡ báº±ng
má»™t máº­t kháº©u bá»• sung',
             'tweb.Login.Password.Title' => 'Nháº­p máº­t kháº©u cá»§a báº¡n',
             'tweb.Login.PhoneLabelInvalid' => 'Sá»‘ Ä‘iá»‡n thoáº¡i khÃ´ng há»£p lá»‡',
             'tweb.Login.QR.Cancel' => 'Ä�Äƒng nháº­p báº±ng sá»‘ Ä‘iá»‡n thoáº¡i >',
             'tweb.Login.QR.Help1' => 'Má»Ÿ Telegram trÃªn Ä‘iá»‡n thoáº¡i cá»§a báº¡n',
             'tweb.Login.QR.Help2' => 'VÃ o **CÃ i Ä‘áº·t** > **Thiáº¿t bá»‹** > **ThÃªm thiáº¿t bá»‹**',
             'tweb.Login.QR.Help3' => 'HÆ°á»›ng Ä‘iá»‡n thoáº¡i vÃ o mÃ n hÃ¬nh nÃ y Ä‘á»ƒ xÃ¡c nháº­n Ä‘Äƒng nháº­p',
             'tweb.Login.QR.Login' => 'Ä�Äƒng nháº­p báº±ng mÃ£ QR >',
             'tweb.Login.QR.Subtitle' => 'QuÃ©t báº±ng á»©ng dá»¥ng Telegram trÃªn Ä‘iá»‡n thoáº¡i cá»§a báº¡n',
             'tweb.Login.QR.Title' => 'Ä�Äƒng nháº­p báº±ng mÃ£ QR',
             'tweb.Login.Register.Subtitle' => 'Nháº­p tÃªn cá»§a báº¡n
Ä‘á»ƒ táº¡o tÃ i khoáº£n',
             'tweb.Login.StartText' => 'Vui lÃ²ng xÃ¡c nháº­n mÃ£ quá»‘c gia
vÃ  nháº­p sá»‘ Ä‘iá»‡n thoáº¡i cá»§a báº¡n.',
             'tweb.Login.Title' => 'Ä�Äƒng nháº­p vÃ o MadelineProto',
             'tweb.LoginPassword' => 'Máº­t kháº©u',
             'tweb.Optional' => 'khÃ´ng báº¯t buá»™c',
             'tweb.PASSWORD_HASH_INVALID' => 'Máº­t kháº©u khÃ´ng Ä‘Ãºng',
             'tweb.PHONE_CODE_EMPTY' => 'MÃ£ khÃ´ng há»£p lá»‡',
             'tweb.PHONE_CODE_EXPIRED' => 'MÃ£ Ä‘Ã£ háº¿t háº¡n',
             'tweb.PHONE_CODE_INVALID' => 'MÃ£ khÃ´ng há»£p lá»‡',
             'tweb.PHONE_NUMBER_BANNED' => 'Sá»‘ Ä‘iá»‡n thoáº¡i nÃ y Ä‘Ã£ bá»‹ cáº¥m',
             'tweb.PHONE_NUMBER_FLOOD' => 'QuÃ¡ nhiá»�u láº§n thá»­, vui lÃ²ng thá»­ láº¡i sau',
             'tweb.PHONE_NUMBER_INVALID' => 'Sá»‘ Ä‘iá»‡n thoáº¡i khÃ´ng há»£p lá»‡',
             'tweb.PHONE_PASSWORD_FLOOD' => 'QuÃ¡ nhiá»�u láº§n thá»­, vui lÃ²ng thá»­ láº¡i sau',
             'tweb.PhoneNumber.Code.Fragment.Info' => 'Láº¥y mÃ£ trong má»¥c Numbers trÃªn [Fragment]()',
             'tweb.PleaseWait' => 'Vui lÃ²ng chá»�...',
             'tweb.StartMessaging' => 'Báº¯t Ä‘áº§u nháº¯n tin',
             'tweb.YourName' => 'TÃªn cá»§a báº¡n',
             'type_extract_error' => 'KhÃ´ng thá»ƒ trÃ­ch xuáº¥t kiá»ƒu "%s", báº¡n nÃªn cáº­p nháº­t MadelineProto!',
             'type_extract_error_id' => 'KhÃ´ng thá»ƒ trÃ­ch xuáº¥t kiá»ƒu: %s vá»›i id %s, báº¡n nÃªn cáº­p nháº­t MadelineProto!',
             'update_madelineproto' => 'Báº¡n Ä‘ang cháº¡y phiÃªn báº£n cÅ© cá»§a MadelineProto, cáº§n pháº£i cáº­p nháº­t: phiÃªn báº£n hiá»‡n táº¡i lÃ  %s, nhÆ°ng phiÃªn báº£n má»›i nháº¥t vá»›i nhiá»�u báº£n sá»­a lá»—i vÃ  tÃ­nh nÄƒng má»›i lÃ  %s!',
             'value_bigger_than_2147483647' => 'GiÃ¡ trá»‹ %s Ä‘Æ°á»£c cung cáº¥p lá»›n hÆ¡n 2147483647',
             'value_bigger_than_4294967296' => 'GiÃ¡ trá»‹ %s Ä‘Æ°á»£c cung cáº¥p lá»›n hÆ¡n 4294967296',
             'value_bigger_than_9223372036854775807' => 'GiÃ¡ trá»‹ %s Ä‘Æ°á»£c cung cáº¥p lá»›n hÆ¡n 9223372036854775807',
             'value_smaller_than_0' => 'GiÃ¡ trá»‹ %s Ä‘Æ°á»£c cung cáº¥p nhá»� hÆ¡n 0',
             'value_smaller_than_2147483648' => 'GiÃ¡ trá»‹ %s Ä‘Æ°á»£c cung cáº¥p nhá»� hÆ¡n -2147483648',
             'value_smaller_than_9223372036854775808' => 'GiÃ¡ trá»‹ %s Ä‘Æ°á»£c cung cáº¥p nhá»� hÆ¡n -9223372036854775808',
             'waveform_must_have_100_values' => 'Máº£ng waveform pháº£i cÃ³ 100 giÃ¡ trá»‹!',
             'waveform_value' => 'GiÃ¡ trá»‹ waveform pháº£i náº±m trong khoáº£ng tá»« 0 Ä‘áº¿n 31!',
             'webContinue' => 'Tiáº¿p tá»¥c',
             'webDocs' => 'TÃ i liá»‡u',
             'webDoneLead' => 'MadelineProto váº«n tiáº¿p tá»¥c cháº¡y trÃªn mÃ¡y chá»§: báº¡n cÃ³ thá»ƒ Ä‘Ã³ng trang nÃ y.',
             'webError' => '%s. HÃ£y kiá»ƒm tra vÃ  thá»­ láº¡i.',
             'webFieldRequired' => 'Vui lÃ²ng Ä‘iá»�n vÃ o trÆ°á»�ng nÃ y.',
             'webHidePassword' => 'áº¨n',
             'webLogin' => 'Ä�Äƒng nháº­p',
             'webOptional' => 'khÃ´ng báº¯t buá»™c',
             'webPhoneInvalid' => 'Nháº­p sá»‘ Ä‘iá»‡n thoáº¡i kÃ¨m mÃ£ quá»‘c gia, vÃ­ dá»¥ +39 123 456 7890.',
             'webProgress' => 'Tiáº¿n trÃ¬nh thiáº¿t láº­p',
             'webQrTitle' => 'Ä�Äƒng nháº­p báº±ng mÃ£ QR',
             'webQrWaiting' => 'Ä�ang chá»� quÃ©tâ€¦',
             'webShowPassword' => 'Hiá»‡n',
             'webStepAccount' => 'TÃ i khoáº£n',
             'webStepApp' => 'API ID vÃ  hash',
             'webStepDone' => 'Ä�ang cháº¡y',
             'webStepVerify' => 'XÃ¡c minh',
         ],
        'zh' =>
         [
             '2fa_uncalled' => 'æˆ‘ä¸�ç­‰ä½ è¾“å…¥å¯†ç �ï¼�è¯·å…ˆè°ƒç”¨ phoneLogin å’Œ completePhoneLogin æ–¹æ³•ï¼�',
             'accepting_call' => 'æ­£åœ¨æŽ¥å�¬æ�¥è‡ª %s çš„ç”µè¯�...',
             'account_banned' => '!!!!!!! è­¦å‘Š !!!!!!!
Telegram çš„é˜²æ´ªç³»ç»Ÿå·²æš‚å�œæ­¤å¸�æˆ·ã€‚
è¦�ç»§ç»­ï¼Œéœ€è¦�æ‰‹åŠ¨éªŒè¯�ã€‚
å�‘é€�ç”µå­�é‚®ä»¶è‡³ recovery@telegram.orgï¼Œè¦�æ±‚è§£ç¦�ç”µè¯�å�·ç � %sï¼Œå¹¶ç®€è¦�æ��è¿°æ‚¨å°†å¦‚ä½•å¤„ç�†æ­¤ç”µè¯�å�·ç �ã€‚
ç„¶å�Žé‡�æ–°ç™»å½•ã€‚
å¦‚æžœæ‚¨æœ‰æ„�åˆ é™¤æ­¤å¸�æˆ·ï¼Œè¯·å¿½ç•¥æ­¤æ¶ˆæ�¯ã€‚',
             'already_loggedIn' => 'MadelineProto çš„è¿™ä¸ªå®žä¾‹å·²ç»�ç™»å½•ï¼�',
             'apiAppDescLabelWeb' => 'æ��è¿°',
             'apiAppInstructionsAuto0' => 'è¾“å…¥åº”ç”¨ç¨‹åº�çš„å��ç§°ï¼Œå�¯ä»¥æ˜¯ä»»ä½•å†…å®¹ï¼š ',
             'apiAppInstructionsAuto1' => 'è¾“å…¥åº”ç”¨ç¨‹åº�çš„ç®€ç§°ï¼Œå­—æ¯�æ•°å­—ï¼Œ5-32ä¸ªå­—ç¬¦ï¼š ',
             'apiAppInstructionsAuto2' => 'è¾“å…¥åº”ç”¨ç¨‹åº�/ç½‘ç«™çš„ URLï¼Œæˆ– t.me/yourusernameï¼š ',
             'apiAppInstructionsAuto3' => 'è¾“å…¥åº”ç”¨çº²é¢†ï¼š ',
             'apiAppInstructionsAuto4' => 'æ��è¿°ä½ çš„åº”ç”¨ï¼š ',
             'apiAppInstructionsAutoTypeOther' => 'å…¶ä»–ï¼ˆåœ¨æ��è¿°ä¸­æŒ‡å®šï¼‰',
             'apiAppInstructionsManual0' => 'ä½ çš„åº”ç”¨å��ç§°ï¼Œå�¯ä»¥æ˜¯ä»»æ„�å��ç§°',
             'apiAppInstructionsManual1' => 'æ‚¨çš„åº”ç”¨çš„ç®€ç§°ï¼Œå­—æ¯�æ•°å­—ï¼Œ5-32 ä¸ªå­—ç¬¦',
             'apiAppInstructionsManual2' => 'ä½ çš„åº”ç”¨/ç½‘ç«™çš„ URLï¼Œæˆ–è€… t.me/yourusername',
             'apiAppInstructionsManual3' => 'ä»»ä½•äº‹',
             'apiAppInstructionsManual4' => 'åœ¨æ­¤æ��è¿°æ‚¨çš„åº”ç”¨',
             'apiAppShortNameLabelWeb' => 'ç®€ç§°',
             'apiAppTitleLabelWeb' => 'åº”ç”¨å��ç§°',
             'apiAppUrlLabelWeb' => 'URL',
             'apiAppWeb' => 'è¾“å…¥ API ä¿¡æ�¯',
             'apiAutoCodeLabelWeb' => 'ç™»å½•ç �',
             'apiAutoCodeLeadWeb' => 'my.telegram.org å·²å°†ç™»å½•ç �å�‘é€�åˆ°æ‚¨çš„ Telegram åº”ç”¨ä¸­ä¸Ž Telegram çš„å¯¹è¯�é‡Œï¼ˆä¸�æ˜¯é€šè¿‡çŸ­ä¿¡ï¼‰ã€‚',
             'apiAutoCodeTitleWeb' => 'è¾“å…¥ Telegram å�‘é€�çš„éªŒè¯�ç �',
             'apiAutoErrorTitle' => 'my.telegram.org è¿”å›žäº†é”™è¯¯',
             'apiAutoFallbackManual' => 'è¯·æ”¹ä¸ºæ‰‹åŠ¨è¾“å…¥ API ID å’Œ hashã€‚',
             'apiAutoPhoneHintWeb' => 'my.telegram.org ä¼šå°†ç™»å½•ç �å�‘é€�åˆ°æ‚¨çš„ Telegram åº”ç”¨ï¼Œè€Œä¸�æ˜¯é€šè¿‡çŸ­ä¿¡å�‘é€�ã€‚',
             'apiAutoPrompt0' => 'è¾“å…¥å·²ç»�åœ¨ Telegram æ³¨å†Œçš„ç”µè¯�å�·ç �ï¼š ',
             'apiAutoPrompt1' => 'è¾“å…¥æ‚¨åœ¨ Telegram ä¸­æ”¶åˆ°çš„éªŒè¯�ç �ï¼š ',
             'apiAutoWeb' => 'è¾“å…¥<b>å·²åœ¨ç”µæŠ¥ä¸Šæ³¨å†Œ</b>çš„ç”µè¯�å�·ç �ä»¥èŽ·å�– API ID',
             'apiChooseAutoWeb' => 'è‡ªåŠ¨èŽ·å�–',
             'apiChooseManualAuto' => 'æ‚¨æƒ³ä»Ž my.telegram.org è‡ªåŠ¨èŽ·å�– API ID å’Œ hashï¼ˆaï¼‰ï¼Œè¿˜æ˜¯æ‰‹åŠ¨è¾“å…¥ï¼ˆmï¼‰ï¼Ÿ',
             'apiChooseManualAutoTip' => 'è¯·æ³¨æ„�ï¼Œæ‚¨ä¹Ÿå�¯ä»¥ä½¿ç”¨ä»¥ä¸‹è®¾ç½®ç›´æŽ¥åœ¨ä»£ç �ä¸­æ��ä¾› API ID/hashï¼š%s',
             'apiChooseManualAutoTipWeb' => 'è¯·æ³¨æ„�ï¼Œæ‚¨è¿˜å�¯ä»¥ä½¿ç”¨<a target="_blank" href="%s">è®¾ç½®</a>ç›´æŽ¥åœ¨ä»£ç �ä¸­æ��ä¾› API ID/hashã€‚',
             'apiChooseManualWeb' => 'æˆ‘å·²æœ‰ API ID',
             'apiChoosePrompt' => 'æ‚¨çš„é€‰æ‹© (m/a): ',
             'apiCreateErrorTitle' => 'my.telegram.org æœªèƒ½åˆ›å»ºåº”ç”¨',
             'apiCreateLeadWeb' => 'æ­¤å¸�æˆ·åœ¨ my.telegram.org ä¸Šè¿˜æ²¡æœ‰åº”ç”¨ã€‚è¿™äº›ä¿¡æ�¯ä»…å¯¹æ‚¨å�¯è§�ï¼Œä»¥å�Žä¹Ÿå�¯ä»¥ä¿®æ”¹ã€‚',
             'apiCreateTitleWeb' => 'åˆ›å»ºæ‚¨çš„åº”ç”¨',
             'apiCreateVpsWarning' => 'Telegram ä¸�å…�è®¸æ�¥è‡ª VPSã€�VPN å�Šå…¶ä»–æ•°æ�®ä¸­å¿ƒ IP æ®µçš„è¯·æ±‚åœ¨ my.telegram.org ä¸Šåˆ›å»ºæ–°åº”ç”¨ï¼Œè€Œæ­¤è¯·æ±‚æ˜¯ä»Žè¿�è¡Œ MadelineProto çš„æœºå™¨çš„ IP å�‘å‡ºçš„ã€‚è¯·ä½¿ç”¨å®¶åº­ç½‘ç»œï¼ˆä¾‹å¦‚ä½¿ç”¨ç§»åŠ¨æ•°æ�®çš„æ‰‹æœºï¼‰è®¿é—® https://my.telegram.org æ‰‹åŠ¨åˆ›å»ºåº”ç”¨ï¼Œç„¶å�Žè¾“å…¥ API ID å’Œ hashã€‚',
             'apiCreateWeb' => 'åˆ›å»ºåº”ç”¨',
             'apiError' => 'é”™è¯¯ï¼š%s. è¯·é‡�è¯•ã€‚',
             'apiInvalidWeb' => 'API ID æ˜¯ä¸€ä¸ªæ•°å­—ï¼ŒAPI hash é•¿åº¦ä¸º 32 ä¸ªå­—ç¬¦ï¼šè¯·ä»Ž my.telegram.org é‡�æ–°å¤�åˆ¶ã€‚',
             'apiManualInstructions0' => 'ç™»å½• https://my.telegram.org',
             'apiManualInstructions1' => 'å‰�å¾€API å¼€å�‘å·¥å…·',
             'apiManualInstructions2' => 'ç‚¹å‡»åˆ›å»ºåº”ç”¨ç¨‹åº�',
             'apiManualPrompt0' => 'è¾“å…¥æ‚¨çš„ API IDï¼š ',
             'apiManualPrompt1' => 'è¾“å…¥æ‚¨çš„ API hashï¼š ',
             'apiManualWeb' => 'è¾“å…¥æ‚¨çš„ API ID å’Œ API hash',
             'apiParamsError' => 'æ‚¨æ²¡æœ‰æ��ä¾›æ‰€æœ‰å¿…éœ€çš„å�‚æ•°ï¼�',
             'apiSaveWeb' => 'ä¿�å­˜å¹¶ç»§ç»­',
             'apiSendCodeWeb' => 'å�‘é€�éªŒè¯�ç �',
             'apiSetupLeadWeb' => 'æ¯�ä¸ª Telegram åº”ç”¨éƒ½éœ€è¦� API ID å’Œ hashã€‚MadelineProto å�¯ä»¥ä½¿ç”¨çŽ°æœ‰ Telegram å¸�æˆ·çš„ç”µè¯�å�·ç �ï¼Œä¸ºæ‚¨ä»Ž my.telegram.org èŽ·å�–ã€‚',
             'apiSetupTitleWeb' => 'èŽ·å�–æ‚¨çš„ API ID å’Œ hash',
             'apiStartOverWeb' => 'é‡�æ–°å¼€å§‹',
             'api_not_set' => 'æ‚¨å¿…é¡»æ��ä¾›ä¸€ä¸ª api key å’Œä¸€ä¸ª api idï¼Œè¯·èŽ·å�–æ‚¨è‡ªå·±çš„ @ my.telegram.org',
             'array_invalid' => 'æ‚¨æ²¡æœ‰æ��ä¾›æœ‰æ•ˆçš„æ•°ç»„',
             'baseDirLimitation' => 'é…�ç½®äº† basedir é™�åˆ¶ï¼šè¿™å�¯èƒ½ä¼šå½±å“�æ€§èƒ½å¹¶å¯¼è‡´ä¸€äº›é—®é¢˜ï¼Œè¯·å°½å�¯èƒ½ç¦�ç”¨å®ƒï¼�',
             'bool_error' => 'æ— æ³•æ��å�–å¸ƒå°”å€¼',
             'botAlreadyRunning' => 'æœºå™¨äººå·²å¼€å§‹è¿�è¡Œï¼�',
             'botapi_conversion_error' => 'æ— æ³•å°† %s è½¬æ�¢ä¸ºæœºå™¨äºº API å¯¹è±¡',
             'call_already_accepted' => 'å‘¼å�« %s å·²è¢«æŽ¥å�—',
             'call_already_declined' => 'å·²æ‹’ç»�æ�¥ç”µ %s',
             'call_completing' => 'æ­£åœ¨å®Œæˆ�æ�¥è‡ª %s çš„å‘¼å�«...',
             'call_confirming' => 'ç¡®è®¤æ�¥è‡ª %s çš„å‘¼å�«...',
             'call_discarding' => 'æ­£åœ¨ä¸¢å¼ƒå‘¼å�« %s...',
             'call_error_1' => 'æ— æ³•æ‰¾åˆ°å¹¶æŽ¥å�—å‘¼å�« %s',
             'call_error_2' => 'æ— æ³•æ‰¾åˆ°å¹¶ç¡®è®¤å‘¼å�« %s',
             'call_error_3' => 'æ— æ³•æ‰¾åˆ°å¹¶å®Œæˆ�å‘¼å�« %s',
             'cli_need_dl.php_link' => 'é€šè¿‡ CLI ä½¿ç”¨ getDownloadLink æ—¶ï¼Œè¯·æŒ‡å®šä¸‹è½½è„šæœ¬ URLï¼�',
             'constructor_not_found' => 'æœªæ‰¾åˆ°è¯¥ç±»åž‹çš„æž„é€ å‡½æ•°ï¼š ',
             'could_not_connect_to_MadelineProto' => 'æ— æ³•è¿žæŽ¥åˆ° MadelineProtoï¼Œè¯·å�¯ç”¨ proc_open å¹¶åˆ é™¤ open_basedir é™�åˆ¶æˆ–ç¦�ç”¨ webserver è·¯å¾„é‡�å†™ä»¥è¿›è¡Œä¿®å¤�ï¼�å¦‚æžœæ‚¨å·²ç»�è¿™æ ·å�šäº†ï¼Œè¯·ç¡®ä¿� PHP çš„ CLI ç‰ˆæœ¬ä¸Ž web ç‰ˆæœ¬å®Œå…¨ç›¸å�Œï¼ˆç›¸å�Œçš„ç‰ˆæœ¬ã€�æ‰©å±•ç­‰ï¼‰ï¼Œå¹¶æŸ¥çœ‹ MadelineProto.log æ–‡ä»¶ä»¥èŽ·å�–æœ‰å…³é˜»æ­¢ IPC æœ�åŠ¡å™¨å�¯åŠ¨çš„é”™è¯¯çš„æ›´å¤šä¿¡æ�¯ã€‚',
             'could_not_convert_object' => 'æ— æ³•è½¬æ�¢ %s ç±»åž‹çš„å¯¹è±¡',
             'deserialization_error' => 'å��åº�åˆ—åŒ–æ—¶å�‘ç”Ÿé”™è¯¯',
             'dl.php_check_logs_make_sure_session_running' => 'ç›¸å…³çš„ MadelineProto EventHandler æœºå™¨äººæˆ– MadelineProto IPC æœ�åŠ¡å™¨å¤„äºŽç¦»çº¿çŠ¶æ€�ï¼Œè¯·æ£€æŸ¥æ—¥å¿—å¹¶ç¡®ä¿�å…¶ä¸­è‡³å°‘æœ‰ä¸€ä¸ªæ­£åœ¨è¿�è¡Œï¼�',
             'dl.php_powered_by_madelineproto' => 'Telegram æ–‡ä»¶ä¸‹è½½æœ�åŠ¡å™¨ï¼ˆæœ€é«˜ 4GBï¼‰ï¼Œç”± <a href="https://docs.madelineproto.xyz" target="_blank">MadelineProto</a> æ��ä¾›æ”¯æŒ�ï¼�<br>å�•å‡»<a href="https://docs.madelineproto.xyz/docs/FILES.html#getting-a-download-link" target="_blank">æ­¤å¤„</a>äº†è§£æœ‰å…³å¦‚ä½•è®¾ç½®æ‚¨è‡ªå·±çš„ Telegram æ–‡ä»¶ä¸‹è½½æœ�åŠ¡å™¨çš„æ›´å¤šä¿¡æ�¯ï¼�',
             'do_not_delete_MadelineProto.log' => 'ç»�ä¸�èƒ½åˆ é™¤ MadelineProto.log æ–‡ä»¶ï¼Œè¯·åœ¨è®¾ç½®ä¸­è®¾ç½®è‡ªå®šä¹‰æœ€å¤§å¤§å°�ï¼�',
             'do_not_remove_MadelineProto.log_phar' => 'è¯·ä¸�è¦�åˆ é™¤ madeline.phar æˆ– madeline.phpï¼Œå�¦åˆ™ MadelineProto å°†å´©æºƒã€‚å¦‚æžœæ‚¨å¯¹ MadelineProto æœ‰ä»»ä½•é—®é¢˜ï¼Œè¯·æŠ¥å‘Šç»™ https://github.com/danog/MadelineProto æˆ– https://t.me/pwrtelegramgroup',
             'do_not_use_blocking_class' => 'å‡ºäºŽæ€§èƒ½åŽŸå› ï¼Œå¤„ç�†ç¨‹åº�å�¯èƒ½ä¸�ä¼šä½¿ç”¨é�žå¼‚æ­¥é˜»å¡žç±» %sï¼Œè¯·æ”¹ç”¨ %s',
             'do_not_use_blocking_function' => 'å‡ºäºŽæ€§èƒ½åŽŸå› ï¼Œäº‹ä»¶å¤„ç�†ç¨‹åº�å�¯èƒ½ä¸�ä¼šä½¿ç”¨é�žå¼‚æ­¥é˜»å¡žå‡½æ•° %sï¼Œè¯·æ”¹ç”¨ %s',
             'do_not_use_deprecated_function' => '%s å‡½æ•°å·²å¼ƒç”¨ï¼Œè¯·ä½¿ç”¨ %s ä»£æ›¿',
             'do_not_use_non_root_require_in_event_handler' => 'å‡ºäºŽæ€§èƒ½åŽŸå› ï¼Œæ‚¨ä¸�èƒ½åœ¨äº‹ä»¶å¤„ç�†ç¨‹åº�ç±»ä¸­ä½¿ç”¨ require æˆ– includeï¼Œå�ªå…�è®¸ä½¿ç”¨æ ¹çº§éœ€è¦�ã€‚',
             'do_not_use_yield' => 'MadelineProto 8 ä¸�è¦�æ±‚æˆ–æ”¯æŒ�åœ¨å¼‚æ­¥å‡½æ•°ä¸­ä½¿ç”¨ Yieldï¼Œæ‚¨å¿…é¡»åˆ é™¤ä»¥å‰�ç”¨äºŽå¼‚æ­¥å‡½æ•°è°ƒç”¨çš„æ‰€æœ‰ Yield å…³é”®å­—',
             'done' => 'å®Œæˆ�ï¼�',
             'encode_double_error' => 'æ— æ³•æ­£ç¡®ç¼–ç �å�Œé‡�',
             'extensionRecommended' => 'è­¦å‘Šï¼šæœªå®‰è£… %s æ‰©å±•ï¼Œè¯·å®‰è£…å®ƒä»¥åŠ å¿« MadelineProto çš„é€Ÿåº¦ï¼�',
             'extensionRequired' => 'MadelineProto éœ€è¦� %s æ‰©å±•æ‰�èƒ½è¿�è¡Œ.%s',
             'extensionRequiredInstallWithApt' => 'å°�è¯•è¿�è¡Œ sudo apt-get install %s.',
             'extensionRequiredInstallWithCustomInstructions' => 'æŒ‰ç…§ %s ä¸­çš„è¯´æ˜Žè¿›è¡Œå®‰è£…ã€‚',
             'file_not_exist' => 'æ–‡ä»¶ä¸�å­˜åœ¨',
             'file_parsing' => 'æ­£åœ¨è§£æž� %s...',
             'file_type_invalid' => 'æ£€æµ‹åˆ°æ— æ•ˆçš„æ–‡ä»¶ç±»åž‹ (%s)',
             'fingerprint_invalid' => 'å¯†é’¥æŒ‡çº¹æ— æ•ˆï¼�',
             'go' => 'åŽ»',
             'invalid_dl.php_session' => '%s ä¸�æ˜¯æœ‰æ•ˆçš„ä¸‹è½½è„šæœ¬ï¼Œå› ä¸ºå…¶ä¼šè¯� ID ä¸�å�Œï¼ˆé¢„æœŸä¸º %sï¼Œå®žé™…ä¸º %sï¼‰',
             'ipc_tcp_warning' => 'UNIX sockets are not available, so the IPC server of the session listens on TCP (127.0.0.1) instead: any program running on this machine can connect to it, and control the session. On Windows 10 1803+, enable the sockets extension (extension=sockets in php.ini) to use UNIX sockets.',
             'length_too_big' => 'é•¿åº¦å¤ªå¤§',
             'loginBot' => 'è¾“å…¥æ‚¨çš„æœºå™¨äººä»¤ç‰Œï¼š ',
             'loginBotTokenHintWeb' => 'åˆ›å»ºæœºå™¨äººæ—¶ @BotFather æ��ä¾›ç»™æ‚¨çš„ä»¤ç‰Œã€‚',
             'loginBotTokenWeb' => 'æœºå™¨äººä»¤ç‰Œ',
             'loginChoosePromptWeb' => 'æ‚¨æƒ³ä»¥ç”¨æˆ·èº«ä»½è¿˜æ˜¯æœºå™¨äººèº«ä»½ç™»å½•ï¼Ÿ',
             'loginCodeLeadWeb' => 'Telegram å·²å°†ç™»å½•ç �å�‘é€�åˆ°æ‚¨çš„ Telegram åº”ç”¨ï¼›å¦‚æžœæ‚¨æœªåœ¨å…¶ä»–åœ°æ–¹ç™»å½•ï¼Œåˆ™ä¼šé€šè¿‡çŸ­ä¿¡å�‘é€�ã€‚',
             'loginCodeTitleWeb' => 'è¾“å…¥ç™»å½•ç �',
             'loginLeadWeb' => 'é€‰æ‹©æ­¤ MadelineProto å®žä¾‹è¦�ä½¿ç”¨çš„å¸�æˆ·æˆ–æœºå™¨äººã€‚',
             'loginManual' => 'æˆ–è€…ï¼Œæ‚¨ä¹Ÿå�¯ä»¥è¾“å…¥æœºå™¨äººä»¤ç‰Œæˆ–ç”µè¯�å�·ç �æ‰‹åŠ¨ç™»å½•ï¼š ',
             'loginNoCode' => 'æ‚¨æ²¡æœ‰æ��ä¾›ç”µè¯�ä»£ç �ï¼�',
             'loginNoName' => 'æ‚¨æ²¡æœ‰æ��ä¾›å��å­—ï¼�',
             'loginNoPass' => 'æ‚¨æ²¡æœ‰æ��ä¾›å¯†ç �ï¼�',
             'loginOptionBot' => 'æœºå™¨äºº',
             'loginOptionUser' => 'ç”¨æˆ·',
             'loginPasswordLabelWeb' => 'å¯†ç �',
             'loginPasswordLeadWeb' => 'æ­¤å¸�æˆ·å�—å¯†ç �ä¿�æŠ¤ï¼Œè¯¥å¯†ç �åœ¨â€œè®¾ç½®â€�>â€œéš�ç§�ä¸Žå®‰å…¨â€�>â€œä¸¤æ­¥éªŒè¯�â€�ä¸­è®¾ç½®ã€‚',
             'loginPasswordTitleWeb' => 'ä¸¤æ­¥éªŒè¯�',
             'loginQr' => 'æ‰«æ��ä»¥ä¸ŠäºŒç»´ç �å�³å�¯è‡ªåŠ¨ç™»å½•ã€‚',
             'loginQrCodeExpired' => 'QR ç �å·²è¿‡æœŸï¼Œæ­£åœ¨ç”Ÿæˆ�æ–°çš„ QR ç �...',
             'loginQrCodeSuccessful' => 'äºŒç»´ç �ç™»å½•æˆ�åŠŸï¼�',
             'loginTitleWeb' => 'ç™»å½• Telegram',
             'loginTypeWeb' => 'ç™»å½•èº«ä»½',
             'loginUser' => 'è¾“å…¥ä½ çš„ç”µè¯�å�·ç �ï¼š ',
             'loginUserCode' => 'è¾“å…¥éªŒè¯�ç �ï¼š ',
             'loginUserPass' => 'è¾“å…¥æ‚¨çš„å¯†ç �ï¼ˆæ��ç¤º %sï¼‰ï¼š ',
             'loginUserPassHint' => 'æ��ç¤ºï¼š%s',
             'loginUserPassWeb' => 'è¾“å…¥æ‚¨çš„å¯†ç �ï¼š ',
             'loginUserPhoneCodeWeb' => 'éªŒè¯�ç �',
             'loginUserPhoneHintWeb' => 'è¯·åŒ…å�«å›½å®¶ä»£ç �ï¼Œä¾‹å¦‚ +39 123 456 7890ã€‚',
             'loginUserPhoneWeb' => 'ç”µè¯�å�·ç �',
             'loginWebQr' => 'æ‚¨ä¹Ÿå�¯ä»¥é€šè¿‡æ‰«æ��ä»¥ä¸‹äºŒç»´ç �è‡ªåŠ¨ç™»å½•ï¼š',
             'loginWebQr1' => 'åœ¨æ‰‹æœºä¸Šæ‰“å¼€ Telegram',
             'loginWebQr2' => 'å‰�å¾€â€œè®¾ç½®â€�>â€œè®¾å¤‡â€�>â€œé“¾æŽ¥æ¡Œé�¢è®¾å¤‡â€�',
             'loginWebQr3' => 'å°†ä½ çš„æ‰‹æœºå¯¹å‡†æ­¤å±�å¹•ä»¥ç¡®è®¤ç™»å½•',
             'login_2fa_enabled' => 'å�¯ç”¨ 2FA å�Žï¼Œæ‚¨å°†å¿…é¡»è°ƒç”¨ complete2falogin å‡½æ•°...',
             'login_auth_key' => 'ä½¿ç”¨èº«ä»½éªŒè¯�å¯†é’¥ç™»å½•...',
             'login_bot' => 'ä»¥æœºå™¨äººèº«ä»½ç™»å½•...',
             'login_code_sending' => 'æ­£åœ¨å�‘é€�ä»£ç �...',
             'login_code_sent' => 'ä»£ç �å�‘é€�æˆ�åŠŸï¼�æ”¶åˆ°ä»£ç �å�Žï¼Œæ‚¨åº”è¯¥ä½¿ç”¨ completePhoneLogin å‡½æ•°ã€‚',
             'login_code_uncalled' => 'æˆ‘ä¸�ç­‰ä»£ç �äº†ï¼�è¯·å…ˆè°ƒç”¨ phoneLogin æ–¹æ³•',
             'login_need_signup' => 'å°šæœªä¸ºæ­¤å�·ç �åˆ›å»ºå¸�æˆ·ï¼Œæ‚¨å¿…é¡»è°ƒç”¨ completeSignup å‡½æ•°...',
             'login_ok' => 'ç™»å½•æˆ�åŠŸï¼�',
             'login_user' => 'ä»¥æ™®é€šç”¨æˆ·èº«ä»½ç™»å½•...',
             'logout_ok' => 'é€€å‡ºæˆ�åŠŸï¼�',
             'long_not_16' => 'ç»™å®šå€¼çš„é•¿åº¦ä¸�æ˜¯ 16 ä¸ªå­—èŠ‚',
             'long_not_32' => 'ç»™å®šå€¼çš„é•¿åº¦ä¸�æ˜¯ 32 ä¸ªå­—èŠ‚',
             'long_not_64' => 'ç»™å®šå€¼çš„é•¿åº¦ä¸�æ˜¯ 64 ä¸ªå­—èŠ‚',
             'madelineproto_ready' => 'MadelineProto å·²å‡†å¤‡å¥½ï¼�',
             'manualAdminActionRequired' => '!!!!!!!!! éœ€è¦�ç³»ç»Ÿç®¡ç�†å‘˜æ‰‹åŠ¨æ“�ä½œ !!!!!!!!!',
             'method_not_found' => 'æ‰¾ä¸�åˆ°æ–¹æ³•ï¼š ',
             'mmapErrorPart1' => 'å·²è¾¾åˆ°å†…å­˜æ˜ å°„ï¼ˆmmapï¼‰åŒºåŸŸçš„æœ€å¤§æ•°é‡�ï¼ˆ%sï¼‰ï¼šè¯·å°† vm.max_map_count å†…æ ¸é…�ç½®å¢žåŠ åˆ° 262144 è¿›è¡Œä¿®å¤�ã€‚',
             'mmapErrorPart2' => 'è¦�ä¿®å¤�æ­¤é—®é¢˜ï¼Œè¯·ä»¥ root èº«ä»½è¿�è¡Œä»¥ä¸‹å‘½ä»¤ï¼š%s',
             'mmapErrorPart3' => 'è¦�åœ¨é‡�å�¯å�Žä¿�ç•™æ›´æ”¹ï¼š%s',
             'mmapErrorPart4' => 'åœ¨ Windows å’Œ WSL ä¸Šï¼Œå¢žåŠ é¡µé�¢æ–‡ä»¶çš„å¤§å°�å�¯èƒ½ä¼šæœ‰æ‰€å¸®åŠ©ï¼›å¦‚æžœé—®é¢˜ä»�ç„¶å­˜åœ¨ï¼Œè¯·åˆ‡æ�¢åˆ°åŽŸç”Ÿ Linuxã€‚',
             'must_have_declare_types' => 'å‡ºäºŽæ€§èƒ½åŽŸå› ï¼Œäº‹ä»¶å¤„ç�†ç¨‹åº�æ–‡ä»¶çš„ç¬¬ä¸€ä¸ªè¯­å�¥å¿…é¡»æ˜¯â€œdeclare(strict_types=1);â€�',
             'nearest_dc' => 'æˆ‘ä»¬åœ¨ %sï¼Œæœ€è¿‘çš„ DC æ˜¯ %dã€‚',
             'need_dl.php' => 'æ— æ³•ç”Ÿæˆ�é»˜è®¤ä¸‹è½½è„šæœ¬ï¼ˆ%sï¼‰ï¼Œè¯·åˆ›å»ºä¸€ä¸ªåŒ…å�«ä»¥ä¸‹å†…å®¹çš„ dl.php æ–‡ä»¶ï¼š%s å¹¶å°†å…¶ URL ä¼ é€’ç»™ getDownloadLink çš„ç¬¬äºŒä¸ªå�‚æ•°',
             'noReportPeers' => 'è­¦å‘Šï¼šæœªè®¾ç½®æŠ¥å‘Šå¯¹ç­‰ä½“ï¼Œè¯·å°†ä»¥ä¸‹æ–¹æ³•æ·»åŠ åˆ°æ‚¨çš„äº‹ä»¶å¤„ç�†ç¨‹åº�ä¸­',
             'non_text_conversion' => 'å°šæ— æ³•è½¬æ�¢é�žæ–‡æœ¬ä¿¡æ�¯ï¼�',
             'not_loggedIn' => 'æˆ‘è¿˜æ²¡æœ‰ç™»å½•ï¼�',
             'not_numeric' => 'ç»™å®šå€¼ä¸�æ˜¯æ•°å­—',
             'params_missing' => 'ç¼ºå°‘å¿…éœ€å�‚æ•°',
             'peer_not_in_db' => 'å†…éƒ¨å¯¹ç­‰æ•°æ�®åº“ä¸­ä¸�å­˜åœ¨æ­¤å¯¹ç­‰ç‚¹',
             'plugin_path_does_not_exist' => 'æ�’ä»¶è·¯å¾„ %s ä¸�å­˜åœ¨ï¼�',
             'plugins_do_not_use_require' => 'å‡ºäºŽæ€§èƒ½åŽŸå› ï¼Œæ�’ä»¶å�ªèƒ½é€šè¿‡è§¦å�‘ PSR-4 è‡ªåŠ¨åŠ è½½å™¨æ�¥è‡ªåŠ¨åŒ…å�«æˆ–éœ€è¦�æ�’ä»¶æ–‡ä»¶å¤¹ä¸­çš„å…¶ä»–æ–‡ä»¶ï¼ˆè€Œä¸�æ˜¯é€šè¿‡æ‰‹åŠ¨ require() å®ƒä»¬ï¼‰ã€‚',
             'plugins_must_have_exactly_one_class' => 'ä¸€ä¸ªæ�’ä»¶å¿…é¡»å®šä¹‰ä¸€ä¸ªç±»ï¼�è¦�å®šä¹‰å¤šä¸ªç±»ã€�æŽ¥å�£æˆ–ç‰¹å¾�ï¼Œè¯·åˆ›å»ºå�•ç‹¬çš„æ–‡ä»¶ï¼Œå®ƒä»¬å°†ç”± MadelineProto è‡ªåŠ¨åŠ è½½ã€‚',
             'predicate_not_set' => 'è°“è¯�ï¼ˆ_ ä¸‹çš„å€¼ï¼‰æœªè®¾ç½®ï¼�',
             'recommend_not_use_filesystem_function' => 'ä¸�å»ºè®®ä½¿ç”¨ %s å‡½æ•°ï¼Œå› ä¸ºåœ¨æ›´æ–°å¤„ç�†æœŸé—´è®¿é—®æ–‡ä»¶ç³»ç»Ÿä¼šå‡�æ…¢æ‚¨çš„æœºå™¨äººé€Ÿåº¦ï¼Œè¯·å�‚é˜… https://docs.madelineproto.xyz/docs/UPDATES.html#avoiding-the-use-of-filesystem-functions ä»¥èŽ·å�–ä¸�ä¼šå‡�æ…¢æ‚¨çš„æœºå™¨äººé€Ÿåº¦çš„æ›¿ä»£æ•°æ�®å­˜å‚¨æ–¹æ³•åˆ—è¡¨ï¼�',
             'rpc_tg_error' => 'Telegram è¿”å›žäº† RPC é”™è¯¯ï¼š%sï¼ˆ%sï¼‰ï¼Œç”± %s:%s%sTL è·Ÿè¸ªå¯¼è‡´ï¼š',
             'sec_peer_not_in_db' => 'å†…éƒ¨å¯¹ç­‰æ•°æ�®åº“ä¸­ä¸�å­˜åœ¨æ­¤ç§˜å¯†å¯¹ç­‰ä½“',
             'secret_chat_skipping' => 'æˆ‘çš„æ•°æ�®åº“ä¸­æ²¡æœ‰ç§˜å¯†è�Šå¤© %sï¼Œè·³è¿‡æ¶ˆæ�¯...',
             'serialization_ofd' => 'åº�åˆ—åŒ–å·²è¿‡æ—¶ï¼Œæ­£åœ¨é‡�å»ºå¯¹è±¡ï¼�',
             'session_corrupted' => 'ä¼šè¯�å·²æ�Ÿå��ï¼�',
             'signing_up' => 'æ­£åœ¨æ³¨å†Œä¸ºæ™®é€šç”¨æˆ·...',
             'signupFirstName' => 'è¾“å…¥æ‚¨çš„å��å­—ï¼š ',
             'signupFirstNameWeb' => 'å��å­—',
             'signupLastName' => 'è¾“å…¥æ‚¨çš„å§“æ°�ï¼ˆå�¯ä»¥ä¸ºç©ºï¼‰ï¼š ',
             'signupLastNameWeb' => 'å§“æ°�',
             'signupLeadWeb' => 'æ­¤ç”µè¯�å�·ç �å°šæœªæ³¨å†Œ Telegramï¼šè¯·ä¸ºæ–°å¸�æˆ·è®¾ç½®å��å­—ã€‚',
             'signupTitleWeb' => 'åˆ›å»ºæ‚¨çš„å¸�æˆ·',
             'signupWeb' => 'è¯·æ³¨å†Œ',
             'signup_ok' => 'æŠ¥å��æˆ�åŠŸï¼�',
             'signup_uncalled' => 'æˆ‘ä¸�ç­‰ç�€æ³¨å†Œï¼�è¯·å…ˆè°ƒç”¨ phoneLogin å’Œ completePhoneLogin æ–¹æ³•ï¼�',
             'src_file_invalid' => 'æ��ä¾›çš„æº�æ–‡ä»¶æ— æ•ˆï¼š ',
             'static_analysis_minor' => 'å¯¹ %s è¿›è¡Œé�™æ€�åˆ†æž�æ—¶é�‡åˆ°ä¸€ä¸ªå°�é—®é¢˜ï¼š%s',
             'static_analysis_severe' => 'å¯¹ %s è¿›è¡Œé�™æ€�åˆ†æž�æ—¶é�‡åˆ°ä¸¥é‡�é—®é¢˜ï¼š%s',
             'stream_handle_invalid' => 'æ��ä¾›äº†æ— æ•ˆçš„æµ�å�¥æŸ„ã€‚',
             'string_required' => 'æœŸæœ›çš„æ˜¯å­—ç¬¦ä¸²ï¼�',
             'translate_madelineproto_cli' => 'MadelineProto å�¯ä»¥ç¿»è¯‘æˆ�æ‚¨çš„è¯­è¨€ï¼ˆå½“å‰�ç¿»è¯‘è¿›åº¦ï¼š%d%%)ï¼Œè¯·è®¿é—® https://github.com/danog/MadelineProto/tree/v8/langs ä¸ºç¿»è¯‘å�šå‡ºè´¡çŒ®ï¼�',
             'translate_madelineproto_web' => 'MadelineProto å�¯ä»¥ç¿»è¯‘æˆ�æ‚¨çš„è¯­è¨€ï¼ˆå½“å‰�ç¿»è¯‘è¿›åº¦ï¼š%d%%ï¼‰ï¼Œç‚¹å‡»<a href="https://github.com/danog/MadelineProto/tree/v8/langs" target="_blank">æ­¤å¤„ä¸ºç¿»è¯‘å�šå‡ºè´¡çŒ®ï¼�</a>',
             'tweb.ACCESS_TOKEN_EXPIRED' => 'æ­¤æœºå™¨äººä»¤ç‰Œå·²è¢«æ’¤é”€',
             'tweb.ACCESS_TOKEN_INVALID' => 'æœºå™¨äººä»¤ç‰Œæ— æ•ˆ',
             'tweb.AccDescr.LoginCode' => 'ç™»å½•ç �',
             'tweb.Close' => 'å…³é—­',
             'tweb.Code' => 'éªŒè¯�ç �',
             'tweb.Contacts.PhoneNumber.Placeholder' => 'ç”µè¯�å�·ç �',
             'tweb.Country' => 'å›½å®¶/åœ°åŒº',
             'tweb.DarkMode' => 'æ·±è‰²æ¨¡å¼�',
             'tweb.Error.AnError' => 'å�‘ç”Ÿé”™è¯¯ï¼Œè¯·ç¨�å�Žé‡�è¯•ã€‚',
             'tweb.FIRSTNAME_INVALID' => 'å��å­—æ— æ•ˆ',
             'tweb.FirstName' => 'å��å­—ï¼ˆå¿…å¡«ï¼‰',
             'tweb.LASTNAME_INVALID' => 'å§“æ°�æ— æ•ˆ',
             'tweb.LastName' => 'å§“æ°�ï¼ˆå�¯é€‰ï¼‰',
             'tweb.Login.Bot' => 'ä»¥æœºå™¨äººèº«ä»½ç™»å½• >',
             'tweb.Login.Bot.Cancel' => 'ä»¥ç”¨æˆ·èº«ä»½ç™»å½• >',
             'tweb.Login.Bot.Subtitle' => 'è¾“å…¥åˆ›å»ºæœºå™¨äººæ—¶
@BotFather æ��ä¾›ç»™æ‚¨çš„ä»¤ç‰Œã€‚',
             'tweb.Login.Bot.Title' => 'ä»¥æœºå™¨äººèº«ä»½ç™»å½•',
             'tweb.Login.Bot.Token' => 'æœºå™¨äººä»¤ç‰Œ',
             'tweb.Login.Code.SecretPhrase' => 'å¯†è¯­çŸ­å�¥',
             'tweb.Login.Code.SecretWord' => 'å¯†è¯­',
             'tweb.Login.Code.SentCall' => 'æˆ‘ä»¬å°†è‡´ç”µæ‚¨
å¹¶å‘ŠçŸ¥éªŒè¯�ç �ã€‚',
             'tweb.Login.Code.SentEmail' => 'è¯·æŸ¥çœ‹æ‚¨çš„é‚®ç®± **%s**ï¼ˆåˆ«å¿˜äº†æŸ¥çœ‹åžƒåœ¾é‚®ä»¶æ–‡ä»¶å¤¹ï¼‰ï¼Œå¹¶è¾“å…¥æˆ‘ä»¬åˆšåˆšå�‘é€�ç»™æ‚¨çš„éªŒè¯�ç �ã€‚',
             'tweb.Login.Code.SentInApp' => 'æˆ‘ä»¬å·²é€šè¿‡ Telegram æ¶ˆæ�¯
å�‘æ‚¨å�‘é€�éªŒè¯�ç �ã€‚',
             'tweb.Login.Code.SentMissedCall' => 'å‡ ç§’é’Ÿå†…æ‚¨å°†æ”¶åˆ°æ�¥è‡ª **%s** çš„
ä¸€ä¸ªçŸ­æš‚æ�¥ç”µã€‚è¯·ä¸�è¦�æŽ¥å�¬â€”â€”
å�ªéœ€è¾“å…¥è¯¥å�·ç �çš„æœ€å�Žå‡ ä½�æ•°å­—ã€‚',
             'tweb.Login.Code.SentSms' => 'æˆ‘ä»¬å·²é€šè¿‡çŸ­ä¿¡
å�‘æ‚¨å�‘é€�éªŒè¯�ç �ã€‚',
             'tweb.Login.Code.SentSmsPhrase' => 'æˆ‘ä»¬å·²å�‘æ‚¨çš„æ‰‹æœº **%s**
å�‘é€�äº†ä¸€æ�¡åŒ…å�«å¯†è¯­çŸ­å�¥çš„çŸ­ä¿¡ã€‚',
             'tweb.Login.Code.SentSmsPhraseBeginning' => 'æˆ‘ä»¬å·²å�‘æ‚¨çš„æ‰‹æœº **%1$s** å�‘é€�äº†ä¸€æ�¡çŸ­ä¿¡ï¼Œ
å…¶ä¸­åŒ…å�«ä»¥â€œ**%2$s**â€�å¼€å¤´çš„å¯†è¯­çŸ­å�¥ã€‚',
             'tweb.Login.Code.SentSmsWord' => 'æˆ‘ä»¬å·²å�‘æ‚¨çš„æ‰‹æœº **%s**
å�‘é€�äº†ä¸€æ�¡åŒ…å�«å¯†è¯­çš„çŸ­ä¿¡ã€‚',
             'tweb.Login.Code.SentSmsWordBeginning' => 'æˆ‘ä»¬å·²å�‘æ‚¨çš„æ‰‹æœº **%1$s** å�‘é€�äº†ä¸€æ�¡çŸ­ä¿¡ï¼Œ
å…¶ä¸­åŒ…å�«ä»¥â€œ**%2$s**â€�å¼€å¤´çš„å¯†è¯­ã€‚',
             'tweb.Login.Code.SentUnknown' => 'è¯·åœ¨å�„å¤„æŸ¥æ‰¾
éªŒè¯�ç �ï¼ˆç±»åž‹ï¼š%sï¼‰',
             'tweb.Login.Code.WordBeginningInvalid' => 'å�•è¯�ä¸�æ­£ç¡®',
             'tweb.Login.Code.WordInvalid' => 'ä¸�æ­£ç¡®ï¼Œè¯·é‡�è¯•',
             'tweb.Login.Next' => 'ä¸‹ä¸€æ­¥',
             'tweb.Login.Password.Subtitle' => 'æ‚¨çš„å¸�æˆ·å�—
é™„åŠ å¯†ç �ä¿�æŠ¤',
             'tweb.Login.Password.Title' => 'è¾“å…¥æ‚¨çš„å¯†ç �',
             'tweb.Login.PhoneLabelInvalid' => 'ç”µè¯�å�·ç �æ— æ•ˆ',
             'tweb.Login.QR.Cancel' => 'ä½¿ç”¨ç”µè¯�å�·ç �ç™»å½• >',
             'tweb.Login.QR.Help1' => 'åœ¨æ‰‹æœºä¸Šæ‰“å¼€ Telegram',
             'tweb.Login.QR.Help2' => 'å‰�å¾€ **è®¾ç½®** > **è®¾å¤‡** > **æ·»åŠ è®¾å¤‡**',
             'tweb.Login.QR.Help3' => 'å°†æ‰‹æœºå¯¹å‡†æ­¤å±�å¹•ä»¥ç¡®è®¤ç™»å½•',
             'tweb.Login.QR.Login' => 'ä½¿ç”¨äºŒç»´ç �ç™»å½• >',
             'tweb.Login.QR.Subtitle' => 'ä½¿ç”¨æ‰‹æœºä¸Šçš„ Telegram åº”ç”¨æ‰«æ��',
             'tweb.Login.QR.Title' => 'ä½¿ç”¨äºŒç»´ç �ç™»å½•',
             'tweb.Login.Register.Subtitle' => 'è¯·è¾“å…¥æ‚¨çš„å��å­—
ä»¥åˆ›å»ºå¸�æˆ·',
             'tweb.Login.StartText' => 'è¯·ç¡®è®¤æ‚¨çš„å›½å®¶ä»£ç �
å¹¶è¾“å…¥æ‚¨çš„ç”µè¯�å�·ç �ã€‚',
             'tweb.Login.Title' => 'ç™»å½• MadelineProto',
             'tweb.LoginPassword' => 'å¯†ç �',
             'tweb.Optional' => 'å�¯é€‰',
             'tweb.PASSWORD_HASH_INVALID' => 'å¯†ç �ä¸�æ­£ç¡®',
             'tweb.PHONE_CODE_EMPTY' => 'éªŒè¯�ç �æ— æ•ˆ',
             'tweb.PHONE_CODE_EXPIRED' => 'éªŒè¯�ç �å·²è¿‡æœŸ',
             'tweb.PHONE_CODE_INVALID' => 'éªŒè¯�ç �æ— æ•ˆ',
             'tweb.PHONE_NUMBER_BANNED' => 'æ­¤ç”µè¯�å�·ç �å·²è¢«å°�ç¦�',
             'tweb.PHONE_NUMBER_FLOOD' => 'å°�è¯•æ¬¡æ•°è¿‡å¤šï¼Œè¯·ç¨�å�Žé‡�è¯•',
             'tweb.PHONE_NUMBER_INVALID' => 'ç”µè¯�å�·ç �æ— æ•ˆ',
             'tweb.PHONE_PASSWORD_FLOOD' => 'å°�è¯•æ¬¡æ•°è¿‡å¤šï¼Œè¯·ç¨�å�Žé‡�è¯•',
             'tweb.PhoneNumber.Code.Fragment.Info' => 'åœ¨ [Fragment]() çš„ Numbers æ �ç›®ä¸­èŽ·å�–éªŒè¯�ç �',
             'tweb.PleaseWait' => 'è¯·ç¨�å€™...',
             'tweb.StartMessaging' => 'å¼€å§‹è�Šå¤©',
             'tweb.YourName' => 'æ‚¨çš„å��å­—',
             'type_extract_error' => 'æ— æ³•æ��å�–ç±»åž‹â€œ%sâ€�ï¼Œæ‚¨åº”è¯¥æ›´æ–° MadelineProtoï¼�',
             'type_extract_error_id' => 'æ— æ³•æ��å�–ç±»åž‹ï¼š%sï¼ŒID ä¸º %sï¼Œæ‚¨åº”è¯¥æ›´æ–° MadelineProtoï¼�',
             'update_madelineproto' => 'æ‚¨æ­£åœ¨è¿�è¡Œæ—§ç‰ˆæœ¬çš„ MadelineProtoï¼Œéœ€è¦�æ›´æ–°ï¼šå½“å‰�è¿�è¡Œ %sï¼Œä½†æœ€æ–°ç‰ˆæœ¬å…·æœ‰å¤šé¡¹é”™è¯¯ä¿®å¤�å’Œæ–°åŠŸèƒ½æ˜¯ %sï¼�',
             'value_bigger_than_2147483647' => 'æ��ä¾›çš„å€¼ %s å¤§äºŽ 2147483647',
             'value_bigger_than_4294967296' => 'æ��ä¾›çš„å€¼ %s å¤§äºŽ 4294967296',
             'value_bigger_than_9223372036854775807' => 'æ��ä¾›çš„å€¼ %s å¤§äºŽ 9223372036854775807',
             'value_smaller_than_0' => 'æ��ä¾›çš„å€¼ %s å°�äºŽ 0',
             'value_smaller_than_2147483648' => 'æ��ä¾›çš„å€¼ %s å°�äºŽ -2147483648',
             'value_smaller_than_9223372036854775808' => 'æ��ä¾›çš„å€¼ %s å°�äºŽ -9223372036854775808',
             'waveform_must_have_100_values' => 'æ³¢å½¢æ•°ç»„å¿…é¡»æœ‰ 100 ä¸ªå€¼ï¼�',
             'waveform_value' => 'æ³¢å½¢å€¼å¿…é¡»ä»‹äºŽ0è‡³31ä¹‹é—´ï¼�',
             'webContinue' => 'ç»§ç»­',
             'webDocs' => 'æ–‡æ¡£',
             'webDoneLead' => 'MadelineProto ä¼šåœ¨æœ�åŠ¡å™¨ä¸Šç»§ç»­è¿�è¡Œï¼šæ‚¨å�¯ä»¥å…³é—­æ­¤é¡µé�¢ã€‚',
             'webError' => '%sã€‚è¯·æ£€æŸ¥å�Žé‡�è¯•ã€‚',
             'webFieldRequired' => 'è¯·å¡«å†™æ­¤å­—æ®µã€‚',
             'webHidePassword' => 'éš�è—�',
             'webLogin' => 'ç™»å½•',
             'webOptional' => 'å�¯é€‰',
             'webPhoneInvalid' => 'è¯·è¾“å…¥åŒ…å�«å›½å®¶ä»£ç �çš„ç”µè¯�å�·ç �ï¼Œä¾‹å¦‚ +39 123 456 7890ã€‚',
             'webProgress' => 'è®¾ç½®è¿›åº¦',
             'webQrTitle' => 'ä½¿ç”¨äºŒç»´ç �ç™»å½•',
             'webQrWaiting' => 'ç­‰å¾…æ‰«æ��â€¦',
             'webShowPassword' => 'æ˜¾ç¤º',
             'webStepAccount' => 'å¸�æˆ·',
             'webStepApp' => 'API ID å’Œ hash',
             'webStepDone' => 'è¿�è¡Œä¸­',
             'webStepVerify' => 'éªŒè¯�',
         ],
    ];

    // THIS WILL BE OVERWRITTEN BY $lang["en"]
    /** @var array<string, string> */
    public static array $current_lang =  [
        '2fa_uncalled' => 'I\'m not waiting for the password! Please call the phoneLogin and the completePhoneLogin methods first!',
        'accepting_call' => 'Accepting call from %s...',
        'account_banned' => '!!!!!!! WARNING !!!!!!!
Telegram\'s flood prevention system suspended this account.
To continue, manual verification is required.
Send an email to recover@telegram.org, asking to unban the phone number %s, and shortly describe what will you do with this phone number.
Then login again.
If you intentionally deleted this account, ignore this message.',
        'already_loggedIn' => 'This instance of MadelineProto is already logged in!',
        'apiAppDescLabelWeb' => 'Description',
        'apiAppInstructionsAuto0' => 'Enter the app\'s name, can be anything: ',
        'apiAppInstructionsAuto1' => 'Enter the app\'s short name, alphanumeric, 5-32 characters: ',
        'apiAppInstructionsAuto2' => 'Enter the app/website\'s URL, or t.me/yourusername: ',
        'apiAppInstructionsAuto3' => 'Enter the app platform: ',
        'apiAppInstructionsAuto4' => 'Describe your app: ',
        'apiAppInstructionsAutoTypeOther' => 'Other (specify in description)',
        'apiAppInstructionsManual0' => 'your app\'s name, can be anything',
        'apiAppInstructionsManual1' => 'your app\'s short name, alphanumeric, 5-32 characters',
        'apiAppInstructionsManual2' => 'your app/website\'s URL, or t.me/yourusername',
        'apiAppInstructionsManual3' => 'anything',
        'apiAppInstructionsManual4' => 'Describe your app here',
        'apiAppShortNameLabelWeb' => 'Short name',
        'apiAppTitleLabelWeb' => 'App title',
        'apiAppUrlLabelWeb' => 'URL',
        'apiAppWeb' => 'Enter API information',
        'apiAutoCodeLabelWeb' => 'Login code',
        'apiAutoCodeLeadWeb' => 'my.telegram.org sent a login code to your Telegram app, in the chat with Telegram (not by SMS).',
        'apiAutoCodeTitleWeb' => 'Enter the code from Telegram',
        'apiAutoErrorTitle' => 'my.telegram.org returned an error',
        'apiAutoFallbackManual' => 'Enter the API ID and hash manually instead.',
        'apiAutoPhoneHintWeb' => 'my.telegram.org sends the login code to your Telegram app, not by SMS.',
        'apiAutoPrompt0' => 'Enter a phone number that is already registered on Telegram: ',
        'apiAutoPrompt1' => 'Enter the verification code you received in Telegram: ',
        'apiAutoWeb' => 'Enter a phone number that is <b>already registered</b> on telegram to get the API ID',
        'apiChooseAutoWeb' => 'Get them automatically',
        'apiChooseManualAuto' => 'Do you want to get the API ID and hash automatically from my.telegram.org (a), or enter them manually (m)?',
        'apiChooseManualAutoTip' => 'Note that you can also provide the API ID/hash directly in the code using the settings: %s',
        'apiChooseManualAutoTipWeb' => 'Note that you can also provide the API ID/hash directly in the code using the <a target="_blank" href="%s">settings</a>.',
        'apiChooseManualWeb' => 'I already have an API ID',
        'apiChoosePrompt' => 'Your choice (m/a): ',
        'apiCreateErrorTitle' => 'my.telegram.org didn\'t create the app',
        'apiCreateLeadWeb' => 'This account has no app on my.telegram.org yet. These details are only shown to you, and can be changed later.',
        'apiCreateTitleWeb' => 'Create your app',
        'apiCreateVpsWarning' => 'Telegram doesn\'t let VPS, VPN and other datacenter IP ranges create new apps on my.telegram.org, and the request was made from the IP of the machine running MadelineProto. Create the app manually at https://my.telegram.org from a residential connection (like your phone on mobile data), then enter the API ID and hash.',
        'apiCreateWeb' => 'Create app',
        'apiError' => 'ERROR: %s. Try again.',
        'apiInvalidWeb' => 'The API ID is a number, and the API hash is 32 characters long: copy them again from my.telegram.org.',
        'apiManualInstructions0' => 'Login to https://my.telegram.org',
        'apiManualInstructions1' => 'Go to API development tools',
        'apiManualInstructions2' => 'Click on create application',
        'apiManualPrompt0' => 'Enter your API ID: ',
        'apiManualPrompt1' => 'Enter your API hash: ',
        'apiManualWeb' => 'Enter your API ID and API hash',
        'apiParamsError' => 'You didn\'t provide all of the required parameters!',
        'apiSaveWeb' => 'Save and continue',
        'apiSendCodeWeb' => 'Send code',
        'apiSetupLeadWeb' => 'Every Telegram app needs an API ID and hash. MadelineProto can get them for you from my.telegram.org, with the phone number of an existing Telegram account.',
        'apiSetupTitleWeb' => 'Get your API ID and hash',
        'apiStartOverWeb' => 'Start over',
        'api_not_set' => 'You must provide an api key and an api id, get your own @ my.telegram.org',
        'array_invalid' => 'You didn\'t provide a valid array',
        'baseDirLimitation' => 'A basedir limitation is configured: this can impact performance and cause some issues, please disable it if possible!',
        'bool_error' => 'Could not extract boolean',
        'botAlreadyRunning' => 'The bot is already running!',
        'botapi_conversion_error' => 'Can\'t convert %s to a bot API object',
        'call_already_accepted' => 'Call %s already accepted',
        'call_already_declined' => 'Call %s already declined',
        'call_completing' => 'Completing call from %s...',
        'call_confirming' => 'Confirming call from %s...',
        'call_discarding' => 'Discarding call %s...',
        'call_error_1' => 'Could not find and accept call %s',
        'call_error_2' => 'Could not find and confirm call %s',
        'call_error_3' => 'Could not find and complete call %s',
        'cli_need_dl.php_link' => 'Please specify a download script URL when using getDownloadLink via CLI!',
        'constructor_not_found' => 'Constructor not found for type: ',
        'could_not_connect_to_MadelineProto' => 'Could not connect to MadelineProto, please enable proc_open and remove open_basedir restrictions or disable webserver path rewrites to fix! If you already did that, make sure the CLI version of PHP is exactly the same as the web version (same version, extensions, et cetera) and check out the MadelineProto.log file for more info about the error that prevented the IPC server from starting.',
        'could_not_convert_object' => 'Could not convert object of type %s',
        'deserialization_error' => 'An error occurred on deserialization',
        'dl.php_check_logs_make_sure_session_running' => 'Either the associated MadelineProto EventHandler bot or the MadelineProto IPC server are offline, please check logs and make sure at least one of them is running!',
        'dl.php_powered_by_madelineproto' => 'Telegram file download server (up to 4GB), powered by <a href="https://docs.madelineproto.xyz" target="_blank">MadelineProto</a>!<br>Click <a href="https://docs.madelineproto.xyz/docs/FILES.html#getting-a-download-link" target="_blank">here</a> for more info on how to setup your very own Telegram file download server!',
        'do_not_delete_MadelineProto.log' => 'the MadelineProto.log file must never be deleted, please set a custom max size in the settings, instead!',
        'do_not_remove_MadelineProto.log_phar' => 'Please do not remove madeline.phar or madeline.php, or else MadelineProto will crash. If you have any problem with MadelineProto, report it to https://github.com/danog/MadelineProto or https://t.me/pwrtelegramgroup',
        'do_not_use_blocking_class' => 'for performance reasons, handlers may not use the non-async blocking class %s, please use %s, instead',
        'do_not_use_blocking_function' => 'for performance reasons, event handlers may not use the non-async blocking function %s, please use %s, instead',
        'do_not_use_deprecated_function' => 'the %s function is deprecated, please use %s, instead',
        'do_not_use_non_root_require_in_event_handler' => 'for performance reasons, you must not use require or include inside of an event handler class, only root-level requires are allowed.',
        'do_not_use_yield' => 'MadelineProto 8 does not require or support the use of yield in async functions, you must remove all yield keywords previously used for async function calls',
        'done' => 'Done!',
        'encode_double_error' => 'Could not properly encode double',
        'extensionRecommended' => 'Warning: the %s extension is not installed, please install it to speed up MadelineProto!',
        'extensionRequired' => 'MadelineProto requires the %s extension to run. %s',
        'extensionRequiredInstallWithApt' => 'Try running sudo apt-get install %s.',
        'extensionRequiredInstallWithCustomInstructions' => 'Follow the instructions at %s to install it.',
        'file_not_exist' => 'File does not exist',
        'file_parsing' => 'Parsing %s...',
        'file_type_invalid' => 'Invalid file type detected (%s)',
        'fingerprint_invalid' => 'Invalid key fingerprint!',
        'go' => 'Go',
        'invalid_dl.php_session' => '%s is not a valid download script because its session ID is different (expected %s, got %s)',
        'ipc_tcp_warning' => 'UNIX sockets are not available, so the IPC server of the session listens on TCP (127.0.0.1) instead: any program running on this machine can connect to it, and control the session. On Windows 10 1803+, enable the sockets extension (extension=sockets in php.ini) to use UNIX sockets.',
        'length_too_big' => 'Length is too big',
        'loginBot' => 'Enter your bot token: ',
        'loginBotTokenHintWeb' => 'The token @BotFather gave you when you created the bot.',
        'loginBotTokenWeb' => 'Bot token',
        'loginChoosePromptWeb' => 'Do you want to login as a user or as a bot?',
        'loginCodeLeadWeb' => 'Telegram sent a login code to your Telegram app, or by SMS if you aren\'t logged in anywhere else.',
        'loginCodeTitleWeb' => 'Enter the login code',
        'loginLeadWeb' => 'Choose the account or the bot this MadelineProto instance will run as.',
        'loginManual' => 'Alternatively, you can also enter a bot token or phone number to login manually: ',
        'loginNoCode' => 'You didn\'t provide a phone code!',
        'loginNoName' => 'You didn\'t provide the first name!',
        'loginNoPass' => 'You didn\'t provide the password!',
        'loginOptionBot' => 'Bot',
        'loginOptionUser' => 'User',
        'loginPasswordLabelWeb' => 'Password',
        'loginPasswordLeadWeb' => 'This account is protected by a password, set in Settings > Privacy and Security > Two-Step Verification.',
        'loginPasswordTitleWeb' => 'Two-step verification',
        'loginQr' => 'Open Telegram on your phone, go to Settings > Devices > Link Desktop Device and scan the above QR code to login automatically.',
        'loginQrCodeExpired' => 'The QR code expired, generating a new one...',
        'loginQrCodeSuccessful' => 'QR code login successful!',
        'loginTitleWeb' => 'Log in to Telegram',
        'loginTypeWeb' => 'Log in as',
        'loginUser' => 'Enter your phone number: ',
        'loginUserCode' => 'Enter the code: ',
        'loginUserPass' => 'Enter your password (hint %s): ',
        'loginUserPassHint' => 'Hint: %s',
        'loginUserPassWeb' => 'Enter your password: ',
        'loginUserPhoneCodeWeb' => 'Code',
        'loginUserPhoneHintWeb' => 'With the country code, like +39 123 456 7890.',
        'loginUserPhoneWeb' => 'Phone number',
        'loginWebQr' => 'You can also login automatically by opening Telegram on your phone, going to Settings > Devices > Link Desktop Device and scanning the following QR code:',
        'loginWebQr1' => 'Open Telegram on your phone',
        'loginWebQr2' => 'Go to Settings > Devices > Link Desktop Device',
        'loginWebQr3' => 'Point your phone at this screen to confirm login',
        'login_2fa_enabled' => '2FA enabled, you will have to call the complete2falogin function...',
        'login_auth_key' => 'Logging in using auth key...',
        'login_bot' => 'Logging in as a bot...',
        'login_code_sending' => 'Sending code...',
        'login_code_sent' => 'Code sent successfully! Once you receive the code you should use the completePhoneLogin function.',
        'login_code_uncalled' => 'I\'m not waiting for the code! Please call the phoneLogin method first',
        'login_need_signup' => 'An account has not been created for this number, you will have to call the completeSignup function...',
        'login_ok' => 'Logged in successfully!',
        'login_user' => 'Logging in as a normal user...',
        'logout_ok' => 'Logged out successfully!',
        'long_not_16' => 'Given value is not 16 bytes long',
        'long_not_32' => 'Given value is not 32 bytes long',
        'long_not_64' => 'Given value is not 64 bytes long',
        'madelineproto_ready' => 'MadelineProto is ready!',
        'manualAdminActionRequired' => '!!!!!!!!! MANUAL SYSTEM ADMIN ACTION REQUIRED !!!!!!!!!',
        'method_not_found' => 'Could not find method: ',
        'mmapErrorPart1' => 'The maximum number of memory mapped (mmap) regions was reached (%s): please increase the vm.max_map_count kernel config to 262144 to fix.',
        'mmapErrorPart2' => 'To fix, run the following command as root: %s',
        'mmapErrorPart3' => 'To persist the change across reboots: %s',
        'mmapErrorPart4' => 'On Windows and WSL, increasing the size of the pagefile might help; please switch to native Linux if the issue persists.',
        'must_have_declare_types' => 'for performance reasons, the first statement of an event handler file must be "declare(strict_types=1);"',
        'nearest_dc' => 'We\'re in %s, nearest DC is %d.',
        'need_dl.php' => 'Could not generate default download script (%s), please create a dl.php file with the following content: %s and pass its URL to the second parameter of getDownloadLink',
        'noReportPeers' => 'Warning: no report peers are set, please add the following method to your event handler',
        'non_text_conversion' => 'Can\'t convert non text messages yet!',
        'not_loggedIn' => 'I\'m not logged in!',
        'not_numeric' => 'Given value isn\'t numeric',
        'params_missing' => 'Missing required parameter',
        'peer_not_in_db' => 'This peer is not present in the internal peer database',
        'plugin_path_does_not_exist' => 'Plugin path %s does not exist!',
        'plugins_do_not_use_require' => 'for performance reasons, plugins can only automatically include or require other files present in the plugins folder by triggering the PSR-4 autoloader (not by manually require()\'ing them).',
        'plugins_must_have_exactly_one_class' => 'a plugin must define exactly one class! To define multiple classes, interfaces or traits, create separate files, they will be autoloaded by MadelineProto automatically.',
        'predicate_not_set' => 'Predicate (value under _) was not set!',
        'recommend_not_use_filesystem_function' => 'usage of the %s function is not recommended, because accessing the filesystem during update handling will slow down your bot, please see https://docs.madelineproto.xyz/docs/UPDATES.html#avoiding-the-use-of-filesystem-functions for a list of alternative ways to store data that will not slow down your bot!',
        'rpc_tg_error' => 'Telegram returned an RPC error: %s (%s), caused by %s:%s%sTL trace:',
        'sec_peer_not_in_db' => 'This secret peer is not present in the internal peer database',
        'secret_chat_skipping' => 'I do not have the secret chat %s in the database, skipping message...',
        'serialization_ofd' => 'Serialization is out of date, reconstructing object!',
        'session_corrupted' => 'The session is corrupted!',
        'signing_up' => 'Signing up as a normal user...',
        'signupFirstName' => 'Enter your first name: ',
        'signupFirstNameWeb' => 'First name',
        'signupLastName' => 'Enter your last name (can be empty): ',
        'signupLastNameWeb' => 'Last name',
        'signupLeadWeb' => 'This phone number isn\'t on Telegram yet: choose the name of the new account.',
        'signupTitleWeb' => 'Create your account',
        'signupWeb' => 'Sign up please',
        'signup_ok' => 'Signed up in successfully!',
        'signup_uncalled' => 'I\'m not waiting to signup! Please call the phoneLogin and the completePhoneLogin methods first!',
        'src_file_invalid' => 'Invalid source file was provided: ',
        'static_analysis_minor' => 'A minor issue was encountered during static analysis of %s: %s',
        'static_analysis_severe' => 'A severe issue was encountered during static analysis of %s: %s',
        'stream_handle_invalid' => 'An invalid stream handle was provided.',
        'string_required' => 'A string was expected!',
        'translate_madelineproto_cli' => 'MadelineProto can be translated in your language (current translation progress: %d%%), go to https://github.com/danog/MadelineProto/tree/v8/langs to contribute with the translation!',
        'translate_madelineproto_web' => 'MadelineProto can be translated in your language (current translation progress: %d%%), click <a href="https://github.com/danog/MadelineProto/tree/v8/langs" target="_blank">here to contribute with the translation!</a>',
        'tweb.ACCESS_TOKEN_EXPIRED' => 'This bot token was revoked',
        'tweb.ACCESS_TOKEN_INVALID' => 'Invalid bot token',
        'tweb.AccDescr.LoginCode' => 'Login code',
        'tweb.Close' => 'Close',
        'tweb.Code' => 'Code',
        'tweb.Contacts.PhoneNumber.Placeholder' => 'Phone Number',
        'tweb.Country' => 'Country',
        'tweb.DarkMode' => 'Dark Mode',
        'tweb.Error.AnError' => 'An error occurred. Please try again later.',
        'tweb.FIRSTNAME_INVALID' => 'Invalid first name',
        'tweb.FirstName' => 'First name (required)',
        'tweb.LASTNAME_INVALID' => 'Invalid last name',
        'tweb.LastName' => 'Last name (optional)',
        'tweb.Login.Bot' => 'Log in as a bot >',
        'tweb.Login.Bot.Cancel' => 'Log in as a user >',
        'tweb.Login.Bot.Subtitle' => 'Enter the token @BotFather gave you
when you created the bot.',
        'tweb.Login.Bot.Title' => 'Log in as a Bot',
        'tweb.Login.Bot.Token' => 'Bot Token',
        'tweb.Login.Code.SecretPhrase' => 'Secret phrase',
        'tweb.Login.Code.SecretWord' => 'Secret word',
        'tweb.Login.Code.SentCall' => 'We will call you and dictate
the code.',
        'tweb.Login.Code.SentEmail' => 'Please check your email **%s** (don\'t forget the spam folder) and enter the code we just sent you.',
        'tweb.Login.Code.SentInApp' => 'We have sent you a message in Telegram
with the code.',
        'tweb.Login.Code.SentMissedCall' => 'Within the next few seconds you should receive
a short call from **%s**. Don\'t answer it â€”
just enter the last digits of that number.',
        'tweb.Login.Code.SentSms' => 'We have sent you an SMS
with the code.',
        'tweb.Login.Code.SentSmsPhrase' => 'We\'ve sent an SMS with a secret phrase
to your phone **%s**.',
        'tweb.Login.Code.SentSmsPhraseBeginning' => 'We\'ve sent an SMS with a secret phrase that starts
with "**%2$s**" to your phone **%1$s**.',
        'tweb.Login.Code.SentSmsWord' => 'We\'ve sent an SMS with a secret word
to your phone **%s**.',
        'tweb.Login.Code.SentSmsWordBeginning' => 'We\'ve sent an SMS with a secret word that starts
with "**%2$s**" to your phone **%1$s**.',
        'tweb.Login.Code.SentUnknown' => 'Please check everything
for a code (type: %s)',
        'tweb.Login.Code.WordBeginningInvalid' => 'Incorrect word',
        'tweb.Login.Code.WordInvalid' => 'Incorrect, please try again',
        'tweb.Login.Next' => 'Next',
        'tweb.Login.Password.Subtitle' => 'Your account is protected with
an additional password',
        'tweb.Login.Password.Title' => 'Enter Your Password',
        'tweb.Login.PhoneLabelInvalid' => 'Phone Number Invalid',
        'tweb.Login.QR.Cancel' => 'Log in by phone number >',
        'tweb.Login.QR.Help1' => 'Open Telegram on your phone',
        'tweb.Login.QR.Help2' => 'Go to **Settings** > **Devices** > **Add Device**',
        'tweb.Login.QR.Help3' => 'Point your phone at this screen to confirm login',
        'tweb.Login.QR.Login' => 'Log in by QR Code >',
        'tweb.Login.QR.Subtitle' => 'Scan with Telegram app on your phone',
        'tweb.Login.QR.Title' => 'Log in by QR Code',
        'tweb.Login.Register.Subtitle' => 'Enter your name
to create your account',
        'tweb.Login.StartText' => 'Please confirm your country code
and enter your phone number.',
        'tweb.Login.Title' => 'Sign in to MadelineProto',
        'tweb.LoginPassword' => 'Password',
        'tweb.Optional' => 'optional',
        'tweb.PASSWORD_HASH_INVALID' => 'Incorrect password',
        'tweb.PHONE_CODE_EMPTY' => 'Invalid code',
        'tweb.PHONE_CODE_EXPIRED' => 'Code expired',
        'tweb.PHONE_CODE_INVALID' => 'Invalid code',
        'tweb.PHONE_NUMBER_BANNED' => 'This phone number is banned',
        'tweb.PHONE_NUMBER_FLOOD' => 'Too many attempts, try again later',
        'tweb.PHONE_NUMBER_INVALID' => 'Phone Number Invalid',
        'tweb.PHONE_PASSWORD_FLOOD' => 'Too many attempts, try again later',
        'tweb.PhoneNumber.Code.Fragment.Info' => 'Get the code in the Numbers section on [Fragment]()',
        'tweb.PleaseWait' => 'Please wait...',
        'tweb.StartMessaging' => 'Start Messaging',
        'tweb.YourName' => 'Your Name',
        'type_extract_error' => 'Could not extract type "%s", you should update MadelineProto!',
        'type_extract_error_id' => 'Could not extract type: %s with id %s, you should update MadelineProto!',
        'update_madelineproto' => 'You\'re running an old version of MadelineProto, an update is required: currently running %s, but the latest version with multiple bugfixes and new features is %s!',
        'value_bigger_than_2147483647' => 'Provided value %s is bigger than 2147483647',
        'value_bigger_than_4294967296' => 'Provided value %s is bigger than 4294967296',
        'value_bigger_than_9223372036854775807' => 'Provided value %s is bigger than 9223372036854775807',
        'value_smaller_than_0' => 'Provided value %s is smaller than 0',
        'value_smaller_than_2147483648' => 'Provided value %s is smaller than -2147483648',
        'value_smaller_than_9223372036854775808' => 'Provided value %s is smaller than -9223372036854775808',
        'waveform_must_have_100_values' => 'A waveform array must have 100 values!',
        'waveform_value' => 'A waveform value must be between 0 and 31!',
        'webContinue' => 'Continue',
        'webDocs' => 'Documentation',
        'webDoneLead' => 'MadelineProto keeps running on the server: you can close this page.',
        'webError' => '%s. Check it and try again.',
        'webFieldRequired' => 'Fill in this field.',
        'webHidePassword' => 'Hide',
        'webLogin' => 'Log in',
        'webOptional' => 'optional',
        'webPhoneInvalid' => 'Enter the phone number with its country code, like +39 123 456 7890.',
        'webProgress' => 'Setup progress',
        'webQrTitle' => 'Log in with a QR code',
        'webQrWaiting' => 'Waiting for the scanâ€¦',
        'webShowPassword' => 'Show',
        'webStepAccount' => 'Account',
        'webStepApp' => 'API ID and hash',
        'webStepDone' => 'Running',
        'webStepVerify' => 'Verification',
    ];
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use Amp\ByteStream\WritableStream;
use InvalidArgumentException;

/**
 * Container format of a call recording, as passed to {@see Call::setOutput()} and {@see Call::setOutputFolder()}.
 *
 * {@see self::Webm} and {@see self::Mkv} both mux the incoming audio and video into a Matroska
 * file in pure PHP (the peer's frames are stored as-is, so the video track is whatever codec the
 * peer sends â€” VP8/VP9/H.264/AV1 â€” and the audio is OPUS); they differ only in the declared EBML
 * DocType. {@see self::Opus} keeps the audio-only behaviour, writing an OGG OPUS stream.
 *
 * When no format is passed to {@see Call::setOutput()}, it is autodetected from the extension of the
 * target â€” but only if a {@see LocalFile} was given; a raw stream, whose extension is unknown,
 * defaults to {@see self::Webm}. A {@see Call::setOutputFolder()} recording defaults to {@see self::Mkv}.
 */
enum RecordingFormat
{
    /** Matroska container with the `webm` DocType. */
    case Webm;
    /** Matroska container with the `matroska` DocType. */
    case Mkv;
    /** OGG OPUS audio-only stream. */
    case Opus;

    /**
     * Autodetect the recording format from a file's extension: `.mkv` and `.webm` map to the
     * matching Matroska format, and anything else (including an unknown extension) to {@see self::Opus}.
     *
     * @psalm-pure
     */
    public static function fromFile(LocalFile $file): self
    {
        return match (strtolower(pathinfo($file->file, PATHINFO_EXTENSION))) {
            'mkv' => self::Mkv,
            'webm' => self::Webm,
            default => self::Opus,
        };
    }

    /**
     * Resolve the format of a multi-party call recording, which is always Matroska: the given one, or
     * else {@see self::Webm} for a `.webm` {@see LocalFile} and {@see self::Mkv} for anything else.
     *
     * @throws InvalidArgumentException If {@see self::Opus} was requested.
     *
     * @psalm-pure
     */
    public static function matroskaFor(LocalFile|WritableStream $file, ?self $format): self
    {
        $format ??= $file instanceof LocalFile && self::fromFile($file) === self::Webm ? self::Webm : self::Mkv;
        if (!$format->isMatroska()) {
            throw new InvalidArgumentException('Multi-party call recordings are always Matroska: use RecordingFormat::Mkv or RecordingFormat::Webm.');
        }
        return $format;
    }

    /**
     * Whether this format muxes video (and audio) into a Matroska container, rather than audio-only OGG.
     *
     * @psalm-mutation-free
     */
    public function isMatroska(): bool
    {
        return $this !== self::Opus;
    }

    /**
     * The EBML DocType to declare in the Matroska header for this format.
     *
     * @psalm-mutation-free
     */
    public function docType(): string
    {
        return match ($this) {
            self::Webm => 'webm',
            self::Mkv => 'matroska',
            self::Opus => throw new \LogicException('OPUS is not a Matroska format'),
        };
    }
}
<?php

declare(strict_types=1);

/**
 * DataCenter module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use Amp\Dns\DnsResolver;
use Amp\Http\Client\HttpClient;
use Amp\Socket\ConnectContext;
use Amp\Socket\InternetAddress;
use Amp\Socket\InternetAddressVersion;
use Amp\Sync\LocalKeyedMutex;
use AssertionError;
use danog\MadelineProto\Stream\Common\BufferedRawStream;
use danog\MadelineProto\Stream\Common\UdpBufferedStream;
use danog\MadelineProto\Stream\ConnectionContext;
use danog\MadelineProto\Stream\ContextIterator;
use danog\MadelineProto\Stream\MTProtoTransport\AbridgedStream;
use danog\MadelineProto\Stream\MTProtoTransport\FakeTlsStream;
use danog\MadelineProto\Stream\MTProtoTransport\FullStream;
use danog\MadelineProto\Stream\MTProtoTransport\HttpsStream;
use danog\MadelineProto\Stream\MTProtoTransport\HttpStream;
use danog\MadelineProto\Stream\MTProtoTransport\IntermediatePaddedStream;
use danog\MadelineProto\Stream\MTProtoTransport\IntermediateStream;
use danog\MadelineProto\Stream\MTProtoTransport\ObfuscatedStream;
use danog\MadelineProto\Stream\Transport\DefaultStream;
use danog\MadelineProto\Stream\Transport\WssStream;
use danog\MadelineProto\Stream\Transport\WsStream;
use Revolt\EventLoop;

/**
 * @psalm-type TDcOption=array{
 *      _: 'dcOption',
 *      cdn: bool,
 *      this_port_only: bool,
 *      tcpo_only: bool,
 *      ip_address: string,
 *      port: int,
 *      secret?: string
 * }
 * @psalm-type TDcList=array{
 *      test: array{
 *          ipv4: non-empty-array<int, TDcOption>,
 *          ipv6: non-empty-array<int, TDcOption>,
 *      },
 *      main: array{
 *          ipv4: non-empty-array<int, TDcOption>,
 *          ipv6: non-empty-array<int, TDcOption>,
 *      },
 * }
 * @internal Manages datacenters.
 */
final class DataCenter
{
    /**
     * @deprecated
     * @var array<int, DataCenterConnection>
     */
    private array $sockets = [];

    /**
     * All socket connections to DCs.
     *
     * @var array<int, DataCenterConnection>
     */
    private array $list = [];
    /**
     * Current DC ID.
     */
    public int $currentDatacenter = 1;
    private DoHWrapper $dohWrapper;

    private LocalKeyedMutex $connectMutex;
    /**
     * Constructor function.
     */
    public function __construct(private readonly MTProto $API)
    {
        $this->__wakeup();
        $this->applySettings();
    }

    /**
     * @psalm-pure
     */
    public function __sleep()
    {
        return ['API', 'list', 'currentDatacenter'];
    }

    /**
     * Runs during unserialize(), before MTProto itself is woken up: must not connect or log.
     */
    public function __wakeup(): void
    {
        $this->connectMutex = new LocalKeyedMutex;
        $this->dohWrapper = new DoHWrapper($this->API);
    }

    /**
     * Apply changed connection settings to the existing connections.
     *
     * @internal
     */
    public function applySettings(): void
    {
        if (!$this->getSettings()->hasChanged()) {
            return;
        }
        $this->dohWrapper = new DoHWrapper($this->API);
        foreach ($this->list as $dc => $socket) {
            if (\is_string($dc)) {
                continue;
            }
            $socket->setCtx($this->generateContexts($dc));
            $socket->reconnect();
        }
        $this->getSettings()->applyChanges();
    }
    /**
     * @return array<int, DataCenterConnection>
     */
    public function getLegacy(): array
    {
        return $this->sockets;
    }

    /**
     * @psalm-pure
     */
    public static function isTest(int $dc): bool
    {
        return abs($dc) > 10000;
    }
    /**
     * @psalm-pure
     */
    public static function isMedia(int $dc): bool
    {
        return $dc < 0;
    }
    /**
     * @psalm-mutation-free
     */
    private function getSettings(): \danog\MadelineProto\Settings\Connection
    {
        return $this->API->getSettings()->getConnection();
    }
    /**
     * @psalm-mutation-free
     */
    public function getHTTPClient(): HttpClient
    {
        return $this->dohWrapper->HTTPClient;
    }

    /**
     * @psalm-mutation-free
     */
    public function getDNSClient(): DnsResolver
    {
        return $this->dohWrapper->DoHClient;
    }

    /**
     * Normalizes "bindto" options to add a ":0" in case no port is present, otherwise PHP will silently ignore those.
     *
     * @throws \Error If an invalid option has been passed.
     *
     * @internal
     *
     * @psalm-pure
     */
    private static function normalizeBindToOption(?string $bindTo = null): ?string
    {
        if ($bindTo === null) {
            return null;
        }

        if (preg_match("/\\[(?P<ip>[0-9a-f:]+)](:(?P<port>\\d+))?$/", $bindTo, $match)) {
            $ip = $match['ip'];
            $port = (int) ($match['port'] ?? 0);

            if (inet_pton($ip) === false) {
                throw new \Error("Invalid IPv6 address: $ip");
            }

            if ($port < 0 || $port > 65535) {
                throw new \Error("Invalid port: $port");
            }

            return "[$ip]:$port";
        }

        if (preg_match("/(?P<ip>\\d+\\.\\d+\\.\\d+\\.\\d+)(:(?P<port>\\d+))?$/", $bindTo, $match)) {
            $ip = $match['ip'];
            $port = (int) ($match['port'] ?? 0);

            if (inet_pton($ip) === false) {
                throw new \Error("Invalid IPv4 address: $ip");
            }

            if ($port < 0 || $port > 65535) {
                throw new \Error("Invalid port: $port");
            }

            return "$ip:$port";
        }

        throw new \Error("Invalid bindTo value: $bindTo");
    }

    /**
     * Generate contexts.
     *
     * @param integer $dc_number DC ID to generate contexts for
     */
    private function generateContexts(int $dc_number): ContextIterator
    {
        $settings = $this->getSettings();
        $test = $settings->getTestMode() ? 'test' : 'main';
        $ipv6 = $settings->getIpv6() ? 'ipv6' : 'ipv4';
        if (!isset($this->API->dcList[$test][$ipv6][$dc_number])) {
            throw new AssertionError("No info for DC $dc_number!");
        }

        $ctxs = [];
        $combos = [];
        $default = match ($settings->getProtocol()) {
            AbridgedStream::class =>
                [[DefaultStream::class, []], [BufferedRawStream::class, []], [AbridgedStream::class, []]],
            IntermediateStream::class =>
                [[DefaultStream::class, []], [BufferedRawStream::class, []], [IntermediateStream::class, []]],
            IntermediatePaddedStream::class =>
                [[DefaultStream::class, []], [BufferedRawStream::class, []], [IntermediatePaddedStream::class, []]],
            FullStream::class =>
                [[DefaultStream::class, []], [BufferedRawStream::class, []], [FullStream::class, []]],
            HttpStream::class =>
                [[DefaultStream::class, []], [BufferedRawStream::class, []], [HttpStream::class, []]],
            HttpsStream::class =>
                [[DefaultStream::class, []], [BufferedRawStream::class, []], [HttpsStream::class, []]],
            UdpBufferedStream::class =>
                [[DefaultStream::class, []], [UdpBufferedStream::class, []]],
        };
        if ($settings->getObfuscated() && !\in_array($default[2][0], [HttpsStream::class, HttpStream::class], true)) {
            $default = [[DefaultStream::class, []], [BufferedRawStream::class, []], [ObfuscatedStream::class, []], end($default)];
        }
        if (!\in_array($default[2][0], [HttpsStream::class, HttpStream::class], true)) {
            switch ($settings->getTransport()) {
                case DefaultStream::class:
                    if ($settings->getObfuscated()) {
                        $default = [[DefaultStream::class, []], [BufferedRawStream::class, []], [ObfuscatedStream::class, []], end($default)];
                    }
                    break;
                case WssStream::class:
                    $default = [[DefaultStream::class, []], [WssStream::class, []], [BufferedRawStream::class, []], [ObfuscatedStream::class, []], end($default)];
                    break;
                case WsStream::class:
                    $default = [[DefaultStream::class, []], [WsStream::class, []], [BufferedRawStream::class, []], [ObfuscatedStream::class, []], end($default)];
                    break;
            }
        }
        $combos[] = $default;

        $only = $this->API->dcList[$test][$ipv6][$dc_number]['tcpo_only'];
        if ($only || isset($this->API->dcList[$test][$ipv6][$dc_number]['secret'])) {
            $extra = isset($this->API->dcList[$test][$ipv6][$dc_number]['secret']) ? ['secret' => $this->API->dcList[$test][$ipv6][$dc_number]['secret']] : [];
            $combo = [[DefaultStream::class, []], [BufferedRawStream::class, []], [ObfuscatedStream::class, $extra], [IntermediatePaddedStream::class, []]];
            if ($only) {
                array_unshift($combos, $combo);
            } else {
                $combos []= $combo;
            }
        }
        $proxyCombos = [];
        foreach ($settings->getProxies() as $proxy => $extras) {
            foreach ($extras as $extra) {
                if ($proxy === ObfuscatedStream::class && isset($extra['secret'])) {
                    $secret = ObfuscatedStream::decodeSecret($extra['secret']);
                    if ($secret['domain'] !== null) {
                        // Fake TLS only carries the padded intermediate transport, as in TDLib.
                        $proxyCombos []= [[DefaultStream::class, []], [BufferedRawStream::class, []], [FakeTlsStream::class, $extra], [$proxy, $extra], [IntermediatePaddedStream::class, []]];
                        continue;
                    }
                    if ($secret['padded']) {
                        $combos[] = [[DefaultStream::class, []], [BufferedRawStream::class, []], [$proxy, $extra], [IntermediatePaddedStream::class, []]];
                    }
                }
                foreach ($combos as $orig) {
                    $combo = [];
                    if ($proxy === ObfuscatedStream::class) {
                        $combo = $orig;
                        if ($combo[\count($combo) - 2][0] === ObfuscatedStream::class) {
                            $combo[\count($combo) - 2][1] = $extra;
                        } else {
                            $mtproto = end($combo);
                            $combo[\count($combo) - 1] = [$proxy, $extra];
                            $combo[] = $mtproto;
                        }
                    } else {
                        if ($orig[1][0] === BufferedRawStream::class) {
                            [$first, $second] = [\array_slice($orig, 0, 2), \array_slice($orig, 2)];
                            $first[] = [$proxy, $extra];
                            $combo = array_merge($first, $second);
                        } elseif (\in_array($orig[1][0], [WsStream::class, WssStream::class], true)) {
                            [$first, $second] = [\array_slice($orig, 0, 1), \array_slice($orig, 1)];
                            $first[] = [BufferedRawStream::class, []];
                            $first[] = [$proxy, $extra];
                            $combo = array_merge($first, $second);
                        }
                    }
                    $proxyCombos []= $combo;
                }
            }
        }
        if ($proxyCombos) {
            // Only through the proxies unless allowed: a direct connection would bypass them.
            $combos = $settings->getDirectFallback() ? array_merge($proxyCombos, $combos) : $proxyCombos;
        }
        if ($settings->getRetry() && (!$proxyCombos || $settings->getDirectFallback())) {
            $combos[] = [[DefaultStream::class, []], [BufferedRawStream::class, []], [HttpsStream::class, []]];
        }
        $combos = array_unique($combos, SORT_REGULAR);

        $bind = self::normalizeBindToOption($settings->getBindTo());
        $onlyIPv6 = null;
        if ($bind !== null) {
            $onlyIPv6 = InternetAddress::fromString($bind)->getVersion() === InternetAddressVersion::IPv6
                ? 'ipv6'
                : 'ipv4';
        }
        $context = (new ConnectContext())->withConnectTimeout($settings->getTimeout())->withBindTo($bind);
        foreach ($combos as $combo) {
            foreach ([true, false] as $useDoH) {
                $ipv6Combos = [
                    $settings->getIpv6() ? 'ipv6' : 'ipv4',
                    $settings->getIpv6() ? 'ipv4' : 'ipv6',
                ];
                foreach ($ipv6Combos as $ipv6) {
                    if ($onlyIPv6 !== null && $onlyIPv6 !== $ipv6) {
                        continue;
                    }
                    if (!isset($this->API->dcList[$test][$ipv6][$dc_number]['ip_address'])) {
                        continue;
                    }
                    $address = $this->API->dcList[$test][$ipv6][$dc_number]['ip_address'];
                    if ($ipv6 === 'ipv6') {
                        $address = "[$address]";
                    }
                    $port = $this->API->dcList[$test][$ipv6][$dc_number]['port'];
                    foreach (array_unique([$port, 443, 80, 88, 5222]) as $port) {
                        $stream = end($combo)[0];
                        if ($stream === HttpsStream::class) {
                            $subdomain = $settings->getSslSubdomains()[abs($dc_number)] ?? null;
                            if (!$subdomain) {
                                continue;
                            }
                            if (DataCenter::isMedia($dc_number)) {
                                $subdomain .= '-1';
                            }
                            $path = $settings->getTestMode() ? 'apiw_test1' : 'apiw1';
                            $uri = 'tcp://'.$subdomain.'.web.telegram.org:'.$port.'/'.$path;
                        } elseif ($stream === HttpStream::class) {
                            $uri = 'tcp://'.$address.':'.$port.'/api';
                        } else {
                            $uri = 'tcp://'.$address.':'.$port;
                        }
                        $ctx = (new ConnectionContext())
                            ->setDc($dc_number)
                            ->setSocketContext($context)
                            ->setUri($uri)
                            ->setIpv6($ipv6 === 'ipv6');
                        foreach ($combo as $stream) {
                            if ($stream[0] === DefaultStream::class && $stream[1] === []) {
                                $stream[1] = $useDoH ? new DoHConnector($this->dohWrapper, $ctx) : $this->dohWrapper->dnsConnector;
                            }
                            if (\in_array($stream[0], [WsStream::class, WssStream::class], true) && $stream[1] === []) {
                                $stream[1] = $this->dohWrapper->webSocketConnector;
                                if ($stream[0] === WssStream::class) {
                                    $subdomain = $settings->getSslSubdomains()[abs($dc_number)] ?? null;
                                    if (!$subdomain) {
                                        continue;
                                    }
                                    if (DataCenter::isMedia($dc_number)) {
                                        $subdomain .= '-1';
                                    }
                                    $path = $settings->getTestMode() ? 'apiws_test' : 'apiws';
                                    $uri = 'tcp://'.$subdomain.'.web.telegram.org:'.$port.'/'.$path;
                                } else {
                                    $path = $settings->getTestMode() ? 'apiws_test' : 'apiws';
                                    $uri = 'tcp://'.$address.':'.$port.'/'.$path;
                                }
                                $ctx->setUri($uri);
                            }
                            /** @var array{0: class-string, 1: mixed} $stream */
                            /** @psalm-suppress TooFewArguments Psalm bug */
                            $ctx->addStream(...$stream);
                        }
                        $ctxs[] = $ctx;
                    }
                }
            }
        }
        if (empty($ctxs)) {
            throw new AssertionError("No info for DC $dc_number!");
        }
        return new ContextIterator($ctxs);
    }
    public function waitGetConnection(int $dc): Connection
    {
        return $this->getDataCenterConnection($dc)->waitGetConnection();
    }
    /**
     * Get DataCenterConnection instance.
     *
     * @param int $dc DC ID
     */
    public function getDataCenterConnection(int $dc, ?DataCenterConnection $legacy = null): DataCenterConnection
    {
        if (!isset($this->list[$dc]) || !$this->list[$dc]->hasCtx()) {
            $this->API->logger("Acquiring connect lock for $dc!", Logger::VERBOSE);
            $lock = $this->connectMutex->acquire((string) $dc);
            try {
                if (isset($this->list[$dc]) && $this->list[$dc]->hasCtx()) {
                    return $this->list[$dc];
                }
                $ctxs = $this->generateContexts($dc);

                $this->API->logger("Connecting to DC {$dc}", Logger::WARNING);
                $this->list[$dc] ??= new DataCenterConnection($this->API, $dc, $legacy);
                if ($legacy) {
                    $this->list[$dc]->importFromLegacy($legacy);
                }
                $this->list[$dc]->setCtx($ctxs);
                $this->list[$dc]->connect();
                $this->list[$dc]->auth->connectionState->wakeup();
            } finally {
                EventLoop::queue($lock->release(...));
            }
        }
        return $this->list[$dc];
    }
    /**
     * @psalm-mutation-free
     */
    public function has(int $dc): bool
    {
        $test = $this->getSettings()->getTestMode() ? 'test' : 'main';
        $ipv6 = $this->getSettings()->getIpv6() ? 'ipv6' : 'ipv4';
        return isset($this->API->dcList[$test][$ipv6][$dc]);
    }
    /**
     * Get all DataCenterConnection instances.
     *
     * @return array<int, DataCenterConnection>
     */
    public function getDataCenterConnections(): array
    {
        return $this->list;
    }
}
<?php

declare(strict_types=1);

/**
 * Snitch module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use const DIRECTORY_SEPARATOR;
use const HAD_MADELINE_PHAR;

/**
 * Snitch.
 *
 * @internal
 */
final class Snitch
{
    /**
     * Maximum starts without a phar file.
     */
    public const MAX_NO_PHAR_STARTS = 3;

    /**
     * Whether madeline.phar was downloaded from scratch.
     */
    private array $hadInstalled = [];

    /**
     * Called before serialization.
     *
     * @psalm-pure
     */
    public function __sleep(): array
    {
        return ['hadInstalled'];
    }
    /**
     * Wakeup function.
     */
    public function __wakeup(): void
    {
        if (\defined('HAD_MADELINE_PHAR')) {
            $this->hadInstalled []= HAD_MADELINE_PHAR;
            if (\count($this->hadInstalled) > self::MAX_NO_PHAR_STARTS) {
                array_shift($this->hadInstalled);
                if (!array_sum($this->hadInstalled)) { // For three times, MadelineProto was started with no phar file
                    $this->die();
                }
            }
        }
    }

    /**
     * Die.
     */
    private function die(): void
    {
        //Shutdown::removeCallback('restarter');
        $message = Lang::$current_lang["do_not_remove_MadelineProto.log_phar"];
        Logger::log($message, Logger::FATAL_ERROR);
        file_put_contents(Magic::$cwd.DIRECTORY_SEPARATOR.'DO_NOT_REMOVE_MADELINEPROTO_LOG_SESSION_'.random_int(PHP_INT_MIN, PHP_INT_MAX), $message);
        //die("$message\n");
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

/**
 * Indicates a remote URL to upload.
 *
 * @psalm-immutable
 */
final class RemoteUrl
{
    /**
     * @psalm-mutation-free
     */
    public function __construct(
        public readonly string $url
    ) {
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use danog\MadelineProto\EventHandler\Message\Entities\MessageEntity;
use danog\TelegramEntities\Entities as TelegramEntitiesEntities;

/**
 * Class that converts HTML or markdown to a message + set of entities.
 */
final class TextEntities
{
    /**
     * Creates an Entities container using a message and a list of entities.
     *
     * @psalm-mutation-free
     */
    public function __construct(
        /** Converted message */
        public string $message,
        /**
         * Converted entities.
         *
         * @var list<MessageEntity>
         */
        public array $entities,
    ) {
    }
    /**
     * Manually convert markdown to a message and a set of entities.
     *
     * @return self Object containing message and entities
     *
     * @psalm-mutation-free
     */
    public static function fromMarkdown(string $markdown): self
    {
        $res = TelegramEntitiesEntities::fromMarkdown($markdown);
        return new self(
            $res->message,
            MessageEntity::fromRawEntities($res->entities),
        );
    }

    /**
     * Manually convert HTML to a message and a set of entities.
     *
     * @return self Object containing message and entities
     */
    public static function fromHtml(string $html): self
    {
        $res = TelegramEntitiesEntities::fromHtml($html);
        return new self(
            $res->message,
            MessageEntity::fromRawEntities($res->entities),
        );
    }
}
<?php

declare(strict_types=1);

/**
 * Magic module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use Amp\DeferredFuture;
use Amp\File\Driver\BlockingFilesystemDriver;
use Amp\File\Driver\EioFilesystemDriver;
use Amp\File\Driver\UvFilesystemDriver;
use Amp\SignalException;
use danog\MadelineProto\TL\Conversion\Extension;
use phpseclib4\Math\BigInteger;
use Revolt\EventLoop;
use Throwable;

use const DIRECTORY_SEPARATOR;
use const E_ALL;
use const MADELINE_WORKER_TYPE;
use const PHP_INT_SIZE;

use const PHP_SAPI;
use const SIG_DFL;
use const SIGINT;
use const SIGTERM;

use function Amp\File\filesystem;
use function Amp\Log\hasColorSupport;
use function function_exists;

/**
 * @internal
 */
final class Magic
{
    public const ZERO_CHANNEL_ID = -1000000000000;
    public const ZERO_SECRET_CHAT_ID = -2000000000000;
    public const MIN_INT32 = -2147483648;

    public const MAX_USER_ID = (1 << 40) - 1;
    public const MAX_CHAT_ID = 999_999_999_999;
    public const MAX_CHANNEL_ID = 1000000000000 - (1 << 31);
    /**
     * Static storage.
     *
     */
    public static array $storage = [];
    /**
     * Whether this system is bigendian.
     *
     */
    public static bool $BIG_ENDIAN = false;
    /**
     * Whether this is a TTY console.
     *
     */
    public static bool $isatty = false;
    /**
     * Whether we're in a fork.
     *
     */
    public static bool $isFork = false;
    /**
     * Whether this is an IPC worker.
     */
    public static bool $isIpcWorker = false;
    /**
     * Whether we can get our PID.
     *
     */
    public static bool $can_getmypid = true;
    /**
     * Whether we can get our CWD.
     *
     */
    public static bool $can_getcwd = false;
    /**
     * Whether we can use igbinary.
     *
     */
    public static bool $can_use_igbinary = false;
    /**
     * Whether we've processed forks.
     *
     */
    public static bool $processed_fork = false;
    /**
     * Whether we can use ipv6.
     *
     */
    public static bool $ipv6 = false;
    /**
     * Our PID.
     *
     */
    public static ?int $pid = null;
    /**
     * Whether we've inited all light constants.
     *
     */
    private static bool $initedLight = false;
    /**
     * Whether we've inited all static constants.
     *
     */
    private static bool $inited = false;
    /**
     * Whether we've inited the ipv6 property.
     *
     */
    private static bool $initedIpv6 = false;
    /**
     * Bigint zero.
     *
     */
    public static BigInteger $zero;
    /**
     * Bigint one.
     *
     */
    public static BigInteger $one;
    /**
     * Bigint two.
     *
     */
    public static BigInteger $two;
    /**
     * Bigint 2^1984.
     *
     */
    public static BigInteger $twoe1984;
    /**
     * Bigint 2^2047.
     *
     */
    public static BigInteger $twoe2047;
    /**
     * Bigint 2^2048.
     *
     */
    public static BigInteger $twoe2048;
    /**
     * Bigint 2^255 - 19.
     *
     */
    public static BigInteger $fieldPrime;
    /**
     * Decoded UTF8 emojis for call fingerprint.
     *
     */
    public static array $emojis;
    /**
     * MadelineProto revision.
     *
     */
    public static string $revision;
    /**
     * Latest MadelineProto version.
     *
     */
    public static string $latest_release = API::RELEASE;
    /**
     * Our CWD.
     *
     */
    public static string $cwd;
    /**
     * Caller script CWD.
     *
     */
    public static string $script_cwd;
    /**
     * Whether we're running on altervista.
     *
     */
    public static bool $altervista = false;
    /**
     * Wether we're running on 000webhost (yuck).
     *
     */
    public static bool $zerowebhost = false;
    /**
     * Whether to suspend certain stdout log printing, when reading input.
     */
    public static ?DeferredFuture $suspendPeriodicLogging = null;
    /**
     * All mime types.
     *
     * @var array<string, string>
     */
    public static array $allMimes = [];
    /**
     * Whether the openssl extension is loaded.
     *
     */
    public static bool $hasOpenssl = false;
    /**
     * Whether there's a basedir limitation.
     */
    public static bool $hasBasedirLimitation = false;
    /**
     * Encoded emojis.
     *
     * @var string
     */
    public const JSON_EMOJIS = '["\\ud83d\\ude09","\\ud83d\\ude0d","\\ud83d\\ude1b","\\ud83d\\ude2d","\\ud83d\\ude31","\\ud83d\\ude21","\\ud83d\\ude0e","\\ud83d\\ude34","\\ud83d\\ude35","\\ud83d\\ude08","\\ud83d\\ude2c","\\ud83d\\ude07","\\ud83d\\ude0f","\\ud83d\\udc6e","\\ud83d\\udc77","\\ud83d\\udc82","\\ud83d\\udc76","\\ud83d\\udc68","\\ud83d\\udc69","\\ud83d\\udc74","\\ud83d\\udc75","\\ud83d\\ude3b","\\ud83d\\ude3d","\\ud83d\\ude40","\\ud83d\\udc7a","\\ud83d\\ude48","\\ud83d\\ude49","\\ud83d\\ude4a","\\ud83d\\udc80","\\ud83d\\udc7d","\\ud83d\\udca9","\\ud83d\\udd25","\\ud83d\\udca5","\\ud83d\\udca4","\\ud83d\\udc42","\\ud83d\\udc40","\\ud83d\\udc43","\\ud83d\\udc45","\\ud83d\\udc44","\\ud83d\\udc4d","\\ud83d\\udc4e","\\ud83d\\udc4c","\\ud83d\\udc4a","\\u270c","\\u270b","\\ud83d\\udc50","\\ud83d\\udc46","\\ud83d\\udc47","\\ud83d\\udc49","\\ud83d\\udc48","\\ud83d\\ude4f","\\ud83d\\udc4f","\\ud83d\\udcaa","\\ud83d\\udeb6","\\ud83c\\udfc3","\\ud83d\\udc83","\\ud83d\\udc6b","\\ud83d\\udc6a","\\ud83d\\udc6c","\\ud83d\\udc6d","\\ud83d\\udc85","\\ud83c\\udfa9","\\ud83d\\udc51","\\ud83d\\udc52","\\ud83d\\udc5f","\\ud83d\\udc5e","\\ud83d\\udc60","\\ud83d\\udc55","\\ud83d\\udc57","\\ud83d\\udc56","\\ud83d\\udc59","\\ud83d\\udc5c","\\ud83d\\udc53","\\ud83c\\udf80","\\ud83d\\udc84","\\ud83d\\udc9b","\\ud83d\\udc99","\\ud83d\\udc9c","\\ud83d\\udc9a","\\ud83d\\udc8d","\\ud83d\\udc8e","\\ud83d\\udc36","\\ud83d\\udc3a","\\ud83d\\udc31","\\ud83d\\udc2d","\\ud83d\\udc39","\\ud83d\\udc30","\\ud83d\\udc38","\\ud83d\\udc2f","\\ud83d\\udc28","\\ud83d\\udc3b","\\ud83d\\udc37","\\ud83d\\udc2e","\\ud83d\\udc17","\\ud83d\\udc34","\\ud83d\\udc11","\\ud83d\\udc18","\\ud83d\\udc3c","\\ud83d\\udc27","\\ud83d\\udc25","\\ud83d\\udc14","\\ud83d\\udc0d","\\ud83d\\udc22","\\ud83d\\udc1b","\\ud83d\\udc1d","\\ud83d\\udc1c","\\ud83d\\udc1e","\\ud83d\\udc0c","\\ud83d\\udc19","\\ud83d\\udc1a","\\ud83d\\udc1f","\\ud83d\\udc2c","\\ud83d\\udc0b","\\ud83d\\udc10","\\ud83d\\udc0a","\\ud83d\\udc2b","\\ud83c\\udf40","\\ud83c\\udf39","\\ud83c\\udf3b","\\ud83c\\udf41","\\ud83c\\udf3e","\\ud83c\\udf44","\\ud83c\\udf35","\\ud83c\\udf34","\\ud83c\\udf33","\\ud83c\\udf1e","\\ud83c\\udf1a","\\ud83c\\udf19","\\ud83c\\udf0e","\\ud83c\\udf0b","\\u26a1","\\u2614","\\u2744","\\u26c4","\\ud83c\\udf00","\\ud83c\\udf08","\\ud83c\\udf0a","\\ud83c\\udf93","\\ud83c\\udf86","\\ud83c\\udf83","\\ud83d\\udc7b","\\ud83c\\udf85","\\ud83c\\udf84","\\ud83c\\udf81","\\ud83c\\udf88","\\ud83d\\udd2e","\\ud83c\\udfa5","\\ud83d\\udcf7","\\ud83d\\udcbf","\\ud83d\\udcbb","\\u260e","\\ud83d\\udce1","\\ud83d\\udcfa","\\ud83d\\udcfb","\\ud83d\\udd09","\\ud83d\\udd14","\\u23f3","\\u23f0","\\u231a","\\ud83d\\udd12","\\ud83d\\udd11","\\ud83d\\udd0e","\\ud83d\\udca1","\\ud83d\\udd26","\\ud83d\\udd0c","\\ud83d\\udd0b","\\ud83d\\udebf","\\ud83d\\udebd","\\ud83d\\udd27","\\ud83d\\udd28","\\ud83d\\udeaa","\\ud83d\\udeac","\\ud83d\\udca3","\\ud83d\\udd2b","\\ud83d\\udd2a","\\ud83d\\udc8a","\\ud83d\\udc89","\\ud83d\\udcb0","\\ud83d\\udcb5","\\ud83d\\udcb3","\\u2709","\\ud83d\\udceb","\\ud83d\\udce6","\\ud83d\\udcc5","\\ud83d\\udcc1","\\u2702","\\ud83d\\udccc","\\ud83d\\udcce","\\u2712","\\u270f","\\ud83d\\udcd0","\\ud83d\\udcda","\\ud83d\\udd2c","\\ud83d\\udd2d","\\ud83c\\udfa8","\\ud83c\\udfac","\\ud83c\\udfa4","\\ud83c\\udfa7","\\ud83c\\udfb5","\\ud83c\\udfb9","\\ud83c\\udfbb","\\ud83c\\udfba","\\ud83c\\udfb8","\\ud83d\\udc7e","\\ud83c\\udfae","\\ud83c\\udccf","\\ud83c\\udfb2","\\ud83c\\udfaf","\\ud83c\\udfc8","\\ud83c\\udfc0","\\u26bd","\\u26be","\\ud83c\\udfbe","\\ud83c\\udfb1","\\ud83c\\udfc9","\\ud83c\\udfb3","\\ud83c\\udfc1","\\ud83c\\udfc7","\\ud83c\\udfc6","\\ud83c\\udfca","\\ud83c\\udfc4","\\u2615","\\ud83c\\udf7c","\\ud83c\\udf7a","\\ud83c\\udf77","\\ud83c\\udf74","\\ud83c\\udf55","\\ud83c\\udf54","\\ud83c\\udf5f","\\ud83c\\udf57","\\ud83c\\udf71","\\ud83c\\udf5a","\\ud83c\\udf5c","\\ud83c\\udf61","\\ud83c\\udf73","\\ud83c\\udf5e","\\ud83c\\udf69","\\ud83c\\udf66","\\ud83c\\udf82","\\ud83c\\udf70","\\ud83c\\udf6a","\\ud83c\\udf6b","\\ud83c\\udf6d","\\ud83c\\udf6f","\\ud83c\\udf4e","\\ud83c\\udf4f","\\ud83c\\udf4a","\\ud83c\\udf4b","\\ud83c\\udf52","\\ud83c\\udf47","\\ud83c\\udf49","\\ud83c\\udf53","\\ud83c\\udf51","\\ud83c\\udf4c","\\ud83c\\udf50","\\ud83c\\udf4d","\\ud83c\\udf46","\\ud83c\\udf45","\\ud83c\\udf3d","\\ud83c\\udfe1","\\ud83c\\udfe5","\\ud83c\\udfe6","\\u26ea","\\ud83c\\udff0","\\u26fa","\\ud83c\\udfed","\\ud83d\\uddfb","\\ud83d\\uddfd","\\ud83c\\udfa0","\\ud83c\\udfa1","\\u26f2","\\ud83c\\udfa2","\\ud83d\\udea2","\\ud83d\\udea4","\\u2693","\\ud83d\\ude80","\\u2708","\\ud83d\\ude81","\\ud83d\\ude82","\\ud83d\\ude8b","\\ud83d\\ude8e","\\ud83d\\ude8c","\\ud83d\\ude99","\\ud83d\\ude97","\\ud83d\\ude95","\\ud83d\\ude9b","\\ud83d\\udea8","\\ud83d\\ude94","\\ud83d\\ude92","\\ud83d\\ude91","\\ud83d\\udeb2","\\ud83d\\udea0","\\ud83d\\ude9c","\\ud83d\\udea6","\\u26a0","\\ud83d\\udea7","\\u26fd","\\ud83c\\udfb0","\\ud83d\\uddff","\\ud83c\\udfaa","\\ud83c\\udfad","\\ud83c\\uddef\\ud83c\\uddf5","\\ud83c\\uddf0\\ud83c\\uddf7","\\ud83c\\udde9\\ud83c\\uddea","\\ud83c\\udde8\\ud83c\\uddf3","\\ud83c\\uddfa\\ud83c\\uddf8","\\ud83c\\uddeb\\ud83c\\uddf7","\\ud83c\\uddea\\ud83c\\uddf8","\\ud83c\\uddee\\ud83c\\uddf9","\\ud83c\\uddf7\\ud83c\\uddfa","\\ud83c\\uddec\\ud83c\\udde7","1\\u20e3","2\\u20e3","3\\u20e3","4\\u20e3","5\\u20e3","6\\u20e3","7\\u20e3","8\\u20e3","9\\u20e3","0\\u20e3","\\ud83d\\udd1f","\\u2757","\\u2753","\\u2665","\\u2666","\\ud83d\\udcaf","\\ud83d\\udd17","\\ud83d\\udd31","\\ud83d\\udd34","\\ud83d\\udd35","\\ud83d\\udd36","\\ud83d\\udd37"]';
    /**
     * Initialize magic constants.
     *
     * @param bool $light Use lightweight initialization routine
     */
    public static function start(bool $light): void
    {
        if (self::$inited || (self::$initedLight && $light)) {
            return;
        }
        if (PHP_INT_SIZE < 8) {
            throw new Exception('A 64-bit build of PHP is required to run MadelineProto, PHP 8.0+ recommended.', 0, null, 'MadelineProto', 1);
        }
        if (!\defined('AMP_WORKER')) {
            \define('AMP_WORKER', 1);
        }
        if (!self::$initedLight) {
            // Setup file driver
            $driver = EventLoop::getDriver();

            if (UvFilesystemDriver::isSupported($driver)) {
                $driver = new UvFilesystemDriver($driver);
            } elseif (EioFilesystemDriver::isSupported()) {
                $driver = new EioFilesystemDriver($driver);
            } else {
                $driver = new BlockingFilesystemDriver();
            }
            filesystem($driver);

            // Setup error reporting
            Shutdown::init();
            set_error_handler(Exception::exceptionErrorHandler(...));
            set_exception_handler(Exception::exceptionHandler(...));
            self::$can_use_igbinary = \function_exists('igbinary_serialize');
            self::$isIpcWorker = \defined('MADELINE_WORKER_TYPE') ? MADELINE_WORKER_TYPE === 'madeline-ipc' : false;
            // Important, obtain root relative to caller script
            $backtrace = debug_backtrace(0);

            $last_entry = end($backtrace);
            $_cwd = '/tmp';
            if (\array_key_exists('file', $last_entry) && $last_entry['file'] !== null) {
                $_cwd = \dirname($last_entry['file']);
            } else {
                if (preg_match('/^\{closure\:(.+?)\:/ui', $last_entry['function'], $m)) {
                    $_cwd = \dirname($m[1]);
                }
            }

            self::$script_cwd = self::$cwd = $_cwd;
            if (PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg') {
                try {
                    error_reporting(E_ALL);
                    ini_set('log_errors', 1);
                    ini_set('error_log', self::$script_cwd.DIRECTORY_SEPARATOR.'MadelineProto.log');
                } catch (Throwable $e) {
                    //$this->logger->logger('Could not enable PHP logging');
                }
            }
            try {
                ini_set('memory_limit', -1);
            } catch (Throwable $e) {
            }
            // Check if we're in a console, for colorful log output
            try {
                self::$isatty = \defined('STDOUT') && hasColorSupport();
            } catch (Throwable $e) {
            }
            try {
                self::$cwd = getcwd();
                self::$can_getcwd = true;
            } catch (Throwable $e) {
            }
            try {
                if (\function_exists('set_time_limit')) {
                    set_time_limit(-1);
                }
            } catch (Throwable $e) {
            }
            // Define signal handlers, only in the CLI: under a web server SAPI the process belongs to the web server,
            // which handles the signals itself. The handlers of the event loop replaced its own ones in the worker,
            // and outlive the request (with ext-uv, the loop they belong to): php-fpm workers dumped core on the
            // SIGQUIT that stops idle ones.
            if (\defined('SIGINT') && (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg')) {
                //if (function_exists('pcntl_async_signals')) pcntl_async_signals(true);
                try {
                    pcntl_signal(SIGINT, static fn () => null);
                    pcntl_signal(SIGINT, SIG_DFL);
                    EventLoop::unreference(EventLoop::onSignal(SIGINT, static function (): void {
                        if (self::$suspendPeriodicLogging) {
                            self::togglePeriodicLogging();
                        }
                        throw new SignalException('SIGINT received');
                    }));
                    EventLoop::unreference(EventLoop::onSignal(SIGTERM, static function (): void {
                        if (self::$suspendPeriodicLogging) {
                            self::togglePeriodicLogging();
                        }
                        throw new SignalException('SIGTERM received');
                    }));
                    EventLoop::unreference(EventLoop::onSignal(SIGQUIT, static function (): void {
                        if (self::$suspendPeriodicLogging) {
                            self::togglePeriodicLogging();
                        }
                        throw new SignalException('SIGQUIT received');
                    }));
                } catch (Throwable $e) {
                }
            }
            self::$altervista = isset($_SERVER['SERVER_ADMIN']) && strpos($_SERVER['SERVER_ADMIN'], 'altervista.org');
            self::$zerowebhost = isset($_SERVER['SERVER_ADMIN']) && strpos($_SERVER['SERVER_ADMIN'], '000webhost.io');
            self::$can_getmypid = !self::$altervista && !self::$zerowebhost;
            self::$revision = 'Revision: '.API::RELEASE;
            self::$initedLight = true;
            if ($light) {
                return;
            }
        }
        $result = Tools::testFibers(100);
        if ($result['maxFibers'] < 100) {
            $message = "The maximum number of startable fibers is smaller than 100 ({$result['maxFibers']}): follow the instructions in https://t.me/MadelineProto/596 to fix.";
            if (PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg') {
                echo $message.'<br>';
            }
            $file = 'MadelineProto';
            $line = 1;
            throw new Exception($message, 0, null, $file, $line);
        }
        foreach (['xml', 'dom', 'fileinfo', 'json', 'filter', 'hash', 'zlib'] as $extension) {
            if (!\extension_loaded($extension)) {
                throw Exception::extension($extension);
            }
        }
        if (\extension_loaded('psr')) {
            throw new Exception("Please uninstall the psr extension to use MadelineProto!");
        }
        self::$BIG_ENDIAN = pack('L', 1) === pack('N', 1);
        self::$hasOpenssl = \extension_loaded('openssl');
        try {
            self::$hasBasedirLimitation = (bool) \ini_get('open_basedir');
        } catch (\Throwable) {
        }
        self::$emojis = json_decode(self::JSON_EMOJIS);
        self::$zero = new BigInteger(0);
        self::$one = new BigInteger(1);
        self::$two = new BigInteger(2);
        self::$twoe1984 = new BigInteger('010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', 16);
        self::$twoe2047 = new BigInteger('80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', 16);
        self::$twoe2048 = new BigInteger('0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', 16);
        self::$fieldPrime = new BigInteger('7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed', 16);
        $res = json_decode(file_get_contents(__DIR__.'/v3.json'), true);
        RPCErrorException::$errorMethodMap = $res['result'];
        RPCErrorException::$descriptions += $res['human_result'];
        foreach (Extension::ALL_MIMES as $ext => $mimes) {
            $ext = ".$ext";
            foreach ($mimes as $mime) {
                if (!isset(self::$allMimes[$mime])) {
                    self::$allMimes[$mime] = $ext;
                }
            }
        }
        GarbageCollector::start();
        self::$inited = true;
    }
    /**
     * Check if this is a POSIX fork of the main PHP process.
     */
    public static function isFork(): bool
    {
        if (self::$isFork) {
            return true;
        }
        if (!self::$can_getmypid) {
            return false;
        }
        try {
            if (self::$pid === null) {
                self::$pid = getmypid();
            }
            return self::$isFork = self::$pid !== getmypid();
        } catch (Throwable $e) {
            return self::$can_getmypid = false;
        }
    }
    public static function getPid(): ?int
    {
        self::isFork();
        return self::$pid;
    }
    /**
     * Get current working directory.
     */
    public static function getcwd(): string
    {
        return self::$can_getcwd ? getcwd() : self::$cwd;
    }
    /**
     * Toggle periodic logging.
     */
    public static function togglePeriodicLogging(): void
    {
        if (self::$suspendPeriodicLogging) {
            $deferred = self::$suspendPeriodicLogging;
            self::$suspendPeriodicLogging = null;
            $deferred->complete();
        } else {
            self::$suspendPeriodicLogging = new DeferredFuture;
            $f = new DeferredFuture;
            $f->complete();
            $f->getFuture()->await();
        }
    }

    /**
     * Set whether we can use ipv6.
     *
     * @param bool $ipv6 Whether we can use ipv6.
     *
     * @psalm-external-mutation-free
     */
    public static function setIpv6(bool $ipv6): void
    {
        if (!self::$initedIpv6) {
            self::$ipv6 = $ipv6;
            self::$initedIpv6 = true;
        }
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Settings;

use Amp\Socket\SocketAddress;
use danog\MadelineProto\SettingsAbstract;

/**
 * Metric settings.
 *
 * @psalm-external-mutation-free
 */
final class Metrics extends SettingsAbstract
{
    /**
     * Whether to enable additional prometheus stat collection for this session.
     */
    protected bool $enablePrometheusCollection = false;
    /**
     * Whether to enable memprof memory stat collection for this session.
     */
    protected bool $enableMemprofCollection = false;

    /**
     * Whether to expose metrics on the specified endpoint via HTTP.
     */
    protected ?SocketAddress $metricsBindTo = null;
    /**
     * Whether to expose metrics with startAndLoop, by providing a ?metrics or ?pprof query string.
     */
    protected bool $returnMetricsFromStartAndLoop = false;

    /**
     * Whether to expose prometheus/memprof metrics with startAndLoop, by providing a ?metrics or ?pprof query string.
     *
     * @psalm-external-mutation-free
     */
    public function setReturnMetricsFromStartAndLoop(bool $enable): self
    {
        $this->returnMetricsFromStartAndLoop = $enable;
        return $this;
    }
    /**
     * Whether to expose prometheus/memprof metrics with startAndLoop, by providing a ?metrics or ?pprof query string.
     */
    public function getReturnMetricsFromStartAndLoop(): bool
    {
        return $this->returnMetricsFromStartAndLoop;
    }

    /**
     * Whether to enable additional prometheus stat collection for this session.
     *
     * @psalm-external-mutation-free
     */
    public function setEnablePrometheusCollection(bool $enable): self
    {
        $this->enablePrometheusCollection = $enable;
        return $this;
    }
    /**
     * Whether additional prometheus stat collection is enabled for this session.
     */
    public function getEnablePrometheusCollection(): bool
    {
        return $this->enablePrometheusCollection;
    }

    /**
     * Whether to enable memprof memory stat collection for this session.
     *
     * @psalm-external-mutation-free
     */
    public function setEnableMemprofCollection(bool $enable): self
    {
        $this->enableMemprofCollection = $enable;
        return $this;
    }
    /**
     * Whether to enable memprof memory stat collection for this session.
     */
    public function getEnableMemprofCollection(): bool
    {
        return $this->enableMemprofCollection;
    }

    /**
     * Whether to expose metrics on the specified endpoint via HTTP.
     *
     * @psalm-external-mutation-free
     */
    public function setMetricsBindTo(?SocketAddress $metricsBindTo): self
    {
        $this->metricsBindTo = $metricsBindTo;
        return $this;
    }

    /**
     * Whether to expose metrics on the specified endpoint via HTTP.
     */
    public function getMetricsBindTo(): ?SocketAddress
    {
        return $this->metricsBindTo;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Settings;

use danog\MadelineProto\Exception;
use danog\MadelineProto\Magic;
use danog\MadelineProto\SettingsAbstract;
use danog\MadelineProto\Stream\MTProtoBufferInterface;
use danog\MadelineProto\Stream\MTProtoTransport\AbridgedStream;
use danog\MadelineProto\Stream\MTProtoTransport\HttpStream;
use danog\MadelineProto\Stream\Proxy\HttpProxy;
use danog\MadelineProto\Stream\RawStreamInterface;
use danog\MadelineProto\Stream\StreamInterface;
use danog\MadelineProto\Stream\Transport\DefaultStream;

/**
 * Connection settings.
 */
final class Connection extends SettingsAbstract
{
    /**
     * RSA keys.
     */
    protected array $rsaKeys = [
        "-----BEGIN RSA PUBLIC KEY-----\n".
        "MIIBCgKCAQEA6LszBcC1LGzyr992NzE0ieY+BSaOW622Aa9Bd4ZHLl+TuFQ4lo4g\n".
        "5nKaMBwK/BIb9xUfg0Q29/2mgIR6Zr9krM7HjuIcCzFvDtr+L0GQjae9H0pRB2OO\n".
        "62cECs5HKhT5DZ98K33vmWiLowc621dQuwKWSQKjWf50XYFw42h21P2KXUGyp2y/\n".
        "+aEyZ+uVgLLQbRA1dEjSDZ2iGRy12Mk5gpYc397aYp438fsJoHIgJ2lgMv5h7WY9\n".
        "t6N/byY9Nw9p21Og3AoXSL2q/2IJ1WRUhebgAdGVMlV1fkuOQoEzR7EdpqtQD9Cs\n".
        "5+bfo3Nhmcyvk5ftB0WkJ9z6bNZ7yxrP8wIDAQAB\n".
        '-----END RSA PUBLIC KEY-----',
    ];
    /**
     * Test RSA keys.
     */
    protected array $testRsaKeys =  [
        "-----BEGIN RSA PUBLIC KEY-----\n".
        "MIIBCgKCAQEAyMEdY1aR+sCR3ZSJrtztKTKqigvO/vBfqACJLZtS7QMgCGXJ6XIR\n".
        "yy7mx66W0/sOFa7/1mAZtEoIokDP3ShoqF4fVNb6XeqgQfaUHd8wJpDWHcR2OFwv\n".
        "plUUI1PLTktZ9uW2WE23b+ixNwJjJGwBDJPQEQFBE+vfmH0JP503wr5INS1poWg/\n".
        "j25sIWeYPHYeOrFp/eXaqhISP6G+q2IeTaWTXpwZj4LzXq5YOpk4bYEQ6mvRq7D1\n".
        "aHWfYmlEGepfaYR8Q0YqvvhYtMte3ITnuSJs171+GDqpdKcSwHnd6FudwGO4pcCO\n".
        "j4WcDuXc2CTHgH8gFTNhp/Y8/SpDOhvn9QIDAQAB\n".
        '-----END RSA PUBLIC KEY-----',
    ];
    /**
     * Maximum media socket count.
     */
    protected int $maxMediaSocketCount = 10;
    /**
     * Robin period (seconds).
     */
    protected int $robinPeriod = 10;
    /**
     * Protocol identifier.
     *
     * @var class-string<MTProtoBufferInterface>
     */
    protected string $protocol = AbridgedStream::class;
    /**
     * Transport identifier.
     *
     * @var class-string<RawStreamInterface>
     */
    protected string $transport = DefaultStream::class;
    /**
     * Proxy identifiers.
     *
     * @var array<class-string<StreamInterface>, array>
     */
    protected array $proxy = [];
    /**
     * Whether to use the obfuscated protocol.
     */
    protected bool $obfuscated = false;

    /**
     * Whether we're in test mode.
     */
    protected bool $testMode = false;

    /**
     * Whether to use ipv6.
     */
    protected bool $ipv6 = false;

    /**
     * Connection timeout.
     */
    protected float $timeout = 5.0;
    /**
     * Ping interval.
     */
    protected int $pingInterval = 60;

    /**
     * Whether to retry connection.
     */
    protected bool $retry = true;

    /**
     * Whether to connect directly when the proxies fail.
     */
    protected bool $directFallback = false;

    /**
     * Whether to use DNS over HTTPS.
     */
    protected bool $useDoH = true;

    /**
     * Bind on specific address and port.
     */
    protected ?string $bindTo = null;

    /**
     * Subdomains of web.telegram.org for https protocol.
     */
    protected array $sslSubdomains = [
        1 => 'pluto',
        2 => 'venus',
        3 => 'aurora',
        4 => 'vesta',
        5 => 'flora',
    ];

    public function __construct()
    {
        $this->init();
    }
    public function __wakeup(): void
    {
        $this->init();
    }
    public function init(): void
    {
        Magic::start(light: true);

        if (Magic::$altervista) {
            $this->addProxy(HttpProxy::class, ['address' => 'localhost', 'port' => 80]);
            $this->setProtocol(HttpStream::class);
        } else {
            $this->removeProxy(HttpProxy::class, ['address' => 'localhost', 'port' => 80]);
        }
    }
    /**
     * Get protocol identifier.
     */
    public function getProtocol(): string
    {
        return $this->protocol;
    }

    /**
     * Set protocol identifier.
     *
     * Available MTProto transport protocols (smaller overhead is better):
     *
     * `\danog\MadelineProto\Stream\MTProtoTransport\AbridgedStream`: Lightest protocol available
     * Overhead: Very small
     * Minimum envelope length: 1 byte (length)
     * Maximum envelope length: 4 bytes (length)
     *
     * `\danog\MadelineProto\Stream\MTProtoTransport\IntermediateStream`: I guess they like having multiple protocols
     * Overhead: small
     * Minimum envelope length: 4 bytes (length)
     * Maximum envelope length: 4 bytes (length)
     *
     * `\danog\MadelineProto\Stream\MTProtoTransport\IntermediatePaddedStream`: Padded version of the intermediate protocol, to use with obfuscation enabled to bypass ISP blocks
     * Overhead: small-medium
     * Minimum envelope length: random
     * Maximum envelope length: random
     *
     * `\danog\MadelineProto\Stream\MTProtoTransport\FullStream`: The basic MTProto transport protocol
     * Overhead: medium
     * Minimum envelope length: 12 bytes (length+seqno+crc)
     * Maximum envelope length: 12 bytes (length+seqno+crc)
     * Pros:
     * Initial integrity check with crc32
     * Transport sequence number check
     *
     * Cons:
     * Initial integrity check with crc32 is not that useful since the TCP protocol already uses it internally
     * Transport sequence number check is also not that useful since transport sequence numbers are not encrypted and thus cannot be used to avoid replay attacks, and MadelineProto already uses MTProto sequence numbers and message ids for that.
     *
     * `\danog\MadelineProto\Stream\MTProtoTransport\HttpStream`: MTProto over HTTP for browsers and webhosts
     * Overhead: medium
     * Pros:
     * Can be used on restricted webhosts or browsers
     * Cons:
     * Very big envelope length
     *
     * `\danog\MadelineProto\Stream\MTProtoTransport\HttpsStream`: MTProto over HTTPS for browsers and webhosts, very secure
     * Overhead: high
     * Pros:
     * Can be used on restricted webhosts or browsers
     * Provides an additional layer of security by trasmitting data over TLS
     * Integrity checks with HMAC built into TLS
     * Sequence number checks built into TLS
     * Cons:
     * Very big envelope length
     * Requires an additional round of encryption
     *
     * @param class-string<MTProtoBufferInterface> $protocol Protocol identifier
     *
     * @psalm-external-mutation-free
     */
    public function setProtocol(string $protocol): self
    {
        if (!isset(class_implements($protocol)[MTProtoBufferInterface::class])) {
            throw new Exception('An invalid protocol was specified!');
        }
        $this->protocol = $protocol;

        return $this;
    }

    /**
     * Get whether to use ipv6.
     */
    public function getIpv6(): bool
    {
        return $this->ipv6;
    }

    /**
     * Set whether to use ipv6.
     *
     * @param bool $ipv6 Whether to use ipv6
     *
     * @psalm-external-mutation-free
     */
    public function setIpv6(bool $ipv6): self
    {
        $this->ipv6 = $ipv6;

        return $this;
    }

    /**
     * Get subdomains of web.telegram.org for https protocol.
     */
    public function getSslSubdomains(): array
    {
        return $this->sslSubdomains;
    }

    /**
     * Set subdomains of web.telegram.org for https protocol.
     *
     * @param array $sslSubdomains Subdomains of web.telegram.org for https protocol.
     *
     * @psalm-external-mutation-free
     */
    public function setSslSubdomains(array $sslSubdomains): self
    {
        $this->sslSubdomains = $sslSubdomains;

        return $this;
    }

    /**
     * Get maximum media socket count.
     */
    public function getMaxMediaSocketCount(): int
    {
        return $this->maxMediaSocketCount;
    }

    /**
     * Set maximum media socket count.
     *
     * @param int $maxMediaSocketCount Maximum media socket count.
     *
     * @psalm-external-mutation-free
     */
    public function setMaxMediaSocketCount(int $maxMediaSocketCount): self
    {
        $this->maxMediaSocketCount = $maxMediaSocketCount;

        return $this;
    }

    /**
     * Get robin period (seconds).
     */
    public function getRobinPeriod(): int
    {
        return $this->robinPeriod;
    }

    /**
     * Set robin period (seconds).
     *
     * @param int $robinPeriod Robin period (seconds).
     *
     * @psalm-external-mutation-free
     */
    public function setRobinPeriod(int $robinPeriod): self
    {
        $this->robinPeriod = $robinPeriod;

        return $this;
    }

    /**
     * Get proxy identifiers.
     *
     * @return array<class-string<StreamInterface>, array>
     */
    public function getProxies(): array
    {
        return $this->proxy;
    }

    /**
     * Add proxy identifier to list, one of:.
     *
     * `\danog\MadelineProto\Stream\MTProtoTransport\ObfuscatedStream::class`
     * `\danog\MadelineProto\Stream\Proxy\HttpProxy::class`
     * `\danog\MadelineProto\Stream\Proxy\SocksProxy::class`
     *
     * @param class-string<StreamInterface> $proxy Proxy identifier
     * @param array                         $extra Extra
     *
     * @psalm-external-mutation-free
     */
    public function addProxy(string $proxy, array $extra = []): self
    {
        if (!isset(class_implements($proxy)[StreamInterface::class])) {
            throw new Exception('An invalid proxy class was specified!');
        }
        if (!isset($this->proxy[$proxy])) {
            $this->proxy[$proxy] = [];
        }
        $this->proxy[$proxy][] = $extra;

        return $this;
    }

    /**
     * Set proxies.
     *
     * The key must be one of:
     *
     * `\danog\MadelineProto\Stream\MTProtoTransport\ObfuscatedStream::class`
     * `\danog\MadelineProto\Stream\Proxy\HttpProxy::class`
     * `\danog\MadelineProto\Stream\Proxy\SocksProxy::class`
     *
     * The value must be a list of extra (URI, username, password) for that proxy.
     *
     * @param array<class-string<StreamInterface>, list<array>> $proxies Proxies
     *
     * @psalm-external-mutation-free
     */
    public function setProxies(array $proxies): self
    {
        foreach ($proxies as $proxy => $_) {
            if (!isset(class_implements($proxy)[StreamInterface::class])) {
                throw new Exception('An invalid proxy class was specified!');
            }
        }
        $this->proxy = $proxies;
        return $this;
    }
    /**
     * Clear proxies.
     *
     * @psalm-external-mutation-free
     */
    public function clearProxies(): self
    {
        $this->proxy = [];

        return $this;
    }

    /**
     * Remove specific proxy pair.
     *
     * @psalm-external-mutation-free
     */
    public function removeProxy(string $proxy, array $extra): self
    {
        if (!isset($this->proxy[$proxy])) {
            return $this;
        }
        if (false === $index = array_search($extra, $this->proxy[$proxy], true)) {
            return $this;
        }
        unset($this->proxy[$proxy][$index]);
        if (empty($this->proxy[$proxy])) {
            unset($this->proxy[$proxy]);
        }
        return $this;
    }
    /**
     * Get whether to use the obfuscated protocol: useful to bypass ISP blocks.
     */
    public function getObfuscated(): bool
    {
        return $this->obfuscated;
    }

    /**
     * Set whether to use the obfuscated protocol: useful to bypass ISP blocks.
     *
     * @param bool $obfuscated Whether to use the obfuscated protocol.
     *
     * @psalm-external-mutation-free
     */
    public function setObfuscated(bool $obfuscated): self
    {
        $this->obfuscated = $obfuscated;

        return $this;
    }

    /**
     * Get whether we're in test mode.
     */
    public function getTestMode(): bool
    {
        return $this->testMode;
    }

    /**
     * Set whether we're in test mode.
     *
     * @param bool $testMode Whether we're in test mode.
     *
     * @psalm-external-mutation-free
     */
    public function setTestMode(bool $testMode): self
    {
        $this->testMode = $testMode;

        return $this;
    }

    /**
     * Get transport identifier.
     *
     * @return class-string<RawStreamInterface>
     */
    public function getTransport(): string
    {
        return $this->transport;
    }

    /**
     * Sets the transport protocol to use when connecting to telegram.
     * Not supported by HTTP and HTTPS protocols, obfuscation must be enabled.
     *
     * `danog\MadelineProto\Stream\Transport`: Default TCP transport
     * `danog\MadelineProto\Stream\WsTransport`: Plain websocket transport
     * `danog\MadelineProto\Stream\WssTransport`: TLS websocket transport
     *
     * @param class-string<RawStreamInterface> $transport Transport identifier.
     *
     * @psalm-external-mutation-free
     */
    public function setTransport(string $transport): self
    {
        if (!isset(class_implements($transport)[RawStreamInterface::class])) {
            throw new Exception('An invalid transport was specified!');
        }
        $this->transport = $transport;

        return $this;
    }

    /**
     * Get whether to retry connection.
     */
    public function getRetry(): bool
    {
        return $this->retry;
    }

    /**
     * Set whether to retry connection.
     *
     * @param bool $retry Whether to retry connection.
     *
     * @psalm-external-mutation-free
     */
    public function setRetry(bool $retry): self
    {
        $this->retry = $retry;

        return $this;
    }

    /**
     * Get whether to connect directly when the proxies fail.
     */
    public function getDirectFallback(): bool
    {
        return $this->directFallback;
    }

    /**
     * Set whether to connect directly (without the proxies) when the proxies fail.
     *
     * Off by default: when proxies are set, Telegram and the URLs requested over HTTP are only ever
     * reached through them.
     *
     * @param bool $directFallback Whether to connect directly when the proxies fail.
     *
     * @psalm-external-mutation-free
     */
    public function setDirectFallback(bool $directFallback): self
    {
        $this->directFallback = $directFallback;

        return $this;
    }

    /**
     * Get connection timeout.
     */
    public function getTimeout(): float
    {
        return $this->timeout;
    }

    /**
     * Set connection timeout.
     *
     * @param float $timeout Connection timeout.
     *
     * @psalm-external-mutation-free
     */
    public function setTimeout(float $timeout): self
    {
        $this->timeout = $timeout;

        return $this;
    }

    /**
     * Get ping interval.
     */
    public function getPingInterval(): int
    {
        return $this->pingInterval;
    }

    /**
     * Set ping interval.
     *
     * @param int $pingInterval Ping interval
     *
     * @psalm-external-mutation-free
     */
    public function setPingInterval(int $pingInterval): self
    {
        $this->pingInterval = $pingInterval;

        return $this;
    }

    /**
     * Get whether to use DNS over HTTPS.
     */
    public function getUseDoH(): bool
    {
        return $this->useDoH;
    }

    /**
     * Set whether to use DNS over HTTPS.
     *
     * @param bool $useDoH Whether to use DNS over HTTPS
     *
     * @psalm-external-mutation-free
     */
    public function setUseDoH(bool $useDoH): self
    {
        $this->useDoH = $useDoH;

        return $this;
    }

    /**
     * Get bind on specific address and port.
     */
    public function getBindTo(): ?string
    {
        return $this->bindTo;
    }

    /**
     * Set bind on specific address and port.
     *
     * @param null|string $bindTo Bind on specific address and port.
     *
     * @psalm-external-mutation-free
     */
    public function setBindTo(?string $bindTo): self
    {
        $this->bindTo = $bindTo;

        return $this;
    }

    /**
     * Get RSA keys.
     *
     */
    public function getRsaKeys(): array
    {
        return $this->rsaKeys;
    }

    /**
     * Set RSA keys.
     *
     * @param array $rsaKeys RSA keys
     *
     * @psalm-external-mutation-free
     */
    public function setRsaKeys(array $rsaKeys): self
    {
        $this->rsaKeys = $rsaKeys;

        return $this;
    }

    /**
     * Get test RSA keys.
     *
     */
    public function getTestRsaKeys(): array
    {
        return $this->testRsaKeys;
    }

    /**
     * Set test RSA keys.
     *
     * @param array $testRsaKeys Test RSA keys
     *
     * @psalm-external-mutation-free
     */
    public function setTestRsaKeys(array $testRsaKeys): self
    {
        $this->testRsaKeys = $testRsaKeys;

        return $this;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Settings;

use danog\MadelineProto\SettingsAbstract;

/**
 * Serialization settings.
 *
 * @psalm-external-mutation-free
 */
final class Serialization extends SettingsAbstract
{
    /**
     * Serialization interval, in seconds.
     */
    protected int $interval = 30;

    /**
     * Get serialization interval, in seconds.
     */
    public function getInterval(): int
    {
        return $this->interval;
    }

    /**
     * Set serialization interval, in seconds.
     *
     * @param int $interval Serialization interval, in seconds (minimum 10 seconds).
     *
     * @psalm-external-mutation-free
     */
    public function setInterval(int $interval): self
    {
        if ($interval < 10) {
            throw new \AssertionError("The serialization interval cannot be smaller than 10 seconds!");
        }
        $this->interval = $interval;

        return $this;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Settings;

use danog\MadelineProto\SettingsAbstract;
use danog\MadelineProto\Tools;
use danog\MadelineProto\WebUI\TemplateEngine;

/**
 * Web template settings for the login and status pages.
 *
 * The pages are rendered by native PHP templates (see {@see TemplateEngine}): to customize them, copy the
 * templates you want to change from the builtin template directory ({@see TemplateEngine::BUILTIN_DIRECTORY})
 * to a directory of yours, and add it with {@see self::addDirectory()}.
 *
 * @psalm-external-mutation-free
 */
final class Templates extends SettingsAbstract
{
    /**
     * Template directories, searched in order before the builtin one.
     *
     * @var list<string>
     */
    protected array $directories = [];

    private ?TemplateEngine $engine = null;

    /**
     * Get the template directories, searched in order before the builtin one.
     *
     * @return list<string>
     */
    public function getDirectories(): array
    {
        return $this->directories;
    }

    /**
     * Set the template directories, searched in order before the builtin one.
     *
     * A directory only needs to contain the templates it overrides.
     *
     * @param list<string> $directories Template directories
     */
    public function setDirectories(array $directories): self
    {
        $this->directories = array_values(array_map(Tools::absolute(...), $directories));
        $this->engine = null;

        return $this;
    }

    /**
     * Add a template directory, searched before the ones that were already added and the builtin one.
     *
     * A directory only needs to contain the templates it overrides.
     *
     * @param string $directory Template directory
     */
    public function addDirectory(string $directory): self
    {
        return $this->setDirectories([$directory, ...$this->directories]);
    }

    /**
     * Get the template engine.
     *
     * @internal
     */
    public function getEngine(): TemplateEngine
    {
        return $this->engine ??= new TemplateEngine($this->directories);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Settings;

use danog\MadelineProto\Exception;
use danog\MadelineProto\Lang;
use danog\MadelineProto\Magic;
use danog\MadelineProto\SettingsAbstract;
use Throwable;

use const PHP_VERSION;

/**
 * App information.
 *
 * @psalm-suppress UnsupportedPropertyReferenceUsage
 */
final class AppInfo extends SettingsAbstract
{
    /**
     * API ID.
     */
    protected ?int $apiId = null;
    /**
     * API hash.
     */
    protected ?string $apiHash = null;
    /**
     * Device model.
     */
    protected string $deviceModel;
    /**
     * System version.
     */
    protected string $systemVersion;
    /**
     * App version.
     */
    protected string $appVersion;
    /**
     * Language code.
     */
    protected string $langCode = 'en';
    /**
     * System language code.
     */
    protected string $systemLangCode = 'en';
    /**
     * Language pack.
     */
    protected string $langPack = '';

    /**
     * Whether to show a prompt, asking to enter an API ID/API hash if none is provided.
     */
    protected bool $showPrompt = true;

    public function __construct()
    {
        // Detect device model
        try {
            $this->deviceModel = php_uname('s');
        } catch (Throwable $e) {
            $this->deviceModel = 'Web server';
        }
        // Detect system version
        try {
            $this->systemVersion = php_uname('r');
        } catch (Throwable $e) {
            $this->systemVersion = PHP_VERSION;
        }
        // Detect language
        // The primary subtag of the first language, which names the translations in langs/: "ckb" of
        // "ckb-IQ,en;q=0.8", "zh" of "zh_CN.UTF-8".
        $code = '';
        if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) && \is_string($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
            $code = preg_split('/[-_;,.\s]/', trim($_SERVER['HTTP_ACCEPT_LANGUAGE']), 2)[0];
        } elseif (isset($_SERVER['LANG']) && \is_string($_SERVER['LANG'])) {
            $code = preg_split('/[-_.@]/', $_SERVER['LANG'], 2)[0];
        }
        $code = strtolower($code);
        // "C" and "POSIX" are the default locale, not a language.
        if ($code !== '' && $code !== '*' && $code !== 'c' && $code !== 'posix') {
            $this->setLangCode($code);
        }
        $this->init();
        $this->appVersion = \danog\MadelineProto\API::RELEASE;
    }
    public function __wakeup(): void
    {
        $this->init();
    }
    public function init(): void
    {
        Magic::start(light: true);
        // Detect language pack
        if (isset(Lang::$lang[$this->langCode])) {
            Lang::$current_lang =& Lang::$lang[$this->langCode];
            Lang::$currentPercentage = Lang::PERCENTAGES[$this->langCode];
        } else {
            Lang::$currentPercentage = 0;
        }
    }

    /**
     * Check if the settings have API ID/hash information.
     *
     * @psalm-mutation-free
     */
    public function hasApiInfo(): bool
    {
        return isset($this->apiHash, $this->apiId) && $this->apiId;
    }
    /**
     * Get API ID.
     *
     * @psalm-external-mutation-free
     */
    public function getApiId(): int
    {
        if (!isset($this->apiId)) {
            throw new Exception(Lang::$current_lang['api_not_set']);
        }
        return $this->apiId;
    }

    /**
     * Set API ID.
     *
     * @param int $apiId API ID.
     *
     * @psalm-external-mutation-free
     */
    public function setApiId(int $apiId): self
    {
        $this->apiId = $apiId;
        return $this;
    }

    /**
     * Get API hash.
     *
     * @psalm-external-mutation-free
     */
    public function getApiHash(): string
    {
        if (!isset($this->apiHash)) {
            throw new Exception(Lang::$current_lang['api_not_set']);
        }
        return $this->apiHash;
    }

    /**
     * Set API hash.
     *
     * @param string $apiHash API hash.
     *
     * @psalm-external-mutation-free
     */
    public function setApiHash(string $apiHash): self
    {
        $this->apiHash = $apiHash;

        return $this;
    }

    /**
     * Get device model.
     */
    public function getDeviceModel(): string
    {
        return $this->deviceModel;
    }

    /**
     * Set device model.
     *
     * @param string $deviceModel Device model.
     *
     * @psalm-external-mutation-free
     */
    public function setDeviceModel(string $deviceModel): self
    {
        $this->deviceModel = $deviceModel;

        return $this;
    }

    /**
     * Get system version.
     */
    public function getSystemVersion(): string
    {
        return $this->systemVersion;
    }

    /**
     * Set system version.
     *
     * @param string $systemVersion System version.
     *
     * @psalm-external-mutation-free
     */
    public function setSystemVersion(string $systemVersion): self
    {
        $this->systemVersion = $systemVersion;

        return $this;
    }

    /**
     * Get app version.
     */
    public function getAppVersion(): string
    {
        return $this->appVersion;
    }

    /**
     * Set app version.
     *
     * @param string $appVersion App version.
     *
     * @psalm-external-mutation-free
     */
    public function setAppVersion(string $appVersion): self
    {
        $this->appVersion = $appVersion;

        return $this;
    }

    /**
     * Get language code.
     */
    public function getLangCode(): string
    {
        return $this->langCode;
    }

    /**
     * Set language code.
     *
     * @param string $langCode Language code.
     *
     * @psalm-external-mutation-free
     */
    public function setLangCode(string $langCode): self
    {
        $this->langCode = $langCode;
        if (isset(Lang::$lang[$this->langCode])) {
            Lang::$current_lang =& Lang::$lang[$this->langCode];
            Lang::$currentPercentage = Lang::PERCENTAGES[$this->langCode];
        } else {
            Lang::$currentPercentage = 0;
        }

        return $this;
    }

    /**
     * Get system language code.
     */
    public function getSystemLangCode(): string
    {
        return $this->systemLangCode;
    }

    /**
     * Set system language code.
     *
     * @param string $langCode Language code.
     *
     * @psalm-external-mutation-free
     */
    public function setSystemLangCode(string $langCode): self
    {
        $this->systemLangCode = $langCode;

        return $this;
    }

    /**
     * Get language pack.
     */
    public function getLangPack(): string
    {
        return $this->langPack;
    }

    /**
     * Set language pack.
     *
     * @param string $langPack Language pack.
     *
     * @psalm-external-mutation-free
     */
    public function setLangPack(string $langPack): self
    {
        $this->langPack = $langPack;

        return $this;
    }

    /**
     * Get whether to show a prompt, asking to enter an API ID/API hash if none is provided.
     *
     */
    public function getShowPrompt(): bool
    {
        return $this->showPrompt;
    }

    /**
     * Set whether to show a prompt, asking to enter an API ID/API hash if none is provided.
     *
     * @param bool $showPrompt Whether to show a prompt, asking to enter an API ID/API hash if none is provided.
     *
     * @psalm-external-mutation-free
     */
    public function setShowPrompt(bool $showPrompt): static
    {
        $this->showPrompt = $showPrompt;

        return $this;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Settings;

use danog\MadelineProto\SettingsAbstract;

/**
 * Secret chat settings.
 *
 * @psalm-external-mutation-free
 */
final class SecretChats extends SettingsAbstract
{
    /**
     * What secret chats to accept.
     *
     * Boolean or array of IDs
     *
     * @var bool|array<int>
     */
    protected bool|array $accept = true;

    /**
     * Get boolean or array of IDs.
     *
     * @return bool|array<int>
     */
    public function getAccept(): bool|array
    {
        return $this->accept;
    }

    /**
     * Set boolean or array of IDs.
     *
     * @param bool|array<int> $accept Boolean or array of IDs
     *
     * @psalm-external-mutation-free
     */
    public function setAccept(bool|array $accept): self
    {
        $this->accept = $accept;

        return $this;
    }

    /**
     * Can we accept this chat.
     *
     * @internal
     *
     * @psalm-mutation-free
     */
    public function canAccept(int $id): bool
    {
        if (!$this->accept) {
            return false;
        }
        if ($this->accept === true) {
            return true;
        }
        return \in_array($id, $this->accept, true);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Settings;

use danog\MadelineProto\SettingsAbstract;

/**
 * Cryptography settings.
 *
 * @psalm-external-mutation-free
 */
final class Auth extends SettingsAbstract
{
    /**
     * Max tries for generating auth key.
     */
    protected int $maxAuthTries = 5;

    /**
     * @deprecated Always true
     * Get whether to use PFS.
     *
     * @psalm-pure
     */
    public function getPfs(): bool
    {
        return true;
    }

    /**
     * Set whether to use PFS.
     *
     * @deprecated Always true
     *
     * @param bool $pfs Whether to use PFS
     *
     * @psalm-mutation-free
     */
    public function setPfs(bool $pfs): self
    {
        return $this;
    }

    /**
     * Get max tries for generating auth key.
     */
    public function getMaxAuthTries(): int
    {
        return $this->maxAuthTries;
    }

    /**
     * Set max tries for generating auth key.
     *
     * @param int<1, max> $maxAuthTries Max tries for generating auth key
     *
     * @psalm-external-mutation-free
     */
    public function setMaxAuthTries(int $maxAuthTries): self
    {
        $this->maxAuthTries = max(1, $maxAuthTries);

        return $this;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Settings;

use danog\MadelineProto\SettingsAbstract;

/**
 * RPC settings.
 *
 * @psalm-external-mutation-free
 */
final class RPC extends SettingsAbstract
{
    /**
     * RPC resend timeout.
     */
    protected int $rpcResendTimeout = 10;
    /**
     * RPC drop timeout.
     */
    protected int $rpcDropTimeout = 60;

    /**
     * Flood timeout: if FLOOD_WAIT_ time is bigger than this, throw exception instead of waiting asynchronously.
     */
    protected int $floodTimeout = 30;

    /**
     * Encode payload with GZIP if bigger than.
     */
    protected int $gzipEncodeIfGt = 1024 * 1024;

    /**
     * Get RPC drop timeout.
     */
    public function getRpcDropTimeout(): int
    {
        return $this->rpcDropTimeout;
    }

    /**
     * Set RPC drop timeout.
     *
     * @param int $rpcDropTimeout RPC timeout
     *
     * @psalm-external-mutation-free
     */
    public function setRpcDropTimeout(int $rpcDropTimeout): self
    {
        $this->rpcDropTimeout = $rpcDropTimeout;

        return $this;
    }

    /**
     * Get RPC resend timeout.
     */
    public function getRpcResendTimeout(): int
    {
        return $this->rpcResendTimeout;
    }

    /**
     * Set RPC resend timeout.
     *
     * @param int $rpcResendTimeout RPC timeout.
     *
     * @psalm-external-mutation-free
     */
    public function setRpcResendTimeout(int $rpcResendTimeout): self
    {
        $this->rpcResendTimeout = $rpcResendTimeout;

        return $this;
    }

    /**
     * Get flood timeout: if FLOOD_WAIT_ time is bigger than this, throw exception instead of waiting asynchronously.
     *
     * @psalm-mutation-free
     */
    public function getFloodTimeout(): int
    {
        return max(5, $this->floodTimeout);
    }

    /**
     * Set flood timeout: if FLOOD_WAIT_ time is bigger than this, throw exception instead of waiting asynchronously.
     *
     * Must be bigger than 5.
     *
     * @param int $floodTimeout Flood timeout: if FLOOD_WAIT_ time is bigger than this, throw exception instead of waiting asynchronously
     *
     * @psalm-external-mutation-free
     */
    public function setFloodTimeout(int $floodTimeout): self
    {
        $this->floodTimeout = $floodTimeout;

        return $this;
    }

    /**
     * Get encode payload with GZIP if bigger than.
     */
    public function getGzipEncodeIfGt(): int
    {
        return $this->gzipEncodeIfGt;
    }

    /**
     * Set encode payload with GZIP if bigger than.
     *
     * @param int $gzipEncodeIfGt Encode payload with GZIP if bigger than
     *
     * @psalm-external-mutation-free
     */
    public function setGzipEncodeIfGt(int $gzipEncodeIfGt): self
    {
        $this->gzipEncodeIfGt = $gzipEncodeIfGt;

        return $this;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Settings;

use danog\MadelineProto\Magic;
use danog\MadelineProto\SettingsAbstract;

/**
 * IPC server settings.
 */
final class Ipc extends SettingsAbstract
{
    public function __construct()
    {
        Magic::start(light: true);
    }

    /**
     * Get WARNING: this will cause slow startup if enabled.
     *
     * @psalm-pure
     */
    public function getSlow(): bool
    {
        return Magic::$isIpcWorker;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Settings\Database;

/**
 * Generic db backend settings.
 */
abstract class SqlAbstract extends DriverDatabaseAbstract
{
    /**
     * Database name.
     */
    protected string $database = 'MadelineProto';
    /**
     * Username.
     */
    protected string $username = 'root';

    /**
     * Maximum connection limit.
     *
     * @var positive-int
     */
    protected int $maxConnections = 100;

    /**
     * Idle timeout.
     *
     * @var positive-int
     */
    protected int $idleTimeout = 60;

    /**
     * Database URI.
     */
    protected string $uri = 'tcp://127.0.0.1';

    /**
     * Get maximum connection limit.
     *
     * @return positive-int
     */
    public function getMaxConnections(): int
    {
        return $this->maxConnections;
    }

    /**
     * Set maximum connection limit.
     *
     * @param positive-int $maxConnections Maximum connection limit.
     *
     * @psalm-external-mutation-free
     */
    public function setMaxConnections(int $maxConnections): static
    {
        $this->maxConnections = $maxConnections;

        return $this;
    }

    /**
     * Get idle timeout.
     *
     * @return positive-int
     */
    public function getIdleTimeout(): int
    {
        return $this->idleTimeout;
    }

    /**
     * Set idle timeout.
     *
     * @param positive-int $idleTimeout Idle timeout.
     *
     * @psalm-external-mutation-free
     */
    public function setIdleTimeout(int $idleTimeout): static
    {
        $this->idleTimeout = $idleTimeout;

        return $this;
    }

    /**
     * Get database name.
     */
    #[\Override]
    public function getDatabase(): string
    {
        return $this->database;
    }

    /**
     * Set database name.
     *
     * @param string $database Database name.
     *
     * @psalm-external-mutation-free
     */
    public function setDatabase(string $database): static
    {
        $this->database = $database;

        return $this;
    }

    /**
     * Get username.
     */
    public function getUsername(): string
    {
        return $this->username;
    }

    /**
     * Set username.
     *
     * @param string $username Username.
     *
     * @psalm-external-mutation-free
     */
    public function setUsername(string $username): static
    {
        $this->username = $username;

        return $this;
    }

    /**
     * Get database URI.
     */
    #[\Override]
    public function getUri(): string
    {
        return $this->uri;
    }

    /**
     * Set database URI.
     *
     * @param string $uri Database URI.
     *
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function setUri(string $uri): static
    {
        $this->uri = $uri;

        return $this;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Settings\Database;

use danog\MadelineProto\Settings\DatabaseAbstract;

/**
 * Base class for database backends.
 */
abstract class DriverDatabaseAbstract extends DatabaseAbstract
{
    /**
     * For how long to keep records in memory after last read, for cached backends.
     *
     * @var int<0, max>
     */
    protected int $cacheTtl = 5 * 60;
    /**
     * Database password.
     */
    protected string $password = '';

    /**
     * Which serializer to use by default.
     *
     * If null, the best serializater is chosen.
     */
    protected ?SerializerType $serializer = null;

    /**
     * If set, indicates that the filesystem is ephemeral, and thus session files will not be used to store persistent data.
     *
     * Must contain a unique string, used as prefix for database tables, different for every session.
     * The prefix may be the same if different databases are used.
     *
     * This is useful when running MadelineProto inside docker containers without volumes, using just a database.
     *
     * Note that the session folder must still NEVER be deleted *if* MadelineProto is running,
     * or else the session will be dropped from the database due to AUTH_KEY_DUPLICATED errors.
     *
     * Stopping the container and then deleting the session folder is 100% OK though.
     */
    protected ?string $ephemeralFilesystemPrefix = null;

    /**
     * If set, indicates that the filesystem is ephemeral, and thus session files will not be used to store persistent data.
     *
     * Must contain a unique string, used as prefix for database tables, different for every session.
     * The prefix may be the same if different databases are used.
     *
     * This is useful when running MadelineProto inside docker containers without volumes, using just a database.
     *
     * Note that the session folder must still NEVER be deleted *if* MadelineProto is running,
     * or else the session will be dropped from the database due to AUTH_KEY_DUPLICATED errors.
     *
     * Stopping the container and then deleting the session folder is 100% OK though.
     */
    public function getEphemeralFilesystemPrefix(): ?string
    {
        return $this->ephemeralFilesystemPrefix;
    }

    /**
     * If set, indicates that the filesystem is ephemeral, and thus session files will not be used to store persistent data.
     *
     * Must contain a unique string, used as prefix for database tables, different for every session.
     * The prefix may be the same if different databases are used.
     *
     * This is useful when running MadelineProto inside docker containers without volumes, using just a database.
     *
     * Note that the session folder must still NEVER be deleted *if* MadelineProto is running,
     * or else the session will be dropped from the database due to AUTH_KEY_DUPLICATED errors.
     *
     * Stopping the container and then deleting the session folder is 100% OK though.
     *
     * @param ?string $ephemeralFilesystemPrefix The database prefix
     *
     * @psalm-external-mutation-free
     */
    public function setEphemeralFilesystemPrefix(?string $ephemeralFilesystemPrefix): static
    {
        $this->ephemeralFilesystemPrefix = $ephemeralFilesystemPrefix;

        return $this;
    }

    /**
     * Get the DB's unique ID.
     *
     * @internal
     */
    public function getDbIdentifier(): string
    {
        $uri = parse_url($this->getUri());
        $host = $uri['host'] ?? '';
        $port = $uri['port'] ?? '';
        return "$host:$port:".$this->getDatabase();
    }

    /**
     * Get for how long to keep records in memory after last read, for cached backends.
     */
    public function getCacheTtl(): int
    {
        return $this->cacheTtl;
    }

    /**
     * Set for how long to keep records in memory after last read, for cached backends.
     *
     * The cache TTL identifier can be a string like '+5 minutes'.
     * When data is retrieved from a database it is stored in memory.
     * This helps to reduce latency, improve speed and reduce mysql/postgres/redis load.
     * Data will be removed from the cache if last access was more than this amount of time.
     * Clean up is done once per minute.
     *
     * @param int|string $cacheTtl For how long to keep records in memory after last read, for cached backends.
     */
    public function setCacheTtl(int|string $cacheTtl): static
    {
        $this->cacheTtl = \is_int($cacheTtl) ? $cacheTtl : strtotime($cacheTtl) - time();

        return $this;
    }

    /**
     * Get password.
     */
    public function getPassword(): string
    {
        return $this->password;
    }

    /**
     * Set password.
     *
     * @param string $password Password.
     *
     * @psalm-external-mutation-free
     */
    public function setPassword(string $password): static
    {
        $this->password = $password;

        return $this;
    }

    /**
     * Get database name/ID.
     *
     * @psalm-impure
     */
    abstract public function getDatabase(): string|int;
    /**
     * Get database URI.
     * @psalm-impure
     */
    abstract public function getUri(): string;

    /**
     * Set database URI.
     * @psalm-impure
     */
    abstract public function setUri(string $uri): static;

    public function getSerializer(): ?SerializerType
    {
        return $this->serializer;
    }

    /**
     * Which serializer to use by default.
     *
     * If null, the best serializer is chosen.
     *
     * @psalm-external-mutation-free
     */
    public function setSerializer(?SerializerType $serializer): static
    {
        $this->serializer = $serializer;
        return $this;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Settings\Database;

use Amp\Postgres\PostgresConfig;
use danog\AsyncOrm\Serializer\Igbinary;
use danog\AsyncOrm\Serializer\Native;
use danog\AsyncOrm\Settings;
use danog\AsyncOrm\Settings\PostgresSettings;

/**
 * Postgres backend settings.
 */
final class Postgres extends SqlAbstract
{
    #[\Override]
    public function getOrmSettings(): Settings
    {
        $host = str_replace(['tcp://', 'unix://'], '', $this->getUri());
        if ($host[0] === '/') {
            $port = 0;
        } else {
            $host = explode(':', $host, 2);
            if (\count($host) === 2) {
                [$host, $port] = $host;
            } else {
                $host = $host[0];
                $port = PostgresConfig::DEFAULT_PORT;
            }
        }
        $config = new PostgresConfig(
            host: $host,
            port: (int) $port,
            user: $this->getUsername(),
            password: $this->getPassword(),
            database: $this->getDatabase()
        );
        return new PostgresSettings(
            $config,
            match ($this->serializer) {
                SerializerType::IGBINARY => new Igbinary,
                SerializerType::SERIALIZE => new Native,
                null => null
            },
            $this->cacheTtl,
            $this->maxConnections,
            $this->idleTimeout,
        );
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Settings\Database;

use Amp\Redis\RedisConfig;
use danog\AsyncOrm\Serializer\Igbinary;
use danog\AsyncOrm\Serializer\Native;
use danog\AsyncOrm\Settings;
use danog\AsyncOrm\Settings\RedisSettings;

/**
 * Redis backend settings.
 */
final class Redis extends DriverDatabaseAbstract
{
    #[\Override]
    public function getOrmSettings(): Settings
    {
        return new RedisSettings(
            RedisConfig::fromUri($this->uri)->withDatabase($this->database)->withPassword($this->password),
            match ($this->serializer) {
                SerializerType::IGBINARY => new Igbinary,
                SerializerType::SERIALIZE => new Native,
                null => null
            },
            $this->cacheTtl,
        );
    }
    /**
     * Database number.
     */
    protected int $database = 0;
    /**
     * Database URI.
     */
    protected string $uri = 'redis://127.0.0.1';

    /**
     * Get database number.
     */
    #[\Override]
    public function getDatabase(): int
    {
        return $this->database;
    }

    /**
     * Set database number.
     *
     * @param int $database Database number.
     *
     * @psalm-external-mutation-free
     */
    public function setDatabase(int $database): self
    {
        $this->database = $database;

        return $this;
    }

    /**
     * Get database URI.
     */
    #[\Override]
    public function getUri(): string
    {
        return $this->uri;
    }

    /**
     * Set database URI.
     *
     * @param string $uri Database URI.
     *
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function setUri(string $uri): static
    {
        $this->uri = $uri;

        return $this;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Settings\Database;

use danog\AsyncOrm\Settings;
use danog\AsyncOrm\Settings\MemorySettings;
use danog\MadelineProto\Settings\DatabaseAbstract;

/**
 * Memory backend settings.
 *
 * @psalm-external-mutation-free
 */
final class Memory extends DatabaseAbstract
{
    /**
     * @psalm-pure
     */
    #[\Override]
    public function getOrmSettings(): Settings
    {
        return new MemorySettings;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Settings\Database;

use Amp\Mysql\MysqlConfig;
use AssertionError;
use danog\AsyncOrm\Serializer\Igbinary;
use danog\AsyncOrm\Serializer\Native;
use danog\AsyncOrm\Settings;
use danog\AsyncOrm\Settings\MysqlSettings;

/**
 * MySQL backend settings.
 *
 * MariaDb 10.2+ or Mysql 5.6+ required.
 */
final class Mysql extends SqlAbstract
{
    /**
     * @var int<1, max>|null $optimizeIfWastedGtMb
     */
    private ?int $optimizeIfWastedGtMb = null;
    /**
     * @internal Not entirely sure whether this should be exposed.

     * Whether to optimize MySQL tables automatically if more than the specified amount of megabytes is wasted by the MySQL engine.

     * Be careful when tweaking this setting as it may lead to slowdowns on startup.

     * A good setting is 10mb.
     *
     * @param int<1, max>|null $optimizeIfWastedGtMb
     *
     * @psalm-external-mutation-free
     */
    public function setOptimizeIfWastedGtMb(?int $optimizeIfWastedGtMb): self
    {
        /** @psalm-suppress DocblockTypeContradiction */
        if ($optimizeIfWastedGtMb !== null && $optimizeIfWastedGtMb <= 0) {
            /** @var int $optimizeIfWastedGtMb */
            throw new AssertionError("An invalid value was specified: $optimizeIfWastedGtMb");
        }
        $this->optimizeIfWastedGtMb = $optimizeIfWastedGtMb;
        return $this;
    }
    /**
     * @internal Not entirely sure whether this should be exposed.
     *
     * Whether to optimize MySQL tables automatically if more than the specified amount of bytes is wasted by the MySQL engine.
     *
     * Be careful when tweaking this setting as it may lead to slowdowns on startup.
     *
     * A good setting is 10mb.
     *
     * @return int<1, max>|null
     */
    public function getOptimizeIfWastedGtMb(): ?int
    {
        return $this->optimizeIfWastedGtMb;
    }

    #[\Override]
    public function getOrmSettings(): Settings
    {
        $host = str_replace(['tcp://', 'unix://'], '', $this->getUri());
        if ($host[0] === '/') {
            $port = 0;
        } else {
            $host = explode(':', $host, 2);
            if (\count($host) === 2) {
                [$host, $port] = $host;
            } else {
                $host = $host[0];
                $port = MysqlConfig::DEFAULT_PORT;
            }
        }
        $config = new MysqlConfig(
            host: $host,
            port: (int) $port,
            user: $this->getUsername(),
            password: $this->getPassword(),
            database: $this->getDatabase()
        );
        return new MysqlSettings(
            $config,
            match ($this->serializer) {
                SerializerType::IGBINARY => new Igbinary,
                SerializerType::SERIALIZE => new Native,
                null => null
            },
            $this->cacheTtl,
            $this->maxConnections,
            $this->idleTimeout,
            $this->optimizeIfWastedGtMb
        );
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Settings\Database;

enum SerializerType: string
{
    case SERIALIZE = 'serialize';
    case IGBINARY = 'igbinary';
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Settings;

use danog\MadelineProto\SettingsAbstract;

/**
 * VoIP settings.
 *
 * @psalm-external-mutation-free
 */
final class VoIP extends SettingsAbstract
{
    /**
     * How far ahead of playback the files played in calls are read, in milliseconds.
     */
    protected int $playbackLookahead = 0;

    /**
     * Get how far ahead of playback the files played in calls are read, in milliseconds.
     */
    public function getPlaybackLookahead(): int
    {
        return $this->playbackLookahead;
    }

    /**
     * Set how far ahead of playback the files played in calls are read, in milliseconds.
     *
     * The frames read ahead are kept in memory, for every call: by default (0), only the next frame is
     * read ahead, to keep memory usage to a minimum. Reading further ahead avoids stuttering when the
     * files are read from a slow source (like a slow URL), at the cost of about the file's bitrate
     * times the lookahead of memory per call.
     *
     * @param int<0, max> $playbackLookahead Lookahead, in milliseconds.
     */
    public function setPlaybackLookahead(int $playbackLookahead): self
    {
        if ($playbackLookahead < 0) {
            throw new \AssertionError("The playback lookahead can't be negative!");
        }
        $this->playbackLookahead = $playbackLookahead;

        return $this;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Settings;

use danog\MadelineProto\SettingsAbstract;
use Throwable;

/**
 * TL schema settings.
 */
final class TLSchema extends SettingsAbstract
{
    /**
     * TL layer version.
     */
    protected int $layer = 232;
    /**
     * API schema path.
     */
    protected string $APISchema = __DIR__ . '/../TL_telegram_v232.tl';
    /**
     * MTProto schema path.
     */
    protected string $MTProtoSchema = __DIR__.'/../TL_mtproto_v1.tl';
    /**
     * Secret schema path.
     */
    protected string $secretSchema = __DIR__.'/../TL_secret.tl';
    /**
     * @internal Other schemas
     *
     * @var array<string, string>
     */
    protected array $other = [];
    /**
     * Whether the scheme was upgraded.
     */
    private bool $wasUpgraded = true;
    /**
     * Whether to enable fuzzing mode (all parameters will be populated with default values).
     */
    protected bool $fuzzMode = false;
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function __sleep()
    {
        return array_merge(['wasUpgraded'], parent::__sleep());
    }

    /**
     * Upgrade scheme autonomously.
     */
    public function __wakeup(): void
    {
        $exists = false;
        try {
            $exists = file_exists($this->APISchema);
        } catch (Throwable) {
        }
        // Scheme was upgraded or path has changed
        if (!$exists) {
            $new = new self;
            $this->setAPISchema($new->getAPISchema());
            $this->setMTProtoSchema($new->getMTProtoSchema());
            $this->setSecretSchema($new->getSecretSchema());
            $this->setLayer($new->getLayer());
            $this->wasUpgraded = true;
        }
    }
    /**
     * Returns whether the TL parser should re-parse the TL schemes.
     */
    public function needsUpgrade(): bool
    {
        return $this->wasUpgraded;
    }
    /**
     * Signal that scheme was re-parsed.
     *
     * @psalm-external-mutation-free
     */
    public function upgrade(): void
    {
        $this->wasUpgraded = false;
    }
    /**
     * Get TL layer version.
     */
    public function getLayer(): int
    {
        return $this->layer;
    }

    /**
     * Set TL layer version.
     *
     * @param int $layer TL layer version.
     *
     * @psalm-external-mutation-free
     */
    public function setLayer(int $layer): self
    {
        $this->layer = $layer;

        return $this;
    }

    /**
     * Get MTProto schema path.
     */
    public function getMTProtoSchema(): string
    {
        return $this->MTProtoSchema;
    }

    /**
     * Set MTProto schema path.
     *
     * @param string $MTProtoSchema MTProto schema path.
     *
     * @psalm-external-mutation-free
     */
    public function setMTProtoSchema(string $MTProtoSchema): self
    {
        $this->MTProtoSchema = $MTProtoSchema;

        return $this;
    }

    /**
     * Get API schema path.
     */
    public function getAPISchema(): string
    {
        return $this->APISchema;
    }

    /**
     * Set API schema path.
     *
     * @param string $APISchema API schema path.
     *
     * @psalm-external-mutation-free
     */
    public function setAPISchema(string $APISchema): self
    {
        $this->APISchema = $APISchema;

        return $this;
    }

    /**
     * Get secret schema path.
     */
    public function getSecretSchema(): string
    {
        return $this->secretSchema;
    }

    /**
     * Set secret schema path.
     *
     * @param string $secretSchema Secret schema path.
     *
     * @psalm-external-mutation-free
     */
    public function setSecretSchema(string $secretSchema): self
    {
        $this->secretSchema = $secretSchema;

        return $this;
    }

    /**
     * Get the value of other.
     *
     * @return array<string, string>
     */
    public function getOther(): array
    {
        return $this->other;
    }

    /**
     * Set the value of other.
     *
     * @param array<string, string> $other
     *
     * @psalm-external-mutation-free
     */
    public function setOther(array $other): self
    {
        $this->other = $other;

        return $this;
    }

    /**
     * Get the value of the fuzz mode.
     */
    public function getFuzzMode(): bool
    {
        return $this->fuzzMode;
    }

    /**
     * Set the value of the fuzz mode.
     *
     * @psalm-external-mutation-free
     */
    public function setFuzzMode(bool $fuzz): self
    {
        $this->fuzzMode = $fuzz;

        return $this;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Settings;

use danog\AsyncOrm\Settings;
use danog\MadelineProto\SettingsAbstract;

/**
 * Base class for storage backends.
 *
 * @psalm-external-mutation-free
 */
abstract class DatabaseAbstract extends SettingsAbstract
{
    /**
     * Whether to enable the file reference database. If disabled, will break file downloads.
     */
    protected bool $enableFileReferenceDb = true;
    /**
     * Whether to enable the min database. If disabled, will break sendMessage (and other methods) in certain conditions.
     */
    protected bool $enableMinDb = true;
    /**
     * Whether to enable the username database. If disabled, will break sendMessage (and other methods) with usernames.
     */
    protected bool $enableUsernameDb = true;
    /**
     * Whether to enable the full peer info database. If disabled, will break getFullInfo.
     */
    protected bool $enableFullPeerDb = true;
    /**
     * Whether to enable the peer info database. If disabled, will break getInfo.
     */
    protected bool $enablePeerInfoDb = true;

    /**
     * Get whether to enable the file reference database. If disabled, will break file downloads.
     */
    public function getEnableFileReferenceDb(): bool
    {
        return $this->enableFileReferenceDb;
    }

    /**
     * Set whether to enable the file reference database. If disabled, will break file downloads.
     *
     * @param bool $enableFileReferenceDb Whether to enable the file reference database. If disabled, will break file downloads.
     *
     * @psalm-external-mutation-free
     */
    public function setEnableFileReferenceDb(bool $enableFileReferenceDb): static
    {
        $this->enableFileReferenceDb = $enableFileReferenceDb;

        return $this;
    }

    /**
     * Get whether to enable the min database. If disabled, will break sendMessage (and other methods) in certain conditions.
     */
    public function getEnableMinDb(): bool
    {
        return $this->enableMinDb;
    }

    /**
     * Set whether to enable the min database. If disabled, will break sendMessage (and other methods) in certain conditions.
     *
     * @param bool $enableMinDb Whether to enable the min database. If disabled, will break sendMessage (and other methods) in certain conditions.
     *
     * @psalm-external-mutation-free
     */
    public function setEnableMinDb(bool $enableMinDb): static
    {
        $this->enableMinDb = $enableMinDb;

        return $this;
    }

    /**
     * Get whether to enable the username database. If disabled, will break sendMessage (and other methods) with usernames.
     */
    public function getEnableUsernameDb(): bool
    {
        return $this->enableUsernameDb;
    }

    /**
     * Set whether to enable the username database. If disabled, will break sendMessage (and other methods) with usernames.
     *
     * @param bool $enableUsernameDb Whether to enable the username database. If disabled, will break sendMessage (and other methods) with usernames.
     *
     * @psalm-external-mutation-free
     */
    public function setEnableUsernameDb(bool $enableUsernameDb): static
    {
        $this->enableUsernameDb = $enableUsernameDb;

        return $this;
    }

    /**
     * Get whether to enable the full peer info database. If disabled, will break getFullInfo.
     */
    public function getEnableFullPeerDb(): bool
    {
        return $this->enableFullPeerDb;
    }

    /**
     * Set whether to enable the full peer info database. If disabled, will break getFullInfo.
     *
     * @param bool $enableFullPeerDb Whether to enable the full peer info database. If disabled, will break getFullInfo.
     *
     * @psalm-external-mutation-free
     */
    public function setEnableFullPeerDb(bool $enableFullPeerDb): static
    {
        $this->enableFullPeerDb = $enableFullPeerDb;

        return $this;
    }

    /**
     * Get whether to enable the peer info database. If disabled, will break getInfo.
     */
    public function getEnablePeerInfoDb(): bool
    {
        return $this->enablePeerInfoDb;
    }

    /**
     * Set whether to enable the peer info database. If disabled, will break getInfo.
     *
     * @param bool $enablePeerInfoDb Whether to enable the peer info database. If disabled, will break getInfo.
     *
     * @psalm-external-mutation-free
     */
    public function setEnablePeerInfoDb(bool $enablePeerInfoDb): static
    {
        $this->enablePeerInfoDb = $enablePeerInfoDb;

        return $this;
    }

    /** @psalm-impure */
    abstract public function getOrmSettings(): Settings;
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Settings;

use danog\MadelineProto\SettingsAbstract;

/**
 * PWRTelegram settings.
 *
 * @psalm-external-mutation-free
 */
final class Pwr extends SettingsAbstract
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Settings;

use danog\MadelineProto\SettingsAbstract;

/**
 * Peer database settings.
 *
 * @psalm-external-mutation-free
 */
final class Peer extends SettingsAbstract
{
    /**
     * Cache time for full peer information (seconds).
     */
    protected int $fullInfoCacheTime = 60*60;
    /**
     * Should madeline fetch the full member list of every group it meets?
     */
    protected bool $fullFetch = false;
    /**
     * Whether to cache all peers on startup for userbots.
     */
    protected bool $cacheAllPeersOnStartup = false;

    /**
     * Get cache time for full peer information (seconds).
     */
    public function getFullInfoCacheTime(): int
    {
        return $this->fullInfoCacheTime;
    }

    /**
     * Set cache time for full peer information (seconds).
     *
     * @param int $fullInfoCacheTime Cache time for full peer information (seconds).
     *
     * @psalm-external-mutation-free
     */
    public function setFullInfoCacheTime(int $fullInfoCacheTime): self
    {
        $this->fullInfoCacheTime = $fullInfoCacheTime;

        return $this;
    }

    /**
     * Get should madeline fetch the full member list of every group it meets?
     */
    public function getFullFetch(): bool
    {
        return $this->fullFetch;
    }

    /**
     * Set should madeline fetch the full member list of every group it meets?
     *
     * @param bool $fullFetch Should madeline fetch the full member list of every group it meets?
     *
     * @psalm-external-mutation-free
     */
    public function setFullFetch(bool $fullFetch): self
    {
        $this->fullFetch = $fullFetch;

        return $this;
    }

    /**
     * Get whether to cache all peers on startup for userbots.
     */
    public function getCacheAllPeersOnStartup(): bool
    {
        return $this->cacheAllPeersOnStartup;
    }

    /**
     * Set whether to cache all peers on startup for userbots.
     *
     * @param bool $cacheAllPeersOnStartup Whether to cache all peers on startup for userbots.
     *
     * @psalm-external-mutation-free
     */
    public function setCacheAllPeersOnStartup(bool $cacheAllPeersOnStartup): self
    {
        $this->cacheAllPeersOnStartup = $cacheAllPeersOnStartup;

        return $this;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Settings;

use Closure;
use danog\MadelineProto\Logger as MadelineProtoLogger;
use danog\MadelineProto\Magic;
use danog\MadelineProto\SettingsAbstract;
use danog\MadelineProto\Tools;

use const PHP_SAPI;

/**
 * Logger settings.
 */
final class Logger extends SettingsAbstract
{
    /**
     * Logger type.
     *
     * @var MadelineProtoLogger::LOGGER_* $type Logger type.
     */
    protected int $type;

    /**
     * Extra parameter for logger.
     *
     * @var null|callable|string
     */
    protected $extra;

    /**
     * Logging level.
     *
     * @var MadelineProtoLogger::LEVEL_*
     */
    protected int $level = MadelineProtoLogger::LEVEL_VERBOSE;

    /**
     * Maximum filesize for logger, in case of file logging.
     */
    protected int $maxSize = 1 * 1024 * 1024;

    public function __construct()
    {
        $this->type = (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg')
            ? MadelineProtoLogger::ECHO_LOGGER
            : MadelineProtoLogger::FILE_LOGGER;
        Magic::start(light: true);
        $this->extra = Magic::$script_cwd.'/MadelineProto.log';
    }

    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function __sleep()
    {
        return $this->extra instanceof Closure
            ? ['type', 'level', 'maxSize']
            : ['type', 'extra', 'level', 'maxSize'];
    }
    /**
     * Wakeup function.
     */
    public function __wakeup(): void
    {
        $this->type = (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg')
            ? MadelineProtoLogger::ECHO_LOGGER
            : MadelineProtoLogger::FILE_LOGGER;
        if (!$this->extra && $this->type === MadelineProtoLogger::FILE_LOGGER) {
            $this->extra = Magic::$script_cwd.'/MadelineProto.log';
        }

        $this->init();
    }
    /**
     * Initialize global logging.
     */
    private function init(): void
    {
        Magic::start(light: true);
        MadelineProtoLogger::constructorFromSettings($this);
    }
    /**
     * Get $type Logger type.
     *
     * @return MadelineProtoLogger::LOGGER_*
     *
     * @psalm-mutation-free
     */
    public function getType(): int
    {
        return \defined('MADELINE_WORKER') ? MadelineProtoLogger::FILE_LOGGER : $this->type;
    }

    /**
     * Set $type Logger type.
     *
     * @param MadelineProtoLogger::LOGGER_* $type $type Logger type.
     *
     * @psalm-external-mutation-free
     */
    public function setType(int $type): self
    {
        $this->type = $type;

        return $this;
    }

    /**
     * Get extra parameter for logger.
     *
     */
    public function getExtra(): callable|string|null
    {
        return $this->type === MadelineProtoLogger::FILE_LOGGER
            ? Tools::absolute($this->extra)
            : $this->extra;
    }

    /**
     * Set extra parameter for logger.
     *
     * @param null|callable|string $extra Extra parameter for logger.
     */
    public function setExtra(callable|string|null $extra): self
    {
        if ($this->type === MadelineProtoLogger::CALLABLE_LOGGER && !\is_callable($extra)) {
            $this->setType((PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg')
                    ? MadelineProtoLogger::ECHO_LOGGER
                    : MadelineProtoLogger::FILE_LOGGER);
            return $this;
        }
        $this->extra = $extra;

        return $this;
    }

    /**
     * Get logging level.
     *
     * @return MadelineProtoLogger::LEVEL_*
     */
    public function getLevel(): int
    {
        return $this->level;
    }

    /**
     * Set logging level.
     *
     * @param MadelineProtoLogger::LEVEL_* $level Logging level.
     *
     * @psalm-external-mutation-free
     */
    public function setLevel(int $level): self
    {
        $this->level = max($level, MadelineProtoLogger::NOTICE);

        return $this;
    }

    /**
     * Get maximum filesize for logger, in case of file logging.
     */
    public function getMaxSize(): int
    {
        return $this->maxSize;
    }

    /**
     * Set maximum filesize for logger, in case of file logging.
     *
     * @param int $maxSize Maximum filesize for logger, in case of file logging.
     *
     * @psalm-external-mutation-free
     */
    public function setMaxSize(int $maxSize): self
    {
        $this->maxSize = $maxSize === -1 ? $maxSize : max($maxSize, 25 * 1024 * 1024);

        return $this;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Settings;

use danog\MadelineProto\SettingsAbstract;

/**
 * File management settings.
 *
 * @psalm-external-mutation-free
 */
final class Files extends SettingsAbstract
{
    /**
     * Allow automatic upload of files from file paths present in constructors?
     */
    protected bool $allowAutomaticUpload = true;
    /**
     * Upload parallel chunk count.
     */
    protected int $uploadParallelChunks = 20;
    /**
     * Download parallel chunk count.
     */
    protected int $downloadParallelChunks = 20;

    /**
     * Whether to report undownloadable media to TSF.
     */
    protected bool $reportBrokenMedia = true;

    /**
     * Custom download link URL for CLI bots, used by `getDownloadLink`.
     */
    protected ?string $downloadLink = null;

    /**
     * Whether to resume interrupted calls that upload files after a restart.
     */
    protected bool $resumeInterruptedCalls = true;

    /**
     * Whether files served to browsers can't run as pages of the website serving them.
     */
    protected bool $sandboxDownloads = true;

    /**
     * Get allow automatic upload of files from file paths present in constructors?
     */
    public function getAllowAutomaticUpload(): bool
    {
        return $this->allowAutomaticUpload;
    }

    /**
     * Set allow automatic upload of files from file paths present in constructors?
     *
     * @param bool $allowAutomaticUpload Allow automatic upload of files from file paths present in constructors?
     *
     * @psalm-external-mutation-free
     */
    public function setAllowAutomaticUpload(bool $allowAutomaticUpload): self
    {
        $this->allowAutomaticUpload = $allowAutomaticUpload;

        return $this;
    }

    /**
     * Get upload parallel chunk count.
     */
    public function getUploadParallelChunks(): int
    {
        return $this->uploadParallelChunks;
    }

    /**
     * Set upload parallel chunk count.
     *
     * @param int $uploadParallelChunks Upload parallel chunk count
     *
     * @psalm-external-mutation-free
     */
    public function setUploadParallelChunks(int $uploadParallelChunks): self
    {
        $this->uploadParallelChunks = $uploadParallelChunks;

        return $this;
    }

    /**
     * Get download parallel chunk count.
     */
    public function getDownloadParallelChunks(): int
    {
        return $this->downloadParallelChunks;
    }

    /**
     * Set download parallel chunk count.
     *
     * @param int $downloadParallelChunks Download parallel chunk count
     *
     * @psalm-external-mutation-free
     */
    public function setDownloadParallelChunks(int $downloadParallelChunks): self
    {
        $this->downloadParallelChunks = $downloadParallelChunks;

        return $this;
    }

    /**
     * Get whether to resume interrupted calls that upload files after a restart.
     */
    public function getResumeInterruptedCalls(): bool
    {
        return $this->resumeInterruptedCalls;
    }

    /**
     * Set whether to resume interrupted calls that upload files after a restart.
     *
     * If enabled, method calls and sendMedia calls (sendDocument, sendPhoto, ...) uploading a file that survives a restart
     * (a local file, a URL, a Telegram file or a resumable stream) are saved in the session until they complete:
     * if the session is restarted in the meantime, the call is made again once the session starts, resuming the interrupted upload.
     *
     * Disable it if your code already makes interrupted calls again after a restart, to avoid sending files twice.
     *
     * @param bool $resumeInterruptedCalls Whether to resume interrupted calls that upload files after a restart
     *
     * @psalm-external-mutation-free
     */
    public function setResumeInterruptedCalls(bool $resumeInterruptedCalls): self
    {
        $this->resumeInterruptedCalls = $resumeInterruptedCalls;

        return $this;
    }

    /**
     * Get whether files served to browsers can't run as pages of the website serving them.
     */
    public function getSandboxDownloads(): bool
    {
        return $this->sandboxDownloads;
    }

    /**
     * Set whether files served to browsers can't run as pages of the website serving them.
     *
     * Applies to the files served by the download script (getDownloadLink, downloadToBrowser) and by downloadToResponse:
     * their MIME type and name come from whoever sent them on Telegram, or from the query string of the download script.
     * If enabled, they're served with a sandboxing Content-Security-Policy and X-Content-Type-Options: nosniff, with
     * their MIME type normalized, and only media and plain text are shown inline (everything else is downloaded).
     *
     * Disable it only if the files served are trusted, and must be shown as pages of the website (HTML, SVG, PDF...).
     *
     * @param bool $sandboxDownloads Whether files served to browsers can't run as pages of the website serving them
     *
     * @psalm-external-mutation-free
     */
    public function setSandboxDownloads(bool $sandboxDownloads): self
    {
        $this->sandboxDownloads = $sandboxDownloads;

        return $this;
    }

    /**
     * Get whether to report undownloadable media to TSF.
     */
    public function getReportBrokenMedia(): bool
    {
        return $this->reportBrokenMedia;
    }

    /**
     * Set whether to report undownloadable media to TSF.
     *
     * @param bool $reportBrokenMedia Whether to report undownloadable media to TSF
     *
     * @psalm-external-mutation-free
     */
    public function setReportBrokenMedia(bool $reportBrokenMedia): self
    {
        $this->reportBrokenMedia = $reportBrokenMedia;

        return $this;
    }

    /**
     * Get custom download link URL for CLI bots, used by `getDownloadLink`.
     *
     */
    public function getDownloadLink(): ?string
    {
        return $this->downloadLink;
    }

    /**
     * Only needed for CLI bots, not bots started via web.
     *
     * Sets custom download link URL for CLI bots, used by `getDownloadLink`.
     *
     * Can be null, in which case MadelineProto will automatically generate a download link.
     *
     * @param ?string $downloadLink Custom download link URL for CLI bots, used by `getDownloadLink`.
     *
     * @psalm-external-mutation-free
     */
    public function setDownloadLink(?string $downloadLink): self
    {
        $this->downloadLink = $downloadLink;

        return $this;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use Amp\ByteStream\ReadableStream;
use Amp\Cancellation;
use Closure;
use Generator;

/**
 * A streaming Matroska/WebM demuxer, written entirely in PHP.
 *
 * WebM stores VP8/VP9 video and OPUS audio as bare, already-encoded frames, which is exactly what
 * Telegram calls put on the wire: MadelineProto can therefore play a `.webm` file without decoding
 * anything, and without needing FFI, ffmpeg or any shell command.
 *
 * Only the elements needed to pull frames out of a file are parsed: everything else is skipped.
 *
 * @internal
 */
final class Matroska
{
    // EBML element IDs, including their length marker, as they appear on disk.
    private const ID_SEGMENT = 0x18538067;
    private const ID_INFO = 0x1549A966;
    private const ID_TIMESTAMP_SCALE = 0x2AD7B1;
    private const ID_TRACKS = 0x1654AE6B;
    private const ID_TRACK_ENTRY = 0xAE;
    private const ID_TRACK_NUMBER = 0xD7;
    private const ID_TRACK_TYPE = 0x83;
    private const ID_CODEC_ID = 0x86;
    private const ID_CODEC_PRIVATE = 0x63A2;
    private const ID_CLUSTER = 0x1F43B675;
    private const ID_TIMESTAMP = 0xE7;
    private const ID_SIMPLE_BLOCK = 0xA3;
    private const ID_BLOCK_GROUP = 0xA0;
    private const ID_BLOCK = 0xA1;

    /** Elements whose children we walk into instead of skipping. */
    private const MASTER_ELEMENTS = [
        self::ID_SEGMENT => true,
        self::ID_INFO => true,
        self::ID_TRACKS => true,
        self::ID_TRACK_ENTRY => true,
        self::ID_CLUSTER => true,
        self::ID_BLOCK_GROUP => true,
    ];

    public const TRACK_TYPE_VIDEO = 1;
    public const TRACK_TYPE_AUDIO = 2;

    /**
     * Frames of the file, in storage order.
     *
     * Each entry is `['track' => int, 'codec' => string, 'type' => int, 'data' => string,
     * 'timestamp' => int (milliseconds), 'keyframe' => bool]`.
     *
     * @var iterable<array{track: int, codec: string, type: int, data: string, timestamp: int, keyframe: bool}>
     */
    public readonly iterable $frames;

    /**
     * Track metadata, keyed by track number.
     *
     * @var array<int, array{codec: string, type: int, private: ?string}>
     */
    public array $tracks = [];

    /** Duration of one timestamp unit, in nanoseconds. */
    private int $timestampScale = 1000000;

    /**
     * Byte offset of the read cursor: the resume start offset plus every byte consumed since.
     */
    private int $bytesConsumed = 0;

    /**
     * Byte offset of the Cluster element currently being read.
     *
     * A Cluster carries its own timestamp and is self-contained, so re-opening the file here with
     * the `$startOffset` constructor argument (and the track list, which lives once at the start of
     * the file) resumes reading exactly this cluster â€” which is what makes playback byte-offset
     * resumable across a serialize/unserialize cycle.
     */
    public int $unitOffset = 0;

    /** @var Closure(int): ?string Reads exactly the requested number of bytes, or null at EOF. */
    private Closure $read;
    private string $buffer = '';
    private bool $eof = false;

    /**
     * @param int $startOffset Byte offset to resume from; it must be the offset of a Cluster.
     * @param array<int, array{codec: string, type: int, private: ?string}> $resumeTracks The track
     *              list from the first open, which is only stored once at the start of the file.
     * @param int $resumeTimestampScale The timestamp scale from the first open (0 to keep the default).
     */
    public function __construct(
        LocalFile|RemoteUrl|ReadableStream $stream,
        ?Cancellation $cancellation = null,
        int $startOffset = 0,
        array $resumeTracks = [],
        int $resumeTimestampScale = 0,
    ) {
        $this->read = Tools::openBuffered($stream, $cancellation, $startOffset);
        $this->bytesConsumed = $startOffset;
        if ($startOffset > 0) {
            // Resuming inside the segment, past the header: the track list and timestamp scale were
            // read on the first open and are handed back here, since they are not repeated per cluster.
            $this->tracks = $resumeTracks;
            if ($resumeTimestampScale > 0) {
                $this->timestampScale = $resumeTimestampScale;
            }
        }
        $it = $this->read();
        // Prime the generator so that the track list is populated before the caller iterates.
        $it->current();
        $this->frames = $it;
    }

    /**
     * The timestamp scale (nanoseconds per tick) declared by the file, needed to resume it.
     */
    public function getTimestampScale(): int
    {
        return $this->timestampScale;
    }

    /**
     * Whether the file declares a track using the given codec.
     *
     * @psalm-mutation-free
     */
    public function hasCodec(string $codec): bool
    {
        foreach ($this->tracks as $track) {
            if ($track['codec'] === $codec) {
                return true;
            }
        }
        return false;
    }

    /**
     * Make sure at least `$length` bytes are buffered, reading more from the stream if needed.
     */
    private function fill(int $length): bool
    {
        while (\strlen($this->buffer) < $length && !$this->eof) {
            $chunk = ($this->read)($length - \strlen($this->buffer));
            if ($chunk === null || $chunk === '') {
                $this->eof = true;
                break;
            }
            $this->buffer .= $chunk;
        }
        return \strlen($this->buffer) >= $length;
    }

    private function consume(int $length): string
    {
        $this->fill($length);
        $data = substr($this->buffer, 0, $length);
        $this->buffer = substr($this->buffer, \strlen($data));
        $this->bytesConsumed += \strlen($data);
        return $data;
    }

    /**
     * Read an EBML variable length integer.
     *
     * @param bool $keepMarker Element IDs keep the length marker, sizes strip it.
     * @return array{int, int}|null The value and how many bytes it occupied, or null at EOF.
     */
    private function readVint(bool $keepMarker): ?array
    {
        if (!$this->fill(1)) {
            return null;
        }
        $first = \ord($this->buffer[0]);
        if ($first === 0) {
            return null;
        }
        $length = 1;
        for ($mask = 0x80; $mask > 0 && !($first & $mask); $mask >>= 1) {
            $length++;
        }
        if ($length > 8 || !$this->fill($length)) {
            return null;
        }
        $raw = $this->consume($length);

        $value = \ord($raw[0]);
        if (!$keepMarker) {
            // Clear the marker bit that indicates the length.
            $value &= (1 << (8 - $length)) - 1;
        }
        for ($i = 1; $i < $length; $i++) {
            $value = ($value << 8) | \ord($raw[$i]);
        }
        return [$value, $length];
    }

    /**
     * Walk the file, yielding every frame found in a cluster.
     *
     * @return Generator<array{track: int, codec: string, type: int, data: string, timestamp: int, keyframe: bool}>
     */
    private function read(): Generator
    {
        $clusterTimestamp = 0;
        /** @var array{number: ?int, codec: ?string, type: ?int, private: ?string} $pending */
        $pending = ['number' => null, 'codec' => null, 'type' => null, 'private' => null];

        while (true) {
            // The byte offset of this element, recorded before its ID is read so a Cluster's offset
            // is the offset a resume can seek back to.
            $elementStart = $this->bytesConsumed;
            $id = $this->readVint(true);
            if ($id === null) {
                break;
            }
            $size = $this->readVint(false);
            if ($size === null) {
                break;
            }
            [$elementId] = $id;
            [$elementSize] = $size;

            if ($elementId === self::ID_CLUSTER) {
                $this->unitOffset = $elementStart;
            }

            // An "unknown size" master element (all size bits set) is streamed: walk into it.
            $unknownSize = $elementSize === (1 << (7 * $size[1])) - 1;

            if (isset(self::MASTER_ELEMENTS[$elementId])) {
                if ($elementId === self::ID_TRACK_ENTRY) {
                    $pending = ['number' => null, 'codec' => null, 'type' => null, 'private' => null];
                }
                continue;
            }

            if ($unknownSize) {
                continue;
            }

            switch ($elementId) {
                case self::ID_TIMESTAMP_SCALE:
                    $this->timestampScale = self::toInt($this->consume($elementSize));
                    break;
                case self::ID_TRACK_NUMBER:
                    $pending['number'] = self::toInt($this->consume($elementSize));
                    $this->flushTrack($pending);
                    break;
                case self::ID_TRACK_TYPE:
                    $pending['type'] = self::toInt($this->consume($elementSize));
                    $this->flushTrack($pending);
                    break;
                case self::ID_CODEC_ID:
                    $pending['codec'] = rtrim($this->consume($elementSize), "\0");
                    $this->flushTrack($pending);
                    break;
                case self::ID_CODEC_PRIVATE:
                    // Codec setup data, such as the AVCDecoderConfigurationRecord of an H.264
                    // track: some codecs cannot be decoded at all without it.
                    $pending['private'] = $this->consume($elementSize);
                    $this->flushTrack($pending);
                    break;
                case self::ID_TIMESTAMP:
                    $clusterTimestamp = self::toInt($this->consume($elementSize));
                    break;
                case self::ID_SIMPLE_BLOCK:
                case self::ID_BLOCK:
                    $block = $this->consume($elementSize);
                    foreach ($this->parseBlock($block, $clusterTimestamp, $elementId === self::ID_SIMPLE_BLOCK) as $frame) {
                        yield $frame;
                    }
                    break;
                default:
                    // Not interesting: skip the payload entirely.
                    $this->skip($elementSize);
            }

            if ($this->eof && $this->buffer === '') {
                break;
            }
        }
    }

    /**
     * Register a track once all three of its mandatory fields are known.
     *
     * Called again for every field, so a `CodecPrivate` that arrives after the codec ID simply
     * updates the entry that is already there.
     *
     * @param array{number: ?int, codec: ?string, type: ?int, private: ?string} $pending
     *
     * @psalm-external-mutation-free
     */
    private function flushTrack(array $pending): void
    {
        if ($pending['number'] === null || $pending['codec'] === null || $pending['type'] === null) {
            return;
        }
        $this->tracks[$pending['number']] = [
            'codec' => $pending['codec'],
            'type' => $pending['type'],
            'private' => $pending['private'],
        ];
    }

    /**
     * Discard `$length` bytes without buffering them all at once.
     */
    private function skip(int $length): void
    {
        while ($length > 0) {
            if ($this->buffer !== '') {
                $take = min($length, \strlen($this->buffer));
                $this->buffer = substr($this->buffer, $take);
                $this->bytesConsumed += $take;
                $length -= $take;
                continue;
            }
            if (!$this->fill(min($length, 65536))) {
                return;
            }
        }
    }

    /**
     * Split a (Simple)Block into its frames.
     *
     * @return list<array{track: int, codec: string, type: int, data: string, timestamp: int, keyframe: bool}>
     */
    private function parseBlock(string $block, int $clusterTimestamp, bool $simple): array
    {
        $offset = 0;
        $trackNumber = self::readBlockVint($block, $offset);
        if ($trackNumber === null || \strlen($block) < $offset + 3) {
            return [];
        }
        // A signed 16 bit offset from the cluster timestamp.
        /** @var int $relative */
        $relative = unpack('n', substr($block, $offset, 2))[1];
        if ($relative >= 0x8000) {
            $relative -= 0x10000;
        }
        $offset += 2;
        $flags = \ord($block[$offset++]);
        // For a BlockGroup the keyframe flag lives in the group, not the block; treating those
        // frames as non-key is safe, the decoder simply waits for the next real keyframe.
        $keyframe = $simple && ($flags & 0x80) !== 0;
        $lacing = ($flags >> 1) & 0x03;

        $payload = substr($block, $offset);
        $frames = $lacing === 0 ? [$payload] : self::unlace($payload, $lacing);

        $track = $this->tracks[$trackNumber] ?? ['codec' => '', 'type' => 0, 'private' => null];
        $timestamp = (int) (($clusterTimestamp + $relative) * $this->timestampScale / 1000000);

        $result = [];
        foreach ($frames as $frame) {
            if ($frame === '') {
                continue;
            }
            $result[] = [
                'track' => $trackNumber,
                'codec' => $track['codec'],
                'type' => $track['type'],
                'data' => $frame,
                'timestamp' => $timestamp,
                'keyframe' => $keyframe,
            ];
        }
        return $result;
    }

    /**
     * Undo Xiph, fixed size or EBML lacing.
     *
     * @return list<string>
     */
    private static function unlace(string $payload, int $lacing): array
    {
        if ($payload === '') {
            return [];
        }
        $offset = 0;
        $count = \ord($payload[$offset++]) + 1;
        $sizes = [];

        if ($lacing === 2) {
            // Fixed size lacing: every frame has the same length.
            $remaining = \strlen($payload) - $offset;
            $size = intdiv($remaining, $count);
            $sizes = array_fill(0, $count, $size);
        } elseif ($lacing === 1) {
            // Xiph lacing: sizes are encoded as a run of 255 bytes terminated by a smaller one.
            for ($i = 0; $i < $count - 1; $i++) {
                $size = 0;
                while ($offset < \strlen($payload)) {
                    $byte = \ord($payload[$offset++]);
                    $size += $byte;
                    if ($byte !== 255) {
                        break;
                    }
                }
                $sizes[] = $size;
            }
        } else {
            // EBML lacing: the first size is absolute, the rest are signed deltas.
            $first = self::readBlockVint($payload, $offset, false);
            if ($first === null) {
                return [$payload];
            }
            $sizes[] = $first;
            $previous = $first;
            for ($i = 1; $i < $count - 1; $i++) {
                $delta = self::readSignedBlockVint($payload, $offset);
                if ($delta === null) {
                    break;
                }
                $previous += $delta;
                $sizes[] = $previous;
            }
        }

        $frames = [];
        foreach ($sizes as $size) {
            $frames[] = substr($payload, $offset, $size);
            $offset += $size;
        }
        if ($lacing !== 2) {
            // The last frame takes whatever is left.
            $frames[] = substr($payload, $offset);
        }
        return $frames;
    }

    /**
     * Read a variable length integer out of an in-memory block.
     */
    private static function readBlockVint(string $data, int &$offset, bool $keepMarker = false): ?int
    {
        if ($offset >= \strlen($data)) {
            return null;
        }
        $first = \ord($data[$offset]);
        if ($first === 0) {
            return null;
        }
        $length = 1;
        for ($mask = 0x80; $mask > 0 && !($first & $mask); $mask >>= 1) {
            $length++;
        }
        if ($length > 8 || $offset + $length > \strlen($data)) {
            return null;
        }
        $value = $keepMarker ? $first : ($first & ((1 << (8 - $length)) - 1));
        for ($i = 1; $i < $length; $i++) {
            $value = ($value << 8) | \ord($data[$offset + $i]);
        }
        $offset += $length;
        return $value;
    }

    /**
     * Read a signed variable length integer, as used by EBML lacing deltas.
     */
    private static function readSignedBlockVint(string $data, int &$offset): ?int
    {
        $start = $offset;
        $value = self::readBlockVint($data, $offset);
        if ($value === null) {
            return null;
        }
        $length = $offset - $start;
        // The range is shifted so that it can express negative deltas.
        return $value - ((1 << (7 * $length - 1)) - 1);
    }

    /**
     * Decode a big endian unsigned integer of any width.
     *
     * @psalm-pure
     */
    private static function toInt(string $data): int
    {
        $value = 0;
        for ($i = 0, $length = \strlen($data); $i < $length; $i++) {
            $value = ($value << 8) | \ord($data[$i]);
        }
        return $value;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

/** @internal */
/** @psalm-mutable */
interface SettingsGetter
{
    /** @psalm-impure */
    public function getSettings(): Settings;
}
<?php

declare(strict_types=1);

/**
 * Referenced timeout cancellation.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2026 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use Amp\Cancellation;
use Amp\DeferredCancellation;
use Amp\TimeoutException;
use Revolt\EventLoop;

/**
 * Timeout cancellation whose timer keeps the event loop alive.
 *
 * Unlike Amp\TimeoutCancellation, the timer is *referenced*: some callers
 * (i.e. the IPC connection polling loop) await futures that can only complete
 * through this timeout, so an unreferenced timer would let the event loop
 * terminate underneath them.
 *
 * The timer is cancelled when the cancellation is destroyed, so it does not
 * keep the event loop alive after the awaited operation has completed.
 *
 * @internal
 */
final class ReferencedTimeoutCancellation implements Cancellation
{
    private readonly DeferredCancellation $deferred;
    private readonly string $watcher;

    public function __construct(float $timeout, string $message = "Operation timed out")
    {
        $deferred = $this->deferred = new DeferredCancellation;
        $this->watcher = EventLoop::delay(
            $timeout,
            static fn () => $deferred->cancel(new TimeoutException($message)),
        );
    }

    public function __destruct()
    {
        EventLoop::cancel($this->watcher);
    }

    #[\Override]
    public function subscribe(\Closure $callback): string
    {
        return $this->deferred->getCancellation()->subscribe($callback);
    }

    #[\Override]
    public function unsubscribe(string $id): void
    {
        $this->deferred->getCancellation()->unsubscribe($id);
    }

    #[\Override]
    public function isRequested(): bool
    {
        return $this->deferred->isCancelled();
    }

    #[\Override]
    public function throwIfRequested(): void
    {
        $this->deferred->getCancellation()->throwIfRequested();
    }
}
<?php

declare(strict_types=1);

/**
 * Raw stream interface.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Stream;

use Amp\ByteStream\ClosedException;
use Amp\ByteStream\PendingReadError;
use Amp\ByteStream\StreamException;
use Amp\Cancellation;

/**
 * Raw stream interface.
 *
 * @author Daniil Gentili <daniil@daniil.it>
 * @psalm-mutable
 */
interface RawStreamInterface extends StreamInterface
{
    /**
     * Reads data from the stream.
     *
     * @param Cancellation|null $cancellation Cancel the read operation. The state in which the stream will be after
     *                                        a cancelled operation is implementation dependent.
     *
     * @return string|null Returns a string when new data is available or {@code null} if the stream has closed.
     *
     * @throws PendingReadError Thrown if another read operation is still pending.
     * @throws StreamException  If the stream contains invalid data, e.g. invalid compression
     * @psalm-impure
     */
    public function read(?Cancellation $cancellation = null): ?string;
    /**
     * Writes data to the stream.
     *
     * @param string $bytes Bytes to write.
     *
     * @throws ClosedException If the stream has already been closed.
     * @throws StreamException If writing to the stream fails.
     * @psalm-impure
     */
    public function write(string $bytes): void;
}
<?php

declare(strict_types=1);

/**
 * Generic stream interface.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Stream;

use Amp\Socket\Socket;

/**
 * Generic stream interface.
 *
 * @author Daniil Gentili <daniil@daniil.it>
 * @psalm-mutable
 */
interface StreamInterface
{
    /**
     * Connect to a server.
     *
     * @param ConnectionContext $ctx The connection context
     * @psalm-impure
     */
    public function connect(ConnectionContext $ctx, string $header = ''): void;
    /**
     * Disconnect from the server.
     * @psalm-impure
     */
    public function disconnect(): void;
    /**
     * Get underlying AMPHP socket resource.
     * @psalm-impure
     */
    public function getSocket(): Socket;
}
<?php

declare(strict_types=1);

/**
 * MTProto buffer interface.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Stream;

/**
 * MTProto buffer interface, for reading transport MTProto header info.
 *
 * @author Daniil Gentili <daniil@daniil.it>
 * @psalm-mutable
 */
interface MTProtoBufferInterface
{
}
<?php

declare(strict_types=1);

/**
 * Buffer interface.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Stream;

/**
 * Buffer interface.
 *
 * @author Daniil Gentili <daniil@daniil.it>
 * @psalm-mutable
 */
interface BufferInterface extends ReadBufferInterface, WriteBufferInterface
{
}
<?php

declare(strict_types=1);

/**
 * Generic stream proxy interface.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Stream;

/**
 * Stream proxy interface.
 *
 * @author Daniil Gentili <daniil@daniil.it>
 *
 * @template T
 * @psalm-mutable
 */
interface ProxyStreamInterface
{
    /**
     * Set extra proxy data.
     *
     * @param T $extra Proxy data
     * @psalm-impure
     */
    public function setExtra($extra): void;
}
<?php

declare(strict_types=1);

/**
 * Buffer interface.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Stream;

/**
 * Write buffer interface.
 *
 * @author Daniil Gentili <daniil@daniil.it>
 * @psalm-mutable
 */
interface WriteBufferInterface
{
    /**
     * Write data asynchronously.
     *
     * @param string $data Data to write
     * @psalm-impure
     */
    public function bufferWrite(string $data): void;
}
<?php declare(strict_types=1);

namespace danog\MadelineProto\Stream;

use IteratorAggregate;
use Traversable;

/**
 * @internal
 *
 * @implements IteratorAggregate<int, ConnectionContext>
 */
final class ContextIterator implements IteratorAggregate
{
    /**
     * @psalm-mutation-free
     */
    public function __construct(
        /** @var non-empty-list<ConnectionContext> */
        private readonly array $ctxs
    ) {
    }

    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function getIterator(): Traversable
    {
        foreach ($this->ctxs as $ctx) {
            yield $ctx->clone();
        }
    }
}
<?php

declare(strict_types=1);

/**
 * Buffered stream interface.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Stream;

/**
 * Buffered stream interface.
 *
 * @author Daniil Gentili <daniil@daniil.it>
 * @psalm-mutable
 */
interface BufferedStreamInterface extends StreamInterface
{
    /**
     * Get read buffer asynchronously.
     *
     * @param int $length Length of payload, as detected by this layer
     * @psalm-impure
     */
    public function getReadBuffer(?int &$length): ReadBufferInterface;
    /**
     * Get write buffer asynchronously.
     *
     * @param int $length Total length of data that is going to be piped in the buffer
     * @psalm-impure
     */
    public function getWriteBuffer(int $length, string $append = ''): WriteBufferInterface;
    /**
     * Get stream name.
     *
     * Is supposed to return __CLASS__
     * @psalm-impure
     */
    public static function getName(): string;
    /**
     * Get underlying stream resource.
     * @psalm-impure
     */
    public function getStream(): RawStreamInterface;
}
<?php

declare(strict_types=1);

/**
 * Buffer interface.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Stream;

use Amp\Cancellation;

/**
 * Read buffer interface.
 *
 * @author Daniil Gentili <daniil@daniil.it>
 * @psalm-mutable
 */
interface ReadBufferInterface
{
    /**
     * Read data asynchronously.
     *
     * @param int $length How much data to read
     * @psalm-impure
     */
    public function bufferRead(int $length, ?Cancellation $cancellation = null): ?string;
}
<?php

declare(strict_types=1);

/**
 * TCP Intermediate stream wrapper.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Stream\MTProtoTransport;

use Amp\Socket\Socket;
use danog\MadelineProto\Stream\BufferedStreamInterface;
use danog\MadelineProto\Stream\ConnectionContext;
use danog\MadelineProto\Stream\MTProtoBufferInterface;
use danog\MadelineProto\Stream\RawStreamInterface;
use danog\MadelineProto\Tools;

/**
 * TCP Intermediate stream wrapper.
 *
 * Manages obfuscated2 encryption/decryption
 *
 * @author Daniil Gentili <daniil@daniil.it>
 */
final class IntermediatePaddedStream implements BufferedStreamInterface, MTProtoBufferInterface
{
    private $stream;
    /**
     * Connect to stream.
     *
     * @param ConnectionContext $ctx The connection context
     */
    #[\Override]
    public function connect(ConnectionContext $ctx, string $header = ''): void
    {
        $this->stream = ($ctx->getStream(str_repeat(\chr(221), 4).$header));
    }
    /**
     * Async close.
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function disconnect(): void
    {
        $this->stream->disconnect();
    }
    /**
     * Get write buffer asynchronously.
     *
     * @param int $length Length of data that is going to be written to the write buffer
     */
    #[\Override]
    public function getWriteBuffer(int $length, string $append = ''): \danog\MadelineProto\Stream\WriteBufferInterface
    {
        $padding_length = Tools::randomInt(modulus: 16);
        $buffer = $this->stream->getWriteBuffer(4 + $length + $padding_length, $append.Tools::random($padding_length));
        //$buffer->bufferWrite(pack('V', ($padding_length + $length) | (1 << 31)));
        $buffer->bufferWrite(pack('V', $padding_length + $length));
        return $buffer;
    }
    /**
     * Get read buffer asynchronously.
     *
     * @param int $length Length of payload, as detected by this layer
     */
    #[\Override]
    public function getReadBuffer(?int &$length): \danog\MadelineProto\Stream\ReadBufferInterface
    {
        $buffer = $this->stream->getReadBuffer($l);
        $length = unpack('V', $buffer->bufferRead(4))[1];
        return $buffer;
    }
    /**
     * {@inheritdoc}
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function getSocket(): Socket
    {
        return $this->stream->getSocket();
    }
    /**
     * {@inheritDoc}
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function getStream(): RawStreamInterface
    {
        return $this->stream;
    }
    /**
     * @psalm-pure
     */
    #[\Override]
    public static function getName(): string
    {
        return self::class;
    }
}
<?php

declare(strict_types=1);

/**
 * HTTPS stream wrapper.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Stream\MTProtoTransport;

use danog\MadelineProto\Stream\ConnectionContext;
use danog\MadelineProto\Stream\MTProtoBufferInterface;
use danog\MadelineProto\Stream\RawStreamInterface;

/**
 * HTTPS stream wrapper.
 *
 * @author Daniil Gentili <daniil@daniil.it>
 */
final class HttpsStream extends HttpStream implements MTProtoBufferInterface
{
    /**
     * Connect to stream.
     *
     * @param ConnectionContext $ctx The connection context
     */
    #[\Override]
    public function connect(ConnectionContext $ctx, string $header = ''): void
    {
        parent::connect($ctx->clone()->secure(true), $header);
    }
    /**
     * {@inheritDoc}
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function getStream(): RawStreamInterface
    {
        return $this->stream;
    }
    /**
     * @psalm-pure
     */
    #[\Override]
    public static function getName(): string
    {
        return self::class;
    }
}
<?php

declare(strict_types=1);

/**
 * TCP Intermediate stream wrapper.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Stream\MTProtoTransport;

use Amp\Socket\Socket;
use danog\MadelineProto\Stream\BufferedStreamInterface;
use danog\MadelineProto\Stream\ConnectionContext;
use danog\MadelineProto\Stream\MTProtoBufferInterface;
use danog\MadelineProto\Stream\RawStreamInterface;

/**
 * TCP Intermediate stream wrapper.
 *
 * Manages obfuscated2 encryption/decryption
 *
 * @author Daniil Gentili <daniil@daniil.it>
 */
final class IntermediateStream implements BufferedStreamInterface, MTProtoBufferInterface
{
    private $stream;
    /**
     * Connect to stream.
     *
     * @param ConnectionContext $ctx The connection context
     */
    #[\Override]
    public function connect(ConnectionContext $ctx, string $header = ''): void
    {
        $this->stream = ($ctx->getStream(str_repeat(\chr(238), 4).$header));
    }
    /**
     * Async close.
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function disconnect(): void
    {
        $this->stream->disconnect();
    }
    /**
     * Get write buffer asynchronously.
     *
     * @param int $length Length of data that is going to be written to the write buffer
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function getWriteBuffer(int $length, string $append = ''): \danog\MadelineProto\Stream\WriteBufferInterface
    {
        $buffer = $this->stream->getWriteBuffer($length + 4, $append);
        //$buffer->bufferWrite(pack('V', ($length) | (1 << 31)));
        $buffer->bufferWrite(pack('V', $length));
        return $buffer;
    }
    /**
     * Get read buffer asynchronously.
     *
     * @param int $length Length of payload, as detected by this layer
     */
    #[\Override]
    public function getReadBuffer(?int &$length): \danog\MadelineProto\Stream\ReadBufferInterface
    {
        $buffer = $this->stream->getReadBuffer($l);
        $length = unpack('V', $buffer->bufferRead(4))[1];
        return $buffer;
    }
    /**
     * {@inheritdoc}
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function getSocket(): Socket
    {
        return $this->stream->getSocket();
    }
    /**
     * {@inheritDoc}
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function getStream(): RawStreamInterface
    {
        return $this->stream;
    }
    /**
     * @psalm-pure
     */
    #[\Override]
    public static function getName(): string
    {
        return self::class;
    }
}
<?php

declare(strict_types=1);

/**
 * HTTP stream wrapper.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Stream\MTProtoTransport;

use Amp\Cancellation;
use Amp\Socket\Socket;
use danog\MadelineProto\Exception;
use danog\MadelineProto\Logger;
use danog\MadelineProto\Stream\BufferedProxyStreamInterface;
use danog\MadelineProto\Stream\BufferedStreamInterface;
use danog\MadelineProto\Stream\ConnectionContext;
use danog\MadelineProto\Stream\MTProtoBufferInterface;
use danog\MadelineProto\Stream\RawStreamInterface;
use danog\MadelineProto\Stream\ReadBufferInterface;
use danog\MadelineProto\Tools;
use Psr\Http\Message\UriInterface;

/**
 * HTTP stream wrapper.
 *
 * @author Daniil Gentili <daniil@daniil.it>
 *
 * @implements BufferedProxyStreamInterface<array{user?: string, password?: string}>
 */
class HttpStream implements MTProtoBufferInterface, BufferedProxyStreamInterface, ReadBufferInterface
{
    /**
     * Stream.
     *
     */
    protected BufferedStreamInterface&RawStreamInterface $stream;
    private $code;
    private $ctx;
    private $header = '';
    /**
     * URI of the HTTP API.
     */
    private UriInterface $uri;
    /**
     * Connect to stream.
     *
     * @param ConnectionContext $ctx The connection context
     */
    #[\Override]
    public function connect(ConnectionContext $ctx, string $header = ''): void
    {
        $this->ctx = $ctx->clone();
        $this->stream = ($ctx->getStream($header));
        $this->uri = $ctx->getUri();
    }
    /**
     * Set proxy data.
     *
     * @param array $extra Proxy parameters
     *
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function setExtra($extra): void
    {
        if (isset($extra['user']) && isset($extra['password'])) {
            $this->header = base64_encode($extra['user'].':'.$extra['password'])."\r\n";
        }
    }
    /**
     * Async close.
     */
    #[\Override]
    public function disconnect(): void
    {
        $this->stream->disconnect();
    }
    /**
     * Get write buffer asynchronously.
     *
     * @param int $length Length of data that is going to be written to the write buffer
     */
    #[\Override]
    public function getWriteBuffer(int $length, string $append = ''): \danog\MadelineProto\Stream\WriteBufferInterface
    {
        $headers = 'POST '.$this->uri->getPath()." HTTP/1.1\r\nHost: ".$this->uri->getHost().':'.$this->uri->getPort()."\r\n"."Content-Type: application/x-www-form-urlencoded\r\nConnection: keep-alive\r\nKeep-Alive: timeout=100000, max=10000000\r\nContent-Length: ".$length.$this->header."\r\n\r\n";
        $buffer = $this->stream->getWriteBuffer(\strlen($headers) + $length, $append);
        $buffer->bufferWrite($headers);
        return $buffer;
    }
    /**
     * Get read buffer asynchronously.
     *
     * @param int $length Length of payload, as detected by this layer
     */
    #[\Override]
    public function getReadBuffer(?int &$length): ReadBufferInterface
    {
        $buffer = $this->stream->getReadBuffer($l);
        $headers = '';
        $was_crlf = false;
        while (true) {
            $piece = $buffer->bufferRead(2);
            $headers .= $piece;
            if ($piece === "\n\r") {
                // Assume end of headers with \r\n\r\n
                $headers .= $buffer->bufferRead(1);
                break;
            }
            if ($was_crlf && $piece === "\r\n") {
                break;
            }
            $was_crlf = $piece === "\r\n";
        }
        $headers = explode("\r\n", $headers);
        [$protocol, $code, $description] = explode(' ', $headers[0], 3);
        [$protocol, $protocol_version] = explode('/', $protocol);
        if ($protocol !== 'HTTP') {
            throw new Exception('Wrong protocol');
        }
        $code = (int) $code;
        unset($headers[0]);
        if (array_pop($headers).array_pop($headers) !== '') {
            throw new Exception('Wrong last header');
        }
        foreach ($headers as $key => $current_header) {
            unset($headers[$key]);
            $current_header = explode(':', $current_header, 2);
            $headers[strtolower($current_header[0])] = trim($current_header[1]);
        }
        $close = $protocol_version === '1.0';
        if (isset($headers['connection'])) {
            $close = strtolower($headers['connection']) === 'close';
        }
        if ($code !== 200) {
            $read = '';
            if (isset($headers['content-length'])) {
                $read = $buffer->bufferRead((int) $headers['content-length']);
            }
            if ($close) {
                $this->disconnect();
                $this->connect($this->ctx);
            }
            Logger::log($read);
            $this->code = Tools::packSignedInt(-$code);
            $length = 4;
            return $this;
        }
        if ($close) {
            $this->stream->disconnect();
            $this->stream->connect($this->ctx);
        }
        if (isset($headers['content-length'])) {
            $length = (int) $headers['content-length'];
        }
        return $buffer;
    }
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function bufferRead(int $length, ?Cancellation $cancellation = null): string
    {
        return $this->code;
    }
    /**
     * {@inheritdoc}
     */
    #[\Override]
    public function getSocket(): Socket
    {
        return $this->stream->getSocket();
    }
    /**
     * {@inheritDoc}
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function getStream(): RawStreamInterface
    {
        return $this->stream;
    }
    /**
     * @psalm-pure
     */
    #[\Override]
    public static function getName(): string
    {
        return self::class;
    }
}
<?php

declare(strict_types=1);

/**
 * Fake TLS stream wrapper.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Stream\MTProtoTransport;

use Amp\Cancellation;
use Amp\Socket\Socket;
use danog\MadelineProto\Exception;
use danog\MadelineProto\Stream\BufferedProxyStreamInterface;
use danog\MadelineProto\Stream\BufferedStreamInterface;
use danog\MadelineProto\Stream\BufferInterface;
use danog\MadelineProto\Stream\ConnectionContext;
use danog\MadelineProto\Stream\RawStreamInterface;
use danog\MadelineProto\Tools;
use phpseclib4\Math\BigInteger;

/**
 * Fake TLS stream wrapper, for MTProxy secrets starting with ee.
 *
 * Sits below ObfuscatedStream: sends a TLS 1.3 ClientHello for the domain of the secret, checks the
 * ServerHello of the proxy, then carries the obfuscated2 stream in TLS application data records.
 * Follows TDLib (td/mtproto/TlsInit.cpp, TcpTransport.cpp, TlsReaderByteFlow.cpp).
 *
 * @author Daniil Gentili <daniil@daniil.it>
 *
 * @implements BufferedProxyStreamInterface<array{secret: string, address?: string, port?: int}>
 */
final class FakeTlsStream implements BufferedProxyStreamInterface, BufferInterface, RawStreamInterface
{
    /**
     * Maximum size of the payload of the application data records we send.
     */
    public const MAX_TLS_PACKET_LENGTH = 2878;

    private RawStreamInterface&BufferedStreamInterface $stream;
    private string $key;
    private string $domain;
    /**
     * Data written with write() (the obfuscated2 header), sent in the same record as the first frame.
     */
    private string $header = '';
    private bool $firstRecord = true;
    private string $writeBuffer = '';
    private int $writeLength = 0;
    private string $append = '';
    private string $readBuffer = '';

    /**
     * Length of the ECH payload, chosen once per process like TDLib's default hello.
     */
    private static ?int $echLength = null;
    private static ?BigInteger $mod = null;

    /**
     * Connect to stream.
     *
     * @param ConnectionContext $ctx The connection context
     */
    #[\Override]
    public function connect(ConnectionContext $ctx, string $header = ''): void
    {
        $this->stream = $ctx->getStream($header);
        $hello = self::generateClientHello($this->domain, $this->key, time());
        $this->stream->write($hello);

        $buffer = $this->stream->getReadBuffer($l);
        $response = '';
        foreach (["\x16\x03\x03", "\x14\x03\x03\x00\x01\x01\x17\x03\x03"] as $prefix) {
            $recordHeader = $buffer->bufferRead(\strlen($prefix) + 2, $ctx->getCancellation());
            if (substr($recordHeader, 0, -2) !== $prefix) {
                throw new Exception('First part of response to hello is invalid');
            }
            $length = unpack('n', substr($recordHeader, -2))[1];
            $response .= $recordHeader.($length ? $buffer->bufferRead($length, $ctx->getCancellation()) : '');
        }
        if (!self::checkServerHello($this->key, substr($hello, 11, 32), $response)) {
            throw new Exception('Response hash mismatch');
        }
    }

    /**
     * Generate a ClientHello as TDLib's default (non-Darwin) one.
     *
     * Its random is HMAC-SHA256(key, hello with a zero random), with the last 4 bytes XORed with the
     * unix time (little endian).
     *
     * @internal
     */
    public static function generateClientHello(string $domain, string $key, int $time): string
    {
        $grease = array_map(static fn (int $c): int => ($c & 0xF0) + 0x0A, array_values(unpack('C*', Tools::random(7))));
        for ($i = 1; $i < 7; $i += 2) {
            if ($grease[$i] === $grease[$i - 1]) {
                $grease[$i] ^= 0x10;
            }
        }
        $grease = array_map(static fn (int $c): string => str_repeat(\chr($c), 2), $grease);
        self::$echLength ??= Tools::randomInt(modulus: 4) * 32 + 144;

        $extensions = [
            "\x00\x00".self::scope(self::scope("\x00".self::scope($domain))),
            "\x00\x05\x00\x05\x01\x00\x00\x00\x00",
            "\x00\x0a\x00\x0c\x00\x0a".$grease[4]."\x11\xec\x00\x1d\x00\x17\x00\x18",
            "\x00\x0b\x00\x02\x01\x00",
            "\x00\x0d\x00\x18\x00\x16\x09\x04\x09\x05\x09\x06\x04\x03\x08\x04\x04\x01\x05\x03\x08\x05\x05\x01\x08\x06\x06\x01",
            "\x00\x10\x00\x0e\x00\x0c\x02\x68\x32\x08\x68\x74\x74\x70\x2f\x31\x2e\x31",
            "\x00\x12\x00\x00",
            "\x00\x17\x00\x00",
            "\x00\x1b\x00\x03\x02\x00\x02",
            "\x00\x23\x00\x00",
            "\x00\x2b\x00\x07\x06".$grease[6]."\x03\x04\x03\x03",
            "\x00\x2d\x00\x02\x01\x01",
            "\x00\x33\x04\xef\x04\xed".$grease[4]."\x00\x01\x00\x11\xec\x04\xc0".self::mlKem768Key().self::key()."\x00\x1d\x00\x20".self::key(),
            "\x44\xcd\x00\x05\x00\x03\x02\x68\x32",
            "\xfe\x0d".self::scope("\x00\x00\x01\x00\x01".Tools::random(1)."\x00\x20".self::key().self::scope(Tools::random(self::$echLength))),
            "\xff\x01\x00\x01\x00",
        ];
        shuffle($extensions);
        $extensions = $grease[2]."\x00\x00".implode('', $extensions).$grease[3]."\x00\x01\x00";

        $prefix = "\x16\x03\x01\x00\x00\x01\x00\x00\x00\x03\x03".str_repeat("\0", 32)."\x20".Tools::random(32)
            ."\x00\x20".$grease[0]."\x13\x01\x13\x02\x13\x03\xc0\x2b\xc0\x2f\xc0\x2c\xc0\x30\xcc\xa9\xcc\xa8\xc0\x13\xc0\x14\x00\x9c\x00\x9d\x00\x2f\x00\x35\x01\x00"
            ."\x00\x00";
        // Pad to 517 bytes the hellos shorter than 513 bytes (with a post-quantum key share, none is).
        $padding = 513 - \strlen($prefix) - \strlen($extensions);
        if ($padding > 0) {
            $extensions .= "\x00\x15".self::scope(str_repeat("\0", $padding));
        }

        $hello = substr($prefix, 0, -2).self::scope($extensions);
        $hello = substr_replace($hello, pack('n', \strlen($hello) - 5), 3, 2);
        $hello = substr_replace($hello, pack('n', \strlen($hello) - 9), 7, 2);

        $random = hash_hmac('sha256', $hello, $key, true);
        $random = substr($random, 0, 28).pack('V', unpack('V', substr($random, 28))[1] ^ ($time & 0xFFFFFFFF));
        return substr_replace($hello, $random, 11, 32);
    }

    /**
     * Check the random of the response of the proxy to a ClientHello (its records up to the first
     * application data one): it must be HMAC-SHA256(key, client random || response with a zero random).
     *
     * @internal
     */
    public static function checkServerHello(string $key, string $clientRandom, string $response): bool
    {
        if (\strlen($response) < 11 + 32) {
            return false;
        }
        $serverRandom = substr($response, 11, 32);
        $response = substr_replace($response, str_repeat("\0", 32), 11, 32);
        return hash_equals(hash_hmac('sha256', $clientRandom.$response, $key, true), $serverRandom);
    }

    private static function scope(string $data): string
    {
        return pack('n', \strlen($data)).$data;
    }

    /**
     * A random ML-KEM-768 encapsulation key: 384 pairs of 12-bit coefficients modulo 3329, then 32 random bytes.
     */
    private static function mlKem768Key(): string
    {
        $key = '';
        $random = array_values(unpack('V*', Tools::random(384 * 2 * 4)));
        for ($i = 0; $i < 384 * 2; $i += 2) {
            $a = $random[$i] % 3329;
            $b = $random[$i + 1] % 3329;
            $key .= \chr($a & 255).\chr(($a >> 8) + (($b & 15) << 4)).\chr($b >> 4);
        }
        return $key.Tools::random(32);
    }

    /**
     * A random X25519 public key: the x coordinate of a random point of Curve25519, multiplied by its cofactor 8.
     */
    private static function key(): string
    {
        $mod = self::$mod ??= new BigInteger('7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed', 16);
        $pow = $mod->subtract(new BigInteger(1))->bitwise_rightShift(1);
        $one = new BigInteger(1);
        do {
            $key = Tools::random(32);
            $key[31] = \chr(\ord($key[31]) & 127);
            $x = new BigInteger($key, 256);
        } while (!self::getY2($x, $mod)->powMod($pow, $mod)->equals($one));
        for ($i = 0; $i < 3; $i++) {
            // x_2 = (x^2 - 1)^2 / (4 * y^2)
            $denominator = self::getY2($x, $mod)->multiply(new BigInteger(4))->powMod($one, $mod);
            $numerator = $x->multiply($x)->subtract($one)->powMod($one, $mod);
            $numerator = $numerator->multiply($numerator)->powMod($one, $mod);
            $x = $numerator->multiply($denominator->modInverse($mod))->powMod($one, $mod);
        }
        return strrev(str_pad($x->toBytes(), 32, "\0", STR_PAD_LEFT));
    }

    /**
     * y^2 = x^3 + 486662 * x^2 + x.
     */
    private static function getY2(BigInteger $x, BigInteger $mod): BigInteger
    {
        $one = new BigInteger(1);
        $y = $x->add(new BigInteger(486662))->multiply($x)->powMod($one, $mod);
        return $y->add($one)->multiply($x)->powMod($one, $mod);
    }

    /**
     * Async close.
     */
    #[\Override]
    public function disconnect(): void
    {
        $this->stream->disconnect();
    }

    /**
     * Get write buffer asynchronously.
     *
     * @param int $length Length of data that is going to be written to the write buffer
     */
    #[\Override]
    public function getWriteBuffer(int $length, string $append = ''): \danog\MadelineProto\Stream\WriteBufferInterface
    {
        $this->writeLength = $length;
        $this->append = $append;
        return $this;
    }

    /**
     * Buffer a frame, sending it in application data records once complete.
     */
    #[\Override]
    public function bufferWrite(string $data): void
    {
        $this->writeBuffer .= $data;
        $left = $this->writeLength - \strlen($this->writeBuffer) - \strlen($this->append);
        if ($left > 0) {
            return;
        }
        $data = $this->writeBuffer.$this->append;
        $this->writeBuffer = '';
        $this->append = '';
        $this->writeLength = 0;
        if ($left < 0) {
            throw new Exception('Tried to send too much out of frame data, cannot append');
        }
        $this->writeRecords($data);
    }

    /**
     * Send data in application data records.
     *
     * Before the first frame, data is held back (that's the obfuscated2 header) and sent in the same
     * record as the first frame, as TDLib does.
     */
    #[\Override]
    public function write(string $bytes): void
    {
        if ($this->firstRecord) {
            $this->header .= $bytes;
            return;
        }
        $this->writeRecords($bytes);
    }

    /**
     * Send data in application data records of at most MAX_TLS_PACKET_LENGTH bytes, the first one
     * preceded by a change_cipher_spec record.
     */
    private function writeRecords(string $bytes): void
    {
        $records = '';
        if ($this->firstRecord) {
            $this->firstRecord = false;
            $records = "\x14\x03\x03\x00\x01\x01";
            $bytes = $this->header.$bytes;
            $this->header = '';
        }
        foreach (str_split($bytes, self::MAX_TLS_PACKET_LENGTH) as $chunk) {
            $records .= "\x17\x03\x03".pack('n', \strlen($chunk)).$chunk;
        }
        $this->stream->write($records);
    }

    /**
     * Get read buffer asynchronously.
     *
     * @param int $length Length of payload, as detected by this layer
     */
    #[\Override]
    public function getReadBuffer(?int &$length): \danog\MadelineProto\Stream\ReadBufferInterface
    {
        $length = \strlen($this->readBuffer);
        return $this;
    }

    /**
     * Read data from application data records.
     */
    #[\Override]
    public function bufferRead(int $length, ?Cancellation $cancellation = null): string
    {
        while (\strlen($this->readBuffer) < $length) {
            $this->readBuffer .= $this->readRecord($cancellation);
        }
        $data = substr($this->readBuffer, 0, $length);
        $this->readBuffer = substr($this->readBuffer, $length);
        return $data;
    }

    /**
     * Read the payload of the next application data record (or what's left of it).
     */
    #[\Override]
    public function read(?Cancellation $cancellation = null): ?string
    {
        if ($this->readBuffer !== '') {
            $data = $this->readBuffer;
            $this->readBuffer = '';
            return $data;
        }
        return $this->readRecord($cancellation);
    }

    private function readRecord(?Cancellation $cancellation): string
    {
        $buffer = $this->stream->getReadBuffer($l);
        $header = $buffer->bufferRead(5, $cancellation);
        if (substr($header, 0, 3) !== "\x17\x03\x03") {
            throw new Exception('Invalid bytes at the beginning of a packet (emulated tls)');
        }
        $length = unpack('n', substr($header, 3))[1];
        return $length ? $buffer->bufferRead($length, $cancellation) : '';
    }

    /**
     * Set the MTProxy secret.
     */
    #[\Override]
    public function setExtra($extra): void
    {
        ['key' => $this->key, 'domain' => $domain] = ObfuscatedStream::decodeSecret($extra['secret']);
        if ($domain === null) {
            throw new Exception('Fake TLS needs an MTProxy secret starting with ee');
        }
        $this->domain = $domain;
    }

    /**
     * {@inheritdoc}
     */
    #[\Override]
    public function getSocket(): Socket
    {
        return $this->stream->getSocket();
    }

    /**
     * {@inheritDoc}
     */
    #[\Override]
    public function getStream(): RawStreamInterface
    {
        return $this->stream;
    }

    /**
     * @psalm-pure
     */
    #[\Override]
    public static function getName(): string
    {
        return self::class;
    }
}
<?php

declare(strict_types=1);

/**
 * Abridged stream wrapper.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Stream\MTProtoTransport;

use Amp\Socket\Socket;
use danog\MadelineProto\Stream\BufferedStreamInterface;
use danog\MadelineProto\Stream\ConnectionContext;
use danog\MadelineProto\Stream\MTProtoBufferInterface;
use danog\MadelineProto\Stream\RawStreamInterface;

/**
 * Abridged stream wrapper.
 *
 * @author Daniil Gentili <daniil@daniil.it>
 */
final class AbridgedStream implements BufferedStreamInterface, MTProtoBufferInterface
{
    private $stream;
    /**
     * Connect to stream.
     *
     * @param ConnectionContext $ctx The connection context
     */
    #[\Override]
    public function connect(ConnectionContext $ctx, string $header = ''): void
    {
        $this->stream = ($ctx->getStream(\chr(239).$header));
    }
    /**
     * Async close.
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function disconnect(): void
    {
        $this->stream->disconnect();
    }
    /**
     * Get write buffer asynchronously.
     *
     * @param int $length Length of data that is going to be written to the write buffer
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function getWriteBuffer(int $length, string $append = ''): \danog\MadelineProto\Stream\WriteBufferInterface
    {
        $length >>= 2;
        if ($length < 127) {
            // $message = \chr($length | (1 << 7));
            $message = \chr($length);
        } else {
            // $message = \chr(255).substr(pack('V', $length), 0, 3);
            $message = \chr(127).substr(pack('V', $length), 0, 3);
        }
        $buffer = $this->stream->getWriteBuffer(\strlen($message) + $length, $append);
        $buffer->bufferWrite($message);
        return $buffer;
    }
    /**
     * Get read buffer asynchronously.
     *
     * @param int $length Length of payload, as detected by this layer
     */
    #[\Override]
    public function getReadBuffer(?int &$length): \danog\MadelineProto\Stream\ReadBufferInterface
    {
        $buffer = $this->stream->getReadBuffer($l);
        $c = $buffer->bufferRead(1);
        $length = \ord($c);
        /*if (($length & (1 << 7)) !== 0) {
            $length = unpack('V', strrev($c.$buffer->bufferRead(3)))[1];
            return $buffer;
        }*/
        if ($length >= 127) {
            $length = unpack('V', ($buffer->bufferRead(3))."\0")[1];
        }
        $length <<= 2;
        return $buffer;
    }
    /**
     * {@inheritdoc}
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function getSocket(): Socket
    {
        return $this->stream->getSocket();
    }
    /**
     * {@inheritDoc}
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function getStream(): RawStreamInterface
    {
        return $this->stream;
    }
    /**
     * @psalm-pure
     */
    #[\Override]
    public static function getName(): string
    {
        return self::class;
    }
}
<?php

declare(strict_types=1);

/**
 * TCP full stream wrapper.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Stream\MTProtoTransport;

use Amp\Socket\Socket;
use danog\MadelineProto\Exception;
use danog\MadelineProto\Stream\BufferedStreamInterface;
use danog\MadelineProto\Stream\Common\HashedBufferedStream;
use danog\MadelineProto\Stream\ConnectionContext;
use danog\MadelineProto\Stream\MTProtoBufferInterface;
use danog\MadelineProto\Stream\RawStreamInterface;

/**
 * TCP full stream wrapper.
 *
 * Manages obfuscated2 encryption/decryption
 *
 * @author Daniil Gentili <daniil@daniil.it>
 */
final class FullStream implements BufferedStreamInterface, MTProtoBufferInterface
{
    private $stream;
    private $in_seq_no = -1;
    private $out_seq_no = -1;
    /**
     * Stream to use as data source.
     */
    #[\Override]
    public function connect(ConnectionContext $ctx, string $header = ''): void
    {
        $this->in_seq_no = -1;
        $this->out_seq_no = -1;
        $this->stream = new HashedBufferedStream();
        $this->stream->setExtra('crc32b_rev');
        $this->stream->connect($ctx, $header);
    }
    /**
     * Async close.
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function disconnect(): void
    {
        $this->stream->disconnect();
    }
    /**
     * Get write buffer asynchronously.
     *
     * @param int $length Length of data that is going to be written to the write buffer
     *
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function getWriteBuffer(int $length, string $append = ''): \danog\MadelineProto\Stream\WriteBufferInterface
    {
        $this->stream->startWriteHash();
        $this->stream->checkWriteHash($length + 8);
        $buffer = $this->stream->getWriteBuffer($length + 12, $append);
        $this->out_seq_no++;
        $buffer->bufferWrite(pack('VV', $length + 12, $this->out_seq_no));
        return $buffer;
    }
    /**
     * Get read buffer asynchronously.
     *
     * @param int $length Length of payload, as detected by this layer
     */
    #[\Override]
    public function getReadBuffer(?int &$length): \danog\MadelineProto\Stream\ReadBufferInterface
    {
        $this->stream->startReadHash();
        $buffer = $this->stream->getReadBuffer($l);
        $read_length = unpack('V', $buffer->bufferRead(4))[1];
        $length = $read_length - 12;
        $this->stream->checkReadHash($read_length - 8);
        $this->in_seq_no++;
        $in_seq_no = unpack('V', $buffer->bufferRead(4))[1];
        if ($in_seq_no != $this->in_seq_no) {
            throw new Exception('Incoming seq_no mismatch');
        }
        return $buffer;
    }
    /**
     * {@inheritdoc}
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function getSocket(): Socket
    {
        return $this->stream->getSocket();
    }
    /**
     * {@inheritDoc}
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function getStream(): RawStreamInterface
    {
        return $this->stream;
    }
    /**
     * @psalm-pure
     */
    #[\Override]
    public static function getName(): string
    {
        return self::class;
    }
}
<?php

declare(strict_types=1);

/**
 * Obfuscated2 stream wrapper.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Stream\MTProtoTransport;

use danog\MadelineProto\Exception;
use danog\MadelineProto\Stream\BufferedProxyStreamInterface;
use danog\MadelineProto\Stream\Common\CtrStream;
use danog\MadelineProto\Stream\ConnectionContext;
use danog\MadelineProto\Tools;

/**
 * Obfuscated2 stream wrapper.
 *
 * Manages obfuscated2 encryption/decryption
 *
 * @author Daniil Gentili <daniil@daniil.it>
 *
 * @implements BufferedProxyStreamInterface<array{secret?: string, address?: string, port?: int}>
 */
final class ObfuscatedStream extends CtrStream implements BufferedProxyStreamInterface
{
    /**
     * Maximum length of the domain of a fake TLS secret, as in TDLib, to fit in a ClientHello.
     */
    public const MAX_DOMAIN_LENGTH = 182;
    private $stream;
    private $extra;
    /**
     * Connect to stream.
     *
     * @param ConnectionContext $ctx The connection context
     */
    #[\Override]
    public function connect(ConnectionContext $ctx, string $header = ''): void
    {
        if (isset($this->extra['address'])) {
            $ctx = $ctx->clone();
            $ctx->setUri('tcp://'.$this->extra['address'].':'.$this->extra['port']);
        }
        do {
            $random = Tools::random(64);
        } while (\in_array(substr($random, 0, 4), ['PVrG', 'GET ', 'POST', 'HEAD', str_repeat(\chr(238), 4), str_repeat(\chr(221), 4)], true) || $random[0] === \chr(0xef) || substr($random, 4, 4) === "\0\0\0\0");
        if (\strlen($header) === 1) {
            $header = str_repeat($header, 4);
        }
        $random = substr_replace($random, $header.substr($random, 56 + \strlen($header)), 56);
        $random = substr_replace($random, pack('s', $ctx->getDc()).substr($random, 60 + 2), 60);
        $reversed = strrev($random);
        $key = substr($random, 8, 32);
        $keyRev = substr($reversed, 8, 32);
        if (isset($this->extra['secret'])) {
            $key = hash('sha256', $key.$this->extra['secret'], true);
            $keyRev = hash('sha256', $keyRev.$this->extra['secret'], true);
        }
        $iv = substr($random, 40, 16);
        $ivRev = substr($reversed, 40, 16);
        parent::setExtra(['encrypt' => ['key' => $key, 'iv' => $iv], 'decrypt' => ['key' => $keyRev, 'iv' => $ivRev]]);
        parent::connect($ctx);
        $random = substr_replace($random, substr($this->getEncryptor()->encrypt($random), 56, 8), 56, 8);
        $this->getStream()->write($random);
    }
    /**
     * Set extra.
     *
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function setExtra($extra): void
    {
        if (isset($extra['secret'])) {
            $extra['secret'] = self::decodeSecret($extra['secret'])['key'];
        }
        $this->extra = $extra;
    }

    /**
     * Decode an MTProxy secret: raw, or hex or (URL-safe, as in proxy links) base64 encoded.
     *
     * Returns its 16-byte key, whether the transport must be padded (dd and ee secrets), and the
     * domain that fake TLS secrets (ee) append to the key.
     *
     * @return array{key: string, padded: bool, domain: ?string}
     *
     * @psalm-pure
     */
    public static function decodeSecret(string $secret): array
    {
        if (\strlen($secret) !== 16) {
            $decoded = preg_match('/^(?:[a-f0-9]{2})+$/i', $secret)
                ? hex2bin($secret)
                : base64_decode(strtr($secret, '-_', '+/'), true);
            if ($decoded === false) {
                throw new Exception('The MTProxy secret is neither hex nor base64');
            }
            $secret = $decoded;
        }
        if (\strlen($secret) === 16) {
            return ['key' => $secret, 'padded' => false, 'domain' => null];
        }
        if (\strlen($secret) === 17 && $secret[0] === "\xdd") {
            return ['key' => substr($secret, 1), 'padded' => true, 'domain' => null];
        }
        if (\strlen($secret) >= 18 && $secret[0] === "\xee") {
            if (\strlen($secret) > 17 + self::MAX_DOMAIN_LENGTH) {
                throw new Exception('The domain of the fake TLS MTProxy secret is too long');
            }
            return ['key' => substr($secret, 1, 16), 'padded' => true, 'domain' => substr($secret, 17)];
        }
        throw new Exception('Invalid MTProxy secret: it must be 16 bytes long, 17 starting with dd, or start with ee followed by the key and a domain');
    }
    /**
     * @psalm-pure
     */
    #[\Override]
    public static function getName(): string
    {
        return self::class;
    }
}
<?php

declare(strict_types=1);

/**
 * Connection context.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Stream;

use Amp\Cancellation;
use Amp\Socket\ConnectContext;
use danog\MadelineProto\Stream\MTProtoTransport\HttpsStream;
use danog\MadelineProto\Stream\MTProtoTransport\HttpStream;
use danog\MadelineProto\Stream\MTProtoTransport\ObfuscatedStream;
use danog\MadelineProto\Stream\Transport\DefaultStream;
use danog\MadelineProto\Tools;
use League\Uri\Http;
use Psr\Http\Message\UriInterface;

/**
 * Connection context class.
 *
 * Is responsible for maintaining state about a certain connection to a DC.
 * That includes the Stream chain that is required to use the connection, the connection URI, and other connection-related data.
 *
 * @author Daniil Gentili <daniil@daniil.it>
 */
final class ConnectionContext
{
    /**
     * Whether to use a secure socket.
     *
     */
    private bool $secure = false;
    /**
     * Whether to use test servers.
     *
     */
    private bool $test = false;
    /**
     * The connection URI.
     *
     */
    private UriInterface $uri;
    /**
     * Whether this connection context will be used by the DNS client.
     *
     */
    private bool $isDns = false;
    /**
     * Socket context.
     *
     */
    private ConnectContext $socketContext;
    /**
     * Cancellation token.
     *
     */
    private ?Cancellation $cancellationToken = null;
    /**
     * The telegram DC ID.
     *
     */
    private int $dc = 0;
    /**
     * Whether to use IPv6.
     *
     */
    private bool $ipv6 = false;
    /**
     * An array of arrays containing an array with the stream name and the extra parameter to pass to it.
     *
     * @var list<array{0: class-string, 1: mixed}>
     */
    private array $nextStreams = [];
    /**
     * The current stream key.
     */
    private int $key = 0;
    /**
     * Set the socket context.
     *
     * @psalm-external-mutation-free
     */
    public function setSocketContext(ConnectContext $socketContext): self
    {
        $this->socketContext = $socketContext;
        return $this;
    }
    /**
     * Get the socket context.
     */
    public function getSocketContext(): ConnectContext
    {
        return $this->socketContext;
    }
    /**
     * Set the connection URI.
     *
     */
    public function setUri(string|UriInterface $uri): self
    {
        $this->uri = $uri instanceof UriInterface ? $uri : Http::new($uri);
        return $this;
    }
    /**
     * Get the URI as a string.
     *
     * @psalm-mutation-free
     */
    public function getStringUri(): string
    {
        return (string) $this->uri;
    }
    /**
     * Get the URI.
     */
    public function getUri(): UriInterface
    {
        return $this->uri;
    }
    /**
     * Set the cancellation token.
     *
     * @psalm-external-mutation-free
     */
    public function setCancellation(Cancellation $cancellationToken): self
    {
        $this->cancellationToken = $cancellationToken;
        return $this;
    }
    /**
     * Get the cancellation token.
     */
    public function getCancellation(): ?Cancellation
    {
        return $this->cancellationToken;
    }
    /**
     * Return a clone of the current connection context.
     *
     * @psalm-mutation-free
     */
    public function clone(): self
    {
        return clone $this;
    }
    /**
     * Whether this is a test connection.
     */
    public function isTest(): bool
    {
        return $this->test;
    }
    /**
     * Whether this connection context will only be used by the DNS client.
     */
    public function isDns(): bool
    {
        return $this->isDns;
    }
    /**
     * Whether this connection context will only be used by the DNS client.
     *
     * @psalm-external-mutation-free
     */
    public function setIsDns(bool $isDns): self
    {
        $this->isDns = $isDns;
        return $this;
    }
    /**
     * Set the secure boolean.
     *
     * @psalm-external-mutation-free
     */
    public function secure(bool $secure): self
    {
        $this->secure = $secure;
        return $this;
    }
    /**
     * Whether to use TLS with socket connections.
     */
    public function isSecure(): bool
    {
        return $this->secure;
    }
    /**
     * Set the DC ID.
     *
     * @psalm-external-mutation-free
     */
    public function setDc(int $dc): self
    {
        $this->dc = $dc;
        return $this;
    }
    /**
     * Get the int DC ID.
     */
    public function getDc(): int
    {
        return $this->dc;
    }
    /**
     * Whether to use ipv6.
     *
     * @psalm-external-mutation-free
     */
    public function setIpv6(bool $ipv6): self
    {
        $this->ipv6 = $ipv6;
        return $this;
    }
    /**
     * Whether to use ipv6.
     */
    public function getIpv6(): bool
    {
        return $this->ipv6;
    }
    /**
     * Add a stream to the stream chain.
     *
     * @param class-string $streamName
     *
     * @psalm-external-mutation-free
     */
    public function addStream(string $streamName, $extra = null): self
    {
        $this->nextStreams[] = [$streamName, $extra];
        $this->key = \count($this->nextStreams) - 1;
        return $this;
    }
    /**
     * Check if connected via HTTP.
     *
     * @return boolean
     *
     * @psalm-mutation-free
     */
    public function isHttp(): bool
    {
        return \in_array(Tools::end($this->nextStreams)[0], [HttpStream::class, HttpsStream::class], true);
    }
    /**
     * Check if has stream within stream chain.
     *
     * @param string $stream Stream name
     *
     * @psalm-mutation-free
     */
    public function hasStreamName(string $stream): bool
    {
        foreach ($this->nextStreams as [$name]) {
            if ($name === $stream) {
                return true;
            }
        }
        return false;
    }
    /**
     * Get a stream from the stream chain.
     */
    public function getStream(string $buffer = ''): StreamInterface
    {
        [$clazz, $extra] = $this->nextStreams[$this->key--];
        $obj = new $clazz();
        if ($obj instanceof ProxyStreamInterface) {
            $obj->setExtra($extra);
        }
        $obj->connect($this, $buffer);
        return $obj;
    }
    /**
     * Get the inputClientProxy proxy MTProto object.
     *
     * @psalm-mutation-free
     */
    public function getInputClientProxy(): array|null
    {
        foreach ($this->nextStreams as $couple) {
            [$streamName, $extra] = $couple;
            if ($streamName === ObfuscatedStream::class && isset($extra['address'])) {
                $extra['_'] = 'inputClientProxy';
                return $extra;
            }
        }
        return null;
    }
    /**
     * Get a description "name" of the context.
     *
     * @psalm-mutation-free
     */
    public function getName(): string
    {
        $string = $this->getStringUri();
        if ($this->isSecure()) {
            $string .= ' (TLS)';
        }
        $string .= $this->isTest() ? ' test' : ' main';
        $string .= ' DC ';
        $string .= $this->getDc();
        $string .= ', via ';
        $string .= $this->getIpv6() ? 'ipv6' : 'ipv4';
        $string .= ' using ';
        foreach (array_reverse($this->nextStreams) as $k => $stream) {
            if ($k) {
                $string .= ' => ';
            }
            $string .= preg_replace('/.*\\\\/', '', $stream[0]);
            if ($stream[1] && $stream[0] !== DefaultStream::class) {
                $string .= ' ('.json_encode($stream[1]).')';
            }
        }
        return $string;
    }
    /**
     * Returns a representation of the context.
     *
     * @psalm-mutation-free
     */
    public function __toString(): string
    {
        return $this->getName();
    }
}
<?php

declare(strict_types=1);

/**
 * Hash stream wrapper.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Stream\Common;

use Amp\Cancellation;
use Amp\Socket\Socket;
use danog\MadelineProto\Exception;
use danog\MadelineProto\Stream\BufferedProxyStreamInterface;
use danog\MadelineProto\Stream\BufferInterface;
use danog\MadelineProto\Stream\ConnectionContext;
use danog\MadelineProto\Stream\RawStreamInterface;

/**
 * Hash stream wrapper.
 *
 * @author Daniil Gentili <daniil@daniil.it>
 *
 * @implements BufferedProxyStreamInterface<string>
 */
final class HashedBufferedStream implements BufferedProxyStreamInterface, BufferInterface
{
    private $hash_name;
    private $read_hash;
    private $write_hash;
    private $write_buffer;
    private $write_check_after = 0;
    private $write_check_pos = 0;
    private $read_buffer;
    private $read_check_after = 0;
    private $read_check_pos = 0;
    private $stream;
    private $rev = false;
    /**
     * Enable read hashing.
     *
     * @psalm-external-mutation-free
     */
    public function startReadHash(): void
    {
        $this->read_hash = hash_init($this->hash_name);
    }
    /**
     * Check the read hash after N bytes are read.
     *
     * @param int $after The number of bytes to read before checking the hash
     *
     * @psalm-external-mutation-free
     */
    public function checkReadHash(int $after): void
    {
        $this->read_check_after = $after;
    }
    /**
     * Stop read hashing and get final hash.
     *
     * @psalm-external-mutation-free
     */
    public function getReadHash(): string
    {
        $hash = hash_final($this->read_hash, true);
        if ($this->rev) {
            $hash = strrev($hash);
        }
        $this->read_hash = null;
        $this->read_check_after = 0;
        $this->read_check_pos = 0;
        return $hash;
    }
    /**
     * Check if we are read hashing.
     *
     * @psalm-mutation-free
     */
    public function hasReadHash(): bool
    {
        return $this->read_hash !== null;
    }
    /**
     * Enable write hashing.
     *
     * @psalm-external-mutation-free
     */
    public function startWriteHash(): void
    {
        $this->write_hash = hash_init($this->hash_name);
    }
    /**
     * Write the write hash after N bytes are read.
     *
     * @param int $after The number of bytes to read before writing the hash
     *
     * @psalm-external-mutation-free
     */
    public function checkWriteHash(int $after): void
    {
        $this->write_check_after = $after;
    }
    /**
     * Stop write hashing and get final hash.
     *
     * @psalm-external-mutation-free
     */
    public function getWriteHash(): string
    {
        $hash = hash_final($this->write_hash, true);
        if ($this->rev) {
            $hash = strrev($hash);
        }
        $this->write_hash = null;
        $this->write_check_after = 0;
        $this->write_check_pos = 0;
        return $hash;
    }
    /**
     * Check if we are write hashing.
     *
     * @psalm-mutation-free
     */
    public function hasWriteHash(): bool
    {
        return $this->write_hash !== null;
    }
    /**
     * Set the hash algorithm.
     *
     * @param string $hash Algorithm name
     *
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function setExtra($hash): void
    {
        $rev = strpos($hash, '_rev');
        $this->rev = false;
        if ($rev !== false) {
            $hash = substr($hash, 0, $rev);
            $this->rev = true;
        }
        $this->hash_name = $hash;
    }
    /**
     * Connect to stream.
     *
     * @param ConnectionContext $ctx The connection context
     */
    #[\Override]
    public function connect(ConnectionContext $ctx, string $header = ''): void
    {
        $this->write_hash = null;
        $this->write_check_after = 0;
        $this->write_check_pos = 0;
        $this->read_hash = null;
        $this->read_check_after = 0;
        $this->read_check_pos = 0;
        $this->stream = ($ctx->getStream($header));
    }
    /**
     * Async close.
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function disconnect(): void
    {
        $this->stream->disconnect();
    }
    /**
     * Get read buffer asynchronously.
     *
     * @param int $length Length of payload, as detected by this layer
     *
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function getReadBuffer(?int &$length): \danog\MadelineProto\Stream\ReadBufferInterface
    {
        //if ($this->read_hash) {
        $this->read_buffer = $this->stream->getReadBuffer($length);
        return $this;
        //}
        //return $this->stream->getReadBuffer($length);
    }
    /**
     * Get write buffer asynchronously.
     *
     * @param int $length Length of data that is going to be written to the write buffer
     *
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function getWriteBuffer(int $length, string $append = ''): \danog\MadelineProto\Stream\WriteBufferInterface
    {
        //if ($this->write_hash) {
        $this->write_buffer = $this->stream->getWriteBuffer($length, $append);
        return $this;
        //}
        //return $this->stream->getWriteBuffer($length, $append);
    }
    /**
     * Reads data from the stream.
     */
    #[\Override]
    public function bufferRead(int $length, ?Cancellation $cancellation = null): ?string
    {
        if ($this->read_hash === null) {
            return $this->read_buffer->bufferRead($length, $cancellation);
        }
        if ($this->read_check_after && $length + $this->read_check_pos >= $this->read_check_after) {
            if ($length + $this->read_check_pos > $this->read_check_after) {
                throw new Exception('Tried to read too much out of frame data');
            }
            $data = $this->read_buffer->bufferRead($length, $cancellation);
            hash_update($this->read_hash, $data);
            $hash = $this->getReadHash();
            if ($hash !== $this->read_buffer->bufferRead(\strlen($hash), $cancellation)) {
                throw new Exception('Hash mismatch');
            }
            return $data;
        }
        $data = $this->read_buffer->bufferRead($length, $cancellation);
        hash_update($this->read_hash, $data);
        if ($this->read_check_after) {
            $this->read_check_pos += $length;
        }
        return $data;
    }
    /**
     * Writes data to the stream.
     *
     * @param string $data Bytes to write.
     */
    #[\Override]
    public function bufferWrite(string $data): void
    {
        if ($this->write_hash === null) {
            $this->write_buffer->bufferWrite($data);
            return;
        }
        $length = \strlen($data);
        if ($this->write_check_after && $length + $this->write_check_pos >= $this->write_check_after) {
            if ($length + $this->write_check_pos > $this->write_check_after) {
                throw new Exception('Too much out of frame data was sent, cannot check hash');
            }
            hash_update($this->write_hash, $data);
            $this->write_buffer->bufferWrite($data.$this->getWriteHash());
            return;
        }
        if ($this->write_check_after) {
            $this->write_check_pos += $length;
        }
        if ($this->write_hash) {
            hash_update($this->write_hash, $data);
        }
        $this->write_buffer->bufferWrite($data);
    }
    /**
     * {@inheritdoc}
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function getSocket(): Socket
    {
        return $this->stream->getSocket();
    }
    /**
     * {@inheritDoc}
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function getStream(): RawStreamInterface
    {
        return $this->stream;
    }
    /**
     * @psalm-pure
     */
    #[\Override]
    public static function getName(): string
    {
        return self::class;
    }
}
<?php

declare(strict_types=1);

/**
 * Buffered raw stream.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Stream\Common;

use Amp\ByteStream\ClosedException;
use Amp\Cancellation;
use Amp\File\File;
use Amp\Socket\Socket;
use danog\MadelineProto\Exception;
use danog\MadelineProto\Stream\BufferedStreamInterface;
use danog\MadelineProto\Stream\BufferInterface;
use danog\MadelineProto\Stream\ConnectionContext;
use danog\MadelineProto\Stream\ProxyStreamInterface;
use danog\MadelineProto\Stream\RawStreamInterface;
use RuntimeException;

/**
 * Buffered raw stream.
 *
 * @author Daniil Gentili <daniil@daniil.it>
 *
 * @implements ProxyStreamInterface<File>
 */
final class FileBufferedStream implements BufferedStreamInterface, BufferInterface, ProxyStreamInterface, RawStreamInterface
{
    private ?File $stream = null;
    private int $append_after;
    private string $append;
    /**
     * Connect.
     */
    #[\Override]
    public function connect(ConnectionContext $ctx, string $header = ''): void
    {
        if ($header !== '') {
            $this->stream->write($header);
        }
    }
    /**
     * Async chunked read.
     */
    #[\Override]
    public function read(?Cancellation $token = null): ?string
    {
        if (!$this->stream) {
            throw new ClosedException('MadelineProto stream was disconnected');
        }
        return $this->stream->read($token);
    }
    /**
     * Async write.
     *
     * @param string $data Data to write
     */
    #[\Override]
    public function write(string $data): void
    {
        if (!$this->stream) {
            throw new ClosedException('MadelineProto stream was disconnected');
        }
        $this->stream->write($data);
    }
    /**
     * Async close.
     *
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function disconnect(): void
    {
        if ($this->stream) {
            $this->stream = null;
        }
    }
    /**
     * Get read buffer asynchronously.
     *
     * @param int $length Length of payload, as detected by this layer
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function getReadBuffer(?int &$length): \danog\MadelineProto\Stream\ReadBufferInterface
    {
        if (!$this->stream) {
            throw new ClosedException('MadelineProto stream was disconnected');
        }
        return $this;
    }
    /**
     * Get write buffer asynchronously.
     *
     * @param int $length Total length of data that is going to be piped in the buffer
     *
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function getWriteBuffer(int $length, string $append = ''): \danog\MadelineProto\Stream\WriteBufferInterface
    {
        if (\strlen($append)) {
            $this->append = $append;
            $this->append_after = $length - \strlen($append);
        }
        return $this;
    }
    /**
     * Read data asynchronously.
     *
     * @param int $length Amount of data to read
     */
    #[\Override]
    public function bufferRead(int $length, ?Cancellation $cancellation = null): ?string
    {
        return $this->stream->read($cancellation, $length);
    }
    /**
     * Async write.
     *
     * @param string $data Data to write
     */
    #[\Override]
    public function bufferWrite(string $data): void
    {
        if ($this->append_after) {
            $this->append_after -= \strlen($data);
            if ($this->append_after === 0) {
                $data .= $this->append;
                $this->append = '';
            } elseif ($this->append_after < 0) {
                $this->append_after = 0;
                $this->append = '';
                throw new Exception('Tried to send too much out of frame data, cannot append');
            }
        }
        $this->write($data);
    }
    /**
     * Set file handle.
     *
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function setExtra($extra): void
    {
        $this->stream = $extra;
    }
    /**
     * {@inheritDoc}
     *
     * @psalm-pure
     */
    #[\Override]
    public function getStream(): RawStreamInterface
    {
        throw new RuntimeException("Can't get underlying RawStreamInterface, is a File handle!");
    }
    /**
     * {@inheritDoc}
     *
     * @psalm-pure
     */
    #[\Override]
    public function getSocket(): Socket
    {
        throw new RuntimeException("Can't get underlying socket, is a File handle!");
    }
    /**
     * Get class name.
     *
     * @psalm-pure
     */
    #[\Override]
    public static function getName(): string
    {
        return self::class;
    }
}
<?php

declare(strict_types=1);

/**
 * Buffered raw stream.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Stream\Common;

use Amp\Cancellation;
use danog\MadelineProto\Stream\BufferedStreamInterface;
use danog\MadelineProto\Stream\BufferInterface;
use danog\MadelineProto\Stream\RawStreamInterface;

/**
 * Buffered raw stream, that simply returns less data on EOF instead of throwing.
 *
 * @author Daniil Gentili <daniil@daniil.it>
 */
final class SimpleBufferedRawStream extends BufferedRawStream implements BufferedStreamInterface, BufferInterface, RawStreamInterface
{
    /**
     * Read data asynchronously.
     *
     * @param int $length Amount of data to read
     */
    #[\Override]
    public function bufferRead(int $length, ?Cancellation $cancellation = null): string
    {
        $size = fstat($this->memory_stream)['size'];
        $offset = ftell($this->memory_stream);
        $buffer_length = $size - $offset;
        if ($buffer_length < $length && $buffer_length) {
            fseek($this->memory_stream, $offset + $buffer_length);
        }
        while ($buffer_length < $length) {
            $chunk = $this->read($cancellation);
            if ($chunk === null) {
                break;
            }
            fwrite($this->memory_stream, $chunk);
            $buffer_length += \strlen($chunk);
        }
        fseek($this->memory_stream, $offset);
        return fread($this->memory_stream, $length);
    }
    /**
     * {@inheritDoc}
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function getStream(): RawStreamInterface
    {
        return $this->stream;
    }
    /**
     * Get class name.
     *
     * @psalm-pure
     */
    #[\Override]
    public static function getName(): string
    {
        return self::class;
    }
}
<?php

declare(strict_types=1);

/**
 * AES CTR stream wrapper.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Stream\Common;

use Amp\Cancellation;
use Amp\Socket\Socket;
use danog\MadelineProto\Exception;
use danog\MadelineProto\MTProtoTools\Crypt\AesCtr;
use danog\MadelineProto\Stream\BufferedProxyStreamInterface;
use danog\MadelineProto\Stream\BufferInterface;
use danog\MadelineProto\Stream\ConnectionContext;
use danog\MadelineProto\Stream\RawStreamInterface;

/**
 * AES CTR stream wrapper.
 *
 * Manages AES CTR encryption/decryption
 *
 * @author Daniil Gentili <daniil@daniil.it>
 *
 * @implements BufferedProxyStreamInterface<array{
 *      encrypt: array{key: string, iv: string},
 *      decrypt: array{key: string, iv: string},
 * }>
 */
class CtrStream implements BufferedProxyStreamInterface, BufferInterface
{
    private AesCtr $encrypt;
    private AesCtr $decrypt;
    private $stream;
    private $write_buffer;
    private $read_buffer;
    private $extra;
    private $append = '';
    private $append_after = 0;
    /**
     * Connect to stream.
     *
     * @param ConnectionContext $ctx The connection context
     */
    #[\Override]
    public function connect(ConnectionContext $ctx, string $header = ''): void
    {
        $this->encrypt = new AesCtr($this->extra['encrypt']['key'], $this->extra['encrypt']['iv']);
        $this->decrypt = new AesCtr($this->extra['decrypt']['key'], $this->extra['decrypt']['iv']);
        $this->stream = ($ctx->getStream($header));
    }
    /**
     * Async close.
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function disconnect(): void
    {
        $this->stream->disconnect();
    }
    /**
     * Get write buffer asynchronously.
     *
     * @param int $length Length of data that is going to be written to the write buffer
     *
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function getWriteBuffer(int $length, string $append = ''): \danog\MadelineProto\Stream\WriteBufferInterface
    {
        $this->write_buffer = $this->stream->getWriteBuffer($length);
        if (\strlen($append)) {
            $this->append = $append;
            $this->append_after = $length - \strlen($append);
        }
        return $this;
    }
    /**
     * Get read buffer asynchronously.
     *
     * @param int $length Length of payload, as detected by this layer
     *
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function getReadBuffer(?int &$length): \danog\MadelineProto\Stream\ReadBufferInterface
    {
        $this->read_buffer = $this->stream->getReadBuffer($length);
        return $this;
    }
    /**
     * Decrypts read data asynchronously.
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function bufferRead(int $length, ?Cancellation $cancellation = null): string
    {
        return $this->decrypt->encrypt($this->read_buffer->bufferRead($length, $cancellation));
    }
    /**
     * Writes data to the stream.
     *
     * @param string $data Bytes to write.
     *
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function bufferWrite(string $data): void
    {
        if ($this->append_after) {
            $this->append_after -= \strlen($data);
            if ($this->append_after === 0) {
                $data .= $this->append;
                $this->append = '';
            } elseif ($this->append_after < 0) {
                $this->append_after = 0;
                $this->append = '';
                throw new Exception('Tried to send too much out of frame data, cannot append');
            }
        }
        $this->write_buffer->bufferWrite($this->encrypt->encrypt($data));
    }
    /**
     * Set obfuscation keys/IVs.
     *
     * @param array $data Keys
     *
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function setExtra($data): void
    {
        $this->extra = $data;
    }
    /**
     * {@inheritdoc}
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function getSocket(): Socket
    {
        return $this->stream->getSocket();
    }
    /**
     * {@inheritDoc}
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function getStream(): RawStreamInterface
    {
        return $this->stream;
    }
    /**
     * @psalm-mutation-free
     */
    public function getEncryptor(): AesCtr
    {
        return $this->encrypt;
    }
    /**
     * @psalm-mutation-free
     */
    public function getDecryptor(): AesCtr
    {
        return $this->decrypt;
    }
    /**
     * @psalm-pure
     */
    #[\Override]
    public static function getName(): string
    {
        return self::class;
    }
}
<?php

declare(strict_types=1);

/**
 * Buffered raw stream.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Stream\Common;

use Amp\ByteStream\ClosedException;
use Amp\Cancellation;
use Amp\Socket\Socket;
use danog\MadelineProto\Exception;
use danog\MadelineProto\NothingInTheSocketException;
use danog\MadelineProto\Stream\BufferedStreamInterface;
use danog\MadelineProto\Stream\BufferInterface;
use danog\MadelineProto\Stream\ConnectionContext;
use danog\MadelineProto\Stream\RawStreamInterface;

/**
 * Buffered raw stream.
 *
 * @author Daniil Gentili <daniil@daniil.it>
 */
class BufferedRawStream implements BufferedStreamInterface, BufferInterface, RawStreamInterface
{
    private const MAX_SIZE = 10 * 1024 * 1024;
    protected $stream;
    protected $memory_stream;
    private $append = '';
    private $append_after = 0;
    /**
     * Asynchronously connect to a TCP/TLS server.
     *
     * @param ConnectionContext $ctx Connection context
     */
    #[\Override]
    public function connect(ConnectionContext $ctx, string $header = ''): void
    {
        $this->stream = $ctx->getStream($header);
        $this->memory_stream = fopen('php://memory', 'r+');
    }
    /**
     * Async chunked read.
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function read(?Cancellation $cancellation = null): ?string
    {
        if (!$this->stream) {
            throw new ClosedException('MadelineProto stream was disconnected');
        }
        return $this->stream->read($cancellation);
    }
    /**
     * Async write.
     *
     * @param string $data Data to write
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function write(string $data): void
    {
        if (!$this->stream) {
            throw new ClosedException('MadelineProto stream was disconnected');
        }
        $this->stream->write($data);
    }
    /**
     * Async close.
     */
    #[\Override]
    public function disconnect(): void
    {
        if ($this->memory_stream) {
            fclose($this->memory_stream);
            $this->memory_stream = null;
        }
        if ($this->stream) {
            $this->stream->disconnect();
            $this->stream = null;
        }
    }
    /**
     * Get read buffer asynchronously.
     *
     * @param int $length Length of payload, as detected by this layer
     */
    #[\Override]
    public function getReadBuffer(?int &$length): \danog\MadelineProto\Stream\ReadBufferInterface
    {
        if (!$this->stream) {
            throw new ClosedException('MadelineProto stream was disconnected');
        }
        $size = fstat($this->memory_stream)['size'];
        $offset = ftell($this->memory_stream);
        $length = $size - $offset;
        if ($length === 0 || $size > self::MAX_SIZE) {
            $new_memory_stream = fopen('php://memory', 'r+');
            if ($length) {
                fwrite($new_memory_stream, fread($this->memory_stream, $length));
                fseek($new_memory_stream, 0);
            }
            fclose($this->memory_stream);
            $this->memory_stream = $new_memory_stream;
        }
        return $this;
    }
    /**
     * Get write buffer asynchronously.
     *
     * @param int $length Total length of data that is going to be piped in the buffer
     *
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function getWriteBuffer(int $length, string $append = ''): \danog\MadelineProto\Stream\WriteBufferInterface
    {
        if (\strlen($append)) {
            $this->append = $append;
            $this->append_after = $length - \strlen($append);
        }
        return $this;
    }
    /**
     * Read data asynchronously.
     *
     * @param int $length Amount of data to read
     */
    #[\Override]
    public function bufferRead(int $length, ?Cancellation $cancellation = null): string
    {
        if (!$this->stream) {
            throw new ClosedException('MadelineProto stream was disconnected');
        }
        $size = fstat($this->memory_stream)['size'];
        $offset = ftell($this->memory_stream);
        $buffer_length = $size - $offset;
        if ($buffer_length >= $length) {
            return fread($this->memory_stream, $length);
        }
        $size = fstat($this->memory_stream)['size'];
        $offset = ftell($this->memory_stream);
        $buffer_length = $size - $offset;
        if ($buffer_length < $length && $buffer_length) {
            fseek($this->memory_stream, $offset + $buffer_length);
        }
        while ($buffer_length < $length) {
            $chunk = $this->read($cancellation);
            if ($chunk === null) {
                $this->disconnect();
                throw new NothingInTheSocketException();
            }
            fwrite($this->memory_stream, $chunk);
            $buffer_length += \strlen($chunk);
        }
        fseek($this->memory_stream, $offset);
        return fread($this->memory_stream, $length);
    }
    /**
     * Async write.
     *
     * @param string $data Data to write
     *
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function bufferWrite(string $data): void
    {
        if ($this->append_after) {
            $this->append_after -= \strlen($data);
            if ($this->append_after === 0) {
                $data .= $this->append;
                $this->append = '';
            } elseif ($this->append_after < 0) {
                $this->append_after = 0;
                $this->append = '';
                throw new Exception('Tried to send too much out of frame data, cannot append');
            }
        }
        $this->write($data);
    }
    /**
     * Get remaining data from buffer.
     */
    public function bufferClear(): string
    {
        $size = fstat($this->memory_stream)['size'];
        $offset = ftell($this->memory_stream);
        $buffer_length = $size - $offset;
        $data = fread($this->memory_stream, $buffer_length);
        fclose($this->memory_stream);
        $this->memory_stream = null;
        return $data;
    }
    /**
     * {@inheritdoc}
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function getSocket(): Socket
    {
        return $this->stream->getSocket();
    }
    /**
     * {@inheritDoc}
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function getStream(): RawStreamInterface
    {
        return $this->stream;
    }
    /**
     * Get class name.
     *
     * @psalm-pure
     */
    #[\Override]
    public static function getName(): string
    {
        return self::class;
    }
}
<?php

declare(strict_types=1);

/**
 * UDP stream wrapper.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Stream\Common;

use Amp\ByteStream\ClosedException;
use Amp\Cancellation;
use danog\MadelineProto\Exception;
use danog\MadelineProto\NothingInTheSocketException;
use danog\MadelineProto\Stream\BufferedStreamInterface;
use danog\MadelineProto\Stream\ConnectionContext;
use danog\MadelineProto\Stream\MTProtoBufferInterface;
use danog\MadelineProto\Stream\RawStreamInterface;
use danog\MadelineProto\Stream\ReadBufferInterface;
use danog\MadelineProto\Stream\Transport\DefaultStream;
use danog\MadelineProto\Stream\WriteBufferInterface;

use function Amp\Socket\socketConnector;

/**
 * UDP stream wrapper.
 *
 * @author Daniil Gentili <daniil@daniil.it>
 */
final class UdpBufferedStream extends DefaultStream implements BufferedStreamInterface, MTProtoBufferInterface
{
    /**
     * Connect to stream.
     *
     * @param ConnectionContext $ctx The connection context
     */
    #[\Override]
    public function connect(ConnectionContext $ctx, string $header = ''): void
    {
        $ctx = $ctx->clone();
        $uri = $ctx->getUri();
        $this->stream = (($this->connector ?? socketConnector())->connect((string) $uri, $ctx->getSocketContext(), $ctx->getCancellation()));
        if (\strlen($header) === '') {
            return;
        }
        $this->stream->write($header);
    }
    /**
     * Async close.
     */
    #[\Override]
    public function disconnect(): void
    {
        $this->stream->close();
    }
    /**
     * Get read buffer asynchronously.
     *
     * @param int $length Length of payload, as detected by this layer
     */
    #[\Override]
    public function getReadBuffer(?int &$length): ReadBufferInterface
    {
        if (!$this->stream) {
            throw new ClosedException('MadelineProto stream was disconnected');
        }
        $chunk = $this->read();
        if ($chunk === null) {
            $this->disconnect();
            throw new NothingInTheSocketException();
        }
        $length = \strlen($chunk);
        return new class($chunk) implements ReadBufferInterface {
            /**
             * Buffer.
             *
             * @var resource
             */
            private $buffer;
            /**
             * Constructor function.
             *
             * @param string $buf Buffer
             */
            public function __construct(string $buf)
            {
                $this->buffer = fopen('php://memory', 'r+');
                fwrite($this->buffer, $buf);
                fseek($this->buffer, 0);
            }
            /**
             * Read data from buffer.
             *
             * @param integer $length Length
             */
            #[\Override]
            public function bufferRead(int $length, ?Cancellation $_ = null): string
            {
                return fread($this->buffer, $length);
            }
            /**
             * Destructor function.
             */
            public function __destruct()
            {
                fclose($this->buffer);
            }
        };
    }
    /**
     * Get write buffer asynchronously.
     *
     * @param int $length Total length of data that is going to be piped in the buffer
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function getWriteBuffer(int $length, string $append = ''): WriteBufferInterface
    {
        return new class($length, $append, $this) implements WriteBufferInterface {
            private string $append = '';
            private int $append_after = 0;
            private string $data = '';
            /**
             * Constructor function.
             *
             * @psalm-mutation-free
             */
            public function __construct(private readonly int $length, string $append, private readonly RawStreamInterface $stream)
            {
                if (\strlen($append)) {
                    $this->append = $append;
                    $this->append_after = $length - \strlen($append);
                }
            }
            /**
             * Async write.
             *
             * @param string $data Data to write
             */
            #[\Override]
            public function bufferWrite(string $data): void
            {
                $this->data .= $data;
                if ($this->append_after) {
                    $this->append_after -= \strlen($data);
                    if ($this->append_after === 0) {
                        $this->data .= $this->append;
                        $this->append = '';
                        $this->stream->write($this->data);
                    } elseif ($this->append_after < 0) {
                        $this->append_after = 0;
                        $this->append = '';
                        throw new Exception('Tried to send too much out of frame data, cannot append');
                    }
                } else {
                    if (\strlen($this->data) > $this->length) {
                        throw new Exception('Tried to send too much out of frame data, cannot append');
                    }
                    if (\strlen($this->data) === $this->length) {
                        $this->stream->write($this->data);
                    }
                }
            }
        };
    }
    /**
     * @psalm-pure
     */
    #[\Override]
    public static function getName(): string
    {
        return self::class;
    }
}
<?php

declare(strict_types=1);

/**
 * Socks5 stream wrapper.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Stream\Proxy;

use Amp\Cancellation;
use Amp\Socket\ClientTlsContext;
use Amp\Socket\Socket;
use danog\MadelineProto\Exception;
use danog\MadelineProto\Logger;
use danog\MadelineProto\Stream\BufferedProxyStreamInterface;
use danog\MadelineProto\Stream\BufferedStreamInterface;
use danog\MadelineProto\Stream\ConnectionContext;
use danog\MadelineProto\Stream\RawProxyStreamInterface;
use danog\MadelineProto\Stream\RawStreamInterface;
use Webmozart\Assert\Assert;

/**
 * Socks5 stream wrapper.
 *
 * @author Daniil Gentili <daniil@daniil.it>
 *
 * @implements RawProxyStreamInterface<array{address: string, port: int, username?: string, password?: string}>
 * @implements BufferedProxyStreamInterface<array{address: string, port: int, username?: string, password?: string}>
 */
final class SocksProxy implements RawProxyStreamInterface, BufferedProxyStreamInterface
{
    private const REPS = [0 => 'succeeded', 1 => 'general SOCKS server failure', 2 => 'connection not allowed by ruleset', 3 => 'Network unreachable', 4 => 'Host unreachable', 5 => 'Connection refused', 6 => 'TTL expired', 7 => 'Command not supported', 8 => 'Address type not supported'];
    /**
     * Stream.
     *
     */
    protected RawStreamInterface&BufferedStreamInterface $stream;
    private $extra;
    /**
     * Connect to stream.
     *
     * @param ConnectionContext $ctx The connection context
     */
    #[\Override]
    public function connect(ConnectionContext $ctx, string $header = ''): void
    {
        $ctx = $ctx->clone();
        $uri = $ctx->getUri();
        $secure = $ctx->isSecure();
        if ($secure) {
            $ctx->setSocketContext($ctx->getSocketContext()->withTlsContext(new ClientTlsContext($uri->getHost())));
        }
        $ctx->setUri('tcp://'.$this->extra['address'].':'.$this->extra['port'])->secure(false);
        $methods = \chr(0);
        if (isset($this->extra['username']) && isset($this->extra['password'])) {
            $methods .= \chr(2);
        }
        $this->stream = $ctx->getStream(\chr(5).\chr(\strlen($methods)).$methods);
        Assert::true($this->stream instanceof BufferedStreamInterface);
        Assert::true($this->stream instanceof RawStreamInterface);
        $l = 2;
        $buffer = $this->stream->getReadBuffer($l);
        $version = \ord($buffer->bufferRead(1));
        $method = \ord($buffer->bufferRead(1));
        if ($version !== 5) {
            throw new Exception("Wrong SOCKS5 version: {$version}");
        }
        if ($method === 2) {
            $auth = \chr(1).\chr(\strlen($this->extra['username'])).$this->extra['username'].\chr(\strlen($this->extra['password'])).$this->extra['password'];
            $this->stream->write($auth);
            $buffer = $this->stream->getReadBuffer($l);
            $version = \ord($buffer->bufferRead(1));
            $result = \ord($buffer->bufferRead(1));
            if ($version !== 1) {
                throw new Exception("Wrong authorized SOCKS version: {$version}");
            }
            if ($result !== 0) {
                throw new Exception("Wrong authorization status: {$version}");
            }
        } elseif ($method !== 0) {
            throw new Exception("Wrong method: {$method}");
        }
        $payload = pack('C3', 0x5, 0x1, 0x0);
        try {
            $ip = inet_pton($uri->getHost());
            $payload .= $ip ? pack('C1', \strlen($ip) === 4 ? 0x1 : 0x4).$ip : pack('C2', 0x3, \strlen($uri->getHost())).$uri->getHost();
        } catch (Exception $e) {
            $payload .= pack('C2', 0x3, \strlen($uri->getHost())).$uri->getHost();
        }
        $payload .= pack('n', $uri->getPort());
        $this->stream->write($payload);
        $l = 4;
        $buffer = $this->stream->getReadBuffer($l);
        $version = \ord($buffer->bufferRead(1));
        if ($version !== 5) {
            throw new Exception("Wrong SOCKS5 version: {$version}");
        }
        $rep = \ord($buffer->bufferRead(1));
        if ($rep !== 0) {
            $rep = self::REPS[$rep] ?? $rep;
            throw new Exception("Wrong SOCKS5 rep: {$rep}");
        }
        $rsv = \ord($buffer->bufferRead(1));
        if ($rsv !== 0) {
            throw new Exception("Wrong socks5 final RSV: {$rsv}");
        }
        switch (\ord($buffer->bufferRead(1))) {
            case 1:
                $buffer = $this->stream->getReadBuffer($l);
                $ip = inet_ntop($buffer->bufferRead(4));
                break;
            case 4:
                $l = 16;
                $buffer = $this->stream->getReadBuffer($l);
                $ip = inet_ntop($buffer->bufferRead(16));
                break;
            case 3:
                $l = 1;
                $buffer = $this->stream->getReadBuffer($l);
                $length = \ord($buffer->bufferRead(1));
                $buffer = $this->stream->getReadBuffer($length);
                $ip = $buffer->bufferRead($length);
                break;
        }
        $l = 2;
        $buffer = $this->stream->getReadBuffer($l);
        $port = unpack('n', $buffer->bufferRead(2))[1];
        Logger::log(['Connected to '.$ip.':'.$port.' via socks5']);
        if ($secure) {
            $this->getSocket()->setupTls();
        }
        if (\strlen($header)) {
            ($this->stream->getWriteBuffer(\strlen($header)))->bufferWrite($header);
        }
    }
    /**
     * Async close.
     */
    #[\Override]
    public function disconnect(): void
    {
        $this->stream->disconnect();
    }
    /**
     * Get write buffer asynchronously.
     *
     * @param int $length Length of data that is going to be written to the write buffer
     */
    #[\Override]
    public function getWriteBuffer(int $length, string $append = ''): \danog\MadelineProto\Stream\WriteBufferInterface
    {
        return $this->stream->getWriteBuffer($length, $append);
    }
    /**
     * Get read buffer asynchronously.
     *
     * @param int $length Length of payload, as detected by this layer
     */
    #[\Override]
    public function getReadBuffer(?int &$length): \danog\MadelineProto\Stream\ReadBufferInterface
    {
        return $this->stream->getReadBuffer($length);
    }
    #[\Override]
    public function read(?Cancellation $token = null): ?string
    {
        return $this->stream->read($token);
    }
    #[\Override]
    public function write(string $data): void
    {
        $this->stream->write($data);
    }
    /**
     * Sets proxy data.
     *
     * @param array $extra Proxy data
     *
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function setExtra($extra): void
    {
        $this->extra = $extra;
    }
    /**
     * {@inheritDoc}
     */
    #[\Override]
    public function getStream(): RawStreamInterface
    {
        return $this->stream;
    }
    /**
     * {@inheritdoc}
     */
    #[\Override]
    public function getSocket(): Socket
    {
        return $this->stream->getSocket();
    }
    /**
     * @psalm-pure
     */
    #[\Override]
    public static function getName(): string
    {
        return self::class;
    }
}
<?php

declare(strict_types=1);

/**
 * HTTP proxy stream wrapper.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Stream\Proxy;

use Amp\Cancellation;
use Amp\Socket\ClientTlsContext;
use Amp\Socket\Socket;
use danog\MadelineProto\Exception;
use danog\MadelineProto\Logger;
use danog\MadelineProto\Stream\BufferedProxyStreamInterface;
use danog\MadelineProto\Stream\BufferedStreamInterface;
use danog\MadelineProto\Stream\ConnectionContext;
use danog\MadelineProto\Stream\RawProxyStreamInterface;
use danog\MadelineProto\Stream\RawStreamInterface;
use Webmozart\Assert\Assert;

/**
 * HTTP proxy stream wrapper.
 *
 * @author Daniil Gentili <daniil@daniil.it>
 *
 * @implements RawProxyStreamInterface<array{address: string, port: int, username?: string, password?: string}>
 * @implements BufferedProxyStreamInterface<array{address: string, port: int, username?: string, password?: string}>
 */
final class HttpProxy implements RawProxyStreamInterface, BufferedProxyStreamInterface
{
    /**
     * Stream.
     */
    protected RawStreamInterface&BufferedStreamInterface $stream;
    private $extra;
    /**
     * Connect to stream.
     *
     * @param ConnectionContext $ctx The connection context
     */
    #[\Override]
    public function connect(ConnectionContext $ctx, string $header = ''): void
    {
        $ctx = $ctx->clone();
        $uri = $ctx->getUri();
        $secure = $ctx->isSecure();
        if ($secure) {
            $ctx->setSocketContext($ctx->getSocketContext()->withTlsContext(new ClientTlsContext($uri->getHost())));
        }
        $ctx->setUri('tcp://'.$this->extra['address'].':'.$this->extra['port'])->secure(false);
        $this->stream = $ctx->getStream();
        Assert::isInstanceOf($this->stream, BufferedStreamInterface::class);
        Assert::isInstanceOf($this->stream, RawStreamInterface::class);
        $address = $uri->getHost();
        $port = $uri->getPort();
        try {
            if (\strlen(inet_pton($address) ?: '') === 16) {
                $address = '['.$address.']';
            }
        } catch (Exception) {
        }
        $this->stream->write("CONNECT {$address}:{$port} HTTP/1.1\r\nHost: {$address}:{$port}\r\nAccept: */*\r\n".$this->getProxyAuthHeader()."Connection: keep-Alive\r\n\r\n");
        $buffer = $this->stream->getReadBuffer($l);
        $headers = '';
        $was_crlf = false;
        while (true) {
            $piece = $buffer->bufferRead(2);
            $headers .= $piece;
            if ($piece === "\n\r") {
                // Assume end of headers with \r\n\r\n
                $headers .= $buffer->bufferRead(1);
                break;
            }
            if ($was_crlf && $piece === "\r\n") {
                break;
            }
            $was_crlf = $piece === "\r\n";
        }
        $headers = explode("\r\n", $headers);
        [$protocol, $code, $description] = explode(' ', $headers[0], 3);
        [$protocol, $protocol_version] = explode('/', $protocol);
        if ($protocol !== 'HTTP') {
            throw new Exception('Wrong protocol');
        }
        $code = (int) $code;
        unset($headers[0]);
        if (array_pop($headers).array_pop($headers) !== '') {
            throw new Exception('Wrong last header');
        }
        foreach ($headers as $key => $current_header) {
            unset($headers[$key]);
            $current_header = explode(':', $current_header, 2);
            $headers[strtolower($current_header[0])] = trim($current_header[1]);
        }
        if ($code !== 200) {
            $read = '';
            if (isset($headers['content-length'])) {
                $length = (int) $headers['content-length'];
                if ($length < 0) {
                    Logger::log("Trying to read negative amount {$headers['content-length']}");
                } else {
                    $read = $buffer->bufferRead($length);
                }
            }
            Logger::log(trim($read));
            throw new Exception($description, $code);
        }
        if (isset($headers['content-length'])) {
            $length = (int) $headers['content-length'];
            $read = $buffer->bufferRead($length);
        }
        if ($secure) {
            $this->getSocket()->setupTls();
        }
        Logger::log('Connected to '.$address.':'.$port.' via http');
        if (\strlen($header)) {
            ($this->stream->getWriteBuffer(\strlen($header)))->bufferWrite($header);
        }
    }
    /**
     * Async close.
     */
    #[\Override]
    public function disconnect(): void
    {
        $this->stream->disconnect();
    }
    /**
     * Get write buffer asynchronously.
     *
     * @param int $length Length of data that is going to be written to the write buffer
     */
    #[\Override]
    public function getWriteBuffer(int $length, string $append = ''): \danog\MadelineProto\Stream\WriteBufferInterface
    {
        return $this->stream->getWriteBuffer($length, $append);
    }
    /**
     * Get read buffer asynchronously.
     *
     * @param int $length Length of payload, as detected by this layer
     */
    #[\Override]
    public function getReadBuffer(?int &$length): \danog\MadelineProto\Stream\ReadBufferInterface
    {
        return $this->stream->getReadBuffer($length);
    }
    #[\Override]
    public function read(?Cancellation $cancellation = null): ?string
    {
        return $this->stream->read($cancellation);
    }
    #[\Override]
    public function write(string $data): void
    {
        $this->stream->write($data);
    }
    private function getProxyAuthHeader(): string
    {
        if (!isset($this->extra['username']) || !isset($this->extra['password'])) {
            return '';
        }
        return 'Proxy-Authorization: Basic '.base64_encode($this->extra['username'].':'.$this->extra['password'])."\r\n";
    }
    /**
     * Sets proxy data.
     *
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function setExtra($extra): void
    {
        $this->extra = $extra;
    }
    /**
     * {@inheritdoc}
     */
    #[\Override]
    public function getSocket(): Socket
    {
        return $this->stream->getSocket();
    }
    /**
     * {@inheritDoc}
     */
    #[\Override]
    public function getStream(): RawStreamInterface
    {
        return $this->stream;
    }
    /**
     * @psalm-pure
     */
    #[\Override]
    public static function getName(): string
    {
        return self::class;
    }
}
<?php

declare(strict_types=1);

/**
 * Raw stream proxy interface.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Stream;

/**
 * Raw stream proxy interface.
 *
 * @author Daniil Gentili <daniil@daniil.it>
 *
 * @template TT
 *
 * @extends ProxyStreamInterface<TT>
 * @psalm-mutable
 */
interface RawProxyStreamInterface extends RawStreamInterface, ProxyStreamInterface
{
}
<?php

declare(strict_types=1);

/**
 * Websocket TLS stream wrapper.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Stream\Transport;

use danog\MadelineProto\Stream\ConnectionContext;

/**
 * Websocket TLS stream wrapper.
 *
 * @author Daniil Gentili <daniil@daniil.it>
 */
final class WssStream extends WsStream
{
    /**
     * Connect to stream.
     *
     * @param ConnectionContext $ctx The connection context
     */
    #[\Override]
    public function connect(ConnectionContext $ctx, string $header = ''): void
    {
        parent::connect($ctx->clone()->secure(true), $header);
    }
    /**
     * @psalm-pure
     */
    #[\Override]
    public static function getName(): string
    {
        return self::class;
    }
}
<?php

declare(strict_types=1);

/**
 * Websocket stream wrapper.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Stream\Transport;

use Amp\Cancellation;
use Amp\Http\Client\HttpClientBuilder;
use Amp\Socket\Socket;
use Amp\Websocket\Client\Connector;
use Amp\Websocket\Client\Rfc6455ConnectionFactory;
use Amp\Websocket\Client\Rfc6455Connector;
use Amp\Websocket\Client\WebsocketConnection;
use Amp\Websocket\Client\WebsocketConnector;
use Amp\Websocket\Client\WebsocketHandshake;
use Amp\Websocket\ClosedException;
use Amp\Websocket\WebsocketMessage;
use AssertionError;
use danog\MadelineProto\Stream\ConnectionContext;
use danog\MadelineProto\Stream\ProxyStreamInterface;
use danog\MadelineProto\Stream\RawStreamInterface;
use Throwable;

/**
 * Websocket stream wrapper.
 *
 * @author Daniil Gentili <daniil@daniil.it>
 *
 * @implements ProxyStreamInterface<?WebsocketConnector>
 */
class WsStream implements RawStreamInterface, ProxyStreamInterface
{
    /**
     * Websocket stream.
     */
    private WebsocketConnection $stream;
    /**
     * Websocket message.
     *
     */
    private ?WebsocketMessage $message = null;
    /**
     * Websocket Connector.
     *
     */
    private WebsocketConnector $connector;
    /**
     * Connect to stream.
     *
     * @param ConnectionContext $ctx The connection context
     */
    #[\Override]
    public function connect(ConnectionContext $ctx, string $header = ''): void
    {
        $uri = $ctx->getStringUri();
        $uri = str_replace('tcp://', $ctx->isSecure() ? 'wss://' : 'ws://', $uri);
        $handshake = new WebsocketHandshake($uri, ['Sec-WebSocket-Protocol' => 'binary']);
        $this->stream = ($this->connector ?? new Rfc6455Connector(new Rfc6455ConnectionFactory(), HttpClientBuilder::buildDefault()))->connect($handshake, $ctx->getCancellation());
        if (\strlen($header)) {
            $this->write($header);
        }
    }
    /**
     * Async close.
     */
    #[\Override]
    public function disconnect(): void
    {
        try {
            $this->stream->close();
        } catch (Throwable $e) {
        }
    }
    #[\Override]
    public function read(?Cancellation $token = null): ?string
    {
        try {
            if (!$this->message) {
                $this->message = $this->stream->receive($token);
                if (!$this->message) {
                    return null;
                }
                $data = $this->message->buffer($token);
                $this->message = null;
            }
        } catch (Throwable $e) {
            if ($e instanceof ClosedException && $e->getReason() !== 'Client closed the underlying TCP connection') {
                throw $e;
            }
            return null;
        }
        return $data;
    }
    /**
     * Async write.
     *
     * @param string $data Data to write
     */
    #[\Override]
    public function write(string $data): void
    {
        $this->stream->sendBinary($data);
    }
    /**
     * {@inheritdoc}
     *
     * @psalm-pure
     */
    #[\Override]
    public function getSocket(): Socket
    {
        throw new AssertionError("Unreachable!");
    }
    /**
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function setExtra($extra): void
    {
        if ($extra instanceof WebsocketConnector) {
            $this->connector = $extra;
        }
    }
    /**
     * @psalm-pure
     */
    public static function getName(): string
    {
        return self::class;
    }
}
<?php

declare(strict_types=1);

/**
 * Default stream wrapper.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Stream\Transport;

use Amp\ByteStream\ClosedException;
use Amp\Cancellation;
use Amp\Socket\ClientTlsContext;
use Amp\Socket\Connector;
use Amp\Socket\Socket;
use Amp\Socket\SocketConnector;
use AssertionError;
use danog\MadelineProto\Logger;
use danog\MadelineProto\Stream\ConnectionContext;
use danog\MadelineProto\Stream\ProxyStreamInterface;
use danog\MadelineProto\Stream\RawStreamInterface;
use Throwable;
use Webmozart\Assert\Assert;

use function Amp\Socket\socketConnector;

/**
 * Default stream wrapper.
 *
 * Manages reading data in chunks
 *
 * @author Daniil Gentili <daniil@daniil.it>
 *
 * @implements ProxyStreamInterface<?SocketConnector>
 */
class DefaultStream implements RawStreamInterface, ProxyStreamInterface
{
    /**
     * Socket.
     *
     */
    protected ?Socket $stream = null;
    /**
     * Connector.
     */
    protected ?SocketConnector $connector = null;
    public function setupTls(?Cancellation $cancellationToken = null): void
    {
        $this->stream->setupTls($cancellationToken);
    }
    /**
     * @psalm-pure
     */
    public function getStream(): RawStreamInterface
    {
        throw new AssertionError("No underlying stream!");
    }
    #[\Override]
    public function connect(ConnectionContext $ctx, string $header = ''): void
    {
        $ctx = $ctx->clone();
        $uri = $ctx->getUri();
        $secure = $ctx->isSecure();
        if ($secure) {
            $ctx->setSocketContext($ctx->getSocketContext()->withTlsContext(new ClientTlsContext($uri->getHost())));
        }
        $this->stream = (($this->connector ?? socketConnector())->connect((string) $uri, $ctx->getSocketContext(), $ctx->getCancellation()));
        if ($secure) {
            $this->stream->setupTls();
        }
        $this->stream->write($header);
    }
    /**
     * Async chunked read.
     */
    #[\Override]
    public function read(?Cancellation $cancellation = null): ?string
    {
        return $this->stream ? $this->stream->read($cancellation) : null;
    }
    /**
     * Async write.
     *
     * @param string $data Data to write
     */
    #[\Override]
    public function write(string $data): void
    {
        if (!$this->stream) {
            throw new ClosedException('MadelineProto stream was disconnected');
        }
        $this->stream->write($data);
    }
    /**
     * Close.
     */
    #[\Override]
    public function disconnect(): void
    {
        try {
            if ($this->stream) {
                $this->stream->close();
                $this->stream = null;
            }
        } catch (Throwable $e) {
            Logger::log('Got exception while closing stream: '.$e->getMessage());
        }
    }
    /**
     * Close.
     */
    public function close(): void
    {
        $this->disconnect();
    }
    /**
     * {@inheritdoc}
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function getSocket(): Socket
    {
        Assert::notNull($this->stream);
        return $this->stream;
    }
    /**
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function setExtra($extra): void
    {
        $this->connector = $extra;
    }
    /**
     * @psalm-pure
     */
    public static function getName(): string
    {
        return self::class;
    }
}
<?php

declare(strict_types=1);

/**
 * Premade stream wrapper.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Stream\Transport;

use Amp\ByteStream\ClosedException;
use Amp\ByteStream\ReadableStream;
use Amp\ByteStream\WritableStream;
use Amp\Cancellation;
use Amp\Socket\Socket;
use danog\MadelineProto\Logger;
use danog\MadelineProto\Stream\ConnectionContext;
use danog\MadelineProto\Stream\ProxyStreamInterface;
use danog\MadelineProto\Stream\RawStreamInterface;
use Throwable;
use Webmozart\Assert\Assert;

/**
 * Premade stream wrapper.
 *
 * Manages reading data in chunks
 *
 * @author Daniil Gentili <daniil@daniil.it>
 *
 * @implements ProxyStreamInterface<Socket>
 */
final class PremadeStream implements RawStreamInterface, ProxyStreamInterface
{
    private Socket|ReadableStream|null $stream = null;
    /**
     * @psalm-mutation-free
     */
    public function __construct()
    {
    }
    public function setupTls(?Cancellation $cancellationToken = null): void
    {
        \assert($this->stream instanceof Socket);
        $this->stream->setupTls($cancellationToken);
    }
    public function getStream()
    {
        return $this->stream;
    }
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function connect(ConnectionContext $ctx, string $header = ''): void
    {
        if ($header !== '') {
            $this->stream->write($header);
        }
    }
    /**
     * Async chunked read.
     */
    #[\Override]
    public function read(?Cancellation $cancellation = null): ?string
    {
        return $this->stream ? $this->stream->read($cancellation) : null;
    }
    /**
     * Async write.
     *
     * @param string $data Data to write
     */
    #[\Override]
    public function write(string $data): void
    {
        if (!$this->stream) {
            throw new ClosedException('MadelineProto stream was disconnected');
        }
        \assert($this->stream instanceof WritableStream);
        /** @psalm-suppress UndefinedInterfaceMethod */
        $this->stream->write($data);
    }
    /**
     * Async close.
     */
    #[\Override]
    public function disconnect(): void
    {
        try {
            if ($this->stream) {
                if (method_exists($this->stream, 'close')) {
                    $this->stream->close();
                }
                $this->stream = null;
            }
        } catch (Throwable $e) {
            Logger::log('Got exception while closing stream: '.$e->getMessage());
        }
    }
    public function close(): void
    {
        $this->disconnect();
    }
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function getSocket(): Socket
    {
        Assert::true($this->stream instanceof Socket);
        return $this->stream;
    }
    /**
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function setExtra(mixed $extra): void
    {
        $this->stream = $extra;
    }
    /**
     * @psalm-pure
     */
    public static function getName(): string
    {
        return self::class;
    }
}
<?php

declare(strict_types=1);

/**
 * Buffered proxy stream interface.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Stream;

/**
 * Buffered proxy stream interface.
 *
 * @author Daniil Gentili <daniil@daniil.it>
 *
 * @template TT
 * @extends ProxyStreamInterface<TT>
 * @psalm-mutable
 */
interface BufferedProxyStreamInterface extends BufferedStreamInterface, ProxyStreamInterface
{
}
<?php

declare(strict_types=1);

/**
 * ADNL stream wrapper.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Stream\ADNLTransport;

use Amp\Socket\Socket;
use danog\MadelineProto\Stream\BufferedStreamInterface;
use danog\MadelineProto\Stream\ConnectionContext;
use danog\MadelineProto\Stream\MTProtoBufferInterface;
use danog\MadelineProto\Stream\RawStreamInterface;
use danog\MadelineProto\Tools;

/**
 * ADNL stream wrapper.
 *
 * Manages ADNL envelope
 *
 * @author Daniil Gentili <daniil@daniil.it>
 */
final class ADNLStream implements BufferedStreamInterface, MTProtoBufferInterface
{
    private $stream;
    /**
     * Connect to stream.
     *
     * @param ConnectionContext $ctx The connection context
     */
    #[\Override]
    public function connect(ConnectionContext $ctx, string $header = ''): void
    {
        $this->stream = $ctx->getStream($header);
    }
    /**
     * Async close.
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function disconnect(): void
    {
        $this->stream->disconnect();
    }
    /**
     * Get write buffer asynchronously.
     *
     * @param int $length Length of data that is going to be written to the write buffer
     */
    #[\Override]
    public function getWriteBuffer(int $length, string $append = ''): \danog\MadelineProto\Stream\WriteBufferInterface
    {
        $length += 64;
        $buffer = $this->stream->getWriteBuffer($length + 4, $append);
        $buffer->bufferWrite(pack('V', $length));
        $this->stream->startWriteHash();
        $this->stream->checkWriteHash($length - 32);
        $buffer->bufferWrite(Tools::random(32));
        return $buffer;
    }
    /**
     * Get read buffer asynchronously.
     *
     * @param int $length Length of payload, as detected by this layer
     */
    #[\Override]
    public function getReadBuffer(?int &$length): \danog\MadelineProto\Stream\ReadBufferInterface
    {
        $buffer = $this->stream->getReadBuffer($l);
        $length = unpack('V', $buffer->bufferRead(4))[1] - 32;
        $this->stream->startReadHash();
        $this->stream->checkReadHash($length);
        $buffer->bufferRead(32);
        $length -= 32;
        return $buffer;
    }
    /**
     * {@inheritdoc}
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function getSocket(): Socket
    {
        return $this->stream->getSocket();
    }
    /**
     * {@inheritDoc}
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function getStream(): RawStreamInterface
    {
        return $this->stream;
    }
    /**
     * @psalm-pure
     */
    #[\Override]
    public static function getName(): string
    {
        return self::class;
    }
}
<?php

declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Reactive;

use danog\Loop\GenericLoop;
use SplQueue;

/**
 * @template T
 *
 * @internal
 *
 * @implements Subscriber<T>
 */
final class Actor implements Subscriber
{
    /** @var SplQueue<list{T}|list{T, T}> */
    private readonly SplQueue $queue;
    private GenericLoop $loop;

    public function __construct(
        /** @var Subscriber<T> $subscriber */
        private readonly Subscriber $subscriber
    ) {
        /** @var SplQueue<list{T}|list{T, T}> */
        $this->queue = new SplQueue;
        $this->queue->setIteratorMode(SplQueue::IT_MODE_DELETE);
        $this->__wakeup();
    }

    /**
     * @psalm-pure
     */
    public function __sleep()
    {
        return ['queue', 'subscriber'];
    }

    public function __wakeup(): void
    {
        $this->loop = new GenericLoop(function (): ?float {
            foreach ($this->queue as $item) {
                if (\count($item) === 1) {
                    $this->subscriber->onAttach($item[0]);
                } else {
                    $this->subscriber->onStateChange($item[0], $item[1]);
                }
            }
            return GenericLoop::PAUSE;
        }, '');
    }

    #[\Override]
    public function onAttach($initState): void
    {
        $this->queue->enqueue([$initState]);
        if ($this->loop->isRunning()) {
            $this->loop->resume(true);
        } else {
            $this->loop->start();
        }
    }

    #[\Override]
    public function onStateChange($prevState, $state): void
    {
        $this->queue->enqueue([$prevState, $state]);
        if ($this->loop->isRunning()) {
            $this->loop->resume(true);
        } else {
            $this->loop->start();
        }
    }

    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function __toString(): string
    {
        return 'Actor<' . $this->subscriber::class . '>';
    }
}
<?php

declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Reactive;

/**
 * @internal
 * @psalm-mutable
 */
interface EphemeralSubscriber
{
}
<?php

declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Reactive;

/**
 * Only kept to load the sessions saved by the versions up to 8.8.9, which saved the fibers waiting for a state
 * (without their state): Publisher drops them when loading, and keeps the waits itself (Publisher::waitForState()).
 *
 * @internal
 *
 * @implements Subscriber<mixed>
 */
final class AsyncWaiter implements Subscriber, EphemeralSubscriber
{
    /**
     * @psalm-pure
     */
    public function __sleep()
    {
        return [];
    }

    #[\Override]
    public function onAttach($initState): void
    {
    }

    #[\Override]
    public function onStateChange($prevState, $state): void
    {
    }
}
<?php

declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Reactive;

/**
 * @internal
 *
 * @template T
 *
 * @implements Subscriber<T>
 */
final class SimpleSubscriberAdaptor implements Subscriber
{
    /**
     * @psalm-mutation-free
     */
    public function __construct(
        /** @var SimpleSubscriber<T> $subscriber */
        public readonly SimpleSubscriber $subscriber
    ) {
    }

    #[\Override]
    public function onAttach($initState): void
    {
        $this->subscriber->onSimpleStateChange($initState);
    }

    #[\Override]
    public function onStateChange($prevState, $state): void
    {
        $this->subscriber->onSimpleStateChange($state);
    }
}
<?php

declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Reactive;

/**
 * @internal
 *
 * @template T
 *
 * @extends BaseSubscriber<T>
 * @psalm-mutable
 */
interface Subscriber extends BaseSubscriber
{
    /**
     * @param T $initState
     * @psalm-impure
     */
    public function onAttach($initState): void;
    /**
     * @param T $prevState
     * @param T $state
     * @psalm-impure
     */
    public function onStateChange($prevState, $state): void;
}
<?php

declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Reactive;

use Amp\Cancellation;
use Amp\DeferredFuture;
use WeakMap;
use Webmozart\Assert\Assert;

/**
 * @internal
 *
 * @template T
 */
final class Publisher
{
    /** @var WeakMap<BaseSubscriber<T>, Subscriber<T>> */
    private WeakMap $subscribers;
    private bool $wokeup = false;
    /**
     * Fibers waiting for a state (waitForState()): never serialized, a wait can't survive a restart.
     *
     * @var array<int, list{T, DeferredFuture<null>}>
     */
    private array $waiters = [];
    private int $waiterId = 0;
    /**
     * @param T $state
     *
     * @psalm-mutation-free
     */
    public function __construct(
        private mixed $state
    ) {
        $this->subscribers = new WeakMap;
        $this->wokeup = true;
    }

    /** @return T */
    public function getState(): mixed
    {
        return $this->state;
    }

    public function __serialize(): array
    {
        $subscribers = [];
        foreach ($this->subscribers as $subscriber => $v) {
            if ($subscriber instanceof EphemeralSubscriber) {
                continue;
            }
            $subscribers []= [$subscriber, $v];
        }
        return ['state' => $this->state, 'subscribers' => $subscribers];
    }

    /**
     * @param array{state: T, subscribers: list<{BaseSubscriber<T>, Subscriber<T>}>} $data
     */
    public function __unserialize(array $data): void
    {
        $this->state = $data['state'];
        /** @var WeakMap<BaseSubscriber<T>, Subscriber<T>>  */
        $this->subscribers = new WeakMap;
        foreach ($data['subscribers'] as [$subscriber, $v]) {
            // The waiters (AsyncWaiter) saved by the versions up to 8.8.9, without their state: a wait can't survive a
            // restart.
            if ($subscriber instanceof EphemeralSubscriber) {
                continue;
            }
            $this->subscribers[$subscriber] = $v;
        }
    }

    public function wakeup(): void
    {
        if (!$this->wokeup) {
            $this->wokeup = true;
            foreach ($this->subscribers as $v) {
                $v->onAttach($this->state);
            }
        }
    }

    /** @param BaseSubscriber<T> $subscriber */
    public function subscribe(BaseSubscriber $subscriber): void
    {
        if ($subscriber instanceof SimpleSubscriber) {
            $subscriberK = $subscriber;
            $subscriber = new SimpleSubscriberAdaptor($subscriber);
        } else {
            Assert::isInstanceOf($subscriber, Subscriber::class);
            $subscriberK = $subscriber;
        }
        if (!isset($this->subscribers[$subscriberK])) {
            $subscriber = new Actor($subscriber);
            $this->subscribers[$subscriberK] = $subscriber;
            $subscriber->onAttach($this->state);
        }
    }

    /** @param BaseSubscriber<T> $subscriber */
    public function unsubscribe(BaseSubscriber $subscriber): void
    {
        unset($this->subscribers[$subscriber]);
    }

    /** @param T $state */
    public function publish($state): void
    {
        if ($state !== $this->state) {
            $prev = $this->state;
            $this->state = $state;
            $this->wokeup = true;
            foreach ($this->waiters as $id => [$waitFor, $deferred]) {
                if ($state === $waitFor) {
                    // Completed once: removed even before the waiting fiber resumes.
                    unset($this->waiters[$id]);
                    $deferred->complete();
                }
            }
            foreach ($this->subscribers as $subscriber) {
                $subscriber->onStateChange($prev, $state);
            }
        }
    }

    /** @param T $state */
    public function waitForState($state, ?Cancellation $cancellation = null): void
    {
        if ($state === $this->state) {
            return;
        }
        $id = $this->waiterId++;
        $deferred = new DeferredFuture;
        $this->waiters[$id] = [$state, $deferred];
        try {
            $deferred->getFuture()->await($cancellation);
        } finally {
            unset($this->waiters[$id]);
        }
    }
}
<?php

declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Reactive;

/**
 * @internal
 *
 * @template T
 *
 * @extends BaseSubscriber<T>
 * @psalm-mutable
 */
interface SimpleSubscriber extends BaseSubscriber
{
    /**
     * @param T $state
     * @psalm-impure
     */
    public function onSimpleStateChange($state): void;
}
<?php

declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\Reactive;

/**
 * @internal
 *
 * @template T
 * @psalm-mutable
 */
interface BaseSubscriber
{
}
<?php

declare(strict_types=1);

/**
 * ApiStart module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\ApiWrappers;

use Amp\ByteStream\WritableStream;
use Amp\Http\Client\HttpException;
use danog\MadelineProto\Exception;
use danog\MadelineProto\Lang;
use danog\MadelineProto\Magic;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\MyTelegramOrgWrapper;
use danog\MadelineProto\Settings;
use danog\MadelineProto\Tools;
use danog\MadelineProto\WebUI\Action;
use danog\MadelineProto\WebUI\Alert;
use danog\MadelineProto\WebUI\Field;
use danog\MadelineProto\WebUI\FieldType;
use danog\MadelineProto\WebUI\Page;
use danog\MadelineProto\WebUI\SafeHtml;
use danog\MadelineProto\WebUI\Step;
use danog\MadelineProto\WebUI\Template;
use danog\MadelineProto\WebUI\WebUI;

use const PHP_EOL;

use const PHP_SAPI;

use function Amp\ByteStream\getStdout;

/**
 * Gets the API ID and hash, manually or automatically from my.telegram.org.
 *
 * On the web, the my.telegram.org login spans multiple requests, made before the session exists: its state is
 * kept in the session directory (see {@see \danog\MadelineProto\SessionPaths::getApiSetupState()}).
 *
 * @internal
 */
trait Start
{
    /**
     * Start API ID generation process.
     *
     * @param Settings $settings Settings
     *
     * @return ?array{api_id: int, api_hash: string} Null if a page was shown, and the request should end
     */
    private function APIStart(Settings $settings): ?array
    {
        if (Magic::$isIpcWorker) {
            throw new Exception('Not inited!');
        }
        if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
            return $this->APIStartCli($settings);
        }
        return $this->APIStartWeb($settings);
    }

    /**
     * @return array{api_id: int, api_hash: string}
     */
    private function APIStartCli(Settings $settings): array
    {
        $stdout = getStdout();
        $stdout->write(\sprintf(Lang::$current_lang['apiChooseManualAutoTip'], 'https://docs.madelineproto.xyz/docs/SETTINGS.html').PHP_EOL);
        $stdout->write(Lang::$current_lang['apiChooseManualAuto'].PHP_EOL);
        if (!str_contains(strtolower(Tools::readLine(Lang::$current_lang['apiChoosePrompt'])), 'm')) {
            $app = $this->APIStartCliAuto($settings, $stdout);
            if ($app !== null) {
                return $app;
            }
            $stdout->write(Lang::$current_lang['apiAutoFallbackManual'].PHP_EOL.PHP_EOL);
        }

        $stdout->write('1) '.Lang::$current_lang['apiManualInstructions0'].PHP_EOL);
        $stdout->write('2) '.Lang::$current_lang['apiManualInstructions1'].PHP_EOL);
        $stdout->write('3) ');
        foreach (['App title', 'Short name', 'URL', 'Platform', 'Description'] as $k => $key) {
            $stdout->write($k ? "    $key: " : "$key: ");
            $stdout->write(Lang::$current_lang["apiAppInstructionsManual$k"].PHP_EOL);
        }
        $stdout->write('4) '.Lang::$current_lang['apiManualInstructions2'].PHP_EOL);

        return [
            'api_id' => (int) Tools::readLine('5) '.Lang::$current_lang['apiManualPrompt0']),
            'api_hash' => trim(Tools::readLine('6) '.Lang::$current_lang['apiManualPrompt1'], hidden: true)),
        ];
    }

    /**
     * Get the API ID and hash from my.telegram.org on the CLI.
     *
     * @return ?array{api_id: int, api_hash: string} Null if it failed, and the user should enter them manually
     */
    private function APIStartCliAuto(Settings $settings, WritableStream $stdout): ?array
    {
        try {
            $wrapper = new MyTelegramOrgWrapper($settings);
            $wrapper->login(trim(Tools::readLine(Lang::$current_lang['apiAutoPrompt0'])));
            $wrapper->completeLogin(trim(Tools::readLine(Lang::$current_lang['apiAutoPrompt1'], hidden: true)));
            if ($wrapper->hasApp()) {
                return $wrapper->getApp();
            }
        } catch (Exception|HttpException $e) {
            $stdout->write(PHP_EOL.Lang::$current_lang['apiAutoErrorTitle'].': '.$e->getMessage().PHP_EOL);
            return null;
        }
        try {
            return $wrapper->createApp([
                'app_title' => Tools::readLine(Lang::$current_lang['apiAppInstructionsAuto0']),
                'app_shortname' => Tools::readLine(Lang::$current_lang['apiAppInstructionsAuto1']),
                'app_url' => Tools::readLine(Lang::$current_lang['apiAppInstructionsAuto2']),
                'app_platform' => 'other',
                'app_desc' => Tools::readLine(Lang::$current_lang['apiAppInstructionsAuto4']),
            ]);
        } catch (Exception|HttpException $e) {
            $stdout->write(PHP_EOL.self::getCreateAppErrorAlert($e)->toText().PHP_EOL);
            return null;
        }
    }

    /**
     * @return ?array{api_id: int, api_hash: string}
     */
    private function APIStartWeb(Settings $settings): ?array
    {
        $templates = $settings->getTemplates();
        $wrapper = $this->session->getApiSetupState();
        $action = $_POST['api_setup'] ?? null;

        if ($action === 'manual') {
            $this->session->storeApiSetupState(null);
            WebUI::write($templates, self::getManualApiPage());
            return null;
        }
        if ($action === 'reset') {
            // Actions submit the whole form, ignore its fields
            $this->session->storeApiSetupState(null);
            $wrapper = null;
            $_POST = [];
        }

        if (isset($_POST['api_id'], $_POST['api_hash'])) {
            $apiId = trim((string) $_POST['api_id']);
            $apiHash = strtolower(trim((string) $_POST['api_hash']));
            if (!ctype_digit($apiId) || (int) $apiId <= 0 || !preg_match('/^[a-f0-9]{32}$/', $apiHash)) {
                WebUI::write($templates, self::getManualApiPage()->addAlert(Alert::error(Lang::$current_lang['apiInvalidWeb'])));
                return null;
            }
            $this->session->storeApiSetupState(null);
            return ['api_id' => (int) $apiId, 'api_hash' => $apiHash];
        }

        if ($wrapper === null) {
            if (!isset($_POST['my_phone'])) {
                WebUI::write($templates, self::getAutoApiPage());
                return null;
            }
            $phone = trim((string) $_POST['my_phone']);
            try {
                $wrapper = new MyTelegramOrgWrapper($settings);
                $wrapper->login($phone);
            } catch (Exception|HttpException $e) {
                WebUI::write($templates, self::getAutoApiPage($phone)->addAlert(self::getAutoErrorAlert($e)));
                return null;
            }
            $this->session->storeApiSetupState($wrapper);
            WebUI::redirectToSelf() || WebUI::write($templates, self::getCodeApiPage($wrapper->getPhoneNumber()));
            return null;
        }

        if (!$wrapper->loggedIn()) {
            if (!isset($_POST['my_code'])) {
                WebUI::write($templates, self::getCodeApiPage($wrapper->getPhoneNumber()));
                return null;
            }
            try {
                $wrapper->completeLogin(trim((string) $_POST['my_code']));
                if ($wrapper->hasApp()) {
                    $this->session->storeApiSetupState(null);
                    return $wrapper->getApp();
                }
            } catch (Exception|HttpException $e) {
                $this->session->storeApiSetupState($wrapper);
                WebUI::write($templates, self::getCodeApiPage($wrapper->getPhoneNumber())->addAlert(self::getAutoErrorAlert($e)));
                return null;
            }
            // Logged in, with the creation hash of the app
            $this->session->storeApiSetupState($wrapper);
            WebUI::redirectToSelf() || WebUI::write($templates, self::getCreateApiPage());
            return null;
        }

        if (!isset($_POST['app_title'], $_POST['app_shortname'])) {
            WebUI::write($templates, self::getCreateApiPage());
            return null;
        }
        $app = [
            'app_title' => trim((string) $_POST['app_title']),
            'app_shortname' => trim((string) $_POST['app_shortname']),
            'app_url' => trim((string) ($_POST['app_url'] ?? '')),
            'app_platform' => 'other',
            'app_desc' => trim((string) ($_POST['app_desc'] ?? '')),
        ];
        if ($app['app_title'] === '' || $app['app_shortname'] === '') {
            WebUI::write($templates, self::getCreateApiPage()->addAlert(Alert::error(Lang::$current_lang['apiParamsError'])));
            return null;
        }
        try {
            $result = $wrapper->createApp($app);
        } catch (Exception|HttpException $e) {
            // Keep the login: the error may be fixable (a short name already taken), and from a datacenter IP
            // the page offers the manual setup
            $this->session->storeApiSetupState($wrapper);
            WebUI::write($templates, self::getCreateApiPage()->addAlert(self::getCreateAppErrorAlert($e)));
            return null;
        }
        $this->session->storeApiSetupState(null);
        return $result;
    }

    private static function getCreateAppErrorAlert(\Throwable $e): Alert
    {
        return Alert::error(
            SafeHtml::linkify(rtrim($e->getMessage(), '.!').'. '.Lang::$current_lang['apiCreateVpsWarning']),
            Lang::$current_lang['apiCreateErrorTitle'],
        );
    }

    private static function getAutoErrorAlert(\Throwable $e): Alert
    {
        return Alert::error(
            \sprintf(Lang::$current_lang['webError'], rtrim($e->getMessage(), '.!')),
            Lang::$current_lang['apiAutoErrorTitle'],
        );
    }

    private static function getAutoApiPage(string $phone = ''): Page
    {
        return new Page(
            title: Lang::$current_lang['apiSetupTitleWeb'],
            view: 'api.auto',
            lead: Lang::$current_lang['apiSetupLeadWeb'],
            step: Step::APP,
            fields: [
                new Field(
                    name: 'my_phone',
                    label: Lang::$current_lang['loginUserPhoneWeb'],
                    type: FieldType::PHONE,
                    value: $phone,
                    placeholder: '+39 123 456 7890',
                    hint: Lang::$current_lang['apiAutoPhoneHintWeb'],
                    autofocus: true,
                ),
            ],
            submit: Lang::$current_lang['apiSendCodeWeb'],
            alerts: MTProto::getWebAlerts(),
            actions: [new Action(Lang::$current_lang['apiChooseManualWeb'], 'api_setup', 'manual')],
        );
    }

    private static function getCodeApiPage(string $phoneNumber): Page
    {
        return new Page(
            title: Lang::$current_lang['apiAutoCodeTitleWeb'],
            view: 'api.code',
            data: ['phoneNumber' => $phoneNumber],
            lead: Lang::$current_lang['apiAutoCodeLeadWeb'],
            step: Step::APP,
            fields: [
                // my.telegram.org codes are alphanumeric
                new Field(
                    name: 'my_code',
                    label: Lang::$current_lang['apiAutoCodeLabelWeb'],
                    type: FieldType::PASSWORD,
                    autocomplete: 'one-time-code',
                    autofocus: true,
                ),
            ],
            submit: Lang::$current_lang['webContinue'],
            alerts: MTProto::getWebAlerts(),
            actions: [
                new Action(Lang::$current_lang['apiStartOverWeb'], 'api_setup', 'reset'),
                new Action(Lang::$current_lang['apiChooseManualWeb'], 'api_setup', 'manual'),
            ],
        );
    }

    private static function getCreateApiPage(): Page
    {
        return new Page(
            title: Lang::$current_lang['apiCreateTitleWeb'],
            view: 'api.create',
            lead: Lang::$current_lang['apiCreateLeadWeb'],
            step: Step::APP,
            fields: [
                new Field(
                    name: 'app_title',
                    label: Lang::$current_lang['apiAppTitleLabelWeb'],
                    value: (string) ($_POST['app_title'] ?? ''),
                    hint: ucfirst(Lang::$current_lang['apiAppInstructionsManual0']),
                    autofocus: true,
                ),
                new Field(
                    name: 'app_shortname',
                    label: Lang::$current_lang['apiAppShortNameLabelWeb'],
                    value: (string) ($_POST['app_shortname'] ?? ''),
                    hint: ucfirst(Lang::$current_lang['apiAppInstructionsManual1']),
                    maxLength: 32,
                ),
                new Field(
                    name: 'app_url',
                    label: Lang::$current_lang['apiAppUrlLabelWeb'],
                    value: (string) ($_POST['app_url'] ?? ''),
                    hint: ucfirst(Lang::$current_lang['apiAppInstructionsManual2']),
                    required: false,
                ),
                new Field(
                    name: 'app_desc',
                    label: Lang::$current_lang['apiAppDescLabelWeb'],
                    type: FieldType::TEXTAREA,
                    value: (string) ($_POST['app_desc'] ?? ''),
                    required: false,
                ),
            ],
            submit: Lang::$current_lang['apiCreateWeb'],
            alerts: MTProto::getWebAlerts(),
            actions: [
                new Action(Lang::$current_lang['apiStartOverWeb'], 'api_setup', 'reset'),
                new Action(Lang::$current_lang['apiChooseManualWeb'], 'api_setup', 'manual'),
            ],
        );
    }

    private static function getManualApiPage(): Page
    {
        $appFields = '';
        foreach (['App title', 'Short name', 'URL', 'Platform', 'Description'] as $k => $key) {
            $appFields .= '<li>'.SafeHtml::escape("$key: ".Lang::$current_lang["apiAppInstructionsManual$k"])->html.'</li>';
        }
        return new Page(
            title: Lang::$current_lang['apiManualWeb'],
            view: 'api.manual',
            lead: new SafeHtml(\sprintf(Lang::$current_lang['apiChooseManualAutoTipWeb'], 'https://docs.madelineproto.xyz/docs/SETTINGS.html')),
            step: Step::APP,
            fields: [
                new Field(
                    name: 'api_id',
                    label: 'API ID',
                    value: trim((string) ($_POST['api_id'] ?? '')),
                    placeholder: '1234567',
                    autofocus: true,
                ),
                new Field(
                    name: 'api_hash',
                    label: 'API hash',
                    type: FieldType::PASSWORD,
                    value: trim((string) ($_POST['api_hash'] ?? '')),
                    autocomplete: 'off',
                    placeholder: '0123456789abcdef0123456789abcdef',
                    maxLength: 32,
                ),
            ],
            submit: Lang::$current_lang['apiSaveWeb'],
            alerts: MTProto::getWebAlerts(),
            actions: [new Action(Lang::$current_lang['apiChooseAutoWeb'], 'api_setup', 'reset')],
            instructions: [
                SafeHtml::linkify(Lang::$current_lang['apiManualInstructions0']),
                Lang::$current_lang['apiManualInstructions1'],
                new SafeHtml(SafeHtml::escape(Template::translate('apiAppWeb'))->html.":<ul>$appFields</ul>"),
                Lang::$current_lang['apiManualInstructions2'],
            ],
        );
    }
}
<?php

declare(strict_types=1);

/**
 * Logger module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use Amp\ByteStream\Pipe;
use Amp\ByteStream\WritableResourceStream;
use Amp\ByteStream\WritableStream;
use danog\Loop\Loop;
use danog\MadelineProto\Settings\Logger as SettingsLogger;
use Psr\Log\LoggerInterface;
use Revolt\EventLoop;
use Throwable;
use Webmozart\Assert\Assert;

use const DEBUG_BACKTRACE_IGNORE_ARGS;
use const DIRECTORY_SEPARATOR;

use const E_ALL;
use const FILE_APPEND;
use const JSON_PRETTY_PRINT;
use const JSON_UNESCAPED_SLASHES;
use const PATHINFO_DIRNAME;
use const PHP_EOL;
use const PHP_SAPI;

use function Amp\async;
use function Amp\ByteStream\getStderr;
use function Amp\ByteStream\getStdout;
use function Amp\ByteStream\pipe;

/**
 * Logger class.
 */
final class Logger
{
    /**
     * @internal ANSI foreground color escapes
     */
    public const FOREGROUND = ['default' => 39, 'black' => 30, 'red' => 31, 'green' => 32, 'yellow' => 33, 'blue' => 34, 'magenta' => 35, 'cyan' => 36, 'light_gray' => 37, 'dark_gray' => 90, 'light_red' => 91, 'light_green' => 92, 'light_yellow' => 93, 'light_blue' => 94, 'light_magenta' => 95, 'light_cyan' => 96, 'white' => 97];
    /**
     * @internal ANSI background color escapes
     */
    public const BACKGROUND = ['default' => 49, 'black' => 40, 'red' => 41, 'magenta' => 45, 'yellow' => 43, 'green' => 42, 'blue' => 44, 'cyan' => 46, 'light_gray' => 47, 'dark_gray' => 100, 'light_red' => 101, 'light_green' => 102, 'light_yellow' => 103, 'light_blue' => 104, 'light_magenta' => 105, 'light_cyan' => 106, 'white' => 107];
    /**
     * @internal ANSI modifier escapes
     */
    public const SET = ['bold' => 1, 'dim' => 2, 'underlined' => 3, 'blink' => 4, 'reverse' => 5, 'hidden' => 6];
    /**
     * @internal ANSI reset modifier escapes
     */
    public const RESET = ['all' => 0, 'bold' => 21, 'dim' => 22, 'underlined' => 24, 'blink' => 25, 'reverse' => 26, 'hidden' => 28];
    /**
     * Logging mode.
     *
     */
    private readonly int $mode;
    /**
     * Optional logger parameter.
     *
     * @var null|string|callable
     */
    private readonly mixed $optional;
    /**
     * Logger prefix.
     *
     */
    private readonly string $prefix;
    /**
     * Logging level.
     *
     */
    private readonly int $level;
    /**
     * Logging colors.
     *
     */
    private array $colors;
    /**
     * Newline.
     *
     */
    private readonly string $newline;
    /**
     * Logfile.
     *
     */
    private readonly WritableStream $stdout;
    /**
     * Unbuffered logfile.
     *
     */
    private readonly WritableStream $stdoutUnbuffered;
    /**
     * @var array<int, list{WritableStream, \Amp\Future}>
     */
    private static array $closePromises = [];
    /**
     * Log rotation loop ID.
     */
    private ?string $rotateId = null;
    /**
     * Log rotation loop ID of each log file, by real path.
     *
     * @var array<string, string>
     */
    private static array $rotations = [];
    /**
     * Real path of the log file, in file mode.
     */
    private ?string $realPath = null;
    /**
     * PSR logger.
     */
    private readonly PsrLogger $psr;
    /**
     * Default logger instance.
     */
    public static ?self $default = null;
    /**
     * Whether the AGPL notice was printed.
     *
     */
    private static bool $printed = false;
    /**
     * Ultra verbose logging.
     *
     * @internal
     */
    public const ULTRA_VERBOSE = 5;
    /**
     * Maximum length of a log entry, in bytes: longer ones (like exceptions whose message contains a
     * huge query) only keep their beginning and their end.
     *
     * @internal
     */
    public const MAX_ENTRY_LENGTH = 64 * 1024;
    /**
     * Verbose logging.
     *
     * @internal
     */
    public const VERBOSE = 4;
    /**
     * Notice logging.
     *
     * @internal
     */
    public const NOTICE = 3;
    /**
     * Warning logging.
     *
     * @internal
     */
    public const WARNING = 2;
    /**
     * Error logging.
     *
     * @internal
     */
    public const ERROR = 1;
    /**
     * Log only fatal errors.
     *
     * @internal
     */
    public const FATAL_ERROR = 0;

    /**
     * Default logger (syslog).
     *
     * @internal
     */
    public const DEFAULT_LOGGER = 1;
    /**
     * File logger.
     *
     * @internal
     */
    public const FILE_LOGGER = 2;
    /**
     * Echo logger.
     *
     * @internal
     */
    public const ECHO_LOGGER = 3;
    /**
     * Callable logger.
     *
     * @internal
     */
    public const CALLABLE_LOGGER = 4;

    /**
     * Ultra verbose level.
     */
    public const LEVEL_ULTRA_VERBOSE = self::ULTRA_VERBOSE;
    /**
     * Verbose level.
     */
    public const LEVEL_VERBOSE = self::VERBOSE;
    /**
     * Notice level.
     */
    public const LEVEL_NOTICE = self::NOTICE;
    /**
     * Warning level.
     */
    public const LEVEL_WARNING = self::WARNING;
    /**
     * Error level.
     */
    public const LEVEL_ERROR = self::ERROR;
    /**
     * Fatal error level.
     */
    public const LEVEL_FATAL = self::FATAL_ERROR;

    /**
     * Default logger (syslog).
     */
    public const LOGGER_DEFAULT = self::DEFAULT_LOGGER;
    /**
     * Echo logger.
     */
    public const LOGGER_ECHO = self::ECHO_LOGGER;
    /**
     * File logger.
     */
    public const LOGGER_FILE = self::FILE_LOGGER;
    /**
     * Callable logger.
     */
    public const LOGGER_CALLABLE = self::CALLABLE_LOGGER;

    /**
     * Construct global static logger from MadelineProto settings.
     *
     * @param SettingsLogger $settings Settings instance
     */
    public static function constructorFromSettings(SettingsLogger $settings): self
    {
        return self::$default = new self($settings);
    }

    /**
     * Construct logger.
     */
    public function __construct(SettingsLogger $settings, string $prefix = '')
    {
        $this->psr = new PsrLogger($this);
        $this->prefix = $prefix === '' ? '' : ', '.$prefix;

        $this->mode = $settings->getType();
        $this->level = $settings->getLevel();

        $optional = $settings->getExtra();

        $maxSize = $settings->getMaxSize();

        if ($this->mode === self::FILE_LOGGER) {
            if (!$optional || !file_exists(pathinfo($optional, PATHINFO_DIRNAME))) {
                $optional = Magic::$script_cwd.DIRECTORY_SEPARATOR.'MadelineProto.log';
            }
            if (!str_ends_with($optional, '.log')) {
                $optional .= '.log';
            }
            if ($maxSize !== -1 && file_exists($optional) && filesize($optional) > $maxSize) {
                file_put_contents($optional, '');
            }
        }
        $this->optional = $optional;
        $this->colors[self::ULTRA_VERBOSE] = implode(';', [self::FOREGROUND['light_gray'], self::SET['dim']]);
        $this->colors[self::VERBOSE] = implode(';', [self::FOREGROUND['green'], self::SET['bold']]);
        $this->colors[self::NOTICE] = implode(';', [self::FOREGROUND['yellow'], self::SET['bold']]);
        $this->colors[self::WARNING] = implode(';', [self::FOREGROUND['white'], self::SET['dim'], self::BACKGROUND['red']]);
        $this->colors[self::ERROR] = implode(';', [self::FOREGROUND['white'], self::SET['bold'], self::BACKGROUND['red']]);
        $this->colors[self::FATAL_ERROR] = implode(';', [self::FOREGROUND['red'], self::SET['bold'], self::BACKGROUND['light_gray']]);
        $newline = PHP_EOL;
        if ($this->mode === self::ECHO_LOGGER) {
            $stdout = getStdout();
            if (PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg') {
                $newline = '<br>'.$newline;
            }
        } elseif ($this->mode === self::FILE_LOGGER) {
            $stdout = new WritableResourceStream(fopen($this->optional, 'a'));
            // Only the last logger of a file rotates it: a logger that was replaced (by MTProto::setupLogger) may stay
            // alive, through the reference cycle with its PSR logger or whoever holds that, and would keep truncating
            // the file at its own maximum size.
            $this->realPath = realpath($this->optional) ?: $this->optional;
            if (isset(self::$rotations[$this->realPath])) {
                EventLoop::cancel(self::$rotations[$this->realPath]);
                unset(self::$rotations[$this->realPath]);
            }
            if ($maxSize !== -1) {
                $optional = $this->optional;
                $this->rotateId = self::$rotations[$this->realPath] = EventLoop::repeat(
                    10,
                    static function () use ($maxSize, $optional, $stdout): void {
                        clearstatcache(true, $optional);
                        if (file_exists($optional) && filesize($optional) >= $maxSize) {
                            ftruncate($stdout->getResource(), 0);
                            self::log("Automatically truncated logfile to $maxSize, MadelineProto ".\danog\MadelineProto\API::RELEASE);
                        }
                    },
                );
                EventLoop::unreference($this->rotateId);
            }
        } elseif ($this->mode === self::DEFAULT_LOGGER) {
            $result = \ini_get('error_log');
            if ($result === 'syslog') {
                $stdout = getStderr();
            } elseif ($result) {
                $stdout = new WritableResourceStream(fopen($result, 'a+'));
            } else {
                $stdout = getStderr();
            }
        }
        $this->newline = $newline;

        if (isset($stdout)) {
            $pipe = new Pipe(PHP_INT_MAX);
            $this->stdoutUnbuffered = $stdout;
            $this->stdout = $pipe->getSink();
            $source = $pipe->getSource();
            $promise = async(static function () use ($source, $stdout, &$promise): void {
                try {
                    pipe($source, $stdout);
                } finally {
                    unset(self::$closePromises[spl_object_id($promise)]);
                }
            });
            self::$closePromises[spl_object_id($promise)] = [$this->stdout, $promise];
        }

        self::$default = $this;
        if (PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg') {
            try {
                error_reporting(E_ALL);
                ini_set('log_errors', '1');
                ini_set('error_log', $this->mode === self::FILE_LOGGER
                    ? $this->optional
                    : Magic::$script_cwd.DIRECTORY_SEPARATOR.'MadelineProto.log');
            } catch (Exception) {
                $this->logger('Could not enable PHP logging');
            }
        }

        if (!self::$printed) {
            self::$printed = true;
            $this->colors[self::NOTICE] = implode(';', [self::FOREGROUND['light_gray'], self::SET['bold'], self::BACKGROUND['magenta']]);
            $this->logger('MadelineProto '.\danog\MadelineProto\API::RELEASE);
            $this->logger('Copyright (C) 2016-'.date('Y').' Daniil Gentili');
            $this->logger('Licensed under AGPLv3');
            $this->logger('https://github.com/danog/MadelineProto');

            $this->colors[self::NOTICE] = implode(';', [self::FOREGROUND['light_gray'], self::SET['bold'], self::BACKGROUND['blue']]);
            if (Lang::$currentPercentage !== 100) {
                $this->logger(\sprintf(Lang::$current_lang['translate_madelineproto_cli'], Lang::$currentPercentage));
            }
            $this->colors[self::NOTICE] = implode(';', [self::FOREGROUND['yellow'], self::SET['bold']]);
        }
    }
    /**
     * Truncate logfile.
     */
    public function truncate(): void
    {
        if ($this->mode === self::FILE_LOGGER) {
            Assert::true($this->stdoutUnbuffered instanceof WritableResourceStream);
            ftruncate($this->stdoutUnbuffered->getResource(), 0);
        }
    }
    /**
     * @internal Internal function used to flush the log buffer on shutdown.
     */
    public static function finalize(): void
    {
        foreach (self::$closePromises as [$stdout, $promise]) {
            $stdout->close();
            $promise->await();
        }
    }
    /**
     * Destructor function.
     */
    public function __destruct()
    {
        if ($this->rotateId) {
            EventLoop::cancel($this->rotateId);
            if ($this->realPath !== null && (self::$rotations[$this->realPath] ?? null) === $this->rotateId) {
                unset(self::$rotations[$this->realPath]);
            }
        }
    }
    /**
     * Log a message.
     *
     * @param mixed $param Message
     * @param int   $level Logging level
     */
    public static function log(mixed $param, int $level = self::NOTICE): void
    {
        if (!\is_null(self::$default)) {
            self::$default->logger($param, $level, basename(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1)[0]['file'], '.php'));
        } else {
            echo $param.PHP_EOL;
        }
    }
    /**
     * Truncate a string longer than the specified length (in bytes), keeping its beginning and its end.
     *
     * @internal
     *
     * @psalm-pure
     */
    public static function truncateEntry(string $string, int $length): string
    {
        $omitted = \strlen($string) - $length;
        if ($omitted <= 0) {
            return $string;
        }
        $half = intdiv($length, 2);
        return substr($string, 0, $half)."\n[... $omitted bytes omitted ...]\n".substr($string, -$half);
    }

    /**
     * Log a message.
     *
     * @param mixed  $param Message to log
     * @param int    $level Logging level
     * @param string $file  File that originated the message
     *
     * @psalm-taint-escape html
     * @psalm-taint-escape quotes
     */
    public function logger(mixed $param, int $level = self::NOTICE, string $file = ''): void
    {
        if ($level > $this->level) {
            return;
        }
        if (Magic::$suspendPeriodicLogging) {
            Magic::$suspendPeriodicLogging->getFuture()->map(fn () => $this->logger($param, $level, $file));
            return;
        }

        if ($this->mode === self::CALLABLE_LOGGER) {
            EventLoop::queue($this->optional, $param, $level);
            return;
        }
        $prefix = $this->prefix;
        if ($param instanceof Throwable) {
            $param = (string) $param;
        } elseif (!\is_string($param)) {
            $param = json_encode($param, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR);
        }
        $param = self::truncateEntry($param, self::MAX_ENTRY_LENGTH);
        if (empty($file)) {
            $file = basename(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1)[0]['file'], '.php');
        }
        $param = str_pad($file.$prefix.': ', 16 + \strlen($prefix))."\t".$param;
        if ($this->mode === self::DEFAULT_LOGGER) {
            try {
                $this->stdout->write($param.$this->newline);
            } catch (\Throwable) {
                error_log($param);
            }
            return;
        }
        $param = Magic::$isatty ? "\33[".$this->colors[$level].'m'.$param."\33[0m".$this->newline : $param.$this->newline;
        try {
            $this->stdout->write($param);
        } catch (\Throwable) {
            switch ($this->mode) {
                case self::ECHO_LOGGER:
                    echo $param;
                    break;
                case self::FILE_LOGGER:
                    file_put_contents($this->optional, $param, FILE_APPEND);
                    break;
            }
        }
    }

    /**
     * Get PSR logger.
     */
    public function getPsrLogger(): LoggerInterface
    {
        return $this->psr;
    }
}
<?php

declare(strict_types=1);

/**
 * Tools module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use danog\MadelineProto\EventHandler\Message\Entities\Code;
use danog\MadelineProto\EventHandler\Message\Entities\Mention;
use danog\MadelineProto\EventHandler\Message\Entities\MessageEntity;
use danog\MadelineProto\EventHandler\Message\Entities\Spoiler;
use danog\MadelineProto\EventHandler\Message\Entities\Url;
use danog\MadelineProto\TL\Conversion\Extension;
use danog\TelegramEntities\Entities;
use danog\TelegramEntities\EntityTools;
use Throwable;

/**
 * Some tools.
 */
abstract class StrTools extends Extension
{
    /**
     * Get Telegram UTF-8 length of string.
     *
     * @param string $text Text
     *
     * @psalm-pure
     */
    public static function mbStrlen(string $text): int
    {
        return EntityTools::mbStrlen($text);
    }
    /**
     * Telegram UTF-8 multibyte substring.
     *
     * @param string   $text   Text to substring
     * @param integer  $offset Offset
     * @param null|int $length Length
     *
     * @psalm-pure
     */
    public static function mbSubstr(string $text, int $offset, ?int $length = null): string
    {
        return EntityTools::mbSubstr($text, $offset, $length);
    }
    /**
     * Telegram UTF-8 multibyte split.
     *
     * @param string        $text   Text
     * @param integer       $length Length
     *
     * @return array<string>
     *
     * @psalm-pure
     */
    public static function mbStrSplit(string $text, int $length): array
    {
        return EntityTools::mbStrSplit($text, $length);
    }
    /**
     * Manually convert HTML to a message and a set of entities.
     *
     * NOTE: You don't have to use this method to send HTML messages.
     *
     * This method is already called automatically by using parse_mode: "HTML" in messages.sendMessage, messages.sendMedia, et cetera...
     *
     * @see https://docs.madelineproto.xyz/API_docs/methods/messages.sendMessage.html#usage-of-parse_mode
     *
     * @return TextEntities Object containing message and entities
     */
    public static function htmlToMessageEntities(string $html): TextEntities
    {
        return TextEntities::fromHtml($html);
    }
    /**
     * Manually convert markdown to a message and a set of entities.
     *
     * NOTE: You don't have to use this method to send Markdown messages.
     *
     * This method is already called automatically by using parse_mode: "Markdown" in messages.sendMessage, messages.sendMedia, et cetera...
     *
     * @see https://docs.madelineproto.xyz/API_docs/methods/messages.sendMessage.html#usage-of-parse_mode
     *
     * @return TextEntities Object containing message and entities
     *
     * @psalm-mutation-free
     */
    public static function markdownToMessageEntities(string $markdown): TextEntities
    {
        return TextEntities::fromMarkdown($markdown);
    }
    /**
     * Convert a message and a set of entities to HTML.
     *
     * @param list<MessageEntity|array{_: string, offset: int, length: int}> $entities
     * @param bool                                                           $allowTelegramTags Whether to allow telegram-specific tags like tg-spoiler, tg-emoji, mention links and so on...
     */
    public static function entitiesToHtml(string $message, array $entities, bool $allowTelegramTags = false): string
    {
        if (isset($entities[0]) && \is_array($entities[0])) {
            $entities = MessageEntity::fromRawEntities($entities);
        }
        foreach ($entities as &$e) {
            $e = $e->toBotAPI();
        }
        return (new Entities($message, $entities))->toHTML($allowTelegramTags);
    }
    /**
     * Convert to camelCase.
     *
     * @param string $input String
     *
     * @psalm-pure
     */
    public static function toCamelCase(string $input): string
    {
        return lcfirst(str_replace('_', '', ucwords($input, '_')));
    }
    /**
     * Convert to snake_case.
     *
     * @param string $input String
     */
    public static function toSnakeCase(string $input): string
    {
        preg_match_all('!([A-Z][A-Z0-9]*(?=$|[A-Z][a-z0-9])|[A-Za-z][a-z0-9]+)!', $input, $matches);
        $ret = $matches[0];
        foreach ($ret as &$match) {
            $match = $match == strtoupper($match) ? strtolower($match) : lcfirst($match);
        }
        return implode('_', $ret);
    }
    /**
     * Escape string for MadelineProto's HTML entity converter.
     *
     * @param string $what String to escape
     *
     * @psalm-pure
     */
    public static function htmlEscape(string $what): string
    {
        return EntityTools::htmlEscape($what);
    }
    /**
     * Escape string for markdown.
     *
     * @param string $what String to escape
     *
     * @psalm-pure
     */
    public static function markdownEscape(string $what): string
    {
        return EntityTools::markdownEscape($what);
    }
    /**
     * Escape string for markdown codeblock.
     *
     * @param string $what String to escape
     *
     * @psalm-pure
     */
    public static function markdownCodeblockEscape(string $what): string
    {
        return EntityTools::markdownCodeblockEscape($what);
    }
    /**
     * Escape string for markdown code section.
     *
     * @param string $what String to escape
     *
     * @psalm-pure
     */
    public static function markdownCodeEscape(string $what): string
    {
        return EntityTools::markdownCodeEscape($what);
    }
    /**
     * Escape string for URL.
     *
     * @param string $what String to escape
     *
     * @psalm-pure
     */
    public static function markdownUrlEscape(string $what): string
    {
        return EntityTools::markdownUrlEscape($what);
    }
    /**
     * Escape type name.
     *
     * @internal
     *
     * @param string $type String to escape
     *
     * @psalm-pure
     */
    public static function typeEscape(string $type): string
    {
        $type = str_replace(['<', '>'], ['_of_', ''], $type);
        return preg_replace('/.*_of_/', '', $type);
    }
    /**
     * Escape method name.
     *
     * @internal
     *
     * @param string $method Method name
     *
     * @psalm-pure
     */
    public static function methodEscape(string $method): string
    {
        return str_replace('.', '->', $method);
    }
    /**
     * Strip markdown tags.
     *
     * @internal
     *
     * @psalm-mutation-free
     */
    public static function toString(string $markdown): string
    {
        if ($markdown === '') {
            return $markdown;
        }
        try {
            return Entities::fromMarkdown($markdown)->message;
        } catch (Throwable) {
            return Entities::fromMarkdown(str_replace('_', '\\_', $markdown))->message;
        }
    }
}
<?php

declare(strict_types=1);

/**
 * Proxying AMPHP connector.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use Amp\Cancellation;
use Amp\Socket\ConnectContext;
use Amp\Socket\Socket;
use Amp\Socket\SocketAddress;
use Amp\Socket\SocketConnector;
use Throwable;

/** @internal */
final class ContextConnector implements SocketConnector
{
    /**
     * @psalm-mutation-free
     */
    public function __construct(private DoHWrapper $doHWrapper, private LoggerGetter $loggerGetter, private bool $fromDns = false)
    {
    }
    #[\Override]
    public function connect(SocketAddress|string $uri, ?ConnectContext $context = null, ?Cancellation $cancellation = null): Socket
    {
        $ctx = $context ?? new ConnectContext();
        $ctxs = $this->doHWrapper->generateContexts($uri, $ctx);
        if (empty($ctxs)) {
            throw new Exception("No contexts for raw connection to URI {$uri}");
        }
        $logger = $this->loggerGetter->getLogger();
        foreach ($ctxs as $ctx) {
            try {
                $ctx->setIsDns($this->fromDns);
                if ($cancellation) {
                    $ctx->setCancellation($cancellation);
                }
                $result = ($ctx->getStream());
                $logger->logger('OK!', Logger::WARNING);
                return $result->getSocket();
            } catch (Throwable $e) {
                if (\defined('MADELINEPROTO_TEST') && \constant('MADELINEPROTO_TEST') === 'pony') {
                    throw $e;
                }
                $logger->logger('Connection failed: '.$e, Logger::ERROR);
            }
        }
        throw new Exception("Could not connect to URI {$uri}");
    }
}
<?php

declare(strict_types=1);

/**
 * CallHandler module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\MTProtoSession;

use Amp\CompositeCancellation;
use Amp\DeferredFuture;
use Amp\Future;
use Amp\Sync\LocalKeyedMutex;
use Amp\TimeoutCancellation;
use Closure;
use danog\MadelineProto\DataCenterConnection;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\MTProto\Container;
use danog\MadelineProto\MTProto\LinkedList;
use danog\MadelineProto\MTProto\MTProtoOutgoingMessage;
use danog\MadelineProto\MTProto\SpecialMethodType;
use danog\MadelineProto\TL\Exception;
use danog\MadelineProto\Tools;
use danog\MadelineProto\WrappedFuture;
use Revolt\EventLoop;
use Throwable;

use function Amp\async;
use function Amp\Future\await;

/**
 * Manages method and object calls.
 *
 *
 * @property LinkedList $mainPendingOutgoing
 * @property LinkedList $uninitedPendingOutgoing
 * @property DataCenterConnection $shared
 * @property MTProto $API
 * @internal
 */
trait CallHandler
{
    /**
     * Recall method.
     */
    public function methodRecall(MTProtoOutgoingMessage $request, ?int $forceDatacenter = null, float|Future|null $defer = null): void
    {
        $id = $request->getMsgId();
        if ($request->unencrypted) {
            unset($this->unencrypted_new_outgoing[$id]);
        } else {
            unset($this->new_outgoing[$id]);
        }
        if ($request instanceof Container) {
            $this->ack_queue = array_merge($request->acks, $this->ack_queue);
            foreach ($request->msgs as $msg) {
                $this->methodRecall($msg, $forceDatacenter, $defer);
            }
            return;
        }
        if ($request->cancellation?->isRequested()) {
            return;
        }
        if (\is_float($defer)) {
            $d = new DeferredFuture;
            $id = EventLoop::delay($defer, $d->complete(...));
            $request->cancellation?->subscribe(static fn () => EventLoop::cancel($id));
            $defer = $d->getFuture();
        }
        $request->unlink();
        if ($defer) {
            $defer->catch(static fn (Throwable $e) => $request->reply(static fn (): Throwable => $e));
            $defer->map(function ($result) use ($request, $forceDatacenter): void {
                if ($result instanceof Closure) {
                    $request->reply($result);
                } else {
                    $this->methodRecall($request, $forceDatacenter);
                }
            });
            return;
        }
        $datacenter = $forceDatacenter ?? $this->datacenter;
        if ($forceDatacenter !== null && !$request->resetForResend()) {
            return;
        }
        if ($datacenter === $this->datacenter) {
            EventLoop::queue($this->sendMessage(...), $request);
        } else {
            EventLoop::queue(function () use ($datacenter, $request): void {
                $this->API->datacenter->waitGetConnection($datacenter)
                    ->sendMessage($request);
            });
        }
    }
    /**
     * Call method and wait asynchronously for response.
     *
     * @param string $method Method name
     * @param array  $args   Arguments
     */
    public function methodCallAsyncRead(string $method, array $args)
    {
        if (isset($args['message']) && \is_string($args['message']) && mb_strlen($args['message'], 'UTF-8') > ($this->API->getConfig())['message_length_max'] && mb_strlen($this->API->parseMode($args)['message'], 'UTF-8') > ($this->API->getConfig())['message_length_max']) {
            $peer = $args['peer'];
            $args = $this->API->splitToChunks($args);
            $promises = [];
            $queueId = $method.' '.$this->API->getId($peer);

            $promises = [];
            foreach ($args as $k => $sub) {
                if ($k > 0 && isset($sub['random_id']) && \is_int($sub['random_id'])) {
                    // Each message needs its own random ID, derived from the one of the call so that it's the same if the call is made again.
                    $sub['random_id'] = Tools::unpackSignedLong(substr(hash('sha256', pack('q', $sub['random_id']).pack('V', $k), true), 0, 8));
                }
                $sub['queueId'] = $queueId;
                $sub = $this->API->botAPIToMTProto($sub);
                $this->methodAbstractions($method, $sub);
                $promises[] = async($this->methodCallAsyncRead(...), $method, $sub);
            }

            return await($promises);
        }

        $queueId = $args['queueId'] ?? null;
        if ($queueId !== null) {
            $_ = $this->abstractionQueueMutex->acquire($queueId);
        }

        $readFuture = $this->methodCallAsyncWrite($method, $args);
        return $readFuture->await();
    }

    private LocalKeyedMutex $abstractionQueueMutex;
    private ?float $drop = null;
    /**
     * Call method and make sure it is asynchronously sent (generator).
     *
     * @param string $method Method name
     * @param array  $args   Arguments
     */
    public function methodCallAsyncWrite(string $method, array $args): WrappedFuture
    {
        $cancellation = $args['cancellation'] ?? null;
        $cancellation?->throwIfRequested();
        if (isset($args['id']) && \is_array($args['id']) && isset($args['id']['_']) && isset($args['id']['dc_id']) && ($args['id']['_'] === 'inputBotInlineMessageID' || $args['id']['_'] === 'inputBotInlineMessageID64') && $this->datacenter != $args['id']['dc_id']) {
            return $this->API->methodCallAsyncWrite($method, $args, $args['id']['dc_id']);
        }
        $special = $args['specialMethodType'] ?? null;
        if ($special === SpecialMethodType::FILE_RELATED
            && !$this->shared->auth->isMedia
            && !$this->shared->auth->isCdn
            && $this->API->datacenter->has(-$this->datacenter)
        ) {
            $this->API->logger('Using media DC');
            return $this->API->methodCallAsyncWrite($method, $args, -$this->datacenter);
        }

        $args = $this->API->botAPIToMTProto($args);

        $response = new DeferredFuture;
        $this->methodAbstractions($method, $args);
        if (\in_array($method, ['messages.sendEncrypted', 'messages.sendEncryptedFile', 'messages.sendEncryptedService'], true)) {
            $args['method'] = $method;
            if (isset($args['peer'])) {
                $args = $this->API->getSecretChatController($args['peer'])->encryptSecretMessage($args, $response->getFuture());
            } else {
                if (!$this->API->getSettings()->getSchema()->getFuzzMode()) {
                    throw new Exception('No peer specified for encrypted message!');
                }
            }
        }

        $methodInfo = $this->API->getTL()->getMethods()->findByMethod($method);
        if (!$methodInfo) {
            throw new Exception("Could not find method $method!");
        }
        $encrypted = $methodInfo['encrypted'];
        $timeout = new TimeoutCancellation(
            $args['timeout'] ?? ($this->drop ??= (float) $this->API->getSettings()->getRpc()->getRpcDropTimeout()),
            "Timeout while waiting for $method"
        );
        $cancellation = $cancellation !== null
            ? new CompositeCancellation($cancellation, $timeout)
            : $timeout;
        $message = new MTProtoOutgoingMessage(
            connection: $this,
            body: $args,
            constructor: $method,
            type: $methodInfo['type'],
            subtype: $methodInfo['subtype'] ?? null,
            specialMethodType: $special,
            isMethod: true,
            unencrypted: !$encrypted,
            floodWaitLimit: $args['floodWaitLimit'] ?? null,
            resultDeferred: $response,
            cancellation: $cancellation,
            takeoutId: $args['takeoutId'] ?? null,
            businessConnectionId: $args['businessConnectionId'] ?? null,
            sessionBound: isset($args['madelineMsgId']),
        );
        if (isset($args['madelineMsgId'])) {
            $message->setMsgId($args['madelineMsgId']);
        }
        $this->sendMessage($message);
        $message->getSendPromise()->await($cancellation);
        return new WrappedFuture($response->getFuture());
    }
    /**
     * Send object.
     *
     * @param string $object Object name
     * @param array  $args   Arguments
     */
    public function objectCallAsync(string $object, array $args, ?DeferredFuture $promise = null): void
    {
        $cancellation = $args['cancellation'] ?? null;
        $cancellation?->throwIfRequested();
        $timeout = new TimeoutCancellation($this->drop ??= (float) $this->API->getSettings()->getRpc()->getRpcDropTimeout());
        $cancellation = $cancellation !== null
            ? new CompositeCancellation($cancellation, $timeout)
            : $timeout;
        $this->sendMessage(
            new MTProtoOutgoingMessage(
                connection: $this,
                body: $args,
                constructor: $object,
                type: '',
                isMethod: false,
                unencrypted: false,
                specialMethodType: null,
                resultDeferred: $promise,
                cancellation: $cancellation,
            ),
        );
    }
}
<?php

declare(strict_types=1);

/**
 * MsgIdHandler module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\MTProtoSession;

use danog\MadelineProto\Connection;
use danog\MadelineProto\Exception;
use danog\MadelineProto\Logger;
use danog\MadelineProto\Magic;

/**
 * Manages message ids.
 *
 * @internal
 */
final class MsgIdHandler
{
    /**
     * Session instance.
     */
    private Connection $session;

    /**
     * Constructor.
     *
     * @param Connection $session Session
     *
     * @psalm-mutation-free
     */
    public function __construct(Connection $session)
    {
        $this->session = $session;
    }

    /**
     * Maximum incoming ID.
     *
     */
    private int $maxIncomingId = 0;
    /**
     * Maximum outgoing ID.
     *
     */
    private int $maxOutgoingId = 0;

    /**
     * Check validity of given message ID.
     */
    public function checkOutgoingMessageId(int $newMessageId): void
    {
        $minMessageId = (time() + $this->session->time_delta - 300) << 32;
        if ($newMessageId < $minMessageId) {
            $this->session->API->logger->logger('Given message id ('.$newMessageId.') is too old compared to the min value ('.$minMessageId.').', Logger::WARNING);
        }
        $maxMessageId = (time() + $this->session->time_delta + 30) << 32;
        if ($newMessageId > $maxMessageId) {
            throw new Exception('Given message id ('.$newMessageId.') is too new compared to the max value ('.$maxMessageId.'). Please sync your date using NTP.');
        }
        if ($newMessageId % 4) {
            throw new Exception('Given message id ('.$newMessageId.') is not divisible by 4. Please sync your date using NTP.');
        }
        if ($newMessageId <= $this->maxOutgoingId) {
            throw new Exception('Given message id ('.$newMessageId.') is lower than or equal to the current limit ('.$this->maxOutgoingId.'). Please sync your date using NTP.');
        }
        $this->maxOutgoingId = $newMessageId;
    }
    /**
     * Check validity of given message ID.
     */
    public function checkIncomingMessageId(int $newMessageId, bool $container): void
    {
        $minMessageId = (time() + $this->session->time_delta - 300) << 32;
        if ($newMessageId < $minMessageId) {
            $this->session->API->logger->logger('Given message id ('.$newMessageId.') is too old compared to the min value ('.$minMessageId.').', Logger::WARNING);
        }
        $maxMessageId = (time() + $this->session->time_delta + 30) << 32;
        if ($newMessageId > $maxMessageId) {
            throw new Exception('Given message id ('.$newMessageId.') is too new compared to the max value ('.$maxMessageId.'). Please sync your date using NTP.');
        }
        if (!($newMessageId % 2)) {
            throw new Exception('message id mod 4 != 1 or 3');
        }
        $key = $this->maxIncomingId;
        if ($container) {
            if ($newMessageId >= $key) {
                $this->session->API->logger->logger('Given message id ('.$newMessageId.') is bigger than or equal to the current limit ('.$key.'). Please sync your date using NTP.', Logger::ULTRA_VERBOSE);
            }
        } else {
            if ($newMessageId <= $key) {
                $this->session->API->logger->logger('Given message id ('.$newMessageId.') is lower than or equal to the current limit ('.$key.'). Please sync your date using NTP.', Logger::ULTRA_VERBOSE);
            }
        }
        $this->maxIncomingId = $newMessageId;
    }
    /**
     * Generate message ID.
     */
    public function generateMessageId(): int
    {
        $messageId = (time() + $this->session->time_delta) << 32;
        $messageId += hrtime(true) % 1000_000;
        $messageId += 4 - ($messageId % 4);
        if ($messageId <= $this->maxOutgoingId) {
            $messageId = $this->maxOutgoingId + 4;
        }
        $this->checkOutgoingMessageId($messageId);
        return $messageId;
    }
    /**
     * Get maximum message ID.
     *
     * @param boolean $incoming Incoming or outgoing message ID
     *
     * @psalm-mutation-free
     */
    public function getMaxId(bool $incoming)
    {
        return $this->{$incoming ? 'maxIncomingId' : 'maxOutgoingId'};
    }

    /**
     * Cleanup incoming and outgoing messages.
     */
    public function cleanup(): void
    {
        $usage = memory_get_usage();
        $this->session->cleanupSession();
        $cleaned = round(($usage - memory_get_usage())/1024/1024, 1);
        if ($cleaned && !Magic::$suspendPeriodicLogging) {
            $this->session->API->logger->logger("Zend hashmap reallocation done. Cleaned memory: $cleaned Mb", Logger::VERBOSE);
        }
    }
}
<?php

declare(strict_types=1);

/**
 * Session module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\MTProtoSession;

use Amp\Sync\LocalKeyedMutex;
use danog\BetterPrometheus\BetterCounter;
use danog\BetterPrometheus\BetterGauge;
use danog\BetterPrometheus\BetterHistogram;
use danog\MadelineProto\Logger;
use danog\MadelineProto\MTProto\Container;
use danog\MadelineProto\MTProto\LinkedList;
use danog\MadelineProto\MTProto\MTProtoIncomingMessage;
use danog\MadelineProto\MTProto\MTProtoOutgoingMessage;
use danog\MadelineProto\Tools;
use SplQueue;

/**
 * Manages MTProto session-specific data.
 *
 * @internal
 */
trait Session
{
    use AuthKeyHandler;
    use ResponseHandler;
    use SeqNoHandler;
    use CallHandler;
    use Reliable;
    public ?BetterGauge $pendingOutgoingGauge = null;
    public ?BetterGauge $inFlightGauge = null;
    public ?BetterCounter $incomingCtr = null;
    public ?BetterCounter $outgoingCtr = null;
    public ?BetterCounter $incomingBytesCtr = null;
    public ?BetterCounter $outgoingBytesCtr = null;

    public ?BetterHistogram $requestLatencies = null;

    public ?BetterCounter $requestResponse = null;
    /**
     * Incoming message array.
     *
     * @var array<MTProtoIncomingMessage>
     */
    //public array $incoming_messages = [];
    /**
     * Outgoing message array.
     *
     * @var array<MTProtoOutgoingMessage>
     */
    //public array $outgoing_messages = [];
    /**
     * New incoming message ID array.
     *
     * @var SplQueue<MTProtoIncomingMessage>
     */
    public SplQueue $new_incoming;
    /**
     * New outgoing message array.
     *
     * @var array<MTProtoOutgoingMessage>
     */
    public array $new_outgoing = [];
    /**
     * New unencrypted outgoing message array.
     *
     * @var array<MTProtoOutgoingMessage>
     */
    public array $unencrypted_new_outgoing = [];

    /**
     * Pending outgoing messages.
     */
    public LinkedList $unencryptedPendingOutgoing;
    /**
     * Pending outgoing messages.
     */
    public LinkedList $uninitedPendingOutgoing;
    /**
     * Pending outgoing messages.
     */
    public LinkedList $mainPendingOutgoing;

    /**
     * Time delta with server.
     *
     */
    public int $time_delta = 0;
    /**
     * Ack queue.
     *
     * @var list<int>
     */
    public array $ack_queue = [];
    /**
     * Message ID handler.
     *
     */
    public MsgIdHandler $msgIdHandler;
    /**
     * Reset MTProto session.
     */
    public function resetSession(string $why): void
    {
        $this->API->logger("Resetting session in DC {$this->datacenterId} due to $why...", Logger::WARNING);
        $this->session_id = Tools::random(8);
        $this->session_in_seq_no = 0;
        $this->session_out_seq_no = 0;
        $this->msgIdHandler ??= new MsgIdHandler($this);
        if (!isset($this->new_incoming)) {
            $q = new SplQueue;
            $q->setIteratorMode(SplQueue::IT_MODE_DELETE);
            $this->new_incoming = $q;
        }
        // The server only answers the requests sent in the current session: send those still waiting
        // for an answer again in the new one (with new message IDs), or they are never answered.
        // A container and the messages it carries are all listed: recall the containers first (which
        // recalls their messages too), then the messages still listed, so that each is sent once.
        foreach ([true, false] as $containers) {
            foreach ($this->new_outgoing as $id => $msg) {
                if ($msg instanceof Container === $containers && isset($this->new_outgoing[$id]) && !$msg->hasReply()) {
                    $this->methodRecall($msg, forceDatacenter: $this->datacenter);
                }
            }
        }
    }
    /**
     * Cleanup incoming and outgoing messages.
     *
     * @psalm-external-mutation-free
     */
    public function cleanupSession(): void
    {
        $new_outgoing = [];
        foreach ($this->new_outgoing as $key => $message) {
            $new_outgoing[$key] = $message;
        }
        $this->new_outgoing = $new_outgoing;

        $unencrypted_new_outgoing = [];
        foreach ($this->unencrypted_new_outgoing as $key => $message) {
            $unencrypted_new_outgoing[$key] = $message;
        }
        $this->unencrypted_new_outgoing = $unencrypted_new_outgoing;
    }
    /**
     * Create MTProto session if needed.
     */
    public function createSession(): void
    {
        $labels = ['datacenter' => (string) $this->datacenter, 'connection' => (string) $this->id];
        $this->pendingOutgoingGauge = $this->API->getPromGauge("MadelineProto", "pending_outgoing_mtproto_messages_count", "Number of not-yet sent outgoing MTProto messages", $labels);
        $this->inFlightGauge = $this->API->getPromGauge("MadelineProto", "inflight_requests_count", "Number of in-flight requests", $labels);
        $this->incomingCtr = $this->API->getPromCounter("MadelineProto", "incoming_mtproto_messages_count", "Number of received MTProto messages", $labels);
        $this->outgoingCtr = $this->API->getPromCounter("MadelineProto", "outgoing_mtproto_messages_count", "Number of sent MTProto messages", $labels);
        $this->incomingBytesCtr = $this->API->getPromCounter("MadelineProto", "incoming_bytes_count", "Number of received bytes", $labels);
        $this->outgoingBytesCtr = $this->API->getPromCounter("MadelineProto", "outgoing_bytes_count", "Number of sent bytes", $labels);
        $this->requestResponse = $this->API->getPromCounter("MadelineProto", "request_responses_count", "Received RPC error or success status of requests by method.", $labels);
        $this->requestLatencies = $this->API->getPromHistogram(
            "MadelineProto",
            "request_latencies",
            "Successful request latency in nanoseconds by method",
            $labels,
            [
                5_000_000,
                10_000_000,
                25_000_000,
                50_000_000,
                75_000_000,
                100_000_000,
                250_000_000,
                500_000_000,
                750_000_000,
                1000_000_000,
                2500_000_000,
                5000_000_000,
                7500_000_000,
                10000_000_000,
            ]
        );
        $this->mainPendingOutgoing ??= new LinkedList;
        $this->unencryptedPendingOutgoing ??= new LinkedList;
        $this->uninitedPendingOutgoing ??= new LinkedList;
        if ($this->session_id === null) {
            $this->resetSession("creating initial session");
        }
        $this->abstractionQueueMutex ??= new LocalKeyedMutex;
    }
    /**
     * Backup eventual unsent messages before session deletion.
     *
     * @return array<MTProtoOutgoingMessage>
     *
     * @psalm-mutation-free
     */
    public function backupSession(): array
    {
        $pending = array_merge($this->new_outgoing, $this->unencrypted_new_outgoing);
        foreach ([$this->mainPendingOutgoing ?? null, $this->unencryptedPendingOutgoing ?? null, $this->uninitedPendingOutgoing ?? null] as $k => $list) {
            $message = $list;
            while ($message !== null) {
                $message = $message->prev;
                if (!$message instanceof MTProtoOutgoingMessage) {
                    break;
                }
                $pending []= $message;
            }
        }
        return $pending;
    }
}
<?php

declare(strict_types=1);

/**
 * Reliable module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\MTProtoSession;

use danog\MadelineProto\Logger;
use danog\MadelineProto\MTProto;
use Revolt\EventLoop;

/**
 * Manages responses.
 *
 * @internal
 */
trait Reliable
{
    /**
     * Called when receiving a new_msg_detailed_info.
     *
     * @psalm-mutation-free
     */
    public function onNewMsgDetailedInfo(array $content): void
    {
        /*if (isset($this->incoming_messages[$content['answer_msg_id']])) {
            $this->incoming_messages[$content['answer_msg_id']]->ack();
        } else {
            EventLoop::queue($this->objectCall(...), 'msg_resend_req', ['msg_ids' => [$content['answer_msg_id']]]);
        }*/
    }
    /**
     * Called when receiving a msg_detailed_info.
     *
     * @psalm-mutation-free
     */
    public function onMsgDetailedInfo(array $content): void
    {
        if (isset($this->new_outgoing[$content['msg_id']])) {
            $this->onNewMsgDetailedInfo($content);
        }
    }
    /**
     * Called when receiving a msg_resend_req.
     */
    public function onMsgResendReq(array $content, int $current_msg_id): void
    {
        $ok = true;
        foreach ($content['msg_ids'] as $msg_id) {
            if (!isset($this->new_outgoing[$msg_id])) {
                $ok = false;
            }
        }
        if ($ok) {
            foreach ($content['msg_ids'] as $msg_id) {
                if (isset($this->new_outgoing[$msg_id])) {
                    $this->methodRecall($this->new_outgoing[$msg_id]);
                }
            }
        } else {
            $this->sendMsgsStateInfo($content['msg_ids'], $current_msg_id);
        }
    }
    /**
     * Called when receiving a msg_resend_ans_req.
     *
     * @psalm-mutation-free
     */
    public function onMsgResendAnsReq(array $content, int $current_msg_id): void
    {
        $this->sendMsgsStateInfo($content['msg_ids'], $current_msg_id);
    }

    /**
     * Called when receiving a msgs_all_info.
     */
    public function onMsgsAllInfo(array $content): void
    {
        foreach ($content['msg_ids'] as $key => $msg_id) {
            $info = \ord($content['info'][$key]);
            $status = 'Status for message id '.$msg_id.': ';
            foreach (MTProto::MSGS_INFO_FLAGS as $flag => $description) {
                if (($info & $flag) !== 0) {
                    $status .= $description;
                }
            }
            $this->API->logger($status, Logger::NOTICE);
        }
    }
    /**
     * Send state info for message IDs.
     *
     * @param array $msg_ids    Message IDs to send info about
     * @param int   $req_msg_id Message ID of msgs_state_req that initiated this
     *
     * @psalm-mutation-free
     */
    public function sendMsgsStateInfo(array $msg_ids, int $req_msg_id): void
    {
        /*$this->API->logger('Sending state info for '.\count($msg_ids).' message IDs');
        $info = '';
        foreach ($msg_ids as $msg_id) {
            $cur_info = 0;
            if (!isset($this->incoming_messages[$msg_id])) {
                $shifted = $msg_id >> 32;
                if ($shifted > (time() + $this->time_delta + 30)) {
                    $this->API->logger("Do not know anything about {$msg_id} and it is too big");
                    $cur_info |= 3;
                } elseif ($shifted < (time() + $this->time_delta - 300)) {
                    $this->API->logger("Do not know anything about {$msg_id} and it is too small");
                    $cur_info |= 1;
                } else {
                    $this->API->logger("Do not know anything about {$msg_id}");
                    $cur_info |= 2;
                }
            } else {
                $this->API->logger("Know about {$msg_id}");
                $cur_info = $this->incoming_messages[$msg_id]->getState();
            }
            $info .= \chr($cur_info);
        }
        EventLoop::queue($this->objectCall(...), 'msgs_state_info', ['req_msg_id' => $req_msg_id, 'info' => $info]);*/
    }
}
<?php

declare(strict_types=1);

/**
 * SeqNoHandler module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\MTProtoSession;

use danog\MadelineProto\Logger;
use danog\MadelineProto\MTProto\MTProtoIncomingMessage;

/**
 * Manages sequence number.
 *
 * @internal
 */
trait SeqNoHandler
{
    public int $session_out_seq_no = 0;
    public int $session_in_seq_no = 0;
    public ?string $session_id = null;
    /**
     * @psalm-external-mutation-free
     */
    public function generateOutSeqNo(bool $contentRelated): int
    {
        $in = $contentRelated ? 1 : 0;
        $value = $this->session_out_seq_no;
        $this->session_out_seq_no += $in;
        //$this->API->logger("OUT: $value + $in = ".$this->session_out_seq_no);
        return $value * 2 + $in;
    }
    /**
     * @psalm-external-mutation-free
     */
    public function checkInSeqNo(MTProtoIncomingMessage $message): void
    {
        if ($message->hasSeqNo()) {
            $in = $message->contentRelated ? 1 : 0;
            $value = $this->session_in_seq_no;
            $this->session_in_seq_no += $in;
            $value *= 2;
            //$this->API->logger("IN $message ({$message->getMsgId()}): $value + $in = {$message->getSeqNo()}");
            $seq_no = $value + $in;
            if ($seq_no !== $message->getSeqNo()) {
                $this->API->logger("SECURITY WARNING: Seqno mismatch (should be $seq_no, is {$message->getSeqNo()}, $message)", Logger::ULTRA_VERBOSE);
            }
        }
    }
}
<?php

declare(strict_types=1);

/**
 * AuthKeyHandler module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\MTProtoSession;

use danog\MadelineProto\DataCenterConnection;
use danog\MadelineProto\Exception;
use danog\MadelineProto\Logger;
use danog\MadelineProto\Magic;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\MTProtoTools\Crypt;
use danog\MadelineProto\RPCErrorException;
use danog\MadelineProto\SecurityException;
use danog\MadelineProto\Tools;
use danog\PrimeModule;
use phpseclib4\Math\BigInteger;
use Throwable;

use const PHP_EOL;
use function hash;

use function time;

/**
 * Manages the creation of the authorization key.
 *
 * https://core.telegram.org/mtproto/auth_key
 * https://core.telegram.org/mtproto/samples-auth_key
 *
 * @property DataCenterConnection $shared
 * @property int                  $datacenter
 * @property MTProto              $API
 * @property Logger               $logger
 *
 * @internal
 */
trait AuthKeyHandler
{
    /**
     * Create authorization key.
     */
    public function createAuthKey(bool $temp): void
    {
        $expires_in = $temp ? MTProto::PFS_DURATION : -1;
        $cdn = $this->shared->auth->isCdn;
        $test = $this->API->settings->getConnection()->getTestMode();

        for ($retry_id_total = 1; $retry_id_total <= $this->API->settings->getAuth()->getMaxAuthTries(); $retry_id_total++) {
            try {
                $this->API->logger('Requesting pq...', Logger::VERBOSE);
                /**
                 * ***********************************************************************
                 * Make pq request, DH exchange initiation.
                 *
                 * @method req_pq_multi
                 * @param [
                 *         int128         $nonce                             : The value of nonce is selected randomly by the client (random number) and identifies the client within this communication
                 * ]
                 * @return ResPQ [
                 *               int128         $nonce                             : The value of nonce is selected randomly by the server
                 *               int128         $server_nonce                         : The value of server_nonce is selected randomly by the server
                 *               string         $pq                             : This is a representation of a natural number (in binary big endian format). This number is the product of two different odd prime numbers
                 *               Vector long    $server_public_key_fingerprints                : This is a list of public RSA key fingerprints
                 *               ]
                 */
                $nonce = Tools::random(16);
                $ResPQ = $this->methodCallAsyncRead('req_pq_multi', ['nonce' => $nonce]);
                /*
                 * ***********************************************************************
                 * Check if the client's nonce and the server's nonce are the same
                 */
                if ($ResPQ['nonce'] !== $nonce) {
                    throw new SecurityException('wrong nonce');
                }
                /*
                 * ***********************************************************************
                 * Find our key in the server_public_key_fingerprints vector
                 */
                $fps = $ResPQ['server_public_key_fingerprints'];

                $key = $this->API->findRsaKey($fps, $test, $cdn);
                if (!$key) {
                    if ($cdn) {
                        $this->API->getCdnConfig();
                        $key = $this->API->findRsaKey($fps, $test, $cdn);
                    }
                    if (!$key) {
                        throw new SecurityException("Couldn't find any of our keys in the server_public_key_fingerprints vector.");
                    }
                }

                $pq_bytes = $ResPQ['pq'];
                $server_nonce = $ResPQ['server_nonce'];
                /*
                 * ***********************************************************************
                 * Compute p and q
                 */
                $ok = false;
                $pq = Tools::unpackSignedLong(strrev($pq_bytes));
                foreach ([
                    'native_single_cpp',
                    'python_single_alt',
                    'python_single',
                    'native_single',
                ] as $method) {
                    $this->API->logger("Factorizing with $method (please wait, might take a while)");
                    if ($method !== 'native_single_cpp') {
                        $this->API->logger('Install https://prime.madelineproto.xyz and the FFI extension to speed this up!');
                    }

                    $p = 0;
                    $q = 0;
                    try {
                        $p = PrimeModule::$method($pq);
                    } catch (Throwable $e) {
                        $this->API->logger("While factorizing with $method: $e");
                    }

                    if ($p) {
                        $q = $pq / $p;
                        if ($p > $q) {
                            [$p, $q] = [$q, $p];
                        }
                        if ($pq === $p*$q) {
                            $ok = true;
                            break;
                        }
                    }
                }
                if (!$ok) {
                    throw new SecurityException("Couldn't compute p and q, install prime.madelineproto.xyz to fix. Original pq: {$pq}, computed p: {$p}, computed q: {$q}, computed pq: ".$p*$q);
                }
                $this->API->logger('Factorization '.$pq.' = '.$p.' * '.$q, Logger::VERBOSE);
                /*
                 * ***********************************************************************
                 * Serialize object for req_DH_params
                 */
                $p_bytes = strrev(Tools::packUnsignedInt($p));
                $q_bytes = strrev(Tools::packUnsignedInt($q));
                $new_nonce = Tools::random(32);
                $data_unserialized = ['_' => 'p_q_inner_data'.($expires_in < 0 ? '' : '_temp').'_dc', 'pq' => $pq_bytes, 'p' => $p_bytes, 'q' => $q_bytes, 'nonce' => $nonce, 'server_nonce' => $server_nonce, 'new_nonce' => $new_nonce, 'expires_in' => $expires_in, 'dc' => $this->datacenter];
                $p_q_inner_data = ($this->API->getTL()->serializeObject(['type' => ''], $data_unserialized, 'p_q_inner_data'));
                /*
                 * ***********************************************************************
                 * Encrypt serialized object
                 */
                if (\strlen($p_q_inner_data) > 144) {
                    throw new SecurityException('p_q_inner_data is too long!');
                }
                $data_with_padding = $p_q_inner_data.Tools::random(192 - \strlen($p_q_inner_data));
                $data_pad_reversed = strrev($data_with_padding);

                for ($tryInner = 0; $tryInner < 10; $tryInner++) {
                    $temp_key = Tools::random(32);
                    $data_with_hash = $data_pad_reversed.hash('sha256', $temp_key.$data_with_padding, true);
                    $aes_encrypted = Crypt::igeEncrypt($data_with_hash, $temp_key, str_repeat("\0", 32));
                    $temp_key_xor = $temp_key ^ hash('sha256', $aes_encrypted, true);
                    $key_aes_encrypted_bigint = new BigInteger($temp_key_xor.$aes_encrypted, 256);

                    $ok = $key_aes_encrypted_bigint->compare($key->n) < 0;
                    if ($ok) {
                        break;
                    }
                }

                if (!$ok) {
                    throw new SecurityException('Failed to generate a valid payload within 10 attempts.');
                }

                $encrypted_data = $key->encrypt($key_aes_encrypted_bigint);
                $this->API->logger('Starting Diffie Hellman key exchange', Logger::VERBOSE);
                /*
                 * ***********************************************************************
                 * Starting Diffie Hellman key exchange, Server authentication
                 * @method req_DH_params
                 * @param [
                 *         int128         $nonce                             : The value of nonce is selected randomly by the client (random number) and identifies the client within this communication
                 *         int128        $server_nonce                    : The value of server_nonce is selected randomly by the server
                 *         string        $p                                : The value of BigInteger
                 *         string        $q                                : The value of BigInteger
                 *         long        $public_key_fingerprint            : This is our key in the server_public_key_fingerprints vector
                 *         string        $encrypted_data
                 * ]
                 * @return Server_DH_Params [
                 *         int128         $nonce                         : The value of nonce is selected randomly by the server
                 *         int128         $server_nonce                     : The value of server_nonce is selected randomly by the server
                 *         string         $new_nonce_hash                    : Return this value if server responds with server_DH_params_fail
                 *         string         $encrypted_answer                : Return this value if server responds with server_DH_params_ok
                 * ]
                 */
                $server_dh_params = $this->methodCallAsyncRead('req_DH_params', ['nonce' => $nonce, 'server_nonce' => $server_nonce, 'p' => $p_bytes, 'q' => $q_bytes, 'public_key_fingerprint' => $key->fp, 'encrypted_data' => $encrypted_data]);
                /*
                 * ***********************************************************************
                 * Check if the client's nonce and the server's nonce are the same
                 */
                if ($nonce != $server_dh_params['nonce']) {
                    throw new SecurityException('wrong nonce.');
                }
                /*
                 * ***********************************************************************
                 * Check if server_nonce and new server_nonce are the same
                 */
                if ($server_nonce != $server_dh_params['server_nonce']) {
                    throw new SecurityException('wrong server nonce.');
                }
                /*
                 * ***********************************************************************
                 * Check valid new nonce hash if return from server
                 * new nonce hash return in server_DH_params_fail
                 */
                if (isset($server_dh_params['new_nonce_hash']) && substr(sha1($new_nonce), -32) != $server_dh_params['new_nonce_hash']) {
                    throw new SecurityException('wrong new nonce hash.');
                }
                /*
                 * ***********************************************************************
                 * Get key, iv and decrypt answer
                 */
                $encrypted_answer = $server_dh_params['encrypted_answer'];
                $tmp_aes_key = sha1($new_nonce.$server_nonce, true).substr(sha1($server_nonce.$new_nonce, true), 0, 12);
                $tmp_aes_iv = substr(sha1($server_nonce.$new_nonce, true), 12, 8).sha1($new_nonce.$new_nonce, true).substr($new_nonce, 0, 4);
                $answer_with_hash = Crypt::igeDecrypt($encrypted_answer, $tmp_aes_key, $tmp_aes_iv);
                /*
                 * ***********************************************************************
                 * Separate answer and hash
                 */
                $answer_hash = substr($answer_with_hash, 0, 20);
                /** @var string */
                $answer = substr($answer_with_hash, 20);
                /*
                 * ***********************************************************************
                 * Deserialize answer
                 * @return Server_DH_inner_data [
                 *         int128         $nonce                             : The value of nonce is selected randomly by the client (random number) and identifies the client within this communication
                 *         int128        $server_nonce                    : The value of server_nonce is selected randomly by the server
                 *         int            $g
                 *         string        $dh_prime
                 *         string        $g_a
                 *         int            $server_time
                 * ]
                 */
                $server_DH_inner_data = $this->API->getTL()->deserialize($answer, ['type' => '', 'encrypted' => false, 'connection' => null]);
                /*
                 * ***********************************************************************
                 * Do some checks
                 */
                $server_DH_inner_data_length = $this->API->getTL()->getLength($answer);
                if (sha1(substr($answer, 0, $server_DH_inner_data_length), true) != $answer_hash) {
                    throw new SecurityException('answer_hash mismatch.');
                }
                if ($nonce != $server_DH_inner_data['nonce']) {
                    throw new SecurityException('wrong nonce');
                }
                if ($server_nonce != $server_DH_inner_data['server_nonce']) {
                    throw new SecurityException('wrong server nonce');
                }
                $g = new BigInteger($server_DH_inner_data['g']);
                $g_a = new BigInteger((string) $server_DH_inner_data['g_a'], 256);
                $dh_prime = new BigInteger((string) $server_DH_inner_data['dh_prime'], 256);
                /*
                 * ***********************************************************************
                 * Time delta
                 */
                $server_time = $server_DH_inner_data['server_time'];
                $this->time_delta = $server_time - time();
                $this->API->logger(\sprintf('Server-client time delta = %.1f s', $this->time_delta), Logger::VERBOSE);
                Crypt::checkPG($dh_prime, $g);
                Crypt::checkG($g_a, $dh_prime);
                for ($retry_id = 0; $retry_id <= $this->API->settings->getAuth()->getMaxAuthTries(); $retry_id++) {
                    $this->API->logger('Generating b...', Logger::VERBOSE);
                    $b = new BigInteger(Tools::random(256), 256);
                    $this->API->logger('Generating g_b...', Logger::VERBOSE);
                    $g_b = $g->powMod($b, $dh_prime);
                    Crypt::checkG($g_b, $dh_prime);
                    /*
                     * ***********************************************************************
                     * Check validity of g_b
                     * 1 < g_b < dh_prime - 1
                     */
                    $this->API->logger('Executing g_b check...', Logger::VERBOSE);
                    if ($g_b->compare(Magic::$one) <= 0 || $g_b->compare($dh_prime->subtract(Magic::$one)) >= 0) {
                        throw new SecurityException('g_b is invalid (1 < g_b < dh_prime - 1 is false).');
                    }
                    $this->API->logger('Preparing client_DH_inner_data...', Logger::VERBOSE);
                    $g_b_str = $g_b->toBytes();
                    /*
                     * ***********************************************************************
                     * serialize client_DH_inner_data
                     * @method client_DH_inner_data
                     * @param Server_DH_inner_data [
                     *         int128         $nonce                             : The value of nonce is selected randomly by the client (random number) and identifies the client within this communication
                     *         int128        $server_nonce                    : The value of server_nonce is selected randomly by the server
                     *         long        $retry_id                        : First attempt
                     *         string        $g_b                            : g^b mod dh_prime
                     * ]
                     */
                    $data = ($this->API->getTL()->serializeObject(['type' => ''], ['_' => 'client_DH_inner_data', 'nonce' => $nonce, 'server_nonce' => $server_nonce, 'retry_id' => $retry_id, 'g_b' => $g_b_str], 'client_DH_inner_data'));
                    /*
                     * ***********************************************************************
                     * encrypt client_DH_inner_data
                     */
                    $data_with_sha = sha1($data, true).$data;
                    $data_with_sha_padded = $data_with_sha.Tools::random(Tools::posmod(-\strlen($data_with_sha), 16));
                    $encrypted_data = Crypt::igeEncrypt($data_with_sha_padded, $tmp_aes_key, $tmp_aes_iv);
                    $this->API->logger('Executing set_client_DH_params...', Logger::VERBOSE);
                    /*
                     * ***********************************************************************
                     * Send set_client_DH_params query
                     * @method set_client_DH_params
                     * @param Server_DH_inner_data [
                     *         int128         $nonce                             : The value of nonce is selected randomly by the client (random number) and identifies the client within this communication
                     *         int128        $server_nonce                    : The value of server_nonce is selected randomly by the server
                     *         string        $encrypted_data
                     * ]
                     * @return Set_client_DH_params_answer [
                     *         string         $_                                 : This value is dh_gen_ok, dh_gen_retry OR dh_gen_fail
                     *         int128         $server_nonce                     : The value of server_nonce is selected randomly by the server
                     *         int128         $new_nonce_hash1                : Return this value if server responds with dh_gen_ok
                     *         int128         $new_nonce_hash2                : Return this value if server responds with dh_gen_retry
                     *         int128         $new_nonce_hash2                : Return this value if server responds with dh_gen_fail
                     * ]
                     */
                    $Set_client_DH_params_answer = $this->methodCallAsyncRead('set_client_DH_params', ['nonce' => $nonce, 'server_nonce' => $server_nonce, 'encrypted_data' => $encrypted_data]);
                    /*
                     * ***********************************************************************
                     * Generate auth_key
                     */
                    $this->API->logger(
                        \extension_loaded('gmp') ? 'Generating authorization key...' : 'Generating authorization key (install gmp to speed up this process)...',
                        Logger::VERBOSE
                    );
                    $auth_key = $g_a->powMod($b, $dh_prime);
                    $auth_key_str = $auth_key->toBytes();
                    $auth_key_sha = sha1($auth_key_str, true);
                    $auth_key_aux_hash = substr($auth_key_sha, 0, 8);
                    $new_nonce_hash1 = substr(sha1($new_nonce.\chr(1).$auth_key_aux_hash, true), -16);
                    $new_nonce_hash2 = substr(sha1($new_nonce.\chr(2).$auth_key_aux_hash, true), -16);
                    $new_nonce_hash3 = substr(sha1($new_nonce.\chr(3).$auth_key_aux_hash, true), -16);
                    /*
                     * ***********************************************************************
                     * Check if the client's nonce and the server's nonce are the same
                     */
                    if ($Set_client_DH_params_answer['nonce'] != $nonce) {
                        throw new SecurityException('wrong nonce.');
                    }
                    /*
                     * ***********************************************************************
                     * Check if server_nonce and new server_nonce are the same
                     */
                    if ($Set_client_DH_params_answer['server_nonce'] != $server_nonce) {
                        throw new SecurityException('wrong server nonce');
                    }
                    /*
                     * ***********************************************************************
                     * Check Set_client_DH_params_answer type
                     */
                    switch ($Set_client_DH_params_answer['_']) {
                        case 'dh_gen_ok':
                            if ($Set_client_DH_params_answer['new_nonce_hash1'] != $new_nonce_hash1) {
                                throw new SecurityException('wrong new_nonce_hash1');
                            }
                            $this->API->logger('Diffie Hellman key exchange processed successfully!', Logger::VERBOSE);
                            if ($temp) {
                                $this->shared->auth->setTempAuthKey(
                                    $auth_key_str,
                                    substr($new_nonce, 0, 8) ^ substr($server_nonce, 0, 8)
                                );
                            } else {
                                $this->shared->auth->setAuthKey($auth_key_str);
                            }
                            $this->API->logger('Auth key generated', Logger::NOTICE);
                            return;
                        case 'dh_gen_retry':
                            if ($Set_client_DH_params_answer['new_nonce_hash2'] != $new_nonce_hash2) {
                                throw new SecurityException('wrong new_nonce_hash_2');
                            }
                            //repeat foreach
                            $this->API->logger('Retrying Auth', Logger::VERBOSE);
                            break;
                        case 'dh_gen_fail':
                            if ($Set_client_DH_params_answer['new_nonce_hash3'] != $new_nonce_hash3) {
                                throw new SecurityException('wrong new_nonce_hash_3');
                            }
                            $this->API->logger('Auth Failed', Logger::WARNING);
                            break 2;
                        default:
                            throw new SecurityException('Response Error');
                            break;
                    }
                }
            } catch (SecurityException|Exception|RPCErrorException $e) {
                $this->API->logger("An exception occurred while generating the authorization key in DC {$this->datacenter}: ".$e.' in '.basename($e->getFile(), '.php').' on line '.$e->getLine().'. Retrying...', Logger::WARNING);
                $this->reconnect();
            } catch (Throwable $e) {
                $this->API->logger("An exception occurred while generating the authorization key in DC {$this->datacenter}: ".$e.PHP_EOL.' Retrying (try number '.$retry_id_total.')...', Logger::WARNING);
                $this->reconnect();
            }
        }
        if (!$cdn) {
            throw new SecurityException('Auth Failed, please check the logfile for more information, make sure to install https://prime.madelineproto.xyz!');
        }
    }
}
<?php

declare(strict_types=1);

/**
 * ResponseHandler module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\MTProtoSession;

use Amp\CancelledException;
use Amp\SignalException;
use danog\BetterPrometheus\BetterHistogram;
use danog\Loop\Loop;
use danog\MadelineProto\DataCenterConnection;
use danog\MadelineProto\FileRedirect;
use danog\MadelineProto\Lang;
use danog\MadelineProto\Logger;
use danog\MadelineProto\Loop\Update\UpdateLoop;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\MTProto\ConnectionState;
use danog\MadelineProto\MTProto\MTProtoIncomingMessage;
use danog\MadelineProto\MTProto\MTProtoOutgoingMessage;
use danog\MadelineProto\MTProto\SpecialMethodType;
use danog\MadelineProto\PTSException;
use danog\MadelineProto\RPCError\FloodPremiumWaitError;
use danog\MadelineProto\RPCError\FloodWaitError;
use danog\MadelineProto\RPCError\RateLimitError;
use danog\MadelineProto\RPCErrorException;
use danog\MadelineProto\SecretPeerNotInDbException;
use danog\MadelineProto\SecurityException;
use Revolt\EventLoop;
use SplQueue;
use Throwable;

use const PHP_EOL;

/**
 * Manages responses.
 *
 * @property ?BetterHistogram $requestLatencies
 * @property MTProto $API
 * @property DataCenterConnection $shared
 * @internal
 */
trait ResponseHandler
{
    /**
     * @param iterable<array-key, MTProtoIncomingMessage> $messages
     */
    private function handleMessages(iterable $messages): ?float
    {
        foreach ($messages as $message) {
            $this->API->logger($message->log($this->datacenter), Logger::ULTRA_VERBOSE);

            $type = $message->getPredicate();
            if ($type !== 'msg_container') {
                $this->checkInSeqNo($message);
            }
            try {
                match ($type) {
                    'msgs_ack' => $message->read(),

                    'rpc_result',
                    'future_salts',
                    'msgs_state_info',
                    'bad_server_salt',
                    'bad_msg_notification',
                    'pong' => $this->handleResponse($message),

                    'new_session_created' => $this->handleNewSession($message),
                    'msg_container' => $this->handleContainer($message),
                    'msg_copy' => $this->handleMsgCopy($message),
                    'http_wait' => $this->API->logger($message->read(), Logger::NOTICE),
                    'msgs_state_req' => $this->sendMsgsStateInfo($message->read()['msg_ids'], $message->getMsgId()),
                    'msgs_all_info' => $this->onMsgsAllInfo($message->read()),
                    'msg_detailed_info' => $this->onMsgDetailedInfo($message->read()),
                    'msg_new_detailed_info' => $this->onNewMsgDetailedInfo($message->read()),
                    'msg_resend_req' => $this->onMsgResendReq($message->read(), $message->getMsgId()),
                    'msg_resend_ans_req' => $this->onMsgResendAnsReq($message->read(), $message->getMsgId()),
                    default => $this->handleFallback($message)
                };
            } catch (\Throwable $e) {
                $this->API->logger("An error occurred while handling $message: $e", Logger::FATAL_ERROR);
            }
        }
        return Loop::PAUSE;
    }
    private function handleFallback(MTProtoIncomingMessage $message): void
    {
        $message->ack();
        $response_type = $this->API->getTL()->getConstructors()->findByPredicate($message->getContent()['_'])['type'];
        if ($response_type == 'Updates') {
            if ($message->unencrypted) {
                throw new SecurityException("Can't accept unencrypted update!");
            }
            if (!$this->shared->auth->isCdn) {
                EventLoop::queue($this->API->handleUpdates(...), $message->read());
            }
            return;
        }

        $this->API->logger('Trying to assign a response of type ' . $response_type . ' to its request...', Logger::VERBOSE);
        foreach ($this->unencrypted_new_outgoing as $expecting_msg_id => $expecting) {
            if (!$expecting->type) {
                continue;
            }
            $this->API->logger("Does the request of return type {$expecting->type} match?", Logger::VERBOSE);
            if ($response_type === $expecting->type) {
                $this->API->logger('Yes', Logger::VERBOSE);
                $this->handleResponse($message, $expecting_msg_id);
                return;
            }
            $this->API->logger('No', Logger::VERBOSE);
        }
        $this->API->logger('Dunno how to handle ' . PHP_EOL . var_export($message->read(), true), Logger::FATAL_ERROR);
    }
    private function handleNewSession(MTProtoIncomingMessage $message): void
    {
        $this->shared->auth->setServerSalt($message->read()['server_salt']);
        if (isset($this->API->updaters[UpdateLoop::GENERIC])) {
            $this->API->updaters[UpdateLoop::GENERIC]->resume();
        }
    }
    private function handleContainer(MTProtoIncomingMessage $message): void
    {
        $tmp = new SplQueue;
        $tmp->setIteratorMode(SplQueue::IT_MODE_DELETE);
        foreach ($message->read()['messages'] as $msg) {
            $this->msgIdHandler->checkIncomingMessageId($msg['msg_id'], true);
            $newMessage = new MTProtoIncomingMessage(
                $this,
                $msg['body'],
                $msg['msg_id'],
                $message->unencrypted,
                true
            );
            $newMessage->setSeqNo($msg['seqno']);
            $this->checkInSeqNo($newMessage);
            $newMessage->setSeqNo(null);
            $tmp->enqueue($newMessage);
            $this->incomingCtr?->inc();
            //$this->incoming_messages[$msg['msg_id']] = $newMessage;
        }
        $this->checkInSeqNo($message);
        $this->handleMessages($tmp);
    }
    private function handleMsgCopy(MTProtoIncomingMessage $message): void
    {
        $content = $message->read();
        $referencedMsgId = $content['msg_id'];
        /*if (isset($this->incoming_messages[$referencedMsgId])) {
            $this->incoming_messages[$referencedMsgId]->ack();
        } else {*/
        $this->msgIdHandler->checkIncomingMessageId($referencedMsgId, true);
        $message = new MTProtoIncomingMessage(
            $this,
            $content['orig_message'],
            $referencedMsgId,
            $message->unencrypted,
            true
        );
        $this->incomingCtr?->inc();
        //$this->incoming_messages[$referencedMsgId] = $message;
        $this->handleMessages([$message]);
        //}
    }

    /**
     * Handle RPC response.
     */
    private function handleResponse(MTProtoIncomingMessage $message, ?int $requestId = null): void
    {
        $requestId ??= $message->getRequestId();
        $response = $message->read();
        if ($response['_'] === 'rpc_result') {
            if ($message->unencrypted) {
                throw new SecurityException("Can't accept unencrypted result!");
            }
            $response = $response['result'];
        }
        $arr = $message->unencrypted ? $this->unencrypted_new_outgoing : $this->new_outgoing;
        if (!isset($arr[$requestId])) {
            $this->API->logger("Got a response $message with message ID $requestId, but there is no request!", Logger::ERROR);
            return;
        }
        /** @var MTProtoOutgoingMessage */
        $request = $arr[$requestId];
        if ($request->hasReply()) {
            $this->API->logger("Already got a response to $request, but there is another reply $message with message ID $requestId!", Logger::FATAL_ERROR);
            return;
        }
        if ($response instanceof Throwable) {
            // The result couldn't be deserialized, see ReadLoop::deserialize.
            $request->reply(static fn (): Throwable => $response);
            return;
        }
        $constructor = $response['_'] ?? '';
        if ($request->specialMethodType === SpecialMethodType::USER_RELATED) {
            $this->API->loginState->publish($this->API->loginState->getState()->setDc($this->datacenter));
        }
        if ($constructor === 'rpc_error') {
            try {
                $exception = $this->handleRpcError($request, $response);
            } catch (Throwable $e) {
                $exception = static fn (): \Throwable => $e;
            }
            if ($exception) {
                $request->reply($exception);
            }
            return;
        }
        if ($constructor === 'bad_server_salt' || $constructor === 'bad_msg_notification') {
            $this->API->logger('Received bad_msg_notification: ' . MTProto::BAD_MSG_ERROR_CODES[$response['error_code']], Logger::WARNING);
            switch ($response['error_code']) {
                case 48:
                    $this->shared->auth->setServerSalt($response['new_server_salt']);
                    $this->methodRecall($request);
                    return;
                case 20:
                    $this->methodRecall($request, $this->datacenter);
                    return;
                case 16:
                case 17:
                    $this->time_delta = ($message->getMsgId() >> 32) - time();
                    $this->API->logger('Set time delta to ' . $this->time_delta, Logger::WARNING);
                    $this->API->resetMTProtoSession("time delta update");
                    $this->shared->auth->setTempAuthKey(null, null);
                    $this->methodRecall($request, $this->datacenter);
                    return;
            }
            $request->reply(static fn () => RPCErrorException::make('Received bad_msg_notification: ' . MTProto::BAD_MSG_ERROR_CODES[$response['error_code']], $response['error_code'], $request->constructor));
            return;
        }
        if ($constructor === 'rpc_answer_dropped_running'
            || $constructor === 'rpc_answer_unknown'
            || $constructor === 'rpc_answer_dropped'
        ) {
            $request->reply(static fn () => new CancelledException(RPCErrorException::make("Request cancelled using $constructor", 0, $request->constructor)));
            return;
        }

        if (isset($response['_']) && !$this->shared->auth->isCdn) {
            $responseType = $this->API->getTL()->getConstructors()->findByPredicate($response['_'])['type'];
            if ($responseType === 'Updates') {
                $body = $request->getBodyOrEmpty();
                $trimmed = $body;
                if (isset($trimmed['peer']) && (
                    !\is_array($trimmed['peer'])
                    || (($trimmed['peer']['_'] ?? null) !== 'inputPhoneCall')
                )) {
                    try {
                        $trimmed['peer'] = \is_string($body['peer']) ? $body['peer'] : $this->API->getIdInternal($body['peer']);
                    } catch (Throwable $e) {
                    }
                }
                if (isset($trimmed['message'])) {
                    $trimmed['message'] = (string) $body['message'];
                }
                $response['request'] = ['_' => $request->constructor, 'body' => $trimmed];
                unset($body);
                EventLoop::queue($this->API->handleUpdates(...), $response);
            } elseif ($responseType === 'messages.SentEncryptedMessage') {
                $body = $request->getBodyOrEmpty();
                try {
                    $response = $this->API->getSecretChatController($body['peer'])->handleSent($body, $response);
                } catch (SecretPeerNotInDbException) {
                }
            }
        }

        if ($this->API->referenceDatabase !== null
            && !$this->shared->auth->isCdn
            && $this->API->getSettings()->getDb()->getEnableFileReferenceDb()
        ) {
            // Before replying, which frees the body: the file references must be stored by the time the caller
            // gets the result (refreshes compare them, downloads use them).
            $this->API->referenceDatabase->onMethodResult($request->constructor, $request->getBodyOrEmpty(), $response);
        }

        $this->requestResponse?->inc([
            'method' => $request->constructor,
            'error_message' => 'OK',
            'error_code' => '200',
        ]);

        $request->reply($response);
    }
    /**
     * @param array{error_message: string, error_code: int} $response
     * @return (callable(): Throwable)|null
     */
    private function handleRpcError(MTProtoOutgoingMessage $request, array $response): ?callable
    {
        $this->requestResponse?->inc([
            'method' => $request->constructor,
            'error_message' => preg_replace('/\d+/', 'X', $response['error_message']),
            'error_code' => (string) $response['error_code'],
        ]);

        if (\in_array($response['error_message'], ['PERSISTENT_TIMESTAMP_EMPTY', 'PERSISTENT_TIMESTAMP_INVALID'], true)) {
            return static fn () => new PTSException($response['error_message']);
        }
        if ($response['error_message'] === 'PERSISTENT_TIMESTAMP_OUTDATED') {
            $response['error_code'] = 500;
        }
        if (str_starts_with($response['error_message'], 'FILE_REFERENCE_')
            && $request->specialMethodType !== SpecialMethodType::FILEREF_RELATED
            && !$request->refreshedFileReferences
            && $this->API->getSettings()->getDb()->getEnableFileReferenceDb()
        ) {
            $this->API->logger("Got {$response['error_message']}, refreshing file reference and repeating method call...");
            $this->methodRecall($request, $this->datacenter, $request->refreshReferences($response['error_message'], $response['error_code']));
            return null;
        }

        switch ($response['error_code']) {
            case 500:
            case -500:
            case -503:
                if ((($response['error_code'] === -503 || $response['error_message'] === '-503') && !\in_array($request->constructor, ['messages.getBotCallbackAnswer', 'messages.getInlineBotResults'], true))
                    || (\in_array($response['error_message'], ['MSGID_DECREASE_RETRY', 'HISTORY_GET_FAILED', 'RPC_CONNECT_FAILED', 'RPC_CALL_FAIL', 'RPC_MCGET_FAIL', 'PERSISTENT_TIMESTAMP_OUTDATED', 'RPC_MCGET_FAIL', 'no workers running', 'No workers running'], true))) {
                    $this->API->logger("Resending $request in 1 second due to {$response['error_message']}");
                    $this->methodRecall($request, $this->datacenter, 1.0);
                    return null;
                }
                return static fn () => RPCErrorException::make($response['error_message'], $response['error_code'], $request->constructor);
            case 303:
                $datacenter = (int) preg_replace('/[^0-9]+/', '', $response['error_message']);
                if ($this->API->isTestMode()) {
                    $datacenter += 10_000;
                }
                if ($request->specialMethodType === SpecialMethodType::FILE_RELATED) {
                    return fn () => new FileRedirect(
                        $this->API->datacenter->has(-$datacenter)
                            ? -$datacenter
                            : $datacenter
                    );
                }
                $this->API->datacenter->currentDatacenter = $datacenter;
                $this->API->logger("Resending $request to new DC $datacenter...");
                $this->methodRecall($request, $datacenter);
                return null;
            case 400:
                if ($response['error_message'] === 'CONNECTION_NOT_INITED') {
                    $this->shared->auth->connectionState->publish(ConnectionState::ENCRYPTED_NOT_INITED);
                    $this->API->logger("Resending $request due to CONNECTION_NOT_INITED...");
                    $this->methodRecall($request, $this->datacenter);
                    return null;
                }
                return static fn () => RPCErrorException::make($response['error_message'], $response['error_code'], $request->constructor);
            case 401:
                switch ($response['error_message']) {
                    case 'USER_DEACTIVATED':
                    case 'USER_DEACTIVATED_BAN':
                    case 'SESSION_REVOKED':
                    case 'SESSION_EXPIRED':
                        $this->API->logger($response['error_message'], Logger::FATAL_ERROR);
                        $phone = null;
                        if (\in_array($response['error_message'], ['USER_DEACTIVATED', 'USER_DEACTIVATED_BAN'], true)) {
                            $phone = isset($this->API->authorization['user']['phone']) ? '+' . $this->API->authorization['user']['phone'] : '???';
                            $this->API->logger(\sprintf(Lang::$current_lang['account_banned'], $phone), Logger::FATAL_ERROR);
                        }
                        $this->API->logout();
                        return static fn () => new SignalException(\sprintf(Lang::$current_lang['account_banned'], $phone ?? '?'));
                    case 'AUTH_KEY_UNREGISTERED':
                    case 'AUTH_KEY_INVALID':
                        if ($this->API->loginState->getState()->state !== \danog\MadelineProto\API::LOGGED_IN) {
                            $request->reply(static fn () => RPCErrorException::make($response['error_message'], $response['error_code'], $request->constructor));
                            return null;
                        }
                        $this->session_id = null;
                        $this->session_in_seq_no = 0;
                        $this->session_out_seq_no = 0;
                        $this->shared->auth->setAuthKey(null);
                        $this->API->logger("Auth key not registered in DC {$this->datacenter} with RPC error {$response['error_message']}, resetting temporary and permanent auth keys...", Logger::ERROR);
                        if ($this->API->loginState->getState()->authorizedDc == $this->datacenter) {
                            $this->API->logger('Permanent auth key was main authorized key, logging out...', Logger::FATAL_ERROR);
                            $phone = isset($this->API->authorization['user']['phone']) ? '+' . $this->API->authorization['user']['phone'] : 'you are currently using';
                            $this->API->logger(\sprintf(Lang::$current_lang['account_banned'], $phone), Logger::FATAL_ERROR);
                            $this->API->logout();
                            return static fn () => new SignalException(\sprintf(Lang::$current_lang['account_banned'], $phone));
                        }
                        $this->methodRecall($request, $this->datacenter);
                        return null;
                    case 'AUTH_KEY_PERM_EMPTY':
                        $this->API->logger('Temporary auth key not bound, resetting temporary auth key...', Logger::ERROR);
                        $this->shared->auth->setTempAuthKey(null, null);
                        $this->methodRecall($request, $this->datacenter);
                        return null;
                }
                return static fn () => RPCErrorException::make($response['error_message'], $response['error_code'], $request->constructor);
            case 420:
                $seconds = (int) preg_replace('/[^0-9]+/', '', $response['error_message']);
                \assert($seconds > 0);
                $limit = $request->floodWaitLimit ?? $this->API->settings->getRPC()->getFloodTimeout();
                if ($seconds < $limit) {
                    $this->API->logger("Flood, waiting $seconds seconds before repeating async call of $request...", Logger::NOTICE);
                    $this->methodRecall($request, $this->datacenter, (float) $seconds);
                    return null;
                }
                if (str_starts_with($response['error_message'], 'FLOOD_WAIT_')) {
                    return static fn () => new FloodWaitError(
                        $response['error_message'],
                        $seconds,
                        $response['error_code'],
                        $request->constructor
                    );
                }
                if (str_starts_with($response['error_message'], 'FLOOD_PREMIUM_WAIT_')) {
                    return static fn () => new FloodPremiumWaitError(
                        $response['error_message'],
                        $seconds,
                        $response['error_code'],
                        $request->constructor
                    );
                }
                return static fn () => new RateLimitError(
                    $response['error_message'],
                    $seconds,
                    $response['error_code'],
                    $request->constructor
                );
            default:
                return static fn () => RPCErrorException::make($response['error_message'], $response['error_code'], $request->constructor);
        }
    }
}
P’©rè   þÎ boolFalse#bc799737 = Bool;
boolTrue#997275b5 = Bool;
true#3fedd339 = True;
vector#1cb5c415 {t:Type} # [ t ] = Vector t;

fileIdPhoto#47a0bd49 id:long = FileId;
fileIdDocument#461b1d89 id:long = FileId;

fileSourceMessage#b19f4c78 flags:# quick_reply_shortcut_id:flags.0?int peer:long id:int = FileSource;
fileSourceStarsTransaction#c1bac8c7 flags:# peer:long id:string refund:flags.0?true ton:flags.1?true = FileSource;
fileSourceScheduledMessage#9dc1f5f7 peer:long id:int = FileSource;
fileSourceStory#c820e3eb id:int peer:long = FileSource;
fileSourceWebPage#9e5b749c url:string = FileSource;
fileSourceBotApp#01cf8b7a id:long access_hash:long = FileSource;
fileSourceUserFull#70fdb7b0 id:long = FileSource;
fileSourceAdminLog#4797f959 channel:long max_id:long = FileSource;
fileSourceStoryAlbum#5e01f223 peer:long = FileSource;
fileSourceBotPreviewMedia#0aa91441 bot:long = FileSource;
fileSourceBotPreviewInfo#f9d2d6fc bot:long lang_code:string = FileSource;
fileSourcePaidMedia#b18d9042 id:int peer:long = FileSource;
fileSourceSavedMusic#dd1a7664 user_id:long id:long access_hash:long = FileSource;
fileSourceChatFull#9de75fde chat_id:long = FileSource;
fileSourceChannelFull#6fe19339 channel:long = FileSource;
fileSourceCommunityFull#bf21671d channel:long = FileSource;
fileSourcePremiumPromo#c907a44f = FileSource;
fileSourceAttachMenuBot#c3002694 bot:long = FileSource;
fileSourceTheme#92d05e0c id:long access_hash:long = FileSource;
fileSourceWallPaper#50dbf2f7 id:long access_hash:long = FileSource;
fileSourceStickerSet#34c73709 stickerset:InputStickerSet = FileSource;
fileSourceSavedGifs#13e78e07 = FileSource;
fileSourceSavedRingtones#2b25ef1b = FileSource;
fileSourceAvailableEffects#eb8578f0 = FileSource;
fileSourceAvailableReactions#0e432388 = FileSource;
fileSourceUserProfilePhoto#e39ee274 user_id:long max_id:long = FileSource;
fileSourceDocumentByHash#0f151e0f sha256:bytes size:long mime_type:string = FileSource;
fileSourceWelcomeMessages#fd77304a peer:long = FileSource;
  þü {"constructors":[{"id":"-1132882121","predicate":"boolFalse","params":[],"type":"Bool"},{"id":"-1720552011","predicate":"boolTrue","params":[],"type":"Bool"},{"id":"1072550713","predicate":"true","params":[],"type":"True"},{"id":"481674261","predicate":"vector","params":[],"type":"Vector t"},{"id":"1201716553","predicate":"fileIdPhoto","params":[{"name":"id","type":"long"}],"type":"FileId"},{"id":"1176182153","predicate":"fileIdDocument","params":[{"name":"id","type":"long"}],"type":"FileId"},{"id":"-1314960264","predicate":"fileSourceMessage","params":[{"name":"flags","type":"#"},{"name":"quick_reply_shortcut_id","type":"flags.0?int"},{"name":"peer","type":"long"},{"name":"id","type":"int"}],"type":"FileSource"},{"id":"-1044723513","predicate":"fileSourceStarsTransaction","params":[{"name":"flags","type":"#"},{"name":"peer","type":"long"},{"name":"id","type":"string"},{"name":"refund","type":"flags.0?true"},{"name":"ton","type":"flags.1?true"}],"type":"FileSource"},{"id":"-1648232969","predicate":"fileSourceScheduledMessage","params":[{"name":"peer","type":"long"},{"name":"id","type":"int"}],"type":"FileSource"},{"id":"-937368597","predicate":"fileSourceStory","params":[{"name":"id","type":"int"},{"name":"peer","type":"long"}],"type":"FileSource"},{"id":"-1638173540","predicate":"fileSourceWebPage","params":[{"name":"url","type":"string"}],"type":"FileSource"},{"id":"30378874","predicate":"fileSourceBotApp","params":[{"name":"id","type":"long"},{"name":"access_hash","type":"long"}],"type":"FileSource"},{"id":"1895675824","predicate":"fileSourceUserFull","params":[{"name":"id","type":"long"}],"type":"FileSource"},{"id":"1201142105","predicate":"fileSourceAdminLog","params":[{"name":"channel","type":"long"},{"name":"max_id","type":"long"}],"type":"FileSource"},{"id":"1577185827","predicate":"fileSourceStoryAlbum","params":[{"name":"peer","type":"long"}],"type":"FileSource"},{"id":"178852929","predicate":"fileSourceBotPreviewMedia","params":[{"name":"bot","type":"long"}],"type":"FileSource"},{"id":"-103622916","predicate":"fileSourceBotPreviewInfo","params":[{"name":"bot","type":"long"},{"name":"lang_code","type":"string"}],"type":"FileSource"},{"id":"-1316122558","predicate":"fileSourcePaidMedia","params":[{"name":"id","type":"int"},{"name":"peer","type":"long"}],"type":"FileSource"},{"id":"-585468316","predicate":"fileSourceSavedMusic","params":[{"name":"user_id","type":"long"},{"name":"id","type":"long"},{"name":"access_hash","type":"long"}],"type":"FileSource"},{"id":"-1645781026","predicate":"fileSourceChatFull","params":[{"name":"chat_id","type":"long"}],"type":"FileSource"},{"id":"1877054265","predicate":"fileSourceChannelFull","params":[{"name":"channel","type":"long"}],"type":"FileSource"},{"id":"-1088329955","predicate":"fileSourceCommunityFull","params":[{"name":"channel","type":"long"}],"type":"FileSource"},{"id":"-922246065","predicate":"fileSourcePremiumPromo","params":[],"type":"FileSource"},{"id":"-1023400300","predicate":"fileSourceAttachMenuBot","params":[{"name":"bot","type":"long"}],"type":"FileSource"},{"id":"-1831838196","predicate":"fileSourceTheme","params":[{"name":"id","type":"long"},{"name":"access_hash","type":"long"}],"type":"FileSource"},{"id":"1356591863","predicate":"fileSourceWallPaper","params":[{"name":"id","type":"long"},{"name":"access_hash","type":"long"}],"type":"FileSource"},{"id":"885470985","predicate":"fileSourceStickerSet","params":[{"name":"stickerset","type":"InputStickerSet"}],"type":"FileSource"},{"id":"333942279","predicate":"fileSourceSavedGifs","params":[],"type":"FileSource"},{"id":"723906331","predicate":"fileSourceSavedRingtones","params":[],"type":"FileSource"},{"id":"-343574288","predicate":"fileSourceAvailableEffects","params":[],"type":"FileSource"},{"id":"239281032","predicate":"fileSourceAvailableReactions","params":[],"type":"FileSource"},{"id":"-476126604","predicate":"fileSourceUserProfilePhoto","params":[{"name":"user_id","type":"long"},{"name":"max_id","type":"long"}],"type":"FileSource"},{"id":"253042191","predicate":"fileSourceDocumentByHash","params":[{"name":"sha256","type":"bytes"},{"name":"size","type":"long"},{"name":"mime_type","type":"string"}],"type":"FileSource"},{"id":"-42520502","predicate":"fileSourceWelcomeMessages","params":[{"name":"peer","type":"long"}],"type":"FileSource"}],"methods":[]}Äµ[  ÅYÌ”Document   document   Äµ   uÄN    ZÀ;Þdocument   µur™fileSourceStickerSet   Äµ   ��6¢†5Äµ   ¿¡Document   document   
attributes DocumentAttribute  ÏÕÙ¬
stickerset Äµ    Äµ    uÄN   ZÀ;Þdocument   µur™fileSourceSavedMusic   Äµ   .ª w„6ñXÄµ   ¿¡users.SavedMusic   users.getSavedMusicid 	InputUser  ÏÕÙ¬user_idI•r¢†5Äµ   ¿¡Document   document   id long   ÏÕÙ¬id I•r¢†5Äµ   ¿¡Document   document   access_hashlong   ÏÕÙ¬access_hashÄµ    ZÀ;Þusers.getSavedMusic7—y¼Äµ    uÄN   ZÀ;Þdocument   µur™fileSourceSavedMusic   Äµ   .ª w„6ñXÄµ   ¿¡users.SavedMusic   users.getSavedMusicByIDid 	InputUser  ÏÕÙ¬user_idI•r¢†5Äµ   ¿¡Document   document   id long   ÏÕÙ¬id I•r¢†5Äµ   ¿¡Document   document   access_hashlong   ÏÕÙ¬access_hashÄµ    ZÀ;Þusers.getSavedMusicByID7—y¼Äµ    fileIdDocument ÅYÌ”Photo  photo  Äµ   uÄN   ZÀ;Þphoto  µur™fileSourceUserProfilePhoto Äµ   .ª w„6ñXÄµ   ¿¡photos.Photos  photos.getUserPhotos   user_id	InputUser  ÏÕÙ¬user_idI•r¢†5Äµ   ¿¡Photo  photo  id long   ÏÕÙ¬max_id Äµ    ZÀ;Þphotos.getUserPhotos   7—y¼Äµ    fileIdPhoto6ò©ýmessages.getDialogsÄµ    7—y¼¶ Mnmessages.Dialogs   messages.dialogs   Äµ   gb   dialogsDialog gb   messages   MessageÄµ    7—y¼¶ MnMessagemessageÄµ   gb   reply_to   MessageReplyHeader gb   media  MessageMedia   gb   rich_message   RichMessageÄµ   uÄN    ZÀ;Þmessageµur™fileSourceMessage  Äµ   I•r¢†5Äµ   ¿¡Messagemessagequick_reply_shortcut_idint}áÆquick_reply_shortcut_idœ3}¢†5Äµ   ¿¡Messagemessagepeer_idPeer   ÏÕÙ¬peer   I•r¢†5Äµ   ¿¡Messagemessageid intÏÕÙ¬id Äµ    Äµ   ZÀ;Þmessages.getScheduledMessages  7—y¼ZÀ;ÞupdateNewScheduledMessage  µur™uÄN   ZÀ;Þmessageµur™fileSourceScheduledMessage Äµ   ´Ï¥„6ñXÄµ   ¿¡messages.Messages  messages.getScheduledMessages  peer   	InputPeer  ÏÕÙ¬peer   I•r¢†5Äµ   ¿¡Messagemessageid intÏÕÙ¬id Äµ    ZÀ;Þmessages.getScheduledMessages  7—y¼Äµ    uÄN   ZÀ;Þmessageµur™fileSourceScheduledMessage Äµ   œ3}„6ñXÄµ   ¿¡Update updateNewScheduledMessage  messageMessageÏÕÙ¬¿¡Messagemessagepeer_idPeer   ÏÕÙ¬peer   I•r¢†5Äµ   ¿¡Messagemessageid intÏÕÙ¬id Äµ    ZÀ;ÞupdateNewScheduledMessage  µur™Äµ    7—y¼¶ MnMessageMedia   messageMediaPhoto  Äµ   gb   photo  Photo  gb   video  Document   Äµ    7—y¼¶ Mnmessages.Dialogs   messages.dialogsSlice  Äµ   gb   dialogsDialog gb   messages   MessageÄµ    7—y¼6ò©ýmessages.getMessages   Äµ    7—y¼¶ Mnmessages.Messages  messages.messages  Äµ   gb   messages   Messagegb   topics 
ForumTopic Äµ    7—y¼6ò©ýmessages.getHistoryÄµ    7—y¼6ò©ýmessages.searchÄµ    7—y¼6ò©ýmessages.searchGlobal  Äµ    7—y¼6ò©ýmessages.getUnreadMentions Äµ    7—y¼6ò©ýmessages.getRecentLocationsÄµ    7—y¼6ò©ýmessages.getScheduledHistory   Äµ    7—y¼6ò©ýmessages.getScheduledMessages  Äµ    µur™6ò©ýmessages.getRepliesÄµ    7—y¼6ò©ýmessages.getUnreadReactionsÄµ    7—y¼6ò©ýmessages.searchSentMedia   Äµ    7—y¼6ò©ýmessages.getSavedHistory   Äµ    7—y¼6ò©ýmessages.getQuickReplyMessages Äµ    7—y¼6ò©ýmessages.getUnreadPollVotesÄµ    7—y¼6ò©ý"messages.getPersonalChannelHistory Äµ    7—y¼6ò©ýmessages.getRichMessageÄµ    7—y¼6ò©ýchannels.getMessages   Äµ    7—y¼6ò©ýchannels.searchPosts   Äµ    7—y¼¶ Mnmessages.Messages  messages.messagesSlice Äµ   gb   messages   Messagegb   topics 
ForumTopic Äµ    7—y¼¶ Mnmessages.Messages  messages.channelMessages   Äµ   gb   messages   Messagegb   topics 
ForumTopic Äµ    7—y¼¶ MnUpdate updateNewMessage   Äµ   gb    messageMessageÄµ    7—y¼¶ MnUpdate updateNewChannelMessageÄµ   gb    messageMessageÄµ    7—y¼¶ MnUpdate updateEditChannelMessage   Äµ   gb    messageMessageÄµ    7—y¼¶ MnUpdate updateEditMessage  Äµ   gb    messageMessageÄµ    7—y¼¶ MnUpdate updateNewScheduledMessage  Äµ   gb    messageMessageÄµ    µur™¶ MnUpdate updateQuickReplyMessageÄµ   gb    messageMessageÄµ    7—y¼¶ MnUpdate updateBotNewBusinessMessageÄµ   gb    messageMessagegb   reply_to_message   MessageÄµ    7—y¼¶ MnUpdate updateBotEditBusinessMessage   Äµ   gb    messageMessagegb   reply_to_message   MessageÄµ    7—y¼¶ MnUpdate updateBusinessBotCallbackQuery Äµ   gb    messageMessagegb   reply_to_message   MessageÄµ    7—y¼¶ MnUpdate updateBotGuestChatQueryÄµ   gb    messageMessagegb   reference_messages MessageÄµ    7—y¼6ò©ýupdates.getDifference  Äµ    7—y¼¶ Mnupdates.Difference updates.difference Äµ   gb   new_messages   MessageÄµ    7—y¼¶ Mnupdates.Difference updates.differenceSliceÄµ   gb   new_messages   MessageÄµ    7—y¼6ò©ýupdates.getChannelDifference   Äµ    7—y¼¶ Mnupdates.ChannelDifference   updates.channelDifferenceTooLong   Äµ   gb    dialog Dialog gb   messages   MessageÄµ    7—y¼¶ Mnupdates.ChannelDifference  updates.channelDifference  Äµ   gb   new_messages   MessageÄµ    7—y¼6ò©ýmessages.getPeerDialogsÄµ    7—y¼¶ Mnmessages.PeerDialogs   messages.peerDialogs   Äµ   gb   dialogsDialog gb   messages   MessageÄµ    7—y¼6ò©ýmessages.getPinnedDialogs  Äµ    7—y¼6ò©ýchannels.getAdminLog   Äµ    µur™¶ Mnchannels.AdminLogResults   channels.adminLogResults   Äµ   gb   events ChannelAdminLogEvent   Äµ    7—y¼¶ MnChannelAdminLogEvent   channelAdminLogEvent   Äµ   gb    action ChannelAdminLogEventAction Äµ   uÄN   ZÀ;ÞchannelAdminLogEvent   µur™fileSourceAdminLog Äµ   fb¶„6ñXÄµ   ¿¡channels.AdminLogResults   channels.getAdminLog   channelInputChannel   ÏÕÙ¬channelI•r¢†5Äµ   ¿¡ChannelAdminLogEvent   channelAdminLogEvent   id long   ÏÕÙ¬max_id Äµ    ZÀ;Þchannels.getAdminLog   7—y¼Äµ    7—y¼¶ MnChannelAdminLogEventAction &channelAdminLogEventActionUpdatePinned Äµ   gb    messageMessageÄµ    7—y¼¶ MnChannelAdminLogEventAction %channelAdminLogEventActionEditMessage  Äµ   gb    prev_message   Messagegb    new_messageMessageÄµ    7—y¼¶ MnChannelAdminLogEventAction 'channelAdminLogEventActionDeleteMessageÄµ   gb    messageMessageÄµ    7—y¼¶ MnChannelAdminLogEventAction "channelAdminLogEventActionStopPoll Äµ   gb    messageMessageÄµ    7—y¼¶ MnChannelAdminLogEventAction %channelAdminLogEventActionSendMessage  Äµ   gb    messageMessageÄµ    7—y¼6ò©ýmessages.getDiscussionMessage  Äµ    7—y¼¶ Mnmessages.DiscussionMessage messages.discussionMessage Äµ   gb   messages   MessageÄµ    7—y¼6ò©ý!messages.getSearchResultsCalendar  Äµ    7—y¼¶ Mnmessages.SearchResultsCalendar messages.searchResultsCalendar Äµ   gb   messages   MessageÄµ    7—y¼6ò©ýmessages.getForumTopicsÄµ    7—y¼¶ Mnmessages.ForumTopics   messages.forumTopics   Äµ   gb   topics 
ForumTopic gb   messages   MessageÄµ    7—y¼6ò©ýmessages.getForumTopicsByIDÄµ    7—y¼6ò©ýstories.getStoryViewsList  Äµ    7—y¼¶ Mnstories.StoryViewsList stories.storyViewsList Äµ   gb   views  	StoryView  Äµ    7—y¼¶ Mn	StoryView  storyViewPublicForward Äµ   gb    messageMessageÄµ    7—y¼6ò©ýstats.getMessagePublicForwards Äµ    7—y¼¶ Mnstats.PublicForwards   stats.publicForwards   Äµ   gb   forwards   PublicForward  Äµ    7—y¼¶ MnPublicForward  publicForwardMessage   Äµ   gb    messageMessageÄµ    7—y¼6ò©ýstats.getStoryPublicForwards   Äµ    7—y¼6ò©ýstories.getStoryReactionsList  Äµ    7—y¼¶ Mnstories.StoryReactionsList stories.storyReactionsList Äµ   gb   	reactions  StoryReaction  Äµ    7—y¼¶ MnStoryReaction  storyReactionPublicForward Äµ   gb    messageMessageÄµ    7—y¼6ò©ýmessages.getSavedDialogs   Äµ    7—y¼¶ Mnmessages.SavedDialogs  messages.savedDialogs  Äµ   gb   dialogsSavedDialoggb   messages   MessageÄµ    7—y¼6ò©ýmessages.getPinnedSavedDialogs Äµ    7—y¼6ò©ýmessages.getSavedDialogsByID   Äµ    7—y¼¶ Mnmessages.SavedDialogs  messages.savedDialogsSlice Äµ   gb   dialogsSavedDialoggb   messages   MessageÄµ    7—y¼6ò©ýmessages.getQuickReplies   Äµ    7—y¼¶ Mnmessages.QuickReplies  messages.quickReplies  Äµ   gb   messages   MessageÄµ    7—y¼¶ MnMessageMedia   messageMediaPoll   Äµ   gb    poll   Poll   gb    resultsPollResultsgb   attached_media MessageMedia   Äµ    7—y¼¶ MnUpdate updateServiceNotification  Äµ   gb    media  MessageMedia   Äµ    7—y¼¶ MnMessagemessageService Äµ   gb   reply_to   MessageReplyHeader gb    action MessageAction  Äµ   uÄN    ZÀ;ÞmessageService µur™fileSourceMessage  Äµ   œ3}¢†5Äµ   ¿¡MessagemessageService peer_idPeer   ÏÕÙ¬peer   I•r¢†5Äµ   ¿¡MessagemessageService id intÏÕÙ¬id Äµ   quick_reply_shortcut_idÄµ   ZÀ;Þmessages.getScheduledMessages  7—y¼ZÀ;ÞupdateNewScheduledMessage  µur™7—y¼¶ MnMessageAction  messageActionPollAppendAnswer  Äµ   gb    answer 
PollAnswer Äµ    7—y¼¶ Mn
PollAnswer 
pollAnswer Äµ   gb   media  MessageMedia   Äµ    7—y¼¶ MnMessageAction  messageActionPollDeleteAnswer  Äµ   gb    answer 
PollAnswer Äµ    7—y¼¶ MnUpdate updateMessagePoll  Äµ   gb   poll   Poll   gb    resultsPollResultsÄµ   uÄN    ZÀ;ÞupdateMessagePoll  µur™fileSourceMessage  Äµ   œ3}¢†5Äµ   ¿¡Update updateMessagePoll  peer   Peer   îŸþøpeer   I•r¢†5Äµ   ¿¡Update updateMessagePoll  msg_id intîŸþøid Äµ   quick_reply_shortcut_idÄµ   ZÀ;Þmessages.getScheduledMessages  7—y¼ZÀ;ÞupdateNewScheduledMessage  µur™7—y¼¶ MnPoll   poll   Äµ   gb   answers
PollAnswer Äµ    7—y¼¶ MnPollResultspollResultsÄµ   gb   solution_media MessageMedia   Äµ    7—y¼¶ MnMessageReplyHeader messageReplyHeader Äµ   gb   reply_mediaMessageMedia   Äµ    7—y¼¶ MnUpdate updateNewEphemeralMessage  Äµ   gb    messageEphemeralMessage   Äµ    7—y¼¶ MnEphemeralMessage   ephemeralMessage   Äµ   gb   media  MessageMedia   gb   reply_to   MessageReplyHeader gb   rich_message   RichMessageÄµ    7—y¼¶ MnUpdate updateEditEphemeralMessage Äµ   gb    messageEphemeralMessage   Äµ    7—y¼¶ MnUpdate updateEphemeralBotCallbackQueryÄµ   gb    messageEphemeralMessage   Äµ    7—y¼6ò©ýephemeral.getWelcomeMessages   Äµ   uÄN    ZÀ;Þephemeral.getWelcomeMessages   7—y¼fileSourceWelcomeMessages  Äµ   ´Ï¥¢†5Äµ   ¿¡ephemeral.WelcomeMessages  ephemeral.getWelcomeMessages   peer   	InputPeer  ÏÕÙ¬peer   Äµ    Äµ    7—y¼¶ Mnephemeral.WelcomeMessages  ephemeral.welcomeMessages  Äµ   gb   messages   EphemeralMessage   Äµ    7—y¼6ò©ýmessages.getSponsoredMessages  Äµ    7—y¼¶ Mnmessages.SponsoredMessages messages.sponsoredMessages Äµ   gb   messages   SponsoredMessage   Äµ    7—y¼¶ MnSponsoredMessage   sponsoredMessage   Äµ   gb   media  MessageMedia   Äµ    7—y¼¶ MnUpdate updateMessageExtendedMedia Äµ   gb   extended_media MessageExtendedMedia   Äµ   uÄN    ZÀ;ÞupdateMessageExtendedMedia µur™fileSourcePaidMediaÄµ   I•r¢†5Äµ   ¿¡Update updateMessageExtendedMedia msg_id intÏÕÙ¬id œ3}¢†5Äµ   ¿¡Update updateMessageExtendedMedia peer   Peer   ÏÕÙ¬peer   Äµ    Äµ    7—y¼¶ MnMessageExtendedMedia   messageExtendedMedia   Äµ   gb    media  MessageMedia   Äµ    7—y¼¶ MnUpdate updateStoryÄµ   gb    story  	StoryItem  Äµ    7—y¼¶ Mn	StoryItem  	storyItem  Äµ   gb    media  MessageMedia   gb   music  Document   Äµ   uÄN   ZÀ;Þ	storyItem  µur™fileSourceStoryÄµ   I•r¢†5Äµ   ¿¡	StoryItem  	storyItem  id intÏÕÙ¬id ´Ï¥„6ñXÄµ   ¿¡stories.Storiesstories.getPinnedStories   peer   	InputPeer  ÏÕÙ¬peer   Äµ    ZÀ;Þstories.getPinnedStories   7—y¼Äµ    uÄN   ZÀ;Þ	storyItem  µur™fileSourceStoryÄµ   I•r¢†5Äµ   ¿¡	StoryItem  	storyItem  id intÏÕÙ¬id ´Ï¥„6ñXÄµ   ¿¡stories.Storiesstories.getStoriesArchive  peer   	InputPeer  ÏÕÙ¬peer   Äµ    ZÀ;Þstories.getStoriesArchive  7—y¼Äµ    uÄN   ZÀ;Þ	storyItem  µur™fileSourceStoryÄµ   I•r¢†5Äµ   ¿¡	StoryItem  	storyItem  id intÏÕÙ¬id ´Ï¥„6ñXÄµ   ¿¡stories.Storiesstories.getStoriesByID peer   	InputPeer  ÏÕÙ¬peer   Äµ    ZÀ;Þstories.getStoriesByID 7—y¼Äµ    uÄN   ZÀ;Þ	storyItem  µur™fileSourceStoryÄµ   I•r¢†5Äµ   ¿¡	StoryItem  	storyItem  id intÏÕÙ¬id ´Ï¥„6ñXÄµ   ¿¡stories.Storiesstories.getAlbumStoriespeer   	InputPeer  ÏÕÙ¬peer   Äµ    ZÀ;Þstories.getAlbumStories7—y¼Äµ    uÄN   ZÀ;Þ	storyItem  µur™fileSourceStoryÄµ   I•r¢†5Äµ   ¿¡	StoryItem  	storyItem  id intÏÕÙ¬id œ3}„6ñXÄµ   ¿¡PeerStoriespeerStoriespeer   Peer   ÏÕÙ¬peer   Äµ    ZÀ;ÞpeerStoriesµur™Äµ    uÄN    ZÀ;Þ	storyItem  µur™fileSourceStoryÄµ   I•r¢†5Äµ   ¿¡	StoryItem  	storyItem  id intÏÕÙ¬id œ3}¢†5Äµ   ¿¡	StoryItem  	storyItem  from_idPeer   îŸþøpeer   Äµ    Äµ    7—y¼¶ MnUpdate updateWebPage  Äµ   gb    webpageWebPageÄµ    7—y¼¶ MnWebPagewebPageÄµ   gb   photo  Photo  gb   document   Document   gb   cached_pagePage   gb   
attributes WebPageAttribute   Äµ   uÄN    ZÀ;ÞwebPageµur™fileSourceWebPage  Äµ   I•r¢†5Äµ   ¿¡WebPagewebPageurlstring ÏÕÙ¬urlÄµ    Äµ    7—y¼¶ MnWebPageAttribute   webPageAttributeStory  Äµ   gb   story  	StoryItem  Äµ   uÄN    ZÀ;ÞwebPageAttributeStory  µur™fileSourceStoryÄµ   I•r¢†5Äµ   ¿¡WebPageAttribute   webPageAttributeStory  story  	StoryItem  îŸþø¿¡	StoryItem  	storyItem  id intÏÕÙ¬id œ3}¢†5Äµ   ¿¡WebPageAttribute   webPageAttributeStory  peer   Peer   ÏÕÙ¬peer   Äµ    Äµ    7—y¼¶ MnUpdate updateChannelWebPage   Äµ   gb    webpageWebPageÄµ    7—y¼6ò©ýmessages.getWebPageÄµ    7—y¼¶ Mnmessages.WebPage   messages.webPage   Äµ   gb    webpageWebPageÄµ    7—y¼6ò©ýstories.getPinnedStories   Äµ    µur™¶ Mnstories.Storiesstories.storiesÄµ   gb   stories	StoryItem  Äµ    7—y¼6ò©ýstories.getStoriesArchive  Äµ    µur™6ò©ýstories.getStoriesByID Äµ    µur™6ò©ýstories.getAlbumStoriesÄµ    µur™¶ Mn	StoryView  storyViewPublicRepost  Äµ   gb    story  	StoryItem  Äµ   uÄN    ZÀ;ÞstoryViewPublicRepost  µur™fileSourceStoryÄµ   I•r¢†5Äµ   ¿¡	StoryView  storyViewPublicRepost  story  	StoryItem  ÏÕÙ¬¿¡	StoryItem  	storyItem  id intÏÕÙ¬id œ3}¢†5Äµ   ¿¡	StoryView  storyViewPublicRepost  peer_idPeer   ÏÕÙ¬peer   Äµ    Äµ    7—y¼¶ MnPeerStoriespeerStoriesÄµ   gb   stories	StoryItem  Äµ    µur™6ò©ýmessages.getFullChat   Äµ    7—y¼¶ Mnmessages.ChatFull  messages.chatFull  Äµ   gb    	full_chat  ChatFull   Äµ    7—y¼¶ MnChatFull   channelFullÄµ   gb    
chat_photo Photo  gb   bot_info   BotInfogb   storiesPeerStoriesgb   	wallpaper  	WallPaper  gb   share_theme
ShareTheme Äµ   uÄN    ZÀ;ÞchannelFullµur™fileSourceChannelFull  Äµ   I•r¢†5Äµ   ¿¡ChatFull   channelFullid long   ÏÕÙ¬channelÄµ    Äµ    7—y¼6ò©ýchannels.getFullChannelÄµ    7—y¼6ò©ýusers.getFullUser  Äµ    7—y¼¶ Mnusers.UserFull users.userFull Äµ   gb    	full_user  UserFull   Äµ    7—y¼¶ MnUserFull   userFull   Äµ   gb   personal_photo Photo  gb   profile_photo  Photo  gb   fallback_photo Photo  gb   bot_info   BotInfogb   theme  	ChatTheme  gb   	wallpaper  	WallPaper  gb   storiesPeerStoriesgb   business_intro BusinessIntro  gb   saved_musicDocument   gb   wallet_cardwallet.Cardgb   share_theme
ShareTheme Äµ   uÄN    ZÀ;ÞuserFull   µur™fileSourceUserFull Äµ   I•r¢†5Äµ   ¿¡UserFull   userFull   id long   ÏÕÙ¬id Äµ    Äµ    uÄN    ZÀ;ÞuserFull   µur™fileSourceSavedMusic   Äµ   I•r¢†5Äµ   ¿¡UserFull   userFull   id long   ÏÕÙ¬user_idI•r¢†5Äµ   ¿¡UserFull   userFull   saved_musicDocument   îŸþø¿¡Document   document   id long   ÏÕÙ¬id I•r¢†5Äµ   ¿¡UserFull   userFull   saved_musicDocument   îŸþø¿¡Document   document   access_hashlong   ÏÕÙ¬access_hashÄµ    Äµ    7—y¼6ò©ýstories.getAllStories  Äµ    7—y¼¶ Mnstories.AllStories stories.allStories Äµ   gb   peer_stories   PeerStoriesÄµ    7—y¼6ò©ýstories.getPeerStories Äµ    7—y¼¶ Mnstories.PeerStoriesstories.peerStoriesÄµ   gb    storiesPeerStoriesÄµ    7—y¼¶ MnPublicForward  publicForwardStory Äµ   gb    story  	StoryItem  Äµ   uÄN    ZÀ;ÞpublicForwardStory µur™fileSourceStoryÄµ   I•r¢†5Äµ   ¿¡PublicForward  publicForwardStory story  	StoryItem  ÏÕÙ¬¿¡	StoryItem  	storyItem  id intÏÕÙ¬id œ3}¢†5Äµ   ¿¡PublicForward  publicForwardStory peer   Peer   ÏÕÙ¬peer   Äµ    Äµ    7—y¼¶ MnStoryReaction  storyReactionPublicRepost  Äµ   gb    story  	StoryItem  Äµ   uÄN    ZÀ;ÞstoryReactionPublicRepost  µur™fileSourceStoryÄµ   I•r¢†5Äµ   ¿¡StoryReaction  storyReactionPublicRepost  story  	StoryItem  ÏÕÙ¬¿¡	StoryItem  	storyItem  id intÏÕÙ¬id œ3}¢†5Äµ   ¿¡StoryReaction  storyReactionPublicRepost  peer_idPeer   ÏÕÙ¬peer   Äµ    Äµ    7—y¼6ò©ýstories.searchPostsÄµ    7—y¼¶ Mnstories.FoundStories   stories.foundStories   Äµ   gb   stories
FoundStory Äµ    7—y¼¶ Mn
FoundStory 
foundStory Äµ   gb    story  	StoryItem  Äµ   uÄN    ZÀ;Þ
foundStory µur™fileSourceStoryÄµ   I•r¢†5Äµ   ¿¡
FoundStory 
foundStory story  	StoryItem  ÏÕÙ¬¿¡	StoryItem  	storyItem  id intÏÕÙ¬id œ3}¢†5Äµ   ¿¡
FoundStory 
foundStory peer   Peer   ÏÕÙ¬peer   Äµ    Äµ    7—y¼6ò©ýpayments.getStarsStatusÄµ    µur™¶ Mnpayments.StarsStatus   payments.starsStatus   Äµ   gb   historyStarsTransaction   Äµ    7—y¼¶ MnStarsTransaction   starsTransaction   Äµ   gb   extended_media MessageMedia   gb   stargift   StarGift   Äµ   uÄN   ZÀ;ÞstarsTransaction   µur™fileSourceStarsTransaction Äµ   ´Ï¥„6ñXÄµ   ¿¡payments.StarsStatus   payments.getStarsStatuspeer   	InputPeer  ÏÕÙ¬peer   I•r„6ñXÄµ   ¿¡payments.StarsStatus   payments.getStarsStatustontrue   }áÆtonI•r¢†5Äµ   ¿¡StarsTransaction   starsTransaction   id string ÏÕÙ¬id I•r¢†5Äµ   ¿¡StarsTransaction   starsTransaction   refund true   }áÆrefund Äµ    ZÀ;Þpayments.getStarsStatus7—y¼Äµ    uÄN   ZÀ;ÞstarsTransaction   µur™fileSourceStarsTransaction Äµ   ´Ï¥„6ñXÄµ   ¿¡payments.StarsStatus   payments.getStarsTransactions  peer   	InputPeer  ÏÕÙ¬peer   I•r„6ñXÄµ   ¿¡payments.StarsStatus   payments.getStarsTransactions  tontrue   }áÆtonI•r¢†5Äµ   ¿¡StarsTransaction   starsTransaction   id string ÏÕÙ¬id I•r¢†5Äµ   ¿¡StarsTransaction   starsTransaction   refund true   }áÆrefund Äµ    ZÀ;Þpayments.getStarsTransactions  7—y¼Äµ    uÄN   ZÀ;ÞstarsTransaction   µur™fileSourceStarsTransaction Äµ   ´Ï¥„6ñXÄµ   ¿¡payments.StarsStatus   !payments.getStarsTransactionsByID  peer   	InputPeer  ÏÕÙ¬peer   I•r„6ñXÄµ   ¿¡payments.StarsStatus   !payments.getStarsTransactionsByID  tontrue   }áÆtonI•r¢†5Äµ   ¿¡StarsTransaction   starsTransaction   id string ÏÕÙ¬id I•r¢†5Äµ   ¿¡StarsTransaction   starsTransaction   refund true   }áÆrefund Äµ    ZÀ;Þ!payments.getStarsTransactionsByID  7—y¼Äµ    uÄN   ZÀ;ÞstarsTransaction   µur™fileSourceStarsTransaction Äµ   ´Ï¥„6ñXÄµ   ¿¡payments.StarsStatus   payments.getStarsSubscriptions peer   	InputPeer  ÏÕÙ¬peer   I•r¢†5Äµ   ¿¡StarsTransaction   starsTransaction   id string ÏÕÙ¬id I•r¢†5Äµ   ¿¡StarsTransaction   starsTransaction   refund true   }áÆrefund Äµ   tonZÀ;Þpayments.getStarsSubscriptions 7—y¼Äµ    7—y¼6ò©ýpayments.getStarsTransactions  Äµ    µur™6ò©ý!payments.getStarsTransactionsByID  Äµ    µur™6ò©ýpayments.getStarsSubscriptions Äµ    µur™6ò©ýbots.getPreviewInfoÄµ   uÄN    ZÀ;Þbots.getPreviewInfo7—y¼fileSourceBotPreviewInfo   Äµ   .ª w¢†5Äµ   ¿¡bots.PreviewInfo   bots.getPreviewInfobot	InputUser  ÏÕÙ¬botI•r¢†5Äµ   ¿¡bots.PreviewInfo   bots.getPreviewInfo	lang_code  string ÏÕÙ¬	lang_code  Äµ    Äµ    7—y¼¶ Mnbots.PreviewInfo   bots.previewInfo   Äµ   gb   media  BotPreviewMediaÄµ    7—y¼¶ MnBotPreviewMediabotPreviewMediaÄµ   gb    media  MessageMedia   Äµ    7—y¼6ò©ýbots.addPreviewMedia   Äµ   uÄN    ZÀ;Þbots.addPreviewMedia   7—y¼fileSourceBotPreviewInfo   Äµ   .ª w¢†5Äµ   ¿¡BotPreviewMediabots.addPreviewMedia   bot	InputUser  ÏÕÙ¬botI•r¢†5Äµ   ¿¡BotPreviewMediabots.addPreviewMedia   	lang_code  string ÏÕÙ¬	lang_code  Äµ    Äµ    7—y¼6ò©ýbots.editPreviewMedia  Äµ   uÄN    ZÀ;Þbots.editPreviewMedia  7—y¼fileSourceBotPreviewInfo   Äµ   .ª w¢†5Äµ   ¿¡BotPreviewMediabots.editPreviewMedia  bot	InputUser  ÏÕÙ¬botI•r¢†5Äµ   ¿¡BotPreviewMediabots.editPreviewMedia  	lang_code  string ÏÕÙ¬	lang_code  Äµ    Äµ    7—y¼6ò©ýbots.getPreviewMedias  Äµ   uÄN    ZÀ;Þbots.getPreviewMedias  7—y¼fileSourceBotPreviewMedia  Äµ   .ª w¢†5Äµ   ¿¡Vector t   bots.getPreviewMedias  bot	InputUser  ÏÕÙ¬botÄµ    Äµ    7—y¼6ò©ýmessages.getWebPagePreview Äµ    7—y¼¶ Mnmessages.WebPagePreviewmessages.webPagePreviewÄµ   gb    media  MessageMedia   Äµ    7—y¼6ò©ýmessages.uploadMedia   Äµ    7—y¼6ò©ýmessages.uploadImportedMedia   Äµ    7—y¼¶ MnMessageMedia   messageMediaInvoiceÄµ   gb   extended_media MessageExtendedMedia   Äµ    7—y¼¶ MnMessageMedia   messageMediaPaidMedia  Äµ   gb   extended_media MessageExtendedMedia   Äµ    7—y¼¶ MnMessageMedia   messageMediaStory  Äµ   gb   story  	StoryItem  Äµ   uÄN    ZÀ;ÞmessageMediaStory  µur™fileSourceStoryÄµ   I•r¢†5Äµ   ¿¡MessageMedia   messageMediaStory  story  	StoryItem  îŸþø¿¡	StoryItem  	storyItem  id intÏÕÙ¬id œ3}¢†5Äµ   ¿¡MessageMedia   messageMediaStory  peer   Peer   ÏÕÙ¬peer   Äµ    Äµ    7—y¼¶ MnMessageMedia   messageMediaWebPageÄµ   gb    webpageWebPageÄµ    7—y¼¶ MnMessageMedia   messageMediaDocument   Äµ   gb   document   Document   gb   alt_documents  Document   gb   video_coverPhoto  Äµ    7—y¼¶ Mn	WallPaper  	wallPaper  Äµ   gb    document   Document   Äµ   uÄN    ZÀ;Þ	wallPaper  µur™fileSourceWallPaperÄµ   I•r¢†5Äµ   ¿¡	WallPaper  	wallPaper  id long   ÏÕÙ¬id I•r¢†5Äµ   ¿¡	WallPaper  	wallPaper  access_hashlong   ÏÕÙ¬access_hashÄµ    Äµ    7—y¼¶ MnMessageAction  messageActionSetChatWallPaper  Äµ   gb    	wallpaper  	WallPaper  Äµ    7—y¼¶ MnUpdate updatePeerWallpaperÄµ   gb   	wallpaper  	WallPaper  Äµ    7—y¼¶ MnChannelAdminLogEventAction )channelAdminLogEventActionChangeWallpaper  Äµ   gb    
prev_value 	WallPaper  gb    	new_value  	WallPaper  Äµ    7—y¼6ò©ýaccount.getWallPapers  Äµ    7—y¼¶ Mnaccount.WallPapers account.wallPapers Äµ   gb   
wallpapers 	WallPaper  Äµ    7—y¼¶ MnUpdate updateThemeÄµ   gb    theme  Theme  Äµ    7—y¼¶ MnTheme  theme  Äµ   gb   document   Document   gb   settings   ThemeSettings  Äµ   uÄN    ZÀ;Þtheme  µur™fileSourceThemeÄµ   I•r¢†5Äµ   ¿¡Theme  theme  id long   ÏÕÙ¬id I•r¢†5Äµ   ¿¡Theme  theme  access_hashlong   ÏÕÙ¬access_hashÄµ    Äµ    7—y¼¶ MnThemeSettings  themeSettings  Äµ   gb   	wallpaper  	WallPaper  Äµ    7—y¼6ò©ýaccount.getThemes  Äµ    7—y¼¶ Mnaccount.Themes account.themes Äµ   gb   themes Theme  Äµ    7—y¼6ò©ýaccount.getChatThemes  Äµ    7—y¼6ò©ýaccount.createThemeÄµ    7—y¼6ò©ýaccount.updateThemeÄµ    7—y¼6ò©ýaccount.getTheme   Äµ    7—y¼¶ MnWebPageAttribute   webPageAttributeTheme  Äµ   gb   	documents  Document   gb   settings   ThemeSettings  Äµ    7—y¼¶ MnMessageAction  messageActionSetChatTheme  Äµ   gb    theme  	ChatTheme  Äµ    7—y¼¶ Mn	ChatTheme  chatThemeUniqueGiftÄµ   gb    gift   StarGift   gb   theme_settings ThemeSettings  Äµ    7—y¼6ò©ýaccount.getUniqueGiftChatThemesÄµ    7—y¼¶ Mnaccount.ChatThemes account.chatThemes Äµ   gb   themes 	ChatTheme  Äµ    7—y¼6ò©ýaccount.getWallPaper   Äµ    7—y¼6ò©ýaccount.uploadWallPaperÄµ    7—y¼6ò©ýaccount.getMultiWallPapers Äµ    7—y¼6ò©ýhelp.getAppUpdate  Äµ    7—y¼¶ Mnhelp.AppUpdate help.appUpdate Äµ   gb   document   Document   gb   stickerDocument   Äµ    7—y¼6ò©ýmessages.getStickers   Äµ    7—y¼¶ Mnmessages.Stickers  messages.stickers  Äµ   gb   stickers   Document   Äµ    7—y¼¶ MnUpdate updateNewStickerSetÄµ   gb    
stickerset messages.StickerSetÄµ    7—y¼¶ Mnmessages.StickerSetmessages.stickerSetÄµ   gb   	documents  Document   Äµ   uÄN    ZÀ;Þmessages.stickerSetµur™fileSourceStickerSet   Äµ   pÔgÁ¢†5Äµ   ¿¡messages.StickerSetmessages.stickerSetset
StickerSet ÏÕÙ¬
stickerset Äµ    Äµ    7—y¼6ò©ýmessages.getStickerSet Äµ    7—y¼6ò©ýstickers.createStickerSet  Äµ    7—y¼6ò©ýstickers.removeStickerFromSet  Äµ    7—y¼6ò©ýstickers.changeStickerPosition Äµ    7—y¼6ò©ýstickers.addStickerToSet   Äµ    7—y¼6ò©ýstickers.setStickerSetThumbÄµ    7—y¼6ò©ýstickers.changeSticker Äµ    7—y¼6ò©ýstickers.renameStickerSet  Äµ    7—y¼6ò©ýstickers.replaceStickerÄµ    7—y¼¶ MnChatFull   chatFull   Äµ   gb   
chat_photo Photo  gb   bot_info   BotInfoÄµ   uÄN    ZÀ;ÞchatFull   µur™fileSourceChatFull Äµ   I•r¢†5Äµ   ¿¡ChatFull   chatFull   id long   ÏÕÙ¬chat_idÄµ    Äµ    7—y¼¶ MnBotInfobotInfoÄµ   gb   description_photo  Photo  gb   description_document   Document   Äµ   uÄN    ZÀ;ÞbotInfoµur™fileSourceUserFull Äµ   I•r¢†5Äµ   ¿¡BotInfobotInfouser_idlong   îŸþøid Äµ    Äµ    7—y¼6ò©ýmessages.getSavedGifs  Äµ    7—y¼¶ Mnmessages.SavedGifs messages.savedGifs Äµ   gb   gifs   Document   Äµ   uÄN    ZÀ;Þmessages.savedGifs µur™fileSourceSavedGifsÄµ    Äµ    Äµ    7—y¼6ò©ýmessages.getInlineBotResults   Äµ    7—y¼¶ Mnmessages.BotResultsmessages.botResultsÄµ   gb   resultsBotInlineResultÄµ    7—y¼¶ MnBotInlineResultbotInlineMediaResult   Äµ   gb   document   Document   gb    send_message   BotInlineMessage   Äµ    7—y¼6ò©ý!messages.getPreparedInlineMessage  Äµ    7—y¼¶ Mnmessages.PreparedInlineMessage messages.preparedInlineMessage Äµ   gb    result BotInlineResultÄµ    7—y¼6ò©ýmessages.getRecentStickers Äµ    7—y¼¶ Mnmessages.RecentStickersmessages.recentStickersÄµ   gb   stickers   Document   Äµ    7—y¼6ò©ýmessages.getFeaturedStickers   Äµ    7—y¼¶ Mnmessages.FeaturedStickers  messages.featuredStickers  Äµ   gb   sets   StickerSetCovered  Äµ    7—y¼¶ MnStickerSetCovered  stickerSetCovered  Äµ   gb    cover  Document   Äµ    7—y¼6ò©ýmessages.getOldFeaturedStickersÄµ    7—y¼6ò©ý!messages.getFeaturedEmojiStickers  Äµ    7—y¼6ò©ýmessages.getArchivedStickers   Äµ    7—y¼¶ Mnmessages.ArchivedStickers  messages.archivedStickers  Äµ   gb   sets   StickerSetCovered  Äµ    7—y¼6ò©ýmessages.installStickerSet Äµ    7—y¼¶ Mn messages.StickerSetInstallResult   'messages.stickerSetInstallResultArchiveÄµ   gb   sets   StickerSetCovered  Äµ    7—y¼6ò©ýhelp.getRecentMeUrls   Äµ    7—y¼¶ Mnhelp.RecentMeUrls  help.recentMeUrls  Äµ   gb   urls   RecentMeUrlÄµ    7—y¼¶ MnRecentMeUrlrecentMeUrlStickerSet  Äµ   gb    setStickerSetCovered  Äµ    7—y¼6ò©ýmessages.searchStickerSets Äµ    7—y¼¶ Mnmessages.FoundStickerSets  messages.foundStickerSets  Äµ   gb   sets   StickerSetCovered  Äµ    7—y¼6ò©ýmessages.searchEmojiStickerSetsÄµ    7—y¼6ò©ýmessages.getMyStickers Äµ    7—y¼¶ Mnmessages.MyStickersmessages.myStickersÄµ   gb   sets   StickerSetCovered  Äµ    7—y¼6ò©ýmessages.getAttachedStickers   Äµ    7—y¼¶ MnStickerSetCovered  stickerSetMultiCovered Äµ   gb   covers Document   Äµ   uÄN    ZÀ;ÞstickerSetMultiCovered µur™fileSourceStickerSet   Äµ   pÔgÁ¢†5Äµ   ¿¡StickerSetCovered  stickerSetMultiCovered set
StickerSet ÏÕÙ¬
stickerset Äµ    Äµ    7—y¼¶ MnStickerSetCovered  stickerSetFullCovered  Äµ   gb   	documents  Document   Äµ   uÄN    ZÀ;ÞstickerSetFullCovered  µur™fileSourceStickerSet   Äµ   pÔgÁ¢†5Äµ   ¿¡StickerSetCovered  stickerSetFullCovered  set
StickerSet ÏÕÙ¬
stickerset Äµ    Äµ    7—y¼¶ MnMessageMedia   messageMediaGame   Äµ   gb    game   Game   Äµ    7—y¼¶ MnGame   game   Äµ   gb    photo  Photo  gb   document   Document   Äµ    7—y¼6ò©ýmessages.getFavedStickers  Äµ    7—y¼¶ Mnmessages.FavedStickers messages.favedStickers Äµ   gb   stickers   Document   Äµ    7—y¼¶ MnPage   page   Äµ   gb   photos Photo  gb   	documents  Document   Äµ    7—y¼¶ MnWebPageAttribute   webPageAttributeStickerSet Äµ   gb   stickers   Document   Äµ    7—y¼¶ MnWebPageAttribute   "webPageAttributeStarGiftCollection Äµ   gb   icons  Document   Äµ    7—y¼6ò©ýmessages.getAvailableReactions Äµ    7—y¼¶ Mnmessages.AvailableReactionsmessages.availableReactionsÄµ   gb   	reactions  AvailableReaction  Äµ   uÄN    ZÀ;Þmessages.availableReactionsµur™fileSourceAvailableReactions   Äµ    Äµ    Äµ    7—y¼¶ MnAvailableReaction  availableReaction  Äµ   gb    static_iconDocument   gb    appear_animation   Document   gb    select_animation   Document   gb    activate_animation Document   gb    effect_animation   Document   gb   around_animation   Document   gb   center_iconDocument   Äµ    7—y¼6ò©ýmessages.getAttachMenuBots Äµ    7—y¼¶ MnAttachMenuBots attachMenuBots Äµ   gb   bots   AttachMenuBot  Äµ    7—y¼¶ MnAttachMenuBot  attachMenuBot  Äµ   gb   icons  AttachMenuBotIcon  Äµ   uÄN    ZÀ;ÞattachMenuBot  µur™fileSourceAttachMenuBotÄµ   I•r¢†5Äµ   ¿¡AttachMenuBot  attachMenuBot  bot_id long   ÏÕÙ¬botÄµ    Äµ    7—y¼¶ MnAttachMenuBotIcon  attachMenuBotIcon  Äµ   gb    icon   Document   Äµ    7—y¼6ò©ýmessages.getAttachMenuBot  Äµ    7—y¼¶ MnAttachMenuBotsBot  attachMenuBotsBot  Äµ   gb    botAttachMenuBot  Äµ    7—y¼6ò©ýaccount.getSavedRingtones  Äµ    7—y¼¶ Mnaccount.SavedRingtones account.savedRingtones Äµ   gb   	ringtones  Document   Äµ   uÄN    ZÀ;Þaccount.savedRingtones µur™fileSourceSavedRingtones   Äµ    Äµ    Äµ    7—y¼6ò©ýaccount.saveRingtone   Äµ    7—y¼¶ Mnaccount.SavedRingtone  account.savedRingtoneConverted Äµ   gb    document   Document   Äµ   uÄN    ZÀ;Þaccount.savedRingtoneConverted µur™fileSourceSavedRingtones   Äµ    Äµ    Äµ    7—y¼6ò©ýhelp.getPremiumPromo   Äµ   uÄN    ZÀ;Þhelp.getPremiumPromo   7—y¼fileSourcePremiumPromo Äµ    Äµ    Äµ    7—y¼¶ Mnhelp.PremiumPromo  help.premiumPromo  Äµ   gb   videos Document   Äµ    7—y¼¶ MnMessageAction  messageActionBotAllowedÄµ   gb   appBotApp Äµ    7—y¼¶ MnBotApp botApp Äµ   gb    photo  Photo  gb   document   Document   Äµ   uÄN    ZÀ;ÞbotApp µur™fileSourceBotApp   Äµ   I•r¢†5Äµ   ¿¡BotApp botApp id long   ÏÕÙ¬id I•r¢†5Äµ   ¿¡BotApp botApp access_hashlong   ÏÕÙ¬access_hashÄµ    Äµ    7—y¼6ò©ýmessages.getBotApp Äµ    7—y¼¶ Mnmessages.BotAppmessages.botAppÄµ   gb    appBotApp Äµ    7—y¼¶ MnBusinessIntro  businessIntro  Äµ   gb   stickerDocument   Äµ    7—y¼6ò©ýmessages.getAvailableEffects   Äµ    7—y¼¶ Mnmessages.AvailableEffects  messages.availableEffects  Äµ   gb   	documents  Document   Äµ   uÄN    ZÀ;Þmessages.availableEffects  µur™fileSourceAvailableEffects Äµ    Äµ    Äµ    7—y¼¶ MnMessageAction  messageActionStarGift  Äµ   gb    gift   StarGift   Äµ    7—y¼¶ MnStarGift   starGift   Äµ   gb    stickerDocument   Äµ    7—y¼¶ MnMessageAction  messageActionStarGiftUniqueÄµ   gb    gift   StarGift   Äµ    7—y¼¶ MnMessageAction  "messageActionStarGiftPurchaseOffer Äµ   gb    gift   StarGift   Äµ    7—y¼¶ MnMessageAction  *messageActionStarGiftPurchaseOfferDeclined Äµ   gb    gift   StarGift   Äµ    7—y¼¶ MnWebPageAttribute   webPageAttributeUniqueStarGift Äµ   gb    gift   StarGift   Äµ    7—y¼¶ MnWebPageAttribute   webPageAttributeStarGiftAuctionÄµ   gb    gift   StarGift   Äµ    7—y¼6ò©ýpayments.getStarGifts  Äµ    7—y¼¶ Mnpayments.StarGifts payments.starGifts Äµ   gb   gifts  StarGift   Äµ    7—y¼6ò©ýpayments.getUniqueStarGift Äµ    7—y¼¶ Mnpayments.UniqueStarGiftpayments.uniqueStarGiftÄµ   gb    gift   StarGift   Äµ    7—y¼6ò©ýpayments.getSavedStarGifts Äµ    7—y¼¶ Mnpayments.SavedStarGiftspayments.savedStarGiftsÄµ   gb   gifts  SavedStarGift  Äµ    7—y¼¶ MnSavedStarGift  savedStarGift  Äµ   gb    gift   StarGift   Äµ    7—y¼6ò©ýpayments.getSavedStarGift  Äµ    7—y¼6ò©ýpayments.getCraftStarGifts Äµ    7—y¼6ò©ýpayments.getResaleStarGiftsÄµ    7—y¼¶ Mnpayments.ResaleStarGifts   payments.resaleStarGifts   Äµ   gb   gifts  StarGift   gb   
attributes StarGiftAttribute  Äµ    7—y¼6ò©ý payments.getStarGiftAuctionState   Äµ    7—y¼¶ Mnpayments.StarGiftAuctionState  payments.starGiftAuctionState  Äµ   gb    gift   StarGift   Äµ    7—y¼6ò©ý"payments.getStarGiftActiveAuctions Äµ    7—y¼¶ Mnpayments.StarGiftActiveAuctionspayments.starGiftActiveAuctionsÄµ   gb   auctions   StarGiftActiveAuctionState Äµ    7—y¼¶ MnStarGiftActiveAuctionState starGiftActiveAuctionState Äµ   gb    gift   StarGift   Äµ    7—y¼¶ MnUpdate updateSentWalletTransactionÄµ   gb   transactionWalletTransaction  Äµ    7—y¼¶ MnWalletTransaction  walletTransaction  Äµ   gb   nftwallet.NftItem Äµ    7—y¼¶ Mnwallet.NftItem wallet.nftItem Äµ   gb   gift   StarGift   Äµ    7—y¼6ò©ýwallet.getTransactions Äµ    7—y¼¶ Mnwallet.Transactionswallet.transactionsÄµ   gb   transactions   WalletTransaction  Äµ    7—y¼6ò©ýwallet.getTransactionsByIDsÄµ    7—y¼6ò©ýwallet.getTransactionsByMsgHashÄµ    7—y¼6ò©ýwallet.getNfts Äµ    7—y¼¶ Mnwallet.NftItemswallet.nftItemsÄµ   gb   items  wallet.NftItem Äµ    7—y¼¶ Mnwallet.Cardwallet.cardÄµ   gb   stickers   wallet.CardSticker gb   bg_nft_itemwallet.NftItem Äµ    7—y¼¶ Mnwallet.CardSticker wallet.cardSticker Äµ   gb    nftwallet.NftItem Äµ    7—y¼6ò©ýwallet.setCardStickers Äµ    7—y¼¶ Mn
ShareTheme shareThemeGift Äµ   gb    stargift   StarGift   Äµ    7—y¼6ò©ýmessages.searchStickersÄµ    7—y¼¶ Mnmessages.FoundStickers messages.foundStickers Äµ   gb   stickers   Document   Äµ    7—y¼¶ MnStarGift   starGiftUnique Äµ   gb   
attributes StarGiftAttribute  Äµ    7—y¼¶ MnStarGiftAttribute  starGiftAttributeModel Äµ   gb    document   Document   Äµ    7—y¼6ò©ý"payments.getStarGiftUpgradePreview Äµ    7—y¼¶ Mnpayments.StarGiftUpgradePreviewpayments.starGiftUpgradePreviewÄµ   gb   sample_attributes  StarGiftAttribute  Äµ    7—y¼6ò©ý%payments.getStarGiftUpgradeAttributes  Äµ    7—y¼¶ Mn"payments.StarGiftUpgradeAttributes "payments.starGiftUpgradeAttributes Äµ   gb   
attributes StarGiftAttribute  Äµ    7—y¼¶ MnStarGiftAttribute  starGiftAttributePattern   Äµ   gb    document   Document   Äµ    7—y¼6ò©ýpayments.getStarGiftCollectionsÄµ    7—y¼¶ Mnpayments.StarGiftCollections   payments.starGiftCollections   Äµ   gb   collectionsStarGiftCollection Äµ    7—y¼¶ MnStarGiftCollection starGiftCollection Äµ   gb   icon   Document   Äµ    7—y¼6ò©ý!payments.createStarGiftCollection  Äµ    7—y¼6ò©ý!payments.updateStarGiftCollection  Äµ    7—y¼6ò©ýstories.getAlbums  Äµ   uÄN    ZÀ;Þstories.getAlbums  7—y¼fileSourceStoryAlbum   Äµ   ´Ï¥¢†5Äµ   ¿¡stories.Albums stories.getAlbums  peer   	InputPeer  ÏÕÙ¬peer   Äµ    Äµ    7—y¼¶ Mnstories.Albums stories.albums Äµ   gb   albums 
StoryAlbum Äµ    7—y¼¶ Mn
StoryAlbum 
storyAlbum Äµ   gb   
icon_photo Photo  gb   
icon_video Document   Äµ    7—y¼6ò©ýstories.createAlbumÄµ   uÄN    ZÀ;Þstories.createAlbum7—y¼fileSourceStoryAlbum   Äµ   ´Ï¥¢†5Äµ   ¿¡
StoryAlbum stories.createAlbumpeer   	InputPeer  ÏÕÙ¬peer   Äµ    Äµ    7—y¼6ò©ýstories.updateAlbumÄµ   uÄN    ZÀ;Þstories.updateAlbum7—y¼fileSourceStoryAlbum   Äµ   ´Ï¥¢†5Äµ   ¿¡
StoryAlbum stories.updateAlbumpeer   	InputPeer  ÏÕÙ¬peer   Äµ    Äµ    7—y¼6ò©ýusers.getSavedMusicÄµ    µur™¶ Mnusers.SavedMusic   users.savedMusic   Äµ   gb   	documents  Document   Äµ    7—y¼6ò©ýusers.getSavedMusicByIDÄµ    µur™¶ MnRichMessagerichMessageÄµ   gb   photos Photo  gb   	documents  Document   Äµ    7—y¼¶ MnUpdate updateUserTyping   Äµ   gb    action SendMessageAction  Äµ    7—y¼¶ MnSendMessageAction  !sendMessageRichMessageDraftAction  Äµ   gb    rich_message   RichMessageÄµ    7—y¼¶ MnUpdate updateChatUserTyping   Äµ   gb    action SendMessageAction  Äµ    7—y¼¶ MnUpdate updateChannelUserTypingÄµ   gb    action SendMessageAction  Äµ    7—y¼¶ MnBotInlineResultbotInlineResultÄµ   gb    send_message   BotInlineMessage   Äµ    7—y¼¶ MnBotInlineMessage   botInlineMessageRichMessageÄµ   gb    rich_message   RichMessageÄµ    7—y¼¶ MnDialog dialog Äµ   gb   draft  DraftMessage   Äµ    7—y¼¶ MnDraftMessage   draftMessage   Äµ   gb   rich_message   RichMessageÄµ    7—y¼¶ MnUpdate updateDraftMessage Äµ   gb    draft  DraftMessage   Äµ    7—y¼¶ Mn
ForumTopic 
forumTopic Äµ   gb   draft  DraftMessage   Äµ    7—y¼¶ MnChannelAdminLogEventAction %channelAdminLogEventActionCreateTopic  Äµ   gb    topic  
ForumTopic Äµ    7—y¼¶ MnChannelAdminLogEventAction #channelAdminLogEventActionEditTopicÄµ   gb    
prev_topic 
ForumTopic gb    	new_topic  
ForumTopic Äµ    7—y¼¶ MnChannelAdminLogEventAction %channelAdminLogEventActionDeleteTopic  Äµ   gb    topic  
ForumTopic Äµ    7—y¼¶ MnChannelAdminLogEventAction "channelAdminLogEventActionPinTopic Äµ   gb   
prev_topic 
ForumTopic gb   	new_topic  
ForumTopic Äµ    7—y¼¶ MnSavedDialogmonoForumDialogÄµ   gb   draft  DraftMessage   Äµ    7—y¼6ò©ýmessages.translateRichMessage  Äµ    7—y¼¶ Mnmessages.TranslatedRichMessage messages.translatedRichMessage Äµ   gb   result RichMessageÄµ    7—y¼6ò©ý!messages.composeRichMessageWithAI  Äµ    7—y¼¶ Mn"messages.ComposedRichMessageWithAI "messages.composedRichMessageWithAI Äµ   gb    result RichMessageÄµ    7—y¼6ò©ýaccount.uploadThemeÄµ    7—y¼6ò©ýaccount.uploadRingtone Äµ   uÄN    ZÀ;Þaccount.uploadRingtone 7—y¼fileSourceSavedRingtones   Äµ    Äµ    Äµ    7—y¼6ò©ýmessages.getDocumentByHash Äµ   uÄN    ZÀ;Þmessages.getDocumentByHash 7—y¼fileSourceDocumentByHash   Äµ   I•r¢†5Äµ   ¿¡Document   messages.getDocumentByHash sha256 bytes  ÏÕÙ¬sha256 I•r¢†5Äµ   ¿¡Document   messages.getDocumentByHash size   long   ÏÕÙ¬size   I•r¢†5Äµ   ¿¡Document   messages.getDocumentByHash 	mime_type  string ÏÕÙ¬	mime_type  Äµ    Äµ    7—y¼6ò©ý messages.getCustomEmojiDocuments   Äµ    7—y¼¶ MnChatFull   communityFull  Äµ   gb    
chat_photo Photo  Äµ   uÄN    ZÀ;ÞcommunityFull  µur™fileSourceCommunityFullÄµ   I•r¢†5Äµ   ¿¡ChatFull   communityFull  id long   ÏÕÙ¬channelÄµ    Äµ    7—y¼¶ MnMessageAction  messageActionChatEditPhoto Äµ   gb    photo  Photo  Äµ    7—y¼¶ MnMessageAction   messageActionSuggestProfilePhoto   Äµ   gb    photo  Photo  Äµ    7—y¼6ò©ýphotos.getUserPhotos   Äµ    µur™¶ Mnphotos.Photos  photos.photos  Äµ   gb   photos Photo  Äµ    7—y¼¶ Mnphotos.Photos  photos.photosSlice Äµ   gb   photos Photo  Äµ    7—y¼6ò©ýphotos.updateProfilePhoto  Äµ   uÄN    ZÀ;Þphotos.updateProfilePhoto  7—y¼fileSourceUserProfilePhoto Äµ   .ª w¢†5Äµ   ¿¡photos.Photo   photos.updateProfilePhoto  bot	InputUser  ¡w+ ì[p	InputUser  Š�inputUserSelf  Äµ    user_idI•r¢†5Äµ   ¿¡photos.Photo   photos.updateProfilePhoto      photos.Photo   ÏÕÙ¬¿¡photos.Photo   photos.photo   photo  Photo  ÏÕÙ¬¿¡Photo  photo  id long   ÏÕÙ¬max_id Äµ    Äµ    7—y¼¶ Mnphotos.Photo   photos.photo   Äµ   gb    photo  Photo  Äµ    7—y¼6ò©ýphotos.uploadProfilePhoto  Äµ   uÄN    ZÀ;Þphotos.uploadProfilePhoto  7—y¼fileSourceUserProfilePhoto Äµ   .ª w¢†5Äµ   ¿¡photos.Photo   photos.uploadProfilePhoto  bot	InputUser  ¡w+ ì[p	InputUser  Š�inputUserSelf  Äµ    user_idI•r¢†5Äµ   ¿¡photos.Photo   photos.uploadProfilePhoto      photos.Photo   ÏÕÙ¬¿¡photos.Photo   photos.photo   photo  Photo  ÏÕÙ¬¿¡Photo  photo  id long   ÏÕÙ¬max_id Äµ    Äµ    7—y¼6ò©ý photos.uploadContactProfilePhoto   Äµ   uÄN    ZÀ;Þ photos.uploadContactProfilePhoto   7—y¼fileSourceUserProfilePhoto Äµ   .ª w¢†5Äµ   ¿¡photos.Photo    photos.uploadContactProfilePhoto   user_id	InputUser  ÏÕÙ¬user_idI•r¢†5Äµ   ¿¡photos.Photo    photos.uploadContactProfilePhoto       photos.Photo   ÏÕÙ¬¿¡photos.Photo   photos.photo   photo  Photo  ÏÕÙ¬¿¡Photo  photo  id long   ÏÕÙ¬max_id Äµ    Äµ    7—y¼¶ MnChannelAdminLogEventAction %channelAdminLogEventActionChangePhoto  Äµ   gb    
prev_photo Photo  gb    	new_photo  Photo  Äµ    7—y¼¶ MnMessageAction   messageActionRequestedPeerSentMe   Äµ   gb   peers  RequestedPeer  Äµ    7—y¼¶ MnRequestedPeer  requestedPeerUser  Äµ   gb   photo  Photo  Äµ    7—y¼¶ MnRequestedPeer  requestedPeerChat  Äµ   gb   photo  Photo  Äµ    7—y¼¶ MnRequestedPeer  requestedPeerChannel   Äµ   gb   photo  Photo  Äµ    7—y¼Äµ[   ]ù+
InputPhoto 
inputPhoto fileIdPhoto]ù+InputDocument  inputDocument  fileIdDocument ]ù+InputFileLocation  inputDocumentFileLocation  fileIdDocument ]ù+InputFileLocation  inputPhotoFileLocation fileIdPhoto»õD’account.reportProfilePhoto Äµ   gb    photo_id   
InputPhoto »õD’photos.updateProfilePhoto  Äµ   gb    id 
InputPhoto »õD’photos.deletePhotosÄµ   gb   id 
InputPhoto mÅ?
InputMedia inputMediaPhotoÄµ   gb    id 
InputPhoto gb   video  InputDocument  »õD’messages.sendMedia Äµ   gb    media  
InputMedia »õD’messages.editMessage   Äµ   gb   media  
InputMedia gb   rich_message   InputRichMessage   »õD’messages.editInlineBotMessage  Äµ   gb   media  
InputMedia gb   rich_message   InputRichMessage   »õD’messages.saveDraft Äµ   gb   media  
InputMedia gb   rich_message   InputRichMessage   »õD’messages.uploadMedia   Äµ   gb    media  
InputMedia »õD’messages.uploadImportedMedia   Äµ   gb    media  
InputMedia »õD’bots.addPreviewMedia   Äµ   gb    media  
InputMedia »õD’bots.editPreviewMedia  Äµ   gb    media  
InputMedia gb    	new_media  
InputMedia »õD’bots.deletePreviewMediaÄµ   gb   media  
InputMedia »õD’bots.reorderPreviewMedias  Äµ   gb   order  
InputMedia »õD’payments.exportInvoice Äµ   gb    invoice_media  
InputMedia »õD’stories.sendStory  Äµ   gb    media  
InputMedia gb   music  InputDocument  »õD’stories.editStory  Äµ   gb   media  
InputMedia gb   music  InputDocument  »õD’ephemeral.sendMessage  Äµ   gb   media  
InputMedia gb   rich_message   InputRichMessage   »õD’ephemeral.editMessage  Äµ   gb   media  
InputMedia gb   rich_message   InputRichMessage   mÅ?
InputMedia inputMediaInvoice  Äµ   gb   extended_media 
InputMedia mÅ?InputSingleMedia   inputSingleMedia   Äµ   gb    media  
InputMedia »õD’messages.sendMultiMediaÄµ   gb   multi_mediaInputSingleMedia   mÅ?
PollAnswer inputPollAnswerÄµ   gb   media  
InputMedia »õD’messages.addPollAnswer Äµ   gb    answer 
PollAnswer mÅ?
InputMedia inputMediaPoll Äµ   gb   attached_media 
InputMedia gb   solution_media 
InputMedia mÅ?
InputMedia inputMediaPaidMediaÄµ   gb   extended_media 
InputMedia mÅ?
InputMedia inputMediaUploadedDocument Äµ   gb    file   	InputFile  gb   thumb  	InputFile  gb   stickers   InputDocument  gb   video_cover
InputPhoto mÅ?
InputMedia inputMediaDocument Äµ   gb    id InputDocument  gb   video_cover
InputPhoto mÅ?
InputMedia inputMediaDocumentExternal Äµ   gb   video_cover
InputPhoto mÅ?InputChatPhoto inputChatPhoto Äµ   gb    id 
InputPhoto »õD’messages.editChatPhoto Äµ   gb    photo  InputChatPhoto »õD’channels.editPhoto Äµ   gb    photo  InputChatPhoto mÅ?InputBotInlineResult   inputBotInlineResultPhoto  Äµ   gb    photo  
InputPhoto gb    send_message   InputBotInlineMessage  »õD’messages.setInlineBotResults   Äµ   gb   resultsInputBotInlineResult   »õD’!messages.sendWebViewResultMessage  Äµ   gb    result InputBotInlineResult   »õD’"messages.savePreparedInlineMessage Äµ   gb    result InputBotInlineResult   »õD’messages.setBotGuestChatResult Äµ   gb    result InputBotInlineResult   mÅ?InputStickeredMediainputStickeredMediaPhoto   Äµ   gb    id 
InputPhoto »õD’messages.getAttachedStickers   Äµ   gb    media  InputStickeredMediamÅ?InputRichFile  inputRichFilePhoto Äµ   gb    photo  
InputPhoto mÅ?InputRichMessage   inputRichMessageHTML   Äµ   gb   files  InputRichFile  »õD’messages.sendMessage   Äµ   gb   rich_message   InputRichMessage   »õD’messages.translateRichMessage  Äµ   gb   text   InputRichMessage   »õD’!messages.composeRichMessageWithAI  Äµ   gb   text   InputRichMessage   mÅ?SendMessageAction  &inputSendMessageRichMessageDraftAction Äµ   gb    rich_message   InputRichMessage   »õD’messages.setTyping Äµ   gb    action SendMessageAction  mÅ?InputBotInlineMessage   inputBotInlineMessageRichMessage   Äµ   gb    rich_message   InputRichMessage   mÅ?InputBotInlineResult   inputBotInlineResult   Äµ   gb    send_message   InputBotInlineMessage  mÅ?InputBotInlineResult   inputBotInlineResultDocument   Äµ   gb    document   InputDocument  gb    send_message   InputBotInlineMessage  mÅ?InputBotInlineResult   inputBotInlineResultGame   Äµ   gb    send_message   InputBotInlineMessage  mÅ?InputRichMessage   inputRichMessageMarkdown   Äµ   gb   files  InputRichFile  mÅ?InputRichMessage   inputRichMessage   Äµ   gb   photos 
InputPhoto gb   	documents  InputDocument  »õD’account.createThemeÄµ   gb   document   InputDocument  »õD’account.updateThemeÄµ   gb   document   InputDocument  »õD’account.saveRingtone   Äµ   gb    id InputDocument  »õD’account.saveMusic  Äµ   gb    id InputDocument  gb   after_id   InputDocument  »õD’users.getSavedMusicByIDÄµ   gb   	documents  InputDocument  »õD’messages.saveGif   Äµ   gb    id InputDocument  »õD’messages.saveRecentSticker Äµ   gb    id InputDocument  »õD’messages.faveSticker   Äµ   gb    id InputDocument  »õD’messages.reportMusicListen Äµ   gb    id InputDocument  »õD’stickers.createStickerSet  Äµ   gb   thumb  InputDocument  gb   stickers   InputStickerSetItem»õD’stickers.removeStickerFromSet  Äµ   gb    stickerInputDocument  »õD’stickers.changeStickerPosition Äµ   gb    stickerInputDocument  »õD’stickers.setStickerSetThumbÄµ   gb   thumb  InputDocument  »õD’stickers.changeSticker Äµ   gb    stickerInputDocument  »õD’stickers.replaceStickerÄµ   gb    stickerInputDocument  gb    new_stickerInputStickerSetItemmÅ?	InputFile  inputFileStoryDocument Äµ   gb    id InputDocument  »õD’account.uploadWallPaperÄµ   gb    file   	InputFile  »õD’account.uploadThemeÄµ   gb    file   	InputFile  gb   thumb  	InputFile  »õD’account.uploadRingtone Äµ   gb    file   	InputFile  »õD’messages.initHistoryImport Äµ   gb    file   	InputFile  »õD’photos.uploadProfilePhoto  Äµ   gb   file   	InputFile  gb   video  	InputFile  »õD’ photos.uploadContactProfilePhoto   Äµ   gb   file   	InputFile  gb   video  	InputFile  »õD’phone.saveCallLog  Äµ   gb    file   	InputFile  mÅ?
InputMedia inputMediaUploadedPhotoÄµ   gb    file   	InputFile  gb   stickers   InputDocument  gb   video  InputDocument  mÅ?InputChatPhoto inputChatUploadedPhoto Äµ   gb   file   	InputFile  gb   video  	InputFile  mÅ?InputStickeredMediainputStickeredMediaDocumentÄµ   gb    id InputDocument  mÅ?InputWebFileLocation   #inputWebFileAudioAlbumThumbLocationÄµ   gb   document   InputDocument  »õD’upload.getWebFile  Äµ   gb    location   InputWebFileLocation   mÅ?InputStickerSetIteminputStickerSetItemÄµ   gb    document   InputDocument  »õD’stickers.addStickerToSet   Äµ   gb    stickerInputStickerSetItemmÅ?InputBusinessIntro inputBusinessIntro Äµ   gb   stickerInputDocument  »õD’account.updateBusinessIntroÄµ   gb   intro  InputBusinessIntro mÅ?InputRichFile  inputRichFileDocument  Äµ   gb    document   InputDocument  »õD’upload.getFile Äµ   gb    location   InputFileLocation  »õD’upload.getFileHashes   Äµ   gb    location   InputFileLocation  Äµ   ‰VáÔfileSourceMessage  ®Â¤�ì[p	InputPeer  P7�peer   ì[pint�A�ôid ì[pint�A�ôquick_reply_shortcut_id‰VáÔfileSourceScheduledMessage ƒ3ÿÂmessages.getScheduledMessages  Äµ   Â0):peer   ì[p	InputPeer  P7�peer   Â0):id ì[pVector<int>r�ûøÄµ   ì[pint�A�ôid ‰VáÔfileSourceStoryƒ3ÿÂstories.getStoriesByID Äµ   Â0):id ì[pVector<int>r�ûøÄµ   ì[pint�A�ôid Â0):peer   ì[p	InputPeer  P7�peer   ‰VáÔfileSourceWebPage  ƒ3ÿÂmessages.getWebPageÄµ   Â0):urlì[pstring �A�ôurlÂ0):hash   ì[pint|¾úË    ‰VáÔfileSourceBotApp   ƒ3ÿÂmessages.getBotApp Äµ   Â0):appì[pInputBotAppŠ�inputBotAppID  Äµ   Â0):id ì[plong   �A�ôid Â0):access_hashì[plong   �A�ôaccess_hashÂ0):hash   ì[plong   :�‹Ð        ‰VáÔfileSourceUserFull ƒ3ÿÂusers.getFullUser  Äµ   Â0):id ì[p	InputUser  &CîÀid ‰VáÔfileSourceAdminLog ƒ3ÿÂchannels.getAdminLog   Äµ   Â0):channelì[pInputChannel   1u´<channelÂ0):max_id ì[plong   �A�ômax_id Â0):min_id ì[plong   �A�ômax_id Â0):limit  ì[pint|¾úË   Â0):q  ì[pstring ŽêV+    ‰VáÔfileSourceStoryAlbum   ƒ3ÿÂstories.getAlbums  Äµ   Â0):peer   ì[p	InputPeer  P7�peer   Â0):hash   ì[plong   :�‹Ð        ‰VáÔfileSourceBotPreviewMedia  ƒ3ÿÂbots.getPreviewMedias  Äµ   Â0):botì[p	InputUser  &CîÀbot‰VáÔfileSourceBotPreviewInfo   ƒ3ÿÂbots.getPreviewInfoÄµ   Â0):botì[p	InputUser  &CîÀbotÂ0):	lang_code  ì[pstring �A�ô	lang_code  ‰VáÔfileSourcePaidMediaƒ3ÿÂmessages.getExtendedMedia  Äµ   Â0):id ì[pVector<int>r�ûøÄµ   ì[pint�A�ôid Â0):peer   ì[p	InputPeer  P7�peer   ‰VáÔfileSourceSavedMusic   ƒ3ÿÂusers.getSavedMusicByIDÄµ   Â0):id ì[p	InputUser  &CîÀuser_idÂ0):	documents  ì[pVector<InputDocument>  r�ûøÄµ   ì[pInputDocument  Š�inputDocument  Äµ   Â0):id ì[plong   �A�ôid Â0):access_hashì[plong   �A�ôaccess_hashÂ0):file_reference ì[pbytes  ¤•³ý    ‰VáÔfileSourceChatFull ƒ3ÿÂmessages.getFullChat   Äµ   Â0):chat_idì[plong   �A�ôchat_id‰VáÔfileSourceChannelFull  ƒ3ÿÂchannels.getFullChannelÄµ   Â0):channelì[pInputChannel   1u´<channel‰VáÔfileSourceCommunityFullƒ3ÿÂchannels.getFullChannelÄµ   Â0):channelì[pInputChannel   1u´<channel‰VáÔfileSourcePremiumPromo ƒ3ÿÂhelp.getPremiumPromo   Äµ    ‰VáÔfileSourceStarsTransaction ƒ3ÿÂ!payments.getStarsTransactionsByID  Äµ   Â0):peer   ì[p	InputPeer  P7�peer   Â0):tonì[ptrue   �A�ôtonÂ0):id ì[pVector<InputStarsTransaction>  r�ûøÄµ   ì[pInputStarsTransaction  Š�inputStarsTransaction  Äµ   Â0):id ì[pstring �A�ôid Â0):refund ì[ptrue   �A�ôrefund ‰VáÔfileSourceAttachMenuBotƒ3ÿÂmessages.getAttachMenuBot  Äµ   Â0):botì[p	InputUser  &CîÀbot‰VáÔfileSourceThemeƒ3ÿÂaccount.getTheme   Äµ   Â0):theme  ì[p
InputTheme Š�
inputTheme Äµ   Â0):id ì[plong   �A�ôid Â0):access_hashì[plong   �A�ôaccess_hashÂ0):format ì[pstring ’OŽ‰VáÔfileSourceWallPaperƒ3ÿÂaccount.getWallPaper   Äµ   Â0):	wallpaper  ì[pInputWallPaper Š�inputWallPaper Äµ   Â0):id ì[plong   �A�ôid Â0):access_hashì[plong   �A�ôaccess_hash‰VáÔfileSourceStickerSet   ƒ3ÿÂmessages.getStickerSet Äµ   Â0):
stickerset ì[pInputStickerSet�A�ô
stickerset Â0):hash   ì[pint|¾úË    ‰VáÔfileSourceSavedGifsƒ3ÿÂmessages.getSavedGifs  Äµ   Â0):hash   ì[plong   :�‹Ð        ‰VáÔfileSourceSavedRingtones   ƒ3ÿÂaccount.getSavedRingtones  Äµ   Â0):hash   ì[plong   :�‹Ð        ‰VáÔfileSourceAvailableEffects ƒ3ÿÂmessages.getAvailableEffects   Äµ   Â0):hash   ì[pint|¾úË    ‰VáÔfileSourceAvailableReactions   ƒ3ÿÂmessages.getAvailableReactions Äµ   Â0):hash   ì[pint|¾úË    ‰VáÔfileSourceUserProfilePhoto ƒ3ÿÂphotos.getUserPhotos   Äµ   Â0):user_idì[p	InputUser  &CîÀuser_idÂ0):offset ì[pint|¾úËÿÿÿÿÂ0):max_id ì[plong   �A�ômax_id Â0):limit  ì[pint|¾úË   ‰VáÔfileSourceDocumentByHash   ƒ3ÿÂmessages.getDocumentByHash Äµ   Â0):sha256 ì[pbytes  �A�ôsha256 Â0):size   ì[plong   �A�ôsize   Â0):	mime_type  ì[pstring �A�ô	mime_type  ‰VáÔfileSourceWelcomeMessages  ƒ3ÿÂephemeral.getWelcomeMessages   Äµ   Â0):peer   ì[p	InputPeer  P7�peer   Â0):hash   ì[plong   :�‹Ð        Äµ   o?T+ZÀ;Þmessages.getSponsoredMessages  7—y¼4Do not store file references from sponsored messages   o?T+ZÀ;Þhelp.getAppUpdate  7—y¼;Don't handle file references from ephemeral app update infoo?T+ZÀ;Þhelp.getRecentMeUrls   7—y¼2Don't handle file references from recent t.me URLs o?T+ZÀ;ÞdraftMessage   µur™#Do not store references from draftso?T+ZÀ;ÞrecentMeUrlChatInvite  µur™2Do not store references based on chat invite links o?T+ZÀ;Þmessages.checkChatInvite   7—y¼2Do not store references based on chat invite links o?T+ZÀ;Þmessages.getInlineBotResults   7—y¼ Inline bot results are ephemeral   o?T+ZÀ;Þ!messages.getPreparedInlineMessage  7—y¼ Inline bot results are ephemeral   o?T+ZÀ;Þmessages.uploadMedia   7—y¼RA freshly uploaded media file will obtain a context only once it is sent to a chat o?T+ZÀ;Þmessages.uploadImportedMedia   7—y¼RA freshly uploaded media file will obtain a context only once it is sent to a chat o?T+ZÀ;ÞupdateServiceNotification  µur™$Cannot refetch service notifications   o?T+ZÀ;Þmessages.getWebPagePreview 7—y¼¯No locations are added for the method call, as it doesn't use persistent IDs as input; the location is instead extracted from the persistent IDs in the returned WebPage objecto?T+ZÀ;Þpayments.resaleStarGifts   µur™/Contexts for star gifts are not yet implementedo?T+ZÀ;Þpayments.starGiftUpgradePreviewµur™/Contexts for star gifts are not yet implementedo?T+ZÀ;ÞstarGift   µur™/Contexts for star gifts are not yet implementedo?T+ZÀ;ÞstarGiftUnique µur™/Contexts for star gifts are not yet implementedo?T+ZÀ;ÞstarGiftCollection µur™/Contexts for star gifts are not yet implementedo?T+ZÀ;Þpayments.starGiftCollections   µur™/Contexts for star gifts are not yet implementedo?T+ZÀ;Þ"payments.starGiftUpgradeAttributes µur™/Contexts for star gifts are not yet implementedo?T+ZÀ;ÞupdateEditEphemeralMessage µur™4Do not store file references from ephemeral messages   o?T+ZÀ;ÞupdateEphemeralBotCallbackQueryµur™4Do not store file references from ephemeral messages   o?T+ZÀ;ÞupdateNewEphemeralMessage  µur™4Do not store file references from ephemeral messages   o?T+ZÀ;Þmessages.translatedRichMessage µur™:Do not store file references from translated rich messages o?T+ZÀ;Þ"messages.composedRichMessageWithAI µur™8Do not store file references from composed rich messages   o?T+ZÀ;ÞupdateChatUserTyping   µur™DDocuments encountered in rich text message live drafts are ephemeral   o?T+ZÀ;ÞupdateChannelUserTypingµur™DDocuments encountered in rich text message live drafts are ephemeral   o?T+ZÀ;ÞupdateUserTyping   µur™DDocuments encountered in rich text message live drafts are ephemeral   o?T+ZÀ;Þ messages.getCustomEmojiDocuments   7—y¼,Do not store file references in this context   o?T+ZÀ;Þaccount.uploadTheme7—y¼cA freshly uploaded theme file will obtain a context only once it is created via account.createTheme<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

/**
 * @psalm-external-mutation-free
 */
final class SettingsEmpty extends SettingsAbstract
{
}
{
    "constructors": [
        {
            "id": "-1132882121",
            "predicate": "boolFalse",
            "params": [],
            "type": "Bool"
        },
        {
            "id": "-1720552011",
            "predicate": "boolTrue",
            "params": [],
            "type": "Bool"
        },
        {
            "id": "1072550713",
            "predicate": "true",
            "params": [],
            "type": "True"
        },
        {
            "id": "481674261",
            "predicate": "vector",
            "params": [],
            "type": "Vector t"
        },
        {
            "id": "1923715664",
            "predicate": "fileReferenceMap",
            "params": [
                {
                    "name": "layer",
                    "type": "int"
                },
                {
                    "name": "db_schema",
                    "type": "string"
                },
                {
                    "name": "db_schema_json",
                    "type": "string"
                },
                {
                    "name": "traversers_incoming",
                    "type": "Vector<TraverserIncoming>"
                },
                {
                    "name": "traversers_outgoing",
                    "type": "Vector<TraverserOutgoing>"
                },
                {
                    "name": "refresh_actions",
                    "type": "Vector<RefreshAction>"
                },
                {
                    "name": "skipped_incoming_sources",
                    "type": "Vector<SkippedSource>"
                }
            ],
            "type": "FileReferenceMap"
        },
        {
            "id": "1645615975",
            "predicate": "traverseParam",
            "params": [
                {
                    "name": "flags",
                    "type": "#"
                },
                {
                    "name": "name",
                    "type": "string"
                },
                {
                    "name": "is_flag",
                    "type": "flags.0?true"
                },
                {
                    "name": "is_vector",
                    "type": "flags.1?true"
                },
                {
                    "name": "type",
                    "type": "string"
                }
            ],
            "type": "TraverseParam"
        },
        {
            "id": "-39194058",
            "predicate": "traverseMethodResult",
            "params": [
                {
                    "name": "name",
                    "type": "string"
                },
                {
                    "name": "push_sources",
                    "type": "Vector<Source>"
                },
                {
                    "name": "is_needed_parent",
                    "type": "Bool"
                }
            ],
            "type": "TraverserIncoming"
        },
        {
            "id": "1850540214",
            "predicate": "traverseIncomingConstructor",
            "params": [
                {
                    "name": "type",
                    "type": "string"
                },
                {
                    "name": "predicate",
                    "type": "string"
                },
                {
                    "name": "params",
                    "type": "Vector<TraverseParam>"
                },
                {
                    "name": "push_sources",
                    "type": "Vector<Source>"
                },
                {
                    "name": "is_needed_parent",
                    "type": "Bool"
                }
            ],
            "type": "TraverserIncoming"
        },
        {
            "id": "-1798547003",
            "predicate": "traverseCommitSourceLocation",
            "params": [
                {
                    "name": "type",
                    "type": "string"
                },
                {
                    "name": "predicate",
                    "type": "string"
                },
                {
                    "name": "push_sources",
                    "type": "Vector<Source>"
                },
                {
                    "name": "stored_constructor",
                    "type": "string"
                }
            ],
            "type": "TraverserIncoming"
        },
        {
            "id": "-566509478",
            "predicate": "predicate",
            "params": [
                {
                    "name": "predicate",
                    "type": "string"
                },
                {
                    "name": "is_constructor",
                    "type": "Bool"
                }
            ],
            "type": "Predicate"
        },
        {
            "id": "1321497882",
            "predicate": "source",
            "params": [
                {
                    "name": "flags",
                    "type": "#"
                },
                {
                    "name": "predicate",
                    "type": "Predicate"
                },
                {
                    "name": "stored_constructor",
                    "type": "string"
                },
                {
                    "name": "stored_params",
                    "type": "Vector<FieldExtractor>"
                },
                {
                    "name": "skipped_flags",
                    "type": "Vector<string>"
                },
                {
                    "name": "needs_parent",
                    "type": "flags.3?Predicate"
                },
                {
                    "name": "needs_not_parents",
                    "type": "Vector<Predicate>"
                }
            ],
            "type": "Source"
        },
        {
            "id": "1069875821",
            "predicate": "traverseOutgoingConstructor",
            "params": [
                {
                    "name": "type",
                    "type": "string"
                },
                {
                    "name": "predicate",
                    "type": "string"
                },
                {
                    "name": "params",
                    "type": "Vector<TraverseParam>"
                }
            ],
            "type": "TraverserOutgoing"
        },
        {
            "id": "53213533",
            "predicate": "traverseSwapLocation",
            "params": [
                {
                    "name": "type",
                    "type": "string"
                },
                {
                    "name": "predicate",
                    "type": "string"
                },
                {
                    "name": "stored_constructor",
                    "type": "string"
                }
            ],
            "type": "TraverserOutgoing"
        },
        {
            "id": "-1840974405",
            "predicate": "traverseMethodCall",
            "params": [
                {
                    "name": "name",
                    "type": "string"
                },
                {
                    "name": "params",
                    "type": "Vector<TraverseParam>"
                }
            ],
            "type": "TraverserOutgoing"
        },
        {
            "id": "726941551",
            "predicate": "skippedSource",
            "params": [
                {
                    "name": "predicate",
                    "type": "Predicate"
                },
                {
                    "name": "why",
                    "type": "string"
                }
            ],
            "type": "SkippedSource"
        },
        {
            "id": "-723429751",
            "predicate": "refreshAction",
            "params": [
                {
                    "name": "stored_constructor",
                    "type": "string"
                },
                {
                    "name": "action",
                    "type": "ActionOp"
                }
            ],
            "type": "RefreshAction"
        },
        {
            "id": "-1395010097",
            "predicate": "paramNotFlag",
            "params": [],
            "type": "ParamFlag"
        },
        {
            "id": "-117530642",
            "predicate": "paramIsFlagAbortIfEmpty",
            "params": [],
            "type": "ParamFlag"
        },
        {
            "id": "539719585",
            "predicate": "paramIsFlagFallback",
            "params": [
                {
                    "name": "fallback",
                    "type": "TypedOp"
                }
            ],
            "type": "ParamFlag"
        },
        {
            "id": "499573117",
            "predicate": "paramIsFlagPassthrough",
            "params": [],
            "type": "ParamFlag"
        },
        {
            "id": "429985727",
            "predicate": "pathPart",
            "params": [
                {
                    "name": "type",
                    "type": "string"
                },
                {
                    "name": "constructor",
                    "type": "string"
                },
                {
                    "name": "param",
                    "type": "string"
                },
                {
                    "name": "param_type",
                    "type": "string"
                },
                {
                    "name": "flag",
                    "type": "ParamFlag"
                }
            ],
            "type": "PathPart"
        },
        {
            "id": "204834466",
            "predicate": "path",
            "params": [
                {
                    "name": "parts",
                    "type": "Vector<PathPart>"
                }
            ],
            "type": "Path"
        },
        {
            "id": "1492203140",
            "predicate": "pathParent",
            "params": [
                {
                    "name": "parts",
                    "type": "Vector<PathPart>"
                }
            ],
            "type": "Path"
        },
        {
            "id": "1913034057",
            "predicate": "extractAndStore",
            "params": [
                {
                    "name": "from",
                    "type": "Path"
                },
                {
                    "name": "to",
                    "type": "string"
                }
            ],
            "type": "FieldExtractor"
        },
        {
            "id": "916294932",
            "predicate": "extractInputStickerSetFromDocumentAttributesAndStore",
            "params": [
                {
                    "name": "from",
                    "type": "Path"
                },
                {
                    "name": "to",
                    "type": "string"
                }
            ],
            "type": "FieldExtractor"
        },
        {
            "id": "-1050160016",
            "predicate": "extractInputStickerSetFromStickerSetAndStore",
            "params": [
                {
                    "name": "from",
                    "type": "Path"
                },
                {
                    "name": "to",
                    "type": "string"
                }
            ],
            "type": "FieldExtractor"
        },
        {
            "id": "2100494748",
            "predicate": "extractPeerIdFromPeerAndStore",
            "params": [
                {
                    "name": "from",
                    "type": "Path"
                },
                {
                    "name": "to",
                    "type": "string"
                }
            ],
            "type": "FieldExtractor"
        },
        {
            "id": "-1524969548",
            "predicate": "extractPeerIdFromInputPeerAndStore",
            "params": [
                {
                    "name": "from",
                    "type": "Path"
                },
                {
                    "name": "to",
                    "type": "string"
                }
            ],
            "type": "FieldExtractor"
        },
        {
            "id": "1450556567",
            "predicate": "extractChannelIdFromChannelAndStore",
            "params": [
                {
                    "name": "from",
                    "type": "Path"
                },
                {
                    "name": "to",
                    "type": "string"
                }
            ],
            "type": "FieldExtractor"
        },
        {
            "id": "-1235065330",
            "predicate": "extractChannelIdFromInputChannelAndStore",
            "params": [
                {
                    "name": "from",
                    "type": "Path"
                },
                {
                    "name": "to",
                    "type": "string"
                }
            ],
            "type": "FieldExtractor"
        },
        {
            "id": "1199107171",
            "predicate": "extractUserIdFromUserAndStore",
            "params": [
                {
                    "name": "from",
                    "type": "Path"
                },
                {
                    "name": "to",
                    "type": "string"
                }
            ],
            "type": "FieldExtractor"
        },
        {
            "id": "1998629422",
            "predicate": "extractUserIdFromInputUserAndStore",
            "params": [
                {
                    "name": "from",
                    "type": "Path"
                },
                {
                    "name": "to",
                    "type": "string"
                }
            ],
            "type": "FieldExtractor"
        },
        {
            "id": "-1023462525",
            "predicate": "callOp",
            "params": [
                {
                    "name": "method",
                    "type": "string"
                },
                {
                    "name": "args",
                    "type": "Vector<TypedOpArg>"
                }
            ],
            "type": "ActionOp"
        },
        {
            "id": "-1918582098",
            "predicate": "getMessageOp",
            "params": [
                {
                    "name": "peer",
                    "type": "TypedOp"
                },
                {
                    "name": "id",
                    "type": "TypedOp"
                },
                {
                    "name": "quick_reply_shortcut_id",
                    "type": "TypedOp"
                }
            ],
            "type": "ActionOp"
        },
        {
            "id": "975777986",
            "predicate": "typedOpArg",
            "params": [
                {
                    "name": "key",
                    "type": "string"
                },
                {
                    "name": "value",
                    "type": "TypedOp"
                }
            ],
            "type": "TypedOpArg"
        },
        {
            "id": "1885016300",
            "predicate": "typedOp",
            "params": [
                {
                    "name": "type",
                    "type": "string"
                },
                {
                    "name": "op",
                    "type": "TypedOpOp"
                }
            ],
            "type": "TypedOp"
        },
        {
            "id": "-191938161",
            "predicate": "copyOp",
            "params": [
                {
                    "name": "from",
                    "type": "string"
                }
            ],
            "type": "TypedOpOp"
        },
        {
            "id": "1018459441",
            "predicate": "getInputChannelByIdOp",
            "params": [
                {
                    "name": "from",
                    "type": "string"
                }
            ],
            "type": "TypedOpOp"
        },
        {
            "id": "-1058127066",
            "predicate": "getInputUserByIdOp",
            "params": [
                {
                    "name": "from",
                    "type": "string"
                }
            ],
            "type": "TypedOpOp"
        },
        {
            "id": "427898704",
            "predicate": "getInputPeerByIdOp",
            "params": [
                {
                    "name": "from",
                    "type": "string"
                }
            ],
            "type": "TypedOpOp"
        },
        {
            "id": "276794762",
            "predicate": "constructorOp",
            "params": [
                {
                    "name": "constructor",
                    "type": "string"
                },
                {
                    "name": "args",
                    "type": "Vector<TypedOpArg>"
                }
            ],
            "type": "TypedOpOp"
        },
        {
            "id": "-117731470",
            "predicate": "vectorOp",
            "params": [
                {
                    "name": "values",
                    "type": "Vector<TypedOp>"
                }
            ],
            "type": "TypedOpOp"
        },
        {
            "id": "-872759684",
            "predicate": "intLiteralOp",
            "params": [
                {
                    "name": "value",
                    "type": "int"
                }
            ],
            "type": "TypedOpOp"
        },
        {
            "id": "-796160710",
            "predicate": "longLiteralOp",
            "params": [
                {
                    "name": "value",
                    "type": "long"
                }
            ],
            "type": "TypedOpOp"
        },
        {
            "id": "727116430",
            "predicate": "stringLiteralOp",
            "params": [
                {
                    "name": "value",
                    "type": "string"
                }
            ],
            "type": "TypedOpOp"
        },
        {
            "id": "-38562396",
            "predicate": "bytesLiteralOp",
            "params": [
                {
                    "name": "value",
                    "type": "bytes"
                }
            ],
            "type": "TypedOpOp"
        },
        {
            "id": "937457937",
            "predicate": "boolLiteralOp",
            "params": [
                {
                    "name": "value",
                    "type": "Bool"
                }
            ],
            "type": "TypedOpOp"
        },
        {
            "id": "911336383",
            "predicate": "doubleLiteralOp",
            "params": [
                {
                    "name": "value",
                    "type": "double"
                }
            ],
            "type": "TypedOpOp"
        },
        {
            "id": "-1907387896",
            "predicate": "themeFormatLiteralOp",
            "params": [],
            "type": "TypedOpOp"
        }
    ],
    "methods": []
}<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Calls;

/**
 * A participant of an end-to-end encrypted {@see ConferenceCall}, as reconstructed from the
 * [shared-state chain Â»](https://core.telegram.org/api/end-to-end/group-calls).
 */
final class ConferenceCallParticipant extends MultiCallParticipant
{
    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(
        int $peerId,
        /** The participant's Ed25519 public key. */
        public readonly string $publicKey,
        /** The participant's permission bits in the shared-state chain. */
        public readonly int $permissions,
        /** The protocol version of the shared-state block that last updated this participant. */
        public readonly int $version,
    ) {
        parent::__construct($peerId);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Calls;

use Amp\ByteStream\ReadableStream;
use Amp\ByteStream\WritableStream;
use danog\MadelineProto\CallStream;
use danog\MadelineProto\EventHandler\Call;
use danog\MadelineProto\EventHandler\SimpleFilters;
use danog\MadelineProto\EventHandler\Update;
use danog\MadelineProto\LocalDirectory;
use danog\MadelineProto\LocalFile;
use danog\MadelineProto\MediaDestination;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\RecordingFormat;
use danog\MadelineProto\RemoteUrl;
use danog\MadelineProto\VoIP\CallState;
use danog\MadelineProto\VoIP\DiscardReason;
use danog\MadelineProto\VoIP\MediaState;
use InvalidArgumentException;

/**
 * This update represents a private (one-to-one) VoIP Telegram call.
 *
 * The old name {@see \danog\MadelineProto\VoIP} is kept as an alias for backwards compatibility.
 *
 * @psalm-import-type StreamMask from \danog\MadelineProto\CallStream
 */
final class PrivateCall extends Update implements SimpleFilters, Call
{
    /** Phone call ID */
    public readonly int $callID;
    /** Whether the call is an outgoing call */
    public readonly bool $outgoing;
    /** Whether this is a video call. */
    public bool $video = false;
    /** ID of the other user in the call */
    public readonly int $otherID;
    /** When was the call created */
    public readonly int $date;
    /** Why the call was discarded, if it was. */
    public ?DiscardReason $discardReason = null;
    /**
     * If the call was upgraded to a [conference call Â»](https://core.telegram.org/api/group-calls#conference-calls),
     * the [conference deep link Â»](https://core.telegram.org/api/links#conference-links) slug of the new conference,
     * which can be joined with {@see \danog\MadelineProto\API::joinConferenceCallBySlug()}.
     */
    public ?string $conferenceSlug = null;

    /**
     * Constructor.
     *
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(
        MTProto $API,
        array $call
    ) {
        parent::__construct($API);
        $call['_'] = 'inputPhoneCall';
        $this->date = $call['date'];
        $this->callID = $call['id'];
        $this->video = $call['video'] ?? false;
        if ($call['admin_id'] === $API->getSelf()['id']) {
            $this->outgoing = true;
            $this->otherID = $call['participant_id'];
        } else {
            $this->outgoing = false;
            $this->otherID = $call['admin_id'];
        }
    }

    /**
     * Accept the incoming call (for an outgoing call, which we are already in, only applies `$muted`).
     *
     * @param bool $muted Whether to accept with our own audio muted.
     */
    #[\Override]
    public function join(bool $muted = false): static
    {
        $this->getClient()->acceptCall($this->callID);
        if ($muted) {
            $this->getClient()->setCallMuted($this->callID, true);
        }
        return $this;
    }
    /**
     * Discard call.
     *
     * @param int<1, 5> $rating  Call rating in stars
     * @param string    $comment Additional comment on call quality.
     */
    #[\Override]
    public function discard(DiscardReason $reason = DiscardReason::HANGUP, ?int $rating = null, ?string $comment = null): static
    {
        $this->getClient()->discardCall($this->callID, $reason, $rating, $comment);
        return $this;
    }

    /**
     * Whether the call is running (accepted by both parties and connected).
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function isJoined(): bool
    {
        return $this->getCallState() === CallState::RUNNING;
    }

    /**
     * Get call state.
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function getCallState(): CallState
    {
        return $this->getClient()->getCallState($this->callID) ?? CallState::ENDED;
    }

    /**
     * The other party of the call, keyed by their user ID, with their media state (mute, camera and
     * screencast status) as reported by their client.
     *
     * Empty until the call is connected and the other party has reported their media state.
     *
     * @psalm-mutation-free
     */
    public function getRemoteMediaState(): ?MediaState
    {
        return $this->getClient()->getCallRemoteMediaState($this->callID);
    }

    /**
     * Get the key verification emojis (will return null if the call is not inited yet).
     *
     * @return ?list{string, string, string, string}
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function getVisualization(): ?array
    {
        return $this->getClient()->getCallVisualization($this->callID);
    }

    /**
     * Play a file, transmitting its audio and, if it carries a transmittable one, its video.
     *
     * A WebM/Matroska file with VP8, VP9, H.264, H.265 or AV1 video (if the other party's app supports
     * it) has its video transmitted too; any other
     * file (or a raw audio stream) is played as audio only. The file is demuxed in pure PHP and its
     * frames are sent as-is where possible, so no transcoding (and thus no FFI extension) is
     * required for pre-encoded WebM/OGG-OPUS input.
     */
    #[\Override]
    public function play(LocalFile|RemoteUrl|ReadableStream $file, MediaDestination $dest = MediaDestination::Camera): static
    {
        $this->getClient()->callPlay($this->callID, $file, $dest);

        return $this;
    }

    /**
     * Play a file, blocking until it has finished playing if a stream is provided.
     */
    #[\Override]
    public function playBlocking(LocalFile|RemoteUrl|ReadableStream $file, MediaDestination $dest = MediaDestination::Camera): static
    {
        $this->getClient()->callPlayBlocking($this->callID, $file, $dest);

        return $this;
    }

    /**
     * Record the other party into one file (or stream) with a fixed set of tracks.
     *
     * The file holds the streams chosen with `$streams` â€” a bitmask of {@see CallStream::AUDIO},
     * {@see CallStream::VIDEO} and {@see CallStream::SCREEN}, every one of which must be available â€” or,
     * when null, every stream the other party currently sends; the available streams are returned. The
     * tracks are fixed for the whole file: a stream turned off stops being written and resumes when it
     * comes back, and only a change of codec or the end of the call finishes the file (see
     * {@see Call::setOutput()}); every such event is reported by a {@see CallStreams} update.
     * `$participant` may only be the other party, and is therefore optional.
     *
     * A {@see RecordingFormat::Webm} or {@see RecordingFormat::Mkv} target records the chosen streams,
     * muxed into a Matroska file in pure PHP (the peer's frames are stored as-is, so the video tracks are
     * whatever codec the peer sends â€” VP8/VP9/H.264/H.265/AV1 â€” and the audio is OPUS).
     * {@see RecordingFormat::Opus} writes an audio-only OGG OPUS stream (so only {@see CallStream::AUDIO}
     * may be chosen); it is the only format the legacy libtgvoip engine, negotiated by very old clients,
     * supports. When `$format` is null it is autodetected from the extension of `$file`, but only if a
     * {@see LocalFile} was passed; a raw stream, whose extension is unknown, defaults to WebM.
     *
     * Before the call is connected (the other party has not reported what it sends yet) any set of
     * streams is accepted, the recording starts with the chosen ones once media flows, and 0 is returned.
     *
     * @param StreamMask|null $streams The streams to record, as a bitmask of {@see CallStream} flags, or null for every available one.
     *
     * @throws InvalidArgumentException If a chosen stream is not available, or nothing is.
     *
     * @return StreamMask The streams the other party currently sends, as a bitmask of {@see CallStream} flags.
     */
    #[\Override]
    public function setOutput(LocalFile|WritableStream $file, string|int|null $participant = null, ?RecordingFormat $format = null, ?int $streams = null): int
    {
        if ($participant !== null && $this->getClient()->getId($participant) !== $this->otherID) {
            throw new InvalidArgumentException("Only the other party ({$this->otherID}) of a one-to-one call can be recorded.");
        }
        return $this->getClient()->callSetOutput($this->callID, $file, $format, $streams);
    }

    /**
     * Record the other party into a directory, as `<dir>/0_<streams>.mkv`, `<dir>/1_<streams>.mkv`, â€¦:
     * one file per combination of streams (audio, camera, screencast) they send, a new one each time
     * they turn one on or off (see {@see Call::setOutputFolder()}). `$participant` may only be the
     * other party, and is therefore optional.
     */
    #[\Override]
    public function setOutputFolder(LocalDirectory $dir, string|int|null $participant = null, ?RecordingFormat $format = null): static
    {
        if ($participant !== null && $this->getClient()->getId($participant) !== $this->otherID) {
            throw new InvalidArgumentException("Only the other party ({$this->otherID}) of a one-to-one call can be recorded.");
        }
        $this->getClient()->callSetOutputFolder($this->callID, $dir, $format);

        return $this;
    }

    /**
     * Play file.
     */
    #[\Override]
    public function then(LocalFile|RemoteUrl|ReadableStream $file, MediaDestination $dest = MediaDestination::Camera): static
    {
        $this->getClient()->callPlay($this->callID, $file, $dest);

        return $this;
    }
    /**
     * When called, skips to the next file in the playlist.
     */
    #[\Override]
    public function skip(MediaDestination $dest = MediaDestination::Camera): static
    {
        $this->getClient()->skipPlay($this->callID, $dest);

        return $this;
    }
    /**
     * Stops playing all files, clears the main and the hold playlist.
     */
    #[\Override]
    public function stop(MediaDestination $dest = MediaDestination::Camera): static
    {
        $this->getClient()->stopPlay($this->callID, $dest);

        return $this;
    }

    /**
     * Pauses the currently playing file.
     *
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function pause(MediaDestination $dest = MediaDestination::Camera): static
    {
        $this->getClient()->pausePlay($this->callID, $dest);

        return $this;
    }

    /**
     * Whether the currently playing file is paused.
     *
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function isPaused(MediaDestination $dest = MediaDestination::Camera): bool
    {
        return $this->getClient()->isPlayPaused($this->callID, $dest);
    }

    /**
     * Resumes the currently playing file.
     *
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function resume(MediaDestination $dest = MediaDestination::Camera): static
    {
        $this->getClient()->resumePlay($this->callID, $dest);

        return $this;
    }

    /**
     * Files to play on hold.
     */
    #[\Override]
    public function playOnHold(MediaDestination $dest = MediaDestination::Camera, LocalFile|RemoteUrl|ReadableStream ...$files): static
    {
        $this->getClient()->callPlayOnHold($this->callID, $dest, ...$files);

        return $this;
    }

    /**
     * Get the file that is currently being played.
     *
     * Will return a string with the object ID of the stream if we're currently playing a stream, otherwise returns the related LocalFile or RemoteUrl.
     *
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function getCurrent(MediaDestination $dest = MediaDestination::Camera): RemoteUrl|LocalFile|string|null
    {
        return $this->getClient()->callGetCurrent($this->callID, $dest);
    }

    /**
     * Mute or unmute our own audio stream.
     */
    #[\Override]
    public function setMuted(bool $muted = true): static
    {
        $this->getClient()->setCallMuted($this->callID, $muted);

        return $this;
    }

    /**
     * Whether our own audio stream is muted.
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function isMuted(): bool
    {
        return $this->getClient()->isCallMuted($this->callID);
    }

    /**
     * Start sharing a screen: bring up the presentation (screencast) video stream, so that files played
     * on {@see MediaDestination::Presentation} are transmitted as a screencast. The other party is told
     * the screencast is active as soon as a file with video plays on it.
     */
    #[\Override]
    public function enablePresentation(): static
    {
        $this->getClient()->enableCallPresentation($this->callID);

        return $this;
    }

    /**
     * Stop sharing the screen: stop the presentation playlist and tell the other party the screencast
     * is inactive.
     */
    #[\Override]
    public function disablePresentation(): static
    {
        $this->getClient()->disableCallPresentation($this->callID);

        return $this;
    }

    /**
     * Whether a screencast is currently being transmitted.
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function isSharingScreen(): bool
    {
        return $this->getClient()->isCallSharingScreen($this->callID);
    }

    /**
     * Get call representation.
     *
     * @psalm-mutation-free
     */
    public function __toString(): string
    {
        return "call {$this->callID} with {$this->otherID}";
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Calls;

use Amp\ByteStream\ReadableStream;
use Amp\ByteStream\WritableStream;
use danog\MadelineProto\EventHandler\MultiCall;
use danog\MadelineProto\EventHandler\Update;
use danog\MadelineProto\LocalDirectory;
use danog\MadelineProto\LocalFile;
use danog\MadelineProto\MediaDestination;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\ParseMode;
use danog\MadelineProto\RecordingFormat;
use danog\MadelineProto\RemoteUrl;

/**
 * What a [video chat/livestream Â»](https://core.telegram.org/api/group-calls#video-chats-livestreams)
 * ({@see GroupCall}) and a [live story Â»](https://core.telegram.org/api/group-calls#live-stories)
 * ({@see LiveStory}) have in common: both are group calls the server mixes (not end-to-end encrypted),
 * with the same joining, participants, playback, recording, in-call messages and settings.
 *
 * See https://core.telegram.org/api/group-calls for more info.
 *
 * @template TParticipant of AbstractGroupCallParticipant
 *
 * @implements MultiCall<TParticipant>
 *
 * @psalm-import-type StreamMask from \danog\MadelineProto\CallStream
 */
abstract class AbstractGroupCall extends Update implements MultiCall
{
    /** Group call ID. */
    public readonly int $id;
    /** Access hash of the group call. */
    public readonly int $accessHash;
    /** Bot API ID of the peer the call belongs to (the group or channel of a video chat, the poster of a live story), if known. */
    public readonly ?int $peerId;

    /** Number of participants. */
    public int $participantsCount = 0;
    /** Whether new participants join muted. */
    public bool $joinMuted = false;
    /** Whether we may change whether new participants join muted (admins). */
    public bool $canChangeJoinMuted = false;
    /** Whether participants are sorted by join date (ascending) rather than by activity. */
    public bool $joinDateAsc = false;
    /** Whether we may start streaming video (the video limit is not reached yet). */
    public bool $canStartVideo = false;
    /** Whether the media is published by an external RTMP application rather than by a participant. */
    public bool $rtmpStream = false;
    /** Whether the listeners are hidden. */
    public bool $listenersHidden = false;
    /** Whether we created this call. */
    public bool $creator = false;
    /** Whether in-call messages are enabled. */
    public bool $messagesEnabled = false;
    /** Whether we may enable or disable in-call messages (admins). */
    public bool $canChangeMessagesEnabled = false;
    /** The invite link of the call, if any. */
    public ?string $inviteLink = null;
    /** How many participants are transmitting video. */
    public ?int $unmutedVideoCount = null;
    /** How many participants may transmit video at once. */
    public int $unmutedVideoLimit = 0;
    /** The DC to use when downloading media chunks in stream mode. */
    public ?int $streamDcId = null;
    /** Whether the call has ended. */
    public bool $discarded = false;
    /** Duration of the call in seconds, once it has ended. */
    public ?int $duration = null;

    /**
     * Constructor.
     *
     * @internal
     *
     * @param array<string, mixed> $call
     *
     * @psalm-mutation-free
     */
    public function __construct(
        MTProto $API,
        array $call,
        ?int $peerId = null,
    ) {
        parent::__construct($API);
        $this->id = $call['id'];
        $this->accessHash = $call['access_hash'];
        $this->peerId = $peerId;
        $this->update($call);
    }

    /**
     * @internal
     *
     * @param array<string, mixed> $call
     *
     * @psalm-external-mutation-free
     */
    public function update(array $call): void
    {
        if ($call['_'] === 'groupCallDiscarded') {
            $this->discarded = true;
            $this->duration = $call['duration'] ?? null;
            return;
        }
        $this->participantsCount = $call['participants_count'];
        $this->joinMuted = $call['join_muted'];
        $this->canChangeJoinMuted = $call['can_change_join_muted'];
        $this->joinDateAsc = $call['join_date_asc'];
        $this->canStartVideo = $call['can_start_video'];
        $this->rtmpStream = $call['rtmp_stream'];
        $this->listenersHidden = $call['listeners_hidden'];
        $this->creator = $call['creator'];
        $this->messagesEnabled = $call['messages_enabled'];
        $this->canChangeMessagesEnabled = $call['can_change_messages_enabled'];
        $this->inviteLink = $call['invite_link'] ?? null;
        $this->unmutedVideoCount = $call['unmuted_video_count'] ?? null;
        $this->unmutedVideoLimit = $call['unmuted_video_limit'];
        $this->streamDcId = $call['stream_dc_id'] ?? null;
    }

    /**
     * Join the call.
     *
     * @param bool $muted Whether to join muted.
     */
    #[\Override]
    public function join(bool $muted = false): static
    {
        $this->getClient()->joinGroupCallById($this->id, $muted);
        return $this;
    }

    /**
     * Leave the group call, without ending it for the other participants.
     */
    #[\Override]
    public function leave(): static
    {
        $this->getClient()->leaveGroupCall($this->id);
        return $this;
    }

    /**
     * End the group call for all participants.
     */
    #[\Override]
    public function discard(): static
    {
        $this->getClient()->discardGroupCall($this->id);
        return $this;
    }

    /**
     * Invite users to the group call (or, for a live story, to watch it).
     */
    #[\Override]
    public function invite(string|int ...$users): static
    {
        $this->getClient()->inviteToGroupCall($this->id, ...$users);
        return $this;
    }

    /**
     * Change the title of the group call.
     */
    #[\Override]
    public function setTitle(string $title): static
    {
        $this->getClient()->setGroupCallTitle($this->id, $title);
        return $this;
    }

    /**
     * Whether we are currently in the group call (joined and not left).
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function isJoined(): bool
    {
        return $this->getCallState() === GroupCallState::JOINED;
    }

    /**
     * Get the state of the group call.
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function getCallState(): GroupCallState
    {
        return $this->getClient()->getGroupCallState($this->id);
    }

    /**
     * Get all known participants, indexed by their bot API peer ID.
     *
     * @return array<int, TParticipant>
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function getParticipants(): array
    {
        /** @var array<int, TParticipant> */
        return $this->getClient()->getGroupCallParticipants($this->id);
    }

    /**
     * A participant of the call by their id, username or peer, or null if they are not in it.
     *
     * @return TParticipant|null
     */
    #[\Override]
    public function getParticipant(string|int $participant): ?AbstractGroupCallParticipant
    {
        /** @var TParticipant|null */
        return $this->getClient()->getGroupCallParticipant($this->id, $participant);
    }

    /**
     * Video chats and livestreams are not end-to-end encrypted, so there is no key to verify: always null.
     *
     * @psalm-pure
     */
    #[\Override]
    public function getVisualization(): ?array
    {
        return null;
    }

    /**
     * Mute or unmute our own audio stream.
     */
    #[\Override]
    public function setMuted(bool $muted = true): static
    {
        $this->getClient()->setGroupCallMuted($this->id, $muted);
        return $this;
    }

    /**
     * Whether our own audio stream is muted.
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function isMuted(): bool
    {
        return $this->getClient()->isGroupCallMuted($this->id);
    }

    /**
     * Send an [in-call message Â»](https://core.telegram.org/api/group-calls#in-call-messages), shown as
     * an overlay by the participants' clients (there is no chat history), if messages are enabled.
     *
     * @param string         $message   The text; markup in `$parseMode` is converted to entities.
     * @param ParseMode|null $parseMode Whether to parse HTML or Markdown markup in the text.
     * @param int|null       $paidStars Live stories only: Telegram Stars to donate with the message (at least {@see self::$sendPaidMessagesStars}).
     * @param string|int|null $sendAs    Live stories only: the peer to send the message as.
     */
    #[\Override]
    public function sendMessage(string $message, ?ParseMode $parseMode = null, ?int $paidStars = null, string|int|null $sendAs = null): static
    {
        $this->getClient()->sendGroupCallMessage($this->id, $message, $parseMode, $paidStars, $sendAs);
        return $this;
    }

    /**
     * Send an [in-call reaction Â»](https://core.telegram.org/api/group-calls#in-call-reactions).
     *
     * @param string   $emoji         The emoji.
     * @param int|null $customEmojiId The document id of a custom emoji to send instead, `$emoji` being its fallback.
     */
    #[\Override]
    public function sendReaction(string $emoji, ?int $customEmojiId = null): static
    {
        $this->getClient()->sendGroupCallReaction($this->id, $emoji, $customEmojiId);
        return $this;
    }

    /**
     * Delete in-call messages: our own, or anyone's if we are an admin.
     *
     * @param list<int> $ids        IDs of the messages to delete.
     * @param bool      $reportSpam Also report them as spam (admins only).
     */
    public function deleteMessages(array $ids, bool $reportSpam = false): static
    {
        $this->getClient()->deleteGroupCallMessages($this->id, $ids, $reportSpam);
        return $this;
    }

    /**
     * Delete every in-call message of a participant (admins only).
     *
     * @param bool $reportSpam Also report them as spam.
     */
    public function deleteParticipantMessages(string|int $participant, bool $reportSpam = false): static
    {
        $this->getClient()->deleteGroupCallParticipantMessages($this->id, $participant, $reportSpam);
        return $this;
    }

    /**
     * Enable or disable in-call messages (admins only).
     */
    #[\Override]
    public function setMessagesEnabled(bool $enabled): static
    {
        $this->getClient()->toggleGroupCallSettings($this->id, messagesEnabled: $enabled);
        return $this;
    }

    /**
     * Mute or unmute a participant (admins only; a participant muted by an admin may not unmute
     * themselves), or, for a non-admin, mute a participant only for ourselves.
     */
    #[\Override]
    public function muteParticipant(string|int $participant, bool $muted = true): static
    {
        $this->getClient()->editGroupCallParticipant($this->id, $participant, muted: $muted);
        return $this;
    }

    /**
     * Set our local playback volume of a participant.
     *
     * @param int<1, 20000> $volume From 1 to 20000, where 10000 is 100%.
     */
    #[\Override]
    public function setParticipantVolume(string|int $participant, int $volume): static
    {
        $this->getClient()->editGroupCallParticipant($this->id, $participant, volume: $volume);
        return $this;
    }

    /**
     * Pause or resume our own video stream, telling the other participants to keep showing the last
     * frame rather than hiding it.
     */
    #[\Override]
    public function setVideoPaused(bool $paused): static
    {
        $this->getClient()->editGroupCallParticipant($this->id, ['_' => 'inputPeerSelf'], videoPaused: $paused);
        return $this;
    }

    /**
     * Whether new participants join muted (admins only).
     */
    #[\Override]
    public function setJoinMuted(bool $joinMuted): static
    {
        $this->getClient()->toggleGroupCallSettings($this->id, joinMuted: $joinMuted);
        return $this;
    }

    /**
     * Invalidate every invite link exported so far (admins only).
     */
    #[\Override]
    public function resetInviteHash(): static
    {
        $this->getClient()->toggleGroupCallSettings($this->id, resetInviteHash: true);
        return $this;
    }

    /**
     * Record one participant into a single file (or stream) with a fixed set of tracks, muxed into
     * Matroska in pure PHP.
     *
     * `$participant` (a user id, username or peer) is required â€” every participant is recorded to
     * its own file, see {@see self::setOutputFolder()} to record everyone at once â€” except in
     * [stream mode Â»](https://core.telegram.org/api/group-calls#stream-mode) ({@see self::isStreamMode()}),
     * where there is a single mixed audio (and, for an RTMP livestream, video) stream rather than one per
     * participant: pass no `$participant`, and any {@see RecordingFormat}. Our own media is never recorded.
     *
     * The file holds the streams chosen with `$streams` â€” a bitmask of {@see CallStream::AUDIO},
     * {@see CallStream::VIDEO} and {@see CallStream::SCREEN}, every one of which must be available â€” or,
     * when null, every stream the participant currently sends; the available streams are returned. The
     * tracks are fixed for the whole file: a stream turned off stops being written and resumes when it
     * comes back, and only a change of codec or the end of the call finishes the file (see
     * {@see Call::setOutput()}); every such event is reported by a {@see CallStreams} update.
     *
     * Participants' frames are stored as-is, so the video tracks are whatever codec they send and the
     * audio is OPUS; `$format` picks the {@see RecordingFormat::Mkv} (default) or {@see RecordingFormat::Webm}
     * DocType, autodetected from a `.webm` extension.
     *
     * @param StreamMask|null $streams The streams to record, as a bitmask of {@see CallStream} flags, or null for every available one.
     *
     * @throws \InvalidArgumentException If a chosen stream is not available, or nothing is.
     *
     * @return StreamMask The streams the participant currently sends, as a bitmask of {@see CallStream} flags.
     */
    #[\Override]
    public function setOutput(LocalFile|WritableStream $file, string|int|null $participant = null, ?RecordingFormat $format = null, ?int $streams = null): int
    {
        return $this->getClient()->groupCallSetOutput($this->id, $file, $participant, $format, $streams);
    }

    /**
     * Record group call media into a directory, muxed into Matroska files in pure PHP.
     *
     * Records every transmitting participant â€” or only the given `$participant` â€” as
     * `<dir>/<peerId>.<n>_<streams>.mkv` files, one per combination of the audio, camera video and
     * screen share they send, each on or off at any time (see {@see Call::setOutputFolder()};
     * participants that start transmitting later are picked up too). Our own media is never recorded.
     * In [stream mode Â»](https://core.telegram.org/api/group-calls#stream-mode) the mixed stream is
     * recorded as `<dir>/stream.mkv` (or `.ogg` for {@see RecordingFormat::Opus}).
     *
     * `$format` picks the {@see RecordingFormat::Mkv} (default) or {@see RecordingFormat::Webm} DocType.
     */
    #[\Override]
    public function setOutputFolder(LocalDirectory $dir, string|int|null $participant = null, ?RecordingFormat $format = null): static
    {
        $this->getClient()->groupCallSetOutputFolder($this->id, $dir, $participant, $format);
        return $this;
    }

    /**
     * Play a file, transmitting its audio and, if it carries a transmittable one, its video.
     *
     * A WebM/Matroska file with VP8, VP9, H.264 or H.265 video has its video transmitted too; any other
     * file (or a raw audio stream) is played as audio only. Frames are demuxed in pure PHP and sent
     * as-is where possible, so no transcoding (and thus no FFI extension) is required for
     * pre-encoded WebM/OGG-OPUS input.
     */
    #[\Override]
    public function play(LocalFile|RemoteUrl|ReadableStream $file, MediaDestination $dest = MediaDestination::Camera): static
    {
        $this->getClient()->groupCallPlay($this->id, $file, $dest);
        return $this;
    }

    /**
     * Play a file, blocking until it has finished playing if a stream is provided.
     */
    #[\Override]
    public function playBlocking(LocalFile|RemoteUrl|ReadableStream $file, MediaDestination $dest = MediaDestination::Camera): static
    {
        $this->getClient()->groupCallPlayBlocking($this->id, $file, $dest);
        return $this;
    }

    /**
     * Play file.
     */
    #[\Override]
    public function then(LocalFile|RemoteUrl|ReadableStream $file, MediaDestination $dest = MediaDestination::Camera): static
    {
        $this->getClient()->groupCallPlay($this->id, $file, $dest);
        return $this;
    }

    /**
     * When called, skips to the next file in the playlist.
     */
    #[\Override]
    public function skip(MediaDestination $dest = MediaDestination::Camera): static
    {
        $this->getClient()->groupCallSkipPlay($this->id, $dest);
        return $this;
    }

    /**
     * Stops playing all files, clears the main and the hold playlist.
     */
    #[\Override]
    public function stop(MediaDestination $dest = MediaDestination::Camera): static
    {
        $this->getClient()->groupCallStopPlay($this->id, $dest);
        return $this;
    }

    /**
     * Pauses the currently playing file.
     *
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function pause(MediaDestination $dest = MediaDestination::Camera): static
    {
        $this->getClient()->groupCallPausePlay($this->id, $dest);
        return $this;
    }

    /**
     * Whether the currently playing file is paused.
     *
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function isPaused(MediaDestination $dest = MediaDestination::Camera): bool
    {
        return $this->getClient()->isGroupCallPlayPaused($this->id, $dest);
    }

    /**
     * Resumes the currently playing file.
     *
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function resume(MediaDestination $dest = MediaDestination::Camera): static
    {
        $this->getClient()->groupCallResumePlay($this->id, $dest);
        return $this;
    }

    /**
     * Files to play on hold.
     */
    #[\Override]
    public function playOnHold(MediaDestination $dest = MediaDestination::Camera, LocalFile|RemoteUrl|ReadableStream ...$files): static
    {
        $this->getClient()->groupCallPlayOnHold($this->id, $dest, ...$files);
        return $this;
    }

    /**
     * Get the file that is currently being played.
     *
     * Will return a string with the object ID of the stream if we're currently playing a stream,
     * otherwise returns the related LocalFile or RemoteUrl.
     *
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function getCurrent(MediaDestination $dest = MediaDestination::Camera): RemoteUrl|LocalFile|string|null
    {
        return $this->getClient()->groupCallGetCurrent($this->id, $dest);
    }

    /**
     * Start sharing a screen: a second connection (phone.joinGroupCallPresentation) whose video is
     * transmitted on the {@see MediaDestination::Presentation} stream. Idempotent; requires the call to
     * be joined.
     */
    #[\Override]
    public function enablePresentation(): static
    {
        $this->getClient()->enableGroupCallPresentation($this->id);
        return $this;
    }

    /**
     * Stop sharing the screen.
     */
    #[\Override]
    public function disablePresentation(): static
    {
        $this->getClient()->disableGroupCallPresentation($this->id);
        return $this;
    }

    /**
     * Whether a screen-share is currently being transmitted.
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function isSharingScreen(): bool
    {
        return $this->getClient()->isGroupCallSharingScreen($this->id);
    }

    /**
     * Whether the server switched us to [stream mode Â»](https://core.telegram.org/api/group-calls#stream-mode)
     * (a large livestream, or an RTMP one): the call's media is received by downloading chunks, and
     * there is a single mixed stream to record with {@see self::setOutput()} (pass no participant).
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function isStreamMode(): bool
    {
        return $this->getClient()->isGroupCallStreamMode($this->id);
    }

    /**
     * Whether the call's media is published by a single external RTMP publisher.
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function isRtmpMode(): bool
    {
        return $this->getClient()->isGroupCallRtmpMode($this->id);
    }

    /**
     * Get call representation.
     *
     * @psalm-mutation-free
     */
    abstract public function __toString(): string;
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Calls;

use danog\MadelineProto\MTProto;

/**
 * What an in-call message of a video chat ({@see GroupCallMessage}) and of a live story
 * ({@see LiveStoryMessage}) have in common: both are server-side (not end-to-end encrypted) messages
 * of a group call, with a server-assigned {@see self::$id} and moderation.
 */
abstract class AbstractGroupCallMessage extends MultiCallMessage
{
    /** ID of the message in the call. */
    public readonly int $id;
    /** Whether the sender is an admin of the call. */
    public readonly bool $fromAdmin;
    /** Bot API ID of the peer the call belongs to (the group or channel of a video chat, the poster of a live story), if known. */
    public readonly ?int $peerId;

    /**
     * @internal
     *
     * @param array{call: array{id: int}, message: array{id: int, from_id: mixed, date: int, from_admin: bool, message: array{text: string, entities?: list<array<array-key, mixed>>}}} $rawUpdate
     *
     * @psalm-mutation-free
     */
    public function __construct(MTProto $API, array $rawUpdate)
    {
        parent::__construct($API, $rawUpdate);
        $message = $rawUpdate['message'];
        $this->id = $message['id'];
        $this->fromAdmin = $message['from_admin'];
        $this->peerId = $API->getGroupCallPeerId($this->callId);
    }

    /**
     * Delete this message from the call (our own, or anyone's if we are an admin).
     *
     * @param bool $reportSpam Also report it as spam (admins only).
     */
    public function delete(bool $reportSpam = false): void
    {
        $this->getClient()->deleteGroupCallMessages($this->callId, [$this->id], $reportSpam);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Calls;

use JsonSerializable;

/**
 * A participant of a multi-party call, returned by {@see \danog\MadelineProto\EventHandler\MultiCall::getParticipants()}
 * and {@see \danog\MadelineProto\EventHandler\MultiCall::getParticipant()}.
 *
 * The concrete type depends on the call: a {@see \danog\MadelineProto\EventHandler\Calls\GroupCallParticipant}
 * for a group call (video chat or livestream, {@see AbstractGroupCall}), or a {@see ConferenceCallParticipant}
 * for an end-to-end encrypted {@see ConferenceCall}.
 */
abstract class MultiCallParticipant implements JsonSerializable
{
    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(
        /** Bot API ID of the peer this participant is (a user id, in a conference). */
        public readonly int $peerId,
    ) {
    }

    /**
     * @return array<string, mixed>
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function jsonSerialize(): array
    {
        return get_object_vars($this);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Calls;

use danog\MadelineProto\MTProto;

/**
 * This update represents a Telegram [video chat or livestream Â»](https://core.telegram.org/api/group-calls#video-chats-livestreams):
 * a group call associated with a group or channel.
 *
 * On top of what every group call offers ({@see AbstractGroupCall}), a video chat has a title, can be
 * scheduled, recorded server-side, joined on behalf of a channel we own, and moderated by its admins.
 * See https://core.telegram.org/api/group-calls for more info.
 *
 * @extends AbstractGroupCall<GroupCallParticipant>
 */
final class GroupCall extends AbstractGroupCall
{
    /** Title of the call, if it has one. */
    public ?string $title = null;
    /** Whether we subscribed to a notification when this scheduled call starts. */
    public bool $scheduleStartSubscribed = false;
    /** Whether a server-side recording of the video is in progress. */
    public bool $recordVideoActive = false;
    /** When the server-side recording started, if one is in progress. */
    public ?int $recordStartDate = null;
    /** When the call is scheduled to start, if it is a scheduled call. */
    public ?int $scheduleDate = null;
    /** Whether this is an end-to-end encrypted conference call rather than a video chat/livestream. */
    public bool $conference = false;

    /**
     * @internal
     *
     * @param array<string, mixed> $call
     *
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function update(array $call): void
    {
        parent::update($call);
        if ($call['_'] === 'groupCallDiscarded') {
            return;
        }
        $this->title = $call['title'] ?? null;
        $this->scheduleStartSubscribed = $call['schedule_start_subscribed'];
        $this->recordVideoActive = $call['record_video_active'];
        $this->recordStartDate = $call['record_start_date'] ?? null;
        $this->scheduleDate = $call['schedule_date'] ?? null;
        $this->conference = $call['conference'];
    }

    /**
     * Join the group call.
     *
     * @param bool        $muted      Whether to join muted.
     * @param string|int|null $joinAs     Peer to join as: ourselves, or a channel we own (see {@see MTProto::getGroupCallJoinAs()}).
     * @param string|null $inviteHash Invite hash from a [video chat invite link Â»](https://core.telegram.org/api/links#video-chat-livestream-links), if any.
     */
    #[\Override]
    public function join(bool $muted = false, string|int|null $joinAs = null, ?string $inviteHash = null): static
    {
        $this->getClient()->joinGroupCallById($this->id, $muted, $joinAs, $inviteHash);
        return $this;
    }

    /**
     * Export an invite link for this group call.
     *
     * @param bool $canSelfUnmute Whether users joining with this link may speak without asking; admins only.
     */
    #[\Override]
    public function exportInvite(bool $canSelfUnmute = false): string
    {
        return $this->getClient()->exportGroupCallInvite($this->id, $canSelfUnmute);
    }

    /**
     * Remove participants from the group call.
     *
     * A video chat has no notion of removing someone from just the call: as official clients do, this
     * kicks them from the group or channel the call belongs to (removal from a basic group, a ban from
     * a supergroup or channel), which also drops them from the call. Requires the `ban_users` admin right.
     */
    #[\Override]
    public function removeParticipant(string|int ...$participants): static
    {
        $this->getClient()->removeGroupCallParticipants($this->id, ...$participants);
        return $this;
    }

    /**
     * Raise or lower our hand, asking the admins to let us speak when muted by them.
     */
    public function raiseHand(bool $raised = true): static
    {
        $this->getClient()->editGroupCallParticipant($this->id, ['_' => 'inputPeerSelf'], raiseHand: $raised);
        return $this;
    }

    /**
     * Start a server-side recording of the call (admins only), sent to the admin's Saved Messages
     * once stopped.
     *
     * @param string|null $title    Title of the recording.
     * @param bool        $video    Whether to record video as well as audio.
     * @param bool        $portrait Whether the video is recorded in portrait (true) or landscape (false) orientation.
     */
    public function startRecording(?string $title = null, bool $video = false, bool $portrait = false): static
    {
        $this->getClient()->toggleGroupCallRecord($this->id, true, $title, $video, $portrait);
        return $this;
    }

    /**
     * Stop the server-side recording of the call (admins only).
     */
    public function stopRecording(): static
    {
        $this->getClient()->toggleGroupCallRecord($this->id, false);
        return $this;
    }

    /**
     * Start this scheduled call now (admins only).
     */
    public function startScheduled(): static
    {
        $this->getClient()->startScheduledGroupCall($this->id);
        return $this;
    }

    /**
     * Subscribe to (or unsubscribe from) a notification from the Telegram service account when this
     * scheduled call starts.
     */
    public function setStartSubscription(bool $subscribed): static
    {
        $this->getClient()->toggleGroupCallStartSubscription($this->id, $subscribed);
        return $this;
    }

    /**
     * Get call representation.
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function __toString(): string
    {
        $title = $this->title !== null ? " \"{$this->title}\"" : '';
        $chat = $this->peerId !== null ? " of chat {$this->peerId}" : '';
        return "group call {$this->id}$title$chat";
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Calls;

/**
 * A participant of a [video chat/livestream Â»](https://core.telegram.org/api/group-calls#video-chats-livestreams)
 * ({@see GroupCall}), mirroring [groupCallParticipant](https://core.telegram.org/constructor/groupCallParticipant).
 */
final class GroupCallParticipant extends AbstractGroupCallParticipant
{
    /**
     * @internal
     *
     * @param array<string, mixed>              $participant
     * @param AbstractGroupCallParticipant|null $cached      The previously known state of this participant, if any.
     *
     * @psalm-mutation-free
     */
    public static function fromRaw(array $participant, int $peerId, ?AbstractGroupCallParticipant $cached = null): self
    {
        return new self(...self::commonArgs($participant, $peerId, $cached));
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Calls;

use danog\MadelineProto\CallStream;
use danog\MadelineProto\EventHandler\Call;
use danog\MadelineProto\EventHandler\Update;
use danog\MadelineProto\MTProto;

/**
 * The streams a participant of a call sends (their microphone, camera and screen share, see
 * {@see CallStream}) or their codecs changed, or a recording of them ended.
 *
 * Emitted for every call type: the other party of a one-to-one {@see PrivateCall}, and every
 * participant of a {@see GroupCall}, {@see LiveStory} or {@see ConferenceCall}. In
 * [stream mode Â»](https://core.telegram.org/api/group-calls#stream-mode) the mixed stream is
 * reported as participant `0`.
 *
 * Use {@see self::$streams} to decide what to record with {@see Call::setOutput()}: the file's
 * tracks are fixed when it is opened, so a stream that becomes available afterwards is not
 * added to it â€” call {@see Call::setOutput()} again to start a new file with it.
 *
 * @psalm-import-type StreamMask from CallStream
 */
final class CallStreams extends Update
{
    /** The call. */
    public readonly Call $call;
    /** Bot API id of the participant whose streams these are (`0` for the mixed stream of a stream-mode group call). */
    public readonly int $participant;
    /**
     * The streams the participant currently sends: a bitmask of {@see CallStream::AUDIO},
     * {@see CallStream::VIDEO} and {@see CallStream::SCREEN}.
     *
     * @var StreamMask
     */
    public readonly int $streams;
    /**
     * The codec of every stream media was received for so far, as a Matroska codec ID (`A_OPUS`,
     * `V_VP8`, `V_VP9`, `V_MPEG4/ISO/AVC`, `V_MPEGH/ISO/HEVC`, `V_AV1`), keyed by stream:
     * {@see CallStream::AUDIO}, {@see CallStream::VIDEO} or {@see CallStream::SCREEN}. A stream that is
     * on but whose first frame has not arrived yet is not listed.
     *
     * @var array<int, string>
     */
    public readonly array $codecs;
    /** Whether a recording of this participant just ended (false when the streams merely changed). */
    public readonly bool $recordingStopped;

    /**
     * @internal
     *
     * @param array{call: Call, participant: int, streams: StreamMask, codecs: array<int, string>, recordingStopped: bool} $rawUpdate
     *
     * @psalm-mutation-free
     */
    public function __construct(MTProto $API, array $rawUpdate)
    {
        parent::__construct($API);
        $this->call = $rawUpdate['call'];
        $this->participant = $rawUpdate['participant'];
        $this->streams = $rawUpdate['streams'];
        $this->codecs = $rawUpdate['codecs'];
        $this->recordingStopped = $rawUpdate['recordingStopped'];
    }

    /**
     * Whether the participant currently sends a given stream ({@see CallStream::AUDIO}, {@see CallStream::VIDEO} or {@see CallStream::SCREEN}).
     *
     * @param StreamMask $stream
     *
     * @psalm-mutation-free
     */
    public function has(int $stream): bool
    {
        return ($this->streams & $stream) === $stream;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Calls;

use JsonSerializable;

/**
 * A donor of a [live story](https://core.telegram.org/api/group-calls#live-stories), from
 * [groupCallDonor](https://core.telegram.org/constructor/groupCallDonor).
 *
 * @psalm-immutable
 */
final class GroupCallDonor implements JsonSerializable
{
    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(
        /** Bot API ID of the donor, or null for an anonymous donor. */
        public readonly ?int $peerId,
        /** How many Telegram Stars they donated. */
        public readonly int $stars,
        /** Whether this is one of the top donors. */
        public readonly bool $top,
        /** Whether this is us. */
        public readonly bool $my,
    ) {
    }

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function jsonSerialize(): array
    {
        return get_object_vars($this);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Calls;

/**
 * This update represents a Telegram [live story Â»](https://core.telegram.org/api/group-calls#live-stories):
 * a livestream posted as a story by a user, group or channel, of which the poster is the only
 * publisher; everyone else joins as a listener.
 *
 * On top of what every group call offers ({@see AbstractGroupCall}), a live story takes paid comments
 * and Telegram Stars donations. It has no title (its caption is the story's), cannot be scheduled or
 * recorded server-side, and does not support screen sharing.
 *
 * @extends AbstractGroupCall<LiveStoryParticipant>
 */
final class LiveStory extends AbstractGroupCall
{
    /** The minimum Telegram Stars donation required to comment, if any. */
    public ?int $sendPaidMessagesStars = null;
    /** Bot API ID of the peer we send in-call messages as by default. */
    public ?int $defaultSendAs = null;

    /**
     * @internal
     *
     * @param array<string, mixed> $call
     *
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function update(array $call): void
    {
        parent::update($call);
        if ($call['_'] === 'groupCallDiscarded') {
            return;
        }
        $this->sendPaidMessagesStars = $call['send_paid_messages_stars'] ?? null;
        $this->defaultSendAs = isset($call['default_send_as']) ? $this->getClient()->getIdInternal($call['default_send_as']) : null;
    }

    /**
     * Export an invite link to the live story.
     *
     * @param bool $canSelfUnmute Ignored: only the poster of a live story publishes media.
     */
    #[\Override]
    public function exportInvite(bool $canSelfUnmute = false): string
    {
        return $this->getClient()->exportGroupCallInvite($this->id, false);
    }

    /**
     * Viewers cannot be removed from a live story; a viewer's comments can be moderated with
     * {@see self::deleteParticipantMessages()} instead.
     *
     * @psalm-pure
     */
    #[\Override]
    public function removeParticipant(string|int ...$participants): static
    {
        throw new \LogicException('Viewers cannot be removed from a live story.');
    }

    /**
     * donate Telegram Stars to the streamer, without a message.
     */
    public function donate(int $stars): self
    {
        $this->getClient()->sendGroupCallMessage($this->id, '', null, $stars);
        return $this;
    }

    /**
     * the Telegram Stars donated so far and the top donors.
     */
    public function getStars(): GroupCallStars
    {
        return $this->getClient()->getGroupCallStars($this->id);
    }

    /**
     * the peer we send in-call messages as by default.
     */
    public function setDefaultSendAs(string|int $peer): self
    {
        $this->getClient()->saveDefaultGroupCallSendAs($this->id, $peer);
        return $this;
    }

    /**
     * the minimum Telegram Stars donation required to comment, or null to let
     * everyone comment for free.
     */
    public function setPaidMessagesStars(?int $stars): self
    {
        $this->getClient()->toggleGroupCallSettings($this->id, sendPaidMessagesStars: $stars ?? 0);
        return $this;
    }

    /**
     * Get call representation.
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function __toString(): string
    {
        $chat = $this->peerId !== null ? " of {$this->peerId}" : '';
        return "live story {$this->id}$chat";
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Calls;

/**
 * A participant of a [live story Â»](https://core.telegram.org/api/group-calls#live-stories)
 * ({@see LiveStory}): a {@see groupCallParticipant} that additionally carries the Telegram Stars the
 * participant has donated ({@see self::$paidStarsTotal}).
 */
final class LiveStoryParticipant extends AbstractGroupCallParticipant
{
    /**
     * @internal
     *
     * @param list<int> $videoSources
     * @param list<int> $presentationSources
     * @param int<1, 20000> $volume Playback volume, from 1 to 20000 where 10000 is 100%.
     *
     * @psalm-mutation-free
     */
    public function __construct(
        int $peerId,
        int $source,
        int $date,
        ?int $activeDate,
        bool $muted,
        bool $canSelfUnmute,
        bool $mutedByYou,
        bool $self,
        bool $justJoined,
        bool $videoJoined,
        array $videoSources,
        array $presentationSources,
        ?string $videoEndpoint,
        ?string $presentationEndpoint,
        int $volume,
        ?string $about,
        ?int $raiseHandRating,
        bool $volumeByAdmin,
        /** Total Telegram Stars donated by this participant, if any. */
        public readonly ?int $paidStarsTotal,
    ) {
        parent::__construct(
            $peerId,
            $source,
            $date,
            $activeDate,
            $muted,
            $canSelfUnmute,
            $mutedByYou,
            $self,
            $justJoined,
            $videoJoined,
            $videoSources,
            $presentationSources,
            $videoEndpoint,
            $presentationEndpoint,
            $volume,
            $about,
            $raiseHandRating,
            $volumeByAdmin,
        );
    }

    /**
     * @internal
     *
     * @param array<string, mixed>              $participant
     * @param AbstractGroupCallParticipant|null $cached      The previously known state of this participant, if any.
     *
     * @psalm-mutation-free
     */
    public static function fromRaw(array $participant, int $peerId, ?AbstractGroupCallParticipant $cached = null): self
    {
        return new self(
            ...self::commonArgs($participant, $peerId, $cached),
            paidStarsTotal: $participant['paid_stars_total'] ?? null,
        );
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Calls;

/**
 * What a participant of a video chat ({@see GroupCallParticipant}) and of a live story
 * ({@see LiveStoryParticipant}) have in common, mirroring the shared fields of
 * [groupCallParticipant](https://core.telegram.org/constructor/groupCallParticipant).
 */
abstract class AbstractGroupCallParticipant extends MultiCallParticipant
{
    /**
     * @internal
     *
     * @param list<int> $videoSources        Signed SSRCs of this participant's camera video streams, empty if none.
     * @param list<int> $presentationSources Signed SSRCs of this participant's screen-share (presentation) streams, empty if none.
     *
     * @psalm-mutation-free
     */
    public function __construct(
        int $peerId,
        /** WebRTC audio source ID (SSRC) of this participant, `0` if unknown. */
        public readonly int $source,
        /** When did this participant join the call. */
        public readonly int $date,
        /** When was this participant last active, if ever. */
        public readonly ?int $activeDate,
        /** Whether the participant is muted. */
        public readonly bool $muted,
        /** Whether a muted participant may unmute themselves. */
        public readonly bool $canSelfUnmute,
        /** Whether we muted this participant only for ourselves. */
        public readonly bool $mutedByYou,
        /** Whether this participant is ourselves. */
        public readonly bool $self,
        /** Whether the participant just joined. */
        public readonly bool $justJoined,
        /** Whether the participant is transmitting video. */
        public readonly bool $videoJoined,
        /** @var list<int> */
        public readonly array $videoSources,
        /** @var list<int> */
        public readonly array $presentationSources,
        /** The SFU endpoint id of this participant's camera stream, needed to subscribe to it. */
        public readonly ?string $videoEndpoint,
        /** The SFU endpoint id of this participant's screen-share stream, needed to subscribe to it. */
        public readonly ?string $presentationEndpoint,
        /**
         * Playback volume, from 1 to 20000 where 10000 is 100%.
         *
         * @var int<1, 20000>
         */
        public readonly int $volume,
        /** Bio of the participant, if any. */
        public readonly ?string $about,
        /** Raised hand rating, if the participant raised their hand. */
        public readonly ?int $raiseHandRating,
        /** Whether the participant's volume was set by an admin. */
        public readonly bool $volumeByAdmin,
    ) {
        parent::__construct($peerId);
    }

    /**
     * The common {@see groupCallParticipant} fields, as named constructor arguments shared by every
     * subclass, applying the `min` rules (a min update keeps `volume`, `muted_by_you` and the video
     * source groups from the cached state, see https://core.telegram.org/constructor/groupCallParticipant).
     *
     * @param array<string, mixed> $participant
     * @param self|null            $cached      The previously known state of this participant, if any.
     *
     * @return array<string, mixed>
     *
     * @psalm-mutation-free
     */
    final protected static function commonArgs(array $participant, int $peerId, ?self $cached): array
    {
        $min = $participant['min'];
        // Video/presentation source groups are only carried in full (non-min) updates; a min update
        // keeps whatever the cached state already had, exactly like volume and muted_by_you.
        $video = self::videoSources($participant['video'] ?? null);
        $presentation = self::videoSources($participant['presentation'] ?? null);
        $videoEndpoint = self::endpoint($participant['video'] ?? null);
        $presentationEndpoint = self::endpoint($participant['presentation'] ?? null);
        if ($min && $cached !== null) {
            if (!\is_array($participant['video'] ?? null)) {
                $video = $cached->videoSources;
                $videoEndpoint = $cached->videoEndpoint;
            }
            if (!\is_array($participant['presentation'] ?? null)) {
                $presentation = $cached->presentationSources;
                $presentationEndpoint = $cached->presentationEndpoint;
            }
        }
        return [
            'peerId' => $peerId,
            'source' => $participant['source'],
            'date' => $participant['date'],
            'activeDate' => $participant['active_date'] ?? null,
            'muted' => $participant['muted'],
            'canSelfUnmute' => $participant['can_self_unmute'],
            'mutedByYou' => $min && $cached !== null ? $cached->mutedByYou : $participant['muted_by_you'],
            'self' => $participant['self'],
            'justJoined' => $participant['just_joined'],
            'videoJoined' => $participant['video_joined'],
            'videoSources' => $video,
            'presentationSources' => $presentation,
            'videoEndpoint' => $videoEndpoint,
            'presentationEndpoint' => $presentationEndpoint,
            'volume' => $min && $cached !== null ? $cached->volume : ($participant['volume'] ?? 10000),
            'about' => $participant['about'] ?? null,
            'raiseHandRating' => $participant['raise_hand_rating'] ?? null,
            'volumeByAdmin' => $participant['volume_by_admin'],
        ];
    }

    /**
     * Flatten the source SSRCs of a
     * [groupCallParticipantVideo](https://core.telegram.org/constructor/groupCallParticipantVideo),
     * taking each source group's primary (first) source and every simulcast layer, deduplicated.
     * Retransmission (FID) pairs contribute only their main source, since we do not record rtx.
     *
     * @param mixed $video The `video` or `presentation` field of a groupCallParticipant, if present.
     *
     * @return list<int> Signed SSRCs.
     *
     * @psalm-pure
     */
    private static function videoSources(mixed $video): array
    {
        if (!\is_array($video) || ($video['paused'] ?? false)) {
            return [];
        }
        $sources = [];
        foreach ($video['source_groups'] ?? [] as $group) {
            $sim = ($group['semantics'] ?? '') === 'SIM';
            foreach ($group['sources'] ?? [] as $index => $source) {
                if ($sim || $index === 0) {
                    $sources[$source] = true;
                }
            }
        }
        return array_values(array_keys($sources));
    }

    /**
     * The `endpoint` of a groupCallParticipantVideo, the key used to subscribe to that stream over
     * the colibri data channel.
     *
     * @psalm-pure
     */
    private static function endpoint(mixed $video): ?string
    {
        if (!\is_array($video)) {
            return null;
        }
        $endpoint = $video['endpoint'] ?? null;
        return \is_string($endpoint) && $endpoint !== '' ? $endpoint : null;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Calls;

use JsonSerializable;

/**
 * The [Telegram Stars donations](https://core.telegram.org/api/group-calls#paid-live-story-donations)
 * received by a live story, from [phone.groupCallStars](https://core.telegram.org/constructor/phone.groupCallStars).
 *
 * @psalm-immutable
 */
final class GroupCallStars implements JsonSerializable
{
    /**
     * @internal
     *
     * @param list<GroupCallDonor> $topDonors
     *
     * @psalm-mutation-free
     */
    public function __construct(
        /** Total Telegram Stars donated so far. */
        public readonly int $totalStars,
        /**
         * The top donors.
         *
         * @var list<GroupCallDonor>
         */
        public readonly array $topDonors,
    ) {
    }

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function jsonSerialize(): array
    {
        return get_object_vars($this);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Calls;

/**
 * An end-to-end encrypted [in-call message or reaction Â»](https://core.telegram.org/api/end-to-end/group-calls#conference-in-call-messages)
 * sent in a {@see ConferenceCall}.
 *
 * Conference messages carry no server-assigned id and cannot be deleted or moderated, unlike a plain
 * {@see AbstractGroupCallMessage}.
 */
final class ConferenceCallMessage extends MultiCallMessage
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Calls;

use danog\MadelineProto\EventHandler\Update;
use danog\MadelineProto\MTProto;

/**
 * Some [in-call messages Â»](https://core.telegram.org/api/group-calls#in-call-messages) of a group call
 * were deleted.
 */
final class GroupCallMessagesDeleted extends Update
{
    /** ID of the group call. */
    public readonly int $callId;
    /**
     * IDs of the deleted messages.
     *
     * @var list<int>
     */
    public readonly array $ids;

    /**
     * @internal
     *
     * @param array{call: array{id: int}, messages: list<int>} $rawUpdate
     *
     * @psalm-mutation-free
     */
    public function __construct(MTProto $API, array $rawUpdate)
    {
        parent::__construct($API);
        $this->callId = $rawUpdate['call']['id'];
        $this->ids = $rawUpdate['messages'];
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Calls;

use danog\MadelineProto\EventHandler\Update;
use danog\MadelineProto\MTProto;

/**
 * Participants of a group call joined, left, or changed state (muted, video, volume, raised hand...),
 * from [updateGroupCallParticipants](https://core.telegram.org/constructor/updateGroupCallParticipants).
 */
final class GroupCallParticipants extends Update
{
    /** ID of the group call. */
    public readonly int $callId;
    /**
     * The participants that changed, by their bot API peer ID.
     *
     * @var array<int, AbstractGroupCallParticipant>
     */
    public readonly array $participants;
    /**
     * Bot API peer IDs of the participants that left the call.
     *
     * @var list<int>
     */
    public readonly array $left;

    /**
     * @internal
     *
     * @param array{call: array{id: int}, participants: list<array<array-key, mixed>>} $rawUpdate
     *
     * @psalm-mutation-free
     */
    public function __construct(MTProto $API, array $rawUpdate)
    {
        parent::__construct($API);
        $this->callId = $rawUpdate['call']['id'];
        $liveStory = $API->isGroupCallLiveStory($this->callId);
        $participants = [];
        $left = [];
        foreach ($rawUpdate['participants'] as $participant) {
            $peerId = $API->getIdInternal($participant['peer']);
            if ($peerId === null) {
                continue;
            }
            if ($participant['left']) {
                $left[] = $peerId;
                continue;
            }
            $participants[$peerId] = $liveStory
                ? LiveStoryParticipant::fromRaw($participant, $peerId)
                : GroupCallParticipant::fromRaw($participant, $peerId);
        }
        $this->participants = $participants;
        $this->left = $left;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Calls;

use danog\MadelineProto\MTProto;

/**
 * An [in-call message or reaction Â»](https://core.telegram.org/api/group-calls#in-call-messages) sent in a [live story Â»](https://core.telegram.org/api/group-calls#live-stories) ({@see LiveStory}).
 *
 * On top of a plain {@see AbstractGroupCallMessage}, a live story message may carry a Telegram Stars
 * donation ({@see self::$paidStars}); a donation with an empty text is a standalone donation
 * ({@see self::isDonation()}).
 */
final class LiveStoryMessage extends AbstractGroupCallMessage
{
    /** Telegram Stars donated with this message, if any. */
    public readonly ?int $paidStars;

    /**
     * @internal
     *
     * @param array{call: array{id: int}, message: array{id: int, from_id: mixed, date: int, from_admin: bool, paid_message_stars?: int, message: array{text: string, entities?: list<array<array-key, mixed>>}}} $rawUpdate
     *
     * @psalm-mutation-free
     */
    public function __construct(MTProto $API, array $rawUpdate)
    {
        parent::__construct($API, $rawUpdate);
        $this->paidStars = $rawUpdate['message']['paid_message_stars'] ?? null;
    }

    /**
     * Whether this is a reaction rather than a text message: a single (possibly custom) emoji, and not
     * a donation.
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function isReaction(): bool
    {
        return $this->paidStars === null && parent::isReaction();
    }

    /**
     * Whether this is a standalone donation (Telegram Stars with no text).
     *
     * @psalm-mutation-free
     */
    public function isDonation(): bool
    {
        return $this->paidStars !== null && $this->message->text === '';
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Calls;

/**
 * State of a group call we are interacting with.
 */
enum GroupCallState
{
    /** The call exists but we did not join it yet. */
    case NOT_JOINED;
    /** We are joining the call. */
    case JOINING;
    /** We joined the call and media is flowing. */
    case JOINED;
    /** We left the call, or the call ended. */
    case LEFT;
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Calls;

use danog\MadelineProto\EventHandler\CommandType;
use danog\MadelineProto\EventHandler\Message\Entities\CustomEmoji;
use danog\MadelineProto\EventHandler\Message\Entities\MessageEntity;
use danog\MadelineProto\EventHandler\Message\Entities\TextWithEntities;
use danog\MadelineProto\EventHandler\SimpleFilters;
use danog\MadelineProto\EventHandler\Update;
use danog\MadelineProto\MTProto;

/**
 * An [in-call message or reaction Â»](https://core.telegram.org/api/group-calls#in-call-messages) sent in a multi-party call, shown as an overlay by the participants' clients (there is no chat history).
 *
 * The concrete type depends on the call: a {@see GroupCallMessage} for a video chat or livestream, a
 * {@see LiveStoryMessage} (which can carry a Telegram Stars donation) for a live story, or a
 * {@see ConferenceCallMessage} for an end-to-end encrypted conference.
 *
 * A reaction is a message whose text is a single emoji (with, for a custom emoji, one custom emoji
 * entity spanning it), see {@see self::isReaction()}.
 *
 * Like chat messages, in-call messages can be matched by the message filters (commands, text, regexes,
 * sender, incoming/outgoing, peer).
 */
abstract class MultiCallMessage extends Update implements SimpleFilters
{
    /** ID of the call the message was sent in. */
    public readonly int $callId;
    /** Bot API ID of the sender. */
    public readonly int $fromId;
    /** When the message was sent. */
    public readonly int $date;
    /** The text of the message (or the emoji of a reaction) with its entities. */
    public readonly TextWithEntities $message;
    /** Whether we sent the message. */
    public readonly bool $out;

    /** Bot command (if present) */
    public readonly ?string $command;
    /** Bot command type (if present) */
    public readonly ?CommandType $commandType;
    /** @var list<string> Bot command arguments (if present) */
    public readonly ?array $commandArgs;

    /**
     * @readonly
     *
     * @var list<string> Regex matches, if a filter regex is present
     */
    public ?array $matches = null;
    /**
     * @readonly
     *
     * @var array<array-key, array<array-key, list{string, int}|null|string>|mixed> Regex matches, if a filter multiple match regex is present
     */
    public ?array $matchesAll = null;

    /**
     * @internal
     *
     * @param array{call: array{id: int}, message: array{from_id: mixed, date: int, message: array{text: string, entities?: list<array<array-key, mixed>>}, ...}, ...} $rawUpdate
     *
     * @psalm-mutation-free
     */
    public function __construct(MTProto $API, array $rawUpdate)
    {
        parent::__construct($API);
        $message = $rawUpdate['message'];
        $this->callId = $rawUpdate['call']['id'];
        $this->fromId = $API->getIdInternal($message['from_id']) ?? 0;
        $this->date = $message['date'];
        $this->message = new TextWithEntities(
            $message['message']['text'],
            MessageEntity::fromRawEntities($message['message']['entities'] ?? []),
            null,
        );
        $self = $API->getSelf();
        $this->out = $self !== false && $this->fromId === $self['id'];
        [$this->commandType, $this->command, $this->commandArgs] = CommandType::parse($this->message->text);
    }

    /**
     * Build the right message subclass for an `updateGroupCallMessage`: a {@see ConferenceCallMessage}
     * for an end-to-end encrypted message, a {@see LiveStoryMessage} for a live story, or a plain
     * {@see GroupCallMessage} for a video chat or livestream.
     *
     * @internal
     *
     * @param array{call: array{id: int}, encrypted?: bool, message: array<string, mixed>} $rawUpdate
     *
     * @psalm-mutation-free
     */
    public static function fromUpdate(MTProto $API, array $rawUpdate): self
    {
        if ($rawUpdate['encrypted'] ?? false) {
            return new ConferenceCallMessage($API, $rawUpdate);
        }
        if ($API->isGroupCallLiveStory($rawUpdate['call']['id'])) {
            return new LiveStoryMessage($API, $rawUpdate);
        }
        return new GroupCallMessage($API, $rawUpdate);
    }

    /**
     * Whether this is a reaction rather than a text message: a single (possibly custom) emoji.
     *
     * @psalm-mutation-free
     */
    public function isReaction(): bool
    {
        $text = $this->message->text;
        if ($text === '') {
            return false;
        }
        if ($this->message->entities !== []) {
            $entity = $this->message->entities[0];
            return \count($this->message->entities) === 1
                && $entity instanceof CustomEmoji
                && $entity->offset === 0
                && $entity->length === \strlen(mb_convert_encoding($text, 'UTF-16', 'UTF-8')) / 2;
        }
        return mb_strlen($text) <= 8
            && preg_match('/^[\p{So}\p{Sk}\x{200D}\x{FE0F}\x{20E3}\x{1F3FB}-\x{1F3FF}\x{1F1E6}-\x{1F1FF}]+$/u', $text) === 1;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Calls;

use Amp\ByteStream\ReadableStream;
use Amp\ByteStream\WritableStream;
use danog\MadelineProto\EventHandler\MultiCall;
use danog\MadelineProto\EventHandler\Update;
use danog\MadelineProto\LocalDirectory;
use danog\MadelineProto\LocalFile;
use danog\MadelineProto\MediaDestination;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\ParseMode;
use danog\MadelineProto\RecordingFormat;
use danog\MadelineProto\RemoteUrl;

/**
 * This update represents a Telegram [end-to-end encrypted conference call Â»](https://core.telegram.org/api/end-to-end/group-calls).
 *
 * Unlike a plain {@see GroupCall} (a video chat or livestream), a conference call is not associated
 * with any group or channel: it is a standalone, end-to-end encrypted multi-party call, whose media
 * the SFU only ever forwards as ciphertext. On top of the shared {@see MultiCall} surface it exposes
 * the conference-specific behaviour: the [emoji verification Â»](https://core.telegram.org/api/end-to-end/group-calls#verifying-the-key)
 * of the key, end-to-end encrypted in-call messages, and removing participants by rekeying.
 *
 * This is a thin, serializable handle: every operation is delegated to the live conference controller
 * ({@see \danog\MadelineProto\Tgcalls\E2E\ConferenceCall}) by call ID, so it keeps working across a
 * process restart and over IPC.
 *
 * @implements MultiCall<ConferenceCallParticipant>
 *
 * @psalm-import-type StreamMask from \danog\MadelineProto\CallStream
 */
final class ConferenceCall extends Update implements MultiCall
{
    /** Conference call ID. */
    public readonly int $id;
    /** Access hash of the conference call. */
    public readonly int $accessHash;

    /** Number of participants. */
    public int $participantsCount = 0;
    /** Whether we created this call. */
    public bool $creator = false;
    /** The invite link of the conference call, if any. */
    public ?string $inviteLink = null;
    /** Whether in-call messages are enabled. */
    public bool $messagesEnabled = false;
    /** How many participants are transmitting video. */
    public ?int $unmutedVideoCount = null;
    /** How many participants may transmit video at once. */
    public int $unmutedVideoLimit = 0;
    /** Whether the call has ended. */
    public bool $discarded = false;
    /** Duration of the call in seconds, once it has ended. */
    public ?int $duration = null;

    /**
     * Constructor.
     *
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(
        MTProto $API,
        array $call,
    ) {
        parent::__construct($API);
        $this->id = $call['id'];
        $this->accessHash = $call['access_hash'];
        $this->update($call);
    }

    /**
     * @internal
     *
     * @param array<string, mixed> $call
     *
     * @psalm-external-mutation-free
     */
    public function update(array $call): void
    {
        if ($call['_'] === 'groupCallDiscarded') {
            $this->discarded = true;
            $this->duration = $call['duration'] ?? null;
            return;
        }
        $this->participantsCount = $call['participants_count'];
        $this->creator = $call['creator'];
        $this->inviteLink = $call['invite_link'] ?? null;
        $this->messagesEnabled = $call['messages_enabled'];
        $this->unmutedVideoCount = $call['unmuted_video_count'] ?? null;
        $this->unmutedVideoLimit = $call['unmuted_video_limit'];
    }

    /**
     * Join the conference call.
     *
     * @param bool $muted Whether to join muted.
     */
    #[\Override]
    public function join(bool $muted = false): static
    {
        $this->getClient()->joinConferenceCall([
            '_' => 'inputGroupCall',
            'id' => $this->id,
            'access_hash' => $this->accessHash,
        ], $muted);
        return $this;
    }

    /**
     * Whether we are currently in the conference (joined and not left/removed).
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function isJoined(): bool
    {
        return $this->getClient()->isConferenceCallJoined($this->id);
    }

    /**
     * Get the state of the conference call.
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function getCallState(): GroupCallState
    {
        return $this->getClient()->getConferenceCallState($this->id);
    }

    /**
     * Leave the conference call, without ending it for the other participants.
     */
    #[\Override]
    public function leave(): static
    {
        $this->getClient()->leaveConferenceCall($this->id);
        return $this;
    }

    /**
     * End the conference call for everyone, if we created it, and leave it.
     *
     * Only the creator of a conference may end it: for anyone else the server refuses and this simply
     * {@see self::leave()}s the call.
     */
    #[\Override]
    public function discard(): static
    {
        $this->getClient()->discardConferenceCall($this->id);
        return $this;
    }

    /**
     * Get the participants currently in the conference, keyed by their user id, each with their
     * Ed25519 public key, permission bits and protocol version from the shared-state chain.
     *
     * @return array<int, ConferenceCallParticipant>
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function getParticipants(): array
    {
        return $this->getClient()->getConferenceCallParticipants($this->id);
    }

    /**
     * A participant of the conference by their id, username or peer, with their Ed25519 public key,
     * permission bits and protocol version from the shared-state chain, or null if not in it.
     *
     */
    #[\Override]
    public function getParticipant(string|int $participant): ?ConferenceCallParticipant
    {
        return $this->getParticipants()[$this->getClient()->getId($participant)] ?? null;
    }

    /**
     * Remove participants from the conference and rekey for the remaining members, so the removed
     * members can no longer decrypt the call's media. Requires the `remove_users` permission.
     */
    #[\Override]
    public function removeParticipant(string|int ...$participants): static
    {
        $this->getClient()->removeConferenceCallParticipants($this->id, ...$participants);
        return $this;
    }

    /**
     * Invite users to the conference call, ringing them.
     */
    #[\Override]
    public function invite(string|int ...$users): static
    {
        $this->getClient()->inviteToConferenceCall($this->id, ...$users);
        return $this;
    }

    /**
     * The [conference link Â»](https://core.telegram.org/api/links#conference-links) of this conference
     * call, which anyone can use to join it.
     *
     * @param bool $canSelfUnmute Ignored: a conference has no admins, everyone may speak.
     */
    #[\Override]
    public function exportInvite(bool $canSelfUnmute = false): string
    {
        return $this->getClient()->exportConferenceCallInvite($this->id);
    }

    /**
     * Change the title of the conference call.
     */
    #[\Override]
    public function setTitle(string $title): static
    {
        $this->getClient()->setConferenceCallTitle($this->id, $title);
        return $this;
    }

    /**
     * The four [key verification emojis Â»](https://core.telegram.org/api/end-to-end/group-calls#verifying-the-key),
     * which every participant can compare to make sure nobody is in the middle.
     *
     * Verification (a commit-reveal exchange between all participants) runs automatically whenever
     * the set of participants changes; this returns null until it completes for the current state.
     *
     * @return list<string>|null
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function getVisualization(): ?array
    {
        return $this->getClient()->getConferenceCallVisualization($this->id);
    }

    /**
     * Send an end-to-end encrypted [in-call message Â»](https://core.telegram.org/api/end-to-end/group-calls#conference-in-call-messages)
     * to every participant of the conference.
     *
     * @param string         $message   The text; markup in `$parseMode` is converted to entities (bold, italic, underline, strikethrough, spoiler and custom emoji are supported).
     * @param ParseMode|null $parseMode Whether to parse HTML or Markdown markup in the text.
     * @param int|null       $paidStars Ignored: conference messages cannot carry donations.
     * @param string|int|null $sendAs    Ignored: conference messages are always sent as ourselves.
     */
    #[\Override]
    public function sendMessage(string $message, ?ParseMode $parseMode = null, ?int $paidStars = null, string|int|null $sendAs = null): static
    {
        $this->getClient()->sendConferenceCallMessage($this->id, $message, $parseMode);
        return $this;
    }

    /**
     * Send an end-to-end encrypted [in-call reaction Â»](https://core.telegram.org/api/end-to-end/group-calls#conference-in-call-reactions).
     *
     * @param string   $emoji         The emoji.
     * @param int|null $customEmojiId The document id of a custom emoji to send instead, `$emoji` being its fallback.
     */
    #[\Override]
    public function sendReaction(string $emoji, ?int $customEmojiId = null): static
    {
        $this->getClient()->sendConferenceCallReaction($this->id, $emoji, $customEmojiId);
        return $this;
    }

    /**
     * Enable or disable in-call messages.
     */
    #[\Override]
    public function setMessagesEnabled(bool $enabled): static
    {
        $this->getClient()->toggleConferenceCallSettings($this->id, messagesEnabled: $enabled);
        return $this;
    }

    /**
     * Mute a participant for ourselves only (a conference has no admins).
     */
    #[\Override]
    public function muteParticipant(string|int $participant, bool $muted = true): static
    {
        $this->getClient()->editConferenceCallParticipant($this->id, $participant, muted: $muted);
        return $this;
    }

    /**
     * Set our local playback volume of a participant.
     *
     * @param int<1, 20000> $volume From 1 to 20000, where 10000 is 100%.
     */
    #[\Override]
    public function setParticipantVolume(string|int $participant, int $volume): static
    {
        $this->getClient()->editConferenceCallParticipant($this->id, $participant, volume: $volume);
        return $this;
    }

    /**
     * Pause or resume our own video stream, telling the other participants to keep showing the last
     * frame rather than hiding it.
     */
    #[\Override]
    public function setVideoPaused(bool $paused): static
    {
        $this->getClient()->editConferenceCallParticipant($this->id, ['_' => 'inputPeerSelf'], videoPaused: $paused);
        return $this;
    }

    /**
     * Whether new participants join muted.
     */
    #[\Override]
    public function setJoinMuted(bool $joinMuted): static
    {
        $this->getClient()->toggleConferenceCallSettings($this->id, joinMuted: $joinMuted);
        return $this;
    }

    /**
     * Invalidate the conference link exported so far, so a new one is generated.
     */
    #[\Override]
    public function resetInviteHash(): static
    {
        $this->getClient()->toggleConferenceCallSettings($this->id, resetInviteHash: true);
        return $this;
    }

    /**
     * A conference is always received over WebRTC (its media is end-to-end encrypted): never in stream mode.
     *
     * @psalm-pure
     */
    #[\Override]
    public function isStreamMode(): bool
    {
        return false;
    }

    /**
     * A conference has no RTMP publisher.
     *
     * @psalm-pure
     */
    #[\Override]
    public function isRtmpMode(): bool
    {
        return false;
    }

    /**
     * Whether a screen-share is currently being transmitted.
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function isSharingScreen(): bool
    {
        return $this->getClient()->isConferenceCallSharingScreen($this->id);
    }

    /**
     * Start sharing a screen: a second, end-to-end encrypted connection whose video is transmitted on
     * the {@see MediaDestination::Presentation} stream. Idempotent; requires the call to be joined.
     */
    #[\Override]
    public function enablePresentation(): static
    {
        $this->getClient()->enableConferenceCallPresentation($this->id);
        return $this;
    }

    /**
     * Stop sharing the screen.
     */
    #[\Override]
    public function disablePresentation(): static
    {
        $this->getClient()->disableConferenceCallPresentation($this->id);
        return $this;
    }

    /**
     * Record one participant into a single file (or stream) with a fixed set of tracks, muxed into
     * Matroska in pure PHP. Every recording is plaintext â€” the frames are decrypted before they are muxed.
     *
     * `$participant` (a user id, username or peer) is required: every participant is recorded to its
     * own file, see {@see self::setOutputFolder()} to record everyone at once. Our own media is never recorded.
     *
     * The file holds the streams chosen with `$streams` â€” a bitmask of {@see CallStream::AUDIO},
     * {@see CallStream::VIDEO} and {@see CallStream::SCREEN}, every one of which must be available â€” or,
     * when null, every stream the participant currently sends; the available streams are returned. The
     * tracks are fixed for the whole file: a stream turned off stops being written and resumes when it
     * comes back, and only a change of codec or the end of the call finishes the file (see
     * {@see Call::setOutput()}); every such event is reported by a {@see CallStreams} update.
     *
     * Participants' frames are stored as-is, so the video tracks are whatever codec they send and the
     * audio is OPUS; `$format` picks the {@see RecordingFormat::Mkv} (default) or {@see RecordingFormat::Webm}
     * DocType, autodetected from a `.webm` extension. Audio-only OGG OPUS recordings are not supported.
     *
     * @param StreamMask|null $streams The streams to record, as a bitmask of {@see CallStream} flags, or null for every available one.
     *
     * @throws \InvalidArgumentException If a chosen stream is not available, or nothing is.
     *
     * @return StreamMask The streams the participant currently sends, as a bitmask of {@see CallStream} flags.
     */
    #[\Override]
    public function setOutput(LocalFile|WritableStream $file, string|int|null $participant = null, ?RecordingFormat $format = null, ?int $streams = null): int
    {
        return $this->getClient()->conferenceCallSetOutput($this->id, $file, $participant, $format, $streams);
    }

    /**
     * Record conference call media into a directory, muxed into Matroska files in pure PHP.
     *
     * Records every transmitting participant â€” or only the given `$participant` â€” as
     * `<dir>/<userId>.<n>_<streams>.mkv` files, one per combination of the audio, camera video and
     * screen share they send, each on or off at any time (see {@see Call::setOutputFolder()};
     * participants that start transmitting later are picked up too). Our own media is never recorded.
     * Every recording is plaintext â€” the frames are decrypted before they are muxed.
     *
     * `$format` picks the {@see RecordingFormat::Mkv} (default) or {@see RecordingFormat::Webm} DocType.
     */
    #[\Override]
    public function setOutputFolder(LocalDirectory $dir, string|int|null $participant = null, ?RecordingFormat $format = null): static
    {
        $this->getClient()->conferenceCallSetOutputFolder($this->id, $dir, $participant, $format);
        return $this;
    }

    /**
     * Mute or unmute our own audio stream.
     */
    #[\Override]
    public function setMuted(bool $muted = true): static
    {
        $this->getClient()->setConferenceCallMuted($this->id, $muted);
        return $this;
    }

    /**
     * Whether our own audio stream is muted.
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function isMuted(): bool
    {
        return $this->getClient()->isConferenceCallMuted($this->id);
    }

    /**
     * Play a file, transmitting its audio and, if it carries a transmittable one, its video, all
     * end-to-end encrypted before it reaches the SFU.
     *
     * A WebM/Matroska file with VP8, VP9, H.264 or H.265 video has its video transmitted too; any other
     * file (or a raw audio stream) is played as audio only. Frames are demuxed in pure PHP and sent
     * as-is where possible, so no transcoding (and thus no FFI extension) is required for
     * pre-encoded WebM/OGG-OPUS input.
     */
    #[\Override]
    public function play(LocalFile|RemoteUrl|ReadableStream $file, MediaDestination $dest = MediaDestination::Camera): static
    {
        $this->getClient()->conferenceCallPlay($this->id, $file, $dest);
        return $this;
    }

    /**
     * Play a file, blocking until it has finished playing if a stream is provided.
     */
    #[\Override]
    public function playBlocking(LocalFile|RemoteUrl|ReadableStream $file, MediaDestination $dest = MediaDestination::Camera): static
    {
        $this->getClient()->conferenceCallPlayBlocking($this->id, $file, $dest);
        return $this;
    }

    /**
     * Play file.
     */
    #[\Override]
    public function then(LocalFile|RemoteUrl|ReadableStream $file, MediaDestination $dest = MediaDestination::Camera): static
    {
        $this->getClient()->conferenceCallPlay($this->id, $file, $dest);
        return $this;
    }

    /**
     * When called, skips to the next file in the playlist.
     */
    #[\Override]
    public function skip(MediaDestination $dest = MediaDestination::Camera): static
    {
        $this->getClient()->conferenceCallSkipPlay($this->id, $dest);
        return $this;
    }

    /**
     * Stops playing all files, clears the main and the hold playlist.
     */
    #[\Override]
    public function stop(MediaDestination $dest = MediaDestination::Camera): static
    {
        $this->getClient()->conferenceCallStopPlay($this->id, $dest);
        return $this;
    }

    /**
     * Pauses the currently playing file.
     *
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function pause(MediaDestination $dest = MediaDestination::Camera): static
    {
        $this->getClient()->conferenceCallPausePlay($this->id, $dest);
        return $this;
    }

    /**
     * Whether the currently playing file is paused.
     *
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function isPaused(MediaDestination $dest = MediaDestination::Camera): bool
    {
        return $this->getClient()->isConferenceCallPlayPaused($this->id, $dest);
    }

    /**
     * Resumes the currently playing file.
     *
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function resume(MediaDestination $dest = MediaDestination::Camera): static
    {
        $this->getClient()->conferenceCallResumePlay($this->id, $dest);
        return $this;
    }

    /**
     * Files to play on hold.
     */
    #[\Override]
    public function playOnHold(MediaDestination $dest = MediaDestination::Camera, LocalFile|RemoteUrl|ReadableStream ...$files): static
    {
        $this->getClient()->conferenceCallPlayOnHold($this->id, $dest, ...$files);
        return $this;
    }

    /**
     * Get the file that is currently being played.
     *
     * Will return a string with the object ID of the stream if we're currently playing a stream,
     * otherwise returns the related LocalFile or RemoteUrl.
     *
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function getCurrent(MediaDestination $dest = MediaDestination::Camera): RemoteUrl|LocalFile|string|null
    {
        return $this->getClient()->conferenceCallGetCurrent($this->id, $dest);
    }

    /**
     * Get call representation.
     *
     * @psalm-mutation-free
     */
    public function __toString(): string
    {
        return "E2E conference call {$this->id}";
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Calls;

/**
 * Reason why a call or group call cannot be started in a chat, the value is a human-readable description.
 */
enum CallDenialReason: string
{
    /** We are a bot, and bots cannot make one-to-one calls. */
    case BOTS_CANNOT_CALL = 'Bots cannot make calls!';
    /** The other user of the private chat is ourselves. */
    case CANNOT_CALL_SELF = 'Cannot call ourselves!';
    /** The other user cannot be called at all. */
    case CALLS_UNAVAILABLE = 'This user cannot be called!';
    /** The privacy settings of the other user do not allow us to call them. */
    case PRIVACY_SETTINGS = "This user's privacy settings do not allow us to call them!";
    /** The other user cannot be video called. */
    case VIDEO_CALLS_UNAVAILABLE = 'This user cannot be video called!';

    /** We are a bot, and bots cannot create group calls. */
    case BOTS_CANNOT_CREATE_GROUP_CALLS = 'Bots cannot create group calls!';
    /** We are not a member of the group or channel. */
    case NOT_A_MEMBER = 'We are not a member of this chat!';
    /** We lack the `manage_call` admin right in the group or channel. */
    case MANAGE_CALL_RIGHT_REQUIRED = 'The manage_call admin right is required to create a group call in this chat!';
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Calls;

/**
 * An [in-call message or reaction Â»](https://core.telegram.org/api/group-calls#in-call-messages) sent in a [video chat or livestream Â»](https://core.telegram.org/api/group-calls#video-chats-livestreams)
 * ({@see GroupCall}), mirroring [groupCallMessage](https://core.telegram.org/constructor/groupCallMessage).
 */
final class GroupCallMessage extends AbstractGroupCallMessage
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Query;

use danog\MadelineProto\EventHandler\CallbackQuery;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\TL\Types\Bytes;
use ReflectionClass;
use ReflectionProperty;

/** Represents a query sent by the user by clicking on a button. */
abstract class ButtonQuery extends CallbackQuery
{
    /** Data associated with the callback button. Be aware that a bad client can send arbitrary data in this field. */
    public readonly string $data;

    /**
     * @readonly
     * @var list<string> Regex matches, if a filter regex is present.
     */
    public ?array $matches = null;
    /**
     * @readonly
     *
     * @var array<array-key, array<array-key, list{string, int}|null|string>|mixed> Regex matches, if a filter multiple match regex is present
     */
    public ?array $matchesAll = null;

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(MTProto $API, array $rawCallback)
    {
        parent::__construct($API, $rawCallback);
        $this->data = (string) $rawCallback['data'];
    }

    /** @internal */
    #[\Override]
    public function jsonSerialize(): mixed
    {
        $res = ['_' => static::class];
        $refl = new ReflectionClass($this);
        foreach ($refl->getProperties(ReflectionProperty::IS_PUBLIC) as $prop) {
            $res[$prop->getName()] = $prop->getValue($this);
        }
        $res['data'] = new Bytes($res['data']);
        return $res;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Query;

use danog\MadelineProto\MTProto;
use danog\MadelineProto\ParseMode;
use danog\MadelineProto\Tools;

/** @internal */
trait InlineTrait
{
    /** Inline message ID */
    public readonly string $inlineMessageId;
    protected readonly array $rawId;

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(MTProto $API, array $rawCallback)
    {
        parent::__construct($API, $rawCallback);
        $this->rawId = $rawCallback['msg_id'];
        $this->inlineMessageId = Tools::base64urlEncode(match ($rawCallback['msg_id']['_']) {
            'inputBotInlineMessageID' => (
                Tools::packSignedInt($rawCallback['msg_id']['dc_id']).
                Tools::packSignedLong($rawCallback['msg_id']['id']).
                Tools::packSignedLong($rawCallback['msg_id']['access_hash'])
            ),
            'inputBotInlineMessageID64' => (
                Tools::packSignedInt($rawCallback['msg_id']['dc_id']).
                Tools::packSignedLong($rawCallback['msg_id']['owner_id']).
                Tools::packSignedInt($rawCallback['msg_id']['id']).
                Tools::packSignedLong($rawCallback['msg_id']['access_hash'])
            ),
        });
    }

    /**
     * Edit message text.
     *
     * @param string     $message     New message
     * @param ParseMode  $parseMode   Whether to parse HTML or Markdown markup in the message
     * @param array|null $replyMarkup Reply markup for inline keyboards
     * @param bool       $noWebpage   Disable webpage preview
     */
    public function editText(
        string    $message,
        ?array    $replyMarkup = null,
        ParseMode $parseMode = ParseMode::TEXT,
        bool      $noWebpage = false
    ): void {
        $this->getClient()->methodCallAsyncRead(
            'messages.editInlineBotMessage',
            [
                'id' => $this->rawId,
                'message' => $message,
                'reply_markup' => $replyMarkup,
                'parse_mode' => $parseMode,
                'no_webpage' => $noWebpage,
            ],
        );
    }

    /**
     * Edit message keyboard.
     *
     * @param array $replyMarkup Reply markup for inline keyboards
     */
    public function editReplyMarkup(array $replyMarkup): void
    {
        $this->getClient()->methodCallAsyncRead(
            'messages.editInlineBotMessage',
            [
                'id' => $this->rawId,
                'reply_markup' => $replyMarkup,
            ],
        );
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Query;

use danog\DialogId\DialogId;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Message\ReportReason;
use danog\MadelineProto\EventHandler\Update;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\ParseMode;

/** @internal */
trait ChatTrait
{
    /** Chat where the inline keyboard was sent */
    public readonly int $chatId;

    /** Message ID */
    public readonly int $messageId;

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(MTProto $API, array $rawCallback)
    {
        parent::__construct($API, $rawCallback);
        $this->chatId = $API->getIdInternal($rawCallback['peer']);
        $this->messageId = $rawCallback['msg_id'];
    }

    /**
     * Edit message text.
     *
     * @param string     $message      New message
     * @param ParseMode  $parseMode    Whether to parse HTML or Markdown markup in the message
     * @param array|null $replyMarkup  Reply markup for inline keyboards
     * @param int|null   $scheduleDate Scheduled message date for scheduled messages
     * @param bool       $noWebpage    Disable webpage preview
     */
    public function editText(
        string    $message,
        ?array    $replyMarkup = null,
        ParseMode $parseMode = ParseMode::TEXT,
        bool      $noWebpage = false,
        ?int      $scheduleDate = null
    ): Message {
        $result = $this->getClient()->methodCallAsyncRead(
            'messages.editMessage',
            [
                'peer' => $this->chatId,
                'id' => $this->messageId,
                'message' => $message,
                'reply_markup' => $replyMarkup,
                'parse_mode' => $parseMode,
                'schedule_date' => $scheduleDate,
                'no_webpage' => $noWebpage,
            ],
        );
        return $this->getClient()->wrapMessage($this->getClient()->extractMessage($result));
    }

    /**
     * Edit message keyboard.
     *
     * @param array $replyMarkup Reply markup for inline keyboards
     */
    public function editReplyMarkup(array $replyMarkup): Message
    {
        $result = $this->getClient()->methodCallAsyncRead(
            'messages.editMessage',
            [
                'peer' => $this->chatId,
                'id' => $this->messageId,
                'reply_markup' => $replyMarkup,
            ],
        );
        return $this->getClient()->wrapMessage($this->getClient()->extractMessage($result));
    }

    /**
     * Delete the message.
     *
     * @param boolean $revoke Whether to delete the message for all participants of the chat.
     */
    public function delete(bool $revoke = true): void
    {
        $this->getClient()->methodCallAsyncRead(
            DialogId::isSupergroupOrChannelOrMonoforum($this->chatId) ? 'channels.deleteMessages' : 'messages.deleteMessages',
            [
                'channel' => $this->chatId,
                'id' => [$this->messageId],
                'revoke' => $revoke,
            ]
        );
    }

    /**
     * Pin a message.
     *
     * @param bool $pmOneside Whether the message should only be pinned on the local side of a one-to-one chat
     * @param bool $silent    Pin the message silently, without triggering a notification
     */
    public function pin(bool $pmOneside = false, bool $silent = false): void
    {
        $this->getClient()->methodCallAsyncRead(
            'messages.updatePinnedMessage',
            [
                'peer' => $this->chatId,
                'id' => $this->messageId,
                'pm_oneside' => $pmOneside,
                'silent' => $silent,
                'unpin' => false,
            ]
        );
    }

    /**
     * Unpin a message.
     *
     * @param bool $pmOneside Whether the message should only be pinned on the local side of a one-to-one chat
     * @param bool $silent    Pin the message silently, without triggering a notification
     */
    public function unpin(bool $pmOneside = false, bool $silent = false): ?Update
    {
        $result = $this->getClient()->methodCallAsyncRead(
            'messages.updatePinnedMessage',
            [
                'peer' => $this->chatId,
                'id' => $this->messageId,
                'pm_oneside' => $pmOneside,
                'silent' => $silent,
                'unpin' => true,
            ]
        );
        return $this->getClient()->wrapUpdate($result);
    }

    /**
     * Report a message in a chat for violation of telegramâ€™s Terms of Service.
     *
     * @param ReportReason $reason  Why are these messages being reported
     * @param string       $message Comment for report moderation
     */
    public function report(ReportReason $reason, string $message): bool
    {
        return $this->getClient()->methodCallAsyncRead(
            'messages.report',
            [
                'reason' => ['_' => $reason->value],
                'message' => $message,
                'id' => [$this->messageId],
                'peer' => $this->chatId,
            ]
        );
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Query;

/** Represents a query sent by the user by clicking on a button in an inline message. */
final class InlineButtonQuery extends ButtonQuery
{
    use InlineTrait;
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Query;

/** Represents a query sent by the user by clicking on a button in a chat. */
final class ChatButtonQuery extends ButtonQuery
{
    use ChatTrait;
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Query;

use danog\MadelineProto\EventHandler\CallbackQuery;
use danog\MadelineProto\EventHandler\Query;
use danog\MadelineProto\MTProto;

/** Represents a query sent by the user by clicking on a "Play game" button. */
abstract class GameQuery extends CallbackQuery
{
    /** Short name of a Game to be returned, serves as the unique identifier for the game */
    public readonly string $gameShortName;

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(MTProto $API, array $rawCallback)
    {
        parent::__construct($API, $rawCallback);
        $this->gameShortName = $rawCallback['game_short_name'];
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Query;

/** Represents a query sent by the user by clicking on a "Play game" button in an inline message. */
final class InlineGameQuery extends GameQuery
{
    use InlineTrait;
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Query;

/** Represents a query sent by the user by clicking on a "Play game" button in a chat. */
final class ChatGameQuery extends GameQuery
{
    use ChatTrait;
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler;

use danog\MadelineProto\EventHandler\Action\Cancel;
use danog\MadelineProto\EventHandler\Action\ChooseContact;
use danog\MadelineProto\EventHandler\Action\ChooseSticker;
use danog\MadelineProto\EventHandler\Action\EmojiSeen;
use danog\MadelineProto\EventHandler\Action\EmojiTap;
use danog\MadelineProto\EventHandler\Action\GamePlay;
use danog\MadelineProto\EventHandler\Action\GeoLocation;
use danog\MadelineProto\EventHandler\Action\GroupCallSpeaking;
use danog\MadelineProto\EventHandler\Action\HistoryImport;
use danog\MadelineProto\EventHandler\Action\RecordAudio;
use danog\MadelineProto\EventHandler\Action\RecordRound;
use danog\MadelineProto\EventHandler\Action\RecordVideo;
use danog\MadelineProto\EventHandler\Action\Typing;
use danog\MadelineProto\EventHandler\Action\UploadAudio;
use danog\MadelineProto\EventHandler\Action\UploadDocument;
use danog\MadelineProto\EventHandler\Action\UploadPhoto;
use danog\MadelineProto\EventHandler\Action\UploadRound;
use danog\MadelineProto\EventHandler\Action\UploadVideo;
use JsonSerializable;
use ReflectionClass;
use ReflectionProperty;

/**
 * In-progress actions.
 */
abstract class Action implements JsonSerializable
{
    /**
     * @internal
     *
     * @psalm-pure
     */
    public static function fromRawAction(array $rawAction): Action
    {
        return match ($rawAction['_']) {
            'sendMessageTypingAction' => new Typing,
            'sendMessageCancelAction' => new Cancel,
            'sendMessageGamePlayAction' => new GamePlay,
            'sendMessageGeoLocationAction' => new GeoLocation,
            'sendMessageChooseContactAction' => new ChooseContact,
            'sendMessageChooseStickerAction' => new ChooseSticker,
            'sendMessageRecordAudioAction' => new RecordAudio,
            'sendMessageRecordRoundAction' => new RecordRound,
            'sendMessageRecordVideoAction' => new RecordVideo,
            'speakingInGroupCallAction' => new GroupCallSpeaking,
            'sendMessageUploadVideoAction' => new UploadVideo($rawAction['progress']),
            'sendMessageUploadRoundAction' => new UploadRound($rawAction['progress']),
            'sendMessageUploadAudioAction' => new UploadAudio($rawAction['progress']),
            'sendMessageUploadPhotoAction' => new UploadPhoto($rawAction['progress']),
            'sendMessageUploadDocumentAction' => new UploadDocument($rawAction['progress']),
            'sendMessageHistoryImportAction' => new HistoryImport($rawAction['progress']),
            'sendMessageEmojiInteractionSeen' => new EmojiSeen($rawAction['emoticon']),
            'sendMessageEmojiInteraction' => new EmojiTap(
                $rawAction['emoticon'],
                $rawAction['msg_id'],
                $rawAction['interaction']['a']
            ),
        };
    }

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function toRawAction(): array
    {
        return match (true) {
            $this instanceof Typing =>  [ '_' => 'sendMessageTypingAction' ],
            $this instanceof Cancel =>  [ '_' => 'sendMessageCancelAction' ],
            $this instanceof GamePlay => [ '_' => 'sendMessageGamePlayAction' ],
            $this instanceof GeoLocation => [ '_' => 'sendMessageGeoLocationAction' ],
            $this instanceof ChooseContact => [ '_' => 'sendMessageChooseContactAction' ],
            $this instanceof ChooseSticker => [ '_' => 'sendMessageChooseStickerAction' ],
            $this instanceof RecordAudio => [ '_' => 'sendMessageRecordAudioAction' ],
            $this instanceof RecordRound => [ '_' => 'sendMessageRecordRoundAction' ],
            $this instanceof RecordVideo => [ '_' => 'sendMessageRecordVideoAction' ],
            $this instanceof UploadVideo => [ '_' => 'sendMessageUploadVideoAction'],
            $this instanceof UploadRound => [ '_' => 'sendMessageUploadRoundAction'],
            $this instanceof UploadAudio => [ '_' => 'sendMessageUploadAudioAction'],
            $this instanceof UploadPhoto => [ '_' => 'sendMessageUploadPhotoAction'],
            $this instanceof UploadDocument => [ '_' => 'sendMessageUploadDocumentAction'],
            $this instanceof HistoryImport => [ '_' => 'sendMessageHistoryImportAction'],
            $this instanceof GroupCallSpeaking => [ '_' => 'speakingInGroupCallAction' ],
            $this instanceof EmojiSeen => [ '_' => 'sendMessageEmojiInteractionSeen' ],
            $this instanceof EmojiTap => [ '_' => 'sendMessageEmojiInteraction' ],
        };
    }

    /** @internal */
    #[\Override]
    public function jsonSerialize(): mixed
    {
        $res = ['_' => static::class];
        $refl = new ReflectionClass($this);
        foreach ($refl->getProperties(ReflectionProperty::IS_PUBLIC) as $prop) {
            $res[$prop->getName()] = $prop->getValue($this);
        }
        return $res;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler;

/**
 * Info about a forwarded message.
 *
 * @psalm-immutable
 */
final class ForwardedInfo
{
    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(
        /** When was the message originally sent */
        public readonly int $date,
        /** The ID of the user that originally sent the message */
        public readonly ?int $fromId,
        /** The name of the user that originally sent the message */
        public readonly ?string $fromName,
        /** ID of the channel message that was forwarded */
        public readonly ?int $forwardedChannelMsgId,
        /** For channels and if signatures are enabled, author of the channel message that was forwareded */
        public readonly ?string $forwardedChannelMsgAuthor,
        /** Only for messages forwarded to Saved Messages, full info about the user/channel that originally sent the message */
        public readonly ?int $savedFromId,
        /** Only for messages forwarded to Saved Messages, ID of the message that was forwarded from the original user/channel */
        public readonly ?int $savedFromMsgId,
    ) {
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Mahdi <mahdi.talaee1379@gmail.com>
 * @copyright 2016-2025 Mahdi <mahdi.talaee1379@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Message;

/**
 * Represents a reply to one of our messages in a channel comment group that we're not a member of (i.e. received via `@replies`).
 */
final class CommentReply extends GroupMessage
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Mahdi <mahdi.talaee1379@gmail.com>
 * @copyright 2016-2025 Mahdi <mahdi.talaee1379@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Message;

use AssertionError;
use danog\MadelineProto\EventHandler\AbstractPrivateMessage;
use danog\MadelineProto\EventHandler\Message\Service\DialogScreenshotTaken;
use danog\MadelineProto\MTProto;

/**
 * Represents New encrypted message.
 */
final class SecretMessage extends AbstractPrivateMessage
{
    /** @internal */
    public function __construct(MTProto $API, array $rawMessage, array $info, bool $scheduled)
    {
        parent::__construct($API, $rawMessage, $info, $scheduled);
    }

    /**
     * ID of the other user in this secret chat.
     *
     * @psalm-mutation-free
     */
    #[\Override]
    protected function getCallPeer(): int
    {
        return $this->senderId;
    }

    #[\Override]
    public function getReply(string $class = SecretMessage::class): ?SecretMessage
    {
        if ($class !== SecretMessage::class) {
            throw new AssertionError("A class that extends SecretMessage was expected.");
        }
        if ($this->replyToMsgId === null) {
            return null;
        }
        if ($this->replyCached) {
            if (!$this->replyCache instanceof $class) {
                return null;
            }
            return $this->replyCache;
        }
        $message = $this->getClient()->getSecretMessage(
            chatId: $this->chatId,
            randomId: $this->replyToMsgId
        );
        $this->replyCache = $message;
        $this->replyCached = true;
        return $this->replyCache;
    }

    /**
     * Delete the message.
     *
     * @param boolean $revoke Whether to delete the message for all participants of the chat.
     */
    #[\Override]
    public function delete(bool $revoke = true): void
    {
        if (!$revoke) {
            return;
        }
        $this->getClient()->methodCallAsyncRead(
            'messages.sendEncryptedService',
            [
                'peer' => $this->chatId,
                'message' => [
                    '_' => 'decryptedMessageService',
                    'action' => ['_' => 'decryptedMessageActionDeleteMessages', 'random_ids' => [$this->id]],
                ],
            ]
        );
    }

    #[\Override]
    public function screenShot(): DialogScreenshotTaken
    {
        $result = $this->getClient()->methodCallAsyncRead(
            'messages.sendEncryptedService',
            [
                'peer' => $this->chatId,
                'message' => [
                    '_' => 'decryptedMessageService',
                    'action' => ['_' => 'decryptedMessageActionScreenshotMessages', 'random_ids' => [$this->id]],
                ],
            ]
        );
        return $this->getClient()->wrapMessage($this->getClient()->extractMessage($result));
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Message\Service;

use danog\MadelineProto\EventHandler\Message\ServiceMessage;
use danog\MadelineProto\MTProto;

/**
 * A user was accepted into the group by an admin.
 */
final class DialogMemberJoinedByRequest extends ServiceMessage
{
    /** @internal */
    public function __construct(MTProto $API, array $rawMessage, array $info)
    {
        parent::__construct($API, $rawMessage, $info);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Message\Service;

use danog\MadelineProto\EventHandler\Message\ServiceMessage;
use danog\MadelineProto\EventHandler\Wallpaper;
use danog\MadelineProto\MTProto;

/**
 * The [wallpaper](https://core.telegram.org/api/wallpapers) of the current chat was changed.
 */
final class DialogSetChatWallPaper extends ServiceMessage
{
    public function __construct(
        MTProto $API,
        array $rawMessage,
        array $info,

        /** @var Wallpaper New [wallpaper](https://core.telegram.org/api/wallpapers) */
        public readonly Wallpaper $wallpaper,

        /** @var bool Whether the user applied a wallpaper previously sent by the other user in a DialogSetChatWallPaper message. */
        public readonly bool $same = false,

        /** @var bool Whether the wallpaper was set for both users of the private chat. */
        public readonly bool $forBoth = false,
    ) {
        parent::__construct($API, $rawMessage, $info);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Message\Service;

use danog\MadelineProto\EventHandler\Message\ServiceMessage;
use danog\MadelineProto\MTProto;

/**
 * Chat history was cleared.
 */
final class DialogHistoryCleared extends ServiceMessage
{
    /** @internal */
    public function __construct(MTProto $API, array $rawMessage, array $info)
    {
        parent::__construct($API, $rawMessage, $info);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Message\Service;

use danog\MadelineProto\EventHandler\Message\ServiceMessage;
use danog\MadelineProto\MTProto;

/**
 * Someone scored in a game.
 */
final class DialogGameScore extends ServiceMessage
{
    public function __construct(
        MTProto $API,
        array $rawMessage,
        array $info,

        /** @var int Game ID */
        public readonly int $gameId,

        /** @var int Score */
        public readonly int $score
    ) {
        parent::__construct($API, $rawMessage, $info);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Message\Service;

use danog\MadelineProto\EventHandler\Message\ServiceMessage;
use danog\MadelineProto\MTProto;

/**
 * The chat theme was changed.
 */
final class DialogSetChatTheme extends ServiceMessage
{
    /** @var ?string The emoji that identifies a chat theme, can be null for unique gift-based themes */
    public readonly ?string $emoticon;

    /** @var ?array https://docs.madelineproto.xyz/API_docs/constructors/chatThemeUniqueGift.html */
    public readonly ?array $uniqueGift;

    public function __construct(
        MTProto $API,
        array $rawMessage,
        array $info,
        array $theme,
    ) {
        parent::__construct($API, $rawMessage, $info);
        $this->emoticon = $theme['emoticon'] ?? null;
        // TODO: rm when all constructors are objects.
        if ($theme['_'] === 'chatThemeUniqueGift') {
            $this->uniqueGift = $theme;
        } else {
            $this->uniqueGift = null;
        }
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Message\Service;

use danog\MadelineProto\EventHandler\Message\ServiceMessage;
use danog\MadelineProto\MTProto;

/**
 * A chat or channel was created.
 */
final class DialogCreated extends ServiceMessage
{
    public function __construct(
        MTProto $API,
        array $rawMessage,
        array $info,

        /** @var string Title of the created chat or channel */
        public readonly string $title,

        /** @var list<int> List of group members */
        public readonly array $users,
    ) {
        parent::__construct($API, $rawMessage, $info);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Mahdi <mahdi.talaee1379@gmail.com>
 * @copyright 2016-2025 Mahdi <mahdi.talaee1379@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Message\Service;

use danog\MadelineProto\EventHandler\Message\ServiceMessage;
use danog\MadelineProto\MTProto;

/**
 * Emitted only in private chats when enabling or disabling content protection Â».
 */
class DialogNoForwardToggle extends ServiceMessage
{
    public function __construct(
        MTProto $API,
        array $rawMessage,
        array $info,

        /** @var bool Previous protection status (if true, the chat was protected). May be equal to new_value when replying to requests */
        public readonly bool $prev,

        /** @var bool New protection status. */
        public readonly bool $new
    ) {
        parent::__construct($API, $rawMessage, $info);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Mahdi <mahdi.talaee1379@gmail.com>
 * @copyright 2016-2025 Mahdi <mahdi.talaee1379@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Message\Service;

use danog\MadelineProto\EventHandler\Message\ServiceMessage;
use danog\MadelineProto\MTProto;

/**
 * Messages marked as read.
 */
final class DialogReadMessages extends ServiceMessage
{
    /** @internal */
    public function __construct(
        MTProto $API,
        array $rawMessage,
        array $info,

        /** @var list<int> $ids List of message IDs. */
        public readonly array $ids
    ) {
        parent::__construct($API, $rawMessage, $info);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Mahdi <mahdi.talaee1379@gmail.com>
 * @copyright 2016-2025 Mahdi <mahdi.talaee1379@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Message\Service;

use danog\MadelineProto\EventHandler\Message\ServiceMessage;
use danog\MadelineProto\MTProto;

/**
 * Emitted only in private chats if the other side requested to disable content protection Â».
 */
class DialogNoForwardRequest extends ServiceMessage
{
    public function __construct(
        MTProto $API,
        array $rawMessage,
        array $info,

        /** @var bool If set, this request was accepted or rejected by the other user and thus cannot be used anymore. */
        public readonly bool $expired,

        /** @var bool Previous protection status. */
        public readonly bool $prev,

        /** @var bool New requested protection status. */
        public readonly bool $new
    ) {
        parent::__construct($API, $rawMessage, $info);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Message\Service;

use danog\MadelineProto\EventHandler\Message\ServiceMessage;
use danog\MadelineProto\MTProto;

/**
 * The title of a channel or group has changed.
 */
final class DialogTitleChanged extends ServiceMessage
{
    public function __construct(
        MTProto $API,
        array $rawMessage,
        array $info,

        /** @var string New title */
        public readonly string $title
    ) {
        parent::__construct($API, $rawMessage, $info);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Mahdi <mahdi.talaee1379@gmail.com>
 * @copyright 2016-2025 Mahdi <mahdi.talaee1379@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Message\Service;

use danog\MadelineProto\EventHandler\Message\ServiceMessage;
use danog\MadelineProto\MTProto;

/**
 * Deleted messages.
 */
final class DialogDeleteMessages extends ServiceMessage
{
    public function __construct(
        MTProto $API,
        array $rawMessage,
        array $info,

        /** @var list<int> $ids List of deleted message IDs. */
        public readonly array $ids
    ) {
        parent::__construct($API, $rawMessage, $info);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Message\Service;

use danog\MadelineProto\EventHandler\Media\Photo;
use danog\MadelineProto\EventHandler\Message\ServiceMessage;
use danog\MadelineProto\MTProto;

/**
 * A new profile picture was suggested using [photos.uploadContactProfilePhoto](https://docs.madelineproto.xyz/API_docs/methods/photos.uploadContactProfilePhoto.html).
 */
final class DialogSuggestProfilePhoto extends ServiceMessage
{
    public function __construct(
        MTProto $API,
        array $rawMessage,
        array $info,

        /** @var ?Photo The photo that the user suggested we set as profile picture. */
        public readonly ?Photo $photo
    ) {
        parent::__construct($API, $rawMessage, $info);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Message\Service;

use danog\MadelineProto\EventHandler\Message\ServiceMessage;
use danog\MadelineProto\MTProto;

/**
 * Data from an opened [reply keyboard bot web app](https://core.telegram.org/api/bots/webapps) was relayed to the bot that owns it (user & bot side service message).
 * Clients should display a service message with the text Data from the Â«$textÂ» button was transferred to the bot.
 */
final class DialogWebView extends ServiceMessage
{
    public function __construct(
        MTProto $API,
        array $rawMessage,
        array $info,

        /** @var string Text of the [keyboardButtonSimpleWebView](https://docs.madelineproto.xyz/API_docs/constructors/keyboardButtonSimpleWebView.html) that was pressed to open the web app. */
        public readonly string $text,

        /** @var ?string Relayed data. (bot side service message) */
        public readonly ?string $data,
    ) {
        parent::__construct($API, $rawMessage, $info);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Message\Service\DialogGroupCall;

use danog\MadelineProto\EventHandler\Message\Service\DialogGroupCall;
use danog\MadelineProto\MTProto;

/**
 * The group call has started or ended.
 */
final class GroupCall extends DialogGroupCall
{
    /** Whether that group call ended or not. */
    public readonly bool $ended;

    public function __construct(
        MTProto $API,
        array $rawMessage,
        array $info,

        /** @var ?int Group call duration */
        public readonly ?int $duration
    ) {
        parent::__construct($API, $rawMessage, $info);
        $this->ended = (bool) $duration;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Message\Service\DialogGroupCall;

use danog\MadelineProto\EventHandler\Message\Service\DialogGroupCall;
use danog\MadelineProto\MTProto;

/**
 * A group call was scheduled.
 */
final class GroupCallScheduled extends DialogGroupCall
{
    public function __construct(
        MTProto $API,
        array $rawMessage,
        array $info,

        /** When is this group call scheduled to start */
        public readonly int $scheduleDate
    ) {
        parent::__construct($API, $rawMessage, $info);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Message\Service\DialogGroupCall;

use danog\MadelineProto\EventHandler\Message\Service\DialogGroupCall;
use danog\MadelineProto\MTProto;

/**
 * A set of users was invited to the group call.
 */
final class GroupCallInvited extends DialogGroupCall
{
    public function __construct(
        MTProto $API,
        array $rawMessage,
        array $info,

        /** The invited users */
        public readonly array $users
    ) {
        parent::__construct($API, $rawMessage, $info);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Message\Service;

use danog\MadelineProto\EventHandler\Message\ServiceMessage;
use danog\MadelineProto\MTProto;

/**
 * A user of the chat is now in proximity of another user.
 */
final class DialogGeoProximityReached extends ServiceMessage
{
    public function __construct(
        MTProto $API,
        array $rawMessage,
        array $info,

        /** @var int The user or chat that is now in proximity of to_id */
        public readonly int $fromId,

        /** @var int The user or chat that subscribed to [live geolocation proximity alerts](https://core.telegram.org/api/live-location#proximity-alert) */
        public readonly int $toId,

        /** @var int Distance, in meters (0-100000) */
        public readonly int $distance
    ) {
        parent::__construct($API, $rawMessage, $info);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Message\Service;

use danog\MadelineProto\EventHandler\Message\ServiceMessage;
use danog\MadelineProto\MTProto;

/**
 * The Time-To-Live of messages in this chat was changed.
 */
final class DialogSetTTL extends ServiceMessage
{
    public function __construct(
        MTProto $API,
        array $rawMessage,
        array $info,

        /** @var int New Time-To-Live of all messages sent in this chat; if 0, autodeletion was disabled. */
        public readonly int $period,

        /** @var ?int If set, the chat TTL setting was set not due to a manual change by one of participants, but automatically because one of the participants has the [default TTL settings enabled Â»](https://docs.madelineproto.xyz/API_docs/methods/messages.setDefaultHistoryTTL.html). For example, when a user writes to us for the first time and we have set a default messages TTL of 1 week, this service message (with auto_setting_from=our_userid) will be emitted before our first message. */
        public readonly ?int $autoSettingFrom
    ) {
        parent::__construct($API, $rawMessage, $info);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Message\Service;

use danog\MadelineProto\EventHandler\AbstractMessage;
use danog\MadelineProto\EventHandler\Message\ServiceMessage;
use danog\MadelineProto\MTProto;

/**
 * A message was pinned in a chat.
 */
final class DialogMessagePinned extends ServiceMessage
{
    /** @internal */
    public function __construct(
        MTProto $API,
        array $rawMessage,
        array $info,
    ) {
        parent::__construct($API, $rawMessage, $info);
    }

    /**
     * Gets the pinned message.
     *
     * May return null if the pinned message was deleted.
     *
     * @template T as AbstractMessage
     *
     * @param class-string<T> $class Only return a reply if it is of the specified type, return null otherwise.
     *
     * @return ?T
     */
    public function getPinnedMessage(string $class = AbstractMessage::class): ?AbstractMessage
    {
        return $this->getReply($class);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Message\Service;

use danog\MadelineProto\EventHandler\Message\ServiceMessage;
use danog\MadelineProto\MTProto;

/**
 * Contains info about a peer that the user shared with the bot after clicking on a [keyboardButtonRequestPeer](https://docs.madelineproto.xyz/API_docs/constructors/keyboardButtonRequestPeer.html) button.
 */
final class DialogPeerRequested extends ServiceMessage
{
    /** @internal */
    public function __construct(
        MTProto $API,
        array $rawMessage,
        array $info,

        /** @var int buttonId contained in the [keyboardButtonRequestPeer](https://docs.madelineproto.xyz/API_docs/constructors/keyboardButtonRequestPeer.html) */
        public readonly int $buttonId,

        /** @var list<int> The shared peers */
        public readonly array $peers
    ) {
        parent::__construct($API, $rawMessage, $info);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Mahdi <mahdi.talaee1379@gmail.com>
 * @copyright 2016-2025 Mahdi <mahdi.talaee1379@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Message\Service;

use danog\MadelineProto\EventHandler\Message\ServiceMessage;
use danog\MadelineProto\MTProto;

/**
 * A payment was sent.
 */
final class DialogPaymentSent extends ServiceMessage
{
    public function __construct(
        MTProto $API,
        array $rawMessage,
        array $info,
        /** Whether this is the first payment of a recurring payment we just subscribed to */
        public readonly ?bool $recurringInit,
        /** Whether this payment is part of a recurring payment */
        public readonly ?bool $recurringUsed,
        /** Three-letter ISO 4217 currency code */
        public readonly string $currency,
        /** Price of the product in the smallest units of the currency  */
        public readonly int $totalAmount,
        /** An invoice slug taken from an invoice deep link or from the premium_invoice_slug app config parameter */
        public readonly ?string $invoiceSlug,
        /** The date that subscription has been ended */
        public readonly ?int $subscriptionUntilDate
    ) {
        parent::__construct($API, $rawMessage, $info);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Message\Service;

use danog\MadelineProto\EventHandler\BotApp;
use danog\MadelineProto\EventHandler\Message\ServiceMessage;
use danog\MadelineProto\MTProto;

/**
 * We have given the bot permission to send us direct messages.
 * The optional fields specify how did we authorize the bot to send us messages.
 */
final class DialogBotAllowed extends ServiceMessage
{
    /** We have authorized the bot to send us messages by installing the botâ€™s [attachment menu](https://core.telegram.org/api/bots/attach). */
    public readonly bool $attachMenu;

    /** We have authorized the bot to send us messages by logging into a website via [Telegram Login Â»](https://core.telegram.org/widgets/login); this field contains the domain name of the website on which the user has logged in. */
    public readonly ?string $domain;

    /** We have authorized the bot to send us messages by opening the specified [bot web app](https://core.telegram.org/api/bots/webapps). */
    public readonly ?BotApp $app;

    /** @internal */
    public function __construct(MTProto $API, array $rawMessage, array $info)
    {
        parent::__construct($API, $rawMessage, $info);
        $this->attachMenu = $rawMessage['action']['attach_menu'];
        $this->domain = $rawMessage['action']['domain'] ?? null;
        $this->app = isset($rawMessage['action']['app']) ? new BotApp($API, $rawMessage['action']['app']) : null;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Message\Service;

use danog\MadelineProto\EventHandler\Message\ServiceMessage;
use danog\MadelineProto\MTProto;

/**
 * Indicates the chat was [migrated](https://core.telegram.org/api/channel) to the specified supergroup.
 */
final class DialogChatMigrateTo extends ServiceMessage
{
    public function __construct(
        MTProto $API,
        array $rawMessage,
        array $info,

        /** @var int The supergroup it was migrated to */
        public readonly int $channelId
    ) {
        parent::__construct($API, $rawMessage, $info);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Mahdi <mahdi.talaee1379@gmail.com>
 * @copyright 2016-2025 Mahdi <mahdi.talaee1379@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Message\Service;

use danog\MadelineProto\EventHandler\Message\ServiceMessage;
use danog\MadelineProto\MTProto;

/**
 * Info about a gifted Telegram Stars.
 */
final class DialogGiftStars extends ServiceMessage
{
    /** @internal */
    public function __construct(
        MTProto $API,
        array $rawMessage,
        array $info,
        /** Three-letter ISO 4217 currency code */
        public readonly string $currency,
        /** Price of the gift in the smallest units of the currency (integer, not float/double). */
        public readonly int $amount,
        /** Amount of gifted stars */
        public readonly int $stars,
        /** If the gift was bought using a cryptocurrency, the cryptocurrency name. */
        public readonly ?string $cryptoCurrency,
        /** If the gift was bought using a cryptocurrency, price of the gift in the smallest units of a cryptocurrency. */
        public readonly ?int $cryptoAmount,
        /** Identifier of the transaction, only visible to the receiver of the gift. */
        public readonly string $transactionId,
    ) {
        parent::__construct($API, $rawMessage, $info);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Message\Service;

use danog\MadelineProto\EventHandler\Message\ServiceMessage;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\VoIP\DiscardReason;

/**
 * A phone call.
 */
final class DialogPhoneCall extends ServiceMessage
{
    public function __construct(
        MTProto $API,
        array $rawMessage,
        array $info,

        /** @var bool Is this a video call? */
        public readonly bool $video,

        /** @var int Call ID */
        public readonly int $callId,

        /** @var ?DiscardReason If the call has ended, the reason why it ended */
        public readonly ?DiscardReason $reason,

        /** @var ?int Duration of the call in seconds */
        public readonly ?int $duration,
    ) {
        parent::__construct($API, $rawMessage, $info);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Message\Service;

use danog\MadelineProto\EventHandler\Message\ServiceMessage;
use danog\MadelineProto\MTProto;

/**
 * A screenshot of the chat was taken.
 */
final class DialogScreenshotTaken extends ServiceMessage
{
    public function __construct(
        MTProto $API,
        array $rawMessage,
        array $info,

        /** @var list<int> List of affected message ids that appeared on the screenshot, only for secret chats. */
        public readonly array $ids = [],
    ) {
        parent::__construct($API, $rawMessage, $info);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Message\Service;

use danog\MadelineProto\EventHandler\Message\ServiceMessage;
use danog\MadelineProto\MTProto;

/**
 * Info about a gifted Telegram Premium subscription.
 */
final class DialogGiftPremium extends ServiceMessage
{
    public function __construct(
        MTProto $API,
        array $rawMessage,
        array $info,

        /** @var string Three-letter ISO 4217 [currency](https://core.telegram.org/bots/payments#supported-currencies) code */
        public readonly string $currency,

        /** @var int Price of the gift in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45 pass amount = 145. See the exp parameter in [currencies.json](https://core.telegram.org/bots/payments/currencies.json), it shows the number of digits past the decimal point for each currency (2 for the majority of currencies). */
        public readonly int $amount,

        /** @var int Duration of the gifted Telegram Premium subscription */
        public readonly int $months,

        /** @var ?int If the gift was bought using a cryptocurrency, the cryptocurrency name. */
        public readonly ?int $cryptoCurrency,

        /** @var ?int If the gift was bought using a cryptocurrency, price of the gift in the smallest units of a cryptocurrency. */
        public readonly ?int $cryptoAmount
    ) {
        parent::__construct($API, $rawMessage, $info);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Message\Service;

use danog\MadelineProto\EventHandler\Calls\ConferenceCall;
use danog\MadelineProto\EventHandler\Message\ServiceMessage;
use danog\MadelineProto\MTProto;

/**
 * A service message about an [end-to-end encrypted conference call Â»](https://core.telegram.org/api/group-calls#conference-calls):
 * an invitation to one (incoming or outgoing), a missed one, or one that ended.
 */
final class DialogConferenceCall extends ServiceMessage
{
    /**
     * @internal
     *
     * @param list<int> $otherParticipants
     */
    public function __construct(
        MTProto $API,
        array $rawMessage,
        array $info,
        /** ID of the conference call. */
        public readonly int $callId,
        /** Whether we missed the call. */
        public readonly bool $missed,
        /** Whether the call is still active. */
        public readonly bool $active,
        /** Whether this is a video call. */
        public readonly bool $video,
        /** Duration of the call in seconds, if it ended. */
        public readonly ?int $duration,
        /**
         * Bot API IDs of the other participants of the call.
         *
         * @var list<int>
         */
        public readonly array $otherParticipants,
    ) {
        parent::__construct($API, $rawMessage, $info);
    }

    /**
     * Accept the invitation: join the conference call.
     *
     * @param bool $muted Whether to join muted.
     */
    public function join(bool $muted = false): ConferenceCall
    {
        return $this->getClient()->joinConferenceCallByInviteMessage($this->id, $muted);
    }

    /**
     * Decline the invitation.
     */
    public function decline(): void
    {
        $this->getClient()->declineConferenceCallInvite($this->id);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Message\Service;

use danog\MadelineProto\EventHandler\Message\ServiceMessage;
use danog\MadelineProto\MTProto;

/**
 * Indicates the channel was [migrated](https://core.telegram.org/api/channel) from the specified chat.
 */
final class DialogChannelMigrateFrom extends ServiceMessage
{
    public function __construct(
        MTProto $API,
        array $rawMessage,
        array $info,

        /** @var string The old chat title */
        public readonly string $oldTitle,

        /** @var int The old chat title */
        public readonly int $oldChatId,
    ) {
        parent::__construct($API, $rawMessage, $info);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Mahdi <mahdi.talaee1379@gmail.com>
 * @copyright 2016-2025 Mahdi <mahdi.talaee1379@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Message\Service;

use danog\MadelineProto\EventHandler\Message\Entities\TextWithEntities;
use danog\MadelineProto\EventHandler\Message\ServiceMessage;
use danog\MadelineProto\EventHandler\Payments\StarGift;
use danog\MadelineProto\MTProto;

/**
 * Info about a Star gifted.
 */
final class DialogStarGift extends ServiceMessage
{
    /** @internal */
    public function __construct(
        MTProto $API,
        array $rawMessage,
        array $info,
        /** Show the name of sender hide or no */
        public readonly ?bool $hide,
        /** Show the gift is saved on profile or no */
        public readonly ?bool $saved,
        /** Show the gift is converted to stars or no */
        public readonly ?bool $converted,
        /** The gift */
        public readonly StarGift $gift,
        /** Styled text that sender of gift provided */
        public readonly ?TextWithEntities $message,
        /** Amount of stars after the gift converted */
        public readonly ?int $convertStars
    ) {
        parent::__construct($API, $rawMessage, $info);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Message\Service;

use danog\MadelineProto\EventHandler\Message\ServiceMessage;
use danog\MadelineProto\MTProto;

/**
 * [Forum topic](https://core.telegram.org/api/forum#forum-topics) information was edited.
 */
final class DialogTopicEdited extends ServiceMessage
{
    public function __construct(
        MTProto $API,
        array $rawMessage,
        array $info,

        /**
         * If not null, indicates that the topic name has changed, contains the new topic name.
         *
         * Ignore this field if null.
         */
        public readonly ?string $title,

        /**
         * If not null, indicates that the topic icon has changed, and contains the ID of the new [custom emoji](https://core.telegram.org/api/custom-emoji) used as topic icon (0 if it was removed).
         *
         * Ignore this field if null.
         */
        public readonly ?int $iconEmojiId,

        /**
         * If not null, indicates whether the topic was opened or closed.
         *
         * Ignore this field if null.
         */
        public readonly ?bool $closed,

        /**
         * If not null, indicates whether the topic was hidden or unhidden (only valid for the â€œGeneralâ€� topic, id=1).
         *
         * Ignore this field if null.
         */
        public readonly ?bool $hidden
    ) {
        parent::__construct($API, $rawMessage, $info);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Message\Service;

use danog\MadelineProto\EventHandler\Message\ServiceMessage;
use danog\MadelineProto\MTProto;

/**
 * A user joined the chat via an invite link.
 */
final class DialogChatJoinedByLink extends ServiceMessage
{
    public function __construct(
        MTProto $API,
        array $rawMessage,
        array $info,

        /** @var int ID of the user that created the invite link */
        public readonly int $inviterId
    ) {
        parent::__construct($API, $rawMessage, $info);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Message\Service;

use danog\MadelineProto\EventHandler\Media\Photo;
use danog\MadelineProto\EventHandler\Message\ServiceMessage;
use danog\MadelineProto\MTProto;

/**
 * The photo of the dialog was changed or deleted.
 */
final class DialogPhotoChanged extends ServiceMessage
{
    public function __construct(
        MTProto $API,
        array $rawMessage,
        array $info,

        /** @var ?Photo New photo (or no photo if it was deleted) */
        public readonly ?Photo $photo
    ) {
        parent::__construct($API, $rawMessage, $info);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Message\Service;

use danog\MadelineProto\EventHandler\Message\ServiceMessage;
use danog\MadelineProto\MTProto;

/**
 * Some members joined the chat or channel.
 */
final class DialogMembersJoined extends ServiceMessage
{
    public function __construct(
        MTProto $API,
        array $rawMessage,
        array $info,

        /** @var list<int> List of IDs of the users that joined the chat or channel. */
        public readonly array $joined
    ) {
        parent::__construct($API, $rawMessage, $info);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Message\Service;

use danog\MadelineProto\EventHandler\Message\ServiceMessage;
use danog\MadelineProto\MTProto;

/**
 * A member left the chat or channel.
 */
final class DialogMemberLeft extends ServiceMessage
{
    public function __construct(
        MTProto $API,
        array $rawMessage,
        array $info,

        /**  @var int ID of the user that left the channel */
        public readonly int $left,
    ) {
        parent::__construct($API, $rawMessage, $info);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Message\Service;

use danog\MadelineProto\EventHandler\Message\ServiceMessage;
use danog\MadelineProto\MTProto;

/**
 * A [forum topic](https://core.telegram.org/api/forum#forum-topics) was created.
 */
final class DialogTopicCreated extends ServiceMessage
{
    public function __construct(
        MTProto $API,
        array $rawMessage,
        array $info,

        /** @var string Topic name. */
        public readonly string $title,

        /** @var int If no custom emoji icon is specified, specifies the color of the fallback topic icon (RGB), one of 0x6FB9F0, 0xFFD67E, 0xCB86DB, 0x8EEE98, 0xFF93B2, or 0xFB6F5F. */
        public readonly int $iconColor,

        /** @var ?int ID of the [custom emoji](https://core.telegram.org/api/custom-emoji) used as topic icon. */
        public readonly ?int $iconEmojiId,
    ) {
        parent::__construct($API, $rawMessage, $info);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Message\Service;

use danog\MadelineProto\EventHandler\Message\ServiceMessage;
use danog\MadelineProto\MTProto;

/**
 * A contact just signed up to telegram.
 */
final class DialogContactSignUp extends ServiceMessage
{
    /** @internal */
    public function __construct(MTProto $API, array $rawMessage, array $info)
    {
        parent::__construct($API, $rawMessage, $info);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Message\Service;

use danog\MadelineProto\EventHandler\Message\ServiceMessage;
use danog\MadelineProto\MTProto;

/**
 * Represents a service message about a group call.
 */
abstract class DialogGroupCall extends ServiceMessage
{
    /** Group call ID */
    public readonly int $callId;

    /** Group call access hash */
    public readonly int $accessHash;

    /** @internal */
    public function __construct(MTProto $API, array $rawMessage, array $info)
    {
        parent::__construct($API, $rawMessage, $info);
        $this->callId = $rawMessage['action']['call']['id'];
        $this->accessHash = $rawMessage['action']['call']['access_hash'];
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Message\Service;

use danog\MadelineProto\EventHandler\Message\ServiceMessage;
use danog\MadelineProto\MTProto;

/**
 * The channel was created.
 */
final class DialogChannelCreated extends ServiceMessage
{
    public function __construct(
        MTProto $API,
        array $rawMessage,
        array $info,

        /** @var string Original channel/supergroup title */
        public readonly string $title
    ) {
        parent::__construct($API, $rawMessage, $info);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Mahdi <mahdi.talaee1379@gmail.com>
 * @copyright 2016-2025 Mahdi <mahdi.talaee1379@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Message\Service;

use danog\MadelineProto\EventHandler\Message\ServiceMessage;
use danog\MadelineProto\EventHandler\Payments\PaymentCharge;
use danog\MadelineProto\EventHandler\Payments\PaymentRequestedInfo;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\TL\Types\Bytes;

/**
 * A user just sent a payment to me (a bot).
 */
final class DialogPaymentSentMe extends ServiceMessage
{
    public function __construct(
        MTProto $API,
        array $rawMessage,
        array $info,
        /** Whether this is the first payment of a recurring payment we just subscribed to */
        public readonly ?bool $recurringInit,
        /** Whether this payment is part of a recurring payment */
        public readonly ?bool $recurringUsed,
        /** Three-letter ISO 4217 currency code */
        public readonly string $currency,
        /** Price of the product in the smallest units of the currency  */
        public readonly int $totalAmount,
        /** Bot specified invoice payload */
        public readonly Bytes $payload,
        /** Order info provided by the user */
        public readonly ?PaymentRequestedInfo $paymentInfo,
        /** Identifier of the shipping option chosen by the user */
        public readonly ?string $shippingOptionId,
        /** Provider payment identifier */
        public readonly PaymentCharge $charge,
        /** The date that subscription has been ended */
        public readonly ?int $subscriptionUntilDate
    ) {
        parent::__construct($API, $rawMessage, $info);
    }

    public function refundStars(
        int $userId
    ): void {
        $this->getClient()->methodCallAsyncRead(
            'payments.refundStarsCharge',
            [
                'user_id' => $userId,
                'charge_id' => $this->charge->id,
            ]
        );
    }
}
<?php declare(strict_types=1);

namespace danog\MadelineProto\EventHandler\Message;

enum ReportReason: string
{
    /** Report for spam */
    case SPAM = 'inputReportReasonSpam';
    /** Report for violence */
    case VIOLENCE = 'inputReportReasonViolence';
    /** Report for pornography */
    case PORNOGRAPHY = 'inputReportReasonPornography';
    /** Report for child abuse */
    case CHILD_ABUSE = 'inputReportReasonChildAbuse';
    /** Report for copyrighted content */
    case COPYRIGHT = 'inputReportReasonCopyright';
    /** Report an irrelevant geogroup */
    case GEO_IRRELEVANT = 'inputReportReasonGeoIrrelevant';
    /** Report for impersonation */
    case FAKE = 'inputReportReasonFake';
    /** Report for illegal drugs */
    case ILLEGAL_DRUGS = 'inputReportReasonIllegalDrugs';
    /** Report for divulgation of personal details */
    case PERSONAL_DETAILS = 'inputReportReasonPersonalDetails';
    /** Other */
    case OTHER = 'inputReportReasonOther';
}
<?php declare(strict_types=1);

namespace danog\MadelineProto\EventHandler\Message\Entities;

/**
 * Message entity representing italic text.
 */
final class Italic extends MessageEntity
{
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toBotAPI(): array
    {
        return ['type' => 'italic', 'offset' => $this->offset, 'length' => $this->length];
    }
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toMTProto(): array
    {
        return ['_' => 'messageEntityItalic', 'offset' => $this->offset, 'length' => $this->length];
    }
}
<?php declare(strict_types=1);

namespace danog\MadelineProto\EventHandler\Message\Entities;

/**
 * Message entity representing a block quote.
 */
final class Blockquote extends MessageEntity
{
    /**
     * @psalm-mutation-free
     */
    public function __construct(
        /** Offset of message entity within message (in UTF-16 code units) */
        public readonly int $offset,

        /** Length of message entity within message (in UTF-16 code units) */
        public readonly int $length,

        /** Whether the quote is collapsed by default (an expandable block quote) */
        public readonly bool $collapsed = false
    ) {
    }

    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toBotAPI(): array
    {
        return ['type' => $this->collapsed ? 'expandable_blockquote' : 'blockquote', 'offset' => $this->offset, 'length' => $this->length];
    }
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toMTProto(): array
    {
        return ['_' => 'messageEntityBlockquote', 'collapsed' => $this->collapsed, 'offset' => $this->offset, 'length' => $this->length];
    }
}
<?php declare(strict_types=1);

namespace danog\MadelineProto\EventHandler\Message\Entities;

/**
 * Represents a custom emoji.
 */
final class CustomEmoji extends MessageEntity
{
    /**
     * @psalm-mutation-free
     */
    public function __construct(
        /** Offset of message entity within message (in UTF-16 code units) */
        public readonly int $offset,

        /** Length of message entity within message (in UTF-16 code units) */
        public readonly int $length,

        /** Document ID of the [custom emoji](https://core.telegram.org/api/custom-emoji). */
        public readonly int $documentId
    ) {
    }

    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toBotAPI(): array
    {
        return ['type' => 'custom_emoji', 'offset' => $this->offset, 'length' => $this->length, 'custom_emoji_id' => $this->documentId];
    }
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toMTProto(): array
    {
        return ['_' => 'messageEntityCustomEmoji', 'offset' => $this->offset, 'length' => $this->length, 'document_id' => $this->documentId];
    }
}
<?php declare(strict_types=1);

namespace danog\MadelineProto\EventHandler\Message\Entities;

/**
 * Message entity representing a preformatted codeblock, allowing the user to specify a programming language for the codeblock.
 */
final class Pre extends MessageEntity
{
    /**
     * @psalm-mutation-free
     */
    public function __construct(
        /** Offset of message entity within message (in UTF-16 code units) */
        public readonly int $offset,

        /** Length of message entity within message (in UTF-16 code units) */
        public readonly int $length,

        /** Programming language of the code */
        public readonly string $language
    ) {
    }

    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toBotAPI(): array
    {
        $res = ['type' => 'pre', 'offset' => $this->offset, 'length' => $this->length];
        if ($this->language !== '') {
            $res['language'] = $this->language;
        }
        return $res;
    }
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toMTProto(): array
    {
        return ['_' => 'messageEntityPre', 'offset' => $this->offset, 'length' => $this->length, 'language' => $this->language];
    }
}
<?php declare(strict_types=1);

namespace danog\MadelineProto\EventHandler\Message\Entities;

/**
 * Indicates a credit card number.
 */
final class BankCard extends MessageEntity
{
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toBotAPI(): array
    {
        return ['type' => 'bank_card_number', 'offset' => $this->offset, 'length' => $this->length];
    }
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toMTProto(): array
    {
        return ['_' => 'messageEntityBankCard', 'offset' => $this->offset, 'length' => $this->length];
    }
}
<?php declare(strict_types=1);

namespace danog\MadelineProto\EventHandler\Message\Entities;

/**
 * Message entity representing bold text.
 */
final class Bold extends MessageEntity
{
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toBotAPI(): array
    {
        return ['type' => 'bold', 'offset' => $this->offset, 'length' => $this->length];
    }
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toMTProto(): array
    {
        return ['_' => 'messageEntityBold', 'offset' => $this->offset, 'length' => $this->length];
    }
}
<?php declare(strict_types=1);

namespace danog\MadelineProto\EventHandler\Message\Entities;

/**
 * Message entity representing a spoiler.
 */
final class Spoiler extends MessageEntity
{
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toBotAPI(): array
    {
        return ['type' => 'spoiler', 'offset' => $this->offset, 'length' => $this->length];
    }
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toMTProto(): array
    {
        return ['_' => 'messageEntitySpoiler', 'offset' => $this->offset, 'length' => $this->length];
    }
}
<?php declare(strict_types=1);

namespace danog\MadelineProto\EventHandler\Message\Entities;

use AssertionError;
use JsonSerializable;
use ReflectionClass;
use ReflectionProperty;

/**
 * Master class for message entities.
 */
abstract class MessageEntity implements JsonSerializable
{
    /**
     * @psalm-mutation-free
     */
    public function __construct(
        /** Offset of message entity within message (in UTF-16 code units) */
        public readonly int $offset,

        /** Length of message entity within message (in UTF-16 code units) */
        public readonly int $length
    ) {
    }

    /**
     * @param list<array> $entities
     *
     * @return list<self>
     *
     * @psalm-pure
     */
    public static function fromRawEntities(array $entities): array
    {
        return array_map(self::fromRawEntity(...), $entities);
    }

    /**
     * @psalm-pure
     */
    public static function fromRawEntity(array|self $entity): self
    {
        if ($entity instanceof self) {
            return $entity;
        }
        return match ($entity['_'] ?? $entity['type']) {
            'mention', 'messageEntityMention' => new Mention($entity['offset'], $entity['length']),
            'hashtag', 'messageEntityHashtag' => new Hashtag($entity['offset'], $entity['length']),
            'bot_command', 'messageEntityBotCommand' => new BotCommand($entity['offset'], $entity['length']),
            'email', 'messageEntityEmail' => new Email($entity['offset'], $entity['length']),
            'bold', 'messageEntityBold' => new Bold($entity['offset'], $entity['length']),
            'italic', 'messageEntityItalic' => new Italic($entity['offset'], $entity['length']),
            'url', 'messageEntityUrl' => new Url($entity['offset'], $entity['length']),
            'code', 'messageEntityCode' => new Code($entity['offset'], $entity['length']),
            /** @psalm-suppress MixedArgument */
            'pre', 'messageEntityPre' => new Pre($entity['offset'], $entity['length'], $entity['language'] ?? ''),
            'text_link', 'messageEntityTextUrl' => new TextUrl($entity['offset'], $entity['length'], $entity['url']),
            'text_mention', 'messageEntityMentionName' => new MentionName($entity['offset'], $entity['length'], $entity['user_id'] ?? $entity['user']['id']),
            'inputMessageEntityMentionName' => new InputMentionName($entity['offset'], $entity['length'], $entity['user_id']),
            'phone_number', 'messageEntityPhone' => new Phone($entity['offset'], $entity['length']),
            'cashtag', 'messageEntityCashtag' => new Cashtag($entity['offset'], $entity['length']),
            'underline', 'messageEntityUnderline' => new Underline($entity['offset'], $entity['length']),
            'strikethrough', 'messageEntityStrike' => new Strike($entity['offset'], $entity['length']),
            // block_quote and bank_card are the names used before Bot API 10
            'blockquote', 'block_quote' => new Blockquote($entity['offset'], $entity['length']),
            'expandable_blockquote' => new Blockquote($entity['offset'], $entity['length'], true),
            'messageEntityBlockquote' => new Blockquote($entity['offset'], $entity['length'], $entity['collapsed'] ?? false),
            'bank_card_number', 'bank_card', 'messageEntityBankCard' => new BankCard($entity['offset'], $entity['length']),
            'spoiler', 'messageEntitySpoiler' => new Spoiler($entity['offset'], $entity['length']),
            'custom_emoji', 'messageEntityCustomEmoji' => new CustomEmoji($entity['offset'], $entity['length'], $entity['document_id'] ?? $entity['custom_emoji_id']),
            /** @psalm-suppress MixedArgument */
            'date_time' => FormattedDate::fromBotAPIFormat($entity['offset'], $entity['length'], $entity['unix_time'], $entity['date_time_format'] ?? ''),
            'messageEntityFormattedDate' => new FormattedDate(
                $entity['offset'],
                $entity['length'],
                $entity['date'],
                $entity['relative'] ?? false,
                $entity['short_time'] ?? false,
                $entity['long_time'] ?? false,
                $entity['short_date'] ?? false,
                $entity['long_date'] ?? false,
                $entity['day_of_week'] ?? false,
            ),
            'unknown', 'messageEntityUnknown' => new Unknown($entity['offset'], $entity['length']),
            // Entity types added in newer layers must not prevent wrapping the message they're in.
            default => isset($entity['_']) && str_starts_with($entity['_'], 'messageEntity')
                ? new Unknown($entity['offset'], $entity['length'], $entity)
                : throw new AssertionError("Unknown entity type: ".($entity['_'] ?? $entity['type']))
        };
    }

    /**
     * Convert entity to bot API entity.
     * @psalm-mutation-free
     */
    abstract public function toBotAPI(): array;
    /**
     * Convert entity to MTProto entity.
     * @psalm-mutation-free
     */
    abstract public function toMTProto(): array;

    /** @internal */
    #[\Override]
    public function jsonSerialize(): mixed
    {
        $res = ['_' => static::class];
        $refl = new ReflectionClass($this);
        foreach ($refl->getProperties(ReflectionProperty::IS_PUBLIC) as $prop) {
            $res[$prop->getName()] = $prop->getValue($this);
        }
        return $res;
    }
}
<?php declare(strict_types=1);

namespace danog\MadelineProto\EventHandler\Message\Entities;

/**
 * Message entity of a type not (yet) supported by MadelineProto, or an explicitly unknown entity (messageEntityUnknown).
 *
 * Telegram adds new entity types in new layers: they're wrapped in this class instead of failing to wrap the whole message.
 */
final class Unknown extends MessageEntity
{
    /**
     * @psalm-mutation-free
     */
    public function __construct(
        /** Offset of message entity within message (in UTF-16 code units) */
        public readonly int $offset,

        /** Length of message entity within message (in UTF-16 code units) */
        public readonly int $length,

        /** Raw MTProto entity */
        public readonly array $raw = ['_' => 'messageEntityUnknown'],
    ) {
    }

    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toBotAPI(): array
    {
        return ['type' => 'unknown', 'offset' => $this->offset, 'length' => $this->length];
    }
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toMTProto(): array
    {
        return [...$this->raw, 'offset' => $this->offset, 'length' => $this->length];
    }
}
<?php declare(strict_types=1);

namespace danog\MadelineProto\EventHandler\Message\Entities;

/**
 * Message entity mentioning the current user.
 */
final class Mention extends MessageEntity
{
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toBotAPI(): array
    {
        return ['type' => 'mention', 'offset' => $this->offset, 'length' => $this->length];
    }
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toMTProto(): array
    {
        return ['_' => 'messageEntityMention', 'offset' => $this->offset, 'length' => $this->length];
    }
}
<?php declare(strict_types=1);

namespace danog\MadelineProto\EventHandler\Message\Entities;

/**
 * Message entity representing a text url: for in-text urls like https://google.com use Url.
 */
final class TextUrl extends MessageEntity
{
    /**
     * @psalm-mutation-free
     */
    public function __construct(
        /** Offset of message entity within message (in UTF-16 code units) */
        public readonly int $offset,

        /** Length of message entity within message (in UTF-16 code units) */
        public readonly int $length,

        /** The actual URL */
        public readonly string $url
    ) {
    }

    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toBotAPI(): array
    {
        return ['type' => 'text_link', 'offset' => $this->offset, 'length' => $this->length, 'url' => $this->url];
    }
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toMTProto(): array
    {
        return ['_' => 'messageEntityTextUrl', 'offset' => $this->offset, 'length' => $this->length, 'url' => $this->url];
    }
}
<?php declare(strict_types=1);

namespace danog\MadelineProto\EventHandler\Message\Entities;

/**
 * Message entity representing a [user mention](https://core.telegram.org/api/mentions) created by the user, not returned by the API.
 */
final class InputMentionName extends MessageEntity
{
    /**
     * @psalm-mutation-free
     */
    public function __construct(
        /** Offset of message entity within message (in UTF-16 code units) */
        public readonly int $offset,

        /** Length of message entity within message (in UTF-16 code units) */
        public readonly int $length,

        /** Identifier of the user that was mentioned */
        public readonly int|string $userId
    ) {
    }

    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toBotAPI(): array
    {
        return ['type' => 'text_mention', 'offset' => $this->offset, 'length' => $this->length, 'user' => ['id' => $this->userId]];
    }
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toMTProto(): array
    {
        return ['_' => 'inputMessageEntityMentionName', 'offset' => $this->offset, 'length' => $this->length, 'user_id' => $this->userId];
    }
}
<?php declare(strict_types=1);

namespace danog\MadelineProto\EventHandler\Message\Entities;

/**
 * Message entity representing an email@example.com.
 */
final class Email extends MessageEntity
{
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toBotAPI(): array
    {
        return ['type' => 'email', 'offset' => $this->offset, 'length' => $this->length];
    }
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toMTProto(): array
    {
        return ['_' => 'messageEntityEmail', 'offset' => $this->offset, 'length' => $this->length];
    }
}
<?php declare(strict_types=1);

namespace danog\MadelineProto\EventHandler\Message\Entities;

/**
 * Message entity representing a [user mention](https://core.telegram.org/api/mentions).
 */
final class MentionName extends MessageEntity
{

    /**
     * @psalm-mutation-free
     */
    public function __construct(
        /** Offset of message entity within message (in UTF-16 code units) */
        public readonly int $offset,

        /** Length of message entity within message (in UTF-16 code units) */
        public readonly int $length,

        /** Identifier of the user that was mentioned */
        public readonly int $userId
    ) {
    }

    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toBotAPI(): array
    {
        return ['type' => 'text_mention', 'offset' => $this->offset, 'length' => $this->length, 'user' => ['id' => $this->userId]];
    }
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toMTProto(): array
    {
        return ['_' => 'messageEntityMentionName', 'offset' => $this->offset, 'length' => $this->length, 'user_id' => $this->userId];
    }
}
<?php declare(strict_types=1);

namespace danog\MadelineProto\EventHandler\Message\Entities;

/**
 * Message entity representing a phone number.
 */
final class Phone extends MessageEntity
{
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toBotAPI(): array
    {
        return ['type' => 'phone_number', 'offset' => $this->offset, 'length' => $this->length];
    }
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toMTProto(): array
    {
        return ['_' => 'messageEntityPhone', 'offset' => $this->offset, 'length' => $this->length];
    }
}
<?php declare(strict_types=1);

namespace danog\MadelineProto\EventHandler\Message\Entities;

/**
 * Message entity representing underlined text.
 */
final class Underline extends MessageEntity
{
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toBotAPI(): array
    {
        return ['type' => 'underline', 'offset' => $this->offset, 'length' => $this->length];
    }
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toMTProto(): array
    {
        return ['_' => 'messageEntityUnderline', 'offset' => $this->offset, 'length' => $this->length];
    }
}
<?php declare(strict_types=1);

namespace danog\MadelineProto\EventHandler\Message\Entities;

use AssertionError;

/**
 * Message entity representing a date and time, displayed by clients in the local time zone and format of the user.
 */
final class FormattedDate extends MessageEntity
{
    /**
     * @psalm-mutation-free
     */
    public function __construct(
        /** Offset of message entity within message (in UTF-16 code units) */
        public readonly int $offset,

        /** Length of message entity within message (in UTF-16 code units) */
        public readonly int $length,

        /** Unix timestamp of the date */
        public readonly int $date,

        /** Whether to show the date relative to the current time (e.g. "in 5 minutes"), ignoring the other flags */
        public readonly bool $relative = false,

        /** Whether to show the time in short format (e.g. 22:45) */
        public readonly bool $shortTime = false,

        /** Whether to show the time in long format (e.g. 22:45:00) */
        public readonly bool $longTime = false,

        /** Whether to show the date in short format (e.g. 17.03.22) */
        public readonly bool $shortDate = false,

        /** Whether to show the date in long format (e.g. March 17, 2022) */
        public readonly bool $longDate = false,

        /** Whether to show the day of the week */
        public readonly bool $dayOfWeek = false,
    ) {
    }

    /**
     * Create an entity from a bot API date_time_format string (as accepted by the bot API: r, or a combination of w, d/D and t/T).
     *
     * @psalm-pure
     */
    public static function fromBotAPIFormat(int $offset, int $length, int $date, string $format): self
    {
        if ($format === 'r' || $format === 'R') {
            return new self($offset, $length, $date, relative: true);
        }
        $flags = ['shortTime' => false, 'longTime' => false, 'shortDate' => false, 'longDate' => false, 'dayOfWeek' => false];
        foreach (str_split($format) as $c) {
            match ($c) {
                't' => [$flags['shortTime'], $flags['longTime']] = [true, false],
                'T' => [$flags['shortTime'], $flags['longTime']] = [false, true],
                'd' => [$flags['shortDate'], $flags['longDate']] = [true, false],
                'D' => [$flags['shortDate'], $flags['longDate']] = [false, true],
                'w', 'W' => $flags['dayOfWeek'] = true,
                default => throw new AssertionError("Invalid date_time_format: $format"),
            };
        }
        return new self($offset, $length, $date, ...$flags);
    }

    /**
     * Get the bot API date_time_format string.
     *
     * @psalm-mutation-free
     */
    public function getBotAPIFormat(): string
    {
        if ($this->relative) {
            return 'r';
        }
        $format = $this->dayOfWeek ? 'w' : '';
        if ($this->longDate) {
            $format .= 'D';
        } elseif ($this->shortDate) {
            $format .= 'd';
        }
        if ($this->longTime) {
            $format .= 'T';
        } elseif ($this->shortTime) {
            $format .= 't';
        }
        return $format;
    }

    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toBotAPI(): array
    {
        return ['type' => 'date_time', 'offset' => $this->offset, 'length' => $this->length, 'unix_time' => $this->date, 'date_time_format' => $this->getBotAPIFormat()];
    }
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toMTProto(): array
    {
        return [
            '_' => 'messageEntityFormattedDate',
            'relative' => $this->relative,
            'short_time' => $this->shortTime,
            'long_time' => $this->longTime,
            'short_date' => $this->shortDate,
            'long_date' => $this->longDate,
            'day_of_week' => $this->dayOfWeek,
            'offset' => $this->offset,
            'length' => $this->length,
            'date' => $this->date,
        ];
    }
}
<?php declare(strict_types=1);

namespace danog\MadelineProto\EventHandler\Message\Entities;

/**
 * #hashtag message entity.
 */
final class Hashtag extends MessageEntity
{
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toBotAPI(): array
    {
        return ['type' => 'hashtag', 'offset' => $this->offset, 'length' => $this->length];
    }
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toMTProto(): array
    {
        return ['_' => 'messageEntityHashtag', 'offset' => $this->offset, 'length' => $this->length];
    }
}
<?php declare(strict_types=1);

namespace danog\MadelineProto\EventHandler\Message\Entities;

/**
 * Message entity representing a codeblock.
 */
final class Code extends MessageEntity
{
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toBotAPI(): array
    {
        return ['type' => 'code', 'offset' => $this->offset, 'length' => $this->length];
    }
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toMTProto(): array
    {
        return ['_' => 'messageEntityCode', 'offset' => $this->offset, 'length' => $this->length];
    }
}
<?php declare(strict_types=1);

namespace danog\MadelineProto\EventHandler\Message\Entities;

/**
 * Message entity representing a $cashtag.
 */
final class Cashtag extends MessageEntity
{
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toBotAPI(): array
    {
        return ['type' => 'cashtag', 'offset' => $this->offset, 'length' => $this->length];
    }
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toMTProto(): array
    {
        return ['_' => 'messageEntityCashtag', 'offset' => $this->offset, 'length' => $this->length];
    }
}
<?php declare(strict_types=1);

namespace danog\MadelineProto\EventHandler\Message\Entities;

/**
 * Message entity representing a bot /command.
 */
final class BotCommand extends MessageEntity
{
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toBotAPI(): array
    {
        return ['type' => 'bot_command', 'offset' => $this->offset, 'length' => $this->length];
    }
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toMTProto(): array
    {
        return ['_' => 'messageEntityBotCommand', 'offset' => $this->offset, 'length' => $this->length];
    }
}
<?php declare(strict_types=1);

namespace danog\MadelineProto\EventHandler\Message\Entities;

/**
 * Message entity representing an in-text url: https://google.com; for text urls, use TextUrl.
 */
final class Url extends MessageEntity
{
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toBotAPI(): array
    {
        return ['type' => 'url', 'offset' => $this->offset, 'length' => $this->length];
    }
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toMTProto(): array
    {
        return ['_' => 'messageEntityUrl', 'offset' => $this->offset, 'length' => $this->length];
    }
}
<?php declare(strict_types=1);

namespace danog\MadelineProto\EventHandler\Message\Entities;

/**
 * Message entity representing strikethrough text.
 */
final class Strike extends MessageEntity
{
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toBotAPI(): array
    {
        return ['type' => 'strikethrough', 'offset' => $this->offset, 'length' => $this->length];
    }
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toMTProto(): array
    {
        return ['_' => 'messageEntityStrike', 'offset' => $this->offset, 'length' => $this->length];
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Mahdi <mahdi.talaee1379@gmail.com>
 * @copyright 2016-2025 Mahdi <mahdi.talaee1379@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Message\Entities;

use danog\MadelineProto\ParseMode;
use danog\MadelineProto\StrTools;
use JsonSerializable;
use ReflectionClass;
use ReflectionProperty;

final class TextWithEntities implements JsonSerializable
{
    /**
     * @psalm-mutation-free
     */
    public function __construct(
        /** Text */
        public readonly string $text,
        /**
         * Message entities for styled text.
         * @var list<MessageEntity>
         */
        public readonly array $entities,
        /** Whether to parse HTML or Markdown markup in the message */
        public readonly ?ParseMode $parseMode,
    ) {
    }

    /** @internal */
    #[\Override]
    public function jsonSerialize(): mixed
    {
        $res = ['_' => static::class];
        $refl = new ReflectionClass($this);
        foreach ($refl->getProperties(ReflectionProperty::IS_PUBLIC) as $prop) {
            $res[$prop->getName()] = $prop->getValue($this);
        }
        return $res;
    }

    protected readonly string $html;
    protected readonly string $htmlTelegram;

    /**
     * Get an HTML version of the message.
     *
     * @psalm-suppress InaccessibleProperty
     *
     * @param bool $allowTelegramTags Whether to allow telegram-specific tags like tg-spoiler, tg-emoji, mention links and so on...
     */
    public function getHTML(bool $allowTelegramTags = false): string
    {
        if (!$this->entities) {
            return StrTools::htmlEscape($this->text);
        }
        if ($allowTelegramTags) {
            return $this->htmlTelegram ??= StrTools::entitiesToHtml($this->text, $this->entities, $allowTelegramTags);
        }
        return $this->html ??= StrTools::entitiesToHtml($this->text, $this->entities, $allowTelegramTags);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Message;

use danog\MadelineProto\EventHandler\GroupOrChannelMessage;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\RPCError\MsgIdInvalidError;

/**
 * Represents an incoming or outgoing channel message.
 */
final class ChannelMessage extends GroupOrChannelMessage
{
    /** @internal */
    public function __construct(MTProto $API, array $rawMessage, array $info, bool $scheduled)
    {
        parent::__construct($API, $rawMessage, $info, $scheduled);
    }

    /**
     * Obtains the copy of the current message, that was sent to the linked group.
     *
     * Can be used to reply in the comment section, for example:
     *
     * ```php
     * $update->getDiscussion()->reply("Comment");
     * ```
     *
     */
    public function getDiscussion(): ?GroupMessage
    {
        try {
            $r = $this->getClient()->methodCallAsyncRead(
                'messages.getDiscussionMessage',
                ['peer' => $this->chatId, 'msg_id' => $this->id]
            )['messages'];
            $r = end($r);
            $v = $this->getClient()->wrapMessage($r);
            \assert($v instanceof GroupMessage);
            return $v;
        } catch (MsgIdInvalidError) {
            return null;
        }
    }

    /**
     * Disable message signatures in channels.
     *
     */
    public function disableSignatures(): void
    {
        $this->getClient()->methodCallAsyncRead(
            'channels.toggleSignatures',
            [
                'channel' => $this->chatId,
                'enabled' => false,
            ]
        );
    }

    /**
     * Enable message signatures in channels.
     *
     */
    public function enableSignatures(): void
    {
        $this->getClient()->methodCallAsyncRead(
            'channels.toggleSignatures',
            [
                'channel' => $this->chatId,
                'enabled' => true,
            ]
        );
    }

    /**
     * Increase the view counter of a current message in the channel.
     *
     */
    public function view(): void
    {
        $this->getClient()->methodCallAsyncRead(
            'messages.getMessagesViews',
            [
                'peer' => $this->chatId,
                'id' => [$this->id],
                'increment' => true,
            ]
        );
    }

    /**
     * Hide message history for new channel users.
     *
     */
    public function hideHistory(): void
    {
        $this->getClient()->methodCallAsyncRead(
            'channels.toggleParticipantsHidden',
            [
                'channel' => $this->chatId,
                'enabled' => true,
            ]
        );
    }

    /**
     * Unhide message history for new channel users.
     *
     */
    public function unhideHistory(): void
    {
        $this->getClient()->methodCallAsyncRead(
            'channels.toggleParticipantsHidden',
            [
                'channel' => $this->chatId,
                'enabled' => false,
            ]
        );
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Message;

use danog\MadelineProto\EventHandler\AbstractMessage;

/**
 * Represents info about a service message.
 */
abstract class ServiceMessage extends AbstractMessage
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Message;

use danog\MadelineProto\EventHandler\AbstractPrivateMessage;
use danog\MadelineProto\EventHandler\Message\Service\DialogScreenshotTaken;

/**
 * Represents an incoming or outgoing private message.
 */
final class PrivateMessage extends AbstractPrivateMessage
{
    /**
     * ID of the other user in this private chat.
     *
     * @psalm-mutation-free
     */
    #[\Override]
    protected function getCallPeer(): int
    {
        return $this->chatId;
    }

    /**
     * @inheritDoc
     */
    #[\Override]
    public function screenShot(): DialogScreenshotTaken
    {
        $result = $this->getClient()->methodCallAsyncRead(
            'messages.sendScreenshotNotification',
            [
                'peer' => $this->chatId,
                'reply_to' => [ '_' => 'inputReplyToMessage', 'reply_to_msg_id' => 0 ],
            ]
        );
        return $this->getClient()->wrapMessage($this->getClient()->extractMessage($result));
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Message;

use danog\DialogId\DialogId;
use danog\MadelineProto\EventHandler\GroupOrChannelMessage;
use danog\MadelineProto\EventHandler\Message\Service\DialogTopicCreated;
use danog\MadelineProto\EventHandler\Message\Service\DialogTopicEdited;
use danog\MadelineProto\EventHandler\Topic\IconColor;
use Webmozart\Assert\Assert;
use Webmozart\Assert\InvalidArgumentException;

/**
 * Represents an incoming or outgoing group message.
 */
class GroupMessage extends GroupOrChannelMessage
{
    /**
     * Hide the participants list in a [supergroup](https://core.telegram.org/api/channel).
     * The supergroup must have at least `hidden_members_group_size_min` participants in order to use this method, as specified by the [client configuration parameters Â»](https://core.telegram.org/api/config#client-configuration).
     *
     * @throws InvalidArgumentException
     */
    public function hideMembers(): void
    {
        Assert::true(DialogId::isSupergroupOrChannelOrMonoforum($this->chatId));
        $this->getClient()->methodCallAsyncRead(
            'channels.toggleParticipantsHidden',
            [
                'channel' => $this->chatId,
                'enabled' => true,
            ]
        );
    }

    /**
     * Display the participants list in a [supergroup](https://core.telegram.org/api/channel).
     * The supergroup must have at least `hidden_members_group_size_min` participants in order to use this method, as specified by the [client configuration parameters Â»](https://core.telegram.org/api/config#client-configuration).
     *
     * @throws InvalidArgumentException
     */
    public function unhideMembers(): void
    {
        Assert::true(DialogId::isSupergroupOrChannelOrMonoforum($this->chatId));
        $this->getClient()->methodCallAsyncRead(
            'channels.toggleParticipantsHidden',
            [
                'channel' => $this->chatId,
                'enabled' => false,
            ]
        );
    }

    /**
     * Hide message history for new supergroup users.
     *
     * @throws InvalidArgumentException
     */
    public function hideHistory(): void
    {
        Assert::true(DialogId::isSupergroupOrChannelOrMonoforum($this->chatId));
        $this->getClient()->methodCallAsyncRead(
            'channels.togglePreHistoryHidden',
            [
                'channel' => $this->chatId,
                'enabled' => true,
            ]
        );
    }

    /**
     * Unhide message history for new supergroup users.
     *
     * @throws InvalidArgumentException
     */
    public function unhideHistory(): void
    {
        Assert::true(DialogId::isSupergroupOrChannelOrMonoforum($this->chatId));
        $this->getClient()->methodCallAsyncRead(
            'channels.togglePreHistoryHidden',
            [
                'channel' => $this->chatId,
                'enabled' => false,
            ]
        );
    }

    /**
     * Ban message sender from current supergroup.
     *
     * @param int $untilDate Validity of said permissions (it is considered forever any value less then 30 seconds or more then 366 days).
     */
    public function ban(int $untilDate = 0): void
    {
        $chatBannedRights = [
            '_' => 'chatBannedRights',
            'view_messages' => false,
            'send_messages' => false,
            'send_media' => false,
            'send_stickers' => false,
            'send_gifs' => false,
            'send_games' => false,
            'send_inline' => false,
            'embed_links' => false,
            'send_polls' => false,
            'change_info' => false,
            'invite_users' => false,
            'pin_messages' => false,
            'manage_topics' => false,
            'send_photos' => false,
            'send_videos' => false,
            'send_roundvideos' => false,
            'send_audios' => false,
            'send_voices' => false,
            'send_docs' => false,
            'send_plain' => false,
            'until_date' => $untilDate,
        ];
        $this->getClient()->methodCallAsyncRead(
            'channels.editBanned',
            [
                'channel' => $this->chatId,
                'participant' => $this->senderId,
                'banned_rights' => $chatBannedRights,
            ]
        );
    }

    /**
     * Unban message sender from current supergroup.
     *
     * @param int $untilDate Validity of said permissions (it is considered forever any value less then 30 seconds or more then 366 days).
     */
    public function unban(int $untilDate = 0): void
    {
        $chatBannedRights = [
            '_' => 'chatBannedRights',
            'view_messages' => true,
            'send_messages' => true,
            'send_media' => true,
            'send_stickers' => true,
            'send_gifs' => true,
            'send_games' => true,
            'send_inline' => true,
            'embed_links' => true,
            'send_polls' => true,
            'change_info' => true,
            'invite_users' => true,
            'pin_messages' => true,
            'manage_topics' => true,
            'send_photos' => true,
            'send_videos' => true,
            'send_roundvideos' => true,
            'send_audios' => true,
            'send_voices' => true,
            'send_docs' => true,
            'send_plain' => true,
            'until_date' => $untilDate,
        ];
        $this->getClient()->methodCallAsyncRead(
            'channels.editBanned',
            [
                'channel' => $this->chatId,
                'participant' => $this->senderId,
                'banned_rights' => $chatBannedRights,
            ]
        );
    }

    /**
     * Kick message sender from current supergroup.
     */
    public function kick(): void
    {
        $this->ban();
        $this->unban();
    }

    /**
     * Delete all supergroup message.
     */
    public function deleteAll(bool $forEveryone = true, int $maxId = 0): void
    {
        $this->getClient()->methodCallAsyncRead(
            'channels.deleteHistory',
            [
                'channel' => $this->chatId,
                'for_everyone' => $forEveryone,
                'max_id' => $maxId,
            ]
        );
    }

    /**
     * Delete all messages sent by a specific participant of a given supergroup.
     *
     * @param  string|integer|null      $member The participant whose messages should be deleted, if null or absent defaults to the sender of the message.
     * @throws InvalidArgumentException
     */
    public function deleteUserMessages(string|int|null $member = null): void
    {
        Assert::true(DialogId::isSupergroupOrChannelOrMonoforum($this->chatId));
        $member ??= $this->senderId;
        $this->getClient()->methodCallAsyncRead(
            'channels.deleteParticipantHistory',
            [
                'channel' => $this->chatId,
                'participant' => $member,
            ]
        );
    }

    /**
     * Turn a [basic group into a supergroup](https://core.telegram.org/api/channel#migration).
     *
     * @return integer                  the channel id we migrated to
     * @throws InvalidArgumentException
     */
    public function toSuperGroup(): int
    {
        Assert::true(DialogId::isChat($this->chatId));
        $client = $this->getClient();
        $result = $client->methodCallAsyncRead(
            'messages.migrateChat',
            [
                'chat_id' => $this->chatId,
            ]
        );
        $v = $client->getIdInternal($result['updates'][0]);
        \assert($v !== null);
        return $v;
    }

    /**
     * Enable the [native antispam system](https://core.telegram.org/api/antispam).
     *
     * @throws InvalidArgumentException
     */
    public function enableAntiSpam(): void
    {
        Assert::true(DialogId::isSupergroupOrChannelOrMonoforum($this->chatId));
        $this->getClient()->methodCallAsyncRead(
            'channels.toggleAntiSpam',
            [
                'channel' => $this->chatId,
                'enabled' => true,
            ]
        );
    }

    /**
     * Disable the [native antispam system](https://core.telegram.org/api/antispam).
     *
     * @throws InvalidArgumentException
     */
    public function disableAntiSpam(): void
    {
        Assert::true(DialogId::isSupergroupOrChannelOrMonoforum($this->chatId));
        $this->getClient()->methodCallAsyncRead(
            'channels.toggleAntiSpam',
            [
                'channel' => $this->chatId,
                'enabled' => false,
            ]
        );
    }

    /**
     * Enable [forum functionality](https://core.telegram.org/api/forum) in a supergroup.
     *
     */
    public function enableTopics(): void
    {
        if (!$this->topicId) {
            $this->getClient()->methodCallAsyncRead(
                'channels.toggleForum',
                [
                    'channel' => $this->chatId,
                    'enabled' => true,
                ]
            );
        }
    }

    /**
     * Disable [forum functionality](https://core.telegram.org/api/forum) in a supergroup.
     *
     */
    public function disableTopics(): void
    {
        if ($this->topicId) {
            $this->getClient()->methodCallAsyncRead(
                'channels.toggleForum',
                [
                    'channel' => $this->chatId,
                    'enabled' => false,
                ]
            );
        }
    }

    /**
     * Create a [forum topic](https://core.telegram.org/api/forum); requires [`manage_topics` rights](https://core.telegram.org/api/rights).
     *
     * @param string        $title Topic title (maximum UTF-8 length: 128)
     * @param IconColor|int $icon  Icon color, or ID of the [custom emoji](https://core.telegram.org/api/custom-emoji) used as topic icon.
     *                             [Telegram Premium](https://core.telegram.org/api/premium) users can use any custom emoji, other users can only use the custom emojis contained in the [inputStickerSetEmojiDefaultTopicIcons](https://docs.madelineproto.xyz/API_docs/constructors/inputStickerSetEmojiDefaultTopicIcons.html) emoji pack.
     *                             If no custom emoji icon is specified, specifies the color of the fallback topic icon
     *
     * @throws InvalidArgumentException
     */
    public function createTopic(string $title, IconColor|int $icon = IconColor::NONE): DialogTopicCreated
    {
        Assert::true(DialogId::isSupergroupOrChannelOrMonoforum($this->chatId));
        if (!$this->topicId) {
            $this->enableTopics();
        }
        $client = $this->getClient();
        $result = $client->methodCallAsyncRead(
            'messages.createForumTopic',
            [
                'peer' => $this->chatId,
                'title' => $title,
                ...\is_int($icon)
                    ? ['icon_emoji_id' => $icon]
                    : ['icon_color' => $icon->value],
            ]
        );
        return $client->wrapMessage($client->extractMessage($result));
    }

    /**
     * Edit a [forum topic](https://core.telegram.org/api/forum); requires [`manage_topics` rights](https://core.telegram.org/api/rights).
     *
     * @param  string                   $title   Topic title (maximum UTF-8 length: 128)
     * @param  integer                  $icon    ID of the [custom emoji](https://core.telegram.org/api/custom-emoji) used as topic icon. [Telegram Premium](https://core.telegram.org/api/premium) users can use any custom emoji, other users can only use the custom emojis contained in the [inputStickerSetEmojiDefaultTopicIcons](https://docs.madelineproto.xyz/API_docs/constructors/inputStickerSetEmojiDefaultTopicIcons.html) emoji pack. Pass 0 to switch to the fallback topic icon.
     * @param  integer|null             $topicId Topic ID, if absent defaults to the topic where this message was sent.
     * @throws InvalidArgumentException
     */
    public function editTopic(string $title, int $icon = 0, ?int $topicId = null): DialogTopicEdited
    {
        Assert::true(DialogId::isSupergroupOrChannelOrMonoforum($this->chatId));
        $topicId ??= $this->topicId;
        Assert::notNull($topicId, "No topic ID was provided!");
        $client = $this->getClient();
        $result = $client->methodCallAsyncRead(
            'messages.editForumTopic',
            [
                'peer' => $this->chatId,
                'topic_id' => $topicId,
                'title' => $title,
                'icon_emoji_id' => $icon,
            ]
        );
        return $client->wrapMessage($client->extractMessage($result));
    }

    /**
     * Open a [forum topic](https://core.telegram.org/api/forum); requires [`manage_topics` rights](https://core.telegram.org/api/rights).
     *
     * @param  integer|null             $topicId Topic ID, if absent defaults to the topic where this message was sent.
     * @throws InvalidArgumentException
     */
    public function openTopic(?int $topicId = null): DialogTopicEdited
    {
        Assert::true(DialogId::isSupergroupOrChannelOrMonoforum($this->chatId));
        $topicId ??= $this->topicId;
        Assert::notNull($topicId, "No topic ID was provided!");
        $client = $this->getClient();
        $result = $client->methodCallAsyncRead(
            'messages.editForumTopic',
            [
                'peer' => $this->chatId,
                'topic_id' => $topicId,
                'closed' => false,
            ]
        );
        return $client->wrapMessage($client->extractMessage($result));
    }

    /**
     * Close a [forum topic](https://core.telegram.org/api/forum); requires [`manage_topics` rights](https://core.telegram.org/api/rights).
     *
     * @param  integer|null             $topicId Topic ID, if absent defaults to the topic where this message was sent.
     * @throws InvalidArgumentException
     */
    public function closeTopic(?int $topicId = null): DialogTopicEdited
    {
        Assert::true(DialogId::isSupergroupOrChannelOrMonoforum($this->chatId));
        $topicId ??= $this->topicId;
        Assert::notNull($topicId, "No topic ID was provided!");
        $client = $this->getClient();
        $result = $client->methodCallAsyncRead(
            'messages.editForumTopic',
            [
                'peer' => $this->chatId,
                'topic_id' => $topicId,
                'closed' => true,
            ]
        );
        return $client->wrapMessage($client->extractMessage($result));
    }

    /**
     * Delete message history of a [forum topic](https://core.telegram.org/api/forum).
     *
     * @param  integer|null             $topicId Topic ID, if absent defaults to the topic where this message was sent.
     * @throws InvalidArgumentException
     */
    public function deleteTopic(?int $topicId = null): void
    {
        Assert::true(DialogId::isSupergroupOrChannelOrMonoforum($this->chatId));
        $topicId ??= $this->topicId;
        Assert::notNull($topicId, "No topic ID was provided!");
        $this->getClient()->methodCallAsyncRead(
            'messages.deleteTopicHistory',
            [
                'peer' => $this->chatId,
                'top_msg_id' => $topicId,
            ]
        );
    }

    /**
     * Toggle supergroup slow mode: Users will only be able to send one message every `$seconds` seconds.
     *
     * @param integer $seconds Users will only be able to send one message every `$seconds` seconds
     * @throws InvalidArgumentException
     */
    public function enableSlowMode(int $seconds): void
    {
        Assert::true(DialogId::isSupergroupOrChannelOrMonoforum($this->chatId));
        Assert::false($seconds === 0);
        $this->getClient()->methodCallAsyncRead(
            'channels.toggleSlowMode',
            [
                'channel' => $this->chatId,
                'seconds' => $seconds,
            ]
        );
    }

    /**
     * Disable supergroup slow mode.
     *
     * @throws InvalidArgumentException
     */
    public function disableSlowMode(): void
    {
        Assert::true(DialogId::isSupergroupOrChannelOrMonoforum($this->chatId));
        $this->getClient()->methodCallAsyncRead(
            'channels.toggleSlowMode',
            [
                'channel' => $this->chatId,
                'seconds' => 0,
            ]
        );
    }

    /**
     * Enable to all users [should join a discussion group in order to comment on a post Â»](https://core.telegram.org/api/discussion#requiring-users-to-join-the-group).
     *
     */
    public function enableJoinToComment(): void
    {
        Assert::true(DialogId::isSupergroupOrChannelOrMonoforum($this->chatId));
        $this->getClient()->methodCallAsyncRead(
            'channels.toggleJoinToSend',
            [
                'channel' => $this->chatId,
                'enabled' => false,
            ]
        );
    }

    /**
     * Disable to all users [should join a discussion group in order to comment on a post Â»](https://core.telegram.org/api/discussion#requiring-users-to-join-the-group).
     *
     */
    public function disableJoinToComment(): void
    {
        Assert::true(DialogId::isSupergroupOrChannelOrMonoforum($this->chatId));
        $this->getClient()->methodCallAsyncRead(
            'channels.toggleJoinToSend',
            [
                'channel' => $this->chatId,
                'enabled' => false,
            ]
        );
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler;

use danog\MadelineProto\MTProto;

/**
 * Indicates that some messages were deleted.
 */
abstract class Delete extends Update
{
    /** @var list<int> List of identifiers of deleted messages */
    public readonly array $ids;

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(MTProto $API, array $rawDelete)
    {
        parent::__construct($API);
        $this->ids = $rawDelete['messages'];
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Wallpaper;

use JsonSerializable;
use ReflectionClass;
use ReflectionProperty;

/**
 * Info on how to generate a wallpaper, according to [these instructions Â»](https://core.telegram.org/api/wallpapers).
 */
final class WallpaperSettings implements JsonSerializable
{
    /** For [image wallpapers](https://core.telegram.org/api/wallpapers#image-wallpapers): if set, the JPEG must be downscaled to fit in 450x450 square and then box-blurred with radius 12. */
    public readonly bool $blur;

    /** If set, the background needs to be slightly moved when the device is rotated. */
    public readonly bool $motion;

    /** Used for [solid](https://core.telegram.org/api/wallpapers#solid-fill), [gradient](https://core.telegram.org/api/wallpapers#gradient-fill) and [freeform gradient](https://core.telegram.org/api/wallpapers#freeform-gradient-fill) fills. */
    public readonly ?int $backgroundColor;

    /** Used for [gradient](https://core.telegram.org/api/wallpapers#gradient-fill) and [freeform gradient](https://core.telegram.org/api/wallpapers#freeform-gradient-fill) fills. */
    public readonly ?int $secondBackgroundColor;

    /** Used for [freeform gradient](https://core.telegram.org/api/wallpapers#freeform-gradient-fill) fills. */
    public readonly ?int $thirdBackgroundColor;

    /** Used for [freeform gradient](https://core.telegram.org/api/wallpapers#freeform-gradient-fill) fills. */
    public readonly ?int $fourthBackgroundColor;

    /** Used for [pattern wallpapers](https://core.telegram.org/api/wallpapers#pattern-wallpapers). */
    public readonly int $intensity;

    /** Clockwise rotation angle of the gradient, in degrees; 0-359. Should be always divisible by 45. */
    public readonly int $rotation;

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(array $rawWallpaperSetting)
    {
        $this->blur = $rawWallpaperSetting['blur'];
        $this->motion = $rawWallpaperSetting['motion'];
        $this->intensity = $rawWallpaperSetting['intensity'];
        $this->rotation = $rawWallpaperSetting['rotation'] ?? 0;
        $this->backgroundColor = $rawWallpaperSetting['background_color'] ?? null;
        $this->secondBackgroundColor = $rawWallpaperSetting['second_background_color'] ?? null;
        $this->thirdBackgroundColor = $rawWallpaperSetting['third_background_color'] ?? null;
        $this->fourthBackgroundColor = $rawWallpaperSetting['fourth_background_color'] ?? null;
    }

    /** @internal */
    #[\Override]
    public function jsonSerialize(): mixed
    {
        $res = ['_' => static::class];
        $refl = new ReflectionClass($this);
        foreach ($refl->getProperties(ReflectionProperty::IS_PUBLIC) as $prop) {
            $res[$prop->getName()] = $prop->getValue($this);
        }
        return $res;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Keyboard;

use danog\MadelineProto\EventHandler\Keyboard;

/**
 * Represents an inline keyboard.
 */
final class InlineKeyboard extends Keyboard
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Keyboard;

use danog\MadelineProto\EventHandler\Keyboard;

/**
 * Represents a reply keyboard.
 */
final class ReplyKeyboard extends Keyboard
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\User;

use danog\MadelineProto\EventHandler\Update;
use danog\MadelineProto\MTProto;

/**
 * A bot was stopped or re-started.
 */
final class BotStopped extends Update
{
    /** When did this action occur */
    public readonly int $date;

    /** Whether the bot was stopped or started. */
    public readonly bool $stopped;

    /** The user ID */
    public readonly int $userId;

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(MTProto $API, array $rawBotStopped)
    {
        parent::__construct($API);
        $this->date = $rawBotStopped['date'];
        $this->stopped = $rawBotStopped['stopped'];
        $this->userId = $rawBotStopped['user_id'];
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\User\Status;

use danog\MadelineProto\EventHandler\User\Status;

/**
 * User status has not been set yet.
 */
final class EmptyStatus extends Status
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\User\Status;

use danog\MadelineProto\EventHandler\User\Status;
use danog\MadelineProto\MTProto;

/**
 * Online status of the user.
 */
final class Online extends Status
{
    /** Time to expiration of the current online status */
    public readonly int $expires;

    /**
     * @psalm-mutation-free
     */
    public function __construct(MTProto $API, array $rowUserStatus)
    {
        parent::__construct($API, $rowUserStatus);
        $this->expires = $rowUserStatus['status']['expires'];
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\User\Status;

use danog\MadelineProto\EventHandler\User\Status;
use danog\MadelineProto\MTProto;

/**
 * The [emoji status](https://core.telegram.org/api/emoji-status) of a certain user has changed or was removed.
 */
final class Emoji extends Status
{
    /** [Custom emoji document ID](https://core.telegram.org/api/custom-emoji) */
    public readonly ?int $emojiId;

    /** This status is valid until this date */
    public readonly ?int $until;

    public function __construct(MTProto $API, array $rawEmojiStatus)
    {
        parent::__construct($API, $rawEmojiStatus);
        $this->emojiId = $rawEmojiStatus['emoji_status']['document_id'] ?? null;
        $this->until = $rawEmojiStatus['emoji_status']['until'] ?? null;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\User\Status;

use danog\MadelineProto\EventHandler\User\Status;

/**
 * Online status: last seen last week.
 */
final class LastWeek extends Status
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\User\Status;

use danog\MadelineProto\EventHandler\User\Status;
use danog\MadelineProto\MTProto;

/**
 * The userâ€™s offline status.
 */
final class Offline extends Status
{
    /** Time the user was last seen online */
    public readonly int $wasOnline;

    /**
     * @psalm-mutation-free
     */
    public function __construct(MTProto $API, array $rowUserStatus)
    {
        parent::__construct($API, $rowUserStatus);
        $this->wasOnline = $rowUserStatus['status']['was_online'];
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\User\Status;

use danog\MadelineProto\EventHandler\User\Status;

/**
 * Online status: last seen recently.
 */
final class Recently extends Status
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\User\Status;

use danog\MadelineProto\EventHandler\User\Status;

/**
 * Online status: last seen last month.
 */
final class LastMonth extends Status
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\User;

use danog\MadelineProto\EventHandler\Update;
use danog\MadelineProto\MTProto;

/**
 * A userâ€™s phone number was changed.
 */
final class Phone extends Update
{
    /** User ID */
    public readonly int $userId;

    /** New phone number. */
    public readonly string $number;

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(MTProto $API, array $rawUserPhone)
    {
        parent::__construct($API);
        $this->userId = $rawUserPhone['user_id'];
        $this->number = $rawUserPhone['phone'];
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\User;

use danog\MadelineProto\EventHandler\Update;
use danog\MadelineProto\MTProto;

/**
 * A peer was blocked.
 */
final class Blocked extends Update
{
    /** Whether blocked or unblocked. */
    public readonly bool $blocked;

    /** Whether we hid or unhid stories of this user */
    public readonly bool $stories;

    /** The user ID */
    public readonly int $userId;

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(MTProto $API, array $rawPeerBlocked)
    {
        parent::__construct($API);
        $this->blocked = $rawPeerBlocked['blocked'];
        $this->stories = $rawPeerBlocked['blocked_my_stories_from'];
        $this->userId = $API->getIdInternal($rawPeerBlocked);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\User;

use danog\MadelineProto\EventHandler\Update;
use danog\MadelineProto\EventHandler\User\Status\EmptyStatus;
use danog\MadelineProto\EventHandler\User\Status\LastMonth;
use danog\MadelineProto\EventHandler\User\Status\LastWeek;
use danog\MadelineProto\EventHandler\User\Status\Offline;
use danog\MadelineProto\EventHandler\User\Status\Online;
use danog\MadelineProto\EventHandler\User\Status\Recently;
use danog\MadelineProto\MTProto;

/**
 * Contains a status update.
 */
abstract class Status extends Update
{
    /** User identifier */
    public readonly int $userId;

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(MTProto $API, array $rowUserStatus)
    {
        parent::__construct($API);
        $this->userId = $rowUserStatus['user_id'];
    }

    /**
     * @psalm-pure
     */
    public static function fromRawStatus(MTProto $API, array $rowUserStatus): ?Status
    {
        return match ($rowUserStatus['status']['_']) {
            'userStatusEmpty' => new EmptyStatus($API, $rowUserStatus),
            'userStatusOnline' => new Online($API, $rowUserStatus),
            'userStatusOffline' => new Offline($API, $rowUserStatus),
            'userStatusRecently' => new Recently($API, $rowUserStatus),
            'userStatusLastWeek' => new LastWeek($API, $rowUserStatus),
            'userStatusLastMonth' => new LastMonth($API, $rowUserStatus),
            default => null
        };
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\User;

use JsonSerializable;
use ReflectionClass;
use ReflectionProperty;

/**
 * Represents an username.
 */
final class UsernameInfo implements JsonSerializable
{
    /** Whether the username is bought, meaning it was bought on [fragment](https://fragment.com). */
    public readonly bool $bought;

    /** Whether the username is active. */
    public readonly bool $active;

    /** The username. */
    public readonly string $username;

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(array $rawUserNames)
    {
        $this->bought = !$rawUserNames['editable'];
        $this->active = $rawUserNames['active'];
        $this->username = $rawUserNames['username'];
    }

    /** @internal */
    #[\Override]
    public function jsonSerialize(): mixed
    {
        $res = ['_' => static::class];
        $refl = new ReflectionClass($this);
        foreach ($refl->getProperties(ReflectionProperty::IS_PUBLIC) as $prop) {
            $res[$prop->getName()] = $prop->getValue($this);
        }
        return $res;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\User;

use danog\MadelineProto\EventHandler\Update;
use danog\MadelineProto\MTProto;

/**
 * Changes were made to the userâ€™s first name, last name or username.
 */
final class Username extends Update
{
    /** User identifier */
    public readonly int $userId;

    /** New first name. Corresponds to the new value of `real_first_name` field of the [userFull](https://docs.madelineproto.xyz/API_docs/constructors/userFull.html) constructor. */
    public readonly string $firstName;

    /** New last name. Corresponds to the new value of `real_last_name` field of the [userFull](https://docs.madelineproto.xyz/API_docs/constructors/userFull.html) constructor. */
    public readonly string $lastName;

    /** @var list<UsernameInfo> */
    public readonly array $usernames;

    /** @internal */
    public function __construct(MTProto $API, array $rawUserName)
    {
        parent::__construct($API);
        $this->userId = $rawUserName['user_id'];
        $this->firstName = $rawUserName['first_name'];
        $this->lastName = $rawUserName['last_name'];
        $this->usernames = array_map(
            static fn (array $username) => new UsernameInfo($username),
            $rawUserName['usernames']
        );
    }
}
<?php

declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler;

use Amp\ByteStream\ReadableStream;
use Amp\Cancellation;
use AssertionError;
use danog\DialogId\DialogId;
use danog\MadelineProto\BotApiFileId;
use danog\MadelineProto\EventHandler\Action\Typing;
use danog\MadelineProto\EventHandler\Message\Service\DialogSetTTL;
use danog\MadelineProto\EventHandler\Story\Story;
use danog\MadelineProto\EventHandler\Story\StoryDeleted;
use danog\MadelineProto\LocalFile;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\ParseMode;
use danog\MadelineProto\RemoteUrl;
use Webmozart\Assert\Assert;
use Webmozart\Assert\InvalidArgumentException;

use function array_column;
use function array_filter;
use function array_map;
use function is_subclass_of;

/**
 * Represents an incoming or outgoing message.
 */
abstract class AbstractMessage extends Update implements SimpleFilters
{
    /** Message ID */
    public readonly int $id;

    /** Whether the message is outgoing */
    public readonly bool $out;

    /** ID of the chat where the message was sent */
    public readonly int $chatId;

    /** ID of the sender of the message */
    public readonly int $senderId;

    /** ID of the message to which this message is replying */
    public readonly ?int $replyToMsgId;

    /** When was the message sent */
    public readonly int $date;

    /** ID of the forum topic where the message was sent */
    public readonly ?int $topicId;

    /** ID of the message thread where the message was sent */
    public readonly ?int $threadId;

    /** Whether this is a reply to a scheduled message */
    public readonly bool $replyToScheduled;

    /** Whether we were mentioned in this message */
    public readonly bool $mentioned;

    /** Whether this message was sent without any notification (silently) */
    public readonly bool $silent;

    /** Time-to-live of the message */
    public readonly ?int $ttlPeriod;

    /** @internal */
    public function __construct(
        MTProto $API,
        array $rawMessage,
        array $info
    ) {
        parent::__construct($API);
        if (isset($rawMessage['decrypted_message'])) {
            $rawMessage = $rawMessage['decrypted_message'];
            $secretChat = $this->getClient()->getSecretChat($rawMessage['chat_id']);
        } else {
            $secretChat = null;
        }
        $fromReplies = (($info['User']['username'] ?? '') === 'replies');
        $this->out = $rawMessage['out'] ?? false;
        $this->id = $fromReplies ? $rawMessage['fwd_from']['saved_from_msg_id'] : $rawMessage['id'] ?? $rawMessage['random_id'];
        $this->chatId = isset($secretChat) ? $secretChat->chatId : ($fromReplies ? $rawMessage['reply_to']['reply_to_peer_id'] : $info['bot_api_id']);
        $this->senderId = isset($secretChat)  ? $secretChat->otherID : ($fromReplies ? $this->getClient()->getIdInternal($rawMessage['fwd_from']['from_id']) : (isset($rawMessage['from_id'])
            ? $this->getClient()->getIdInternal($rawMessage['from_id'])
            : $this->chatId));
        $this->date = $rawMessage['date'];
        $this->mentioned = $rawMessage['mentioned'] ?? false;
        $this->silent = $rawMessage['silent'] ?? false;
        $this->ttlPeriod = $rawMessage['ttl_period'] ?? $rawMessage['ttl'] ?? null;
        if (isset($rawMessage['reply_to']) && $rawMessage['reply_to']['_'] === 'messageReplyHeader') {
            $replyTo = $rawMessage['reply_to'];
            $this->replyToScheduled = $replyTo['reply_to_scheduled'];
            if ($replyTo['forum_topic']) {
                if (isset($replyTo['reply_to_top_id'])) {
                    $this->topicId = $replyTo['reply_to_top_id'];
                    $this->replyToMsgId = $replyTo['reply_to_msg_id'];
                } else {
                    $this->topicId = $replyTo['reply_to_msg_id'];
                    $this->replyToMsgId = null;
                }
                $this->threadId = null;
            } elseif ($info['Chat']['forum'] ?? false) {
                $this->topicId = 1;
                $this->replyToMsgId = $replyTo['reply_to_msg_id'];
                $this->threadId = $replyTo['reply_to_top_id'] ?? null;
            } else {
                $this->topicId = null;
                $this->replyToMsgId = $replyTo['reply_to_msg_id'] ?? null;
                $this->threadId = $replyTo['reply_to_top_id'] ?? null;
            }
        } elseif ($info['Chat']['forum'] ?? false) {
            $this->topicId = 1;
            $this->replyToMsgId = null;
            $this->threadId = null;
            $this->replyToScheduled = false;
        } else {
            $this->topicId = null;
            $this->replyToMsgId = $rawMessage['reply_to_random_id'] ?? null;
            $this->threadId = null;
            $this->replyToScheduled = false;
        }
    }

    /**
     * Check if the current message replies to another message.
     *
     * @psalm-mutation-free
     */
    public function isReply(): bool
    {
        return $this->replyToMsgId !== null;
    }

    protected ?self $replyCache = null;
    protected bool $replyCached = false;
    /**
     * Get replied-to message.
     *
     * May return null if the replied-to message was deleted or if the message does not reply to any other message.
     *
     * @template T as AbstractMessage
     * @param class-string<T> $class Only return a reply if it is of the specified type, return null otherwise.
     * @return ?T
     */
    public function getReply(string $class = self::class): ?self
    {
        if ($class !== self::class && !is_subclass_of($class, self::class)) {
            throw new AssertionError("A class that extends AbstractMessage was expected.");
        }
        if ($this->replyToMsgId === null) {
            return null;
        }
        if ($this->replyCached) {
            if (!$this->replyCache instanceof $class) {
                return null;
            }
            return $this->replyCache;
        }
        $messages = $this->getClient()->methodCallAsyncRead(
            DialogId::isSupergroupOrChannelOrMonoforum($this->chatId) ? 'channels.getMessages' : 'messages.getMessages',
            [
                'channel' => $this->chatId,
                'id' => [['_' => 'inputMessageReplyTo', 'id' => $this->id]],
            ],
        )['messages'];
        /** @psalm-suppress InaccessibleProperty */
        $this->replyCache = $messages ? $this->getClient()->wrapMessage($messages[0]) : null;
        $this->replyCached = true;
        if (!$this->replyCache instanceof $class) {
            return null;
        }
        return $this->replyCache;
    }

    /**
     * Delete the message.
     *
     * @param boolean $revoke Whether to delete the message for all participants of the chat.
     */
    public function delete(bool $revoke = true): void
    {
        $this->getClient()->methodCallAsyncRead(
            DialogId::isSupergroupOrChannelOrMonoforum($this->chatId) ? 'channels.deleteMessages' : 'messages.deleteMessages',
            [
                'channel' => $this->chatId,
                'id' => [$this->id],
                'revoke' => $revoke,
            ],
        );
    }

    /**
     * Reply to the message.
     *
     * @param string $message Message to send
     * @param ParseMode $parseMode Parse mode
     * @param array|null $replyMarkup Keyboard information.
     * @param integer|string|null $sendAs Peer to send the message as.
     * @param integer|null $scheduleDate Schedule date.
     * @param boolean $silent Whether to send the message silently, without triggering notifications.
     * @param boolean $noForwards Only for bots, disallows further re-forwarding and saving of the messages, even if the destination chat doesnâ€™t have [content protection](https://telegram.org/blog/protected-content-delete-by-date-and-more) enabled
     * @param boolean $background Send this message as background message
     * @param boolean $clearDraft Clears the draft field
     * @param boolean $noWebpage Set this flag to disable generation of the webpage preview
     * @param boolean $updateStickersetsOrder Whether to move used stickersets to top
     */
    public function reply(
        string $message,
        ParseMode $parseMode = ParseMode::TEXT,
        ?array $replyMarkup = null,
        int|string|null $sendAs = null,
        ?int $scheduleDate = null,
        bool $noWebpage = false,
        bool $silent = false,
        bool $noForwards = false,
        bool $background = false,
        bool $clearDraft = false,
        bool $updateStickersetsOrder = false,
        ?Cancellation $cancellation = null
    ): Message {
        return $this->getClient()->sendMessage(
            peer: $this->chatId,
            message: $message,
            parseMode: $parseMode,
            replyToMsgId: $this->id,
            topMsgId: $this->topicId === 1 ? null : $this->topicId,
            replyMarkup: $replyMarkup,
            sendAs: $sendAs,
            scheduleDate: $scheduleDate,
            silent: $silent,
            noForwards: $noForwards,
            background: $background,
            clearDraft: $clearDraft,
            noWebpage: $noWebpage,
            updateStickersetsOrder: $updateStickersetsOrder,
            cancellation: $cancellation,
        );
    }

    /**
     * Reply a document.
     *
     * Please use named arguments to call this method.
     *
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream      $file                   File to upload: can be a message to reuse media present in a message.
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream|null $thumb                  Optional: Thumbnail to upload
     * @param string                                                             $caption                Caption of document
     * @param ?callable(float, float, float)                                     $callback               Upload callback (percent, speed in mbps, seconds elapsed)
     * @param ?string                                                            $fileName               Optional file name, if absent will be extracted from the passed $file.
     * @param ParseMode                                                          $parseMode              Text parse mode for the caption
     * @param array|null                                                         $replyMarkup            Keyboard information.
     * @param integer|null                                                       $sendAs                 Peer to send the message as.
     * @param integer|null                                                       $scheduleDate           Schedule date.
     * @param boolean                                                            $silent                 Whether to send the message silently, without triggering notifications.
     * @param boolean                                                            $background             Send this message as background message
     * @param boolean                                                            $clearDraft             Clears the draft field
     * @param boolean                                                            $updateStickersetsOrder Whether to move used stickersets to top
     * @param boolean                                                            $forceResend            Whether to forcefully resend the file, even if its type and name are the same.
     * @param Cancellation                                                       $cancellation           Cancellation.
     */
    public function replyDocument(
        Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file,
        Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream|null $thumb = null,
        string $caption = '',
        ParseMode $parseMode = ParseMode::TEXT,
        ?callable $callback = null,
        ?string $fileName = null,
        ?string $mimeType = null,
        ?int $ttl = null,
        bool $spoiler = false,
        ?array $replyMarkup = null,
        int|string|null $sendAs = null,
        ?int $scheduleDate = null,
        bool $silent = false,
        bool $noForwards = false,
        bool $background = false,
        bool $clearDraft = false,
        bool $updateStickersetsOrder = false,
        bool $forceResend = false,
        ?Cancellation $cancellation = null,
    ): Message {
        return $this->getClient()->sendDocument(
            peer: $this->chatId,
            file: $file,
            thumb: $thumb,
            caption: $caption,
            parseMode: $parseMode,
            callback: $callback,
            fileName: $fileName,
            mimeType: $mimeType,
            ttl: $ttl,
            spoiler: $spoiler,
            replyToMsgId: $this->id,
            topMsgId: $this->topicId === 1 ? null : $this->topicId,
            replyMarkup: $replyMarkup,
            sendAs: $sendAs,
            scheduleDate: $scheduleDate,
            silent: $silent,
            noForwards: $noForwards,
            background: $background,
            clearDraft: $clearDraft,
            updateStickersetsOrder: $updateStickersetsOrder,
            forceResend: $forceResend,
            cancellation: $cancellation,
        );
    }

    /**
     * Reply a video.
     *
     * Please use named arguments to call this method.
     *
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file                   File to upload: can be a message to reuse media present in a message.
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream|null $thumb                  Optional: Thumbnail to upload
     * @param string                                                        $caption                Caption of document
     * @param ParseMode                                                     $parseMode              Text parse mode for the caption
     * @param ?callable(float, float, float)                                $callback               Upload callback (percent, speed in mbps, seconds elapsed)
     * @param ?string                                                       $fileName               Optional file name, if absent will be extracted from the passed $file.
     * @param integer|null                                                  $ttl                     Time to live
     * @param boolean                                                       $spoiler                 Whether the message is a spoiler
     * @param boolean                                                       $roundMessage            Whether the message should be round
     * @param boolean                                                       $supportsStreaming        Whether the video supports streaming
     * @param boolean                                                       $noSound                 Whether the video has no sound
     * @param integer|null                                                  $duration                Duration of the video
     * @param integer|null                                                  $width                   Width of the video
     * @param integer|null                                                  $height                  Height of the video
     * @param array|null                                                    $replyMarkup             Keyboard information.
     * @param integer|string|null                                           $sendAs                 Peer to send the message as.
     * @param integer|null                                                  $scheduleDate            Schedule date.
     * @param boolean                                                       $silent                  Whether to send the message silently, without triggering notifications.
     * @param boolean                                                       $noForwards              Whether to disable forwards for this message.
     * @param boolean                                                       $background              Send this message as background message
     * @param boolean                                                       $clearDraft              Clears the draft field
     * @param boolean                                                       $forceResend             Whether to forcefully resend the file, even if its type and name are the same.
     * @param Cancellation                                                  $cancellation            Cancellation.
     *
     */
    public function replyVideo(
        Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file,
        Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream|null $thumb = null,
        string $caption = '',
        ParseMode $parseMode = ParseMode::TEXT,
        ?callable $callback = null,
        ?string $fileName = null,
        string $mimeType = 'video/mp4',
        ?int $ttl = null,
        bool $spoiler = false,
        bool $roundMessage = false,
        bool $supportsStreaming = true,
        bool $noSound = false,
        ?int $duration = null,
        ?int $width = null,
        ?int $height = null,
        ?array $replyMarkup = null,
        int|string|null $sendAs = null,
        ?int $scheduleDate = null,
        bool $silent = false,
        bool $noForwards = false,
        bool $background = false,
        bool $clearDraft = false,
        bool $forceResend = false,
        bool $updateStickersetsOrder = false,
        ?Cancellation $cancellation = null,
    ): Message {
        return $this->getClient()->sendVideo(
            peer: $this->chatId,
            file: $file,
            thumb: $thumb,
            caption: $caption,
            parseMode: $parseMode,
            callback: $callback,
            fileName: $fileName,
            mimeType: $mimeType,
            ttl: $ttl,
            spoiler: $spoiler,
            roundMessage: $roundMessage,
            supportsStreaming: $supportsStreaming,
            noSound: $noSound,
            duration: $duration,
            width: $width,
            height: $height,
            replyToMsgId: $this->id,
            topMsgId: $this->topicId === 1 ? null : $this->topicId,
            replyMarkup: $replyMarkup,
            sendAs: $sendAs,
            scheduleDate: $scheduleDate,
            silent: $silent,
            noForwards: $noForwards,
            background: $background,
            clearDraft: $clearDraft,
            forceResend: $forceResend,
            updateStickersetsOrder: $updateStickersetsOrder,
            cancellation: $cancellation,
        );
    }

    /**
     * Reply a gif.
     *
     * Please use named arguments to call this method.
     *
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file                   File to upload: can be a message to reuse media present in a message.
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream|null $thumb                  Optional: Thumbnail to upload
     * @param string                                                        $caption                Caption of document
     * @param ParseMode                                                     $parseMode              Text parse mode for the caption
     * @param ?callable(float, float, float)                                $callback               Upload callback (percent, speed in mbps, seconds elapsed)
     * @param ?string                                                       $fileName               Optional file name, if absent will be extracted from the passed $file.
     * @param integer|null                                                  $ttl                     Time to live
     * @param boolean                                                       $spoiler                 Whether the message is a spoiler
     * @param array|null                                                    $replyMarkup             Keyboard information.
     * @param integer|string|null                                           $sendAs                 Peer to send the message as.
     * @param integer|null                                                  $scheduleDate            Schedule date.
     * @param boolean                                                       $silent                  Whether to send the message silently, without triggering notifications.
     * @param boolean                                                       $noForwards              Whether to disable forwards for this message.
     * @param boolean                                                       $background              Send this message as background message
     * @param boolean                                                       $clearDraft              Clears the draft field
     * @param boolean                                                       $forceResend             Whether to forcefully resend the file, even if its type and name are the same.
     * @param ?Cancellation                                                  $cancellation            Cancellation.
     *
     */
    public function replyGif(
        Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file,
        Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream|null $thumb = null,
        string $caption = '',
        ParseMode $parseMode = ParseMode::TEXT,
        ?callable $callback = null,
        ?string $fileName = null,
        ?int $ttl = null,
        bool $spoiler = false,
        ?array $replyMarkup = null,
        int|string|null $sendAs = null,
        ?int $scheduleDate = null,
        bool $silent = false,
        bool $noForwards = false,
        bool $background = false,
        bool $clearDraft = false,
        bool $forceResend = false,
        ?Cancellation $cancellation = null,
    ): Message {
        return $this->getClient()->sendGif(
            peer: $this->chatId,
            file: $file,
            thumb: $thumb,
            caption: $caption,
            parseMode: $parseMode,
            callback: $callback,
            fileName: $fileName,
            ttl: $ttl,
            spoiler: $spoiler,
            replyToMsgId: $this->id,
            topMsgId: $this->topicId === 1 ? null : $this->topicId,
            replyMarkup: $replyMarkup,
            sendAs: $sendAs,
            scheduleDate: $scheduleDate,
            silent: $silent,
            noForwards: $noForwards,
            background: $background,
            clearDraft: $clearDraft,
            forceResend: $forceResend,
            cancellation: $cancellation,
        );
    }

    /**
     * Reply an audio.
     *
     * Please use named arguments to call this method.
     *
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file                   File to upload: can be a message to reuse media present in a message.
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream|null $thumb                  Optional: Thumbnail to upload
     * @param string                                                        $caption                Caption of document
     * @param ParseMode                                                     $parseMode              Text parse mode for the caption
     * @param ?callable(float, float, float)                                $callback               Upload callback (percent, speed in mbps, seconds elapsed)
     * @param ?string                                                       $fileName               Optional file name, if absent will be extracted from the passed $file.
     * @param integer|null                                                  $duration                Duration of the audio
     * @param string|null                                                   $title                   Title of the audio
     * @param string|null                                                   $performer               Performer of the audio
     * @param array|null                                                    $replyMarkup             Keyboard information.
     * @param integer|string|null                                           $sendAs                 Peer to send the message as.
     * @param integer|null                                                  $scheduleDate            Schedule date.
     * @param boolean                                                       $silent                  Whether to send the message silently, without triggering notifications.
     * @param boolean                                                       $noForwards              Whether to disable forwards for this message.
     * @param boolean                                                       $background              Send this message as background message
     * @param boolean                                                       $clearDraft              Clears the draft field
     * @param boolean                                                       $forceResend             Whether to forcefully resend the file, even if its type and name are the same.
     * @param ?Cancellation                                                  $cancellation            Cancellation.
     *
     */
    public function replyAudio(
        Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file,
        Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream|null $thumb = null,
        string $caption = '',
        ParseMode $parseMode = ParseMode::TEXT,
        ?callable $callback = null,
        ?string $fileName = null,
        ?string $mimeType = null,
        ?int $duration = null,
        ?string $title = null,
        ?string $performer = null,
        ?int $ttl = null,
        ?array $replyMarkup = null,
        int|string|null $sendAs = null,
        ?int $scheduleDate = null,
        bool $silent = false,
        bool $noForwards = false,
        bool $background = false,
        bool $clearDraft = false,
        bool $forceResend = false,
        ?Cancellation $cancellation = null,
    ): Message {
        return $this->getClient()->sendAudio(
            peer: $this->chatId,
            file: $file,
            thumb: $thumb,
            caption: $caption,
            parseMode: $parseMode,
            callback: $callback,
            fileName: $fileName,
            mimeType: $mimeType,
            duration: $duration,
            title: $title,
            performer: $performer,
            ttl: $ttl,
            replyToMsgId: $this->id,
            topMsgId: $this->topicId === 1 ? null : $this->topicId,
            replyMarkup: $replyMarkup,
            sendAs: $sendAs,
            scheduleDate: $scheduleDate,
            silent: $silent,
            noForwards: $noForwards,
            background: $background,
            clearDraft: $clearDraft,
            forceResend: $forceResend,
            cancellation: $cancellation
        );
    }

    /**
     * Reply a voice.
     *
     * Please use named arguments to call this method.
     *
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file                   File to upload: can be a message to reuse media present in a message.
     * @param string                                                        $caption                Caption of document
     * @param ParseMode                                                     $parseMode              Text parse mode for the caption
     * @param ?callable(float, float, float)                                $callback               Upload callback (percent, speed in mbps, seconds elapsed)
     * @param ?string                                                       $fileName               Optional file name, if absent will be extracted from the passed $file.
     * @param integer|null                                                  $ttl                     Time to live
     * @param integer|null                                                  $duration                Duration of the voice
     * @param array|null                                                    $waveform                Waveform of the voice
     * @param array|null                                                    $replyMarkup             Keyboard information.
     * @param integer|string|null                                           $sendAs                 Peer to send the message as.
     * @param integer|null                                                  $scheduleDate            Schedule date.
     * @param boolean                                                       $silent                  Whether to send the message silently, without triggering notifications.
     * @param boolean                                                       $noForwards              Whether to disable forwards for this message.
     * @param boolean                                                       $background              Send this message as background message
     * @param boolean                                                       $clearDraft              Clears the draft field
     * @param boolean                                                       $forceResend             Whether to forcefully resend the file, even if its type and name are the same.
     * @param ?Cancellation                                                  $cancellation            Cancellation.
     *
     */
    public function replyVoice(
        Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file,
        string $caption = '',
        ParseMode $parseMode = ParseMode::TEXT,
        ?callable $callback = null,
        ?string $fileName = null,
        ?int $ttl = null,
        ?int $duration = null,
        ?array $waveform = null,
        ?array $replyMarkup = null,
        int|string|null $sendAs = null,
        ?int $scheduleDate = null,
        bool $silent = false,
        bool $noForwards = false,
        bool $background = false,
        bool $clearDraft = false,
        bool $forceResend = false,
        ?Cancellation $cancellation = null,
    ): Message {
        return $this->getClient()->sendVoice(
            peer: $this->chatId,
            file: $file,
            caption: $caption,
            parseMode: $parseMode,
            callback: $callback,
            fileName: $fileName,
            ttl: $ttl,
            duration: $duration,
            waveform: $waveform,
            replyToMsgId: $this->id,
            topMsgId: $this->topicId === 1 ? null : $this->topicId,
            replyMarkup: $replyMarkup,
            sendAs: $sendAs,
            scheduleDate: $scheduleDate,
            silent: $silent,
            noForwards: $noForwards,
            background: $background,
            clearDraft: $clearDraft,
            forceResend: $forceResend,
            cancellation: $cancellation
        );
    }

    /**
     * Reply a photo.
     *
     * Please use named arguments to call this method.
     *
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file                   File to upload: can be a message to reuse media present in a message.
     * @param string                                                        $caption                Caption of document
     * @param ?callable(float, float, float)                                $callback               Upload callback (percent, speed in mbps, seconds elapsed)
     * @param ?string                                                       $fileName               Optional file name, if absent will be extracted from the passed $file.
     * @param ParseMode                                                     $parseMode              Text parse mode for the caption
     * @param array|null                                                    $replyMarkup            Keyboard information.
     * @param integer|null                                                  $sendAs                 Peer to send the message as.
     * @param integer|null                                                  $scheduleDate           Schedule date.
     * @param boolean                                                       $silent                 Whether to send the message silently, without triggering notifications.
     * @param boolean                                                       $background             Send this message as background message
     * @param boolean                                                       $clearDraft             Clears the draft field
     * @param boolean                                                       $updateStickersetsOrder Whether to move used stickersets to top
     * @param boolean                                                       $forceResend            Whether to forcefully resend the file, even if its type and name are the same.
     * @param Cancellation                                                  $cancellation           Cancellation.
     *
     */
    public function replyPhoto(
        Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file,
        string $caption = '',
        ParseMode $parseMode = ParseMode::TEXT,
        ?callable $callback = null,
        ?string $fileName = null,
        ?int $ttl = null,
        bool $spoiler = false,
        ?array $replyMarkup = null,
        int|string|null $sendAs = null,
        ?int $scheduleDate = null,
        bool $silent = false,
        bool $noForwards = false,
        bool $background = false,
        bool $clearDraft = false,
        bool $updateStickersetsOrder = false,
        bool $forceResend = false,
        ?Cancellation $cancellation = null,
    ): Message {
        return  $this->getClient()->sendPhoto(
            peer: $this->chatId,
            file: $file,
            caption: $caption,
            parseMode: $parseMode,
            callback: $callback,
            fileName: $fileName,
            ttl: $ttl,
            spoiler: $spoiler,
            replyToMsgId: $this->id,
            topMsgId: $this->topicId === 1 ? null : $this->topicId,
            replyMarkup: $replyMarkup,
            sendAs: $sendAs,
            scheduleDate: $scheduleDate,
            silent: $silent,
            noForwards: $noForwards,
            background: $background,
            clearDraft: $clearDraft,
            updateStickersetsOrder: $updateStickersetsOrder,
            forceResend: $forceResend,
            cancellation: $cancellation
        );
    }

    /**
     * Reply a sticker.
     *
     * Please use named arguments to call this method.
     *
     * @param Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file                   File to upload: can be a message to reuse media present in a message.
     * @param ?callable(float, float, float)                                $callback               Upload callback (percent, speed in mbps, seconds elapsed)
     * @param ?string                                                       $fileName               Optional file name, if absent will be extracted from the passed $file.
     * @param array|null                                                    $replyMarkup            Keyboard information.
     * @param integer|null                                                  $sendAs                 Peer to send the message as.
     * @param integer|null                                                  $scheduleDate           Schedule date.
     * @param boolean                                                       $silent                 Whether to send the message silently, without triggering notifications.
     * @param boolean                                                       $noForwards             Whether to disable forwards for this message.
     * @param boolean                                                       $background             Send this message as background message
     * @param boolean                                                       $clearDraft             Clears the draft field
     * @param boolean                                                       $updateStickersetsOrder Whether to move used stickersets to top
     * @param boolean                                                       $forceResend            Whether to forcefully resend the file, even if its type and name are the same.
     * @param Cancellation                                                  $cancellation           Cancellation.
     *
     */
    public function replySticker(
        Message|Media|LocalFile|RemoteUrl|BotApiFileId|ReadableStream $file,
        string $mimeType,
        string $emoji = '',
        ?callable $callback = null,
        ?string $fileName = null,
        ?int $ttl = null,
        ?array $replyMarkup = null,
        int|string|null $sendAs = null,
        ?int $scheduleDate = null,
        bool $silent = false,
        bool $noForwards = false,
        bool $background = false,
        bool $clearDraft = false,
        bool $updateStickersetsOrder = false,
        bool $forceResend = false,
        ?Cancellation $cancellation = null,
    ): Message {
        return $this->getClient()->sendSticker(
            peer: $this->chatId,
            file: $file,
            emoji: $emoji,
            callback: $callback,
            fileName: $fileName,
            mimeType: $mimeType,
            ttl: $ttl,
            replyToMsgId: $this->id,
            topMsgId: $this->topicId === 1 ? null : $this->topicId,
            replyMarkup: $replyMarkup,
            sendAs: $sendAs,
            scheduleDate: $scheduleDate,
            silent: $silent,
            noForwards: $noForwards,
            background: $background,
            clearDraft: $clearDraft,
            updateStickersetsOrder: $updateStickersetsOrder,
            forceResend: $forceResend,
            cancellation: $cancellation
        );
    }

    /**
     * Send a text message.
     *
     * @param string $message Message to send
     * @param ParseMode $parseMode Parse mode
     * @param array|null $replyMarkup Keyboard information.
     * @param integer|string|null $sendAs Peer to send the message as.
     * @param integer|null $scheduleDate Schedule date.
     * @param boolean $silent Whether to send the message silently, without triggering notifications.
     * @param boolean $noForwards Only for bots, disallows further re-forwarding and saving of the messages, even if the destination chat doesnâ€™t have [content protection](https://telegram.org/blog/protected-content-delete-by-date-and-more) enabled
     * @param boolean $background Send this message as background message
     * @param boolean $clearDraft Clears the draft field
     * @param boolean $noWebpage Set this flag to disable generation of the webpage preview
     * @param boolean $updateStickersetsOrder Whether to move used stickersets to top
     */
    public function sendText(
        string $message,
        ParseMode $parseMode = ParseMode::TEXT,
        ?array $replyMarkup = null,
        int|string|null $sendAs = null,
        ?int $scheduleDate = null,
        bool $noWebpage = false,
        bool $silent = false,
        bool $noForwards = false,
        bool $background = false,
        bool $clearDraft = false,
        bool $updateStickersetsOrder = false,
        ?Cancellation $cancellation = null
    ): Message {
        return $this->getClient()->sendMessage(
            peer: $this->chatId,
            message: $message,
            parseMode: $parseMode,
            topMsgId: $this->topicId === 1 ? null : $this->topicId,
            replyMarkup: $replyMarkup,
            sendAs: $sendAs,
            scheduleDate: $scheduleDate,
            silent: $silent,
            noForwards: $noForwards,
            background: $background,
            clearDraft: $clearDraft,
            noWebpage: $noWebpage,
            updateStickersetsOrder: $updateStickersetsOrder,
            cancellation: $cancellation,
        );
    }

    /**
     * Adds the user to the blacklist.
     *
     * @throws InvalidArgumentException
     */
    public function block(): bool
    {
        Assert::true($this->senderId > 0);
        return $this->getClient()->methodCallAsyncRead(
            'contacts.block',
            [
                'id' => $this->senderId,
            ],
        );
    }

    /**
     * Deletes the user from the blacklist.
     *
     * @throws InvalidArgumentException
     */
    public function unblock(): bool
    {
        Assert::true($this->senderId > 0);
        return $this->getClient()->methodCallAsyncRead(
            'contacts.unblock',
            [
                'id' => $this->senderId,
            ],
        );
    }

    /**
     * Get user stories.
     *
     * @return list<AbstractStory>
     * @throws InvalidArgumentException
     */
    public function getStories(): array
    {
        // TODO : support seen channel story
        Assert::true($this->senderId > 0);
        $client = $this->getClient();
        $result = $client->methodCallAsyncRead(
            'stories.getPeerStories',
            [
                'peer' => $this->senderId,
            ],
        )['stories']['stories'];
        $result = array_filter($result, static fn (array $t): bool => $t['_'] !== 'storyItemDeleted');
        // Recall it because storyItemSkipped
        // TODO: Do this more efficiently
        $result = $client->methodCallAsyncRead(
            'stories.getStoriesByID',
            [
                'peer' => $this->senderId,
                'id' => array_column($result, 'id'),
            ],
        )['stories'];
        return array_map(
            fn (array $arr): AbstractStory =>
                $arr['_'] === 'storyItemDeleted'
                    ? new StoryDeleted($client, ['peer' => $this->senderId, 'story' => $arr])
                    : new Story($client, ['peer' => $this->senderId, 'story' => $arr]),
            $result,
        );
    }

    /**
     * Sends a current user typing event
     * (see [SendMessageAction](https://docs.madelineproto.xyz/API_docs/types/SendMessageAction.html) for all event types) to a conversation partner or group.
     */
    public function setAction(Action $action = new Typing): bool
    {
        $action = $action->toRawAction() + [ 'msg_id' => $this->id ];
        return $this->getClient()->methodCallAsyncRead(
            'messages.setTyping',
            [
                'peer' => $this->chatId,
                'top_msg_id' => $this->topicId,
                'action' => $action,
            ],
        );
    }

    /**
     * Mark selected message as read.
     *
     * @return boolean if set, read all messages in current chat.
     */
    public function read(bool $readAll = false): bool
    {
        if (DialogId::isSupergroupOrChannelOrMonoforum($this->chatId)) {
            return $this->getClient()->methodCallAsyncRead(
                'channels.readHistory',
                [
                    'channel' => $this->chatId,
                    'max_id' => $readAll ? 0 : $this->id,
                ],
            );
        }
        $this->getClient()->methodCallAsyncRead(
            'messages.readHistory',
            [
                'peer' => $this->chatId,
                'max_id' => $readAll ? 0 : $this->id,
            ],
        );
        return true;
    }

    /**
     * Set maximum Time-To-Live of all messages in the specified chat.
     *
     * @param int<1, max> $seconds Automatically delete all messages sent in the chat after this many seconds
     * @throws InvalidArgumentException
     */
    public function enableTTL(int $seconds = 86400): DialogSetTTL
    {
        Assert::false($seconds <= 0);
        $client = $this->getClient();
        $result = $client->methodCallAsyncRead(
            'messages.setHistoryTTL',
            [
                'peer' => $this->chatId,
                'period' => $seconds,
            ],
        );
        return $client->wrapMessage($client->extractMessage($result));
    }

    /**
     * Disable Time-To-Live of all messages in the specified chat.
     */
    public function disableTTL(): DialogSetTTL
    {
        $client = $this->getClient();
        $result = $client->methodCallAsyncRead(
            'messages.setHistoryTTL',
            [
                'peer' => $this->chatId,
                'period' => 0,
            ],
        );
        return $client->wrapMessage($client->extractMessage($result));
    }

    /**
     * Show the [real-time chat translation popup](https://core.telegram.org/api/translation) for a certain chat.
     */
    public function enableAutoTranslate(): bool
    {
        return $this->getClient()->methodCallAsyncRead(
            'messages.togglePeerTranslations',
            [
                'peer' => $this->chatId,
                'disabled' => false,
            ],
        );
    }

    /**
     * Hide the [real-time chat translation popup](https://core.telegram.org/api/translation) for a certain chat.
     */
    public function disableAutoTranslate(): bool
    {
        return $this->getClient()->methodCallAsyncRead(
            'messages.togglePeerTranslations',
            [
                'peer' => $this->chatId,
                'disabled' => true,
            ],
        );
    }
}
<?php declare(strict_types=1);

namespace danog\MadelineProto\EventHandler\Payments;

use danog\MadelineProto\EventHandler\Media\StaticSticker;
use danog\MadelineProto\EventHandler\Media\Sticker;
use danog\MadelineProto\Ipc\IpcCapable;
use danog\MadelineProto\MTProto;

final class StarGift extends IpcCapable implements \JsonSerializable
{
    /** Show the gift is limited or not. */
    public readonly ?bool $limited;
    /** Show the gift is soldOut or not. */
    public readonly ?bool $soldOut;
    /** Gift id. */
    public readonly int $id;
    /** Gift sticker info. */
    public readonly ?Sticker $sticker;
    /** Amount of stars that need for buying this gift. */
    public readonly int $stars;
    /** Amount of gift that left. */
    public readonly ?int $availabilityRemains;
    /** Amount of total gift. */
    public readonly ?int $availabilityTotal;
    public readonly int $convertStars;
    /** Show timestamp for first buy of the gift */
    public readonly ?int $startSell;
    /** Show timestamp for last buy of the gift */
    public readonly ?int $endSell;

    public function __construct(MTProto $API, array $rawStarGift)
    {
        $this->limited = $rawStarGift['limited'] ?? null;
        $this->soldOut = $rawStarGift['sold_out'] ?? null;
        $this->id = $rawStarGift['id'];
        $this->sticker = isset($rawStarGift['sticker']) ?
            new StaticSticker(
                $API,
                $rawStarGift['sticker'],
                $rawStarGift['sticker']['attributes'],
                $rawStarGift['sticker']['attributes'],
                false
            ) : null;
        $this->stars = $rawStarGift['stars'];
        $this->availabilityRemains = $rawStarGift['availability_remains'] ?? null;
        $this->availabilityTotal = $rawStarGift['availability_total'] ?? null;
        $this->convertStars = $rawStarGift['convert_stars'];
        $this->startSell = $rawStarGift['first_sale_date'] ?? null;
        $this->endSell = $rawStarGift['last_sale_date'] ?? null;
        parent::__construct($API);
    }

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function jsonSerialize(): mixed
    {
        $v = get_object_vars($this);
        unset($v['API'], $v['session']);
        $v['_'] = static::class;
        return $v;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Mahdi <mahdi.talaee1379@gmail.com>
 * @copyright 2016-2025 Mahdi <mahdi.talaee1379@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Payments;

use danog\MadelineProto\EventHandler\Update;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\TL\Types\Bytes;

/**
 * This object contains information about an incoming pre-checkout query.
 */
final class Payment extends Update
{
    /** Unique query identifier */
    public readonly int $queryId;
    /** User who sent the query */
    public readonly int $userId;
    /** Bot specified invoice payload */
    public readonly Bytes $payload;
    /** Order info provided by the user */
    public readonly ?PaymentRequestedInfo $info;
    /** Identifier of the shipping option chosen by the user */
    public readonly ?string $shippingOptionId;
    /** Three-letter ISO 4217 currency code */
    public readonly string $currency;
    /**Total amount in the smallest units of the currency (integer, not float/double). */
    public readonly int $totalAmount;
    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(MTProto $API, array $rawRequestedPayment)
    {
        parent::__construct($API);
        $this->queryId = $rawRequestedPayment['query_id'];
        $this->userId = $rawRequestedPayment['user_id'];
        $this->payload = $rawRequestedPayment['payload'];
        $this->info = isset($rawRequestedPayment['info'])
            ? PaymentRequestedInfo::fromRawInfo($rawRequestedPayment['info'])
            : null;
        $this->shippingOptionId = $rawRequestedPayment['shipping_option_id'] ?? null;
        $this->currency = $rawRequestedPayment['currency'];
        $this->totalAmount = $rawRequestedPayment['total_amount'];

    }

    /**
     * Accept pending payment.
     * note that you must call this function or reject function up to 10 seconds after user accept payment!!.
     */
    public function accept(): true
    {
        return $this->getClient()->methodCallAsyncRead(
            'messages.setBotPrecheckoutResults',
            [
                'success' => true,
                'query_id' => $this->queryId,
            ]
        );
    }

    /**
     * Reject pending payment.
     * note that you must call this function or accept function up to 10 seconds after user accept payment!!.
     * @param string $errorMessage if the success isnâ€™t set. Error message in human-readable form that explains the reason for failure to proceed with the checkout
     */
    public function reject(string $errorMessage): false
    {
        return $this->getClient()->methodCallAsyncRead(
            'messages.setBotPrecheckoutResults',
            [
                'success' => false,
                'query_id' => $this->queryId,
                'error' => $errorMessage,
            ]
        );
    }
}
<?php declare(strict_types=1);

namespace danog\MadelineProto\EventHandler\Payments;

use JsonSerializable;
use ReflectionClass;
use ReflectionProperty;

/**
 * Order info provided by the user.
 */
final class PaymentRequestedInfo implements JsonSerializable
{
    /**
     * @psalm-mutation-free
     */
    public function __construct(
        /** Userâ€™s full name */
        public readonly ?string $name,
        /** Userâ€™s phone number */
        public readonly ?string $phone,
        /** Userâ€™s email address */
        public readonly ?string $email,
        /** Userâ€™s shipping address */
        public readonly ?PostAddress $shippingAddress
    ) {

    }

    /**
     * @internal
     *
     * @psalm-pure
     */
    public static function fromRawInfo(array $rawInfo): self
    {
        return new self(
            $rawInfo['name'] ?? null,
            $rawInfo['phone'] ?? null,
            $rawInfo['email'] ?? null,
            isset($rawInfo['shipping_address']) ? new PostAddress(
                $rawInfo['shipping_address']['street_line1'],
                $rawInfo['shipping_address']['street_line2'],
                $rawInfo['shipping_address']['city'],
                $rawInfo['shipping_address']['state'],
                $rawInfo['shipping_address']['country_iso2'],
                $rawInfo['shipping_address']['post_code'],
            ) : null,
        );
    }

    /** @internal */
    #[\Override]
    public function jsonSerialize(): mixed
    {
        $res = ['_' => static::class];
        $refl = new ReflectionClass($this);
        foreach ($refl->getProperties(ReflectionProperty::IS_PUBLIC) as $prop) {
            $res[$prop->getName()] = $prop->getValue($this);
        }
        return $res;
    }
}
<?php declare(strict_types=1);

namespace danog\MadelineProto\EventHandler\Payments;

use JsonSerializable;
use ReflectionClass;
use ReflectionProperty;

/**
 * Shipping address.
 */
final class PostAddress implements JsonSerializable
{
    /**
     * @psalm-mutation-free
     */
    public function __construct(
        /** First line of the address */
        public readonly string $streetLine1,
        /** Second line of the address */
        public readonly string $streetLine2,
        /** City */
        public readonly string $city,
        /** State, if applicable (empty otherwise) */
        public readonly string $state,
        /** ISO 3166-1 alpha-2 country code */
        public readonly string $countryIso2,
        /** Address post code */
        public readonly string $postCode
    ) {

    }

    /** @internal */
    #[\Override]
    public function jsonSerialize(): mixed
    {
        $res = ['_' => static::class];
        $refl = new ReflectionClass($this);
        foreach ($refl->getProperties(ReflectionProperty::IS_PUBLIC) as $prop) {
            $res[$prop->getName()] = $prop->getValue($this);
        }
        return $res;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Mahdi <mahdi.talaee1379@gmail.com>
 * @copyright 2016-2025 Mahdi <mahdi.talaee1379@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Payments;

use JsonSerializable;
use ReflectionClass;
use ReflectionProperty;

/**
 * Payment identifier.
 */
final class PaymentCharge implements JsonSerializable
{

    /**
     * @psalm-mutation-free
     */
    public function __construct(
        /** Telegram payment identifier */
        public readonly string $id,
        /** Provider payment identifier */
        public readonly string $providerChargeId
    ) {

    }

    /** @internal */
    #[\Override]
    public function jsonSerialize(): mixed
    {
        $res = ['_' => static::class];
        $refl = new ReflectionClass($this);
        foreach ($refl->getProperties(ReflectionProperty::IS_PUBLIC) as $prop) {
            $res[$prop->getName()] = $prop->getValue($this);
        }
        return $res;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\SimpleFilter;

/** Allows only stickers */
/** @psalm-mutable */
interface HasSticker
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\SimpleFilter;

/** Allows messages from the bot admin */
/** @psalm-mutable */
interface FromAdmin
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\SimpleFilter;

/** Allows only document photos */
/** @psalm-mutable */
interface HasDocumentPhoto
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\SimpleFilter;

/** Allows only messages that reply to one of our messages */
/** @psalm-mutable */
interface IsReplyToSelf
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\SimpleFilter;

/** Allows only messages that reply to other messages */
/** @psalm-mutable */
interface IsReply
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\SimpleFilter;

/** Allows only messages that contain a quiz poll */
/** @psalm-mutable */
interface HasQuizPoll
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\SimpleFilter;

/** Allows only messages with no media */
/** @psalm-mutable */
interface HasNoMedia
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\SimpleFilter;

/** Allows only audio messages */
/** @psalm-mutable */
interface HasAudio
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\SimpleFilter;

/** Represents an incoming message */
/** @psalm-mutable */
interface Incoming
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\SimpleFilter;

/** Represents an ended call */
/** @psalm-mutable */
interface Ended
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\SimpleFilter;

/** Allows only messages that contain a live location */
/** @psalm-mutable */
interface HasLiveLocation
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\SimpleFilter;

/** Allow only currently running calls */
/** @psalm-mutable */
interface Running
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\SimpleFilter;

/** Allows only forwarded messages */
/** @psalm-mutable */
interface IsForwarded
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\SimpleFilter;

/** Allow only messages coming from groups that has topics (Supergroups only). */
/** @psalm-mutable */
interface HasTopic
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\SimpleFilter;

/** Allows only messages that contain a single poll */
/** @psalm-mutable */
interface HasSinglePoll
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\SimpleFilter;

/** Allows only messages that contain a venue */
/** @psalm-mutable */
interface HasVenue
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\SimpleFilter;

/** Allows only photos */
/** @psalm-mutable */
interface HasPhoto
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\SimpleFilter;

/** Allows only voice messages */
/** @psalm-mutable */
interface HasVoice
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\SimpleFilter;

/** Allows only media messages */
/** @psalm-mutable */
interface HasMedia
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\SimpleFilter;

/** Allows only messages that contain a poll */
/** @psalm-mutable */
interface HasPoll
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\SimpleFilter;

/** Allows only documents */
/** @psalm-mutable */
interface HasDocument
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\SimpleFilter;

/** Allows only videos */
/** @psalm-mutable */
interface HasVideo
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\SimpleFilter;

/** Allows messages that weren't edited. */
/** @psalm-mutable */
interface IsNotEdited
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\SimpleFilter;

/** Allows messages that were edited. */
/** @psalm-mutable */
interface IsEdited
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\SimpleFilter;

/** Allows messages from the bot admin or outgoing messages */
/** @psalm-mutable */
interface FromAdminOrOutgoing
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\SimpleFilter;

/** Represents an outgoing message */
/** @psalm-mutable */
interface Outgoing
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\SimpleFilter;

/** Allows only round videos */
/** @psalm-mutable */
interface HasRoundVideo
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\SimpleFilter;

/** Allows only GIFs */
/** @psalm-mutable */
interface HasGif
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\SimpleFilter;

/** Allows only messages that contain a multiple poll */
/** @psalm-mutable */
interface HasMultiplePoll
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\SimpleFilter;

/** Allows only messages that contain a location (a geo point, a live location or a venue) */
/** @psalm-mutable */
interface HasLocation
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler;

use JsonSerializable;
use ReflectionClass;
use ReflectionProperty;

/**
 * Represents a bot command that can be used in a chat.
 */
final class Command implements JsonSerializable
{
    /** `/command` name. */
    public readonly string $command;

    /** Description of the command. */
    public readonly string $description;

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(array $rawCommand)
    {
        $this->command = $rawCommand['command'];
        $this->description = $rawCommand['description'];
    }

    /** @internal */
    #[\Override]
    public function jsonSerialize(): mixed
    {
        $res = ['_' => static::class];
        $refl = new ReflectionClass($this);
        foreach ($refl->getProperties(ReflectionProperty::IS_PUBLIC) as $prop) {
            $res[$prop->getName()] = $prop->getValue($this);
        }
        return $res;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link      https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Channel;

use danog\MadelineProto\EventHandler\ChatInvite;
use danog\MadelineProto\EventHandler\Participant;
use danog\MadelineProto\EventHandler\Participant\Left;
use danog\MadelineProto\EventHandler\Update;
use danog\MadelineProto\MTProto;

/**
 * A participant has left, joined, was banned or admin'd in a [channel or supergroup](https://core.telegram.org/api/channel).
 */
final class ChannelParticipant extends Update
{
    /** Whether the participant joined using a [chat folder deep link Â»](https://core.telegram.org/api/links#chat-folder-links). */
    public readonly bool $viaChatlist;

    /** Channel ID */
    public readonly int $chatId;

    /** Date of the event */
    public readonly int $date;

    /** User that triggered the change (inviter, admin that kicked the user, or the even the userId itself) */
    public readonly int $actorId;

    /** User that was affected by the change */
    public readonly int $userId;

    /** Previous participant status */
    public readonly ?Participant $prevParticipant;

    /** New participant status */
    public readonly ?Participant $newParticipant;

    /** Chat invite used to join the channel/supergroup */
    public readonly ?ChatInvite $inviteLink;

    /**
     * @psalm-mutation-free
     */
    public function __construct(MTProto $API, array $rawChannelParticipant)
    {
        parent::__construct($API);
        $this->viaChatlist = $rawChannelParticipant['via_chatlist'];
        $this->chatId = $rawChannelParticipant['channel_id'];
        $this->date = $rawChannelParticipant['date'];
        $this->actorId = $rawChannelParticipant['actor_id'];
        $this->userId = $rawChannelParticipant['user_id'];
        $this->inviteLink = isset($rawChannelParticipant['invite'])
            ? ChatInvite::fromRawChatInvite($rawChannelParticipant['invite'])
            : null;
        // If null, user wasn't a participant.
        $this->prevParticipant = isset($rawChannelParticipant['prev_participant'])
            ? Participant::fromRawParticipant($rawChannelParticipant['prev_participant'])
            : new Left(['peer' => $this->userId]);
        // If null, user has left.
        $this->newParticipant = isset($rawChannelParticipant['new_participant'])
            ? Participant::fromRawParticipant($rawChannelParticipant['new_participant'])
            : new Left(['peer' => $this->userId]);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Channel;

use danog\MadelineProto\EventHandler\Update;
use danog\MadelineProto\MTProto;

/**
 * Indicates that the forward counter of a message in a channel has changed.
 */
final class MessageForwards extends Update
{
    /** Channel ID */
    public readonly int $chatId;

    /** ID of the message */
    public readonly int $id;

    /** New forward counter */
    public readonly int $forwards;

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(MTProto $API, array $rawMessageViews)
    {
        parent::__construct($API);
        $this->chatId = $rawMessageViews['channel_id'];
        $this->id = $rawMessageViews['id'];
        $this->forwards = $rawMessageViews['forwards'];
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Channel;

use danog\MadelineProto\EventHandler\Update;
use danog\MadelineProto\MTProto;

/**
 * A new channel is available, or info about an existing channel was changed.
 */
final class UpdateChannel extends Update
{
    /** Channel ID */
    public readonly int $chatId;

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(MTProto $API, array $rawUpdateChannel)
    {
        parent::__construct($API);
        $this->chatId = $API->getIdInternal($rawUpdateChannel);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Channel;

use danog\MadelineProto\EventHandler\Update;
use danog\MadelineProto\MTProto;

/**
 * Indicates that the view counter of a message in a channel has changed.
 */
final class MessageViewsChanged extends Update
{
    /** Channel ID */
    public readonly int $chatId;

    /** ID of the message */
    public readonly int $id;

    /** New view counter */
    public readonly int $views;

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(MTProto $API, array $rawMessageViews)
    {
        parent::__construct($API);
        $this->chatId = $API->getIdInternal($rawMessageViews);
        $this->id = $rawMessageViews['id'];
        $this->views = $rawMessageViews['views'];
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Delete;

use danog\MadelineProto\EventHandler\Delete;
use danog\MadelineProto\MTProto;

/**
 * Some messages were deleted in a private chat or simple group.
 */
final class DeleteMessages extends Delete
{
    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(MTProto $API, array $rawDelete)
    {
        parent::__construct($API, $rawDelete);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Delete;

use danog\MadelineProto\EventHandler\Delete;
use danog\MadelineProto\MTProto;

/**
 * Some [scheduled messages](https://core.telegram.org/api/scheduled-messages) were deleted from the schedule queue of a chat.
 */
final class DeleteScheduledMessages extends Delete
{
    /** Peer */
    public readonly int $chatId;

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(MTProto $API, array $rawDelete)
    {
        parent::__construct($API, $rawDelete);
        $this->chatId = $API->getIdInternal($rawDelete['peer']);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Delete;

use danog\MadelineProto\EventHandler\Delete;
use danog\MadelineProto\MTProto;

/**
 * Some messages in a [supergroup/channel](https://core.telegram.org/api/channel) were deleted.
 */
final class DeleteChannelMessages extends Delete
{
    /** Channel ID */
    public readonly int $chatId;

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(MTProto $API, array $rawDelete)
    {
        parent::__construct($API, $rawDelete);
        $this->chatId = $API->getIdInternal($rawDelete);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler;

use AssertionError;
use danog\MadelineProto\CallNotAllowedException;
use danog\MadelineProto\EventHandler\Calls\CallDenialReason;
use danog\MadelineProto\EventHandler\Calls\GroupCall;
use danog\MadelineProto\MTProto;

/**
 * Represents a group or channel message.
 */
abstract class GroupOrChannelMessage extends Message
{
    /** @internal */
    public function __construct(MTProto $API, array $rawMessage, array $info, bool $scheduled)
    {
        parent::__construct($API, $rawMessage, $info, $scheduled);
    }

    /**
     * Get info about a [channel/supergroup](https://core.telegram.org/api/channel) participant.
     *
     * @param  string|integer|null $member Participant to get info about; can be empty or null to get info about the sender of the message.
     * @throws AssertionError
     */
    public function getMember(string|int|null $member = null): Participant
    {
        $client = $this->getClient();
        $member ??= $this->senderId;
        $result = $client->methodCallAsyncRead(
            'channels.getParticipant',
            [
                'channel' => $this->chatId,
                'participant' => $member,
            ]
        )['participant'];
        return Participant::fromRawParticipant($result);
    }

    /**
     * Enable [content protection](https://telegram.org/blog/protected-content-delete-by-date-and-more) on a chat.
     *
     */
    public function enableProtection(): void
    {
        $this->getClient()->methodCallAsyncRead(
            'messages.toggleNoForwards',
            [
                'peer' => $this->chatId,
                'enabled' => true,
            ]
        );
    }

    /**
     * Disable [content protection](https://telegram.org/blog/protected-content-delete-by-date-and-more) on a chat.
     *
     */
    public function disableProtection(): void
    {
        $this->getClient()->methodCallAsyncRead(
            'messages.toggleNoForwards',
            [
                'peer' => $this->chatId,
                'enabled' => false,
            ]
        );
    }

    /**
     * Get the video chat or livestream currently active in this chat, if any.
     */
    #[\Override]
    public function getCall(): ?GroupCall
    {
        return $this->getClient()->getGroupCall($this->chatId);
    }

    /**
     * Whether {@see self::requestCall()} will succeed, i.e. there is already an active video chat or
     * livestream, or we have the rights to create one (`manage_call` admin right).
     */
    #[\Override]
    public function canRequestCall(): bool
    {
        return $this->getCall() !== null || $this->getCallDenialReason() === null;
    }

    /**
     * Get the video chat or livestream currently active in this chat, or create one if there is none.
     *
     * Requires the `manage_call` admin right to create a new call, see
     * [video chats/livestreams Â»](https://core.telegram.org/api/group-calls#video-chats-livestreams).
     *
     * Note that the call is not joined automatically, use `join()` on the returned call to join it.
     *
     * @param string|null $title        Custom title for a newly created call, defaults to the chat name.
     * @param int|null    $scheduleDate If set, a newly created call is scheduled to start at the specified UNIX timestamp.
     * @param bool        $rtmpStream   Whether the media of a newly created call is published by an external RTMP application.
     *
     * @throws CallNotAllowedException If there is no active call, and we can't create one.
     */
    #[\Override]
    public function requestCall(?string $title = null, ?int $scheduleDate = null, bool $rtmpStream = false): GroupCall
    {
        $call = $this->getCall();
        if ($call !== null) {
            return $call;
        }
        $reason = $this->getCallDenialReason();
        if ($reason !== null) {
            throw new CallNotAllowedException($reason);
        }
        return $this->getClient()->createGroupCall($this->chatId, $title, $scheduleDate, $rtmpStream);
    }

    /**
     * Returns why we can't create a group call in this chat, or null if we can.
     */
    #[\Override]
    public function getCallDenialReason(): ?CallDenialReason
    {
        $client = $this->getClient();
        if ($client->isSelfBot()) {
            return CallDenialReason::BOTS_CANNOT_CREATE_GROUP_CALLS;
        }
        $chat = $client->getInfo($this->chatId)['Chat'] ?? [];
        if (($chat['left'] ?? false) || ($chat['deactivated'] ?? false)) {
            return CallDenialReason::NOT_A_MEMBER;
        }
        if (!($chat['creator'] ?? false) && !($chat['admin_rights']['manage_call'] ?? false)) {
            return CallDenialReason::MANAGE_CALL_RIGHT_REQUIRED;
        }
        return null;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Attributes;

use Attribute;

/**
 * Attribute that enables periodic execution of a certain method.
 *
 * @psalm-immutable
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class Cron
{
    /**
     * @psalm-mutation-free
     */
    public function __construct(
        public readonly float $period
    ) {
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Attributes;

use Attribute;

/**
 * Attribute that marks a handler method.
 *
 * @psalm-pure
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class Handler
{
}
<?php declare(strict_types=1);
/**
 * This file is automatically generated by the build_docs.php file
 * and is used only for autocompletion in multiple IDEs
 * don't modify it manually.
 *
 * @generated Automatically generated by build_docs.php â€” do not edit.
 */

namespace danog\MadelineProto\EventHandler;

/**
 * @internal An internal interface used to avoid type errors when using simple filters.
 * @psalm-mutable
 */
interface SimpleFilters extends \danog\MadelineProto\EventHandler\SimpleFilter\Ended, \danog\MadelineProto\EventHandler\SimpleFilter\FromAdmin, \danog\MadelineProto\EventHandler\SimpleFilter\FromAdminOrOutgoing, \danog\MadelineProto\EventHandler\SimpleFilter\HasAudio, \danog\MadelineProto\EventHandler\SimpleFilter\HasDocument, \danog\MadelineProto\EventHandler\SimpleFilter\HasDocumentPhoto, \danog\MadelineProto\EventHandler\SimpleFilter\HasGif, \danog\MadelineProto\EventHandler\SimpleFilter\HasLiveLocation, \danog\MadelineProto\EventHandler\SimpleFilter\HasLocation, \danog\MadelineProto\EventHandler\SimpleFilter\HasMedia, \danog\MadelineProto\EventHandler\SimpleFilter\HasMultiplePoll, \danog\MadelineProto\EventHandler\SimpleFilter\HasNoMedia, \danog\MadelineProto\EventHandler\SimpleFilter\HasPhoto, \danog\MadelineProto\EventHandler\SimpleFilter\HasPoll, \danog\MadelineProto\EventHandler\SimpleFilter\HasQuizPoll, \danog\MadelineProto\EventHandler\SimpleFilter\HasRoundVideo, \danog\MadelineProto\EventHandler\SimpleFilter\HasSinglePoll, \danog\MadelineProto\EventHandler\SimpleFilter\HasSticker, \danog\MadelineProto\EventHandler\SimpleFilter\HasTopic, \danog\MadelineProto\EventHandler\SimpleFilter\HasVenue, \danog\MadelineProto\EventHandler\SimpleFilter\HasVideo, \danog\MadelineProto\EventHandler\SimpleFilter\HasVoice, \danog\MadelineProto\EventHandler\SimpleFilter\Incoming, \danog\MadelineProto\EventHandler\SimpleFilter\IsEdited, \danog\MadelineProto\EventHandler\SimpleFilter\IsForwarded, \danog\MadelineProto\EventHandler\SimpleFilter\IsNotEdited, \danog\MadelineProto\EventHandler\SimpleFilter\IsReply, \danog\MadelineProto\EventHandler\SimpleFilter\IsReplyToSelf, \danog\MadelineProto\EventHandler\SimpleFilter\Outgoing, \danog\MadelineProto\EventHandler\SimpleFilter\Running
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler;

use danog\MadelineProto\EventHandler\Media\DocumentPhoto;
use danog\MadelineProto\EventHandler\Wallpaper\WallpaperSettings;
use danog\MadelineProto\MTProto;
use JsonSerializable;
use ReflectionClass;
use ReflectionProperty;

/**
 * Represents a [wallpaper](https://core.telegram.org/api/wallpapers).
 */
final class Wallpaper implements JsonSerializable
{
    /** Identifier */
    public readonly int $id;

    /** Access hash */
    public readonly int $accessHash;

    /** Whether we created this wallpaper */
    public readonly bool $creator;

    /**  Whether this is the default wallpaper */
    public readonly bool $default;

    /** Whether this is a [pattern wallpaper](https://core.telegram.org/api/wallpapers#pattern-wallpapers) */
    public readonly bool $pattern;

    /** Whether this wallpaper should be used in dark mode. */
    public readonly bool $dark;

    /** Unique wallpaper ID, used when generating [wallpaper links](https://core.telegram.org/api/links#wallpaper-links) or [importing wallpaper links](https://core.telegram.org/api/wallpapers). */
    public readonly string $uniqueId;

    /** The actual wallpaper */
    public readonly DocumentPhoto $media;

    /** Info on how to generate the wallpaper, according to [these instructions](https://core.telegram.org/api/wallpapers). */
    public readonly ?WallpaperSettings $settings;

    /** @internal */
    public function __construct(MTProto $API, array $rawWallpaper)
    {
        $this->id = $rawWallpaper['id'];
        $this->accessHash = $rawWallpaper['access_hash'];
        $this->creator = $rawWallpaper['creator'];
        $this->default = $rawWallpaper['default'];
        $this->pattern = $rawWallpaper['pattern'];
        $this->dark = $rawWallpaper['dark'];
        $this->uniqueId = $rawWallpaper['slug'];
        $this->media = $API->wrapMedia($rawWallpaper['document']);
        $this->settings = isset($rawWallpaper['settings'])
            ? new WallpaperSettings($rawWallpaper['settings'])
            : null;
    }

    /** @internal */
    #[\Override]
    public function jsonSerialize(): mixed
    {
        $res = ['_' => static::class];
        $refl = new ReflectionClass($this);
        foreach ($refl->getProperties(ReflectionProperty::IS_PUBLIC) as $prop) {
            $res[$prop->getName()] = $prop->getValue($this);
        }
        return $res;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Pinned;

use danog\MadelineProto\EventHandler\Pinned;
use danog\MadelineProto\MTProto;

/**
 * Represents messages that were pinned/unpinned in a [channel](https://core.telegram.org/api/channel).
 */
final class PinnedChannelMessages extends Pinned
{
    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(MTProto $API, array $rawPinned)
    {
        parent::__construct($API, $rawPinned);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Pinned;

use danog\MadelineProto\EventHandler\Pinned;
use danog\MadelineProto\MTProto;

/**
 * Represents messages that were pinned/unpinned in a [chat/supergroup](https://core.telegram.org/api/channel).
 */
final class PinnedGroupMessages extends Pinned
{
    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(MTProto $API, array $rawPinned)
    {
        parent::__construct($API, $rawPinned);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Pinned;

use danog\MadelineProto\EventHandler\Pinned;
use danog\MadelineProto\MTProto;

/**
 * Some messages were pinned in a private chat.
 */
final class PinnedPrivateMessages extends Pinned
{
    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(MTProto $API, array $rawPinned)
    {
        parent::__construct($API, $rawPinned);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler;

use AssertionError;
use danog\MadelineProto\EventHandler\Keyboard\InlineKeyboard;
use danog\MadelineProto\EventHandler\Keyboard\ReplyKeyboard;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\TL\Types\Button;

/**
 * Represents an inline or reply keyboard.
 */
abstract class Keyboard
{
    /** @var non-empty-list<non-empty-list<Button>> */
    public readonly array $buttons;

    /** @internal */
    protected function __construct(MTProto $API, Message $message, array $buttons)
    {
        foreach ($buttons as &$row) {
            $row = $row['buttons'];
            foreach ($row as &$button) {
                $button = new Button($API, $message, $button);
            } unset($button);
        } unset($row);
        $this->buttons = $buttons;
    }

    /** @internal */
    public static function fromRawReplyMarkup(MTProto $API, Message $message, array $rawReplyMarkup): ?self
    {
        return match ($rawReplyMarkup['_']) {
            'replyKeyboardMarkup' => new ReplyKeyboard($API, $message, $rawReplyMarkup['rows']),
            'replyInlineMarkup' => new InlineKeyboard($API, $message, $rawReplyMarkup['rows']),
            default => null
        };
    }

    /**
     * Press button at the specified keyboard coordinates.
     *
     * @param bool $waitForResult If true, waits for a result from the bot before returning.
     */
    public function pressByCoordinates(int $row, int $column, bool $waitForResult): mixed
    {
        return $this->buttons[$row][$column]->click(!$waitForResult);
    }

    /**
     * Presses the first keyboard button with the specified label.
     *
     * @param bool $waitForResult If true, waits for a result from the bot before returning.
     *
     * @throws AssertionError If a button with the specified label cannot be found.
     */
    public function press(string $label, bool $waitForResult): mixed
    {
        foreach ($this->buttons as $rows) {
            foreach ($rows as $button) {
                if ($button->label === $label) {
                    return $button->click(!$waitForResult);
                }
            }
        }
        throw new AssertionError("Could not find a button with the specified label!");
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Participant;

use danog\MadelineProto\EventHandler\Participant;
use danog\MadelineProto\EventHandler\Participant\Rights\Admin as AdminRights;

/**
 * Admin.
 */
final class Admin extends Participant
{
    /** Can this admin promote other admins with the same permissions? */
    public readonly bool $canEdit;

    /** Is this the current user */
    public readonly bool $self;

    /** Admin user ID */
    public readonly int $userId;

    /** User that invited the admin to the channel/group */
    public readonly ?int $inviterId;

    /** User that promoted the user to admin */
    public readonly int $promotedBy;

    /** When did the user join */
    public readonly int $date;

    /** Admin [rights](https://core.telegram.org/api/rights) */
    public readonly AdminRights $adminRights;

    /** The role (rank) of the admin in the group: just an arbitrary string, `admin` by default */
    public readonly string $rank;

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(
        array $rawParticipant
    ) {
        $this->canEdit = $rawParticipant['can_edit'];
        $this->self = $rawParticipant['self'];
        $this->userId = $rawParticipant['user_id'];
        $this->inviterId = $rawParticipant['inviter_id'] ?? null;
        $this->promotedBy = $rawParticipant['promoted_by'];
        $this->date = $rawParticipant['date'];
        $this->rank = $rawParticipant['rank'] ?? 'admin';
        $this->adminRights = new AdminRights($rawParticipant['admin_rights']);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Participant;

use danog\MadelineProto\EventHandler\Participant;
use danog\MadelineProto\EventHandler\Participant\Rights\Banned as BannedRights;

/**
 * Banned/kicked user.
 */
final class Banned extends Participant
{
    /** Whether the user has left the group */
    public readonly bool $left;

    /** The banned peer */
    public readonly int $peer;

    /** User was kicked by the specified admin */
    public readonly int $kickedBy;

    /** When did the user join the group */
    public readonly int $date;

    /** Banned [rights](https://core.telegram.org/api/rights) */
    public readonly BannedRights $bannedRights;

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(array $rawParticipant)
    {
        $this->left = $rawParticipant['left'];
        $this->kickedBy = $rawParticipant['kicked_by'];
        $this->date = $rawParticipant['date'];
        $this->bannedRights = new BannedRights($rawParticipant['banned_rights']);
        $this->peer = $rawParticipant['peer'];
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Participant;

use danog\MadelineProto\EventHandler\Participant;
use danog\MadelineProto\EventHandler\Participant\Rights\Admin as AdminRights;

/**
 * Channel/supergroup creator.
 */
final class Creator extends Participant
{
    /** User ID */
    public readonly int $userId;

    /** Creator admin rights */
    public readonly AdminRights $adminRights;

    /** The role (rank) of the group creator in the group: just an arbitrary string, `admin` by default */
    public readonly string $rank;

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(
        array $rawParticipant
    ) {
        $this->userId = $rawParticipant['user_id'];
        $this->adminRights = new AdminRights($rawParticipant['admin_rights']);
        $this->rank = $rawParticipant['rank'] ?? 'Owner';
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Participant;

use danog\MadelineProto\EventHandler\Participant;

/**
 * Myself.
 */
final class MySelf extends Participant
{
    /** Whether I joined upon specific approval of an admin */
    public readonly bool $viaRequest;

    /** User ID */
    public readonly int $userId;

    /** User that invited me to the channel/supergroup */
    public readonly ?int $inviterId;

    /** When did I join the channel/supergroup */
    public readonly int $date;

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(
        array $rawParticipant
    ) {
        $this->viaRequest = $rawParticipant['via_request'];
        $this->userId = $rawParticipant['user_id'];
        $this->inviterId = $rawParticipant['inviter_id'] ?? null;
        $this->date = $rawParticipant['date'];
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Participant;

use JsonSerializable;
use ReflectionClass;
use ReflectionProperty;

abstract class Rights implements JsonSerializable
{
    /** @internal */
    #[\Override]
    public function jsonSerialize(): mixed
    {
        $res = ['_' => static::class];
        $refl = new ReflectionClass($this);
        foreach ($refl->getProperties(ReflectionProperty::IS_PUBLIC) as $prop) {
            $res[$prop->getName()] = $prop->getValue($this);
        }
        return $res;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Participant\Rights;

use danog\MadelineProto\EventHandler\Participant\Rights;

/**
 * Represents the rights of an admin in a [channel/supergroup](https://core.telegram.org/api/channel).
 */
final class Admin extends Rights
{
    /** If set, allows the admin to modify the description of the [channel/supergroup](https://core.telegram.org/api/channel) */
    public readonly bool $changeInfo;

    /** If set, allows the admin to post messages in the [channel](https://core.telegram.org/api/channel) */
    public readonly bool $postMessages;

    /** If set, allows the admin to also edit messages from other admins in the [channel](https://core.telegram.org/api/channel) */
    public readonly bool $editMessages;

    /** If set, allows the admin to also delete messages from other admins in the [channel](https://core.telegram.org/api/channel) */
    public readonly bool $deleteMessages;

    /** If set, allows the admin to ban users from the [channel/supergroup](https://core.telegram.org/api/channel) */
    public readonly bool $banUsers;

    /** If set, allows the admin to invite users in the [channel/supergroup](https://core.telegram.org/api/channel) */
    public readonly bool $inviteUsers;

    /** If set, allows the admin to pin messages in the [channel/supergroup](https://core.telegram.org/api/channel) */
    public readonly bool $pinMessages;

    /** If set, allows the admin to add other admins with the same (or more limited) permissions in the [channel/supergroup](https://core.telegram.org/api/channel) */
    public readonly bool $addAdmins;

    /** Whether this admin is anonymous */
    public readonly bool $anonymous;

    /** If set, allows the admin to change group call/livestream settings */
    public readonly bool $manageCall;

    /** Set this flag if none of the other flags are set,
     * but you still want the user to be an admin: if this or any of the other flags are set,
     * the admin can get the chat [admin log](https://core.telegram.org/api/recent-actions), get [chat statistics](https://core.telegram.org/api/stats), get [message statistics in channels](https://core.telegram.org/api/stats), get channel members,
     * see anonymous administrators in supergroups and ignore slow mode.
     */
    public readonly bool $other;

    /** If set, allows the admin to create, delete or modify [forum topics Â»](https://core.telegram.org/api/forum#forum-topics). */
    public readonly bool $manageTopics;

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(
        array $rawRights
    ) {
        $this->changeInfo = $rawRights['change_info'];
        $this->postMessages = $rawRights['post_messages'];
        $this->editMessages = $rawRights['edit_messages'];
        $this->deleteMessages = $rawRights['delete_messages'];
        $this->banUsers = $rawRights['ban_users'];
        $this->inviteUsers = $rawRights['invite_users'];
        $this->pinMessages = $rawRights['pin_messages'];
        $this->addAdmins = $rawRights['add_admins'];
        $this->anonymous = $rawRights['anonymous'];
        $this->manageCall = $rawRights['manage_call'];
        $this->other = $rawRights['other'];
        $this->manageTopics = $rawRights['manage_topics'];
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Participant\Rights;

use danog\MadelineProto\EventHandler\Participant\Rights;

/**
 * Represents the rights of a normal user in a [supergroup/channel/chat](https://core.telegram.org/api/channel). In this case, the flags are inverted: if set, a flag does not allow a user to do X.
 */
final class Banned extends Rights
{
    /** If set, does not allow a user to view messages in a [supergroup/chat](https://core.telegram.org/api/channel) */
    public readonly bool $viewMessages;

    /** If set, does not allow a user to send messages in a [supergroup/chat](https://core.telegram.org/api/channel) */
    public readonly bool $sendMessages;

    /** If set, does not allow a user to send any media in a [supergroup/chat](https://core.telegram.org/api/channel) */
    public readonly bool $sendMedia;

    /** If set, does not allow a user to send stickers in a [supergroup/chat](https://core.telegram.org/api/channel) */
    public readonly bool $sendStickers;

    /** If set, does not allow a user to send stickers in a [supergroup/chat](https://core.telegram.org/api/channel) */
    public readonly bool $sendGifs;

    /** If set, does not allow a user to send games in a [supergroup/chat](https://core.telegram.org/api/channel) */
    public readonly bool $sendGames;

    /** If set, does not allow a user to use inline bots in a [supergroup/chat](https://core.telegram.org/api/channel) */
    public readonly bool $sendInline;

    /** If set, does not allow a user to embed links in the messages of a [supergroup/chat](https://core.telegram.org/api/channel) */
    public readonly bool $embedLinks;

    /** If set, does not allow a user to send polls in a [supergroup/chat](https://core.telegram.org/api/channel) */
    public readonly bool $sendPolls;

    /** If set, does not allow any user to change the description of a [supergroup/chat](https://core.telegram.org/api/channel) */
    public readonly bool $changeInfo;

    /** If set, does not allow any user to invite users in a [supergroup/chat](https://core.telegram.org/api/channel) */
    public readonly bool $inviteUsers;

    /** If set, does not allow any user to pin messages in a [supergroup/chat](https://core.telegram.org/api/channel) */
    public readonly bool $pinMessages;

    /** If set, does not allow any user to create, delete or modify [forum topics Â»](https://core.telegram.org/api/forum#forum-topics). */
    public readonly bool $manageTopics;

    /** If set, does not allow a user to send photos in a [supergroup/chat](https://core.telegram.org/api/channel) */
    public readonly bool $sendPhotos;

    /** If set, does not allow a user to send videos in a [supergroup/chat](https://core.telegram.org/api/channel) */
    public readonly bool $sendVideos;

    /** If set, does not allow a user to send round videos in a [supergroup/chat](https://core.telegram.org/api/channel) */
    public readonly bool $sendRoundvideos;

    /** If set, does not allow a user to send audio files in a [supergroup/chat](https://core.telegram.org/api/channel) */
    public readonly bool $sendAudios;

    /** If set, does not allow a user to send documents in a [supergroup/chat](https://core.telegram.org/api/channel) */
    public readonly bool $sendDocs;

    /** If set, does not allow a user to send text messages in a [supergroup/chat](https://core.telegram.org/api/channel) */
    public readonly bool $sendPlain;

    /** Validity of said permissions (it is considered forever any value less then 30 seconds or more then 366 days). */
    public readonly int $untilDate;

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(
        array $rawRights
    ) {
        $this->viewMessages = $rawRights['view_messages'];
        $this->sendMessages = $rawRights['send_messages'];
        $this->sendMedia = $rawRights['send_media'];
        $this->sendStickers = $rawRights['send_stickers'];
        $this->sendGifs = $rawRights['send_gifs'];
        $this->sendGames = $rawRights['send_games'];
        $this->sendInline = $rawRights['send_inline'];
        $this->embedLinks = $rawRights['embed_links'];
        $this->sendPolls = $rawRights['send_polls'];
        $this->changeInfo = $rawRights['change_info'];
        $this->inviteUsers = $rawRights['invite_users'];
        $this->pinMessages = $rawRights['pin_messages'];
        $this->manageTopics = $rawRights['manage_topics'];
        $this->sendPhotos = $rawRights['send_photos'];
        $this->sendVideos = $rawRights['send_videos'];
        $this->sendRoundvideos = $rawRights['send_roundvideos'];
        $this->sendAudios = $rawRights['send_audios'];
        $this->sendDocs = $rawRights['send_docs'];
        $this->sendPlain = $rawRights['send_plain'];
        $this->untilDate = $rawRights['until_date'];
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Participant;

use danog\MadelineProto\EventHandler\Participant;

/**
 * A participant that left the channel/supergroup.
 */
final class Left extends Participant
{
    /** The peer that left */
    public readonly int $peer;

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(array $rawParticipant)
    {
        $this->peer = $rawParticipant['peer'];
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Participant;

use danog\MadelineProto\EventHandler\Participant;

/**
 * Channel/supergroup participant.
 */
final class Member extends Participant
{
    /** Participant user ID */
    public readonly int $userId;

    /** Date joined */
    public readonly int $date;

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(
        array $rawParticipant
    ) {
        $this->userId = $rawParticipant['user_id'];
        $this->date = $rawParticipant['date'];
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Action;

use danog\MadelineProto\EventHandler\Action;

/**
 * User is recording a round video to share.
 */
final class RecordRound extends Action
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Action;

use danog\MadelineProto\EventHandler\Action;

/**
 * User is choosing a sticker.
 */
final class ChooseSticker extends Action
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Action;

use danog\MadelineProto\EventHandler\Action;

/**
 * Invalidate all previous action updates. E.g. when user deletes entered text or aborts a video upload.
 */
final class Cancel extends Action
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Action;

use danog\MadelineProto\EventHandler\Action;

/**
 * User is recording a voice message.
 */
final class RecordAudio extends Action
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Action;

use danog\MadelineProto\EventHandler\Action;

/**
 * User is selecting a location to share.
 */
final class GeoLocation extends Action
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Action;

use danog\MadelineProto\EventHandler\Action;

/**
 * User is uploading a photo.
 */
final class UploadPhoto extends Action
{
    /**
     * @psalm-mutation-free
     */
    public function __construct(
        /** @var ?int Progress percentage */
        public readonly ?int $progress
    ) {
    }

    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toRawAction(): array
    {
        return parent::toRawAction() + [ 'progress' => $this->progress ];
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Action;

use danog\MadelineProto\EventHandler\Action;

/**
 * User has clicked on an animated emoji triggering a [reaction, click here for more info Â»](https://core.telegram.org/api/animated-emojis#emoji-reactions).
 */
final class EmojiTap extends Action
{
    /**
     * @psalm-mutation-free
     */
    public function __construct(
        /** @var string Emoji */
        public readonly string $emoticon,

        /** @var int Message ID of the animated emoji that was clicked */
        public readonly ?int $id,

        /**
         * t: number of seconds that passed since the previous tap in the array, the first tap uses a value of `0.0`.
         * i: 1-based index of the randomly chosen animation for the tap (equivalent to the index of a specific emoji-related animation in [stickerPack](https://core.telegram.org/constructor/stickerPack) + 1).
         * @var list<array{t:float,i:int}>
         */
        public readonly array $animation,
    ) {
    }

    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toRawAction(): array
    {
        return parent::toRawAction() + [
            'emoticon' => $this->emoticon,
            'interaction' => [
                'v' => 1,
                'a' => $this->animation,
            ],
        ];
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Action;

use danog\MadelineProto\EventHandler\Action;

/**
 * User is watching an animated emoji reaction triggered by another user, [click here for more info Â»](https://core.telegram.org/api/animated-emojis#emoji-reactions).
 */
final class EmojiSeen extends Action
{
    /**
     * @psalm-mutation-free
     */
    public function __construct(
        /** @var string Emoji */
        public readonly string $emoticon
    ) {
    }

    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toRawAction(): array
    {
        return parent::toRawAction() + [ 'emoticon' => $this->emoticon ];
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Action;

use danog\MadelineProto\EventHandler\Action;

/**
 * User is recording a video.
 */
final class RecordVideo extends Action
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Action;

use danog\MadelineProto\EventHandler\Action;

/**
 * User is uploading a round video.
 */
final class UploadRound extends Action
{
    /**
     * @psalm-mutation-free
     */
    public function __construct(
        /** @var ?int Progress percentage */
        public readonly ?int $progress
    ) {
    }

    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toRawAction(): array
    {
        return parent::toRawAction() + [ 'progress' => $this->progress ];
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Action;

use danog\MadelineProto\EventHandler\Action;

/**
 * Chat history is being imported.
 */
final class HistoryImport extends Action
{
    /**
     * @psalm-mutation-free
     */
    public function __construct(
        /** @var ?int Progress percentage */
        public readonly ?int $progress
    ) {
    }

    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toRawAction(): array
    {
        return parent::toRawAction() + [ 'progress' => $this->progress ];
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Action;

use danog\MadelineProto\EventHandler\Action;

/**
 * User is currently speaking in the group call.
 */
final class GroupCallSpeaking extends Action
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Action;

use danog\MadelineProto\EventHandler\Action;

/**
 * User is uploading a voice message.
 */
final class UploadAudio extends Action
{
    /**
     * @psalm-mutation-free
     */
    public function __construct(
        /** @var ?int Progress percentage */
        public readonly ?int $progress
    ) {
    }

    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toRawAction(): array
    {
        return parent::toRawAction() + [ 'progress' => $this->progress ];
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Action;

use danog\MadelineProto\EventHandler\Action;

/**
 * User is uploading a file.
 */
final class UploadDocument extends Action
{
    /**
     * @psalm-mutation-free
     */
    public function __construct(
        /** @var ?int Progress percentage */
        public readonly ?int $progress
    ) {
    }

    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toRawAction(): array
    {
        return parent::toRawAction() + [ 'progress' => $this->progress ];
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Action;

use danog\MadelineProto\EventHandler\Action;

/**
 * User is typing.
 */
final class Typing extends Action
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Action;

use danog\MadelineProto\EventHandler\Action;

/**
 * User is playing a game.
 */
final class GamePlay extends Action
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Action;

use danog\MadelineProto\EventHandler\Action;

/**
 * User is uploading a video.
 */
final class UploadVideo extends Action
{
    /**
     * @psalm-mutation-free
     */
    public function __construct(
        /** @var ?int Progress percentage */
        public readonly ?int $progress
    ) {
    }

    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function toRawAction(): array
    {
        return parent::toRawAction() + [ 'progress' => $this->progress ];
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Action;

use danog\MadelineProto\EventHandler\Action;

/**
 * User is selecting a contact to share.
 */
final class ChooseContact extends Action
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler;

use danog\MadelineProto\Ipc\IpcCapable;
use JsonSerializable;
use ReflectionClass;
use ReflectionProperty;

/**
 * Represents a generic update.
 *
 * NOTE: use serialize(), not json_encode() to serialize updates.
 *
 * json_encode() may only be used for logging updates.
 */
abstract class Update extends IpcCapable implements JsonSerializable
{
    /** @internal */
    #[\Override]
    public function jsonSerialize(): mixed
    {
        $res = ['_' => static::class];
        $refl = new ReflectionClass($this);
        foreach ($refl->getProperties(ReflectionProperty::IS_PUBLIC) as $prop) {
            $res[$prop->getName()] = $prop->getValue($this);
        }
        return $res;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler;

use JsonSerializable;

enum CommandType: string implements JsonSerializable
{
    case SLASH = '/';
    case DOT = '.';
    case BANG = '!';

    /**
     * Parse the command at the start of a text, if any.
     *
     * @internal
     *
     * @return array{?self, ?string, ?list<string>} The command type, the command (without the bot username) and its arguments.
     *
     * @psalm-pure
     */
    public static function parse(string $text): array
    {
        $type = self::tryFrom($text[0] ?? '');
        if ($type === null) {
            return [null, null, null];
        }
        $space = strpos($text, ' ', 1) ?: \strlen($text);
        $args = explode(' ', substr($text, $space+1));
        $length = ($len = strpos($cmd = substr($text, 1, $space-1), "@")) ? $len : $space-1;
        return [$type, substr($cmd, 0, $length), $args === [''] ? [] : $args];
    }

    /** @internal */
    #[\Override]
    public function jsonSerialize(): mixed
    {
        return $this->value;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler;

use AssertionError;
use danog\MadelineProto\EventHandler\Participant\Admin;
use danog\MadelineProto\EventHandler\Participant\Banned;
use danog\MadelineProto\EventHandler\Participant\Creator;
use danog\MadelineProto\EventHandler\Participant\Left;
use danog\MadelineProto\EventHandler\Participant\Member;
use danog\MadelineProto\EventHandler\Participant\MySelf;
use JsonSerializable;
use ReflectionClass;
use ReflectionProperty;

/**
 * Info about a channel participant.
 */
abstract class Participant implements JsonSerializable
{
    /**
     * @psalm-pure
     */
    public static function fromRawParticipant(array $rawParticipant): self
    {
        return match ($rawParticipant['_']) {
            'channelParticipant'        => new Member($rawParticipant),
            'channelParticipantLeft'    => new Left($rawParticipant),
            'channelParticipantSelf'    => new MySelf($rawParticipant),
            'channelParticipantAdmin'   => new Admin($rawParticipant),
            'channelParticipantBanned'  => new Banned($rawParticipant),
            'channelParticipantCreator' => new Creator($rawParticipant),
            default => throw new AssertionError("undefined Participant type: {$rawParticipant['_']}")
        };
    }

    /** @internal */
    #[\Override]
    public function jsonSerialize(): mixed
    {
        $res = ['_' => static::class];
        $refl = new ReflectionClass($this);
        foreach ($refl->getProperties(ReflectionProperty::IS_PUBLIC) as $prop) {
            $res[$prop->getName()] = $prop->getValue($this);
        }
        return $res;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler;

use danog\MadelineProto\EventHandler\Location\LiveLocation;
use danog\MadelineProto\EventHandler\Location\StaticLocation;
use danog\MadelineProto\EventHandler\Location\Venue;
use danog\MadelineProto\EventHandler\Media\GeoPoint;
use JsonSerializable;
use ReflectionClass;
use ReflectionProperty;

/** Location attached to a message: a geo point, a live location or a venue */
abstract class AbstractLocation implements JsonSerializable
{
    /** The geo point */
    public readonly GeoPoint $geo;

    /** @internal */
    public function __construct(array $rawGeoPoint)
    {
        $this->geo = new GeoPoint($rawGeoPoint);
    }

    /**
     * Wrap the location contained in a message media constructor.
     *
     * Returns null for media that isn't a location, and for locations without a geo point (geoPointEmpty).
     *
     * @internal
     */
    public static function fromRawMedia(array $media): ?self
    {
        if (isset($media['geo']) && $media['geo']['_'] !== 'geoPoint') {
            return null;
        }
        return match ($media['_']) {
            'messageMediaGeo' => new StaticLocation($media['geo']),
            'messageMediaGeoLive' => new LiveLocation($media),
            'messageMediaVenue' => new Venue($media['geo'], $media),
            // Secret chats send the coordinates inline
            'decryptedMessageMediaGeoPoint' => new StaticLocation($media),
            'decryptedMessageMediaVenue' => new Venue($media, $media),
            default => null,
        };
    }

    /** @internal */
    #[\Override]
    public function jsonSerialize(): mixed
    {
        $res = ['_' => static::class];
        $refl = new ReflectionClass($this);
        foreach ($refl->getProperties(ReflectionProperty::IS_PUBLIC) as $prop) {
            $res[$prop->getName()] = $prop->getValue($this);
        }
        return $res;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler;

use danog\MadelineProto\EventHandler\Privacy\Rule;
use danog\MadelineProto\EventHandler\Privacy\RuleDestination;
use danog\MadelineProto\MTProto;

/**
 * Indicates some privacy rules for a user or set of users.
 */
final class Privacy extends Update
{
    /** New privacy rule. */
    public readonly Rule $rule;

    /** @var list<RuleDestination> Peers to which the privacy rules apply  */
    public readonly array $appliesTo;

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(MTProto $API, array $rawPrivacy)
    {
        parent::__construct($API);
        $this->rule = Rule::from($rawPrivacy['key']['_']);
        $this->appliesTo = array_map(RuleDestination::fromRawRule(...), $rawPrivacy['rules']);
    }
}
<?php declare(strict_types=1);
/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Plugin;

use danog\MadelineProto\EventHandler\Filter\FilterCommand;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\SimpleFilter\FromAdmin;
use danog\MadelineProto\EventHandler\SimpleFilter\Incoming;
use danog\MadelineProto\PluginEventHandler;

/**
 * Plugin that offers a /restart command to admins that can be used to restart the bot, applying changes.
 */
final class RestartPlugin extends PluginEventHandler
{
    #[FilterCommand('restart')]
    public function cmd(Incoming&Message&FromAdmin $_): void
    {
        $this->restart();
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Story;

use danog\MadelineProto\EventHandler\Update;
use danog\MadelineProto\MTProto;

/**
 * Represents a reaction to a story.
 */
final class StoryReaction extends Update
{
    /** ID of the sender of the story */
    public readonly int $senderId;

    /** Story ID */
    public readonly int $id;

    public readonly int|string|null $reaction;

    /** @internal */
    public function __construct(MTProto $API, array $rawStory)
    {
        parent::__construct($API);
        $this->senderId = $API->getIdInternal($rawStory);
        $this->id = $rawStory['story_id'];
        $this->reaction = $rawStory['reaction']['emoticon'] ?? $rawStory['reaction']['document_id'] ?? null;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Story;

use danog\MadelineProto\EventHandler\AbstractStory;

/**
 * Represents a deleted story.
 */
final class StoryDeleted extends AbstractStory
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Story;

use AssertionError;
use danog\MadelineProto\EventHandler\AbstractStory;
use danog\MadelineProto\EventHandler\Media\Gif;
use danog\MadelineProto\EventHandler\Media\Photo;
use danog\MadelineProto\EventHandler\Media\Video;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Message\Entities\MessageEntity;
use danog\MadelineProto\EventHandler\Message\ReportReason;
use danog\MadelineProto\EventHandler\Privacy\RuleDestination;
use danog\MadelineProto\Ipc\Client;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\ParseMode;
use danog\MadelineProto\StrTools;

/**
 * Represents a Telegram story.
 */
final class Story extends AbstractStory
{
    /** Whether this story is pinned */
    public readonly bool $pinned;

    /** Whether this story is visible to everyone */
    public readonly bool $public;

    /** Whether this story is visible to only close friends of the user (@see Privacy::AllowCloseFriends) */
    public readonly bool $closeFriends;

    /** Whether this story is only visible to your countacts */
    public readonly bool $contacts;

    /** Whether this story is only visible to a select list of contacts */
    public readonly bool $selectedContacts;

    /** Whether this media is protected */
    public readonly bool $protected;

    /** Whether this story was edited */
    public readonly bool $edited;

    /** When was the story sent */
    public readonly int $date;

    /** Expiration date of the story */
    public readonly int $expireDate;

    /** Story caption */
    public readonly string $caption;

    /** @var list<MessageEntity> Message [entities](https://core.telegram.org/api/entities) for story caption */
    public readonly array $entities;

    /** Attached media. */
    public readonly Gif|Photo|Video $media;

    /** A set of physical coordinates associated to this story */
    //public readonly ?array $mediaAreas; //!

    /** @var list<RuleDestination> */
    public readonly array $privacy;

    /** Our reaction to the story */
    public readonly int|string|null $sentReaction;

    /** Reaction counter */
    public readonly ?int $reactionCount;

    /** View counter */
    public readonly ?int $views;

    /** @var list<int> List of users who recently viewed the story */
    public readonly array $recentViewers;

    /**
     * @readonly
     *
     * @var list<string> Regex matches, if a filter regex is present
     */
    public ?array $matches = null;

    /**
     * @readonly
     *
     * @var array<array-key, array<array-key, list{string, int}|null|string>|mixed> Regex matches, if a filter multiple match regex is present
     */
    public ?array $matchesAll = null;

    /** @internal */
    public function __construct(MTProto|Client $API, array $rawStory)
    {
        parent::__construct($API, $rawStory);
        if ($rawStory['story']['min']) {
            // TODO: cache
            $rawStory = $API->methodCallAsyncRead('stories.getStoriesByID', ['peer' => $rawStory['peer'], 'id' => [$rawStory['story']['id']]])['stories'][0];
        } else {
            $rawStory = $rawStory['story'];
        }

        $this->pinned = $rawStory['pinned'];
        $this->public = $rawStory['public'];
        $this->closeFriends = $rawStory['close_friends'];
        $this->protected = $rawStory['noforwards'];
        $this->edited = $rawStory['edited'];
        $this->contacts = $rawStory['contacts'];
        $this->selectedContacts = $rawStory['selected_contacts'];

        $this->date = $rawStory['date'];
        $this->expireDate = $rawStory['expire_date'];

        $this->media = $API->wrapMedia($rawStory['media'], $this->protected);
        $this->entities = MessageEntity::fromRawEntities($rawStory['entities'] ?? []);
        $this->privacy = array_map(RuleDestination::fromRawRule(...), $rawStory['privacy'] ?? []);

        $this->recentViewers = $rawStory['views']['recent_viewers'] ?? [];
        $this->views = $rawStory['views']['views_count'] ?? null;
        $this->reactionCount = $rawStory['views']['reactions_count'] ?? null;

        $this->caption = $rawStory['caption'] ?? '';
        //$this->mediaAreas = $rawStory['mediaAreas'] ?? null; //!
        $this->sentReaction = $rawStory['sent_reaction']['emoticon'] ?? $rawStory['sent_reaction']['document_id'] ?? null;
    }

    /**
     * Reply to the story.
     *
     * @param string       $message                Message to send
     * @param ParseMode    $parseMode              Parse mode
     * @param array|null   $replyMarkup            Keyboard information.
     * @param integer|null $scheduleDate           Schedule date.
     * @param boolean      $silent                 Whether to send the message silently, without triggering notifications.
     * @param boolean      $background             Send this message as background message
     * @param boolean      $clearDraft             Clears the draft field
     * @param boolean      $noWebpage              Set this flag to disable generation of the webpage preview
     * @param boolean      $updateStickersetsOrder Whether to move used stickersets to top
     *
     */
    public function reply(
        string $message,
        ParseMode $parseMode = ParseMode::TEXT,
        ?array $replyMarkup = null,
        ?int $scheduleDate = null,
        bool $silent = false,
        bool $background = false,
        bool $clearDraft = false,
        bool $noWebpage = false,
        bool $updateStickersetsOrder = false,
    ): Message {
        $client = $this->getClient();
        $result = $client->methodCallAsyncRead(
            'messages.sendMessage',
            [
                'peer' => $this->senderId,
                'message' => $message,
                'parse_mode' => $parseMode,
                'reply_to' => ['_' => 'inputReplyToStory', 'user_id' => $this->senderId, 'story_id' => $this->id],
                'reply_markup' => $replyMarkup,
                'schedule_date' => $scheduleDate,
                'silent' => $silent,
                'background' => $background,
                'clear_draft' => $clearDraft,
                'no_webpage' => $noWebpage,
                'update_stickersets_order' => $updateStickersetsOrder,
            ]
        );
        if (isset($result['_'])) {
            return $client->wrapMessage($client->extractMessage($result));
        }

        $last = null;
        foreach ($result as $updates) {
            /** @var Message */
            $new = $client->wrapMessage($client->extractMessage($updates));
            if ($last) {
                $last->nextSent = $new;
            } else {
                $first = $new;
            }
            $last = $new;
        }
        return $first;
    }

    /**
     * Delete the story.
     *
     */
    public function delete(): void
    {
        $this->getClient()->methodCallAsyncRead(
            'stories.deleteStories',
            [
                'peer' => $this->senderId,
                'id' => [$this->id],
            ]
        );
    }

    /**
     * Export story link e.g: https://t.me/username/s/storyid.
     *
     */
    public function exportLink(): string
    {
        return $this->getClient()->methodCallAsyncRead(
            'stories.exportStoryLink',
            [
                'peer' => $this->senderId,
                'id' => $this->id,
            ]
        )['link'];
    }

    /**
     * Report a story for violation of telegramâ€™s Terms of Service.
     *
     * @param  ReportReason $reason  Why is story being reported
     * @param  string       $message Comment for report moderation
     * @return boolean
     */
    public function report(ReportReason $reason, string $message = ''): bool
    {
        return $this->getClient()->methodCallAsyncRead(
            'stories.report',
            [
                'peer' => $this->senderId,
                'id' => [$this->id],
                'reason' => ['_' => $reason->value],
                'message' => $message,
            ]
        );
    }

    /**
     * Pin a story.
     *
     */
    public function pin(): void
    {
        $this->getClient()->methodCallAsyncRead(
            'stories.togglePinned',
            [
                'peer' => $this->senderId,
                'id' => [$this->id],
                'pinned' => true,
            ]
        );
    }

    /**
     * Unpin a story.
     *
     */
    public function unpin(): void
    {
        $this->getClient()->methodCallAsyncRead(
            'stories.togglePinned',
            [
                'id' => [$this->id],
                'pinned' => false,
            ]
        );
    }

    /**
     * Mark story as read.
     *
     * @return boolean
     */
    public function view(): bool
    {
        return $this->getClient()->methodCallAsyncRead(
            'stories.incrementStoryViews',
            [
                'peer' => $this->senderId,
                'id' => [$this->id],
            ]
        );
    }

    /**
     * Reaction to story.
     *
     * @param integer|string $reaction string or int Reaction
     * @param boolean        $recent
     */
    public function addReaction(int|string $reaction, bool $recent = true): StoryReaction
    {
        $client = $this->getClient();
        $result = $client->methodCallAsyncRead(
            'stories.sendReaction',
            [
                'add_to_recent' => $recent,
                'peer' => $this->senderId,
                'story_id' => $this->id,
                'reaction' => \is_int($reaction)
                ? ['_' => 'reactionCustomEmoji', 'document_id' => $reaction]
                : ['_' => 'reactionEmoji', 'emoticon' => $reaction],
            ]
        )['updates'];
        foreach ($result as $update) {
            if ($update['_'] === 'updateSentStoryReaction') {
                return $client->wrapUpdate($update);
            }
        }
        throw new AssertionError("Could not find updateSentStoryReaction!");
    }

    /**
     * Delete reaction from story.
     *
     * @param boolean $recent string or int Reaction
     */
    public function delReaction(bool $recent = true): StoryReaction
    {
        $client = $this->getClient();
        $result = $client->methodCallAsyncRead(
            'stories.sendReaction',
            [
                'add_to_recent' => $recent,
                'peer' => $this->senderId,
                'story_id' => $this->id,
            ]
        )['updates'];
        foreach ($result as $update) {
            if ($update['_'] === 'updateSentStoryReaction') {
                return $client->wrapUpdate($update);
            }
        }
        throw new AssertionError("Could not find updateSentStoryReaction!");
    }

    protected readonly string $html;
    protected readonly string $htmlTelegram;

    /**
     * Get an HTML version of the story caption.
     *
     * @psalm-suppress InaccessibleProperty
     *
     * @param bool $allowTelegramTags Whether to allow telegram-specific tags like tg-spoiler, tg-emoji, mention links and so on...
     */
    public function getHTML(bool $allowTelegramTags = false): string
    {
        if (!$this->entities) {
            return StrTools::htmlEscape($this->caption);
        }
        if ($allowTelegramTags) {
            return $this->htmlTelegram ??= StrTools::entitiesToHtml($this->caption, $this->entities, $allowTelegramTags);
        }
        return $this->html ??= StrTools::entitiesToHtml($this->caption, $this->entities, $allowTelegramTags);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter;

use Attribute;
use danog\MadelineProto\EventHandler\Calls\PrivateCall;
use danog\MadelineProto\EventHandler\Update;
use danog\MadelineProto\VoIP\CallState;

/**
 * Allow only ended calls.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterEnded extends Filter
{
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return $update instanceof PrivateCall && $update->getCallState() === CallState::ENDED;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter;

use Attribute;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Update;

/**
 * Allow only forwarded messages.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterForwarded extends Filter
{
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return $update instanceof Message && $update->fwdInfo !== null;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter;

use Attribute;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Update;

/**
 * Allow any media messages.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterMedia extends Filter
{
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return $update instanceof Message && $update->media !== null;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Mahdi <mahdi.talaee1379@gmail.com>
 * @copyright 2016-2025 Mahdi <mahdi.talaee1379@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter;

use Attribute;
use danog\MadelineProto\EventHandler\Message\CommentReply;
use danog\MadelineProto\EventHandler\Update;

/**
 * Allow messages that coming from @replies.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterCommentReply extends Filter
{
    /**
     * @psalm-pure
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return $update instanceof CommentReply;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter;

use Attribute;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Update;

/**
 * Allow only messages that contain a poll.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterPoll extends Filter
{
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return ($update instanceof Message && $update->poll !== null);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter;

use Attribute;
use danog\MadelineProto\EventHandler\Message\ServiceMessage;
use danog\MadelineProto\EventHandler\Update;

/**
 * Allow only service messages of any type.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterService extends Filter
{
    /**
     * @psalm-pure
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return $update instanceof ServiceMessage;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter;

use Attribute;
use danog\MadelineProto\EventHandler\Calls\MultiCallMessage;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Story\Story;
use danog\MadelineProto\EventHandler\Update;
use Webmozart\Assert\Assert;

/**
 * Allow only messages that start with a specific content.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterTextStarts extends Filter
{
    /**
     * @psalm-mutation-free
     */
    public function __construct(
        private readonly string $content
    ) {
        Assert::notEmpty($content);
    }

    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return ($update instanceof Message && str_starts_with($update->message, $this->content)) ||
            ($update instanceof Story && str_starts_with($update->caption, $this->content)) ||
            ($update instanceof MultiCallMessage && str_starts_with($update->message->text, $this->content));
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter;

use Attribute;
use danog\MadelineProto\EventHandler\AbstractMessage;
use danog\MadelineProto\EventHandler\Calls\MultiCallMessage;
use danog\MadelineProto\EventHandler\Calls\PrivateCall;
use danog\MadelineProto\EventHandler\Update;
use danog\MadelineProto\VoIP\CallState;

/**
 * Allow only outgoing messages.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterOutgoing extends Filter
{
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return ($update instanceof AbstractMessage && $update->out)
            || ($update instanceof MultiCallMessage && $update->out)
            || ($update instanceof PrivateCall && $update->getCallState() === CallState::REQUESTED);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter;

use danog\MadelineProto\EventHandler;
use danog\MadelineProto\EventHandler\AbstractMessage;
use danog\MadelineProto\EventHandler\AbstractStory;
use danog\MadelineProto\EventHandler\BotCommands;
use danog\MadelineProto\EventHandler\Calls\MultiCallMessage;
use danog\MadelineProto\EventHandler\ChatInviteRequester\BotChatInviteRequest;
use danog\MadelineProto\EventHandler\InlineQuery;
use danog\MadelineProto\EventHandler\Query\ButtonQuery;
use danog\MadelineProto\EventHandler\Story\StoryReaction;
use danog\MadelineProto\EventHandler\Typing;
use danog\MadelineProto\EventHandler\Update;
use danog\MadelineProto\EventHandler\User\Blocked;
use danog\MadelineProto\EventHandler\User\BotStopped;
use danog\MadelineProto\EventHandler\User\Phone;
use danog\MadelineProto\EventHandler\User\Status;
use danog\MadelineProto\EventHandler\User\Username;

/**
 * Allow incoming or outgoing group messages made by a certain list of senders.
 *
 * @internal
 */
abstract class AbstractFilterFromSenders extends Filter
{
    /** @var array<string|int> */
    private readonly array $peers;
    /** @var list<int> */
    private readonly array $peersResolved;
    /**
     * @psalm-mutation-free
     */
    public function __construct(string|int ...$idOrUsername)
    {
        $this->peers = array_unique($idOrUsername);
    }
    #[\Override]
    public function initialize(EventHandler $API): Filter
    {
        if (\count($this->peers) === 1) {
            return (new FilterFromSender(array_values($this->peers)[0]))->initialize($API);
        }
        $res = [];
        foreach ($this->peers as $peer) {
            $res []= $API->getId($peer);
        }
        /** @psalm-suppress InaccessibleProperty */
        $this->peersResolved = $res;
        return $this;
    }
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return $update instanceof AbstractMessage && \in_array($update->senderId, $this->peersResolved, true) ||
            ($update instanceof AbstractStory && \in_array($update->senderId, $this->peersResolved, true)) ||
            ($update instanceof MultiCallMessage && \in_array($update->fromId, $this->peersResolved, true)) ||
            ($update instanceof StoryReaction && \in_array($update->senderId, $this->peersResolved, true)) ||
            ($update instanceof ButtonQuery && \in_array($update->userId, $this->peersResolved, true)) ||
            ($update instanceof InlineQuery && \in_array($update->userId, $this->peersResolved, true)) ||
            ($update instanceof Typing && \in_array($update->userId, $this->peersResolved, true)) ||
            ($update instanceof Blocked && \in_array($update->userId, $this->peersResolved, true)) ||
            ($update instanceof BotStopped && \in_array($update->userId, $this->peersResolved, true)) ||
            ($update instanceof Phone && \in_array($update->userId, $this->peersResolved, true)) ||
            ($update instanceof Status && \in_array($update->userId, $this->peersResolved, true)) ||
            ($update instanceof Username && \in_array($update->userId, $this->peersResolved, true)) ||
            ($update instanceof BotCommands && \in_array($update->botId, $this->peersResolved, true)) ||
            ($update instanceof BotChatInviteRequest && \in_array($update->userId, $this->peersResolved, true));
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter;

use Attribute;
use danog\MadelineProto\EventHandler;
use danog\MadelineProto\EventHandler\AbstractMessage;
use danog\MadelineProto\EventHandler\Calls\MultiCallMessage;
use danog\MadelineProto\EventHandler\Update;

/**
 * Allow only messages coming from bots.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterFromBot extends Filter
{
    private readonly EventHandler $API;
    /**
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function initialize(EventHandler $API): Filter
    {
        $this->API = $API;
        return $this;
    }
    #[\Override]
    public function apply(Update $update): bool
    {
        return ($update instanceof AbstractMessage && $this->API->isBot($update->senderId))
            || ($update instanceof MultiCallMessage && $this->API->isBot($update->fromId));
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter;

use Attribute;

/**
 * Allow incoming or outgoing group messages made by a certain sender.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterSender extends AbstractFilterFromSender
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter;

use Attribute;

/**
 * Allow incoming or outgoing group messages made by a certain sender.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterFromSender extends AbstractFilterFromSender
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter;

use Attribute;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Update;

/**
 * Allow only messages that contain a location (a geo point, a live location or a venue).
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterLocation extends Filter
{
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return ($update instanceof Message && $update->location !== null);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter;

use AssertionError;
use Attribute;
use danog\MadelineProto\EventHandler\Calls\MultiCallMessage;
use danog\MadelineProto\EventHandler\CommandType;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Update;
use Webmozart\Assert\Assert;

/**
 * Allow only messages containing the specified case-insensitive command.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterCommandCaseInsensitive extends Filter
{
    /**
     * @var list<CommandType>
     */
    public readonly array $commandTypes;

    /** Command */
    private readonly string $command;

    /**
     * @param string            $command Command
     * @param list<CommandType> $types   Command types, if empty all command types are allowed.
     *
     * @psalm-mutation-free
     */
    public function __construct(
        string $command,
        array $types = [CommandType::BANG, CommandType::DOT, CommandType::SLASH],
    ) {
        $this->command = mb_strtolower($command);
        Assert::true(preg_match("/^\w+$/", $command) === 1, "An invalid command was specified!");
        Assert::notEmpty($types, 'No command types were specified!');
        $c = [];
        foreach ($types as $type) {
            if (isset($c[$type->value])) {
                throw new AssertionError($type->value." was already specified!");
            }
            $c[$type->value] = true;
        }
        $this->commandTypes = $types;
    }
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return ($update instanceof Message || $update instanceof MultiCallMessage) && $update->command !== null && mb_strtolower($update->command) === $this->command && \in_array($update->commandType, $this->commandTypes, true);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Mahdi <mahdi.talaee1379@gmail.com>
 * @copyright 2016-2025 Mahdi <mahdi.talaee1379@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter;

use AssertionError;
use Attribute;
use danog\MadelineProto\API;
use danog\MadelineProto\EventHandler;
use danog\MadelineProto\EventHandler\CommandType;
use danog\MadelineProto\EventHandler\Filter\Combinator\FiltersOr;
use danog\MadelineProto\EventHandler\Update;
use Webmozart\Assert\Assert;

/**
 * Allow only messages containing the specified command, optionally postfixed with the bot's username.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterBotCommand extends Filter
{
    #[\Override]
    public function initialize(EventHandler $API): Filter
    {
        Assert::true($API->isSelfBot(), 'This filter can only be used by bots!');
        $filters = [new FilterCommand($this->command, [CommandType::SLASH])];
        foreach ($API->getInfo('me', API::INFO_TYPE_USERNAMES) as $username) {
            $filters[] = new FilterCommand("{$this->command}@$username", [CommandType::SLASH]);
        }
        return new FiltersOr(...$filters);
    }
    /**
     * @psalm-mutation-free
     */
    public function __construct(private readonly string $command)
    {
    }

    /**
     * @psalm-pure
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        throw new AssertionError("Unreachable!");
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter;

use Attribute;
use danog\MadelineProto\EventHandler\Calls\MultiCallMessage;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Story\Story;
use danog\MadelineProto\EventHandler\Update;
use Webmozart\Assert\Assert;

/**
 * Allow only messages with a specific case-insensitive content.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterTextCaseInsensitive extends Filter
{
    private readonly string $content;

    /**
     * @psalm-mutation-free
     */
    public function __construct(
        string $content,
    ) {
        Assert::notEmpty($content);
        $this->content = mb_strtolower($content);
    }

    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return ($update instanceof Message && mb_strtolower($update->message) === $this->content) ||
            ($update instanceof Story && mb_strtolower($update->caption) === $this->content) ||
            ($update instanceof MultiCallMessage && mb_strtolower($update->message->text) === $this->content);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter;

use Attribute;
use danog\MadelineProto\EventHandler\Calls\MultiCallMessage;
use danog\MadelineProto\EventHandler\InlineQuery;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Query\ButtonQuery;
use danog\MadelineProto\EventHandler\Story\Story;
use danog\MadelineProto\EventHandler\Update;
use Webmozart\Assert\Assert;

/**
 * Allow only messages or button queries matching the specified regex.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterRegexMatchAll extends Filter
{
    /** @param non-empty-string $regex */
    public function __construct(
        private readonly string $regex,
        private readonly int $flags = 0,
        private readonly int $offset = 0
    ) {
        preg_match_all($regex, '', $m, $flags, $offset);
        Assert::eq(preg_last_error_msg(), 'No error');
    }

    #[\Override]
    public function apply(Update $update): bool
    {
        if ($update instanceof Message && preg_match_all($this->regex, $update->message, $matches, $this->flags, $this->offset)) {
            /** @psalm-suppress InaccessibleProperty */
            $update->matchesAll = $matches;
            return true;
        }

        if ($update instanceof ButtonQuery && preg_match_all($this->regex, $update->data, $matches, $this->flags, $this->offset)) {
            /** @psalm-suppress InaccessibleProperty */
            $update->matchesAll = $matches;
            return true;
        }

        if ($update instanceof InlineQuery && preg_match_all($this->regex, $update->query, $matches, $this->flags, $this->offset)) {
            /** @psalm-suppress InaccessibleProperty */
            $update->matchesAll = $matches;
            return true;
        }

        if ($update instanceof Story && preg_match_all($this->regex, $update->caption, $matches, $this->flags, $this->offset)) {
            /** @psalm-suppress InaccessibleProperty */
            $update->matchesAll = $matches;
            return true;
        }
        if ($update instanceof MultiCallMessage && preg_match_all($this->regex, $update->message->text, $matches, $this->flags, $this->offset)) {
            /** @psalm-suppress InaccessibleProperty */
            $update->matchesAll = $matches;
            return true;
        }
        return false;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter;

use Attribute;
use danog\MadelineProto\EventHandler\Message\GroupMessage;
use danog\MadelineProto\EventHandler\Update;

/**
 * Allow only updates coming from groups.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterGroup extends Filter
{
    /**
     * @psalm-pure
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return $update instanceof GroupMessage;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter;

use Attribute;
use danog\MadelineProto\EventHandler\Calls\MultiCallMessage;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Story\Story;
use danog\MadelineProto\EventHandler\Update;
use Webmozart\Assert\Assert;

/**
 * Allow only messages that contain a specific content.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterTextContains extends Filter
{
    /**
     * @psalm-mutation-free
     */
    public function __construct(
        private readonly string $content
    ) {
        Assert::notEmpty($content);
    }

    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return ($update instanceof Message && str_contains($update->message, $this->content)) ||
            ($update instanceof Story && str_contains($update->caption, $this->content)) ||
            ($update instanceof MultiCallMessage && str_contains($update->message->text, $this->content));
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter;

use Attribute;
use danog\MadelineProto\EventHandler\Calls\MultiCallMessage;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Story\Story;
use danog\MadelineProto\EventHandler\Update;
use Webmozart\Assert\Assert;

/**
 * Allow only messages with a specific content.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterText extends Filter
{
    /**
     * @psalm-mutation-free
     */
    public function __construct(
        private readonly string $content
    ) {
        Assert::notEmpty($content);
    }

    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return ($update instanceof Message && $update->message === $this->content) ||
            ($update instanceof Story && $update->caption === $this->content) ||
            ($update instanceof MultiCallMessage && $update->message->text === $this->content);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter;

use Attribute;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Update;

/**
 * Allow any messages except media messages.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterNoMedia extends Filter
{
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return $update instanceof Message && $update->media === null;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter;

use Attribute;
use danog\MadelineProto\EventHandler\Calls\PrivateCall;
use danog\MadelineProto\EventHandler\Update;
use danog\MadelineProto\VoIP\CallState;

/**
 * Allow only running calls.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterRunning extends Filter
{
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return $update instanceof PrivateCall && $update->getCallState() === CallState::RUNNING;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter;

use Attribute;
use danog\MadelineProto\EventHandler\AbstractMessage;
use danog\MadelineProto\EventHandler\Calls\MultiCallMessage;
use danog\MadelineProto\EventHandler\Calls\PrivateCall;
use danog\MadelineProto\EventHandler\Update;
use danog\MadelineProto\VoIP\CallState;

/**
 * Allow only incoming messages.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterIncoming extends Filter
{
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return ($update instanceof AbstractMessage && !$update->out)
            || ($update instanceof MultiCallMessage && !$update->out)
            || ($update instanceof PrivateCall && $update->getCallState() === CallState::INCOMING);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter;

use Attribute;
use danog\MadelineProto\EventHandler\Message\PrivateMessage;
use danog\MadelineProto\EventHandler\Update;

/**
 * Allow only updates coming from private chats.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterPrivate extends Filter
{
    /**
     * @psalm-pure
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return $update instanceof PrivateMessage;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter;

use danog\MadelineProto\EventHandler;
use danog\MadelineProto\EventHandler\AbstractMessage;
use danog\MadelineProto\EventHandler\AbstractStory;
use danog\MadelineProto\EventHandler\BotCommands;
use danog\MadelineProto\EventHandler\Calls\MultiCallMessage;
use danog\MadelineProto\EventHandler\ChatInviteRequester\BotChatInviteRequest;
use danog\MadelineProto\EventHandler\InlineQuery;
use danog\MadelineProto\EventHandler\Query\ButtonQuery;
use danog\MadelineProto\EventHandler\Story\StoryReaction;
use danog\MadelineProto\EventHandler\Typing;
use danog\MadelineProto\EventHandler\Update;
use danog\MadelineProto\EventHandler\User\Blocked;
use danog\MadelineProto\EventHandler\User\BotStopped;
use danog\MadelineProto\EventHandler\User\Phone;
use danog\MadelineProto\EventHandler\User\Status;
use danog\MadelineProto\EventHandler\User\Username;

/**
 * Allow incoming or outgoing group messages made by a certain sender.
 *
 * @internal
 */
abstract class AbstractFilterFromSender extends Filter
{
    private readonly int $peerResolved;
    /**
     * @psalm-mutation-free
     */
    public function __construct(private readonly string|int $peer)
    {
    }
    #[\Override]
    public function initialize(EventHandler $API): Filter
    {
        /** @psalm-suppress InaccessibleProperty */
        $this->peerResolved = $API->getId($this->peer);
        return $this;
    }
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return ($update instanceof AbstractMessage && $update->senderId === $this->peerResolved) ||
            ($update instanceof AbstractStory && $update->senderId === $this->peerResolved) ||
            ($update instanceof MultiCallMessage && $update->fromId === $this->peerResolved) ||
            ($update instanceof StoryReaction && $update->senderId === $this->peerResolved) ||
            ($update instanceof ButtonQuery && $update->userId === $this->peerResolved) ||
            ($update instanceof InlineQuery && $update->userId === $this->peerResolved) ||
            ($update instanceof Typing && $update->userId === $this->peerResolved) ||
            ($update instanceof Blocked && $update->userId === $this->peerResolved) ||
            ($update instanceof BotStopped && $update->userId === $this->peerResolved) ||
            ($update instanceof Phone && $update->userId === $this->peerResolved) ||
            ($update instanceof Status && $update->userId === $this->peerResolved) ||
            ($update instanceof Username && $update->userId === $this->peerResolved) ||
            ($update instanceof BotCommands && $update->botId === $this->peerResolved) ||
            ($update instanceof BotChatInviteRequest && $update->userId === $this->peerResolved);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter;

use Attribute;
use danog\MadelineProto\EventHandler\Update;

/**
 * Allow all updates.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterAllowAll extends Filter
{
    /**
     * @psalm-pure
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return true;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter;

use Attribute;
use danog\MadelineProto\EventHandler;
use danog\MadelineProto\EventHandler\AbstractStory;
use danog\MadelineProto\EventHandler\BotCommands;
use danog\MadelineProto\EventHandler\Calls\AbstractGroupCallMessage;
use danog\MadelineProto\EventHandler\Channel\ChannelParticipant;
use danog\MadelineProto\EventHandler\Channel\MessageForwards;
use danog\MadelineProto\EventHandler\Channel\MessageViewsChanged;
use danog\MadelineProto\EventHandler\Channel\UpdateChannel;
use danog\MadelineProto\EventHandler\ChatInviteRequester;
use danog\MadelineProto\EventHandler\Delete\DeleteChannelMessages;
use danog\MadelineProto\EventHandler\Delete\DeleteScheduledMessages;
use danog\MadelineProto\EventHandler\InlineQuery;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Pinned;
use danog\MadelineProto\EventHandler\Query\ButtonQuery;
use danog\MadelineProto\EventHandler\Story\StoryReaction;
use danog\MadelineProto\EventHandler\Typing;
use danog\MadelineProto\EventHandler\Update;
use danog\MadelineProto\EventHandler\User\Blocked;
use danog\MadelineProto\EventHandler\User\BotStopped;
use danog\MadelineProto\EventHandler\User\Phone;
use danog\MadelineProto\EventHandler\User\Status;
use danog\MadelineProto\EventHandler\User\Username;

/**
 * Allow messages coming from or sent to a certain peer.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterPeer extends Filter
{
    private readonly int $peerResolved;
    /**
     * @psalm-mutation-free
     */
    public function __construct(private readonly string|int $peer)
    {
    }
    #[\Override]
    public function initialize(EventHandler $API): Filter
    {
        /** @psalm-suppress InaccessibleProperty */
        $this->peerResolved = $API->getId($this->peer);
        return $this;
    }
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return ($update instanceof Message && $update->chatId === $this->peerResolved) ||
            ($update instanceof AbstractStory && $update->senderId === $this->peerResolved) ||
            ($update instanceof AbstractGroupCallMessage && $update->peerId === $this->peerResolved) ||
            ($update instanceof StoryReaction && $update->senderId === $this->peerResolved) ||
            ($update instanceof ButtonQuery && $update->userId === $this->peerResolved) ||
            ($update instanceof InlineQuery && $update->userId === $this->peerResolved) ||
            ($update instanceof Typing && $update->userId === $this->peerResolved) ||
            ($update instanceof Blocked && $update->userId === $this->peerResolved) ||
            ($update instanceof BotStopped && $update->userId === $this->peerResolved) ||
            ($update instanceof Phone && $update->userId === $this->peerResolved) ||
            ($update instanceof Status && $update->userId === $this->peerResolved) ||
            ($update instanceof Username && $update->userId === $this->peerResolved) ||
            ($update instanceof BotCommands && $update->chatId === $this->peerResolved) ||
            ($update instanceof ChatInviteRequester && $update->chatId === $this->peerResolved) ||
            ($update instanceof MessageForwards && $update->chatId === $this->peerResolved) ||
            ($update instanceof MessageViewsChanged && $update->chatId === $this->peerResolved) ||
            ($update instanceof MessageViewsChanged && $update->chatId === $this->peerResolved) ||
            ($update instanceof UpdateChannel && $update->chatId === $this->peerResolved) ||
            ($update instanceof DeleteChannelMessages && $update->chatId === $this->peerResolved) ||
            ($update instanceof DeleteScheduledMessages && $update->chatId === $this->peerResolved) ||
            ($update instanceof Pinned && $update->chatId === $this->peerResolved) ||
            ($update instanceof ChannelParticipant && $update->chatId === $this->peerResolved);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter\Poll;

use Attribute;
use danog\MadelineProto\EventHandler\Filter\Filter;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Poll\SinglePoll;
use danog\MadelineProto\EventHandler\Update;

/**
 * Allow only messages that contain a single poll.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterSinglePoll extends Filter
{
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return ($update instanceof Message && $update->poll instanceof SinglePoll);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter\Poll;

use Attribute;
use danog\MadelineProto\EventHandler\Filter\Filter;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Poll\MultiplePoll;
use danog\MadelineProto\EventHandler\Update;

/**
 * Allow only messages that contain a multiple poll.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterMultiplePoll extends Filter
{
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return ($update instanceof Message && $update->poll instanceof MultiplePoll);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter\Poll;

use Attribute;
use danog\MadelineProto\EventHandler\Filter\Filter;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Poll\QuizPoll;
use danog\MadelineProto\EventHandler\Update;

/**
 * Allow only messages that contain a quiz poll.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterQuizPoll extends Filter
{
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return ($update instanceof Message && $update->poll instanceof QuizPoll);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter;

use AssertionError;
use danog\MadelineProto\EventHandler;
use danog\MadelineProto\EventHandler\Filter\Combinator\FiltersAnd;
use danog\MadelineProto\EventHandler\Filter\Combinator\FiltersOr;
use danog\MadelineProto\EventHandler\Filter\Location\FilterLiveLocation;
use danog\MadelineProto\EventHandler\Filter\Location\FilterVenue;
use danog\MadelineProto\EventHandler\Filter\Media\FilterAudio;
use danog\MadelineProto\EventHandler\Filter\Media\FilterDocument;
use danog\MadelineProto\EventHandler\Filter\Media\FilterDocumentPhoto;
use danog\MadelineProto\EventHandler\Filter\Media\FilterGif;
use danog\MadelineProto\EventHandler\Filter\Media\FilterPhoto;
use danog\MadelineProto\EventHandler\Filter\Media\FilterRoundVideo;
use danog\MadelineProto\EventHandler\Filter\Media\FilterSticker;
use danog\MadelineProto\EventHandler\Filter\Media\FilterVideo;
use danog\MadelineProto\EventHandler\Filter\Media\FilterVoice;
use danog\MadelineProto\EventHandler\Filter\Poll\FilterMultiplePoll;
use danog\MadelineProto\EventHandler\Filter\Poll\FilterQuizPoll;
use danog\MadelineProto\EventHandler\Filter\Poll\FilterSinglePoll;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Message\ChannelMessage;
use danog\MadelineProto\EventHandler\Message\GroupMessage;
use danog\MadelineProto\EventHandler\Message\PrivateMessage;
use danog\MadelineProto\EventHandler\Message\SecretMessage;
use danog\MadelineProto\EventHandler\Message\ServiceMessage;
use danog\MadelineProto\EventHandler\SimpleFilter\Ended;
use danog\MadelineProto\EventHandler\SimpleFilter\FromAdmin;
use danog\MadelineProto\EventHandler\SimpleFilter\FromAdminOrOutgoing;
use danog\MadelineProto\EventHandler\SimpleFilter\HasAudio;
use danog\MadelineProto\EventHandler\SimpleFilter\HasDocument;
use danog\MadelineProto\EventHandler\SimpleFilter\HasDocumentPhoto;
use danog\MadelineProto\EventHandler\SimpleFilter\HasGif;
use danog\MadelineProto\EventHandler\SimpleFilter\HasLiveLocation;
use danog\MadelineProto\EventHandler\SimpleFilter\HasLocation;
use danog\MadelineProto\EventHandler\SimpleFilter\HasMedia;
use danog\MadelineProto\EventHandler\SimpleFilter\HasMultiplePoll;
use danog\MadelineProto\EventHandler\SimpleFilter\HasNoMedia;
use danog\MadelineProto\EventHandler\SimpleFilter\HasPhoto;
use danog\MadelineProto\EventHandler\SimpleFilter\HasPoll;
use danog\MadelineProto\EventHandler\SimpleFilter\HasQuizPoll;
use danog\MadelineProto\EventHandler\SimpleFilter\HasRoundVideo;
use danog\MadelineProto\EventHandler\SimpleFilter\HasSinglePoll;
use danog\MadelineProto\EventHandler\SimpleFilter\HasSticker;
use danog\MadelineProto\EventHandler\SimpleFilter\HasTopic;
use danog\MadelineProto\EventHandler\SimpleFilter\HasVenue;
use danog\MadelineProto\EventHandler\SimpleFilter\HasVideo;
use danog\MadelineProto\EventHandler\SimpleFilter\HasVoice;
use danog\MadelineProto\EventHandler\SimpleFilter\Incoming;
use danog\MadelineProto\EventHandler\SimpleFilter\IsEdited;
use danog\MadelineProto\EventHandler\SimpleFilter\IsForwarded;
use danog\MadelineProto\EventHandler\SimpleFilter\IsNotEdited;
use danog\MadelineProto\EventHandler\SimpleFilter\IsReply;
use danog\MadelineProto\EventHandler\SimpleFilter\IsReplyToSelf;
use danog\MadelineProto\EventHandler\SimpleFilter\Outgoing;
use danog\MadelineProto\EventHandler\SimpleFilter\Running;
use danog\MadelineProto\EventHandler\Update;
use ReflectionIntersectionType;
use ReflectionNamedType;
use ReflectionType;
use ReflectionUnionType;

abstract class Filter
{
    /** @psalm-impure */
    abstract public function apply(Update $update): bool;
    /**
     * Run some initialization logic, optionally returning a new filter to replace the current one.
     *
     * @psalm-mutation-free
     */
    public function initialize(EventHandler $API): Filter
    {
        return $this;
    }

    public static function fromReflectionType(ReflectionType $type): Filter
    {
        return match (true) {
            $type instanceof ReflectionUnionType => new FiltersOr(
                ...array_map(
                    self::fromReflectionType(...),
                    $type->getTypes()
                )
            ),
            $type instanceof ReflectionIntersectionType => new FiltersAnd(
                ...array_map(
                    self::fromReflectionType(...),
                    $type->getTypes()
                )
            ),
            $type instanceof ReflectionNamedType => match ($type->getName()) {
                Incoming::class => new FilterIncoming,
                Outgoing::class => new FilterOutgoing,
                Update::class => new FilterAllowAll,
                Message::class => new FilterMessage,
                PrivateMessage::class => new FilterPrivate,
                SecretMessage::class => new FilterSecret,
                GroupMessage::class => new FilterGroup,
                ChannelMessage::class => new FilterChannel,
                ServiceMessage::class => new FilterService,
                IsEdited::class => new FilterEdited,
                IsNotEdited::class => new FilterNotEdited,
                IsForwarded::class => new FilterForwarded,
                IsReply::class => new FilterReply,
                IsReplyToSelf::class => new FilterReplyToSelf,
                HasMedia::class => new FilterMedia,
                HasNoMedia::class => new FilterNoMedia,
                FromAdmin::class => new FilterFromAdmin,
                HasAudio::class => new FilterAudio,
                HasDocument::class => new FilterDocument,
                HasDocumentPhoto::class => new FilterDocumentPhoto,
                HasGif::class => new FilterGif,
                HasPhoto::class => new FilterPhoto,
                HasRoundVideo::class => new FilterRoundVideo,
                HasSticker::class => new FilterSticker,
                HasVideo::class => new FilterVideo,
                HasVoice::class => new FilterVoice,
                HasTopic::class => new FilterTopic,
                HasPoll::class => new FilterPoll,
                HasQuizPoll::class => new FilterQuizPoll,
                HasSinglePoll::class => new FilterSinglePoll,
                HasMultiplePoll::class => new FilterMultiplePoll,
                HasLocation::class => new FilterLocation,
                HasLiveLocation::class => new FilterLiveLocation,
                HasVenue::class => new FilterVenue,
                Ended::class => new FilterEnded,
                Running::class => new FilterRunning,
                FromAdminOrOutgoing::class => new FiltersOr(new FilterFromAdmin, new FilterOutgoing),
                default => is_subclass_of($type->getName(), Update::class)
                    ? new class($type->getName()) extends Filter {
                        /**
                         * @psalm-mutation-free
                         */
                        public function __construct(private readonly string $class)
                        {
                        }
                        /**
                         * @psalm-mutation-free
                         */
                        #[\Override]
                        public function apply(Update $update): bool
                        {
                            return $update instanceof $this->class;
                        }
                    }
                : throw new AssertionError("Unknown type ".$type->getName().", did you forget to `use` it?")
            }
        };
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter;

use Attribute;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Update;

/**
 * Allows messages that weren't edited.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterNotEdited extends Filter
{
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return $update instanceof Message && $update->editDate === null;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter;

use Attribute;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Update;

/**
 * Allow messages that reply to other messages.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterReply extends Filter
{
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return $update instanceof Message && $update->replyToMsgId !== null;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter;

use AssertionError;
use Attribute;
use danog\MadelineProto\EventHandler\Calls\MultiCallMessage;
use danog\MadelineProto\EventHandler\CommandType;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Update;
use Webmozart\Assert\Assert;

/**
 * Allow only messages containing the specified command.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterCommand extends Filter
{
    /**
     * @var array<CommandType>
     */
    public readonly array $commandTypes;
    /**
     * @param string            $command Command
     * @param list<CommandType> $types   Command types, if empty all command types are allowed.
     *
     * @psalm-mutation-free
     */
    public function __construct(private readonly string $command, array $types = [CommandType::BANG, CommandType::DOT, CommandType::SLASH])
    {
        Assert::true(preg_match("/^[\w@]+$/", $command) === 1, "An invalid command was specified!");
        Assert::notEmpty($types, 'No command types were specified!');
        $c = [];
        foreach ($types as $type) {
            if (isset($c[$type->value])) {
                throw new AssertionError($type->value." was already specified!");
            }
            $c[$type->value] = true;
        }
        $this->commandTypes = $types;
    }
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return ($update instanceof Message || $update instanceof MultiCallMessage) && $update->command === $this->command && \in_array($update->commandType, $this->commandTypes, true);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter;

use Attribute;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Update;

/**
 * Allows messages that were edited.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterEdited extends Filter
{
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return $update instanceof Message && $update->editDate !== null;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter;

use Attribute;
use danog\MadelineProto\EventHandler\Calls\MultiCallMessage;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Story\Story;
use danog\MadelineProto\EventHandler\Update;
use Webmozart\Assert\Assert;

/**
 * Allow only messages that ends with a specific case-insensitive content.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterTextEndsCaseInsensitive extends Filter
{
    private readonly string $content;

    /**
     * @psalm-mutation-free
     */
    public function __construct(
        string $content,
    ) {
        Assert::notEmpty($content);
        $this->content = mb_strtolower($content);
    }

    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return ($update instanceof Message && str_ends_with(mb_strtolower($update->message), $this->content)) ||
            ($update instanceof Story && str_ends_with(mb_strtolower($update->caption), $this->content)) ||
            ($update instanceof MultiCallMessage && str_ends_with(mb_strtolower($update->message->text), $this->content));
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter;

use Attribute;
use danog\MadelineProto\EventHandler;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Update;

/**
 * Allow only forwarded messages from a certain sender.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterForwardedFrom extends Filter
{
    private readonly int $peerResolved;

    /**
     * @psalm-mutation-free
     */
    public function __construct(private readonly string|int $peer)
    {
    }

    #[\Override]
    public function initialize(EventHandler $API): Filter
    {
        /** @psalm-suppress InaccessibleProperty */
        $this->peerResolved = $API->getId($this->peer);
        return $this;
    }

    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return $update instanceof Message && ($update->fwdInfo?->fromId === $this->peerResolved);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter;

use Attribute;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Update;

/**
 * Allow messages that reply to one of our messages.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterReplyToSelf extends Filter
{
    #[\Override]
    public function apply(Update $update): bool
    {
        return $update instanceof Message && $update->getReply()?->out;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter;

use Attribute;
use danog\MadelineProto\EventHandler\Message\SecretMessage;
use danog\MadelineProto\EventHandler\Update;

/**
 * Allow only updates coming from secret chats.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterSecret extends Filter
{
    /**
     * @psalm-pure
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return $update instanceof SecretMessage;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter;

use Attribute;
use danog\MadelineProto\EventHandler\Query\ButtonQuery;
use danog\MadelineProto\EventHandler\Update;
use Webmozart\Assert\Assert;

/**
 * Filters based on the content of a button query.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterButtonQueryData extends Filter
{
    /**
     * @psalm-mutation-free
     */
    public function __construct(
        private readonly string $content
    ) {
        Assert::notEmpty($content);
    }
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return $update instanceof ButtonQuery && $update->data === $this->content;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter;

use Attribute;
use danog\MadelineProto\EventHandler\Calls\MultiCallMessage;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Story\Story;
use danog\MadelineProto\EventHandler\Update;
use Webmozart\Assert\Assert;

/**
 * Allow only messages that ends with a specific content.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterTextEnds extends Filter
{
    /**
     * @psalm-mutation-free
     */
    public function __construct(
        private readonly string $content
    ) {
        Assert::notEmpty($content);
    }

    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return ($update instanceof Message && str_ends_with($update->message, $this->content)) ||
            ($update instanceof Story && str_ends_with($update->caption, $this->content)) ||
            ($update instanceof MultiCallMessage && str_ends_with($update->message->text, $this->content));
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter\Combinator;

use Attribute;
use danog\MadelineProto\EventHandler;
use danog\MadelineProto\EventHandler\Filter\Filter;
use danog\MadelineProto\EventHandler\Filter\FilterAllowAll;
use danog\MadelineProto\EventHandler\Update;
use Webmozart\Assert\Assert;

/**
 * ORs multiple filters.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FiltersOr extends Filter
{
    /** @var array<Filter> */
    private readonly array $filters;
    /**
     * @psalm-mutation-free
     */
    public function __construct(Filter ...$filters)
    {
        Assert::notEmpty($filters);
        $this->filters = $filters;
    }
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function initialize(EventHandler $API): Filter
    {
        $final = [];
        foreach ($this->filters as $filter) {
            $filter = $filter->initialize($API);
            if ($filter instanceof self) {
                $final = array_merge($final, $filter->filters);
            } else {
                $final []= $filter;
            }
        }
        foreach ($final as $f) {
            if ($f instanceof FilterAllowAll) {
                return $f;
            }
        }
        return match (\count($final)) {
            0 => new FilterAllowAll,
            1 => $final[0],
            default => new self(...$final)
        };
    }
    #[\Override]
    public function apply(Update $update): bool
    {
        foreach ($this->filters as $filter) {
            if ($filter->apply($update)) {
                return true;
            }
        }
        return false;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter\Combinator;

use Attribute;
use danog\MadelineProto\EventHandler;
use danog\MadelineProto\EventHandler\Filter\Filter;
use danog\MadelineProto\EventHandler\Update;

/**
 * NOTs a filter.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterNot extends Filter
{
    /**
     * @psalm-mutation-free
     */
    public function __construct(private readonly Filter $filter)
    {
    }
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function initialize(EventHandler $API): Filter
    {
        $filter = $this->filter->initialize($API);
        if ($filter instanceof self) {
            // The nested filter is a FilterNot, optimize !!A => A
            return $filter->filter;
        }
        if ($filter === $this->filter) {
            // The nested filter didn't replace itself
            return $this;
        }
        // The nested filter replaced itself, re-wrap it
        return new self($filter);
    }

    #[\Override]
    public function apply(Update $update): bool
    {
        return !$this->filter->apply($update);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter\Combinator;

use Attribute;
use danog\MadelineProto\EventHandler;
use danog\MadelineProto\EventHandler\Filter\Filter;
use danog\MadelineProto\EventHandler\Filter\FilterAllowAll;
use danog\MadelineProto\EventHandler\Update;
use Webmozart\Assert\Assert;

/**
 * ANDs multiple filters.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FiltersAnd extends Filter
{
    /** @var array<Filter> */
    private readonly array $filters;
    /**
     * @psalm-mutation-free
     */
    public function __construct(Filter ...$filters)
    {
        Assert::notEmpty($filters);
        $this->filters = $filters;
    }
    #[\Override]
    public function initialize(EventHandler $API): Filter
    {
        $final = [];
        foreach ($this->filters as $filter) {
            $filter = $filter->initialize($API);
            if ($filter instanceof self) {
                $final = array_merge($final, $filter->filters);
            } else {
                $final []= $filter;
            }
        }
        $final = array_filter(
            $final,
            static fn (Filter $f): bool => !$f instanceof FilterAllowAll,
        );
        $final = array_values($final);
        return match (\count($final)) {
            0 => new FilterAllowAll,
            1 => $final[0],
            default => new self(...$final)
        };
    }
    #[\Override]
    public function apply(Update $update): bool
    {
        foreach ($this->filters as $filter) {
            if (!$filter->apply($update)) {
                return false;
            }
        }
        return true;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter\Location;

use Attribute;
use danog\MadelineProto\EventHandler\Filter\Filter;
use danog\MadelineProto\EventHandler\Location\Venue;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Update;

/**
 * Allow only messages that contain a venue.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterVenue extends Filter
{
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return ($update instanceof Message && $update->location instanceof Venue);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter\Location;

use Attribute;
use danog\MadelineProto\EventHandler\Filter\Filter;
use danog\MadelineProto\EventHandler\Location\LiveLocation;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Update;

/**
 * Allow only messages that contain a live location.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterLiveLocation extends Filter
{
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return ($update instanceof Message && $update->location instanceof LiveLocation);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter;

use Attribute;
use danog\MadelineProto\EventHandler\Calls\MultiCallMessage;
use danog\MadelineProto\EventHandler\InlineQuery;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Query\ButtonQuery;
use danog\MadelineProto\EventHandler\Story\Story;
use danog\MadelineProto\EventHandler\Update;
use Webmozart\Assert\Assert;

/**
 * Allow only messages or button queries matching the specified regex.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterRegex extends Filter
{
    /**
     * @param non-empty-string $regex
     * @param int-mask<0, 256, 512> $flags
     */
    public function __construct(
        private readonly string $regex,
        private readonly int $flags = 0,
        private readonly int $offset = 0
    ) {
        preg_match($regex, '', $m, $flags, $offset);
        Assert::eq(preg_last_error_msg(), 'No error');
    }

    #[\Override]
    public function apply(Update $update): bool
    {
        if ($update instanceof Message && preg_match($this->regex, $update->message, $matches, $this->flags, $this->offset)) {
            /** @psalm-suppress InaccessibleProperty */
            $update->matches = $matches;
            return true;
        }

        if ($update instanceof ButtonQuery && preg_match($this->regex, $update->data, $matches, $this->flags, $this->offset)) {
            /** @psalm-suppress InaccessibleProperty */
            $update->matches = $matches;
            return true;
        }

        if ($update instanceof InlineQuery && preg_match($this->regex, $update->query, $matches, $this->flags, $this->offset)) {
            /** @psalm-suppress InaccessibleProperty */
            $update->matches = $matches;
            return true;
        }

        if ($update instanceof Story && preg_match($this->regex, $update->caption, $matches, $this->flags, $this->offset)) {
            /** @psalm-suppress InaccessibleProperty */
            $update->matches = $matches;
            return true;
        }
        if ($update instanceof MultiCallMessage && preg_match($this->regex, $update->message->text, $matches, $this->flags, $this->offset)) {
            /** @psalm-suppress InaccessibleProperty */
            $update->matches = $matches;
            return true;
        }
        return false;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter\Media;

use Attribute;
use danog\MadelineProto\EventHandler\Filter\Filter;
use danog\MadelineProto\EventHandler\Media\Voice;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Update;

/**
 * Allow only voice messages.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterVoice extends Filter
{
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return $update instanceof Message && $update->media instanceof Voice;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter\Media;

use Attribute;
use danog\MadelineProto\EventHandler\Filter\Filter;
use danog\MadelineProto\EventHandler\Media\AbstractSticker;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Update;

/**
 * Allow only stickers.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterSticker extends Filter
{
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return $update instanceof Message && $update->media instanceof AbstractSticker;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter\Media;

use Attribute;
use danog\MadelineProto\EventHandler\Filter\Filter;
use danog\MadelineProto\EventHandler\Media\Video;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Update;

/**
 * Allow only videos.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterVideo extends Filter
{
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return $update instanceof Message && $update->media instanceof Video;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter\Media;

use Attribute;
use danog\MadelineProto\EventHandler\Filter\Filter;
use danog\MadelineProto\EventHandler\Media\Photo;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Update;

/**
 * Allow only photos.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterPhoto extends Filter
{
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return $update instanceof Message && $update->media instanceof Photo;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter\Media;

use Attribute;
use danog\MadelineProto\EventHandler\Filter\Filter;
use danog\MadelineProto\EventHandler\Media\Gif;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Update;

/**
 * Allow only GIFs.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterGif extends Filter
{
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return $update instanceof Message && $update->media instanceof Gif;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter\Media;

use Attribute;
use danog\MadelineProto\EventHandler\Filter\Filter;
use danog\MadelineProto\EventHandler\Media\DocumentPhoto;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Update;

/**
 * Allow only documents containing an image.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterDocumentPhoto extends Filter
{
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return $update instanceof Message && $update->media instanceof DocumentPhoto;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter\Media;

use Attribute;
use danog\MadelineProto\EventHandler\Filter\Filter;
use danog\MadelineProto\EventHandler\Media\RoundVideo;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Update;

/**
 * Allow only round videos.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterRoundVideo extends Filter
{
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return $update instanceof Message && $update->media instanceof RoundVideo;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter\Media;

use Attribute;
use danog\MadelineProto\EventHandler\Filter\Filter;
use danog\MadelineProto\EventHandler\Media\Audio;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Update;

/**
 * Allow only audio files.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterAudio extends Filter
{
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return $update instanceof Message && $update->media instanceof Audio;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter\Media;

use Attribute;
use danog\MadelineProto\EventHandler\Filter\Filter;
use danog\MadelineProto\EventHandler\Media\Document;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Update;

/**
 * Allow only documents.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterDocument extends Filter
{
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return $update instanceof Message && $update->media instanceof Document;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter;

use Attribute;

/**
 * Allow incoming or outgoing group messages made by a certain list of senders.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterFromSenders extends AbstractFilterFromSenders
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter;

use Attribute;

/**
 * Allow incoming or outgoing group messages made by a certain list of senders.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterSenders extends AbstractFilterFromSenders
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter;

use Attribute;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Typing\SupergroupUserTyping;
use danog\MadelineProto\EventHandler\Update;
use Webmozart\Assert\Assert;

/**
 * Allow only messages with a specific topic id (Supergroups only).
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterTopicId extends Filter
{
    /**
     * @psalm-mutation-free
     */
    public function __construct(
        private readonly int $topicId
    ) {
        Assert::greaterThan($topicId, 0);
    }
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return ($update instanceof Message && $update->topicId === $this->topicId) ||
        ($update instanceof SupergroupUserTyping && $update->topicId === $this->topicId);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter;

use Attribute;
use danog\MadelineProto\EventHandler\Calls\MultiCallMessage;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Story\Story;
use danog\MadelineProto\EventHandler\Update;
use Webmozart\Assert\Assert;

/**
 * Allow only messages that contain a specific case-insensitive content.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterTextContainsCaseInsensitive extends Filter
{
    private readonly string $content;

    /**
     * @psalm-mutation-free
     */
    public function __construct(
        string $content,
    ) {
        Assert::notEmpty($content);
        $this->content = mb_strtolower($content);
    }
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return ($update instanceof Message && str_contains(mb_strtolower($update->message), $this->content)) ||
            ($update instanceof Story && str_contains(mb_strtolower($update->caption), $this->content)) ||
            ($update instanceof MultiCallMessage && str_contains(mb_strtolower($update->message->text), $this->content));
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter;

use Attribute;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Typing\SupergroupUserTyping;
use danog\MadelineProto\EventHandler\Update;

/**
 * Allow only messages coming from groups that has topics (Supergroups only).
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterTopic extends Filter
{
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return ($update instanceof Message && $update->topicId !== null) ||
        ($update instanceof SupergroupUserTyping && $update->topicId !== null);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter;

use Attribute;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Update;

/**
 * Allow any non-service message.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterMessage extends Filter
{
    /**
     * @psalm-pure
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return $update instanceof Message;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter;

use Attribute;
use danog\MadelineProto\EventHandler\Message\ChannelMessage;
use danog\MadelineProto\EventHandler\Update;

/**
 * Allow only updates coming from channels.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterChannel extends Filter
{
    /**
     * @psalm-pure
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return $update instanceof ChannelMessage;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter;

use Attribute;
use danog\MadelineProto\EventHandler\Calls\MultiCallMessage;
use danog\MadelineProto\EventHandler\Message;
use danog\MadelineProto\EventHandler\Story\Story;
use danog\MadelineProto\EventHandler\Update;
use Webmozart\Assert\Assert;

/**
 * Allow only messages that start with a specific case-insensitive content.
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterTextStartsCaseInsensitive extends Filter
{
    private readonly string $content;

    /**
     * @psalm-mutation-free
     */
    public function __construct(
        string $content,
    ) {
        Assert::notEmpty($content);
        $this->content = mb_strtolower($content);
    }

    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return ($update instanceof Message && str_starts_with(mb_strtolower($update->message), $this->content)) ||
            ($update instanceof Story && str_starts_with(mb_strtolower($update->caption), $this->content)) ||
            ($update instanceof MultiCallMessage && str_starts_with(mb_strtolower($update->message->text), $this->content));
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Filter;

use Attribute;
use danog\MadelineProto\EventHandler;
use danog\MadelineProto\EventHandler\AbstractMessage;
use danog\MadelineProto\EventHandler\AbstractStory;
use danog\MadelineProto\EventHandler\Calls\MultiCallMessage;
use danog\MadelineProto\EventHandler\InlineQuery;
use danog\MadelineProto\EventHandler\Query\ButtonQuery;
use danog\MadelineProto\EventHandler\Story\StoryReaction;
use danog\MadelineProto\EventHandler\Typing;
use danog\MadelineProto\EventHandler\Update;
use danog\MadelineProto\EventHandler\User\Blocked;
use danog\MadelineProto\EventHandler\User\BotStopped;
use danog\MadelineProto\EventHandler\User\Phone;
use danog\MadelineProto\EventHandler\User\Status;
use danog\MadelineProto\EventHandler\User\Username;

/**
 * Allow only messages coming from the admin (defined as the peers returned by getReportPeers).
 */
#[Attribute(Attribute::TARGET_METHOD)]
final class FilterFromAdmin extends Filter
{
    private readonly array $adminIds;
    #[\Override]
    public function initialize(EventHandler $API): Filter
    {
        /** @psalm-suppress InaccessibleProperty */
        $this->adminIds = $API->getAdminIds();
        return $this;
    }
    /**
     * @psalm-mutation-free
     */
    #[\Override]
    public function apply(Update $update): bool
    {
        return ($update instanceof AbstractMessage && \in_array($update->senderId, $this->adminIds, true)) ||
            ($update instanceof AbstractStory && \in_array($update->senderId, $this->adminIds, true)) ||
            ($update instanceof MultiCallMessage && \in_array($update->fromId, $this->adminIds, true)) ||
            ($update instanceof StoryReaction && \in_array($update->senderId, $this->adminIds, true)) ||
            ($update instanceof ButtonQuery && \in_array($update->userId, $this->adminIds, true)) ||
            ($update instanceof InlineQuery && \in_array($update->userId, $this->adminIds, true)) ||
            ($update instanceof Typing && \in_array($update->userId, $this->adminIds, true)) ||
            ($update instanceof Blocked && \in_array($update->userId, $this->adminIds, true)) ||
            ($update instanceof BotStopped && \in_array($update->userId, $this->adminIds, true)) ||
            ($update instanceof Phone && \in_array($update->userId, $this->adminIds, true)) ||
            ($update instanceof Status && \in_array($update->userId, $this->adminIds, true)) ||
            ($update instanceof Username && \in_array($update->userId, $this->adminIds, true));
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Poll;

use danog\MadelineProto\EventHandler\AbstractPoll;

/** Represents a poll with a option can be chosen as answer*/
final class SinglePoll extends AbstractPoll
{
    /** @internal */
    public function __construct(array $rawPoll)
    {
        parent::__construct($rawPoll);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Poll;

use danog\MadelineProto\EventHandler\Message\Entities\MessageEntity;
use danog\MadelineProto\StrTools;
use danog\MadelineProto\TL\Types\Bytes;
use JsonSerializable;
use ReflectionClass;
use ReflectionProperty;

/** Represents a possible answer of a poll */
final class PollAnswer implements JsonSerializable
{
    /** Textual representation of the answer */
    public readonly string $text;

    /**
     * Styled text entities in the answer.
     *
     * @var list<MessageEntity>
     */
    public readonly array $entities;

    /** The param that has to be passed to [messages.sendVote](https://docs.madelineproto.xyz/API_docs/methods/messages.sendVote.html) */
    public readonly string $option;

    /** Whether we have chosen this answer */
    public readonly ?bool $chosen;

    /** For quizzes, whether the option we have chosen is correct */
    public readonly ?bool $correct;

    /** How many users voted for this option */
    public readonly ?int $voters;

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(array $rawAnswer)
    {
        $this->text = $rawAnswer['text']['text'];
        $this->entities = MessageEntity::fromRawEntities($rawAnswer['text']['entities']);
        $this->option = (string) $rawAnswer['option'];
        $this->chosen = $rawAnswer['chosen'] ?? null;
        $this->correct = $rawAnswer['correct'] ?? null;
        $this->voters = $rawAnswer['voters'] ?? null;
    }

    protected readonly string $html;
    protected readonly string $htmlTelegram;

    /**
     * Get an HTML version of the answer.
     *
     * @psalm-suppress InaccessibleProperty
     *
     * @param bool $allowTelegramTags Whether to allow telegram-specific tags like tg-spoiler, tg-emoji, mention links and so on...
     */
    public function getHTML(bool $allowTelegramTags = false): string
    {
        if (!$this->entities) {
            return StrTools::htmlEscape($this->text);
        }
        if ($allowTelegramTags) {
            return $this->htmlTelegram ??= StrTools::entitiesToHtml($this->text, $this->entities, $allowTelegramTags);
        }
        return $this->html ??= StrTools::entitiesToHtml($this->text, $this->entities, $allowTelegramTags);
    }

    /** @internal */
    #[\Override]
    public function jsonSerialize(): mixed
    {
        $res = ['_' => static::class];
        $refl = new ReflectionClass($this);
        foreach ($refl->getProperties(ReflectionProperty::IS_PUBLIC) as $prop) {
            $res[$prop->getName()] = $prop->getValue($this);
        }
        $res['option'] = new Bytes($res['option']);
        return $res;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Poll;

use danog\MadelineProto\EventHandler\AbstractPoll;

/** Represents a poll with multiple options can be chosen as answer */
final class MultiplePoll extends AbstractPoll
{
    /** @internal */
    public function __construct(array $rawPoll)
    {
        parent::__construct($rawPoll);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Poll;

use danog\MadelineProto\EventHandler\AbstractPoll;
use danog\MadelineProto\EventHandler\Message\Entities\MessageEntity;
use danog\MadelineProto\StrTools;

/** Represents a quiz (with wrong and correct answers, results shown in the return type) poll */
final class QuizPoll extends AbstractPoll
{
    /** Explanation of quiz solution */
    public readonly ?string $solution;

    /** @var list<MessageEntity> Message [entities](https://core.telegram.org/api/entities) for styled text in quiz solution */
    public readonly array $solutionEntities;

    /** @internal */
    public function __construct(array $rawPoll)
    {
        parent::__construct($rawPoll);
        $this->solution = $rawPoll['results']['solution'] ?? null;
        $this->solutionEntities = MessageEntity::fromRawEntities($rawPoll['results']['solution_entites'] ?? []);
    }

    protected readonly string $htmlSolution;
    protected readonly string $htmlSolutionTelegram;

    /**
     * Get an HTML version of the solution.
     *
     * @psalm-suppress InaccessibleProperty
     *
     * @param bool $allowTelegramTags Whether to allow telegram-specific tags like tg-spoiler, tg-emoji, mention links and so on...
     */
    public function getSolutionHTML(bool $allowTelegramTags = false): ?string
    {
        if ($this->solution === null) {
            return null;
        }
        if (!$this->solutionEntities) {
            return StrTools::htmlEscape($this->solution);
        }
        if ($allowTelegramTags) {
            return $this->htmlSolutionTelegram ??= StrTools::entitiesToHtml($this->solution, $this->solutionEntities, $allowTelegramTags);
        }
        return $this->htmlSolution ??= StrTools::entitiesToHtml($this->solution, $this->solutionEntities, $allowTelegramTags);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler;

use danog\MadelineProto\EventHandler\Message\Entities\MessageEntity;
use danog\MadelineProto\EventHandler\Poll\MultiplePoll;
use danog\MadelineProto\EventHandler\Poll\PollAnswer;
use danog\MadelineProto\EventHandler\Poll\QuizPoll;
use danog\MadelineProto\EventHandler\Poll\SinglePoll;
use danog\MadelineProto\StrTools;
use JsonSerializable;
use ReflectionClass;
use ReflectionProperty;

/** Poll */
abstract class AbstractPoll implements JsonSerializable
{
    /** ID of the poll */
    public readonly int $id;

    /** Whether the poll is closed and doesnâ€™t accept any more answers */
    public readonly bool $closed;

    /** The question of the poll */
    public readonly string $question;

    /**
     * Styled text entities in the question of the poll.
     *
     * @var list<MessageEntity>
     */
    public readonly array $questionEntities;

    /** @var list<PollAnswer> The possible answers */
    public readonly array $answers;

    /**	Amount of time in seconds the poll will be active after creation, 5-600 */
    public readonly ?int $closePeriod;

    /** Point in time (Unix timestamp) when the poll will be automatically closed. Must be at least 5 and no more than 600 seconds in the future */
    public readonly ?int $closeDate;

    /** @var list<int> IDs of the last users that recently voted in the poll */
    public readonly array $recentVoters;

    /** Total number of people that voted in the poll */
    public readonly ?int $totalVoters;

    /** @internal */
    public function __construct(array $rawPoll)
    {
        $this->id           = $rawPoll['poll']['id'];
        $this->closed       = $rawPoll['poll']['closed'];
        $this->question     = $rawPoll['poll']['question']['text'];
        $this->questionEntities = MessageEntity::fromRawEntities($rawPoll['poll']['question']['entities']);
        $this->closeDate    = $rawPoll['poll']['close_date'] ?? null;
        $this->closePeriod  = $rawPoll['poll']['close_period'] ?? null;
        $this->recentVoters = $rawPoll['results']['recent_voters'] ?? [];
        $this->totalVoters  = $rawPoll['results']['total_voters'] ?? null;
        $this->answers = self::getPollAnswers($rawPoll['poll']['answers'], $rawPoll['results']['results'] ?? []);
    }

    public static function fromRawPoll(array $rawPoll): AbstractPoll
    {
        if ($rawPoll['poll']['quiz']) {
            return new QuizPoll($rawPoll);
        }

        if ($rawPoll['poll']['multiple_choice']) {
            return new MultiplePoll($rawPoll);
        }

        return new SinglePoll($rawPoll);
    }

    /**
     * @return list<PollAnswer>
     *
     * @psalm-pure
     */
    private static function getPollAnswers(array $answers, array $result): array
    {
        $out = [];
        foreach ($answers as $key => $value) {
            $merge = array_merge($value, $result[$key] ?? []);
            $out[] = new PollAnswer($merge);
        }
        return $out;
    }

    protected readonly string $htmlQuestion;
    protected readonly string $htmlQuestionTelegram;

    /**
     * Get an HTML version of the question.
     *
     * @psalm-suppress InaccessibleProperty
     *
     * @param bool $allowTelegramTags Whether to allow telegram-specific tags like tg-spoiler, tg-emoji, mention links and so on...
     */
    public function getQuestionHTML(bool $allowTelegramTags = false): string
    {
        if (!$this->questionEntities) {
            return StrTools::htmlEscape($this->question);
        }
        if ($allowTelegramTags) {
            return $this->htmlQuestionTelegram ??= StrTools::entitiesToHtml($this->question, $this->questionEntities, $allowTelegramTags);
        }
        return $this->htmlQuestion ??= StrTools::entitiesToHtml($this->question, $this->questionEntities, $allowTelegramTags);
    }

    /** @internal */
    #[\Override]
    public function jsonSerialize(): mixed
    {
        $res = ['_' => static::class];
        $refl = new ReflectionClass($this);
        foreach ($refl->getProperties(ReflectionProperty::IS_PUBLIC) as $prop) {
            $res[$prop->getName()] = $prop->getValue($this);
        }
        return $res;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler;

use Amp\ByteStream\ReadableStream;
use Amp\Cancellation;
use danog\MadelineProto\EventHandler\Media\AbstractThumbnail;
use danog\MadelineProto\EventHandler\Media\InlineThumbnail;
use danog\MadelineProto\EventHandler\Media\Thumbnail;
use danog\MadelineProto\EventHandler\Media\VideoThumbnail;
use danog\MadelineProto\Ipc\IpcCapable;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\ResumableStream;
use danog\MadelineProto\Tools;
use JsonSerializable;

/**
 * Represents a generic media.
 */
abstract class Media extends IpcCapable implements JsonSerializable
{
    /** Media filesize */
    public readonly int $size;

    /** Media file name */
    public readonly string $fileName;

    /** Media file extension */
    public readonly string $fileExt;

    /** Media creation date */
    public readonly int $creationDate;

    /** Media MIME type */
    public readonly string $mimeType;

    /** Time-to-live of media */
    public readonly ?int $ttl;

    /**
     * Thumbnails, from the smallest to the biggest: for photos, the smaller sizes of the photo.
     *
     * Includes the thumbnails inlined in the media (always the only thumbnail of secret chat media),
     * which are downloaded from memory, without contacting Telegram.
     *
     * @var list<Thumbnail|InlineThumbnail>
     */
    public readonly array $thumbs;

    /**
     * Animated thumbnails (MPEG4 videos), from the smallest to the biggest.
     *
     * @var list<VideoThumbnail>
     */
    public readonly array $videoThumbs;

    /** Whether the media should be hidden behind a spoiler */
    public readonly bool $spoiler;

    /** File ID in bot API format (always present even for users) */
    public readonly string $botApiFileId;

    /** Unique file ID in bot API format (always present even for users) */
    public readonly string $botApiFileUniqueId;

    /** @internal Media location */
    public readonly array $location;

    /** @internal Encryption key for secret chat files */
    public readonly ?string $key;
    /** @internal Encryption IV for secret chat files */
    public readonly ?string $iv;
    /** @internal Encryption key fingerprint for secret chat files */
    protected readonly ?int $keyFingerprint;

    /** Whether this media originates from a secret chat. */
    public readonly bool $encrypted;

    /** @internal */
    public function __construct(
        MTProto $API,
        array $rawMedia,

        /** Whether this media is protected */
        public readonly bool $protected
    ) {
        parent::__construct($API);
        if ($rawMedia['secret'] ?? false) {
            $rawMedia = $rawMedia['document'];
        }
        $info = $this->extractDownloadInfo($API, $rawMedia);
        [
            'name' => $name,
            'ext' => $this->fileExt,
            'mime' => $this->mimeType,
            'size' => $this->size,
            'InputFileLocation' => $this->location
        ] = $info;
        $this->fileName = $name.$this->fileExt;
        $this->keyFingerprint = $info['key_fingerprint'] ?? null;
        $this->key = isset($info['key']) ? (string) $info['key'] : null;
        $this->iv = isset($info['iv']) ? (string) $info['iv'] : null;

        [
            'file_id' => $this->botApiFileId,
            'file_unique_id' => $this->botApiFileUniqueId
        ] = $this->extractBotApiFile($API, $rawMedia);

        $this->creationDate = ($rawMedia['document'] ?? $rawMedia['photo'] ?? $rawMedia)['date'];
        $this->ttl = $rawMedia['ttl_seconds'] ?? null;
        $this->spoiler = $rawMedia['spoiler'] ?? false;
        $this->encrypted = isset($rawMedia['iv']);
        [$this->thumbs, $this->videoThumbs] = $this->extractThumbs($API, $rawMedia);
    }

    /**
     * @internal
     *
     * @return array{name: string, ext: string, mime: string, size: int, InputFileLocation: array, key_fingerprint?: int, key?: string, iv?: string}
     */
    protected function extractDownloadInfo(MTProto $API, array $rawMedia): array
    {
        return $API->getDownloadInfo($rawMedia);
    }

    /**
     * @internal
     *
     * @return array{file_id: string, file_unique_id: string}
     */
    protected function extractBotApiFile(MTProto $API, array $rawMedia): array
    {
        return $API->extractBotAPIFile($API->MTProtoToBotAPI($rawMedia));
    }

    /**
     * @internal
     *
     * @return array{list<Thumbnail|InlineThumbnail>, list<VideoThumbnail>}
     */
    protected function extractThumbs(MTProto $API, array $rawMedia): array
    {
        if (isset($rawMedia['iv'])) {
            // Secret chat media only carries an inline JPEG thumbnail.
            if ((string) ($rawMedia['thumb'] ?? '') === '') {
                return [[], []];
            }
            return [[new InlineThumbnail($API, $rawMedia, [
                '_' => 'photoCachedSize',
                'type' => InlineThumbnail::SECRET_CHAT_TYPE,
                'w' => $rawMedia['thumb_w'] ?? 0,
                'h' => $rawMedia['thumb_h'] ?? 0,
                'bytes' => $rawMedia['thumb'],
            ], $this->protected)], []];
        }
        if (isset($rawMedia['photo'])) {
            $sizes = $rawMedia['photo']['sizes'];
            $videoSizes = $rawMedia['photo']['video_sizes'] ?? [];
            // The biggest size is the photo itself.
            $mainType = Tools::maxSize($sizes)['type'];
        } else {
            if ($rawMedia['_'] === 'decryptedMessageMediaExternalDocument') {
                $rawMedia = ['_' => 'messageMediaDocument', 'document' => $rawMedia + ['thumbs' => [$rawMedia['thumb']]]];
            }
            $sizes = $rawMedia['document']['thumbs'] ?? [];
            $videoSizes = $rawMedia['document']['video_thumbs'] ?? [];
            $mainType = null;
        }
        $thumbs = [];
        $videoThumbs = [];
        foreach ($sizes as $size) {
            switch ($size['_']) {
                case 'photoSize':
                case 'photoSizeProgressive':
                    if ($size['type'] !== $mainType) {
                        $thumbs[] = new Thumbnail($API, $rawMedia, $size, $this->protected);
                    }
                    break;
                case 'photoStrippedSize':
                case 'photoCachedSize':
                    $thumbs[] = new InlineThumbnail($API, $rawMedia, $size, $this->protected);
                    break;
            }
        }
        foreach ($videoSizes as $size) {
            if ($size['_'] === 'videoSize') {
                $videoThumbs[] = new VideoThumbnail($API, $rawMedia, $size, $this->protected);
            }
        }
        $cmp = static fn (AbstractThumbnail $a, AbstractThumbnail $b): int => $a->width*$a->height <=> $b->width*$b->height;
        usort($thumbs, $cmp);
        usort($videoThumbs, $cmp);
        return [$thumbs, $videoThumbs];
    }

    /**
     * Gets a download link for any file up to 4GB.
     *
     * @param string|null $scriptUrl Optional path to custom download script (not needed when running via web)
     */
    public function getDownloadLink(?string $scriptUrl = null): string
    {
        return $this->getClient()->getDownloadLink($this, $scriptUrl);
    }

    /**
     * Get a readable amp stream with the file contents.
     *
     * The returned stream is resumable: it can be serialized, and once unserialized,
     * reading continues from the end of the last chunk read before serialization.
     *
     * @param (callable(float, float, float): void)|null $cb Progress callback
     */
    public function getStream(?callable $cb = null, int $offset = 0, int $end = -1, ?Cancellation $cancellation = null): ReadableStream&ResumableStream
    {
        return $this->getClient()->downloadToReturnedStream($this, $cb, $offset, $end, $cancellation);
    }

    /**
     * Download the media to working directory or passed path.
     *
     * @param string $dir Directory where to download the file
     * @param (callable(float, float, float): void)|null $cb Progress callback
     */
    public function downloadToDir(?string $dir = null, ?callable $cb = null, ?Cancellation $cancellation = null): string
    {
        $dir ??= getcwd();
        return $this->getClient()->downloadToDir($this, $dir, $cb, $cancellation);
    }
    /**
     * Download the media to file.
     *
     * @param string $file Downloaded file path
     * @param (callable(float, float, float): void)|null $cb Progress callback
     */
    public function downloadToFile(string $file, ?callable $cb = null, ?Cancellation $cancellation = null): string
    {
        return $this->getClient()->downloadToFile($this, $file, $cb, $cancellation);
    }

    /**
     * @return array{
     *      ext: string,
     *      name: string,
     *      mime: string,
     *      size: int,
     *      InputFileLocation: array,
     *      key_fingerprint?: string,
     *      key?: string,
     *      iv?: string,
     * }
     *
     * @psalm-mutation-free
     */
    public function getDownloadInfo(): array
    {
        $result = [
            'name' => basename($this->fileName, $this->fileExt),
            'ext' => $this->fileExt,
            'mime' => $this->mimeType,
            'size' => $this->size,
            'InputFileLocation' => $this->location,
        ];
        if ($this->key !== null) {
            $result['key_fingerprint'] = $this->keyFingerprint;
            $result['key'] = $this->key;
            $result['iv'] = $this->iv;
        }
        return $result;
    }
    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function jsonSerialize(): mixed
    {
        $v = get_object_vars($this);
        $v['_'] = static::class;
        unset($v['API'], $v['session'], $v['location'], $v['key'], $v['iv'], $v['keyFingerprint']);
        return $v;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler;

use danog\MadelineProto\EventHandler\Calls\MultiCallParticipant;
use danog\MadelineProto\ParseMode;

/**
 * Common interface for the multi-party call types â€” {@see Calls\GroupCall} (video chats, livestreams),
 * {@see Calls\LiveStory} (live stories) and {@see Calls\ConferenceCall} (end-to-end encrypted conference
 * calls) â€” on top of the surface every call shares ({@see Call}).
 *
 * It covers what a call with more than two participants adds over a one-to-one {@see Calls\PrivateCall} call:
 * leaving without ending it for everyone else, managing who is in it (inviting, invite links, removing
 * participants), its title and settings, and messaging its participants. What only some call types
 * offer (server-side recording, scheduling, raising a hand, moderating messages, donations) lives on the
 * concrete classes: {@see Calls\AbstractGroupCall} for everything a video chat and a live story share,
 * {@see Calls\GroupCall} and {@see Calls\LiveStory} for their own.
 *
 * @template TParticipant of MultiCallParticipant
 */
interface MultiCall extends Call
{
    /**
     * The participants currently known to be in the call, keyed by their bot API id.
     *
     * The element type is call-type specific: a {@see Calls\GroupCallParticipant} (or, for a live
     * story, a {@see Calls\LiveStoryParticipant}) for a group call, a {@see Calls\ConferenceCallParticipant}
     * for a conference.
     *
     * @return array<int, TParticipant>
     *
     * @psalm-mutation-free
     */
    public function getParticipants(): array;

    /**
     * A participant of the call by their id, username or peer, or null if they are not in it.
     *
     * @return TParticipant|null
     *
     * @psalm-impure
     */
    public function getParticipant(string|int $participant): ?MultiCallParticipant;

    /**
     * Leave the call, keeping it running for the other participants (unlike {@see Call::discard()},
     * which ends it).
     *
     * @psalm-impure
     */
    public function leave(): static;

    /**
     * Change the title of the call.
     *
     * @psalm-impure
     */
    public function setTitle(string $title): static;

    /**
     * Invite users to the call.
     *
     * @param string|int ...$users The users to invite (user ids, usernames or peers).
     *
     * @psalm-impure
     */
    public function invite(string|int ...$users): static;

    /**
     * Export an invite link to the call.
     *
     * @param bool $canSelfUnmute Whether users joining with this link may speak without asking; ignored where the call has no such distinction.
     *
     * @psalm-impure
     */
    public function exportInvite(bool $canSelfUnmute = false): string;

    /**
     * Remove participants from the call.
     *
     * @param string|int ...$participants The participants to remove (user ids, usernames or peers).
     *
     * @psalm-impure
     */
    public function removeParticipant(string|int ...$participants): static;

    /**
     * Send an [in-call message Â»](https://core.telegram.org/api/group-calls#in-call-messages) to the
     * participants of the call, shown as an overlay by their clients (there is no chat history).
     *
     * @param string         $message   The text; markup in `$parseMode` is converted to entities.
     * @param ParseMode|null $parseMode Whether to parse HTML or Markdown markup in the text.
     * @param int|null       $paidStars Live stories only: Telegram Stars to donate with the message (at least the story's minimum, see {@see Calls\LiveStory::setPaidMessagesStars()}).
     * @param string|int|null $sendAs    Live stories only: the peer to send the message as.
     *
     * @psalm-impure
     */
    public function sendMessage(string $message, ?ParseMode $parseMode = null, ?int $paidStars = null, string|int|null $sendAs = null): static;

    /**
     * Send an [in-call reaction Â»](https://core.telegram.org/api/group-calls#in-call-reactions): a
     * single emoji, or a custom emoji (with the fallback emoji as `$emoji`).
     *
     * @param string   $emoji         The emoji.
     * @param int|null $customEmojiId The document id of a custom emoji to send instead, `$emoji` being its fallback.
     *
     * @psalm-impure
     */
    public function sendReaction(string $emoji, ?int $customEmojiId = null): static;

    /**
     * Enable or disable in-call messages (admins only).
     *
     * @psalm-impure
     */
    public function setMessagesEnabled(bool $enabled): static;

    /**
     * Mute or unmute a participant (admins only), or, for a non-admin, mute a participant only for
     * ourselves. A participant muted by an admin may not unmute themselves.
     *
     * @psalm-impure
     */
    public function muteParticipant(string|int $participant, bool $muted = true): static;

    /**
     * Set our local playback volume of a participant.
     *
     * @param int<1, 20000> $volume From 1 to 20000, where 10000 is 100%.
     *
     * @psalm-impure
     */
    public function setParticipantVolume(string|int $participant, int $volume): static;

    /**
     * Pause or resume our own video stream, telling the other participants to keep showing the last
     * frame rather than hiding it.
     *
     * @psalm-impure
     */
    public function setVideoPaused(bool $paused): static;

    /**
     * Whether new participants join muted (admins only).
     *
     * @psalm-impure
     */
    public function setJoinMuted(bool $joinMuted): static;

    /**
     * Invalidate every invite link exported so far (admins only).
     *
     * @psalm-impure
     */
    public function resetInviteHash(): static;

    /**
     * Whether the server switched us to [stream mode Â»](https://core.telegram.org/api/group-calls#stream-mode):
     * the call's media is received by downloading chunks rather than over WebRTC, and there is a single
     * mixed stream to record (see {@see Call::setOutput()} and {@see Call::setOutputFolder()}) rather than one per participant.
     *
     * @psalm-mutation-free
     */
    public function isStreamMode(): bool;

    /**
     * Whether the call's media is published by a single external RTMP publisher (an RTMP livestream).
     *
     * @psalm-mutation-free
     */
    public function isRtmpMode(): bool;
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler;

use danog\MadelineProto\MTProto;

/**
 * Indicates that some messages were pinned/unpinned.
 */
abstract class Pinned extends Update
{
    /** Whether the messages were pinned or unpinned. */
    public readonly bool $pinned;

    /** @var list<int> List of identifiers of pinned messages. */
    public readonly array $ids;

    /** ID of the chat where the messages were pinned. */
    public readonly int $chatId;

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(MTProto $API, array $rawPinned)
    {
        parent::__construct($API);
        $this->pinned = $rawPinned['pinned'];
        $this->ids = $rawPinned['messages'];
        $this->chatId = $API->getIdInternal($rawPinned);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler;

use JsonSerializable;

/** @internal */
enum InlineQueryPeerType: string implements JsonSerializable
{
    /** private chat */
    case PM = 'inlineQueryPeerTypePM';

    /** [chat](https://core.telegram.org/api/channel) */
    case Chat = 'inlineQueryPeerTypeChat';

    /** private chat with a bot. */
    case BotPM = 'inlineQueryPeerTypeBotPM';

    /** [channel](https://core.telegram.org/api/channel) */
    case Broadcast = 'inlineQueryPeerTypeBroadcast';

    /** [supergroup](https://core.telegram.org/api/channel) */
    case Megagroup = 'inlineQueryPeerTypeMegagroup';

    /** private chat with the bot itself */
    case SameBotPM = 'inlineQueryPeerTypeSameBotPM';

    /** @internal */
    #[\Override]
    public function jsonSerialize(): string
    {
        return $this->name;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\ChatInviteRequester;

use danog\MadelineProto\EventHandler\ChatInvite;
use danog\MadelineProto\EventHandler\ChatInviteRequester;
use danog\MadelineProto\MTProto;

/**
 * Indicates someone has requested to join a chat or channel (bots only).
 */
final class BotChatInviteRequest extends ChatInviteRequester
{
    /** When was the [join request Â»](https://core.telegram.org/api/invites#join-requests) made */
    public readonly int $date;

    /** The user ID that is asking to join the chat or channel */
    public readonly int $userId;

    /** Bio of the user */
    public readonly string $about;

    /** Chat invite link that was used by the user to send the [join request Â»](https://core.telegram.org/api/invites#join-requests) */
    public readonly ChatInvite $invite;

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(MTProto $API, array $rawChatInviteRequester)
    {
        parent::__construct($API, $rawChatInviteRequester);
        $this->date = $rawChatInviteRequester['date'];
        $this->userId = $rawChatInviteRequester['user_id'];
        $this->about = $rawChatInviteRequester['about'];
        $this->invite = ChatInvite::fromRawChatInvite($rawChatInviteRequester['invite']);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\ChatInviteRequester;

use danog\MadelineProto\EventHandler\ChatInviteRequester;
use danog\MadelineProto\MTProto;

/**
 * Someone has requested to join a chat or channel.
 */
final class PendingJoinRequests extends ChatInviteRequester
{
    /** Number of pending [join requests Â»](https://core.telegram.org/api/invites#join-requests) for the chat or channel */
    public readonly int $pending;

    /** @var list<int> IDs of users that have recently requested to join */
    public readonly array $recent;

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(MTProto $API, array $rawChatInviteRequester)
    {
        parent::__construct($API, $rawChatInviteRequester);
        $this->pending = $rawChatInviteRequester['requests_pending'];
        $this->recent = $rawChatInviteRequester['recent_requesters'];
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler;

use danog\MadelineProto\MTProto;

/** Represents a query sent by the user by clicking on a button. */
abstract class CallbackQuery extends Update
{
    /** Query ID */
    public readonly int $queryId;

    /** ID of the user that pressed the button */
    public readonly int $userId;

    /** Global identifier, uniquely corresponding to the chat to which the message with the callback button was sent. Useful for high scores in games. */
    public readonly int $chatInstance;

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(MTProto $API, array $rawCallback)
    {
        parent::__construct($API);
        $this->queryId = $rawCallback['query_id'];
        $this->userId = $rawCallback['user_id'];
        $this->chatInstance = $rawCallback['chat_instance'];
    }

    /**
     * @param ?string     $message   Popup to show
     * @param bool        $alert     Whether to show the message as a popup instead of a toast notification
     * @param string|null $url       URL to open
     * @param int         $cacheTime Cache validity (default set to 5 min based on telegram official docs ...)
     */
    public function answer(
        ?string  $message = null,
        bool    $alert = false,
        ?string $url = null,
        int     $cacheTime = 5 * 60
    ): bool {
        return $this->getClient()->methodCallAsyncRead(
            'messages.setBotCallbackAnswer',
            [
                'query_id' => $this->queryId,
                'message' => $message,
                'alert' => $alert,
                'url' => $url,
                'cache_time' => $cacheTime,
            ],
        );
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler;

use danog\MadelineProto\EventHandler\ChatInvite\ChatInviteExported;
use danog\MadelineProto\EventHandler\ChatInvite\ChatInvitePublicJoin;
use JsonSerializable;
use ReflectionClass;
use ReflectionProperty;

/**
 * Chat invite link that was used by the user to send the [join request Â»](https://core.telegram.org/api/invites#join-requests).
 */
abstract class ChatInvite implements JsonSerializable
{
    /**
     * @psalm-pure
     */
    public static function fromRawChatInvite(array $rawChatInvite): self
    {
        return match ($rawChatInvite['_']) {
            'chatInviteExported' => new ChatInviteExported($rawChatInvite),
            'chatInvitePublicJoinRequests' => new ChatInvitePublicJoin($rawChatInvite),
            default => throw new \AssertionError('Unknown ChatInvite type \'_\':' . $rawChatInvite['_']),
        };
    }

    /** @internal */
    #[\Override]
    public function jsonSerialize(): mixed
    {
        $res = ['_' => static::class];
        $refl = new ReflectionClass($this);
        foreach ($refl->getProperties(ReflectionProperty::IS_PUBLIC) as $prop) {
            $res[$prop->getName()] = $prop->getValue($this);
        }
        return $res;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler;

use danog\MadelineProto\MTProto;

/**
 * A user is typing.
 */
abstract class Typing extends Update
{
    /** The peer that is typing. */
    public readonly int $userId;

    /** Whether the user is typing, sending a media or doing something else. */
    public readonly Action $action;

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(MTProto $API, array $rawTyping)
    {
        parent::__construct($API);
        $this->userId = $rawTyping['user_id'] ?? $API->getIdInternal($rawTyping['from_id']);
        $this->action = Action::fromRawAction($rawTyping['action']);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Location;

use danog\MadelineProto\EventHandler\AbstractLocation;

/** Represents a location that isn't updated by the sender */
final class StaticLocation extends AbstractLocation
{
    /** @internal */
    public function __construct(array $rawGeoPoint)
    {
        parent::__construct($rawGeoPoint);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Location;

use danog\MadelineProto\EventHandler\AbstractLocation;

/** Represents a venue */
final class Venue extends AbstractLocation
{
    /** Venue name */
    public readonly string $title;

    /** Address */
    public readonly string $address;

    /** Venue provider: currently only "foursquare" and "gplaces" (Google Places) need to be supported */
    public readonly string $provider;

    /** Venue ID in the provider's database */
    public readonly string $venueId;

    /** Venue type in the provider's database (empty for venues received in secret chats) */
    public readonly string $venueType;

    /** @internal */
    public function __construct(array $rawGeoPoint, array $rawMedia)
    {
        parent::__construct($rawGeoPoint);
        $this->title = $rawMedia['title'];
        $this->address = $rawMedia['address'];
        $this->provider = $rawMedia['provider'];
        $this->venueId = $rawMedia['venue_id'];
        $this->venueType = $rawMedia['venue_type'] ?? '';
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Location;

use danog\MadelineProto\EventHandler\AbstractLocation;

/** Represents a live location, which the sender updates by editing the message until it expires */
final class LiveLocation extends AbstractLocation
{
    /** For how long (in seconds) the location is updated, starting from the date of the message; 0x7fffffff for an indefinite period */
    public readonly int $period;

    /** Direction in which the sender is moving, in degrees (1-360) */
    public readonly ?int $heading;

    /** The sender is notified when another user comes closer than this distance, in meters */
    public readonly ?int $proximityNotificationRadius;

    /** @internal */
    public function __construct(array $rawMedia)
    {
        parent::__construct($rawMedia['geo']);
        $this->period = $rawMedia['period'];
        $this->heading = $rawMedia['heading'] ?? null;
        $this->proximityNotificationRadius = $rawMedia['proximity_notification_radius'] ?? null;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler;

use danog\MadelineProto\MTProto;

/**
 * Indicates someone has requested to join a chat or channel.
 */
abstract class ChatInviteRequester extends Update
{
    /** The chat or channel in question */
    public readonly int $chatId;

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(MTProto $API, array $rawChatInviteRequester)
    {
        parent::__construct($API);
        $this->chatId = $API->getIdInternal($rawChatInviteRequester['peer']);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler;

use danog\MadelineProto\MTProto;

/**
 * The [command set](https://core.telegram.org/api/bots/commands) of a certain bot in a certain chat has changed.
 */
final class BotCommands extends Update
{
    /** ID of the bot that changed its command set. */
    public readonly int $botId;

    /** The affected chat. */
    public readonly int $chatId;

    /** @var list<Command> New bot commands. */
    public readonly array $commands;

    /** @internal */
    public function __construct(MTProto $API, array $rawBotCommands)
    {
        parent::__construct($API);
        $this->botId = $rawBotCommands['bot_id'];
        $this->chatId = $API->getIdInternal($rawBotCommands['peer']);
        $this->commands = array_map(
            static fn (array $command): Command => new Command($command),
            $rawBotCommands['commands']
        );
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler;

use danog\MadelineProto\CallNotAllowedException;
use danog\MadelineProto\EventHandler\Calls\CallDenialReason;
use danog\MadelineProto\EventHandler\Keyboard\InlineKeyboard;
use danog\MadelineProto\EventHandler\Keyboard\ReplyKeyboard;
use danog\MadelineProto\EventHandler\Media\Audio;
use danog\MadelineProto\EventHandler\Media\Document;
use danog\MadelineProto\EventHandler\Media\DocumentPhoto;
use danog\MadelineProto\EventHandler\Media\Gif;
use danog\MadelineProto\EventHandler\Media\MaskSticker;
use danog\MadelineProto\EventHandler\Media\Photo;
use danog\MadelineProto\EventHandler\Media\RoundVideo;
use danog\MadelineProto\EventHandler\Media\Sticker;
use danog\MadelineProto\EventHandler\Media\Video;
use danog\MadelineProto\EventHandler\Media\Voice;
use danog\MadelineProto\EventHandler\Message\Entities\MessageEntity;
use danog\MadelineProto\EventHandler\Message\ReportReason;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\ParseMode;
use danog\MadelineProto\StrTools;
use Webmozart\Assert\Assert;

/**
 * Represents an incoming or outgoing message.
 */
abstract class Message extends AbstractMessage
{
    /** Content of the message */
    public readonly string $message;

    /** @var list<int|string> list of our message reactions */
    protected array $reactions = [];

    /** Info about a forwarded message */
    public readonly ?ForwardedInfo $fwdInfo;

    /** Bot command (if present) */
    public readonly ?string $command;

    /** Bot command type (if present) */
    public readonly ?CommandType $commandType;

    /** @var list<string> Bot command arguments (if present) */
    public readonly ?array $commandArgs;

    /** Whether this message is protected */
    public readonly bool $protected;

    /**
     * @readonly
     *
     * @var list<string> Regex matches, if a filter regex is present
     */
    public ?array $matches = null;
    /**
     * @readonly
     *
     * @var array<array-key, array<array-key, list{string, int}|null|string>|mixed> Regex matches, if a filter multiple match regex is present
     */
    public ?array $matchesAll = null;

    /**
     * Attached media.
     */
    public readonly Audio|Document|DocumentPhoto|Gif|MaskSticker|Photo|RoundVideo|Sticker|Video|Voice|null $media;

    /** Whether this message is a *sent* scheduled message */
    public readonly bool $fromScheduled;

    /** If the message was generated by an inline query, ID of the bot that generated it */
    public readonly ?int $viaBotId;

    /** Last edit date of the message */
    public readonly ?int $editDate;

    /**
     * Indicates if the post has a hidden edit, which is an edit that does not modify the actual message content.
     * Used to signify non-content related updates such as reactions.
     */
    public readonly bool $editHide;

    /** Inline or reply keyboard. */
    public readonly InlineKeyboard|ReplyKeyboard|null $keyboard;

    /** Whether this message was [imported from a foreign chat service](https://core.telegram.org/api/import) */
    public readonly bool $imported;

    /** For Public Service Announcement messages, the PSA type */
    public readonly ?string $psaType;

    /**
     * @readonly
     * For sent messages, contains the next message in the chain if the original message had to be split.
     */
    public ?self $nextSent = null;
    // Todo media (photosizes, thumbs), albums, reactions, games eventually

    /** View counter for messages from channels or forwarded from channels */
    public readonly ?int $views;

    /** Forward counter for messages from channels or forwarded from channels */
    public readonly ?int $forwards;

    /** Author of the post, if signatures are enabled for messages from channels or forwarded from channels */
    public readonly ?string $signature;

    /** @var list<MessageEntity> Message [entities](https://core.telegram.org/api/entities) for styled text */
    public readonly array $entities;

    /**
     * Group ID for albums.
     *
     * All messages associated to the same album will have an identical grouped ID.
     */
    public readonly ?int $groupedId;

    /** The poll */
    public readonly ?AbstractPoll $poll;

    /** The location (a geo point, a live location or a venue) */
    public readonly ?AbstractLocation $location;

    /** @internal */
    public function __construct(
        MTProto $API,
        array $rawMessage,
        array $info,

        /** Whether this message is a scheduled message */
        public readonly bool $scheduled
    ) {
        parent::__construct($API, $rawMessage, $info);
        if (isset($rawMessage['decrypted_message'])) {
            $this->protected = true;
            $rawMessage = $rawMessage['decrypted_message'];
        } else {
            $this->protected = $rawMessage['noforwards'];
        }
        $this->views = $rawMessage['views'] ?? null;
        $this->forwards = $rawMessage['forwards'] ?? null;
        $this->signature = $rawMessage['post_author'] ?? null;
        $this->groupedId = $rawMessage['grouped_id'] ?? null;
        $this->editDate = $rawMessage['edit_date'] ?? null;
        $this->editHide = $rawMessage['edit_hide'] ?? false;
        $this->message = $rawMessage['message'];
        $this->fromScheduled = $rawMessage['from_scheduled'] ?? false;

        $this->entities = MessageEntity::fromRawEntities($rawMessage['entities'] ?? []);
        $this->viaBotId = $rawMessage['via_bot_id'] ??
            (isset($rawMessage['via_bot_name']) ? $this->getClient()->getId($rawMessage['via_bot_name']) : null);

        if (isset($rawMessage['fwd_from'])) {
            $fwdFrom = $rawMessage['fwd_from'];
            $this->fwdInfo = new ForwardedInfo(
                $fwdFrom['date'],
                isset($fwdFrom['from_id'])
                    ? $this->getClient()->getIdInternal($fwdFrom['from_id'])
                    : null,
                $fwdFrom['from_name'] ?? null,
                $fwdFrom['channel_post'] ?? null,
                $fwdFrom['post_author'] ?? null,
                isset($fwdFrom['saved_from_peer'])
                    ? $this->getClient()->getIdInternal($fwdFrom['saved_from_peer'])
                    : null,
                $fwdFrom['saved_from_msg_id'] ?? null
            );
            $this->psaType = $fwdFrom['psa_type'] ?? null;
            $this->imported = $fwdFrom['imported'];
        } else {
            $this->fwdInfo = null;
            $this->psaType = null;
            $this->imported = false;
        }

        $this->media = isset($rawMessage['media'])
            ? $API->wrapMedia($rawMessage['media'], $this->protected)
            : null;

        $this->keyboard = isset($rawMessage['reply_markup'])
            ? Keyboard::fromRawReplyMarkup($API, $this, $rawMessage['reply_markup'])
            : null;

        $this->poll = ($rawMessage['media']['_'] ?? '') === 'messageMediaPoll'
            ? AbstractPoll::fromRawPoll($rawMessage['media'])
            : null;

        $this->location = isset($rawMessage['media'])
            ? AbstractLocation::fromRawMedia($rawMessage['media'])
            : null;

        [$this->commandType, $this->command, $this->commandArgs] = CommandType::parse($this->message);

        foreach ($rawMessage['reactions']['results'] ?? [] as $r) {
            if (isset($r['chosen_order'])) {
                // Todo: live synchronization using a message database...
                $this->reactions []= $r['reaction']['emoticon'] ?? $r['reaction']['document_id'];
            }
        }
    }

    /**
     * Pin a message.
     *
     * @param bool $pmOneside Whether the message should only be pinned on the local side of a one-to-one chat
     * @param bool $silent    Pin the message silently, without triggering a notification
     */
    public function pin(bool $pmOneside = false, bool $silent = false): void
    {
        $this->getClient()->methodCallAsyncRead(
            'messages.updatePinnedMessage',
            [
                'peer' => $this->chatId,
                'id' => $this->id,
                'pm_oneside' => $pmOneside,
                'silent' => $silent,
                'unpin' => false,
            ]
        );
    }

    /**
     * Unpin a message.
     *
     * @param bool $pmOneside Whether the message should only be pinned on the local side of a one-to-one chat
     * @param bool $silent    Pin the message silently, without triggering a notification
     */
    public function unpin(bool $pmOneside = false, bool $silent = false): ?Update
    {
        $result = $this->getClient()->methodCallAsyncRead(
            'messages.updatePinnedMessage',
            [
                'peer' => $this->chatId,
                'id' => $this->id,
                'pm_oneside' => $pmOneside,
                'silent' => $silent,
                'unpin' => true,
            ]
        );
        return $this->getClient()->wrapUpdate($result);
    }

    /**
     * Get our reactions on the message.
     *
     * @return list<string|int>
     */
    public function getOurReactions(): array
    {
        return $this->reactions;
    }

    /**
     * Report a message in a chat for violation of telegramâ€™s Terms of Service.
     *
     * @param ReportReason $reason  Why are these messages being reported
     * @param string       $message Comment for report moderation
     */
    public function report(ReportReason $reason, string $message): bool
    {
        return $this->getClient()->methodCallAsyncRead(
            'messages.report',
            [
                'reason' => ['_' => $reason->value],
                'message' => $message,
                'id' => [$this->id],
                'peer' => $this->chatId,
            ]
        );
    }

    /**
     * Save message sender to your account contacts.
     *
     * @param string      $firstName                First name
     * @param string|null $lastName                 Last name
     * @param string|null $phoneNumber              Telegram ID of the other user
     * @param bool        $addPhonePrivacyException Allow the other user to see our phone number?
     */
    public function saveContact(
        string  $firstName,
        ?string $lastName = null,
        ?string $phoneNumber = null,
        bool    $addPhonePrivacyException = false
    ): void {
        $this->getClient()->methodCallAsyncRead(
            'contacts.addContact',
            [
                'first_name' => $firstName,
                'last_name' => $lastName,
                'phone_number' => $phoneNumber,
                'add_phone_privacy_exception' => $addPhonePrivacyException,
                'id' => $this->senderId,
            ]
        );
    }

    /**
     * Remove message sender from your account contacts.
     */
    public function removeContact(): void
    {
        $this->getClient()->methodCallAsyncRead(
            'contacts.deleteContacts',
            [
                'id' => [$this->senderId],
            ]
        );
    }

    /**
     * Invite message sender to requested channel.
     *
     * @param string|int $channel Username, Channel ID
     */
    public function inviteToChannel(string|int $channel): void
    {
        $this->getClient()->methodCallAsyncRead(
            'channels.inviteToChannel',
            [
                'channel' => $channel,
                'users' => [$this->senderId],
            ]
        );
    }

    /**
     * Add reaction to message.
     *
     * @param string|int $reaction    reaction
     * @param bool       $big         Whether a bigger and longer reaction should be shown
     * @param bool       $addToRecent Add this reaction to the recent reactions list.
     *
     * @return list<string|int>
     */
    public function addReaction(int|string $reaction, bool $big = false, bool $addToRecent = true): array
    {
        if (\in_array($reaction, $this->reactions, true)) {
            return $this->reactions;
        }
        $this->getClient()->methodCallAsyncRead(
            'messages.sendReaction',
            [
                'peer' => $this->chatId,
                'msg_id' => $this->id,
                'reaction' => \is_int($reaction)
                    ? [['_' => 'reactionCustomEmoji', 'document_id' => $reaction]]
                    : [['_' => 'reactionEmoji', 'emoticon' => $reaction]],
                'big' => $big,
                'add_to_recent' => $addToRecent,
            ]
        );
        $this->reactions[] = $reaction;
        return $this->reactions;
    }

    /**
     * Delete reaction from message.
     *
     * @param string|int $reaction string or int Reaction
     *
     * @return list<string|int>
     */
    public function delReaction(int|string $reaction): array
    {
        $key = array_search($reaction, $this->reactions, true);
        if ($key === false) {
            return $this->reactions;
        }
        unset($this->reactions[$key]);
        $this->reactions = array_values($this->reactions);
        $r = array_map(static fn (string|int $r): array => \is_int($r) ? ['_' => 'reactionCustomEmoji', 'document_id' => $r] : ['_' => 'reactionEmoji', 'emoticon' => $r], $this->reactions);
        $r[]= ['_' => 'reactionEmpty'];
        $this->getClient()->methodCallAsyncRead(
            'messages.sendReaction',
            [
                'peer' => $this->chatId,
                'msg_id' => $this->id,
                'reaction' =>  $r,
            ]
        );
        return $this->reactions;
    }

    /**
     * Translate text message(for media translate it caption).
     *
     * @param string $toLang Two-letter ISO 639-1 language code of the language to which the message is translated
     *
     */
    public function translate(
        string $toLang
    ): string {
        if (empty($message = $this->message)) {
            return $message;
        }
        $result = $this->getClient()->methodCallAsyncRead(
            'messages.translateText',
            [
                'peer' => $this->chatId,
                'id' => [$this->id],
                'to_lang' => $toLang,
            ]
        );
        return $result['result'][0]['text'];
    }

    /**
     * Edit message text.
     *
     * @param string     $message      New message
     * @param ParseMode  $parseMode    Whether to parse HTML or Markdown markup in the message
     * @param array|null $replyMarkup  Reply markup for inline keyboards
     * @param int|null   $scheduleDate Scheduled message date for scheduled messages
     * @param bool       $noWebpage    Disable webpage preview
     *
     */
    public function editText(
        string    $message,
        ParseMode $parseMode = ParseMode::TEXT,
        ?array    $replyMarkup = null,
        ?int      $scheduleDate = null,
        bool      $noWebpage = false
    ): Message {
        $result = $this->getClient()->methodCallAsyncRead(
            'messages.editMessage',
            [
                'peer' => $this->chatId,
                'id' => $this->id,
                'message' => $message,
                'reply_markup' => $replyMarkup,
                'parse_mode' => $parseMode,
                'schedule_date' => $scheduleDate,
                'no_webpage' => $noWebpage,
            ]
        );
        return $this->getClient()->wrapMessage($this->getClient()->extractMessage($result));
    }

    /**
     * Edit message keyboard.
     *
     * @param array $replyMarkup Reply markup for inline keyboards
     */
    public function editReplyMarkup(array $replyMarkup): Message
    {
        $result = $this->getClient()->methodCallAsyncRead(
            'messages.editMessage',
            [
                'peer' => $this->chatId,
                'id' => $this->id,
                'reply_markup' => $replyMarkup,
            ],
        );
        return $this->getClient()->wrapMessage($this->getClient()->extractMessage($result));
    }

    /**
     * If the message is outgoing, will edit the message's text, otherwise will reply to the message.
     *
     * @param string     $message      New message
     * @param ParseMode  $parseMode    Whether to parse HTML or Markdown markup in the message
     * @param array|null $replyMarkup  Reply markup for inline keyboards
     * @param int|null   $scheduleDate Scheduled message date for scheduled messages
     * @param bool       $noWebpage    Disable webpage preview
     *
     * @psalm-mutation-free
     */
    public function replyOrEdit(
        string    $message,
        ParseMode $parseMode = ParseMode::TEXT,
        ?array    $replyMarkup = null,
        ?int      $scheduleDate = null,
        bool      $noWebpage = false,
    ): Message {
        $method = $this->out ? 'editText' : 'reply';
        return $this->$method(
            message: $message,
            parseMode: $parseMode,
            replyMarkup: $replyMarkup,
            scheduleDate: $scheduleDate,
            noWebpage: $noWebpage,
        );
    }

    /**
     * Forwards messages by their IDs.
     *
     * @param integer|string $peer Destination peer
     * @param list<int> $id IDs of messages
     * @param bool $dropAuthor Whether to forward messages without quoting the original author
     * @param bool $dropCaption Whether to strip captions from media
     * @param int $topicId Destination [forum topic](https://core.telegram.org/api/forum#forum-topics)
     * @param boolean $silent Whether to send the message silently, without triggering notifications.
     * @param boolean $noForwards Only for bots, disallows further re-forwarding and saving of the messages, even if the destination chat doesnâ€™t have [content protection](https://telegram.org/blog/protected-content-delete-by-date-and-more) enabled
     * @param boolean $background Send this message as background message
     * @param boolean $score When forwarding games, whether to include your score in the game
     * @param integer|null $scheduleDate Schedule date.
     * @param integer|string|null $sendAs Peer to send the message as.
     *
     * @return non-empty-list<Message>
     */
    public function forward(
        int|string $peer,
        array $id = [],
        bool $dropAuthor = false,
        bool $dropCaption = false,
        int $topicId = 1,
        bool $silent = false,
        bool $noForwards = false,
        bool $background = false,
        bool $score = false,
        ?int $scheduleDate = null,
        int|string|null $sendAs = null,
    ): array {
        Assert::false($this->protected);
        $result = $this->getClient()->methodCallAsyncRead(
            'messages.forwardMessages',
            [
                'from_peer' => $this->chatId,
                'to_peer' => $peer,
                'id' => $id = empty($id) ? [$this->id] : $id,
                'silent' => $silent,
                'send_as' => $sendAs,
                'top_msg_id' => $topicId,
                'background' => $background,
                'noforwards' => $noForwards,
                'with_my_score' => $score,
                'schedule_date' => $scheduleDate,
                'drop_author' => $dropAuthor,
                'drop_media_captions' => $dropCaption,
            ]
        );
        $result = array_map($this->getClient()->wrapUpdate(...), $this->getClient()->extractUpdates($result));
        return array_values(array_filter($result));
    }

    /**
     * Get the call currently associated with this chat, if any.
     *
     * For private and secret chats this is the pending or running one-to-one call with the other user;
     * for groups and channels, the currently active video chat or livestream.
     *
     * @psalm-impure
     */
    abstract public function getCall(): ?Call;

    /**
     * Whether {@see self::requestCall()} will succeed in this chat, i.e. there is already a call we can
     * return, or our permissions (and the other party's privacy settings) allow us to start a new one.
     *
     * @psalm-impure
     */
    abstract public function canRequestCall(): bool;

    /**
     * Returns why we can't start a new call in this chat, or null if we can.
     *
     * Unlike {@see self::canRequestCall()}, this does not take into account an already existing call.
     *
     * @psalm-impure
     */
    abstract public function getCallDenialReason(): ?CallDenialReason;

    /**
     * Get the call currently associated with this chat, or start a new one if there is none.
     *
     * For private and secret chats this calls the other user;
     * for groups and channels, this creates a video chat or livestream (use `join()` on the returned call to join it:
     * `requestCall()->join()` works in every chat, as joining an outgoing one-to-one call does nothing).
     *
     * @throws CallNotAllowedException If there is no active call, and our permissions do not allow us to start one.
     *
     * @psalm-impure
     */
    abstract public function requestCall(): Call;

    protected readonly string $html;
    protected readonly string $htmlTelegram;

    /**
     * Get an HTML version of the message.
     *
     * @psalm-suppress InaccessibleProperty
     *
     * @param bool $allowTelegramTags Whether to allow telegram-specific tags like tg-spoiler, tg-emoji, mention links and so on...
     */
    public function getHTML(bool $allowTelegramTags = false): string
    {
        if (!$this->entities) {
            return StrTools::htmlEscape($this->message);
        }
        if ($allowTelegramTags) {
            return $this->htmlTelegram ??= StrTools::entitiesToHtml($this->message, $this->entities, $allowTelegramTags);
        }
        return $this->html ??= StrTools::entitiesToHtml($this->message, $this->entities, $allowTelegramTags);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Topic;

use JsonSerializable;

/**
 * Specifies the color of the fallback topic icon (RGB) if no custom emoji icon is specified.
 */
enum IconColor: int implements JsonSerializable
{
    case NONE = 0;
    case BLUE = 0x6fb9f0;
    case YELLOW = 0xffd67e;
    case PURPLE = 0xcb86db;
    case GREEN = 0x8eee98;
    case PINK = 0xff93b2;
    case RED = 0xfb6f5f;

    /** @internal */
    #[\Override]
    public function jsonSerialize(): string
    {
        return $this->name;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Media;

use danog\MadelineProto\EventHandler\Media;
use danog\MadelineProto\MTProto;

/**
 * Represents a generic sticker.
 */
abstract class AbstractSticker extends Media
{
    /** @var string Emoji representation of sticker */
    public readonly string $emoji;

    /** @var array Associated stickerset */
    public readonly array $stickerset;

    /** @internal */
    public function __construct(
        MTProto $API,
        array $rawMedia,
        array $stickerAttribute,
        public readonly ?int $width,
        public readonly ?int $height,
        bool $protected,
    ) {
        parent::__construct($API, $rawMedia, $protected);
        $this->emoji = $stickerAttribute['alt'];
        $this->stickerset = $stickerAttribute['stickerset'];
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Media;

use danog\MadelineProto\MTProto;

/**
 * Represents a video sticker.
 */
final class VideoSticker extends Sticker
{
    public readonly float $duration;

    /** @internal */
    public function __construct(
        MTProto $API,
        array $rawMedia,
        array $stickerAttribute,
        array $extraAttribute,
        bool $protected,
    ) {
        parent::__construct($API, $rawMedia, $stickerAttribute, $extraAttribute['w'], $extraAttribute['h'], $protected);
        $this->duration = $extraAttribute['duration'] ?? 0.0;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Media;

use danog\MadelineProto\EventHandler\Media;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\Tools;

/**
 * Represents a photo.
 */
final class Photo extends Media
{
    /** @var bool If true; the current media has attached mask stickers. */
    public readonly bool $hasStickers;

    /** Photo width */
    public readonly int $width;

    /** Photo height */
    public readonly int $height;

    /** @internal */
    public function __construct(
        MTProto $API,
        array   $rawMedia,
        bool    $protected,
    ) {
        parent::__construct($API, $rawMedia, $protected);
        $this->hasStickers = $rawMedia['photo']['has_stickers'] ?? false;
        $size = isset($rawMedia['photo']) ? Tools::maxSize($rawMedia['photo']['sizes']) : $rawMedia;
        $this->width = $size['w'] ?? 0;
        $this->height = $size['h'] ?? 0;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Media;

use danog\MadelineProto\EventHandler\AbstractStory;
use danog\MadelineProto\EventHandler\Story\Story;
use danog\MadelineProto\EventHandler\Story\StoryDeleted;
use danog\MadelineProto\Ipc\IpcCapable;
use danog\MadelineProto\MTProto;
use JsonSerializable;
use ReflectionClass;
use ReflectionProperty;

/**
 * Represents a forwarded story.
 */
abstract class MediaStory extends IpcCapable implements JsonSerializable // for now. I should think a way
{
    public readonly bool $viaMention;

    public readonly int $senderId;

    public readonly int $storyId;

    protected readonly ?AbstractStory $story;

    /** @internal */
    public function __construct(
        MTProto $API,
        array $rawMedia,
    ) {
        parent::__construct($API);
        $this->viaMention = $rawMedia['via_mention'];
        $this->senderId = $API->getIdInternal($rawMedia['peer']);
        $this->storyId = $rawMedia['id'];
        $this->story = match ($rawMedia['story']['_'] ?? null) {
            'storyItem' => new Story($API, [
                'peer' => $this->senderId,
                'story' => $rawMedia['story'],
            ]),
            'storyItemDeleted' => new StoryDeleted($API, [
                'peer' => $this->senderId,
                'story' => $rawMedia['story'],
            ]),
            'storyItemSkipped' => null, // Will it happen?
            default => null
        };
    }

    /**
     * Get story.
     *
     * @psalm-suppress InaccessibleProperty
     */
    public function getStory(): ?AbstractStory
    {
        $client = $this->getClient();
        $result = $client->methodCallAsyncRead(
            'stories.getStoriesByID',
            [
                'peer' => $this->senderId,
                'id' => [ $this->storyId ],
            ]
        )['stories'][0] ?? false;
        if ($result) {
            $arr = [ 'peer' => $this->senderId, 'story' => $result ];
            return $this->story ??= $result['_'] === 'storyItem' // I hope storyItemSkipped never happen
                ? new StoryDeleted($client, $arr)
                : new Story($client, $arr);
        }
        return null;
    }

    /** @internal */
    #[\Override]
    public function jsonSerialize(): mixed
    {
        $res = ['_' => static::class];
        $refl = new ReflectionClass($this);
        foreach ($refl->getProperties(ReflectionProperty::IS_PUBLIC) as $prop) {
            $res[$prop->getName()] = $prop->getValue($this);
        }
        return $res;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Media;

use danog\MadelineProto\MTProto;

/**
 * Represents a custom emoji sticker.
 */
final class CustomEmoji extends AbstractSticker
{
    /** @var bool Whether this custom emoji can be sent by non-Premium users */
    public readonly bool $free;

    /** @var bool Whether the color of this TGS custom emoji should be changed to the text color when used in messages, the accent color if used as emoji status, white on chat photos, or another appropriate color based on context. */
    public readonly bool $textColor;

    /** @internal */
    public function __construct(
        MTProto $API,
        array $rawMedia,
        array $stickerAttribute,
        array $photoAttribute,
        bool $protected,
    ) {
        parent::__construct(
            $API,
            $rawMedia,
            $stickerAttribute,
            $photoAttribute['w'],
            $photoAttribute['h'],
            $protected
        );
        $this->free = $stickerAttribute['free'];
        $this->textColor = $stickerAttribute['text_color'];
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Media;

/**
 * Represents a round video.
 */
final class RoundVideo extends AbstractVideo
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Media;

use danog\MadelineProto\MTProto;

/**
 * Represents a mask sticker.
 */
final class MaskSticker extends AbstractSticker
{
    /**
     * Part of the face, relative to which the mask should be placed.
     */
    public readonly MaskPosition $position;
    /**
     * Shift by X-axis measured in widths of the mask scaled to the face size, from left to right.
     *
     * For example, -1.0 will place the mask just to the left of the default mask position.
     */
    public readonly float $x;
    /**
     * Shift by Y-axis measured in widths of the mask scaled to the face size, from left to right.
     *
     * For example, -1.0 will place the mask just below the default mask position.
     */
    public readonly float $y;
    /**
     * Mask scaling coefficient.
     *
     * For example, 2.0 means a doubled size.
     */
    public readonly float $zoom;

    /** @internal */
    public function __construct(
        MTProto $API,
        array $rawMedia,
        array $stickerAttribute,
        array $photoAttribute,
        bool $protected,
    ) {
        parent::__construct($API, $rawMedia, $stickerAttribute, $photoAttribute['w'], $photoAttribute['h'], $protected);
        $coords = $stickerAttribute['mask_coords'];
        $this->position = match ($coords['n']) {
            0 => MaskPosition::Forehead,
            1 => MaskPosition::Eyes,
            2 => MaskPosition::Mouth,
            3 => MaskPosition::Chin
        };
        $this->x = $coords['x'];
        $this->y = $coords['y'];
        $this->zoom = $coords['zoom'];
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Mahdi <mahdi.talaee1379@gmail.com>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Media;

use AssertionError;
use danog\MadelineProto\Exception;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\Tools;

/**
 * Represents a thumbnail inlined in a media (like the stripped preview of a photo, or the thumbnail of a secret chat media).
 *
 * It behaves like any other thumbnail, but it's downloaded from memory, without contacting Telegram:
 * its file ID carries the thumbnail itself, authenticated by the session that generated it: it's accepted by
 * every method of that session (not by the bot API, nor by other sessions).
 */
final class InlineThumbnail extends AbstractThumbnail
{
    /** @internal The type of the thumbnails of secret chat media, as in tdlib. */
    public const SECRET_CHAT_TYPE = 't';

    private const FILE_ID_PREFIX = 'inline:';
    private const FLAG_STRIPPED = 1;
    private const MAC_LENGTH = 16;
    /** Thumbnail types are letters: see https://core.telegram.org/api/files#image-thumbnail-types */
    private const TYPE_REGEX = '/^[a-zA-Z]$/D';

    /** @internal The thumbnail itself */
    protected readonly string $contents;

    /** @internal What the file ID carries (stripped thumbnails are inflated when downloaded) */
    private readonly string $payload;

    /** @internal */
    public function __construct(
        MTProto $API,
        array $rawMedia,
        array $rawSize,
        bool $protected,
    ) {
        if (!preg_match(self::TYPE_REGEX, $rawSize['type'])) {
            throw new AssertionError('Invalid thumbnail type: '.json_encode($rawSize['type']));
        }
        $bytes = (string) $rawSize['bytes'];
        $stripped = $rawSize['_'] === 'photoStrippedSize';
        $this->contents = $stripped ? Tools::inflateStripped($bytes) : $bytes;
        $this->payload = \chr($stripped ? self::FLAG_STRIPPED : 0).$rawSize['type'].$bytes;
        if ($stripped && \strlen($bytes) >= 3 && \ord($bytes[0]) === 1) {
            // In the header of stripped thumbnails, as read by Tools::inflateStripped().
            $rawSize['w'] = \ord($bytes[2]);
            $rawSize['h'] = \ord($bytes[1]);
        } elseif (!($rawSize['w'] ?? 0) || !($rawSize['h'] ?? 0)) {
            // From the header of the image, when the function is available (it may be disabled).
            try {
                $size = \function_exists('getimagesizefromstring') ? getimagesizefromstring($this->contents) : false;
            } catch (\Throwable) {
                // Not an image it can read
                $size = false;
            }
            [$rawSize['w'], $rawSize['h']] = $size ?: [0, 0];
        }
        parent::__construct($API, $rawMedia, $rawSize, $protected);
    }

    #[\Override]
    protected function extractFormat(array $rawMedia): array
    {
        return self::getFormat($this->contents);
    }

    #[\Override]
    protected function extractDownloadInfo(MTProto $API, array $rawMedia): array
    {
        $id = ($rawMedia['photo'] ?? $rawMedia['document'] ?? $rawMedia['file'] ?? [])['id'] ?? null;
        [$ext, $mime] = $this->extractFormat($rawMedia);
        return [
            'name' => ($id === null ? self::hash($this->contents) : Tools::unpackSignedLongString($id)).'_'.$this->type,
            'ext' => $ext,
            'mime' => $mime,
            'size' => \strlen($this->contents),
            'InputFileLocation' => [],
        ];
    }

    #[\Override]
    protected function extractBotApiFile(MTProto $API, array $rawMedia): array
    {
        return [
            'file_id' => self::FILE_ID_PREFIX.Tools::base64urlEncode(self::mac($this->payload, $API->getInlineFileIdKey()).$this->payload),
            'file_unique_id' => self::FILE_ID_PREFIX.self::hash($this->contents),
        ];
    }

    #[\Override]
    public function getDownloadInfo(): array
    {
        $result = parent::getDownloadInfo();
        $result['data'] = $this->contents;
        return $result;
    }

    /**
     * Whether this is the file ID of an inline thumbnail.
     *
     * @internal
     */
    public static function isFileId(string $fileId): bool
    {
        return str_starts_with($fileId, self::FILE_ID_PREFIX);
    }

    /**
     * Unpack the file ID of an inline thumbnail into its download info.
     *
     * Returns null if it's not the file ID of an inline thumbnail, throws if it wasn't generated by the session
     * whose key is passed.
     *
     * @internal
     *
     * @return ?array{name: string, ext: string, mime: string, size: int, InputFileLocation: array, data: string}
     */
    public static function unpackFileId(string $fileId, string $key): ?array
    {
        if (!self::isFileId($fileId)) {
            return null;
        }
        $data = Tools::base64urlDecode(substr($fileId, \strlen(self::FILE_ID_PREFIX)));
        $payload = substr($data, self::MAC_LENGTH);
        if (\strlen($payload) < 2
            || !hash_equals(self::mac($payload, $key), substr($data, 0, self::MAC_LENGTH))
        ) {
            throw new Exception('This file ID of an inline thumbnail was not generated by this session');
        }
        $type = $payload[1];
        if (!preg_match(self::TYPE_REGEX, $type)) {
            throw new Exception('Invalid file ID of an inline thumbnail');
        }
        $contents = substr($payload, 2);
        if (\ord($payload[0]) & self::FLAG_STRIPPED) {
            $contents = Tools::inflateStripped($contents);
        }
        [$ext, $mime] = self::getFormat($contents);
        return [
            'name' => self::hash($contents).'_'.$type,
            'ext' => $ext,
            'mime' => $mime,
            'size' => \strlen($contents),
            'InputFileLocation' => [],
            'data' => $contents,
        ];
    }

    /**
     * Get the unique file ID of the file ID of an inline thumbnail (null if it's not one).
     *
     * @internal
     */
    public static function getUniqueFileId(string $fileId, string $key): ?string
    {
        $info = self::unpackFileId($fileId, $key);
        return $info === null ? null : self::FILE_ID_PREFIX.self::hash($info['data']);
    }

    private static function mac(string $payload, string $key): string
    {
        return substr(hash_hmac('sha256', $payload, $key, true), 0, self::MAC_LENGTH);
    }

    private static function hash(string $contents): string
    {
        return bin2hex(substr(hash('sha256', $contents, true), 0, 16));
    }

    /** @return array{string, string} */
    private static function getFormat(string $contents): array
    {
        return substr($contents, 0, 4) === 'RIFF' && substr($contents, 8, 4) === 'WEBP'
            ? ['.webp', 'image/webp']
            : ['.jpg', 'image/jpeg'];
    }

    /** @internal */
    #[\Override]
    public function jsonSerialize(): mixed
    {
        $v = parent::jsonSerialize();
        unset($v['contents']);
        return $v;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Media;

use danog\MadelineProto\MTProto;

/**
 * Represents a voice message.
 */
final class Voice extends AbstractAudio
{
    /**
     * 100 values from 0 to 31, representing a waveform.
     *
     * @var list<int<0, 31>>|null
     */
    public readonly ?array $waveform;

    /** @internal */
    public function __construct(
        MTProto $API,
        array $rawMedia,
        array $audioAttribute,
        bool $protected,
    ) {
        parent::__construct($API, $rawMedia, $audioAttribute, $protected);
        $this->waveform = $audioAttribute['waveform'] ?? null;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Media;

use danog\MadelineProto\EventHandler\Media;

use danog\MadelineProto\MTProto;

/**
 * Represents a document.
 */
final class Document extends Media
{
    /** @internal */
    public function __construct(
        MTProto $API,
        array $rawMedia,
        bool $protected
    ) {
        parent::__construct($API, $rawMedia, $protected);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Mahdi <mahdi.talaee1379@gmail.com>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Media;

use danog\MadelineProto\MTProto;

/**
 * Represents a downloadable animated thumbnail (MPEG4 video) of a media or photo.
 */
final class VideoThumbnail extends AbstractThumbnail
{
    /** Timestamp in seconds of the frame of the video that should be used as static preview. */
    public readonly ?float $startTimestamp;

    /** @internal */
    public function __construct(
        MTProto $API,
        array $rawMedia,
        array $rawSize,
        bool $protected,
    ) {
        $this->startTimestamp = $rawSize['video_start_ts'] ?? null;
        parent::__construct($API, $rawMedia, $rawSize, $protected);
    }

    #[\Override]
    protected function extractFormat(array $rawMedia): array
    {
        return ['.mp4', 'video/mp4'];
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Media;

use danog\MadelineProto\EventHandler\Media;
use danog\MadelineProto\MTProto;

/**
 * Represents a generic video.
 */
abstract class AbstractVideo extends Media
{
    /** Video duration in seconds */
    public readonly float $duration;

    /** Whether the video supports streaming */
    public readonly bool $supportsStreaming;

    /** Video width */
    public readonly int $width;

    /** Video height */
    public readonly int $height;

    /** @internal */
    public function __construct(
        MTProto $API,
        array $rawMedia,
        array $attribute,
        bool $protected,
    ) {
        parent::__construct($API, $rawMedia, $protected);
        $this->duration = $attribute['duration'] ?? 0.0;
        $this->supportsStreaming = $attribute['supports_streaming'] ?? false;
        $this->width = $attribute['w'] ?? 0;
        $this->height = $attribute['h'] ?? 0;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Media;

use danog\MadelineProto\MTProto;

/**
 * Represents a static sticker.
 */
final class StaticSticker extends Sticker
{
    /** @internal */
    public function __construct(
        MTProto $API,
        array $rawMedia,
        array $stickerAttribute,
        array $photoAttribute,
        bool $protected,
    ) {
        parent::__construct(
            $API,
            $rawMedia,
            $stickerAttribute,
            $photoAttribute['w'],
            $photoAttribute['h'],
            $protected
        );
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Media;

use JsonSerializable;

/** Position of the mask */
enum MaskPosition: int implements JsonSerializable
{
    case Forehead = 0;
    case Eyes = 1;
    case Mouth = 2;
    case Chin = 3;

    /** @internal */
    #[\Override]
    public function jsonSerialize(): mixed
    {
        return $this->value;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Media;

use danog\MadelineProto\EventHandler\Media;
use danog\MadelineProto\MTProto;

/**
 * Represents a photo uploaded as a document.
 */
final class DocumentPhoto extends Media
{
    /** @var bool If true; the current media has attached mask stickers. */
    public readonly bool $hasStickers;

    public readonly int $width;
    public readonly int $height;

    /** @internal */
    public function __construct(
        MTProto $API,
        array $rawMedia,
        array $attribute,
        bool $protected,
    ) {
        parent::__construct($API, $rawMedia, $protected);
        $this->width = $attribute['w'];
        $this->height = $attribute['h'];
        $hasStickers = false;
        foreach ($rawMedia['document']['attributes'] as ['_' => $t]) {
            if ($t === 'documentAttributeHasStickers') {
                $hasStickers = true;
                break;
            }
        }
        $this->hasStickers = $hasStickers;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Media;

use danog\MadelineProto\MTProto;

/**
 * Represents a video.
 */
final class Video extends AbstractVideo
{
    /** If true; the current media has attached mask stickers. */
    public readonly bool $hasStickers;

    /** @internal */
    public function __construct(
        MTProto $API,
        array $rawMedia,
        array $attribute,
        bool $protected,
    ) {
        parent::__construct($API, $rawMedia, $attribute, $protected);
        $hasStickers = false;
        foreach ($rawMedia['document']['attributes'] ?? [] as ['_' => $t]) {
            if ($t === 'documentAttributeHasStickers') {
                $hasStickers = true;
                break;
            }
        }
        $this->hasStickers = $hasStickers;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Mahdi <mahdi.talaee1379@gmail.com>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Media;

use danog\Decoder\FileId;
use danog\Decoder\FileIdType;
use danog\Decoder\PhotoSizeSource\PhotoSizeSourceThumbnail;
use danog\MadelineProto\EventHandler\Media;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\Tools;

/**
 * Represents a thumbnail of a media, or a smaller size of a photo.
 */
abstract class AbstractThumbnail extends Media
{
    /** Thumbnail type, see https://core.telegram.org/api/files#image-thumbnail-types */
    public readonly string $type;

    /** Thumbnail width */
    public readonly int $width;

    /** Thumbnail height */
    public readonly int $height;

    /** @internal */
    public function __construct(
        MTProto $API,
        array $rawMedia,
        private readonly array $rawSize,
        bool $protected,
    ) {
        $this->type = $rawSize['type'];
        $this->width = $rawSize['w'];
        $this->height = $rawSize['h'];
        parent::__construct($API, $rawMedia, $protected);
    }

    /**
     * File extension and MIME type of the thumbnail.
     *
     * @internal
     *
     * @return array{string, string}
     */
    abstract protected function extractFormat(array $rawMedia): array;

    #[\Override]
    protected function extractDownloadInfo(MTProto $API, array $rawMedia): array
    {
        $info = $API->getDownloadInfo($rawMedia);
        $info['InputFileLocation']['thumb_size'] = $this->type;
        $info['size'] = $this->rawSize['size'] ?? max($this->rawSize['sizes']);
        $info['name'] = Tools::unpackSignedLongString(($rawMedia['photo'] ?? $rawMedia['document'])['id']).'_'.$this->type;
        [$info['ext'], $info['mime']] = $this->extractFormat($rawMedia);
        return $info;
    }

    #[\Override]
    protected function extractBotApiFile(MTProto $API, array $rawMedia): array
    {
        $media = $rawMedia['photo'] ?? $rawMedia['document'];
        $type = isset($rawMedia['photo']) ? FileIdType::PHOTO : FileIdType::THUMBNAIL;
        $fileId = new FileId(
            id: $media['id'],
            type: $type,
            accessHash: $media['access_hash'],
            fileReference: isset($media['file_reference']) ? (string) $media['file_reference'] : null,
            dcId: $media['dc_id'],
            photoSizeSource: new PhotoSizeSourceThumbnail(
                thumbType: $this->type,
                thumbFileType: $type,
            ),
        );
        return [
            'file_id' => (string) $fileId,
            'file_unique_id' => $fileId->getUniqueBotAPI(),
        ];
    }

    #[\Override]
    protected function extractThumbs(MTProto $API, array $rawMedia): array
    {
        return [[], []];
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Media;

use danog\MadelineProto\MTProto;

/**
 * Represents a GIF (or an MPEG4 file without sound).
 */
final class Gif extends AbstractVideo
{
    /** @var bool If true; the current media has attached mask stickers. */
    public readonly bool $hasStickers;

    /** @internal */
    public function __construct(
        MTProto $API,
        array $rawMedia,
        array $attribute,
        bool $protected,
    ) {
        parent::__construct($API, $rawMedia, $attribute, $protected);
        $hasStickers = false;
        foreach ($rawMedia['document']['attributes'] as ['_' => $t]) {
            if ($t === 'documentAttributeHasStickers') {
                $hasStickers = true;
                break;
            }
        }
        $this->hasStickers = $hasStickers;
    }

    /**
     * Add GIF to saved gifs list.
     *
     */
    public function save(): bool
    {
        return $this->getClient()->methodCallAsyncRead(
            'messages.saveGif',
            [
                'id' => $this->botApiFileId,
                'unsave' => false,
            ]
        );
    }

    /**
     * Remove GIF from saved gifs list.
     *
     */
    public function unsave(): bool
    {
        return $this->getClient()->methodCallAsyncRead(
            'messages.saveGif',
            [
                'id' => $this->botApiFileId,
                'unsave' => true,
            ]
        );
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Media;

use JsonSerializable;

/** Represents a geo point */
final class GeoPoint implements JsonSerializable
{
    /** Longitude */
    public readonly float $long;

    /** Latitude */
    public readonly float $lat;

    /** Access hash (null for geo points received in secret chats) */
    public readonly ?int $accessHash;

    /** The estimated horizontal accuracy of the location, in meters; as defined by the sender. */
    public readonly ?int $accuracyRadius;

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(array $rawGeoPoint)
    {
        $this->long = $rawGeoPoint['long'];
        $this->lat = $rawGeoPoint['lat'];
        $this->accessHash = $rawGeoPoint['access_hash'] ?? null;
        $this->accuracyRadius = $rawGeoPoint['accuracy_radius'] ?? null;
    }

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    #[\Override]
    public function jsonSerialize(): mixed
    {
        $v = get_object_vars($this);
        $v['_'] = static::class;
        return $v;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Media;

use danog\MadelineProto\MTProto;

/**
 * Represents a sticker.
 */
abstract class Sticker extends AbstractSticker
{
    /** Whether this is a premium sticker and a premium sticker animation must be played. */
    public readonly bool $premiumSticker;

    /** @internal */
    public function __construct(
        MTProto $API,
        array $rawMedia,
        array $stickerAttribute,
        ?int $width,
        ?int $height,
        bool $protected,
    ) {
        parent::__construct($API, $rawMedia, $stickerAttribute, $width, $height, $protected);
        $this->premiumSticker = !($rawMedia['nopremium'] ?? true);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Media;

use danog\MadelineProto\MTProto;

/**
 * Represents an animated sticker.
 */
final class AnimatedSticker extends Sticker
{
    /** @internal */
    public function __construct(
        MTProto $API,
        array $rawMedia,
        array $stickerAttribute,
        array $photoAttribute,
        bool $protected,
    ) {
        parent::__construct(
            $API,
            $rawMedia,
            $stickerAttribute,
            $photoAttribute['w'],
            $photoAttribute['h'],
            $protected
        );
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Mahdi <mahdi.talaee1379@gmail.com>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Media;

/**
 * Represents a downloadable thumbnail of a media (JPEG, or WEBP for stickers), or a smaller size of a photo.
 */
final class Thumbnail extends AbstractThumbnail
{
    #[\Override]
    protected function extractFormat(array $rawMedia): array
    {
        if (!isset($rawMedia['document'])) {
            return ['.jpg', 'image/jpeg'];
        }
        // Like tdlib: the server replaces sticker thumbnails with WEBP ones,
        // except for old or big stickers, recognizable by their stripped or progressive thumbnails.
        $document = $rawMedia['document'];
        $sticker = $document['mime_type'] === 'application/x-tgsticker';
        foreach ($document['attributes'] as ['_' => $t]) {
            $sticker = $sticker || $t === 'documentAttributeSticker';
        }
        foreach ($document['thumbs'] ?? [] as ['_' => $t]) {
            if ($t === 'photoStrippedSize' || $t === 'photoSizeProgressive') {
                $sticker = false;
            }
        }
        return $sticker ? ['.webp', 'image/webp'] : ['.jpg', 'image/jpeg'];
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Media;

use danog\MadelineProto\EventHandler\Media;
use danog\MadelineProto\MTProto;

/**
 * Represents a generic audio file.
 */
abstract class AbstractAudio extends Media
{
    /** Audio duration in seconds */
    public readonly int $duration;

    /** @internal */
    public function __construct(
        MTProto $API,
        array $rawMedia,
        array $audioAttribute,
        bool $protected,
    ) {
        parent::__construct($API, $rawMedia, $protected);
        $this->duration = $audioAttribute['duration'] ?? 0;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Media;

use danog\MadelineProto\MTProto;

/**
 * Represents an audio file.
 */
final class Audio extends AbstractAudio
{
    /** @var ?string Song name */
    public readonly ?string $title;

    /** @var ?string Performer */
    public readonly ?string $performer;

    /** @internal */
    public function __construct(
        MTProto $API,
        array $rawMedia,
        array $audioAttribute,
        bool $protected,
    ) {
        parent::__construct($API, $rawMedia, $audioAttribute, $protected);
        $this->title = $audioAttribute['title'] ?? null;
        $this->performer = $audioAttribute['performer'] ?? null;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Mahdi <mahdi.talaee1379@gmail.com>
 * @copyright 2016-2025 Mahdi <mahdi.talaee1379@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler;

use danog\MadelineProto\CallNotAllowedException;
use danog\MadelineProto\EventHandler\Calls\CallDenialReason;
use danog\MadelineProto\EventHandler\Calls\PrivateCall;
use danog\MadelineProto\EventHandler\Message\Service\DialogScreenshotTaken;
use danog\MadelineProto\MTProto;

/**
 * Represents a private or secret chat message.
 */
abstract class AbstractPrivateMessage extends Message
{
    /** @internal */
    public function __construct(MTProto $API, array $rawMessage, array $info, bool $scheduled)
    {
        parent::__construct($API, $rawMessage, $info, $scheduled);
    }

    /**
     * Notify the other user in a private chat that a screenshot of the chat was taken.
     *
     * @psalm-impure
     */
    abstract public function screenShot(): DialogScreenshotTaken;

    /**
     * ID of the other user in this private or secret chat.
     *
     * @psalm-mutation-free
     */
    abstract protected function getCallPeer(): int;

    /**
     * Get the pending or running one-to-one call with the other user of this chat, if any.
     *
     * @psalm-external-mutation-free
     */
    #[\Override]
    public function getCall(): ?PrivateCall
    {
        return $this->getClient()->getCallByPeer($this->getCallPeer());
    }

    /**
     * Whether {@see self::requestCall()} will succeed, i.e. there is already a call with the other user,
     * or we can call them.
     *
     * @param bool $video Whether to check if a video call can be started.
     */
    #[\Override]
    public function canRequestCall(bool $video = false): bool
    {
        return $this->getCall() !== null || $this->getCallDenialReason($video) === null;
    }

    /**
     * Get the pending or running one-to-one call with the other user of this chat, or call them if there is none.
     *
     * @param bool $video Whether to start a video call.
     *
     * @throws CallNotAllowedException If the other user cannot be called.
     */
    #[\Override]
    public function requestCall(bool $video = false): PrivateCall
    {
        $call = $this->getCall();
        if ($call !== null) {
            return $call;
        }
        $reason = $this->getCallDenialReason($video);
        if ($reason !== null) {
            throw new CallNotAllowedException($reason);
        }
        return $this->getClient()->requestCall($this->getCallPeer(), $video);
    }

    /**
     * Returns why we can't call the other user, or null if we can.
     *
     * @param bool $video Whether to check if a video call can be started.
     */
    #[\Override]
    public function getCallDenialReason(bool $video = false): ?CallDenialReason
    {
        $client = $this->getClient();
        if ($client->isSelfBot()) {
            return CallDenialReason::BOTS_CANNOT_CALL;
        }
        $peer = $this->getCallPeer();
        $self = $client->getSelf();
        if ($self !== false && $peer === $self['id']) {
            return CallDenialReason::CANNOT_CALL_SELF;
        }
        $info = $client->getFullInfo($peer);
        /** @var array{phone_calls_available?: bool, phone_calls_private?: bool, video_calls_available?: bool} */
        $full = $info['full'] ?? [];
        if (!($full['phone_calls_available'] ?? false)) {
            return CallDenialReason::CALLS_UNAVAILABLE;
        }
        if ($full['phone_calls_private'] ?? false) {
            return CallDenialReason::PRIVACY_SETTINGS;
        }
        if ($video && !($full['video_calls_available'] ?? false)) {
            return CallDenialReason::VIDEO_CALLS_UNAVAILABLE;
        }
        return null;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler;

use danog\MadelineProto\EventHandler\Media\Document;
use danog\MadelineProto\EventHandler\Media\Photo;
use danog\MadelineProto\MTProto;
use JsonSerializable;
use ReflectionClass;
use ReflectionProperty;

/**
 * Represents information about a [named bot web app](https://core.telegram.org/api/bots/webapps#named-bot-web-apps).
 */
final class BotApp implements JsonSerializable
{
    /** App ID */
    public readonly int $id;

    /** Access hash*/
    public readonly int $accessHash;

    /** Bot web app short name, used to generate [named bot web app deep links](https://core.telegram.org/api/links#named-bot-web-app-links). */
    public readonly string $name;

    /** Bot web app title. */
    public readonly string $title;

    /** Bot web app description. */
    public readonly string $description;

    /** Bot web app photo. */
    public readonly ?Photo $photo;

    /** Bot web app animation. */
    public readonly ?Document $document;

    /** Hash to pass to [messages.getBotApp](https://docs.madelineproto.xyz/API_docs/methods/messages.getBotApp.html), to avoid refetching bot app info if it hasnâ€™t changed. */
    public readonly int $hash;

    /** @internal */
    public function __construct(
        MTProto $API,
        array $rawBotApp,

        /** Whether the web app was never used by the user, and confirmation must be asked from the user before opening it. */
        public readonly ?bool $inactive = null,

        /** The bot is asking permission to send messages to the user: if the user agrees, set the write_allowed flag when invoking [messages.requestAppWebView](https://docs.madelineproto.xyz/API_docs/methods/messages.requestAppWebView.html). */
        public readonly ?bool $requestWriteAccess = null,
        public readonly ?bool $hasSettings = null,
    ) {
        $this->id = $rawBotApp['id'];
        $this->accessHash = $rawBotApp['access_hash'];
        $this->name = $rawBotApp['short_name'];
        $this->title = $rawBotApp['title'];
        $this->description = $rawBotApp['description'];
        $this->hash = $rawBotApp['hash'];
        $this->photo = isset($rawBotApp['photo']) ? $API->wrapMedia($rawBotApp['photo']) : null;
        $this->document = isset($rawBotApp['document']) ? $API->wrapMedia($rawBotApp['document']) : null;
    }

    /** @internal */
    #[\Override]
    public function jsonSerialize(): mixed
    {
        $res = ['_' => static::class];
        $refl = new ReflectionClass($this);
        foreach ($refl->getProperties(ReflectionProperty::IS_PUBLIC) as $prop) {
            $res[$prop->getName()] = $prop->getValue($this);
        }
        return $res;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\ChatInvite;

use danog\MadelineProto\EventHandler\ChatInvite;

/**
 * Used in updates and in the channel log to indicate when a user is requesting to join or has joined a [discussion group](https://core.telegram.org/api/discussion#requiring-users-to-join-the-group).
 */
final class ChatInvitePublicJoin extends ChatInvite
{
    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(array $rawChatInvite)
    {
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\ChatInvite;

use danog\MadelineProto\EventHandler\ChatInvite;

/**
 * Represents an exported chat invite.
 */
final class ChatInviteExported extends ChatInvite
{
    /** Whether this chat invite was revoked. */
    public readonly bool $revoked;

    /** Whether this chat invite has no expiration. */
    public readonly bool $permanent;

    /** Whether users importing this invite link will have to be [approved to join the channel or group](https://core.telegram.org/api/invites#join-requests). */
    public readonly bool $requestNeeded;

    /** Chat invitation link. */
    public readonly string $link;

    /** ID of the admin that created this chat invite. */
    public readonly int $adminId;

    /** When was this chat invite last modified. */
    public readonly int $date;

    /** When was this chat invite last modified. */
    public readonly ?int $created;

    /** When does this chat invite expire. */
    public readonly ?int $expire;

    /** Maximum number of users that can join using this link. */
    public readonly ?int $limit;

    /** How many users joined using this link. */
    public readonly ?int $used;

    /** Number of users that have already used this link to join. */
    public readonly ?int $requested;

    /** Custom description for the invite link, visible only to admins. */
    public readonly ?string $title;

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(array $rawChatInvite)
    {
        $this->revoked = $rawChatInvite['revoked'] ?? false;
        $this->permanent = $rawChatInvite['permanent'] ?? false;
        $this->requestNeeded = $rawChatInvite['request_needed'] ?? false;
        $this->link = $rawChatInvite['link'];
        $this->adminId = $rawChatInvite['admin_id'];
        $this->date = $rawChatInvite['date'];
        $this->created = $rawChatInvite['start_date'] ?? null;
        $this->expire = $rawChatInvite['expire_date'] ?? null;
        $this->limit = $rawChatInvite['usage_limit'] ?? null;
        $this->requested = $rawChatInvite['requested'] ?? null;
        $this->title = $rawChatInvite['title'] ?? null;
        $this->used = $rawChatInvite['usage'] ?? null;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler;

use danog\MadelineProto\Ipc\Client;
use danog\MadelineProto\MTProto;

/**
 * Represents a Telegram Story.
 */
abstract class AbstractStory extends Update
{
    /** ID of the sender of the story */
    public readonly int $senderId;

    /** Story ID */
    public readonly int $id;

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(MTProto|Client $API, array $rawStory)
    {
        parent::__construct($API);
        $this->senderId = $API->getIdInternal($rawStory['peer']);
        $this->id = $rawStory['story']['id'];
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Typing;

use danog\MadelineProto\EventHandler\Typing;

/**
 * The user is preparing a message; typing, recording, uploading, etc. This update is valid for 6 seconds. If no further updates of this kind are received after 6 seconds, it should be considered that the user stopped doing whatever they were doing.
 */
final class UserTyping extends Typing
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Typing;

use danog\MadelineProto\EventHandler\Typing;
use danog\MadelineProto\MTProto;

/**
 * The user is preparing a message in a group; typing, recording, uploading, etc. This update is valid for 6 seconds. If no further updates of this kind are received after 6 seconds, it should be considered that the user stopped doing whatever they were doing.
 */
final class ChatUserTyping extends Typing
{
    /** Group ID. */
    public readonly int $chatId;

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(MTProto $API, array $rawTyping)
    {
        parent::__construct($API, $rawTyping);
        $this->chatId = -$rawTyping['chat_id'];
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Typing;

use danog\MadelineProto\EventHandler\Typing;
use danog\MadelineProto\MTProto;

/**
 * A user is typing in a [supergroup](https://core.telegram.org/api/channel).
 */
final class SupergroupUserTyping extends Typing
{
    /** Channel ID. */
    public readonly int $chatId;

    /** [Topic](https://core.telegram.org/api/threads) ID. */
    public readonly ?int $topicId;

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(MTProto $API, array $rawTyping)
    {
        parent::__construct($API, $rawTyping);
        $this->chatId = $API->getIdInternal($rawTyping);
        $this->topicId = $rawTyping['top_msg_id'] ?? null;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Typing;

use danog\MadelineProto\EventHandler\Typing;
use danog\MadelineProto\MTProto;

/**
 * The user is preparing a message in a secret chat; typing, recording, uploading, etc. This update is valid for 6 seconds. If no further updates of this kind are received after 6 seconds, it should be considered that the user stopped doing whatever they were doing.
 */
final class SecretUserTyping extends Typing
{
    /** Secret chat ID. */
    public readonly int $chatId;

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(MTProto $API, array $rawMessage, array $info)
    {
        $this->chatId = $rawMessage['chat_id'];
        parent::__construct($API, [
            'user_id' => $info['user_id'],
            'action' => $rawMessage['decrypted_message']['action'],
        ]);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler;

use danog\MadelineProto\EventHandler\Media\GeoPoint;
use danog\MadelineProto\MTProto;

/**
 * An incoming inline query.
 */
final class InlineQuery extends Update
{
    /** Query ID */
    public readonly int $queryId;

    /** Text of query */
    public readonly string $query;

    /** User that sent the query */
    public readonly int $userId;

    /** Offset to navigate through results */
    public readonly string $offset;

    /** Attached geolocation */
    public readonly ?GeoPoint $geo;

    /** Type of the chat from which the inline query was sent. */
    public readonly InlineQueryPeerType $peerType;

    /**
     * @readonly
     *
     * @var list<string> Regex matches, if a filter regex is present
     */
    public ?array $matches = null;
    /**
     * @readonly
     *
     * @var array<array-key, array<array-key, list{string, int}|null|string>|mixed> Regex matches, if a filter multiple match regex is present
     */
    public ?array $matchesAll = null;

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(MTProto $API, array $rawInlineQuery)
    {
        parent::__construct($API);
        $this->queryId = $rawInlineQuery['query_id'];
        $this->query = $rawInlineQuery['query'];
        $this->userId = $rawInlineQuery['user_id'];
        $this->offset = $rawInlineQuery['offset'];
        $this->peerType = InlineQueryPeerType::from($rawInlineQuery['peer_type']['_']);
        $this->geo = ($rawInlineQuery['geo']['_'] ?? null) === 'geoPoint'
            ? new GeoPoint($rawInlineQuery['geo'])
            : null;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler;

use Amp\ByteStream\ReadableStream;
use Amp\ByteStream\WritableStream;
use danog\MadelineProto\CallStream;
use danog\MadelineProto\EventHandler\Calls\GroupCallState;
use danog\MadelineProto\LocalDirectory;
use danog\MadelineProto\LocalFile;
use danog\MadelineProto\MediaDestination;
use danog\MadelineProto\RecordingFormat;
use danog\MadelineProto\RemoteUrl;
use danog\MadelineProto\VoIP\CallState;
use Stringable;

/**
 * Common interface implemented by every call type: one-to-one {@see Calls\PrivateCall} calls, and the
 * multi-party {@see Calls\GroupCall} (video chats, livestreams) and {@see Calls\ConferenceCall} (end-to-end
 * encrypted conference) calls, which additionally implement {@see MultiCall}.
 *
 * It covers the whole surface shared by all call types, so code can drive any call uniformly:
 *  - the lifecycle ({@see self::join()}, {@see self::discard()}, {@see self::isJoined()}, {@see self::getCallState()}),
 *  - who is in a multi-party call ({@see MultiCall::getParticipants()}) and, for end-to-end encrypted calls, the
 *    key verification emojis ({@see self::getVisualization()}),
 *  - the playlist/DJ playback controls and the mute controls,
 *  - screen sharing ({@see self::enablePresentation()} and friends) and
 *  - recording ({@see self::setOutput()} and {@see self::setOutputFolder()}).
 *
 * Every playlist control takes a {@see MediaDestination} selecting the stream it acts on: the main
 * camera+mic stream (default) or a separate presentation (screen-share) stream, which is started on
 * first use.
 *
 * @psalm-import-type StreamMask from CallStream
 */
interface Call extends Stringable
{
    /**
     * Join the call: accept an incoming one-to-one call, or join a multi-party call.
     *
     * @param bool $muted Whether to join with our own audio muted.
     *
     * @psalm-impure
     */
    public function join(bool $muted = false): static;

    /**
     * Discard the call: hang up a one-to-one call, or end a multi-party call for every participant
     * (see {@see MultiCall::leave()} to leave one without ending it).
     *
     * @psalm-impure
     */
    public function discard(): static;

    /**
     * Whether we are currently in the call: a one-to-one call that is running, or a multi-party
     * call we joined and did not leave.
     *
     * @psalm-mutation-free
     */
    public function isJoined(): bool;

    /**
     * The state of the call: a {@see CallState} for a one-to-one call, a {@see GroupCallState} for a
     * multi-party call.
     *
     * @psalm-mutation-free
     */
    public function getCallState(): CallState|GroupCallState;

    /**
     * The key verification emojis of an end-to-end encrypted call (a one-to-one call or a conference),
     * which every participant can compare to make sure nobody is in the middle.
     *
     * Returns null until the key is established (and, for a conference, every participant has taken
     * part in the verification), and always for a call that is not end-to-end encrypted (a video chat
     * or livestream).
     *
     * @return list<string>|null
     *
     * @psalm-mutation-free
     */
    public function getVisualization(): ?array;

    /**
     * Play a file, transmitting its audio and, if it carries a transmittable one, its video.
     *
     * A WebM/Matroska file with VP8, VP9, H.264 or H.265 video (or AV1, in a one-to-one call with an app
     * that supports it) has its video transmitted too; any other
     * file (or a raw audio stream) is played as audio only. Frames are demuxed in pure PHP and sent
     * as-is where possible, so no transcoding (and thus no FFI extension) is required for pre-encoded
     * WebM/OGG-OPUS input. `$dest` selects the camera or the presentation (screencast) stream.
     *
     * @psalm-impure
     */
    public function play(LocalFile|RemoteUrl|ReadableStream $file, MediaDestination $dest = MediaDestination::Camera): static;

    /**
     * Play a file like {@see self::play()}, but block until it has finished playing if a stream was passed.
     *
     * @psalm-impure
     */
    public function playBlocking(LocalFile|RemoteUrl|ReadableStream $file, MediaDestination $dest = MediaDestination::Camera): static;

    /**
     * Add a file to the playlist, to be played once the current one finishes.
     *
     * @psalm-impure
     */
    public function then(LocalFile|RemoteUrl|ReadableStream $file, MediaDestination $dest = MediaDestination::Camera): static;

    /**
     * Set the files to play, on loop, while the given stream's main playlist is empty.
     *
     * A hold file being played is replaced right away by the new ones (or stopped, if none are passed).
     *
     * @psalm-impure
     */
    public function playOnHold(MediaDestination $dest = MediaDestination::Camera, LocalFile|RemoteUrl|ReadableStream ...$files): static;

    /**
     * Skip to the next file in the playlist.
     *
     * @psalm-impure
     */
    public function skip(MediaDestination $dest = MediaDestination::Camera): static;

    /**
     * Stop playing all files, clearing the main and the hold playlist.
     *
     * @psalm-impure
     */
    public function stop(MediaDestination $dest = MediaDestination::Camera): static;

    /**
     * Pause playback of the current file.
     *
     * @psalm-impure
     */
    public function pause(MediaDestination $dest = MediaDestination::Camera): static;

    /**
     * Whether playback of the current file is paused.
     *
     * @psalm-external-mutation-free
     */
    public function isPaused(MediaDestination $dest = MediaDestination::Camera): bool;

    /**
     * Resume playback of the current file.
     *
     * @psalm-impure
     */
    public function resume(MediaDestination $dest = MediaDestination::Camera): static;

    /**
     * The file or stream currently being played, if any.
     *
     * Returns a string with the object ID of the stream if a stream is playing, otherwise the related
     * {@see LocalFile} or {@see RemoteUrl}.
     *
     * @psalm-external-mutation-free
     */
    public function getCurrent(MediaDestination $dest = MediaDestination::Camera): RemoteUrl|LocalFile|string|null;

    /**
     * Mute or unmute our own outgoing audio.
     *
     * @psalm-impure
     */
    public function setMuted(bool $muted = true): static;

    /**
     * Whether our own outgoing audio is muted.
     *
     * @psalm-mutation-free
     */
    public function isMuted(): bool;

    /**
     * Start sharing a screen: bring up the presentation (screencast) stream, so that files played on
     * {@see MediaDestination::Presentation} are transmitted as a screen-share. Idempotent, and done
     * automatically by the first playback on the presentation stream.
     *
     * @psalm-impure
     */
    public function enablePresentation(): static;

    /**
     * Stop sharing the screen, dropping the presentation playlist.
     *
     * @psalm-impure
     */
    public function disablePresentation(): static;

    /**
     * Whether a screen-share is currently being transmitted.
     *
     * @psalm-mutation-free
     */
    public function isSharingScreen(): bool;

    /**
     * Record the incoming media of a participant into one file (or stream) with a fixed set of tracks.
     *
     * A participant sends up to three streams â€” their microphone audio, their camera video and their
     * screen share (a second video track), see {@see CallStream} â€” and may turn any of them on or off
     * at any time. `$streams` picks which of them the recording holds, as a bitmask of
     * {@see CallStream::AUDIO}, {@see CallStream::VIDEO} and {@see CallStream::SCREEN}; every chosen
     * stream must be available (sent by the participant right now, see {@see Calls\CallStreams}) or an
     * exception is thrown. When `$streams` is null, every available stream is recorded (and if none is,
     * an exception is thrown). Either way the streams currently available are returned.
     *
     * A Matroska file's track list is fixed in its header, so the file keeps its tracks for its whole
     * duration: a stream the participant turns off simply stops being written, and is written again when
     * it comes back; a stream that becomes available later is not added (call this method again to start
     * a new file with it). Only two things finish the file early: a change of codec of one of its video
     * streams, or the end of the call (or our leaving it). Every start and end of a recording, and every
     * change of the available streams or their codecs, is reported by a {@see Calls\CallStreams} update.
     *
     * `$file` is where to record to: a {@see LocalFile}, or a {@see WritableStream}. `$participant` is
     * who to record: in a one-to-one call it may only be the other party, and is therefore optional;
     * in a multi-party call it is required, every participant being recorded to its own file (see
     * {@see self::setOutputFolder()} to record everyone at once) â€” except in
     * [stream mode Â»](https://core.telegram.org/api/group-calls#stream-mode), where the call is a
     * single mixed stream, recorded without specifying a participant. Our own media is never recorded.
     *
     * A {@see RecordingFormat::Webm} or {@see RecordingFormat::Mkv} target muxes the media into a
     * Matroska file in pure PHP: the frames are stored as-is, so the video tracks are whatever codec the
     * participant sends (VP8/VP9/H.264/H.265/AV1) and the audio is OPUS. {@see RecordingFormat::Opus} writes
     * an audio-only OGG OPUS stream, and is supported by one-to-one calls (and stream mode) only. When
     * `$format` is null it is autodetected from the extension of `$file` if a {@see LocalFile} was passed;
     * a raw stream, whose extension is unknown, defaults to WebM.
     *
     * @param StreamMask|null $streams The streams to record, as a bitmask of {@see CallStream} flags, or null for every available one.
     *
     * @throws \InvalidArgumentException If a chosen stream is not available, or nothing is.
     *
     * @return StreamMask The streams the participant currently sends, as a bitmask of {@see CallStream} flags.
     *
     * @psalm-impure
     */
    public function setOutput(LocalFile|WritableStream $file, string|int|null $participant = null, ?RecordingFormat $format = null, ?int $streams = null): int;

    /**
     * Record the incoming media of the call into a directory, following every change of the streams.
     *
     * Unlike {@see self::setOutput()}, whose file keeps a fixed set of tracks, a directory recording
     * follows the participants turning their microphone, camera and screen share on and off: since a
     * Matroska file's track list is fixed in its header, every such change closes the current file and
     * continues in a new one, so each participant's recording is a numbered series of files, each named
     * after the streams it holds: `<n>_<streams>.mkv`, with `n` counting from 0 and `streams` listing
     * `audio`, `video` and `screen` (joined by commas, in that order): `0_audio.mkv`, `1_audio,video.mkv`,
     * `2_audio,video,screen.mkv`, â€¦. Every file opened and finished is reported by a {@see Calls\CallStreams} update.
     *
     * In a one-to-one call the other party is recorded as `<dir>/<n>_<streams>.mkv`. In a multi-party
     * call every participant that transmits something (including ones that start later) is recorded as
     * `<dir>/<peerId>.<n>_<streams>.mkv`, or only the one given as `$participant` (a user id, username
     * or peer) if any; our own media is never recorded. In
     * [stream mode Â»](https://core.telegram.org/api/group-calls#stream-mode) the mixed stream is recorded
     * as `<dir>/stream.mkv` (`.webm`, or `.ogg` for {@see RecordingFormat::Opus}) and, in an
     * automatically-scaled livestream, each publisher's video as `<dir>/video-<endpoint>.mkv`.
     *
     * The files are Matroska ({@see RecordingFormat::Mkv} by default, or {@see RecordingFormat::Webm}).
     *
     * @psalm-impure
     */
    public function setOutputFolder(LocalDirectory $dir, string|int|null $participant = null, ?RecordingFormat $format = null): static;
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Privacy\RuleDestination;

use danog\MadelineProto\EventHandler\Privacy\RuleDestination;

/**
 * Disallow only participants of certain chats.
 */
final class DisallowChatParticipants extends RuleDestination
{
    /** Allowed chats */
    public readonly array $chats;

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(array $rawUsers)
    {
        $this->chats = $rawUsers['chats'];
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Privacy\RuleDestination;

use danog\MadelineProto\EventHandler\Privacy\RuleDestination;

/**
 * Disallow only contacts.
 */
final class DisallowContacts extends RuleDestination
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Privacy\RuleDestination;

use danog\MadelineProto\EventHandler\Privacy\RuleDestination;

/**
 * Allow all participants of certain chats.
 */
final class AllowChatParticipants extends RuleDestination
{
    /** Allowed chats */
    public readonly array $chats;

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(array $rawUsers)
    {
        $this->chats = $rawUsers['chats'];
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Privacy\RuleDestination;

use danog\MadelineProto\EventHandler\Privacy\RuleDestination;

final class AllowCloseFriends extends RuleDestination
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Privacy\RuleDestination;

use danog\MadelineProto\EventHandler\Privacy\RuleDestination;

/**
 * Disallow all users.
 */
final class DisallowAll extends RuleDestination
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Privacy\RuleDestination;

use danog\MadelineProto\EventHandler\Privacy\RuleDestination;

/**
 * Allow only certain user.
 */
final class AllowUsers extends RuleDestination
{
    /** Allowed users */
    public readonly array $users;

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(array $rawUsers)
    {
        $this->users = $rawUsers['users'];
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Privacy\RuleDestination;

use danog\MadelineProto\EventHandler\Privacy\RuleDestination;

/**
 * Disallow only certain users.
 */
final class DisallowUsers extends RuleDestination
{
    /** Allowed users */
    public readonly array $users;

    /**
     * @internal
     *
     * @psalm-mutation-free
     */
    public function __construct(array $rawUsers)
    {
        $this->users = $rawUsers['users'];
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Privacy\RuleDestination;

use danog\MadelineProto\EventHandler\Privacy\RuleDestination;

/**
 * Allow all contacts.
 */
final class AllowContacts extends RuleDestination
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Privacy\RuleDestination;

use danog\MadelineProto\EventHandler\Privacy\RuleDestination;

/**
 * Allow all users.
 */
final class AllowAll extends RuleDestination
{
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Privacy;

use JsonSerializable;

/** Represents a privacy rule. */
enum Rule: string implements JsonSerializable
{
    /** Whether we can see the last online timestamp of this user */
    case STATUS_TIMESTAMP = 'privacyKeyStatusTimestamp';
    /** Whether the user can be invited to chats */
    case CHAT_INVITE = 'privacyKeyChatInvite';
    /** Whether the user accepts phone calls */
    case PHONE_CALL = 'privacyKeyPhoneCall';
    /** Whether P2P connections in phone calls with this user are allowed */
    case PHONE_P2P = 'privacyKeyPhoneP2P';
    /** Whether messages forwarded from the user will be [anonymously forwarded](https://telegram.org/blog/unsend-privacy-emoji#anonymous-forwarding) */
    case FORWARDS = 'privacyKeyForwards';
    /** Whether the profile picture of the user is visible */
    case PROFILE_PHOTO = 'privacyKeyProfilePhoto';
    /** Whether the user allows us to see his phone number */
    case PHONE_NUMBER = 'privacyKeyPhoneNumber';
    /** Whether this user can be added to our contact list by their phone number */
    case ADDED_BY_PHONE = 'privacyKeyAddedByPhone';
    /** Whether the user accepts voice messages */
    case VOICE_MESSAGES = 'privacyKeyVoiceMessages';
    /** Whether the user can see our bio. */
    case ABOUT = 'privacyKeyAbout';
    /** Whether the user can send us messages without paying. */
    case MESSAGES_WITHOUT_PAYING = 'privacyKeyNoPaidMessages';

    /** @internal */
    #[\Override]
    public function jsonSerialize(): string
    {
        return $this->name;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @copyright 2016-2025 Amir Hossein Jafari <amirhosseinjafari8228@gmail.com>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto\EventHandler\Privacy;

use danog\MadelineProto\EventHandler\Privacy\RuleDestination\AllowAll;
use danog\MadelineProto\EventHandler\Privacy\RuleDestination\AllowChatParticipants;
use danog\MadelineProto\EventHandler\Privacy\RuleDestination\AllowCloseFriends;
use danog\MadelineProto\EventHandler\Privacy\RuleDestination\AllowContacts;
use danog\MadelineProto\EventHandler\Privacy\RuleDestination\AllowUsers;
use danog\MadelineProto\EventHandler\Privacy\RuleDestination\DisallowAll;
use danog\MadelineProto\EventHandler\Privacy\RuleDestination\DisallowChatParticipants;
use danog\MadelineProto\EventHandler\Privacy\RuleDestination\DisallowContacts;
use danog\MadelineProto\EventHandler\Privacy\RuleDestination\DisallowUsers;
use JsonSerializable;
use ReflectionClass;
use ReflectionProperty;

/**
 * To whom does a privacy rule apply?
 */
abstract class RuleDestination implements JsonSerializable
{
    /**
     * @psalm-pure
     */
    public static function fromRawRule(array $rawRule): RuleDestination
    {
        return match ($rawRule['_']) {
            'privacyValueAllowAll' => new AllowAll,
            'privacyValueDisallowAll' => new DisallowAll,
            'privacyValueAllowContacts' => new AllowContacts,
            'privacyValueDisallowContacts' => new DisallowContacts,
            'privacyValueAllowCloseFriends' => new AllowCloseFriends,
            'privacyValueAllowUsers' => new AllowUsers($rawRule),
            'privacyValueDisallowUsers' => new DisallowUsers($rawRule),
            'privacyValueAllowChatParticipants' => new AllowChatParticipants($rawRule),
            'privacyValueDisallowChatParticipants' => new DisallowChatParticipants($rawRule),
        };
    }

    /** @internal */
    #[\Override]
    public function jsonSerialize(): mixed
    {
        $res = ['_' => static::class];
        $refl = new ReflectionClass($this);
        foreach ($refl->getProperties(ReflectionProperty::IS_PUBLIC) as $prop) {
            $res[$prop->getName()] = $prop->getValue($this);
        }
        return $res;
    }
}
<?php

declare(strict_types=1);

/**
 * API wrapper module.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

use danog\MadelineProto\Ipc\Client;

/**
 * The root of a session. Dynamic properties are allowed only for unserialization: see __wakeup().
 */
#[\AllowDynamicProperties]
final class APIWrapper
{
    private MTProto|Client|null $API = null;

    /**
     * API wrapper.
     *
     * @psalm-mutation-free
     */
    public function __construct(
        private SessionPaths $session,
    ) {
    }
    /**
     * @psalm-external-mutation-free
     */
    public function setSession(SessionPaths $session): void
    {
        $this->session = $session;
    }

    /**
     * @psalm-external-mutation-free
     */
    public function logger(mixed $param, int $level = Logger::NOTICE, string $file = ''): void
    {
        ($this->API->logger ?? Logger::$default)->logger($param, $level, $file);
    }
    public function setAPI(Client|MTProto|null $API): void
    {
        $this->API?->unreference();
        $this->API = $API;
    }

    /**
     * Sleep function.
     *
     * @psalm-pure
     */
    public function __sleep(): array
    {
        return ['API'];
    }

    /**
     * Drop the properties removed since the session was saved (webApiTemplate): they come back as dynamic
     * properties (allowed, see the class, or a deprecation), and __sleep() doesn't save them again.
     *
     * @internal
     */
    public function __wakeup(): void
    {
        foreach (get_object_vars($this) as $name => $_) {
            if (!property_exists(self::class, (string) $name) && !str_starts_with((string) $name, "\0")) {
                unset($this->{$name});
            }
        }
    }

    /**
     * Get MTProto instance.
     */
    public function getAPI(): Client|MTProto|null
    {
        return $this->API;
    }

    /**
     * Get IPC path.
     *
     * @internal
     *
     * @psalm-mutation-free
     */
    public function getIpcPath(): string
    {
        return $this->session->getIpcPath();
    }

    /**
     * Serialize session.
     */
    public function serialize(): void
    {
        if ($this->API === null) {
            return;
        }
        if ($this->API instanceof Client) {
            return;
        }
        $this->API->waitForInit();
        $API = $this->API;

        if ($API->getAuthorization() === API::LOGGED_OUT) {
            $API->deleteSession();
            $this->session->delete();
            return;
        }

        $this->session->serialize(
            $API->serializeSession($this),
            $this->session->getSessionPath(),
        );

        $this->session->storeLightState($API);

        if (!Magic::$suspendPeriodicLogging) {
            Logger::log('Saved session!');
        }
    }

    /**
     * Get session path.
     */
    public function getSession(): SessionPaths
    {
        return $this->session;
    }
}
<?php

declare(strict_types=1);

/**
 * IPC light state.
 *
 * This file is part of MadelineProto.
 * MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with MadelineProto.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 * @link https://docs.madelineproto.xyz MadelineProto documentation
 */

namespace danog\MadelineProto;

/**
 * Light state.
 *
 * @internal
 */
final class LightState
{
    /**
     * Event handler class name.
     *
     * @var null|class-string<EventHandler>
     */
    private ?string $eventHandler = null;

    /**
     * @psalm-mutation-free
     */
    public function __construct(MTProto $API)
    {
        if ($API->hasEventHandler()) {
            $this->eventHandler = $API->getEventHandler()::class;
        }
    }

    /**
     * Check whether we can start IPC.
     */
    public function canStartIpc(): bool
    {
        return !$this->eventHandler || class_exists($this->eventHandler);
    }

    /**
     * Get event handler class name.
     *
     * @return null|class-string<EventHandler>
     */
    public function getEventHandler(): ?string
    {
        return $this->eventHandler;
    }
}
{
    "name": "danog/madelineproto",
    "description": "Async PHP client API for the telegram MTProto protocol.",
    "type": "library",
    "license": "AGPL-3.0-only",
    "homepage": "https://docs.madelineproto.xyz",
    "keywords": [
        "telegram",
        "mtproto",
        "protocol",
        "bytes",
        "messenger",
        "client",
        "PHP",
        "video",
        "stickers",
        "audio",
        "files",
        "GB"
    ],
    "conflict": {
        "krakjoe/pthreads-polyfill": "*",
        "ext-pthreads": "*"
    },
    "require": {
        "php-64bit": ">=8.2",
        "danog/primemodule": "^1.0.13",
        "symfony/polyfill-mbstring": ">=1.38.2",
        "ext-json": "*",
        "ext-xml": "*",
        "ext-dom": "*",
        "ext-filter": "*",
        "ext-hash": "*",
        "ext-zlib": "*",
        "ext-fileinfo": "*",
        "amphp/amp": "^3.1.3",
        "amphp/http-client": "^5.3.6",
        "amphp/websocket-client": "^2.0.2",
        "amphp/http": "^2.1.2",
        "amphp/socket": "^2.4.1",
        "amphp/dns": "^2.4.1",
        "amphp/byte-stream": "^2.1.2",
        "amphp/file": "^3.2.0",
        "amphp/mysql": "^3.1.1",
        "amphp/postgres": "^2.2.1",
        "amphp/process": "^2.1",
        "danog/dns-over-https": "^1.0.1",
        "amphp/http-client-cookies": "^2",
        "danog/tg-file-decoder": "^1.0.2",
        "league/uri": "^7.8.1",
        "danog/ipc": "^1.1.0",
        "amphp/log": "^2",
        "danog/loop": "^1.1.1",
        "phpseclib/phpseclib": "^4.0.1",
        "amphp/redis": "^2.0.4",
        "psr/http-factory": "^1.1.0",
        "psr/log": "^3.0.2",
        "webmozart/assert": "^2.4.1",
        "bacon/bacon-qr-code": "^3.1.1",
        "nikic/php-parser": "^5.9.0",
        "revolt/event-loop": "^1.0.9",
        "danog/async-orm": "^1.1.7",
        "danog/telegram-entities": "^1.1.0",
        "danog/better-prometheus": "^0.1.1",
        "amphp/http-server": "^3.4.6",
        "danog/tg-dialog-id": "^2",
        "symfony/polyfill-php83": "^1.41",
        "danog/php-rtc-av": "^1.1.11",
        "danog/php-rtc-codecs": "^1.1.19",
        "danog/php-rtc-dtls": "^2.0.19",
        "danog/php-rtc-ice": "^2.0.18",
        "danog/php-rtc-ntp": "^1.1.14",
        "danog/php-rtc-rtcp": "^1.1.11",
        "danog/php-rtc-rtp": "^2.0.23",
        "danog/php-rtc-sctp": "^2.0.14",
        "danog/php-rtc-srtp": "^1.1.16",
        "danog/php-rtc-stun": "^2.0.18",
        "danog/php-rtc-webrtc": "^2.0.19",
        "danog/php-rtc-datachannel": "^1.1.10",
        "danog/php-rtc-sdp": "^1.1.12",
        "danog/php-rtc-stats": "^1.1.7",
        "danog/php-rtc-turn": "^2.0.9",
        "danog/php-rtc-mdns": "^1.1.12",
        "danog/php-rtc-exception": "^1.1.10",
        "danog/php-rtc-mixin": "^2.0.9",
        "danog/php-rtc-rtp-parameter": "^1.1.8",
        "symfony/polyfill-iconv": "^1.37",
        "symfony/polyfill-php84": "^1.38",
        "symfony/polyfill-php85": "^1.41",
        "symfony/polyfill-php86": "^1.41"
    },
    "require-dev": {
        "ext-ctype": "*",
        "danog/phpdoc": "^0.1.24",
        "phpunit/phpunit": "^9.6.36",
        "amphp/phpunit-util": "^3",
        "bamarni/composer-bin-plugin": "1.9.1",
        "symfony/yaml": "^6.4.45",
        "dg/bypass-finals": "dev-master",
        "brianium/paratest": "^6.11.1",
        "vimeo/psalm": "dev-master",
        "sebastian/diff": "^4.0.6"
    },
    "suggest": {
        "ext-primemodule": "Install the primemodule and FFI extensions to speed up MadelineProto (https://prime.madelineproto.xyz)",
        "ext-ffi": "Install the FFI extension (with libopus, libvpx and ffmpeg) to transcode audio and video in realtime; not required to play and record video and audio inside calls",
        "ext-pdo": "Install the pdo extension to store session data on MySQL",
        "ext-openssl": "Install the openssl extension for faster crypto",
        "ext-sodium": "Install the sodium extension for faster crypto",
        "ext-uv": "Install the uv extension to greatly speed up MadelineProto!",
        "ext-gmp": "Install the gmp extension to speed up authorization",
        "ext-bcmath": "Install the bcmath extension to speed up authorization",
        "ext-sockets": "Install the sockets extension on Windows to use UNIX sockets for IPC (otherwise TCP on 127.0.0.1 is used, which any local user can connect to)"
    },
    "authors": [
        {
            "name": "Daniil Gentili",
            "email": "daniil@daniil.it"
        }
    ],
    "autoload": {
        "psr-4": {
            "danog\\MadelineProto\\": "src"
        },
        "files": [
            "src/polyfill.php"
        ]
    },
    "autoload-dev": {
        "psr-4": {
            "danog\\MadelineProto\\Test\\": "tests/danog/MadelineProto",
            "danog\\MadelineProto\\": "tools/"
        },
        "files": [
            "tools/build_docs/schemas.php",
            "tools/build_docs/merge.php",
            "tools/build_docs/layerUpgrade.php"
        ]
    },
    "scripts": {
        "build": [
            "@docs",
            "@docs-fix",
            "@cs-fix",
            "@fuzz",
            "@file_ref_map",
            "@test-light"
        ],
        "test": [
            "@paratest",
            "@test-session"
        ],
        "test-light": [
            "@paratest-light"
        ],
        "cs-fix": "PHP_CS_FIXER_IGNORE_ENV=1 php -d pcre.jit=0 vendor/bin/php-cs-fixer fix -v",
        "psalm": "psalm --no-cache --threads=10",
        "docs": "php tools/build_docs.php",
        "fuzz": "php tools/fuzzer.php",
        "file_ref_map": "php tools/gen_filerefmap.php",
        "file_ref_map_docs": "php tools/gen_filerefmap_docs.php",
        "docs-fix": "tools/fix_docs.sh",
        "paratest": "@php -dzend.assertions=1 ./vendor/bin/paratest -v --exclude-group session",
        "test-session": "tests/session_tests.sh",
        "paratest-light": "@php -dzend.assertions=1 ./vendor/bin/paratest -vc phpunit-light.xml --exclude-group session",
        "bin": "echo 'bin not installed'",
        "post-install-cmd": [
            "@composer bin all install --ansi"
        ],
        "post-update-cmd": [
            "@composer bin all update --ansi"
        ]
    },
    "config": {
        "allow-plugins": {
            "bamarni/composer-bin-plugin": true,
            "phabel/phabel": true,
            "dealerdirect/phpcodesniffer-composer-installer": true,
            "symfony/thanks": true
        }
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTCP;

use Webrtc\RTCP\Exception\RtcpPacketException;

/**
 * RTCP RTP Feedback Packet (RTPFB)
 *
 * Provides transport-layer feedback information about RTP packets.
 * Used for:
 * - Generic NACK (Negative ACKnowledgement)
 * - Temporary Maximum Media Stream Bitrate Request (TMMBR)
 * - Temporary Maximum Media Stream Bitrate Notification (TMMBN)
 *
 * @see https://datatracker.ietf.org/doc/html/rfc4585#section-6.2 Defined in RFC 4585 section 6.2
 */
final readonly class RtcpRtpfbPacket implements RtcpPacketInterface
{
    /**
     * Constructs a new RTP Feedback packet
     *
     * @param int $fmt Feedback message type:
     *                 - 1: Generic NACK
     *                 - 2: Reserved
     *                 - 3: TMMBR
     *                 - 4: TMMBN
     * @param int $ssrc SSRC of feedback sender
     * @param int $mediaSsrc SSRC of a media source being reported
     * @param int[] $lost Array of lost packet sequence numbers (for NACK)
     */
    public function __construct(
        private int   $fmt,
        private int   $ssrc,
        private int   $mediaSsrc,
        private array $lost = []
    ) {
    }

    /**
     * Encodes RTPFB packet into binary format
     *
     * For NACK packets, encodes lost packets using PID (Packet ID) and
     * BLP (Bitmask of Following Lost Packets) format
     *
     * @return string Binary RTCP RTPFB packet
     */
    #[\Override]
    public function encode(): string
    {
        $payload = pack('NN', $this->ssrc, $this->mediaSsrc);

        if (!empty($this->lost)) {
            $pid = $this->lost[0];
            $blp = 0;

            foreach (array_slice($this->lost, 1) as $p) {
                $d = $p - $pid - 1;
                if ($d < 16) {
                    $blp |= 1 << $d;
                } else {
                    $payload .= pack('nn', $pid, $blp);
                    $pid = $p;
                    $blp = 0;
                }
            }

            $payload .= pack('nn', $pid, $blp);
        }

        return $this->packRtcpPacket($this->fmt, $payload);
    }

    /**
     * Decodes binary data into RTPFB packet
     *
     * @param string $data Binary RTPFB packet data (without header)
     * @param int $count Feedback message type from header
     * @return self New RtcpRtpfbPacket instance
     * @throws RtcpPacketException If data length is invalid
     */
    #[\Override]
    public static function decode(string $data, int $count): self
    {
        if (strlen($data) < 8 || strlen($data) % 4 != 0) {
            throw new RtcpPacketException("RTCP RTP feedback length is invalid");
        }

        $unpacked = unpack('Nssrc/NmediaSsrc', substr($data, 0, 8));
        if ($unpacked === false) {
            throw new RtcpPacketException("RTCP RTP feedback is invalid");
        }

        /** @var array{ssrc: int, mediaSsrc: int} $unpacked */
        $ssrc = $unpacked['ssrc'];
        $mediaSsrc = $unpacked['mediaSsrc'];

        $lost = [];
        // Only a NACK lists lost packets: the others, like transport-wide congestion control feedback
        // (sent several times a second), have FCIs of their own.
        for ($pos = 8; $count === RtcpConstants::RTCP_RTPFB_NACK && $pos < strlen($data); $pos += 4) {
            $unpacked = unpack('npid/nblp', substr($data, $pos, 4));
            if ($unpacked === false) {
                throw new RtcpPacketException("RTCP RTP feedback is invalid");
            }

            /** @var array{pid: int, blp: int} $unpacked */
            $pid = $unpacked['pid'];
            $blp = $unpacked['blp'];

            $lost[] = $pid;
            for ($d = 0; $d < 16; $d++) {
                if (($blp >> $d) & 1) {
                    $lost[] = $pid + $d + 1;
                }
            }
        }

        return new self($count, $ssrc, $mediaSsrc, $lost);
    }

    /**
     * Constructs RTCP packet header and combines with payload
     *
     * @param int $fmt Feedback message type
     * @param string $payload Packet payload
     * @return string Complete RTCP packet
     */
    private function packRtcpPacket(int $fmt, string $payload): string
    {
        $version = 2;
        $padding = 0;
        $length = strlen($payload) / 4;

        $header = pack('CCn', ($version << 6) | ($padding << 5) | $fmt, RtcpConstants::RTCP_RTPFB, $length);
        return $header . $payload;
    }

    /**
     * Get a feedback message type
     *
     * @return int FMT field value
     */
    public function getFmt(): int
    {
        return $this->fmt;
    }

    /**
     * Get sender SSRC
     *
     * @return int Synchronization source identifier of sender
     */
    public function getSsrc(): int
    {
        return $this->ssrc;
    }

    /**
     * Get media SSRC
     *
     * @return int SSRC of a media source being reported
     */
    public function getMediaSsrc(): int
    {
        return $this->mediaSsrc;
    }

    /**
     * Get lost packet IDs
     *
     * For NACK packets, returns array of lost sequence numbers
     *
     * @return int[] Array of lost packet sequence numbers
     */
    public function getLost(): array
    {
        return $this->lost;
    }

    public function __toString(): string
    {
        return __CLASS__;
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTCP;

interface RtcpPacketInterface
{
    public function encode(): string;

    public static function decode(string $data, int $count): self;

    public function __toString(): string;
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTCP;

use Webrtc\RTCP\Exception\RtcpPacketException;

/**
 * RTCP Sender Report (SR) Packet
 *
 * Sent by active senders to report transmission and reception statistics.
 * Contains:
 * - SSRC of reporting endpoint
 * - Sender information block
 * - Zero or more receiver report blocks
 *
 * Defined in RFC 3550 section 6.4.1
 */
final readonly class RtcpSrPacket implements RtcpPacketInterface
{
    /**
     * Constructs new Sender Report packet
     *
     * @param int $ssrc SSRC of reporting endpoint
     * @param RtcpSenderInfo $senderInfo Transmission statistics
     * @param RtcpReceiverInfo[] $reports Reception statistics
     */
    public function __construct(
        private int            $ssrc,
        private RtcpSenderInfo $senderInfo,
        private array          $reports = []
    ) {
    }

    /**
     * Encodes SR packet into binary format
     *
     * @return string Binary RTCP SR packet
     */
    #[\Override]
    public function encode(): string
    {
        $payload = pack('N', $this->ssrc);
        $payload .= $this->senderInfo->encode();
        foreach ($this->reports as $report) {
            $payload .= $report->encode();
        }
        return $this->packRtcpPacket(count($this->reports), $payload);
    }

    /**
     * Decodes binary data into SR packet
     *
     * @param string $data Binary SR packet data (without header)
     * @param int $count Number of report blocks expected
     * @return self New RtcpSrPacket instance
     * @throws RtcpPacketException If data length is invalid
     */
    #[\Override]
    public static function decode(string $data, int $count): self
    {
        $expectedLength = 24 + 24 * $count;
        if (strlen($data) != $expectedLength) {
            throw new RtcpPacketException("RTCP sender report length is invalid");
        }

        $unpacked = unpack('N', substr($data, 0, 4));
        if ($unpacked === false) {
            throw new RtcpPacketException("RTCP sender report is invalid");
        }

        /** @var array{1: int} $unpacked */
        $ssrc = $unpacked[1];
        $senderInfo = RtcpSenderInfo::decode(substr($data, 4, 20));
        $reports = [];
        $pos = 24;

        for ($i = 0; $i < $count; $i++) {
            $reportData = substr($data, $pos, 24);
            $reports[] = RtcpReceiverInfo::decode($reportData);
            $pos += 24;
        }

        return new self($ssrc, $senderInfo, $reports);
    }

    /**
     * Constructs RTCP packet header and combines with payload
     *
     * @param int $count Number of report blocks
     * @param string $payload Packet payload
     * @return string Complete RTCP packet
     */
    private function packRtcpPacket(int $count, string $payload): string
    {
        $version = 2;
        $padding = 0;
        $length = strlen($payload) / 4;

        $header = pack('CCn', ($version << 6) | ($padding << 5) | $count, RtcpConstants::RTCP_SR, $length);
        return $header . $payload;
    }

    /**
     * Get sender SSRC
     *
     * @return int Synchronization source identifier
     */
    public function getSsrc(): int
    {
        return $this->ssrc;
    }

    /**
     * Get sender information
     *
     * @return RtcpSenderInfo Transmission statistics
     */
    public function getSenderInfo(): RtcpSenderInfo
    {
        return $this->senderInfo;
    }

    /**
     * Get receiver reports
     *
     * @return RtcpReceiverInfo[] Reception statistics
     */
    public function getReports(): array
    {
        return $this->reports;
    }

    public function __toString(): string
    {
        return __CLASS__;
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTCP;

use Webrtc\RTCP\Exception\RtcpPacketException;

/**
 * RTCP Source Description (SDES) Packet
 *
 * Provides additional information about participants in an RTP session.
 * Contains one or more source description chunks, each describing one
 * synchronization source (SSRC).
 *
 * Each chunk contains:
 * - SSRC identifier
 * - Zero or more SDES items (type-length-value format)
 * - Null terminator
 *
 * Defined in RFC 3550 section 6.5
 */
final readonly class RtcpSdesPacket implements RtcpPacketInterface
{
    /**
     * Constructs a new SDES packet
     *
     * @param RtcpSourceInfo[] $chunks Array of source description chunks
     */
    public function __construct(private array $chunks = [])
    {
    }

    /**
     * Encodes SDES packet into binary format
     *
     * Constructs a packet with:
     * - RTCP header (version, padding, count, packet type, length)
     * - One or more SDES chunks (SSRC + items)
     * - Padding to 32-bit boundary
     *
     * @return string Binary RTCP SDES packet
     * @throws RtcpPacketException If encoding fails
     */
    #[\Override]
    public function encode(): string
    {
        $payload = '';
        foreach ($this->chunks as $chunk) {
            $payload .= pack('N', $chunk->getSsrc());
            foreach ($chunk->getItems() as $item) {
                $payload .= pack('CC', $item[0], strlen($item[1])) . $item[1];
            }
            $payload .= "\x00\x00"; // End of chunk
        }

        // Pad to 32-bit boundary
        while (strlen($payload) % 4 != 0) {
            $payload .= "\x00";
        }

        return RtcpUtility::packRtcpPacket(RtcpConstants::RTCP_SDES, count($this->chunks), $payload);
    }

    /**
     * Decodes binary data into SDES packet
     *
     * @param string $data Binary SDES packet data (without header)
     * @param int $count Number of chunks expected
     * @return self New RtcpSdesPacket instance
     * @throws RtcpPacketException If data is invalid or truncated
     */
    #[\Override]
    public static function decode(string $data, int $count): self
    {
        $pos = 0;
        $chunks = [];

        for ($i = 0; $i < $count; $i++) {
            if (strlen($data) < $pos + 4) {
                throw new RtcpPacketException("RTCP SDES source is truncated");
            }

            $unpacked = unpack('N', substr($data, $pos, 4));
            if ($unpacked === false) {
                throw new RtcpPacketException("RTCP SDES source is invalid");
            }

            /** @var array{1: int} $unpacked */
            $ssrc = $unpacked[1];
            $pos += 4;

            $items = [];
            while ($pos < strlen($data) - 1) {
                $unpacked = unpack('Ctype/Clength', substr($data, $pos, 2));
                if ($unpacked === false) {
                    throw new RtcpPacketException("RTCP SDES item is invalid");
                }

                /** @var array{type: int, length: int} $unpacked */
                $type = $unpacked['type'];
                $length = $unpacked['length'];
                $pos += 2;

                if (strlen($data) < $pos + $length) {
                    throw new RtcpPacketException("RTCP SDES item is truncated");
                }

                $value = substr($data, $pos, $length);
                $pos += $length;

                if ($type == 0) {
                    break; // End of chunk
                }
                $items[] = [$type, $value];
            }

            $chunks[] = new RtcpSourceInfo($ssrc, $items);
        }

        return new self($chunks);
    }

    /**
     * Get source description chunks
     *
     * @return RtcpSourceInfo[] Array of source description chunks
     */
    public function getChunks(): array
    {
        return $this->chunks;
    }

    public function __toString(): string
    {
        return __CLASS__;
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTCP;

use Webrtc\RTCP\Exception\RtcpPacketException;

/**
 * Represents an RTCP BYE (Goodbye) packet used in RTP communication.
 *
 * This class implements the RtcpPacketInterface and provides functionality to:
 * - Construct a new BYE packet with a list of SSRCs
 * - Encode the packet into binary format for transmission
 * - Decode binary data into a packet instance
 * - Access the list of SSRCs included in the packet
 *
 * The RTCP BYE packet indicates that one or more sources are no longer active.
 */
final readonly class RtcpByePacket implements RtcpPacketInterface
{

    /**
     * Constructs a new RtcpByePacket instance.
     *
     * @param int[] $sources List of synchronization sources (SSRCs) to include in the packet.
     */
    public function __construct(private array $sources = [])
    {
    }

    /**
     * Encodes the RTCP BYE packet into a binary string.
     *
     * This method converts the list of SSRCs into a binary payload and constructs the RTCP packet
     * header. The resulting binary string can be transmitted over the network.
     *
     * @return string The binary representation of the RTCP BYE packet.
     */
    #[\Override]
    public function encode(): string
    {
        $payload = '';
        foreach ($this->sources as $ssrc) {
            $payload .= pack('N', $ssrc); // Pack as 32-bit unsigned big-endian
        }
        return $this->packRtcpPacket(count($this->sources), $payload);
    }

    /**
     * Decodes a binary string into an RtcpByePacket instance.
     *
     * This method parses the binary data of an RTCP BYE packet, extracts the list of SSRCs,
     * and returns a new RtcpByePacket instance.
     *
     * @param string $data The binary data of the RTCP BYE packet.
     * @param int $count The number of SSRCs expected in the packet.
     * @return self A new RtcpByePacket instance.
     * @throws RtcpPacketException If the binary data is invalid or the length is incorrect.
     */
    #[\Override]
    public static function decode(string $data, int $count): self
    {
        if (strlen($data) < 4 * $count) {
            throw new RtcpPacketException("RTCP bye length is invalid");
        }

        $sources = [];
        if ($count > 0) {
            $format = 'N' . $count; // Unpack as 32-bit unsigned big-endian
            $unpacked = unpack($format, $data);
            if ($unpacked === false) {
                throw new RtcpPacketException("RTCP bye data is invalid");
            }

            /** @var list<int> $sources */
            $sources = array_values($unpacked);
        }

        return new self($sources);
    }

    /**
     * Constructs the RTCP packet header and combines it with the payload.
     *
     * This helper method creates the RTCP packet header based on the provided parameters
     * and appends the payload to it.
     *
     * @param int $count The number of SSRCs or other count-specific data.
     * @param string $payload The binary payload of the RTCP packet.
     * @return string The complete RTCP packet as a binary string.
     */
    private function packRtcpPacket(int $count, string $payload): string
    {
        $version = 2; // RTCP version
        $padding = 0; // No padding
        $length = strlen($payload) / 4; // Length in 32-bit words

        // Pack header: version (2 bits), padding (1 bit), count (5 bits), packet type (8 bits), length (16 bits)
        $header = pack('CCn', ($version << 6) | ($padding << 5) | $count, RtcpConstants::RTCP_BYE, $length);

        return $header . $payload;
    }

    /**
     * @return array
     */
    public function getSources(): array
    {
        return $this->sources;
    }

    /**
     * @return string
     */
    public function __toString(): string
    {
        return __CLASS__;
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTCP;

final class RtcpConstants
{
    // Header lengths
    const RTCP_HEADER_LENGTH = 4;

    // RTCP packet types
    const RTCP_SR = 200;
    const RTCP_RR = 201;
    const RTCP_SDES = 202;
    const RTCP_BYE = 203;
    const RTCP_RTPFB = 205;
    const RTCP_PSFB = 206;

    // RTCP Feedback Message Types
    const RTCP_RTPFB_NACK = 1;

    // RTCP Payload-Specific Feedback Messages
    const RTCP_PSFB_PLI = 1;
    const RTCP_PSFB_SLI = 2;
    const RTCP_PSFB_RPSI = 3;
    const RTCP_PSFB_APP = 15;
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTCP;

use Webrtc\RTCP\Exception\RtcpPacketException;

/**
 * RTCP Packet Parser
 *
 * This class provides functionality to decode RTCP (RTP Control Protocol) packets
 * from binary data into their respective RTCP packet types. It handles the common
 * RTCP header parsing- and delegate-specific packet type parsing to specialized
 * packet classes.
 *
 * RTCP packets are used alongside RTP to provide quality feedback, participant
 * information, and control information in multimedia streaming sessions.
 */
final class RtcpPacket
{

    /**
     * Decodes binary RTCP data into an array of RTCP packet objects
     *
     * This method processes compound RTCP packets (which may contain multiple
     * individual RTCP packets) and returns an array of parsed packet objects.
     *
     * @param string $data The binary RTCP packet data to decode
     * @return list<RtcpPacketInterface> Array of decoded RTCP packet objects
     * @throws RtcpPacketException If the packet is malformed or invalid
     */
    public static function decode(string $data): array
    {
        $pos = 0;
        $packets = [];

        while ($pos < strlen($data)) {
            $header = self::extractHeader($data, $pos);
            $payload = self::extractPayload($data, $pos, $header['length'], $header['padding']);
            $packets[] = self::createPacket($header['packet_type'], $payload, $header['count']);
        }

        return $packets;
    }

    /**
     * Extracts and validates the RTCP header from packet data
     *
     * Parses the common RTCP header fields including version, padding,
     * packet type, and length. Validates the header structure.
     *
     * @param string $data The binary packet data
     * @param int $pos Current position in the data (updated by reference)
     * @return array{version: int, padding: int, count: int, packet_type: int, length: int} Associative array containing parsed header fields:
     *               - version: RTCP protocol version (must be 2)
     *               - padding: Whether padding is present
     *               - count: Report count or packet-specific field
     *               - packet_type: RTCP packet type identifier
     *               - length: Length of the packet in 32-bit words minus one
     * @throws RtcpPacketException If header is invalid or truncated
     */
    private static function extractHeader(string $data, int &$pos): array
    {
        if (strlen($data) < $pos + RtcpConstants::RTCP_HEADER_LENGTH) {
            throw new RtcpPacketException("RTCP packet length is less than " . RtcpConstants::RTCP_HEADER_LENGTH . " bytes");
        }

        $unpacked = unpack("Cv_p_count/Cpacket_type/nlength", substr($data, $pos, 4));
        if ($unpacked === false) {
            throw new RtcpPacketException("RTCP packet header is invalid");
        }

        /** @var array{v_p_count: int, packet_type: int, length: int} $header */
        $header = $unpacked;
        $pos += 4;

        $version = $header['v_p_count'] >> 6;
        if ($version != 2) {
            throw new RtcpPacketException("RTCP packet has invalid version");
        }

        return [
            'version' => $version,
            'padding' => ($header['v_p_count'] >> 5) & 1,
            'count' => $header['v_p_count'] & 0x1F,
            'packet_type' => $header['packet_type'],
            'length' => $header['length'],
        ];
    }

    /**
     * Extracts the payload from an RTCP packet
     *
     * Retrieves the payload data based on the length specified in the header
     * and handles padding if present.
     *
     * @param string $data The binary packet data
     * @param int $pos Current position in the data (updated by reference)
     * @param int $length Length field from header (in 32-bit words minus one)
     * @param int $padding Whether padding is present
     * @return string The extracted payload data (with padding removed if present)
     * @throws RtcpPacketException If payload is truncated or padding is invalid
     */
    private static function extractPayload(string $data, int &$pos, int $length, int $padding): string
    {
        $end = $pos + $length * 4;
        if (strlen($data) < $end) {
            throw new RtcpPacketException("RTCP packet is truncated");
        }

        $payload = substr($data, $pos, $end - $pos);
        $pos = $end;

        if ($padding) {
            self::validatePadding($payload);
            $payload = substr($payload, 0, -ord($payload[strlen($payload) - 1]));
        }

        return $payload;
    }

    /**
     * Validates padding in an RTCP packet
     *
     * Checks that the padding length byte is valid and doesn't exceed
     * the packet length.
     *
     * @param string $payload The packet payload including padding
     * @return void
     * @throws RtcpPacketException If padding length is invalid
     */
    private static function validatePadding(string $payload): void
    {
        if (strlen($payload) == 0 || ord($payload[strlen($payload) - 1]) == 0 || ord($payload[strlen($payload) - 1]) > strlen($payload)) {
            throw new RtcpPacketException("RTCP packet padding length is invalid");
        }
    }

    /**
     * Creates a specific RTCP packet object based on a packet type
     *
     * Factory method that delegates to specialized packet decoders based
     * on the RTCP packet type.
     *
     * @param int $packetType RTCP packet type identifier
     * @param string $payload The decoded payload data
     * @param int $count Report count or packet-specific field
     * @return RtcpPacketInterface Instance of the appropriate RTCP packet type
     * @throws RtcpPacketException If a packet type is unknown
     */
    private static function createPacket(int $packetType, string $payload, int $count): RtcpPacketInterface
    {
        return match ($packetType) {
            RtcpConstants::RTCP_BYE => RtcpByePacket::decode($payload, $count),
            RtcpConstants::RTCP_SDES => RtcpSdesPacket::decode($payload, $count),
            RtcpConstants::RTCP_SR => RtcpSrPacket::decode($payload, $count),
            RtcpConstants::RTCP_RR => RtcpRrPacket::decode($payload, $count),
            RtcpConstants::RTCP_RTPFB => RtcpRtpfbPacket::decode($payload, $count),
            RtcpConstants::RTCP_PSFB => RtcpPsfbPacket::decode($payload, $count),
            default => throw new RtcpPacketException("Unknown RTCP packet type: $packetType"),
        };
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTCP\Exception;

final class RtcpPacketException extends \Exception implements RtcpExceptionInterface
{

}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTCP\Exception;

use Webrtc\Exception\WebrtcExceptionInterface;

interface RtcpExceptionInterface extends WebrtcExceptionInterface
{

}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTCP;

/**
 * RTCP Source Description Item
 *
 * Represents a single source description chunk containing:
 * - SSRC identifier
 * - Array of SDES items (type-length-value tuples)
 *
 * SDES item types include:
 * - CNAME (canonical name) - required
 * - NAME (username)
 * - EMAIL (email address)
 * - PHONE (phone number)
 * - LOC (geographic location)
 * - TOOL (application/tool name)
 * - NOTE (notice/status)
 * - PRIV (private extensions)
 */
final readonly class RtcpSourceInfo
{
    /**
     * Constructs new source description chunk
     *
     * @param int $ssrc Synchronization source identifier
     * @param list<array{0: int, 1: string}> $items Array of SDES items as [type, value] tuples
     */
    public function __construct(private int $ssrc, private array $items = [])
    {
    }

    /**
     * Get SSRC identifier
     *
     * @return int Source identifier
     */
    public function getSsrc(): int
    {
        return $this->ssrc;
    }

    /**
     * Get SDES items
     *
     * @return list<array{0: int, 1: string}> Array of [type, value] tuples
     */
    public function getItems(): array
    {
        return $this->items;
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTCP;

use Webrtc\RTCP\Exception\RtcpPacketException;

/**
 * RTCP Receiver Report (RR) Packet
 *
 * Sent by participants to report reception statistics for one or more sources.
 * Contains:
 * - SSRC of reporting endpoint
 * - Zero or more receiver report blocks (RtcpReceiverInfo)
 *
 * @see https://datatracker.ietf.org/doc/html/rfc3550#section-6.4.2 Defined in RFC 3550 section 6.4.2
 */
final readonly class RtcpRrPacket implements RtcpPacketInterface
{
    /**
     * Constructs a new Receiver Report packet
     *
     * @param int $ssrc SSRC of reporting endpoint
     * @param RtcpReceiverInfo[] $reports Array of receiver report blocks
     */
    public function __construct(private int $ssrc, private array $reports = [])
    {
    }

    /**
     * Encodes RR packet into binary format
     *
     * @return string Binary RTCP RR packet
     */
    #[\Override]
    public function encode(): string
    {
        $payload = pack('N', $this->ssrc);
        foreach ($this->reports as $report) {
            $payload .= $report->encode();
        }
        return $this->packRtcpPacket(count($this->reports), $payload);
    }

    /**
     * Decodes binary data into RR packet
     *
     * @param string $data Binary RR packet data (without header)
     * @param int $count Number of report blocks expected
     * @return self New RtcpRrPacket instance
     * @throws RtcpPacketException If data length doesn't match the expected size
     */
    #[\Override]
    public static function decode(string $data, int $count): self
    {
        $expectedLength = 4 + 24 * $count;
        if (strlen($data) != $expectedLength) {
            throw new RtcpPacketException("RTCP receiver report length is invalid");
        }

        $unpacked = unpack('N', substr($data, 0, 4));
        if ($unpacked === false) {
            throw new RtcpPacketException("RTCP receiver report is invalid");
        }

        /** @var array{1: int} $unpacked */
        $ssrc = $unpacked[1];
        $reports = [];
        $pos = 4;

        for ($i = 0; $i < $count; $i++) {
            $reportData = substr($data, $pos, 24);
            $reports[] = RtcpReceiverInfo::decode($reportData);
            $pos += 24;
        }

        return new self($ssrc, $reports);
    }

    /**
     * Constructs RTCP packet header and combines with payload
     *
     * @param int $count Number of report blocks
     * @param string $payload Packet payload (SSRC + report blocks)
     * @return string Complete RTCP packet
     */
    private function packRtcpPacket(int $count, string $payload): string
    {
        $version = 2;
        $padding = 0;
        $length = strlen($payload) / 4;

        $header = pack('CCn', ($version << 6) | ($padding << 5) | $count, RtcpConstants::RTCP_RR, $length);
        return $header . $payload;
    }

    /**
     * Get reporting endpoint SSRC
     *
     * @return int Synchronization source identifier
     */
    public function getSsrc(): int
    {
        return $this->ssrc;
    }

    /**
     * Get receiver report blocks
     *
     * @return RtcpReceiverInfo[] Array of report blocks
     */
    public function getReports(): array
    {
        return $this->reports;
    }

    public function __toString(): string
    {
        return __CLASS__;
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTCP;

use Webrtc\RTCP\Exception\RtcpPacketException;

/**
 * RTCP Receiver Report Block
 *
 * Represents a single receiver report block within an RTCP Receiver Report (RR) packet.
 * Contains statistics about reception from a single synchronization source.
 *
 * Each block contains:
 * - Fraction of packets lost
 * - Cumulative number of packets lost
 * - Highest sequence number received
 * - Interarrival jitter
 * - Last SR timestamp (LSR)
 * - Delay since last SR (DLSR)
 */
final readonly class RtcpReceiverInfo implements RtcpPacketInterface
{
    /**
     * Constructs a new receiver report block
     *
     * @param int $ssrc The SSRC identifier of the source
     * @param int $fractionLost Fraction of packets lost (8-bit fixed point, 0-255)
     * @param int $packetsLost Cumulative number of packets lost (24-bit signed)
     * @param int $highestSequence Extended highest sequence number received
     * @param int $jitter Interarrival jitter estimate (32-bit unsigned)
     * @param int $lsr Last SR timestamp (middle 32 bits of NTP timestamp)
     * @param int $dlsr Delay since last SR (1/65536 seconds)
     */
    public function __construct(
        private int $ssrc,
        private int $fractionLost,
        private int $packetsLost,
        private int $highestSequence,
        private int $jitter,
        private int $lsr,
        private int $dlsr
    )
    {
    }

    /**
     * Encodes receiver report block into binary format
     *
     * @return string 24-byte binary report block
     */
    #[\Override]
    public function encode(): string
    {
        $data = pack('NC', $this->ssrc, $this->fractionLost);
        $data .= RtcpUtility::packPacketsLost($this->packetsLost);
        $data .= pack('NNNN', $this->highestSequence, $this->jitter, $this->lsr, $this->dlsr);
        return $data;
    }

    /**
     * Decodes binary data into receiver report block
     *
     * @param string $data 24-byte report block data
     * @param int|null $count Unused parameter (for interface compatibility)
     * @return self New RtcpReceiverInfo instance
     * @throws RtcpPacketException If data length is not exactly 24 bytes
     */
    #[\Override]
    public static function decode(string $data, ?int $count = null): self
    {
        if (strlen($data) != 24) {
            throw new RtcpPacketException("Receiver report block length is invalid");
        }

        $unpacked = unpack('Nssrc/CfractionLost', $data);
        if ($unpacked === false) {
            throw new RtcpPacketException("Receiver report block is invalid");
        }

        /** @var array{ssrc: int, fractionLost: int} $unpacked */
        $ssrc = $unpacked['ssrc'];
        $fractionLost = $unpacked['fractionLost'];

        $packetsLost = RtcpUtility::unpackPacketsLost(substr($data, 5, 3));

        $unpacked = unpack('NhighestSequence/Njitter/Nlsr/Ndlsr', substr($data, 8));
        if ($unpacked === false) {
            throw new RtcpPacketException("Receiver report block is invalid");
        }

        /** @var array{highestSequence: int, jitter: int, lsr: int, dlsr: int} $unpacked */
        $highestSequence = $unpacked['highestSequence'];
        $jitter = $unpacked['jitter'];
        $lsr = $unpacked['lsr'];
        $dlsr = $unpacked['dlsr'];

        return new self($ssrc, $fractionLost, $packetsLost, $highestSequence, $jitter, $lsr, $dlsr);
    }

    /**
     * Get delay since last SR (DLSR)
     *
     * @return int Delay in 1/65536 second units
     */
    public function getDlsr(): int
    {
        return $this->dlsr;
    }

    /**
     * Get a fraction of packets lost
     *
     * @return int 8-bit fixed point number (0-255)
     */
    public function getFractionLost(): int
    {
        return $this->fractionLost;
    }

    /**
     * Get cumulative packets lost
     *
     * @return int 24-bit signed integer
     */
    public function getPacketsLost(): int
    {
        return $this->packetsLost;
    }

    /**
     * Get the highest sequence number received
     *
     * @return int Extended highest sequence number
     */
    public function getHighestSequence(): int
    {
        return $this->highestSequence;
    }

    /**
     * Get interarrival jitter estimate
     *
     * @return int Jitter in timestamp units
     */
    public function getJitter(): int
    {
        return $this->jitter;
    }

    /**
     * Get last SR timestamp (LSR)
     *
     * @return int Middle 32 bits of last SR's NTP timestamp
     */
    public function getLsr(): int
    {
        return $this->lsr;
    }

    /**
     * Get source SSRC
     *
     * @return int Synchronization source identifier
     */
    public function getSsrc(): int
    {
        return $this->ssrc;
    }

    public function __toString(): string
    {
        return __CLASS__;
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTCP;

use Webrtc\RTCP\Exception\RtcpPacketException;

/**
 * RTCP Utility Functions
 *
 * Provides helper methods for RTCP packet manipulation:
 * - Packet loss encoding/decoding
 * - RTCP packet construction
 */
final class RtcpUtility
{
    /**
     * Encodes packet loss count into 24-bit format
     *
     * @param int $packetsLost 24-bit signed packet loss count
     * @return string 3-byte binary representation
     */
    public static function packPacketsLost(int $packetsLost): string
    {
        return substr(pack("N", $packetsLost), 1);
    }

    /**
     * Decodes 24-bit packet loss count
     *
     * @param string $data 3-byte binary packet loss data
     * @return int Signed packet loss count
     */
    public static function unpackPacketsLost(string $data): int
    {
        $data = (ord($data[0]) & 0x80) ? "\xFF" . $data : "\x00" . $data;
        $unpacked = unpack("N", $data);
        if ($unpacked === false) {
            return 0;
        }

        /** @var int $value */
        $value = $unpacked[1];

        if ($value & 0x80000000) {
            $value -= 0x100000000;
        }

        return $value;
    }

    /**
     * Constructs RTCP packet header
     *
     * @param int $packetType RTCP packet type constant
     * @param int $count Report count or subtype
     * @param string $payload Packet payload
     * @return string Complete RTCP packet
     * @throws RtcpPacketException If payload length isn't 32-bit aligned
     */
    public static function packRtcpPacket(int $packetType, int $count, string $payload): string
    {
        if (strlen($payload) % 4 !== 0) {
            throw new RtcpPacketException("Payload length must be a multiple of 4");
        }
        return pack("CCn", (2 << 6) | $count, $packetType, strlen($payload) / 4) . $payload;
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTCP;

use Webrtc\RTCP\Exception\RtcpPacketException;

/**
 * RTCP Sender Report Information
 *
 * Contains timing and statistics information sent in SR packets.
 * Includes:
 * - NTP timestamp (64 bits)
 * - RTP timestamp (32 bits)
 * - Packet count (32 bits)
 * - Octet count (32 bits)
 *
 * Part of RTCP Sender Report (SR) packet defined in RFC 3550 section 6.4.1
 */
final readonly class RtcpSenderInfo implements RtcpPacketInterface
{
    /**
     * Constructs new sender info block
     *
     * @param int $rtpTimestamp Media timestamp
     * @param int $packetCount Total packets sent
     * @param int $octetCount Total payload bytes sent
     */
    public function __construct(
        private int $ntpTimestampHigh,
        private int $ntpTimestampLow,
        private int    $rtpTimestamp,
        private int    $packetCount,
        private int    $octetCount
    ) {
    }

    /**
     * Encodes sender info into binary format
     *
     * @return string 20-byte binary sender info block
     */
    #[\Override]
    public function encode(): string
    {
        return pack(
            'NNNNN',
            $this->ntpTimestampHigh,
            $this->ntpTimestampLow,
            $this->rtpTimestamp,
            $this->packetCount,
            $this->octetCount
        );
    }

    /**
     * Decodes binary data into sender info
     *
     * @param string $data 20-byte sender info block
     * @param int|null $count Unused (interface compatibility)
     * @return self New RtcpSenderInfo instance
     * @throws RtcpPacketException If data length is not 20 bytes
     */
    #[\Override]
    public static function decode(string $data, ?int $count = null): self
    {
        if (strlen($data) != 20) {
            throw new RtcpPacketException("Sender information block length is invalid");
        }

        $unpacked = unpack('NntpHigh/NntpLow/NrtpTimestamp/NpacketCount/NoctetCount', $data);
        if ($unpacked === false) {
            throw new RtcpPacketException("Sender information block is invalid");
        }

        /** @var array{ntpHigh: int, ntpLow: int, rtpTimestamp: int, packetCount: int, octetCount: int} $unpacked */
        return new self(
            $unpacked['ntpHigh'],
            $unpacked['ntpLow'],
            $unpacked['rtpTimestamp'],
            $unpacked['packetCount'],
            $unpacked['octetCount']
        );
    }

    /**
     * Get NTP timestamp
     */
    public function getNtpTimestampHigh(): int
    {
        return $this->ntpTimestampHigh;
    }

    public function getNtpTimestampLow(): int
    {
        return $this->ntpTimestampLow;
    }

    /**
     * Get RTP timestamp
     *
     * @return int 32-bit media timestamp
     */
    public function getRtpTimestamp(): int
    {
        return $this->rtpTimestamp;
    }

    /**
     * Get packet count
     *
     * @return int Total packets sent
     */
    public function getPacketCount(): int
    {
        return $this->packetCount;
    }

    /**
     * Get octet count
     *
     * @return int Total payload bytes sent
     */
    public function getOctetCount(): int
    {
        return $this->octetCount;
    }

    public function __toString(): string
    {
        return __CLASS__;
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTCP;

use Webrtc\RTCP\Exception\RtcpPacketException;

/**
 * RTCP Payload-Specific Feedback Packet (PSFB)
 *
 * Represents an RTCP Payload-Specific Feedback packet as defined in RFC 4585.
 * These packets provide feedback specific to a particular payload format,
 * such as picture loss indications or slice loss indications in video streams.
 *
 * Common PSFB message types include:
 * - PLI (Picture Loss Indication)
 * - SLI (Slice Loss Indication)
 * - RPSI (Reference Picture Selection Indication)
 * - FIR (Full Intra Request)
 */
final readonly class RtcpPsfbPacket implements RtcpPacketInterface
{
    /**
     * Constructs a new RtcpPsfbPacket instance.
     *
     * @param int $fmt Feedback message type (FMT) - specific to payload format
     * @param int $ssrc The synchronization source identifier of the feedback sender
     * @param int $mediaSsrc The synchronization source identifier of the media source
     * @param string $fci Feedback control information (format depends on FMT)
     */
    public function __construct(
        private int    $fmt,
        private int    $ssrc,
        private int    $mediaSsrc,
        private string $fci = ''
    )
    {
    }

    /**
     * Encodes the PSFB packet into binary format
     *
     * Constructs the complete RTCP packet including header and payload:
     * - 4-byte header (version, padding, count, packet type, length)
     * - 8-byte sender and media SSRC fields
     * - Variable length FCI data
     *
     * @return string Binary representation of the PSFB packet
     */
    #[\Override]
    public function encode(): string
    {
        $payload = pack('NN', $this->ssrc, $this->mediaSsrc) . $this->fci;
        return $this->packRtcpPacket($this->fmt, $payload);
    }

    /**
     * Decodes binary data into an RtcpPsfbPacket instance
     *
     * @param string $data Binary PSFB packet data (without header)
     * @param int $count Feedback message type from header
     * @return self New RtcpPsfbPacket instance
     * @throws RtcpPacketException If data is too short (less than 8 bytes)
     */
    #[\Override]
    public static function decode(string $data, int $count): self
    {
        if (strlen($data) < 8) {
            throw new RtcpPacketException("RTCP payload-specific feedback length is invalid");
        }

        $unpacked = unpack('Nssrc/NmediaSsrc', $data);
        if ($unpacked === false) {
            throw new RtcpPacketException("RTCP payload-specific feedback is invalid");
        }

        /** @var array{ssrc: int, mediaSsrc: int} $unpacked */
        $ssrc = $unpacked['ssrc'];
        $mediaSsrc = $unpacked['mediaSsrc'];
        $fci = substr($data, 8);

        return new self($count, $ssrc, $mediaSsrc, $fci);
    }

    /**
     * Constructs RTCP packet header and combines with payload
     *
     * @param int $fmt Feedback message type
     * @param string $payload Packet payload (SSRCs + FCI)
     * @return string Complete RTCP packet
     */
    private function packRtcpPacket(int $fmt, string $payload): string
    {
        $version = 2; // RTCP version
        $padding = 0; // No padding
        $length = strlen($payload) / 4; // Length in 32-bit words

        $header = pack('CCn', ($version << 6) | ($padding << 5) | $fmt, RtcpConstants::RTCP_PSFB, $length);
        return $header . $payload;
    }

    /**
     * Get a feedback message type (FMT)
     *
     * @return int Feedback message type identifier
     */
    public function getFmt(): int
    {
        return $this->fmt;
    }

    /**
     * Get sender SSRC
     *
     * @return int Synchronization source identifier of feedback sender
     */
    public function getSsrc(): int
    {
        return $this->ssrc;
    }

    /**
     * Get media SSRC
     *
     * @return int Synchronization source identifier of a media source
     */
    public function getMediaSsrc(): int
    {
        return $this->mediaSsrc;
    }

    /**
     * Get feedback control information
     *
     * @return string Format-specific feedback data
     */
    public function getFci(): string
    {
        return $this->fci;
    }

    public function __toString(): string
    {
        return __CLASS__;
    }
}{
    "name": "danog/php-rtc-rtcp",
    "type": "library",
    "description": "Pure-PHP RTCP packet parsing and generation. MadelineProto's PHP 8.2+ fork fixes unsigned NTP handling and integrates with the modular danog/php-rtc stack.",
    "keywords": [
        "rtp",
        "rtcp"
    ],
    "homepage": "https://github.com/danog/php-rtc-rtcp",
    "license": "BSD-3-Clause",
    "authors": [
        {
            "name": "Amin Yazdanpanah",
            "email": "github@aminyazdanpanah.com"
        },
        {
            "name": "Sana Moniri",
            "email": "s.mo@quasarstream.com"
        },
        {
            "name": "Quasar Stream Community"
        },
        {
            "name": "Daniil Gentili",
            "email": "daniil@daniil.it"
        }
    ],
    "require": {
        "php-64bit": "^8.2",
        "danog/php-rtc-exception": "^1.1.5"
    },
    "require-dev": {
        "phpunit/phpunit": "^11.5 || ^12.0 || ^13.3.2",
        "bamarni/composer-bin-plugin": "^1.9.1"
    },
    "autoload": {
        "psr-4": {
            "Webrtc\\RTCP\\": "src"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\Webrtc\\RTCP\\": "tests"
        }
    },
    "minimum-stability": "stable",
    "scripts": {
        "post-install-cmd": [
            "@composer bin all install --ansi"
        ],
        "post-update-cmd": [
            "@composer bin all update --ansi"
        ]
    },
    "config": {
        "allow-plugins": {
            "bamarni/composer-bin-plugin": true
        }
    },
    "extra": {
        "bamarni-bin": {
            "bin-links": true,
            "forward-command": true
        }
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\ICE;

use Webrtc\Exception\InvalidArgumentException;
use Webrtc\ICE\Enum\CandidateType;
use Webrtc\ICE\Enum\TransportType;
use Webrtc\STUN\IceCandidateInterface;
use Override;

/**
 * RTCIceCandidate - ICE Candidate Representation
 *
 * Represents a candidate for establishing network connections in WebRTC ICE protocol.
 * Handles both local and remote candidates with all necessary attributes for ICE negotiation.
 *
 * Key Features:
 * - Represents ICE candidates with all SDP attributes
 * - Supports both UDP and TCP transport protocols
 * - Handles host, server reflexive, peer reflexive, and relayed candidate types
 * - Provides SDP parsing and generation
 * - Implements candidate pairing logic
 * - Follows RFC 8445 (ICE) specifications
 *
 * @see https://datatracker.ietf.org/doc/html/rfc8445 ICE RFC 8445
 * @api
 */
class RTCIceCandidate implements IceCandidateInterface
{
    private string $protocolId;
    private string $host;
    private int $port;
    private CandidateType $type;
    private TransportType $transport = TransportType::udp;
    private ?string $relatedAddress = null;
    private ?int $relatedPort = null;
    private ?string $tcpType = null;
    private ?int $generation = null;
    private ?int $sdpMid = null;
    private ?int $sdpMLineIndex = null;
    private ?int $priority;
    private ?string $foundation;

    /**
     * Constructor - Creates a new ICE candidate
     *
     * @param int $componentId The component ID (1 for RTP, 2 for RTCP)
     */
    public function __construct(private readonly int $componentId)
    {
        $this->protocolId = '';
        $this->host = '';
        $this->port = 0;
        $this->type = CandidateType::host;
        $this->priority = null;
        $this->foundation = null;
    }

    /**
     * Get the component ID (1 for RTP, 2 for RTCP)
     *
     * @return int
     */
    #[Override]
    public function getComponentId(): int
    {
        return $this->componentId;
    }

    /**
     * Get the candidate's IP address
     *
     * @return string
     */
    #[Override]
    public function getHost(): string
    {
        return $this->host;
    }

    /**
     * Set the candidate's IP address
     *
     * @param string $host
     */
    public function setHost(string $host): void
    {
        $this->host = $host;
    }

    /**
     * Get the candidate type (host, srflx, prflx, relay)
     *
     * @return CandidateType
     */
    public function getType(): CandidateType
    {
        return $this->type;
    }

    /**
     * Set the candidate type
     *
     * @param CandidateType $type
     */
    public function setType(CandidateType $type): void
    {
        $this->type = $type;
    }

    /**
     * Get the candidate's port number
     *
     * @return int
     */
    #[Override]
    public function getPort(): int
    {
        return $this->port;
    }

    /**
     * Set the candidate's port number
     *
     * @param int $port
     */
    public function setPort(int $port): void
    {
        $this->port = $port;
    }

    /**
     * Get the transport protocol (udp/tcp)
     *
     * @return TransportType
     */
    public function getTransport(): TransportType
    {
        return $this->transport;
    }

    /**
     * Set the transport protocol
     *
     * @param TransportType $transport
     */
    public function setTransport(TransportType $transport): void
    {
        $this->transport = $transport;
    }

    /**
     * Get the related address (for reflexive candidates)
     *
     * @return string|null
     */
    public function getRelatedAddress(): ?string
    {
        return $this->relatedAddress;
    }

    /**
     * Set the related address
     *
     * @param string|null $relatedAddress
     */
    public function setRelatedAddress(?string $relatedAddress): void
    {
        $this->relatedAddress = $relatedAddress;
    }

    /**
     * Get the related port (for reflexive candidates)
     *
     * @return int|null
     */
    public function getRelatedPort(): ?int
    {
        return $this->relatedPort;
    }

    /**
     * Set the related port
     *
     * @param int|null $relatedPort
     */
    public function setRelatedPort(?int $relatedPort): void
    {
        $this->relatedPort = $relatedPort;
    }

    /**
     * Get the TCP type (active/passive/so) for TCP candidates
     *
     * @return string|null
     */
    public function getTcpType(): ?string
    {
        return $this->tcpType;
    }

    /**
     * Set the TCP type
     *
     * @param string|null $tcpType
     */
    public function setTcpType(?string $tcpType): void
    {
        $this->tcpType = $tcpType;
    }

    /**
     * Get the generation identifier for ICE restarts
     *
     * @return int|null
     */
    public function getGeneration(): ?int
    {
        return $this->generation;
    }

    /**
     * Set the generation identifier
     *
     * @param int|null $generation
     */
    public function setGeneration(?int $generation): void
    {
        $this->generation = $generation;
    }

    /**
     * Set the candidate priority
     *
     * @param int|null $priority
     */
    public function setPriority(?int $priority): void
    {
        $this->priority = $priority;
    }

    /**
     * Set the foundation string
     *
     * @param string|null $foundation
     */
    public function setFoundation(?string $foundation): void
    {
        $this->foundation = $foundation;
    }

    /**
     * Get the foundation string (unique identifier for candidate pairs)
     *
     * If not explicitly set, generates one based on candidate properties
     *
     * @return string
     * @see https://datatracker.ietf.org/doc/html/rfc8445#section-5.1.1.3
     */
    public function getFoundation(): string
    {
        return $this->foundation ?? \md5(implode("|", [$this->type->name, $this->transport->name, $this->host]));
    }

    /**
     * Calculate the candidate priority according to RFC 8445
     *
     * @param int|null $type Optional type preference override
     * @param int $localPreference Local preference value (default: 65535)
     * @return int Calculated priority value
     * @see https://datatracker.ietf.org/doc/html/rfc8445#section-5.1.2.1
     */
    public function getPriority(?int $type = null, int $localPreference = 0xffff): int
    {
        return $this->priority ?? (1 << 24) * ($type ?? $this->type->value) + (1 << 8) * $localPreference + (256 - $this->componentId);
    }

    /**
     * Parse an SDP candidate string into an RTCIceCandidate object
     *
     * Example SDP format:
     * "foundation componentId transport priority host port typ type [extensions]"
     *
     * @param string $sdp The SDP candidate string
     * @return self
     * @throws InvalidArgumentException If SDP format is invalid
     *
     * @example
     * $candidate = RTCIceCandidate::parseSDP(
     *     "ac1986cbcd1877717d1c1b5528d50921 1 udp 2130706431 192.168.1.1 32652 typ host"
     * );
     */
    public static function parseSDP(string $sdp): self
    {
        $sdpParts = explode(" ", $sdp);

        if (count($sdpParts) < 8) {
            throw new InvalidArgumentException("SDP does not have enough properties");
        }

        $candidate = new self(self::parseIntField($sdpParts[1], "component id"));
        $candidate->setFoundation($sdpParts[0] ?? null);
        $candidate->setPriority(self::parseIntField($sdpParts[3], "priority"));
        $candidate->setTransport(match (strtolower($sdpParts[2])) {
            'udp' => TransportType::udp,
            'tcp' => TransportType::tcp,
            default => throw new InvalidArgumentException("Invalid transport type"),
        });
        $candidate->setHost($sdpParts[4]);
        $candidate->setPort(self::parseIntField($sdpParts[5], "port"));
        $candidate->setType(match ($sdpParts[7]) {
            'host' => CandidateType::host,
            'srflx' => CandidateType::srflx,
            'prflx' => CandidateType::prflx,
            'relay' => CandidateType::relay,
            default => throw new InvalidArgumentException("Invalid candidate type"),
        });

        // Parse extensions (raddr, rport, tcptype, generation)
        for ($i = 8; $i < count($sdpParts); $i += 2) {
            switch ($sdpParts[$i]) {
                case "raddr":
                    $candidate->setRelatedAddress($sdpParts[$i + 1]);
                    break;
                case "rport":
                    $candidate->setRelatedPort(self::parseIntField($sdpParts[$i + 1] ?? '', "rport"));
                    break;
                case "tcptype":
                    $candidate->setTcpType($sdpParts[$i + 1]);
                    break;
                case "generation":
                    $candidate->setGeneration(self::parseIntField($sdpParts[$i + 1] ?? '', "generation"));
                    break;
            }
        }

        return $candidate;
    }

    /**
     * Parses a non-negative integer field from an untrusted remote ICE candidate line.
     *
     * A bare (int) cast turns a malformed token into 0 â€” an invalid port/priority/component is
     * then accepted as a well-formed candidate with garbage values and only fails opaquely during
     * connectivity checks, hiding that the candidate line itself was malformed.
     *
     * @throws InvalidArgumentException When $value is not a base-10 non-negative integer.
     */
    private static function parseIntField(string $value, string $field): int
    {
        if (!ctype_digit($value)) {
            throw new InvalidArgumentException("Invalid $field in ICE candidate, expected a non-negative integer: \"$value\"");
        }
        return (int)$value;
    }

    /**
     * Convert candidate to SDP string format
     *
     * @return string SDP-formatted candidate string
     */
    public function convert2SDP(): string
    {
        $sdp = sprintf(
            "%s %d %s %d %s %d typ %s",
            $this->getFoundation(),
            $this->componentId,
            $this->transport->name,
            $this->getPriority(),
            $this->host,
            $this->port,
            $this->type->name
        );

        // Add optional parameters if present
        $optionalParams = [
            'raddr' => $this->relatedAddress,
            'rport' => $this->relatedPort,
            'tcptype' => $this->tcpType,
            'generation' => $this->generation
        ];

        foreach ($optionalParams as $key => $value) {
            if ($value !== null) {
                $sdp .= sprintf(" %s %s", $key, $value);
            }
        }

        return $sdp;
    }

    /**
     * Check if this candidate can be paired with another candidate
     *
     * Candidates are pairable if they have:
     * - Same IP version
     * - Same transport protocol
     * - Same component ID
     *
     * @param RTCIceCandidate $candidate The candidate to check against
     * @return bool True if pairable, false otherwise
     */
    public function isPairableWith(RTCIceCandidate $candidate): bool
    {
        return Utils::IPVersion($this->host) === Utils::IPVersion($candidate->getHost())
            && $this->transport === $candidate->getTransport()
            && $this->componentId === $candidate->getComponentId();
    }

    /**
     * Get the protocol ID
     *
     * @return string
     */
    public function getProtocolId(): string
    {
        return $this->protocolId;
    }

    /**
     * Set the protocol ID
     *
     * @param string $protocolId
     */
    public function setProtocolId(string $protocolId): void
    {
        $this->protocolId = $protocolId;
    }

    /**
     * String representation of the candidate (SDP format)
     *
     * @return string
     */
    public function __toString(): string
    {
        return $this->convert2SDP();
    }

    /**
     * Create a new candidate with all properties
     *
     * @param string|null $protocolId Protocol identifier
     * @param int $componentId Component ID (1 for RTP, 2 for RTCP)
     * @param string $host IP address
     * @param int $port Port number
     * @param CandidateType $type Candidate type
     * @param TransportType $transportType Transport protocol
     * @param string|null $relatedAddress Related address for reflexive candidates
     * @param int|null $relatedPort Related port for reflexive candidates
     * @param string|null $tcpType TCP type for TCP candidates
     * @param string|null $generation Generation identifier
     * @return self
     */
    public static function create(
        ?string $protocolId,
        int $componentId,
        string $host,
        int $port,
        CandidateType $type,
        TransportType $transportType = TransportType::udp,
        ?string $relatedAddress = null,
        ?int $relatedPort = null,
        ?string $tcpType = null,
        ?string $generation = null
    ): self {
        $candidate = new self($componentId);
        if ($protocolId !== null) {
            $candidate->setProtocolId($protocolId);
        }
        $candidate->setHost($host);
        $candidate->setPort($port);
        $candidate->setType($type);
        $candidate->setTransport($transportType);
        $candidate->setRelatedAddress($relatedAddress);
        $candidate->setRelatedPort($relatedPort);
        $candidate->setTcpType($tcpType);
        $candidate->setGeneration($generation !== null ? (int)$generation : null);

        return $candidate;
    }

    /**
     * Get the SDP media stream identification tag
     *
     * @return int|null
     */
    public function getSdpMid(): ?int
    {
        return $this->sdpMid;
    }

    /**
     * Set the SDP media stream identification tag
     *
     * @param int|null $sdpMid
     */
    public function setSdpMid(?int $sdpMid): void
    {
        $this->sdpMid = $sdpMid;
    }

    /**
     * Get the SDP media description line index
     *
     * @return int|null
     */
    public function getSdpMLineIndex(): ?int
    {
        return $this->sdpMLineIndex;
    }

    /**
     * Set the SDP media description line index
     *
     * @param int|null $sdpMLineIndex
     */
    public function setSdpMLineIndex(?int $sdpMLineIndex): void
    {
        $this->sdpMLineIndex = $sdpMLineIndex;
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\ICE;

use Amp\Socket\InternetAddress;
use Webrtc\ICE\Enum\RTCIceCandidatePairStats;
use Webrtc\STUN\IceConnectionProtocolInterface;

/**
 * RTCIceCandidatePair Class
 *
 * Represents a pair of ICE candidates (local and remote) that form a potential
 * connection path for WebRTC communication. This class manages the state of the
 * candidate pair and provides information about the connection endpoints.
 *
 * ICE candidate pairs are created by combining a local candidate with a remote
 * candidate, and they undergo various states during the ICE connectivity checks
 * as defined in RFC 8445. A nominated candidate pair is the one selected for
 * actual data transmission.
 *
 * @see https://datatracker.ietf.org/doc/html/rfc8445#section-6.1.2.2
 */
final class RTCIceCandidatePair
{
    /**
     * Indicates whether the local agent has nominated this candidate pair
     *
     * A nominated pair is the candidate pair selected for data transmission
     *
     * @var bool
     */
    private bool $nominated = false;

    /**
     * Indicates whether the remote agent has nominated this candidate pair
     *
     * @var bool
     */
    private bool $remoteNominated = false;

    /**
     * The current state of this candidate pair in the ICE connectivity check process
     *
     * @var RTCIceCandidatePairStats
     */
    private RTCIceCandidatePairStats $state = RTCIceCandidatePairStats::FROZEN;

    /** Smoothed round-trip time of the binding requests sent on this pair, in seconds. */
    private ?float $rtt = null;

    /** How many round-trip times were measured. */
    private int $rttSamples = 0;

    /** When a binding request sent on this pair was last answered (microtime). */
    private ?float $lastResponseAt = null;

    /** When a keepalive binding request was last sent on this pair (microtime). */
    private ?float $lastPingAt = null;

    /** Whether a keepalive binding request is waiting for its response. */
    private bool $pinging = false;

    /**
     * Creates a new RTCIceCandidatePair instance
     *
     * @param IceConnectionProtocolInterface $protocol The protocol used for connection
     *                                                (contains the local candidate)
     * @param RTCIceCandidate $remoteCandidate The remote ICE candidate
     */
    /** The address of the remote candidate, and the host and port it was created from. */
    private ?InternetAddress $remoteAddress = null;
    private ?string $remoteHost = null;
    private ?int $remotePort = null;

    public function __construct(private readonly IceConnectionProtocolInterface $protocol, private readonly RTCIceCandidate $remoteCandidate)
    {
    }

    /**
     * Gets the component ID of the local candidate
     *
     * The component ID identifies the specific component within an ICE session
     * (e.g., 1 for RTP, 2 for RTCP)
     *
     * @return int The component ID
     */
    public function getComponentId(): int
    {
        $candidate = $this->protocol->getCandidate();
        assert($candidate !== null);
        return $candidate->getComponentId();
    }

    /**
     * Gets the local candidate's address information
     *
     * @return InternetAddress The local candidate address
     */
    public function getLocalAddress(): InternetAddress
    {
        $candidate = $this->protocol->getCandidate();
        assert($candidate !== null);
        /** @var int<0, 65535> $port */
        $port = $candidate->getPort();
        return new InternetAddress($candidate->getHost(), $port);
    }

    /**
     * Gets the local ICE candidate object
     *
     * @return RTCIceCandidate The local ICE candidate
     */
    public function getLocalCandidate(): RTCIceCandidate
    {
        $candidate = $this->protocol->getCandidate();
        assert($candidate instanceof RTCIceCandidate);
        return $candidate;
    }

    /**
     * Gets the remote candidate's address information
     *
     * @return InternetAddress The remote candidate address
     */
    public function getRemoteAddress(): InternetAddress
    {
        $host = $this->remoteCandidate->getHost();
        /** @var int<0, 65535> $port */
        $port = $this->remoteCandidate->getPort();
        // Used for every packet sent: reuse the address, as long as the candidate doesn't change.
        if ($this->remoteAddress === null || $this->remoteHost !== $host || $this->remotePort !== $port) {
            $this->remoteAddress = new InternetAddress($host, $port);
            $this->remoteHost = $host;
            $this->remotePort = $port;
        }
        return $this->remoteAddress;
    }

    /**
     * Gets the protocol interface used for the ICE connection
     *
     * The protocol interface provides access to the underlying transport protocol
     * (UDP, TCP, etc.) and contains the local candidate information
     *
     * @return IceConnectionProtocolInterface The protocol interface
     */
    public function getProtocol(): IceConnectionProtocolInterface
    {
        return $this->protocol;
    }

    /**
     * Gets the remote ICE candidate object
     *
     * @return RTCIceCandidate The remote ICE candidate
     */
    public function getRemoteCandidate(): RTCIceCandidate
    {
        return $this->remoteCandidate;
    }

    /**
     * Checks if the remote agent has nominated this candidate pair
     *
     * Remote nomination occurs when the remote peer has selected this candidate pair
     * for data transmission
     *
     * @return bool True if nominated by the remote agent, false otherwise
     */
    public function isRemoteNominated(): bool
    {
        return $this->remoteNominated;
    }

    /**
     * Sets the remote nomination status for this candidate pair
     *
     * @param bool $remoteNominated True to mark as nominated by the remote agent
     * @return void
     */
    public function setRemoteNominated(bool $remoteNominated): void
    {
        $this->remoteNominated = $remoteNominated;
    }

    /**
     * Checks if the local agent has nominated this candidate pair
     *
     * Local nomination occurs when the local peer has selected this candidate pair
     * for data transmission
     *
     * @return bool True if nominated by the local agent, false otherwise
     */
    public function isNominated(): bool
    {
        return $this->nominated;
    }

    /**
     * Sets the local nomination status for this candidate pair
     *
     * @param bool $nominated True to mark as nominated by the local agent
     * @return void
     */
    public function setNominated(bool $nominated): void
    {
        $this->nominated = $nominated;
    }

    /**
     * Returns a string representation of this candidate pair
     *
     * Includes local address, remote address, and current state for debugging
     *
     * @return string The string representation
     */
    public function __toString(): string
    {
        return sprintf("CandidatePair(Local Address: %s -> Remote Address: %s | State: %s)", $this->getLocalAddress(), $this->getRemoteAddress(), $this->state->name);
    }

    /**
     * Gets the current state of this candidate pair
     *
     * The state represents the progress of the ICE connectivity check for this pair
     * (e.g., FROZEN, IN_PROGRESS, SUCCEEDED, FAILED)
     *
     * @return RTCIceCandidatePairStats The current state
     */
    public function getState(): RTCIceCandidatePairStats
    {
        return $this->state;
    }

    /**
     * Sets the state of this candidate pair
     *
     * Updates the state as the ICE connectivity check progresses
     *
     * @param RTCIceCandidatePairStats $state The new state
     * @return void
     */
    public function setState(RTCIceCandidatePairStats $state): void
    {
        $this->state = $state;
    }

    /**
     * Records the response to a binding request sent on this pair.
     *
     * @param float $rtt How long the response took, in seconds.
     */
    public function recordResponse(float $rtt, float $now): void
    {
        // Smoothed like TCP's (RFC 6298), but reacting faster to a path getting slower.
        $this->rtt = $this->rtt === null ? $rtt : 0.75 * $this->rtt + 0.25 * $rtt;
        $this->rttSamples++;
        $this->lastResponseAt = $now;
    }

    /**
     * Considers this pair answered now, if it never was: for a pair that became valid without a timed request.
     */
    public function touch(float $now): void
    {
        $this->lastResponseAt ??= $now;
    }

    /**
     * Gets the smoothed round-trip time of this pair, in seconds, if measured.
     */
    public function getRtt(): ?float
    {
        return $this->rtt;
    }

    /**
     * Gets how many round-trip times were measured on this pair.
     */
    public function getRttSamples(): int
    {
        return $this->rttSamples;
    }

    /**
     * Gets when a binding request sent on this pair was last answered (microtime), if ever.
     */
    public function getLastResponseAt(): ?float
    {
        return $this->lastResponseAt;
    }

    /**
     * Whether a binding request sent on this pair was answered in the specified number of seconds.
     */
    public function respondedWithin(float $seconds, float $now): bool
    {
        return $this->lastResponseAt !== null && $now - $this->lastResponseAt <= $seconds;
    }

    /**
     * Gets when a keepalive binding request was last sent on this pair (microtime), if ever.
     */
    public function getLastPingAt(): ?float
    {
        return $this->lastPingAt;
    }

    /**
     * Whether a keepalive binding request is waiting for its response.
     */
    public function isPinging(): bool
    {
        return $this->pinging;
    }

    /**
     * Records that a keepalive binding request was sent, or that it got answered or timed out.
     */
    public function setPinging(bool $pinging, float $now): void
    {
        $this->pinging = $pinging;
        if ($pinging) {
            $this->lastPingAt = $now;
        }
    }

    /**
     * Restarts the liveness bookkeeping, as after the agent was serialized and restored: the timestamps
     * of another process mean nothing, and the requests it was waiting for won't be answered to it.
     */
    public function resetLiveness(float $now): void
    {
        if ($this->lastResponseAt !== null) {
            $this->lastResponseAt = $now;
        }
        $this->lastPingAt = null;
        $this->pinging = false;
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\ICE;

interface RTCIceServerInterface
{
    public function getUrls(): array;

    public function getUsername(): ?string;

    public function getCredential(): ?string;

    public function getCredentialType(): string;
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\ICE;

use Random\RandomException;
use function parse_url;

/**
 * Utility class for ICE-related operations.
 */
final class Utils
{
    /**
     * Generate a random alphanumeric string of a given length.
     *
     * @param int $length The desired length of the string.
     * @return string The generated random string.
     * @throws RandomException If randomness cannot be generated securely.
     */
    public static function getRandomString(int $length): string
    {
        $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
        $charactersLength = strlen($characters);
        $randomString = '';

        for ($i = 0; $i < $length; $i++) {
            $index = random_int(0, $charactersLength - 1);
            $randomString .= $characters[$index];
        }

        return $randomString;
    }

    /**
     * Determine the IP version of a given IP address.
     *
     * @param string $ip The IP address to check.
     * @return int|false Returns 4 for IPv4, 6 for IPv6, or false if invalid.
     */
    public static function IPVersion(string $ip): int|false
    {
        $packedIp = @inet_pton($ip);
        if ($packedIp === false) {
            return false;
        }

        return isset($packedIp[4]) ? 6 : 4;
    }

    /**
     * Generate a random 64-bit integer.
     *
     * @return int The random 64-bit integer.
     * @throws RandomException If randomness cannot be generated securely.
     */
    public static function generateRandom64BitInt(): int
    {
        return random_int(PHP_INT_MIN, PHP_INT_MAX);
    }

    /**
     * Parse an address string to extract host and port.
     *
     * @param string $address The input address (with or without a scheme).
     * @return array{0: string|null, 1: int|null} Array of host and port.
     */
    public static function parseAddressToHostPort(string $address): array
    {
        if (!str_contains($address, '://')) {
            $address = 'udp://' . $address;
        }

        $address = parse_url($address);
        if ($address === false) {
            return [null, null];
        }

        $host = $address['host'] ?? null;
        $port = $address['port'] ?? null;

        return [is_string($host) ? trim($host, '[]') : null, is_int($port) ? $port : null];
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\ICE;

use Webrtc\Mixin\DataClass;

#[DataClass]
final class RTCIceParameters
{
    /**
     * @param string|null $usernameFragment Remote/Local Ice username
     * @param string|null $password Remote/Local Ice password
     * @param bool $iceLite Remote/Local if is ice lite
     */
    public function __construct(
        public ?string $usernameFragment = null,
        public ?string $password = null,
        public bool    $iceLite = false
    )
    {
    }
}<?php

namespace Webrtc\ICE\Listener;

use Webrtc\ICE\Enum\IceGatheringState;

/**
 * Notified when an {@see \Webrtc\ICE\RTCIceGatherer} changes its gathering state.
 *
 * A typed replacement for the former Evenement "statechange" event: the listener object is
 * registered on the gatherer and, being an ordinary object rather than a closure, survives a
 * serialize cycle as part of the graph with no wrapper.
 */
interface IceGathererStateChangeListener
{
    public function onIceGathererStateChange(IceGatheringState $state): void;
}
<?php

namespace Webrtc\ICE\Listener;

/**
 * Notified when an {@see \Webrtc\ICE\RTCIceConnection} closes (the former "onClose" event).
 */
interface IceConnectionClosedListener
{
    public function onIceConnectionClosed(): void;
}
<?php

namespace Webrtc\ICE\Listener;

use Webrtc\ICE\Enum\IceTransportState;

/**
 * Notified when an {@see \Webrtc\ICE\RTCIceTransport} changes its transport state.
 *
 * A typed replacement for the former Evenement "statechange" event: the listener object is
 * registered on the transport and, being an ordinary object rather than a closure, survives a
 * serialize cycle as part of the graph with no wrapper.
 */
interface IceTransportStateChangeListener
{
    public function onIceTransportStateChange(IceTransportState $state): void;
}
<?php

namespace Webrtc\ICE\Listener;

/**
 * Notified when an {@see \Webrtc\ICE\RTCIceTransportInterface} reports a disconnecting close or
 * error, replacing the former Evenement "close"/"error" events.
 */
interface IceTransportDisconnectListener
{
    public function onIceTransportDisconnect(): void;
}
<?php

namespace Webrtc\ICE\Listener;

/**
 * Receives application data delivered by an {@see \Webrtc\ICE\RTCIceTransportInterface}.
 *
 * Replaces the former Evenement "data" event that the DTLS transport and the DTLS handshake both
 * listened to.
 */
interface IceTransportDataListener
{
    public function onIceTransportData(string $data, int $componentId): void;
}
<?php

namespace Webrtc\ICE\Listener;

/**
 * Receives application data delivered by an {@see \Webrtc\ICE\RTCIceConnection}.
 *
 * A typed replacement for the former Evenement "data" event: the listener object is registered on
 * the connection and, being an ordinary object rather than a closure, survives a serialize cycle
 * as part of the graph with no wrapper.
 */
interface IceConnectionDataListener
{
    public function onIceConnectionData(string $data, int $componentId): void;
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\ICE;

use Webrtc\Exception\InvalidArgumentException;
use Webrtc\ICE\Enum\IceRole;
use Webrtc\ICE\Listener\IceTransportDataListener;
use Webrtc\ICE\Listener\IceTransportDisconnectListener;
use Webrtc\ICE\Listener\IceTransportStateChangeListener;

interface RTCIceTransportInterface
{
    /** Register a listener for application data arriving over the transport. */
    public function addDataListener(IceTransportDataListener $listener): void;

    /** Remove a previously registered data listener. */
    public function removeDataListener(IceTransportDataListener $listener): void;

    /** Register a listener notified when the transport reports a disconnecting close/error. */
    public function addDisconnectListener(IceTransportDisconnectListener $listener): void;

    /** Register a listener notified when the transport state changes. */
    public function addStateChangeListener(IceTransportStateChangeListener $listener): void;

    public function send(string $bytes): void;

    public function getRole(): IceRole;

    public function addRemoteCandidate(RTCIceCandidate $candidate): void;

    public function getIceGatherer(): RTCIceGathererInterface;

    public function isRoleSet(): bool;

    public function setRoleSet(bool $roleSet): void;

    public function getIceConnection(): RTCIceConnectionInterface;

    public function start(RTCIceParameters $remoteIceParameters): void;

    public function stop(): void;
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\ICE;

use Webrtc\ICE\Enum\IceGatheringState;

interface RTCIceGathererInterface
{
    public function getIceConnection(): RTCIceConnectionInterface;
    public function getState(): IceGatheringState;

    /** @return RTCIceCandidate[] */
    public function getLocalCandidates(): array;
    public function getLocalParameters(): RTCIceParameters;

    /**
     * Refresh the gathered host candidates from the live sockets after a serialize/unserialize cycle
     * may have rebound them, returning whether any local address changed.
     */
    public function refreshLocalCandidates(): bool;
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\ICE;

use Webrtc\TURN\TurnConfigurationInterface;

interface RTCIceProtocolConfigurationInterface extends TurnConfigurationInterface
{
    public function getStunServer(): ?array;

    public function setStunServer(?array $stunServer): void;
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\ICE;

use Webrtc\Exception\InvalidArgumentException;

/**
 * ICE Protocol Parser
 *
 * Parses ICE server configurations and generates protocol-specific connection settings.
 * Handles both STUN and TURN server configurations with proper URL parsing and validation.
 *
 * Key Features:
 * - Parses ICE server URLs (STUN/TURN)
 * - Validates URL schemes and formats
 * - Configures STUN server connections
 * - Configures TURN server connections with authentication
 * - Handles both UDP and TCP transport
 * - Supports secure (TLS) connections
 *
 * @see https://datatracker.ietf.org/doc/html/rfc7064 STUN URI Scheme
 * @see https://datatracker.ietf.org/doc/html/rfc7065 TURN URI Scheme
 *
 * @psalm-type ParsedIceUrl = array{scheme: string, host: string, port: int, transport: string|null}
 */
final readonly class IceProtocolParser
{
    /**
     * @param RTCIceServer[] $iceServes Array of ICE server configurations
     */
    public function __construct(private array $iceServes)
    {
    }

    /**
     * Generate ICE connection configuration
     *
     * Processes all configured ICE servers and created a unified connection configuration
     * with proper STUN/TURN server settings, transport, and authentication.
     *
     * @return RTCIceProtocolConfigurationInterface Fully configured ICE protocol settings
     *
     * @example
     * $parser = new IceProtocolParser($iceServers);
     * $config = $parser->getIceConnectionConfiguration();
     */
    public function getIceConnectionConfiguration(): RTCIceProtocolConfigurationInterface
    {
        $iceConnectionConfiguration = new RTCIceProtocolConfiguration();

        foreach ($this->iceServes as $iceServe) {
            foreach ($iceServe->getUrls() as $url) {
                $parsedUrl = $this->parseUrl($url);

                switch ($parsedUrl['scheme']) {
                    case 'stun':
                    case 'stuns':
                        $this->configureStunServer($iceConnectionConfiguration, $parsedUrl);
                        break;

                    case 'turn':
                    case 'turns':
                        $this->configureTurnServer($iceConnectionConfiguration, $parsedUrl, $iceServe);
                        break;

                    default:
                        break;
                }
            }
        }

        return $iceConnectionConfiguration;
    }

    /**
     * Parse and validate ICE server URL
     *
     * Processes ICE server URLs with the following capabilities:
     * - Handles both standard (with ://) and shorthand (with :) URL formats
     * - Extracts scheme, host, port, and query parameters
     * - Sets default ports for protocols (3478 for STUN/TURN, 5349 for secure variants)
     * - Validates transport protocols for TURN servers
     *
     * @param string $url ICE server URL to parse
     * @return ParsedIceUrl Parsed URL components including:
     *                  - scheme (stun/stuns/turn/turns)
     *                  - host
     *                  - port
     *                  - transport (for TURN servers, null otherwise)
     * @throws InvalidArgumentException If URL is malformed or contains invalid components
     *
     * @example
     * $parsed = $parser->parseUrl('turn:example.com?transport=udp');
     * // Returns array with scheme=turn, host=example.com, port=3478, transport=udp
     */
    public function parseUrl(string $url): array
    {
        if (preg_match('/^[a-zA-Z][a-zA-Z\d+\-.]*:/', $url) && !str_contains($url, '://')) {
            $url = preg_replace('/^([^:]+):/', '$1://', $url, 1) ?? $url;
        }

        $parts = parse_url($url);
        if ($parts === false) {
            throw new InvalidArgumentException("Invalid url: {$url}");
        }

        if (!isset($parts["scheme"]) || !isset($parts["host"]) || !in_array($parts["scheme"], ["stun", "stuns", "turn", "turns"])) {
            throw new InvalidArgumentException("Invalid url: {$url}");
        }

        $scheme = $parts["scheme"];
        $host = $parts["host"];
        $port = $parts["port"] ?? (in_array($scheme, ["stuns", "turns"]) ? 5349 : 3478);

        $transport = null;
        if (in_array($scheme, ["turn", "turns"])) {
            $query = [];
            if (isset($parts["query"])) {
                parse_str($parts["query"], $query);
            }

            /** @var string|null $queryTransport */
            $queryTransport = $query["transport"] ?? null;
            $transport = is_string($queryTransport)
                ? $queryTransport
                : ($scheme === "turn" ? "udp" : "tcp");
        }

        return [
            "scheme" => $scheme,
            "host" => $host,
            "port" => $port,
            "transport" => $transport,
        ];
    }

    /**
     * Configure STUN server settings
     *
     * Sets the primary STUN server configuration if not already set.
     * Only configure the first valid STUN server encountered.
     *
     * @param RTCIceProtocolConfiguration $config Configuration object to modify
     * @param ParsedIceUrl $parsedUrl Parsed URL components from parseUrl()
     */
    private function configureStunServer(RTCIceProtocolConfiguration $config, array $parsedUrl): void
    {
        if ($config->getStunServer() === null) {
            $config->setStunServer([$parsedUrl['host'], $parsedUrl['port']]);
        }
    }

    /**
     * Configure TURN server settings
     *
     * Sets TURN server configuration with authentication if:
     * - No TURN server is already configured
     * - Transport protocol is valid for the scheme
     * - Credential type is 'password'
     *
     * @param RTCIceProtocolConfiguration $config Configuration object to modify
     * @param ParsedIceUrl $parsedUrl Parsed URL components from parseUrl()
     * @param RTCIceServer $iceServe Source ICE server configuration containing credentials
     */
    private function configureTurnServer(RTCIceProtocolConfiguration $config, array $parsedUrl, RTCIceServer $iceServe): void
    {
        if ($config->getTurnServer() !== null) {
            return;
        }

        if (($parsedUrl['scheme'] === 'turn' && !in_array($parsedUrl['transport'], ['udp', 'tcp'])) ||
            ($parsedUrl['scheme'] === 'turns' && $parsedUrl['transport'] !== 'tcp') ||
            ($iceServe->getCredentialType() !== 'password')) {
            return;
        }

        $config->setTurnServer([$parsedUrl['host'], $parsedUrl['port']]);
        $config->setTurnSsl($parsedUrl['scheme'] === 'turns');
        $config->setTurnTransport((string)$parsedUrl['transport']);
        $config->setTurnUsername($iceServe->getUsername());
        $config->setTurnPassword($iceServe->getCredential());
    }
}<?php

namespace Webrtc\ICE;

use Webrtc\Exception\InvalidArgumentException;
use Webrtc\ICE\Enum\IceRole;
use Webrtc\ICE\Enum\TransportPolicyType;

final class RTCICESetting
{
    private ?array $icePortRange = null;
    private TransportPolicyType $transportPolicy = TransportPolicyType::ALL;
    /** @var array<int, string>|null */
    private ?array $nat1to1 = null;
    private bool $iceLite = false;
    // it will be changed for only testing purpose
    private IceRole $iceRole = IceRole::Controlling;

    public function getIcePortRange(): ?array
    {
        return $this->icePortRange;
    }

    public function setIcePortRange(int $minPort, int $maxPort): void
    {
        if ($maxPort- $minPort < 100) {
            throw new InvalidArgumentException("maxPort - minPort must be greater than 100");
        }

        if ($minPort < 1024 || $maxPort > 65535 || $minPort > $maxPort) {
            throw new InvalidArgumentException("Invalid port range [$minPort, $maxPort]");
        }

        $this->icePortRange = [$minPort, $maxPort];
    }

    public function getTransportPolicy(): TransportPolicyType
    {
        return $this->transportPolicy;
    }

    public function setTransportPolicy(TransportPolicyType $transportPolicy): void
    {
        $this->transportPolicy = $transportPolicy;
    }

    /**
     * @return array<int, string>|null
     */
    public function getNat1to1(): ?array
    {
        return $this->nat1to1;
    }

    /**
     * @param array<int, string>|null $nat1to1
     */
    public function setNat1to1(?array $nat1to1): void
    {
        $this->nat1to1 = $nat1to1;
    }

    public function isIceLite(): bool
    {
        return $this->iceLite;
    }

    public function setIceLite(bool $iceLite): void
    {
        $this->iceLite = $iceLite;
    }

    public function getIceRole(): IceRole
    {
        return $this->iceRole;
    }

    public function setIceRole(IceRole $iceRole): void
    {
        $this->iceRole = $iceRole;
    }

}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\ICE;

use Override;
use Psr\Log\LoggerInterface;
use Random\RandomException;
use Throwable;
use Webrtc\ICE\Enum\IceGatheringState;
use Webrtc\ICE\Listener\IceGathererStateChangeListener;
use Webrtc\Mixin\SerializableState;

/**
 * The RTCIceGatherer is responsible for gathering ICE candidates on the local machine.
 *
 * This class initializes an ICE connection using a list of ICE servers and a specified role.
 * It controls the gathering process and emits events when the ICE gathering state changes.
 * It follows the structure outlined in the WebRTC specification.
 *
 * Events:
 * - `statechange`: Emitted when the ICE gathering state transitions.
 */
final class RTCIceGatherer implements RTCIceGathererInterface
{
    private RTCIceConnectionInterface $iceConnection;
    private IceGatheringState $state = IceGatheringState::new;

    /** @var \WeakMap<IceGathererStateChangeListener, null> Listeners for gathering state changes. */
    private \WeakMap $statechangeListeners;

    /**
     * Constructs a new RTCIceGatherer instance.
     *
     * Initializes ICE connection with the given ICE servers and role. Optionally accepts a logger.
     *
     * @param RTCIceServer[] $iceServes List of ICE server configurations.
     * @param ?RTCICESetting $setting The ICE settings.
     * @param LoggerInterface|null $logger Optional logger for debugging.
     *
     * @throws RandomException
     */
    public function __construct(private array $iceServes, ?RTCICESetting $setting = null, ?LoggerInterface $logger = null)
    {
        /** @var \WeakMap<IceGathererStateChangeListener, null> */
        $this->statechangeListeners = new \WeakMap();

        $protocolParser = new IceProtocolParser($iceServes);

        if (!$setting) {
            $setting = new RTCICESetting();
        }

        $this->iceConnection = new RTCIceConnection($protocolParser->getIceConnectionConfiguration(), $setting->getIceRole());
        $this->iceConnection->setIcePortRange($setting->getIcePortRange());
        $this->iceConnection->setTransportPolicy($setting->getTransportPolicy());
        $this->iceConnection->setRemoteIsLite($setting->isIceLite());
        $this->iceConnection->setNat1to1($setting->getNat1to1());

        if ($logger) {
            $this->iceConnection->setLogger($logger);
        }
    }

    /**
     * Returns the list of configured ICE servers.
     *
     * @return array The ICE server configuration.
     */
    public function getIceServes(): array
    {
        return $this->iceServes;
    }

    /**
     * Initiates the ICE candidate gathering process.
     *
     * Changes state from 'new' to 'gathering', then to 'complete' after gathering is finished.
     * Emits a 'statechange' event on state transitions.
     *
     * @return void
     * @throws Throwable
     *
     * @throws RandomException
     */
    public function gather(): void
    {
        if ($this->state === IceGatheringState::new) {
            $this->setState(IceGatheringState::gathering);
            $this->iceConnection->gatherCandidates();
            $this->setState(IceGatheringState::complete);
        }
    }

    /**
     * Returns the current ICE gathering state.
     *
     * @return IceGatheringState The state of ICE candidate gathering.
     */
    #[\Override]
    public function getState(): IceGatheringState
    {
        return $this->state;
    }

    /**
     * Register a listener notified when the gathering state changes.
     *
     * Typed replacement for on('statechange'); the listener is a plain object captured by serialization.
     */
    public function addStateChangeListener(IceGathererStateChangeListener $listener): void
    {
        $this->statechangeListeners[$listener] = null;
    }

    /**
     * Sets the ICE gathering state and notifies registered state-change listeners.
     *
     * @param IceGatheringState $state The new ICE gathering state.
     *
     * @return void
     */
    public function setState(IceGatheringState $state): void
    {
        $this->state = $state;
        $this->notifyStateChange($state);
    }

    /**
     * @param IceGatheringState $state
     * @return void
     */
    private function notifyStateChange(IceGatheringState $state): void
    {
        foreach ($this->statechangeListeners as $listener => $_) {
            $listener->onIceGathererStateChange($state);
        }
    }

    /**
     * Returns the list of local ICE candidates that have been gathered.
     *
     * @return RTCIceCandidate[] The gathered local candidates.
     */
    #[\Override]
    public function getLocalCandidates(): array
    {
        return $this->iceConnection->getLocalCandidates();
    }

    /**
     * Refresh the gathered host candidates from the live sockets after a serialize/unserialize cycle
     * that may have rebound them to a different local port, and report whether any address changed.
     * The refreshed candidates are the ones {@see self::getLocalCandidates()} returns.
     */
    #[\Override]
    public function refreshLocalCandidates(): bool
    {
        return $this->iceConnection instanceof RTCIceConnection
            ? $this->iceConnection->refreshLocalCandidates()
            : false;
    }

    /**
     * Returns the local ICE parameters (username fragment and password).
     *
     * @return RTCIceParameters The local ICE credentials.
     */
    #[\Override]
    public function getLocalParameters(): RTCIceParameters
    {
        return new RTCIceParameters($this->iceConnection->getLocalUsername(), $this->iceConnection->getLocalPassword());
    }

    /**
     * Returns the ICE connection used internally for gathering and signaling.
     *
     * @return RTCIceConnectionInterface The ICE connection object.
     */
    #[\Override]
    public function getIceConnection(): RTCIceConnectionInterface
    {
        return $this->iceConnection;
    }

    /**
     * Closes the internal ICE connection and releases resources.
     *
     * @return void
     * @throws RandomException
     * @throws Throwable
     */
    public function close(): void
    {
        $this->iceConnection->close();
    }

    /**
     * Manually replaces the internal ICE connection.
     *
     * @param RTCIceConnectionInterface $iceConnection The new ICE connection instance.
     *
     * @return void
     */
    public function setIceConnection(RTCIceConnectionInterface $iceConnection): void
    {
        $this->iceConnection = $iceConnection;
    }

    /**
     * @return array<string, mixed>
     */
    public function __serialize(): array
    {
        $state = SerializableState::export($this, [
            // WeakMaps cannot be serialized; snapshot their keys and rebuild on the far side.
            'statechangeListeners' => ['__uninitialized' => true],
        ]);
        $state['__statechangeListeners'] = SerializableState::weakMapToList($this->statechangeListeners);

        return $state;
    }

    /**
     * @param array<string, mixed> $data
     */
    public function __unserialize(array $data): void
    {
        /** @var list<IceGathererStateChangeListener> $statechangeListeners */
        $statechangeListeners = $data['__statechangeListeners'] ?? [];
        unset($data['__statechangeListeners']);

        SerializableState::import($this, $data);
        /** @var \WeakMap<IceGathererStateChangeListener, null> */
        $this->statechangeListeners = SerializableState::listToWeakMap($statechangeListeners);
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\ICE\Trait;

use Amp\Dns\DnsRecord;
use Throwable;
use Webrtc\Exception\InvalidArgumentException;
use function Amp\Dns\resolve;

/**
 * DNS Resolution Trait
 *
 * Provides DNS resolution functionality for ICE (Interactive Connectivity Establishment) implementations.
 * This trait handles both direct IP addresses and domain name resolution with caching support.
 *
 * Features:
 * - Automatic fallback to Google's public DNS (8.8.8.8) if no system nameservers are configured
 * - DNS resolution through amphp
 * - Caching of DNS queries for improved performance
 * - Support for both IPv4 and IPv6 addresses
 *
 */
trait DNS
{
    /**
     * Resolve a network address to its IP equivalent
     *
     * This method takes an address array (typically [host, port]) and resolves the host
     * to an IP address if it isn't already one, through the system resolver, which already
     * handles caching and fallback.
     *
     * The lookup blocks the calling fiber rather than returning a promise.
     *
     * @param array $address An array containing [host, port] where host can be either:
     *                       - A domain name (e.g., 'example.com')
     *                       - An IP address (IPv4 or IPv6)
     *
     * @return array{0: string, 1: int<0, 65535>} Returns an array in the format [resolved_ip, original_port]
     *
     * @throws Throwable If DNS resolution fails or encounters an error
     *
     * @example
     * // Resolve a domain name
     * $resolved = $this->resolveDNS(['example.com', 1234]);
     * // Might return ['93.184.216.34', 1234]
     *
     * // Pass through an existing IP
     * $resolved = $this->resolveDNS(['192.0.2.1', 5678]);
     * // Returns ['192.0.2.1', 5678]
     */
    private function resolveDNS(array $address): array
    {
        $host = $address[0] ?? null;
        $port = $address[1] ?? null;

        if (!is_string($host) || $host === '') {
            throw new InvalidArgumentException("Invalid STUN/DNS host given: " . (is_scalar($host) ? (string)$host : gettype($host)));
        }

        if (!is_int($port) || $port < 0 || $port > 65535) {
            throw new InvalidArgumentException("Invalid STUN/DNS port given: " . (is_scalar($port) ? (string)$port : gettype($port)));
        }

        if (filter_var($host, FILTER_VALIDATE_IP)) {
            return [$host, $port];
        }

        // The system resolver already caches and falls back, so there is nothing to configure
        // here; the first A or AAAA record is what the candidate needs.
        /** @var list<DnsRecord> $records */
        $records = resolve($host);

        if (!isset($records[0])) {
            throw new \RuntimeException("No DNS records found for {$host}");
        }

        return [$records[0]->getValue(), $port];
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\ICE\Trait;

use Webrtc\ICE\Utils;

/**
 * Network Adapter Trait
 *
 * Provides functionality for retrieving and managing local network interface addresses.
 * This trait handles both IPv4 and IPv6 addresses with caching support and filtering capabilities.
 *
 * Key Features:
 * - Retrieves all available network interface addresses
 * - Filters loopback addresses (127.0.0.1 and ::1)
 * - Supports both IPv4 and IPv6 addresses
 * - Includes address caching mechanism
 * - Provides filtered results based on protocol preferences
 */
trait NetworkAdapter
{
    /** @var array<string, array<int, string>>|null */
    private ?array $hostAddressCache = null;
    /**
     * Retrieve host addresses from local network interfaces
     *
     * Gets all available host addresses from network adapters, with optional protocol filtering.
     * Results are cached to improve performance on subsequent calls.
     *
     * @param bool $useIPv4 Whether to include IPv4 addresses (default: true)
     * @param bool $useIPv6 Whether to include IPv6 addresses (default: true)
     * @return array<int, string> Array of filtered IP addresses based on protocol preferences
     * @throws \Throwable If network interface retrieval fails
     *
     * @example
     * // Get all addresses
     * $addresses = $this->getHostAddresses(true, true);
     *
     * // Get only IPv6 addresses
     * $addresses = $this->getHostAddresses(false, true);
     */
    public function getHostAddresses(bool $useIPv4, bool $useIPv6): array
    {
        $hostAddresses = $this->hostAddressCache ?? $this->getHostFromNetworkAdapter();

        return $this->filterAddresses($hostAddresses, $useIPv4, $useIPv6);
    }

    /**
     * Retrieve and process addresses from network interfaces
     *
     * Collects all unicast addresses from available network interfaces,
     * excluding loopback addresses, and organizes them by IP version.
     * IPv6 addresses are enclosed in square brackets for URI compatibility.
     *
     * @return array<string, array<int, string>> Array of addresses grouped by IP version (v4, v6)
     *
     * @example
     * $addresses = $this->getHostFromNetworkAdapter();
     * // Returns ['v4' => ['192.168.1.2'], 'v6' => ['[fe80::1]']]
     */
    private function getHostFromNetworkAdapter(): array
    {
        /** @var array<string, array<int, string>> $hostAddresses */
        $hostAddresses = ['v4' => [], 'v6' => []];

        /** @var array<string, array{unicast?: array<int, array{address?: string}>}> $interfaces */
        $interfaces = $this->getInterfaces();

        foreach ($interfaces as $interface) {
            foreach ($interface["unicast"] ?? [] as $adapter) {
                $address = $adapter["address"] ?? null;
                if (!is_string($address)) {
                    continue;
                }

                if (!in_array($address, ["127.0.0.1", "::1"], true)) {
                    $version = Utils::IPVersion($address);
                    if ($version === false) {
                        continue;
                    }

                    $hostAddresses[$version === 6 ? "v6" : "v4"][] = $version === 6 ? "[$address]" : $address;
                }
            }
        }

        $this->hostAddressCache = $hostAddresses;
        return $hostAddresses;
    }

    /**
     * Filter addresses by protocol version
     *
     * Filters the collected addresses based on the requested protocol versions.
     * Can return IPv4 only, IPv6 only, or combined addresses.
     *
     * @param array $hostAddresses Addresses to filter (grouped by v4/v6)
     * @param bool $useIPv4 Whether to include IPv4 addresses
     * @param bool $useIPv6 Whether to include IPv6 addresses
     * @return array<int, string> Filtered array of addresses
     *
     * @example
     * $filtered = $this->filterAddresses($hostAddresses, true, false);
     * // Returns only IPv4 addresses
     */
    private function filterAddresses(array $hostAddresses, bool $useIPv4, bool $useIPv6): array
    {
        /** @var array<int, string> $v4 */
        $v4 = $hostAddresses["v4"] ?? [];
        /** @var array<int, string> $v6 */
        $v6 = $hostAddresses["v6"] ?? [];

        return match (true) {
            $useIPv4 && $useIPv6 => array_merge($v4, $v6),
            $useIPv4 => $v4,
            default => $v6
        };
    }

    /**
     * Get network interface information
     *
     * Wrapper for PHP's net_get_interfaces() function.
     * Returns an array of all the network interfaces on the machine.
     *
     * @return array<string, array<string, mixed>> Network interface information
     * @see https://www.php.net/manual/en/function.net-get-interfaces.php
     */
    public function getInterfaces(): array
    {
        $interfaces = net_get_interfaces();
        if ($interfaces === false) {
            return [];
        }

        return $interfaces;
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\ICE\Trait;

use RuntimeException;
use Throwable;
use Webrtc\MDNS\Factory;

/**
 * mDNS (Multicast DNS) Resolution Trait
 *
 * Provides functionality for resolving .local domains using mDNS (Multicast DNS) protocol.
 * This trait is specifically designed for local network service discovery and resolution.
 *
 * Key Features:
 * - Resolves .local domains to IPv4 addresses (A records)
 * - Validates whether a domain is an mDNS-compatible .local domain
 * - Integrated error handling with optional logging
 * - Resolution blocks the calling fiber rather than returning a promise
 *
 * @see https://datatracker.ietf.org/doc/html/rfc6762 RFC 6762 for mDNS specifications
 */
trait Mdns
{
    private ?Factory $mdnsFactory = null;

    public function setMdnsFactory(?Factory $factory): void
    {
        $this->mdnsFactory = $factory;
    }

    /**
     * Resolve an mDNS .local domain to its IPv4 address
     *
     * Performs an mDNS query (type A record) for the given .local domain and returns
     * the first resolved IPv4 address. The resolution is performed asynchronously.
     *
     * @param string $domain The .local domain to resolve (e.g., "myservice.local")
     *
     * @return string|false Returns the IPv4 address as string if resolved successfully,
     *                      false if resolution fails or domain is invalid
     *
     * @throws RuntimeException If the mDNS resolver encounters an unexpected error
     *
     * @example
     * $ip = $this->resolveMdns('printer.local');
     * // Returns "192.168.1.100" or false if resolution fails
     */
    private function resolveMdns(string $domain): string|false
    {
        $factory = $this->mdnsFactory ?? new Factory();
        $resolver = $factory->createResolver();

        try {
            // IP successfully resolved
            return $resolver->resolve($domain);
        } catch (Throwable $e) {
            $this?->logger?->error(sprintf("Could not resolve the domain: %s. %s", $domain, $e->getMessage()));
            return false;
        }
    }

    /**
     * Validate whether a domain is a valid mDNS .local domain
     *
     * Checks if the given domain conforms to the mDNS specification for .local domains:
     * - Ends with ".local"
     * - Contains only a-z, A-Z, 0-9, and hyphens
     * - Each label part is 1-63 characters long
     *
     * @param string $domain Domain to validate (e.g., "my-device.local")
     *
     * @return bool True if the domain is a valid mDNS domain, false otherwise
     *
     * @example
     * $valid = $this->isMdnsDomain('mydevice.local'); // true
     * $valid = $this->isMdnsDomain('example.com'); // false
     * $valid = $this->isMdnsDomain('invalid..local'); // false
     */
    private function isMdnsDomain(string $domain): bool
    {
        return preg_match("/^[a-zA-Z0-9-]{1,63}\.local$/", $domain) === 1;
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\ICE;

use Override;
use Psr\Log\LoggerInterface;
use Throwable;
use Webrtc\Exception\InvalidArgumentException;
use Webrtc\ICE\Enum\IceRole;
use Webrtc\ICE\Enum\IceTransportState;
use Webrtc\ICE\Listener\IceConnectionClosedListener;
use Webrtc\ICE\Listener\IceConnectionDataListener;
use Webrtc\ICE\Listener\IceTransportDataListener;
use Webrtc\ICE\Listener\IceTransportDisconnectListener;
use Webrtc\ICE\Listener\IceTransportStateChangeListener;
use Webrtc\Mixin\SerializableState;

/**
 * Class RTCIceTransport
 *
 * Handles ICE transport functionality including connection state,
 * sending data, and reacting to events emitted by the ICE connection.
 *
 * Listens to its {@see RTCIceConnection} through typed listener interfaces and re-delivers the
 * data and disconnect it sees to its own typed listeners (the DTLS transport and handshake). These
 * are ordinary objects, so the whole wiring is captured verbatim by a serialize cycle.
 */
final class RTCIceTransport implements RTCIceTransportInterface, IceConnectionDataListener, IceConnectionClosedListener
{
    /**
     * @var IceTransportState Current transport state.
     */
    private IceTransportState $state = IceTransportState::new;

    /**
     * @var RTCIceConnectionInterface ICE connection instance.
     */
    private RTCIceConnectionInterface $iceConnection;

    /**
     * @var bool Whether the ICE role has been set.
     */
    private bool $roleSet = false;

    /** @var \WeakMap<IceTransportDataListener, null> Listeners for application data from the connection. */
    private \WeakMap $dataListeners;

    /** @var \WeakMap<IceTransportDisconnectListener, null> Listeners for a disconnecting close/error. */
    private \WeakMap $disconnectListeners;

    /** @var \WeakMap<IceTransportStateChangeListener, null> Listeners for transport state changes. */
    private \WeakMap $statechangeListeners;

    /**
     * RTCIceTransport constructor.
     *
     * @param RTCIceGathererInterface $iceGatherer The ICE gatherer instance.
     * @param LoggerInterface|null $logger Optional PSR-3 logger.
     */
    public function __construct(
        private readonly RTCIceGathererInterface $iceGatherer,
        private readonly ?LoggerInterface $logger = null
    ) {
        /** @var \WeakMap<IceTransportDataListener, null> */
        $this->dataListeners = new \WeakMap();
        /** @var \WeakMap<IceTransportDisconnectListener, null> */
        $this->disconnectListeners = new \WeakMap();
        /** @var \WeakMap<IceTransportStateChangeListener, null> */
        $this->statechangeListeners = new \WeakMap();
        /** @var RTCIceConnection $iceConnection */
        $iceConnection = $iceGatherer->getIceConnection();
        $this->iceConnection = $iceConnection;
        $iceConnection->addDataListener($this);
        $iceConnection->addClosedListener($this);
    }

    /**
     * Register a listener for application data arriving over this transport.
     *
     * Typed replacement for on('data'); the listener is a plain object captured by serialization.
     */
    #[\Override]
    public function addDataListener(IceTransportDataListener $listener): void
    {
        $this->dataListeners[$listener] = null;
    }

    #[\Override]
    public function removeDataListener(IceTransportDataListener $listener): void
    {
        unset($this->dataListeners[$listener]);
    }

    /**
     * Register a listener notified when the transport reports a disconnecting close/error.
     */
    #[\Override]
    public function addDisconnectListener(IceTransportDisconnectListener $listener): void
    {
        $this->disconnectListeners[$listener] = null;
    }

    /**
     * Register a listener notified when the transport state changes.
     *
     * Typed replacement for on('statechange'); the listener is a plain object captured by serialization.
     */
    #[\Override]
    public function addStateChangeListener(IceTransportStateChangeListener $listener): void
    {
        $this->statechangeListeners[$listener] = null;
    }

    #[\Override]
    public function onIceConnectionData(string $data, int $componentId): void
    {
        foreach ($this->dataListeners as $listener => $_) {
            $listener->onIceTransportData($data, $componentId);
        }
    }

    #[\Override]
    public function onIceConnectionClosed(): void
    {
        $this->failure();
    }

    /**
     * Get the ICE gatherer instance.
     *
     * @return RTCIceGathererInterface
     */
    #[\Override]
    public function getIceGatherer(): RTCIceGathererInterface
    {
        return $this->iceGatherer;
    }

    /**
     * Get the ICE role.
     *
     * @return IceRole
     */
    #[\Override]
    public function getRole(): IceRole
    {
        return $this->iceConnection->getIceRole();
    }

    /**
     * Get the current ICE transport state.
     *
     * @return IceTransportState
     */
    public function getState(): IceTransportState
    {
        return $this->state;
    }

    /**
     * Add a remote ICE candidate.
     *
     * @param RTCIceCandidate $candidate
     * @return void
     */
    #[\Override]
    public function addRemoteCandidate(RTCIceCandidate $candidate): void
    {
        if (!$this->iceConnection->isRemoteCandidatesEnd()) {
            $this->iceConnection->addRemoteCandidate($candidate);
        }
    }

    /**
     * Get the list of all remote candidates.
     *
     * @return array
     */
    public function getRemoteCandidates(): array
    {
        return $this->iceConnection->getRemoteCandidates();
    }

    /**
     * Start the ICE transport with the given remote ICE parameters.
     *
     * @param RTCIceParameters $remoteIceParameters
     * @return void Returns once the transport has finished checking.
     * @throws InvalidArgumentException If the transport is already closed.
     */
    #[\Override]
    public function start(RTCIceParameters $remoteIceParameters): void
    {
        if ($this->state === IceTransportState::closed) {
            throw new InvalidArgumentException("RTCIceTransport is closed");
        }

        $this->setState(IceTransportState::checking);

        $this->iceConnection->setRemoteIsLite($remoteIceParameters->iceLite);
        $this->iceConnection->setRemoteUsername($remoteIceParameters->usernameFragment);
        $this->iceConnection->setRemotePassword($remoteIceParameters->password);

        try {
            $this->iceConnection->connect();
            $this->setState(IceTransportState::complete);
        } catch (Throwable) {
            $this->setState(IceTransportState::failed);
        }
    }

    /**
     * Gracefully shut down the ICE connection.
     *
     * @return void
     */
    #[\Override]
    public function stop(): void
    {
        if ($this->state === IceTransportState::closed) {
            return;
        }

        $this->setState(IceTransportState::closed);

        try {
            $this->iceConnection->close();
        } catch (Throwable) {
            // Silently catch errors to ensure graceful shutdown.
        }
    }

    /**
     * Forcefully stop the ICE connection without a graceful shutdown.
     *
     * @return void
     */
    public function stopIceConnection(): void
    {
        if ($this->state !== IceTransportState::closed) {
            $this->setState(IceTransportState::closed);
            $this->iceConnection->close();
        }
    }

    /**
     * Set the transport state and notify state-change listeners.
     * Also clears listeners on shutdown to aid garbage collection.
     *
     * @param IceTransportState $state
     * @return void
     */
    private function setState(IceTransportState $state): void
    {
        if ($state !== $this->state) {
            $this->logger?->debug(sprintf(
                "Ice transport state has been changed from %s to %s",
                $this->state->name,
                $state->name
            ));

            $this->state = $state;
            $this->notifyStateChange($state);

            if ($state === IceTransportState::closed) {
                /** @var \WeakMap<IceTransportDataListener, null> */
                $this->dataListeners = new \WeakMap();
                /** @var \WeakMap<IceTransportDisconnectListener, null> */
                $this->disconnectListeners = new \WeakMap();
                /** @var \WeakMap<IceTransportStateChangeListener, null> */
                $this->statechangeListeners = new \WeakMap();
            }
        }
    }

    /**
     * @param IceTransportState $state
     * @return void
     */
    private function notifyStateChange(IceTransportState $state): void
    {
        foreach ($this->statechangeListeners as $listener => $_) {
            $listener->onIceTransportStateChange($state);
        }
    }

    /**
     * Get the underlying ICE connection.
     *
     * @return RTCIceConnectionInterface
     */
    #[\Override]
    public function getIceConnection(): RTCIceConnectionInterface
    {
        return $this->iceConnection;
    }

    /**
     * Send data over the ICE connection.
     *
     * @param string $bytes
     * @return void
     */
    #[\Override]
    public function send(string $bytes): void
    {
        $this->iceConnection->sendData($bytes);
    }

    /**
     * Notify the connection that all remote candidates have been sent.
     *
     * @return void
     */
    public function endRemoteCandidate(): void
    {
        $this->iceConnection->endOfRemoteCandidate();
    }

    /**
     * Check if the ICE role has been set.
     *
     * @return bool
     */
    #[\Override]
    public function isRoleSet(): bool
    {
        return $this->roleSet;
    }

    /**
     * Mark the ICE role as a set.
     *
     * @param bool $roleSet
     * @return void
     */
    #[\Override]
    public function setRoleSet(bool $roleSet): void
    {
        $this->roleSet = $roleSet;
    }

    /**
     * Transition to the "failed" state if the connection was previously "complete".
     *
     * @return void
     */
    private function failure(): void
    {
        if ($this->state === IceTransportState::complete) {
            $this->setState(IceTransportState::failed);
        }
    }

    /**
     * @return array<string, mixed>
     */
    public function __serialize(): array
    {
        $state = SerializableState::export($this, [
            // WeakMaps cannot be serialized; snapshot their keys and rebuild on the far side.
            'dataListeners' => ['__uninitialized' => true],
            'disconnectListeners' => ['__uninitialized' => true],
            'statechangeListeners' => ['__uninitialized' => true],
        ]);
        $state['__dataListeners'] = SerializableState::weakMapToList($this->dataListeners);
        $state['__disconnectListeners'] = SerializableState::weakMapToList($this->disconnectListeners);
        $state['__statechangeListeners'] = SerializableState::weakMapToList($this->statechangeListeners);

        return $state;
    }

    /**
     * @param array<string, mixed> $data
     */
    public function __unserialize(array $data): void
    {
        /** @var list<IceTransportDataListener> $dataListeners */
        $dataListeners = $data['__dataListeners'] ?? [];
        /** @var list<IceTransportDisconnectListener> $disconnectListeners */
        $disconnectListeners = $data['__disconnectListeners'] ?? [];
        /** @var list<IceTransportStateChangeListener> $statechangeListeners */
        $statechangeListeners = $data['__statechangeListeners'] ?? [];
        unset($data['__dataListeners'], $data['__disconnectListeners'], $data['__statechangeListeners']);

        SerializableState::import($this, $data);
        /** @var \WeakMap<IceTransportDataListener, null> */
        $this->dataListeners = SerializableState::listToWeakMap($dataListeners);
        /** @var \WeakMap<IceTransportDisconnectListener, null> */
        $this->disconnectListeners = SerializableState::listToWeakMap($disconnectListeners);
        /** @var \WeakMap<IceTransportStateChangeListener, null> */
        $this->statechangeListeners = SerializableState::listToWeakMap($statechangeListeners);
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\ICE;

use Override;

/**
 * Class RTCIceServer
 *
 * Represents a STUN or TURN server configuration used during ICE candidate gathering.
 * This class holds information such as the server URLs, credentials, and credential type
 * needed to authenticate with the server.
 *
 * Typically used to configure ICE transport behavior when establishing peer-to-peer
 * WebRTC connections.
 */
final class RTCIceServer implements RTCIceServerInterface
{
    /**
     * @var array<string> List of STUN/TURN server URLs.
     */
    private array $urls = [];

    /**
     * @var string|null Username used for TURN server authentication.
     */
    private ?string $username = null;

    /**
     * @var string|null Credential (password or token) used for TURN server authentication.
     */
    private ?string $credential = null;

    /**
     * @var string Type of credential (e.g., "password" or "token"). Defaults to "password".
     */
    private string $credentialType = "password";

    /**
     * Get the list of STUN or TURN server URLs.
     *
     * @return array<string> Array of server URLs.
     */
    #[\Override]
    public function getUrls(): array
    {
        return $this->urls;
    }

    /**
     * Set one or more server URLs for the ICE server.
     *
     * @param array<string>|string $urls A string or array of STUN/TURN URLs.
     *                           If a string is passed, it is added to the existing list.
     *
     * @return void
     */
    public function setUrls(array|string $urls): void
    {
        $this->urls = is_string($urls) ? array_merge($this->urls, [$urls]) : $urls;
    }

    /**
     * Get the TURN username used for authentication.
     *
     * @return string|null Username or null if not set.
     */
    #[\Override]
    public function getUsername(): ?string
    {
        return $this->username;
    }

    /**
     * Set the TURN username used for authentication.
     *
     * @param string|null $username The username to set or null to unset.
     *
     * @return void
     */
    public function setUsername(?string $username): void
    {
        $this->username = $username;
    }

    /**
     * Get the credential (e.g., password) for TURN authentication.
     *
     * @return string|null Credential or null if not set.
     */
    #[\Override]
    public function getCredential(): ?string
    {
        return $this->credential;
    }

    /**
     * Set the credential (e.g., password) for TURN authentication.
     *
     * @param string|null $credential Credential or null to unset.
     *
     * @return void
     */
    public function setCredential(?string $credential): void
    {
        $this->credential = $credential;
    }

    /**
     * Get the credential type used for TURN authentication.
     *
     * @return string Credential type (typically "password" or "token").
     */
    #[\Override]
    public function getCredentialType(): string
    {
        return $this->credentialType;
    }

    /**
     * Set the credential type used for TURN authentication.
     *
     * @param string $credentialType Credential type to set.
     *
     * @return void
     */
    public function setCredentialType(string $credentialType): void
    {
        $this->credentialType = $credentialType;
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\ICE\Enum;

/**
 * Candidate pair state.
 *
 * @see https://datatracker.ietf.org/doc/html/rfc8445#section-6.1.2.6
 */
enum RTCIceCandidatePairStats: int
{
    case FROZEN = 0;
    case WAITING = 1;
    case IN_PROGRESS = 2;
    case SUCCEEDED = 3;
    case FAILED = 4;
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\ICE\Enum;

/**
 * Candidate types and their Type Preferences value
 *
 * @see https://datatracker.ietf.org/doc/html/rfc8445#section-5.1.2.2
 */
enum CandidateType: int
{
    case host = 126;
    case prflx = 110;
    case srflx = 100;
    case relay = 0;
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\ICE\Enum;

enum IceRole
{
    case Controlling;
    case Controlled;
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\ICE\Enum;

/**
 * Candidate pair state.
 *
 * @see https://datatracker.ietf.org/doc/html/rfc8445#section-6.1.2.6
 */
enum CheckListState: int
{
    case ICE_COMPLETED = 0;
    case ICE_FAILED = 1;
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\ICE\Enum;

enum IceTransportState: int
{
    case complete = 0;
    case gathering = 1;
    case new = 2;
    case closed = 3;
    case failed = 4;
    case checking = 5;
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\ICE\Enum;

enum TransportPolicyType: int
{
    // All ICE candidates will be considered.
    case ALL = 0;

    // Only ICE candidates whose IP addresses are being relayed,
    // such as those being passed through a STUN or TURN server,
    // will be considered.
    case RELAY = 1;

}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\ICE\Enum;

enum IceGatheringState: int
{
    case complete = 0;
    case gathering = 1;
    case new = 2;
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\ICE\Enum;

enum TransportType: int
{
    case udp = 0;
    case tcp = 1;
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\ICE;

use Amp\Socket\InternetAddress;
use Throwable;
use Webrtc\ICE\Enum\IceRole;
use Webrtc\STUN\IceConnectionProtocolInterface;
use Webrtc\STUN\Message\MessageInterface;

interface RTCIceConnectionInterface
{
    public function getIceRole(): IceRole;
    public function isUseIPv4(): bool;

    public function setUseIPv4(bool $useIPv4): void;

    public function isUseIPv6(): bool;

    public function setUseIPv6(bool $useIPv6): void;

    public function getLocalUsername(): string;

    public function setLocalUsername(string $localUsername): void;

    public function getLocalPassword(): string;

    public function setLocalPassword(string $localPassword): void;

    /**
     * @return RTCIceCandidate[]
     */
    public function getLocalCandidates(): array;

    /**
     * @param RTCIceCandidate[] $localCandidates
     */
    public function setLocalCandidates(array $localCandidates): void;

    /**
     * @return RTCIceCandidate[]
     */
    public function getRemoteCandidates(): array;

    /**
     * @param RTCIceCandidate[] $remoteCandidates
     */
    public function setRemoteCandidates(array $remoteCandidates): void;

    public function getRemoteUsername(): ?string;

    public function setRemoteUsername(?string $remoteUsername): void;

    public function getRemotePassword(): ?string;

    public function setRemotePassword(?string $remotePassword): void;

    /**
     * @return array<int>
     */
    public function getComponentIds(): array;

    /**
     * @param array<int> $componentIds
     */
    public function setComponentIds(array $componentIds): void;

    public function isRemoteIsLite(): bool;

    public function setRemoteIsLite(bool $remoteIsLite): void;

    public function gatherCandidates(): void;

    public function addRemoteCandidate(RTCIceCandidate $remoteCandidate): void;

    public function endOfRemoteCandidate(): void;

    public function connect(): void;

    public function unfreezeChecks(): void;

    public function checkIncoming(MessageInterface $message, InternetAddress $address, IceConnectionProtocolInterface $protocol): void;

    public function completeCheckAction(RTCIceCandidatePair $pair): void;

    public function periodicConsentCheck(): void;

    public function close(): void;

    public function sendData(string $data, int $componentId = 1): void;

    public function onDataReceived(string $data, int $componentId): void;

    public function onRequestReceived(MessageInterface $message, InternetAddress $address, IceConnectionProtocolInterface $protocol, string $data): void;

    public function respondError(MessageInterface $orgMessage, InternetAddress $address, IceConnectionProtocolInterface $protocol, array $errorCode): void;

    public function onClose(?IceConnectionProtocolInterface $protocol = null): void;

    public function onError(Throwable $e): void;

    public function setIceRole(IceRole $iceRole): void;

    public function isRemoteCandidatesEnd(): bool;
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\ICE;

use Override;
use Webrtc\ICE\Listener\IceConnectionClosedListener;
use Webrtc\ICE\Listener\IceConnectionDataListener;
use Psr\Log\LoggerInterface;
use Ramsey\Uuid\Uuid;
use Random\RandomException;
use Amp\DeferredFuture;
use Amp\Future;
use Amp\Socket\InternetAddress;
use Revolt\EventLoop;
use Throwable;
use Webrtc\Exception\InvalidArgumentException;
use Webrtc\Exception\RuntimeException;
use Webrtc\ICE\Enum\CandidateType;
use Webrtc\ICE\Enum\CheckListState;
use Webrtc\ICE\Enum\IceRole;
use Webrtc\ICE\Enum\RTCIceCandidatePairStats;
use Webrtc\ICE\Enum\TransportPolicyType;
use Webrtc\ICE\Enum\TransportType;
use Webrtc\ICE\Trait\DNS;
use Webrtc\ICE\Trait\Mdns;
use Webrtc\ICE\Trait\NetworkAdapter;
use Webrtc\Mixin\SerializableState;
use Webrtc\STUN\Enum\MessageAttribute;
use Webrtc\STUN\Enum\MessageClass;
use Webrtc\STUN\Enum\MessageMethod;
use Webrtc\STUN\Exception\TransactionException;
use Webrtc\STUN\Exception\TransactionExceptionInterface;
use Webrtc\STUN\IceConnectionProtocolInterface;
use Webrtc\STUN\Message\Message;
use Webrtc\STUN\Message\MessageInterface;
use Webrtc\STUN\ReceiverInterface;
use Webrtc\STUN\Stun;
use Webrtc\STUN\StunInterface;
use Webrtc\TURN\Turn;
use Webrtc\TURN\TurnInterface;
use function Amp\async;

/**
 * RTCIceConnection - ICE Connection Implementation
 *
 * Implements the Interactive Connectivity Establishment (ICE) protocol for WebRTC.
 * Handles candidate gathering, connectivity checks, and network address translation.
 *
 * Key Features:
 * - Implements full ICE protocol (RFC 8445)
 * - Supports both controlling and controlled roles
 * - Handles STUN and TURN server interactions
 * - Manages candidate pairs and connectivity checks
 * - Implements consent freshness checks (RFC 7675)
 * - Supports IPv4 and IPv6 candidates
 * - Provides event-driven interface for connection state changes
 *
 * @see https://datatracker.ietf.org/doc/html/rfc8445 ICE RFC 8445
 * @see https://www.rfc-editor.org/rfc/rfc7675 Consent Freshness RFC 7675
 * @api
 */
class RTCIceConnection implements RTCIceConnectionInterface, ReceiverInterface
{
    use NetworkAdapter, Mdns, DNS;

    /* Constants */

    /** @var int Maximum number of binding retry attempts */
    private const RETRY_BINDING_MAX = 10;

    /** How often the keepalive timer runs, in seconds. */
    private const KEEPALIVE_TICK = 0.5;

    /** How often the selected pair is checked, in seconds. */
    private const KEEPALIVE_INTERVAL = 1.0;

    /** How often the other valid pairs (the backups) are checked, in seconds. */
    private const BACKUP_INTERVAL = 4.0;

    /** Retransmissions of a keepalive request on the selected pair: an answer can take up to 3.5 seconds. */
    private const KEEPALIVE_RETRANSMISSIONS = 2;

    /**
     * Retransmissions of a request on another pair: an answer can take up to 1.5 seconds.
     *
     * Kept short, as a request holds its socket, and so the connection, until it ends.
     */
    private const BACKUP_RETRANSMISSIONS = 1;
    /**
     * Retransmissions of a connectivity check to a relayed candidate (RFC 8445 14.3). It's dropped by the TURN server
     * until the peer's allocation permits the sender, which happens when the peer checks the pair from its side: sent
     * only once, it could fail before. The other checks are sent once, so that the unreachable pairs fail quickly.
     */
    private const RELAYED_CHECK_RETRANSMISSIONS = 2;

    /** How long the selected pair can go without answering before switching to a backup, in seconds. */
    private const UNWRITABLE_TIMEOUT = 3.0;

    /** How long a backup can go without answering and still be switched to, in seconds. */
    private const BACKUP_ALIVE_TIMEOUT = 2.0 * self::BACKUP_INTERVAL + 1.0;

    /** How long a component can go without any pair answering before consent expires (RFC 7675), in seconds. */
    private const CONSENT_TIMEOUT = 30.0;

    /** A pair is only switched to for its latency if it costs at most this fraction of the selected one... */
    private const SWITCH_IMPROVEMENT = 0.75;

    /** ...and at least this much less, in seconds: switching between paths that are all fast gains nothing. */
    private const MIN_SWITCH_GAIN = 0.01;

    /** The minimum time between two switches made for latency, in seconds. */
    private const MIN_SWITCH_INTERVAL = 10.0;

    /** How many round-trip times must be measured on a pair before switching to it for its latency. */
    private const MIN_RTT_SAMPLES = 3;

    /** Added to the round-trip time of relayed pairs when comparing them: a direct path is preferred. */
    private const RELAY_PENALTY = 0.05;

    /* Properties */

    /** @var int tiebreaker value for role conflicts */
    private int $tieBreaker;

    /** @var string Local ICE username fragment */
    private string $localUsername;

    /** @var string Local ICE password */
    private string $localPassword;

    /** @var string|null Remote ICE username fragment */
    private ?string $remoteUsername = null;

    /** @var string|null Remote ICE password */
    private ?string $remotePassword = null;

    /** @var array<int> Component IDs (typically 1 for RTP, 2 for RTCP) */
    private array $componentIds;

    /** @var RTCIceCandidatePair[] List of candidate pairs to check */
    private array $checkList = [];

    /** @var array<array-key, array{0: MessageInterface, 1: InternetAddress, 2: IceConnectionProtocolInterface}> Early checks received before checklist creation */
    private array $earlyChecks = [];

    /** @var RTCIceCandidate[] Local ICE candidates */
    private array $localCandidates = [];

    /** @var array<int, RTCIceCandidatePair> Nominated candidate pairs, by component */
    private array $nominated = [];

    /** @var array<int> Components being nominated */
    private array $nominating = [];

    /** @var array<array-key, StunInterface|TurnInterface> Protocol instances */
    private array $protocols = [];

    /** @var RTCIceCandidate[] Remote ICE candidates */
    private array $remoteCandidates = [];

    /** @var bool Whether to use IPv4 candidates */
    private bool $useIPv4 = true;

    /** @var bool Whether to use IPv6 candidates */
    private bool $useIPv6 = true;

    /** @var bool Whether the connection is closed */
    private bool $closed = false;

    /** @var bool Whether remote agent is lite implementation */
    private bool $remoteIsLite = false;

    /** @var bool Whether checklist processing is complete */
    private bool $checkListDone = false;

    /** @var bool Whether early checks are processed */
    private bool $earlyChecksDone = false;

    /** @var bool Whether remote candidates are complete */
    private bool $remoteCandidatesEnd = false;

    /** @var bool Whether local candidates are complete */
    private bool $localCandidatesEnd = false;

    /** @var bool Whether local candidate gathering has started */
    private bool $localCandidatesStart = false;

    /** @var CheckListState|null Current state of the checklist */
    private ?CheckListState $checkListState = null;

    /** @var TransportPolicyType Transport policy (all, relay, etc.) */
    private TransportPolicyType $transportPolicy = TransportPolicyType::ALL;

    /** @var LoggerInterface|null Logger instance */
    private ?LoggerInterface $logger = null;

    /** @var DeferredFuture<void>|null Current binding-check completion, while ICE negotiation is active. */
    private ?DeferredFuture $bindingCheck = null;

    /** Whether checklist progression has already been queued. */
    private bool $bindingCheckScheduled = false;

    /** @var string|null Handle of the consent check timer */
    private ?string $queryConsentTimer = null;

    /** @var \WeakMap<IceConnectionDataListener, null> Registered application-data listeners. */
    private \WeakMap $dataListeners;

    /** @var \WeakMap<IceConnectionClosedListener, null> Registered close listeners. */
    private \WeakMap $closedListeners;

    /**
     * Register a listener for application data received over this connection.
     *
     * A typed replacement for on('data'): the listener is a plain object, so it is captured
     * verbatim by a serialize cycle rather than needing a serializable-closure wrapper. The
     * registry is a WeakMap, so the caller must keep its own reference â€” a listener held only here
     * is collected and drops out on its own.
     */
    public function addDataListener(IceConnectionDataListener $listener): void
    {
        $this->dataListeners[$listener] = null;
    }

    /**
     * Register a listener notified when this connection closes (typed replacement for on('onClose')).
     */
    public function addClosedListener(IceConnectionClosedListener $listener): void
    {
        $this->closedListeners[$listener] = null;
    }

    private function notifyClosed(): void
    {
        foreach ($this->closedListeners as $listener => $_) {
            $listener->onIceConnectionClosed();
        }
    }

    /** Consecutive consent-check failures, reset on a successful check. */
    /** When the selected pair of a component was last changed by the keepalive logic (microtime). */
    private ?float $lastSwitchAt = null;

    /** Whether the keepalive logic changed the selected pair of a component. */
    private bool $keepaliveSwitched = false;

    /** Multiplies the intervals and timeouts of the keepalives, for tests. */
    private float $timeScale = 1.0;

    /** Whether the connection is being closed. */
    private bool $closing = false;

    /** When a pair that was not checked when ICE completed was last checked (microtime). */
    private ?float $lastDiscoveryAt = null;

    /** @var bool Whether waiting for binding response */
    private bool $isBindingWait = false;

    /** @var int Count of failed binding attempts */
    private int $tryFailedCount = 0;

    /** @var ?array Range of ports that host candidates allow */
    private ?array $icePortRange = null;

    /**
     * @var array<int, string>|null If set, this will result in all host candidates (which normally have a private IP address)
     * to be rewritten with the public address provided in the settings
     */
    private ?array $nat1to1 = null;

    /**
     * Constructor - Creates a new ICE connection
     *
     * @param RTCIceProtocolConfigurationInterface $configuration ICE protocol configuration
     * @param IceRole $iceRole The role of this ICE agent (controlling/controlled)
     * @throws RandomException If random number generation fails
     */
    public function __construct(private readonly RTCIceProtocolConfigurationInterface $configuration, private IceRole $iceRole = IceRole::Controlled)
    {
        $this->localUsername = Utils::getRandomString(4);
        $this->localPassword = Utils::getRandomString(22);
        $this->tieBreaker = Utils::generateRandom64BitInt();
        $this->componentIds = range(1, 1);
        /** @var \WeakMap<IceConnectionDataListener, null> */
        $this->dataListeners = new \WeakMap();
        /** @var \WeakMap<IceConnectionClosedListener, null> */
        $this->closedListeners = new \WeakMap();
    }

    /**
     * @return bool
     */
    #[Override]
    public function isUseIPv4(): bool
    {
        return $this->useIPv4;
    }

    /**
     * @param bool $useIPv4
     * @return void
     */
    #[Override]
    public function setUseIPv4(bool $useIPv4): void
    {
        $this->useIPv4 = $useIPv4;
    }

    /**
     * @return bool
     */
    #[Override]
    public function isUseIPv6(): bool
    {
        return $this->useIPv6;
    }

    /**
     * @param bool $useIPv6
     * @return void
     */
    #[Override]
    public function setUseIPv6(bool $useIPv6): void
    {
        $this->useIPv6 = $useIPv6;
    }

    /**
     * @return string
     */
    #[Override]
    public function getLocalUsername(): string
    {
        return $this->localUsername;
    }

    /**
     * @param string $localUsername
     * @return void
     */
    #[Override]
    public function setLocalUsername(string $localUsername): void
    {
        $this->localUsername = $localUsername;
    }

    /**
     * @return string
     */
    #[Override]
    public function getLocalPassword(): string
    {
        return $this->localPassword;
    }

    /**
     * @param string $localPassword
     * @return void
     */
    #[Override]
    public function setLocalPassword(string $localPassword): void
    {
        $this->localPassword = $localPassword;
    }

    /**
     * @return RTCIceCandidate[]
     */
    #[Override]
    public function getLocalCandidates(): array
    {
        return $this->localCandidates;
    }

    /**
     * @param RTCIceCandidate[] $localCandidates
     * @return void
     */
    #[Override]
    public function setLocalCandidates(array $localCandidates): void
    {
        $this->localCandidates = $localCandidates;
    }

    /**
     * @return RTCIceCandidate[]
     */
    #[Override]
    public function getRemoteCandidates(): array
    {
        return $this->remoteCandidates;
    }

    /**
     * @param RTCIceCandidate[] $remoteCandidates
     * @return void
     */
    #[Override]
    public function setRemoteCandidates(array $remoteCandidates): void
    {
        $this->remoteCandidates = $remoteCandidates;
    }

    /**
     * @return string|null
     */
    #[Override]
    public function getRemoteUsername(): ?string
    {
        return $this->remoteUsername;
    }

    /**
     * @param string|null $remoteUsername
     * @return void
     */
    #[Override]
    public function setRemoteUsername(?string $remoteUsername): void
    {
        $this->remoteUsername = $remoteUsername;
    }

    /**
     * @return string|null
     */
    #[Override]
    public function getRemotePassword(): ?string
    {
        return $this->remotePassword;
    }

    /**
     * @param string|null $remotePassword
     * @return void
     */
    #[Override]
    public function setRemotePassword(?string $remotePassword): void
    {
        $this->remotePassword = $remotePassword;
    }

    /**
     * @return array<int>
     */
    #[Override]
    public function getComponentIds(): array
    {
        return $this->componentIds;
    }

    /**
     * @param array<int> $componentIds
     * @return void
     */
    #[Override]
    public function setComponentIds(array $componentIds): void
    {
        $this->componentIds = $componentIds;
    }

    /**
     * @return bool
     */
    #[Override]
    public function isRemoteIsLite(): bool
    {
        return $this->remoteIsLite;
    }

    /**
     * @param bool $remoteIsLite
     * @return void
     */
    #[Override]
    public function setRemoteIsLite(bool $remoteIsLite): void
    {
        $this->remoteIsLite = $remoteIsLite;
    }

    /**
     * @param LoggerInterface $logger
     * @return void
     */
    public function setLogger(LoggerInterface $logger): void
    {
        $this->logger = $logger;
    }

    /**
     * Gather local ICE candidates
     *
     * Collects host candidates and queries STUN/TURN servers as configured.
     * Candidates are gathered for each component ID (typically 1 for RTP, 2 for RTCP).
     *
     * @throws RandomException If random number generation fails
     * @throws Throwable If candidate gathering fails
     * @see https://datatracker.ietf.org/doc/html/rfc8445#section-2.1
     */
    #[Override]
    public function gatherCandidates(): void
    {
        if (!$this->localCandidatesStart) {
            $this->localCandidatesStart = true;
            foreach ($this->componentIds as $componentId) {
                $this->localCandidates = array_merge($this->getCandidate($componentId), $this->localCandidates);
            }

            $this->localCandidatesEnd = true;
        }
    }

    /**
     * Refresh the host candidates' addresses from the live sockets, and report whether any changed.
     *
     * After a serialize/unserialize cycle the underlying UDP sockets may rebind to a different local
     * port (e.g. when the saved address is no longer assignable). The candidate objects still carry
     * the old port, which strands the remote peer's checks. This re-reads each host candidate's
     * address from its (already reconnected) protocol socket, without creating any new socket or
     * disturbing the running connectivity checks. The caller re-signals the refreshed candidates.
     *
     * Server-reflexive and relayed candidates are left untouched: their public mapping can only be
     * recovered by re-querying the STUN/TURN server, which is out of scope for this in-place refresh.
     *
     * @return bool Whether any host candidate's address changed.
     */
    public function refreshLocalCandidates(): bool
    {
        $changed = false;
        foreach ($this->protocols as $protocol) {
            $candidate = $protocol->getCandidate();
            if (!$candidate instanceof RTCIceCandidate || $candidate->getType() !== CandidateType::host) {
                continue;
            }
            $host = $protocol->getLocalHost();
            $port = $protocol->getLocalPort();
            if ($candidate->getHost() !== $host || $candidate->getPort() !== $port) {
                $candidate->setHost($host);
                $candidate->setPort($port);
                $changed = true;
            }
        }
        return $changed;
    }

    /**
     * Gathers all types of ICE candidates for a specific component.
     *
     * This method combines host candidates, server-reflexive candidates (from STUN servers),
     * and relayed candidates (from TURN servers) based on the current configuration.
     *
     * @param int $componentId The component ID to gather candidates for
     * @return RTCIceCandidate[] An array of RTCIceCandidate objects
     * @throws RandomException If there's an error in random generation
     * @throws Throwable For any other exceptions that might occur during candidate gathering
     */
    private function getCandidate(int $componentId): array
    {

        // Gather host candidates
        $candidates = $this->getHostCandidates($componentId);

        // Query the STUN server for IPv4 server-reflexive candidates and get STUN candidates.
        if ($this->configuration->getStunServer() !== null) {
            $candidates = array_merge($candidates, $this->getServerReflexiveCandidates($componentId));
        }

        // Connect to TURN server, get relayed candidate and return candidates
        if ($this->configuration->getTurnServer() !== null) {
            if ($turn = $this->getRelayedCandidate()) {
                $candidate = $candidates [] = $this->getTurnCandidate($turn, $componentId);
                $turn->setCandidate($candidate);
                $this->protocols[$turn->getId()] = $turn;
            }
        }

        return $candidates;
    }

    /**
     * Discovers and creates host ICE candidates.
     *
     * This method gets local IP addresses and creates host candidates based on
     * the configured transport policy. Only creates candidates when the transport
     * policy is set to 'all'.
     *
     * @param int $componentId The component ID to create candidates for
     * @return RTCIceCandidate[] An array of host RTCIceCandidate objects
     * @throws Throwable For any exceptions that might occur during candidate creation
     */
    private function getHostCandidates(int $componentId): array
    {
        $addresses = $this->nat1to1 ?? $this->getHostAddresses($this->useIPv4, $this->useIPv6);
        $candidates = [];

        foreach ($addresses as $address) {
            if ($protocol = $this->getStun($address)) {
                if ($this->transportPolicy === TransportPolicyType::ALL) {
                    $candidate = $candidates [] = $this->createCandidate($protocol->getId(), $protocol->getLocalHost(), $protocol->getLocalPort(), $componentId, CandidateType::host);
                    $protocol->setCandidate($candidate);
                }

                $this->protocols[$protocol->getId()] = $protocol;
            }
        }

        return $candidates;
    }

    /**
     * Discovers and creates server-reflexive candidates using STUN servers.
     *
     * This method iterates through available protocols to get server-reflexive
     * candidates for IPv4 addresses by contacting the configured STUN server.
     *
     * @param int $componentId The component ID to create candidates for
     * @return RTCIceCandidate[] An array of server-reflexive RTCIceCandidate objects
     * @throws RandomException If there's an error in random generation
     * @throws Throwable For any other exceptions during candidate discovery
     */
    private function getServerReflexiveCandidates(int $componentId): array
    {
        $candidates = [];

        foreach ($this->protocols as $protocol) {
            if ($protocol instanceof Stun && Utils::IPVersion($protocol->getLocalHost()) === 4) {
                if ($candidate = $this->getServerReflexiveCandidate($protocol, $componentId)) {
                    $candidates [] = $candidate;
                    $protocol->setCandidate($candidate);
                    $this->protocols [$protocol->getId()] = $protocol;
                }
            }
        }

        return $candidates;
    }

    /**
     * Attempts to get a single server-reflexive candidate via a STUN server.
     *
     * Sends a binding request to the configured STUN server and creates a candidate
     * from the XOR_MAPPED_ADDRESS in the response.
     *
     * @param StunInterface $stun The STUN interface to use for the request
     * @param int $componentId The component ID to create the candidate for
     * @return RTCIceCandidate|false A server-reflexive candidate if successful, false otherwise
     * @throws RandomException If there's an error in random generation
     * @throws Throwable For any other exceptions during the STUN request
     */
    private function getServerReflexiveCandidate(StunInterface $stun, int $componentId): RTCIceCandidate|false
    {
        $stunServer = $this->configuration->getStunServer();
        if ($stunServer === null || $stunServer === []) {
            return false;
        }
        /** @var array{0: string, 1: int<0, 65535>} $stunServer */
        $message = Message::new(MessageClass::REQUEST, MessageMethod::BINDING);
        try {
            $stunServerIp = $this->resolveDNS($stunServer);
            [$response,] = $stun->request($message, new InternetAddress($stunServerIp[0], $stunServerIp[1]), null);
            /** @var InternetAddress $mappedAddress */
            $mappedAddress = $response->attributes()->get(MessageAttribute::XOR_MAPPED_ADDRESS);

            return $this->createCandidate($stun->getId(), $mappedAddress->getAddress(), $mappedAddress->getPort(), $componentId, CandidateType::srflx, $stun->getLocalHost(), $stun->getLocalPort());
        } catch (Throwable $e) {
            $this->logger?->error(sprintf("Could not request stun server: %s - %s", $e->getMessage(), implode(":", $stunServer)));
            // Keep the host-candidate socket. Closing it here dropped the only path
            // ICE can use when the STUN server is unreachable.
            return false;
        }
    }

    /**
     * Attempts to connect to a TURN server and get a relayed candidate.
     *
     * Creates and connects to a TURN server using the configured credentials
     * and settings.
     *
     * @return Turn|false A TURN interface if successful, false otherwise
     * @throws Throwable For any other exceptions during TURN server connection
     */
    private function getRelayedCandidate(): Turn|false
    {
        $turnServer = $this->configuration->getTurnServer();
        if ($turnServer === null) {
            return false;
        }
        /** @var array{0: string, 1: int<0, 65535>} $turnServer */

        try {
            $configuration = clone $this->configuration;
            $configuration->setTurnServer($this->resolveDNS($turnServer));

            $turn = Turn::create($configuration, $this, $this->logger);
            $turn->connect();
            return $turn;
        } catch (Throwable $e) {
            $this->logger?->error("Couldn't connect to Turn server {$e->getMessage()}", ["TurnServer" => $turnServer]);
            return false;
        }
    }

    /**
     * Creates a relay candidate from a TURN connection.
     *
     * Uses the relayed address and port obtained from the TURN server to create
     * a relay candidate.
     *
     * @param TurnInterface $turn The TURN interface containing the relayed information
     * @param int $componentId The component ID to create the candidate for
     * @return RTCIceCandidate A relay ICE candidate
     */
    private function getTurnCandidate(TurnInterface $turn, int $componentId): RTCIceCandidate
    {
        return $this->createCandidate(
            $turn->getId(),
            $turn->getRelayedHost(),
            $turn->getRelayedPort(),
            $componentId,
            CandidateType::relay,
            $turn->getLocalHost(),
            $turn->getLocalPort(),
            $this->configuration->getTurnTransport() === "tcp" ? TransportType::tcp : TransportType::udp
        );
    }

    /**
     * Creates a STUN interface for a given local address.
     *
     * Attempts to create a STUN interface that will be used for candidate gathering.
     *
     * @param string $address The local address to bind the STUN interface to
     * @return StunInterface|false A STUN interface if successful, false otherwise
     */
    private function getStun(string $address): StunInterface|false
    {
        try {
            return Stun::create($this, new InternetAddress($address, 0), $this->logger, $this->icePortRange);
        } catch (Throwable $e) {
            $this->logger?->debug("Couldn't create STUN interface for {$address}: {$e->getMessage()}");
            return false;
        }
    }

    /**
     * Creates an RTCIceCandidate object with the provided parameters.
     *
     * Factory method for creating standardized ICE candidate objects with the necessary
     * attributes based on the type of candidate being created.
     *
     * @param string|null $protocolId The protocol ID associated with this candidate
     * @param string $host The candidate's IP address
     * @param int $port The candidate's port
     * @param int $componentId The component ID for this candidate
     * @param CandidateType $type The type of candidate (host, srflx, relay)
     * @param string|null $relatedAddress The related address for non-host candidates
     * @param int|null $relatedPort The related port for non-host candidates
     * @param TransportType $transportType The transport protocol (UDP or TCP)
     * @return RTCIceCandidate A configured ICE candidate object
     */
    private function createCandidate(
        ?string       $protocolId,
        string        $host,
        int           $port,
        int           $componentId,
        CandidateType $type,
        ?string       $relatedAddress = null,
        ?int          $relatedPort = null,
        TransportType $transportType = TransportType::udp
    ): RTCIceCandidate
    {
        return RTCIceCandidate::create(
            $protocolId,
            $componentId,
            $host,
            $port,
            $type,
            $transportType,
            $relatedAddress,
            $relatedPort
        );
    }

    /**
     * Adds a remote ICE candidate to the connection.
     *
     * This method processes and validates a remote candidate before adding it to the connection's
     * remote candidate list. It handles mDNS resolution for applicable candidates and creates
     * appropriate check lists for connectivity testing.
     *
     * @param RTCIceCandidate $remoteCandidate The remote candidate to add
     * @return void
     * @throws InvalidArgumentException If remote candidates are added after end-of-candidates has been signaled
     */
    #[Override]
    public function addRemoteCandidate(RTCIceCandidate $remoteCandidate): void
    {
        if ($this->remoteCandidatesEnd) {
            throw new InvalidArgumentException("Unable to add remote candidate after the end-of-candidates stage.");
        }

        // Resolve mDNS candidate
        if ($this->isMdnsDomain($remoteCandidate->getHost())) {
            $ip = $this->resolveMdns($remoteCandidate->getHost());
            if ($ip !== false) {
                $remoteCandidate->setHost($ip);
            } else {
                $this->logger?->error("Couldn't resolve the remote host", ["RemoteHost" => $remoteCandidate->getHost()]);
                return;
            }
        }

        // Validate the remote candidate add it
        if ($this->validateRemoteCandidate($remoteCandidate)) {
            $this->remoteCandidates [] = $remoteCandidate;
            $this->createCheckList($remoteCandidate);
            $this->scheduleBindingCheck();
        }
    }

    /**
     * Validates that a remote candidate meets the requirements for use.
     *
     * This method verifies that the remote candidate has a supported type and
     * that its host address is a valid IP address.
     *
     * @param RTCIceCandidate $remoteCandidate The remote candidate to validate
     * @return bool True if the candidate is valid, false otherwise
     */
    private function validateRemoteCandidate(RTCIceCandidate $remoteCandidate): bool
    {
        if (!in_array($remoteCandidate->getType()->name, ["host", "relay", "srflx"])) {
            $this->logger?->error(sprintf("Invalid candidate type %s", $remoteCandidate->getType()->name));
            return false;
        }

        $ip = trim($remoteCandidate->getHost(), '[]');
        if (!filter_var($ip, FILTER_VALIDATE_IP)) {
            $this->logger?->error(sprintf("Invalid candidate host %s", $remoteCandidate->getHost()));
            return false;
        }

        return true;
    }

    /**
     * Signals that all remote candidates have been received.
     *
     * This method marks the end of the remote candidate gathering phase and triggers
     * the reset of component IDs to ensure only components with paired candidates are used.
     *
     * @return void
     */
    #[Override]
    public function endOfRemoteCandidate(): void
    {
        $this->resetComponentIds();
        $this->remoteCandidatesEnd = true;
        $this->scheduleBindingCheck();
    }

    /**
     * Removes component IDs that have no matching remote candidates.
     *
     * This method is called after all remote candidates have been received to
     * identify and remove any component IDs for which the remote party has not
     * provided any candidates.
     *
     * @return void
     */
    private function resetComponentIds(): void
    {
        // Collect seen component IDs
        $seenComponentIds = array_unique(array_map(function (RTCIceCandidate $candidate) {
            return $candidate->getComponentId();
        }, $this->remoteCandidates));
        $missingComponentIds = array_diff($this->componentIds, $seenComponentIds);

        if (!empty($missingComponentIds)) {
            $this->logger?->debug("Component IDs " . implode(", ", $missingComponentIds) . " have no candidate pairs");
            $this->componentIds = $seenComponentIds;
        }
    }

    /**
     * Establishes an ICE connection by setting up checklists and starting the connectivity checks.
     *
     * This method validates connection preconditions, creates the checklist of candidate pairs,
     * unfreezes initial candidates, and processes any early checks that arrived before
     * the checklist was created. It then performs binding checks to establish connectivity
     * and begins periodic consent freshness tests once a connection is established.
     *
     * @return void Returns once the ICE connection is established.
     * @throws RuntimeException If ICE negotiation fails or preconditions arenâ€™t met
     */
    #[Override]
    public function connect(): void
    {
        $this->validateConnectionPreconditions();
        $this->createCheckList();
        $this->unfreezeChecks();
        $this->processEarlyChecks();

        try {
            $this->bindCheck()->await();
        } catch (Throwable $e) {
            $this->close();
            throw new RuntimeException("ICE negotiation failed: " . $e->getMessage(), (int)$e->getCode(), $e);
        }

        if ($this->checkListState === CheckListState::ICE_FAILED) {
            $this->close();
            throw new RuntimeException("ICE negotiation failed");
        }

        // Start consent freshness tests
        $this->periodicConsentCheck();
    }

    /**
     * Validates that all required preconditions for a connection establishment are met.
     *
     * Verifies that local candidates have been gathered and that remote credentials
     * (username and password) have been set before attempting to establish a connection.
     *
     * @throws RuntimeException If local candidates gathering wasnâ€™t performed or remote credentials are missing
     * @throws RandomException If there's an error in random generation
     * @throws Throwable For other exceptions that might occur during validation
     */
    private function validateConnectionPreconditions(): void
    {
        if (!$this->localCandidatesEnd) {
            $this->close();
            throw new RuntimeException("Local candidates gathering was not performed");
        }

        if ($this->remoteUsername === null || $this->remotePassword === null) {
            $this->close();
            throw new RuntimeException("Remote username or password is missing");
        }
    }

    /**
     * Processes connectivity checks that arrived before checklist creation.
     *
     * Handles any STUN binding requests received before the checklist
     * was created by passing them to the checkIncoming method.
     *
     * @return void
     */
    private function processEarlyChecks(): void
    {
        foreach ($this->earlyChecks as $earlyCheck) {
            $this->checkIncoming($earlyCheck[0], $earlyCheck[1], $earlyCheck[2]);
        }

        $this->earlyChecks = [];
        $this->earlyChecksDone = true;
    }

    /**
     * Creates a checklist of candidate pairs for connectivity testing.
     *
     * Pairs each compatible local protocol with remote candidates to create
     * candidate pairs that will be tested for connectivity. Follows the process
     * described in RFC 8445 section 6.1.2.2.
     *
     * @param RTCIceCandidate|null $rCandidate Optional specific remote candidate to pair with locals
     * @return void
     * @see https://datatracker.ietf.org/doc/html/rfc8445#section-6.1.2.2
     */
    private function createCheckList(?RTCIceCandidate $rCandidate = null): void
    {
        $remoteCandidates = $rCandidate ? [$rCandidate] : $this->remoteCandidates;
        foreach ($remoteCandidates as $remoteCandidate) {
            foreach ($this->protocols as $protocol) {
                $localCandidate = $protocol->getCandidate();
                if ($localCandidate === null) {
                    continue;
                }
                assert($localCandidate instanceof RTCIceCandidate);
                if ($localCandidate->isPairableWith($remoteCandidate) && !$this->findPair($protocol, $remoteCandidate)) {
                    $this->checkList[] = new RTCIceCandidatePair($protocol, $remoteCandidate);
                }
            }
        }

        $this->sortCheckList();
    }

    /**
     * Sorts the checklist in decreasing order of candidate pair priority.
     *
     * Implements the algorithm defined in RFC 8445 section 6.1.2.3 to calculate
     * pair priorities and sort the checklist accordingly.
     *
     * @return void
     * @see https://datatracker.ietf.org/doc/html/rfc8445#section-6.1.2.3
     */
    private function sortCheckList(): void
    {
        // Sort the candidate pairs in decreasing order of priority
        usort($this->checkList, function (RTCIceCandidatePair $a, RTCIceCandidatePair $b): int {
            return $this->getPairPriority($b) <=> $this->getPairPriority($a);
        });
    }

    /**
     * Computes the priority of a candidate pair.
     *
     * @see https://datatracker.ietf.org/doc/html/rfc8445#section-6.1.2.3
     */
    private function getPairPriority(RTCIceCandidatePair $pair): int
    {
        $G = $this->isControllingRole() ? $pair->getLocalCandidate()->getPriority() : $pair->getRemoteCandidate()->getPriority();
        $D = $this->isControllingRole() ? $pair->getRemoteCandidate()->getPriority() : $pair->getLocalCandidate()->getPriority();

        // Candidate priorities are below 2^31: this fits in a 64-bit integer.
        return (1 << 32) * min($G, $D) + 2 * max($G, $D) + ($G > $D ? 1 : 0);
    }

    /**
     * Finds an existing candidate pair in the checklist.
     *
     * Searches for a candidate pair that matches the given protocol and remote candidate.
     *
     * @param IceConnectionProtocolInterface $protocol The local protocol interface
     * @param RTCIceCandidate $remoteCandidate The remote candidate
     * @return RTCIceCandidatePair|null The matching candidate pair or null if none found
     */
    private function findPair(IceConnectionProtocolInterface $protocol, RTCIceCandidate $remoteCandidate): ?RTCIceCandidatePair
    {
        foreach ($this->checkList as $candidatePair) {
            if ($candidatePair->getProtocol()->getId() === $protocol->getId() && $candidatePair->getRemoteCandidate() === $remoteCandidate) {
                return $candidatePair;
            }
        }
        return null;
    }

    /**
     * Unfreezes candidate pairs to begin connectivity checks.
     *
     * Initiates the connectivity check process by changing the state of the
     * first pair for the first component from "frozen" to "waiting", then
     * unfreezes additional pairs with different foundations.
     *
     * @return void
     */
    #[Override]
    public function unfreezeChecks(): void
    {
        $firstPair = $this->getFirstPairForFirstComponent();

        if ($firstPair && $firstPair->getState() === RTCIceCandidatePairStats::FROZEN) {
            $this->changeCandidatePairState($firstPair, RTCIceCandidatePairStats::WAITING);
        }

        $this->unfreezePairsWithDifferentFoundations($firstPair);
    }

    /**
     * Gets the first candidate pair for the first component in the checklist.
     *
     * Identifies the pair with the lowest component ID, which is typically
     * the first to be checked according to the ICE algorithm.
     *
     * @return RTCIceCandidatePair|null The first candidate pair or null if none exists
     */
    private function getFirstPairForFirstComponent(): ?RTCIceCandidatePair
    {
        if ($this->componentIds === []) {
            return null;
        }

        $minComponentId = min($this->componentIds);
        foreach ($this->checkList as $pair) {
            if ($pair->getComponentId() === $minComponentId) {
                return $pair;
            }
        }
        return null;
    }

    /**
     * Unfreezes pairs that have the same component ID but different foundations.
     *
     * Part of the ICE algorithm to ensure diverse connectivity checks across
     * different network paths.
     *
     * @param RTCIceCandidatePair|null $firstPair The reference pair to compare foundations against
     * @return void
     */
    private function unfreezePairsWithDifferentFoundations(?RTCIceCandidatePair $firstPair): void
    {
        if (!$firstPair) {
            return;
        }

        $seenFoundations = [$firstPair->getLocalCandidate()->getFoundation()];
        foreach ($this->checkList as $pair) {
            if (
                $pair->getComponentId() === $firstPair->getComponentId() &&
                !in_array($pair->getLocalCandidate()->getFoundation(), $seenFoundations, true) &&
                $pair->getState() === RTCIceCandidatePairStats::FROZEN
            ) {
                $this->changeCandidatePairState($pair, RTCIceCandidatePairStats::WAITING);
                $seenFoundations[] = $pair->getLocalCandidate()->getFoundation();
            }
        }
    }

    /**
     * Changes the state of a candidate pair and logs the transition.
     *
     * Updates the state of a candidate pair as it progresses through the
     * connectivity check process.
     *
     * @param RTCIceCandidatePair $pair The candidate pair to update
     * @param RTCIceCandidatePairStats $state The new state to set
     * @return void
     */
    public function changeCandidatePairState(RTCIceCandidatePair $pair, RTCIceCandidatePairStats $state): void
    {
        $this->logger?->debug(sprintf(
            "Candidate pair state changed (%s): %s -> %s",
            $pair,
            $pair->getState()->name,
            $state->name
        ));
        $pair->setState($state);
    }

    /**
     * Handles incoming STUN binding requests for connectivity checks.
     *
     * Processes incoming connectivity checks by finding or creating the appropriate
     * remote candidate, matching it with a local protocol to form a candidate pair,
     * and potentially starting a binding check on that pair.
     *
     * @param MessageInterface $message The incoming STUN message
     * @param InternetAddress $address The source address of the message
     * @param IceConnectionProtocolInterface $protocol The local protocol that received the message
     * @return void
     * @throws RandomException If there's an error in random generation
     */
    #[Override]
    public function checkIncoming(MessageInterface $message, InternetAddress $address, IceConnectionProtocolInterface $protocol): void
    {
        $localCandidate = $protocol->getCandidate();
        assert($localCandidate !== null);

        $remoteCandidate = $this->findOrCreateRemoteCandidate($message, $address, $localCandidate->getComponentId());
        $pair = $this->findPair($protocol, $remoteCandidate) ?? $this->createNewCandidatePair($protocol, $remoteCandidate);
        if (in_array($pair->getState(), [RTCIceCandidatePairStats::WAITING, RTCIceCandidatePairStats::FAILED], true)) {
            $this->startCheckBinding($pair);
        }

        $this->updateNominatedFlag($message, $pair);
    }

    /**
     * Finds an existing remote candidate or creates a peer-reflexive candidate.
     *
     * Maps an incoming message's source address to a known remote candidate or
     * creates a new peer-reflexive candidate if no match is found.
     *
     * @param MessageInterface $message The incoming STUN message
     * @param InternetAddress $address The source address
     * @param int $componentId The component ID
     * @return RTCIceCandidate The matching or newly created remote candidate
     * @throws RandomException If there's an error in random generation
     */
    private function findOrCreateRemoteCandidate(MessageInterface $message, InternetAddress $address, int $componentId): RTCIceCandidate
    {
        $host = $address->getAddress();
        $port = $address->getPort();
        foreach ($this->remoteCandidates as $candidate) {
            if (trim($candidate->getHost(), '[]') === $host && $candidate->getPort() === $port) {
                assert($candidate->getComponentId() === $componentId);
                return $candidate;
            }
        }

        return $this->createPeerReflexiveCandidate($message, $host, $port, $componentId);
    }

    /**
     * Creates a peer-reflexive candidate from an incoming connectivity check.
     *
     * Generates a new remote candidate with type "prflx" based on the source
     * address of an incoming connectivity check.
     *
     * @param MessageInterface $message The incoming STUN message
     * @param string $host The source host address
     * @param int $port The source port
     * @param int $componentId The component ID
     * @return RTCIceCandidate The newly created peer-reflexive candidate
     * @throws RandomException If there's an error in random generation
     */
    private function createPeerReflexiveCandidate(MessageInterface $message, string $host, int $port, int $componentId): RTCIceCandidate
    {
        $candidate = $this->createCandidate(Uuid::uuid4()->toString(), $host, $port, $componentId, CandidateType::prflx);
        /** @var int|null $priority */
        $priority = $message->attributes()->get(MessageAttribute::PRIORITY);
        if ($priority !== null) {
            $candidate->setPriority($priority);
        }
        $candidate->setFoundation(Utils::getRandomString(10));
        $this->remoteCandidates[] = $candidate;

        $this->logger?->info("Discovered peer reflexive candidate: " . $candidate->getHost());
        return $candidate;
    }

    /**
     * Creates a new candidate pair for connectivity checking.
     *
     * Forms a candidate pair from a local protocol and remote candidate,
     * adds it to the checklist, and sorts the checklist.
     *
     * @param IceConnectionProtocolInterface $protocol The local protocol
     * @param RTCIceCandidate $remoteCandidate The remote candidate
     * @return RTCIceCandidatePair The newly created candidate pair
     */
    private function createNewCandidatePair(IceConnectionProtocolInterface $protocol, RTCIceCandidate $remoteCandidate): RTCIceCandidatePair
    {
        $pair = new RTCIceCandidatePair($protocol, $remoteCandidate);
        $pair->setState(RTCIceCandidatePairStats::WAITING);
        $this->checkList[] = $pair;
        $this->sortCheckList();

        return $pair;
    }

    /**
     * Updates the nomination status of a candidate pair.
     *
     * Sets the nominated flag on a pair when the controlling agent indicates
     * a nomination through the USE_CANDIDATE attribute.
     *
     * @param MessageInterface $message The incoming STUN message
     * @param RTCIceCandidatePair $pair The candidate pair to potentially nominate
     * @return void
     */
    public function updateNominatedFlag(MessageInterface $message, RTCIceCandidatePair $pair): void
    {
        if ($message->attributes()->has(MessageAttribute::USE_CANDIDATE) && $this->isControlledRole()) {
            $pair->setRemoteNominated(true);

            if ($pair->getState() === RTCIceCandidatePairStats::SUCCEEDED) {
                $pair->setNominated(true);
                $this->completeCheckAction($pair);
                $this->scheduleBindingCheck();
            }
        }
    }

    /**
     * Processes a successful connectivity checks binding response.
     *
     * Handles a successful STUN binding response by validating the source address,
     * updating nomination status, and marking the pair as succeeded.
     *
     * @param InternetAddress $address The source address of the response
     * @param RTCIceCandidatePair $pair The candidate pair being checked
     * @param bool $nominate Whether the controlling agent is nominating this pair
     * @return void
     * @throws RandomException If there's an error in random generation
     */
    public function handleCheckBinding(InternetAddress $address, RTCIceCandidatePair $pair, bool $nominate): void
    {
        // Validate source address
        $remoteAddress = $pair->getRemoteAddress();
        if ($address->getAddress() !== $remoteAddress->getAddress() || $address->getPort() !== $remoteAddress->getPort()) {
            $this->logger?->debug("Check $pair failed: source address mismatch");
            $this->markPairFailed($pair);

            return;
        }

        // Mark as nominated if applicable
        if ($nominate || $pair->isRemoteNominated()) {
            $pair->setNominated(true);
        } elseif ($this->shouldNominate($pair)) {
            $this->performNomination($pair);

            return;
        }

        $this->markPairSucceeded($pair);
    }

    /**
     * Determines whether a candidate pair should be nominated.
     *
     * Checks if this agent is in the controlling role and if the component
     * has not yet been nominated.
     *
     * @param RTCIceCandidatePair $pair The candidate pair to evaluate
     * @return bool True if the pair should be nominated, false otherwise
     */
    private function shouldNominate(RTCIceCandidatePair $pair): bool
    {
        return $this->isControllingRole() && !in_array($pair->getComponentId(), $this->nominating);
    }

    /**
     * Initiates nomination of a candidate pair.
     *
     * Sends a STUN binding request with the USE_CANDIDATE attribute to
     * nominate a specific candidate pair for use.
     *
     * @param RTCIceCandidatePair $pair The candidate pair to nominate
     * @return void
     * @throws RandomException If there's an error in random generation
     */
    private function performNomination(RTCIceCandidatePair $pair): void
    {
        $this->logger?->info("Check $pair nominating pair");
        $this->nominating[] = $pair->getComponentId();
        $message = $this->buildBindingMessage($pair, true);
        $remoteAddress = $pair->getRemoteAddress();

        $password = $this->remotePassword;
        $retransmissions = self::getCheckRetransmissions($pair);
        // The request blocks, so it runs in its own fiber: the caller drives the check list
        // and must not stall on one pair's transaction. It only holds the connection weakly, so that
        // a pending request doesn't keep an abandoned connection alive.
        $weak = \WeakReference::create($this);
        async(static function () use ($weak, $pair, $message, $remoteAddress, $password, $retransmissions): void {
            try {
                $pair->getProtocol()->request($message, $remoteAddress, $password, $retransmissions);
                $pair->setNominated(true);
                $weak->get()?->markPairSucceeded($pair);
            } catch (Throwable) {
                $self = $weak->get();
                $self?->logger?->info("Check $pair failed: could not nominate pair");
                $self?->markPairFailed($pair);
            }
        })->ignore();
    }

    /**
     * Processes a failed connectivity check binding response.
     *
     * Handles error responses from STUN binding requests, including handling
     * role conflicts (487 error) by switching roles and retrying.
     *
     * @param TransactionExceptionInterface $e The exception containing error details
     * @param RTCIceCandidatePair $pair The candidate pair being checked
     * @param MessageInterface $message The original binding request message
     * @return void
     * @throws RandomException If there's an error in random generation
     */
    public function handleBindingError(TransactionExceptionInterface $e, RTCIceCandidatePair $pair, MessageInterface $message): void
    {
        $stunMessage = $e instanceof TransactionException ? $e->getStunMessage() : null;
        /** @var array{0: int, 1: string}|null $errorCode */
        $errorCode = $stunMessage?->attributes()->get(MessageAttribute::ERROR_CODE);

        if ($errorCode !== null && $errorCode[0] === 487) {
            $this->setRole(($message->attributes()->has(MessageAttribute::ICE_CONTROLLING) ? IceRole::Controlled : IceRole::Controlling));
            $this->startCheckBinding($pair); // Retry after switching roles
        } else {
            $this->markPairFailed($pair);
        }
    }

    /**
     * Gets how many times a connectivity check of a pair is retransmitted.
     */
    private static function getCheckRetransmissions(RTCIceCandidatePair $pair): int
    {
        return $pair->getRemoteCandidate()->getType() === CandidateType::relay ? self::RELAYED_CHECK_RETRANSMISSIONS : 0;
    }

    /**
     * Initiates a connectivity check by sending a STUN binding request.
     *
     * Changes the candidate pair state to "in_progress" and sends a STUN binding
     * request to test connectivity, with appropriate handlers for success or failure.
     *
     * @param RTCIceCandidatePair $pair The candidate pair to check
     * @return void
     * @throws RandomException If there's an error in random generation
     */
    public function startCheckBinding(RTCIceCandidatePair $pair): void
    {
        $this->changeCandidatePairState($pair, RTCIceCandidatePairStats::IN_PROGRESS);
        $nominate = $this->isControllingRole() && !$this->remoteIsLite;
        $message = $this->buildBindingMessage($pair, $nominate);
        $remoteAddress = $pair->getRemoteAddress();

        $password = $this->remotePassword;
        $retransmissions = self::getCheckRetransmissions($pair);
        // The request blocks, so it runs in its own fiber: several pairs are checked
        // concurrently and the check list has to keep moving while each is outstanding. It only
        // holds the connection weakly, so that a pending request doesn't keep an abandoned
        // connection alive (once established, the peer's checks keep triggering new ones).
        $weak = \WeakReference::create($this);
        async(static function () use ($weak, $pair, $message, $remoteAddress, $nominate, $password, $retransmissions): void {
            try {
                $start = microtime(true);
                [, $address] = $pair->getProtocol()->request($message, $remoteAddress, $password, $retransmissions);
                $self = $weak->get();
                if ($self === null) {
                    return;
                }
                if ($address === null) {
                    $self->markPairFailed($pair);
                    return;
                }
                $now = microtime(true);
                $pair->recordResponse($now - $start, $now);
                $self->handleCheckBinding($address, $pair, $nominate);
            } catch (TransactionExceptionInterface $e) {
                $weak->get()?->handleBindingError($e, $pair, $message);
            } catch (Throwable $e) {
                // Anything else is a bug rather than a statement about this pair, but the
                // check list is driven by pairs reaching a terminal state: letting the fiber
                // die here would leave isBindingWait set and stall the whole exchange.
                $self = $weak->get();
                $self?->logger?->error("Check $pair aborted: {$e->getMessage()}");
                $self?->markPairFailed($pair);
            }
        })->ignore();
    }

    /**
     * Creates a STUN binding request message for a connectivity check.
     *
     * Builds a STUN binding request with appropriate attributes for ICE connectivity
     * checking, including role information and nomination flags.
     *
     * @param RTCIceCandidatePair $pair The candidate pair for which to build the message
     * @param bool $nominate Whether to include the USE_CANDIDATE attribute for nomination
     * @return MessageInterface The constructed STUN binding request message
     * @throws RandomException If there's an error in random generation
     */
    public function buildBindingMessage(RTCIceCandidatePair $pair, bool $nominate): MessageInterface
    {
        $messageAttr = [
            MessageAttribute::USERNAME->name => sprintf("%s:%s", $this->remoteUsername ?? '', $this->localUsername),
            MessageAttribute::PRIORITY->name => $pair->getLocalCandidate()->getPriority(CandidateType::prflx->value)
        ];

        $message = Message::new(MessageClass::REQUEST, MessageMethod::BINDING, $messageAttr);

        if ($this->isControllingRole()) {
            $message->attributes()->add(MessageAttribute::ICE_CONTROLLING, $this->tieBreaker);

            if ($nominate) {
                $message->attributes()->add(MessageAttribute::USE_CANDIDATE);
            }
        } else {
            $message->attributes()->add(MessageAttribute::ICE_CONTROLLED, $this->tieBreaker);
        }

        return $message;
    }

    /**
     * Sets the ICE role (controlling or controlled) for this agent.
     *
     * Updates the role of this ICE agent and re-sorts the checklist based on the new role.
     *
     * @param IceRole $role The new role to set
     * @return void
     */
    private function setRole(IceRole $role): void
    {
        $this->logger?->info(sprintf("Switching ice role to %s.", $role->name));
        $this->iceRole = $role;
        $this->sortCheckList();
    }

    /**
     * Marks a candidate pair as failed.
     *
     * Updates the state of a candidate pair to "failed" and performs
     * post-failure actions.
     *
     * @param RTCIceCandidatePair $pair The candidate pair to mark as failed
     * @return void
     */
    private function markPairFailed(RTCIceCandidatePair $pair): void
    {
        $this->changeCandidatePairState($pair, RTCIceCandidatePairStats::FAILED);
        $this->completeCheckAction($pair);
        $this->tryFailedCount++;
        $this->isBindingWait = false;
        $this->scheduleBindingCheck();
    }

    /**
     * Marks a candidate pair as successfully connected.
     *
     * Updates the state of a candidate pair to "succeeded" and performs
     * post-success actions.
     *
     * @param RTCIceCandidatePair $pair The candidate pair to mark as succeeded
     * @return void
     */
    private function markPairSucceeded(RTCIceCandidatePair $pair): void
    {
        $this->changeCandidatePairState($pair, RTCIceCandidatePairStats::SUCCEEDED);
        $this->completeCheckAction($pair);
        $this->tryFailedCount = 0; // Reset failure count
        $this->isBindingWait = false;
        $this->scheduleBindingCheck();
    }

    /**
     * Initiates reactive binding-check processing.
     *
     * Checklist processing advances in response to candidate and pair state changes.
     *
     * @return Future<void> Completes once a candidate pair has been nominated.
     */
    private function bindCheck(): Future
    {
        /** @var DeferredFuture<void> */
        $this->bindingCheck = new DeferredFuture();
        $future = $this->bindingCheck->getFuture();
        $this->scheduleBindingCheck();

        return $future;
    }

    /**
     * Queues checklist progression once, coalescing simultaneous state changes.
     */
    private function scheduleBindingCheck(): void
    {
        if ($this->bindingCheck === null || $this->bindingCheckScheduled) {
            return;
        }

        $this->bindingCheckScheduled = true;
        EventLoop::queue(function (): void {
            $this->bindingCheckScheduled = false;
            $this->advanceBindingCheck();
        });
    }

    /**
     * Advances the checklist until a check is in flight or negotiation settles.
     */
    private function advanceBindingCheck(): void
    {
        if ($this->bindingCheck === null) {
            return;
        }

        if (isset($this->checkListState) && $this->checkListState === CheckListState::ICE_COMPLETED) {
            $this->settleBindingCheck();
            return;
        }

        if ((isset($this->checkListState) && $this->checkListState === CheckListState::ICE_FAILED)
            || $this->tryFailedCount > self::RETRY_BINDING_MAX
        ) {
            $this->settleBindingCheck(new RuntimeException("Binding check failed"));
            return;
        }

        if ($this->isBindingWait) {
            return;
        }

        $this->isBindingWait = true;
        if ($this->tryCheckBinding()) {
            $this->settleBindingCheck();
            return;
        }

        // tryCheckBinding leaves isBindingWait set when a check is already in flight (a
        // triggered check from processEarlyChecks starts one before bindCheck runs).
        // Failing here would abort ICE while that transaction is still outstanding â€” the
        // race that makes the early-check exchange fail on runners slower than loopback.
        if ($this->isBindingWait) {
            return;
        }

        if ($this->tryFailedCount > self::RETRY_BINDING_MAX) {
            $this->settleBindingCheck(new RuntimeException("Binding check failed"));
        }
    }

    /**
     * Completes the active binding check exactly once.
     */
    private function settleBindingCheck(?Throwable $error = null): void
    {
        $bindingCheck = $this->bindingCheck;
        if ($bindingCheck === null) {
            return;
        }

        $this->bindingCheck = null;
        $this->isBindingWait = false;

        if ($error === null) {
            $bindingCheck->complete();
        } else {
            $bindingCheck->error($error);
        }
    }

    /**
     * Attempts to find and start checking a candidate pair.
     *
     * Looks for pairs in the "waiting" or "frozen" states to begin connectivity
     * checks on, or determine if the ICE process has completed or failed.
     *
     * @return bool True if ICE has completed, false if checks are still ongoing
     */
    private function tryCheckBinding(): bool
    {
        if (array_any([RTCIceCandidatePairStats::WAITING, RTCIceCandidatePairStats::FROZEN], fn($state) => $this->tryCheckBindingByPairState($state))) {
            return false;
        }

        // Nothing new was started. If a check is already in flight (typical for early
        // Binding requests processed just before bindCheck), a connectivity check is
        // outstanding: wait for its response instead of declaring the checklist failed.
        if (array_any($this->checkList, fn($p) => $p->getState() === RTCIceCandidatePairStats::IN_PROGRESS)) {
            return false;
        }

        // No check is outstanding. The flag means "a check is in flight" and is
        // otherwise only cleared when a pair reaches a terminal state, so leaving
        // it set here makes every later tick return early and the exchange stalls.
        $this->isBindingWait = false;

        if (empty($this->checkList)) {
            if ($this->remoteCandidatesEnd) {
                $this->tryFailedCount = self::RETRY_BINDING_MAX + 1;
            }
            return false;
        }

        // A controlled agent with a valid pair waits reactively for USE-CANDIDATE.
        if ($this->isControlledRole() && $this->hasAnySucceededPair()) {
            return false;
        }

        if ($this->remoteCandidatesEnd) {
            $this->tryFailedCount = self::RETRY_BINDING_MAX + 1;
        }

        return $this->remoteCandidatesEnd && $this->checkListDone;
    }

    /**
     * Attempts to find and check pairs in a specific state.
     *
     * Looks for candidate pairs in a given state and attempt to start
     * connectivity checks on them.
     *
     * @param RTCIceCandidatePairStats $state The state to look for
     * @return bool True if a pair was found and checking started, false otherwise
     */
    private function tryCheckBindingByPairState(RTCIceCandidatePairStats $state): bool
    {
        return array_any($this->checkList, fn($pair) => $pair->getState() === $state && $this->tryStartCheckBinding($pair));
    }

    /**
     * Attempts to start connectivity checking on a candidate pair.
     *
     * Tries to initiate a connectivity check on a specific candidate pair,
     * handling any exceptions that may occur.
     *
     * @param RTCIceCandidatePair $pair The candidate pair to check
     * @return bool True if the check was started successfully, false otherwise
     */
    private function tryStartCheckBinding(RTCIceCandidatePair $pair): bool
    {
        try {
            $this->startCheckBinding($pair);
            return true;
        } catch (Throwable $e) {
            $this->logger?->debug("Couldn't start connectivity check for candidate pair: {$e->getMessage()}");
            return false;
        }
    }

    /**
     * Processes the completion of a connectivity check.
     *
     * Handles actions after a connectivity check is completed, including
     * updating the checklist state and potentially finalizing the ICE process.
     *
     * @param RTCIceCandidatePair $pair The candidate pair that completed checking
     * @return void
     */
    #[Override]
    public function completeCheckAction(RTCIceCandidatePair $pair): void
    {
        if ($pair->getState() === RTCIceCandidatePairStats::SUCCEEDED) {
            if ($this->handleSuccessfulPair($pair)) {
                return;
            }
        }

        if ($this->checkListHasPendingPairs()) {
            return;
        }

        if ($this->isControlledRole() && $this->hasAnySucceededPair()) {
            return;
        }

        $this->finalizeChecklistState();
    }

    /**
     * Processes a successfully checked candidate pair.
     *
     * Updates the connection state based on a successful connectivity check,
     * including a handling nomination and unfreezing related pairs.
     *
     * @param RTCIceCandidatePair $pair The successfully checked candidate pair
     * @return bool True if ICE has completed, false otherwise
     */
    private function handleSuccessfulPair(RTCIceCandidatePair $pair): bool
    {
        if ($pair->isNominated()) {
            // Checks run in parallel, so several pairs can be nominated: the highest-priority one is
            // used, rather than whichever was nominated last (RFC 8445, section 8.1.1). Once the
            // keepalives changed the pair, they choose it instead (see periodicConsentCheck()).
            $current = $this->nominated[$pair->getComponentId()] ?? null;
            if ($current === null || (!$this->keepaliveSwitched && $this->getPairPriority($pair) > $this->getPairPriority($current))) {
                $this->nominated[$pair->getComponentId()] = $pair;
            }
            $this->failOtherPairsInComponent($pair);
        }

        if ($this->checkIfICECompleted()) {
            $this->suspendUnusedResource();

            return true;
        }

        $this->updateFrozenPairs($pair);

        return false;
    }

    /**
     * Fails all other pairs for a component once one pair is nominated.
     *
     * Marks waiting or frozen pairs as failed when another pair in the same
     * component has been nominated.
     *
     * @param RTCIceCandidatePair $pair The nominated pair
     * @return void
     */
    private function failOtherPairsInComponent(RTCIceCandidatePair $pair): void
    {
        foreach ($this->checkList as $p) {
            if ($p->getComponentId() === $pair->getComponentId() &&
                in_array($p->getState(), [RTCIceCandidatePairStats::WAITING, RTCIceCandidatePairStats::FROZEN])) {
                $this->changeCandidatePairState($p, RTCIceCandidatePairStats::FAILED);
            }
        }
    }

    /**
     * Checks if the ICE process has completed successfully.
     *
     * Determines if all components have nominated pairs, indicating that
     * ICE connectivity establishment has completed successfully.
     *
     * @return bool True if ICE has completed, false otherwise
     */
    private function checkIfICECompleted(): bool
    {
        if (count($this->nominated) === count($this->componentIds)) {
            if (!$this->checkListDone) {
                $this->logger?->info("ICE completed");
                $this->checkListState = CheckListState::ICE_COMPLETED;
                $this->checkListDone = true;
            }

            return true;
        }

        return false;
    }

    /**
     * Updates frozen pairs with the same foundation as a successful pair.
     *
     * Changes the state of frozen pairs with the same foundation as a successful
     * pair from "frozen" to "waiting".
     *
     * @param RTCIceCandidatePair $pair The successfully checked pair
     * @return void
     */
    private function updateFrozenPairs(RTCIceCandidatePair $pair): void
    {
        foreach ($this->checkList as $p) {
            if ($p->getLocalCandidate()->getFoundation() === $pair->getLocalCandidate()->getFoundation() &&
                $p->getState() === RTCIceCandidatePairStats::FROZEN) {
                $this->changeCandidatePairState($p, RTCIceCandidatePairStats::WAITING);
            }
        }
    }

    /**
     * Checks if the checklist contains any pairs in a pending state.
     *
     * Determines if any candidate pairs are still in a state other than
     * "succeeded" or "failed".
     *
     * @return bool True if there are pending pairs, false otherwise
     */
    private function checkListHasPendingPairs(): bool
    {
        return array_any($this->checkList, fn($p) => !in_array($p->getState(), [RTCIceCandidatePairStats::SUCCEEDED, RTCIceCandidatePairStats::FAILED]));
    }

    /**
     * Checks if any candidate pair has succeeded.
     *
     * Determines if at least one candidate pair has successfully completed
     * connectivity checks.
     *
     * @return bool True if any pair has succeeded, false otherwise
     */
    private function hasAnySucceededPair(): bool
    {
        return array_any($this->checkList, fn($p) => $p->getState() === RTCIceCandidatePairStats::SUCCEEDED);
    }

    /**
     * Finalizes the checklist state when all checks are complete.
     *
     * Sets the checklist state to fail if all checks have completed
     * without finding valid connectivity.
     *
     * @return void
     */
    private function finalizeChecklistState(): void
    {
        $this->tryFailedCount++;
        if (!$this->checkListDone) {
            $this->logger?->error("ICE failed");
            $this->checkListState = CheckListState::ICE_FAILED;
            $this->checkListDone = true;
        }
    }

    /**
     * Keeps the connection alive once established, switching to another path when needed.
     *
     * STUN binding requests are sent periodically on the selected pair of each component, and less
     * often on the other valid pairs (the backups), keeping their NAT bindings open and measuring
     * their round-trip times. If the selected pair stops answering, the best backup that still
     * answers is selected; a backup that is clearly faster is also selected, with hysteresis.
     * Consent to send (RFC 7675) only expires once no pair of a component answered for 30 seconds.
     *
     * @see https://www.rfc-editor.org/rfc/rfc7675
     *
     * @return void
     */
    #[Override]
    public function periodicConsentCheck(): void
    {
        $now = microtime(true);
        $this->lastSwitchAt = $now;
        foreach ($this->checkList as $pair) {
            if ($pair->getState() === RTCIceCandidatePairStats::SUCCEEDED) {
                $pair->touch($now);
            }
        }
        foreach ($this->nominated as $pair) {
            $pair->touch($now);
        }

        // A repeat watcher registered as $this->onConsentTimer(...) captures $this strongly and
        // would keep the event loop holding this whole ICE connection alive forever, so an
        // unset()+gc_collect_cycles() could never reclaim it. Hold only a weak reference and let
        // the tick cancel itself once the owner has been collected.
        $weak = \WeakReference::create($this);
        $this->queryConsentTimer = EventLoop::repeat(self::KEEPALIVE_TICK * $this->timeScale, static function (string $id) use ($weak): void {
            $self = $weak->get();
            if ($self === null) {
                EventLoop::cancel($id);

                return;
            }
            $self->onConsentTimer();
        });
    }

    /**
     * Multiplies the intervals and timeouts of the keepalives, to test them quickly.
     *
     * @internal
     */
    public function setTimeScale(float $timeScale): void
    {
        $this->timeScale = $timeScale;
    }

    /**
     * Randomizes the interval between two checks of a pair, as RFC 7675 (section 5.1) requires.
     */
    private static function randomizeInterval(float $interval): float
    {
        return $interval * (0.8 + 0.4 * (float)mt_rand() / (float)mt_getrandmax());
    }

    /**
     * Closes the ICE connection and releases all associated resources.
     *
     * Stops timers, clears protocol and candidate references,
     * marks the checklist as failed, and emits an 'onClose' event.
     *
     * @return void
     * @throws RandomException
     *
     * @throws Throwable
     */
    #[Override]
    public function close(): void
    {
        // The sockets closed here are not given up one by one (see onClose()).
        $this->closing = true;
        $this->stopPeriodicConsentCheck();
        $this->markCheckListAsFailed();
        $this->clearResources();
        $this->notifyClosed();
        /** @var \WeakMap<IceConnectionClosedListener, null> */
        $this->closedListeners = new \WeakMap();
        /** @var \WeakMap<IceConnectionDataListener, null> */
        $this->dataListeners = new \WeakMap();
        $this->settleBindingCheck(new RuntimeException("Binding check failed"));

        if (!$this->closed) {
            $this->closed = true;
            $this->logger?->info("ICE connection closed");
        }
    }

    /**
     * Stops the timer responsible for periodic consent checks.
     *
     * If the timer is active, it will be canceled and removed.
     *
     * @return void
     */
    private function stopPeriodicConsentCheck(): void
    {
        if ($this->queryConsentTimer !== null) {
            EventLoop::cancel($this->queryConsentTimer);
            $this->queryConsentTimer = null;
        }
    }

    /**
     * Marks the connection's checklist as failed if it has not already been processed.
     *
     * This indicates that ICE negotiation has failed.
     *
     * @return void
     */
    private function markCheckListAsFailed(): void
    {
        if (!empty($this->checkList) && !$this->checkListDone) {
            $this->checkListState = CheckListState::ICE_FAILED;
        }
    }

    /**
     * Releases all protocol and candidate resources associated with this ICE connection.
     *
     * Iterates through all protocols and closes/deletes them safely.
     *
     * @return void
     * @throws RandomException
     *
     * @throws Throwable
     */
    private function clearResources(): void
    {
        foreach ($this->protocols as $protocol) {
            if ($protocol instanceof Turn) {
                @$protocol->delete();
            }
            @$protocol->close();
        }

        $this->nominated = [];
        $this->protocols = [];
        $this->localCandidates = [];
    }

    /**
     * Sends application data over the nominated candidate pair for a specific component.
     *
     * @param string $data The data to send.
     * @param int $componentId The component ID (default is 1).
     *
     * @return void
     * @throws RuntimeException If no nominated pair exists for the given component.
     *
     */
    #[Override]
    public function sendData(string $data, int $componentId = 1): void
    {
        if (!isset($this->nominated[$componentId])) {
            throw new RuntimeException("No Connection");
        }
        $pair = $this->nominated[$componentId];
        try {
            $pair->getProtocol()->send($data, $pair->getRemoteAddress());
        } catch (Throwable $e) {
            // Like any datagram that doesn't make it, it's lost: the keepalives notice that the path is
            // down, and switch to another one. Throwing would make the senders give up on the connection.
            $this->logger?->debug("Could not send on $pair: {$e->getMessage()}");
        }
    }

    /**
     * Handles incoming application data.
     *
     * Emits a 'data' event containing the received data and component ID.
     *
     * @param string $data The received data.
     * @param int $componentId The ID of the receiving component.
     *
     * @return void
     */
    #[Override]
    public function onDataReceived(string $data, int $componentId): void
    {
        foreach ($this->dataListeners as $listener => $_) {
            $listener->onIceConnectionData($data, $componentId);
        }
    }

    /**
     * Handles an incoming STUN binding request.
     *
     * Validates and responds to the request, handling role conflicts and authentication.
     * Also processes early or ongoing connectivity checks.
     *
     * @param MessageInterface $message The incoming STUN message.
     * @param InternetAddress $address The address from which the message was received.
     * @param IceConnectionProtocolInterface $protocol The protocol used for this connection.
     * @param string $data The raw received data.
     *
     * @return void
     * @throws RandomException
     *
     */
    #[Override]
    public function onRequestReceived(MessageInterface $message, InternetAddress $address, IceConnectionProtocolInterface $protocol, string $data): void
    {
        // A request relayed by TURN comes from the connection of the allocation: the pairs use the protocol that
        // wraps it, which relays the data too.
        $protocol = $this->protocols[$protocol->getId()] ?? $protocol;
        if (!$this->isBindingRequest($message) || !$this->authenticateRequest($message, $data)) {
            $this->respondError($message, $address, $protocol, [400, "Bad Request"]);

            return;
        }

        if ($this->handleRoleConflict($message, $address, $protocol)) {
            return;
        }

        // Send success binding response
        $this->sendBindingResponse($message, $address, $protocol);
        if (empty($this->checkList) && !$this->earlyChecksDone) {
            $this->earlyChecks[] = [$message, $address, $protocol];
        } else {
            $this->checkIncoming($message, $address, $protocol);
        }
    }

    /**
     * Determines if a given message is a STUN binding request.
     *
     * @param MessageInterface $message The STUN message to evaluate.
     *
     * @return bool True if it's a binding request; false otherwise.
     */
    public function isBindingRequest(MessageInterface $message): bool
    {
        return $message->getMessageMethod() === MessageMethod::BINDING;
    }

    /**
     * Validates the authentication of a STUN binding request.
     *
     * Checks message integrity and username attributes to ensure authenticity.
     *
     * @param MessageInterface $message The STUN message to validate.
     * @param string $data The raw STUN message data.
     *
     * @return bool True if authenticated; false otherwise.
     */
    public function authenticateRequest(MessageInterface $message, string $data): bool
    {
        try {
            Message::decode($data, $this->localPassword);

            if ($this->remoteUsername !== null) {
                $expectedUsername = sprintf("%s:%s", $this->localUsername, $this->remoteUsername);
                return $message->attributes()->get(MessageAttribute::USERNAME) === $expectedUsername;
            }

            return true;
        } catch (Throwable $e) {
            $this->logger?->error("Authentication failed: " . $e->getMessage());
            return false;
        }
    }

    /**
     * Handles ICE role conflict resolution between peers.
     *
     * If a role conflict is detected, either resolves the conflict or sends a role conflict error.
     *
     * @param MessageInterface $message The incoming message.
     * @param InternetAddress $address The sender address.
     * @param IceConnectionProtocolInterface $protocol The associated protocol.
     *
     * @return bool True, if a conflict was handled and the message shouldnâ€™t be processed further.
     * @throws RandomException
     *
     */
    public function handleRoleConflict(MessageInterface $message, InternetAddress $address, IceConnectionProtocolInterface $protocol): bool
    {
        $attributes = $message->attributes();

        if ($this->isControllingRole() && $attributes->has(MessageAttribute::ICE_CONTROLLING)) {
            $this->logger?->info("Role conflict detected: expected controlling role.");
            /** @var int|null $controlling */
            $controlling = $attributes->get(MessageAttribute::ICE_CONTROLLING);
            if (is_int($controlling) && $this->compareTieBreaker($controlling) >= 0) {
                $this->respondError($message, $address, $protocol, [487, "Role Conflict"]);

                return true;
            }

            $this->setRole(IceRole::Controlled);
        } elseif ($this->isControlledRole() && $attributes->has(MessageAttribute::ICE_CONTROLLED)) {
            $this->logger?->info("Role conflict detected: expected controlled role.");
            /** @var int|null $controlled */
            $controlled = $attributes->get(MessageAttribute::ICE_CONTROLLED);
            if (is_int($controlled) && $this->compareTieBreaker($controlled) < 0) {
                $this->respondError($message, $address, $protocol, [487, "Role Conflict"]);

                return true;
            }

            $this->setRole(IceRole::Controlling);
        }

        return false;
    }

    private function compareTieBreaker(int $remoteTieBreaker): int
    {
        return ($this->tieBreaker ^ PHP_INT_MIN) <=> ($remoteTieBreaker ^ PHP_INT_MIN);
    }

    /**
     * Sends a STUN binding success response to a validated request.
     *
     * Adds the necessary attributes and message integrity, then sends the response asynchronously.
     *
     * @param MessageInterface $message The original binding request.
     * @param InternetAddress $address The destination address.
     * @param IceConnectionProtocolInterface $protocol The protocol to use for sending.
     *
     * @return void
     * @throws RandomException
     *
     */
    public function sendBindingResponse(MessageInterface $message, InternetAddress $address, IceConnectionProtocolInterface $protocol): void
    {
        $responseAttributes = [MessageAttribute::XOR_MAPPED_ADDRESS->name => $address];
        $responseMessage = Message::new(MessageClass::RESPONSE, MessageMethod::BINDING, $responseAttributes);
        $responseMessage->setTransactionId($message->getTransactionId());
        $responseMessage->addMessageIntegrity($this->localPassword);

        // A response is not a transaction: nothing answers it, so it's just sent (like error responses).
        $protocol->sendMessage($responseMessage, $address);
    }

    /**
     * Sends a STUN error response to an invalid or unauthenticated request.
     *
     * Constructs and sends a binding error message using the given error code.
     *
     * @param MessageInterface $orgMessage The original message to respond to.
     * @param InternetAddress $address The address to send the error to.
     * @param IceConnectionProtocolInterface $protocol The protocol used for transmission.
     * @param array $errorCode The error code and reason [code, reason].
     *
     * @return void
     * @throws RandomException
     *
     */
    #[Override]
    public function respondError(MessageInterface $orgMessage, InternetAddress $address, IceConnectionProtocolInterface $protocol, array $errorCode): void
    {
        $messageAttr = [MessageAttribute::ERROR_CODE->name => $errorCode];
        $message = Message::new(MessageClass::ERROR, $orgMessage->getMessageMethod(), $messageAttr);
        $message->setTransactionId($orgMessage->getTransactionId());
        $message->addMessageIntegrity($this->localPassword);

        $protocol->sendMessage($message, $address);
    }

    /**
     * Checks whether the local agent is currently in the controlling role.
     *
     * @return bool True if in controlling role, false otherwise.
     */
    public function isControllingRole(): bool
    {
        return $this->iceRole === IceRole::Controlling;
    }

    /**
     * Checks whether the local agent is currently in the controlled role.
     *
     * @return bool True if in controlled role, false otherwise.
     */
    private function isControlledRole(): bool
    {
        return $this->iceRole === IceRole::Controlled;
    }

    /**
     * Called when the socket of a protocol closed.
     *
     * Only the pairs using it are given up, switching to a backup if a selected pair used it: the
     * connection is only closed once none of its sockets is left.
     *
     * @param IceConnectionProtocolInterface|null $protocol The protocol whose socket closed.
     *
     * @return void
     */
    #[Override]
    public function onClose(?IceConnectionProtocolInterface $protocol = null): void
    {
        if ($this->closed || $this->closing) {
            return;
        }
        if ($protocol === null) {
            $this->notifyClosed();
            return;
        }
        $id = $protocol->getId();
        if (!isset($this->protocols[$id])) {
            return;
        }
        unset($this->protocols[$id]);
        foreach ($this->checkList as $pair) {
            if ($pair->getProtocol()->getId() === $id && $pair->getState() !== RTCIceCandidatePairStats::FAILED) {
                $this->changeCandidatePairState($pair, RTCIceCandidatePairStats::FAILED);
            }
        }
        if ($this->protocols === []) {
            $this->logger?->info("All the sockets of the ICE connection closed");
            $this->notifyClosed();
            return;
        }

        $now = microtime(true);
        foreach ($this->nominated as $componentId => $selected) {
            if ($selected->getProtocol()->getId() !== $id) {
                continue;
            }
            $backups = array_values(array_filter(
                $this->getValidPairs($componentId),
                fn (RTCIceCandidatePair $pair): bool => $pair !== $selected && $pair->respondedWithin(self::BACKUP_ALIVE_TIMEOUT * $this->timeScale, $now)
            ));
            // A backup that did not answer recently may still work: a closed socket certainly doesn't.
            $best = $this->getCheapestPair($backups, 0) ?? $this->getCheapestPair(array_values(array_filter(
                $this->getValidPairs($componentId),
                static fn (RTCIceCandidatePair $pair): bool => $pair !== $selected && $pair->getState() === RTCIceCandidatePairStats::SUCCEEDED
            )), 0);
            if ($best !== null) {
                $this->switchPair($componentId, $best, $now, "the socket of the selected pair closed");
            }
        }
    }

    /**
     * Invoked when an error occurs.
     *
     * @param Throwable $e The exception or error encountered.
     *
     * @return void
     */
    #[Override]
    public function onError(Throwable $e): void
    {
    }

    /**
     * Suspends unused transport resources that are not part of nominated candidate pairs.
     *
     * Intended to optimize resource usage by pausing inactive transports.
     * Currently disabled (FixMe).
     *
     * @return void
     */
    private function suspendUnusedResource(): void
    {
        // FixMe: it closes all resources
//        $unUsedProtocols = array_udiff($this->protocols, $this->nominated, function (Stun|Turn $protocol, RTCIceCandidatePair $pair) {
//            return strcmp($protocol->getId(), $pair->getProtocol()->getId());
//        });
//
//        foreach ($unUsedProtocols as $protocol) {
//            $protocol->pause();
//        }
    }

    /**
     * Returns the current ICE role (Controlling or Controlled).
     *
     * @return IceRole The current ICE role.
     */
    #[Override]
    public function getIceRole(): IceRole
    {
        return $this->iceRole;
    }

    /**
     * Checks if the ICE connection is closed.
     *
     * @return bool True if closed, false otherwise.
     */
    public function isClosed(): bool
    {
        return $this->closed;
    }

    /**
     * Gets the default local candidate for a specific component.
     *
     * @param int $component The component ID.
     *
     * @return RTCIceCandidate|null The candidate with the highest priority, or null if not found.
     */
    public function getDefaultCandidate(int $component): ?RTCIceCandidate

    {
        $sortedCandidates = $this->localCandidates;
        usort($sortedCandidates, fn(RTCIceCandidate $a, RTCIceCandidate $b) => $a->getPriority() <=> $b->getPriority());

        foreach ($sortedCandidates as $candidate) {
            if ($candidate->getComponentId() === $component) {
                return $candidate;
            }
        }
        return null;
    }

    /**
     * Returns the current ICE checklist (candidate pairs to check).
     *
     * @return array The checklist array.
     */
    public function getCheckList(): array
    {
        return $this->checkList;
    }

    /**
     * Sets the flag indicating whether remote candidates have finished being provided.
     *
     * @param bool $remoteCandidatesEnd True if all remote candidates are gathered.
     *
     * @return void
     */
    public function setRemoteCandidatesEnd(bool $remoteCandidatesEnd): void
    {
        $this->remoteCandidatesEnd = $remoteCandidatesEnd;
        $this->scheduleBindingCheck();
    }

    /**
     * Sets the tie-breaker value used during ICE role conflict resolution.
     *
     * @param int $tieBreaker A unique 64-bit tiebreaker value.
     *
     * @return void
     */
    public function setTieBreaker(int $tieBreaker): void
    {
        $this->tieBreaker = $tieBreaker;
    }

    /**
     * Sets the consent check timer (only for testing purposes).
     *
     * @param string|null $queryConsentTimer The timer handle or null to unset.
     *
     * @return void
     */
    public function setQueryConsentTimer(?string $queryConsentTimer): void
    {
        $this->queryConsentTimer = $queryConsentTimer;
    }

    /**
     * Gets the currently nominated candidate pairs.
     *
     * @return array The nominated candidate pairs.
     */
    public function getNominated(): array
    {
        return $this->nominated;
    }

    /**
     * Checks whether the end-of-candidates signal has been received from the remote peer.
     *
     * @return bool True if no more remote candidates are expected.
     */
    #[Override]
    public function isRemoteCandidatesEnd(): bool
    {
        return $this->remoteCandidatesEnd;
    }

    /**
     * Sets the transport policy for the ICE connection.
     *
     * Validates that required servers are configured for the RELAY-only policy.
     *
     * @param TransportPolicyType $transportPolicy The desired transport policy.
     *
     * @return void
     * @throws InvalidArgumentException If required, servers are missing.
     *
     */
    public function setTransportPolicy(TransportPolicyType $transportPolicy): void
    {
        if ($this->configuration->getStunServer() === null
            && $this->configuration->getTurnServer() === null
            && $transportPolicy === TransportPolicyType::RELAY) {
            throw new InvalidArgumentException("Relay transport policy requires a STUN and/or TURN server.");
        }

        $this->transportPolicy = $transportPolicy;
    }

    /**
     * Sets the current ICE role (Controlling or Controlled).
     *
     * @param IceRole $iceRole The ICE role to set.
     *
     * @return void
     */
    #[Override]
    public function setIceRole(IceRole $iceRole): void

    {
        $this->iceRole = $iceRole;
    }

    public function setIcePortRange(?array $icePortRange): void
    {
        $this->icePortRange = $icePortRange;
    }

    /**
     * @return array<int, string>|null
     */
    public function getNat1to1(): ?array
    {
        return $this->nat1to1;
    }

    /**
     * @param array<int, string>|null $nat1to1
     */
    public function setNat1to1(?array $nat1to1): void
    {
        $this->nat1to1 = $nat1to1;
    }

    /**
     * Consent-freshness tick.
     *
     * Scheduled only as the first-class callable `$this->onConsentTimer(...)` handed to
     * EventLoop::repeat(), including when the watcher is re-armed after unserialize. That callable
     * captures this method's private scope, so the event loop can drive it while it stays private.
     */
    private function onConsentTimer(): void
    {
        $now = microtime(true);
        foreach ($this->nominated as $componentId => $selected) {
            foreach ($this->getValidPairs($componentId) as $pair) {
                $interval = ($pair === $selected ? self::KEEPALIVE_INTERVAL : self::BACKUP_INTERVAL) * $this->timeScale;
                $lastPingAt = $pair->getLastPingAt();
                if (!$pair->isPinging() && ($lastPingAt === null || $now - $lastPingAt >= self::randomizeInterval($interval))) {
                    $this->ping($pair);
                }
            }
            // The pairs that were not checked when ICE completed (they were cut off by the nomination,
            // or formed afterwards with candidates trickled late) are checked once, one at a time, to
            // find more backups.
            if ($this->lastDiscoveryAt === null || $now - $this->lastDiscoveryAt >= self::KEEPALIVE_INTERVAL * $this->timeScale) {
                $discovering = false;
                $next = null;
                foreach ($this->checkList as $pair) {
                    if ($pair->getComponentId() !== $componentId
                        || $pair->getLastResponseAt() !== null
                        || \in_array($pair->getState(), [RTCIceCandidatePairStats::IN_PROGRESS, RTCIceCandidatePairStats::SUCCEEDED], true)
                    ) {
                        continue;
                    }
                    if ($pair->isPinging()) {
                        $discovering = true;
                        break;
                    }
                    if ($pair->getLastPingAt() === null) {
                        $next ??= $pair;
                    }
                }
                if (!$discovering && $next !== null) {
                    $this->lastDiscoveryAt = $now;
                    $this->ping($next);
                }
            }
            $this->selectPair($componentId, $now);
            if ($this->closed) {
                return;
            }
        }
    }

    /**
     * Gets the pairs of a component that were validated by a connectivity check: the selected one and its backups.
     *
     * Backups that did not answer for the whole consent timeout are given up.
     *
     * @return list<RTCIceCandidatePair>
     */
    private function getValidPairs(int $componentId): array
    {
        $now = microtime(true);
        $selected = $this->nominated[$componentId] ?? null;
        $valid = [];
        foreach ($this->checkList as $pair) {
            if ($pair->getComponentId() !== $componentId || $pair->getState() !== RTCIceCandidatePairStats::SUCCEEDED) {
                continue;
            }
            if ($pair !== $selected && !$pair->respondedWithin(self::CONSENT_TIMEOUT * $this->timeScale, $now)) {
                $this->logger?->debug(sprintf("Giving up backup pair %s: it did not answer for %.1f seconds", $pair, self::CONSENT_TIMEOUT * $this->timeScale));
                $this->changeCandidatePairState($pair, RTCIceCandidatePairStats::FAILED);
                continue;
            }
            $valid[] = $pair;
        }
        if ($selected !== null && !in_array($selected, $valid, true)) {
            $valid[] = $selected;
        }
        return $valid;
    }

    /**
     * Sends a keepalive binding request on a pair, measuring its round-trip time.
     */
    private function ping(RTCIceCandidatePair $pair): void
    {
        $pair->setPinging(true, microtime(true));
        $message = $this->buildBindingMessage($pair, false);
        $remoteAddress = $pair->getRemoteAddress();
        $password = $this->remotePassword;
        $retransmissions = \in_array($pair, $this->nominated, true) ? self::KEEPALIVE_RETRANSMISSIONS : self::BACKUP_RETRANSMISSIONS;

        // Weak self-reference so an in-flight check (which awaits a STUN transaction) does not pin
        // this connection past an unset(); if it was collected mid-flight, the check simply stops.
        $weak = \WeakReference::create($this);
        async(static function () use ($weak, $pair, $message, $remoteAddress, $password, $retransmissions): void {
            $start = microtime(true);
            try {
                $pair->getProtocol()->request($message, $remoteAddress, $password, $retransmissions);
                $now = microtime(true);
                $pair->recordResponse($now - $start, $now);
                $self = $weak->get();
                if ($self !== null && !$self->closed && $pair->getState() !== RTCIceCandidatePairStats::SUCCEEDED) {
                    // A pair that was not checked when ICE completed: now a backup.
                    $self->changeCandidatePairState($pair, RTCIceCandidatePairStats::SUCCEEDED);
                }
            } catch (Throwable $e) {
                $weak->get()?->logger?->debug("Keepalive check failed for pair: $pair. Error: {$e->getMessage()}");
            } finally {
                $pair->setPinging(false, microtime(true));
            }
            $self = $weak->get();
            if ($self !== null && !$self->closed) {
                $self->selectPair($pair->getComponentId(), microtime(true));
            }
        })->ignore();
    }

    /**
     * Selects the pair a component sends on: a backup if the selected pair stopped answering, or if a
     * backup is clearly faster.
     */
    private function selectPair(int $componentId, float $now): void
    {
        $selected = $this->nominated[$componentId] ?? null;
        if ($selected === null || $this->closed) {
            return;
        }
        $backups = array_values(array_filter(
            $this->getValidPairs($componentId),
            fn (RTCIceCandidatePair $pair): bool => $pair !== $selected && $pair->respondedWithin(self::BACKUP_ALIVE_TIMEOUT * $this->timeScale, $now)
        ));

        if (!$selected->respondedWithin(self::UNWRITABLE_TIMEOUT * $this->timeScale, $now)) {
            $best = $this->getCheapestPair($backups, 0);
            if ($best !== null) {
                $this->switchPair($componentId, $best, $now, "the selected pair stopped answering");
            } elseif (!$selected->respondedWithin(self::CONSENT_TIMEOUT * $this->timeScale, $now)) {
                $this->logger?->error(sprintf("Consent to send expired: no pair answered for %.1f seconds", self::CONSENT_TIMEOUT * $this->timeScale));
                $this->close();
            }
            return;
        }

        if ($this->lastSwitchAt !== null && $now - $this->lastSwitchAt < self::MIN_SWITCH_INTERVAL * $this->timeScale) {
            return;
        }
        $selectedCost = $this->getPairCost($selected);
        $best = $this->getCheapestPair($backups, self::MIN_RTT_SAMPLES);
        $bestCost = $best !== null ? $this->getPairCost($best) : null;
        if ($best !== null && $selectedCost !== null && $bestCost !== null
            && $bestCost < $selectedCost * self::SWITCH_IMPROVEMENT
            && $selectedCost - $bestCost >= self::MIN_SWITCH_GAIN
        ) {
            $this->switchPair($componentId, $best, $now, "it has a lower latency");
        }
    }

    /**
     * Gets the pair with the lowest cost, among the ones with enough round-trip time samples.
     *
     * @param list<RTCIceCandidatePair> $pairs
     */
    private function getCheapestPair(array $pairs, int $minSamples): ?RTCIceCandidatePair
    {
        $pairs = array_values(array_filter($pairs, static fn (RTCIceCandidatePair $pair): bool => $pair->getRttSamples() >= $minSamples));
        // The pairs without a measured round-trip time rank by their priority, after the measured ones.
        usort($pairs, function (RTCIceCandidatePair $a, RTCIceCandidatePair $b): int {
            $costA = $this->getPairCost($a);
            $costB = $this->getPairCost($b);
            if ($costA !== null && $costB !== null) {
                return $costA <=> $costB;
            }
            if ($costA !== null || $costB !== null) {
                return $costA !== null ? -1 : 1;
            }
            return $this->getPairPriority($b) <=> $this->getPairPriority($a);
        });
        return $pairs[0] ?? null;
    }

    /**
     * Gets the cost of sending on a pair: its round-trip time, higher for relayed pairs.
     */
    private function getPairCost(RTCIceCandidatePair $pair): ?float
    {
        $rtt = $pair->getRtt();
        if ($rtt === null) {
            return null;
        }
        $relayed = $pair->getLocalCandidate()->getType() === CandidateType::relay
            || $pair->getRemoteCandidate()->getType() === CandidateType::relay;
        return $rtt + ($relayed ? self::RELAY_PENALTY : 0.0);
    }

    /**
     * Changes the pair a component sends on.
     */
    private function switchPair(int $componentId, RTCIceCandidatePair $pair, float $now, string $reason): void
    {
        $previous = $this->nominated[$componentId];
        $this->nominated[$componentId] = $pair;
        $this->lastSwitchAt = $now;
        $this->keepaliveSwitched = true;
        $this->logger?->info(sprintf(
            "Switching from %s (round-trip time %s) to %s (round-trip time %s): %s",
            $previous,
            self::formatRtt($previous->getRtt()),
            $pair,
            self::formatRtt($pair->getRtt()),
            $reason
        ));
    }

    private static function formatRtt(?float $rtt): string
    {
        return $rtt === null ? "unknown" : sprintf("%.1fms", $rtt * 1000.0);
    }

    /**
     * @return array<string, mixed>
     */
    public function __serialize(): array
    {
        $state = SerializableState::export($this, [
            'bindingCheck' => null,
            'bindingCheckScheduled' => false,
            'queryConsentTimer' => $this->queryConsentTimer !== null,
            // WeakMaps cannot be serialized; snapshot their keys and rebuild on the far side.
            'dataListeners' => ['__uninitialized' => true],
            'closedListeners' => ['__uninitialized' => true],
        ]);
        $state['__dataListeners'] = SerializableState::weakMapToList($this->dataListeners);
        $state['__closedListeners'] = SerializableState::weakMapToList($this->closedListeners);

        return $state;
    }

    /**
     * @param array<string, mixed> $data
     */
    public function __unserialize(array $data): void
    {
        /** @var list<IceConnectionDataListener> $dataListeners */
        $dataListeners = $data['__dataListeners'] ?? [];
        /** @var list<IceConnectionClosedListener> $closedListeners */
        $closedListeners = $data['__closedListeners'] ?? [];
        unset($data['__dataListeners'], $data['__closedListeners']);

        $restartConsent = false;
        /**
         * @var mixed $value
         */
        foreach ($data as $key => $value) {
            if (str_ends_with($key, "\0queryConsentTimer")) {
                $restartConsent = $value === true;
                $data[$key] = null;
            }
        }
        SerializableState::import($this, $data);
        /** @var \WeakMap<IceConnectionDataListener, null> */
        $this->dataListeners = SerializableState::listToWeakMap($dataListeners);
        /** @var \WeakMap<IceConnectionClosedListener, null> */
        $this->closedListeners = SerializableState::listToWeakMap($closedListeners);
        $this->bindingCheck = null;
        $this->queryConsentTimer = null;
        // The timestamps of another process mean nothing, and the requests it was waiting for won't be answered here.
        $now = microtime(true);
        foreach ($this->checkList as $pair) {
            $pair->resetLiveness($now);
        }
        foreach ($this->nominated as $pair) {
            $pair->resetLiveness($now);
        }
        if ($restartConsent && $this->nominated !== []) {
            $this->periodicConsentCheck();
        }
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\ICE;

use Override;
use Webrtc\Exception\InvalidArgumentException;

/**
 * Class RTCIceProtocolConfiguration
 *
 * Represents the configuration for ICE servers (STUN and TURN) used in ICE negotiation.
 *
 * This class allows setting and retrieving information about the STUN server and TURN server,
 * including credentials and transport settings. It is used internally by ICE components
 * to determine how candidates should be gathered and communicated.
 */
final class RTCIceProtocolConfiguration implements RTCIceProtocolConfigurationInterface
{
    private ?array $stunServer = null;
    private ?array $turnServer = null;
    private bool $turnSsl = false;
    private string $turnTransport = 'udp';
    private ?string $turnUsername = null;
    private ?string $turnPassword = null;

    /**
     * Get the STUN server configuration.
     *
     * @return array|null The STUN server as an array (e.g., ['host' => ..., 'port' => ...]) or null.
     */
    #[\Override]

    public function getStunServer(): ?array
    {
        return $this->stunServer;
    }

    /**
     * Set the STUN server configuration.
     *
     * An empty array means "no STUN server" and is normalized to null.
     *
     * @param array|null $stunServer The STUN server array or null to unset.
     *
     * @return void
     */
    #[\Override]

    public function setStunServer(?array $stunServer): void
    {
        $this->stunServer = $stunServer === [] ? null : $stunServer;
    }

    /**
     * Get the TURN server configuration.
     *
     * @return array|null The TURN server as an array (e.g., ['host' => ..., 'port' => ...]) or null.
     */
    #[\Override]

    public function getTurnServer(): ?array
    {
        return $this->turnServer;
    }

    /**
     * Set the TURN server configuration.
     *
     * @param array|null $turnServer The TURN server array or null to unset.
     *
     * @return void
     */
    #[\Override]

    public function setTurnServer(?array $turnServer): void
    {
        $this->turnServer = $turnServer;
    }

    /**
     * Check if TURN over SSL (TURNS) is enabled.
     *
     * @return bool True if SSL is enabled, false otherwise.
     */
    #[\Override]

    public function getTurnSsl(): bool
    {
        return $this->turnSsl;
    }

    /**
     * Set whether TURN over SSL (TURNS) should be used.
     *
     * @param bool $turnSsl True to enable TURNS, false to disable.
     *
     * @return void
     */
    #[\Override]

    public function setTurnSsl(bool $turnSsl): void
    {
        $this->turnSsl = $turnSsl;
    }

    /**
     * Get the TURN server username for authentication.
     *
     * @return string|null The username or null if not set.
     */
    #[\Override]

    public function getTurnUsername(): ?string
    {
        return $this->turnUsername;
    }

    /**
     * Set the TURN server username for authentication.
     *
     * @param string|null $turnUsername The username or null to unset.
     *
     * @return void
     */
    #[\Override]

    public function setTurnUsername(?string $turnUsername): void
    {
        $this->turnUsername = $turnUsername;
    }

    /**
     * Get the TURN server password for authentication.
     *
     * @return string|null The password or null if not set.
     */
    #[\Override]

    public function getTurnPassword(): ?string
    {
        return $this->turnPassword;
    }

    /**
     * Set the TURN server password for authentication.
     *
     * @param string|null $turnPassword The password or null to unset.
     *
     * @return void
     */
    #[\Override]

    public function setTurnPassword(?string $turnPassword): void
    {
        $this->turnPassword = $turnPassword;
    }

    /**
     * Get the TURN server transport protocol.
     *
     * @return string Either 'udp' or 'tcp'.
     */
    #[\Override]

    public function getTurnTransport(): string
    {
        return $this->turnTransport;
    }

    /**
     * Set the TURN server transport protocol.
     *
     * @param string $turnTransport Must be either 'udp' or 'tcp'.
     *
     * @throws InvalidArgumentException If an invalid transport protocol is given.
     *
     * @return void
     */
    #[\Override]

    public function setTurnTransport(string $turnTransport): void
    {
        if (!in_array($turnTransport, ['udp', 'tcp'], true)) {
            throw new InvalidArgumentException('Invalid turn transport');
        }
        $this->turnTransport = $turnTransport;
    }
}
{
    "name": "danog/php-rtc-ice",
    "type": "library",
    "description": "WebRTC ICE connectivity in PHP, ported to Amp v3 fibers and sockets. MadelineProto's PHP 8.2+ fork integrates the modular danog/php-rtc STUN, TURN, and mDNS stack.",
    "keywords": [
        "webrtc",
        "ice",
        "turn",
        "stun"
    ],
    "homepage": "https://github.com/danog/php-rtc-ice",
    "license": "BSD-3-Clause",
    "authors": [
        {
            "name": "Amin Yazdanpanah",
            "email": "github@aminyazdanpanah.com"
        },
        {
            "name": "Sana Moniri",
            "email": "s.mo@quasarstream.com"
        },
        {
            "name": "Quasar Stream Community"
        },
        {
            "name": "Daniil Gentili",
            "email": "daniil@daniil.it"
        }
    ],
    "require": {
        "php-64bit": "^8.2",
        "amphp/amp": "^3.1.3",
        "amphp/socket": "^2.4.1",
        "amphp/dns": "^2.4.1",
        "danog/php-rtc-exception": "^1.1.5",
        "danog/php-rtc-turn": "^2.0.9",
        "danog/php-rtc-stun": "^2.0.17",
        "danog/php-rtc-mixin": "^2.0.9",
        "danog/php-rtc-mdns": "^1.1.7",
        "psr/log": "^3.0.2",
        "symfony/polyfill-php84": "*"
    },
    "require-dev": {
        "mockery/mockery": "^1.6.15",
        "phpunit/phpunit": "^11.5 || ^12.0 || ^13.3.2",
        "bamarni/composer-bin-plugin": "^1.9.1"
    },
    "autoload": {
        "psr-4": {
            "Webrtc\\ICE\\": "src"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\Webrtc\\ICE\\": "tests"
        }
    },
    "minimum-stability": "stable",
    "scripts": {
        "post-install-cmd": [
            "@composer bin all install --ansi"
        ],
        "post-update-cmd": [
            "@composer bin all update --ansi"
        ]
    },
    "config": {
        "allow-plugins": {
            "bamarni/composer-bin-plugin": true
        }
    },
    "extra": {
        "bamarni-bin": {
            "bin-links": true,
            "forward-command": true
        }
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SCTP;

final class SctpConstant
{
    // Local constants
    public const COOKIE_LENGTH = 24;
    public const COOKIE_LIFETIME = 60;
    public const MAX_STREAMS = 65535;
    public const USERDATA_MAX_LENGTH = 1200;

    // Protocol constants
    public const SCTP_CAUSE_INVALID_STREAM = 0x0001;
    public const SCTP_CAUSE_STALE_COOKIE = 0x0003;

    public const SCTP_DATA_LAST_FRAG = 0x01;
    public const SCTP_DATA_FIRST_FRAG = 0x02;
    public const SCTP_DATA_UNORDERED = 0x04;

    public const SCTP_MAX_ASSOCIATION_RETRANS = 10;
    public const SCTP_MAX_BURST = 4;
    public const SCTP_MAX_INIT_RETRANS = 8;
    public const SCTP_RTO_ALPHA = 1 / 8;
    public const SCTP_RTO_BETA = 1 / 4;
    public const SCTP_RTO_INITIAL = 3.0;
    public const SCTP_RTO_MIN = 1;
    public const SCTP_RTO_MAX = 60;
    public const SCTP_TSN_MODULO = 2 ** 32;

    public const RECONFIG_MAX_STREAMS = 135;

    // Parameters
    public const SCTP_STATE_COOKIE = 0x0007;
    public const SCTP_STR_RESET_OUT_REQUEST = 0x000D;
    public const SCTP_STR_RESET_RESPONSE = 0x0010;
    public const SCTP_STR_RESET_ADD_OUT_STREAMS = 0x0011;
    public const SCTP_SUPPORTED_CHUNK_EXT = 0x8008;
    public const SCTP_PRSCTP_SUPPORTED = 0xC000;

    // Data channel constants
    public const DATA_CHANNEL_ACK = 2;
    public const DATA_CHANNEL_OPEN = 3;

    public const DATA_CHANNEL_RELIABLE = 0x00;
    public const DATA_CHANNEL_PARTIAL_RELIABLE_REXMIT = 0x01;
    public const DATA_CHANNEL_PARTIAL_RELIABLE_TIMED = 0x02;
    public const DATA_CHANNEL_RELIABLE_UNORDERED = 0x80;
    public const DATA_CHANNEL_PARTIAL_RELIABLE_REXMIT_UNORDERED = 0x81;
    public const DATA_CHANNEL_PARTIAL_RELIABLE_TIMED_UNORDERED = 0x82;

    public const WEBRTC_DCEP = 50;
    public const WEBRTC_STRING = 51;
    public const WEBRTC_BINARY = 53;
    public const WEBRTC_STRING_EMPTY = 56;
    public const WEBRTC_BINARY_EMPTY = 57;
}
<?php declare(strict_types=1);

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SCTP;

use Webrtc\Exception\InvalidArgumentException;
use Webrtc\SCTP\Chunk\DataChunk;
use Generator;

/**
 * Class InboundStream.
 *
 * Handles the reassembly of incoming SCTP (Stream Control Transmission Protocol) DataChunks
 * into complete user messages for a single SCTP stream. This class maintains message order,
 * supports ordered and unordered delivery, and handles fragmentation and defragmentation
 * according to SCTP specifications.
 *
 * Key responsibilities:
 * - Accepts incoming DataChunks and inserts them into the reassembly buffer.
 * - Detects and prevents duplicate chunks.
 * - Reassembles complete messages from fragment sequences.
 * - Maintains and updates stream sequence numbers for ordered delivery.
 * - Allows pruning of acknowledged or obsolete chunks based on TSN (Transmission Sequence Number).
 *
 * Used in the context of SCTP-based WebRTC data channels to correctly parse and deliver
 * incoming fragmented messages on each stream.
 */
final class InboundStream
{
    /** @var DataChunk[] List of DataChunk objects for reassembly. */
    private array $reassembly = [];

    /** @var int Sequence number for ordered chunks. */
    private int $sequenceNumber = 0;

    /**
     * Adds a DataChunk to the reassembly list in the correct order.
     *
     * @param DataChunk $chunk The DataChunk to add.
     * @throws InvalidArgumentException If a duplicate chunk is detected.
     */
    public function addChunk(DataChunk $chunk): void
    {
        if ($this->isChunkNew($chunk)) {
            $this->reassembly[] = $chunk;
            return;
        }

        foreach ($this->reassembly as $i => $rchunk) {
            if ($rchunk->getTsn() === $chunk->getTsn()) {
                throw new InvalidArgumentException("Duplicate chunk in reassembly");
            }

            if (SctpUtility::uint32Gt($rchunk->getTsn(), $chunk->getTsn())) {
                array_splice($this->reassembly, (int) $i, 0, [$chunk]);
                return;
            }
        }
    }

    /**
     * Checks if the chunk is new and should be appended at the end.
     *
     */
    private function isChunkNew(DataChunk $chunk): bool
    {
        return empty($this->reassembly) || SctpUtility::uint32Gt($chunk->getTsn(), end($this->reassembly)->getTsn());
    }

    /**
     * Pops complete messages from the reassembly list.
     *
     * @return Generator<array{0: int, 1: int, 2: string}> Yields tuples of (streamId, protocol, userData).
     */
    public function popMessages(): Generator
    {
        $pos = 0;

        while ($pos < \count($this->reassembly)) {
            $message = $this->processMessageExtraction($pos);
            if ($message !== null) {
                yield $message;
            } else {
                break;
            }
        }
    }

    /**
     * Handles message extraction and validation.
     *
     * @param int &$pos Current position in reassembly an array.
     * @return array{0: int, 1: int, 2: string}|null Returns a complete message tuple or null if incomplete.
     */
    private function processMessageExtraction(int &$pos): ?array
    {
        $startPos = null;
        $expectedTsn = -1;

        while ($pos < \count($this->reassembly)) {
            $chunk = $this->reassembly[$pos];

            if ($startPos === null) {
                if (!$this->canStartMessage($chunk)) {
                    $pos++;
                    continue;
                }

                $extraction = $this->initializeMessageExtraction($pos, $chunk);
                if ($extraction === null) {
                    return null;
                }
                [$startPos, , $expectedTsn] = $extraction;
            } elseif ($chunk->getTsn() !== $expectedTsn) {
                return null;
            }

            if ($chunk->getFlags() & SctpConstant::SCTP_DATA_LAST_FRAG) {
                return $this->assembleMessage($startPos, $pos, $chunk);
            }

            $pos++;
            $expectedTsn = SctpUtility::tsnPlusOne($expectedTsn);
        }

        return null;
    }

    /**
     * Initializes variables for message extraction.
     *
     * @param int $pos Current position in reassembly an array.
     * @param DataChunk $chunk First chunk of the message.
     * @return array{0: int, 1: bool, 2: int}|null Returns an array with start position, ordered flag, and expected TSN or null if invalid.
     */
    private function initializeMessageExtraction(int $pos, DataChunk $chunk): ?array
    {
        $ordered = !($chunk->getFlags() & SctpConstant::SCTP_DATA_UNORDERED);
        if ($ordered && SctpUtility::uint16Gt($chunk->getStreamSeq(), $this->sequenceNumber)) {
            return null;
        }

        return [$pos, $ordered, $chunk->getTsn()];
    }

    /**
     * Checks if a chunk can start a new message.
     *
     */
    private function canStartMessage(DataChunk $chunk): bool
    {
        return ($chunk->getFlags() & SctpConstant::SCTP_DATA_FIRST_FRAG) !== 0;
    }

    /**
     * Assembles a message from collected chunks and updates the sequence number.
     *
     * @param int $startPos Start position in reassembly an array.
     * @param int &$pos Current position in reassembly an array.
     * @param DataChunk $chunk The last fragment chunk.
     * @return array{0: int, 1: int, 2: string} The assembled message tuple.
     */
    private function assembleMessage(int $startPos, int &$pos, DataChunk $chunk): array
    {
        $userData = implode('', array_map(static fn (DataChunk $c) => $c->getUserData(), \array_slice($this->reassembly, $startPos, $pos - $startPos + 1)));

        $this->reassembly = array_merge(
            \array_slice($this->reassembly, 0, $startPos),
            \array_slice($this->reassembly, $pos + 1)
        );

        if (!($chunk->getFlags() & SctpConstant::SCTP_DATA_UNORDERED) && $chunk->getStreamSeq() === $this->sequenceNumber) {
            $this->sequenceNumber = SctpUtility::uint16Add($this->sequenceNumber, 1);
        }

        $pos = $startPos;
        return [$chunk->getStreamId(), $chunk->getProtocol(), $userData];
    }

    /**
     * Prunes chunks up to the given TSN.
     *
     * @param int $tsn The TSN up to which chunks should be pruned.
     * @return int The total size of the pruned chunks.
     */
    public function pruneChunks(int $tsn): int
    {
        $pos = -1;
        $size = 0;

        foreach ($this->reassembly as $i => $chunk) {
            if (SctpUtility::uint32Gte($tsn, $chunk->getTsn())) {
                $pos = $i;
                $size += \strlen($chunk->getUserData());
            } else {
                break;
            }
        }

        $this->reassembly = \array_slice($this->reassembly, (int) $pos + 1);
        return $size;
    }

    public function getSequenceNumber(): int
    {
        return $this->sequenceNumber;
    }

    public function setSequenceNumber(int $sequenceNumber): void
    {
        $this->sequenceNumber = $sequenceNumber;
    }

    /**
     * @return DataChunk[]
     */
    public function getReassembly(): array
    {
        return $this->reassembly;
    }

}
<?php

namespace Webrtc\SCTP\Listener;

use Webrtc\DataChannel\RTCDataChannel;

/**
 * Notified when an {@see \Webrtc\SCTP\RTCSctpTransport} opens a remotely-initiated data channel,
 * replacing the former Evenement "datachannel" event.
 */
interface DataChannelListener
{
    public function onDataChannel(RTCDataChannel $channel): void;
}
<?php declare(strict_types=1);

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SCTP\Chunk;

use Override;
use Webrtc\Exception\InvalidArgumentException;
use Webrtc\SCTP\SctpUtility;

/**
 * Represents the base structure of an SCTP INIT or INIT-ACK chunk.
 *
 * This class provides common functionality and fields used by both INIT and INIT-ACK
 * chunks, including fields such as initiate tag, receiver window size, stream numbers,
 * initial TSN, and optional parameters.
 */
class BaseInitChunk extends Chunk
{
    /** @var int Initiate tag used for association identification. */
    protected int $initiateTag;

    /** @var int Advertised receiver window size. */
    protected int $advertisedRwnd;

    /** @var int Maximum number of outbound streams supported. */
    protected int $outboundStreams;

    /** @var int Maximum number of inbound streams supported. */
    protected int $inboundStreams;

    /** @var int Initial Transmission Sequence Number. */
    protected int $initialTsn;

    /** @var array<array-key, array{0: int, 1: string}> List of encoded optional parameters. */
    protected array $params = [];

    /**
     * Constructs a new BaseInitChunk.
     *
     * If a body is provided, it will be parsed into the appropriate fields.
     *
     * @param int $flags Flags associated with the chunk.
     * @param string|null $body Binary body content of the chunk.
     */
    public function __construct(int $flags = 0, ?string $body = null)
    {
        parent::__construct($flags);
        if ($body !== null && $body !== "") {
            $unpacked = unpack("NinitiateTag/NadvertisedRwnd/noutboundStreams/ninboundStreams/NinitialTsn", substr($body, 0, 16));
            if ($unpacked === false) {
                throw new InvalidArgumentException("Failed to unpack INIT chunk body");
            }
            $this->initiateTag = (int) $unpacked["initiateTag"];
            $this->advertisedRwnd = (int) $unpacked["advertisedRwnd"];
            $this->outboundStreams = (int) $unpacked["outboundStreams"];
            $this->inboundStreams = (int) $unpacked["inboundStreams"];
            $this->initialTsn = (int) $unpacked["initialTsn"];
            $this->params = SctpUtility::decodeParams(substr($body, 16));
        } else {
            $this->initiateTag = 0;
            $this->advertisedRwnd = 0;
            $this->outboundStreams = 0;
            $this->inboundStreams = 0;
            $this->initialTsn = 0;
            $this->params = [];
        }
    }

    /**
     * Gets the initiate tag.
     *
     * @return int Initiate tag value.
     */
    public function getInitiateTag(): int
    {
        return $this->initiateTag;
    }

    /**
     * Sets the initiate tag.
     *
     * @param int $initiateTag Initiate tag value.
     */
    public function setInitiateTag(int $initiateTag): void
    {
        $this->initiateTag = $initiateTag;
    }

    /**
     * Gets the advertised receiver window.
     *
     * @return int Receiver window size.
     */
    public function getAdvertisedRwnd(): int
    {
        return $this->advertisedRwnd;
    }

    /**
     * Sets the advertised receiver window.
     *
     * @param int $advertisedRwnd Receiver window size.
     */
    public function setAdvertisedRwnd(int $advertisedRwnd): void
    {
        $this->advertisedRwnd = $advertisedRwnd;
    }

    /**
     * Gets the number of outbound streams.
     *
     * @return int Outbound stream count.
     */
    public function getOutboundStreams(): int
    {
        return $this->outboundStreams;
    }

    /**
     * Sets the number of outbound streams.
     *
     * @param int $outboundStreams Outbound stream count.
     */
    public function setOutboundStreams(int $outboundStreams): void
    {
        $this->outboundStreams = $outboundStreams;
    }

    /**
     * Gets the number of inbound streams.
     *
     * @return int Inbound stream count.
     */
    public function getInboundStreams(): int
    {
        return $this->inboundStreams;
    }

    /**
     * Sets the number of inbound streams.
     *
     * @param int $inboundStreams Inbound stream count.
     */
    public function setInboundStreams(int $inboundStreams): void
    {
        $this->inboundStreams = $inboundStreams;
    }

    /**
     * Gets the initial transmission sequence number.
     *
     * @return int Initial TSN.
     */
    public function getInitialTsn(): int
    {
        return $this->initialTsn;
    }

    /**
     * Sets the initial transmission sequence number.
     *
     * @param int $initialTsn Initial TSN value.
     */
    public function setInitialTsn(int $initialTsn): void
    {
        $this->initialTsn = $initialTsn;
    }

    /**
     * Gets the list of optional parameters.
     *
     * @return array<array-key, array{0: int, 1: string}> Parameters array.
     */
    public function getParams(): array
    {
        return $this->params;
    }

    /**
     * Sets the list of optional parameters.
     *
     * @param array<array-key, array{0: int, 1: string}> $params Parameters to set.
     */
    public function setParams(array $params): void
    {
        $this->params = $params;
    }

    /**
     * Adds one or more parameters to the existing parameter list.
     *
     * @param array<array-key, array{0: int, 1: string}> $param A parameter or array of parameters to add.
     */
    public function addParams(array $param): void
    {
        $this->params = array_merge($this->params, $param);
    }

    /**
     * Gets the binary representation of the chunk body.
     *
     * @return string Encoded chunk body.
     */
    #[Override]
    public function getBody(): string
    {
        $body = pack("NNnnN", $this->initiateTag, $this->advertisedRwnd, $this->outboundStreams, $this->inboundStreams, $this->initialTsn);
        $body .= SctpUtility::encodeParams($this->params);
        return $body;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SCTP\Chunk;

/**
 * Reconfiguration chunk.
 */
final class ReconfigChunk extends BaseParamsChunk
{
    /** @var int Chunk type identifier. */
    protected int $type = 130;
}
<?php declare(strict_types=1);

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SCTP\Chunk;

/**
 * Cookie Acknowledgment chunk.
 */
final class CookieAckChunk extends Chunk
{
    /** @var int Chunk type identifier. */
    protected int $type = 11;
}
<?php declare(strict_types=1);

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SCTP\Chunk;

/**
 * Heartbeat chunk.
 */
final class HeartbeatChunk extends BaseParamsChunk
{
    /** @var int Chunk type identifier. */
    protected int $type = 4;
}
<?php declare(strict_types=1);

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SCTP\Chunk;

use Override;
use Webrtc\SCTP\SctpUtility;

/**
 * Base class for chunks that contain parameters.
 */
class BaseParamsChunk extends Chunk
{
    /** @var array<array-key, array{0: int, 1: string}> List of parameters. */
    protected array $params;

    /**
     * BaseParamsChunk constructor.
     *
     * @param int $flags Chunk flags.
     * @param string|null $body Chunk body.
     */
    public function __construct(int $flags = 0, ?string $body = null)
    {
        parent::__construct($flags);
        $this->params = ($body !== null && $body !== "") ? SctpUtility::decodeParams($body) : [];
    }

    /** @return array<array-key, array{0: int, 1: string}> */
    public function getParams(): array
    {
        return $this->params;
    }

    /** @param array<array-key, array{0: int, 1: string}> $params */
    public function setParams(array $params): void
    {
        $this->params = $params;
    }

    /**
     * Add params and merge with existing ones.
     *
     * @param array<array-key, array{0: int, 1: string}> $param
     */
    public function addParams(array $param): void
    {
        $this->params = array_merge($this->params, $param);
    }

    /**
     * Gets the encoded body of the chunk.
     *
     * @return string Encoded body.
     */
    #[Override]
    public function getBody(): string
    {
        return SctpUtility::encodeParams($this->params);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SCTP\Chunk;

/**
 * Error chunk.
 */
final class ErrorChunk extends BaseParamsChunk
{
    /** @var int Chunk type identifier. */
    protected int $type = 9;
}
<?php declare(strict_types=1);

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SCTP\Chunk;

use Override;
use Webrtc\Exception\InvalidArgumentException;

/**
 * Forward TSN chunk.
 */
final class ForwardTsnChunk extends Chunk
{
    /** @var int Chunk type identifier. */
    protected int $type = 192;

    /** @var int Cumulative TSN. */
    protected int $cumulativeTsn;

    /** @var array<array-key, array{0: int, 1: int}> List of streams. */
    protected array $streams = [];

    /**
     * ForwardTsnChunk constructor.
     *
     * @param int $flags Chunk flags.
     * @param string|null $body Chunk body.
     */
    public function __construct(int $flags = 0, ?string $body = null)
    {
        parent::__construct($flags);
        if ($body !== null && $body !== "") {
            $cumulative = unpack("N", substr($body, 0, 4));
            if ($cumulative === false) {
                throw new InvalidArgumentException("Failed to unpack FORWARD TSN cumulative TSN");
            }
            $this->cumulativeTsn = (int) $cumulative[1];
            $pos = 4;
            while ($pos < \strlen($body)) {
                $stream = unpack("nstreamId/nstreamSeq", substr($body, $pos, 4));
                if ($stream === false) {
                    throw new InvalidArgumentException("Failed to unpack FORWARD TSN stream");
                }
                $this->streams[] = [0 => (int) $stream["streamId"], 1 => (int) $stream["streamSeq"]];
                $pos += 4;
            }
        } else {
            $this->cumulativeTsn = 0;
        }
    }

    /**
     * Gets the encoded body of the chunk.
     *
     * @return string Encoded body.
     */
    #[Override]
    public function getBody(): string
    {
        $body = pack("N", $this->cumulativeTsn);
        foreach ($this->streams as [$streamId, $streamSeq]) {
            $body .= pack("nn", $streamId, $streamSeq);
        }
        return $body;
    }

    /**
     * Returns a string representation of the chunk.
     *
     * @return string String representation.
     */
    public function __toString(): string
    {
        return sprintf(
            "ForwardTsnChunk(cumulativeTsn=%d, streams=%s)",
            $this->cumulativeTsn,
            (string) json_encode($this->streams)
        );
    }

    public function getCumulativeTsn(): int
    {
        return $this->cumulativeTsn;
    }

    public function setCumulativeTsn(int $cumulativeTsn): void
    {
        $this->cumulativeTsn = $cumulativeTsn;
    }

    /** @return array<array-key, array{0: int, 1: int}> */
    public function getStreams(): array
    {
        return $this->streams;
    }

    /** @param array<array-key, array{0: int, 1: int}> $streams */
    public function setStreams(array $streams): void
    {
        $this->streams = $streams;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SCTP\Chunk;

interface ChunkInterface
{
    public function encode(): string;

    public function __toString(): string;

    public function getAttributes(): AttributeChunk;

    public function getType(): int;

    public function getBody(): string;

    public function setBody(string $body): void;

    public function getFlags(): int;

    public function setFlags(int $flags): void;
}
<?php declare(strict_types=1);

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SCTP\Chunk;

/**
 * Abort chunk.
 */
final class AbortChunk extends BaseParamsChunk
{
    /** @var int Chunk type identifier. */
    protected int $type = 6;
}
<?php declare(strict_types=1);

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SCTP\Chunk;

use Override;
use Webrtc\Exception\InvalidArgumentException;
use Webrtc\SCTP\SctpUtility;

/**
 * Data chunk.
 */
final class DataChunk extends Chunk
{
    /** @var int Chunk type identifier. */
    protected int $type = 0;

    /** @var int Transmission Sequence Number. */
    protected int $tsn;

    /** @var int Stream identifier. */
    protected int $streamId;

    /** @var int Stream sequence number. */
    protected int $streamSeq;

    /** @var int Protocol identifier. */
    protected int $protocol;

    /** @var string User data. */
    protected string $userData;

    /**
     * DataChunk constructor.
     *
     * @param int $flags Chunk flags.
     * @param string|null $body Chunk body.
     */
    public function __construct(int $flags = 0, ?string $body = null)
    {
        parent::__construct($flags);
        if ($body !== null && $body !== "") {
            $unpacked = unpack("Ntsn/nstreamId/nstreamSeq/Nprotocol", $body);
            if ($unpacked === false) {
                throw new InvalidArgumentException("Failed to unpack DATA chunk body");
            }
            $this->tsn = (int) $unpacked["tsn"];
            $this->streamId = (int) $unpacked["streamId"];
            $this->streamSeq = (int) $unpacked["streamSeq"];
            $this->protocol = (int) $unpacked["protocol"];
            $this->userData = substr($body, 12);
        } else {
            $this->tsn = 0;
            $this->streamId = 0;
            $this->streamSeq = 0;
            $this->protocol = 0;
            $this->userData = "";
        }
    }

    /**
     * Encodes the chunk into a binary string.
     *
     * @return string Binary representation of the chunk.
     */
    #[Override]
    public function encode(): string
    {
        $length = 16 + \strlen($this->userData);
        $data = pack("CCnNnnN", $this->type, $this->flags, $length, $this->tsn, $this->streamId, $this->streamSeq, $this->protocol) . $this->userData;
        if ($length % 4) {
            $data .= str_repeat("\x00", SctpUtility::padl($length));
        }
        return $data;
    }

    /**
     * Returns a string representation of the chunk.
     *
     * @return string String representation.
     */
    public function __toString(): string
    {
        return sprintf(
            "DataChunk(flags=%d, tsn=%d, streamId=%d, streamSeq=%d)",
            $this->flags,
            $this->tsn,
            $this->streamId,
            $this->streamSeq
        );
    }

    public function getTsn(): int
    {
        return $this->tsn;
    }

    public function getStreamId(): int
    {
        return $this->streamId;
    }

    public function setStreamId(int $streamId): void
    {
        $this->streamId = $streamId;
    }

    public function getUserData(): string
    {
        return $this->userData;
    }

    public function setUserData(string $userData): void
    {
        $this->userData = $userData;
    }

    public function setTsn(int $tsn): void
    {
        $this->tsn = $tsn;
    }

    public function getStreamSeq(): int
    {
        return $this->streamSeq;
    }

    public function setStreamSeq(int $streamSeq): void
    {
        $this->streamSeq = $streamSeq;
    }

    public function getProtocol(): int
    {
        return $this->protocol;
    }

    public function setProtocol(int $protocol): void
    {
        $this->protocol = $protocol;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SCTP\Chunk;

/**
 * Heartbeat Acknowledgment chunk.
 */
final class HeartbeatAckChunk extends BaseParamsChunk
{
    /** @var int Chunk type identifier. */
    protected int $type = 5;
}
<?php declare(strict_types=1);

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SCTP\Chunk;

use Webrtc\Mixin\DataClass;

#[DataClass]
final class AttributeChunk
{
    /**
     * Attrs of chunk.
     *
     */
    public function __construct(
        public int $bookSize = 0,
        public float $expiry = 0,
        public ?int $maxRetransmits = null,
        public bool $abandoned = false,
        public bool $acked = false,
        public int $misses = 0,
        public bool $retransmit = false,
        public int $sentCount = 0,
        public ?float $sentTime = null,
    ) {
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SCTP\Chunk;

/**
 * Shutdown Complete chunk.
 */
final class ShutdownCompleteChunk extends Chunk
{
    /** @var int Chunk type identifier. */
    protected int $type = 14;
}
<?php declare(strict_types=1);

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SCTP\Chunk;

/**
 * Init Acknowledgment chunk.
 */
final class InitAckChunk extends BaseInitChunk
{
    /** @var int Chunk type identifier. */
    protected int $type = 2;
}
<?php declare(strict_types=1);

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SCTP\Chunk;

use Override;
use Webrtc\Exception\InvalidArgumentException;

/**
 * Shutdown chunk.
 */
final class ShutdownChunk extends Chunk
{
    /** @var int Chunk type identifier. */
    protected int $type = 7;

    /** @var int Cumulative TSN. */
    protected int $cumulativeTsn;

    /**
     * ShutdownChunk constructor.
     *
     * @param int $flags Chunk flags.
     * @param string|null $body Chunk body.
     */
    public function __construct(int $flags = 0, ?string $body = null)
    {
        parent::__construct($flags);
        if ($body !== null && $body !== "") {
            $unpacked = unpack("N", $body);
            if ($unpacked === false) {
                throw new InvalidArgumentException("Failed to unpack SHUTDOWN chunk body");
            }
            $this->cumulativeTsn = (int) $unpacked[1];
        } else {
            $this->cumulativeTsn = 0;
        }
    }

    /**
     * Gets the encoded body of the chunk.
     *
     * @return string Encoded body.
     */
    #[Override]
    public function getBody(): string
    {
        return pack("N", $this->cumulativeTsn);
    }

    /**
     * Returns a string representation of the chunk.
     *
     * @return string String representation.
     */
    public function __toString(): string
    {
        return sprintf(
            "ShutdownChunk(flags=%d, cumulativeTsn=%d)",
            $this->flags,
            $this->cumulativeTsn
        );
    }

    public function getCumulativeTsn(): int
    {
        return $this->cumulativeTsn;
    }

    public function setCumulativeTsn(int $cumulativeTsn): void
    {
        $this->cumulativeTsn = $cumulativeTsn;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SCTP\Chunk;

/**
 * Init chunk.
 */
final class InitChunk extends BaseInitChunk
{
    /** @var int Chunk type identifier. */
    protected int $type = 1;

}
<?php declare(strict_types=1);

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SCTP\Chunk;

use Override;
use Webrtc\SCTP\SctpUtility;

/**
 * Base class for SCTP chunks.
 */
class Chunk implements ChunkInterface
{
    /** @var int Chunk type identifier. */
    protected int $type = -1;

    /** @var int Chunk flags. */
    protected int $flags = 0;

    /** @var string Chunk body. */
    protected string $body;
    private AttributeChunk $attributes;

    /**
     * Chunk constructor.
     *
     * @param int $flags Chunk flags.
     * @param string $body Chunk body.
     */
    public function __construct(int $flags = 0, string $body = "")
    {
        $this->flags = $flags;
        $this->body = $body;
        $this->attributes = new AttributeChunk();
    }

    /**
     * Encodes the chunk into a binary string.
     *
     * @return string Binary representation of the chunk.
     */
    #[Override]
    public function encode(): string
    {
        $body = $this->getBody();

        $length = \strlen($body) + 4;
        $data = pack("CCn", $this->type, $this->flags, $length) . $body;
        $data .= str_repeat("\x00", SctpUtility::padl($length));
        return $data;
    }

    /**
     * Returns a string representation of the chunk.
     *
     * @return string String representation.
     */
    public function __toString(): string
    {
        return sprintf("%s(flags=%d)", SctpUtility::chunkType($this), $this->flags);
    }

    #[Override]
    public function getAttributes(): AttributeChunk
    {
        return $this->attributes;
    }

    #[Override]
    public function getType(): int
    {
        return $this->type;
    }

    #[Override]
    public function getBody(): string
    {
        return $this->body;
    }

    #[Override]
    public function setBody(string $body): void
    {
        $this->body = $body;
    }

    #[Override]
    public function getFlags(): int
    {
        return $this->flags;
    }

    #[Override]
    public function setFlags(int $flags): void
    {
        $this->flags = $flags;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SCTP\Chunk;

/**
 * Shutdown Acknowledgment chunk.
 */
final class ShutdownAckChunk extends Chunk
{
    /** @var int Chunk type identifier. */
    protected int $type = 8;
}
<?php declare(strict_types=1);

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SCTP\Chunk;

use Override;
use Webrtc\Exception\InvalidArgumentException;

/**
 * Selective Acknowledgment chunk.
 */
final class SackChunk extends Chunk
{
    /** @var int Chunk type identifier. */
    protected int $type = 3;

    /** @var int Cumulative TSN. */
    protected int $cumulativeTsn;

    /** @var int Advertised receiver window. */
    protected int $advertisedRwnd;

    /** @var array<array-key, array{0: int, 1: int}> List of gaps. */
    protected array $gaps = [];

    /** @var array<int> List of duplicate TSNs. */
    protected array $duplicates = [];

    /**
     * SackChunk constructor.
     *
     * @param int $flags Chunk flags.
     * @param string|null $body Chunk body.
     */
    public function __construct(int $flags = 0, ?string $body = null)
    {
        parent::__construct($flags);
        if ($body !== null && $body !== "") {
            $unpacked = unpack("NcumulativeTsn/NadvertisedRwnd/ngaps/nduplicates", substr($body, 0, 12));
            if ($unpacked === false) {
                throw new InvalidArgumentException("Failed to unpack SACK chunk body");
            }
            $this->cumulativeTsn = (int) $unpacked["cumulativeTsn"];
            $this->advertisedRwnd = (int) $unpacked["advertisedRwnd"];
            $pos = 12;
            for ($i = 0; $i < (int) $unpacked["gaps"]; $i++) {
                $gap = unpack("nstart/nend", substr($body, $pos, 4));
                if ($gap === false) {
                    throw new InvalidArgumentException("Failed to unpack SACK gap");
                }
                $this->gaps[] = [0 => (int) $gap["start"], 1 => (int) $gap["end"]];
                $pos += 4;
            }
            for ($i = 0; $i < (int) $unpacked["duplicates"]; $i++) {
                $dup = unpack("N", substr($body, $pos, 4));
                if ($dup === false) {
                    throw new InvalidArgumentException("Failed to unpack SACK duplicate");
                }
                $this->duplicates[] = (int) $dup[1];
                $pos += 4;
            }
        } else {
            $this->cumulativeTsn = 0;
            $this->advertisedRwnd = 0;
        }
    }

    /**
     * Encodes the chunk into a binary string.
     *
     * @return string Binary representation of the chunk.
     */
    #[Override]
    public function encode(): string
    {
        $length = 16 + 4 * (\count($this->gaps) + \count($this->duplicates));
        $data = pack("CCnNNnn", $this->type, $this->flags, $length, $this->cumulativeTsn, $this->advertisedRwnd, \count($this->gaps), \count($this->duplicates));
        foreach ($this->gaps as [$start, $end]) {
            $data .= pack("nn", $start, $end);
        }
        foreach ($this->duplicates as $tsn) {
            $data .= pack("N", $tsn);
        }
        return $data;
    }

    /**
     * Returns a string representation of the chunk.
     *
     * @return string String representation.
     */
    public function __toString(): string
    {
        return sprintf(
            "SackChunk(flags=%d, advertisedRwnd=%d, cumulativeTsn=%d, gaps=%s)",
            $this->flags,
            $this->advertisedRwnd,
            $this->cumulativeTsn,
            (string) json_encode($this->gaps)
        );
    }

    public function getCumulativeTsn(): int
    {
        return $this->cumulativeTsn;
    }

    public function setCumulativeTsn(int $cumulativeTsn): void
    {
        $this->cumulativeTsn = $cumulativeTsn;
    }

    /** @return array<array-key, array{0: int, 1: int}> */
    public function getGaps(): array
    {
        return $this->gaps;
    }

    /** @param array<array-key, array{0: int, 1: int}> $gaps */
    public function setGaps(array $gaps): void
    {
        $this->gaps = $gaps;
    }

    public function getAdvertisedRwnd(): int
    {
        return $this->advertisedRwnd;
    }

    public function setAdvertisedRwnd(int $advertisedRwnd): void
    {
        $this->advertisedRwnd = $advertisedRwnd;
    }

    /** @return array<int> */
    public function getDuplicates(): array
    {
        return $this->duplicates;
    }

    /** @param array<int> $duplicates */
    public function setDuplicates(array $duplicates): void
    {
        $this->duplicates = $duplicates;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SCTP\Chunk;

/**
 * Cookie Echo chunk.
 */
final class CookieEchoChunk extends Chunk
{
    /** @var int Chunk type identifier. */
    protected int $type = 10;
}
<?php declare(strict_types=1);

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SCTP;

use Webrtc\Exception\InvalidArgumentException;
use Webrtc\SCTP\Chunk\AbortChunk;
use Webrtc\SCTP\Chunk\Chunk;
use Webrtc\SCTP\Chunk\ChunkInterface;
use Webrtc\SCTP\Chunk\CookieAckChunk;
use Webrtc\SCTP\Chunk\CookieEchoChunk;
use Webrtc\SCTP\Chunk\DataChunk;
use Webrtc\SCTP\Chunk\ErrorChunk;
use Webrtc\SCTP\Chunk\ForwardTsnChunk;
use Webrtc\SCTP\Chunk\HeartbeatAckChunk;
use Webrtc\SCTP\Chunk\HeartbeatChunk;
use Webrtc\SCTP\Chunk\InitAckChunk;
use Webrtc\SCTP\Chunk\InitChunk;
use Webrtc\SCTP\Chunk\ReconfigChunk;
use Webrtc\SCTP\Chunk\SackChunk;
use Webrtc\SCTP\Chunk\ShutdownAckChunk;
use Webrtc\SCTP\Chunk\ShutdownChunk;
use Webrtc\SCTP\Chunk\ShutdownCompleteChunk;

/**
 * SCTP Packet handling class.
 *
 * This class provides functionality to encode and decode Stream Control Transmission Protocol (SCTP) packets.
 * SCTP is a transport layer protocol that provides reliable, message-oriented communication with congestion control.
 *
 * The class handles:
 * - Parsing raw binary SCTP packets into their components (header fields and chunks)
 * - Constructing SCTP packets from individual components
 * - Validating packet integrity through checksum verification
 * - Supporting all standard SCTP chunk types as defined in RFC 4960
 */
final class SctpPacket
{
    /**
     * Mapping of SCTP chunk type codes to their corresponding class names.
     *
     * @var array<int, string> CHUNK_TYPES
     */
    public const CHUNK_TYPES = [
        0 => "DataChunk",
        1 => "InitChunk",
        2 => "InitAckChunk",
        3 => "SackChunk",
        4 => "HeartbeatChunk",
        5 => "HeartbeatAckChunk",
        6 => "AbortChunk",
        7 => "ShutdownChunk",
        8 => "ShutdownAckChunk",
        9 => "ErrorChunk",
        10 => "CookieEchoChunk",
        11 => "CookieAckChunk",
        14 => "ShutdownCompleteChunk",
        130 => "ReconfigChunk",
        192 => "ForwardTsnChunk",
    ];

    /**
     * Decodes a binary SCTP packet into its constituent parts.
     *
     * Parses the packet header and all chunks, validates the checksum, and returns
     * the packet components as an array. Throw an exception if the packet is invalid.
     *
     * @param string $data The binary string representing the SCTP packet.
     * @return array{
     *     0: int, // Source port
     *     1: int, // Destination port
     *     2: int, // Verification tag
     *     3: ChunkInterface[] // Array of chunk objects
     * }
     * @throws InvalidArgumentException If:
     *         - Packet length is less than 12 bytes (minimum SCTP header size)
     *         - Checksum validation fails
     *         - Chunk parsing fails
     */
    public static function decode(string $data): array
    {
        $length = \strlen($data);
        if ($length < 12) {
            throw new InvalidArgumentException("SCTP packet length is less than 12 bytes");
        }

        $unpacked = unpack("nsourcePort/ndestinationPort/NverificationTag", $data);
        if ($unpacked === false) {
            throw new InvalidArgumentException("SCTP packet header could not be unpacked");
        }
        $sourcePort = (int) $unpacked['sourcePort'];
        $destinationPort = (int) $unpacked['destinationPort'];
        $verificationTag = (int) $unpacked['verificationTag'];

        // Verify checksum
        $checksumData = unpack("V", substr($data, 8, 4));
        if ($checksumData === false) {
            throw new InvalidArgumentException("SCTP checksum could not be unpacked");
        }
        $checksum = (int) $checksumData[1];
        if ($checksum !== SctpUtility::crc32c(substr($data, 0, 8) . "\x00\x00\x00\x00" . substr($data, 12))) {
            throw new InvalidArgumentException("SCTP packet has invalid checksum");
        }

        $chunks = [];
        $pos = 12;
        while ($pos <= $length - 4) {
            $chunkHeader = unpack("Ctype/Cflags/nlength", substr($data, $pos, 4));
            if ($chunkHeader === false) {
                throw new InvalidArgumentException("SCTP chunk header could not be unpacked");
            }
            $chunkType = (int) $chunkHeader['type'];
            $chunkFlags = (int) $chunkHeader['flags'];
            $chunkLength = (int) $chunkHeader['length'];

            $chunkBody = substr($data, $pos + 4, $chunkLength - 4);
            if (isset(self::CHUNK_TYPES[$chunkType])) {
                $chunk = self::chunkFromType($chunkType, $chunkFlags, $chunkBody);
                if ($chunk !== null) {
                    $chunks[] = $chunk;
                }
            }

            $pos += $chunkLength + SctpUtility::padl($chunkLength);
        }

        return [$sourcePort, $destinationPort, $verificationTag, $chunks];
    }

    /**
     * Instantiates an SCTP chunk object for the given chunk type.
     *
     * @param int $type The SCTP chunk type code.
     * @param int $flags The chunk flags.
     * @param string $body The chunk body.
     * @return ChunkInterface|null The chunk object, or null if the type is not supported.
     */
    private static function chunkFromType(int $type, int $flags, string $body): ?ChunkInterface
    {
        return match ($type) {
            0 => new DataChunk($flags, $body),
            1 => new InitChunk($flags, $body),
            2 => new InitAckChunk($flags, $body),
            3 => new SackChunk($flags, $body),
            4 => new HeartbeatChunk($flags, $body),
            5 => new HeartbeatAckChunk($flags, $body),
            6 => new AbortChunk($flags, $body),
            7 => new ShutdownChunk($flags, $body),
            8 => new ShutdownAckChunk($flags, $body),
            9 => new ErrorChunk($flags, $body),
            10 => new CookieEchoChunk($flags, $body),
            11 => new CookieAckChunk($flags, $body),
            14 => new ShutdownCompleteChunk($flags, $body),
            130 => new ReconfigChunk($flags, $body),
            192 => new ForwardTsnChunk($flags, $body),
            default => null,
        };
    }

    /**
     * Encodes SCTP packet components into a binary packet.
     *
     * Constructs a valid SCTP packet with header fields and a single chunk,
     * calculates and inserts the proper checksum.
     *
     * @param int $sourcePort The source port number (16-bit unsigned integer)
     * @param int $destinationPort The destination port number (16-bit unsigned integer)
     * @param int $verificationTag The verification tag (32-bit unsigned integer)
     * @param Chunk $chunk The chunk object to include in the packet
     * @return string The binary string representation of the complete SCTP packet
     */
    public static function encode(int $sourcePort, int $destinationPort, int $verificationTag, Chunk $chunk): string
    {
        $header = pack("nnN", $sourcePort, $destinationPort, $verificationTag);
        $data = $chunk->encode();
        $checksum = SctpUtility::crc32c($header . "\x00\x00\x00\x00" . $data);
        return $header . pack("V", $checksum) . $data;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SCTP\Exception;

use Webrtc\Exception\Exception;

final class SctpException extends Exception implements SctpExceptionInterface
{

}
<?php declare(strict_types=1);

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SCTP\Exception;

use Webrtc\Exception\WebrtcExceptionInterface;

interface SctpExceptionInterface extends WebrtcExceptionInterface
{

}
<?php declare(strict_types=1);

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SCTP;

use Random\RandomException;
use Webrtc\Exception\InvalidArgumentException;

/**
 * SCTP Utility class.
 *
 * Provides various helper methods for SCTP protocol operations including
 * - Parameter encoding/decoding
 * - Checksum calculation
 * - Sequence number arithmetic
 * - Random number generation
 * - Binary data handling
 *
 * All methods are static as this is a utility class with no instance state.
 */
final class SctpUtility
{
    /**
     * Gets the type/class name of a chunk object.
     *
     * @param object $chunk The chunk object to inspect
     * @return string The fully qualified class name of the chunk
     */
    public static function chunkType(object $chunk): string
    {
        return $chunk::class;
    }

    /**
     * Decodes SCTP parameters from a binary string.
     *
     * Parses the binary parameter format defined in RFC 4960 Section 3.2.1:
     * - 16-bit parameter type
     * - 16-bit parameter length (including header)
     * - Parameter value
     * - Optional padding to 4-byte boundary
     *
     * @param string $body The binary string containing the parameters
     * @return array<array{0: int, 1: string}> Array of parameter tuples, each containing:
     *         - [0] Parameter types (16-bit unsigned integer)
     *         - [1] Parameter value (binary string)
     */
    public static function decodeParams(string $body): array
    {
        $params = [];
        $pos = 0;
        $length = \strlen($body);

        while ($pos <= $length - 4) {
            $typeData = unpack("n", substr($body, $pos, 2));
            if ($typeData === false) {
                throw new InvalidArgumentException("Failed to unpack SCTP parameter type");
            }
            $lengthData = unpack("n", substr($body, $pos + 2, 2));
            if ($lengthData === false) {
                throw new InvalidArgumentException("Failed to unpack SCTP parameter length");
            }
            $paramType = (int) $typeData[1];
            $paramLength = (int) $lengthData[1];
            $paramValue = substr($body, $pos + 4, $paramLength - 4);
            $params[] = [$paramType, $paramValue];
            $pos += $paramLength + self::padl($paramLength);
        }

        return $params;
    }

    /**
     * Encodes SCTP parameters into a binary string.
     *
     * Creates the binary parameter format defined in RFC 4960 Section 3.2.1:
     * - 16-bit parameter type
     * - 16-bit parameter length (including header)
     * - Parameter value
     * - Optional padding to 4-byte boundary
     *
     * @param array<array{0: int, 1: string}> $params Array of parameter tuples, each containing:
     *        - [0] Parameter types (16-bit unsigned integer)
     *        - [1] Parameter value (binary string)
     * @return string The binary string representation of the parameters
     */
    public static function encodeParams(array $params): string
    {
        $body = "";
        $padding = "";
        foreach ($params as [$paramType, $paramValue]) {
            $paramLength = \strlen($paramValue) + 4;
            $body .= $padding;
            $body .= pack("nn", $paramType, $paramLength) . $paramValue;
            $padding = str_repeat("\x00", self::padl($paramLength));
        }

        return $body;
    }

    /**
     * Calculates required padding length for SCTP parameters.
     *
     * SCTP parameters must be padded to 4-byte boundaries as per RFC 4960.
     *
     * @param int $length The length of the parameter (including header)
     * @return int The number of padding bytes needed (0-3)
     */
    public static function padl(int $length): int
    {
        $m = $length % 4;
        return $m ? 4 - $m : 0;
    }

    /**
     * Generates a cryptographically secure random 16-bit unsigned integer.
     *
     * @return int Random value between 0 and 65535 (inclusive)
     * @throws RandomException If random bytes cannot be generated
     */
    public static function random16(): int
    {
        $unpacked = unpack("n", random_bytes(2));
        if ($unpacked === false) {
            throw new InvalidArgumentException("Failed to unpack random 16-bit value");
        }
        return (int) $unpacked[1];
    }

    /**
     * Generates a cryptographically secure random 32-bit unsigned integer.
     *
     * @return int Random value between 0 and 4294967295 (inclusive)
     * @throws RandomException If random bytes cannot be generated
     */
    public static function random32(): int
    {
        $unpacked = unpack("N", random_bytes(4));
        if ($unpacked === false) {
            throw new InvalidArgumentException("Failed to unpack random 32-bit value");
        }
        return (int) $unpacked[1];
    }

    /**
     * Decrements a TSN (Transmission Sequence Number) with proper modulo arithmetic.
     *
     * Handles wrap-around of 32-bit TSN space as defined by SCTP_TSN_MODULO.
     *
     * @param int $a The TSN value to decrement
     * @return int (a - 1) modulo SCTP_TSN_MODULO
     */
    public static function tsnMinusOne(int $a): int
    {
        return ($a - 1 + SctpConstant::SCTP_TSN_MODULO) % SctpConstant::SCTP_TSN_MODULO;
    }

    /**
     * Increments a TSN (Transmission Sequence Number) with proper modulo arithmetic.
     *
     * Handles wrap-around of 32-bit TSN space as defined by SCTP_TSN_MODULO.
     *
     * @param int $a The TSN value to increment
     * @return int (a + 1) modulo SCTP_TSN_MODULO
     */
    public static function tsnPlusOne(int $a): int
    {
        return ($a + 1) % SctpConstant::SCTP_TSN_MODULO;
    }

    /**
     * Calculates CRC32C checksum (Castagnoli variant) for SCTP packets.
     *
     * Implements the checksum algorithm defined in RFC 3309 for SCTP.
     * Uses polynomial 0x1EDC6F41 (reversed to 0x82F63B78).
     *
     * @param string $data The data to calculate checksum for
     * @return int The 32-bit CRC32C checksum
     */
    public static function crc32c(string $data): int
    {
        /** @var array<int, int> $table */
        static $table;
        if (!$table) {
            for ($n = 0; $n < 256; $n++) {
                $c = $n;
                for ($k = 0; $k < 8; $k++) {
                    if ($c & 1) {
                        $c = 0x82F63B78 ^ ($c >> 1);
                    } else {
                        $c >>= 1;
                    }
                }
                $table[$n] = $c;
            }
        }

        $crc = 0xFFFFFFFF;
        for ($i = 0, $len = \strlen($data); $i < $len; $i++) {
            $crc = $table[($crc ^ \ord($data[$i])) & 0xFF] ^ ($crc >> 8);
        }

        return $crc ^ 0xFFFFFFFF;
    }

    /**
     * Compares two unsigned 32-bit integers accounting for wrap-around.
     *
     * @param int $a First unsigned 32-bit integer
     * @param int $b Second unsigned 32-bit integer
     * @return bool True if $a is greater than $b considering wrap-around
     */
    public static function uint32Gt(int $a, int $b): bool
    {
        $halfMod = 0x80000000; // 2^31
        return (($a < $b) && (($b - $a) > $halfMod)) || (($a > $b) && (($a - $b) < $halfMod));
    }

    /**
     * Compares two unsigned 32-bit integers accounting for wrap-around.
     *
     * @param int $a First unsigned 32-bit integer
     * @param int $b Second unsigned 32-bit integer
     * @return bool True, if $a is greater than or equal to $b considering wrap-around
     */
    public static function uint32Gte(int $a, int $b): bool
    {
        return ($a === $b) || self::uint32Gt($a, $b);
    }

    /**
     * Compares two unsigned 16-bit integers accounting for wrap-around.
     *
     * @param int $a First unsigned 16-bit integer
     * @param int $b Second unsigned 16-bit integer
     * @return bool True if $a is greater than $b considering wrap-around
     */
    public static function uint16Gt(int $a, int $b): bool
    {
        $halfMod = 0x8000;
        return (($a < $b) && (($b - $a) > $halfMod)) || (($a > $b) && (($a - $b) < $halfMod));
    }

    /**
     * Adds two unsigned 16-bit integers with proper wrap-around.
     *
     * @param int $a First unsigned 16-bit integer
     * @param int $b Second unsigned 16-bit integer
     * @return int ($a + $b) modulo 65536
     */
    public static function uint16Add(int $a, int $b): int
    {
        return ($a + $b) & 0xFFFF;
    }

    /**
     * Checks if a string contains binary (non-ASCII) data.
     *
     * @param string $str The string to check
     * @return bool True if the string contains bytes outside ASCII printable range
     */
    public static function isBinary(string $str): bool
    {
        return preg_match('~[^\x09\x0A\x0D\x20-\x7E]~', $str) > 0;
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SCTP\Trait;

use Webrtc\DataChannel\Enum\State as DataChannelState;
use Webrtc\DataChannel\RTCDataChannel;
use Webrtc\DataChannel\RTCDataChannelParameters;
use Webrtc\Exception\InvalidArgumentException;
use Webrtc\Exception\RuntimeException;
use Webrtc\SCTP\Chunk\Chunk;
use Webrtc\SCTP\Chunk\DataChunk;
use Webrtc\SCTP\Chunk\ForwardTsnChunk;
use Webrtc\SCTP\Enum\State;
use Webrtc\SCTP\Param\StreamResetOutgoingParam;
use Webrtc\SCTP\SctpConstant;
use Webrtc\SCTP\SctpUtility;
use Revolt\EventLoop;
use SplQueue;

/**
 * Trait DataChannel.
 *
 * Provides support for managing SCTP-based WebRTC data channels.
 * Includes timers, retransmissions, congestion control, data flushing,
 * and stream lifecycle management.
 */
trait DataChannel
{
    private ?string $dataChannelTask = null;
    private ?int $dataChannelId = null;
    /** @var SplQueue<array{0: RTCDataChannel, 1: int, 2: string}> */
    private SplQueue $dataChannelQueue;
    /** @var array<int, RTCDataChannel> */
    private array $dataChannels = [];

    /**
     *  Returns the current data channel timer.
     *
     */
    public function getDataChannelTask(): ?string
    {
        return $this->dataChannelTask;
    }

    /**
     * Handler for data channel task expiration.
     * Clears retransmission states and resets congestion control.
     *
     */
    public function dataChannelTaskExpired(): void
    {
        $this->dataChannelTask = null;
        $this->log(" DataChannel task expired!");

        // Mark retransmitting or abandoned chunks
        foreach ($this->sentQueue as $chunk) {
            if (!$this->maybeAbandon($chunk)) {
                $chunk->getAttributes()->retransmit = true;
            }
        }
        $this->updateAdvancedPeerAckPoint();

        // Adjust a congestion window
        $this->fastRecoveryExit = null;
        $this->flightSize = 0;
        $this->partialBytesAcked = 0;

        $this->ssthresh = max(intdiv($this->cwnd, 2), 4 * SctpConstant::USERDATA_MAX_LENGTH);
        $this->cwnd = SctpConstant::USERDATA_MAX_LENGTH;

        EventLoop::queue(fn () => $this->transmit());
    }

    /**
     * Restarts the data channel timer.
     *
     */
    private function dataChannelTaskRestart(): void
    {
        $this->log(" Datachannel timer restarted");
        if ($this->dataChannelTask !== null) {
            EventLoop::cancel($this->dataChannelTask);
            $this->dataChannelTask = null;
        }
        $this->armDataChannelTimer();
    }

    /**
     * Arm the data-channel retransmission timer without pinning the transport.
     *
     * Registering it as $this->dataChannelTaskExpired(...) captures $this strongly and would keep
     * the event loop holding this SCTP transport alive forever, defeating an unset()+gc cycle.
     * A weak reference lets the transport be collected; an orphaned timer fires once into nothing.
     */
    private function armDataChannelTimer(): void
    {
        $weak = \WeakReference::create($this);
        $this->dataChannelTask = EventLoop::delay($this->rto, static function () use ($weak): void {
            $weak->get()?->dataChannelTaskExpired();
        });
    }

    /**
     * Starts the data channel timer if not already running.
     *
     * @throws RuntimeException if already started.
     */
    private function dataChannelTaskStart(): void
    {
        if ($this->dataChannelTask !== null) {
            throw new RuntimeException("Datachannel timer already started");
        }
        $this->log(" Datachannel timer started");
        $this->armDataChannelTimer();
    }

    /**
     * Cancels the running data channel timer, if any.
     */
    public function dataChannelTaskCancel(): void
    {
        if ($this->dataChannelTask !== null) {
            $this->log(" Datachannel timer canceled");
            EventLoop::cancel($this->dataChannelTask);
            $this->dataChannelTask = null;
        }
    }

    /**
     *
     * Processes and sends the forward TSN chunk if it exists.
     * If there is no forward TSN chunk, it does nothing.
     * Ensures the data channel task is started if it's not already running.
     */
    private function processForwardTsn(): void
    {
        if ($this->forwardTsnChunk !== null) {
            $this->sendChunk($this->forwardTsnChunk);
            $this->forwardTsnChunk = null;
            if ($this->dataChannelTask === null) {
                $this->dataChannelTaskStart();
            }
        }
    }

    /**
     * Calculates the congestion window size (cwnd) based on the current flight size.
     * Takes into account the fast recovery state and burst size.
     *
     * @return int The calculated congestion window size.
     */
    private function calculateCwnd(): int
    {
        $burstSize = ($this->fastRecoveryExit !== null)
            ? 2 * SctpConstant::USERDATA_MAX_LENGTH
            : 4 * SctpConstant::USERDATA_MAX_LENGTH;

        return min($this->flightSize + $burstSize, $this->cwnd);
    }

    /**
     * Handles the retransmission of chunks that need it, and the transmission of chunks
     * from the outbound queue. It ensures that the flight size doesnâ€™t exceed the congestion window (cwnd).
     *
     * @param int $cwnd The current congestion window size.
     */
    private function retransmitAndTransmit(int $cwnd): void
    {
        $retransmitEarliest = true;

        // Retransmit chunks from the sent queue if they need retransmission
        foreach ($this->sentQueue as $chunk) {
            if ($chunk->getAttributes()->retransmit) {
                if ($this->fastRecoveryTransmit) {
                    $this->fastRecoveryTransmit = false;
                } elseif ($this->flightSize >= $cwnd) {
                    return; // Stop if flight size exceeds cwnd
                }

                $this->flightSizeIncrease($chunk);
                $this->resetChunkRetransmitState($chunk);
                $this->sendChunk($chunk);

                if ($retransmitEarliest) {
                    $this->dataChannelTaskRestart();
                }
            }
            $retransmitEarliest = false;
        }

        // Transmit from the outbound queue if we haven't reached the congestion window size
        while (!$this->outboundQueue->isEmpty() && $this->flightSize < $cwnd) {
            $chunk = $this->outboundQueue->dequeue();
            $this->sentQueue->enqueue($chunk);
            $this->flightSizeIncrease($chunk);

            // Update chunk counters and send the chunk
            $chunk->getAttributes()->sentCount += 1;
            $chunk->getAttributes()->sentTime = microtime(true);
            $this->sendChunk($chunk);

            // Start the datachannel task if not already running
            $this->ensureDataChannelTaskRunning();
        }
    }

    /**
     * Ensures that the data channel task is running.
     * Starts it if it is not already running.
     */
    private function ensureDataChannelTaskRunning(): void
    {
        if ($this->dataChannelTask === null) {
            $this->dataChannelTaskStart();
        }
    }

    /**
     * Resets the retransmitted state of a chunk.
     * This includes clearing the misses count, marking it as no longer needing retransmission, and incrementing the sent count.
     *
     * @param Chunk $chunk The chunk whose retransmit state will be reset.
     */
    private function resetChunkRetransmitState(Chunk $chunk): void
    {
        $chunk->getAttributes()->misses = 0;
        $chunk->getAttributes()->retransmit = false;
        $chunk->getAttributes()->sentCount += 1;
    }

    /**
     *
     * Main method that orchestrates the transmission process.
     * This method:
     * - Processes the forward TSN chunk.
     * - Calculates the congestion window size (cwnd).
     * - Handles retransmission of chunks and transmits chunks from the outbound queue.
     */
    public function transmit(): void
    {
        $this->processForwardTsn();
        $cwnd = $this->calculateCwnd();
        $this->retransmitAndTransmit($cwnd);
    }

    /**
     * Transmit reconfiguration requests.
     */
    private function transmitReconfig(): void
    {
        if (
            $this->state === State::ESTABLISHED &&
            !empty($this->reconfigQueue) &&
            $this->reconfigRequest === null
        ) {
            $streams = \array_slice($this->reconfigQueue, 0, SctpConstant::RECONFIG_MAX_STREAMS);
            $this->reconfigQueue = \array_slice($this->reconfigQueue, SctpConstant::RECONFIG_MAX_STREAMS);

            $param = new StreamResetOutgoingParam(
                requestSequence: $this->reconfigRequestSeq,
                responseSequence: $this->reconfigResponseSeq,
                lastTsn: SctpUtility::tsnMinusOne($this->localTsn),
                streams: $streams
            );

            $this->reconfigRequest = $param;
            $this->reconfigRequestSeq = SctpUtility::tsnPlusOne($this->reconfigRequestSeq);

            // Transmit the reconfiguration parameter asynchronously.
            EventLoop::queue(fn () => $this->sendReconfigParam($param));
        }
    }

    /**
     * Update the advanced peer acknowledgment point.
     */
    public function updateAdvancedPeerAckPoint(): void
    {
        if (SctpUtility::uint32Gt($this->lastSackedTsn, $this->advancedPeerAckTsn)) {
            $this->advancedPeerAckTsn = $this->lastSackedTsn;
        }

        $done = 0;
        $streams = [];
        while (!$this->sentQueue->isEmpty() && $this->sentQueue->top()->getAttributes()->abandoned) {
            $chunk = $this->sentQueue->dequeue();
            $this->advancedPeerAckTsn = $chunk->getTsn();
            $done++;
            if (!($chunk->getFlags() & SctpConstant::SCTP_DATA_UNORDERED)) {
                $streams[$chunk->getStreamId()] = $chunk->getStreamSeq();
            }
        }

        if ($done) {
            // Build FORWARD TSN
            $this->forwardTsnChunk = new ForwardTsnChunk();
            $this->forwardTsnChunk->setCumulativeTsn($this->advancedPeerAckTsn);
            $this->forwardTsnChunk->setStreams(array_map(static fn ($key, $value) => [$key, $value], array_keys($streams), $streams));
        }
    }

    /**
     * Sends data over the specified stream, fragmenting as needed.
     */
    public function sendDataStream(
        int    $streamId,
        int    $ppId,
        string $userData,
        ?float $expiry = 0,
        ?int   $maxRetransmits = null,
        ?bool  $ordered = true
    ): void {
        // Determine stream sequence
        $streamSeq = $ordered ? ($this->outboundStreamSeq[$streamId] ?? 0) : 0;
        $fragments = (int) ceil(\strlen($userData) / SctpConstant::USERDATA_MAX_LENGTH);
        $pos = 0;

        for ($fragment = 0; $fragment < $fragments; $fragment++) {
            $chunk = new DataChunk();
            $chunk->setFlags(0);
            if (!$ordered) {
                $chunk->setFlags(SctpConstant::SCTP_DATA_UNORDERED);
            }
            if ($fragment === 0) {
                $chunk->setFlags($chunk->getFlags() | SctpConstant::SCTP_DATA_FIRST_FRAG);
            }
            if ($fragment === $fragments - 1) {
                $chunk->setFlags($chunk->getFlags() | SctpConstant::SCTP_DATA_LAST_FRAG);
            }

            $chunk->setTsn($this->localTsn);
            $chunk->setStreamId($streamId);
            $chunk->setStreamSeq($streamSeq);
            $chunk->setProtocol($ppId);
            $chunk->setUserData(substr($userData, $pos, SctpConstant::USERDATA_MAX_LENGTH));

            $chunk->getAttributes()->bookSize = \strlen($chunk->getUserData());
            $chunk->getAttributes()->expiry = $expiry ?? 0;
            $chunk->getAttributes()->maxRetransmits = $maxRetransmits;

            $pos += SctpConstant::USERDATA_MAX_LENGTH;
            $this->localTsn = SctpUtility::tsnPlusOne($this->localTsn);
            $this->outboundQueue->enqueue($chunk);
        }

        if ($ordered) {
            $this->outboundStreamSeq[$streamId] = SctpUtility::uint16Add($streamSeq, 1);
        }

        // Transmit outbound data
        $this->transmit();
    }

    /**
     * Requests closing a data channel by sending an Outgoing Stream Reset Request.
     *
     * @param RTCDataChannel $channel The channel to be closed.
     */
    public function dataChannelClose(RTCDataChannel $channel): void
    {
        if (!\in_array($channel->getReadyState(), [DataChannelState::Closing, DataChannelState::Closed], true)) {
            $channel->setReadyState(DataChannelState::Closing);

            if ($this->state == State::ESTABLISHED) {
                $id = $channel->getId();
                if ($id === null) {
                    throw new RuntimeException("Cannot close a data channel that has no ID");
                }
                $this->reconfigQueue[] = $id;
                if (\count($this->reconfigQueue) == 1) {
                    $this->transmitReconfig();
                }

            } else {
                // No stream id yet, so there is nothing to reset. Leave the queued OPEN for
                // flush to skip; rebuilding the queue here has dropped the sibling channel's
                // OPEN on the Windows PHP 8.4 runner.
                $id = $channel->getId();
                if ($id !== null) {
                    unset($this->dataChannels[$id]);
                }

                $channel->setReadyState(DataChannelState::Closed);
            }
        }
    }

    /**
     * Marks the specified data channel as closed.
     *
     * @param int $streamId The ID of the stream to close.
     */
    public function dataChannelClosed(int $streamId): void
    {
        if (isset($this->dataChannels[$streamId])) {
            $channel = $this->dataChannels[$streamId];
            $channel->setReadyState(DataChannelState::Closed);
            unset($this->dataChannels[$streamId]);
        }
    }

    /**
     *
     * Attempts to flush buffered data to the SCTP layer, waiting for the association to be established.
     */
    public function dataChannelFlush(): void
    {
        if ($this->state != State::ESTABLISHED) {
            return;
        }

        while (!$this->dataChannelQueue->isEmpty() && $this->outboundQueue->isEmpty()) {
            [$channel, $protocol, $userData] = $this->dataChannelQueue->dequeue();

            if (\in_array($channel->getReadyState(), [DataChannelState::Closing, DataChannelState::Closed], true)) {
                continue;
            }

            $streamId = $channel->getId();
            if ($streamId === null) {
                $streamId = $this->dataChannelId;
                if ($streamId === null) {
                    throw new RuntimeException("Cannot assign an ID to a data channel");
                }
                while (isset($this->dataChannels[$streamId])) {
                    $streamId += 2;
                }
                $this->dataChannels[$streamId] = $channel;
                $channel->setId($streamId);
            }

            if ($protocol === SctpConstant::WEBRTC_DCEP) {
                $this->sendDataStream($streamId, $protocol, $userData);
            } else {
                $expiry = null;
                $maxPacketLifeTime = $channel->getMaxPacketLifeTime();
                if ($maxPacketLifeTime !== null) {
                    $expiry = (float)time() + ((float)$maxPacketLifeTime / 1000.0);
                }
                $this->sendDataStream(
                    $streamId,
                    $protocol,
                    $userData,
                    $expiry,
                    $channel->getMaxRetransmits(),
                    $channel->getOrdered()
                );
                $channel->addBufferedAmount(-\strlen($userData));
            }

        }
    }

    /**
     * Adds a negotiated data channel to the handler.
     *
     * @param RTCDataChannel $channel The channel to add.
     *
     * @throws InvalidArgumentException If the data channel ID is already registered.
     */
    public function dataChannelAddNegotiated(RTCDataChannel $channel): void
    {
        $id = $channel->getId();
        if ($id === null) {
            throw new InvalidArgumentException("Data channel ID is required for negotiated data channels");
        }
        if (isset($this->dataChannels[$id])) {
            throw new InvalidArgumentException("Data channel with ID {$id} already registered");
        }

        $this->dataChannels[$id] = $channel;

        if ($this->state == State::ESTABLISHED) {
            $channel->setReadyState(DataChannelState::Open);
        }
    }

    /**
     * Opens a new data channel, registering it and setting its properties.
     *
     * @param RTCDataChannel $channel The channel to open.
     *
     * @throws InvalidArgumentException If the channel ID is already registered.
     */
    public function dataChannelOpen(RTCDataChannel $channel): void
    {
        $id = $channel->getId();
        if ($id !== null) {
            if (isset($this->dataChannels[$id])) {
                throw new InvalidArgumentException("Data channel with ID {$id} already registered");
            }
            $this->dataChannels[$id] = $channel;

        }
        $channelType = SctpConstant::DATA_CHANNEL_RELIABLE;
        $priority = 0;
        $reliability = 0;

        if (!$channel->getOrdered()) {
            $channelType |= 0x80;
        }
        if ($channel->getMaxRetransmits() !== null) {
            $channelType |= 1;
            $reliability = $channel->getMaxRetransmits();
        } elseif ($channel->getMaxPacketLifeTime() !== null) {
            $channelType |= 2;
            $reliability = $channel->getMaxPacketLifeTime();
        }

        $data = pack("CCnNnn", SctpConstant::DATA_CHANNEL_OPEN, $channelType, $priority, $reliability, \strlen($channel->getLabel()), \strlen($channel->getProtocol()));
        $data .= $channel->getLabel();
        $data .= $channel->getProtocol();
        $this->dataChannelQueue->enqueue([$channel, SctpConstant::WEBRTC_DCEP, $data]);
        // Before the association exists the queued callback cannot send anything, and on the
        // Windows PHP 8.4 runner it is enough to stall the DTLS handshake that connect() has
        // also queued. Flush now only once the association is up; setState(ESTABLISHED) flushes
        // whatever is still queued.
        if ($this->state === State::ESTABLISHED) {
            EventLoop::queue(fn () => $this->dataChannelFlush());
        }
    }

    /**
     * Receives a message for a data channel, handling different message types.
     *
     * @param int $streamId The ID of the stream.
     * @param int $ppId The protocol ID.
     * @param string $data The received data.
     *
     * @throws InvalidArgumentException If an invalid stream ID is encountered.
     */
    public function dataChannelReceive(int $streamId, int $ppId, string $data): void
    {
        if ($ppId === SctpConstant::WEBRTC_DCEP && \strlen($data) > 0) {
            $msgType = \ord($data[0]);
            if ($msgType === SctpConstant::DATA_CHANNEL_OPEN && \strlen($data) >= 12) {
                // Assert no existing channel for this stream ID
                if (isset($this->dataChannels[$streamId])) {
                    throw new InvalidArgumentException("Data channel with stream ID $streamId already exists");
                }

                $unpacked = unpack("CmsgType/CchannelType/npriority/Nreliability/nlabelLength/nprotocolLength", $data);
                if ($unpacked === false) {
                    throw new InvalidArgumentException("Failed to unpack data channel open message");
                }
                $pos = 12;
                $labelLength = (int) $unpacked["labelLength"];
                $protocolLength = (int) $unpacked["protocolLength"];
                $label = substr($data, $pos, $labelLength);

                $pos += $labelLength;
                $protocol = substr($data, $pos, $protocolLength);

                // Determine reliability settings
                $maxPacketLifeTime = null;
                $maxRetransmits = null;
                $channelType = (int) $unpacked["channelType"];
                if (($channelType & 0x03) === 1) {
                    $maxRetransmits = (int) $unpacked["reliability"];
                } elseif (($channelType & 0x03) === 2) {
                    $maxPacketLifeTime = (int) $unpacked["reliability"];
                }

                // Register the channel
                $parameters = new RTCDataChannelParameters(
                    label: $label,
                    maxPacketLifeTime: $maxPacketLifeTime,
                    maxRetransmits: $maxRetransmits,
                    ordered: ($channelType & 0x80) === 0,
                    protocol: $protocol,
                    id: $streamId
                );
                $channel = new RTCDataChannel($this, $parameters, false);
                $channel->setReadyState(DataChannelState::Open);
                $this->dataChannels[$streamId] = $channel;

                // Send ACK
                $this->dataChannelQueue->enqueue([
                    $channel,
                    SctpConstant::WEBRTC_DCEP,
                    pack("C", SctpConstant::DATA_CHANNEL_ACK),
                ]);

                EventLoop::queue(fn () => $this->dataChannelFlush());

                // Notify listeners (the peer connection) of the new channel.
                $this->notifyDataChannel($channel);
            } elseif ($msgType === SctpConstant::DATA_CHANNEL_ACK) {
                if (!isset($this->dataChannels[$streamId])) {
                    throw new InvalidArgumentException("Data channel with stream ID $streamId does not exist");
                }
                $channel = $this->dataChannels[$streamId];
                $channel->setReadyState(DataChannelState::Open);
            }
        } elseif ($ppId === SctpConstant::WEBRTC_STRING && isset($this->dataChannels[$streamId])) {
            // Deliver message for string data
            $this->dataChannels[$streamId]->dispatchMessage($data);
        } elseif ($ppId === SctpConstant::WEBRTC_STRING_EMPTY && isset($this->dataChannels[$streamId])) {
            // Deliver empty string message
            $this->dataChannels[$streamId]->dispatchMessage("");
        } elseif ($ppId === SctpConstant::WEBRTC_BINARY && isset($this->dataChannels[$streamId])) {
            // Deliver binary message
            $this->dataChannels[$streamId]->dispatchMessage($data);
        } elseif ($ppId === SctpConstant::WEBRTC_BINARY_EMPTY && isset($this->dataChannels[$streamId])) {
            // Deliver empty binary message
            $this->dataChannels[$streamId]->dispatchMessage("");
        }
    }

    /**
     * Sends data over a specified data channel.
     *
     * @param RTCDataChannel $channel The channel to send data over.
     * @param string $data The data to send.
     */
    public function dataChannelSend(RTCDataChannel $channel, string $data): void
    {
        if ($data === "") {
            $ppId = SctpConstant::WEBRTC_STRING_EMPTY;
            $userData = "\x00";
        } elseif (mb_check_encoding($data, 'UTF-8')) {
            $ppId = SctpConstant::WEBRTC_STRING;
            $converted = mb_convert_encoding($data, 'UTF-8', 'ISO-8859-1');
            if ($converted === false) {
                throw new RuntimeException("Failed to convert data channel payload to UTF-8");
            }
            $userData = $converted;
        } elseif ($data === "\x00") {
            $ppId = SctpConstant::WEBRTC_BINARY_EMPTY;
            $userData = "\x00";
        } else {
            $ppId = SctpConstant::WEBRTC_BINARY;
            $userData = $data;
        }

        $channel->addBufferedAmount(\strlen($userData));
        $this->dataChannelQueue->enqueue([$channel, $ppId, $userData]);
        $this->dataChannelFlush();
    }

    /**
     * Update the RTO given a new round-trip measurement.
     *
     * @param float $rtt The round-trip time.
     */
    public function updateRto(float $rtt): void
    {
        if ($this->srtt === null) {
            $this->rttvar = $rtt / 2.0;
            $this->srtt = $rtt;
        } else {
            if ($this->rttvar === null) {
                $this->rttvar = $rtt / 2.0;
            }
            $this->rttvar = (1.0 - (float)SctpConstant::SCTP_RTO_BETA) * $this->rttvar + (float)SctpConstant::SCTP_RTO_BETA * abs($this->srtt - $rtt);
            $this->srtt = (1.0 - (float)SctpConstant::SCTP_RTO_ALPHA) * $this->srtt + (float)SctpConstant::SCTP_RTO_ALPHA * $rtt;
        }
        $this->rto = max(SctpConstant::SCTP_RTO_MIN, min($this->srtt + 4.0 * $this->rttvar, SctpConstant::SCTP_RTO_MAX));
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SCTP;

/**
 * Receiver for reassembled messages of a raw signaling association.
 *
 * tgcalls runs its signaling channel over a plain SCTP association that never negotiates a
 * data channel: it opens stream 0 and sends user data on it. A sink installed with
 * {@see RTCSctpTransport::setSignalingSink()} receives that user data directly, bypassing the
 * data channel layer.
 *
 * The sink is part of the transport's serializable state, so it must be a real, serializable
 * invokable object â€” never a Closure, which cannot be serialized. Requiring this interface
 * (rather than an arbitrary callable) makes that contract explicit at the type level: the
 * consumer implements it on an ordinary object that survives a serialize cycle.
 */
interface SignalingSinkInterface
{
    /**
     * Handle one reassembled signaling message.
     *
     * @param string $data The reassembled user data received on the signaling stream.
     */
    public function __invoke(string $data): void;
}
<?php declare(strict_types=1);

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SCTP;

use Webrtc\Exception\RuntimeException;
use Webrtc\Mixin\SerializableState;
use Webrtc\SCTP\Chunk\Chunk;
use Webrtc\SCTP\Enum\State;
use Psr\Log\LoggerInterface;
use Revolt\EventLoop;

/**
 * SCTP Timer management class.
 *
 * This class handles retransmission timers for SCTP chunks, implementing the
 * retransmission timeout (RTO) mechanism as defined in RFC 4960.
 *
 * Features:
 * - Manages retransmission timers for reliable chunk delivery
 * - Implements exponential backoff for retransmissions
 * - Tracks retransmission attempts and enforces maximum retry limits
 * - Integrates with the Revolt event loop for timer management
 * - Provides logging capabilities for debugging timer operations
 */
final class SctpTimer
{
    /** @var Chunk|null The chunk being tracked by this timer */
    private ?Chunk $chunk = null;

    /** @var string|null Handle of the active timer */
    private ?string $task = null;

    /** @var int Count of failed transmission attempts */
    private int $failures = 0;

    /**
     * Constructor.
     *
     * @param RTCSctpTransport $transport The SCTP transport instance this timer belongs to
     * @param int $maxTries Maximum number of retransmission attempts before giving up
     * @param LoggerInterface|null $logger Optional logger for debugging messages
     */
    public function __construct(
        private readonly RTCSctpTransport $transport,
        private readonly int              $maxTries,
        private readonly ?LoggerInterface $logger = null
    ) {
    }

    /**
     * Starts the timer for a chunk.
     *
     * @param Chunk $chunk The chunk to track with this timer
     * @throws RuntimeException If the timer is already running
     */
    public function start(Chunk $chunk): void
    {
        if ($this->task !== null) {
            throw new RuntimeException('Task already started');
        }
        $this->chunk = $chunk;
        $this->log("it started -> chunk: " . \get_class($this->chunk));
        $this->armTimer();
    }

    /**
     * Arm the retransmission timer without pinning this object in the event loop.
     *
     * Registering the timer as $this->expired(...) would capture $this strongly, and since the
     * transport holds its timers (and each timer holds the transport back) that strong watcher
     * would keep the whole SCTP association alive forever â€” an unset()+gc_collect_cycles() could
     * never reclaim it. Holding only a weak reference lets the association be collected; a timer
     * left armed for a collected owner simply fires once into nothing and is removed.
     */
    private function armTimer(): void
    {
        $weak = \WeakReference::create($this);
        $this->task = EventLoop::delay($this->transport->getRto(), static function () use ($weak): void {
            $weak->get()?->expired();
        });
    }

    /**
     * Cancels the active timer.
     *
     * Clears the timer and resets the tracked chunk. Safe to call even if no timer is active.
     */
    public function cancel(): void
    {
        if ($this->task !== null && $this->chunk !== null) {
            $this->log("it canceled -> chunk: " . \get_class($this->chunk));
            EventLoop::cancel($this->task);
            $this->task = null;
            $this->chunk = null;
        }
    }

    /**
     * Handles timer expiration event.
     *
     * This method is called when the timer expires. It either:
     * - Triggers chunk retransmission if under max retry limit, or
     * - Closes the transport if max retries are exceeded
     */
    public function expired(): void
    {
        $this->task = null;
        $this->log("it expired -> chunk: " . ($this->chunk !== null ? \get_class($this->chunk) : 'null'));
        if ($this->failures >= $this->maxTries) {
            $this->transport->setState(State::CLOSED);
        } else {
            $chunk = $this->chunk;
            if ($chunk !== null) {
                EventLoop::queue(function () use ($chunk): void {
                    $this->transport->sendChunk($chunk);
                });
            }
            $this->armTimer();
        }
        $this->failures++;
    }

    /**
     * Logs a debug message if logger is configured.
     *
     * @param string $message The message to log
     */
    private function log(string $message): void
    {
        $this->logger?->debug(sprintf('SCTP timer: %s', $message));
    }

    /**
     * Gets the current failure counts.
     *
     * @return int Number of transmission failures for the current chunk
     */
    public function getFailures(): int
    {
        return $this->failures;
    }

    /**
     * Sets the failure count.
     *
     * @param int $failures New failure count value
     */
    public function setFailures(int $failures): void
    {
        $this->failures = $failures;
    }

    /**
     * Gets the active timer instance.
     *
     * @return string|null Handle of the active timer, or null if not running
     */
    public function getTask(): ?string
    {
        return $this->task;
    }

    /**
     * Sets the timer instance.
     *
     * Note: Generally prefer using start()/cancel() methods rather than
     * manually managing the timer instance.
     *
     * @param string|null $task Handle of the timer to set
     */
    public function setTask(?string $task): void
    {
        $this->task = $task;
    }

    /**
     * @return array<string, mixed>
     */
    public function __serialize(): array
    {
        return SerializableState::export($this, [
            'task' => $this->task !== null,
        ]);
    }

    /**
     * @param array<string, mixed> $data
     */
    public function __unserialize(array $data): void
    {
        $restart = false;
        /**
         * @var mixed $value
         */
        foreach ($data as $key => $value) {
            if (str_ends_with($key, "\0task")) {
                $restart = $value === true;
                $data[$key] = null;
            }
        }
        SerializableState::import($this, $data);
        $this->task = null;
        if ($restart && $this->chunk !== null) {
            $this->armTimer();
        }
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SCTP;



use Override;
use Webrtc\DataChannel\Enum\State as DataChannelState;
use Webrtc\DataChannel\RTCDataChannel;
use Webrtc\DataChannel\RTCSctpTransportInterface;
use Webrtc\Exception\InvalidArgumentException;
use Webrtc\ICE\Enum\IceRole;
use Webrtc\SCTP\Listener\DataChannelListener;
use Webrtc\SCTP\Chunk\AbortChunk;
use Webrtc\SCTP\Chunk\Chunk;
use Webrtc\SCTP\Chunk\ChunkInterface;
use Webrtc\SCTP\Chunk\CookieAckChunk;
use Webrtc\SCTP\Chunk\CookieEchoChunk;
use Webrtc\SCTP\Chunk\DataChunk;
use Webrtc\SCTP\Chunk\ErrorChunk;
use Webrtc\SCTP\Chunk\ForwardTsnChunk;
use Webrtc\SCTP\Chunk\HeartbeatAckChunk;
use Webrtc\SCTP\Chunk\HeartbeatChunk;
use Webrtc\SCTP\Chunk\InitAckChunk;
use Webrtc\SCTP\Chunk\InitChunk;
use Webrtc\SCTP\Chunk\ReconfigChunk;
use Webrtc\SCTP\Chunk\SackChunk;
use Webrtc\SCTP\Chunk\ShutdownAckChunk;
use Webrtc\SCTP\Chunk\ShutdownChunk;
use Webrtc\SCTP\Chunk\ShutdownCompleteChunk;
use Webrtc\SCTP\Enum\State;
use Webrtc\SCTP\Exception\SctpException;
use Webrtc\SCTP\Param\StreamAddOutgoingParam;
use Webrtc\SCTP\Param\StreamParamInterface;
use Webrtc\SCTP\Param\StreamResetOutgoingParam;
use Webrtc\SCTP\Param\StreamResetResponseParam;
use Webrtc\SCTP\Trait\DataChannel;
use Webrtc\SDP\SctpParameter\RTCSctpCapabilities;
use Webrtc\Stats\enum\TLSState;
use Webrtc\Mixin\SerializableState;
use Psr\Log\LoggerInterface;
use Random\RandomException;
use Revolt\EventLoop;
use SplQueue;
use function Amp\async;

/**
 * Class RTCSctpTransport.
 *
 * RTCSctpTransport represents the SCTP (Stream Control Transmission Protocol) transport over a DTLS transport.
 * It manages the lifecycle, state, and data channel association of the SCTP transport used in WebRTC.
 * The class handles chunk encoding, transmission, state management, and stream configuration.
 *
 * This implementation supports both client and server roles and is capable of managing stream reset, congestion control,
 * reconfiguration parameters, and forward TSNs. It interacts with the lower-level RTCDtlsTransport and provides a
 * high-level interface for data channels and SCTP signaling.
 */
final class RTCSctpTransport implements RTCSctpTransportInterface
{
    use DataChannel;

    private const RECONFIG_PARAM_TYPES = [
        13 => "StreamResetOutgoingParam",
        16 => "StreamResetResponseParam",
        17 => "StreamAddOutgoingParam",
    ];

    // States
    private State $state = State::CLOSED;

    /** When set, incoming user data goes here instead of to the data channel layer. */
    private ?SignalingSinkInterface $signalingSink = null;
    private bool $started = false;

    /** @var \WeakMap<DataChannelListener, null> Listeners for remotely-opened data channels. */
    private \WeakMap $dataChannelListeners;

    // Local variables
    private int $localVerificationTag;

    // Remote variables
    private ?int $remotePort = null;
    private ?LoggerInterface $logger = null;

    // Inbound
    private int $advertisedRwnd = 1024 * 1024;
    private int $inboundStreamsMax = SctpConstant::MAX_STREAMS;
    private int $inboundStreamsCount = 0;
    private int $localTsn;
    private bool $sackNeeded = false;
    private ?int $lastReceivedTsn = null;

    // Outbound
    private int $outboundStreamsCount = SctpConstant::MAX_STREAMS;

    // Timers
    private int|float $rto = SctpConstant::SCTP_RTO_INITIAL;
    private SctpTimer $timer1;
    private SctpTimer $timer2;

    private array $remoteExtensions = [];
    private bool $remotePartialReliability = false;
    private int $remoteVerificationTag = 0;
    /** @var array<int, InboundStream> */
    private array $inboundStreams = [];

    /** @var int[] */
    private array $sackDuplicates = [];
    /** @var int[] */
    private array $sackMisordered = [];
    private int $cwnd = 3 * SctpConstant::USERDATA_MAX_LENGTH;
    private ?int $fastRecoveryExit = null;
    private bool $fastRecoveryTransmit = false;
    private ?ForwardTsnChunk $forwardTsnChunk = null;
    private int $flightSize = 0;

    private int $lastSackedTsn;
    private int $advancedPeerAckTsn;

    /** @var SplQueue<DataChunk> */
    private SplQueue $outboundQueue;
    /** @var array<int, int> */
    private array $outboundStreamSeq = [];

    private int $partialBytesAcked = 0;
    /** @var SplQueue<DataChunk> */
    private SplQueue $sentQueue;

    private ?float $srtt = null;
    private ?float $rttvar = null;

    // Reconfiguration properties
    /** @var int[] */
    private array $reconfigQueue = [];
    private ?StreamParamInterface $reconfigRequest = null; // Type depends on implementation
    /**
     * The streams of which only one direction was reset yet: by the peer (incoming) or by us (outgoing).
     *
     * A stream is only closed once both are (RFC 8831 6.7): before, the peer may still send on it, so its id can't be
     * used by another data channel.
     *
     * @var array<int, true>
     */
    private array $incomingStreamsReset = [];
    /** @var array<int, true> */
    private array $outgoingStreamsReset = [];
    private int $reconfigRequestSeq;
    private int $reconfigResponseSeq = 0;

    private bool $bundled = false;
    private ?string $mid = null;
    private string $hmacKey;
    private bool $localPartialReliability = true;

    /** @var array<string, int> */
    private array $chunkTypes;
    private int $ssthresh;

    /**
     * Constructs a new RTCSctpTransport instance.
     *
     * @param RTCSctpDtlsTransportInterface $transport An established DTLS transport instance.
     * @param int $localPort The local SCTP port used for data channels (default: 5000).
     * @throws SctpException If the DTLS transport is closed.
     * @throws RandomException If a random generation fails.
     */
    /** @var bool|null Explicit association role (see {@see self::setClientRole()}), null follows the ICE role. */
    private ?bool $clientRole = null;

    public function __construct(private RTCSctpDtlsTransportInterface $transport, readonly private int $localPort = 5000)
    {
        if ($transport->getState() == TLSState::CLOSED) {
            throw new SctpException("The connection to RTCSctpTransport can't be established.");
        }

        $this->localVerificationTag = SctpUtility::random32();
        $this->localTsn = SctpUtility::random32();
        $this->hmacKey = random_bytes(16);
        $this->chunkTypes = array_flip(SctpPacket::CHUNK_TYPES);
        $this->lastSackedTsn = SctpUtility::tsnMinusOne($this->localTsn);
        $this->advancedPeerAckTsn = SctpUtility::tsnMinusOne($this->localTsn);
        $this->reconfigRequestSeq = $this->localTsn;
        $this->timer1 = new SctpTimer($this, SctpConstant::SCTP_MAX_INIT_RETRANS, $this->logger);
        $this->timer2 = new SctpTimer($this, SctpConstant::SCTP_MAX_ASSOCIATION_RETRANS, $this->logger);
        $this->ssthresh = 3 * SctpConstant::USERDATA_MAX_LENGTH;

        $dataChannelQueue = new SplQueue();
        /** @var SplQueue<array{0: \Webrtc\DataChannel\RTCDataChannel, 1: int, 2: string}> $dataChannelQueue */
        $this->dataChannelQueue = $dataChannelQueue;

        $sentQueue = new SplQueue();
        /** @var SplQueue<DataChunk> $sentQueue */
        $this->sentQueue = $sentQueue;

        $outboundQueue = new SplQueue();
        /** @var SplQueue<DataChunk> $outboundQueue */
        $this->outboundQueue = $outboundQueue;

        /** @var \WeakMap<DataChannelListener, null> */
        $this->dataChannelListeners = new \WeakMap();
    }

    /**
     * Get the maximum number of RTCDataChannel instances allowed by SCTP negotiation.
     *
     * @return int|null The negotiated maximum number of channels, or null if negotiation incomplete.
     */
    /**
     * Whether the association is up and user data can be sent.
     */
    public function isEstablished(): bool
    {
        return $this->state === State::ESTABLISHED;
    }

    /**
     * Deliver reassembled messages of the signaling association to this callback.
     *
     * tgcalls runs its signaling channel over a plain SCTP association that never negotiates a
     * data channel: it just opens stream 0 and sends user data on it. When a sink is installed,
     * incoming user data bypasses the data channel layer entirely.
     */
    /**
     * @param SignalingSinkInterface|null $sink A serializable invokable object, not a Closure.
     */
    public function setSignalingSink(?SignalingSinkInterface $sink): void
    {
        $this->signalingSink = $sink;
    }

    /**
     * Send one message on a raw stream, without the data channel establishment protocol.
     */
    public function sendSignaling(int $streamId, int $ppId, string $data): void
    {
        if (!$this->isEstablished()) {
            throw new SctpException("SCTP association is not established; cannot send signaling data.");
        }
        $this->sendDataStream($streamId, $ppId, $data);
    }

    public function getMaxChannels(): ?int
    {
        if ($this->inboundStreamsCount) {
            return min($this->inboundStreamsCount, $this->outboundStreamsCount);
        }
        return null;
    }

    /**
     * Register a listener notified when a remotely-initiated data channel opens.
     *
     * Typed replacement for on('datachannel'): the listener is a plain object, captured verbatim
     * by a serialize cycle rather than through a serializable-closure wrapper.
     */
    public function addDataChannelListener(DataChannelListener $listener): void
    {
        $this->dataChannelListeners[$listener] = null;
    }

    private function notifyDataChannel(RTCDataChannel $channel): void
    {
        foreach ($this->dataChannelListeners as $listener => $_) {
            $listener->onDataChannel($channel);
        }
    }

    /**
     * Determine if this SCTP transport is acting as a server.
     *
     * @return bool True if acting as server, false if client.
     */
    public function isServer(): bool
    {
        if ($this->clientRole !== null) {
            return !$this->clientRole;
        }
        return $this->transport->getIceTransport()->getRole() != IceRole::Controlling;
    }

    /**
     * Force the association role instead of deriving it from the ICE role.
     *
     * The client sends the INIT (and, by convention, opens the data channels); the server waits for
     * it. WebRTC ties this to the ICE role, but an in-band SCTP association set up outside SDP (the
     * way tgcalls runs its call and group-call data channels) may need the opposite: a group call
     * client is ICE-controlled yet must initiate the association towards the SFU.
     *
     * @param bool|null $client true to act as client, false as server, null to follow the ICE role.
     */
    public function setClientRole(?bool $client): void
    {
        $this->clientRole = $client;
    }

    /**
     * Get the local SCTP port number used for data channels.
     *
     * @return int The port number.
     */
    public function getPort(): int
    {
        return $this->localPort;
    }

    /**
     * Get the current SCTP state.
     *
     * @return State The current state of the transport.
     */
    public function getState(): State
    {
        return $this->state;
    }

    /**
     * Get the underlying DTLS transport used by SCTP.
     *
     * @return RTCSctpDtlsTransportInterface The DTLS transport.
     */
    public function getDtlsTransport(): RTCSctpDtlsTransportInterface
    {
        return $this->transport;
    }

    /**
     * Get the capabilities of this SCTP transport (e.g., max message size).
     *
     * @return RTCSctpCapabilities SCTP transport capabilities.
     */
    public static function getCapabilities(): RTCSctpCapabilities
    {
        return new RTCSctpCapabilities(65536);
    }

    /**
     * Set a new DTLS transport for this SCTP transport.
     *
     * @param RTCSctpDtlsTransportInterface $transport The new DTLS transport.
     */
    public function setTransport(RTCSctpDtlsTransportInterface $transport): void
    {
        $this->transport = $transport;
    }

    /**
     * Start the SCTP association with the given remote port.
     *
     * @param int $remotePort Remote SCTP port to connect to.
     */
    public function start(int $remotePort): void
    {
        if (!$this->started) {
            $this->started = true;
            $this->setState(State::CONNECTING);
            $this->remotePort = $remotePort;
            $this->dataChannelId = \intval(!$this->isServer());
            $this->transport->setSctpReceiver($this);
            if (!$this->isServer()) {
                $this->init();
            }
        }
    }

    /**
     *
     * Stop the SCTP transport and terminate the association.
     *
     *
     */
    public function stop(): void
    {
        $this->transport->removeSctpReceiver($this);
        $this->setState(State::CLOSED);
        if ($this->state != State::CLOSED) {
            $this->sendChunk(new AbortChunk()); // Abort the association.
        }
    }

    /**
     *
     * Internal method to initialize an SCTP association by sending an INIT chunk.
     *
     *
     */
    private function init(): void
    {
        $chunk = new InitChunk();
        $chunk->setInitiateTag($this->localVerificationTag);
        $chunk->setAdvertisedRwnd($this->advertisedRwnd);
        $chunk->setOutboundStreams($this->outboundStreamsCount);
        $chunk->setInboundStreams($this->inboundStreamsMax);
        $chunk->setInitialTsn($this->localTsn);
        $chunk->addParams($this->getExtensions());

        // Start timer #1 and enter COOKIE-WAIT state
        $this->timer1->start($chunk);
        $this->setState(State::COOKIE_WAIT);

        $this->sendChunk($chunk);
    }

    /**
     * Send a chunk asynchronously over the DTLS transport.
     *
     * @param Chunk $chunk The SCTP chunk to send.
     */
    public function sendChunk(Chunk $chunk): void
    {
        // Sending can block on the DTLS transport, and callers include timer callbacks that
        // must not be held up, so this runs in its own fiber.
        async(function () use ($chunk): void {
            $this->log(sprintf(" Sent chunk %s", $chunk));
            $this->transport->sendData($this->encodeChunk($chunk));
        })->ignore();
    }

    /**
     * Encode a chunk into a binary SCTP packet.
     *
     * @param Chunk $chunk The chunk to encode.
     * @return string Encoded SCTP packet.
     */
    private function encodeChunk(Chunk $chunk): string
    {
        return SctpPacket::encode($this->localPort, $this->remotePort ?? 0, $this->remoteVerificationTag, $chunk);
    }

    /**
     * Get the current RTO (Retransmission Timeout) value.
     *
     * @return float|int RTO in milliseconds.
     */
    public function getRto(): float|int
    {
        return $this->rto;
    }

    /**
     * Get the PSR-logging interface.
     *
     * @return LoggerInterface|null The logger instance or null.
     */
    public function getLogger(): ?LoggerInterface
    {
        return $this->logger;
    }

    /**
     * Set a PSR-compatible logger for internal debugging and events.
     *
     * @param LoggerInterface|null $logger Logger instance or null to disable logging.
     */
    public function setLogger(?LoggerInterface $logger): void
    {
        $this->logger = $logger;
    }

    /**
     * Decrease the congestion window's flight size based on a chunk's size.
     *
     * @param DataChunk $chunk The acknowledged chunk.
     */
    private function decreaseFlightSize(DataChunk $chunk): void
    {
        $this->flightSize = max(0, $this->flightSize - $chunk->getAttributes()->bookSize);
    }

    /**
     * Set the RTO (Retransmission Timeout) value.
     *
     * @param float|int $rto New RTO value.
     */
    public function setRto(float|int $rto): void
    {
        $this->rto = $rto;
    }

    /**
     * Get the maximum number of inbound SCTP streams.
     *
     * @return int Maximum number of inbound streams.
     */
    public function getInboundStreamsMax(): int
    {
        return $this->inboundStreamsMax;
    }

    /**
     * Set the maximum number of inbound SCTP streams.
     *
     * @param int $inboundStreamsMax The new inbound stream limit.
     */
    public function setInboundStreamsMax(int $inboundStreamsMax): void
    {
        $this->inboundStreamsMax = $inboundStreamsMax;
    }

    /**
     * Get the current count of outbound SCTP streams.
     *
     * @return int Number of outbound streams.
     */
    public function getOutboundStreamsCount(): int
    {
        return $this->outboundStreamsCount;
    }

    /**
     * Set the number of outbound SCTP streams.
     *
     * @param int $outboundStreamsCount Number of outbound streams.
     */
    public function setOutboundStreamsCount(int $outboundStreamsCount): void
    {
        $this->outboundStreamsCount = $outboundStreamsCount;
    }

    /**
     * Get the number of negotiated inbound streams.
     *
     * @return int Count of inbound streams.
     */
    public function getInboundStreamsCount(): int
    {
        return $this->inboundStreamsCount;
    }

    /**
     * Set the number of active inbound SCTP streams.
     *
     * @param int $inboundStreamsCount Number of inbound streams.
     */
    public function setInboundStreamsCount(int $inboundStreamsCount): void
    {
        $this->inboundStreamsCount = $inboundStreamsCount;
    }

    /**
     * Get the list of supported remote extensions.
     *
     * @return array SCTP extensions negotiated from remote peer.
     */
    public function getRemoteExtensions(): array
    {
        return $this->remoteExtensions;
    }

    /**
     * Set the list of negotiated remote SCTP extensions.
     *
     * @param array $remoteExtensions List of extensions.
     */
    public function setRemoteExtensions(array $remoteExtensions): void
    {
        $this->remoteExtensions = $remoteExtensions;
    }

    /**
     * Get the current Re-configuration Request Sequence number.
     *
     * @return int Reconfiguration request sequence number.
     */
    public function getReconfigRequestSeq(): int
    {
        return $this->reconfigRequestSeq;
    }

    /**
     * Set the Re-configuration Request Sequence number.
     *
     * @param int $reconfigRequestSeq The sequence number to set.
     */
    public function setReconfigRequestSeq(int $reconfigRequestSeq): void
    {
        $this->reconfigRequestSeq = $reconfigRequestSeq;
    }

    /**
     * Check if local partial reliability (PR-SCTP) is enabled.
     *
     * @return bool True if local PR-SCTP is supported, false otherwise.
     */
    public function isLocalPartialReliability(): bool
    {
        return $this->localPartialReliability;
    }

    /**
     * Enable or disable local partial reliability (PR-SCTP).
     *
     * @param bool $localPartialReliability Whether to enable local PR-SCTP.
     */
    public function setLocalPartialReliability(bool $localPartialReliability): void
    {
        $this->localPartialReliability = $localPartialReliability;
    }

    /**
     * Check if the remote party supports partial reliability (PR-SCTP).
     *
     * @return bool True if remote PR-SCTP is supported, false otherwise.
     */
    public function isRemotePartialReliability(): bool
    {
        return $this->remotePartialReliability;
    }

    /**
     * Set whether the remote party supports partial reliability (PR-SCTP).
     *
     * @param bool $remotePartialReliability Whether remote PR-SCTP is supported.
     */
    public function setRemotePartialReliability(bool $remotePartialReliability): void
    {
        $this->remotePartialReliability = $remotePartialReliability;
    }

    /**
     * Get the current local Transmission Sequence Number (TSN).
     *
     * @return int The local TSN.
     */
    public function getLocalTsn(): int
    {
        return $this->localTsn;
    }

    /**
     * Set the local Transmission Sequence Number (TSN).
     *
     * @param int $localTsn The TSN to set.
     */
    public function setLocalTsn(int $localTsn): void
    {
        $this->localTsn = $localTsn;
    }

    /**
     * Get the queue of sent chunks.
     *
     * @return SplQueue<DataChunk> Queue of sent chunks pending acknowledgment.
     */
    public function getSentQueue(): SplQueue
    {
        return $this->sentQueue;
    }

    /**
     * Get the outbound queue of data chunks.
     *
     * @return SplQueue<DataChunk> Queue of chunks pending transmission.
     */
    public function getOutboundQueue(): SplQueue
    {
        return $this->outboundQueue;
    }

    /**
     * Set the last TSN acknowledged by the peer.
     *
     * @param int $lastSackedTsn The last acknowledged TSN.
     */
    public function setLastSackedTsn(int $lastSackedTsn): void
    {
        $this->lastSackedTsn = $lastSackedTsn;
    }

    /**
     * Set the advanced peer acknowledgment TSN.
     *
     * @param int $advancedPeerAckTsn The advanced cumulative TSN from the peer.
     */
    public function setAdvancedPeerAckTsn(int $advancedPeerAckTsn): void
    {
        $this->advancedPeerAckTsn = $advancedPeerAckTsn;
    }

    /**
     * Get the advanced peer acknowledgment TSN.
     *
     * @return int The advanced peer ACK TSN.
     */
    public function getAdvancedPeerAckTsn(): int
    {
        return $this->advancedPeerAckTsn;
    }

    /**
     * Get the most recently received FORWARD TSN chunk.
     *
     * @return ForwardTsnChunk|null The FORWARD TSN chunk or null if not set.
     */
    public function getForwardTsnChunk(): ?ForwardTsnChunk
    {
        return $this->forwardTsnChunk;
    }

    /**
     * Get the last received TSN from the peer.
     *
     * @return int|null The last received TSN or null if not available.
     */
    public function getLastReceivedTsn(): ?int
    {
        return $this->lastReceivedTsn;
    }

    /**
     * Set the last received TSN from the peer.
     *
     * @param int|null $lastReceivedTsn The TSN value.
     */
    public function setLastReceivedTsn(?int $lastReceivedTsn): void
    {
        $this->lastReceivedTsn = $lastReceivedTsn;
    }

    /**
     * Check if a Selective ACK (SACK) needs to be sent.
     *
     * @return bool True if a SACK is pending, false otherwise.
     */
    public function isSackNeeded(): bool
    {
        return $this->sackNeeded;
    }

    /**
     * Indicate whether a SACK should be sent.
     *
     * @param bool $sackNeeded Whether a SACK is required.
     */
    public function setSackNeeded(bool $sackNeeded): void
    {
        $this->sackNeeded = $sackNeeded;
    }

    /**
     * Get the list of duplicate TSNs received.
     *
     * @return int[] List of duplicate TSNs.
     */
    public function getSackDuplicates(): array
    {
        return $this->sackDuplicates;
    }

    /**
     * Set the list of duplicate TSNs.
     *
     * @param int[] $sackDuplicates Duplicate TSNs.
     */
    public function setSackDuplicates(array $sackDuplicates): void
    {
        $this->sackDuplicates = $sackDuplicates;
    }

    /**
     * Get the list of misordered TSNs.
     *
     * @return int[] List of misordered TSNs.
     */
    public function getSackMisordered(): array
    {
        return $this->sackMisordered;
    }

    /**
     * Set the list of misordered TSNs.
     *
     * @param int[] $sackMisordered Misordered TSNs.
     */
    public function setSackMisordered(array $sackMisordered): void
    {
        $this->sackMisordered = $sackMisordered;
    }

    /**
     * Get the last acknowledged TSN.
     *
     * @return int The last SACKed TSN.
     */
    public function getLastSackedTsn(): int
    {
        return $this->lastSackedTsn;
    }

    /**
     * Get the outbound stream sequence numbers.
     *
     * @return array<int, int> Associative array of stream ID => sequence number.
     */
    public function getOutboundStreamSeq(): array
    {
        return $this->outboundStreamSeq;
    }

    /**
     * Get the current congestion window (cwnd) size.
     *
     * @return int The cwnd in bytes.
     */
    public function getCwnd(): int
    {
        return $this->cwnd;
    }

    /**
     * Set the congestion window (cwnd) size.
     *
     * @param int $cwnd The cwnd value in bytes.
     */
    public function setCwnd(int $cwnd): void
    {
        $this->cwnd = $cwnd;
    }

    /**
     * Get the slow-start threshold (ssthresh).
     *
     * @return int The ssthresh value in bytes.
     */
    public function getSsthresh(): int
    {
        return $this->ssthresh;
    }

    /**
     * Set the slow-start threshold (ssthresh).
     *
     * @param int $ssthresh The threshold in bytes.
     */
    public function setSsthresh(int $ssthresh): void
    {
        $this->ssthresh = $ssthresh;
    }

    /**
     * Get the TSN at which fast recovery ends.
     *
     * @return int|null The TSN value or null if not set.
     */
    public function getFastRecoveryExit(): ?int
    {
        return $this->fastRecoveryExit;
    }

    /**
     * Set the TSN at which fast recovery ends.
     *
     * @param int|null $fastRecoveryExit The TSN value.
     */
    public function setFastRecoveryExit(?int $fastRecoveryExit): void
    {
        $this->fastRecoveryExit = $fastRecoveryExit;
    }

    /**
     * Get the number of bytes currently in flight (unacknowledged).
     *
     * @return int Current flight size in bytes.
     */
    public function getFlightSize(): int
    {
        return $this->flightSize;
    }

    /**
     * Set the current flight size (unacknowledged data).
     *
     * @param int $flightSize Bytes in flight.
     */
    public function setFlightSize(int $flightSize): void
    {
        $this->flightSize = $flightSize;
    }

    /**
     * Get the T2-shutdown timer instance.
     *
     * @return SctpTimer The shutdown timer.
     */
    public function getTimer2(): SctpTimer
    {
        return $this->timer2;
    }

    /**
     * Get the T1-init timer instance.
     *
     * @return SctpTimer The init timer.
     */
    public function getTimer1(): SctpTimer
    {
        return $this->timer1;
    }

    /**
     * Get the media stream identification (MID) associated with this transport.
     *
     * @return string|null The MID value or null if not set.
     */
    public function getMid(): ?string
    {
        return $this->mid;
    }

    /**
     * Check if chunk bundling is currently active.
     *
     * @return bool True if bundling is active, false otherwise.
     */
    public function isBundled(): bool
    {
        return $this->bundled;
    }

    /**
     * Enable or disable chunk bundling.
     *
     * @param bool $bundled Whether bundling is enabled.
     */
    public function setBundled(bool $bundled): void
    {
        $this->bundled = $bundled;
    }

    /**
     * Set the media stream identification (MID).
     *
     * @param string|null $mid The MID value.
     */
    public function setMid(?string $mid): void
    {
        $this->mid = $mid;
    }

    /**
     * Increase the flight size by the book size of the chunk.
     *
     * @param DataChunk $chunk The data chunk.
     */
    private function flightSizeIncrease(DataChunk $chunk): void
    {
        $this->flightSize += $chunk->getAttributes()->bookSize;
    }

    /**
     * Set what extensions are supported by the remote party.
     *
     * @param array<array-key, array{0: int, 1: string}> $params List of parameters (key-value pairs).
     */
    private function setExtensions(array $params): void
    {
        foreach ($params as [$k, $v]) {
            if ($k == SctpConstant::SCTP_PRSCTP_SUPPORTED) {
                $this->remotePartialReliability = true;
            } elseif ($k == SctpConstant::SCTP_SUPPORTED_CHUNK_EXT) {
                $unpackedExt = unpack('C*', $v); // Convert bytes to an array of integers
                if ($unpackedExt === false) {
                    continue;
                }
                $this->remoteExtensions = array_values($unpackedExt);
            }
        }
    }

    /**
     * Get what extensions are supported by the local party.
     *
     * @return array<array-key, array{0: int, 1: string}>
     */
    private function getExtensions(): array
    {
        $params = [];
        $extensions = [];
        if ($this->localPartialReliability) {
            $params[] = [SctpConstant::SCTP_PRSCTP_SUPPORTED, ""];
            $extensions[] = $this->chunkTypes["ForwardTsnChunk"];
        }

        $extensions[] = $this->chunkTypes["ReconfigChunk"];
        $params[] = [SctpConstant::SCTP_SUPPORTED_CHUNK_EXT, pack('C*', ...$extensions)]; // Convert an array of integers to bytes

        return $params;
    }

    /**
     * Get or create the inbound stream with the specified ID.
     *
     * @param int $streamId The stream ID.
     * @return InboundStream The inbound stream.
     */
    private function getInboundStream(int $streamId): InboundStream
    {
        if (!isset($this->inboundStreams[$streamId])) {
            $this->inboundStreams[$streamId] = new InboundStream();
        }
        return $this->inboundStreams[$streamId];
    }

    /**
     * Handle data received from the network.
     *
     * @param string $data The received data.
     */
    #[Override]
    public function onReceived(string $data): void
    {
        try {
            [, , $verificationTag, $chunks] = SctpPacket::decode($data);
        } catch (InvalidArgumentException $e) {
            // Malformed packet (bad length, unpack failure or CRC32c mismatch): drop it, but log
            // like the bad-verification-tag path below so corruption/interop issues stay visible.
            $this->log(sprintf("Dropping malformed SCTP packet: %s", $e->getMessage()));
            return;
        }

        $initChunk = \count(array_filter($chunks, static fn ($chunk) => $chunk instanceof InitChunk));
        $expectedTag = $initChunk > 0 ? 0 : $this->localVerificationTag;
        // Verify tag
        if ($verificationTag != $expectedTag) {
            $this->log(sprintf("Bad verification tag %d vs %d", $verificationTag, $expectedTag));
            return;
        }

        // Handle chunks
        foreach ($chunks as $chunk) {
            $this->receiveChunk($chunk);
        }

        // Send SACK if needed
        if ($this->sackNeeded) {
            $this->sendSack();
        }
    }

    /**
     * Determine if a chunk needs to be marked as abandoned.
     *
     * @param DataChunk $chunk The data chunk.
     * @return bool True if the chunk was abandoned, false otherwise.
     */
    public function maybeAbandon(DataChunk $chunk): bool
    {
        if ($chunk->getAttributes()->abandoned) {
            return true;
        }

        // Check if the chunk should be abandoned
        $abandoned = ($chunk->getAttributes()->maxRetransmits !== null && $chunk->getAttributes()->sentCount > $chunk->getAttributes()->maxRetransmits) ||
            ($chunk->getAttributes()->expiry > 0 && $chunk->getAttributes()->expiry < time());
        if (!$abandoned) {
            return false;
        }

        // Locate chunk position in queue
        $chunkPos = null;
        foreach ($this->sentQueue as $pos => $queuedChunk) {
            if ($queuedChunk === $chunk) {
                $chunkPos = $pos;
                break;
            }
        }

        if ($chunkPos === null) {
            return false; // Chunk is not found in the queue
        }

        // Mark chunks as abandoned (both before and after the current chunk)
        $this->markChunksAbandoned($chunkPos, true);
        $this->markChunksAbandoned($chunkPos, false);

        return true;
    }

    /**
     * Mark chunks as abandoned in the specified direction.
     *
     * @param int $startPos Position of the chunk to start from.
     * @param bool $reverse If true, process backwards; otherwise, forward.
     */
    private function markChunksAbandoned(int $startPos, bool $reverse): void
    {
        $step = $reverse ? -1 : 1;
        $limit = $reverse ? 0 : \count($this->sentQueue) - 1;

        for ($pos = $startPos; $reverse ? $pos >= $limit : $pos <= $limit; $pos += $step) {
            $chunk = $this->sentQueue[$pos];
            $chunk->getAttributes()->abandoned = true;
            $chunk->getAttributes()->retransmit = false;

            if (($reverse && ($chunk->getFlags() & SctpConstant::SCTP_DATA_FIRST_FRAG)) ||
                (!$reverse && ($chunk->getFlags() & SctpConstant::SCTP_DATA_LAST_FRAG))) {
                break;
            }
        }
    }

    /**
     * Handle an incoming chunk.
     *
     * @param ChunkInterface $chunk The incoming chunk.
     */
    public function receiveChunk(ChunkInterface $chunk): void
    {
        $this->log(sprintf(" Received chunk %s", $chunk));
        match ($chunk::class) {
            DataChunk::class => $this->receiveDataChunk($chunk),
            ForwardTsnChunk::class => $this->receiveForwardTsnChunk($chunk),
            SackChunk::class => $this->receiveSackChunk($chunk),
            HeartbeatChunk::class => $this->receiveHeartbeatChunk($chunk),
            AbortChunk::class => $this->receiveAbortChunk($chunk),
            ShutdownChunk::class => $this->receiveShutdownChunk($chunk),
            ShutdownCompleteChunk::class => $this->receiveShutdownCompleteChunk($chunk),
            ReconfigChunk::class => $this->receiveReconfigChunk($chunk),
            InitChunk::class => $this->receiveInitChunk($chunk),
            CookieEchoChunk::class => $this->receiveCookieEchoChunk($chunk),
            InitAckChunk::class => $this->receiveInitAckChunk($chunk),
            CookieAckChunk::class => $this->receiveCookieAckChunk($chunk),
            ErrorChunk::class => $this->receiveErrorChunk($chunk),
        };
    }

    /**
     * Handle a DATA chunk.
     *
     * @param DataChunk $chunk The DATA chunk.
     */
    public function receiveDataChunk(DataChunk $chunk): void
    {
        $this->sackNeeded = true;

        // Mark as received
        if ($this->markReceived($chunk->getTsn())) {
            return;
        }

        // Find stream
        $inboundStream = $this->getInboundStream($chunk->getStreamId());

        // Defragment data
        $inboundStream->addChunk($chunk);
        $this->advertisedRwnd -= \strlen($chunk->getUserData());
        foreach ($inboundStream->popMessages() as [$streamId, $ppId, $userData]) {
            $this->advertisedRwnd += \strlen($userData);
            $this->receive($streamId, $ppId, $userData);
        }
    }

    /**
     * Mark incoming data TSN as received.
     *
     * @param int $tsn The TSN to mark.
     * @return bool True if the TSN was a duplicate, false otherwise.
     */
    public function markReceived(int $tsn): bool
    {
        // Mark incoming data TSN as received.
        $lastReceived = $this->lastReceivedTsn;
        if ($lastReceived === null) {
            $lastReceived = SctpUtility::tsnMinusOne($tsn);
            $this->lastReceivedTsn = $lastReceived;
        }

        // It's a duplicate
        if (SctpUtility::uint32Gte($lastReceived, $tsn) || \in_array($tsn, $this->sackMisordered, true)) {
            $this->sackDuplicates[] = $tsn;
            return true;
        }

        // Consolidate misordered entries
        $this->sackMisordered[] = $tsn;
        sort($this->sackMisordered);

        foreach ($this->sackMisordered as $index => $misorderedTsn) {
            if ($misorderedTsn === SctpUtility::tsnPlusOne($lastReceived)) {
                $this->lastReceivedTsn = $misorderedTsn;
                $lastReceived = $misorderedTsn;
                unset($this->sackMisordered[$index]);
            } else {
                break;
            }
        }

        // Filter out obsolete entries
        $this->sackDuplicates = array_filter($this->sackDuplicates, fn (int $x) => SctpUtility::uint32Gt($x, $lastReceived));
        $this->sackMisordered = array_filter($this->sackMisordered, fn (int $x) => SctpUtility::uint32Gt($x, $lastReceived));

        return false;
    }

    /**
     * Receive data stream -> ULP.
     *
     * @param int $streamId The stream ID.
     * @param int $ppId The payload protocol ID.
     * @param string $data The received data.
     */
    public function receive(int $streamId, int $ppId, string $data): void
    {
        if ($this->signalingSink !== null) {
            ($this->signalingSink)($data);
            return;
        }
        $this->dataChannelReceive($streamId, $ppId, $data);
    }

    /**
     * Handle a FORWARD TSN chunk.
     *
     * @param ForwardTsnChunk $chunk The FORWARD TSN chunk.
     */
    public function receiveForwardTsnChunk(ForwardTsnChunk $chunk): void
    {
        $this->sackNeeded = true;

        $lastReceived = $this->lastReceivedTsn;
        if ($lastReceived === null) {
            $lastReceived = SctpUtility::tsnMinusOne($chunk->getCumulativeTsn());
            $this->lastReceivedTsn = $lastReceived;
        }

        // Ignore duplicate TSNs
        if (SctpUtility::uint32Gte($lastReceived, $chunk->getCumulativeTsn())) {
            return;
        }

        $isObsolete = static function (int $tsn) use (&$lastReceived): bool {
            return SctpUtility::uint32Gt($tsn, $lastReceived);
        };

        // Advance cumulative TSN
        $this->lastReceivedTsn = $chunk->getCumulativeTsn();
        $lastReceived = $chunk->getCumulativeTsn();
        $this->sackMisordered = array_filter($this->sackMisordered, $isObsolete);

        // Sort and update misordered TSNs
        sort($this->sackMisordered);
        foreach ($this->sackMisordered as $tsn) {
            if ($tsn === SctpUtility::tsnPlusOne($lastReceived)) {
                $this->lastReceivedTsn = $tsn;
                $lastReceived = $tsn;
            } else {
                break;
            }
        }

        // Remove obsolete entries
        $this->sackDuplicates = array_values(array_filter($this->sackDuplicates, $isObsolete));
        $this->sackMisordered = array_values(array_filter($this->sackMisordered, $isObsolete));

        // Process reassembly
        foreach ($chunk->getStreams() as [$streamId, $streamSeq]) {
            $inboundStream = $this->getInboundStream($streamId);

            // Update sequence number and process messages
            $inboundStream->setSequenceNumber(SctpUtility::uint16Add($streamSeq, 1));
            foreach ($inboundStream->popMessages() as [$streamId, $ppId, $userData]) {
                $this->advertisedRwnd += \strlen($userData);
                $this->receive($streamId, $ppId, $userData);
            }
        }

        // Prune obsolete chunks
        foreach ($this->inboundStreams as $inboundStream) {
            $this->advertisedRwnd += $inboundStream->pruneChunks($lastReceived);
        }
    }

    /**
     * Handle a SACK chunk.
     *
     * @param SackChunk $chunk The SACK chunk.
     */
    public function receiveSackChunk(SackChunk $chunk): void
    {
        if (SctpUtility::uint32Gt($this->lastSackedTsn, $chunk->getCumulativeTsn())) {
            return;
        }

        $this->lastSackedTsn = $chunk->getCumulativeTsn();
        $cwndFullyUtilized = $this->flightSize >= $this->cwnd;

        [$done, $doneBytes] = $this->handleAcknowledgedData();
        $loss = $this->handleGapBlocks($chunk);

        $this->adjustCongestionWindow($done, $doneBytes, $cwndFullyUtilized, $loss, $chunk);
        $this->handleDataChannelTimer($done);
        $this->updateAdvancedPeerAckPoint();

        $this->dataChannelFlush();
        $this->transmit();
    }

    /**
     * Processes acknowledged data chunks up to the last reported SACK TSN.
     *
     * Chunks are dequeued from the sent queue if their TSN is less than or equal to
     * the last acknowledged TSN. Updates RTO if applicable and decreases flight size.
     *
     * @return array{0: int, 1: int} Number of chunks acknowledged and total acknowledged bytes.
     */
    private function handleAcknowledgedData(): array
    {
        $done = 0;
        $doneBytes = 0;

        while (!$this->sentQueue->isEmpty() && SctpUtility::uint32Gte($this->lastSackedTsn, $this->sentQueue->bottom()->getTsn())) {
            $schunk = $this->sentQueue->dequeue();
            $done++;
            if (!$schunk->getAttributes()->acked) {
                $doneBytes += $schunk->getAttributes()->bookSize;
                $this->decreaseFlightSize($schunk);
            }

            // Update RTO estimate
            if ($done === 1 && $schunk->getAttributes()->sentCount === 1) {
                $sentTime = $schunk->getAttributes()->sentTime;
                if ($sentTime !== null) {
                    $this->updateRto(((float) time()) - $sentTime);
                }
            }
        }

        return [$done, $doneBytes];
    }

    /**
     * Handles reported SACK gap blocks and marks missing chunks for retransmission.
     *
     * Chunks marked as missing for three consecutive SACKs are retransmitted unless abandoned.
     *
     * @param SackChunk $chunk The received SACK chunk with gap acknowledgment blocks.
     * @return bool True if any loss was detected, false otherwise.
     */
    private function handleGapBlocks(SackChunk $chunk): bool
    {
        $loss = false;
        $highestSeenTsn = 0;

        if (!empty($chunk->getGaps())) {
            $seen = [];
            foreach ($chunk->getGaps() as $gap) {
                for ($pos = $gap[0]; $pos <= $gap[1]; $pos++) {
                    $highestSeenTsn = ($chunk->getCumulativeTsn() + $pos) % SctpConstant::SCTP_TSN_MODULO;
                    $seen[$highestSeenTsn] = true;
                }
            }

            $highestNewlyAcked = $chunk->getCumulativeTsn();
            foreach ($this->sentQueue as $schunk) {
                if (SctpUtility::uint32Gt($schunk->getTsn(), $highestSeenTsn)) {
                    break;
                }
                if (isset($seen[$schunk->getTsn()]) && !$schunk->getAttributes()->acked) {
                    $schunk->getAttributes()->acked = true;
                    $this->decreaseFlightSize($schunk);
                    $highestNewlyAcked = $schunk->getTsn();
                }
            }

            // Handle missing chunks before HTNA
            foreach ($this->sentQueue as $schunk) {
                if (SctpUtility::uint32Gt($schunk->getTsn(), $highestNewlyAcked)) {
                    break;
                }
                if (!isset($seen[$schunk->getTsn()])) {
                    $schunk->getAttributes()->misses++;
                    if ($schunk->getAttributes()->misses === 3) {
                        $schunk->getAttributes()->misses = 0;
                        if (!$this->maybeAbandon($schunk)) {
                            $schunk->getAttributes()->retransmit = true;
                        }
                        $schunk->getAttributes()->acked = false;
                        $this->decreaseFlightSize($schunk);
                        $loss = true;
                    }
                }
            }
        }
        return $loss;
    }

    /**
     * Adjusts the congestion window (cwnd) based on acknowledgment and loss information.
     *
     * Implements slow start and congestion avoidance algorithms, as well as fast recovery exit.
     *
     * @param int $done Number of chunks acknowledged.
     * @param int $doneBytes Number of bytes acknowledged.
     * @param bool $cwndFullyUtilized Whether cwnd was fully utilized.
     * @param bool $loss Whether loss was detected.
     * @param SackChunk $chunk The current SACK chunk.
     */
    private function adjustCongestionWindow(int $done, int $doneBytes, bool $cwndFullyUtilized, bool $loss, SackChunk $chunk): void
    {
        if ($this->fastRecoveryExit === null) {
            if ($done && $cwndFullyUtilized) {
                if ($this->cwnd <= $this->ssthresh) {
                    // Slow start
                    $this->cwnd += min($doneBytes, SctpConstant::USERDATA_MAX_LENGTH);
                } else {
                    // Congestion avoidance
                    $this->partialBytesAcked += $doneBytes;
                    if ($this->partialBytesAcked >= $this->cwnd) {
                        $this->partialBytesAcked -= $this->cwnd;
                        $this->cwnd += SctpConstant::USERDATA_MAX_LENGTH;
                    }
                }
            }
            if ($loss) {
                $this->ssthresh = max((int) ($this->cwnd / 2), 4 * SctpConstant::USERDATA_MAX_LENGTH);
                $this->cwnd = $this->ssthresh;
                $this->partialBytesAcked = 0;
                $this->fastRecoveryExit = $this->sentQueue->top()->getTsn();
                $this->fastRecoveryTransmit = true;
            }
        } elseif (SctpUtility::uint32Gte($chunk->getCumulativeTsn(), $this->fastRecoveryExit)) {
            $this->fastRecoveryExit = null;
        }
    }

    /**
     * Starts or stops the retransmission timer (T3) based on acknowledgment state.
     *
     * Stops the timer if no data is outstanding. Restart it if new data is acknowledged.
     *
     * @param int $done Number of chunks acknowledged in the latest SACK.
     */
    private function handleDataChannelTimer(int $done): void
    {
        if ($this->sentQueue->isEmpty()) {
            // No outstanding data, stop T3
            $this->dataChannelTaskCancel();
        } elseif ($done) {
            // The earliest outstanding chunk was acknowledged, restart T3
            $this->dataChannelTaskRestart();
        }
    }

    /**
     * Responds to a HEARTBEAT chunk by sending a HEARTBEAT_ACK.
     *
     * @param HeartbeatChunk $chunk The received HEARTBEAT chunk.
     */
    public function receiveHeartbeatChunk(HeartbeatChunk $chunk): void
    {
        $ack = new HeartbeatAckChunk();
        $ack->setParams($chunk->getParams());
        $this->sendChunk($ack);
    }

    /**
     * Handles an ABORT chunk and transitions the connection to CLOSED state.
     *
     * @param AbortChunk $chunk The received ABORT chunk.
     */
    public function receiveAbortChunk(AbortChunk $chunk): void
    {
        $this->log(" The connection was terminated by the remote party.");
        $this->setState(State::CLOSED);
    }

    /**
     * Handles a SHUTDOWN chunk and responds with SHUTDOWN_ACK.
     *
     * Also cancels the shutdown timer and sets the appropriate connection state.
     *
     * @param ShutdownChunk $chunk The received SHUTDOWN chunk.
     */
    public function receiveShutdownChunk(ShutdownChunk $chunk): void
    {
        $this->timer2->cancel();
        $this->setState(State::SHUTDOWN_RECEIVED);
        $ack = new ShutdownAckChunk();
        $this->timer2->start($ack);
        $this->setState(State::SHUTDOWN_ACK_SENT);
        $this->sendChunk($ack);
    }

    /**
     * Finalizes connection shutdown on receipt of SHUTDOWN_COMPLETE chunk.
     *
     * Transitions to CLOSED state if in SHUTDOWN_ACK_SENT state.
     *
     * @param ShutdownCompleteChunk $chunk The received SHUTDOWN_COMPLETE chunk.
     */
    public function receiveShutdownCompleteChunk(ShutdownCompleteChunk $chunk): void
    {
        if ($this->state != State::SHUTDOWN_ACK_SENT) {
            return;
        }
        $this->timer2->cancel();
        $this->setState(State::CLOSED);
    }

    /**
     * Handles a RECONFIG chunk by parsing and applying each parameter.
     *
     * Only processes the chunk if in ESTABLISHED state.
     *
     * @param ReconfigChunk $chunk The received RECONFIG chunk.
     */
    public function receiveReconfigChunk(ReconfigChunk $chunk): void
    {
        if ($this->state != State::ESTABLISHED) {
            return;
        }
        foreach ($chunk->getParams() as $param) {
            $paramType = self::RECONFIG_PARAM_TYPES[$param[0]] ?? null;
            if ($paramType === null) {
                continue;
            }
            $cls = "Webrtc\SCTP\Param\\" . $paramType;
            if (class_exists($cls)) {
                /** @var class-string<StreamParamInterface> $cls */
                $this->receiveReconfigParam($cls::decode($param[1]));
            }
        }
    }

    /**
     * Handles an INIT chunk from a peer to begin a connection establishment.
     *
     * Sets TSN, verification tag, stream limits, and responds with INIT_ACK.
     * Only valid if the local endpoint is acting as server.
     *
     * @param InitChunk $chunk The received INIT chunk.
     */
    public function receiveInitChunk(InitChunk $chunk): void
    {
        if (!$this->isServer()) {
            return;
        }
        $this->lastReceivedTsn = SctpUtility::tsnMinusOne($chunk->getInitialTsn());
        $this->reconfigResponseSeq = SctpUtility::tsnMinusOne($chunk->getInitialTsn());
        $this->remoteVerificationTag = $chunk->getInitiateTag();

        $this->ssthresh = $chunk->getAdvertisedRwnd();
        $this->setExtensions($chunk->getParams());

        $this->log(sprintf(
            "The peer supports %d outbound and %d inbound streams.",
            $chunk->getOutboundStreams(),
            $chunk->getInboundStreams()
        ));
        $this->inboundStreamsCount = min(
            $chunk->getOutboundStreams(),
            $this->inboundStreamsMax
        );
        $this->outboundStreamsCount = min(
            $this->outboundStreamsCount,
            $chunk->getInboundStreams()
        );

        $ack = new InitAckChunk();
        $ack->setInitiateTag($this->localVerificationTag);
        $ack->setAdvertisedRwnd($this->advertisedRwnd);
        $ack->setOutboundStreams($this->outboundStreamsCount);
        $ack->setInboundStreams($this->inboundStreamsMax);
        $ack->setInitialTsn($this->localTsn);
        $ack->addParams($this->getExtensions());

        // Generate state cookie
        $cookie = pack("N", $this->getTime());
        $cookie .= hash_hmac("sha1", $cookie, $this->hmacKey, true);
        $ack->addParams([[SctpConstant::SCTP_STATE_COOKIE, $cookie]]);
        $this->sendChunk($ack);
    }

    /**
     * Validates a COOKIE_ECHO chunk and establishes the association.
     *
     * Verifies the state cookie MAC and lifetime. Responds with COOKIE_ACK.
     * Only valid if the local endpoint is acting as server.
     *
     * @param CookieEchoChunk $chunk The received COOKIE_ECHO chunk.
     */
    public function receiveCookieEchoChunk(CookieEchoChunk $chunk): void
    {
        if (!$this->isServer()) {
            return;
        }
        // Check state cookie MAC
        $cookie = $chunk->getBody();
        if (
            \strlen($cookie) != SctpConstant::COOKIE_LENGTH ||
            hash_hmac("sha1", substr($cookie, 0, 4), $this->hmacKey, true) != substr($cookie, 4)
        ) {
            $this->log("x State cookie is invalid");

            return;
        }

        // Check state cookie lifetime
        $stampData = unpack("N", substr($cookie, 0, 4));
        if ($stampData === false) {
            $this->log("x State cookie is invalid");
            return;
        }
        $stamp = (int) $stampData[1];
        $now = $this->getTime();
        if ($stamp < $now - SctpConstant::COOKIE_LIFETIME || $stamp > $now) {
            $this->log("Cookie has expired");
            $error = new ErrorChunk();
            $error->addParams([[SctpConstant::SCTP_CAUSE_STALE_COOKIE, str_repeat("\x00", 8)]]);
            $this->sendChunk($error);
            return;
        }
        $this->setState(State::ESTABLISHED);
        $ack = new CookieAckChunk();
        $this->sendChunk($ack);
    }

    /**
     * Handles an INIT_ACK chunk and replies with COOKIE_ECHO.
     *
     * Extracts state cookie and stream parameters from the INIT_ACK.
     * Only processed if currently in COOKIE_WAIT state.
     *
     * @param InitAckChunk $chunk The received INIT_ACK chunk.
     */
    public function receiveInitAckChunk(InitAckChunk $chunk): void
    {
        if ($this->state != State::COOKIE_WAIT) {
            return;
        }
        $this->setState(State::COOKIE_ECHOED);

        $this->timer1->cancel();
        $this->lastReceivedTsn = SctpUtility::tsnMinusOne($chunk->getInitialTsn());
        $this->reconfigResponseSeq = SctpUtility::tsnMinusOne($chunk->getInitialTsn());
        $this->remoteVerificationTag = $chunk->getInitiateTag();
        $this->ssthresh = $chunk->getAdvertisedRwnd();
        $this->setExtensions($chunk->getParams());

        $this->log(sprintf(
            "Peer supports %d outbound streams, %d max inbound streams",
            $chunk->getOutboundStreams(),
            $chunk->getInboundStreams()
        ));
        $this->inboundStreamsCount = min(
            $chunk->getOutboundStreams(),
            $this->inboundStreamsMax
        );
        $this->outboundStreamsCount = min(
            $this->outboundStreamsCount,
            $chunk->getInboundStreams()
        );

        $echo = new CookieEchoChunk();
        foreach ($chunk->getParams() as [$k, $v]) {
            if ($k == SctpConstant::SCTP_STATE_COOKIE) {
                $echo->setBody($v);
                break;
            }
        }

        // Start T1 timer and enter COOKIE-ECHOED state
        $this->timer1->start($echo);

        $this->sendChunk($echo);
    }

    /**
     * Handles a COOKIE_ACK chunk and transitions to ESTABLISHED state.
     *
     * Only processed if currently in COOKIE_ECHOED state.
     *
     * @param CookieAckChunk $chunk The received COOKIE_ACK chunk.
     */
    public function receiveCookieAckChunk(CookieAckChunk $chunk): void
    {
        if ($this->state != State::COOKIE_ECHOED) {
            return;
        }
        $this->setState(State::ESTABLISHED);
        $this->timer1->cancel();
    }

    /**
     * Handles an ERROR chunk received during handshake.
     *
     * Closes the connection if in COOKIE_WAIT or COOKIE_ECHOED state.
     *
     * @param ErrorChunk $chunk The received ERROR chunk.
     */
    public function receiveErrorChunk(ErrorChunk $chunk): void
    {
        if (!\in_array($this->state, [State::COOKIE_WAIT, State::COOKIE_ECHOED], true)) {
            return;
        }

        $this->timer1->cancel();
        $this->setState(State::CLOSED);
        $this->log("Could not establish association");
    }

    /**
     * Handle a RE-CONFIG parameter.
     *
     * @param StreamParamInterface $param The RE-CONFIG parameter.
     */
    public function receiveReconfigParam(StreamParamInterface $param): void
    {
        $this->log(sprintf(" received param: %s", $param));

        if ($param instanceof StreamResetOutgoingParam) {
            // Mark closed inbound streams
            foreach ($param->getStreams() as $streamId) {
                unset($this->inboundStreams[$streamId]);
                $this->incomingStreamsReset[$streamId] = true;

                // Close the data channel, resetting our direction of the stream too: the peer waits for it.
                $channel = $this->dataChannels[$streamId] ?? null;
                if ($channel) {
                    $this->dataChannelClose($channel);
                } elseif (!isset($this->outgoingStreamsReset[$streamId]) && !$this->isStreamResetPending($streamId)) {
                    $this->reconfigQueue[] = $streamId;
                    $this->transmitReconfig();
                }
                $this->streamResetProgressed($streamId);
            }

            // Send response
            $responseParam = new StreamResetResponseParam(
                $param->getRequestSequence(),
                1 // Result: success
            );
            $this->reconfigResponseSeq = $param->getRequestSequence();

            $this->sendReconfigParam($responseParam);
        } elseif ($param instanceof StreamAddOutgoingParam) {
            // Increase inbound streams
            $this->inboundStreamsCount += $param->getNewStreams();

            // Send response
            $responseParam = new StreamResetResponseParam(
                $param->getRequestSequence(),
                1 // Result: success
            );
            $this->reconfigResponseSeq = $param->getRequestSequence();

            $this->sendReconfigParam($responseParam);
        } elseif ($param instanceof StreamResetResponseParam) {
            if (
                $this->reconfigRequest instanceof StreamResetOutgoingParam &&
                $param->getResponseSequence() == $this->reconfigRequest->getRequestSequence()
            ) {
                // Mark closed streams
                $streams = $this->reconfigRequest->getStreams();
                $this->reconfigRequest = null;
                foreach ($streams as $streamId) {
                    unset($this->outboundStreamSeq[$streamId]);
                    $this->outgoingStreamsReset[$streamId] = true;
                    $this->streamResetProgressed($streamId);
                }

                $this->transmitReconfig();
            }
        }
    }

    /**
     * Whether a reset of our direction of a stream is queued or awaiting the peer's response.
     */
    private function isStreamResetPending(int $streamId): bool
    {
        return \in_array($streamId, $this->reconfigQueue, true)
            || ($this->reconfigRequest instanceof StreamResetOutgoingParam && \in_array($streamId, $this->reconfigRequest->getStreams(), true));
    }

    /**
     * Closes the data channel of a stream once both of its directions are reset.
     */
    private function streamResetProgressed(int $streamId): void
    {
        if (isset($this->incomingStreamsReset[$streamId], $this->outgoingStreamsReset[$streamId])) {
            unset($this->incomingStreamsReset[$streamId], $this->outgoingStreamsReset[$streamId]);
            $this->dataChannelClosed($streamId);
        }
    }

    /**
     * Send a RE-CONFIG parameter.
     *
     * @param StreamParamInterface $param The RE-CONFIG parameter.
     */
    public function sendReconfigParam(StreamParamInterface $param): void
    {
        $paramType = -1;

        $chunk = new ReconfigChunk();
        foreach (self::RECONFIG_PARAM_TYPES as $k => $cls) {
            if ($param instanceof ("Webrtc\SCTP\Param\\" . $cls)) {
                $paramType = $k;
                break;
            }
        }
        $chunk->addParams([[$paramType, $param->encode()]]);

        $this->log(sprintf(" sent stream param %s", $param));
        $this->sendChunk($chunk);
    }

    /**
     * Build and send a selective acknowledgement (SACK) chunk.
     *
     */
    public function sendSack(): void
    {
        $lastReceived = $this->lastReceivedTsn;
        if ($lastReceived === null) {
            return;
        }

        $gaps = [];
        $gapNext = null;
        foreach ($this->sackMisordered as $tsn) {
            $pos = ($tsn - $lastReceived) % SctpConstant::SCTP_TSN_MODULO;
            if ($tsn == $gapNext) {
                $gaps[\count($gaps) - 1] = [$gaps[\count($gaps) - 1][0], $pos];
            } else {
                $gaps[] = [$pos, $pos];
            }
            $gapNext = SctpUtility::tsnPlusOne($tsn);
        }

        $sack = new SackChunk();
        $sack->setCumulativeTsn($lastReceived);
        $sack->setAdvertisedRwnd(max(0, $this->advertisedRwnd));
        $sack->setDuplicates($this->sackDuplicates);
        $sack->setGaps($gaps);

        $this->sendChunk($sack);

        $this->sackDuplicates = [];
        $this->sackNeeded = false;
    }

    /**
     * Transition the SCTP association to a new state.
     *
     * @param State $state The new state.
     */
    public function setState(State $state): void
    {
        if ($state != $this->state) {
            $this->log(sprintf(" State has changed FROM: %s TO: %s", $this->state->name, $state->name));
            $this->state = $state;
        }

        if ($state == State::ESTABLISHED) {
            foreach ($this->dataChannels as $channel) {
                if ($channel->isNegotiated() && $channel->getReadyState() != DataChannelState::Open) {
                    $channel->setReadyState(DataChannelState::Open);
                }
            }
            $this->dataChannelFlush();
        } elseif ($state == State::CLOSED) {
            $this->timer1->cancel();
            $this->timer2->cancel();
            $this->dataChannelTaskCancel();
            $this->state = State::CLOSED;

            // Close data channels
            foreach ($this->dataChannels as $streamId => $channel) {
                $this->dataChannelClosed($streamId);
            }

            // Release data channel listeners on shutdown.
            /** @var \WeakMap<DataChannelListener, null> */
            $this->dataChannelListeners = new \WeakMap();
        }
    }

    /**
     */
    #[Override]
    public function onErrorOrClosed(): void
    {
        $this->stop();
    }

    private function log(string $message): void
    {
        $this->logger?->debug(sprintf("[RTC_SCTP]: %s", $message));
    }

    public function getTime(): int
    {
        return time();
    }

    /**
     * @return array<string, mixed>
     */
    public function __serialize(): array
    {
        $state = SerializableState::export($this, [
            'dataChannelTask' => $this->dataChannelTask !== null,
            // WeakMap cannot be serialized; snapshot its keys and rebuild on the far side.
            'dataChannelListeners' => ['__uninitialized' => true],
        ]);
        $state['__dataChannelListeners'] = SerializableState::weakMapToList($this->dataChannelListeners);

        return $state;
    }

    /**
     * @param array<string, mixed> $data
     */
    public function __unserialize(array $data): void
    {
        /** @var list<DataChannelListener> $dataChannelListeners */
        $dataChannelListeners = $data['__dataChannelListeners'] ?? [];
        unset($data['__dataChannelListeners']);

        $restart = false;
        /**
         * @var mixed $value
         */
        foreach ($data as $key => $value) {
            if (str_ends_with($key, "\0dataChannelTask")) {
                $restart = $value === true;
                $data[$key] = null;
            }
        }
        SerializableState::import($this, $data);
        /** @var \WeakMap<DataChannelListener, null> */
        $this->dataChannelListeners = SerializableState::listToWeakMap($dataChannelListeners);
        $this->dataChannelTask = null;
        if ($restart) {
            $this->dataChannelTaskStart();
        }
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SCTP\Param;

use Override;
use Webrtc\Exception\InvalidArgumentException;

/**
 * Represents a Stream Add Outgoing Parameter.
 */
readonly final class StreamAddOutgoingParam implements StreamParamInterface
{
    /**
     * StreamAddOutgoingParam constructor.
     *
     * @param int $requestSequence Request sequence number.
     * @param int $newStreams Number of new streams.
     */
    public function __construct(
        private int $requestSequence,
        private int $newStreams
    ) {
    }

    /**
     * Encodes the parameter into a binary string.
     *
     * @return string Binary representation of the parameter.
     */
    #[Override]
    public function encode(): string
    {
        return pack("Nnn", $this->requestSequence, $this->newStreams, 0);
    }

    /**
     * Decodes a binary string into a StreamAddOutgoingParam object.
     *
     * @param string $data Binary string to decode.
     * @return StreamAddOutgoingParam Decoded object.
     */
    #[Override]
    public static function decode(string $data): self
    {
        $unpacked = unpack("NrequestSequence/nnewStreams/nreserved", $data);
        if ($unpacked === false) {
            throw new InvalidArgumentException("Failed to unpack Stream Add Outgoing parameter");
        }
        return new self((int) $unpacked["requestSequence"], (int) $unpacked["newStreams"]);
    }

    public function getNewStreams(): int
    {
        return $this->newStreams;
    }

    public function getRequestSequence(): int
    {
        return $this->requestSequence;
    }

    public function __toString(): string
    {
        return sprintf("StreamAddOutgoingParam requestSequence: %d newStreams: %d", $this->requestSequence, $this->newStreams);
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SCTP\Param;

use Override;
use Webrtc\Exception\InvalidArgumentException;

/**
 * Represents a Stream Reset Response Parameter.
 */
readonly final class StreamResetResponseParam implements StreamParamInterface
{
    /**
     * StreamResetResponseParam constructor.
     *
     * @param int $responseSequence Response sequence number.
     * @param int $result Result code.
     */
    public function __construct(
        private int $responseSequence,
        private int $result
    ) {
    }

    /**
     * Encodes the parameter into a binary string.
     *
     * @return string Binary representation of the parameter.
     */
    #[Override]
    public function encode(): string
    {
        return pack("NN", $this->responseSequence, $this->result);
    }

    /**
     * Decodes a binary string into a StreamResetResponseParam object.
     *
     * @param string $data Binary string to decode.
     * @return StreamResetResponseParam Decoded object.
     */
    #[Override]
    public static function decode(string $data): self
    {
        $unpacked = unpack("NresponseSequence/Nresult", $data);
        if ($unpacked === false) {
            throw new InvalidArgumentException("Failed to unpack Stream Reset Response parameter");
        }
        return new self((int) $unpacked["responseSequence"], (int) $unpacked["result"]);
    }

    public function getResponseSequence(): int
    {
        return $this->responseSequence;
    }

    public function getResult(): int
    {
        return $this->result;
    }

    public function __toString(): string
    {
        return sprintf(
            "StreamResetResponseParam responseSequence: %d result:%d",
            $this->responseSequence,
            $this->result
        );
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SCTP\Param;

use Override;
use Webrtc\Exception\InvalidArgumentException;

/**
 * Represents a Stream Reset Outgoing Parameter.
 */
readonly final class StreamResetOutgoingParam implements StreamParamInterface
{
    /**
     * StreamResetOutgoingParam constructor.
     *
     * @param int $requestSequence Request sequence number.
     * @param int $responseSequence Response sequence number.
     * @param int $lastTsn Last Transmission Sequence Number (TSN).
     * @param array<int> $streams List of streams.
     */
    public function __construct(
        private int   $requestSequence,
        private int   $responseSequence,
        private int   $lastTsn,
        private array $streams = []
    ) {
    }

    /** @return array<int> */
    public function getStreams(): array
    {
        return $this->streams;
    }

    /**
     * Encodes the parameter into a binary string.
     *
     * @return string Binary representation of the parameter.
     */
    #[Override]
    public function encode(): string
    {
        $data = pack("NNN", $this->requestSequence, $this->responseSequence, $this->lastTsn);
        foreach ($this->streams as $stream) {
            $data .= pack("n", $stream);
        }
        return $data;
    }

    /**
     * Decodes a binary string into a StreamResetOutgoingParam object.
     *
     * @param string $data Binary string to decode.
     * @return StreamResetOutgoingParam Decoded object.
     */
    #[Override]
    public static function decode(string $data): self
    {
        $unpacked = unpack("NrequestSequence/NresponseSequence/NlastTsn", substr($data, 0, 12));
        if ($unpacked === false) {
            throw new InvalidArgumentException("Failed to unpack Stream Reset Outgoing parameter");
        }
        $streams = [];
        for ($pos = 12; $pos < \strlen($data); $pos += 2) {
            $stream = unpack("n", substr($data, $pos, 2));
            if ($stream === false) {
                throw new InvalidArgumentException("Failed to unpack Stream Reset Outgoing stream");
            }
            $streams[] = (int) $stream[1];
        }
        return new self(
            (int) $unpacked["requestSequence"],
            (int) $unpacked["responseSequence"],
            (int) $unpacked["lastTsn"],
            $streams
        );
    }

    public function getRequestSequence(): int
    {
        return $this->requestSequence;
    }

    public function getResponseSequence(): int
    {
        return $this->responseSequence;
    }

    public function getLastTsn(): int
    {
        return $this->lastTsn;
    }

    public function __toString(): string
    {
        return sprintf(
            "StreamResetOutgoingParam requestSequence: %d, responseSequence: %d, lastTsn: %d streams: %d",
            $this->requestSequence,
            $this->responseSequence,
            $this->lastTsn,
            \count($this->streams)
        );
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SCTP\Param;

interface StreamParamInterface
{
    public function encode(): string;

    public static function decode(string $data): self;

    public function __toString(): string;
}
<?php declare(strict_types=1);

namespace Webrtc\SCTP;

use Webrtc\DataChannel\RTCSctpTransportInterface;
use Webrtc\ICE\RTCIceTransportInterface;
use Webrtc\Stats\enum\TLSState;

interface RTCSctpDtlsTransportInterface
{
    public function getState(): TLSState;

    public function getIceTransport(): RTCIceTransportInterface;

    public function setSctpReceiver(?RTCSctpTransportInterface $sctpReceiver = null): void;

    public function removeSctpReceiver(RTCSctpTransport $param): void;

    public function sendData(string $data): void;

}
<?php declare(strict_types=1);

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SCTP\Enum;

enum State: int
{
    case CLOSED = 1;
    case COOKIE_WAIT = 2;
    case COOKIE_ECHOED = 3;
    case ESTABLISHED = 4;
    case SHUTDOWN_PENDING = 5;
    case SHUTDOWN_SENT = 6;
    case SHUTDOWN_RECEIVED = 7;
    case SHUTDOWN_ACK_SENT = 8;
    case CONNECTING = 9;
}
{
    "name": "danog/php-rtc-sctp",
    "type": "library",
    "description": "SCTP for PHP WebRTC data channels, ported to Amp v3 fibers and timers. MadelineProto's PHP 8.2+ fork is part of the modular danog/php-rtc stack.",
    "keywords": [
        "webrtc",
        "sctp",
        "datachannel"
    ],
    "homepage": "https://github.com/danog/php-rtc-sctp",
    "license": "BSD-3-Clause",
    "authors": [
        {
            "name": "Amin Yazdanpanah",
            "email": "github@aminyazdanpanah.com"
        },
        {
            "name": "Sana Moniri",
            "email": "s.mo@quasarstream.com"
        },
        {
            "name": "Quasar Stream Community"
        },
        {
            "name": "Daniil Gentili",
            "email": "daniil@daniil.it"
        }
    ],
    "require": {
        "php-64bit": "^8.2",
        "amphp/amp": "^3.1.3",
        "danog/php-rtc-exception": "^1.1.5",
        "danog/php-rtc-sdp": "^1.1.6",
        "danog/php-rtc-stats": "^1.1.3",
        "danog/php-rtc-ice": "^1.1.18 || ^2.0",
        "danog/php-rtc-datachannel": "^1.1.4",
        "danog/php-rtc-mixin": "^2.0",
        "symfony/polyfill-mbstring": "*"
    },
    "require-dev": {
        "phpunit/phpunit": "^11.5 || ^12.0 || ^13.3.2",
        "mockery/mockery": "^1.6.15",
        "symfony/phpunit-bridge": "7.3.x-dev",
        "dg/bypass-finals": "dev-master",
        "bamarni/composer-bin-plugin": "^1.9.1"
    },
    "autoload": {
        "psr-4": {
            "Webrtc\\SCTP\\": "src"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\Webrtc\\SCTP\\": "tests"
        }
    },
    "minimum-stability": "stable",
    "scripts": {
        "post-install-cmd": [
            "@composer bin all install --ansi"
        ],
        "post-update-cmd": [
            "@composer bin all update --ansi"
        ]
    },
    "config": {
        "allow-plugins": {
            "bamarni/composer-bin-plugin": true
        }
    },
    "extra": {
        "bamarni-bin": {
            "bin-links": true,
            "forward-command": true
        }
    }
}
<?php

declare(strict_types=1);

namespace Webrtc\Srtp;

use phpseclib4\Crypt\AES as PhpseclibAes;
use Webrtc\Srtp\Exception\SrtpException;

/**
 * The AES primitives of SRTP (GCM and CTR), using the fastest implementation available.
 *
 * libsodium is used for AES-256-GCM, if the CPU supports it; OpenSSL for everything else; phpseclib if
 * OpenSSL isn't available. Every packet is protected with a different nonce, but with the same few keys:
 * phpseclib ciphers are kept for every key, instead of being set up for every packet.
 *
 * @internal
 */
final class Aes
{
    public const BACKEND_SODIUM = 'sodium';
    public const BACKEND_OPENSSL = 'openssl';
    public const BACKEND_PHPSECLIB = 'phpseclib';

    /** How many phpseclib ciphers to keep, at most: a session uses a few keys at a time. */
    private const MAX_CACHED_CIPHERS = 64;

    /**
     * The backend to use, instead of the fastest available one (if available): for tests.
     *
     * @var self::BACKEND_*|null
     */
    public static ?string $backend = null;

    /** @var array<string, PhpseclibAes> phpseclib ciphers, by mode and key. */
    private static array $ciphers = [];

    private static ?bool $sodiumGcm = null;
    /** @var array<string, bool> Whether OpenSSL supports a cipher, by name. */
    private static array $opensslCiphers = [];

    /**
     * Encrypt with AES-GCM, returning the ciphertext followed by the 16-byte authentication tag.
     *
     * @throws SrtpException If encryption fails.
     */
    public static function gcmEncrypt(string $key, string $nonce, string $aad, string $plaintext): string
    {
        switch (self::backend('gcm', $key)) {
            case self::BACKEND_SODIUM:
                return sodium_crypto_aead_aes256gcm_encrypt($plaintext, $aad, $nonce, $key);
            case self::BACKEND_OPENSSL:
                $tag = '';
                $ciphertext = openssl_encrypt($plaintext, self::opensslName('gcm', $key), $key, OPENSSL_RAW_DATA, $nonce, $tag, $aad, 16);
                if ($ciphertext === false || \strlen($tag) !== 16) {
                    throw new SrtpException('AES-GCM encryption failed!');
                }
                return $ciphertext.$tag;
            default:
                $aes = self::cipher('gcm', $key);
                $aes->setNonce($nonce);
                $aes->setAAD($aad);
                $ciphertext = $aes->encrypt($plaintext);
                return $ciphertext.$aes->getTag();
        }
    }

    /**
     * Decrypt with AES-GCM and verify the 16-byte authentication tag (in constant time).
     *
     * @return string|false The plaintext, or false if authentication fails.
     */
    public static function gcmDecrypt(string $key, string $nonce, string $aad, string $ciphertext, string $tag): string|false
    {
        switch (self::backend('gcm', $key)) {
            case self::BACKEND_SODIUM:
                return sodium_crypto_aead_aes256gcm_decrypt($ciphertext.$tag, $aad, $nonce, $key);
            case self::BACKEND_OPENSSL:
                return openssl_decrypt($ciphertext, self::opensslName('gcm', $key), $key, OPENSSL_RAW_DATA, $nonce, $tag, $aad);
            default:
                // GCM with a 96-bit nonce encrypts from inc32(J0) = nonce || 2. Derive a candidate
                // plaintext without releasing it, then run the authenticated encryption direction
                // and compare its complete result in constant time. This avoids phpseclib's portable
                // GCM decrypt path, whose tag comparison is not constant-time.
                $plain = self::ctr($key, $nonce.pack('N', 2), $ciphertext);
                $verified = self::gcmEncrypt($key, $nonce, $aad, $plain);
                $ciphertextValid = hash_equals(substr($verified, 0, -16), $ciphertext);
                $tagValid = hash_equals(substr($verified, -16), $tag);
                return $ciphertextValid && $tagValid ? $plain : false;
        }
    }

    /**
     * Encrypt (or decrypt) with AES-CTR.
     *
     * @throws SrtpException If encryption fails.
     */
    public static function ctr(string $key, string $iv, string $data): string
    {
        if ($data === '') {
            return '';
        }
        switch (self::backend('ctr', $key)) {
            case self::BACKEND_OPENSSL:
                $result = openssl_encrypt($data, self::opensslName('ctr', $key), $key, OPENSSL_RAW_DATA, $iv);
                if ($result === false) {
                    throw new SrtpException('AES-CTR encryption failed!');
                }
                return $result;
            default:
                $aes = self::cipher('ctr', $key);
                $aes->setIV($iv);
                return $aes->encrypt($data);
        }
    }

    /**
     * @param 'gcm'|'ctr' $mode
     * @return self::BACKEND_*
     */
    private static function backend(string $mode, string $key): string
    {
        $sodium = $mode === 'gcm' && \strlen($key) === 32 && (self::$sodiumGcm ??= \function_exists('sodium_crypto_aead_aes256gcm_is_available') && sodium_crypto_aead_aes256gcm_is_available());
        $openssl = \extension_loaded('openssl') && (self::$opensslCiphers[$name = self::opensslName($mode, $key)] ??= \in_array($name, openssl_get_cipher_methods(), true));
        if (self::$backend !== null) {
            $available = match (self::$backend) {
                self::BACKEND_SODIUM => $sodium,
                self::BACKEND_OPENSSL => $openssl,
                self::BACKEND_PHPSECLIB => true,
            };
            if ($available) {
                return self::$backend;
            }
        }
        if ($sodium) {
            return self::BACKEND_SODIUM;
        }
        return $openssl ? self::BACKEND_OPENSSL : self::BACKEND_PHPSECLIB;
    }

    /** @param 'gcm'|'ctr' $mode */
    private static function opensslName(string $mode, string $key): string
    {
        return 'aes-'.(\strlen($key) * 8).'-'.$mode;
    }

    /** @param 'gcm'|'ctr' $mode */
    private static function cipher(string $mode, string $key): PhpseclibAes
    {
        $id = $mode.$key;
        if (isset(self::$ciphers[$id])) {
            return self::$ciphers[$id];
        }
        if (\count(self::$ciphers) >= self::MAX_CACHED_CIPHERS) {
            self::$ciphers = [];
        }
        $aes = new PhpseclibAes($mode);
        $aes->setKey($key);
        if ($mode === 'ctr') {
            $aes->disablePadding();
        }
        return self::$ciphers[$id] = $aes;
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Srtp\Exception;

use Exception;

class SrtpException extends Exception implements SrtpExceptionInterface
{

}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Srtp\Exception;

use Throwable;

final class SrtpValidateException extends SrtpException
{
    /**
     * The libsrtp status codes, kept so that a numeric status can still be rendered.
     *
     * Nothing produces these any more now that SRTP is implemented in PHP rather than bound
     * to libsrtp, but fromStatus() remains for callers translating a foreign status code.
     */
    private const ERRORS = [
        "nothing to report",
        "unspecified failure",
        "unsupported parameter",
        "couldn't allocate memory",
        "couldn't deallocate properly",
        "couldn't initialize",
        "can't process as much data as requested",
        "authentication failure",
        "cipher failure",
        "replay check failed (bad index)",
        "replay check failed (index too old)",
        "algorithm failed test routine",
        "unsupported operation",
        "no appropriate context found",
        "unable to perform desired validation",
        "can't use key any more",
        "error in use of socket",
        "error in use POSIX signals",
        "nonce check failed",
        "couldn't read data",
        "couldn't write data",
        "error parsing data",
        "error encoding data",
        "error while using semaphores",
        "error while using pfkey",
        "error MKI present in packet is invalid",
        "packet index is too old to consider",
        "packet index advanced, reset needed",
    ];

    /**
     * Build a validation failure from a description of what was wrong.
     *
     * This used to take a libsrtp status code, a signature left over from the days when the
     * package bound to that library. Every caller passes a message, so each of them raised a
     * TypeError instead of the exception the API documents.
     */
    public function __construct(string $message = '', int $code = 0, ?Throwable $previous = null)
    {
        parent::__construct($message, $code, $previous);
    }

    /**
     * Build a validation failure from a libsrtp status code.
     */
    public static function fromStatus(int $status): self
    {
        return new self(self::ERRORS[$status] ?? 'Unknown Error!', $status);
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Srtp\Exception;

use Webrtc\Exception\WebrtcExceptionInterface;

interface SrtpExceptionInterface extends WebrtcExceptionInterface
{

}<?php

/**
 * This file is part of the PHP WebRTC package, vendored and modified for MadelineProto.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Srtp;

use Throwable;
use Webrtc\Srtp\Enum\SsrcType;
use Webrtc\Srtp\Exception\SrtpException;
use Webrtc\Srtp\Exception\SrtpValidateException;

/** A pure-PHP SRTP/SRTCP session implementing RFC 3711 and RFC 7714. */
final class Session
{
    private const LABEL_RTP_ENCRYPTION = 0x00;
    private const LABEL_RTP_AUTH = 0x01;
    private const LABEL_RTP_SALT = 0x02;
    private const LABEL_RTCP_ENCRYPTION = 0x03;
    private const LABEL_RTCP_AUTH = 0x04;
    private const LABEL_RTCP_SALT = 0x05;
    private const AUTH_KEY_LENGTH = 20;
    private const SEQ_MODULO = 0x10000;
    private const SEQ_MEDIAN = 0x8000;
    private const MAX_ROC = 0xFFFFFFFF;
    private const MAX_SRTCP_INDEX = 0x7FFFFFFF;
    private const MAX_SRTP_INVOCATIONS = 0x1000000000000;
    private const MAX_SRTCP_INVOCATIONS = 0x80000000;
    private const MAX_AES_CM_BYTES = 0x100000;
    private const MAX_GCM_PLAINTEXT_BYTES = 0xFFFFFFFE0;

    private bool $ready = false;
    private bool $aead = false;
    private bool $allowRepeatTx = false;
    private int $windowSize = 1024;
    private int $rtpTagLength = 10;
    private int $rtcpTagLength = 10;
    private int $keyGeneration = 0;
    private int $rtpKeyInvocations = 0;
    private int $rtcpKeyInvocations = 0;
    private SsrcType $ssrcType = SsrcType::UNDEFINED;
    private int $ssrcValue = 0;

    private string $rtpKey = '';
    private string $rtpSalt = '';
    private string $rtpAuthKey = '';
    private string $rtcpKey = '';
    private string $rtcpSalt = '';
    private string $rtcpAuthKey = '';

    /**
     * The packet indexes used so far for each SSRC, within the replay window.
     *
     * Every used index is mapped to the key generation it was used with, followed by the SHA-256
     * of the packet that was protected with it: an index is never used for a different packet, and
     * an identical retransmission (if allowed) is just protected again, which gives the same result.
     * The packets themselves are not kept, as the window spans more than a thousand of them.
     *
     * @var array<int, array{highestIndex: int, sent: array<int, string>, pruned?: ?int}>
     */
    private array $rtpSendState = [];

    /** @var array<int, array{highestIndex: int, received: array<int, true>, pruned?: ?int}> */
    private array $rtpReceiveState = [];

    /** @var array<int, int> Last outgoing SRTCP index for each SSRC. */
    private array $rtcpIndex = [];

    /** @var array<int, array{generation: int, invocations: int}> */
    private array $rtcpKeyUsage = [];

    /** @var array<int, array{highestIndex: int, received: array<int, true>, pruned?: ?int}> */
    private array $rtcpReceiveState = [];

    /** @var array<int, true> SSRCs removed while the current master key was active. */
    private array $retiredSsrc = [];

    private ?string $masterKeyId = null;

    /** @throws SrtpValidateException If the policy is not usable. */
    public function __construct(?Policy $policy = null)
    {
        if (PHP_INT_SIZE < 8) {
            throw new SrtpValidateException('SRTP requires a 64-bit PHP build for 48-bit packet indices.');
        }
        if ($policy !== null) {
            $this->addStream($policy);
        }
    }

    /**
     * Install or re-key the cryptographic context.
     *
     * Packet indices deliberately survive a re-key, as required by RFC 3711 section 3.3.1.
     * Derived material is computed before the live context is changed, so a primitive failure
     * cannot leave a half-installed policy behind.
     *
     * @throws SrtpValidateException If the policy is unusable or key derivation fails.
     */
    public function addStream(Policy $policy): void
    {
        $material = $policy->getKey();
        if ($material === null) {
            throw new SrtpValidateException('The SRTP policy has no key!');
        }

        try {
            $profile = $policy->getSrtpProfile();
            $aead = Policy::isAead($profile);
            $rtpTagLength = Policy::rtpTagLength($profile);
            $rtcpTagLength = Policy::rtcpTagLength($profile);
            $masterKey = $policy->getMasterKey();
            $masterSalt = $policy->getMasterSalt();
            $keyLength = \strlen($masterKey);
            $saltLength = \strlen($masterSalt);

            $rtpKey = self::derive($masterKey, $masterSalt, self::LABEL_RTP_ENCRYPTION, $keyLength);
            $rtpSalt = self::derive($masterKey, $masterSalt, self::LABEL_RTP_SALT, $saltLength);
            $rtcpKey = self::derive($masterKey, $masterSalt, self::LABEL_RTCP_ENCRYPTION, $keyLength);
            $rtcpSalt = self::derive($masterKey, $masterSalt, self::LABEL_RTCP_SALT, $saltLength);
            $rtpAuthKey = $aead
                ? ''
                : self::derive($masterKey, $masterSalt, self::LABEL_RTP_AUTH, self::AUTH_KEY_LENGTH);
            $rtcpAuthKey = $aead
                ? ''
                : self::derive($masterKey, $masterSalt, self::LABEL_RTCP_AUTH, self::AUTH_KEY_LENGTH);
            $keyId = hash('sha256', $profile->value."\0".$material, true);
            if (\strlen($keyId) !== 32) {
                throw new SrtpValidateException('Could not identify the installed SRTP master key.');
            }
        } catch (SrtpValidateException $e) {
            throw $e;
        } catch (Throwable $e) {
            throw new SrtpValidateException('Could not derive the SRTP session keys.', 0, $e);
        }

        $keyChanged = $this->masterKeyId === null || !hash_equals($this->masterKeyId, $keyId);
        if ($this->masterKeyId !== null && $keyChanged) {
            // A new master key starts a new SSRC-allocation lifetime. Packet indices remain.
            $this->retiredSsrc = [];
            foreach ($this->rtpSendState as &$state) {
                $state['sent'] = [];
            }
            unset($state);
            foreach ($this->rtpReceiveState as &$state) {
                $state['received'] = [];
            }
            unset($state);
            // Old SRTCP packets cannot authenticate under the new key. Starting a fresh replay
            // list also permits the explicit 31-bit index to wrap safely after a re-key.
            $this->rtcpReceiveState = [];
        }
        if ($keyChanged) {
            $this->rtpKeyInvocations = 0;
            $this->rtcpKeyInvocations = 0;
        }

        $this->aead = $aead;
        $this->allowRepeatTx = $policy->getAllowRepeatTx();
        $this->windowSize = $policy->getWindowSize();
        $this->rtpTagLength = $rtpTagLength;
        $this->rtcpTagLength = $rtcpTagLength;
        $this->rtpKey = $rtpKey;
        $this->rtpSalt = $rtpSalt;
        $this->rtpAuthKey = $rtpAuthKey;
        $this->rtcpKey = $rtcpKey;
        $this->rtcpSalt = $rtcpSalt;
        $this->rtcpAuthKey = $rtcpAuthKey;
        $this->ssrcType = $policy->getSsrcType();
        $this->ssrcValue = $policy->getSsrcValue();
        $this->masterKeyId = $keyId;
        if ($keyChanged) {
            $this->keyGeneration++;
        }
        $this->ready = true;
    }

    /**
     * Retire an SSRC and forget its inbound replay state.
     *
     * Outbound indices are retained to prevent AES-CTR keystream or AES-GCM nonce reuse. The SSRC
     * cannot be accepted or issued again until a new master key is installed.
     *
     * @throws SrtpValidateException If the SSRC is not an unsigned 32-bit integer.
     */
    public function removeStream(int|string $ssrc): void
    {
        $ssrc = self::normalizeSsrc($ssrc);
        unset($this->rtpReceiveState[$ssrc], $this->rtcpReceiveState[$ssrc]);
        // Re-issuing an SSRC under the same key can repeat an RTP/SRTCP nonce. Retiring even an
        // as-yet unseen SSRC also makes an administrative removal effective against later input.
        $this->retiredSsrc[$ssrc] = true;
    }

    /** RFC 3711 AES-CM PRF with key_derivation_rate equal to zero. */
    private static function derive(string $masterKey, string $masterSalt, int $label, int $length): string
    {
        $x = str_pad($masterSalt, 16, "\0");
        $x[7] = \chr(\ord($x[7]) ^ $label);
        return self::keystream($masterKey, $x, $length);
    }

    /** Produce AES counter-mode keystream. */
    private static function keystream(string $key, string $iv, int $length): string
    {
        if ($length > self::MAX_AES_CM_BYTES) {
            throw new SrtpException('AES-CM cannot safely process more than 2^16 blocks per packet.');
        }
        if ($length === 0) {
            return '';
        }
        $result = Aes::ctr($key, $iv, str_repeat("\0", $length));
        if (\strlen($result) < $length) {
            throw new SrtpException('AES-CTR returned a truncated keystream.');
        }
        return substr($result, 0, $length);
    }

    /** RFC 3711 section 4.1.1 AES-CM IV. */
    private static function iv(string $salt, int $ssrc, int $index): string
    {
        $mix = str_repeat("\0", 4).pack('N', $ssrc)
            .pack('N', ($index >> 16) & 0xFFFFFFFF).pack('n', $index & 0xFFFF)."\0\0";
        return str_pad($salt, 16, "\0") ^ $mix;
    }

    /** RFC 7714 SRTP/SRTCP nonce. A 31-bit SRTCP index has two leading zero octets. */
    private static function aeadNonce(string $salt, int $ssrc, int $index): string
    {
        $mix = "\0\0".pack('N', $ssrc)
            .pack('N', ($index >> 16) & 0xFFFFFFFF).pack('n', $index & 0xFFFF);
        return $salt ^ $mix;
    }

    /** @throws SrtpException If the packet has an invalid or truncated RTP header. */
    private static function rtpHeaderLength(string $packet, int $trailerLength = 0): int
    {
        $packetLength = \strlen($packet) - $trailerLength;
        if ($packetLength < 12) {
            throw new SrtpException('RTP packet is too short!');
        }
        $first = \ord($packet[0]);
        if (($first >> 6) !== 2) {
            throw new SrtpException('Unsupported RTP version!');
        }
        $length = 12 + 4 * ($first & 0x0F);
        if ($length > $packetLength) {
            throw new SrtpException('Truncated RTP header!');
        }
        if (($first & 0x10) !== 0) {
            if ($length + 4 > $packetLength) {
                throw new SrtpException('Truncated RTP header extension!');
            }
            $words = unpack('nlength', substr($packet, $length + 2, 2));
            if ($words === false) {
                throw new SrtpException('Could not parse the RTP header extension!');
            }
            $length += 4 + 4 * (int) $words['length'];
            if ($length > $packetLength) {
                throw new SrtpException('Truncated RTP header extension!');
            }
        }
        return $length;
    }

    /** @throws SrtpException If RTP padding is malformed. */
    private static function validateRtpPadding(string $packet, int $headerLength): void
    {
        if ((\ord($packet[0]) & 0x20) === 0) {
            return;
        }
        $payloadLength = \strlen($packet) - $headerLength;
        if ($payloadLength === 0) {
            throw new SrtpException('RTP padding flag is set on an empty payload!');
        }
        $paddingLength = \ord($packet[\strlen($packet) - 1]);
        if ($paddingLength === 0 || $paddingLength > $payloadLength) {
            throw new SrtpException('Invalid RTP padding length!');
        }
    }

    /** @throws SrtpException If the packet cannot be a valid RTCP packet. */
    private static function validateRtcpEnvelope(string $packet, int $plainLength): void
    {
        if ($plainLength < 8 || $plainLength > \strlen($packet)) {
            throw new SrtpException('RTCP packet is too short!');
        }
        if ($plainLength % 4 !== 0) {
            throw new SrtpException('RTCP packet is not 32-bit aligned!');
        }
        if ((\ord($packet[0]) >> 6) !== 2) {
            throw new SrtpException('Unsupported RTCP version!');
        }
    }

    /**
     * Validate the part of RTCP framing needed by SRTCP itself.
     *
     * The RTCP length describes only the first member of a compound packet and is deliberately
     * not used to locate the SRTCP trailer (RFC 3711 section 3.4). Full RTCP semantic validation
     * belongs to the RTCP parser after cryptographic processing.
     */
    private static function validateRtcpPacket(string $packet): void
    {
        self::validateRtcpEnvelope($packet, \strlen($packet));
    }

    /** @throws SrtpException If the packet cannot be protected. */
    public function protect(string $packet): string
    {
        $this->assertReady();
        try {
            $headerLength = self::rtpHeaderLength($packet);
            self::validateRtpPadding($packet, $headerLength);
            $fields = self::rtpFields($packet);
            $ssrc = $fields['ssrc'];
            $this->assertSsrcAllowed($ssrc);

            [$roc, $index] = $this->reserveOutgoingRtp($ssrc, $fields['seq'], $packet);

            $header = substr($packet, 0, $headerLength);
            $payload = substr($packet, $headerLength);
            if ($this->aead) {
                $result = $header.$this->encryptGcm(
                    $this->rtpKey,
                    self::aeadNonce($this->rtpSalt, $ssrc, $index),
                    $header,
                    $payload,
                    'SRTP'
                );
            } else {
                $encrypted = $payload === ''
                    ? ''
                    : $payload ^ self::keystream(
                        $this->rtpKey,
                        self::iv($this->rtpSalt, $ssrc, $index),
                        \strlen($payload)
                    );
                $authenticated = $header.$encrypted;
                $tag = substr(self::hmacSha1(
                    $authenticated.pack('N', $roc),
                    $this->rtpAuthKey
                ), 0, $this->rtpTagLength);
                $result = $authenticated.$tag;
            }
            return $result;
        } catch (SrtpException $e) {
            throw $e;
        } catch (Throwable $e) {
            throw new SrtpException('Could not protect the RTP packet.', 0, $e);
        }
    }

    /** @throws SrtpException If authentication fails or the packet is malformed/replayed. */
    public function unprotect(string $packet): string
    {
        $this->assertReady();
        try {
            $headerLength = self::rtpHeaderLength($packet, $this->rtpTagLength);
            $fields = self::rtpFields($packet);
            $this->assertSsrcAllowed($fields['ssrc']);
            [$roc, $index] = $this->estimateIncomingRtp($fields['ssrc'], $fields['seq']);
            $this->checkReplay($this->rtpReceiveState[$fields['ssrc']] ?? null, $index, 'SRTP');

            $header = substr($packet, 0, $headerLength);
            if ($this->aead) {
                $body = substr($packet, $headerLength, -$this->rtpTagLength);
                $tag = substr($packet, -$this->rtpTagLength);
                $payload = $this->decryptGcm(
                    $this->rtpKey,
                    self::aeadNonce($this->rtpSalt, $fields['ssrc'], $index),
                    $header,
                    $body,
                    $tag,
                    'SRTP'
                );
            } else {
                $authenticated = substr($packet, 0, -$this->rtpTagLength);
                $tag = substr($packet, -$this->rtpTagLength);
                $expected = substr(self::hmacSha1(
                    $authenticated.pack('N', $roc),
                    $this->rtpAuthKey
                ), 0, $this->rtpTagLength);
                if (!hash_equals($expected, $tag)) {
                    throw new SrtpException('SRTP authentication failed!');
                }
                $encrypted = substr($authenticated, $headerLength);
                $payload = $encrypted === ''
                    ? ''
                    : $encrypted ^ self::keystream(
                        $this->rtpKey,
                        self::iv($this->rtpSalt, $fields['ssrc'], $index),
                        \strlen($encrypted)
                    );
            }

            $plain = $header.$payload;
            self::validateRtpPadding($plain, $headerLength);
            $this->acceptReplay($this->rtpReceiveState, $fields['ssrc'], $index);
            return $plain;
        } catch (SrtpException $e) {
            throw $e;
        } catch (Throwable $e) {
            throw new SrtpException('Could not unprotect the SRTP packet.', 0, $e);
        }
    }

    /** @throws SrtpException If the packet cannot be protected. */
    public function protectRtcp(string $packet): string
    {
        $this->assertReady();
        try {
            self::validateRtcpPacket($packet);
            $ssrc = self::rtcpSsrc($packet);
            $this->assertSsrcAllowed($ssrc);
            if ($this->rtcpKeyInvocations >= self::MAX_SRTCP_INVOCATIONS) {
                throw new SrtpException('The SRTCP packet limit for this key has been reached; re-keying is required.');
            }
            // Consume the invocation before touching the primitive so a failed operation cannot
            // be retried in a way that accidentally reuses partially consumed cryptographic state.
            $this->rtcpKeyInvocations++;
            $usage = $this->rtcpKeyUsage[$ssrc] ?? null;
            if ($usage === null || $usage['generation'] !== $this->keyGeneration) {
                $usage = ['generation' => $this->keyGeneration, 'invocations' => 0];
            }
            if ($usage['invocations'] >= self::MAX_SRTCP_INDEX + 1) {
                throw new SrtpException('The SRTCP packet limit for this key has been reached; re-keying is required.');
            }
            $previous = $this->rtcpIndex[$ssrc] ?? 0;
            // libSRTP and pion start at one. The explicit index is modulo 2^31, while the
            // per-key invocation counter prevents any nonce from recurring under one key.
            $index = ($previous + 1) & self::MAX_SRTCP_INDEX;
            $this->rtcpIndex[$ssrc] = $index;
            $usage['invocations']++;
            $this->rtcpKeyUsage[$ssrc] = $usage;
            $indexWord = pack('N', $index | 0x80000000);
            $header = substr($packet, 0, 8);
            $payload = substr($packet, 8);

            if ($this->aead) {
                return $header.$this->encryptGcm(
                    $this->rtcpKey,
                    self::aeadNonce($this->rtcpSalt, $ssrc, $index),
                    $header.$indexWord,
                    $payload,
                    'SRTCP'
                ).$indexWord;
            }

            $encrypted = $payload === ''
                ? ''
                : $payload ^ self::keystream(
                    $this->rtcpKey,
                    self::iv($this->rtcpSalt, $ssrc, $index),
                    \strlen($payload)
                );
            $authenticated = $header.$encrypted.$indexWord;
            $tag = substr(self::hmacSha1($authenticated, $this->rtcpAuthKey), 0, $this->rtcpTagLength);
            return $authenticated.$tag;
        } catch (SrtpException $e) {
            throw $e;
        } catch (Throwable $e) {
            throw new SrtpException('Could not protect the RTCP packet.', 0, $e);
        }
    }

    /** @throws SrtpException If authentication fails or the packet is malformed/replayed. */
    public function unprotectRtcp(string $packet): string
    {
        $this->assertReady();
        try {
            $minimum = 8 + 4 + $this->rtcpTagLength;
            if (\strlen($packet) < $minimum) {
                throw new SrtpException('SRTCP packet is too short!');
            }
            $plainLength = \strlen($packet) - 4 - $this->rtcpTagLength;
            self::validateRtcpEnvelope($packet, $plainLength);
            $header = substr($packet, 0, 8);
            $ssrc = self::rtcpSsrc($packet);
            $this->assertSsrcAllowed($ssrc);

            if ($this->aead) {
                $indexWord = substr($packet, -4);
                $tag = substr($packet, -(4 + $this->rtcpTagLength), $this->rtcpTagLength);
                $body = substr($packet, 8, -(4 + $this->rtcpTagLength));
            } else {
                $tag = substr($packet, -$this->rtcpTagLength);
                $authenticated = substr($packet, 0, -$this->rtcpTagLength);
                $expected = substr(
                    self::hmacSha1($authenticated, $this->rtcpAuthKey),
                    0,
                    $this->rtcpTagLength
                );
                if (!hash_equals($expected, $tag)) {
                    throw new SrtpException('SRTCP authentication failed!');
                }
                $indexWord = substr($authenticated, -4);
                $body = substr($authenticated, 8, -4);
            }

            $unpacked = unpack('Nindex', $indexWord);
            if ($unpacked === false) {
                throw new SrtpException('Could not parse the SRTCP index!');
            }
            $rawIndex = (int) $unpacked['index'];
            $encrypted = ($rawIndex & 0x80000000) !== 0;
            $index = $rawIndex & self::MAX_SRTCP_INDEX;

            if (!$this->aead) {
                $this->checkReplay($this->rtcpReceiveState[$ssrc] ?? null, $index, 'SRTCP');
                $payload = !$encrypted || $body === ''
                    ? $body
                    : $body ^ self::keystream(
                        $this->rtcpKey,
                        self::iv($this->rtcpSalt, $ssrc, $index),
                        \strlen($body)
                    );
            } elseif ($encrypted) {
                $payload = $this->decryptGcm(
                    $this->rtcpKey,
                    self::aeadNonce($this->rtcpSalt, $ssrc, $index),
                    $header.$indexWord,
                    $body,
                    $tag,
                    'SRTCP'
                );
                // RFC 7714 requires AEAD validation before any replay-state action.
                $this->checkReplay($this->rtcpReceiveState[$ssrc] ?? null, $index, 'SRTCP');
            } else {
                // With E=0 the RTCP packet and ESRTCP word are AAD; the cipher is only a tag.
                $empty = $this->decryptGcm(
                    $this->rtcpKey,
                    self::aeadNonce($this->rtcpSalt, $ssrc, $index),
                    $header.$body.$indexWord,
                    '',
                    $tag,
                    'SRTCP'
                );
                if ($empty !== '') {
                    throw new SrtpException('Invalid authentication-only SRTCP ciphertext!');
                }
                $this->checkReplay($this->rtcpReceiveState[$ssrc] ?? null, $index, 'SRTCP');
                $payload = $body;
            }

            $plain = $header.$payload;
            self::validateRtcpPacket($plain);
            $this->acceptReplay($this->rtcpReceiveState, $ssrc, $index);
            return $plain;
        } catch (SrtpException $e) {
            throw $e;
        } catch (Throwable $e) {
            throw new SrtpException('Could not unprotect the SRTCP packet.', 0, $e);
        }
    }

    /** @return array{seq: int, ssrc: int} */
    private static function rtpFields(string $packet): array
    {
        $fields = unpack('nseq/Ntimestamp/Nssrc', substr($packet, 2, 10));
        if ($fields === false) {
            throw new SrtpException('Could not parse the RTP header!');
        }
        return ['seq' => (int) $fields['seq'], 'ssrc' => (int) $fields['ssrc']];
    }

    private static function rtcpSsrc(string $packet): int
    {
        $field = unpack('Nssrc', substr($packet, 4, 4));
        if ($field === false) {
            throw new SrtpException('Could not parse the RTCP SSRC!');
        }
        return (int) $field['ssrc'];
    }

    /**
     * Estimate an index relative to the greatest accepted index, following RFC 3711 Appendix A
     * with the initial-ROC guard used by interoperable implementations.
     *
     * @return array{roc: int, index: int, difference: int}
     */
    private static function estimateIndex(int $highestIndex, int $seq): array
    {
        $localRoc = intdiv($highestIndex, self::SEQ_MODULO);
        $localSeq = $highestIndex & 0xFFFF;
        $roc = $localRoc;
        $difference = $seq - $localSeq;

        if ($highestIndex > self::SEQ_MEDIAN) {
            if ($localSeq < self::SEQ_MEDIAN && $seq - $localSeq > self::SEQ_MEDIAN) {
                $roc = $localRoc - 1;
                $difference -= self::SEQ_MODULO;
            } elseif ($localSeq >= self::SEQ_MEDIAN && $localSeq - self::SEQ_MEDIAN > $seq) {
                if ($localRoc === self::MAX_ROC) {
                    throw new SrtpException('The SRTP packet limit for this key has been reached; re-keying is required.');
                }
                $roc = $localRoc + 1;
                $difference += self::SEQ_MODULO;
            }
        }

        return ['roc' => $roc, 'index' => $highestIndex + $difference, 'difference' => $difference];
    }

    /** @return array{int, int} ROC and packet index. */
    private function reserveOutgoingRtp(int $ssrc, int $seq, string $packet): array
    {
        if ($this->rtpKeyInvocations >= self::MAX_SRTP_INVOCATIONS) {
            throw new SrtpException('The SRTP packet limit for this key has been reached; re-keying is required.');
        }
        $this->rtpKeyInvocations++;
        if (!isset($this->rtpSendState[$ssrc])) {
            $index = $seq;
            $this->rtpSendState[$ssrc] = ['highestIndex' => $index, 'sent' => [], 'pruned' => $index];
            $estimate = ['roc' => 0, 'index' => $index, 'difference' => 0];
        } else {
            $estimate = self::estimateIndex($this->rtpSendState[$ssrc]['highestIndex'], $seq);
        }

        /** @psalm-suppress UnsupportedPropertyReferenceUsage Psalm cannot model a reference into an array property; the alias is local and correct. */
        $state = &$this->rtpSendState[$ssrc];
        $index = $estimate['index'];
        $used = pack('N', $this->keyGeneration).hash('sha256', $packet, true);
        if (isset($state['sent'][$index])) {
            if ($this->allowRepeatTx && hash_equals($state['sent'][$index], $used)) {
                // The same packet with the same key and index: protecting it again gives the same result.
                return [$estimate['roc'], $index];
            }
            throw new SrtpException('Refusing to reuse an SRTP packet index!');
        }
        if ($state['highestIndex'] - $index >= $this->windowSize) {
            throw new SrtpException('Outgoing SRTP packet index is too old to use safely!');
        }

        if ($estimate['difference'] > 0) {
            $state['highestIndex'] = $index;
        }
        // Reserve before invoking a primitive.
        $state['sent'][$index] = $used;
        // Not saved by older versions.
        $state['pruned'] ??= null;
        $this->pruneWindow($state['sent'], $state['highestIndex'], $state['pruned']);
        return [$estimate['roc'], $index];
    }

    /** @return array{int, int} ROC and packet index. */
    private function estimateIncomingRtp(int $ssrc, int $seq): array
    {
        if (!isset($this->rtpReceiveState[$ssrc])) {
            return [0, $seq];
        }
        $estimate = self::estimateIndex($this->rtpReceiveState[$ssrc]['highestIndex'], $seq);
        return [$estimate['roc'], $estimate['index']];
    }

    /** @param array{highestIndex: int, received: array<int, true>, pruned?: ?int}|null $state */
    private function checkReplay(?array $state, int $index, string $protocol): void
    {
        if ($state === null) {
            return;
        }
        if ($state['highestIndex'] - $index >= $this->windowSize) {
            throw new SrtpException("$protocol packet is too old!");
        }
        if (isset($state['received'][$index])) {
            throw new SrtpException("Replayed $protocol packet!");
        }
    }

    /** @param array<int, array{highestIndex: int, received: array<int, true>, pruned?: ?int}> $states */
    private function acceptReplay(array &$states, int $ssrc, int $index): void
    {
        if (!isset($states[$ssrc])) {
            $states[$ssrc] = ['highestIndex' => $index, 'received' => [$index => true], 'pruned' => $index];
            return;
        }
        $state = &$states[$ssrc];
        if ($index > $state['highestIndex']) {
            $state['highestIndex'] = $index;
        }
        $state['received'][$index] = true;
        // Not saved by older versions.
        $state['pruned'] ??= null;
        $this->pruneWindow($state['received'], $state['highestIndex'], $state['pruned']);
    }

    /**
     * Forget the entries that left the window.
     *
     * The window only moves forward, and no entry is ever added before it: the indexes that left it
     * since it was last pruned are removed directly. (Iterating over the entries, or looking for the
     * first one, would go through the whole window for every packet.)
     *
     * @param array<int, mixed> $entries
     * @param ?int              $pruned  No entry is older than this index (null if unknown, for windows saved by older versions).
     */
    private function pruneWindow(array &$entries, int $highestIndex, ?int &$pruned): void
    {
        $oldest = $highestIndex - $this->windowSize + 1;
        $pruned ??= array_key_first($entries) ?? $oldest;
        if ($pruned >= $oldest) {
            return;
        }
        if ($oldest - $pruned > \count($entries)) {
            // The window moved further than there are entries.
            foreach (array_keys($entries) as $index) {
                if ($index < $oldest) {
                    unset($entries[$index]);
                }
            }
        } else {
            for ($index = $pruned; $index < $oldest; $index++) {
                unset($entries[$index]);
            }
        }
        $pruned = $oldest;
    }

    private function decryptGcm(
        string $key,
        string $nonce,
        string $aad,
        string $ciphertext,
        string $tag,
        string $protocol
    ): string {
        if (\strlen($ciphertext) > self::MAX_GCM_PLAINTEXT_BYTES) {
            throw new SrtpException("$protocol ciphertext exceeds the AES-GCM per-invocation limit!");
        }
        if (\strlen($tag) !== 16) {
            throw new SrtpException("$protocol authentication tag has an invalid length!");
        }
        try {
            $plain = Aes::gcmDecrypt($key, $nonce, $aad, $ciphertext, $tag);
        } catch (Throwable $e) {
            if ($e instanceof SrtpException) {
                throw $e;
            }
            throw new SrtpException("$protocol authentication failed!", 0, $e);
        }
        if ($plain === false) {
            throw new SrtpException("$protocol authentication failed!");
        }
        return $plain;
    }

    /** Return ciphertext followed by its full RFC 7714 authentication tag. */
    private function encryptGcm(
        string $key,
        string $nonce,
        string $aad,
        string $plaintext,
        string $protocol
    ): string {
        if (\strlen($plaintext) > self::MAX_GCM_PLAINTEXT_BYTES) {
            throw new SrtpException("$protocol plaintext exceeds the AES-GCM per-invocation limit!");
        }
        try {
            $result = Aes::gcmEncrypt($key, $nonce, $aad, $plaintext);
        } catch (Throwable $e) {
            throw new SrtpException("$protocol encryption failed!", 0, $e);
        }
        if (\strlen($result) !== \strlen($plaintext) + 16) {
            throw new SrtpException("$protocol encryption returned an invalid result!");
        }
        return $result;
    }

    /** Return a full binary HMAC-SHA1 result or fail closed. */
    private static function hmacSha1(string $message, string $key): string
    {
        $result = hash_hmac('sha1', $message, $key, true);
        if (\strlen($result) !== self::AUTH_KEY_LENGTH) {
            throw new SrtpException('HMAC-SHA1 returned an invalid result!');
        }
        return $result;
    }

    /** @throws SrtpException If a specific-SSRC policy does not match the packet. */
    private function assertSsrcAllowed(int $ssrc): void
    {
        if (isset($this->retiredSsrc[$ssrc])) {
            throw new SrtpException('The packet SSRC was removed and cannot be reused with the same key!');
        }
        if ($this->ssrcType === SsrcType::SPECIFIC && $ssrc !== $this->ssrcValue) {
            throw new SrtpException('The packet SSRC does not match the installed SRTP policy!');
        }
    }

    /** @throws SrtpValidateException If the SSRC is not an unsigned decimal 32-bit value. */
    private static function normalizeSsrc(int|string $ssrc): int
    {
        if (\is_int($ssrc)) {
            if ($ssrc < 0 || $ssrc > 0xFFFFFFFF) {
                throw new SrtpValidateException('The SSRC must be an unsigned 32-bit integer.');
            }
            return $ssrc;
        }
        if (!preg_match('/^(?:0|[1-9][0-9]*)$/D', $ssrc)) {
            throw new SrtpValidateException('The SSRC must be an unsigned decimal 32-bit integer.');
        }
        $normalized = ltrim($ssrc, '0');
        $normalized = $normalized === '' ? '0' : $normalized;
        if (\strlen($normalized) > 10 || (\strlen($normalized) === 10 && $normalized > '4294967295')) {
            throw new SrtpValidateException('The SSRC must be an unsigned 32-bit integer.');
        }
        return (int) $normalized;
    }

    /** @throws SrtpException If no key was installed. */
    private function assertReady(): void
    {
        if (!$this->ready) {
            throw new SrtpException('The SRTP session has no key!');
        }
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package, vendored and modified for MadelineProto.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Srtp;

use Webrtc\Srtp\Enum\SrtpProfile;
use Webrtc\Srtp\Enum\SsrcType;
use Webrtc\Srtp\Exception\SrtpValidateException;

/**
 * Cryptographic policy of an SRTP stream.
 *
 * Upstream this was a thin wrapper around a libsrtp `srtp_policy_t`; it is now a plain value
 * object, since {@see Session} implements SRTP directly in PHP.
 */
final class Policy
{
    /** Master key concatenated with the master salt, as produced by the DTLS key exporter. */
    private ?string $key = null;

    private bool $allowRepeatTx = false;

    private int $windowSize = 1024;

    /**
     * @throws SrtpValidateException If the profile is not supported.
     */
    public function __construct(
        private readonly SrtpProfile $srtpProfile = SrtpProfile::AES128_CM_SHA1_80,
        ?string $key = null,
        private SsrcType $ssrcType = SsrcType::UNDEFINED,
        private int $ssrcValue = 0,
    ) {
        // Resolve every parameter up front. Unsupported enum cases must fail here instead of
        // falling through to plausible-looking default key and tag sizes.
        self::parameters($srtpProfile);
        self::validateSsrcValue($ssrcValue);
        if ($key !== null) {
            $this->setKey($key);
        }
    }

    /**
     * @return array{key: int, salt: int, rtpTag: int, rtcpTag: int, aead: bool}
     * @throws SrtpValidateException If the profile is not implemented.
     */
    private static function parameters(SrtpProfile $profile): array
    {
        return match ($profile) {
            SrtpProfile::AES128_CM_SHA1_80 => [
                'key' => 16, 'salt' => 14, 'rtpTag' => 10, 'rtcpTag' => 10, 'aead' => false,
            ],
            SrtpProfile::AES128_CM_SHA1_32 => [
                'key' => 16, 'salt' => 14, 'rtpTag' => 4, 'rtcpTag' => 10, 'aead' => false,
            ],
            SrtpProfile::AEAD_AES_128_GCM => [
                'key' => 16, 'salt' => 12, 'rtpTag' => 16, 'rtcpTag' => 16, 'aead' => true,
            ],
            SrtpProfile::AEAD_AES_256_GCM => [
                'key' => 32, 'salt' => 12, 'rtpTag' => 16, 'rtcpTag' => 16, 'aead' => true,
            ],
            default => throw new SrtpValidateException("Unsupported SRTP profile: {$profile->name}"),
        };
    }

    /**
     * Master key length of a profile, in bytes.
     *
     * @throws SrtpValidateException If the profile is not implemented.
     */
    public static function keyLength(SrtpProfile $profile): int
    {
        return self::parameters($profile)['key'];
    }

    /**
     * Master salt length of a profile, in bytes.
     */
    public static function saltLength(SrtpProfile $profile): int
    {
        return self::parameters($profile)['salt'];
    }

    /**
     * Whether the profile uses an AEAD cipher, which authenticates without a separate HMAC.
     */
    public static function isAead(SrtpProfile $profile): bool
    {
        return self::parameters($profile)['aead'];
    }

    /**
     * Length of the authentication tag appended to protected RTP packets, in bytes.
     */
    public static function rtpTagLength(SrtpProfile $profile): int
    {
        return self::parameters($profile)['rtpTag'];
    }

    /**
     * Length of the authentication tag appended to protected RTCP packets, in bytes.
     *
     * SRTCP always uses the full 80-bit tag with the HMAC-SHA1 profiles.
     */
    public static function rtcpTagLength(SrtpProfile $profile): int
    {
        return self::parameters($profile)['rtcpTag'];
    }

    public function getAllowRepeatTx(): bool
    {
        return $this->allowRepeatTx;
    }

    public function setAllowRepeatTx(bool $allowRepeatTx): void
    {
        $this->allowRepeatTx = $allowRepeatTx;
    }

    /**
     * @throws SrtpValidateException If the key does not match the profile's expected length.
     */
    public function setKey(?string $key = null): void
    {
        if ($key !== null) {
            $expected = self::keyLength($this->srtpProfile) + self::saltLength($this->srtpProfile);
            if (\strlen($key) !== $expected) {
                throw new SrtpValidateException(
                    "Invalid SRTP key length: expected $expected bytes, got ".\strlen($key)
                );
            }
        }
        $this->key = $key;
    }

    public function getKey(): ?string
    {
        return $this->key;
    }

    /**
     * The master key, without the trailing master salt.
     *
     * @throws SrtpValidateException If no keying material is installed.
     */
    public function getMasterKey(): string
    {
        if ($this->key === null) {
            throw new SrtpValidateException('The SRTP policy has no key!');
        }
        return substr($this->key, 0, self::keyLength($this->srtpProfile));
    }

    /**
     * The master salt, i.e. the tail of the exported key material.
     *
     * @throws SrtpValidateException If no keying material is installed.
     */
    public function getMasterSalt(): string
    {
        if ($this->key === null) {
            throw new SrtpValidateException('The SRTP policy has no key!');
        }
        return substr($this->key, self::keyLength($this->srtpProfile));
    }

    public function getSrtpProfile(): SrtpProfile
    {
        return $this->srtpProfile;
    }

    public function getSsrcType(): SsrcType
    {
        return $this->ssrcType;
    }

    public function setSsrcType(SsrcType $ssrcType): void
    {
        $this->ssrcType = $ssrcType;
    }

    public function getSsrcValue(): int
    {
        return $this->ssrcValue;
    }

    public function setSsrcValue(int $ssrcValue): void
    {
        self::validateSsrcValue($ssrcValue);
        $this->ssrcValue = $ssrcValue;
    }

    public function getWindowSize(): int
    {
        return $this->windowSize;
    }

    public function setWindowSize(int $windowSize): void
    {
        if ($windowSize < 64) {
            throw new SrtpValidateException('The SRTP replay window must contain at least 64 packets.');
        }
        $this->windowSize = $windowSize;
    }

    /**
     * @throws SrtpValidateException If the value cannot be encoded as an unsigned 32-bit SSRC.
     */
    private static function validateSsrcValue(int $ssrcValue): void
    {
        if ($ssrcValue < 0 || $ssrcValue > 0xFFFFFFFF) {
            throw new SrtpValidateException('The SSRC must be an unsigned 32-bit integer.');
        }
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Srtp\Enum;

/**
 * @brief identifies a particular SRTP profile
 *
 * An SrtpProfile enumeration is used to identify a particular SRTP
 * profile (that is, a set of algorithms and parameters).
 */
enum SrtpProfile: int
{
    case AES128_CM_SHA1_80 = 1;
    case AES128_CM_SHA1_32 = 2;
    case NULL_SHA1_80 = 5;
    case NULL_SHA1_32 = 6;
    case AEAD_AES_128_GCM = 7;
    case AEAD_AES_256_GCM = 8;
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Srtp\Enum;
/**
 * SsrcType describes the type of SSRC.
 *
 * An SsrcType enumeration is used to indicate a type of SSRC.
 */
enum SsrcType: int
{
    /**
     * Indicates an undefined SSRC type.
     */
    case UNDEFINED = 0;
    /**
     * Indicates a specific SSRC value
     */
    case SPECIFIC = 1;
    /**
     *  Indicates any inbound SSRC value
     *  (i.e., a value that is used in the function srtp_unprotect())
     */
    case ANY_INBOUND = 2;
    /**
     * Indicates any outbound SSRC value
     * (i.e., a value that is used in the function srtp_protect())
     */
    case ANY_OUTBOUND = 3;
}
{
    "name": "danog/php-rtc-srtp",
    "type": "library",
    "description": "Pure-PHP SRTP and SRTCP encryption using phpseclib, with AES-CM and AES-GCM profiles. MadelineProto's PHP 8.2+ fork removes the libsrtp/FFI dependency.",
    "keywords": [
        "srtp"
    ],
    "homepage": "https://github.com/danog/php-rtc-srtp",
    "license": "BSD-3-Clause",
    "authors": [
        {
            "name": "Amin Yazdanpanah",
            "email": "github@aminyazdanpanah.com"
        },
        {
            "name": "Sana Moniri",
            "email": "s.mo@quasarstream.com"
        },
        {
            "name": "Quasar Stream Community"
        },
        {
            "name": "Daniil Gentili",
            "email": "daniil@daniil.it"
        }
    ],
    "require": {
        "php-64bit": "^8.2",
        "danog/php-rtc-exception": "^1.1.5",
        "danog/php-rtc-mixin": "^1.1.6 || ^2.0",
        "phpseclib/phpseclib": "^4.0"
    },
    "require-dev": {
        "phpunit/phpunit": "^11.5 || ^12.0 || ^13.3.2",
        "bamarni/composer-bin-plugin": "^1.9.1"
    },
    "autoload": {
        "psr-4": {
            "Webrtc\\Srtp\\": "src"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\Webrtc\\Srtp\\": "tests"
        }
    },
    "minimum-stability": "stable",
    "scripts": {
        "post-install-cmd": [
            "@composer bin all install --ansi"
        ],
        "post-update-cmd": [
            "@composer bin all update --ansi"
        ]
    },
    "config": {
        "allow-plugins": {
            "bamarni/composer-bin-plugin": true
        }
    },
    "extra": {
        "bamarni-bin": {
            "bin-links": true,
            "forward-command": true
        }
    }
}
<?php declare(strict_types=1);

use Amp\Mysql\MysqlConfig;
use Amp\Postgres\PostgresConfig;
use Amp\Redis\RedisConfig;
use danog\AsyncOrm\Annotations\OrmMappedArray;
use danog\AsyncOrm\DbArray;
use danog\AsyncOrm\DbAutoProperties;
use danog\AsyncOrm\DbObject;
use danog\AsyncOrm\KeyType;
use danog\AsyncOrm\Settings;
use danog\AsyncOrm\Settings\MysqlSettings;
use danog\AsyncOrm\Settings\PostgresSettings;
use danog\AsyncOrm\Settings\RedisSettings;
use danog\AsyncOrm\ValueType;

require __DIR__ . '/../vendor/autoload.php';

// Any of the following database backends can be used,
// remove the ones you don't need.
$settings = new MysqlSettings(
    new MysqlConfig(
        host: "/var/run/mysqld/mysqld.sock",
        user: 'user',
        password: 'password',
        database: 'database'
    ),
    cacheTtl: 100
);
$settings = new PostgresSettings(
    new PostgresConfig(
        host: "127.0.0.1",
        user: "user",
        password: "password",
        database: "database"
    ),
    cacheTtl: 100
);
$settings = new RedisSettings(
    RedisConfig::fromUri("redis://127.0.0.1"),
    cacheTtl: 100
);

/**
 * An object stored in a database.
 */
class MyObject extends DbObject
{
    public function __construct(
        private string $value
    ) {
    }
    public function setValue(string $value): void
    {
        $this->value = $value;
    }
    public function getValue(): string
    {
        return $this->value;
    }
}

/**
 * Main class of your application.
 */
final class Application
{
    use DbAutoProperties;

    /**
     * This field is automatically connected to the database using the specified Settings.
     *
     * @var DbArray<string, MyObject>
     */
    #[OrmMappedArray(KeyType::STRING, ValueType::OBJECT)]
    private DbArray $dbProperty1;

    /**
     * This field is automatically connected to the database using the specified Settings.
     *
     * @var DbArray<string, int>
     */
    #[OrmMappedArray(KeyType::STRING, ValueType::INT)]
    private DbArray $dbProperty2;

    public function __construct(
        Settings $settings,
        string $tablePrefix
    ) {
        $this->initDbProperties($settings, $tablePrefix);
    }

    public function businessLogic(): void
    {
        $this->dbProperty1['someOtherKey'] = new MyObject("initialValue");

        // Can store integers, strings, arrays or objects depending on the specified ValueType
        $this->dbProperty2['someKey'] = 123;
        var_dump($this->dbProperty2['someKey']);
    }

    public function businessLogic2(string $value): void
    {
        $obj = $this->dbProperty1['someOtherKey'];
        $obj->setValue($value);
        $obj->save();
    }

    public function businessLogic3(): string
    {
        return $this->dbProperty1['someOtherKey']->getValue();
    }

    public function shutdown(): void
    {
        // Flush all database caches, saving all changes.
        $this->saveDbProperties();
    }
}

$app = new Application($settings, 'tablePrefix');
$app->businessLogic();
$app->businessLogic2("newValue");
var_dump($app->businessLogic3());
$app->shutdown();
<?php declare(strict_types=1);

use Amp\Mysql\MysqlConfig;
use Amp\Postgres\PostgresConfig;
use Amp\Redis\RedisConfig;
use danog\AsyncOrm\DbArrayBuilder;
use danog\AsyncOrm\DbObject;
use danog\AsyncOrm\KeyType;
use danog\AsyncOrm\Settings\MysqlSettings;
use danog\AsyncOrm\Settings\PostgresSettings;
use danog\AsyncOrm\Settings\RedisSettings;
use danog\AsyncOrm\ValueType;

require __DIR__ . '/../vendor/autoload.php';

// Any of the following database backends can be used,
// remove the ones you don't need.
$settings = new MysqlSettings(
    new MysqlConfig(
        host: "/var/run/mysqld/mysqld.sock",
        user: 'user',
        password: 'password',
        database: 'database'
    ),
    cacheTtl: 100
);
$settings = new PostgresSettings(
    new PostgresConfig(
        host: "127.0.0.1",
        user: "user",
        password: "password",
        database: "database"
    ),
    cacheTtl: 100
);
$settings = new RedisSettings(
    RedisConfig::fromUri("redis://127.0.0.1"),
    cacheTtl: 100
);

$fieldConfig = new DbArrayBuilder(
    'tableName',
    $settings,
    KeyType::STRING,
    ValueType::OBJECT
);

$db = $fieldConfig->build();

class MyObject extends DbObject
{
    public function __construct(
        public string $value
    ) {
    }
}

$db->set("a", new MyObject('v'));
$obj = $db->get("a");

var_dump($obj->value);
$obj->value = 'newValue';
$obj->save();

var_dump($db->get("a")->value); // newValue
<?php

$config = new Amp\CodeStyle\Config;

$config->getFinder()
    ->in(__DIR__ . '/src')
    ->in(__DIR__ . '/tests')
    ->in(__DIR__ . '/examples');

$cacheDir = getenv('TRAVIS') ? getenv('HOME') . '/.php-cs-fixer' : __DIR__;

$config->setCacheFile($cacheDir . '/.php_cs.cache');

return $config;
<?php declare(strict_types=1);

/**
 * Copyright 2024 Daniil Gentili.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @author    Alexander Pankratov <alexander@i-c-a.su>
 * @copyright 2016-2024 Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2024 Alexander Pankratov <alexander@i-c-a.su>
 * @license   https://opensource.org/license/apache-2-0 Apache 2.0
 * @link https://github.com/danog/AsyncOrm AsyncOrm documentation
 */

namespace danog\AsyncOrm;

use AssertionError;
use danog\AsyncOrm\Internal\Containers\ObjectContainer;

/** @api */
abstract class DbObject
{
    private ObjectContainer $mapper;
    private string|int|null $key = null;

    /**
     * Initialize database instance.
     *
     * @internal Do not invoke manually.
     */
    final public function initDb(ObjectContainer $mapper, string|int $key, DbArrayBuilder $config): void
    {
        $this->mapper = $mapper;
        $this->key = $key;
        $this->onLoaded($config);
    }

    /**
     * Save object to database.
     */
    public function save(): void
    {
        if ($this->key === null) {
            throw new AssertionError("Cannot save an uninitialized object!");
        }
        $this->onBeforeSave();
        $this->mapper->inner->set($this->key, $this);
        $this->onAfterSave();
    }

    // @codeCoverageIgnoreStart
    /**
     * Method invoked after loading the object.
     *
     * @psalm-suppress PossiblyUnusedParam
     *
     * @psalm-impure
     */
    protected function onLoaded(DbArrayBuilder $config): void
    {

    }
    /**
     * Method invoked before saving the object.
     *
     * @psalm-impure
     */
    protected function onBeforeSave(): void
    {

    }
    /**
     * Method invoked after saving the object.
     *
     * @psalm-impure
     */
    protected function onAfterSave(): void
    {

    }
    // @codeCoverageIgnoreEnd
}
<?php declare(strict_types=1);

/**
 * Copyright 2024 Daniil Gentili.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @author    Alexander Pankratov <alexander@i-c-a.su>
 * @copyright 2016-2024 Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2024 Alexander Pankratov <alexander@i-c-a.su>
 * @license   https://opensource.org/license/apache-2-0 Apache 2.0
 * @link https://github.com/danog/AsyncOrm AsyncOrm documentation
 */

namespace danog\AsyncOrm\Serializer;

use danog\AsyncOrm\Serializer;

/**
 * Native serializer.
 *
 * @api
 *
 * @template TValue
 * @implements Serializer<TValue>
 */
final class Native implements Serializer
{
    #[\Override]
    public function serialize(mixed $value): mixed
    {
        return \serialize($value);
    }
    #[\Override]
    public function deserialize(mixed $value): mixed
    {
        \assert(\is_string($value));
        /** @var TValue */
        return \unserialize($value);
    }
}
<?php declare(strict_types=1);

/**
 * Copyright 2024 Daniil Gentili.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @author    Alexander Pankratov <alexander@i-c-a.su>
 * @copyright 2016-2024 Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2024 Alexander Pankratov <alexander@i-c-a.su>
 * @license   https://opensource.org/license/apache-2-0 Apache 2.0
 * @link https://github.com/danog/AsyncOrm AsyncOrm documentation
 */

namespace danog\AsyncOrm\Serializer;

use danog\AsyncOrm\Serializer;

/**
 * JSON serializer.
 *
 * @api
 *
 * @template TValue
 * @implements Serializer<TValue>
 */
final class Json implements Serializer
{
    /**
     * @psalm-pure
     */
    #[\Override]
    public function serialize(mixed $value): mixed
    {
        return \json_encode($value, flags: JSON_THROW_ON_ERROR|JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES);
    }
    /**
     * @psalm-pure
     */
    #[\Override]
    public function deserialize(mixed $value): mixed
    {
        \assert(\is_string($value));
        /** @var TValue */
        return \json_decode($value, true, flags: JSON_THROW_ON_ERROR);
    }
}
<?php declare(strict_types=1);

/**
 * Copyright 2024 Daniil Gentili.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @author    Alexander Pankratov <alexander@i-c-a.su>
 * @copyright 2016-2024 Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2024 Alexander Pankratov <alexander@i-c-a.su>
 * @license   https://opensource.org/license/apache-2-0 Apache 2.0
 * @link https://github.com/danog/AsyncOrm AsyncOrm documentation
 */

namespace danog\AsyncOrm\Serializer;

use danog\AsyncOrm\Serializer;

/**
 * Igbinary serializer.
 *
 * @api
 *
 * @template TValue
 * @implements Serializer<TValue>
 */
final class Igbinary implements Serializer
{
    #[\Override]
    public function serialize(mixed $value): mixed
    {
        return \igbinary_serialize($value);
    }
    #[\Override]
    public function deserialize(mixed $value): mixed
    {
        \assert(\is_string($value));
        /** @var TValue */
        return \igbinary_unserialize($value);
    }
}
<?php declare(strict_types=1);

/**
 * Copyright 2024 Daniil Gentili.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2023 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/license/apache-2-0 Apache 2.0
 * @link https://github.com/danog/AsyncOrm AsyncOrm documentation
 */

namespace danog\AsyncOrm;

/**
 * Base interface for ORM settings.
 */
interface Settings
{
    /**
     * @return class-string<DbArray>
     *
     * @psalm-pure
     */
    public function getDriverClass(): string;
}
<?php declare(strict_types=1);

/**
 * Copyright 2024 Daniil Gentili.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @author    Alexander Pankratov <alexander@i-c-a.su>
 * @copyright 2016-2024 Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2024 Alexander Pankratov <alexander@i-c-a.su>
 * @license   https://opensource.org/license/apache-2-0 Apache 2.0
 * @link https://github.com/danog/AsyncOrm AsyncOrm documentation
 */

namespace danog\AsyncOrm;

use danog\AsyncOrm\Annotations\OrmMappedArray;
use danog\AsyncOrm\Internal\Driver\CachedArray;
use danog\AsyncOrm\Settings\DriverSettings;
use danog\AsyncOrm\Settings\MysqlSettings;
use ReflectionClass;

use function Amp\async;
use function Amp\Future\await;

/**
 * Trait that provides autoconfiguration of OrmMappedArray properties.
 *
 * @api
 */
trait DbAutoProperties
{
    /** @var list<CachedArray> */
    private array $properties = [];

    /** @return list<\ReflectionProperty> */
    private function getDbAutoProperties(): array
    {
        $res = [];
        foreach ((new ReflectionClass(static::class))->getProperties() as $property) {
            $attr = $property->getAttributes(OrmMappedArray::class);
            if (!$attr) {
                continue;
            }
            $res []= $property;
        }
        return $res;
    }

    /**
     * Initialize database properties.
     */
    public function initDbProperties(Settings $settings, string $tablePrefix): void
    {
        $this->properties = [];
        $promises = [];
        foreach ($this->getDbAutoProperties() as $property) {
            $attr = $property->getAttributes(OrmMappedArray::class);
            \assert(\count($attr) !== 0);
            $attr = $attr[0]->newInstance();

            if ($settings instanceof DriverSettings) {
                $ttl = $attr->cacheTtl ?? $settings->cacheTtl;
                if ($ttl !== $settings->cacheTtl) {
                    $settings = new $settings(...\array_merge(
                        (array) $settings,
                        ['cacheTtl' => $ttl]
                    ));
                }
                $serializer = $attr->serializer ?? $settings->serializer;
                if ($serializer !== $settings->serializer) {
                    $settings = new $settings(...\array_merge(
                        (array) $settings,
                        ['serializer' => $serializer]
                    ));
                }
                if ($settings instanceof MysqlSettings) {
                    $optimize = $attr->optimizeIfWastedMb ?? $settings->optimizeIfWastedMb;

                    if ($optimize !== $settings->optimizeIfWastedMb) {
                        $settings = new $settings(...\array_merge(
                            (array) $settings,
                            ['optimizeIfWastedMb' => $optimize]
                        ));
                    }
                }
            }

            $config = new DbArrayBuilder(
                $tablePrefix.($attr->tablePostfix ?? $property->getName()),
                $settings,
                $attr->keyType,
                $attr->valueType,
            );

            $promises[] = async(function () use ($config, $property) {
                $v = $config->build(
                    $property->isInitialized($this)
                        ? $property->getValue($this)
                        : null
                );
                $property->setValue($this, $v);
                if ($v instanceof CachedArray) {
                    $this->properties []= $v;
                }
            });
        }
        await($promises);
    }

    /**
     * Save all properties.
     */
    public function saveDbProperties(): void
    {
        $futures = [];
        foreach ($this->properties as $prop) {
            $futures []= async($prop->flushCache(...));
        }
        await($futures);
    }
}
<?php declare(strict_types=1);

/**
 * Copyright 2024 Daniil Gentili.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @author    Alexander Pankratov <alexander@i-c-a.su>
 * @copyright 2016-2024 Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2024 Alexander Pankratov <alexander@i-c-a.su>
 * @license   https://opensource.org/license/apache-2-0 Apache 2.0
 * @link https://github.com/danog/AsyncOrm AsyncOrm documentation
 */

namespace danog\AsyncOrm;

/**
 * Serializer interface.
 *
 * @template TValue
 */
interface Serializer
{
    /**
     * @param TValue $value
     *
     * @psalm-impure
     */
    public function serialize(mixed $value): mixed;
    /**
     * @return TValue
     *
     * @psalm-impure
     */
    public function deserialize(mixed $value): mixed;
}
<?php declare(strict_types=1);

/**
 * Copyright 2024 Daniil Gentili.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @author    Alexander Pankratov <alexander@i-c-a.su>
 * @copyright 2016-2024 Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2024 Alexander Pankratov <alexander@i-c-a.su>
 * @license   https://opensource.org/license/apache-2-0 Apache 2.0
 * @link https://github.com/danog/AsyncOrm AsyncOrm documentation
 */

namespace danog\AsyncOrm\Driver;

use Amp\Sql\SqlConnectionPool;
use Amp\Sql\SqlResult;
use danog\AsyncOrm\DbArrayBuilder;
use danog\AsyncOrm\Serializer;

/**
 * Generic SQL database backend.
 *
 * @template TKey as array-key
 * @template TValue
 * @extends DriverArray<TKey, TValue>
 *
 * @api
 */
abstract class SqlArray extends DriverArray
{
    /**
     * @psalm-suppress ConstructorSignatureMismatch
     * @param Serializer<TValue> $serializer
     */
    protected function __construct(
        DbArrayBuilder $config,
        Serializer $serializer,
        protected readonly SqlConnectionPool $db,
        private readonly string $get,
        private readonly string $set,
        private readonly string $unset,
        private readonly string $count,
        private readonly string $iterate,
        private readonly string $clear,
    ) {
        parent::__construct($config, $serializer);
    }

    /**
     * Get iterator.
     *
     * @return \Traversable<array-key, mixed>
     */
    #[\Override]
    public function getIterator(): \Traversable
    {
        foreach ($this->execute($this->iterate) as ['key' => $key, 'value' => $value]) {
            yield $key => $this->serializer->deserialize($value);
        }
    }

    #[\Override]
    public function get(mixed $key): mixed
    {
        $key = (string) $key;

        $row = $this->execute($this->get, ['index' => $key])->fetchRow();
        if ($row === null) {
            return null;
        }

        $value = $this->serializer->deserialize($row['value']);

        return $value;
    }

    #[\Override]
    public function set(string|int $key, mixed $value): void
    {
        $key = (string) $key;
        /** @var scalar */
        $value = $this->serializer->serialize($value);

        $this->execute(
            $this->set,
            [
                'index' => $key,
                'value' => $value,
            ],
        );
    }

    /**
     * Unset value for an offset.
     *
     * @link https://php.net/manual/en/arrayiterator.offsetunset.php
     */
    #[\Override]
    public function unset(string|int $key): void
    {
        $key = (string) $key;

        $this->execute(
            $this->unset,
            ['index' => $key],
        );
    }

    /**
     * Count elements.
     *
     * @link https://php.net/manual/en/arrayiterator.count.php
     * @return int The number of elements or public properties in the associated
     *             array or object, respectively.
     */
    #[\Override]
    public function count(): int
    {
        $row = $this->execute($this->count);
        $res = $row->fetchRow();
        \assert($res !== null && isset($res['count']) && \is_int($res['count']));
        return $res['count'];
    }

    /**
     * Clear all elements.
     */
    #[\Override]
    public function clear(): void
    {
        $this->execute($this->clear);
    }

    /**
     * Perform async request to db.
     *
     * Uses an unnamed statement: preparing a named one for each query costs two more round trips, and
     * deallocating it races with the next queries on the connection (amphp/postgres#75).
     *
     * @param array<string, scalar> $params
     */
    protected function execute(string $sql, array $params = []): SqlResult
    {
        return $this->db->execute($sql, $params);
    }

    /**
     * Import data from existing table.
     *
     * @psalm-impure
     */
    abstract protected function importFromTable(string $fromTable): void;

    /**
     * What identifies the server and database this array's table is on, whatever address
     * reaches them (TCP, another host name, a unix socket), or null if it can't be told.
     *
     * @psalm-impure
     */
    abstract protected function getDatabaseIdentity(): ?string;
}
<?php declare(strict_types=1);

/**
 * Copyright 2024 Daniil Gentili.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @author    Alexander Pankratov <alexander@i-c-a.su>
 * @copyright 2016-2024 Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2024 Alexander Pankratov <alexander@i-c-a.su>
 * @license   https://opensource.org/license/apache-2-0 Apache 2.0
 * @link https://github.com/danog/AsyncOrm AsyncOrm documentation
 */

namespace danog\AsyncOrm\Driver;

use danog\AsyncOrm\DbArray;
use danog\AsyncOrm\DbArrayBuilder;
use danog\AsyncOrm\Serializer;
use danog\AsyncOrm\Settings\DriverSettings;
use danog\AsyncOrm\Settings\SqlSettings;

use function Amp\async;
use function Amp\Future\await;

/**
 * Base class for driver-based arrays.
 *
 * @template TKey as array-key
 * @template TValue
 *
 * @psalm-consistent-constructor
 *
 * @extends DbArray<TKey, TValue>
 *
 * @api
 */
abstract class DriverArray extends DbArray
{
    private bool $inited = false;
    /**
     * @param Serializer<TValue> $serializer
     *
     * @psalm-impure Subclasses connect to the database, and the constructor is consistent.
     */
    protected function __construct(
        protected readonly DbArrayBuilder $config,
        protected readonly Serializer $serializer
    ) {
        $this->inited = true;
    }

    /**
     * @template TTKey as array-key
     * @template TTValue
     * @param DbArray<TTKey, TTValue> $previous
     * @return DbArray<TTKey, TTValue>
     */
    #[\Override]
    public static function getInstance(DbArrayBuilder $config, DbArray|null $previous): DbArray
    {
        $migrate = true;
        /** @psalm-suppress DocblockTypeContradiction TODO fix in psalm */
        if ($previous !== null
            && $previous::class === static::class
            && $previous->config == $config
        ) {
            if ($previous->inited) {
                return $previous;
            }
            $migrate = false;
        }
        \assert($config->settings instanceof DriverSettings);

        /** @var DbArray<TTKey, TTValue> */
        $instance = new static($config, $config->settings->serializer);

        if ($previous === null || !$migrate) {
            return $instance;
        }

        /** @psalm-suppress DocblockTypeContradiction TODO fix in psalm */
        $sameDriver = $previous instanceof SqlArray
            && $instance instanceof SqlArray
            && $previous::class === $instance::class;
        if ($sameDriver) {
            \assert($config->settings instanceof SqlSettings);
            \assert($previous->config->settings instanceof SqlSettings);
            $c = $config->settings->config;
            $prevC = $previous->config->settings->config;
            if ($c->getHost() === $prevC->getHost()
                && $c->getPort() === $prevC->getPort()
                && $c->getDatabase() === $prevC->getDatabase()
            ) {
                return self::sameDatabase($instance, $previous);
            }
        }

        /** @psalm-suppress DocblockTypeContradiction TODO fix in psalm */
        if ($previous instanceof self && !$previous->inited) {
            // Unserialized (e.g. from a session) and not woken up yet: it has its settings,
            // but no connection or queries. Bring it up from its own settings to read it.
            /** @var DbArray<TTKey, TTValue> */
            $previous = $previous::getInstance($previous->config, null);
        }

        // The old rows are deleted only once they're known to be stored elsewhere: by
        // another driver, or by an SQL server and database that tell they're not the same.
        // Otherwise the previous array may be the very same storage under other settings
        // (another address, cache TTL or serializer), and the copy rewrites its rows in place.
        $deleteOld = $previous::class !== $instance::class;
        if ($sameDriver) {
            /** @psalm-suppress DocblockTypeContradiction TODO fix in psalm */
            \assert($previous instanceof SqlArray);
            // The same server and database under another address (e.g. TCP and a unix
            // socket): copying the rows would read and rewrite the very same table.
            $id = $instance->getDatabaseIdentity();
            $prevId = $previous->getDatabaseIdentity();
            if ($id !== null && $id === $prevId) {
                return self::sameDatabase($instance, $previous);
            }
            $deleteOld = $id !== null && $prevId !== null;
        }

        $promises = [];
        foreach ($previous->getIterator() as $key => $value) {
            // The new copy first: if this is interrupted, a row is at worst in both.
            $f = async($instance->set(...), $key, $value);
            if ($deleteOld) {
                $f = $f->map(static fn () => $previous->unset($key));
            }
            $promises []= $f;
            if (\count($promises) % 500 === 0) {
                // @codeCoverageIgnoreStart
                await($promises);
                $promises = [];
                // @codeCoverageIgnoreEnd
            }
        }
        if ($promises) {
            await($promises);
        }

        return $instance;
    }

    /**
     * The array is on the same database as before: at most, its table was renamed.
     *
     * @template TTKey as array-key
     * @template TTValue
     * @param SqlArray<TTKey, TTValue> $instance
     * @return SqlArray<TTKey, TTValue>
     */
    private static function sameDatabase(SqlArray $instance, SqlArray $previous): SqlArray
    {
        if ($instance->config->table !== $previous->config->table) {
            $instance->importFromTable($previous->config->table);
        }
        // Only keyType/valueType changed, and we already migrated those on construction.
        return $instance;
    }

    /**
     * Sleep function.
     *
     * @psalm-pure
     */
    public function __sleep(): array
    {
        return ['config'];
    }
}
<?php declare(strict_types=1);

/**
 * Copyright 2024 Daniil Gentili.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @author    Alexander Pankratov <alexander@i-c-a.su>
 * @copyright 2016-2024 Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2024 Alexander Pankratov <alexander@i-c-a.su>
 * @license   https://opensource.org/license/apache-2-0 Apache 2.0
 * @link https://github.com/danog/AsyncOrm AsyncOrm documentation
 */

namespace danog\AsyncOrm\Driver;

use ArrayIterator;
use danog\AsyncOrm\DbArray;
use danog\AsyncOrm\DbArrayBuilder;

/**
 * Memory database backend.
 *
 * @template TKey as array-key
 * @template TValue
 * @extends DbArray<TKey, TValue>
 * @api
 */
final class MemoryArray extends DbArray
{
    public function __construct(
        /** @var array<TKey, TValue> */
        private array $data
    ) {
    }

    #[\Override]
    public static function getInstance(DbArrayBuilder $config, DbArray|null $previous): DbArray
    {
        if ($previous instanceof self) {
            return $previous;
        }
        if ($previous instanceof DbArray) {
            $temp = $previous->getArrayCopy();
            $previous->clear();
            $previous = $temp;
        } else {
            $previous = [];
        }
        return new self($previous);
    }

    /**
     * @psalm-capabilities read-props|write-this-props|write-refs
     */
    #[\Override]
    public function set(string|int $key, mixed $value): void
    {
        $this->data[$key] = $value;
    }
    /**
     * @psalm-capabilities read-props
     */
    #[\Override]
    public function get(string|int $key): mixed
    {
        return $this->data[$key] ?? null;
    }
    /**
     * @psalm-capabilities read-props|write-this-props|write-refs
     */
    #[\Override]
    public function unset(string|int $key): void
    {
        unset($this->data[$key]);
    }

    /**
     * @psalm-capabilities read-props|write-this-props|write-refs
     */
    #[\Override]
    public function clear(): void
    {
        $this->data = [];
    }

    /**
     * @psalm-capabilities read-props
     */
    #[\Override]
    public function count(): int
    {
        return \count($this->data);
    }

    /**
     * @psalm-capabilities read-props
     */
    #[\Override]
    public function getIterator(): \Traversable
    {
        return new ArrayIterator($this->data);
    }

    #[\Override]
    public function getArrayCopy(): array
    {
        return $this->data;
    }
}
<?php declare(strict_types=1);

/**
 * Copyright 2024 Daniil Gentili.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2023 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/license/apache-2-0 Apache 2.0
 * @link https://github.com/danog/AsyncOrm AsyncOrm documentation
 */

namespace danog\AsyncOrm;

/**
 * Specifies the serializer to use when saving values.
 */
enum ValueType: string
{
    /**
     * Direct storage of UTF-8 string values.
     */
    case STRING = 'string';
    /**
     * Direct storage of integer values.
     */
    case INT = 'int';
    /**
     * Direct storage of boolean values.
     */
    case BOOL = 'bool';
    /**
     * Direct storage of floating point (double precision) values.
     */
    case FLOAT = 'float';
    /**
     * Objects extending DbObject, serialized as specified in the settings.
     */
    case OBJECT = 'object';
    /**
     * Values of any scalar type, serialized as specified in the settings.
     *
     * Using SCALAR worsens performances, please use any of the other types if possible.
     */
    case SCALAR = 'scalar';
}
<?php declare(strict_types=1);

/**
 * Copyright 2024 Daniil Gentili.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2023 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/license/apache-2-0 Apache 2.0
 * @link https://github.com/danog/AsyncOrm AsyncOrm documentation
 */

namespace danog\AsyncOrm;

use ArrayAccess;
use Countable;
use IteratorAggregate;
use Traversable;

/**
 * DB array interface.
 *
 * @template TKey as array-key
 * @template TValue
 *
 * @implements ArrayAccess<TKey, TValue>
 * @implements Traversable<TKey, TValue>
 * @implements IteratorAggregate<TKey, TValue>
 *
 * @api
 */
abstract class DbArray implements Countable, ArrayAccess, Traversable, IteratorAggregate
{
    /**
     * Check if element exists.
     *
     * @param TKey $key
     */
    final public function isset(string|int $key): bool
    {
        return $this->get($key) !== null;
    }

    /**
     * @param TKey $offset
     * @return TValue
     */
    #[\Override]
    final public function offsetGet(mixed $offset): mixed
    {
        return $this->get($offset);
    }

    /**
     * @param TKey $offset
     */
    #[\Override]
    final public function offsetExists(mixed $offset): bool
    {
        return $this->isset($offset);
    }

    /**
     * @param TKey $offset
     * @param TValue $value
     */
    #[\Override]
    final public function offsetSet(mixed $offset, mixed $value): void
    {
        $this->set($offset, $value);
    }

    /**
     * @param TKey $offset
     */
    #[\Override]
    final public function offsetUnset(mixed $offset): void
    {
        $this->unset($offset);
    }

    public function getArrayCopy(): array
    {
        return \iterator_to_array($this->getIterator());
    }

    /**
     * Unset element.
     *
     * @param TKey $key
     *
     * @psalm-impure
     */
    abstract public function unset(string|int $key): void;
    /**
     * Set element.
     *
     * @param TKey   $key
     * @param TValue $value
     *
     * @psalm-impure
     */
    abstract public function set(string|int $key, mixed $value): void;
    /**
     * Get element.
     *
     * @param TKey   $key
     * @return ?TValue
     *
     * @psalm-impure
     */
    abstract public function get(string|int $key): mixed;
    /**
     * Clear all elements.
     *
     * @psalm-impure
     */
    abstract public function clear(): void;

    /**
     * Get instance.
     *
     * @template TTKey as array-key
     * @template TTValue as DbObject
     *
     * @param DbArray<TTKey, TValue>|null $previous
     * @return DbArray<TTKey, TValue>
     *
     * @psalm-impure
     */
    abstract public static function getInstance(DbArrayBuilder $config, self|null $previous): self;
}
<?php declare(strict_types=1);

/**
 * Copyright 2024 Daniil Gentili.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @author    Alexander Pankratov <alexander@i-c-a.su>
 * @copyright 2016-2024 Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2024 Alexander Pankratov <alexander@i-c-a.su>
 * @license   https://opensource.org/license/apache-2-0 Apache 2.0
 * @link https://github.com/danog/AsyncOrm AsyncOrm documentation
 */

namespace danog\AsyncOrm\Internal\Serializer;

use danog\AsyncOrm\Serializer;

/**
 * Passthrough serializer.
 *
 * @internal
 *
 * @template TValue
 * @implements Serializer<TValue>
 */
final class Passthrough implements Serializer
{
    /**
     * @param TValue $value
     *
     * @return TValue
     *
     * @psalm-pure
     */
    #[\Override]
    public function serialize(mixed $value): mixed
    {
        return $value;
    }
    /**
     * @param TValue $value
     *
     * @psalm-pure
     */
    #[\Override]
    public function deserialize(mixed $value): mixed
    {
        return $value;
    }
}
<?php declare(strict_types=1);

/**
 * Copyright 2024 Daniil Gentili.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @author    Alexander Pankratov <alexander@i-c-a.su>
 * @copyright 2016-2024 Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2024 Alexander Pankratov <alexander@i-c-a.su>
 * @license   https://opensource.org/license/apache-2-0 Apache 2.0
 * @link https://github.com/danog/AsyncOrm AsyncOrm documentation
 */

namespace danog\AsyncOrm\Internal\Serializer;

use danog\AsyncOrm\Serializer;

/**
 * Bool casting serializer.
 *
 * @internal
 *
 * @implements Serializer<bool>
 */
final class BoolString implements Serializer
{
    /**
     * @psalm-pure
     */
    #[\Override]
    public function serialize(mixed $value): mixed
    {
        return match ($value) {
            true => '1',
            false => '0',
        };
    }
    /**
     * @psalm-pure
     */
    #[\Override]
    public function deserialize(mixed $value): mixed
    {
        /** @psalm-suppress UnhandledMatchCondition Intentional */
        return match ($value) {
            '1' => true,
            '0' => false,
        };
    }
}
<?php declare(strict_types=1);

/**
 * Copyright 2024 Daniil Gentili.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @author    Alexander Pankratov <alexander@i-c-a.su>
 * @copyright 2016-2024 Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2024 Alexander Pankratov <alexander@i-c-a.su>
 * @license   https://opensource.org/license/apache-2-0 Apache 2.0
 * @link https://github.com/danog/AsyncOrm AsyncOrm documentation
 */

namespace danog\AsyncOrm\Internal\Serializer;

use Amp\Postgres\PostgresByteA;
use danog\AsyncOrm\Serializer;

/**
 * @internal BYTEA serializer
 *
 * @template TValue
 * @implements Serializer<TValue>
 */
final class ByteaSerializer implements Serializer
{
    /**
     * @param Serializer<TValue> $inner
     */
    public function __construct(
        private readonly Serializer $inner
    ) {
    }
    #[\Override]
    public function serialize(mixed $value): mixed
    {
        return new PostgresByteA((string) $this->inner->serialize($value));
    }
    #[\Override]
    public function deserialize(mixed $value): mixed
    {
        return $this->inner->deserialize($value);
    }
}
<?php declare(strict_types=1);

/**
 * Copyright 2024 Daniil Gentili.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @author    Alexander Pankratov <alexander@i-c-a.su>
 * @copyright 2016-2024 Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2024 Alexander Pankratov <alexander@i-c-a.su>
 * @license   https://opensource.org/license/apache-2-0 Apache 2.0
 * @link https://github.com/danog/AsyncOrm AsyncOrm documentation
 */

namespace danog\AsyncOrm\Internal\Serializer;

use danog\AsyncOrm\Serializer;

/**
 * Float casting serializer.
 *
 * @internal
 *
 * @implements Serializer<float>
 */
final class FloatString implements Serializer
{
    /**
     * @psalm-pure
     */
    #[\Override]
    public function serialize(mixed $value): mixed
    {
        return (string) $value;
    }
    /**
     * @psalm-pure
     */
    #[\Override]
    public function deserialize(mixed $value): mixed
    {
        return (float) $value;
    }
}
<?php declare(strict_types=1);

/**
 * Copyright 2024 Daniil Gentili.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @author    Alexander Pankratov <alexander@i-c-a.su>
 * @copyright 2016-2024 Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2024 Alexander Pankratov <alexander@i-c-a.su>
 * @license   https://opensource.org/license/apache-2-0 Apache 2.0
 * @link https://github.com/danog/AsyncOrm AsyncOrm documentation
 */

namespace danog\AsyncOrm\Internal\Serializer;

use danog\AsyncOrm\Serializer;

/**
 * Integer casting serializer.
 *
 * @internal
 *
 * @implements Serializer<int>
 */
final class IntString implements Serializer
{
    /**
     * @psalm-pure
     */
    #[\Override]
    public function serialize(mixed $value): mixed
    {
        return (string) $value;
    }
    /**
     * @psalm-pure
     */
    #[\Override]
    public function deserialize(mixed $value): mixed
    {
        return (int) $value;
    }
}
<?php declare(strict_types=1);

/**
 * Copyright 2024 Daniil Gentili.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @author    Alexander Pankratov <alexander@i-c-a.su>
 * @copyright 2016-2024 Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2024 Alexander Pankratov <alexander@i-c-a.su>
 * @license   https://opensource.org/license/apache-2-0 Apache 2.0
 * @link https://github.com/danog/AsyncOrm AsyncOrm documentation
 */

namespace danog\AsyncOrm\Internal\Serializer;

use AssertionError;
use danog\AsyncOrm\Serializer;

/**
 * Bool casting serializer.
 *
 * @internal
 *
 * @implements Serializer<bool>
 */
final class BoolInt implements Serializer
{
    /**
     * @psalm-pure
     */
    #[\Override]
    public function serialize(mixed $value): mixed
    {
        return match ($value) {
            true => 1,
            false => 0,
        };
    }
    /**
     * @psalm-pure
     */
    #[\Override]
    public function deserialize(mixed $value): mixed
    {
        \assert(\is_string($value));
        return match (\ord($value)) {
            1 => true,
            0 => false,
            default => throw new AssertionError(\var_export($value, true))
        };
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of AsyncOrm.
 * AsyncOrm is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General private License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * AsyncOrm is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General private License for more details.
 * You should have received a copy of the GNU General private License along with AsyncOrm.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @author    Alexander Pankratov <alexander@i-c-a.su>
 * @copyright 2016-2024 Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2024 Alexander Pankratov <alexander@i-c-a.su>
 * @license   https://opensource.org/license/apache-2-0 Apache 2.0
 * @link https://github.com/danog/AsyncOrm AsyncOrm documentation
 */

namespace danog\AsyncOrm\Internal\Containers;

use Amp\Sync\LocalMutex;
use danog\AsyncOrm\DbArray;
use Revolt\EventLoop;
use Traversable;

/**
 * @template TKey as array-key
 * @template TValue
 * @internal
 */
final class CacheContainer
{
    /**
     * @var array<TKey, TValue>
     */
    private array $cache = [];
    /**
     * @var array<TKey, int|true>
     */
    private array $ttl = [];

    /**
     * Cache cleanup watcher ID.
     */
    private ?string $cacheCleanupId = null;

    private LocalMutex $mutex;

    public function __construct(
        /** @var DbArray<TKey, TValue> */
        public DbArray $inner,
        public int $cacheTtl
    ) {
        $this->mutex = new LocalMutex;
    }
    /**
     * @psalm-pure
     */
    public function __sleep()
    {
        return ['inner'];
    }
    public function __wakeup(): void
    {
        $this->mutex = new LocalMutex;
    }

    public function startCacheCleanupLoop(): void
    {
        if ($this->cacheCleanupId !== null) {
            EventLoop::cancel($this->cacheCleanupId);
        }
        $this->cacheCleanupId = EventLoop::repeat(
            \max(1, $this->cacheTtl / 5),
            fn () => $this->flushCache(),
        );
    }
    public function stopCacheCleanupLoop(): void
    {
        if ($this->cacheCleanupId !== null) {
            EventLoop::cancel($this->cacheCleanupId);
            $this->cacheCleanupId = null;
        }
    }

    /**
     * @param TKey $index
     * @return TValue
     */
    public function get(string|int $index): mixed
    {
        if (isset($this->ttl[$index])) {
            if ($this->ttl[$index] !== true) {
                $this->ttl[$index] = \time() + $this->cacheTtl;
            }
            return $this->cache[$index];
        }

        $result = $this->inner->get($index);
        /** @psalm-suppress ParadoxicalCondition Concurrency */
        if (isset($this->ttl[$index])) {
            if ($this->ttl[$index] !== true) {
                $this->ttl[$index] = \time() + $this->cacheTtl;
            }
            return $this->cache[$index];
        }

        $this->ttl[$index] = \time() + $this->cacheTtl;
        $this->cache[$index] = $result;

        return $result;
    }

    /**
     * @param TKey $key
     * @param ?TValue $value
     *
     * @psalm-capabilities read-props|write-this-props|write-refs
     */
    public function set(string|int $key, mixed $value): void
    {
        if (isset($this->ttl[$key]) && $this->cache[$key] === $value) {
            return;
        }
        $this->cache[$key] = $value;
        $this->ttl[$key] = true;
    }

    /** @return Traversable<TKey, TValue> */
    public function getIterator(): Traversable
    {
        $this->flushCache();
        return $this->inner->getIterator();
    }

    public function count(): int
    {
        $this->flushCache();
        return $this->inner->count();
    }

    public function clear(): void
    {
        $lock = $this->mutex->acquire();
        $this->cache = [];
        $this->ttl = [];
        $lock->release();

        $this->inner->clear();
    }

    /**
     * Flush all flushable keys.
     */
    public function flushCache(): void
    {
        // Not initialized if the unserialization of the session failed before __wakeup ran:
        // such a container only holds defaults, there's nothing to flush.
        /** @psalm-suppress RedundantPropertyInitializationCheck */
        if (!isset($this->mutex)) {
            return;
        }
        $lock = $this->mutex->acquire();
        try {
            $updatedValues = [];
            $newValues = [];
            $newTtl = [];
            $now = \time();
            foreach ($this->ttl as $key => $ttl) {
                if ($ttl === true) {
                    $updatedValues[$key] = $this->cache[$key];
                    if ($this->cache[$key] === null) {
                        $this->inner->unset($key);
                    } else {
                        $this->inner->set($key, $this->cache[$key]);
                    }
                } elseif ($ttl > $now) {
                    $newTtl[$key] = $ttl;
                    $newValues[$key] = $this->cache[$key];
                }
            }
            foreach ($updatedValues as $key => $value) {
                if (($newValues[$key] = $this->cache[$key]) === $value) {
                    // The value we wrote is equal to the latest value,
                    // turn into a read-cache entry
                    $newTtl[$key] = \time() + $this->cacheTtl;
                } else {
                    // The value we wrote is already old,
                    // keep it a write-cache entry to re-write it later
                    $newTtl[$key] = $this->ttl[$key];
                }
            }
            $this->ttl = $newTtl;
            $this->cache = $newValues;
        } finally {
            EventLoop::queue($lock->release(...));
        }
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of AsyncOrm.
 * AsyncOrm is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General private License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * AsyncOrm is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General private License for more details.
 * You should have received a copy of the GNU General private License along with AsyncOrm.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @author    Alexander Pankratov <alexander@i-c-a.su>
 * @copyright 2016-2024 Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2024 Alexander Pankratov <alexander@i-c-a.su>
 * @license   https://opensource.org/license/apache-2-0 Apache 2.0
 * @link https://github.com/danog/AsyncOrm AsyncOrm documentation
 */

namespace danog\AsyncOrm\Internal\Containers;

use Amp\Sync\LocalMutex;
use danog\AsyncOrm\DbArray;
use danog\AsyncOrm\DbArrayBuilder;
use danog\AsyncOrm\DbObject;
use Revolt\EventLoop;
use Traversable;

use function Amp\async;

/**
 * @template TKey as array-key
 * @template TValue as DbObject
 * @internal
 */
final class ObjectContainer
{
    /**
     * @var array<TKey, ObjectReference<TValue>>
     */
    private array $cache = [];

    /**
     * Cache cleanup watcher ID.
     */
    private ?string $cacheCleanupId = null;

    private LocalMutex $mutex;

    public function __construct(
        /** @var DbArray<TKey, TValue> */
        public DbArray $inner,
        public DbArrayBuilder $config,
        public int $cacheTtl,
    ) {
        $this->mutex = new LocalMutex;
    }
    /**
     * @psalm-pure
     */
    public function __sleep()
    {
        return ['inner'];
    }
    public function __wakeup(): void
    {
        $this->mutex = new LocalMutex;
    }

    public function startCacheCleanupLoop(): void
    {
        if ($this->cacheCleanupId !== null) {
            EventLoop::cancel($this->cacheCleanupId);
        }
        $this->cacheCleanupId = EventLoop::repeat(
            \max(1, $this->cacheTtl / 5),
            fn () => $this->flushCache(),
        );
    }
    /**
     * @psalm-suppress PossiblyUnusedMethod Psalm ignores the call in ObjectArray::__destruct
     */
    public function stopCacheCleanupLoop(): void
    {
        if ($this->cacheCleanupId !== null) {
            EventLoop::cancel($this->cacheCleanupId);
            $this->cacheCleanupId = null;
        }
    }

    /**
     * @param TKey $index
     * @return ?TValue
     */
    public function get(string|int $index): mixed
    {
        if (isset($this->cache[$index])) {
            $obj = $this->cache[$index];
            $ref = $obj->get();
            if ($ref !== null) {
                $obj->ttl = \time() + $this->cacheTtl;
                return $ref;
            }
            // @codeCoverageIgnoreStart
            unset($this->cache[$index]);
            // @codeCoverageIgnoreEnd
        }

        $result = $this->inner->get($index);
        if (isset($this->cache[$index])) {
            return $this->cache[$index]->get();
        }
        if ($result === null) {
            return null;
        }

        $this->cache[$index] = new ObjectReference(
            $result,
            \time() + $this->cacheTtl,
            $f = async(
                $result->initDb(...),
                $this,
                $index,
                $this->config
            )
        );
        $f->await();

        return $result;
    }

    /**
     * @param TKey $key
     * @param TValue $value
     */
    public function set(string|int $key, DbObject $value): void
    {
        if (isset($this->cache[$key]) && $this->cache[$key]->get() === $value) {
            return;
        }
        $value->initDb(
            $this,
            $key,
            $this->config
        );
        $this->cache[$key] = new ObjectReference(
            $value,
            \time() + $this->cacheTtl,
            null
        );
        $value->save();
    }

    /** @param TKey $key */
    public function unset(string|int $key): void
    {
        unset($this->cache[$key]);
        $this->inner->unset($key);
    }

    /** @return Traversable<TKey, TValue> */
    public function getIterator(): Traversable
    {
        foreach ($this->inner->getIterator() as $key => $value) {
            if (isset($this->cache[$key])) {
                $obj = $this->cache[$key];
                $ref = $obj->get();
                if ($ref !== null) {
                    $obj->ttl = \time() + $this->cacheTtl;
                    yield $key => $ref;
                    continue;
                }
            }
            $this->cache[$key] = new ObjectReference(
                $value,
                \time() + $this->cacheTtl,
                $f = async(
                    $value->initDb(...),
                    $this,
                    $key,
                    $this->config
                )
            );
            $f->await();
            yield $key => $value;
        }
    }

    public function count(): int
    {
        return $this->inner->count();
    }

    public function clear(): void
    {
        $lock = $this->mutex->acquire();
        $this->cache = [];
        $lock->release();

        $this->inner->clear();
    }

    /**
     * Flush all flushable keys.
     */
    public function flushCache(): void
    {
        // Not initialized if the unserialization of the session failed before __wakeup ran:
        // such a container only holds defaults, there's nothing to flush.
        /** @psalm-suppress RedundantPropertyInitializationCheck */
        if (!isset($this->mutex)) {
            return;
        }
        $lock = $this->mutex->acquire();
        try {
            $now = \time();
            $new = [];
            foreach ($this->cache as $key => $value) {
                if ($value->ttl <= $now) {
                    $value->obj = null;
                }
                if ($value->get() !== null) {
                    $new[$key] = $value;
                }
            }
            $this->cache = $new;
        } finally {
            EventLoop::queue($lock->release(...));
        }
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of AsyncOrm.
 * AsyncOrm is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General private License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * AsyncOrm is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General private License for more details.
 * You should have received a copy of the GNU General private License along with AsyncOrm.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @author    Alexander Pankratov <alexander@i-c-a.su>
 * @copyright 2016-2024 Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2024 Alexander Pankratov <alexander@i-c-a.su>
 * @license   https://opensource.org/license/apache-2-0 Apache 2.0
 * @link https://github.com/danog/AsyncOrm AsyncOrm documentation
 */

namespace danog\AsyncOrm\Internal\Containers;

use Amp\Future;
use danog\AsyncOrm\DbObject;
use WeakReference;

/**
 * @template TObject as DbObject
 * @internal
 * @api
 */
final class ObjectReference
{
    /** @var WeakReference<TObject> */
    private readonly WeakReference $reference;
    public ?DbObject $obj;
    /** @param TObject $object */
    public function __construct(
        DbObject $object,
        public int $ttl,
        private ?Future $initFuture
    ) {
        $this->obj = $object;
        $this->reference = WeakReference::create($object);
    }

    /** @return ?TObject */
    public function get(): ?DbObject
    {
        $ref = $this->reference->get();
        if ($ref === null) {
            return $ref;
        }
        if ($this->initFuture !== null) {
            $this->initFuture->await();
            $this->initFuture = null;
        }
        return $ref;
    }
}
<?php declare(strict_types=1);

/**
 * Copyright 2024 Daniil Gentili.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @author    Alexander Pankratov <alexander@i-c-a.su>
 * @copyright 2016-2024 Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2024 Alexander Pankratov <alexander@i-c-a.su>
 * @license   https://opensource.org/license/apache-2-0 Apache 2.0
 * @link https://github.com/danog/AsyncOrm AsyncOrm documentation
 */

namespace danog\AsyncOrm\Internal\Driver;

use danog\AsyncOrm\DbArray;
use danog\AsyncOrm\DbArrayBuilder;
use danog\AsyncOrm\Internal\Containers\CacheContainer;
use danog\AsyncOrm\Settings\DriverSettings;
use Revolt\EventLoop;
use Traversable;

/**
 * Array caching proxy.
 *
 * @template TKey as array-key
 * @template TValue
 *
 * @internal
 * @api
 *
 * @extends DbArray<TKey, TValue>
 */
final class CachedArray extends DbArray
{
    /** @var CacheContainer<TKey, TValue> */
    private readonly CacheContainer $cache;

    /**
     * Get instance.
     */
    #[\Override]
    public static function getInstance(DbArrayBuilder $config, DbArray|null $previous): DbArray
    {
        \assert($config->settings instanceof DriverSettings);
        $new = $config->settings->getDriverClass();
        if ($previous === null) {
            $previous = new self($new::getInstance($config, null), $config->settings->cacheTtl);
        } elseif ($previous instanceof self) {
            $previous->cache->inner = $new::getInstance($config, $previous->cache->inner);
            $previous->cache->cacheTtl = $config->settings->cacheTtl;
        } else {
            $previous = new self($new::getInstance($config, $previous), $config->settings->cacheTtl);
        }
        $previous->cache->startCacheCleanupLoop();
        return $previous;
    }

    public function __construct(DbArray $inner, int $cacheTtl)
    {
        $this->cache = new CacheContainer($inner, $cacheTtl);
    }

    public function __destruct()
    {
        $this->cache->stopCacheCleanupLoop();
        EventLoop::queue($this->cache->flushCache(...));
    }

    public function flushCache(): void
    {
        $this->cache->flushCache();
    }

    #[\Override]
    public function count(): int
    {
        return $this->cache->count();
    }

    #[\Override]
    public function clear(): void
    {
        $this->cache->clear();
    }

    /** @param TKey $key */
    #[\Override]
    public function get(mixed $key): mixed
    {
        return $this->cache->get($key);
    }

    /**
     * @param TKey $key
     * @param TValue $value
     *
     * @psalm-capabilities read-props|write-this-props|write-props|write-refs
     */
    #[\Override]
    public function set(string|int $key, mixed $value): void
    {
        $this->cache->set($key, $value);
    }

    /**
     * @param TKey $key
     *
     * @psalm-capabilities read-props|write-this-props|write-props|write-refs
     */
    #[\Override]
    public function unset(string|int $key): void
    {
        $this->cache->set($key, null);
    }

    /** @return Traversable<TKey, TValue> */
    #[\Override]
    public function getIterator(): Traversable
    {
        return $this->cache->getIterator();
    }
}
<?php declare(strict_types=1);

/**
 * Copyright 2024 Daniil Gentili.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @author    Alexander Pankratov <alexander@i-c-a.su>
 * @copyright 2016-2024 Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2024 Alexander Pankratov <alexander@i-c-a.su>
 * @license   https://opensource.org/license/apache-2-0 Apache 2.0
 * @link https://github.com/danog/AsyncOrm AsyncOrm documentation
 */

namespace danog\AsyncOrm\Internal\Driver;

use Amp\Postgres\PostgresConnectionPool;
use Amp\Sync\LocalKeyedMutex;
use danog\AsyncOrm\DbArrayBuilder;
use danog\AsyncOrm\Driver\SqlArray;
use danog\AsyncOrm\Internal\Serializer\ByteaSerializer;
use danog\AsyncOrm\Internal\Serializer\Passthrough;
use danog\AsyncOrm\KeyType;
use danog\AsyncOrm\Serializer;
use danog\AsyncOrm\Settings\PostgresSettings;
use danog\AsyncOrm\ValueType;

/**
 * Postgres database backend.
 *
 * @internal
 * @template TKey as array-key
 * @template TValue
 * @extends SqlArray<TKey, TValue>
 */
final class PostgresArray extends SqlArray
{
    /** @var array<PostgresConnectionPool> */
    private static array $connections = [];

    private static ?LocalKeyedMutex $mutex = null;
    /**
     * @psalm-suppress MethodSignatureMismatch
     * @param Serializer<TValue> $serializer
     */
    public function __construct(DbArrayBuilder $config, Serializer $serializer)
    {
        self::$mutex ??= new LocalKeyedMutex;
        $settings = $config->settings;
        \assert($settings instanceof PostgresSettings);

        $dbKey = $settings->getDbIdentifier();
        $_ = self::$mutex->acquire($dbKey);

        if (!isset(self::$connections[$dbKey])) {
            try {
                $db = $settings->config->getDatabase();
                $user = $settings->config->getUser();
                $connection =  new PostgresConnectionPool($settings->config->withDatabase(null));

                $result = $connection->query("SELECT * FROM pg_database WHERE datname = '{$db}'");

                // Replace with getRowCount once it gets fixed
                if (!\iterator_count($result)) {
                    $connection->query("
                        CREATE DATABASE {$db}
                        OWNER {$user}
                        ENCODING utf8
                    ");
                }
                $connection->close();
            } catch (\Throwable) {
            }
            self::$connections[$dbKey] = new PostgresConnectionPool($settings->config, $settings->maxConnections, $settings->idleTimeout);
        }

        $connection = self::$connections[$dbKey];

        $keyType = match ($config->keyType) {
            KeyType::STRING_OR_INT => "VARCHAR(255)",
            KeyType::STRING => "VARCHAR(255)",
            KeyType::INT => "BIGINT",
        };
        $valueType = match ($config->valueType) {
            ValueType::INT => "BIGINT",
            ValueType::STRING => "VARCHAR(255)",
            ValueType::FLOAT => "FLOAT(53)",
            ValueType::BOOL => "BOOLEAN",
            ValueType::SCALAR, ValueType::OBJECT => "BYTEA",
        };
        /** @var Serializer<TValue> */
        $serializer = match ($config->valueType) {
            ValueType::SCALAR, ValueType::OBJECT => new ByteaSerializer($serializer),
            default => new Passthrough
        };

        /** @psalm-suppress InvalidArgument */
        parent::__construct(
            $config,
            $serializer,
            $connection,
            "SELECT value FROM \"bytea_{$config->table}\" WHERE key = :index",
            "
                INSERT INTO \"bytea_{$config->table}\"
                (key,value)
                VALUES (:index, :value)
                ON CONFLICT (key) DO UPDATE SET value = :value
            ",
            "
                DELETE FROM \"bytea_{$config->table}\"
                WHERE key = :index
            ",
            "SELECT count(key) as count FROM \"bytea_{$config->table}\"",
            "SELECT key, value FROM \"bytea_{$config->table}\"",
            "DELETE FROM \"bytea_{$config->table}\""
        );

        $connection->query("
            CREATE TABLE IF NOT EXISTS \"bytea_{$config->table}\"
            (
                \"key\" $keyType PRIMARY KEY NOT NULL,
                \"value\" $valueType NOT NULL
            );            
        ");

        $result = $connection->query("SELECT * FROM information_schema.columns WHERE table_name='bytea_{$config->table}'");
        while ($column = $result->fetchRow()) {
            ['column_name' => $key, 'data_type' => $type, 'is_nullable' => $null] = $column;
            \assert(\is_string($key));
            \assert(\is_string($type));
            $type = \strtoupper($type);
            if (\str_starts_with($type, 'BIGINT')) {
                $type = 'BIGINT';
            }
            if ($key === 'key') {
                $expected = $keyType;
            } elseif ($key === 'value') {
                $expected = $valueType;
            } else {
                // @codeCoverageIgnoreStart
                $connection->query("ALTER TABLE \"bytea_{$config->table}\" DROP \"$key\"");
                continue;
                // @codeCoverageIgnoreEnd
            }
            if ($expected !== $type) {
                if ($expected === 'BIGINT') {
                    $expected .= " USING $key::bigint";
                }
                $connection->query("ALTER TABLE \"bytea_{$config->table}\" ALTER COLUMN \"$key\" TYPE $expected");
            }
            if ($null !== 'NO') {
                // @codeCoverageIgnoreStart
                $connection->query("ALTER TABLE \"bytea_{$config->table}\" ALTER COLUMN \"$key\" SET NOT NULL");
                // @codeCoverageIgnoreEnd
            }
        }
    }

    /** @psalm-impure */
    #[\Override]
    protected function getDatabaseIdentity(): ?string
    {
        try {
            // The cluster's system identifier is unique per initdb.
            $row = $this->db->query(
                "SELECT system_identifier::text || chr(1) || current_database() AS id FROM pg_control_system()"
            )->fetchRow();
        } catch (\Throwable) {
            // pg_control_system() may be restricted: then it can't be told.
            return null;
        }
        return isset($row['id']) && \is_string($row['id']) ? $row['id'] : null;
    }

    #[\Override]
    protected function importFromTable(string $fromTable): void
    {
        if ($this->config->table === $fromTable) {
            return;
        }

        $this->db->query(/** @lang PostgreSQL */ "
            DROP TABLE \"bytea_{$this->config->table}\";
        ");
        $this->db->query(/** @lang PostgreSQL */ "
            ALTER TABLE \"bytea_$fromTable\" RENAME TO \"bytea_{$this->config->table}\";
        ");
    }
}
<?php declare(strict_types=1);

/**
 * Copyright 2024 Daniil Gentili.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @author    Alexander Pankratov <alexander@i-c-a.su>
 * @copyright 2016-2024 Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2024 Alexander Pankratov <alexander@i-c-a.su>
 * @license   https://opensource.org/license/apache-2-0 Apache 2.0
 * @link https://github.com/danog/AsyncOrm AsyncOrm documentation
 */

namespace danog\AsyncOrm\Internal\Driver;

use AssertionError;
use danog\AsyncOrm\DbArray;
use danog\AsyncOrm\DbArrayBuilder;
use danog\AsyncOrm\DbObject;
use danog\AsyncOrm\Internal\Containers\ObjectContainer;
use danog\AsyncOrm\Settings\DriverSettings;
use Traversable;

/**
 * Object caching proxy.
 *
 * @internal
 *
 * @template TKey as array-key
 * @template TValue as DbObject
 *
 * @extends DbArray<TKey, TValue>
 */
final class ObjectArray extends DbArray
{
    /** @var ObjectContainer<TKey, TValue> */
    private readonly ObjectContainer $cache;

    /**
     * Get instance.
     *
     * @template TTKey as array-key
     * @template TTValue as DbObject
     *
     * @param DbArray<TTKey, TValue>|null $previous
     * @return DbArray<TTKey, TValue>
     */
    #[\Override]
    public static function getInstance(DbArrayBuilder $config, DbArray|null $previous): DbArray
    {
        $new = $config->settings->getDriverClass();
        \assert($config->settings instanceof DriverSettings);
        if ($previous === null) {
            $previous = new self($new::getInstance($config, null), $config, $config->settings->cacheTtl);
        } elseif ($previous instanceof self) {
            $previous->cache->inner = $new::getInstance($config, $previous->cache->inner);
            $previous->cache->config = $config;
            $previous->cache->cacheTtl = $config->settings->cacheTtl;
        } else {
            // @codeCoverageIgnoreStart
            throw new AssertionError("Impossible!");
            // @codeCoverageIgnoreEnd
        }
        $previous->cache->startCacheCleanupLoop();
        /** @var DbArray<TTKey, TValue> */
        return $previous;
    }

    /** @param DbArray<TKey, TValue> $inner */
    public function __construct(DbArray $inner, DbArrayBuilder $config, int $cacheTtl)
    {
        $this->cache = new ObjectContainer($inner, $config, $cacheTtl);
    }

    public function __destruct()
    {
        $this->cache->stopCacheCleanupLoop();
    }

    /** @api */
    #[\Override]
    public function count(): int
    {
        return $this->cache->count();
    }

    #[\Override]
    public function clear(): void
    {
        $this->cache->clear();
    }

    /**
     * @param TKey $key
     * @return TValue
     */
    #[\Override]
    public function get(mixed $key): mixed
    {
        return $this->cache->get($key);
    }

    /**
     * @param TKey $key
     * @param TValue $value
     */
    #[\Override]
    public function set(string|int $key, mixed $value): void
    {
        $this->cache->set($key, $value);
    }

    /** @param TKey $key */
    #[\Override]
    public function unset(string|int $key): void
    {
        $this->cache->unset($key);
    }

    /** @return Traversable<TKey, TValue> */
    #[\Override]
    public function getIterator(): Traversable
    {
        return $this->cache->getIterator();
    }
}
<?php declare(strict_types=1);

/**
 * Copyright 2024 Daniil Gentili.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @author    Alexander Pankratov <alexander@i-c-a.su>
 * @copyright 2016-2024 Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2024 Alexander Pankratov <alexander@i-c-a.su>
 * @license   https://opensource.org/license/apache-2-0 Apache 2.0
 * @link https://github.com/danog/AsyncOrm AsyncOrm documentation
 */

namespace danog\AsyncOrm\Internal\Driver;

use Amp\Redis\Connection\ReconnectingRedisLink;
use Amp\Redis\RedisClient;
use Amp\Sync\LocalKeyedMutex;
use danog\AsyncOrm\DbArrayBuilder;
use danog\AsyncOrm\Driver\DriverArray;
use danog\AsyncOrm\Internal\Serializer\BoolString;
use danog\AsyncOrm\Internal\Serializer\FloatString;
use danog\AsyncOrm\Internal\Serializer\IntString;
use danog\AsyncOrm\Internal\Serializer\Passthrough;
use danog\AsyncOrm\KeyType;
use danog\AsyncOrm\Serializer;
use danog\AsyncOrm\Settings\RedisSettings;
use danog\AsyncOrm\ValueType;
use Revolt\EventLoop;

use function Amp\Redis\createRedisConnector;

/**
 * Redis database backend.
 *
 * @internal
 *
 * @template TKey as array-key
 * @template TValue
 * @extends DriverArray<TKey, TValue>
 */
final class RedisArray extends DriverArray
{
    /** @var array<RedisClient> */
    private static array $connections = [];
    private static ?LocalKeyedMutex $mutex = null;

    private readonly RedisClient $db;
    private readonly bool $castToInt;

    /**
     * @api
     * @param Serializer<TValue> $serializer
     */
    public function __construct(DbArrayBuilder $config, Serializer $serializer)
    {
        /** @var Serializer<TValue> */
        $serializer = match ($config->valueType) {
            ValueType::INT => new IntString,
            ValueType::FLOAT => new FloatString,
            ValueType::BOOL => new BoolString,
            ValueType::SCALAR, ValueType::OBJECT => $serializer,
            default => new Passthrough
        };
        $this->castToInt = $config->keyType === KeyType::INT;
        parent::__construct($config, $serializer);

        self::$mutex ??= new LocalKeyedMutex;
        \assert($config->settings instanceof RedisSettings);
        $dbKey = $config->settings->getDbIdentifier();
        $lock = self::$mutex->acquire($dbKey);

        try {
            if (!isset(self::$connections[$dbKey])) {
                self::$connections[$dbKey] = new RedisClient(new ReconnectingRedisLink(createRedisConnector($config->settings->config)));
                self::$connections[$dbKey]->ping();
            }
        } finally {
            EventLoop::queue($lock->release(...));
        }
        $this->db = self::$connections[$dbKey];
    }

    #[\Override]
    public function set(string|int $key, mixed $value): void
    {
        /** @var string */
        $value = $this->serializer->serialize($value);
        $this->db->set($this->config->table.':'.(string) $key, $value);
    }

    #[\Override]
    public function get(string|int $key): mixed
    {
        $key = (string) $key;

        $value = $this->db->get($this->config->table.':'.$key);

        if ($value !== null) {
            /** @var TValue */
            $value = $this->serializer->deserialize($value);
        }

        return $value;
    }

    #[\Override]
    public function unset(string|int $key): void
    {
        $this->db->delete($this->config->table.':'.(string) $key);
    }

    /**
     * Get iterator.
     *
     * @return \Traversable<array-key, mixed>
     */
    #[\Override]
    public function getIterator(): \Traversable
    {
        $request = $this->db->scan($this->config->table.':*');

        $len = \strlen($this->config->table)+1;
        foreach ($request as $key) {
            $sub = \substr($key, $len);
            if ($this->castToInt) {
                $sub = (int) $sub;
            }
            yield $sub => $this->serializer->deserialize($this->db->get($key));
        }
    }

    /**
     * Count elements.
     *
     * @api
     *
     * @link https://php.net/manual/en/arrayiterator.count.php
     * @return int The number of elements or public properties in the associated
     *             array or object, respectively.
     */
    #[\Override]
    public function count(): int
    {
        return \iterator_count($this->db->scan($this->config->table.':*'));
    }

    /**
     * Clear all elements.
     */
    #[\Override]
    public function clear(): void
    {
        $request = $this->db->scan($this->config->table.':*');

        $keys = [];
        foreach ($request as $key) {
            $keys[] = $key;
            if (\count($keys) === 10) {
                // @codeCoverageIgnoreStart
                $this->db->delete(...$keys);
                $keys = [];
                // @codeCoverageIgnoreEnd
            }
        }
        if ($keys) {
            $this->db->delete(...$keys);
        }
    }
}
<?php declare(strict_types=1);

/**
 * Copyright 2024 Daniil Gentili.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @author    Alexander Pankratov <alexander@i-c-a.su>
 * @copyright 2016-2024 Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2024 Alexander Pankratov <alexander@i-c-a.su>
 * @license   https://opensource.org/license/apache-2-0 Apache 2.0
 * @link https://github.com/danog/AsyncOrm AsyncOrm documentation
 */

namespace danog\AsyncOrm\Internal\Driver;

use Amp\Mysql\MysqlConnectionPool;
use Amp\Sql\SqlResult;
use Amp\Sync\LocalKeyedMutex;
use AssertionError;
use danog\AsyncOrm\DbArrayBuilder;
use danog\AsyncOrm\Driver\Mysql;
use danog\AsyncOrm\Driver\SqlArray;
use danog\AsyncOrm\Internal\Serializer\BoolInt;
use danog\AsyncOrm\Internal\Serializer\Passthrough;
use danog\AsyncOrm\KeyType;
use danog\AsyncOrm\Serializer;
use danog\AsyncOrm\ValueType;
use PDO;
use Revolt\EventLoop;

/**
 * MySQL database backend.
 *
 * @internal
 *
 * @template TKey as array-key
 * @template TValue
 * @extends SqlArray<TKey, TValue>
 */
final class MysqlArray extends SqlArray
{
    /** @var array<list{MysqlConnectionPool, \PDO}> */
    private static array $connections = [];

    private static ?LocalKeyedMutex $mutex = null;

    // We're forced to use quoting (just like PDO does internally when using prepares) because native MySQL prepares are extremely slow.
    protected PDO $pdo;

    /**
     * @psalm-suppress MethodSignatureMismatch
     * @param Serializer<TValue> $serializer
     */
    public function __construct(DbArrayBuilder $config, Serializer $serializer)
    {
        $settings = $config->settings;
        \assert($settings instanceof \danog\AsyncOrm\Settings\MysqlSettings);

        self::$mutex ??= new LocalKeyedMutex;
        $dbKey = $settings->getDbIdentifier();
        $lock = self::$mutex->acquire($dbKey);

        try {
            if (!isset(self::$connections[$dbKey])) {
                $db = $settings->config->getDatabase();
                $connection = new MysqlConnectionPool($settings->config->withDatabase(null), 1);
                $connection->query("
                    CREATE DATABASE IF NOT EXISTS `{$db}`
                    CHARACTER SET 'utf8mb4' 
                    COLLATE 'utf8mb4_general_ci'
                ");
                try {
                    $max = $connection->query("SHOW VARIABLES LIKE 'max_connections'")->fetchRow();
                    \assert(\is_array($max));
                    $max = $max['Value'] ?? null;
                    \assert(\is_int($max));
                    if ($max < 100000) {
                        $connection->query("SET GLOBAL max_connections = 100000");
                    }
                    // @codeCoverageIgnoreStart
                } catch (\Throwable) {
                }
                // @codeCoverageIgnoreEnd
                $connection->close();

                $host = $settings->config->getHost();
                $port = $settings->config->getPort();
                // @codeCoverageIgnoreStart
                if (!\extension_loaded('pdo_mysql')) {
                    throw new AssertionError("PDO is needed for the mysql backend!");
                }

                $pdo = new PDO(
                    $host[0] === '/'
                        ? "mysql:unix_socket={$host};charset=UTF8"
                        : "mysql:host={$host};port={$port};charset=UTF8",
                    $settings->config->getUser(),
                    $settings->config->getPassword(),
                );
                // @codeCoverageIgnoreEnd

                self::$connections[$dbKey] = [
                    new MysqlConnectionPool($settings->config, $settings->maxConnections, $settings->idleTimeout),
                    $pdo,
                ];
            }
        } finally {
            EventLoop::queue($lock->release(...));
        }

        [$db, $pdo] = self::$connections[$dbKey];
        $this->pdo = $pdo;

        $keyType = match ($config->keyType) {
            KeyType::STRING_OR_INT => "VARCHAR(255)",
            KeyType::STRING => "VARCHAR(255)",
            KeyType::INT => "BIGINT",
        };
        $valueType = match ($config->valueType) {
            ValueType::INT => "BIGINT",
            ValueType::STRING => "VARCHAR(255)",
            ValueType::OBJECT => "MEDIUMBLOB",
            ValueType::FLOAT => "FLOAT(53)",
            ValueType::BOOL => "BIT(1)",
            ValueType::SCALAR, ValueType::OBJECT => "MEDIUMBLOB"
        };
        /** @var Serializer<TValue> */
        $serializer = match ($config->valueType) {
            ValueType::SCALAR, ValueType::OBJECT => $serializer,
            ValueType::BOOL => new BoolInt,
            default => new Passthrough
        };
        /** @psalm-suppress InvalidArgument */
        parent::__construct(
            $config,
            $serializer,
            $db,
            "SELECT `value` FROM `{$config->table}` WHERE `key` = :index LIMIT 1",
            "
                REPLACE INTO `{$config->table}` 
                SET `key` = :index, `value` = :value 
            ",
            "
                DELETE FROM `{$config->table}`
                WHERE `key` = :index
            ",
            "
                SELECT count(`key`) as `count` FROM `{$config->table}`
            ",
            "
                SELECT `key`, `value` FROM `{$config->table}`
            ",
            "
                DELETE FROM `{$config->table}`
            "
        );

        $db->query("
            CREATE TABLE IF NOT EXISTS `{$config->table}`
            (
                `key` $keyType PRIMARY KEY NOT NULL,
                `value` $valueType NOT NULL
            )
            ENGINE = InnoDB
            CHARACTER SET 'utf8mb4' 
            COLLATE 'utf8mb4_general_ci'
        ");

        $result = $db->query("DESCRIBE `{$config->table}`");
        while ($column = $result->fetchRow()) {
            ['Field' => $key, 'Type' => $type, 'Null' => $null] = $column;
            \assert(\is_string($key));
            \assert(\is_string($type));
            $type = \strtoupper($type);
            if (\str_starts_with($type, 'BIGINT')) {
                $type = 'BIGINT';
            }
            if ($key === 'key') {
                $expected = $keyType;
            } elseif ($key === 'value') {
                $expected = $valueType;
            } else {
                // @codeCoverageIgnoreStart
                $db->query("ALTER TABLE `{$config->table}` DROP `$key`");
                continue;
                // @codeCoverageIgnoreEnd
            }
            if ($expected !== $type || $null !== 'NO') {
                $db->query("ALTER TABLE `{$config->table}` MODIFY `$key` $expected NOT NULL");
            }
        }

        if ($settings->optimizeIfWastedMb !== null) {
            $database = $settings->config->getDatabase();
            $result = $db->prepare("SELECT data_free FROM information_schema.tables WHERE table_schema=? AND table_name=?")
                ->execute([$database, $config->table])
                ->fetchRow();
            if ($result === null) {
                // @codeCoverageIgnoreStart
                throw new AssertionError("Result cannot be null!");
                // @codeCoverageIgnoreEnd
            }
            $result = $result['data_free'] ?? $result['DATA_FREE'];
            if (!\is_int($result)) {
                // @codeCoverageIgnoreStart
                throw new AssertionError("data_free must be an integer!");
                // @codeCoverageIgnoreEnd
            }
            if (($result >> 20) >= $settings->optimizeIfWastedMb) {
                $db->query("OPTIMIZE TABLE `{$config->table}`");
            }
        }
    }

    /** @psalm-impure */
    #[\Override]
    protected function getDatabaseIdentity(): ?string
    {
        $row = $this->db->query(
            "SELECT CONCAT_WS(CHAR(0), @@hostname, @@port, @@datadir, @@server_id, DATABASE()) AS id"
        )->fetchRow();
        return isset($row['id']) && \is_string($row['id']) ? $row['id'] : null;
    }

    /**
     * Perform async request to db.
     */
    #[\Override]
    protected function execute(string $sql, array $params = []): SqlResult
    {
        foreach ($params as $key => $value) {
            if (\is_string($value)) {
                $value = $this->pdo->quote($value);
            } else {
                $value = (string) $value;
            }
            $sql = \str_replace(":$key", $value, $sql);
        }

        return $this->db->query($sql);
    }

    #[\Override]
    protected function importFromTable(string $fromTable): void
    {
        if ($this->config->table === $fromTable) {
            return;
        }

        $this->db->query("
            REPLACE INTO `{$this->config->table}`
            SELECT * FROM `{$fromTable}`;
        ");

        $this->db->query("
            DROP TABLE `{$fromTable}`;
        ");
    }
}
<?php declare(strict_types=1);

/**
 * This file is part of AsyncOrm.
 * AsyncOrm is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General private License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * AsyncOrm is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General private License for more details.
 * You should have received a copy of the GNU General private License along with AsyncOrm.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2024 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/license/apache-2-0 Apache 2.0
 * @link https://docs.AsyncOrm.xyz AsyncOrm documentation
 */

namespace danog\AsyncOrm\Annotations;

use Attribute;
use danog\AsyncOrm\KeyType;
use danog\AsyncOrm\Serializer;
use danog\AsyncOrm\ValueType;

/**
 * Attribute use to autoconfigure ORM properties.
 *
 * @api
 *
 * @psalm-immutable
 */
#[Attribute(Attribute::TARGET_PROPERTY)]
final class OrmMappedArray
{
    public function __construct(
        /**
         * Key type.
         */
        public readonly KeyType $keyType,
        /**
         * Value type.
         */
        public readonly ValueType $valueType,
        /**
         * TTL of the cache, if null defaults to the value specified in the settings.
         *
         * If zero disables caching.
         *
         * @var int<0, max>|null
         */
        public readonly ?int $cacheTtl = null,
        /**
         * Optimize table if more than this many megabytes are wasted, if null defaults to the value specified in the settings.
         *
         * @var int<1, max>|null
         */
        public readonly ?int $optimizeIfWastedMb = null,
        /**
         * Table name postfix, if null defaults to the property name.
         */
        public readonly ?string $tablePostfix = null,
        /**
         * Provide custom serializer for table.
         */
        public readonly ?Serializer $serializer = null,
    ) {
    }
}
<?php declare(strict_types=1);

/**
 * Copyright 2024 Daniil Gentili.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2023 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/license/apache-2-0 Apache 2.0
 * @link https://github.com/danog/AsyncOrm AsyncOrm documentation
 */

namespace danog\AsyncOrm;

/**
 * Specifies the type of keys.
 */
enum KeyType: string
{
    /**
     * Strings or integers.
     */
    case STRING_OR_INT = 'string_or_int';
    /**
     * Only strings.
     */
    case STRING = 'string';
    /**
     * Only integers.
     */
    case INT = 'int';
}
<?php declare(strict_types=1);

/**
 * Copyright 2024 Daniil Gentili.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2023 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/license/apache-2-0 Apache 2.0
 * @link https://github.com/danog/AsyncOrm AsyncOrm documentation
 */

namespace danog\AsyncOrm\Settings;

use danog\AsyncOrm\Serializer;
use danog\AsyncOrm\Serializer\Igbinary;
use danog\AsyncOrm\Serializer\Native;
use danog\AsyncOrm\Settings;

/**
 * Base settings class for database backends.
 */
abstract readonly class DriverSettings implements Settings
{
    final public const DEFAULT_CACHE_TTL = 5*60;
    public readonly Serializer $serializer;
    /**
     * @param ?Serializer $serializer to use for object and mixed type values, if null defaults to either Igbinary or Native.
     *
     * @psalm-capabilities read-props
     */
    public function __construct(
        ?Serializer $serializer = null,
        /**
         * @var int<0, max> $cacheTtl For how long to keep records in memory after last read.
         */
        public int $cacheTtl = self::DEFAULT_CACHE_TTL,
    ) {
        $this->serializer = $serializer ?? (\extension_loaded('igbinary') ? new Igbinary : new Native);
    }

    /**
     * Get the DB's unique ID.
     *
     * @internal
     *
     * @psalm-capabilities read-props
     */
    abstract public function getDbIdentifier(): string;
}
<?php declare(strict_types=1);

/**
 * Copyright 2024 Daniil Gentili.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2023 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/license/apache-2-0 Apache 2.0
 * @link https://github.com/danog/AsyncOrm AsyncOrm documentation
 */

namespace danog\AsyncOrm\Settings;

use Amp\Sql\SqlConfig;
use danog\AsyncOrm\Serializer;

/**
 * Generic SQL db backend settings.
 *
 * @template T as SqlConfig
 */
abstract readonly class SqlSettings extends DriverSettings
{
    final public const DEFAULT_SQL_MAX_CONNECTIONS = 100;
    final public const DEFAULT_SQL_IDLE_TIMEOUT = 60;
    /**
     * Maximum connection limit.
     *
     * @var positive-int
     */
    public int $maxConnections;
    /**
     * Idle timeout.
     *
     * @var positive-int
     */
    public int $idleTimeout;

    /**
     * @param ?Serializer $serializer to use for object and mixed type values.
     * @param int<0, max> $cacheTtl Cache TTL in seconds
     * @param int<1, max> $maxConnections Maximum connection limit
     * @param int<1, max> $idleTimeout Idle timeout
     *
     * @psalm-capabilities read-props
     */
    public function __construct(
        /** @var T */
        public readonly SqlConfig $config,
        ?Serializer $serializer,
        int $cacheTtl = self::DEFAULT_CACHE_TTL,
        int $maxConnections = self::DEFAULT_SQL_MAX_CONNECTIONS,
        int $idleTimeout = self::DEFAULT_SQL_IDLE_TIMEOUT,
    ) {
        parent::__construct($serializer, $cacheTtl);
        $this->maxConnections = $maxConnections;
        $this->idleTimeout = $idleTimeout;
    }
    /**
     * @internal
     *
     * @psalm-capabilities read-props
     */
    #[\Override]
    public function getDbIdentifier(): string
    {
        $host = $this->config->getHost();
        $port = $this->config->getPort();
        return "$host:$port:".(string) $this->config->getDatabase();
    }
}
<?php declare(strict_types=1);

/**
 * Copyright 2024 Daniil Gentili.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2023 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/license/apache-2-0 Apache 2.0
 * @link https://github.com/danog/AsyncOrm AsyncOrm documentation
 */

namespace danog\AsyncOrm\Settings;

use Amp\Postgres\PostgresConfig;
use danog\AsyncOrm\Internal\Driver\PostgresArray;
use danog\AsyncOrm\Serializer;

/**
 * Postgres backend settings.
 * @extends SqlSettings<PostgresConfig>
 */
final readonly class PostgresSettings extends SqlSettings
{
    /**
     * @api
     *
     * @param ?Serializer $serializer to use for object and mixed type values, if null defaults to either Igbinary or Native.
     * @param int<0, max> $cacheTtl Cache TTL in seconds
     * @param int<1, max> $maxConnections Maximum connection limit
     * @param int<1, max> $idleTimeout Idle timeout
     *
     * @psalm-capabilities read-props
     */
    public function __construct(
        PostgresConfig $config,
        ?Serializer $serializer = null,
        int $cacheTtl = self::DEFAULT_CACHE_TTL,
        int $maxConnections = self::DEFAULT_SQL_MAX_CONNECTIONS,
        int $idleTimeout = self::DEFAULT_SQL_IDLE_TIMEOUT,
    ) {
        parent::__construct($config, $serializer, $cacheTtl, $maxConnections, $idleTimeout);
    }

    /**
     * @psalm-pure
     */
    #[\Override]
    public function getDriverClass(): string
    {
        return PostgresArray::class;
    }
}
<?php declare(strict_types=1);

/**
 * Copyright 2024 Daniil Gentili.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2023 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/license/apache-2-0 Apache 2.0
 * @link https://github.com/danog/AsyncOrm AsyncOrm documentation
 */

namespace danog\AsyncOrm\Settings;

use danog\AsyncOrm\Driver\MemoryArray;
use danog\AsyncOrm\Settings;

/**
 * MemorySettings backend settings.
 */
final readonly class MemorySettings implements Settings
{
    /**
     * @psalm-pure
     */
    #[\Override]
    public function getDriverClass(): string
    {
        return MemoryArray::class;
    }
}
<?php declare(strict_types=1);

/**
 * Copyright 2024 Daniil Gentili.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2023 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/license/apache-2-0 Apache 2.0
 * @link https://github.com/danog/AsyncOrm AsyncOrm documentation
 */

namespace danog\AsyncOrm\Settings;

use Amp\Redis\RedisConfig;
use danog\AsyncOrm\Internal\Driver\RedisArray;
use danog\AsyncOrm\Serializer;

/**
 * Redis backend settings.
 */
final readonly class RedisSettings extends DriverSettings
{
    /**
     * @api
     *
     * @param ?Serializer $serializer to use for object and mixed type values, if null defaults to either Igbinary or Native.
     * @param int<0, max> $cacheTtl Cache TTL in seconds
     *
     * @psalm-capabilities read-props
     */
    public function __construct(
        public readonly RedisConfig $config,
        ?Serializer $serializer = null,
        int $cacheTtl = self::DEFAULT_CACHE_TTL,
    ) {
        parent::__construct($serializer, $cacheTtl);
    }
    /**
     * @internal
     *
     * @psalm-capabilities read-props
     */
    #[\Override]
    public function getDbIdentifier(): string
    {
        $host = $this->config->getConnectUri();
        return "$host\0".$this->config->getDatabase();
    }
    /**
     * @psalm-pure
     */
    #[\Override]
    public function getDriverClass(): string
    {
        return RedisArray::class;
    }
}
<?php declare(strict_types=1);

/**
 * Copyright 2024 Daniil Gentili.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2023 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/license/apache-2-0 Apache 2.0
 * @link https://github.com/danog/AsyncOrm AsyncOrm documentation
 */

namespace danog\AsyncOrm\Settings;

use Amp\Mysql\MysqlConfig;
use danog\AsyncOrm\Internal\Driver\MysqlArray;
use danog\AsyncOrm\Serializer;

/**
 * MySQL backend settings.
 *
 * MariaDb 10.2+ or Mysql 5.6+ required.
 *
 * @extends SqlSettings<MysqlConfig>
 */
final readonly class MysqlSettings extends SqlSettings
{
    /**
     * @api
     *
     * @param ?Serializer $serializer to use for object and mixed type values, if null defaults to either Igbinary or Native.
     * @param int<0, max> $cacheTtl Cache TTL in seconds, if 0 disables caching.
     * @param int<1, max> $maxConnections Maximum connection limit
     * @param int<1, max> $idleTimeout Idle timeout
     *
     * @psalm-capabilities read-props
     */
    public function __construct(
        MysqlConfig $config,
        ?Serializer $serializer = null,
        int $cacheTtl = self::DEFAULT_CACHE_TTL,
        int $maxConnections = self::DEFAULT_SQL_MAX_CONNECTIONS,
        int $idleTimeout = self::DEFAULT_SQL_IDLE_TIMEOUT,
        /**
         *
         * Whether to optimize MySQL tables automatically if more than the specified amount of megabytes is wasted by the MySQL engine.
         *
         * Be careful when tweaking this setting as it may lead to slowdowns on startup.
         *
         * If null disables optimization.
         *
         * @var int<1, max>|null $optimizeIfWastedMb
         */
        public ?int $optimizeIfWastedMb = null,
    ) {
        parent::__construct($config, $serializer, $cacheTtl, $maxConnections, $idleTimeout);
    }

    /**
     * @psalm-pure
     */
    #[\Override]
    public function getDriverClass(): string
    {
        return MysqlArray::class;
    }
}
<?php declare(strict_types=1);
/**
 * Copyright 2024 Daniil Gentili.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2024 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/license/apache-2-0 Apache 2.0
 * @link https://github.com/danog/AsyncOrm AsyncOrm documentation
 */

namespace danog\AsyncOrm;

use AssertionError;
use danog\AsyncOrm\Internal\Driver\CachedArray;
use danog\AsyncOrm\Internal\Driver\ObjectArray;
use danog\AsyncOrm\Serializer\Json;
use danog\AsyncOrm\Settings\DriverSettings;
use danog\AsyncOrm\Settings\MemorySettings;

/**
 * Contains configuration needed to build a DbArray.
 *
 * @api
 */
final readonly class DbArrayBuilder
{
    public function __construct(
        /**
         * Table name.
         */
        public readonly string $table,
        /**
         * Settings.
         */
        public readonly Settings $settings,
        /**
         * Key type.
         */
        public readonly KeyType $keyType,
        /**
         * Value type.
         */
        public readonly ValueType $valueType,
    ) {
    }

    /**
     * Build database array.
     *
     * @template TKey as array-key
     * @template TValue
     *
     * @param DbArray<TKey, TValue>|null $previous
     * @return DbArray<TKey, TValue>
     */
    public function build(?DbArray $previous = null): DbArray
    {
        if ($this->valueType === ValueType::OBJECT) {
            if (!$this->settings instanceof DriverSettings) {
                throw new AssertionError("Objects can only be saved to a database backend!");
            }
            if ($this->settings->serializer instanceof Json) {
                throw new AssertionError("The JSON backend cannot be used when serializing objects!");
            }
            /** @psalm-suppress MixedArgumentTypeCoercion */
            return ObjectArray::getInstance($this, $previous);
        }
        if ($this->settings instanceof MemorySettings
            || (
                $this->settings instanceof DriverSettings
                && $this->settings->cacheTtl === 0
            )
        ) {
            return $this->settings->getDriverClass()::getInstance($this, $previous);
        }

        return CachedArray::getInstance($this, $previous);
    }
}
{
    "name": "danog/async-orm",
    "description": "Async ORM based on AMPHP v3 and fibers.",
    "type": "library",
    "license": "Apache-2.0",
    "autoload": {
        "psr-4": {
            "danog\\AsyncOrm\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "danog\\TestAsyncOrm\\": "tests/"
        }
    },
    "authors": [
        {
            "name": "Daniil Gentili",
            "email": "daniil@daniil.it"
        },
        {
            "name": "Alexander Pankratov",
            "email": "alexander@i-c-a.su"
        }
    ],
    "require": {
        "php": ">=8.2.4",
        "amphp/mysql": "^3.0",
        "amphp/postgres": "^2.0",
        "amphp/redis": "^2.0",
        "amphp/sync": "^2.2",
        "revolt/event-loop": "^1.0.6",
        "symfony/polyfill-php83": "^1.32"
    },
    "require-dev": {
        "vimeo/psalm": "dev-master",
        "phpunit/phpunit": "^11.0.9",
        "amphp/php-cs-fixer-config": "^2.0.1",
        "friendsofphp/php-cs-fixer": "^3.52.1",
        "amphp/process": "^2.0.2",
        "infection/infection": "^0.28.1",
        "danog/phpdoc": "^0.1.22"
    },
    "scripts": {
        "cs-fix": "PHP_CS_FIXER_IGNORE_ENV=1 php -d pcre.jit=0 vendor/bin/php-cs-fixer fix -v"
    },
    "config": {
        "allow-plugins": {
            "dealerdirect/phpcodesniffer-composer-installer": true,
            "infection/extension-installer": true
        }
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\TURN;

use Psr\Log\LoggerInterface;
use Ramsey\Uuid\Uuid;
use Random\RandomException;
use Amp\Socket\ClientTlsContext;
use Amp\Socket\ConnectContext;
use Amp\Socket\Socket;
use function Amp\Socket\connect;
use Throwable;
use Webrtc\Exception\RuntimeException;
use Webrtc\Mixin\SerializableState;
use Webrtc\STUN\ReceiverInterface;
use Webrtc\STUN\Trait\Request;
use Webrtc\STUN\Utils;
use Webrtc\TURN\Trait\TurnConnection;

/**
 * Class TurnTcpConnection
 * Protocol for handling TURN over TCP.
 */
final class TurnTcpConnection extends TCPConnection implements TurnConnectionInterface
{
    use Request, TurnConnection {
        TurnConnection::sendMessage insteadof Request;
    }

    private string $id;
    private string $buffer = "";

    /**
     * The receiver of the messages, which owns this protocol: not owned by it, so that a pending
     * request doesn't keep the receiver alive.
     *
     * @var \WeakReference<ReceiverInterface>|null
     */
    private ?\WeakReference $receiver;

    /**
     * @param TurnConfigurationInterface $configuration
     * @param ReceiverInterface $receiver
     * @param ?LoggerInterface $logger
     * @param Socket $socket
     */
    public function __construct(private readonly TurnConfigurationInterface $configuration,
                                ReceiverInterface                           $receiver,
                                private readonly ?LoggerInterface            $logger,
                                Socket                                      $socket)
    {
        $this->receiver = \WeakReference::create($receiver);
        parent::__construct($socket);
        $this->id = Uuid::uuid4()->toString();
    }

    /**
     * @param string $transactionId
     * @return void
     */
    #[\Override]
    public function removeTransaction(string $transactionId): void
    {
        unset($this->transactionIds[$transactionId]);
    }

    /**
     * @return string
     */
    #[\Override]
    public function getId(): string
    {
        return $this->id;
    }

    /**
     * Handle received messages.
     *
     * @param string $data The received data.
     * @return void
     * @throws RandomException
     */
    #[\Override]
    public function onTCPReceived(string $data): void
    {
        $this->buffer .= $data;

        while (strlen($this->buffer) >= 4) {
            $unpacked = unpack("nChannel/nLength", substr($this->buffer, 0, 4));
            assert($unpacked !== false);
            $length = (int) $unpacked['Length'];
            $length += Utils::paddingLength($length);

            if ($this->isChannelData($this->buffer)) {
                $fullLength = 4 + $length;
            } else {
                $fullLength = 20 + $length;
            }

            if (strlen($this->buffer) < $fullLength) {
                break;
            }

            $address = $this->getRemoteAddress();
            assert($address !== null);

            $data = substr($this->buffer, 0, $fullLength);
            $this->onReceived($data, $address);
            $this->buffer = substr($this->buffer, $fullLength);
        }
    }

    /**
     * Add pad if needed
     *
     * @param string $data
     * @return string
     */
    #[\Override]
    protected function padded(string $data): string
    {
        $padLen = Utils::paddingLength(strlen($data));
        return $data . str_repeat("\0", $padLen);

    }
    /**
     * Create a TurnTCP instance.
     *
     * @param TurnConfigurationInterface $configuration
     * @param ReceiverInterface $receiver The receiver.
     * @param ?LoggerInterface $logger
     * @return self
     */
    public static function create(TurnConfigurationInterface $configuration, ReceiverInterface $receiver, ?LoggerInterface $logger = null): self
    {
        $turnServer = $configuration->getTurnServer();
        if ($turnServer === null || !isset($turnServer[0], $turnServer[1])) {
            throw new RuntimeException('No TURN server is configured');
        }
        $address = implode(":", array_map(static fn (mixed $part): string => (string) $part, $turnServer));

        try {
            $context = (new ConnectContext())->withTlsContext(
                (new ClientTlsContext((string) $turnServer[0]))->withoutPeerVerification()
            );

            $socket = connect($address, $context);
            if ($configuration->getTurnSsl()) {
                // TURN over TLS starts the handshake immediately rather than after a STARTTLS
                // style upgrade, so it has to happen before the first allocation is sent.
                $socket->setupTls();
            }

            return new static($configuration, $receiver, $logger, $socket);
        } catch (Throwable $e) {
            throw new RuntimeException(sprintf("Could not connect to %s - %s", $address, $e->getMessage()), (int) $e->getCode(), $e);
        }
    }

    #[\Override]
    protected function restoreTcpSocket(?string $remote): void
    {
        $turnServer = $this->configuration->getTurnServer();
        if ($turnServer === null || !isset($turnServer[0], $turnServer[1])) {
            return;
        }
        $address = implode(":", array_map(static fn (mixed $part): string => (string) $part, $turnServer));
        $context = (new ConnectContext())->withTlsContext(
            (new ClientTlsContext((string) $turnServer[0]))->withoutPeerVerification()
        );
        $socket = connect($address, $context);
        if ($this->configuration->getTurnSsl()) {
            $socket->setupTls();
        }
        $this->socket = $socket;
        $this->listen();
    }

    /**
     * @return array<string, mixed>
     */
    #[\Override]
    public function __serialize(): array
    {
        return SerializableState::export($this, [
            'socket' => ['_tcp' => (string) $this->socket->getRemoteAddress()],
            'receiver' => $this->receiver?->get(),
            ...$this->turnSerializeReplacements(),
        ]);
    }

    /**
     * @param array<string, mixed> $data
     */
    #[\Override]
    public function __unserialize(array $data): void
    {
        $restartRefresh = $this->consumeTurnRefreshFlag($data);
        $key = self::class . "\0receiver";
        // Before the socket is listened to again.
        $this->receiver = isset($data[$key]) && $data[$key] instanceof ReceiverInterface ? \WeakReference::create($data[$key]) : null;
        unset($data[$key]);
        parent::__unserialize($data);
        $this->restoreTurnTimers($restartRefresh);
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\TURN;

use Amp\Socket\InternetAddress;
use Psr\Log\LoggerInterface;
use Random\RandomException;
use Webrtc\STUN\IceConnectionProtocolInterface;
use Webrtc\STUN\ReceiverInterface;

interface TurnConnectionInterface extends IceConnectionProtocolInterface
{
    public function connect(): ?InternetAddress;

    public function delete(): void;

    public function getRelayedAddress(): ?InternetAddress;

    public function sendData(string $data, InternetAddress $address): void;

}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\TURN;

interface TurnConfigurationInterface
{
    public function getTurnServer(): array|null;

    public function setTurnServer(array $turnServer): void;

    public function getTurnSsl(): bool;

    public function setTurnSsl(bool $turnSsl): void;

    public function getTurnUsername(): string|null;

    public function setTurnUsername(string $turnUsername): void;

    public function getTurnPassword(): string|null;

    public function setTurnPassword(string $turnPassword): void;

    public function getTurnTransport(): string;

    public function setTurnTransport(string $turnTransport): void;
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\TURN;

use Amp\Socket\InternetAddress;
use Psr\Log\LoggerInterface;
use Ramsey\Uuid\Uuid;
use Amp\Socket\UdpSocket;
use Throwable;
use Webrtc\Exception\RuntimeException;
use Webrtc\Mixin\SerializableState;
use Webrtc\STUN\Datagram;
use Webrtc\STUN\ReceiverInterface;
use Webrtc\STUN\Trait\Request;
use Webrtc\TURN\Trait\TurnConnection;
use function Amp\Socket\bindUdpSocket;

/**
 * Class TurnUDPConnection
 * Protocol for handling TURN over UDP.
 */
final class TurnUdpConnection extends Datagram implements TurnConnectionInterface
{
    use Request, TurnConnection {
        TurnConnection::sendMessage insteadof Request;
    }

    private string $id;

    /**
     * The receiver of the messages, which owns this protocol: not owned by it, so that a pending
     * request doesn't keep the receiver alive.
     *
     * @var \WeakReference<ReceiverInterface>|null
     */
    private ?\WeakReference $receiver;

    /**
     * @param TurnConfigurationInterface $configuration
     * @param ReceiverInterface $receiver
     * @param ?LoggerInterface $logger
     * @param UdpSocket $socket
     */
    public function __construct(private readonly TurnConfigurationInterface $configuration,
                                ReceiverInterface                           $receiver,
                                private readonly ?LoggerInterface            $logger,
                                UdpSocket                                   $socket)
    {
        $this->receiver = \WeakReference::create($receiver);
        $turnServer = $this->configuration->getTurnServer();
        if ($turnServer === null || !isset($turnServer[0], $turnServer[1])) {
            throw new RuntimeException('No TURN server is configured');
        }
        $port = (int) $turnServer[1];
        if ($port < 0 || $port > 65535) {
            throw new RuntimeException('Invalid TURN server port');
        }
        $this->remoteAddress = new InternetAddress((string) $turnServer[0], $port);
        $this->id = Uuid::uuid4()->toString();
        parent::__construct($socket);
    }

    /**
     * @param string $transactionId
     * @return void
     */
    #[\Override]
    public function removeTransaction(string $transactionId): void
    {
        unset($this->transactionIds[$transactionId]);
    }

    /**
     * @return string
     */
    #[\Override]
    public function getId(): string
    {
        return $this->id;
    }

    /**
     * Create a turn udp instance.
     *
     * @param TurnConfigurationInterface $configuration
     * @param ReceiverInterface $receiver The receiver.
     * @param ?LoggerInterface $logger
     * @return self
     */
    public static function create(TurnConfigurationInterface $configuration, ReceiverInterface $receiver, ?LoggerInterface $logger = null): self
    {
        try {
            // Bind an ephemeral local socket; the server address is kept as the default
            // destination rather than connecting, so the same socket can also reach peers.
            $socket = bindUdpSocket('0.0.0.0:0');
            return new static($configuration, $receiver, $logger, $socket);
        } catch (Throwable $e) {
            throw new RuntimeException(sprintf("Could not bind to %s", $e->getMessage()), (int) $e->getCode(), $e);
        }
    }

    /**
     * @return array<string, mixed>
     */
    #[\Override]
    public function __serialize(): array
    {
        $address = $this->socket->getAddress();

        return SerializableState::export($this, [
            'socket' => ['_udp' => [$address->getAddress(), $address->getPort()]],
            'receiver' => $this->receiver?->get(),
            ...$this->turnSerializeReplacements(),
        ]);
    }

    /**
     * @param array<string, mixed> $data
     */
    #[\Override]
    public function __unserialize(array $data): void
    {
        $restartRefresh = $this->consumeTurnRefreshFlag($data);
        $key = self::class . "\0receiver";
        // Before the socket is listened to again.
        $this->receiver = isset($data[$key]) && $data[$key] instanceof ReceiverInterface ? \WeakReference::create($data[$key]) : null;
        unset($data[$key]);
        parent::__unserialize($data);
        $this->restoreTurnTimers($restartRefresh);
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\TURN\Trait;

use Amp\Socket\InternetAddress;
use Exception;
use Random\RandomException;
use Amp\DeferredFuture;
use Amp\Future;
use Revolt\EventLoop;
use Throwable;
use Webrtc\Exception\InvalidArgumentException;
use Webrtc\STUN\Enum\MessageAttribute;
use Webrtc\STUN\Enum\MessageClass;
use Webrtc\STUN\Enum\MessageMethod;
use Webrtc\STUN\Exception\TransactionException;
use Webrtc\STUN\Exception\TransactionExceptionInterface;
use Webrtc\STUN\Message\Message;
use Webrtc\STUN\Message\MessageInterface;

/**
 * This trait represents a TURN connection.
 *
 * TURN (Traversal Using Relays around NAT) is a protocol that allows clients
 * behind firewalls or Network Address Translators (NATs) to communicate with each other.
 */
trait TurnConnection
{
    private const UDP_TRANSPORT = 0x11000000;
    private const TCP_TRANSPORT = 0x06000000;

    /**
     * @var int The lifetime of the connection in seconds.
     */
    private int $lifetime = 600;

    /**
     * @var ?string The integrity key to sign the package.
     */
    private ?string $integrityKey = null;

    /**
     * @var InternetAddress|null The relayed address of the connection.
     */
    private ?InternetAddress $relayedAddress = null;

    /**
     * @var string The nonce used for authentication.
     */
    private string $nonce = "";

    /**
     * @var ?string The realm for authentication (if provided by the server).
     */
    private ?string $realm = null;

    /**
     * @var ?string Handle of the timer used for refreshing the connection.
     */
    private ?string $refreshPeriodicTimer = null;

    /**
     * @var array<string, Future> Channel binds in flight, keyed by peer address.
     *
     * Two sends to the same unbound peer must not each allocate a channel, so the second
     * waits on the bind the first started rather than starting its own.
     */
    private array $peerBinding = [];

    /**
     * @var array<string, int> An associative array to map peers to channels.
     * Key: peer address, Value: channel number.
     */
    private array $peerToChannel = [];

    /**
     * @var array<int, InternetAddress> An associative array to map channels to peers.
     * Key: channel number, Value: peer address.
     */
    private array $channelToPeer = [];

    /**
     * @var int The starting and ending range for channel numbers (inclusive).
     */
    private int $channelNumber = 0x4000; // to 0x7FFF (reference: https://datatracker.ietf.org/doc/html/rfc5766#section-11)

    /**
     * @var int The refresh time for channels in seconds.
     */
    private int $channelRefreshTime = 500;

    /**
     * @var array<int, int> An associative array to store channel refresh timestamps.
     * Key: channel number, Value: timestamp (seconds since epoch).
     */
    private array $channelRefreshAt = [];



    /**
     * Get the lifetime of the connection.
     *
     * @return int The lifetime in seconds.
     */
    public function getLifetime(): int
    {
        return $this->lifetime;
    }

    /**
     * Set the lifetime of the connection.
     *
     * @param int $lifetime The lifetime in seconds.
     * @throws \InvalidArgumentException if the lifetime is less than 0.
     */
    public function setLifetime(int $lifetime): void
    {
        if ($lifetime < 0) {
            throw new \InvalidArgumentException('Lifetime cannot be less than 0');
        }

        $this->lifetime = $lifetime;
    }

    /**
     * Binds a channel to a peer address.
     *
     * @param int $channelNumber The channel number.
     * @param InternetAddress $address The peer address.
     * @return void Returns once the channel is bound.
     * @throws RandomException
     */
    private function channelBind(int $channelNumber, InternetAddress $address): void
    {
        $messageAttr = [
            MessageAttribute::CHANNEL_NUMBER->name => $channelNumber,
            MessageAttribute::XOR_PEER_ADDRESS->name => $address
        ];
        $message = Message::new(MessageClass::REQUEST, MessageMethod::CHANNEL_BIND, $messageAttr);

        $this->requestWithRetry($message);
    }

    /**
     * Initiates a TURN connection and allocates a relay.
     *
     * This method establishes a connection with a TURN server and allocates resources.
     *
     * @return InternetAddress|null The relayed address the server allocated.
     * @throws RandomException
     */
    public function connect(): ?InternetAddress
    {
        $messageAttr = [
            MessageAttribute::LIFETIME->name => $this->lifetime,
            MessageAttribute::REQUESTED_TRANSPORT->name => self::UDP_TRANSPORT
        ];
        $message = Message::new(MessageClass::REQUEST, MessageMethod::ALLOCATE, $messageAttr);

        [$response] = $this->requestWithRetry($message);

        $timeToExpiry = null;
        if ($response instanceof Message) {
            $timeToExpiry = $response->attributes()->get(MessageAttribute::LIFETIME);
            $relayedAddress = $response->attributes()->get(MessageAttribute::XOR_RELAYED_ADDRESS);
            assert($relayedAddress instanceof InternetAddress);
            $this->relayedAddress = $relayedAddress;
        }

        if (is_int($timeToExpiry) && $timeToExpiry !== 0) {
            // Refresh well before the allocation expires, as RFC 8656 section 3.2 advises.
            $this->armRefreshTimer($timeToExpiry * 5 / 6);
        }

        return $this->relayedAddress;
    }

    /**
     * Gets relayed address
     *
     * @return InternetAddress|null
     */
    public function getRelayedAddress(): ?InternetAddress
    {
        return $this->relayedAddress;
    }

    /**
     * Handles errors that occur during transmission.
     *
     * This method logs the error message and forwards it to the receiver for further handling.
     *
     * @param Throwable $e The exception object representing the error.
     * @return void
     * @throws RandomException
     * @throws Throwable
     */
    protected function onError(Throwable $e): void
    {
        $this->logger?->error("An error occurred while transmitting", ["ErrorMessage" => $e->getMessage()]);
        $this->receiver?->get()?->onError($e);
        $this->delete();
    }

    /**
     * Called when the connection is closed.
     *
     * This method logs the closure event and informs the receiver about the closed connection.
     *
     * @return void
     * @throws RandomException
     * @throws Throwable
     */
    protected function onClose(): void
    {
        $this->logger?->debug("The connection has been closed", ["Address" => $this->getLocalAddress()]);
        $this->receiver?->get()?->onClose($this);
        @$this->delete();
    }

    /**
     * Called when the connection is ended (similar to onClose).
     *
     * This method logs the ending event and informs the receiver about the ended connection.
     * The behavior is similar to `onClose` but might be used for specific purposes depending on the implementation.
     *
     * @return void
     */
    protected function onEnded(): void
    {
        $this->logger?->debug("The connection has been Ended", ["Address" => $this->getLocalAddress()]);
        $this->receiver?->get()?->onClose($this);
    }

    /**
     * Handles received messages.
     *
     * This method parses received data and determines the message type based on its format.
     * It then calls appropriate methods for further handling depending on the message class and transaction ID.
     *
     * @param string $data The received data.
     * @param InternetAddress $peerAddress The address of the peer who sent the message.
     * @return void
     * @throws RandomException
     */
    public function onReceived(string $data, InternetAddress $peerAddress): void
    {
        if (strlen($data) >= 4 && $this->isChannelData($data)) {
            $unpacked = unpack("nChannel/nLength", substr($data, 0, 4));
            assert($unpacked !== false);
            $channel = (int) $unpacked['Channel'];
            $length = (int) $unpacked['Length'];
            if (strlen($data) >= $length + 4 && $peerAddress = $this->channelToPeer[$channel] ?? null) {
                $payload = substr($data, 4, $length);
                if ($message = $this->decodeMessage($payload)) {
                    // The message without the ChannelData header, which its integrity is checked on.
                    $this->handleMessage($message, $peerAddress, $payload);
                } else {
// The STUN layer's candidate type (Webrtc\ICE\RTCIceCandidate in older
                    // published releases of php-rtc-stun, Webrtc\STUN\IceCandidateInterface in the
                    // current one) may not be resolvable in some environments, because the ICE
                    // package (which contains the concrete RTCIceCandidate) is intentionally not a
                    // dependency of the TURN package. All we need from it is the component id.
                    $this->receiver?->get()?->onDataReceived($payload, $this->getCandidate()?->getComponentId() ?? 0);
                }
            }

            return;
        }

        if ($message = $this->decodeMessage($data)) {
            $messageClass = $message->getMessageClass();
            $transactionId = $message->getTransactionId();

            // A request sent by a peer straight to the socket of the allocation, rather than to the relayed address,
            // is not answered: that would validate a path the data (always relayed) doesn't take.
            if (in_array($messageClass, [MessageClass::RESPONSE, MessageClass::ERROR]) && isset($this->transactionIds[$transactionId])) {
                $transaction = $this->transactionIds[$transactionId];
                $transaction->responseReceived($message, $peerAddress);
            }
        }
    }

    /**
     * Sends a STUN message: to the TURN server, or to a peer through the relay.
     *
     * The connectivity checks of the relayed candidate, and the answers to the ones it receives, are relayed like
     * the data: sent from the socket of the allocation, they would validate a path the data doesn't take.
     *
     * @param MessageInterface $message The message to send
     * @param InternetAddress|null $address The peer, or null for the TURN server
     * @throws RandomException
     */
    public function sendMessage(MessageInterface $message, ?InternetAddress $address): void
    {
        if ($address === null) {
            $this->logger?->debug("Send a STUN/TURN Message", ["Message" => $message->humanReadable()]);
            $this->send((string) $message);
            return;
        }
        $this->logger?->debug("Relay a STUN Message", ["Message" => $message->humanReadable(), "ToAddress" => $address]);
        $this->sendData((string) $message, $address);
    }

    /**
     * Attempts to decode a received message.
     *
     * This method tries to decode the provided data into a MessageInterface object.
     * It returns the decoded message on success or `false` if the decoding fails.
     *
     * @param string $data The data to be decoded.
     * @return MessageInterface|false The decoded message object or `false` if decoding fails.
     * @throws RandomException
     */
    private function decodeMessage(string $data): MessageInterface|false
    {
        try {
            return Message::decode($data);
        } catch (InvalidArgumentException) {
            return false;
        }
    }

    /**
     * Handles a newly received message.
     *
     * This method takes a decoded message object and processes it based on its class and transaction ID.
     * It performs actions like updating internal state, notifying the receiver, or handling errors.
     *
     * @param MessageInterface $message The decoded message object.
     * @param InternetAddress $address The address of the peer who sent the message.
     * @param string $data The raw received data (might be used for additional processing).
     * @return void
     */
    private function handleMessage(MessageInterface $message, InternetAddress $address, string $data): void
    {

        $this->logger?->info("A new TURN message has been received", ["Message" => $message->humanReadable(), "FromAddress" => $address]);

        $messageClass = $message->getMessageClass();
        $transactionId = $message->getTransactionId();

        if (in_array($messageClass, [MessageClass::RESPONSE, MessageClass::ERROR]) && isset($this->transactionIds[$transactionId])) {
            $transaction = $this->transactionIds[$transactionId];
            $transaction->responseReceived($message, $address);
        } elseif ($messageClass === MessageClass::REQUEST) {
            $this->receiver?->get()?->onRequestReceived($message, $address, $this, $data);
        }
    }

    /**
     * Checks if the received data is formatted as a ChannelData message.
     *
     * This method analyzes the first byte of the data to determine if it follows the ChannelData message format.
     * It returns `true` if the format matches and `false` otherwise.
     *
     * @param string $data The received data.
     * @return bool Whether the data is formatted as a ChannelData message.
     * @link https://datatracker.ietf.org/doc/html/rfc5766#section-2.5 (RFC 5766 - ChannelData message format)
     */
    private function isChannelData(string $data): bool
    {
        return (ord($data[0]) & 0xC0) == 0x40;
    }

    /**
     * Deletes the TURN allocation and closes the connection.
     *
     * This method sends a request to deallocate the previously allocated resources and then closes the connection.
     * It also logs the successful deletion of the allocation.
     *
     * @return void
     * @throws RandomException
     * @throws Throwable
     */
    public function delete(): void
    {
        if ($this->refreshPeriodicTimer !== null) {
            EventLoop::cancel($this->refreshPeriodicTimer);
            $this->refreshPeriodicTimer = null;
        }

        $messageAttr = [
            MessageAttribute::LIFETIME->name => 0
        ];
        $message = Message::new(MessageClass::REQUEST, MessageMethod::REFRESH, $messageAttr);
        try {
            $this->requestWithRetry($message);
            $this->logger?->info("TURN allocation deleted", ["RelayedAddress" => $this->relayedAddress]);
        } catch (Throwable) {
            $this->logger?->error("Could not TURN allocation deleted", ["RelayedAddress" => $this->relayedAddress]);
        } finally {
            $this->close();
        }

    }

    /**
     * Refreshes the TURN allocation.
     *
     * This method sends a request to refresh the lifetime of the current TURN allocation.
     * It updates the internal state and logs the successful refresh with the new expiry time.
     * In case of errors, it logs the failure with
     *
     * Armed only as the first-class callable `$this->onRefreshTimer(...)` passed to the event
     * loop, including when the refresh timer is restored after unserialize. That callable keeps
     * this method's private scope, so the loop can drive it without it being public.
     *
     * @return void
     * @throws RandomException
     */
    private function onRefreshTimer(): void
    {
        $this->refresh();
    }

    /**
     * @return array<string, mixed>
     */
    protected function turnSerializeReplacements(): array
    {
        return [
            'refreshPeriodicTimer' => $this->refreshPeriodicTimer !== null,
            'peerBinding' => [],
        ];
    }

    /**
     * @param array<string, mixed> $data
     */
    protected function consumeTurnRefreshFlag(array &$data): bool
    {
        $restartRefresh = false;
        /**
         * @var mixed $value
         */
        foreach ($data as $key => $value) {
            if (str_ends_with($key, "\0refreshPeriodicTimer")) {
                $restartRefresh = $value === true;
                $data[$key] = null;
            }
        }

        return $restartRefresh;
    }

    protected function restoreTurnTimers(bool $restartRefresh): void
    {
        $this->peerBinding = [];
        $this->refreshPeriodicTimer = null;
        if ($restartRefresh && $this->relayedAddress !== null) {
            $this->armRefreshTimer($this->lifetime * 5 / 6);
        }
    }

    /**
     * Arm the TURN allocation-refresh timer without pinning this connection.
     *
     * Registering it as $this->onRefreshTimer(...) captures $this strongly, so the event loop would
     * keep the connection alive forever and an unset()+gc could never reclaim it. A weak reference
     * lets it be collected; the tick cancels itself once the owner is gone.
     */
    private function armRefreshTimer(float $interval): void
    {
        $weak = \WeakReference::create($this);
        $this->refreshPeriodicTimer = EventLoop::repeat($interval, static function (string $id) use ($weak): void {
            $self = $weak->get();
            if ($self === null) {
                EventLoop::cancel($id);

                return;
            }
            $self->onRefreshTimer();
        });
    }

    private function refresh(): void
    {
        $messageAttr = [
            MessageAttribute::LIFETIME->name => $this->lifetime
        ];
        $message = Message::new(MessageClass::REQUEST, MessageMethod::REFRESH, $messageAttr);

        try {
            [$response] = $this->requestWithRetry($message);

            if ($response instanceof Message) {
                $this->logger?->info("TURN allocation refreshed", ["RelatedAddress" => $this->relayedAddress, "ExpiresInSeconds" => $response->attributes()->get(MessageAttribute::LIFETIME)]);
            }
        } catch (Throwable $e) {
            $this->logger?->error("TURN allocation refreshed failed", ["RelatedAddress" => $this->relayedAddress, "ErrorMessage" => $e->getMessage()]);
        }
    }

    /**
     * Sends a request with retry logic for authentication errors.
     *
     * This method sends the given message and handles potential authentication failures. If an authentication error occurs, it updates the long-term credentials and retries the request with the updated credentials.
     *
     * @param MessageInterface $message The message to be sent.
     * @return array{MessageInterface, InternetAddress|null} The response and where it came from.
     * @throws TransactionExceptionInterface If the request failed for a reason retrying cannot fix.
     */
    private function requestWithRetry(MessageInterface $message): array
    {
        $this->addAuthenticatedAttributes($message);

        try {
            return $this->request($message, null, $this->integrityKey);
        } catch (TransactionException $e) {
            return $this->handleRetryRequestError($e, $message);
        }
    }

    /**
     * If an authentication error occurs, it updates the long-term credentials and retries the request with the updated credentials.
     *
     * @param TransactionException $error
     * @param MessageInterface $message
     * @return array{MessageInterface, InternetAddress|null} The response to the retried request.
     * @throws RandomException
     */
    private function handleRetryRequestError(TransactionException $error, MessageInterface $message): array
    {
        $stunMessage = $error->getStunMessage();
        if ($stunMessage === null) {
            $this->logger?->error("Error processing request: {$error->getMessage()}");

            throw $error;
        }

        $errorCodeAttribute = $stunMessage->attributes()->get(MessageAttribute::ERROR_CODE);
        assert(is_array($errorCodeAttribute) && isset($errorCodeAttribute[0]) && is_int($errorCodeAttribute[0]));
        $errorCode = $errorCodeAttribute[0];

        if ($this->configuration->getTurnUsername() !== null &&
            $this->configuration->getTurnPassword() !== null &&
            $stunMessage->attributes()->has(MessageAttribute::NONCE) &&
            ($errorCode === 401 || ($errorCode === 438 && $this->realm !== null))) {
            // Update long-term credentials
            $nonce = $stunMessage->attributes()->get(MessageAttribute::NONCE);
            assert(is_string($nonce));
            $this->nonce = $nonce;
            if ($errorCode == 401) {
                $realm = $stunMessage->attributes()->get(MessageAttribute::REALM);
                assert(is_string($realm));
                $this->realm = $realm;
            }

            // Retry request with authentication
            $message->setTransactionId(random_bytes(12));
            $this->makeIntegrityKey();
            $this->addAuthenticatedAttributes($message);

            try {
                return $this->request($message, null, $this->integrityKey);
            } catch (TransactionException $e) {
                $this->logger?->error("Failed to request with retry: {$e->getMessage()}");

                throw new TransactionException("Failed to request with retry: {$e->getMessage()}", $e->getCode(), $e);
            }
        }

        $this->logger?->error("Error processing request: {$error->getMessage()}");

        throw $error;
    }

    /**
     * @param MessageInterface $message
     * @return void
     */
    private function addAuthenticatedAttributes(MessageInterface $message): void
    {
        if ($this->integrityKey !== null) {
            $messageAttr = [
                MessageAttribute::USERNAME->name => $this->configuration->getTurnUsername(),
                MessageAttribute::NONCE->name => $this->nonce,
                MessageAttribute::REALM->name => $this->realm
            ];
            $message->attributes()->merge($messageAttr);
        }
    }

    /**
     * Creates an integrity key for authentication.
     *
     * This method generates an MD5 hash of the username, realm, and password to create the integrity key.
     *
     * @return void The generated integrity key.
     */
    private function makeIntegrityKey(): void
    {
        $this->integrityKey = md5(implode(":", [$this->configuration->getTurnUsername(), $this->realm, $this->configuration->getTurnPassword()]), true);
    }

    /**
     * Sends data to a specific address.
     *
     * Data travels over a TURN channel, which has to be bound to the peer first and rebound
     * before it expires. Both happen inline here: the fiber simply waits for the bind.
     *
     * @param string $data The data to be sent.
     * @param InternetAddress $address The address of the recipient.
     * @return void
     * @throws RandomException
     */
    public function sendData(string $data, InternetAddress $address): void
    {
        $addressKey = $address->toString();
        $this->ensureChannel($address, $addressKey);
        $this->sendPacket($this->peerToChannel[$addressKey], $data);
    }

    /**
     * Make sure a live channel exists for a peer, binding or rebinding it if not.
     *
     * @throws RandomException
     */
    private function ensureChannel(InternetAddress $address, string $addressKey): void
    {
        // Another fiber may already be binding this peer; take its result rather than
        // allocating a second channel for the same address.
        while (isset($this->peerBinding[$addressKey])) {
            $this->peerBinding[$addressKey]->await();
        }

        $now = time();
        $channel = $this->peerToChannel[$addressKey] ?? null;

        if ($channel !== null && $now <= ($this->channelRefreshAt[$channel] ?? 0)) {
            return;
        }

        $channel ??= $this->channelNumber++;
        $deferred = new DeferredFuture();
        $this->peerBinding[$addressKey] = $deferred->getFuture();

        try {
            $this->channelBind($channel, $address);

            $this->channelRefreshAt[$channel] = $now + $this->channelRefreshTime;
            $this->channelToPeer[$channel] = $address;
            $this->peerToChannel[$addressKey] = $channel;

            unset($this->peerBinding[$addressKey]);
            $deferred->complete();
        } catch (\Throwable $e) {
            unset($this->peerBinding[$addressKey]);
            // Fibers waiting for this bind get the error, but there might be none: it's thrown here anyway.
            $deferred->getFuture()->ignore();
            $deferred->error($e);

            throw $e;
        }
    }

    /**
     * @param int $channel
     * @param string $data
     * @return void
     */
    private function sendPacket(int $channel, string $data): void
    {
        $header = pack("nn", $channel, strlen($data));
        $this->send($header . $data);
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\TURN;

use Amp\Socket\InternetAddress;
use Amp\Socket\Socket;
use Throwable;
use Webrtc\Mixin\SerializableState;
use Webrtc\STUN\BaseProtocol;
use function Amp\async;
use function Amp\Socket\connect;

/**
 * Abstract TCP Connection Class
 *
 * Provides base functionality for TCP-based communication in TURN protocol.
 * Handles TCP socket operations, event forwarding, and requires implementation
 * of protocol-specific message handling.
 */
abstract class TCPConnection extends BaseProtocol
{
    /** Whether the read loop should keep delivering data. */
    private bool $paused = false;

    /**
     * TCP connection constructor.
     *
     * @param Socket $socket The established TCP socket connection
     */
/**
     * TCP connection constructor.
     *
     * @param Socket $socket The established TCP socket connection
     */
    public function __construct(protected Socket $socket)
    {
        $this->listen();
    }

    /**
     * @return array<string, mixed>
     */
    public function __serialize(): array
    {
        return SerializableState::export($this, [
            'socket' => ['_tcp' => (string) $this->socket->getRemoteAddress()],
        ]);
    }

    /**
     * @param array<string, mixed> $data
     */
    public function __unserialize(array $data): void
    {
        $remote = null;
        /**
         * @var mixed $value
         */
        foreach ($data as $key => $value) {
            if (!is_array($value) || !array_key_exists('_tcp', $value)) {
                continue;
            }
            unset($data[$key]);
            if (is_string($value['_tcp'])) {
                $remote = $value['_tcp'];
            }
        }
        SerializableState::import($this, $data);
        $this->restoreTcpSocket($remote);
    }

    /**
     * Reconnect the TCP socket after unserialize. Subclasses may wrap TLS around this.
     */
    protected function restoreTcpSocket(?string $remote): void
    {
        if ($remote === null || $remote === '') {
            return;
        }
        $this->socket = connect($remote);
        $this->listen();
    }

    protected ?InternetAddress $remoteAddress = null;

    /**
     * Deliver incoming data to onTCPReceived() until the peer closes.
     *
     * Reading runs in its own fiber rather than through an event emitter, so a read error
     * reaches onError() rather than becoming an unobserved rejection.
     */
    protected function listen(): void
    {
        // The read loop must not capture $this, or the fiber the event loop parks in read() would
        // pin this connection forever and an unset()+gc could never reclaim it. It holds the socket
        // and only a weak reference back; __destruct() closes the socket to unwind the fiber once
        // the last real reference is gone.
        $weak = \WeakReference::create($this);
        $socket = $this->socket;
        async(static function () use ($weak, $socket): void {
            try {
                while (($chunk = $socket->read()) !== null) {
                    $self = $weak->get();
                    if ($self === null) {
                        $socket->close();

                        return;
                    }
                    if ($self->paused) {
                        continue;
                    }

                    // Dispatch in its own fiber: handling data can block on a transaction of
                    // its own, and doing that inline would stop this loop from reading the
                    // reply it is waiting for.
                    async(static fn () => $weak->get()?->onTCPReceived($chunk))->ignore();
                    unset($self);
                }

                $self = $weak->get();
                if ($self !== null) {
                    $self->onEnded();
                    $self->onClose();
                }
            } catch (Throwable $e) {
                $weak->get()?->onError($e);
            }
        });
    }

    /**
     * Release the socket when the connection is garbage-collected, unblocking the parked read loop.
     */
    public function __destruct()
    {
        // socket is always set here: the constructor connects it, a successful unserialize
        // reconnects it, and __destruct never runs on an object whose __unserialize threw.
        $this->socket->close();
    }

    /**
     * Send data over the TCP connection
     *
     * @param string $data The data to send
     * @param InternetAddress|null $remoteAddress Unused parameter (maintained for interface compatibility)
     * @return void
     */
    #[\Override]
    public function send(string $data, ?InternetAddress $remoteAddress = null): void
    {
        $this->socket->write($this->padded($data));
    }

    /**
     * Immediately close the TCP connection
     *
     * @return void
     */
    #[\Override]
    public function close(): void
    {
        $this->socket->close();
    }

    /**
     * End the TCP connection gracefully after writing pending data
     *
     * @return void
     */
    #[\Override]
    public function end(): void
    {
        $this->socket->end();
    }

    /**
     * Resume reading from the connection
     *
     * @return void
     */
    #[\Override]
    public function resume(): void
    {
        $this->paused = false;
    }

    /**
     * Pause reading from the connection
     *
     * @return void
     */
    #[\Override]
    public function pause(): void
    {
        $this->paused = true;
    }

    /**
     * Get the local connection address
     *
     * @return InternetAddress The local address
     */
    #[\Override]
    public function getLocalAddress(): InternetAddress
    {
        return InternetAddress::fromString($this->socket->getLocalAddress()->toString());
    }

    /**
     * Get the local host address
     *
     * @return string The local hostname/IP address
     */
    #[\Override]
    public function getLocalHost(): string
    {
        return $this->getLocalAddress()->getAddress();
    }

    /**
     * Get the local port number
     *
     * @return int The local port number
     */
    #[\Override]
    public function getLocalPort(): int
    {
        return $this->getLocalAddress()->getPort();
    }

    /**
     * Get the remote connection address
     *
     * @return InternetAddress|null The remote address or null if not connected
     */
    #[\Override]
    public function getRemoteAddress(): ?InternetAddress
    {
        return InternetAddress::fromString($this->socket->getRemoteAddress()->toString());
    }

    /**
     * Handle received TCP data (abstract method)
     *
     * @param string $data The received data
     * @return void
     */
    protected abstract function onTCPReceived(string $data): void;

    /**
     * Handle connection errors (abstract method)
     *
     * @param Throwable $e The thrown exception
     * @return void
     */
    protected abstract function onError(Throwable $e): void;

    /**
     * Handle connection end event (abstract method)
     *
     * @return void
     */
    protected abstract function onEnded(): void;

    /**
     * Handle connection close event (abstract method)
     *
     * @return void
     */
    protected abstract function onClose(): void;

    /**
     * Apply protocol-specific padding to data (abstract method)
     *
     * @param string $data The data to pad
     * @return string The padded data
     */
    protected abstract function padded(string $data): string;

}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\TURN;

use Amp\Socket\InternetAddress;
use Psr\Log\LoggerInterface;
use Webrtc\STUN\IceConnectionProtocolInterface;
use Webrtc\STUN\ReceiverInterface;

interface TurnInterface extends IceConnectionProtocolInterface
{
    public function connect(): ?InternetAddress;

    public function delete(): void;

    function getRelayedAddress(): ?InternetAddress;

    function getRelayedHost(): string;

    function getRelayedPort(): int;

    public static function create(TurnConfigurationInterface $configuration, ReceiverInterface $receiver, LoggerInterface $logger): Turn;
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\TURN;

use Amp\Socket\InternetAddress;
use Psr\Log\LoggerInterface;
use Random\RandomException;
use Throwable;
use Webrtc\Exception\InvalidArgumentException;
use Webrtc\STUN\IceCandidateInterface;
use Webrtc\STUN\Message\MessageInterface;
use Webrtc\STUN\ReceiverInterface;

/**
 * Class TurnTransport
 * Behaves like a Datagram transport but uses a TURN allocation.
 */
final class Turn implements TurnInterface
{
    public function __construct(private TurnConnectionInterface $connectionProtocol)
    {
    }

    /**
     * Initiates a TURN connection and allocates a relay.
     *
     * @return InternetAddress|null The relayed address the server allocated.
     * @throws RandomException
     */
    #[\Override]
    public function connect(): ?InternetAddress
    {
        return $this->connectionProtocol->connect();
    }

    /**
     * Deletes the TURN allocation and closes the connection.
     *
     * This method sends a request to deallocate the previously allocated resources and then closes the connection.
     * It also logs the successful deletion of the allocation.
     *
     * @return void
     * @throws RandomException
     * @throws Throwable
     */
    #[\Override]
    public function delete(): void
    {
        $this->connectionProtocol->delete();
    }

    /**
     * Send the data bytes to the remote peer at the given address.
     * This will bind a TURN channel as necessary.
     *
     * @param string $data The data to send.
     * @param InternetAddress|null $remoteAddress The remote address.
     * @throws RandomException
     */
    #[\Override]
    public function send(string $data, ?InternetAddress $remoteAddress = null): void
    {
        if ($remoteAddress === null) {
            throw new InvalidArgumentException('A remote address is required for TURN data.');
        }

        $this->connectionProtocol->sendData($data, $remoteAddress);
    }

    /**
     * Close the transport.
     * After the TURN allocation has been deleted, the protocol's
     * connection_lost() method will be called with None as its argument.
     *
     * @return void
     * @throws RandomException
     * @throws Throwable
     */
    #[\Override]
    public function close(): void
    {
        $this->delete();
    }

    /**
     * End the connection gracefully.
     *
     * @return void
     */
    #[\Override]
    public function end(): void
    {
        $this->connectionProtocol->end();
    }

    /**
     * Resume the connection.
     *
     * @return void
     */
    #[\Override]
    public function resume(): void
    {
        $this->connectionProtocol->resume();
    }

    /**
     * Pause the connection.
     *
     * @return void
     */
    #[\Override]
    public function pause(): void
    {
        $this->connectionProtocol->pause();
    }

    /**
     * Get the local address.
     *
     * @return InternetAddress The local address.
     */
    #[\Override]
    public function getLocalAddress(): InternetAddress
    {
        return $this->connectionProtocol->getLocalAddress();
    }

    /**
     * Get the local host.
     *
     * @return string The local address.
     */
    #[\Override]
    public function getLocalHost(): string
    {
        return $this->connectionProtocol->getLocalHost();
    }

    /**
     * Get the local host.
     *
     * @return int The local address.
     */
    #[\Override]
    public function getLocalPort(): int
    {
        return $this->connectionProtocol->getLocalPort();
    }

    /**
     * Get the remote address.
     *
     * @return InternetAddress|null The remote address.
     */
    #[\Override]
    public function getRemoteAddress(): ?InternetAddress
    {
        return $this->connectionProtocol->getRemoteAddress();
    }

    /**
     * @return IceCandidateInterface|null
     *
     * The candidate type is negotiated with the STUN layer. When the TURN package is used
     * standalone the published danog/php-rtc-stun still types these methods with the concrete
     * Webrtc\ICE\RTCIceCandidate from the (intentionally, due to the circular dependency, not
     * installed) ICE package; psalm cannot see either class here, so it cannot verify the
     * signature against the STUN interface until a STUN release carrying IceCandidateInterface
     * is published. See Webrtc\STUN\IceCandidateInterface in php-rtc-stun.
     *
     * USER OVERRIDE: do not suppress these issues, fix the root cause, instead.
     */
    #[\Override]
    public function getCandidate(): ?IceCandidateInterface
    {
        return $this->connectionProtocol->getCandidate();
    }

    /**
     * @param IceCandidateInterface $candidate
     * @return void
     *
     * See getCandidate() for why the STUN-layer candidate signature cannot be verified here yet.
     *
     * USER OVERRIDE: do not suppress these issues, fix the root cause, instead.
     */
    #[\Override]
    public function setCandidate(IceCandidateInterface $candidate): void
    {
        $this->connectionProtocol->setCandidate($candidate);
    }

    /**
     * Send a STUN message.
     *
     * @param MessageInterface $message
     * @param InternetAddress|null $address
     * @return void
     */
    #[\Override]
    public function sendMessage(MessageInterface $message, ?InternetAddress $address): void
    {
        $this->connectionProtocol->sendMessage($message, $address);
    }

    /**
     * Execute a STUN transaction and return the response.
     *
     * @param MessageInterface $message
     * @param InternetAddress|null $address
     * @param ?string $integrity_key
     * @param int $retransmissions
     * @return array{MessageInterface, InternetAddress|null} The response and where it came from.
     */
    #[\Override]
    public function request(MessageInterface $message, ?InternetAddress $address, ?string $integrity_key, int $retransmissions = 0): array
    {
        return $this->connectionProtocol->request($message, $address, $integrity_key, $retransmissions);
    }

    /**
     * Remove a pending transaction by its ID.
     *
     * @param string $transactionId
     * @return void
     */
    #[\Override]
    public function removeTransaction(string $transactionId): void
    {
        $this->connectionProtocol->removeTransaction($transactionId);
    }

    /**
     * Protocol ID
     *
     * @return string
     */
    #[\Override]
    public function getId(): string
    {
        return $this->connectionProtocol->getId();
    }

    /**
     * Gets relayed address
     *
     * @return InternetAddress|null
     */
    #[\Override]
    function getRelayedAddress(): ?InternetAddress
    {
        return $this->connectionProtocol->getRelayedAddress();
    }

    /**
     * Gets relayed host
     *
     * @return string
     */
    #[\Override]
    function getRelayedHost(): string
    {
        $relayedAddress = $this->connectionProtocol->getRelayedAddress();
        assert($relayedAddress !== null);

        return $relayedAddress->getAddress();
    }

    /**
     * Gets relayed port
     *
     * @return int
     */
    #[\Override]
    function getRelayedPort(): int
    {
        $relayedAddress = $this->connectionProtocol->getRelayedAddress();
        assert($relayedAddress !== null);

        return $relayedAddress->getPort();
    }

    /**
     * Create an instance of TURN object based on the configuration provided
     *
     * @param TurnConfigurationInterface $configuration
     * @param ReceiverInterface $receiver
     * @param LoggerInterface|null $logger
     * @return Turn
     */
    #[\Override]
    public static function create(TurnConfigurationInterface $configuration, ReceiverInterface $receiver, ?LoggerInterface $logger = null): Turn
    {
        $turnConnection = $configuration->getTurnTransport() === "tcp" ?
            TurnTcpConnection::create($configuration, $receiver, $logger) : TurnUdpConnection::create($configuration, $receiver, $logger);

        return new static($turnConnection);
    }
}
{
    "name": "danog/php-rtc-turn",
    "type": "library",
    "description": "TURN relay transport for PHP WebRTC, ported to Amp v3 TCP, UDP, and fibers. MadelineProto's PHP 8.2+ fork supports the modular danog/php-rtc stack.",
    "keywords": [
        "webrtc",
        "turn",
        "traversal-using-relays-around-nat"
    ],
    "homepage": "https://github.com/danog/php-rtc-turn",
    "license": "BSD-3-Clause",
    "authors": [
        {
            "name": "Amin Yazdanpanah",
            "email": "github@aminyazdanpanah.com"
        },
        {
            "name": "Sana Moniri",
            "email": "s.mo@quasarstream.com"
        },
        {
            "name": "Quasar Stream Community"
        },
        {
            "name": "Daniil Gentili",
            "email": "daniil@daniil.it"
        }
    ],
    "require": {
        "php": "^8.2",
        "amphp/amp": "^3.1.3",
        "amphp/socket": "^2.4.1",
        "psr/log": "^3.0.2",
        "danog/php-rtc-exception": "^1.1.5",
        "danog/php-rtc-mixin": "^2.0",
        "danog/php-rtc-stun": "^2.0.17"
    },
    "require-dev": {
        "phpunit/phpunit": "^11.5 || ^12.0 || ^13.3.2",
        "mockery/mockery": "^1.6.15",
        "bamarni/composer-bin-plugin": "^1.9.1"
    },
    "autoload": {
        "psr-4": {
            "Webrtc\\TURN\\": "src"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\Webrtc\\TURN\\": "tests"
        }
    },
    "minimum-stability": "stable",
    "prefer-stable": true,
    "scripts": {
        "post-install-cmd": [
            "@composer bin all install --ansi"
        ],
        "post-update-cmd": [
            "@composer bin all update --ansi"
        ]
    },
    "config": {
        "allow-plugins": {
            "bamarni/composer-bin-plugin": true
        }
    },
    "extra": {
        "bamarni-bin": {
            "bin-links": true,
            "forward-command": true
        }
    }
}
<?php declare(strict_types=1);

use danog\DialogId\DialogId;

require 'vendor/autoload.php';

function expect(bool $expect): void
{
    if (!$expect) {
        throw new AssertionError("Not verified!");
    }
}

$link = "https://t.me/c/1234567890/8892";
if (preg_match("|t.me/c/(\d+)/(\d+)|", $link, $matches)) {
    // Returns -1001234567890
    echo DialogId::fromSupergroupOrChannelId((int) $matches[1]).PHP_EOL;
}

// Converts an MTProto supergroup/channel ID => bot API dialog ID
expect(DialogId::fromSupergroupOrChannelId(1234567890) === -1001234567890);

// Converts an MTProto chat ID => bot API dialog ID
expect(DialogId::fromChatId(123456789) === -123456789);

// Converts an MTProto user ID => bot API dialog ID
expect(DialogId::fromUserId(123456789) === 123456789);

// Converts an MTProto secret chat ID => bot API dialog ID
expect(DialogId::fromSecretChatId(123456789) === -1999876543211);

// Converts a bot API dialog ID => MTProto supergroup/channel ID
expect(DialogId::toSupergroupOrChannelId(-1001234567890) === 1234567890);

// Converts a bot API dialog ID => MTProto chat ID
expect(DialogId::toChatId(-123456789) === 123456789);

// Converts a bot API dialog ID => MTProto user ID
expect(DialogId::toUserId(123456789) === 123456789);

// Converts a bot API dialog ID => MTProto secret chat ID
expect(DialogId::toSecretChatId(-1999876543211) === 123456789);

expect(DialogId::getType(101374607) === DialogId::USER);
expect(DialogId::getType(-123456789) === DialogId::CHAT);
expect(DialogId::getType(-1001234567890) === DialogId::CHANNEL_OR_SUPERGROUP);
expect(DialogId::getType(-1999898625393) === DialogId::SECRET_CHAT);

expect(DialogId::isUser(1099511627775) === true);
expect(DialogId::isChat(-999_999_999_999) === true);
expect(DialogId::isSupergroupOrChannel(-1997852516352) === true);
expect(DialogId::isSecretChat(-2002147483648) === true);

expect(DialogId::isUser(101374607) === true);
expect(DialogId::isChat(-101374607) === true);
expect(DialogId::isSupergroupOrChannel(-1001234567890) === true);
expect(DialogId::isSecretChat(-1999898625393) === true);

// Converts a bot API dialog ID => MTProto ID automatically depending on type
expect(DialogId::toMTProtoId(-1001234567890) === 1234567890);
expect(DialogId::toMTProtoId(-123456789) === 123456789);
expect(DialogId::toMTProtoId(123456789) === 123456789);
expect(DialogId::toMTProtoId(-1999876543211) === 123456789);

echo "OK!".PHP_EOL;
<?php

$config = new class extends Amp\CodeStyle\Config {
    public function getRules(): array
    {
        return array_merge(parent::getRules(), [
            'void_return' => true,
        ]);
    }
};

$config->getFinder()
    ->in(__DIR__ . '/src')
    ->in(__DIR__ . '/examples')
    ->in(__DIR__ . '/test');

$cacheDir = getenv('TRAVIS') ? getenv('HOME') . '/.php-cs-fixer' : __DIR__;

$config->setCacheFile($cacheDir . '/.php_cs.cache');

return $config;
<?php declare(strict_types=1);

namespace danog\TestDialogId;

use AssertionError;
use danog\DialogId\DialogId;
use PHPUnit\Framework\TestCase;

final class DialogIdTest extends TestCase
{
    public function testAll(): void
    {
        $this->assertSame(DialogId::USER, DialogId::getType(101374607));
        $this->assertSame(DialogId::CHAT, DialogId::getType(-101374607));
        $this->assertSame(DialogId::CHANNEL_OR_SUPERGROUP, DialogId::getType(-1001234567890));
        $this->assertSame(DialogId::SECRET_CHAT, DialogId::getType(-1999898625393));
        $this->assertSame(DialogId::MONOFORUM, DialogId::getType(-2002147483649));
        $this->assertSame(DialogId::MONOFORUM, DialogId::getType(-4000000000000));

        $this->assertTrue(DialogId::isUser(1099511627775));
        $this->assertTrue(DialogId::isChat(-999_999_999_999));
        $this->assertTrue(DialogId::isSupergroupOrChannel(-1997852516352));
        $this->assertTrue(DialogId::isSecretChat(-2002147483648));
        $this->assertTrue(DialogId::isMonoforum(-2002147483649));

        $this->assertTrue(DialogId::isUser(101374607));
        $this->assertTrue(DialogId::isChat(-101374607));
        $this->assertTrue(DialogId::isSupergroupOrChannel(-1001234567890));
        $this->assertTrue(DialogId::isSupergroupOrChannelOrMonoforum(-1001234567890));
        $this->assertTrue(DialogId::isSecretChat(-1999898625393));
        $this->assertTrue(DialogId::isMonoforum(-4000000000000));
        $this->assertTrue(DialogId::isSupergroupOrChannelOrMonoforum(-4000000000000));

        $this->assertFalse(DialogId::isSupergroupOrChannelOrMonoforum(1));

        $this->assertSame(101374607, DialogId::toUserId(101374607));
        $this->assertSame(101374607, DialogId::toChatId(-101374607));
        $this->assertSame(1234567890, DialogId::toSupergroupOrChannelId(-1001234567890));
        $this->assertSame(101374607, DialogId::toSecretChatId(-1999898625393));
        $this->assertSame(1002147483649, DialogId::toMonoforumId(-2002147483649));

        $this->assertSame(101374607, DialogId::toMTProtoId(101374607));
        $this->assertSame(101374607, DialogId::toMTProtoId(-101374607));
        $this->assertSame(1234567890, DialogId::toMTProtoId(-1001234567890));
        $this->assertSame(101374607, DialogId::toMTProtoId(-1999898625393));
        $this->assertSame(1002147483649, DialogId::toMTProtoId(-2002147483649));

        $this->assertSame(101374607, DialogId::fromUserId(101374607));
        $this->assertSame(-101374607, DialogId::fromChatId(101374607));
        $this->assertSame(-1001234567890, DialogId::fromSupergroupOrChannelId(1234567890));
        $this->assertSame(-1999898625393, DialogId::fromSecretChatId(101374607));
        $this->assertSame(-2002147483649, DialogId::fromMonoforumId(1002147483649));
    }

    public function testException1(): void
    {
        $this->expectException(AssertionError::class);
        $this->expectExceptionMessage("Invalid ID -4000000000001 provided!");
        DialogId::getType(-4000000000001);
    }
    public function testException2(): void
    {
        $this->expectException(AssertionError::class);
        $this->expectExceptionMessage("Invalid ID 0 provided!");
        $this->assertTrue(DialogId::getType(0));
    }
    public function testException3(): void
    {
        $this->expectException(AssertionError::class);
        $this->expectExceptionMessage("Expected a chat ID, but produced the following type: USER");
        DialogId::fromChatId(-100);
    }
    public function testException4(): void
    {
        $this->expectException(AssertionError::class);
        $this->expectExceptionMessage("Expected a user ID, but produced the following type: CHAT");
        DialogId::fromUserId(-100);
    }
    public function testException5(): void
    {
        $this->expectException(AssertionError::class);
        $this->expectExceptionMessage("Expected a supergroup/channel ID, but produced the following type: CHAT");
        DialogId::fromSupergroupOrChannelId(-100);
    }
    public function testException6(): void
    {
        $this->expectException(AssertionError::class);
        $this->expectExceptionMessage("Expected a secret chat ID, but produced the following type: CHAT");
        DialogId::fromSecretChatId((1 << 40) - 1);
    }
    public function testException7(): void
    {
        $this->expectException(AssertionError::class);
        $this->expectExceptionMessage("Expected a monoforum ID, but produced the following type: CHAT");
        DialogId::fromMonoforumId(-100);
    }

    public function testException3_rev(): void
    {
        $this->expectException(AssertionError::class);
        $this->expectExceptionMessage("Expected a chat ID, got the following type: USER");
        DialogId::toChatId(100);
    }
    public function testException4_rev(): void
    {
        $this->expectException(AssertionError::class);
        $this->expectExceptionMessage("Expected a user ID, got the following type: CHAT");
        DialogId::toUserId(-100);
    }
    public function testException5_rev(): void
    {
        $this->expectException(AssertionError::class);
        $this->expectExceptionMessage("Expected a supergroup/channel ID, got the following type: CHAT");
        DialogId::toSupergroupOrChannelId(-100);
    }
    public function testException6_rev(): void
    {
        $this->expectException(AssertionError::class);
        $this->expectExceptionMessage("Expected a secret chat ID, got the following type: USER");
        DialogId::toSecretChatId((1 << 40) - 1);
    }
    public function testException7_rev(): void
    {
        $this->expectException(AssertionError::class);
        $this->expectExceptionMessage("Expected a monoforum ID, got the following type: USER");
        DialogId::toMonoforumId((1 << 40) - 1);
    }
}
<?php declare(strict_types=1);

namespace danog\DialogId;

use AssertionError;

/**
 * Represents the type of a bot API dialog ID.
 *
 * @psalm-immutable
 *
 * @api
 */
enum DialogId
{
    private const ZERO_CHANNEL_ID = -1000000000000;
    private const ZERO_SECRET_CHAT_ID = -2000000000000;

    private const MAX_USER_ID = (1 << 40) - 1;

    private const MIN_CHAT_ID = -999_999_999_999;

    private const MIN_CHANNEL_ID = self::ZERO_CHANNEL_ID - (1000000000000 - (1 << 31));

    private const MIN_SECRET_CHAT_ID = self::ZERO_SECRET_CHAT_ID - 2147483648;

    private const MIN_MONOFORUM_CHANNEL_ID = self::ZERO_CHANNEL_ID - 3000000000000;

    /**
     * Dialog type: user.
     */
    case USER;
    /**
     * Dialog type: chat.
     */
    case CHAT;
    /**
     * Dialog type: supergroup or channel, see https://core.telegram.org/api/channel for more info.
     */
    case CHANNEL_OR_SUPERGROUP;
    /**
     * Dialog type: secret chat.
     */
    case SECRET_CHAT;
    /**
     * Dialog type: monoforum.
     */
    case MONOFORUM;

    /**
     * Get the type of a dialog using just its bot API dialog ID.
     *
     * @psalm-pure
     *
     * @param integer $id Bot API ID.
     */
    public static function getType(int $id): self
    {
        if ($id < 0) {
            if (self::MIN_CHAT_ID <= $id) {
                return DialogId::CHAT;
            }
            if (self::MIN_CHANNEL_ID <= $id && $id !== self::ZERO_CHANNEL_ID) {
                return DialogId::CHANNEL_OR_SUPERGROUP;
            }
            if (self::MIN_SECRET_CHAT_ID <= $id && $id !== self::ZERO_SECRET_CHAT_ID) {
                return DialogId::SECRET_CHAT;
            }
            if (self::MIN_MONOFORUM_CHANNEL_ID <= $id) {
                return DialogId::MONOFORUM;
            }
        } elseif ($id > 0) {
            return DialogId::USER;
        }
        throw new AssertionError("Invalid ID $id provided!");
    }

    /**
     * Checks whether the provided bot API ID is a supergroup or channel ID.
     *
     * @psalm-pure
     */
    public static function isSupergroupOrChannel(int $id): bool
    {
        return self::getType($id) === self::CHANNEL_OR_SUPERGROUP;
    }

    /**
     * Checks whether the provided bot API ID is a supergroup, channel or monoforum.
     *
     * @psalm-pure
     */
    public static function isSupergroupOrChannelOrMonoforum(int $id): bool
    {
        $t = self::getType($id);
        return $t === self::CHANNEL_OR_SUPERGROUP || $t === self::MONOFORUM;
    }

    /**
     * Checks whether the provided bot API ID is a monoforum.
     *
     * @psalm-pure
     */
    public static function isMonoforum(int $id): bool
    {
        return self::getType($id) === self::MONOFORUM;
    }

    /**
     * Checks whether the provided bot API ID is a chat ID.
     *
     * @psalm-pure
     */
    public static function isChat(int $id): bool
    {
        return self::getType($id) === self::CHAT;
    }
    /**
     * Checks whether the provided bot API ID is a user ID.
     *
     * @psalm-pure
     */
    public static function isUser(int $id): bool
    {
        return self::getType($id) === self::USER;
    }
    /**
     * Checks whether the provided bot API ID is a secret chat ID.
     *
     * @psalm-pure
     */
    public static function isSecretChat(int $id): bool
    {
        return self::getType($id) === self::SECRET_CHAT;
    }

    /**
     * Convert MTProto secret chat ID to bot API secret chat ID.
     *
     * @psalm-pure
     *
     * @param int $id MTProto secret chat ID
     *
     * @return int Bot API secret chat ID
     */
    public static function fromSecretChatId(int $id): int
    {
        $id += self::ZERO_SECRET_CHAT_ID;
        $type = self::getType($id);
        if ($type !== self::SECRET_CHAT) {
            throw new AssertionError("Expected a secret chat ID, but produced the following type: ".$type->name);
        }
        return $id;
    }
    /**
     * Convert bot API secret chat ID to MTProto secret chat ID.
     *
     * @psalm-pure
     *
     * @param int $id Bot API secret chat ID
     *
     * @return int MTProto secret chat ID
     */
    public static function toSecretChatId(int $id): int
    {
        $type = self::getType($id);
        if ($type !== self::SECRET_CHAT) {
            throw new AssertionError("Expected a secret chat ID, got the following type: ".$type->name);
        }
        return $id - self::ZERO_SECRET_CHAT_ID;
    }

    /**
     * Convert MTProto channel ID to bot API channel ID.
     *
     * @psalm-pure
     *
     * @param int $id MTProto channel ID
     */
    public static function fromSupergroupOrChannelId(int $id): int
    {
        $id = self::ZERO_CHANNEL_ID - $id;
        $type = self::getType($id);
        if ($type !== self::CHANNEL_OR_SUPERGROUP) {
            throw new AssertionError("Expected a supergroup/channel ID, but produced the following type: ".$type->name);
        }
        return $id;
    }
    /**
     * Convert bot API channel ID to MTProto channel ID.
     *
     * @psalm-pure
     *
     * @param int $id Bot API channel ID
     */
    public static function toSupergroupOrChannelId(int $id): int
    {
        $type = self::getType($id);
        if ($type !== self::CHANNEL_OR_SUPERGROUP) {
            throw new AssertionError("Expected a supergroup/channel ID, got the following type: ".$type->name);
        }
        return (-$id) + self::ZERO_CHANNEL_ID;
    }

    /**
     * Convert MTProto chat ID to bot API chat ID.
     *
     * @psalm-pure
     *
     * @param int $id MTProto chat ID
     */
    public static function fromChatId(int $id): int
    {
        $id = -$id;
        $type = self::getType($id);
        if ($type !== self::CHAT) {
            throw new AssertionError("Expected a chat ID, but produced the following type: ".$type->name);
        }
        return $id;
    }
    /**
     * Convert bot API chat ID to MTProto chat ID.
     *
     * @psalm-pure
     *
     * @param int $id Bot API chat ID
     */
    public static function toChatId(int $id): int
    {
        $type = self::getType($id);
        if ($type !== self::CHAT) {
            throw new AssertionError("Expected a chat ID, got the following type: ".$type->name);
        }
        return -$id;
    }

    /**
     * Convert MTProto monoforum ID to bot API monoforum ID.
     *
     * @psalm-pure
     *
     * @param int $id MTProto monoforum ID
     */
    public static function fromMonoforumId(int $id): int
    {
        $id = self::ZERO_CHANNEL_ID - $id;
        $type = self::getType($id);
        if ($type !== self::MONOFORUM) {
            throw new AssertionError("Expected a monoforum ID, but produced the following type: ".$type->name);
        }
        return $id;
    }

    /**
     * Convert bot API monoforum ID to MTProto monoforum ID.
     *
     * @psalm-pure
     *
     * @param int $id Bot API monoforum ID
     */
    public static function toMonoforumId(int $id): int
    {
        $type = self::getType($id);
        if ($type !== self::MONOFORUM) {
            throw new AssertionError("Expected a monoforum ID, got the following type: ".$type->name);
        }
        return (-$id) + self::ZERO_CHANNEL_ID;
    }

    /**
     * Convert MTProto user ID to bot API user ID.
     *
     * @psalm-pure
     *
     * @param int $id MTProto user ID
     */
    public static function fromUserId(int $id): int
    {
        $type = self::getType($id);
        if ($type !== self::USER) {
            throw new AssertionError("Expected a user ID, but produced the following type: ".$type->name);
        }
        return $id;
    }
    /**
     * Convert bot API user ID to MTProto user ID.
     *
     * @psalm-pure
     *
     * @param int $id Bot API user ID
     */
    public static function toUserId(int $id): int
    {
        $type = self::getType($id);
        if ($type !== self::USER) {
            throw new AssertionError("Expected a user ID, got the following type: ".$type->name);
        }
        return $id;
    }

    /**
     * Convert bot API ID to MTProto ID (automatically detecting the correct type).
     *
     * @psalm-pure
     *
     * @param int $id Bot API dialog ID
     */
    public static function toMTProtoId(int $id): int
    {
        return match (self::getType($id)) {
            self::USER => self::toUserId($id),
            self::MONOFORUM => self::toMonoforumId($id),
            self::CHAT => self::toChatId($id),
            self::CHANNEL_OR_SUPERGROUP => self::toSupergroupOrChannelId($id),
            self::SECRET_CHAT => self::toSecretChatId($id)
        };
    }
}
{
    "name": "danog/tg-dialog-id",
    "description": "A library to work with Telegram bot API dialog IDs",
    "type": "library",
    "license": "Apache-2.0",
    "autoload": {
        "psr-4": {
            "danog\\DialogId\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "danog\\TestDialogId\\": "test/"
        }
    },
    "authors": [
        {
            "name": "Daniil Gentili",
            "email": "daniil@daniil.it"
        }
    ],
    "require": {
        "php-64bit": ">=8.1"
    },
    "require-dev": {
        "vimeo/psalm": "dev-master",
        "phpunit/phpunit": "^11.0.9|^10",
        "amphp/php-cs-fixer-config": "^2.0.1",
        "infection/infection": "^0.28.1",
        "danog/phpdoc": "^0.1.22",
        "amphp/file": "^3.1"
    },
    "config": {
        "allow-plugins": {
            "infection/extension-installer": true
        }
    }
}
// Enums
enum AVColorRange {
    AVCOL_RANGE_UNSPECIFIED = 0,
    AVCOL_RANGE_MPEG        = 1,
    AVCOL_RANGE_JPEG        = 2,
    AVCOL_RANGE_NB
};

enum AVPictureType {
    AV_PICTURE_TYPE_NONE = 0,
    AV_PICTURE_TYPE_I,
    AV_PICTURE_TYPE_P,
    AV_PICTURE_TYPE_B,
    AV_PICTURE_TYPE_S,
    AV_PICTURE_TYPE_SI,
    AV_PICTURE_TYPE_SP,
    AV_PICTURE_TYPE_BI,
};

enum AVMediaType {
    AVMEDIA_TYPE_UNKNOWN = -1,
    AVMEDIA_TYPE_VIDEO,
    AVMEDIA_TYPE_AUDIO,
    AVMEDIA_TYPE_DATA,
    AVMEDIA_TYPE_SUBTITLE,
    AVMEDIA_TYPE_ATTACHMENT,
    AVMEDIA_TYPE_NB
};

enum AVColorSpace {
    AVCOL_SPC_RGB         = 0,
    AVCOL_SPC_BT709       = 1,
    AVCOL_SPC_UNSPECIFIED = 2,
    AVCOL_SPC_RESERVED    = 3,
    AVCOL_SPC_FCC         = 4,
    AVCOL_SPC_BT470BG     = 5,
    AVCOL_SPC_SMPTE170M   = 6,
    AVCOL_SPC_SMPTE240M   = 7,
    AVCOL_SPC_YCGCO       = 8,
    AVCOL_SPC_YCOCG       = AVCOL_SPC_YCGCO,
    AVCOL_SPC_BT2020_NCL  = 9,
    AVCOL_SPC_BT2020_CL   = 10,
    AVCOL_SPC_SMPTE2085   = 11,
    AVCOL_SPC_CHROMA_DERIVED_NCL = 12,
    AVCOL_SPC_CHROMA_DERIVED_CL = 13,
    AVCOL_SPC_ICTCP       = 14,
    AVCOL_SPC_IPT_C2      = 15,
    AVCOL_SPC_YCGCO_RE    = 16,
    AVCOL_SPC_YCGCO_RO    = 17,
    AVCOL_SPC_NB
};

enum AVChannelOrder {
    AV_CHANNEL_ORDER_UNSPEC,
    AV_CHANNEL_ORDER_NATIVE,
    AV_CHANNEL_ORDER_CUSTOM,
    AV_CHANNEL_ORDER_AMBISONIC,
    FF_CHANNEL_ORDER_NB
};

enum AVChannel {
    AV_CHAN_NONE = -1,
    AV_CHAN_FRONT_LEFT,
    AV_CHAN_FRONT_RIGHT,
    AV_CHAN_FRONT_CENTER,
    AV_CHAN_LOW_FREQUENCY,
    AV_CHAN_BACK_LEFT,
    AV_CHAN_BACK_RIGHT,
    AV_CHAN_FRONT_LEFT_OF_CENTER,
    AV_CHAN_FRONT_RIGHT_OF_CENTER,
    AV_CHAN_BACK_CENTER,
    AV_CHAN_SIDE_LEFT,
    AV_CHAN_SIDE_RIGHT,
    AV_CHAN_TOP_CENTER,
    AV_CHAN_TOP_FRONT_LEFT,
    AV_CHAN_TOP_FRONT_CENTER,
    AV_CHAN_TOP_FRONT_RIGHT,
    AV_CHAN_TOP_BACK_LEFT,
    AV_CHAN_TOP_BACK_CENTER,
    AV_CHAN_TOP_BACK_RIGHT,
    AV_CHAN_STEREO_LEFT = 29,
    AV_CHAN_STEREO_RIGHT,
    AV_CHAN_WIDE_LEFT,
    AV_CHAN_WIDE_RIGHT,
    AV_CHAN_SURROUND_DIRECT_LEFT,
    AV_CHAN_SURROUND_DIRECT_RIGHT,
    AV_CHAN_LOW_FREQUENCY_2,
    AV_CHAN_TOP_SIDE_LEFT,
    AV_CHAN_TOP_SIDE_RIGHT,
    AV_CHAN_BOTTOM_FRONT_CENTER,
    AV_CHAN_BOTTOM_FRONT_LEFT,
    AV_CHAN_BOTTOM_FRONT_RIGHT,
    AV_CHAN_SIDE_SURROUND_LEFT,
    AV_CHAN_SIDE_SURROUND_RIGHT,
    AV_CHAN_TOP_SURROUND_LEFT,
    AV_CHAN_TOP_SURROUND_RIGHT,
    AV_CHAN_UNUSED = 0x200,
    AV_CHAN_UNKNOWN = 0x300,
    AV_CHAN_AMBISONIC_BASE = 0x400,
    AV_CHAN_AMBISONIC_END  = 0x7ff,
};

enum AVFrameSideDataType {
    AV_FRAME_DATA_PANSCAN,
    AV_FRAME_DATA_A53_CC,
    AV_FRAME_DATA_STEREO3D,
    AV_FRAME_DATA_MATRIXENCODING,
    AV_FRAME_DATA_DOWNMIX_INFO,
    AV_FRAME_DATA_REPLAYGAIN,
    AV_FRAME_DATA_DISPLAYMATRIX,
    AV_FRAME_DATA_AFD,
    AV_FRAME_DATA_MOTION_VECTORS,
    AV_FRAME_DATA_SKIP_SAMPLES,
    AV_FRAME_DATA_AUDIO_SERVICE_TYPE,
    AV_FRAME_DATA_MASTERING_DISPLAY_METADATA,
    AV_FRAME_DATA_GOP_TIMECODE,
    AV_FRAME_DATA_SPHERICAL,
    AV_FRAME_DATA_CONTENT_LIGHT_LEVEL,
    AV_FRAME_DATA_ICC_PROFILE,
    AV_FRAME_DATA_S12M_TIMECODE,
    AV_FRAME_DATA_DYNAMIC_HDR_PLUS,
    AV_FRAME_DATA_REGIONS_OF_INTEREST,
    AV_FRAME_DATA_VIDEO_ENC_PARAMS,
    AV_FRAME_DATA_SEI_UNREGISTERED,
    AV_FRAME_DATA_FILM_GRAIN_PARAMS,
    AV_FRAME_DATA_DETECTION_BBOXES,
    AV_FRAME_DATA_DOVI_RPU_BUFFER,
    AV_FRAME_DATA_DOVI_METADATA,
    AV_FRAME_DATA_DYNAMIC_HDR_VIVID,
    AV_FRAME_DATA_AMBIENT_VIEWING_ENVIRONMENT,
    AV_FRAME_DATA_VIDEO_HINT,
    AV_FRAME_DATA_LCEVC,
    AV_FRAME_DATA_VIEW_ID,
};

enum AVColorPrimaries {
    AVCOL_PRI_RESERVED0   = 0,
    AVCOL_PRI_BT709       = 1,
    AVCOL_PRI_UNSPECIFIED = 2,
    AVCOL_PRI_RESERVED    = 3,
    AVCOL_PRI_BT470M      = 4,
    AVCOL_PRI_BT470BG     = 5,
    AVCOL_PRI_SMPTE170M   = 6,
    AVCOL_PRI_SMPTE240M   = 7,
    AVCOL_PRI_FILM        = 8,
    AVCOL_PRI_BT2020      = 9,
    AVCOL_PRI_SMPTE428    = 10,
    AVCOL_PRI_SMPTEST428_1 = AVCOL_PRI_SMPTE428,
    AVCOL_PRI_SMPTE431    = 11,
    AVCOL_PRI_SMPTE432    = 12,
    AVCOL_PRI_EBU3213     = 22,
    AVCOL_PRI_JEDEC_P22   = AVCOL_PRI_EBU3213,
    AVCOL_PRI_NB
};

enum AVColorTransferCharacteristic {
    AVCOL_TRC_RESERVED0    = 0,
    AVCOL_TRC_BT709        = 1,
    AVCOL_TRC_UNSPECIFIED  = 2,
    AVCOL_TRC_RESERVED     = 3,
    AVCOL_TRC_GAMMA22      = 4,
    AVCOL_TRC_GAMMA28      = 5,
    AVCOL_TRC_SMPTE170M    = 6,
    AVCOL_TRC_SMPTE240M    = 7,
    AVCOL_TRC_LINEAR       = 8,
    AVCOL_TRC_LOG          = 9,
    AVCOL_TRC_LOG_SQRT     = 10,
    AVCOL_TRC_IEC61966_2_4 = 11,
    AVCOL_TRC_BT1361_ECG   = 12,
    AVCOL_TRC_IEC61966_2_1 = 13,
    AVCOL_TRC_BT2020_10    = 14,
    AVCOL_TRC_BT2020_12    = 15,
    AVCOL_TRC_SMPTE2084    = 16,
    AVCOL_TRC_SMPTEST2084  = AVCOL_TRC_SMPTE2084,
    AVCOL_TRC_SMPTE428     = 17,
    AVCOL_TRC_SMPTEST428_1 = AVCOL_TRC_SMPTE428,
    AVCOL_TRC_ARIB_STD_B67 = 18,
    AVCOL_TRC_NB
};

enum AVChromaLocation {
    AVCHROMA_LOC_UNSPECIFIED = 0,
    AVCHROMA_LOC_LEFT        = 1,
    AVCHROMA_LOC_CENTER      = 2,
    AVCHROMA_LOC_TOPLEFT     = 3,
    AVCHROMA_LOC_TOP         = 4,
    AVCHROMA_LOC_BOTTOMLEFT  = 5,
    AVCHROMA_LOC_BOTTOM      = 6,
    AVCHROMA_LOC_NB
};

enum AVPixelFormat {
    AV_PIX_FMT_NONE = -1,
    AV_PIX_FMT_YUV420P,
    AV_PIX_FMT_YUYV422,
    AV_PIX_FMT_RGB24,
    AV_PIX_FMT_BGR24,
    AV_PIX_FMT_YUV422P,
    AV_PIX_FMT_YUV444P,
    AV_PIX_FMT_YUV410P,
    AV_PIX_FMT_YUV411P,
    AV_PIX_FMT_GRAY8,
    AV_PIX_FMT_MONOWHITE,
    AV_PIX_FMT_MONOBLACK,
    AV_PIX_FMT_PAL8,
    AV_PIX_FMT_YUVJ420P,
    AV_PIX_FMT_YUVJ422P,
    AV_PIX_FMT_YUVJ444P,
    AV_PIX_FMT_UYVY422,
    AV_PIX_FMT_UYYVYY411,
    AV_PIX_FMT_BGR8,
    AV_PIX_FMT_BGR4,
    AV_PIX_FMT_BGR4_BYTE,
    AV_PIX_FMT_RGB8,
    AV_PIX_FMT_RGB4,
    AV_PIX_FMT_RGB4_BYTE,
    AV_PIX_FMT_NV12,
    AV_PIX_FMT_NV21,
    AV_PIX_FMT_ARGB,
    AV_PIX_FMT_RGBA,
    AV_PIX_FMT_ABGR,
    AV_PIX_FMT_BGRA,
    AV_PIX_FMT_GRAY16BE,
    AV_PIX_FMT_GRAY16LE,
    AV_PIX_FMT_YUV440P,
    AV_PIX_FMT_YUVJ440P,
    AV_PIX_FMT_YUVA420P,
    AV_PIX_FMT_RGB48BE,
    AV_PIX_FMT_RGB48LE,
    AV_PIX_FMT_RGB565BE,
    AV_PIX_FMT_RGB565LE,
    AV_PIX_FMT_RGB555BE,
    AV_PIX_FMT_RGB555LE,
    AV_PIX_FMT_BGR565BE,
    AV_PIX_FMT_BGR565LE,
    AV_PIX_FMT_BGR555BE,
    AV_PIX_FMT_BGR555LE,
    AV_PIX_FMT_VAAPI,
    AV_PIX_FMT_YUV420P16LE,
    AV_PIX_FMT_YUV420P16BE,
    AV_PIX_FMT_YUV422P16LE,
    AV_PIX_FMT_YUV422P16BE,
    AV_PIX_FMT_YUV444P16LE,
    AV_PIX_FMT_YUV444P16BE,
    AV_PIX_FMT_DXVA2_VLD,
    AV_PIX_FMT_RGB444LE,
    AV_PIX_FMT_RGB444BE,
    AV_PIX_FMT_BGR444LE,
    AV_PIX_FMT_BGR444BE,
    AV_PIX_FMT_YA8,
    AV_PIX_FMT_Y400A = AV_PIX_FMT_YA8,
    AV_PIX_FMT_GRAY8A= AV_PIX_FMT_YA8,
    AV_PIX_FMT_BGR48BE,
    AV_PIX_FMT_BGR48LE,
    AV_PIX_FMT_YUV420P9BE,
    AV_PIX_FMT_YUV420P9LE,
    AV_PIX_FMT_YUV420P10BE,
    AV_PIX_FMT_YUV420P10LE,
    AV_PIX_FMT_YUV422P10BE,
    AV_PIX_FMT_YUV422P10LE,
    AV_PIX_FMT_YUV444P9BE,
    AV_PIX_FMT_YUV444P9LE,
    AV_PIX_FMT_YUV444P10BE,
    AV_PIX_FMT_YUV444P10LE,
    AV_PIX_FMT_YUV422P9BE,
    AV_PIX_FMT_YUV422P9LE,
    AV_PIX_FMT_GBRP,
    AV_PIX_FMT_GBR24P = AV_PIX_FMT_GBRP,
    AV_PIX_FMT_GBRP9BE,
    AV_PIX_FMT_GBRP9LE,
    AV_PIX_FMT_GBRP10BE,
    AV_PIX_FMT_GBRP10LE,
    AV_PIX_FMT_GBRP16BE,
    AV_PIX_FMT_GBRP16LE,
    AV_PIX_FMT_YUVA422P,
    AV_PIX_FMT_YUVA444P,
    AV_PIX_FMT_YUVA420P9BE,
    AV_PIX_FMT_YUVA420P9LE,
    AV_PIX_FMT_YUVA422P9BE,
    AV_PIX_FMT_YUVA422P9LE,
    AV_PIX_FMT_YUVA444P9BE,
    AV_PIX_FMT_YUVA444P9LE,
    AV_PIX_FMT_YUVA420P10BE,
    AV_PIX_FMT_YUVA420P10LE,
    AV_PIX_FMT_YUVA422P10BE,
    AV_PIX_FMT_YUVA422P10LE,
    AV_PIX_FMT_YUVA444P10BE,
    AV_PIX_FMT_YUVA444P10LE,
    AV_PIX_FMT_YUVA420P16BE,
    AV_PIX_FMT_YUVA420P16LE,
    AV_PIX_FMT_YUVA422P16BE,
    AV_PIX_FMT_YUVA422P16LE,
    AV_PIX_FMT_YUVA444P16BE,
    AV_PIX_FMT_YUVA444P16LE,
    AV_PIX_FMT_VDPAU,
    AV_PIX_FMT_XYZ12LE,
    AV_PIX_FMT_XYZ12BE,
    AV_PIX_FMT_NV16,
    AV_PIX_FMT_NV20LE,
    AV_PIX_FMT_NV20BE,
    AV_PIX_FMT_RGBA64BE,
    AV_PIX_FMT_RGBA64LE,
    AV_PIX_FMT_BGRA64BE,
    AV_PIX_FMT_BGRA64LE,
    AV_PIX_FMT_YVYU422,
    AV_PIX_FMT_YA16BE,
    AV_PIX_FMT_YA16LE,
    AV_PIX_FMT_GBRAP,
    AV_PIX_FMT_GBRAP16BE,
    AV_PIX_FMT_GBRAP16LE,
    AV_PIX_FMT_QSV,
    AV_PIX_FMT_MMAL,
    AV_PIX_FMT_D3D11VA_VLD,
    AV_PIX_FMT_CUDA,
    AV_PIX_FMT_0RGB,
    AV_PIX_FMT_RGB0,
    AV_PIX_FMT_0BGR,
    AV_PIX_FMT_BGR0,
    AV_PIX_FMT_YUV420P12BE,
    AV_PIX_FMT_YUV420P12LE,
    AV_PIX_FMT_YUV420P14BE,
    AV_PIX_FMT_YUV420P14LE,
    AV_PIX_FMT_YUV422P12BE,
    AV_PIX_FMT_YUV422P12LE,
    AV_PIX_FMT_YUV422P14BE,
    AV_PIX_FMT_YUV422P14LE,
    AV_PIX_FMT_YUV444P12BE,
    AV_PIX_FMT_YUV444P12LE,
    AV_PIX_FMT_YUV444P14BE,
    AV_PIX_FMT_YUV444P14LE,
    AV_PIX_FMT_GBRP12BE,
    AV_PIX_FMT_GBRP12LE,
    AV_PIX_FMT_GBRP14BE,
    AV_PIX_FMT_GBRP14LE,
    AV_PIX_FMT_YUVJ411P,
    AV_PIX_FMT_BAYER_BGGR8,
    AV_PIX_FMT_BAYER_RGGB8,
    AV_PIX_FMT_BAYER_GBRG8,
    AV_PIX_FMT_BAYER_GRBG8,
    AV_PIX_FMT_BAYER_BGGR16LE,
    AV_PIX_FMT_BAYER_BGGR16BE,
    AV_PIX_FMT_BAYER_RGGB16LE,
    AV_PIX_FMT_BAYER_RGGB16BE,
    AV_PIX_FMT_BAYER_GBRG16LE,
    AV_PIX_FMT_BAYER_GBRG16BE,
    AV_PIX_FMT_BAYER_GRBG16LE,
    AV_PIX_FMT_BAYER_GRBG16BE,
    AV_PIX_FMT_YUV440P10LE,
    AV_PIX_FMT_YUV440P10BE,
    AV_PIX_FMT_YUV440P12LE,
    AV_PIX_FMT_YUV440P12BE,
    AV_PIX_FMT_AYUV64LE,
    AV_PIX_FMT_AYUV64BE,
    AV_PIX_FMT_VIDEOTOOLBOX,
    AV_PIX_FMT_P010LE,
    AV_PIX_FMT_P010BE,
    AV_PIX_FMT_GBRAP12BE,
    AV_PIX_FMT_GBRAP12LE,
    AV_PIX_FMT_GBRAP10BE,
    AV_PIX_FMT_GBRAP10LE,
    AV_PIX_FMT_MEDIACODEC,
    AV_PIX_FMT_GRAY12BE,
    AV_PIX_FMT_GRAY12LE,
    AV_PIX_FMT_GRAY10BE,
    AV_PIX_FMT_GRAY10LE,
    AV_PIX_FMT_P016LE,
    AV_PIX_FMT_P016BE,
    AV_PIX_FMT_D3D11,
    AV_PIX_FMT_GRAY9BE,
    AV_PIX_FMT_GRAY9LE,
    AV_PIX_FMT_GBRPF32BE,
    AV_PIX_FMT_GBRPF32LE,
    AV_PIX_FMT_GBRAPF32BE,
    AV_PIX_FMT_GBRAPF32LE,
    AV_PIX_FMT_DRM_PRIME,
    AV_PIX_FMT_OPENCL,
    AV_PIX_FMT_GRAY14BE,
    AV_PIX_FMT_GRAY14LE,
    AV_PIX_FMT_GRAYF32BE,
    AV_PIX_FMT_GRAYF32LE,
    AV_PIX_FMT_YUVA422P12BE,
    AV_PIX_FMT_YUVA422P12LE,
    AV_PIX_FMT_YUVA444P12BE,
    AV_PIX_FMT_YUVA444P12LE,
    AV_PIX_FMT_NV24,
    AV_PIX_FMT_NV42,
    AV_PIX_FMT_VULKAN,
    AV_PIX_FMT_Y210BE,
    AV_PIX_FMT_Y210LE,
    AV_PIX_FMT_X2RGB10LE,
    AV_PIX_FMT_X2RGB10BE,
    AV_PIX_FMT_X2BGR10LE,
    AV_PIX_FMT_X2BGR10BE,
    AV_PIX_FMT_P210BE,
    AV_PIX_FMT_P210LE,
    AV_PIX_FMT_P410BE,
    AV_PIX_FMT_P410LE,
    AV_PIX_FMT_P216BE,
    AV_PIX_FMT_P216LE,
    AV_PIX_FMT_P416BE,
    AV_PIX_FMT_P416LE,
    AV_PIX_FMT_VUYA,
    AV_PIX_FMT_RGBAF16BE,
    AV_PIX_FMT_RGBAF16LE,
    AV_PIX_FMT_VUYX,
    AV_PIX_FMT_P012LE,
    AV_PIX_FMT_P012BE,
    AV_PIX_FMT_Y212BE,
    AV_PIX_FMT_Y212LE,
    AV_PIX_FMT_XV30BE,
    AV_PIX_FMT_XV30LE,
    AV_PIX_FMT_XV36BE,
    AV_PIX_FMT_XV36LE,
    AV_PIX_FMT_RGBF32BE,
    AV_PIX_FMT_RGBF32LE,
    AV_PIX_FMT_RGBAF32BE,
    AV_PIX_FMT_RGBAF32LE,
    AV_PIX_FMT_P212BE,
    AV_PIX_FMT_P212LE,
    AV_PIX_FMT_P412BE,
    AV_PIX_FMT_P412LE,
    AV_PIX_FMT_GBRAP14BE,
    AV_PIX_FMT_GBRAP14LE,
    AV_PIX_FMT_D3D12,
    AV_PIX_FMT_NB
};

enum AVSampleFormat {
    AV_SAMPLE_FMT_NONE = -1,
    AV_SAMPLE_FMT_U8,
    AV_SAMPLE_FMT_S16,
    AV_SAMPLE_FMT_S32,
    AV_SAMPLE_FMT_FLT,
    AV_SAMPLE_FMT_DBL,
    AV_SAMPLE_FMT_U8P,
    AV_SAMPLE_FMT_S16P,
    AV_SAMPLE_FMT_S32P,
    AV_SAMPLE_FMT_FLTP,
    AV_SAMPLE_FMT_DBLP,
    AV_SAMPLE_FMT_S64,
    AV_SAMPLE_FMT_S64P,
    AV_SAMPLE_FMT_NB
};

typedef enum {
    AV_CLASS_CATEGORY_NA = 0,
    AV_CLASS_CATEGORY_INPUT,
    AV_CLASS_CATEGORY_OUTPUT,
    AV_CLASS_CATEGORY_MUXER,
    AV_CLASS_CATEGORY_DEMUXER,
    AV_CLASS_CATEGORY_ENCODER,
    AV_CLASS_CATEGORY_DECODER,
    AV_CLASS_CATEGORY_FILTER,
    AV_CLASS_CATEGORY_BITSTREAM_FILTER,
    AV_CLASS_CATEGORY_SWSCALER,
    AV_CLASS_CATEGORY_SWRESAMPLER,
    AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT = 40,
    AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT,
    AV_CLASS_CATEGORY_DEVICE_AUDIO_OUTPUT,
    AV_CLASS_CATEGORY_DEVICE_AUDIO_INPUT,
    AV_CLASS_CATEGORY_DEVICE_OUTPUT,
    AV_CLASS_CATEGORY_DEVICE_INPUT,
    AV_CLASS_CATEGORY_NB
} AVClassCategory;

// Structs
typedef struct AVFilterContext AVFilterContext;

typedef struct AVFilterLink    AVFilterLink;

typedef struct AVFilterPad     AVFilterPad;

typedef struct AVFilterFormats AVFilterFormats;

typedef struct AVFilterChannelLayouts AVFilterChannelLayouts;

typedef struct AVRational{
    int num;
    int den;
} AVRational;

typedef struct AVFilterFormatsConfig {
    AVFilterFormats *formats;
    AVFilterFormats  *samplerates;
    AVFilterChannelLayouts  *channel_layouts;
    AVFilterFormats *color_spaces;
    AVFilterFormats *color_ranges;
} AVFilterFormatsConfig;

typedef struct AVBuffer AVBuffer;

typedef struct AVDictionary AVDictionary;

typedef struct AVBufferRef {
    AVBuffer *buffer;
    uint8_t *data;
    size_t   size;
} AVBufferRef;

typedef struct AVFrameSideData {
    enum AVFrameSideDataType type;
    uint8_t *data;
    size_t   size;
    AVDictionary *metadata;
    AVBufferRef *buf;
} AVFrameSideData;

typedef struct AVChannelCustom {
    enum AVChannel id;
    char name[16];
    void *opaque;
} AVChannelCustom;

typedef struct AVChannelLayout {
    enum AVChannelOrder order;
    int nb_channels;
    union {
        uint64_t mask;
        AVChannelCustom *map;
    } u;
    void *opaque;
} AVChannelLayout;

typedef struct AVFrame {
#define AV_NUM_DATA_POINTERS 8
    uint8_t *data[8];
    int linesize[8];
    uint8_t **extended_data;
    int width, height;
    int nb_samples;
    int format;
#if FF_API_FRAME_KEY
    int key_frame;
#endif
    enum AVPictureType pict_type;
    AVRational sample_aspect_ratio;
    int64_t pts;
    int64_t pkt_dts;
    AVRational time_base;
    int quality;
    void *opaque;
    int repeat_pict;
#if FF_API_INTERLACED_FRAME
    int interlaced_frame;
    int top_field_first;
#endif
#if FF_API_PALETTE_HAS_CHANGED
    int palette_has_changed;
#endif
    int sample_rate;
    AVBufferRef *buf[8];
    AVBufferRef **extended_buf;
    int        nb_extended_buf;
    AVFrameSideData **side_data;
    int            nb_side_data;
#define AV_FRAME_FLAG_CORRUPT       (1 << 0)
#define AV_FRAME_FLAG_KEY (1 << 1)
#define AV_FRAME_FLAG_DISCARD   (1 << 2)
#define AV_FRAME_FLAG_INTERLACED (1 << 3)
#define AV_FRAME_FLAG_TOP_FIELD_FIRST (1 << 4)
    int flags;
    enum AVColorRange color_range;
    enum AVColorPrimaries color_primaries;
    enum AVColorTransferCharacteristic color_trc;
    enum AVColorSpace colorspace;
    enum AVChromaLocation chroma_location;
    int64_t best_effort_timestamp;
#if FF_API_FRAME_PKT
    int64_t pkt_pos;
#endif
    AVDictionary *metadata;
    int decode_error_flags;
#define FF_DECODE_ERROR_INVALID_BITSTREAM   1
#define FF_DECODE_ERROR_MISSING_REFERENCE   2
#define FF_DECODE_ERROR_CONCEALMENT_ACTIVE  4
#define FF_DECODE_ERROR_DECODE_SLICES       8
#if FF_API_FRAME_PKT
    int pkt_size;
#endif
    AVBufferRef *hw_frames_ctx;
    AVBufferRef *opaque_ref;
    size_t crop_top;
    size_t crop_bottom;
    size_t crop_left;
    size_t crop_right;
    AVBufferRef *private_ref;
    AVChannelLayout ch_layout;
    int64_t duration;
} AVFrame;

typedef struct AVClass {
    const char* class_name;
    const char* (*item_name)(void* ctx);
    const struct AVOption *option;
    int version;
    int log_level_offset_offset;
    int parent_log_context_offset;
    AVClassCategory category;
    AVClassCategory (*get_category)(void* ctx);
    int (*query_ranges)(struct AVOptionRanges **, void *obj, const char *key, int flags);
    void* (*child_next)(void *obj, void *prev);
    const struct AVClass* (*child_class_iterate)(void **iter);
} AVClass;

typedef struct AVFilter {
    const char *name;
    const char *description;
    const AVFilterPad *inputs;
    const AVFilterPad *outputs;
    const AVClass *priv_class;
    int flags;
    uint8_t nb_inputs;
    uint8_t nb_outputs;
    uint8_t formats_state;
    int (*preinit)(AVFilterContext *ctx);
    int (*init)(AVFilterContext *ctx);
    void (*uninit)(AVFilterContext *ctx);
    union {
        int (*query_func)(AVFilterContext *);
        int (*query_func2)(const AVFilterContext *,
                           struct AVFilterFormatsConfig **cfg_in,
                           struct AVFilterFormatsConfig **cfg_out);
        const enum AVPixelFormat *pixels_list;
        const enum AVSampleFormat *samples_list;
        enum AVPixelFormat  pix_fmt;
        enum AVSampleFormat sample_fmt;
    } formats;
    int priv_size;
    int flags_internal;
    int (*process_command)(AVFilterContext *, const char *cmd, const char *arg, char *res, int res_len, int flags);
    int (*activate)(AVFilterContext *ctx);
} AVFilter;

struct AVFilterContext {
    const AVClass *av_class;
    const AVFilter *filter;
    char *name;
    AVFilterPad   *input_pads;
    AVFilterLink **inputs;
    unsigned    nb_inputs;
    AVFilterPad   *output_pads;
    AVFilterLink **outputs;
    unsigned    nb_outputs;
    void *priv;
    struct AVFilterGraph *graph;
    int thread_type;
    int nb_threads;
    char *enable_str;
    int is_disabled;
    AVBufferRef *hw_device_ctx;
    int extra_hw_frames;
};

struct AVFilterLink {
    AVFilterContext *src;
    AVFilterPad *srcpad;
    AVFilterContext *dst;
    AVFilterPad *dstpad;
    enum AVMediaType type;
    int format;
    int w;
    int h;
    AVRational sample_aspect_ratio;
    enum AVColorSpace colorspace;
    enum AVColorRange color_range;
    int sample_rate;
    AVChannelLayout ch_layout;
    AVRational time_base;
    AVFilterFormatsConfig incfg;
    AVFilterFormatsConfig outcfg;
};

struct AVFilterPad {
    const char *name;
    enum AVMediaType type;
#define AVFILTERPAD_FLAG_NEEDS_WRITABLE                  (1 << 0)
#define AVFILTERPAD_FLAG_FREE_NAME                       (1 << 1)
    int flags;
    union {
        AVFrame *(*video)(AVFilterLink *link, int w, int h);
        AVFrame *(*audio)(AVFilterLink *link, int nb_samples);
    } get_buffer;
    int (*filter_frame)(AVFilterLink *link, AVFrame *frame);
    int (*request_frame)(AVFilterLink *link);
    int (*config_props)(AVFilterLink *link);
};

typedef int (avfilter_action_func)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs);

typedef int (avfilter_execute_func)(AVFilterContext *ctx, avfilter_action_func *func, void *arg, int *ret, int nb_jobs);

typedef struct AVFilterGraph {
    const AVClass *av_class;
    AVFilterContext **filters;
    unsigned nb_filters;
    char *scale_sws_opts;
    int thread_type;
    int nb_threads;
    void *opaque;
    avfilter_execute_func *execute;
    char *aresample_swr_opts;
} AVFilterGraph;

typedef struct FFFilterContext {
    AVFilterContext p;
    avfilter_execute_func *execute;
    unsigned state_flags;
    unsigned ready;
    struct AVExpr *enable;
    double *var_values;
    struct AVFilterCommand *command_queue;
} FFFilterContext;

// Functions
void av_log_set_level(int level);
const char *av_version_info(void);
void avfilter_graph_free(AVFilterGraph **graph);
AVFilterGraph *avfilter_graph_alloc(void);
const AVFilter *avfilter_get_by_name(const char *name);
AVFilterContext *avfilter_graph_alloc_filter(AVFilterGraph *graph, const AVFilter *filter, const char *name);
int avfilter_init_str(AVFilterContext *ctx, const char *args);
int avfilter_init_dict(AVFilterContext *ctx, AVDictionary **options);
int avfilter_link(AVFilterContext *src, unsigned srcpad, AVFilterContext *dst, unsigned dstpad);
int avfilter_graph_config(AVFilterGraph *graphctx, void *log_ctx);
int av_buffersrc_write_frame(AVFilterContext *ctx, const AVFrame *frame);
int av_buffersink_get_frame(AVFilterContext *ctx, AVFrame *frame);
void av_buffersink_set_frame_size(AVFilterContext *ctx, unsigned frame_size);
int av_dict_copy(AVDictionary **dst, const AVDictionary *src, int flags);
char *avfilter_graph_dump(AVFilterGraph *graph, const char *options);
void av_log(void* avcl, int level, const char *fmt, ...);
void av_log_set_callback(void (*callback)(void*, int, const char*, va_list));
AVFrame *av_frame_alloc(void);// Enums
enum AVUtil {
    AV_NOPTS_VALUE = -9223372036854775808,
};

enum AVMediaType {
    AVMEDIA_TYPE_UNKNOWN = -1,
    AVMEDIA_TYPE_VIDEO,
    AVMEDIA_TYPE_AUDIO,
    AVMEDIA_TYPE_DATA,
    AVMEDIA_TYPE_SUBTITLE,
    AVMEDIA_TYPE_ATTACHMENT,
    AVMEDIA_TYPE_NB
};

enum AVPictureType {
    AV_PICTURE_TYPE_NONE = 0,
    AV_PICTURE_TYPE_I,
    AV_PICTURE_TYPE_P,
    AV_PICTURE_TYPE_B,
    AV_PICTURE_TYPE_S,
    AV_PICTURE_TYPE_SI,
    AV_PICTURE_TYPE_SP,
    AV_PICTURE_TYPE_BI,
};

enum AVPacketSideDataType {
    AV_PKT_DATA_PALETTE,
    AV_PKT_DATA_NEW_EXTRADATA,
    AV_PKT_DATA_PARAM_CHANGE,
    AV_PKT_DATA_H263_MB_INFO,
    AV_PKT_DATA_REPLAYGAIN,
    AV_PKT_DATA_DISPLAYMATRIX,
    AV_PKT_DATA_STEREO3D,
    AV_PKT_DATA_AUDIO_SERVICE_TYPE,
    AV_PKT_DATA_QUALITY_STATS,
    AV_PKT_DATA_FALLBACK_TRACK,
    AV_PKT_DATA_CPB_PROPERTIES,
    AV_PKT_DATA_SKIP_SAMPLES,
    AV_PKT_DATA_JP_DUALMONO,
    AV_PKT_DATA_STRINGS_METADATA,
    AV_PKT_DATA_SUBTITLE_POSITION,
    AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL,
    AV_PKT_DATA_WEBVTT_IDENTIFIER,
    AV_PKT_DATA_WEBVTT_SETTINGS,
    AV_PKT_DATA_METADATA_UPDATE,
    AV_PKT_DATA_MPEGTS_STREAM_ID,
    AV_PKT_DATA_MASTERING_DISPLAY_METADATA,
    AV_PKT_DATA_SPHERICAL,
    AV_PKT_DATA_CONTENT_LIGHT_LEVEL,
    AV_PKT_DATA_A53_CC,
    AV_PKT_DATA_ENCRYPTION_INIT_INFO,
    AV_PKT_DATA_ENCRYPTION_INFO,
    AV_PKT_DATA_AFD,
    AV_PKT_DATA_PRFT,
    AV_PKT_DATA_ICC_PROFILE,
    AV_PKT_DATA_DOVI_CONF,
    AV_PKT_DATA_S12M_TIMECODE,
    AV_PKT_DATA_DYNAMIC_HDR10_PLUS,
    AV_PKT_DATA_IAMF_MIX_GAIN_PARAM,
    AV_PKT_DATA_IAMF_DEMIXING_INFO_PARAM,
    AV_PKT_DATA_IAMF_RECON_GAIN_INFO_PARAM,
    AV_PKT_DATA_AMBIENT_VIEWING_ENVIRONMENT,
    AV_PKT_DATA_FRAME_CROPPING,
    AV_PKT_DATA_LCEVC,
    AV_PKT_DATA_NB
};

enum AVFrameSideDataType {
    AV_FRAME_DATA_PANSCAN,
    AV_FRAME_DATA_A53_CC,
    AV_FRAME_DATA_STEREO3D,
    AV_FRAME_DATA_MATRIXENCODING,
    AV_FRAME_DATA_DOWNMIX_INFO,
    AV_FRAME_DATA_REPLAYGAIN,
    AV_FRAME_DATA_DISPLAYMATRIX,
    AV_FRAME_DATA_AFD,
    AV_FRAME_DATA_MOTION_VECTORS,
    AV_FRAME_DATA_SKIP_SAMPLES,
    AV_FRAME_DATA_AUDIO_SERVICE_TYPE,
    AV_FRAME_DATA_MASTERING_DISPLAY_METADATA,
    AV_FRAME_DATA_GOP_TIMECODE,
    AV_FRAME_DATA_SPHERICAL,
    AV_FRAME_DATA_CONTENT_LIGHT_LEVEL,
    AV_FRAME_DATA_ICC_PROFILE,
    AV_FRAME_DATA_S12M_TIMECODE,
    AV_FRAME_DATA_DYNAMIC_HDR_PLUS,
    AV_FRAME_DATA_REGIONS_OF_INTEREST,
    AV_FRAME_DATA_VIDEO_ENC_PARAMS,
    AV_FRAME_DATA_SEI_UNREGISTERED,
    AV_FRAME_DATA_FILM_GRAIN_PARAMS,
    AV_FRAME_DATA_DETECTION_BBOXES,
    AV_FRAME_DATA_DOVI_RPU_BUFFER,
    AV_FRAME_DATA_DOVI_METADATA,
    AV_FRAME_DATA_DYNAMIC_HDR_VIVID,
    AV_FRAME_DATA_AMBIENT_VIEWING_ENVIRONMENT,
    AV_FRAME_DATA_VIDEO_HINT,
    AV_FRAME_DATA_LCEVC,
    AV_FRAME_DATA_VIEW_ID,
};

enum AVColorRange {
    AVCOL_RANGE_UNSPECIFIED = 0,
    AVCOL_RANGE_MPEG        = 1,
    AVCOL_RANGE_JPEG        = 2,
    AVCOL_RANGE_NB
};

enum AVColorPrimaries {
    AVCOL_PRI_RESERVED0   = 0,
    AVCOL_PRI_BT709       = 1,
    AVCOL_PRI_UNSPECIFIED = 2,
    AVCOL_PRI_RESERVED    = 3,
    AVCOL_PRI_BT470M      = 4,
    AVCOL_PRI_BT470BG     = 5,
    AVCOL_PRI_SMPTE170M   = 6,
    AVCOL_PRI_SMPTE240M   = 7,
    AVCOL_PRI_FILM        = 8,
    AVCOL_PRI_BT2020      = 9,
    AVCOL_PRI_SMPTE428    = 10,
    AVCOL_PRI_SMPTEST428_1 = AVCOL_PRI_SMPTE428,
    AVCOL_PRI_SMPTE431    = 11,
    AVCOL_PRI_SMPTE432    = 12,
    AVCOL_PRI_EBU3213     = 22,
    AVCOL_PRI_JEDEC_P22   = AVCOL_PRI_EBU3213,
    AVCOL_PRI_NB
};

enum AVColorTransferCharacteristic {
    AVCOL_TRC_RESERVED0    = 0,
    AVCOL_TRC_BT709        = 1,
    AVCOL_TRC_UNSPECIFIED  = 2,
    AVCOL_TRC_RESERVED     = 3,
    AVCOL_TRC_GAMMA22      = 4,
    AVCOL_TRC_GAMMA28      = 5,
    AVCOL_TRC_SMPTE170M    = 6,
    AVCOL_TRC_SMPTE240M    = 7,
    AVCOL_TRC_LINEAR       = 8,
    AVCOL_TRC_LOG          = 9,
    AVCOL_TRC_LOG_SQRT     = 10,
    AVCOL_TRC_IEC61966_2_4 = 11,
    AVCOL_TRC_BT1361_ECG   = 12,
    AVCOL_TRC_IEC61966_2_1 = 13,
    AVCOL_TRC_BT2020_10    = 14,
    AVCOL_TRC_BT2020_12    = 15,
    AVCOL_TRC_SMPTE2084    = 16,
    AVCOL_TRC_SMPTEST2084  = AVCOL_TRC_SMPTE2084,
    AVCOL_TRC_SMPTE428     = 17,
    AVCOL_TRC_SMPTEST428_1 = AVCOL_TRC_SMPTE428,
    AVCOL_TRC_ARIB_STD_B67 = 18,
    AVCOL_TRC_NB
};

enum AVColorSpace {
    AVCOL_SPC_RGB         = 0,
    AVCOL_SPC_BT709       = 1,
    AVCOL_SPC_UNSPECIFIED = 2,
    AVCOL_SPC_RESERVED    = 3,
    AVCOL_SPC_FCC         = 4,
    AVCOL_SPC_BT470BG     = 5,
    AVCOL_SPC_SMPTE170M   = 6,
    AVCOL_SPC_SMPTE240M   = 7,
    AVCOL_SPC_YCGCO       = 8,
    AVCOL_SPC_YCOCG       = AVCOL_SPC_YCGCO,
    AVCOL_SPC_BT2020_NCL  = 9,
    AVCOL_SPC_BT2020_CL   = 10,
    AVCOL_SPC_SMPTE2085   = 11,
    AVCOL_SPC_CHROMA_DERIVED_NCL = 12,
    AVCOL_SPC_CHROMA_DERIVED_CL = 13,
    AVCOL_SPC_ICTCP       = 14,
    AVCOL_SPC_IPT_C2      = 15,
    AVCOL_SPC_YCGCO_RE    = 16,
    AVCOL_SPC_YCGCO_RO    = 17,
    AVCOL_SPC_NB
};

enum AVChromaLocation {
    AVCHROMA_LOC_UNSPECIFIED = 0,
    AVCHROMA_LOC_LEFT        = 1,
    AVCHROMA_LOC_CENTER      = 2,
    AVCHROMA_LOC_TOPLEFT     = 3,
    AVCHROMA_LOC_TOP         = 4,
    AVCHROMA_LOC_BOTTOMLEFT  = 5,
    AVCHROMA_LOC_BOTTOM      = 6,
    AVCHROMA_LOC_NB
};

enum AVChannelOrder {
    AV_CHANNEL_ORDER_UNSPEC,
    AV_CHANNEL_ORDER_NATIVE,
    AV_CHANNEL_ORDER_CUSTOM,
    AV_CHANNEL_ORDER_AMBISONIC,
    FF_CHANNEL_ORDER_NB
};

enum AVChannel {
    AV_CHAN_NONE = -1,
    AV_CHAN_FRONT_LEFT,
    AV_CHAN_FRONT_RIGHT,
    AV_CHAN_FRONT_CENTER,
    AV_CHAN_LOW_FREQUENCY,
    AV_CHAN_BACK_LEFT,
    AV_CHAN_BACK_RIGHT,
    AV_CHAN_FRONT_LEFT_OF_CENTER,
    AV_CHAN_FRONT_RIGHT_OF_CENTER,
    AV_CHAN_BACK_CENTER,
    AV_CHAN_SIDE_LEFT,
    AV_CHAN_SIDE_RIGHT,
    AV_CHAN_TOP_CENTER,
    AV_CHAN_TOP_FRONT_LEFT,
    AV_CHAN_TOP_FRONT_CENTER,
    AV_CHAN_TOP_FRONT_RIGHT,
    AV_CHAN_TOP_BACK_LEFT,
    AV_CHAN_TOP_BACK_CENTER,
    AV_CHAN_TOP_BACK_RIGHT,
    AV_CHAN_STEREO_LEFT = 29,
    AV_CHAN_STEREO_RIGHT,
    AV_CHAN_WIDE_LEFT,
    AV_CHAN_WIDE_RIGHT,
    AV_CHAN_SURROUND_DIRECT_LEFT,
    AV_CHAN_SURROUND_DIRECT_RIGHT,
    AV_CHAN_LOW_FREQUENCY_2,
    AV_CHAN_TOP_SIDE_LEFT,
    AV_CHAN_TOP_SIDE_RIGHT,
    AV_CHAN_BOTTOM_FRONT_CENTER,
    AV_CHAN_BOTTOM_FRONT_LEFT,
    AV_CHAN_BOTTOM_FRONT_RIGHT,
    AV_CHAN_SIDE_SURROUND_LEFT,
    AV_CHAN_SIDE_SURROUND_RIGHT,
    AV_CHAN_TOP_SURROUND_LEFT,
    AV_CHAN_TOP_SURROUND_RIGHT,
    AV_CHAN_UNUSED = 0x200,
    AV_CHAN_UNKNOWN = 0x300,
    AV_CHAN_AMBISONIC_BASE = 0x400,
    AV_CHAN_AMBISONIC_END  = 0x7ff,
};

enum AVPixelFormat {
    AV_PIX_FMT_NONE = -1,
    AV_PIX_FMT_YUV420P,
    AV_PIX_FMT_YUYV422,
    AV_PIX_FMT_RGB24,
    AV_PIX_FMT_BGR24,
    AV_PIX_FMT_YUV422P,
    AV_PIX_FMT_YUV444P,
    AV_PIX_FMT_YUV410P,
    AV_PIX_FMT_YUV411P,
    AV_PIX_FMT_GRAY8,
    AV_PIX_FMT_MONOWHITE,
    AV_PIX_FMT_MONOBLACK,
    AV_PIX_FMT_PAL8,
    AV_PIX_FMT_YUVJ420P,
    AV_PIX_FMT_YUVJ422P,
    AV_PIX_FMT_YUVJ444P,
    AV_PIX_FMT_UYVY422,
    AV_PIX_FMT_UYYVYY411,
    AV_PIX_FMT_BGR8,
    AV_PIX_FMT_BGR4,
    AV_PIX_FMT_BGR4_BYTE,
    AV_PIX_FMT_RGB8,
    AV_PIX_FMT_RGB4,
    AV_PIX_FMT_RGB4_BYTE,
    AV_PIX_FMT_NV12,
    AV_PIX_FMT_NV21,
    AV_PIX_FMT_ARGB,
    AV_PIX_FMT_RGBA,
    AV_PIX_FMT_ABGR,
    AV_PIX_FMT_BGRA,
    AV_PIX_FMT_GRAY16BE,
    AV_PIX_FMT_GRAY16LE,
    AV_PIX_FMT_YUV440P,
    AV_PIX_FMT_YUVJ440P,
    AV_PIX_FMT_YUVA420P,
    AV_PIX_FMT_RGB48BE,
    AV_PIX_FMT_RGB48LE,
    AV_PIX_FMT_RGB565BE,
    AV_PIX_FMT_RGB565LE,
    AV_PIX_FMT_RGB555BE,
    AV_PIX_FMT_RGB555LE,
    AV_PIX_FMT_BGR565BE,
    AV_PIX_FMT_BGR565LE,
    AV_PIX_FMT_BGR555BE,
    AV_PIX_FMT_BGR555LE,
    AV_PIX_FMT_VAAPI,
    AV_PIX_FMT_YUV420P16LE,
    AV_PIX_FMT_YUV420P16BE,
    AV_PIX_FMT_YUV422P16LE,
    AV_PIX_FMT_YUV422P16BE,
    AV_PIX_FMT_YUV444P16LE,
    AV_PIX_FMT_YUV444P16BE,
    AV_PIX_FMT_DXVA2_VLD,
    AV_PIX_FMT_RGB444LE,
    AV_PIX_FMT_RGB444BE,
    AV_PIX_FMT_BGR444LE,
    AV_PIX_FMT_BGR444BE,
    AV_PIX_FMT_YA8,
    AV_PIX_FMT_Y400A = AV_PIX_FMT_YA8,
    AV_PIX_FMT_GRAY8A= AV_PIX_FMT_YA8,
    AV_PIX_FMT_BGR48BE,
    AV_PIX_FMT_BGR48LE,
    AV_PIX_FMT_YUV420P9BE,
    AV_PIX_FMT_YUV420P9LE,
    AV_PIX_FMT_YUV420P10BE,
    AV_PIX_FMT_YUV420P10LE,
    AV_PIX_FMT_YUV422P10BE,
    AV_PIX_FMT_YUV422P10LE,
    AV_PIX_FMT_YUV444P9BE,
    AV_PIX_FMT_YUV444P9LE,
    AV_PIX_FMT_YUV444P10BE,
    AV_PIX_FMT_YUV444P10LE,
    AV_PIX_FMT_YUV422P9BE,
    AV_PIX_FMT_YUV422P9LE,
    AV_PIX_FMT_GBRP,
    AV_PIX_FMT_GBR24P = AV_PIX_FMT_GBRP,
    AV_PIX_FMT_GBRP9BE,
    AV_PIX_FMT_GBRP9LE,
    AV_PIX_FMT_GBRP10BE,
    AV_PIX_FMT_GBRP10LE,
    AV_PIX_FMT_GBRP16BE,
    AV_PIX_FMT_GBRP16LE,
    AV_PIX_FMT_YUVA422P,
    AV_PIX_FMT_YUVA444P,
    AV_PIX_FMT_YUVA420P9BE,
    AV_PIX_FMT_YUVA420P9LE,
    AV_PIX_FMT_YUVA422P9BE,
    AV_PIX_FMT_YUVA422P9LE,
    AV_PIX_FMT_YUVA444P9BE,
    AV_PIX_FMT_YUVA444P9LE,
    AV_PIX_FMT_YUVA420P10BE,
    AV_PIX_FMT_YUVA420P10LE,
    AV_PIX_FMT_YUVA422P10BE,
    AV_PIX_FMT_YUVA422P10LE,
    AV_PIX_FMT_YUVA444P10BE,
    AV_PIX_FMT_YUVA444P10LE,
    AV_PIX_FMT_YUVA420P16BE,
    AV_PIX_FMT_YUVA420P16LE,
    AV_PIX_FMT_YUVA422P16BE,
    AV_PIX_FMT_YUVA422P16LE,
    AV_PIX_FMT_YUVA444P16BE,
    AV_PIX_FMT_YUVA444P16LE,
    AV_PIX_FMT_VDPAU,
    AV_PIX_FMT_XYZ12LE,
    AV_PIX_FMT_XYZ12BE,
    AV_PIX_FMT_NV16,
    AV_PIX_FMT_NV20LE,
    AV_PIX_FMT_NV20BE,
    AV_PIX_FMT_RGBA64BE,
    AV_PIX_FMT_RGBA64LE,
    AV_PIX_FMT_BGRA64BE,
    AV_PIX_FMT_BGRA64LE,
    AV_PIX_FMT_YVYU422,
    AV_PIX_FMT_YA16BE,
    AV_PIX_FMT_YA16LE,
    AV_PIX_FMT_GBRAP,
    AV_PIX_FMT_GBRAP16BE,
    AV_PIX_FMT_GBRAP16LE,
    AV_PIX_FMT_QSV,
    AV_PIX_FMT_MMAL,
    AV_PIX_FMT_D3D11VA_VLD,
    AV_PIX_FMT_CUDA,
    AV_PIX_FMT_0RGB,
    AV_PIX_FMT_RGB0,
    AV_PIX_FMT_0BGR,
    AV_PIX_FMT_BGR0,
    AV_PIX_FMT_YUV420P12BE,
    AV_PIX_FMT_YUV420P12LE,
    AV_PIX_FMT_YUV420P14BE,
    AV_PIX_FMT_YUV420P14LE,
    AV_PIX_FMT_YUV422P12BE,
    AV_PIX_FMT_YUV422P12LE,
    AV_PIX_FMT_YUV422P14BE,
    AV_PIX_FMT_YUV422P14LE,
    AV_PIX_FMT_YUV444P12BE,
    AV_PIX_FMT_YUV444P12LE,
    AV_PIX_FMT_YUV444P14BE,
    AV_PIX_FMT_YUV444P14LE,
    AV_PIX_FMT_GBRP12BE,
    AV_PIX_FMT_GBRP12LE,
    AV_PIX_FMT_GBRP14BE,
    AV_PIX_FMT_GBRP14LE,
    AV_PIX_FMT_YUVJ411P,
    AV_PIX_FMT_BAYER_BGGR8,
    AV_PIX_FMT_BAYER_RGGB8,
    AV_PIX_FMT_BAYER_GBRG8,
    AV_PIX_FMT_BAYER_GRBG8,
    AV_PIX_FMT_BAYER_BGGR16LE,
    AV_PIX_FMT_BAYER_BGGR16BE,
    AV_PIX_FMT_BAYER_RGGB16LE,
    AV_PIX_FMT_BAYER_RGGB16BE,
    AV_PIX_FMT_BAYER_GBRG16LE,
    AV_PIX_FMT_BAYER_GBRG16BE,
    AV_PIX_FMT_BAYER_GRBG16LE,
    AV_PIX_FMT_BAYER_GRBG16BE,
    AV_PIX_FMT_YUV440P10LE,
    AV_PIX_FMT_YUV440P10BE,
    AV_PIX_FMT_YUV440P12LE,
    AV_PIX_FMT_YUV440P12BE,
    AV_PIX_FMT_AYUV64LE,
    AV_PIX_FMT_AYUV64BE,
    AV_PIX_FMT_VIDEOTOOLBOX,
    AV_PIX_FMT_P010LE,
    AV_PIX_FMT_P010BE,
    AV_PIX_FMT_GBRAP12BE,
    AV_PIX_FMT_GBRAP12LE,
    AV_PIX_FMT_GBRAP10BE,
    AV_PIX_FMT_GBRAP10LE,
    AV_PIX_FMT_MEDIACODEC,
    AV_PIX_FMT_GRAY12BE,
    AV_PIX_FMT_GRAY12LE,
    AV_PIX_FMT_GRAY10BE,
    AV_PIX_FMT_GRAY10LE,
    AV_PIX_FMT_P016LE,
    AV_PIX_FMT_P016BE,
    AV_PIX_FMT_D3D11,
    AV_PIX_FMT_GRAY9BE,
    AV_PIX_FMT_GRAY9LE,
    AV_PIX_FMT_GBRPF32BE,
    AV_PIX_FMT_GBRPF32LE,
    AV_PIX_FMT_GBRAPF32BE,
    AV_PIX_FMT_GBRAPF32LE,
    AV_PIX_FMT_DRM_PRIME,
    AV_PIX_FMT_OPENCL,
    AV_PIX_FMT_GRAY14BE,
    AV_PIX_FMT_GRAY14LE,
    AV_PIX_FMT_GRAYF32BE,
    AV_PIX_FMT_GRAYF32LE,
    AV_PIX_FMT_YUVA422P12BE,
    AV_PIX_FMT_YUVA422P12LE,
    AV_PIX_FMT_YUVA444P12BE,
    AV_PIX_FMT_YUVA444P12LE,
    AV_PIX_FMT_NV24,
    AV_PIX_FMT_NV42,
    AV_PIX_FMT_VULKAN,
    AV_PIX_FMT_Y210BE,
    AV_PIX_FMT_Y210LE,
    AV_PIX_FMT_X2RGB10LE,
    AV_PIX_FMT_X2RGB10BE,
    AV_PIX_FMT_X2BGR10LE,
    AV_PIX_FMT_X2BGR10BE,
    AV_PIX_FMT_P210BE,
    AV_PIX_FMT_P210LE,
    AV_PIX_FMT_P410BE,
    AV_PIX_FMT_P410LE,
    AV_PIX_FMT_P216BE,
    AV_PIX_FMT_P216LE,
    AV_PIX_FMT_P416BE,
    AV_PIX_FMT_P416LE,
    AV_PIX_FMT_VUYA,
    AV_PIX_FMT_RGBAF16BE,
    AV_PIX_FMT_RGBAF16LE,
    AV_PIX_FMT_VUYX,
    AV_PIX_FMT_P012LE,
    AV_PIX_FMT_P012BE,
    AV_PIX_FMT_Y212BE,
    AV_PIX_FMT_Y212LE,
    AV_PIX_FMT_XV30BE,
    AV_PIX_FMT_XV30LE,
    AV_PIX_FMT_XV36BE,
    AV_PIX_FMT_XV36LE,
    AV_PIX_FMT_RGBF32BE,
    AV_PIX_FMT_RGBF32LE,
    AV_PIX_FMT_RGBAF32BE,
    AV_PIX_FMT_RGBAF32LE,
    AV_PIX_FMT_P212BE,
    AV_PIX_FMT_P212LE,
    AV_PIX_FMT_P412BE,
    AV_PIX_FMT_P412LE,
    AV_PIX_FMT_GBRAP14BE,
    AV_PIX_FMT_GBRAP14LE,
    AV_PIX_FMT_D3D12,
    AV_PIX_FMT_NB
};

enum AVCodecID {
    AV_CODEC_ID_NONE,
    AV_CODEC_ID_MPEG1VIDEO,
    AV_CODEC_ID_MPEG2VIDEO,
    AV_CODEC_ID_H261,
    AV_CODEC_ID_H263,
    AV_CODEC_ID_RV10,
    AV_CODEC_ID_RV20,
    AV_CODEC_ID_MJPEG,
    AV_CODEC_ID_MJPEGB,
    AV_CODEC_ID_LJPEG,
    AV_CODEC_ID_SP5X,
    AV_CODEC_ID_JPEGLS,
    AV_CODEC_ID_MPEG4,
    AV_CODEC_ID_RAWVIDEO,
    AV_CODEC_ID_MSMPEG4V1,
    AV_CODEC_ID_MSMPEG4V2,
    AV_CODEC_ID_MSMPEG4V3,
    AV_CODEC_ID_WMV1,
    AV_CODEC_ID_WMV2,
    AV_CODEC_ID_H263P,
    AV_CODEC_ID_H263I,
    AV_CODEC_ID_FLV1,
    AV_CODEC_ID_SVQ1,
    AV_CODEC_ID_SVQ3,
    AV_CODEC_ID_DVVIDEO,
    AV_CODEC_ID_HUFFYUV,
    AV_CODEC_ID_CYUV,
    AV_CODEC_ID_H264,
    AV_CODEC_ID_INDEO3,
    AV_CODEC_ID_VP3,
    AV_CODEC_ID_THEORA,
    AV_CODEC_ID_ASV1,
    AV_CODEC_ID_ASV2,
    AV_CODEC_ID_FFV1,
    AV_CODEC_ID_4XM,
    AV_CODEC_ID_VCR1,
    AV_CODEC_ID_CLJR,
    AV_CODEC_ID_MDEC,
    AV_CODEC_ID_ROQ,
    AV_CODEC_ID_INTERPLAY_VIDEO,
    AV_CODEC_ID_XAN_WC3,
    AV_CODEC_ID_XAN_WC4,
    AV_CODEC_ID_RPZA,
    AV_CODEC_ID_CINEPAK,
    AV_CODEC_ID_WS_VQA,
    AV_CODEC_ID_MSRLE,
    AV_CODEC_ID_MSVIDEO1,
    AV_CODEC_ID_IDCIN,
    AV_CODEC_ID_8BPS,
    AV_CODEC_ID_SMC,
    AV_CODEC_ID_FLIC,
    AV_CODEC_ID_TRUEMOTION1,
    AV_CODEC_ID_VMDVIDEO,
    AV_CODEC_ID_MSZH,
    AV_CODEC_ID_ZLIB,
    AV_CODEC_ID_QTRLE,
    AV_CODEC_ID_TSCC,
    AV_CODEC_ID_ULTI,
    AV_CODEC_ID_QDRAW,
    AV_CODEC_ID_VIXL,
    AV_CODEC_ID_QPEG,
    AV_CODEC_ID_PNG,
    AV_CODEC_ID_PPM,
    AV_CODEC_ID_PBM,
    AV_CODEC_ID_PGM,
    AV_CODEC_ID_PGMYUV,
    AV_CODEC_ID_PAM,
    AV_CODEC_ID_FFVHUFF,
    AV_CODEC_ID_RV30,
    AV_CODEC_ID_RV40,
    AV_CODEC_ID_VC1,
    AV_CODEC_ID_WMV3,
    AV_CODEC_ID_LOCO,
    AV_CODEC_ID_WNV1,
    AV_CODEC_ID_AASC,
    AV_CODEC_ID_INDEO2,
    AV_CODEC_ID_FRAPS,
    AV_CODEC_ID_TRUEMOTION2,
    AV_CODEC_ID_BMP,
    AV_CODEC_ID_CSCD,
    AV_CODEC_ID_MMVIDEO,
    AV_CODEC_ID_ZMBV,
    AV_CODEC_ID_AVS,
    AV_CODEC_ID_SMACKVIDEO,
    AV_CODEC_ID_NUV,
    AV_CODEC_ID_KMVC,
    AV_CODEC_ID_FLASHSV,
    AV_CODEC_ID_CAVS,
    AV_CODEC_ID_JPEG2000,
    AV_CODEC_ID_VMNC,
    AV_CODEC_ID_VP5,
    AV_CODEC_ID_VP6,
    AV_CODEC_ID_VP6F,
    AV_CODEC_ID_TARGA,
    AV_CODEC_ID_DSICINVIDEO,
    AV_CODEC_ID_TIERTEXSEQVIDEO,
    AV_CODEC_ID_TIFF,
    AV_CODEC_ID_GIF,
    AV_CODEC_ID_DXA,
    AV_CODEC_ID_DNXHD,
    AV_CODEC_ID_THP,
    AV_CODEC_ID_SGI,
    AV_CODEC_ID_C93,
    AV_CODEC_ID_BETHSOFTVID,
    AV_CODEC_ID_PTX,
    AV_CODEC_ID_TXD,
    AV_CODEC_ID_VP6A,
    AV_CODEC_ID_AMV,
    AV_CODEC_ID_VB,
    AV_CODEC_ID_PCX,
    AV_CODEC_ID_SUNRAST,
    AV_CODEC_ID_INDEO4,
    AV_CODEC_ID_INDEO5,
    AV_CODEC_ID_MIMIC,
    AV_CODEC_ID_RL2,
    AV_CODEC_ID_ESCAPE124,
    AV_CODEC_ID_DIRAC,
    AV_CODEC_ID_BFI,
    AV_CODEC_ID_CMV,
    AV_CODEC_ID_MOTIONPIXELS,
    AV_CODEC_ID_TGV,
    AV_CODEC_ID_TGQ,
    AV_CODEC_ID_TQI,
    AV_CODEC_ID_AURA,
    AV_CODEC_ID_AURA2,
    AV_CODEC_ID_V210X,
    AV_CODEC_ID_TMV,
    AV_CODEC_ID_V210,
    AV_CODEC_ID_DPX,
    AV_CODEC_ID_MAD,
    AV_CODEC_ID_FRWU,
    AV_CODEC_ID_FLASHSV2,
    AV_CODEC_ID_CDGRAPHICS,
    AV_CODEC_ID_R210,
    AV_CODEC_ID_ANM,
    AV_CODEC_ID_BINKVIDEO,
    AV_CODEC_ID_IFF_ILBM,
#define AV_CODEC_ID_IFF_BYTERUN1 AV_CODEC_ID_IFF_ILBM
    AV_CODEC_ID_KGV1,
    AV_CODEC_ID_YOP,
    AV_CODEC_ID_VP8,
    AV_CODEC_ID_PICTOR,
    AV_CODEC_ID_ANSI,
    AV_CODEC_ID_A64_MULTI,
    AV_CODEC_ID_A64_MULTI5,
    AV_CODEC_ID_R10K,
    AV_CODEC_ID_MXPEG,
    AV_CODEC_ID_LAGARITH,
    AV_CODEC_ID_PRORES,
    AV_CODEC_ID_JV,
    AV_CODEC_ID_DFA,
    AV_CODEC_ID_WMV3IMAGE,
    AV_CODEC_ID_VC1IMAGE,
    AV_CODEC_ID_UTVIDEO,
    AV_CODEC_ID_BMV_VIDEO,
    AV_CODEC_ID_VBLE,
    AV_CODEC_ID_DXTORY,
    AV_CODEC_ID_V410,
    AV_CODEC_ID_XWD,
    AV_CODEC_ID_CDXL,
    AV_CODEC_ID_XBM,
    AV_CODEC_ID_ZEROCODEC,
    AV_CODEC_ID_MSS1,
    AV_CODEC_ID_MSA1,
    AV_CODEC_ID_TSCC2,
    AV_CODEC_ID_MTS2,
    AV_CODEC_ID_CLLC,
    AV_CODEC_ID_MSS2,
    AV_CODEC_ID_VP9,
    AV_CODEC_ID_AIC,
    AV_CODEC_ID_ESCAPE130,
    AV_CODEC_ID_G2M,
    AV_CODEC_ID_WEBP,
    AV_CODEC_ID_HNM4_VIDEO,
    AV_CODEC_ID_HEVC,
#define AV_CODEC_ID_H265 AV_CODEC_ID_HEVC
    AV_CODEC_ID_FIC,
    AV_CODEC_ID_ALIAS_PIX,
    AV_CODEC_ID_BRENDER_PIX,
    AV_CODEC_ID_PAF_VIDEO,
    AV_CODEC_ID_EXR,
    AV_CODEC_ID_VP7,
    AV_CODEC_ID_SANM,
    AV_CODEC_ID_SGIRLE,
    AV_CODEC_ID_MVC1,
    AV_CODEC_ID_MVC2,
    AV_CODEC_ID_HQX,
    AV_CODEC_ID_TDSC,
    AV_CODEC_ID_HQ_HQA,
    AV_CODEC_ID_HAP,
    AV_CODEC_ID_DDS,
    AV_CODEC_ID_DXV,
    AV_CODEC_ID_SCREENPRESSO,
    AV_CODEC_ID_RSCC,
    AV_CODEC_ID_AVS2,
    AV_CODEC_ID_PGX,
    AV_CODEC_ID_AVS3,
    AV_CODEC_ID_MSP2,
    AV_CODEC_ID_VVC,
#define AV_CODEC_ID_H266 AV_CODEC_ID_VVC
    AV_CODEC_ID_Y41P,
    AV_CODEC_ID_AVRP,
    AV_CODEC_ID_012V,
    AV_CODEC_ID_AVUI,
    AV_CODEC_ID_TARGA_Y216,
    AV_CODEC_ID_V308,
    AV_CODEC_ID_V408,
    AV_CODEC_ID_YUV4,
    AV_CODEC_ID_AVRN,
    AV_CODEC_ID_CPIA,
    AV_CODEC_ID_XFACE,
    AV_CODEC_ID_SNOW,
    AV_CODEC_ID_SMVJPEG,
    AV_CODEC_ID_APNG,
    AV_CODEC_ID_DAALA,
    AV_CODEC_ID_CFHD,
    AV_CODEC_ID_TRUEMOTION2RT,
    AV_CODEC_ID_M101,
    AV_CODEC_ID_MAGICYUV,
    AV_CODEC_ID_SHEERVIDEO,
    AV_CODEC_ID_YLC,
    AV_CODEC_ID_PSD,
    AV_CODEC_ID_PIXLET,
    AV_CODEC_ID_SPEEDHQ,
    AV_CODEC_ID_FMVC,
    AV_CODEC_ID_SCPR,
    AV_CODEC_ID_CLEARVIDEO,
    AV_CODEC_ID_XPM,
    AV_CODEC_ID_AV1,
    AV_CODEC_ID_BITPACKED,
    AV_CODEC_ID_MSCC,
    AV_CODEC_ID_SRGC,
    AV_CODEC_ID_SVG,
    AV_CODEC_ID_GDV,
    AV_CODEC_ID_FITS,
    AV_CODEC_ID_IMM4,
    AV_CODEC_ID_PROSUMER,
    AV_CODEC_ID_MWSC,
    AV_CODEC_ID_WCMV,
    AV_CODEC_ID_RASC,
    AV_CODEC_ID_HYMT,
    AV_CODEC_ID_ARBC,
    AV_CODEC_ID_AGM,
    AV_CODEC_ID_LSCR,
    AV_CODEC_ID_VP4,
    AV_CODEC_ID_IMM5,
    AV_CODEC_ID_MVDV,
    AV_CODEC_ID_MVHA,
    AV_CODEC_ID_CDTOONS,
    AV_CODEC_ID_MV30,
    AV_CODEC_ID_NOTCHLC,
    AV_CODEC_ID_PFM,
    AV_CODEC_ID_MOBICLIP,
    AV_CODEC_ID_PHOTOCD,
    AV_CODEC_ID_IPU,
    AV_CODEC_ID_ARGO,
    AV_CODEC_ID_CRI,
    AV_CODEC_ID_SIMBIOSIS_IMX,
    AV_CODEC_ID_SGA_VIDEO,
    AV_CODEC_ID_GEM,
    AV_CODEC_ID_VBN,
    AV_CODEC_ID_JPEGXL,
    AV_CODEC_ID_QOI,
    AV_CODEC_ID_PHM,
    AV_CODEC_ID_RADIANCE_HDR,
    AV_CODEC_ID_WBMP,
    AV_CODEC_ID_MEDIA100,
    AV_CODEC_ID_VQC,
    AV_CODEC_ID_PDV,
    AV_CODEC_ID_EVC,
    AV_CODEC_ID_RTV1,
    AV_CODEC_ID_VMIX,
    AV_CODEC_ID_LEAD,
    AV_CODEC_ID_FIRST_AUDIO = 0x10000,
    AV_CODEC_ID_PCM_S16LE = 0x10000,
    AV_CODEC_ID_PCM_S16BE,
    AV_CODEC_ID_PCM_U16LE,
    AV_CODEC_ID_PCM_U16BE,
    AV_CODEC_ID_PCM_S8,
    AV_CODEC_ID_PCM_U8,
    AV_CODEC_ID_PCM_MULAW,
    AV_CODEC_ID_PCM_ALAW,
    AV_CODEC_ID_PCM_S32LE,
    AV_CODEC_ID_PCM_S32BE,
    AV_CODEC_ID_PCM_U32LE,
    AV_CODEC_ID_PCM_U32BE,
    AV_CODEC_ID_PCM_S24LE,
    AV_CODEC_ID_PCM_S24BE,
    AV_CODEC_ID_PCM_U24LE,
    AV_CODEC_ID_PCM_U24BE,
    AV_CODEC_ID_PCM_S24DAUD,
    AV_CODEC_ID_PCM_ZORK,
    AV_CODEC_ID_PCM_S16LE_PLANAR,
    AV_CODEC_ID_PCM_DVD,
    AV_CODEC_ID_PCM_F32BE,
    AV_CODEC_ID_PCM_F32LE,
    AV_CODEC_ID_PCM_F64BE,
    AV_CODEC_ID_PCM_F64LE,
    AV_CODEC_ID_PCM_BLURAY,
    AV_CODEC_ID_PCM_LXF,
    AV_CODEC_ID_S302M,
    AV_CODEC_ID_PCM_S8_PLANAR,
    AV_CODEC_ID_PCM_S24LE_PLANAR,
    AV_CODEC_ID_PCM_S32LE_PLANAR,
    AV_CODEC_ID_PCM_S16BE_PLANAR,
    AV_CODEC_ID_PCM_S64LE,
    AV_CODEC_ID_PCM_S64BE,
    AV_CODEC_ID_PCM_F16LE,
    AV_CODEC_ID_PCM_F24LE,
    AV_CODEC_ID_PCM_VIDC,
    AV_CODEC_ID_PCM_SGA,
    AV_CODEC_ID_ADPCM_IMA_QT = 0x11000,
    AV_CODEC_ID_ADPCM_IMA_WAV,
    AV_CODEC_ID_ADPCM_IMA_DK3,
    AV_CODEC_ID_ADPCM_IMA_DK4,
    AV_CODEC_ID_ADPCM_IMA_WS,
    AV_CODEC_ID_ADPCM_IMA_SMJPEG,
    AV_CODEC_ID_ADPCM_MS,
    AV_CODEC_ID_ADPCM_4XM,
    AV_CODEC_ID_ADPCM_XA,
    AV_CODEC_ID_ADPCM_ADX,
    AV_CODEC_ID_ADPCM_EA,
    AV_CODEC_ID_ADPCM_G726,
    AV_CODEC_ID_ADPCM_CT,
    AV_CODEC_ID_ADPCM_SWF,
    AV_CODEC_ID_ADPCM_YAMAHA,
    AV_CODEC_ID_ADPCM_SBPRO_4,
    AV_CODEC_ID_ADPCM_SBPRO_3,
    AV_CODEC_ID_ADPCM_SBPRO_2,
    AV_CODEC_ID_ADPCM_THP,
    AV_CODEC_ID_ADPCM_IMA_AMV,
    AV_CODEC_ID_ADPCM_EA_R1,
    AV_CODEC_ID_ADPCM_EA_R3,
    AV_CODEC_ID_ADPCM_EA_R2,
    AV_CODEC_ID_ADPCM_IMA_EA_SEAD,
    AV_CODEC_ID_ADPCM_IMA_EA_EACS,
    AV_CODEC_ID_ADPCM_EA_XAS,
    AV_CODEC_ID_ADPCM_EA_MAXIS_XA,
    AV_CODEC_ID_ADPCM_IMA_ISS,
    AV_CODEC_ID_ADPCM_G722,
    AV_CODEC_ID_ADPCM_IMA_APC,
    AV_CODEC_ID_ADPCM_VIMA,
    AV_CODEC_ID_ADPCM_AFC,
    AV_CODEC_ID_ADPCM_IMA_OKI,
    AV_CODEC_ID_ADPCM_DTK,
    AV_CODEC_ID_ADPCM_IMA_RAD,
    AV_CODEC_ID_ADPCM_G726LE,
    AV_CODEC_ID_ADPCM_THP_LE,
    AV_CODEC_ID_ADPCM_PSX,
    AV_CODEC_ID_ADPCM_AICA,
    AV_CODEC_ID_ADPCM_IMA_DAT4,
    AV_CODEC_ID_ADPCM_MTAF,
    AV_CODEC_ID_ADPCM_AGM,
    AV_CODEC_ID_ADPCM_ARGO,
    AV_CODEC_ID_ADPCM_IMA_SSI,
    AV_CODEC_ID_ADPCM_ZORK,
    AV_CODEC_ID_ADPCM_IMA_APM,
    AV_CODEC_ID_ADPCM_IMA_ALP,
    AV_CODEC_ID_ADPCM_IMA_MTF,
    AV_CODEC_ID_ADPCM_IMA_CUNNING,
    AV_CODEC_ID_ADPCM_IMA_MOFLEX,
    AV_CODEC_ID_ADPCM_IMA_ACORN,
    AV_CODEC_ID_ADPCM_XMD,
    AV_CODEC_ID_AMR_NB = 0x12000,
    AV_CODEC_ID_AMR_WB,
    AV_CODEC_ID_RA_144 = 0x13000,
    AV_CODEC_ID_RA_288,
    AV_CODEC_ID_ROQ_DPCM = 0x14000,
    AV_CODEC_ID_INTERPLAY_DPCM,
    AV_CODEC_ID_XAN_DPCM,
    AV_CODEC_ID_SOL_DPCM,
    AV_CODEC_ID_SDX2_DPCM,
    AV_CODEC_ID_GREMLIN_DPCM,
    AV_CODEC_ID_DERF_DPCM,
    AV_CODEC_ID_WADY_DPCM,
    AV_CODEC_ID_CBD2_DPCM,
    AV_CODEC_ID_MP2 = 0x15000,
    AV_CODEC_ID_MP3,
    AV_CODEC_ID_AAC,
    AV_CODEC_ID_AC3,
    AV_CODEC_ID_DTS,
    AV_CODEC_ID_VORBIS,
    AV_CODEC_ID_DVAUDIO,
    AV_CODEC_ID_WMAV1,
    AV_CODEC_ID_WMAV2,
    AV_CODEC_ID_MACE3,
    AV_CODEC_ID_MACE6,
    AV_CODEC_ID_VMDAUDIO,
    AV_CODEC_ID_FLAC,
    AV_CODEC_ID_MP3ADU,
    AV_CODEC_ID_MP3ON4,
    AV_CODEC_ID_SHORTEN,
    AV_CODEC_ID_ALAC,
    AV_CODEC_ID_WESTWOOD_SND1,
    AV_CODEC_ID_GSM,
    AV_CODEC_ID_QDM2,
    AV_CODEC_ID_COOK,
    AV_CODEC_ID_TRUESPEECH,
    AV_CODEC_ID_TTA,
    AV_CODEC_ID_SMACKAUDIO,
    AV_CODEC_ID_QCELP,
    AV_CODEC_ID_WAVPACK,
    AV_CODEC_ID_DSICINAUDIO,
    AV_CODEC_ID_IMC,
    AV_CODEC_ID_MUSEPACK7,
    AV_CODEC_ID_MLP,
    AV_CODEC_ID_GSM_MS,
    AV_CODEC_ID_ATRAC3,
    AV_CODEC_ID_APE,
    AV_CODEC_ID_NELLYMOSER,
    AV_CODEC_ID_MUSEPACK8,
    AV_CODEC_ID_SPEEX,
    AV_CODEC_ID_WMAVOICE,
    AV_CODEC_ID_WMAPRO,
    AV_CODEC_ID_WMALOSSLESS,
    AV_CODEC_ID_ATRAC3P,
    AV_CODEC_ID_EAC3,
    AV_CODEC_ID_SIPR,
    AV_CODEC_ID_MP1,
    AV_CODEC_ID_TWINVQ,
    AV_CODEC_ID_TRUEHD,
    AV_CODEC_ID_MP4ALS,
    AV_CODEC_ID_ATRAC1,
    AV_CODEC_ID_BINKAUDIO_RDFT,
    AV_CODEC_ID_BINKAUDIO_DCT,
    AV_CODEC_ID_AAC_LATM,
    AV_CODEC_ID_QDMC,
    AV_CODEC_ID_CELT,
    AV_CODEC_ID_G723_1,
    AV_CODEC_ID_G729,
    AV_CODEC_ID_8SVX_EXP,
    AV_CODEC_ID_8SVX_FIB,
    AV_CODEC_ID_BMV_AUDIO,
    AV_CODEC_ID_RALF,
    AV_CODEC_ID_IAC,
    AV_CODEC_ID_ILBC,
    AV_CODEC_ID_OPUS,
    AV_CODEC_ID_COMFORT_NOISE,
    AV_CODEC_ID_TAK,
    AV_CODEC_ID_METASOUND,
    AV_CODEC_ID_PAF_AUDIO,
    AV_CODEC_ID_ON2AVC,
    AV_CODEC_ID_DSS_SP,
    AV_CODEC_ID_CODEC2,
    AV_CODEC_ID_FFWAVESYNTH,
    AV_CODEC_ID_SONIC,
    AV_CODEC_ID_SONIC_LS,
    AV_CODEC_ID_EVRC,
    AV_CODEC_ID_SMV,
    AV_CODEC_ID_DSD_LSBF,
    AV_CODEC_ID_DSD_MSBF,
    AV_CODEC_ID_DSD_LSBF_PLANAR,
    AV_CODEC_ID_DSD_MSBF_PLANAR,
    AV_CODEC_ID_4GV,
    AV_CODEC_ID_INTERPLAY_ACM,
    AV_CODEC_ID_XMA1,
    AV_CODEC_ID_XMA2,
    AV_CODEC_ID_DST,
    AV_CODEC_ID_ATRAC3AL,
    AV_CODEC_ID_ATRAC3PAL,
    AV_CODEC_ID_DOLBY_E,
    AV_CODEC_ID_APTX,
    AV_CODEC_ID_APTX_HD,
    AV_CODEC_ID_SBC,
    AV_CODEC_ID_ATRAC9,
    AV_CODEC_ID_HCOM,
    AV_CODEC_ID_ACELP_KELVIN,
    AV_CODEC_ID_MPEGH_3D_AUDIO,
    AV_CODEC_ID_SIREN,
    AV_CODEC_ID_HCA,
    AV_CODEC_ID_FASTAUDIO,
    AV_CODEC_ID_MSNSIREN,
    AV_CODEC_ID_DFPWM,
    AV_CODEC_ID_BONK,
    AV_CODEC_ID_MISC4,
    AV_CODEC_ID_APAC,
    AV_CODEC_ID_FTR,
    AV_CODEC_ID_WAVARC,
    AV_CODEC_ID_RKA,
    AV_CODEC_ID_AC4,
    AV_CODEC_ID_OSQ,
    AV_CODEC_ID_QOA,
    AV_CODEC_ID_LC3,
    AV_CODEC_ID_FIRST_SUBTITLE = 0x17000,
    AV_CODEC_ID_DVD_SUBTITLE = 0x17000,
    AV_CODEC_ID_DVB_SUBTITLE,
    AV_CODEC_ID_TEXT,
    AV_CODEC_ID_XSUB,
    AV_CODEC_ID_SSA,
    AV_CODEC_ID_MOV_TEXT,
    AV_CODEC_ID_HDMV_PGS_SUBTITLE,
    AV_CODEC_ID_DVB_TELETEXT,
    AV_CODEC_ID_SRT,
    AV_CODEC_ID_MICRODVD,
    AV_CODEC_ID_EIA_608,
    AV_CODEC_ID_JACOSUB,
    AV_CODEC_ID_SAMI,
    AV_CODEC_ID_REALTEXT,
    AV_CODEC_ID_STL,
    AV_CODEC_ID_SUBVIEWER1,
    AV_CODEC_ID_SUBVIEWER,
    AV_CODEC_ID_SUBRIP,
    AV_CODEC_ID_WEBVTT,
    AV_CODEC_ID_MPL2,
    AV_CODEC_ID_VPLAYER,
    AV_CODEC_ID_PJS,
    AV_CODEC_ID_ASS,
    AV_CODEC_ID_HDMV_TEXT_SUBTITLE,
    AV_CODEC_ID_TTML,
    AV_CODEC_ID_ARIB_CAPTION,
    AV_CODEC_ID_FIRST_UNKNOWN = 0x18000,
    AV_CODEC_ID_TTF = 0x18000,
    AV_CODEC_ID_SCTE_35,
    AV_CODEC_ID_EPG,
    AV_CODEC_ID_BINTEXT,
    AV_CODEC_ID_XBIN,
    AV_CODEC_ID_IDF,
    AV_CODEC_ID_OTF,
    AV_CODEC_ID_SMPTE_KLV,
    AV_CODEC_ID_DVD_NAV,
    AV_CODEC_ID_TIMED_ID3,
    AV_CODEC_ID_BIN_DATA,
    AV_CODEC_ID_SMPTE_2038,
    AV_CODEC_ID_LCEVC,
    AV_CODEC_ID_PROBE = 0x19000,
    AV_CODEC_ID_MPEG2TS = 0x20000,
    AV_CODEC_ID_MPEG4SYSTEMS = 0x20001,
    AV_CODEC_ID_FFMETADATA = 0x21000,
    AV_CODEC_ID_WRAPPED_AVFRAME = 0x21001,
    AV_CODEC_ID_VNULL,
    AV_CODEC_ID_ANULL,
};

enum AVFieldOrder {
    AV_FIELD_UNKNOWN,
    AV_FIELD_PROGRESSIVE,
    AV_FIELD_TT,
    AV_FIELD_BB,
    AV_FIELD_TB,
    AV_FIELD_BT,
};

enum AVAudioServiceType {
    AV_AUDIO_SERVICE_TYPE_MAIN              = 0,
    AV_AUDIO_SERVICE_TYPE_EFFECTS           = 1,
    AV_AUDIO_SERVICE_TYPE_VISUALLY_IMPAIRED = 2,
    AV_AUDIO_SERVICE_TYPE_HEARING_IMPAIRED  = 3,
    AV_AUDIO_SERVICE_TYPE_DIALOGUE          = 4,
    AV_AUDIO_SERVICE_TYPE_COMMENTARY        = 5,
    AV_AUDIO_SERVICE_TYPE_EMERGENCY         = 6,
    AV_AUDIO_SERVICE_TYPE_VOICE_OVER        = 7,
    AV_AUDIO_SERVICE_TYPE_KARAOKE           = 8,
    AV_AUDIO_SERVICE_TYPE_NB,
};

enum AVSampleFormat {
    AV_SAMPLE_FMT_NONE = -1,
    AV_SAMPLE_FMT_U8,
    AV_SAMPLE_FMT_S16,
    AV_SAMPLE_FMT_S32,
    AV_SAMPLE_FMT_FLT,
    AV_SAMPLE_FMT_DBL,
    AV_SAMPLE_FMT_U8P,
    AV_SAMPLE_FMT_S16P,
    AV_SAMPLE_FMT_S32P,
    AV_SAMPLE_FMT_FLTP,
    AV_SAMPLE_FMT_DBLP,
    AV_SAMPLE_FMT_S64,
    AV_SAMPLE_FMT_S64P,
    AV_SAMPLE_FMT_NB
};

enum AVDiscard{
    AVDISCARD_NONE    =-16,
    AVDISCARD_DEFAULT =  0,
    AVDISCARD_NONREF  =  8,
    AVDISCARD_BIDIR   = 16,
    AVDISCARD_NONINTRA= 24,
    AVDISCARD_NONKEY  = 32,
    AVDISCARD_ALL     = 48,
};

enum AVStreamGroupParamsType {
    AV_STREAM_GROUP_PARAMS_NONE,
    AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT,
    AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION,
    AV_STREAM_GROUP_PARAMS_TILE_GRID,
    AV_STREAM_GROUP_PARAMS_LCEVC,
};

enum AVDurationEstimationMethod {
    AVFMT_DURATION_FROM_PTS,
    AVFMT_DURATION_FROM_STREAM,
    AVFMT_DURATION_FROM_BITRATE
};

typedef enum {
    AV_CLASS_CATEGORY_NA = 0,
    AV_CLASS_CATEGORY_INPUT,
    AV_CLASS_CATEGORY_OUTPUT,
    AV_CLASS_CATEGORY_MUXER,
    AV_CLASS_CATEGORY_DEMUXER,
    AV_CLASS_CATEGORY_ENCODER,
    AV_CLASS_CATEGORY_DECODER,
    AV_CLASS_CATEGORY_FILTER,
    AV_CLASS_CATEGORY_BITSTREAM_FILTER,
    AV_CLASS_CATEGORY_SWSCALER,
    AV_CLASS_CATEGORY_SWRESAMPLER,
    AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT = 40,
    AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT,
    AV_CLASS_CATEGORY_DEVICE_AUDIO_OUTPUT,
    AV_CLASS_CATEGORY_DEVICE_AUDIO_INPUT,
    AV_CLASS_CATEGORY_DEVICE_OUTPUT,
    AV_CLASS_CATEGORY_DEVICE_INPUT,
    AV_CLASS_CATEGORY_NB
}AVClassCategory;

// Structs
typedef struct AVRational{
    int num;
    int den;
} AVRational;

typedef struct AVBuffer AVBuffer;

typedef struct AVBufferRef {
    AVBuffer *buffer;
    uint8_t *data;
    size_t   size;
} AVBufferRef;

typedef struct AVPacketSideData {
    uint8_t *data;
    size_t   size;
    enum AVPacketSideDataType type;
} AVPacketSideData;

typedef struct AVPacket {
    AVBufferRef *buf;
    int64_t pts;
    int64_t dts;
    uint8_t *data;
    int   size;
    int   stream_index;
    int   flags;
    AVPacketSideData *side_data;
    int side_data_elems;
    int64_t duration;
    int64_t pos;
    void *opaque;
    AVBufferRef *opaque_ref;
    AVRational time_base;
} AVPacket;

typedef struct AVDictionaryEntry {
    char *key;
    char *value;
} AVDictionaryEntry;

typedef struct AVDictionary AVDictionary;

typedef struct AVFrameSideData {
    enum AVFrameSideDataType type;
    uint8_t *data;
    size_t   size;
    AVDictionary *metadata;
    AVBufferRef *buf;
} AVFrameSideData;

typedef struct AVChannelCustom {
    enum AVChannel id;
    char name[16];
    void *opaque;
} AVChannelCustom;

typedef struct AVChannelLayout {
    enum AVChannelOrder order;
    int nb_channels;
    union {
        uint64_t mask;
        AVChannelCustom *map;
    } u;
    void *opaque;
} AVChannelLayout;

typedef struct AVFrame {
#define AV_NUM_DATA_POINTERS 8
    uint8_t *data[8];
    int linesize[8];
    uint8_t **extended_data;
    int width, height;
    int nb_samples;
    int format;
#if FF_API_FRAME_KEY
    int key_frame;
#endif
    enum AVPictureType pict_type;
    AVRational sample_aspect_ratio;
    int64_t pts;
    int64_t pkt_dts;
    AVRational time_base;
    int quality;
    void *opaque;
    int repeat_pict;
#if FF_API_INTERLACED_FRAME
    int interlaced_frame;
    int top_field_first;
#endif
#if FF_API_PALETTE_HAS_CHANGED
    int palette_has_changed;
#endif
    int sample_rate;
    AVBufferRef *buf[8];
    AVBufferRef **extended_buf;
    int        nb_extended_buf;
    AVFrameSideData **side_data;
    int            nb_side_data;
#define AV_FRAME_FLAG_CORRUPT       (1 << 0)
#define AV_FRAME_FLAG_KEY (1 << 1)
#define AV_FRAME_FLAG_DISCARD   (1 << 2)
#define AV_FRAME_FLAG_INTERLACED (1 << 3)
#define AV_FRAME_FLAG_TOP_FIELD_FIRST (1 << 4)
    int flags;
    enum AVColorRange color_range;
    enum AVColorPrimaries color_primaries;
    enum AVColorTransferCharacteristic color_trc;
    int colorspace;
    enum AVChromaLocation chroma_location;
    int64_t best_effort_timestamp;
#if FF_API_FRAME_PKT
    int64_t pkt_pos;
#endif
    AVDictionary *metadata;
    int decode_error_flags;
#define FF_DECODE_ERROR_INVALID_BITSTREAM   1
#define FF_DECODE_ERROR_MISSING_REFERENCE   2
#define FF_DECODE_ERROR_CONCEALMENT_ACTIVE  4
#define FF_DECODE_ERROR_DECODE_SLICES       8
#if FF_API_FRAME_PKT
    int pkt_size;
#endif
    AVBufferRef *hw_frames_ctx;
    AVBufferRef *opaque_ref;
    size_t crop_top;
    size_t crop_bottom;
    size_t crop_left;
    size_t crop_right;
    AVBufferRef *private_ref;
    AVChannelLayout ch_layout;
    int64_t duration;
} AVFrame;

typedef struct AVClass {
    const char* class_name;
    const char* (*item_name)(void* ctx);
    const struct AVOption *option;
    int version;
    int log_level_offset_offset;
    int parent_log_context_offset;
    AVClassCategory category;
    AVClassCategory (*get_category)(void* ctx);
    int (*query_ranges)(struct AVOptionRanges **, void *obj, const char *key, int flags);
    void* (*child_next)(void *obj, void *prev);
    const struct AVClass* (*child_class_iterate)(void **iter);
} AVClass;

typedef struct AVProfile {
    int profile;
    const char *name;
} AVProfile;

typedef struct AVCodec {
    const char *name;
    const char *long_name;
    enum AVMediaType type;
    enum AVCodecID id;
    int capabilities;
    uint8_t max_lowres;
    const AVRational *supported_framerates;
    const enum AVPixelFormat *pix_fmts;
    const int *supported_samplerates;
    const enum AVSampleFormat *sample_fmts;
    const AVClass *priv_class;
    const AVProfile *profiles;
    const char *wrapper_name;
    const AVChannelLayout *ch_layouts;
} AVCodec;

typedef struct RcOverride{
    int start_frame;
    int end_frame;
    int qscale;
    float quality_factor;
} RcOverride;

typedef struct AVCodecContext {
    const AVClass *av_class;
    int log_level_offset;
    enum AVMediaType codec_type;
    const struct AVCodec  *codec;
    enum AVCodecID     codec_id;
    unsigned int codec_tag;
    void *priv_data;
    struct AVCodecInternal *internal;
    void *opaque;
    int64_t bit_rate;
    int flags;
    int flags2;
    uint8_t *extradata;
    int extradata_size;
    AVRational time_base;
    AVRational pkt_timebase;
    AVRational framerate;
    int ticks_per_frame;
    int delay;
    int width, height;
    int coded_width, coded_height;
    AVRational sample_aspect_ratio;
    enum AVPixelFormat pix_fmt;
    enum AVPixelFormat sw_pix_fmt;
    enum AVColorPrimaries color_primaries;
    enum AVColorTransferCharacteristic color_trc;
    enum AVColorSpace colorspace;
    enum AVColorRange color_range;
    enum AVChromaLocation chroma_sample_location;
    enum AVFieldOrder field_order;
    int refs;
    int has_b_frames;
    int slice_flags;
    void (*draw_horiz_band)(struct AVCodecContext *s, const AVFrame *src, int offset[8], int y, int type, int height);
    enum AVPixelFormat (*get_format)(struct AVCodecContext *s, const enum AVPixelFormat * fmt);
    int max_b_frames;
    float b_quant_factor;
    float b_quant_offset;
    float i_quant_factor;
    float i_quant_offset;
    float lumi_masking;
    float temporal_cplx_masking;
    float spatial_cplx_masking;
    float p_masking;
    float dark_masking;
    int nsse_weight;
    int me_cmp;
    int me_sub_cmp;
    int mb_cmp;
    int ildct_cmp;
    int dia_size;
    int last_predictor_count;
    int me_pre_cmp;
    int pre_dia_size;
    int me_subpel_quality;
    int me_range;
    int mb_decision;
    uint16_t *intra_matrix;
    uint16_t *inter_matrix;
    uint16_t *chroma_intra_matrix;
    int intra_dc_precision;
    int mb_lmin;
    int mb_lmax;
    int bidir_refine;
    int keyint_min;
    int gop_size;
    int mv0_threshold;
    int slices;
    int sample_rate;
    enum AVSampleFormat sample_fmt;
    AVChannelLayout ch_layout;
    int frame_size;
    int block_align;
    int cutoff;
    enum AVAudioServiceType audio_service_type;
    enum AVSampleFormat request_sample_fmt;
    int initial_padding;
    int trailing_padding;
    int seek_preroll;
    int (*get_buffer2)(struct AVCodecContext *s, AVFrame *frame, int flags);
    int bit_rate_tolerance;
    int global_quality;
    int compression_level;
    float qcompress;
    float qblur;
    int qmin;
    int qmax;
    int max_qdiff;
    int rc_buffer_size;
    int rc_override_count;
    RcOverride *rc_override;
    int64_t rc_max_rate;
    int64_t rc_min_rate;
    float rc_max_available_vbv_use;
    float rc_min_vbv_overflow_use;
    int rc_initial_buffer_occupancy;
    int trellis;
    char *stats_out;
    char *stats_in;
    int workaround_bugs;
    int strict_std_compliance;
    int error_concealment;
    int debug;
    int err_recognition;
    const struct AVHWAccel *hwaccel;
    void *hwaccel_context;
    AVBufferRef *hw_frames_ctx;
    AVBufferRef *hw_device_ctx;
    int hwaccel_flags;
    int extra_hw_frames;
    uint64_t error[8];
    int dct_algo;
    int idct_algo;
    int bits_per_coded_sample;
    int bits_per_raw_sample;
    int thread_count;
    int thread_type;
    int active_thread_type;
    int (*execute)(struct AVCodecContext *c, int (*func)(struct AVCodecContext *c2, void *arg), void *arg2, int *ret, int count, int size);
    int (*execute2)(struct AVCodecContext *c, int (*func)(struct AVCodecContext *c2, void *arg, int jobnr, int threadnr), void *arg2, int *ret, int count);
    int profile;
} AVCodecContext;

typedef struct AVCodecDescriptor {
    enum AVCodecID     id;
    enum AVMediaType type;
    const char      *name;
    const char *long_name;
    int             props;
    const char *const *mime_types;
    const struct AVProfile *profiles;
} AVCodecDescriptor;

typedef struct AVComponentDescriptor {
    int plane;
    int step;
    int offset;
    int shift;
    int depth;
} AVComponentDescriptor;

typedef struct AVPixFmtDescriptor {
    const char *name;
    uint8_t nb_components;
    uint8_t log2_chroma_w;
    uint8_t log2_chroma_h;
    uint64_t flags;
    AVComponentDescriptor comp[4];
    const char *alias;
} AVPixFmtDescriptor;

typedef struct AVCodecParameters {
    enum AVMediaType codec_type;
    enum AVCodecID codec_id;
    uint32_t codec_tag;
    uint8_t *extradata;
    int extradata_size;
    AVPacketSideData *coded_side_data;
    int nb_coded_side_data;
    int format;
    int64_t bit_rate;
    int bits_per_coded_sample;
    int bits_per_raw_sample;
    int profile;
    int level;
    int width;
    int height;
    AVRational sample_aspect_ratio;
    AVRational framerate;
    enum AVFieldOrder field_order;
    enum AVColorRange color_range;
    enum AVColorPrimaries color_primaries;
    enum AVColorTransferCharacteristic color_trc;
    enum AVColorSpace color_space;
    enum AVChromaLocation chroma_location;
    int video_delay;
    AVChannelLayout ch_layout;
    int sample_rate;
    int block_align;
    int frame_size;
    int initial_padding;
    int trailing_padding;
    int seek_preroll;
} AVCodecParameters;

typedef struct AVStream {
    const AVClass *av_class;
    int index;
    int id;
    AVCodecParameters *codecpar;
    void *priv_data;
    AVRational time_base;
    int64_t start_time;
    int64_t duration;
    int64_t nb_frames;
    int disposition;
    enum AVDiscard discard;
    AVRational sample_aspect_ratio;
    AVDictionary *metadata;
    AVRational avg_frame_rate;
    AVPacket attached_pic;
    int event_flags;
    AVRational r_frame_rate;
    int pts_wrap_bits;
} AVStream;

typedef struct AVStreamGroup {
    const AVClass *av_class;
    void *priv_data;
    unsigned int index;
    int64_t id;
    enum AVStreamGroupParamsType type;
    union {
        struct AVIAMFAudioElement *iamf_audio_element;
        struct AVIAMFMixPresentation *iamf_mix_presentation;
        struct AVStreamGroupTileGrid *tile_grid;
        struct AVStreamGroupLCEVC *lcevc;
    } params;
    AVDictionary *metadata;
    unsigned int nb_streams;
    AVStream **streams;
    int disposition;
} AVStreamGroup;

typedef struct AVIOContext {
    const AVClass *av_class;
    unsigned char *buffer;
    int buffer_size;
    unsigned char *buf_ptr;
    unsigned char *buf_end;
    void *opaque;
    int (*read_packet)(void *opaque, uint8_t *buf, int buf_size);
    int (*write_packet)(void *opaque, const uint8_t *buf, int buf_size);
    int64_t (*seek)(void *opaque, int64_t offset, int whence);
    int64_t pos;
    int eof_reached;
    int error;
    int write_flag;
    int max_packet_size;
    int min_packet_size;
    unsigned long checksum;
    unsigned char *checksum_ptr;
    unsigned long (*update_checksum)(unsigned long checksum, const uint8_t *buf, unsigned int size);
    int (*read_pause)(void *opaque, int pause);
    int64_t (*read_seek)(void *opaque, int stream_index, int64_t timestamp, int flags);
    int seekable;
    int direct;
    const char *protocol_whitelist;
    const char *protocol_blacklist;
    int (*write_data_type)(void *opaque, const uint8_t *buf, int buf_size, enum AVIODataMarkerType type, int64_t time);
    int ignore_boundary_point;
    unsigned char *buf_ptr_max;
    int64_t bytes_read;
    int64_t bytes_written;
} AVIOContext;

typedef struct AVChapter {
    int64_t id;
    AVRational time_base;
    int64_t start, end;
    AVDictionary *metadata;
} AVChapter;

typedef struct AVProgram {
    int            id;
    int            flags;
    enum AVDiscard discard;
    unsigned int   *stream_index;
    unsigned int   nb_stream_indexes;
    AVDictionary *metadata;
    int program_num;
    int pmt_pid;
    int pcr_pid;
    int pmt_version;
    int64_t start_time;
    int64_t end_time;
    int64_t pts_wrap_reference;
    int pts_wrap_behavior;
} AVProgram;

typedef struct AVIOInterruptCB {
    int (*callback)(void*);
    void *opaque;
} AVIOInterruptCB;

typedef int (*av_format_control_message)(struct AVFormatContext *s, int type, void *data, size_t data_size);

typedef struct AVFormatContext {
    const AVClass *av_class;
    const struct AVInputFormat *iformat;
    const struct AVOutputFormat *oformat;
    void *priv_data;
    AVIOContext *pb;
    int ctx_flags;
    unsigned int nb_streams;
    AVStream **streams;
    unsigned int nb_stream_groups;
    AVStreamGroup **stream_groups;
    unsigned int nb_chapters;
    AVChapter **chapters;
    char *url;
    int64_t start_time;
    int64_t duration;
    int64_t bit_rate;
    unsigned int packet_size;
    int max_delay;
    int flags;
    int64_t probesize;
    int64_t max_analyze_duration;
    const uint8_t *key;
    int keylen;
    unsigned int nb_programs;
    AVProgram **programs;
    enum AVCodecID video_codec_id;
    enum AVCodecID audio_codec_id;
    enum AVCodecID subtitle_codec_id;
    enum AVCodecID data_codec_id;
    AVDictionary *metadata;
    int64_t start_time_realtime;
    int fps_probe_size;
    int error_recognition;
    AVIOInterruptCB interrupt_callback;
    int debug;
    int max_streams;
    unsigned int max_index_size;
    unsigned int max_picture_buffer;
    int64_t max_interleave_delta;
    int max_ts_probe;
    int max_chunk_duration;
    int max_chunk_size;
    int max_probe_packets;
    int strict_std_compliance;
    int event_flags;
    int avoid_negative_ts;
    int audio_preload;
    int use_wallclock_as_timestamps;
    int skip_estimate_duration_from_pts;
    int avio_flags;
    enum AVDurationEstimationMethod duration_estimation_method;
    int64_t skip_initial_bytes;
    unsigned int correct_ts_overflow;
    int seek2any;
    int flush_packets;
    int probe_score;
    int format_probesize;
    char *codec_whitelist;
    char *format_whitelist;
    char *protocol_whitelist;
    char *protocol_blacklist;
    int io_repositioned;
    const struct AVCodec *video_codec;
    const struct AVCodec *audio_codec;
    const struct AVCodec *subtitle_codec;
    const struct AVCodec *data_codec;
    int metadata_header_padding;
    void *opaque;
    av_format_control_message control_message_cb;
    int64_t output_ts_offset;
    uint8_t *dump_separator;
    int (*io_open)(struct AVFormatContext *s, AVIOContext **pb, const char *url, int flags, AVDictionary **options);
    int (*io_close2)(struct AVFormatContext *s, AVIOContext *pb);
    int64_t duration_probesize;
} AVFormatContext;

// Functions
int av_get_pix_fmt_loss(enum AVPixelFormat dst_pix_fmt, enum AVPixelFormat src_pix_fmt, int has_alpha);
AVFrame *av_frame_alloc(void);
int av_frame_get_buffer(AVFrame *frame, int align);
int av_image_get_buffer_size(enum AVPixelFormat pix_fmt, int width, int height, int align);
const AVCodec *avcodec_find_encoder_by_name(const char *name);
AVCodecContext *avcodec_alloc_context3(const AVCodec *codec);
int av_dict_set(AVDictionary **pm, const char *key, const char *value, int flags);
int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);
int avcodec_send_frame(AVCodecContext *avctx, const AVFrame *frame);
AVPacket *av_packet_alloc(void);
int avcodec_receive_packet(AVCodecContext *avctx, AVPacket *avpkt);
const AVCodec *avcodec_find_decoder(enum AVCodecID id);
int avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt);
int avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame);
int av_strerror(int errnum, char *errbuf, size_t errbuf_size);
void avcodec_free_context(AVCodecContext **avctx);
void av_frame_free(AVFrame **frame);
void av_dict_free(AVDictionary **m);
void av_packet_free(AVPacket **pkt);
enum AVPixelFormat av_get_pix_fmt(const char *name);
int av_image_fill_arrays(uint8_t *dst_data[4], int dst_linesize[4], const uint8_t *src, enum AVPixelFormat pix_fmt, int width, int height, int align);
void av_log_set_level(int level);
int av_image_alloc(uint8_t * pointers[8], int linesizes[8], int w, int h, enum AVPixelFormat pix_fmt, int align);
const AVPixFmtDescriptor * av_pix_fmt_desc_get(enum AVPixelFormat pix_fmt);
const AVCodecDescriptor *avcodec_descriptor_get_by_name(const char *name);
const AVCodecDescriptor *avcodec_descriptor_get(enum AVCodecID id);
int av_codec_is_encoder(const AVCodec *codec);
int av_codec_is_decoder(const AVCodec *codec);
int avcodec_is_open(AVCodecContext *s);
AVDictionaryEntry *av_dict_get(const AVDictionary *m, const char *key, const AVDictionaryEntry *prev, int flags);
const char *av_version_info(void);
void av_free(void *ptr);
void av_freep(void *ptr);
const AVCodec *avcodec_find_encoder(enum AVCodecID id);
int avcodec_parameters_to_context(AVCodecContext *codec, const struct AVCodecParameters *par);
void av_packet_unref(AVPacket *pkt);
const AVCodec *avcodec_find_decoder_by_name(const char *name);
int av_new_packet(AVPacket *pkt, int size);
int avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt);
enum AVSampleFormat av_get_sample_fmt(const char *name);
int av_channel_layout_from_string(AVChannelLayout *channel_layout, const char *str);
int avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame);
const char *av_get_sample_fmt_name(enum AVSampleFormat sample_fmt);
int av_channel_layout_describe(const AVChannelLayout *channel_layout, char *buf, size_t buf_size);
int av_samples_get_buffer_size(int *linesize, int nb_channels, int nb_samples, enum AVSampleFormat sample_fmt, int align);
void *av_malloc(size_t size);
int avcodec_fill_audio_frame(AVFrame *frame, int nb_channels, enum AVSampleFormat sample_fmt, const uint8_t *buf, int buf_size, int align);
int av_sample_fmt_is_planar(enum AVSampleFormat sample_fmt);
int av_get_bytes_per_sample(enum AVSampleFormat sample_fmt);
enum AVSampleFormat av_get_planar_sample_fmt(enum AVSampleFormat sample_fmt);
enum AVChannel av_channel_layout_channel_from_index(const AVChannelLayout *channel_layout, unsigned int idx);
int av_channel_name(char *buf, size_t buf_size, enum AVChannel channel);
int av_channel_description(char *buf, size_t buf_size, enum AVChannel channel);
const char *av_get_media_type_string(enum AVMediaType media_type);
const AVCodec *av_codec_iterate(void **opaque);
void *av_realloc(void *ptr, size_t size);
const AVCodec *avcodec_find_encoder(enum AVCodecID id);
void av_log(void* avcl, int level, const char *fmt, ...);
void av_log_set_callback(void (*callback)(void*, int, const char*, va_list));
int av_dict_count(const AVDictionary *m);// Enums
enum AVMediaType {
    AVMEDIA_TYPE_UNKNOWN = -1,
    AVMEDIA_TYPE_VIDEO,
    AVMEDIA_TYPE_AUDIO,
    AVMEDIA_TYPE_DATA,
    AVMEDIA_TYPE_SUBTITLE,
    AVMEDIA_TYPE_ATTACHMENT,
    AVMEDIA_TYPE_NB
};

enum AVCodecID {
    AV_CODEC_ID_NONE,
    AV_CODEC_ID_MPEG1VIDEO,
    AV_CODEC_ID_MPEG2VIDEO,
    AV_CODEC_ID_H261,
    AV_CODEC_ID_H263,
    AV_CODEC_ID_RV10,
    AV_CODEC_ID_RV20,
    AV_CODEC_ID_MJPEG,
    AV_CODEC_ID_MJPEGB,
    AV_CODEC_ID_LJPEG,
    AV_CODEC_ID_SP5X,
    AV_CODEC_ID_JPEGLS,
    AV_CODEC_ID_MPEG4,
    AV_CODEC_ID_RAWVIDEO,
    AV_CODEC_ID_MSMPEG4V1,
    AV_CODEC_ID_MSMPEG4V2,
    AV_CODEC_ID_MSMPEG4V3,
    AV_CODEC_ID_WMV1,
    AV_CODEC_ID_WMV2,
    AV_CODEC_ID_H263P,
    AV_CODEC_ID_H263I,
    AV_CODEC_ID_FLV1,
    AV_CODEC_ID_SVQ1,
    AV_CODEC_ID_SVQ3,
    AV_CODEC_ID_DVVIDEO,
    AV_CODEC_ID_HUFFYUV,
    AV_CODEC_ID_CYUV,
    AV_CODEC_ID_H264,
    AV_CODEC_ID_INDEO3,
    AV_CODEC_ID_VP3,
    AV_CODEC_ID_THEORA,
    AV_CODEC_ID_ASV1,
    AV_CODEC_ID_ASV2,
    AV_CODEC_ID_FFV1,
    AV_CODEC_ID_4XM,
    AV_CODEC_ID_VCR1,
    AV_CODEC_ID_CLJR,
    AV_CODEC_ID_MDEC,
    AV_CODEC_ID_ROQ,
    AV_CODEC_ID_INTERPLAY_VIDEO,
    AV_CODEC_ID_XAN_WC3,
    AV_CODEC_ID_XAN_WC4,
    AV_CODEC_ID_RPZA,
    AV_CODEC_ID_CINEPAK,
    AV_CODEC_ID_WS_VQA,
    AV_CODEC_ID_MSRLE,
    AV_CODEC_ID_MSVIDEO1,
    AV_CODEC_ID_IDCIN,
    AV_CODEC_ID_8BPS,
    AV_CODEC_ID_SMC,
    AV_CODEC_ID_FLIC,
    AV_CODEC_ID_TRUEMOTION1,
    AV_CODEC_ID_VMDVIDEO,
    AV_CODEC_ID_MSZH,
    AV_CODEC_ID_ZLIB,
    AV_CODEC_ID_QTRLE,
    AV_CODEC_ID_TSCC,
    AV_CODEC_ID_ULTI,
    AV_CODEC_ID_QDRAW,
    AV_CODEC_ID_VIXL,
    AV_CODEC_ID_QPEG,
    AV_CODEC_ID_PNG,
    AV_CODEC_ID_PPM,
    AV_CODEC_ID_PBM,
    AV_CODEC_ID_PGM,
    AV_CODEC_ID_PGMYUV,
    AV_CODEC_ID_PAM,
    AV_CODEC_ID_FFVHUFF,
    AV_CODEC_ID_RV30,
    AV_CODEC_ID_RV40,
    AV_CODEC_ID_VC1,
    AV_CODEC_ID_WMV3,
    AV_CODEC_ID_LOCO,
    AV_CODEC_ID_WNV1,
    AV_CODEC_ID_AASC,
    AV_CODEC_ID_INDEO2,
    AV_CODEC_ID_FRAPS,
    AV_CODEC_ID_TRUEMOTION2,
    AV_CODEC_ID_BMP,
    AV_CODEC_ID_CSCD,
    AV_CODEC_ID_MMVIDEO,
    AV_CODEC_ID_ZMBV,
    AV_CODEC_ID_AVS,
    AV_CODEC_ID_SMACKVIDEO,
    AV_CODEC_ID_NUV,
    AV_CODEC_ID_KMVC,
    AV_CODEC_ID_FLASHSV,
    AV_CODEC_ID_CAVS,
    AV_CODEC_ID_JPEG2000,
    AV_CODEC_ID_VMNC,
    AV_CODEC_ID_VP5,
    AV_CODEC_ID_VP6,
    AV_CODEC_ID_VP6F,
    AV_CODEC_ID_TARGA,
    AV_CODEC_ID_DSICINVIDEO,
    AV_CODEC_ID_TIERTEXSEQVIDEO,
    AV_CODEC_ID_TIFF,
    AV_CODEC_ID_GIF,
    AV_CODEC_ID_DXA,
    AV_CODEC_ID_DNXHD,
    AV_CODEC_ID_THP,
    AV_CODEC_ID_SGI,
    AV_CODEC_ID_C93,
    AV_CODEC_ID_BETHSOFTVID,
    AV_CODEC_ID_PTX,
    AV_CODEC_ID_TXD,
    AV_CODEC_ID_VP6A,
    AV_CODEC_ID_AMV,
    AV_CODEC_ID_VB,
    AV_CODEC_ID_PCX,
    AV_CODEC_ID_SUNRAST,
    AV_CODEC_ID_INDEO4,
    AV_CODEC_ID_INDEO5,
    AV_CODEC_ID_MIMIC,
    AV_CODEC_ID_RL2,
    AV_CODEC_ID_ESCAPE124,
    AV_CODEC_ID_DIRAC,
    AV_CODEC_ID_BFI,
    AV_CODEC_ID_CMV,
    AV_CODEC_ID_MOTIONPIXELS,
    AV_CODEC_ID_TGV,
    AV_CODEC_ID_TGQ,
    AV_CODEC_ID_TQI,
    AV_CODEC_ID_AURA,
    AV_CODEC_ID_AURA2,
    AV_CODEC_ID_V210X,
    AV_CODEC_ID_TMV,
    AV_CODEC_ID_V210,
    AV_CODEC_ID_DPX,
    AV_CODEC_ID_MAD,
    AV_CODEC_ID_FRWU,
    AV_CODEC_ID_FLASHSV2,
    AV_CODEC_ID_CDGRAPHICS,
    AV_CODEC_ID_R210,
    AV_CODEC_ID_ANM,
    AV_CODEC_ID_BINKVIDEO,
    AV_CODEC_ID_IFF_ILBM,
#define AV_CODEC_ID_IFF_BYTERUN1 AV_CODEC_ID_IFF_ILBM
    AV_CODEC_ID_KGV1,
    AV_CODEC_ID_YOP,
    AV_CODEC_ID_VP8,
    AV_CODEC_ID_PICTOR,
    AV_CODEC_ID_ANSI,
    AV_CODEC_ID_A64_MULTI,
    AV_CODEC_ID_A64_MULTI5,
    AV_CODEC_ID_R10K,
    AV_CODEC_ID_MXPEG,
    AV_CODEC_ID_LAGARITH,
    AV_CODEC_ID_PRORES,
    AV_CODEC_ID_JV,
    AV_CODEC_ID_DFA,
    AV_CODEC_ID_WMV3IMAGE,
    AV_CODEC_ID_VC1IMAGE,
    AV_CODEC_ID_UTVIDEO,
    AV_CODEC_ID_BMV_VIDEO,
    AV_CODEC_ID_VBLE,
    AV_CODEC_ID_DXTORY,
    AV_CODEC_ID_V410,
    AV_CODEC_ID_XWD,
    AV_CODEC_ID_CDXL,
    AV_CODEC_ID_XBM,
    AV_CODEC_ID_ZEROCODEC,
    AV_CODEC_ID_MSS1,
    AV_CODEC_ID_MSA1,
    AV_CODEC_ID_TSCC2,
    AV_CODEC_ID_MTS2,
    AV_CODEC_ID_CLLC,
    AV_CODEC_ID_MSS2,
    AV_CODEC_ID_VP9,
    AV_CODEC_ID_AIC,
    AV_CODEC_ID_ESCAPE130,
    AV_CODEC_ID_G2M,
    AV_CODEC_ID_WEBP,
    AV_CODEC_ID_HNM4_VIDEO,
    AV_CODEC_ID_HEVC,
#define AV_CODEC_ID_H265 AV_CODEC_ID_HEVC
    AV_CODEC_ID_FIC,
    AV_CODEC_ID_ALIAS_PIX,
    AV_CODEC_ID_BRENDER_PIX,
    AV_CODEC_ID_PAF_VIDEO,
    AV_CODEC_ID_EXR,
    AV_CODEC_ID_VP7,
    AV_CODEC_ID_SANM,
    AV_CODEC_ID_SGIRLE,
    AV_CODEC_ID_MVC1,
    AV_CODEC_ID_MVC2,
    AV_CODEC_ID_HQX,
    AV_CODEC_ID_TDSC,
    AV_CODEC_ID_HQ_HQA,
    AV_CODEC_ID_HAP,
    AV_CODEC_ID_DDS,
    AV_CODEC_ID_DXV,
    AV_CODEC_ID_SCREENPRESSO,
    AV_CODEC_ID_RSCC,
    AV_CODEC_ID_AVS2,
    AV_CODEC_ID_PGX,
    AV_CODEC_ID_AVS3,
    AV_CODEC_ID_MSP2,
    AV_CODEC_ID_VVC,
#define AV_CODEC_ID_H266 AV_CODEC_ID_VVC
    AV_CODEC_ID_Y41P,
    AV_CODEC_ID_AVRP,
    AV_CODEC_ID_012V,
    AV_CODEC_ID_AVUI,
    AV_CODEC_ID_TARGA_Y216,
    AV_CODEC_ID_V308,
    AV_CODEC_ID_V408,
    AV_CODEC_ID_YUV4,
    AV_CODEC_ID_AVRN,
    AV_CODEC_ID_CPIA,
    AV_CODEC_ID_XFACE,
    AV_CODEC_ID_SNOW,
    AV_CODEC_ID_SMVJPEG,
    AV_CODEC_ID_APNG,
    AV_CODEC_ID_DAALA,
    AV_CODEC_ID_CFHD,
    AV_CODEC_ID_TRUEMOTION2RT,
    AV_CODEC_ID_M101,
    AV_CODEC_ID_MAGICYUV,
    AV_CODEC_ID_SHEERVIDEO,
    AV_CODEC_ID_YLC,
    AV_CODEC_ID_PSD,
    AV_CODEC_ID_PIXLET,
    AV_CODEC_ID_SPEEDHQ,
    AV_CODEC_ID_FMVC,
    AV_CODEC_ID_SCPR,
    AV_CODEC_ID_CLEARVIDEO,
    AV_CODEC_ID_XPM,
    AV_CODEC_ID_AV1,
    AV_CODEC_ID_BITPACKED,
    AV_CODEC_ID_MSCC,
    AV_CODEC_ID_SRGC,
    AV_CODEC_ID_SVG,
    AV_CODEC_ID_GDV,
    AV_CODEC_ID_FITS,
    AV_CODEC_ID_IMM4,
    AV_CODEC_ID_PROSUMER,
    AV_CODEC_ID_MWSC,
    AV_CODEC_ID_WCMV,
    AV_CODEC_ID_RASC,
    AV_CODEC_ID_HYMT,
    AV_CODEC_ID_ARBC,
    AV_CODEC_ID_AGM,
    AV_CODEC_ID_LSCR,
    AV_CODEC_ID_VP4,
    AV_CODEC_ID_IMM5,
    AV_CODEC_ID_MVDV,
    AV_CODEC_ID_MVHA,
    AV_CODEC_ID_CDTOONS,
    AV_CODEC_ID_MV30,
    AV_CODEC_ID_NOTCHLC,
    AV_CODEC_ID_PFM,
    AV_CODEC_ID_MOBICLIP,
    AV_CODEC_ID_PHOTOCD,
    AV_CODEC_ID_IPU,
    AV_CODEC_ID_ARGO,
    AV_CODEC_ID_CRI,
    AV_CODEC_ID_SIMBIOSIS_IMX,
    AV_CODEC_ID_SGA_VIDEO,
    AV_CODEC_ID_GEM,
    AV_CODEC_ID_VBN,
    AV_CODEC_ID_JPEGXL,
    AV_CODEC_ID_QOI,
    AV_CODEC_ID_PHM,
    AV_CODEC_ID_RADIANCE_HDR,
    AV_CODEC_ID_WBMP,
    AV_CODEC_ID_MEDIA100,
    AV_CODEC_ID_VQC,
    AV_CODEC_ID_PDV,
    AV_CODEC_ID_EVC,
    AV_CODEC_ID_RTV1,
    AV_CODEC_ID_VMIX,
    AV_CODEC_ID_LEAD,
    AV_CODEC_ID_FIRST_AUDIO = 0x10000,
    AV_CODEC_ID_PCM_S16LE = 0x10000,
    AV_CODEC_ID_PCM_S16BE,
    AV_CODEC_ID_PCM_U16LE,
    AV_CODEC_ID_PCM_U16BE,
    AV_CODEC_ID_PCM_S8,
    AV_CODEC_ID_PCM_U8,
    AV_CODEC_ID_PCM_MULAW,
    AV_CODEC_ID_PCM_ALAW,
    AV_CODEC_ID_PCM_S32LE,
    AV_CODEC_ID_PCM_S32BE,
    AV_CODEC_ID_PCM_U32LE,
    AV_CODEC_ID_PCM_U32BE,
    AV_CODEC_ID_PCM_S24LE,
    AV_CODEC_ID_PCM_S24BE,
    AV_CODEC_ID_PCM_U24LE,
    AV_CODEC_ID_PCM_U24BE,
    AV_CODEC_ID_PCM_S24DAUD,
    AV_CODEC_ID_PCM_ZORK,
    AV_CODEC_ID_PCM_S16LE_PLANAR,
    AV_CODEC_ID_PCM_DVD,
    AV_CODEC_ID_PCM_F32BE,
    AV_CODEC_ID_PCM_F32LE,
    AV_CODEC_ID_PCM_F64BE,
    AV_CODEC_ID_PCM_F64LE,
    AV_CODEC_ID_PCM_BLURAY,
    AV_CODEC_ID_PCM_LXF,
    AV_CODEC_ID_S302M,
    AV_CODEC_ID_PCM_S8_PLANAR,
    AV_CODEC_ID_PCM_S24LE_PLANAR,
    AV_CODEC_ID_PCM_S32LE_PLANAR,
    AV_CODEC_ID_PCM_S16BE_PLANAR,
    AV_CODEC_ID_PCM_S64LE,
    AV_CODEC_ID_PCM_S64BE,
    AV_CODEC_ID_PCM_F16LE,
    AV_CODEC_ID_PCM_F24LE,
    AV_CODEC_ID_PCM_VIDC,
    AV_CODEC_ID_PCM_SGA,
    AV_CODEC_ID_ADPCM_IMA_QT = 0x11000,
    AV_CODEC_ID_ADPCM_IMA_WAV,
    AV_CODEC_ID_ADPCM_IMA_DK3,
    AV_CODEC_ID_ADPCM_IMA_DK4,
    AV_CODEC_ID_ADPCM_IMA_WS,
    AV_CODEC_ID_ADPCM_IMA_SMJPEG,
    AV_CODEC_ID_ADPCM_MS,
    AV_CODEC_ID_ADPCM_4XM,
    AV_CODEC_ID_ADPCM_XA,
    AV_CODEC_ID_ADPCM_ADX,
    AV_CODEC_ID_ADPCM_EA,
    AV_CODEC_ID_ADPCM_G726,
    AV_CODEC_ID_ADPCM_CT,
    AV_CODEC_ID_ADPCM_SWF,
    AV_CODEC_ID_ADPCM_YAMAHA,
    AV_CODEC_ID_ADPCM_SBPRO_4,
    AV_CODEC_ID_ADPCM_SBPRO_3,
    AV_CODEC_ID_ADPCM_SBPRO_2,
    AV_CODEC_ID_ADPCM_THP,
    AV_CODEC_ID_ADPCM_IMA_AMV,
    AV_CODEC_ID_ADPCM_EA_R1,
    AV_CODEC_ID_ADPCM_EA_R3,
    AV_CODEC_ID_ADPCM_EA_R2,
    AV_CODEC_ID_ADPCM_IMA_EA_SEAD,
    AV_CODEC_ID_ADPCM_IMA_EA_EACS,
    AV_CODEC_ID_ADPCM_EA_XAS,
    AV_CODEC_ID_ADPCM_EA_MAXIS_XA,
    AV_CODEC_ID_ADPCM_IMA_ISS,
    AV_CODEC_ID_ADPCM_G722,
    AV_CODEC_ID_ADPCM_IMA_APC,
    AV_CODEC_ID_ADPCM_VIMA,
    AV_CODEC_ID_ADPCM_AFC,
    AV_CODEC_ID_ADPCM_IMA_OKI,
    AV_CODEC_ID_ADPCM_DTK,
    AV_CODEC_ID_ADPCM_IMA_RAD,
    AV_CODEC_ID_ADPCM_G726LE,
    AV_CODEC_ID_ADPCM_THP_LE,
    AV_CODEC_ID_ADPCM_PSX,
    AV_CODEC_ID_ADPCM_AICA,
    AV_CODEC_ID_ADPCM_IMA_DAT4,
    AV_CODEC_ID_ADPCM_MTAF,
    AV_CODEC_ID_ADPCM_AGM,
    AV_CODEC_ID_ADPCM_ARGO,
    AV_CODEC_ID_ADPCM_IMA_SSI,
    AV_CODEC_ID_ADPCM_ZORK,
    AV_CODEC_ID_ADPCM_IMA_APM,
    AV_CODEC_ID_ADPCM_IMA_ALP,
    AV_CODEC_ID_ADPCM_IMA_MTF,
    AV_CODEC_ID_ADPCM_IMA_CUNNING,
    AV_CODEC_ID_ADPCM_IMA_MOFLEX,
    AV_CODEC_ID_ADPCM_IMA_ACORN,
    AV_CODEC_ID_ADPCM_XMD,
    AV_CODEC_ID_AMR_NB = 0x12000,
    AV_CODEC_ID_AMR_WB,
    AV_CODEC_ID_RA_144 = 0x13000,
    AV_CODEC_ID_RA_288,
    AV_CODEC_ID_ROQ_DPCM = 0x14000,
    AV_CODEC_ID_INTERPLAY_DPCM,
    AV_CODEC_ID_XAN_DPCM,
    AV_CODEC_ID_SOL_DPCM,
    AV_CODEC_ID_SDX2_DPCM,
    AV_CODEC_ID_GREMLIN_DPCM,
    AV_CODEC_ID_DERF_DPCM,
    AV_CODEC_ID_WADY_DPCM,
    AV_CODEC_ID_CBD2_DPCM,
    AV_CODEC_ID_MP2 = 0x15000,
    AV_CODEC_ID_MP3,
    AV_CODEC_ID_AAC,
    AV_CODEC_ID_AC3,
    AV_CODEC_ID_DTS,
    AV_CODEC_ID_VORBIS,
    AV_CODEC_ID_DVAUDIO,
    AV_CODEC_ID_WMAV1,
    AV_CODEC_ID_WMAV2,
    AV_CODEC_ID_MACE3,
    AV_CODEC_ID_MACE6,
    AV_CODEC_ID_VMDAUDIO,
    AV_CODEC_ID_FLAC,
    AV_CODEC_ID_MP3ADU,
    AV_CODEC_ID_MP3ON4,
    AV_CODEC_ID_SHORTEN,
    AV_CODEC_ID_ALAC,
    AV_CODEC_ID_WESTWOOD_SND1,
    AV_CODEC_ID_GSM,
    AV_CODEC_ID_QDM2,
    AV_CODEC_ID_COOK,
    AV_CODEC_ID_TRUESPEECH,
    AV_CODEC_ID_TTA,
    AV_CODEC_ID_SMACKAUDIO,
    AV_CODEC_ID_QCELP,
    AV_CODEC_ID_WAVPACK,
    AV_CODEC_ID_DSICINAUDIO,
    AV_CODEC_ID_IMC,
    AV_CODEC_ID_MUSEPACK7,
    AV_CODEC_ID_MLP,
    AV_CODEC_ID_GSM_MS,
    AV_CODEC_ID_ATRAC3,
    AV_CODEC_ID_APE,
    AV_CODEC_ID_NELLYMOSER,
    AV_CODEC_ID_MUSEPACK8,
    AV_CODEC_ID_SPEEX,
    AV_CODEC_ID_WMAVOICE,
    AV_CODEC_ID_WMAPRO,
    AV_CODEC_ID_WMALOSSLESS,
    AV_CODEC_ID_ATRAC3P,
    AV_CODEC_ID_EAC3,
    AV_CODEC_ID_SIPR,
    AV_CODEC_ID_MP1,
    AV_CODEC_ID_TWINVQ,
    AV_CODEC_ID_TRUEHD,
    AV_CODEC_ID_MP4ALS,
    AV_CODEC_ID_ATRAC1,
    AV_CODEC_ID_BINKAUDIO_RDFT,
    AV_CODEC_ID_BINKAUDIO_DCT,
    AV_CODEC_ID_AAC_LATM,
    AV_CODEC_ID_QDMC,
    AV_CODEC_ID_CELT,
    AV_CODEC_ID_G723_1,
    AV_CODEC_ID_G729,
    AV_CODEC_ID_8SVX_EXP,
    AV_CODEC_ID_8SVX_FIB,
    AV_CODEC_ID_BMV_AUDIO,
    AV_CODEC_ID_RALF,
    AV_CODEC_ID_IAC,
    AV_CODEC_ID_ILBC,
    AV_CODEC_ID_OPUS,
    AV_CODEC_ID_COMFORT_NOISE,
    AV_CODEC_ID_TAK,
    AV_CODEC_ID_METASOUND,
    AV_CODEC_ID_PAF_AUDIO,
    AV_CODEC_ID_ON2AVC,
    AV_CODEC_ID_DSS_SP,
    AV_CODEC_ID_CODEC2,
    AV_CODEC_ID_FFWAVESYNTH,
    AV_CODEC_ID_SONIC,
    AV_CODEC_ID_SONIC_LS,
    AV_CODEC_ID_EVRC,
    AV_CODEC_ID_SMV,
    AV_CODEC_ID_DSD_LSBF,
    AV_CODEC_ID_DSD_MSBF,
    AV_CODEC_ID_DSD_LSBF_PLANAR,
    AV_CODEC_ID_DSD_MSBF_PLANAR,
    AV_CODEC_ID_4GV,
    AV_CODEC_ID_INTERPLAY_ACM,
    AV_CODEC_ID_XMA1,
    AV_CODEC_ID_XMA2,
    AV_CODEC_ID_DST,
    AV_CODEC_ID_ATRAC3AL,
    AV_CODEC_ID_ATRAC3PAL,
    AV_CODEC_ID_DOLBY_E,
    AV_CODEC_ID_APTX,
    AV_CODEC_ID_APTX_HD,
    AV_CODEC_ID_SBC,
    AV_CODEC_ID_ATRAC9,
    AV_CODEC_ID_HCOM,
    AV_CODEC_ID_ACELP_KELVIN,
    AV_CODEC_ID_MPEGH_3D_AUDIO,
    AV_CODEC_ID_SIREN,
    AV_CODEC_ID_HCA,
    AV_CODEC_ID_FASTAUDIO,
    AV_CODEC_ID_MSNSIREN,
    AV_CODEC_ID_DFPWM,
    AV_CODEC_ID_BONK,
    AV_CODEC_ID_MISC4,
    AV_CODEC_ID_APAC,
    AV_CODEC_ID_FTR,
    AV_CODEC_ID_WAVARC,
    AV_CODEC_ID_RKA,
    AV_CODEC_ID_AC4,
    AV_CODEC_ID_OSQ,
    AV_CODEC_ID_QOA,
    AV_CODEC_ID_LC3,
    AV_CODEC_ID_FIRST_SUBTITLE = 0x17000,
    AV_CODEC_ID_DVD_SUBTITLE = 0x17000,
    AV_CODEC_ID_DVB_SUBTITLE,
    AV_CODEC_ID_TEXT,
    AV_CODEC_ID_XSUB,
    AV_CODEC_ID_SSA,
    AV_CODEC_ID_MOV_TEXT,
    AV_CODEC_ID_HDMV_PGS_SUBTITLE,
    AV_CODEC_ID_DVB_TELETEXT,
    AV_CODEC_ID_SRT,
    AV_CODEC_ID_MICRODVD,
    AV_CODEC_ID_EIA_608,
    AV_CODEC_ID_JACOSUB,
    AV_CODEC_ID_SAMI,
    AV_CODEC_ID_REALTEXT,
    AV_CODEC_ID_STL,
    AV_CODEC_ID_SUBVIEWER1,
    AV_CODEC_ID_SUBVIEWER,
    AV_CODEC_ID_SUBRIP,
    AV_CODEC_ID_WEBVTT,
    AV_CODEC_ID_MPL2,
    AV_CODEC_ID_VPLAYER,
    AV_CODEC_ID_PJS,
    AV_CODEC_ID_ASS,
    AV_CODEC_ID_HDMV_TEXT_SUBTITLE,
    AV_CODEC_ID_TTML,
    AV_CODEC_ID_ARIB_CAPTION,
    AV_CODEC_ID_FIRST_UNKNOWN = 0x18000,
    AV_CODEC_ID_TTF = 0x18000,
    AV_CODEC_ID_SCTE_35,
    AV_CODEC_ID_EPG,
    AV_CODEC_ID_BINTEXT,
    AV_CODEC_ID_XBIN,
    AV_CODEC_ID_IDF,
    AV_CODEC_ID_OTF,
    AV_CODEC_ID_SMPTE_KLV,
    AV_CODEC_ID_DVD_NAV,
    AV_CODEC_ID_TIMED_ID3,
    AV_CODEC_ID_BIN_DATA,
    AV_CODEC_ID_SMPTE_2038,
    AV_CODEC_ID_LCEVC,
    AV_CODEC_ID_PROBE = 0x19000,
    AV_CODEC_ID_MPEG2TS = 0x20000,
    AV_CODEC_ID_MPEG4SYSTEMS = 0x20001,
    AV_CODEC_ID_FFMETADATA = 0x21000,
    AV_CODEC_ID_WRAPPED_AVFRAME = 0x21001,
    AV_CODEC_ID_VNULL,
    AV_CODEC_ID_ANULL,
};

enum AVPacketSideDataType {
    AV_PKT_DATA_PALETTE,
    AV_PKT_DATA_NEW_EXTRADATA,
    AV_PKT_DATA_PARAM_CHANGE,
    AV_PKT_DATA_H263_MB_INFO,
    AV_PKT_DATA_REPLAYGAIN,
    AV_PKT_DATA_DISPLAYMATRIX,
    AV_PKT_DATA_STEREO3D,
    AV_PKT_DATA_AUDIO_SERVICE_TYPE,
    AV_PKT_DATA_QUALITY_STATS,
    AV_PKT_DATA_FALLBACK_TRACK,
    AV_PKT_DATA_CPB_PROPERTIES,
    AV_PKT_DATA_SKIP_SAMPLES,
    AV_PKT_DATA_JP_DUALMONO,
    AV_PKT_DATA_STRINGS_METADATA,
    AV_PKT_DATA_SUBTITLE_POSITION,
    AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL,
    AV_PKT_DATA_WEBVTT_IDENTIFIER,
    AV_PKT_DATA_WEBVTT_SETTINGS,
    AV_PKT_DATA_METADATA_UPDATE,
    AV_PKT_DATA_MPEGTS_STREAM_ID,
    AV_PKT_DATA_MASTERING_DISPLAY_METADATA,
    AV_PKT_DATA_SPHERICAL,
    AV_PKT_DATA_CONTENT_LIGHT_LEVEL,
    AV_PKT_DATA_A53_CC,
    AV_PKT_DATA_ENCRYPTION_INIT_INFO,
    AV_PKT_DATA_ENCRYPTION_INFO,
    AV_PKT_DATA_AFD,
    AV_PKT_DATA_PRFT,
    AV_PKT_DATA_ICC_PROFILE,
    AV_PKT_DATA_DOVI_CONF,
    AV_PKT_DATA_S12M_TIMECODE,
    AV_PKT_DATA_DYNAMIC_HDR10_PLUS,
    AV_PKT_DATA_IAMF_MIX_GAIN_PARAM,
    AV_PKT_DATA_IAMF_DEMIXING_INFO_PARAM,
    AV_PKT_DATA_IAMF_RECON_GAIN_INFO_PARAM,
    AV_PKT_DATA_AMBIENT_VIEWING_ENVIRONMENT,
    AV_PKT_DATA_FRAME_CROPPING,
    AV_PKT_DATA_LCEVC,
    AV_PKT_DATA_NB
};

enum AVFieldOrder {
    AV_FIELD_UNKNOWN,
    AV_FIELD_PROGRESSIVE,
    AV_FIELD_TT,
    AV_FIELD_BB,
    AV_FIELD_TB,
    AV_FIELD_BT,
};

enum AVColorRange {
    AVCOL_RANGE_UNSPECIFIED = 0,
    AVCOL_RANGE_MPEG = 1,
    AVCOL_RANGE_JPEG = 2,
    AVCOL_RANGE_NB
};

enum AVColorPrimaries {
    AVCOL_PRI_RESERVED0 = 0,
    AVCOL_PRI_BT709 = 1,
    AVCOL_PRI_UNSPECIFIED = 2,
    AVCOL_PRI_RESERVED = 3,
    AVCOL_PRI_BT470M = 4,
    AVCOL_PRI_BT470BG = 5,
    AVCOL_PRI_SMPTE170M = 6,
    AVCOL_PRI_SMPTE240M = 7,
    AVCOL_PRI_FILM = 8,
    AVCOL_PRI_BT2020 = 9,
    AVCOL_PRI_SMPTE428 = 10,
    AVCOL_PRI_SMPTEST428_1 = AVCOL_PRI_SMPTE428,
    AVCOL_PRI_SMPTE431 = 11,
    AVCOL_PRI_SMPTE432 = 12,
    AVCOL_PRI_EBU3213 = 22,
    AVCOL_PRI_JEDEC_P22 = AVCOL_PRI_EBU3213,
    AVCOL_PRI_NB
};

enum AVColorTransferCharacteristic {
    AVCOL_TRC_RESERVED0 = 0,
    AVCOL_TRC_BT709 = 1,
    AVCOL_TRC_UNSPECIFIED = 2,
    AVCOL_TRC_RESERVED = 3,
    AVCOL_TRC_GAMMA22 = 4,
    AVCOL_TRC_GAMMA28 = 5,
    AVCOL_TRC_SMPTE170M = 6,
    AVCOL_TRC_SMPTE240M = 7,
    AVCOL_TRC_LINEAR = 8,
    AVCOL_TRC_LOG = 9,
    AVCOL_TRC_LOG_SQRT = 10,
    AVCOL_TRC_IEC61966_2_4 = 11,
    AVCOL_TRC_BT1361_ECG = 12,
    AVCOL_TRC_IEC61966_2_1 = 13,
    AVCOL_TRC_BT2020_10 = 14,
    AVCOL_TRC_BT2020_12 = 15,
    AVCOL_TRC_SMPTE2084 = 16,
    AVCOL_TRC_SMPTEST2084 = AVCOL_TRC_SMPTE2084,
    AVCOL_TRC_SMPTE428 = 17,
    AVCOL_TRC_SMPTEST428_1 = AVCOL_TRC_SMPTE428,
    AVCOL_TRC_ARIB_STD_B67 = 18,
    AVCOL_TRC_NB
};

enum AVColorSpace {
    AVCOL_SPC_RGB = 0,
    AVCOL_SPC_BT709 = 1,
    AVCOL_SPC_UNSPECIFIED = 2,
    AVCOL_SPC_RESERVED = 3,
    AVCOL_SPC_FCC = 4,
    AVCOL_SPC_BT470BG = 5,
    AVCOL_SPC_SMPTE170M = 6,
    AVCOL_SPC_SMPTE240M = 7,
    AVCOL_SPC_YCGCO = 8,
    AVCOL_SPC_YCOCG = AVCOL_SPC_YCGCO,
    AVCOL_SPC_BT2020_NCL = 9,
    AVCOL_SPC_BT2020_CL = 10,
    AVCOL_SPC_SMPTE2085 = 11,
    AVCOL_SPC_CHROMA_DERIVED_NCL = 12,
    AVCOL_SPC_CHROMA_DERIVED_CL = 13,
    AVCOL_SPC_ICTCP = 14,
    AVCOL_SPC_IPT_C2 = 15,
    AVCOL_SPC_YCGCO_RE = 16,
    AVCOL_SPC_YCGCO_RO = 17,
    AVCOL_SPC_NB
};

enum AVChromaLocation {
    AVCHROMA_LOC_UNSPECIFIED = 0,
    AVCHROMA_LOC_LEFT = 1,
    AVCHROMA_LOC_CENTER = 2,
    AVCHROMA_LOC_TOPLEFT = 3,
    AVCHROMA_LOC_TOP = 4,
    AVCHROMA_LOC_BOTTOMLEFT = 5,
    AVCHROMA_LOC_BOTTOM = 6,
    AVCHROMA_LOC_NB
};

enum AVChannelOrder {
    AV_CHANNEL_ORDER_UNSPEC,
    AV_CHANNEL_ORDER_NATIVE,
    AV_CHANNEL_ORDER_CUSTOM,
    AV_CHANNEL_ORDER_AMBISONIC,
    FF_CHANNEL_ORDER_NB
};

enum AVChannel {
    AV_CHAN_NONE = -1,
    AV_CHAN_FRONT_LEFT,
    AV_CHAN_FRONT_RIGHT,
    AV_CHAN_FRONT_CENTER,
    AV_CHAN_LOW_FREQUENCY,
    AV_CHAN_BACK_LEFT,
    AV_CHAN_BACK_RIGHT,
    AV_CHAN_FRONT_LEFT_OF_CENTER,
    AV_CHAN_FRONT_RIGHT_OF_CENTER,
    AV_CHAN_BACK_CENTER,
    AV_CHAN_SIDE_LEFT,
    AV_CHAN_SIDE_RIGHT,
    AV_CHAN_TOP_CENTER,
    AV_CHAN_TOP_FRONT_LEFT,
    AV_CHAN_TOP_FRONT_CENTER,
    AV_CHAN_TOP_FRONT_RIGHT,
    AV_CHAN_TOP_BACK_LEFT,
    AV_CHAN_TOP_BACK_CENTER,
    AV_CHAN_TOP_BACK_RIGHT,
    AV_CHAN_STEREO_LEFT = 29,
    AV_CHAN_STEREO_RIGHT,
    AV_CHAN_WIDE_LEFT,
    AV_CHAN_WIDE_RIGHT,
    AV_CHAN_SURROUND_DIRECT_LEFT,
    AV_CHAN_SURROUND_DIRECT_RIGHT,
    AV_CHAN_LOW_FREQUENCY_2,
    AV_CHAN_TOP_SIDE_LEFT,
    AV_CHAN_TOP_SIDE_RIGHT,
    AV_CHAN_BOTTOM_FRONT_CENTER,
    AV_CHAN_BOTTOM_FRONT_LEFT,
    AV_CHAN_BOTTOM_FRONT_RIGHT,
    AV_CHAN_SIDE_SURROUND_LEFT,
    AV_CHAN_SIDE_SURROUND_RIGHT,
    AV_CHAN_TOP_SURROUND_LEFT,
    AV_CHAN_TOP_SURROUND_RIGHT,
    AV_CHAN_UNUSED = 0x200,
    AV_CHAN_UNKNOWN = 0x300,
    AV_CHAN_AMBISONIC_BASE = 0x400,
    AV_CHAN_AMBISONIC_END = 0x7ff,
};

enum AVDiscard {
    AVDISCARD_NONE = -16,
    AVDISCARD_DEFAULT = 0,
    AVDISCARD_NONREF = 8,
    AVDISCARD_BIDIR = 16,
    AVDISCARD_NONINTRA = 24,
    AVDISCARD_NONKEY = 32,
    AVDISCARD_ALL = 48,
};

enum AVStreamGroupParamsType {
    AV_STREAM_GROUP_PARAMS_NONE,
    AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT,
    AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION,
    AV_STREAM_GROUP_PARAMS_TILE_GRID,
    AV_STREAM_GROUP_PARAMS_LCEVC,
};

enum AVDurationEstimationMethod {
    AVFMT_DURATION_FROM_PTS,
    AVFMT_DURATION_FROM_STREAM,
    AVFMT_DURATION_FROM_BITRATE
};

typedef enum AVClassCategory {
    AV_CLASS_CATEGORY_NA = 0,
    AV_CLASS_CATEGORY_INPUT,
    AV_CLASS_CATEGORY_OUTPUT,
    AV_CLASS_CATEGORY_MUXER,
    AV_CLASS_CATEGORY_DEMUXER,
    AV_CLASS_CATEGORY_ENCODER,
    AV_CLASS_CATEGORY_DECODER,
    AV_CLASS_CATEGORY_FILTER,
    AV_CLASS_CATEGORY_BITSTREAM_FILTER,
    AV_CLASS_CATEGORY_SWSCALER,
    AV_CLASS_CATEGORY_SWRESAMPLER,
    AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT = 40,
    AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT,
    AV_CLASS_CATEGORY_DEVICE_AUDIO_OUTPUT,
    AV_CLASS_CATEGORY_DEVICE_AUDIO_INPUT,
    AV_CLASS_CATEGORY_DEVICE_OUTPUT,
    AV_CLASS_CATEGORY_DEVICE_INPUT,
    AV_CLASS_CATEGORY_NB
}AVClassCategory;

// Structs
typedef struct AVDictionary AVDictionary;

typedef struct AVBuffer AVBuffer;

typedef struct AVChannelCustom {
    enum AVChannel id;
    char name[16];
    void *opaque;
} AVChannelCustom;

typedef struct AVRational {
    int num;
    int den;
} AVRational;

typedef struct AVChannelLayout {
    enum AVChannelOrder order;
    int nb_channels;
    union {
        uint64_t mask;
        AVChannelCustom *map;
    } u;
    void *opaque;
} AVChannelLayout;

typedef struct AVPacketSideData {
    uint8_t *data;
    size_t size;
    enum AVPacketSideDataType type;
} AVPacketSideData;

typedef struct AVCodecParameters {
    enum AVMediaType codec_type;
    enum AVCodecID codec_id;
    uint32_t codec_tag;
    uint8_t *extradata;
    int extradata_size;
    AVPacketSideData *coded_side_data;
    int nb_coded_side_data;
    int format;
    int64_t bit_rate;
    int bits_per_coded_sample;
    int bits_per_raw_sample;
    int profile;
    int level;
    int width;
    int height;
    AVRational sample_aspect_ratio;
    AVRational framerate;
    enum AVFieldOrder field_order;
    enum AVColorRange color_range;
    enum AVColorPrimaries color_primaries;
    enum AVColorTransferCharacteristic color_trc;
    enum AVColorSpace color_space;
    enum AVChromaLocation chroma_location;
    int video_delay;
    AVChannelLayout ch_layout;
    int sample_rate;
    int block_align;
    int frame_size;
    int initial_padding;
    int trailing_padding;
    int seek_preroll;
} AVCodecParameters;

typedef struct AVClass {
    const char* class_name;
    const char* (*item_name)(void* ctx);
    const struct AVOption *option;
    int version;
    int log_level_offset_offset;
    int parent_log_context_offset;
    AVClassCategory category;
    AVClassCategory (*get_category)(void* ctx);
    int (*query_ranges)(struct AVOptionRanges **, void *obj, const char *key, int flags);
    void* (*child_next)(void *obj, void *prev);
    const struct AVClass* (*child_class_iterate)(void **iter);
} AVClass;

typedef struct AVBufferRef {
    AVBuffer *buffer;
    uint8_t *data;
    size_t size;
} AVBufferRef;

typedef struct AVPacket {
    AVBufferRef *buf;
    int64_t pts;
    int64_t dts;
    uint8_t *data;
    int size;
    int stream_index;
    int flags;
    AVPacketSideData *side_data;
    int side_data_elems;
    int64_t duration;
    int64_t pos;
    void *opaque;
    AVBufferRef *opaque_ref;
    AVRational time_base;
} AVPacket;

typedef struct AVStream {
    const AVClass *av_class;
    int index;
    int id;
    AVCodecParameters *codecpar;
    void *priv_data;
    AVRational time_base;
    int64_t start_time;
    int64_t duration;
    int64_t nb_frames;
    int disposition;
    enum AVDiscard discard;
    AVRational sample_aspect_ratio;
    AVDictionary *metadata;
    AVRational avg_frame_rate;
    AVPacket attached_pic;
    int event_flags;
    AVRational r_frame_rate;
    int pts_wrap_bits;
} AVStream;

typedef struct AVIOContext {
    const AVClass *av_class;
    unsigned char *buffer;
    int buffer_size;
    unsigned char *buf_ptr;
    unsigned char *buf_end;
    void *opaque;
    int (*read_packet)(void *opaque, uint8_t *buf, int buf_size);
    int (*write_packet)(void *opaque, const uint8_t *buf, int buf_size);
    int64_t (*seek)(void *opaque, int64_t offset, int whence);
    int64_t pos;
    int eof_reached;
    int error;
    int write_flag;
    int max_packet_size;
    int min_packet_size;
    unsigned long checksum;
    unsigned char *checksum_ptr;
    unsigned long (*update_checksum)(unsigned long checksum, const uint8_t *buf, unsigned int size);
    int (*read_pause)(void *opaque, int pause);
    int64_t (*read_seek)(void *opaque, int stream_index, int64_t timestamp, int flags);
    int seekable;
    int direct;
    const char *protocol_whitelist;
    const char *protocol_blacklist;
    int (*write_data_type)(void *opaque, const uint8_t *buf, int buf_size, enum AVIODataMarkerType type, int64_t time);
    int ignore_boundary_point;
    unsigned char *buf_ptr_max;
    int64_t bytes_read;
    int64_t bytes_written;
} AVIOContext;

typedef struct AVStreamGroup {
    const AVClass *av_class;
    void *priv_data;
    unsigned int index;
    int64_t id;
    enum AVStreamGroupParamsType type;
    union {
        struct AVIAMFAudioElement *iamf_audio_element;
        struct AVIAMFMixPresentation *iamf_mix_presentation;
        struct AVStreamGroupTileGrid *tile_grid;
        struct AVStreamGroupLCEVC *lcevc;
    } params;
    AVDictionary *metadata;
    unsigned int nb_streams;
    AVStream **streams;
    int disposition;
} AVStreamGroup;

typedef struct AVChapter {
    int64_t id;
    AVRational time_base;
    int64_t start, end;
    AVDictionary *metadata;
} AVChapter;

typedef struct AVProgram {
    int id;
    int flags;
    enum AVDiscard discard;
    unsigned int *stream_index;
    unsigned int nb_stream_indexes;
    AVDictionary *metadata;
    int program_num;
    int pmt_pid;
    int pcr_pid;
    int pmt_version;
    int64_t start_time;
    int64_t end_time;
    int64_t pts_wrap_reference;
    int pts_wrap_behavior;
} AVProgram;

typedef struct AVIOInterruptCB {
    int (*callback)(void*);
    void *opaque;
} AVIOInterruptCB;

typedef int (*av_format_control_message)(struct AVFormatContext *s, int type, void *data, size_t data_size);

typedef struct AVFormatContext {
    const AVClass *av_class;
    const struct AVInputFormat *iformat;
    const struct AVOutputFormat *oformat;
    void *priv_data;
    AVIOContext *pb;
    int ctx_flags;
    unsigned int nb_streams;
    AVStream **streams;
    unsigned int nb_stream_groups;
    AVStreamGroup **stream_groups;
    unsigned int nb_chapters;
    AVChapter **chapters;
    char *url;
    int64_t start_time;
    int64_t duration;
    int64_t bit_rate;
    unsigned int packet_size;
    int max_delay;
    int flags;
    int64_t probesize;
    int64_t max_analyze_duration;
    const uint8_t *key;
    int keylen;
    unsigned int nb_programs;
    AVProgram **programs;
    enum AVCodecID video_codec_id;
    enum AVCodecID audio_codec_id;
    enum AVCodecID subtitle_codec_id;
    enum AVCodecID data_codec_id;
    AVDictionary *metadata;
    int64_t start_time_realtime;
    int fps_probe_size;
    int error_recognition;
    AVIOInterruptCB interrupt_callback;
    int debug;
    int max_streams;
    unsigned int max_index_size;
    unsigned int max_picture_buffer;
    int64_t max_interleave_delta;
    int max_ts_probe;
    int max_chunk_duration;
    int max_chunk_size;
    int max_probe_packets;
    int strict_std_compliance;
    int event_flags;
    int avoid_negative_ts;
    int audio_preload;
    int use_wallclock_as_timestamps;
    int skip_estimate_duration_from_pts;
    int avio_flags;
    enum AVDurationEstimationMethod duration_estimation_method;
    int64_t skip_initial_bytes;
    unsigned int correct_ts_overflow;
    int seek2any;
    int flush_packets;
    int probe_score;
    int format_probesize;
    char *codec_whitelist;
    char *format_whitelist;
    char *protocol_whitelist;
    char *protocol_blacklist;
    int io_repositioned;
    const struct AVCodec *video_codec;
    const struct AVCodec *audio_codec;
    const struct AVCodec *subtitle_codec;
    const struct AVCodec *data_codec;
    int metadata_header_padding;
    void *opaque;
    av_format_control_message control_message_cb;
    int64_t output_ts_offset;
    uint8_t *dump_separator;
    int (*io_open)(struct AVFormatContext *s, AVIOContext **pb, const char *url, int flags, AVDictionary **options);
    int (*io_close2)(struct AVFormatContext *s, AVIOContext *pb);
    int64_t duration_probesize;
} AVFormatContext;

typedef struct AVInputFormat {
    const char *name;
    const char *long_name;
    int flags;
    const char *extensions;
    const struct AVCodecTag * const *codec_tag;
    const AVClass *priv_class;
    const char *mime_type;
} AVInputFormat;

// Functions
void av_log_set_level(int level);
const char *av_version_info(void);
AVFormatContext *avformat_alloc_context(void);
int avformat_open_input(AVFormatContext **ps, const char *url, const AVInputFormat *fmt, AVDictionary **options);
int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options);
int av_read_frame(AVFormatContext *s, AVPacket *pkt);
void avformat_close_input(AVFormatContext **s);// Structs
typedef struct SwsContext SwsContext;

typedef struct SwsVector {
    double *coeff;
    int length;
} SwsVector;

typedef struct SwsFilter {
    SwsVector *lumH;
    SwsVector *lumV;
    SwsVector *chrH;
    SwsVector *chrV;
} SwsFilter;

// Functions
SwsContext *sws_getCachedContext(SwsContext *context, int srcW, int srcH, enum AVPixelFormat srcFormat, int dstW, int dstH, enum AVPixelFormat dstFormat, int flags, SwsFilter *srcFilter, SwsFilter *dstFilter, const double *param);
int sws_scale(SwsContext *c, const uint8_t *const srcSlice[], const int srcStride[], int srcSliceY, int srcSliceH, uint8_t *const dst[], const int dstStride[]);
void sws_freeContext(SwsContext *swsContext);<?php

function detectPlatform(): string
{
    $os = strtolower(PHP_OS);
    $arch = php_uname('m');

    if (str_contains($os, 'darwin')) {
        return str_contains($arch, 'arm64') ? 'macosx_arm64' : 'macosx_x86_64';
    }

    if (str_contains($os, 'linux')) {
        // Check for musl (Alpine Linux)
        $ldd = @shell_exec('ldd --version 2>&1');
        if (str_contains($ldd ?? '', 'musl')) {
            return match ($arch) {
                'aarch64' => 'musllinux_aarch64',
                'i686' => 'musllinux_i686',
                'x86_64' => 'musllinux_x86_64',
                default => throw new RuntimeException("Unsupported architecture: $arch")
            };
        }

        return match ($arch) {
            'aarch64' => 'manylinux_aarch64',
            'i686' => 'manylinux_i686',
            'x86_64' => 'manylinux_x86_64',
            default => throw new RuntimeException("Unsupported architecture: $arch")
        };
    }

    if (str_contains($os, 'win')) {
        return 'win_amd64';
    }

    throw new RuntimeException("Unsupported operating system: $os");
}

function coloredProgressBar($current, $total, $barLength = 30) {
    static $startTime = null;
    static $lastTime = null;
    static $lastProgress = 0;

    // Initialize timing on first call
    if ($startTime === null) {
        $startTime = time();
        $lastTime = $startTime;
        $lastProgress = $current;
        return;
    }

    $progress = ($current / $total);
    $filled = floor($barLength * $progress);
    $empty = $barLength - $filled;

    // ANSI color codes
    $green = "\033[32m";
    $yellow = "\033[33m";
    $reset = "\033[0m";

    $bar = $green . str_repeat('â–“', $filled);
    if ($filled < $barLength) {
        $bar .= $yellow . 'â–‘' . str_repeat(' ', $empty - 1);
    }
    $bar .= $reset;

    $percent = number_format($progress * 100, 1);

    // Convert to megabytes with 2 decimal places
    $currentMB = number_format($current / (1024 * 1024), 2);
    $totalMB = number_format($total / (1024 * 1024), 2);
    $status = str_pad("$currentMB/$totalMB MB", 15, ' ', STR_PAD_LEFT);

    // Calculate time remaining
    $now = time();
    $timeElapsed = $now - $startTime;

    if ($current > 0 && $current < $total) {
        $timeRemaining = ($timeElapsed / $current) * ($total - $current);

        // Format time remaining (HH:MM:SS or MM:SS)
        if ($timeRemaining > 3600) {
            $timeStr = gmdate("H:i:s", $timeRemaining);
        } else {
            $timeStr = gmdate("i:s", $timeRemaining);
        }
    } else {
        $timeStr = "00:00";
    }

    echo "\r[$bar] $percent% $status \t ETA: $timeStr";
    if ($current === $total) {
        $timeElapsedStr = gmdate("H:i:s", $timeElapsed);
        echo "\r[$bar] $percent% $status \t Time: $timeElapsedStr" . str_repeat(' ', 10) . PHP_EOL;
    }

    $lastTime = $now;
    $lastProgress = $current;
}
function downloadWithProgress(string $url, string $destination): void
{
    $progress = null;
    $totalBytes = 0;
    $ctx = stream_context_create([], ['notification' => function (
        $notificationCode,
        $severity,
        $message,
        $messageCode,
        $bytesTransferred,
        $bytesMax
    ) use (&$progress, &$totalBytes) {
        if ($bytesMax > 0) {
            $totalBytes = $bytesMax;
        }
        if ($notificationCode === STREAM_NOTIFY_PROGRESS) {
            if ($totalBytes > 0) {
                coloredProgressBar($bytesTransferred, $totalBytes);
            }
        }
    }]);

    $data = @file_get_contents($url, false, $ctx);
    if ($data === false) {
        throw new RuntimeException("Failed to download from $url");
    }

    if (file_put_contents($destination, $data) === false) {
        throw new RuntimeException("Failed to write to $destination");
    }
}

function extractTarGz(string $archivePath, string $extractTo): void
{
    if (!is_dir($extractTo)) {
        mkdir($extractTo, 0755, true);
    }

    if (stripos(PHP_OS, 'WIN') === 0) {
        $phar = new PharData($archivePath);
        $phar->extractTo($extractTo, null, true);
    } else {
        $escapedArchive = escapeshellarg($archivePath);
        $escapedTarget = escapeshellarg($extractTo);
        $cmd = "tar -xzf $escapedArchive -C $escapedTarget";

        exec($cmd, $output, $exitCode);
        if ($exitCode !== 0) {
            throw new RuntimeException("Failed to extract archive using tar (exit code $exitCode).");
        }
    }
}

function copyLibFiles(string $source, string $destination): void
{
    if (!is_dir($source)) {
        throw new RuntimeException("Source directory $source does not exist");
    }

    if (!is_dir($destination)) {
        mkdir($destination, 0755, true);
    }

    $dir = new RecursiveDirectoryIterator($source, RecursiveDirectoryIterator::SKIP_DOTS);
    $iterator = new RecursiveIteratorIterator($dir, RecursiveIteratorIterator::SELF_FIRST);

    foreach ($iterator as $item) {
        $target = $destination . DIRECTORY_SEPARATOR . $iterator->getSubPathName();
        if ($item->isDir()) {
            if (!is_dir($target)) {
                mkdir($target, 0755);
            }
        } else {
            copy($item->getPathname(), $target);
        }
    }
}

function deleteDirectory(string $dir): bool {
    if (!file_exists($dir)) {
        return false;
    }

    if (!is_dir($dir)) {
        return unlink($dir);
    }

    $items = scandir($dir);
    foreach ($items as $item) {
        if ($item === '.' || $item === '..') {
            continue;
        }

        $path = $dir . DIRECTORY_SEPARATOR . $item;

        if (is_dir($path)) {
            deleteDirectory($path);
        } else {
            unlink($path);
        }
    }

    return rmdir($dir);
}


try {
    $baseUrl = "https://github.com/PHP-WebRTC/ffmpeg-builder/releases/download";
    $ffmpegVersion = "v9.0.1";
    $platform = detectPlatform();
    $fileName = "ffmpeg-$platform.tar.gz";
    $url = "$baseUrl/$ffmpegVersion/$fileName";

    echo "Detected platform: $platform\n";
    echo "Downloading FFmpeg libraries...\n";

    $tempDir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'ffmpeg-download';
    if (!is_dir($tempDir)) {
        mkdir($tempDir, 0755, true);
    }

    $archivePath = $tempDir . DIRECTORY_SEPARATOR . $fileName;
    downloadWithProgress($url, $archivePath);

    echo "Extracting archive...\n";
    $extractPath = $tempDir . DIRECTORY_SEPARATOR . 'extracted';
    extractTarGz($archivePath, $extractPath);

    echo "Copying libraries...\n";
    $libSource = $extractPath . DIRECTORY_SEPARATOR . 'lib';
    $libDestination = __DIR__ . '/../ffmpeg/lib/';
    copyLibFiles($libSource, $libDestination);

    echo "Cleaning up...\n";
    unlink($archivePath);
    deleteDirectory($extractPath);

    echo "FFmpeg libraries successfully installed!\n";
} catch (Exception $e) {
    echo "Error: " . $e->getMessage() . "\n";
    exit(1);
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\AVCodec;

use Webrtc\AVCodec\Data\Packet;
use Webrtc\AVCodec\Frame\FrameInterface;

interface TransCoderInterface
{
    public function encode(?FrameInterface $frame): array;

    public function decode(?Packet $packet): array;
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\AVCodec\Audio;

use Webrtc\AVCodec\Exception\AvCodecException;
use Webrtc\AVCodec\Filter\Filter;
use Webrtc\AVCodec\Filter\Graph;
use Webrtc\AVCodec\Format\AudioFormat;
use Webrtc\AVCodec\Frame\AudioFrame;
use Webrtc\Exception\InvalidArgumentException;

/**
 * Class AudioResampler
 *
 * Handles audio resampling, allowing conversion of the sample rate, channel layout,
 * and/or format of an audio frame.
 */
class AudioResampler
{
    private ?AudioFormat $format;
    private ?string $layout;
    private int $rate;
    private ?int $frameSize;
    private Graph|null $graph = null;
    private bool $isPassthrough = false;
    private AudioFrame $frame;

    /**
     * @param AudioFormat|string $format The target audio format, or a string that specifies it.
     * @param string $layout The target channel layout, or a value that specifies it.
     * @param int $rate The target sample rate in Hz.
     * @param ?int $frameSize The frame size for resampling.
     */
    public function __construct(AudioFormat|string $format, string $layout, int $rate = 0, ?int $frameSize = 0)
    {
        $this->format = $format instanceof AudioFormat ? $format : new AudioFormat($format);
        $this->layout = $layout;
        $this->rate = $rate;
        $this->frameSize = $frameSize;
    }

    /**
     * Resamples an audio frame.
     *
     * Converts the `sample_rate`, `channel_layout`, and/or `format` of an audio frame.
     * If no resampling is needed, returns the input frame as a single-element list.
     * If `frame` is `null`, flushes the resampler.
     *
     * @param AudioFrame|null $frame The input audio frame or `null` to flush the resampler.
     * @return AudioFrame[] An array of resampled audio frames. If flushing, returns an empty array.
     *
     * @throws InvalidArgumentException|AvCodecException If the input frame does not match the resampler configuration.
     */
    public function resample(?AudioFrame $frame): array
    {
        if ($this->graph === null && $frame === null) {
            return [];
        }

        if ($this->isPassthrough) {
            return [$frame]; // If passthrough is enabled, return the frame as is.
        }

        // Initialize the graph based on the first frame.
        if ($this->graph === null) {
            $this->initializeGraph($frame);
            if ($this->isPassthrough) {
                return [$frame];
            }
        }

        // Validate the input frame matches the resampler setup.
        if ($frame !== null) {
            $this->validateFrame($frame);
        }

        $this->graph->push($frame);
        $output = [];

        // Collect all frames from the resampler output. pull() throws AvCodecException with the
        // EAGAIN code when there is simply no more output to drain; any other exception is a real
        // resampling failure and must propagate rather than be silently treated as end-of-output.
        while (true) {
            try {
                $output[] = $this->graph->pull();
            } catch (AvCodecException $e) {
                if ($e->getCode() !== EAGAIN) {
                    throw $e;
                }
                break;
            }
        }

        return $output;
    }

    /**
     * Initializes the filter graph for resampling based on the first input frame.
     *
     * @param AudioFrame $frame The input frame used to initialize the graph.
     */
    private function initializeGraph(AudioFrame $frame): void
    {
        $this->frame = $frame;

        $this->format = $this->format ?? $frame->getFormat();
        $this->layout = $this->layout ?? $frame->getLayout()->getName();
        $this->rate = $this->rate ?: $frame->getSampleRate();

        // Check if passthrough is possible.
        if (
            $frame->getFormat()->getSampleFormat() === $this->format->getSampleFormat() &&
            $frame->getLayout()->getName() === $this->layout &&
            $frame->getSampleRate() === $this->rate &&
            $this->frameSize === 0
        ) {
            $this->isPassthrough = true;
            return;
        }

        if ($frame->getSampleRate() === 0 and $frame->getSampleRate() !== $this->rate) {
            $frame->setSampleRate($this->rate);
        }

        // Configure the filter graph.
        $this->graph = new Graph();
        $timeBaseOpt = [];
        if (isset($frame->getTimeBase()->num) && isset($frame->getTimeBase()->den)) {
            $timeBaseOpt = ["time_base" => $frame->getTimeBase()->num . "/" . $frame->getTimeBase()?->den];
        }
        $abuffer = $this->graph->add(new Filter("abuffer"), options: array_merge([
            "sample_rate" => $frame->getSampleRate(),
            "sample_fmt" => $frame->getFormat()->getName(),
            "channel_layout" => $frame->getLayout()->getName()
        ], $timeBaseOpt)
        );
        $aformat = $this->graph->add(new Filter("aformat"), options: [
            "sample_rates" => $this->rate,
            "sample_fmts" => $this->format->getName(),
            "channel_layouts" => $this->layout
        ]);
        $abuffersink = $this->graph->add(new Filter("abuffersink"), args: [null]);
        $abuffer->linkTo($aformat);
        $aformat->linkTo($abuffersink);

        $this->graph->configure();

        if ($this->frameSize > 0) {
            $this->graph->setAudioFrameSize($this->frameSize);
        }
    }

    /**
     * Validates that the input frame matches the resampler setup.
     *
     * @param AudioFrame $frame The frame to validate.
     *
     * @throws InvalidArgumentException If the frame does not match the resampler setup.
     */
    private function validateFrame(AudioFrame $frame): void
    {
        if (
            $frame->getFormat()->getSampleFormat() != $this->frame->getFormat()->getSampleFormat() ||
            $frame->getLayout()->getName() != $this->frame->getLayout()->getName() ||
            $frame->getSampleRate() != $this->frame->getSampleRate()
        ) {
            throw new InvalidArgumentException("Frame does not match AudioResampler setup.");
        }
    }

    /**
     * @return array{format: AudioFormat|null, layout: string|null, rate: int, frameSize: int|null}
     */
    public function __serialize(): array
    {
        return [
            'format' => $this->format,
            'layout' => $this->layout,
            'rate' => $this->rate,
            'frameSize' => $this->frameSize,
        ];
    }

    /**
     * @param array{format?: AudioFormat|null, layout?: string|null, rate?: int, frameSize?: int|null} $data
     */
    public function __unserialize(array $data): void
    {
        $this->format = $data['format'] ?? null;
        $this->layout = $data['layout'] ?? null;
        $this->rate = $data['rate'] ?? 0;
        $this->frameSize = $data['frameSize'] ?? null;
        $this->graph = null;
        $this->isPassthrough = false;
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\AVCodec\Audio;

/**
 * Class AudioChannel
 *
 * Represents an audio channel with a name and description.
 */
readonly class AudioChannel
{
    /**
     * AudioChannel constructor.
     *
     * @param string $name The name of the audio channel.
     * @param string $description A brief description of the audio channel.
     */
    public function __construct(
        private string $name,
        private string $description
    )
    {
    }

    /**
     * @return string The name of the audio channel.
     */
    public function getName(): string
    {
        return $this->name;
    }

    /**
     * @return string The description of the audio channel.
     */
    public function getDescription(): string
    {
        return $this->description;
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\AVCodec\Audio;

use FFI;
use FFI\CData;
use Webrtc\Exception\InvalidArgumentException;
use Webrtc\Exception\RuntimeException;
use Webrtc\Mixin\SharedLibraryInterface;

/**
 * Class AudioLayout
 *
 * Represents an audio Layout with channel count and channel name.
 */
class AudioLayout implements SharedLibraryInterface
{
    private CData $layout;
    private FFI $libAVCodec;

    public function __construct(string|CData $layout)
    {
        $this->initiateSharedLibrary();

        if (is_string($layout)) {
            $this->layout = $this->libAVCodec->new("AVChannelLayout");

            $result = $this->libAVCodec->av_channel_layout_from_string(FFI::addr($this->layout), $layout);

            if ($result < 0) {
                throw new  InvalidArgumentException("Error parsing channel layout: $result");
            }
        } else {
            $this->layout = $layout;
        }
    }

    /**
     * @return int The number of channels
     */
    public function getNbChannels(): int
    {
        return $this->layout->nb_channels;
    }

    /**
     * @return array<AudioChannel>
     */
    public function getChannels(): array
    {
        $results = [];
        for ($i = 0; $i < $this->layout->nb_channels; $i++) {
            $channel = $this->libAVCodec->av_channel_layout_channel_from_index(FFI::addr($this->layout), $i);

            $buf = $this->libAVCodec->new('char[16]');
            $buf2 = $this->libAVCodec->new('char[128]');

            $size = $this->libAVCodec->av_channel_name($buf, 16, $channel) - 1;
            $size2 = $this->libAVCodec->av_channel_description($buf2, 128, $channel) - 1;

            $results[] = new AudioChannel(
                FFI::string($buf, $size),
                FFI::string($buf2, $size2)
            );
        }

        return $results;
    }

    /**
     * @return string The canonical name of the audio layout.
     */
    public function getName(): string
    {
        $bufSize = 128;
        $buf = $this->libAVCodec->new("char[$bufSize]");
        $ret = $this->libAVCodec->av_channel_layout_describe(FFI::addr($this->layout), $buf, $bufSize);
        if ($ret < 0) {
            throw new RuntimeException("Failed to get layout name: " . $ret);
        }

        return FFI::string($buf);
    }

    public function getLayout(): ?CData
    {
        return $this->layout;
    }

    /**
     * Load the shared library instance for AVCodec.
     *
     * @return void
     *
     * @throws RuntimeException If the shared library is not initialized.
     */
    public function initiateSharedLibrary(): void
    {
        global $libAVCodec;

        if (!$libAVCodec instanceof FFI) {
            throw new RuntimeException("Failed to load AVCodec shared library.");
        }

        $this->libAVCodec = $libAVCodec;
    }

    /**
     * @return array{name: string}
     */
    public function __serialize(): array
    {
        return ['name' => $this->getName()];
    }

    /**
     * @param array{name?: string} $data
     */
    public function __unserialize(array $data): void
    {
        $this->__construct($data['name'] ?? 'stereo');
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\AVCodec;

use FFI;
use FFI\Exception as FFIException;
use Webrtc\AVCodec\Exception\AvCodecException;

/**
 * Class AVFilter
 *
 * This class provides methods to initialize the AVFilter library and handle
 * AVFilter-related operations.
 */
class AVFilter
{
    /**
     * The minimum supported major version of libavfilter.
     */
    private const SUPPORTED_VERSION = 9;

    /**
     * The path to the AVFilter C header file.
     */
    private const HEADER_FILE_PATH = __DIR__ . "/libav/include/avfilter.h";

    /**
     * Initializes the AVFilter library.
     *
     * This method attempts to load the libavfilter shared library via FFI, verify its version,
     * and set necessary configurations. If initialization fails, a detailed exception is thrown.
     *
     * @param bool $debug Whether to enable debug logging for AVFilter.
     *
     * @return void
     *
     * @throws AvCodecException If the AVFilter library cannot be loaded or the version is unsupported.
     */
    public static function init(bool $debug = false): void
    {
        global $libAVFilter;

        if (!isset($libAVFilter)) {
            try {
                $lib = getenv("LIB_AVFILTER_PATH") ?: self::getLibPath();
                // Bind into a local first. A library that fails the check below must not be left
                // behind in the global, or the next init() call would see it already set, skip the
                // check and hand out a binding whose struct layouts do not match the loaded ABI.
                $binding = FFI::cdef(file_get_contents(self::HEADER_FILE_PATH), $lib);

                LibraryVersion::assertSupported(
                    $binding->av_version_info(),
                    self::SUPPORTED_VERSION,
                    "libavfilter"
                );

                $libAVFilter = $binding;

                if ($debug) {
                    $logCallback = function ($avcl, $level, $fmt, $args) {
                        echo "[FFmpeg] $fmt\n";
                    };
                    $libAVFilter->av_log_set_callback($logCallback);
                    $libAVFilter->av_log(null, 8, "Test log message\n");
                }

            } catch (FFIException $e) {
                $os = PHP_OS_FAMILY;
                $installHint = match ($os) {
                    'Windows' => <<<EOT
Download and install FFmpeg 9 with development files (DLLs) from https://ffmpeg.org/download.html.
Make sure avfilter-*.dll is in your PATH or specify the correct LIB_AVFILTER_PATH environment variable.
EOT,
                    'Darwin' => <<<EOT
Install FFmpeg with development headers on macOS:

    brew install ffmpeg

Ensure it is version 9.x. You may need to run:

    brew upgrade ffmpeg
EOT,
                    'Linux' => <<<EOT
Install FFmpeg development packages on Linux:

For Debian/Ubuntu:

    sudo apt update
    sudo apt install libavfilter-dev

For Fedora/RHEL:

    sudo dnf install ffmpeg-devel

Ensure the installed version is 9.x. If needed, build FFmpeg manually from source.
EOT,
                    default => "Please install FFmpeg 9 and ensure development libraries are available. See https://ffmpeg.org/download.html for instructions."
                };

                throw new AvCodecException(sprintf(
                    "Couldn't load AvFilter library: %s\n\nInstallation instructions:\n%s",
                    $e->getMessage(),
                    $installHint
                ), $e->getCode(), $e);
            }
        }
    }

    /**
     * Determines and returns the appropriate libavfilter shared library path.
     *
     * This method tries common library locations based on the operating system
     * and returns the most probable path to the libavfilter shared library.
     * If no common path is found, it returns a generic library name.
     *
     * @return string The path or name of the libavfilter shared library.
     */
    private static function getLibPath(): string
    {
        $candidates = [];

        $os = PHP_OS_FAMILY;

        if ($os === 'Windows') {
            $candidates = [
                'avfilter-12.dll',
                'avfilter.dll',
            ];
        } elseif ($os === 'Darwin') { // macOS
            $candidates = [
                '/usr/local/lib/libavfilter.dylib',
                '/opt/homebrew/lib/libavfilter.dylib',
                'libavfilter.dylib',
            ];
        } elseif ($os === 'Linux') {
            $candidates = [
                '/usr/lib/x86_64-linux-gnu/libavfilter.so',
                '/usr/local/lib/libavfilter.so',
                'libavfilter.so',
            ];
        } else {
            $candidates = [
                'libavfilter',
            ];
        }

        foreach ($candidates as $candidate) {
            if (is_file($candidate) || @file_exists($candidate)) {
                return $candidate;
            }
        }

        return match ($os) {
            'Windows' => 'avfilter.dll',
            'Darwin' => 'libavfilter.dylib',
            'Linux' => 'libavfilter.so',
            default => 'libavfilter',
        };
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\AVCodec;

use FFI;
use FFI\Exception as FFIException;
use Throwable;
use Webrtc\AVCodec\Exception\AvCodecException;

/**
 * Class AVCodec
 *
 * This class provides methods to initialize the AVCodec library and handle
 * AVCodec-related operations.
 */
class AVCodec
{
    /**
     * The minimum supported major version of libavcodec.
     */
    private const SUPPORTED_VERSION = 9;

    /**
     * The path to the AVCodec C header file.
     */
    private const HEADER_FILE_PATH = __DIR__ . "/libav/include/avcodec.h";

    /**
     * Initializes the AVCodec library.
     *
     * This method attempts to load the libavcodec shared library via FFI, verify its version,
     * and set the necessary constants. If initialization fails, a detailed exception is thrown.
     *
     * @param bool $debug Whether to enable debug logging for AVCodec.
     *
     * @return void
     *
     * @throws AvCodecException If the AVCodec library cannot be loaded or the version is unsupported.
     */
    /**
     * Report whether the FFmpeg stack this package binds against can actually be loaded.
     *
     * Transcoding is optional: media that is already encoded is packetized without FFI, so
     * callers use this to decide whether transcoding is on the table rather than to decide
     * whether they can proceed at all.
     *
     * @return bool True when libavcodec, libavformat and libavfilter all load and match.
     */
    public static function isAvailable(): bool
    {
        if (!extension_loaded('FFI')) {
            return false;
        }

        try {
            self::init();
            AVFormat::init();
            AVFilter::init();
        } catch (Throwable) {
            return false;
        }

        return true;
    }

    public static function init(bool $debug = false): void
    {
        global $libAVCodec;

        if (!isset($libAVCodec)) {
            try {
                $lib = getenv("LIB_AVCODEC_PATH") ?: self::getLibPath();

                // Bind into a local first. A library that fails the check below must not be left
                // behind in the global, or the next init() call would see it already set, skip the
                // check and hand out a binding whose struct layouts do not match the loaded ABI.
                $binding = FFI::cdef(file_get_contents(self::HEADER_FILE_PATH), $lib);

                LibraryVersion::assertSupported(
                    $binding->av_version_info(),
                    self::SUPPORTED_VERSION,
                    "libavcodec"
                );

                $libAVCodec = $binding;

                self::setDefinition();

                if ($debug) {
                    $logCallback = function ($avcl, $level, $fmt, $args) {
                        echo "[FFmpeg] $fmt\n";
                    };
                    $libAVCodec->av_log_set_callback($logCallback);
                } else {
                    $libAVCodec->av_log_set_level(-8);
                }

            } catch (FFIException $e) {
                $os = PHP_OS_FAMILY;
                $installHint = match ($os) {
                    'Windows' => <<<EOT
Download and install FFmpeg 9 with development files (DLLs) from https://ffmpeg.org/download.html.
Make sure avcodec-*.dll is in your PATH or specify the LIB_AVCODEC_PATH environment variable.
EOT,
                    'Darwin' => <<<EOT
Install FFmpeg with development headers on macOS:

    brew install ffmpeg

Ensure it is version 9.x. You may need to use:

    brew upgrade ffmpeg

If your Homebrew version is too old, you might need to tap a custom formula or build from source.
EOT,
                    'Linux' => <<<EOT
Install FFmpeg development packages on Linux:

For Debian/Ubuntu:

    sudo apt update
    sudo apt install libavcodec-dev

For Fedora/RHEL:

    sudo dnf install ffmpeg-devel

Make sure the installed version is 9.x. If your package manager provides an older version, consider building FFmpeg manually from source.
EOT,
                    default => "Please install FFmpeg 9 and ensure the development libraries (headers and shared libs) are available on your system. See https://ffmpeg.org/download.html for instructions."
                };

                throw new AvCodecException(sprintf(
                    "Couldn't load AvCodec library: %s\n\nInstallation instructions:\n%s",
                    $e->getMessage(),
                    $installHint
                ), $e->getCode(), $e);
            }
        }
    }

    /**
     * Defines the necessary constants related to AVCodec if not already defined.
     *
     * @return void
     */
    private static function setDefinition(): void
    {
        // FFmpeg reports "try again" as AVERROR(EAGAIN), -errno: errno values differ across platforms.
        define("EAGAIN", \in_array(PHP_OS_FAMILY, ['Darwin', 'BSD'], true) ? 35 : 11);
        define("AVERROR_EOF", -541478725);
        define("AV_CODEC_CAP_VARIABLE_FRAME_SIZE", (1 << 16));

        /**
         * AV_ROUND constants for rounding operations
         */
        define('AV_ROUND_ZERO', 0);       // Round toward zero
        define('AV_ROUND_INF', 1);        // Round away from zero
        define('AV_ROUND_DOWN', 2);       // Round toward negative infinity
        define('AV_ROUND_UP', 3);         // Round toward positive infinity
        define('AV_ROUND_NEAR_INF', 5);   // Round to nearest and ties away from zero
        define('AV_ROUND_PASS_MINMAX', 8192); // Flag to pass INT64_MIN/MAX through unchanged

        /**
         * Integer limit constants
         */
        define('INT64_MIN', -9223372036854775808);  // Minimum value for 64-bit signed integer
        define('INT64_MAX', 9223372036854775807);   // Maximum value for 64-bit signed integer
        define('UINT64_C', '0xFFFFFFFFFFFFFFFF');   // Unsigned 64-bit integer constant
    }

    /**
     * Determines and returns the appropriate libavcodec shared library path.
     *
     * This method tries common library locations based on the operating system
     * and returns the most probable path to the libavcodec shared library.
     * If no common path is found, it returns a generic library name.
     *
     * @return string The path or name of the libavcodec shared library.
     */
    private static function getLibPath(): string
    {
        $os = PHP_OS_FAMILY;

        if ($os === 'Windows') {
            $candidates = [
                'avcodec-63.dll',
                'avcodec.dll',
            ];
        } elseif ($os === 'Darwin') { // macOS
            $candidates = [
                '/usr/local/lib/libavcodec.dylib',
                '/opt/homebrew/lib/libavcodec.dylib',
                'libavcodec.dylib',
            ];
        } elseif ($os === 'Linux') {
            $candidates = [
                '/usr/lib/x86_64-linux-gnu/libavcodec.so',
                '/usr/local/lib/libavcodec.so',
                'libavcodec.so',
            ];
        } else {
            $candidates = [
                'libavcodec',
            ];
        }

        foreach ($candidates as $candidate) {
            if (is_file($candidate) || @file_exists($candidate)) {
                return $candidate;
            }
        }

        return match ($os) {
            'Windows' => 'avcodec.dll',
            'Darwin' => 'libavcodec.dylib',
            'Linux' => 'libavcodec.so',
            default => 'libavcodec',
        };
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\AVCodec;

use FFI;
use FFI\Exception as FFIException;
use Webrtc\AVCodec\Exception\AvCodecException;

/**
 * Class SWScale
 *
 * This class provides methods to initialize the SWScale library and handle
 * SWScale-related operations.
 */
class SWScale
{
    /**
     * The path to the SWScale C header file.
     */
    private const HEADER_FILE_PATH = __DIR__ . "/libav/include/swscale.h";

    /**
     * Initializes the SWScale library.
     *
     * This method attempts to load the libswscale shared library via FFI.
     * If initialization fails, a detailed exception is thrown.
     *
     * @return void
     *
     * @throws AvCodecException If the SWScale library cannot be loaded.
     */
    public static function init(): void
    {
        global $libSWScale;

        if (!isset($libSWScale)) {
            try {
                $lib = getenv("LIB_SWSCALE_PATH") ?: self::getLibPath();
                $libSWScale = FFI::cdef(file_get_contents(self::HEADER_FILE_PATH), $lib);

                if (!$libSWScale) {
                    throw new AvCodecException("FFI failed to load libswscale shared library.");
                }

            } catch (FFIException $e) {
                $os = PHP_OS_FAMILY;
                $installHint = match ($os) {
                    'Windows' => <<<EOT
Download and install FFmpeg (with development headers) from https://ffmpeg.org/download.html.
Ensure swscale-*.dll is accessible in your PATH or specify LIB_SWSCALE_PATH environment variable.
EOT,
                    'Darwin' => <<<EOT
Install FFmpeg with development headers on macOS:

    brew install ffmpeg

Make sure FFmpeg version is compatible and includes swscale.
EOT,
                    'Linux' => <<<EOT
Install FFmpeg development packages on Linux:

For Debian/Ubuntu:

    sudo apt update
    sudo apt install libswscale-dev

For Fedora/RHEL:

    sudo dnf install ffmpeg-devel

Make sure installed version includes swscale library.
EOT,
                    default => "Please install FFmpeg and ensure libswscale is available. Visit https://ffmpeg.org/download.html for instructions."
                };

                throw new AvCodecException(sprintf(
                    "Couldn't load SWScale library: %s\n\nInstallation instructions:\n%s",
                    $e->getMessage(),
                    $installHint
                ), $e->getCode(), $e);
            }
        }
    }

    /**
     * Determines and returns the appropriate libswscale shared library path.
     *
     * This method tries common library locations based on the operating system
     * and returns the most probable path to the libswscale shared library.
     *
     * @return string The path or name of the libswscale shared library.
     */
    private static function getLibPath(): string
    {
        $os = PHP_OS_FAMILY;

        if ($os === 'Windows') {
            $candidates = [
                'swscale-10.dll',
                'swscale.dll',
            ];
        } elseif ($os === 'Darwin') { // macOS
            $candidates = [
                '/usr/local/lib/libswscale.dylib',
                '/opt/homebrew/lib/libswscale.dylib',
                'libswscale.dylib',
            ];
        } elseif ($os === 'Linux') {
            $candidates = [
                '/usr/lib/x86_64-linux-gnu/libswscale.so',
                '/usr/local/lib/libswscale.so',
                'libswscale.so',
            ];
        } else {
            $candidates = [
                'libswscale',
            ];
        }

        foreach ($candidates as $candidate) {
            if (is_file($candidate) || @file_exists($candidate)) {
                return $candidate;
            }
        }

        return match ($os) {
            'Windows' => 'swscale.dll',
            'Darwin' => 'libswscale.dylib',
            'Linux' => 'libswscale.so',
            default => 'libswscale',
        };
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\AVCodec\Exception;

use Webrtc\Exception\Exception;

class AvCodecException extends Exception
{

}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\AVCodec;

use FFI;
use FFI\Exception as FFIException;
use Webrtc\AVCodec\Exception\AvCodecException;

/**
 * Class AVFormat
 *
 * This class provides methods to initialize the AVFormat library and handle
 * AVFormat-related operations.
 */
class AVFormat
{
    /**
     * The minimum supported major version of libavformat.
     */
    private const SUPPORTED_VERSION = 9;

    /**
     * The path to the AVFormat C header file.
     */
    private const HEADER_FILE_PATH = __DIR__ . "/libav/include/avformat.h";

    /**
     * Initializes the AVFormat library.
     *
     * This method attempts to load the libavformat shared library via FFI, verify its version,
     * and set necessary configurations. If initialization fails, a detailed exception is thrown.
     *
     * @param bool $debug Whether to enable debug logging for AVFormat.
     *
     * @return void
     *
     * @throws AvCodecException If the AVFormat library cannot be loaded or the version is unsupported.
     */
    public static function init(bool $debug = false): void
    {
        global $libAVFormat;

        if (!isset($libAVFormat)) {
            try {
                $lib = getenv("LIB_AVFORMAT_PATH") ?: self::getLibPath();
                // Bind into a local first. A library that fails the check below must not be left
                // behind in the global, or the next init() call would see it already set, skip the
                // check and hand out a binding whose struct layouts do not match the loaded ABI.
                $binding = FFI::cdef(file_get_contents(self::HEADER_FILE_PATH), $lib);

                LibraryVersion::assertSupported(
                    $binding->av_version_info(),
                    self::SUPPORTED_VERSION,
                    "libavformat"
                );

                $libAVFormat = $binding;

                if (!$debug) {
                    $libAVFormat->av_log_set_level(-8); // Set minimal logging when not debugging
                }

            } catch (FFIException $e) {
                $os = PHP_OS_FAMILY;
                $installHint = match ($os) {
                    'Windows' => <<<EOT
Download and install FFmpeg 9 with development files (DLLs) from https://ffmpeg.org/download.html.
Ensure avformat-*.dll is accessible in your PATH or specify LIB_AVFORMAT_PATH environment variable.
EOT,
                    'Darwin' => <<<EOT
Install FFmpeg with development headers on macOS:

    brew install ffmpeg

Ensure it is version 9.x. Upgrade if necessary:

    brew upgrade ffmpeg
EOT,
                    'Linux' => <<<EOT
Install FFmpeg development packages on Linux:

For Debian/Ubuntu:

    sudo apt update
    sudo apt install libavformat-dev

For Fedora/RHEL:

    sudo dnf install ffmpeg-devel

Ensure the installed version is 9.x. If needed, compile FFmpeg manually.
EOT,
                    default => "Please install FFmpeg 9 and ensure development libraries are available. Visit https://ffmpeg.org/download.html for instructions."
                };

                throw new AvCodecException(sprintf(
                    "Couldn't load AvFormat library: %s\n\nInstallation instructions:\n%s",
                    $e->getMessage(),
                    $installHint
                ), $e->getCode(), $e);
            }
        }
    }

    /**
     * Determines and returns the appropriate libavformat shared library path.
     *
     * This method tries common library locations based on the operating system
     * and returns the most probable path to the libavformat shared library.
     * If no common path is found, it returns a generic library name.
     *
     * @return string The path or name of the libavformat shared library.
     */
    private static function getLibPath(): string
    {
        $candidates = [];

        $os = PHP_OS_FAMILY;

        if ($os === 'Windows') {
            $candidates = [
                'avformat-63.dll',
                'avformat.dll',
            ];
        } elseif ($os === 'Darwin') { // macOS
            $candidates = [
                '/usr/local/lib/libavformat.dylib',
                '/opt/homebrew/lib/libavformat.dylib',
                'libavformat.dylib',
            ];
        } elseif ($os === 'Linux') {
            $candidates = [
                '/usr/lib/x86_64-linux-gnu/libavformat.so',
                '/usr/local/lib/libavformat.so',
                'libavformat.so',
            ];
        } else {
            $candidates = [
                'libavformat',
            ];
        }

        foreach ($candidates as $candidate) {
            if (is_file($candidate) || @file_exists($candidate)) {
                return $candidate;
            }
        }

        return match ($os) {
            'Windows' => 'avformat.dll',
            'Darwin' => 'libavformat.dylib',
            'Linux' => 'libavformat.so',
            default => 'libavformat',
        };
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\AVCodec\Data;

interface BufferInterface
{
    public function putData(string $data): void;
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\AVCodec\Data;

use Exception;
use FFI;
use Webrtc\AVCodec\Frame\VideoFrame;
use Webrtc\Exception\InvalidArgumentException;
use Webrtc\Exception\RuntimeException;

/**
 * VideoPlane Class
 *
 * Represents a single plane of video data within a video frame.
 * Video frames can have multiple planes depending on the pixel format (e.g., Y, U, V planes
 * in YUV formats). This class provides access to individual plane data and properties.
 */
class VideoPlane extends Buffer
{
    /**
     * @var int Size of the plane buffer in bytes
     */
    private int $bufferSize;

    /**
     * Constructor for VideoPlane
     *
     * Initializes a video plane for the specified frame and plane index.
     * Calculates the appropriate buffer size based on the pixel format.
     *
     * @param VideoFrame $frame The video frame that contains this plane
     * @param int $index The index of the plane within the frame (e.g., 0 for Y plane in YUV)
     *
     * @throws RuntimeException If the specified plane canâ€™t be found in the pixel format
     */
    public function __construct(private readonly VideoFrame $frame, private readonly int $index)
    {
        parent::__construct();
        if ($frame->getVideoFormat()->getName() == "pal8" && $index == 1) {
            $this->bufferSize = 256 * 4;
            return;
        }

        for ($i = 0; $i < $frame->getVideoFormat()->getVideoFormat()->nb_components; $i++) {
            if ($frame->getVideoFormat()->getVideoFormat()->comp[$i]->plane == $index) {
                $height = $frame->getVideoFormat()->getComponents()[$i]->getHeight();
                break;
            }
        }

        if (!isset($height)) {
            throw new RuntimeException("could not find plane $index of {$frame->getVideoFormat()->getName()}");
        }
        $this->bufferSize = abs($this->frame->getFrame()->linesize[$this->index]) * $height;//$this->libAVCodec->av_image_get_buffer_size($videoFormat->getFormat(), $videoFormat->getWidth(), $videoFormat->getHeight(), 32);
    }

    /**
     * Get the plane index
     *
     * @return int The index of this plane within the frame
     */
    public function getIndex(): int
    {
        return $this->index;
    }

    /**
     * Get the parent video frame
     *
     * @return VideoFrame The video frame that contains this plane
     */
    public function getFrame(): VideoFrame
    {
        return $this->frame;
    }

    /**
     * Get the pointer to the plane data
     *
     * Returns the raw FFI pointer to the plane data in memory.
     *
     * @return mixed FFI pointer to the plane data
     */
    public function getPointer(): FFI\CData
    {
        return $this->frame->getFrame()->extended_data[$this->index];
    }

    /**
     * Get the plane data as a string
     *
     * Converts the raw plane data to a PHP string.
     *
     * @return string The plane data as a binary string
     */
    public function getData(): string
    {
        return FFI::string($this->frame->getFrame()->data[$this->index], $this->bufferSize);
    }

    /**
     * Get the size of the plane buffer
     *
     * @return int The size of the plane buffer in bytes
     */
    public function getSize(): int
    {
        return $this->bufferSize;
    }

    /**
     * Get the line size (stride) of the plane
     *
     * The line size may include padding and might be larger than
     * the visible width of the plane.
     *
     * @return mixed The line size in bytes
     */
    public function getLineSize(): int
    {
        return $this->frame->getFrame()->linesize[$this->index];
    }

    /**
     * Put data into the plane buffer
     *
     * Copies the provided data string into the plane buffer.
     * The data size must match the buffer size exactly.
     *
     * @param string $data The binary data to copy into the plane buffer
     *
     * @return void
     * @throws Exception If the buffer is not writable
     * @throws InvalidArgumentException If the input data size doesn't match the buffer size
     * @throws RuntimeException If there's a null pointer in extended_data
     */
    public function putData(string $data): void
    {
        if (!$this->bufferWritable) {
            throw new Exception("Buffer is not writable");
        }
        // TODO: check this line code later
//        $this->allocateBuffer();
        $dataSize = strlen($data);


        // TODO: check this line code later
//        // Manually ensure input data size fits allocated frame buffer
//        if ($dataSize > $this->bufferSize / 3) { // Assuming three equal parts for Y, U, V
//            throw new InvalidArgumentException("Input data size exceeds allocated buffer size");
//        }

        if (strlen($data) != $this->bufferSize) {
            throw new InvalidArgumentException("Input data size exceeds allocated buffer size");
        }

        if (FFI::isNull($this->frame->getFrame()->extended_data[$this->index])) {
            throw new RuntimeException("Null pointer encountered in extended_data");
        }

        FFI::memcpy($this->frame->getFrame()->extended_data[$this->index], $data, $dataSize);
    }

    /**
     * Allocate buffer for the plane
     *
     * Allocates memory for the plane buffer and fills the image arrays.
     * Currently, disabled and marked with TODOs.
     *
     * @return void
     * @throws RuntimeException If buffer allocation or image array filling fails
     */
    private function allocateBuffer(): void
    {
        // TODO: check this line code later
//        if ($this->libAVCodec->av_frame_get_buffer($this->frame->getFrame(), 32) < 0) {
//            throw new InvalidArgumentException("Failed to allocate frame buffer");
//        }

        // Allocate buffer size manually and ensure it is correctly filled
        $videoFormat = $this->frame->getVideoFormat();
        $buffer = $this->libAVCodec->new("uint8_t[$this->bufferSize]");
        if ($buffer === null) {
            throw new RuntimeException("Failed to allocate buffer");
        }

        if ($this->libAVCodec->av_image_fill_arrays($this->frame->getFrame()->data, $this->frame->getFrame()->linesize, $buffer, $videoFormat->getFormat(), $videoFormat->getWidth(), $videoFormat->getHeight(), 32) < 0) {
            throw new RuntimeException("Failed to fill image arrays");
        }
    }

    /**
     * Destructor for VideoPlane
     *
     * Cleans up resources when the object is destroyed.
     * Currently, most functionality is disabled and marked with TODOs.
     */
    public function __destruct()
    {
        // TODO: check this line code later
//        if ($this->buffer) {
//            FFI::free($this->buffer);
//            $this->buffer = null;
//        }
    }

    /**
     * @return array{frame: VideoFrame, index: int, bufferWritable: bool}
     */
    public function __serialize(): array
    {
        return [
            'frame' => $this->frame,
            'index' => $this->index,
            'bufferWritable' => $this->bufferWritable,
        ];
    }

    /**
     * @param array{frame?: VideoFrame, index?: int, bufferWritable?: bool} $data
     */
    public function __unserialize(array $data): void
    {
        $this->__construct($data['frame'], $data['index'] ?? 0);
        $this->bufferWritable = $data['bufferWritable'] ?? true;
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\AVCodec\Data;

use FFI;
use Webrtc\Mixin\SharedLibraryInterface;

/**
 * Buffer Abstract Base Class
 *
 * Provides common functionality for working with audio/video data buffers
 * through FFI. Serves as the base class for all buffer types in the system.
 *
 * @package Webrtc\AVCodec\Data
 */
abstract class Buffer implements SharedLibraryInterface, BufferInterface
{
    /**
     * @var FFI $libAVCodec FFI instance for AVCodec library
     */
    protected FFI $libAVCodec;

    /**
     * @var bool $bufferWritable Flag indicating if buffer can be modified
     */
    protected bool $bufferWritable = true;

    /**
     * Constructor - initializes the shared library
     */
    public function __construct()
    {
        $this->initiateSharedLibrary();
    }

    /**
     * Get buffer size (abstract)
     *
     * @return int Size of buffer in bytes
     */
    abstract protected function getSize(): int;

    /**
     * Initialize shared library instance
     */
    public function initiateSharedLibrary(): void
    {
        global $libAVCodec;

        if ($libAVCodec instanceof FFI) {
            $this->libAVCodec = $libAVCodec;
        }
    }

    /**
     * Check if buffer is writable
     *
     * @return bool True if buffer can be modified
     */
    public function isBufferWritable(): bool
    {
        return $this->bufferWritable;
    }

    /**
     * Set buffer writable flag
     *
     * @param bool $isBufferWritable New writable state
     */
    public function setIsBufferWritable(bool $isBufferWritable): void
    {
        $this->bufferWritable = $isBufferWritable;
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\AVCodec\Data;

use FFI;
use FFI\CData;
use stdClass;
use Webrtc\Exception\InvalidArgumentException;

/**
 * Packet Class
 *
 * Represents an encoded audio/video packet (AVPacket) in the FFmpeg context.
 * Handles creation, storage, and manipulation of encoded media packets.
 *
 * @package Webrtc\AVCodec\Data
 */
class Packet extends Buffer
{
    /**
     * @var CData $packet FFI CData representing AVPacket
     */
    private CData $packet;

    /**
     * @var stdClass $timeBase Time base for PTS/DTS calculations
     */
    private stdClass $timeBase;

    /**
     * Constructor - allocates a new AVPacket
     */
    public function __construct()
    {
        parent::__construct();
        $this->timeBase = new stdClass();
        $this->packet = $this->libAVCodec->av_packet_alloc();
    }

    /**
     * Destructor - free packet memory
     */
    public function __destruct()
    {
        $this->libAVCodec->av_packet_free(FFI::addr($this->packet));
    }

    /**
     * Get packet size in bytes
     *
     * @return int Size of packet data
     */
    public function getSize(): int
    {
        return $this->packet->size;
    }

    /**
     * Get raw pointer to packet data
     *
     * @return mixed FFI pointer to packet data
     */
    public function getPointer(): CData
    {
        return $this->packet->data;
    }

    /**
     * Get packet data as string
     *
     * @return string Raw packet data bytes
     */
    public function getData(): string
    {
        return FFI::string($this->packet->data, $this->packet->size);
    }

    /**
     * Get presentation timestamp (PTS)
     *
     * @return int|null PTS value or null if not set (AV_NOPTS_VALUE)
     */
    public function getPts(): ?int
    {
        if ($this->packet->pts != $this->libAVCodec->AV_NOPTS_VALUE) {
            return $this->packet->pts;
        }
        return null;
    }

    /**
     * Set presentation timestamp (PTS)
     *
     * @param int|null $value PTS value or null to set as AV_NOPTS_VALUE
     */
    public function setPts(?int $value): void
    {
        $this->packet->pts = $value ?? $this->libAVCodec->AV_NOPTS_VALUE;
    }

    /**
     * Get raw FFI packet object
     *
     * @return CData The FFI CData AVPacket
     */
    public function getPacket(): CData
    {
        return $this->packet;
    }

    /**
     * Store data in a packet
     *
     * @param string $data Data to store in a packet
     * @throws InvalidArgumentException If packet creation fails
     */
    public function putData(string $data): void
    {
        $res = $this->libAVCodec->av_new_packet($this->packet, strlen($data));
        if ($res < 0) {
            throw new InvalidArgumentException("Couldn't create a new packet");
        }

        FFI::memcpy($this->packet->data, $data, strlen($data));
    }

    /**
     * Set time base for timestamp calculations
     *
     * @param int $numerator Time base numerator
     * @param int $denominator Time base denominator
     */
    public function setTimeBase(int $numerator, int $denominator): void
    {
        $this->timeBase->num = $numerator;
        $this->timeBase->den = $denominator;
    }

    /**
     * Get current time base
     *
     * @return stdClass Object with num/den properties
     */
    public function getTimeBase(): stdClass
    {
        return $this->timeBase;
    }

    /**
     * @return array{payload: string, pts: int|null, timeBase: stdClass, bufferWritable: bool}
     */
    public function __serialize(): array
    {
        $this->initiateSharedLibrary();

        return [
            'payload' => $this->getSize() > 0 ? $this->getData() : '',
            'pts' => $this->getPts(),
            'timeBase' => $this->timeBase,
            'bufferWritable' => $this->bufferWritable,
        ];
    }

    /**
     * @param array{payload?: string, pts?: int|null, timeBase?: stdClass, bufferWritable?: bool} $data
     */
    public function __unserialize(array $data): void
    {
        $this->__construct();
        $payload = $data['payload'] ?? '';
        if ($payload !== '') {
            $this->putData($payload);
        }
        if (array_key_exists('pts', $data)) {
            $this->setPts($data['pts']);
        }
        if (isset($data['timeBase']) && $data['timeBase'] instanceof stdClass) {
            $this->timeBase = $data['timeBase'];
        }
        if (isset($data['bufferWritable'])) {
            $this->bufferWritable = $data['bufferWritable'];
        }
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\AVCodec\Data;

use FFI;
use Webrtc\AVCodec\Frame\AudioFrame;
use Webrtc\Exception\InvalidArgumentException;

/**
 * Audio Plane Class
 *
 * Represents a single plane of audio data within an audio frame.
 * Provides access to raw audio data and metadata for a specific channel/plane.
 *
 * @package Webrtc\AVCodec\Data
 */
class AudioPlane extends Buffer
{
    /**
     * @var int $bufferSize Size of the audio buffer in bytes
     */
    private int $bufferSize;

    /**
     * Constructor
     *
     * @param AudioFrame $frame Parent audio frame
     * @param int $index Plane/channel index
     */
    public function __construct(private readonly AudioFrame $frame, private readonly int $index)
    {
        parent::__construct();
        $this->bufferSize = abs($this->frame->getFrame()->linesize[0]);
    }

    /**
     * Get the plane/channel index
     *
     * @return int Zero-based plane index
     */
    public function getIndex(): int
    {
        return $this->index;
    }

    /**
     * Get the parent audio frame
     *
     * @return AudioFrame Associated audio frame
     */
    public function getFrame(): AudioFrame
    {
        return $this->frame;
    }

    /**
     * Get raw pointer to audio data
     *
     * @return mixed FFI pointer to audio data
     */
    public function getPointer(): FFI\CData
    {
        return $this->frame->getFrame()->extended_data[$this->index];
    }

    /**
     * Get audio data as string
     *
     * @return string Raw audio data bytes
     */
    public function getData(): string
    {
        return FFI::string($this->frame->getFrame()->data[$this->index], $this->bufferSize);
    }

    /**
     * Get buffer size in bytes
     *
     * @return int Size of audio buffer
     */
    public function getSize(): int
    {
        return $this->bufferSize;
    }

    /**
     * Get line size (stride) of audio data
     *
     * @return mixed Line size in bytes
     */
    public function getLineSize(): int
    {
        return $this->frame->getFrame()->linesize[$this->index];
    }

    /**
     * Write data to audio plane
     *
     * @param string $data Raw audio data to write
     * @throws InvalidArgumentException If data exceeds buffer size
     */
    public function putData(string $data): void
    {
        if(strlen($data) > $this->bufferSize) {
            throw new InvalidArgumentException("Input data size exceeds allocated buffer size");
        }

        FFI::memcpy($this->frame->getFrame()->extended_data[$this->index], $data, strlen($data));
    }

    /**
     * @return array{frame: AudioFrame, index: int, bufferWritable: bool}
     */
    public function __serialize(): array
    {
        return [
            'frame' => $this->frame,
            'index' => $this->index,
            'bufferWritable' => $this->bufferWritable,
        ];
    }

    /**
     * @param array{frame?: AudioFrame, index?: int, bufferWritable?: bool} $data
     */
    public function __unserialize(array $data): void
    {
        $this->__construct($data['frame'], $data['index'] ?? 0);
        $this->bufferWritable = $data['bufferWritable'] ?? true;
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\AVCodec\Filter;

use FFI;
use Webrtc\AVCodec\Frame\AudioFrame;
use Webrtc\AVCodec\Frame\FrameInterface;
use Webrtc\AVCodec\Frame\VideoFrame;
use Webrtc\Exception\InvalidArgumentException;
use Webrtc\Exception\RuntimeException;
use Webrtc\Mixin\SharedLibraryInterface;

/**
 * Graph Class
 *
 * Manages a filter graph for audio and video processing using FFmpeg's libavfilter library.
 *
 * This class provides methods to construct, configure, and manage filter graphs, including:
 * - Adding and linking filters.
 * - Configuring the filter graph.
 * - Pushing and pulling frames for processing.
 */
class Graph implements SharedLibraryInterface
{
    /**
     * @var FFI\CData $graph Pointer to the FFmpeg filter graph.
     */
    private FFI\CData $graph;
    /**
     * @var bool $configured Indicates if the graph is configured for processing.
     */
    private bool $configured = false;
    /**
     * @var array $nameCounts Tracks filter name counts for unique naming.
     */
    private array $nameCounts = [];
    /**
     * @var int $nbFiltersSeen Number of filters processed by the graph.
     */
    private int $nbFiltersSeen = 0;
    /**
     * @var array $contextByPtr Filter contexts indexed by pointer.
     */
    private array $contextByPtr = [];
    /**
     * @var array $contextByName Filter contexts indexed by name.
     */
    private array $contextByName = [];
    /**
     * @var array $contextByType Filter contexts grouped by type.
     */
    private array $contextByType = [];
    /**
     * @var FFI $libAVFilter
     */
    private FFI $libAVFilter;

    /**
     * Constructor
     * Allocates a new filter graph.
     */
    public function __construct()
    {
        $this->initiateSharedLibrary();
        $this->graph = $this->libAVFilter->avfilter_graph_alloc();
    }

    /**
     * Destructor
     * Frees the graph
     */
    public function __destruct()
    {
        if ($this->graph) {
            $this->libAVFilter->avfilter_graph_free(FFI::addr($this->graph));
        }
    }

    /**
     * Get a unique name for a filter.
     *
     * @param string $name Base name of the filter.
     * @return string Unique name for the filter.
     */
    private function getUniqueName(string $name): string
    {
        $count = $this->nameCounts[$name] ?? 0;
        $this->nameCounts[$name] = $count + 1;
        return $count > 0 ? "{$name}_{$count}" : $name;
    }

    /**
     * Configure the filter graph.
     *
     * @param bool $autoBuffer Automatically buffer the graph.
     * @param bool $force Force reconfiguration of the graph.
     * @return void
     */
    public function configure(bool $autoBuffer = true, bool $force = false): void
    {
        if ($this->configured && !$force) {
            return;
        }

        $ret = $this->libAVFilter->avfilter_graph_config($this->graph, null);
        if ($ret < 0) {
            throw new RuntimeException("Cannot configure graph config.");
        }
        $this->configured = true;

        // TODO: Check this line later
//        $this->autoRegister();
    }

    /**
     * Link nodes together for simple filter graphs.
     *
     * @param array $nodes Array of nodes to link.
     * @return self
     */
    public function linkNodes(array $nodes): self
    {
        for ($i = 0; $i < count($nodes) - 1; $i++) {
            $nodes[$i]->linkTo($nodes[$i + 1]);
        }
        return $this;
    }

    /**
     * Add a filter to the graph.
     *
     * @param mixed $filter Filter name or Filter object.
     * @param array|null $args
     * @param array|null $options
     * @return FilterContext The created filter context.
     */
    public function add(Filter $filter, ?array $args = null, ?array $options = null): FilterContext
    {
        $name = $this->getUniqueName($options['name'] ?? $filter->getName());
        unset($options['name']);
        $context = new FilterContext($this, $filter, $name);
        if ($args) {
            $context->setup(...$args);
        }
        if (!empty($options)) {
            $context->setupWithDictionary($options);
        }

        // TODO: Check this line later
//        $this->registerContext($context);
        $this->autoRegister();

        return $context;
    }

    /**
     * Register a filter context.
     *
     * @param FilterContext $context The filter context to register.
     * @return void
     */
    private function registerContext(FilterContext $context): void
    {
        $this->contextByPtr[spl_object_id($context)] = $context;
        $this->contextByName[$context->getName()] = $context;
        $this->contextByType[$context->getFilter()->getName()][] = $context;
    }

    /**
     * Automatically register filter contexts in the graph.
     *
     * @return void
     */
    private function autoRegister(): void
    {
        for ($i = $this->nbFiltersSeen; $i < $this->graph->nb_filters; $i++) {
            $cCtx = $this->graph->filters[$i];
            if (isset($this->contextByPtr[spl_object_id($cCtx)])) {
                continue;
            }
            $filter = new Filter($cCtx->filter);
            $cloneCtx = new FilterContext($this, $filter, context: $cCtx);
            $this->registerContext($cloneCtx);
        }
        $this->nbFiltersSeen = $this->graph->nb_filters;
    }

    /**
     * @param int $frameSize
     * @return void
     */
    public function setAudioFrameSize(int $frameSize): void
    {
        if (!$this->configured) {
            throw new InvalidArgumentException("Graph not configured");
        }

        $sinks = $this->contextByType['abuffersink'] ?? [];
        if (empty($sinks)) {
            throw new InvalidArgumentException("Missing abuffersink filter");
        }

        foreach ($sinks as $sink) {
            $this->libAVFilter->av_buffersink_set_frame_size($sink->getCtx(), $frameSize);
        }
    }

    /**
     * @param FrameInterface|null $frame
     * @return void
     */
    public function push(?FrameInterface $frame): void
    {
        if ($frame === null) {
            $contexts = array_merge(
                $this->contextByType['buffer'] ?? [],
                $this->contextByType['abuffer'] ?? []
            );
        } elseif ($frame instanceof VideoFrame) {
            $contexts = $this->contextByType['buffer'] ?? [];
        } elseif ($frame instanceof AudioFrame) {
            $contexts = $this->contextByType['abuffer'] ?? [];
        } else {
            throw new InvalidArgumentException("Invalid frame type");
        }

        foreach ($contexts as $ctx) {
            $ctx->push($frame);
        }
    }

    /**
     * @return FrameInterface
     */
    public function pull(): FrameInterface
    {
        $videoSinks = $this->contextByType['buffersink'] ?? [];
        $audioSinks = $this->contextByType['abuffersink'] ?? [];

        $numSinks = count($videoSinks) + count($audioSinks);

        if ($numSinks !== 1) {
            throw new InvalidArgumentException("Can only auto-pull with a single sink; found {$numSinks}");
        }

        return ($videoSinks ?: $audioSinks)[0]->pull();
    }

    /**
     * @return void
     */
    public function initiateSharedLibrary(): void
    {
        global $libAVFilter;

        if ($libAVFilter instanceof FFI) {
            $this->libAVFilter = $libAVFilter;
        }
    }

    /**
     * @return mixed
     */
    public function getGraph()
    {
        return $this->graph;
    }

    /**
     * @return array{nameCounts: array}
     */
    public function __serialize(): array
    {
        return ['nameCounts' => $this->nameCounts];
    }

    /**
     * @param array{nameCounts?: array} $data
     */
    public function __unserialize(array $data): void
    {
        $this->__construct();
        $this->nameCounts = is_array($data['nameCounts'] ?? null) ? $data['nameCounts'] : [];
        $this->configured = false;
        $this->nbFiltersSeen = 0;
        $this->contextByPtr = [];
        $this->contextByName = [];
        $this->contextByType = [];
    }
}

<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\AVCodec\Filter;

use FFI;
use FFI\CData;
use Webrtc\Exception\InvalidArgumentException;
use Webrtc\Mixin\SharedLibraryInterface;

/**
 * Class Filter
 *
 * Represents an AVFilter and provides access to its properties, options, and capabilities.
 */
class Filter implements SharedLibraryInterface
{
    /**
     * @var CData|null Pointer to the underlying AVFilter structure.
     */
    private ?CData $filter;
    private FFI $libAVFilter;

    /**
     * Filter constructor.
     *
     * @param string|CData $filter
     */
    public function __construct(string|CData $filter)
    {
        $this->initiateSharedLibrary();
        $this->filter = $filter instanceof CData ? $filter : $this->libAVFilter->avfilter_get_by_name($filter);
        if (!$this->filter) {
            throw new InvalidArgumentException("No filter found with the name: $filter");
        }
    }

    /**
     * Get the name of the filter.
     *
     * @return string The name of the filter.
     */
    public function getName(): string
    {
        return $this->filter->name;
    }

    /**
     * Get the description of the filter.
     *
     * @return string The description of the filter.
     */
    public function getDescription(): string
    {
        return $this->filter->description;
    }

    /**
     * Get the flags for the filter.
     *
     * @return int The filter's flags.
     */
    public function getFlags(): int
    {
        return $this->filter->flags;
    }

    /**
     * Check if the filter supports dynamic inputs.
     *
     * @return bool True if the filter supports dynamic inputs; otherwise, false.
     */
    public function hasDynamicInputs(): bool
    {
        return (bool)($this->filter->flags & (1 << 0));
    }

    /**
     * Check if the filter supports dynamic outputs.
     *
     * @return bool True if the filter supports dynamic outputs; otherwise, false.
     */
    public function hasDynamicOutputs(): bool
    {
        return (bool)($this->filter->flags & (1 << 1));
    }

    /**
     * Check if the filter supports timeline features.
     *
     * @return bool True if the filter supports timeline features; otherwise, false.
     */
    public function supportsTimeline(): bool
    {
        return (bool)($this->filter->flags & $this->libAVFilter->AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC);
    }

    /**
     * Check if the filter supports slice threading.
     *
     * @return bool True if the filter supports slice threading; otherwise, false.
     */
    public function supportsSliceThreads(): bool
    {
        return (bool)($this->filter->flags & $this->libAVFilter->AVFILTER_FLAG_SLICE_THREADS);
    }

    /**
     * Check if the filter supports command processing.
     *
     * @return bool True if the filter supports commands; otherwise, false.
     */
    public function supportsCommands(): bool
    {
        return $this->filter->process_command !== null;
    }

    /**
     * Initiate the shared library
     *
     * @return void
     */
    public function initiateSharedLibrary(): void
    {
        global $libAVFilter;

        if ($libAVFilter instanceof FFI) {
            $this->libAVFilter = $libAVFilter;
        }
    }

    /**
     * @return CData|null
     */
    public function getFilter(): ?CData
    {
        return $this->filter;
    }

    /**
     * @return array{name: string}
     */
    public function __serialize(): array
    {
        return ['name' => $this->getName()];
    }

    /**
     * @param array{name?: string} $data
     */
    public function __unserialize(array $data): void
    {
        $this->__construct($data['name'] ?? '');
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\AVCodec\Filter;

use FFI;
use FFI\CData;
use Webrtc\AVCodec\Context\Dictionary;
use Webrtc\AVCodec\Exception\AvCodecException;
use Webrtc\AVCodec\Frame\AudioFrame;
use Webrtc\AVCodec\Frame\FrameInterface;
use Webrtc\AVCodec\Frame\VideoFrame;
use Webrtc\Exception\RuntimeException;
use Webrtc\Mixin\SharedLibraryInterface;

/**
 * Class FilterContext
 *
 * Represents a filter context in a multimedia processing graph, allowing linking,
 * initialization, and frame management. Interacts with the AVFilter library via FFI.
 */
class FilterContext implements SharedLibraryInterface
{
    /**
     * @var CData|null Pointer to the filter context structure.
     */
    private ?CData $ctx;
    private FFI $libAVFilter;

    /**
     * FilterContext constructor.
     *
     * This constructor is intentionally private and restricted to internal calls.
     *
     * @param Graph $graph
     * @param Filter $filter
     * @param string|null $name
     * @param CData|null $context
     */
    public function __construct(private readonly Graph $graph, private readonly Filter $filter, ?string $name = null, ?CData $context = null)
    {
        $this->initiateSharedLibrary();

        $this->ctx = $context ?? $this->libAVFilter->avfilter_graph_alloc_filter($graph->getGraph(), $filter->getFilter(), $name);

        if (!$this->ctx) {
            throw new RuntimeException("Could not allocate AVFilterContext.");
        }
    }

    /**
     * Initiate avfilter using string
     *
     * @param ...$args
     * @return void
     */
    public function setup(...$args): void
    {
        if (count($args) > 0) {
            $ret = $this->libAVFilter->avfilter_init_str($this->ctx, ...$args);

            if ($ret < 0) {
                throw new RuntimeException("Cannot initiate filter context.");
            }
        }
    }

    /**
     *  Initiate avfilter using Dictionary object
     *
     * @param array $opts
     * @return void
     */
    public function setupWithDictionary(array $opts): void
    {
        $dic = new Dictionary();
        $dic->update($opts);

        $castedDic = $this->libAVFilter->cast("AVDictionary*", $dic());

        $ret = $this->libAVFilter->avfilter_init_dict($this->ctx, FFI::addr($castedDic));

        if ($ret < 0) {
            throw new RuntimeException("Cannot initiate filter context.");
        }
    }

    /**
     * Get the name of the filter context.
     *
     * @return string|null The name of the filter context, or null if unavailable.
     */
    public function getName(): ?string
    {
        return FFI::string($this->ctx->name) ?? null;
    }

    /**
     * Link the current filter context to another.
     *
     * @param FilterContext $dest
     * @param int $srcPad
     * @param int $destPad
     */
    public function linkTo(FilterContext $dest, int $srcPad = 0, int $destPad = 0): void
    {
        $ret = $this->libAVFilter->avfilter_link($this->ctx, $srcPad, $dest->getCtx(), $destPad);

        if ($ret < 0) {
            throw new RuntimeException("Cannot initiate filter context.");
        }
    }

    /**
     * Push a frame to the filter context.
     *
     * @param ?FrameInterface $frame The frame to push, or null for end-of-stream.
     */
    public function push(?FrameInterface $frame): void
    {
        $frame = $frame ? $this->libAVFilter->cast("AVFrame*", $frame->getFrame()) : null;
        $ret = $this->libAVFilter->av_buffersrc_write_frame($this->ctx, $frame);
        if ($ret < 0) {
            throw new RuntimeException("Cannot initiate filter context.");
        }
    }

    /**
     * Pull a frame from the filter context.
     *
     * @return FrameInterface The pulled frame.
     * @throws AvCodecException When no frame is currently available (EAGAIN) or the stream ended (EOF).
     * @throws RuntimeException When av_buffersink_get_frame reports a genuine error.
     */
    public function pull(): FrameInterface
    {
        $frame = $this->libAVFilter->av_frame_alloc();
        $this->graph->configure();
        $ret = $this->libAVFilter->av_buffersink_get_frame($this->ctx, $frame);

        // EAGAIN (need more input) and EOF both mean "no more output right now". Signal that
        // with the EAGAIN code so callers can tell it apart from a real libav failure below;
        // otherwise a genuine error would be indistinguishable from normal end-of-output.
        if ($ret === -EAGAIN || $ret === AVERROR_EOF) {
            throw new AvCodecException("No frame available from filter context.", EAGAIN);
        }

        if ($ret < 0) {
            throw new RuntimeException("Cannot pull frame from filter context (error {$ret}).");
        }

        return $this->generateFrame($frame);
    }

    /**
     * @return void
     */
    public function initiateSharedLibrary(): void
    {
        global $libAVFilter;

        if ($libAVFilter instanceof FFI) {
            $this->libAVFilter = $libAVFilter;
        }
    }

    /**
     * @return Filter
     */
    public function getFilter(): Filter
    {
        return $this->filter;
    }

    /**
     * @return Graph
     */
    public function getGraph(): Graph
    {
        return $this->graph;
    }

    /**
     * @return CData|null
     */
    public function getCtx(): ?CData
    {
        return $this->ctx;
    }

    /**
     * Generate a new Video or Audio frame and copy the org frame
     *
     * @param CData $frame
     * @return FrameInterface|null
     */
    private function generateFrame(CData $frame): ?FrameInterface
    {
        $filterName = $this->filter->getName();

        return match ($filterName) {
            "buffersink" => $this->generateVideoFrame($frame),
            "abuffersink" => $this->generateAudioFrame($frame),
            default => null,
        };
    }

    /**
     *  Generate a new Video frame and copy the org frame
     *
     * @param CData $frame
     * @return VideoFrame
     */
    private function generateVideoFrame(CData $frame): VideoFrame
    {
        $videoFrame = new VideoFrame();
        FFI::memcpy(FFI::addr($videoFrame->getFrame()), FFI::addr($frame), FFI::sizeof($frame));
        $videoFrame->setVideoFormat();
        $timeBase = $this->ctx->inputs[0]->time_base;
        $videoFrame->setTimeBase($timeBase->num, $timeBase->den);

        return $videoFrame;
    }

    /**
     * Generate a new Audio frame and copy the org frame
     *
     * @param CData $frame
     * @return AudioFrame
     */
    private function generateAudioFrame(CData $frame): AudioFrame
    {
        $audioFrame = new AudioFrame();
        FFI::memcpy(FFI::addr($audioFrame->getFrame()), FFI::addr($frame), FFI::sizeof($frame));
        $audioFrame->initializeUserAttributes();
        $timeBase = $this->ctx->inputs[0]->time_base;
        $audioFrame->setTimeBase($timeBase->num, $timeBase->den);

        return $audioFrame;
    }

    /**
     * @return array{graph: Graph, filter: string, name: string|null}
     */
    public function __serialize(): array
    {
        return [
            'graph' => $this->graph,
            'filter' => $this->filter->getName(),
            'name' => $this->getName(),
        ];
    }

    /**
     * @param array{graph?: Graph, filter?: string, name?: string|null} $data
     */
    public function __unserialize(array $data): void
    {
        $this->__construct($data['graph'], new Filter($data['filter'] ?? ''), $data['name'] ?? null);
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\AVCodec;

use FFI;
use Webrtc\AVCodec\Context\ContextInterface;
use Webrtc\AVCodec\Context\VideoContext;
use Webrtc\AVCodec\Data\Packet;
use Webrtc\AVCodec\Frame\Frame;
use Webrtc\AVCodec\Frame\FrameInterface;
use Webrtc\Exception\InvalidArgumentException;
use Webrtc\Exception\RuntimeException;
use Webrtc\Mixin\SharedLibraryInterface;

/**
 * TransCoder Class
 *
 * This class handles encoding and decoding of audio and video data using FFI to interface
 * with the AVCodec library. It manages the flow of frames and packets between the codec
 * context and the application.
 */
class TransCoder implements TransCoderInterface, SharedLibraryInterface
{
    /**
     * @var FFI The AVCodec library interface
     */
    private FFI $libAVCodec;

    /**
     * @var FrameInterface|null Cached frame for decoding operations
     */
    private ?FrameInterface $nextFrame = null;

    /**
     * Constructor for TransCoder
     *
     * @param ContextInterface $context The codec context to use for encoding/decoding operations
     */
    public function __construct(private ContextInterface $context)
    {
        $this->initiateSharedLibrary();
    }

    /**
     * Encodes a frame into one or more packets
     *
     * Processes the input frame through the encoder context and produces an array
     * of encoded packets. If frame is null, it will flush the encoder.
     *
     * @param FrameInterface|null $frame The frame to encode, or null to flush the encoder
     *
     * @return array An array of Packet objects
     * @throws RuntimeException If an error occurs during encoding
     */
    public function encode(?FrameInterface $frame): array
    {
        $frames = $this->prepareFrame($frame);
        $packets = [];

        foreach ($frames as $frame) {
            foreach ($this->sendFrameAndReceive($frame) as $packet) {
                $contextTimeBase = $this->context->getTimeBase();
                $packet->setTimeBase($contextTimeBase->num, $contextTimeBase->den);
                $packets [] = $packet;
            }
        }

        return $packets;
    }

    /**
     * Sends a frame to the encoder and receives any resulting packets
     *
     * This is a generator method that yields packets as they become available
     * from the encoder.
     *
     * @param FrameInterface|null $frame The frame to encode, or null to flush the encoder
     *
     * @return \Generator A generator yielding Packet objects
     * @throws RuntimeException If an error occurs during encoding
     */
    public function sendFrameAndReceive(?FrameInterface $frame): \Generator
    {
        $res = $this->libAVCodec->avcodec_send_frame($this->context->getContext(), $frame?->getFrame());
        if ($res < 0) {
            throw new RuntimeException("An error occurred while encoding");
        }

        $packet = $this->recvPacket();
        while ($packet) {
            yield $packet;
            $packet = $this->recvPacket();
        }
    }

    /**
     * Receives a packet from the encoder
     *
     * @return Packet|null A packet if available, or null if no more packets are available
     */
    private function recvPacket(): ?Packet
    {
        $packet = new Packet();
        $res = $this->libAVCodec->avcodec_receive_packet($this->context->getContext(), $packet->getPacket());

        if ($res === -EAGAIN || $res === AVERROR_EOF) {
            return null;
        }
        if ($res === 0) {
            return $packet;
        }
        return null;
    }

    /**
     * Decodes a packet into one or more frames
     *
     * Processes the input packet through the decoder context and produces
     * an array of decoded frames. If packet is null, it will flush the decoder.
     *
     * @param Packet|null $packet The packet to decode, or null to flush the decoder
     *
     * @return array An array of FrameInterface objects
     * @throws InvalidArgumentException If the codec is unknown or not properly initialized
     * @throws RuntimeException If an error occurs during decoding
     */
    public function decode(?Packet $packet): array
    {
        if (!$this->context->getContext()) {
            throw new InvalidArgumentException("Cannot decode unknown codec");
        }

        $this->context->open(false);

        $frames = [];
        foreach ($this->sendPacketAndGet($packet) as $frame) {
            if ($frame instanceof Frame) {
                $packetBaseTime = $packet?->getTimeBase();
                if ($packetBaseTime && isset($packetBaseTime->num) && isset($packetBaseTime->den)) {
                    $frame->setTimeBase($packetBaseTime->num, $packetBaseTime->den);
                }
            }
            $frames[] = $frame;
        }

        return $frames;
    }

    /**
     * Send a packet and receive frames
     *
     * @param Packet|null $packet The packet to be sent, or null to flush
     *
     * @return array An array of received frames
     * @throws RuntimeException If an error occurs during decoding
     */
    private function sendPacketAndGet(?Packet $packet): array
    {
        $frames = [];

        $res = $this->libAVCodec->avcodec_send_packet($this->context->getContext(), $packet?->getPacket());
        if ($res < 0) {
            throw new RuntimeException("An error occurred while decoding");
        }

        // Receive frames in a loop
        while (true) {
            $frame = $this->getFrame();
            if ($frame) {
                $frames[] = $frame;
            } else {
                break;
            }
        }

        return $frames;
    }

    /**
     * Receive a frame from the codec
     *
     * @return FrameInterface|null Returns a Frame object if available, or null if no more frames are available
     * @throws RuntimeException If an error occurs during decoding
     */
    private function getFrame(): ?FrameInterface
    {
        // Allocate the next frame if not already allocated
        if ($this->nextFrame === null) {
            $this->nextFrame = $this->context->getNewFrame();
        }
        $frame = $this->nextFrame;

        // Receive the frame
        $res = $this->libAVCodec->avcodec_receive_frame($this->context->getContext(), $frame->getFrame());

        // Handle special return codes
        if ($res === -EAGAIN || $res === AVERROR_EOF) {
            return null;
        }

        // Check for other errors
        if ($res < 0) {
            throw new RuntimeException("An error occurred while decoding");
        }

        // If successful, clear the next frame and return the frame
        if ($res === 0) {
            $this->nextFrame = null;
            if ($this->context instanceof VideoContext) {
                $frame->setVideoFormat();
            }
            return $frame;
        }

        return null;
    }

    /**
     * Prepares frames for encoding
     *
     * Validates codec type, opens the context if needed, and prepares
     * the frames with proper time base settings.
     *
     * @param FrameInterface|null $frame The frame to prepare, or null to flush
     *
     * @return array An array of prepared frames ready for encoding
     * @throws RuntimeException If encoding is not supported for the codec type
     */
    private function prepareFrame(?FrameInterface $frame): array
    {
        // Check if codec_type is supported
        if (!in_array($this->context->getContext()->codec_type, [0, 1])) {
            throw new RuntimeException("Encoding is only supported for audio and video.");
        }
        $this->context->open(false);

        $frames = $this->context->prepareFramesForEncode($frame);

        foreach ($frames as $frame) {
            $frame?->rebaseTime($this->context->getContext()->time_base);
        }

        return $frames;
    }

    /**
     * Initializes the AVCodec shared library
     *
     * This method implements the SharedLibraryInterface requirement.
     * It uses a global FFI instance of the AVCodec library if available.
     *
     * @return void
     */
    public function initiateSharedLibrary(): void
    {
        global $libAVCodec;

        if ($libAVCodec instanceof FFI) {
            $this->libAVCodec = $libAVCodec;
        }
    }

    /**
     * @return array{context: ContextInterface, nextFrame: FrameInterface|null}
     */
    public function __serialize(): array
    {
        return [
            'context' => $this->context,
            'nextFrame' => $this->nextFrame,
        ];
    }

    /**
     * @param array{context?: ContextInterface, nextFrame?: FrameInterface|null} $data
     */
    public function __unserialize(array $data): void
    {
        $this->initiateSharedLibrary();
        $this->context = $data['context'];
        $this->nextFrame = $data['nextFrame'] ?? null;
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\AVCodec\Util;

use FFI\CData;
use Webrtc\Exception\InvalidArgumentException;

/**
 * Util Class
 *
 * This class provides utility functions for rescaling values between different
 * time bases, similar to the av_rescale functions in FFmpeg's libavutil.
 */
class Util
{
    /**
     * Rescales a 64-bit integer with specified rounding
     *
     * Rescales the value a from a scale with size b to a scale with size c,
     * applying the specified rounding method.
     *
     * The formula is:  a * b / c
     *
     * @param int $a Input value to rescale
     * @param int $b Input scale
     * @param int $c Output scale
     * @param int $rnd Rounding method (one of the AV_ROUND_* constants)
     *
     * @return int The rescaled value
     * @throws InvalidArgumentException When input parameters are invalid
     */
    public static function rescaleRnd(int $a, int $b, int $c, int $rnd): int
    {
        $r = 0;

        // Assertions
        if ($c <= 0 || $b < 0 || !((($rnd & ~AV_ROUND_PASS_MINMAX) <= 5) && (($rnd & ~AV_ROUND_PASS_MINMAX) != 4))) {
            throw new InvalidArgumentException("Invalid parameters for rescaling.");
        }

        if ($rnd & AV_ROUND_PASS_MINMAX) {
            if ($a == INT64_MIN || $a == INT64_MAX) {
                return $a;
            }
            $rnd -= AV_ROUND_PASS_MINMAX;
        }

        if ($a < 0) {
            return -self::rescaleRnd(-max($a, INT64_MIN), $b, $c, $rnd ^ (($rnd >> 1) & 1));
        }

        if ($rnd == AV_ROUND_NEAR_INF) {
            $r = intval($c / 2);
        } else if ($rnd & 1) {
            $r = $c - 1;
        }

        if ($b <= PHP_INT_MAX && $c <= PHP_INT_MAX) {
            if ($a <= PHP_INT_MAX) {
                return intval(($a * $b + $r) / $c);
            } else {
                $ad = intval($a / $c);
                $a2 = intval(($a % $c * $b + $r) / $c);
                if ($ad >= (PHP_INT_MAX >> 1) && $b && $ad > (INT64_MAX - $a2) / $b) {
                    return INT64_MIN;
                }
                return intval($ad * $b + $a2);
            }
        } else {
            $a0 = $a & 0xFFFFFFFF;
            $a1 = $a >> 32;
            $b0 = $b & 0xFFFFFFFF;
            $b1 = $b >> 32;
            $t1 = $a0 * $b1 + $a1 * $b0;
            $t1a = $t1 << 32;
            $a0 = $a0 * $b0 + $t1a;
            $a1 = $a1 * $b1 + ($t1 >> 32) + ($a0 < $t1a);
            $a0 += $r;
            $a1 += $a0 < $r;

            for ($i = 63; $i >= 0; $i--) {
                $a1 = ($a1 << 1) + (($a0 >> $i) & 1);
                $t1 <<= 1;
                if ($c <= $a1) {
                    $a1 -= $c;
                    $t1++;
                }
            }
            if ($t1 > INT64_MAX) {
                return INT64_MIN;
            }
            return $t1;
        }
    }

    /**
     * Rescale a value using rational numbers with specified rounding
     *
     * Rescales the value a from time base bq to time base cq using the specified
     * rounding method.
     *
     * The formula is: a * bq->num * cq->den / (cq->num * bq->den)
     *
     * @param int $a Input value to rescale
     * @param \stdClass $bq Input time base (object with num and den properties)
     * @param CData $cq Output time base (FFI CData structure with num and den properties)
     * @param int $rnd Rounding method (one of the AV_ROUND_* constants)
     *
     * @return int The rescaled value
     */
    public static function rescaleQRnd(int $a, \stdClass $bq, CData $cq, int $rnd): int
    {
        $b = $bq->num * (int)$cq->den;
        $c = $cq->num * (int)$bq->den;
        return self::rescaleRnd($a, $b, $c, $rnd);
    }

    /**
     * Rescale a value using rational numbers with nearest rounding
     *
     * Convenience wrapper around rescaleQRnd that uses AV_ROUND_NEAR_INF
     * as the rounding method.
     *
     * @param int $a Input value to rescale
     * @param \stdClass $bq Input time base (object with num and den properties)
     * @param CData $cq Output time base (FFI CData structure with num and den properties)
     *
     * @return int The rescaled value
     */
    public static function rescaleQ(int $a, \stdClass $bq, CData $cq): int
    {
        return self::rescaleQRnd($a, $bq, $cq, AV_ROUND_NEAR_INF);
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\AVCodec\Context;

use FFI\CData;
use Webrtc\AVCodec\Audio\AudioLayout;
use Webrtc\AVCodec\Audio\AudioResampler;
use Webrtc\AVCodec\Codec;
use Webrtc\AVCodec\Exception\AvCodecException;
use Webrtc\AVCodec\Format\AudioFormat;
use Webrtc\AVCodec\Frame\AudioFrame;
use Webrtc\AVCodec\Frame\FrameInterface;


/**
 * Class AudioCodecContext
 *
 * Represents an audio codec context, providing functionality for audio encoding and decoding,
 * frame preparation, and audio property management. This class is an extension of the Context
 * class and integrates additional audio-specific features.
 *
 */
class AudioContext extends Context
{
    private ?AudioResampler $resampler = null;
    private AudioFormat $format;
    private AudioLayout $layout;

    /**
     * @param CData $context Pointer to the C AVCodec structure.
     * @param Codec $codec
     */
    public function __construct(CData $context, Codec $codec)
    {
        parent::__construct($context, $codec);
    }

    /**
     * Prepares audio frames for encoding.
     *
     * Resamples the input frame if needed, and flushes the resampler when the input frame is null.
     *
     * @param FrameInterface|AudioFrame|null $frame The input audio frame or null to flush the resampler.
     * @return array An array of resampled frames, or an array with a single null element if flushing.
     * @throws AvCodecException
     */
    public function prepareFramesForEncode(null|AudioFrame|FrameInterface $frame): array
    {
        $allowVarFrameSize = ($this->context->codec->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE) !== 0;

        if ($this->resampler === null) {
            $this->resampler = new AudioResampler(
                $this->getFormat(),
                $this->getLayout()->getName(),
                $this->context->sample_rate,
                $allowVarFrameSize ? null : $this->context->frame_size,
            );
        }

        return $this->resampler->resample($frame);
    }

    /**
     * Gets the frame size.
     *
     * The number of samples per channel in an audio frame.
     *
     * @return int The frame size.
     */
    public function getFrameSize(): int
    {
        return $this->context->frame_size;
    }

    /**
     * Gets the sample rate of the audio data.
     *
     * @return int The sample rate in samples per second.
     */
    public function getSampleRate(): int
    {
        return $this->context->sample_rate;
    }

    /**
     * Sets the sample rate of the audio data.
     *
     * @param int $value The sample rate in samples per second.
     */
    public function setSampleRate(int $value): void
    {
        $this->context->sample_rate = $value;
    }

    /**
     * @return int The number of channels.
     */
    public function getChannels(): int
    {
        return $this->getLayout()->getNbChannels();
    }

    /**
     * @return AudioLayout The channel layout.
     */
    public function getLayout(): AudioLayout
    {
        return $this->layout;
    }

    /**
     * @param string $value The channel layout to set.
     */
    public function setLayout(string $value): void
    {
        $this->layout = new AudioLayout($value);
        $this->context->ch_layout = $this->layout->getLayout();
    }

    /**
     * @return AudioFormat The audio sample format.
     */
    public function getFormat(): AudioFormat
    {
        return $this->format;
    }

    /**
     * @param mixed $value The sample format to set.
     */
    public function setFormat(mixed $value): void
    {
        $this->format = new AudioFormat($value);
        $this->context->sample_fmt = $this->format->getSampleFormat();
    }

    /**
     * @return FrameInterface create a copy of this object
     */
    public function getNewFrame(): FrameInterface
    {
        return new AudioFrame($this->format->getName(), $this->layout->getName(), $this->getSampleRate());
    }

    /**
     * @param AudioResampler|null $resampler
     * @return void
     */
    public function setResampler(?AudioResampler $resampler): void
    {
        $this->resampler = $resampler;
    }

    /**
     * @return array<string, mixed>
     */
    public function __serialize(): array
    {
        $data = $this->serializeContextState();
        $data['sample_rate'] = $this->context->sample_rate;
        $data['frame_size'] = $this->context->frame_size;
        if (isset($this->format)) {
            $data['format'] = $this->format->getName();
        }
        if (isset($this->layout)) {
            $data['layout'] = $this->layout->getName();
        }
        $data['resampler'] = $this->resampler;

        return $data;
    }

    /**
     * @param array<string, mixed> $data
     */
    public function __unserialize(array $data): void
    {
        $this->restoreContextState($data);
        if (isset($data['format']) && is_string($data['format'])) {
            $this->setFormat($data['format']);
        }
        if (isset($data['layout']) && is_string($data['layout'])) {
            $this->setLayout($data['layout']);
        }
        if (isset($data['sample_rate'])) {
            $this->setSampleRate((int) $data['sample_rate']);
        }
        $this->resampler = $data['resampler'] instanceof AudioResampler ? $data['resampler'] : null;
        if (!empty($data['open'])) {
            $this->open(false);
        }
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\AVCodec\Context;

use FFI\CData;
use Webrtc\AVCodec\Frame\FrameInterface;

interface ContextInterface
{
    public function open(bool $strict = true): void;

    public function getContext(): CData;

    public function prepareFramesForEncode(?FrameInterface $frame): array;

    public function getTimeBase(): CData;

    public function getNewFrame(): FrameInterface;
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\AVCodec\Context;

use FFI\CData;
use Webrtc\AVCodec\Codec;
use Webrtc\AVCodec\Exception\AvCodecException;
use Webrtc\AVCodec\Format\VideoFormat;
use Webrtc\AVCodec\Frame\FrameInterface;
use Webrtc\AVCodec\Frame\VideoFrame;
use Webrtc\Exception\InvalidArgumentException;
use Webrtc\Exception\RuntimeException;

/**
 * Video Context Class
 *
 * Extends the base Context class with video-specific functionality.
 * Handles video codec configuration, frame preparation, and video format management.
 *
 * @package Webrtc\AVCodec\Context
 */
class VideoContext extends Context
{
    private const AV_NOPTS_VALUE = -9223372036854775808;
    /**
     * @var VideoFormat|null $format Current video format configuration
     */
    private ?VideoFormat $format;

    /**
     * Constructor
     *
     * @param CData $context FFI CData representing AVCodecContext
     * @param Codec $codec Associated codec instance
     */
    public function __construct(CData $context, Codec $codec)
    {
        parent::__construct($context, $codec);
    }

    /**
     * Get the current video format
     *
     * @return VideoFormat Current video format
     */
    public function getFormat(): VideoFormat
    {
        return $this->format;
    }

    /**
     * Set video format
     *
     * Updates a pixel format, width, and height in one operation
     *
     * @param VideoFormat $format Video format to set
     */
    public function setFormat(VideoFormat $format): void
    {
        $this->context->pix_fmt = $format->getFormat();
        $this->context->width = $format->getWidth();
        $this->context->height = $format->getHeight();

        $this->format = $format;
    }

    /**
     * Get video width
     *
     * @return int Current width in pixels
     */
    public function getWidth(): int
    {
        return $this->context->width;
    }

    /**
     * Set video width
     *
     * @param int $width New width in pixels
     */
    public function setWidth(int $width): void
    {
        $this->context->width = $width;
    }

    /**
     * Get video height
     *
     * @return int Current height in pixels
     */
    public function getHeight(): int
    {
        return $this->context->height;
    }

    /**
     * Set video height
     *
     * @param int $height New height in pixels
     */
    public function setHeight(int $height): void
    {
        $this->context->height = $height;
    }

    /**
     * Get pixel format name
     *
     * @return string|null Name of current pixel format
     */
    public function getPixFormat(): ?string
    {
        return $this->format->getName();
    }

    /**
     * Set pixel format by name
     *
     * @param string $pixFormat Name of pixel format to set
     */
    public function setPixFormat(string $pixFormat): void
    {
        $this->context->pix_fmt = VideoFormat::getPixFmt($pixFormat);
        $this->format = new VideoFormat($this->context->pix_fmt, $this->context->width, $this->context->height);
    }

    /**
     * Set framerate
     *
     * @param int $numerator Framerate numerator
     * @param int $denominator Framerate denominator
     */
    public function setFramerate(int $numerator, int $denominator): void
    {
        $this->context->framerate->num = $numerator;
        $this->context->framerate->den = $denominator;
    }

    /**
     * Get framerate
     *
     * @return CData FFI CData representing the framerate
     */
    public function getFramerate(): CData
    {
        return $this->context->framerate;
    }

    /**
     * Prepare frames for encoding
     *
     * @param FrameInterface|null $frame Frame to prepare
     * @return array Array of prepared frames
     * @throws InvalidArgumentException If a format isn't set
     */
    public function prepareFramesForEncode(?FrameInterface $frame): array
    {
        if (!$this->format) {
            throw new InvalidArgumentException("The format has not been set yet");
        }

        if ($frame && $frame->getFrame()->pts <= self::AV_NOPTS_VALUE) {
            $frame->getFrame()->pts = $this->encodedFrameCount;
        }

        $this->encodedFrameCount += 1;

        return [$frame];
    }

    /**
     * Create a new empty video frame
     *
     * @return FrameInterface New video frame instance
     * @throws AvCodecException
     */
    public function getNewFrame(): FrameInterface
    {
        return new VideoFrame(0, 0);
    }

    /**
     * Get GOP (Group of Pictures) size
     *
     * @return int Number of frames between keyframes
     * @throws RuntimeException If accessed in decoder mode
     */
    public function getGopSize(): int
    {
        if ($this->codec->isDecoder()) {
            throw new RuntimeException("Cannot access 'gop_size' as a decoder");
        }

        return $this->context->gop_size;
    }

    /**
     * Set GOP (Group of Pictures) size
     *
     * @param int $value Number of frames between keyframes
     * @throws RuntimeException If modified in decoder mode
     */
    public function setGopSize(int $value): void
    {
        if ($this->codec->isDecoder()) {
            throw new RuntimeException("Cannot modify 'gop_size' as a decoder");
        }

        $this->context->gop_size = $value;
    }

    /**
     * @return array<string, mixed>
     */
    public function __serialize(): array
    {
        $data = $this->serializeContextState();
        $data['width'] = $this->context->width;
        $data['height'] = $this->context->height;
        $data['pix_fmt'] = $this->context->pix_fmt;
        $data['gop_size'] = $this->context->gop_size;
        $data['framerate'] = [$this->context->framerate->num, $this->context->framerate->den];
        $data['hasFormat'] = isset($this->format) && $this->format !== null;

        return $data;
    }

    /**
     * @param array<string, mixed> $data
     */
    public function __unserialize(array $data): void
    {
        $this->restoreContextState($data);
        if (isset($data['width'])) {
            $this->setWidth((int) $data['width']);
        }
        if (isset($data['height'])) {
            $this->setHeight((int) $data['height']);
        }
        if (!empty($data['hasFormat']) && isset($data['pix_fmt'])) {
            $this->format = new VideoFormat((int) $data['pix_fmt'], $this->context->width, $this->context->height);
            $this->context->pix_fmt = (int) $data['pix_fmt'];
        }
        if (isset($data['gop_size']) && !$this->codec->isDecoder()) {
            $this->setGopSize((int) $data['gop_size']);
        }
        if (isset($data['framerate']) && is_array($data['framerate'])) {
            $this->setFramerate((int) $data['framerate'][0], (int) $data['framerate'][1]);
        }
        if (!empty($data['open'])) {
            $this->open(false);
        }
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\AVCodec\Context;

use ArrayIterator;
use FFI;
use FFI\CData;
use Traversable;
use Webrtc\Exception\InvalidArgumentException;
use Webrtc\Mixin\SharedLibraryInterface;

/**
 * AVCodec Dictionary Class
 *
 * This class provides a PHP interface to AVDictionary using FFI.
 * It allows manipulation of key-value pairs used for codec options and metadata.
 *
 * Implements Countable, IteratorAggregate for collection-like behavior.
 *
 * @package Webrtc\AVCodec\Context
 */
class Dictionary implements SharedLibraryInterface, \Countable, \IteratorAggregate
{

    private const IGNORE_SUFFIX = 2;
    private FFI $libAVCodec;

    /**
     * @var CData $dictionary FFI CData representing AVDictionary
     */
    private CData $dictionary;

    /**
     * Constructor - initializes the dictionary
     */
    public function __construct()
    {
        $this->initiateSharedLibrary();
        $this->dictionary = $this->libAVCodec->new("AVDictionary*");
    }

    /**
     * Magic setter - allows property-style assignment
     *
     * @param string $name Key name
     * @param mixed $value Value to set
     * @throws InvalidArgumentException If setting fails
     */
    public function __set(string $name, $value): void
    {
        $this->set($name, $value);
    }

    /**
     * Magic getter - allows property-style access
     *
     * @param string $name Key name
     * @return string The value
     * @throws InvalidArgumentException If key doesn't exist
     */
    public function __get(string $name)
    {
        $element = $this->libAVCodec->av_dict_get($this->dictionary, $name, null, 0);
        return $element != null ? FFI::string($element->value) : throw new \InvalidArgumentException("there is no value with the key " . $name);
    }

    /**
     * Invoke as function - returns the raw CData dictionary
     *
     * @return CData|null The FFI CData AVDictionary
     */
    public function __invoke(): ?CData
    {
        return $this->dictionary;
    }

    /**
     * Magic isset - checks if key exists
     *
     * @param string $name Key name
     * @return bool True if key exists
     */
    public function __isset(string $name): bool
    {
        $element = $this->libAVCodec->av_dict_get($this->dictionary, $name, null, 0);
        return $element !== null;
    }

    /**
     * Magic unset - removes a key-value pair
     *
     * @param string $name Key name
     * @throws InvalidArgumentException If key doesn't exist
     */
    public function __unset(string $name): void
    {

        $element = $this->libAVCodec->av_dict_get($this->dictionary, $name, null, 0);
        if ($element !== null) {
            $this->libAVCodec->av_dict_set(FFI::addr($this->dictionary), $name, null, 0);
        }
        else{
            throw new InvalidArgumentException("Couldn't set the option");
        }
    }

    /**
     * Update dictionary with multiple key-value pairs
     *
     * @param array $options Associative array of options
     */
    public function update(array $options): void
    {
        foreach ($options as $key => $value) {
            $this->set($key, $value);
        }
    }

    /**
     * Destructor - free dictionary memory
     */
    public function __destruct()
    {
        if ($this->dictionary != null) {
            $this->libAVCodec->av_dict_free(FFI::addr($this->dictionary));
        }
    }

    /**
     * Initialize shared library instance
     */
    public function initiateSharedLibrary(): void
    {
        global $libAVCodec;

        if ($libAVCodec instanceof FFI) {
            $this->libAVCodec = $libAVCodec;
        }
    }

    /**
     * Count elements - the number of returns of dictionary entries
     *
     * @return int Number of entries
     */
    public function count(): int
    {
        return $this->libAVCodec->av_dict_count($this->dictionary);
    }

    /**
     * Get iterator for foreach support
     *
     * @return Traversable Iterator for dictionary entries
     */
    public function getIterator(): Traversable
    {
        $elements = [];
        while (true) {
            $element = $this->libAVCodec->av_dict_get($this->dictionary, "", null, self::IGNORE_SUFFIX);

            if ($element !== null) {
                $elements [FFI::string($element->key)] = FFI::string($element->value);
                break;
            } else {
                break;
            }
        }

        return new ArrayIterator($elements);
    }

    /**
     * Set a key-value pair
     *
     * @param string $name Key name
     * @param mixed $value Value to set
     * @throws InvalidArgumentException If setting fails
     */
    private function set(string $name, mixed $value): void
    {
        $res = $this->libAVCodec->av_dict_set(FFI::addr($this->dictionary), $name, (string)$value, 0);

        if ($res < 0) {
            throw new \InvalidArgumentException("Couldn't set the option" . $name . ": " . $value);
        }
    }

    /**
     * Convert dictionary to a PHP array
     *
     * @return array Associative array of all key-value pairs
     */
    public function toArray(): array
    {
        return iterator_to_array($this->getIterator());
    }

    /**
     * @return array<string, string>
     */
    public function __serialize(): array
    {
        $pairs = [];
        $prev = null;
        while (true) {
            $element = $this->libAVCodec->av_dict_get($this->dictionary, "", $prev, self::IGNORE_SUFFIX);
            if ($element === null) {
                break;
            }
            $pairs[FFI::string($element->key)] = FFI::string($element->value);
            $prev = $element;
        }

        return $pairs;
    }

    /**
     * @param array<string, string> $data
     */
    public function __unserialize(array $data): void
    {
        $this->__construct();
        $this->update($data);
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\AVCodec\Context;

use FFI;
use FFI\CData;
use Webrtc\AVCodec\Codec;
use Webrtc\Exception\InvalidArgumentException;
use Webrtc\Exception\RuntimeException;
use Webrtc\Mixin\SharedLibraryInterface;

/**
 * AVCodec Context Class
 *
 * This class provides an abstraction layer for AVCodecContext using PHP FFI.
 * It handles the creation, configuration, and management of codec contexts
 * for both encoding and decoding operations.
 *
 * @package Webrtc\AVCodec\Context
 */
abstract class Context implements SharedLibraryInterface, ContextInterface
{
    /**
     * @var FFI $libAVCodec FFI instance for AVCodec library
     */
    protected FFI $libAVCodec;

    /**
     * @var array $options Codec context options
     */
    private array $options = [];

    /**
     * @var bool $extraDataSet Flag indicating if extradata is set
     */
    private bool $extraDataSet = false;

    /**
     * @var int $encodedFrameCount Counter for encoded frames
     */
    protected int $encodedFrameCount = 0;

    /**
     * Constructor
     *
     * @param CData $context FFI CData representing AVCodecContext
     * @param Codec $codec Associated codec instance
     */
    public function __construct(protected CData $context, protected Codec $codec)
    {
        $this->initiateSharedLibrary();
        $this->context->thread_count = 0; // Use as many threads as there are CPUs
        $this->context->thread_type = 2; // Thread within a frame
    }

    /**
     * Create a new context instance
     *
     * @param Codec $codec The codec to create context for
     * @return Context|VideoContext|AudioContext|null The created context instance
     */
    public static function create(Codec $codec): Context|VideoContext|AudioContext|null
    {
        global $libAVCodec;

        if ($libAVCodec instanceof FFI) {
            $ctx = $libAVCodec->avcodec_alloc_context3($codec->getCodec());
            return new static($ctx, $codec);
        }

        return null;
    }

    /**
     * Open the codec context
     *
     * @param bool $strict Whether to throw exceptions on errors
     * @throws RuntimeException If context is already open (when strict=true)
     * @throws RuntimeException If opening context fails
     */
    public function open(bool $strict = true): void
    {
        if ($this->libAVCodec->avcodec_is_open($this->context)) {
            if ($strict) {
                throw new RuntimeException("Context is already open.");
            }
            return;
        }

        $dictionary = new Dictionary;
        $dictionary->update($this->options);

        if ($this->context->time_base->num < 0 && $this->isEncoder()) {
            $this->setDefaultTimeBase();
        }
        $res = $this->libAVCodec->avcodec_open2($this->context, $this->codec->getCodec(), FFI::addr($dictionary()));

        if ($res < 0) {
            $errorMsgBuffer = $this->libAVCodec->new("char[1024]");
            $this->libAVCodec->av_strerror($res, $errorMsgBuffer, 1024);
            $errorMessage = FFI::string($errorMsgBuffer);
            throw new RuntimeException("Couldn't open the context: " . $errorMessage);
        }

//        foreach ($dictionary as $key => $value) {
//            $this->options[FFI::string($key)] = FFI::string($value);
//        }
    }

    /**
     * Close the codec context
     *
     * @param bool $strict Whether to throw exceptions on errors
     * @throws InvalidArgumentException If context is already closed (when strict=true)
     * @throws RuntimeException If closing context fails
     */
    public function close($strict = true): void
    {
        if (!$this->libAVCodec->avcodec_is_open($this->context)) {
            if ($strict) {
                throw new \InvalidArgumentException("CodecContext is already closed.");
            }
            return;
        }
        $res = $this->libAVCodec->avcodec_free_context(FFI::addr($this->context));

        if ($res < 0) {
            throw new RuntimeException("Couldn't close the context");
        }
    }

    /**
     * Destructor - cleans up resources
     */
    public function __destruct()
    {
        if ($this->context && $this->extraDataSet) {
            $this->libAVCodec->av_freep(FFI::addr($this->context->extradata));
        }
        if ($this->context) {
            $this->libAVCodec->avcodec_free_context(FFI::addr($this->context));
        }
    }

    /**
     * Check if context is for encoding
     *
     * @return bool True if context is for encoding, false for decoding
     */
    public function isEncoder(): bool
    {
        return (bool)$this->libAVCodec->av_codec_is_encoder($this->context->codec);
    }

    /**
     * Initialize shared library instance
     */
    public function initiateSharedLibrary(): void
    {
        global $libAVCodec;

        if ($libAVCodec instanceof FFI) {
            $this->libAVCodec = $libAVCodec;
        }
    }

    /**
     * Set default the time base for the context
     */
    private function setDefaultTimeBase(): void
    {
        $this->context->time_base->num = 1;
        $this->context->time_base->den = 1000000;
    }

    /**
     * Set the bitrate for the context
     *
     * @param int $bitrate The bitrate in bits per second
     */
    public function setBitRate(int $bitrate): void
    {
        $this->context->bit_rate = $bitrate;
    }

    /**
     * Get the current bitrate
     *
     * @return int The current bitrate in bits per second
     */
    public function getBitrate(): int
    {
        return $this->context->bit_rate;
    }

    /**
     * Set the time base for the context
     *
     * @param int $numerator Time base numerator
     * @param int $denominator Time base denominator
     * @throws RuntimeException If called on a decoder context
     */
    public function setTimeBase(int $numerator, int $denominator): void
    {
        if ($this->codec->isDecoder()) {
            throw new RuntimeException("Cannot access 'gop_size' as a decoder");
        }
        $this->context->time_base->num = $numerator;
        $this->context->time_base->den = $denominator;
    }

    /**
     * Get the current time base
     *
     * @return CData FFI CData representing the time base
     * @throws RuntimeException If called on a decoder context
     */
    public function getTimeBase(): CData
    {
        if ($this->codec->isDecoder()) {
            throw new RuntimeException("Cannot access 'gop_size' as a decoder");
        }

        return $this->context->time_base;
    }

    /**
     * Get all context options
     *
     * @return array Current context options
     */
    public function getOptions(): array
    {
        return $this->options;
    }

    /**
     * Set context options
     *
     * @param array $options New options to set
     */
    public function setOptions(array $options): void
    {
        $this->options = array_merge($options, $this->options);
    }

    /**
     * Get the raw FFI context object
     *
     * @return CData The FFI CData AVCodecContext
     */
    public function getContext(): CData
    {
        return $this->context;
    }

    public function profile(int $profile): void
    {
        $this->context->profile = $profile;
    }

    /**
     * Get the current skip frame setting as a human-readable string
     *
     * Possible return values:
     * - "NONE"      Discard nothing
     * - "DEFAULT"   Discard useless packets like 0 size packets in AVI
     * - "NONREF"    Discard all non-reference frames
     * - "BIDIR"     Discard all bidirectional frames
     * - "NONINTRA"  Discard all non-intra frames
     * - "NONKEY"    Discard all frames except keyframes
     * - "ALL"       Discard all
     *
     * @return string Current skip frame setting
     */
    public function getSkipFrame(): string
    {
        $value = $this->context->skip_frame;

        return match ($value) {
            $this->libAVCodec->AVDISCARD_NONE => "NONE",
            $this->libAVCodec->AVDISCARD_DEFAULT => "DEFAULT",
            $this->libAVCodec->AVDISCARD_NONREF => "NONREF",
            $this->libAVCodec->AVDISCARD_BIDIR => "BIDIR",
            $this->libAVCodec->AVDISCARD_NONINTRA => "NONINTRA",
            $this->libAVCodec->AVDISCARD_NONKEY => "NONKEY",
            $this->libAVCodec->AVDISCARD_ALL => "ALL",
            default => (string)$value,
        };
    }

    /**
     * Get the codec tag as a 4-character string
     *
     * @return string The codec tag
     */
    public function getCodecTag(): string
    {
        return pack("V", $this->context->codec_tag);
    }

    /**
     * Set the codec tag
     *
     * @param string $value 4-character codec tag string
     * @throws InvalidArgumentException If tag is not 4 characters
     */
    public function setCodecTag(string $value): void
    {
        if (strlen($value) !== 4) {
            throw new InvalidArgumentException("Codec tag should be a 4-character string.");
        }

        $this->context->codec_tag = unpack("V", $value)[1];
    }

    /**
     * Get the size of extradata
     *
     * @return int Size of extradata in bytes
     */
    public function getExtradataSize(): int
    {
        return $this->context->extradata_size;
    }

    /**
     * Get the extradata
     *
     * @return string|null The extradata or null if not set
     */
    public function getExtradata(): ?string
    {
        if ($this->context->extradata === null) {
            return null;
        }

        if ($this->context->extradata_size > 0) {
            return FFI::string($this->context->extradata, $this->context->extradata_size);
        }

        return null;
    }

    /**
     * Set the extradata
     *
     * @param string|null $data The extradata to set or null to clear
     * @throws RuntimeException If memory allocation fails
     */
    public function setExtradata(?string $data): void
    {
        if ($data === null) {
            $this->libAVCodec->av_freep(FFI::addr($this->context->extradata));
            $this->context->extradata_size = 0;
        } else {
            $length = strlen($data);
            $newMemory = $this->libAVCodec->av_realloc(
                $this->context->extradata,
                $length + 64 // AV_INPUT_BUFFER_PADDING_SIZE
            );

            if ($newMemory === null) {
                throw new RuntimeException("Cannot allocate extradata");
            }

            $this->context->extradata = $newMemory;
            FFI::memcpy($this->context->extradata, $data, $length);
            $this->context->extradata_size = $length;
        }
    }

    /**
     * @return array<string, mixed>
     */
    protected function serializeContextState(): array
    {
        return [
            'codecName' => $this->codec->getName(),
            'codecMode' => $this->codec->isEncoder() ? 'w' : 'r',
            'options' => $this->options,
            'encodedFrameCount' => $this->encodedFrameCount,
            'bitrate' => $this->context->bit_rate,
            'extradata' => $this->getExtradata(),
            'open' => (bool) $this->libAVCodec->avcodec_is_open($this->context),
            'codec_tag' => $this->context->codec_tag,
            'profile' => $this->context->profile,
            'time_base' => [$this->context->time_base->num, $this->context->time_base->den],
        ];
    }

    /**
     * @param array<string, mixed> $data
     */
    protected function restoreContextState(array $data): void
    {
        $this->initiateSharedLibrary();
        $this->codec = new Codec((string) ($data['codecName'] ?? ''), (string) ($data['codecMode'] ?? 'r'));
        $this->context = $this->libAVCodec->avcodec_alloc_context3($this->codec->getCodec());
        $this->context->thread_count = 0;
        $this->context->thread_type = 2;
        $this->options = is_array($data['options'] ?? null) ? $data['options'] : [];
        $this->encodedFrameCount = (int) ($data['encodedFrameCount'] ?? 0);
        $this->extraDataSet = false;
        if (isset($data['bitrate'])) {
            $this->setBitRate((int) $data['bitrate']);
        }
        if (isset($data['codec_tag'])) {
            $this->context->codec_tag = (int) $data['codec_tag'];
        }
        if (isset($data['profile'])) {
            $this->profile((int) $data['profile']);
        }
        if (isset($data['time_base']) && is_array($data['time_base']) && !$this->codec->isDecoder()) {
            $this->setTimeBase((int) $data['time_base'][0], (int) $data['time_base'][1]);
        }
        if (array_key_exists('extradata', $data)) {
            $this->setExtradata(is_string($data['extradata']) ? $data['extradata'] : null);
        }
    }

    /**
     * @return array<string, mixed>
     */
    public function __serialize(): array
    {
        return $this->serializeContextState();
    }

    /**
     * @param array<string, mixed> $data
     */
    public function __unserialize(array $data): void
    {
        $this->restoreContextState($data);
        if (!empty($data['open'])) {
            $this->open(false);
        }
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\AVCodec\Enum;
enum PictureType: int
{
    case NONE = 0; // Undefined
    case I = 1; // Intra
    case P = 2; // Predicted
    case B = 3; // Bi-dir predicted
    case S = 4; // S(GMC)-VOP MPEG-4
    case SI = 5; // Switching Intra
    case SP = 6; // Switching Predicted
    case BI = 7; // BI type
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\AVCodec\Enum;
enum Colorspace: int {
    case ITU709 = 11;
    case FCC = 12;
    case ITU601 = 13;
    case ITU624 = 14;
    case SMPTE170M = 15;
    case SMPTE240M = 16;
    case DEFAULT = 17;
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\AVCodec\Enum;

enum Interpolation: int {
    case FAST_BILINEAR = 1;
    case BILINEAR = 2;
    case BICUBIC = 3;
    case X = 4;
    case POINT = 5;
    case AREA = 6;
    case BICUBLIN = 7;
    case GAUSS = 8;
    case SINC = 9;
    case LANCZOS = 10;
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\AVCodec\Enum;

enum ColorRange: int {
    case UNSPECIFIED = 18;
    case MPEG = 19;
    case JPEG = 20;
    case NB = 21;
}// Enums
enum AVColorRange {
    AVCOL_RANGE_UNSPECIFIED = 0,
    AVCOL_RANGE_MPEG        = 1,
    AVCOL_RANGE_JPEG        = 2,
    AVCOL_RANGE_NB
};

enum AVPictureType {
    AV_PICTURE_TYPE_NONE = 0,
    AV_PICTURE_TYPE_I,
    AV_PICTURE_TYPE_P,
    AV_PICTURE_TYPE_B,
    AV_PICTURE_TYPE_S,
    AV_PICTURE_TYPE_SI,
    AV_PICTURE_TYPE_SP,
    AV_PICTURE_TYPE_BI,
};

enum AVMediaType {
    AVMEDIA_TYPE_UNKNOWN = -1,
    AVMEDIA_TYPE_VIDEO,
    AVMEDIA_TYPE_AUDIO,
    AVMEDIA_TYPE_DATA,
    AVMEDIA_TYPE_SUBTITLE,
    AVMEDIA_TYPE_ATTACHMENT,
    AVMEDIA_TYPE_NB
};

enum AVColorSpace {
    AVCOL_SPC_RGB         = 0,
    AVCOL_SPC_BT709       = 1,
    AVCOL_SPC_UNSPECIFIED = 2,
    AVCOL_SPC_RESERVED    = 3,
    AVCOL_SPC_FCC         = 4,
    AVCOL_SPC_BT470BG     = 5,
    AVCOL_SPC_SMPTE170M   = 6,
    AVCOL_SPC_SMPTE240M   = 7,
    AVCOL_SPC_YCGCO       = 8,
    AVCOL_SPC_YCOCG       = AVCOL_SPC_YCGCO,
    AVCOL_SPC_BT2020_NCL  = 9,
    AVCOL_SPC_BT2020_CL   = 10,
    AVCOL_SPC_SMPTE2085   = 11,
    AVCOL_SPC_CHROMA_DERIVED_NCL = 12,
    AVCOL_SPC_CHROMA_DERIVED_CL = 13,
    AVCOL_SPC_ICTCP       = 14,
    AVCOL_SPC_IPT_C2      = 15,
    AVCOL_SPC_YCGCO_RE    = 16,
    AVCOL_SPC_YCGCO_RO    = 17,
    AVCOL_SPC_NB
};

enum AVChannelOrder {
    AV_CHANNEL_ORDER_UNSPEC,
    AV_CHANNEL_ORDER_NATIVE,
    AV_CHANNEL_ORDER_CUSTOM,
    AV_CHANNEL_ORDER_AMBISONIC,
    FF_CHANNEL_ORDER_NB
};

enum AVChannel {
    AV_CHAN_NONE = -1,
    AV_CHAN_FRONT_LEFT,
    AV_CHAN_FRONT_RIGHT,
    AV_CHAN_FRONT_CENTER,
    AV_CHAN_LOW_FREQUENCY,
    AV_CHAN_BACK_LEFT,
    AV_CHAN_BACK_RIGHT,
    AV_CHAN_FRONT_LEFT_OF_CENTER,
    AV_CHAN_FRONT_RIGHT_OF_CENTER,
    AV_CHAN_BACK_CENTER,
    AV_CHAN_SIDE_LEFT,
    AV_CHAN_SIDE_RIGHT,
    AV_CHAN_TOP_CENTER,
    AV_CHAN_TOP_FRONT_LEFT,
    AV_CHAN_TOP_FRONT_CENTER,
    AV_CHAN_TOP_FRONT_RIGHT,
    AV_CHAN_TOP_BACK_LEFT,
    AV_CHAN_TOP_BACK_CENTER,
    AV_CHAN_TOP_BACK_RIGHT,
    AV_CHAN_STEREO_LEFT = 29,
    AV_CHAN_STEREO_RIGHT,
    AV_CHAN_WIDE_LEFT,
    AV_CHAN_WIDE_RIGHT,
    AV_CHAN_SURROUND_DIRECT_LEFT,
    AV_CHAN_SURROUND_DIRECT_RIGHT,
    AV_CHAN_LOW_FREQUENCY_2,
    AV_CHAN_TOP_SIDE_LEFT,
    AV_CHAN_TOP_SIDE_RIGHT,
    AV_CHAN_BOTTOM_FRONT_CENTER,
    AV_CHAN_BOTTOM_FRONT_LEFT,
    AV_CHAN_BOTTOM_FRONT_RIGHT,
    AV_CHAN_SIDE_SURROUND_LEFT,
    AV_CHAN_SIDE_SURROUND_RIGHT,
    AV_CHAN_TOP_SURROUND_LEFT,
    AV_CHAN_TOP_SURROUND_RIGHT,
    AV_CHAN_UNUSED = 0x200,
    AV_CHAN_UNKNOWN = 0x300,
    AV_CHAN_AMBISONIC_BASE = 0x400,
    AV_CHAN_AMBISONIC_END  = 0x7ff,
};

enum AVFrameSideDataType {
    AV_FRAME_DATA_PANSCAN,
    AV_FRAME_DATA_A53_CC,
    AV_FRAME_DATA_STEREO3D,
    AV_FRAME_DATA_MATRIXENCODING,
    AV_FRAME_DATA_DOWNMIX_INFO,
    AV_FRAME_DATA_REPLAYGAIN,
    AV_FRAME_DATA_DISPLAYMATRIX,
    AV_FRAME_DATA_AFD,
    AV_FRAME_DATA_MOTION_VECTORS,
    AV_FRAME_DATA_SKIP_SAMPLES,
    AV_FRAME_DATA_AUDIO_SERVICE_TYPE,
    AV_FRAME_DATA_MASTERING_DISPLAY_METADATA,
    AV_FRAME_DATA_GOP_TIMECODE,
    AV_FRAME_DATA_SPHERICAL,
    AV_FRAME_DATA_CONTENT_LIGHT_LEVEL,
    AV_FRAME_DATA_ICC_PROFILE,
    AV_FRAME_DATA_S12M_TIMECODE,
    AV_FRAME_DATA_DYNAMIC_HDR_PLUS,
    AV_FRAME_DATA_REGIONS_OF_INTEREST,
    AV_FRAME_DATA_VIDEO_ENC_PARAMS,
    AV_FRAME_DATA_SEI_UNREGISTERED,
    AV_FRAME_DATA_FILM_GRAIN_PARAMS,
    AV_FRAME_DATA_DETECTION_BBOXES,
    AV_FRAME_DATA_DOVI_RPU_BUFFER,
    AV_FRAME_DATA_DOVI_METADATA,
    AV_FRAME_DATA_DYNAMIC_HDR_VIVID,
    AV_FRAME_DATA_AMBIENT_VIEWING_ENVIRONMENT,
    AV_FRAME_DATA_VIDEO_HINT,
    AV_FRAME_DATA_LCEVC,
    AV_FRAME_DATA_VIEW_ID,
};

enum AVColorPrimaries {
    AVCOL_PRI_RESERVED0   = 0,
    AVCOL_PRI_BT709       = 1,
    AVCOL_PRI_UNSPECIFIED = 2,
    AVCOL_PRI_RESERVED    = 3,
    AVCOL_PRI_BT470M      = 4,
    AVCOL_PRI_BT470BG     = 5,
    AVCOL_PRI_SMPTE170M   = 6,
    AVCOL_PRI_SMPTE240M   = 7,
    AVCOL_PRI_FILM        = 8,
    AVCOL_PRI_BT2020      = 9,
    AVCOL_PRI_SMPTE428    = 10,
    AVCOL_PRI_SMPTEST428_1 = AVCOL_PRI_SMPTE428,
    AVCOL_PRI_SMPTE431    = 11,
    AVCOL_PRI_SMPTE432    = 12,
    AVCOL_PRI_EBU3213     = 22,
    AVCOL_PRI_JEDEC_P22   = AVCOL_PRI_EBU3213,
    AVCOL_PRI_NB
};

enum AVColorTransferCharacteristic {
    AVCOL_TRC_RESERVED0    = 0,
    AVCOL_TRC_BT709        = 1,
    AVCOL_TRC_UNSPECIFIED  = 2,
    AVCOL_TRC_RESERVED     = 3,
    AVCOL_TRC_GAMMA22      = 4,
    AVCOL_TRC_GAMMA28      = 5,
    AVCOL_TRC_SMPTE170M    = 6,
    AVCOL_TRC_SMPTE240M    = 7,
    AVCOL_TRC_LINEAR       = 8,
    AVCOL_TRC_LOG          = 9,
    AVCOL_TRC_LOG_SQRT     = 10,
    AVCOL_TRC_IEC61966_2_4 = 11,
    AVCOL_TRC_BT1361_ECG   = 12,
    AVCOL_TRC_IEC61966_2_1 = 13,
    AVCOL_TRC_BT2020_10    = 14,
    AVCOL_TRC_BT2020_12    = 15,
    AVCOL_TRC_SMPTE2084    = 16,
    AVCOL_TRC_SMPTEST2084  = AVCOL_TRC_SMPTE2084,
    AVCOL_TRC_SMPTE428     = 17,
    AVCOL_TRC_SMPTEST428_1 = AVCOL_TRC_SMPTE428,
    AVCOL_TRC_ARIB_STD_B67 = 18,
    AVCOL_TRC_NB
};

enum AVChromaLocation {
    AVCHROMA_LOC_UNSPECIFIED = 0,
    AVCHROMA_LOC_LEFT        = 1,
    AVCHROMA_LOC_CENTER      = 2,
    AVCHROMA_LOC_TOPLEFT     = 3,
    AVCHROMA_LOC_TOP         = 4,
    AVCHROMA_LOC_BOTTOMLEFT  = 5,
    AVCHROMA_LOC_BOTTOM      = 6,
    AVCHROMA_LOC_NB
};

enum AVPixelFormat {
    AV_PIX_FMT_NONE = -1,
    AV_PIX_FMT_YUV420P,
    AV_PIX_FMT_YUYV422,
    AV_PIX_FMT_RGB24,
    AV_PIX_FMT_BGR24,
    AV_PIX_FMT_YUV422P,
    AV_PIX_FMT_YUV444P,
    AV_PIX_FMT_YUV410P,
    AV_PIX_FMT_YUV411P,
    AV_PIX_FMT_GRAY8,
    AV_PIX_FMT_MONOWHITE,
    AV_PIX_FMT_MONOBLACK,
    AV_PIX_FMT_PAL8,
    AV_PIX_FMT_YUVJ420P,
    AV_PIX_FMT_YUVJ422P,
    AV_PIX_FMT_YUVJ444P,
    AV_PIX_FMT_UYVY422,
    AV_PIX_FMT_UYYVYY411,
    AV_PIX_FMT_BGR8,
    AV_PIX_FMT_BGR4,
    AV_PIX_FMT_BGR4_BYTE,
    AV_PIX_FMT_RGB8,
    AV_PIX_FMT_RGB4,
    AV_PIX_FMT_RGB4_BYTE,
    AV_PIX_FMT_NV12,
    AV_PIX_FMT_NV21,
    AV_PIX_FMT_ARGB,
    AV_PIX_FMT_RGBA,
    AV_PIX_FMT_ABGR,
    AV_PIX_FMT_BGRA,
    AV_PIX_FMT_GRAY16BE,
    AV_PIX_FMT_GRAY16LE,
    AV_PIX_FMT_YUV440P,
    AV_PIX_FMT_YUVJ440P,
    AV_PIX_FMT_YUVA420P,
    AV_PIX_FMT_RGB48BE,
    AV_PIX_FMT_RGB48LE,
    AV_PIX_FMT_RGB565BE,
    AV_PIX_FMT_RGB565LE,
    AV_PIX_FMT_RGB555BE,
    AV_PIX_FMT_RGB555LE,
    AV_PIX_FMT_BGR565BE,
    AV_PIX_FMT_BGR565LE,
    AV_PIX_FMT_BGR555BE,
    AV_PIX_FMT_BGR555LE,
    AV_PIX_FMT_VAAPI,
    AV_PIX_FMT_YUV420P16LE,
    AV_PIX_FMT_YUV420P16BE,
    AV_PIX_FMT_YUV422P16LE,
    AV_PIX_FMT_YUV422P16BE,
    AV_PIX_FMT_YUV444P16LE,
    AV_PIX_FMT_YUV444P16BE,
    AV_PIX_FMT_DXVA2_VLD,
    AV_PIX_FMT_RGB444LE,
    AV_PIX_FMT_RGB444BE,
    AV_PIX_FMT_BGR444LE,
    AV_PIX_FMT_BGR444BE,
    AV_PIX_FMT_YA8,
    AV_PIX_FMT_Y400A = AV_PIX_FMT_YA8,
    AV_PIX_FMT_GRAY8A= AV_PIX_FMT_YA8,
    AV_PIX_FMT_BGR48BE,
    AV_PIX_FMT_BGR48LE,
    AV_PIX_FMT_YUV420P9BE,
    AV_PIX_FMT_YUV420P9LE,
    AV_PIX_FMT_YUV420P10BE,
    AV_PIX_FMT_YUV420P10LE,
    AV_PIX_FMT_YUV422P10BE,
    AV_PIX_FMT_YUV422P10LE,
    AV_PIX_FMT_YUV444P9BE,
    AV_PIX_FMT_YUV444P9LE,
    AV_PIX_FMT_YUV444P10BE,
    AV_PIX_FMT_YUV444P10LE,
    AV_PIX_FMT_YUV422P9BE,
    AV_PIX_FMT_YUV422P9LE,
    AV_PIX_FMT_GBRP,
    AV_PIX_FMT_GBR24P = AV_PIX_FMT_GBRP,
    AV_PIX_FMT_GBRP9BE,
    AV_PIX_FMT_GBRP9LE,
    AV_PIX_FMT_GBRP10BE,
    AV_PIX_FMT_GBRP10LE,
    AV_PIX_FMT_GBRP16BE,
    AV_PIX_FMT_GBRP16LE,
    AV_PIX_FMT_YUVA422P,
    AV_PIX_FMT_YUVA444P,
    AV_PIX_FMT_YUVA420P9BE,
    AV_PIX_FMT_YUVA420P9LE,
    AV_PIX_FMT_YUVA422P9BE,
    AV_PIX_FMT_YUVA422P9LE,
    AV_PIX_FMT_YUVA444P9BE,
    AV_PIX_FMT_YUVA444P9LE,
    AV_PIX_FMT_YUVA420P10BE,
    AV_PIX_FMT_YUVA420P10LE,
    AV_PIX_FMT_YUVA422P10BE,
    AV_PIX_FMT_YUVA422P10LE,
    AV_PIX_FMT_YUVA444P10BE,
    AV_PIX_FMT_YUVA444P10LE,
    AV_PIX_FMT_YUVA420P16BE,
    AV_PIX_FMT_YUVA420P16LE,
    AV_PIX_FMT_YUVA422P16BE,
    AV_PIX_FMT_YUVA422P16LE,
    AV_PIX_FMT_YUVA444P16BE,
    AV_PIX_FMT_YUVA444P16LE,
    AV_PIX_FMT_VDPAU,
    AV_PIX_FMT_XYZ12LE,
    AV_PIX_FMT_XYZ12BE,
    AV_PIX_FMT_NV16,
    AV_PIX_FMT_NV20LE,
    AV_PIX_FMT_NV20BE,
    AV_PIX_FMT_RGBA64BE,
    AV_PIX_FMT_RGBA64LE,
    AV_PIX_FMT_BGRA64BE,
    AV_PIX_FMT_BGRA64LE,
    AV_PIX_FMT_YVYU422,
    AV_PIX_FMT_YA16BE,
    AV_PIX_FMT_YA16LE,
    AV_PIX_FMT_GBRAP,
    AV_PIX_FMT_GBRAP16BE,
    AV_PIX_FMT_GBRAP16LE,
    AV_PIX_FMT_QSV,
    AV_PIX_FMT_MMAL,
    AV_PIX_FMT_D3D11VA_VLD,
    AV_PIX_FMT_CUDA,
    AV_PIX_FMT_0RGB,
    AV_PIX_FMT_RGB0,
    AV_PIX_FMT_0BGR,
    AV_PIX_FMT_BGR0,
    AV_PIX_FMT_YUV420P12BE,
    AV_PIX_FMT_YUV420P12LE,
    AV_PIX_FMT_YUV420P14BE,
    AV_PIX_FMT_YUV420P14LE,
    AV_PIX_FMT_YUV422P12BE,
    AV_PIX_FMT_YUV422P12LE,
    AV_PIX_FMT_YUV422P14BE,
    AV_PIX_FMT_YUV422P14LE,
    AV_PIX_FMT_YUV444P12BE,
    AV_PIX_FMT_YUV444P12LE,
    AV_PIX_FMT_YUV444P14BE,
    AV_PIX_FMT_YUV444P14LE,
    AV_PIX_FMT_GBRP12BE,
    AV_PIX_FMT_GBRP12LE,
    AV_PIX_FMT_GBRP14BE,
    AV_PIX_FMT_GBRP14LE,
    AV_PIX_FMT_YUVJ411P,
    AV_PIX_FMT_BAYER_BGGR8,
    AV_PIX_FMT_BAYER_RGGB8,
    AV_PIX_FMT_BAYER_GBRG8,
    AV_PIX_FMT_BAYER_GRBG8,
    AV_PIX_FMT_BAYER_BGGR16LE,
    AV_PIX_FMT_BAYER_BGGR16BE,
    AV_PIX_FMT_BAYER_RGGB16LE,
    AV_PIX_FMT_BAYER_RGGB16BE,
    AV_PIX_FMT_BAYER_GBRG16LE,
    AV_PIX_FMT_BAYER_GBRG16BE,
    AV_PIX_FMT_BAYER_GRBG16LE,
    AV_PIX_FMT_BAYER_GRBG16BE,
    AV_PIX_FMT_YUV440P10LE,
    AV_PIX_FMT_YUV440P10BE,
    AV_PIX_FMT_YUV440P12LE,
    AV_PIX_FMT_YUV440P12BE,
    AV_PIX_FMT_AYUV64LE,
    AV_PIX_FMT_AYUV64BE,
    AV_PIX_FMT_VIDEOTOOLBOX,
    AV_PIX_FMT_P010LE,
    AV_PIX_FMT_P010BE,
    AV_PIX_FMT_GBRAP12BE,
    AV_PIX_FMT_GBRAP12LE,
    AV_PIX_FMT_GBRAP10BE,
    AV_PIX_FMT_GBRAP10LE,
    AV_PIX_FMT_MEDIACODEC,
    AV_PIX_FMT_GRAY12BE,
    AV_PIX_FMT_GRAY12LE,
    AV_PIX_FMT_GRAY10BE,
    AV_PIX_FMT_GRAY10LE,
    AV_PIX_FMT_P016LE,
    AV_PIX_FMT_P016BE,
    AV_PIX_FMT_D3D11,
    AV_PIX_FMT_GRAY9BE,
    AV_PIX_FMT_GRAY9LE,
    AV_PIX_FMT_GBRPF32BE,
    AV_PIX_FMT_GBRPF32LE,
    AV_PIX_FMT_GBRAPF32BE,
    AV_PIX_FMT_GBRAPF32LE,
    AV_PIX_FMT_DRM_PRIME,
    AV_PIX_FMT_OPENCL,
    AV_PIX_FMT_GRAY14BE,
    AV_PIX_FMT_GRAY14LE,
    AV_PIX_FMT_GRAYF32BE,
    AV_PIX_FMT_GRAYF32LE,
    AV_PIX_FMT_YUVA422P12BE,
    AV_PIX_FMT_YUVA422P12LE,
    AV_PIX_FMT_YUVA444P12BE,
    AV_PIX_FMT_YUVA444P12LE,
    AV_PIX_FMT_NV24,
    AV_PIX_FMT_NV42,
    AV_PIX_FMT_VULKAN,
    AV_PIX_FMT_Y210BE,
    AV_PIX_FMT_Y210LE,
    AV_PIX_FMT_X2RGB10LE,
    AV_PIX_FMT_X2RGB10BE,
    AV_PIX_FMT_X2BGR10LE,
    AV_PIX_FMT_X2BGR10BE,
    AV_PIX_FMT_P210BE,
    AV_PIX_FMT_P210LE,
    AV_PIX_FMT_P410BE,
    AV_PIX_FMT_P410LE,
    AV_PIX_FMT_P216BE,
    AV_PIX_FMT_P216LE,
    AV_PIX_FMT_P416BE,
    AV_PIX_FMT_P416LE,
    AV_PIX_FMT_VUYA,
    AV_PIX_FMT_RGBAF16BE,
    AV_PIX_FMT_RGBAF16LE,
    AV_PIX_FMT_VUYX,
    AV_PIX_FMT_P012LE,
    AV_PIX_FMT_P012BE,
    AV_PIX_FMT_Y212BE,
    AV_PIX_FMT_Y212LE,
    AV_PIX_FMT_XV30BE,
    AV_PIX_FMT_XV30LE,
    AV_PIX_FMT_XV36BE,
    AV_PIX_FMT_XV36LE,
    AV_PIX_FMT_RGBF32BE,
    AV_PIX_FMT_RGBF32LE,
    AV_PIX_FMT_RGBAF32BE,
    AV_PIX_FMT_RGBAF32LE,
    AV_PIX_FMT_P212BE,
    AV_PIX_FMT_P212LE,
    AV_PIX_FMT_P412BE,
    AV_PIX_FMT_P412LE,
    AV_PIX_FMT_GBRAP14BE,
    AV_PIX_FMT_GBRAP14LE,
    AV_PIX_FMT_D3D12,
    AV_PIX_FMT_NB
};

enum AVSampleFormat {
    AV_SAMPLE_FMT_NONE = -1,
    AV_SAMPLE_FMT_U8,
    AV_SAMPLE_FMT_S16,
    AV_SAMPLE_FMT_S32,
    AV_SAMPLE_FMT_FLT,
    AV_SAMPLE_FMT_DBL,
    AV_SAMPLE_FMT_U8P,
    AV_SAMPLE_FMT_S16P,
    AV_SAMPLE_FMT_S32P,
    AV_SAMPLE_FMT_FLTP,
    AV_SAMPLE_FMT_DBLP,
    AV_SAMPLE_FMT_S64,
    AV_SAMPLE_FMT_S64P,
    AV_SAMPLE_FMT_NB
};

typedef enum {
    AV_CLASS_CATEGORY_NA = 0,
    AV_CLASS_CATEGORY_INPUT,
    AV_CLASS_CATEGORY_OUTPUT,
    AV_CLASS_CATEGORY_MUXER,
    AV_CLASS_CATEGORY_DEMUXER,
    AV_CLASS_CATEGORY_ENCODER,
    AV_CLASS_CATEGORY_DECODER,
    AV_CLASS_CATEGORY_FILTER,
    AV_CLASS_CATEGORY_BITSTREAM_FILTER,
    AV_CLASS_CATEGORY_SWSCALER,
    AV_CLASS_CATEGORY_SWRESAMPLER,
    AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT = 40,
    AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT,
    AV_CLASS_CATEGORY_DEVICE_AUDIO_OUTPUT,
    AV_CLASS_CATEGORY_DEVICE_AUDIO_INPUT,
    AV_CLASS_CATEGORY_DEVICE_OUTPUT,
    AV_CLASS_CATEGORY_DEVICE_INPUT,
    AV_CLASS_CATEGORY_NB
} AVClassCategory;

// Structs
typedef struct AVFilterContext AVFilterContext;

typedef struct AVFilterLink    AVFilterLink;

typedef struct AVFilterPad     AVFilterPad;

typedef struct AVFilterFormats AVFilterFormats;

typedef struct AVFilterChannelLayouts AVFilterChannelLayouts;

typedef struct AVRational{
    int num;
    int den;
} AVRational;

typedef struct AVFilterFormatsConfig {
    AVFilterFormats *formats;
    AVFilterFormats  *samplerates;
    AVFilterChannelLayouts  *channel_layouts;
    AVFilterFormats *color_spaces;
    AVFilterFormats *color_ranges;
} AVFilterFormatsConfig;

typedef struct AVBuffer AVBuffer;

typedef struct AVDictionary AVDictionary;

typedef struct AVBufferRef {
    AVBuffer *buffer;
    uint8_t *data;
    size_t   size;
} AVBufferRef;

typedef struct AVFrameSideData {
    enum AVFrameSideDataType type;
    uint8_t *data;
    size_t   size;
    AVDictionary *metadata;
    AVBufferRef *buf;
} AVFrameSideData;

typedef struct AVChannelCustom {
    enum AVChannel id;
    char name[16];
    void *opaque;
} AVChannelCustom;

typedef struct AVChannelLayout {
    enum AVChannelOrder order;
    int nb_channels;
    union {
        uint64_t mask;
        AVChannelCustom *map;
    } u;
    void *opaque;
} AVChannelLayout;

typedef struct AVFrame {
#define AV_NUM_DATA_POINTERS 8
    uint8_t *data[8];
    int linesize[8];
    uint8_t **extended_data;
    int width, height;
    int nb_samples;
    int format;
#if FF_API_FRAME_KEY
    int key_frame;
#endif
    enum AVPictureType pict_type;
    AVRational sample_aspect_ratio;
    int64_t pts;
    int64_t pkt_dts;
    AVRational time_base;
    int quality;
    void *opaque;
    int repeat_pict;
#if FF_API_INTERLACED_FRAME
    int interlaced_frame;
    int top_field_first;
#endif
#if FF_API_PALETTE_HAS_CHANGED
    int palette_has_changed;
#endif
    int sample_rate;
    AVBufferRef *buf[8];
    AVBufferRef **extended_buf;
    int        nb_extended_buf;
    AVFrameSideData **side_data;
    int            nb_side_data;
#define AV_FRAME_FLAG_CORRUPT       (1 << 0)
#define AV_FRAME_FLAG_KEY (1 << 1)
#define AV_FRAME_FLAG_DISCARD   (1 << 2)
#define AV_FRAME_FLAG_INTERLACED (1 << 3)
#define AV_FRAME_FLAG_TOP_FIELD_FIRST (1 << 4)
    int flags;
    enum AVColorRange color_range;
    enum AVColorPrimaries color_primaries;
    enum AVColorTransferCharacteristic color_trc;
    enum AVColorSpace colorspace;
    enum AVChromaLocation chroma_location;
    int64_t best_effort_timestamp;
#if FF_API_FRAME_PKT
    int64_t pkt_pos;
#endif
    AVDictionary *metadata;
    int decode_error_flags;
#define FF_DECODE_ERROR_INVALID_BITSTREAM   1
#define FF_DECODE_ERROR_MISSING_REFERENCE   2
#define FF_DECODE_ERROR_CONCEALMENT_ACTIVE  4
#define FF_DECODE_ERROR_DECODE_SLICES       8
#if FF_API_FRAME_PKT
    int pkt_size;
#endif
    AVBufferRef *hw_frames_ctx;
    AVBufferRef *opaque_ref;
    size_t crop_top;
    size_t crop_bottom;
    size_t crop_left;
    size_t crop_right;
    AVBufferRef *private_ref;
    AVChannelLayout ch_layout;
    int64_t duration;
} AVFrame;

typedef struct AVClass {
    const char* class_name;
    const char* (*item_name)(void* ctx);
    const struct AVOption *option;
    int version;
    int log_level_offset_offset;
    int parent_log_context_offset;
    AVClassCategory category;
    AVClassCategory (*get_category)(void* ctx);
    int (*query_ranges)(struct AVOptionRanges **, void *obj, const char *key, int flags);
    void* (*child_next)(void *obj, void *prev);
    const struct AVClass* (*child_class_iterate)(void **iter);
} AVClass;

typedef struct AVFilter {
    const char *name;
    const char *description;
    const AVFilterPad *inputs;
    const AVFilterPad *outputs;
    const AVClass *priv_class;
    int flags;
    uint8_t nb_inputs;
    uint8_t nb_outputs;
    uint8_t formats_state;
    int (*preinit)(AVFilterContext *ctx);
    int (*init)(AVFilterContext *ctx);
    void (*uninit)(AVFilterContext *ctx);
    union {
        int (*query_func)(AVFilterContext *);
        int (*query_func2)(const AVFilterContext *,
                           struct AVFilterFormatsConfig **cfg_in,
                           struct AVFilterFormatsConfig **cfg_out);
        const enum AVPixelFormat *pixels_list;
        const enum AVSampleFormat *samples_list;
        enum AVPixelFormat  pix_fmt;
        enum AVSampleFormat sample_fmt;
    } formats;
    int priv_size;
    int flags_internal;
    int (*process_command)(AVFilterContext *, const char *cmd, const char *arg, char *res, int res_len, int flags);
    int (*activate)(AVFilterContext *ctx);
} AVFilter;

struct AVFilterContext {
    const AVClass *av_class;
    const AVFilter *filter;
    char *name;
    AVFilterPad   *input_pads;
    AVFilterLink **inputs;
    unsigned    nb_inputs;
    AVFilterPad   *output_pads;
    AVFilterLink **outputs;
    unsigned    nb_outputs;
    void *priv;
    struct AVFilterGraph *graph;
    int thread_type;
    int nb_threads;
    char *enable_str;
    int is_disabled;
    AVBufferRef *hw_device_ctx;
    int extra_hw_frames;
};

struct AVFilterLink {
    AVFilterContext *src;
    AVFilterPad *srcpad;
    AVFilterContext *dst;
    AVFilterPad *dstpad;
    enum AVMediaType type;
    int format;
    int w;
    int h;
    AVRational sample_aspect_ratio;
    enum AVColorSpace colorspace;
    enum AVColorRange color_range;
    int sample_rate;
    AVChannelLayout ch_layout;
    AVRational time_base;
    AVFilterFormatsConfig incfg;
    AVFilterFormatsConfig outcfg;
};

struct AVFilterPad {
    const char *name;
    enum AVMediaType type;
#define AVFILTERPAD_FLAG_NEEDS_WRITABLE                  (1 << 0)
#define AVFILTERPAD_FLAG_FREE_NAME                       (1 << 1)
    int flags;
    union {
        AVFrame *(*video)(AVFilterLink *link, int w, int h);
        AVFrame *(*audio)(AVFilterLink *link, int nb_samples);
    } get_buffer;
    int (*filter_frame)(AVFilterLink *link, AVFrame *frame);
    int (*request_frame)(AVFilterLink *link);
    int (*config_props)(AVFilterLink *link);
};

typedef int (avfilter_action_func)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs);

typedef int (avfilter_execute_func)(AVFilterContext *ctx, avfilter_action_func *func, void *arg, int *ret, int nb_jobs);

typedef struct AVFilterGraph {
    const AVClass *av_class;
    AVFilterContext **filters;
    unsigned nb_filters;
    char *scale_sws_opts;
    int thread_type;
    int nb_threads;
    void *opaque;
    avfilter_execute_func *execute;
    char *aresample_swr_opts;
} AVFilterGraph;

typedef struct FFFilterContext {
    AVFilterContext p;
    avfilter_execute_func *execute;
    unsigned state_flags;
    unsigned ready;
    struct AVExpr *enable;
    double *var_values;
    struct AVFilterCommand *command_queue;
} FFFilterContext;

// Functions
void av_log_set_level(int level);
const char *av_version_info(void);
void avfilter_graph_free(AVFilterGraph **graph);
AVFilterGraph *avfilter_graph_alloc(void);
const AVFilter *avfilter_get_by_name(const char *name);
AVFilterContext *avfilter_graph_alloc_filter(AVFilterGraph *graph, const AVFilter *filter, const char *name);
int avfilter_init_str(AVFilterContext *ctx, const char *args);
int avfilter_init_dict(AVFilterContext *ctx, AVDictionary **options);
int avfilter_link(AVFilterContext *src, unsigned srcpad, AVFilterContext *dst, unsigned dstpad);
int avfilter_graph_config(AVFilterGraph *graphctx, void *log_ctx);
int av_buffersrc_write_frame(AVFilterContext *ctx, const AVFrame *frame);
int av_buffersink_get_frame(AVFilterContext *ctx, AVFrame *frame);
void av_buffersink_set_frame_size(AVFilterContext *ctx, unsigned frame_size);
int av_dict_copy(AVDictionary **dst, const AVDictionary *src, int flags);
char *avfilter_graph_dump(AVFilterGraph *graph, const char *options);
void av_log(void* avcl, int level, const char *fmt, ...);
void av_log_set_callback(void (*callback)(void*, int, const char*, va_list));
AVFrame *av_frame_alloc(void);// Enums
enum AVUtil {
    AV_NOPTS_VALUE = -9223372036854775808,
};

enum AVMediaType {
    AVMEDIA_TYPE_UNKNOWN = -1,
    AVMEDIA_TYPE_VIDEO,
    AVMEDIA_TYPE_AUDIO,
    AVMEDIA_TYPE_DATA,
    AVMEDIA_TYPE_SUBTITLE,
    AVMEDIA_TYPE_ATTACHMENT,
    AVMEDIA_TYPE_NB
};

enum AVPictureType {
    AV_PICTURE_TYPE_NONE = 0,
    AV_PICTURE_TYPE_I,
    AV_PICTURE_TYPE_P,
    AV_PICTURE_TYPE_B,
    AV_PICTURE_TYPE_S,
    AV_PICTURE_TYPE_SI,
    AV_PICTURE_TYPE_SP,
    AV_PICTURE_TYPE_BI,
};

enum AVPacketSideDataType {
    AV_PKT_DATA_PALETTE,
    AV_PKT_DATA_NEW_EXTRADATA,
    AV_PKT_DATA_PARAM_CHANGE,
    AV_PKT_DATA_H263_MB_INFO,
    AV_PKT_DATA_REPLAYGAIN,
    AV_PKT_DATA_DISPLAYMATRIX,
    AV_PKT_DATA_STEREO3D,
    AV_PKT_DATA_AUDIO_SERVICE_TYPE,
    AV_PKT_DATA_QUALITY_STATS,
    AV_PKT_DATA_FALLBACK_TRACK,
    AV_PKT_DATA_CPB_PROPERTIES,
    AV_PKT_DATA_SKIP_SAMPLES,
    AV_PKT_DATA_JP_DUALMONO,
    AV_PKT_DATA_STRINGS_METADATA,
    AV_PKT_DATA_SUBTITLE_POSITION,
    AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL,
    AV_PKT_DATA_WEBVTT_IDENTIFIER,
    AV_PKT_DATA_WEBVTT_SETTINGS,
    AV_PKT_DATA_METADATA_UPDATE,
    AV_PKT_DATA_MPEGTS_STREAM_ID,
    AV_PKT_DATA_MASTERING_DISPLAY_METADATA,
    AV_PKT_DATA_SPHERICAL,
    AV_PKT_DATA_CONTENT_LIGHT_LEVEL,
    AV_PKT_DATA_A53_CC,
    AV_PKT_DATA_ENCRYPTION_INIT_INFO,
    AV_PKT_DATA_ENCRYPTION_INFO,
    AV_PKT_DATA_AFD,
    AV_PKT_DATA_PRFT,
    AV_PKT_DATA_ICC_PROFILE,
    AV_PKT_DATA_DOVI_CONF,
    AV_PKT_DATA_S12M_TIMECODE,
    AV_PKT_DATA_DYNAMIC_HDR10_PLUS,
    AV_PKT_DATA_IAMF_MIX_GAIN_PARAM,
    AV_PKT_DATA_IAMF_DEMIXING_INFO_PARAM,
    AV_PKT_DATA_IAMF_RECON_GAIN_INFO_PARAM,
    AV_PKT_DATA_AMBIENT_VIEWING_ENVIRONMENT,
    AV_PKT_DATA_FRAME_CROPPING,
    AV_PKT_DATA_LCEVC,
    AV_PKT_DATA_NB
};

enum AVFrameSideDataType {
    AV_FRAME_DATA_PANSCAN,
    AV_FRAME_DATA_A53_CC,
    AV_FRAME_DATA_STEREO3D,
    AV_FRAME_DATA_MATRIXENCODING,
    AV_FRAME_DATA_DOWNMIX_INFO,
    AV_FRAME_DATA_REPLAYGAIN,
    AV_FRAME_DATA_DISPLAYMATRIX,
    AV_FRAME_DATA_AFD,
    AV_FRAME_DATA_MOTION_VECTORS,
    AV_FRAME_DATA_SKIP_SAMPLES,
    AV_FRAME_DATA_AUDIO_SERVICE_TYPE,
    AV_FRAME_DATA_MASTERING_DISPLAY_METADATA,
    AV_FRAME_DATA_GOP_TIMECODE,
    AV_FRAME_DATA_SPHERICAL,
    AV_FRAME_DATA_CONTENT_LIGHT_LEVEL,
    AV_FRAME_DATA_ICC_PROFILE,
    AV_FRAME_DATA_S12M_TIMECODE,
    AV_FRAME_DATA_DYNAMIC_HDR_PLUS,
    AV_FRAME_DATA_REGIONS_OF_INTEREST,
    AV_FRAME_DATA_VIDEO_ENC_PARAMS,
    AV_FRAME_DATA_SEI_UNREGISTERED,
    AV_FRAME_DATA_FILM_GRAIN_PARAMS,
    AV_FRAME_DATA_DETECTION_BBOXES,
    AV_FRAME_DATA_DOVI_RPU_BUFFER,
    AV_FRAME_DATA_DOVI_METADATA,
    AV_FRAME_DATA_DYNAMIC_HDR_VIVID,
    AV_FRAME_DATA_AMBIENT_VIEWING_ENVIRONMENT,
    AV_FRAME_DATA_VIDEO_HINT,
    AV_FRAME_DATA_LCEVC,
    AV_FRAME_DATA_VIEW_ID,
};

enum AVColorRange {
    AVCOL_RANGE_UNSPECIFIED = 0,
    AVCOL_RANGE_MPEG        = 1,
    AVCOL_RANGE_JPEG        = 2,
    AVCOL_RANGE_NB
};

enum AVColorPrimaries {
    AVCOL_PRI_RESERVED0   = 0,
    AVCOL_PRI_BT709       = 1,
    AVCOL_PRI_UNSPECIFIED = 2,
    AVCOL_PRI_RESERVED    = 3,
    AVCOL_PRI_BT470M      = 4,
    AVCOL_PRI_BT470BG     = 5,
    AVCOL_PRI_SMPTE170M   = 6,
    AVCOL_PRI_SMPTE240M   = 7,
    AVCOL_PRI_FILM        = 8,
    AVCOL_PRI_BT2020      = 9,
    AVCOL_PRI_SMPTE428    = 10,
    AVCOL_PRI_SMPTEST428_1 = AVCOL_PRI_SMPTE428,
    AVCOL_PRI_SMPTE431    = 11,
    AVCOL_PRI_SMPTE432    = 12,
    AVCOL_PRI_EBU3213     = 22,
    AVCOL_PRI_JEDEC_P22   = AVCOL_PRI_EBU3213,
    AVCOL_PRI_NB
};

enum AVColorTransferCharacteristic {
    AVCOL_TRC_RESERVED0    = 0,
    AVCOL_TRC_BT709        = 1,
    AVCOL_TRC_UNSPECIFIED  = 2,
    AVCOL_TRC_RESERVED     = 3,
    AVCOL_TRC_GAMMA22      = 4,
    AVCOL_TRC_GAMMA28      = 5,
    AVCOL_TRC_SMPTE170M    = 6,
    AVCOL_TRC_SMPTE240M    = 7,
    AVCOL_TRC_LINEAR       = 8,
    AVCOL_TRC_LOG          = 9,
    AVCOL_TRC_LOG_SQRT     = 10,
    AVCOL_TRC_IEC61966_2_4 = 11,
    AVCOL_TRC_BT1361_ECG   = 12,
    AVCOL_TRC_IEC61966_2_1 = 13,
    AVCOL_TRC_BT2020_10    = 14,
    AVCOL_TRC_BT2020_12    = 15,
    AVCOL_TRC_SMPTE2084    = 16,
    AVCOL_TRC_SMPTEST2084  = AVCOL_TRC_SMPTE2084,
    AVCOL_TRC_SMPTE428     = 17,
    AVCOL_TRC_SMPTEST428_1 = AVCOL_TRC_SMPTE428,
    AVCOL_TRC_ARIB_STD_B67 = 18,
    AVCOL_TRC_NB
};

enum AVColorSpace {
    AVCOL_SPC_RGB         = 0,
    AVCOL_SPC_BT709       = 1,
    AVCOL_SPC_UNSPECIFIED = 2,
    AVCOL_SPC_RESERVED    = 3,
    AVCOL_SPC_FCC         = 4,
    AVCOL_SPC_BT470BG     = 5,
    AVCOL_SPC_SMPTE170M   = 6,
    AVCOL_SPC_SMPTE240M   = 7,
    AVCOL_SPC_YCGCO       = 8,
    AVCOL_SPC_YCOCG       = AVCOL_SPC_YCGCO,
    AVCOL_SPC_BT2020_NCL  = 9,
    AVCOL_SPC_BT2020_CL   = 10,
    AVCOL_SPC_SMPTE2085   = 11,
    AVCOL_SPC_CHROMA_DERIVED_NCL = 12,
    AVCOL_SPC_CHROMA_DERIVED_CL = 13,
    AVCOL_SPC_ICTCP       = 14,
    AVCOL_SPC_IPT_C2      = 15,
    AVCOL_SPC_YCGCO_RE    = 16,
    AVCOL_SPC_YCGCO_RO    = 17,
    AVCOL_SPC_NB
};

enum AVChromaLocation {
    AVCHROMA_LOC_UNSPECIFIED = 0,
    AVCHROMA_LOC_LEFT        = 1,
    AVCHROMA_LOC_CENTER      = 2,
    AVCHROMA_LOC_TOPLEFT     = 3,
    AVCHROMA_LOC_TOP         = 4,
    AVCHROMA_LOC_BOTTOMLEFT  = 5,
    AVCHROMA_LOC_BOTTOM      = 6,
    AVCHROMA_LOC_NB
};

enum AVAlphaMode {
    AVALPHA_MODE_UNSPECIFIED = 0,
    AVALPHA_MODE_STRAIGHT,
    AVALPHA_MODE_PREMULTIPLIED,
};

enum AVChannelOrder {
    AV_CHANNEL_ORDER_UNSPEC,
    AV_CHANNEL_ORDER_NATIVE,
    AV_CHANNEL_ORDER_CUSTOM,
    AV_CHANNEL_ORDER_AMBISONIC,
    FF_CHANNEL_ORDER_NB
};

enum AVChannel {
    AV_CHAN_NONE = -1,
    AV_CHAN_FRONT_LEFT,
    AV_CHAN_FRONT_RIGHT,
    AV_CHAN_FRONT_CENTER,
    AV_CHAN_LOW_FREQUENCY,
    AV_CHAN_BACK_LEFT,
    AV_CHAN_BACK_RIGHT,
    AV_CHAN_FRONT_LEFT_OF_CENTER,
    AV_CHAN_FRONT_RIGHT_OF_CENTER,
    AV_CHAN_BACK_CENTER,
    AV_CHAN_SIDE_LEFT,
    AV_CHAN_SIDE_RIGHT,
    AV_CHAN_TOP_CENTER,
    AV_CHAN_TOP_FRONT_LEFT,
    AV_CHAN_TOP_FRONT_CENTER,
    AV_CHAN_TOP_FRONT_RIGHT,
    AV_CHAN_TOP_BACK_LEFT,
    AV_CHAN_TOP_BACK_CENTER,
    AV_CHAN_TOP_BACK_RIGHT,
    AV_CHAN_STEREO_LEFT = 29,
    AV_CHAN_STEREO_RIGHT,
    AV_CHAN_WIDE_LEFT,
    AV_CHAN_WIDE_RIGHT,
    AV_CHAN_SURROUND_DIRECT_LEFT,
    AV_CHAN_SURROUND_DIRECT_RIGHT,
    AV_CHAN_LOW_FREQUENCY_2,
    AV_CHAN_TOP_SIDE_LEFT,
    AV_CHAN_TOP_SIDE_RIGHT,
    AV_CHAN_BOTTOM_FRONT_CENTER,
    AV_CHAN_BOTTOM_FRONT_LEFT,
    AV_CHAN_BOTTOM_FRONT_RIGHT,
    AV_CHAN_SIDE_SURROUND_LEFT,
    AV_CHAN_SIDE_SURROUND_RIGHT,
    AV_CHAN_TOP_SURROUND_LEFT,
    AV_CHAN_TOP_SURROUND_RIGHT,
    AV_CHAN_UNUSED = 0x200,
    AV_CHAN_UNKNOWN = 0x300,
    AV_CHAN_AMBISONIC_BASE = 0x400,
    AV_CHAN_AMBISONIC_END  = 0x7ff,
};

enum AVPixelFormat {
    AV_PIX_FMT_NONE = -1,
    AV_PIX_FMT_YUV420P,
    AV_PIX_FMT_YUYV422,
    AV_PIX_FMT_RGB24,
    AV_PIX_FMT_BGR24,
    AV_PIX_FMT_YUV422P,
    AV_PIX_FMT_YUV444P,
    AV_PIX_FMT_YUV410P,
    AV_PIX_FMT_YUV411P,
    AV_PIX_FMT_GRAY8,
    AV_PIX_FMT_MONOWHITE,
    AV_PIX_FMT_MONOBLACK,
    AV_PIX_FMT_PAL8,
    AV_PIX_FMT_YUVJ420P,
    AV_PIX_FMT_YUVJ422P,
    AV_PIX_FMT_YUVJ444P,
    AV_PIX_FMT_UYVY422,
    AV_PIX_FMT_UYYVYY411,
    AV_PIX_FMT_BGR8,
    AV_PIX_FMT_BGR4,
    AV_PIX_FMT_BGR4_BYTE,
    AV_PIX_FMT_RGB8,
    AV_PIX_FMT_RGB4,
    AV_PIX_FMT_RGB4_BYTE,
    AV_PIX_FMT_NV12,
    AV_PIX_FMT_NV21,
    AV_PIX_FMT_ARGB,
    AV_PIX_FMT_RGBA,
    AV_PIX_FMT_ABGR,
    AV_PIX_FMT_BGRA,
    AV_PIX_FMT_GRAY16BE,
    AV_PIX_FMT_GRAY16LE,
    AV_PIX_FMT_YUV440P,
    AV_PIX_FMT_YUVJ440P,
    AV_PIX_FMT_YUVA420P,
    AV_PIX_FMT_RGB48BE,
    AV_PIX_FMT_RGB48LE,
    AV_PIX_FMT_RGB565BE,
    AV_PIX_FMT_RGB565LE,
    AV_PIX_FMT_RGB555BE,
    AV_PIX_FMT_RGB555LE,
    AV_PIX_FMT_BGR565BE,
    AV_PIX_FMT_BGR565LE,
    AV_PIX_FMT_BGR555BE,
    AV_PIX_FMT_BGR555LE,
    AV_PIX_FMT_VAAPI,
    AV_PIX_FMT_YUV420P16LE,
    AV_PIX_FMT_YUV420P16BE,
    AV_PIX_FMT_YUV422P16LE,
    AV_PIX_FMT_YUV422P16BE,
    AV_PIX_FMT_YUV444P16LE,
    AV_PIX_FMT_YUV444P16BE,
    AV_PIX_FMT_DXVA2_VLD,
    AV_PIX_FMT_RGB444LE,
    AV_PIX_FMT_RGB444BE,
    AV_PIX_FMT_BGR444LE,
    AV_PIX_FMT_BGR444BE,
    AV_PIX_FMT_YA8,
    AV_PIX_FMT_Y400A = AV_PIX_FMT_YA8,
    AV_PIX_FMT_GRAY8A= AV_PIX_FMT_YA8,
    AV_PIX_FMT_BGR48BE,
    AV_PIX_FMT_BGR48LE,
    AV_PIX_FMT_YUV420P9BE,
    AV_PIX_FMT_YUV420P9LE,
    AV_PIX_FMT_YUV420P10BE,
    AV_PIX_FMT_YUV420P10LE,
    AV_PIX_FMT_YUV422P10BE,
    AV_PIX_FMT_YUV422P10LE,
    AV_PIX_FMT_YUV444P9BE,
    AV_PIX_FMT_YUV444P9LE,
    AV_PIX_FMT_YUV444P10BE,
    AV_PIX_FMT_YUV444P10LE,
    AV_PIX_FMT_YUV422P9BE,
    AV_PIX_FMT_YUV422P9LE,
    AV_PIX_FMT_GBRP,
    AV_PIX_FMT_GBR24P = AV_PIX_FMT_GBRP,
    AV_PIX_FMT_GBRP9BE,
    AV_PIX_FMT_GBRP9LE,
    AV_PIX_FMT_GBRP10BE,
    AV_PIX_FMT_GBRP10LE,
    AV_PIX_FMT_GBRP16BE,
    AV_PIX_FMT_GBRP16LE,
    AV_PIX_FMT_YUVA422P,
    AV_PIX_FMT_YUVA444P,
    AV_PIX_FMT_YUVA420P9BE,
    AV_PIX_FMT_YUVA420P9LE,
    AV_PIX_FMT_YUVA422P9BE,
    AV_PIX_FMT_YUVA422P9LE,
    AV_PIX_FMT_YUVA444P9BE,
    AV_PIX_FMT_YUVA444P9LE,
    AV_PIX_FMT_YUVA420P10BE,
    AV_PIX_FMT_YUVA420P10LE,
    AV_PIX_FMT_YUVA422P10BE,
    AV_PIX_FMT_YUVA422P10LE,
    AV_PIX_FMT_YUVA444P10BE,
    AV_PIX_FMT_YUVA444P10LE,
    AV_PIX_FMT_YUVA420P16BE,
    AV_PIX_FMT_YUVA420P16LE,
    AV_PIX_FMT_YUVA422P16BE,
    AV_PIX_FMT_YUVA422P16LE,
    AV_PIX_FMT_YUVA444P16BE,
    AV_PIX_FMT_YUVA444P16LE,
    AV_PIX_FMT_VDPAU,
    AV_PIX_FMT_XYZ12LE,
    AV_PIX_FMT_XYZ12BE,
    AV_PIX_FMT_NV16,
    AV_PIX_FMT_NV20LE,
    AV_PIX_FMT_NV20BE,
    AV_PIX_FMT_RGBA64BE,
    AV_PIX_FMT_RGBA64LE,
    AV_PIX_FMT_BGRA64BE,
    AV_PIX_FMT_BGRA64LE,
    AV_PIX_FMT_YVYU422,
    AV_PIX_FMT_YA16BE,
    AV_PIX_FMT_YA16LE,
    AV_PIX_FMT_GBRAP,
    AV_PIX_FMT_GBRAP16BE,
    AV_PIX_FMT_GBRAP16LE,
    AV_PIX_FMT_QSV,
    AV_PIX_FMT_MMAL,
    AV_PIX_FMT_D3D11VA_VLD,
    AV_PIX_FMT_CUDA,
    AV_PIX_FMT_0RGB,
    AV_PIX_FMT_RGB0,
    AV_PIX_FMT_0BGR,
    AV_PIX_FMT_BGR0,
    AV_PIX_FMT_YUV420P12BE,
    AV_PIX_FMT_YUV420P12LE,
    AV_PIX_FMT_YUV420P14BE,
    AV_PIX_FMT_YUV420P14LE,
    AV_PIX_FMT_YUV422P12BE,
    AV_PIX_FMT_YUV422P12LE,
    AV_PIX_FMT_YUV422P14BE,
    AV_PIX_FMT_YUV422P14LE,
    AV_PIX_FMT_YUV444P12BE,
    AV_PIX_FMT_YUV444P12LE,
    AV_PIX_FMT_YUV444P14BE,
    AV_PIX_FMT_YUV444P14LE,
    AV_PIX_FMT_GBRP12BE,
    AV_PIX_FMT_GBRP12LE,
    AV_PIX_FMT_GBRP14BE,
    AV_PIX_FMT_GBRP14LE,
    AV_PIX_FMT_YUVJ411P,
    AV_PIX_FMT_BAYER_BGGR8,
    AV_PIX_FMT_BAYER_RGGB8,
    AV_PIX_FMT_BAYER_GBRG8,
    AV_PIX_FMT_BAYER_GRBG8,
    AV_PIX_FMT_BAYER_BGGR16LE,
    AV_PIX_FMT_BAYER_BGGR16BE,
    AV_PIX_FMT_BAYER_RGGB16LE,
    AV_PIX_FMT_BAYER_RGGB16BE,
    AV_PIX_FMT_BAYER_GBRG16LE,
    AV_PIX_FMT_BAYER_GBRG16BE,
    AV_PIX_FMT_BAYER_GRBG16LE,
    AV_PIX_FMT_BAYER_GRBG16BE,
    AV_PIX_FMT_YUV440P10LE,
    AV_PIX_FMT_YUV440P10BE,
    AV_PIX_FMT_YUV440P12LE,
    AV_PIX_FMT_YUV440P12BE,
    AV_PIX_FMT_AYUV64LE,
    AV_PIX_FMT_AYUV64BE,
    AV_PIX_FMT_VIDEOTOOLBOX,
    AV_PIX_FMT_P010LE,
    AV_PIX_FMT_P010BE,
    AV_PIX_FMT_GBRAP12BE,
    AV_PIX_FMT_GBRAP12LE,
    AV_PIX_FMT_GBRAP10BE,
    AV_PIX_FMT_GBRAP10LE,
    AV_PIX_FMT_MEDIACODEC,
    AV_PIX_FMT_GRAY12BE,
    AV_PIX_FMT_GRAY12LE,
    AV_PIX_FMT_GRAY10BE,
    AV_PIX_FMT_GRAY10LE,
    AV_PIX_FMT_P016LE,
    AV_PIX_FMT_P016BE,
    AV_PIX_FMT_D3D11,
    AV_PIX_FMT_GRAY9BE,
    AV_PIX_FMT_GRAY9LE,
    AV_PIX_FMT_GBRPF32BE,
    AV_PIX_FMT_GBRPF32LE,
    AV_PIX_FMT_GBRAPF32BE,
    AV_PIX_FMT_GBRAPF32LE,
    AV_PIX_FMT_DRM_PRIME,
    AV_PIX_FMT_OPENCL,
    AV_PIX_FMT_GRAY14BE,
    AV_PIX_FMT_GRAY14LE,
    AV_PIX_FMT_GRAYF32BE,
    AV_PIX_FMT_GRAYF32LE,
    AV_PIX_FMT_YUVA422P12BE,
    AV_PIX_FMT_YUVA422P12LE,
    AV_PIX_FMT_YUVA444P12BE,
    AV_PIX_FMT_YUVA444P12LE,
    AV_PIX_FMT_NV24,
    AV_PIX_FMT_NV42,
    AV_PIX_FMT_VULKAN,
    AV_PIX_FMT_Y210BE,
    AV_PIX_FMT_Y210LE,
    AV_PIX_FMT_X2RGB10LE,
    AV_PIX_FMT_X2RGB10BE,
    AV_PIX_FMT_X2BGR10LE,
    AV_PIX_FMT_X2BGR10BE,
    AV_PIX_FMT_P210BE,
    AV_PIX_FMT_P210LE,
    AV_PIX_FMT_P410BE,
    AV_PIX_FMT_P410LE,
    AV_PIX_FMT_P216BE,
    AV_PIX_FMT_P216LE,
    AV_PIX_FMT_P416BE,
    AV_PIX_FMT_P416LE,
    AV_PIX_FMT_VUYA,
    AV_PIX_FMT_RGBAF16BE,
    AV_PIX_FMT_RGBAF16LE,
    AV_PIX_FMT_VUYX,
    AV_PIX_FMT_P012LE,
    AV_PIX_FMT_P012BE,
    AV_PIX_FMT_Y212BE,
    AV_PIX_FMT_Y212LE,
    AV_PIX_FMT_XV30BE,
    AV_PIX_FMT_XV30LE,
    AV_PIX_FMT_XV36BE,
    AV_PIX_FMT_XV36LE,
    AV_PIX_FMT_RGBF32BE,
    AV_PIX_FMT_RGBF32LE,
    AV_PIX_FMT_RGBAF32BE,
    AV_PIX_FMT_RGBAF32LE,
    AV_PIX_FMT_P212BE,
    AV_PIX_FMT_P212LE,
    AV_PIX_FMT_P412BE,
    AV_PIX_FMT_P412LE,
    AV_PIX_FMT_GBRAP14BE,
    AV_PIX_FMT_GBRAP14LE,
    AV_PIX_FMT_D3D12,
    AV_PIX_FMT_NB
};

enum AVCodecID {
    AV_CODEC_ID_NONE,
    AV_CODEC_ID_MPEG1VIDEO,
    AV_CODEC_ID_MPEG2VIDEO,
    AV_CODEC_ID_H261,
    AV_CODEC_ID_H263,
    AV_CODEC_ID_RV10,
    AV_CODEC_ID_RV20,
    AV_CODEC_ID_MJPEG,
    AV_CODEC_ID_MJPEGB,
    AV_CODEC_ID_LJPEG,
    AV_CODEC_ID_SP5X,
    AV_CODEC_ID_JPEGLS,
    AV_CODEC_ID_MPEG4,
    AV_CODEC_ID_RAWVIDEO,
    AV_CODEC_ID_MSMPEG4V1,
    AV_CODEC_ID_MSMPEG4V2,
    AV_CODEC_ID_MSMPEG4V3,
    AV_CODEC_ID_WMV1,
    AV_CODEC_ID_WMV2,
    AV_CODEC_ID_H263P,
    AV_CODEC_ID_H263I,
    AV_CODEC_ID_FLV1,
    AV_CODEC_ID_SVQ1,
    AV_CODEC_ID_SVQ3,
    AV_CODEC_ID_DVVIDEO,
    AV_CODEC_ID_HUFFYUV,
    AV_CODEC_ID_CYUV,
    AV_CODEC_ID_H264,
    AV_CODEC_ID_INDEO3,
    AV_CODEC_ID_VP3,
    AV_CODEC_ID_THEORA,
    AV_CODEC_ID_ASV1,
    AV_CODEC_ID_ASV2,
    AV_CODEC_ID_FFV1,
    AV_CODEC_ID_4XM,
    AV_CODEC_ID_VCR1,
    AV_CODEC_ID_CLJR,
    AV_CODEC_ID_MDEC,
    AV_CODEC_ID_ROQ,
    AV_CODEC_ID_INTERPLAY_VIDEO,
    AV_CODEC_ID_XAN_WC3,
    AV_CODEC_ID_XAN_WC4,
    AV_CODEC_ID_RPZA,
    AV_CODEC_ID_CINEPAK,
    AV_CODEC_ID_WS_VQA,
    AV_CODEC_ID_MSRLE,
    AV_CODEC_ID_MSVIDEO1,
    AV_CODEC_ID_IDCIN,
    AV_CODEC_ID_8BPS,
    AV_CODEC_ID_SMC,
    AV_CODEC_ID_FLIC,
    AV_CODEC_ID_TRUEMOTION1,
    AV_CODEC_ID_VMDVIDEO,
    AV_CODEC_ID_MSZH,
    AV_CODEC_ID_ZLIB,
    AV_CODEC_ID_QTRLE,
    AV_CODEC_ID_TSCC,
    AV_CODEC_ID_ULTI,
    AV_CODEC_ID_QDRAW,
    AV_CODEC_ID_VIXL,
    AV_CODEC_ID_QPEG,
    AV_CODEC_ID_PNG,
    AV_CODEC_ID_PPM,
    AV_CODEC_ID_PBM,
    AV_CODEC_ID_PGM,
    AV_CODEC_ID_PGMYUV,
    AV_CODEC_ID_PAM,
    AV_CODEC_ID_FFVHUFF,
    AV_CODEC_ID_RV30,
    AV_CODEC_ID_RV40,
    AV_CODEC_ID_VC1,
    AV_CODEC_ID_WMV3,
    AV_CODEC_ID_LOCO,
    AV_CODEC_ID_WNV1,
    AV_CODEC_ID_AASC,
    AV_CODEC_ID_INDEO2,
    AV_CODEC_ID_FRAPS,
    AV_CODEC_ID_TRUEMOTION2,
    AV_CODEC_ID_BMP,
    AV_CODEC_ID_CSCD,
    AV_CODEC_ID_MMVIDEO,
    AV_CODEC_ID_ZMBV,
    AV_CODEC_ID_AVS,
    AV_CODEC_ID_SMACKVIDEO,
    AV_CODEC_ID_NUV,
    AV_CODEC_ID_KMVC,
    AV_CODEC_ID_FLASHSV,
    AV_CODEC_ID_CAVS,
    AV_CODEC_ID_JPEG2000,
    AV_CODEC_ID_VMNC,
    AV_CODEC_ID_VP5,
    AV_CODEC_ID_VP6,
    AV_CODEC_ID_VP6F,
    AV_CODEC_ID_TARGA,
    AV_CODEC_ID_DSICINVIDEO,
    AV_CODEC_ID_TIERTEXSEQVIDEO,
    AV_CODEC_ID_TIFF,
    AV_CODEC_ID_GIF,
    AV_CODEC_ID_DXA,
    AV_CODEC_ID_DNXHD,
    AV_CODEC_ID_THP,
    AV_CODEC_ID_SGI,
    AV_CODEC_ID_C93,
    AV_CODEC_ID_BETHSOFTVID,
    AV_CODEC_ID_PTX,
    AV_CODEC_ID_TXD,
    AV_CODEC_ID_VP6A,
    AV_CODEC_ID_AMV,
    AV_CODEC_ID_VB,
    AV_CODEC_ID_PCX,
    AV_CODEC_ID_SUNRAST,
    AV_CODEC_ID_INDEO4,
    AV_CODEC_ID_INDEO5,
    AV_CODEC_ID_MIMIC,
    AV_CODEC_ID_RL2,
    AV_CODEC_ID_ESCAPE124,
    AV_CODEC_ID_DIRAC,
    AV_CODEC_ID_BFI,
    AV_CODEC_ID_CMV,
    AV_CODEC_ID_MOTIONPIXELS,
    AV_CODEC_ID_TGV,
    AV_CODEC_ID_TGQ,
    AV_CODEC_ID_TQI,
    AV_CODEC_ID_AURA,
    AV_CODEC_ID_AURA2,
    AV_CODEC_ID_V210X,
    AV_CODEC_ID_TMV,
    AV_CODEC_ID_V210,
    AV_CODEC_ID_DPX,
    AV_CODEC_ID_MAD,
    AV_CODEC_ID_FRWU,
    AV_CODEC_ID_FLASHSV2,
    AV_CODEC_ID_CDGRAPHICS,
    AV_CODEC_ID_R210,
    AV_CODEC_ID_ANM,
    AV_CODEC_ID_BINKVIDEO,
    AV_CODEC_ID_IFF_ILBM,
#define AV_CODEC_ID_IFF_BYTERUN1 AV_CODEC_ID_IFF_ILBM
    AV_CODEC_ID_KGV1,
    AV_CODEC_ID_YOP,
    AV_CODEC_ID_VP8,
    AV_CODEC_ID_PICTOR,
    AV_CODEC_ID_ANSI,
    AV_CODEC_ID_A64_MULTI,
    AV_CODEC_ID_A64_MULTI5,
    AV_CODEC_ID_R10K,
    AV_CODEC_ID_MXPEG,
    AV_CODEC_ID_LAGARITH,
    AV_CODEC_ID_PRORES,
    AV_CODEC_ID_JV,
    AV_CODEC_ID_DFA,
    AV_CODEC_ID_WMV3IMAGE,
    AV_CODEC_ID_VC1IMAGE,
    AV_CODEC_ID_UTVIDEO,
    AV_CODEC_ID_BMV_VIDEO,
    AV_CODEC_ID_VBLE,
    AV_CODEC_ID_DXTORY,
    AV_CODEC_ID_V410,
    AV_CODEC_ID_XWD,
    AV_CODEC_ID_CDXL,
    AV_CODEC_ID_XBM,
    AV_CODEC_ID_ZEROCODEC,
    AV_CODEC_ID_MSS1,
    AV_CODEC_ID_MSA1,
    AV_CODEC_ID_TSCC2,
    AV_CODEC_ID_MTS2,
    AV_CODEC_ID_CLLC,
    AV_CODEC_ID_MSS2,
    AV_CODEC_ID_VP9,
    AV_CODEC_ID_AIC,
    AV_CODEC_ID_ESCAPE130,
    AV_CODEC_ID_G2M,
    AV_CODEC_ID_WEBP,
    AV_CODEC_ID_HNM4_VIDEO,
    AV_CODEC_ID_HEVC,
#define AV_CODEC_ID_H265 AV_CODEC_ID_HEVC
    AV_CODEC_ID_FIC,
    AV_CODEC_ID_ALIAS_PIX,
    AV_CODEC_ID_BRENDER_PIX,
    AV_CODEC_ID_PAF_VIDEO,
    AV_CODEC_ID_EXR,
    AV_CODEC_ID_VP7,
    AV_CODEC_ID_SANM,
    AV_CODEC_ID_SGIRLE,
    AV_CODEC_ID_MVC1,
    AV_CODEC_ID_MVC2,
    AV_CODEC_ID_HQX,
    AV_CODEC_ID_TDSC,
    AV_CODEC_ID_HQ_HQA,
    AV_CODEC_ID_HAP,
    AV_CODEC_ID_DDS,
    AV_CODEC_ID_DXV,
    AV_CODEC_ID_SCREENPRESSO,
    AV_CODEC_ID_RSCC,
    AV_CODEC_ID_AVS2,
    AV_CODEC_ID_PGX,
    AV_CODEC_ID_AVS3,
    AV_CODEC_ID_MSP2,
    AV_CODEC_ID_VVC,
#define AV_CODEC_ID_H266 AV_CODEC_ID_VVC
    AV_CODEC_ID_Y41P,
    AV_CODEC_ID_AVRP,
    AV_CODEC_ID_012V,
    AV_CODEC_ID_AVUI,
    AV_CODEC_ID_TARGA_Y216,
    AV_CODEC_ID_V308,
    AV_CODEC_ID_V408,
    AV_CODEC_ID_YUV4,
    AV_CODEC_ID_AVRN,
    AV_CODEC_ID_CPIA,
    AV_CODEC_ID_XFACE,
    AV_CODEC_ID_SNOW,
    AV_CODEC_ID_SMVJPEG,
    AV_CODEC_ID_APNG,
    AV_CODEC_ID_DAALA,
    AV_CODEC_ID_CFHD,
    AV_CODEC_ID_TRUEMOTION2RT,
    AV_CODEC_ID_M101,
    AV_CODEC_ID_MAGICYUV,
    AV_CODEC_ID_SHEERVIDEO,
    AV_CODEC_ID_YLC,
    AV_CODEC_ID_PSD,
    AV_CODEC_ID_PIXLET,
    AV_CODEC_ID_SPEEDHQ,
    AV_CODEC_ID_FMVC,
    AV_CODEC_ID_SCPR,
    AV_CODEC_ID_CLEARVIDEO,
    AV_CODEC_ID_XPM,
    AV_CODEC_ID_AV1,
    AV_CODEC_ID_BITPACKED,
    AV_CODEC_ID_MSCC,
    AV_CODEC_ID_SRGC,
    AV_CODEC_ID_SVG,
    AV_CODEC_ID_GDV,
    AV_CODEC_ID_FITS,
    AV_CODEC_ID_IMM4,
    AV_CODEC_ID_PROSUMER,
    AV_CODEC_ID_MWSC,
    AV_CODEC_ID_WCMV,
    AV_CODEC_ID_RASC,
    AV_CODEC_ID_HYMT,
    AV_CODEC_ID_ARBC,
    AV_CODEC_ID_AGM,
    AV_CODEC_ID_LSCR,
    AV_CODEC_ID_VP4,
    AV_CODEC_ID_IMM5,
    AV_CODEC_ID_MVDV,
    AV_CODEC_ID_MVHA,
    AV_CODEC_ID_CDTOONS,
    AV_CODEC_ID_MV30,
    AV_CODEC_ID_NOTCHLC,
    AV_CODEC_ID_PFM,
    AV_CODEC_ID_MOBICLIP,
    AV_CODEC_ID_PHOTOCD,
    AV_CODEC_ID_IPU,
    AV_CODEC_ID_ARGO,
    AV_CODEC_ID_CRI,
    AV_CODEC_ID_SIMBIOSIS_IMX,
    AV_CODEC_ID_SGA_VIDEO,
    AV_CODEC_ID_GEM,
    AV_CODEC_ID_VBN,
    AV_CODEC_ID_JPEGXL,
    AV_CODEC_ID_QOI,
    AV_CODEC_ID_PHM,
    AV_CODEC_ID_RADIANCE_HDR,
    AV_CODEC_ID_WBMP,
    AV_CODEC_ID_MEDIA100,
    AV_CODEC_ID_VQC,
    AV_CODEC_ID_PDV,
    AV_CODEC_ID_EVC,
    AV_CODEC_ID_RTV1,
    AV_CODEC_ID_VMIX,
    AV_CODEC_ID_LEAD,
    AV_CODEC_ID_FIRST_AUDIO = 0x10000,
    AV_CODEC_ID_PCM_S16LE = 0x10000,
    AV_CODEC_ID_PCM_S16BE,
    AV_CODEC_ID_PCM_U16LE,
    AV_CODEC_ID_PCM_U16BE,
    AV_CODEC_ID_PCM_S8,
    AV_CODEC_ID_PCM_U8,
    AV_CODEC_ID_PCM_MULAW,
    AV_CODEC_ID_PCM_ALAW,
    AV_CODEC_ID_PCM_S32LE,
    AV_CODEC_ID_PCM_S32BE,
    AV_CODEC_ID_PCM_U32LE,
    AV_CODEC_ID_PCM_U32BE,
    AV_CODEC_ID_PCM_S24LE,
    AV_CODEC_ID_PCM_S24BE,
    AV_CODEC_ID_PCM_U24LE,
    AV_CODEC_ID_PCM_U24BE,
    AV_CODEC_ID_PCM_S24DAUD,
    AV_CODEC_ID_PCM_ZORK,
    AV_CODEC_ID_PCM_S16LE_PLANAR,
    AV_CODEC_ID_PCM_DVD,
    AV_CODEC_ID_PCM_F32BE,
    AV_CODEC_ID_PCM_F32LE,
    AV_CODEC_ID_PCM_F64BE,
    AV_CODEC_ID_PCM_F64LE,
    AV_CODEC_ID_PCM_BLURAY,
    AV_CODEC_ID_PCM_LXF,
    AV_CODEC_ID_S302M,
    AV_CODEC_ID_PCM_S8_PLANAR,
    AV_CODEC_ID_PCM_S24LE_PLANAR,
    AV_CODEC_ID_PCM_S32LE_PLANAR,
    AV_CODEC_ID_PCM_S16BE_PLANAR,
    AV_CODEC_ID_PCM_S64LE,
    AV_CODEC_ID_PCM_S64BE,
    AV_CODEC_ID_PCM_F16LE,
    AV_CODEC_ID_PCM_F24LE,
    AV_CODEC_ID_PCM_VIDC,
    AV_CODEC_ID_PCM_SGA,
    AV_CODEC_ID_ADPCM_IMA_QT = 0x11000,
    AV_CODEC_ID_ADPCM_IMA_WAV,
    AV_CODEC_ID_ADPCM_IMA_DK3,
    AV_CODEC_ID_ADPCM_IMA_DK4,
    AV_CODEC_ID_ADPCM_IMA_WS,
    AV_CODEC_ID_ADPCM_IMA_SMJPEG,
    AV_CODEC_ID_ADPCM_MS,
    AV_CODEC_ID_ADPCM_4XM,
    AV_CODEC_ID_ADPCM_XA,
    AV_CODEC_ID_ADPCM_ADX,
    AV_CODEC_ID_ADPCM_EA,
    AV_CODEC_ID_ADPCM_G726,
    AV_CODEC_ID_ADPCM_CT,
    AV_CODEC_ID_ADPCM_SWF,
    AV_CODEC_ID_ADPCM_YAMAHA,
    AV_CODEC_ID_ADPCM_SBPRO_4,
    AV_CODEC_ID_ADPCM_SBPRO_3,
    AV_CODEC_ID_ADPCM_SBPRO_2,
    AV_CODEC_ID_ADPCM_THP,
    AV_CODEC_ID_ADPCM_IMA_AMV,
    AV_CODEC_ID_ADPCM_EA_R1,
    AV_CODEC_ID_ADPCM_EA_R3,
    AV_CODEC_ID_ADPCM_EA_R2,
    AV_CODEC_ID_ADPCM_IMA_EA_SEAD,
    AV_CODEC_ID_ADPCM_IMA_EA_EACS,
    AV_CODEC_ID_ADPCM_EA_XAS,
    AV_CODEC_ID_ADPCM_EA_MAXIS_XA,
    AV_CODEC_ID_ADPCM_IMA_ISS,
    AV_CODEC_ID_ADPCM_G722,
    AV_CODEC_ID_ADPCM_IMA_APC,
    AV_CODEC_ID_ADPCM_VIMA,
    AV_CODEC_ID_ADPCM_AFC,
    AV_CODEC_ID_ADPCM_IMA_OKI,
    AV_CODEC_ID_ADPCM_DTK,
    AV_CODEC_ID_ADPCM_IMA_RAD,
    AV_CODEC_ID_ADPCM_G726LE,
    AV_CODEC_ID_ADPCM_THP_LE,
    AV_CODEC_ID_ADPCM_PSX,
    AV_CODEC_ID_ADPCM_AICA,
    AV_CODEC_ID_ADPCM_IMA_DAT4,
    AV_CODEC_ID_ADPCM_MTAF,
    AV_CODEC_ID_ADPCM_AGM,
    AV_CODEC_ID_ADPCM_ARGO,
    AV_CODEC_ID_ADPCM_IMA_SSI,
    AV_CODEC_ID_ADPCM_ZORK,
    AV_CODEC_ID_ADPCM_IMA_APM,
    AV_CODEC_ID_ADPCM_IMA_ALP,
    AV_CODEC_ID_ADPCM_IMA_MTF,
    AV_CODEC_ID_ADPCM_IMA_CUNNING,
    AV_CODEC_ID_ADPCM_IMA_MOFLEX,
    AV_CODEC_ID_ADPCM_IMA_ACORN,
    AV_CODEC_ID_ADPCM_XMD,
    AV_CODEC_ID_AMR_NB = 0x12000,
    AV_CODEC_ID_AMR_WB,
    AV_CODEC_ID_RA_144 = 0x13000,
    AV_CODEC_ID_RA_288,
    AV_CODEC_ID_ROQ_DPCM = 0x14000,
    AV_CODEC_ID_INTERPLAY_DPCM,
    AV_CODEC_ID_XAN_DPCM,
    AV_CODEC_ID_SOL_DPCM,
    AV_CODEC_ID_SDX2_DPCM,
    AV_CODEC_ID_GREMLIN_DPCM,
    AV_CODEC_ID_DERF_DPCM,
    AV_CODEC_ID_WADY_DPCM,
    AV_CODEC_ID_CBD2_DPCM,
    AV_CODEC_ID_MP2 = 0x15000,
    AV_CODEC_ID_MP3,
    AV_CODEC_ID_AAC,
    AV_CODEC_ID_AC3,
    AV_CODEC_ID_DTS,
    AV_CODEC_ID_VORBIS,
    AV_CODEC_ID_DVAUDIO,
    AV_CODEC_ID_WMAV1,
    AV_CODEC_ID_WMAV2,
    AV_CODEC_ID_MACE3,
    AV_CODEC_ID_MACE6,
    AV_CODEC_ID_VMDAUDIO,
    AV_CODEC_ID_FLAC,
    AV_CODEC_ID_MP3ADU,
    AV_CODEC_ID_MP3ON4,
    AV_CODEC_ID_SHORTEN,
    AV_CODEC_ID_ALAC,
    AV_CODEC_ID_WESTWOOD_SND1,
    AV_CODEC_ID_GSM,
    AV_CODEC_ID_QDM2,
    AV_CODEC_ID_COOK,
    AV_CODEC_ID_TRUESPEECH,
    AV_CODEC_ID_TTA,
    AV_CODEC_ID_SMACKAUDIO,
    AV_CODEC_ID_QCELP,
    AV_CODEC_ID_WAVPACK,
    AV_CODEC_ID_DSICINAUDIO,
    AV_CODEC_ID_IMC,
    AV_CODEC_ID_MUSEPACK7,
    AV_CODEC_ID_MLP,
    AV_CODEC_ID_GSM_MS,
    AV_CODEC_ID_ATRAC3,
    AV_CODEC_ID_APE,
    AV_CODEC_ID_NELLYMOSER,
    AV_CODEC_ID_MUSEPACK8,
    AV_CODEC_ID_SPEEX,
    AV_CODEC_ID_WMAVOICE,
    AV_CODEC_ID_WMAPRO,
    AV_CODEC_ID_WMALOSSLESS,
    AV_CODEC_ID_ATRAC3P,
    AV_CODEC_ID_EAC3,
    AV_CODEC_ID_SIPR,
    AV_CODEC_ID_MP1,
    AV_CODEC_ID_TWINVQ,
    AV_CODEC_ID_TRUEHD,
    AV_CODEC_ID_MP4ALS,
    AV_CODEC_ID_ATRAC1,
    AV_CODEC_ID_BINKAUDIO_RDFT,
    AV_CODEC_ID_BINKAUDIO_DCT,
    AV_CODEC_ID_AAC_LATM,
    AV_CODEC_ID_QDMC,
    AV_CODEC_ID_CELT,
    AV_CODEC_ID_G723_1,
    AV_CODEC_ID_G729,
    AV_CODEC_ID_8SVX_EXP,
    AV_CODEC_ID_8SVX_FIB,
    AV_CODEC_ID_BMV_AUDIO,
    AV_CODEC_ID_RALF,
    AV_CODEC_ID_IAC,
    AV_CODEC_ID_ILBC,
    AV_CODEC_ID_OPUS,
    AV_CODEC_ID_COMFORT_NOISE,
    AV_CODEC_ID_TAK,
    AV_CODEC_ID_METASOUND,
    AV_CODEC_ID_PAF_AUDIO,
    AV_CODEC_ID_ON2AVC,
    AV_CODEC_ID_DSS_SP,
    AV_CODEC_ID_CODEC2,
    AV_CODEC_ID_FFWAVESYNTH,
    AV_CODEC_ID_SONIC,
    AV_CODEC_ID_SONIC_LS,
    AV_CODEC_ID_EVRC,
    AV_CODEC_ID_SMV,
    AV_CODEC_ID_DSD_LSBF,
    AV_CODEC_ID_DSD_MSBF,
    AV_CODEC_ID_DSD_LSBF_PLANAR,
    AV_CODEC_ID_DSD_MSBF_PLANAR,
    AV_CODEC_ID_4GV,
    AV_CODEC_ID_INTERPLAY_ACM,
    AV_CODEC_ID_XMA1,
    AV_CODEC_ID_XMA2,
    AV_CODEC_ID_DST,
    AV_CODEC_ID_ATRAC3AL,
    AV_CODEC_ID_ATRAC3PAL,
    AV_CODEC_ID_DOLBY_E,
    AV_CODEC_ID_APTX,
    AV_CODEC_ID_APTX_HD,
    AV_CODEC_ID_SBC,
    AV_CODEC_ID_ATRAC9,
    AV_CODEC_ID_HCOM,
    AV_CODEC_ID_ACELP_KELVIN,
    AV_CODEC_ID_MPEGH_3D_AUDIO,
    AV_CODEC_ID_SIREN,
    AV_CODEC_ID_HCA,
    AV_CODEC_ID_FASTAUDIO,
    AV_CODEC_ID_MSNSIREN,
    AV_CODEC_ID_DFPWM,
    AV_CODEC_ID_BONK,
    AV_CODEC_ID_MISC4,
    AV_CODEC_ID_APAC,
    AV_CODEC_ID_FTR,
    AV_CODEC_ID_WAVARC,
    AV_CODEC_ID_RKA,
    AV_CODEC_ID_AC4,
    AV_CODEC_ID_OSQ,
    AV_CODEC_ID_QOA,
    AV_CODEC_ID_LC3,
    AV_CODEC_ID_FIRST_SUBTITLE = 0x17000,
    AV_CODEC_ID_DVD_SUBTITLE = 0x17000,
    AV_CODEC_ID_DVB_SUBTITLE,
    AV_CODEC_ID_TEXT,
    AV_CODEC_ID_XSUB,
    AV_CODEC_ID_SSA,
    AV_CODEC_ID_MOV_TEXT,
    AV_CODEC_ID_HDMV_PGS_SUBTITLE,
    AV_CODEC_ID_DVB_TELETEXT,
    AV_CODEC_ID_SRT,
    AV_CODEC_ID_MICRODVD,
    AV_CODEC_ID_EIA_608,
    AV_CODEC_ID_JACOSUB,
    AV_CODEC_ID_SAMI,
    AV_CODEC_ID_REALTEXT,
    AV_CODEC_ID_STL,
    AV_CODEC_ID_SUBVIEWER1,
    AV_CODEC_ID_SUBVIEWER,
    AV_CODEC_ID_SUBRIP,
    AV_CODEC_ID_WEBVTT,
    AV_CODEC_ID_MPL2,
    AV_CODEC_ID_VPLAYER,
    AV_CODEC_ID_PJS,
    AV_CODEC_ID_ASS,
    AV_CODEC_ID_HDMV_TEXT_SUBTITLE,
    AV_CODEC_ID_TTML,
    AV_CODEC_ID_ARIB_CAPTION,
    AV_CODEC_ID_FIRST_UNKNOWN = 0x18000,
    AV_CODEC_ID_TTF = 0x18000,
    AV_CODEC_ID_SCTE_35,
    AV_CODEC_ID_EPG,
    AV_CODEC_ID_BINTEXT,
    AV_CODEC_ID_XBIN,
    AV_CODEC_ID_IDF,
    AV_CODEC_ID_OTF,
    AV_CODEC_ID_SMPTE_KLV,
    AV_CODEC_ID_DVD_NAV,
    AV_CODEC_ID_TIMED_ID3,
    AV_CODEC_ID_BIN_DATA,
    AV_CODEC_ID_SMPTE_2038,
    AV_CODEC_ID_LCEVC,
    AV_CODEC_ID_PROBE = 0x19000,
    AV_CODEC_ID_MPEG2TS = 0x20000,
    AV_CODEC_ID_MPEG4SYSTEMS = 0x20001,
    AV_CODEC_ID_FFMETADATA = 0x21000,
    AV_CODEC_ID_WRAPPED_AVFRAME = 0x21001,
    AV_CODEC_ID_VNULL,
    AV_CODEC_ID_ANULL,
};

enum AVFieldOrder {
    AV_FIELD_UNKNOWN,
    AV_FIELD_PROGRESSIVE,
    AV_FIELD_TT,
    AV_FIELD_BB,
    AV_FIELD_TB,
    AV_FIELD_BT,
};

enum AVAudioServiceType {
    AV_AUDIO_SERVICE_TYPE_MAIN              = 0,
    AV_AUDIO_SERVICE_TYPE_EFFECTS           = 1,
    AV_AUDIO_SERVICE_TYPE_VISUALLY_IMPAIRED = 2,
    AV_AUDIO_SERVICE_TYPE_HEARING_IMPAIRED  = 3,
    AV_AUDIO_SERVICE_TYPE_DIALOGUE          = 4,
    AV_AUDIO_SERVICE_TYPE_COMMENTARY        = 5,
    AV_AUDIO_SERVICE_TYPE_EMERGENCY         = 6,
    AV_AUDIO_SERVICE_TYPE_VOICE_OVER        = 7,
    AV_AUDIO_SERVICE_TYPE_KARAOKE           = 8,
    AV_AUDIO_SERVICE_TYPE_NB,
};

enum AVSampleFormat {
    AV_SAMPLE_FMT_NONE = -1,
    AV_SAMPLE_FMT_U8,
    AV_SAMPLE_FMT_S16,
    AV_SAMPLE_FMT_S32,
    AV_SAMPLE_FMT_FLT,
    AV_SAMPLE_FMT_DBL,
    AV_SAMPLE_FMT_U8P,
    AV_SAMPLE_FMT_S16P,
    AV_SAMPLE_FMT_S32P,
    AV_SAMPLE_FMT_FLTP,
    AV_SAMPLE_FMT_DBLP,
    AV_SAMPLE_FMT_S64,
    AV_SAMPLE_FMT_S64P,
    AV_SAMPLE_FMT_NB
};

enum AVDiscard{
    AVDISCARD_NONE    =-16,
    AVDISCARD_DEFAULT =  0,
    AVDISCARD_NONREF  =  8,
    AVDISCARD_BIDIR   = 16,
    AVDISCARD_NONINTRA= 24,
    AVDISCARD_NONKEY  = 32,
    AVDISCARD_ALL     = 48,
};

enum AVStreamGroupParamsType {
    AV_STREAM_GROUP_PARAMS_NONE,
    AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT,
    AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION,
    AV_STREAM_GROUP_PARAMS_TILE_GRID,
    AV_STREAM_GROUP_PARAMS_LCEVC,
};

enum AVDurationEstimationMethod {
    AVFMT_DURATION_FROM_PTS,
    AVFMT_DURATION_FROM_STREAM,
    AVFMT_DURATION_FROM_BITRATE
};

typedef enum {
    AV_CLASS_CATEGORY_NA = 0,
    AV_CLASS_CATEGORY_INPUT,
    AV_CLASS_CATEGORY_OUTPUT,
    AV_CLASS_CATEGORY_MUXER,
    AV_CLASS_CATEGORY_DEMUXER,
    AV_CLASS_CATEGORY_ENCODER,
    AV_CLASS_CATEGORY_DECODER,
    AV_CLASS_CATEGORY_FILTER,
    AV_CLASS_CATEGORY_BITSTREAM_FILTER,
    AV_CLASS_CATEGORY_SWSCALER,
    AV_CLASS_CATEGORY_SWRESAMPLER,
    AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT = 40,
    AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT,
    AV_CLASS_CATEGORY_DEVICE_AUDIO_OUTPUT,
    AV_CLASS_CATEGORY_DEVICE_AUDIO_INPUT,
    AV_CLASS_CATEGORY_DEVICE_OUTPUT,
    AV_CLASS_CATEGORY_DEVICE_INPUT,
    AV_CLASS_CATEGORY_NB
}AVClassCategory;

// Structs
typedef struct AVRational{
    int num;
    int den;
} AVRational;

typedef struct AVBuffer AVBuffer;

typedef struct AVBufferRef {
    AVBuffer *buffer;
    uint8_t *data;
    size_t   size;
} AVBufferRef;

typedef struct AVPacketSideData {
    uint8_t *data;
    size_t   size;
    enum AVPacketSideDataType type;
} AVPacketSideData;

typedef struct AVPacket {
    AVBufferRef *buf;
    int64_t pts;
    int64_t dts;
    uint8_t *data;
    int   size;
    int   stream_index;
    int   flags;
    AVPacketSideData *side_data;
    int side_data_elems;
    int64_t duration;
    int64_t pos;
    void *opaque;
    AVBufferRef *opaque_ref;
    AVRational time_base;
} AVPacket;

typedef struct AVDictionaryEntry {
    char *key;
    char *value;
} AVDictionaryEntry;

typedef struct AVDictionary AVDictionary;

typedef struct AVFrameSideData {
    enum AVFrameSideDataType type;
    uint8_t *data;
    size_t   size;
    AVDictionary *metadata;
    AVBufferRef *buf;
} AVFrameSideData;

typedef struct AVChannelCustom {
    enum AVChannel id;
    char name[16];
    void *opaque;
} AVChannelCustom;

typedef struct AVChannelLayout {
    enum AVChannelOrder order;
    int nb_channels;
    union {
        uint64_t mask;
        AVChannelCustom *map;
    } u;
    void *opaque;
} AVChannelLayout;

typedef struct AVFrame {
#define AV_NUM_DATA_POINTERS 8
    uint8_t *data[8];
    int linesize[8];
    uint8_t **extended_data;
    int width, height;
    int nb_samples;
    int format;
    enum AVPictureType pict_type;
    AVRational sample_aspect_ratio;
    int64_t pts;
    int64_t pkt_dts;
    AVRational time_base;
    int quality;
    void *opaque;
    int repeat_pict;
    int sample_rate;
    AVBufferRef *buf[8];
    AVBufferRef **extended_buf;
    int        nb_extended_buf;
    AVFrameSideData **side_data;
    int            nb_side_data;
#define AV_FRAME_FLAG_CORRUPT       (1 << 0)
#define AV_FRAME_FLAG_KEY (1 << 1)
#define AV_FRAME_FLAG_DISCARD   (1 << 2)
#define AV_FRAME_FLAG_INTERLACED (1 << 3)
#define AV_FRAME_FLAG_TOP_FIELD_FIRST (1 << 4)
    int flags;
    enum AVColorRange color_range;
    enum AVColorPrimaries color_primaries;
    enum AVColorTransferCharacteristic color_trc;
    int colorspace;
    enum AVChromaLocation chroma_location;
    int64_t best_effort_timestamp;
    AVDictionary *metadata;
    int decode_error_flags;
#define FF_DECODE_ERROR_INVALID_BITSTREAM   1
#define FF_DECODE_ERROR_MISSING_REFERENCE   2
#define FF_DECODE_ERROR_CONCEALMENT_ACTIVE  4
#define FF_DECODE_ERROR_DECODE_SLICES       8
    AVBufferRef *hw_frames_ctx;
    AVBufferRef *opaque_ref;
    size_t crop_top;
    size_t crop_bottom;
    size_t crop_left;
    size_t crop_right;
    void *private_ref;
    AVChannelLayout ch_layout;
    int64_t duration;
    enum AVAlphaMode alpha_mode;
} AVFrame;

typedef struct AVClass {
    const char* class_name;
    const char* (*item_name)(void* ctx);
    const struct AVOption *option;
    int version;
    int log_level_offset_offset;
    int parent_log_context_offset;
    AVClassCategory category;
    AVClassCategory (*get_category)(void* ctx);
    int (*query_ranges)(struct AVOptionRanges **, void *obj, const char *key, int flags);
    void* (*child_next)(void *obj, void *prev);
    const struct AVClass* (*child_class_iterate)(void **iter);
} AVClass;

typedef struct AVProfile {
    int profile;
    const char *name;
} AVProfile;

typedef struct AVCodec {
    const char *name;
    const char *long_name;
    enum AVMediaType type;
    enum AVCodecID id;
    int capabilities;
    uint8_t max_lowres;
    const AVClass *priv_class;
    const AVProfile *profiles;
    const char *wrapper_name;
} AVCodec;

enum AVCodecConfig {
    AV_CODEC_CONFIG_PIX_FORMAT,
    AV_CODEC_CONFIG_FRAME_RATE,
    AV_CODEC_CONFIG_SAMPLE_RATE,
    AV_CODEC_CONFIG_SAMPLE_FORMAT,
    AV_CODEC_CONFIG_CHANNEL_LAYOUT,
    AV_CODEC_CONFIG_COLOR_RANGE,
    AV_CODEC_CONFIG_COLOR_SPACE,
    AV_CODEC_CONFIG_ALPHA_MODE,
};

typedef struct RcOverride{
    int start_frame;
    int end_frame;
    int qscale;
    float quality_factor;
} RcOverride;

typedef struct AVCodecContext {
    const AVClass *av_class;
    int log_level_offset;
    enum AVMediaType codec_type;
    const struct AVCodec  *codec;
    enum AVCodecID     codec_id;
    unsigned int codec_tag;
    void *priv_data;
    struct AVCodecInternal *internal;
    void *opaque;
    int64_t bit_rate;
    int flags;
    int flags2;
    uint8_t *extradata;
    int extradata_size;
    AVRational time_base;
    AVRational pkt_timebase;
    AVRational framerate;
    int delay;
    int width, height;
    int coded_width, coded_height;
    AVRational sample_aspect_ratio;
    enum AVPixelFormat pix_fmt;
    enum AVPixelFormat sw_pix_fmt;
    enum AVColorPrimaries color_primaries;
    enum AVColorTransferCharacteristic color_trc;
    enum AVColorSpace colorspace;
    enum AVColorRange color_range;
    enum AVChromaLocation chroma_sample_location;
    enum AVFieldOrder field_order;
    int refs;
    int has_b_frames;
    int slice_flags;
    void (*draw_horiz_band)(struct AVCodecContext *s, const AVFrame *src, int offset[8], int y, int type, int height);
    enum AVPixelFormat (*get_format)(struct AVCodecContext *s, const enum AVPixelFormat * fmt);
    int max_b_frames;
    float b_quant_factor;
    float b_quant_offset;
    float i_quant_factor;
    float i_quant_offset;
    float lumi_masking;
    float temporal_cplx_masking;
    float spatial_cplx_masking;
    float p_masking;
    float dark_masking;
    int nsse_weight;
    int me_cmp;
    int me_sub_cmp;
    int mb_cmp;
    int ildct_cmp;
    int dia_size;
    int last_predictor_count;
    int me_pre_cmp;
    int pre_dia_size;
    int me_subpel_quality;
    int me_range;
    int mb_decision;
    uint16_t *intra_matrix;
    uint16_t *inter_matrix;
    uint16_t *chroma_intra_matrix;
    int intra_dc_precision;
    int mb_lmin;
    int mb_lmax;
    int bidir_refine;
    int keyint_min;
    int gop_size;
    int mv0_threshold;
    int slices;
    int sample_rate;
    enum AVSampleFormat sample_fmt;
    AVChannelLayout ch_layout;
    int frame_size;
    int block_align;
    int cutoff;
    enum AVAudioServiceType audio_service_type;
    enum AVSampleFormat request_sample_fmt;
    int initial_padding;
    int trailing_padding;
    int seek_preroll;
    int (*get_buffer2)(struct AVCodecContext *s, AVFrame *frame, int flags);
    int bit_rate_tolerance;
    int global_quality;
    int compression_level;
    float qcompress;
    float qblur;
    int qmin;
    int qmax;
    int max_qdiff;
    int rc_buffer_size;
    int rc_override_count;
    RcOverride *rc_override;
    int64_t rc_max_rate;
    int64_t rc_min_rate;
    float rc_max_available_vbv_use;
    float rc_min_vbv_overflow_use;
    int rc_initial_buffer_occupancy;
    int trellis;
    char *stats_out;
    char *stats_in;
    int workaround_bugs;
    int strict_std_compliance;
    int error_concealment;
    int debug;
    int err_recognition;
    const struct AVHWAccel *hwaccel;
    void *hwaccel_context;
    AVBufferRef *hw_frames_ctx;
    AVBufferRef *hw_device_ctx;
    int hwaccel_flags;
    int extra_hw_frames;
    uint64_t error[8];
    int dct_algo;
    int idct_algo;
    int bits_per_coded_sample;
    int bits_per_raw_sample;
    int thread_count;
    int thread_type;
    int active_thread_type;
    int (*execute)(struct AVCodecContext *c, int (*func)(struct AVCodecContext *c2, void *arg), void *arg2, int *ret, int count, int size);
    int (*execute2)(struct AVCodecContext *c, int (*func)(struct AVCodecContext *c2, void *arg, int jobnr, int threadnr), void *arg2, int *ret, int count);
    int profile;
} AVCodecContext;

typedef struct AVCodecDescriptor {
    enum AVCodecID     id;
    enum AVMediaType type;
    const char      *name;
    const char *long_name;
    int             props;
    const char *const *mime_types;
    const struct AVProfile *profiles;
} AVCodecDescriptor;

typedef struct AVComponentDescriptor {
    int plane;
    int step;
    int offset;
    int shift;
    int depth;
} AVComponentDescriptor;

typedef struct AVPixFmtDescriptor {
    const char *name;
    uint8_t nb_components;
    uint8_t log2_chroma_w;
    uint8_t log2_chroma_h;
    uint64_t flags;
    AVComponentDescriptor comp[4];
    const char *alias;
} AVPixFmtDescriptor;

typedef struct AVCodecParameters {
    enum AVMediaType codec_type;
    enum AVCodecID codec_id;
    uint32_t codec_tag;
    uint8_t *extradata;
    int extradata_size;
    AVPacketSideData *coded_side_data;
    int nb_coded_side_data;
    int format;
    int64_t bit_rate;
    int bits_per_coded_sample;
    int bits_per_raw_sample;
    int profile;
    int level;
    int width;
    int height;
    AVRational sample_aspect_ratio;
    AVRational framerate;
    enum AVFieldOrder field_order;
    enum AVColorRange color_range;
    enum AVColorPrimaries color_primaries;
    enum AVColorTransferCharacteristic color_trc;
    enum AVColorSpace color_space;
    enum AVChromaLocation chroma_location;
    int video_delay;
    AVChannelLayout ch_layout;
    int sample_rate;
    int block_align;
    int frame_size;
    int initial_padding;
    int trailing_padding;
    int seek_preroll;
} AVCodecParameters;

typedef struct AVStream {
    const AVClass *av_class;
    int index;
    int id;
    AVCodecParameters *codecpar;
    void *priv_data;
    AVRational time_base;
    int64_t start_time;
    int64_t duration;
    int64_t nb_frames;
    int disposition;
    enum AVDiscard discard;
    AVRational sample_aspect_ratio;
    AVDictionary *metadata;
    AVRational avg_frame_rate;
    AVPacket attached_pic;
    int event_flags;
    AVRational r_frame_rate;
    int pts_wrap_bits;
} AVStream;

typedef struct AVStreamGroup {
    const AVClass *av_class;
    void *priv_data;
    unsigned int index;
    int64_t id;
    enum AVStreamGroupParamsType type;
    union {
        struct AVIAMFAudioElement *iamf_audio_element;
        struct AVIAMFMixPresentation *iamf_mix_presentation;
        struct AVStreamGroupTileGrid *tile_grid;
        struct AVStreamGroupLCEVC *lcevc;
    } params;
    AVDictionary *metadata;
    unsigned int nb_streams;
    AVStream **streams;
    int disposition;
} AVStreamGroup;

typedef struct AVIOContext {
    const AVClass *av_class;
    unsigned char *buffer;
    int buffer_size;
    unsigned char *buf_ptr;
    unsigned char *buf_end;
    void *opaque;
    int (*read_packet)(void *opaque, uint8_t *buf, int buf_size);
    int (*write_packet)(void *opaque, const uint8_t *buf, int buf_size);
    int64_t (*seek)(void *opaque, int64_t offset, int whence);
    int64_t pos;
    int eof_reached;
    int error;
    int write_flag;
    int max_packet_size;
    int min_packet_size;
    unsigned long checksum;
    unsigned char *checksum_ptr;
    unsigned long (*update_checksum)(unsigned long checksum, const uint8_t *buf, unsigned int size);
    int (*read_pause)(void *opaque, int pause);
    int64_t (*read_seek)(void *opaque, int stream_index, int64_t timestamp, int flags);
    int seekable;
    int direct;
    const char *protocol_whitelist;
    const char *protocol_blacklist;
    int (*write_data_type)(void *opaque, const uint8_t *buf, int buf_size, enum AVIODataMarkerType type, int64_t time);
    int ignore_boundary_point;
    unsigned char *buf_ptr_max;
    int64_t bytes_read;
    int64_t bytes_written;
} AVIOContext;

typedef struct AVChapter {
    int64_t id;
    AVRational time_base;
    int64_t start, end;
    AVDictionary *metadata;
} AVChapter;

typedef struct AVProgram {
    int            id;
    int            flags;
    enum AVDiscard discard;
    unsigned int   *stream_index;
    unsigned int   nb_stream_indexes;
    AVDictionary *metadata;
    int program_num;
    int pmt_pid;
    int pcr_pid;
    int pmt_version;
    int64_t start_time;
    int64_t end_time;
    int64_t pts_wrap_reference;
    int pts_wrap_behavior;
} AVProgram;

typedef struct AVIOInterruptCB {
    int (*callback)(void*);
    void *opaque;
} AVIOInterruptCB;

typedef int (*av_format_control_message)(struct AVFormatContext *s, int type, void *data, size_t data_size);

typedef struct AVFormatContext {
    const AVClass *av_class;
    const struct AVInputFormat *iformat;
    const struct AVOutputFormat *oformat;
    void *priv_data;
    AVIOContext *pb;
    int ctx_flags;
    unsigned int nb_streams;
    AVStream **streams;
    unsigned int nb_stream_groups;
    AVStreamGroup **stream_groups;
    unsigned int nb_chapters;
    AVChapter **chapters;
    char *url;
    int64_t start_time;
    int64_t duration;
    int64_t bit_rate;
    unsigned int packet_size;
    int max_delay;
    int flags;
    int64_t probesize;
    int64_t max_analyze_duration;
    const uint8_t *key;
    int keylen;
    unsigned int nb_programs;
    AVProgram **programs;
    enum AVCodecID video_codec_id;
    enum AVCodecID audio_codec_id;
    enum AVCodecID subtitle_codec_id;
    enum AVCodecID data_codec_id;
    AVDictionary *metadata;
    int64_t start_time_realtime;
    int fps_probe_size;
    int error_recognition;
    AVIOInterruptCB interrupt_callback;
    int debug;
    int max_streams;
    unsigned int max_index_size;
    unsigned int max_picture_buffer;
    int64_t max_interleave_delta;
    int max_ts_probe;
    int max_chunk_duration;
    int max_chunk_size;
    int max_probe_packets;
    int strict_std_compliance;
    int event_flags;
    int avoid_negative_ts;
    int audio_preload;
    int use_wallclock_as_timestamps;
    int skip_estimate_duration_from_pts;
    int avio_flags;
    enum AVDurationEstimationMethod duration_estimation_method;
    int64_t skip_initial_bytes;
    unsigned int correct_ts_overflow;
    int seek2any;
    int flush_packets;
    int probe_score;
    int format_probesize;
    char *codec_whitelist;
    char *format_whitelist;
    char *protocol_whitelist;
    char *protocol_blacklist;
    int io_repositioned;
    const struct AVCodec *video_codec;
    const struct AVCodec *audio_codec;
    const struct AVCodec *subtitle_codec;
    const struct AVCodec *data_codec;
    int metadata_header_padding;
    void *opaque;
    av_format_control_message control_message_cb;
    int64_t output_ts_offset;
    uint8_t *dump_separator;
    int (*io_open)(struct AVFormatContext *s, AVIOContext **pb, const char *url, int flags, AVDictionary **options);
    int (*io_close2)(struct AVFormatContext *s, AVIOContext *pb);
    int64_t duration_probesize;
} AVFormatContext;

// Functions
int av_get_pix_fmt_loss(enum AVPixelFormat dst_pix_fmt, enum AVPixelFormat src_pix_fmt, int has_alpha);
AVFrame *av_frame_alloc(void);
int av_frame_get_buffer(AVFrame *frame, int align);
int av_image_get_buffer_size(enum AVPixelFormat pix_fmt, int width, int height, int align);
const AVCodec *avcodec_find_encoder_by_name(const char *name);
AVCodecContext *avcodec_alloc_context3(const AVCodec *codec);
int av_dict_set(AVDictionary **pm, const char *key, const char *value, int flags);
int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);
int avcodec_send_frame(AVCodecContext *avctx, const AVFrame *frame);
AVPacket *av_packet_alloc(void);
int avcodec_receive_packet(AVCodecContext *avctx, AVPacket *avpkt);
const AVCodec *avcodec_find_decoder(enum AVCodecID id);
int avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt);
int avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame);
int av_strerror(int errnum, char *errbuf, size_t errbuf_size);
void avcodec_free_context(AVCodecContext **avctx);
void av_frame_free(AVFrame **frame);
void av_dict_free(AVDictionary **m);
void av_packet_free(AVPacket **pkt);
enum AVPixelFormat av_get_pix_fmt(const char *name);
int av_image_fill_arrays(uint8_t *dst_data[4], int dst_linesize[4], const uint8_t *src, enum AVPixelFormat pix_fmt, int width, int height, int align);
void av_log_set_level(int level);
int av_image_alloc(uint8_t * pointers[8], int linesizes[8], int w, int h, enum AVPixelFormat pix_fmt, int align);
const AVPixFmtDescriptor * av_pix_fmt_desc_get(enum AVPixelFormat pix_fmt);
const AVCodecDescriptor *avcodec_descriptor_get_by_name(const char *name);
const AVCodecDescriptor *avcodec_descriptor_get(enum AVCodecID id);
int av_codec_is_encoder(const AVCodec *codec);
int av_codec_is_decoder(const AVCodec *codec);
int avcodec_is_open(AVCodecContext *s);
AVDictionaryEntry *av_dict_get(const AVDictionary *m, const char *key, const AVDictionaryEntry *prev, int flags);
const char *av_version_info(void);
void av_free(void *ptr);
void av_freep(void *ptr);
const AVCodec *avcodec_find_encoder(enum AVCodecID id);
int avcodec_parameters_to_context(AVCodecContext *codec, const struct AVCodecParameters *par);
void av_packet_unref(AVPacket *pkt);
const AVCodec *avcodec_find_decoder_by_name(const char *name);
int av_new_packet(AVPacket *pkt, int size);
int avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt);
enum AVSampleFormat av_get_sample_fmt(const char *name);
int av_channel_layout_from_string(AVChannelLayout *channel_layout, const char *str);
int avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame);
const char *av_get_sample_fmt_name(enum AVSampleFormat sample_fmt);
int av_channel_layout_describe(const AVChannelLayout *channel_layout, char *buf, size_t buf_size);
int av_samples_get_buffer_size(int *linesize, int nb_channels, int nb_samples, enum AVSampleFormat sample_fmt, int align);
void *av_malloc(size_t size);
int avcodec_fill_audio_frame(AVFrame *frame, int nb_channels, enum AVSampleFormat sample_fmt, const uint8_t *buf, int buf_size, int align);
int av_sample_fmt_is_planar(enum AVSampleFormat sample_fmt);
int av_get_bytes_per_sample(enum AVSampleFormat sample_fmt);
enum AVSampleFormat av_get_planar_sample_fmt(enum AVSampleFormat sample_fmt);
enum AVChannel av_channel_layout_channel_from_index(const AVChannelLayout *channel_layout, unsigned int idx);
int av_channel_name(char *buf, size_t buf_size, enum AVChannel channel);
int av_channel_description(char *buf, size_t buf_size, enum AVChannel channel);
const char *av_get_media_type_string(enum AVMediaType media_type);
const AVCodec *av_codec_iterate(void **opaque);
int avcodec_get_supported_config(const AVCodecContext *avctx, const AVCodec *codec, enum AVCodecConfig config, unsigned flags, const void **out_configs, int *out_num_configs);
void *av_realloc(void *ptr, size_t size);
const AVCodec *avcodec_find_encoder(enum AVCodecID id);
void av_log(void* avcl, int level, const char *fmt, ...);
void av_log_set_callback(void (*callback)(void*, int, const char*, va_list));
int av_dict_count(const AVDictionary *m);
// Enums
enum AVMediaType {
    AVMEDIA_TYPE_UNKNOWN = -1,
    AVMEDIA_TYPE_VIDEO,
    AVMEDIA_TYPE_AUDIO,
    AVMEDIA_TYPE_DATA,
    AVMEDIA_TYPE_SUBTITLE,
    AVMEDIA_TYPE_ATTACHMENT,
    AVMEDIA_TYPE_NB
};

enum AVCodecID {
    AV_CODEC_ID_NONE,
    AV_CODEC_ID_MPEG1VIDEO,
    AV_CODEC_ID_MPEG2VIDEO,
    AV_CODEC_ID_H261,
    AV_CODEC_ID_H263,
    AV_CODEC_ID_RV10,
    AV_CODEC_ID_RV20,
    AV_CODEC_ID_MJPEG,
    AV_CODEC_ID_MJPEGB,
    AV_CODEC_ID_LJPEG,
    AV_CODEC_ID_SP5X,
    AV_CODEC_ID_JPEGLS,
    AV_CODEC_ID_MPEG4,
    AV_CODEC_ID_RAWVIDEO,
    AV_CODEC_ID_MSMPEG4V1,
    AV_CODEC_ID_MSMPEG4V2,
    AV_CODEC_ID_MSMPEG4V3,
    AV_CODEC_ID_WMV1,
    AV_CODEC_ID_WMV2,
    AV_CODEC_ID_H263P,
    AV_CODEC_ID_H263I,
    AV_CODEC_ID_FLV1,
    AV_CODEC_ID_SVQ1,
    AV_CODEC_ID_SVQ3,
    AV_CODEC_ID_DVVIDEO,
    AV_CODEC_ID_HUFFYUV,
    AV_CODEC_ID_CYUV,
    AV_CODEC_ID_H264,
    AV_CODEC_ID_INDEO3,
    AV_CODEC_ID_VP3,
    AV_CODEC_ID_THEORA,
    AV_CODEC_ID_ASV1,
    AV_CODEC_ID_ASV2,
    AV_CODEC_ID_FFV1,
    AV_CODEC_ID_4XM,
    AV_CODEC_ID_VCR1,
    AV_CODEC_ID_CLJR,
    AV_CODEC_ID_MDEC,
    AV_CODEC_ID_ROQ,
    AV_CODEC_ID_INTERPLAY_VIDEO,
    AV_CODEC_ID_XAN_WC3,
    AV_CODEC_ID_XAN_WC4,
    AV_CODEC_ID_RPZA,
    AV_CODEC_ID_CINEPAK,
    AV_CODEC_ID_WS_VQA,
    AV_CODEC_ID_MSRLE,
    AV_CODEC_ID_MSVIDEO1,
    AV_CODEC_ID_IDCIN,
    AV_CODEC_ID_8BPS,
    AV_CODEC_ID_SMC,
    AV_CODEC_ID_FLIC,
    AV_CODEC_ID_TRUEMOTION1,
    AV_CODEC_ID_VMDVIDEO,
    AV_CODEC_ID_MSZH,
    AV_CODEC_ID_ZLIB,
    AV_CODEC_ID_QTRLE,
    AV_CODEC_ID_TSCC,
    AV_CODEC_ID_ULTI,
    AV_CODEC_ID_QDRAW,
    AV_CODEC_ID_VIXL,
    AV_CODEC_ID_QPEG,
    AV_CODEC_ID_PNG,
    AV_CODEC_ID_PPM,
    AV_CODEC_ID_PBM,
    AV_CODEC_ID_PGM,
    AV_CODEC_ID_PGMYUV,
    AV_CODEC_ID_PAM,
    AV_CODEC_ID_FFVHUFF,
    AV_CODEC_ID_RV30,
    AV_CODEC_ID_RV40,
    AV_CODEC_ID_VC1,
    AV_CODEC_ID_WMV3,
    AV_CODEC_ID_LOCO,
    AV_CODEC_ID_WNV1,
    AV_CODEC_ID_AASC,
    AV_CODEC_ID_INDEO2,
    AV_CODEC_ID_FRAPS,
    AV_CODEC_ID_TRUEMOTION2,
    AV_CODEC_ID_BMP,
    AV_CODEC_ID_CSCD,
    AV_CODEC_ID_MMVIDEO,
    AV_CODEC_ID_ZMBV,
    AV_CODEC_ID_AVS,
    AV_CODEC_ID_SMACKVIDEO,
    AV_CODEC_ID_NUV,
    AV_CODEC_ID_KMVC,
    AV_CODEC_ID_FLASHSV,
    AV_CODEC_ID_CAVS,
    AV_CODEC_ID_JPEG2000,
    AV_CODEC_ID_VMNC,
    AV_CODEC_ID_VP5,
    AV_CODEC_ID_VP6,
    AV_CODEC_ID_VP6F,
    AV_CODEC_ID_TARGA,
    AV_CODEC_ID_DSICINVIDEO,
    AV_CODEC_ID_TIERTEXSEQVIDEO,
    AV_CODEC_ID_TIFF,
    AV_CODEC_ID_GIF,
    AV_CODEC_ID_DXA,
    AV_CODEC_ID_DNXHD,
    AV_CODEC_ID_THP,
    AV_CODEC_ID_SGI,
    AV_CODEC_ID_C93,
    AV_CODEC_ID_BETHSOFTVID,
    AV_CODEC_ID_PTX,
    AV_CODEC_ID_TXD,
    AV_CODEC_ID_VP6A,
    AV_CODEC_ID_AMV,
    AV_CODEC_ID_VB,
    AV_CODEC_ID_PCX,
    AV_CODEC_ID_SUNRAST,
    AV_CODEC_ID_INDEO4,
    AV_CODEC_ID_INDEO5,
    AV_CODEC_ID_MIMIC,
    AV_CODEC_ID_RL2,
    AV_CODEC_ID_ESCAPE124,
    AV_CODEC_ID_DIRAC,
    AV_CODEC_ID_BFI,
    AV_CODEC_ID_CMV,
    AV_CODEC_ID_MOTIONPIXELS,
    AV_CODEC_ID_TGV,
    AV_CODEC_ID_TGQ,
    AV_CODEC_ID_TQI,
    AV_CODEC_ID_AURA,
    AV_CODEC_ID_AURA2,
    AV_CODEC_ID_V210X,
    AV_CODEC_ID_TMV,
    AV_CODEC_ID_V210,
    AV_CODEC_ID_DPX,
    AV_CODEC_ID_MAD,
    AV_CODEC_ID_FRWU,
    AV_CODEC_ID_FLASHSV2,
    AV_CODEC_ID_CDGRAPHICS,
    AV_CODEC_ID_R210,
    AV_CODEC_ID_ANM,
    AV_CODEC_ID_BINKVIDEO,
    AV_CODEC_ID_IFF_ILBM,
#define AV_CODEC_ID_IFF_BYTERUN1 AV_CODEC_ID_IFF_ILBM
    AV_CODEC_ID_KGV1,
    AV_CODEC_ID_YOP,
    AV_CODEC_ID_VP8,
    AV_CODEC_ID_PICTOR,
    AV_CODEC_ID_ANSI,
    AV_CODEC_ID_A64_MULTI,
    AV_CODEC_ID_A64_MULTI5,
    AV_CODEC_ID_R10K,
    AV_CODEC_ID_MXPEG,
    AV_CODEC_ID_LAGARITH,
    AV_CODEC_ID_PRORES,
    AV_CODEC_ID_JV,
    AV_CODEC_ID_DFA,
    AV_CODEC_ID_WMV3IMAGE,
    AV_CODEC_ID_VC1IMAGE,
    AV_CODEC_ID_UTVIDEO,
    AV_CODEC_ID_BMV_VIDEO,
    AV_CODEC_ID_VBLE,
    AV_CODEC_ID_DXTORY,
    AV_CODEC_ID_V410,
    AV_CODEC_ID_XWD,
    AV_CODEC_ID_CDXL,
    AV_CODEC_ID_XBM,
    AV_CODEC_ID_ZEROCODEC,
    AV_CODEC_ID_MSS1,
    AV_CODEC_ID_MSA1,
    AV_CODEC_ID_TSCC2,
    AV_CODEC_ID_MTS2,
    AV_CODEC_ID_CLLC,
    AV_CODEC_ID_MSS2,
    AV_CODEC_ID_VP9,
    AV_CODEC_ID_AIC,
    AV_CODEC_ID_ESCAPE130,
    AV_CODEC_ID_G2M,
    AV_CODEC_ID_WEBP,
    AV_CODEC_ID_HNM4_VIDEO,
    AV_CODEC_ID_HEVC,
#define AV_CODEC_ID_H265 AV_CODEC_ID_HEVC
    AV_CODEC_ID_FIC,
    AV_CODEC_ID_ALIAS_PIX,
    AV_CODEC_ID_BRENDER_PIX,
    AV_CODEC_ID_PAF_VIDEO,
    AV_CODEC_ID_EXR,
    AV_CODEC_ID_VP7,
    AV_CODEC_ID_SANM,
    AV_CODEC_ID_SGIRLE,
    AV_CODEC_ID_MVC1,
    AV_CODEC_ID_MVC2,
    AV_CODEC_ID_HQX,
    AV_CODEC_ID_TDSC,
    AV_CODEC_ID_HQ_HQA,
    AV_CODEC_ID_HAP,
    AV_CODEC_ID_DDS,
    AV_CODEC_ID_DXV,
    AV_CODEC_ID_SCREENPRESSO,
    AV_CODEC_ID_RSCC,
    AV_CODEC_ID_AVS2,
    AV_CODEC_ID_PGX,
    AV_CODEC_ID_AVS3,
    AV_CODEC_ID_MSP2,
    AV_CODEC_ID_VVC,
#define AV_CODEC_ID_H266 AV_CODEC_ID_VVC
    AV_CODEC_ID_Y41P,
    AV_CODEC_ID_AVRP,
    AV_CODEC_ID_012V,
    AV_CODEC_ID_AVUI,
    AV_CODEC_ID_TARGA_Y216,
    AV_CODEC_ID_V308,
    AV_CODEC_ID_V408,
    AV_CODEC_ID_YUV4,
    AV_CODEC_ID_AVRN,
    AV_CODEC_ID_CPIA,
    AV_CODEC_ID_XFACE,
    AV_CODEC_ID_SNOW,
    AV_CODEC_ID_SMVJPEG,
    AV_CODEC_ID_APNG,
    AV_CODEC_ID_DAALA,
    AV_CODEC_ID_CFHD,
    AV_CODEC_ID_TRUEMOTION2RT,
    AV_CODEC_ID_M101,
    AV_CODEC_ID_MAGICYUV,
    AV_CODEC_ID_SHEERVIDEO,
    AV_CODEC_ID_YLC,
    AV_CODEC_ID_PSD,
    AV_CODEC_ID_PIXLET,
    AV_CODEC_ID_SPEEDHQ,
    AV_CODEC_ID_FMVC,
    AV_CODEC_ID_SCPR,
    AV_CODEC_ID_CLEARVIDEO,
    AV_CODEC_ID_XPM,
    AV_CODEC_ID_AV1,
    AV_CODEC_ID_BITPACKED,
    AV_CODEC_ID_MSCC,
    AV_CODEC_ID_SRGC,
    AV_CODEC_ID_SVG,
    AV_CODEC_ID_GDV,
    AV_CODEC_ID_FITS,
    AV_CODEC_ID_IMM4,
    AV_CODEC_ID_PROSUMER,
    AV_CODEC_ID_MWSC,
    AV_CODEC_ID_WCMV,
    AV_CODEC_ID_RASC,
    AV_CODEC_ID_HYMT,
    AV_CODEC_ID_ARBC,
    AV_CODEC_ID_AGM,
    AV_CODEC_ID_LSCR,
    AV_CODEC_ID_VP4,
    AV_CODEC_ID_IMM5,
    AV_CODEC_ID_MVDV,
    AV_CODEC_ID_MVHA,
    AV_CODEC_ID_CDTOONS,
    AV_CODEC_ID_MV30,
    AV_CODEC_ID_NOTCHLC,
    AV_CODEC_ID_PFM,
    AV_CODEC_ID_MOBICLIP,
    AV_CODEC_ID_PHOTOCD,
    AV_CODEC_ID_IPU,
    AV_CODEC_ID_ARGO,
    AV_CODEC_ID_CRI,
    AV_CODEC_ID_SIMBIOSIS_IMX,
    AV_CODEC_ID_SGA_VIDEO,
    AV_CODEC_ID_GEM,
    AV_CODEC_ID_VBN,
    AV_CODEC_ID_JPEGXL,
    AV_CODEC_ID_QOI,
    AV_CODEC_ID_PHM,
    AV_CODEC_ID_RADIANCE_HDR,
    AV_CODEC_ID_WBMP,
    AV_CODEC_ID_MEDIA100,
    AV_CODEC_ID_VQC,
    AV_CODEC_ID_PDV,
    AV_CODEC_ID_EVC,
    AV_CODEC_ID_RTV1,
    AV_CODEC_ID_VMIX,
    AV_CODEC_ID_LEAD,
    AV_CODEC_ID_FIRST_AUDIO = 0x10000,
    AV_CODEC_ID_PCM_S16LE = 0x10000,
    AV_CODEC_ID_PCM_S16BE,
    AV_CODEC_ID_PCM_U16LE,
    AV_CODEC_ID_PCM_U16BE,
    AV_CODEC_ID_PCM_S8,
    AV_CODEC_ID_PCM_U8,
    AV_CODEC_ID_PCM_MULAW,
    AV_CODEC_ID_PCM_ALAW,
    AV_CODEC_ID_PCM_S32LE,
    AV_CODEC_ID_PCM_S32BE,
    AV_CODEC_ID_PCM_U32LE,
    AV_CODEC_ID_PCM_U32BE,
    AV_CODEC_ID_PCM_S24LE,
    AV_CODEC_ID_PCM_S24BE,
    AV_CODEC_ID_PCM_U24LE,
    AV_CODEC_ID_PCM_U24BE,
    AV_CODEC_ID_PCM_S24DAUD,
    AV_CODEC_ID_PCM_ZORK,
    AV_CODEC_ID_PCM_S16LE_PLANAR,
    AV_CODEC_ID_PCM_DVD,
    AV_CODEC_ID_PCM_F32BE,
    AV_CODEC_ID_PCM_F32LE,
    AV_CODEC_ID_PCM_F64BE,
    AV_CODEC_ID_PCM_F64LE,
    AV_CODEC_ID_PCM_BLURAY,
    AV_CODEC_ID_PCM_LXF,
    AV_CODEC_ID_S302M,
    AV_CODEC_ID_PCM_S8_PLANAR,
    AV_CODEC_ID_PCM_S24LE_PLANAR,
    AV_CODEC_ID_PCM_S32LE_PLANAR,
    AV_CODEC_ID_PCM_S16BE_PLANAR,
    AV_CODEC_ID_PCM_S64LE,
    AV_CODEC_ID_PCM_S64BE,
    AV_CODEC_ID_PCM_F16LE,
    AV_CODEC_ID_PCM_F24LE,
    AV_CODEC_ID_PCM_VIDC,
    AV_CODEC_ID_PCM_SGA,
    AV_CODEC_ID_ADPCM_IMA_QT = 0x11000,
    AV_CODEC_ID_ADPCM_IMA_WAV,
    AV_CODEC_ID_ADPCM_IMA_DK3,
    AV_CODEC_ID_ADPCM_IMA_DK4,
    AV_CODEC_ID_ADPCM_IMA_WS,
    AV_CODEC_ID_ADPCM_IMA_SMJPEG,
    AV_CODEC_ID_ADPCM_MS,
    AV_CODEC_ID_ADPCM_4XM,
    AV_CODEC_ID_ADPCM_XA,
    AV_CODEC_ID_ADPCM_ADX,
    AV_CODEC_ID_ADPCM_EA,
    AV_CODEC_ID_ADPCM_G726,
    AV_CODEC_ID_ADPCM_CT,
    AV_CODEC_ID_ADPCM_SWF,
    AV_CODEC_ID_ADPCM_YAMAHA,
    AV_CODEC_ID_ADPCM_SBPRO_4,
    AV_CODEC_ID_ADPCM_SBPRO_3,
    AV_CODEC_ID_ADPCM_SBPRO_2,
    AV_CODEC_ID_ADPCM_THP,
    AV_CODEC_ID_ADPCM_IMA_AMV,
    AV_CODEC_ID_ADPCM_EA_R1,
    AV_CODEC_ID_ADPCM_EA_R3,
    AV_CODEC_ID_ADPCM_EA_R2,
    AV_CODEC_ID_ADPCM_IMA_EA_SEAD,
    AV_CODEC_ID_ADPCM_IMA_EA_EACS,
    AV_CODEC_ID_ADPCM_EA_XAS,
    AV_CODEC_ID_ADPCM_EA_MAXIS_XA,
    AV_CODEC_ID_ADPCM_IMA_ISS,
    AV_CODEC_ID_ADPCM_G722,
    AV_CODEC_ID_ADPCM_IMA_APC,
    AV_CODEC_ID_ADPCM_VIMA,
    AV_CODEC_ID_ADPCM_AFC,
    AV_CODEC_ID_ADPCM_IMA_OKI,
    AV_CODEC_ID_ADPCM_DTK,
    AV_CODEC_ID_ADPCM_IMA_RAD,
    AV_CODEC_ID_ADPCM_G726LE,
    AV_CODEC_ID_ADPCM_THP_LE,
    AV_CODEC_ID_ADPCM_PSX,
    AV_CODEC_ID_ADPCM_AICA,
    AV_CODEC_ID_ADPCM_IMA_DAT4,
    AV_CODEC_ID_ADPCM_MTAF,
    AV_CODEC_ID_ADPCM_AGM,
    AV_CODEC_ID_ADPCM_ARGO,
    AV_CODEC_ID_ADPCM_IMA_SSI,
    AV_CODEC_ID_ADPCM_ZORK,
    AV_CODEC_ID_ADPCM_IMA_APM,
    AV_CODEC_ID_ADPCM_IMA_ALP,
    AV_CODEC_ID_ADPCM_IMA_MTF,
    AV_CODEC_ID_ADPCM_IMA_CUNNING,
    AV_CODEC_ID_ADPCM_IMA_MOFLEX,
    AV_CODEC_ID_ADPCM_IMA_ACORN,
    AV_CODEC_ID_ADPCM_XMD,
    AV_CODEC_ID_AMR_NB = 0x12000,
    AV_CODEC_ID_AMR_WB,
    AV_CODEC_ID_RA_144 = 0x13000,
    AV_CODEC_ID_RA_288,
    AV_CODEC_ID_ROQ_DPCM = 0x14000,
    AV_CODEC_ID_INTERPLAY_DPCM,
    AV_CODEC_ID_XAN_DPCM,
    AV_CODEC_ID_SOL_DPCM,
    AV_CODEC_ID_SDX2_DPCM,
    AV_CODEC_ID_GREMLIN_DPCM,
    AV_CODEC_ID_DERF_DPCM,
    AV_CODEC_ID_WADY_DPCM,
    AV_CODEC_ID_CBD2_DPCM,
    AV_CODEC_ID_MP2 = 0x15000,
    AV_CODEC_ID_MP3,
    AV_CODEC_ID_AAC,
    AV_CODEC_ID_AC3,
    AV_CODEC_ID_DTS,
    AV_CODEC_ID_VORBIS,
    AV_CODEC_ID_DVAUDIO,
    AV_CODEC_ID_WMAV1,
    AV_CODEC_ID_WMAV2,
    AV_CODEC_ID_MACE3,
    AV_CODEC_ID_MACE6,
    AV_CODEC_ID_VMDAUDIO,
    AV_CODEC_ID_FLAC,
    AV_CODEC_ID_MP3ADU,
    AV_CODEC_ID_MP3ON4,
    AV_CODEC_ID_SHORTEN,
    AV_CODEC_ID_ALAC,
    AV_CODEC_ID_WESTWOOD_SND1,
    AV_CODEC_ID_GSM,
    AV_CODEC_ID_QDM2,
    AV_CODEC_ID_COOK,
    AV_CODEC_ID_TRUESPEECH,
    AV_CODEC_ID_TTA,
    AV_CODEC_ID_SMACKAUDIO,
    AV_CODEC_ID_QCELP,
    AV_CODEC_ID_WAVPACK,
    AV_CODEC_ID_DSICINAUDIO,
    AV_CODEC_ID_IMC,
    AV_CODEC_ID_MUSEPACK7,
    AV_CODEC_ID_MLP,
    AV_CODEC_ID_GSM_MS,
    AV_CODEC_ID_ATRAC3,
    AV_CODEC_ID_APE,
    AV_CODEC_ID_NELLYMOSER,
    AV_CODEC_ID_MUSEPACK8,
    AV_CODEC_ID_SPEEX,
    AV_CODEC_ID_WMAVOICE,
    AV_CODEC_ID_WMAPRO,
    AV_CODEC_ID_WMALOSSLESS,
    AV_CODEC_ID_ATRAC3P,
    AV_CODEC_ID_EAC3,
    AV_CODEC_ID_SIPR,
    AV_CODEC_ID_MP1,
    AV_CODEC_ID_TWINVQ,
    AV_CODEC_ID_TRUEHD,
    AV_CODEC_ID_MP4ALS,
    AV_CODEC_ID_ATRAC1,
    AV_CODEC_ID_BINKAUDIO_RDFT,
    AV_CODEC_ID_BINKAUDIO_DCT,
    AV_CODEC_ID_AAC_LATM,
    AV_CODEC_ID_QDMC,
    AV_CODEC_ID_CELT,
    AV_CODEC_ID_G723_1,
    AV_CODEC_ID_G729,
    AV_CODEC_ID_8SVX_EXP,
    AV_CODEC_ID_8SVX_FIB,
    AV_CODEC_ID_BMV_AUDIO,
    AV_CODEC_ID_RALF,
    AV_CODEC_ID_IAC,
    AV_CODEC_ID_ILBC,
    AV_CODEC_ID_OPUS,
    AV_CODEC_ID_COMFORT_NOISE,
    AV_CODEC_ID_TAK,
    AV_CODEC_ID_METASOUND,
    AV_CODEC_ID_PAF_AUDIO,
    AV_CODEC_ID_ON2AVC,
    AV_CODEC_ID_DSS_SP,
    AV_CODEC_ID_CODEC2,
    AV_CODEC_ID_FFWAVESYNTH,
    AV_CODEC_ID_SONIC,
    AV_CODEC_ID_SONIC_LS,
    AV_CODEC_ID_EVRC,
    AV_CODEC_ID_SMV,
    AV_CODEC_ID_DSD_LSBF,
    AV_CODEC_ID_DSD_MSBF,
    AV_CODEC_ID_DSD_LSBF_PLANAR,
    AV_CODEC_ID_DSD_MSBF_PLANAR,
    AV_CODEC_ID_4GV,
    AV_CODEC_ID_INTERPLAY_ACM,
    AV_CODEC_ID_XMA1,
    AV_CODEC_ID_XMA2,
    AV_CODEC_ID_DST,
    AV_CODEC_ID_ATRAC3AL,
    AV_CODEC_ID_ATRAC3PAL,
    AV_CODEC_ID_DOLBY_E,
    AV_CODEC_ID_APTX,
    AV_CODEC_ID_APTX_HD,
    AV_CODEC_ID_SBC,
    AV_CODEC_ID_ATRAC9,
    AV_CODEC_ID_HCOM,
    AV_CODEC_ID_ACELP_KELVIN,
    AV_CODEC_ID_MPEGH_3D_AUDIO,
    AV_CODEC_ID_SIREN,
    AV_CODEC_ID_HCA,
    AV_CODEC_ID_FASTAUDIO,
    AV_CODEC_ID_MSNSIREN,
    AV_CODEC_ID_DFPWM,
    AV_CODEC_ID_BONK,
    AV_CODEC_ID_MISC4,
    AV_CODEC_ID_APAC,
    AV_CODEC_ID_FTR,
    AV_CODEC_ID_WAVARC,
    AV_CODEC_ID_RKA,
    AV_CODEC_ID_AC4,
    AV_CODEC_ID_OSQ,
    AV_CODEC_ID_QOA,
    AV_CODEC_ID_LC3,
    AV_CODEC_ID_FIRST_SUBTITLE = 0x17000,
    AV_CODEC_ID_DVD_SUBTITLE = 0x17000,
    AV_CODEC_ID_DVB_SUBTITLE,
    AV_CODEC_ID_TEXT,
    AV_CODEC_ID_XSUB,
    AV_CODEC_ID_SSA,
    AV_CODEC_ID_MOV_TEXT,
    AV_CODEC_ID_HDMV_PGS_SUBTITLE,
    AV_CODEC_ID_DVB_TELETEXT,
    AV_CODEC_ID_SRT,
    AV_CODEC_ID_MICRODVD,
    AV_CODEC_ID_EIA_608,
    AV_CODEC_ID_JACOSUB,
    AV_CODEC_ID_SAMI,
    AV_CODEC_ID_REALTEXT,
    AV_CODEC_ID_STL,
    AV_CODEC_ID_SUBVIEWER1,
    AV_CODEC_ID_SUBVIEWER,
    AV_CODEC_ID_SUBRIP,
    AV_CODEC_ID_WEBVTT,
    AV_CODEC_ID_MPL2,
    AV_CODEC_ID_VPLAYER,
    AV_CODEC_ID_PJS,
    AV_CODEC_ID_ASS,
    AV_CODEC_ID_HDMV_TEXT_SUBTITLE,
    AV_CODEC_ID_TTML,
    AV_CODEC_ID_ARIB_CAPTION,
    AV_CODEC_ID_FIRST_UNKNOWN = 0x18000,
    AV_CODEC_ID_TTF = 0x18000,
    AV_CODEC_ID_SCTE_35,
    AV_CODEC_ID_EPG,
    AV_CODEC_ID_BINTEXT,
    AV_CODEC_ID_XBIN,
    AV_CODEC_ID_IDF,
    AV_CODEC_ID_OTF,
    AV_CODEC_ID_SMPTE_KLV,
    AV_CODEC_ID_DVD_NAV,
    AV_CODEC_ID_TIMED_ID3,
    AV_CODEC_ID_BIN_DATA,
    AV_CODEC_ID_SMPTE_2038,
    AV_CODEC_ID_LCEVC,
    AV_CODEC_ID_PROBE = 0x19000,
    AV_CODEC_ID_MPEG2TS = 0x20000,
    AV_CODEC_ID_MPEG4SYSTEMS = 0x20001,
    AV_CODEC_ID_FFMETADATA = 0x21000,
    AV_CODEC_ID_WRAPPED_AVFRAME = 0x21001,
    AV_CODEC_ID_VNULL,
    AV_CODEC_ID_ANULL,
};

enum AVPacketSideDataType {
    AV_PKT_DATA_PALETTE,
    AV_PKT_DATA_NEW_EXTRADATA,
    AV_PKT_DATA_PARAM_CHANGE,
    AV_PKT_DATA_H263_MB_INFO,
    AV_PKT_DATA_REPLAYGAIN,
    AV_PKT_DATA_DISPLAYMATRIX,
    AV_PKT_DATA_STEREO3D,
    AV_PKT_DATA_AUDIO_SERVICE_TYPE,
    AV_PKT_DATA_QUALITY_STATS,
    AV_PKT_DATA_FALLBACK_TRACK,
    AV_PKT_DATA_CPB_PROPERTIES,
    AV_PKT_DATA_SKIP_SAMPLES,
    AV_PKT_DATA_JP_DUALMONO,
    AV_PKT_DATA_STRINGS_METADATA,
    AV_PKT_DATA_SUBTITLE_POSITION,
    AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL,
    AV_PKT_DATA_WEBVTT_IDENTIFIER,
    AV_PKT_DATA_WEBVTT_SETTINGS,
    AV_PKT_DATA_METADATA_UPDATE,
    AV_PKT_DATA_MPEGTS_STREAM_ID,
    AV_PKT_DATA_MASTERING_DISPLAY_METADATA,
    AV_PKT_DATA_SPHERICAL,
    AV_PKT_DATA_CONTENT_LIGHT_LEVEL,
    AV_PKT_DATA_A53_CC,
    AV_PKT_DATA_ENCRYPTION_INIT_INFO,
    AV_PKT_DATA_ENCRYPTION_INFO,
    AV_PKT_DATA_AFD,
    AV_PKT_DATA_PRFT,
    AV_PKT_DATA_ICC_PROFILE,
    AV_PKT_DATA_DOVI_CONF,
    AV_PKT_DATA_S12M_TIMECODE,
    AV_PKT_DATA_DYNAMIC_HDR10_PLUS,
    AV_PKT_DATA_IAMF_MIX_GAIN_PARAM,
    AV_PKT_DATA_IAMF_DEMIXING_INFO_PARAM,
    AV_PKT_DATA_IAMF_RECON_GAIN_INFO_PARAM,
    AV_PKT_DATA_AMBIENT_VIEWING_ENVIRONMENT,
    AV_PKT_DATA_FRAME_CROPPING,
    AV_PKT_DATA_LCEVC,
    AV_PKT_DATA_NB
};

enum AVFieldOrder {
    AV_FIELD_UNKNOWN,
    AV_FIELD_PROGRESSIVE,
    AV_FIELD_TT,
    AV_FIELD_BB,
    AV_FIELD_TB,
    AV_FIELD_BT,
};

enum AVColorRange {
    AVCOL_RANGE_UNSPECIFIED = 0,
    AVCOL_RANGE_MPEG = 1,
    AVCOL_RANGE_JPEG = 2,
    AVCOL_RANGE_NB
};

enum AVColorPrimaries {
    AVCOL_PRI_RESERVED0 = 0,
    AVCOL_PRI_BT709 = 1,
    AVCOL_PRI_UNSPECIFIED = 2,
    AVCOL_PRI_RESERVED = 3,
    AVCOL_PRI_BT470M = 4,
    AVCOL_PRI_BT470BG = 5,
    AVCOL_PRI_SMPTE170M = 6,
    AVCOL_PRI_SMPTE240M = 7,
    AVCOL_PRI_FILM = 8,
    AVCOL_PRI_BT2020 = 9,
    AVCOL_PRI_SMPTE428 = 10,
    AVCOL_PRI_SMPTEST428_1 = AVCOL_PRI_SMPTE428,
    AVCOL_PRI_SMPTE431 = 11,
    AVCOL_PRI_SMPTE432 = 12,
    AVCOL_PRI_EBU3213 = 22,
    AVCOL_PRI_JEDEC_P22 = AVCOL_PRI_EBU3213,
    AVCOL_PRI_NB
};

enum AVColorTransferCharacteristic {
    AVCOL_TRC_RESERVED0 = 0,
    AVCOL_TRC_BT709 = 1,
    AVCOL_TRC_UNSPECIFIED = 2,
    AVCOL_TRC_RESERVED = 3,
    AVCOL_TRC_GAMMA22 = 4,
    AVCOL_TRC_GAMMA28 = 5,
    AVCOL_TRC_SMPTE170M = 6,
    AVCOL_TRC_SMPTE240M = 7,
    AVCOL_TRC_LINEAR = 8,
    AVCOL_TRC_LOG = 9,
    AVCOL_TRC_LOG_SQRT = 10,
    AVCOL_TRC_IEC61966_2_4 = 11,
    AVCOL_TRC_BT1361_ECG = 12,
    AVCOL_TRC_IEC61966_2_1 = 13,
    AVCOL_TRC_BT2020_10 = 14,
    AVCOL_TRC_BT2020_12 = 15,
    AVCOL_TRC_SMPTE2084 = 16,
    AVCOL_TRC_SMPTEST2084 = AVCOL_TRC_SMPTE2084,
    AVCOL_TRC_SMPTE428 = 17,
    AVCOL_TRC_SMPTEST428_1 = AVCOL_TRC_SMPTE428,
    AVCOL_TRC_ARIB_STD_B67 = 18,
    AVCOL_TRC_NB
};

enum AVColorSpace {
    AVCOL_SPC_RGB = 0,
    AVCOL_SPC_BT709 = 1,
    AVCOL_SPC_UNSPECIFIED = 2,
    AVCOL_SPC_RESERVED = 3,
    AVCOL_SPC_FCC = 4,
    AVCOL_SPC_BT470BG = 5,
    AVCOL_SPC_SMPTE170M = 6,
    AVCOL_SPC_SMPTE240M = 7,
    AVCOL_SPC_YCGCO = 8,
    AVCOL_SPC_YCOCG = AVCOL_SPC_YCGCO,
    AVCOL_SPC_BT2020_NCL = 9,
    AVCOL_SPC_BT2020_CL = 10,
    AVCOL_SPC_SMPTE2085 = 11,
    AVCOL_SPC_CHROMA_DERIVED_NCL = 12,
    AVCOL_SPC_CHROMA_DERIVED_CL = 13,
    AVCOL_SPC_ICTCP = 14,
    AVCOL_SPC_IPT_C2 = 15,
    AVCOL_SPC_YCGCO_RE = 16,
    AVCOL_SPC_YCGCO_RO = 17,
    AVCOL_SPC_NB
};

enum AVChromaLocation {
    AVCHROMA_LOC_UNSPECIFIED = 0,
    AVCHROMA_LOC_LEFT = 1,
    AVCHROMA_LOC_CENTER = 2,
    AVCHROMA_LOC_TOPLEFT = 3,
    AVCHROMA_LOC_TOP = 4,
    AVCHROMA_LOC_BOTTOMLEFT = 5,
    AVCHROMA_LOC_BOTTOM = 6,
    AVCHROMA_LOC_NB
};

enum AVChannelOrder {
    AV_CHANNEL_ORDER_UNSPEC,
    AV_CHANNEL_ORDER_NATIVE,
    AV_CHANNEL_ORDER_CUSTOM,
    AV_CHANNEL_ORDER_AMBISONIC,
    FF_CHANNEL_ORDER_NB
};

enum AVChannel {
    AV_CHAN_NONE = -1,
    AV_CHAN_FRONT_LEFT,
    AV_CHAN_FRONT_RIGHT,
    AV_CHAN_FRONT_CENTER,
    AV_CHAN_LOW_FREQUENCY,
    AV_CHAN_BACK_LEFT,
    AV_CHAN_BACK_RIGHT,
    AV_CHAN_FRONT_LEFT_OF_CENTER,
    AV_CHAN_FRONT_RIGHT_OF_CENTER,
    AV_CHAN_BACK_CENTER,
    AV_CHAN_SIDE_LEFT,
    AV_CHAN_SIDE_RIGHT,
    AV_CHAN_TOP_CENTER,
    AV_CHAN_TOP_FRONT_LEFT,
    AV_CHAN_TOP_FRONT_CENTER,
    AV_CHAN_TOP_FRONT_RIGHT,
    AV_CHAN_TOP_BACK_LEFT,
    AV_CHAN_TOP_BACK_CENTER,
    AV_CHAN_TOP_BACK_RIGHT,
    AV_CHAN_STEREO_LEFT = 29,
    AV_CHAN_STEREO_RIGHT,
    AV_CHAN_WIDE_LEFT,
    AV_CHAN_WIDE_RIGHT,
    AV_CHAN_SURROUND_DIRECT_LEFT,
    AV_CHAN_SURROUND_DIRECT_RIGHT,
    AV_CHAN_LOW_FREQUENCY_2,
    AV_CHAN_TOP_SIDE_LEFT,
    AV_CHAN_TOP_SIDE_RIGHT,
    AV_CHAN_BOTTOM_FRONT_CENTER,
    AV_CHAN_BOTTOM_FRONT_LEFT,
    AV_CHAN_BOTTOM_FRONT_RIGHT,
    AV_CHAN_SIDE_SURROUND_LEFT,
    AV_CHAN_SIDE_SURROUND_RIGHT,
    AV_CHAN_TOP_SURROUND_LEFT,
    AV_CHAN_TOP_SURROUND_RIGHT,
    AV_CHAN_UNUSED = 0x200,
    AV_CHAN_UNKNOWN = 0x300,
    AV_CHAN_AMBISONIC_BASE = 0x400,
    AV_CHAN_AMBISONIC_END = 0x7ff,
};

enum AVDiscard {
    AVDISCARD_NONE = -16,
    AVDISCARD_DEFAULT = 0,
    AVDISCARD_NONREF = 8,
    AVDISCARD_BIDIR = 16,
    AVDISCARD_NONINTRA = 24,
    AVDISCARD_NONKEY = 32,
    AVDISCARD_ALL = 48,
};

enum AVStreamGroupParamsType {
    AV_STREAM_GROUP_PARAMS_NONE,
    AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT,
    AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION,
    AV_STREAM_GROUP_PARAMS_TILE_GRID,
    AV_STREAM_GROUP_PARAMS_LCEVC,
};

enum AVDurationEstimationMethod {
    AVFMT_DURATION_FROM_PTS,
    AVFMT_DURATION_FROM_STREAM,
    AVFMT_DURATION_FROM_BITRATE
};

typedef enum AVClassCategory {
    AV_CLASS_CATEGORY_NA = 0,
    AV_CLASS_CATEGORY_INPUT,
    AV_CLASS_CATEGORY_OUTPUT,
    AV_CLASS_CATEGORY_MUXER,
    AV_CLASS_CATEGORY_DEMUXER,
    AV_CLASS_CATEGORY_ENCODER,
    AV_CLASS_CATEGORY_DECODER,
    AV_CLASS_CATEGORY_FILTER,
    AV_CLASS_CATEGORY_BITSTREAM_FILTER,
    AV_CLASS_CATEGORY_SWSCALER,
    AV_CLASS_CATEGORY_SWRESAMPLER,
    AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT = 40,
    AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT,
    AV_CLASS_CATEGORY_DEVICE_AUDIO_OUTPUT,
    AV_CLASS_CATEGORY_DEVICE_AUDIO_INPUT,
    AV_CLASS_CATEGORY_DEVICE_OUTPUT,
    AV_CLASS_CATEGORY_DEVICE_INPUT,
    AV_CLASS_CATEGORY_NB
}AVClassCategory;

// Structs
typedef struct AVDictionary AVDictionary;

typedef struct AVBuffer AVBuffer;

typedef struct AVChannelCustom {
    enum AVChannel id;
    char name[16];
    void *opaque;
} AVChannelCustom;

typedef struct AVRational {
    int num;
    int den;
} AVRational;

typedef struct AVChannelLayout {
    enum AVChannelOrder order;
    int nb_channels;
    union {
        uint64_t mask;
        AVChannelCustom *map;
    } u;
    void *opaque;
} AVChannelLayout;

typedef struct AVPacketSideData {
    uint8_t *data;
    size_t size;
    enum AVPacketSideDataType type;
} AVPacketSideData;

typedef struct AVCodecParameters {
    enum AVMediaType codec_type;
    enum AVCodecID codec_id;
    uint32_t codec_tag;
    uint8_t *extradata;
    int extradata_size;
    AVPacketSideData *coded_side_data;
    int nb_coded_side_data;
    int format;
    int64_t bit_rate;
    int bits_per_coded_sample;
    int bits_per_raw_sample;
    int profile;
    int level;
    int width;
    int height;
    AVRational sample_aspect_ratio;
    AVRational framerate;
    enum AVFieldOrder field_order;
    enum AVColorRange color_range;
    enum AVColorPrimaries color_primaries;
    enum AVColorTransferCharacteristic color_trc;
    enum AVColorSpace color_space;
    enum AVChromaLocation chroma_location;
    int video_delay;
    AVChannelLayout ch_layout;
    int sample_rate;
    int block_align;
    int frame_size;
    int initial_padding;
    int trailing_padding;
    int seek_preroll;
} AVCodecParameters;

typedef struct AVClass {
    const char* class_name;
    const char* (*item_name)(void* ctx);
    const struct AVOption *option;
    int version;
    int log_level_offset_offset;
    int parent_log_context_offset;
    AVClassCategory category;
    AVClassCategory (*get_category)(void* ctx);
    int (*query_ranges)(struct AVOptionRanges **, void *obj, const char *key, int flags);
    void* (*child_next)(void *obj, void *prev);
    const struct AVClass* (*child_class_iterate)(void **iter);
} AVClass;

typedef struct AVBufferRef {
    AVBuffer *buffer;
    uint8_t *data;
    size_t size;
} AVBufferRef;

typedef struct AVPacket {
    AVBufferRef *buf;
    int64_t pts;
    int64_t dts;
    uint8_t *data;
    int size;
    int stream_index;
    int flags;
    AVPacketSideData *side_data;
    int side_data_elems;
    int64_t duration;
    int64_t pos;
    void *opaque;
    AVBufferRef *opaque_ref;
    AVRational time_base;
} AVPacket;

typedef struct AVStream {
    const AVClass *av_class;
    int index;
    int id;
    AVCodecParameters *codecpar;
    void *priv_data;
    AVRational time_base;
    int64_t start_time;
    int64_t duration;
    int64_t nb_frames;
    int disposition;
    enum AVDiscard discard;
    AVRational sample_aspect_ratio;
    AVDictionary *metadata;
    AVRational avg_frame_rate;
    AVPacket attached_pic;
    int event_flags;
    AVRational r_frame_rate;
    int pts_wrap_bits;
} AVStream;

typedef struct AVIOContext {
    const AVClass *av_class;
    unsigned char *buffer;
    int buffer_size;
    unsigned char *buf_ptr;
    unsigned char *buf_end;
    void *opaque;
    int (*read_packet)(void *opaque, uint8_t *buf, int buf_size);
    int (*write_packet)(void *opaque, const uint8_t *buf, int buf_size);
    int64_t (*seek)(void *opaque, int64_t offset, int whence);
    int64_t pos;
    int eof_reached;
    int error;
    int write_flag;
    int max_packet_size;
    int min_packet_size;
    unsigned long checksum;
    unsigned char *checksum_ptr;
    unsigned long (*update_checksum)(unsigned long checksum, const uint8_t *buf, unsigned int size);
    int (*read_pause)(void *opaque, int pause);
    int64_t (*read_seek)(void *opaque, int stream_index, int64_t timestamp, int flags);
    int seekable;
    int direct;
    const char *protocol_whitelist;
    const char *protocol_blacklist;
    int (*write_data_type)(void *opaque, const uint8_t *buf, int buf_size, enum AVIODataMarkerType type, int64_t time);
    int ignore_boundary_point;
    unsigned char *buf_ptr_max;
    int64_t bytes_read;
    int64_t bytes_written;
} AVIOContext;

typedef struct AVStreamGroup {
    const AVClass *av_class;
    void *priv_data;
    unsigned int index;
    int64_t id;
    enum AVStreamGroupParamsType type;
    union {
        struct AVIAMFAudioElement *iamf_audio_element;
        struct AVIAMFMixPresentation *iamf_mix_presentation;
        struct AVStreamGroupTileGrid *tile_grid;
        struct AVStreamGroupLCEVC *lcevc;
    } params;
    AVDictionary *metadata;
    unsigned int nb_streams;
    AVStream **streams;
    int disposition;
} AVStreamGroup;

typedef struct AVChapter {
    int64_t id;
    AVRational time_base;
    int64_t start, end;
    AVDictionary *metadata;
} AVChapter;

typedef struct AVProgram {
    int id;
    int flags;
    enum AVDiscard discard;
    unsigned int *stream_index;
    unsigned int nb_stream_indexes;
    AVDictionary *metadata;
    int program_num;
    int pmt_pid;
    int pcr_pid;
    int pmt_version;
    int64_t start_time;
    int64_t end_time;
    int64_t pts_wrap_reference;
    int pts_wrap_behavior;
} AVProgram;

typedef struct AVIOInterruptCB {
    int (*callback)(void*);
    void *opaque;
} AVIOInterruptCB;

typedef int (*av_format_control_message)(struct AVFormatContext *s, int type, void *data, size_t data_size);

typedef struct AVFormatContext {
    const AVClass *av_class;
    const struct AVInputFormat *iformat;
    const struct AVOutputFormat *oformat;
    void *priv_data;
    AVIOContext *pb;
    int ctx_flags;
    unsigned int nb_streams;
    AVStream **streams;
    unsigned int nb_stream_groups;
    AVStreamGroup **stream_groups;
    unsigned int nb_chapters;
    AVChapter **chapters;
    char *url;
    int64_t start_time;
    int64_t duration;
    int64_t bit_rate;
    unsigned int packet_size;
    int max_delay;
    int flags;
    int64_t probesize;
    int64_t max_analyze_duration;
    const uint8_t *key;
    int keylen;
    unsigned int nb_programs;
    AVProgram **programs;
    enum AVCodecID video_codec_id;
    enum AVCodecID audio_codec_id;
    enum AVCodecID subtitle_codec_id;
    enum AVCodecID data_codec_id;
    AVDictionary *metadata;
    int64_t start_time_realtime;
    int fps_probe_size;
    int error_recognition;
    AVIOInterruptCB interrupt_callback;
    int debug;
    int max_streams;
    unsigned int max_index_size;
    unsigned int max_picture_buffer;
    int64_t max_interleave_delta;
    int max_ts_probe;
    int max_chunk_duration;
    int max_chunk_size;
    int max_probe_packets;
    int strict_std_compliance;
    int event_flags;
    int avoid_negative_ts;
    int audio_preload;
    int use_wallclock_as_timestamps;
    int skip_estimate_duration_from_pts;
    int avio_flags;
    enum AVDurationEstimationMethod duration_estimation_method;
    int64_t skip_initial_bytes;
    unsigned int correct_ts_overflow;
    int seek2any;
    int flush_packets;
    int probe_score;
    int format_probesize;
    char *codec_whitelist;
    char *format_whitelist;
    char *protocol_whitelist;
    char *protocol_blacklist;
    int io_repositioned;
    const struct AVCodec *video_codec;
    const struct AVCodec *audio_codec;
    const struct AVCodec *subtitle_codec;
    const struct AVCodec *data_codec;
    int metadata_header_padding;
    void *opaque;
    av_format_control_message control_message_cb;
    int64_t output_ts_offset;
    uint8_t *dump_separator;
    int (*io_open)(struct AVFormatContext *s, AVIOContext **pb, const char *url, int flags, AVDictionary **options);
    int (*io_close2)(struct AVFormatContext *s, AVIOContext *pb);
    int64_t duration_probesize;
} AVFormatContext;

typedef struct AVInputFormat {
    const char *name;
    const char *long_name;
    int flags;
    const char *extensions;
    const struct AVCodecTag * const *codec_tag;
    const AVClass *priv_class;
    const char *mime_type;
} AVInputFormat;

// Functions
void av_log_set_level(int level);
const char *av_version_info(void);
AVFormatContext *avformat_alloc_context(void);
int avformat_open_input(AVFormatContext **ps, const char *url, const AVInputFormat *fmt, AVDictionary **options);
int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options);
int av_read_frame(AVFormatContext *s, AVPacket *pkt);
void avformat_close_input(AVFormatContext **s);// Structs
typedef struct SwsContext SwsContext;

typedef struct SwsVector {
    double *coeff;
    int length;
} SwsVector;

typedef struct SwsFilter {
    SwsVector *lumH;
    SwsVector *lumV;
    SwsVector *chrH;
    SwsVector *chrV;
} SwsFilter;

// Functions
SwsContext *sws_getCachedContext(SwsContext *context, int srcW, int srcH, enum AVPixelFormat srcFormat, int dstW, int dstH, enum AVPixelFormat dstFormat, int flags, SwsFilter *srcFilter, SwsFilter *dstFilter, const double *param);
int sws_scale(SwsContext *c, const uint8_t *const srcSlice[], const int srcStride[], int srcSliceY, int srcSliceH, uint8_t *const dst[], const int dstStride[]);
void sws_freeContext(SwsContext *swsContext);<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\AVCodec\Frame;

use FFI;
use Webrtc\AVCodec\Enum\ColorRange;
use Webrtc\AVCodec\Enum\Colorspace;
use Webrtc\AVCodec\Enum\Interpolation;
use Webrtc\AVCodec\Format\VideoFormat;
use Webrtc\Exception\InvalidArgumentException;
use Webrtc\Mixin\SharedLibraryInterface;

/**
 * VideoFrameReformater Class
 *
 * This class handles reformatting of video frames using FFI to interface with the SWScale library.
 * It provides functionality to change video frame attributes such as dimensions, format, colorspace,
 * color range, and interpolation method.
 *
 * @package Webrtc\AVCodec
 */
class VideoFrameReformater implements SharedLibraryInterface
{
    /**
     * @var mixed SWScale context
     */
    private $ctx;

    /**
     * @var FFI SWScale library interface
     */
    private $libSWScale;

    /**
     * Constructor for VideoFrameReformater
     *
     * Initializes the shared library interface
     */
    public function __construct()
    {
        $this->initiateSharedLibrary();
    }

    /**
     * Destructor for VideoFrameReformater
     *
     * Frees the SWScale context when the object is destroyed
     */
    public function __destruct()
    {
        if (isset($this->ctx) && $this->ctx !== null && isset($this->libSWScale)) {
            $this->libSWScale->sws_freeContext($this->ctx);
        }
    }

    /**
     * @return array{}
     */
    public function __serialize(): array
    {
        return [];
    }

    /**
     * @param array $data
     */
    public function __unserialize(array $data): void
    {
        $this->__construct();
        $this->ctx = null;
    }

    /**
     * Resolves the value of an enum parameter
     *
     * Returns the enum value if the parameter is an instance of the specified enum class,
     * otherwise returns the provided value or default value.
     *
     * @param mixed $value The input value to resolve
     * @param string $enumClass The expected enum class name
     * @param int $default The default value to use if $value is null
     *
     * @return int The resolved integer value
     */
    private function resolveEnumValue($value, string $enumClass, int $default): int
    {
        return $value instanceof $enumClass ? $value->value : ($value ?? $default);
    }

    /**
     * Reformats a video frame
     *
     * Creates a new video frame with the specified parameters, or uses the original
     * frame's parameters where null is specified.
     *
     * @param VideoFrame $frame The source video frame
     * @param int|null $width New width (null for source width)
     * @param int|null $height New height (null for source height)
     * @param string|null $format New pixel format (null for source format)
     * @param Colorspace|null $srcColorspace Source colorspace
     * @param Colorspace|null $dstColorspace Destination colorspace
     * @param Interpolation|null $interpolation Scaling algorithm to use
     * @param ColorRange|null $srcColorRange Source color range
     * @param ColorRange|null $dstColorRange Destination color range
     *
     * @return VideoFrame The reformatted video frame
     */
    public function reformat(VideoFrame     $frame, ?int $width = null, ?int $height = null, ?string $format = null,
                             ?Colorspace    $srcColorspace = null, ?Colorspace $dstColorspace = null,
                             ?Interpolation $interpolation = null, ?ColorRange $srcColorRange = null,
                             ?ColorRange    $dstColorRange = null): VideoFrame
    {
        $videoFormat = new VideoFormat($format ?? $frame->getVideoFormat()->getFormat(), $width ?? $frame->getVideoFormat()->getWidth(), $height ?? $frame->getVideoFormat()->getHeight());

        $cSrcColorspace = $this->resolveEnumValue($srcColorspace, Colorspace::class, $frame->getColorspace());
        $cDstColorspace = $this->resolveEnumValue($dstColorspace, Colorspace::class, $frame->getColorspace());
        $cInterpolation = $this->resolveEnumValue($interpolation, Interpolation::class, Interpolation::BILINEAR->value);
        $cSrcColorRange = $this->resolveEnumValue($srcColorRange, ColorRange::class, 0);
        $cDstColorRange = $this->resolveEnumValue($dstColorRange, ColorRange::class, 0);

        return $this->createNewFrame(
            $frame,
            $width ?? $frame->getFrame()->width,
            $height ?? $frame->getFrame()->height,
            $videoFormat,
            $cSrcColorspace,
            $cDstColorspace,
            $cInterpolation,
            $cSrcColorRange,
            $cDstColorRange
        );
    }

    /**
     * Creates a new video frame with the specified parameters
     *
     * This method performs the actual frame reformatting using SWScale functions.
     * If the source and destination parameters are identical, it returns the original frame.
     *
     * @param VideoFrame $frame The source video frame
     * @param int $width Target width
     * @param int $height Target height
     * @param VideoFormat $dstFormat Target video format
     * @param int $srcColorspace Source colorspace value
     * @param int $dstColorspace Destination colorspace value
     * @param int $interpolation Scaling algorithm to use
     * @param int $srcColorRange Source color range value
     * @param int $dstColorRange Destination color range value
     *
     * @return VideoFrame The new video frame
     * @throws InvalidArgumentException If the source frame doesn't have a format set
     */
    private function createNewFrame(VideoFrame $frame, int $width, int $height, VideoFormat $dstFormat,
                                    int        $srcColorspace, int $dstColorspace, int $interpolation,
                                    int        $srcColorRange, int $dstColorRange): VideoFrame
    {

        if ($frame->getFrame()->format < 0) {
            throw new InvalidArgumentException("Frame does not have format set.");
        }

        $srcColorRange = ($srcColorRange === ColorRange::JPEG->value) ? 1 : 0;
        $dstColorRange = ($dstColorRange === ColorRange::JPEG->value) ? 1 : 0;

        $srcFormat = $frame->getVideoFormat()->getFormat();

        if ($dstFormat->getFormat() === $srcFormat && $width === $frame->getFrame()->width &&
            $height === $frame->getFrame()->height && $dstColorspace === $srcColorspace &&
            $srcColorRange === $dstColorRange) {
            return $frame;
        }

        $this->ctx = $this->libSWScale->sws_getCachedContext(
            $this->ctx,
            $frame->getFrame()->width,
            $frame->getFrame()->height,
            $srcFormat,
            $width,
            $height,
            $dstFormat->getFormat(),
            $interpolation,
            null,
            null,
            null
        );

        if ($srcColorspace !== $dstColorspace || $srcColorRange !== $dstColorRange) {
            $invTbl = $this->libSWScale->sws_getCoefficients($srcColorspace);
            $tbl = $this->libSWScale->sws_getCoefficients($dstColorspace);

            $this->libSWScale->sws_setColorspaceDetails(
                $this->ctx,
                $invTbl,
                $srcColorRange,
                $tbl,
                $dstColorRange,
                0, 0, 0
            );
        }

        $newFrame = new VideoFrame($width, $height);
        $newFrame->setVideoFormat($dstFormat);
        $newFrame->setPts($frame->getPts());
        $newFrame->setTimeBase($frame->getTimeBase()->num, $frame->getTimeBase()->den);

        $this->libSWScale->sws_scale(
            $this->ctx,
            $frame->getFrame()->data,
            $frame->getFrame()->linesize,
            0,
            $frame->getFrame()->height,
            $newFrame->getFrame()->data,
            $newFrame->getFrame()->linesize
        );

        return $newFrame;
    }

    /**
     * Initializes the SWScale shared library
     *
     * This method implements the SharedLibraryInterface requirement.
     * It uses a global FFI instance of the SWScale library if available.
     *
     * @return void
     */
    public function initiateSharedLibrary(): void
    {
        global $libSWScale;
        if ($libSWScale instanceof FFI) {
            $this->libSWScale = $libSWScale;
        }
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\AVCodec\Frame;

use FFI;
use FFI\CData;
use Webrtc\AVCodec\Data\VideoPlane;
use Webrtc\AVCodec\Enum\ColorRange;
use Webrtc\AVCodec\Enum\Colorspace;
use Webrtc\AVCodec\Enum\Interpolation;
use Webrtc\AVCodec\Enum\PictureType;
use Webrtc\AVCodec\Exception\AvCodecException;
use Webrtc\AVCodec\Format\VideoFormat;

/**
 * Video Frame Class
 *
 * Represents a video frame in FFmpeg/AVCodec context, handling allocation,
 * storage, and manipulation of video frame data including planes, formats,
 * and frame attributes.
 *
 * @package Webrtc\AVCodec\Frame
 */
class VideoFrame extends Frame
{
    /**
     * @var VideoFormat $videFormat Video format information
     */
    private VideoFormat $videFormat;

    /**
     * @var CData|null $data FFI data buffer
     */
    private ?CData $data = null;

    /**
     * @var CData|null $lineSize FFI line size buffer
     */
    private ?CData $lineSize = null;

    /**
     * @var VideoFrameReformater|null $reformater Frame reformatter instance
     */
    private ?VideoFrameReformater $reformater = null;

    /**
     * Constructor
     *
     * @param int $width Frame width in pixels
     * @param int $height Frame height in pixels
     * @param string|int $format Pixel format (name or enum)
     * @param CData|null $frame Existing FFI AVFrame to wrap
     * @throws AvCodecException If allocation fails
     */
    public function __construct(int $width = 0, int $height = 0, string|int $format = "yuv420p", ?CData $frame = null)
    {
        if ($frame) {
            $this->initiateSharedLibrary();
            $this->timeBase = new \stdClass();
            $this->frame = $frame;
            $this->setVideoFormat();
            return;
        }

        parent::__construct();

        $this->frame->width = $width;
        $this->frame->height = $height;
        $this->frame->format = is_string($format) ? $this->getFormat($format) : $format;

        $this->data = $this->libAVCodec->new('uint8_t*[8]', false);  // Prevent automatic garbage collection
        $this->lineSize = $this->libAVCodec->new('int[8]', false);

        if ($width > 0 && $height > 0) {
            $result = $this->libAVCodec->av_image_alloc(
                $this->data,
                $this->lineSize,
                $width,
                $height,
                $this->frame->format,
                16
            );

            if ($result < 0) {
                throw new AvCodecException("Failed to allocate image.");
            }

            $this->frame->data = $this->data;
            $this->frame->linesize = $this->lineSize;
        }

        $this->setVideoFormat();
    }

    /**
     * Get pixel format enum from name
     *
     * @param string $formatName Pixel format name
     * @return int Pixel format enum
     * @throws AvCodecException If format is invalid
     */
    private function getFormat(string $formatName): int
    {
        $format = $this->libAVCodec->av_get_pix_fmt($formatName);
        if ($format == $this->libAVCodec->AV_PIX_FMT_NONE) {
            throw new AvCodecException(sprintf("Invalid format: %s", $formatName));
        }
        return $format;
    }

    /**
     * Set video format
     *
     * @param VideoFormat|null $videoFormat VideoFormat instance or null to create from frame
     */
    public function setVideoFormat(?VideoFormat $videoFormat = null): void
    {
        $this->videFormat = $videoFormat ?? new VideoFormat($this->frame->format, $this->frame->width, $this->frame->height);
    }

    /**
     * Get video format
     *
     * @return VideoFormat Current video format
     */
    public function getVideoFormat(): VideoFormat
    {
        return $this->videFormat;
    }

    /**
     * Iterate through video planes
     *
     * @return \Generator<VideoPlane> Generator yielding VideoPlane objects
     */
    public function planes(): \Generator
    {
        $planeCount = 0;
        $maxPlaneCount = 2;

        if ($this->videFormat->getName() == "pal8") {
            $maxPlaneCount = 2;
        } else {
            $videoFormatComponentsCount = $this->videFormat->getVideoFormat()->nb_components;
            for ($i = 0; $i < $videoFormatComponentsCount; $i++) {
                $maxPlaneCount = max($this->videFormat->getVideoFormat()->comp[$i]->plane + 1, $maxPlaneCount);
            }
        }

        // TODO: check this line later
//        $this->frame->extended_data = $this->libAVCodec->cast('uint8_t**', $this->frame->data);
        while ($planeCount < $maxPlaneCount && !FFI::isNull($this->frame->data[$planeCount])) {
            yield new VideoPlane($this, $planeCount++);
        }
    }

    /**
     * Set picture type
     *
     * @param PictureType $type Picture type enum
     */
    public function setPictureType(PictureType $type): void
    {
        $this->frame->pict_type = $type->value;
    }

    /**
     * Get picture type
     *
     * @return PictureType|null Picture type enum or null if not set
     */
    public function getPictureType(): ?PictureType
    {
        return PictureType::tryFrom($this->frame->pict_type);
    }

    /**
     * Destructor - frees allocated memory
     */
    public function __destruct()
    {
        if ($this->data) {
            $this->libAVCodec->av_freep(FFI::addr($this->data));
            $this->data = null;
        }

        if ($this->lineSize) {
            FFI::free($this->lineSize);
            $this->lineSize = null;
        }

        parent::__destruct();
    }

    public function initializeUserAttributes()
    {
        // TODO:
    }

    /**
     * Get raw picture type value
     *
     * @return int FFmpeg picture type value
     */
    public function getPictType(): int
    {
        return $this->frame->pict_type;
    }

    /**
     * Set raw picture type value
     *
     * @param int $value FFmpeg picture type value
     */
    public function setPictType(int $value): void
    {
        $this->frame->pict_type = $value;
    }

    /**
     * Get colorspace
     *
     * @return mixed Current colorspace value
     */
    public function getColorspace(): mixed
    {
        return $this->frame->colorspace;
    }

    /**
     * Set colorspace
     *
     * @param mixed $value Colorspace value to set
     */
    public function setColorspace($value): void
    {
        $this->frame->colorspace = $value;
    }

    /**
     * Get color range
     *
     * @return mixed Current color range value
     */
    public function getColorRange()
    {
        return $this->frame->color_range;
    }

    /**
     * Set color range
     *
     * @param mixed $value Color range value to set
     */
    public function setColorRange($value): void
    {
        $this->frame->color_range = $value;
    }

    /**
     * Reformat video frame
     *
     * @param int|null $width New width
     * @param int|null $height New height
     * @param string|null $format New pixel format
     * @param Colorspace|null $srcColorspace Source colorspace
     * @param Colorspace|null $dstColorspace Destination colorspace
     * @param Interpolation|null $interpolation Interpolation method
     * @param ColorRange|null $srcColorRange Source color range
     * @param ColorRange|null $dstColorRange Destination color range
     * @return VideoFrame Reformatted video frame
     */
    public function reformat(?int           $width = null, ?int $height = null, ?string $format = null,
                             ?Colorspace    $srcColorspace = null, ?Colorspace $dstColorspace = null,
                             ?Interpolation $interpolation = null, ?ColorRange $srcColorRange = null,
                             ?ColorRange    $dstColorRange = null): VideoFrame
    {
        if ($this->reformater === null) {
            $this->reformater = new VideoFrameReformater();
        }
        return $this->reformater->reformat(
            $this,
            $width,
            $height,
            $format,
            $srcColorspace,
            $dstColorspace,
            $interpolation,
            $srcColorRange,
            $dstColorRange
        );
    }

    /**
     * @return array{width: int, height: int, format: string, pts: int|null, timeBase: \stdClass, pictType: int, colorspace: mixed, colorRange: mixed, planes: list<string>}
     */
    public function __serialize(): array
    {
        $planes = [];
        foreach ($this->planes() as $plane) {
            $planes[] = $plane->getData();
        }

        return [
            'width' => $this->frame->width,
            'height' => $this->frame->height,
            'format' => $this->videFormat->getName(),
            'pts' => $this->getPts(),
            'timeBase' => $this->timeBase,
            'pictType' => $this->frame->pict_type,
            'colorspace' => $this->frame->colorspace,
            'colorRange' => $this->frame->color_range,
            'planes' => $planes,
        ];
    }

    /**
     * @param array{width?: int, height?: int, format?: string, pts?: int|null, timeBase?: \stdClass, pictType?: int, colorspace?: mixed, colorRange?: mixed, planes?: list<string>} $data
     */
    public function __unserialize(array $data): void
    {
        $this->__construct($data['width'] ?? 0, $data['height'] ?? 0, $data['format'] ?? 'yuv420p');
        if (array_key_exists('pts', $data)) {
            $this->setPts($data['pts']);
        }
        if (isset($data['timeBase']) && $data['timeBase'] instanceof \stdClass) {
            $this->timeBase = $data['timeBase'];
        }
        if (isset($data['pictType'])) {
            $this->setPictType($data['pictType']);
        }
        if (array_key_exists('colorspace', $data)) {
            $this->setColorspace($data['colorspace']);
        }
        if (array_key_exists('colorRange', $data)) {
            $this->setColorRange($data['colorRange']);
        }
        $index = 0;
        foreach ($this->planes() as $plane) {
            $payload = $data['planes'][$index] ?? '';
            if ($payload !== '') {
                $plane->putData($payload);
            }
            $index++;
        }
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\AVCodec\Frame;

use FFI;
use FFI\CData;
use Webrtc\AVCodec\Util\Util;
use Webrtc\Exception\RuntimeException;
use Webrtc\Mixin\SharedLibraryInterface;

/**
 * Frame Abstract Base Class
 *
 * Represents a media frame (audio/video) in the FFmpeg context.
 * Provides common frame operations and properties for all frame types.
 *
 * @package Webrtc\AVCodec\Frame
 */
abstract class Frame implements FrameInterface, SharedLibraryInterface
{
    private const AV_NOPTS_VALUE = -9223372036854775808;

    /**
     * @var FFI|null $libAVCodec FFI instance for AVCodec library
     */
    protected ?FFI $libAVCodec;

    /**
     * @var CData|null $frame FFI CData representing AVFrame
     */
    protected ?CData $frame = null;

    /**
     * @var \stdClass $timeBase Time base for PTS calculations
     */
    protected \stdClass $timeBase;

    /**
     * Constructor - allocates a new AVFrame
     * @throws RuntimeException If frame allocation fails
     */
    public function __construct()
    {
        $this->initiateSharedLibrary();
        $this->timeBase = new \stdClass();
        $this->frame = $this->libAVCodec->av_frame_alloc();

        if ($this->frame === null) {
            throw new RuntimeException("Failed to allocate frame.");
        }
    }

    /**
     * Destructor - frees frame memory
     */
    public function __destruct()
    {
        $this->libAVCodec->av_frame_free(FFI::addr($this->frame));
        $this->frame = null;
    }

    /**
     * Initialize shared library instance
     */
    public function initiateSharedLibrary(): void
    {
        global $libAVCodec;

        if ($libAVCodec instanceof FFI) {
            $this->libAVCodec = $libAVCodec;
        }
    }

    /**
     * Get presentation timestamp (PTS)
     *
     * @return int|null PTS value or null if not set (AV_NOPTS_VALUE)
     */
    public function getPts(): ?int
    {
        return $this->frame->pts == $this->libAVCodec->AV_NOPTS_VALUE ? null : $this->frame->pts;
    }

    /**
     * Set presentation timestamp (PTS)
     *
     * @param int|null $pts PTS value or null to set as AV_NOPTS_VALUE
     */
    public function setPts(?int $pts): void
    {
        $this->frame->pts = $pts ?? $this->libAVCodec->AV_NOPTS_VALUE;
    }

    /**
     * Get current time base
     *
     * @return \stdClass Object with num/den properties
     */
    public function getTimeBase(): \stdClass
    {
        return $this->timeBase;
    }

    /**
     * Set time base for timestamp calculations
     *
     * @param int $numerator Time base numerator
     * @param int $denominator Time base denominator
     */
    public function setTimeBase(int $numerator, int $denominator): void
    {
        $this->timeBase->num = $numerator;
        $this->timeBase->den = $denominator;
    }

    /**
     * Get raw FFI frame object
     *
     * @return CData The FFI CData AVFrame
     */
    public function getFrame(): CData
    {
        return $this->frame;
    }

    /**
     * Write data to frame buffer
     *
     * @param string $data Data to write
     * @param int $index Plane/buffer index
     */
    public function putData(string $data, int $index = 0): void
    {
        if (!FFI::isNull($this->frame->extended_data[$index])) {
            FFI::memcpy($this->frame->extended_data[$index], $data, strlen($data));
        }
    }

    /**
     * Rebase timestamps to new time base
     *
     * @param CData $dst Target time base (FFI AVRational)
     * @throws RuntimeException If target time base is zero
     */
    public function rebaseTime(CData $dst): void
    {
        if (!$dst->num) {
            throw new RuntimeException("Cannot rebase to zero time.");
        }

        if (!isset($this->timeBase->num)) {
            $this->timeBase->num = $dst->num;
            $this->timeBase->den = $dst->den;
            return;
        }

        if ($this->timeBase->num == $dst->num && $this->timeBase->den == $dst->den) {
            return;
        }

        if ($this->frame->pts <= self::AV_NOPTS_VALUE) {
            $this->frame->pts = Util::rescaleQ($this->frame->pts, $this->timeBase, $dst);
        }

        $this->timeBase->num = $dst->num;
        $this->timeBase->den = $dst->den;
    }

    /**
     * Check if frame is a keyframe
     *
     * @return bool True if frame is a keyframe
     */
    public function isKeyFrame(): bool
    {
        return (bool)($this->frame->flags & (1 << 1));
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\AVCodec\Frame;

use FFI;
use FFI\CData;
use Ramsey\Uuid\Uuid;

/**
 * Opaque Container Class
 *
 * Provides a mechanism to store PHP objects and associate them with FFmpeg buffers,
 * allowing safe passing of PHP objects through FFmpeg's C API.
 *
 * @package Webrtc\AVCodec\Frame
 */
class OpaqueContainer
{
    /**
     * @var array $byName Storage for objects keyed by UUID
     */
    private array $byName;

    /**
     * Constructor
     *
     * @param FFI $libavcodec Initialized FFI instance for AVCodec
     */
    public function __construct(private readonly FFI $libavcodec)
    {
        $this->byName = [];
    }

    /**
     * Add an object to the container
     *
     * Creates an AVBufferRef containing a UUID that maps to the stored object
     *
     * @param mixed $v Object to store
     * @return CData FFI AVBufferRef containing the reference
     */
    public function add(mixed $v): CData
    {
        $uuid = Uuid::uuid4()->toString();
        $ref = $this->libavcodec->av_buffer_create($uuid, strlen($uuid), [$this, 'keyFree'], null, 0);
        $this->byName[$uuid] = $v;
        return $ref;
    }

    /**
     * Retrieve an object by name/UUID
     *
     * @param string $name UUID key of the object
     * @return mixed|null The stored object or null if not found
     */
    public function get(string $name): mixed
    {
        return $this->byName[$name] ?? null;
    }

    /**
     * Retrieve and remove an object by name/UUID
     *
     * @param string $name UUID key of the object
     * @return mixed|null The stored object or null if not found
     */
    public function pop(string $name): mixed
    {
        $value = $this->byName[$name] ?? null;
        unset($this->byName[$name]);
        return $value;
    }

    /**
     * Callback for buffer cleanup
     *
     * @param CData $opaque Opaque pointer from FFmpeg
     * @param CData $data Data pointer from FFmpeg
     */
    public function keyFree(FFI\CData $opaque, FFI\CData $data)
    {
        // TODO: Cleanup function if required
    }

    /**
     * @return array{byName: array}
     */
    public function __serialize(): array
    {
        return ['byName' => $this->byName];
    }

    /**
     * @param array{byName?: array} $data
     */
    public function __unserialize(array $data): void
    {
        global $libAVCodec;
        $this->__construct($libAVCodec);
        $this->byName = is_array($data['byName'] ?? null) ? $data['byName'] : [];
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\AVCodec\Frame;

use FFI\CData;
use Webrtc\AVCodec\Audio\AudioLayout;
use Webrtc\AVCodec\Data\AudioPlane;
use Webrtc\AVCodec\Format\AudioFormat;
use Webrtc\Exception\RuntimeException;

/**
 * A frame of audio data.
 */
class AudioFrame extends Frame
{
    private ?CData $buffer = null;
    private int $bufferSize;
    private AudioFormat $format;
    private AudioLayout $layout;

    /**
     * Initializes an AudioFrame instance.
     *
     * @param string|int $format The audio format (e.g., "s16").
     * @param string $layout The channel layout (e.g., "stereo").
     * @param int $samples The number of samples per channel.
     * @param int $align Alignment for the buffer.
     */
    public function __construct(string|int $format = "s16", string $layout = "stereo", int $samples = 0, int $align = 1)
    {
        parent::__construct();
        $this->format = new AudioFormat($format);
        $this->layout = new AudioLayout($layout);

        $this->initialize($samples, $align);
    }

    /**
     * Performs internal initialization of the audio frame.
     *
     * @param int $nbSamples The number of samples per channel.
     * @param int $align Alignment for the buffer.
     */
    private function initialize(int $nbSamples, int $align): void
    {
        $this->frame->nb_samples = $nbSamples;
        $this->frame->format = $this->format->getSampleFormat();
        $this->frame->ch_layout = $this->layout->getLayout();

//        $this->initializeUserAttributes();

        if ($this->layout->getNbChannels() !== 0 && $nbSamples > 0) {
            // Free the old buffer if any.
            if ($this->buffer !== null) {
                $this->libAVCodec->av_free($this->buffer);
            }

            // Allocate a new buffer.
            $this->bufferSize = $this->libAVCodec->av_samples_get_buffer_size(
                null,
                $this->layout->getNbChannels(),
                $nbSamples,
                $this->format->getSampleFormat(),
                $align
            );

            $this->buffer = $this->libAVCodec->av_malloc($this->bufferSize);
            if (!$this->buffer) {
                throw new RuntimeException("Cannot allocate AudioFrame buffer");
            }

            // Connect data pointers to the buffer.
            $ret = $this->libAVCodec->avcodec_fill_audio_frame(
                $this->frame,
                $this->layout->getNbChannels(),
                $this->format->getSampleFormat(),
                $this->buffer,
                $this->bufferSize,
                $align
            );

            if ($ret < 0) {
                throw new RuntimeException("Cannot fill audio frame");
            }
        }
    }

    /**
     * Frees the allocated buffer when the object is destroyed.
     */
    public function __destruct()
    {
        parent::__destruct();
        $this->libAVCodec->av_free($this->buffer);
    }

    /**
     * Initializes user-defined attributes such as layout and format.
     */
    public function initializeUserAttributes(): void
    {
//        var_dump($this->frame->format);
//        die;
        $this->layout = new AudioLayout($this->frame->ch_layout);
        $this->format = new AudioFormat($this->frame->format);
    }

    /**
     * Returns the audio planes of the frame.
     *
     * @return AudioPlane[]
     */
    public function getPlanes(): array
    {
        $planes = [];
        $i = 0;
//        var_dump($this->frame->extended_data[0]);
//        var_dump($this->frame->extended_data[1]);


        while ($this->frame->extended_data[$i]) {
            $planes[] = new AudioPlane($this, $i++);
        }

        return $planes;
    }

    /**
     * Gets the number of audio samples per channel.
     *
     * @return int
     */
    public function getSamples(): int
    {
        return $this->frame->nb_samples;
    }

    /**
     * Gets the sample rate of the audio data.
     *
     * @return int
     */
    public function getSampleRate(): int
    {
        return $this->frame->sample_rate;
    }

    /**
     * Sets the sample rate of the audio data.
     *
     * @param int $value The sample rate in Hz.
     */
    public function setSampleRate(int $value): void
    {
        $this->frame->sample_rate = $value;
    }

    public function getFormat(): AudioFormat
    {
        return $this->format;
    }

    public function getLayout(): AudioLayout
    {
        return $this->layout;
    }

    /**
     * @return array{format: string, layout: string, samples: int, sampleRate: int, pts: int|null, timeBase: \stdClass, planes: list<string>}
     */
    public function __serialize(): array
    {
        $planes = [];
        foreach ($this->getPlanes() as $plane) {
            $planes[] = $plane->getData();
        }

        return [
            'format' => $this->format->getName(),
            'layout' => $this->layout->getName(),
            'samples' => $this->getSamples(),
            'sampleRate' => $this->getSampleRate(),
            'pts' => $this->getPts(),
            'timeBase' => $this->timeBase,
            'planes' => $planes,
        ];
    }

    /**
     * @param array{format?: string, layout?: string, samples?: int, sampleRate?: int, pts?: int|null, timeBase?: \stdClass, planes?: list<string>} $data
     */
    public function __unserialize(array $data): void
    {
        $this->__construct($data['format'] ?? 's16', $data['layout'] ?? 'stereo', $data['samples'] ?? 0);
        if (isset($data['sampleRate'])) {
            $this->setSampleRate($data['sampleRate']);
        }
        if (array_key_exists('pts', $data)) {
            $this->setPts($data['pts']);
        }
        if (isset($data['timeBase']) && $data['timeBase'] instanceof \stdClass) {
            $this->timeBase = $data['timeBase'];
        }
        foreach ($data['planes'] ?? [] as $index => $payload) {
            if ($payload !== '') {
                $this->putData($payload, $index);
            }
        }
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\AVCodec\Frame;

use FFI\CData;

interface FrameInterface
{
    public function getFrame(): CData;

    public function rebaseTime(CData $dst): void;
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\AVCodec;

use Webrtc\AVCodec\Exception\AvCodecException;

/**
 * Checks that a loaded FFmpeg shared library matches the headers this package binds against.
 *
 * The headers under src/libav/include describe one specific major version of FFmpeg. Binding
 * them against a different major means every struct offset is wrong, which does not fail
 * loudly: it silently corrupts the heap the first time a frame or context is written. The
 * major version therefore has to match exactly, not merely be "new enough".
 */
final class LibraryVersion
{
    /**
     * Extract the major version from what av_version_info() reports.
     *
     * FFmpeg reports either a release ("n9.0.1", "9.0.1") or a git description
     * ("N-109103-g1c0b0f2b1a"), the latter carrying no usable version number.
     *
     * @param string $info The value returned by av_version_info().
     * @return int|null The major version, or null if it could not be determined.
     */
    public static function major(string $info): ?int
    {
        return preg_match('/^n?(\d+)\./', $info, $m) ? (int) $m[1] : null;
    }

    /**
     * Assert that a loaded library matches the headers, or explain why it does not.
     *
     * @param string $info The value returned by av_version_info().
     * @param int $supported The major version the bundled headers describe.
     * @param string $library The library being loaded, for the error message.
     * @return void
     * @throws AvCodecException If the version does not match or cannot be determined.
     */
    public static function assertSupported(string $info, int $supported, string $library): void
    {
        $major = self::major($info);

        if ($major === $supported) {
            return;
        }

        throw new AvCodecException(sprintf(
            $major === null
                ? "%s reports the unrecognised version \"%s\", so it cannot be checked against the "
                    . "FFmpeg %d headers this package binds against. Set the library path explicitly "
                    . "to an FFmpeg %d build."
                : "%s is FFmpeg %s, but this package binds against the FFmpeg %d headers. Binding "
                    . "mismatched majors corrupts memory rather than failing cleanly, so loading is "
                    . "refused. Install FFmpeg %d, or leave the FFI extension disabled to play back "
                    . "pre-encoded media without transcoding.",
            $library,
            $major === null ? $info : (string) $major,
            $supported,
            $supported
        ));
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\AVCodec;

use FFI;
use FFI\CData;
use stdClass;
use Webrtc\AVCodec\Format\AudioFormat;
use Webrtc\AVCodec\Format\VideoFormat;
use Webrtc\Exception\InvalidArgumentException;
use Webrtc\Mixin\SharedLibraryInterface;

/**
 * Class Codec
 *
 * This class represents a codec and provides functionalities to interact with
 * the FFmpeg library for encoding or decoding media streams.
 *
 * Implements SharedLibraryInterface to ensure shared library access.
 */
class Codec implements SharedLibraryInterface
{
    /**
     * @var object Codec instance returned by FFmpeg.
     */
    private $codec;

    /**
     * @var object|null Descriptor for the codec.
     */
    private $desc;

    /**
     * @var bool Indicates if the codec is an encoder.
     */
    private $isEncoder;

    /**
     * @var FFI Reference to the FFmpeg shared library.
     */
    private FFI $libAVCodec;

    /**
     * Codec constructor.
     *
     * @param string $name The name of the codec.
     * @param string $mode The mode for the codec: "r" (decoder) or "w" (encoder).
     *
     * @throws InvalidArgumentException If the codec cannot be found or initialized.
     * @throws \InvalidArgumentException If the mode is invalid.
     */
    public function __construct(string $name, string $mode = "r")
    {
        $this->initiateSharedLibrary();
        $this->initializeCodec($name, $mode);
    }

    /**
     * Initializes the shared library reference.
     *
     * @throws InvalidArgumentException If the shared library is not initialized.
     */
    public function initiateSharedLibrary(): void
    {
        global $libAVCodec;
        if ($libAVCodec instanceof FFI) {
            $this->libAVCodec = $libAVCodec;
        } else {
            throw new InvalidArgumentException("Shared library not initialized.");
        }
    }

    /**
     * Initializes the codec by name and mode.
     *
     * @param string $name The name of the codec.
     * @param string $mode The mode for the codec: "r" (decoder) or "w" (encoder).
     *
     * @throws InvalidArgumentException If the codec cannot be found or the mode mismatches.
     * @throws InvalidArgumentException If the mode is invalid.
     */
    private function initializeCodec(string $name, string $mode): void
    {
        if (!in_array($mode, ["r", "w"], true)) {
            throw new InvalidArgumentException('Invalid mode; must be "r" or "w".');
        }

        $this->codec = $this->findCodecByName($name, $mode);
        if (!$this->codec) {
            throw new InvalidArgumentException("Unknown codec: $name");
        }

        $this->desc = $this->desc ?? $this->libAVCodec->avcodec_descriptor_get($this->codec->id);

        $this->isEncoder = $this->libAVCodec->av_codec_is_encoder($this->codec);

        if ($this->isEncoder && $this->libAVCodec->av_codec_is_decoder($this->codec)) {
            throw new InvalidArgumentException("{$this->codec->name} is both encoder and decoder.");
        }

        if (($mode == "w") != $this->isEncoder) {
            throw new InvalidArgumentException("Codec mode mismatch: $name, $mode.");
        }
    }

    /**
     * Finds a codec by its name and mode.
     *
     * @param string $name The name of the codec.
     * @param string $mode The mode for the codec: "r" (decoder) or "w" (encoder).
     *
     * @return object|null The codec instance, or null if not found.
     */
    private function findCodecByName(string $name, string $mode): ?object
    {
        $finder = $mode === "w" ? "avcodec_find_encoder_by_name" : "avcodec_find_decoder_by_name";
        $codec = $this->libAVCodec->$finder($name);

        if (!$codec) {
            $this->desc = $this->libAVCodec->avcodec_descriptor_get_by_name($name);
            if ($this->desc) {
                $finder = $mode === "w" ? "avcodec_find_encoder" : "avcodec_find_decoder";
                $codec = $this->libAVCodec->$finder($this->desc->id);
            }
        }

        return $codec;
    }

    /**
     * Checks if the codec is a decoder.
     *
     * @return bool True if the codec is a decoder; false otherwise.
     */
    public function isDecoder(): bool
    {
        return !$this->isEncoder;
    }

    /**
     * Checks if the codec is an encoder.
     *
     * @return bool True if the codec is a decoder; false otherwise.
     */
    public function isEncoder(): bool
    {
        return $this->isEncoder;
    }

    /**
     * Gets the name of the codec.
     *
     * @return string The codec name.
     */
    public function getName(): string
    {
        return $this->codec->name ?? "";
    }

    /**
     * Gets the long name of the codec.
     *
     * @return string The long name of the codec.
     */
    public function getLongName(): string
    {
        return $this->codec->long_name ?? "";
    }

    /**
     * Gets the type of media associated with the codec.
     *
     * @return string The media type.
     */
    public function getType(): string
    {
        return $this->libAVCodec->av_get_media_type_string($this->codec->type) ?? "";
    }

    /**
     * Gets the ID of the codec.
     *
     * @return int The codec ID.
     */
    public function getId(): int
    {
        return $this->codec->id;
    }

    /**
     * Gets the codec instance.
     *
     * @return object The codec instance.
     */
    public function getCodec()
    {
        return $this->codec;
    }

    /**
     *
     *
     * @return bool
     */
    public function getDelay(): bool
    {
        return boolval($this->codec->capabilities & (1 << 5));
    }

    /**
     * A list of supported VideoFormat, or null.
     *
     * @return array|null
     */
    public function getVideoFormats(): ?array
    {
        $config = $this->getSupportedConfig($this->libAVCodec->AV_CODEC_CONFIG_PIX_FORMAT);
        if ($config === null) {
            return null;
        }

        [$values, $count] = $config;
        $formats = $this->libAVCodec->cast('const enum AVPixelFormat *', $values);
        $ret = [];
        for ($i = 0; $i < $count; $i++) {
            $ret[] = new VideoFormat($formats[$i], 0, 0);
        }

        return $ret;
    }

    /**
     * A list of supported frame rates (as fractions), or null.
     *
     * @return array|null
     */
    public function getFrameRates(): ?array
    {
        $config = $this->getSupportedConfig($this->libAVCodec->AV_CODEC_CONFIG_FRAME_RATE);
        if ($config === null) {
            return null;
        }

        [$values, $count] = $config;
        $rates = $this->libAVCodec->cast('const AVRational *', $values);
        $ret = [];
        for ($i = 0; $i < $count; $i++) {
            $frameRateObj = new StdClass();
            $frameRateObj->num = $rates[$i]->num;
            $frameRateObj->den = $rates[$i]->den;
            $ret[] = $frameRateObj;
        }

        return $ret;
    }

    /**
     * A list of supported AudioFormat, or null.
     *
     * @return array|null
     */
    public function getAudioFormats(): ?array
    {
        $config = $this->getSupportedConfig($this->libAVCodec->AV_CODEC_CONFIG_SAMPLE_FORMAT);
        if ($config === null) {
            return null;
        }

        [$values, $count] = $config;
        $formats = $this->libAVCodec->cast('const enum AVSampleFormat *', $values);
        $ret = [];
        for ($i = 0; $i < $count; $i++) {
            $ret[] = new AudioFormat($formats[$i]);
        }

        return $ret;
    }

    /**
     * A list of supported audio sample rates (int), or null.
     *
     * @return array|null
     */
    public function getAudioRates(): ?array
    {
        $config = $this->getSupportedConfig($this->libAVCodec->AV_CODEC_CONFIG_SAMPLE_RATE);
        if ($config === null) {
            return null;
        }

        [$values, $count] = $config;
        $rates = $this->libAVCodec->cast('const int *', $values);
        $ret = [];
        for ($i = 0; $i < $count; $i++) {
            $ret[] = $rates[$i];
        }

        return $ret;
    }

    /**
     * @return array{CData, int}|null
     */
    private function getSupportedConfig(int $type): ?array
    {
        $values = $this->libAVCodec->new('const void *[1]');
        $count = $this->libAVCodec->new('int[1]');
        $result = $this->libAVCodec->avcodec_get_supported_config(
            null,
            $this->codec,
            $type,
            0,
            $values,
            $count
        );

        if ($result < 0 || FFI::isNull($values[0])) {
            return null;
        }

        return [$values[0], $count[0]];
    }

    public static function getCodecNames(): array
    {
        global $libAVCodec;
        if (!$libAVCodec instanceof FFI) {
            throw new InvalidArgumentException("Shared library not initialized.");
        }

        $names = [];
        $opaque = $libAVCodec->new("void *");

        while (true) {
            $ptr = $libAVCodec->av_codec_iterate(FFI::addr($opaque));
            if ($ptr === null) {
                break;
            }
            $names[] = $ptr->name;
        }

        return array_unique($names);
    }

    /**
     * Check if codec is experimental and is thus avoided in favor of non-experimental encoders.
     *
     * @return bool
     */
    public function isExperimental(): bool
    {
        return (bool)($this->codec->capabilities & (1 << 9));
    }

    /**
     * @return array{name: string, mode: string}
     */
    public function __serialize(): array
    {
        return [
            'name' => $this->getName(),
            'mode' => $this->isEncoder ? 'w' : 'r',
        ];
    }

    /**
     * @param array{name?: string, mode?: string} $data
     */
    public function __unserialize(array $data): void
    {
        $this->__construct($data['name'] ?? '', $data['mode'] ?? 'r');
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\AVCodec\Format;

use FFI;
use Webrtc\AVCodec\Exception\AvCodecException;
use Webrtc\Exception\InvalidArgumentException;
use Webrtc\Exception\RuntimeException;
use Webrtc\Mixin\SharedLibraryInterface;

/**
 * Class AudioFormat
 *
 * Represents an audio sample format and provides utility methods for handling
 * its properties such as name, byte size, a bit of depth, planar/packed format, and
 * container names. This class interacts with the AVCodec library via FFI.
 */
class AudioFormat implements SharedLibraryInterface
{
    /**
     * @var int The sample format retrieved from the AVCodec library.
     */
    private int $sampleFormat;

    /**
     * @var FFI The AVCodec shared library interface.
     */
    private FFI $libAVCodec;

    /**
     * AudioFormat constructor.
     *
     * @param string| int $format The name of the audio sample format.
     *
     * @throws RuntimeException If the provided format name is invalid.
     */
    public function __construct(string|int $format)
    {
        $this->initiateSharedLibrary();
        $this->sampleFormat = is_int($format) ? $format : $this->libAVCodec->av_get_sample_fmt($format);

        if ($this->sampleFormat < 0) {
            throw new RuntimeException("Invalid sample format: $format");
        }
    }

    /**
     * Get the name of the sample format.
     *
     * @return string The name of the sample format.
     */
    public function getName(): string
    {
        return $this->libAVCodec->av_get_sample_fmt_name($this->sampleFormat);
    }

    /**
     * Get the number of bytes per sample for the format.
     *
     * @return int The number of bytes per sample.
     */
    public function getBytes(): int
    {
        return $this->libAVCodec->av_get_bytes_per_sample($this->sampleFormat);
    }

    /**
     * Get the bit depth of the sample format.
     *
     * @return int The bit depth (bytes per sample * 8).
     */
    public function getBits(): int
    {
        return $this->getBytes() << 3; // Multiply by 8 using bitwise shift
    }

    /**
     * Determine if the sample format is planar.
     *
     * @return bool True if the format is planar, false otherwise.
     */
    public function isPlanar(): bool
    {
        return (bool)$this->libAVCodec->av_sample_fmt_is_planar($this->sampleFormat);
    }

    /**
     * Determine if the sample format is packed.
     *
     * @return bool True if the format is packed, false otherwise.
     */
    public function isPacked(): bool
    {
        return !$this->isPlanar();
    }

    /**
     * Get the planar version of the sample format.
     *
     * @return self The planar format representation.
     */
    public function getPlanar(): self
    {
        if ($this->isPlanar()) {
            return $this;
        }
        $planarFormat = $this->libAVCodec->av_get_planar_sample_fmt($this->sampleFormat);
        return new self((int)$planarFormat);
    }

    /**
     * Get the packed version of the sample format.
     *
     * @return self The packed format representation.
     */
    public function getPacked(): self
    {
        if ($this->isPacked()) {
            return $this;
        }
        $packedFormat = $this->libAVCodec->av_get_packed_sample_fmt($this->sampleFormat);
        return new self($packedFormat);
    }

    /**
     * Get the container name for the sample format.
     *
     * @return string The container name.
     *
     * @throws InvalidArgumentException If the format is planar.
     * @throws AvCodecException If the format layout is unknown.
     */
    public function getContainerName(): string
    {
        if ($this->isPlanar()) {
            throw new InvalidArgumentException("Planar formats do not have container names.");
        }

        $containerFormatPostfix = (unpack('S', pack('v', 1))[1] === 1) ? "le" : "be";

        return match ($this->sampleFormat) {
            $this->libAVCodec->AV_SAMPLE_FMT_U8 => "u8",
            $this->libAVCodec->AV_SAMPLE_FMT_S16 => "s16$containerFormatPostfix",
            $this->libAVCodec->AV_SAMPLE_FMT_S32 => "s32$containerFormatPostfix",
            $this->libAVCodec->AV_SAMPLE_FMT_FLT => "f32$containerFormatPostfix",
            $this->libAVCodec->AV_SAMPLE_FMT_DBL => "f64$containerFormatPostfix",
            default => throw new AvCodecException("Unknown sample format layout."),
        };
    }

    /**
     * Load the shared library instance for AVCodec.
     *
     * @return void
     *
     * @throws RuntimeException If the shared library is not initialized.
     */
    public function initiateSharedLibrary(): void
    {
        global $libAVCodec;

        if (!$libAVCodec instanceof FFI) {
            throw new RuntimeException("Failed to load AVCodec shared library.");
        }

        $this->libAVCodec = $libAVCodec;
    }

    /**
     * @return int
     */
    public function getSampleFormat(): int
    {
        return $this->sampleFormat;
    }

    /**
     * @return array{sampleFormat: int}
     */
    public function __serialize(): array
    {
        return ['sampleFormat' => $this->sampleFormat];
    }

    /**
     * @param array{sampleFormat?: int} $data
     */
    public function __unserialize(array $data): void
    {
        $this->__construct($data['sampleFormat'] ?? 0);
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\AVCodec\Format;

use FFI;
use FFI\CData;
use Webrtc\Exception\InvalidArgumentException;
use Webrtc\Mixin\SharedLibraryInterface;

/**
 * Video Format Class
 *
 * Represents a video pixel format and its properties, providing utilities
 * for working with different video formats in FFmpeg/AVCodec.
 *
 * @package Webrtc\AVCodec\Format
 */
class VideoFormat implements SharedLibraryInterface
{
    /**
     * @var FFI $libAVCodec FFI instance for AVCodec library
     */
    private FFI $libAVCodec;

    /**
     * @var CData|null $videoFormat FFI CData representing AVPixFmtDescriptor
     */
    private ?CData $videoFormat;

    /**
     * @var array $components Array of VideoFormatComponent objects
     */
    private array $components;

    /**
     * Constructor
     *
     * @param int|string|null $format Pixel format (enum value or name string)
     * @param int $width Frame width
     * @param int $height Frame height
     */
    public function __construct(private int|string|null $format,
                                private int             $width,
                                private int             $height,
    )
    {
        $this->initiateSharedLibrary();
        $this->format = is_string($this->format) ? self::getPixFmt($this->format) : $this->format;
        $this->videoFormat = $this->libAVCodec->av_pix_fmt_desc_get($this->format);
        $this->setVideoFormatComponents();
    }

    /**
     * Initialize format components
     */
    private function setVideoFormatComponents(): void
    {
        for ($i = 0; $i < $this->videoFormat->nb_components; $i++) {
            $this->components [] = new VideoFormatComponent($this, $i);
        }

    }

    /**
     * Get raw FFI video format descriptor
     *
     * @return CData AVPixFmtDescriptor structure
     */
    public function getVideoFormat(): CData
    {
        return $this->videoFormat;
    }

    /**
     * Get format components
     *
     * @return array<VideoFormatComponent> Array of video components
     */
    public function getComponents(): array
    {
        return $this->components;
    }

    /**
     * Get format name
     *
     * @return string Format name (e.g. "yuv420p")
     */
    public function getName(): string
    {
        return $this->videoFormat->name;
    }

    /**
     * Initialize shared library instance
     */
    public function initiateSharedLibrary(): void
    {
        global $libAVCodec;

        if ($libAVCodec instanceof FFI) {
            $this->libAVCodec = $libAVCodec;
        }
    }

    /**
     * Calculate chroma width
     *
     * @param int $lumaWidth Optional luma width (defaults to frame width)
     * @return int Chroma width
     */
    public function chromaWidth(int $lumaWidth = 0): int
    {
        $lumaWidth = $lumaWidth > 0 ? $lumaWidth : $this->width;
        return $lumaWidth ? -((-$lumaWidth) >> $this->videoFormat->log2_chroma_w) : 0;
    }

    /**
     * Calculate chroma height
     *
     * @param int $lumaHeight Optional luma height (defaults to frame height)
     * @return int Chroma height
     */
    public function chromaHeight(int $lumaHeight = 0): int
    {
        $lumaHeight = $lumaHeight > 0 ? $lumaHeight : $this->height;
        return $lumaHeight ? -((-$lumaHeight) >> $this->videoFormat->log2_chroma_h) : 0;
    }

    /**
     * Get frame height
     *
     * @return int Height in pixels
     */
    public function getHeight(): int
    {
        return $this->height;
    }

    /**
     * Get pixel format enum value
     *
     * @return int FFmpeg pixel format enum
     */
    public function getFormat(): int
    {
        return $this->format;
    }

    /**
     * Get frame width
     *
     * @return int Width in pixels
     */
    public function getWidth(): int
    {
        return $this->width;
    }

    /**
     * Set frame width
     *
     * @param int $width New width in pixels
     */
    public function setWidth(int $width): void
    {
        $this->width = $width;
    }

    /**
     * Set frame height
     *
     * @param int $height New height in pixels
     */
    public function setHeight(int $height): void
    {
        $this->height = $height;
    }

    /**
     * Convert pixel format name to enum value
     *
     * @param string $name Pixel format name (e.g. "yuv420p")
     * @return int FFmpeg pixel format enum
     * @throws InvalidArgumentException If the format is invalid or library not initialized
     */
    public static function getPixFmt(string $name): int
    {
        global $libAVCodec;

        if (!$libAVCodec instanceof FFI) {
            throw new InvalidArgumentException('LibAVCodec must be initialized');
        }

        $pixFmt = $libAVCodec->av_get_pix_fmt($name);

        if ($pixFmt === $libAVCodec->AV_PIX_FMT_NONE) {
            throw new InvalidArgumentException("Not a valid pixel format: {$name}");
        }

        return $pixFmt;
    }

    /**
     * Check if format has palette
     *
     * @return bool True if the format uses color palette
     */
    public function hasPalette(): bool
    {
        return (bool)($this->videoFormat->flags & (1 << 1));
    }

    /**
     * Check if the format is bitstream
     *
     * @return bool True if format is bitstream
     */
    public function isBitStream(): bool
    {
        return (bool)($this->videoFormat->flags & (1 << 2));
    }

    /**
     * Check if the format is planar
     *
     * @return bool True if the format is planar
     */
    public function isPlanar(): bool
    {
        return (bool)($this->videoFormat->flags & (1 << 4));
    }

    /**
     * Check if the format is RGB
     *
     * @return bool True if the format is RGB
     */
    public function isRgb(): bool
    {
        return (bool)($this->videoFormat->flags & (1 << 5));
    }

    /**
     * @return array{format: int|string|null, width: int, height: int}
     */
    public function __serialize(): array
    {
        return [
            'format' => $this->format,
            'width' => $this->width,
            'height' => $this->height,
        ];
    }

    /**
     * @param array{format?: int|string|null, width?: int, height?: int} $data
     */
    public function __unserialize(array $data): void
    {
        $this->__construct($data['format'] ?? null, $data['width'] ?? 0, $data['height'] ?? 0);
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\AVCodec\Format;

use FFI\CData;

/**
 * Video Format Component Class
 *
 * Represents an individual component (color channel/plane) of a video format.
 * Provides detailed information about each component in a pixel format.
 *
 * @package Webrtc\AVCodec\Format
 */
class VideoFormatComponent
{
    /**
     * @var CData $component FFI CData representing AVComponentDescriptor
     */
    private CData $component;

    /**
     * Constructor
     *
     * @param VideoFormat $videoFormat Parent video format
     * @param int $index Component index
     */
    public function __construct(private readonly VideoFormat $videoFormat,
                                private readonly int         $index)
    {
        $this->component = $this->videoFormat->getVideoFormat()->comp[$this->index];
    }

    /**
     * @return array{videoFormat: VideoFormat, index: int}
     */
    public function __serialize(): array
    {
        return [
            'videoFormat' => $this->videoFormat,
            'index' => $this->index,
        ];
    }

    /**
     * @param array{videoFormat?: VideoFormat, index?: int} $data
     */
    public function __unserialize(array $data): void
    {
        $this->__construct($data['videoFormat'], $data['index'] ?? 0);
    }

    /**
     * Get parent video format
     *
     * @return VideoFormat Associated video format
     */
    public function getVideoFormat(): VideoFormat
    {
        return $this->videoFormat;
    }

    /**
     * Get component index
     *
     * @return int Component index (0-based)
     */
    public function getIndex(): int
    {
        return $this->index;
    }

    /**
     * Get raw FFI component descriptor
     *
     * @return CData AVComponentDescriptor structure
     */
    public function getComponent(): CData
    {
        return $this->component;
    }

    /**
     * Get plane index for this component
     *
     * @return int Plane index where this component is stored
     */
    public function getPlane(): int
    {
        return $this->component->plane;
    }

    /**
     * Get bit depth of component
     *
     * @return int Number of bits used per sample
     */
    public function getBits(): int
    {
        return $this->component->depth;
    }

    /**
     * Check if component is alpha channel
     *
     * @return bool True if component represents alpha/transparency
     */
    public function isAlpha(): bool
    {
        return ($this->index === 1 && $this->videoFormat->getVideoFormat()->nb_components === 2) ||
            ($this->index === 3 && $this->videoFormat->getVideoFormat()->nb_components === 4);
    }

    /**
     * Check if component is luma (Y) channel
     *
     * @return bool True if component represents luma
     */
    public function isLuma(): bool
    {
        return $this->index === 0 && (
                $this->videoFormat->getVideoFormat()->nb_components === 1 ||
                $this->videoFormat->getVideoFormat()->nb_components === 2 ||
                !$this->videoFormat->isRgb()
            );
    }

    /**
     * Check if component is chroma (U/V) channel
     *
     * @return bool True if component represents chroma
     */
    public function isChroma(): bool
    {
        return in_array($this->index, [1, 2]) &&
            ($this->videoFormat->getVideoFormat()->log2_chroma_w || $this->videoFormat->getVideoFormat()->log2_chroma_h);
    }

    /**
     * Get component width
     *
     * Accounts for chroma subsampling if component is chroma
     * @return int Width in pixels
     */
    public function getWidth(): int
    {
        return $this->isChroma() ? $this->videoFormat->chromaWidth() : $this->videoFormat->getWidth();
    }

    /**
     * Get component height
     *
     * Accounts for chroma subsampling if component is chroma
     * @return int Height in pixels
     */
    public function getHeight(): int
    {
        return $this->isChroma() ? $this->videoFormat->chromaHeight() : $this->videoFormat->getHeight();
    }
}{
    "name": "danog/php-rtc-av",
    "type": "library",
    "description": "FFmpeg/libav FFI bindings for PHP media processing. MadelineProto's PHP 8.2+ fork uses lazy, optional FFI, strict ABI checks, and the modular danog/php-rtc stack.",
    "keywords": [
        "ffmpeg",
        "libavcodec",
        "avcodec",
        "libavfilter",
        "avfilter",
        "libavformat",
        "avformat",
        "libswscale",
        "swscale",
        "webrtc",
        "codec"
    ],
    "homepage": "https://github.com/danog/php-rtc-av",
    "license": "MIT",
    "authors": [
        {
            "name": "Amin Yazdanpanah",
            "email": "github@aminyazdanpanah.com"
        },
        {
            "name": "Sana Moniri",
            "email": "s.mo@quasarstream.com"
        },
        {
            "name": "Quasar Stream Community"
        },
        {
            "name": "Daniil Gentili",
            "email": "daniil@daniil.it"
        }
    ],
    "require": {
        "php-64bit": "^8.2",
        "danog/php-rtc-exception": "^1.1.5",
        "danog/php-rtc-mixin": "^1.1.6 || ^2.0"
    },
    "require-dev": {
        "phpunit/phpunit": "^11.5 || ^12.0 || ^13.3.2",
        "bamarni/composer-bin-plugin": "^1.9.1"
    },
    "autoload": {
        "psr-4": {
            "Webrtc\\AVCodec\\": "src"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\Webrtc\\AVCodec\\": "tests"
        }
    },
    "scripts": {
        "setup-ffmpeg": [
            "php scripts/ffmpeg-downloader.php"
        ],
        "pre-install-cmd": [
            "@check-linux"
        ],
        "pre-update-cmd": [
            "@check-linux"
        ],
        "check-linux": [
            "php -r \"if (stripos(PHP_OS_FAMILY, 'Linux') === false) { fwrite(STDERR, 'This package only supports Linux.' . PHP_EOL); exit(1); }\""
        ]
    },
    "minimum-stability": "stable",
    "suggest": {
        "ext-ffi": "Install the FFI extension and libavcodec/libavformat/libavfilter to transcode media; not required to mux or packetize already-encoded media"
    },
    "config": {
        "allow-plugins": {
            "bamarni/composer-bin-plugin": true
        }
    },
    "extra": {
        "bamarni-bin": {
            "bin-links": true,
            "forward-command": true
        }
    }
}
<?php declare(strict_types=1);

require 'vendor/autoload.php';

use danog\Loop\PeriodicLoop;

use function Amp\delay;

/** @var PeriodicLoop[] */
$loops = [];
for ($x = 0; $x < 10; $x++) {
    $callable = function (PeriodicLoop $loop): bool {
        static $number = 0;
        echo "$loop: $number".PHP_EOL;
        $number++;
        return $number == 10;
    };
    $loop = new PeriodicLoop($callable, "Loop number $x", 1.0);
    $loop->start();
    delay(0.1);
    $loops []= $loop;
}
delay(5);
echo "Resuming prematurely all loops!".PHP_EOL;
foreach ($loops as $loop) {
    $loop->resume();
}
echo "OK done, waiting 5 more seconds!".PHP_EOL;
delay(5);
echo "Closing all loops!".PHP_EOL;
delay(0.01);
<?php declare(strict_types=1);

require 'vendor/autoload.php';

use danog\Loop\GenericLoop;
use danog\Loop\Loop;

use function Amp\delay;

class MyLoop extends Loop
{
    private int $number = 0;

    public function __construct(private string $name)
    {
    }

    protected function loop(): ?float
    {
        echo "$this: {$this->number}".PHP_EOL;
        $this->number++;
        return $this->number < 10 ? 1.0 : GenericLoop::STOP;
    }

    public function __toString(): string
    {
        return $this->name;
    }
}

/** @var MyLoop[] */
$loops = [];
for ($x = 0; $x < 10; $x++) {
    $loop = new MyLoop("Loop number $x");
    $loop->start();
    delay(0.1);
    $loops []= $loop;
}
delay(5);
echo "Resuming prematurely all loops!".PHP_EOL;
foreach ($loops as $loop) {
    $loop->resume();
}
echo "OK done, waiting 5 more seconds!".PHP_EOL;
delay(5);
echo "Closing all loops!".PHP_EOL;
delay(0.01);
<?php declare(strict_types=1);

require 'vendor/autoload.php';

use danog\Loop\GenericLoop;

use function Amp\delay;

/** @var GenericLoop[] */
$loops = [];
for ($x = 0; $x < 10; $x++) {
    $callable = function (GenericLoop $loop): float {
        static $number = 0;
        echo "$loop: $number".PHP_EOL;
        $number++;
        return $number < 10 ? 1.0 : GenericLoop::STOP;
    };
    $loop = new GenericLoop($callable, "Loop number $x");
    $loop->start();
    delay(0.1);
    $loops []= $loop;
}
delay(5);
echo "Resuming prematurely all loops!".PHP_EOL;
foreach ($loops as $loop) {
    $loop->resume();
}
echo "OK done, waiting 5 more seconds!".PHP_EOL;
delay(5);
echo "Closing all loops!".PHP_EOL;
delay(0.01);
<?php

$config = new class extends Amp\CodeStyle\Config {
    public function getRules(): array
    {
        return array_merge(parent::getRules(), [
            'void_return' => true,
        ]);
    }
};

$config->getFinder()
    ->in(__DIR__ . '/lib')
    ->in(__DIR__ . '/test')
    ->in(__DIR__ . '/examples');

$cacheDir = getenv('TRAVIS') ? getenv('HOME') . '/.php-cs-fixer' : __DIR__;

$config->setCacheFile($cacheDir . '/.php_cs.cache');

return $config;
<?php declare(strict_types=1);
/**
 * Periodic loop.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2020 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/MIT MIT
 */

namespace danog\Loop;

/**
 * Periodic loop.
 *
 * @api
 *
 * @author Daniil Gentili <daniil@daniil.it>
 */
class PeriodicLoop extends GenericLoop
{
    /**
     * Constructor.
     *
     * Runs a callback at a periodic interval.
     *
     * The callable will be passed the instance of the current loop.
     *
     * The loop can be stopped from the outside by calling stop()
     * and from the inside by returning `true`.
     *
     * @param callable(static):bool $callback Callable to run
     * @param string   $name     Loop name
     * @param ?float   $interval Loop interval; if null, pauses indefinitely or until `resume()` is called.
     */
    public function __construct(callable $callback, string $name, ?float $interval)
    {
        /** @psalm-suppress InvalidArgument */
        parent::__construct(
            /** @param static $loop */
            static function (self $loop) use ($callback, $interval): ?float {
                if ($callback($loop) === true) {
                    return GenericLoop::STOP;
                }
                return $interval;
            },
            $name
        );
    }
}
<?php declare(strict_types=1);

/**
 * Generic loop.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2020 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/MIT MIT
 */

namespace danog\Loop;

use Amp\DeferredFuture;
use AssertionError;
use Revolt\EventLoop;
use Stringable;

/**
 * Generic loop, runs single callable.
 *
 * @api
 *
 * @author Daniil Gentili <daniil@daniil.it>
 */
abstract class Loop implements Stringable
{
    /**
     * Stop the loop.
     */
    public const STOP = -1.0;
    /**
     * Pause the loop.
     */
    public const PAUSE = null;
    /**
     * Rerun the loop.
     */
    public const CONTINUE = 0.0;
    /**
     * Whether the loop is running.
     */
    private bool $running = false;
    /**
     * Resume timer ID.
     */
    private ?string $resumeTimer = null;
    /**
     * Resume deferred ID.
     */
    private ?string $resumeImmediate = null;
    /**
     * Shutdown deferred.
     */
    private ?DeferredFuture $shutdownDeferred = null;

    /**
     * Report pause, can be overriden for logging.
     *
     * @psalm-suppress PossiblyUnusedParam
     *
     * @param float $timeout Pause duration, 0 = forever
     */
    protected function reportPause(float $timeout): void
    {
    }

    /**
     * Start the loop.
     *
     * Returns false if the loop is already running.
     */
    public function start(): bool
    {
        while ($this->shutdownDeferred !== null) {
            $this->shutdownDeferred->getFuture()->await();
        }
        if ($this->running) {
            return false;
        }
        $this->running = true;
        if (!$this->resume()) {
            // @codeCoverageIgnoreStart
            throw new AssertionError("Could not resume!");
            // @codeCoverageIgnoreEnd
        }
        $this->startedLoop();
        return true;
    }
    /**
     * Stops loop.
     *
     * Returns false if the loop is not running.
     */
    public function stop(): bool
    {
        if (!$this->running) {
            return false;
        }
        $this->running = false;
        if ($this->resumeTimer) {
            $storedWatcherId = $this->resumeTimer;
            EventLoop::cancel($storedWatcherId);
            $this->resumeTimer = null;
        }
        if ($this->resumeImmediate) {
            $storedWatcherId = $this->resumeImmediate;
            EventLoop::cancel($storedWatcherId);
            $this->resumeImmediate = null;
        }
        if ($this->paused) {
            $this->exitedLoop();
        } else {
            if ($this->shutdownDeferred !== null) {
                // @codeCoverageIgnoreStart
                throw new AssertionError("Shutdown deferred is not null!");
                // @codeCoverageIgnoreEnd
            }
            $this->shutdownDeferred = new DeferredFuture;
        }
        return true;
    }
    abstract protected function loop(): ?float;

    private bool $paused = true;
    private function loopInternal(): void
    {
        if (!$this->running) {
            // @codeCoverageIgnoreStart
            throw new AssertionError("Already running!");
            // @codeCoverageIgnoreEnd
        }
        if (!$this->paused) {
            // @codeCoverageIgnoreStart
            throw new AssertionError("Already paused!");
            // @codeCoverageIgnoreEnd
        }
        $this->paused = false;
        try {
            $timeout = $this->loop();
        } catch (\Throwable $e) {
            $this->exitedLoopInternal();
            throw $e;
        }
        /** @var bool $this->running */
        if (!$this->running) {
            $this->exitedLoopInternal();
            return;
        }
        if ($timeout === self::STOP) {
            $this->exitedLoopInternal();
            return;
        }

        $this->paused = true;
        if ($timeout === self::PAUSE) {
            $this->reportPause(0.0);
        } else {
            if (!$this->resumeImmediate) {
                if ($this->resumeTimer !== null) {
                    // @codeCoverageIgnoreStart
                    throw new AssertionError("Already have a resume timer!");
                    // @codeCoverageIgnoreEnd
                }
                $this->resumeTimer = EventLoop::delay($timeout, function (): void {
                    $this->resumeTimer = null;
                    $this->loopInternal();
                });
            }
            if ($timeout !== self::CONTINUE) {
                $this->reportPause($timeout);
            }
        }
    }

    private function exitedLoopInternal(): void
    {
        $this->running = false;
        $this->paused = true;
        if ($this->resumeTimer !== null) {
            // @codeCoverageIgnoreStart
            throw new AssertionError("Already have a resume timer!");
            // @codeCoverageIgnoreEnd
        }
        if ($this->resumeImmediate !== null) {
            // @codeCoverageIgnoreStart
            throw new AssertionError("Already have a resume immediate timer!");
            // @codeCoverageIgnoreEnd
        }
        $this->exitedLoop();
        if ($this->shutdownDeferred !== null) {
            $d = $this->shutdownDeferred;
            $this->shutdownDeferred = null;
            EventLoop::queue($d->complete(...));
        }
    }
    /**
     * Signal that loop was started.
     */
    protected function startedLoop(): void
    {
    }
    /**
     * Signal that loop has exited.
     */
    protected function exitedLoop(): void
    {
    }
    /**
     * Check whether loop is running.
     */
    public function isRunning(): bool
    {
        return $this->running;
    }
    /**
     * Check whether loop is paused (different from isRunning, a loop may be running but paused).
     */
    public function isPaused(): bool
    {
        return $this->paused;
    }

    /**
     * Resume the loop.
     *
     * If resume is called multiple times, and the event loop hasn't resumed the loop yet,
     * the loop will be resumed only once, not N times for every call.
     *
     * @param bool $postpone If true, multiple resumes will postpone the resuming to the end of the callback queue instead of leaving its position unchanged.
     *
     * @return bool Returns false if the loop is not paused.
     */
    public function resume(bool $postpone = false): bool
    {
        if ($this->running && $this->paused) {
            if ($this->resumeImmediate) {
                if (!$postpone) {
                    return true;
                }
                $resumeImmediate = $this->resumeImmediate;
                $this->resumeImmediate = null;
                EventLoop::cancel($resumeImmediate);
            }
            if ($this->resumeTimer) {
                $timer = $this->resumeTimer;
                $this->resumeTimer = null;
                EventLoop::cancel($timer);
            }
            $this->resumeImmediate = EventLoop::defer(function (): void {
                $this->resumeImmediate = null;
                $this->loopInternal();
            });
            return true;
        }
        return false;
    }
}
<?php declare(strict_types=1);

/**
 * Generic loop.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2020 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/MIT MIT
 */

namespace danog\Loop;

/**
 * Generic loop, runs single callable.
 *
 * @api
 *
 * @author Daniil Gentili <daniil@daniil.it>
 */
class GenericLoop extends Loop
{
    /**
     * Callable.
     *
     * @var callable(static):?float
     */
    private $callable;
    /**
     * Constructor.
     *
     * The return value of the callable can be:
     * * A number - the loop will be paused for the specified number of seconds
     * * GenericLoop::STOP - The loop will stop
     * * GenericLoop::PAUSE - The loop will pause forever (or until loop is `resumed()`
     *                        from outside the loop)
     * * GenericLoop::CONTINUE - Return this if you want to rerun the loop immediately
     *
     * If the callable does not return anything,
     * the loop will behave is if GenericLoop::PAUSE was returned.
     *
     * The callable will be passed the instance of the current loop.
     *
     * The loop can be stopped from the outside by using stop().
     *
     * @param callable(static):?float $callable Callable to run
     * @param string   $name     Loop name
     */
    public function __construct(callable $callable, private string $name)
    {
        $this->callable = $callable;
    }

    protected function loop(): ?float
    {
        return ($this->callable)($this);
    }
    public function __toString(): string
    {
        return $this->name;
    }
}
<?php declare(strict_types=1);
/**
 * Loop test.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2020 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/MIT MIT
 */

namespace danog\Loop\Test;

use PHPUnit\Framework\TestCase;
use Revolt\EventLoop;

/**
 * Fixtures.
 */
abstract class Fixtures extends TestCase
{
    const LOOP_NAME = 'TTTT';
    protected static function waitTick(): void
    {
        $f = new \Amp\DeferredFuture;
        \Revolt\EventLoop::defer(fn () => $f->complete());
        $f->getFuture()->await();
    }
    protected function setUp(): void
    {
        EventLoop::run();
    }
    protected function tearDown(): void
    {
        EventLoop::run();
    }
}
<?php declare(strict_types=1);
/**
 * Exception test trait.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2020 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/MIT MIT
 */

namespace danog\Loop\Test\Traits;

trait BasicException
{
    use Basic;
    /**
     * Loop implementation.
     *
     */
    public function loop(): ?float
    {
        $this->inited = true;
        throw new \RuntimeException('Threw exception!');
        $this->ran = true;
    }
}
<?php declare(strict_types=1);
/**
 * Loop test trait.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2020 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/MIT MIT
 */

namespace danog\Loop\Test\Traits;

trait Logging
{
    /**
     * Check whether the loop started.
     */
    private int $startCounter = 0;
    /**
     * Check whether the loop ended.
     */
    private int $endCounter = 0;

    /**
     * Signal that loop started.
     *
     */
    final protected function startedLoop(): void
    {
        parent::startedLoop();
        $this->startCounter++;
    }
    /**
     * Signal that loop ended.
     *
     */
    final protected function exitedLoop(): void
    {
        parent::exitedLoop();
        $this->endCounter++;
    }

    /**
     * Get start counter.
     */
    public function startCounter(): int
    {
        return $this->startCounter;
    }
    /**
     * Get end counter.
     */
    public function endCounter(): int
    {
        return $this->endCounter;
    }
}
<?php declare(strict_types=1);
/**
 * Loop test trait.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2020 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/MIT MIT
 */

namespace danog\Loop\Test\Traits;

use function Amp\delay;

trait LoggingPause
{
    use Logging;
    /**
     * Number of times loop was paused.
     */
    private int $pauseCount = 0;
    /**
     * Last pause delay.
     */
    private float $lastPause = 0;
    /**
     * Get number of times loop was paused.
     */
    public function getPauseCount(): int
    {
        return $this->pauseCount;
    }

    /**
     * Get last pause.
     */
    public function getLastPause(): float
    {
        return $this->lastPause;
    }
    /**
     * Report pause, can be overriden for logging.
     *
     * @param integer $timeout Pause duration, 0 = forever
     *
     */
    protected function reportPause(float $timeout): void
    {
        parent::reportPause($timeout);
        $this->pauseCount++;
        $this->lastPause = $timeout;
    }
}
<?php declare(strict_types=1);
/**
 * Loop test trait.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2020 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/MIT MIT
 */

namespace danog\Loop\Test\Traits;

use danog\Loop\Loop;
use danog\Loop\Test\LoopTest;

use function Amp\delay;

trait Basic
{
    use Logging;
    /**
     * Check whether the loop inited.
     *
     * @var bool
     */
    private $inited = false;
    /**
     * Check whether the loop ran.
     *
     * @var bool
     */
    private $ran = false;
    /**
     * Check whether the loop inited.
     */
    public function inited(): bool
    {
        return $this->inited;
    }
    /**
     * Check whether the loop ran.
     */
    public function ran(): bool
    {
        return $this->ran;
    }
    /**
     * Loop implementation.
     */
    public function loop(): ?float
    {
        $this->inited = true;
        delay(0.1);
        $this->ran = true;
        return Loop::STOP;
    }
    /**
     * Get loop name.
     *
     */
    public function __toString(): string
    {
        return LoopTest::LOOP_NAME;
    }
}
<?php declare(strict_types=1);
/**
 * Loop test.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2020 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/MIT MIT
 */

namespace danog\Loop\Test;

use danog\Loop\GenericLoop;
use danog\Loop\Loop;
use danog\Loop\Test\Interfaces\LoggingPauseInterface;
use danog\Loop\Test\Traits\Basic;
use danog\Loop\Test\Traits\LoggingPause;
use Revolt\EventLoop;

use function Amp\delay;

class GenericTest extends Fixtures
{
    /**
     * Test basic loop.
     *
     * @param bool $stopSig Whether to stop with signal
     *
     *
     *
     * @dataProvider provideTrueFalse
     */
    public function testGeneric(bool $stopSig): void
    {
        $runCount = 0;
        $pauseTime = GenericLoop::PAUSE;
        $callable = function (GenericLoop $genericLoop) use (&$runCount, &$pauseTime, &$l) {
            $l = $genericLoop;
            $runCount++;
            return $pauseTime;
        };
        $this->fixtureAssertions($callable, $runCount, $pauseTime, $stopSig, $l);
        $obj = new class() {
            public $pauseTime = GenericLoop::PAUSE;
            public $runCount = 0;
            public ?GenericLoop $loop;
            public function run(GenericLoop $loop)
            {
                $this->loop = $loop;
                $this->runCount++;
                return $this->pauseTime;
            }
        };
        $this->fixtureAssertions([$obj, 'run'], $obj->runCount, $obj->pauseTime, $stopSig, $obj->loop);
        $obj = new class() {
            public $pauseTime = GenericLoop::PAUSE;
            public $runCount = 0;
            public ?GenericLoop $loop;
            public function run(GenericLoop $loop)
            {
                $this->loop = $loop;
                $this->runCount++;
                return $this->pauseTime;
            }
        };
        $this->fixtureAssertions(\Closure::fromCallable([$obj, 'run']), $obj->runCount, $obj->pauseTime, $stopSig, $obj->loop);
    }
    /**
     * Test generator loop.
     *
     * @param bool $stopSig Whether to stop with signal
     *
     *
     *
     * @dataProvider provideTrueFalse
     */
    public function testGenerator(bool $stopSig): void
    {
        $runCount = 0;
        $pauseTime = GenericLoop::PAUSE;
        $callable = function (GenericLoop $loop) use (&$runCount, &$pauseTime, &$l) {
            $l = $loop;
            $runCount++;
            return $pauseTime;
        };
        $this->fixtureAssertions($callable, $runCount, $pauseTime, $stopSig, $l);
        $obj = new class() {
            public $pauseTime = GenericLoop::PAUSE;
            public $runCount = 0;
            public ?GenericLoop $loop;
            public function run(GenericLoop $loop)
            {
                $this->loop = $loop;
                $this->runCount++;
                return $this->pauseTime;
            }
        };
        $this->fixtureAssertions([$obj, 'run'], $obj->runCount, $obj->pauseTime, $stopSig, $obj->loop);
        $obj = new class() {
            public $pauseTime = GenericLoop::PAUSE;
            public $runCount = 0;
            public ?GenericLoop $loop;
            public function run(GenericLoop $loop)
            {
                $this->loop = $loop;
                $this->runCount++;
                return $this->pauseTime;
            }
        };
        $this->fixtureAssertions(\Closure::fromCallable([$obj, 'run']), $obj->runCount, $obj->pauseTime, $stopSig, $obj->loop);
    }
    /**
     * Fixture assertions for started loop.
     */
    private function fixtureStarted(Loop&LoggingPauseInterface $loop, int $offset = 1): void
    {
        $this->assertTrue($loop->isRunning());
        $this->assertEquals($offset, $loop->startCounter());
        $this->assertEquals($offset-1, $loop->endCounter());
    }
    /**
     * Run fixture assertions.
     *
     * @param callable $closure   Closure
     * @param integer  $runCount  Run count
     * @param ?float   $pauseTime Pause time
     * @param bool     $stopSig   Whether to stop with signal
     */
    private function fixtureAssertions(callable $closure, int &$runCount, ?float &$pauseTime, bool $stopSig, ?GenericLoop &$l): void
    {
        $loop = new class($closure, Fixtures::LOOP_NAME) extends GenericLoop implements LoggingPauseInterface {
            use LoggingPause;
        };
        $expectedRunCount = 0;

        $this->assertEquals(Fixtures::LOOP_NAME, "$loop");

        $this->assertFalse($loop->isRunning());
        $this->assertEquals(0, $loop->startCounter());
        $this->assertEquals(0, $loop->endCounter());

        $this->assertEquals($expectedRunCount, $runCount);
        $this->assertEquals(0, $loop->getPauseCount());

        $this->assertTrue($loop->start());
        self::waitTick();
        $this->fixtureStarted($loop);
        $expectedRunCount++;
        $this->assertEquals($loop, $l);

        $this->assertEquals($expectedRunCount, $runCount);
        $this->assertEquals(1, $loop->getPauseCount());
        $this->assertEquals(0, $loop->getLastPause());
        $this->assertTrue($loop->isPaused());

        $pauseTime = 0.1;
        $this->assertTrue($loop->resume());
        self::waitTick();
        $this->fixtureStarted($loop);
        $expectedRunCount++;

        $this->assertEquals($expectedRunCount, $runCount);
        $this->assertEquals(2, $loop->getPauseCount());
        $this->assertEquals(0.1, $loop->getLastPause());
        $this->assertTrue($loop->isPaused());

        delay(0.048);
        $this->fixtureStarted($loop);

        $this->assertEquals($expectedRunCount, $runCount);
        $this->assertEquals(2, $loop->getPauseCount());
        $this->assertEquals(0.1, $loop->getLastPause());
        $this->assertTrue($loop->isPaused());

        delay(0.060);
        $this->fixtureStarted($loop);
        $expectedRunCount++;

        $this->assertEquals($expectedRunCount, $runCount);
        $this->assertEquals(3, $loop->getPauseCount());
        $this->assertEquals(0.1, $loop->getLastPause());
        $this->assertTrue($loop->isPaused());

        $this->assertTrue($loop->resume());
        self::waitTick();
        $expectedRunCount++;

        $this->assertEquals($expectedRunCount, $runCount);
        $this->assertEquals(4, $loop->getPauseCount());
        $this->assertEquals(0.1, $loop->getLastPause());
        $this->assertTrue($loop->isPaused());

        if ($stopSig) {
            $this->assertTrue($loop->stop());
        } else {
            $pauseTime = GenericLoop::STOP;
            $this->assertTrue($loop->resume());
            $expectedRunCount++;
        }
        self::waitTick();
        $this->assertEquals($expectedRunCount, $runCount);
        $this->assertEquals(4, $loop->getPauseCount());
        $this->assertEquals(0.1, $loop->getLastPause());
        $this->assertTrue($loop->isPaused());

        $this->assertEquals(1, $loop->startCounter());
        $this->assertEquals(1, $loop->endCounter());

        $this->assertFalse($loop->isRunning());
        $this->assertFalse($loop->stop());
        $this->assertFalse($loop->resume());

        // Restart loop
        $pauseTime = GenericLoop::PAUSE;
        $this->assertTrue($loop->start());
        self::waitTick();
        $this->fixtureStarted($loop, 2);
        $expectedRunCount++;

        $this->assertEquals($expectedRunCount, $runCount);
        $this->assertEquals(5, $loop->getPauseCount());
        $this->assertEquals(0.0, $loop->getLastPause());
        $this->assertTrue($loop->isPaused());

        if ($stopSig) {
            $this->assertTrue($loop->stop());
        } else {
            $pauseTime = GenericLoop::STOP;
            $this->assertTrue($loop->resume());
            $expectedRunCount++;
        }
        self::waitTick();
        $this->assertEquals($expectedRunCount, $runCount);
        $this->assertEquals(5, $loop->getPauseCount());
        $this->assertEquals(0.0, $loop->getLastPause());
        $this->assertTrue($loop->isPaused());

        $this->assertEquals(2, $loop->startCounter());
        $this->assertEquals(2, $loop->endCounter());

        $this->assertFalse($loop->isRunning());
        $this->assertFalse($loop->stop());
        $this->assertFalse($loop->resume());

        // Restart loop and stop it immediately
        $pauseTime = GenericLoop::PAUSE;
        $this->assertTrue($loop->start());
        $this->assertTrue($loop->stop());
        self::waitTick();

        $this->assertEquals($expectedRunCount, $runCount);
        $this->assertEquals(5, $loop->getPauseCount());
        $this->assertEquals(0.0, $loop->getLastPause());
        $this->assertTrue($loop->isPaused());

        $this->assertEquals(3, $loop->startCounter());
        $this->assertEquals(3, $loop->endCounter());

        $this->assertFalse($loop->isRunning());
        $this->assertFalse($loop->stop());
        $this->assertFalse($loop->resume());

        // Restart loop with delay and stop it immediately
        $pauseTime = 1.0;
        $this->assertTrue($loop->start());
        $this->assertTrue($loop->stop());
        self::waitTick();

        $this->assertEquals($expectedRunCount, $runCount);
        $this->assertEquals(5, $loop->getPauseCount());
        $this->assertEquals(0.0, $loop->getLastPause());
        $this->assertTrue($loop->isPaused());

        $this->assertEquals(4, $loop->startCounter());
        $this->assertEquals(4, $loop->endCounter());

        $this->assertFalse($loop->isRunning());
        $this->assertFalse($loop->stop());
        $this->assertFalse($loop->resume());

        // Restart loop, without postponing resuming
        $pauseTime = GenericLoop::PAUSE;
        $this->assertTrue($loop->start());
        self::waitTick();
        $this->fixtureStarted($loop, 5);
        $expectedRunCount++;

        $this->assertEquals($expectedRunCount, $runCount);
        $this->assertEquals(6, $loop->getPauseCount());
        $this->assertEquals(0.0, $loop->getLastPause());
        $this->assertTrue($loop->isPaused());

        $pauseTime = GenericLoop::STOP;
        $this->assertTrue($loop->resume(false));
        $this->assertTrue($loop->resume(false));
        $expectedRunCount++;
        self::waitTick();
        $this->assertEquals($expectedRunCount, $runCount);
        $this->assertEquals(6, $loop->getPauseCount());
        $this->assertEquals(0.0, $loop->getLastPause());
        $this->assertTrue($loop->isPaused());

        $this->assertEquals(5, $loop->startCounter());
        $this->assertEquals(5, $loop->endCounter());

        $this->assertFalse($loop->isRunning());
        $this->assertFalse($loop->stop());
        $this->assertFalse($loop->resume());

        // Restart loop, postponing resuming
        $pauseTime = GenericLoop::PAUSE;
        $this->assertTrue($loop->start());
        self::waitTick();
        $this->fixtureStarted($loop, 6);
        $expectedRunCount++;

        $this->assertEquals($expectedRunCount, $runCount);
        $this->assertEquals(7, $loop->getPauseCount());
        $this->assertEquals(0.0, $loop->getLastPause());
        $this->assertTrue($loop->isPaused());

        $pauseTime = GenericLoop::STOP;
        $this->assertTrue($loop->resume(true));
        EventLoop::queue(fn () => $this->assertTrue($loop->resume(true)));
        self::waitTick();
        $this->assertEquals($expectedRunCount, $runCount);
        $this->assertEquals(7, $loop->getPauseCount());
        $this->assertEquals(0.0, $loop->getLastPause());
        $this->assertTrue($loop->isPaused());

        $this->assertEquals(6, $loop->startCounter());
        $this->assertEquals(5, $loop->endCounter());

        self::waitTick();
        $expectedRunCount++;
        $this->assertEquals($expectedRunCount, $runCount);
        $this->assertEquals(7, $loop->getPauseCount());
        $this->assertEquals(0.0, $loop->getLastPause());
        $this->assertTrue($loop->isPaused());

        $this->assertEquals(6, $loop->startCounter());
        $this->assertEquals(6, $loop->endCounter());
    }

    /**
     * Provide true false.
     *
     */
    public function provideTrueFalse(): array
    {
        return [
            [true],
            [false]
        ];
    }
}
<?php declare(strict_types=1);
/**
 * Loop test.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2020 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/MIT MIT
 */

namespace danog\Loop\Test;

use danog\Loop\Loop;
use danog\Loop\Test\Interfaces\BasicInterface;
use danog\Loop\Test\Traits\Basic;
use danog\Loop\Test\Traits\BasicException;
use Revolt\EventLoop;
use RuntimeException;

use function Amp\delay;

class LoopTest extends Fixtures
{
    /**
     * Execute pre-start assertions.
     */
    private function assertPreStart(BasicInterface&Loop $loop): void
    {
        $this->assertEquals(self::LOOP_NAME, "$loop");

        $this->assertFalse($loop->isRunning());
        $this->assertFalse($loop->ran());

        $this->assertFalse($loop->inited());

        $this->assertEquals(0, $loop->startCounter());
        $this->assertEquals(0, $loop->endCounter());
    }
    /**
     * Execute after-start assertions.
     */
    private function assertAfterStart(BasicInterface&Loop $loop, int $prevRun = 0): void
    {
        self::waitTick();
        $this->assertTrue($loop->inited());

        if ($prevRun === 0) {
            $this->assertFalse($loop->ran());
        } else {
            $this->assertTrue($loop->ran());
        }

        $this->assertTrue($loop->isRunning());

        $this->assertEquals($prevRun+1, $loop->startCounter());
        $this->assertEquals($prevRun, $loop->endCounter());

        $this->assertFalse($loop->start());
        $this->assertFalse($loop->isPaused());
    }
    /**
     * Execute final assertions.
     */
    private function assertFinal(BasicInterface&Loop $loop, int $count = 1): void
    {
        $this->assertTrue($loop->ran());
        $this->assertFalse($loop->isRunning());

        $this->assertTrue($loop->inited());

        $this->assertEquals($count, $loop->startCounter());
        $this->assertEquals($count, $loop->endCounter());
    }
    /**
     * Test basic loop.
     */
    public function testLoop(): void
    {
        $loop = new class() extends Loop implements BasicInterface {
            use Basic;
        };
        $this->assertPreStart($loop);
        $this->assertTrue($loop->start());
        $this->assertAfterStart($loop);

        delay(0.110);

        $this->assertFinal($loop);
    }
    /**
     * Test basic loop.
     */
    public function testLoopStopFromOutside(): void
    {
        $loop = new class() extends Loop implements BasicInterface {
            use Basic;
            /**
             * Loop implementation.
             */
            public function loop(): ?float
            {
                $this->inited = true;
                delay(0.1);
                $this->ran = true;
                return 1000.0;
            }
        };
        $this->assertPreStart($loop);
        $this->assertTrue($loop->start());
        $this->assertAfterStart($loop);

        $this->assertTrue($loop->stop());
        delay(0.110);

        $this->assertFinal($loop);
    }
    /**
     * Test basic loop.
     */
    public function testLoopStopFromOutsideRestart(): void
    {
        $loop = new class() extends Loop implements BasicInterface {
            use Basic;
            /**
             * Loop implementation.
             */
            public function loop(): ?float
            {
                $this->inited = true;
                delay(0.1);
                $this->ran = true;
                return 1000.0;
            }
        };
        $this->assertPreStart($loop);
        $this->assertTrue($loop->start());
        $this->assertAfterStart($loop);

        EventLoop::queue(function () use ($loop): void {
            $this->assertTrue($loop->stop());
        });
        self::waitTick();

        $this->assertTrue($loop->start());
        $this->assertAfterStart($loop, 1);

        $this->assertTrue($loop->stop());
        delay(0.110);

        $this->assertFinal($loop, 2);
    }
    /**
     * Test basic exception in loop.
     */
    public function testException(): void
    {
        $loop = new class() extends Loop implements BasicInterface {
            use BasicException;
        };

        $e_thrown = null;
        EventLoop::setErrorHandler(function (\RuntimeException $e) use (&$e_thrown): void {
            $e_thrown = $e;
        });

        $this->assertPreStart($loop);
        $this->assertTrue($loop->start());
        self::waitTick();
        $this->assertFalse($loop->isRunning());

        $this->assertTrue($loop->inited());

        $this->assertEquals(1, $loop->startCounter());
        $this->assertEquals(1, $loop->endCounter());

        $this->assertInstanceOf(RuntimeException::class, $e_thrown);

        EventLoop::setErrorHandler(null);
    }
}
<?php declare(strict_types=1);
/**
 * Loop test.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2020 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/MIT MIT
 */

namespace danog\Loop\Test;

use danog\Loop\Loop;
use danog\Loop\PeriodicLoop;
use danog\Loop\Test\Interfaces\LoggingInterface;
use danog\Loop\Test\Traits\Basic;
use danog\Loop\Test\Traits\Logging;

use function Amp\delay;

class PeriodicTest extends Fixtures
{
    /**
     * Test basic loop.
     *
     * @param bool $stopSig Whether to stop with signal
     *
     *
     *
     * @dataProvider provideTrueFalse
     */
    public function testGeneric(bool $stopSig): void
    {
        $runCount = 0;
        $retValue = false;
        $callable = function (?PeriodicLoop $loop) use (&$runCount, &$retValue, &$l) {
            $l = $loop;
            $runCount++;
            return $retValue;
        };
        $this->fixtureAssertions($callable, $runCount, $retValue, $stopSig, $l);
        $obj = new class() {
            public $retValue = false;
            public $runCount = 0;
            public ?PeriodicLoop $loop = null;
            public function run(PeriodicLoop $periodicLoop)
            {
                $this->loop = $periodicLoop;
                $this->runCount++;
                return $this->retValue;
            }
        };
        $this->fixtureAssertions([$obj, 'run'], $obj->runCount, $obj->retValue, $stopSig, $obj->loop);
        $obj = new class() {
            public $retValue = false;
            public $runCount = 0;
            public ?PeriodicLoop $loop = null;
            public function run(PeriodicLoop $periodicLoop)
            {
                $this->loop = $periodicLoop;
                $this->runCount++;
                return $this->retValue;
            }
        };
        $this->fixtureAssertions(\Closure::fromCallable([$obj, 'run']), $obj->runCount, $obj->retValue, $stopSig, $obj->loop);
    }
    /**
     * Test generator loop.
     *
     * @param bool $stopSig Whether to stop with signal
     *
     *
     *
     * @dataProvider provideTrueFalse
     */
    public function testGenerator(bool $stopSig): void
    {
        $runCount = 0;
        $retValue = false;
        $callable = function (?PeriodicLoop $loop) use (&$runCount, &$retValue, &$l) {
            $l = $loop;
            $runCount++;
            return $retValue;
        };
        $this->fixtureAssertions($callable, $runCount, $retValue, $stopSig, $l);
        $obj = new class() {
            public $retValue = false;
            public $runCount = 0;
            public ?PeriodicLoop $loop = null;
            public function run(?PeriodicLoop $loop)
            {
                $this->loop = $loop;
                $this->runCount++;
                return $this->retValue;
            }
        };
        $this->fixtureAssertions([$obj, 'run'], $obj->runCount, $obj->retValue, $stopSig, $obj->loop);
        $obj = new class() {
            public $retValue = false;
            public $runCount = 0;
            public ?PeriodicLoop $loop = null;
            public function run(?PeriodicLoop $loop)
            {
                $this->loop = $loop;
                $this->runCount++;
                return $this->retValue;
            }
        };
        $this->fixtureAssertions(\Closure::fromCallable([$obj, 'run']), $obj->runCount, $obj->retValue, $stopSig, $obj->loop);
    }
    /**
     * Fixture assertions for started loop.
     */
    private function fixtureStarted(PeriodicLoop&LoggingInterface $loop): void
    {
        $this->assertTrue($loop->isRunning());
        $this->assertEquals(1, $loop->startCounter());
        $this->assertEquals(0, $loop->endCounter());
    }
    /**
     * Run fixture assertions.
     *
     * @param callable $closure  Closure
     * @param integer  $runCount Run count
     * @param bool     $retValue Pause time
     * @param bool     $stopSig  Whether to stop with signal
     */
    private function fixtureAssertions(callable $closure, int &$runCount, bool &$retValue, bool $stopSig, ?PeriodicLoop &$l): void
    {
        $loop = new class($closure, Fixtures::LOOP_NAME, 0.1) extends PeriodicLoop implements LoggingInterface {
            use Logging;
        };
        $this->assertEquals(Fixtures::LOOP_NAME, "$loop");

        $this->assertFalse($loop->isRunning());
        $this->assertEquals(0, $loop->startCounter());
        $this->assertEquals(0, $loop->endCounter());

        $this->assertEquals(0, $runCount);

        $this->assertTrue($loop->start());
        $this->fixtureStarted($loop);
        self::waitTick();

        $this->assertTrue($loop->isPaused());
        $this->assertEquals(1, $runCount);

        $this->assertEquals($loop, $l);

        delay(0.048);
        $this->fixtureStarted($loop);

        $this->assertTrue($loop->isPaused());
        $this->assertEquals(1, $runCount);

        delay(0.060);
        $this->fixtureStarted($loop);

        $this->assertTrue($loop->isPaused());
        $this->assertEquals(2, $runCount);

        $this->assertTrue($loop->resume());
        self::waitTick();

        $this->assertTrue($loop->isPaused());
        $this->assertEquals(3, $runCount);

        if ($stopSig) {
            $this->assertTrue($loop->stop());
        } else {
            $retValue = true;
            $this->assertTrue($loop->resume());
        }
        self::waitTick();
        $this->assertEquals($stopSig ? 3 : 4, $runCount);

        $this->assertTrue($loop->isPaused());
        $this->assertFalse($loop->isRunning());

        $this->assertEquals(1, $loop->startCounter());
        $this->assertEquals(1, $loop->endCounter());
    }

    /**
     * Provide true false.
     *
     */
    public function provideTrueFalse(): array
    {
        return [
            [true],
            [false]
        ];
    }
}
<?php declare(strict_types=1);

/**
 * Resumable loop test interface.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2020 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/MIT MIT
 */

namespace danog\Loop\Test\Interfaces;

/**
 * Resumable loop test interface.
 *
 * @author Daniil Gentili <daniil@daniil.it>
 */
interface IntervalInterface
{
    /**
     * Set sleep interval.
     *
     * @param ?int $interval Interval
     *
     */
    public function setInterval(?int $interval): void;
}
<?php declare(strict_types=1);

/**
 * Basic loop test interface.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2020 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/MIT MIT
 */

namespace danog\Loop\Test\Interfaces;

/**
 * Basic loop test interface.
 *
 * @author Daniil Gentili <daniil@daniil.it>
 */
interface LoggingPauseInterface extends LoggingInterface
{
    /**
     * Get number of times loop was paused.
     */
    public function getPauseCount(): int;
    /**
     * Get last pause.
     */
    public function getLastPause(): float;
}
<?php declare(strict_types=1);

/**
 * Basic loop test interface.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2020 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/MIT MIT
 */

namespace danog\Loop\Test\Interfaces;

/**
 * Basic loop test interface.
 *
 * @author Daniil Gentili <daniil@daniil.it>
 */
interface LoggingInterface
{
    /**
     * Get start counter.
     */
    public function startCounter(): int;
    /**
     * Get end counter.
     */
    public function endCounter(): int;
}
<?php declare(strict_types=1);

/**
 * Basic loop test interface.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2020 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/MIT MIT
 */

namespace danog\Loop\Test\Interfaces;

/**
 * Basic loop test interface.
 *
 * @author Daniil Gentili <daniil@daniil.it>
 */
interface BasicInterface extends LoggingInterface
{
    /**
     * Check whether the loop inited.
     */
    public function inited(): bool;
    /**
     * Check whether the loop ran.
     */
    public function ran(): bool;
}
{
    "name": "danog/loop",
    "description": "Loop abstraction for AMPHP.",
    "keywords": [
        "asynchronous",
        "async",
        "concurrent",
        "multi-threading",
        "multi-processing"
    ],
    "homepage": "https://github.com/danog/loop",
    "license": "MIT",
    "authors": [
        {
            "name": "Daniil Gentili",
            "email": "daniil@daniil.it"
        }
    ],
    "require": {
        "php": ">=8.1",
        "amphp/amp": "^3"
    },
    "require-dev": {
        "phpunit/phpunit": "^9",
        "amphp/phpunit-util": "^3",
        "amphp/php-cs-fixer-config": "^2",
        "psalm/phar": "dev-master"
    },
    "autoload": {
        "psr-4": {
            "danog\\Loop\\": "lib"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "danog\\Loop\\Test\\": "test"
        }
    },
    "scripts": {
        "check": [
            "@cs",
            "@test"
        ],
        "cs": "php-cs-fixer fix -v --diff --dry-run",
        "cs-fix": "php-cs-fixer fix -v --diff",
        "test": "phpdbg -qrr -dzend.assertions=1 -dassert.exception=1 ./vendor/bin/phpunit --coverage-text"
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Exception;

final class InvalidArgumentException extends \InvalidArgumentException implements WebrtcExceptionInterface
{
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Exception;

final class NotImplementedException extends \Exception implements WebrtcExceptionInterface
{
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Exception;


interface WebrtcExceptionInterface extends \Throwable
{
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Exception;

final class RuntimeException extends \RuntimeException implements WebrtcExceptionInterface
{
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Exception;

/**
 * @api
 */
class Exception extends \Exception implements WebrtcExceptionInterface
{
}{
    "name": "danog/php-rtc-exception",
    "type": "library",
    "description": "Lightweight exceptions shared by the WebRTC components. MadelineProto's PHP 8.2+ fork is a Composer-compatible foundation of the modular danog/php-rtc stack.",
    "keywords": [
        "webrtc"
    ],
    "homepage": "https://github.com/danog/php-rtc-exception",
    "license": "BSD-3-Clause",
    "authors": [
        {
            "name": "Amin Yazdanpanah",
            "email": "github@aminyazdanpanah.com"
        },
        {
            "name": "Sana Moniri",
            "email": "s.mo@quasarstream.com"
        },
        {
            "name": "Quasar Stream Community"
        },
        {
            "name": "Daniil Gentili",
            "email": "daniil@daniil.it"
        }
    ],
    "require": {
        "php": "^8.2"
    },
    "autoload": {
        "psr-4": {
            "Webrtc\\Exception\\": "src"
        }
    },
    "minimum-stability": "stable",
    "scripts": {
        "post-install-cmd": [
            "@composer bin all install --ansi"
        ],
        "post-update-cmd": [
            "@composer bin all update --ansi"
        ]
    },
    "config": {
        "allow-plugins": {
            "bamarni/composer-bin-plugin": true
        }
    },
    "require-dev": {
        "bamarni/composer-bin-plugin": "^1.9.1"
    },
    "extra": {
        "bamarni-bin": {
            "bin-links": true,
            "forward-command": true
        }
    }
}
<?php

$config = new Amp\CodeStyle\Config();
$config->getFinder()
    ->in(__DIR__ . '/examples')
    ->in(__DIR__ . '/lib')
    ->in(__DIR__ . '/test');

$cacheDir = getenv('TRAVIS') ? getenv('HOME') . '/.php-cs-fixer' : __DIR__;

$config->setCacheFile($cacheDir . '/.php_cs.cache');

return $config;
<?php
/**
 * Creates JsonDecoder objects.
 *
 * @author Daniil Gentili <https://daniil.it>, Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>,
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 */

namespace danog\LibDNSJson;

use \LibDNS\Messages\MessageFactory;
use \LibDNS\Packets\PacketFactory;
use \LibDNS\Records\QuestionFactory;
use \LibDNS\Records\RDataBuilder;
use \LibDNS\Records\RDataFactory;
use \LibDNS\Records\RecordCollectionFactory;
use \LibDNS\Records\ResourceBuilder;
use \LibDNS\Records\ResourceFactory;
use \LibDNS\Records\TypeDefinitions\FieldDefinitionFactory;
use \LibDNS\Records\TypeDefinitions\TypeDefinitionFactory;
use \LibDNS\Records\TypeDefinitions\TypeDefinitionManager;
use \LibDNS\Records\Types\TypeBuilder;
use \LibDNS\Records\Types\TypeFactory;
use LibDNS\Decoder\DecodingContextFactory;

/**
 * Creates JsonDecoder objects.
 *
 * @author Daniil Gentili <https://daniil.it>, Chris Wright <https://github.com/DaveRandom>
 */
class JsonDecoderFactory
{
    /**
     * Create a new JsonDecoder object.
     *
     * @param ?\LibDNS\Records\TypeDefinitions\TypeDefinitionManager $typeDefinitionManager
     */
    public function create(?TypeDefinitionManager $typeDefinitionManager = null): JsonDecoder
    {
        $typeBuilder = new TypeBuilder(new TypeFactory);

        return new JsonDecoder(
            new PacketFactory,
            new MessageFactory(new RecordCollectionFactory),
            new QuestionFactory,
            new ResourceBuilder(
                new ResourceFactory,
                new RDataBuilder(
                    new RDataFactory,
                    $typeBuilder
                ),
                $typeDefinitionManager ?: new TypeDefinitionManager(
                    new TypeDefinitionFactory,
                    new FieldDefinitionFactory
                )
            ),
            $typeBuilder,
            new DecodingContextFactory
        );
    }
}
<?php declare(strict_types = 1);
/**
 * Encodes Message objects to query strings.
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Encoder
 * @author Daniil Gentili <https://daniil.it>, Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */

namespace danog\LibDNSJson;

use \LibDNS\Messages\Message;
use LibDNS\Messages\MessageTypes;

/**
 * Encodes Message objects to query strings.
 *
 * @category LibDNS
 * @package Encoder
 * @author Daniil Gentili <https://daniil.it>, Chris Wright <https://github.com/DaveRandom>
 */
class QueryEncoder
{
    /**
     * Encode a Message to URL payload.
     *
     * @param \LibDNS\Messages\Message $message  The Message to encode
     */
    public function encode(Message $message): string
    {
        if ($message->getType() !== MessageTypes::QUERY) {
            throw new \InvalidArgumentException('Invalid question: is not a question record');
        }
        $questions = $message->getQuestionRecords();
        if ($questions->count() === 0) {
            throw new \InvalidArgumentException('Invalid question: 0 question records provided');
        }
        $question = $questions->getRecordByIndex(0);
        return \http_build_query(
            [
                'cd' => 0, // Do not disable result validation
                'do' => 0, // Do not send me DNSSEC data
                'type' => $question->getType(), // Record type being requested
                'name' => \implode('.', $question->getName()->getLabels()), // Record name being requested
                'ct' => 'application/dns-json', // Content-type of request
            ]
        );
    }
}
<?php

namespace danog\LibDNSJson;

use LibDNS\Decoder\DecodingContextFactory;
use LibDNS\Messages\Message;
use LibDNS\Messages\MessageFactory;
use LibDNS\Messages\MessageTypes;
use LibDNS\Packets\PacketFactory;
use LibDNS\Records\Question;
use LibDNS\Records\QuestionFactory;
use LibDNS\Records\Resource;
use LibDNS\Records\ResourceBuilder;
use LibDNS\Records\Types\Anything;
use LibDNS\Records\Types\BitMap;
use LibDNS\Records\Types\Char;
use LibDNS\Records\Types\CharacterString;
use LibDNS\Records\Types\DomainName;
use LibDNS\Records\Types\IPv4Address;
use LibDNS\Records\Types\IPv6Address;
use LibDNS\Records\Types\Long;
use LibDNS\Records\Types\Short;
use LibDNS\Records\Types\Type;
use LibDNS\Records\Types\TypeBuilder;
use LibDNS\Records\Types\Types;

/**
 * Decodes JSON DNS strings to Message objects.
 *
 * @author Daniil Gentili <https://daniil.it>, Chris Wright <https://github.com/DaveRandom>
 */
class JsonDecoder
{
    /**
     * @var \LibDNS\Packets\PacketFactory
     */
    private $packetFactory;

    /**
     * @var \LibDNS\Messages\MessageFactory
     */
    private $messageFactory;

    /**
     * @var \LibDNS\Records\QuestionFactory
     */
    private $questionFactory;

    /**
     * @var \LibDNS\Records\ResourceBuilder
     */
    private $resourceBuilder;

    /**
     * @var \LibDNS\Records\Types\TypeBuilder
     */
    private $typeBuilder;

    /**
     * @var \LibDNS\Decoder\DecodingContextFactory
     */
    private $decodingContextFactory;

    /**
     * Constructor.
     *
     */
    public function __construct(
        PacketFactory $packetFactory,
        MessageFactory $messageFactory,
        QuestionFactory $questionFactory,
        ResourceBuilder $resourceBuilder,
        TypeBuilder $typeBuilder,
        DecodingContextFactory $decodingContextFactory
    ) {
        $this->packetFactory = $packetFactory;
        $this->messageFactory = $messageFactory;
        $this->questionFactory = $questionFactory;
        $this->resourceBuilder = $resourceBuilder;
        $this->typeBuilder = $typeBuilder;
        $this->decodingContextFactory = $decodingContextFactory;
    }
    /**
     * Decode a question record.
     *
     *
     * @throws \UnexpectedValueException When the record is invalid
     */
    private function decodeQuestionRecord(array $record): Question
    {
        /** @var \LibDNS\Records\Types\DomainName $domainName */
        $domainName = $this->typeBuilder->build(Types::DOMAIN_NAME);
        $labels = \explode('.', $record['name']);
        if (!empty($last = \array_pop($labels))) {
            $labels[] = $last;
        }
        $domainName->setLabels($labels);

        $question = $this->questionFactory->create($record['type']);
        $question->setName($domainName);
        //$question->setClass($meta['class']);
        return $question;
    }

    /**
     * Decode a resource record.
     *
     *
     * @throws \UnexpectedValueException When the record is invalid
     * @throws \InvalidArgumentException When a type subtype is unknown
     */
    private function decodeResourceRecord(array $record): Resource
    {
        /** @var \LibDNS\Records\Types\DomainName $domainName */
        $domainName = $this->typeBuilder->build(Types::DOMAIN_NAME);
        $labels = \explode('.', $record['name']);
        if (!empty($last = \array_pop($labels))) {
            $labels[] = $last;
        }
        $domainName->setLabels($labels);
        /* @var \LibDNS\Records\Resource $resource */
        $resource = $this->resourceBuilder->build($record['type']);
        $resource->setName($domainName);
        //$resource->setClass($meta['class']);
        $resource->setTTL($record['TTL']);

        $data = $resource->getData();

        $typeDef = $data->getTypeDefinition();
        $record['data'] = \explode(' ', $record['data'], $typeDef->count());

        $fieldDef = $index = null;
        foreach ($data->getTypeDefinition() as $index => $fieldDef) {
            $field = $this->typeBuilder->build($fieldDef->getType());
            $this->decodeType($field, $record['data'][$index]);
            $data->setField($index, $field);
        }

        return $resource;
    }
    /**
     * Decode a Type field.
     *
     *
     * @param \LibDNS\Records\Types\Type $type The object to populate with the result
     * @throws \UnexpectedValueException When the packet data is invalid
     * @throws \InvalidArgumentException When the Type subtype is unknown
     */
    private function decodeType(Type $type, string $data): void
    {
        if ($type instanceof Anything) {
            $this->decodeAnything($type, $data);
        } elseif ($type instanceof BitMap) {
            $this->decodeBitMap($type, $data);
        } elseif ($type instanceof Char) {
            $this->decodeChar($type, $data);
        } elseif ($type instanceof CharacterString) {
            $this->decodeCharacterString($type, $data);
        } elseif ($type instanceof DomainName) {
            $this->decodeDomainName($type, $data);
        } elseif ($type instanceof IPv4Address) {
            $this->decodeIPv4Address($type, $data);
        } elseif ($type instanceof IPv6Address) {
            $this->decodeIPv6Address($type, $data);
        } elseif ($type instanceof Long) {
            $this->decodeLong($type, $data);
        } elseif ($type instanceof Short) {
            $this->decodeShort($type, $data);
        } else {
            throw new \InvalidArgumentException('Unknown Type '.\get_class($type));
        }
    }
    /**
     * Decode an Anything field.
     *
     *
     * @param \LibDNS\Records\Types\Anything $anything The object to populate with the result
     * @throws \UnexpectedValueException When the packet data is invalid
     */
    private function decodeAnything(Anything $anything, string $data): void
    {
        $anything->setValue(\hex2bin($data));
    }

    /**
     * Decode a BitMap field.
     *
     *
     * @param \LibDNS\Records\Types\BitMap $bitMap The object to populate with the result
     * @throws \UnexpectedValueException When the packet data is invalid
     */
    private function decodeBitMap(BitMap $bitMap, string $data): void
    {
        $bitMap->setValue(\hex2bin($data));
    }

    /**
     * Decode a Char field.
     *
     *
     * @param \LibDNS\Records\Types\Char $char The object to populate with the result
     * @throws \UnexpectedValueException When the packet data is invalid
     */
    private function decodeChar(Char $char, string $result): void
    {
        $value = \unpack('C', $result)[1];
        $char->setValue($value);
    }

    /**
     * Decode a CharacterString field.
     *
     *
     * @param \LibDNS\Records\Types\CharacterString $characterString The object to populate with the result
     * @throws \UnexpectedValueException When the packet data is invalid
     */
    private function decodeCharacterString(CharacterString $characterString, string $result): void
    {
        $characterString->setValue($result);
    }

    /**
     * Decode a DomainName field.
     *
     *
     * @param \LibDNS\Records\Types\DomainName $domainName The object to populate with the result
     * @throws \UnexpectedValueException When the packet data is invalid
     */
    private function decodeDomainName(DomainName $domainName, string $result): void
    {
        $labels = \explode('.', $result);
        if (!empty($last = \array_pop($labels))) {
            $labels[] = $last;
        }

        $domainName->setLabels($labels);
    }

    /**
     * Decode an IPv4Address field.
     *
     *
     * @param \LibDNS\Records\Types\IPv4Address $ipv4Address The object to populate with the result
     * @throws \UnexpectedValueException When the packet data is invalid
     */
    private function decodeIPv4Address(IPv4Address $ipv4Address, string $result): void
    {
        $octets = \unpack('C4', \inet_pton($result));
        $ipv4Address->setOctets($octets);
    }

    /**
     * Decode an IPv6Address field.
     *
     *
     * @param \LibDNS\Records\Types\IPv6Address $ipv6Address The object to populate with the result
     * @throws \UnexpectedValueException When the packet data is invalid
     */
    private function decodeIPv6Address(IPv6Address $ipv6Address, string $result): void
    {
        $shorts = \unpack('n8', \inet_pton($result));
        $ipv6Address->setShorts($shorts);
    }

    /**
     * Decode a Long field.
     *
     *
     * @param \LibDNS\Records\Types\Long $long The object to populate with the result
     * @throws \UnexpectedValueException When the packet data is invalid
     */
    private function decodeLong(Long $long, string $result): void
    {
        $long->setValue($result);
    }

    /**
     * Decode a Short field.
     *
     *
     * @param \LibDNS\Records\Types\Short $short The object to populate with the result
     * @throws \UnexpectedValueException When the packet data is invalid
     */
    private function decodeShort(Short $short, string $result): void
    {
        $short->setValue($result);
    }

    /**
     * Decode a Message from JSON-encoded string.
     *
     * @param int $requestId The message ID to set
     * @throws \UnexpectedValueException When the packet data is invalid
     * @throws \InvalidArgumentException When a type subtype is unknown
     */
    public function decode(string $result, int $requestId): Message
    {
        $result = \json_decode($result, true);
        if ($result === false) {
            $error = \json_last_error_msg();
            throw new \InvalidArgumentException("Could not decode JSON DNS payload ($error)");
        }
        if (!isset($result['Status'], $result['TC'], $result['RD'], $result['RA'])) {
            throw new \InvalidArgumentException('Wrong reply from server, missing required fields');
        }

        $message = $this->messageFactory->create();
        $decodingContext = $this->decodingContextFactory->create($this->packetFactory->create());

        //$message->isAuthoritative(true);
        $message->setType(MessageTypes::RESPONSE);
        $message->setID($requestId);
        $message->setResponseCode($result['Status']);
        $message->isTruncated($result['TC']);
        $message->isRecursionDesired($result['RD']);
        $message->isRecursionAvailable($result['RA']);

        $decodingContext->setExpectedQuestionRecords(isset($result['Question']) ? \count($result['Question']) : 0);
        $decodingContext->setExpectedAnswerRecords(isset($result['Answer']) ? \count($result['Answer']) : 0);
        $decodingContext->setExpectedAuthorityRecords(0);
        $decodingContext->setExpectedAdditionalRecords(isset($result['Additional']) ? \count($result['Additional']) : 0);

        $questionRecords = $message->getQuestionRecords();
        $expected = $decodingContext->getExpectedQuestionRecords();
        for ($i = 0; $i < $expected; $i++) {
            $questionRecords->add($this->decodeQuestionRecord($result['Question'][$i]));
        }

        $answerRecords = $message->getAnswerRecords();
        $expected = $decodingContext->getExpectedAnswerRecords();
        for ($i = 0; $i < $expected; $i++) {
            $answerRecords->add($this->decodeResourceRecord($result['Answer'][$i]));
        }

        $authorityRecords = $message->getAuthorityRecords();
        $expected = $decodingContext->getExpectedAuthorityRecords();
        for ($i = 0; $i < $expected; $i++) {
            $authorityRecords->add($this->decodeResourceRecord($result['Authority'][$i]));
        }

        $additionalRecords = $message->getAdditionalRecords();
        $expected = $decodingContext->getExpectedAdditionalRecords();
        for ($i = 0; $i < $expected; $i++) {
            $additionalRecords->add($this->decodeResourceRecord($result['Additional'][$i]));
        }
        return $message;
    }
}
<?php declare(strict_types=1);
/**
 * Creates QueryEncoder objects.
 *
 * PHP version 5.4
 *
 * @category LibDNS
 * @package Encoder
 * @author Daniil Gentili <https://daniil.it>, Chris Wright <https://github.com/DaveRandom>
 * @copyright Copyright (c) Chris Wright <https://github.com/DaveRandom>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @version 2.0.0
 */

namespace danog\LibDNSJson;

/**
 * Creates QueryEncoder objects.
 *
 * @category LibDNS
 * @package Encoder
 * @author Daniil Gentili <https://daniil.it>, Chris Wright <https://github.com/DaveRandom>
 */
class QueryEncoderFactory
{
    /**
     * Create a new Encoder object.
     *
     */
    public function create(): QueryEncoder
    {
        return new QueryEncoder();
    }
}
<?php

namespace danog\LibDNSJson\Test;

use danog\LibDNSJson\JsonDecoder;
use danog\LibDNSJson\JsonDecoderFactory;
use PHPUnit\Framework\TestCase;

class JsonDecoderFactoryTest extends TestCase
{
    public function testJsonDecoderFactoryWorks()
    {
        $this->assertInstanceOf(JsonDecoder::class, (new JsonDecoderFactory)->create());
    }
}
<?php

namespace danog\LibDNSJson\Test;

use danog\LibDNSJson\QueryEncoder;
use danog\LibDNSJson\QueryEncoderFactory;
use PHPUnit\Framework\TestCase;

class QueryEncoderFactoryTest extends TestCase
{
    public function testQueryEncoderFactoryWorks()
    {
        $this->assertInstanceOf(QueryEncoder::class, (new QueryEncoderFactory)->create());
    }
}
<?php

namespace danog\LibDNSJson\Test;

use danog\LibDNSJson\JsonDecoderFactory;
use danog\LibDNSJson\QueryEncoderFactory;
use LibDNS\Messages\MessageTypes;
use PHPUnit\Framework\TestCase;

class QueryEncoderTest extends TestCase
{
    /**
     * Test encoding of valid DNS message payloads.
     *
     * @return void
     *
     * @dataProvider provideValidQueryPayloads
     */
    public function testEncodesValidQueryPayloads(string $message)
    {
        $decoder = (new JsonDecoderFactory)->create();
        $response = $decoder->decode($message, 0);
        $response->setType(MessageTypes::QUERY);

        $encoder = (new QueryEncoderFactory)->create();
        $request = $encoder->encode($response);

        $this->assertIsString($request, "Got a ".\gettype($request)." instead of a string");
        \parse_str($request, $output);
        $this->assertNotEmpty($output);
        $this->assertArrayHasKey('cd', $output);
        $this->assertArrayHasKey('do', $output);
        $this->assertArrayHasKey('ct', $output);
        $this->assertArrayHasKey('type', $output);
        $this->assertArrayHasKey('name', $output);
        $this->assertEquals($output['cd'], 0);
        $this->assertEquals($output['do'], 0);
        $this->assertEquals($output['ct'], 'application/dns-json');
        $this->assertEquals($output['type'], $response->getQuestionRecords()->getRecordByIndex(0)->getType());
        $this->assertEquals($output['name'], \implode('.', $response->getQuestionRecords()->getRecordByIndex(0)->getName()->getLabels()));
    }

    public function provideValidQueryPayloads()
    {
        return [
            [
                '{
                    "Status": 0,
                    "TC": false,
                    "RD": true,
                    "RA": true,
                    "AD": false,
                    "CD": false,
                    "Question":
                    [
                    {
                        "name": "apple.com.",
                        "type": 1
                    }
                    ],
                    "Answer":
                    [
                    {
                        "name": "apple.com.",
                        "type": 1,
                        "TTL": 3599,
                        "data": "17.178.96.59"
                    },
                    {
                        "name": "apple.com.",
                        "type": 1,
                        "TTL": 3599,
                        "data": "17.172.224.47"
                    },
                    {
                        "name": "apple.com.",
                        "type": 1,
                        "TTL": 3599,
                        "data": "17.142.160.59"
                    }
                    ],
                    "Additional": [ ],
                    "edns_client_subnet": "12.34.56.78/0"
                }',
                2
            ],
            [
                '{"Status": 0,"TC": false,"RD": true, "RA": true, "AD": true,"CD": false,"Question":[{"name": "example.com.", "type": 28}],"Answer":[{"name": "example.com.", "type": 28, "TTL": 7092, "data": "2606:2800:220:1:248:1893:25c8:1946"}]}',
                3
            ],
            [
                '{"Status": 0,"TC": false,"RD": true, "RA": true, "AD": false,"CD": false,"Question":[{"name": "daniil.it.", "type": 1}],"Answer":[{"name": "daniil.it.", "type": 1, "TTL": 300, "data": "104.27.146.166"},{"name": "daniil.it.", "type": 1, "TTL": 300, "data": "104.27.147.166"}]}',
                3
            ],
            [
                '{"Status": 0,"TC": false,"RD": true, "RA": true, "AD": false,"CD": false,"Question":[{"name": "amphp.org.", "type": 15}],"Answer":[{"name": "amphp.org.", "type": 15, "TTL": 86400, "data": "0 mail.negativeion.net."}]}',
                3
            ],
        ];
    }

    /**
     * Test query encoding of invalid DNS payloads.
     *
     * @return void
     *
     * @dataProvider provideInvalidQueryPayloads
     */
    public function testEncodesInvalidQueryPayloads($request)
    {
        $encoder = (new QueryEncoderFactory)->create();
        $this->expectException(\InvalidArgumentException::class);
        $encoder->encode($request);
    }

    public function provideInvalidQueryPayloads()
    {
        $decoder = (new JsonDecoderFactory)->create();
        return [
            [
                $decoder->decode(
                    '{
                        "Status": 0,
                        "TC": false,
                        "RD": true,
                        "RA": true,
                        "AD": false,
                        "CD": false,
                        "Question":
                        [
                        {
                            "name": "apple.com.",
                            "type": 1
                        }
                        ],
                        "Answer":
                        [
                        {
                            "name": "apple.com.",
                            "type": 1,
                            "TTL": 3599,
                            "data": "17.178.96.59"
                        },
                        {
                            "name": "apple.com.",
                            "type": 1,
                            "TTL": 3599,
                            "data": "17.172.224.47"
                        },
                        {
                            "name": "apple.com.",
                            "type": 1,
                            "TTL": 3599,
                            "data": "17.142.160.59"
                        }
                        ],
                        "Additional": [ ],
                        "edns_client_subnet": "12.34.56.78/0"
                    }',
                    2
                )
            ],
            [
                $decoder->decode(
                    '{
                        "Status": 0,
                        "TC": false,
                        "RD": true,
                        "RA": true,
                        "AD": false,
                        "CD": false,
                        "Question":
                        [
                        ],
                        "Answer":
                        [
                        {
                            "name": "apple.com.",
                            "type": 1,
                            "TTL": 3599,
                            "data": "17.178.96.59"
                        },
                        {
                            "name": "apple.com.",
                            "type": 1,
                            "TTL": 3599,
                            "data": "17.172.224.47"
                        },
                        {
                            "name": "apple.com.",
                            "type": 1,
                            "TTL": 3599,
                            "data": "17.142.160.59"
                        }
                        ],
                        "Additional": [ ],
                        "edns_client_subnet": "12.34.56.78/0"
                    }',
                    2
                )
            ],
        ];
    }
}
<?php

namespace danog\LibDNSJson\Test;

use danog\LibDNSJson\JsonDecoderFactory;
use LibDNS\Messages\Message;
use LibDNS\Messages\MessageTypes;
use PHPUnit\Framework\TestCase;

class JsonDecoderTest extends TestCase
{
    /**
     * Test decoding of valid JSON DNS payloads.
     *
     * @return void
     *
     * @dataProvider provideValidJsonPayloads
     */
    public function testDecodesValidJsonPayloads(string $message, int $requestId)
    {
        $decoder = (new JsonDecoderFactory)->create();
        $response = $decoder->decode($message, $requestId);

        $this->assertInstanceOf(Message::class, $response);
        $this->assertEquals(MessageTypes::RESPONSE, $response->getType());
    }

    public function provideValidJsonPayloads()
    {
        return [
            [
                '{
                    "Status": 0,
                    "TC": false,
                    "RD": true,
                    "RA": true,
                    "AD": false,
                    "CD": false,
                    "Question":
                    [
                    {
                        "name": "apple.com.",
                        "type": 1
                    }
                    ],
                    "Answer":
                    [
                    {
                        "name": "apple.com.",
                        "type": 1,
                        "TTL": 3599,
                        "data": "17.178.96.59"
                    },
                    {
                        "name": "apple.com.",
                        "type": 1,
                        "TTL": 3599,
                        "data": "17.172.224.47"
                    },
                    {
                        "name": "apple.com.",
                        "type": 1,
                        "TTL": 3599,
                        "data": "17.142.160.59"
                    }
                    ],
                    "Additional": [ ],
                    "edns_client_subnet": "12.34.56.78/0"
                }',
                2,
            ],
            [
                '{"Status": 0,"TC": false,"RD": true, "RA": true, "AD": true,"CD": false,"Question":[{"name": "example.com.", "type": 28}],"Answer":[{"name": "example.com.", "type": 28, "TTL": 7092, "data": "2606:2800:220:1:248:1893:25c8:1946"}]}',
                3,
            ],
            [
                '{"Status": 0,"TC": false,"RD": true, "RA": true, "AD": false,"CD": false,"Question":[{"name": "daniil.it.", "type": 1}],"Answer":[{"name": "daniil.it.", "type": 1, "TTL": 300, "data": "104.27.146.166"},{"name": "daniil.it.", "type": 1, "TTL": 300, "data": "104.27.147.166"}]}',
                3,
            ],
            [
                '{"Status": 0,"TC": false,"RD": true, "RA": true, "AD": false,"CD": false,"Question":[{"name": "amphp.org.", "type": 15}],"Answer":[{"name": "amphp.org.", "type": 15, "TTL": 86400, "data": "0 mail.negativeion.net."}]}',
                3,
            ],
            [
                '{"Status": 0,"TC": false,"RD": true,"RA": true,"AD": false,"CD": false,"Question":[ {"name": "daniil.it.","type": 6}],"Answer":[ {"name": "daniil.it.","type": 6,"TTL": 3493,"data": "cruz.ns.cloudflare.com. dns.cloudflare.com. 2031387933 10000 2400 604800 3600"}]}',
                3,
            ],
        ];
    }

    /**
     * Test decoding of invalid JSON DNS payloads.
     *
     * @param string $message
     * @param int $requestId
     * @return void
     *
     * @dataProvider provideInvalidJsonPayloads
     */
    public function testDecodesInvalidJsonPayloads($message, $requestId)
    {
        $decoder = (new JsonDecoderFactory)->create();
        $this->expectException(\InvalidArgumentException::class);
        $decoder->decode($message, $requestId);
    }

    public function provideInvalidJsonPayloads()
    {
        return [
            [
                '{lmfao
                    "Status": 0,
                    "TC": false,
                    "RD": true,
                    "RA": true,
                    "AD": false,
                    "CD": false,
                    "Question":
                    [
                    {
                        "name": "apple.com.",
                        "type": 1
                    }
                    ],
                    "Answer":
                    [
                    {
                        "name": "apple.com.",
                        "type": 1,
                        "TTL": 3599,
                        "data": "17.178.96.59"
                    },
                    {
                        "name": "apple.com.",
                        "type": 1,
                        "TTL": 3599,
                        "data": "17.172.224.47"
                    },
                    {
                        "name": "apple.com.",
                        "type": 1,
                        "TTL": 3599,
                        "data": "17.142.160.59"
                    }
                    ],
                    "Additional": [ ],
                    "edns_client_subnet": "12.34.56.78/0"
                }',
                2,
            ],
            [
                'xd{"Status": 0,"TC": false,"RD": true, "RA": true, "AD": true,"CD": false,"Question":[{"name": "example.com.", "type": 28}],"Answer":[{"name": "example.com.", "type": 28, "TTL": 7092, "data": "2606:2800:220:1:248:1893:25c8:1946"}]}',
                3,
            ],
            [
                'whaaa{"Status": 0,"TC": false,"RD": true, "RA": true, "AD": false,"CD": false,"Question":[{"name": "daniil.it.", "type": 1}],"Answer":[{"name": "daniil.it.", "type": 1, "TTL": 300, "data": "104.27.146.166"},{"name": "daniil.it.", "type": 1, "TTL": 300, "data": "104.27.147.166"}]}',
                3,
            ],
            [
                'xdxdxxxxx{"Status": 0,"TC": false,"RD": true, "RA": true, "AD": false,"CD": false,"Question":[{"name": "amphp.org.", "type": 15}],"Answer":[{"name": "amphp.org.", "type": 15, "TTL": 86400, "data": "0 mail.negativeion.net."}]}',
                3,
            ],
            [
                '{"TC": false,"RD": true, "RA": true, "AD": false,"CD": false,"Question":[{"name": "amphp.org.", "type": 15}],"Answer":[{"name": "amphp.org.", "type": 15, "TTL": 86400, "data": "0 mail.negativeion.net."}]}',
                3,
            ],
            [
                '{"Status": 0,"RD": true, "RA": true, "AD": false,"CD": false,"Question":[{"name": "amphp.org.", "type": 15}],"Answer":[{"name": "amphp.org.", "type": 15, "TTL": 86400, "data": "0 mail.negativeion.net."}]}',
                3,
            ],
            [
                '{"Status": 0,"TC": false,"RA": true, "AD": false,"CD": false,"Question":[{"name": "amphp.org.", "type": 15}],"Answer":[{"name": "amphp.org.", "type": 15, "TTL": 86400, "data": "0 mail.negativeion.net."}]}',
                3,
            ],
            [
                '{"Status": 0,"TC": false,"RD": true, "AD": false,"CD": false,"Question":[{"name": "amphp.org.", "type": 15}],"Answer":[{"name": "amphp.org.", "type": 15, "TTL": 86400, "data": "0 mail.negativeion.net."}]}',
                3,
            ],
            [
                'xd',
                0
            ],
        ];
    }
}
{
    "name": "danog/libdns-json",
    "homepage": "https://github.com/danog/libdns-json",
    "description": "Encoder/decoder for google's JSON DNS message format based on libdns",
    "keywords": [
        "dns",
        "doh",
        "dns-over-https",
        "https",
        "json",
        "libdns",
        "message"
    ],
    "license": "MIT",
    "authors": [{
            "name": "Daniil Gentili",
            "email": "daniil@daniil.it"
        },
        {
            "name": "Chris Wright",
            "email": "addr@daverandom.com"
        }
    ],
    "require": {
        "php": ">=8.1",
        "daverandom/libdns": "^2.0.1",
        "ext-json": "*"
    },
    "require-dev": {
        "amphp/php-cs-fixer-config": "v2.x-dev",
        "phpunit/phpunit": "^9",
        "psalm/phar": "^5.1"
    },
    "autoload": {
        "psr-4": {
            "danog\\LibDNSJson\\": "lib"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "danog\\LibDNSJson\\Test\\": "test"
        }
    },
    "scripts": {
        "check": [
            "@cs",
            "@test"
        ],
        "cs": "PHP_CS_FIXER_IGNORE_ENV=1 php-cs-fixer fix -v --diff --dry-run",
        "cs-fix": "PHP_CS_FIXER_IGNORE_ENV=1 php-cs-fixer fix -v --diff",
        "test": "@php -dzend.assertions=1 -dassert.exception=1 ./vendor/bin/phpunit --coverage-text"
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SDP;

use Webrtc\Exception\InvalidArgumentException;
use Webrtc\SDP\Enum\H264Level;
use Webrtc\SDP\Enum\H264Profile;

/**
 * H.264 SDP utility class.
 *
 * Provides functionality for parsing H.264 profile-level-id strings from SDP
 * and mapping them to standardized H.264 profile and level enumerations.
 *
 * Implements parsing according to RFC 6184 (RTP Payload Format for H.264 Video)
 * and handles profile/level identification as defined in the ITU-T H.264 standard.
 */
final class H264Sdp
{
    /**
     * Gets the predefined H.264 profile matching patterns.
     *
     * Each pattern consists of:
     * - profile_idc value (integer)
     * - BitPattern object for profile_iop matching
     * - Corresponding H264Profile enum value
     *
     * The patterns follow the H.264 standard's profile/level identification scheme.
     *
     * @return array<array{0: int, 1: BitPattern, 2: H264Profile}> Array of pattern tuples
     */
    public static function getPatterns(): array
    {
        return [
            [0x42, new BitPattern("x1xx0000"), H264Profile::PROFILE_CONSTRAINED_BASELINE],
            [0x4D, new BitPattern("1xxx0000"), H264Profile::PROFILE_CONSTRAINED_BASELINE],
            [0x58, new BitPattern("11xx0000"), H264Profile::PROFILE_CONSTRAINED_BASELINE],
            [0x42, new BitPattern("x0xx0000"), H264Profile::PROFILE_BASELINE],
            [0x58, new BitPattern("10xx0000"), H264Profile::PROFILE_BASELINE],
            [0x4D, new BitPattern("0x0x0000"), H264Profile::PROFILE_MAIN],
            [0x64, new BitPattern("00000000"), H264Profile::PROFILE_HIGH],
            [0x64, new BitPattern("00001100"), H264Profile::PROFILE_CONSTRAINED_HIGH],
            [0xF4, new BitPattern("00000000"), H264Profile::PROFILE_PREDICTIVE_HIGH_444],
        ];
    }

    /**
     * Parses an H.264 profile-level-id string into profile and level.
     *
     * The profile-level-id string format is defined in RFC 6184 Section 8.2.2 as:
     * - 6 hexadecimal digits representing:
     *   - Bytes 1-2: profile_idc (1 byte) + reserved zero bits (1 byte)
     *   - Byte 3: profile_iop (constraint_set flags)
     *   - Byte 4: level_idc
     *
     * @param string|null $profileStr The profile-level-id string (e.g. "42e01f")
     * @return array{0: H264Profile, 1: H264Level} Tuple containing profile and level enums
     * @throws InvalidArgumentException If:
     *         - Input is not a 6-character hex string
     *         - Unrecognized profile_idc/profile_iop combination
     *         - Invalid level_idc value
     */
    public static function parseH264ProfileLevelId(?string $profileStr): array
    {
        if ($profileStr === null || !preg_match('/^[0-9a-fA-F]{6}$/', $profileStr)) {
            throw new InvalidArgumentException("Expected a 6-character hexadecimal string");
        }

        // Split into three 2-character hex pairs and convert to decimal
        $profileIdc = (int) hexdec(substr($profileStr, 0, 2));
        $profileIop = (int) hexdec(substr($profileStr, 2, 2));
        $levelIdc = (int) hexdec(substr($profileStr, 4, 2));

        // Special case for level 1.1 vs. 1.b detection
        $level = match ($levelIdc) {
            H264Level::LEVEL1_1->value => ($profileIop & 0x10) ? H264Level::LEVEL1_B : H264Level::LEVEL1_1,
            default => H264Level::tryFrom($levelIdc) ?? throw new InvalidArgumentException("$levelIdc is not a valid H264Level"),
        };

        // Match against known profile patterns
        foreach (self::getPatterns() as [$idc, $pattern, $profile]) {
            if ($idc === $profileIdc && $pattern->matches($profileIop)) {
                return [$profile, $level];
            }
        }

        throw new InvalidArgumentException("Unrecognized profile_iop = $profileIop, profile_idc = $profileIdc");
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SDP;

use Webrtc\Exception\InvalidArgumentException;
use Webrtc\ICE\RTCIceCandidate;
use Webrtc\ICE\RTCIceParameters;
use Webrtc\RTPParameter\RTCRtpParameters;
use Webrtc\SDP\DtlsParameter\RTCDtlsParameters;
use Webrtc\SDP\Enum\SDPDirections;
use Webrtc\SDP\SctpParameter\RTCSctpCapabilities;

/**
 * Represents a media description for SDP (Session Description Protocol).
 */
final class MediaDescription
{
    // RTP properties
    private string $kind;
    private int $port;
    private ?string $host = null;
    private string $profile;
    private ?SDPDirections $direction = null;
    private ?string $msid = null;

    // RTCP properties
    private ?int $rtcpPort = null;
    private ?string $rtcpHost = null;
    private bool $rtcpMux = false;

    // SSRC properties
    /** @var SsrcDescription[] */
    private array $ssrc = [];

    /** @var GroupDescription[] */
    private array $ssrcGroup = [];

    // Formats
    /** @var list<int|string> */
    private array $fmt;

    // RTP parameters
    private RTCRtpParameters $rtp;

    // SCTP properties
    private ?RTCSctpCapabilities $sctpCapabilities = null;
    /** @var array<int, string> */
    private array $sctpmap = [];
    private ?int $sctpPort = null;

    // DTLS properties
    private ?RTCDtlsParameters $dtls;

    // ICE properties
    private ?RTCIceParameters $ice;
    /** @var RTCIceCandidate[] */
    private array $iceCandidates = [];
    private bool $iceCandidatesComplete = false;

    private ?string $iceOptions = null;

    // DTLS role to SDP setup mapping
    private const DTLS_ROLE_SETUP = [
        'auto' => 'actpass',
        'client' => 'active',
        'server' => 'passive',
    ];

    /**
     * Initializes a new MediaDescription instance.
     *
     * @param string $kind The media kind (e.g., "audio", "video").
     * @param int $port The media port.
     * @param string $profile The media profile (e.g., "RTP/AVP").
     * @param list<int|string> $fmt The media formats.
     */
    public function __construct(
        string $kind, int $port, string $profile, array $fmt)
    {
        $this->kind = $kind;
        $this->port = $port;
        $this->profile = $profile;
        $this->fmt = $fmt;
        $this->rtp = new RTCRtpParameters();
        $this->ice = new RTCIceParameters();
        $this->dtls = new RTCDtlsParameters();
    }

    /**
     * Returns the string representation of the media description in SDP format.
     *
     * @return string The SDP-formatted media description.
     */
    public function __toString(): string
    {
        $lines = [];
        $lines[] = $this->generateMediaLine();
        $lines[] = $this->generateConnectionLine();
        $lines[] = $this->generateDirectionLine();
        $lines = array_merge($lines, $this->generateHeaderExtensions());
        $lines[] = $this->generateMidLine();
        $lines[] = $this->generateMsidLine();
        $lines = array_merge($lines, $this->generateRtcpLines());
        $lines = array_merge($lines, $this->generateSsrcGroupLines());
        $lines = array_merge($lines, $this->generateSsrcLines());
        $lines = array_merge($lines, $this->generateCodecLines());
        $lines = array_merge($lines, $this->generateSctpLines());
        $lines = array_merge($lines, $this->generateIceLines());
        $lines = array_merge($lines, $this->generateDtlsLines());

        return implode("\r\n", array_filter($lines)) . "\r\n";
    }

    /**
     * Generates the "m=" media line.
     *
     * @return string The media line.
     */
    private function generateMediaLine(): string
    {
        return sprintf(
            "m=%s %d %s %s",
            $this->kind,
            $this->port,
            $this->profile,
            implode(' ', array_map('strval', $this->fmt))
        );
    }

    /**
     * Generates the "c=" connection line.
     *
     * @return string|null The connection line, or null if no host is set.
     */
    private function generateConnectionLine(): ?string
    {
        if ($this->host === null) {
            return null;
        }
        return sprintf("c=%s", SDPUtility::ipAddressToSdp($this->host));
    }

    /**
     * Generates the "a=" direction line.
     *
     * @return string|null The direction line, or null if no direction is set.
     */
    private function generateDirectionLine(): ?string
    {
        if ($this->direction === null) {
            return null;
        }
        return sprintf("a=%s", $this->direction->name);
    }

    /**
     * Generates the "a=extmap:" header extension lines.
     *
     * @return array<string> The header extension lines.
     */
    private function generateHeaderExtensions(): array
    {
        $lines = [];
        foreach ($this->rtp->headerExtensions as $header) {
            $lines[] = sprintf("a=extmap:%d %s", $header->id, $header->uri);
        }
        return $lines;
    }

    /**
     * Generates the "a=mid:" line.
     *
     * @return string|null The mid-line, or null if no muxId is set.
     */
    private function generateMidLine(): ?string
    {
        if ($this->rtp->muxId === "") {
            return null;
        }
        return sprintf("a=mid:%s", $this->rtp->muxId);
    }

    /**
     * Generates the "a=msid:" line.
     *
     * @return string|null The msid line, or null if no msid is set.
     */
    private function generateMsidLine(): ?string
    {
        if ($this->msid === null) {
            return null;
        }
        return sprintf("a=msid:%s", $this->msid);
    }

    /**
     * Generates the RTCP-related lines.
     *
     * @return array<string> The RTCP lines.
     */
    private function generateRtcpLines(): array
    {
        $lines = [];
        if ($this->rtcpPort !== null && $this->rtcpHost !== null) {
            $lines[] = sprintf(
                "a=rtcp:%d %s",
                $this->rtcpPort,
                SDPUtility::ipAddressToSdp($this->rtcpHost)
            );
            if ($this->rtcpMux) {
                $lines[] = "a=rtcp-mux";
            }
        }
        return $lines;
    }

    /**
     * Generates the "a=ssrc-group:" lines.
     *
     * @return array<string> The SSRC group lines.
     */
    private function generateSsrcGroupLines(): array
    {
        $lines = [];
        foreach ($this->ssrcGroup as $group) {
            $lines[] = sprintf("a=ssrc-group:%s", $group);
        }
        return $lines;
    }

    /**
     * Generates the "a=ssrc:" lines.
     *
     * @return array<string> The SSRC lines.
     */
    private function generateSsrcLines(): array
    {
        $lines = [];
        foreach ($this->ssrc as $ssrcInfo) {
            $attributes = [
                'cname' => $ssrcInfo->cname,
                'msid' => $ssrcInfo->msid,
                'mslabel' => $ssrcInfo->mslabel,
                'label' => $ssrcInfo->label,
            ];
            foreach ($attributes as $attr => $value) {
                if ($value !== null) {
                    $lines[] = sprintf("a=ssrc:%d %s:%s", $ssrcInfo->ssrc, $attr, $value);
                }
            }
        }
        return $lines;
    }

    /**
     * Generates the codec-related lines.
     *
     * @return array<string> The codec lines.
     */
    private function generateCodecLines(): array
    {
        $lines = [];
        foreach ($this->rtp->codecs as $codec) {
            if ($codec->payloadType === null) {
                throw new InvalidArgumentException("Codec is missing a payload type and cannot be serialized to SDP");
            }
            $lines[] = sprintf("a=rtpmap:%d %s", $codec->payloadType, $codec);

            // RTCP feedback
            foreach ($codec->rtcpFeedback as $feedback) {
                $value = $feedback->type;
                if ($feedback->parameter !== null) {
                    $value .= ' ' . $feedback->parameter;
                }
                $lines[] = sprintf("a=rtcp-fb:%d %s", $codec->payloadType, $value);
            }

            // Parameters
            $params = SDPUtility::parametersToSdp($codec->parameters);
            if ($params !== '') {
                $lines[] = sprintf("a=fmtp:%d %s", $codec->payloadType, $params);
            }
        }
        return $lines;
    }

    /**
     * Generates the SCTP-related lines.
     *
     * @return array<string> The SCTP lines.
     */
    private function generateSctpLines(): array
    {
        $lines = [];
        foreach ($this->sctpmap as $key => $value) {
            $lines[] = sprintf("a=sctpmap:%d %s", $key, $value);
        }
        if ($this->sctpPort !== null) {
            $lines[] = sprintf("a=sctp-port:%d", $this->sctpPort);
        }
        if ($this->sctpCapabilities !== null) {
            $lines[] = sprintf("a=max-message-size:%d", $this->sctpCapabilities->maxMessageSize);
        }
        return $lines;
    }

    /**
     * Generates the ICE-related lines.
     *
     * @return array<string> The ICE lines.
     */
    private function generateIceLines(): array
    {
        $lines = [];
        foreach ($this->iceCandidates as $candidate) {
            $lines[] = sprintf("a=candidate:%s", $candidate->convert2SDP());
        }
        if ($this->iceCandidatesComplete) {
            $lines[] = "a=end-of-candidates";
        }
        if ($this->ice !== null && $this->ice->usernameFragment !== null) {
            $lines[] = sprintf("a=ice-ufrag:%s", $this->ice->usernameFragment);
        }
        if ($this->ice !== null && $this->ice->password !== null) {
            $lines[] = sprintf("a=ice-pwd:%s", $this->ice->password);
        }
        if ($this->iceOptions !== null) {
            $lines[] = sprintf("a=ice-options:%s", $this->iceOptions);
        }
        return $lines;
    }

    /**
     * Generates the DTLS-related lines.
     *
     * @return array<string> The DTLS lines.
     */
    private function generateDtlsLines(): array
    {
        $lines = [];
        if ($this->dtls !== null && $this->dtls->fingerprints !== []) {
            foreach ($this->dtls->fingerprints as $fingerprint) {
                $lines[] = sprintf(
                    "a=fingerprint:%s %s",
                    $fingerprint->algorithm,
                    $fingerprint->value
                );
            }
            $lines[] = sprintf("a=setup:%s", self::DTLS_ROLE_SETUP[$this->dtls->role->value] ?? 'actpass');
        }
        return $lines;
    }

    /**
     * @return RTCDtlsParameters|null
     */
    public function getDtls(): ?RTCDtlsParameters
    {
        return $this->dtls;
    }

    /**
     * Returns the DTLS parameters, throwing if they are absent.
     *
     * @throws InvalidArgumentException If the media description has no DTLS parameters.
     */
    public function requireDtls(): RTCDtlsParameters
    {
        if ($this->dtls === null) {
            throw new InvalidArgumentException("Media description has no DTLS parameters");
        }
        return $this->dtls;
    }

    /**
     * Returns the ICE parameters, throwing if they are absent.
     *
     * @throws InvalidArgumentException If the media description has no ICE parameters.
     */
    public function requireIce(): RTCIceParameters
    {
        if ($this->ice === null) {
            throw new InvalidArgumentException("Media description has no ICE parameters");
        }
        return $this->ice;
    }

    /**
     * @param RTCDtlsParameters|null $dtls
     * @return void
     */
    public function setDtls(?RTCDtlsParameters $dtls): void
    {
        $this->dtls = $dtls;
    }

    /**
     * @return RTCIceParameters|null
     */
    public function getIce(): ?RTCIceParameters
    {
        return $this->ice;
    }

    /**
     * @param RTCIceParameters|null $ice
     * @return void
     */
    public function setIce(?RTCIceParameters $ice): void
    {
        $this->ice = $ice;
    }

    /**
     * @return string|null
     */
    public function getIceOptions(): ?string
    {
        return $this->iceOptions;
    }

    /**
     * @param string|null $iceOptions
     * @return void
     */
    public function setIceOptions(?string $iceOptions): void
    {
        $this->iceOptions = $iceOptions;
    }

    /**
     * @return string|null
     */
    public function getHost(): ?string
    {
        return $this->host;
    }

    /**
     * @param string|null $host
     * @return void
     */
    public function setHost(?string $host): void
    {
        $this->host = $host;
    }

    /**
     * @return RTCIceCandidate[]
     */
    public function getIceCandidates(): array
    {
        return $this->iceCandidates;
    }

    /**
     * @param array<array-key, RTCIceCandidate> $iceCandidates
     * @return void
     */
    public function setIceCandidates(array $iceCandidates): void
    {
        $this->iceCandidates = $iceCandidates;
    }

    /**
     * @param RTCIceCandidate $iceCandidate
     * @return void
     */
    public function addIceCandidates(RTCIceCandidate $iceCandidate): void
    {
        $this->iceCandidates [] = $iceCandidate;
    }

    /**
     * @return bool
     */
    public function isIceCandidatesComplete(): bool
    {
        return $this->iceCandidatesComplete;
    }

    /**
     * @param bool $iceCandidatesComplete
     * @return void
     */
    public function setIceCandidatesComplete(bool $iceCandidatesComplete): void
    {
        $this->iceCandidatesComplete = $iceCandidatesComplete;
    }

    /**
     * @return RTCSctpCapabilities|null
     */
    public function getSctpCapabilities(): ?RTCSctpCapabilities
    {
        return $this->sctpCapabilities;
    }

    /**
     * @param RTCSctpCapabilities|null $sctpCapabilities
     * @return void
     */
    public function setSctpCapabilities(?RTCSctpCapabilities $sctpCapabilities): void
    {
        $this->sctpCapabilities = $sctpCapabilities;
    }

    /**
     * @return RTCRtpParameters
     */
    public function getRtp(): RTCRtpParameters
    {
        return $this->rtp;
    }

    /**
     * @param RTCRtpParameters $rtp
     * @return void
     */
    public function setRtp(RTCRtpParameters $rtp): void
    {
        $this->rtp = $rtp;
    }

    /**
     * @return string|null
     */
    public function getMsid(): ?string
    {
        return $this->msid;
    }

    /**
     * @param string|null $msid
     * @return void
     */
    public function setMsid(?string $msid): void
    {
        $this->msid = $msid;
    }

    /**
     * @return int|null
     */
    public function getRtcpPort(): ?int
    {
        return $this->rtcpPort;
    }

    /**
     * @param int|null $rtcpPort
     * @return void
     */
    public function setRtcpPort(?int $rtcpPort): void
    {
        $this->rtcpPort = $rtcpPort;
    }

    /**
     * @return string|null
     */
    public function getRtcpHost(): ?string
    {
        return $this->rtcpHost;
    }

    /**
     * @param string|null $rtcpHost
     * @return void
     */
    public function setRtcpHost(?string $rtcpHost): void
    {
        $this->rtcpHost = $rtcpHost;
    }

    /**
     * @return bool
     */
    public function isRtcpMux(): bool
    {
        return $this->rtcpMux;
    }

    /**
     * @param bool $rtcpMux
     * @return void
     */
    public function setRtcpMux(bool $rtcpMux): void
    {
        $this->rtcpMux = $rtcpMux;
    }

    /**
     * @return string[]
     */
    public function getSctpmap(): array
    {
        return $this->sctpmap;
    }

    /**
     * @param array<int, string> $sctpmap
     * @return void
     */
    public function setSctpmap(array $sctpmap): void
    {
        $this->sctpmap = $sctpmap;
    }

    /**
     * @param int $formatId
     * @param string $formatDesc
     * @return void
     */
    public function addSctpmap(int $formatId, string $formatDesc): void
    {
        $this->sctpmap[$formatId] = $formatDesc;
    }

    /**
     * @return int|null
     */
    public function getSctpPort(): ?int
    {
        return $this->sctpPort;
    }

    /**
     * @param int|null $sctpPort
     * @return void
     */
    public function setSctpPort(?int $sctpPort): void
    {
        $this->sctpPort = $sctpPort;
    }

    /**
     * @return GroupDescription[]
     */
    public function getSsrcGroup(): array
    {
        return $this->ssrcGroup;
    }

    /**
     * @param array<array-key, GroupDescription> $ssrcGroup
     * @return void
     */
    public function setSsrcGroup(array $ssrcGroup): void
    {
        $this->ssrcGroup = $ssrcGroup;
    }

    /**
     * @param GroupDescription $ssrcGroup
     * @return void
     */
    public function addSsrcGroup(GroupDescription $ssrcGroup): void
    {
        $this->ssrcGroup []= $ssrcGroup;
    }

    /**
     * @return SsrcDescription[]
     */
    public function getSsrc(): array
    {
        return $this->ssrc;
    }

    /**
     * @param array<array-key, SsrcDescription> $ssrc
     * @return void
     */
    public function setSsrc(array $ssrc): void
    {
        $this->ssrc = $ssrc;
    }

    /**
     * @param SsrcDescription $ssrc
     * @return void
     */
    public function addSsrc(SsrcDescription $ssrc): void
    {
        $this->ssrc []= $ssrc;
    }

    /**
     * @return string
     */
    public function getKind(): string
    {
        return $this->kind;
    }

    /**
     * @param string $kind
     * @return void
     */
    public function setKind(string $kind): void
    {
        $this->kind = $kind;
    }

    /**
     * @return int
     */
    public function getPort(): int
    {
        return $this->port;
    }

    /**
     * @param int $port
     * @return void
     */
    public function setPort(int $port): void
    {
        $this->port = $port;
    }

    /**
     * @return string
     */
    public function getProfile(): string
    {
        return $this->profile;
    }

    /**
     * @param string $profile
     * @return void
     */
    public function setProfile(string $profile): void
    {
        $this->profile = $profile;
    }

    /**
     * @return SDPDirections|null
     */
    public function getDirection(): ?SDPDirections
    {
        return $this->direction;
    }

    /**
     * @param SDPDirections|null $direction
     * @return void
     */
    public function setDirection(?SDPDirections $direction): void
    {
        $this->direction = $direction;
    }

    /**
     * @return list<int|string>
     */
    public function getFmt(): array
    {
        return $this->fmt;
    }

    /**
     * @param list<int|string> $fmt
     * @return void
     */
    public function setFmt(array $fmt): void
    {
        $this->fmt = $fmt;
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SDP\SctpParameter;

/**
 * Provides information about the capabilities of the RTCSctpTransport.
 */
final class RTCSctpCapabilities
{
    /**
     * The maximum size of data that the implementation can send or
     * 0 if the implementation can handle messages of any size.
     */
    public function __construct(public int $maxMessageSize = 0)
    {
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SDP;

use Webrtc\Exception\InvalidArgumentException;
use Webrtc\ICE\RTCIceCandidate;
use Webrtc\RTPParameter\RTCRtcpFeedback;
use Webrtc\RTPParameter\RTCRtpCodecParameters;
use Webrtc\RTPParameter\RTCRtpHeaderExtensionParameters;
use Webrtc\SDP\DtlsParameter\RTCDtlsFingerprint;
use Webrtc\SDP\Enum\DtlsRole;
use Webrtc\SDP\Enum\SDPDirections;
use Webrtc\SDP\SctpParameter\RTCSctpCapabilities;

/**
 * Represents a session description for SDP (Session Description Protocol).
 */
final class SessionDescription
{
    /** @var array<string, DtlsRole> Mapping of DTLS setup roles. */
    private const DTLS_SETUP_ROLE = [
        "actpass" => DtlsRole::Auto,
        "active" => DtlsRole::Client,
        "passive" => DtlsRole::Server,
    ];

    /** @var string[] Supported SSRC attributes. */
    private const SSRC_INFO_ATTRS = ["cname", "msid", "mslabel", "label"];
    private const FORBIDDEN_PAYLOAD_TYPES = [72, 73, 74, 75, 76];
    /** @var int The SDP version. */
    private int $version = 0;
    /** @var string|null The origin field. */
    private ?string $origin = null;
    /** @var string The session name. */
    private string $name = "-";
    /** @var string The session time. */
    private string $time = "0 0";
    /** @var string|null The connection host. */
    private ?string $host = null;
    /** @var GroupDescription[] The group descriptions. */
    private array $group = [];
    /** @var GroupDescription[] The MSID semantic descriptions. */
    private array $msidSemantic = [];
    /** @var MediaDescription[] The media descriptions. */
    private array $media = [];
    /** @var RTCDtlsFingerprint[] */
    private ?array $dtlsFingerprints = null;
    private bool $iceLite = false;
    private ?string $iceOptions = null;
    private ?DtlsRole $dtlsRole = null;
    private ?string $icePassword = null;
    private ?string $iceUsernameFragment = null;
    private ?string $type = null;

    /**
     * Initializes a new SessionDescription instance.
     */
    public function __construct()
    {
    }

    /**
     * Decodes an SDP string and creates a SessionDescription instance.
     *
     * @param string $sdp The SDP string to decode.
     * @return SessionDescription The decoded session description.
     */
    public static function decode(string $sdp): SessionDescription
    {
        $session = new SessionDescription();
        list($sessionLines, $mediaGroups) = self::splitSdpIntoLines($sdp);

        $session->decodeSessionLines($sessionLines);
        $session->decodeMediaGroups($mediaGroups);

        return $session;
    }

    /**
     * Splits an SDP string into session lines and media groups.
     *
     * @param string $sdp The SDP string.
     * @return array{string[], string[][]} The session lines and media groups.
     */
    private static function splitSdpIntoLines(string $sdp): array
    {
        $lines = explode("\r\n", trim($sdp));
        $sessionLines = [];
        $mediaGroups = [];
        $currentMediaGroup = [];

        foreach ($lines as $line) {
            if (str_starts_with($line, "m=")) {
                if (!empty($currentMediaGroup)) {
                    $mediaGroups[] = $currentMediaGroup;
                }
                $currentMediaGroup = [$line];
            } elseif (!empty($currentMediaGroup)) {
                $currentMediaGroup[] = $line;
            } else {
                $sessionLines[] = $line;
            }
        }

        if (!empty($currentMediaGroup)) {
            $mediaGroups[] = $currentMediaGroup;
        }

        return [$sessionLines, $mediaGroups];
    }

    /**
     * Decodes session-level lines.
     *
     * @param string[] $sessionLines The session lines.
     */
    private function decodeSessionLines(array $sessionLines): void
    {
        foreach ($sessionLines as $line) {
            if (str_starts_with($line, "v=")) {
                $this->version = (int)substr($line, 2);
            } elseif (str_starts_with($line, "o=")) {
                $this->origin = substr($line, 2);
            } elseif (str_starts_with($line, "s=")) {
                $this->name = substr($line, 2);
            } elseif (str_starts_with($line, "c=")) {
                $this->host = SDPUtility::ipAddressFromSdp(substr($line, 2));
            } elseif (str_starts_with($line, "t=")) {
                $this->time = substr($line, 2);
            } elseif (str_starts_with($line, "a=")) {
                list($attr, $value) = self::parseAttributeLine($line);
                switch ($attr) {
                    case "fingerprint":
                        list($algorithm, $fingerprint) = explode(" ", $value);
                        $this->dtlsFingerprints[] = new RTCDtlsFingerprint($algorithm, $fingerprint);
                        break;
                    case "ice-lite":
                        $this->iceLite = true;
                        break;
                    case "ice-options":
                        $this->iceOptions = $value;
                        break;
                    case "ice-pwd":
                        $this->icePassword = $value;
                        break;
                    case "ice-ufrag":
                        $this->iceUsernameFragment = $value;
                        break;
                    case "group":
                        $this->group[] = SDPUtility::parseGroup($value);
                        break;
                    case "msid-semantic":
                        $this->msidSemantic[] = SDPUtility::parseGroup($value);
                        break;
                    case "setup":
                        $this->dtlsRole = self::DTLS_SETUP_ROLE[$value] ?? null;
                        break;
                }
            }
        }
    }

    /**
     * Decodes media-level groups.
     *
     * @param string[][] $mediaGroups The media groups.
     */
    private function decodeMediaGroups(array $mediaGroups): void
    {
        foreach ($mediaGroups as $mediaLines) {
            $media = $this->decodeMediaDescription($mediaLines[0]);
            $this->media[] = $media;
            $this->assignMediaAttribute($media);

            foreach (array_slice($mediaLines, 1) as $line) {
                $this->decodeMediaAttribute($media, $line);
            }

            foreach (array_slice($mediaLines, 1) as $line) {
                $this->decodeMediaFmtpAndRtcpFpAttribute($media, $line);
            }
        }
    }

    /**
     * Apply session-level attributes to media descriptions
     *
     * @param MediaDescription $media
     * @return void
     */
    private function assignMediaAttribute(MediaDescription $media): void
    {
        $dtls = $media->getDtls();
        $ice = $media->getIce();
        if ($dtls === null || $ice === null) {
            throw new InvalidArgumentException("Media description is missing DTLS or ICE parameters");
        }
        if ($this->dtlsFingerprints !== null && $this->dtlsFingerprints !== []) {
            $dtls->fingerprints = $this->dtlsFingerprints;
        }
        if ($this->dtlsRole !== null) {
            $dtls->role = $this->dtlsRole;
        }
        if ($this->iceUsernameFragment !== null && $this->iceUsernameFragment !== '') {
            $ice->usernameFragment = $this->iceUsernameFragment;
        }
        if ($this->icePassword !== null && $this->icePassword !== '') {
            $ice->password = $this->icePassword;
        }
        if ($this->iceLite) {
            $ice->iceLite = true;
        }
        if ($this->iceOptions !== null && $this->iceOptions !== '') {
            $media->setIceOptions($this->iceOptions);
        }
    }

    /**
     * Decodes a media description line.
     *
     * @param string $line The media description line.
     * @return MediaDescription The decoded media description.
     */
    private function decodeMediaDescription(string $line): MediaDescription
    {
        if (!preg_match("/^m=([^ ]+) ([0-9]+) ([A-Z\/]+) (.+)$/", $line, $matches)) {
            throw new InvalidArgumentException("Invalid media line format");
        }

        $kind = $matches[1];
        $port = (int)$matches[2]; // regex guarantees [0-9]+
        $profile = $matches[3];
        $fmt = explode(" ", $matches[4]);

        // Validate payload types
        $fmtInt = null;
        if (in_array($kind, ["audio", "video"])) {
            $fmtInt = array_map(fn(string $pt) => SDPUtility::parseInt($pt, "payload type"), $fmt);
            foreach ($fmtInt as $pt) {
                if ($pt >= 256 || in_array($pt, self::FORBIDDEN_PAYLOAD_TYPES)) {
                    throw new InvalidArgumentException("Invalid payload type: $pt");
                }
            }
        }

        return new MediaDescription($kind, $port, $profile, $fmtInt ?? $fmt);
    }

    /**
     * Decodes a media attribute line.
     *
     * @param MediaDescription $media The media description.
     * @param string $line The attribute line.
     */
    private function decodeMediaAttribute(MediaDescription $media, string $line): void
    {
        if (str_starts_with($line, "c=")) {
            $media->setHost(SDPUtility::ipAddressFromSdp(substr($line, 2)));
        } elseif (str_starts_with($line, "a=")) {
            list($attr, $value) = self::parseAttributeLine($line);
            switch ($attr) {
                case "candidate":
                    $media->addIceCandidates(RTCIceCandidate::parseSDP($value));
                    break;
                case "end-of-candidates":
                    $media->setIceCandidatesComplete(true);
                    break;
                case "extmap":
                    $this->decodeExtmapAttribute($media, $value);
                    break;
                case "fingerprint":
                    [$algorithm, $fingerprint] = self::splitPair($value, " ");
                    $media->requireDtls()->fingerprints[] = new RTCDtlsFingerprint($algorithm, $fingerprint);
                    break;
                case "ice-options":
                    $media->setIceOptions($value);
                    break;
                case "ice-pwd":
                    $media->requireIce()->password = $value;
                    break;
                case "ice-ufrag":
                    $media->requireIce()->usernameFragment = $value;
                    break;
                case "max-message-size":
                    $media->setSctpCapabilities(new RTCSctpCapabilities(SDPUtility::parseInt($value, "max-message-size")));
                    break;
                case "mid":
//                    var_dump($value);
                    $media->getRtp()->muxId = $value;
                    break;
                case "msid":
                    $media->setMsid($value);
                    break;
                case "rtcp":
                    $this->decodeRtcpAttribute($media, $value);
                    break;
                case "rtcp-mux":
                    $media->setRtcpMux(true);
                    break;
                case "setup":
                    $role = self::DTLS_SETUP_ROLE[$value] ?? null;
                    if ($role === null) {
                        throw new InvalidArgumentException("Invalid DTLS setup role: $value");
                    }
                    $media->requireDtls()->role = $role;
                    break;
                case "rtpmap":
                    $this->decodeRtpmapAttribute($media, $value);
                    break;
                case "sctpmap":
                    [$formatId, $formatDesc] = self::splitPair($value, " ");
                    $media->addSctpmap(SDPUtility::parseInt($formatId, "sctpmap format"), $formatDesc);
                    break;
                case "sctp-port":
                    $media->setSctpPort(SDPUtility::parseInt($value, "sctp-port"));
                    break;
                case "ssrc-group":
                    $media->addSsrcGroup(SDPUtility::parseGroup($value, "int"));
                    break;
                case "ssrc":
                    $this->decodeSsrcAttribute($media, $value);
                    break;
                default:
                    $direction = SDPDirections::fromString($attr);
                    if ($direction !== SDPDirections::unknown) {
                        $media->setDirection($direction);
                    }
            }
        }
    }

    /**
     * Decodes a media attribute of fmtp and rtcp-fb line that requires codecs to have been decoded first.
     *
     * @param MediaDescription $media The media description.
     * @param string $line The attribute line.
     */
    private function decodeMediaFmtpAndRtcpFpAttribute(MediaDescription $media, string $line): void
    {
        if (str_starts_with($line, "a=")) {
            list($attr, $value) = self::parseAttributeLine($line);
            switch ($attr) {
                case "fmtp":
                    $this->decodeFmtpAttribute($media, $value);
                    break;
                case "rtcp-fb":
                    $this->decodeRtcpFbAttribute($media, $value);
                    break;
            }
        }
    }

    /**
     * Decodes an "extmap" attribute.
     *
     * @param MediaDescription $media The media description.
     * @param string $value The attribute value.
     */
    private function decodeExtmapAttribute(MediaDescription $media, string $value): void
    {
        list($extId, $extUri) = explode(" ", $value);
        if (str_contains($extId, "/")) {
            list($extId,) = explode("/", $extId);
        }
        $extension = new RTCRtpHeaderExtensionParameters(SDPUtility::parseInt($extId, "extmap id"), $extUri);
        $media->getRtp()->headerExtensions[] = $extension;
    }

    /**
     * Decodes an "rtcp" attribute.
     *
     * @param MediaDescription $media The media description.
     * @param string $value The attribute value.
     */
    private function decodeRtcpAttribute(MediaDescription $media, string $value): void
    {
        [$port, $rest] = self::splitPair($value, " ");
        $media->setRtcpPort(SDPUtility::parseInt($port, "rtcp port"));
        $media->setRtcpHost(SDPUtility::ipAddressFromSdp($rest));
    }

    /**
     * Decodes an "rtpmap" attribute.
     *
     * @param MediaDescription $media The media description.
     * @param string $value The attribute value.
     */
    private function decodeRtpmapAttribute(MediaDescription $media, string $value): void
    {
        [$formatId, $formatDesc] = self::splitPair($value, " ");

        $bits = explode("/", $formatDesc);
        if (!isset($bits[1])) {
            throw new InvalidArgumentException("Malformed rtpmap value, expected \"codec/clockrate\": $value");
        }
        $channels = $media->getKind() === "video" ? null : (($media->getKind() === "audio" && count($bits) > 2) ? SDPUtility::parseInt($bits[2], "rtpmap channels") : 1);
        $codec = new RTCRtpCodecParameters(
            $media->getKind() . "/" . $bits[0],
            SDPUtility::parseInt($bits[1], "rtpmap clock rate"),
            $channels,
            (int)$formatId
        );
        $media->getRtp()->codecs[] = $codec;
    }

    /**
     * Decodes a "ssrc" attribute.
     *
     * @param MediaDescription $media The media description.
     * @param string $value The attribute value.
     */
    private function decodeSsrcAttribute(MediaDescription $media, string $value): void
    {
        [$ssrcStr, $ssrcDesc] = self::splitPair($value, " ");
        $ssrc = SDPUtility::parseInt($ssrcStr, "ssrc");
        $descParts = explode(":", $ssrcDesc, 2);
        $ssrcAttr = $descParts[0];

        $ssrcInfo = null;
        foreach ($media->getSsrc() as $info) {
            if ($info->ssrc === $ssrc) {
                $ssrcInfo = $info;
                break;
            }
        }
        if ($ssrcInfo === null) {
            $ssrcInfo = new SsrcDescription($ssrc);
            $media->addSsrc($ssrcInfo);
        }
        if (isset($descParts[1]) && in_array($ssrcAttr, self::SSRC_INFO_ATTRS, true)) {
            $ssrcInfo->$ssrcAttr = $descParts[1];
        }
    }

    /**
     * Decodes an "fmtp" attribute.
     *
     * @param MediaDescription $media The media description.
     * @param string $value The attribute value.
     */
    private function decodeFmtpAttribute(MediaDescription $media, string $value): void
    {
        [$formatId, $formatDesc] = self::splitPair($value, " ");
        $codec = self::findCodec($media->getRtp()->codecs, SDPUtility::parseInt($formatId, "fmtp format"));
        $codec->parameters = SDPUtility::parametersFromSdp($formatDesc);
    }

    /**
     * Decodes an "rtcp-fb" attribute.
     *
     * @param MediaDescription $media The media description.
     * @param string $value The attribute value.
     */
    private function decodeRtcpFbAttribute(MediaDescription $media, string $value): void
    {
        $bits = explode(" ", $value, 3);
        if (!isset($bits[1])) {
            throw new InvalidArgumentException("Malformed rtcp-fb value, expected \"payloadType type\": $value");
        }
        foreach ($media->getRtp()->codecs as $codec) {
            if ($bits[0] === "*" || $bits[0] === (string)$codec->payloadType) {
                $codec->rtcpFeedback[] = new RTCRtcpFeedback(
                    $bits[1],
                    $bits[2] ?? null
                );
            }
        }
    }

    /**
     * Parses an attribute line.
     *
     * @param string $line The attribute line.
     * @return array{string, string} The attribute name and value.
     */
    /**
     * @return array{string, string}
     */
    private static function parseAttributeLine(string $line): array
    {
        $parts = explode(":", substr($line, 2), 2);
        return [$parts[0], $parts[1] ?? ""];
    }

    /**
     * Splits a value into exactly two parts on the first occurrence of a separator.
     *
     * @param non-empty-string $separator
     * @return array{string, string}
     * @throws InvalidArgumentException If the value does not contain the separator.
     */
    private static function splitPair(string $value, string $separator): array
    {
        $parts = explode($separator, $value, 2);
        if (!isset($parts[1])) {
            throw new InvalidArgumentException("Malformed SDP value, expected \"$separator\" in: $value");
        }
        return [$parts[0], $parts[1]];
    }

    /**
     * Finds a codec by payload type.
     *
     * @param RTCRtpCodecParameters[] $codecs The codec array.
     * @param int $payloadType The payload type.
     * @return RTCRtpCodecParameters The codec.
     * @throws InvalidArgumentException If the codec is not found.
     */
    private static function findCodec(array $codecs, int $payloadType): RTCRtpCodecParameters
    {
        foreach ($codecs as $codec) {
            if ($codec->payloadType === $payloadType) {
                return $codec;
            }
        }
        throw new InvalidArgumentException("Codec not found for payload type: $payloadType");
    }

    /**
     * Returns the string representation of the session description in SDP format.
     *
     * @return string The SDP-formatted session description.
     */
    public function __toString(): string
    {
        $lines = [
            "v=$this->version",
            "o=$this->origin",
            "s=$this->name",
        ];

        if ($this->host !== null) {
            $lines[] = "c=" . SDPUtility::ipAddressToSdp($this->host);
        }

        $lines[] = "t=$this->time";

        $sessionIceLite = false;
        foreach ($this->media as $m) {
            $ice = $m->getIce();
            if ($ice !== null && $ice->iceLite) {
                $sessionIceLite = true;
                break;
            }
        }
        if ($sessionIceLite) {
            $lines[] = "a=ice-lite";
        }

        foreach ($this->group as $group) {
            $lines[] = "a=group:$group";
        }

        foreach ($this->msidSemantic as $group) {
            $lines[] = "a=msid-semantic:$group";
        }

        return implode("\r\n", $lines) . "\r\n" . implode("", array_map('strval', $this->media));
    }

    /**
     * Returns the WebRTC track ID for a media description.
     *
     * @param MediaDescription $media The media description.
     * @return string|null The track ID, or null if not found.
     */
    public function webrtcTrackId(MediaDescription $media): ?string
    {
        if (!in_array($media, $this->media, true)) {
            throw new InvalidArgumentException("Media description not found in session");
        }

        if ($media->getMsid() !== null && str_contains($media->getMsid(), " ")) {
            $bits = explode(" ", $media->getMsid());
            if (array_any($this->msidSemantic, fn($group) => $group->semantic === "WMS" && (in_array($bits[0], $group->items) || in_array("*", $group->items)))) {
                return $bits[1];
            }
        }

        return null;
    }

    /**
     * @return int
     */
    public function getVersion(): int
    {
        return $this->version;
    }

    /**
     * @return string|null
     */
    public function getHost(): ?string
    {
        return $this->host;
    }

    /**
     * @return string|null
     */
    public function getOrigin(): ?string
    {
        return $this->origin;
    }

    /**
     * @return string
     */
    public function getName(): string
    {
        return $this->name;
    }

    /**
     * @return string
     */
    public function getTime(): string
    {
        return $this->time;
    }

    /**
     * @return GroupDescription[]
     */
    public function getMsidSemantic(): array
    {
        return $this->msidSemantic;
    }

    /**
     * @param GroupDescription $description
     * @return void
     */
    public function appendMsidSemantic(GroupDescription $description): void
    {
        $this->msidSemantic [] = $description;
    }

    /**
     * @return GroupDescription[]
     */
    public function getGroup(): array
    {
        return $this->group;
    }

    /**
     * @return MediaDescription[]
     */
    public function getMedia(): array
    {
        return $this->media;
    }

    /**
     * @param array<array-key, MediaDescription> $media
     * @return void
     */
    public function setMedia(array $media): void
    {
        $this->media = $media;
    }

    /**
     * @param MediaDescription $description
     * @return void
     */
    public function addMedia(MediaDescription $description): void
    {
        $this->media[] = $description;
    }

    /**
     * @return string|null
     */
    public function getType(): ?string
    {
        return $this->type;
    }

    /**
     * @param string $type
     * @return bool
     */
    public function isType(string $type): bool
    {
        return $this->type === $type;
    }

    /**
     * @param string|null $type
     * @return void
     */
    public function setType(?string $type): void
    {
        $this->type = $type;
    }

    /**
     * @param string|null $origin
     * @return void
     */
    public function setOrigin(?string $origin): void
    {
        $this->origin = $origin;
    }

    /**
     * @param array<array-key, GroupDescription> $group
     * @return void
     */
    public function setGroup(array $group): void
    {
        $this->group = $group;
    }

    /**
     * @param GroupDescription $group
     * @return void
     */
    public function addGroup(GroupDescription $group): void
    {
        $this->group[] = $group;
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SDP\DtlsParameter;

use Webrtc\Mixin\DataClass;
use Webrtc\SDP\Enum\DtlsRole;

/**
 * Represents DTLS (Datagram Transport Layer Security) parameters.
 */
#[DataClass]
final class RTCDtlsParameters
{
    /**
     * Initializes a new RTCDtlsParameters instance.
     *
     * @param RTCDtlsFingerprint[] $fingerprints List of DTLS fingerprints, one for each certificate.
     * @param DtlsRole $role The DTLS role, with a default of "auto".
     */
    public function __construct(
        public array    $fingerprints = [],
        public DtlsRole $role = DtlsRole::Auto
    )
    {
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SDP\DtlsParameter;

use Webrtc\Mixin\DataClass;

/**
 * Represents a DTLS fingerprint.
 */
#[DataClass]
final class RTCDtlsFingerprint
{
    /**
     * Initializes a new RTCDtlsFingerprint instance.
     *
     * @param string $algorithm The hash algorithm (e.g., "sha-256").
     * @param string $value The fingerprint value.
     */
    public function __construct(
        public string $algorithm,
        public string $value
    )
    {
    }

    public function isAlgorithm(string $algorithm): bool
    {
        return $this->algorithm === $algorithm;
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SDP;

use Webrtc\Exception\InvalidArgumentException;

final class SDPUtility
{
    private const IP_REGEX = "/^IN (IP4|IP6) ([^ ]+)$/";
    public const FMTP_INT_PARAMETERS = ["cname", "msid", "mslabel", "label"];

    /**
     * Parses a non-negative integer field taken from untrusted remote SDP text.
     *
     * Bare (int)/intval casts silently turn malformed tokens ("abc", "12xyz") into 0, which then
     * slips past range checks and surfaces much later as an opaque failure (port 0, clock rate 0,
     * two distinct SSRCs collapsing to 0, ...). This rejects non-numeric input up front instead.
     *
     * @param string $value The raw token from the SDP line.
     * @param string $field Human-readable field name for the error message.
     * @return int
     * @throws InvalidArgumentException When $value is not a base-10 non-negative integer.
     */
    public static function parseInt(string $value, string $field): int
    {
        if (!ctype_digit($value)) {
            throw new InvalidArgumentException("Invalid $field in SDP, expected a non-negative integer: \"$value\"");
        }
        return (int)$value;
    }

    /**
     * Extracts the IP address from an SDP string.
     *
     * @param string $sdp
     * @return string
     * @throws InvalidArgumentException
     */
    public static function ipAddressFromSDP(string $sdp): string
    {
        if (!preg_match(self::IP_REGEX, $sdp, $matches)) {
            throw new InvalidArgumentException("Invalid SDP format: IP address not found.");
        }
        return $matches[2];
    }

    /**
     * Converts an IP address to an SDP string.
     *
     * @param string $addr
     * @return string
     */
    public static function ipAddressToSDP(string $addr): string
    {
        $version = filter_var($addr, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) ? 6 : 4;
        return "IN IP$version $addr";
    }

    /**
     * Extracts parameters from an SDP string.
     *
     * @param string $sdp
     * @return array<string, int|null|string>
     */
    public static function parametersFromSDP(string $sdp): array
    {
        $parameters = [];
        $params = explode(';', $sdp);

        foreach ($params as $param) {
            $parts = explode('=', $param, 2);
            if (isset($parts[1])) {
                $parameters[$parts[0]] = in_array($parts[0], self::FMTP_INT_PARAMETERS) ? (int)$parts[1] : $parts[1];
            } else {
                $parameters[$param] = null;
            }
        }

        return $parameters;
    }

    /**
     * Converts parameters to an SDP string.
     *
     * @param array<string, int|null|string> $parameters
     * @return string
     */
    public static function parametersToSDP(array $parameters): string
    {
        $params = [];
        foreach ($parameters as $k => $v) {
            $params[] = $v !== null ? "$k=$v" : $k;
        }
        return implode(';', $params);
    }

    /**
     * Parses a group description from an SDP string.
     *
     * @param string $value
     * @param string $type
     * @return GroupDescription
     */
    public static function parseGroup(string $value, string $type = 'string'): GroupDescription
    {
        $bits = explode(' ', trim($value));
        $items = array_map(
            fn(string $item) => $type === 'int' ? self::parseInt($item, 'group item') : $item,
            array_slice($bits, 1)
        );
        return new GroupDescription(semantic: $bits[0], items: $items);
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SDP;

use Webrtc\Mixin\DataClass;

/**
 * Represents a group description with a semantic label and a list of items.
 */
#[DataClass]
final class GroupDescription
{
    /**
     * Initializes a new GroupDescription instance.
     *
     * @param string $semantic The semantic label for the group.
     * @param array<int|string> $items The list of items in the group.
     */
    public function __construct(
        public string $semantic,
        public array  $items
    )
    {
    }

    /**
     * Returns the string representation of the group description.
     *
     * @return string The formatted string containing the semantic label and items.
     */
    public function __toString(): string
    {
        $itemsString = implode(' ', array_map('strval', $this->items));
        return "$this->semantic $itemsString";
    }
}

<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SDP;

use Webrtc\Exception\InvalidArgumentException;

/**
 * Represents a session description for WebRTC.
 */
final class RTCSessionDescription
{
    /** @var string The SDP (Session Description Protocol) string. */
    private string $sdp;

    /** @var string The type of session description. */
    private string $type;

    /** @var string[] Valid types for the session description. */
    private const VALID_TYPES = ["offer", "pranswer", "answer", "rollback"];

    /**
     * Initializes a new RTCSessionDescription instance.
     *
     * @param string $sdp The SDP string.
     * @param string $type The type of session description.
     * @throws InvalidArgumentException If the type is invalid.
     */
    public function __construct(string $sdp, string $type)
    {
        $this->validateType($type);
        $this->sdp = $sdp;
        $this->type = $type;
    }

    /**
     * Validates the session description type.
     *
     * @param string $type The type to validate.
     * @throws InvalidArgumentException If the type is invalid.
     */
    private function validateType(string $type): void
    {
        if (!in_array($type, self::VALID_TYPES, true)) {
            throw new InvalidArgumentException(
                "'type' must be one of ['offer', 'pranswer', 'answer', 'rollback'] (got '$type')"
            );
        }
    }

    /**
     * Returns the SDP string.
     *
     * @return string The SDP string.
     */
    public function getSdp(): string
    {
        return $this->sdp;
    }

    /**
     * Returns the session description type.
     *
     * @return string The type.
     */
    public function getType(): string
    {
        return $this->type;
    }

    /**
     * Returns the string representation of the session description.
     *
     * @return string The formatted string containing the SDP and type.
     */
    public function __toString(): string
    {
        return sprintf("RTCSessionDescription(sdp=%s, type=%s)", $this->sdp, $this->type);
    }

    /**
     * @param string $sdp
     * @return void
     */
    public function setSdp(string $sdp): void
    {
        $this->sdp = $sdp;
    }

    /**
     * @param string $type
     * @return void
     */
    public function setType(string $type): void
    {
        $this->type = $type;
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SDP;

/**
 * Represents a pattern a bit and provides functionality to match integers against it.
 */
final class BitPattern
{
    /** @var int The bitmask used to filter out 'x' bits. */
    private int $mask;

    /** @var int The value to compare against after applying the mask. */
    private int $maskedValue;

    /**
     * Initializes a new BitPattern instance.
     *
     * @param string $pattern A string representing the bit pattern (e.g., "1x0x1x0x").
     *                        '1' and '0' represent fixed bits, and 'x' represents wildcard bits.
     */
    public function __construct(string $pattern)
    {
        $this->mask = ~$this->createByteMask('x', $pattern);
        $this->maskedValue = $this->createByteMask('1', $pattern);
    }

    /**
     * Checks if the given integer matches the bit pattern.
     *
     * @param int $value The integer value to check against the bit pattern.
     * @return bool True if the value matches the pattern, false otherwise.
     */
    public function matches(int $value): bool
    {
        return ($value & $this->mask) === $this->maskedValue;
    }

    /**
     * Creates a bitmask based on a specific character in the pattern string.
     *
     * @param string $character The character to match in the pattern (e.g., '1' or 'x').
     * @param string $pattern The bit pattern string (e.g., "1x0x1x0x").
     * @return int A bitmask representing the positions of the specified character.
     */
    private function createByteMask(string $character, string $pattern): int
    {
        return (
            ((int) ($pattern[0] === $character) << 7) |
            ((int) ($pattern[1] === $character) << 6) |
            ((int) ($pattern[2] === $character) << 5) |
            ((int) ($pattern[3] === $character) << 4) |
            ((int) ($pattern[4] === $character) << 3) |
            ((int) ($pattern[5] === $character) << 2) |
            ((int) ($pattern[6] === $character) << 1) |
            ((int) ($pattern[7] === $character) << 0)
        );
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SDP\Enum;

enum H264Profile: int
{
    case PROFILE_CONSTRAINED_BASELINE = 0;
    case PROFILE_BASELINE = 1;
    case PROFILE_MAIN = 2;
    case PROFILE_CONSTRAINED_HIGH = 3;
    case PROFILE_HIGH = 4;
    case PROFILE_PREDICTIVE_HIGH_444 = 5;
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SDP\Enum;

enum H264Level: int
{
    case LEVEL1_B = -1;
    case LEVEL1 = 10;
    case LEVEL1_1 = 11;
    case LEVEL1_2 = 12;
    case LEVEL1_3 = 13;
    case LEVEL2 = 20;
    case LEVEL2_1 = 21;
    case LEVEL2_2 = 22;
    case LEVEL3 = 30;
    case LEVEL3_1 = 31;
    case LEVEL3_2 = 32;
    case LEVEL4 = 40;
    case LEVEL4_1 = 41;
    case LEVEL4_2 = 42;
    case LEVEL5 = 50;
    case LEVEL5_1 = 51;
    case LEVEL5_2 = 52;
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SDP\Enum;

enum SDPDirections : int
{
    case inactive = 0;
    case sendonly = 1;
    case recvonly = 2;
    case sendrecv = 3;
    case unknown = -1;

    public static function fromString(string $direction): SDPDirections
    {
        return match ($direction) {
            'inactive' => self::inactive,
            'sendonly' => self::sendonly,
            'recvonly' => self::recvonly,
            'sendrecv' => self::sendrecv,
            default => self::unknown,
        };
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SDP\Enum;

enum DtlsRole: string
{
    case Auto = "auto";
    case Client = "client";
    case Server = "server";
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SDP\Enum;

enum SDPType : int
{
    case offer = 0;
    case answer = 1;
    case pranswer = 2;
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\SDP;

use Webrtc\Mixin\DataClass;

/**
 * Represents an SSRC (Synchronization Source) description.
 */
#[DataClass]
final class SsrcDescription
{
    /**
     * Initializes a new SsrcDescription instance.
     *
     * @param int $ssrc The SSRC identifier.
     * @param string|null $cname The CNAME associated with the SSRC (optional).
     * @param string|null $msid The MSID associated with the SSRC (optional).
     * @param string|null $mslabel The MSLABEL associated with the SSRC (optional).
     * @param string|null $label The label associated with the SSRC (optional).
     */
    public function __construct(
        public int     $ssrc,
        public ?string $cname = null,
        public ?string $msid = null,
        public ?string $mslabel = null,
        public ?string $label = null
    )
    {
    }
}{
    "name": "danog/php-rtc-sdp",
    "type": "library",
    "description": "SDP parsing and generation for PHP WebRTC. MadelineProto's PHP 8.2+ fork is a Composer-compatible component of the modular danog/php-rtc stack.",
    "keywords": [
        "rtp",
        "sdp"
    ],
    "homepage": "https://github.com/danog/php-rtc-sdp",
    "license": "BSD-3-Clause",
    "authors": [
        {
            "name": "Amin Yazdanpanah",
            "email": "github@aminyazdanpanah.com"
        },
        {
            "name": "Sana Moniri",
            "email": "s.mo@quasarstream.com"
        },
        {
            "name": "Quasar Stream Community"
        },
        {
            "name": "Daniil Gentili",
            "email": "daniil@daniil.it"
        }
    ],
    "require": {
        "php": "^8.2",
        "danog/php-rtc-exception": "^1.1.5",
        "danog/php-rtc-rtp-parameter": "^1.1.4",
        "danog/php-rtc-ice": "^1.1.18 || ^2.0",
        "symfony/polyfill-php84": "*"
    },
    "require-dev": {
        "phpunit/phpunit": "^11.5 || ^12.0 || ^13.3.2",
        "bamarni/composer-bin-plugin": "^1.9.1"
    },
    "autoload": {
        "psr-4": {
            "Webrtc\\SDP\\": "src"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\Webrtc\\SDP\\": "tests"
        }
    },
    "minimum-stability": "stable",
    "scripts": {
        "post-install-cmd": [
            "@composer bin all install --ansi"
        ],
        "post-update-cmd": [
            "@composer bin all update --ansi"
        ]
    },
    "config": {
        "allow-plugins": {
            "bamarni/composer-bin-plugin": true
        }
    },
    "extra": {
        "bamarni-bin": {
            "bin-links": true,
            "forward-command": true
        }
    }
}
<?php

require 'vendor/autoload.php';

use danog\BetterPrometheus\BetterCollectorRegistry;
use Prometheus\Storage\InMemory;
use Prometheus\Storage\Redis;

$adapter = new InMemory;
// Any other promphp adapter may also be used...
// $adapter = new Redis();

$registry = new BetterCollectorRegistry($adapter);

// Note the difference with promphp: the labels are keys => values, not just keys.  
$counter = $registry->getOrRegisterCounter(
    'test',
    'some_counter',
    'it increases',
    // Note: these are default label key+values, they will be sent verbatim, no changes
    ['someLabel' => 'defaultValue']
);

// Specify some additional labels post-construction like this (both keys and values)...
$counter->incBy(3, ['type' => 'blue']);

// ...or add some more default labels to the counter, creating a new counter:
$newCounter = $counter->addLabels(['someOtherLabel' => 'someOtherDefaultValue']);
assert($newCounter !== $counter); // true
$newCounter->incBy(3, ['type' => 'blue']);



// Gauges can also be used
$gauge = $registry->getOrRegisterGauge(
    'test',
    'some_gauge',
    'it sets',
    ['someLabel' => 'defaultValue']
);
$gauge->set(2.5, ['type' => 'blue']);



// As well as histograms
$histogram = $registry->getOrRegisterHistogram(
    'test',
    'some_histogram',
    'it observes',
    ['someLabel' => 'defaultValue'],
    // [0.1, 1, 2, 3.5, 4, 5, 6, 7, 8, 9]
);
$histogram->observe(3.5, ['type' => 'blue']);


// And suummaries
$summary = $registry->getOrRegisterSummary(
    'test',
    'some_summary',
    'it observes a sliding window',
    ['someLabel' => 'defaultValue'],
    // 84600,
    // [0.01, 0.05, 0.5, 0.95, 0.99]
);

$summary->observe(5, ['type' => 'blue']);
<?php

$config = new class extends Amp\CodeStyle\Config {
    public function getRules(): array
    {
        return array_merge(parent::getRules(), [
            'void_return' => true,
            'phpdoc_to_param_type' => true,
            'phpdoc_to_return_type' => true,
            'phpdoc_to_property_type' => true,
        ]);
    }
};

$config->getFinder()
    ->in(__DIR__ . '/lib');

$cacheDir = getenv('TRAVIS') ? getenv('HOME') . '/.php-cs-fixer' : __DIR__;

$config->setCacheFile($cacheDir . '/.php_cs.cache');

return $config;
<?php

declare(strict_types=1);

namespace danog\BetterPrometheus;

use InvalidArgumentException;
use Prometheus\Histogram;
use Prometheus\Storage\Adapter;

/**
 * A better prometheus histogram.
 *
 * @api
 */
final class BetterHistogram extends BetterCollector
{
    const TYPE = 'histogram';

    /**
     * @var non-empty-list<float>
     */
    private readonly array $buckets;

    /**
     * @param array<string, string> $labels
     * @param non-empty-list<float>|null $buckets
     */
    public function __construct(
        Adapter $adapter,
        string $namespace,
        string $name,
        string $help,
        array $labels = [],
        ?array $buckets = null
    ) {
        parent::__construct($adapter, $namespace, $name, $help, $labels);

        if (null === $buckets) {
            /** @var non-empty-list<float> */
            $buckets = Histogram::getDefaultBuckets();
        }

        /** @psalm-suppress TypeDoesNotContainType */
        if (0 === \count($buckets)) {
            throw new InvalidArgumentException("Histogram must have at least one bucket.");
        }

        for ($i = 0; $i < \count($buckets) - 1; $i++) {
            if ($buckets[$i] >= $buckets[$i + 1]) {
                throw new InvalidArgumentException(
                    "Histogram buckets must be in increasing order: " .
                    $buckets[$i] . " >= " . $buckets[$i + 1]
                );
            }
        }
        $this->buckets = $buckets;
    }

    /** @param array<string, string> $labels */
    protected static function assertValidLabels(array $labels): void
    {
        if (isset($labels['le'])) {
            throw new \InvalidArgumentException("Histogram cannot have a label named 'le'.");
        }
        parent::assertValidLabels($labels);
    }

    public function addLabels(array $labels): static
    {
        return new self(
            $this->storageAdapter,
            $this->namespace,
            $this->name,
            $this->help,
            $this->labels + $labels,
            $this->buckets
        );
    }

    /**
     * @param double|int $value e.g. 123
     * @param array<string, string> $labels e.g. ['status' => '201', 'opcode' => 'SOME_OP']
     */
    public function observe(float|int $value, array $labels = []): void
    {
        self::assertValidLabels($labels);
        $labels = $this->labels + $labels;

        $this->storageAdapter->updateHistogram(
            [
                'value'       => $value,
                'name'        => $this->metricName,
                'help'        => $this->help,
                'type'        => self::TYPE,
                'labelNames'  => \array_keys($labels),
                'labelValues' => \array_values($labels),
                'buckets'     => $this->buckets,
            ]
        );
    }
}
<?php

declare(strict_types=1);

namespace danog\BetterPrometheus;

use InvalidArgumentException;
use Prometheus\Storage\Adapter;
use Prometheus\Summary;

/**
 * A better prometheus summary.
 *
 * @api
 */
final class BetterSummary extends BetterCollector
{
    const RESERVED_LABELS = ['quantile'];
    const TYPE = 'summary';

    /**
     * @var non-empty-list<float>
     */
    private readonly array $quantiles;

    /**
     * @param array<string, string> $labels
     * @param ?non-empty-list<float> $quantiles
     */
    public function __construct(
        Adapter $adapter,
        string $namespace,
        string $name,
        string $help,
        array $labels = [],
        private readonly int $maxAgeSeconds = 600,
        ?array $quantiles = null
    ) {
        parent::__construct($adapter, $namespace, $name, $help, $labels);

        if (null === $quantiles) {
            /** @var non-empty-list<float> */
            $quantiles = Summary::getDefaultQuantiles();
        }

        /** @psalm-suppress TypeDoesNotContainType */
        if (0 === \count($quantiles)) {
            throw new InvalidArgumentException("Summary must have at least one quantile.");
        }

        for ($i = 0; $i < \count($quantiles) - 1; $i++) {
            if ($quantiles[$i] >= $quantiles[$i + 1]) {
                throw new InvalidArgumentException(
                    "Summary quantiles must be in increasing order: " .
                    $quantiles[$i] . " >= " . $quantiles[$i + 1]
                );
            }
        }

        foreach ($quantiles as $quantile) {
            if ($quantile <= 0 || $quantile >= 1) {
                throw new InvalidArgumentException("Quantile $quantile invalid: Expected number between 0 and 1.");
            }
        }

        if ($maxAgeSeconds <= 0) {
            throw new InvalidArgumentException("maxAgeSeconds $maxAgeSeconds invalid: Expected number greater than 0.");
        }

        $this->quantiles = $quantiles;
    }

    public function addLabels(array $labels): static
    {
        return new self(
            $this->storageAdapter,
            $this->namespace,
            $this->name,
            $this->help,
            $this->labels + $labels,
            $this->maxAgeSeconds,
            $this->quantiles
        );
    }

    /** @param array<string, string> $labels */
    protected static function assertValidLabels(array $labels): void
    {
        if (isset($labels['quantile'])) {
            throw new \InvalidArgumentException("Sumamry cannot have a label named 'quantile'.");
        }
        parent::assertValidLabels($labels);
    }

    /**
     * @param double|int $value e.g. 123
     * @param array<string, string> $labels e.g. ['status' => '404', 'opcode' => 'SOME_OP']
     */
    public function observe(float|int $value, array $labels = []): void
    {
        self::assertValidLabels($labels);
        $labels = $this->labels + $labels;

        $this->storageAdapter->updateSummary(
            [
                'value'         => $value,
                'name'          => $this->metricName,
                'help'          => $this->help,
                'type'          => self::TYPE,
                'labelNames'    => \array_keys($labels),
                'labelValues'   => \array_values($labels),
                'maxAgeSeconds' => $this->maxAgeSeconds,
                'quantiles'     => $this->quantiles,
            ]
        );
    }
}
<?php

declare(strict_types=1);

namespace danog\BetterPrometheus;

use Prometheus\Collector;
use Prometheus\Storage\Adapter;

/**
 * A better prometheus collector.
 *
 * @api
 */
abstract class BetterCollector
{
    protected readonly string $metricName;

    /**
     * Constructor.
     */
    public function __construct(
        /** Storage adapter */
        public readonly Adapter $storageAdapter,
        /** Metric namespace */
        public readonly string $namespace,
        /** Metric name */
        public readonly string $name,
        /** Info about the metric */
        public readonly string $help,
        /** @var array<string, string> $labels Default labels, i.e. ['instance' => 'instance_1'] */
        public readonly array $labels = []
    ) {
        self::assertValidLabels($labels);
        $metricName = ($namespace !== '' ? $namespace . '_' : '') . $name;
        Collector::assertValidMetricName($metricName);
        $this->metricName = $metricName;
    }

    /** @param array<string, string> $labels */
    protected static function assertValidLabels(array $labels): void
    {
        foreach ($labels as $labelKey => $_) {
            Collector::assertValidLabel($labelKey);
        }
    }

    /**
     * Create a new instance of this collector, with these additional labels.
     *
     * @param array<string, string> $labels
     */
    abstract public function addLabels(array $labels): static;
}
<?php

declare(strict_types=1);

namespace danog\BetterPrometheus;

use Prometheus\Exception\MetricNotFoundException;
use Prometheus\Exception\MetricsRegistrationException;
use Prometheus\MetricFamilySamples;
use Prometheus\Storage\Adapter;

/**
 * A better collector registry.
 *
 * @api
 */
final class BetterCollectorRegistry
{
    /**
     * @var array<string, BetterGauge>
     */
    private array $gauges = [];

    /**
     * @var array<string, BetterCounter>
     */
    private array $counters = [];

    /**
     * @var array<string, BetterHistogram>
     */
    private array $histograms = [];

    /**
     * @var array<string, BetterSummary>
     */
    private array $summaries = [];

    /**
     * @psalm-suppress UnusedProperty
     */
    private ?BetterGauge $defaultGauge = null;

    /**
     * CollectorRegistry constructor.
     *
     */
    public function __construct(
        public readonly Adapter $storageAdapter,
        bool $registerDefaultMetrics = true
    ) {
        if ($registerDefaultMetrics) {
            $this->defaultGauge = $this->getOrRegisterGauge(
                "",
                "php_info",
                "Information about the PHP environment.",
                ["version" => PHP_VERSION]
            );
            $this->defaultGauge->set(1);
        }
    }

    /**
     * Removes all previously stored metrics from underlying storage adapter.
     */
    public function wipeStorage(): void
    {
        $this->storageAdapter->wipeStorage();
    }

    /**
     * @psalm-suppress TooManyArguments
     *
     * @return list<MetricFamilySamples>
     */
    public function getMetricFamilySamples(bool $sortMetrics = true): array
    {
        /** @var list<MetricFamilySamples> */
        return $this->storageAdapter->collect($sortMetrics);
    }

    /**
     * @param string $namespace e.g. cms
     * @param string $name e.g. duration_seconds
     * @param string $help e.g. The duration something took in seconds.
     * @param array<string, string> $labels e.g. ['controller' => 'someController', 'action' => 'someAction']
     *
     * @throws MetricsRegistrationException
     */
    public function registerGauge(string $namespace, string $name, string $help, array $labels = []): BetterGauge
    {
        $metricIdentifier = "$namespace:$name";
        if (isset($this->gauges[$metricIdentifier])) {
            throw new MetricsRegistrationException("Metric already registered");
        }
        $this->gauges[$metricIdentifier] = new BetterGauge(
            $this->storageAdapter,
            $namespace,
            $name,
            $help,
            $labels
        );
        return $this->gauges[$metricIdentifier];
    }

    /**
     * @throws MetricNotFoundException
     */
    public function getGauge(string $namespace, string $name): BetterGauge
    {
        $metricIdentifier = "$namespace:$name";
        if (!isset($this->gauges[$metricIdentifier])) {
            throw new MetricNotFoundException("Metric not found:" . $metricIdentifier);
        }
        return $this->gauges[$metricIdentifier];
    }

    /**
     * @param string $namespace e.g. cms
     * @param string $name e.g. duration_seconds
     * @param string $help e.g. The duration something took in seconds.
     * @param array<string, string> $labels e.g. ['controller' => 'someController', 'action' => 'someAction']
     *
     * @throws MetricsRegistrationException
     */
    public function getOrRegisterGauge(string $namespace, string $name, string $help, array $labels = []): BetterGauge
    {
        try {
            $gauge = $this->getGauge($namespace, $name);
        } catch (MetricNotFoundException $e) {
            $gauge = $this->registerGauge($namespace, $name, $help, $labels);
        }
        return $gauge;
    }

    /**
     * @param string $namespace e.g. cms
     * @param string $name e.g. requests
     * @param string $help e.g. The number of requests made.
     * @param array<string, string> $labels e.g. ['controller' => 'someController', 'action' => 'someAction']
     *
     * @throws MetricsRegistrationException
     */
    public function registerCounter(string $namespace, string $name, string $help, array $labels = []): BetterCounter
    {
        $metricIdentifier = "$namespace:$name";
        if (isset($this->counters[$metricIdentifier])) {
            throw new MetricsRegistrationException("Metric already registered");
        }
        $this->counters[$metricIdentifier] = new BetterCounter(
            $this->storageAdapter,
            $namespace,
            $name,
            $help,
            $labels
        );
        return $this->counters["$namespace:$name"];
    }

    /**
     *
     * @throws MetricNotFoundException
     */
    public function getCounter(string $namespace, string $name): BetterCounter
    {
        $metricIdentifier = "$namespace:$name";
        if (!isset($this->counters[$metricIdentifier])) {
            throw new MetricNotFoundException("Metric not found:" . $metricIdentifier);
        }
        return $this->counters["$namespace:$name"];
    }

    /**
     * @param string $namespace e.g. cms
     * @param string $name e.g. requests
     * @param string $help e.g. The number of requests made.
     * @param array<string, string> $labels e.g. ['controller' => 'someController', 'action' => 'someAction']
     *
     * @throws MetricsRegistrationException
     */
    public function getOrRegisterCounter(string $namespace, string $name, string $help, array $labels = []): BetterCounter
    {
        try {
            $counter = $this->getCounter($namespace, $name);
        } catch (MetricNotFoundException $e) {
            $counter = $this->registerCounter($namespace, $name, $help, $labels);
        }
        return $counter;
    }

    /**
     * @param string $namespace e.g. cms
     * @param string $name e.g. duration_seconds
     * @param string $help e.g. A histogram of the duration in seconds.
     * @param array<string, string> $labels e.g. ['controller' => 'someController', 'action' => 'someAction']
     * @param non-empty-list<float>|null $buckets e.g. [100, 200, 300]
     *
     * @throws MetricsRegistrationException
     */
    public function registerHistogram(
        string $namespace,
        string $name,
        string $help,
        array $labels = [],
        ?array $buckets = null
    ): BetterHistogram {
        $metricIdentifier = "$namespace:$name";
        if (isset($this->histograms[$metricIdentifier])) {
            throw new MetricsRegistrationException("Metric already registered");
        }
        $this->histograms[$metricIdentifier] = new BetterHistogram(
            $this->storageAdapter,
            $namespace,
            $name,
            $help,
            $labels,
            $buckets
        );
        return $this->histograms[$metricIdentifier];
    }

    /**
     *
     * @throws MetricNotFoundException
     */
    public function getHistogram(string $namespace, string $name): BetterHistogram
    {
        $metricIdentifier = "$namespace:$name";
        if (!isset($this->histograms[$metricIdentifier])) {
            throw new MetricNotFoundException("Metric not found:" . $metricIdentifier);
        }
        return $this->histograms["$namespace:$name"];
    }

    /**
     * @param string $namespace e.g. cms
     * @param string $name e.g. duration_seconds
     * @param string $help e.g. A histogram of the duration in seconds.
     * @param array<string, string> $labels e.g. ['controller' => 'someController', 'action' => 'someAction']
     * @param non-empty-list<float>|null $buckets e.g. [100, 200, 300]
     *
     * @throws MetricsRegistrationException
     */
    public function getOrRegisterHistogram(
        string $namespace,
        string $name,
        string $help,
        array $labels = [],
        ?array $buckets = null
    ): BetterHistogram {
        try {
            $histogram = $this->getHistogram($namespace, $name);
        } catch (MetricNotFoundException $e) {
            $histogram = $this->registerHistogram($namespace, $name, $help, $labels, $buckets);
        }
        return $histogram;
    }

    /**
     * @param string $namespace e.g. cms
     * @param string $name e.g. duration_seconds
     * @param string $help e.g. A summary of the duration in seconds.
     * @param array<string, string> $labels e.g. ['controller' => 'someController', 'action' => 'someAction']
     * @param int $maxAgeSeconds e.g. 604800
     * @param non-empty-list<float>|null $quantiles e.g. [0.01, 0.5, 0.99]
     *
     * @throws MetricsRegistrationException
     */
    public function registerSummary(
        string $namespace,
        string $name,
        string $help,
        array $labels = [],
        int $maxAgeSeconds = 600,
        ?array $quantiles = null
    ): BetterSummary {
        $metricIdentifier = "$namespace:$name";
        if (isset($this->summaries[$metricIdentifier])) {
            throw new MetricsRegistrationException("Metric already registered");
        }
        $this->summaries[$metricIdentifier] = new BetterSummary(
            $this->storageAdapter,
            $namespace,
            $name,
            $help,
            $labels,
            $maxAgeSeconds,
            $quantiles
        );
        return $this->summaries[$metricIdentifier];
    }

    /**
     *
     * @throws MetricNotFoundException
     */
    public function getSummary(string $namespace, string $name): BetterSummary
    {
        $metricIdentifier = "$namespace:$name";
        if (!isset($this->summaries[$metricIdentifier])) {
            throw new MetricNotFoundException("Metric not found:" . $metricIdentifier);
        }
        return $this->summaries["$namespace:$name"];
    }

    /**
     * @param string $namespace e.g. cms
     * @param string $name e.g. duration_seconds
     * @param string $help e.g. A summary of the duration in seconds.
     * @param array<string, string> $labels e.g. ['controller' => 'someController', 'action' => 'someAction']
     * @param int $maxAgeSeconds e.g. 604800
     * @param non-empty-list<float>|null $quantiles e.g. [0.01, 0.5, 0.99]
     *
     * @throws MetricsRegistrationException
     */
    public function getOrRegisterSummary(
        string $namespace,
        string $name,
        string $help,
        array $labels = [],
        int $maxAgeSeconds = 600,
        ?array $quantiles = null
    ): BetterSummary {
        try {
            $summary = $this->getSummary($namespace, $name);
        } catch (MetricNotFoundException $e) {
            $summary = $this->registerSummary($namespace, $name, $help, $labels, $maxAgeSeconds, $quantiles);
        }
        return $summary;
    }
}
<?php

declare(strict_types=1);

namespace danog\BetterPrometheus;

use Prometheus\Storage\Adapter;

/**
 * A better prometheus counter.
 *
 * @api
 */
final class BetterCounter extends BetterCollector
{
    private const TYPE = 'counter';

    public function addLabels(array $labels): static
    {
        return new self(
            $this->storageAdapter,
            $this->namespace,
            $this->name,
            $this->help,
            $this->labels + $labels
        );
    }

    /**
     * @param array<string, string> $labels e.g. ['status' => '201', 'opcode' => 'SOME_OP']
     */
    public function inc(array $labels = []): void
    {
        $this->incBy(1, $labels);
    }

    /**
     * @param int|float $count e.g. 2
     * @param array<string, string> $labels e.g. ['status' => '201', 'opcode' => 'SOME_OP']
     */
    public function incBy(int|float $count, array $labels = []): void
    {
        self::assertValidLabels($labels);
        $labels = $this->labels + $labels;
        $this->storageAdapter->updateCounter(
            [
                'name' => $this->metricName,
                'help' => $this->help,
                'type' => self::TYPE,
                'labelNames' => \array_keys($labels),
                'labelValues' => \array_values($labels),
                'value' => $count,
                'command' => \is_float($count) ? Adapter::COMMAND_INCREMENT_FLOAT : Adapter::COMMAND_INCREMENT_INTEGER,
            ]
        );
    }
}
<?php

declare(strict_types=1);

namespace danog\BetterPrometheus;

use Prometheus\Storage\Adapter;

/**
 * A better prometheus gauge.
 *
 * @api
 */
final class BetterGauge extends BetterCollector
{
    private const TYPE = 'gauge';

    public function addLabels(array $labels): static
    {
        return new self(
            $this->storageAdapter,
            $this->namespace,
            $this->name,
            $this->help,
            $this->labels + $labels
        );
    }

    /**
     * @param int|double $value e.g. 123
     * @param array<string, string> $labels e.g. ['status' => '201', 'opcode' => 'SOME_OP']
     */
    public function set(int|float $value, array $labels = []): void
    {
        self::assertValidLabels($labels);
        $labels = $this->labels + $labels;
        $this->storageAdapter->updateGauge(
            [
                'name' => $this->metricName,
                'help' => $this->help,
                'type' => self::TYPE,
                'labelNames' => \array_keys($labels),
                'labelValues' => \array_values($labels),
                'value' => $value,
                'command' => Adapter::COMMAND_SET,
            ]
        );
    }
    /**
     * @param array<string, string> $labels e.g. ['status' => '201', 'opcode' => 'SOME_OP']
     */
    public function incBy(int|float $value, array $labels = []): void
    {
        self::assertValidLabels($labels);
        $labels = $this->labels + $labels;
        $this->storageAdapter->updateGauge(
            [
                'name' => $this->metricName,
                'help' => $this->help,
                'type' => self::TYPE,
                'labelNames' => \array_keys($labels),
                'labelValues' => \array_values($labels),
                'value' => $value,
                'command' => \is_float($value) ? Adapter::COMMAND_INCREMENT_FLOAT : Adapter::COMMAND_INCREMENT_INTEGER,
            ]
        );
    }

    /**
     * @param array<string, string> $labels e.g. ['status' => '201', 'opcode' => 'SOME_OP']
     */
    public function inc(array $labels = []): void
    {
        $this->incBy(1, $labels);
    }

    /**
     * @param array<string, string> $labels e.g. ['status' => '201', 'opcode' => 'SOME_OP']
     */
    public function dec(array $labels = []): void
    {
        $this->decBy(1, $labels);
    }

    /**
     * @param array<string, string> $labels e.g. ['status' => '201', 'opcode' => 'SOME_OP']
     */
    public function decBy(int|float $value, array $labels = []): void
    {
        $this->incBy(-$value, $labels);
    }
}
{
    "name": "danog/better-prometheus",
    "description": "A better Prometheus library for PHP applications",
    "type": "library",
    "require": {
        "php": ">=8.1",
        "promphp/prometheus_client_php": "^2.10"
    },
    "license": "Apache-2.0",
    "autoload": {
        "psr-4": {
            "danog\\BetterPrometheus\\": "lib/"
        }
    },
    "require-dev": {
        "amphp/php-cs-fixer-config": "^2",
        "psalm/phar": "dev-master",
        "danog/phpdoc": "^0.1.24"
    },
    "authors": [
        {
            "name": "Daniil Gentili",
            "email": "daniil@daniil.it"
        }
    ],
    "scripts": {
        "cs": "php-cs-fixer fix -v --diff --dry-run",
        "cs-fix": "php-cs-fixer fix -v --diff"
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Codecs;

use Webrtc\RTPParameter\RTCRtpCodecCapability;
use Webrtc\RTPParameter\RTCRtpCodecParameters;

/**
 * Codec Utility Class
 *
 * Provides helper methods and utilities for codec-related operations.
 * Contains platform-agnostic functionality needed by various codec implementations.
 *
 * @package Webrtc\Codecs
 */
final class CodecUtility
{
    /**
     * Gets the number of available CPU cores
     *
     * Cross-platform method to determine available processing cores.
     * Uses different system commands based on the operating system.
     *
     * @return int Number of CPU cores
     */
    public static function getNumberOfCPUCores(): int
    {
        $cores = PHP_OS_FAMILY === 'Windows'
            ? (string) getenv('NUMBER_OF_PROCESSORS')
            : (string) @exec('nproc');

        $cores = trim($cores);
        return $cores !== '' ? (int) $cores : 1;
    }

    /**
     * Checks if a codec is an RTX (Retransmission) codec
     *
     * @param RTCRtpCodecParameters|RTCRtpCodecCapability $codec Codec to check
     * @return bool True if the codec is RTX, false otherwise
     */
    public static function isRtx(RTCRtpCodecParameters|RTCRtpCodecCapability $codec): bool
    {
        return strtolower(explode("/", $codec->mimeType)[1]) === 'rtx';
    }

    /**
     * Gets the payload type an RTX codec retransmits, as declared by its `apt` parameter
     *
     * The value arrives as an int when the codec was built from the local capability list, and as
     * a string when it was parsed out of an `a=fmtp` line, so it has to be normalised before it can
     * be compared against a payload type. A codec with no usable `apt` yields null, which never
     * matches a real payload type.
     *
     * @param RTCRtpCodecParameters|RTCRtpCodecCapability $codec Codec to read
     * @return int|null The associated payload type, or null if the codec declares none
     */
    public static function apt(RTCRtpCodecParameters|RTCRtpCodecCapability $codec): ?int
    {
        $apt = $codec->parameters['apt'] ?? null;

        return is_int($apt) || (is_string($apt) && ctype_digit($apt)) ? (int) $apt : null;
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Codecs;

interface PayloadDescriptorInterface
{
    public function encode(): string;

    public static function decode(string $data): array;
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Codecs\Audio\PCM;

use Webrtc\AVCodec\Exception\AvCodecException;

/**
 * PCM A-law Decoder Class
 *
 * Decodes A-law PCM audio to standard 8 kHz mono signed 16-bit PCM format.
 * Extends the base PCMDecoder functionality specifically for A-law codec.
 *
 * @package Webrtc\Codecs\Audio\PCM
 */
final class PCMaDecoder extends PCMDecoder
{
    /**
     * Constructor
     *
     * Initializes the decoder with A-law PCM codec configuration.
     * Set up the parent PCMDecoder with 'pcm_alaw' codec specification.
     * @throws AvCodecException
     */
    public function __construct()
    {
        parent::__construct("pcm_alaw");
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Codecs\Audio\PCM;

use Webrtc\AVCodec\AVCodec;
use Webrtc\AVCodec\AVFilter;
use Webrtc\AVCodec\Codec;
use Webrtc\AVCodec\Context\AudioContext;
use Webrtc\AVCodec\Data\Packet;
use Webrtc\Codecs\EncodedPacket;
use Webrtc\AVCodec\Exception\AvCodecException;
use Webrtc\AVCodec\Frame\AudioFrame;
use Webrtc\AVCodec\Frame\FrameInterface;
use Webrtc\AVCodec\TransCoder;
use Webrtc\Codecs\Encoder;
use Webrtc\Codecs\EncoderInterface;
use Webrtc\Exception\RuntimeException;

/**
 * PCM Audio Encoder Abstract Base Class
 *
 * Provides base functionality for encoding PCM audio to various codecs.
 * Handles common PCM input configuration (8 kHz, mono, signed 16-bit)
 * and provides a standard encoding pipeline.
 *
 * @package Webrtc\Codecs\Audio\PCM
 */
abstract class PCMEncoder extends Encoder implements EncoderInterface
{
    /**
     * @var int SAMPLE_RATE Input sample rate (8kHz)
     */
    private const SAMPLE_RATE = 8000;

    /**
     * @var TransCoder|null $transcoder Audio transcoder instance
     */
    private ?TransCoder $transcoder;

    /**
     * Constructor
     *
     * @param string $codecName Name of the target audio codec
     * @throws AvCodecException
     */
    public function __construct(string $codecName)
    {
        AVCodec::init();
        AVFilter::init();

        $audioContext = AudioContext::create(new Codec($codecName, "w"));
        \assert($audioContext instanceof AudioContext);
        $audioContext->setFormat("s16");
        $audioContext->setLayout("mono");
        $audioContext->setSampleRate(self::SAMPLE_RATE);
        $audioContext->open();
        $this->transcoder = new TransCoder($audioContext);
    }

    /**
     * Encode an audio frame
     *
     * @param FrameInterface $frame Audio frame to encode
     * @param bool $useKeyframe Ignored for audio (interface compatibility)
     * @return array|string [packets, pts] Array of encoded packets and presentation timestamp
     * @throws RuntimeException If frame validation fails
     */
    #[\Override]
    public function encode(FrameInterface $frame, bool $useKeyframe = false): string|array
    {
        $this->validateFrame($frame);
        \assert($this->transcoder instanceof TransCoder);
        /** @var Packet[] $packets */
        $packets = $this->transcoder->encode($frame);

        return [array_map(static fn(Packet $pkt): string => $pkt->getData(), $packets), $packets[0]->getPts()];
    }

    /**
     * Package encoded data for transport
     *
     * @param Packet|EncodedPacket $packet Encoded audio packet
     * @return array|string [packets, pts] Array containing packet data and converted timestamp
     */
    #[\Override]
    public function pack(Packet|EncodedPacket $packet): string|array
    {
        if ($packet instanceof EncodedPacket) {
            // Already timed in the 8kHz PCM RTP clock: pass it straight through, no codec needed.
            return [[$packet->getData()], $packet->getTimestamp()];
        }
        return [[$packet->getData()], $this->convertTimebase($packet->getPts() ?? 0, $this->getTimebaseArray($packet->getTimeBase()), [1, 8000])];
    }

    /**
     * Validate input audio frame
     *
     * @param FrameInterface $frame Frame to validate
     * @throws RuntimeException If the frame is invalid
     */
    private function validateFrame(FrameInterface $frame): void
    {
        if (!$frame instanceof AudioFrame) {
            throw new RuntimeException("frame is not audio");
        }
        if ($frame->getFormat()->getName() != "s16") {
            throw new RuntimeException("format is not valid");
        }
        if (!in_array($frame->getLayout()->getName(), ["mono", "stereo"])) {
            throw new RuntimeException("Audio frame should be either mono or stereo");
        }
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Codecs\Audio\PCM;

use Webrtc\AVCodec\AVCodec;
use Webrtc\AVCodec\Codec;
use Webrtc\AVCodec\Context\AudioContext;
use Webrtc\AVCodec\Data\Packet;
use Webrtc\AVCodec\Exception\AvCodecException;
use Webrtc\AVCodec\TransCoder;
use Webrtc\Codecs\DecoderInterface;
use Webrtc\Codecs\JitterFrameInterface;

/**
 * PCM Audio Decoder Abstract Base Class
 *
 * Provides base functionality for decoding various audio codecs to PCM format.
 * Handles common PCM output configuration (8 kHz, mono, signed 16-bit).
 *
 * @package Webrtc\Codecs\Audio\PCM
 */
abstract class PCMDecoder implements DecoderInterface
{
    /**
     * @var int SAMPLE_RATE Output sample rate (8kHz)
     */
    private const SAMPLE_RATE = 8000;

    /**
     * @var TransCoder|null $transcoder Audio transcoder instance
     */
    private ?TransCoder $transcoder;

    /**
     * Constructor
     *
     * @param string $codecName Name of the audio codec to decode
     * @throws AvCodecException
     */
    public function __construct(string $codecName)
    {
        AVCodec::init();

        $audioContext = AudioContext::create(new Codec($codecName));
        \assert($audioContext instanceof AudioContext);
        $audioContext->setFormat("s16");
        $audioContext->setLayout("mono");
        $audioContext->setSampleRate(self::SAMPLE_RATE);
        $this->transcoder = new TransCoder($audioContext);
    }

    /**
     * Decode an audio packet to PCM frames
     *
     * @param JitterFrameInterface $frame Input frame containing encoded audio and timestamp
     * @return array Array of decoded AudioFrame objects
     */
    #[\Override]
    public function decode(JitterFrameInterface $frame): array
    {
        $packet = new Packet();
        $packet->putData($frame->getData());
        $packet->setPts($frame->getTimestamp());
        $packet->setTimeBase(1, 8000);

        \assert($this->transcoder instanceof TransCoder);
        return $this->transcoder->decode($packet);
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Codecs\Audio\PCM;

use Webrtc\AVCodec\Exception\AvCodecException;

/**
 * PCM Î¼-law Decoder Class
 *
 * Decodes Î¼-law (mu-law) PCM audio to standard 8 kHz mono signed 16-bit PCM format.
 * Extends the base PCMDecoder functionality specifically for Î¼-law codec (G.711).
 *
 * @package Webrtc\Codecs\Audio\PCM
 */
final class PCMuDecoder extends PCMDecoder
{
    /**
     * Constructor
     *
     * Initializes the decoder with Î¼-law PCM codec configuration.
     * Configures the parent PCMDecoder with:
     * - 'pcm_mulaw' codec specification
     * - 8kHz output sample rate
     * - Mono channel output
     * - Signed 16-bit PCM output
     * @throws AvCodecException
     */
    public function __construct()
    {
        parent::__construct("pcm_mulaw");
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Codecs\Audio\PCM;

use Webrtc\AVCodec\Exception\AvCodecException;

/**
 * PCM Î¼-law Encoder Class
 *
 * Encodes 16-bit linear PCM audio to Î¼-law (G.711) compressed format.
 * This encoder is optimized for telephony applications and VoIP systems,
 * providing the standard 64 kbps Î¼-law encoding used in North American
 * digital telephony networks.
 *
 * @package Webrtc\Codecs\Audio\PCM
 */
final class PCMuEncoder extends PCMEncoder
{
    /**
     * Constructor - Initializes Î¼-law PCM encoder
     *
     * Configures the encoder with the following specifications:
     * - Codec: pcm_mulaw (ITU-T G.711 Î¼-law)
     * - Sample Rate: 8000 Hz (standard telephony)
     * - Channels: Mono
     * - Input Format: Signed 16-bit PCM
     * - Output Format: 8-bit Î¼-law compressed
     *
     * The encoder maintains compatibility with:
     * - PSTN systems
     * - VoIP protocols (SIP, RTP)
     * - Legacy telephony equipment
     * @throws AvCodecException
     */
    public function __construct()
    {
        parent::__construct("pcm_mulaw");
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Codecs\Audio\PCM;

use Webrtc\AVCodec\Exception\AvCodecException;

/**
 * PCM A-law Encoder Class
 *
 * Encodes standard 8 kHz mono signed 16-bit PCM audio to A-law PCM format (G.711).
 * Extends the base PCMEncoder functionality specifically for A-law encoding.
 *
 * @package Webrtc\Codecs\Audio\PCM
 */
final class PCMaEncoder extends PCMEncoder
{
    /**
     * Constructor
     *
     * Initializes the encoder with A-law PCM codec configuration.
     * Set up the parent PCMEncoder with 'pcm_alaw' codec specification.
     * Configure the encoder for:
     * - 8kHz sample rate
     * - Mono channel layout
     * - A-law compression
     * @throws AvCodecException
     */
    public function __construct()
    {
        parent::__construct("pcm_alaw");
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Codecs\Audio\Opus;

use Webrtc\AVCodec\AVCodec;
use Webrtc\AVCodec\AVFilter;
use Webrtc\AVCodec\Audio\AudioResampler;
use Webrtc\AVCodec\Codec;
use Webrtc\AVCodec\Context\AudioContext;
use Webrtc\AVCodec\Data\Packet;
use Webrtc\AVCodec\Exception\AvCodecException;
use Webrtc\AVCodec\Frame\AudioFrame;
use Webrtc\AVCodec\TransCoder;
use Webrtc\Codecs\DecoderInterface;
use Webrtc\Codecs\JitterFrameInterface;

/**
 * Opus Audio Decoder Class
 *
 * Provides decoding of Opus audio packets to raw PCM audio frames, backed by
 * FFmpeg's native Opus decoder (via danog/php-rtc-av) rather than libopus directly.
 * The decoder emits planar float; this class resamples it to the standard Opus
 * output format (interleaved s16, 48 kHz stereo, 960 samples per frame).
 *
 * @package Webrtc\Codecs\Audio\Opus
 */
final class OpusDecoder implements DecoderInterface
{
    /**
     * @var int SAMPLE_RATE Opus required sample rate (48kHz)
     */
    private const SAMPLE_RATE = 48000;

    /**
     * @var int CHANNELS Opus output channel count (stereo)
     */
    private const CHANNELS = 2;

    /**
     * @var int SAMPLE_BYTES Bytes per sample for the s16 output format
     */
    private const SAMPLE_BYTES = 2;

    /**
     * @var TransCoder $transcoder Opus transcoder instance
     */
    private TransCoder $transcoder;

    /**
     * @var AudioResampler $resampler Converts the decoder's float output to s16
     */
    private AudioResampler $resampler;

    /**
     * Constructor
     *
     * Initializes required libraries:
     * - AVCodec for the Opus decoder
     * - AVFilter for the resampler used to convert to s16
     * @throws AvCodecException
     */
    public function __construct()
    {
        AVCodec::init();
        AVFilter::init();

        $context = AudioContext::create(new Codec("opus"));
        \assert($context instanceof AudioContext);
        $context->setFormat("fltp");
        $context->setLayout("stereo");
        $context->setSampleRate(self::SAMPLE_RATE);

        $this->transcoder = new TransCoder($context);
        $this->resampler = new AudioResampler("s16", "stereo", self::SAMPLE_RATE, 960);
    }

    /**
     * Decode an Opus packet to audio frame(s)
     *
     * @param JitterFrameInterface $frame Input frame containing Opus packet and timestamp
     * @return AudioFrame[] Array containing one decoded AudioFrame (Opus uses fixed frame sizes)
     * @throws AvCodecException
     */
    #[\Override]
    public function decode(JitterFrameInterface $frame): array
    {
        $packet = new Packet();
        $packet->putData($frame->getData());
        $packet->setPts($frame->getTimestamp());
        $packet->setTimeBase(1, self::SAMPLE_RATE);

        $frames = [];
        foreach ($this->transcoder->decode($packet) as $decoded) {
            \assert($decoded instanceof AudioFrame);
            foreach ($this->resampler->resample($decoded) as $resampled) {
                $frames[] = $this->buildFrame($resampled, $frame->getTimestamp());
            }
        }

        return $frames;
    }

    /**
     * Repackages a resampled frame into a tightly-allocated s16 AudioFrame.
     *
     * The resampler pads its output buffer for alignment, so its plane reports more
     * bytes than the frame logically holds. Copying the logical samples into a fresh
     * frame yields the exact 4 * samples byte layout callers expect from Opus.
     *
     * @param AudioFrame $frame Resampled s16 frame from the filter graph
     * @param int $pts Presentation timestamp to stamp on the output
     * @return AudioFrame Clean s16 stereo frame
     */
    private function buildFrame(AudioFrame $frame, int $pts): AudioFrame
    {
        $samples = $frame->getSamples();
        $length = $samples * self::CHANNELS * self::SAMPLE_BYTES;
        $data = substr($frame->getPlanes()[0]->getData(), 0, $length);

        $audioFrame = new AudioFrame("s16", "stereo", $samples);
        $audioFrame->getPlanes()[0]->putData($data);
        $audioFrame->setSampleRate(self::SAMPLE_RATE);
        $audioFrame->setPts($pts);
        $audioFrame->setTimeBase(1, self::SAMPLE_RATE);

        return $audioFrame;
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Codecs\Audio\Opus;

use Webrtc\AVCodec\Audio\AudioResampler;
use Webrtc\AVCodec\AVCodec;
use Webrtc\AVCodec\AVFilter;
use Webrtc\AVCodec\Codec;
use Webrtc\AVCodec\Context\AudioContext;
use Webrtc\AVCodec\Data\Packet;
use Webrtc\Codecs\EncodedPacket;
use Webrtc\AVCodec\Exception\AvCodecException;
use Webrtc\AVCodec\Frame\AudioFrame;
use Webrtc\AVCodec\Frame\FrameInterface;
use Webrtc\AVCodec\TransCoder;
use Webrtc\Codecs\Encoder as BEncoder;
use Webrtc\Codecs\EncoderInterface;
use Webrtc\Exception\RuntimeException;

/**
 * Opus Audio Encoder Class
 *
 * Provides encoding of audio frames to Opus format, backed by FFmpeg's libopus
 * encoder (via danog/php-rtc-av). Input frames are resampled to Opus-compatible
 * parameters (48 kHz, stereo, 960 samples per frame) before encoding.
 *
 * @package Webrtc\Codecs\Audio\Opus
 */
final class OpusEncoder extends BEncoder implements EncoderInterface
{    /**
     * @var int SAMPLE_RATE Opus required sample rate (48kHz)
     */
    private const SAMPLE_RATE = 48000;

    /**
     * @var int SAMPLES_PER_FRAME Opus frame size (960 samples)
     */
    private const SAMPLES_PER_FRAME = 960;

    /**
     * @var TransCoder|null $transcoder Opus encoder transcoder instance
     */
    private ?TransCoder $transcoder = null;

    /**
     * @var AudioResampler|null $resampler Audio resampler instance
     */
    private ?AudioResampler $resampler = null;

    /**
     * Constructor
     *
     * The native libraries are only loaded on the first actual encode: packing an
     * already-encoded OPUS frame for RTP needs no codec at all, so FFI stays optional.
     */
    public function __construct()
    {
    }

    /**
     * Load libav/libopus on demand.
     *
     * @throws AvCodecException
     */
    private function ensureEncoder(): void
    {
        if (isset($this->transcoder)) {
            return;
        }
        AVCodec::init();
        AVFilter::init();

        $context = AudioContext::create(new Codec("libopus", "w"));
        \assert($context instanceof AudioContext);
        $context->setFormat("s16");
        $context->setLayout("stereo");
        $context->setSampleRate(self::SAMPLE_RATE);

        $this->transcoder = new TransCoder($context);
        $this->resampler = new AudioResampler("s16", "stereo", self::SAMPLE_RATE, self::SAMPLES_PER_FRAME);
    }

    /**
     * Encode an audio frame to Opus format
     *
     * @param FrameInterface|AudioFrame $frame Audio frame to encode
     * @param bool $useKeyframe Ignored for audio (kept for interface compatibility)
     * @return array|string [packets, pts] Array of encoded packets and presentation timestamp
     * @throws RuntimeException If frame validation fails
     * @throws AvCodecException
     */
    #[\Override]
    public function encode(FrameInterface|AudioFrame $frame, bool $useKeyframe = false): string|array
    {
        $this->ensureEncoder();
        $this->validateFrame($frame);
        \assert($frame instanceof AudioFrame);
        \assert($this->resampler instanceof AudioResampler);
        $frames = $this->resampler->resample($frame);

        $packets = [];
        foreach ($frames as $frame) {
            \assert($this->transcoder instanceof TransCoder);
            /** @var Packet[] $encoded */
            $encoded = $this->transcoder->encode($frame);
            foreach ($encoded as $packet) {
                $packets[] = $packet->getData();
            }
        }

        return [$packets, $frame->getPts()];
    }

    /**
     * Package encoded data into transport format
     *
     * @param Packet|EncodedPacket $packet Encoded audio packet
     * @return array|string [packets, pts] Array containing packet data and converted timestamp
     */
    #[\Override]
    public function pack(Packet|EncodedPacket $packet): string|array
    {
        if ($packet instanceof EncodedPacket) {
            // Already timed in the 48kHz OPUS clock: pass it straight through, no codec needed.
            return [[$packet->getData()], $packet->getTimestamp()];
        }
        return [[$packet->getData()], $this->convertTimebase($packet->getPts() ?? 0, $this->getTimebaseArray($packet->getTimeBase()), [1, 48000])];
    }

    /**
     * Validate audio frame meets Opus requirements
     *
     * @param FrameInterface $frame Frame to validate
     * @throws RuntimeException If the frame is invalid
     */
    private function validateFrame(FrameInterface $frame): void
    {
        if (!$frame instanceof AudioFrame) {
            throw new RuntimeException("frame is not audio");
        }
        if ($frame->getFormat()->getName() != "s16") {
            throw new RuntimeException("format is not valid");
        }
        if (!in_array($frame->getLayout()->getName(), ["mono", "stereo"])) {
            throw new RuntimeException("Audio frame should be either mono or stereo");
        }
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Codecs;

interface JitterFrameInterface
{
    public function getData(): string;

    public function getTimestamp(): int;
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Codecs;

/**
 * Abstract Encoder Base Class
 *
 * Provides common functionality and base implementation for all encoder classes.
 * Handles bitrate management and timestamp conversion operations shared across
 * different codec implementations.
 *
 * @package Webrtc\Codecs
 */
abstract class Encoder implements EncoderInterface
{
    /**
     * @var int $bitrate Current target bitrate in bits per second (default: 1 Mbps)
     */
    protected int $bitrate = 1000000;

    /**
     * Gets the current encoder bitrate
     *
     * @return int Current bitrate in bits per second
     */
    public function getBitrate(): int
    {
        return $this->bitrate;
    }

    /**
     * Sets the target encoder bitrate
     *
     * @param int $bitrate New bitrate in bits per second
     */
    #[\Override]
    public function setBitrate(int $bitrate): void
    {
        $this->bitrate = $bitrate;
    }

    /**
     * Converts timestamps between different timebases
     *
     * @param int $pts Presentation timestamp to convert
     * @param array{num: int, den: int} $fromBase Source timebase as ['num' => numerator, 'den' => denominator]
     * @param array{0: int, 1: int} $toBase Target timebase as [numerator, denominator]
     * @return int Converted timestamp
     */
    protected function convertTimebase(int $pts, array $fromBase, array $toBase): int
    {
        if ($fromBase['num'] != $toBase[0] || $fromBase['den'] != $toBase[1]) {
            $pts = (int)((float)$pts * ((float)$fromBase['num'] / (float)$fromBase['den']) / ((float)$toBase[0] / (float)$toBase[1]));
        }
        return $pts;
    }

    /**
     * Normalizes a time base object ({num, den}) into the array shape convertTimebase() expects
     *
     * @param \stdClass $timeBase Time base object as returned by frames and packets
     * @return array{num: int, den: int}
     */
    protected function getTimebaseArray(\stdClass $timeBase): array
    {
        return ['num' => (int)$timeBase->num, 'den' => (int)$timeBase->den];
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Codecs;

use Webrtc\AVCodec\Data\Packet;
use Webrtc\AVCodec\Frame\FrameInterface;

interface EncoderInterface
{
    public function encode(FrameInterface $frame, bool $useKeyframe): string|array;
    public function pack(Packet|EncodedPacket $packet): string|array;
    public function setBitrate(int $bitrate): void;
}<?php

/**
 * This file is part of the PHP WebRTC package, vendored and modified for MadelineProto.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Codecs;

/**
 * An already-encoded media unit, carried through the stack without ever touching a codec.
 *
 * The upstream stack represents encoded media with `AVCodec\Data\Packet`, which is a thin wrapper
 * around an FFI `AVPacket` and therefore requires libavcodec. When media is read from a file that
 * already stores the exact codec and framing the peer expects (OGG OPUS, WebM VP8, MP4 H.264), no
 * transcoding is needed at all, and this pure-PHP class is used instead so that the FFI extension
 * stays entirely optional.
 */
final class EncodedPacket
{
    /**
     * @param string $data      The encoded payload (one full frame).
     * @param int    $timestamp Presentation timestamp, already expressed in the codec's RTP clock.
     * @param bool   $keyframe  Whether this frame can be decoded without any previous frame.
     * @param ?int   $audioLevel Audio level in -dBov (0..127), if known.
     */
    public function __construct(
        private readonly string $data,
        private readonly int $timestamp,
        private readonly bool $keyframe = true,
        private readonly ?int $audioLevel = null,
    ) {
    }

    public function getData(): string
    {
        return $this->data;
    }

    public function getTimestamp(): int
    {
        return $this->timestamp;
    }

    public function isKeyframe(): bool
    {
        return $this->keyframe;
    }

    public function getAudioLevel(): ?int
    {
        return $this->audioLevel;
    }

    public function getSize(): int
    {
        return \strlen($this->data);
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Codecs\Video\X264;

use Exception;
use Generator;
use Iterator;
use Webrtc\AVCodec\AVCodec;
use Webrtc\AVCodec\Codec;
use Webrtc\AVCodec\Context\Context;
use Webrtc\AVCodec\Context\VideoContext;
use Webrtc\AVCodec\Data\Packet;
use Webrtc\Codecs\EncodedPacket;
use Webrtc\AVCodec\Enum\PictureType;
use Webrtc\AVCodec\Exception\AvCodecException;
use Webrtc\AVCodec\Format\VideoFormat;
use Webrtc\AVCodec\Frame\FrameInterface;
use Webrtc\AVCodec\Frame\VideoFrame;
use Webrtc\AVCodec\TransCoder;
use Webrtc\Codecs\Encoder;
use Webrtc\Codecs\EncoderInterface;
use Webrtc\Exception\InvalidArgumentException;
use Webrtc\Exception\RuntimeException;

/**
 * H.264 Video Encoder Class
 *
 * Implements real-time H.264 video encoding with RTP packetization.
 * Supports both hardware-accelerated (OMX) and software (libx264) encoding.
 * Provides adaptive bitrate control and frame-type management.
 *
 * @package Webrtc\Codecs\Video\X264
 */
final class H264Encoder extends Encoder implements EncoderInterface
{
    private const FU_A_HEADER_SIZE = 2;
    private const NAL_TYPE_FU_A = 28;
    private const NAL_TYPE_STAP_A = 24;
    private const NAL_HEADER_SIZE = 1;
    private const LENGTH_FIELD_SIZE = 2;
    private const STAP_A_HEADER_SIZE = 1;
    private const DEFAULT_CODEC_NAMES = ["h264_omx", "libx264"]; // Based on their priorities
    private const PACKET_MAX = 1300; // Maximum packet size.
    /**
     * @var string $bufferData Buffered encoded data
     */
    private string $bufferData = "";

    /**
     * @var int|null $bufferPts PTS of buffered data
     */
    private ?int $bufferPts = null;

    /**
     * @var VideoContext|null $encoderContext Active encoder context
     */
    private ?VideoContext $encoderContext = null;

    /**
     * @var bool $codecBuffering Flag for codec buffering mode
     */
    private bool $codecBuffering = false;

    /**
     * @var int $bitrate Current target bitrate (1Mbps default)
     */
    protected int $bitrate = 1000000;

    /**
     * Constructor - initializes AVCodec
     * @throws AvCodecException
     */
    public function __construct()
    {
        // Packetizing an already-encoded H.264 bitstream needs no codec: libav is loaded lazily.
    }

    /**
     * Load libav on demand.
     *
     * @throws AvCodecException
     */
    private function ensureEncoder(): void
    {
        AVCodec::init();
    }

    /**
     * Packetizes encoded H.264 data
     *
     * @param iterable<string> $packets Encoded NAL units
     * @return array Packetized RTP payloads
     */
    public static function packetize(iterable $packets): array
    {
        $packetizedPackages = [];
        $packetsIterator = (function () use ($packets) {
            foreach ($packets as $packet) {
                yield $packet;
            }
        })();

        $packet = $packetsIterator->current();
        while ($packet !== null) {
            if (strlen($packet) > self::PACKET_MAX) {
                $packetizedPackages = array_merge(
                    $packetizedPackages,
                    self::packetizeFuA($packet)
                );
                $packetsIterator->next();
                $packet = $packetsIterator->current();
            } else {
                list($packetized, $packet) = self::packetizeStapA($packet, $packetsIterator);
                $packetizedPackages[] = $packetized;
            }
        }

        return $packetizedPackages;
    }

    /**
     * Splits large NAL units into FU-A fragments
     *
     * @param string $data NAL unit data
     * @return array FU-A packets
     */
    private static function packetizeFuA(string $data): array
    {
        $availableSize = self::PACKET_MAX - self::FU_A_HEADER_SIZE;
        $payloadSize = strlen($data) - self::NAL_HEADER_SIZE;
        $numPackets = (int)ceil($payloadSize / $availableSize);
        $numLargerPackets = $payloadSize % $numPackets;
        $packetsize = (int)floor($payloadSize / $numPackets);

        $fNri = ord($data[0]) & (0x80 | 0x60); // Extract forbidden bit and NRI
        $nal = ord($data[0]) & 0x1F; // Extract NAL type

        $fuIndicator = $fNri | self::NAL_TYPE_FU_A;

        $fuHeaderEnd = chr($fuIndicator) . chr($nal | 0x40);
        $fuHeaderMiddle = chr($fuIndicator) . chr($nal);
        $fuHeaderStart = chr($fuIndicator) . chr($nal | 0x80);
        $fuHeader = $fuHeaderStart;

        $packets = [];
        $offset = self::NAL_HEADER_SIZE;

        while ($offset < strlen($data)) {
            if ($numLargerPackets > 0) {
                $numLargerPackets--;
                $payload = substr($data, $offset, $packetsize + 1);
                $offset += $packetsize + 1;
            } else {
                $payload = substr($data, $offset, $packetsize);
                $offset += $packetsize;
            }

            if ($offset == strlen($data)) {
                $fuHeader = $fuHeaderEnd;
            }

            $packets[] = $fuHeader . $payload;
            $fuHeader = $fuHeaderMiddle;
        }

        if ($offset !== strlen($data)) {
            throw new RuntimeException("Incorrect fragment data");
        }

        return $packets;
    }

    /**
     * Aggregates small NAL units into STAP-A packets
     *
     * @param string $data Initial NAL unit
     * @param \Iterator<mixed, string> $packetsIterator Remaining NAL units
     * @return array{0: string, 1: string|null} [STAP-A packet, next NAL unit]
     */
    private static function packetizeStapA(string $data, \Iterator $packetsIterator): array
    {
        $counter = 0;
        $availableSize = self::PACKET_MAX - self::STAP_A_HEADER_SIZE;

        $stapHeader = self::NAL_TYPE_STAP_A | (ord($data[0]) & 0xE0);

        $payload = "";
        // A failure while walking the NAL iterator must propagate: it was previously swallowed
        // and a partially-aggregated STAP-A was returned with next=null, silently dropping the
        // frame's remaining NAL units and emitting an incomplete packet.
        $nalu = $data; // with header
        while ($nalu !== null && strlen($nalu) <= $availableSize && $counter < 9) {
            $stapHeader |= ord($nalu[0]) & 0x80;

            $nri = ord($nalu[0]) & 0x60;
            if (($stapHeader & 0x60) < $nri) {
                $stapHeader = ($stapHeader & 0x9F) | $nri;
            }

            $availableSize -= self::LENGTH_FIELD_SIZE + strlen($nalu);
            $counter++;
            $payload .= pack("n", strlen($nalu)) . $nalu;
            $packetsIterator->next();
            $nalu = $packetsIterator->current();
        }

        if ($counter == 0) {
            $packetsIterator->next();
            $nalu = $packetsIterator->current();
        }

        if ($counter <= 1) {
            return [$data, $nalu];
        } else {
            return [chr($stapHeader) . $payload, $nalu];
        }
    }

    /**
     * Splits H.264 bitstream into NAL units
     *
     * @param string $buf Encoded bitstream
     * @return Generator<mixed, string> NAL units
     */
    public static function splitBitstream(string $buf): Generator
    {
        $i = 0;

        while (true) {
            $i = strpos($buf, "\x00\x00\x01", $i);
            if ($i === false) {
                return;
            }

            $i += 3;
            $nalStart = $i;

            $i = strpos($buf, "\x00\x00\x01", $i);
            if ($i === false) {
                yield substr($buf, $nalStart);
                return;
            }

            if ($buf[$i - 1] === "\x00") {
                yield substr($buf, $nalStart, $i - $nalStart - 1);
            } else {
                yield substr($buf, $nalStart, $i - $nalStart);
            }
        }
    }

    /**
     * Encodes video frame to H.264
     *
     * @param VideoFrame $frame Input frame
     * @param bool $useKeyframe Force keyframe
     * @return Generator<mixed, string> Encoded NAL units
     */
    public function encodeFrame(VideoFrame $frame, bool $useKeyframe): Generator
    {
        if ($this->encoderContext && (
                $frame->getVideoFormat()->getWidth() !== $this->encoderContext->getWidth() ||
                $frame->getVideoFormat()->getHeight() !== $this->encoderContext->getHeight() ||
                abs($this->bitrate - $this->encoderContext->getBitrate()) / $this->encoderContext->getBitrate() > 0.1
            )) {
            $this->bufferData = "";
            $this->bufferPts = null;
            $this->encoderContext = null;
        }
        $frame->setPictureType($useKeyframe ? PictureType::I : PictureType::NONE);

        if ($this->encoderContext === null) {
            // Initialize codec context (throws if no H.264 encoder can be initialised)
            $this->encoderContext = $this->getContext($frame->getVideoFormat());
        }

        $dataToSend = "";

        $transCoder = new TransCoder($this->encoderContext);
        /** @var Packet[] $packets */
        $packets = $transCoder->encode($frame);

        foreach ($packets as $packet) {
            $packetBytes = $packet->getData();

            if ($this->codecBuffering) {
                if ($packet->getPts() === $this->bufferPts) {
                    $this->bufferData .= $packetBytes;
                } else {
                    $dataToSend .= $this->bufferData;
                    $this->bufferData = $packetBytes;
                    $this->bufferPts = $packet->getPts();
                }
            } else {
                $dataToSend .= $packetBytes;
            }
        }

        if (!empty($dataToSend)) {
            foreach (self::splitBitstream($dataToSend) as $nalUnit) {
                yield $nalUnit;
            }
        }
    }

    /**
     * Encodes frame and packetizes output
     *
     * @param FrameInterface $frame Input frame
     * @param bool $useKeyframe Force keyframe
     * @return array [packets, timestamp]
     */
    #[\Override]
    public function encode(FrameInterface $frame, bool $useKeyframe = false): array
    {
        if (!$frame instanceof VideoFrame) {
            throw new InvalidArgumentException("");
        }

        $this->ensureEncoder();
        $packets = $this->encodeFrame($frame, $useKeyframe);
        $timestamp = $this->convertTimebase($frame->getPts() ?? 0, $this->getTimebaseArray($frame->getTimeBase()), [1, 90000]);

        return [self::packetize($packets), $timestamp];
    }

    /**
     * Gets appropriate encoder context
     *
     * @param VideoFormat $format Video format
     * @return VideoContext Encoder context
     * @throws RuntimeException When none of the candidate encoders could be initialised.
     */
    public function getContext(VideoFormat $format): VideoContext
    {
        $lastError = null;
        foreach (self::DEFAULT_CODEC_NAMES as $codecName) {
            try {
                $this->codecBuffering = $codecName === "h264_omx";
                return $this->createContext($format, new Codec($codecName, "w"));
            } catch (Exception $e) {
                // Each candidate may legitimately be unavailable; remember why and try the next.
                $lastError = $e;
            }
        }
        // Every candidate failed: surface the last reason instead of returning null and letting
        // the caller blow up later with an opaque error on a null context.
        throw new RuntimeException(
            "Could not initialise any H.264 encoder (tried: " . implode(", ", self::DEFAULT_CODEC_NAMES) . ")",
            0,
            $lastError
        );
    }

    /**
     * Creates new encoder context
     *
     * @param VideoFormat $format Video format
     * @param Codec $codec Codec to use
     * @return VideoContext Encoder context
     */
    private function createContext(VideoFormat $format, Codec $codec): VideoContext
    {
        $videoContext = VideoContext::create($codec);
        \assert($videoContext instanceof VideoContext);
        $videoContext->setFormat($format);
        $videoContext->setBitRate($this->bitrate);
        $videoContext->setFramerate(30, 1);
        $videoContext->setTimeBase(1, 30);
        $videoContext->setOptions([
            "profile" => "baseline",
            "level" => "31",
            "tune" => "zerolatency",
        ]);
        $videoContext->open();

        return $videoContext;
    }

    /**
     * Packetizes encoded packet for RTP
     *
     * @param Packet|EncodedPacket $packet Encoded packet
     * @return array [packets, timestamp]
     */
    #[\Override]
    public function pack(Packet|EncodedPacket $packet): array
    {
        $packages = $this->splitBitstream($packet->getData());
        $timestamp = $packet instanceof EncodedPacket
            ? $packet->getTimestamp()
            : $this->convertTimebase($packet->getPts() ?? 0, $this->getTimebaseArray($packet->getTimeBase()), [1, 90000]);

        return [$this->packetize($packages), $timestamp];
    }
}

<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Codecs\Video\X264;

use Webrtc\Codecs\PayloadDescriptorInterface;
use Webrtc\Exception\InvalidArgumentException;
use Webrtc\Exception\NotImplementedException;

/**
 * H.264 Payload Descriptor Class
 *
 * Handles parsing and reconstruction of H.264 payloads in RTP packets, according to RFC 6184.
 * Supports three packetization modes: Single NAL Unit, Fragmentation Unit (FU-A), and
 * Single-Time Aggregation Packet (STAP-A).
 *
 * @package Webrtc\Codecs\Video\X264
 */
final class H264PayloadDescriptor implements PayloadDescriptorInterface
{
    /**
     * @var int NAL_HEADER_SIZE Size of NAL unit header (1 byte)
     */
    private const NAL_HEADER_SIZE = 1;

    /**
     * @var int NAL_TYPE_FU_A Fragmentation Unit type value (28)
     */
    private const NAL_TYPE_FU_A = 28;

    /**
     * @var int NAL_TYPE_STAP_A Aggregation Packet type value (24)
     */
    private const NAL_TYPE_STAP_A = 24;

    /**
     * @var int LENGTH_FIELD_SIZE Size of length field in STAP-A (2 bytes)
     */
    private const LENGTH_FIELD_SIZE = 2;

    /**
     * Parses H.264 payload and reconstructs NAL units
     *
     * @param string $data RTP payload data
     * @return array [bool, string] Tuple containing:
     *               - Bool: Whether this is a start fragment
     *               - string: Reconstructed NAL unit(s) with start codes
     * @throws InvalidArgumentException For malformed packets
     */
    #[\Override]
    public static function decode(string $data): array
    {
        if (strlen($data) < 2) {
            throw new InvalidArgumentException("NAL unit is too short");
        }

        $nalType = ord($data[0]) & 0x1F;
        $fNri = ord($data[0]) & (0x80 | 0x60);

        return match (true) {
            $nalType >= 1 && $nalType <= 23 => self::handleSingleNalUnit($data),
            $nalType === self::NAL_TYPE_FU_A => self::handleFragmentationUnit($data, $fNri),
            $nalType === self::NAL_TYPE_STAP_A => self::handleStapA($data),
            default => throw new InvalidArgumentException("NAL unit type $nalType is not supported"),
        };

    }

    /**
     * Handles Single NAL Unit packets
     *
     * @param string $data Payload data
     * @return array [true, string] Always treats as complete unit
     */
    private static function handleSingleNalUnit(string $data): array
    {
        $output = chr(0) . chr(0) . chr(0) . chr(1) . $data;

        return [true, $output];
    }

    /**
     * Handles Fragmentation Unit (FU-A) packets
     *
     * @param string $data Payload data
     * @param int $fNri Forbidden and NRI bits
     * @return array [bool, string] Start fragment flag and reconstructed data
     */
    private static function handleFragmentationUnit(string $data, int $fNri): array
    {
        $pos = self::NAL_HEADER_SIZE;
        $originalNalType = ord($data[$pos]) & 0x1F;
        $firstFragment = (bool)(ord($data[$pos]) & 0x80);
        $pos++;

        $output = "";
        if ($firstFragment) {
            $originalNalHeader = chr($fNri | $originalNalType);
            $output .= chr(0) . chr(0) . chr(0) . chr(1) . $originalNalHeader;
        }
        $output .= substr($data, $pos);

        return [$firstFragment, $output];
    }

    /**
     * Handles Aggregation Packet (STAP-A)
     *
     * @param string $data Payload data
     * @return array [true, string] Always treats as complete units
     * @throws InvalidArgumentException For truncated packets
     */
    private static function handleStapA(string $data): array
    {
        $pos = self::NAL_HEADER_SIZE;
        $offsets = [];
        $output = "";

        while ($pos < strlen($data)) {
            if (strlen($data) < $pos + self::LENGTH_FIELD_SIZE) {
                throw new InvalidArgumentException("STAP-A length field is truncated");
            }

            $naluSize = unpack("n", substr($data, $pos, self::LENGTH_FIELD_SIZE));
            \assert($naluSize !== false);
            $naluSize = (int)$naluSize[1];
            $pos += self::LENGTH_FIELD_SIZE;
            $offsets[] = $pos;

            $pos += $naluSize;
            if (strlen($data) < $pos) {
                throw new InvalidArgumentException("STAP-A data is truncated");
            }
        }

        $offsets[] = strlen($data) + self::LENGTH_FIELD_SIZE;
        for ($i = 0; $i < count($offsets) - 1; $i++) {
            $start = $offsets[$i];
            $end = $offsets[$i + 1] - self::LENGTH_FIELD_SIZE;
            $output .= chr(0) . chr(0) . chr(0) . chr(1) . substr($data, $start, $end - $start);
        }

        return [true, $output];
    }

    /**
     * Not implemented - encoding not supported
     *
     * @throws NotImplementedException Always throws
     */
    #[\Override]
    public function encode(): string
    {
        throw new NotImplementedException("encoding not supported!");
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Codecs\Video\X264;

use Webrtc\AVCodec\AVCodec;
use Webrtc\AVCodec\Codec;
use Webrtc\AVCodec\Context\VideoContext;
use Webrtc\AVCodec\Data\Packet;
use Webrtc\AVCodec\Exception\AvCodecException;
use Webrtc\AVCodec\TransCoder;
use Webrtc\Codecs\DecoderInterface;
use Webrtc\Codecs\JitterFrameInterface;
use Webrtc\Exception\WebrtcExceptionInterface;

/**
 * H.264 Video Decoder Class
 *
 * Implements decoding of H.264/AVC video streams for WebRTC applications.
 * Provides efficient frame-by-frame decoding with proper timestamp handling.
 *
 * @package Webrtc\Codecs\Video\X264
 */
final class H264Decoder implements DecoderInterface
{
    /**
     * @var TransCoder $transcoder H.264 transcoder instance
     */
    private TransCoder $transcoder;

    /**
     * Constructor
     *
     * Initializes the H.264 decoder with:
     * - AVCodec library
     * - H.264 codec context in read mode
     * - Default decoder configuration
     * @throws AvCodecException
     */
    public function __construct()
    {
        AVCodec::init();
        $context = VideoContext::create(new Codec("h264"));
        \assert($context instanceof VideoContext);
        $this->transcoder = new TransCoder($context);
    }

    /**
     * Decodes an H.264 encoded frame
     *
     * @param JitterFrameInterface $frame Input frame containing:
     *                          - Data: Encoded H.264 payload
     *                          - timestamp: Presentation timestamp
     * @return array Array of decoded VideoFrame objects
     *               Empty array on decoding failure
     */
    #[\Override]
    public function decode(JitterFrameInterface $frame): array
    {
        try {
            $packet = new Packet();
            $packet->putData($frame->getData());
            $packet->setPts($frame->getTimestamp()); // Set presentation timestamp
            $packet->setTimeBase(1, 90000);   // Set time base

            return $this->transcoder->decode($packet);
        } catch (WebrtcExceptionInterface) {
            // A decode failure (malformed bitstream) legitimately yields no frames; catching only
            // library-domain exceptions lets genuine programming errors surface instead of being
            // silently swallowed as an empty decode on the hot path.
            return [];
        }
    }
}

<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Codecs\Video\Vp8;

use Webrtc\Codecs\PayloadDescriptorInterface;
use Webrtc\Exception\InvalidArgumentException;

/**
 * VP8 Payload Descriptor Class
 *
 * Handles the generation and parsing of VP8 payload descriptors for RTP packets.
 * Implements RFC 7741 for VP8 video payload header in RTP streams.
 *
 * @package Webrtc\Codecs\Video\Vp8
 */
final class Vp8PayloadDescriptor implements PayloadDescriptorInterface
{
    /**
     * @var int $partitionStart Indicates start of partition (1-bit)
     */
    private int $partitionStart;

    /**
     * @var int $partitionId Partition index (4-bit)
     */
    private int $partitionId;

    /**
     * @var int|null $pictureId Picture identifier (7 or 15-bit)
     */
    private ?int $pictureId;

    /**
     * @var int|null $tl0picidx Temporal layer zero index (8-bit)
     */
    private ?int $tl0picidx;

    /**
     * @var array{0: int, 1: int}|null $tid Temporal layer indices [Y, K] (2-bit + 1-bit)
     */
    private ?array $tid;

    /**
     * @var int|null $keyidx Key frame index (5-bit)
     */
    private ?int $keyidx;

    /**
     * Constructor
     *
     * @param int $partitionStart Partition start flag
     * @param int $partitionId Partition identifier
     * @param int|null $pictureId Optional picture ID
     * @param int|null $tl0picidx Optional TL0PICIDX
     * @param array{0: int, 1: int}|null $tid Optional temporal layer info
     * @param int|null $keyidx Optional keyframe index
     */
    public function __construct(
        int    $partitionStart,
        int    $partitionId,
        ?int   $pictureId = null,
        ?int   $tl0picidx = null,
        ?array $tid = null,
        ?int   $keyidx = null
    )
    {
        $this->partitionStart = $partitionStart;
        $this->partitionId = $partitionId;
        $this->pictureId = $pictureId;
        $this->tl0picidx = $tl0picidx;
        $this->tid = $tid;
        $this->keyidx = $keyidx;
    }

    /**
     * Gets partition start flag
     *
     * @return int Partition start (0 or 1)
     */
    public function getPartitionStart(): int
    {
        return $this->partitionStart;
    }

    /**
     * Gets partition ID
     *
     * @return int Partition identifier (0-15)
     */
    public function getPartitionId(): int
    {
        return $this->partitionId;
    }

    /**
     * Gets picture ID
     *
     * @return int|null Picture identifier or null
     */
    public function getPictureId(): ?int
    {
        return $this->pictureId;
    }

    /**
     * Gets TL0PICIDX
     *
     * @return int|null Temporal layer zero index or null
     */
    public function getTl0picidx(): ?int
    {
        return $this->tl0picidx;
    }

    /**
     * Gets temporal layer info
     *
     * @return array{0: int, 1: int}|null [Y, K] indices or null
     */
    public function getTid(): ?array
    {
        return $this->tid;
    }

    /**
     * Gets keyframe index
     *
     * @return int|null Keyframe index or null
     */
    public function getKeyidx(): ?int
    {
        return $this->keyidx;
    }

    /**
     * Encodes descriptor to binary string
     *
     * @return string Binary payload descriptor
     */
    #[\Override]
    public function encode(): string
    {
        $data = $this->encodeHeader();
        $data .= $this->encodeExtendedFields();
        return $data;
    }

    /**
     * Encode Header
     *
     * @return string
     */
    private function encodeHeader(): string
    {
        $octet = ($this->partitionStart << 4) | $this->partitionId;
        $extOctet = $this->computeExtensionOctet();

        return $extOctet ? pack('C', (1 << 7) | $octet) . pack('C', $extOctet) : pack('C', $octet);
    }

    /**
     * @return int
     */
    private function computeExtensionOctet(): int
    {
        $extOctet = 0;
        if ($this->pictureId !== null) {
            $extOctet |= 1 << 7;
        }
        if ($this->tl0picidx !== null) {
            $extOctet |= 1 << 6;
        }
        if ($this->tid !== null) {
            $extOctet |= 1 << 5;
        }
        if ($this->keyidx !== null) {
            $extOctet |= 1 << 4;
        }
        return $extOctet;
    }

    /**
     * @return string
     */
    private function encodeExtendedFields(): string
    {
        $data = '';
        if ($this->pictureId !== null) {
            $data .= $this->encodePictureId();
        }
        if ($this->tl0picidx !== null) {
            $data .= pack('C', $this->tl0picidx);
        }
        if ($this->tid !== null || $this->keyidx !== null) {
            $data .= $this->encodeTidKeyidx();
        }
        return $data;
    }

    /**
     * @return string
     */
    private function encodePictureId(): string
    {
        return $this->pictureId < 128 ? pack('C', $this->pictureId) : pack('n', (1 << 15) | $this->pictureId);
    }

    /**
     * @return string
     */
    private function encodeTidKeyidx(): string
    {
        $t_k = 0;
        if ($this->tid !== null) {
            $t_k |= ($this->tid[0] << 6) | ($this->tid[1] << 5);
        }
        if ($this->keyidx !== null) {
            $t_k |= $this->keyidx;
        }
        return pack('C', $t_k);
    }

    /**
     * Decodes descriptor from binary data
     *
     * @param string $data Binary input data
     * @return array [Vp8PayloadDescriptor, remaining_data]
     * @throws InvalidArgumentException On malformed input
     */
    #[\Override]
    public static function decode(string $data): array
    {
        if (strlen($data) < 1) {
            throw new InvalidArgumentException("VPX descriptor is too short");
        }

        $pos = 0;
        $octet = ord($data[$pos++]);
        $extended = $octet >> 7;
        $partitionStart = ($octet >> 4) & 1;
        $partitionId = $octet & 0xF;

        list($pictureId, $tl0picidx, $tid, $keyidx, $pos) = self::decodeExtendedFields($data, $pos, (bool)$extended);

        return [new self($partitionStart, $partitionId, $pictureId, $tl0picidx, $tid, $keyidx), substr($data, $pos)];
    }

    /**
     * @param string $data
     * @param int $pos
     * @param bool $extended
     * @return array{0: int|null, 1: int|null, 2: array{0: int, 1: int}|null, 3: int|null, 4: int}
     */
    private static function decodeExtendedFields(string $data, int $pos, bool $extended): array
    {
        $pictureId = $tl0picidx = $tid = $keyidx = null;
        if ($extended) {
            list($extI, $extL, $extT, $extK, $pos) = self::decodeExtensionOctet($data, $pos);
            if ($extI) {
                list($pictureId, $pos) = self::decodePictureId($data, $pos);
            }
            if ($extL) {
                if (strlen($data) < $pos + 1) {
                    throw new InvalidArgumentException("VPX descriptor has truncated TL0PICIDX");
                }
                $tl0picidx = ord($data[$pos++]);
            }
            if ($extT || $extK) {
                list($tid, $keyidx) = self::decodeTidKeyidx($data, $pos++, $extT, $extK);
            }
        }
        return [$pictureId, $tl0picidx, $tid, $keyidx, $pos];
    }

    /**
     * @param string $data
     * @param int $pos
     * @return array{0: int, 1: int, 2: int, 3: int, 4: int}
     */
    private static function decodeExtensionOctet(string $data, int $pos): array
    {
        if (strlen($data) < $pos + 1) {
            throw new InvalidArgumentException("VPX descriptor has truncated extended bits");
        }
        $octet = ord($data[$pos++]);
        $extI = ($octet >> 7) & 1;
        $extL = ($octet >> 6) & 1;
        $extT = ($octet >> 5) & 1;
        $extK = ($octet >> 4) & 1;

        return [$extI, $extL, $extT, $extK, $pos];
    }

    /**
     * @param string $data
     * @param int $pos
     * @return array{0: int, 1: int}
     */
    private static function decodePictureId(string $data, int $pos): array
    {
        if (strlen($data) < $pos + 1) {
            throw new InvalidArgumentException("VPX descriptor has truncated PictureID");
        }
        if (ord($data[$pos]) & 0x80) {
            if (strlen($data) < $pos + 2) {
                throw new InvalidArgumentException("VPX descriptor has truncated long PictureID");
            }
            $decoded = unpack('n', substr($data, $pos, 2));
            \assert($decoded !== false);
            $pictureId = (int)$decoded[1] & 0x7FFF;
            $pos += 2;
        } else {
            $pictureId = ord($data[$pos++]);
        }

        return [$pictureId, $pos];
    }

    /**
     * @param string $data
     * @param int $pos
     * @param int $extT
     * @param int $extK
     * @return array{0: array{0: int, 1: int}|null, 1: int|null}
     */
    private static function decodeTidKeyidx(string $data, int $pos, int $extT, int $extK): array
    {
        if (strlen($data) < $pos + 1) {
            throw new InvalidArgumentException("VPX descriptor has truncated T/K");
        }
        $t_k = ord($data[$pos++]);
        $keyidx = null;
        $tid = null;

        if ($extT) {
            $tid = [($t_k >> 6) & 3, ($t_k >> 5) & 1];
        }
        if ($extK) {
            $keyidx = $t_k & 0x1F;
        }

        return [$tid, $keyidx];
    }

    /**
     * String representation of descriptor
     *
     * @return string Human-readable descriptor info
     */
    public function __toString(): string
    {
        return sprintf(
            "VpxPayloadDescriptor(S=%d, PID=%d, pic_id=%s)",
            $this->partitionStart,
            $this->partitionId,
            $this->pictureId !== null ? $this->pictureId : 'null'
        );
    }

    /**
     * Sets partition start flag
     *
     * @param int $partitionStart New partition start value
     */
    public function setPartitionStart(int $partitionStart): void
    {
        $this->partitionStart = $partitionStart;
    }

    /**
     * Sets picture ID
     *
     * @param int|null $pictureId New picture ID
     */
    public function setPictureId(?int $pictureId): void
    {
        $this->pictureId = $pictureId;
    }
}

<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Codecs\Video\Vp8;

use Exception;
use Webrtc\AVCodec\AVCodec;
use Webrtc\AVCodec\Codec;
use Webrtc\AVCodec\Context\VideoContext;
use Webrtc\AVCodec\Data\Packet;
use Webrtc\AVCodec\Enum\PictureType;
use Webrtc\AVCodec\Exception\AvCodecException;
use Webrtc\AVCodec\Format\VideoFormat;
use Webrtc\AVCodec\Frame\FrameInterface;
use Webrtc\AVCodec\Frame\VideoFrame;
use Webrtc\AVCodec\SWScale;
use Webrtc\AVCodec\TransCoder;
use Webrtc\Codecs\CodecUtility;
use Webrtc\Codecs\EncodedPacket;
use Webrtc\Codecs\Encoder;
use Webrtc\Codecs\EncoderInterface;

/**
 * VP8 Video Encoder Class
 *
 * Implements real-time VP8 video encoding optimized for WebRTC applications,
 * backed by FFmpeg's libvpx encoder (via danog/php-rtc-av). Provides adaptive
 * bitrate control and RFC 7741 payload packetization for RTP transport.
 *
 * @package Webrtc\Codecs\Video\Vp8
 */
final class Vp8Encoder extends Encoder implements EncoderInterface
{
    /**
     * @var int VIDEO_CLOCK_RATE Standard video clock rate (90kHz)
     */
    private const VIDEO_CLOCK_RATE = 90000;

    /**
     * @var int MAX_FRAME_RATE Maximum supported frame rate (30fps)
     */
    private const MAX_FRAME_RATE = 30;

    /**
     * Maximum RTP payload size, chosen to stay inside a typical MTU.
     */
    private const PACKET_MAX = 1300;

    /**
     * @var int $pictureId Current picture identifier (15-bit)
     */
    private int $pictureId;

    /**
     * @var VideoContext|null $encoderContext Active encoder context
     */
    private ?VideoContext $encoderContext = null;

    /**
     * @var int $bitrate Current target bitrate (500kbps default)
     */
    protected int $bitrate = 500000;

    /**
     * Constructor
     *
     * Only the picture ID is needed to packetize already-encoded VP8 frames; libav is
     * loaded lazily, on the first real encode.
     */
    public function __construct()
    {
        $this->pictureId = rand(0, (1 << 15) - 1);
    }

    /**
     * Load libav on demand.
     *
     * @throws AvCodecException
     */
    private function ensureEncoder(): void
    {
        AVCodec::init();
        SWScale::init();
    }

    /**
     * Encodes a video frame to VP8 format
     *
     * @param FrameInterface|VideoFrame $frame Input video frame
     * @param bool $useKeyframe Force keyframe generation
     * @return array [payloads, timestamp] Encoded packets and presentation timestamp
     * @throws AvCodecException
     */
    #[\Override]
    public function encode(FrameInterface|VideoFrame $frame, bool $useKeyframe = false): array
    {
        $this->ensureEncoder();
        \assert($frame instanceof VideoFrame);

        if ($frame->getVideoFormat()->getName() !== "yuv420p") {
            $frame = $frame->reformat(format: "yuv420p");
        }

        if ($this->encoderContext !== null && (
                $frame->getVideoFormat()->getWidth() !== $this->encoderContext->getWidth() ||
                $frame->getVideoFormat()->getHeight() !== $this->encoderContext->getHeight() ||
                $this->bitrate !== $this->encoderContext->getBitrate()
            )) {
            $this->encoderContext = null;
        }

        $frame->setPictureType($useKeyframe ? PictureType::I : PictureType::NONE);

        if ($this->encoderContext === null) {
            $this->encoderContext = $this->createContext($frame->getVideoFormat());
        }

        $transCoder = new TransCoder($this->encoderContext);

        // Capture the timestamp before encoding: the transcoder rebases the frame's
        // time base to the context's during encode, which would corrupt the conversion.
        $timestamp = $this->getTimeBase($frame);

        $buffer = "";
        foreach ($transCoder->encode($frame) as $packet) {
            \assert($packet instanceof Packet);
            $buffer .= $packet->getData();
        }

        $payloads = $this->packetize($buffer, $this->pictureId);
        $this->pictureId = ($this->pictureId + 1) % (1 << 15);

        return [$payloads, $timestamp];
    }

    /**
     * Creates a configured VP8 encoder context
     *
     * @param VideoFormat $format Video format
     * @return VideoContext Encoder context
     * @throws AvCodecException
     */
    private function createContext(VideoFormat $format): VideoContext
    {
        $context = VideoContext::create(new Codec("libvpx", "w"));
        \assert($context instanceof VideoContext);
        $context->setFormat($format);
        $context->setBitRate($this->bitrate);
        $context->setFramerate(self::MAX_FRAME_RATE, 1);
        $context->setTimeBase(1, self::VIDEO_CLOCK_RATE);
        $context->setOptions([
            "deadline" => "realtime",
            "cpu-used" => "5",
            "threads" => (string)$this->calcCpuCore($format),
        ]);
        $context->open();

        return $context;
    }

    /**
     * Packages encoded data for RTP transport
     *
     * @param Packet|EncodedPacket $packet Encoded video packet
     * @return array [payloads, timestamp] Packets and converted timestamp
     */
    #[\Override]
    public function pack(Packet|EncodedPacket $packet): array
    {
        $payloads = $this->packetize($packet->getData(), $this->pictureId);
        $timestamp = $packet instanceof EncodedPacket
            ? $packet->getTimestamp()
            : $this->convertTimebase($packet->getPts() ?? 0, $this->getTimebaseArray($packet->getTimeBase()), [1, self::VIDEO_CLOCK_RATE]);
        $this->pictureId = ($this->pictureId + 1) % (1 << 15);
        return [$payloads, $timestamp];
    }

    /**
     * Packetizes encoded data with VP8 payload descriptors
     *
     * @param string $buffer Encoded frame data
     * @param int $pictureId Picture identifier
     * @return array Array of RTP payload packets
     */
    public function packetize(string $buffer, int $pictureId): array
    {
        $payloads = [];
        $descr = new Vp8PayloadDescriptor(1, 0);
        $descr->setPictureId($pictureId);

        $length = strlen($buffer);
        $pos = 0;

        while ($pos < $length) {
            $descrBytes = $descr->encode();
            $size = min($length - $pos, self::PACKET_MAX - strlen($descrBytes));
            $payloads[] = $descrBytes . substr($buffer, $pos, $size);
            $descr->setPartitionStart(0); // Update the descriptor's partition start flag
            $pos += $size;
        }

        return $payloads;
    }

    /**
     * Converts frame timestamp to 90kHz clock base
     *
     * @param VideoFrame $frame Input video frame
     * @return int Timestamp in 90kHz units
     */
    public function getTimeBase(VideoFrame $frame): int
    {
        return $this->convertTimebase($frame->getPts() ?? 0, $this->getTimebaseArray($frame->getTimeBase()), [1, self::VIDEO_CLOCK_RATE]);
    }

    /**
     * Calculates optimal thread count for encoding
     *
     * @param VideoFormat $format Input video format
     * @return int Number of threads to use
     */
    private function calcCpuCore(VideoFormat $format): int
    {
        $totalPixel = $format->getWidth() * $format->getHeight();
        $cpuCores = CodecUtility::getNumberOfCPUCores();

        return self::numberOfThreads($totalPixel, $cpuCores);
    }

    /**
     * Determines thread count based on resolution and CPU cores
     *
     * @param int $pixels Frame resolution (width Ã— height)
     * @param int $cpus Available CPU cores
     * @return int Recommended thread count
     */
    public static function numberOfThreads(int $pixels, int $cpus): int
    {
        if ($pixels >= 1920 * 1080 && $cpus > 8) {
            return 8;
        } elseif ($pixels > 1280 * 960 && $cpus >= 6) {
            return 3;
        } elseif ($pixels > 640 * 480 && $cpus >= 3) {
            return 2;
        } else {
            return 1;
        }
    }

    /**
     * Sets current picture identifier
     *
     * @param int $pictureId New picture ID (15-bit)
     */
    public function setPictureId(int $pictureId): void
    {
        $this->pictureId = $pictureId;
    }

    /**
     * Updates target bitrate
     *
     * @param int $bitrate New bitrate in bits per second
     */
    #[\Override]
    public function setBitrate(int $bitrate): void
    {
        parent::setBitrate($bitrate);
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Codecs\Video\Vp8;

use Webrtc\AVCodec\AVCodec;
use Webrtc\AVCodec\Codec;
use Webrtc\AVCodec\Context\VideoContext;
use Webrtc\AVCodec\Data\Packet;
use Webrtc\AVCodec\Exception\AvCodecException;
use Webrtc\AVCodec\TransCoder;
use Webrtc\Codecs\DecoderInterface;
use Webrtc\Codecs\JitterFrameInterface;
use Webrtc\Exception\WebrtcExceptionInterface;

/**
 * VP8 Video Decoder Class
 *
 * Implements real-time decoding of VP8-encoded video frames using FFmpeg's VP8
 * decoder (via danog/php-rtc-av). Provides efficient frame-by-frame decoding
 * suitable for WebRTC applications.
 *
 * @package Webrtc\Codecs\Video\Vp8
 */
final class Vp8Decoder implements DecoderInterface
{
    /**
     * @var TransCoder $transcoder VP8 transcoder instance
     */
    private TransCoder $transcoder;

    /**
     * Constructor
     *
     * Initializes the AVCodec library and a VP8 codec context in read mode.
     * @throws AvCodecException
     */
    public function __construct()
    {
        AVCodec::init();
        $context = VideoContext::create(new Codec("vp8"));
        \assert($context instanceof VideoContext);
        $this->transcoder = new TransCoder($context);
    }

    /**
     * Decodes a VP8-encoded video frame
     *
     * @param JitterFrameInterface $frame Input frame containing VP8 payload and timestamp
     * @return array Array of decoded VideoFrame objects, empty on decoding failure
     */
    #[\Override]
    public function decode(JitterFrameInterface $frame): array
    {
        try {
            $packet = new Packet();
            $packet->putData($frame->getData());
            $packet->setPts($frame->getTimestamp());
            $packet->setTimeBase(1, 90000);

            return $this->transcoder->decode($packet);
        } catch (WebrtcExceptionInterface) {
            // A decode failure (malformed bitstream) legitimately yields no frames; catching only
            // library-domain exceptions lets genuine programming errors surface instead of being
            // silently swallowed as an empty decode on the hot path.
            return [];
        }
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Codecs\Video\Vp9;

use Webrtc\AVCodec\Data\Packet;
use Webrtc\AVCodec\Frame\FrameInterface;
use Webrtc\Codecs\EncodedPacket;
use Webrtc\Codecs\Encoder;
use Webrtc\Exception\RuntimeException;

/**
 * VP9 Video Encoder Class
 *
 * Packetizes VP9 frames for RTP transport, following RFC 9628.
 *
 * Unlike {@see \Webrtc\Codecs\Video\Vp8\Vp8Encoder} this class does not drive libvpx:
 * it exists so that VP9 that is *already* encoded â€” the frames a WebM or MKV file
 * stores â€” can be sent without decoding and re-encoding it, which is by far the
 * common case and needs no native library at all. {@see self::encode()} is therefore
 * the only unsupported operation.
 *
 * @package Webrtc\Codecs\Video\Vp9
 */
final class Vp9Encoder extends Encoder
{
    /**
     * @var int Maximum RTP payload size, chosen to stay inside a typical MTU
     */
    private const PACKET_MAX = 1300;

    /**
     * @var int VIDEO_CLOCK_RATE Standard video clock rate (90kHz)
     */
    private const VIDEO_CLOCK_RATE = 90000;

    /**
     * @var int $pictureId Current picture identifier (15-bit)
     */
    private int $pictureId;

    /**
     * @var int $tl0PicIdx Running temporal-layer-zero index (8-bit)
     */
    private int $tl0PicIdx;

    /**
     * Constructor
     *
     * Picks a random initial picture ID and TL0PICIDX, as recommended for a fresh RTP stream.
     */
    public function __construct()
    {
        $this->pictureId = rand(0, (1 << 15) - 1);
        $this->tl0PicIdx = rand(0, 0xFF);
    }

    /**
     * Encodes a raw video frame to VP9
     *
     * Not implemented: nothing in the stack transcodes to VP9, and a half-configured
     * libvpx VP9 encoder would produce a stream that silently degrades rather than
     * failing, so this reports the limitation instead of guessing at settings.
     *
     * @param FrameInterface $frame Input video frame
     * @param bool $useKeyframe Force keyframe generation
     * @throws RuntimeException Always
     */
    #[\Override]
    public function encode(FrameInterface $frame, bool $useKeyframe = false): array
    {
        throw new RuntimeException(
            'Realtime VP9 encoding is not implemented; VP9 that is already encoded is packetized '
            . 'by pack() without any native library.'
        );
    }

    /**
     * Packages an already-encoded VP9 frame for RTP transport
     *
     * @param Packet|EncodedPacket $packet Encoded video packet
     * @return array [payloads, timestamp] Packets and converted timestamp
     */
    #[\Override]
    public function pack(Packet|EncodedPacket $packet): array
    {
        $keyframe = $packet instanceof EncodedPacket && $packet->isKeyframe();
        $payloads = $this->packetize($packet->getData(), $this->pictureId, $keyframe, $this->tl0PicIdx);
        $timestamp = $packet instanceof EncodedPacket
            ? $packet->getTimestamp()
            : $this->convertTimebase($packet->getPts() ?? 0, $this->getTimebaseArray($packet->getTimeBase()), [1, self::VIDEO_CLOCK_RATE]);
        $this->pictureId = ($this->pictureId + 1) % (1 << 15);
        // Every frame is a temporal-layer-zero frame in this single-layer stream, so the index
        // that anchors the non-flexible reference structure advances once per frame.
        $this->tl0PicIdx = ($this->tl0PicIdx + 1) & 0xFF;

        return [$payloads, $timestamp];
    }

    /**
     * Packetizes encoded data with VP9 payload descriptors
     *
     * A VP9 frame is an opaque byte string as far as RTP is concerned, so it is simply
     * split across packets; the B and E bits mark the first and last fragment, which is
     * what lets a receiver reassemble the frame.
     *
     * @param string $buffer Encoded frame data
     * @param int $pictureId Picture identifier
     * @param bool $keyframe Whether the frame is a keyframe
     * @param int $tl0PicIdx Temporal-layer-zero index for this frame
     * @return array Array of RTP payload packets
     */
    public function packetize(string $buffer, int $pictureId, bool $keyframe = false, int $tl0PicIdx = 0): array
    {
        $payloads = [];
        // WebRTC's VP9 reference finder demands more than the bare frame boundaries in
        // non-flexible mode: it drops any frame whose descriptor omits TL0PICIDX, and drops a
        // keyframe that carries no scalability structure (rtp_vp9_ref_finder.cc). So describe the
        // stream as a single spatial/temporal layer â€” layer indices with TID=SID=0 plus a running
        // TL0PICIDX on every packet â€” and attach a minimal SS to the first packet of each keyframe.
        // The P bit means "inter-picture predicted", so it is precisely the inverse of a keyframe:
        // a receiver uses it to find a decodable starting point.
        $descriptor = new Vp9PayloadDescriptor(
            startOfFrame: 1,
            endOfFrame: 0,
            interPicturePredicted: !$keyframe,
            pictureId: $pictureId,
            layerIndices: [0, 0, 0, 0],
            tl0picidx: $tl0PicIdx & 0xFF,
        );
        if ($keyframe) {
            // Minimal SS: N_S=0 (one spatial layer), Y=0 (no resolution signalled), G=0 (no GOF
            // description, so the receiver assumes a single temporal layer). One zero octet says all
            // of that; only the first packet of the keyframe carries it.
            $descriptor->setScalabilityStructure("\x00");
        }

        $length = strlen($buffer);
        $pos = 0;

        // An empty frame still deserves one packet, so that B and E are both signalled.
        do {
            $descriptorBytes = $descriptor->encode();
            $size = min($length - $pos, self::PACKET_MAX - strlen($descriptorBytes));
            $fragment = substr($buffer, $pos, $size);
            $pos += $size;

            $descriptor->setEndOfFrame($pos >= $length ? 1 : 0);
            // Re-encode once the end bit is known. The descriptor length may shrink after the first
            // packet (the SS is dropped), which the per-iteration size computation above accounts for.
            $payloads[] = $descriptor->encode() . $fragment;
            // B and the scalability structure belong to the first packet of the frame only.
            $descriptor->setStartOfFrame(0);
            $descriptor->setScalabilityStructure(null);
        } while ($pos < $length);

        return $payloads;
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Codecs\Video\Vp9;

use Webrtc\Codecs\PayloadDescriptorInterface;
use Webrtc\Exception\InvalidArgumentException;

/**
 * VP9 Payload Descriptor Class
 *
 * Handles the generation and parsing of VP9 payload descriptors for RTP packets.
 * Implements RFC 9628 (formerly draft-ietf-payload-vp9) for the VP9 video payload
 * header in RTP streams.
 *
 * The first octet is always present:
 *
 * ```
 *          0 1 2 3 4 5 6 7
 *         +-+-+-+-+-+-+-+-+
 *         |I|P|L|F|B|E|V|Z|
 *         +-+-+-+-+-+-+-+-+
 * ```
 *
 * I: PictureID present, P: inter-picture predicted, L: layer indices present,
 * F: flexible mode, B: start of frame, E: end of frame, V: scalability structure
 * present, Z: not a reference frame for upper spatial layers.
 *
 * Only the fields a single-layer stream needs are modelled: the scalability structure
 * (V) is parsed far enough to be skipped, since it only ever appears on keyframes of
 * streams that use spatial layers.
 *
 * @package Webrtc\Codecs\Video\Vp9
 */
final class Vp9PayloadDescriptor implements PayloadDescriptorInterface
{
    /**
     * @var int $startOfFrame Start of a frame (B bit)
     */
    private int $startOfFrame;

    /**
     * @var int $endOfFrame End of a frame (E bit)
     */
    private int $endOfFrame;

    /**
     * @var bool $interPicturePredicted Inter-picture predicted frame (P bit)
     */
    private bool $interPicturePredicted;

    /**
     * @var int|null $pictureId Picture identifier (7 or 15-bit)
     */
    private ?int $pictureId;

    /**
     * @var bool $nonReference Not a reference frame for upper spatial layers (Z bit)
     */
    private bool $nonReference;

    /**
     * @var array{0: int, 1: int, 2: int, 3: int}|null $layerIndices [temporalId, switchingUp, spatialId, interLayer] when present
     */
    private ?array $layerIndices;

    /**
     * @var int|null $tl0picidx Temporal layer zero index, non-flexible mode only
     */
    private ?int $tl0picidx;

    /**
     * @var string|null $scalabilityStructure Raw scalability structure (SS) bytes, when the V bit is set
     */
    private ?string $scalabilityStructure;

    /**
     * Constructor
     *
     * @param int $startOfFrame Whether this payload starts a frame
     * @param int $endOfFrame Whether this payload ends a frame
     * @param bool $interPicturePredicted Whether the frame is inter-picture predicted
     * @param int|null $pictureId Optional picture ID
     * @param bool $nonReference Whether upper spatial layers do not reference this frame
     * @param array{0: int, 1: int, 2: int, 3: int}|null $layerIndices Optional layer indices
     * @param int|null $tl0picidx Optional TL0PICIDX, non-flexible mode only
     * @param string|null $scalabilityStructure Optional raw scalability structure bytes (sets the V bit)
     */
    public function __construct(
        int    $startOfFrame,
        int    $endOfFrame,
        bool   $interPicturePredicted = false,
        ?int   $pictureId = null,
        bool   $nonReference = false,
        ?array $layerIndices = null,
        ?int   $tl0picidx = null,
        ?string $scalabilityStructure = null
    )
    {
        $this->startOfFrame = $startOfFrame;
        $this->endOfFrame = $endOfFrame;
        $this->interPicturePredicted = $interPicturePredicted;
        $this->pictureId = $pictureId;
        $this->nonReference = $nonReference;
        $this->layerIndices = $layerIndices;
        $this->tl0picidx = $tl0picidx;
        $this->scalabilityStructure = $scalabilityStructure;
    }

    /**
     * Whether this payload starts a frame
     *
     * @return int Start of frame flag (0 or 1)
     */
    public function getStartOfFrame(): int
    {
        return $this->startOfFrame;
    }

    /**
     * Whether this payload ends a frame
     *
     * @return int End of frame flag (0 or 1)
     */
    public function getEndOfFrame(): int
    {
        return $this->endOfFrame;
    }

    /**
     * Whether the frame is inter-picture predicted
     *
     * A keyframe is exactly a frame with this flag cleared, which is how a receiver
     * detects one without parsing the bitstream.
     *
     * @return bool True when the frame depends on a previous one
     */
    public function isInterPicturePredicted(): bool
    {
        return $this->interPicturePredicted;
    }

    /**
     * Gets picture ID
     *
     * @return int|null Picture identifier or null
     */
    public function getPictureId(): ?int
    {
        return $this->pictureId;
    }

    /**
     * Whether upper spatial layers do not reference this frame
     *
     * @return bool True when the frame may be discarded by upper layers
     */
    public function isNonReference(): bool
    {
        return $this->nonReference;
    }

    /**
     * Gets layer indices
     *
     * @return array{0: int, 1: int, 2: int, 3: int}|null [temporalId, switchingUp, spatialId, interLayer] or null
     */
    public function getLayerIndices(): ?array
    {
        return $this->layerIndices;
    }

    /**
     * Gets TL0PICIDX
     *
     * @return int|null Temporal layer zero index or null
     */
    public function getTl0picidx(): ?int
    {
        return $this->tl0picidx;
    }

    /**
     * Sets the start of frame flag
     *
     * @param int $startOfFrame New start of frame value
     */
    public function setStartOfFrame(int $startOfFrame): void
    {
        $this->startOfFrame = $startOfFrame;
    }

    /**
     * Sets the end of frame flag
     *
     * @param int $endOfFrame New end of frame value
     */
    public function setEndOfFrame(int $endOfFrame): void
    {
        $this->endOfFrame = $endOfFrame;
    }

    /**
     * Sets whether the frame is inter-picture predicted
     *
     * @param bool $interPicturePredicted New inter-picture predicted value
     */
    public function setInterPicturePredicted(bool $interPicturePredicted): void
    {
        $this->interPicturePredicted = $interPicturePredicted;
    }

    /**
     * Sets picture ID
     *
     * @param int|null $pictureId New picture ID
     */
    public function setPictureId(?int $pictureId): void
    {
        $this->pictureId = $pictureId;
    }

    /**
     * Sets the scalability structure (SS)
     *
     * Passing raw SS bytes sets the V bit and appends them to the descriptor; null clears it.
     *
     * @param string|null $scalabilityStructure New scalability structure bytes, or null
     */
    public function setScalabilityStructure(?string $scalabilityStructure): void
    {
        $this->scalabilityStructure = $scalabilityStructure;
    }

    /**
     * Encodes descriptor to binary string
     *
     * @return string Binary payload descriptor
     */
    #[\Override]
    public function encode(): string
    {
        $octet = 0;
        if ($this->pictureId !== null) {
            $octet |= 1 << 7;
        }
        if ($this->interPicturePredicted) {
            $octet |= 1 << 6;
        }
        if ($this->layerIndices !== null) {
            $octet |= 1 << 5;
        }
        // The F bit stays clear: we never signal flexible mode, so a reference index
        // list is never appended and TL0PICIDX describes the temporal structure.
        if ($this->startOfFrame) {
            $octet |= 1 << 3;
        }
        if ($this->endOfFrame) {
            $octet |= 1 << 2;
        }
        // The V bit announces a scalability structure appended after the layer indices.
        if ($this->scalabilityStructure !== null) {
            $octet |= 1 << 1;
        }
        if ($this->nonReference) {
            $octet |= 1;
        }

        $data = pack('C', $octet);
        if ($this->pictureId !== null) {
            $data .= $this->encodePictureId();
        }
        if ($this->layerIndices !== null) {
            $data .= $this->encodeLayerIndices();
        }
        if ($this->scalabilityStructure !== null) {
            $data .= $this->scalabilityStructure;
        }

        return $data;
    }

    /**
     * Encodes the picture ID, using the long form above 127
     *
     * @return string Binary picture ID
     */
    private function encodePictureId(): string
    {
        return $this->pictureId < 128
            ? pack('C', $this->pictureId)
            : pack('n', (1 << 15) | $this->pictureId);
    }

    /**
     * Encodes the layer indices, plus TL0PICIDX in non-flexible mode
     *
     * @return string Binary layer indices
     */
    private function encodeLayerIndices(): string
    {
        \assert($this->layerIndices !== null);
        [$temporalId, $switchingUp, $spatialId, $interLayer] = $this->layerIndices;
        $data = pack(
            'C',
            (($temporalId & 7) << 5) | (($switchingUp & 1) << 4) | (($spatialId & 7) << 1) | ($interLayer & 1)
        );

        // The F bit is always clear, so TL0PICIDX is mandatory whenever L is set.
        return $data . pack('C', $this->tl0picidx ?? 0);
    }

    /**
     * Decodes descriptor from binary data
     *
     * @param string $data Binary input data
     * @return array{0: Vp9PayloadDescriptor, 1: string} [Vp9PayloadDescriptor, remaining_data]
     * @throws InvalidArgumentException On malformed input
     */
    #[\Override]
    public static function decode(string $data): array
    {
        if (strlen($data) < 1) {
            throw new InvalidArgumentException("VP9 descriptor is too short");
        }

        $pos = 0;
        $octet = ord($data[$pos++]);
        $hasPictureId = (bool)($octet >> 7 & 1);
        $interPicturePredicted = (bool)($octet >> 6 & 1);
        $hasLayerIndices = (bool)($octet >> 5 & 1);
        $flexibleMode = (bool)($octet >> 4 & 1);
        $startOfFrame = $octet >> 3 & 1;
        $endOfFrame = $octet >> 2 & 1;
        $hasScalabilityStructure = (bool)($octet >> 1 & 1);
        $nonReference = (bool)($octet & 1);

        $pictureId = null;
        if ($hasPictureId) {
            [$pictureId, $pos] = self::decodePictureId($data, $pos);
        }

        $layerIndices = null;
        $tl0picidx = null;
        if ($hasLayerIndices) {
            [$layerIndices, $tl0picidx, $pos] = self::decodeLayerIndices($data, $pos, $flexibleMode);
        }

        if ($flexibleMode && $interPicturePredicted) {
            $pos = self::skipReferenceIndices($data, $pos);
        }

        if ($hasScalabilityStructure) {
            $pos = self::skipScalabilityStructure($data, $pos);
        }

        if (strlen($data) < $pos) {
            throw new InvalidArgumentException("VP9 descriptor is truncated");
        }

        return [
            new self(
                $startOfFrame,
                $endOfFrame,
                $interPicturePredicted,
                $pictureId,
                $nonReference,
                $layerIndices,
                $tl0picidx
            ),
            substr($data, $pos),
        ];
    }

    /**
     * Decodes the 7 or 15-bit picture ID
     *
     * @param string $data Binary input data
     * @param int $pos Current offset
     * @return array{0: int, 1: int} [pictureId, newPos]
     * @throws InvalidArgumentException On truncated input
     */
    private static function decodePictureId(string $data, int $pos): array
    {
        if (strlen($data) < $pos + 1) {
            throw new InvalidArgumentException("VP9 descriptor has truncated PictureID");
        }
        if (ord($data[$pos]) & 0x80) {
            if (strlen($data) < $pos + 2) {
                throw new InvalidArgumentException("VP9 descriptor has truncated long PictureID");
            }
            $decoded = unpack('n', substr($data, $pos, 2));
            \assert($decoded !== false);
            return [((int)$decoded[1]) & 0x7FFF, $pos + 2];
        }

        return [ord($data[$pos]), $pos + 1];
    }

    /**
     * Decodes the layer indices octet, and TL0PICIDX in non-flexible mode
     *
     * @param string $data Binary input data
     * @param int $pos Current offset
     * @param bool $flexibleMode Whether the F bit is set
     * @return array{0: array{0: int, 1: int, 2: int, 3: int}, 1: int|null, 2: int} [layerIndices, tl0picidx, newPos]
     * @throws InvalidArgumentException On truncated input
     */
    private static function decodeLayerIndices(string $data, int $pos, bool $flexibleMode): array
    {
        if (strlen($data) < $pos + 1) {
            throw new InvalidArgumentException("VP9 descriptor has truncated layer indices");
        }
        $octet = ord($data[$pos++]);
        $layerIndices = [$octet >> 5 & 7, $octet >> 4 & 1, $octet >> 1 & 7, $octet & 1];

        $tl0picidx = null;
        if (!$flexibleMode) {
            if (strlen($data) < $pos + 1) {
                throw new InvalidArgumentException("VP9 descriptor has truncated TL0PICIDX");
            }
            $tl0picidx = ord($data[$pos++]);
        }

        return [$layerIndices, $tl0picidx, $pos];
    }

    /**
     * Skips the reference index list of a flexible mode payload
     *
     * @param string $data Binary input data
     * @param int $pos Current offset
     * @return int Offset past the list
     * @throws InvalidArgumentException On truncated input
     */
    private static function skipReferenceIndices(string $data, int $pos): int
    {
        // Up to three P_DIFF fields, each chained by its own least significant "more" bit.
        for ($i = 0; $i < 3; $i++) {
            if (strlen($data) < $pos + 1) {
                throw new InvalidArgumentException("VP9 descriptor has truncated reference indices");
            }
            $octet = ord($data[$pos++]);
            if (!($octet & 1)) {
                break;
            }
        }

        return $pos;
    }

    /**
     * Skips the scalability structure of a payload that carries one
     *
     * @param string $data Binary input data
     * @param int $pos Current offset
     * @return int Offset past the structure
     * @throws InvalidArgumentException On truncated input
     */
    private static function skipScalabilityStructure(string $data, int $pos): int
    {
        if (strlen($data) < $pos + 1) {
            throw new InvalidArgumentException("VP9 descriptor has truncated scalability structure");
        }
        $octet = ord($data[$pos++]);
        $spatialLayers = ($octet >> 5 & 7) + 1;
        $hasResolutions = (bool)($octet >> 4 & 1);
        $hasPictureGroup = (bool)($octet >> 3 & 1);

        if ($hasResolutions) {
            // Two 16-bit values, width and height, per spatial layer.
            $pos += 4 * $spatialLayers;
        }

        if ($hasPictureGroup) {
            if (strlen($data) < $pos + 1) {
                throw new InvalidArgumentException("VP9 descriptor has truncated picture group");
            }
            $pictureGroupSize = ord($data[$pos++]);
            for ($i = 0; $i < $pictureGroupSize; $i++) {
                if (strlen($data) < $pos + 1) {
                    throw new InvalidArgumentException("VP9 descriptor has truncated picture group entry");
                }
                $entry = ord($data[$pos++]);
                // Each entry is followed by one octet per reference it declares.
                $pos += $entry >> 2 & 3;
            }
        }

        return $pos;
    }

    /**
     * String representation of descriptor
     *
     * @return string Human-readable descriptor info
     */
    public function __toString(): string
    {
        return sprintf(
            "Vp9PayloadDescriptor(B=%d, E=%d, P=%d, pic_id=%s)",
            $this->startOfFrame,
            $this->endOfFrame,
            $this->interPicturePredicted ? 1 : 0,
            $this->pictureId !== null ? $this->pictureId : 'null'
        );
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Codecs\Video\Vp9;

use Webrtc\AVCodec\AVCodec;
use Webrtc\AVCodec\Codec;
use Webrtc\AVCodec\Context\VideoContext;
use Webrtc\AVCodec\Data\Packet;
use Webrtc\AVCodec\Exception\AvCodecException;
use Webrtc\AVCodec\TransCoder;
use Webrtc\Codecs\DecoderInterface;
use Webrtc\Codecs\JitterFrameInterface;
use Webrtc\Exception\WebrtcExceptionInterface;

/**
 * VP9 Video Decoder Class
 *
 * Implements real-time decoding of VP9-encoded video frames using FFmpeg's VP9
 * decoder (via danog/php-rtc-av). Differs from
 * {@see \Webrtc\Codecs\Video\Vp8\Vp8Decoder} only in the codec it opens.
 *
 * @package Webrtc\Codecs\Video\Vp9
 */
final class Vp9Decoder implements DecoderInterface
{
    /**
     * @var TransCoder $transcoder VP9 transcoder instance
     */
    private TransCoder $transcoder;

    /**
     * Constructor
     *
     * Initializes the AVCodec library and a VP9 codec context in read mode.
     * @throws AvCodecException
     */
    public function __construct()
    {
        AVCodec::init();
        $context = VideoContext::create(new Codec("vp9"));
        \assert($context instanceof VideoContext);
        $this->transcoder = new TransCoder($context);
    }

    /**
     * Decodes a VP9-encoded video frame
     *
     * @param JitterFrameInterface $frame Input frame containing VP9 payload and timestamp
     * @return array Array of decoded VideoFrame objects, empty on decoding failure
     */
    #[\Override]
    public function decode(JitterFrameInterface $frame): array
    {
        try {
            $packet = new Packet();
            $packet->putData($frame->getData());
            $packet->setPts($frame->getTimestamp());
            $packet->setTimeBase(1, 90000);

            return $this->transcoder->decode($packet);
        } catch (WebrtcExceptionInterface) {
            // A decode failure (malformed bitstream) legitimately yields no frames; catching only
            // library-domain exceptions lets genuine programming errors surface instead of being
            // silently swallowed as an empty decode on the hot path.
            return [];
        }
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Codecs\Video\Av1;

use Webrtc\Codecs\PayloadDescriptorInterface;
use Webrtc\Exception\InvalidArgumentException;
use Webrtc\Exception\NotImplementedException;

/**
 * AV1 payload descriptor, per the AV1 RTP payload format specification
 * (https://aomediacodec.github.io/av1-rtp-spec/).
 *
 * An RTP packet carries an aggregation header (Z|Y|W|N) followed by OBU elements, each either
 * prefixed with its leb128 size or, for the last one when W is set, running to the end of the
 * packet. An OBU may be fragmented over several packets (the Z and Y bits), so a single packet
 * cannot be turned back into whole OBUs on its own.
 *
 * {@see self::decode()} therefore emits an intermediate representation â€” each element as a
 * continuation flag byte, its leb128 size and its bytes â€” which concatenates across the packets of
 * a temporal unit, and {@see self::assemble()} turns that back into a plain temporal unit of OBUs
 * in the low-overhead format (every OBU carrying its size field), as Matroska/MP4 and decoders
 * expect.
 *
 * @package Webrtc\Codecs\Video\Av1
 */
final class Av1PayloadDescriptor implements PayloadDescriptorInterface
{
    private const OBU_TEMPORAL_DELIMITER = 2;
    private const OBU_HAS_EXTENSION = 0x04;
    private const OBU_HAS_SIZE_FIELD = 0x02;
    /** Marker of an element that starts a new OBU in the intermediate representation. */
    private const ELEMENT_START = "\x00";
    /** Marker of an element that continues the previous OBU. */
    private const ELEMENT_CONTINUATION = "\x01";

    /**
     * Parse one AV1 RTP payload into the intermediate element list.
     *
     * @param string $data RTP payload data
     * @return array{bool, string} Whether the packet starts a new OBU (Z bit clear), and the
     *                             elements in the intermediate representation.
     * @throws InvalidArgumentException For malformed packets
     */
    #[\Override]
    public static function decode(string $data): array
    {
        $length = strlen($data);
        if ($length < 1) {
            throw new InvalidArgumentException("AV1 payload is too short");
        }
        $aggregationHeader = ord($data[0]);
        $continues = (bool)($aggregationHeader & 0x80); // Z: the first element continues an OBU.
        $count = ($aggregationHeader >> 4) & 0x03; // W: the number of elements, 0 = sized.

        $output = "";
        $pos = 1;
        $index = 0;
        while ($pos < $length) {
            if ($count !== 0 && $index === $count - 1) {
                $size = $length - $pos;
            } else {
                $size = self::readLeb128($data, $pos);
            }
            if ($size < 0 || $pos + $size > $length) {
                throw new InvalidArgumentException("AV1 OBU element is truncated");
            }
            $output .= ($index === 0 && $continues ? self::ELEMENT_CONTINUATION : self::ELEMENT_START)
                . self::writeLeb128($size)
                . substr($data, $pos, $size);
            $pos += $size;
            $index++;
        }
        if ($count !== 0 && $index !== $count) {
            throw new InvalidArgumentException("AV1 payload announces $count OBU elements but carries $index");
        }
        return [!$continues, $output];
    }

    /**
     * Turn the concatenated intermediate elements of a whole temporal unit back into OBUs with
     * size fields. Fragments are merged, and temporal delimiters (which the RTP format omits and
     * container formats do not store) are dropped should a sender include one.
     *
     * @param string $elements The concatenated output of {@see self::decode()} for every packet.
     * @return string The temporal unit in the low-overhead bitstream format.
     * @throws InvalidArgumentException For a malformed element list
     */
    public static function assemble(string $elements): string
    {
        $length = strlen($elements);
        $pos = 0;
        $obus = [];
        $current = null;
        while ($pos < $length) {
            $marker = $elements[$pos++];
            if ($pos >= $length) {
                throw new InvalidArgumentException("AV1 element list is truncated");
            }
            $size = self::readLeb128($elements, $pos);
            if ($pos + $size > $length) {
                throw new InvalidArgumentException("AV1 element list is truncated");
            }
            $fragment = substr($elements, $pos, $size);
            $pos += $size;
            if ($marker === self::ELEMENT_CONTINUATION && $current !== null) {
                $current .= $fragment;
                continue;
            }
            if ($current !== null) {
                $obus[] = $current;
            }
            $current = $fragment;
        }
        if ($current !== null) {
            $obus[] = $current;
        }

        $output = "";
        foreach ($obus as $obu) {
            $withSize = self::withSizeField($obu);
            if ($withSize !== null) {
                $output .= $withSize;
            }
        }
        return $output;
    }

    /**
     * Return an OBU with its size field set (adding it if the sender stripped it), or null for a
     * temporal delimiter or an OBU too short to carry a header.
     */
    private static function withSizeField(string $obu): ?string
    {
        if ($obu === '') {
            return null;
        }
        $header = ord($obu[0]);
        $type = ($header >> 3) & 0x0F;
        if ($type === self::OBU_TEMPORAL_DELIMITER) {
            return null;
        }
        $headerLength = 1 + (($header & self::OBU_HAS_EXTENSION) ? 1 : 0);
        if (strlen($obu) < $headerLength) {
            return null;
        }
        if ($header & self::OBU_HAS_SIZE_FIELD) {
            return $obu;
        }
        return chr($header | self::OBU_HAS_SIZE_FIELD)
            . substr($obu, 1, $headerLength - 1)
            . self::writeLeb128(strlen($obu) - $headerLength)
            . substr($obu, $headerLength);
    }

    private static function readLeb128(string $data, int &$offset): int
    {
        $value = 0;
        $shift = 0;
        $length = strlen($data);
        do {
            if ($offset >= $length) {
                throw new InvalidArgumentException("AV1 leb128 size is truncated");
            }
            $byte = ord($data[$offset++]);
            $value |= ($byte & 0x7F) << $shift;
            $shift += 7;
        } while (($byte & 0x80) && $shift < 56);
        return $value;
    }

    private static function writeLeb128(int $value): string
    {
        $out = '';
        do {
            $byte = $value & 0x7F;
            $value >>= 7;
            if ($value !== 0) {
                $byte |= 0x80;
            }
            $out .= chr($byte);
        } while ($value !== 0);
        return $out;
    }

    /**
     * Not implemented - encoding not supported
     *
     * @throws NotImplementedException Always throws
     */
    #[\Override]
    public function encode(): string
    {
        throw new NotImplementedException("encoding not supported!");
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Codecs\Video\Av1;

use Webrtc\AVCodec\Data\Packet;
use Webrtc\AVCodec\Frame\FrameInterface;
use Webrtc\Codecs\Encoder;
use Webrtc\Codecs\EncoderInterface;
use Webrtc\Codecs\EncodedPacket;
use Webrtc\Exception\RuntimeException;

/**
 * AV1 RTP payloader.
 *
 * Packetizes an already-encoded AV1 temporal unit into RTP payloads following the AV1 RTP
 * specification (https://aomediacodec.github.io/av1-rtp-spec/). Nothing is decoded or re-encoded:
 * the OBUs a Matroska/WebM file stores are re-framed for RTP and put on the wire as-is, so AV1
 * playback needs no codec library (and therefore no FFI), exactly like VP8/VP9/H.264.
 *
 * Only packing (sending pre-encoded frames) is implemented; realtime AV1 encoding from raw frames
 * is not, because MadelineProto only ever transmits pre-encoded media.
 *
 * @package Webrtc\Codecs\Video\Av1
 */
final class Av1Encoder extends Encoder implements EncoderInterface
{
    /** Every video codec on the wire uses a 90kHz RTP clock. */
    private const VIDEO_CLOCK_RATE = 90000;

    /** Maximum RTP payload size, leaving room for the RTP header within a typical MTU. */
    private const PACKET_MAX = 1200;

    /** OBU types we care about (see the AV1 bitstream specification, section 6.2.2). */
    private const OBU_SEQUENCE_HEADER = 1;
    private const OBU_TEMPORAL_DELIMITER = 2;

    /** Bit in the OBU header byte marking the presence of a trailing leb128 obu_size field. */
    private const OBU_HAS_SIZE_FIELD = 0x02;

    protected int $bitrate = 1000000;

    /**
     * Realtime AV1 encoding is intentionally unsupported: only pre-encoded frames are transmitted.
     */
    #[\Override]
    public function encode(FrameInterface $frame, bool $useKeyframe = false): string|array
    {
        throw new RuntimeException('Realtime AV1 encoding is not supported; play a pre-encoded AV1 WebM/MKV file instead.');
    }

    /**
     * Packetize one pre-encoded AV1 temporal unit into RTP payloads.
     *
     * @return array{0: list<string>, 1: int} [payloads, timestamp]
     */
    #[\Override]
    public function pack(Packet|EncodedPacket $packet): string|array
    {
        $timestamp = $packet instanceof EncodedPacket
            ? $packet->getTimestamp()
            : $this->convertTimebase($packet->getPts() ?? 0, $this->getTimebaseArray($packet->getTimeBase()), [1, self::VIDEO_CLOCK_RATE]);

        [$elements, $newCodedVideoSequence] = $this->toRtpObus($packet->getData());

        return [$this->packetize($elements, $newCodedVideoSequence), $timestamp];
    }

    /**
     * Split a temporal unit into RTP-ready OBU elements.
     *
     * Each element keeps its OBU header (and extension byte, if any) with the `obu_has_size_field`
     * bit cleared, followed by the OBU payload â€” the RTP framing carries the length instead. Temporal
     * delimiter OBUs are dropped, as the RTP timestamp already delimits temporal units.
     *
     * @return array{0: list<string>, 1: bool} [obu elements, whether a new coded video sequence starts]
     */
    private function toRtpObus(string $tu): array
    {
        $elements = [];
        $newCodedVideoSequence = false;
        $offset = 0;
        $length = \strlen($tu);

        while ($offset < $length) {
            $header = \ord($tu[$offset]);
            $type = ($header >> 3) & 0x0F;
            $hasExtension = (bool) (($header >> 2) & 1);
            $hasSize = (bool) (($header >> 1) & 1);
            $headerLength = 1 + ($hasExtension ? 1 : 0);

            $cursor = $offset + $headerLength;
            if ($hasSize) {
                $size = self::readLeb128($tu, $cursor);
            } else {
                // Without an explicit size the OBU runs to the end of the temporal unit.
                $size = $length - $cursor;
            }
            $payloadEnd = $cursor + $size;
            if ($size < 0 || $payloadEnd > $length) {
                break; // Malformed: stop rather than emit garbage.
            }

            if ($type !== self::OBU_TEMPORAL_DELIMITER) {
                if ($type === self::OBU_SEQUENCE_HEADER) {
                    $newCodedVideoSequence = true;
                }
                $rtpHeader = \chr($header & ~self::OBU_HAS_SIZE_FIELD);
                if ($hasExtension) {
                    $rtpHeader .= $tu[$offset + 1];
                }
                $elements[] = $rtpHeader . substr($tu, $cursor, $size);
            }

            $offset = $payloadEnd;
        }

        return [$elements, $newCodedVideoSequence];
    }

    /**
     * Pack OBU elements into RTP payloads, fragmenting any element too large for one packet.
     *
     * The aggregation header uses W=0, so every element is prefixed by its leb128 length; the Z and
     * Y bits flag an element that continues from the previous packet or into the next one, and the N
     * bit marks the first packet of a new coded video sequence.
     *
     * @param list<string> $elements
     * @return list<string>
     */
    private function packetize(array $elements, bool $newCodedVideoSequence): array
    {
        /** @var list<array{z: bool, y: bool, elems: list<string>}> $packets */
        $packets = [];
        /** @var array{z: bool, y: bool, elems: list<string>} $current */
        $current = ['z' => false, 'y' => false, 'elems' => []];
        $currentSize = 1; // The aggregation header.

        $flush = static function () use (&$packets, &$current, &$currentSize): void {
            if ($current['elems'] !== []) {
                $packets[] = $current;
            }
            $current = ['z' => false, 'y' => false, 'elems' => []];
            $currentSize = 1;
        };

        foreach ($elements as $obu) {
            $len = \strlen($obu);
            $pos = 0;
            $isStart = true;
            while ($pos < $len) {
                // Reserve up to 2 bytes for the element's leb128 length (enough for a full MTU).
                $available = self::PACKET_MAX - $currentSize - 2;
                if ($available < 1) {
                    $flush();
                    $available = self::PACKET_MAX - $currentSize - 2;
                }
                $chunk = min($available, $len - $pos);
                $fragment = substr($obu, $pos, $chunk);
                $pos += $chunk;

                if ($current['elems'] === []) {
                    // The first element of a packet that is a continuation sets the Z bit.
                    $current['z'] = !$isStart;
                }
                $current['elems'][] = $fragment;
                $currentSize += self::leb128Length(\strlen($fragment)) + \strlen($fragment);
                $isStart = false;

                if ($pos < $len) {
                    // The OBU continues in the next packet: this packet ends with a fragment.
                    $current['y'] = true;
                    $flush();
                }
            }
        }
        $flush();

        $payloads = [];
        $first = true;
        foreach ($packets as $packet) {
            $aggregationHeader = ($packet['z'] ? 0x80 : 0)
                | ($packet['y'] ? 0x40 : 0)
                // W = 0: each element is explicitly length-prefixed below.
                | (($newCodedVideoSequence && $first) ? 0x08 : 0);
            $buffer = \chr($aggregationHeader);
            foreach ($packet['elems'] as $element) {
                $buffer .= self::writeLeb128(\strlen($element)) . $element;
            }
            $payloads[] = $buffer;
            $first = false;
        }

        return $payloads;
    }

    /**
     * Read an unsigned leb128 value, advancing the cursor past it.
     */
    private static function readLeb128(string $data, int &$offset): int
    {
        $value = 0;
        $shift = 0;
        do {
            $byte = \ord($data[$offset++]);
            $value |= ($byte & 0x7F) << $shift;
            $shift += 7;
        } while (($byte & 0x80) && $shift < 56);
        return $value;
    }

    /**
     * Encode an unsigned integer as leb128.
     */
    private static function writeLeb128(int $value): string
    {
        $out = '';
        do {
            $byte = $value & 0x7F;
            $value >>= 7;
            if ($value !== 0) {
                $byte |= 0x80;
            }
            $out .= \chr($byte);
        } while ($value !== 0);
        return $out;
    }

    /**
     * Number of bytes the leb128 encoding of a value occupies.
     */
    private static function leb128Length(int $value): int
    {
        $n = 1;
        while ($value >= 0x80) {
            $value >>= 7;
            $n++;
        }
        return $n;
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Codecs\Video\H265;

use Webrtc\AVCodec\Data\Packet;
use Webrtc\AVCodec\Frame\FrameInterface;
use Webrtc\Codecs\EncodedPacket;
use Webrtc\Codecs\Encoder;
use Webrtc\Codecs\EncoderInterface;
use Webrtc\Codecs\Video\X264\H264Encoder;
use Webrtc\Exception\RuntimeException;

/**
 * H.265 (HEVC) RTP packetizer, per RFC 7798.
 *
 * Only pre-encoded bitstreams are supported: {@see self::pack()} splits an Annex B access unit into
 * NAL units and emits single NAL unit packets, aggregation packets (AP) for runs of small NAL units
 * (parameter sets and the like) and fragmentation units (FU) for NAL units larger than the MTU.
 * There is no realtime HEVC encoder.
 *
 * @package Webrtc\Codecs\Video\H265
 */
final class H265Encoder extends Encoder implements EncoderInterface
{
    private const VIDEO_CLOCK_RATE = 90000;
    /** Maximum RTP payload size. */
    private const PACKET_MAX = 1200;
    /** Payload header (2) + FU header (1). */
    private const FU_HEADER_SIZE = 3;
    private const LENGTH_FIELD_SIZE = 2;
    /** RFC 7798 recommends at most this many NAL units per AP. */
    private const AP_MAX_UNITS = 9;

    protected int $bitrate = 1000000;

    #[\Override]
    public function encode(FrameInterface $frame, bool $useKeyframe = false): string|array
    {
        throw new RuntimeException('Realtime H.265 encoding is not supported; play a pre-encoded HEVC MKV file instead.');
    }

    #[\Override]
    public function pack(Packet|EncodedPacket $packet): string|array
    {
        $timestamp = $packet instanceof EncodedPacket
            ? $packet->getTimestamp()
            : $this->convertTimebase($packet->getPts() ?? 0, $this->getTimebaseArray($packet->getTimeBase()), [1, self::VIDEO_CLOCK_RATE]);
        $nalUnits = [];
        foreach (H264Encoder::splitBitstream($packet->getData()) as $nalUnit) {
            if (strlen($nalUnit) >= H265PayloadDescriptor::NAL_HEADER_SIZE) {
                $nalUnits[] = $nalUnit;
            }
        }
        return [self::packetize($nalUnits), $timestamp];
    }

    /**
     * Packetize the NAL units of one access unit.
     *
     * @param list<string> $nalUnits NAL units without start codes, each with its 2-byte header.
     * @return list<string> RTP payloads.
     */
    public static function packetize(array $nalUnits): array
    {
        $payloads = [];
        $count = count($nalUnits);
        $i = 0;
        while ($i < $count) {
            $nalUnit = $nalUnits[$i];
            if (strlen($nalUnit) > self::PACKET_MAX) {
                $payloads = array_merge($payloads, self::fragment($nalUnit));
                $i++;
                continue;
            }
            // Aggregate a run of small NAL units into one AP, as long as they fit.
            $aggregated = [];
            $size = H265PayloadDescriptor::NAL_HEADER_SIZE;
            while ($i < $count
                && count($aggregated) < self::AP_MAX_UNITS
                && $size + self::LENGTH_FIELD_SIZE + strlen($nalUnits[$i]) <= self::PACKET_MAX
            ) {
                $aggregated[] = $nalUnits[$i];
                $size += self::LENGTH_FIELD_SIZE + strlen($nalUnits[$i]);
                $i++;
            }
            if (count($aggregated) === 1) {
                $payloads[] = $aggregated[0];
                continue;
            }
            $payloads[] = self::aggregate($aggregated);
        }
        return $payloads;
    }

    /**
     * Build an aggregation packet (type 48) out of several NAL units.
     *
     * @param list<string> $nalUnits
     */
    private static function aggregate(array $nalUnits): string
    {
        $forbidden = 0;
        $layerAndTid = ord($nalUnits[0][1]);
        foreach ($nalUnits as $nalUnit) {
            $forbidden |= ord($nalUnit[0]) & 0x80;
            // The AP carries the lowest TID of its units (the LayerId is 0 in a WebRTC stream).
            $layerAndTid = min($layerAndTid, ord($nalUnit[1]));
        }
        $payload = chr($forbidden | (H265PayloadDescriptor::NAL_TYPE_AP << 1)) . chr($layerAndTid);
        foreach ($nalUnits as $nalUnit) {
            $payload .= pack("n", strlen($nalUnit)) . $nalUnit;
        }
        return $payload;
    }

    /**
     * Split one NAL unit into fragmentation units (type 49).
     *
     * @return list<string>
     */
    private static function fragment(string $nalUnit): array
    {
        $header0 = ord($nalUnit[0]);
        $type = ($header0 >> 1) & 0x3F;
        $payloadHeader = chr(($header0 & 0x81) | (H265PayloadDescriptor::NAL_TYPE_FU << 1)) . $nalUnit[1];
        $data = substr($nalUnit, H265PayloadDescriptor::NAL_HEADER_SIZE);
        $length = strlen($data);
        $chunk = self::PACKET_MAX - self::FU_HEADER_SIZE;
        $packets = [];
        for ($offset = 0; $offset < $length; $offset += $chunk) {
            $fuHeader = $type;
            if ($offset === 0) {
                $fuHeader |= 0x80;
            }
            if ($offset + $chunk >= $length) {
                $fuHeader |= 0x40;
            }
            $packets[] = $payloadHeader . chr($fuHeader) . substr($data, $offset, $chunk);
        }
        return $packets;
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Codecs\Video\H265;

use Webrtc\Codecs\PayloadDescriptorInterface;
use Webrtc\Exception\InvalidArgumentException;
use Webrtc\Exception\NotImplementedException;

/**
 * H.265 (HEVC) payload descriptor.
 *
 * Parses the RTP payload formats of RFC 7798 back into Annex B NAL units: single NAL unit packets,
 * aggregation packets (AP, type 48) and fragmentation units (FU, type 49), without the optional
 * DONL/DOND fields (`sprop-max-don-diff` is 0, the only mode WebRTC uses). PACI packets (type 50)
 * are not supported.
 *
 * The output of consecutive packets of one access unit concatenates into the Annex B access unit.
 *
 * @package Webrtc\Codecs\Video\H265
 */
final class H265PayloadDescriptor implements PayloadDescriptorInterface
{
    /** Size of the HEVC NAL unit header: F(1) Type(6) LayerId(6) TID(3). */
    public const NAL_HEADER_SIZE = 2;
    /** Aggregation packet NAL unit type. */
    public const NAL_TYPE_AP = 48;
    /** Fragmentation unit NAL unit type. */
    public const NAL_TYPE_FU = 49;
    /** PACI packet NAL unit type (not supported). */
    public const NAL_TYPE_PACI = 50;
    /** Size of the NAL unit size field in an AP. */
    private const LENGTH_FIELD_SIZE = 2;
    private const START_CODE = "\x00\x00\x00\x01";

    /**
     * Parse an H.265 RTP payload into Annex B NAL unit(s).
     *
     * @param string $data RTP payload data
     * @return array{bool, string} Whether this is the start of a NAL unit (always true except for
     *                             a non-first fragment), and the reconstructed Annex B bytes.
     * @throws InvalidArgumentException For malformed packets
     */
    #[\Override]
    public static function decode(string $data): array
    {
        if (strlen($data) < self::NAL_HEADER_SIZE + 1) {
            throw new InvalidArgumentException("NAL unit is too short");
        }

        $nalType = self::nalType($data);

        return match (true) {
            $nalType < self::NAL_TYPE_AP => [true, self::START_CODE . $data],
            $nalType === self::NAL_TYPE_AP => self::handleAggregationPacket($data),
            $nalType === self::NAL_TYPE_FU => self::handleFragmentationUnit($data),
            default => throw new InvalidArgumentException("NAL unit type $nalType is not supported"),
        };
    }

    /**
     * The type of a NAL unit, from its two-byte header.
     */
    public static function nalType(string $nal): int
    {
        return (ord($nal[0]) >> 1) & 0x3F;
    }

    /**
     * @return array{bool, string}
     */
    private static function handleAggregationPacket(string $data): array
    {
        $pos = self::NAL_HEADER_SIZE;
        $length = strlen($data);
        $output = "";
        while ($pos < $length) {
            if ($length < $pos + self::LENGTH_FIELD_SIZE) {
                throw new InvalidArgumentException("AP length field is truncated");
            }
            $naluSize = unpack("n", substr($data, $pos, self::LENGTH_FIELD_SIZE));
            \assert($naluSize !== false);
            $naluSize = (int)$naluSize[1];
            $pos += self::LENGTH_FIELD_SIZE;
            if ($naluSize < self::NAL_HEADER_SIZE || $length < $pos + $naluSize) {
                throw new InvalidArgumentException("AP data is truncated");
            }
            $output .= self::START_CODE . substr($data, $pos, $naluSize);
            $pos += $naluSize;
        }
        return [true, $output];
    }

    /**
     * @return array{bool, string}
     */
    private static function handleFragmentationUnit(string $data): array
    {
        $fuHeader = ord($data[self::NAL_HEADER_SIZE]);
        $start = (bool)($fuHeader & 0x80);
        $fuType = $fuHeader & 0x3F;
        $payload = substr($data, self::NAL_HEADER_SIZE + 1);
        if (!$start) {
            return [false, $payload];
        }
        // Rebuild the original NAL unit header: keep the F bit and the LayerId/TID of the payload
        // header, replace the type.
        $header = chr((ord($data[0]) & 0x81) | ($fuType << 1)) . $data[1];
        return [true, self::START_CODE . $header . $payload];
    }

    /**
     * Not implemented - encoding not supported
     *
     * @throws NotImplementedException Always throws
     */
    #[\Override]
    public function encode(): string
    {
        throw new NotImplementedException("encoding not supported!");
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Codecs;

interface DecoderInterface
{
    public function decode(JitterFrameInterface $frame): array;
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Codecs;

use Exception;
use Webrtc\Codecs\Audio\Opus\OpusDecoder;
use Webrtc\Codecs\Audio\Opus\OpusEncoder;
use Webrtc\Codecs\Audio\PCM\PCMaDecoder;
use Webrtc\Codecs\Audio\PCM\PCMaEncoder;
use Webrtc\Codecs\Audio\PCM\PCMuDecoder;
use Webrtc\Codecs\Audio\PCM\PCMuEncoder;
use Webrtc\Codecs\Video\Av1\Av1Encoder;
use Webrtc\Codecs\Video\Av1\Av1PayloadDescriptor;
use Webrtc\Codecs\Video\H265\H265Encoder;
use Webrtc\Codecs\Video\H265\H265PayloadDescriptor;
use Webrtc\Codecs\Video\Vp8\Vp8Decoder;
use Webrtc\Codecs\Video\Vp8\Vp8Encoder;
use Webrtc\Codecs\Video\Vp8\Vp8PayloadDescriptor;
use Webrtc\Codecs\Video\Vp9\Vp9Decoder;
use Webrtc\Codecs\Video\Vp9\Vp9Encoder;
use Webrtc\Codecs\Video\Vp9\Vp9PayloadDescriptor;
use Webrtc\Codecs\Video\X264\H264Decoder;
use Webrtc\Codecs\Video\X264\H264Encoder;
use Webrtc\Codecs\Video\X264\H264PayloadDescriptor;
use Webrtc\Exception\InvalidArgumentException;
use Webrtc\RTPParameter\RTCRtcpFeedback;
use Webrtc\RTPParameter\RTCRtpCapabilities;
use Webrtc\RTPParameter\RTCRtpCodecCapability;
use Webrtc\RTPParameter\RTCRtpCodecParameters;
use Webrtc\RTPParameter\RTCRtpHeaderExtensionCapability;
use Webrtc\RTPParameter\RTCRtpHeaderExtensionParameters;

/**
 * WebRTC Codec Management Class
 *
 * Provides centralized codec and RTP capabilities management for WebRTC applications.
 * Handles audio/video codec registration, capability negotiation, and encoder/decoder
 * instantiation.
 *
 * @package Webrtc\Codecs
 */
final class Codec
{
    /**
     * @var RTCRtpCodecParameters[][] Registered codecs by media type
     */
    private array $codecs;

    /**
     * @var array{audio: RTCRtpHeaderExtensionParameters[], video: RTCRtpHeaderExtensionParameters[]} Header extensions by media type
     */
    private array $headerExtensions;

    /**
     * @var int Dynamic payload type counter
     */
    private int $dynamicPt = 97;

    /**
     * Constructor - initializes default codecs and header extensions
     */
    public function __construct()
    {
        $this->initializeDefaultCodecs();
        $this->initializeHeaderExtensions();
        $this->initCodecs();
    }

    /**
     * Initializes default audio codecs
     */
    private function initializeDefaultCodecs(): void
    {
        $this->codecs = [
            'audio' => [
                new RTCRtpCodecParameters('audio/opus', 48000, 2, 96),
                new RTCRtpCodecParameters('audio/PCMU', 8000, 1, 0),
                new RTCRtpCodecParameters('audio/PCMA', 8000, 1, 8),
            ],
            'video' => [],
        ];
    }

    /**
     * Initializes default header extensions
     */
    private function initializeHeaderExtensions(): void
    {
        $this->headerExtensions = [
            // The same ID for an extension of both kinds, as BUNDLE requires.
            'audio' => [
                new RTCRtpHeaderExtensionParameters(1, 'urn:ietf:params:rtp-hdrext:sdes:mid'),
                new RTCRtpHeaderExtensionParameters(2, 'urn:ietf:params:rtp-hdrext:ssrc-audio-level'),
                new RTCRtpHeaderExtensionParameters(4, 'http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01'),
            ],
            'video' => [
                new RTCRtpHeaderExtensionParameters(1, 'urn:ietf:params:rtp-hdrext:sdes:mid'),
                new RTCRtpHeaderExtensionParameters(3, 'http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time'),
                new RTCRtpHeaderExtensionParameters(4, 'http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01'),
            ],
        ];
    }

    /**
     * Adds video codec with RTX support
     *
     * @param string $mimeType Codec MIME type
     * @param array<string, int|string|null> $parameters Codec-specific parameters
     */
    private function addVideoCodec(string $mimeType, array $parameters = []): void
    {
        $clockRate = 90000;
        $this->codecs['video'][] = new RTCRtpCodecParameters(
            $mimeType,
            $clockRate,
            null,
            $this->dynamicPt,
            [
                new RTCRtcpFeedback('nack'),
                new RTCRtcpFeedback('nack', 'pli'),
                new RTCRtcpFeedback('goog-remb'),
            ],
            $parameters
        );

        $this->codecs['video'][] = new RTCRtpCodecParameters(
            'video/rtx',
            $clockRate,
            null,
            $this->dynamicPt + 1,
            [],
            ['apt' => $this->dynamicPt]
        );

        $this->dynamicPt += 2;
    }

    /**
     * Initializes all supported codecs
     */
    private function initCodecs(): void
    {
        // These fmtp parameters are the generic *fallback* capability set advertised before any
        // specific bitstream is known. When a pre-encoded file is transmitted, the caller should
        // derive the real parameters from the bitstream with {@see self::fmtpFromBitstream()} and
        // override these, so what is advertised matches what is actually sent.
        $this->addVideoCodec('video/VP8');
        // Profile 0 is the 8-bit 4:2:0 profile, the only one every VP9 decoder must support.
        $this->addVideoCodec('video/VP9', ['profile-id' => '0']);
        foreach (['42001f', '42e01f'] as $profileLevelId) {
            $this->addVideoCodec('video/H264', [
                'level-asymmetry-allowed' => '1',
                'packetization-mode' => '1',
                'profile-level-id' => $profileLevelId,
            ]);
        }
        // AV1 fallback: profile 0 (8-bit 4:2:0 Main), Main tier; level-idx 5 (=level 3.1) is a mid
        // default only used when the bitstream's real level is unknown.
        $this->addVideoCodec('video/AV1', ['profile' => '0', 'level-idx' => '5', 'tier' => '0']);
        // H.265 fallback: Main profile, Main tier, level 3.1 (level-id 93), single RTP stream â€”
        // the defaults libwebrtc assumes for an H.265 payload type that omits its parameters.
        $this->addVideoCodec('video/H265', ['level-id' => '93', 'profile-id' => '1', 'tier-flag' => '0', 'tx-mode' => 'SRST']);
    }

    /**
     * Derive the SDP `a=fmtp` parameters that describe an encoded video bitstream, so an endpoint can
     * advertise what it actually transmits instead of a hardcoded guess.
     *
     * The parameters are read from the codec's configuration record (the Matroska/MP4 CodecPrivate:
     * `av1C` for AV1, `avcC` for H.264, the WebM VP9 feature metadata for VP9). VP9 in WebM usually
     * stores no configuration record â€” its profile lives in every frame â€” so a keyframe may be passed
     * as a fallback source.
     *
     * @param string      $mimeType     Codec MIME type, e.g. `video/AV1`, `video/H264`, `video/VP9` (case-insensitive).
     * @param string      $codecPrivate The track configuration record, or `''` if none was stored.
     * @param string|null $keyframe     A keyframe of the stream, used when $codecPrivate is absent/insufficient.
     * @return array<string, string> fmtp overrides to merge onto the advertised parameters; `[]` if none could be derived.
     */
    public static function fmtpFromBitstream(string $mimeType, string $codecPrivate, ?string $keyframe = null): array
    {
        return match (strtolower($mimeType)) {
            'video/av1'  => self::av1FmtpFromBitstream($codecPrivate),
            'video/h264' => self::h264FmtpFromBitstream($codecPrivate),
            'video/h265' => self::h265FmtpFromBitstream($codecPrivate),
            'video/vp9'  => self::vp9FmtpFromBitstream($codecPrivate, $keyframe),
            default      => [],
        };
    }

    /**
     * Read `profile-id`, `tier-flag` and `level-id` from an HEVCDecoderConfigurationRecord (`hvcC`):
     * byte 1 is general_profile_space(2) general_tier_flag(1) general_profile_idc(5), and byte 12 is
     * general_level_idc â€” the same numbers RFC 7798 puts in the SDP.
     *
     * @return array<string, string>
     */
    private static function h265FmtpFromBitstream(string $hvcc): array
    {
        if (\strlen($hvcc) < 13 || \ord($hvcc[0]) !== 1) {
            return [];
        }
        $b1 = \ord($hvcc[1]);
        return [
            'level-id'   => (string) \ord($hvcc[12]),
            'profile-id' => (string) ($b1 & 0x1F),
            'tier-flag'  => (string) (($b1 >> 5) & 0x01),
            'tx-mode'    => 'SRST',
        ];
    }

    /**
     * Read `profile`/`level-idx`/`tier` from an AV1CodecConfigurationRecord (`av1C`).
     *
     * Layout (https://aomediacodec.github.io/av1-isobmff/#av1codecconfigurationrecord-syntax):
     * byte 0 = marker(1) | version(7); byte 1 = seq_profile(3) | seq_level_idx_0(5);
     * byte 2 = seq_tier_0(1) | high_bitdepth(1) | ...
     *
     * @return array<string, string>
     */
    private static function av1FmtpFromBitstream(string $av1c): array
    {
        if (\strlen($av1c) < 3 || (\ord($av1c[0]) & 0x80) === 0) {
            return [];
        }
        $b1 = \ord($av1c[1]);
        $b2 = \ord($av1c[2]);
        return [
            'profile'   => (string) (($b1 >> 5) & 0x07),
            'level-idx' => (string) ($b1 & 0x1F),
            'tier'      => (string) (($b2 >> 7) & 0x01),
        ];
    }

    /**
     * Read `profile-level-id` from an AVCDecoderConfigurationRecord (`avcC`): bytes 1-3 are
     * AVCProfileIndication, profile_compatibility and AVCLevelIndication â€” exactly the three bytes
     * of the SDP profile-level-id.
     *
     * @return array<string, string>
     */
    private static function h264FmtpFromBitstream(string $avcc): array
    {
        if (\strlen($avcc) < 4) {
            return [];
        }
        return [
            'level-asymmetry-allowed' => '1',
            'packetization-mode'      => '1',
            'profile-level-id'        => bin2hex($avcc[1].$avcc[2].$avcc[3]),
        ];
    }

    /**
     * Read the VP9 `profile-id`, from the WebM VP9 feature metadata (CodecPrivate) if present, else
     * from the uncompressed header of a keyframe.
     *
     * @return array<string, string>
     */
    private static function vp9FmtpFromBitstream(string $vpcc, ?string $keyframe): array
    {
        $profile = self::vp9ProfileFromMetadata($vpcc);
        if ($profile === null && $keyframe !== null) {
            $profile = self::vp9ProfileFromFrame($keyframe);
        }
        return $profile === null ? [] : ['profile-id' => (string) $profile];
    }

    /**
     * The WebM VP9 CodecPrivate is a sequence of {id byte, length byte, value} features; feature
     * id 1 is the profile.
     */
    private static function vp9ProfileFromMetadata(string $vpcc): ?int
    {
        $offset = 0;
        $length = \strlen($vpcc);
        while ($offset + 2 <= $length) {
            $id  = \ord($vpcc[$offset]);
            $len = \ord($vpcc[$offset + 1]);
            $offset += 2;
            if ($offset + $len > $length) {
                break;
            }
            if ($id === 1 && $len >= 1) {
                return \ord($vpcc[$offset]);
            }
            $offset += $len;
        }
        return null;
    }

    /**
     * The VP9 uncompressed header starts with frame_marker (2 bits = 0b10) then profile_low_bit and
     * profile_high_bit; profile = (high << 1) | low.
     */
    private static function vp9ProfileFromFrame(string $frame): ?int
    {
        if ($frame === '') {
            return null;
        }
        $b = \ord($frame[0]);
        if (($b >> 6) !== 0b10) {
            return null;
        }
        return ((($b >> 4) & 1) << 1) | (($b >> 5) & 1);
    }

    /**
     * Gets capabilities for specified media type
     *
     * @param string $kind Media type ('audio' or 'video')
     * @return RTCRtpCapabilities Codec and header extension capabilities
     * @throws InvalidArgumentException For unknown media types
     */
    public function getCapabilities(string $kind): RTCRtpCapabilities
    {
        if (!isset($this->codecs[$kind])) {
            throw new InvalidArgumentException("Cannot get capabilities for unknown media $kind");
        }

        $codecs = [];
        $headerExtensions = [];
        $rtxAdded = false;

        foreach ($this->codecs[$kind] as $codec) {
            if (CodecUtility::isRtx($codec)) {
                if (!$rtxAdded) {
                    $codecs[] = new RTCRtpCodecCapability(
                        $codec->mimeType,
                        $codec->clockRate
                    );
                    $rtxAdded = true;
                }
            } else {
                $codecs[] = new RTCRtpCodecCapability(
                    $codec->mimeType,
                    $codec->clockRate,
                    $codec->channels,
                    $codec->parameters
                );
            }
        }

foreach ($this->headerExtensions[$kind] as $extension) {
            $headerExtensions[] = new RTCRtpHeaderExtensionCapability($extension->uri);
        }

        return new RTCRtpCapabilities($codecs, $headerExtensions);
    }

    /**
     * Gets decoder instance for specified codec
     *
     * @param RTCRtpCodecParameters $codec Codec parameters
     * @return DecoderInterface Appropriate decoder instance
     * @throws InvalidArgumentException For unsupported codecs
     */
    public static function getDecoder(RTCRtpCodecParameters $codec): DecoderInterface
    {
        return match (strtolower($codec->mimeType)) {
            'audio/opus' => new OpusDecoder,
            'audio/pcma' => new PCMaDecoder,
            'audio/pcmu' => new PCMuDecoder,
            'video/h264' => new H264Decoder,
            'video/vp8'  => new Vp8Decoder,
            'video/vp9'  => new Vp9Decoder,
            default => throw new InvalidArgumentException("No decoder found for MIME type `$codec->mimeType`"),
        };
    }

    /**
     * Gets encoder instance for specified codec
     *
     * @param RTCRtpCodecParameters $codec Codec parameters
     * @return EncoderInterface Appropriate encoder instance
     * @throws InvalidArgumentException For unsupported codecs
     */
    public static function getEncoder(RTCRtpCodecParameters $codec): EncoderInterface
    {
        return match (strtolower($codec->mimeType)) {
            'audio/opus' => new OpusEncoder,
            'audio/pcma' => new PCMaEncoder,
            'audio/pcmu' => new PCMuEncoder,
            'video/h264' => new H264Encoder,
            'video/vp8'  => new Vp8Encoder,
            'video/vp9'  => new Vp9Encoder,
            'video/av1'  => new Av1Encoder,
            'video/h265' => new H265Encoder,
            default => throw new InvalidArgumentException("No encoder found for MIME type `$codec->mimeType`"),
        };
    }

    /**
     * Processes RTP payload according to codec requirements
     *
     * @param RTCRtpCodecParameters $codec Codec parameters
     * @param string $payload RTP payload data
     * @return array [bool, string] Processed payload data
     * @throws Exception For processing failures
     */
    public static function depayload(RTCRtpCodecParameters $codec, string $payload): array
    {
        return match (strtolower($codec->mimeType)) {
            "video/vp8" => Vp8PayloadDescriptor::decode($payload),
            "video/vp9" => Vp9PayloadDescriptor::decode($payload),
            "video/h264" => H264PayloadDescriptor::decode($payload),
            "video/h265" => H265PayloadDescriptor::decode($payload),
            "video/av1" => Av1PayloadDescriptor::decode($payload),
            default => [true, $payload],
        };
    }

    /**
     * Turn the concatenated depayloaded packets of one frame into the frame's encoded bitstream.
     *
     * For most codecs the depayloaded packets simply concatenate into the frame. AV1 is the
     * exception: an OBU may be fragmented across packets and the RTP format strips the OBU size
     * fields, so the frame has to be reassembled once all of its packets are in (see
     * {@see Av1PayloadDescriptor::assemble()}).
     *
     * @param RTCRtpCodecParameters $codec Codec parameters
     * @param string $frame The concatenated depayloaded packets of the frame
     * @return string The encoded frame
     */
    public static function finalizeFrame(RTCRtpCodecParameters $codec, string $frame): string
    {
        return match (strtolower($codec->mimeType)) {
            "video/av1" => Av1PayloadDescriptor::assemble($frame),
            default => $frame,
        };
    }

    /**
     * Gets registered codecs
     *
     * @param string|null $kind Optional media type filter
     * @return array Codecs array
     */
    public function getCodecs(?string $kind = null): array
    {
        return $kind !== null ? $this->codecs[$kind] : $this->codecs;
    }

    /**
     * Gets registered header extensions
     *
     * @param string|null $kind Optional media type filter
     * @return RTCRtpHeaderExtensionParameters[] Header extensions array
     */
    public function getHeaderExtensions(?string $kind = null): array
    {
        if ($kind !== null) {
            return $this->headerExtensions[$kind];
        }

        $headerExtensions = [];
        foreach ($this->headerExtensions as $extensions) {
            foreach ($extensions as $extension) {
                $headerExtensions[] = $extension;
            }
        }
        return $headerExtensions;
    }
}{
    "name": "danog/php-rtc-codecs",
    "type": "library",
    "description": "WebRTC audio/video codec and RTP packetization utilities. MadelineProto's PHP 8.2+ fork adds optional transcoding FFI, VP9 packetization, and the modular danog/php-rtc stack.",
    "keywords": [
        "webrtc",
        "codecs",
        "h264",
        "vp8",
        "opus",
        "pcma",
        "pcmu"
    ],
    "homepage": "https://github.com/danog/php-rtc-codecs",
    "license": "BSD-3-Clause",
    "authors": [
        {
            "name": "Amin Yazdanpanah",
            "email": "github@aminyazdanpanah.com"
        },
        {
            "name": "Sana Moniri",
            "email": "s.mo@quasarstream.com"
        },
        {
            "name": "Quasar Stream Community"
        },
        {
            "name": "Daniil Gentili",
            "email": "daniil@daniil.it"
        }
    ],
    "require": {
        "php": "^8.2",
        "danog/php-rtc-exception": "^1.1.5",
        "danog/php-rtc-av": "^1.1.3",
        "danog/php-rtc-rtp-parameter": "^1.1.4"
    },
    "require-dev": {
        "phpunit/phpunit": "^11.5 || ^12.0 || ^13.3.2",
        "bamarni/composer-bin-plugin": "^1.9.1"
    },
    "autoload": {
        "psr-4": {
            "Webrtc\\Codecs\\": "src"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\Webrtc\\Codecs\\": "tests"
        }
    },
    "minimum-stability": "stable",
    "suggest": {
        "ext-ffi": "Install the FFI extension and libopus/libvpx/libavcodec to transcode media; not required to packetize already-encoded media"
    },
    "scripts": {
        "post-install-cmd": [
            "@composer bin all install --ansi"
        ],
        "post-update-cmd": [
            "@composer bin all update --ansi"
        ]
    },
    "config": {
        "allow-plugins": {
            "bamarni/composer-bin-plugin": true
        }
    },
    "extra": {
        "bamarni-bin": {
            "bin-links": true,
            "forward-command": true
        }
    }
}
<?php

$config = new class extends Amp\CodeStyle\Config {
    public function getRules(): array
    {
        return array_merge(
            parent::getRules(),
            [
                'phpdoc_to_property_type' => true,
                'phpdoc_to_param_type' => true,
            ]
        );
    }  
};
$config->getFinder()
    ->in(__DIR__ . '/src')
    ->in(__DIR__ . '/tests');

$cacheDir = getenv('TRAVIS') ? getenv('HOME') . '/.php-cs-fixer' : __DIR__;

$config->setCacheFile($cacheDir . '/.php_cs.cache');

return $config;
<?php declare(strict_types=1);
/**
 * Decoded UniqueFileId class.
 *
 * This file is part of tg-file-decoder.
 * tg-file-decoder is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * tg-file-decoder is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with tg-file-decoder.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2019 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 *
 * @link https://github.com/tg-file-decoder Documentation
 */

namespace danog\Decoder;

use danog\Decoder\PhotoSizeSource\PhotoSizeSourceDialogPhoto;
use danog\Decoder\PhotoSizeSource\PhotoSizeSourceStickersetThumbnailVersion;
use danog\Decoder\PhotoSizeSource\PhotoSizeSourceThumbnail;

/**
 * Represents decoded unique bot API file ID.
 *
 * @api
 */
final class UniqueFileId
{
    /**
     * Basic constructor function.
     */
    public function __construct(
        /**
         * File type.
         *
         */
        public UniqueFileIdType $type,
        /**
         * File ID.
         *
         */
        public ?int $id = null,
        /**
         * Photo subtype.
         *
         */
        public ?int $subType = null,
        /**
         * Photo volume ID.
         *
         */
        public ?int $volumeId = null,
        /**
         * Photo local ID.
         *
         */
        public ?int $localId = null,
        /**
         * Sticker set ID.
         *
         */
        public ?int $stickerSetId = null,
        /**
         * Sticker set version.
         *
         */
        public ?int $stickerSetVersion = null,
        /**
         * Weblocation URL.
         *
         */
        public ?string $url= null,
    ) {
    }

    /**
     * Get unique bot API file ID.
     *
     */
    public function __toString(): string
    {
        return $this->getUniqueBotAPI();
    }

    /**
     * Get unique bot API file ID.
     *
     */
    public function getUniqueBotAPI(): string
    {
        $fileId = \pack('V', $this->type->value);
        if ($this->url !== null) {
            $fileId .= Tools::packTLString($this->url);
        } elseif ($this->type === UniqueFileIdType::PHOTO) {
            if ($this->volumeId !== null) {
                $fileId .= Tools::packLong($this->volumeId);
                $fileId .= \pack('l', $this->localId);
            } elseif ($this->stickerSetId !== null) {
                \assert($this->subType !== null);
                $fileId .= \chr($this->subType);
                $fileId .= Tools::packLong($this->stickerSetId);
                $fileId .= \pack('l', $this->stickerSetVersion);
            } else {
                \assert($this->subType !== null && $this->id !== null);
                $fileId .= Tools::packLong($this->id);
                $fileId .= \chr($this->subType);
            }
        } elseif ($this->id !== null) {
            $fileId .= Tools::packLong($this->id);
        }

        return Tools::base64urlEncode(Tools::rleEncode($fileId));
    }

    /**
     * Decode unique bot API file ID.
     *
     * @param string $fileId Bot API file ID
     *
     */
    public static function fromUniqueBotAPI(string $fileId): self
    {
        $orig = $fileId;
        $fileId = Tools::rleDecode(Tools::base64urlDecode($fileId));

        /** @var int */
        $typeId = \unpack('V', $fileId)[1];
        $type = UniqueFileIdType::from($typeId);
        $url = null;

        $subType = null;
        $id = null;
        $fileId = \substr($fileId, 4);
        $volume_id = null;
        $local_id = null;
        $sticker_set_id = null;
        $sticker_set_version = null;
        if ($type === UniqueFileIdType::WEB) {
            $res = \fopen('php://memory', 'rw+b');
            \assert($res !== false);
            \fwrite($res, $fileId);
            \fseek($res, 0);
            $fileId = $res;
            $url = Tools::readTLString($fileId);

            $l = \fstat($fileId)['size'] - \ftell($fileId);
            \trigger_error("Unique file ID $orig has $l bytes of leftover data");
        } elseif (\strlen($fileId) === 12) {
            // Legacy photos
            $volume_id = Tools::unpackLong(\substr($fileId, 0, 8));
            $local_id = Tools::unpackInt(\substr($fileId, 8));
        } elseif (\strlen($fileId) === 9) {
            // Dialog photos/thumbnails
            $id = Tools::unpackLong($fileId);
            $subType = \ord($fileId[8]);
        } elseif (\strlen($fileId) === 13) {
            // Stickerset ID/version
            $subType = \ord($fileId[0]);
            $sticker_set_id = Tools::unpackLong(\substr($fileId, 1, 8));
            $sticker_set_version = Tools::unpackInt(\substr($fileId, 9));
        } elseif (\strlen($fileId) === 8) {
            // Any other document
            $id = Tools::unpackLong($fileId);
        } else {
            $l = \strlen($fileId);
            \trigger_error("Unique file ID $orig has $l bytes of leftover data");
        }
        return new self(
            type: $type,
            id: $id,
            subType: $subType,
            volumeId: $volume_id,
            localId: $local_id,
            stickerSetId: $sticker_set_id,
            stickerSetVersion: $sticker_set_version,
            url: $url
        );
    }

    /**
     * Convert full bot API file ID to unique file ID.
     *
     * @param string $fileId Full bot API file ID
     *
     */
    public static function fromBotAPI(string $fileId): self
    {
        return FileId::fromBotAPI($fileId)->getUnique();
    }

    /**
     * Turn full file ID into unique file ID.
     *
     * @param FileId $fileId Full file ID
     *
     */
    public static function fromFileId(FileId $fileId): self
    {
        if ($fileId->url !== null) {
            return new self(
                UniqueFileIdType::WEB,
                url: $fileId->url
            );
        }
        $type = $fileId->type->toUnique();
        if ($type === UniqueFileIdType::PHOTO) {
            $photoSize = $fileId->photoSizeSource;
            $subType = null;
            if ($photoSize instanceof PhotoSizeSourceThumbnail) {
                $subType = \ord($photoSize->thumbType);
                if ($subType === 97) {
                    $subType = 0;
                } elseif ($subType === 99) {
                    $subType = 1;
                } else {
                    $subType = $subType+5;
                }
                $subType = $subType;
            } elseif ($photoSize instanceof PhotoSizeSourceDialogPhoto) {
                $subType = $photoSize->isSmallDialogPhoto() ? 0 : 1;
            } elseif ($photoSize instanceof PhotoSizeSourceStickersetThumbnailVersion) {
                return new self(
                    $type,
                    $fileId->id,
                    2,
                    stickerSetId: $photoSize->stickerSetId,
                    stickerSetVersion: $photoSize->stickerSetVersion,
                );
            }
            return new self(
                $type,
                $fileId->id,
                $subType,
                $fileId->volumeId,
                $fileId->localId,
            );
        }
        return new self($type, $fileId->id);
    }
}
<?php declare(strict_types=1);
/**
 * Type enum + helper functions.
 *
 * This file is part of tg-file-decoder.
 * tg-file-decoder is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * tg-file-decoder is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with tg-file-decoder.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2019 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 *
 * @link https://github.com/tg-file-decoder Documentation
 */

namespace danog\Decoder;

use AssertionError;

/**
 * Represents decoded bot API file ID type.
 *
 * @api
 */
enum FileIdType: string
{
    /**
     * Thumbnail.
     */
    case THUMBNAIL = 'thumbnail';
    /**
     * Profile photo.
     * Used for users and channels, chat photos are normal PHOTOs.
     */
    case PROFILE_PHOTO = 'profile_photo';
    /**
     * Normal photos.
     */
    case PHOTO = 'photo';

    /**
     * Voice messages.
     */
    case VOICE = 'voice';
    /**
     * Video.
     */
    case VIDEO = 'video';
    /**
     * Document.
     */
    case DOCUMENT = 'document';
    /**
     * Secret chat document.
     */
    case ENCRYPTED = 'encrypted';
    /**
     * Temporary document.
     */
    case TEMP = 'temp';
    /**
     * Sticker.
     */
    case STICKER = 'sticker';
    /**
     * Music.
     */
    case AUDIO = 'audio';
    /**
     * GIF.
     */
    case ANIMATION = 'animation';
    /**
     * Encrypted thumbnail.
     */
    case ENCRYPTED_THUMBNAIL = 'encrypted_thumbnail';
    /**
     * Wallpaper.
     */
    case WALLPAPER = 'wallpaper';
    /**
     * Round video.
     */
    case VIDEO_NOTE = 'video_note';
    /**
     * Passport raw file.
     */
    case SECURE_RAW = 'secure_raw';
    /**
     * Passport file.
     */
    case SECURE = 'secure';
    /**
     * Background.
     */
    case BACKGROUND = 'background';
    /**
     * Size.
     */
    case SIZE = 'size';

    /** @internal Should not be used manually. */
    /**
     * Obtain a bot API type ID.
     *
     * @internal Should not be used manually.
     */
    public static function fromInnerID(int $id): self
    {
        return match ($id) {
            0 => self::THUMBNAIL,
            1 => self::PROFILE_PHOTO,
            2 => self::PHOTO,
            3 => self::VOICE,
            4 => self::VIDEO,
            5 => self::DOCUMENT,
            6 => self::ENCRYPTED,
            7 => self::TEMP,
            8 => self::STICKER,
            9 => self::AUDIO,
            10 => self::ANIMATION,
            11 => self::ENCRYPTED_THUMBNAIL,
            12 => self::WALLPAPER,
            13 => self::VIDEO_NOTE,
            14 => self::SECURE_RAW,
            15 => self::SECURE,
            16 => self::BACKGROUND,
            17 => self::SIZE,
        };
    }

    /**
     * Obtain a bot API type ID.
     *
     * @internal Should not be used manually.
     */
    public function toInnerID(): int
    {
        return match ($this) {
            self::THUMBNAIL => 0,
            self::PROFILE_PHOTO => 1,
            self::PHOTO => 2,
            self::VOICE=> 3,
            self::VIDEO => 4,
            self::DOCUMENT=> 5,
            self::ENCRYPTED => 6,
            self::TEMP => 7,
            self::STICKER => 8,
            self::AUDIO => 9,
            self::ANIMATION => 10,
            self::ENCRYPTED_THUMBNAIL => 11,
            self::WALLPAPER => 12,
            self::VIDEO_NOTE => 13,
            self::SECURE_RAW => 14,
            self::SECURE => 15,
            self::BACKGROUND=> 16,
            self::SIZE=>17,
        };
    }

    /**
     * Convert file ID type to unique file ID type.
     */
    public function toUnique(): UniqueFileIdType
    {
        return match ($this) {
            self::PHOTO => UniqueFileIdType::PHOTO,
            self::PROFILE_PHOTO => UniqueFileIdType::PHOTO,
            self::THUMBNAIL => UniqueFileIdType::PHOTO,
            self::ENCRYPTED_THUMBNAIL => UniqueFileIdType::PHOTO,
            self::WALLPAPER => UniqueFileIdType::PHOTO,

            self::VIDEO => UniqueFileIdType::DOCUMENT,
            self::VOICE => UniqueFileIdType::DOCUMENT,
            self::DOCUMENT => UniqueFileIdType::DOCUMENT,
            self::STICKER => UniqueFileIdType::DOCUMENT,
            self::AUDIO => UniqueFileIdType::DOCUMENT,
            self::ANIMATION => UniqueFileIdType::DOCUMENT,
            self::VIDEO_NOTE => UniqueFileIdType::DOCUMENT,
            self::BACKGROUND => UniqueFileIdType::DOCUMENT,

            self::SECURE => UniqueFileIdType::SECURE,
            self::SECURE_RAW => UniqueFileIdType::SECURE,

            self::ENCRYPTED => UniqueFileIdType::ENCRYPTED,

            self::TEMP => UniqueFileIdType::TEMP,

            default => throw new AssertionError("Cannot convert file ID of type ".$this->name." to a unique file ID!")
        };
    }
}
<?php declare(strict_types=1);

namespace danog\Decoder;

\define('BIG_ENDIAN', \pack('L', 1) === \pack('N', 1));

/** @internal */
final class Tools
{
    public const WEB_LOCATION_FLAG =  1 << 24;
    public const FILE_REFERENCE_FLAG = 1 << 25;

    /**
     * Unpack long properly, returns an actual number in any case.
     *
     * @internal
     *
     * @param string $field Field to unpack
     */
    public static function unpackLong(string $field): int
    {
        /** @var int */
        return \unpack('q', BIG_ENDIAN ? \strrev($field) : $field)[1];
    }
    /**
     * Unpack integer.
     * @internal
     *
     * @param string $field Field to unpack
     */
    public static function unpackInt(string $field): int
    {
        /** @var int */
        return \unpack('l', $field)[1];
    }
    /**
     * Pack string long.
     *
     * @internal
     *
     */
    public static function packLong(int $field): string
    {
        $res = \pack('q', $field);
        return BIG_ENDIAN ? \strrev($res) : $res;
    }
    /**
     * Base64URL decode.
     *
     * @param string $data Data to decode
     *
     * @internal
     *
     */
    public static function base64urlDecode(string $data): string
    {
        return \base64_decode(\str_pad(\strtr($data, '-_', '+/'), \strlen($data) % 4, '=', STR_PAD_RIGHT));
    }

    /**
     * Base64URL encode.
     *
     * @param string $data Data to encode
     *
     * @internal
     *
     */
    public static function base64urlEncode(string $data): string
    {
        return \rtrim(\strtr(\base64_encode($data), '+/', '-_'), '=');
    }

    /**
     * Null-byte RLE decode.
     *
     * @param string $string Data to decode
     *
     * @internal
     *
     */
    public static function rleDecode(string $string): string
    {
        $new = '';
        $last = '';
        $null = "\0";
        foreach (\str_split($string) as $cur) {
            if ($last === $null) {
                $new .= \str_repeat($last, \ord($cur));
                $last = '';
            } else {
                $new .= $last;
                $last = $cur;
            }
        }
        $string = $new.$last;

        return $string;
    }

    /**
     * Null-byte RLE encode.
     *
     * @param string $string Data to encode
     *
     * @internal
     *
     */
    public static function rleEncode(string $string): string
    {
        $new = '';
        $count = 0;
        $null = "\0";
        foreach (\str_split($string) as $cur) {
            if ($cur === $null) {
                ++$count;
            } else {
                if ($count > 0) {
                    $new .= $null.\chr($count);
                    $count = 0;
                }
                $new .= $cur;
            }
        }
        if ($count > 0) {
            $new .= $null.\chr($count);
        }

        return $new;
    }

    /**
     * Positive modulo
     * Works just like the % (modulus) operator, only returns always a postive number.
     *
     * @param int $a A
     * @param int $b B
     *
     * @internal
     *
     * @return int Modulo
     */
    public static function posmod(int $a, int $b): int
    {
        $resto = $a % $b;

        return $resto < 0 ? $resto + \abs($b) : $resto;
    }

    /**
     * Read TL string.
     *
     * @param resource $stream Byte stream
     *
     * @internal
     *
     */
    public static function readTLString(mixed $stream): string
    {
        $l = \ord(\stream_get_contents($stream, 1));
        if ($l > 254) {
            throw new \InvalidArgumentException("Length too big!");
        }
        if ($l === 254) {
            /** @var int */
            $long_len = \unpack('V', \stream_get_contents($stream, 3).\chr(0))[1];
            $x = \stream_get_contents($stream, $long_len);
            $resto = self::posmod(-$long_len, 4);
            if ($resto > 0) {
                \fseek($stream, $resto, SEEK_CUR);
            }
        } else {
            $x = $l ? \stream_get_contents($stream, $l) : '';
            $resto = self::posmod(-($l + 1), 4);
            if ($resto > 0) {
                \fseek($stream, $resto, SEEK_CUR);
            }
        }
        \assert($x !== false);
        return $x;
    }

    /**
     * Pack TL string.
     *
     * @param string $string String
     *
     * @internal
     */
    public static function packTLString(string $string): string
    {
        $l = \strlen($string);
        $concat = '';
        if ($l <= 253) {
            $concat .= \chr($l);
            $concat .= $string;
            $concat .= \pack('@'.self::posmod(-$l - 1, 4));
        } else {
            $concat .= \chr(254);
            $concat .= \substr(\pack('V', $l), 0, 3);
            $concat .= $string;
            $concat .= \pack('@'.self::posmod(-$l, 4));
        }
        return $concat;
    }

}
<?php declare(strict_types=1);
/**
 * Photosize source class.
 *
 * This file is part of tg-file-decoder.
 * tg-file-decoder is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * tg-file-decoder is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with tg-file-decoder.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2019 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 *
 * @link https://github.com/tg-file-decoder Documentation
 */

namespace danog\Decoder\PhotoSizeSource;

use danog\Decoder\FileIdType;
use danog\Decoder\PhotoSizeSource;

/**
 * Represents source of photosize.
 *
 * @api
 */
final class PhotoSizeSourceThumbnail extends PhotoSizeSource
{
    public function __construct(
        /**
         * File type of original file.
         *
         */
        public FileIdType $thumbFileType,
        /**
         * Thumbnail size.
         *
         */
        public string $thumbType,
    ) {
    }
}
<?php declare(strict_types=1);
/**
 * Photosize source class.
 *
 * This file is part of tg-file-decoder.
 * tg-file-decoder is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * tg-file-decoder is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with tg-file-decoder.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2019 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 *
 * @link https://github.com/tg-file-decoder Documentation
 */

namespace danog\Decoder\PhotoSizeSource;

use danog\Decoder\PhotoSizeSource;

/**
 * Represents source of photosize.
 *
 * @api
 */
abstract class PhotoSizeSourceDialogPhoto extends PhotoSizeSource
{
    public function __construct(
        public int $dialogId,
        public int $dialogAccessHash,
    ) {
    }
    /**
     * Get whether the big or small version of the photo is being used.
     *
     */
    abstract public function isSmallDialogPhoto(): bool;
}
<?php declare(strict_types=1);
/**
 * Photosize source class.
 *
 * This file is part of tg-file-decoder.
 * tg-file-decoder is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * tg-file-decoder is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with tg-file-decoder.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2019 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 *
 * @link https://github.com/tg-file-decoder Documentation
 */

namespace danog\Decoder\PhotoSizeSource;

use danog\Decoder\PhotoSizeSource;

/**
 * Represents source of photosize.
 *
 * @api
 */
final class PhotoSizeSourceLegacy extends PhotoSizeSource
{
    public function __construct(
        public int $secret,
    ) {
    }
}
<?php declare(strict_types=1);
/**
 * Photosize source class.
 *
 * This file is part of tg-file-decoder.
 * tg-file-decoder is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * tg-file-decoder is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with tg-file-decoder.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2019 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 *
 * @link https://github.com/tg-file-decoder Documentation
 */

namespace danog\Decoder\PhotoSizeSource;

/**
 * Represents source of photosize.
 *
 * @api
 */
final class PhotoSizeSourceDialogPhotoSmall extends PhotoSizeSourceDialogPhoto
{
    /**
     * Get whether the big or small version of the photo is being used.
     *
     */
    public function isSmallDialogPhoto(): bool
    {
        return true;
    }
}
<?php declare(strict_types=1);
/**
 * Photosize source class.
 *
 * This file is part of tg-file-decoder.
 * tg-file-decoder is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * tg-file-decoder is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with tg-file-decoder.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2019 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 *
 * @link https://github.com/tg-file-decoder Documentation
 */

namespace danog\Decoder\PhotoSizeSource;

use danog\Decoder\PhotoSizeSource;

/**
 * Represents source of photosize.
 *
 * @api
 */
final class PhotoSizeSourceStickersetThumbnailVersion extends PhotoSizeSource
{
    public function __construct(
        /**
         * Stickerset ID.
         *
         */
        public int $stickerSetId,
        /**
         * Stickerset access hash.
         *
         */
        public int $stickerSetAccessHash,
        /**
         * Stickerset version.
         *
         */
        public int $stickerSetVersion
    ) {

    }
}
<?php declare(strict_types=1);
/**
 * Photosize source class.
 *
 * This file is part of tg-file-decoder.
 * tg-file-decoder is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * tg-file-decoder is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with tg-file-decoder.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2019 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 *
 * @link https://github.com/tg-file-decoder Documentation
 */

namespace danog\Decoder\PhotoSizeSource;

use danog\Decoder\PhotoSizeSource;

/**
 * Represents source of photosize.
 *
 * @api
 */
final class PhotoSizeSourceStickersetThumbnail extends PhotoSizeSource
{
    public function __construct(
        /**
         * Stickerset ID.
         *
         */
        public int $stickerSetId,
        /**
         * Stickerset access hash.
         *
         */
        public int $stickerSetAccessHash
    ) {

    }
}
<?php declare(strict_types=1);
/**
 * Photosize source class.
 *
 * This file is part of tg-file-decoder.
 * tg-file-decoder is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * tg-file-decoder is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with tg-file-decoder.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2019 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 *
 * @link https://github.com/tg-file-decoder Documentation
 */

namespace danog\Decoder\PhotoSizeSource;

/**
 * Represents source of photosize.
 *
 * @api
 */
final class PhotoSizeSourceDialogPhotoBig extends PhotoSizeSourceDialogPhoto
{
    /**
     * Get whether the big or small version of the photo is being used.
     *
     */
    public function isSmallDialogPhoto(): bool
    {
        return false;
    }
}
<?php declare(strict_types=1);
/**
 * Photosize source class.
 *
 * This file is part of tg-file-decoder.
 * tg-file-decoder is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * tg-file-decoder is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with tg-file-decoder.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2019 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 *
 * @link https://github.com/tg-file-decoder Documentation
 */

namespace danog\Decoder;

/**
 * Represents source of photosize.
 */
abstract class PhotoSizeSource
{
}
<?php declare(strict_types=1);
/**
 * Type enum + helper functions.
 *
 * This file is part of tg-file-decoder.
 * tg-file-decoder is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * tg-file-decoder is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with tg-file-decoder.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2019 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 *
 * @link https://github.com/tg-file-decoder Documentation
 */

namespace danog\Decoder;

/**
 * Represents decoded unique bot API file ID type.
 *
 * @api
 */
enum UniqueFileIdType: int
{
    case WEB = 0;
    case PHOTO = 1;
    case DOCUMENT = 2;
    case SECURE = 3;
    case ENCRYPTED = 4;
    case TEMP = 5;
}
<?php declare(strict_types=1);
/**
 * Decoded FileId class.
 *
 * This file is part of tg-file-decoder.
 * tg-file-decoder is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * tg-file-decoder is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with tg-file-decoder.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2019 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 *
 * @link https://github.com/tg-file-decoder Documentation
 */

namespace danog\Decoder;

use danog\Decoder\PhotoSizeSource\PhotoSizeSourceDialogPhoto;
use danog\Decoder\PhotoSizeSource\PhotoSizeSourceDialogPhotoBig;
use danog\Decoder\PhotoSizeSource\PhotoSizeSourceDialogPhotoSmall;
use danog\Decoder\PhotoSizeSource\PhotoSizeSourceLegacy;
use danog\Decoder\PhotoSizeSource\PhotoSizeSourceStickersetThumbnail;
use danog\Decoder\PhotoSizeSource\PhotoSizeSourceStickersetThumbnailVersion;
use danog\Decoder\PhotoSizeSource\PhotoSizeSourceThumbnail;

/**
 * Represents decoded bot API file ID.
 *
 * @api
 */
final class FileId
{
    /**
     * Basic constructor function.
     */
    public function __construct(
        /**
         * DC ID.
         *
         */
        public int $dcId,

        /**
         * File type.
         *
         */
        public FileIdType $type,

        /**
         * File id.
         *
         */
        public ?int $id,
        /**
         * File access hash.
         *
         */
        public int $accessHash,

        /**
         * Photo size source.
         *
         */
        public ?PhotoSizeSource $photoSizeSource = null,

        /**
         * Photo volume ID.
         *
         */
        public ?int $volumeId = null,
        /**
         * Photo local ID.
         *
         */
        public ?int $localId = null,

        /**
         * File reference.
         *
         */
        public ?string $fileReference = null,
        /**
         * File URL for weblocation.
         *
         */
        public ?string $url = null,

        /**
         * Bot API file ID version.
         *
         */
        public int $version = 4,

        /**
         * Bot API file ID subversion.
         *
         */
        public int $subVersion = 47,
    ) {
    }

    /**
     * Decode file ID from bot API file ID.
     *
     * @param string $fileId File ID
     *
     */
    public static function fromBotAPI(string $fileId): self
    {
        $orig = $fileId;
        $fileId = Tools::rleDecode(Tools::base64urlDecode($fileId));
        $version = \ord($fileId[\strlen($fileId) - 1]);
        $subVersion = $version === 4 ? \ord($fileId[\strlen($fileId) - 2]) : 0;

        $res = \fopen('php://memory', 'rw+b');
        \assert($res !== false);
        \fwrite($res, $fileId);
        \fseek($res, 0);
        $fileId = $res;
        $read = function (int $length) use (&$fileId): string {
            $res = \stream_get_contents($fileId, $length);
            \assert($res !== false);
            return $res;
        };

        $typeId = Tools::unpackInt($read(4));
        $dc_id = Tools::unpackInt($read(4));
        $fileReference = $typeId & Tools::FILE_REFERENCE_FLAG ? Tools::readTLString($fileId) : null;
        $hasWebLocation = (bool) ($typeId & Tools::WEB_LOCATION_FLAG);
        $typeId &= ~Tools::FILE_REFERENCE_FLAG;
        $typeId &= ~Tools::WEB_LOCATION_FLAG;

        if ($hasWebLocation) {
            $url = Tools::readTLString($fileId);
            $access_hash = Tools::unpackLong($read(8));
            return new self(
                $dc_id,
                FileIdType::fromInnerId($typeId),
                null,
                $access_hash,
                fileReference: $fileReference,
                url: $url,
                version: $version,
                subVersion: $subVersion
            );
        }
        $id = Tools::unpackLong($read(8));
        $access_hash = Tools::unpackLong($read(8));

        $volume_id = null;
        $local_id = null;
        $photoSizeSource = null;
        if ($typeId <= FileIdType::PHOTO->toInnerID()) {
            if ($subVersion < 32) {
                $volume_id = Tools::unpackLong($read(8));
            }

            /** @var int */
            $arg = $subVersion >= 22 ? \unpack('V', $read(4))[1] : 0;
            $photosize_source = PhotoSizeSourceType::from($arg);
            switch ($photosize_source) {
                case PhotoSizeSourceType::LEGACY:
                    $photoSizeSource = new PhotoSizeSourceLegacy(Tools::unpackLong($read(8)));
                    break;
                case PhotoSizeSourceType::FULL_LEGACY:
                    $photoSizeSource = new PhotoSizeSourceLegacy(Tools::unpackLong($read(8)));
                    break;
                case PhotoSizeSourceType::THUMBNAIL:
                    /** @var array{file_type: int, thumbnail_type: string} */
                    $result = \unpack('Vfile_type/athumbnail_type', $read(8));
                    $photoSizeSource = new PhotoSizeSourceThumbnail(
                        FileIdType::fromInnerId($result['file_type']),
                        $result['thumbnail_type']
                    );
                    break;
                case PhotoSizeSourceType::DIALOGPHOTO_BIG:
                case PhotoSizeSourceType::DIALOGPHOTO_SMALL:
                    $clazz = $photosize_source === PhotoSizeSourceType::DIALOGPHOTO_SMALL
                        ? PhotoSizeSourceDialogPhotoSmall::class
                        : PhotoSizeSourceDialogPhotoBig::class;
                    $photoSizeSource = new $clazz(
                        Tools::unpackLong($read(8)),
                        Tools::unpackLong($read(8)),
                    );
                    break;
                case PhotoSizeSourceType::STICKERSET_THUMBNAIL:
                    $photoSizeSource = new PhotoSizeSourceStickersetThumbnail(
                        Tools::unpackLong($read(8)),
                        Tools::unpackLong($read(8))
                    );
                    break;
                case PhotoSizeSourceType::DIALOGPHOTO_BIG_LEGACY:
                case PhotoSizeSourceType::DIALOGPHOTO_SMALL_LEGACY:
                    $clazz = $photosize_source === PhotoSizeSourceType::DIALOGPHOTO_SMALL_LEGACY
                        ? PhotoSizeSourceDialogPhotoSmall::class
                        : PhotoSizeSourceDialogPhotoBig::class;
                    $photoSizeSource = new $clazz(
                        Tools::unpackLong($read(8)),
                        Tools::unpackLong($read(8))
                    );

                    break;
                case PhotoSizeSourceType::STICKERSET_THUMBNAIL_LEGACY:
                    $photoSizeSource = new PhotoSizeSourceStickersetThumbnail(
                        Tools::unpackLong($read(8)),
                        Tools::unpackLong($read(8)),
                    );

                    break;
                case PhotoSizeSourceType::STICKERSET_THUMBNAIL_VERSION:
                    $photoSizeSource = new PhotoSizeSourceStickersetThumbnailVersion(
                        Tools::unpackLong($read(8)),
                        Tools::unpackLong($read(8)),
                        Tools::unpackInt($read(4))
                    );
                    break;
            }
            if ($photosize_source === PhotoSizeSourceType::FULL_LEGACY || ($subVersion >= 22 && $subVersion < 32)) {
                $local_id = Tools::unpackInt($read(4));
            }
        }
        $l = \fstat($fileId)['size'] - \ftell($fileId);
        $l -= $version >= 4 ? 2 : 1;
        if ($l > 0) {
            \trigger_error("File ID $orig has $l bytes of leftover data");
        }

        return new self(
            dcId: $dc_id,
            type: FileIdType::fromInnerId($typeId),
            id: $id,
            accessHash: $access_hash,
            volumeId: $volume_id,
            localId: $local_id,
            fileReference: $fileReference,
            version: $version,
            subVersion: $subVersion,
            photoSizeSource: $photoSizeSource,
        );
    }

    /**
     * Get bot API file ID.
     *
     */
    public function getBotAPI(): string
    {
        $type = $this->type->toInnerID();
        if ($this->fileReference !== null) {
            $type |= Tools::FILE_REFERENCE_FLAG;
        }
        if ($this->url !== null) {
            $type |= Tools::WEB_LOCATION_FLAG;
        }

        $fileId = \pack('VV', $type, $this->dcId);
        if ($this->fileReference !== null) {
            $fileId .= Tools::packTLString($this->fileReference);
        }
        if ($this->url !== null) {
            $fileId .= Tools::packTLString($this->url);
            $fileId .= Tools::packLong($this->accessHash);
            return Tools::base64urlEncode(Tools::rleEncode($fileId));
        }

        \assert($this->id !== null);
        $fileId .= Tools::packLong($this->id);
        $fileId .= Tools::packLong($this->accessHash);

        if ($this->photoSizeSource !== null) {
            $photoSize = $this->photoSizeSource;
            if ($this->subVersion < 32) {
                $fileId .= Tools::packLong($this->volumeId);
            }

            switch (true) {
                case $photoSize instanceof PhotoSizeSourceLegacy:
                    $fileId .= Tools::packLong($photoSize->secret);
                    break;
                case $photoSize instanceof PhotoSizeSourceThumbnail:
                    if ($this->subVersion >= 22) {
                        $fileId .= \pack('V', PhotoSizeSourceType::THUMBNAIL->value);
                    }
                    $fileId .= \pack('Va4', $photoSize->thumbFileType->toInnerID(), $photoSize->thumbType);
                    break;
                case $photoSize instanceof PhotoSizeSourceDialogPhoto:
                    if ($this->subVersion >= 22) {
                        $fileId .= \pack(
                            'V',
                            $this->volumeId !== null ?
                            (
                                $photoSize->isSmallDialogPhoto()
                                ? PhotoSizeSourceType::DIALOGPHOTO_SMALL_LEGACY->value
                                : PhotoSizeSourceType::DIALOGPHOTO_BIG_LEGACY->value
                            ) : (
                                $photoSize->isSmallDialogPhoto()
                                ? PhotoSizeSourceType::DIALOGPHOTO_SMALL->value
                                : PhotoSizeSourceType::DIALOGPHOTO_BIG->value
                            )
                        );
                    }
                    $fileId .= Tools::packLong($photoSize->dialogId);
                    $fileId .= Tools::packLong($photoSize->dialogAccessHash);
                    break;
                case $photoSize instanceof PhotoSizeSourceStickersetThumbnail:
                    if ($this->subVersion >= 22) {
                        $fileId .= \pack('V', PhotoSizeSourceType::STICKERSET_THUMBNAIL->value);
                    }
                    $fileId .= Tools::packLong($photoSize->stickerSetId);
                    $fileId .= Tools::packLong($photoSize->stickerSetAccessHash);
                    break;
                case $photoSize instanceof PhotoSizeSourceStickersetThumbnailVersion:
                    if ($this->subVersion >= 22) {
                        $fileId .= \pack('V', PhotoSizeSourceType::STICKERSET_THUMBNAIL_VERSION->value);
                    }
                    $fileId .= Tools::packLong($photoSize->stickerSetId);
                    $fileId .= Tools::packLong($photoSize->stickerSetAccessHash);
                    $fileId .= \pack('l', $photoSize->stickerSetVersion);
                    break;
            }
            if ($this->localId !== null) {
                $fileId .= \pack('l', $this->localId);
            }
        }

        if ($this->version >= 4) {
            $fileId .= \chr($this->subVersion);
        }
        $fileId .= \chr($this->version);

        return Tools::base64urlEncode(Tools::rleEncode($fileId));
    }

    /**
     * Get unique file ID from file ID.
     *
     */
    public function getUnique(): UniqueFileId
    {
        return UniqueFileId::fromFileId($this);
    }
    /**
     * Get unique bot API file ID from file ID.
     *
     */
    public function getUniqueBotAPI(): string
    {
        return UniqueFileId::fromFileId($this)->getUniqueBotAPI();
    }
    /**
     * Get bot API file ID.
     *
     */
    public function __toString(): string
    {
        return $this->getBotAPI();
    }
}
<?php declare(strict_types=1);

/**
 * Type enum + helper functions.
 *
 * This file is part of tg-file-decoder.
 * tg-file-decoder is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * tg-file-decoder is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU General Public License along with tg-file-decoder.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2019 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/licenses/AGPL-3.0 AGPLv3
 *
 * @link https://github.com/tg-file-decoder Documentation
 */

namespace danog\Decoder;

/**
 * @internal Not for public use
 */
enum PhotoSizeSourceType: int
{
    case LEGACY = 0;
    case THUMBNAIL = 1;
    case DIALOGPHOTO_SMALL = 2;
    case DIALOGPHOTO_BIG = 3;
    case STICKERSET_THUMBNAIL = 4;
    case FULL_LEGACY = 5;
    case DIALOGPHOTO_SMALL_LEGACY = 6;
    case DIALOGPHOTO_BIG_LEGACY = 7;
    case STICKERSET_THUMBNAIL_LEGACY = 8;
    case STICKERSET_THUMBNAIL_VERSION = 9;
}
{
    "name": "danog/tg-file-decoder",
    "description": "Decode Telegram bot API file IDs",
    "type": "project",
    "license": "AGPL-3.0-only",
    "keywords": ["telegram", "mtproto", "bot API", "file ID", "video", "stickers", "audio", "files"],
    "authors": [
        {
            "name": "Daniil Gentili",
            "email": "daniil@daniil.it"
        }
    ],
    "require": {
        "php-64bit": ">=8.2"
    },
    "require-dev": {
        "phpunit/phpunit": "^11",
        "amphp/php-cs-fixer-config": "^2",
        "vimeo/psalm": "dev-master",
        "danog/phpdoc": "^0.1.22"
    },
    "autoload": {
        "psr-4": {
            "danog\\Decoder\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "danog\\Decoder\\Test\\": "tests"
        }
    },
    "scripts": {
        "check": [
            "@cs",
            "@test"
        ],
        "cs": "PHP_CS_FIXER_IGNORE_ENV=1 php-cs-fixer fix -v --diff --dry-run",
        "cs-fix": "PHP_CS_FIXER_IGNORE_ENV=1 php-cs-fixer fix -v --diff",
        "test": "@php -dzend.assertions=1 -dassert.exception=1 ./vendor/bin/phpunit --coverage-text  --configuration phpunit.xml.dist"
    },
    "config": {
        "allow-plugins": {
            "phabel/phabel": true
        }
    }
}
<?php declare(strict_types=1);

use Amp\Http\Client\HttpClientBuilder;
use Amp\Http\Client\Request;
use danog\TelegramEntities\Entities;
use danog\TelegramEntities\EntityTools;

require __DIR__.'/../vendor/autoload.php';

$token = getenv('TOKEN');
if (!$token) {
    throw new AssertionError("A TOKEN environment variable must be specified!");
}

$dest = getenv('DEST');
if (!$dest) {
    throw new AssertionError("A DEST environment variable must be specified!");
}

$client = HttpClientBuilder::buildDefault();

$sm = function (string $message, string $parse_mode = '', array $entities = []) use ($token, $dest, $client): array {
    $res = $client->request(new Request("https://api.telegram.org/bot$token/sendMessage?".http_build_query([
        'text' => $message,
        'parse_mode' => $parse_mode,
        'entities' => json_encode($entities),
        'chat_id' => $dest
    ])));

    return json_decode($res->getBody()->buffer(), true)['result'];
};

$result = $sm("*This is a â�¤ï¸� test*", parse_mode: "MarkdownV2");

// Convert a message+entities back to HTML
$entities = new Entities($result['text'], $result['entities']);
var_dump($entities->toHTML()); // <b>This is a â�¤ï¸� test</b>

// Modify $entities as needed
$entities->message = "A message with â�¤ï¸� emojis";

// EntityTools::mb* methods compute the length in UTF-16 code units, as required by the bot API.
$entities->entities[0]['length'] = EntityTools::mbStrlen($entities->message);

// then resend:
$sm($entities->message, entities: $entities->entities);

// Convert HTML to an array of entities locally
$entities = Entities::fromHtml("<b>This is <i>a â�¤ï¸� nested</i> test</b>");
$sm($entities->message, entities: $entities->entities);

// Convert markdown to an array of entities locally
$entities = Entities::fromMarkdown("*This is _a â�¤ï¸� nested_ test*");
$sm($entities->message, entities: $entities->entities);

// Escape text using utility methods
$generic = EntityTools::markdownEscape("Automatically escaped to prevent *markdown injection*!");
$link = EntityTools::markdownUrlEscape("https://google.com");
$code = EntityTools::markdownCodeEscape("test with autoescaped ` test");
$codeBlock = EntityTools::markdownCodeblockEscape("<?php echo 'test with autoescaped ``` test';");

$entities = Entities::fromMarkdown("This is _a â�¤ï¸� [nested]($link)_ `$code`

```php
$codeBlock
```

$generic
");

$sm($entities->message, entities: $entities->entities);

// Escape text for the HTML parser!
$generic = EntityTools::htmlEscape("Automatically escaped to prevent <b>HTML injection</b>!");
$entities = Entities::fromHtml($generic);

$sm($entities->message, entities: $entities->entities);

// See https://github.com/danog/telegram-entities for the full list of available methods!
<?php

$config = new Amp\CodeStyle\Config;

$config->getFinder()
    ->in(__DIR__ . '/src')
    ->in(__DIR__ . '/tests')
    ->in(__DIR__ . '/examples');

$cacheDir = getenv('TRAVIS') ? getenv('HOME') . '/.php-cs-fixer' : __DIR__;

$config->setCacheFile($cacheDir . '/.php_cs.cache');

return $config;
<?php declare(strict_types=1);

/**
 * Tools module.
 *
 * Copyright 2024 Daniil Gentili.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2024 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/license/apache-2-0 Apache 2.0
 * @link https://github.com/danog/telegram-entities TelegramEntities documentation
 */

namespace danog\TelegramEntities;

use Webmozart\Assert\Assert;

/**
 * Telegram UTF-16 styled text entity tools.
 *
 * @api
 *
 * @psalm-immutable
 */
final class EntityTools
{
    // @codeCoverageIgnoreStart
    /**
     * @psalm-suppress UnusedConstructor
     *
     * @internal Can only be used statically.
     *
     * @psalm-mutation-free
     */
    private function __construct()
    {
    }
    // @codeCoverageIgnoreEnd

    /**
     * Get length of string in UTF-16 code points.
     *
     * @param string $text Text
     *
     * @psalm-pure
     */
    public static function mbStrlen(string $text): int
    {
        $length = 0;
        $textlength = \strlen($text);
        for ($x = 0; $x < $textlength; $x++) {
            $char = \ord($text[$x]);
            if (($char & 0xc0) != 0x80) {
                $length += 1 + ($char >= 0xf0 ? 1 : 0);
            }
        }
        return $length;
    }

    /**
     * Telegram UTF-16 multibyte substring.
     *
     * @param string   $text   Text to substring
     * @param integer  $offset Offset
     * @param null|int $length Length
     *
     * @psalm-pure
     */
    public static function mbSubstr(string $text, int $offset, ?int $length = null): string
    {
        /** @var string */
        $converted = \mb_convert_encoding($text, 'UTF-16');
        /** @var string */
        return \mb_convert_encoding(
            \substr(
                $converted,
                $offset<<1,
                $length === null ? null : ($length<<1),
            ),
            'UTF-8',
            'UTF-16',
        );
    }

    /**
     * Telegram UTF-16 multibyte split.
     *
     * @param string $text Text
     * @param integer<0, max> $length Length
     *
     * @return list<string>
     *
     * @psalm-pure
     */
    public static function mbStrSplit(string $text, int $length): array
    {
        $result = [];
        /** @var string */
        $text = \mb_convert_encoding($text, 'UTF-16');
        /** @psalm-suppress ArgumentTypeCoercion */
        foreach (\str_split($text, $length<<1) as $chunk) {
            $chunk = \mb_convert_encoding($chunk, 'UTF-8', 'UTF-16');
            Assert::string($chunk);
            $result []= $chunk;
        }
        /** @var list<string> */
        return $result;
    }

    /**
     * Telegram UTF-16 multibyte subreplace.
     *
     * @param string   $string  Text
     * @param string   $replace Replacement
     * @param integer  $offset  Offset
     * @param null|int $length  Length
     *
     * @return string The result string is returned. If string is an array then array is returned.
     *
     * @psalm-pure
     */
    public static function mbSubstrReplace(
        string   $string,
        string   $replace,
        int      $offset,
        int|null $length = null
    ): string {
        /** @var string */
        $string  = \mb_convert_encoding($string, 'UTF-16');
        /** @var string */
        $replace = \mb_convert_encoding($replace, 'UTF-16');
        /** @var string */
        return \mb_convert_encoding(
            \substr_replace(
                $string,
                $replace,
                $offset << 1,
                $length === null ? null : ($length << 1),
            ),
            'UTF-8',
            'UTF-16',
        );
    }

    /**
     * Escape string for this library's HTML entity converter.
     *
     * @param string $what String to escape
     *
     * @psalm-pure
     */
    public static function htmlEscape(string $what): string
    {
        return \htmlspecialchars($what, ENT_QUOTES|ENT_SUBSTITUTE|ENT_XML1);
    }

    /**
     * Escape string for markdown.
     *
     * @param string $what String to escape
     *
     * @psalm-pure
     */
    public static function markdownEscape(string $what): string
    {
        return \str_replace(
            [
                '\\',
                '_',
                '*',
                '[',
                ']',
                '(',
                ')',
                '~',
                '`',
                '>',
                '#',
                '+',
                '-',
                '=',
                '|',
                '{',
                '}',
                '.',
                '!',
            ],
            [
                '\\\\',
                '\\_',
                '\\*',
                '\\[',
                '\\]',
                '\\(',
                '\\)',
                '\\~',
                '\\`',
                '\\>',
                '\\#',
                '\\+',
                '\\-',
                '\\=',
                '\\|',
                '\\{',
                '\\}',
                '\\.',
                '\\!',
            ],
            $what
        );
    }

    /**
     * Escape string for markdown codeblock.
     *
     * @param string $what String to escape
     *
     * @psalm-pure
     */
    public static function markdownCodeblockEscape(string $what): string
    {
        return \str_replace('```', '\\```', $what);
    }

    /**
     * Escape string for markdown code section.
     *
     * @param string $what String to escape
     *
     * @psalm-pure
     */
    public static function markdownCodeEscape(string $what): string
    {
        return \str_replace('`', '\\`', $what);
    }

    /**
     * Escape string for URL.
     *
     * @param string $what String to escape
     *
     * @psalm-pure
     */
    public static function markdownUrlEscape(string $what): string
    {
        return \str_replace(['\\', ')'], ['\\\\', '\\)'], $what);
    }
}
<?php declare(strict_types=1);

/**
 * Copyright 2024 Daniil Gentili.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @author    Daniil Gentili <daniil@daniil.it>
 * @copyright 2016-2024 Daniil Gentili <daniil@daniil.it>
 * @license   https://opensource.org/license/apache-2-0 Apache 2.0
 * @link https://github.com/danog/telegram-entities TelegramEntities documentation
 */

namespace danog\TelegramEntities;

use AssertionError;
use DOMDocument;
use DOMElement;
use DOMNode;
use DOMText;

/**
 * Class that represents a message + set of Telegram entities.
 *
 * Entity types and fields follow the Bot API MessageEntity type.
 *
 * @api
 *
 * @psalm-type TEntity=(
 *      array{
 *          type: "bold"|"italic"|"code"|"strikethrough"|"underline"|"blockquote"|"expandable_blockquote"|"url"|"email"|"phone_number"|"spoiler"|"mention"|"hashtag"|"cashtag"|"bot_command"|"bank_card_number",
 *          offset: int<0, max>,
 *          length: int<0, max>
 *      }
 *      |array{type: "text_mention", user: array{id: int, ...}, offset: int, length: int}
 *      |array{type: "custom_emoji", custom_emoji_id: int, offset: int, length: int}
 *      |array{type: "pre", language?: string, offset: int, length: int}
 *      |array{type: "text_link", url: string, offset: int, length: int}
 *      |array{type: "date_time", unix_time: int, date_time_format: string, offset: int, length: int}
 * )
 */
final class Entities
{
    /**
     * Creates an Entities container using a message and a list of entities.
     *
     * @psalm-mutation-free
     */
    public function __construct(
        /** Converted message */
        public string $message,
        /**
         * Converted entities.
         *
         * @var list<TEntity>
         */
        public array $entities,
    ) {
    }

    /**
     * Manually convert markdown to a message and a set of entities.
     *
     * Supports the MarkdownV2 syntax of the Bot API, including block quotations (lines starting with `>`),
     * expandable block quotations (a block quotation whose last line ends with `||`),
     * custom emojis (`![ðŸ‘�](tg://emoji?id=5368324170671202286)`) and
     * date-times (`![22:45 tomorrow](tg://time?unix=1647531900&format=wDT)`).
     *
     * @return Entities Object containing message and entities
     *
     * @psalm-pure
     */
    public static function fromMarkdown(string $markdown): self
    {
        $markdown = \trim(\str_replace("\r\n", "\n", $markdown));
        $message = '';
        $messageLen = 0;
        $entities = [];
        $offset = 0;
        /** @var list<array{0: string, 1: int}> */
        $stack = [];
        // Whether a > at the current position starts (or continues) a block quotation:
        // only markup characters may precede it on its line.
        $canStartQuote = true;
        while ($offset < \strlen($markdown)) {
            $len = \strcspn($markdown, "*_~`[]|!\\>\n", $offset);
            $piece = \substr($markdown, $offset, $len);
            $offset += $len;
            if ($piece !== '') {
                $canStartQuote = false;
            }
            if ($offset === \strlen($markdown)) {
                $message .= $piece;
                $messageLen += EntityTools::mbStrlen($piece);
                break;
            }

            $char = $markdown[$offset++];
            $next = $markdown[$offset] ?? '';
            if ($char === "\n") {
                $message .= $piece."\n";
                $messageLen += EntityTools::mbStrlen($piece) + 1;
                if ($stack && $next !== '>' && self::hasQuote($stack)) {
                    // The quotation includes the newline, like in the Bot API
                    [$stack, $quote] = self::closeQuote($stack, $messageLen - 1, $messageLen, \substr($markdown, $offset-3, 2) === '||');
                    if ($quote !== null) {
                        $entities []= $quote;
                    }
                }
                $canStartQuote = true;
                continue;
            }
            if ($char === '>') {
                $message .= $piece;
                $messageLen += EntityTools::mbStrlen($piece);
                if ($canStartQuote && self::hasQuote($stack)) {
                    // Continuation of the block quotation
                } elseif ($canStartQuote && !$stack) {
                    $stack []= ['>', $messageLen];
                } else {
                    // Not at the start of a line, or inside another entity (like in *> text*)
                    $message .= $char;
                    $messageLen++;
                    $canStartQuote = false;
                }
                continue;
            }
            if ($char === '\\') {
                $message .= $piece.$next;
                $messageLen += EntityTools::mbStrlen($piece.$next);
                $offset += \strlen($next);
                $canStartQuote = false;
                continue;
            }

            if ($char === '_' && $next === '_') {
                $offset++;
                $char = '__';
            } elseif ($char === '|') {
                if ($next === '|') {
                    $offset++;
                    $char = '||';
                } else {
                    $message .= $piece.$char;
                    $messageLen += EntityTools::mbStrlen($piece)+1;
                    $canStartQuote = false;
                    continue;
                }
            } elseif ($char === '!') {
                if ($next === '[') {
                    $offset++;
                    $char = '](';
                } else {
                    $message .= $piece.$char;
                    $messageLen += EntityTools::mbStrlen($piece)+1;
                    $canStartQuote = false;
                    continue;
                }
            } elseif ($char === '[') {
                $char = '](';
            } elseif ($char === ']') {
                if (!$stack || \end($stack)[0] !== '](') {
                    $message .= $piece.$char;
                    $messageLen += EntityTools::mbStrlen($piece)+1;
                    $canStartQuote = false;
                    continue;
                }
                if ($next !== '(') {
                    \array_pop($stack);
                    $message .= '['.$piece.$char;
                    $messageLen += EntityTools::mbStrlen($piece)+2;
                    $canStartQuote = false;
                    continue;
                }
                $offset++;
                $char = "](";
            } elseif ($char === '`') {
                $message .= $piece;
                $messageLen += EntityTools::mbStrlen($piece);
                $canStartQuote = false;

                $token = '`';
                $language = null;
                if ($next === '`' && ($markdown[$offset+1] ?? '') === '`') {
                    $token = '```';

                    $offset += 2;
                    $langLen = \strcspn($markdown, "\n ", $offset);
                    $language = \substr($markdown, $offset, $langLen);
                    $offset += $langLen;
                    if (($markdown[$offset] ?? '') === "\n") {
                        $offset++;
                    }
                }

                $piece = '';
                $posClose = $offset;
                while (($posClose = \strpos($markdown, $token, $posClose)) !== false) {
                    if ($markdown[$posClose-1] === '\\') {
                        $piece .= \substr($markdown, $offset, ($posClose-$offset)-1).$token;
                        $posClose += \strlen($token);
                        $offset = $posClose;
                        continue;
                    }
                    break;
                }
                /** @var int|false $posClose */
                if ($posClose === false) {
                    throw new AssertionError("Unclosed ``` opened @ pos $offset!");
                }
                $piece .= \substr($markdown, $offset, $posClose-$offset);

                $start = $messageLen;

                $message .= $piece;
                $pieceLen = EntityTools::mbStrlen($piece);
                $messageLen += $pieceLen;

                for ($x = \strlen($piece)-1; $x >= 0; $x--) {
                    if (!(
                        $piece[$x] === ' '
                        || $piece[$x] === "\r"
                        || $piece[$x] === "\n"
                    )) {
                        break;
                    }
                    $pieceLen--;
                }
                if ($pieceLen > 0) {
                    \assert($start >= 0);
                    $tmp = [
                        'type' => match ($token) {
                            '```' => 'pre',
                            '`' => 'code',
                        },
                        'offset' => $start,
                        'length' => $pieceLen,
                    ];
                    if ($language !== null) {
                        $tmp['language'] = $language;
                    }
                    $entities []= $tmp;
                    unset($tmp);
                }

                $offset = $posClose+\strlen($token);
                continue;
            }

            if ($stack && \end($stack)[0] === $char) {
                [, $start] = \array_pop($stack);
                if ($char === '](') {
                    $link = '';
                    $posClose = $offset;
                    $length = \strlen($markdown);
                    while ($posClose < $length && $markdown[$posClose] !== ')') {
                        $c = $markdown[$posClose];
                        if ($c === '\\' && $posClose + 1 < $length && \ord($markdown[$posClose + 1]) >= 1 && \ord($markdown[$posClose + 1]) <= 126) {
                            $link .= $markdown[$posClose + 1];
                            $posClose += 2;
                            continue;
                        }
                        $link .= $c;
                        $posClose++;
                    }
                    if ($posClose >= $length) {
                        throw new AssertionError("Unclosed ) opened @ pos $offset!");
                    }
                    $entity = self::handleLink($link);
                    $offset = $posClose+1;
                } else {
                    $entity = match ($char) {
                        '*' => ['type' => 'bold'],
                        '_' => ['type' => 'italic'],
                        '__' =>  ['type' => 'underline'],
                        '`' => ['type' => 'code'],
                        '~' => ['type' => 'strikethrough'],
                        '||' => ['type' => 'spoiler'],
                        default => throw new AssertionError("Unknown char $char @ pos $offset!")
                    };
                }
                $message .= $piece;
                $messageLen += EntityTools::mbStrlen($piece);
                if ($piece !== '') {
                    $canStartQuote = false;
                }

                $lengthReal = $messageLen-$start;
                if ($lengthReal > 0) {
                    $entities []= $entity + ['offset' => $start, 'length' => $lengthReal];
                }
            } else {
                $message .= $piece;
                $messageLen += EntityTools::mbStrlen($piece);
                $stack []= [$char, $messageLen];
            }
        }
        if ($stack && self::hasQuote($stack)) {
            [$stack, $quote] = self::closeQuote($stack, $messageLen, $messageLen, \str_ends_with($markdown, '||'));
            if ($quote !== null) {
                $entities []= $quote;
            }
        }
        if ($stack) {
            throw new AssertionError("Found unclosed markdown elements ".\implode(', ', \array_column($stack, 0)));
        }
        return self::trimmed($message, $entities);
    }

    /**
     * Trim whitespace from the start and the end of the message, moving and shortening the entities accordingly.
     *
     * @param list<TEntity> $entities
     *
     * @psalm-pure
     */
    private static function trimmed(string $message, array $entities): self
    {
        $trimmed = \trim($message);
        // The trimmed characters are single bytes, and single UTF-16 code units
        $leading = \strlen($message) - \strlen(\ltrim($message));
        $length = EntityTools::mbStrlen($trimmed);
        $result = [];
        foreach ($entities as $entity) {
            $start = \max(0, $entity['offset'] - $leading);
            $end = \min($length, $entity['offset'] + $entity['length'] - $leading);
            if ($end > $start) {
                $entity['offset'] = $start;
                $entity['length'] = $end - $start;
                $result []= $entity;
            }
        }
        return new self($trimmed, $result);
    }

    /**
     * @param list<array{0: string, 1: int}> $stack
     *
     * @psalm-pure
     */
    private static function hasQuote(array $stack): bool
    {
        foreach ($stack as [$char]) {
            if ($char === '>') {
                return true;
            }
        }
        return false;
    }

    /**
     * Close the block quotation at the end of the current line.
     *
     * @param non-empty-list<array{0: string, 1: int}> $stack
     * @param int $lineEnd End of the last line of the quotation, before the newline
     * @param int $end End of the quotation
     * @param bool $endsWithSpoilerMarker Whether the line ends with ||, which makes the quotation expandable
     *
     * @return array{0: list<array{0: string, 1: int}>, 1: ?array{type: "blockquote"|"expandable_blockquote", offset: int<0, max>, length: int<0, max>}} New stack and quotation entity, if not empty
     *
     * @psalm-pure
     */
    private static function closeQuote(array $stack, int $lineEnd, int $end, bool $endsWithSpoilerMarker): array
    {
        $type = 'blockquote';
        $top = $stack[\array_key_last($stack)];
        if ($endsWithSpoilerMarker && $top[0] === '||' && $top[1] === $lineEnd) {
            $stack = \array_slice($stack, 0, -1);
            $type = 'expandable_blockquote';
            $top = $stack ? $stack[\array_key_last($stack)] : ['', 0];
        }
        if ($top[0] !== '>') {
            throw new AssertionError("Found unclosed markdown element {$top[0]} at the end of a block quotation");
        }
        $stack = \array_slice($stack, 0, -1);
        $start = $top[1];
        $lengthReal = $end - $start;
        if ($lengthReal > 0 && $start >= 0) {
            /** @var int<1, max> $lengthReal */
            return [$stack, ['type' => $type, 'offset' => $start, 'length' => $lengthReal]];
        }
        return [$stack, null];
    }

    /**
     * Manually convert HTML to a message and a set of entities.
     *
     * Supports the HTML syntax of the Bot API, including `<blockquote expandable>`,
     * `<pre><code class="language-php">`, `<tg-emoji emoji-id="...">` and
     * `<tg-time unix="..." format="...">`.
     *
     * @return Entities Object containing message and entities
     */
    public static function fromHtml(string $html): Entities
    {
        $dom = new DOMDocument();
        $html = \preg_replace(
            ['/\<br(\s*)?\/?\>/i', '/(\<blockquote\b[^>]*?\s)expandable(?=[\s\/>])/i'],
            ["\n", '$1expandable=""'],
            $html
        );
        \assert($html !== null);
        $dom->loadxml('<body>' . \trim($html) . '</body>');
        $message = '';
        $entities = [];
        /** @psalm-suppress PossiblyNullArgument Ignore, will throw anyway */
        self::parseNode($dom->getElementsByTagName('body')->item(0), 0, $message, $entities);
        return self::trimmed($message, $entities);
    }
    /**
     * @return integer Length of the node
     *
     * @param-out list<TEntity> $entities
     * @param list<TEntity> $entities
     */
    private static function parseNode(DOMNode|DOMText $node, int $offset, string &$message, array &$entities): int
    {
        if ($node instanceof DOMText) {
            $message .= $node->wholeText;
            return EntityTools::mbStrlen($node->wholeText);
        }
        // @codeCoverageIgnoreStart
        if ($node->nodeName === 'br') {
            $message .= "\n";
            return 1;
        }
        // @codeCoverageIgnoreEnd
        $children = $node->childNodes;
        /** @var DOMElement $node */
        $entity = match ($node->nodeName) {
            's', 'strike', 'del' => ['type' => 'strikethrough'],
            'u', 'ins' =>  ['type' => 'underline'],
            'blockquote' => $node->hasAttribute('expandable')
                ? ['type' => 'expandable_blockquote']
                : ['type' => 'blockquote'],
            'b', 'strong' => ['type' => 'bold'],
            'i', 'em' => ['type' => 'italic'],
            'code' => ['type' => 'code'],
            'spoiler', 'tg-spoiler' => ['type' => 'spoiler'],
            'pre' => self::handlePre($node, $children),
            'span' => $node->hasAttribute('class') && $node->getAttribute('class') === 'tg-spoiler'
                    ? ['type' => 'spoiler']
                    : null,
            'tg-emoji' => ['type' => 'custom_emoji', 'custom_emoji_id' => (int) $node->getAttribute('emoji-id')],
            'emoji' => ['type' => 'custom_emoji', 'custom_emoji_id' => (int) $node->getAttribute('id')],
            'tg-time' => self::handleDateTime($node->getAttribute('unix'), $node->getAttribute('format')),
            'a' => self::handleLink($node->getAttribute('href')),
            default => null,
        };
        $length = 0;
        /** @var DOMNode|DOMText */
        foreach ($children as $sub) {
            $length += self::parseNode($sub, $offset+$length, $message, $entities);
        }
        if ($entity !== null) {
            $lengthReal = $length;
            if ($lengthReal > 0) {
                \assert($offset >= 0);
                $entity['offset'] = $offset;
                $entity['length'] = $lengthReal;
                /** @psalm-check-type $entity = TEntity */
                $entities []= $entity;
            }
        }
        return $length;
    }
    /**
     * Handle a pre tag, either with a language attribute, or wrapping a code tag with a language-* class (Bot API syntax).
     *
     * @param \DOMNodeList $children Children to parse, replaced with the code tag's children if it carries the language
     *
     * @param-out \DOMNodeList $children
     *
     * @return array{type: "pre", language?: string}
     */
    private static function handlePre(DOMElement $node, \DOMNodeList &$children): array
    {
        if ($node->hasAttribute('language')) {
            return ['type' => 'pre', 'language' => $node->getAttribute('language')];
        }
        $code = $children->length === 1 ? $children->item(0) : null;
        if ($code instanceof DOMElement
            && $code->nodeName === 'code'
            && \str_starts_with($code->getAttribute('class'), 'language-')
        ) {
            $children = $code->childNodes;
            return ['type' => 'pre', 'language' => \substr($code->getAttribute('class'), 9)];
        }
        return ['type' => 'pre'];
    }
    /**
     * @return array{type: "date_time", unix_time: int, date_time_format: string}
     *
     * @psalm-pure
     */
    private static function handleDateTime(string $unixTime, string $format): array
    {
        if (!\ctype_digit($unixTime) || (int) $unixTime <= 0) {
            throw new AssertionError("Invalid Unix time specified: $unixTime");
        }
        return ['type' => 'date_time', 'unix_time' => (int) $unixTime, 'date_time_format' => self::normalizeDateTimeFormat($format)];
    }
    /**
     * Normalize a date-time format as the Bot API does: r for relative times, or w (day of the week), d|D (short|long date), t|T (short|long time), in this order.
     *
     * @psalm-pure
     */
    public static function normalizeDateTimeFormat(string $format): string
    {
        if ($format === 'r' || $format === 'R') {
            return 'r';
        }
        $dayOfWeek = $date = $time = '';
        foreach (\str_split($format) as $c) {
            match ($c) {
                't', 'T' => $time = $c,
                'd', 'D' => $date = $c,
                'w', 'W' => $dayOfWeek = 'w',
                default => throw new AssertionError("Invalid date-time format specified: $format"),
            };
        }
        return $dayOfWeek.$date.$time;
    }
    /**
     * @return array{type: "text_mention", user: array{id: int}}|array{type: "custom_emoji", custom_emoji_id: int}|array{type: "text_link", url: string}|array{type: "date_time", unix_time: int, date_time_format: string}
     *
     * @psalm-pure
     */
    private static function handleLink(string $href): array
    {
        if (\preg_match('|^mention:(.+)|', $href, $matches) || \preg_match('|^tg://user\\?id=(.+)|', $href, $matches)) {
            return ['type' => 'text_mention', 'user' => ['id' => (int) $matches[1]]];
        }
        if (\preg_match('|^emoji:(\d+)$|', $href, $matches) || \preg_match('|^tg://emoji\\?id=(.+)|', $href, $matches)) {
            return ['type' => 'custom_emoji', 'custom_emoji_id' => (int) $matches[1]];
        }
        if (\preg_match('|^tg://time\\?(.+)|', $href, $matches)) {
            return self::handleDateTime(
                \preg_match('/(?:^|&)unix=([^&]*)/', $matches[1], $unix) ? $unix[1] : '',
                \preg_match('/(?:^|&)format=([^&]*)/', $matches[1], $format) ? $format[1] : '',
            );
        }
        return ['type' => 'text_link', 'url' => $href];
    }
    /**
     * Convert a message and a set of entities to HTML.
     *
     * @param bool $allowTelegramTags Whether to allow telegram-specific tags like tg-spoiler, tg-emoji, tg-time, mention links and so on...
     *
     * @psalm-mutation-free
     */
    public function toHTML(bool $allowTelegramTags = false): string
    {
        $insertions = [];
        foreach ($this->entities as $entity) {
            ['offset' => $offset, 'length' => $length] = $entity;
            $insertions[$offset] ??= '';
            /** @psalm-suppress PossiblyUndefinedArrayOffset, DocblockTypeContradiction */
            $insertions[$offset] .= match ($entity['type']) {
                'bold' => '<b>',
                'italic' => '<i>',
                'code' => '<code>',
                'pre' => isset($entity['language']) && $entity['language'] !== ''
                    ? ($allowTelegramTags
                        ? '<pre><code class="language-'.EntityTools::htmlEscape($entity['language']).'">'
                        : '<pre language="'.EntityTools::htmlEscape($entity['language']).'">')
                    : '<pre>',
                'text_link' => '<a href="'.EntityTools::htmlEscape($entity['url']).'">',
                'strikethrough' => '<s>',
                "underline" => '<u>',
                "blockquote", "block_quote" => '<blockquote>',
                "expandable_blockquote" => $allowTelegramTags ? '<blockquote expandable>' : '<blockquote>',
                "url" => '<a href="'.EntityTools::htmlEscape(EntityTools::mbSubstr($this->message, $offset, $length)).'">',
                "email" => '<a href="mailto:'.EntityTools::htmlEscape(EntityTools::mbSubstr($this->message, $offset, $length)).'">',
                "phone_number", "phone" => '<a href="tel:'.EntityTools::htmlEscape(EntityTools::mbSubstr($this->message, $offset, $length)).'">',
                "mention" => '<a href="https://t.me/'.EntityTools::htmlEscape(EntityTools::mbSubstr($this->message, $offset+1, $length-1)).'">',
                "spoiler" => $allowTelegramTags ? '<tg-spoiler>' : '<span class="tg-spoiler">',
                "custom_emoji" => $allowTelegramTags ? '<tg-emoji emoji-id="'.$entity['custom_emoji_id'].'">' : '',
                "text_mention" => $allowTelegramTags ? '<a href="tg://user?id='.$entity['user']['id'].'">' : '',
                "date_time" => $allowTelegramTags
                    ? '<tg-time unix="'.$entity['unix_time'].'"'.(($entity['date_time_format'] ?? '') !== '' ? ' format="'.EntityTools::htmlEscape($entity['date_time_format']).'"' : '').'>'
                    : '',
                default => '',
            };
            $offset += $length;
            /** @psalm-suppress DocblockTypeContradiction */
            $insertions[$offset] = match ($entity['type']) {
                "bold" => '</b>',
                "italic" => '</i>',
                "code" => '</code>',
                "pre" => isset($entity['language']) && $entity['language'] !== '' && $allowTelegramTags
                    ? '</code></pre>'
                    : '</pre>',
                "text_link", "url", "email", "mention", "phone_number", "phone" => '</a>',
                "strikethrough" => '</s>',
                "underline" => '</u>',
                "blockquote", "block_quote", "expandable_blockquote" => '</blockquote>',
                "spoiler" => $allowTelegramTags ? '</tg-spoiler>' : '</span>',
                "custom_emoji" => $allowTelegramTags ? "</tg-emoji>" : '',
                "text_mention" => $allowTelegramTags ? '</a>' : '',
                "date_time" => $allowTelegramTags ? '</tg-time>' : '',
                default => '',
            } . ($insertions[$offset] ?? '');
        }
        \ksort($insertions);
        $final = '';
        $pos = 0;
        foreach ($insertions as $offset => $insertion) {
            $final .= EntityTools::htmlEscape(EntityTools::mbSubstr($this->message, $pos, $offset-$pos));
            $final .= $insertion;
            $pos = $offset;
        }
        return \str_replace("\n", "<br>", $final.EntityTools::htmlEscape(EntityTools::mbSubstr($this->message, $pos)));
    }
}
{
    "name": "danog/telegram-entities",
    "description": "A library to work with Telegram UTF-16 styled text entities.",
    "type": "library",
    "license": "Apache-2.0",
    "autoload": {
        "psr-4": {
            "danog\\TelegramEntities\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "danog\\TestTelegramEntities\\": "tests/"
        }
    },
    "authors": [
        {
            "name": "Daniil Gentili",
            "email": "daniil@daniil.it"
        }
    ],
    "require": {
        "php-64bit": ">=8.2.4",
        "webmozart/assert": "^2",
        "symfony/polyfill-mbstring": "*"
    },
    "require-dev": {
        "vimeo/psalm": "dev-master",
        "phpunit/phpunit": "^11.0.9",
        "amphp/php-cs-fixer-config": "^2.0.1",
        "friendsofphp/php-cs-fixer": "^3.52.1",
        "danog/phpdoc": "^0.1.22",
        "amphp/http-client": "^5.0"
    },
    "scripts": {
        "cs-fix": "PHP_CS_FIXER_IGNORE_ENV=1 php -d pcre.jit=0 vendor/bin/php-cs-fixer fix -v"
    },
    "config": {
        "allow-plugins": {
            "dealerdirect/phpcodesniffer-composer-installer": true,
            "infection/extension-installer": true
        }
    }

}
<?php

namespace Webrtc\DataChannel\Listener;

/**
 * Notified when an {@see \Webrtc\DataChannel\RTCDataChannel}'s buffered amount falls below its threshold.
 *
 * Typed replacement for the former Evenement "bufferedamountlow" event; the listener is a plain
 * object captured verbatim by serialization.
 */
interface DataChannelBufferedAmountLowListener
{
    public function onDataChannelBufferedAmountLow(): void;
}
<?php

namespace Webrtc\DataChannel\Listener;

/**
 * Notified when an {@see \Webrtc\DataChannel\RTCDataChannel} transitions to the open state.
 *
 * Typed replacement for the former Evenement "open" event; the listener is a plain object
 * captured verbatim by serialization.
 */
interface DataChannelOpenListener
{
    public function onDataChannelOpen(): void;
}
<?php

namespace Webrtc\DataChannel\Listener;

/**
 * Receives application data delivered into an {@see \Webrtc\DataChannel\RTCDataChannel} by the SCTP transport.
 *
 * Typed replacement for the former Evenement "message" event; the listener is a plain object
 * captured verbatim by serialization.
 */
interface DataChannelMessageListener
{
    public function onDataChannelMessage(string $data): void;
}
<?php

namespace Webrtc\DataChannel\Listener;

/**
 * Notified when an {@see \Webrtc\DataChannel\RTCDataChannel} transitions to the closed state.
 *
 * Typed replacement for the former Evenement "close" event; the listener is a plain object
 * captured verbatim by serialization.
 */
interface DataChannelCloseListener
{
    public function onDataChannelClose(): void;
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\DataChannel;

final class RTCDataChannelParameters
{
    /**
     * Create a new RTCDataChannelParameters instance.
     *
     * @param string $label A name describing the data channel.
     * @param int|null $maxPacketLifeTime The maximum packet lifetime in milliseconds.
     * @param int|null $maxRetransmits The maximum number of retransmissions.
     * @param bool $ordered Whether the data channel guarantees in-order delivery.
     * @param string $protocol The name of the subprotocol in use.
     * @param bool $negotiated Whether the data channel is negotiated out-of-band.
     * @param int|null $id A numeric ID for the channel.
     */
    public function __construct(
        public string $label = "",
        public ?int   $maxPacketLifeTime = null,
        public ?int   $maxRetransmits = null,
        public bool   $ordered = true,
        public string $protocol = "",
        public bool   $negotiated = false,
        public ?int   $id = null
    )
    {
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\DataChannel;

use Psr\Log\LoggerInterface;
use Webrtc\DataChannel\Enum\State;
use Webrtc\DataChannel\Listener\DataChannelBufferedAmountLowListener;
use Webrtc\DataChannel\Listener\DataChannelCloseListener;
use Webrtc\DataChannel\Listener\DataChannelMessageListener;
use Webrtc\DataChannel\Listener\DataChannelOpenListener;
use Webrtc\Exception\InvalidArgumentException;
use Webrtc\Exception\RuntimeException;
use Webrtc\Mixin\SerializableState;

/**
 * Represents a bidirectional peer-to-peer data channel for WebRTC communications.
 *
 * The RTCDataChannel interface enables direct communication between peers with:
 * - Configurable reliability (ordered/unordered, retransmission policies)
 * - Flow control via buffered amounted monitoring
 * - Typed-listener interface for state changes
 * - Support for both in-band and out-of-band negotiation
 *
 * Listeners (typed replacements for the former Evenement events):
 * - {@see DataChannelOpenListener}: notified when the channel transitions to the open state
 * - {@see DataChannelCloseListener}: notified when the channel transitions to the closed state
 * - {@see DataChannelMessageListener}: receives application data delivered by the SCTP transport
 * - {@see DataChannelBufferedAmountLowListener}: notified when buffered data falls below the threshold
 *
 * @package Webrtc\DataChannel
 */
final class RTCDataChannel implements RTCDataChannelInterface
{
    private int $bufferedAmount = 0;
    private int $bufferedAmountLowThreshold = 0;
    private ?int $id;
    private RTCDataChannelParameters $parameters;
    private State $readyState = State::Connecting;
    private RTCSctpTransportInterface $transport;
    private bool $sendOpen;
    private ?LoggerInterface $logger = null;

    /** @var \WeakMap<DataChannelOpenListener, null> Listeners notified when the channel opens. */
    private \WeakMap $openListeners;

    /** @var \WeakMap<DataChannelCloseListener, null> Listeners notified when the channel closes. */
    private \WeakMap $closeListeners;

    /** @var \WeakMap<DataChannelMessageListener, null> Listeners receiving application data. */
    private \WeakMap $messageListeners;

    /** @var \WeakMap<DataChannelBufferedAmountLowListener, null> Listeners notified when buffered amount drops below the threshold. */
    private \WeakMap $bufferedAmountLowListeners;

    /**
     * Creates a new RTCDataChannel instance.
     *
     * The constructor handles both in-band and out-of-band negotiated channels:
     * - For in-band: Automatically initiates the opening procedure
     * - For out-of-band: Verifies ID validity and registers with transport
     *
     * @param RTCSctpTransportInterface $transport The SCTP transport layer for data transmission
     * @param RTCDataChannelParameters $parameters Configuration parameters for the channel
     * @param bool $sendOpen Whether to immediately send open request (for in-band)
     * @throws InvalidArgumentException If negotiated channel has invalid ID
     */
    public function __construct(
        RTCSctpTransportInterface         $transport,
        RTCDataChannelParameters $parameters,
        bool                     $sendOpen = true
    )
    {
        /** @var \WeakMap<DataChannelOpenListener, null> */
        $this->openListeners = new \WeakMap();
        /** @var \WeakMap<DataChannelCloseListener, null> */
        $this->closeListeners = new \WeakMap();
        /** @var \WeakMap<DataChannelMessageListener, null> */
        $this->messageListeners = new \WeakMap();
        /** @var \WeakMap<DataChannelBufferedAmountLowListener, null> */
        $this->bufferedAmountLowListeners = new \WeakMap();

        $this->transport = $transport;
        $this->parameters = $parameters;
        $this->id = $parameters->id;
        $this->sendOpen = $sendOpen;

        if ($this->parameters->negotiated && ($this->id === null || $this->id < 0 || $this->id > 65534)) {
            throw new InvalidArgumentException(
                "ID must be in range 0-65534 if data channel is negotiated out-of-band"
            );
        }
        if (!$this->parameters->negotiated) {
            if ($this->sendOpen) {
                $this->sendOpen = false;
                $this->transport->dataChannelOpen($this);
            }
        } else {
            $this->transport->dataChannelAddNegotiated($this);
        }
    }

    /**
     * Register a listener notified when the channel transitions to the open state.
     *
     * Typed replacement for on('open'); the listener is a plain object captured by serialization.
     */
    public function addOpenListener(DataChannelOpenListener $listener): void
    {
        $this->openListeners[$listener] = null;
    }

    /**
     * Register a listener notified when the channel transitions to the closed state.
     *
     * Typed replacement for on('close'); the listener is a plain object captured by serialization.
     */
    public function addCloseListener(DataChannelCloseListener $listener): void
    {
        $this->closeListeners[$listener] = null;
    }

    /**
     * Register a listener receiving application data delivered by the SCTP transport.
     *
     * Typed replacement for on('message'); the listener is a plain object captured by serialization.
     */
    public function addMessageListener(DataChannelMessageListener $listener): void
    {
        $this->messageListeners[$listener] = null;
    }

    /**
     * Register a listener notified when the buffered amount falls below the threshold.
     *
     * Typed replacement for on('bufferedamountlow'); the listener is a plain object captured by serialization.
     */
    public function addBufferedAmountLowListener(DataChannelBufferedAmountLowListener $listener): void
    {
        $this->bufferedAmountLowListeners[$listener] = null;
    }

    /**
     * Deliver application data into this channel.
     *
     * Called by the SCTP transport in place of the former emit("message", [$data]); dispatches to
     * every registered {@see DataChannelMessageListener}.
     *
     * @param string $data The received application data
     */
    public function dispatchMessage(string $data): void
    {
        foreach ($this->messageListeners as $listener => $_) {
            $listener->onDataChannelMessage($data);
        }
    }

    private function notifyOpen(): void
    {
        foreach ($this->openListeners as $listener => $_) {
            $listener->onDataChannelOpen();
        }
    }

    private function notifyClose(): void
    {
        foreach ($this->closeListeners as $listener => $_) {
            $listener->onDataChannelClose();
        }
    }

    private function notifyBufferedAmountLow(): void
    {
        foreach ($this->bufferedAmountLowListeners as $listener => $_) {
            $listener->onDataChannelBufferedAmountLow();
        }
    }

    /**
     * Gets the current amount of buffered outgoing data in bytes.
     *
     * This represents data that has been queued but not yet transmitted.
     * Useful for implementing application-level flow control.
     *
     * @return int Bytes currently buffered (0 indicates empty buffer)
     */
    public function getBufferedAmount(): int
    {
        return $this->bufferedAmount;
    }

    /**
     * Gets the low threshold for buffered amount notifications.
     *
     * When the buffered amount crosses below this value after being above it,
     * a "bufferedamountlow" event will be emitted.
     *
     * @return int Current threshold value in bytes
     */
    public function getBufferedAmountLowThreshold(): int
    {
        return $this->bufferedAmountLowThreshold;
    }

    /**
     * Sets the low threshold for buffered amount notifications.
     *
     * @param int $value New threshold in bytes (0-4294967295)
     * @throws InvalidArgumentException If value is outside valid range
     */
    public function setBufferedAmountLowThreshold(int $value): void
    {
        if ($value < 0 || $value > 4294967295) {
            throw new InvalidArgumentException("bufferedAmountLowThreshold must be in range 0 - 4294967295");
        }
        $this->bufferedAmountLowThreshold = $value;
    }

    /**
     * Checks if the channel was negotiated out-of-band.
     *
     * Out-of-band negotiated channels skip the in-band opening procedure
     * and must have pre-arranged IDs.
     *
     * @return bool True if negotiated out-of-band, false for in-band
     */
    public function isNegotiated(): bool
    {
        return $this->parameters->negotiated;
    }

    /**
     * Gets the channel's unique identifier.
     *
     * The ID is assigned either:
     * - Automatically for in-band negotiated channels
     * - Explicitly for out-of-band negotiated channels
     *
     * @return int|null The channel ID (null if not yet assigned)
     */
    public function getId(): ?int
    {
        return $this->id;
    }

    /**
     * Gets the channel's label.
     *
     * The label provides a human-readable identifier for the channel's purpose.
     *
     * @return string The channel label
     */
    public function getLabel(): string
    {
        return $this->parameters->label;
    }

    /**
     * Checks if the channel guarantees message ordering.
     *
     * When ordered=true, messages are delivered in the order they were sent.
     * When ordered=false, messages may be delivered out of order.
     *
     * @return bool True if ordered delivery is enabled
     */
    public function isOrdered(): bool
    {
        return $this->parameters->ordered;
    }

    /**
     * Gets the maximum packet lifetime (in milliseconds).
     *
     * This specifies how long the implementation will attempt to transmit a message
     * before giving up (for unreliable channels).
     *
     * @return int|null Lifetime in ms or null if using default
     */
    public function getMaxPacketLifeTime(): ?int
    {
        return $this->parameters->maxPacketLifeTime;
    }

    /**
     * Gets the maximum number of retransmission attempts.
     *
     * This specifies how many times a message will be retransmitted before giving up
     * (for unreliable channels).
     *
     * @return int|null Maximum retransmits or null if using default
     */
    public function getMaxRetransmits(): ?int
    {
        return $this->parameters->maxRetransmits;
    }

    /**
     * Gets the application-specific subprotocol.
     *
     * The protocol string identifies the format/meaning of messages exchanged
     * over the channel (similar to WebSocket subprotocols).
     *
     * @return string The protocol identifier
     */
    public function getProtocol(): string
    {
        return $this->parameters->protocol;
    }

    /**
     * Gets the current connection state.
     *
     * Possible states:
     * - Connecting: Channel is being established
     * - Open: Ready for data transfer
     * - Closing: Channel is being closed
     * - Closed: Channel is fully closed
     *
     * @return State Current state enum value
     */
    public function getReadyState(): State
    {
        return $this->readyState;
    }

    /**
     * Gets the underlying SCTP transport.
     *
     * @return RTCSctpTransportInterface The transport instance
     */
    public function getTransport(): RTCSctpTransportInterface
    {
        return $this->transport;
    }

    /**
     * Initiates channel closure.
     *
     * This begins an orderly shutdown of the data channel.
     * The "close" event will be emitted when the shutdown is complete.
     */
    public function close(): void
    {
        $this->transport->dataChannelClose($this);
    }

    /**
     * Sends data through the channel.
     *
     * @param string $data The binary or text data to send
     * @throws RuntimeException If a channel is not in open state
     */
    public function send(string $data): void
    {
        if ($this->readyState != State::Open) {
            throw new RuntimeException("Data channel is not open");
        }

        $this->transport->dataChannelSend($this, $data);
    }

    /**
     * Updates the buffered amount and checks threshold crossing.
     *
     * The transport layer uses this internal method to:
     * 1. Track queued data
     * 2. Trigger "bufferedamountlow" events when appropriate
     *
     * @param int $amount The number of bytes to add
     */
    public function addBufferedAmount(int $amount): void
    {
        $crossesThreshold = (
            $this->bufferedAmount > $this->bufferedAmountLowThreshold &&
            $this->bufferedAmount + $amount <= $this->bufferedAmountLowThreshold
        );

        $this->bufferedAmount += $amount;

        if ($crossesThreshold) {
            $this->notifyBufferedAmountLow();
        }
    }

    /**
     * Assigns the channel ID.
     *
     * Note: This should only be called by the transport layer during setup.
     *
     * @param int $id The channel identifier to assign
     */
    public function setId(int $id): void
    {
        $this->id = $id;
    }

    /**
     * Updates the channel's ready state.
     *
     * This internal method handles state transitions and notifies registered listeners:
     * - {@see DataChannelOpenListener} when transitioning to Open state
     * - {@see DataChannelCloseListener} when transitioning to Closed state
     *
     * @param State $state The new state to transition to
     */
    public function setReadyState(State $state): void
    {
        if ($state != $this->readyState) {
            $this->log(sprintf(" Change Datachannel(%s) state from %s to %s", $this->parameters->label, $this->readyState->name, $state->name));
            $this->readyState = $state;

            if ($state == State::Open) {
                $this->notifyOpen();
            } elseif ($state == State::Closed) {
                $this->notifyClose();
                /** @var \WeakMap<DataChannelOpenListener, null> */
                $this->openListeners = new \WeakMap();
                /** @var \WeakMap<DataChannelCloseListener, null> */
                $this->closeListeners = new \WeakMap();
                /** @var \WeakMap<DataChannelMessageListener, null> */
                $this->messageListeners = new \WeakMap();
                /** @var \WeakMap<DataChannelBufferedAmountLowListener, null> */
                $this->bufferedAmountLowListeners = new \WeakMap();
            }
        }
    }

    /**
     * @param string $msg
     * @return void
     */
    private function log(string $msg): void
    {
        $this->logger?->debug(sprintf("RTCDataChannel(%s) ", $msg));
    }

    /**
     * @return bool
     */
    public function getOrdered(): bool
    {
        return $this->parameters->ordered;
    }

    /**
     * Attaches a logger for debug output.
     *
     * @param LoggerInterface $logger The PSR-3 compatible logger instance
     */
    public function setLogger(LoggerInterface $logger): void
    {
        $this->logger = $logger;
    }

    /**
     * @return array<string, mixed>
     */
    public function __serialize(): array
    {
        $state = SerializableState::export($this, [
            // WeakMaps cannot be serialized; snapshot their keys and rebuild on the far side.
            'openListeners' => ['__uninitialized' => true],
            'closeListeners' => ['__uninitialized' => true],
            'messageListeners' => ['__uninitialized' => true],
            'bufferedAmountLowListeners' => ['__uninitialized' => true],
        ]);
        $state['__openListeners'] = SerializableState::weakMapToList($this->openListeners);
        $state['__closeListeners'] = SerializableState::weakMapToList($this->closeListeners);
        $state['__messageListeners'] = SerializableState::weakMapToList($this->messageListeners);
        $state['__bufferedAmountLowListeners'] = SerializableState::weakMapToList($this->bufferedAmountLowListeners);

        return $state;
    }

    /**
     * @param array<string, mixed> $data
     */
    public function __unserialize(array $data): void
    {
        /** @var list<DataChannelOpenListener> $openListeners */
        $openListeners = $data['__openListeners'] ?? [];
        /** @var list<DataChannelCloseListener> $closeListeners */
        $closeListeners = $data['__closeListeners'] ?? [];
        /** @var list<DataChannelMessageListener> $messageListeners */
        $messageListeners = $data['__messageListeners'] ?? [];
        /** @var list<DataChannelBufferedAmountLowListener> $bufferedAmountLowListeners */
        $bufferedAmountLowListeners = $data['__bufferedAmountLowListeners'] ?? [];
        unset(
            $data['__openListeners'],
            $data['__closeListeners'],
            $data['__messageListeners'],
            $data['__bufferedAmountLowListeners'],
        );

        SerializableState::import($this, $data);
        /** @var \WeakMap<DataChannelOpenListener, null> */
        $this->openListeners = SerializableState::listToWeakMap($openListeners);
        /** @var \WeakMap<DataChannelCloseListener, null> */
        $this->closeListeners = SerializableState::listToWeakMap($closeListeners);
        /** @var \WeakMap<DataChannelMessageListener, null> */
        $this->messageListeners = SerializableState::listToWeakMap($messageListeners);
        /** @var \WeakMap<DataChannelBufferedAmountLowListener, null> */
        $this->bufferedAmountLowListeners = SerializableState::listToWeakMap($bufferedAmountLowListeners);
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\DataChannel;

interface RTCDataChannelInterface
{

}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\DataChannel\Enum;

enum State
{
    case Connecting;
    case Open;
    case Closed;
    case Closing;
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\DataChannel;

interface RTCSctpTransportInterface
{
    public function dataChannelOpen(RTCDataChannel $channel): void;

    public function dataChannelAddNegotiated(RTCDataChannel $channel): void;

    public function dataChannelClose(RTCDataChannel $channel): void;

    public function dataChannelSend(RTCDataChannel $channel, string $data): void;

    public function onReceived(string $data): void;

    public function onErrorOrClosed(): void;
}{
    "name": "danog/php-rtc-datachannel",
    "type": "library",
    "description": "A PHP implementation of WebRTC's RTCDataChannel interface. MadelineProto's PHP 8.2+ fork is a Composer-compatible component of the modular danog/php-rtc stack.",
    "keywords": [
        "webrtc",
        "datachannel"
    ],
    "homepage": "https://github.com/danog/php-rtc-datachannel",
    "license": "BSD-3-Clause",
    "authors": [
        {
            "name": "Amin Yazdanpanah",
            "email": "github@aminyazdanpanah.com"
        },
        {
            "name": "Sana Moniri",
            "email": "s.mo@quasarstream.com"
        },
        {
            "name": "Quasar Stream Community"
        },
        {
            "name": "Daniil Gentili",
            "email": "daniil@daniil.it"
        }
    ],
    "require": {
        "php": "^8.2",
        "danog/php-rtc-exception": "^1.1.5",
        "danog/php-rtc-mixin": "^2.0",
        "psr/log": "^3.0.2"
    },
    "require-dev": {
        "phpunit/phpunit": "^11.5 || ^12.0 || ^13.3.2",
        "bamarni/composer-bin-plugin": "^1.9.1"
    },
    "autoload": {
        "psr-4": {
            "Webrtc\\DataChannel\\": "src"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\Webrtc\\DataChannel\\": "tests"
        }
    },
    "minimum-stability": "stable",
    "scripts": {
        "post-install-cmd": [
            "@composer bin all install --ansi"
        ],
        "post-update-cmd": [
            "@composer bin all update --ansi"
        ]
    },
    "config": {
        "allow-plugins": {
            "bamarni/composer-bin-plugin": true
        }
    },
    "extra": {
        "bamarni-bin": {
            "bin-links": true,
            "forward-command": true
        }
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Webrtc;

use Webrtc\DataChannel\RTCDataChannel;
use Webrtc\DataChannel\RTCDataChannelParameters;
use Webrtc\ICE\Enum\IceGatheringState;
use Webrtc\ICE\RTCIceCandidate;
use Webrtc\RTP\Enum\MediaKind;
use Webrtc\RTP\MediaStreamTrack\MediaStreamTrack;
use Webrtc\RTP\RTCRtpTransceiver;
use Webrtc\RTP\Sender\RTCRtpSender;
use Webrtc\SDP\Enum\SDPDirections;
use Webrtc\SDP\RTCSessionDescription;
use Webrtc\Stats\RTCStatsReport;
use Webrtc\Webrtc\Enum\ConnectionState;
use Webrtc\Webrtc\Enum\IceConnectionState;
use Webrtc\Webrtc\Enum\SignalingState;

interface RTCPeerConnectionInterface
{
    public function getConnectionState(): ConnectionState;
    public function getIceConnectionState(): IceConnectionState;
    public function getIceGatheringState(): IceGatheringState;
    public function getSignalingState(): SignalingState;
    public function getLocalDescription(): ?RtcSessionDescription;
    public function getRemoteDescription(): ?RtcSessionDescription;
    public function addIceCandidate(RTCIceCandidate $candidate): void;
    public function addTrack(MediaStreamTrack $track): RTCRtpSender;
    public function addTransceiver(MediaKind|MediaStreamTrack $trackOrKind, SDPDirections $direction = SDPDirections::sendrecv): RTCRtpTransceiver;
    public function close(): void;
    public function createAnswer(): RTCSessionDescription;
    public function createOffer(): RTCSessionDescription;
    public function createDataChannel(RTCDataChannelParameters $parameters): RTCDataChannel;
    public function getReceivers(): array;
    public function getSenders(): array;
    public function getStats(): RTCStatsReport;
    public function setLocalDescription(RTCSessionDescription $rtcSessionDescription): void;
    public function setRemoteDescription(RTCSessionDescription $sessionDescription): void;
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Webrtc;

use DateInvalidOperationException;
use Override;
use Psr\Log\LoggerInterface;
use Ramsey\Uuid\Uuid;
use Revolt\EventLoop;
use Random\RandomException;
use Throwable;
use Webrtc\Codecs\Codec;
use Webrtc\Codecs\CodecUtility;
use Webrtc\DataChannel\RTCDataChannel;
use Webrtc\DataChannel\RTCDataChannelParameters;
use Webrtc\DTLS\DTLS\Exception\DTLSException;
use Webrtc\DTLS\DTLS\Exception\RTCCertificateException;
use Webrtc\DTLS\DTLS\Exception\TLSException;
use Webrtc\DTLS\DTLS\RTCCertificate;
use Webrtc\DTLS\DTLS\RTCDtlsTransport;
use Webrtc\DTLS\Listener\DtlsTransportStateChangeListener;
use Webrtc\DTLS\Exception\OpenSSLException;
use Webrtc\DTLS\Exception\SSLException;
use Webrtc\DTLS\Exception\SysCallException;
use Webrtc\DTLS\Exception\ZeroReturnException;
use Webrtc\Exception\InvalidArgumentException;
use Webrtc\Exception\RuntimeException;
use Webrtc\ICE\Enum\IceGatheringState;
use Webrtc\ICE\Enum\IceRole;
use Webrtc\ICE\Enum\IceTransportState;
use Webrtc\ICE\Listener\IceGathererStateChangeListener;
use Webrtc\ICE\Listener\IceTransportStateChangeListener;
use Webrtc\ICE\RTCIceCandidate;
use Webrtc\ICE\RTCIceGatherer;
use Webrtc\ICE\RTCIceParameters;
use Webrtc\ICE\RTCIceServer;
use Webrtc\ICE\RTCIceTransport;
use Webrtc\ICE\RTCIceTransportInterface;
use Webrtc\NTP\NetworkTimeProtocol;
use Webrtc\RTP\Enum\MediaKind;
use Webrtc\RTP\MediaStreamTrack\MediaStreamTrack;
use Webrtc\RTP\MediaStreamTrack\RemoteStreamTrack;
use Webrtc\RTP\MediaStreamTrack\RTCTrackEvent;
use Webrtc\RTP\Receiver\RTCRtpReceiver;
use Webrtc\RTP\RTCRTPDtlsTransportInterface;
use Webrtc\RTP\RTCRtpTransceiver;
use Webrtc\RTP\RtpConstants;
use Webrtc\RTP\Sender\RTCRtpSender;
use Webrtc\RTPParameter\RTCRtcpFeedback;
use Webrtc\RTPParameter\RTCRtpCodecCapability;
use Webrtc\RTPParameter\RTCRtpCodecParameters;
use Webrtc\RTPParameter\RTCRtpDecodingParameters;
use Webrtc\RTPParameter\RTCRtpEncodingParameters;
use Webrtc\RTPParameter\RTCRtpHeaderExtensionParameters;
use Webrtc\RTPParameter\RTCRtpParameters;
use Webrtc\RTPParameter\RTCRtpReceiveParameters;
use Webrtc\RTPParameter\RTCRtpRtxParameters;
use Webrtc\RTPParameter\RTCRtpSendParameters;
use Webrtc\SCTP\Exception\SctpException;
use Webrtc\SCTP\Listener\DataChannelListener;
use Webrtc\SCTP\RTCSctpDtlsTransportInterface;
use Webrtc\SCTP\RTCSctpTransport;
use Webrtc\SDP\DtlsParameter\RTCDtlsParameters;
use Webrtc\SDP\Enum\DtlsRole;
use Webrtc\SDP\Enum\H264Profile;
use Webrtc\SDP\Enum\SDPDirections;
use Webrtc\SDP\GroupDescription;
use Webrtc\SDP\H264Sdp;
use Webrtc\SDP\MediaDescription;
use Webrtc\SDP\RTCSessionDescription;
use Webrtc\SDP\SessionDescription;
use Webrtc\SDP\SsrcDescription;
use Webrtc\Srtp\Exception\SrtpException;
use Webrtc\Stats\enum\TLSState;
use Webrtc\Stats\RTCStatsReport;
use Webrtc\Webrtc\Enum\ConnectionState;
use Webrtc\Webrtc\Enum\IceConnectionState;
use Webrtc\Webrtc\Enum\SignalingState;
use Webrtc\Webrtc\Listener\PeerConnectionConnectionStateChangeListener;
use Webrtc\Webrtc\Listener\PeerConnectionDataChannelListener;
use Webrtc\Webrtc\Listener\PeerConnectionIceConnectionStateChangeListener;
use Webrtc\Webrtc\Listener\PeerConnectionIceGatheringStateChangeListener;
use Webrtc\Webrtc\Listener\PeerConnectionSignalingStateChangeListener;
use Webrtc\Webrtc\Listener\PeerConnectionTrackListener;
use Webrtc\Mixin\SerializableState;

/**
 * RTCPeerConnection represents a WebRTC connection between the local device and a remote peer.
 *
 * This class implements the RTCPeerConnectionInterface and provides methods to create and manage
 * WebRTC connections, including media streaming and data channels.
 *
 * Key Features:
 * - Manages ICE candidates for network connectivity
 * - Handles DTLS for secure communication
 * - Supports media tracks (audio/video)
 * - Provides data channel functionality
 * - Manages connection state and signaling
 *
 * Events:
 * - "connectionstatechange": Fired when the connection state changes
 * - "iceconnectionstatechange": Fired when the ICE connection state changes
 * - "icegatheringstatechange": Fired when the ICE gathering state changes
 * - "signalingstatechange": Fired when the signaling state changes
 * - "track": Fired when a new media track is received
 * - "datachannel": Fired when a new data channel is created by the remote peer
 */
final class RTCPeerConnection implements RTCPeerConnectionInterface, DataChannelListener, IceGathererStateChangeListener, IceTransportStateChangeListener, DtlsTransportStateChangeListener
{
    /**
     * Application listeners for a received remote media track.
     * @var \WeakMap<PeerConnectionTrackListener, null>
     */
    private \WeakMap $trackListeners;

    /**
     * Application listeners for a remotely-opened data channel.
     * @var \WeakMap<PeerConnectionDataChannelListener, null>
     */
    private \WeakMap $peerConnectionDataChannelListeners;

    /**
     * Application listeners for signaling-state changes.
     * @var \WeakMap<PeerConnectionSignalingStateChangeListener, null>
     */
    private \WeakMap $signalingStateChangeListeners;

    /**
     * Application listeners for overall connection-state changes.
     * @var \WeakMap<PeerConnectionConnectionStateChangeListener, null>
     */
    private \WeakMap $connectionStateChangeListeners;

    /**
     * Application listeners for ICE connection-state changes.
     * @var \WeakMap<PeerConnectionIceConnectionStateChangeListener, null>
     */
    private \WeakMap $iceConnectionStateChangeListeners;

    /**
     * Application listeners for ICE gathering-state changes.
     * @var \WeakMap<PeerConnectionIceGatheringStateChangeListener, null>
     */
    private \WeakMap $iceGatheringStateChangeListeners;

    /**
     * Port number used for discard protocol (used as placeholder in SDP)
     */
    private const DISCARD_PORT = 9;

    /**
     * IP address used as placeholder in SDP before real candidates are gathered
     */
    private const DISCARD_HOST = "0.0.0.0";

    /**
     * Configuration object containing ICE servers, certificates, and other settings
     */
    private ?RTCConfigurationInterface $configuration;

    /**
     * List of RTCCertificate objects used for DTLS handshake
     * @var list<RTCCertificate>
     */
    private array $certificates = [];

    /**
     * Canonical Name (CNAME) used in RTCP packets for synchronization
     */
    private string $cname;

    /**
     * List of all active DTLS transports for media and data channels
     * @var array<int, RTCDtlsTransport>
     */
    private array $dtlsTransports = [];

    /**
     * List of all active ICE transports for network connectivity
     * @var array<int, RTCIceTransport>
     */
    private array $iceTransports = [];

    /**
     * Remote DTLS parameters indexed by transport object ID
     * @var array<int, RTCDtlsParameters|null>
     */
    private array $remoteDtlsParameter = [];

    /**
     * Remote ICE parameters indexed by transport object ID
     * @var array<int, RTCIceParameters|null>
     */
    private array $remoteIceParameters = [];

    /**
     * List of media stream identifiers (MIDs) that have been processed
     * @var array<string, true>
     */
    private array $seenMids = [];

    /**
     * SCTP transport for data channels (null until first data channel is created)
     */
    private ?RTCSctpTransport $sctp = null;

    /**
     * Flag indicating whether to use legacy SDP format for SCTP
     */
    private bool $sctpLegacySdp = true;

    /**
     * Remote SCTP port number (null until set by remote description)
     */
    private ?int $sctpRemotePort = null;

    /**
     * Whether the SCTP association runs in-band over the media transport, without an SDP
     * `m=application` section (see {@see self::createInbandSctp()}).
     */
    private bool $sctpInband = false;

    /**
     * Unique identifier for this peer connection's media streams
     */
    private string $streamId;

    /**
     * List of all media transceivers in this connection
     * @var list<RTCRtpTransceiver>
     */
    private array $transceivers = [];

    /**
     * Overall connection state (connected, disconnected, failed, etc.)
     * @var ConnectionState
     */
    private ConnectionState $connectionState = ConnectionState::new;

    /**
     * ICE connection state (checking, completed, failed, etc.)
     * @var IceConnectionState
     */
    private IceConnectionState $iceConnectionState = IceConnectionState::new;

    /**
     * ICE candidate gathering state (new, gathering, complete)
     * @var IceGatheringState
     */
    private IceGatheringState $iceGatheringState = IceGatheringState::new;

    /**
     * Signaling state for offer/answer negotiation
     * @var SignalingState
     */
    private SignalingState $signalingState = SignalingState::stable;

    /**
     * Flag indicating whether the connection has been closed
     */
    private bool $isClosed = false;

    /**
     * Current local description that has been successfully negotiated
     */
    private ?SessionDescription $currentLocalDescription = null;

    /**
     * Current remote description that has been successfully negotiated
     */
    private ?SessionDescription $currentRemoteDescription = null;

    /**
     * Local description that is pending negotiation
     */
    private ?SessionDescription $pendingLocalDescription = null;

    /**
     * Remote description that is pending negotiation
     */
    private ?SessionDescription $pendingRemoteDescription = null;

    /**
     * Logger instance for debugging output (optional)
     */
    private ?LoggerInterface $logger = null;

    /**
     * Whether a scheduled connect() is currently running.
     */
    private bool $connecting = false;

    /**
     * Whether a description was applied while connect() was running.
     */
    private bool $connectPending = false;

    /**
     * Creates a new RTCPeerConnection instance.
     *
     * @param array{
     *     iceServers?: list<array{
     *         urls: string|list<string>,
     *         username?: string,
     *         credential?: string,
     *         credentialType?: string
     *     }>,
     *     certificatePath?: string,
     *     privateKeyPath?: string
     * }|RTCConfigurationInterface|null $configuration Configuration options for the connection
     * @throws RTCCertificateException If certificate generation fails
     * @throws OpenSSLException|DateInvalidOperationException If there's an SSL-related error
     */
    public function __construct(null|array|RTCConfigurationInterface $configuration = null)
    {
        $this->configuration = $configuration instanceof RTCConfigurationInterface ? $configuration : new RTCConfiguration($configuration);
        $this->certificates[] = new RTCCertificate($this->configuration->getPrivateKeyPath(), $this->configuration->getCertificatePath());
        $this->cname = Uuid::uuid4()->toString();
        $this->streamId = Uuid::uuid4()->toString();

        /** @var \WeakMap<PeerConnectionTrackListener, null> */
        $this->trackListeners = new \WeakMap();
        /** @var \WeakMap<PeerConnectionDataChannelListener, null> */
        $this->peerConnectionDataChannelListeners = new \WeakMap();
        /** @var \WeakMap<PeerConnectionSignalingStateChangeListener, null> */
        $this->signalingStateChangeListeners = new \WeakMap();
        /** @var \WeakMap<PeerConnectionConnectionStateChangeListener, null> */
        $this->connectionStateChangeListeners = new \WeakMap();
        /** @var \WeakMap<PeerConnectionIceConnectionStateChangeListener, null> */
        $this->iceConnectionStateChangeListeners = new \WeakMap();
        /** @var \WeakMap<PeerConnectionIceGatheringStateChangeListener, null> */
        $this->iceGatheringStateChangeListeners = new \WeakMap();
    }

    /**
     * Register a listener notified when a remote media track is received (was the "track" event).
     */
    public function addTrackListener(PeerConnectionTrackListener $listener): void
    {
        $this->trackListeners[$listener] = null;
    }

    /**
     * Register a listener notified when a remotely-opened data channel arrives (was "datachannel").
     */
    public function addPeerConnectionDataChannelListener(PeerConnectionDataChannelListener $listener): void
    {
        $this->peerConnectionDataChannelListeners[$listener] = null;
    }

    /**
     * Register a listener notified when the signaling state changes (was "signalingstatechange").
     */
    public function addSignalingStateChangeListener(PeerConnectionSignalingStateChangeListener $listener): void
    {
        $this->signalingStateChangeListeners[$listener] = null;
    }

    /**
     * Register a listener notified when the connection state changes (was "connectionstatechange").
     */
    public function addConnectionStateChangeListener(PeerConnectionConnectionStateChangeListener $listener): void
    {
        $this->connectionStateChangeListeners[$listener] = null;
    }

    /**
     * Register a listener notified when the ICE connection state changes (was
     * "iceconnectionstatechange").
     */
    public function addIceConnectionStateChangeListener(PeerConnectionIceConnectionStateChangeListener $listener): void
    {
        $this->iceConnectionStateChangeListeners[$listener] = null;
    }

    /**
     * Register a listener notified when the ICE gathering state changes (was
     * "icegatheringstatechange").
     */
    public function addIceGatheringStateChangeListener(PeerConnectionIceGatheringStateChangeListener $listener): void
    {
        $this->iceGatheringStateChangeListeners[$listener] = null;
    }

    private function notifyTrack(MediaStreamTrack $track): void
    {
        foreach ($this->trackListeners as $listener => $_) {
            $listener->onPeerConnectionTrack($track);
        }
    }

    private function notifyPeerConnectionDataChannel(RTCDataChannel $channel): void
    {
        foreach ($this->peerConnectionDataChannelListeners as $listener => $_) {
            $listener->onPeerConnectionDataChannel($channel);
        }
    }

    private function notifySignalingStateChange(): void
    {
        foreach ($this->signalingStateChangeListeners as $listener => $_) {
            $listener->onPeerConnectionSignalingStateChange();
        }
    }

    private function notifyConnectionStateChange(): void
    {
        foreach ($this->connectionStateChangeListeners as $listener => $_) {
            $listener->onPeerConnectionConnectionStateChange();
        }
    }

    private function notifyIceConnectionStateChange(): void
    {
        foreach ($this->iceConnectionStateChangeListeners as $listener => $_) {
            $listener->onPeerConnectionIceConnectionStateChange();
        }
    }

    private function notifyIceGatheringStateChange(): void
    {
        foreach ($this->iceGatheringStateChangeListeners as $listener => $_) {
            $listener->onPeerConnectionIceGatheringStateChange();
        }
    }

    /**
     * Consumes an ICE gatherer's state-change notification (was the "statechange" callable
     * registered on the gatherer).
     */
    #[\Override]
    public function onIceGathererStateChange(IceGatheringState $state): void
    {
        $this->updateIceGatheringState();
    }

    /**
     * Consumes an ICE transport's state-change notification (was the "statechange" callables
     * registered on the transport).
     */
    #[\Override]
    public function onIceTransportStateChange(IceTransportState $state): void
    {
        $this->updateIceConnectionState();
        $this->updateConnectionState();
    }

    /**
     * Consumes a DTLS transport's state-change notification (was the "statechange" callable
     * registered on the transport).
     */
    #[\Override]
    public function onDtlsTransportStateChange(): void
    {
        $this->updateConnectionState();
    }

    /**
     * Gets the current connection state.
     *
     * @return ConnectionState The current connection state
     */
    #[Override]
    public function getConnectionState(): ConnectionState
    {
        return $this->connectionState;
    }

    /**
     * Sets the connection state and emits "connectionstatechange" event if changed.
     *
     * @param ConnectionState $connectionState The new connection state
     */
    public function setConnectionState(ConnectionState $connectionState): void
    {
        $this->connectionState = $connectionState;
    }

    /**
     * Gets the current ICE connection state.
     *
     * @return IceConnectionState The current ICE connection state
     */
    #[Override]
    public function getIceConnectionState(): IceConnectionState
    {
        return $this->iceConnectionState;
    }

    /**
     * Sets the ICE connection state and emits "iceconnectionstatechange" event if changed.
     *
     * @param IceConnectionState $iceConnectionState The new ICE connection state
     */
    public function setIceConnectionState(IceConnectionState $iceConnectionState): void
    {
        $this->iceConnectionState = $iceConnectionState;
    }

    /**
     * Gets the current ICE gathering state.
     *
     * @return IceGatheringState The current ICE gathering state
     */
    #[Override]
    public function getIceGatheringState(): IceGatheringState
    {
        return $this->iceGatheringState;
    }

    /**
     * Sets the ICE gathering state and emits "icegatheringstatechange" event if changed.
     *
     * @param IceGatheringState $iceGatheringState The new ICE gathering state
     */
    public function setIceGatheringState(IceGatheringState $iceGatheringState): void
    {
        $this->iceGatheringState = $iceGatheringState;
    }

    /**
     * Gets the local description of the connection.
     *
     * @return RtcSessionDescription|null The local session description or null if not set
     */
    #[Override]
    public function getLocalDescription(): ?RtcSessionDescription
    {
        $sdp = $this->pendingLocalDescription ?? $this->currentLocalDescription ?? null;
        if (!$sdp) {
            return null;
        }
        $type = $sdp->getType();
        if ($type === null) {
            throw new RuntimeException("Local description has no type");
        }

        return new RtcSessionDescription((string)$sdp, $type);
    }

    /**
     * Gets the remote description of the connection.
     *
     * @return RtcSessionDescription|null The remote session description or null if not set
     */
    #[Override]
    public function getRemoteDescription(): ?RtcSessionDescription
    {
        $sdp = $this->pendingRemoteDescription ?? $this->currentRemoteDescription ?? null;
        if (!$sdp) {
            return null;
        }
        $type = $sdp->getType();
        if ($type === null) {
            throw new RuntimeException("Remote description has no type");
        }

        return new RtcSessionDescription((string)$sdp, $type);
    }

    /**
     * Gets the SCTP transport used for data channels.
     *
     * @return RTCSctpTransport|null The SCTP transport or null if not established
     */
    public function getSctp(): ?RTCSctpTransport
    {
        return $this->sctp;
    }

    /**
     * Sets the SCTP transport for data channels.
     *
     * @param RTCSctpTransport|null $sctp The SCTP transport to set
     */
    public function setSctp(?RTCSctpTransport $sctp): void
    {
        $this->sctp = $sctp;
    }

    /**
     * Gets the current signaling state.
     *
     * @return SignalingState The current signaling state
     */
    #[Override]
    public function getSignalingState(): SignalingState
    {
        return $this->signalingState;
    }

    /**
     * Sets the signaling state and emits "signalingstatechange" event.
     *
     * @param SignalingState $signalingState The new signaling state
     */
    public function setSignalingState(SignalingState $signalingState): void
    {
        $this->signalingState = $signalingState;
        $this->notifySignalingStateChange();
    }

    /**
     * Sets a logger for debugging purposes.
     *
     * @param LoggerInterface $logger The logger instance
     */
    public function setLogger(LoggerInterface $logger): void
    {
        $this->logger = $logger;
    }

    /**
     * Adds an ICE candidate received from the remote peer.
     *
     * @param RTCIceCandidate $candidate The ICE candidate to add
     * @throws InvalidArgumentException If a candidate is missing required fields
     */
    #[Override]
    public function addIceCandidate(RTCIceCandidate $candidate): void
    {
        $mid = $candidate->getSdpMid();
        $index = $candidate->getSdpMLineIndex();
        if ($mid === null && $index === null) {
            throw new InvalidArgumentException("Candidate must have either sdpMid or sdpMLineIndex");
        }

        // As in WebRTC, the sdpMid identifies the media section, falling back to the sdpMLineIndex.
        // A bundled media section already uses the transport of its BUNDLE group.
        $dtlsTransport = $mid !== null ? $this->getDtlsTransportByMid($mid) : $this->getDtlsTransportByMLineIndex($index);
        if ($dtlsTransport === null) {
            // Dropping it would leave ICE without the remote candidate to check.
            throw new InvalidArgumentException($mid !== null
                ? "No media section with mid $mid for the candidate"
                : "No media section at index $index for the candidate");
        }
        $dtlsTransport->getIceTransport()->addRemoteCandidate($candidate);
    }

    /**
     * Gets the transport of the media section with the specified mid.
     */
    private function getDtlsTransportByMid(int $mid): ?RTCDtlsTransport
    {
        foreach ($this->transceivers as $transceiver) {
            if ($transceiver->getMid() === (string) $mid) {
                return $this->requireDtlsTransport($transceiver->getDtlsTransport());
            }
        }
        if ($this->sctp && $this->sctp->getMid() === (string) $mid) {
            return $this->requireDtlsTransport($this->sctp->getDtlsTransport());
        }
        return null;
    }

    /**
     * Gets the transport of the media section at the specified index.
     */
    private function getDtlsTransportByMLineIndex(int $index): ?RTCDtlsTransport
    {
        $transceiver = $this->getTransceiverByMLineIndex($index);
        if ($transceiver !== null) {
            return $this->requireDtlsTransport($transceiver->getDtlsTransport());
        }
        $description = $this->pendingRemoteDescription ?? $this->currentRemoteDescription;
        if ($this->sctp && ($description?->getMedia()[$index] ?? null)?->getKind() === "application") {
            return $this->requireDtlsTransport($this->sctp->getDtlsTransport());
        }
        return null;
    }

    /**
     * Adds a media track to be sent to the remote peer.
     *
     * @param MediaStreamTrack $track The media track to add
     * @return RTCRtpSender The sender associated with the track
     * @throws OpenSSLException
     * @throws RandomException
     * @throws SrtpException
     */
    #[Override]
    public function addTrack(MediaStreamTrack $track): RTCRtpSender
    {
        $this->checkNotClosed();

        if (!in_array($track->getKind(), [MediaKind::Audio, MediaKind::Video])) {
            throw new InvalidArgumentException("Invalid track kind {$track->getKind()->value}");
        }

        $this->checkTrackHasNoSender($track);

        foreach ($this->transceivers as $transceiver) {
            if ($transceiver->getKind() === $track->getKind() && !$transceiver->getSender()->getTrack()) {
                $transceiver->getSender()->replaceTrack($track);
                $direction = $transceiver->getDirection();
                assert($direction !== null);
                $transceiver->setDirection(SDPDirections::from($direction->value | SDPDirections::sendonly->value));
                return $transceiver->getSender();
            }
        }
        $transceiver = $this->createTransceiver($track->getKind(), SDPDirections::sendrecv, $track);

        return $transceiver->getSender();
    }

    /**
     * Adds a new transceiver to the connection.
     *
     * @param MediaKind|MediaStreamTrack $trackOrKind Either a media kind or track
     * @param SDPDirections $direction The direction of the transceiver
     * @return RTCRtpTransceiver The created transceiver
     * @throws OpenSSLException
     * @throws RandomException
     * @throws SrtpException
     */
    #[Override]
    public function addTransceiver(MediaKind|MediaStreamTrack $trackOrKind, SDPDirections $direction = SDPDirections::sendrecv): RTCRtpTransceiver
    {
        $this->checkNotClosed();

        if ($trackOrKind instanceof MediaStreamTrack) {
            $kind = $trackOrKind->getKind();
            $track = $trackOrKind;
        } else {
            $kind = $trackOrKind;
            $track = null;
        }

        if (!in_array($kind, [MediaKind::Audio, MediaKind::Video])) {
            throw new RuntimeException("Invalid track kind $kind->value");
        }

        if ($track) {
            $this->checkTrackHasNoSender($track);
        }

        return $this->createTransceiver($kind, $direction, $track);
    }

    /**
     * Closes the peer connection and terminates all transports.
     *
     * @throws OpenSSLException
     * @throws SrtpException If there's an error stopping SRTP
     * @throws TLSException
     * @throws SSLException
     * @throws SysCallException
     * @throws ZeroReturnException
     */
    #[Override]
    public function close(): void
    {
        if ($this->isClosed) {
            return;
        }

        foreach ($this->transceivers as $transceiver) {
            $transceiver->stop();
            $dtlsTransport = $this->requireDtlsTransport($transceiver->getDtlsTransport());
            $dtlsTransport->stop();
            $this->requireIceTransport($dtlsTransport->getIceTransport())->stop();
        }

        if (isset($this->sctp)) {
            $this->sctp->stop();
            // An in-band association shares the first transceiver's transport, stopped above.
            if (!$this->sctpInband) {
                $dtlsTransport = $this->requireDtlsTransport($this->sctp->getDtlsTransport());
                $dtlsTransport->stop();
                $this->requireIceTransport($dtlsTransport->getIceTransport())->stop();
            }
        }

        $this->isClosed = true;
        $this->setSignalingState(SignalingState::closed);

        $this->updateIceGatheringState();
        $this->updateIceConnectionState();
        $this->updateConnectionState();

        /** @var \WeakMap<PeerConnectionTrackListener, null> */
        $this->trackListeners = new \WeakMap();
        /** @var \WeakMap<PeerConnectionDataChannelListener, null> */
        $this->peerConnectionDataChannelListeners = new \WeakMap();
        /** @var \WeakMap<PeerConnectionSignalingStateChangeListener, null> */
        $this->signalingStateChangeListeners = new \WeakMap();
        /** @var \WeakMap<PeerConnectionConnectionStateChangeListener, null> */
        $this->connectionStateChangeListeners = new \WeakMap();
        /** @var \WeakMap<PeerConnectionIceConnectionStateChangeListener, null> */
        $this->iceConnectionStateChangeListeners = new \WeakMap();
        /** @var \WeakMap<PeerConnectionIceGatheringStateChangeListener, null> */
        $this->iceGatheringStateChangeListeners = new \WeakMap();
    }

    /**
     * Creates an SDP answer to a remote offer.
     *
     * @return RTCSessionDescription The answer
     * @throws InvalidArgumentException If called in invalid signaling state
     */
    #[Override]
    public function createAnswer(): RTCSessionDescription
    {
        $this->checkNotClosed();

        if (!in_array($this->signalingState, [SignalingState::haveRemoteOffer, SignalingState::haveLocalPranswer])) {
            throw new InvalidArgumentException("Cannot create answer in signaling state {$this->signalingState->name}");
        }

        [$sessionDescription, $groupDescription] = $this->initSessionDescription("answer");

        $remoteDescription = $this->pendingRemoteDescription ?? $this->currentRemoteDescription;
        if ($remoteDescription === null) {
            throw new InvalidArgumentException("Cannot create answer: no remote description is set");
        }

        foreach ($remoteDescription->getMedia() as $remoteMediaStream) {
            if (in_array($remoteMediaStream->getKind(), ["audio", "video"])) {
                $transceiver = $this->getTransceiverByMid($remoteMediaStream->getRtp()->muxId);
                if ($transceiver === null) {
                    throw new RuntimeException("Cannot create answer: no transceiver found for mid {$remoteMediaStream->getRtp()->muxId}");
                }
                $mediaDescription = $this->createMediaDescriptionForTransceiver($transceiver);
                $dtlsTransport = $this->requireDtlsTransport($transceiver->getDtlsTransport());
            } else {
                if ($this->sctp === null) {
                    throw new RuntimeException("Cannot create answer: no SCTP transport available");
                }
                $mediaDescription = $this->createMediaDescriptionForSctp();
                $dtlsTransport = $this->requireDtlsTransport($this->sctp->getDtlsTransport());
            }

            $dtls = $mediaDescription->getDtls();
            if ($dtls === null) {
                throw new RuntimeException("Cannot create answer: media description has no DTLS parameters");
            }
            $dtls->role = $dtlsTransport->getRole() == DtlsRole::Auto ? DtlsRole::Client : $dtlsTransport->getRole();
            $sessionDescription->addMedia($mediaDescription);
            $groupDescription->items[] = $mediaDescription->getRtp()->muxId;
        }

        $sessionDescription->addGroup($groupDescription);

        $type = $sessionDescription->getType();
        if ($type === null) {
            throw new RuntimeException("Cannot create answer: no description type available");
        }

        return new RTCSessionDescription((string)$sessionDescription, $type);
    }

    /**
     * Creates a media description for a transceiver.
     *
     * @param RTCRtpTransceiver $transceiver The transceiver to describe
     * @param string|null $mid The media identifier
     * @return MediaDescription The created media description
     * @throws OpenSSLException
     */
    private function createMediaDescriptionForTransceiver(RTCRtpTransceiver $transceiver, ?string $mid = null): MediaDescription
    {
        $payloadTypes = [];
        foreach ($transceiver->getCodecs() as $codec) {
            assert($codec->payloadType !== null);
            $payloadTypes[] = $codec->payloadType;
        }

        $mediaDescription = new MediaDescription(
            $transceiver->getKind()->value,
            self::DISCARD_PORT,
            "UDP/TLS/RTP/SAVPF",
            $payloadTypes
        );
        $mediaDescription->setDirection($this->combinedDirection($transceiver));
        $mediaDescription->setMsid("{$transceiver->getSender()->getStreamId()} {$transceiver->getSender()->getTrackId()}");
        $midValue = $mid;
        if ($midValue === null) {
            $midValue = $transceiver->getMid();
        }
        if ($midValue === null) {
            throw new RuntimeException("Unable to determine a media identifier for the transceiver");
        }
        /** @var list<RTCRtpHeaderExtensionParameters> $headerExtensions */
        $headerExtensions = $transceiver->getHeaderExtensions();
        $mediaDescription->setRtp(new RTCRtpParameters($transceiver->getCodecs(), $headerExtensions, $midValue));
        $mediaDescription->setRtcpHost(self::DISCARD_HOST);
        $mediaDescription->setRtcpPort(self::DISCARD_PORT);
        $mediaDescription->setRtcpMux(true);
        $mediaDescription->setSsrc([new SsrcDescription($transceiver->getSender()->getSsrc(), $this->cname)]);

        $rtxCodecs = array_filter($mediaDescription->getRtp()->codecs, fn($codec) => CodecUtility::isRtx($codec));
        if (!empty($rtxCodecs)) {
            $mediaDescription->addSsrc(new SsrcDescription($transceiver->getSender()->getRtxSsrc(), $this->cname));
            $mediaDescription->setSsrcGroup([new GroupDescription("FID", [$transceiver->getSender()->getSsrc(), $transceiver->getSender()->getRtxSsrc()])]);
        }

        $this->addTransportDescription($mediaDescription, $this->requireDtlsTransport($transceiver->getDtlsTransport()));

        return $mediaDescription;
    }

    /**
     * Adds transport information to a media description.
     *
     * @param MediaDescription $mediaDescription The description to augment
     * @param RTCDtlsTransport $dtlsTransport The transport to describe
     * @throws OpenSSLException
     */
    private function addTransportDescription(MediaDescription $mediaDescription, RTCDtlsTransport $dtlsTransport): void
    {
        // ice
        $iceTransport = $dtlsTransport->getIceTransport();
        $iceGatherer = $iceTransport->getIceGatherer();
        $mediaDescription->setIceCandidates($iceGatherer->getLocalCandidates());
        $mediaDescription->setIceCandidatesComplete($iceGatherer->getState() === IceGatheringState::complete);
        $mediaDescription->setIce($iceGatherer->getLocalParameters());

        if (!empty($mediaDescription->getIceCandidates())) {
            $mediaDescription->setHost($mediaDescription->getIceCandidates()[0]->getHost());
            $mediaDescription->setPort($mediaDescription->getIceCandidates()[0]->getPort());
        } else {
            $mediaDescription->setHost(self::DISCARD_HOST);
            $mediaDescription->setPort(self::DISCARD_PORT);
        }

        // dtls
        $dtls = $mediaDescription->getDtls();
        if ($dtls === null) {
            $mediaDescription->setDtls($dtlsTransport->getLocalParameters());
        } else {
            $dtls->fingerprints = $dtlsTransport->getLocalParameters()->fingerprints;
        }
    }

    /**
     * Gets a transceiver by its media identifier.
     *
     * @param string $muxId The media identifier
     * @return RTCRtpTransceiver|null The transceiver or null if not found
     */
    private function getTransceiverByMid(string $muxId): ?RTCRtpTransceiver
    {
        /** @var RTCRtpTransceiver|null $result */
        $result = array_find($this->transceivers, fn(RTCRtpTransceiver $transceiver) => $transceiver->getMid() === $muxId);

        return $result;
    }

    /**
     * Gets a transceiver by its media line index.
     *
     * @param int $index The media line index
     * @return RTCRtpTransceiver|null The transceiver or null if not found
     */
    private function getTransceiverByMLineIndex(int $index): ?RTCRtpTransceiver
    {
        /** @var RTCRtpTransceiver|null $result */
        $result = array_find($this->transceivers, fn(RTCRtpTransceiver $transceiver) => $transceiver->getMlineIndex() === $index);

        return $result;
    }

    /**
     * Computes the direction resulting from intersecting a transceiver direction with its offer direction.
     *
     * @param RTCRtpTransceiver $transceiver The transceiver to evaluate
     * @return SDPDirections The combined direction
     */
    private function combinedDirection(RTCRtpTransceiver $transceiver): SDPDirections
    {
        $offerDirection = $transceiver->getOfferDirection();
        if ($offerDirection === null) {
            $offerDirection = SDPDirections::sendrecv;
        }

        $currentDirection = $transceiver->getDirection();
        assert($currentDirection !== null);

        return SDPDirections::from($currentDirection->value & $offerDirection->value);
    }

    /**
     * Creates a media description for the SCTP transport.
     *
     * @param string|null $mid The media identifier
     * @return MediaDescription The created media description
     * @throws OpenSSLException
     */
    private function createMediaDescriptionForSctp(?string $mid = null): MediaDescription
    {
        if ($this->sctp === null) {
            throw new RuntimeException("SCTP transport is not available");
        }

        if ($this->sctpLegacySdp) {
            $mediaDescription = new MediaDescription("application", self::DISCARD_PORT, "DTLS/SCTP", [$this->sctp->getPort()]);
            $mediaDescription->setSctpMap([$this->sctp->getPort() => "webrtc-datachannel {$this->sctp->getOutboundStreamsCount()}"]);
        } else {
            $mediaDescription = new MediaDescription("application", self::DISCARD_PORT, "UDP/DTLS/SCTP", ["webrtc-datachannel"]);
            $mediaDescription->setSctpPort($this->sctp->getPort());
        }

        $muxId = $mid;
        if ($muxId === null) {
            $muxId = $this->sctp->getMid();
        }
        if ($muxId === null) {
            throw new RuntimeException("Unable to determine a media identifier for the SCTP transport");
        }
        $mediaDescription->getRtp()->muxId = $muxId;
        $mediaDescription->setSctpCapabilities($this->sctp->getCapabilities());

        $this->addTransportDescription($mediaDescription, $this->requireDtlsTransport($this->sctp->getDtlsTransport()));

        return $mediaDescription;
    }

    /**
     * Creates a data channel for sending arbitrary data.
     *
     * @param RTCDataChannelParameters $parameters Configuration for the data channel
     * @return RTCDataChannel The created data channel
     * @throws OpenSSLException
     * @throws RandomException
     * @throws SctpException
     * @throws SrtpException
     */
    #[Override]
    public function createDataChannel(RTCDataChannelParameters $parameters): RTCDataChannel
    {
        if ($parameters->maxPacketLifeTime !== null && $parameters->maxRetransmits !== null) {
            throw new InvalidArgumentException("Cannot specify both maxPacketLifeTime and maxRetransmits");
        }

        if (!$this->sctp) {
            $this->createSctpTransport();
        }

        if ($this->sctp === null) {
            throw new RuntimeException("Unable to create the SCTP transport");
        }

        return new RTCDataChannel($this->sctp, $parameters);
    }

    /**
     * Run the SCTP association in-band over the media transport, outside SDP.
     *
     * Standard WebRTC negotiates data channels through an `m=application` section; tgcalls (both in
     * one-to-one calls and towards the group call SFU) instead opens an SCTP association straight
     * over the DTLS transport that carries the media, with fixed ports and no SDP at all. This sets
     * that up: the association is bound to the first transceiver's DTLS transport (the BUNDLE master),
     * is never described in offers or answers, and starts as soon as that transport connects.
     * Data channels are then created with {@see self::createDataChannel()} as usual, or arrive from
     * the peer through the data channel listener.
     *
     * @param bool|null $client Whether to initiate the association (and open the channels): true or
     *                          false to force it, null to follow the ICE role like standard WebRTC.
     * @param int       $remotePort The peer's SCTP port.
     * @throws RuntimeException If no transceiver exists yet to carry the association, or SCTP is
     *                          already set up through SDP.
     */
    public function createInbandSctp(?bool $client = null, int $remotePort = 5000): void
    {
        $this->checkNotClosed();
        if ($this->sctp !== null) {
            if ($this->sctpInband) {
                return;
            }
            throw new RuntimeException("The SCTP association is already negotiated through SDP");
        }
        $master = $this->transceivers[0] ?? null;
        if ($master === null) {
            throw new RuntimeException("An in-band SCTP association needs a transceiver whose transport it can share");
        }
        $this->sctp = new RTCSctpTransport($this->requireDtlsTransport($master->getDtlsTransport()));
        $this->sctp->setLogger($this->logger);
        $this->sctp->addDataChannelListener($this);
        $this->sctp->setBundled(true);
        $this->sctp->setClientRole($client);
        $this->sctpInband = true;
        $this->sctpRemotePort = $remotePort;
        $this->scheduleConnect();
    }

    /**
     * Whether the SCTP association runs in-band (see {@see self::createInbandSctp()}).
     */
    public function isSctpInband(): bool
    {
        return $this->sctpInband;
    }

    /**
     * Creates an SCTP transport if none exists.
     *
     * @throws OpenSSLException
     * @throws RandomException If random number generation fails
     * @throws SctpException If SCTP transport creation fails
     * @throws SrtpException
     */
    private function createSctpTransport(): void
    {
        $this->sctp = new RTCSctpTransport($this->createDtlsTransport());
        $this->sctp->setLogger($this->logger);

        $this->sctp->addDataChannelListener($this);
    }

    /**
     * Re-emit a remotely-opened data channel to application listeners (was the re-emitted
     * 'datachannel' event). Typed replacement registered via addDataChannelListener().
     */
    #[\Override]
    public function onDataChannel(RTCDataChannel $channel): void
    {
        $this->notifyPeerConnectionDataChannel($channel);
    }

    /**
     * Creates a DTLS transport.
     *
     * @return RTCDtlsTransport The created DTLS transport
     * @throws OpenSSLException If SSL operation fails
     * @throws RandomException|SrtpException If random number generation fails
     */
    private function createDtlsTransport(): RTCDtlsTransport
    {
        if ($this->configuration === null) {
            throw new RuntimeException("Connection configuration is not available");
        }

        // create ICE transport
        $iceServers = [];
        foreach ($this->configuration->getIceServers() as $iceServer) {
            assert($iceServer instanceof RTCIceServer);
            $iceServers[] = $iceServer;
        }

        $iceGatherer = new RTCIceGatherer($iceServers, $this->configuration->iceSettings(), $this->logger);
        $iceGatherer->addStateChangeListener($this);
        $iceTransport = new RTCIceTransport($iceGatherer, $this->logger);
        $iceTransport->addStateChangeListener($this);
        $this->iceTransports[spl_object_id($iceTransport)] = $iceTransport;

        // create DTLS transport
        $dtlsTransport = new RTCDtlsTransport($iceTransport, $this->certificates[0]);
        $dtlsTransport->setLogger($this->logger);
        $dtlsTransport->addStateChangeListener($this);
        $this->dtlsTransports[spl_object_id($dtlsTransport)] = $dtlsTransport;

        //update states
        $this->updateIceGatheringState();
        $this->updateIceConnectionState();
        $this->updateConnectionState();

        return $dtlsTransport;
    }

    /**
     * Creates an SDP offer to initiate a connection.
     *
     * @return RTCSessionDescription The offer
     * @throws RuntimeException If called with no media or data channels
     */
    #[Override]
    public function createOffer(): RTCSessionDescription
    {
        $this->checkNotClosed();

        if (!$this->sctp && empty($this->transceivers)) {
            throw new RuntimeException("Cannot create an offer with no media and no data channels");
        }

        $codec = new Codec();
        foreach ($this->transceivers as $transceiver) {
            /** @var list<RTCRtpCodecParameters> $codecs */
            $codecs = $codec->getCodecs($transceiver->getKind()->value);
            /** @var list<RTCRtpCodecCapability> $preferredCodecs */
            $preferredCodecs = $transceiver->getPreferredCodecs();
            $transceiver->setCodecs($this->findPreferredCodecs($codecs, $preferredCodecs));
            $headerExtensions = [];
            foreach ($codec->getHeaderExtensions($transceiver->getKind()->value) as $extension) {
                $headerExtensions[] = $extension;
            }
            $transceiver->setHeaderExtensions($headerExtensions);
        }
        $mids = $this->seenMids;

        [$sessionDescription, $groupDescription] = $this->initSessionDescription("offer");

        $remoteDescriptionMedias = ($this->pendingRemoteDescription ?? $this->currentRemoteDescription)?->getMedia() ?? [];
        $localDescriptionMedias = ($this->pendingLocalDescription ?? $this->currentLocalDescription)?->getMedia() ?? [];

        for ($i = 0; $i < max(count($remoteDescriptionMedias), count($localDescriptionMedias)); $i++) {
            $mediaDescription = null;
            $mediaInSection = null;
            if (isset($localDescriptionMedias[$i])) {
                $mediaInSection = $localDescriptionMedias[$i];
            } elseif (isset($remoteDescriptionMedias[$i])) {
                $mediaInSection = $remoteDescriptionMedias[$i];
            }
            $mediaKind = $mediaInSection?->getKind();
            $mid = $mediaInSection?->getRtp()->muxId;
            if (in_array($mediaKind, ["audio", "video"])) {
                $mediaIndex = $mid;
                if ($mediaIndex === null) {
                    throw new RuntimeException("Media section has no media identifier");
                }
                $transceiver = $this->getTransceiverByMid($mediaIndex);
                if ($transceiver === null) {
                    throw new RuntimeException("No transceiver found for media identifier {$mediaIndex}");
                }
                $transceiver->setMlineIndex($i);
                $mediaDescription = $this->createMediaDescriptionForTransceiver($transceiver, $mediaIndex);
            } elseif ($mediaKind == "application") {
                $mediaDescription = $this->createMediaDescriptionForSctp($mid);
            }

            if (isset($mediaDescription)) {
                $sessionDescription->addMedia($mediaDescription);
            }
        }

        foreach ($this->transceivers as $transceiver) {
            if ($transceiver->getMid() === null && !$transceiver->isStopped()) {
                $transceiver->setMlineIndex(count($sessionDescription->getMedia()));
                $sessionDescription->addMedia($this->createMediaDescriptionForTransceiver($transceiver, $this->getFreeMid($mids)));
            }
        }

        if ($this->sctp && !$this->sctpInband && $this->sctp->getMid() === null) {
            $sessionDescription->addMedia($this->createMediaDescriptionForSctp($this->getFreeMid($mids)));
        }

        foreach ($sessionDescription->getMedia() as $media) {
            $groupDescription->items[] = $media->getRtp()->muxId;
        }
        $sessionDescription->addGroup($groupDescription);

        $type = $sessionDescription->getType();
        if ($type === null) {
            throw new RuntimeException("Failed to generate the offer description type");
        }

        return new RTCSessionDescription((string)$sessionDescription, $type);
    }

    /**
     * Initializes a new session description.
     *
     * @param string $type The description type (offer/answer)
     * @return array{SessionDescription, GroupDescription} The session and group descriptions
     */
    private function initSessionDescription(string $type): array
    {
        $ntpSeconds = NetworkTimeProtocol::currentMs() >> 3;

        $sessionDescription = new SessionDescription();
        $sessionDescription->setOrigin("- $ntpSeconds $ntpSeconds IN IP4 0.0.0.0");
        $sessionDescription->appendMsidSemantic(new GroupDescription("WMS", ["*"]));
        $sessionDescription->setType($type);

        $groupDescription = new GroupDescription("BUNDLE", []);

        return [$sessionDescription, $groupDescription];
    }

    /**
     * Finds an unused media identifier.
     *
     * @param array $mids Array of used media identifiers
     * @return string The next available media identifier
     */
    private function getFreeMid(array &$mids): string
    {
        $i = 0;
        while (True) {
            if (!isset($mids[$i])) {
                $mids[$i] = true;
                return (string)$i;
            }
            $i += 1;
        }
    }

    /**
     * Gets all RTP receivers attached to the connection.
     *
     * @return array<RTCRtpReceiver> Array of RTP receivers
     */
    #[Override]
    public function getReceivers(): array
    {
        return array_map(fn($transceiver) => $transceiver->getReceiver(), $this->transceivers);
    }

    /**
     * Gets all RTP senders attached to the connection.
     *
     * @return list<RTCRtpSender> Array of RTP senders
     */
    #[Override]
    public function getSenders(): array
    {
        return array_map(fn($transceiver) => $transceiver->getSender(), $this->transceivers);
    }

    /**
     * Gets statistics for the connection.
     *
     * @return RTCStatsReport The statistics report
     */
    #[Override]
    public function getStats(): RTCStatsReport
    {
        $report = new RTCStatsReport();

        $senders = $this->getSenders();
        $receivers = $this->getReceivers();

        foreach ($senders as $sender) {
            $report->merge($sender->getStats());
        }

        foreach ($receivers as $receiver) {
            $report->merge($receiver->getStats());
        }

        return $report;
    }

    /**
     * Gets all transceivers attached to the connection.
     *
     * @return array<RTCRtpTransceiver> Array of transceivers
     */
    public function getTransceivers(): array
    {
        return $this->transceivers;
    }

    /**
     * Sets the local description for the connection.
     *
     * @param RTCSessionDescription $rtcSessionDescription The description to set
     * @return void Returns once complete
     */
    #[Override]
    public function setLocalDescription(RTCSessionDescription $rtcSessionDescription): void
    {
        $this->debug(sprintf("setLocalDescription(%s)\n%s", $rtcSessionDescription->getType(), $rtcSessionDescription->getSdp()));

        if ($rtcSessionDescription->getType() === "rollback") {
            if (!in_array($this->signalingState, [SignalingState::haveLocalOffer, SignalingState::haveRemotePranswer])) {
                throw new InvalidArgumentException("Cannot roll back a local description in signaling state \"" . $this->signalingState->name . "\"");
            }
            $this->pendingLocalDescription = null;
            $this->setSignalingState(SignalingState::stable);
            return;
        }

        $sessionDescription = SessionDescription::decode($rtcSessionDescription->getSdp());
        $sessionDescription->setType($rtcSessionDescription->getType());
        $this->validateDescription($sessionDescription, true);

        if ($sessionDescription->isType("offer")) {
            $this->setSignalingState(SignalingState::haveLocalOffer);
        } elseif ($sessionDescription->isType("answer")) {
            $this->setSignalingState(SignalingState::stable);
        }

        foreach ($sessionDescription->getMedia() as $index => $media) {
            $this->seenMids[$media->getRtp()->muxId] = true;

            if (in_array($media->getKind(), ["audio", "video"])) {
                $transceiver = $this->getTransceiverByMLineIndex((int)$index);
                if ($transceiver === null) {
                    throw new RuntimeException("No transceiver found for media line {$index}");
                }
                $transceiver->setMid($media->getRtp()->muxId);
            } elseif ($media->getKind() === "application") {
                if ($this->sctp === null) {
                    throw new RuntimeException("No SCTP transport available for data channel media section");
                }
                $this->sctp->setMid($media->getRtp()->muxId);
            }
        }

        if ($sessionDescription->isType("offer")) {
            foreach ($this->iceTransports as $iceTransport) {
                if (!$iceTransport->isRoleSet()) {
                    $iceTransport->getIceConnection()->setIceRole(IceRole::Controlling);
                    $iceTransport->setRoleSet(true);
                }
            }
        }

        if ($sessionDescription->isType("answer")) {
            foreach ($sessionDescription->getMedia() as $index => $media) {
                $dtls = $media->getDtls();
                if ($dtls === null) {
                    throw new RuntimeException("Media description has no DTLS parameters");
                }
                if (in_array($media->getKind(), ["audio", "video"])) {
                    $transceiver = $this->getTransceiverByMLineIndex((int)$index);
                    if ($transceiver === null) {
                        throw new RuntimeException("No transceiver found for media line {$index}");
                    }
                    $dtlsTransport = $this->requireDtlsTransport($transceiver->getDtlsTransport());
                } else {
                    if ($this->sctp === null) {
                        throw new RuntimeException("No SCTP transport available");
                    }
                    $dtlsTransport = $this->requireDtlsTransport($this->sctp->getDtlsTransport());
                }
                $dtlsTransport->setRole($dtls->role);
            }
        }

        if (in_array($sessionDescription->getType(), ["answer", "pranswer"])) {
            foreach ($this->transceivers as $transceiver) {
                $transceiver->setCurrentDirection($this->combinedDirection($transceiver));
            }
        }
        $this->gatherIceCandidates();

        foreach ($sessionDescription->getMedia() as $index => $media) {
            if (in_array($media->getKind(), ["audio", "video"])) {
                $transceiver = $this->getTransceiverByMLineIndex((int)$index);
                if ($transceiver === null) {
                    throw new RuntimeException("No transceiver found for media line {$index}");
                }
                $this->addTransportDescription($media, $this->requireDtlsTransport($transceiver->getDtlsTransport()));
            } elseif ($media->getKind() === "application") {
                if ($this->sctp === null) {
                    throw new RuntimeException("No SCTP transport available");
                }
                $this->addTransportDescription($media, $this->requireDtlsTransport($this->sctp->getDtlsTransport()));
            }
        }

        if ($sessionDescription->isType("answer")) {
            $this->currentLocalDescription = $sessionDescription;
            $this->pendingLocalDescription = null;
        } else {
            $this->pendingLocalDescription = $sessionDescription;
        }

        $this->scheduleConnect();
    }

    /**
     * Sets the remote description for the connection.
     *
     * @param RTCSessionDescription $sessionDescription The description to set
     * @return void Returns once complete
     */
    #[Override]
    public function setRemoteDescription(RTCSessionDescription $sessionDescription): void
    {
        $this->debug(sprintf("setRemoteDescription(%s)\n%s", $sessionDescription->getType(), $sessionDescription->getSdp()));
        if ($sessionDescription->getType() === "rollback") {
            if (!in_array($this->signalingState, [SignalingState::haveRemoteOffer, SignalingState::haveLocalPranswer])) {
                throw new InvalidArgumentException("Cannot roll back a remote description in signaling state \"" . $this->signalingState->name . "\"");
            }
            $this->pendingRemoteDescription = null;
            $this->setSignalingState(SignalingState::stable);
            return;
        }
        $sdp = SessionDescription::decode($sessionDescription->getSdp());
        $sdp->setType($sessionDescription->getType());
        $this->validateDescription($sdp, false);
        /** @var array<int, array{RTCIceTransportInterface, MediaDescription}> $iceCandidates */
        $iceCandidates = [];
        /** @var list<RTCTrackEvent|null> $trackEvents */
        $trackEvents = [];
        foreach ($sdp->getMedia() as $index => $media) {
            $dtlsTransport = null;
            $this->seenMids[$media->getRtp()->muxId] = true;
            if (in_array($media->getKind(), ["audio", "video"])) {
                [$trackEvents [], $dtlsTransport] = $this->getTrackEventAndDtlsTransport((int)$index, $media, $sdp);
            } elseif ($media->getKind() === "application") {
                $dtlsTransport = $this->getSctpDtlsTransport((int)$index, $media);
            }

            if (isset($dtlsTransport)) {
                $this->handleTransports($this->requireDtlsTransport($dtlsTransport), $sdp, $media, $iceCandidates);
            }
        }
        $bundleGroupDescriptions = array_filter($sdp->getGroup(), fn(GroupDescription $group) => $group->semantic === "BUNDLE");
        $bundleGroupDescription = $bundleGroupDescriptions[0] ?? null;

        if ($bundleGroupDescription && !empty($bundleGroupDescription->items)) {
            $this->removeBundleTransport($bundleGroupDescription, $iceCandidates);
        }

        foreach ($iceCandidates as [$iceCandidate, $media]) {
            $this->addRemoteCandidates($this->requireIceTransport($iceCandidate), $media);
        }

        foreach ($trackEvents as $trackEvent) {
            if ($trackEvent !== null) {
                $this->notifyTrack($trackEvent->track);
            }
        }

        if ($sdp->isType("offer")) {
            $this->setSignalingState(SignalingState::haveRemoteOffer);
        } elseif ($sdp->isType("answer")) {
            $this->setSignalingState(SignalingState::stable);

        }

        if ($sdp->isType("answer")) {
            $this->currentRemoteDescription = $sdp;
            $this->pendingRemoteDescription = null;
        } else {
            $this->pendingRemoteDescription = $sdp;
        }

        $this->scheduleConnect();

    }

    /**
     * Gets a track event and DTLS transport for a media description.
     *
     * This method handles the negotiation and setup of a transceiver for a remote media description,
     * including codec negotiation, direction configuration, and track creation.
     *
     * @param int $index The media line index in the SDP
     * @param MediaDescription $media The media description from the remote SDP
     * @param SessionDescription $sessionDescription The full session description
     * @return array{RTCTrackEvent|null, RTCRTPDtlsTransportInterface|null} Tuple containing:
     *         - The track event (if a new receiver track was created)
     *         - The associated DTLS transport
     * @throws OpenSSLException
     * @throws RandomException
     * @throws SrtpException
     */
    private function getTrackEventAndDtlsTransport(int $index, MediaDescription $media, SessionDescription $sessionDescription): array
    {
        $trackEvent = null;
        $transceiver = null;
        foreach ($this->transceivers as $trans) {
            if ($trans->getKind()->value === $media->getKind() && ($trans->getMid() === null || $trans->getMid() == $media->getRtp()->muxId)) {
                $transceiver = $trans;
                break;
            }
        }

        if (!$transceiver) {
            $transceiver = $this->createTransceiver(MediaKind::from($media->getKind()), SDPDirections::recvonly);
        }

        if ($transceiver->getMid() === null) {
            $transceiver->setMid($media->getRtp()->muxId);
            $transceiver->setMlineIndex($index);
        }

        if ($media->getPort() === 0) {
            if (in_array($sessionDescription->getType(), ["answer", "pranswer"])) {
                $transceiver->setCurrentDirection(SDPDirections::inactive);
            } else {
                $transceiver->setOfferDirection(SDPDirections::inactive);
            }
            return [null, null];
        }

        $codec = new Codec();
        // negotiate codecs
        /** @var list<RTCRtpCodecParameters> $localCodecs */
        $localCodecs = $codec->getCodecs($media->getKind());
        /** @var list<RTCRtpCodecParameters> $remoteCodecs */
        $remoteCodecs = $media->getRtp()->codecs;
        /** @var list<RTCRtpCodecCapability> $preferredCodecs */
        $preferredCodecs = $transceiver->getPreferredCodecs();
        $mutual = $this->findPreferredCodecs($this->findMutualCodecs($localCodecs, $remoteCodecs), $preferredCodecs);
        if (empty($mutual)) {
            if ($media->getKind() === "video") {
                // Video is optional: if the peer offers no codec we can also produce (for example we
                // are playing a pre-encoded AV1 file but the peer only decodes VP8/VP9/H264), drop the
                // video track instead of failing the whole call â€” the audio keeps working.
                if (in_array($sessionDescription->getType(), ["answer", "pranswer"], true)) {
                    $transceiver->setCurrentDirection(SDPDirections::inactive);
                } else {
                    $transceiver->setOfferDirection(SDPDirections::inactive);
                }
                return [null, null];
            }
            throw new RuntimeException("Failed to set remote {$media->getKind()} description send parameters");
        }
        $transceiver->setCodecs($mutual);
        /** @var list<RTCRtpHeaderExtensionParameters> $localHeaderExtensions */
        $localHeaderExtensions = $codec->getHeaderExtensions($media->getKind());
        /** @var list<RTCRtpHeaderExtensionParameters> $remoteHeaderExtensions */
        $remoteHeaderExtensions = $media->getRtp()->headerExtensions;
        $transceiver->setHeaderExtensions($this->findMutualHeaderExtensions($localHeaderExtensions, $remoteHeaderExtensions));

        // configure direction
        $direction = $media->getDirection();
        if ($direction === null) {
            $direction = SDPDirections::sendrecv;
        }
        $direction = $this->reverseDirection($direction);
        if (in_array($sessionDescription->getType(), ["answer", "pranswer"])) {
            $transceiver->setCurrentDirection($direction);
        } else {
            $transceiver->setOfferDirection($direction);
        }

        // create remote stream track
        if (in_array($direction, [SDPDirections::recvonly, SDPDirections::sendrecv]) && !$transceiver->getReceiver()->getTrack()) {
            $track = new RemoteStreamTrack(MediaKind::from($media->getKind()), $sessionDescription->webrtcTrackId($media));
            $transceiver->getReceiver()->setTrack($track);
            $trackEvent = new RTCTrackEvent($transceiver->getReceiver(), $track, $transceiver);
        }

        // memorise transport parameters
        $this->remoteDtlsParameter[spl_object_id($transceiver)] = $media->getDtls();
        $this->remoteIceParameters[spl_object_id($transceiver)] = $media->getIce();

        return [$trackEvent, $transceiver->getDtlsTransport()];
    }

    /**
     * Creates a new RTCRtpTransceiver with the specified parameters.
     *
     * The transceiver includes both sender and receiver components and is automatically
     * added to the connection's transceiver list.
     *
     * @param MediaKind $kind The media kind (audio/video)
     * @param SDPDirections $direction The initial direction of the transceiver
     * @param MediaStreamTrack|null $senderTrack Optional track for the sender component
     * @return RTCRtpTransceiver The created transceiver
     * @throws OpenSSLException
     * @throws RandomException
     * @throws SrtpException
     */
    private function createTransceiver(MediaKind $kind, SDPDirections $direction, ?MediaStreamTrack $senderTrack = null): RTCRtpTransceiver
    {
        $dtlsTransport = $this->createDtlsTransport();
        $transceiver = new RTCRtpTransceiver($kind, new RTCRtpReceiver($kind, $dtlsTransport), new RTCRtpSender($senderTrack ?? $kind, $dtlsTransport));
        $transceiver->setDirection($direction);
        $transceiver->getReceiver()->setRtcpSsrc($transceiver->getSender()->getSsrc());
        $transceiver->getSender()->setStreamId($this->streamId);
        $transceiver->setDtlsTransport($dtlsTransport);

        $this->transceivers[] = $transceiver;

        return $transceiver;
    }

    /**
     * Gets or creates the SCTP transport's DTLS transport for a media description.
     *
     * This handles SCTP-specific SDP negotiation including legacy SDP format detection
     * and port configuration.
     *
     * @param int $index The media line index in the SDP
     * @param MediaDescription $media The media description from the remote SDP
     * @return RTCDtlsTransport The DTLS transport for the SCTP association
     * @throws OpenSSLException
     * @throws RandomException
     * @throws SctpException
     * @throws SrtpException
     */
    private function getSctpDtlsTransport(int $index, MediaDescription $media): RTCDtlsTransport
    {
        if (!$this->sctp) {
            $this->createSctpTransport();
        }

        if ($this->sctp === null) {
            throw new RuntimeException("Unable to create the SCTP transport");
        }

        if ($this->sctp->getMid() === null) {
            $this->sctp->setMid($media->getRtp()->muxId);
        }

        // configure sctp
        if ($media->getProfile() === "DTLS/SCTP") {
            $this->sctpLegacySdp = true;
            // Legacy DTLS/SCTP carries the SCTP port as the m-line fmt token. A bare (int) cast
            // would turn a malformed value into port 0 and fail opaquely later; validate it.
            $sctpPort = (string)($media->getFmt()[0] ?? '');
            if (!ctype_digit($sctpPort)) {
                throw new InvalidArgumentException("Invalid legacy DTLS/SCTP port in SDP: \"$sctpPort\"");
            }
            $this->sctpRemotePort = (int)$sctpPort;
        } else {
            $this->sctpLegacySdp = false;
            $this->sctpRemotePort = $media->getSctpPort();
        }

        // memorise transport parameters
        $this->remoteDtlsParameter[spl_object_id($this->sctp)] = $media->getDtls();
        $this->remoteIceParameters[spl_object_id($this->sctp)] = $media->getIce();

        return $this->requireDtlsTransport($this->sctp->getDtlsTransport());
    }

    /**
     * Configures ICE and DTLS transports based on session and media descriptions.
     *
     * This method handles:
     * - Setting ICE roles (controlling/controlled) based on offer/answer
     * - Configuring DTLS roles (client/server) based on remote parameters
     * - Tracking ICE candidates for later processing
     *
     * @param RTCDtlsTransport $dtlsTransport The DTLS transport to configure
     * @param SessionDescription $sessionDescription The session description
     * @param MediaDescription $media The media description
     * @param array<int, array{RTCIceTransportInterface, MediaDescription}> $iceCandidates Reference to array storing ICE candidates
     * @param-out array<int, array{RTCIceTransportInterface, MediaDescription}> $iceCandidates
     */
    private function handleTransports(RTCDtlsTransport $dtlsTransport, SessionDescription $sessionDescription, MediaDescription $media, array &$iceCandidates): void
    {
        // add ICE candidates
        $iceTransport = $dtlsTransport->getIceTransport();

        // set an ICE role
        $mediaIce = $media->getIce();
        if ($sessionDescription->isType("offer") && !$iceTransport->isRoleSet()) {
            if ($mediaIce === null) {
                throw new RuntimeException("Media description has no ICE parameters");
            }
            $iceTransport->getIceConnection()->setIceRole($mediaIce->iceLite ? IceRole::Controlling : IceRole::Controlled);
            $iceTransport->setRoleSet(true);
        }

        // set DTLS role
        $mediaDtls = $media->getDtls();
        if ($mediaDtls === null) {
            throw new RuntimeException("Media description has no DTLS parameters");
        }
        if ($sessionDescription->isType("offer") && $mediaDtls->role == DtlsRole::Client) {
            $dtlsTransport->setRole(DtlsRole::Server);
        }
        if ($sessionDescription->isType("answer")) {
            $dtlsTransport->setRole($mediaDtls->role == DtlsRole::Client ? DtlsRole::Server : DtlsRole::Client);
        }

        $iceCandidates[spl_object_id($iceTransport)] = [$iceTransport, $media];
    }

    /**
     * Handles BUNDLE group negotiation by consolidating transports.
     *
     * This implements the BUNDLE mechanism from RFC 8843, where multiple media streams
     * share a single transport. It:
     * - Identifies the primary transport from the BUNDLE group
     * - Reconfigures secondary transports to use the primary transport
     * - Cleans up old unused transports
     *
     * @param GroupDescription $bundleGroupDescription The BUNDLE group from SDP
     * @param array<int, array{RTCIceTransportInterface, MediaDescription}> $iceCandidates Reference to array storing ICE candidates
     * @param-out array<int, array{RTCIceTransportInterface, MediaDescription}> $iceCandidates
     */
    private function removeBundleTransport(GroupDescription $bundleGroupDescription, array &$iceCandidates): void
    {
        // find the main media stream
        $masterMid = $bundleGroupDescription->items[0];
        $masterTransport = null;
        foreach ($this->transceivers as $transceiver) {
            if ($transceiver->getMid() == $masterMid) {
                $masterTransport = $this->requireDtlsTransport($transceiver->getDtlsTransport());
                break;
            }
        }

        if ($this->sctp && $this->sctp->getMid() == $masterMid) {
            $masterTransport = $this->requireDtlsTransport($this->sctp->getDtlsTransport());
        }

        // A remote peer may negotiate a mid it renamed itself (browsers replace the generated
        // mid with one of their own), so the BUNDLE group's master mid can point at a string
        // no transceiver carries anymore. The master transport is still the transport of the
        // first media section, so fall back to it when the literal mid does not match.
        if ($masterTransport === null) {
            foreach ($this->transceivers as $transceiver) {
                if ($transceiver->getMid() !== null) {
                    $masterTransport = $this->requireDtlsTransport($transceiver->getDtlsTransport());
                    break;
                }
            }
        }

        if ($masterTransport === null) {
            throw new RuntimeException("Unable to determine the master transport for the BUNDLE group");
        }

        // replace transport for bundled media
        $oldTransports = [];
        $slaveMids = array_slice($bundleGroupDescription->items, 1);
        foreach ($this->transceivers as $transceiver) {
            if (in_array($transceiver->getMid(), $slaveMids) && !$transceiver->isBundled()) {
                $oldTransports[] = $this->requireDtlsTransport($transceiver->getDtlsTransport());
                $transceiver->getReceiver()->setTransport($masterTransport);
                $transceiver->getSender()->setTransport($masterTransport);
                $transceiver->setBundled(true);
                $transceiver->setDtlsTransport($masterTransport);
            }
        }

        if ($this->sctp && in_array($this->sctp->getMid(), $slaveMids) && !$this->sctp->isBundled()) {
            $oldTransports[] = $this->requireDtlsTransport($this->sctp->getDtlsTransport());
            $this->sctp->setTransport($masterTransport);
            $this->sctp->setBundled(true);
        }

        // stop and discard old ICE transports
        foreach ($oldTransports as $dtlsTransport) {
            $dtlsTransport->stop();
            $iceTransport = $this->requireIceTransport($dtlsTransport->getIceTransport());
            $iceTransport->stop();
            unset($this->dtlsTransports[spl_object_id($dtlsTransport)]);
            unset($this->iceTransports[spl_object_id($iceTransport)]);
            unset($iceCandidates[spl_object_id($iceTransport)]);
        }
        $this->updateIceGatheringState();
        $this->updateIceConnectionState();
        $this->updateConnectionState();
    }

    /**
     * Runs {@see self::connect()} off the current fiber.
     *
     * The ICE and DTLS handshakes both block until they are done, so applying a description must
     * not wait for them.
     */
    private function scheduleConnect(): void
    {
        if ($this->connecting) {
            // connect() only ever starts transports that are still new, so a second concurrent run
            // would race the first one over the same handshakes: re-run it afterwards instead.
            $this->connectPending = true;
            return;
        }
        $this->connecting = true;
        EventLoop::queue($this->runScheduledConnect(...));
    }

    /**
     * Drive ICE/DTLS connect off the current fiber.
     *
     * Queued only as the first-class callable `$this->runScheduledConnect(...)` handed to
     * EventLoop::queue(), including when unserialize re-queues it. That callable captures this
     * method's private scope, so the event loop can run it while it stays private.
     */
    private function runScheduledConnect(): void
    {
            try {
                do {
                    $this->connectPending = false;
                    $this->connect();
                } while ($this->shouldConnectAgain() && !$this->isClosed);
            } catch (Throwable $e) {
                $this->logger?->error("RTCPeerConnection(" . spl_object_id($this) . "): could not connect: $e");
            } finally {
                $this->connecting = false;
            }
            $this->updateIceConnectionState();
            $this->updateConnectionState();
    }

    /**
     * True if a description was applied while connect() was running.
     *
     * connect() blocks through the ICE and DTLS handshakes, during which another
     * fiber may apply a description (scheduling another connect()). Psalm cannot
     * model this cross-fiber re-entrancy, so the flag is read through a method.
     */
    private function shouldConnectAgain(): bool
    {
        return $this->connectPending;
    }

    /**
     * Connects all transports when ready.
     *
     * @throws RandomException If random number generation fails
     * @throws TLSException If TLS handshake fails
     * @throws DTLSException If DTLS handshake fails
     * @throws OpenSSLException|Throwable If SSL operation fails
     */
    private function connect(): void
    {
        foreach ($this->transceivers as $transceiver) {
            $dtlsTransport = $this->requireDtlsTransport($transceiver->getDtlsTransport());
            $iceTransport = $this->requireIceTransport($dtlsTransport->getIceTransport());

            if (!empty($iceTransport->getIceGatherer()->getLocalCandidates()) && isset($this->remoteIceParameters[spl_object_id($transceiver)])) {
                if ($iceTransport->getState() === IceTransportState::new) {
                    $remoteIce = $this->remoteIceParameters[spl_object_id($transceiver)];
                    if ($remoteIce === null) {
                        throw new RuntimeException("Remote ICE parameters are not available");
                    }
                    $iceTransport->start($remoteIce);
                }

                if ($dtlsTransport->getState() == TLSState::NEW && $iceTransport->getState() == IceTransportState::complete) {
                    $remoteDtls = $this->remoteDtlsParameter[spl_object_id($transceiver)];
                    if ($remoteDtls === null) {
                        throw new RuntimeException("Remote DTLS parameters are not available");
                    }
                    $dtlsTransport->start($remoteDtls->fingerprints);
                }

                if ($dtlsTransport->getState() == TLSState::CONNECTED) {
                    if (in_array($transceiver->getCurrentDirection(), [SDPDirections::sendonly, SDPDirections::sendrecv])) {
                        $transceiver->getSender()->start($this->localRtp($transceiver));
                    }
                    if (in_array($transceiver->getCurrentDirection(), [SDPDirections::recvonly, SDPDirections::sendrecv])) {
                        $transceiver->getReceiver()->start($this->remoteRtp($transceiver));
                    }
                }
            }
        }

        if ($this->sctp && $this->sctpInband) {
            // The media transport is driven by the transceiver loop above; the association only
            // has to be started on it. A server registers itself before the DTLS handshake is done
            // (see the comment below for why), a client once the transport carries application data.
            $dtlsTransport = $this->requireDtlsTransport($this->sctp->getDtlsTransport());
            if (!$this->isClosed && $this->sctpRemotePort !== null
                && ($this->sctp->isServer() || $dtlsTransport->getState() == TLSState::CONNECTED)
            ) {
                $this->sctp->start($this->sctpRemotePort);
            }
        } elseif ($this->sctp) {
            $dtlsTransport = $this->requireDtlsTransport($this->sctp->getDtlsTransport());
            $iceTransport = $this->requireIceTransport($dtlsTransport->getIceTransport());
            if ($iceTransport->getIceGatherer()->getLocalCandidates() && isset($this->remoteIceParameters[spl_object_id($this->sctp)])) {
                if ($iceTransport->getState() === IceTransportState::new) {
                    $remoteIce = $this->remoteIceParameters[spl_object_id($this->sctp)];
                    if ($remoteIce === null) {
                        throw new RuntimeException("Remote ICE parameters are not available");
                    }
                    $iceTransport->start($remoteIce);
                }

                // The client sends its first SCTP INIT the instant its DTLS handshake
                // completes, which can land before ours does. Register the server-side
                // receiver up front â€” before dtlsTransport->start() blocks through the
                // handshake â€” so the receiver is provably in place before either side can
                // finish handshaking (and thus before any INIT can arrive), instead of
                // betting a fixed delay covers the skew. start() is idempotent, so the
                // CONNECTED branch below is a no-op on this side.
                if ($this->sctp->isServer() && !$this->isClosed && $this->sctpRemotePort !== null) {
                    $this->sctp->start($this->sctpRemotePort);
                }

                if ($dtlsTransport->getState() == TLSState::NEW && $iceTransport->getState() == IceTransportState::complete) {
                    $remoteDtls = $this->remoteDtlsParameter[spl_object_id($this->sctp)];
                    if ($remoteDtls === null) {
                        throw new RuntimeException("Remote DTLS parameters are not available");
                    }
                    $dtlsTransport->start($remoteDtls->fingerprints);
                }

                if ($dtlsTransport->getState() == TLSState::CONNECTED) {
                    // Client side: emit the INIT now that DTLS carries application data.
                    // Server side: already started above, so this is a no-op.
                    if (!$this->isClosed && $this->sctpRemotePort !== null) {
                        $this->sctp->start($this->sctpRemotePort);
                    }
                }
            }
        }
    }

    /**
     * Gathers ICE candidates for all transports.
     *
     * @throws RandomException If random number generation fails
     * @throws Throwable If gathering fails
     */
    private function gatherIceCandidates(): void
    {
        foreach ($this->iceTransports as $iceTransport) {
            $iceGatherer = $iceTransport->getIceGatherer();
            if (!$iceGatherer instanceof RTCIceGatherer) {
                throw new RuntimeException("Unsupported ICE gatherer implementation");
            }
            $iceGatherer->gather();
        }
    }

    /**
     * Verifies the connection is not closed.
     *
     * @throws RuntimeException If connection is closed
     */
    private function checkNotClosed(): void
    {
        if ($this->isClosed) {
            throw new RuntimeException("RTCPeerConnection is closed");
        }
    }

    /**
     * Verifies a track doesn't already have a sender.
     *
     * @param MediaStreamTrack $track The track to check
     * @throws InvalidArgumentException If track already has a sender
     */
    private function checkTrackHasNoSender(MediaStreamTrack $track): void
    {
        $senders = $this->getSenders();
        foreach ($senders as $sender) {
            if ($sender->getTrack() === $track) {
                throw new InvalidArgumentException("Track already has a sender");
            }
        }
    }

    /**
     * Creates RTP parameters for local sending.
     *
     * @param RTCRtpTransceiver $transceiver The sending transceiver
     * @return RTCRtpSendParameters The send parameters
     */
    private function localRtp(RTCRtpTransceiver $transceiver): RTCRtpSendParameters
    {
        /** @var list<RTCRtpHeaderExtensionParameters> $headerExtensions */
        $headerExtensions = $transceiver->getHeaderExtensions();
        $rtp = new RTCRtpSendParameters($transceiver->getCodecs(), $headerExtensions, $transceiver->getMid() ?? '');
        $rtp->rtcp->cname = $this->cname;
        $rtp->rtcp->ssrc = $transceiver->getSender()->getSsrc();

        $payloadType = null;
        foreach ($transceiver->getCodecs() as $codec) {
            if ($codec->payloadType !== null) {
                $payloadType = $codec->payloadType;
                break;
            }
        }
        assert($payloadType !== null);
        $rtp->encodings = [new RTCRtpEncodingParameters($transceiver->getSender()->getSsrc(), $payloadType)];

        return $rtp;
    }

    /**
     * Creates RTP parameters for remote receiving.
     *
     * @param RTCRtpTransceiver $transceiver The receiving transceiver
     * @return RTCRtpReceiveParameters The receive parameters
     */
    private function remoteRtp(RTCRtpTransceiver $transceiver): RTCRtpReceiveParameters
    {
        $remoteDescription = $this->pendingRemoteDescription ?? $this->currentRemoteDescription;
        if ($remoteDescription === null) {
            throw new RuntimeException("Remote description is not available");
        }
        $mlineIndex = $transceiver->getMlineIndex();
        if ($mlineIndex === null) {
            throw new RuntimeException("Transceiver has no media line index");
        }
        $remoteMedia = $remoteDescription->getMedia()[$mlineIndex] ?? null;
        if ($remoteMedia === null) {
            throw new RuntimeException("No remote media section at media line {$mlineIndex}");
        }
        /** @var list<RTCRtpHeaderExtensionParameters> $headerExtensions */
        $headerExtensions = $transceiver->getHeaderExtensions();
        $rtp = new RTCRtpReceiveParameters($transceiver->getCodecs(), $headerExtensions, $remoteMedia->getRtp()->muxId, $remoteMedia->getRtp()->rtcp);

        if (count($remoteMedia->getSsrc()) > 0) {
            $encoding = [];
            $rtx = [];
            foreach ($transceiver->getCodecs() as $codec) {
                if (CodecUtility::isRtx($codec)) {
                    // A retransmission stream is not an encoding of its own: it is the second SSRC
                    // of the FID group, and belongs to the encoding of the codec it repeats. The
                    // lookup is by payload type, which is how the encodings are keyed.
                    $apt = CodecUtility::apt($codec);
                    if ($apt !== null && count($remoteMedia->getSsrc()) === 2) {
                        $rtx[$apt] = new RTCRtpRtxParameters($remoteMedia->getSsrc()[1]->ssrc);
                    }
                    continue;
                }
            }
            foreach ($transceiver->getCodecs() as $codec) {
                if (!CodecUtility::isRtx($codec) && $codec->payloadType !== null) {
                    $encoding[$codec->payloadType] = new RTCRtpDecodingParameters(
                        $remoteMedia->getSsrc()[0]->ssrc,
                        $codec->payloadType,
                        $rtx[$codec->payloadType] ?? null,
                    );
                }
            }
            $rtp->encodings = $encoding;
        }

        return $rtp;
    }

    /**
     * Updates the connection state based on transport states.
     *
     * @internal Public only because RTCPeerConnection registers itself as a typed state-change
     * listener on the child ICE and DTLS transports (other packages' objects), which invoke this
     * from outside this class. Being plain object wiring rather than a Closure, it survives
     * serialization so the wiring is restored automatically. Not part of the public API.
     */
    public function updateConnectionState(): void
    {
        $dtlsStates = [];
        foreach ($this->dtlsTransports as $transport) {
            $dtlsStates[$transport->getState()->value] = true;
        }

        $iceStates = [];
        foreach ($this->iceTransports as $transport) {
            $iceStates[$transport->getState()->value] = true;
        }

        // Compute new state
        if ($this->isClosed) {
            $state = ConnectionState::closed;
        } elseif (isset($iceStates[IceTransportState::failed->value]) || isset($dtlsStates[TLSState::FAILED->value])) {
            $state = ConnectionState::failed;
        } elseif (isset($iceStates[IceTransportState::new->value]) && isset($dtlsStates[TLSState::NEW->value])) {
            $state = ConnectionState::new;
        } elseif (isset($iceStates[IceTransportState::checking->value]) || isset($dtlsStates[TLSState::CONNECTING->value]) || isset($dtlsStates[TLSState::NEW->value])) {
            $state = ConnectionState::connecting;
        } else {
            $state = ConnectionState::connected;
        }

        // Update state
        if ($state !== $this->connectionState) {
            $this->debug(sprintf("connectionState %s -> %s", $this->connectionState->name, $state->name));
            $this->connectionState = $state;
            $this->notifyConnectionStateChange();
        }

        if (!$this->isClosed && isset($dtlsStates[TLSState::CLOSED->value])) {
            $this->close();
        }
    }

    /**
     * Updates the ICE connection state based on transport states.
     *
     * @internal Public only because RTCPeerConnection registers itself as a typed state-change
     * listener on the child ICE transport (another package's object), which invokes this from
     * outside this class. Being plain object wiring rather than a Closure, it survives
     * serialization so the wiring is restored automatically. Not part of the public API.
     */
    public function updateIceConnectionState(): void
    {
        $iceStates = array_values(array_map(fn(RTCIceTransport $transport) => $transport->getState(), $this->iceTransports));

        if ($this->isClosed || $iceStates === [IceTransportState::closed]) {
            $state = IceConnectionState::closed;
        } elseif (in_array(IceTransportState::failed, $iceStates)) {
            $state = IceConnectionState::failed;
        } elseif ($iceStates === [IceTransportState::complete]) {
            $state = IceConnectionState::completed;
        } elseif (in_array(IceTransportState::checking, $iceStates)) {
            $state = IceConnectionState::checking;
        } else {
            $state = IceConnectionState::new;
        }
        // var_dump($state);

        // update $state
        if ($state !== $this->iceConnectionState) {
            $this->debug(sprintf("iceConnectionState %s -> %s", $this->iceConnectionState->name, $state->name));
            $this->iceConnectionState = $state;
            $this->notifyIceConnectionStateChange();
        }
    }

    /**
     * Updates the ICE gathering state based on the state of all ICE transports.
     *
     * Notifies "icegatheringstatechange" listeners if the state changes.
     *
     * @internal Public only because RTCPeerConnection registers itself as a typed state-change
     * listener on each child ICE gatherer (another package's object), which invokes this from
     * outside this class. Being plain object wiring rather than a Closure, it survives
     * serialization so the wiring is restored automatically. Not part of the public API.
     */
    public function updateIceGatheringState(): void
    {
        // Compute new state
        $states = [];
        foreach ($this->iceTransports as $transport) {
            $states[] = $transport->getIceGatherer()->getState()->name;
        }

        $uniqueStates = array_unique($states);

        if ($uniqueStates === [IceGatheringState::complete->name]) {
            $state = IceGatheringState::complete;
        } elseif (in_array(IceGatheringState::gathering->name, $uniqueStates, true)) {
            $state = IceGatheringState::gathering;
        } else {
            $state = IceGatheringState::new;
        }

        // Update state
        if ($state !== $this->iceGatheringState) {
            $this->logger?->debug(sprintf("iceGatheringState %s -> %s", $this->iceGatheringState->name, $state->name));
            $this->iceGatheringState = $state;
            $this->notifyIceGatheringStateChange();
        }
    }

    /**
     * Finds preferred codecs from available options.
     *
     * @param RTCRtpCodecParameters[] $codecs Available codecs
     * @param RTCRtpCodecCapability[] $preferredCodecs Preferred codecs
     * @return RTCRtpCodecParameters[] Filtered codecs
     */
    public function findPreferredCodecs(array $codecs, array $preferredCodecs): array
    {
        if (empty($preferredCodecs)) {
            return $codecs;
        }

        $rtxCodecs = array_filter($codecs, fn(RTCRtpCodecParameters $codec) => CodecUtility::isRtx($codec));
        $rtxPreferredCodecs = array_filter($preferredCodecs, fn(RTCRtpCodecCapability $codec) => !CodecUtility::isRtx($codec));
        $rtxEnabled = array_any($preferredCodecs, fn(RTCRtpCodecCapability $codec) => CodecUtility::isRtx($codec));

        $filtered = [];
        foreach ($rtxPreferredCodecs as $pref) {
            foreach ($codecs as $codec) {
                if (strtolower($codec->mimeType) === strtolower($pref->mimeType) && $codec->parameters === $pref->parameters) {
                    $filtered[] = $codec;

                    // add corresponding RTX
                    if ($rtxEnabled) {
                        foreach ($rtxCodecs as $rtx) {
                            if (CodecUtility::apt($rtx) === $codec->payloadType) {
                                $filtered[] = $rtx;
                                break;
                            }
                        }
                    }

                    break;
                }
            }
        }

        return $filtered;
    }

    /**
     * Finds mutually supported codecs between local and remote offers.
     *
     * This performs codec negotiation by:
     * - Matching codec types and parameters
     * - Handling RTX (retransmission) codecs specially
     * - Preserving dynamic payload types from the remote offer
     * - Filtering RTCP feedback capabilities based on mutual support
     *
     * @param array<RTCRtpCodecParameters> $localCodecs Local codec capabilities
     * @param array<RTCRtpCodecParameters> $remoteCodecs Remote codec offers
     * @return array<RTCRtpCodecParameters> Mutually supported codecs with negotiated parameters
     */
    public function findMutualCodecs(array $localCodecs, array $remoteCodecs): array
    {
        $mutual = [];
        $mutualBase = [];

        foreach ($remoteCodecs as $remoteCodec) {
            // for RTX, check we accepted the base codec
            if ((strtolower(explode("/", $remoteCodec->mimeType)[1]) === "rtx")) {
                $apt = $remoteCodec->parameters['apt'] ?? null;
                if ($apt !== null && isset($mutualBase[$apt])) {
                    $base = $mutualBase[$apt];
                    if ($remoteCodec->clockRate === $base->clockRate) {
                        $mutual[] = clone $remoteCodec;
                    }
                }
                continue;
            }

            // handle other codecs
            foreach ($localCodecs as $localCodec) {
                if ($this->isCodecCompatible($localCodec, $remoteCodec)) {
                    $codec = clone $localCodec;
                    if (in_array($remoteCodec->payloadType, RtpConstants::DYNAMIC_PAYLOAD_TYPES)) {
                        $codec->payloadType = $remoteCodec->payloadType;
                    }
                    $codec->rtcpFeedback = array_filter($codec->rtcpFeedback, fn(RTCRtcpFeedback $codec) => in_array($codec, $remoteCodec->rtcpFeedback));
                    $mutual[] = $codec;
                    if ($codec->payloadType !== null) {
                        $mutualBase[$codec->payloadType] = $codec;
                    }
                    break;
                }
            }
        }

        return $mutual;
    }

    /**
     * Adds remote ICE candidates to transport.
     *
     * @param RTCIceTransport $iceTransport The transport to add to
     * @param MediaDescription $media The media description containing candidates
     */
    private function addRemoteCandidates(RTCIceTransport $iceTransport, MediaDescription $media): void
    {
        foreach ($media->getIceCandidates() as $candidate) {
            $iceTransport->addRemoteCandidate($candidate);
        }

        if ($media->isIceCandidatesComplete()) {
            $iceTransport->endRemoteCandidate();
        }
    }

    /**
     * Reverses a direction (sendonly <-> recvonly).
     *
     * @param SDPDirections $direction The direction to reverse
     * @return SDPDirections The reversed direction
     * @throws InvalidArgumentException If a direction canâ€™t be reversed
     */
    private function reverseDirection(SDPDirections $direction): SDPDirections
    {
        return match ($direction) {
            SDPDirections::sendonly => SDPDirections::recvonly,
            SDPDirections::recvonly => SDPDirections::sendonly,
            default => $direction
        };
    }

    /**
     * Finds mutually supported header extensions.
     *
     * @param RTCRtpHeaderExtensionParameters[] $localExtensions Local extensions
     * @param RTCRtpHeaderExtensionParameters[] $remoteExtensions Remote extensions
     * @return RTCRtpHeaderExtensionParameters[] Mutual extensions
     */
    private function findMutualHeaderExtensions(array $localExtensions, array $remoteExtensions): array
    {
        $mutual = [];

        foreach ($remoteExtensions as $rExt) {
            foreach ($localExtensions as $lExt) {
                if ($lExt->uri === $rExt->uri) {
                    $mutual[] = $rExt;
                }
            }
        }

        return $mutual;
    }

    /**
     * Validates a session description.
     *
     * @param SessionDescription $description The description to validate
     * @param bool $isLocal Whether the description is local
     * @throws InvalidArgumentException If description is invalid
     */
    private function validateDescription(SessionDescription $description, bool $isLocal): void
    {
        // Check description is compatible with signaling state
        if ($isLocal) {
            if ($description->isType("offer")) {
                if (!in_array($this->signalingState, [SignalingState::stable, SignalingState::haveLocalOffer])) {
                    throw new InvalidArgumentException(
                        "Cannot handle offer in signaling state \"" . $this->signalingState->name . "\""
                    );
                }
            } elseif ($description->isType("answer")) {
                if (!in_array($this->signalingState, [SignalingState::haveRemoteOffer, SignalingState::haveLocalPranswer])) {
                    throw new InvalidArgumentException(
                        "Cannot handle answer in signaling state \"" . $this->signalingState->name . "\""
                    );
                }
            }
        } else {
            if ($description->isType("offer")) {
                if (!in_array($this->signalingState, [SignalingState::stable, SignalingState::haveRemoteOffer])) {
                    throw new InvalidArgumentException(
                        "Cannot handle offer in signaling state \"" . $this->signalingState->name . "\""
                    );
                }
            } elseif ($description->isType("answer")) {
                if (!in_array($this->signalingState, [SignalingState::haveLocalOffer, SignalingState::haveLocalPranswer])) {
                    throw new InvalidArgumentException(
                        "Cannot handle answer in signaling state \"" . $this->signalingState->name . "\""
                    );
                }
            }
        }

        foreach ($description->getMedia() as $media) {
            // RFC 3264 rejects a media stream by setting its port to zero. A rejected section is
            // retained only to preserve m-line ordering and does not establish a transport.
            if ($media->getPort() === 0) {
                continue;
            }
            // Check ICE credentials were provided
            $ice = $media->getIce();
            if ($ice === null || $ice->usernameFragment === null || $ice->password === null) {
                throw new InvalidArgumentException("ICE username fragment or password is missing");
            }

            // Check a DTLS role is allowed
            if (in_array($description->getType(), ["answer", "pranswer"]) &&
                !in_array($media->getDtls()?->role, [DtlsRole::Client, DtlsRole::Server])) {
                throw new InvalidArgumentException(
                    "DTLS setup attribute must be 'active' or 'passive' for an answer"
                );
            }

            // Check RTCP mux is used
            if (in_array($media->getKind(), ["audio", "video"]) && !$media->isRtcpMux()) {
                throw new InvalidArgumentException("RTCP mux is not enabled");
            }
        }

        // Check the number of media section matches
        if (in_array($description->getType(), ["answer", "pranswer"])) {
            $offer = $isLocal ? $this->getRemoteDescription() : $this->getLocalDescription();
            if ($offer === null) {
                throw new InvalidArgumentException("Cannot validate answer: no matching offer description is set");
            }

            $offerMedia = array_map(fn(MediaDescription $media) => [$media->getKind(), $media->getRtp()->muxId], SessionDescription::decode($offer->getSdp())->getMedia());
            $answerMedia = array_map(fn($media) => [$media->getKind(), $media->getRtp()->muxId], $description->getMedia());

            if ($answerMedia != $offerMedia) {
                throw new InvalidArgumentException("Media sections in answer do not match offer");
            }
        }
    }

    /**
     * Checks if two codecs are compatible.
     *
     * @param RTCRtpCodecParameters $a First codec
     * @param RTCRtpCodecParameters $b Second codec
     * @return bool True if compatible
     */
    public function isCodecCompatible(RTCRtpCodecParameters $a, RTCRtpCodecParameters $b): bool
    {
        if (strtolower($a->mimeType) !== strtolower($b->mimeType) || $a->clockRate !== $b->clockRate) {
            return false;
        }

        if (strtolower($a->mimeType) === "video/h264") {
            try {
                return $this->packetization($a) === $this->packetization($b) && $this->profile($a) === $this->profile($b);
            } catch (Throwable $e) {
                // A malformed profile-level-id in the remote codec params makes the pair
                // incompatible, but log it: otherwise H.264 is silently dropped from negotiation
                // and surfaces only as an unexplained "no video".
                $this->logger?->warning("Ignoring H.264 codec with unparseable parameters during codec matching: " . $e->getMessage());
                return false;
            }
        }

        return true;
    }

    /**
     * Gets the H.264 profile from codec parameters.
     *
     * @param RTCRtpCodecParameters $codec The codec parameters
     * @return H264Profile The H.264 profile
     */
    private function profile(RTCRtpCodecParameters $codec): H264Profile
    {
        return H264Sdp::parseH264ProfileLevelId((string)($codec->parameters['profile-level-id'] ?? '42E01F'))[0];
    }

    /**
     * Gets the packetization mode from codec parameters.
     *
     * @param RTCRtpCodecParameters $codec The codec parameters
     * @return string The packetization mode
     */
    private function packetization(RTCRtpCodecParameters $codec): string
    {
        if (array_key_exists('packetization-mode', $codec->parameters)) {
            $mode = $codec->parameters['packetization-mode'];
            if ($mode !== null) {
                return (string)$mode;
            }
        }
        return '0';
    }

    /**
     * Resolves the concrete DTLS transport for a transceiver or SCTP transport.
     *
     * @param RTCRTPDtlsTransportInterface|RTCSctpDtlsTransportInterface|null $transport The transport to resolve
     * @throws RuntimeException If no usable DTLS transport is available
     */
    private function requireDtlsTransport(RTCRTPDtlsTransportInterface|RTCSctpDtlsTransportInterface|null $transport): RTCDtlsTransport
    {
        if (!$transport instanceof RTCDtlsTransport) {
            throw new RuntimeException("DTLS transport is not available");
        }
        return $transport;
    }

    /**
     * Resolves the concrete ICE transport for a DTLS transport.
     *
     * @param RTCIceTransportInterface $transport The transport to resolve
     * @throws RuntimeException If no usable ICE transport is available
     */
    private function requireIceTransport(RTCIceTransportInterface $transport): RTCIceTransport
    {
        if (!$transport instanceof RTCIceTransport) {
            throw new RuntimeException("ICE transport is not available");
        }
        return $transport;
    }

    /**
     * Logs a debug message.
     *
     * @param string $log The message to log
     */
    private function debug(string $log): void
    {
        $this->logger?->debug("RTCPeerConnection(" . spl_object_id($this) . "): " . $log);
    }

    /**
     * Sets whether to use legacy SDP format for SCTP.
     *
     * @param bool $sctpLegacySdp Whether to use a legacy format
     */
    public function setSctpLegacySdp(bool $sctpLegacySdp): void
    {
        $this->sctpLegacySdp = $sctpLegacySdp;
    }

    /**
     * @return array<string, mixed>
     */
    public function __serialize(): array
    {
        $state = SerializableState::export($this, [
            // WeakMaps cannot be serialized; snapshot their keys and rebuild on the far side.
            'trackListeners' => ['__uninitialized' => true],
            'peerConnectionDataChannelListeners' => ['__uninitialized' => true],
            'signalingStateChangeListeners' => ['__uninitialized' => true],
            'connectionStateChangeListeners' => ['__uninitialized' => true],
            'iceConnectionStateChangeListeners' => ['__uninitialized' => true],
            'iceGatheringStateChangeListeners' => ['__uninitialized' => true],
        ]);
        $state['__trackListeners'] = SerializableState::weakMapToList($this->trackListeners);
        $state['__peerConnectionDataChannelListeners'] = SerializableState::weakMapToList($this->peerConnectionDataChannelListeners);
        $state['__signalingStateChangeListeners'] = SerializableState::weakMapToList($this->signalingStateChangeListeners);
        $state['__connectionStateChangeListeners'] = SerializableState::weakMapToList($this->connectionStateChangeListeners);
        $state['__iceConnectionStateChangeListeners'] = SerializableState::weakMapToList($this->iceConnectionStateChangeListeners);
        $state['__iceGatheringStateChangeListeners'] = SerializableState::weakMapToList($this->iceGatheringStateChangeListeners);

        return $state;
    }

    /**
     * @param array<string, mixed> $data
     */
    public function __unserialize(array $data): void
    {
        /** @var list<PeerConnectionTrackListener> $trackListeners */
        $trackListeners = $data['__trackListeners'] ?? [];
        /** @var list<PeerConnectionDataChannelListener> $peerConnectionDataChannelListeners */
        $peerConnectionDataChannelListeners = $data['__peerConnectionDataChannelListeners'] ?? [];
        /** @var list<PeerConnectionSignalingStateChangeListener> $signalingStateChangeListeners */
        $signalingStateChangeListeners = $data['__signalingStateChangeListeners'] ?? [];
        /** @var list<PeerConnectionConnectionStateChangeListener> $connectionStateChangeListeners */
        $connectionStateChangeListeners = $data['__connectionStateChangeListeners'] ?? [];
        /** @var list<PeerConnectionIceConnectionStateChangeListener> $iceConnectionStateChangeListeners */
        $iceConnectionStateChangeListeners = $data['__iceConnectionStateChangeListeners'] ?? [];
        /** @var list<PeerConnectionIceGatheringStateChangeListener> $iceGatheringStateChangeListeners */
        $iceGatheringStateChangeListeners = $data['__iceGatheringStateChangeListeners'] ?? [];
        unset(
            $data['__trackListeners'],
            $data['__peerConnectionDataChannelListeners'],
            $data['__signalingStateChangeListeners'],
            $data['__connectionStateChangeListeners'],
            $data['__iceConnectionStateChangeListeners'],
            $data['__iceGatheringStateChangeListeners'],
        );

        SerializableState::import($this, $data);
        /** @var \WeakMap<PeerConnectionTrackListener, null> */
        $this->trackListeners = SerializableState::listToWeakMap($trackListeners);
        /** @var \WeakMap<PeerConnectionDataChannelListener, null> */
        $this->peerConnectionDataChannelListeners = SerializableState::listToWeakMap($peerConnectionDataChannelListeners);
        /** @var \WeakMap<PeerConnectionSignalingStateChangeListener, null> */
        $this->signalingStateChangeListeners = SerializableState::listToWeakMap($signalingStateChangeListeners);
        /** @var \WeakMap<PeerConnectionConnectionStateChangeListener, null> */
        $this->connectionStateChangeListeners = SerializableState::listToWeakMap($connectionStateChangeListeners);
        /** @var \WeakMap<PeerConnectionIceConnectionStateChangeListener, null> */
        $this->iceConnectionStateChangeListeners = SerializableState::listToWeakMap($iceConnectionStateChangeListeners);
        /** @var \WeakMap<PeerConnectionIceGatheringStateChangeListener, null> */
        $this->iceGatheringStateChangeListeners = SerializableState::listToWeakMap($iceGatheringStateChangeListeners);

        if ($this->connecting) {
            $this->connecting = false;
            $this->scheduleConnect();
        }
    }
}
<?php

namespace Webrtc\Webrtc\Listener;

use Webrtc\DataChannel\RTCDataChannel;

/**
 * Notified when an {@see \Webrtc\Webrtc\RTCPeerConnection} receives a remotely-opened data channel.
 *
 * A typed replacement for the former Evenement "datachannel" event: the listener object is
 * registered on the peer connection and, being an ordinary object rather than a closure, survives a
 * serialize cycle as part of the graph with no wrapper.
 */
interface PeerConnectionDataChannelListener
{
    public function onPeerConnectionDataChannel(RTCDataChannel $channel): void;
}
<?php

namespace Webrtc\Webrtc\Listener;

use Webrtc\RTP\MediaStreamTrack\MediaStreamTrack;

/**
 * Notified when an {@see \Webrtc\Webrtc\RTCPeerConnection} receives a remote media track.
 *
 * A typed replacement for the former Evenement "track" event: the listener object is registered on
 * the peer connection and, being an ordinary object rather than a closure, survives a serialize
 * cycle as part of the graph with no wrapper.
 */
interface PeerConnectionTrackListener
{
    public function onPeerConnectionTrack(MediaStreamTrack $track): void;
}
<?php

namespace Webrtc\Webrtc\Listener;

/**
 * Notified when an {@see \Webrtc\Webrtc\RTCPeerConnection} changes its signaling state.
 *
 * A typed replacement for the former Evenement "signalingstatechange" event: the listener object is
 * registered on the peer connection and, being an ordinary object rather than a closure, survives a
 * serialize cycle as part of the graph with no wrapper.
 */
interface PeerConnectionSignalingStateChangeListener
{
    public function onPeerConnectionSignalingStateChange(): void;
}
<?php

namespace Webrtc\Webrtc\Listener;

/**
 * Notified when an {@see \Webrtc\Webrtc\RTCPeerConnection} changes its ICE gathering state.
 *
 * A typed replacement for the former Evenement "icegatheringstatechange" event: the listener object
 * is registered on the peer connection and, being an ordinary object rather than a closure, survives
 * a serialize cycle as part of the graph with no wrapper.
 */
interface PeerConnectionIceGatheringStateChangeListener
{
    public function onPeerConnectionIceGatheringStateChange(): void;
}
<?php

namespace Webrtc\Webrtc\Listener;

/**
 * Notified when an {@see \Webrtc\Webrtc\RTCPeerConnection} changes its overall connection state.
 *
 * A typed replacement for the former Evenement "connectionstatechange" event: the listener object
 * is registered on the peer connection and, being an ordinary object rather than a closure,
 * survives a serialize cycle as part of the graph with no wrapper.
 */
interface PeerConnectionConnectionStateChangeListener
{
    public function onPeerConnectionConnectionStateChange(): void;
}
<?php

namespace Webrtc\Webrtc\Listener;

/**
 * Notified when an {@see \Webrtc\Webrtc\RTCPeerConnection} changes its ICE connection state.
 *
 * A typed replacement for the former Evenement "iceconnectionstatechange" event: the listener
 * object is registered on the peer connection and, being an ordinary object rather than a closure,
 * survives a serialize cycle as part of the graph with no wrapper.
 */
interface PeerConnectionIceConnectionStateChangeListener
{
    public function onPeerConnectionIceConnectionStateChange(): void;
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Webrtc;

use Override;
use Webrtc\Exception\InvalidArgumentException;
use Webrtc\ICE\RTCIceServer;
use Webrtc\ICE\RTCIceServerInterface;
use Webrtc\ICE\RTCICESetting;

/**
 * The `RTCConfiguration` class provides configuration options for an
 * `RTCPeerConnection` instance.
 *
 * It allows configuring ICE servers (STUN/TURN), certificate paths,
 * private key paths, and low-level ICE behavior through `RTCICESetting`.
 *
 * Example usage:
 * ```php
 * $config = new RTCConfiguration([
 *     'iceServers' => [
 *         ['urls' => 'stun:stun.l.google.com:19302']
 *     ]
 * ]);
 *
 * $config->iceSettings()->setIcePortRange(40000, 50000);
 * ```
 */
final class RTCConfiguration implements RTCConfigurationInterface
{
    /**
     * Default STUN server URL used when no configuration is provided.
     */
    private const DEFAULT_STUN_SERVER = "stun:stun.l.google.com:19302";

    /**
     * Array of RTCIceServer objects representing STUN/TURN servers.
     *
     * @var array<RTCIceServerInterface>
     */
    private array $iceServers = [];

    /**
     * Path to the certificate file for secure connections.
     *
     * @var string|null
     */
    private ?string $certificatePath = null;

    /**
     * Path to the private key file for secure connections.
     *
     * @var string|null
     */
    private ?string $privateKeyPath = null;

    /**
     * ICE settings object defining port range, transport policy,
     * NAT mapping, and IP family usage.
     *
     * @var RTCICESetting
     */
    private RTCICESetting $iceSettings;

    /**
     * Constructs a new RTCConfiguration instance
     *
     * @param array{
     *     iceServers?: list<array{
     *         urls: string|list<string>,
     *         username?: string,
     *         credential?: string,
     *         credentialType?: string
     *     }>,
     *     certificatePath?: string,
     *     privateKeyPath?: string
     * }|null $configuration Optional configuration array. If null,
     *        default configuration with a Google STUN server will be used.
     * @throws InvalidArgumentException If iceServer configuration is invalid
     */
    public function __construct(?array $configuration = null)
    {
        $this->iceSettings = new RTCICESetting();
        $configuration !== null ? $this->parseConfiguration($configuration) : $this->getDefaultConfiguration();
    }

    /**
     * Gets all configured ICE servers
     *
     * @return array<RTCIceServerInterface> Array of configured ICE servers
     */
    #[Override]
    public function getIceServers(): array
    {
        return $this->iceServers;
    }

    /**
     * Sets multiple ICE servers at once
     *
     * @param array<RTCIceServerInterface> $iceServers Array of ICE servers to add
     * @return void
     */
    public function setIceServers(array $iceServers): void
    {
        array_walk($iceServers, fn(RTCIceServerInterface $iceServer) => $this->addIceServer($iceServer));
    }

    /**
     * Adds a single ICE server to the configuration
     *
     * @param RTCIceServerInterface $iceServer The ICE server to add
     * @return void
     */
    public function addIceServer(RTCIceServerInterface $iceServer): void
    {
        $this->iceServers[] = $iceServer;
    }

    /**
     * Gets the path to the certificate file
     *
     * @return string|null Path to the certificate file or null if not set
     */
    #[Override]
    public function getCertificatePath(): ?string
    {
        return $this->certificatePath;
    }

    /**
     * Sets the path to the certificate file
     *
     * @param string|null $certificatePath Path to the certificate file or null to unset
     * @return void
     */
    public function setCertificatePath(?string $certificatePath): void
    {
        $this->certificatePath = $certificatePath;
    }

    /**
     * Gets the path to the private key file
     *
     * @return string|null Path to the private key file or null if not set
     */
    #[\Override]
    public function getPrivateKeyPath(): ?string
    {
        return $this->privateKeyPath;
    }

    /**
     * Sets the path to the private key file
     *
     * @param string|null $privateKeyPath Path to the private key file or null to unset
     * @return void
     */
    public function setPrivateKeyPath(?string $privateKeyPath): void
    {
        $this->privateKeyPath = $privateKeyPath;
    }

    /**
     * Parses a configuration array and sets up the ICE servers and paths
     *
     * @param array{
     *     iceServers?: list<array{
     *         urls: string|list<string>,
     *         username?: string,
     *         credential?: string,
     *         credentialType?: string
     *     }>,
     *     certificatePath?: string,
     *     privateKeyPath?: string
     * } $configuration Configuration array with optional keys:
     *        - 'iceServers': Array of ICE server configurations (required if present)
     *        - 'certificatePath': Path to a certificate file (optional)
     *        - 'privateKeyPath': Path to a private key file (optional)
     * @return void
     * @throws InvalidArgumentException If iceServer configuration is missing required 'urls' key
     */
    private function parseConfiguration(array $configuration): void
    {
        if (isset($configuration['iceServers'])) {
            foreach ($configuration["iceServers"] as $iceServer) {
                if (!isset($iceServer["urls"])) {
                    throw new InvalidArgumentException("urls key is mandatory in iceServer");
                }

                $iceServerObj = new RTCIceServer();
                $iceServerObj->setUrls($iceServer["urls"]);
                $iceServerObj->setUsername($iceServer["username"] ?? null);
                $iceServerObj->setCredential($iceServer["credential"] ?? null);
                $iceServerObj->setCredentialType($iceServer["credentialType"] ?? "password");

                $this->addIceServer($iceServerObj);
            }
        }

        $this->certificatePath = $configuration["certificatePath"] ?? null;
        $this->privateKeyPath = $configuration["privateKeyPath"] ?? null;
    }

    /**
     * Sets up default configuration with Google's public STUN server
     *
     * @return void
     */
    private function getDefaultConfiguration(): void
    {
        $iceServer = new RTCIceServer();
        $iceServer->setUrls([self::DEFAULT_STUN_SERVER]);
        $this->addIceServer($iceServer);
    }

    /**
     * Gets the ICE settings instance associated with this configuration.
     *
     * This object allows customization of advanced ICE parameters such as
     * port ranges, NAT mappings, ICE-Lite mode, and transport policies.
     *
     * @return RTCICESetting The ICE settings instance
     */
    #[\Override]
    public function iceSettings(): RTCICESetting
    {
        return $this->iceSettings;
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Webrtc;

use Webrtc\ICE\RTCICESetting;

interface RTCConfigurationInterface
{
    public function getIceServers(): array;

    public function getCertificatePath(): ?string;

    public function getPrivateKeyPath(): ?string;

    public function iceSettings(): RTCICESetting;
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Webrtc\Enum;

enum SignalingState: int
{
    case closed = 0;
    case haveLocalOffer = 1;
    case haveLocalPranswer = 2;
    case haveRemoteOffer = 3;
    case stable = 4;
    case haveRemotePranswer = 5;
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Webrtc\Enum;

enum IceConnectionState: int
{
    case checking = 0;
    case completed = 1;
    case closed = 2;
    case failed = 3;
    case new = 4;
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Webrtc\Enum;

enum ConnectionState: int
{
    case connected = 0;
    case connecting = 1;
    case closed = 2;
    case failed = 3;
    case new = 4;
}
{
    "name": "danog/php-rtc-webrtc",
    "type": "library",
  "description": "A modular PHP WebRTC stack for PHP 8.2+, ported to Amp v3 with pure-PHP DTLS/SRTP and optional media FFI. Maintained by danog for MadelineProto.",
    "keywords": [
        "rtp",
        "webrtc"
    ],
    "homepage": "https://github.com/danog/php-rtc-webrtc",
    "license": "BSD-3-Clause",
    "authors": [
        {
            "name": "Amin Yazdanpanah",
            "email": "github@aminyazdanpanah.com"
        },
        {
            "name": "Sana Moniri",
            "email": "s.mo@quasarstream.com"
        },
        {
            "name": "Quasar Stream Community"
        },
        {
            "name": "Daniil Gentili",
            "email": "daniil@daniil.it"
        }
    ],
    "require": {
        "php": "^8.2",
        "amphp/amp": "^3.1.3",
        "danog/php-rtc-exception": "^1.1.5",
        "danog/php-rtc-ice": "^2.0.17",
        "danog/php-rtc-rtp": "^2.0.23",
        "danog/php-rtc-sctp": "^2.0.14",
        "danog/php-rtc-stats": "^1.1.3",
        "danog/php-rtc-sdp": "^1.1.6",
        "danog/php-rtc-dtls": "^1.1.9 || ^2.0",
        "danog/php-rtc-ntp": "^1.1.6",
        "danog/php-rtc-mixin": "^2.0",
        "symfony/polyfill-php84": "*"
    },
    "require-dev": {
        "phpunit/phpunit": "^11.5 || ^12.0 || ^13.3.2",
        "bamarni/composer-bin-plugin": "^1.9.1"
    },
    "autoload": {
        "psr-4": {
            "Webrtc\\Webrtc\\": "src"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\Webrtc\\Webrtc\\": "tests"
        }
    },
    "minimum-stability": "stable",
    "scripts": {
        "post-install-cmd": [
            "@composer bin all install --ansi"
        ],
        "post-update-cmd": [
            "@composer bin all update --ansi"
        ]
    },
    "config": {
        "allow-plugins": {
            "bamarni/composer-bin-plugin": true
        }
    },
    "extra": {
        "bamarni-bin": {
            "bin-links": true,
            "forward-command": true
        }
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTP\MediaStreamTrack;

use Amp\Pipeline\ConcurrentIterator;
use Amp\Pipeline\DisposedException;
use Amp\Pipeline\Queue;
use Revolt\EventLoop;
use Webrtc\Codecs\EncodedPacket;
use Webrtc\AVCodec\Frame\FrameInterface;
use Webrtc\RTP\Enum\MediaKind;

/**
 * Represents a remote media stream track (audio, video, etc.) in WebRTC.
 *
 * This class extends `MediaStreamTrack` and is used for handling media tracks
 * from remote sources. It manages the queue of frames and allows for receiving
 * and processing the next frame in the stream.
 */
final class RemoteStreamTrack extends MediaStreamTrack
{
    /**
     * The received frames, for the consumer of the track (see {@see self::getConsumer()}).
     *
     * @var Queue<FrameInterface|EncodedPacket>
     */
    private Queue $frameQueue;

    public function __construct(MediaKind $kind, ?string $id = null)
    {
        parent::__construct($kind, $id);
        /** @var Queue<FrameInterface|EncodedPacket> */
        $this->frameQueue = new Queue();
    }

    /**
     * The received frames.
     *
     * @return ConcurrentIterator<FrameInterface|EncodedPacket>
     */
    public function getConsumer(): ConcurrentIterator
    {
        return $this->frameQueue->iterate();
    }

    /**
     * Hand a received frame to the consumer, waiting for it to be consumed: to the sink instead, if
     * one is attached (when the track is sent by an RTP sender too, see {@see self::setSink()}).
     *
     * @param FrameInterface|EncodedPacket $frame The frame to add to the queue.
     */
    public function queueFrame(FrameInterface|EncodedPacket $frame): void
    {
        if ($this->hasSink()) {
            try {
                $this->push($frame);
            } catch (DisposedException) {
                // The sender stopped: like a consumer that doesn't read the frames any more.
            }
            return;
        }
        $this->frameQueue->push($frame);
    }

    /**
     * Adds a frame to the frame queue without waiting for it to be consumed.
     *
     * For the last frames, once no more will arrive: whoever stops the track mustn't wait for someone to read them.
     *
     * @param FrameInterface|EncodedPacket $frame The frame to add to the queue.
     */
    public function queueLastFrame(FrameInterface|EncodedPacket $frame): void
    {
        if ($this->hasSink()) {
            EventLoop::queue($this->queueFrame(...), $frame);
            return;
        }
        $this->frameQueue->pushAsync($frame)->ignore();
    }

    #[\Override]
    public function stop(): void
    {
        if (!$this->isEnded()) {
            $this->frameQueue->complete();
        }
        parent::stop();
    }

    /**
     * @return array<string, mixed>
     */
    #[\Override]
    public function __serialize(): array
    {
        $state = parent::__serialize();
        // The frames waiting to be consumed are not saved.
        unset($state[self::class."\0frameQueue"]);

        return $state;
    }

    /**
     * @param array<string, mixed> $data
     */
    #[\Override]
    public function __unserialize(array $data): void
    {
        parent::__unserialize($data);
        /** @var Queue<FrameInterface|EncodedPacket> */
        $this->frameQueue = new Queue();
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTP\MediaStreamTrack;

use Amp\DeferredFuture;
use Amp\Pipeline\DisposedException;
use Ramsey\Uuid\Uuid;
use Webrtc\Codecs\EncodedPacket;
use Webrtc\AVCodec\Frame\FrameInterface;
use Webrtc\Mixin\SerializableState;
use Webrtc\RTP\Enum\MediaKind;
use Webrtc\RTP\Listener\MediaStreamTrackEndedListener;

/**
 * Represents a media stream track (audio, video, etc.) in WebRTC.
 *
 * This abstract class serves as the base for creating specific media stream tracks
 * (like audio or video). It provides common functionality such as managing track
 * identification, tracking whether the track has ended, and emitting events when
 * the track state changes.
 */
abstract class MediaStreamTrack
{
    /**
     * The kind of media track (e.g., "audio", "video").
     *
     * @var MediaKind
     */
    protected MediaKind $kind;

    /**
     * A unique ID for the track.
     *
     * @var string
     */
    protected string $id;

    private bool $stopped = false;

    /**
     * Takes the frames pushed with {@see self::push()}, see {@see self::setSink()}.
     *
     * Held weakly, not to keep its owner (an RTP sender) alive; not serialized: the sink attaches
     * itself again.
     *
     * @var \WeakReference<MediaStreamTrackSink>|null
     */
    private ?\WeakReference $sink = null;

    /** Completed when a sink is attached (or the track stops), for a producer waiting for one. */
    private ?DeferredFuture $sinkAttached = null;

    /** @var \WeakMap<MediaStreamTrackEndedListener, null> Listeners notified when the track ends. */
    private \WeakMap $endedListeners;

    /**
     * MediaStreamTrack constructor.
     *
     * Initializes the track with a unique ID generated using UUID.
     */
    public function __construct(MediaKind $kind, ?string $id = null)
    {
        $this->id = $id ?? Uuid::uuid4()->toString();
        $this->kind = $kind;
        /** @var \WeakMap<MediaStreamTrackEndedListener, null> */
        $this->endedListeners = new \WeakMap();
    }

    /**
     * Register a listener notified when the track ends.
     *
     * Typed replacement for on('ended'); the listener is a plain object captured by serialization.
     */
    public function addEndedListener(MediaStreamTrackEndedListener $listener): void
    {
        $this->endedListeners[$listener] = null;
    }

    /**
     * Gets the unique ID of the track.
     *
     * @return string The unique ID of the track.
     */
    public function getId(): string
    {
        return $this->id;
    }

    /**
     * Marks the track as ended and resolves any pending receive operations.
     *
     * This method stops the track and notifies its "ended" listeners. It also clears
     * any listeners attached to the track.
     */
    public function stop(): void
    {
        if ($this->stopped) {
            return;
        }

        $this->stopped = true;
        $this->sink = null;
        $this->wakeProducer();
        $this->notifyEnded();
        /** @var \WeakMap<MediaStreamTrackEndedListener, null> */
        $this->endedListeners = new \WeakMap();
    }

    private function notifyEnded(): void
    {
        foreach ($this->endedListeners as $listener => $_) {
            $listener->onMediaStreamTrackEnded();
        }
    }

    public function isEnded(): bool
    {
        return $this->stopped;
    }

    /**
     * Gets the kind of media track (audio, video, etc.).
     *
     * @return MediaKind The kind of the media track.
     */
    public function getKind(): MediaKind
    {
        return $this->kind;
    }

    /**
     * Sets a custom ID for the track.
     *
     * @param string $id The ID to set for the track.
     */
    public function setId(string $id): void
    {
        $this->id = $id;
    }

    /**
     * Hand the frames pushed with {@see self::push()} to $sink, right away, as they are pushed (or stop
     * handing them to the current one, with null), like an RTP sender does.
     *
     * The sink is held weakly: the producer is told that its consumer went away once it's collected.
     */
    final public function setSink(?MediaStreamTrackSink $sink): void
    {
        $this->sink = $sink === null ? null : \WeakReference::create($sink);
        if ($sink !== null) {
            $this->wakeProducer();
        }
    }

    /**
     * Whether a sink is attached, see {@see self::setSink()}.
     */
    final protected function hasSink(): bool
    {
        return $this->sink?->get() !== null;
    }

    /**
     * Hand a frame to the sink (see {@see self::setSink()}), waiting for one to be attached if there's none.
     *
     * @throws DisposedException If the track was stopped, or its sink went away or takes no more frames.
     */
    final protected function push(FrameInterface|EncodedPacket $frame): void
    {
        $reference = $this->sink;
        while ($reference === null) {
            if ($this->stopped) {
                throw new DisposedException();
            }
            $this->sinkAttached ??= new DeferredFuture();
            $this->sinkAttached->getFuture()->await();
            $reference = $this->sink;
        }
        $sink = $reference->get();
        if ($sink === null || !$sink->onFrame($frame)) {
            $this->sink = null;
            throw new DisposedException();
        }
    }

    private function wakeProducer(): void
    {
        if ($this->sinkAttached !== null) {
            $deferred = $this->sinkAttached;
            $this->sinkAttached = null;
            $deferred->complete();
        }
    }

    /**
     * @return array<string, mixed>
     */
    public function __serialize(): array
    {
        $state = SerializableState::export($this, [
            'sink' => null,
            'sinkAttached' => null,
            // WeakMaps cannot be serialized; snapshot their keys and rebuild on the far side.
            'endedListeners' => ['__uninitialized' => true],
        ]);
        $state['__endedListeners'] = SerializableState::weakMapToList($this->endedListeners);

        return $state;
    }

    /**
     * @param array<string, mixed> $data
     */
    public function __unserialize(array $data): void
    {
        /** @var list<MediaStreamTrackEndedListener> $endedListeners */
        $endedListeners = $data['__endedListeners'] ?? [];
        unset($data['__endedListeners']);
        SerializableState::import($this, $data);
        /** @var \WeakMap<MediaStreamTrackEndedListener, null> */
        $this->endedListeners = SerializableState::listToWeakMap($endedListeners);
    }
}
<?php

namespace Webrtc\RTP\MediaStreamTrack;

use Webrtc\AVCodec\Frame\FrameInterface;
use Webrtc\Codecs\EncodedPacket;

/**
 * Takes the frames of a track as they are pushed, like an RTP sender: see {@see MediaStreamTrack::setSink()}.
 */
interface MediaStreamTrackSink
{
    /**
     * Handle a frame of the track, right away.
     *
     * @return bool False if no more frames will be taken: the producer of the track is told that its
     *              consumer went away.
     */
    public function onFrame(FrameInterface|EncodedPacket $frame): bool;
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTP\MediaStreamTrack;

use Webrtc\RTP\Receiver\RTCRtpReceiver;
use Webrtc\RTP\RTCRtpTransceiver;

final class RTCTrackEvent
{
    /**
     * This event is fired on RTCPeerConnection when a new MediaStreamTrack is added by the remote party.
     *
     * @param RTCRtpReceiver $receiver The RTCRtpReceiver associated with the event.
     * @param MediaStreamTrack $track The MediaStreamTrack associated with the event.
     * @param RTCRtpTransceiver $transceiver The RTCRtpTransceiver associated with the event.
     */
    public function __construct(
        public RTCRtpReceiver    $receiver,
        public MediaStreamTrack  $track,
        public RTCRtpTransceiver $transceiver
    )
    {
    }
}
<?php

namespace Webrtc\RTP;

use Psr\Log\AbstractLogger;
use Psr\Log\LoggerInterface;
use Stringable;

/**
 * Prefixes log lines with the RTP component and media kind.
 *
 * Named (not anonymous) so a sender/receiver survives serialize/unserialize.
 */
final class RtpComponentLogger extends AbstractLogger
{
    public function __construct(
        private readonly string $component,
        private readonly string $kind,
        private readonly ?LoggerInterface $logger,
    ) {
    }

    /**
     * @param mixed $level
     * @param array<mixed> $context
     */
    #[\Override]
    public function log(mixed $level, string|Stringable $message, array $context = []): void
    {
        $this->logger?->log($level, sprintf('%s(%s): %s', $this->component, $this->kind, (string) $message), $context);
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTP\Sender;

use Webrtc\RTPParameter\RTCRtpSendParameters;

interface RtpSenderInterface
{
    public function start(RTCRtpSendParameters $parameters): void;

    public function stop(): void;
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTP\Sender;

use Exception;
use Psr\Log\LoggerInterface;
use Ramsey\Uuid\Uuid;
use Random\RandomException;
use Revolt\EventLoop;
use Throwable;
use Webrtc\AVCodec\Frame\AudioFrame;
use Webrtc\AVCodec\Frame\FrameInterface;
use Webrtc\Codecs\Codec;
use Webrtc\Codecs\EncodedPacket;
use Webrtc\Codecs\CodecUtility;
use Webrtc\Codecs\EncoderInterface;
use Webrtc\Exception\InvalidArgumentException;
use Webrtc\Mixin\SerializableState;
use Webrtc\NTP\NetworkTimeProtocol;
use Webrtc\RTCP\Exception\RtcpExceptionInterface;
use Webrtc\RTCP\RtcpByePacket;
use Webrtc\RTCP\RtcpConstants;
use Webrtc\RTCP\RtcpPacketInterface;
use Webrtc\RTCP\RtcpPsfbPacket;
use Webrtc\RTCP\RtcpRrPacket;
use Webrtc\RTCP\RtcpRtpfbPacket;
use Webrtc\RTCP\RtcpSdesPacket;
use Webrtc\RTCP\RtcpSenderInfo;
use Webrtc\RTCP\RtcpSourceInfo;
use Webrtc\RTCP\RtcpSrPacket;
use Webrtc\RTP\Enum\MediaKind;
use Webrtc\RTP\RtpComponentLogger;
use Webrtc\RTP\HeaderExtension\HeaderExtensions;
use Webrtc\RTP\HeaderExtension\HeaderExtensionsMap;
use Webrtc\RTP\MediaStreamTrack\MediaStreamTrack;
use Webrtc\RTP\MediaStreamTrack\MediaStreamTrackSink;
use Webrtc\RTP\RTCRTPDtlsTransportInterface;
use Webrtc\RTP\RtpConstants;
use Webrtc\RTP\RtpUtility;
use Webrtc\RTP\TransportSequenceNumbers;
use Webrtc\RTPParameter\RTCRtpCodecParameters;
use Webrtc\RTPParameter\RTCRtpSendParameters;
use Webrtc\Stats\enum\StatType;
use Webrtc\Stats\enum\TLSState;
use Webrtc\Stats\RTCOutboundRtpStreamStats;
use Webrtc\Stats\RTCRemoteInboundRtpStreamStats;
use Webrtc\Stats\RTCStatsReport;

/**
 * RTCRtpSender handles the sending of media data over RTP/RTCP.
 *
 * This class is responsible for:
 * - Encoding and packetizing media frames
 * - Sending RTP packets
 * - Generating and processing RTCP packets
 * - Handling retransmissions (RTX)
 * - Collecting and reporting statistics
 * - Managing the media track and transport
 */
final class RTCRtpSender implements RtpSenderInterface, MediaStreamTrackSink
{
    /** @var float Smoothing factor for RTT calculations */
    private const RTT_ALPHA = 0.85;

    /** @var MediaKind The kind of media (e.g., "audio" or "video") */
    private MediaKind $kind;

    /** @var MediaStreamTrack|null The media track being sent */
    private ?MediaStreamTrack $track = null;

    /** @var string|null The CNAME for RTCP */
    private ?string $cname = null;

    /** @var int The SSRC for RTP */
    private int $ssrc;

    /** @var int The SSRC for RTX (retransmissions) */
    private int $rtxSsrc;

    /** @var string The stream ID */
    private string $streamId;

    /** @var bool Whether the sender is enabled */
    private bool $enabled = true;

    /**
     * Loudness, in -dBov, at or below which a packet still counts as carrying speech.
     *
     * Used to fill in the voice activity bit of the RFC 6464 audio level extension. Levels are
     * inverted, so a smaller number is louder and 127 is digital silence; -60dBov is quiet enough
     * to be inaudible in practice, which makes it a reasonable floor for a sender that has no real
     * voice activity detector.
     */
    private const SILENCE_LEVEL_DBOV = 60;

    /** @var EncoderInterface|null The encoder for media frames */
    private ?EncoderInterface $encoder = null;

    /** @var bool Whether to force the next frame to be a keyframe */
    private bool $useKeyframe = false;

    /** @var string|null The media ID (mid) for this sender */
    private ?string $mid = null;

    /**
     * The last RTP packets sent, for retransmissions, by sequence number modulo {@see RtpConstants::RTP_HISTORY_SIZE}:
     * each with the offset of its transport-wide sequence number (null if none), to renumber it.
     *
     * @var array<int, array{string, ?int}>
     */
    private array $sentRtpPackets = [];

    /** @var RTCStatsReport Statistics for this sender */
    private RTCStatsReport $stats;

    /** @var float|null The last sender report timestamp */
    private ?float $lsrTime = null;

    /** @var int|null The last sender report NTP timestamp */
    private ?int $lsr = null;

    private int $ntpTimestampHigh = 0;
    private int $ntpTimestampLow = 0;

    /** @var int The RTP timestamp */
    private int $rtpTimestamp = 0;

    /** @var int The total number of octets sent */
    private int $octetCount = 0;

    /** @var int The total number of packets sent */
    private int $packetCount = 0;

    /** @var float|null The round-trip time (RTT) estimate */
    private ?float $rtt = null;

    /** @var bool Whether the sender has started */
    private bool $started = false;

    /** @var string The track ID */
    private string $trackId;

    /** @var HeaderExtensionsMap Map of RTP header extensions */
    private HeaderExtensionsMap $headerExtensionsMap;

    /** @var int|null The payload type for RTX packets */
    private ?int $rtxPayloadType = null;



    /** @var RTCRtpCodecParameters The codec parameters */
    private RTCRtpCodecParameters $codec;

    /** @var LoggerInterface|null The logger instance */
    private ?LoggerInterface $logger = null;

    /** Consecutive failed sends, to stop a hopelessly broken sender rather than fail on every frame. */
    private int $consecutiveSendFailures = 0;
    /** How many consecutive send failures are tolerated before the sender gives up. */
    private const MAX_CONSECUTIVE_SEND_FAILURES = 100;

    /** Optional end-to-end frame encryptor; when set, each encoded frame is encrypted before it is sent. */
    private ?\Webrtc\RTP\Crypto\FrameCryptorInterface $frameCryptor = null;
    /** Bytes per RTP payload when generically fragmenting an encrypted frame. */

    /** Install (or clear) the end-to-end frame encryptor applied to outgoing frames. */
    public function setFrameCryptor(?\Webrtc\RTP\Crypto\FrameCryptorInterface $frameCryptor): void
    {
        $this->frameCryptor = $frameCryptor;
    }

    /** @var string Handle of The RTCP timer task */
    private string $rtcpTask;

    /** @var int The current RTP sequence number */
    private int $sequenceNumber;

    /** @var int The current RTX sequence number */
    private int $rtxSequenceNumber;

    /** @var int The original timestamp offset */
    private int $orgTimestamp;

    /**
     * Constructs a new RTCRtpSender instance.
     *
     * @param MediaKind|MediaStreamTrack $trackOrKind Either a MediaStreamTrack or a media kind ("audio" or "video")
     * @param RTCRTPDtlsTransportInterface $transport The transport for sending media
     * @throws InvalidArgumentException If the transport is closed
     * @throws RandomException If random number generation fails
     */
    public function __construct(MediaKind|MediaStreamTrack $trackOrKind, private RTCRTPDtlsTransportInterface $transport)
    {
        if ($transport->getState() === TLSState::CLOSED) {
            throw new InvalidArgumentException("Transport is closed");
        }

        if ($trackOrKind instanceof MediaStreamTrack) {
            $this->kind = $trackOrKind->getKind();
            $this->replaceTrack($trackOrKind);
        } else {
            $this->kind = $trackOrKind;
            $this->replaceTrack(null);
        }

        $this->ssrc = random_int(0, 0xFFFFFFFF);
        $this->rtxSsrc = random_int(0, 0xFFFFFFFF);
        $this->orgTimestamp = random_int(0, 0xFFFFFFFF);
        $this->sequenceNumber = $this->generateSequenceNumber();
        $this->rtxSequenceNumber = random_int(0, 0xFFFF);
        $this->streamId = Uuid::uuid4()->toString();
        $this->stats = new RTCStatsReport();
        $this->headerExtensionsMap = new HeaderExtensionsMap();
        $this->codec = new RTCRtpCodecParameters("", 0);
        $this->rtcpTask = "";
    }

    /**
     * Gets the kind of media for this sender.
     *
     * @return MediaKind The media kind ("audio" or "video")
     */
    public function getKind(): MediaKind
    {
        return $this->kind;
    }

    /**
     * Gets the media track being sent.
     *
     * @return MediaStreamTrack|null The media track or null if not set
     */
    public function getTrack(): ?MediaStreamTrack
    {
        return $this->track;
    }

    /**
     * Gets the transport used for sending media.
     *
     * @return RTCRTPDtlsTransportInterface The transport
     */
    public function getTransport(): RTCRTPDtlsTransportInterface
    {
        return $this->transport;
    }

    /**
     * Replaces the media track being sent.
     *
     * @param MediaStreamTrack|null $track The new media track or null to clear
     */
    public function replaceTrack(?MediaStreamTrack $track): void
    {
        if ($this->started && $this->track !== $track) {
            $this->track?->setSink(null);
            $track?->setSink($this);
        }
        $this->track = $track;
        $this->trackId = $track ? $track->getId() : Uuid::uuid4()->toString();
    }

    /**
     * Sets the transport used for sending media.
     *
     * @param RTCRTPDtlsTransportInterface $transport The new transport
     */
    public function setTransport(RTCRTPDtlsTransportInterface $transport): void
    {
        $this->transport = $transport;
    }

    /**
     * Starts sending media with the given parameters.
     *
     * @param RTCRtpSendParameters $parameters The parameters for sending media
     * @throws RandomException If random number generation fails
     */
    #[\Override]
    public function start(RTCRtpSendParameters $parameters): void
    {
        $this->cname = $parameters->rtcp->cname;
        $this->mid = $parameters->muxId;
        $this->codec = $parameters->codecs[0];
        $this->encoder = null;
        $this->rtxPayloadType = null;
        $this->transport->setRtpSender($this, $parameters);
        $this->headerExtensionsMap->configure($parameters);

        foreach ($parameters->codecs as $codec) {
            if (CodecUtility::isRtx($codec) && CodecUtility::apt($codec) === $parameters->codecs[0]->payloadType) {
                $this->rtxPayloadType = $codec->payloadType;
                break;
            }
        }

        // A connected transceiver may be negotiated again with another codec or payload type.
        // Keep the running RTP task, but make every packet after this point use the new parameters.
        if ($this->started) {
            $this->logger?->debug(" RTP Sender reconfigured");
            return;
        }

        // Start RTP and RTCP tasks
        $this->startRtpTask();
        $this->startRtcpTask();
        $this->started = true;
        $this->logger?->debug(" RTP Sender started");
    }

    /**
     * Shuts down RTP and RTCP tasks.
     */
    #[\Override]
    public function stop(): void
    {
        $this->shutdown(true);
    }

    /**
     * Stops the RTP and RTCP tasks.
     *
     * @param bool $bye Whether to tell the peer that the stream ended with an RTCP BYE packet.
     */
    private function shutdown(bool $bye): void
    {
        if (!$this->started) {
            return;
        }
        $this->started = false;

        $this->transport->removeRtpSender($this);

        // Stop RTCP and RTP Tasks
        $this->stopRtcpTask($bye);
        $this->stopRtpTask();
    }

    /**
     * Stops the RTCP task, sending a BYE packet if requested.
     */
    private function stopRtcpTask(bool $bye): void
    {
        EventLoop::cancel($this->rtcpTask);
        if ($bye) {
            $byePacket = new RtcpByePacket([$this->ssrc]);
            $this->sendRtcpPacket([$byePacket]);
        }

        $this->logger?->debug(" RTCP has ended.");
    }

    /**
     * Stops the RTP task and cleans up resources.
     */
    private function stopRtpTask(): void
    {
        if ($this->track) {
            $this->track->stop();
            $this->track = null;
        }
        $this->encoder = null;
        $this->logger?->debug(" RTP has ended.");
    }

    /**
     * Starts the periodic RTP packet sending a task.
     *
     * @throws RandomException
     */
    public function startRtpTask(): void
    {
        $this->logger?->debug("Start RTP task");
        $this->sequenceNumber = $this->generateSequenceNumber();
        $this->orgTimestamp = random_int(0, 0xFFFFFFFF);
        if ($this->track === null) {
            throw new InvalidArgumentException("Cannot start the RTP task without a media track");
        }

        EventLoop::queue($this->drainRtp(...));
    }

    /**
     * Send the frames of the track, see {@see self::onFrame()}.
     *
     * Restarted only as the first-class callable `$this->drainRtp(...)` handed to
     * EventLoop::queue(), including when unserialize re-queues it (once the track was unserialized too).
     */
    private function drainRtp(): void
    {
        // The sender may be stopped (or its track detached) between queueing and execution, in which
        // case there is nothing to send yet.
        if ($this->track === null || !$this->started) {
            return;
        }
        // Held weakly by the track: an unset()+gc can reclaim the sender, stop() (also reached from
        // __destruct) stops the track.
        $this->track->setSink($this);
    }

    /**
     * Send a frame of the track, as soon as it pushes it.
     *
     * @return bool False if the sender stopped sending (it was stopped, its track was detached, or it gave up).
     */
    #[\Override]
    public function onFrame(FrameInterface|EncodedPacket $frame): bool
    {
        // The sender may have been stopped or its track detached while iterating: stop pushing media
        // onto the wire.
        if ($this->track === null || !$this->started || $this->consecutiveSendFailures >= self::MAX_CONSECUTIVE_SEND_FAILURES) {
            return false;
        }
        // While the sender is paused (e.g. the transceiver direction dropped the
        // outgoing media, or the track was muted), drain frames off the consumer
        // without putting anything on the wire. This keeps the encoder from
        // building up a backlog that would burst out the moment we resume.
        if (!$this->enabled) {
            return true;
        }
        // A frame failing to go on the wire â€” a transient socket error, or an SRTP context that
        // cannot encrypt this packet (e.g. a stale one after a resume) â€” must never escape as an
        // uncaught event-loop exception and kill the whole process. Log it (never hide it), drop
        // the frame, and keep draining; but a fault that recurs on every frame is not transient,
        // so give up after a bounded number of consecutive failures instead of looping forever â€”
        // the higher layer (which watches the connection) can then renegotiate or end the call.
        try {
            $this->sendEncodedFrame($frame);
            $this->consecutiveSendFailures = 0;
        } catch (Throwable $e) {
            $this->logger?->error('Failed to send an outgoing RTP frame: '.$e->getMessage());
            if (++$this->consecutiveSendFailures >= self::MAX_CONSECUTIVE_SEND_FAILURES) {
                $this->logger?->error(
                    'Stopping the RTP sender after '.self::MAX_CONSECUTIVE_SEND_FAILURES
                    .' consecutive send failures; the transport needs to be renegotiated.',
                );
                return false;
            }
        }
        return true;
    }

    /**
     * Encode (or packetize) one frame from the track consumer and put it on the wire.
     */
    private function sendEncodedFrame(FrameInterface|EncodedPacket $data): void
    {
        $audioLevel = null;
        if ($data instanceof EncodedPacket) {
            $audioLevel = $data->getAudioLevel();
            if ($this->frameCryptor !== null) {
                // End-to-end encryption the way WebRTC's encoder-to-packetizer frame transformers do
                // it: the whole encoded frame is encrypted before packetization and the ciphertext is
                // then packetized by the codec's own packetizer, so the RTP payload descriptors stay in
                // the clear and any WebRTC receiver depacketizes it normally (its depacketizer-to-decoder
                // transformer then decrypts the reassembled frame). The cryptor is responsible for
                // leaving whatever the packetizer/depacketizer parse of the bitstream unencrypted.
                $data = new EncodedPacket(
                    $this->frameCryptor->encryptFrame($this->kind, $this->ssrc, $data->getData()),
                    $data->getTimestamp(),
                    $data->isKeyframe(),
                    $audioLevel,
                );
            }
        }
        if ($data instanceof FrameInterface) {
            if ($data instanceof AudioFrame) {
                $audioLevel = RtpUtility::computeAudioLevelDbov($data->getPlanes()[0]->getData(), $data->getSamples());
            }
            $useKeyFrame = $this->useKeyframe;
            $this->useKeyframe = false;
            $this->encoder ??= Codec::getEncoder($this->codec);
            $encoded = $this->encoder->encode($data, $useKeyFrame);
            if (\is_string($encoded)) {
                throw new InvalidArgumentException("Encoder returned a raw string instead of a payload list");
            }
            /** @var array{0: string[], 1: int} $encoded */
            [$payloads, $timestamp] = $encoded;
        } else {
            // pack() handles both EncodedPacket and AVCodec Packet: pre-encoded frames are
            // passed straight through (still packetized for video), while raw packets have
            // their timebase converted to the codec's RTP clock.
            $this->encoder ??= Codec::getEncoder($this->codec);
            $encoded = $this->encoder->pack($data);
            if (\is_string($encoded)) {
                throw new InvalidArgumentException("Encoder returned a raw string instead of a payload list");
            }
            /** @var array{0: string[], 1: int} $encoded */
            [$payloads, $timestamp] = $encoded;
        }

        if (empty($payloads)) {
            return;
        }

        // Raw frames encoded here are packetized straight away: end-to-end encryption of the encoded
        // frame is only possible for pre-encoded frames (EncodedPacket), see above.
        if ($this->frameCryptor !== null && $data instanceof FrameInterface) {
            throw new InvalidArgumentException('End-to-end frame encryption requires pre-encoded frames (EncodedPacket)');
        }

        if ($this->codec->payloadType === null) {
            throw new InvalidArgumentException("Cannot generate RTP packet: codec has no payload type");
        }
        // The header extensions are the same for every packet of a frame, sent at the same time.
        [$ntpTimeHigh, $ntpTimeLow] = NetworkTimeProtocol::currentNtpTime();
        $extensions = new HeaderExtensions();
        // abs-send-time is a 6.18 fixed point value: bits 14..37 of the raw NTP timestamp.
        $extensions->setAbsSendTime((($ntpTimeHigh & 0x3F) << 18) | ($ntpTimeLow >> 14));
        $extensions->setMid($this->mid);
        if ($audioLevel !== null) {
            // RFC 6464 carries the loudness as 0..127 in -dBov, 0 being the loudest. Clamp rather
            // than let an out of range value wrap into a nonsensical one.
            $level = min(127, max(0, -$audioLevel));
            // The V bit reports voice activity, and the extension is negotiated with "vad=on"
            // unless the attribute says otherwise, so a sender that leaves it clear is telling the
            // receiver it is silent on every single packet. Selective forwarding units use exactly
            // that to decide whose audio to relay, so anything above the silence floor has to
            // report activity.
            $extensions->setAudioLevel([$level <= self::SILENCE_LEVEL_DBOV, $level]);
        }
        $transport = $this->transport instanceof TransportSequenceNumbers ? $this->transport : null;
        [$extensionProfile, $extensionValue, $transportSequenceNumberOffset] = $this->headerExtensionsMap->setForFrame($extensions, $transport !== null);
        // Version 2, no padding nor CSRCs.
        $firstByte = 0x80;
        $extensionHeader = '';
        if ($extensionValue !== '') {
            $firstByte |= 0x10;
            $extensionHeader = pack('n2', $extensionProfile, intdiv(strlen($extensionValue), 4)).$extensionValue;
        }
        // Every packet has its own transport-wide sequence number, in between.
        $afterTransportSequenceNumber = '';
        if ($transportSequenceNumberOffset !== null) {
            $afterTransportSequenceNumber = substr($extensionHeader, 4 + $transportSequenceNumberOffset + 2);
            $extensionHeader = substr($extensionHeader, 0, 4 + $transportSequenceNumberOffset);
            // Of the whole packet.
            $transportSequenceNumberOffset += 12 + 4;
        }
        $timestamp = ($this->orgTimestamp + $timestamp) & 0xFFFFFFFF;
        $last = count($payloads) - 1;
        foreach ($payloads as $i => $payload) {
            $packet = pack(
                'CCnNN',
                $firstByte,
                ($i === $last ? 0x80 : 0) | $this->codec->payloadType,
                $this->sequenceNumber,
                $timestamp,
                $this->ssrc,
            ).$extensionHeader;
            if ($transport !== null && $transportSequenceNumberOffset !== null) {
                $packet .= pack('n', $transport->nextTransportSequenceNumber()).$afterTransportSequenceNumber;
            }
            $packet .= $payload;
            $this->logger?->debug("Sending packet: seq={$this->sequenceNumber}, ts=$timestamp, ".strlen($packet).' bytes');
            $this->sentRtpPackets[$this->sequenceNumber % RtpConstants::RTP_HISTORY_SIZE] = [$packet, $transportSequenceNumberOffset];
            $this->transport->sendRtp($packet);
            $this->octetCount += strlen($payload);
            $this->packetCount++;
            $this->sequenceNumber = ($this->sequenceNumber + 1) & 0xFFFF;
        }
        // For the sender reports.
        $this->ntpTimestampHigh = $ntpTimeHigh;
        $this->ntpTimestampLow = $ntpTimeLow;
        $this->rtpTimestamp = $timestamp;
    }

    /**
     * Starts the periodic RTCP packet sending a task.
     *
     * @throws RandomException If random number generation fails
     */
    private function startRtcpTask(): void
    {
        $this->logger?->debug("RTCP started");

        // Weak self-reference: a repeat watcher registered as $this->onRtcpTimer(...) would pin
        // this sender in the event loop forever, so an unset()+gc could never reclaim it. The tick
        // cancels itself once the owner has been collected.
        $weak = \WeakReference::create($this);
        $this->rtcpTask = EventLoop::repeat(0.5 + ((float) random_int(0, 1000) / 1000.0), static function (string $id) use ($weak): void {
            $self = $weak->get();
            if ($self === null) {
                EventLoop::cancel($id);

                return;
            }
            $self->onRtcpTimer();
        });
    }

    /**
     * Periodic RTCP tick.
     *
     * Scheduled only as the first-class callable `$this->onRtcpTimer(...)` handed to
     * EventLoop::repeat(), including when the watcher is re-armed after unserialize. That callable
     * keeps this method's private scope, so it does not need to be public.
     */
    private function onRtcpTimer(): void
    {
        try {
            $rtcpPackets = $this->generateRtcpPackets();
            $this->sendRtcpPacket($rtcpPackets);
        } catch (RtcpExceptionInterface $e) {
            $this->logger?->warning("RTCP error: " . $e->getMessage());
        }
    }

    /**
     * Generates RTCP packets for the current state.
     *
     * @return RtcpPacketInterface[] Array of RTCP packets to send
     */
    private function generateRtcpPackets(): array
    {
        $packets = [$this->generateRtcpSrPacket()];

        // The LSR field carries the middle 32 bits of the raw NTP timestamp.
        $this->lsr = (($this->ntpTimestampHigh & 0xFFFF) << 16) | ($this->ntpTimestampLow >> 16);
        $this->lsrTime = microtime(true);

        // Generate RTCP SDES packet
        if ($this->cname !== null) {
            $packets[] = $this->generateRtcpSdesPacket($this->cname);
        }

        return $packets;
    }

    /**
     * Generates an RTCP sender report (SR) packet.
     *
     * @return RtcpSrPacket The generated SR packet
     */
    private function generateRtcpSrPacket(): RtcpSrPacket
    {
        return new RtcpSrPacket(
            ssrc: $this->ssrc,
            senderInfo: new RtcpSenderInfo(
                ntpTimestampHigh: $this->ntpTimestampHigh,
                ntpTimestampLow: $this->ntpTimestampLow,
                rtpTimestamp: $this->rtpTimestamp,
                packetCount: $this->packetCount,
                octetCount: $this->octetCount
            )
        );
    }

    /**
     * Generates an RTCP source description (SDES) packet.
     *
     * @param string $cname The canonical name (CNAME) to advertise in the SDES item
     * @return RtcpSdesPacket The generated SDES packet
     */
    private function generateRtcpSdesPacket(string $cname): RtcpSdesPacket
    {
        return new RtcpSdesPacket([
            new RtcpSourceInfo(
                ssrc: $this->ssrc,
                items: [[1, $cname]]
            )
        ]);
    }

    /**
     * Sends RTCP packets over the transport.
     *
     * @param array<RtcpPacketInterface> $packets The RTCP packets to send
     */
    private function sendRtcpPacket(array $packets): void
    {
        $payload = "";
        foreach ($packets as $packet) {
            $this->logger?->debug("Sending RTCP packet: $packet");
            $payload .= $packet->encode();
        }

        try {
            $this->transport->sendRtcp($payload);
        } catch (Throwable $e) {
            $this->logger?->warning("Failed to send RTCP: " . $e->getMessage());
        }
    }

    /**
     * Handles an incoming RTCP packet.
     *
     * @param RtcpPacketInterface $packet The received RTCP packet
     * @throws Throwable If there's an error with DTLS
     */
    public function handleRtcpPacket(RtcpPacketInterface $packet): void
    {
        if ($packet instanceof RtcpRrPacket || $packet instanceof RtcpSrPacket) {
            $this->handleReportRtcpPacket($packet);
        } elseif ($packet instanceof RtcpRtpfbPacket && $packet->getFmt() === RtcpConstants::RTCP_RTPFB_NACK) {
            foreach ($packet->getLost() as $seq) {
                $this->retransmit($seq);
            }
        } elseif ($packet instanceof RtcpPsfbPacket && $packet->getFmt() === RtcpConstants::RTCP_PSFB_PLI) {
            $this->sendKeyframe();
        } elseif ($packet instanceof RtcpPsfbPacket && $packet->getFmt() === RtcpConstants::RTCP_PSFB_APP) {
            $this->changeMediaBitrate($packet);
        }
    }

    /**
     * Handles RTCP report packets (RR or SR).
     *
     * @param RtcpSrPacket|RtcpRrPacket $packet The report packet
     */
    private function handleReportRtcpPacket(RtcpSrPacket|RtcpRrPacket $packet): void
    {
        foreach ($packet->getReports() as $report) {
            if ($report->getSsrc() !== $this->ssrc) {
                continue;
            }

            // Only the round-trip time requires a locally-sent SR for the LSR reference; the
            // remote-inbound statistics are recorded regardless of whether that reference exists.
            if ($this->lsrTime !== null) {
                // Estimate round-trip time
                $rtt = microtime(true) - $this->lsrTime - ((float) $report->getDlsr() / 65536.0);
                $this->rtt = $this->rtt === null ? $rtt : self::RTT_ALPHA * $this->rtt + (1.0 - self::RTT_ALPHA) * $rtt;
            }

            // Update statistics
            $this->stats->add(new RTCRemoteInboundRtpStreamStats(
                id: "remote_inbound_rtp_stream_" . spl_object_id($this),
                ssrc: $packet->getSsrc(),
                kind: $this->kind->value,
                transportId: $this->transport->getReportTransport()->id,
                packetsReceived: $this->packetCount - $report->getPacketsLost(),
                packetsLost: $report->getPacketsLost(),
                jitter: $report->getJitter(),
                roundTripTime: $this->rtt ?? 0.0,
                fractionLost: (float) $report->getFractionLost()
            ));
        }
    }

    /**
     * Retransmits an RTP packet identified by sequence number.
     *
     * @param int $sequenceNumber The sequence number of the packet to retransmit
     * @throws RandomException
     * @throws Throwable
     */
    public function retransmit(int $sequenceNumber): void
    {
        [$packet, $transportSequenceNumberOffset] = $this->sentRtpPackets[$sequenceNumber % RtpConstants::RTP_HISTORY_SIZE] ?? [null, null];
        if ($packet === null || ((ord($packet[2]) << 8) | ord($packet[3])) !== $sequenceNumber) {
            return;
        }
        // A retransmission is another packet on the transport (it's in the same place in an RTX one).
        if ($transportSequenceNumberOffset !== null && $this->transport instanceof TransportSequenceNumbers) {
            $packet = substr_replace($packet, pack('n', $this->transport->nextTransportSequenceNumber()), $transportSequenceNumberOffset, 2);
        }
        if ($this->rtxPayloadType !== null) {
            // RFC 4588: the same header (and extensions) on the RTX stream, the payload prefixed by
            // the original sequence number.
            $headerLength = 12 + 4 * (ord($packet[0]) & 0x0F);
            if (ord($packet[0]) & 0x10) {
                $headerLength += 4 + 4 * ((ord($packet[$headerLength + 2]) << 8) | ord($packet[$headerLength + 3]));
            }
            $packet = $packet[0]
                .chr((ord($packet[1]) & 0x80) | $this->rtxPayloadType)
                .pack('n', $this->rtxSequenceNumber)
                .substr($packet, 4, 4)
                .pack('N', $this->rtxSsrc)
                .substr($packet, 12, $headerLength - 12)
                .pack('n', $sequenceNumber)
                .substr($packet, $headerLength);
            $this->rtxSequenceNumber = ($this->rtxSequenceNumber + 1) & 0xFFFF;
        }

        $this->logger?->debug("Retransmitting packet: seq=$sequenceNumber");
        $this->transport->sendRtp($packet);
    }

    /**
     * Adjusts media bitrate based on REMB feedback.
     *
     * @param RtcpPsfbPacket $packet The REMB feedback packet
     */
    private function changeMediaBitrate(RtcpPsfbPacket $packet): void
    {
        try {
            [$bitrate, $ssrcs] = RtpUtility::unpackRembFci($packet->getFci());
            if (in_array($this->ssrc, $ssrcs)) {
                $this->logger?->debug("Receiver estimated maximum bitrate: $bitrate bps");
                $this->encoder?->setBitrate($bitrate);
            }
        } catch (Exception) {
            // Ignore invalid REMB packets
        }
    }


    /**
     * Checks if the next frame should be a keyframe.
     *
     * @return bool True if the next frame should be a keyframe
     */
    public function isUseKeyframe(): bool
    {
        return $this->useKeyframe;
    }

    /**
     * Gets the last sender report NTP timestamp.
     *
     * @return int|null The NTP timestamp or null if not available
     */
    public function getLsr(): ?int
    {
        return $this->lsr;
    }

    /**
     * Gets the track ID.
     *
     * @return string The track ID
     */
    public function getTrackId(): string
    {
        return $this->trackId;
    }

    /**
     * Gets the header extensions map.
     *
     * @return HeaderExtensionsMap The header extensions map
     */
    public function getHeaderExtensionsMap(): HeaderExtensionsMap
    {
        return $this->headerExtensionsMap;
    }

    /**
     * Sets the logger instance.
     *
     * @param LoggerInterface|null $logger The logger instance
     */
    public function setLogger(?LoggerInterface $logger): void
    {
        $this->logger = new RtpComponentLogger('RTCRtpSender', $this->kind->value, $logger);
    }

    /**
     * Checks if the sender is enabled.
     *
     * @return bool True if the sender is enabled
     */
    public function isEnabled(): bool
    {
        return $this->enabled;
    }

    /**
     * Enables or disables (pauses) the sender.
     *
     * While disabled the RTP task keeps consuming frames from the track but sends nothing,
     * effectively pausing the outgoing media. On resume a keyframe is forced so a video
     * receiver can start decoding again without waiting for the next natural keyframe.
     *
     * @param bool $enabled True to resume sending, false to pause
     */
    public function setEnabled(bool $enabled): void
    {
        if ($enabled === $this->enabled) {
            return;
        }

        $this->enabled = $enabled;

        if ($enabled && $this->kind === MediaKind::Video) {
            $this->sendKeyframe();
        }
    }

    /**
     * Gets the SSRC for RTP packets.
     *
     * @return int The SSRC
     */
    public function getSsrc(): int
    {
        return $this->ssrc;
    }

    /**
     * Requests the next frame to be a keyframe.
     */
    public function sendKeyframe(): void
    {
        $this->useKeyframe = true;
    }

    /**
     * Gets the stream ID.
     *
     * @return string The stream ID
     */
    public function getStreamId(): string
    {
        return $this->streamId;
    }

    /**
     * Destructor - releases the tasks of the sender when the object is destroyed.
     *
     * No BYE is sent: the object being destroyed doesn't mean the stream ended, for example when the
     * process exits to be restarted, with the session going on in the next one.
     */
    public function __destruct()
    {
        $this->shutdown(false);
    }

    /**
     * Gets the current statistics report.
     *
     * @return RTCStatsReport The statistics report
     */
    public function getStats(): RTCStatsReport
    {
        $this->stats->add(new RTCOutboundRtpStreamStats(
            id: "outbound_rtp_stream_" . spl_object_id($this),
            type: StatType::OutboundRtpStream,
            ssrc: $this->ssrc,
            kind: $this->kind->value,
            transportId: $this->transport->getReportTransport()->id,
            packetsSent: $this->packetCount,
            bytesSent: $this->octetCount,
            trackId: $this->trackId
        ));
        $this->stats->add($this->transport->getReportTransport());

        return $this->stats;
    }

    /**
     * Sets the SSRC for RTP packets.
     *
     * @param int $ssrc The SSRC to set
     */
    public function setSsrc(int $ssrc): void
    {
        $this->ssrc = $ssrc;
    }

    /**
     * Sets the SSRC for RTX packets.
     *
     * @param int $rtxSsrc The RTX SSRC to set
     */
    public function setRtxSsrc(int $rtxSsrc): void
    {
        $this->rtxSsrc = $rtxSsrc;
    }

    /**
     * Sets the stream ID.
     *
     * @param string $streamId The stream ID to set
     */
    public function setStreamId(string $streamId): void
    {
        $this->streamId = $streamId;
    }

    /**
     * Gets the SSRC for RTX packets.
     *
     * @return int The RTX SSRC
     */
    public function getRtxSsrc(): int
    {
        return $this->rtxSsrc;
    }

    /**
     * Generates a random sequence number for RTP packets.
     *
     * @return int The generated sequence number
     * @throws RandomException
     */
    private function generateSequenceNumber(): int
    {
        $bytes = random_bytes(2);
        $unpacked = unpack('n', $bytes);
        if ($unpacked === false) {
            throw new InvalidArgumentException("Failed to unpack sequence number");
        }

        return (int) $unpacked[1] % 32768;
    }

    /**
     * @return array<string, mixed>
     */
    public function __serialize(): array
    {
        return SerializableState::export($this, [
            'rtcpTask' => $this->started && $this->rtcpTask !== '',
            'encoder' => null,
        ]);
    }

    /**
     * @param array<string, mixed> $data
     */
    public function __unserialize(array $data): void
    {
        $restartRtcp = false;
        /**
         * @var mixed $value
         */
        foreach ($data as $key => $value) {
            if (str_ends_with($key, "\0rtcpTask")) {
                $restartRtcp = $value === true;
                $data[$key] = '';
            }
            if (str_ends_with($key, "\0encoder")) {
                unset($data[$key]);
            }
        }
        SerializableState::import($this, $data);
        $this->rtcpTask = '';
        $this->encoder = null;
        if ($restartRtcp) {
            $this->startRtcpTask();
        }
        if ($this->started && $this->track !== null) {
            EventLoop::queue($this->drainRtp(...));
        }
    }
}
<?php

/*final *
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTP\Sender;

final readonly class RTCEncodedFrame
{
    public function __construct(
        /**
         * @var string[]
         */
        private array $payloads,
        private int   $timestamp,
        private ?int   $audioLevel
    )
    {
    }

    /**
     * @return ?int
     */
    public function getAudioLevel(): ?int
    {
        return $this->audioLevel;
    }

    /**
     * @return int
     */
    public function getTimestamp(): int
    {
        return $this->timestamp;
    }

    /**
     * @return string[]
     */
    public function getPayloads(): array
    {
        return $this->payloads;
    }
}<?php

namespace Webrtc\RTP\Listener;

/**
 * Notified when a {@see \Webrtc\RTP\MediaStreamTrack\MediaStreamTrack} has ended.
 *
 * Typed replacement for the former Evenement "ended" event. The listener is a plain object
 * captured verbatim by serialization.
 */
interface MediaStreamTrackEndedListener
{
    public function onMediaStreamTrackEnded(): void;
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTP\HeaderExtension;

use Webrtc\RTP\RtpUtility;
use Webrtc\RTPParameter\RTCRtpParameters;

/**
 * Maps RTP header extension URIs to their corresponding IDs and values.
 *
 * This class helps configure and extract RTP header extensions based on SDP
 * negotiation or other signaling information, and then allows parsing and packing
 * of the actual header extension values during RTP processing.
 */
final class HeaderExtensionsMap
{
    private HeaderExtensions $ids;

    /**
     * Constructs a new HeaderExtensionsMap with an empty extension ID registry.
     */
    public function __construct()
    {
        $this->ids = new HeaderExtensions();
    }

    /**
     * Configures the extension ID mappings from RTCRtpParameters.
     *
     * @param RTCRtpParameters $parameters The RTP parameters with negotiated extensions.
     */
    public function configure(RTCRtpParameters $parameters): void
    {
        foreach ($parameters->headerExtensions as $ext) {
            switch ($ext->uri) {
                case "urn:ietf:params:rtp-hdrext:sdes:mid":
                    $this->ids->setMid($ext->id);
                    break;
                case "urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id":
                    $this->ids->setRepairedRtpStreamId($ext->id);
                    break;
                case "urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id":
                    $this->ids->setRtpStreamId($ext->id);
                    break;
                case "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time":
                    $this->ids->setAbsSendTime($ext->id);
                    break;
                case "urn:ietf:params:rtp-hdrext:toffset":
                    $this->ids->setTransmissionOffset($ext->id);
                    break;
                case "urn:ietf:params:rtp-hdrext:ssrc-audio-level":
                    $this->ids->setAudioLevel($ext->id);
                    break;
                case "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01":
                    $this->ids->setTransportSequenceNumber($ext->id);
                    break;
            }
        }
    }

    /**
     * Parses incoming RTP header extensions from binary data.
     *
     * @param int $extensionProfile The RTP extension profile ID.
     * @param string $extensionValue The raw RTP header extension bytes.
     * @return HeaderExtensions        The parsed extensions with mapped values.
     */
    public function get(int $extensionProfile, string $extensionValue): HeaderExtensions
    {
        $headerExtensions = new HeaderExtensions();
        $extensions = RtpUtility::unpackHeaderExtensions($extensionProfile, $extensionValue);

        foreach ($extensions as [$xId, $xValue]) {
            switch ($xId) {
                case $this->ids->getMid():
                    $headerExtensions->setMid($xValue);
                    break;
                case $this->ids->getRepairedRtpStreamId():
                    $headerExtensions->setRepairedRtpStreamId($xValue);
                    break;
                case $this->ids->getRtpStreamId():
                    $headerExtensions->setRtpStreamId($xValue);
                    break;
                case $this->ids->getAbsSendTime():
                    $absSendTime = unpack("N", "\x00" . $xValue);
                    assert($absSendTime !== false);
                    $headerExtensions->setAbsSendTime((int) $absSendTime[1]);
                    break;
                case $this->ids->getTransmissionOffset():
                    $transmissionOffset = unpack("N", $xValue . "\x00");
                    assert($transmissionOffset !== false);
                    $headerExtensions->setTransmissionOffset(((int) $transmissionOffset[1]) >> 8);
                    break;
                case $this->ids->getAudioLevel():
                    $vadLevel = unpack("C", $xValue);
                    assert($vadLevel !== false);
                    $level = (int) $vadLevel[1];
                    $headerExtensions->setAudioLevel([(bool)($level & 0x80), $level & 0x7F]);
                    break;
                case $this->ids->getTransportSequenceNumber():
                    $transportSeq = unpack("n", $xValue);
                    assert($transportSeq !== false);
                    $headerExtensions->setTransportSequenceNumber((int) $transportSeq[1]);
                    break;
            }
        }

        return $headerExtensions;
    }

    /**
     * Packs RTP header extension values into binary format for outgoing packets.
     *
     * @param HeaderExtensions $values The extension values to encode.
     * @return array{0: int, 1: string} Encoded RTP header extensions as (profile, value).
     */
    public function set(HeaderExtensions $values): array
    {
        return RtpUtility::packHeaderExtensions($this->toList($values));
    }

    /**
     * Packs the header extensions of all packets of a frame, which only differ in their transport-wide
     * sequence number: it's left to fill in, at the returned offset of the extension value.
     *
     * @param HeaderExtensions $values The extension values to encode, but the transport-wide sequence number.
     * @param bool $transportSequenceNumber Whether to number the packets, if negotiated.
     * @return array{0: int, 1: string, 2: ?int} Encoded RTP header extensions as (profile, value), and the
     *                                         offset of the transport-wide sequence number (null if none).
     */
    public function setForFrame(HeaderExtensions $values, bool $transportSequenceNumber): array
    {
        $extensions = $this->toList($values);
        $id = $this->ids->getTransportSequenceNumber();
        if (!$transportSequenceNumber || $id === null) {
            return [...RtpUtility::packHeaderExtensions($extensions), null];
        }
        // The last one, so it ends right before the padding.
        $extensions[] = [$id, "\0\0"];
        [$profile, $value] = RtpUtility::packHeaderExtensions($extensions);
        $length = 0;
        foreach ($extensions as [, $xValue]) {
            $length += ($profile === 0xBEDE ? 1 : 2) + strlen($xValue);
        }

        return [$profile, $value, $length - 2];
    }

    /**
     * @return list<array{0: int, 1: string}> The negotiated extensions, by ID.
     */
    private function toList(HeaderExtensions $values): array
    {
        $extensions = [];

        if (!is_null($values->getMid()) && !is_null($this->ids->getMid())) {
            $extensions[] = [(int) $this->ids->getMid(), (string) $values->getMid()];
        }
        if (!is_null($values->getRepairedRtpStreamId()) && !is_null($this->ids->getRepairedRtpStreamId())) {
            $extensions[] = [(int) $this->ids->getRepairedRtpStreamId(), (string) $values->getRepairedRtpStreamId()];
        }
        if (!is_null($values->getRtpStreamId()) && !is_null($this->ids->getRtpStreamId())) {
            $extensions[] = [(int) $this->ids->getRtpStreamId(), (string) $values->getRtpStreamId()];
        }
        if (!is_null($values->getAbsSendTime()) && !is_null($this->ids->getAbsSendTime())) {
            $extensions[] = [$this->ids->getAbsSendTime(), substr(pack("N", $values->getAbsSendTime()), 1)];
        }
        if (!is_null($values->getTransmissionOffset()) && !is_null($this->ids->getTransmissionOffset())) {
            $extensions[] = [$this->ids->getTransmissionOffset(), substr(pack("l", $values->getTransmissionOffset() << 8), 0, 2)];
        }
        $audioLevel = $values->getAudioLevel();
        $audioLevelId = $this->ids->getAudioLevel();
        if (is_array($audioLevel) && is_int($audioLevelId)) {
            $extensions[] = [$audioLevelId, pack("C", (($audioLevel[0] ? 0x80 : 0) | ($audioLevel[1] & 0x7F)))];
        }
        if (!is_null($values->getTransportSequenceNumber()) && !is_null($this->ids->getTransportSequenceNumber())) {
            $extensions[] = [$this->ids->getTransportSequenceNumber(), pack("n", $values->getTransportSequenceNumber())];
        }

        return $extensions;
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTP\HeaderExtension;

use Webrtc\Mixin\DataClass;

#[DataClass]
final class HeaderExtensions
{
    private int|string|null $mid = null;
    private int|string|null $repairedRtpStreamId = null;
    private int|string|null $rtpStreamId = null;
    private ?int $absSendTime = null;
    private ?int $transmissionOffset = null;
    /** @var array{0: bool, 1: int}|int|null */
    private array|int|null $audioLevel = null;
    private ?int $transportSequenceNumber = null;

    /**
     * @return int|string|null
     */
    public function getMid(): int|string|null
    {
        return $this->mid;
    }

    /**
     * @param int|null|string $mid
     */
    public function setMid(int|string|null $mid): void
    {
        $this->mid = $mid;
    }

    /**
     * @return int|string|null
     */
    public function getRepairedRtpStreamId(): int|string|null
    {
        return $this->repairedRtpStreamId;
    }

    /**
     * @param int|null|string $repairedRtpStreamId
     */
    public function setRepairedRtpStreamId(int|string|null $repairedRtpStreamId): void
    {
        $this->repairedRtpStreamId = $repairedRtpStreamId;
    }

    /**
     * @return int|string|null
     */
    public function getRtpStreamId(): int|string|null
    {
        return $this->rtpStreamId;
    }

    /**
     * @param int|null|string $rtpStreamId
     */
    public function setRtpStreamId(int|string|null $rtpStreamId): void
    {
        $this->rtpStreamId = $rtpStreamId;
    }

    public function getAbsSendTime(): ?int
    {
        return $this->absSendTime;
    }

    public function setAbsSendTime(?int $absSendTime): void
    {
        $this->absSendTime = $absSendTime;
    }

    public function getTransmissionOffset(): ?int
    {
        return $this->transmissionOffset;
    }

    public function setTransmissionOffset(?int $transmissionOffset): void
    {
        $this->transmissionOffset = $transmissionOffset;
    }

    /**
     * @return array{0: bool, 1: int}|int|null
     */
    public function getAudioLevel(): array|int|null
    {
        return $this->audioLevel;
    }

    /**
     * @param array{0: bool, 1: int}|int $audioLevel
     */
    public function setAudioLevel(array|int $audioLevel): void
    {
        $this->audioLevel = $audioLevel;
    }

    public function getTransportSequenceNumber(): ?int
    {
        return $this->transportSequenceNumber;
    }

    public function setTransportSequenceNumber(?int $transportSequenceNumber): void
    {
        $this->transportSequenceNumber = $transportSequenceNumber;
    }

}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTP\Exception;

final class MediaStreamTrackException extends \Exception implements RtpExceptionInterface
{

}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTP\Exception;

use Webrtc\Exception\WebrtcExceptionInterface;

interface RtpExceptionInterface extends WebrtcExceptionInterface
{

}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTP\Exception;

final class RtpSenderException extends \Exception implements RtpExceptionInterface
{

}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTP\Exception;

final class RtpPacketException extends \Exception implements RtpExceptionInterface
{

}
<?php

namespace Webrtc\RTP;

/**
 * A transport that numbers the RTP packets sent over it, for the transport-wide congestion control
 * header extension: the packets of all of its senders share the same sequence.
 */
interface TransportSequenceNumbers
{
    /**
     * The transport-wide sequence number of the next packet sent over the transport.
     *
     * @return int A 16-bit number.
     */
    public function nextTransportSequenceNumber(): int;
}
<?php

declare(strict_types=1);

namespace Webrtc\RTP\Crypto;

use Webrtc\RTP\Enum\MediaKind;

/**
 * Transforms encoded media frames on their way onto and off the wire, for end-to-end encryption that
 * sits on top of the transport (not SRTP): the sender encrypts each full encoded frame before it is
 * packetized, and the receiver decrypts the reassembled frame before it is decoded or handed on raw.
 *
 * A frame is the concatenation of the payloads of all RTP packets sharing one timestamp; the sender
 * fragments the ciphertext generically and the receiver reassembles it by timestamp, so an
 * implementation only has to be a symmetric byte transform keyed by media kind and SSRC.
 */
interface FrameCryptorInterface
{
    /**
     * Encrypt one outgoing encoded frame.
     *
     * @param MediaKind $kind  The media kind of the sending track.
     * @param int       $ssrc  The SSRC the frame is sent on.
     * @param string    $frame The full encoded frame.
     *
     * @return string The bytes to fragment and send in place of the frame.
     */
    public function encryptFrame(MediaKind $kind, int $ssrc, string $frame): string;

    /**
     * Decrypt one reassembled incoming frame.
     *
     * @param MediaKind $kind  The media kind of the receiving track.
     * @param int       $ssrc  The SSRC the frame arrived on.
     * @param string    $frame The reassembled frame bytes as produced by the sender's transform.
     *
     * @return string The original encoded frame.
     */
    public function decryptFrame(MediaKind $kind, int $ssrc, string $frame): string;
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTP;

final class RtpConstants {
    // Used for NACK and retransmission
    public const RTP_HISTORY_SIZE = 128;

    // Reserved to avoid confusion with RTCP
    public const FORBIDDEN_PAYLOAD_TYPES = [72, 73, 74, 75, 76];
    public const DYNAMIC_PAYLOAD_TYPES = [96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127];

    // Packets lost range
    public const PACKETS_LOST_MIN = -(1 << 23);
    public const PACKETS_LOST_MAX = (1 << 23) - 1;
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTP;

use Webrtc\Codecs\Codec;
use Webrtc\Exception\InvalidArgumentException;
use Webrtc\RTP\Enum\MediaKind;
use Webrtc\RTP\Receiver\RTCRtpReceiver;
use Webrtc\RTP\Sender\RTCRtpSender;
use Webrtc\RTPParameter\RTCRtpCodecParameters;
use Webrtc\RTPParameter\RTCRtpCodecCapability;
use Webrtc\RTPParameter\RTCRtpHeaderExtensionParameters;
use Webrtc\SDP\Enum\SDPDirections;

/**
 * Represents a bidirectional RTP stream with a sender and receiver for WebRTC communications.
 *
 * The RTCRtpTransceiver interface describes a permanent pairing of an RTCRtpSender
 * and an RTCRtpReceiver, along with some shared state. Each RTCRtpTransceiver
 * represents one bidirectional stream, with an RTP sender and receiver that share
 * the same media identification (mid) value.
 */
final class RTCRtpTransceiver
{
    /** @var SDPDirections|null The currently negotiated direction of the transceiver. */
    private ?SDPDirections $currentDirection = null;

    /** @var ?SDPDirections The preferred direction of the transceiver. */
    private ?SDPDirections $direction = SDPDirections::sendrecv;
    private ?SDPDirections $offerDirection = SDPDirections::sendrecv;

    /** @var MediaKind The kind of media (e.g., "audio" or "video"). */
    private MediaKind $kind;

    /** @var string|null The media ID (mid) for this transceiver. */
    private ?string $mid = null;

    /** @var int|null The media line index for this transceiver. */
    private ?int $mlineIndex = null;

    /** @var RTCRtpReceiver The receiver for this transceiver. */
    private RTCRtpReceiver $receiver;

    /** @var RTCRtpSender The sender for this transceiver. */
    private RTCRtpSender $sender;

    /** @var bool Whether the transceiver is stopped. */
    private bool $stopped = false;

    /** @var RTCRtpCodecCapability[] List of preferred codecs. */
    private array $preferredCodecs = [];

    /** @var RTCRTPDtlsTransportInterface|null The transport for this transceiver. */
    private ?RTCRTPDtlsTransportInterface $dtlsTransport = null;

    /** @var bool Whether this transceiver is bundled. */
    private bool $bundled = false;

    /** @var RTCRtpCodecParameters[] List of codecs. */
    private array $codecs = [];

    /**
     * fmtp parameter overrides to merge onto negotiated codecs, keyed by lower-cased MIME type.
     * Lets an application advertise codec-specific parameters (e.g. an H.264 profile-level-id or an
     * AV1 profile/level/tier read from the actual bitstream) that differ from the codec table's
     * defaults, since {@see self::setCodecPreferences()} can only reorder the registered capabilities.
     *
     * @var array<string, array<string, int|string|null>>
     */
    private array $codecParameterOverrides = [];

    /** @var RTCRtpHeaderExtensionParameters[] List of header extensions. */
    private array $headerExtensions = [];

    /**
     * Constructs a new RTCRtpTransceiver instance.
     *
     * @param MediaKind $kind The kind of media (e.g., "audio" or "video").
     * @param RTCRtpReceiver $receiver The receiver for this transceiver.
     * @param RTCRtpSender $sender The sender for this transceiver.
     */
    public function __construct(
        MediaKind         $kind,
        RTCRtpReceiver $receiver,
        RTCRtpSender   $sender,
    )
    {
        $this->kind = $kind;
        $this->receiver = $receiver;
        $this->sender = $sender;
    }

    /**
     * Gets the currently negotiated direction of the transceiver.
     *
     * The current direction represents the direction last negotiated in an offer/answer
     * exchange. This may differ from the preferred direction set by the application.
     *
     * @return SDPDirections|null One of 'sendrecv', 'sendonly', 'recvonly', 'inactive', or null if not negotiated yet.
     */
    public function getCurrentDirection(): ?SDPDirections
    {
        return $this->currentDirection;
    }

    /**
     * Gets the preferred direction of the transceiver.
     *
     * The preferred direction indicates the desired direction set by the application,
     * which will be used in the next offer/answer exchange.
     *
     * @return SDPDirections|null One of 'sendrecv', 'sendonly', 'recvonly', or 'inactive'.
     */
    public function getDirection(): SDPDirections|null
    {
        return $this->direction;
    }

    /**
     * Sets the preferred direction of the transceiver.
     *
     * This direction will be used in the next offer/answer exchange. The actual
     * negotiated direction may differ based on the remote peer's answer.
     *
     * @param SDPDirections $direction One of 'sendrecv', 'sendonly', 'recvonly', or 'inactive'.
     * @throws InvalidArgumentException If an invalid direction is provided.
     */
    public function setDirection(SDPDirections $direction): void
    {
        $this->direction = $direction;
    }

    /**
     * Gets the kind of media for this transceiver.
     *
     * @return MediaKind The kind of media (e.g., "audio" or "video").
     */
    public function getKind(): MediaKind
    {
        return $this->kind;
    }

    /**
     * Gets the media ID (mid) for this transceiver.
     *
     * The mid is a unique identifier for this media stream in the SDP.
     *
     * @return string|null The media ID or null if not set.
     */
    public function getMid(): ?string
    {
        return $this->mid;
    }

    /**
     * Gets the receiver for this transceiver.
     *
     * @return RTCRtpReceiver The receiver instance.
     */
    public function getReceiver(): RTCRtpReceiver
    {
        return $this->receiver;
    }

    /**
     * Gets the sender for this transceiver.
     *
     * @return RTCRtpSender The sender instance.
     */
    public function getSender(): RTCRtpSender
    {
        return $this->sender;
    }

    /**
     * Checks if the transceiver is stopped.
     *
     * A stopped transceiver can no longer send or receive media.
     *
     * @return bool True if stopped, false otherwise.
     */
    public function isStopped(): bool
    {
        return $this->stopped;
    }

    /**
     * Overrides the default codec preferences for this transceiver.
     *
     * This allows applications to specify which codecs should be preferred
     * in offer/answer negotiations, and in what order of preference.
     *
     * @param RTCRtpCodecCapability[] $codecs A list of RTCRtpCodecCapability objects in decreasing order of preference.
     * @throws InvalidArgumentException If a codec is not in the capabilities.
     */
    public function setCodecPreferences(array $codecs): void
    {
        if (empty($codecs)) {
            $this->preferredCodecs = [];
            return;
        }

        $capabilities = (new Codec())->getCapabilities($this->kind->value)->codecs;
        $unique = [];
        foreach (array_reverse($codecs) as $codec) {
            if (!in_array($codec, $capabilities)) {
                throw new InvalidArgumentException("Codec is not in capabilities");
            }
            if (!in_array($codec, $unique)) {
                array_unshift($unique, $codec);
            }
        }
        $this->preferredCodecs = $unique;
    }

    /**
     * Permanently stops the transceiver.
     *
     * This stops both the sender and receiver components and marks the transceiver
     * as stopped. A stopped transceiver cannot be restarted.
     */
    public function stop(): void
    {
        $this->receiver->stop();
        $this->sender->stop();
        $this->stopped = true;
    }

    /**
     * Gets the list of preferred codecs for this transceiver.
     *
     * @return array Array of preferred codecs in order of preference.
     */
    public function getPreferredCodecs(): array
    {
        return $this->preferredCodecs;
    }

    /**
     * Gets the DTLS transport associated with this transceiver.
     *
     * @return RTCRTPDtlsTransportInterface|null The DTLS transport or null if not set.
     */
    public function getDtlsTransport(): ?RTCRTPDtlsTransportInterface
    {
        return $this->dtlsTransport;
    }

    /**
     * Checks if this transceiver is bundled.
     *
     * Bundled transceivers share the same transport channel.
     *
     * @return bool True if bundled, false otherwise.
     */
    public function isBundled(): bool
    {
        return $this->bundled;
    }

    /**
     * Gets the list of negotiated codecs for this transceiver.
     *
     * @return array<RTCRtpCodecParameters> List of negotiated codec parameters.
     */
    public function getCodecs(): array
    {
        return $this->codecs;
    }

    /**
     * Gets the list of negotiated header extensions for this transceiver.
     *
     * @return array List of header extension parameters.
     */
    public function getHeaderExtensions(): array
    {
        return $this->headerExtensions;
    }

    /**
     * Sets the DTLS transport for this transceiver.
     *
     * @param RTCRTPDtlsTransportInterface|null $dtlsTransport The DTLS transport to associate.
     */
    public function setDtlsTransport(?RTCRTPDtlsTransportInterface $dtlsTransport): void
    {
        $this->dtlsTransport = $dtlsTransport;
    }

    /**
     * Sets the current direction of the transceiver.
     *
     * This is typically called during offer/answer negotiation to reflect the
     * agreed-upon direction with the remote peer.
     *
     * @param SDPDirections $direction One of 'sendrecv', 'sendonly', 'recvonly', or 'inactive'.
     */
    public function setCurrentDirection(SDPDirections $direction): void
    {
        $this->currentDirection = $direction;

        switch ($direction) {
            case SDPDirections::sendrecv:
                $this->sender->setEnabled(true);
                $this->receiver->setEnabled(true);
                break;
            case SDPDirections::sendonly:
                $this->sender->setEnabled(true);
                $this->receiver->setEnabled(false);
                break;
            case SDPDirections::recvonly:
                $this->sender->setEnabled(false);
                $this->receiver->setEnabled(true);
                break;
            case SDPDirections::inactive:
                $this->sender->setEnabled(false);
                $this->receiver->setEnabled(false);
                break;
            case SDPDirections::unknown:
                throw new InvalidArgumentException('Invalid direction');
        }
    }

    /**
     * Sets the media ID (mid) for this transceiver.
     *
     * @param string $mid The media ID to set.
     */
    public function setMid(string $mid): void
    {
        $this->mid = $mid;
    }

    /**
     * Gets the offer direction for this transceiver.
     *
     * The offer direction is the direction that will be used when generating an offer.
     *
     * @return SDPDirections|null The offer direction or null if not set.
     */
    public function getOfferDirection(): ?SDPDirections
    {
        return $this->offerDirection;
    }

    /**
     * Sets the offer direction for this transceiver.
     *
     * @param SDPDirections|null $offerDirection The direction to use in offers.
     */
    public function setOfferDirection(?SDPDirections $offerDirection): void
    {
        $this->offerDirection = $offerDirection;
    }

    /**
     * Sets the negotiated codecs for this transceiver, applying any fmtp parameter overrides
     * registered with {@see self::setCodecParameterOverrides()}.
     *
     * @param RTCRtpCodecParameters[] $codecs List of negotiated codec parameters.
     */
    public function setCodecs(array $codecs): void
    {
        if ($this->codecParameterOverrides !== []) {
            $codecs = array_map(function (RTCRtpCodecParameters $codec): RTCRtpCodecParameters {
                $override = $this->codecParameterOverrides[strtolower($codec->mimeType)] ?? null;
                if ($override === null) {
                    return $codec;
                }
                // Build a fresh instance so shared codec-table objects are never mutated.
                return new RTCRtpCodecParameters(
                    $codec->mimeType,
                    $codec->clockRate,
                    $codec->channels,
                    $codec->payloadType,
                    $codec->rtcpFeedback,
                    array_merge($codec->parameters, $override),
                );
            }, $codecs);
        }
        $this->codecs = $codecs;
    }

    /**
     * Register fmtp parameter overrides to merge onto negotiated codecs of matching MIME types, so the
     * generated SDP advertises codec parameters taken from the actual media instead of the codec
     * table's defaults. Pass an empty array to clear them.
     *
     * @param array<string, array<string, int|string|null>> $overrides MIME type => fmtp parameters.
     */
    public function setCodecParameterOverrides(array $overrides): void
    {
        $this->codecParameterOverrides = [];
        foreach ($overrides as $mimeType => $parameters) {
            $this->codecParameterOverrides[strtolower($mimeType)] = $parameters;
        }
        // Re-apply immediately so codecs negotiated before this call also carry the overrides.
        if ($this->codecs !== []) {
            $this->setCodecs($this->codecs);
        }
    }

    /**
     * Sets the negotiated header extensions for this transceiver.
     *
     * @param RTCRtpHeaderExtensionParameters[] $headerExtensions List of header extension parameters.
     */
    public function setHeaderExtensions(array $headerExtensions): void
    {
        $this->headerExtensions = $headerExtensions;
    }

    /**
     * Sets whether this transceiver is bundled.
     *
     * @param bool $bundled True if bundled, false otherwise.
     */
    public function setBundled(bool $bundled): void
    {
        $this->bundled = $bundled;
    }

    /**
     * Gets the media line index for this transceiver.
     *
     * @return int|null The media line index or null if not set.
     */
    public function getMlineIndex(): ?int
    {
        return $this->mlineIndex;
    }

    /**
     * Sets the media line index for this transceiver.
     *
     * @param int $idx The media line index to set.
     */
    public function setMlineIndex(int $idx): void
    {
        $this->mlineIndex = $idx;
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTP;

use Random\RandomException;
use Webrtc\RTP\Exception\RtpPacketException;
use Webrtc\RTP\HeaderExtension\HeaderExtensions;
use Webrtc\RTP\HeaderExtension\HeaderExtensionsMap;

/**
 * Represents an RTP packet with header fields and payload data.
 *
 * This class provides functionality to encode and decode RTP packets according to RFC 3550,
 * including handling of header extensions, CSRC items, and padding. It supports both
 * one-byte and two-byte header extensions as defined in RFC 5285.
 */
final class RtpPacket
{
    private const RTP_HEADER_LENGTH = 12;
    private int $version = 2;
    private int $marker = 0;
    private int $payloadType = 0;
    private int $sequenceNumber = 0;
    private int $timestamp = 0;
    private int $ssrc = 0;
    /** @var int[] */
    private array $csrc = [];
    private HeaderExtensions $extensions;
    public string $payload = "";
    private int $paddingSize = 0;
    private ?string $decodedData = null;

    /**
     * Constructs a new RTP packet.
     */
    public function __construct() {
        $this->extensions = new HeaderExtensions();
    }

    /**
     * Decodes a binary string into an RTP packet.
     *
     * @param string $data The binary RTP packet data.
     * @param HeaderExtensionsMap|null $extensionsMap Optional extensions mapping.
     * @return RtpPacket The decoded RTP packet.
     * @throws RtpPacketException If the packet is malformed or truncated.
     */
    public static function decode(string $data, ?HeaderExtensionsMap $extensionsMap = null): RtpPacket
    {
        $extensionsMap = $extensionsMap ?? new HeaderExtensionsMap();

        if (strlen($data) < self::RTP_HEADER_LENGTH) {
            throw new RtpPacketException("RTP packet length is less than " . self::RTP_HEADER_LENGTH . " bytes");
        }

        $header = self::decodeHeader($data);
        $packet = new self();
        $packet->setPayloadType($header['payloadType']);
        $packet->setMarker($header['marker']);
        $packet->setSequenceNumber($header['sequenceNumber']);
        $packet->setTimestamp($header['timestamp']);
        $packet->setSsrc($header['ssrc']);

        $pos = self::decodeCsrc($data, $header['cc'], $packet);

        if ($header['extension']) {
            $pos = self::decodeExtensions($data, $pos, $packet, $extensionsMap);
        }

        self::decodePayloadAndPadding($data, $pos, $header['padding'], $packet);

        return $packet;
    }

    /**
     * Decodes the RTP header fields from binary data.
     *
     * @param string $data The binary RTP packet data.
     * @return array{version: int, padding: int, extension: int, cc: int, marker: int, payloadType: int, sequenceNumber: int, timestamp: int, ssrc: int} Decoded header fields.
     * @throws RtpPacketException If the version is invalid.
     */
    private static function decodeHeader(string $data): array
    {
        $header = unpack("C1vpxcc/C1mpt/n1sequence/N1timestamp/N1ssrc", substr($data, 0, self::RTP_HEADER_LENGTH));
        if ($header === false) {
            throw new RtpPacketException("RTP packet header is malformed");
        }
        $vpxcc = (int) $header['vpxcc'];
        $mpt = (int) $header['mpt'];
        $sequence = (int) $header['sequence'];
        $timestamp = (int) $header['timestamp'];
        $ssrc = (int) $header['ssrc'];

        $version = $vpxcc >> 6;
        if ($version !== 2) {
            throw new RtpPacketException("RTP packet has invalid version");
        }

        return [
            'version' => $version,
            'padding' => ($vpxcc >> 5) & 1,
            'extension' => ($vpxcc >> 4) & 1,
            'cc' => $vpxcc & 0x0F,
            'marker' => $mpt >> 7,
            'payloadType' => $mpt & 0x7F,
            'sequenceNumber' => $sequence,
            'timestamp' => $timestamp,
            'ssrc' => $ssrc
        ];
    }

    /**
     * Decodes CSRC items from the RTP packet.
     *
     * @param string $data The binary RTP packet data.
     * @param int $cc The CSRC count from the header.
     * @param RtpPacket $packet The packet to populate with CSRC items.
     * @return int The new position in the packet after decoding CSRCs.
     * @throws RtpPacketException If CSRC data is truncated.
     */
    private static function decodeCsrc(string $data, int $cc, RtpPacket $packet): int
    {
        $pos = self::RTP_HEADER_LENGTH;
        for ($i = 0; $i < $cc; $i++) {
            if (strlen($data) < $pos + 4) {
                throw new RtpPacketException("RTP packet has truncated CSRC");
            }
            $raw = unpack("N", substr($data, $pos, 4));
            if ($raw === false) {
                throw new RtpPacketException("RTP packet has invalid CSRC");
            }
            $packet->csrc[] = (int) $raw[1];
            $pos += 4;
        }
        return $pos;
    }

    /**
     * Decodes header extensions from the RTP packet.
     *
     * @param string $data The binary RTP packet data.
     * @param int $pos The current position in the packet.
     * @param RtpPacket $packet The packet to populate with extensions.
     * @param HeaderExtensionsMap $extensionsMap The extensions mapping.
     * @return int The new position in the packet after decoding extensions.
     * @throws RtpPacketException If extension data is truncated.
     */
    private static function decodeExtensions(string $data, int $pos, RtpPacket $packet, HeaderExtensionsMap $extensionsMap): int
    {
        if (strlen($data) < $pos + 4) {
            throw new RtpPacketException("RTP packet has truncated extension profile/length");
        }

        $extHeader = unpack("n1profile/n1length", substr($data, $pos, 4));
        if ($extHeader === false) {
            throw new RtpPacketException("RTP packet has malformed extension header");
        }
        $profile = (int) $extHeader['profile'];
        $extLength = (int) $extHeader['length'] * 4;
        $pos += 4;

        if (strlen($data) < $pos + $extLength) {
            throw new RtpPacketException("RTP packet has truncated extension value");
        }

        $extensionValue = substr($data, $pos, $extLength);
        $pos += $extLength;
        $packet->setExtensions($extensionsMap->get($profile, $extensionValue));

        return $pos;
    }

    /**
     * Decodes the payload and padding from the RTP packet.
     *
     * @param string $data The binary RTP packet data.
     * @param int $pos The current position in the packet.
     * @param int $padding Flag indicating if padding is present.
     * @param RtpPacket $packet The packet to populate with payload.
     * @throws RtpPacketException If padding length is invalid.
     */
    private static function decodePayloadAndPadding(string $data, int $pos, int $padding, RtpPacket $packet): void
    {
        if ($padding) {
            $paddingLen = ord($data[strlen($data) - 1]);
            if ($paddingLen <= 0 || $paddingLen > (strlen($data) - $pos)) {
                throw new RtpPacketException("RTP packet padding length is invalid");
            }
            $packet->paddingSize = $paddingLen;
            $packet->payload = substr($data, $pos, -$paddingLen);
        } else {
            $packet->payload = substr($data, $pos);
        }
    }

    /**
     * Encodes the RTP packet into a binary string.
     *
     * @param HeaderExtensionsMap|null $extensionsMap Optional extensions mapping.
     * @return string The binary RTP packet data.
     *
     * @throws RandomException
     */
    public function encode(?HeaderExtensionsMap $extensionsMap = null): string
    {
        $extensionsMap = $extensionsMap ?? new HeaderExtensionsMap();
        [$extensionProfile, $extensionValue] = $extensionsMap->set($this->extensions);

        $hasExtension = (int)!empty($extensionValue);

        $padding = $this->paddingSize > 0;
        $data = pack(
            "C2nN2",
            ($this->version << 6) | ((int) $padding << 5) | ($hasExtension << 4) | count($this->csrc),
            ($this->marker << 7) | $this->payloadType,
            $this->sequenceNumber,
            $this->timestamp,
            $this->ssrc
        );

        foreach ($this->csrc as $csrc) {
            $data .= pack("N", $csrc);
        }

        if ($hasExtension) {
            $data .= pack("n2", $extensionProfile, strlen($extensionValue) / 4);
            $data .= $extensionValue;
        }

        $data .= $this->payload;

        if ($padding) {
            $data .= str_repeat(chr(random_int(0, 255)), $this->paddingSize - 1);
            $data .= chr($this->paddingSize);
        }

        return $data;
    }

    /**
     * Gets the RTP version number.
     *
     * @return int The RTP version (always 2 for this implementation).
     */
    public function getVersion(): int
    {
        return $this->version;
    }

    /**
     * Gets the marker bit value.
     *
     * @return int The marker bit (1 or 0).
     */
    public function getMarker(): int
    {
        return $this->marker;
    }

    /**
     * Gets the payload type.
     *
     * @return int The payload type identifier.
     */
    public function getPayloadType(): int
    {
        return $this->payloadType;
    }

    /**
     * Gets the sequence number.
     *
     * @return int The 16-bit sequence number.
     */
    public function getSequenceNumber(): int
    {
        return $this->sequenceNumber;
    }

    /**
     * Gets the padding size.
     *
     * @return int The number of padding bytes.
     */
    public function getPaddingSize(): int
    {
        return $this->paddingSize;
    }

    /**
     * Gets the timestamp.
     *
     * @return int The 32-bit timestamp.
     */
    public function getTimestamp(): int
    {
        return $this->timestamp;
    }

    /**
     * Gets the synchronization source (SSRC).
     *
     * @return int The 32-bit SSRC identifier.
     */
    public function getSsrc(): int
    {
        return $this->ssrc;
    }

    /**
     * Gets the contributing sources (CSRC).
     *
     * @return int[] List of 32-bit CSRC identifiers.
     */
    public function getCsrc(): array
    {
        return $this->csrc;
    }

    /**
     * Gets the header extensions.
     *
     * @return HeaderExtensions The header extensions container.
     */
    public function getExtensions(): HeaderExtensions
    {
        return $this->extensions;
    }

    /**
     * Gets the payload data.
     *
     * @return string The raw payload data.
     */
    public function getPayload(): string
    {
        return $this->payload;
    }

    /**
     * Sets the marker bit.
     *
     * @param int $marker The marker bit value (1 or 0).
     */
    public function setMarker(int $marker): void
    {
        $this->marker = $marker;
    }

    /**
     * Sets the payload type.
     *
     * @param int $payloadType The payload type identifier.
     */
    public function setPayloadType(int $payloadType): void
    {
        $this->payloadType = $payloadType;
    }

    /**
     * Sets the sequence number.
     *
     * @param int $sequenceNumber The 16-bit sequence number.
     */
    public function setSequenceNumber(int $sequenceNumber): void
    {
        $this->sequenceNumber = $sequenceNumber;
    }

    /**
     * Sets the timestamp.
     *
     * @param int $timestamp The 32-bit timestamp.
     */
    public function setTimestamp(int $timestamp): void
    {
        $this->timestamp = $timestamp;
    }

    /**
     * Sets the synchronization source (SSRC).
     *
     * @param int $ssrc The 32-bit SSRC identifier.
     */
    public function setSsrc(int $ssrc): void
    {
        $this->ssrc = $ssrc;
    }

    /**
     * Sets the header extensions.
     *
     * @param HeaderExtensions $extensions The header extensions container.
     */
    public function setExtensions(HeaderExtensions $extensions): void
    {
        $this->extensions = $extensions;
    }

    /**
     * Sets the contributing sources (CSRC).
     *
     * @param int[] $csrc List of 32-bit CSRC identifiers.
     */
    public function setCsrc(array $csrc): void
    {
        $this->csrc = $csrc;
    }

    /**
     * Sets the payload data.
     *
     * @param string $payload The raw payload data.
     */
    public function setPayload(string $payload): void
    {
        $this->payload = $payload;
    }

    /**
     * Returns a string representation of the RTP packet.
     *
     * @return string Formatted string with key packet fields.
     */
    public function __toString(): string {
        return sprintf(
            "RtpPacket(seq=%d, ts=%d, marker=%d, payload=%d, %d bytes)",
            $this->sequenceNumber,
            $this->timestamp,
            $this->marker,
            $this->payloadType,
            strlen($this->payload)
        );
    }

    /**
     * Sets the decoded payload data.
     *
     * This is typically used to store the decoded media data after processing.
     *
     * @param string $decodedData The decoded payload data.
     */
    public function setDecodedData(string $decodedData): void
    {
        $this->decodedData = $decodedData;
    }

    /**
     * Gets the decoded payload data.
     *
     * @return string|null The decoded payload data or null if not set.
     */
    public function getDecodedData(): ?string
    {
        return $this->decodedData;
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTP\Receiver;

use Webrtc\RTCP\RtcpPacketInterface;
use Webrtc\RTP\RtpPacket;

interface RtpReceiverInterface
{
    public function stop(): void;
    public function handleRtcpPacket(RtcpPacketInterface $packet): void;
    public function handleRtpPacket(RtpPacket $packet, int $arrivalTimeMs): void;
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTP\Receiver;

use Amp\Pipeline\Queue;
use Revolt\EventLoop;
use Webrtc\AVCodec\Frame\FrameInterface;
use Webrtc\Codecs\DecoderInterface;
use Webrtc\Mixin\SerializableState;
use Webrtc\RTP\Jitter\JitterFrame;
use Webrtc\RTP\MediaStreamTrack\RemoteStreamTrack;

/**
 * Class DecoderQueue
 *
 * Manages a queue of frames for asynchronous decoding and playback.
 */
final class DecoderQueue
{
    /** @var Queue<JitterFrame> Queue for storing incoming jitter frames. */
    private Queue $queue;

    private bool $running = false;

    private ?RemoteStreamTrack $track = null;

    /**
     * DecoderQueue constructor.
     *
     * @param DecoderInterface $decoder Decoder used for every frame added to this queue.
     */
    public function __construct(private readonly DecoderInterface $decoder)
    {
        /** @var Queue<JitterFrame> */
        $this->queue = new Queue();
    }

    /**
     * Adds a frame to the processing queue.
     *
     * @param JitterFrame $frame The frame to enqueue for decoding.
     */
    public function addFrame(JitterFrame $frame): void
    {
        $this->queue->push($frame);
    }

    /**
     * Starts processing the frame queue and sends decoded frames to the track.
     *
     * @param RemoteStreamTrack $track The track to send decoded frames to.
     */
    public function start(RemoteStreamTrack $track): void
    {
        if ($this->running) {
            throw new \RuntimeException('DecoderQueue is already started.');
        }
        $this->track = $track;
        $this->running = true;
        EventLoop::queue($this->drain(...));
    }

    /**
     * Drain queued frames onto the track.
     *
     * Restarted only as the first-class callable `$this->drain(...)` handed to EventLoop::queue(),
     * including when unserialize re-queues it. That callable captures this method's private scope,
     * so the event loop can run it while it stays private.
     */
    private function drain(): void
    {
        $track = $this->track;
        if ($track === null) {
            $this->running = false;

            return;
        }
        // Park the decode loop on the queue iterator, not on $this. Iterating inside an instance
        // method keeps $this on the parked fiber's stack, pinning this queue (and the receiver that
        // owns it) in the event loop forever â€” an unset()+gc could never reclaim it. Holding only
        // the iterator and a weak reference lets the owner be collected; __destruct() then
        // completes the queue so this fiber unwinds.
        $weak = \WeakReference::create($this);
        $iterator = $this->queue->iterate();
        EventLoop::queue(static function () use ($weak, $iterator, $track): void {
            foreach ($iterator as $frame) {
                $self = $weak->get();
                if ($self === null) {
                    return;
                }
                $decodedFrame = $self->decoder->decode($frame);
                /** @var FrameInterface[] $decodedFrame */
                foreach ($decodedFrame as $decoded) {
                    $track->queueFrame($decoded);
                }
                unset($self);
            }
            $self = $weak->get();
            if ($self !== null) {
                $self->running = false;
            }
        });
    }

    public function __destruct()
    {
        if (!$this->queue->isComplete()) {
            $this->queue->complete();
        }
    }

    /**
     * Stops the processing of the frame queue and cancels the scheduled task.
     */
    public function stop(): void
    {
        $this->queue->complete();
    }

    /**
     * @return array<string, mixed>
     */
    public function __serialize(): array
    {
        return SerializableState::export($this, [
            'queue' => null,
        ]);
    }

    /**
     * @param array<string, mixed> $data
     */
    public function __unserialize(array $data): void
    {
        foreach (array_keys($data) as $key) {
            if (str_ends_with($key, "\0queue")) {
                unset($data[$key]);
            }
        }
        SerializableState::import($this, $data);
        /** @var Queue<JitterFrame> $queue */
        $queue = new Queue();
        $this->queue = $queue;
        if ($this->running && $this->track !== null) {
            $this->running = false;
            $this->start($this->track);
        }
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTP\Receiver;

use Webrtc\RTP\RtpPacket;

/**
 * StreamStatistics collects and calculates metrics from incoming RTP packets,
 * including packet loss, jitter, and sequence number tracking.
 *
 * This class supports analysis of RTP stream quality over time by keeping track
 * of received packets, estimating jitter based on RFC 3550, and handling 16-bit
 * sequence number wrap-around. It's useful for generating RTCP reports or debugging.
 */
final class StreamStatistics
{
    private ?int $baseSeq = null;
    private ?int $maxSeq = null;
    private int $cycles = 0;
    private int $packetsReceived = 0;

    // Jitter variables
    private int $clockRate;
    private int $jitterQ4 = 0;
    private ?int $lastArrival = null;
    private ?int $lastTimestamp = null;

    // Fraction lost tracking
    private int $expectedPrior = 0;
    private int $receivedPrior = 0;

    /**
     * StreamStatistics constructor.
     *
     * @param int $clockRate The clock rate of the RTP stream.
     */
    public function __construct(int $clockRate)
    {
        $this->clockRate = $clockRate;
    }

    /**
     * Adds an RTP packet to the statistics tracker.
     *
     * @param RtpPacket $packet The received RTP packet.
     */
    public function add(RtpPacket $packet): void
    {
        $inOrder = $this->maxSeq === null || $this->uint16Gt($packet->getSequenceNumber(), $this->maxSeq);
        $this->packetsReceived++;

        if ($this->baseSeq === null) {
            $this->baseSeq = $packet->getSequenceNumber();
        }

        if ($inOrder) {
            $arrival = intval(microtime(true) * (float) $this->clockRate);

            if ($this->maxSeq !== null && $packet->getSequenceNumber() < $this->maxSeq) {
                $this->cycles += (1 << 16);
            }
            $this->maxSeq = $packet->getSequenceNumber();

            // Jitter calculation
            if (
                $this->lastArrival !== null &&
                $this->lastTimestamp !== null &&
                $packet->getTimestamp() !== $this->lastTimestamp &&
                $this->packetsReceived > 1
            ) {
                $diff = abs(
                    ($arrival - $this->lastArrival) - ($packet->getTimestamp() - $this->lastTimestamp)
                );
                $this->jitterQ4 += $diff - (($this->jitterQ4 + 8) >> 4);
            }

            $this->lastArrival = $arrival;
            $this->lastTimestamp = $packet->getTimestamp();
        }
    }

    /**
     * Calculates the fraction of lost RTP packets.
     *
     * @return int The fraction lost as a percentage (0-255).
     */
    public function getFractionLost(): int
    {
        $expectedInterval = $this->getPacketsExpected() - $this->expectedPrior;
        $this->expectedPrior = $this->getPacketsExpected();
        $receivedInterval = $this->packetsReceived - $this->receivedPrior;
        $this->receivedPrior = $this->packetsReceived;
        $lostInterval = $expectedInterval - $receivedInterval;

        if ($expectedInterval === 0 || $lostInterval <= 0) {
            return 0;
        }
        return intval(($lostInterval << 8) / $expectedInterval);
    }

    /**
     * Gets the current jitter value.
     *
     * @return int The estimated jitter value.
     */
    public function getJitter(): int
    {
        return $this->jitterQ4 >> 4;
    }

    /**
     * Gets the total number of packets expected based on sequence numbers.
     *
     * @return int The number of expected packets.
     */
    public function getPacketsExpected(): int
    {
        if ($this->baseSeq === null || $this->maxSeq === null) {
            return 0;
        }
        return $this->cycles + $this->maxSeq - $this->baseSeq + 1;
    }

    /**
     * Gets the total number of lost packets.
     *
     * @return int The number of lost packets.
     */
    public function getPacketsLost(): int
    {
        return $this->clampPacketsLost($this->getPacketsExpected() - $this->packetsReceived);
    }

    /**
     * Checks if a 16-bit sequence number is greater than another, considering wrap-around.
     *
     * @param int $a First sequence number.
     * @param int $b Second sequence number.
     * @return bool True if $a > $b, considering 16-bit overflow.
     */
    private function uint16Gt(int $a, int $b): bool
    {
        $halfMod = 0x8000;
        return (($a < $b) && (($b - $a) > $halfMod)) || (($a > $b) && (($a - $b) < $halfMod));
    }

    /**
     * Ensures the packet loss count does not go negative.
     *
     * @param int $packetsLost The computed number of lost packets.
     * @return int Clamped value ensuring non-negative loss count.
     */
    private function clampPacketsLost(int $packetsLost): int
    {
        return max(0, $packetsLost);
    }

    /**
     * @return int
     */
    public function getPacketsReceived(): int
    {
        return $this->packetsReceived;
    }

    /**
     * @return int|null
     */
    public function getMaxSeq(): ?int
    {
        return $this->maxSeq;
    }

    /**
     * @return int
     */
    public function getJitterQ4(): int
    {
        return $this->jitterQ4;
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTP\Receiver;

/**
 * TimestampMapper normalizes RTP timestamps to a continuous timeline.
 *
 * This class handles 32-bit RTP timestamp wraparounds and produces a stable,
 * monotonic timestamp sequence starting from the first observed timestamp.
 * Useful for synchronizing media streams or aligning packet timing data.
 */
final class TimestampMapper
{
    /** The last timestamp received. */
    private ?int $last = null;
    /** The first timestamp received, on the continuous timeline. */
    private ?int $origin = null;
    /** The last timestamp received, on the continuous timeline. */
    private ?int $extended = null;

    /**
     * Maps an RTP timestamp to a continuous timeline.
     *
     * Timestamps are compared with serial number arithmetic (RFC 3550): a timestamp is placed at the
     * point of the timeline closest to the last one, modulo 2^32. A timestamp slightly before the last
     * one (a reordered packet, or a sender that restarted from a saved state) is not mistaken for a
     * wraparound, which would put it and every following one 2^32 ticks later.
     *
     * @param int $timestamp The RTP timestamp.
     * @return int The mapped timestamp relative to the first received timestamp.
     */
    public function map(int $timestamp): int
    {
        if ($this->origin === null || $this->last === null) {
            // First timestamp received, set as origin
            $this->origin = $this->extended = $this->last = $timestamp;
            return 0;
        }

        $delta = ($timestamp - $this->last) & 0xFFFFFFFF;
        if ($delta >= 0x80000000) {
            $delta -= 0x100000000;
        }
        // The last timestamp is on the continuous timeline already, when restored from a state without it.
        $this->extended = ($this->extended ?? $this->last) + $delta;
        $this->last = $timestamp;

        return $this->extended - $this->origin;
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTP\Receiver\Rate;

/**
 * Class InterArrivalDelta
 *
 * Represents the computed differences (deltas) between two RTP timestamp groups.
 * These deltas are used in delay-based bitrate estimation to analyze trends in
 * network delay and packet delivery over time.
 *
 * Properties:
 * - $timestamp: The difference in RTP timestamps between two groups.
 * - $arrivalTime: The difference in arrival times (milliseconds) between two groups.
 * - $size: The difference in total packet sizes (bytes) between two groups.
 */
final class InterArrivalDelta {
    public int $timestamp;
    public int $arrivalTime;
    public int $size;

    public function __construct(int $timestamp, int $arrivalTime, int $size) {
        $this->timestamp = $timestamp;
        $this->arrivalTime = $arrivalTime;
        $this->size = $size;
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTP\Receiver\Rate;

/**
 * Rate bucket for storing count and value
 */
final class RateBucket {
    public int $count;
    public int $value;

    /**
     * @param int $count
     * @param int $value
     */
    public function __construct(int $count = 0, int $value = 0) {
        $this->count = $count;
        $this->value = $value;
    }

    /**
     * Compare RateBucket with itself.
     *
     * @param RateBucket $other
     * @return bool
     */
    public function compare(RateBucket $other): bool {
        return $this->count === $other->count && $this->value === $other->value;
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTP\Receiver\Rate;

use Webrtc\RTP\Enum\BandwidthUsage;

/**
 * Estimates bandwidth usage based on delay trends.
 *
 * This class implements a delay-based bandwidth overuse estimator. It tracks
 * offset and slope values to detect network overuse or underuse patterns, based
 * on RTP packet timing and size data. The estimator uses a Kalman filter to adapt
 * over time and adjust for network noise.
 *
 * Adapted from the WebRTC project's implementation.
 */
final class OveruseEstimator {

    /** Minimum number of deltas to keep */
    const MIN_NUM_DELTAS = 1000;

    /** Minimum number of frame intervals to store for frame period estimation */
    const MIN_FRAME_PERIOD_HISTORY_LENGTH = 60;

    /**
     * Error covariance matrix for Kalman filter.
     *
     * @var array{0: array{0: float, 1: float}, 1: array{0: float, 1: float}}
     */
    private array $E = [[100.0, 0.0], [0.0, 0.1]];

    /** Number of inter-arrival deltas used */
    private int $numOfDeltas = 0;

    /** Current Kalman filter offset estimate */
    private float $offset = 0.0;

    /** Previous offset value used for hypothesis testing */
    private float $previousOffset = 0.0;

    /** Kalman filter slope estimate */
    private float $slope = 1.0 / 64.0;

    /**
     * History of timestamp deltas for minimum frame period tracking.
     *
     * @var float[]
     */
    private array $tsDeltaHist = [];

    /** Estimated average noise */
    private float $avgNoise = 0.0;

    /** Estimated variance of noise */
    private float $varNoise = 50.0;

    /**
     * Process noise parameters for Kalman filter.
     *
     * @var array{0: float, 1: float}
     */
    private array $processNoise = [1e-13, 1e-3];

    /**
     * Gets the number of inter-arrival deltas used in the estimation.
     *
     * @return int Number of deltas.
     */
    public function getNumOfDeltas(): int {
        return $this->numOfDeltas;
    }

    /**
     * Gets the current offset estimate from the Kalman filter.
     *
     * @return float Estimated offset.
     */
    public function getOffset(): float {
        return $this->offset;
    }

    /**
     * Updates the Kalman filter and noise model with a new observation.
     *
     * @param int $timeDeltaMs Time difference between arrivals (in ms).
     * @param float $timestampDeltaMs RTP timestamp difference converted to ms.
     * @param int $sizeDelta Size difference of packets (in bytes).
     * @param BandwidthUsage $currentHypothesis Current overuse/underuse state.
     */
    public function update(int $timeDeltaMs, float $timestampDeltaMs, int $sizeDelta, BandwidthUsage $currentHypothesis): void {
        $minFramePeriod = $this->updateMinFramePeriod($timestampDeltaMs);
        $tTsDelta = (float) $timeDeltaMs - $timestampDeltaMs;
        $this->numOfDeltas = min($this->numOfDeltas + 1, self::MIN_NUM_DELTAS);

        $this->updateKalmanFilter($currentHypothesis);
        $residual = $this->calculateResidual($tTsDelta, $sizeDelta);
        $this->updateNoiseEstimateIfNeeded($residual, $minFramePeriod, $currentHypothesis);
        $this->updateState($residual, $sizeDelta);
    }

    /**
     * Updates the Kalman filter prediction error covariance matrix.
     *
     * Adjusts the process noise based on the hypothesis (normal, overuse, underuse).
     *
     * @param BandwidthUsage $currentHypothesis The current bandwidth usage hypothesis.
     */
    private function updateKalmanFilter(BandwidthUsage $currentHypothesis): void {
        $this->E[0][0] += $this->processNoise[0];
        $this->E[1][1] += $this->processNoise[1];

        if (($currentHypothesis === BandwidthUsage::OVERUSING && $this->offset < $this->previousOffset) ||
            ($currentHypothesis === BandwidthUsage::UNDERUSING && $this->offset > $this->previousOffset)) {
            $this->E[1][1] += 10.0 * $this->processNoise[1];
        }
    }

    /**
     * Computes the residual (error) between expected and actual time deltas.
     *
     * @param float $tTsDelta The time delta minus timestamp delta.
     * @param int $sizeDelta Packet size delta in bytes.
     * @return float The residual value.
     */
    private function calculateResidual(float $tTsDelta, int $sizeDelta): float {
        return $tTsDelta - $this->slope * (float) $sizeDelta - $this->offset;
    }

    /**
     * Conditionally updates the noise estimate if in NORMAL state.
     *
     * @param float $residual The computed residual.
     * @param float $minFramePeriod Minimum frame period from history.
     * @param BandwidthUsage $currentHypothesis Current bandwidth usage hypothesis.
     */
    private function updateNoiseEstimateIfNeeded(float $residual, float $minFramePeriod, BandwidthUsage $currentHypothesis): void {
        if ($currentHypothesis === BandwidthUsage::NORMAL) {
            $maxResidual = 3.0 * sqrt($this->varNoise);
            $adjustedResidual = abs($residual) < $maxResidual ? $residual : ($residual < 0 ? -$maxResidual : $maxResidual);
            $this->updateNoiseEstimate($adjustedResidual, $minFramePeriod);
        }
    }

    /**
     * Updates the Kalman state with the new residual and size delta.
     *
     * @param float $residual The residual from the prediction.
     * @param int $sizeDelta Packet size delta in bytes.
     */
    private function updateState(float $residual, int $sizeDelta): void {
        $h = [(float) $sizeDelta, 1.0];
        $Eh = [
            $this->E[0][0] * $h[0] + $this->E[0][1] * $h[1],
            $this->E[1][0] * $h[0] + $this->E[1][1] * $h[1]
        ];

        $denom = $this->varNoise + $h[0] * $Eh[0] + $h[1] * $Eh[1];
        $K = [$Eh[0] / $denom, $Eh[1] / $denom];

        $IKh = [
            [1.0 - $K[0] * $h[0], -$K[0] * $h[1]],
            [-$K[1] * $h[0], 1.0 - $K[1] * $h[1]]
        ];

        $e00 = $this->E[0][0];
        $e01 = $this->E[0][1];

        $this->E[0][0] = $e00 * $IKh[0][0] + $this->E[1][0] * $IKh[0][1];
        $this->E[0][1] = $e01 * $IKh[0][0] + $this->E[1][1] * $IKh[0][1];
        $this->E[1][0] = $e00 * $IKh[1][0] + $this->E[1][0] * $IKh[1][1];
        $this->E[1][1] = $e01 * $IKh[1][0] + $this->E[1][1] * $IKh[1][1];

        $this->previousOffset = $this->offset;
        $this->slope += $K[0] * $residual;
        $this->offset += $K[1] * $residual;
    }

    /**
     * Tracks the minimum observed frame period from a sliding window history.
     *
     * @param float $tsDelta Current timestamp delta.
     * @return float Minimum frame period observed.
     */
    private function updateMinFramePeriod(float $tsDelta): float {
        $minFramePeriod = $tsDelta;
        if (count($this->tsDeltaHist) >= self::MIN_FRAME_PERIOD_HISTORY_LENGTH) {
            array_shift($this->tsDeltaHist);
        }

        foreach ($this->tsDeltaHist as $oldTsDelta) {
            $minFramePeriod = min($oldTsDelta, $minFramePeriod);
        }

        $this->tsDeltaHist[] = $tsDelta;
        return $minFramePeriod;
    }

    /**
     * Updates the noise estimate based on the current residual and frame period.
     *
     * @param float $residual The adjusted residual.
     * @param float $tsDelta Frame period in milliseconds.
     */
    private function updateNoiseEstimate(float $residual, float $tsDelta): void {
        $alpha = $this->numOfDeltas > 300 ? 0.002 : 0.01;
        $beta = pow(1.0 - $alpha, $tsDelta * 30.0 / 1000.0);
        $this->avgNoise = $beta * $this->avgNoise + (1.0 - $beta) * $residual;
        $this->varNoise = $beta * $this->varNoise + (1.0 - $beta) * pow($this->avgNoise - $residual, 2);

        if ($this->varNoise < 1) {
            $this->varNoise = 1;
        }
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTP\Receiver\Rate;

use Webrtc\RTP\Enum\BandwidthUsage;

/**
 * Bandwidth overuse detector.
 * Adapted from the webrtc.org codebase.
 */
final class OveruseDetector {
    const MAX_ADAPT_OFFSET_MS = 15;
    const MIN_NUM_DELTAS = 60;
    private BandwidthUsage $hypothesis = BandwidthUsage::NORMAL;
    private ?int $lastUpdateMs = null;
    private float $kUp = 0.0087;
    private float $kDown = 0.039;
    private int $overuseCounter = 0;
    private ?float $overuseTime = null;
    private float $overuseTimeThreshold = 10.0;
    private float $previousOffset = 0.0;
    private float $threshold = 12.5;

    /**
     * Detects bandwidth overuse based on network conditions.
     *
     * @param float $offset The current bandwidth offset.
     * @param float $timestampDeltaMs The time difference in milliseconds.
     * @param int $numOfDeltas The number of deltas available.
     * @param int $nowMs The current timestamp in milliseconds.
     * @return BandwidthUsage The detected bandwidth usage state.
     */
    public function detect(float $offset, float $timestampDeltaMs, int $numOfDeltas, int $nowMs): BandwidthUsage {
        if ($numOfDeltas < 2) {
            return BandwidthUsage::NORMAL;
        }

        $T = (float) min($numOfDeltas, self::MIN_NUM_DELTAS) * $offset;

        if ($T > $this->threshold) {
            if ($this->overuseTime === null) {
                $this->overuseTime = $timestampDeltaMs / 2.0;
            } else {
                $this->overuseTime += $timestampDeltaMs;
            }

            $this->overuseCounter++;

            if ($this->overuseTime > $this->overuseTimeThreshold && $this->overuseCounter > 1 && $offset >= $this->previousOffset) {
                $this->overuseCounter = 0;
                $this->overuseTime = 0;
                $this->hypothesis = BandwidthUsage::OVERUSING;
            }
        } elseif ($T < -$this->threshold) {
            $this->overuseCounter = 0;
            $this->overuseTime = null;
            $this->hypothesis = BandwidthUsage::UNDERUSING;
        } else {
            $this->overuseCounter = 0;
            $this->overuseTime = null;
            $this->hypothesis = BandwidthUsage::NORMAL;
        }

        $this->previousOffset = $offset;
        $this->updateThreshold($T, $nowMs);

        return $this->hypothesis;
    }

    /**
     * Returns the current state of bandwidth usage.
     *
     * @return BandwidthUsage The current bandwidth usage state.
     */
    public function state(): BandwidthUsage {
        return $this->hypothesis;
    }

    /**
     * Updates the overuse detection threshold based on network conditions.
     *
     * @param float $modifiedOffset The adjusted offset value.
     * @param int $nowMs The current timestamp in milliseconds.
     */
    private function updateThreshold(float $modifiedOffset, int $nowMs): void {
        if ($this->lastUpdateMs === null) {
            $this->lastUpdateMs = $nowMs;
        }

        if (abs($modifiedOffset) > $this->threshold + (float) self::MAX_ADAPT_OFFSET_MS) {
            $this->lastUpdateMs = $nowMs;
            return;
        }

        $k = abs($modifiedOffset) < $this->threshold ? $this->kDown : $this->kUp;
        $timeDeltaMs = min($nowMs - $this->lastUpdateMs, 100);
        $this->threshold += $k * (abs($modifiedOffset) - $this->threshold) * (float) $timeDeltaMs;
        $this->threshold = max(6, min($this->threshold, 600));
        $this->lastUpdateMs = $nowMs;
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTP\Receiver\Rate;

use Webrtc\RTP\Enum\BandwidthUsage;

/**
 * A class for estimating the remote bitrate, controlling the rate based on incoming data,
 * inter-arrival times, and overuse detection.
 */
final class RemoteBitrateEstimator
{

    /** abs-send-time estimator */
    private const INTER_ARRIVAL_SHIFT = 26;
    private const TIMESTAMP_GROUP_LENGTH_MS = 5;
    private float $timestampToMs;
    private RateCounter $incomingBitrate;
    private bool $incomingBitrateInitialized = true;
    private InterArrival $interArrival;
    private OveruseEstimator $estimator;
    private OveruseDetector $detector;
    private AimdRateControl $rateControl;
    private ?int $lastUpdateMs = null;
    private array $ssrcs = [];

    /**
     * Constructor for the RemoteBitrateEstimator class.
     * Initializes the components for bitrate estimation, overuse detection, and rate control.
     */
    public function __construct()
    {
        $this->timestampToMs = 1000.0 / (float) (1 << self::INTER_ARRIVAL_SHIFT);
        $this->incomingBitrate = new RateCounter(1000, 8000);
        $this->interArrival = new InterArrival(intval((self::TIMESTAMP_GROUP_LENGTH_MS << self::INTER_ARRIVAL_SHIFT) / 1000), $this->timestampToMs);
        $this->estimator = new OveruseEstimator();
        $this->detector = new OveruseDetector();
        $this->rateControl = new AimdRateControl();
    }

    /**
     * Adds a new packet arrival in the bitrate estimator and updates the estimate if necessary.
     *
     * @param int $arrivalTimeMs The timestamp of the packet arrival in milliseconds.
     * @param int $absSendTime The absolute send time of the packet.
     * @param int $payloadSize The size of the packet payload.
     * @param int $ssrc The SSRC of the stream.
     *
     * @return array|null The target bitrate and a list of SSRCs if the estimate is updated, or null if not.
     */
    public function add(int $arrivalTimeMs, int $absSendTime, int $payloadSize, int $ssrc): ?array
    {
        $this->trackSsrc($arrivalTimeMs, $ssrc);
        $this->updateIncomingBitrate($arrivalTimeMs, $payloadSize);

        $deltas = $this->computeInterArrivalDeltas($arrivalTimeMs, $absSendTime, $payloadSize);
        $this->processInterArrivalDeltas($deltas, $arrivalTimeMs);

        $updateEstimate = $this->shouldUpdateEstimate($arrivalTimeMs);

        if ($updateEstimate) {
            return $this->updateTargetBitrate($arrivalTimeMs);
        }

        return null;
    }

    /**
     * Tracks the SSRC and its associated arrival time.
     *
     * @param int $arrivalTimeMs The timestamp of the packet arrival in milliseconds.
     * @param int $ssrc The SSRC of the stream.
     */
    private function trackSsrc(int $arrivalTimeMs, int $ssrc): void
    {
        $this->ssrcs[$ssrc] = $arrivalTimeMs;
    }

    /**
     * Updates the incoming bitrate based on the packet arrival and payload size.
     *
     * @param int $arrivalTimeMs The timestamp of the packet arrival in milliseconds.
     * @param int $payloadSize The size of the packet payload.
     */
    private function updateIncomingBitrate(int $arrivalTimeMs, int $payloadSize): void
    {
        if ($this->incomingBitrate->rate($arrivalTimeMs) !== null) {
            $this->incomingBitrateInitialized = true;
        } elseif ($this->incomingBitrateInitialized) {
            $this->incomingBitrate->reset();
            $this->incomingBitrateInitialized = false;
        }
        $this->incomingBitrate->add($payloadSize, $arrivalTimeMs);
    }

    /**
     * Computes the inter-arrival deltas based on the timestamp, arrival time, and payload size.
     *
     * @param int $arrivalTimeMs The timestamp of the packet arrival in milliseconds.
     * @param int $absSendTime The absolute send time of the packet.
     * @param int $payloadSize The size of the packet payload.
     */
    private function computeInterArrivalDeltas(int $arrivalTimeMs, int $absSendTime, int $payloadSize): InterArrivalDelta|null
    {
        $timestamp = $absSendTime << 8;
        return $this->interArrival->computeDeltas($timestamp, $arrivalTimeMs, $payloadSize);
    }

    /**
     * Processes the inter-arrival deltas and updates the estimator and detector.
     *
     * @param InterArrivalDelta|null $deltas The inter-arrival deltas.
     * @param int $arrivalTimeMs The timestamp of the packet arrival in milliseconds.
     */
    private function processInterArrivalDeltas(?InterArrivalDelta $deltas, int $arrivalTimeMs): void
    {
        if ($deltas !== null) {
            $timestampDeltaMs = (float) $deltas->timestamp * $this->timestampToMs;
            $this->estimator->update(
                $deltas->arrivalTime,
                $timestampDeltaMs,
                $deltas->size,
                $this->detector->state()
            );
            $this->detector->detect(
                $this->estimator->getOffset(),
                $timestampDeltaMs,
                $this->estimator->getNumOfDeltas(),
                $arrivalTimeMs
            );
        }
    }

    /**
     * Determines if the bitrate estimate should be updated based on the current arrival time and conditions.
     *
     * @param int $arrivalTimeMs The timestamp of the packet arrival in milliseconds.
     *
     * @return bool True if the estimate should be updated, false otherwise.
     */
    private function shouldUpdateEstimate(int $arrivalTimeMs): bool
    {
        if (
            $this->lastUpdateMs === null ||
            ($arrivalTimeMs - $this->lastUpdateMs) > $this->rateControl->feedbackInterval()
        ) {
            return true;
        } elseif ($this->detector->state() === BandwidthUsage::OVERUSING) {
            return true;
        }
        return false;
    }

    /**
     * Updates the target bitrate based on the state of the detector and the incoming bitrate.
     *
     * @param int $arrivalTimeMs The timestamp of the packet arrival in milliseconds.
     *
     * @return array|null The target bitrate and a list of SSRCs if updated, or null otherwise.
     */
    private function updateTargetBitrate(int $arrivalTimeMs): ?array
    {
        $targetBitrate = $this->rateControl->update(
            $this->detector->state(),
            $this->incomingBitrate->rate($arrivalTimeMs),
            $arrivalTimeMs
        );
        if ($targetBitrate !== null) {
            $this->lastUpdateMs = $arrivalTimeMs;
            return [$targetBitrate, array_keys($this->ssrcs)];
        }
        return null;
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTP\Receiver\Rate;

/**
 * Rate counter, which stores the amount received in 1 ms buckets.
 */
final class RateCounter
{
    private int $originIndex = 0;
    private ?int $originMs = null;
    private int $scale;
    private int $windowSize;
    /** @var RateBucket[] */
    private array $buckets;
    private RateBucket $total;

    /**
     * Constructor to initialize the RateCounter with a window size and an optional scale factor.
     *
     * @param int $windowSize The size of the window in milliseconds (number of buckets).
     * @param int $scale The scale factor for the rate calculation (default is 8000).
     */
    public function __construct(int $windowSize, int $scale = 8000)
    {
        $this->scale = $scale;
        $this->windowSize = $windowSize;
        $this->reset();
    }

    /**
     * Adds a value to the rate counter, updating the respective bucket and total count.
     * If the window size is exceeded, old data is erased.
     *
     * @param int $value The value to be added to the rate counter.
     * @param int $nowMs The current time in milliseconds.
     */
    public function add(int $value, int $nowMs): void
    {
        if ($this->originMs === null) {
            $this->originMs = $nowMs;
        } else {
            $this->eraseOld($nowMs);
        }

        // Ensure positive index calculation
        $index = ($this->originIndex + $nowMs - $this->originMs) % $this->windowSize;
        if ($index < 0) {
            $index += $this->windowSize;
        }

        // Ensure bucket exists
        if (!isset($this->buckets[$index])) {
            $this->buckets[$index] = new RateBucket();
        }

        $this->buckets[$index]->count++;
        $this->buckets[$index]->value += $value;
        $this->total->count++;
        $this->total->value += $value;
    }

    /**
     * Calculates the current rate based on the time window and total values.
     * The rate is scaled and rounded.
     *
     * @param int $nowMs The current time in milliseconds.
     * @return int|null The calculated rate or null if no data is available.
     */
    public function rate(int $nowMs): ?int
    {
        if ($this->originMs !== null) {
            $this->eraseOld($nowMs);
            $activeWindowSize = $nowMs - $this->originMs + 1;

            if ($this->total->count > 0 && $activeWindowSize > 1) {
                return (int) round(($this->scale * $this->total->value) / $activeWindowSize);
            }
        }
        return null;
    }

    /**
     * Resets the rate counter, clearing all buckets and resetting the total count.
     */
    public function reset(): void
    {
        $this->buckets = [];
        for ($i = 0; $i < $this->windowSize; $i++) {
            $this->buckets[$i] = new RateBucket();  // Correct way to initialize separate instances
        }
        $this->originIndex = 0;
        $this->originMs = null;
        $this->total = new RateBucket();
    }

    /**
     * Erases old data from the rate counter, removing values that fall outside the window size.
     * This adjusts the total count and shifts the window of data.
     *
     * @param int $nowMs The current time in milliseconds.
     */
    private function eraseOld(int $nowMs): void
    {
        if ($this->originMs === null) {
            return;
        }
        $newOriginMs = $nowMs - $this->windowSize + 1;
        while ($this->originMs < $newOriginMs) {
            $bucket = $this->buckets[$this->originIndex];
            $this->total->count -= $bucket->count;
            $this->total->value -= $bucket->value;
            $bucket->count = 0;
            $bucket->value = 0;

            $this->originIndex = ($this->originIndex + 1) % $this->windowSize;
            $this->originMs++;
        }
    }

    /**
     * @return array
     */
    public function getBuckets(): array
    {
        return $this->buckets;
    }

    /**
     * @return int|null
     */
    public function getOriginMs(): ?int
    {
        return $this->originMs;
    }

    /**
     * @return int
     */
    public function getOriginIndex(): int
    {
        return $this->originIndex;
    }

    /**
     * @return RateBucket
     */
    public function getTotal(): RateBucket
    {
        return $this->total;
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTP\Receiver\Rate;

/**
 * Class InterArrival
 *
 * Tracks and calculates inter-arrival deltas between RTP timestamp groups for bandwidth estimation.
 * This class groups RTP packets based on their timestamps and arrival times, detecting bursts and
 * computing deltas only when appropriate new timestamp groups are formed.
 *
 * Key responsibilities:
 * - Form timestamp groups for incoming packets.
 * - Identify burst packets that belong to the same group.
 * - Compute deltas in timestamp, arrival time, and packet size between successive groups.
 * - Handle 32-bit unsigned integer wrap-around safely.
 *
 * This is a core part of WebRTC bandwidth estimation used in delay-based bitrate control.
 */

final class InterArrival
{
    private const BURST_DELTA_THRESHOLD_MS = 5;
    private int $groupLength;
    private float $timestampToMs;
    private ?TimestampGroup $currentGroup = null;
    private ?TimestampGroup $previousGroup = null;

    public function __construct(int $groupLength, float $timestampToMs)
    {
        $this->groupLength = $groupLength;
        $this->timestampToMs = $timestampToMs;
    }

    /**
     * Computes the inter-arrival deltas between timestamp groups.
     * @param int $timestamp The current packet timestamp.
     * @param int $arrivalTime The arrival time of the packet.
     * @param int $packetSize The size of the packet.
     * @return InterArrivalDelta|null The computed deltas or null if not applicable.
     */
    public function computeDeltas(int $timestamp, int $arrivalTime, int $packetSize): ?InterArrivalDelta
    {
        $deltas = null;
        if ($this->currentGroup === null) {
            $this->currentGroup = new TimestampGroup($timestamp);
        } elseif ($this->packetOutOfOrder($timestamp)) {
            return null;
        } elseif ($this->newTimestampGroup($timestamp, $arrivalTime)) {
            $currentGroup = $this->currentGroup;
            $previousGroup = $this->previousGroup;
            if ($previousGroup !== null) {
                $deltas = new InterArrivalDelta(
                    $this->uint32Add((int) $currentGroup->lastTimestamp, -(int) $previousGroup->lastTimestamp),
                    (int) $currentGroup->arrivalTime - (int) $previousGroup->arrivalTime,
                    $currentGroup->size - $previousGroup->size
                );
            }

            $this->previousGroup = $this->currentGroup;
            $this->currentGroup = new TimestampGroup($timestamp);
        } elseif ($this->uint32Gt($timestamp, (int) $this->currentGroup->lastTimestamp)) {
            $this->currentGroup->lastTimestamp = $timestamp;
        }

        $this->currentGroup->size += $packetSize;
        $this->currentGroup->arrivalTime = $arrivalTime;

        return $deltas;
    }

    /**
     * Determines if a packet belongs to a burst-based-on-timestamp and arrival time deltas.
     * @param int $timestamp The timestamp of the packet.
     * @param int $arrivalTime The arrival time of the packet.
     * @return bool True if the packet belongs to a burst, false otherwise.
     */
    private function belongsToBurst(int $timestamp, int $arrivalTime): bool
    {
        $currentGroup = $this->currentGroup;
        if ($currentGroup === null) {
            return false;
        }
        $timestampDelta = $this->uint32Add($timestamp, -(int) $currentGroup->lastTimestamp);
        $timestampDeltaMs = round($this->timestampToMs * (float) $timestampDelta);
        $arrivalTimeDelta = $arrivalTime - (int) $currentGroup->arrivalTime;
        return $timestampDeltaMs == 0 ||
            (((float) $arrivalTimeDelta - $timestampDeltaMs) < 0 && $arrivalTimeDelta <= self::BURST_DELTA_THRESHOLD_MS);
    }

    /**
     * Determines if a new timestamp group should be created.
     * @param int $timestamp The current packet timestamp.
     * @param int $arrivalTime The arrival time of the packet.
     * @return bool True if a new timestamp group should be created, false otherwise.
     */
    private function newTimestampGroup(int $timestamp, int $arrivalTime): bool
    {
        if ($this->belongsToBurst($timestamp, $arrivalTime)) {
            return false;
        }
        $currentGroup = $this->currentGroup;
        if ($currentGroup === null) {
            return false;
        }
        $timestampDelta = $this->uint32Add($timestamp, -(int) $currentGroup->firstTimestamp);
        return $timestampDelta > $this->groupLength;
    }

    /**
     * Checks if a packet is out of order.
     * @param int $timestamp The timestamp of the packet.
     * @return bool True if the packet is out of order, false otherwise.
     */
    private function packetOutOfOrder(int $timestamp): bool
    {
        $currentGroup = $this->currentGroup;
        if ($currentGroup === null) {
            return false;
        }
        $timestampDelta = $this->uint32Add($timestamp, -(int) $currentGroup->firstTimestamp);
        return $timestampDelta >= 0x80000000;
    }

    /**
     * Performs an unsigned 32-bit addition.
     * @param int $a First operand.
     * @param int $b Second operand.
     * @return int The result of the addition.
     */
    private function uint32Add(int $a, int $b): int
    {
        return ($a + $b) & 0xFFFFFFFF;
    }

    /**
     * Compares two unsigned 32-bit integers to check if one is greater than the other.
     * @param int $a First operand.
     * @param int $b Second operand.
     * @return bool True if $a is greater than $b, false otherwise.
     */
    function uint32Gt(int $a, int $b): bool
    {
        $halfMod = 0x80000000;
        return (($a < $b) && (($b - $a) > $halfMod)) || (($a > $b) && (($a - $b) < $halfMod));
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTP\Receiver\Rate;

use Webrtc\Mixin\DataClass;

#[DataClass]
final class TimestampGroup {
    public ?int $arrivalTime;
    public ?int $firstTimestamp;
    public ?int $lastTimestamp;
    public int $size;

    /**
     * @param int|null $timestamp
     */
    public function __construct(?int $timestamp = null) {
        $this->arrivalTime = null;
        $this->firstTimestamp = $timestamp;
        $this->lastTimestamp = $timestamp;
        $this->size = 0;
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTP\Receiver\Rate;

use Webrtc\RTP\Enum\BandwidthUsage;
use Webrtc\RTP\Enum\RateControlState;

/**
 * Class AimdRateControl
 *
 * Implements Additive-Increase/Multiplicative-Decrease (AIMD) bitrate control for RTP streams.
 * This algorithm dynamically adjusts sending bitrate based on estimated bandwidth usage to
 * optimize media delivery quality while avoiding congestion.
 *
 * The controller starts in a HOLD state and adapts between INCREASE and DECREASE states
 * based on feedback from a bandwidth estimator. It prevents rapid changes and adapts to
 * network conditions using statistical filtering and conservative rate bounding.
 *
 * Properties include:
 * - Initialization of bitrate estimates.
 * - State transitions depending on bandwidth usage (underuse, normal, overuse).
 * - Multiplicative increase and additive increase options based on proximity to maximum throughput.
 * - Exponential averaging of estimated maximum throughput and its variance.
 */
final class AimdRateControl
{
    private ?float $avgMaxBitrateKbps = null;
    private float $varMaxBitrateKbps = 0.4;
    private int $currentBitrate = 30000000;
    private bool $currentBitrateInitialized = false;
    private ?int $firstEstimatedThroughputTime = null;
    private ?int $lastChangeMs = null;
    private bool $nearMax = false;
    private int $latestEstimatedThroughput = 30000000;
    private int $rtt = 200;
    private RateControlState $state = RateControlState::HOLD;

    /**
     * Returns the feedback interval in milliseconds.
     *
     * @return int Milliseconds between bitrate feedback updates.
     */
    public function feedbackInterval(): int
    {
        return 500;
    }

    /**
     * Sets the initial bitrate estimate and marks the controller as initialized.
     *
     * @param int $bitrate Initial bitrate in bits per second.
     * @param int $nowMs Current time in milliseconds.
     */
    public function setEstimate(int $bitrate, int $nowMs): void
    {
        $this->currentBitrate = $this->clampBitrate($bitrate, $bitrate);
        $this->currentBitrateInitialized = true;
        $this->lastChangeMs = $nowMs;
    }

    /**
     * Updates the controller state and bitrate estimate based on observed bandwidth usage.
     *
     * @param BandwidthUsage $bandwidthUsage Current bandwidth usage classification.
     * @param int|null $estimatedThroughput Estimated throughput in bps.
     * @param int $nowMs Current time in milliseconds.
     * @return int|null Updated bitrate, or null if not initialized.
     */
    public function update(BandwidthUsage $bandwidthUsage, ?int $estimatedThroughput, int $nowMs): ?int
    {
        if (!$this->initializeBitrate($estimatedThroughput, $nowMs)) {
            return null;
        }

        $this->updateState($bandwidthUsage, $nowMs);

        $newBitrate = $this->adjustBitrate($estimatedThroughput, $nowMs);

        $this->currentBitrate = $this->clampBitrate($newBitrate, $this->latestEstimatedThroughput);
        return $this->currentBitrate;
    }

    /**
     * Initializes the bitrate controller after receiving enough throughput data.
     *
     * @param int|null $estimatedThroughput Initially estimated throughput.
     * @param int $nowMs Current time in milliseconds.
     * @return bool Whether bitrate is now initialized.
     */
    private function initializeBitrate(?int $estimatedThroughput, int $nowMs): bool
    {
        if (!$this->currentBitrateInitialized && $estimatedThroughput !== null) {
            if ($this->firstEstimatedThroughputTime === null) {
                $this->firstEstimatedThroughputTime = $nowMs;
            } elseif ($nowMs - $this->firstEstimatedThroughputTime > 3000) {
                $this->currentBitrate = $estimatedThroughput;
                $this->currentBitrateInitialized = true;
            }
        }

        return $this->currentBitrateInitialized || $estimatedThroughput !== null;
    }

    /**
     * Transitions the internal AIMD state based on the given bandwidth usage.
     *
     * @param BandwidthUsage $bandwidthUsage The observed bandwidth usage state.
     * @param int $nowMs Current time in milliseconds.
     */
    private function updateState(BandwidthUsage $bandwidthUsage, int $nowMs): void
    {
        if ($bandwidthUsage === BandwidthUsage::NORMAL && $this->state === RateControlState::HOLD) {
            $this->lastChangeMs = $nowMs;
            $this->state = RateControlState::INCREASE;
        } elseif ($bandwidthUsage === BandwidthUsage::OVERUSING) {
            $this->state = RateControlState::DECREASE;
        } elseif ($bandwidthUsage === BandwidthUsage::UNDERUSING) {
            $this->state = RateControlState::HOLD;
        }
    }

    /**
     * Calculates the new bitrate based on current state and throughput.
     *
     * @param int|null $estimatedThroughput Current estimated throughput.
     * @param int $nowMs Current time in milliseconds.
     * @return int Newly calculated bitrate.
     */
    private function adjustBitrate(?int $estimatedThroughput, int $nowMs): int
    {
        $newBitrate = $this->currentBitrate;
        $this->latestEstimatedThroughput = $estimatedThroughput ?? $this->latestEstimatedThroughput;
        $estimatedThroughputKbps = $this->latestEstimatedThroughput / 1000;

        if ($this->state === RateControlState::INCREASE) {
            $newBitrate = $this->increaseBitrate($estimatedThroughputKbps, $nowMs);
        } elseif ($this->state === RateControlState::DECREASE) {
            $newBitrate = $this->decreaseBitrate($estimatedThroughputKbps, $nowMs);
        }

        return $newBitrate;
    }

    /**
     * Increases the current bitrate using additive or multiplicative strategy.
     *
     * @param float $estimatedThroughputKbps Estimated throughput in kbps.
     * @param int $nowMs Current time in milliseconds.
     * @return int New increased bitrate.
     */
    private function increaseBitrate(float $estimatedThroughputKbps, int $nowMs): int
    {
        if ($this->avgMaxBitrateKbps !== null) {
            $sigmaKbps = sqrt($this->varMaxBitrateKbps * $this->avgMaxBitrateKbps);
            if ($estimatedThroughputKbps >= $this->avgMaxBitrateKbps + 3.0 * $sigmaKbps) {
                $this->nearMax = false;
                $this->avgMaxBitrateKbps = null;
            }
        }

        $increaseAmount = $this->nearMax
            ? $this->additiveRateIncrease($this->lastChangeMs ?? $nowMs, $nowMs)
            : $this->multiplicativeRateIncrease($this->currentBitrate, $this->lastChangeMs, $nowMs);

        $this->lastChangeMs = $nowMs;
        return $this->currentBitrate + $increaseAmount;
    }

    /**
     * Decreases the bitrate in response to overuse, updating internal estimates.
     *
     * @param float $estimatedThroughputKbps Estimated throughput in kbps.
     * @param int $nowMs Current time in milliseconds.
     *
     * @return int New reduced bitrate.
     */
    private function decreaseBitrate(float $estimatedThroughputKbps, int $nowMs): int
    {
        if ($this->avgMaxBitrateKbps !== null) {
            $sigmaKbps = sqrt($this->varMaxBitrateKbps * $this->avgMaxBitrateKbps);
            if ($estimatedThroughputKbps < $this->avgMaxBitrateKbps - 3.0 * $sigmaKbps) {
                $this->avgMaxBitrateKbps = null;
            }
        }

        $this->updateMaxThroughputEstimate($estimatedThroughputKbps);

        $this->nearMax = true;
        $this->lastChangeMs = $nowMs;
        $this->state = RateControlState::HOLD;

        return intval(round(0.85 * (float) $this->latestEstimatedThroughput));
    }

    /**
     * Calculates the additive rate increase based on elapsed time.
     *
     * @param int $lastMs Last change timestamp.
     * @param int $nowMs Current time.
     * @return int Bitrate increase amount.
     */
    private function additiveRateIncrease(int $lastMs, int $nowMs): int
    {
        return intval(($nowMs - $lastMs) * $this->nearMaxRateIncrease() / 1000);
    }

    /**
     * Calculates multiplicative bitrate increase based on time and current rate.
     *
     * @param int $newBitrate Current bitrate.
     * @param int|null $lastMs Last time bitrate changed.
     * @param int $nowMs Current time.
     * @return int Increase amount.
     */
    private function multiplicativeRateIncrease(int $newBitrate, ?int $lastMs, int $nowMs): int
    {
        $alpha = 1.08;
        if ($lastMs !== null) {
            $elapsedMs = min($nowMs - $lastMs, 1000);
            $alpha = pow($alpha, $elapsedMs / 1000);
        }
        return intval(max(($alpha - 1.0) * (float) $newBitrate, 1000));
    }

    /**
     * Limits the bitrate to a range based on estimated throughput.
     *
     * @param int $newBitrate Proposed new bitrate.
     * @param int $estimatedThroughput Measured throughput in bps.
     * @return int Clamped bitrate.
     */
    private function clampBitrate(int $newBitrate, int $estimatedThroughput): int
    {
        $maxBitrate = max(intval(1.5 * (float) $estimatedThroughput) + 10000, $this->currentBitrate);
        return min($newBitrate, $maxBitrate);
    }

    /**
     * Computes the near-max rate increase for additive control mode.
     *
     * @return int Additive increases rate in bits per second.
     */
    private function nearMaxRateIncrease(): int
    {
        $bitsPerFrame = (float) $this->currentBitrate / 30.0;
        $packetsPerFrame = ceil($bitsPerFrame / (8.0 * 1200.0));
        $avgPacketSizeBits = $bitsPerFrame / $packetsPerFrame;

        $responseTime = $this->rtt + 100;
        return max(4000, intval(($avgPacketSizeBits * 1000.0) / (float) $responseTime));
    }

    /**
     * Updates the average and variance of maximum throughput estimate.
     *
     * @param float $estimatedThroughputKbps Current estimated throughput in kbps.
     */
    private function updateMaxThroughputEstimate(float $estimatedThroughputKbps): void
    {
        $alpha = 0.05;
        if ($this->avgMaxBitrateKbps === null) {
            $this->avgMaxBitrateKbps = $estimatedThroughputKbps;
        } else {
            $this->avgMaxBitrateKbps = (1.0 - $alpha) * $this->avgMaxBitrateKbps + $alpha * $estimatedThroughputKbps;
        }

        $norm = max(1.0, $this->avgMaxBitrateKbps);
        $this->varMaxBitrateKbps = (1.0 - $alpha) * $this->varMaxBitrateKbps + $alpha *
            pow($this->avgMaxBitrateKbps - $estimatedThroughputKbps, 2.0) / $norm;
        $this->varMaxBitrateKbps = max(0.4, min($this->varMaxBitrateKbps, 2.5));
    }

    /**
     * Returns the current rate control state (HOLD, INCREASE, DECREASE).
     *
     * @return RateControlState Current state.
     */
    public function getState(): RateControlState
    {
        return $this->state;
    }

    /**
     * Returns the average of the estimated maximum throughput in kbps.
     *
     * @return float|null Estimated average max throughput or null if uninitialized.
     */
    public function getAvgMaxBitrateKbps(): ?float
    {
        return $this->avgMaxBitrateKbps;
    }

    /**
     * Returns the variance of the estimated maximum throughput in kbps.
     *
     * @return float Variance of max throughput.
     */
    public function getVarMaxBitrateKbps(): float
    {
        return $this->varMaxBitrateKbps;
    }

    /**
     * Indicates whether the current bitrate is near the estimated maximum.
     *
     * @return bool True if near max, false otherwise.
     */
    public function isNearMax(): bool
    {
        return $this->nearMax;
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTP\Receiver;

use Webrtc\RTP\RtpConstants;
use Webrtc\RTP\RtpPacket;

/**
 * NackGenerator tracks missing RTP packets based on sequence numbers,
 * enabling the generation of NACK (Negative Acknowledgment) feedback.
 *
 * This class is essential for improving media reliability in RTP-based transport by
 * detecting lost packets and maintaining a list of missing sequence numbers.
 * It handles 16-bit sequence number wrap-around and automatically prunes old entries
 * to limit memory usage based on a fixed RTP history window.
 */
final class NackGenerator
{
    private ?int $maxSeq = null;
    /** @var array<int, bool> */
    private array $missing = [];

    /**
     * Add a new RTP packet to track missing packets.
     *
     * @param RtpPacket $packet The received RTP packet.
     * @return bool True if there are missing packets, false otherwise.
     */
    public function add(RtpPacket $packet): bool
    {
        $missed = false;

        if ($this->maxSeq === null) {
            $this->maxSeq = $packet->getSequenceNumber();
            return false;
        }

        // Mark missing packets
        if ($this->uint16Gt($packet->getSequenceNumber(), $this->maxSeq)) {
            $seq = $this->uint16Add($this->maxSeq, 1);
            while ($this->uint16Gt($packet->getSequenceNumber(), $seq)) {
                $this->missing[$seq] = true;
                $missed = true;
                $seq = $this->uint16Add($seq, 1);
            }
            $this->maxSeq = $packet->getSequenceNumber();
        } else {
            unset($this->missing[$packet->getSequenceNumber()]);
        }

        // Limit the number of tracked packets
        $this->truncate();

        return $missed;
    }

    /**
     * Truncate the missing packets to prevent excessive memory usage.
     */
    private function truncate(): void
    {
        if ($this->maxSeq !== null) {
            $minSeq = $this->uint16Add($this->maxSeq, -RtpConstants::RTP_HISTORY_SIZE);
            foreach (array_keys($this->missing) as $seq) {
                if ($this->uint16Gt($minSeq, $seq)) {
                    unset($this->missing[$seq]);
                }
            }
        }
    }

    /**
     * Check if a 16-bit sequence number is greater than another.
     *
     * @param int $a First sequence number.
     * @param int $b Second sequence number.
     * @return bool True if $a > $b considering 16-bit wrap-around.
     */
    private function uint16Gt(int $a, int $b): bool
    {
        $halfMod = 0x8000;
        return (($a < $b) && (($b - $a) > $halfMod)) || (($a > $b) && (($a - $b) < $halfMod));
    }


    /**
     * Add a value to a 16-bit sequence number, handling wrap-around.
     *
     * @param int $a Base sequence number.
     * @param int $b Value to add.
     * @return int The Resulting sequence number within 16-bit range.
     */
    private function uint16Add(int $a, int $b): int
    {
        return ($a + $b) & 0xFFFF;
    }

    /**
     * Get the list of missing sequence numbers.
     *
     * @return int[] List of missing sequence numbers.
     */
    public function getMissing(): array
    {
        return array_keys($this->missing);
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTP\Receiver;

use DateMalformedStringException;
use DateTimeImmutable;
use Exception;
use Throwable;
use Psr\Log\LoggerInterface;
use Random\RandomException;
use Revolt\EventLoop;
use Webrtc\Codecs\Codec;
use Webrtc\Codecs\EncodedPacket;
use Webrtc\Codecs\CodecUtility;
use Webrtc\Exception\InvalidArgumentException;
use Webrtc\Mixin\SerializableState;
use Webrtc\NTP\NetworkTimeProtocol;
use Webrtc\RTCP\Exception\RtcpExceptionInterface;
use Webrtc\RTCP\RtcpByePacket;
use Webrtc\RTCP\RtcpConstants;
use Webrtc\RTCP\RtcpPacketInterface;
use Webrtc\RTCP\RtcpPsfbPacket;
use Webrtc\RTCP\RtcpReceiverInfo;
use Webrtc\RTCP\RtcpRrPacket;
use Webrtc\RTCP\RtcpRtpfbPacket;
use Webrtc\RTCP\RtcpSrPacket;
use Webrtc\RTP\Enum\MediaKind;
use Webrtc\RTP\RtpComponentLogger;
use Webrtc\RTP\Jitter\JitterBuffer;
use Webrtc\RTP\Jitter\JitterFrame;
use Webrtc\RTP\MediaStreamTrack\MediaStreamTrack;
use Webrtc\RTP\MediaStreamTrack\RemoteStreamTrack;
use Webrtc\RTP\Receiver\Rate\RemoteBitrateEstimator;
use Webrtc\RTP\RTCRTPDtlsTransportInterface;
use Webrtc\RTP\RtpPacket;
use Webrtc\RTP\RtpUtility;
use Webrtc\RTPParameter\RTCRtpCapabilities;
use Webrtc\RTPParameter\RTCRtpCodecParameters;
use Webrtc\RTPParameter\RTCRtpReceiveParameters;
use Webrtc\RTPParameter\RTCRtpSynchronizationSource;
use Webrtc\Stats\enum\TLSState;
use Webrtc\Stats\RTCInboundRtpStreamStats;
use Webrtc\Stats\RTCRemoteOutboundRtpStreamStats;
use Webrtc\Stats\RTCStatsReport;

/**
 * RTCRtpReceiver is responsible for receiving and processing RTP media streams,
 * including decoding, statistics gathering, handling RTCP feedback, and managing jitter buffers.
 *
 * This class plays a critical role in the WebRTC stack, managing the flow of audio or video media
 * from the DTLS transport layer to the remote MediaStreamTrack.
 */
final class RTCRtpReceiver implements RtpReceiverInterface

{
    /**
     * Minimum bandwidth (bps) we ever advertise in REMB. A receive-and-record endpoint is not
     * downlink-limited, and the delay-based estimator misfires under PHP's arrival-time jitter, so we
     * never let it throttle the peer below a value that lets it reach its own configured maximum.
     */
    private const REMB_FLOOR_BPS = 4_000_000;

    private bool $enabled = true;
    /** @var array<int, float> When a packet was last received from each SSRC, as a Unix timestamp. */
    private array $activeSources = [];
    /** @var array<int, RTCRtpCodecParameters> */
    private array $codecs = [];
    private ?JitterBuffer $jitterBuffer;
    /** Codec and SSRC of the last frame added to the jitter buffer, to deliver the frames it still holds on stop. */
    private ?RTCRtpCodecParameters $lastCodec = null;
    private int $lastSsrc = 0;
    private ?NackGenerator $nackGenerator;
    private ?RemoteBitrateEstimator $remoteBitrateEstimator;
    private ?RemoteStreamTrack $track = null;
    /** @var array<int, int> */
    private array $rtxSsrc = [];
    private bool $started = false;
    private RTCStatsReport $stats;
    private TimestampMapper $timestampMapper;
    /** @var array<int, int> */
    private array $lsr = [];
    /** @var array<int, float> */
    private array $lsrTime = [];
    /** @var StreamStatistics[] */
    private array $remoteStreams = [];
    private ?int $rtcpSsrc = null;
    private ?LoggerInterface $logger = null;
    private ?DecoderQueue $decoderQueue = null;

    /**
     * Whether assembled frames are delivered still encoded, instead of being decoded to raw media.
     */
    private bool $rawMode = false;
    private string $rtcpTask = "";

    /**
     * Constructor for RTCRtpReceiver.
     *
     * Initializes jitter buffers, optional NACK generator and bitrate estimator,
     * statistics collector, and transport handling.
     *
     * @param MediaKind $kind The kind of media being received (audio or video).
     * @param RTCRTPDtlsTransportInterface $transport The DTLS transport used for secure media delivery.
     * @throws InvalidArgumentException If the DTLS transport is closed.
     */
    public function __construct(private readonly MediaKind $kind, private RTCRTPDtlsTransportInterface $transport)
    {
        if ($transport->getState() === TLSState::CLOSED) {
            throw new InvalidArgumentException("Transport is closed");
        }
        $this->jitterBuffer = $kind === MediaKind::Audio ? new JitterBuffer(16, 4) : new JitterBuffer(128, 0, true);
        $this->nackGenerator = $kind === MediaKind::Video ? new NackGenerator() : null;
        $this->remoteBitrateEstimator = $kind === MediaKind::Video ? new RemoteBitrateEstimator() : null;
        $this->stats = new RTCStatsReport();
        $this->timestampMapper = new TimestampMapper();
    }

    /**
     * Retrieves RTP capabilities supported for the given media kind.
     *
     * @param string $kind 'audio' or 'video'.
     * @return RTCRtpCapabilities|null The RTP capabilities or null if unavailable.
     */
    public static function getCapabilities(string $kind): ?RTCRtpCapabilities

    {
        $codec = new Codec();
        return $codec->getCapabilities($kind);
    }

    /**
     * Gets the remote MediaStreamTrack associated with the receiver.
     *
     * @return ?MediaStreamTrack The media track or null if not set.
     */
    /**
     * Deliver assembled frames without decoding them.
     *
     * Must be set before {@see self::start()}. In this mode the receiver never instantiates a
     * decoder, so no native codec library is required.
     */
    public function setRawMode(bool $rawMode): void
    {
        $this->rawMode = $rawMode;
    }

    /**
     * Whether assembled frames are delivered still encoded.
     */
    public function isRawMode(): bool
    {
        return $this->rawMode;
    }

    /** Optional end-to-end frame decryptor; when set, incoming frames are decrypted after reassembly. */
    private ?\Webrtc\RTP\Crypto\FrameCryptorInterface $frameCryptor = null;

    /** Install (or clear) the end-to-end frame decryptor applied to incoming frames. */
    public function setFrameCryptor(?\Webrtc\RTP\Crypto\FrameCryptorInterface $frameCryptor): void
    {
        $this->frameCryptor = $frameCryptor;
    }

    public function getTrack(): ?RemoteStreamTrack
    {
        return $this->track;
    }

    /**
     * Gets the RTCDtlsTransport used by this receiver.
     *
     * @return RTCRTPDtlsTransportInterface The transport instance.
     */
    public function getTransport(): RTCRTPDtlsTransportInterface
    {
        return $this->transport;
    }

    /**
     * Collects and returns a statistics report for the current inbound RTP stream.
     *
     * @return RTCStatsReport A report containing inbound RTP and transport statistics.
     */
    public function getStats(): RTCStatsReport
    {
        foreach ($this->remoteStreams as $ssrc => $stream) {
            $this->stats->add(new RTCInboundRtpStreamStats(
                id: "inbound_rtp_stream_" . spl_object_id($this),
                ssrc: (int) $ssrc,
                kind: $this->kind->value,
                transportId: $this->transport->getReportTransport()->id,
                packetsReceived: $stream->getPacketsReceived(),
                packetsLost: $stream->getPacketsLost(),
                jitter: $stream->getJitter()
            ));
        }
        $this->stats->add($this->transport->getReportTransport());

        return $this->stats;
    }

    /**
     * Gets a list of synchronization sources (SSRCs) that have been active within the last 10 seconds.
     *
     * @return RTCRtpSynchronizationSource[] An array of synchronization source entries.
     */
    public function getSynchronizationSources(): array
    {
        $cutoff = microtime(true) - 10.0;
        $sources = [];
        foreach ($this->activeSources as $source => $timestamp) {
            if ($timestamp >= $cutoff) {
                $sources[] = new RTCRtpSynchronizationSource(
                    new DateTimeImmutable(sprintf('@%.6F', $timestamp)),
                    $source,
                );
            }
        }
        return $sources;
    }

    /**
     * Starts the RTP receiver with the provided parameters.
     *
     * This includes codec setup, RTX mapping, transport binding, and RTCP reporting.
     *
     * @param RTCRtpReceiveParameters $parameters The receiver's codec and encoding parameters.
     * @throws RandomException
     */
    public function start(RTCRtpReceiveParameters $parameters): void
    {
        $this->codecs = [];
        $this->rtxSsrc = [];
        foreach ($parameters->codecs as $codec) {
            if ($codec->payloadType === null) {
                continue;
            }
            $this->codecs[$codec->payloadType] = $codec;
        }

        foreach ($parameters->encodings as $encoding) {
            if ($encoding->rtx) {
                $this->rtxSsrc[$encoding->rtx->ssrc] = $encoding->ssrc;
            }
        }

        $this->transport->setRtpReceiver($this, $parameters);
        if ($this->started) {
            $this->decoderQueue?->stop();
            $this->decoderQueue = null;
            $this->logger?->debug(" RTP Receiver reconfigured");
            return;
        }
        $this->runRtcp();
        $this->started = true;
    }

    /**
     * Updates the transport used by the receiver.
     *
     * @param RTCRTPDtlsTransportInterface $transport The new transport.
     */
    public function setTransport(RTCRTPDtlsTransportInterface $transport): void
    {
        $this->transport = $transport;
    }

    /**
     * Stops the receiver and cleans up associated resources.
     *
     * Cancels the RTCP timer and removes the receiver from the transport.
     */
    #[\Override]
    public function stop(): void

    {
        if (!$this->started) {
            return;
        }
        $this->transport->removeRtpReceiver($this);

        // Cancel RTCP periodic task
        EventLoop::cancel($this->rtcpTask);
        $this->logger?->debug(" RTCP has ended.");
        // Hand over the encoded frames the jitter buffer was holding back: no packet will arrive anymore
        // to push them out. (A decoder would be stopped right away, so they're only useful in raw mode.)
        if ($this->rawMode && $this->jitterBuffer !== null && $this->lastCodec !== null) {
            foreach ($this->jitterBuffer->flush() as $frame) {
                $this->decodeFrame($frame, $this->lastCodec, $this->lastSsrc, last: true);
            }
        }
        $this->finishUpRtp();
    }

    /**
     * Handles incoming RTCP packets and delegates to appropriate handlers.
     *
     * @param RtcpPacketInterface $packet The RTCP packet to handle.
     * @throws DateMalformedStringException
     */
    #[\Override]
    public function handleRtcpPacket(RtcpPacketInterface $packet): void
    {
        $this->logger?->debug("Received RTCP packet: " . (string) $packet);

        if ($packet instanceof RtcpSrPacket) {
            $this->handleRtcpSrPacket($packet);
        } elseif ($packet instanceof RtcpByePacket) {
            // A source leaving (RFC 3550, section 6.6) doesn't end the receiver, which belongs to its
            // transceiver: the source can come back (a sender restored after a restart of its process
            // does), and only a renegotiation or closing the connection stops receiving.
            $this->logger?->debug("RTCP BYE received");
        }
    }

    /**
     * Handle the reception of an RTCP SR packet.
     *
     * @param RtcpSrPacket $packet The RTCP SR packet to handle.
     * @throws DateMalformedStringException
     */
    private function handleRtcpSrPacket(RtcpSrPacket $packet): void
    {
        $senderInfo = $packet->getSenderInfo();
        $ntpHigh = $senderInfo->getNtpTimestampHigh();
        $ntpLow = $senderInfo->getNtpTimestampLow();

        // The LSR field carries the middle 32 bits of the raw 64-bit NTP timestamp.
        $ntpTimestamp = (($ntpHigh & 0xFFFF) << 16) | ($ntpLow >> 16);

        $this->stats->add(
            new RTCRemoteOutboundRtpStreamStats(
            // RTCStats
                id: "remote_outbound_rtp_stream_" . spl_object_id($this),
                // RTCStreamStats
                ssrc: $packet->getSsrc(),
                kind: $this->kind->value,
                transportId: $this->transport->getReportTransport()->id,
                // RTCSentRtpStreamStats
                packetsSent: $packet->getSenderInfo()->getPacketCount(),
                bytesSent: $packet->getSenderInfo()->getOctetCount(),
                // RTCRemoteOutboundRtpStreamStats
                remoteTimestamp: NetworkTimeProtocol::toDatetime(
                    $ntpHigh,
                    $ntpLow,
                ),
            )
        );


        $this->lsr[$packet->getSsrc()] = $ntpTimestamp;
        $this->lsrTime[$packet->getSsrc()] = microtime(true);
    }

    /**
     * Handles incoming RTP packets and processes them through decoding, jitter buffering,
     * retransmission handling, and statistics collection.
     *
     * @param RtpPacket $packet The RTP packet received.
     * @param int $arrivalTimeMs Time in milliseconds when the packet arrived.
     * @throws DateMalformedStringException
     */
    #[\Override]
    public function handleRtpPacket(RtpPacket $packet, int $arrivalTimeMs): void
    {
        $this->logger?->debug("Received RTP packet: " . (string) $packet);

        if (!$this->enabled) {
            return;
        }

        $this->feedBitrateEstimator($packet, $arrivalTimeMs);
        $this->trackActiveSource($packet);
        $codec = $this->validateCodec($packet);
        if (!$codec) return;

        $this->updateRtcpStatistics($packet, $codec);
        $packet = $this->handleRetransmission($packet, $codec);
        if (!$packet) return;

        $this->handleMissingPackets($packet);
        $decoded = $this->parsePayload($packet, $codec);
        if (!$decoded) return;

        $this->processEncodedFrame($packet, $codec);
    }

    /**
     * Feeds bitrate estimator with packet metadata to help calculate receive bitrate.
     *
     * @param RtpPacket $packet The RTP packet.
     * @param int $arrivalTimeMs Time in milliseconds of arrival.
     */
    private function feedBitrateEstimator(RtpPacket $packet, int $arrivalTimeMs): void
    {
        // BWDEBUG
        if ($this->kind === MediaKind::Video) {
            $hasAst = $packet->getExtensions()->getAbsSendTime() !== null;
        }
        if ($this->remoteBitrateEstimator !== null && $packet->getExtensions()->getAbsSendTime() !== null) {
            // RemoteBitrateEstimator::add(arrivalTimeMs, absSendTime, payloadSize, ssrc) â€” the first
            // two arguments were reversed here, swapping arrival and send time inside the estimator and
            // producing garbage (near-zero) REMB bitrates, which made peers throttle to almost nothing
            // (Telegram Android showed "weak signal" and sent 320x180). Pass them in the correct order.
            $remb = $this->remoteBitrateEstimator->add(
                $arrivalTimeMs,
                $packet->getExtensions()->getAbsSendTime(),
                strlen($packet->getPayload()) + $packet->getPaddingSize(),
                $packet->getSsrc()
            );

            if ($this->rtcpSsrc !== null && $remb !== null) {
                /** @var array{0: int, 1: int[]} $remb */
                // The delay-based estimator relies on browser-precise packet arrival timestamps; in PHP
                // the arrival time carries event-loop jitter far above the overuse detector's ~0.2ms
                // gradient sensitivity, so it flags false congestion and the AIMD collapses toward the
                // low early-call rate, throttling the peer to a tiny resolution. This endpoint only
                // receives and records â€” it is not downlink-limited â€” so we never advertise a REMB below
                // a floor that lets the peer reach its own configured maximum. The peer's encoder still
                // caps itself, so this maximises recording quality without over-sending.
                $rembBitrate = max($remb[0], self::REMB_FLOOR_BPS);
                $rtcpPacket = new RtcpPsfbPacket(
                    fmt: RtcpConstants::RTCP_PSFB_APP,
                    ssrc: $this->rtcpSsrc,
                    mediaSsrc: 0,
                    fci: RtpUtility::packRembFci($rembBitrate, $remb[1])
                );

                $this->sendRtcp($rtcpPacket);
            }
        }
    }

    /**
     * Tracks which synchronization source (SSRC) was active most recently.
     *
     * @param RtpPacket $packet The incoming RTP packet.
     */
    private function trackActiveSource(RtpPacket $packet): void
    {
        // Not a date object, for every packet.
        $this->activeSources[$packet->getSsrc()] = microtime(true);
    }

    /**
     * Validates the codec based on the RTP payload type.
     *
     * @param RtpPacket $packet The RTP packet.
     * @return RTCRtpCodecParameters|null Codec parameters or null if unrecognized.
     */
    private function validateCodec(RtpPacket $packet): ?RTCRtpCodecParameters
    {
        if (!isset($this->codecs[$packet->getPayloadType()])) {
            $this->logger?->debug(sprintf("x RTP packet with unknown payload type %d", $packet->getPayloadType()));
            return null;
        }
        return $this->codecs[$packet->getPayloadType()];
    }

    /**
     * Updates internal RTCP statistics based on the received RTP packet.
     *
     * @param RtpPacket $packet The RTP packet.
     * @param RTCRtpCodecParameters $codec Codec associated with the packet.
     */
    private function updateRtcpStatistics(RtpPacket $packet, RTCRtpCodecParameters $codec): void

    {
        if (!isset($this->remoteStreams[$packet->getSsrc()])) {
            $this->remoteStreams[$packet->getSsrc()] = new StreamStatistics($codec->clockRate);
        }
        $this->remoteStreams[$packet->getSsrc()]->add($packet);
    }

    /**
     * Handles RTX (Retransmission) packets and rewrites them to original format.
     *
     * @param RtpPacket $packet The RTP packet, possibly RTX.
     * @param RTCRtpCodecParameters $codec Codec of the packet.
     * @return RtpPacket|null Original packet or null if decoding failed.
     */
    private function handleRetransmission(RtpPacket $packet, RTCRtpCodecParameters $codec): ?RtpPacket
    {
        if (CodecUtility::isRtx($codec)) {
            $originalSsrc = $this->rtxSsrc[$packet->getSsrc()] ?? null;
            if ($originalSsrc === null) {
                $this->logger?->debug(sprintf("x RTX packet from unknown SSRC %d", $packet->getSsrc()));
                return null;
            }

            if (strlen($packet->getPayload()) < 2) {
                return null;
            }

            $apt = $codec->parameters["apt"] ?? null;
            if (!is_int($apt)) {
                return null;
            }
            $codec = $this->codecs[$apt];
            $packet = RtpUtility::unwrapRtx($packet, (int) $codec->payloadType, $originalSsrc);
        }

        return $packet;
    }

    /**
     * Checks and processes missing RTP packets using NACK.
     *
     * @param RtpPacket $packet The RTP packet.
     */
    private function handleMissingPackets(RtpPacket $packet): void
    {
        if ($this->nackGenerator && $this->nackGenerator->add($packet)) {
            $missing = $this->nackGenerator->getMissing();
            sort($missing);
            $this->sendRtcpNack($packet->getSsrc(), $missing);
        }
    }

    /**
     * Parses the payload of an RTP packet using the appropriate codec.
     *
     * @param RtpPacket $packet The RTP packet.
     * @param RTCRtpCodecParameters $codec Codec used to parse payload.
     * @return bool True if successfully parsed, false otherwise.
     */
    private function parsePayload(RtpPacket $packet, RTCRtpCodecParameters $codec): bool
    {
        // With an end-to-end frame decryptor the packets still carry the codec's payload descriptors
        // (the sender encrypted the encoded frame before packetizing it, like WebRTC's frame
        // transformers): depacketize normally, the reassembled frame is decrypted in decodeFrame().
        try {
            [, $decoded] = Codec::depayload($codec, $packet->payload);
            $packet->setDecodedData((string) $decoded);
            return true;
        } catch (Exception $e) {
            $this->logger?->debug(sprintf(" x RTP payload parsing failed: %s", $e->getMessage()));
            return false;
        }
    }

    /**
     * Adds the packet to the jitter buffer and handles any PLI triggering.
     *
     * @param RtpPacket $packet The RTP packet.
     * @param RTCRtpCodecParameters $codec The codec.
     */
    private function processEncodedFrame(RtpPacket $packet, RTCRtpCodecParameters $codec): void
    {
        if ($this->jitterBuffer === null) {
            return;
        }
        [$pliFlag, $encodedFrame] = $this->jitterBuffer->add($packet);
        $this->lastCodec = $codec;
        $this->lastSsrc = $packet->getSsrc();

        if ($pliFlag) {
            $this->sendRtcpPli($packet->getSsrc());
        }

        $this->decodeFrame($encodedFrame, $codec, $packet->getSsrc());
    }

    /**
     * Start the RTCP communication for the receiver, sending periodic packets.
     *
     * @throws RandomException
     */
    private function runRtcp(): void
    {
        $this->logger?->debug(" RTCP started");

        // Weak self-reference: a repeat watcher registered as $this->onRtcpTimer(...) would pin
        // this receiver in the event loop forever, so an unset()+gc could never reclaim it. The
        // tick cancels itself once the owner has been collected.
        $weak = \WeakReference::create($this);
        $this->rtcpTask = EventLoop::repeat(0.5 + ((float) random_int(0, 1000) / 1000.0), static function (string $id) use ($weak): void {
            $self = $weak->get();
            if ($self === null) {
                EventLoop::cancel($id);

                return;
            }
            $self->onRtcpTimer();
        });
    }

    /**
     * Periodic RTCP tick.
     *
     * Scheduled only as the first-class callable `$this->onRtcpTimer(...)` handed to
     * EventLoop::repeat(), including when the watcher is re-armed after unserialize. That callable
     * keeps this method's private scope, so it does not need to be public.
     */
    private function onRtcpTimer(): void
    {
        try {
            $rtcpPackets = $this->generateRtcpRrPacket();
            if ($rtcpPackets) {
                $this->sendRtcp($rtcpPackets);
            }
        } catch (RtcpExceptionInterface $e) {
            $this->logger?->warning(sprintf("RTCP error: %s", $e->getMessage()));
        }
    }

    /**
     * Generate an RTCP RR (Receiver Report) packet with statistics.
     *
     * @return RtcpRrPacket|null The generated RTCP RR packet, or null if no report was generated.
     */
    private function generateRtcpRrPacket(): ?RtcpRrPacket
    {
        // RTCP RR
        $reports = [];
        foreach ($this->remoteStreams as $ssrc => $stream) {
            $ssrc = (int) $ssrc;
            $lsr = 0;
            $dlsr = 0;
            if (isset($this->lsr[$ssrc], $this->lsrTime[$ssrc])) {
                $lsr = $this->lsr[$ssrc];
                $delay = microtime(true) - $this->lsrTime[$ssrc];
                if ($delay > 0 && $delay < 65536) {
                    $dlsr = intval($delay * 65536.0);
                }
            }

            $reports[] = new RtcpReceiverInfo(
                $ssrc,
                $stream->getFractionLost(),
                $stream->getPacketsLost(),
                $stream->getMaxSeq() ?? 0,
                $stream->getJitter(),
                $lsr,
                $dlsr
            );
        }

        if ($this->rtcpSsrc !== null && !empty($reports)) {
            return new RtcpRrPacket($this->rtcpSsrc, $reports);
        }

        return null;
    }

    /**
     * Send an RTCP packet over the transport.
     *
     * @param RtcpPacketInterface $packet The RTCP packet to send.
     */
    private function sendRtcp(RtcpPacketInterface $packet): void
    {
        $this->logger?->debug(sprintf(" Sent Rtcp packet: %s", $packet));
        try {
            $this->transport->sendRtcp($packet->encode());
        } catch (\Throwable $e) {
            $this->logger?->warning("Failed to send RTCP: " . $e->getMessage());
        }
    }

    /**
     * Set a logger instance for logging.
     *
     * @param LoggerInterface $logger The logger instance.
     */
    public function setLogger(LoggerInterface $logger): void
    {
        $this->logger = new RtpComponentLogger('RTCRtpReceiver', $this->kind->value, $logger);
    }

    /**
     * Send an RTCP NACK (Negative Acknowledgment) to report missing RTP packets.
     *
     * @param int $mediaSsrc The SSRC of the media stream.
     * @param int[] $lost An array of lost packet sequence numbers.
     */
    public function sendRtcpNack(int $mediaSsrc, array $lost): void
    {
        if ($this->rtcpSsrc !== null) {
            $packet = new RtcpRtpfbPacket(
                fmt: RtcpConstants::RTCP_RTPFB_NACK,
                ssrc: $this->rtcpSsrc,
                mediaSsrc: $mediaSsrc,
                lost: $lost
            );

            $this->sendRtcp($packet);
        }
    }

    /**
     * Set the MediaStreamTrack for the receiver.
     *
     * @param RemoteStreamTrack|null $track The MediaStreamTrack to associate with the receiver.
     */
    public function setTrack(?RemoteStreamTrack $track): void
    {
        $this->track = $track;
    }

    /**
     * Send an RTCP PLI (Picture Loss Indication) packet.
     *
     * @param int $mediaSsrc The SSRC of the media stream.
     */
    public function sendRtcpPli(int $mediaSsrc): void
    {
        if ($this->rtcpSsrc !== null) {
            $packet = new RtcpPsfbPacket(
                fmt: RtcpConstants::RTCP_PSFB_PLI,
                ssrc: $this->rtcpSsrc,
                mediaSsrc: $mediaSsrc
            );

            $this->sendRtcp($packet);
        }
    }

    /**
     * Decode the encoded frame from the jitter buffer.
     *
     * @param JitterFrame|null $encodedFrame The encoded frame to decode.
     * @param RTCRtpCodecParameters $codec The codec to use for decoding.
     */
    private function decodeFrame(?JitterFrame $encodedFrame, RTCRtpCodecParameters $codec, int $ssrc = 0, bool $last = false): void
    {
        if (!$encodedFrame) {
            return;
        }

        $encodedFrame->setTimestamp($this->timestampMapper->map($encodedFrame->getTimestamp()));

        if ($this->frameCryptor !== null) {
            // Decrypt the reassembled ciphertext frame back to the clean encoded frame; a frame that
            // fails to decrypt or verify is dropped rather than delivered.
            try {
                $encodedFrame->setData($this->frameCryptor->decryptFrame($this->kind, $ssrc, $encodedFrame->getData()));
            } catch (Throwable $e) {
                $this->logger?->debug('x dropping a frame that failed to decrypt: '.$e->getMessage());
                return;
            }
        }

        if ($this->rawMode) {
            // Hand the assembled, still-encoded frame straight to the track: this avoids loading
            // any codec at all, which is what lets calls work without the FFI extension. Codecs
            // whose packets do not simply concatenate into a frame (AV1) are put back together here.
            try {
                $data = Codec::finalizeFrame($codec, $encodedFrame->getData());
            } catch (Throwable $e) {
                $this->logger?->debug('x dropping a frame that could not be reassembled: '.$e->getMessage());
                return;
            }
            $packet = new EncodedPacket($data, $encodedFrame->getTimestamp());
            // When stopping, the frames are only queued: waiting for them to be read would block until someone does.
            $last ? $this->track?->queueLastFrame($packet) : $this->track?->queueFrame($packet);
            return;
        }

        $track = $this->track;
        if ($track === null) {
            return;
        }

        if ($this->decoderQueue === null) {
            $this->decoderQueue = new DecoderQueue(Codec::getDecoder($codec));
            $this->decoderQueue->start($track);
        }
        $this->decoderQueue->addFrame($encodedFrame);
    }

    /**
     * Clean up and stop RTP processing.
     */
    private function finishUpRtp(): void
    {
        if ($this->track) {
            $this->track->stop();
            $this->track = null;
            $this->decoderQueue?->stop();
        }

        $this->decoderQueue = null;

        $this->logger?->debug(" RTP has ended.");
    }

    /**
     * @return int|null
     */
    public function getRtcpSsrc(): ?int
    {
        return $this->rtcpSsrc;
    }

    /**
     * @param int|null $rtcpSsrc
     * @return void
     */
    public function setRtcpSsrc(?int $rtcpSsrc): void
    {
        $this->rtcpSsrc = $rtcpSsrc;
    }

    /**
     * @return MediaKind
     */
    public function getKind(): MediaKind
    {
        return $this->kind;
    }

    /**
     * @param bool $status
     * @return void
     */
    public function setEnabled(bool $status): void
    {
        $this->enabled = $status;
    }

    /**
     * @return bool
     */
    public function isEnabled(): bool
    {
        return $this->enabled;
    }

    /**
     * @return array<string, mixed>
     */
    public function __serialize(): array
    {
        return SerializableState::export($this, [
            'rtcpTask' => $this->started && $this->rtcpTask !== '',
        ]);
    }

    /**
     * @param array<string, mixed> $data
     */
    public function __unserialize(array $data): void
    {
        $restartRtcp = false;
        /**
         * @var mixed $value
         */
        foreach ($data as $key => $value) {
            if (str_ends_with($key, "\0rtcpTask")) {
                $restartRtcp = $value === true;
                $data[$key] = '';
            }
        }
        SerializableState::import($this, $data);
        $this->rtcpTask = '';
        if ($restartRtcp) {
            $this->runRtcp();
        }
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTP\Enum;

enum BandwidthUsage: int
{
    case     NORMAL = 0;
    case UNDERUSING = 1;
    case OVERUSING = 2;
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTP\Enum;

enum MediaKind: string
{
    case Video = "video";
    case Audio = "audio";
    case Unknown = "unknown";
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTP\Enum;

enum RateControlState: int
{
    case     HOLD = 0;
    case INCREASE = 1;
    case DECREASE = 2;
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTP;

use Webrtc\Exception\InvalidArgumentException;
use Webrtc\RTCP\RtcpByePacket;
use Webrtc\RTCP\RtcpConstants;
use Webrtc\RTCP\RtcpPacketInterface;
use Webrtc\RTCP\RtcpPsfbPacket;
use Webrtc\RTCP\RtcpRrPacket;
use Webrtc\RTCP\RtcpRtpfbPacket;
use Webrtc\RTCP\RtcpSrPacket;
use Webrtc\RTP\Exception\RtpExceptionInterface;
use Webrtc\RTP\Receiver\RtpReceiverInterface;
use Webrtc\RTP\Sender\RtpSenderInterface;

/**
 * Routes RTP and RTCP packets to the appropriate senders and receivers.
 *
 * The RtpRouter maintains mappings between SSRCs, payload types, and media streams
 * to correctly route incoming packets to their associated RtpSender or RtpReceiver
 * instances. It supports both RTP and RTCP packet routing.
 */
final class RtpRouter
{
    /** @var array<int, RtpReceiverInterface> List of registered receivers. */
    private array $receivers = [];

    /** @var array<int, RtpSenderInterface> Map of SSRC to sender instances. */
    private array $senders = [];

    /** @var array<string, RtpReceiverInterface> Map of MID values to receiver instances. */
    private array $midTable = [];

    /** @var array<int, RtpReceiverInterface> Map of SSRC to receiver instances. */
    private array $ssrcTable = [];

    /** @var array<int, array<int, RtpReceiverInterface>> Map of payload types to receiver instances. */
    private array $payloadTypeTable = [];

    /**
     * Registers a receiver with associated SSRCs, payload types, and optional MID.
     *
     * @param RtpReceiverInterface $receiver The receiver instance to register.
     * @param int[] $ssrcs List of SSRCs associated with this receiver.
     * @param int[] $payloadTypes List of payload types associated with this receiver.
     * @param string|null $mid Optional media identification (MID) value.
     */
    public function setReceiver(RtpReceiverInterface $receiver, array $ssrcs, array $payloadTypes, ?string $mid = null): void
    {
        $this->receivers[] = $receiver;
        if ($mid !== null) {
            $this->midTable[$mid] = $receiver;
        }
        foreach ($ssrcs as $ssrc) {
            $this->ssrcTable[$ssrc] = $receiver;
        }
        foreach ($payloadTypes as $payloadType) {
            $this->payloadTypeTable[$payloadType][] = $receiver;
        }
    }

    /**
     * Registers a sender with a specific SSRC.
     *
     * @param RtpSenderInterface $sender The sender instance to register.
     * @param int $ssrc The SSRC associated with this sender.
     */
    public function setSender(RtpSenderInterface $sender, int $ssrc): void
    {
        $this->senders[$ssrc] = $sender;
    }

    /**
     * Routes an RTCP packet to the appropriate recipients.
     *
     * @param RtcpPacketInterface $packet The RTCP packet to route.
     * @return array Array of RTCRtpSender and/or RTCRtpReceiver instances that should receive this packet.
     */
    public function routeRtcp(RtcpPacketInterface $packet): array
    {
        $recipients = [];

        $addRecipient = function (RtpReceiverInterface|RtpSenderInterface|null $recipient) use (&$recipients): void {
            if ($recipient !== null) {
                $recipients[] = $recipient;
            }
        };

        if ($packet instanceof RtcpSrPacket) {
            $addRecipient($this->ssrcTable[$packet->getSsrc()] ?? null);
        } elseif ($packet instanceof RtcpByePacket) {
            $sources = $packet->getSources();
            /** @var int[] $sources */
            foreach ($sources as $source) {
                $addRecipient($this->ssrcTable[$source] ?? null);
            }
        }

        if ($packet instanceof RtcpRrPacket || $packet instanceof RtcpSrPacket) {
            foreach ($packet->getReports() as $report) {
                $addRecipient($this->senders[$report->getSsrc()] ?? null);
            }
        } elseif ($packet instanceof RtcpPsfbPacket || $packet instanceof RtcpRtpfbPacket) {
            $addRecipient($this->senders[$packet->getMediaSsrc()] ?? null);

            if ($packet instanceof RtcpPsfbPacket && $packet->getFmt() === RtcpConstants::RTCP_PSFB_APP) {
                try {
                    [, $ssrcs] = RtpUtility::unpackRembFci($packet->getFci());
                    foreach ($ssrcs as $ssrc) {
                        $addRecipient($this->senders[$ssrc] ?? null);
                    }
                } catch (RtpExceptionInterface) {
                    // A malformed REMB FCI only costs us the extra SSRCs it lists; the packet's
                    // primary media SSRC was already added above, so intentionally keep routing
                    // the compound RTCP rather than dropping it over an optional feedback field.
                }
            }
        }
        return $recipients;
    }

    /**
     * Routes an RTP packet to the appropriate receiver.
     *
     * @param RtpPacket $packet The RTP packet to route.
     * @return RtpReceiverInterface|null The receiver for this packet, or null if no match found.
     */
    public function routeRtp(RtpPacket $packet): ?RtpReceiverInterface
    {
        $ssrcReceiver = $this->ssrcTable[$packet->getSsrc()] ?? null;
        $ptReceivers = $this->payloadTypeTable[$packet->getPayloadType()] ?? [];

        if ($ssrcReceiver !== null && in_array($ssrcReceiver, $ptReceivers, true)) {
            return $ssrcReceiver;
        }

        if ($ssrcReceiver === null && count($ptReceivers) === 1) {
            $ptReceiver = reset($ptReceivers);
            $this->ssrcTable[$packet->getSsrc()] = $ptReceiver;
            return $ptReceiver;
        }

        return null;
    }

    /**
     * Unregisters a receiver, removing all its associations.
     *
     * @param RtpReceiverInterface $receiver The receiver to unregister.
     */
    public function removeReceiver(RtpReceiverInterface $receiver): void
    {
        $this->receivers = array_filter($this->receivers, fn(RtpReceiverInterface $r) => $r !== $receiver);
        $this->discard($this->midTable, $receiver);
        $this->discard($this->ssrcTable, $receiver);
        foreach ($this->payloadTypeTable as &$receivers) {
            $receivers = array_filter($receivers, fn(RtpReceiverInterface $r) => $r !== $receiver);
        }
    }

    /**
     * Unregisters a sender, removing all its associations.
     *
     * @param RtpSenderInterface $sender The sender to unregister.
     */
    public function removeSender(RtpSenderInterface $sender): void
    {
        $this->discard($this->senders, $sender);
    }

    /**
     * Removes all occurrences of a value from an associative array.
     *
     * @template TKey of array-key
     * @template T of RtpReceiverInterface|RtpSenderInterface
     * @param array<TKey, T> $array The array to modify.
     * @param T $value The value to remove.
     */
    private function discard(array &$array, RtpReceiverInterface|RtpSenderInterface $value): void
    {
        foreach ($array as $key => $val) {
            if ($val === $value) {
                unset($array[$key]);
            }
        }
    }

    /**
     * Gets the current SSRC to receiver mapping.
     *
     * @return array The SSRC table.
     */
    public function getSsrcTable(): array
    {
        return $this->ssrcTable;
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTP;

use Webrtc\Exception\InvalidArgumentException;

/**
 * Provides utility functions for RTP/RTCP packet processing.
 *
 * This class contains static helper methods for various RTP/RTCP operations including
 * - Packet loss calculation and clamping
 * - Header extension processing
 * - Audio level computation
 * - RTX packet wrapping/unwrapping
 * - REMB feedback message handling
 */
final class RtpUtility
{
    private const MAX_SAMPLE_VALUE = 32767;
    private const MAX_AUDIO_LEVEL = 0;
    private const MIN_AUDIO_LEVEL = -127;

    /**
     * Clamps the packets lost count to valid range.
     *
     * @param int $count The raw packets lost count.
     * @return int The clamped value between PACKETS_LOST_MIN and PACKETS_LOST_MAX.
     */
    public static function clampPacketsLost(int $count): int
    {
        return max(RtpConstants::PACKETS_LOST_MIN, min($count, RtpConstants::PACKETS_LOST_MAX));
    }

    /**
     * Packs the cumulative packets lost into a 24-bit binary string.
     *
     * @param int $packetsLost The cumulative number of packets lost.
     * @return string The 24-bit binary representation of the packets lost.
     */
    public static function packPacketsLost(int $packetsLost): string
    {
        return substr(pack("N", $packetsLost), 1);
    }

    /**
     * Unpacks the cumulative packets lost from a 24-bit binary string.
     *
     * @param string $data The 24-bit binary data.
     * @return int The cumulative number of packets lost.
     */
    public static function unpackPacketsLost(string $data): int
    {
        $data = (ord($data[0]) & 0x80) ? "\xFF" . $data : "\x00" . $data;
        $raw = unpack("N", $data);
        if ($raw === false) {
            throw new InvalidArgumentException("Failed to unpack packets lost value");
        }
        $value = (int) $raw[1];

        // Convert to signed 32-bit integer
        if ($value & 0x80000000) {
            $value -= 0x100000000;
        }

        return $value;
    }

    /**
     * Constructs the RTCP packet header and combines it with the payload.
     *
     * This helper method creates the RTCP packet header based on the provided parameters
     * and appends the payload to it.
     *
     * @param int $packetType The RTCP packet type (e.g., RTCP_SDES).
     * @param int $count The number of source description chunks.
     * @param string $payload The binary payload of the RTCP packet.
     * @return string The complete RTCP packet as a binary string.
     */
    public static function packRtcpPacket(int $packetType, int $count, string $payload): string
    {
        if (strlen($payload) % 4 !== 0) {
            throw new InvalidArgumentException("Payload length must be a multiple of 4");
        }
        return pack("CCn", (2 << 6) | $count, $packetType, strlen($payload) / 4) . $payload;
    }

    /**
     * @param int[] $ssrcs
     */
    public static function packRembFci(int $bitrate, array $ssrcs): string
    {
        $data = "REMB";
        $exponent = 0;

        if (abs($bitrate) >= abs(0x3FFFF << 63)) {
            $bitrate = 0x3FFFF;
            $exponent = 63;
        } else {
            while ($bitrate > 0x3FFFF) {
                $bitrate >>= 1;
                $exponent += 1;
            }
        }

        $data .= pack("CCn", count($ssrcs), ($exponent << 2) | ($bitrate >> 16), ($bitrate & 0xFFFF));

        foreach ($ssrcs as $ssrc) {
            $data .= pack("N", $ssrc);
        }

        return $data;
    }

    /**
     * @return array{0: int, 1: int[]}
     */
    public static function unpackRembFci(string $data): array
    {
        if (strlen($data) < 8 || !str_starts_with($data, "REMB")) {
            throw new InvalidArgumentException("Invalid REMB prefix");
        }

        $exponent = (ord($data[5]) & 0xFC) >> 2;
        $mantissa = ((ord($data[5]) & 0x03) << 16) | (ord($data[6]) << 8) | ord($data[7]);
        $bitrate = $mantissa << $exponent;

        $pos = 8;
        $ssrcs = [];
        for ($i = 0; $i < ord($data[4]); $i++) {
            $raw = unpack("N", substr($data, $pos, 4));
            if ($raw === false) {
                throw new InvalidArgumentException("Invalid REMB SSRC data");
            }
            $ssrcs[] = (int) $raw[1];
            $pos += 4;
        }

        return [$bitrate, $ssrcs];
    }

    public static function isRtcp(string $msg): bool
    {
        return strlen($msg) >= 2 && ord($msg[1]) >= 192 && ord($msg[1]) <= 208;
    }

    /**
     * Parse header extensions according to RFC 5285.
     *
     * @return array<int, array{0: int, 1: string}>
     */
    public static function unpackHeaderExtensions(int $extensionProfile, ?string $extensionValue): array
    {
        $extensions = [];
        if ($extensionValue === null) {
            return $extensions;
        }
        $pos = 0;
        $length = strlen($extensionValue);

        if ($extensionProfile === 0xBEDE) {
            // One-Byte Header
            while ($pos < $length) {
                if (ord($extensionValue[$pos]) === 0) {
                    $pos++;
                    continue;
                }
                $xId = (ord($extensionValue[$pos]) & 0xF0) >> 4;
                $xLength = (ord($extensionValue[$pos]) & 0x0F) + 1;
                $pos++;
                if ($length < $pos + $xLength) {
                    throw new InvalidArgumentException("RTP one-byte header extension value is truncated");
                }
                $xValue = substr($extensionValue, $pos, $xLength);
                $extensions[] = [$xId, $xValue];
                $pos += $xLength;
            }
        } elseif ($extensionProfile === 0x1000) {
            // Two-Byte Header
            while ($pos < $length) {
                if (ord($extensionValue[$pos]) === 0) {
                    $pos++;
                    continue;
                }
                if ($length < $pos + 2) {
                    throw new InvalidArgumentException("RTP two-byte header extension is truncated");
                }

                // Extract ID and length correctly
                $xId = ord($extensionValue[$pos]);        // First byte is the ID
                $xLength = ord($extensionValue[$pos + 1]); // The Second byte is the length
                $pos += 2;

                // Ensure valid length
                if ($xLength === 0) {
                    continue; // Ignore empty extensions
                }

                if ($length < $pos + $xLength) {
                    throw new InvalidArgumentException("RTP two-byte header extension value is truncated");
                }

                $xValue = substr($extensionValue, $pos, $xLength);
                $extensions[] = [$xId, $xValue];
                $pos += $xLength;
            }
        }

        return $extensions;
    }


    /**
     * Serialize header extensions according to RFC 5285.
     *
     * @param array<int, array{0: int, 1: string}> $extensions
     * @return array{0: int, 1: string}
     */
    public static function packHeaderExtensions(array $extensions): array
    {
        if (empty($extensions)) {
            return [0, ""];
        }
        $oneByte = true;
        foreach ($extensions as [$xId, $xValue]) {
            $xLength = strlen($xValue);
            if ($xId > 14 || $xLength === 0 || $xLength > 16) {
                $oneByte = false;
            }
        }
        $extensionProfile = $oneByte ? 0xBEDE : 0x1000;
        $extensionValue = "";

        foreach ($extensions as [$xId, $xValue]) {
            $xLength = strlen($xValue);
            if ($oneByte) {
                $extensionValue .= pack("C", ($xId << 4) | ($xLength - 1));
            } else {
                $extensionValue .= pack("CC", $xId, $xLength);
            }
            $extensionValue .= $xValue;
        }

        return [$extensionProfile, $extensionValue . str_repeat("\x00", (4 - (strlen($extensionValue) % 4)) % 4)];
    }

    /**
     * Compute the energy level as spelled out in RFC 6465, Appendix A.
     *
     * @param string $data
     * @param int $samples
     * @return int The computed audio level in dBov, rounded to the nearest integer.
     */
    public static function computeAudioLevelDbov(string $data, int $samples): int
    {
        $rms = 0.0;

        // Iterate through the buffer in 16-bit (2-byte) chunks
        for ($i = 0; $i < strlen($data); $i += 2) {
            // Unpack 16-bit signed little-endian sample
            $raw = unpack('v', substr($data, $i, 2));
            if ($raw === false) {
                throw new InvalidArgumentException("Invalid audio sample data");
            }
            $sample = (int) $raw[1];
            // Convert unsigned 16-bit to signed
            if ($sample >= 32768) {
                $sample -= 65536;
            }
            $rms += (float) ($sample * $sample); // Accumulate squared samples
        }

        // Calculate RMS (Root Mean Square)
        $rms = sqrt($rms / ((float) ($samples * self::MAX_SAMPLE_VALUE * self::MAX_SAMPLE_VALUE)));

        // Convert RMS to dBov
        if ($rms > 0) {
            $db = 20.0 * log10($rms);
            $db = max($db, self::MIN_AUDIO_LEVEL); // Clamp to minimum level
            $db = min($db, self::MAX_AUDIO_LEVEL); // Clamp to maximum level
        } else {
            $db = self::MIN_AUDIO_LEVEL; // If RMS is 0, return minimum level
        }

        return (int)round($db); // Round to the nearest integer
    }

    public static function unwrapRtx(RtpPacket $rtx, int $payloadType, int $ssrc): RtpPacket
    {
        $packet = new RtpPacket;
        $packet->setPayloadType($payloadType);
        $packet->setMarker($rtx->getMarker());
        $rtxSequence = unpack("n", substr($rtx->getPayload(), 0, 2));
        if ($rtxSequence === false) {
            throw new InvalidArgumentException("Invalid RTX payload for sequence number");
        }
        $packet->setSequenceNumber((int) $rtxSequence[1]);
        $packet->setTimestamp($rtx->getTimestamp());
        $packet->setSsrc($ssrc);
        $packet->setPayload(substr($rtx->getPayload(), 2));
        $packet->setCsrc($rtx->getCsrc());
        $packet->setExtensions($rtx->getExtensions());

        return $packet;
    }

    public static function wrapRtx(RtpPacket $packet, int $payloadType, int $sequenceNumber, int $ssrc): RtpPacket
    {
        $rtx = new RtpPacket;
        $rtx->setPayloadType($payloadType);
        $rtx->setSequenceNumber($sequenceNumber);
        $rtx->setSsrc($ssrc);
        $rtx->setMarker($packet->getMarker());
        $rtx->setCsrc($packet->getCsrc());
        $rtx->setTimestamp($packet->getTimestamp());
        $rtx->setExtensions($packet->getExtensions());
        $rtx->setPayload(pack("n", $packet->getSequenceNumber()) . $packet->getPayload());

        return $rtx;
    }
}
<?php

namespace Webrtc\RTP;

use Webrtc\RTP\Receiver\RtpReceiverInterface;
use Webrtc\RTP\Sender\RtpSenderInterface;
use Webrtc\RTPParameter\RTCRtpReceiveParameters;
use Webrtc\RTPParameter\RTCRtpSendParameters;
use Webrtc\Stats\enum\TLSState;
use Webrtc\Stats\RTCTransportStats;

interface RTCRTPDtlsTransportInterface
{
    public function getState(): TLSState;
    public function getReportTransport(): RTCTransportStats;
    public function setRtpReceiver(RtpReceiverInterface $receiver, RTCRtpReceiveParameters $parameters): void;
    public function removeRtpReceiver(RtpReceiverInterface $receiver): void;
    public function sendRtcp(string $data): void;
    public function setRtpSender(RtpSenderInterface $sender, RTCRtpSendParameters $parameters): void;
    public function removeRtpSender(RtpSenderInterface $sender): void;
    public function sendRtp(string $data): void;
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTP\Jitter;

use Webrtc\Codecs\JitterFrameInterface;
use Webrtc\Mixin\DataClass;

#[DataClass]
final class JitterFrame implements JitterFrameInterface
{
    /**
     * @param string $data Raw data to decode
     * @param int $timestamp The time of receiving data
     */
    public function __construct(
        private string $data,
        private int    $timestamp = 0
    )
    {
    }

    #[\Override]
    public function getData(): string
    {
        return $this->data;
    }

    #[\Override]
    public function getTimestamp(): int
    {
        return $this->timestamp;
    }

    public function setTimestamp(int $timestamp): void
    {
        $this->timestamp = $timestamp;
    }

    public function setData(string $data): void
    {
        $this->data = $data;
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTP\Jitter;

use Webrtc\Exception\InvalidArgumentException;
use Webrtc\RTP\RtpPacket;

/**
 * JitterBuffer buffers incoming RTP packets and reconstructs complete frames,
 * handling misordering and jitter in the packet stream. It is designed to be
 * used for both audio and video streams.
 *
 * The buffer has a fixed capacity and supports prefetching of frames to allow
 * smoother playout. Misordered packets beyond a defined threshold trigger a
 * buffer reset and (for video) may prompt a Picture Loss Indication (PLI).
 */
final class JitterBuffer
{
    /**
     * Maximum sequence number misordering tolerated before the buffer resets.
     */
    private const MAX_MISORDER = 100;

    private ?int $origin = null;

    /** @var array<int, RtpPacket|null> Ring buffer of RTP packets. */
    private array $packets = [];

    /**
     * JitterBuffer constructor.
     *
     * @param int $capacity Size of the buffer; must be a power of 2.
     * @param int $prefetch Number of complete frames to prefetch before outputting.
     * @param bool $isVideo Whether the buffer handles video (affects PLI triggering).
     *
     * @throws InvalidArgumentException If capacity is not a power of 2.
     */
    public function __construct(private readonly int $capacity, private readonly int $prefetch = 0, private readonly bool $isVideo = false)
    {
        if (($capacity & ($capacity - 1)) !== 0) {
            throw new InvalidArgumentException("Capacity must be a power of 2.");
        }

        $this->packets = array_fill(0, $capacity, null);
    }

    /**
     * Get the buffer capacity.
     *
     * @return int Buffer capacity.
     */
    public function getCapacity(): int
    {
        return $this->capacity;
    }

    /**
     * Add an RTP packet to the jitter buffer.
     *
     * Handles misordering, buffer overflows, and triggers PLI when necessary.
     * Returns a completed frame if a prefetch threshold is met.
     *
     * @param RtpPacket $packet The incoming RTP packet.
     * @return array{bool, JitterFrame|null} Tuple of (PLI flag, completed frame).
     */
    public function add(RtpPacket $packet): array
    {
        $pliFlag = false;
        $seqNum = $packet->getSequenceNumber();

        if ($this->origin === null) {
            $this->origin = $seqNum;
            $delta = $misorder = 0;
        } else {
            $delta = $this->uint16Add($seqNum, -$this->origin);
            $misorder = $this->uint16Add($this->origin, -$seqNum);
        }

        if ($misorder < $delta) {
            if ($misorder >= self::MAX_MISORDER) {
                $this->clearBuffer($seqNum);
                if ($this->isVideo) {
                    $pliFlag = true;
                }
            } else {
                return [$pliFlag, null];
            }
        }

        if ($delta >= $this->capacity) {
            if ($this->smartRemove($delta - $this->capacity + 1)) {
                $this->origin = $packet->getSequenceNumber();
            }

            if ($this->isVideo) {
                $pliFlag = true;
            }
        }

        $this->packets[$seqNum % $this->capacity] = $packet;
        return [$pliFlag, $this->extractFrame()];
    }

    /**
     * Extracts and removes a completed frame from the buffer if available.
     *
     * Waits until enough frames are prefetched before returning a frame.
     *
     * @return JitterFrame|null Completed frame if available.
     */
    private function extractFrame(): ?JitterFrame
    {
        $origin = $this->origin;
        if ($origin === null) {
            return null;
        }
        $packets = [];
        $frame = null;
        $framesCount = 0;
        $removeCount = 0;
        $timestamp = null;

        for ($count = 0; $count < $this->capacity; $count++) {
            $pos = ($origin + $count) % $this->capacity;
            $packet = $this->packets[$pos] ?? null;

            if ($packet === null) {
                break;
            }

            if ($timestamp === null) {
                $timestamp = $packet->getTimestamp();
            } elseif ($packet->getTimestamp() !== $timestamp) {
                if ($frame === null) {
                    $frame = new JitterFrame(
                        implode("", array_map(fn(RtpPacket $p) => $p->getDecodedData(), $packets)),
                        $timestamp
                    );
                    $removeCount = $count;
                }

                $framesCount++;
                if ($framesCount >= $this->prefetch) {
                    $this->remove($removeCount);
                    return $frame;
                }

                $packets = [];
                $timestamp = $packet->getTimestamp();
            }

            $packets[] = $packet;
        }

        return null;
    }

    /**
     * Extracts every frame still in the buffer without waiting for the prefetch, and empties it.
     *
     * Used once the stream ended, when no packet will arrive anymore to push them out. A video frame
     * is only extracted if it's complete: up to a packet with the marker bit, without gaps.
     *
     * @return list<JitterFrame>
     */
    public function flush(): array
    {
        $origin = $this->origin;
        if ($origin === null) {
            return [];
        }
        $frames = [];
        /** @var list<RtpPacket> $packets */
        $packets = [];
        $timestamp = null;
        for ($count = 0; $count < $this->capacity; $count++) {
            $packet = $this->packets[($origin + $count) % $this->capacity] ?? null;
            if ($packet === null) {
                if ($this->isVideo) {
                    // The frame being put together lost a packet, and the frames after it can't be told apart.
                    $packets = [];
                    break;
                }
                // Each audio packet is a frame of its own.
                continue;
            }
            if ($timestamp !== null && $packet->getTimestamp() !== $timestamp && $packets !== []) {
                $frames[] = $this->makeFrame($packets, $timestamp);
                $packets = [];
            }
            $timestamp = $packet->getTimestamp();
            $packets[] = $packet;
        }
        if ($packets !== [] && $timestamp !== null && (!$this->isVideo || end($packets)->getMarker())) {
            $frames[] = $this->makeFrame($packets, $timestamp);
        }
        $this->packets = array_fill(0, $this->capacity, null);
        $this->origin = null;

        return $frames;
    }

    /**
     * @param list<RtpPacket> $packets The packets of a frame, in order.
     */
    private function makeFrame(array $packets, int $timestamp): JitterFrame
    {
        return new JitterFrame(implode("", array_map(fn(RtpPacket $p) => $p->getDecodedData(), $packets)), $timestamp);
    }

    /**
     * Removes a specific number of packets from the buffer.
     *
     * @param int $count Number of packets to remove.
     *
     * @throws InvalidArgumentException If count exceeds buffer capacity.
     */
    public function remove(int $count): void
    {
        if ($count > $this->capacity) {
            throw new InvalidArgumentException("Count cannot exceed buffer capacity.");
        }

        if ($this->origin === null) {
            return;
        }

        for ($i = 0; $i < $count; $i++) {
            $pos = $this->origin % $this->capacity;
            $this->packets[$pos] = null;
            $this->origin = $this->uint16Add($this->origin, 1);
        }
    }

    /**
     * Attempts to remove old packets while preserving complete frames.
     *
     * Removes up to a given count but keeps timestamp continuity.
     *
     * @param int $count Number of packets to remove.
     * @return bool True if the entire buffer was cleared.
     */
    public function smartRemove(int $count): bool
    {
        if ($this->origin === null) {
            return false;
        }
        $timestamp = null;

        for ($i = 0; $i < $this->capacity; $i++) {
            $pos = $this->origin % $this->capacity;
            $packet = $this->packets[$pos] ?? null;

            if ($packet !== null) {
                if ($i >= $count && $timestamp !== $packet->getTimestamp()) {
                    break;
                }
                $timestamp = $packet->getTimestamp();
            }

            $this->packets[$pos] = null;
            $this->origin = $this->uint16Add($this->origin, 1);

            if ($i === $this->capacity - 1) {
                return true;
            }
        }

        return false;
    }

    /**
     * Clears the entire buffer and resets the origin to a new sequence number.
     *
     * @param int $newOrigin New origin sequence number.
     */
    private function clearBuffer(int $newOrigin): void
    {
        $this->packets = array_fill(0, $this->capacity, null);
        $this->origin = $newOrigin;
    }

    /**
     * Adds two 16-bit sequence numbers, wrapping around as needed.
     *
     * @param int $a Base value.
     * @param int $b Value to add (maybe negative).
     * @return int Result wrapped within the 16-bit unsigned range.
     */
    private function uint16Add(int $a, int $b): int
    {
        return ($a + $b) & 0xFFFF;
    }

    /**
     * Get the current sequence number origin of the buffer.
     *
     * @return int|null Origin sequence number or null if unset.
     */
    public function getOrigin(): ?int
    {
        return $this->origin;
    }

    /**
     * Get all packets currently in the jitter buffer.
     *
     * @return array<int, RtpPacket|null> Array of packets indexed by position.
     */
    public function getPackets(): array
    {
        return $this->packets;
    }
}
{
    "name": "danog/php-rtc-rtp",
    "type": "library",
    "description": "RTP packet and media transport handling for PHP, ported to Amp v3. MadelineProto's PHP 8.2+ fork supports optional media FFI in the modular danog/php-rtc stack.",
    "keywords": [
        "webrtc",
        "rtp",
        "real-time-transport-protocol"
    ],
    "homepage": "https://github.com/danog/php-rtc-rtp",
    "license": "BSD-3-Clause",
    "authors": [
        {
            "name": "Amin Yazdanpanah",
            "email": "github@aminyazdanpanah.com"
        },
        {
            "name": "Sana Moniri",
            "email": "s.mo@quasarstream.com"
        },
        {
            "name": "Quasar Stream Community"
        },
        {
            "name": "Daniil Gentili",
            "email": "daniil@daniil.it"
        }
    ],
    "require": {
        "php-64bit": "^8.2",
        "amphp/amp": "^3.1.3",
        "danog/php-rtc-exception": "^1.1.5",
        "danog/php-rtc-codecs": "^1.1.18",
        "danog/php-rtc-rtp-parameter": "^1.1.4",
        "danog/php-rtc-rtcp": "^1.1.4",
        "danog/php-rtc-stats": "^1.1.3",
        "danog/php-rtc-ntp": "^1.1.14",
        "danog/php-rtc-sdp": "^1.1.6",
        "danog/php-rtc-mixin": "^2.0",
        "ramsey/uuid": "^4.9.3",
        "amphp/pipeline": "^1.2.7",
        "symfony/polyfill-php83": "*"
    },
    "require-dev": {
        "phpunit/phpunit": "^11.5 || ^12.0 || ^13.3.2",
        "symfony/phpunit-bridge": "7.3.x-dev",
        "dg/bypass-finals": "dev-master",
        "bamarni/composer-bin-plugin": "^1.9.1"
    },
    "autoload": {
        "psr-4": {
            "Webrtc\\RTP\\": "src"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\Webrtc\\RTP\\": "tests"
        }
    },
    "minimum-stability": "stable",
    "scripts": {
        "post-install-cmd": [
            "@composer bin all install --ansi"
        ],
        "post-update-cmd": [
            "@composer bin all update --ansi"
        ]
    },
    "config": {
        "allow-plugins": {
            "bamarni/composer-bin-plugin": true
        }
    },
    "extra": {
        "bamarni-bin": {
            "bin-links": true,
            "forward-command": true
        }
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package, vendored and modified for MadelineProto.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\DTLS\DTLS;

/**
 * The TLS 1.2 pseudo-random function and the key schedule built on top of it.
 *
 * DTLS 1.2 shares its key schedule with TLS 1.2 (RFC 5246 section 5), so this covers the master
 * secret, the key block, the Finished verify data and the RFC 5705 exporter used to key SRTP.
 */
final class Prf
{
    /** Length of the master secret, in bytes. */
    public const MASTER_SECRET_LENGTH = 48;
    /** Length of the verify_data carried by a Finished message. */
    public const VERIFY_DATA_LENGTH = 12;

    /**
     * P_hash as defined in RFC 5246 section 5, using SHA-256.
     */
    public static function pHash(string $secret, string $seed, int $length, string $algo = 'sha256'): string
    {
        $result = '';
        $a = $seed;
        while (\strlen($result) < $length) {
            $a = hash_hmac($algo, $a, $secret, true);
            $result .= hash_hmac($algo, $a.$seed, $secret, true);
        }
        return substr($result, 0, $length);
    }

    /**
     * PRF(secret, label, seed) as defined in RFC 5246 section 5.
     */
    public static function prf(string $secret, string $label, string $seed, int $length, string $algo = 'sha256'): string
    {
        return self::pHash($secret, $label.$seed, $length, $algo);
    }

    /**
     * Derive the master secret from the pre-master secret and the two hello randoms.
     */
    public static function masterSecret(string $preMasterSecret, string $clientRandom, string $serverRandom): string
    {
        return self::prf(
            $preMasterSecret,
            'master secret',
            $clientRandom.$serverRandom,
            self::MASTER_SECRET_LENGTH
        );
    }

    /**
     * Derive the extended master secret of RFC 7627, which binds the handshake transcript.
     */
    public static function extendedMasterSecret(string $preMasterSecret, string $handshakeHash): string
    {
        return self::prf(
            $preMasterSecret,
            'extended master secret',
            $handshakeHash,
            self::MASTER_SECRET_LENGTH
        );
    }

    /**
     * Expand the master secret into the per-direction keys of an AEAD cipher suite.
     *
     * @return array{clientKey: string, serverKey: string, clientSalt: string, serverSalt: string}
     */
    public static function keyBlock(
        string $masterSecret,
        string $clientRandom,
        string $serverRandom,
        int $keyLength,
        int $saltLength
    ): array {
        // Note the inverted random order compared to the master secret computation.
        $block = self::prf(
            $masterSecret,
            'key expansion',
            $serverRandom.$clientRandom,
            2 * ($keyLength + $saltLength)
        );
        $offset = 0;
        $clientKey = substr($block, $offset, $keyLength);
        $offset += $keyLength;
        $serverKey = substr($block, $offset, $keyLength);
        $offset += $keyLength;
        $clientSalt = substr($block, $offset, $saltLength);
        $offset += $saltLength;
        $serverSalt = substr($block, $offset, $saltLength);

        return [
            'clientKey' => $clientKey,
            'serverKey' => $serverKey,
            'clientSalt' => $clientSalt,
            'serverSalt' => $serverSalt,
        ];
    }

    /**
     * Compute the verify_data of a Finished message.
     *
     * @param bool $client Whether the Finished is the one sent by the client.
     */
    public static function verifyData(string $masterSecret, string $handshakeHash, bool $client): string
    {
        return self::prf(
            $masterSecret,
            $client ? 'client finished' : 'server finished',
            $handshakeHash,
            self::VERIFY_DATA_LENGTH
        );
    }

    /**
     * Export keying material as defined in RFC 5705, used to key SRTP (RFC 5764).
     */
    public static function exportKeyingMaterial(
        string $masterSecret,
        string $label,
        string $clientRandom,
        string $serverRandom,
        int $length,
        ?string $context = null
    ): string {
        $seed = $clientRandom.$serverRandom;
        if ($context !== null) {
            $seed .= pack('n', \strlen($context)).$context;
        }
        return self::prf($masterSecret, $label, $seed, $length);
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package, vendored and modified for MadelineProto.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\DTLS\DTLS;

use Webrtc\DTLS\Exception\SSLException;

/**
 * A cursor over a handshake message body.
 *
 * TLS structures are full of length-prefixed vectors; this keeps the parsing code readable and
 * makes every read bounds-checked, so a malformed message raises a clean error instead of
 * silently producing garbage.
 */
final class Reader
{
    private int $offset = 0;

    public function __construct(private readonly string $buffer)
    {
    }

    /**
     * @throws SSLException If fewer than `$length` bytes remain.
     */
    public function take(int $length): string
    {
        if ($this->offset + $length > \strlen($this->buffer)) {
            throw new SSLException('Truncated DTLS handshake message!');
        }
        $slice = substr($this->buffer, $this->offset, $length);
        $this->offset += $length;
        return $slice;
    }

    /**
     * @throws SSLException If fewer than `$length` bytes remain.
     */
    public function skip(int $length): void
    {
        $this->take($length);
    }

    /**
     * Read a vector with a one byte length prefix.
     *
     * @throws SSLException If the vector is truncated.
     */
    public function takeVector8(): string
    {
        return $this->take(\ord($this->take(1)));
    }

    /**
     * Read a vector with a two byte length prefix.
     *
     * @throws SSLException If the vector is truncated.
     */
    public function takeVector16(): string
    {
        return $this->take(Handshake::uint16($this->take(2)));
    }

    /**
     * Everything that has not been consumed yet.
     */
    public function rest(): string
    {
        return substr($this->buffer, $this->offset);
    }

    public function remaining(): int
    {
        return \strlen($this->buffer) - $this->offset;
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package, vendored and modified for MadelineProto.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\DTLS\DTLS;

use Webrtc\DTLS\Exception\SSLException;
use Webrtc\Srtp\Aes;
use Throwable;

/**
 * The DTLS 1.2 record layer (RFC 6347 section 4.1).
 *
 * A DTLS record adds an explicit 16-bit epoch and a 48-bit sequence number to the TLS record
 * header, because datagrams may be lost or reordered. Records are protected with an AEAD cipher
 * (RFC 5288), whose nonce is the four byte implicit salt from the key block followed by an eight
 * byte explicit part carried in the record itself.
 */
final class RecordLayer
{
    public const TYPE_CHANGE_CIPHER_SPEC = 20;
    public const TYPE_ALERT = 21;
    public const TYPE_HANDSHAKE = 22;
    public const TYPE_APPLICATION_DATA = 23;

    /** DTLS 1.2 is encoded as {254, 253}, i.e. the ones' complement of 1.2. */
    public const VERSION_1_2 = "\xFE\xFD";
    /** DTLS 1.0 is used in the first ClientHello for backwards compatibility. */
    public const VERSION_1_0 = "\xFE\xFF";

    private const HEADER_LENGTH = 13;
    private const EXPLICIT_NONCE_LENGTH = 8;
    private const TAG_LENGTH = 16;

    /** Outgoing epoch, incremented by each ChangeCipherSpec we send. */
    private int $writeEpoch = 0;
    /**
     * Next outgoing record sequence number, per epoch.
     *
     * Kept per epoch rather than as a single counter because a retransmitted flight has to be
     * re-encoded at the epoch it was originally sent in, while still taking a fresh sequence
     * number: peers drop a replayed number as a duplicate.
     *
     * @var array<int, int>
     */
    private array $writeSequences = [0 => 0];
    /** Incoming epoch, incremented by each ChangeCipherSpec we receive. */
    private int $readEpoch = 0;

    private ?string $writeKey = null;
    private ?string $writeSalt = null;
    private ?string $readKey = null;
    private ?string $readSalt = null;

    /** Highest sequence number accepted per epoch, for replay detection.
     *
     * @var array<int, array<int, true>>
     */
    private array $readSequences = [];

    /**
     * Mask applied to the record sequence to derive the explicit AEAD nonce.
     *
     * The nonce only has to be unique per key, and peers are free to choose it however they like:
     * OpenSSL picks a random one. Deriving ours from the sequence keeps it collision free while
     * still making it *different* from the sequence, so that any code confusing the two (the AEAD
     * additional data must use the sequence, never the nonce) fails loudly instead of silently
     * interoperating with itself.
     */
    private readonly string $explicitNonceMask;

    public function __construct()
    {
        $this->explicitNonceMask = random_bytes(8);
    }

    /**
     * Install the keys produced by the handshake, without activating them yet.
     */
    public function setKeys(string $writeKey, string $writeSalt, string $readKey, string $readSalt): void
    {
        $this->writeKey = $writeKey;
        $this->writeSalt = $writeSalt;
        $this->readKey = $readKey;
        $this->readSalt = $readSalt;
    }

    /**
     * Start protecting outgoing records, i.e. handle our own ChangeCipherSpec.
     */
    public function activateWrite(): void
    {
        $this->writeEpoch++;
        $this->writeSequences[$this->writeEpoch] ??= 0;
    }

    /**
     * Start expecting protected incoming records, i.e. handle the peer's ChangeCipherSpec.
     */
    public function activateRead(): void
    {
        $this->readEpoch++;
    }

    public function getWriteEpoch(): int
    {
        return $this->writeEpoch;
    }

    public function getReadEpoch(): int
    {
        return $this->readEpoch;
    }

    /**
     * Serialize one record, protecting it if the write epoch is already encrypted.
     */
    public function encode(int $type, string $payload, ?string $version = null, ?int $epoch = null): string
    {
        $version ??= self::VERSION_1_2;
        // A retransmission names the epoch its flight belongs to; everything else uses the
        // current one. Encrypted records are keyed per epoch, so re-encoding a pre-ChangeCipherSpec
        // record at the new epoch would make it undecryptable to the peer.
        $epoch ??= $this->writeEpoch;
        $sequence = $this->writeSequences[$epoch] ?? 0;
        $this->writeSequences[$epoch] = $sequence + 1;

        if ($epoch === 0 || $this->writeKey === null) {
            return \chr($type).$version.self::sequence($epoch, $sequence)
                .pack('n', \strlen($payload)).$payload;
        }

        // The explicit nonce only has to be unique per key; peers are free to use a random one,
        // so it must never be confused with the record's sequence number.
        $explicit = self::sequence($epoch, $sequence) ^ $this->explicitNonceMask;
        \assert($this->writeSalt !== null);
        $nonce = $this->writeSalt.$explicit;
        // RFC 5246 section 6.2.3.3: the additional data is built from the record's own epoch and
        // sequence number, not from the explicit nonce carried in the payload.
        $aad = self::sequence($epoch, $sequence).\chr($type).$version.pack('n', \strlen($payload));

        $ciphertext = Aes::gcmEncrypt($this->writeKey, $nonce, $aad, $payload);

        $body = $explicit.$ciphertext;
        return \chr($type).$version.self::sequence($epoch, $sequence)
            .pack('n', \strlen($body)).$body;
    }

    /**
     * Split a datagram into records and decrypt the ones belonging to the current read epoch.
     *
     * Records from an unexpected epoch, replays and records that fail authentication are dropped
     * rather than reported, as required by RFC 6347 section 4.1.2.1.
     *
     * @return list<array{type: int, payload: string, epoch: int}>
     */
    public function decode(string $datagram): array
    {
        $records = [];
        $offset = 0;
        $total = \strlen($datagram);

        while ($offset + self::HEADER_LENGTH <= $total) {
            $type = \ord($datagram[$offset]);
            $version = substr($datagram, $offset + 1, 2);
            $epoch = Handshake::uint16(substr($datagram, $offset + 3, 2));
            $sequence = self::parseSequence(substr($datagram, $offset + 5, 6));
            $length = Handshake::uint16(substr($datagram, $offset + 11, 2));
            $offset += self::HEADER_LENGTH;

            if ($offset + $length > $total) {
                // Truncated record: the rest of the datagram is unusable.
                break;
            }
            $body = substr($datagram, $offset, $length);
            $offset += $length;

            if ($epoch !== $this->readEpoch) {
                // Either a retransmission from a previous epoch or an early record from the next
                // one; both are safe to ignore, the peer will retransmit if it matters.
                continue;
            }
            if (isset($this->readSequences[$epoch][$sequence])) {
                continue;
            }
            $this->readSequences[$epoch][$sequence] = true;

            if ($epoch === 0 || $this->readKey === null) {
                $records[] = ['type' => $type, 'payload' => $body, 'epoch' => $epoch];
                if ($type === self::TYPE_CHANGE_CIPHER_SPEC) {
                    // The epoch has to advance right here: peers routinely put ChangeCipherSpec
                    // and the encrypted Finished in the *same* datagram, and the records that
                    // follow in this very buffer already belong to the new epoch.
                    $this->activateRead();
                }
                continue;
            }

            if ($length < self::EXPLICIT_NONCE_LENGTH + self::TAG_LENGTH) {
                continue;
            }
            $explicit = substr($body, 0, self::EXPLICIT_NONCE_LENGTH);
            $ciphertext = substr($body, self::EXPLICIT_NONCE_LENGTH, -self::TAG_LENGTH);
            $tag = substr($body, -self::TAG_LENGTH);

            $aad = self::sequence($epoch, $sequence).\chr($type).$version.pack('n', \strlen($ciphertext));

            \assert($this->readSalt !== null);
            try {
                $plaintext = Aes::gcmDecrypt($this->readKey, $this->readSalt.$explicit, $aad, $ciphertext, $tag);
            } catch (Throwable) {
                $plaintext = false;
            }
            if ($plaintext === false) {
                // A forged or corrupted record; silently discard it.
                continue;
            }

            $records[] = ['type' => $type, 'payload' => $plaintext, 'epoch' => $epoch];
        }

        return $records;
    }

    /**
     * Encode a 16-bit epoch followed by a 48-bit sequence number.
     */
    private static function sequence(int $epoch, int $sequence): string
    {
        return pack('n', $epoch).substr(pack('J', $sequence), 2);
    }

    /**
     * Decode a 48-bit sequence number.
     */
    private static function parseSequence(string $raw): int
    {
        $value = unpack('J', "\0\0".$raw);
        return $value === false ? 0 : (int) $value[1];
    }

    /**
     * @throws SSLException Always; used to report a fatal protocol violation.
     */
    public static function fail(string $message): never
    {
        throw new SSLException($message);
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package, vendored and modified for MadelineProto.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\DTLS\DTLS;

use OpenSSLAsymmetricKey;
use phpseclib4\Crypt\DH;
use phpseclib4\Crypt\EC;
use Webrtc\DTLS\Exception\SSLException;

/**
 * An EC private key: the certificate's, or an ephemeral ECDHE one.
 *
 * Backed by OpenSSL, or by phpseclib if the extension isn't loaded (or fails, e.g. without an
 * openssl.cnf on Windows): see {@see Crypto::useOpenssl()}.
 *
 * @internal
 */
final class EcPrivateKey
{
    /** The SubjectPublicKeyInfo of a P-256 point, up to the uncompressed point itself. */
    private const P256_SPKI_PREFIX = "3059301306072a8648ce3d020106082a8648ce3d030107034200";

    private function __construct(private readonly OpenSSLAsymmetricKey|EC\PrivateKey $key)
    {
    }

    /**
     * Generate a P-256 key.
     */
    public static function generateP256(): self
    {
        if (Crypto::useOpenssl()) {
            $key = openssl_pkey_new(['private_key_type' => OPENSSL_KEYTYPE_EC, 'curve_name' => 'prime256v1']);
            if ($key !== false) {
                return new self($key);
            }
        }
        return new self(EC::createKey('secp256r1'));
    }

    /**
     * Load a PEM-encoded EC private key.
     *
     * @throws SSLException If it isn't an EC private key.
     */
    public static function load(string $pem): self
    {
        if (Crypto::useOpenssl()) {
            $key = openssl_pkey_get_private($pem);
            if ($key !== false) {
                if ((openssl_pkey_get_details($key)['type'] ?? null) !== OPENSSL_KEYTYPE_EC) {
                    throw new SSLException('The provided key is not an EC private key!');
                }
                return new self($key);
            }
        }
        $key = EC::loadPrivateKey($pem);
        if (!$key instanceof EC\PrivateKey) {
            throw new SSLException('The provided key is not an EC private key!');
        }
        return new self($key);
    }

    /**
     * The public point, uncompressed (0x04 || x || y).
     */
    public function getPoint(): string
    {
        if ($this->key instanceof EC\PrivateKey) {
            /** @var string */
            return $this->key->getPublicKey()->getEncodedCoordinates();
        }
        $details = openssl_pkey_get_details($this->key);
        if ($details === false || !isset($details['ec']['x'], $details['ec']['y'], $details['bits'])) {
            throw new SSLException('Could not get the EC public key!');
        }
        $length = intdiv((int) $details['bits'] + 7, 8);
        return "\x04".str_pad((string) $details['ec']['x'], $length, "\0", STR_PAD_LEFT)
            .str_pad((string) $details['ec']['y'], $length, "\0", STR_PAD_LEFT);
    }

    /**
     * The ECDH shared secret (the x coordinate) with an uncompressed P-256 point.
     *
     * @throws SSLException If the point is invalid.
     */
    public function computeSharedSecret(string $peerPoint): string
    {
        $spki = (string) hex2bin(self::P256_SPKI_PREFIX).$peerPoint;
        if ($this->key instanceof EC\PrivateKey) {
            $peerKey = EC::loadPublicKey($spki);
            \assert($peerKey instanceof EC\PublicKey);
            /** @var string */
            return DH::computeSecret($this->key, $peerKey->getEncodedCoordinates());
        }
        // Loading the point checks that it is on the curve.
        $peerKey = openssl_pkey_get_public(Crypto::pem('PUBLIC KEY', $spki));
        $secret = $peerKey === false ? false : openssl_pkey_derive($peerKey, $this->key);
        if ($secret === false) {
            throw new SSLException('Invalid ECDHE public key!');
        }
        return $secret;
    }

    /**
     * Sign with ECDSA and SHA-256, returning the DER-encoded signature.
     */
    public function sign(string $data): string
    {
        if ($this->key instanceof EC\PrivateKey) {
            /** @var string */
            return $this->key->withSignatureFormat('ASN1')->withHash('sha256')->sign($data);
        }
        if (!openssl_sign($data, $signature, $this->key, OPENSSL_ALGO_SHA256)) {
            throw new SSLException('Could not sign: '.Crypto::opensslError());
        }
        /** @var string $signature */
        return $signature;
    }

    /**
     * The key in PEM form.
     */
    public function toPem(): string
    {
        if ($this->key instanceof EC\PrivateKey) {
            return $this->key->toString('PKCS8');
        }
        if (!openssl_pkey_export($this->key, $pem)) {
            throw new SSLException('Could not export the private key: '.Crypto::opensslError());
        }
        /** @var string $pem */
        return $pem;
    }

    /**
     * The phpseclib key.
     */
    public function toPhpseclib(): EC\PrivateKey
    {
        if ($this->key instanceof EC\PrivateKey) {
            return $this->key;
        }
        $key = EC::loadPrivateKey($this->toPem());
        \assert($key instanceof EC\PrivateKey);
        return $key;
    }

    /**
     * The OpenSSL key, if backed by OpenSSL.
     */
    public function getOpenssl(): ?OpenSSLAsymmetricKey
    {
        return $this->key instanceof OpenSSLAsymmetricKey ? $this->key : null;
    }

    /**
     * OpenSSL keys can't be serialized: the key is serialized in PEM form, and loaded back with whichever
     * backend is available then.
     *
     * @return array{pem: string}
     */
    public function __serialize(): array
    {
        return ['pem' => $this->toPem()];
    }

    /**
     * @param array<string, mixed> $data The PEM, or the phpseclib key itself, as serialized before 2.0.19 (only
     *     with phpseclib: OpenSSL keys couldn't be serialized)
     *
     * @throws SSLException If it isn't a serialized key.
     */
    public function __unserialize(array $data): void
    {
        if (isset($data['pem']) && \is_string($data['pem'])) {
            $this->key = self::load($data['pem'])->key;
            return;
        }
        $key = $data["\0".self::class."\0key"] ?? null;
        if (!$key instanceof EC\PrivateKey) {
            throw new SSLException('Invalid serialized EC private key!');
        }
        // An unserialized key skips the initialization of the key formats of phpseclib, needed to export it.
        EC::getSupportedKeyFormats();
        $this->key = self::load($key->toString('PKCS8'))->key;
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\DTLS\DTLS\TLS;

use Psr\Log\LoggerInterface;
use Webrtc\DTLS\DTLS\Enum\SSLHandshakeState;
use Webrtc\DTLS\DTLS\Exception\TLSException;
use Webrtc\DTLS\DTLS\RTCCertificate;
use Webrtc\DTLS\DTLS\Srtp;
use Webrtc\ICE\RTCIceTransportInterface;
use Webrtc\SDP\DtlsParameter\RTCDtlsFingerprint;
use Webrtc\Srtp\Exception\SrtpException;
use Webrtc\DTLS\Enum\BioMethod;
use Webrtc\DTLS\Enum\ContextMethod;
use Webrtc\DTLS\Enum\Verify;
use Webrtc\DTLS\Exception\OpenSSLException;
use Webrtc\DTLS\Exception\SSLException;
use Webrtc\DTLS\Exception\SysCallException;
use Webrtc\DTLS\Exception\ZeroReturnException;
use Webrtc\DTLS\SSL\BIO;
use Webrtc\DTLS\SSL\BIOInterface;
use Webrtc\DTLS\SSL\Context;
use Webrtc\DTLS\SSL\SSL;
use Webrtc\DTLS\SSL\SSLInterface;
use Webrtc\Stats\enum\TLSState;

/**
 * Class TLS
 *
 * Manages DTLS/TLS connections for WebRTC, providing encryption, decryption, and handshake functionality.
 * This class encapsulates the OpenSSL operations needed for secure communication over WebRTC data channels.
 *
 * Key responsibilities:
 * - Managing the TLS/DTLS connection state
 * - Performing encryption and decryption of data
 * - Handling the DTLS handshake process
 * - Validating peer certificates
 * - Managing SRTP (Secure Real-time Transport Protocol) profiles
 * - Exporting keying material for SRTP
 *
 * @package Webrtc\DTLS\DTLS\TLS
 */
final class TLS
{
    /** @var array Supported cipher suites for the TLS connection */
    private const SUPPORTED_CIPHER_SUITES = [
        // AES-128-GCM-SHA256
        "ECDHE-ECDSA-AES128-GCM-SHA256",
        "ECDHE-RSA-AES128-GCM-SHA256",

        // AES-256-CBC-SHA
        "ECDHE-ECDSA-AES256-SHA",
        "ECDHE-RSA-AES256-SHA",

        // AES-256-GCM-SHA384
        "ECDHE-ECDSA-AES256-GCM-SHA384",
        "ECDHE-RSA-AES256-GCM-SHA384"
    ];

    /** @var SSLInterface The SSL connection instance */
    private SSLInterface $ssl;

    /** @var Context The SSL context */
    private Context $context;

    /** @var BIO The BIO buffer for SSL operations */
    private BIO $bio;

    /** @var TLSState Current state of the TLS connection */
    private TLSState $state = TLSState::NEW;

    /** @var LoggerInterface|null Optional logger for debugging */
    private ?LoggerInterface $logger = null;

    /**
     * TLS constructor.
     *
     * Initializes the TLS instance with the provided certificate and sets up the SSL context,
     * BIO buffer, and SSL connection.
     *
     * @param RTCCertificate $certificate The certificate to use for this TLS connection
     * @throws OpenSSLException If OpenSSL initialization fails
     * @throws SrtpException If SRTP profile setup fails
     */
    public function __construct(private readonly RTCCertificate $certificate)
    {
        $this->context = $this->createContext();
        $this->bio = $this->createBIO();
        $this->ssl = $this->createSSL();
    }

    /**
     * Starts the DTLS handshake process.
     *
     * Creates a new Handshake instance and initiates the handshake process with the given transport.
     * The handshake can be either as a client (Connect) or server (Accept).
     *
     * @param RTCIceTransportInterface $transport The ICE transport to use for communication
     * @param SSLHandshakeState $state Whether to initiate as client or server
     * @return void Returns once the handshake has completed.
     */
    public function startHandshaking(RTCIceTransportInterface $transport, SSLHandshakeState $state): void
    {
        $handshake = new Handshake($this, $transport, $state);
        $handshake->do();
    }

    /**
     * Encrypts data using the established TLS connection.
     *
     * @param string $data The plaintext data to encrypt
     * @return string The encrypted data
     * @throws OpenSSLException If an OpenSSL error occurs
     * @throws SysCallException If a system call fails
     * @throws TLSException If the TLS connection is not in the correct state
     * @throws ZeroReturnException If the connection was closed
     * @throws SSLException For general SSL errors
     */
    public function encrypt(string $data): string
    {
        $this->checkState();
        $this->ssl->write($data);
        return (string) $this->bio->read();
    }

    /**
     * Feeds one encrypted datagram into the connection and returns any decrypted application data.
     *
     * @param string $data The encrypted datagram to decrypt
     * @return string|null The decrypted application data, or null if the datagram carried none
     * @throws OpenSSLException If an OpenSSL error occurs
     * @throws SSLException For general SSL errors
     * @throws TLSException If the TLS connection is not in the correct state
     * @throws ZeroReturnException If the connection was closed
     */
    public function decrypt(string $data): ?string
    {
        $this->checkState();
        $this->bio->write($data);
        return $this->ssl->readApplicationData();
    }

    /**
     * Factory method to create a new TLS instance.
     *
     * @param RTCCertificate $certificate The certificate to use
     * @return static A new TLS instance
     * @throws OpenSSLException|SrtpException If initialization fails
     */
    public static function create(RTCCertificate $certificate): static
    {
        return new static($certificate);
    }

    /**
     * Validates the peer's certificate against provided fingerprints.
     *
     * @param RTCDtlsFingerprint[] $peerCerts Array of peer certificate fingerprints to validate against
     * @return bool True if the peer's certificate matches one of the provided fingerprints
     * @throws OpenSSLException If certificate validation fails
     * @throws TLSException If the TLS connection is not in the correct state
     */
    public function validatePeerCertificate(array $peerCerts): bool
    {
        $this->checkState();
        $peerCertDigits = $this->ssl->getPeerCertificateDigest();

        return array_any($peerCerts, fn(RTCDtlsFingerprint $cert) => $cert->isAlgorithm("sha-256") && strtolower($cert->value) === strtolower($peerCertDigits ?? ''));
    }

    /**
     * Exports keying material for SRTP.
     *
     * @param int $keyLength Length of the key material needed
     * @param int $saltLent Length of the salt needed
     * @return string The exported keying material
     * @throws OpenSSLException If key export fails
     * @throws TLSException If the TLS connection is not in the correct state
     */
    public function exportKeyingMaterial(int $keyLength, int $saltLent): string
    {
        $this->checkState();
        return $this->ssl->exportKeyingMaterial("EXTRACTOR-dtls_srtp", 2 * ($keyLength + $saltLent));
    }

    /**
     * Gets the selected SRTP protection profile.
     *
     * @return string The name of the selected SRTP profile
     * @throws TLSException If the TLS connection is not in the correct state
     */
    public function getSelectedSrtpProfile(): string
    {
        $this->checkState();
        return $this->ssl->getSelectedSrtpProfile();
    }

    /**
     * Creates the SSL context for the connection.
     *
     * @return Context The created SSL context
     * @throws SrtpException If SRTP profile setup fails
     */
    private function createContext(): Context
    {
        $ctx = new Context(ContextMethod::DTLS_METHOD);
        $ctx->setVerify(Verify::PEER->value | Verify::FAIL_IF_NO_PEER_CERT->value, fn(mixed ...$args): bool => true);
        $ctx->setCipherList(implode(":", self::SUPPORTED_CIPHER_SUITES));
        $ctx->setTlsextUseSrtp(implode(":", array_map(static fn(array $profile): string => $profile["sslProfile"], Srtp::getProfiles())));
        $this->setCertAndPrivateKey($ctx);
        $this->setLoggerInfo($ctx);

        return $ctx;
    }

    /**
     * Creates the SSL connection instance.
     *
     * @return SSL The created SSL connection
     */
    private function createSSL(): SSL
    {
        return new SSL($this->context, $this->bio);
    }

    /**
     * Creates the BIO buffer for SSL operations.
     *
     * @return BIO The created BIO buffer
     */
    private function createBIO(): BIO
    {
        $method = BioMethod::s_mem;
        $bio = new BIO($method);

        return $bio;
    }

    /**
     * Shuts down the SSL connection gracefully.
     *
     * @return bool True if shutdown was successful
     * @throws OpenSSLException If an OpenSSL error occurs
     * @throws SSLException For general SSL errors
     * @throws SysCallException If a system call fails
     * @throws ZeroReturnException If the connection was closed
     */
    public function shutdown(): bool
    {
        if ($this->ssl->shutdown()) {
            $this->setState(TLSState::CLOSED);
            return true;
        }

        return false;
    }

    /**
     * Gets the SSL connection instance.
     *
     * @return SSLInterface The SSL connection
     */
    public function getSsl(): SSLInterface
    {
        return $this->ssl;
    }

    /**
     * Gets the BIO buffer instance.
     *
     * @return BIOInterface The BIO buffer
     */
    public function getBio(): BIOInterface
    {
        return $this->bio;
    }

    /**
     * Gets the current TLS connection state.
     *
     * @return TLSState The current state
     */
    public function getState(): TLSState
    {
        return $this->state;
    }

    /**
     * Sets the TLS connection state.
     *
     * @param TLSState $state The new state to set
     */
    public function setState(TLSState $state): void
    {
        $this->state = $state;
    }

    /**
     * Verifies that the TLS connection is in the correct state for operations.
     *
     * @throws TLSException If the connection is not in the CONNECTED state
     */
    private function checkState(): void
    {
        if ($this->state !== TLSState::CONNECTED) {
            throw new TLSException("TLS is not connected");
        }
    }

    /**
     * Configures the certificate and private key in the SSL context.
     *
     * @param Context $ctx The SSL context to configure
     */
    private function setCertAndPrivateKey(Context $ctx): void
    {
        $ctx->setCertificate($this->certificate);
    }

    /**
     * Configures logging for the SSL context if a logger is set.
     *
     * @param Context $ctx The SSL context to configure
     */
    private function setLoggerInfo(Context $ctx): void
    {
        // Currently commented out - would enable logging if logger is set
        if ($this->logger) {
            $ctx->setInfoCallBack($this->logger);
        }
    }

    /**
     * Sets the logger for this TLS instance.
     *
     * @param LoggerInterface|null $logger The logger instance or null to disable logging
     */
    public function setLogger(?LoggerInterface $logger): void
    {
        $this->logger = $logger;
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\DTLS\DTLS\TLS;

use Amp\DeferredFuture;
use Revolt\EventLoop;
use Throwable;
use Webrtc\DTLS\DTLS\Enum\SSLHandshakeState;
use Webrtc\DTLS\DTLS\Exception\HandshakeException;
use Webrtc\ICE\Listener\IceTransportDataListener;
use Webrtc\ICE\RTCIceTransportInterface;
use Webrtc\Mixin\SerializableState;
use Webrtc\DTLS\Exception\OpenSSLException;
use Webrtc\DTLS\SSL\BIOInterface;
use Webrtc\DTLS\SSL\SSLInterface;
use Webrtc\Stats\enum\TLSState;

/**
 * Class Handshake
 *
 * Manages the DTLS (Datagram Transport Layer Security) handshake process using an asynchronous event loop.
 * This class handles both client and server-side handshake operations, managing timeouts, BIO buffer operations,
 * and state transitions during the TLS handshake process.
 *
 * @package Webrtc\DTLS\DTLS\TLS
 */
final class Handshake implements IceTransportDataListener
{
    /** @var DeferredFuture Settled when the handshake finishes or fails */
    private DeferredFuture $deferred;

    /** @var SSLInterface The SSL context for the handshake */
    private SSLInterface $ssl;

    /** @var RTCIceTransportInterface The transport layer for sending/receiving data */
    private RTCIceTransportInterface $transport;

    /** @var BIOInterface The BIO buffer for SSL operations */
    private BIOInterface $bio;

    /** @var string|null Handle of the retransmission timer */
    private ?string $timer = null;

    /** @var bool Whether a handshake step is already queued on the event loop */
    private bool $advanceScheduled = false;

    /**
     * Handshake constructor.
     *
     * Initializes the handshake process with the given TLS context, transport, and initial state.
     *
     * @param TLS $tls The TLS context for this handshake
     * @param RTCIceTransportInterface $transport The transport layer for communication
     * @param SSLHandshakeState $state The initial handshake state (Accept or Connect)
     */
    public function __construct(private readonly TLS $tls, RTCIceTransportInterface $transport, private readonly SSLHandshakeState $state)
    {
        $this->deferred = new DeferredFuture();
        $this->transport = $transport;
        $this->ssl = $tls->getSsl();
        $this->bio = $tls->getBio();
        $this->setSSLHandshakeState();
        $this->transport->addDataListener($this);
    }

    /**
     * Receive application data from the ICE transport (was the 'data' event). Typed replacement
     * registered via addDataListener().
     */
    #[\Override]
    public function onIceTransportData(string $data, int $componentId): void
    {
        $this->receive($data);
    }

    /**
     * Receives data from the transport and writes it to the BIO buffer.
     *
     * This method is called when data is received on the transport layer. It cancels any pending
     * timeout timer and writes the received data to the BIO buffer for SSL processing.
     *
     * @param string $bytes The received data bytes
     * @throws OpenSSLException If writing to the BIO buffer fails
     */
    private function receive(string $bytes): void
    {
        if ($this->timer !== null) {
            EventLoop::cancel($this->timer);
            $this->timer = null;
        }

        $this->bio->write($bytes);
        $this->scheduleAdvance();
    }

    /**
     * Initiates the handshake process.
     *
     * Blocks until the handshake completes, or throws if it fails.
     *
     * @return void
     * @throws HandshakeException If the handshake could not be completed.
     */
    public function do(): void
    {
        $this->scheduleAdvance();
        $this->deferred->getFuture()->await();
    }

    /**
     * Sets the SSL handshake state based on the constructor parameter.
     *
     * Configures the SSL context to either accept (server mode) or connect (client mode).
     */
    private function setSSLHandshakeState(): void
    {
        if ($this->state === SSLHandshakeState::Accept) {
            $this->ssl->setAcceptState();
        } else {
            $this->ssl->setConnectState();
        }
    }

    /**
     * Queues a single handshake step on the event loop.
     *
     * The step is never run inline: {@see advance()} sends through the transport, and with the test
     * and real transports emitting synchronously that would re-enter the peer's receive() â€” and back
     * into ours â€” mid-step, corrupting the engine's state. Deferring to a loop microtask lets the
     * current call stack unwind first, so each side advances on its own turn. The flag coalesces the
     * several datagrams of a flight into one step.
     */
    private function scheduleAdvance(): void
    {
        if ($this->advanceScheduled || $this->deferred->isComplete()) {
            return;
        }
        $this->advanceScheduled = true;
        EventLoop::queue($this->runScheduledAdvance(...));
    }

    /**
     * Advance one handshake step.
     *
     * Only ever reached as the first-class callable `$this->runScheduledAdvance(...)` handed to
     * EventLoop::queue() â€” including when __unserialize() re-queues it. That callable captures
     * this method's private scope, so the event loop can invoke it without it being public.
     */
    private function runScheduledAdvance(): void
    {
            $this->advanceScheduled = false;
            $this->advance();
    }

    /**
     * Drives the handshake one step forward.
     *
     * Reached only through {@see scheduleAdvance()} â€” from the opening flight and from each arriving
     * datagram. A lost flight is recovered separately, by the retransmission timer re-sending it.
     * There is no polling: the only things that make progress are inbound data and an expired
     * deadline.
     */
    private function advance(): void
    {
        if ($this->deferred->isComplete()) {
            return;
        }
        try {
            if ($this->ssl->doHandshake()) {
                $this->cleanUp();
                $this->deferred->complete(true);
                return;
            }
            $this->sendBIOData();
        } catch (Throwable $e) {
            $this->teardown();
            $this->deferred->error(new HandshakeException("Handshake Failed", (int) $e->getCode(), $e));
            return;
        }
        // A deadline that has already elapsed is reported as 0.0, which a truthiness test
        // would discard and leave the flight unarmed.
        if ($this->timer === null && ($timeout = $this->ssl->dtlsV1GetTimeout()) !== null) {
            $this->handleDTLSTimeout($timeout);
        }
    }

    /**
     * Flushes every datagram the engine has queued out to the transport.
     *
     * A single flight is several datagrams, and the BIO hands them back one at a time, so this must
     * drain to empty: stopping after one would leave the rest of the flight sitting in the buffer,
     * and with nothing driving another send the peer would only ever see a partial flight until a
     * retransmission timer fired â€” turning every round trip into a one second stall.
     */
    private function sendBIOData(): void
    {
        while (($data = $this->bio->read()) !== null) {
            // A transport send failure means the flight never reached the peer. Let it propagate:
            // the callers (advance() and onDtlsTimeout()) fail the handshake with this as the cause,
            // rather than silently stalling until the retransmission timer gives up on a dead socket.
            $this->transport->send($data);
        }
    }

    /**
     * Handles DTLS timeout events.
     *
     * Sets up a timer to handle DTLS-specific timeout requirements. When the timer expires,
     * it processes the timeout and sends any pending BIO data.
     *
     * @param float $timeout The timeout duration in seconds
     */
    private function handleDTLSTimeout(float $timeout): void
    {
        $this->timer = EventLoop::delay($timeout, $this->onDtlsTimeout(...));
    }

    /**
     * Cleans up resources after handshake completion.
     *
     * Removes event listeners, updates the TLS state, sends any remaining BIO data,
     * and cancels all active timers.
     */
    private function cleanUp(): void
    {
        $this->teardown();
        $this->tls->setState(TLSState::CONNECTED);
        $this->sendBIOData();
    }

    /**
     * Detaches from the transport and cancels the retransmission timer.
     *
     * Runs on both outcomes: after a successful handshake and, without the CONNECTED transition,
     * when one fails â€” so neither the data listener nor a pending timer is left dangling.
     */
    private function teardown(): void
    {
        $this->removeMessageListener();
        if ($this->timer !== null) {
            EventLoop::cancel($this->timer);
            $this->timer = null;
        }
    }

    /**
     * Removes the data event listener from the transport.
     */
    private function removeMessageListener(): void
    {
        $this->transport->removeDataListener($this);
    }

    /**
     * Retransmit-timer callback.
     *
     * Armed only as the first-class callable `$this->onDtlsTimeout(...)` passed to
     * EventLoop::delay() (see handleDTLSTimeout()), including when __unserialize() reschedules
     * the timer. That callable keeps this method's private scope, so it stays private.
     */
    private function onDtlsTimeout(): void
    {
        $this->timer = null;
        // This runs as a bare EventLoop timer callback, so a retransmission/send failure here must
        // be routed into the handshake failure path (as advance() does) rather than escaping
        // uncaught into the event loop.
        try {
            $this->ssl->dtlsV1HandleTimeout();
            $this->sendBIOData();
        } catch (Throwable $e) {
            $this->teardown();
            if (!$this->deferred->isComplete()) {
                $this->deferred->error(new HandshakeException("Handshake Failed", (int) $e->getCode(), $e));
            }
            return;
        }
        if (($timeout = $this->ssl->dtlsV1GetTimeout()) !== null) {
            $this->handleDTLSTimeout($timeout);
        }
    }

    /**
     * @return array<string, mixed>
     */
    public function __serialize(): array
    {
        return SerializableState::export($this, [
            // deferred is non-nullable and holds a suspended fiber; leave it uninitialized on
            // import (SerializableState skips this marker) and __unserialize installs a fresh one.
            'deferred' => ['__uninitialized' => true],
            'timer' => $this->timer !== null,
        ]);
    }

    /**
     * @param array<string, mixed> $data
     */
    public function __unserialize(array $data): void
    {
        $restartTimer = false;
        /**
         * @var mixed $value
         */
        foreach ($data as $key => $value) {
            if (str_ends_with($key, "\0timer")) {
                $restartTimer = $value === true;
                $data[$key] = null;
            }
        }
        SerializableState::import($this, $data);
        $this->deferred = new DeferredFuture();
        $this->timer = null;
        if ($restartTimer) {
            $timeout = $this->ssl->dtlsV1GetTimeout();
            if ($timeout !== null) {
                $this->handleDTLSTimeout($timeout);
            }
        }
        if ($this->advanceScheduled) {
            $this->advanceScheduled = false;
            $this->scheduleAdvance();
        }
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\DTLS\DTLS;

use Psr\Log\LoggerInterface;
use Throwable;
use Webrtc\DataChannel\RTCSctpTransportInterface;
use Webrtc\DTLS\DTLS\Enum\SSLHandshakeState;
use Webrtc\DTLS\Listener\DtlsTransportStateChangeListener;
use Webrtc\DTLS\DTLS\Exception\DTLSException;
use Webrtc\DTLS\DTLS\Exception\TLSException;
use Webrtc\DTLS\DTLS\TLS\TLS;
use Webrtc\ICE\Enum\IceRole;
use Webrtc\ICE\Listener\IceTransportDataListener;
use Webrtc\ICE\Listener\IceTransportDisconnectListener;
use Webrtc\ICE\RTCIceTransportInterface;
use Webrtc\Mixin\SerializableState;
use Webrtc\NTP\NetworkTimeProtocol;
use Webrtc\RTCP\Exception\RtcpExceptionInterface;
use Webrtc\RTCP\RtcpPacket;
use Webrtc\RTP\Exception\RtpExceptionInterface;
use Webrtc\RTP\HeaderExtension\HeaderExtensionsMap;
use Webrtc\RTP\Receiver\RtpReceiverInterface;
use Webrtc\RTP\RTCRTPDtlsTransportInterface;
use Webrtc\RTP\RtpPacket;
use Webrtc\RTP\RtpRouter;
use Webrtc\RTP\RtpUtility;
use Webrtc\RTP\TransportSequenceNumbers;
use Webrtc\RTP\Sender\RTCRtpSender;
use Webrtc\RTP\Sender\RtpSenderInterface;
use Webrtc\RTPParameter\RTCRtpReceiveParameters;
use Webrtc\RTPParameter\RTCRtpSendParameters;
use Webrtc\SCTP\RTCSctpDtlsTransportInterface;
use Webrtc\SCTP\RTCSctpTransport;
use Webrtc\SDP\DtlsParameter\RTCDtlsFingerprint;
use Webrtc\SDP\DtlsParameter\RTCDtlsParameters;
use Webrtc\SDP\Enum\DtlsRole;
use Webrtc\Srtp\Exception\SrtpException;
use Webrtc\Srtp\Exception\SrtpExceptionInterface;
use Webrtc\Srtp\Exception\SrtpValidateException;
use Webrtc\Srtp\Session;
use Webrtc\DTLS\Exception\OpenSSLException;
use Webrtc\DTLS\Exception\SSLException;
use Webrtc\DTLS\Exception\SysCallException;
use Webrtc\DTLS\Exception\ZeroReturnException;
use Webrtc\Stats\enum\TLSState;
use Webrtc\Stats\RTCStatsReport;
use Webrtc\Stats\RTCTransportStats;

/**
 * Class RTCDtlsTransport
 *
 * Represents the DTLS transport layer for WebRTC communications.
 * This class manages the secure transport of media and data channels over WebRTC,
 * handling encryption/decryption, SRTP protection, and packet routing.
 *
 * Key responsibilities:
 * - Establishing and maintaining DTLS connections
 * - Managing SRTP sessions for media encryption
 * - Routing RTP/RTCP packets to appropriate receivers/senders
 * - Handling SCTP data channel traffic
 * - Providing transport statistics
 *
 * @package Webrtc\DTLS\DTLS
 */
final class RTCDtlsTransport implements RTCRTPDtlsTransportInterface, TransportSequenceNumbers, RTCSctpDtlsTransportInterface, IceTransportDataListener, IceTransportDisconnectListener
{
    /** @var TLSState Current state of the DTLS transport */
    private TLSState $state = TLSState::NEW;

    /** @var LoggerInterface|null Optional logger for debugging */
    private ?LoggerInterface $logger = null;

    /** @var RTCTransportStats Transport statistics collector */
    private RTCTransportStats $reportTransport;

    /** @var RtpRouter Routes RTP/RTCP packets to appropriate handlers */
    private RtpRouter $rtpRouter;

    /** @var HeaderExtensionsMap Manages RTP header extensions */
    private HeaderExtensionsMap $headerExtensionsMap;

    /** @var RTCSctpTransportInterface|null SCTP transport for data channels */
    private ?RTCSctpTransportInterface $sctpReceiver = null;

    /** @var Session|null Inbound SRTP session for decrypting incoming media */
    private ?Session $inboundSrtp = null;

    /** @var Session|null Outbound SRTP session for encrypting outgoing media */
    private ?Session $outboundSrtp = null;

    /** @var TLS The underlying TLS/DTLS implementation */
    private TLS $tls;

    /** @var DtlsRole The role (client/server) in the DTLS handshake */
    private DtlsRole $role = DtlsRole::Auto;

    /** @var \WeakMap<DtlsTransportStateChangeListener, null> Listeners notified when the state changes. */
    private \WeakMap $statechangeListeners;

    /** @var int The transport-wide sequence number of the last RTP packet sent, see {@see self::nextTransportSequenceNumber()}. */
    private int $transportSequenceNumber = 0;

    /**
     * RTCDtlsTransport constructor.
     *
     * @param RTCIceTransportInterface $transport The underlying ICE transport
     * @param RTCCertificate $certificate The certificate to use for DTLS
     * @throws OpenSSLException|SrtpException If initialization fails
     */
    public function __construct(private readonly RTCIceTransportInterface $transport, private readonly RTCCertificate $certificate)
    {
        /** @var \WeakMap<DtlsTransportStateChangeListener, null> */
        $this->statechangeListeners = new \WeakMap();
        $this->tls = TLS::create($this->certificate);
        $this->reportTransport = new RTCTransportStats("transport_" . spl_object_id($this));
        $this->rtpRouter = new RtpRouter();
        $this->headerExtensionsMap = new HeaderExtensionsMap();
        $transport->addDisconnectListener($this);
    }

    /**
     * Register a listener notified when the transport state changes.
     *
     * Typed replacement for on('statechange'); the listener is a plain object captured by serialization.
     */
    public function addStateChangeListener(DtlsTransportStateChangeListener $listener): void
    {
        $this->statechangeListeners[$listener] = null;
    }

    /**
     * Notify registered listeners that the transport state changed (was the "statechange" event).
     */
    private function notifyStateChange(): void
    {
        foreach ($this->statechangeListeners as $listener => $_) {
            $listener->onDtlsTransportStateChange();
        }
    }

    /**
     * Handle a disconnecting close/error reported by the ICE transport (was the 'close'/'error'
     * events). Typed replacement registered via addDisconnectListener().
     */
    #[\Override]
    public function onIceTransportDisconnect(): void
    {
        $this->handleDisconnectingError();
    }

    /**
     * Receive application data from the ICE transport (was the 'data' event). Typed replacement
     * registered via addDataListener().
     */
    #[\Override]
    public function onIceTransportData(string $data, int $componentId): void
    {
        $this->onReceivedData($data);
    }

    /**
     * Encrypts and sends application data over the DTLS connection.
     *
     * @param string $data The plaintext data to send
     * @throws SysCallException If a system call fails
     * @throws OpenSSLException If OpenSSL operations fail
     * @throws TLSException If TLS is not in the correct state
     * @throws ZeroReturnException If the connection was closed
     * @throws SSLException For general SSL errors
     */
#[\Override]
    public function sendData(string $data): void
    {
        $encryptedData = $this->tls->encrypt($data);
        if (strlen($encryptedData) > 0) {
            $this->send($encryptedData);
        }
    }

    /**
     * Encrypts and sends RTP media data.
     *
     * @param string $data The RTP packet to send
     * @throws SrtpException|DTLSException If encryption fails or connection isn't established
     */
#[\Override]
    public function sendRtp(string $data): void
    {
        if ($this->state !== TLSState::CONNECTED) {
            throw new DTLSException("Unable to send encrypted RTP: No connection established.");
        }
        if ($this->outboundSrtp === null) {
            throw new DTLSException("Unable to send encrypted RTP: outbound SRTP session is not initialized.");
        }

        $encryptedRtp = $this->outboundSrtp->protect($data);
        $this->send($encryptedRtp);
    }

    /**
     * Encrypts and sends RTCP control data.
     *
     * @param string $data The RTCP packet to send
     * @throws DTLSException|SrtpException If encryption fails or connection isn't established
     */
#[\Override]
    public function sendRtcp(string $data): void
    {
        if ($this->state !== TLSState::CONNECTED) {
            throw new DTLSException("Unable to send encrypted RTCP: No connection established.");
        }
        if ($this->outboundSrtp === null) {
            throw new DTLSException("Unable to send encrypted RTCP: outbound SRTP session is not initialized.");
        }

        $encryptedRtcp = $this->outboundSrtp->protectRtcp($data);
        $this->send($encryptedRtcp);
    }

    /**
     * Gets the underlying TLS implementation.
     *
     * @return TLS The TLS instance
     */
    public function getTls(): TLS
    {
        return $this->tls;
    }

    /**
     * Sets the inbound SRTP session.
     *
     * @param Session $inboundSrtp The SRTP session for decrypting incoming media
     */
    public function setInboundSrtp(Session $inboundSrtp): void
    {
        $this->inboundSrtp = $inboundSrtp;
    }

    /**
     * Sets the outbound SRTP session.
     *
     * @param Session $outboundSrtp The SRTP session for encrypting outgoing media
     */
    public function setOutboundSrtp(Session $outboundSrtp): void
    {
        $this->outboundSrtp = $outboundSrtp;
    }

    /**
     * Gets the local DTLS parameters including certificate fingerprints.
     *
     * @return RTCDtlsParameters The local DTLS parameters
     * @throws OpenSSLException
     */
    public function getLocalParameters(): RTCDtlsParameters
    {
        return new RTCDtlsParameters($this->certificate->getFingerprints());
    }

    /**
     * Sets the DTLS role (client/server/auto).
     *
     * @param DtlsRole $getRole The role to set
     */
    public function setRole(DtlsRole $getRole): void
    {
        $this->role = $getRole;
    }

    /**
     * Gets the current DTLS role.
     *
     * @return DtlsRole The current role
     */
    public function getRole(): DtlsRole
    {
        return $this->role;
    }

    /**
     * Sends any pending data from the BIO buffer.
     */
    private function sendBioData(): void
    {
        $bio = $this->tls->getBio();
        while (($data = $bio->read()) !== null) {
            $this->send($data);
        }
    }

    /**
     * Sends raw data through the underlying transport.
     *
     * @param string $data The data to send
     */
    public function send(string $data): void
    {
        $this->transport->send($data);
        $this->reportTransport->handleSent($data);
    }

    /**
     * Gets the transport statistics collector.
     *
     * @return RTCTransportStats The transport statistics
     */
#[\Override]
    public function getReportTransport(): RTCTransportStats
    {
        return $this->reportTransport;
    }

    /**
     * Removes an SCTP receiver (placeholder implementation).
     *
     * @param RTCSctpTransport $param The SCTP transport to remove
     */
#[\Override]
    public function removeSctpReceiver(RTCSctpTransport $param): void
    {
        // TODO: Implement proper removal logic
    }

    /**
     * Handles incoming data from the transport.
     *
     * @param string $data The received data
     * @throws SysCallException|DTLSException If a system call fails
     */
    private function onReceivedData(string $data): void
    {
        $this->reportTransport->handleReceived($data);

        $firstByte = ord($data[0]);
        if ($firstByte > 19 && $firstByte < 64) {
            $this->handleSctpData($data);
        } elseif ($firstByte > 127 && $firstByte < 192) {
            $this->handleSrtpData($data);
        }
    }

    /**
     * Starts the DTLS transport and begins the handshake process.
     *
     * @param RTCDtlsFingerprint[] $certificates Array of peer certificate fingerprints to validate
     * @throws DTLSException If handshake or setup fails
     * @throws OpenSSLException If OpenSSL operations fail
     * @throws TLSException If TLS is not in the correct state
     */
    public function start(array $certificates): void
    {
        $this->setState(TLSState::CONNECTING);

        // Start handshaking
        try {
            $this->tls->startHandshaking($this->transport, $this->getHandShakeState());
        } catch (Throwable $e) {
            $this->setFailedState("DTLS: " . $e->getMessage());
            return;
        }

        // Check peer certificate fingerprint
        if (!$this->tls->validatePeerCertificate($certificates)) {
            $this->setFailedState("DTLS: Fingerprint mismatch!");
            return;
        }

        // Setup inbound and outbound srtp
        try {
            $this->setupSrtp();
        } catch (SrtpExceptionInterface $e) {
            $this->setFailedState("DTLS: Failed setup srtp. " . $e->getMessage());
            return;
        }

        // Deliver post-handshake application data to onReceivedData() via onIceTransportData().
        $this->transport->addDataListener($this);

        // Set success log and state
        $this->logger?->debug("DTLS: Successful handshake");
        $this->setState(TLSState::CONNECTED);
    }

    /**
     * Stops the DTLS transport and cleans up resources.
     *
     * @return void Returns once shutdown is complete.
     */
    public function stop(): void
    {
        if (in_array($this->state, [TLSState::CONNECTING, TLSState::CONNECTED])) {
            try {
                $this->tls->shutdown(); // Attempt to shut down, regardless of success or failure.
                $this->sendBioData();
            } catch (\Throwable) {
                // TODO: it should try 3 times before giving up
            }
            $this->setState(TLSState::CLOSED);
            $this->logger?->debug("DTLS: DTLS shutdown process has been successfully completed. All secure connections have been terminated.");
        }
    }

    /**
     * Gets the underlying ICE transport.
     *
     * @return RTCIceTransportInterface The ICE transport
     */
#[\Override]
    public function getIceTransport(): RTCIceTransportInterface
    {
        return $this->transport;
    }

    /**
     * Gets the certificate used by this transport.
     *
     * @return RTCCertificate The certificate
     */
    public function getCertificate(): RTCCertificate
    {
        return $this->certificate;
    }

    /**
     * Gets the peer certificate fingerprints.
     *
     * @return array<RTCDtlsFingerprint> Array of certificate fingerprints
     * @throws OpenSSLException If fingerprint retrieval fails
     */
    public function getPeerCertificates(): array
    {
        return $this->certificate->getFingerprints();
    }

    /**
     * Gets the current transport state.
     *
     * @return TLSState The current state
     */
#[\Override]
    public function getState(): TLSState
    {
        return $this->state;
    }

    /**
     * Gets the logger instance.
     *
     * @return LoggerInterface|null The logger or null if not set
     */
    public function getLogger(): ?LoggerInterface
    {
        return $this->logger;
    }

    /**
     * Sets the logger instance.
     *
     * @param LoggerInterface|null $logger The logger to set
     */
    public function setLogger(?LoggerInterface $logger): void
    {
        $this->logger = $logger;
        $this->tls->setLogger($logger);
    }

    /**
     * Sets the SCTP transport for data channels.
     *
     * @param RTCSctpTransportInterface|null $sctpReceiver The SCTP transport to set
     */
#[\Override]
    public function setSctpReceiver(?RTCSctpTransportInterface $sctpReceiver = null): void
    {
        $this->sctpReceiver = $sctpReceiver;
    }

    /**
     * Sets up SRTP sessions for media encryption.
     *
     * @throws TLSException If TLS is not in the correct state
     * @throws SrtpException If SRTP setup fails
     * @throws DTLSException If connection isn't established
     * @throws OpenSSLException If OpenSSL operations fail
     * @throws SrtpValidateException If SRTP validation fails
     */
    public function setupSrtp(): void
    {
        $srtp = new Srtp();

        $selectedProfile = $srtp->getProfile($this->tls->getSelectedSrtpProfile());
        if ($selectedProfile === false) {
            throw new SrtpException('The DTLS handshake did not negotiate any usable SRTP profile!');
        }
        $srtpKeyMaterial = $this->tls->exportKeyingMaterial($selectedProfile["keyLength"], $selectedProfile["saltLent"]);

        // RFC 5764 section 4.2: the keying material is client_write_key | server_write_key | client_write_salt |
        // server_write_salt, where "client" and "server" are the DTLS handshake roles (which are
        // independent of the ICE role). The client protects what it sends with the client key, so we
        // decrypt what the peer sends with THEIR key and encrypt what we send with OURS.
        $isServer = $this->getHandShakeState() === SSLHandshakeState::Accept;

        $this->inboundSrtp = $srtp->getInbound($srtpKeyMaterial, intval(!$isServer));
        $this->outboundSrtp = $srtp->getOutbound($srtpKeyMaterial, intval($isServer));
    }

    /**
     * Sets the transport state and emits statechange event if changed.
     *
     * @param TLSState $state The new state
     */
    public function setState(TLSState $state): void
    {
        if ($state !== $this->state) {
            $this->logger?->debug(sprintf("State changed from %s to %s", $this->state->name, $state->name));
            $this->state = $state;
            $this->notifyStateChange();
        }
    }

    /**
     * Sets the transport to failed state with the given reason.
     *
     * @param string $reason The failure reason
     * @param Throwable|null $e Optional exception that caused the failure
     */
    private function setFailedState(string $reason, ?Throwable $e = null): void
    {
        $this->logger?->error("DTLS: Failed handshaking or srtp setup.", ["reason" => $reason]);
        $this->setState(TLSState::FAILED);
    }

    /**
     * Handles transport disconnection or errors.
     *
     * @throws DTLSException Always throws to indicate connection loss
     */
    private function handleDisconnectingError(): never
    {
        $this->setState(TLSState::CLOSED);
        $this->logger?->alert("DTLS: Connection lost");
        $this->sctpReceiver?->onErrorOrClosed();

        throw new DTLSException("DTLS: Connection lost");
    }

    /**
     * Handles incoming SCTP data channel messages.
     *
     * @param string $data The received SCTP data
     * @throws SysCallException If a system call fails
     */
    private function handleSctpData(string $data): void
    {
        try {
            $decryptedData = $this->tls->decrypt($data);
        } catch (ZeroReturnException) {
            $this->stop();
            return;
        } catch (TLSException|OpenSSLException) {
            // Carrying no application data does not mean the engine produced nothing: a peer
            // that never saw our final flight retransmits its own, and the answer to that is
            // queued right here. Returning without flushing left it sitting in the buffer,
            // so the peer retransmitted until it gave up and the handshake never finished.
            $this->sendBioData();

            return;
        }
        // Flush whatever the engine queued in response â€” an ack, or the answer to a retransmit.
        $this->sendBioData();

        if ($decryptedData !== null) {
            $this->sctpReceiver?->onReceived($decryptedData);
        }
    }

    /**
     * Handles incoming SRTP media packets.
     *
     * @param string $data The received SRTP data
     * @throws DTLSException If RTP processing fails
     */
    private function handleSrtpData(string $data): void
    {
        $arrivalTimeMs = NetworkTimeProtocol::currentMs();
        try {
            if ($this->inboundSrtp === null) {
                throw new DTLSException("Unable to process incoming SRTP: inbound SRTP session is not initialized.");
            }
            if (RtpUtility::isRtcp($data)) {
                $decryptedRtcp = $this->inboundSrtp->unprotectRtcp($data);
                $this->handleRtcpData($decryptedRtcp);
            } else {
                $decryptedRtp = $this->inboundSrtp->unprotect($data);
                $this->handleRtpData($decryptedRtp, $arrivalTimeMs);
            }
        } catch (SrtpExceptionInterface $e) {
            $this->logger?->debug(sprintf("DTLS: SRTP decryption failed: %s", $e->getMessage()));
        }
    }

    /**
     * Handles incoming RTCP control packets.
     *
     * @param string $data The RTCP packet data
     * @throws DTLSException If connection isn't established
     * @throws SrtpException If decryption fails
     */
    public function handleRtcpData(string $data): void
    {
        try {
            $packets = RtcpPacket::decode($data);
        } catch (RtcpExceptionInterface $e) {
            $this->logger?->debug(sprintf("DTLS: RTCP parsing failed: %s", $e->getMessage()));
            return;
        }

        foreach ($packets as $packet) {
            // Route RTCP packet
            /** @var array<int, RTCRtpSender|RtpReceiverInterface> $recipients */
            $recipients = $this->rtpRouter->routeRtcp($packet);
            foreach ($recipients as $recipient) {
                $recipient->handleRtcpPacket($packet);
            }
        }
    }

    /**
     * Handles incoming RTP media packets.
     *
     * @param string $data The RTP packet data
     * @param int $arrivalTimeMs The packet arrival time in milliseconds
     */
    public function handleRtpData(string $data, int $arrivalTimeMs): void
    {
        try {
            $packet = RtpPacket::decode($data, $this->headerExtensionsMap);
        } catch (RtpExceptionInterface $e) {
            $this->logger?->debug(sprintf("DTLS: RTP parsing failed: %s", $e->getMessage()));
            return;
        }

        // Route RTP packet
        $receiver = $this->rtpRouter->routeRtp($packet);
        $receiver?->handleRtpPacket($packet, $arrivalTimeMs);
    }

    /**
     * The transport-wide sequence number of the next RTP packet, shared by all of the senders of the transport.
     *
     * @return int A 16-bit number.
     */
    #[\Override]
    public function nextTransportSequenceNumber(): int
    {
        return $this->transportSequenceNumber = ($this->transportSequenceNumber + 1) & 0xFFFF;
    }

    /**
     * Gets the current transport statistics.
     *
     * @return RTCStatsReport The statistics report
     */
    public function getStats(): RTCStatsReport
    {
        $this->reportTransport->dateTime = new \DateTimeImmutable();

        $report = new RTCStatsReport();
        $report->add($this->reportTransport);

        return $report;
    }

    /**
     * Registers an RTP receiver with the transport.
     *
     * @param RtpReceiverInterface $receiver The RTP receiver to register
     * @param RTCRtpReceiveParameters $parameters The receiver parameters
     */
#[\Override]
    public function setRtpReceiver(RtpReceiverInterface $receiver, RTCRtpReceiveParameters $parameters): void
    {
        $ssrcs = [];
        foreach ($parameters->encodings as $encoding) {
            $ssrcs[] = $encoding->ssrc;
        }

        $this->headerExtensionsMap->configure($parameters);
        $payloadTypes = [];
        foreach ($parameters->codecs as $codec) {
            assert($codec->payloadType !== null);
            $payloadTypes[] = $codec->payloadType;
        }

        $this->rtpRouter->setReceiver(
            $receiver,
            $ssrcs,
            $payloadTypes,
            $parameters->muxId
        );
    }

    /**
     * Removes an RTP receiver from the transport.
     *
     * @param RtpReceiverInterface $receiver The receiver to remove
     */
#[\Override]
    public function removeRtpReceiver(RtpReceiverInterface $receiver): void
    {
        $this->rtpRouter->removeReceiver($receiver);
    }

    /**
     * Registers an RTP sender with the transport.
     *
     * @param RtpSenderInterface $sender The RTP sender to register
     * @param RTCRtpSendParameters $parameters The sender parameters
     */
#[\Override]
    public function setRtpSender(RtpSenderInterface $sender, RTCRtpSendParameters $parameters): void
    {
        $this->headerExtensionsMap->configure($parameters);

        $ssrc = null;
        foreach ($parameters->encodings as $encoding) {
            $ssrc = $encoding->ssrc;
            break;
        }
        if ($ssrc === null) {
            throw new DTLSException('No usable SSRC in the sender parameters!');
        }

        $this->rtpRouter->setSender($sender, $ssrc);
    }

    /**
     * Removes an RTP sender from the transport.
     *
     * @param RtpSenderInterface $sender The sender to remove
     */
#[\Override]
    public function removeRtpSender(RtpSenderInterface $sender): void
    {
        // A transport that never finished constructing (e.g. one restored from a half-built, killed
        // session) has no router yet; tearing its senders down during destruction must not crash.
        /** @psalm-suppress RedundantPropertyInitializationCheck the constructor can throw before this is set */
        if (!isset($this->rtpRouter)) {
            return;
        }
        $this->rtpRouter->removeSender($sender);
    }

    /**
     * Determines the handshake state based on the current role.
     *
     * @return SSLHandshakeState The appropriate handshake state
     */
    private function getHandShakeState(): SSLHandshakeState
    {
        if ($this->role === DtlsRole::Auto) {
            $this->role = $this->transport->getRole() === IceRole::Controlling ? DtlsRole::Server : DtlsRole::Client;
        }

        return $this->role === DtlsRole::Server ? SSLHandshakeState::Accept : SSLHandshakeState::Connect;
    }

    /**
     * @return array<string, mixed>
     */
    public function __serialize(): array
    {
        $state = SerializableState::export($this, [
            // WeakMaps cannot be serialized; snapshot their keys and rebuild on the far side.
            'statechangeListeners' => ['__uninitialized' => true],
        ]);
        $state['__statechangeListeners'] = SerializableState::weakMapToList($this->statechangeListeners);

        return $state;
    }

    /**
     * @param array<string, mixed> $data
     */
    public function __unserialize(array $data): void
    {
        /** @var list<DtlsTransportStateChangeListener> $statechangeListeners */
        $statechangeListeners = $data['__statechangeListeners'] ?? [];
        unset($data['__statechangeListeners']);

        SerializableState::import($this, $data);
        /** @var \WeakMap<DtlsTransportStateChangeListener, null> */
        $this->statechangeListeners = SerializableState::listToWeakMap($statechangeListeners);
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\DTLS\DTLS\Exception;

final class DTLSException extends \Exception implements DTLSExceptionInterface
{

}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\DTLS\DTLS\Exception;

use Webrtc\Exception\WebrtcExceptionInterface;

interface DTLSExceptionInterface extends WebrtcExceptionInterface
{

}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\DTLS\DTLS\Exception;

final class RTCCertificateException extends \Exception implements DTLSExceptionInterface
{

}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\DTLS\DTLS\Exception;

final class TLSException extends \Exception implements DTLSExceptionInterface
{

}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\DTLS\DTLS\Exception;

final class HandshakeException extends \Exception implements DTLSExceptionInterface
{

}<?php

/**
 * This file is part of the PHP WebRTC package, vendored and modified for MadelineProto.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\DTLS\DTLS;

use Webrtc\DTLS\DTLS\RTCCertificate;
use Webrtc\DTLS\Exception\SSLException;
use Throwable;

/**
 * A DTLS 1.2 handshake state machine, in pure PHP.
 *
 * Only what WebRTC actually needs is implemented: DTLS 1.2, ECDHE over P-256, AES-128-GCM, mutual
 * authentication with self-signed certificates and the `use_srtp` extension (RFC 5764). The peer's
 * certificate is *not* validated against a chain of trust, because WebRTC pins it by fingerprint
 * through the signalling channel; what does get verified is that the peer holds the matching
 * private key, via its CertificateVerify signature.
 *
 * The engine is transport agnostic: {@see self::handleDatagram()} takes bytes off the wire and
 * {@see self::takeOutgoing()} hands back datagrams to send.
 */
final class Engine
{
    /** TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256. */
    public const SUITE_ECDHE_ECDSA_AES128_GCM = 0xC02B;
    /** TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256. */
    public const SUITE_ECDHE_RSA_AES128_GCM = 0xC02F;

    private const CURVE_SECP256R1 = 23;
    private const SIG_ECDSA_SECP256R1_SHA256 = 0x0403;
    private const SIG_RSA_PKCS1_SHA256 = 0x0401;

    private const EXT_SUPPORTED_GROUPS = 10;
    private const EXT_EC_POINT_FORMATS = 11;
    private const EXT_SIGNATURE_ALGORITHMS = 13;
    private const EXT_USE_SRTP = 14;
    private const EXT_EXTENDED_MASTER_SECRET = 23;
    private const EXT_RENEGOTIATION_INFO = 0xFF01;

    private const AES_KEY_LENGTH = 16;
    private const AEAD_SALT_LENGTH = 4;

    /** Initial retransmission timeout, doubled on each expiry (RFC 6347 section 4.2.4.1). */
    private const INITIAL_TIMEOUT = 1.0;
    private const MAX_TIMEOUT = 60.0;

    private RecordLayer $records;
    private Handshake $reassembler;

    private bool $isServer = false;
    private bool $started = false;
    private bool $handshakeComplete = false;
    private bool $closed = false;

    /**
     * Whether this side has already put its own close_notify on the wire.
     *
     * Tracked separately from $closed, which is also set when the *peer* closes: a peer that
     * announces a close still has to be answered with our own alert before the association is
     * torn down, and conflating the two swallows that reply.
     */
    private bool $closeNotifySent = false;

    private string $clientRandom = '';
    private string $serverRandom = '';
    private string $cookie = '';
    private int $cipherSuite = self::SUITE_ECDHE_ECDSA_AES128_GCM;

    private ?EcPrivateKey $ecdhePrivate = null;
    private string $masterSecret = '';
    /** The peer's ECDHE public point, kept until we are ready to derive the master secret. */
    private string $peerEcdhePoint = '';
    /** Whether both sides agreed on RFC 7627 extended master secret. */
    private bool $extendedMasterSecret = false;
    /** Whether the peer offered RFC 7627, which decides whether we echo the extension back. */
    private bool $peerOfferedExtendedMasterSecret = false;

    /** Every handshake message exchanged so far, in unfragmented form. */
    private string $transcript = '';

    /** Sequence number of the next handshake message we send. */
    private int $writeMessageSeq = 0;

    /** Datagrams waiting to be sent.
     *
     * @var list<string>
     */
    private array $outgoing = [];
    /**
     * The last flight we sent, kept for retransmission as plaintext records.
     *
     * @var list<array{int, string, int}> Record type, payload and the epoch it was sent in.
     */
    private array $lastFlight = [];

    /** Plaintext application data received from the peer. */
    private string $incomingApplicationData = '';

    private ?string $peerCertificate = null;
    private ?string $peerCertificateDigest = null;
    private string $selectedSrtpProfile = '';

    /** Every SRTP protection profile this engine implements, most preferred first. */
    private const SUPPORTED_SRTP_PROFILES = [
        'SRTP_AEAD_AES_256_GCM' => 0x0008,
        'SRTP_AEAD_AES_128_GCM' => 0x0007,
        'SRTP_AES128_CM_SHA1_80' => 0x0001,
    ];

    /** SRTP protection profiles we offer, most preferred first.
     *
     * @var array<string, int>
     */
    private array $srtpProfiles = self::SUPPORTED_SRTP_PROFILES;

    private float $timeout = self::INITIAL_TIMEOUT;
    private ?float $deadline = null;

    /**
     * @param list<string> $srtpProfiles The protection profiles to offer, most preferred first.
     *                                   Empty offers everything this engine implements.
     */
    public function __construct(
        private readonly RTCCertificate $certificate,
        array $srtpProfiles = []
    ) {
        $this->records = new RecordLayer;
        $this->reassembler = new Handshake;

        if ($srtpProfiles !== []) {
            $requested = array_intersect_key(
                self::SUPPORTED_SRTP_PROFILES,
                array_flip($srtpProfiles)
            );

            if ($requested === []) {
                throw new SSLException(
                    'None of the requested SRTP protection profiles are implemented: '
                    . implode(', ', $srtpProfiles)
                );
            }

            $this->srtpProfiles = $requested;
        }
    }

    /**
     * Act as the DTLS server, i.e. wait for the peer's ClientHello.
     */
    public function setAcceptState(): void
    {
        $this->isServer = true;
    }

    /**
     * Act as the DTLS client, i.e. drive the handshake.
     */
    public function setConnectState(): void
    {
        $this->isServer = false;
    }

    public function isServer(): bool
    {
        return $this->isServer;
    }

    public function isHandshakeComplete(): bool
    {
        return $this->handshakeComplete;
    }

    /**
     * Advance the handshake, sending the first flight if we are the client.
     */
    public function startHandshake(): void
    {
        if ($this->started || $this->isServer) {
            $this->started = true;
            return;
        }
        $this->started = true;
        $this->clientRandom = self::random32();
        $this->sendFlight([[Handshake::CLIENT_HELLO, $this->buildClientHello()]]);
    }

    /**
     * Take the datagrams the engine wants to send.
     *
     * @return list<string>
     */
    public function takeOutgoing(): array
    {
        /** @var list<string> $out */
        $out = $this->outgoing;
        $this->outgoing = [];
        return $out;
    }

    /**
     * Whether any datagram is waiting to be sent.
     */
    public function hasOutgoing(): bool
    {
        return $this->outgoing !== [];
    }

    /**
     * Seconds until the current flight should be retransmitted, or null if no timer is armed.
     */
    public function getTimeout(): ?float
    {
        if ($this->deadline === null || $this->handshakeComplete) {
            return null;
        }
        return max(0.0, $this->deadline - microtime(true));
    }

    /**
     * Retransmit the last flight if the timer expired.
     */
    public function handleTimeout(): bool
    {
        if ($this->deadline === null || $this->handshakeComplete) {
            return false;
        }
        if (microtime(true) < $this->deadline) {
            return false;
        }
        $this->timeout = min($this->timeout + $this->timeout, self::MAX_TIMEOUT);
        $this->deadline = microtime(true) + $this->timeout;
        $this->queueLastFlight();
        return true;
    }

    /**
     * Encode the remembered flight afresh and queue it for sending.
     *
     * Each record is re-encoded at the epoch it was originally sent in but with a new sequence
     * number, as RFC 6347 section 4.2.1 requires: replaying the original numbers would see the
     * whole flight discarded by the peer's duplicate detection.
     */
    private function queueLastFlight(): void
    {
        foreach ($this->lastFlight as [$type, $payload, $epoch]) {
            $this->outgoing[] = $this->records->encode($type, $payload, null, $epoch);
        }
    }

    /**
     * Process one datagram received from the peer.
     *
     * @throws SSLException On a fatal protocol violation.
     */
    public function handleDatagram(string $datagram): void
    {
        foreach ($this->records->decode($datagram) as $record) {
            switch ($record['type']) {
                case RecordLayer::TYPE_HANDSHAKE:
                    if ($this->handshakeComplete) {
                        // Completing the handshake clears the retransmission deadline, so our
                        // final flight is never re-sent on a timer. A peer still sending
                        // handshake records is telling us that flight never arrived; RFC 6347
                        // section 4.2.4 requires answering with it again. Without this the peer
                        // retransmits until it gives up while we consider the exchange finished.
                        $this->queueLastFlight();
                        break;
                    }

                    foreach ($this->reassembler->receive($record['payload']) as $message) {
                        $this->handleHandshakeMessage($message['type'], $message['body'], $message['seq']);
                    }
                    break;
                case RecordLayer::TYPE_CHANGE_CIPHER_SPEC:
                    // The record layer already advanced the read epoch while decoding.
                    break;
                case RecordLayer::TYPE_APPLICATION_DATA:
                    $this->incomingApplicationData .= $record['payload'];
                    break;
                case RecordLayer::TYPE_ALERT:
                    $this->handleAlert($record['payload']);
                    break;
            }
        }
    }

    /**
     * @throws SSLException If the peer reported a fatal condition.
     */
    private function handleAlert(string $payload): void
    {
        if (\strlen($payload) < 2) {
            return;
        }
        $level = \ord($payload[0]);
        $description = \ord($payload[1]);
        if ($description === 0) {
            // close_notify
            $this->closed = true;
            return;
        }
        if ($level === 2) {
            $this->closed = true;
            throw new SSLException("The peer sent a fatal DTLS alert: $description");
        }
    }

    /**
     * Encrypt application data for the peer.
     *
     * @throws SSLException If the handshake is not finished.
     */
    public function write(string $data): void
    {
        if (!$this->handshakeComplete) {
            throw new SSLException('Cannot write application data before the handshake completes!');
        }
        $this->outgoing[] = $this->records->encode(RecordLayer::TYPE_APPLICATION_DATA, $data);
    }

    /**
     * Read whatever application data arrived so far.
     */
    public function read(int $length): string
    {
        $data = substr($this->incomingApplicationData, 0, $length);
        $this->incomingApplicationData = substr($this->incomingApplicationData, \strlen($data));
        return $data;
    }

    public function pendingApplicationData(): int
    {
        return \strlen($this->incomingApplicationData);
    }

    /**
     * Send a close_notify alert.
     */
    public function shutdown(): bool
    {
        if ($this->closeNotifySent) {
            return true;
        }
        $this->closeNotifySent = true;
        $this->closed = true;
        $this->outgoing[] = $this->records->encode(RecordLayer::TYPE_ALERT, "\x01\x00");
        return true;
    }

    public function isClosed(): bool
    {
        return $this->closed;
    }

    /**
     * The SHA-256 fingerprint of the peer's certificate, colon separated and uppercase.
     */
    public function getPeerCertificateDigest(): ?string
    {
        return $this->peerCertificateDigest;
    }

    /**
     * The SRTP protection profile both sides agreed on, as its RFC 5764 name.
     */
    public function getSelectedSrtpProfile(): string
    {
        return $this->selectedSrtpProfile;
    }

    /**
     * Export keying material as defined in RFC 5705.
     *
     * @throws SSLException If the handshake is not finished.
     */
    public function exportKeyingMaterial(string $label, int $length, ?string $context = null): string
    {
        if (!$this->handshakeComplete) {
            throw new SSLException('Cannot export keying material before the handshake completes!');
        }
        return Prf::exportKeyingMaterial(
            $this->masterSecret,
            $label,
            $this->clientRandom,
            $this->serverRandom,
            $length,
            $context
        );
    }

    // ---------------------------------------------------------------- handshake

    /**
     * @throws SSLException On a fatal protocol violation.
     */
    private function handleHandshakeMessage(int $type, string $body, int $seq): void
    {
        // HelloVerifyRequest is excluded from the transcript (RFC 6347 section 4.2.1).
        if ($type !== Handshake::HELLO_VERIFY_REQUEST) {
            $this->transcript .= Handshake::transcriptForm($type, $body, $seq);
        }

        match (true) {
            $type === Handshake::CLIENT_HELLO && $this->isServer => $this->onClientHello($body),
            $type === Handshake::HELLO_VERIFY_REQUEST && !$this->isServer => $this->onHelloVerifyRequest($body),
            $type === Handshake::SERVER_HELLO && !$this->isServer => $this->onServerHello($body),
            $type === Handshake::CERTIFICATE => $this->onCertificate($body),
            $type === Handshake::SERVER_KEY_EXCHANGE && !$this->isServer => $this->onServerKeyExchange($body),
            $type === Handshake::CERTIFICATE_REQUEST && !$this->isServer => null,
            $type === Handshake::SERVER_HELLO_DONE && !$this->isServer => $this->onServerHelloDone(),
            $type === Handshake::CLIENT_KEY_EXCHANGE && $this->isServer => $this->onClientKeyExchange($body),
            $type === Handshake::CERTIFICATE_VERIFY => $this->onCertificateVerify($body),
            $type === Handshake::FINISHED => $this->onFinished($body),
            default => null,
        };
    }

    /**
     * @throws SSLException If the client offers nothing we support.
     */
    private function onClientHello(string $body): void
    {
        $reader = new Reader($body);
        $reader->skip(2); // client_version
        $this->clientRandom = $reader->take(32);
        $reader->takeVector8(); // session_id
        $this->cookie = $reader->takeVector8();

        $suites = $reader->takeVector16();
        $this->cipherSuite = $this->pickCipherSuite($suites);

        $reader->takeVector8(); // compression_methods
        $extensions = self::parseExtensions($reader->rest());
        $this->negotiateSrtpProfile($extensions[self::EXT_USE_SRTP] ?? null);
        $this->peerOfferedExtendedMasterSecret = isset($extensions[self::EXT_EXTENDED_MASTER_SECRET]);
        $this->extendedMasterSecret = $this->peerOfferedExtendedMasterSecret;

        $this->serverRandom = self::random32();
        $this->ecdhePrivate = EcPrivateKey::generateP256();

        // The transcript restarts from this ClientHello; anything before it was a cookie exchange.
        $this->sendFlight([
            [Handshake::SERVER_HELLO, $this->buildServerHello()],
            [Handshake::CERTIFICATE, $this->buildCertificate()],
            [Handshake::SERVER_KEY_EXCHANGE, $this->buildServerKeyExchange()],
            [Handshake::CERTIFICATE_REQUEST, $this->buildCertificateRequest()],
            [Handshake::SERVER_HELLO_DONE, ''],
        ]);
    }

    /**
     * @throws SSLException If no mutually supported cipher suite exists.
     */
    private function pickCipherSuite(string $suites): int
    {
        $offered = [];
        for ($i = 0; $i + 1 < \strlen($suites); $i += 2) {
            $offered[] = Handshake::uint16(substr($suites, $i, 2));
        }
        foreach ([self::SUITE_ECDHE_ECDSA_AES128_GCM, self::SUITE_ECDHE_RSA_AES128_GCM] as $candidate) {
            if (\in_array($candidate, $offered, true)) {
                return $candidate;
            }
        }
        throw new SSLException('The peer does not support any cipher suite we implement!');
    }

    private function onHelloVerifyRequest(string $body): void
    {
        $reader = new Reader($body);
        $reader->skip(2); // server_version
        $this->cookie = $reader->takeVector8();

        // The cookie exchange is not part of the transcript: start over with the new ClientHello.
        $this->transcript = '';
        $this->sendFlight([[Handshake::CLIENT_HELLO, $this->buildClientHello()]]);
    }

    /**
     * @throws SSLException If the server picked something we did not offer.
     */
    private function onServerHello(string $body): void
    {
        $reader = new Reader($body);
        $reader->skip(2); // server_version
        $this->serverRandom = $reader->take(32);
        $reader->takeVector8(); // session_id
        $this->cipherSuite = Handshake::uint16($reader->take(2));
        $reader->skip(1); // compression_method

        if ($this->cipherSuite !== self::SUITE_ECDHE_ECDSA_AES128_GCM
            && $this->cipherSuite !== self::SUITE_ECDHE_RSA_AES128_GCM) {
            throw new SSLException('The peer selected an unsupported cipher suite!');
        }

        $extensions = self::parseExtensions($reader->rest());
        $this->negotiateSrtpProfile($extensions[self::EXT_USE_SRTP] ?? null);
        $this->extendedMasterSecret = isset($extensions[self::EXT_EXTENDED_MASTER_SECRET]);
    }

    private function onCertificate(string $body): void
    {
        $reader = new Reader($body);
        $listLength = Handshake::uint24($reader->take(3));
        if ($listLength === 0) {
            return;
        }
        $certLength = Handshake::uint24($reader->take(3));
        $this->peerCertificate = $reader->take($certLength);
        $this->peerCertificateDigest = RTCCertificate::fingerprint($this->peerCertificate);
    }

    /**
     * @throws SSLException If the key exchange is malformed or unsupported.
     */
    private function onServerKeyExchange(string $body): void
    {
        $reader = new Reader($body);
        $curveType = \ord($reader->take(1));
        if ($curveType !== 3) {
            throw new SSLException('Only named curves are supported in the key exchange!');
        }
        $curve = Handshake::uint16($reader->take(2));
        if ($curve !== self::CURVE_SECP256R1) {
            throw new SSLException('Only the secp256r1 curve is supported!');
        }
        $point = $reader->takeVector8();
        $signatureAlgorithm = Handshake::uint16($reader->take(2));
        $signature = $reader->takeVector16();

        $params = \chr(3).pack('n', self::CURVE_SECP256R1).\chr(\strlen($point)).$point;
        $this->verifyPeerSignature(
            $this->clientRandom.$this->serverRandom.$params,
            $signature,
            $signatureAlgorithm
        );

        $this->ecdhePrivate = EcPrivateKey::generateP256();
        $this->peerEcdhePoint = $point;
    }

    private function onServerHelloDone(): void
    {
        \assert($this->ecdhePrivate !== null);
        $point = $this->ecdhePrivate->getPoint();

        $messages = [
            [Handshake::CERTIFICATE, $this->buildCertificate()],
            [Handshake::CLIENT_KEY_EXCHANGE, \chr(\strlen($point)).$point],
        ];

        // CertificateVerify signs everything sent so far, including our own ClientKeyExchange.
        $transcript = $this->transcript;
        $seq = $this->writeMessageSeq;
        foreach ($messages as [$type, $payload]) {
            $transcript .= Handshake::transcriptForm($type, $payload, $seq++);
        }
        // With RFC 7627 the master secret binds this very transcript, so it can only be derived
        // once our ClientKeyExchange is part of it.
        $this->masterSecret = $this->deriveMasterSecret($this->peerEcdhePoint, $transcript);
        $messages[] = [Handshake::CERTIFICATE_VERIFY, $this->buildCertificateVerify($transcript)];

        $this->sendFlight($messages, true);
    }

    private function onClientKeyExchange(string $body): void
    {
        $reader = new Reader($body);
        $point = $reader->takeVector8();
        // The transcript already contains this ClientKeyExchange, which is exactly the session
        // hash RFC 7627 requires.
        $this->masterSecret = $this->deriveMasterSecret($point, $this->transcript);
    }

    /**
     * @throws SSLException If the peer cannot prove possession of its private key.
     */
    private function onCertificateVerify(string $body): void
    {
        // The signature covers every handshake message before this one.
        $transcript = substr(
            $this->transcript,
            0,
            \strlen($this->transcript) - \strlen(Handshake::transcriptForm(Handshake::CERTIFICATE_VERIFY, $body, 0))
        );

        $reader = new Reader($body);
        $algorithm = Handshake::uint16($reader->take(2));
        $signature = $reader->takeVector16();

        $this->verifyPeerSignature($transcript, $signature, $algorithm);
    }

    /**
     * @throws SSLException If the peer's Finished does not match our transcript.
     */
    private function onFinished(string $body): void
    {
        // The peer's Finished is itself part of the transcript for *our* Finished, but the
        // verify_data it carries covers everything up to but excluding itself.
        $transcript = substr(
            $this->transcript,
            0,
            \strlen($this->transcript) - \strlen(Handshake::transcriptForm(Handshake::FINISHED, $body, 0))
        );
        // The message we just received was sent by the peer: a server validates the client's
        // Finished, and vice versa.
        $expected = Prf::verifyData($this->masterSecret, hash('sha256', $transcript, true), $this->isServer);
        if (!hash_equals($expected, $body)) {
            throw new SSLException('The peer sent an invalid Finished message!');
        }

        if ($this->isServer) {
            // The server answers with its own ChangeCipherSpec and Finished.
            $this->sendFlight([], true);
        }

        $this->handshakeComplete = true;
        $this->deadline = null;
    }

    /**
     * Serialize a flight of handshake messages, optionally followed by our ChangeCipherSpec.
     *
     * @param list<array{int, string}> $messages
     */
    private function sendFlight(array $messages, bool $finish = false): void
    {
        // The flight is remembered as plaintext records tagged with the epoch they belong to,
        // not as finished datagrams: a retransmission has to be re-encoded so it carries fresh
        // record sequence numbers, which the peer's duplicate filter would otherwise reject.
        $flight = [];

        foreach ($messages as [$type, $body]) {
            $seq = $this->writeMessageSeq++;
            $this->transcript .= Handshake::transcriptForm($type, $body, $seq);
            foreach (Handshake::fragment($type, $body, $seq) as $fragment) {
                $flight[] = [RecordLayer::TYPE_HANDSHAKE, $fragment, $this->records->getWriteEpoch()];
            }
        }

        if ($finish) {
            // ChangeCipherSpec is still sent in the old epoch; only what follows it is protected.
            $flight[] = [RecordLayer::TYPE_CHANGE_CIPHER_SPEC, "\x01", $this->records->getWriteEpoch()];
            $this->records->activateWrite();

            $body = Prf::verifyData($this->masterSecret, hash('sha256', $this->transcript, true), !$this->isServer);
            $seq = $this->writeMessageSeq++;
            $this->transcript .= Handshake::transcriptForm(Handshake::FINISHED, $body, $seq);
            foreach (Handshake::fragment(Handshake::FINISHED, $body, $seq) as $fragment) {
                $flight[] = [RecordLayer::TYPE_HANDSHAKE, $fragment, $this->records->getWriteEpoch()];
            }
        }

        $this->lastFlight = $flight;
        $this->queueLastFlight();
        $this->armTimer();
    }

    private function armTimer(): void
    {
        $this->timeout = self::INITIAL_TIMEOUT;
        $this->deadline = microtime(true) + $this->timeout;
    }

    /**
     * Complete the ECDHE exchange and install the resulting keys.
     */
    private function deriveMasterSecret(string $peerPoint, string $sessionTranscript): string
    {
        \assert($this->ecdhePrivate !== null);
        $preMasterSecret = $this->ecdhePrivate->computeSharedSecret($peerPoint);

        $masterSecret = $this->extendedMasterSecret
            ? Prf::extendedMasterSecret($preMasterSecret, hash('sha256', $sessionTranscript, true))
            : Prf::masterSecret($preMasterSecret, $this->clientRandom, $this->serverRandom);

        $keys = Prf::keyBlock(
            $masterSecret,
            $this->clientRandom,
            $this->serverRandom,
            self::AES_KEY_LENGTH,
            self::AEAD_SALT_LENGTH
        );
        if ($this->isServer) {
            $this->records->setKeys($keys['serverKey'], $keys['serverSalt'], $keys['clientKey'], $keys['clientSalt']);
        } else {
            $this->records->setKeys($keys['clientKey'], $keys['clientSalt'], $keys['serverKey'], $keys['serverSalt']);
        }

        return $masterSecret;
    }

    /**
     * @throws SSLException If the signature does not check out.
     */
    private function verifyPeerSignature(string $data, string $signature, int $algorithm): void
    {
        if ($this->peerCertificate === null) {
            throw new SSLException('The peer signed something before sending its certificate!');
        }
        // SignatureAndHashAlgorithm is {hash, signature}: the hash is the high byte.
        $hash = match (($algorithm >> 8) & 0xFF) {
            2 => 'sha1',
            3 => 'sha224',
            4 => 'sha256',
            5 => 'sha384',
            6 => 'sha512',
            default => 'sha256',
        };
        try {
            $valid = Crypto::verify($this->peerCertificate, $data, $signature, $hash);
        } catch (Throwable $e) {
            throw new SSLException('Could not verify the peer signature: '.$e->getMessage(), 0, $e);
        }
        if (!$valid) {
            throw new SSLException('The peer signature is invalid!');
        }
    }

    // ---------------------------------------------------------------- builders

    private function buildClientHello(): string
    {
        return RecordLayer::VERSION_1_2
            .$this->clientRandom
            .\chr(0)
            .\chr(\strlen($this->cookie)).$this->cookie
            .pack('n', 4).pack('nn', self::SUITE_ECDHE_ECDSA_AES128_GCM, self::SUITE_ECDHE_RSA_AES128_GCM)
            .\chr(1).\chr(0)
            .$this->buildExtensions(true);
    }

    private function buildServerHello(): string
    {
        return RecordLayer::VERSION_1_2
            .$this->serverRandom
            .\chr(0)
            .pack('n', $this->cipherSuite)
            .\chr(0)
            .$this->buildExtensions(false);
    }

    private function buildExtensions(bool $client): string
    {
        $extensions = '';

        if ($client) {
            $groups = pack('n', 2).pack('n', self::CURVE_SECP256R1);
            $extensions .= self::extension(self::EXT_SUPPORTED_GROUPS, $groups);
            $extensions .= self::extension(self::EXT_EC_POINT_FORMATS, \chr(1).\chr(0));
            $sigAlgs = pack('n', 4).pack('nn', self::SIG_ECDSA_SECP256R1_SHA256, self::SIG_RSA_PKCS1_SHA256);
            $extensions .= self::extension(self::EXT_SIGNATURE_ALGORITHMS, $sigAlgs);
        }

        if ($client || $this->peerOfferedExtendedMasterSecret) {
            $extensions .= self::extension(self::EXT_EXTENDED_MASTER_SECRET, '');
        }
        $extensions .= self::extension(self::EXT_RENEGOTIATION_INFO, \chr(0));

        // use_srtp: the client offers a list, the server echoes back the single chosen profile.
        if ($client) {
            $profiles = '';
            foreach ($this->srtpProfiles as $id) {
                $profiles .= pack('n', $id);
            }
            $extensions .= self::extension(
                self::EXT_USE_SRTP,
                pack('n', \strlen($profiles)).$profiles.\chr(0)
            );
        } elseif ($this->selectedSrtpProfile !== '') {
            $id = $this->srtpProfiles[$this->selectedSrtpProfile];
            $extensions .= self::extension(self::EXT_USE_SRTP, pack('n', 2).pack('n', $id).\chr(0));
        }

        return pack('n', \strlen($extensions)).$extensions;
    }

    private static function extension(int $type, string $body): string
    {
        return pack('n', $type).pack('n', \strlen($body)).$body;
    }

    private function buildCertificate(): string
    {
        $der = $this->certificate->getDer();
        $entry = Handshake::packUint24(\strlen($der)).$der;
        return Handshake::packUint24(\strlen($entry)).$entry;
    }

    private function buildServerKeyExchange(): string
    {
        \assert($this->ecdhePrivate !== null);
        $point = $this->ecdhePrivate->getPoint();
        $params = \chr(3).pack('n', self::CURVE_SECP256R1).\chr(\strlen($point)).$point;

        $signature = $this->certificate->getKey()->sign($this->clientRandom.$this->serverRandom.$params);

        return $params.pack('n', self::SIG_ECDSA_SECP256R1_SHA256)
            .pack('n', \strlen($signature)).$signature;
    }

    private function buildCertificateRequest(): string
    {
        // certificate_types: ecdsa_sign (64) and rsa_sign (1)
        $types = \chr(2).\chr(64).\chr(1);
        $sigAlgs = pack('n', 4).pack('nn', self::SIG_ECDSA_SECP256R1_SHA256, self::SIG_RSA_PKCS1_SHA256);
        // No acceptable certificate authorities: any self-signed certificate will do.
        return $types.$sigAlgs.pack('n', 0);
    }

    private function buildCertificateVerify(string $transcript): string
    {
        $signature = $this->certificate->getKey()->sign($transcript);

        return pack('n', self::SIG_ECDSA_SECP256R1_SHA256).pack('n', \strlen($signature)).$signature;
    }

    /**
     * Pick the SRTP profile out of a use_srtp extension.
     */
    private function negotiateSrtpProfile(?string $extension): void
    {
        if ($extension === null || \strlen($extension) < 2) {
            return;
        }
        $length = Handshake::uint16(substr($extension, 0, 2));
        $offered = [];
        for ($i = 0; $i + 1 < $length; $i += 2) {
            $offered[] = Handshake::uint16(substr($extension, 2 + $i, 2));
        }
        foreach ($this->srtpProfiles as $name => $id) {
            if (\in_array($id, $offered, true)) {
                $this->selectedSrtpProfile = $name;
                return;
            }
        }
    }

    /**
     * @return array<int, string> Extension body, keyed by extension type.
     */
    private static function parseExtensions(string $raw): array
    {
        if (\strlen($raw) < 2) {
            return [];
        }
        $total = Handshake::uint16(substr($raw, 0, 2));
        $offset = 2;
        $end = min(\strlen($raw), 2 + $total);
        $extensions = [];
        while ($offset + 4 <= $end) {
            $type = Handshake::uint16(substr($raw, $offset, 2));
            $length = Handshake::uint16(substr($raw, $offset + 2, 2));
            $offset += 4;
            if ($offset + $length > $end) {
                break;
            }
            $extensions[$type] = substr($raw, $offset, $length);
            $offset += $length;
        }
        return $extensions;
    }

    private static function random32(): string
    {
        // The first four bytes are conventionally a timestamp; a random value is equally valid
        // and leaks less.
        return random_bytes(32);
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package, vendored and modified for MadelineProto.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\DTLS\DTLS;

/**
 * Framing of DTLS handshake messages (RFC 6347 section 4.2.2).
 *
 * Handshake messages carry a message sequence number and may be split across several datagrams,
 * so this takes care of fragmenting outgoing messages to fit the path MTU and of reassembling
 * incoming ones. The handshake transcript, which both Finished messages authenticate, always uses
 * the *unfragmented* form of each message.
 */
final class Handshake
{
    public const HELLO_REQUEST = 0;
    public const CLIENT_HELLO = 1;
    public const SERVER_HELLO = 2;
    public const HELLO_VERIFY_REQUEST = 3;
    public const CERTIFICATE = 11;
    public const SERVER_KEY_EXCHANGE = 12;
    public const CERTIFICATE_REQUEST = 13;
    public const SERVER_HELLO_DONE = 14;
    public const CERTIFICATE_VERIFY = 15;
    public const CLIENT_KEY_EXCHANGE = 16;
    public const FINISHED = 20;

    public const HEADER_LENGTH = 12;

    /**
     * Largest handshake fragment we emit, chosen to stay well inside a typical 1200 byte datagram
     * once the record and IP headers are accounted for.
     */
    private const MAX_FRAGMENT = 1000;

    /**
     * Partially received messages, keyed by message sequence number.
     *
     * @var array<int, array{type: int, length: int, buffer: string, received: array<int, int>}>
     */
    private array $pending = [];

    /** Next message sequence number we expect to deliver. */
    private int $nextExpected = 0;

    /**
     * Serialize a handshake message, splitting it into fragments if needed.
     *
     * @return list<string> The fragments, each ready to be placed in its own record.
     */
    public static function fragment(int $type, string $body, int $messageSeq): array
    {
        $length = \strlen($body);
        if ($length <= self::MAX_FRAGMENT) {
            return [self::header($type, $length, $messageSeq, 0, $length).$body];
        }

        $fragments = [];
        for ($offset = 0; $offset < $length; $offset += self::MAX_FRAGMENT) {
            $chunk = substr($body, $offset, self::MAX_FRAGMENT);
            $fragments[] = self::header($type, $length, $messageSeq, $offset, \strlen($chunk)).$chunk;
        }
        return $fragments;
    }

    /**
     * The 12 byte handshake header.
     */
    public static function header(int $type, int $length, int $messageSeq, int $offset, int $fragmentLength): string
    {
        return \chr($type)
            .substr(pack('N', $length), 1)
            .pack('n', $messageSeq)
            .substr(pack('N', $offset), 1)
            .substr(pack('N', $fragmentLength), 1);
    }

    /**
     * The form of a message that goes into the handshake transcript: a single, unfragmented copy.
     */
    public static function transcriptForm(int $type, string $body, int $messageSeq): string
    {
        return self::header($type, \strlen($body), $messageSeq, 0, \strlen($body)).$body;
    }

    /**
     * Feed one handshake record payload, which may hold several fragments.
     *
     * Messages are returned in sequence order; out of order fragments are buffered until the gap
     * before them is filled.
     *
     * @return list<array{type: int, body: string, seq: int}>
     */
    public function receive(string $payload): array
    {
        $offset = 0;
        $total = \strlen($payload);

        while ($offset + self::HEADER_LENGTH <= $total) {
            $type = \ord($payload[$offset]);
            $length = self::uint24(substr($payload, $offset + 1, 3));
            $messageSeq = self::uint16(substr($payload, $offset + 4, 2));
            $fragmentOffset = self::uint24(substr($payload, $offset + 6, 3));
            $fragmentLength = self::uint24(substr($payload, $offset + 9, 3));
            $offset += self::HEADER_LENGTH;

            if ($offset + $fragmentLength > $total || $fragmentOffset + $fragmentLength > $length) {
                break;
            }
            $chunk = substr($payload, $offset, $fragmentLength);
            $offset += $fragmentLength;

            if ($messageSeq < $this->nextExpected) {
                // A retransmission of something we already processed.
                continue;
            }

            if (!isset($this->pending[$messageSeq])) {
                $this->pending[$messageSeq] = [
                    'type' => $type,
                    'length' => $length,
                    'buffer' => str_repeat("\0", $length),
                    'received' => [],
                ];
            }
            $entry = $this->pending[$messageSeq];
            if ($fragmentLength > 0) {
                $entry['buffer'] = substr_replace($entry['buffer'], $chunk, $fragmentOffset, $fragmentLength);
            }
            $entry['received'][$fragmentOffset] = max(
                $entry['received'][$fragmentOffset] ?? 0,
                $fragmentLength
            );
            $this->pending[$messageSeq] = $entry;
        }

        return $this->drain();
    }

    /**
     * Emit every message that is complete and in order.
     *
     * @return list<array{type: int, body: string, seq: int}>
     */
    private function drain(): array
    {
        $ready = [];
        while (isset($this->pending[$this->nextExpected])) {
            $entry = $this->pending[$this->nextExpected];
            if (!self::isComplete($entry)) {
                break;
            }
            $ready[] = [
                'type' => $entry['type'],
                'body' => $entry['buffer'],
                'seq' => $this->nextExpected,
            ];
            unset($this->pending[$this->nextExpected]);
            $this->nextExpected++;
        }
        return $ready;
    }

    /**
     * Whether every byte of a message has been received.
     *
     * @param array{type: int, length: int, buffer: string, received: array<int, int>} $entry
     */
    private static function isComplete(array $entry): bool
    {
        if ($entry['length'] === 0) {
            return true;
        }
        $covered = 0;
        $offsets = $entry['received'];
        ksort($offsets);
        foreach ($offsets as $offset => $length) {
            if ($offset > $covered) {
                return false;
            }
            $covered = max($covered, $offset + $length);
        }
        return $covered >= $entry['length'];
    }

    /**
     * Decode a 24-bit big endian integer.
     */
    public static function uint24(string $raw): int
    {
        $value = unpack('N', "\0".$raw);
        return $value === false ? 0 : (int) $value[1];
    }

    /**
     * Decode a 16-bit big endian integer.
     */
    public static function uint16(string $raw): int
    {
        $value = unpack('n', $raw);
        return $value === false ? 0 : (int) $value[1];
    }

    /**
     * Encode a 24-bit big endian integer.
     */
    public static function packUint24(int $value): string
    {
        return substr(pack('N', $value), 1);
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package, vendored and modified for MadelineProto.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\DTLS\DTLS;

use DateTimeImmutable;
use phpseclib4\Crypt\EC;
use phpseclib4\Crypt\RSA;
use phpseclib4\File\X509;
use Throwable;
use Webrtc\DTLS\Exception\SSLException;

/**
 * The certificate cryptography of the handshake: OpenSSL, or phpseclib if the extension isn't loaded.
 *
 * @internal
 */
final class Crypto
{
    public const BACKEND_OPENSSL = 'openssl';
    public const BACKEND_PHPSECLIB = 'phpseclib';

    /**
     * The backend to use, instead of OpenSSL (if available): for tests.
     *
     * @var self::BACKEND_*|null
     */
    public static ?string $backend = null;

    /**
     * Whether to use OpenSSL.
     */
    public static function useOpenssl(): bool
    {
        return self::$backend !== self::BACKEND_PHPSECLIB && \extension_loaded('openssl');
    }

    /**
     * The last OpenSSL error.
     */
    public static function opensslError(): string
    {
        $error = openssl_error_string();
        return $error === false ? 'unknown error' : $error;
    }

    /**
     * Wrap DER data in PEM armour.
     */
    public static function pem(string $label, string $der): string
    {
        return "-----BEGIN $label-----\n".chunk_split(base64_encode($der), 64, "\n")."-----END $label-----\n";
    }

    /**
     * Verify a TLS 1.2 signature (ECDSA, or RSA with PKCS#1 v1.5) with the key of a DER-encoded certificate.
     *
     * @param 'sha1'|'sha224'|'sha256'|'sha384'|'sha512' $hash
     *
     * @throws SSLException If the certificate or the signature can't be parsed.
     */
    public static function verify(string $certificate, string $data, string $signature, string $hash): bool
    {
        if (self::useOpenssl()) {
            $publicKey = openssl_pkey_get_public(self::pem('CERTIFICATE', $certificate));
            if ($publicKey === false) {
                throw new SSLException('Could not load the peer certificate: '.self::opensslError());
            }
            $result = openssl_verify($data, $signature, $publicKey, match ($hash) {
                'sha1' => OPENSSL_ALGO_SHA1,
                'sha224' => OPENSSL_ALGO_SHA224,
                'sha256' => OPENSSL_ALGO_SHA256,
                'sha384' => OPENSSL_ALGO_SHA384,
                'sha512' => OPENSSL_ALGO_SHA512,
            });
            if ($result === -1 || $result === false) {
                throw new SSLException('Could not verify the signature: '.self::opensslError());
            }
            return $result === 1;
        }
        /** @var EC\PublicKey|RSA\PublicKey $publicKey */
        $publicKey = X509::load($certificate)->getPublicKey();
        /** @var EC\PublicKey|RSA\PublicKey $publicKey */
        $publicKey = $publicKey->withHash($hash);
        if ($publicKey instanceof RSA\PublicKey) {
            // TLS 1.2 rsa_pkcs1_* signatures use PKCS#1 v1.5, while phpseclib defaults to PSS.
            /** @var RSA\PublicKey $publicKey */
            $publicKey = $publicKey->withPadding(RSA::SIGNATURE_PKCS1);
        }
        if ($publicKey instanceof EC\PublicKey) {
            /** @var EC\PublicKey $publicKey */
            $publicKey = $publicKey->withSignatureFormat('ASN1');
        }
        /** @var bool */
        return $publicKey->verify($data, $signature);
    }

    /**
     * Create a self-signed certificate, valid for some days.
     *
     * WebRTC peers pin it by fingerprint, without checking its validity dates: with phpseclib it is valid
     * from a day ago anyway, to tolerate clock skew, OpenSSL can't set the start date.
     *
     * @param array{C: string, ST: string, L: string, O: string, CN: string} $dn
     *
     * @return string The certificate, in PEM form.
     */
    public static function selfSign(EcPrivateKey $key, array $dn, int $days): string
    {
        $openssl = $key->getOpenssl();
        if ($openssl !== null) {
            $csr = openssl_csr_new([
                'countryName' => $dn['C'],
                'stateOrProvinceName' => $dn['ST'],
                'localityName' => $dn['L'],
                'organizationName' => $dn['O'],
                'commonName' => $dn['CN'],
            ], $openssl, ['digest_alg' => 'sha256']);
            $certificate = \is_bool($csr) ? false : openssl_csr_sign($csr, null, $openssl, $days, ['digest_alg' => 'sha256'], random_int(1, PHP_INT_MAX));
            if ($certificate !== false && openssl_x509_export($certificate, $pem)) {
                /** @var string $pem */
                return $pem;
            }
            // Without an openssl.cnf, for example: use phpseclib.
        }
        $privateKey = $key->toPhpseclib();
        /** @var \phpseclib4\Crypt\Common\PublicKey $subjectPublicKey */
        $subjectPublicKey = $privateKey->getPublicKey();
        $certificate = new X509($subjectPublicKey);
        $certificate->setSubjectDN($dn);
        // The certificate is self-signed, so the issuer and the subject are identical.
        $certificate->setIssuerDN($dn);
        $certificate->setStartDate('-1 day');
        $certificate->setEndDate("+$days days");
        $certificate->setSerialNumber((string) random_int(1, PHP_INT_MAX), 10);
        // phpseclib 4 inverts signing: the private key signs the certificate in place.
        $privateKey->sign($certificate);
        return $certificate->toString();
    }

    /**
     * The notAfter date of a PEM-encoded certificate.
     *
     * @throws SSLException If it can't be parsed.
     */
    public static function getExpiry(string $certificate): DateTimeImmutable
    {
        if (self::useOpenssl()) {
            $parsed = openssl_x509_parse($certificate);
            if ($parsed === false || !isset($parsed['validTo_time_t'])) {
                throw new SSLException('Could not parse the certificate!');
            }
            return new DateTimeImmutable('@'.(int) $parsed['validTo_time_t']);
        }
        try {
            /** @var array<string, mixed> $tbs */
            $tbs = X509::load($certificate)->toArray()['tbsCertificate'] ?? [];
        } catch (Throwable $e) {
            throw new SSLException('Could not parse the certificate!', 0, $e);
        }
        // phpseclib 4 decodes the validity times into ASN.1 time objects that extend DateTime.
        /** @var array<string, mixed> $validity */
        $validity = $tbs['validity'] ?? [];
        /** @var array{utcTime?: \DateTimeInterface, generalTime?: \DateTimeInterface} $notAfter */
        $notAfter = $validity['notAfter'] ?? [];
        $date = $notAfter['generalTime'] ?? $notAfter['utcTime'] ?? null;
        if (!$date instanceof \DateTimeInterface) {
            throw new SSLException('The certificate has no expiry date!');
        }
        return DateTimeImmutable::createFromInterface($date);
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\DTLS\DTLS;

use Webrtc\DTLS\DTLS\Exception\DTLSException;
use Webrtc\Srtp\Enum\SrtpProfile;
use Webrtc\Srtp\Enum\SsrcType;
use Webrtc\Srtp\Exception\SrtpException;
use Webrtc\Srtp\Exception\SrtpValidateException;
use Webrtc\Srtp\Policy;
use Webrtc\Srtp\Session;

/**
 * Srtp Class
 *
 * Provides a wrapper for Secure Real-time Transport Protocol (SRTP) functionality within
 * WebRTC DTLS implementation. This class handles the management of SRTP profiles, session
 * creation, and key material generation for secure media transmission in WebRTC connections.
 *
 * This class supports multiple SRTP cryptographic profiles, manages inbound and outbound
 * sessions, and handles key derivation from DTLS key material.
 */
final class Srtp
{
    /**
     * The Size of the replay protection window for SRTP packets
     * Helps prevent replay attacks by tracking previously received packets
     */
    private const WINDOW_SIZE = 1024;

    /**
     * Predefined set of supported SRTP cryptographic profiles
     *
     * Each profile includes:
     * - srtpProfile: The SRTP profile enum value from SrtpProfile
     * - sslProfile: The corresponding SSL profile string identifier
     * - keyLength: Length of the encryption key in bytes
     * - saltLent: Length of the salt value in bytes
     *
     * @var list<array{srtpProfile: SrtpProfile, sslProfile: string, keyLength: int, saltLent: int}>
     */
    public const DEFAULT_PROFILES = [
        [
            "srtpProfile" => SrtpProfile::AEAD_AES_256_GCM,
            "sslProfile" => "SRTP_AEAD_AES_256_GCM",
            "keyLength" => 32,
            "saltLent" => 12
        ],
        [
            "srtpProfile" => SrtpProfile::AEAD_AES_128_GCM,
            "sslProfile" => "SRTP_AEAD_AES_128_GCM",
            "keyLength" => 16,
            "saltLent" => 12
        ],
        [
            "srtpProfile" => SrtpProfile::AES128_CM_SHA1_80,
            "sslProfile" => "SRTP_AES128_CM_SHA1_80",
            "keyLength" => 16,
            "saltLent" => 14
        ]
    ];

    /**
     * The currently selected SRTP profile configuration
     *
     * @var array{srtpProfile: SrtpProfile, sslProfile: string, keyLength: int, saltLent: int}|null
     */
    private ?array $profile = null;

    /**
     * Initializes the SRTP library and creates a new Srtp instance
     *
     * @throws SrtpException If the underlying SRTP library fails to initialize
     */
    public function __construct()
    {
    }

    /**
     * Verifies if a specific SRTP profile is available and supported by the current system
     *
     * Attempts to create a Policy with the specified profile to check its availability
     *
     * @param array{srtpProfile: SrtpProfile, sslProfile: string, keyLength: int, saltLent: int} $profile Array containing profile configuration details
     * @return bool True if the profile is available, false otherwise
     */
    private static function checkAvailabilityProfile(array $profile): bool
    {
        try {
            new Policy($profile["srtpProfile"]);
            return true;
        } catch (SrtpValidateException) {
            return false;
        }
    }

    /**
     * Retrieves all available SRTP profiles supported by the current system
     *
     * Iterates through DEFAULT_PROFILES and filters only those supported by the system
     *
     * @return list<array{srtpProfile: SrtpProfile, sslProfile: string, keyLength: int, saltLent: int}> List of available SRTP profiles
     * @throws SrtpException If the SRTP library fails to initialize
     */
    public static function getProfiles(): array
    {
        $profiles = [];
        foreach (self::DEFAULT_PROFILES as $profile) {
            if (self::checkAvailabilityProfile($profile)) {
                $profiles [] = $profile;
            }
        }

        return $profiles;
    }

    /**
     * Retrieves and sets the profile configuration for a specified SRTP profile
     *
     * Searches through available profiles to find one matching the provided SSL profile name
     * and set it as the current profile if found
     *
     * @param string $selectedSrtpProfile The SSL profile name to search for
     * @return array{srtpProfile: SrtpProfile, sslProfile: string, keyLength: int, saltLent: int}|false The matched profile configuration or false if not found
     * @throws SrtpException If the SRTP library fails to initialize during profile retrieval
     */
    public function getProfile(string $selectedSrtpProfile): array|false
    {
        $profiles = self::getProfiles();
        foreach ($profiles as $profile) {
            if ($profile["sslProfile"] === $selectedSrtpProfile) {
                $this->profile = $profile;
                return $profile;
            }
        }

        return false;
    }

    /**
     * Creates an inbound SRTP session using provided key material
     *
     * @param string $srtpKeyMaterial Raw key material from DTLS handshake
     * @param int $index Index for key derivation (0 for client, 1 for server)
     * @return Session The initialized inbound SRTP session
     * @throws DTLSException If the profile is not set or key generation fails
     * @throws SrtpValidateException If the SRTP session creation fails
     */
    public function getInbound(string $srtpKeyMaterial, int $index): Session
    {
        return $this->getSession($srtpKeyMaterial, $index, SsrcType::ANY_INBOUND);
    }

    /**
     * Creates an outbound SRTP session using provided key material
     *
     * @param string $srtpKeyMaterial Raw key material from DTLS handshake
     * @param int $index Index for key derivation (0 for client, 1 for server)
     * @return Session The initialized outbound SRTP session
     * @throws DTLSException If the profile is not set or key generation fails
     * @throws SrtpValidateException If the SRTP session creation fails
     */
    public function getOutbound(string $srtpKeyMaterial, int $index): Session
    {
        return $this->getSession($srtpKeyMaterial, $index, SsrcType::ANY_OUTBOUND);
    }

    /**
     * Generates an SRTP key from the raw key material
     *
     * Extracts the key and salt components from the key material based on profile settings
     * and the specified index. Concatenates them to form the final key.
     *
     * @param string $srtpKeyMaterial Raw key material from DTLS handshake
     * @param int $index Index for key derivation (0 for client, 1 for server)
     * @return string The generated SRTP key
     * @throws DTLSException If the profile is not set
     */
    public function generateKey(string $srtpKeyMaterial, int $index): string
    {
        $profile = $this->profile;
        if ($profile === null) {
            throw new DTLSException("Profile is not set");
        }

        $keyStart = $index * $profile["keyLength"];
        $saltStart = 2 * $profile["keyLength"] + $index * $profile["saltLent"];

        return substr($srtpKeyMaterial, $keyStart, $profile["keyLength"]) . substr($srtpKeyMaterial, $saltStart, $profile["saltLent"]);
    }

    /**
     * Creates an SRTP session of the specified type using provided key material
     *
     * Common implementation for both inbound and outbound session creation
     *
     * @param string $srtpKeyMaterial Raw key material from DTLS handshake
     * @param int $index Index for key derivation (0 for client, 1 for server)
     * @param SsrcType $ssrcType The type of session (inbound or outbound)
     * @return Session The initialized SRTP session
     * @throws DTLSException If the profile is not set or key generation fails
     * @throws SrtpValidateException If the SRTP session creation fails
     */
    private function getSession(string $srtpKeyMaterial, int $index, SsrcType $ssrcType): Session
    {
        $profile = $this->profile;
        if ($profile === null) {
            throw new DTLSException("Profile is not set");
        }
        $key = $this->generateKey($srtpKeyMaterial, $index);

        $policy = new Policy($profile["srtpProfile"], $key, $ssrcType);
        $policy->setAllowRepeatTx(true);
        $policy->setWindowSize(self::WINDOW_SIZE);

        return new Session($policy);
    }

    /**
     * Sets the active SRTP profile configuration
     *
     * @param array{srtpProfile: SrtpProfile, sslProfile: string, keyLength: int, saltLent: int} $profile The profile configuration to set
     * @return void
     */
    public function setProfile(array $profile): void
    {
        $this->profile = $profile;
    }
}<?php

/**
 * This file is part of the PHP WebRTC package, vendored and modified for MadelineProto.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\DTLS\DTLS;

use Webrtc\DTLS\DTLS\Exception\RTCCertificateException;
use Webrtc\SDP\DtlsParameter\RTCDtlsFingerprint;
use DateTimeImmutable;
use phpseclib4\Crypt\EC\PrivateKey;
use Throwable;

/**
 * Class RTCCertificate
 *
 * Represents a certificate used by an RTCDtlsTransport for WebRTC communications.
 * This class handles certificate generation, management, and fingerprinting for DTLS connections.
 *
 * Upstream this was generated through OpenSSL's FFI bindings; it now uses the openssl extension, or
 * phpseclib4 without it, so that a plain PHP installation with no extra extensions can take part in
 * calls. WebRTC certificates are self-signed and pinned by fingerprint, so no chain of trust is involved.
 *
 * The certificate can be either:
 * - Generated automatically with an EC secp256r1 private key (default behavior)
 * - Loaded from existing certificate and private key files
 *
 * @package Webrtc\DTLS\DTLS
 */
final class RTCCertificate
{
    /** @var string Default country for a certificate subject */
    private const COUNTRY = "US";

    /** @var string Default state for a certificate subject */
    private const STATE = "Virginia";

    /** @var string Default city for a certificate subject */
    private const CITY = "Reston";

    /** @var string Default organization for a certificate subject */
    private const ORGANIZATION = "PHP WebRTC";

    /** @var string Default common name for a certificate subject */
    private const ORGANIZATION_WEBSITE = "webrtc.php";

    /** How many days the generated certificate stays valid. */
    private const VALIDITY_DAYS = 30;

    private EcPrivateKey $privateKey;

    /** The certificate in PEM form. */
    private string $certificate;

    /** The certificate in DER form, which is what goes on the wire and gets fingerprinted. */
    private string $der;

    private DateTimeImmutable $expires;

    /**
     * RTCCertificate constructor.
     *
     * @param string|null $privateKey Path to an existing private key file (PEM format)
     * @param string|null $certificate Path to an existing certificate file (PEM format)
     *
     * @throws RTCCertificateException If provided files don't exist or are invalid.
     */
    public function __construct(?string $privateKey = null, ?string $certificate = null)
    {
        if ($privateKey !== null && $certificate !== null) {
            if (!is_file($privateKey) || !is_file($certificate)) {
                throw new RTCCertificateException(sprintf(
                    "Either the private key file or the certificate file (or both) does not exist. Private key path: %s, Certificate path: %s",
                    $privateKey,
                    $certificate
                ));
            }
            $this->load((string) file_get_contents($privateKey), (string) file_get_contents($certificate));
        } else {
            $this->generate();
        }
    }

    /**
     * @throws RTCCertificateException If the key or certificate cannot be parsed.
     */
    private function load(string $privateKey, string $certificate): void
    {
        try {
            $this->privateKey = EcPrivateKey::load($privateKey);
        } catch (Throwable $e) {
            throw new RTCCertificateException('Could not load the private key: '.$e->getMessage(), 0, $e);
        }
        try {
            $this->expires = Crypto::getExpiry($certificate);
        } catch (Throwable $e) {
            throw new RTCCertificateException('Could not parse the certificate!', 0, $e);
        }
        $this->certificate = $certificate;
        $this->der = self::toDer($certificate);
    }

    /**
     * Gets the certificate expiration date.
     */
    public function expires(): DateTimeImmutable
    {
        return $this->expires;
    }

    /**
     * Gets the certificate fingerprints.
     *
     * Returns the SHA-256 digest of the DER encoded certificate, formatted as the colon separated
     * uppercase hex string used by SDP.
     *
     * @return array<RTCDtlsFingerprint> Array of fingerprint objects
     */
    public function getFingerprints(): array
    {
        return [new RTCDtlsFingerprint('sha-256', self::fingerprint($this->der))];
    }

    /**
     * Format the SHA-256 fingerprint of a DER encoded certificate the way SDP expects it.
     */
    public static function fingerprint(string $der): string
    {
        return strtoupper(implode(':', str_split(hash('sha256', $der), 2)));
    }

    /**
     * Generates a new EC secp256r1 key pair and a matching self-signed certificate.
     *
     * @throws RTCCertificateException If generation fails.
     */
    private function generate(): void
    {
        try {
            $this->privateKey = EcPrivateKey::generateP256();
            $this->certificate = Crypto::selfSign($this->privateKey, [
                'C' => self::COUNTRY,
                'ST' => self::STATE,
                'L' => self::CITY,
                'O' => self::ORGANIZATION,
                'CN' => self::ORGANIZATION_WEBSITE,
            ], self::VALIDITY_DAYS);
            $this->der = self::toDer($this->certificate);
            $this->expires = Crypto::getExpiry($this->certificate);
        } catch (RTCCertificateException $e) {
            throw $e;
        } catch (Throwable $e) {
            throw new RTCCertificateException('Could not generate a certificate: '.$e->getMessage(), 0, $e);
        }
    }

    /**
     * Strip the PEM armour of a certificate, yielding its DER encoding.
     */
    private static function toDer(string $pem): string
    {
        $body = preg_replace('#-----(BEGIN|END) CERTIFICATE-----|\s#', '', $pem);
        $der = base64_decode((string) $body, true);
        if ($der === false) {
            throw new RTCCertificateException('The certificate is not valid PEM!');
        }
        return $der;
    }

    /**
     * Gets the certificate, in PEM form.
     */
    public function getCertificate(): string
    {
        return $this->certificate;
    }

    /**
     * Gets the DER encoding of the certificate, as sent in the DTLS Certificate message.
     */
    public function getDer(): string
    {
        return $this->der;
    }

    /**
     * Gets the private key, as a phpseclib key.
     */
    public function getPrivateKey(): PrivateKey
    {
        return $this->privateKey->toPhpseclib();
    }

    /**
     * Gets the private key.
     *
     * @internal
     */
    public function getKey(): EcPrivateKey
    {
        return $this->privateKey;
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\DTLS\DTLS\Enum;

enum SSLHandshakeState: int
{
    case Accept = 0;
    case Connect = 1;
}<?php

namespace Webrtc\DTLS\Listener;

/**
 * Notified when an {@see \Webrtc\DTLS\DTLS\RTCDtlsTransport} changes state.
 *
 * Typed replacement for the former Evenement "statechange" event (which carried no payload). The
 * listener is a plain object captured verbatim by a serialize cycle.
 */
interface DtlsTransportStateChangeListener
{
    public function onDtlsTransportStateChange(): void;
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\DTLS\Exception;

final class SSLException extends OpenSSLException
{

}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\DTLS\Exception;

final class SysCallException extends OpenSSLException
{

}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\DTLS\Exception;

use Webrtc\Exception\Exception;

class OpenSSLException extends Exception
{

}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\DTLS\Exception;

final class ZeroReturnException extends OpenSSLException
{

}<?php

/**
 * This file is part of the PHP WebRTC package, vendored and modified for MadelineProto.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\DTLS\SSL;

use Webrtc\DTLS\Enum\BioMethod;

/**
 * The datagram queues sitting between the DTLS engine and the network.
 *
 * Upstream this wrapped an OpenSSL memory BIO. It is now a plain pair of queues: {@see self::write()}
 * hands a datagram received from the ICE transport to the engine, and {@see self::read()} takes the
 * next datagram the engine wants to put on the wire.
 */
final class BIO implements BIOInterface
{
    /** Datagrams received from the network, waiting to be fed to the engine.
     *
     * @var list<string>
     */
    private array $inbound = [];

    /** Datagrams produced by the engine, waiting to be sent.
     *
     * @var list<string>
     */
    private array $outbound = [];

    public function __construct(private readonly BioMethod $method = BioMethod::s_mem)
    {
    }

    /**
     * Take the next datagram the engine wants to send, or null if there is none.
     */
#[\Override]
    public function read(): ?string
    {
        $datagram = array_shift($this->outbound);
        return \is_string($datagram) ? $datagram : null;
    }

    /**
     * Total number of bytes queued for sending.
     */
#[\Override]
    public function getPendingBytes(): int
    {
        $pending = 0;
        foreach ($this->outbound as $datagram) {
            $pending += \strlen($datagram);
        }
        return $pending;
    }

    /**
     * Queue a datagram received from the network.
     */
#[\Override]
    public function write(string $buf): int
    {
        if ($buf !== '') {
            $this->inbound[] = $buf;
        }
        return \strlen($buf);
    }

    /**
     * Take the next datagram received from the network, for the engine to process.
     */
    public function takeInbound(): ?string
    {
        $datagram = array_shift($this->inbound);
        return \is_string($datagram) ? $datagram : null;
    }

    /**
     * Queue a datagram produced by the engine.
     */
    public function pushOutbound(string $datagram): void
    {
        $this->outbound[] = $datagram;
    }

    public function hasOutbound(): bool
    {
        return $this->outbound !== [];
    }

    /**
     * Kept for interface compatibility: there is no native BIO to inspect any more.
     */
#[\Override]
    public function handleBioErrors(mixed $bio): void
    {
    }

    public function getMethod(): BioMethod
    {
        return $this->method;
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\DTLS\SSL;

interface BIOInterface
{
    public function read(): ?string;

    public function getPendingBytes(): int;

    public function write(string $buf): int ;

    public function handleBioErrors(mixed $bio): void;
}<?php

/**
 * This file is part of the PHP WebRTC package, vendored and modified for MadelineProto.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\DTLS\SSL;

use Webrtc\Exception\RuntimeException;
use Webrtc\DTLS\DTLS\Engine;
use Webrtc\DTLS\Exception\SSLException;
use Webrtc\DTLS\Exception\ZeroReturnException;

/**
 * A DTLS connection, driving the pure-PHP {@see Engine} through the {@see BIO} datagram queues.
 *
 * The interface mirrors the subset of OpenSSL's `SSL_*` API the rest of the stack relies on, so
 * that the transport and handshake drivers did not have to change when the FFI binding was
 * replaced by a native implementation.
 */
final class SSL implements SSLInterface
{
    private Engine $engine;

    public function __construct(
        private readonly Context $context,
        private readonly BIO $bio,
    ) {
        $certificate = $context->getCertificate();
        if ($certificate === null) {
            throw new RuntimeException('The SSL context has no certificate!');
        }
        // Honour what the context was told to offer. Ignoring it meant setTlsextUseSrtp() had
        // no effect at all: every handshake offered the full list regardless, so a peer could
        // end up on a profile the caller never asked for.
        $this->engine = new Engine($certificate, $context->getSrtpProfiles());
    }

#[\Override]
    public function setAcceptState(): void
    {
        $this->engine->setAcceptState();
    }

#[\Override]
    public function setConnectState(): void
    {
        $this->engine->setConnectState();
    }

    /**
     * Advance the handshake by one step.
     *
     * Feeds everything the transport delivered into the engine, flushes whatever the engine wants
     * to send, and reports whether more data is still needed.
     *
     * @return bool True once the handshake is complete, false if it still needs another datagram.
     * @throws SSLException On a fatal protocol violation.
     */
#[\Override]
    public function doHandshake(): bool
    {
        $this->engine->startHandshake();

        while (($datagram = $this->bio->takeInbound()) !== null) {
            $this->engine->handleDatagram($datagram);
        }
        $this->flush();

        return $this->engine->isHandshakeComplete();
    }

    /**
     * Move everything the engine produced into the outbound queue.
     */
    private function flush(): void
    {
        foreach ($this->engine->takeOutgoing() as $datagram) {
            $this->bio->pushOutbound($datagram);
        }
    }

#[\Override]
    public function getPeerCertificateDigest(): ?string
    {
        return $this->engine->getPeerCertificateDigest();
    }

#[\Override]
    public function getSelectedSrtpProfile(): string
    {
        return $this->engine->getSelectedSrtpProfile();
    }

#[\Override]
    public function exportKeyingMaterial(string $label, int $keyLength, ?string $context = null): string
    {
        return $this->engine->exportKeyingMaterial($label, $keyLength, $context);
    }

#[\Override]
    public function shutdown(): bool
    {
        $result = $this->engine->shutdown();
        $this->flush();
        return $result;
    }

    /**
     * Seconds until the current handshake flight must be retransmitted, if a timer is armed.
     */
#[\Override]
    public function dtlsV1GetTimeout(): ?float
    {
        return $this->engine->getTimeout();
    }

    /**
     * Retransmit the last flight if its timer expired.
     */
#[\Override]
    public function dtlsV1HandleTimeout(): bool
    {
        $retransmitted = $this->engine->handleTimeout();
        $this->flush();
        return $retransmitted;
    }

    /**
     * Return any decrypted application data buffered so far.
     *
     * The incoming datagram must already have been written to the BIO. The engine coalesces
     * application data into a single stream, so there is at most one chunk to hand back per call.
     *
     * @return string|null The decrypted application data, or null if none has arrived.
     * @throws ZeroReturnException If the peer has closed the connection.
     */
#[\Override]
    public function readApplicationData(): ?string
    {
        while (($datagram = $this->bio->takeInbound()) !== null) {
            $this->engine->handleDatagram($datagram);
        }
        $this->flush();

        // Application data that arrived before the alert is still delivered first; only once it has
        // been drained does a received close_notify become a clean end of stream.
        if ($this->engine->pendingApplicationData() > 0) {
            return $this->engine->read($this->engine->pendingApplicationData());
        }

        if ($this->engine->isClosed()) {
            throw new ZeroReturnException('The peer closed the DTLS connection.');
        }

        return null;
    }

    /**
     * Encrypt and queue application data.
     *
     * @throws SSLException If the handshake has not completed.
     */
#[\Override]
    public function write(string $buf, int $flags = 0): void
    {
        $this->engine->write($buf);
        $this->flush();
    }

    /**
     * The engine behind this connection.
     */
    public function getEngine(): Engine
    {
        return $this->engine;
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package, vendored and modified for MadelineProto.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\DTLS\SSL;

use Webrtc\DTLS\DTLS\RTCCertificate;
use Webrtc\DTLS\Enum\ContextMethod;
use Psr\Log\LoggerInterface;

/**
 * Configuration shared by every connection created from it.
 *
 * Upstream this wrapped an OpenSSL `SSL_CTX`. Since the DTLS engine is now written in PHP, it is
 * simply the place where the local certificate, the offered SRTP profiles and the logger live.
 */
final class Context
{
    private ?RTCCertificate $certificate = null;

    private ?LoggerInterface $logger = null;

    /** SRTP protection profile names, in the colon separated form OpenSSL used. */
    private string $srtpProfiles = '';

    private string $cipherList = '';

    public function __construct(private readonly ContextMethod $method = ContextMethod::DTLS_METHOD)
    {
    }

    public function getMethod(): ContextMethod
    {
        return $this->method;
    }

    public function setCertificate(RTCCertificate $certificate): void
    {
        $this->certificate = $certificate;
    }

    public function getCertificate(): ?RTCCertificate
    {
        return $this->certificate;
    }

    public function setInfoCallBack(?LoggerInterface $logger): void
    {
        $this->logger = $logger;
    }

    public function getLogger(): ?LoggerInterface
    {
        return $this->logger;
    }

    /**
     * Kept for interface compatibility: the peer certificate is pinned by fingerprint instead of
     * being validated against a chain, so there is no verification mode to configure.
     */
    public function setVerify(int $mode, callable $verify): void
    {
    }

    public function setCipherList(string $cipherList): void
    {
        $this->cipherList = $cipherList;
    }

    public function getCipherList(): string
    {
        return $this->cipherList;
    }

    public function setTlsextUseSrtp(string $profiles): void
    {
        $this->srtpProfiles = $profiles;
    }

    /**
     * @return list<string> The offered SRTP profile names.
     */
    public function getSrtpProfiles(): array
    {
        return $this->srtpProfiles === '' ? [] : explode(':', $this->srtpProfiles);
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\DTLS\SSL;

use Webrtc\DTLS\Exception\ZeroReturnException;

interface SSLInterface
{
    public function setAcceptState(): void;

    public function setConnectState(): void;

    /**
     * Advance the handshake by one step.
     *
     * @return bool True once the handshake is complete, false if it still needs another datagram
     *              from the peer.
     */
    public function doHandshake(): bool;

    public function getPeerCertificateDigest(): ?string;

    public function getSelectedSrtpProfile(): string;

    public function exportKeyingMaterial(string $label, int $keyLength, ?string $context = null): string;

    public function shutdown(): bool ;

    public function dtlsV1GetTimeout(): ?float ;

    public function dtlsV1HandleTimeout(): bool ;

    /**
     * Return any decrypted application data buffered so far.
     *
     * The incoming datagram must already have been written to the BIO. The engine coalesces
     * application data into a single stream, so there is at most one chunk to hand back per call.
     *
     * @return string|null The decrypted application data, or null if none has arrived.
     * @throws ZeroReturnException If the peer has closed the connection.
     */
    public function readApplicationData(): ?string;

    public function write(string $buf, int $flags = 0): void;
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\DTLS\Enum;
enum PrivateKeyType: int
{
    case RSA = 0;
    case RSA_PSS = 1;
    case EC = 2;
    case ED25519 = 3;
    case ED448 = 4;
    case X25519 = 5;
    case X448 = 6;
    case SM2 = 7;
    case DH = 8;
    case X9_42_DH = 9;
    case DHX = 10;
    case DSA = 11;
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\DTLS\Enum;

enum ContextMethod: int
{
    case SSLv23_METHOD = 3;
    case TLSv1_METHOD = 4;
    case TLSv1_1_METHOD = 5;
    case TLSv1_2_METHOD = 6;
    case TLS_METHOD = 7;
    case TLS_SERVER_METHOD = 8;
    case TLS_CLIENT_METHOD = 9;
    case DTLS_METHOD = 10;
    case DTLS_SERVER_METHOD = 11;
    case DTLS_CLIENT_METHOD = 12;
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\DTLS\Enum;

enum ECCurveName: int
{
    case secp256r1 = 415;
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\DTLS\Enum;

enum Verify: int
{
    case  NONE = 0x00;
    case  PEER = 0x01;
    case  FAIL_IF_NO_PEER_CERT = 0x02;
    case  CLIENT_ONCE = 0x04;
    case  POST_HANDSHAKE = 0x08;
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\DTLS\Enum;

enum BioMethod: int
{
    case s_mem = 0;
    case s_dgram_mem = 1;
    case s_secmem = 2;
    case s_socket = 3;
    case s_connect = 4;
    case s_accept = 5;
    case s_fd = 6;
    case s_log = 7;
    case s_bio = 8;
    case s_null = 9;
    case f_null = 10;
    case f_buffer = 11;
    case f_readbuffer = 12;
    case f_linebuffer = 13;
    case f_ntest = 14;
    case f_prefix = 15;
    case s_core = 16;
    case s_dgram_pair = 17;
    case s_datagram = 18;
}{
    "name": "danog/php-rtc-dtls",
    "type": "library",
    "description": "Pure-PHP DTLS 1.2 for WebRTC, powered by phpseclib and Amp v3.",
    "keywords": [
        "rtp",
        "dtls",
        "ssl",
        "crypto",
        "openssl",
        "x509",
        "certificate",
        "privatekey"
    ],
    "homepage": "https://github.com/danog/php-rtc-dtls",
    "license": "BSD-3-Clause",
    "authors": [
        {
            "name": "Amin Yazdanpanah",
            "email": "github@aminyazdanpanah.com"
        },
        {
            "name": "Sana Moniri",
            "email": "s.mo@quasarstream.com"
        },
        {
            "name": "Quasar Stream Community"
        },
        {
            "name": "Daniil Gentili",
            "email": "daniil@daniil.it"
        }
    ],
    "require": {
        "php-64bit": "^8.2",
        "amphp/amp": "^3.1.3",
        "danog/php-rtc-exception": "^1.1.5",
        "danog/php-rtc-ice": "^1.1.18 || ^2.0",
        "danog/php-rtc-rtp": "^2.0.23",
        "danog/php-rtc-sctp": "^1.1.5 || ^2.0",
        "danog/php-rtc-stats": "^1.1.3",
        "danog/php-rtc-sdp": "^1.1.6",
        "danog/php-rtc-mixin": "^2.0",
        "danog/php-rtc-srtp": "^1.1.16",
        "symfony/polyfill-php84": "*",
        "phpseclib/phpseclib": "^4.0"
    },
    "require-dev": {
        "phpunit/phpunit": "^11.5 || ^12.0 || ^13.3.2",
        "dg/bypass-finals": "dev-master",
        "bamarni/composer-bin-plugin": "^1.9.1"
    },
    "autoload": {
        "psr-4": {
            "Webrtc\\DTLS\\": "src"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\Webrtc\\DTLS\\": "tests"
        }
    },
    "minimum-stability": "stable",
    "scripts": {
        "post-install-cmd": [
            "@composer bin all install --ansi"
        ],
        "post-update-cmd": [
            "@composer bin all update --ansi"
        ]
    },
    "config": {
        "allow-plugins": {
            "bamarni/composer-bin-plugin": true
        }
    },
    "extra": {
        "bamarni-bin": {
            "bin-links": true,
            "forward-command": true
        }
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\STUN\Message;

use Amp\Socket\InternetAddress;
use Amp\Socket\InternetAddressVersion;
use Webrtc\Exception\InvalidArgumentException;
use Webrtc\STUN\Enum\MessageAttribute;

/**
 * Stateless helper to encode and decode WebRTC message attributes.
 *
 * Every method is a pure function of its arguments; no instance state is kept.
 */
final class MessageAttributeEncoder
{
    public const COOKIE = 0x2112A442;
    private const IPV4_PROTOCOL = 1;
    private const IPV6_PROTOCOL = 2;

    /**
     * Pack/unpack strategies. Each attribute maps to exactly one via self::CODECS.
     */
    private const CODEC_ADDRESS = 'address';
    private const CODEC_XOR_ADDRESS = 'xorAddress';
    private const CODEC_UNSIGNED = 'unsigned';
    private const CODEC_UNSIGNED_SHORT = 'unsignedShort';
    private const CODEC_UNSIGNED_64 = 'unsigned64';
    private const CODEC_ERROR_CODE = 'errorCode';
    private const CODEC_VALUE = 'value';
    private const CODEC_NULL = 'null';

    /**
     * Map of attribute type to the codec used to pack/unpack its value.
     *
     * @var array<int, string>
     */
    private const CODECS = [
        MessageAttribute::MAPPED_ADDRESS->value => self::CODEC_ADDRESS,
        MessageAttribute::SOURCE_ADDRESS->value => self::CODEC_ADDRESS,
        MessageAttribute::CHANGED_ADDRESS->value => self::CODEC_ADDRESS,
        MessageAttribute::RESPONSE_ORIGIN->value => self::CODEC_ADDRESS,
        MessageAttribute::OTHER_ADDRESS->value => self::CODEC_ADDRESS,

        MessageAttribute::XOR_PEER_ADDRESS->value => self::CODEC_XOR_ADDRESS,
        MessageAttribute::XOR_RELAYED_ADDRESS->value => self::CODEC_XOR_ADDRESS,
        MessageAttribute::XOR_MAPPED_ADDRESS->value => self::CODEC_XOR_ADDRESS,

        MessageAttribute::CHANGE_REQUEST->value => self::CODEC_UNSIGNED,
        MessageAttribute::LIFETIME->value => self::CODEC_UNSIGNED,
        MessageAttribute::REQUESTED_TRANSPORT->value => self::CODEC_UNSIGNED,
        MessageAttribute::PRIORITY->value => self::CODEC_UNSIGNED,
        MessageAttribute::FINGERPRINT->value => self::CODEC_UNSIGNED,

        MessageAttribute::CHANNEL_NUMBER->value => self::CODEC_UNSIGNED_SHORT,

        MessageAttribute::ICE_CONTROLLED->value => self::CODEC_UNSIGNED_64,
        MessageAttribute::ICE_CONTROLLING->value => self::CODEC_UNSIGNED_64,

        MessageAttribute::ERROR_CODE->value => self::CODEC_ERROR_CODE,

        MessageAttribute::USERNAME->value => self::CODEC_VALUE,
        MessageAttribute::MESSAGE_INTEGRITY->value => self::CODEC_VALUE,
        MessageAttribute::REALM->value => self::CODEC_VALUE,
        MessageAttribute::NONCE->value => self::CODEC_VALUE,
        MessageAttribute::SOFTWARE->value => self::CODEC_VALUE,

        MessageAttribute::USE_CANDIDATE->value => self::CODEC_NULL,
    ];

    /**
     * Encode an attribute by name.
     *
     * @param string $attrName The name of the attribute to encode.
     * @param int|string|array|InternetAddress|null $data The data to encode.
     * @param string $transactionId The transaction ID.
     * @return array{int, string|null} The encoded attribute type and value.
     */
    public static function encode(
        string $attrName,
        int|string|array|InternetAddress|null $data,
        string $transactionId
    ): array
    {
        $attribute = MessageAttribute::fromName($attrName);

        if ($attribute === null) {
            throw new InvalidArgumentException("Invalid attribute name: $attrName");
        }

        $packed = match (self::CODECS[$attribute->value]) {
            self::CODEC_ADDRESS => self::packAddress($data),
            self::CODEC_XOR_ADDRESS => self::packXorAddress($data, $transactionId),
            self::CODEC_UNSIGNED => self::packUnsigned($data),
            self::CODEC_UNSIGNED_SHORT => self::packUnsignedShort($data),
            self::CODEC_UNSIGNED_64 => self::packUnsigned64($data),
            self::CODEC_ERROR_CODE => self::packErrorCode($data),
            self::CODEC_VALUE => self::packRawValue($data),
            self::CODEC_NULL => null,
        };

        return [$attribute->value, $packed];
    }

    /**
     * Decode an attribute by type.
     *
     * @param int $attrType The type of the attribute to decode.
     * @param string $data The data to decode.
     * @param string $transactionId The transaction ID.
     * @return array{string, InternetAddress|array<array-key, mixed>|int|string|null} The decoded attribute name and value.
     */
    public static function decode(int $attrType, string $data, string $transactionId): array
    {
        $attribute = MessageAttribute::tryFrom($attrType);

        if ($attribute === null) {
            throw new InvalidArgumentException("Invalid attribute type: $attrType");
        }

        $unpacked = match (self::CODECS[$attribute->value]) {
            self::CODEC_ADDRESS => self::unpackAddress($data),
            self::CODEC_XOR_ADDRESS => self::unpackXorAddress($data, $transactionId),
            self::CODEC_UNSIGNED => self::unpackUnsigned($data),
            self::CODEC_UNSIGNED_SHORT => self::unpackUnsignedShort($data),
            self::CODEC_UNSIGNED_64 => self::unpackUnsigned64($data),
            self::CODEC_ERROR_CODE => self::unpackErrorCode($data),
            self::CODEC_VALUE => $data,
            self::CODEC_NULL => null,
        };

        return [$attribute->name, $unpacked];
    }

    /**
     * Perform the STUN XOR operation on an address.
     *
     * @param string $data The address data.
     * @param string $transactionId The transaction ID.
     * @return string The XORed address.
     */
    public static function xorAddress(string $data, string $transactionId): string
    {
        $xpad = pack('nN', self::COOKIE >> 16, self::COOKIE) . $transactionId;
        $xdata = substr($data, 0, 2);
        for ($i = 2, $len = strlen($data); $i < $len; $i++) {
            $xdata .= chr(ord($data[$i]) ^ ord($xpad[$i - 2]));
        }
        return $xdata;
    }

    /**
     * Pack an address.
     *
     * @param mixed $data The address, which must be an InternetAddress.
     * @return string The packed address.
     */
    public static function packAddress(mixed $data): string
    {
        if (!$data instanceof InternetAddress) {
            throw new InvalidArgumentException('STUN address attributes must be InternetAddress objects');
        }

        $protocol = $data->getVersion() === InternetAddressVersion::IPv4
            ? self::IPV4_PROTOCOL
            : self::IPV6_PROTOCOL;

        return pack('C2n', 0, $protocol, $data->getPort()) . $data->getAddressBytes();
    }

    /**
     * Validate and return a raw attribute value (a byte string) for CODEC_VALUE attributes.
     *
     * @param mixed $data The raw value.
     * @return string|null The validated raw value.
     */
    private static function packRawValue(mixed $data): ?string
    {
        if ($data !== null && !is_string($data)) {
            throw new InvalidArgumentException('Raw STUN attribute value must be a string or null');
        }
        return $data;
    }

    /**
     * Unpack an address.
     *
     * @param string $data The packed address data.
     * @return InternetAddress The unpacked address.
     * @throws InvalidArgumentException If the data is invalid.
     */
    public static function unpackAddress(string $data): InternetAddress
    {
        if (strlen($data) < 4) {
            throw new InvalidArgumentException("STUN address length is less than 4 bytes");
        }
        $header = unpack('Creserved/Cprotocol/nport', substr($data, 0, 4));
        if ($header === false) {
            throw new InvalidArgumentException("Failed to unpack STUN address header");
        }
        $protocol = (int) $header['protocol'];
        $port = (int) $header['port'];

        $address = substr($data, 4);

        if ($protocol === self::IPV4_PROTOCOL) {
            if (strlen($address) !== 4) {
                throw new InvalidArgumentException("STUN address has invalid length for IPv4");
            }
        } elseif ($protocol === self::IPV6_PROTOCOL) {
            if (strlen($address) !== 16) {
                throw new InvalidArgumentException("STUN address has invalid length for IPv6");
            }
        } else {
            throw new InvalidArgumentException("STUN address has unknown protocol");
        }

        $textAddress = inet_ntop($address);
        if ($textAddress === false) {
            throw new InvalidArgumentException("STUN address bytes are not a valid IP address");
        }
        assert($port >= 0 && $port <= 65535);
        return new InternetAddress($textAddress, $port);
    }

    /**
     * Pack an XORed address.
     *
     * @param mixed $data The address, which must be an InternetAddress.
     * @param string $transactionId The transaction ID.
     * @return string The packed XORed address.
     */
    public static function packXorAddress(mixed $data, string $transactionId): string
    {
        return self::xorAddress(self::packAddress($data), $transactionId);
    }

    /**
     * Unpack an XORed address.
     *
     * @param string $data The packed XORed address data.
     * @param string $transactionId The transaction ID.
     * @return InternetAddress The unpacked XORed address.
     * @throws InvalidArgumentException If the data is invalid.
     */
    public static function unpackXorAddress(string $data, string $transactionId): InternetAddress
    {
        return self::unpackAddress(self::xorAddress($data, $transactionId));
    }

    /**
     * Pack an error code.
     *
     * @param mixed $data The [code, reason] pair.
     * @return string The packed error code.
     */
    public static function packErrorCode(mixed $data): string
    {
        if (!is_array($data) || !isset($data[0], $data[1])) {
            throw new InvalidArgumentException('STUN error-code attribute must be a [code, reason] pair');
        }
        $code = (int) $data[0];
        $reason = (string) $data[1];
        return pack('nCC', 0, intdiv($code, 100), $code % 100) . $reason;
    }

    /**
     * Unpack an error code.
     *
     * @param string $data The packed error code data.
     * @return array{int, string} The unpacked error code and reason.
     * @throws InvalidArgumentException If the data is invalid.
     */
    public static function unpackErrorCode(string $data): array
    {
        if (strlen($data) < 4) {
            throw new InvalidArgumentException("STUN error code is less than 4 bytes");
        }
        $header = unpack('nreserved/CcodeHigh/CcodeLow', substr($data, 0, 4));
        if ($header === false) {
            throw new InvalidArgumentException("Failed to unpack STUN error code");
        }
        $codeHigh = (int) $header['codeHigh'];
        $codeLow = (int) $header['codeLow'];
        $reason = substr($data, 4);
        return [$codeHigh * 100 + $codeLow, $reason];
    }

    /**
     * Pack an unsigned integer.
     *
     * @param mixed $data The value to pack.
     * @return string The packed unsigned integer.
     */
    public static function packUnsigned(mixed $data): string
    {
        if (!is_int($data)) {
            throw new InvalidArgumentException('STUN unsigned attribute must be an integer');
        }
        return pack('N', $data);
    }

    /**
     * Unpack an unsigned integer.
     *
     * @param string $data The packed value.
     * @return int The unpacked unsigned integer.
     */
    public static function unpackUnsigned(string $data): int
    {
        $unpacked = unpack('Nvalue', $data);
        if ($unpacked === false) {
            throw new InvalidArgumentException("Failed to unpack unsigned integer");
        }
        return (int) $unpacked['value'];
    }

    /**
     * Pack an unsigned short integer.
     *
     * @param mixed $data The value to pack.
     * @return string The packed unsigned short integer.
     */
    public static function packUnsignedShort(mixed $data): string
    {
        if (!is_int($data)) {
            throw new InvalidArgumentException('STUN unsigned-short attribute must be an integer');
        }
        return pack('n', $data) . "\x00\x00";
    }

    /**
     * Unpack an unsigned short integer from the data.
     *
     * @param string $data The packed value.
     * @return int The unpacked unsigned short integer.
     */
    public static function unpackUnsignedShort(string $data): int
    {
        $unpacked = unpack('nvalue', substr($data, 0, 2));
        if ($unpacked === false) {
            throw new InvalidArgumentException("Failed to unpack unsigned short integer");
        }
        return (int) $unpacked['value'];
    }

    /**
     * Pack a signed 64-bit integer in network byte order.
     *
     * @param mixed $data The value to pack.
     * @return string The packed 64-bit integer.
     */
    public static function packUnsigned64(mixed $data): string
    {
        if (!is_int($data)) {
            throw new InvalidArgumentException('STUN unsigned-64 attribute must be an integer');
        }
        return pack('J', $data);
    }

    /**
     * Unpack a signed 64-bit integer in network byte order.
     *
     * @param string $data The packed value.
     * @return int The unpacked signed 64-bit integer.
     */
    public static function unpackUnsigned64(string $data): int
    {
        $unpacked = unpack('Jvalue', $data);
        if ($unpacked === false) {
            throw new InvalidArgumentException("Failed to unpack 64-bit integer");
        }
        return (int) $unpacked['value'];
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\STUN\Message;

use Amp\Socket\InternetAddress;
use Webrtc\Exception\InvalidArgumentException;
use Webrtc\STUN\Enum\MessageClass;
use Webrtc\STUN\Enum\MessageMethod;

interface MessageInterface
{
    public function attributes(): MessageAttributeCollection;

    public function getTransactionId(): string;

    public function setTransactionId(string $transactionId): void;

    public function getMessageClass(): MessageClass;

    public function getMessageMethod(): MessageMethod;

    public function addMessageIntegrity(string $key): void;

    public function encode(): string;

    public static function decode(string $data, ?string $integrityKey): static;

    public function humanReadable(): string;

    public function __toString(): string;

    /**
     * @param array<string, InternetAddress|array<array-key, mixed>|int|string|null> $attributes
     */
    public static function new(MessageClass $messageClass, MessageMethod $messageMethod, array $attributes = [], ?string $integrityKey = null): MessageInterface;
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\STUN\Message;

/**
 * Class to handle message integrity and fingerprint calculations.
 */
final class MessageIntegrity
{
    public const HEADER_LENGTH = 20;
    private const INTEGRITY_LENGTH = 24;
    private const FINGERPRINT_LENGTH = 8;
    private const FINGERPRINT_XOR = 0x5354554E;

    /**
     * Set the body length in the message data.
     *
     * @param string $data The message data.
     * @param int $length The body length to set.
     * @return string The modified message data with the new length.
     */
    private static function setBodyLength(string $data, int $length): string
    {
        return substr($data, 0, 2) . pack("n", $length) . substr($data, 4);
    }

    /**
     * Calculate the message fingerprint.
     *
     * @param string $data The message data.
     * @return int The calculated fingerprint.
     */
    public static function messageFingerprint(string $data): int
    {
        $checkData = static::setBodyLength($data, strlen($data) - self::HEADER_LENGTH + self::FINGERPRINT_LENGTH);
        return crc32($checkData) ^ self::FINGERPRINT_XOR;
    }

    /**
     * Calculate the message integrity.
     *
     * @param string $data The message data.
     * @param string $key The key for HMAC.
     * @return string The calculated HMAC.
     */
    public static function messageIntegrity(string $data, string $key): string
    {
        $checkData = static::setBodyLength($data, strlen($data) - self::HEADER_LENGTH + self::INTEGRITY_LENGTH);
        return hash_hmac('sha1', $checkData, $key, true);
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\STUN\Message;

use Amp\Socket\InternetAddress;
use Stringable;
use Random\RandomException;
use Webrtc\Exception\InvalidArgumentException;
use Webrtc\STUN\Enum\MessageAttribute;
use Webrtc\STUN\Enum\MessageClass;
use Webrtc\STUN\Enum\MessageMethod;
use Webrtc\STUN\Utils;

/**
 * Class Message
 * Represents a WebRTC message with attributes.
 *
 * @link https://datatracker.ietf.org/doc/html/rfc5389#page-10
 */
final class Message implements MessageInterface
{
    private string $transactionId;
    private MessageAttributeCollection $_attributes;

    /**
     * Message constructor.
     *
     * @param MessageClass $messageClass
     * @param MessageMethod $messageMethod
     * @throws RandomException
     */
    public function __construct(private readonly MessageClass $messageClass, private readonly MessageMethod $messageMethod)
    {
        $this->transactionId = random_bytes(12);
        $this->_attributes = new MessageAttributeCollection;
    }

    /**
     * Get the message attributes.
     *
     * @return MessageAttributeCollection
     */
    #[\Override]
    public function attributes(): MessageAttributeCollection
    {
        return $this->_attributes;
    }

    /**
     * Get the transaction ID.
     *
     * @return string
     */
    #[\Override]
    public function getTransactionId(): string
    {
        return $this->transactionId;
    }

    /**
     * @return MessageClass
     */
    #[\Override]
    public function getMessageClass(): MessageClass
    {
        return $this->messageClass;
    }

    /**
     * @return MessageMethod
     */
    #[\Override]
    public function getMessageMethod(): MessageMethod
    {
        return $this->messageMethod;
    }

    /**
     * Set the transaction ID.
     *
     * @param string $transactionId
     */
    #[\Override]
    public function setTransactionId(string $transactionId): void
    {
        $this->transactionId = $transactionId;
    }

    /**
     * Add message integrity attributes.
     *
     * @param string $key The key for HMAC.
     */
    #[\Override]
    public function addMessageIntegrity(string $key): void
    {
        $this->_attributes->add(MessageAttribute::MESSAGE_INTEGRITY, MessageIntegrity::messageIntegrity($this->encode(), $key));
        $this->_attributes->add(MessageAttribute::FINGERPRINT, MessageIntegrity::messageFingerprint($this->encode()));
    }

    /**
     * Encode the message to bytes.
     *
     * @return string
     */
    #[\Override]
    public function encode(): string
    {
        $encodedAttributes = $this->getEncodedAttributes();
        return pack(
                'nnNa12',
                $this->messageMethod->value | $this->messageClass->value,
                strlen($encodedAttributes),
                MessageAttributeEncoder::COOKIE,
                $this->transactionId
            ) . $encodedAttributes;
    }

    /**
     * Encode the attributes message to bytes
     *
     * @return string
     */
    private function getEncodedAttributes(): string
    {
        $data = '';
        foreach ($this->_attributes as $attrName => $attrValue) {
            [$attrType, $packedData] = MessageAttributeEncoder::encode($attrName, $attrValue, $this->transactionId);
            $packedBytes = $packedData === null ? '' : $packedData;
            $attrLen = strlen($packedBytes);
            $padLen = Utils::paddingLength($attrLen);
            $data .= pack('nn', $attrType, $attrLen) . $packedBytes . str_repeat("\0", $padLen);
        }

        return $data;
    }

    /**
     * Decode a message from bytes.
     *
     * @param string $data The message data.
     * @param ?string $integrityKey Optional integrity key for HMAC validation.
     * @return static
     * @throws InvalidArgumentException If the message is invalid.
     * @throws RandomException
     */
    #[\Override]
    public static function decode(string $data, ?string $integrityKey = null): static
    {
        if (strlen($data) < MessageIntegrity::HEADER_LENGTH) {
            throw new InvalidArgumentException("STUN message length is less than 20 bytes");
        }

        $header = unpack('nmessageType/nlength/Ncookie/a12transactionId', substr($data, 0, MessageIntegrity::HEADER_LENGTH));
        if ($header === false) {
            throw new InvalidArgumentException("Failed to unpack STUN message header");
        }
        $messageType = (int) $header['messageType'];
        $length = (int) $header['length'];
        $transactionId = (string) $header['transactionId'];

        if (strlen($data) !== MessageIntegrity::HEADER_LENGTH + $length) {
            throw new InvalidArgumentException("STUN message length does not match");
        }

        if (!($class = MessageClass::tryFrom($messageType & 0x0110) )|| !($method = MessageMethod::tryFrom($messageType & 0x3EEF))) {
            throw new InvalidArgumentException("STUN message type does not match");
        }

        $message = new static($class, $method);
        $message->setTransactionId($transactionId);
        $message->attributes()->merge(self::getDecodedAttributes($data, $transactionId, $integrityKey));

        return $message;
    }

    /**
     * Decode the attribute message from bytes
     *
     * @param string $data
     * @param string $transactionId
     * @param string|null $integrityKey
     * @return array<string, InternetAddress|array<array-key, mixed>|int|string|null>
     */
    private static function getDecodedAttributes(string $data, string $transactionId, ?string $integrityKey): array
    {
        $attributes = [];
        $pos = MessageIntegrity::HEADER_LENGTH;

        while ($pos <= strlen($data) - 4) {

            $attrHeader = unpack('nattrType/nattrLen', substr($data, $pos, 4));
            if ($attrHeader === false) {
                throw new InvalidArgumentException("Failed to unpack STUN attribute header");
            }
            $attrType = (int) $attrHeader['attrType'];
            $attrLen = (int) $attrHeader['attrLen'];

            $value = substr($data, $pos + 4, $attrLen);
            $padLen = Utils::paddingLength($attrLen);

            // Unrecognised attribute types are comprehension-optional: skip them, as before.
            // But a *known* attribute that fails to decode is a corrupt message and must not be
            // silently dropped â€” doing so previously let a malformed FINGERPRINT or
            // MESSAGE-INTEGRITY attribute skip the verification below and be accepted anyway.
            if (MessageAttribute::tryFrom($attrType) === null) {
                $pos += 4 + $attrLen + $padLen;
                continue;
            }

            [$attrName, $unpackedData] = MessageAttributeEncoder::decode($attrType, $value, $transactionId);
            $attributes[$attrName] = $unpackedData;

            if ($attrName === MessageAttribute::FINGERPRINT->name && $attributes[$attrName] !== MessageIntegrity::messageFingerprint(substr($data, 0, $pos))) {
                throw new InvalidArgumentException("STUN message fingerprint does not match");
            }

            if ($attrName === MessageAttribute::MESSAGE_INTEGRITY->name && $integrityKey !== null
                && $attributes[$attrName] !== MessageIntegrity::messageIntegrity(substr($data, 0, $pos), $integrityKey)) {
                throw new InvalidArgumentException("STUN message integrity does not match");
            }

            $pos += 4 + $attrLen + $padLen;
        }

        return $attributes;
    }

    /**
     * Create a new message.
     *
     * @param MessageClass $messageClass The message class.
     * @param MessageMethod $messageMethod The message method.
     * @param array<string, InternetAddress|array<array-key, mixed>|int|string|null> $attributes The message attributes.
     * @param string|null $integrityKey
     * @return Message The Message object.
     * @throws RandomException
     */
    #[\Override]
    public static function new(MessageClass $messageClass, MessageMethod $messageMethod, array $attributes = [], ?string $integrityKey = null): MessageInterface
    {
        $message = new static($messageClass, $messageMethod);
        $message->attributes()->merge($attributes);
        if ($integrityKey !== null && $integrityKey !== '') {
            $message->addMessageIntegrity($integrityKey);
        }

        return $message;
    }

    /**
     * @return string
     */
    #[\Override]
    public function __toString(): string
    {
        return $this->encode();
    }

    /**
     * Convert a Message object to a human-readable message
     *
     * @return string
     */
    #[\Override]
    public function humanReadable(): string
    {
        $info = [
            "ID: " . bin2hex($this->transactionId),
            "Class: {$this->messageClass->name}",
            "Method: {$this->messageMethod->name}",
            "Attributes: " . $this->getAttributesInfo()
        ];

        return implode(" - ", $info);
    }

    /**
     * Convert attributes to human-readable
     *
     * @return string
     */
    private function getAttributesInfo(): string
    {
        if(count($this->attributes()) === 0) {
            return "No Attributes";
        }

        $attr = '[';

        foreach ($this->attributes()->all() as $attrName => $attrVal) {
            if (is_array($attrVal)) {
                $rendered = "(" . implode(", ", array_map(
                    static fn(mixed $v): string => is_scalar($v) ? (string) $v : get_debug_type($v),
                    $attrVal
                )) . ")";
            } elseif (is_scalar($attrVal) || $attrVal instanceof Stringable) {
                $rendered = (string) $attrVal;
            } else {
                $rendered = get_debug_type($attrVal);
            }
            if ($attrName === "MESSAGE_INTEGRITY") {
                $rendered = bin2hex($rendered);
            }
            $attr .= $attrName . ": " . $rendered . ", ";
        }

        return substr($attr, 0, -2) . "]";
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\STUN\Message;

use Amp\Socket\InternetAddress;
use ArrayIterator;
use Countable;
use IteratorAggregate;
use Traversable;
use Webrtc\STUN\Enum\MessageAttribute;

/**
 * Class representing a collection of message attributes.
 *
 * @psalm-type AttributeValue = InternetAddress|array<array-key, mixed>|int|string|null
 * @implements IteratorAggregate<string, InternetAddress|array<array-key, mixed>|int|string|null>
 */
final class MessageAttributeCollection implements Countable, IteratorAggregate
{
    /**
     * Array of attributes.
     *
     * @var array<string, AttributeValue>
     */
    private array $attributes = [];

    /**
     * MessageAttribute constructor.
     *
     * @param array<string, AttributeValue>|null $attributes Optional initial attributes.
     */
    public function __construct(?array $attributes = [])
    {
        if ($attributes !== null) {
            foreach ($attributes as $key => $value) {
                $this->attributes[$key] = $value;
            }
        }
    }

    /**
     * Get the first attribute value.
     *
     * @return AttributeValue First attribute value or null if empty.
     */
    public function first(): mixed
    {
        $first = reset($this->attributes);
        return $first === false ? null : $first;
    }

    /**
     * Get the last attribute value.
     *
     * @return AttributeValue Last attribute value or null if empty.
     */
    public function end(): mixed
    {
        $last = end($this->attributes);
        return $last === false ? null : $last;
    }

    /**
     * Add an attribute.
     *
     * @param MessageAttribute $attribute Attribute key.
     * @param AttributeValue $value
     * @return self
     */
    public function add(MessageAttribute $attribute, InternetAddress|array|int|string|null $value = null): self
    {
//        $messageAttrExist = (bool)array_filter(MessageAttribute::cases(), fn($attribute) => $attribute->name === $key);
//
//        if (!$messageAttrExist) {
//            throw new InvalidArgumentException("Invalid attribute name.");
//        }

        $this->attributes[$attribute->name] = $value;
        return $this;
    }

    /**
     * Remove an attribute.
     *
     * @param MessageAttribute $attribute Attribute key to remove.
     * @return self
     */
    public function remove(MessageAttribute $attribute): self
    {
        unset($this->attributes[$attribute->name]);
        return $this;
    }

    /**
     * Get all attributes.
     *
     * @return array<string, AttributeValue>
     */
    public function all(): array
    {
        return $this->attributes;
    }

    /**
     * Get all attribute keys.
     *
     * @return list<string>
     */
    public function keys(): array
    {
        return array_keys($this->attributes);
    }

    /**
     * Get all attribute values.
     *
     * @return list<AttributeValue>
     */
    public function values(): array
    {
        return array_values($this->attributes);
    }

    /**
     * Merge attributes into the current attributes.
     *
     * @param array<string, AttributeValue> $attributes Attributes to merge.
     * @return void
     */
    public function merge(array $attributes): void
    {
        foreach ($attributes as $key => $value) {
            $this->attributes[$key] = $value;
        }
    }

    /**
     * Get an attribute by key.
     *
     * @param MessageAttribute|string $attribute Attribute.
     * @param AttributeValue $default Default value if attribute is not found.
     * @return AttributeValue
     */
    public function get(MessageAttribute|string $attribute, InternetAddress|array|int|string|null $default = null): mixed
    {
        return $this->attributes[is_string($attribute) ? $attribute : $attribute->name] ?? $default;
    }

    /**
     * Apply a callback to all attributes.
     *
     * @param callable(AttributeValue): AttributeValue $func Callback function.
     * @return void
     */
    public function map(callable $func): void
    {
        $this->attributes = array_map($func, $this->attributes);
    }

    /**
     * if the attribute exists
     *
     * @param MessageAttribute|string $attribute Attribute.
     * @return bool
     */
    public function has(MessageAttribute|string $attribute): bool
    {
        return array_key_exists(is_string($attribute) ? $attribute : $attribute->name, $this->attributes);
    }

    /**
     * Get the count of attributes.
     *
     * @return int
     */
    #[\Override]
    public function count(): int
    {
        return count($this->attributes);
    }

    /**
     * {@inheritdoc}
     *
     * @return ArrayIterator<string, AttributeValue>
     */
    #[\Override]
    public function getIterator(): Traversable
    {
        return new ArrayIterator($this->attributes);
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\STUN;

/**
 * STUN Utility Class
 *
 * Provides helper methods for STUN (Session Traversal Utilities for NAT) protocol operations
 */
final class Utils
{
    /**
     * Calculate required padding length for STUN attributes
     *
     * STUN attributes are padded to 4-byte boundaries. This method calculates
     * how many additional bytes are needed to reach the next 4-byte boundary.
     *
     * @param int $length The original length of the attribute/data
     * @return int The number of padding bytes needed (0-3)
     *
     * @example
     * <code>
     * Utils::paddingLength(5); // returns 3 (since 5 + 3 = 8, which is divisible by 4)
     * Utils::paddingLength(4); // returns 0
     * Utils::paddingLength(7); // returns 1
     * </code>
     */
    public static function paddingLength(int $length): int
    {
        $rest = $length % 4;
        return $rest === 0 ? 0 : 4 - $rest;
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\STUN;

use Amp\Socket\InternetAddress;

interface BaseProtocolInterface
{
    public function send(string $data, ?InternetAddress $remoteAddress = null): void;

    public function close(): void;

    public function end(): void;

    public function resume(): void;

    public function pause(): void;

    public function getLocalAddress(): InternetAddress;

    public function getLocalHost(): string;

    public function getLocalPort(): int;

    public function getRemoteAddress(): ?InternetAddress;

    public function getCandidate(): ?IceCandidateInterface;

    public function setCandidate(IceCandidateInterface $candidate): void;
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\STUN;

use Amp\Socket\InternetAddress;
use Throwable;
use Webrtc\STUN\Message\MessageInterface;

interface ReceiverInterface
{
    public function onDataReceived(string $data, int $componentId): void;
    public function onRequestReceived(MessageInterface $message, InternetAddress $address, IceConnectionProtocolInterface $protocol, string $data): void;
    /**
     * Called when the socket of a protocol closed.
     *
     * @param IceConnectionProtocolInterface|null $protocol The protocol whose socket closed: a receiver
     *                                                       can have several.
     */
    public function onClose(?IceConnectionProtocolInterface $protocol = null): void;
    public function onError(Throwable $e): void;
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\STUN\Exception;

use Webrtc\Exception\WebrtcExceptionInterface;

interface TransactionExceptionInterface extends WebrtcExceptionInterface
{

}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\STUN\Exception;

use Webrtc\STUN\Message\MessageInterface;
use Webrtc\Exception\Exception;

/**
 * Base class for STUN transaction errors.
 */
class TransactionException extends Exception implements TransactionExceptionInterface
{
    protected ?MessageInterface $stunMessage = null;

    public function getStunMessage(): ?MessageInterface
    {
        return $this->stunMessage;
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\STUN\Exception;

/**
 * Exception class for STUN transaction timeouts.
 */
final class TransactionTimeoutException extends TransactionException
{
    public function __construct(?\Throwable $previous = null)
    {
        parent::__construct("STUN transaction timed out", previous: $previous);
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\STUN\Exception;

use Webrtc\STUN\Enum\MessageAttribute;
use Webrtc\STUN\Message\MessageInterface;

/**
 * Exception class for failed STUN transactions.
 */
final class TransactionFailedException extends TransactionException
{
    public function __construct(MessageInterface $message)
    {
        $this->stunMessage = $message;
        parent::__construct($this->getMessageText());
    }

    private function getMessageText(): string
    {
        $out = "STUN transaction failed";
        $stunMessage = $this->stunMessage;
        if ($stunMessage !== null && $stunMessage->attributes()->has(MessageAttribute::ERROR_CODE)) {
            $errorCode = $stunMessage->attributes()->get(MessageAttribute::ERROR_CODE);
            if (is_array($errorCode)) {
                $out .= sprintf(" (%s - %s)", (string) ($errorCode[0] ?? ""), (string) ($errorCode[1] ?? ""));
            }
        }
        return $out;
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\STUN;

use Amp\DeferredFuture;
use Amp\Socket\InternetAddress;
use Revolt\EventLoop;
use Webrtc\Mixin\SerializableState;
use Webrtc\STUN\Enum\MessageClass;
use Webrtc\STUN\Exception\TransactionFailedException;
use Webrtc\STUN\Exception\TransactionTimeoutException;
use Webrtc\STUN\Message\MessageInterface;

/**
 * Class representing a STUN transaction.
 *
 * @see https://datatracker.ietf.org/doc/html/rfc5389#section-7.2 Sending the Request or Indication
 */
final class Transaction implements TransactionInterface
{
    private const RETRY_MAX = 5;
    private const RETRY_RTO = 0.5;
    private ?InternetAddress $address;

    /** @var DeferredFuture<array{MessageInterface, InternetAddress|null}> */
    private DeferredFuture $deferred;
    private MessageInterface $message;
    private float $timeoutDelay;
    private IceConnectionProtocolInterface $transport;
    private int $tries = 0;
    private int $triesMax;
    private bool $isResolvedOrReject = false;

    /** Handle of the pending retransmission timer, so it can be cancelled once we are done. */
    private ?string $timer = null;

    /**
     * Transaction constructor.
     *
     * @param MessageInterface $message The STUN request message.
     * @param InternetAddress|null $address The destination address.
     * @param IceConnectionProtocolInterface $transport The protocol object.
     * @param int|null $retransmissions Number of retransmissions, default is RETRY_MAX.
     */
    public function __construct(MessageInterface $message, ?InternetAddress $address, IceConnectionProtocolInterface $transport, ?int $retransmissions = null)
    {
        $this->address = $address;
        /** @var DeferredFuture<array{MessageInterface, InternetAddress|null}> */
        $this->deferred = new DeferredFuture();
        $this->message = $message;
        $this->timeoutDelay = self::RETRY_RTO;
        $this->transport = $transport;
        $this->triesMax = 1 + ($retransmissions ?? self::RETRY_MAX);
    }

    /**
     * Handle the received response.
     *
     * @param MessageInterface $message The response message.
     * @param InternetAddress|null $address The source address.
     */
    #[\Override]
    public function responseReceived(MessageInterface $message, ?InternetAddress $address): void
    {
        if ($this->isResolvedOrReject) {
            return;
        }

        $this->settle();
        $this->transport->removeTransaction($message->getTransactionId());

        if ($message->getMessageClass() === MessageClass::RESPONSE) {
            $this->deferred->complete([$message, $address]);
        } else {
            $this->deferred->error(new TransactionFailedException($message));
        }
    }

    /**
     * Run the transaction and wait for its response.
     *
     * @return array{MessageInterface, InternetAddress|null} The response and where it came from.
     * @throws TransactionFailedException If the peer answered with an error response.
     * @throws TransactionTimeoutException If no answer arrived before the retries ran out.
     */
    #[\Override]
    public function execute(): array
    {
        $this->trySend();

        return $this->deferred->getFuture()->await();
    }

    /**
     * Retry the transaction.
     */
    private function trySend(): void
    {
        if ($this->tries >= $this->triesMax) {
            $this->settle();
            // Like an answered transaction: kept by the transport, it would hold on to the exception and so to everything
            // its trace does, for as long as the transport lives.
            $this->transport->removeTransaction($this->message->getTransactionId());
            $this->deferred->error(new TransactionTimeoutException);

            return;
        }

        try {
            $this->transport->sendMessage($this->message, $this->address);
        } catch (\Throwable $e) {
            // A retransmission runs from a timer callback, outside the caller's try/catch, so
            // a send that fails here (typically because the socket was closed while the
            // transaction was still in flight) would otherwise escape as an uncaught event-loop
            // exception. The transaction can no longer complete, so settle it as a timeout and
            // let the awaiting caller handle it like any other unanswered request.
            $this->settle();
            $this->transport->removeTransaction($this->message->getTransactionId());
            $this->deferred->error(new TransactionTimeoutException(previous: $e));

            return;
        }

        $this->timer = EventLoop::delay($this->timeoutDelay, $this->onRetransmitTimer(...));

        $this->timeoutDelay *= 2.0;
        $this->tries += 1;
    }

    /**
     * Mark the transaction finished and stop any pending retransmission.
     *
     * A timer left armed keeps the event loop alive and would retransmit a request that has
     * already been answered.
     */
    private function settle(): void
    {
        $this->isResolvedOrReject = true;

        if ($this->timer !== null) {
            EventLoop::cancel($this->timer);
            $this->timer = null;
        }
    }

    /**
     * Retransmit callback for the event-loop timer.
     *
     * Armed only as the first-class callable `$this->onRetransmitTimer(...)` passed to
     * EventLoop::delay(), including when the timer is rescheduled after unserialize. That callable
     * carries this method's private scope, so it does not need to be public.
     */
    private function onRetransmitTimer(): void
    {
        $this->timer = null;

        if (!$this->isResolvedOrReject) {
            $this->trySend();
        }
    }

    /**
     * @return DeferredFuture
     */
    public function getDeferred(): DeferredFuture
    {
        return $this->deferred;
    }

    /**
     * @return array<string, mixed>
     */
    public function __serialize(): array
    {
        return SerializableState::export($this, [
            // deferred is non-nullable and holds a suspended fiber that cannot be serialized;
            // leave it uninitialized on import (SerializableState skips this marker) so
            // __unserialize can install a fresh one rather than assigning null to a typed property.
            'deferred' => ['__uninitialized' => true],
            'timer' => null,
        ]);
    }

    /**
     * @param array<string, mixed> $data
     */
    public function __unserialize(array $data): void
    {
        SerializableState::import($this, $data);
        /** @var DeferredFuture<array{MessageInterface, InternetAddress|null}> $deferred */
        $deferred = new DeferredFuture();
        $this->deferred = $deferred;
        // Nobody awaits a restored transaction: the fiber that called execute() did not survive the
        // serialization. Its outcome (typically a timeout, when the retries were already exhausted)
        // must not surface as an unhandled future error and kill the process on garbage collection.
        $deferred->getFuture()->ignore();
        $this->timer = null;
        if (!$this->isResolvedOrReject) {
            $this->trySend();
        }
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\STUN;

/**
 * Minimal view of an ICE candidate needed by the STUN layer.
 *
 * The concrete implementation (Webrtc\ICE\RTCIceCandidate) lives in the ICE
 * package, which depends on STUN; STUN references candidates only through this
 * interface to avoid a circular dependency.
 */
interface IceCandidateInterface
{
    /**
     * Returns the ICE component id (1 for RTP, 2 for RTCP) this candidate belongs to.
     */
    public function getComponentId(): int;

    /**
     * Returns the candidate's IP address.
     */
    public function getHost(): string;

    /**
     * Returns the candidate's port number.
     */
    public function getPort(): int;
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\STUN\Trait;

use Amp\Socket\InternetAddress;
use Webrtc\Exception\InvalidArgumentException;
use Webrtc\STUN\Message\MessageInterface;
use Webrtc\STUN\Transaction;

/**
 * Trait Request
 *
 * Provides functionality for sending STUN/TURN messages and handling transactions
 * Includes methods for message transmission and request/response handling
 */
trait Request
{
    /** @var array<string, Transaction> Map of transaction IDs to Transaction objects */
    public array $transactionIds = [];

    /**
     * Remove a pending transaction by its ID.
     *
     * @param string $transactionId
     * @return void
     */
    public function removeTransaction(string $transactionId): void
    {
        unset($this->transactionIds[$transactionId]);
    }

    /**
     * Sends a STUN/TURN message to a specified address
     *
     * @param MessageInterface $message The message to send
     * @param InternetAddress|null $address The target address (null uses default remote address)
     * @return void
     */
    public function sendMessage(MessageInterface $message, ?InternetAddress $address): void
    {
        $this->logger?->debug("Send a STUN/TURN Message", ["Message" => $message->humanReadable(), "ToAddress" => $address ?? $this->remoteAddress]);
        $this->send((string)$message, $address);
    }

    /**
     * Initiates a STUN/TURN request transaction
     *
     * @param MessageInterface $message The request message to send
     * @param InternetAddress|null $address The target address (optional)
     * @param string|null $integrity_key The key for message integrity (optional)
     * @param int $retransmissions Number of retransmission attempts (default: 0)
     * @return array{MessageInterface, InternetAddress|null} The response and where it came from.
     * @throws InvalidArgumentException If the message transaction is already pending
     */
    public function request(
        MessageInterface $message,
        ?InternetAddress $address = null,
        ?string $integrity_key = null,
        int $retransmissions = 0
    ): array {
        if (isset($this->transactionIds[$message->getTransactionId()])) {
            throw new InvalidArgumentException("The message is in pending");
        }

        if (isset($integrity_key)) {
            $message->addMessageIntegrity($integrity_key);
        }

        $transaction = new Transaction($message, $address, $this, $retransmissions);
        $this->transactionIds[$message->getTransactionId()] = $transaction;

        return $transaction->execute();
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\STUN\Trait;

use Webrtc\STUN\IceCandidateInterface;

/**
 * Trait CandidateSetterGetter
 *
 * Provides getter and setter methods for the ICE candidate object
 */
trait CandidateSetterGetter
{
    /** @var IceCandidateInterface|null The ICE candidate object */
    private ?IceCandidateInterface $candidate = null;

    /**
     * Get the current ICE candidate
     *
     * @return IceCandidateInterface|null The current ICE candidate or null if is not set
     */
    public function getCandidate(): ?IceCandidateInterface
    {
        return $this->candidate;
    }

    /**
     * Set the ICE candidate
     *
     * @param IceCandidateInterface $candidate The ICE candidate to set
     * @return void
     */
    public function setCandidate(IceCandidateInterface $candidate): void
    {
        $this->candidate = $candidate;
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\STUN;

interface IceConnectionProtocolInterface extends BaseProtocolInterface, RequestInterface
{
    public function getId(): string;
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\STUN;

use Amp\Socket\InternetAddress;
use Webrtc\STUN\Message\MessageInterface;

interface RequestInterface
{
    public function sendMessage(MessageInterface $message, ?InternetAddress $address): void;

    public function removeTransaction(string $transactionId): void;

    /**
     * Send a request and wait for its response.
     *
     * @return array{MessageInterface, InternetAddress|null} The response and where it came from.
     */
    public function request(MessageInterface $message, ?InternetAddress $address, ?string $integrity_key, int $retransmissions = 0): array;
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\STUN;

use Webrtc\STUN\Trait\CandidateSetterGetter;

/**
 * Interface for a base protocol in WebRTC.
 */
abstract class BaseProtocol implements BaseProtocolInterface
{
    use CandidateSetterGetter;
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\STUN\Enum;


/**
 * Enum representing different message methods for WebRTC protocols.
 *
 * @link https://datatracker.ietf.org/doc/html/rfc5389#section-18.1
 * */
enum MessageMethod: int
{
    case BINDING = 0x1;
    case SHARED_SECRET = 0x2;
    case ALLOCATE = 0x3;
    case REFRESH = 0x4;
    case SEND = 0x6;
    case DATA = 0x7;
    case CREATE_PERMISSION = 0x8;
    case CHANNEL_BIND = 0x9;
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\STUN\Enum;

/**
 * Enum representing different message classes for WebRTC protocols.
 *
 * @link https://datatracker.ietf.org/doc/html/rfc5389#section-6
 * */
enum MessageClass: int
{
    case REQUEST = 0x000;
    case INDICATION = 0x010;
    case RESPONSE = 0x100;
    case ERROR = 0x110;
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\STUN\Enum;

/**
 * Enum representing different message attributes for WebRTC protocols.
 *
 * @link https://datatracker.ietf.org/doc/html/rfc5780#section-7
 * */
enum MessageAttribute: int
{
    case MAPPED_ADDRESS = 0x0001;
    case CHANGE_REQUEST = 0x0003;
    case SOURCE_ADDRESS = 0x0004;
    case CHANGED_ADDRESS = 0x0005;
    case USERNAME = 0x0006;
    case MESSAGE_INTEGRITY = 0x0008;
    case ERROR_CODE = 0x0009;
    case CHANNEL_NUMBER = 0x000C;
    case LIFETIME = 0x000D;
    case XOR_PEER_ADDRESS = 0x0012;
    case REALM = 0x0014;
    case NONCE = 0x0015;
    case XOR_RELAYED_ADDRESS = 0x0016;
    case REQUESTED_TRANSPORT = 0x0019;
    case XOR_MAPPED_ADDRESS = 0x0020;
    case PRIORITY = 0x0024;
    case USE_CANDIDATE = 0x0025;
    case SOFTWARE = 0x8022;
    case FINGERPRINT = 0x8028;
    case ICE_CONTROLLED = 0x8029;
    case ICE_CONTROLLING = 0x802A;
    case RESPONSE_ORIGIN = 0x802B;
    case OTHER_ADDRESS = 0x802C;

    /**
     * Resolve an attribute from its case name.
     *
     * @param string $name The attribute case name.
     * @return self|null The matching attribute, or null if the name is unknown.
     */
    public static function fromName(string $name): ?self
    {
        return match ($name) {
            'MAPPED_ADDRESS' => self::MAPPED_ADDRESS,
            'CHANGE_REQUEST' => self::CHANGE_REQUEST,
            'SOURCE_ADDRESS' => self::SOURCE_ADDRESS,
            'CHANGED_ADDRESS' => self::CHANGED_ADDRESS,
            'USERNAME' => self::USERNAME,
            'MESSAGE_INTEGRITY' => self::MESSAGE_INTEGRITY,
            'ERROR_CODE' => self::ERROR_CODE,
            'CHANNEL_NUMBER' => self::CHANNEL_NUMBER,
            'LIFETIME' => self::LIFETIME,
            'XOR_PEER_ADDRESS' => self::XOR_PEER_ADDRESS,
            'REALM' => self::REALM,
            'NONCE' => self::NONCE,
            'XOR_RELAYED_ADDRESS' => self::XOR_RELAYED_ADDRESS,
            'REQUESTED_TRANSPORT' => self::REQUESTED_TRANSPORT,
            'XOR_MAPPED_ADDRESS' => self::XOR_MAPPED_ADDRESS,
            'PRIORITY' => self::PRIORITY,
            'USE_CANDIDATE' => self::USE_CANDIDATE,
            'SOFTWARE' => self::SOFTWARE,
            'FINGERPRINT' => self::FINGERPRINT,
            'ICE_CONTROLLED' => self::ICE_CONTROLLED,
            'ICE_CONTROLLING' => self::ICE_CONTROLLING,
            'RESPONSE_ORIGIN' => self::RESPONSE_ORIGIN,
            'OTHER_ADDRESS' => self::OTHER_ADDRESS,
            default => null,
        };
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\STUN;

use Amp\Socket\InternetAddress;
use Psr\Log\LoggerInterface;

interface StunInterface extends IceConnectionProtocolInterface
{
    public static function create(
        ReceiverInterface $receiver,
        InternetAddress $address,
        ?LoggerInterface $logger = null,
        ?array $portRange = null
    ): StunInterface;
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\STUN;

use Amp\Socket\BindContext;
use Amp\Socket\InternetAddress;
use Amp\Socket\ResourceUdpSocket;
use Amp\Socket\UdpSocket;
use Throwable;
use Webrtc\Exception\InvalidArgumentException;
use Webrtc\Mixin\SerializableState;
use function Amp\async;
use function Amp\Socket\bindUdpSocket;

/**
 * User Datagram Protocol
 *
 * Handles the UDP communication for WebRTC.
 *
 * @see https://datatracker.ietf.org/doc/html/rfc768 User Datagram Protocol
 */
abstract class Datagram extends BaseProtocol
{
    /** The default remote address for communication. */
    protected ?InternetAddress $remoteAddress = null;

    /** Whether the receive loop should keep delivering datagrams. */
    private bool $paused = false;

    /**
     * Datagram constructor.
     *
     * @param UdpSocket $socket The socket to use for UDP communication
     */
    public function __construct(protected UdpSocket $socket)
    {
        $this->listen();
    }

    /**
     * Bind context shared by every UDP socket a connection opens.
     *
     * SO_REUSEPORT (mapped to SO_REUSEADDR on Windows) lets a deserialized connection rebind
     * to the same local port while the original socket still holds it â€” the exact situation a
     * serialize/unserialize cycle produces. Without it macOS and Windows reject the second bind
     * with "Address already in use"; Linux happens to tolerate it, which is why the gap only
     * showed on those platforms. Both the original and the rebind must set it for the kernel to
     * allow the shared port, so every bind in the package routes through here.
     */
    protected static function udpBindContext(): BindContext
    {
        return (new BindContext())->withReusePort();
    }

    /**
     * @return array<string, mixed>
     */
    public function __serialize(): array
    {
        $address = $this->socket->getAddress();

        return SerializableState::export($this, [
            'socket' => ['_udp' => [$address->getAddress(), $address->getPort()]],
        ]);
    }

    /**
     * @param array<string, mixed> $data
     */
    public function __unserialize(array $data): void
    {
        $bindHost = null;
        $bindPort = null;
        foreach ($data as $key => $value) {
            if (!is_array($value) || !isset($value['_udp']) || !is_array($value['_udp'])) {
                continue;
            }
            $udp = $value['_udp'];
            if (!isset($udp[0], $udp[1])) {
                continue;
            }
            $port = (int) $udp[1];
            if ($port < 0 || $port > 65535) {
                continue;
            }
            $bindHost = (string) $udp[0];
            $bindPort = $port;
            unset($data[$key]);
        }
        SerializableState::import($this, $data);
        if ($bindHost !== null && $bindPort !== null) {
            $this->socket = self::rebind($bindHost, $bindPort);
            $this->listen();
        }
    }

    /**
     * Rebind the datagram socket when resuming a serialized connection. The exact local address saved
     * before the restart may no longer be assignable â€” a temporary/privacy IPv6 address rotates, a
     * DHCP lease changed, an interface went down â€” which previously made a bind failure abort the whole
     * session's deserialization. Fall back to the same port on the matching wildcard address (so the
     * transport keeps its port), and finally to any free port, so a resume never fatals.
     */
    private static function rebind(string $host, int $port): UdpSocket
    {
        $context = self::udpBindContext();
        $wildcard = str_contains($host, ':') ? '[::]' : '0.0.0.0';
        foreach (["$host:$port", "$wildcard:$port", "$wildcard:0"] as $address) {
            try {
                [$host2, $port2] = self::splitAddress($address);
                return bindUdpSocket(new InternetAddress($host2, $port2), $context);
            } catch (Throwable) {
                // Try the next, more permissive, bind target.
            }
        }
        return bindUdpSocket(new InternetAddress('0.0.0.0', 0), $context);
    }

    /**
     * Split a "host:port" (host possibly a bracketed IPv6) into [host, port] for InternetAddress.
     *
     * @return array{0: string, 1: int<0, 65535>}
     */
    private static function splitAddress(string $address): array
    {
        $pos = strrpos($address, ':');
        if ($pos === false) {
            return [trim($address, '[]'), 0];
        }
        $host = substr($address, 0, $pos);
        $port = max(0, min(65535, (int) substr($address, $pos + 1)));
        return [trim($host, '[]'), $port];
    }

    /**
     * Deliver incoming datagrams to onReceived() until the socket closes.
     *
     * Reading runs in its own fiber rather than through an event emitter: the socket hands
     * over one datagram per receive() call, so the loop is the natural shape and errors
     * propagate to onError() instead of an unobserved rejection.
     */
    protected function listen(): void
    {
        // The receive loop must not strongly reference $this, or the fiber the event loop keeps
        // alive to run it would pin this connection forever â€” an unset() followed by
        // gc_collect_cycles() could never reclaim it, and its bound port would leak. It holds the
        // socket (so it stays parked in receive()) and only a weak reference back to the owner, so
        // dropping the last real reference lets the connection be collected; __destruct() then
        // closes the socket, which unblocks receive() and lets this fiber unwind.
        $weak = \WeakReference::create($this);
        $socket = $this->socket;
        async(static function () use ($weak, $socket): void {
            try {
                while (true) {
                    $received = $socket->receive();
                    if ($received === null) {
                        // receive() === null covers two very different events. A genuine close nulls
                        // the underlying resource (isClosed() === true) and ends the loop. On Windows,
                        // though, a UDP send that draws an ICMP port-unreachable â€” a dead STUN/TURN
                        // server, or a peer whose port is not open yet â€” makes the socket's *next* recv
                        // fail once with WSAECONNRESET; amphp reports that one-shot error as
                        // receive() === null and cancels its read watcher, even though the socket is
                        // still bound and usable. Ending the loop there would tear down the only host
                        // candidate mid-negotiation (missing srflx candidates, "Binding check failed").
                        // Instead re-wrap the still-open resource in a fresh socket, which arms a new
                        // read watcher, and keep reading. POSIX never reaches this branch: it surfaces
                        // ICMP errors only on connected sockets, and these are unconnected, so recv
                        // returns null there only on a real close.
                        if ($socket->isClosed() || !$socket instanceof ResourceUdpSocket) {
                            break;
                        }
                        $resource = $socket->getResource();
                        if (!\is_resource($resource)) {
                            break;
                        }
                        $socket = new ResourceUdpSocket($resource);
                        $self = $weak->get();
                        if ($self === null) {
                            $socket->close();

                            return;
                        }
                        $self->socket = $socket;
                        unset($self);
                        continue;
                    }

                    $self = $weak->get();
                    if ($self === null) {
                        $socket->close();

                        return;
                    }
                    [$address, $data] = $received;

                    if ($self->paused) {
                        continue;
                    }
                    // Dispatch in its own fiber. Handling a datagram can block â€” answering a
                    // binding request may itself start a transaction and wait for its reply â€”
                    // and doing that inline stops this loop from reading, so the very replies
                    // being waited on never arrive and every transaction times out.
                    async(static fn () => $weak->get()?->onReceived($data, $address))->ignore();
                    unset($self);
                }

                $weak->get()?->onClose();
            } catch (Throwable $e) {
                $weak->get()?->onError($e);
            }
        });
    }

    /**
     * Release the bound socket when the connection is garbage-collected.
     *
     * The receive loop parks a fiber inside the socket's receive() call; closing the socket here
     * is what unblocks it so the fiber can unwind once the last reference to this connection is
     * gone. Closing a UDP socket sends nothing on the wire, so a peer sees only silence â€” exactly
     * the "the process went away" situation a serialize/unserialize cycle is meant to survive.
     */
    public function __destruct()
    {
        // socket is always set here: the constructor binds it, a successful unserialize rebinds
        // it, and __destruct never runs on an object whose __unserialize threw.
        $this->socket->close();
    }

    /**
     * Send data through the UDP socket
     *
     * @param string $data The data to send
     * @param InternetAddress|null $remoteAddress The target address (uses default remote address if null)
     * @return void
     */
    #[\Override]
    public function send(string $data, ?InternetAddress $remoteAddress = null): void
    {
        $remoteAddress ??= $this->remoteAddress;
        if ($remoteAddress === null) {
            throw new InvalidArgumentException('A remote address must be provided');
        }

        $this->socket->send($remoteAddress, $data);
    }

    /**
     * Close the UDP socket immediately
     *
     * @return void
     */
    #[\Override]
    public function close(): void
    {
        $this->socket->close();
    }

    /**
     * Close the UDP socket.
     *
     * A datagram socket has no write buffer to drain, so this is the same as close(); it is
     * kept because callers written against the previous stream-shaped API still use it.
     *
     * @return void
     */
    #[\Override]
    public function end(): void
    {
        $this->close();
    }

    /**
     * Resume delivering received datagrams
     *
     * @return void
     */
    #[\Override]
    public function resume(): void
    {
        $this->paused = false;
    }

    /**
     * Stop delivering received datagrams
     *
     * @return void
     */
    #[\Override]
    public function pause(): void
    {
        $this->paused = true;
    }

    /**
     * Get the local socket address
     *
     * @return InternetAddress The local socket address
     */
    #[\Override]
    public function getLocalAddress(): InternetAddress
    {
        return $this->socket->getAddress();
    }

    /**
     * Get the local host address
     *
     * @return string The local host IP address
     */
    #[\Override]
    public function getLocalHost(): string
    {
        return $this->getLocalAddress()->getAddress();
    }

    /**
     * Get the local port number
     *
     * @return int The local port number
     */
    #[\Override]
    public function getLocalPort(): int
    {
        return $this->getLocalAddress()->getPort();
    }

    /**
     * Get the remote address
     *
     * @return InternetAddress|null The remote address or null if not connected
     */
    #[\Override]
    public function getRemoteAddress(): ?InternetAddress
    {
        return $this->remoteAddress ?? null;
    }

    /**
     * Handle received messages (abstract method to be implemented by concrete classes)
     *
     * @param string $data The received data
     * @param InternetAddress $peerAddress The address of the peer that sent the data
     * @return void
     */
    protected abstract function onReceived(string $data, InternetAddress $peerAddress): void;

    /**
     * Handle socket errors (abstract method to be implemented by concrete classes)
     *
     * @param Throwable $e The thrown exception
     * @return void
     */
    protected abstract function onError(Throwable $e): void;

    /**
     * Handle socket close events (abstract method to be implemented by concrete classes)
     *
     * @return void
     */
    protected abstract function onClose(): void;

}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\STUN;

use Amp\Socket\InternetAddress;
use Psr\Log\LoggerInterface;
use Ramsey\Uuid\Uuid;
use Random\RandomException;
use Amp\Socket\UdpSocket;
use Throwable;
use Webrtc\Exception\InvalidArgumentException;
use Webrtc\Exception\RuntimeException;
use Webrtc\STUN\Enum\MessageClass;
use Webrtc\STUN\Message\Message;
use Webrtc\STUN\Message\MessageInterface;
use Webrtc\STUN\Trait\Request;
use function Amp\Socket\bindUdpSocket;

/**
 * STUN (Session Traversal Utilities for NAT) Protocol Implementation
 *
 * Handles STUN protocol operations including message encoding/decoding,
 * transaction management, and communication with STUN servers.
 */
final class Stun extends Datagram implements StunInterface
{
    use Request;

    /** @var string Unique identifier for this STUN instance */
    private string $id;

    /**
     * The receiver of the messages, which owns this protocol: not owned by it, so that a pending
     * request doesn't keep the receiver alive.
     *
     * @var \WeakReference<ReceiverInterface>|null
     */
    private ?\WeakReference $receiver;

    /**
     * Constructor
     *
     * @param ReceiverInterface $receiver Message receiver handler
     * @param UdpSocket $socket Datagram socket
     * @param LoggerInterface|null $logger Optional PSR-3 logger
     */
    public function __construct(
        ReceiverInterface $receiver,
        UdpSocket $socket,
        private readonly ?LoggerInterface $logger = null
    ) {
        $this->receiver = \WeakReference::create($receiver);
        parent::__construct($socket);
        $this->id = Uuid::uuid4()->toString();
    }

    /**
     * @return array<string, mixed>
     */
    #[\Override]
    public function __serialize(): array
    {
        $data = parent::__serialize();
        $data[self::class . "\0receiver"] = $this->receiver?->get();

        return $data;
    }

    /**
     * @param array<string, mixed> $data
     */
    #[\Override]
    public function __unserialize(array $data): void
    {
        $key = self::class . "\0receiver";
        // Before the socket is listened to again.
        $this->receiver = isset($data[$key]) && $data[$key] instanceof ReceiverInterface ? \WeakReference::create($data[$key]) : null;
        unset($data[$key]);
        parent::__unserialize($data);
    }


    /**
     * Handle received messages
     *
     * Processes incoming STUN messages or forwards non-STUN data to receiver
     *
     * @param string $data The received raw data
     * @param InternetAddress $peerAddress The peer's IP address and port
     * @return void
     * @throws RandomException
     */
    #[\Override]
    protected function onReceived(string $data, InternetAddress $peerAddress): void
    {
        if ($message = $this->decodeMessage($data)) {
            $this->handleMessage($message, $peerAddress, $data);
        } else {
            $candidate = $this->getCandidate();
            if ($candidate !== null) {
                $this->receiver?->get()?->onDataReceived($data, $candidate->getComponentId());
            }
        }
    }

    /**
     * Decode a STUN message from raw data
     *
     * @param string $data Raw message data to decode
     * @return MessageInterface|false Decoded message object or false on failure
     * @throws RandomException
     */
    private function decodeMessage(string $data): MessageInterface|false
    {
        try {
            return Message::decode($data);
        } catch (InvalidArgumentException) {
            return false;
        }
    }

    /**
     * Process a decoded STUN message
     *
     * Handles different message types (requests, responses, errors) appropriately
     *
     * @param MessageInterface $message The decoded STUN message
     * @param InternetAddress $address Peer address
     * @param string $data Raw message data
     * @return void
     */
    private function handleMessage(MessageInterface $message, InternetAddress $address, string $data): void
    {
        $this->logger?->info(
            "A new STUN message has been received",
            [
                "Message" => $message->humanReadable(),
                "FromAddress" => $address
            ]
        );

        $messageClass = $message->getMessageClass();
        $transactionId = $message->getTransactionId();

        if (in_array($messageClass, [MessageClass::RESPONSE, MessageClass::ERROR])
            && isset($this->transactionIds[$transactionId])) {
            $transaction = $this->transactionIds[$transactionId];
            $transaction->responseReceived($message, $address);
        } elseif ($messageClass === MessageClass::REQUEST) {
            $this->receiver?->get()?->onRequestReceived($message, $address, $this, $data);
        }
    }

    /**
     * Handle socket errors
     *
     * @param Throwable $e The thrown exception
     * @return void
     */
    #[\Override]
    protected function onError(Throwable $e): void
    {
        $this->receiver?->get()?->onError($e);
    }

    /**
     * Handle socket close events
     *
     * @return void
     */
    #[\Override]
    protected function onClose(): void
    {
        $this->receiver?->get()?->onClose($this);
    }

    /**
     * Get the unique STUN instance ID
     *
     * @return string UUID string identifier
     */
    #[\Override]
    public function getId(): string
    {
        return $this->id;
    }

    /**
     * Create a new STUN instance
     *
     * Factory method to create and bind a new STUN server instance
     *
     * @param ReceiverInterface $receiver Message receiver handler
     * @param InternetAddress $address Address to bind to
     * @param LoggerInterface|null $logger Optional PSR-3 logger
     * @param array|null $portRange
     * @return StunInterface
     */
    #[\Override]
    public static function create(
        ReceiverInterface $receiver,
        InternetAddress $address,
        ?LoggerInterface $logger = null,
        ?array $portRange = null
    ): StunInterface {
        if ($portRange !== null && $portRange !== []) {
            $address = new InternetAddress(
                $address->getAddress(),
                self::getRandomPort($portRange, $address)
            );
        }

        try {
            $socket = bindUdpSocket($address, self::udpBindContext());
            return new self($receiver, $socket, $logger);
        } catch (Throwable $e) {
            throw new RuntimeException(
                sprintf("Could not bind to %s - %s", $address, $e->getMessage()),
                (int) $e->getCode(),
                $e
            );
        }
    }

    /**
     * Get a random available port within a given range
     *
     * @param array $portRange Array containing [minPort, maxPort]
     * @param InternetAddress $address Host address to test binding
     * @return int<0, 65535>
     * @throws RuntimeException If no available port is found
     */
    private static function getRandomPort(array $portRange, InternetAddress $address): int
    {
        $min = (int) ($portRange[0] ?? 0);
        $max = (int) ($portRange[1] ?? 0);

        if ($min < 1 || $max > 65535 || $min > $max) {
            throw new InvalidArgumentException("Invalid port range [$min, $max]");
        }

        $ports = range($min, $max);
        shuffle($ports);

        foreach ($ports as $port) {
            assert($port >= 0 && $port <= 65535);
            $candidateAddress = new InternetAddress($address->getAddress(), $port);
            try {
                // Deliberately a strict bind (no SO_REUSEPORT): this probe must only report a
                // port as free when nothing else holds it. The real socket opened in create()
                // adds the reuse flag so a deserialized connection can later rebind the port.
                $socket = bindUdpSocket($candidateAddress);
                $socket->close();

                return $port;
            } catch (Throwable) {
            }
        }

        throw new RuntimeException("No available ports found in range [$min, $max]");
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\STUN;

use Amp\Socket\InternetAddress;
use Webrtc\STUN\Message\MessageInterface;

interface TransactionInterface
{
    public function responseReceived(MessageInterface $message, ?InternetAddress $address): void;

    /**
     * Send the request and wait for its response.
     *
     * @return array{MessageInterface, InternetAddress|null} The response and where it came from.
     */
    public function execute(): array;
}
{
    "name": "danog/php-rtc-stun",
    "type": "library",
    "description": "STUN transactions and NAT discovery for PHP, ported to Amp v3 sockets and fibers. MadelineProto's PHP 8.2+ fork supports the modular danog/php-rtc stack.",
    "keywords": [
        "webrtc",
        "stun",
        "SessionTraversalUtilitiesForNAT"
    ],
    "homepage": "https://github.com/danog/php-rtc-stun",
    "license": "BSD-3-Clause",
    "authors": [
        {
            "name": "Amin Yazdanpanah",
            "email": "github@aminyazdanpanah.com"
        },
        {
            "name": "Sana Moniri",
            "email": "s.mo@quasarstream.com"
        },
        {
            "name": "Quasar Stream Community"
        },
        {
            "name": "Daniil Gentili",
            "email": "daniil@daniil.it"
        }
    ],
    "require": {
        "php-64bit": "^8.2",
        "amphp/amp": "^3.1.3",
        "amphp/socket": "^2.4.1",
        "danog/php-rtc-exception": "^1.1.5",
        "danog/php-rtc-mixin": "^2.0",
        "ramsey/uuid": "^4.9.3",
        "psr/log": "^3.0.2"
    },
    "require-dev": {
        "phpunit/phpunit": "^11.5 || ^12.0 || ^13.3.2",
        "mockery/mockery": "^1.6.15",
        "dg/bypass-finals": "dev-master",
        "bamarni/composer-bin-plugin": "^1.9.1"
    },
    "autoload": {
        "psr-4": {
            "Webrtc\\STUN\\": "src"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\Webrtc\\STUN\\": "tests"
        }
    },
    "minimum-stability": "stable",
    "scripts": {
        "post-install-cmd": [
            "@composer bin all install --ansi"
        ],
        "post-update-cmd": [
            "@composer bin all update --ansi"
        ]
    },
    "config": {
        "allow-plugins": {
            "bamarni/composer-bin-plugin": true
        }
    },
    "extra": {
        "bamarni-bin": {
            "bin-links": true,
            "forward-command": true
        }
    }
}
<?php

/**
 * This file is part of the PHP Wfinal final ebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\NTP;

use DateMalformedStringException;
use DateTimeImmutable;
use DateTimeZone;

/**
 * Class NetworkTimeProtocol
 *
 * This class provides functionality to work with NTP (Network Time Protocol) timestamps.
 * NTP timestamps are based on a custom epoch (January 1, 1900, 00:00:00 UTC) and are represented
 * as 64-bit fixed-point numbers, where the high 32 bits represent seconds and the low 32 bits
 * represent fractional seconds.
 *
 * The full 64-bit value does not fit in a *signed* PHP integer, so it is kept as the raw 64-bit
 * bit pattern (i.e. it may be negative) and every extraction masks off the bits it needs: this
 * yields the same results as unsigned arithmetic without requiring the GMP extension.
 */
final class NetworkTimeProtocol
{
    /** The NTP epoch (January 1, 1900, 00:00:00 UTC) as a Unix timestamp. */
    private const NTP_EPOCH = -2208988800; // strtotime('1900-01-01 00:00:00 UTC')

    /**
     * Get the current datetime in UTC.
     *
     * @return DateTimeImmutable The current datetime in UTC.
     * @throws DateMalformedStringException
     */
    public static function currentDatetime(): DateTimeImmutable
    {
        return new DateTimeImmutable('now', new DateTimeZone('UTC'));
    }

    /**
     * Get the current time in milliseconds since the NTP epoch.
     *
     * @return int The number of milliseconds since the NTP epoch.
     */
    public static function currentMs(): int
    {
        return (int) floor(microtime(true) * 1000.0) - self::NTP_EPOCH * 1000;
    }

    /**
     * Get the current NTP time as a 64-bit fixed-point number.
     *
     * @return array{int, int} The seconds and the fraction of a second, in units of 2^-32 seconds.
     */
    public static function currentNtpTime(): array
    {
        // Like fromDatetime(currentDatetime()), without creating and formatting date objects: this is
        // called for every packet that is sent.
        $now = gettimeofday();
        $high = ($now['sec'] - self::NTP_EPOCH) & 0xFFFFFFFF;
        $low = intdiv($now['usec'] * (1 << 32), 1000000) & 0xFFFFFFFF;

        return [$high, $low];
    }

    /**
     * Convert an NTP timestamp to a DateTimeImmutable object.
     *
     * @return DateTimeImmutable The corresponding DateTime object in UTC.
     * @throws DateMalformedStringException
     */
    public static function toDatetime(int $high, int $low): DateTimeImmutable
    {
        $microseconds = intdiv($low * 1000000, 1 << 32);
        $timestamp = self::NTP_EPOCH + $high;
        $datetime = new DateTimeImmutable('@' . $timestamp, new DateTimeZone('UTC'));

        return $datetime->modify("+$microseconds microseconds");
    }

    /**
     * Convert a DateTimeImmutable object to an NTP timestamp.
     *
     * @param DateTimeImmutable $dt The DateTime object to convert.
     * @return array
     */
    public static function fromDatetime(DateTimeImmutable $dt): array
    {
        $delta = $dt->getTimestamp() - self::NTP_EPOCH;
        $microseconds = (int) $dt->format('u'); // Microseconds

        $high = $delta & 0xFFFFFFFF; // Seconds since NTP epoch
        $low = intdiv($microseconds * (1 << 32), 1000000) & 0xFFFFFFFF; // Fractional seconds

        // Combine both halves into the raw 64-bit NTP bit pattern.
        return [$high, $low];
    }

    /**
     * Merge two 32-bit NTP halves back into the raw 64-bit bit pattern.
     *
     * The full 64-bit value does not fit in a *signed* PHP integer, so it is kept as the raw
     * bit pattern (i.e. it may be negative). Only integer arithmetic is used: no floating point
     * and no decimal-string round trips, so the result is exactly the value that {@see fromNtp()}
     * split.
     *
     * @param int $high The high 32 bits (whole seconds since the NTP epoch).
     * @param int $low The low 32 bits (fractional seconds).
     * @return int The raw 64-bit NTP bit pattern.
     */
    public static function toNtp(int $high, int $low): int
    {
        return ($high << 32) | $low;
    }

    /**
     * Split a raw 64-bit NTP bit pattern into its two 32-bit halves.
     *
     * The high half is the arithmetic (sign-extending) shift, so the caller gets the same bit
     * pattern back from {@see toNtp()}. Uses only integer arithmetic.
     *
     * @param int $ntp The raw 64-bit NTP bit pattern.
     * @return array{0: int, 1: int} The [high, low] halves.
     */
    public static function fromNtp(int $ntp): array
    {
        return [$ntp >> 32, $ntp & 0xFFFFFFFF];
    }
}
{
    "name": "danog/php-rtc-ntp",
    "type": "library",
    "description": "Pure-PHP NTP timestamp conversion for WebRTC. MadelineProto's PHP 8.2+ fork fixes unsigned 64-bit handling and integrates with the modular danog/php-rtc stack.",
    "keywords": [
        "multicastdns",
        "ntp"
    ],
    "homepage": "https://github.com/danog/php-rtc-ntp",
    "license": "BSD-3-Clause",
    "authors": [
        {
            "name": "Amin Yazdanpanah",
            "email": "github@aminyazdanpanah.com"
        },
        {
            "name": "Sana Moniri",
            "email": "s.mo@quasarstream.com"
        },
        {
            "name": "Quasar Stream Community"
        },
        {
            "name": "Daniil Gentili",
            "email": "daniil@daniil.it"
        }
    ],
    "require": {
        "php-64bit": "^8.2",
        "symfony/polyfill-php83": "*"
    },
    "require-dev": {
        "phpunit/phpunit": "^11.5 || ^12.0 || ^13.3.2",
        "symfony/phpunit-bridge": "7.3.x-dev",
        "bamarni/composer-bin-plugin": "^1.9.1"
    },
    "autoload": {
        "psr-4": {
            "Webrtc\\NTP\\": "src"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\Webrtc\\NTP\\": "tests"
        }
    },
    "minimum-stability": "stable",
    "scripts": {
        "post-install-cmd": [
            "@composer bin all install --ansi"
        ],
        "post-update-cmd": [
            "@composer bin all update --ansi"
        ]
    },
    "config": {
        "allow-plugins": {
            "bamarni/composer-bin-plugin": true
        }
    },
    "extra": {
        "bamarni-bin": {
            "bin-links": true,
            "forward-command": true
        }
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTPParameter;
use Webrtc\Mixin\DataClass;

#[DataClass]
final class RTCRtpCodecParameters
{
    /**
     * @param string $mimeType The codec MIME media type/subtype, for instance `'audio/PCMU'`.
     * @param int $clockRate The codec clock rate expressed in Hertz.
     * @param int|null $channels The number of channels supported (e.g., two for stereo).
     * @param int|null $payloadType The value that goes in the RTP Payload Type Field.
     * @param RTCRtcpFeedback[] $rtcpFeedback Transport layer and codec-specific feedback messages for this codec.
     * @param array<string, int|string|null> $parameters Codec-specific parameters available for signaling.
     */
    public function __construct(
        public string $mimeType,
        public int    $clockRate,
        public ?int   $channels = null,
        public ?int   $payloadType = null,
        public array  $rtcpFeedback = [],
        public array  $parameters = []
    )
    {
    }

    public function __toString() {
        $name = explode("/", $this->mimeType)[1];
        $s = "$name/$this->clockRate";
        if ($this->channels == 2) {
            $s .= "/2";
        }
        return $s;
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTPParameter;
use Webrtc\Mixin\DataClass;

#[DataClass]
readonly abstract class RTCRtpCodingParameters
{
    /**
     * @param int $ssrc The Synchronization Source identifier.
     * @param int $payloadType The value that goes in the RTP Payload Type Field.
     * @param RTCRtpRtxParameters|null $rtx represent RTCRtpRtxParameters class
     */
    public function __construct(
        public int                  $ssrc,
        public int                  $payloadType,
        public ?RTCRtpRtxParameters $rtx = null
    )
    {
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTPParameter;
use Webrtc\Mixin\DataClass;

#[DataClass]
final readonly class RTCRtpCapabilities
{
    /**
     * @param RTCRtpCodecCapability[] $codecs An array of `RTCRtpCodecCapability`.
     * @param RTCRtpHeaderExtensionCapability[] $headerExtensions An array of `RTCRtpHeaderExtensionCapability`.
     */
    public function __construct(
        public array $codecs = [],
        public array $headerExtensions = []
    )
    {
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTPParameter;

use Webrtc\Mixin\DataClass;

#[DataClass]
final readonly class RTCRtpCodecCapability
{
    /**
     * @param string $mimeType The codec MIME media type/subtype, for instance `'audio/PCMU'`.
     * @param int $clockRate The codec clock rate expressed in Hertz.
     * @param int|null $channels The number of channels supported (e.g., two for stereo).
     * @param array<string, int|string|null> $parameters Codec-specific parameters available for signaling.
     */
    public function __construct(
        public string $mimeType,
        public int    $clockRate,
        public ?int   $channels = null,
        public array  $parameters = []
    )
    {
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTPParameter;

use Webrtc\Mixin\DataClass;

#[DataClass]
final class RTCRtpReceiveParameters extends RTCRtpParameters
{
    /**
     * @param RTCRtpCodecParameters[] $codecs An array of `RTCRtpCodecParameters` to send or receive.
     * @param RTCRtpHeaderExtensionParameters[] $headerExtensions An array of `RTCRtpHeaderExtensionParameters`.
     * @param string $muxId The muxId assigned to the RTP stream, if any, empty string if unset.
     * @param RTCRtcpParameters $rtcp Parameters to configure RTCP.
     * @param RTCRtpDecodingParameters[] $encodings An array of `RTCRtpDecodingParameters`
     */
    public function __construct(
        array $codecs = [],
        array $headerExtensions = [],
        string $muxId = '',
        RTCRtcpParameters $rtcp = new RTCRtcpParameters(),
        public array $encodings = []
    )
    {
        parent::__construct($codecs, $headerExtensions, $muxId, $rtcp);
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTPParameter;

use DateTimeImmutable;
use Webrtc\Mixin\DataClass;

/**
 * information about a synchronization source (SSRC).
 */
#[DataClass]
final readonly class RTCRtpSynchronizationSource
{
    /**
     * @param DateTimeImmutable $timestamp The timestamp associated with this source.
     * @param int $source The SSRC identifier associated with this source.
     */
    public function __construct(
        public DateTimeImmutable $timestamp,
        public int       $source
    )
    {
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTPParameter;

use Webrtc\Mixin\DataClass;

#[DataClass]
final class RTCRtpSendParameters extends RTCRtpParameters
{
    /**
     * @param RTCRtpCodecParameters[] $codecs An array of `RTCRtpCodecParameters` to send or receive.
     * @param RTCRtpHeaderExtensionParameters[] $headerExtensions An array of `RTCRtpHeaderExtensionParameters`.
     * @param string $muxId The muxId assigned to the RTP stream, if any, empty string if unset.
     * @param RTCRtcpParameters $rtcp Parameters to configure RTCP.
     * @param RTCRtpEncodingParameters[] $encodings An array of `RTCRtpEncodingParameters`
     */
    public function __construct(
        array             $codecs = [],
        array             $headerExtensions = [],
        string            $muxId = "",
        RTCRtcpParameters $rtcp = new RTCRtcpParameters(),
        public array      $encodings = []
    )
    {
        parent::__construct($codecs, $headerExtensions, $muxId, $rtcp);
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTPParameter;

use DateTime;
use Webrtc\Mixin\DataClass;

#[DataClass]
final readonly class RTCRtpContributingSource
{
    /**
     * @param DateTime $timestamp The timestamp associated with this source.
     * @param int $source The CSRC identifier associated with this source.
     */
    public function __construct(
        public DateTime $timestamp,
        public int       $source
    )
    {
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTPParameter;

use Webrtc\Mixin\DataClass;

#[DataClass]
final readonly class RTCRtpEncodingParameters extends RTCRtpCodingParameters
{

}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTPParameter;
use Webrtc\Mixin\DataClass;

#[DataClass]
final readonly class RTCRtcpFeedback
{
    /**
     * @param string $type The type of feedback
     * @param string|null $parameter Optional Parameter
     */
    public function __construct(
        public string  $type,
        public ?string $parameter = null
    )
    {
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTPParameter;

use Webrtc\Mixin\DataClass;

#[DataClass]
class RTCRtpParameters
{
    /**
     * @param RTCRtpCodecParameters[] $codecs An array of `RTCRtpCodecParameters` to send or receive.
     * @param RTCRtpHeaderExtensionParameters[] $headerExtensions An array of `RTCRtpHeaderExtensionParameters`.
     * @param string $muxId The muxId assigned to the RTP stream, if any, empty string if unset.
     * @param RTCRtcpParameters $rtcp Parameters to configure RTCP.
     */
    public function __construct(
        public array              $codecs = [],
        public array              $headerExtensions = [],
        public string             $muxId = '',
        public RTCRtcpParameters $rtcp = new RTCRtcpParameters()
    )
    {
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTPParameter;
use Webrtc\Mixin\DataClass;

#[DataClass]
final readonly class RTCRtpRtxParameters
{
    /**
     * @param int $ssrc The Synchronization Source identifier.
     */
    public function __construct(public int $ssrc)
    {
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTPParameter;
use Webrtc\Mixin\DataClass;

#[DataClass]
final class RTCRtcpParameters
{
    /**
     * @param string|null $cname The Canonical Name (CNAME) used by RTCP.
     * @param bool $mux Whether RTP and RTCP are multiplexed.
     * @param int|null $ssrc The Synchronization Source identifier.
     */
    public function __construct(
        public ?string $cname = null,
        public bool   $mux = false,
        public ?int   $ssrc = null
    )
    {
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTPParameter;

use Webrtc\Mixin\DataClass;

#[DataClass]
final readonly class RTCRtpDecodingParameters extends RTCRtpCodingParameters
{

}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTPParameter;
use Webrtc\Mixin\DataClass;

#[DataClass]
final readonly class RTCRtpHeaderExtensionParameters
{
    /**
     * @param int $id The value that goes in the packet.
     * @param string $uri The URI of the RTP header extension.
     */
    public function __construct(
        public int    $id,
        public string $uri
    )
    {
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\RTPParameter;
use Webrtc\Mixin\DataClass;

#[DataClass]
final readonly class RTCRtpHeaderExtensionCapability
{
    /**
     * @param string $uri The URI of the RTP header extension.
     */
    public function __construct(public string $uri)
    {
    }
}{
    "name": "danog/php-rtc-rtp-parameter",
    "type": "library",
    "description": "RTP codec, payload, SSRC, header-extension, and RTCP parameter objects. MadelineProto's PHP 8.2+ fork supports the modular danog/php-rtc stack.",
    "keywords": [
        "webrtc",
        "rtp-parameter",
        "rtp"
    ],
    "homepage": "https://github.com/danog/php-rtc-rtp-parameter",
    "license": "BSD-3-Clause",
    "authors": [
        {
            "name": "Amin Yazdanpanah",
            "email": "github@aminyazdanpanah.com"
        },
        {
            "name": "Sana Moniri",
            "email": "s.mo@quasarstream.com"
        },
        {
            "name": "Quasar Stream Community"
        },
        {
            "name": "Daniil Gentili",
            "email": "daniil@daniil.it"
        }
    ],
    "require": {
        "php": "^8.2",
        "danog/php-rtc-mixin": "^1.1.6 || ^2.0"
    },
    "autoload": {
        "psr-4": {
            "Webrtc\\RTPParameter\\": "src"
        }
    },
    "minimum-stability": "stable",
    "scripts": {
        "post-install-cmd": [
            "@composer bin all install --ansi"
        ],
        "post-update-cmd": [
            "@composer bin all update --ansi"
        ]
    },
    "config": {
        "allow-plugins": {
            "bamarni/composer-bin-plugin": true
        }
    },
    "extra": {
        "bamarni-bin": {
            "bin-links": true,
            "forward-command": true
        }
    },
    "require-dev": {
        "bamarni/composer-bin-plugin": ">=1.9.1"
    }
}
<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Stats;

use DateTimeImmutable;
use Webrtc\Mixin\DataClass;
use Webrtc\Stats\enum\StatType;

/**
 * Base class for statistics.
 */
#[DataClass]
class RTCStats
{
    /**
     * @param string $id The id associated with this stat.
     * @param StatType $type The type of the stat.
     * @param DateTimeImmutable $dateTime The timestamp associated with this object.
     */
    public function __construct(
        public string $id,
        public StatType $type,
        public DateTimeImmutable $dateTime = new DateTimeImmutable()
    )
    {
    }

}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Stats;

use DateTimeImmutable;
use Webrtc\Mixin\DataClass;
use Webrtc\Stats\enum\StatType;

#[DataClass]
final class RTCOutboundRtpStreamStats extends RTCSentRtpStreamStats
{
    /**
     * @param string $id The id associated with this stat.
     * @param StatType $type The type of the stat.
     * @param int $ssrc The Synchronization Source identifier.
     * @param string $kind Stream Type (e.g. video, audio)
     * @param string $transportId The ID of DTLSTransport object.
     * @param int $packetsSent Total number of RTP packets sent for this SSRC.
     * @param int $bytesSent Total number of bytes sent for this SSRC.
     * @param string $trackId ID of media stream track (UUID)
     * @param DateTimeImmutable $dateTime The timestamp associated with this object.
     */
    public function __construct(
        string            $id,
        StatType          $type,
        int               $ssrc,
        string            $kind,
        string            $transportId,
        int               $packetsSent,
        int               $bytesSent,
        public string     $trackId,
        DateTimeImmutable $dateTime = new DateTimeImmutable()
    )
    {
        parent::__construct($id, $type, $ssrc, $kind, $transportId, $packetsSent, $bytesSent, $dateTime);
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Stats;

/**
 * Provides statistics data about WebRTC connections.
 */
final class RTCStatsReport
{
    /** @var array<string, RTCStats> */
    private array $stats = [];

    public function add(RTCStats $stats): void
    {
        $this->stats[$stats->id] = $stats;
    }

    /**
     * @return array<string, RTCStats>
     */
    public function getStats(): array
    {
        return $this->stats;
    }

    /**
     * @param RTCStatsReport $report
     * @return void
     */
    public function merge(RTCStatsReport $report): void
    {
        foreach ($report->getStats() as $stat) {
            $this->add($stat);
        }
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Stats;

use DateTimeImmutable;
use Webrtc\Mixin\DataClass;
use Webrtc\Stats\enum\StatType;

#[DataClass]
class RTCRtpStreamStats extends RTCStats
{
    /**
     * @param string $id The id associated with this stat.
     * @param StatType $type The type of the stat.
     * @param DateTimeImmutable $dateTime The timestamp associated with this object.
     * @param int $ssrc The Synchronization Source identifier.
     * @param string $kind Stream Type (e.g. video, audio)
     * @param string $transportId The ID of DTLSTransport object.
     */
    public function __construct(
        string        $id,
        StatType      $type,
        DateTimeImmutable     $dateTime,
        public int    $ssrc,
        public string $kind,
        public string $transportId
    )
    {
        parent::__construct($id, $type, $dateTime);
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Stats;

use DateTimeImmutable;
use Webrtc\Mixin\DataClass;
use Webrtc\Stats\enum\StatType;

#[DataClass]
final class RTCRemoteOutboundRtpStreamStats extends RTCSentRtpStreamStats
{
    public function __construct(
        string $id,
        int $ssrc,
        string $kind,
        string $transportId,
        int $packetsSent,
        int $bytesSent,
        DateTimeImmutable $dateTime = new DateTimeImmutable(),
        StatType $type = StatType::RemoteOutboundRtpStream,
        public ?DateTimeImmutable $remoteTimestamp = null
    )
    {
        parent::__construct($id, $type, $ssrc, $kind, $transportId, $packetsSent, $bytesSent, $dateTime);
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Stats;

use DateTimeImmutable;
use Webrtc\Mixin\DataClass;
use Webrtc\Stats\enum\StatType;

#[DataClass]
class RTCSentRtpStreamStats extends RTCRtpStreamStats
{
    /**
     * @param string $id The id associated with this stat.
     * @param StatType $type The type of the stat.
     * @param DateTimeImmutable $dateTime The timestamp associated with this object.
     * @param int $ssrc The Synchronization Source identifier.
     * @param string $kind Stream Type (e.g. video, audio)
     * @param string $transportId The ID of DTLSTransport object.
     * @param int $packetsSent Total number of RTP packets sent for this SSRC.
     * @param int $bytesSent Total number of bytes sent for this SSRC.
     */
    public function __construct(
        string     $id,
        StatType   $type,
        int        $ssrc,
        string     $kind,
        string     $transportId,
        public int $packetsSent,
        public int $bytesSent,
        DateTimeImmutable  $dateTime = new DateTimeImmutable()
    )
    {
        parent::__construct($id, $type, $dateTime, $ssrc, $kind, $transportId);
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Stats;

use DateTimeImmutable;
use Webrtc\Mixin\DataClass;
use Webrtc\Stats\enum\StatType;

#[DataClass]
final class RTCRemoteInboundRtpStreamStats extends RTCReceivedRtpStreamStats
{

    /**
     * @param string $id The id associated with this stat.
     * @param StatType $type The type of the stat.
     * @param DateTimeImmutable $dateTime The timestamp associated with this object.
     * @param int $ssrc The Synchronization Source identifier.
     * @param string $kind Stream Type (e.g. video, audio)
     * @param string $transportId The ID of DTLSTransport object.
     * @param int $packetsReceived Total number of RTP packets received for this SSRC.
     * @param int $packetsLost Total number of RTP packets lost for this SSRC.
     * @param int $jitter Jitter value.
     * @param float $roundTripTime Time of a round trip
     * @param float $fractionLost The amount of fraction packet lost
     */
    public function __construct(
        string       $id,
        int          $ssrc,
        string       $kind,
        string       $transportId,
        int          $packetsReceived,
        int          $packetsLost,
        int          $jitter,
        public float $roundTripTime,
        public float $fractionLost,
        StatType     $type = StatType::RemoteInboundRtpStream,
        DateTimeImmutable    $dateTime = new DateTimeImmutable(),
    )
    {
        parent::__construct($id, $type, $dateTime, $ssrc, $kind, $transportId, $packetsReceived, $packetsLost, $jitter);
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Stats;

use Webrtc\Stats\enum\TLSState;
use Webrtc\ICE\Enum\IceRole;
use Webrtc\Stats\enum\StatType;

final class RTCTransportStats extends RTCStats
{
    public function __construct(
        public string $id,
        public IceRole $role = IceRole::Controlling,
        public int $bytesSent = 0,
        public int $bytesReceived = 0,
        public int $packetsSent = 0,
        public int $packetsReceived = 0,
        public TLSState $state = TLSState::NEW,
    )
    {
        parent::__construct($id, StatType::Transport);
    }

    public function addBytesSent(int $bytesSent): void
    {
        $this->bytesSent += $bytesSent;
    }

    public function addBytesReceived(int $bytesReceived): void
    {
        $this->bytesReceived += $bytesReceived;
    }

    public function addPacketsSent(int $packetsSent = 1): void
    {
        $this->packetsSent += $packetsSent;
    }

    public function addPacketsReceived(int $packetsReceived = 1): void
    {
        $this->packetsReceived += $packetsReceived;
    }

    public function getState(): TLSState
    {
        return $this->state;
    }

    public function setState(TLSState $state): void
    {
        $this->state = $state;
    }

    public function getRole(): IceRole
    {
        return $this->role;
    }

    public function setRole(IceRole $role): void
    {
        $this->role = $role;
    }

    public function handleReceived(string $data): void
    {
        $this->addBytesReceived(strlen($data));
        $this->addPacketsReceived();
    }

    public function handleSent(string $data): void
    {
        $this->addBytesSent(strlen($data));
        $this->addPacketsSent();
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Stats\enum;

enum StatType
{
    case Transport;
    case RemoteInboundRtpStream;
    case RemoteOutboundRtpStream;
    case InboundRtpStream;
    case OutboundRtpStream;
    case ReceivedRtpStream;
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Stats\enum;

enum TLSState: int
{
    case NEW = 0;
    case CONNECTING = 1;
    case CONNECTED = 2;
    case CLOSED = 3;
    case FAILED = 4;
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Stats;

use DateTimeImmutable;
use Webrtc\Mixin\DataClass;
use Webrtc\Stats\enum\StatType;

#[DataClass]
final class RTCInboundRtpStreamStats extends RTCReceivedRtpStreamStats
{
    /**
     * @param string $id The id associated with this stat.
     * @param StatType $type The type of the stat.
     * @param DateTimeImmutable $dateTime The timestamp associated with this object.
     * @param int $ssrc The Synchronization Source identifier.
     * @param string $kind Stream Type (e.g. video, audio)
     * @param string $transportId The ID of DTLSTransport object.
     * @param int $packetsReceived Total number of RTP packets received for this SSRC.
     * @param int $packetsLost Total number of RTP packets lost for this SSRC.
     * @param int $jitter Jitter value.
     */
    public function __construct(
        string $id,
        int $ssrc,
        string $kind,
        string $transportId,
        int $packetsReceived,
        int $packetsLost,
        int $jitter,
        StatType $type = StatType::InboundRtpStream,
        DateTimeImmutable $dateTime = new DateTimeImmutable()
    )
    {
        parent::__construct($id, $type, $dateTime, $ssrc, $kind, $transportId, $packetsReceived, $packetsLost, $jitter);
    }
}<?php

/**
 * This file is part of the PHP WebRTC package.
 *
 * (c) Amin Yazdanpanah <https://www.aminyazdanpanah.com/#contact>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webrtc\Stats;

use DateTimeImmutable;
use Webrtc\Mixin\DataClass;
use Webrtc\Stats\enum\StatType;

#[DataClass]
class RTCReceivedRtpStreamStats extends RTCRtpStreamStats {

    /**
     * @param string $id The id associated with this stat.
     * @param StatType $type The type of the stat.
     * @param DateTimeImmutable $dateTime The timestamp associated with this object.
     * @param int $ssrc The Synchronization Source identifier.
     * @param string $kind Stream Type (e.g. video, audio)
     * @param string $transportId The ID of DTLSTransport object.
     * @param int $packetsReceived Total number of RTP packets received for this SSRC.
     * @param int $packetsLost Total number of RTP packets lost for this SSRC.
     * @param int $jitter Jitter value.
     */
    public function __construct(
        string $id,
        StatType $type,
        DateTimeImmutable $dateTime,
        int $ssrc,
        string $kind,
        string $transportId,
        public int $packetsReceived,
        public int $packetsLost,
        public int $jitter
    ) {
        parent::__construct($id, $type, $dateTime, $ssrc, $kind, $transportId);
    }
}{
    "name": "danog/php-rtc-stats",
    "type": "library",
    "description": "WebRTC statistics objects for RTP, DTLS, ICE, and media metrics. MadelineProto's PHP 8.2+ fork supports the modular danog/php-rtc stack.",
    "keywords": [
        "webrtc",
        "stats"
    ],
    "homepage": "https://github.com/danog/php-rtc-stats",
    "license": "BSD-3-Clause",
    "authors": [
        {
            "name": "Amin Yazdanpanah",
            "email": "github@aminyazdanpanah.com"
        },
        {
            "name": "Sana Moniri",
            "email": "s.mo@quasarstream.com"
        },
        {
            "name": "Quasar Stream Community"
        },
        {
            "name": "Daniil Gentili",
            "email": "daniil@daniil.it"
        }
    ],
    "require": {
        "php": "^8.2",
        "danog/php-rtc-mixin": "^1.1.6 || ^2.0",
        "danog/php-rtc-ice": "^1.1.18 || ^2.0"
    },
    "autoload": {
        "psr-4": {
            "Webrtc\\Stats\\": "src"
        }
    },
    "minimum-stability": "stable",
    "scripts": {
        "post-install-cmd": [
            "@composer bin all install --ansi"
        ],
        "post-update-cmd": [
            "@composer bin all update --ansi"
        ]
    },
    "config": {
        "allow-plugins": {
            "bamarni/composer-bin-plugin": true
        }
    },
    "extra": {
        "bamarni-bin": {
            "bin-links": true,
            "forward-command": true
        }
    },
    "require-dev": {
        "bamarni/composer-bin-plugin": ">=1.9.1"
    }
}
import sys
from math import log

# Multiple Polynomial Quadratic Sieve
def mpqs(n, verbose=False):
  if verbose:
    time1 = clock()

  root_n = isqrt(n)
  root_2n = isqrt(n+n)

  # formula chosen by experimentation
  # seems to be close to optimal for n < 10^50
  bound = int(5 * log(n, 10)**2)

  prime = []
  mod_root = []
  log_p = []
  num_prime = 0

  # find a number of small primes for which n is a quadratic residue
  p = 2
  while p < bound or num_prime < 3:

    # legendre (n|p) is only defined for odd p
    if p > 2:
      leg = legendre(n, p)
    else:
      leg = n & 1
    if leg == 1:
      prime += [p]
      mod_root += [int(mod_sqrt(n, p))]
      log_p += [log(p, 10)]
      num_prime += 1
    elif leg == 0:
      return p

    p = next_prime(p)

  # size of the sieve
  x_max = len(prime)*60

  # maximum value on the sieved range
  m_val = (x_max * root_2n) >> 1

  # fudging the threshold down a bit makes it easier to find powers of primes as factors
  # as well as partial-partial relationships, but it also makes the smoothness check slower.
  # there's a happy medium somewhere, depending on how efficient the smoothness check is
  thresh = log(m_val, 10) * 0.735

  # skip small primes. they contribute very little to the log sum
  # and add a lot of unnecessary entries to the table
  # instead, fudge the threshold down a bit, assuming ~1/4 of them pass
  min_prime = int(thresh*3)
  fudge = sum(log_p[i] for i,p in enumerate(prime) if p < min_prime)/4
  thresh -= fudge

  smooth = []
  used_prime = set()
  partial = {}
  num_smooth = 0
  num_used_prime = 0
  num_partial = 0
  num_poly = 0
  root_A = isqrt(root_2n / x_max)

  while True:
    # find an integer value A such that:
    # A is =~ sqrt(2*n) / x_max
    # A is a perfect square
    # sqrt(A) is prime, and n is a quadratic residue mod sqrt(A)
    while True:
      root_A = next_prime(root_A)
      leg = legendre(n, root_A)
      if leg == 1:
        break
      elif leg == 0:
        return root_A

    A = root_A * root_A

    # solve for an adequate B
    # B*B is a quadratic residue mod n, such that B*B-A*C = n
    # this is unsolvable if n is not a quadratic residue mod sqrt(A)
    b = mod_sqrt(n, root_A)
    B = (b + (n - b*b) * mod_inv(b + b, root_A))%A

    # B*B-A*C = n <=> C = (B*B-n)/A
    C = (B*B - n) / A

    num_poly += 1

    # sieve for prime factors
    sums = [0.0]*(2*x_max)
    i = 0
    for p in prime:
      if p < min_prime:
        i += 1
        continue
      logp = log_p[i]

      inv_A = mod_inv(A, p)
      # modular root of the quadratic
      a = int(((mod_root[i] - B) * inv_A)%p)
      b = int(((p - mod_root[i] - B) * inv_A)%p)

      k = 0
      while k < x_max:
        if k+a < x_max:
          sums[k+a] += logp
        if k+b < x_max:
          sums[k+b] += logp
        if k:
          sums[k-a+x_max] += logp
          sums[k-b+x_max] += logp

        k += p
      i += 1

    # check for smooths
    i = 0
    for v in sums:
      if v > thresh:
        x = x_max-i if i > x_max else i
        vec = set()
        sqr = []
        # because B*B-n = A*C
        # (A*x+B)^2 - n = A*A*x*x+2*A*B*x + B*B - n
        #               = A*(A*x*x+2*B*x+C)
        # gives the congruency
        # (A*x+B)^2 = A*(A*x*x+2*B*x+C) (mod n)
        # because A is chosen to be square, it doesn't need to be sieved
        val = sieve_val = A*x*x + 2*B*x + C

        if sieve_val < 0:
          vec = set([-1])
          sieve_val = -sieve_val

        for p in prime:
          while sieve_val%p == 0:
            if p in vec:
              # keep track of perfect square factors
              # to avoid taking the sqrt of a gigantic number at the end
              sqr += [p]
            vec ^= set([p])
            sieve_val = int(sieve_val / p)

        if sieve_val == 1:
          # smooth
          smooth += [(vec, (sqr, (A*x+B), root_A))]
          used_prime |= vec
        elif sieve_val in partial:
          # combine two partials to make a (xor) smooth
          # that is, every prime factor with an odd power is in our factor base
          pair_vec, pair_vals = partial[sieve_val]
          sqr += list(vec & pair_vec) + [sieve_val]
          vec ^= pair_vec
          smooth += [(vec, (sqr + pair_vals[0], (A*x+B)*pair_vals[1], root_A*pair_vals[2]))]
          used_prime |= vec
          num_partial += 1
        else:
          # save partial for later pairing
          partial[sieve_val] = (vec, (sqr, A*x+B, root_A))
      i += 1

    num_smooth = len(smooth)
    num_used_prime = len(used_prime)


    if num_smooth > num_used_prime:

      used_prime_list = sorted(list(used_prime))

      # set up bit fields for gaussian elimination
      masks = []
      mask = 1
      bit_fields = [0]*num_used_prime
      for vec, vals in smooth:
        masks += [mask]
        i = 0
        for p in used_prime_list:
          if p in vec: bit_fields[i] |= mask
          i += 1
        mask <<= 1

      # row echelon form
      col_offset = 0
      null_cols = []
      for col in xrange(num_smooth):
        pivot = col-col_offset == num_used_prime or bit_fields[col-col_offset] & masks[col] == 0
        for row in xrange(col+1-col_offset, num_used_prime):
          if bit_fields[row] & masks[col]:
            if pivot:
              bit_fields[col-col_offset], bit_fields[row] = bit_fields[row], bit_fields[col-col_offset]
              pivot = False
            else:
              bit_fields[row] ^= bit_fields[col-col_offset]
        if pivot:
          null_cols += [col]
          col_offset += 1

      # reduced row echelon form
      for row in xrange(num_used_prime):
        # lowest set bit
        mask = bit_fields[row] & -bit_fields[row]
        for up_row in xrange(row):
          if bit_fields[up_row] & mask:
            bit_fields[up_row] ^= bit_fields[row]

      # check for non-trivial congruencies
      for col in null_cols:
        all_vec, (lh, rh, rA) = smooth[col]
        lhs = lh   # sieved values (left hand side)
        rhs = [rh] # sieved values - n (right hand side)
        rAs = [rA] # root_As (cofactor of lhs)
        i = 0
        for field in bit_fields:
          if field & masks[col]:
            vec, (lh, rh, rA) = smooth[i]
            lhs += list(all_vec & vec) + lh
            all_vec ^= vec
            rhs += [rh]
            rAs += [rA]
          i += 1

        factor = gcd(list_prod(rAs)*list_prod(lhs) - list_prod(rhs), n)
        if factor != 1 and factor != n:
          break
      else:
        continue
      break

  return factor

# divide and conquer list product
def list_prod(a):
  size = len(a)
  if size == 1:
    return a[0]
  return list_prod(a[:size>>1]) * list_prod(a[size>>1:])

# greatest common divisor of a and b
def gcd(a, b):
  while b:
    a, b = b, a%b
  return a

# modular inverse of a mod m
def mod_inv(a, m):
  a = int(a%m)
  x, u = 0, 1
  while a:
    x, u = u, x - (m/a)*u
    m, a = a, m%a
  return x

# legendre symbol (a|m)
# note: returns m-1 if a is a non-residue, instead of -1
def legendre(a, m):
  return pow(a, (m-1) >> 1, m)

# modular sqrt(n) mod p
# p must be prime
def mod_sqrt(n, p):
  a = n%p
  if p%4 == 3:
    return pow(a, (p+1) >> 2, p)
  elif p%8 == 5:
    v = pow(a << 1, (p-5) >> 3, p)
    i = ((a*v*v << 1) % p) - 1
    return (a*v*i)%p
  elif p%8 == 1:
    # Shank's method
    q = p-1
    e = 0
    while q&1 == 0:
      e += 1
      q >>= 1

    n = 2
    while legendre(n, p) != p-1:
      n += 1

    w = pow(a, q, p)
    x = pow(a, (q+1) >> 1, p)
    y = pow(n, q, p)
    r = e
    while True:
      if w == 1:
        return x

      v = w
      k = 0
      while v != 1 and k+1 < r:
        v = (v*v)%p
        k += 1

      if k == 0:
        return x

      d = pow(y, 1 << (r-k-1), p)
      x = (x*d)%p
      y = (d*d)%p
      w = (w*y)%p
      r = k
  else: # p == 2
    return a

#integer sqrt of n
def isqrt(n):
  n = int(n)
  c = int(n*4/3)
  d = c.bit_length()

  a = d>>1
  if d&1:
    x = 1 << a
    y = (x + (n >> a)) >> 1
  else:
    x = (3 << a) >> 2
    y = (x + (c >> a)) >> 1

  if x != y:
    x = y
    y = int(x + n/x) >> 1
    while y < x:
      x = y
      y = int(x + n/x) >> 1
  return x

# strong probable prime
def is_sprp(n, b=2):
  if n < 2: return False
  d = n-1
  s = 0
  while d&1 == 0:
    s += 1
    d >>= 1

  x = pow(b, d, n)
  if x == 1 or x == n-1:
    return True

  for r in xrange(1, s):
    x = (x * x)%n
    if x == 1:
      return False
    elif x == n-1:
      return True

  return False

# lucas probable prime
# assumes D = 1 (mod 4), (D|n) = -1
def is_lucas_prp(n, D):
  P = 1
  Q = (1-D) >> 2

  # n+1 = 2**r*s where s is odd
  s = n+1
  r = 0
  while s&1 == 0:
    r += 1
    s >>= 1

  # calculate the bit reversal of (odd) s
  # e.g. 19 (10011) <=> 25 (11001)
  t = 0
  while s:
    if s&1:
      t += 1
      s -= 1
    else:
      t <<= 1
      s >>= 1

  # use the same bit reversal process to calculate the sth Lucas number
  # keep track of q = Q**n as we go
  U = 0
  V = 2
  q = 1
  # mod_inv(2, n)
  inv_2 = (n+1) >> 1
  while t:
    if t&1:
      # U, V of n+1
      U, V = ((U + V) * inv_2)%n, ((D*U + V) * inv_2)%n
      q = (q * Q)%n
      t -= 1
    else:
      # U, V of n*2
      U, V = (U * V)%n, (V * V - 2 * q)%n
      q = (q * q)%n
      t >>= 1

  # double s until we have the 2**r*sth Lucas number
  while r:
    U, V = (U * V)%n, (V * V - 2 * q)%n
    q = (q * q)%n
    r -= 1

  # primality check
  # if n is prime, n divides the n+1st Lucas number, given the assumptions
  return U == 0

# primes less than 212
small_primes = set([
    2,  3,  5,  7, 11, 13, 17, 19, 23, 29,
   31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
   73, 79, 83, 89, 97,101,103,107,109,113,
  127,131,137,139,149,151,157,163,167,173,
  179,181,191,193,197,199,211])

# pre-calced sieve of eratosthenes for n = 2, 3, 5, 7
indices = [
    1, 11, 13, 17, 19, 23, 29, 31, 37, 41,
   43, 47, 53, 59, 61, 67, 71, 73, 79, 83,
   89, 97,101,103,107,109,113,121,127,131,
  137,139,143,149,151,157,163,167,169,173,
  179,181,187,191,193,197,199,209]

# distances between sieve values
offsets = [
  10, 2, 4, 2, 4, 6, 2, 6, 4, 2, 4, 6,
   6, 2, 6, 4, 2, 6, 4, 6, 8, 4, 2, 4,
   2, 4, 8, 6, 4, 6, 2, 4, 6, 2, 6, 6,
   4, 2, 4, 6, 2, 6, 4, 2, 4, 2,10, 2]

max_int = 2147483647

# an 'almost certain' primality check
def is_prime(n):
  if n < 212:
    return n in small_primes

  for p in small_primes:
    if n%p == 0:
      return False

  # if n is a 32-bit integer, perform full trial division
  if n <= max_int:
    i = 211
    while i*i < n:
      for o in offsets:
        i += o
        if n%i == 0:
          return False
    return True

  # Baillie-PSW
  # this is technically a probabalistic test, but there are no known pseudoprimes
  if not is_sprp(n, 2): return False

  # idea shamelessly stolen from Mathmatica
  # if n is a 2-sprp and a 3-sprp, n is necessarily square-free
  if not is_sprp(n, 3): return False

  a = 5
  s = 2
  # if n is a perfect square, this will never terminate
  while legendre(a, n) != n-1:
    s = -s
    a = s-a
  return is_lucas_prp(n, a)

# next prime strictly larger than n
def next_prime(n):
  if n < 2:
    return 2
  # first odd larger than n
  n = (n + 1) | 1
  if n < 212:
    while True:
      if n in small_primes:
        return n
      n += 2

  # find our position in the sieve rotation via binary search
  x = int(n%210)
  s = 0
  e = 47
  m = 24
  while m != e:
    if indices[m] < x:
      s = m
      m = (s + e + 1) >> 1
    else:
      e = m
      m = (s + e) >> 1

  i = int(n + (indices[m] - x))
  # adjust offsets
  offs = offsets[m:] + offsets[:m]
  while True:
    for o in offs:
      if is_prime(i):
        return i
      i += o

print(mpqs(int(sys.argv[1])))
# NOTICE!!! This is copied from https://stackoverflow.com/questions/4643647/fast-prime-factorization-module
import sys
import random

def primesbelow(N):
    # http://stackoverflow.com/questions/2068372/fastest-way-to-list-all-primes-below-n-in-python/3035188#3035188
    #""" Input N>=6, Returns a list of primes, 2 <= p < N """
    correction = N % 6 > 1
    N = {0:N, 1:N-1, 2:N+4, 3:N+3, 4:N+2, 5:N+1}[N%6]
    sieve = [True] * (N // 3)
    sieve[0] = False
    for i in range(int(N ** .5) // 3 + 1):
        if sieve[i]:
            k = (3 * i + 1) | 1
            sieve[k*k // 3::2*k] = [False] * ((N//6 - (k*k)//6 - 1)//k + 1)
            sieve[(k*k + 4*k - 2*k*(i%2)) // 3::2*k] = [False] * ((N // 6 - (k*k + 4*k - 2*k*(i%2))//6 - 1) // k + 1)
    return [2, 3] + [(3 * i + 1) | 1 for i in range(1, N//3 - correction) if sieve[i]]

smallprimeset = set(primesbelow(100000))
_smallprimeset = 100000

def isprime(n, precision=7):
    # http://en.wikipedia.org/wiki/Miller-Rabin_primality_test#Algorithm_and_running_time
    if n == 1 or n % 2 == 0:
        return False
    elif n < 1:
        raise ValueError("Out of bounds, first argument must be > 0")
    elif n < _smallprimeset:
        return n in smallprimeset


    d = n - 1
    s = 0
    while d % 2 == 0:
        d //= 2
        s += 1

    for repeat in range(precision):
        a = random.randrange(2, n - 2)
        x = pow(a, d, n)

        if x == 1 or x == n - 1: continue

        for r in range(s - 1):
            x = pow(x, 2, n)
            if x == 1: return False
            if x == n - 1: break
        else: return False

    return True

# https://comeoncodeon.wordpress.com/2010/09/18/pollard-rho-brent-integer-factorization/
def pollard_brent(n):
    if n % 2 == 0: return 2
    if n % 3 == 0: return 3

    y, c, m = random.randint(1, n-1), random.randint(1, n-1), random.randint(1, n-1)

    g, r, q = 1, 1, 1
    while g == 1:
        x = y
        for i in range(r):
            y = (pow(y, 2, n) + c) % n

        k = 0
        while k < r and g==1:
            ys = y
            for i in range(min(m, r-k)):
                y = (pow(y, 2, n) + c) % n
                q = q * abs(x-y) % n

            g = gcd(q, n)
            k += m
        r *= 2
    if g == n:
        while True:
            ys = (pow(ys, 2, n) + c) % n
            g = gcd(abs(x - ys), n)
            if g > 1:
                break

    return g

smallprimes = primesbelow(10000) # might seem low, but 1000*1000 = 1000000, so this will fully factor every composite < 1000000
def primefactors(n, sort=False):
    factors = []

    limit = int(n ** .5) + 1
    for checker in smallprimes:
        if checker > limit: break
        while n % checker == 0:
            factors.append(checker)
            n //= checker
            limit = int(n ** .5) + 1
            if checker > limit: break

    if n < 2: return factors

    while n > 1:
        if isprime(n):
            factors.append(n)
            break
        factor = pollard_brent(n) # trial division did not fully factor, switch to pollard-brent
        factors.extend(primefactors(factor)) # recurse to factor the not necessarily prime factor returned by pollard-brent
        n //= factor

    if sort: factors.sort()

    return factors

def factorization(n):
    factors = {}
    for p1 in primefactors(n):
        try:
            factors[p1] += 1
        except KeyError:
            factors[p1] = 1
    return factors

totients = {}
def totient(n):
    if n == 0: return 1

    try: return totients[n]
    except KeyError: pass

    tot = 1
    for p, exp in factorization(n).items():
        tot *= (p - 1)  *  p ** (exp - 1)

    totients[n] = tot
    return tot

def gcd(a, b):
    if a == b: return a
    while b > 0: a, b = b, a % b
    return a

def lcm(a, b):
    return abs(a * b) // gcd(a, b)

print(primefactors(int(sys.argv[1]))[0])<?php

/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
This file is part of MadelineProto.
MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Affero General Public License for more details.
You should have received a copy of the GNU General Public License along with the MadelineProto.
If not, see <http://www.gnu.org/licenses/>.
*/

namespace danog;

use FFI;

class PrimeModule
{
    public static function native_single($what)
    {
        if (!is_int($what)) {
            return false;
        }
        foreach ([2, 3, 5, 7, 11, 13, 17, 19, 23] as $s) {
            if ($what % $s === 0) {
                return $s;
            }
        }
        $g = 0;
        $it = 0;
        for ($i = 0; $i < 3 || $it < 1000; $i++) {
            $t = ((rand(0, 127) & 15) + 17) % $what;
            $x = (rand() % ($what - 1)) + 1;
            $y = $x;
            $lim = 1 << ($i + 18);
            for ($j = 1; $j <= $lim; $j++) {
                $it++;
                $a = $x;
                $b = $x;
                $c = $t;
                while ($b) {
                    if ($b & 1) {
                        $c += $a;
                        if ($c >= $what) {
                            $c -= $what;
                        }
                    }
                    $a += $a;
                    if ($a >= $what) {
                        $a -= $what;
                    }
                    $b >>= 1;
                }
                $x = $c;
                $z = ($x < $y) ? $what + $x - $y : $x - $y;
                $g = self::gcd($z, $what);
                if ($g != 1) {
                    break;
                }

                if (!($j & ($j - 1))) {
                    $y = $x;
                }
            }
            if ($g > 1 && $g < $what) {
                break;
            }
        }

        if ($g > 1 && $g < $what) {
            return $g;
        }

        return $what;
    }

    public static function native($what)
    {
        $res = [self::native_single($what)];
        while (array_product($res) !== $what) {
            $res[] = self::native_single($what / array_product($res));
        }

        return $res;
    }

    public static function python_single($what)
    {
        if (function_exists('shell_exec')) {
            $res = trim(shell_exec('timeout 10 python2 '.__DIR__.'/prime.py '.$what.' 2>&1') ?? '');
            if ($res == '' || is_null($res) || !is_numeric($res)) {
                copy(__DIR__.'/prime.py', getcwd().'/.prime.py');
                $res = trim(shell_exec('timeout 10 python2 '.getcwd().'/.prime.py '.$what.' 2>&1') ?? '');
                unlink(getcwd().'/.prime.py');
                if ($res == '' || is_null($res) || !is_numeric($res)) {
                    return false;
                }
            }
            $newval = intval($res);
            if (is_int($newval)) {
                $res = $newval;
            }
            if ($res === 0) {
                return false;
            }

            return $res;
        }

        return false;
    }

    public static function factor_single($what)
    {
        if (function_exists('shell_exec')) {
            $res = trim(shell_exec('timeout 10 factor '.$what.' 2>&1') ?? '');
            $res = explode(' ', $res);
            if (count($res) !== 3) {
                return false;
            }
            return (int) $res[1];
        }

        return false;
    }

    public static function factor($what)
    {
        $res = [self::factor_single($what)];
        if ($res[0] === false) {
            return false;
        }
        while (array_product($res) !== $what) {
            $res[] = self::factor_single($what / array_product($res));
        }

        return $res;
    }

    public static function python($what)
    {
        $res = [self::python_single($what)];
        if ($res[0] === false) {
            return false;
        }
        while (array_product($res) !== $what) {
            $res[] = self::python_single($what / array_product($res));
        }

        return $res;
    }

    public static function python_single_alt($what)
    {
        if (function_exists('shell_exec')) {
            $res = trim(shell_exec('python '.__DIR__.'/alt_prime.py '.$what.' 2>&1') ?? '');
            if ($res == '' || is_null($res) || !is_numeric($res)) {
                copy(__DIR__.'/alt_prime.py', getcwd().'/.alt_prime.py');
                $res = trim(shell_exec('python '.getcwd().'/.alt_prime.py '.$what.' 2>&1') ?? '');
                unlink(getcwd().'/.alt_prime.py');
                if ($res == '' || is_null($res) || !is_numeric($res)) {
                    return false;
                }
            }
            $newval = intval($res);
            if (is_int($newval)) {
                $res = $newval;
            }
            if ($res === 0) {
                return false;
            }

            return $res;
        }

        return false;
    }

    public static function python_alt($what)
    {
        $res = [self::python_single_alt($what)];
        if ($res[0] === false) {
            return false;
        }
        while (array_product($res) !== $what) {
            $res[] = self::python_single_alt($what / array_product($res));
        }

        return $res;
    }

    public static function wolfram_single($what)
    {
        $query = 'Do prime factorization of '.$what;
        $params = [
            'async'         => true,
            'banners'       => 'raw',
            'debuggingdata' => false,
            'format'        => 'moutput',
            'formattimeout' => 8,
            'input'         => $query,
            'output'        => 'JSON',
            'proxycode'     => json_decode(file_get_contents('http://www.wolframalpha.com/api/v1/code'), true)['code'],
        ];
        $url = 'https://www.wolframalpha.com/input/json.jsp?'.http_build_query($params);
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, ['Referer: https://www.wolframalpha.com/input/?i='.urlencode($query)]);
        curl_setopt($ch, CURLOPT_URL, $url);
        $res = json_decode(curl_exec($ch), true);
        curl_close($ch);
        $fres = false;
        if (!isset($res['queryresult']['pods'])) {
            return false;
        }
        foreach ($res['queryresult']['pods'] as $cur) {
            if ($cur['id'] === 'Divisors') {
                $fres = explode(', ', preg_replace(["/{\d+, /", "/, \d+}$/"], '', $cur['subpods'][0]['moutput']));
                break;
            }
        }
        if (is_array($fres)) {
            $fres = $fres[0];

            $newval = intval($fres);
            if (is_int($newval)) {
                $fres = $newval;
            }

            return $fres;
        }

        return false;
    }

    public static function wolfram($what)
    {
        $res = [self::wolfram_single($what)];
        while (array_product($res) !== $what) {
            $res[] = self::wolfram_single($what / array_product($res));
        }

        return $res;
    }

    private static ?FFI $ffi = null;

    public static function native_single_cpp($what)
    {
        if (!extension_loaded('primemodule')) {
            if (class_exists(FFI::class)) {
                try {
                    self::$ffi ??= FFI::load('/usr/include/primemodule-ffi.h');
                    $result = self::$ffi->factorizeFFI((string) $what);
                    if ($result > 0) {
                        return $result;
                    }
                } catch (\Throwable $e) {
                }

                return false;
            }

            return false;
        }

        try {
            return factorize($what);
        } catch (\Exception $e) {
            return false;
        }
    }

    public static function native_cpp($what)
    {
        $res = [self::native_single_cpp($what)];
        if ($res[0] == false) {
            return false;
        }
        while (($product = array_product($res)) !== $what) {
            if ($product == 0) {
                return false;
            }
            $res[] = self::native_single_cpp($what / $product);
        }

        return $res;
    }

    public static function auto_single($what)
    {
        $res = self::native_single_cpp($what);
        if ($res !== false) {
            return $res;
        }
        $res = self::python_single_alt($what);
        if ($res !== false) {
            return $res;
        }
        $res = self::python_single($what);
        if ($res !== false) {
            return $res;
        }
        $res = self::native_single((int) $what);
        if ($res !== false) {
            return $res;
        }
        $res = self::wolfram_single($what);
        if ($res !== false) {
            return $res;
        }

        return false;
    }

    public static function auto($what)
    {
        $res = self::native_cpp($what);
        if (is_array($res)) {
            return $res;
        }
        $res = self::python_alt($what);
        if (is_array($res)) {
            return $res;
        }
        $res = self::python($what);
        if (is_array($res)) {
            return $res;
        }
        $res = self::native((int) $what);
        if (is_array($res)) {
            return $res;
        }
        $res = self::wolfram($what);
        if (is_array($res)) {
            return $res;
        }

        return false;
    }

    private static function gcd($a, $b)
    {
        if ($a == $b) {
            return $a;
        }
        while ($b > 0) {
            list($a, $b) = [$b, self::posmod($a, $b)];
        }

        return $a;
    }

    private static function posmod($a, $b)
    {
        $resto = $a % $b;
        if ($resto < 0) {
            $resto += abs($b);
        }

        return $resto;
    }

    private function primesbelow($N)
    {
        $correction = ($N % 6 > 1) ? true : false;
        $N_Array = [$N, $N - 1, $N + 4, $N + 3, $N + 2, $N + 1];
        $N = $N_Array[$N % 6];

        $sieve = [];
        for ($i = 0; $i < (int) $N / 3; $i++) {
            $sieve[$i] = true;
        }
        $sieve[0] = false;

        for ($i = 0; $i < (int) ((int) pow($N, 0.5) / 3) + 1; $i++) {
            if ($sieve[$i]) {
                $k = (3 * $i + 1) | 1;
                $startIndex1 = (int) ($k * $k / 3);
                $period = 2 * $k;
                for ($j = $startIndex1; $j < count($sieve); $j = $j + $period) {
                    $sieve[$j] = false;
                }
                $startIndex2 = (int) (($k * $k + 4 * $k - 2 * $k * ($i % 2)) / 3);
                $period = 2 * $k;
                for ($k = $startIndex2; $k < count($sieve); $k = $k + $period) {
                    $sieve[$k] = false;
                }
            }
        }

        $resultArray = [2, 3];
        $t = 1;
        for ($i = 1; $i < (int) ($N / 3) - $correction; $i++) {
            if ($sieve[$i]) {
                $resultArray[$t + 1] = (3 * $i + 1) | 1;
                $t++;
            }
        }

        return $resultArray;
    }

    private function isprime($n, $precision = 7)
    {
        $smallprimeset = $this->primesbelow(100000);
        $_smallprimeset = 100000;
        if ($n == 1 || $n % 2 == 0) {
            return false;
        } elseif ($n < 1) {
            throw new Exception('Out of bounds, first argument must be > 0');
        } elseif ($n < $_smallprimeset) {
            return in_array($n, $smallprimeset);
        }

        $d = $n - 1;
        $s = 0;
        while ($d % 2 == 0) {
            $d = (int) ($d / 2);
            $s += 1;
        }

        for ($i = 0; $i < $precision; $i++) {
            // random.randrange(2, n - 2) means:
            // $a = mt_rand(2, $n - 3);
            // maybe $n would be bigger that PHP_MAX_INT
            $a = mt_rand(2, 1084);
            $x = bcpowmod($a, $d, $n);

            if ($x == 1 || $x == $n - 1) {
                continue;
            }

            $flagfound = 0;
            for ($j = 0; $j < $s - 1; $j++) {
                $x = bcpowmod($x, 2, $n);
                if ($x == 1) {
                    return false;
                }
                if ($x == $n - 1) {
                    $flagfound = 1;
                    break;
                }
            }
            if ($flagfound == 0) {
                return false;
            }
        }

        return true;
    }

    private function pollard_brent($n)
    {
        $n = (int) $n;

        if ($n % 2 == 0) {
            return 2;
        }

        if (bcmod($n, 2) == 0) {
            return 2;
        }
        if (bcmod($n, 3) == 0) {
            return 3;
        }

        // $y = mt_rand(1, $n-1);
        // $c = mt_rand(1, $n-1);
        // $m = mt_rand(1, $n-1);
        // Again, $n may be bigger than PHP_MAX_INT
        // also, small numbers has a big affect in a good performance
        $y = 2;
        $c = 3;
        $m = 4;

        $g = 1;
        $r = 1;
        $q = 1;

        while ($g == 1) {
            $x = $y;
            for ($i = 0; $i < $r; $i++) {
                // $y = gmp_mod( (bcpowmod($y, 2, $n) + $c) , $n);
                $y = bcmod((bcpowmod($y, 2, $n) + $c), $n);
            }

            $k = 0;
            while ($k < $r && $g == 1) {
                $ys = $y;
                for ($j = 0; $j < min($m, $r - $k); $j++) {

                    // $y = gmp_mod( (bcpowmod($y, 2, $n) + $c), $n );
                    $y = bcmod((bcpowmod($y, 2, $n) + $c), $n);

                    // $q = gmp_mod($q * abs($x-$y), $n);
                    $mul = bcmul($q, abs($x - $y));
                    $q = bcmod($mul, $n);
                }

                $g = $this->gcd2($q, $n);
                $k += $m;
            }
            $r *= 2;
        }
        if ($g == $n) {
            while (true) {
                // $ys = ( bcpowmod($ys, 2, $n) + $c ) % $n;
                $ys = bcmod((bcpowmod($ys, 2, $n) + $c), $n);
                $g = $this->gcd2(abs($x - $ys), $n);
                if ($g > 1) {
                    break;
                }
            }
        }

        return $g;
    }

    public function primefactors($n, $sort = false)
    {
        $smallprimes = $this->primesbelow(10000);
        $factors = [];

        $limit = bcadd(bcsqrt($n), 1);
        foreach ($smallprimes as $checker) {
            if ($checker > $limit) {
                break;
            }
            // while (gmp_mod($n, $checker) == 0) {
            while (bcmod($n, $checker) == 0) {
                array_push($factors, $checker);
                $n = bcdiv($n, $checker);
                $limit = bcadd(bcsqrt($n), 1);
                if ($checker > $limit) {
                    break;
                }
            }
        }

        if ($n < 2) {
            return $factors;
        }

        while ($n > 1) {
            if ($this->isprime($n)) {
                array_push($factors, $n);
                break;
            }
            $factor = $this->pollard_brent($n);
            $factors = array_merge($factors, $this->primefactors($factor));
            $n = (int) ($n / $factor);
        }
        if ($sort) {
            sort($factors);
        }

        return $factors;
    }

    private function gcd2($a, $b)
    {
        if ($a == $b) {
            return $a;
        }
        while ($b > 0) {
            $a2 = $a;
            $a = $b;
            $b = bcmod($a2, $b);
        }

        return $a;
    }
}
{
    "name": "danog/primemodule",
    "description": "Prime module capable of doing prime factorization of huge numbers very quickly.\"",
    "type": "library",
    "license": "AGPL-3.0-only",
    "authors": [
        {
            "name": "Daniil Gentili",
            "email": "daniil@daniil.it"
        }
    ],
    "require": {
        "php": "^8.0"
    },
    "suggest": {
        "ext-primemodule": "Install the native C++ extension for extremely fast factorization (https://prime.madelineproto.xyz)"
    },
    "autoload": {
        "psr-0": {
            "danog\\": "lib/"
        }
    },
    "config": {
        "allow-plugins": {
            "phabel/phabel": true
        }
    },
    "require-dev": {
        "phabel/phabel": "^1"
    },
    "extra": {
        "phabel": {
            "revision": 0
        }
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri;

use BackedEnum;
use DateTimeInterface;
use League\Uri\Contracts\UriComponentInterface;
use Stringable;
use TypeError;
use Uri\Rfc3986\Uri as Rfc3986Uri;
use Uri\WhatWg\Url as WhatWgUrl;
use ValueError;

use function array_is_list;
use function array_map;
use function get_debug_type;
use function implode;
use function is_array;
use function is_float;
use function is_infinite;
use function is_nan;
use function is_object;
use function is_resource;
use function is_scalar;
use function json_encode;

use const JSON_PRESERVE_ZERO_FRACTION;

enum StringCoercionMode
{
    /**
     * PHP conversion mode.
     *
     * Guarantees that only scalar values, BackedEnum, and null are accepted.
     * Any object, Non-backed enums, resource, or recursive structure results in an error.
     *
     * - null: is not converted and stays the `null` value
     * - string: used as-is
     * - bool: converted to string â€œ0â€� (false) or â€œ1â€� (true)
     * - int: converted to numeric string (123 -> â€œ123â€�)
     * - float: converted to decimal string (3.14 -> â€œ3.14â€�)
     * - Backed Enum: converted to their backing value and then stringify see int and string
     */
    case Native;

    /**
     * Ecmascript conversion mode.
     *
     * Guarantees that only scalar values, BackedEnum, and null are accepted.
     * Any resource, or recursive structure results in an error.
     *
     * - null: converted to string â€œnullâ€�
     * - string: used as-is
     * - bool: converted to string â€œfalseâ€� (false) or â€œtrueâ€� (true)
     * - int: converted to numeric string (123 -> â€œ123â€�)
     * - float: converted to decimal string (3.14 -> â€œ3.14â€�), "NaN", "-Infinity" or "Infinity"
     * - Backed Enum: converted to their backing value and then stringify see int and string
     * - Array as list are flatten into a string list using the "," character as separator
     * - Associative array, Non-backed enums, any object without stringification semantics is coerced to "[object Object]".
     * - DateTimeInterface implementing object are coerce to their string representation using DateTimeInterface::RFC2822 format
     */
    case Ecmascript;

    private const RECURSION_MARKER = "\0__RECURSION_INTERNAL_MARKER_WHATWG__\0";

    public function isCoercible(mixed $value): bool
    {
        return self::Ecmascript === $this
            ? !is_resource($value)
            : match (true) {
                $value instanceof Rfc3986Uri,
                $value instanceof WhatWgUrl,
                $value instanceof BackedEnum,
                $value instanceof Stringable,
                is_scalar($value),
                null === $value => true,
                default => false,
            };
    }

    /**
     * @throws TypeError if the type is not supported by the specific case
     * @throws ValueError if circular reference is detected
     */
    public function coerce(mixed $value): ?string
    {
        return match ($this) {
            self::Ecmascript => match (true) {
                $value instanceof Rfc3986Uri => $value->toString(),
                $value instanceof WhatWgUrl => $value->toAsciiString(),
                $value instanceof DateTimeInterface => $value->format(DateTimeInterface::RFC2822),
                $value instanceof BackedEnum => (string) $value->value,
                $value instanceof Stringable => $value->__toString(),
                is_object($value) => '[object Object]',
                is_array($value) => match (true) {
                    self::hasCircularReference($value) => throw new ValueError('Recursive array structure detected; unable to coerce value.'),
                    array_is_list($value) => implode(',', array_map($this->coerce(...), $value)),
                    default => '[object Object]',
                },
                true === $value => 'true',
                false === $value => 'false',
                null === $value => 'null',
                is_float($value) => match (true) {
                    is_nan($value) => 'NaN',
                    is_infinite($value) => 0 < $value ? 'Infinity' : '-Infinity',
                    default => (string) json_encode($value, JSON_PRESERVE_ZERO_FRACTION),
                },
                is_scalar($value) => (string) $value,
                default => throw new TypeError('Unable to coerce value of type "'.get_debug_type($value).'" with "'.$this->name.'" coercion.'),
            },
            self::Native => match (true) {
                $value instanceof UriComponentInterface => $value->value(),
                $value instanceof WhatWgUrl => $value->toAsciiString(),
                $value instanceof Rfc3986Uri => $value->toString(),
                $value instanceof BackedEnum => (string) $value->value,
                $value instanceof Stringable => $value->__toString(),
                false === $value => '0',
                true === $value => '1',
                null === $value => null,
                is_scalar($value) => (string) $value,
                default => throw new TypeError('Unable to coerce value of type "'.get_debug_type($value).'" with "'.$this->name.'" coercion.'),
            },
        };
    }

    /**
     * Array recursion detection.
     * @see https://stackoverflow.com/questions/9042142/detecting-infinite-array-recursion-in-php
     */
    private static function hasCircularReference(array &$arr): bool
    {
        if (isset($arr[self::RECURSION_MARKER])) {
            return true;
        }

        try {
            $arr[self::RECURSION_MARKER] = true;
            foreach ($arr as $key => &$value) {
                if (self::RECURSION_MARKER !== $key && is_array($value) && self::hasCircularReference($value)) {
                    return true;
                }
            }

            return false;
        } finally {
            unset($arr[self::RECURSION_MARKER]);
        }
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri;

enum Occurrence
{
    case First;
    case Last;
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri;

enum HostFormat
{
    case Ascii;
    case Unicode;
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri;

use finfo;
use League\Uri\Exceptions\MissingFeature;
use League\Uri\IPv4\Calculator;

use function class_exists;
use function defined;
use function extension_loaded;
use function function_exists;

use const PHP_INT_SIZE;

/**
 * Allow detecting features needed to make the packages work.
 */
final class FeatureDetection
{
    public static function supportsFileDetection(): void
    {
        static $isSupported = null;
        $isSupported = $isSupported ?? class_exists(finfo::class);

        $isSupported || throw new MissingFeature('Support for file type detection requires the `fileinfo` extension.');

    }

    public static function supportsIdn(): void
    {
        static $isSupported = null;
        $isSupported = $isSupported ?? (function_exists('\idn_to_ascii') && defined('\INTL_IDNA_VARIANT_UTS46'));

        $isSupported || throw new MissingFeature('Support for IDN host requires the `intl` extension for best performance or run "composer require symfony/polyfill-intl-idn" to install a polyfill.');
    }

    public static function supportsIPv4Conversion(): void
    {
        static $isSupported = null;
        $isSupported = $isSupported ?? (extension_loaded('gmp') || extension_loaded('bcmath') || (4 < PHP_INT_SIZE));

        $isSupported || throw new MissingFeature('A '.Calculator::class.' implementation could not be automatically loaded. To perform IPv4 conversion use a x.64 PHP build or install one of the following extension GMP or BCMath. You can also ship your own implementation.');
    }

    public static function supportsDom(): void
    {
        static $isSupported = null;
        $isSupported = $isSupported ?? extension_loaded('dom');

        $isSupported || throw new MissingFeature('To use a DOM related feature, the DOM extension must be installed in your system.');
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri;

enum QueryComposeMode
{
    /**
     * Pre-PHP 8.4 Mode.
     *
     * Strictly uses get_object_vars on objects (Enum included)
     * If the value can not be serialized the entry is skipped.
     *
     * ie http_build_query behavior before PHP8.4
     */
    case Compatible;

    /**
     * PHP 8.4+ enum-compatible lenient mode.
     *
     * Provides stable support for BackedEnum values.
     * UnitEnum values are skipped.
     * Uses get_object_vars() for non-enum objects.
     * Unserializable values are skipped.
     *
     * Behaves like {@see QueryComposeMode::EnumCompatible}
     * but does not throw for UnitEnum values.
     *
     * Mirrors http_build_query behavior in PHP 8.4+,
     * except that error cases are silently ignored
     * instead of throwing.
     *
     * This mode is tolerant by design and skips entries that would otherwise
     * result in an exception in {@see QueryComposeMode::EnumCompatible}.
     */
    case EnumLenient;

    /**
     * PHP 8.4+ mode.
     *
     * Provides stable support for BackedEnum values.
     * Throws for UnitEnum.
     * Uses get_object_vars() for non-enum objects.
     * Unserializable values are skipped.
     *
     * http_build_query behavior in PHP 8.4+.
     */
    case EnumCompatible;

    /**
     * Use PHP version http_build_query algorithm.
     *
     * In pre-PHP8.4 you get the same results as `Compatible`
     * In PHP PHP8.4+ you get the same results as `EnumCompatible`
     */
    case Native;

    /**
     * Validation-first mode.
     *
     * Guarantees that only scalar values, BackedEnum, and null are accepted.
     * Any object, UnitEnum, resource, or recursive structure
     * results in an exception.
     *
     * - null: the key name is used but the separator and its content are omitted
     * - string: used as-is
     * - bool: converted to string â€œ0â€� (false) or â€œ1â€� (true)
     * - int: converted to numeric string (123 -> â€œ123â€�)
     * - float: converted to decimal string (3.14 -> â€œ3.14â€�)
     * - Backed Enum: converted to their backing value and then stringify see int and string
     * - array: empty array: An empty array has zero items, therefore empty arrays are omitted from the query parameter list.
     *     - lists: Becomes a repeated name suffixed with empty brackets (ie "a" with ["foo", false, 1.23] will result in a[]=foo&a[]=0&a[]=1.23)
     *     - maps: Becomes a repeated name suffixed with brackets containing the key (ie "a" with ["b" => "foo", "c" => false, "d" => 1.23] will result in a[b]=foo&a[c]=0&a[d]=1.23)
     *
     * This contract is stable and independent of PHP's http_build_query implementation.
     */
    case Safe;
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\IPv6;

use BackedEnum;
use Stringable;
use ValueError;

use function filter_var;
use function implode;
use function inet_pton;
use function str_split;
use function strtolower;
use function unpack;

use const FILTER_FLAG_IPV6;
use const FILTER_VALIDATE_IP;

final class Converter
{
    /**
     * Significant 10 bits of IP to detect Zone ID regular expression pattern.
     *
     * @var string
     */
    private const HOST_ADDRESS_BLOCK = "\xfe\x80";

    public static function compressIp(BackedEnum|string $ipAddress): string
    {
        if ($ipAddress instanceof BackedEnum) {
            $ipAddress = (string) $ipAddress->value;
        }

        return match (filter_var($ipAddress, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
            false => throw new ValueError('The submitted IP is not a valid IPv6 address.'),
            default =>  strtolower((string) inet_ntop((string) inet_pton($ipAddress))),
        };
    }

    public static function expandIp(BackedEnum|string $ipAddress): string
    {
        if ($ipAddress instanceof BackedEnum) {
            $ipAddress = (string) $ipAddress->value;
        }

        if (false === filter_var($ipAddress, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
            throw new ValueError('The submitted IP is not a valid IPv6 address.');
        }

        $hex = (array) unpack('H*hex', (string) inet_pton($ipAddress));

        return implode(':', str_split(strtolower($hex['hex'] ?? ''), 4));
    }

    public static function compress(BackedEnum|Stringable|string|null $host): ?string
    {
        $components = self::parse($host);
        if (null === $components['ipAddress']) {
            return match (true) {
                null === $host => $host,
                $host instanceof BackedEnum => (string) $host->value,
                default => (string) $host,
            };
        }

        $components['ipAddress'] = self::compressIp($components['ipAddress']);

        return self::build($components);
    }

    public static function expand(Stringable|string|null $host): ?string
    {
        $components = self::parse($host);
        if (null === $components['ipAddress']) {
            return match ($host) {
                null => $host,
                default => (string) $host,
            };
        }

        $components['ipAddress'] = self::expandIp($components['ipAddress']);

        return self::build($components);
    }

    public static function build(array $components): string
    {
        $components['ipAddress'] ??= null;
        $components['zoneIdentifier'] ??= null;

        if (null === $components['ipAddress']) {
            return '';
        }

        return '['.$components['ipAddress'].match ($components['zoneIdentifier']) {
            null => '',
            default => '%'.$components['zoneIdentifier'],
        }.']';
    }

    /**
     * @return array{ipAddress:string|null, zoneIdentifier:string|null}
     */
    private static function parse(BackedEnum|Stringable|string|null $host): array
    {
        if (null === $host) {
            return ['ipAddress' => null, 'zoneIdentifier' => null];
        }

        if ($host instanceof BackedEnum) {
            $host = $host->value;
        }

        $host = (string) $host;
        if ('' === $host) {
            return ['ipAddress' => null, 'zoneIdentifier' => null];
        }

        if (!str_starts_with($host, '[')) {
            return ['ipAddress' => null, 'zoneIdentifier' => null];
        }

        if (!str_ends_with($host, ']')) {
            return ['ipAddress' => null, 'zoneIdentifier' => null];
        }

        [$ipv6, $zoneIdentifier] = explode('%', substr($host, 1, -1), 2) + [1 => null];
        if (false === filter_var($ipv6, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
            return ['ipAddress' => null, 'zoneIdentifier' => null];
        }

        return match (true) {
            null === $zoneIdentifier,
            is_string($ipv6) && str_starts_with((string)inet_pton($ipv6), self::HOST_ADDRESS_BLOCK) =>  ['ipAddress' => $ipv6, 'zoneIdentifier' => $zoneIdentifier],
            default => ['ipAddress' => null, 'zoneIdentifier' => null],
        };
    }

    /**
     * Tells whether the host is an IPv6.
     */
    public static function isIpv6(BackedEnum|Stringable|string|null $host): bool
    {
        return null !== self::parse($host)['ipAddress'];
    }

    public static function normalize(BackedEnum|Stringable|string|null $host): ?string
    {
        if ($host instanceof BackedEnum) {
            $host = $host->value;
        }

        if (null === $host || '' === $host) {
            return $host;
        }

        $host = (string) $host;
        $components = self::parse($host);
        if (null === $components['ipAddress']) {
            return strtolower($host);
        }

        $components['ipAddress'] = strtolower(self::expandIp($components['ipAddress']));

        return self::build($components);
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri;

enum HostType
{
    case RegisteredName;
    case Ipv4;
    case Ipv6;
    case IpvFuture;
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri;

enum UriComparisonMode
{
    case IncludeFragment;
    case ExcludeFragment;
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri;

use BackedEnum;
use Deprecated;
use League\Uri\Exceptions\SyntaxError;
use League\Uri\Idna\Converter as IdnaConverter;
use Stringable;
use Throwable;

use function array_map;
use function array_merge;
use function array_pop;
use function array_reduce;
use function defined;
use function explode;
use function filter_var;
use function function_exists;
use function implode;
use function preg_match;
use function sprintf;
use function str_replace;
use function strpos;
use function strtolower;
use function substr;

use const FILTER_FLAG_IPV4;
use const FILTER_VALIDATE_IP;

/**
 * A class to parse a URI string according to RFC3986.
 *
 * @link    https://tools.ietf.org/html/rfc3986
 * @package League\Uri
 * @author  Ignace Nyamagana Butera <nyamsprod@gmail.com>
 * @since   6.0.0
 *
 * @phpstan-type AuthorityMap array{user: ?string, pass: ?string, host: ?string, port: ?int}
 * @phpstan-type ComponentMap array{scheme: ?string, user: ?string, pass: ?string, host: ?string, port: ?int, path: string, query: ?string, fragment: ?string}
 * @phpstan-type InputComponentMap array{scheme? : ?string, user? : ?string, pass? : ?string, host? : ?string, port? : ?int, path? : ?string, query? : ?string, fragment? : ?string}
 */
final class UriString
{
    /**
     * Default URI component values.
     *
     * @var ComponentMap
     */
    private const URI_COMPONENTS = [
        'scheme' => null, 'user' => null, 'pass' => null, 'host' => null,
        'port' => null, 'path' => '', 'query' => null, 'fragment' => null,
    ];

    /**
     * Simple URI which do not need any parsing.
     *
     * @var array<string, array<string>>
     */
    private const URI_SHORTCUTS = [
        '' => ['path' => ''],
        '#' => ['fragment' => ''],
        '?' => ['query' => ''],
        '?#' => ['query' => '', 'fragment' => ''],
        '/' => ['path' => '/'],
        '//' => ['host' => ''],
        '///' => ['host' => '', 'path' => '/'],
    ];

    /**
     * Range of invalid characters in URI 3986 string.
     *
     * @var string
     */
    private const REGEXP_VALID_URI_RFC3986_CHARS = '/^(?:[A-Za-z0-9\-._~:\/?#[\]@!$&\'()*+,;=%]|%[0-9A-Fa-f]{2})*$/';

    /**
     * Range of invalid characters in URI 3987 string.
     *
     * @var string
     */
    private const REGEXP_INVALID_URI_RFC3987_CHARS = '/[\x00-\x1f\x7f\s]/';

    /**
     * RFC3986 regular expression URI splitter.
     *
     * @link https://tools.ietf.org/html/rfc3986#appendix-B
     * @var string
     */
    private const REGEXP_URI_PARTS = ',^
        (?<scheme>(?<scontent>[^:/?\#]+):)?    # URI scheme component
        (?<authority>//(?<acontent>[^/?\#]*))? # URI authority part
        (?<path>[^?\#]*)                       # URI path component
        (?<query>\?(?<qcontent>[^\#]*))?       # URI query component
        (?<fragment>\#(?<fcontent>.*))?        # URI fragment component
    ,x';

    /**
     * URI scheme regular expression.
     *
     * @link https://tools.ietf.org/html/rfc3986#section-3.1
     * @var string
     */
    private const REGEXP_URI_SCHEME = '/^([a-z][a-z\d+.-]*)?$/i';

    /**
     * Invalid path for URI without scheme and authority regular expression.
     *
     * @link https://tools.ietf.org/html/rfc3986#section-3.3
     * @var string
     */
    private const REGEXP_INVALID_PATH = ',^(([^/]*):)(.*)?/,';

    /**
     * Host and Port splitter regular expression.
     *
     * @var string
     */
    private const REGEXP_HOST_PORT = ',^(?<host>\[.*\]|[^:]*)(:(?<port>.*))?$,';

    /** @var array<string,int> */
    private const DOT_SEGMENTS = ['.' => 1, '..' => 1];

    /**
     * Generate an IRI string representation (RFC3987) from its parsed representation
     * returned by League\UriString::parse() or PHP's parse_url.
     *
     * If you supply your own array, you are responsible for providing
     * valid components without their URI delimiters.
     *
     * @link https://tools.ietf.org/html/rfc3986#section-5.3
     * @link https://tools.ietf.org/html/rfc3986#section-7.5
     */
    public static function toIriString(BackedEnum|Stringable|string $uri): string
    {
        $components = self::parse($uri);
        $port = null;
        if (isset($components['port'])) {
            $port = (int) $components['port'];
            unset($components['port']);
        }

        if (null !== $components['host']) {
            $components['host'] = IdnaConverter::toUnicode($components['host'])->domain();
        }

        $components['path'] = Encoder::decodePath($components['path']);
        $components['user'] = Encoder::decodeNecessary($components['user']);
        $components['pass'] = Encoder::decodeNecessary($components['pass']);
        $components['query'] = Encoder::decodeQuery($components['query']);
        $components['fragment'] = Encoder::decodeFragment($components['fragment']);

        return self::build([
            ...array_map(fn (?string $value) => match (true) {
                null === $value,
                !str_contains($value, '%20') => $value,
                default => str_replace('%20', ' ', $value),
            }, $components),
            ...['port' => $port],
        ]);
    }

    /**
     * Generate a URI string representation from its parsed representation
     * returned by League\UriString::parse() or PHP's parse_url.
     *
     * If you supply your own array, you are responsible for providing
     * valid components without their URI delimiters.
     *
     * @link https://tools.ietf.org/html/rfc3986#section-5.3
     * @link https://tools.ietf.org/html/rfc3986#section-7.5
     *
     * @param InputComponentMap $components
     */
    public static function build(array $components): string
    {
        return self::buildUri(
            $components['scheme'] ?? null,
            self::buildAuthority($components),
            $components['path'] ?? null,
            $components['query'] ?? null,
            $components['fragment'] ?? null,
        );
    }

    /**
     * Generates a URI string representation based on RFC3986 algorithm.
     *
     * Valid URI component MUST be provided without their URI delimiters
     * but properly encoded.
     *
     * @link https://tools.ietf.org/html/rfc3986#section-5.3
     * @link https://tools.ietf.org/html/rfc3986#section-7.5Â§
     */
    public static function buildUri(
        ?string $scheme = null,
        ?string $authority = null,
        ?string $path = null,
        ?string $query = null,
        ?string $fragment = null,
    ): string {
        self::validateComponents($scheme, $authority, $path);
        $uri = '';
        if (null !== $scheme) {
            $uri .= $scheme.':';
        }

        if (null !== $authority) {
            $uri .= '//'.$authority;
        }

        $uri .= $path;
        if (null !== $query) {
            $uri .= '?'.$query;
        }

        if (null !== $fragment) {
            $uri .= '#'.$fragment;
        }

        return $uri;
    }

    /**
     * Generate a URI authority representation from its parsed representation.
     *
     * @param InputComponentMap $components
     */
    public static function buildAuthority(array $components): ?string
    {
        if (!isset($components['host'])) {
            (!isset($components['user']) && !isset($components['pass'])) || throw new SyntaxError('The user info component must not be set if the host is not defined.');
            !isset($components['port']) || throw new SyntaxError('The port component must not be set if the host is not defined.');

            return null;
        }

        $userInfo = $components['user'] ?? null;
        if (isset($components['pass'])) {
            $userInfo .= ':'.$components['pass'];
        }

        $authority = '';
        if (isset($userInfo)) {
            $authority .= $userInfo.'@';
        }

        $authority .= $components['host'];
        if (isset($components['port'])) {
            $authority .= ':'.$components['port'];
        }

        return $authority;
    }

    /**
     * Parses and normalizes the URI following RFC3986 destructive and non-destructive constraints.
     *
     * @throws SyntaxError if the URI is not parsable
     *
     * @return ComponentMap
     */
    public static function parseNormalized(Stringable|string $uri): array
    {
        $components = self::parse($uri);

        if (null !== $components['scheme']) {
            $components['scheme'] = strtolower($components['scheme']);
        }

        $components['host'] = self::normalizeHost($components['host']);

        $path = $components['path'];
        $authority = self::buildAuthority($components);
        $absolutePath = str_starts_with($path, '/');

        // A leading `/.` is used internally to protect a path starting with
        // `//` from being interpreted as an authority.
        if (null === $authority && str_starts_with($path, '/.//')) {
            $path = substr($path, 2);
        }

        // Dot-segment removal applies to:
        // - absolute paths
        // - paths with a scheme or authority
        // - relative paths containing dot segments
        if (
            '/' === ($path[0] ?? '')
            || null !== $components['scheme']
            || null !== $authority
        ) {
            $path = self::removeDotSegments(
                strtr($path, ['%2E' => '.', '%2e' => '.'])
            );
        }

        // A path starting with `//` would be interpreted as an authority when
        // serialized after a scheme (or as a network-path reference otherwise).
        if (null === $authority && str_starts_with($path, '//')) {
            $path = ($absolutePath ? '/.' : './').$path;
        }

        // If there is an authority, the path must be absolute.
        if ('' !== $path && '/' !== $path[0] && null !== $authority) {
            $path = '/'.$path;
        }

        // A relative path whose first segment contains ':' must be
        // prefixed with './' to remain a path-noscheme.
        if (
            null === $components['scheme']
            && null === $authority
            && '' !== $path
            && '/' !== $path[0]
            && str_contains(explode('/', $path, 2)[0], ':')
        ) {
            $path = './'.$path;
        }

        $components['path'] = (string) Encoder::normalizePath($path);
        $components['query'] = Encoder::normalizeQuery($components['query']);
        $components['fragment'] = Encoder::normalizeFragment($components['fragment']);
        $components['user'] = Encoder::normalizeUser($components['user']);
        $components['pass'] = Encoder::normalizePassword($components['pass']);

        return $components;
    }

    /**
     * Parses and normalizes the URI following RFC3986 destructive and non-destructive constraints.
     *
     * @throws SyntaxError if the URI is not parsable
     */
    public static function normalize(Stringable|string $uri): string
    {
        return self::build(self::parseNormalized($uri));
    }

    /**
     * Parses and normalizes the URI following RFC3986 destructive and non-destructive constraints.
     *
     * @throws SyntaxError if the URI is not parsable
     */
    public static function normalizeAuthority(Stringable|string|null $authority): ?string
    {
        if (null === $authority) {
            return null;
        }

        $components = self::parseAuthority($authority);
        $components['host'] = self::normalizeHost($components['host'] ?? null);
        $components['user'] = Encoder::normalizeUser($components['user']);
        $components['pass'] = Encoder::normalizePassword($components['pass']);

        return (string) self::buildAuthority($components);
    }

    /**
     * Resolves a URI against a base URI using RFC3986 rules.
     *
     * This method MUST retain the state of the submitted URI instance, and return
     * a URI instance of the same type that contains the applied modifications.
     *
     * This method MUST be transparent when dealing with error and exceptions.
     * It MUST not alter or silence them apart from validating its own parameters.
     *
     * @see https://www.rfc-editor.org/rfc/rfc3986.html#section-5
     *
     * @throws SyntaxError if the BaseUri is not absolute or in absence of a BaseUri if the uri is not absolute
     */
    public static function resolve(BackedEnum|Stringable|string $uri, BackedEnum|Stringable|string|null $baseUri = null): string
    {
        if ($uri instanceof BackedEnum) {
            $uri = (string) $uri->value;
        }

        if ($baseUri instanceof BackedEnum) {
            $baseUri = (string) $baseUri->value;
        }

        $uri = (string) $uri;
        if ('' === $uri) {
            $uri = $baseUri ?? throw new SyntaxError("The uri can not be the empty string when there's no base URI.");
        }

        $uriComponents = self::parse($uri);
        $baseUriComponents = $uriComponents;
        if (null !== $baseUri && $uri !== (string) $baseUri) {
            $baseUriComponents = self::parse($baseUri);
        }

        null !== $baseUriComponents['scheme'] || throw new SyntaxError('The base URI must be an absolute URI or null; If the base URI is null the URI must be an absolute URI.');

        $authority = self::buildAuthority($uriComponents);
        $path = self::removeDotSegments($uriComponents['path']);
        if ('' !== $path && '/' !== $path[0] && (null !== $authority || $uriComponents['path'] !== $path)) {
            $path = '/'.$path;
        }

        if (null !== $uriComponents['scheme'] && '' !== $uriComponents['scheme']) {
            return self::buildUri($uriComponents['scheme'], $authority, $path, $uriComponents['query'], $uriComponents['fragment']);
        }

        if (null !== $authority) {
            return self::buildUri($baseUriComponents['scheme'], $authority, $path, $uriComponents['query'], $uriComponents['fragment']);
        }

        [$resolvedPath, $query] = self::resolvePathAndQuery($uriComponents, $baseUriComponents);
        $baseAuthority = self::buildAuthority($baseUriComponents);
        $path = self::removeDotSegments($resolvedPath);
        if ('' !== $path && '/' !== $path[0] && (null !== $baseAuthority || $resolvedPath !== $path)) {
            $path = '/'.$path;
        }

        return self::buildUri($baseUriComponents['scheme'], $baseAuthority, $path, $query, $uriComponents['fragment']);
    }

    /**
     * Filter Dot segment according to RFC3986.
     *
     * @see http://tools.ietf.org/html/rfc3986#section-5.2.4
     */
    public static function removeDotSegments(Stringable|string $path): string
    {
        $path = (string) $path;
        if (!str_contains($path, '.')) {
            return $path;
        }

        $reducer = function (array $carry, string $segment): array {
            if ('..' === $segment) {
                array_pop($carry);

                return $carry;
            }

            if (!isset(self::DOT_SEGMENTS[$segment])) {
                $carry[] = $segment;
            }

            return $carry;
        };

        $oldSegments = explode('/', $path);
        $newPath = implode('/', array_reduce($oldSegments, $reducer(...), []));
        if (isset(self::DOT_SEGMENTS[$oldSegments[array_key_last($oldSegments)]])) {
            $newPath .= '/';
        }

        // A ".." overshooting the root must not turn an absolute path into a
        // rootless one; RFC3986 Â§5.2.4 keeps the leading slash (as BaseUri does).
        if (str_starts_with($path, '/') && !str_starts_with($newPath, '/')) {
            return '/'.$newPath;
        }

        return $newPath;
    }

    /**
     * Resolves an URI path and query component.
     *
     * @param ComponentMap $uri
     * @param ComponentMap $baseUri
     *
     * @return array{0:string, 1:string|null}
     */
    private static function resolvePathAndQuery(array $uri, array $baseUri): array
    {
        if (str_starts_with($uri['path'], '/')) {
            return [$uri['path'], $uri['query']];
        }

        if ('' === $uri['path']) {
            return [$baseUri['path'], $uri['query'] ?? $baseUri['query']];
        }

        $targetPath = $uri['path'];
        if (null !== self::buildAuthority($baseUri) && '' === $baseUri['path']) {
            $targetPath = '/'.$targetPath;
        }

        if ('' !== $baseUri['path']) {
            $segments = explode('/', $baseUri['path']);
            array_pop($segments);
            if ([] !== $segments) {
                $targetPath = implode('/', $segments).'/'.$targetPath;
            }
        }

        return [$targetPath, $uri['query']];
    }

    public static function containsRfc3986Chars(Stringable|string $uri): bool
    {
        return 1 === preg_match(self::REGEXP_VALID_URI_RFC3986_CHARS, (string) $uri);
    }

    public static function containsRfc3987Chars(Stringable|string $uri): bool
    {
        return 1 !== preg_match(self::REGEXP_INVALID_URI_RFC3987_CHARS, (string) $uri);
    }

    /**
     * Parse a URI string into its components.
     *
     * This method parses a URI and returns an associative array containing any
     * of the various components of the URI that are present.
     *
     * <code>
     * $components = UriString::parse('http://foo@test.example.com:42?query#');
     * var_export($components);
     * //will display
     * array(
     *   'scheme' => 'http',           // the URI scheme component
     *   'user' => 'foo',              // the URI user component
     *   'pass' => null,               // the URI pass component
     *   'host' => 'test.example.com', // the URI host component
     *   'port' => 42,                 // the URI port component
     *   'path' => '',                 // the URI path component
     *   'query' => 'query',           // the URI query component
     *   'fragment' => '',             // the URI fragment component
     * );
     * </code>
     *
     * The returned array is similar to PHP's parse_url return value with the following
     * differences:
     *
     * <ul>
     * <li>All components are always present in the returned array</li>
     * <li>Empty and undefined component are treated differently. And empty component is
     *   set to the empty string while an undefined component is set to the `null` value.</li>
     * <li>The path component is never undefined</li>
     * <li>The method parses the URI following the RFC3986 rules, but you are still
     *   required to validate the returned components against its related scheme specific rules.</li>
     * </ul>
     *
     * @link https://tools.ietf.org/html/rfc3986
     *
     * @throws SyntaxError if the URI contains invalid characters
     * @throws SyntaxError if the URI contains an invalid scheme
     * @throws SyntaxError if the URI contains an invalid path
     *
     * @return ComponentMap
     */
    public static function parse(BackedEnum|Stringable|string|int $uri): array
    {
        if ($uri instanceof BackedEnum) {
            $uri = $uri->value;
        }

        $uri = (string) $uri;
        if (isset(self::URI_SHORTCUTS[$uri])) {
            /** @var ComponentMap $components */
            $components = [...self::URI_COMPONENTS, ...self::URI_SHORTCUTS[$uri]];

            return $components;
        }

        self::containsRfc3987Chars($uri) || throw new SyntaxError(sprintf('The uri `%s` contains invalid characters', $uri));

        //if the first character is a known URI delimiter, parsing can be simplified
        $first_char = $uri[0];

        //The URI is made of the fragment only
        if ('#' === $first_char) {
            [, $fragment] = explode('#', $uri, 2);
            $components = self::URI_COMPONENTS;
            $components['fragment'] = $fragment;

            return $components;
        }

        //The URI is made of the query and fragment
        if ('?' === $first_char) {
            [, $partial] = explode('?', $uri, 2);
            [$query, $fragment] = explode('#', $partial, 2) + [1 => null];
            $components = self::URI_COMPONENTS;
            $components['query'] = $query;
            $components['fragment'] = $fragment;

            return $components;
        }

        //use RFC3986 URI regexp to split the URI
        preg_match(self::REGEXP_URI_PARTS, $uri, $parts);
        $parts += ['query' => '', 'fragment' => ''];

        if (':' === ($parts['scheme'] ?? null) || 1 !== preg_match(self::REGEXP_URI_SCHEME, $parts['scontent'] ?? '')) {
            throw new SyntaxError(sprintf('The uri `%s` contains an invalid scheme', $uri));
        }

        if ('' === ($parts['scheme'] ?? '').($parts['authority'] ?? '') && 1 === preg_match(self::REGEXP_INVALID_PATH, $parts['path'] ?? '')) {
            throw new SyntaxError(sprintf('The uri `%s` contains an invalid path.', $uri));
        }

        /** @var ComponentMap $components */
        $components = array_merge(
            self::URI_COMPONENTS,
            '' === ($parts['authority'] ?? null) ? [] : self::parseAuthority($parts['acontent'] ?? null),
            [
                'path' => $parts['path'] ?? '',
                'scheme' => '' === ($parts['scheme'] ?? null) ? null : ($parts['scontent'] ?? null),
                'query' => '' === $parts['query'] ? null : ($parts['qcontent'] ?? null),
                'fragment' => '' === $parts['fragment'] ? null : ($parts['fcontent'] ?? null),
            ]
        );

        return $components;
    }

    /**
     * Assert the URI internal state is valid.
     *
     * @link https://tools.ietf.org/html/rfc3986#section-3
     * @link https://tools.ietf.org/html/rfc3986#section-3.3
     *
     * @throws SyntaxError
     */
    private static function validateComponents(?string $scheme, ?string $authority, ?string $path): void
    {
        if (null !== $authority) {
            if (null !== $path && '' !== $path && '/' !== $path[0]) {
                throw new SyntaxError('If an authority is present the path must be empty or start with a `/`.');
            }

            return;
        }

        if (null === $path || '' === $path) {
            return;
        }

        !str_starts_with($path, '//') || throw new SyntaxError('If there is no authority the path `'.$path.'` cannot start with a `//`.');
        if (null !== $scheme || false === ($pos = strpos($path, ':'))) {
            return;
        }

        if (!str_contains(substr($path, 0, $pos), '/')) {
            throw new SyntaxError('In absence of a scheme and an authority the first path segment cannot contain a colon (":") character.');
        }
    }

    /**
     * Parses the URI authority part.
     *
     * @link https://tools.ietf.org/html/rfc3986#section-3.2
     *
     * @throws SyntaxError If the port component is invalid
     *
     * @return AuthorityMap
     */
    public static function parseAuthority(BackedEnum|Stringable|string|null $authority): array
    {
        $components = ['user' => null, 'pass' => null, 'host' => null, 'port' => null];
        if (null === $authority) {
            return $components;
        }

        if ($authority instanceof BackedEnum) {
            $authority = $authority->value;
        }
        $authority = (string) $authority;
        $components['host'] = '';
        if ('' === $authority) {
            return $components;
        }

        $parts = explode('@', $authority, 2);
        if (isset($parts[1])) {
            [$components['user'], $components['pass']] = explode(':', $parts[0], 2) + [1 => null];
        }

        preg_match(self::REGEXP_HOST_PORT, $parts[1] ?? $parts[0], $matches);
        $matches += ['port' => ''];

        $components['port'] = self::filterPort($matches['port']);
        $components['host'] = self::filterHost($matches['host'] ?? '');

        return $components;
    }

    /**
     * Filter and format the port component.
     *
     * @link https://tools.ietf.org/html/rfc3986#section-3.2.2
     *
     * @throws SyntaxError if the registered name is invalid
     */
    private static function filterPort(string $port): ?int
    {
        return match (true) {
            '' === $port => null,
            1 === preg_match('/^\d*$/', $port) => (int) $port,
            default => throw new SyntaxError(sprintf('The port `%s` is invalid', $port)),
        };
    }

    /**
     * Returns whether a hostname is valid.
     *
     * @link https://tools.ietf.org/html/rfc3986#section-3.2.2
     *
     * @throws SyntaxError if the registered name is invalid
     */
    private static function filterHost(Stringable|string|null $host): ?string
    {
        try {
            return HostRecord::from($host)->value;
        } catch (Throwable) {
            throw new SyntaxError(sprintf('Host `%s` is invalid : the IP host is malformed', $host));
        }
    }

    /**
     * Tells whether the scheme component is valid.
     */
    public static function isValidScheme(BackedEnum|Stringable|string|null $scheme): bool
    {
        if ($scheme instanceof BackedEnum) {
            $scheme = $scheme->value;
        }

        return null === $scheme || 1 === preg_match('/^[A-Za-z]([-A-Za-z\d+.]+)?$/', (string) $scheme);
    }

    private static function normalizeHost(BackedEnum|Stringable|string|null $host): ?string
    {
        if ($host instanceof BackedEnum) {
            $host = $host->value;
        }

        if (null !== $host) {
            $host = (string) $host;
        }

        if (null === $host || false !== filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
            return $host;
        }

        $host = (string) Encoder::normalizeHost($host);
        static $isSupported = null;
        $isSupported ??= (function_exists('\idn_to_ascii') && defined('\INTL_IDNA_VARIANT_UTS46'));
        if (! $isSupported) {
            return $host;
        }

        $idnaHost = IdnaConverter::toAscii($host);
        if (!$idnaHost->hasErrors()) {
            return $idnaHost->domain();
        }

        return $host;
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.6.0
     * @codeCoverageIgnore
     * @see HostRecoord::validate()
     *
     * Create a new instance from the environment.
     */
    #[Deprecated(message:'use League\Uri\HostRecord::validate() instead', since:'league/uri:7.6.0')]
    public static function isValidHost(Stringable|string|null $host): bool
    {
        return HostRecord::isValid($host);
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\KeyValuePair;

use BackedEnum;
use League\Uri\Exceptions\SyntaxError;
use League\Uri\StringCoercionMode;
use Stringable;

use function array_combine;
use function explode;
use function implode;
use function is_string;
use function preg_match;
use function str_replace;

use const PHP_QUERY_RFC1738;
use const PHP_QUERY_RFC3986;

final class Converter
{
    private const REGEXP_INVALID_CHARS = '/[\x00-\x1f\x7f]/';

    /**
     * @param non-empty-string $separator the query string separator
     * @param array<string> $fromRfc3986 contains all the RFC3986 encoded characters to be converted
     * @param array<string> $toEncoding contains all the expected encoded characters
     */
    private function __construct(
        private readonly string $separator,
        private readonly array $fromRfc3986 = [],
        private readonly array $toEncoding = [],
    ) {
        if ('' === $this->separator) {
            throw new SyntaxError('The separator character must be a non empty string.');
        }
    }

    /**
     * @param non-empty-string $separator
     */
    public static function new(string $separator): self
    {
        return new self($separator);
    }

    /**
     * @param non-empty-string $separator
     */
    public static function fromRFC3986(string $separator = '&'): self
    {
        return self::new($separator);
    }

    /**
     * @param non-empty-string $separator
     */
    public static function fromRFC1738(string $separator = '&'): self
    {
        return self::new($separator)
            ->withEncodingMap(['%20' => '+']);
    }

    /**
     * @param non-empty-string $separator
     *
     * @see https://url.spec.whatwg.org/#application/x-www-form-urlencoded
     */
    public static function fromFormData(string $separator = '&'): self
    {
        return self::new($separator)
            ->withEncodingMap(['%20' => '+', '%2A' => '*']);
    }

    public static function fromEncodingType(int $encType): self
    {
        return match ($encType) {
            PHP_QUERY_RFC3986 => self::fromRFC3986(),
            PHP_QUERY_RFC1738 => self::fromRFC1738(),
            default => throw new SyntaxError('Unknown or Unsupported encoding.'),
        };
    }

    /**
     * @return non-empty-string
     */
    public function separator(): string
    {
        return $this->separator;
    }

    /**
     * @return array<string, string>
     */
    public function encodingMap(): array
    {
        return array_combine($this->fromRfc3986, $this->toEncoding);
    }

    /**
     * @return list<non-empty-array<string|null>>
     */
    public function toPairs(BackedEnum|Stringable|string|int|float|bool|null $value): array
    {
        $value = StringCoercionMode::Native->coerce($value);
        if (null === $value) {
            return [];
        }

        $value = match (1) {
            preg_match(self::REGEXP_INVALID_CHARS, $value) => throw new SyntaxError('Invalid query string: `'.$value.'`.'),
            default => str_replace($this->toEncoding, $this->fromRfc3986, $value),
        };

        return array_map(
            fn (string $pair): array => explode('=', $pair, 2) + [1 => null],
            explode($this->separator, $value)
        );
    }

    /**
     * @param iterable<array{0:string|null, 1:BackedEnum|Stringable|string|bool|int|float|null}> $pairs
     */
    public function toValue(iterable $pairs): ?string
    {
        $filteredPairs = [];
        foreach ($pairs as $pair) {
            $filteredPairs[] = match (true) {
                !is_string($pair[0]) => throw new SyntaxError('the pair key MUST be a string;, `'.gettype($pair[0]).'` given.'),
                null === $pair[1] => StringCoercionMode::Native->coerce($pair[0]),
                default => StringCoercionMode::Native->coerce($pair[0]).'='.StringCoercionMode::Native->coerce($pair[1]),
            };
        }

        return match ([]) {
            $filteredPairs => null,
            default => str_replace($this->fromRfc3986, $this->toEncoding, implode($this->separator, $filteredPairs)),
        };
    }

    /**
     * @param non-empty-string $separator
     */
    public function withSeparator(string $separator): self
    {
        return match ($this->separator) {
            $separator => $this,
            default => new self($separator, $this->fromRfc3986, $this->toEncoding),
        };
    }

    /**
     * Sets the conversion map.
     *
     * Each key from the iterable structure represents the RFC3986 encoded characters as string,
     * while each value represents the expected output encoded characters
     */
    public function withEncodingMap(iterable $encodingMap): self
    {
        $fromRfc3986 = [];
        $toEncoding = [];
        foreach ($encodingMap as $from => $to) {
            [$fromRfc3986[], $toEncoding[]] = match (true) {
                !is_string($from) => throw new SyntaxError('The encoding output must be a string; `'.gettype($from).'` given.'),
                $to instanceof Stringable,
                is_string($to) => [$from, (string) $to],
                default => throw new SyntaxError('The encoding output must be a string; `'.gettype($to).'` given.'),
            };
        }

        return match (true) {
            $fromRfc3986 !== $this->fromRfc3986,
            $toEncoding !== $this->toEncoding => new self($this->separator, $fromRfc3986, $toEncoding),
            default => $this,
        };
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri;

use BackedEnum;
use Closure;
use Deprecated;
use League\Uri\Exceptions\SyntaxError;
use League\Uri\IPv6\Converter as IPv6Converter;
use SensitiveParameter;
use Stringable;
use Throwable;

use function explode;
use function filter_var;
use function gettype;
use function preg_match;
use function preg_replace_callback;
use function rawurldecode;
use function rawurlencode;
use function sprintf;
use function str_starts_with;
use function strtolower;
use function strtoupper;

use const FILTER_FLAG_IPV4;
use const FILTER_VALIDATE_IP;

final class Encoder
{
    private const REGEXP_CHARS_INVALID = '/[\x00-\x1f\x7f]/';
    private const REGEXP_CHARS_ENCODED = ',%[A-Fa-f0-9]{2},';
    private const REGEXP_CHARS_PREVENTS_DECODING = ',%2[A-F1-24-9]|3[0-9BD]|4[1-9A-F]|5[0-9AF]|6[1-9A-F]|7[0-9E],i';
    private const REGEXP_PART_SUBDELIM = "\!\$&'\(\)\*\+,;\=%";
    private const REGEXP_PART_UNRESERVED = 'A-Za-z\d_\-.~';
    private const REGEXP_PART_ENCODED = '%(?![A-Fa-f\d]{2})';
    private const RFC3986_RESERVED_CHARACTERS = ':/?#[]@!$&\'()*+,;=';

    /**
     * Unreserved characters.
     *
     * @see https://www.rfc-editor.org/rfc/rfc3986.html#section-2.3
     */
    private const REGEXP_UNRESERVED_CHARACTERS = ',%(2[DdEe]|3[0-9]|4[1-9A-Fa-f]|5[0-9AaFf]|6[1-9A-Fa-f]|7[0-9AaEe]),';

    /**
     * Tell whether the user component is correctly encoded.
     */
    public static function isUserEncoded(BackedEnum|Stringable|string|null $encoded): bool
    {
        static $pattern = '/[^'.self::REGEXP_PART_UNRESERVED.self::REGEXP_PART_SUBDELIM.']+|'.self::REGEXP_PART_ENCODED.'/';

        if ($encoded instanceof BackedEnum) {
            $encoded = $encoded->value;
        }

        return null === $encoded || 1 !== preg_match($pattern, (string) $encoded);
    }

    /**
     * Encode User.
     *
     * All generic delimiters MUST be encoded
     */
    public static function encodeUser(BackedEnum|Stringable|string|null $user): ?string
    {
        static $pattern = '/[^'.self::REGEXP_PART_UNRESERVED.self::REGEXP_PART_SUBDELIM.']+|'.self::REGEXP_PART_ENCODED.'/';

        return self::encode($user, $pattern);
    }

    /**
     * Normalize user component.
     *
     * The value returned MUST be percent-encoded, but MUST NOT double-encode
     * any characters. To determine what characters to encode, please refer to
     * RFC 3986.
     */
    public static function normalizeUser(BackedEnum|Stringable|string|null $user): ?string
    {
        return self::normalize(self::encodeUser(self::decodeUnreservedCharacters($user)));
    }

    private static function normalize(?string $component): ?string
    {
        if (null === $component) {
            return null;
        }

        return (string) preg_replace_callback(
            '/%[0-9a-f]{2}/i',
            static fn (array $found) => strtoupper($found[0]),
            $component
        );
    }

    /**
     * Tell whether the password component is correctly encoded.
     */
    public static function isPasswordEncoded(#[SensitiveParameter] BackedEnum|Stringable|string|null $encoded): bool
    {
        static $pattern = '/[^'.self::REGEXP_PART_UNRESERVED.self::REGEXP_PART_SUBDELIM.':]+|'.self::REGEXP_PART_ENCODED.'/';

        if ($encoded instanceof BackedEnum) {
            $encoded = $encoded->value;
        }

        return null === $encoded || 1 !== preg_match($pattern, (string) $encoded);
    }

    /**
     * Encode Password.
     *
     * Generic delimiters ":" MUST NOT be encoded
     */
    public static function encodePassword(#[SensitiveParameter] BackedEnum|Stringable|string|null $component): ?string
    {
        static $pattern = '/[^'.self::REGEXP_PART_UNRESERVED.self::REGEXP_PART_SUBDELIM.':]+|'.self::REGEXP_PART_ENCODED.'/';

        return self::encode($component, $pattern);
    }

    /**
     * Normalize password component.
     *
     * The value returned MUST be percent-encoded, but MUST NOT double-encode
     * any characters. To determine what characters to encode, please refer to
     * RFC 3986.
     */
    public static function normalizePassword(#[SensitiveParameter] BackedEnum|Stringable|string|null $password): ?string
    {
        return self::normalize(self::encodePassword(self::decodeUnreservedCharacters($password)));
    }

    /**
     * Tell whether the userInfo component is correctly encoded.
     */
    public static function isUserInfoEncoded(#[SensitiveParameter] BackedEnum|Stringable|string|null $userInfo): bool
    {
        if (null === $userInfo) {
            return true;
        }

        if ($userInfo instanceof BackedEnum) {
            $userInfo = $userInfo->value;
        }

        [$user, $password] = explode(':', (string) $userInfo, 2) + [1 => null];

        return self::isUserEncoded($user)
            && self::isPasswordEncoded($password);
    }

    public static function encodeUserInfo(#[SensitiveParameter] BackedEnum|Stringable|string|null $userInfo): ?string
    {
        if (null === $userInfo) {
            return null;
        }

        if ($userInfo instanceof BackedEnum) {
            $userInfo = $userInfo->value;
        }

        [$user, $password] = explode(':', (string) $userInfo, 2) + [1 => null];
        $userInfo = self::encodeUser($user);
        if (null === $password) {
            return $userInfo;
        }

        return $userInfo.':'.self::encodePassword($password);
    }

    public static function normalizeUserInfo(#[SensitiveParameter] BackedEnum|Stringable|string|null $userInfo): ?string
    {
        if (null === $userInfo) {
            return null;
        }

        if ($userInfo instanceof BackedEnum) {
            $userInfo = $userInfo->value;
        }

        [$user, $password] = explode(':', (string) $userInfo, 2) + [1 => null];
        $userInfo = self::normalizeUser($user);
        if (null === $password) {
            return $userInfo;
        }

        return $userInfo.':'.self::normalizePassword($password);
    }

    /**
     * Decodes all the URI component characters.
     */
    public static function decodeAll(BackedEnum|Stringable|string|null $component): ?string
    {
        return self::decode($component, static fn (array $matches): string => rawurldecode($matches[0]));
    }

    /**
     * Decodes the URI component while preserving percent-encoded characters that must remain encoded.
     */
    public static function decodeNecessary(BackedEnum|Stringable|string|int|null $component): ?string
    {
        $decoder = static fn (array $matches): string =>
            1 === preg_match(self::REGEXP_CHARS_PREVENTS_DECODING, $matches[0])
                ? strtoupper($matches[0])
                : rawurldecode($matches[0]);

        return self::decode($component, $decoder);
    }

    /**
     * Decodes the component unreserved characters.
     */
    public static function decodeUnreservedCharacters(BackedEnum|Stringable|string|null $str): ?string
    {
        if ($str instanceof BackedEnum) {
            $str = $str->value;
        }

        if (null === $str) {
            return null;
        }

        return preg_replace_callback(
            self::REGEXP_UNRESERVED_CHARACTERS,
            static fn (array $matches): string => rawurldecode($matches[0]),
            (string) $str
        );
    }

    /**
     * Tell whether the path component is correctly encoded.
     */
    public static function isPathEncoded(BackedEnum|Stringable|string|null $encoded): bool
    {
        static $pattern = '/[^'.self::REGEXP_PART_UNRESERVED.self::REGEXP_PART_SUBDELIM.':@\/]+|'.self::REGEXP_PART_ENCODED.'/';

        if ($encoded instanceof BackedEnum) {
            $encoded = $encoded->value;
        }

        return null === $encoded || 1 !== preg_match($pattern, (string) $encoded);
    }

    /**
     * Encode Path.
     *
     * Generic delimiters ":", "@", and "/" MUST NOT be encoded
     */
    public static function encodePath(BackedEnum|Stringable|string|null $component): string
    {
        static $pattern = '/[^'.self::REGEXP_PART_UNRESERVED.self::REGEXP_PART_SUBDELIM.':@\/]+|'.self::REGEXP_PART_ENCODED.'/';

        return (string) self::encode($component, $pattern);
    }

    /**
     * Decodes the path component while preserving characters that should not be decoded in the context of a full valid URI.
     */
    public static function decodePath(BackedEnum|Stringable|string|null $path): ?string
    {
        $path = self::filterComponent($path);
        if (null === $path || '' === $path) {
            return $path;
        }

        1 !== preg_match(self::REGEXP_CHARS_INVALID, $path) || throw new SyntaxError('Invalid component string: '.$path.'.');

        return (string) preg_replace_callback(
            '/%(?!25[A-Fa-f0-9]{2})[A-Fa-f0-9]{2}/',
            static function (array $matches): string {
                $encodedChar = strtoupper($matches[0]);
                $decodedChar = rawurldecode($encodedChar);

                return str_contains(self::RFC3986_RESERVED_CHARACTERS, $decodedChar)
                    ? $encodedChar
                    : $decodedChar;
            },
            $path,
        );
    }

    /**
     * Normalize path component.
     *
     * The value returned MUST be percent-encoded, but MUST NOT double-encode
     * any characters. To determine what characters to encode, please refer to
     * RFC 3986.
     */
    public static function normalizePath(BackedEnum|Stringable|string|null $component): ?string
    {
        return self::normalize(self::encodePath(self::decodePath($component)));
    }

    /**
     * Tell whether the query component is correctly encoded.
     */
    public static function isQueryEncoded(BackedEnum|Stringable|string|null $encoded): bool
    {
        static $pattern = '/[^'.self::REGEXP_PART_UNRESERVED.self::REGEXP_PART_SUBDELIM.'\/?%]+|'.self::REGEXP_PART_ENCODED.'/';
        if ($encoded instanceof BackedEnum) {
            $encoded = $encoded->value;
        }

        return null === $encoded || 1 !== preg_match($pattern, (string) $encoded);
    }

    /**
     * Decodes the query component while preserving characters that should not be decoded in the context of a full valid URI.
     */
    public static function decodeQuery(BackedEnum|Stringable|string|null $query): ?string
    {
        $decoder = static function (array $matches): string {
            $encodedChar = strtoupper($matches[0]);
            $decodedChar = rawurldecode($encodedChar);

            return str_contains(self::RFC3986_RESERVED_CHARACTERS, $decodedChar)
                ? $encodedChar
                : $decodedChar;
        };

        return self::decode($query, $decoder);
    }

    /**
     * Normalize the query component.
     *
     * The value returned MUST be percent-encoded, but MUST NOT double-encode
     * any characters. To determine what characters to encode, please refer to
     * RFC 3986.
     */
    public static function normalizeQuery(BackedEnum|Stringable|string|null $query): ?string
    {
        return self::normalize(self::encodeQueryOrFragment(self::decodeQuery($query)));
    }

    /**
     * Tell whether the query component is correctly encoded.
     */
    public static function isFragmentEncoded(BackedEnum|Stringable|string|null $encoded): bool
    {
        static $pattern = '/[^'.self::REGEXP_PART_UNRESERVED.self::REGEXP_PART_SUBDELIM.':@\/?%]|'.self::REGEXP_PART_ENCODED.'/';

        if ($encoded instanceof BackedEnum) {
            $encoded = $encoded->value;
        }

        return null === $encoded || 1 !== preg_match($pattern, (string) $encoded);
    }

    /**
     * Decodes the fragment component while preserving characters that should not be decoded in the context of a full valid URI.
     */
    public static function decodeFragment(BackedEnum|Stringable|string|null $fragment): ?string
    {
        return self::decode($fragment, static function (array $matches): string {
            $encodedChar = strtoupper($matches[0]);
            $decodedChar = rawurldecode($encodedChar);

            return str_contains(self::RFC3986_RESERVED_CHARACTERS, $decodedChar)
                ? $encodedChar
                : $decodedChar;
        });
    }

    /**
     * Normalize the fragment component.
     *
     * The value returned MUST be percent-encoded, but MUST NOT double-encode
     * any characters. To determine what characters to encode, please refer to
     * RFC 3986.
     */
    public static function normalizeFragment(BackedEnum|Stringable|string|null $fragment): ?string
    {
        return self::normalize(self::encodeQueryOrFragment(self::decodeFragment($fragment)));
    }

    /**
     * Normalize the host component.
     *
     * @see https://www.rfc-editor.org/rfc/rfc3986.html#section-3.2.2
     *
     * The value returned MUST be percent-encoded, but MUST NOT double-encode
     * any characters. To determine what characters to encode, please refer to
     * RFC 3986.
     */
    public static function normalizeHost(BackedEnum|Stringable|string|null $host): ?string
    {
        if ($host instanceof BackedEnum) {
            $host = (string) $host->value;
        }

        if ($host instanceof Stringable) {
            $host = (string) $host;
        }

        if (null === $host || '' === $host || false !== filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
            return $host;
        }

        if (str_starts_with($host, '[')) {
            return IPv6Converter::normalize($host);
        }

        $host = strtolower($host);

        return (!str_contains($host, '%')) ? $host : preg_replace_callback(
            '/%[a-f0-9]{2}/',
            fn (array $matches) => 1 === preg_match('/%([0-7][0-9a-f])/', $matches[0]) ? rawurldecode($matches[0]) : strtoupper($matches[0]),
            $host
        );
    }

    /**
     * Encode Query or Fragment.
     *
     * Generic delimiters ":", "@", "?", and "/" MUST NOT be encoded
     */
    public static function encodeQueryOrFragment(BackedEnum|Stringable|string|null $component): ?string
    {
        static $pattern = '/[^'.self::REGEXP_PART_UNRESERVED.self::REGEXP_PART_SUBDELIM.':@\/?]+|'.self::REGEXP_PART_ENCODED.'/';

        return self::encode($component, $pattern);
    }

    public static function encodeQueryKeyValue(mixed $component): string
    {
        static $pattern = '/[^'.self::REGEXP_PART_UNRESERVED.']+|'.self::REGEXP_PART_ENCODED.'/';
        $encoder = static fn (array $found): string => 1 === preg_match('/[^'.self::REGEXP_PART_UNRESERVED.']/', rawurldecode($found[0])) ? rawurlencode($found[0]) : $found[0];
        $filteredComponent = self::filterComponent($component);

        return match (true) {
            null === $filteredComponent => throw new SyntaxError(sprintf('A pair key/value must be a scalar value `%s` given.', gettype($component))),
            1 === preg_match(self::REGEXP_CHARS_INVALID, $filteredComponent) => rawurlencode($filteredComponent),
            default => (string) preg_replace_callback($pattern, $encoder, $filteredComponent),
        };
    }

    private static function filterComponent(mixed $component): ?string
    {
        try {
            return StringCoercionMode::Native->coerce($component);
        } catch (Throwable $exception) {
            throw new SyntaxError(
                sprintf('The component must be a scalar value `%s` given.', gettype($component)),
                previous: $exception
            );
        }
    }

    /**
     * Encodes the URI component characters using a regular expression to find which characters need encoding.
     */
    private static function encode(BackedEnum|Stringable|string|int|bool|null $component, string $pattern): ?string
    {
        $component = self::filterComponent($component);
        if (null === $component || '' === $component) {
            return $component;
        }

        return (string) preg_replace_callback(
            $pattern,
            static fn (array $found): string => 1 === preg_match('/[^'.self::REGEXP_PART_UNRESERVED.']/', rawurldecode($found[0])) ? rawurlencode($found[0]) : $found[0],
            $component
        );
    }

    /**
     * Decodes the URI component characters using a closure.
     */
    private static function decode(BackedEnum|Stringable|string|int|null $component, Closure $decoder): ?string
    {
        $component = self::filterComponent($component);
        if (null === $component || '' === $component) {
            return $component;
        }

        1 !== preg_match(self::REGEXP_CHARS_INVALID, $component) || throw new SyntaxError('Invalid component string: '.$component.'.');

        return (string) preg_replace_callback(self::REGEXP_CHARS_ENCODED, $decoder, $component);
    }

    /**
     * Decodes the URI component without decoding the unreserved characters which are already encoded.
     *
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.6.0
     * @codeCoverageIgnore
     * @see Encoder::decodeNecessary()
     *
     * Create a new instance from the environment.
     */
    #[Deprecated(message:'use League\Uri\Encoder::decodeNecessary() instead', since:'league/uri:7.6.0')]
    public static function decodePartial(BackedEnum|Stringable|string|int|null $component): ?string
    {
        return self::decodeNecessary($component);
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri;

use BackedEnum;
use League\Uri\Exceptions\SyntaxError;
use League\Uri\KeyValuePair\Converter;
use ReflectionEnum;
use ReflectionException;
use SplObjectStorage;
use Stringable;
use TypeError;
use UnitEnum;
use ValueError;

use function array_is_list;
use function array_key_exists;
use function array_keys;
use function get_debug_type;
use function get_object_vars;
use function http_build_query;
use function implode;
use function is_array;
use function is_object;
use function is_resource;
use function is_scalar;
use function rawurldecode;
use function str_replace;
use function strpos;
use function substr;

use const PHP_QUERY_RFC1738;
use const PHP_QUERY_RFC3986;

/**
 * A class to parse the URI query string.
 *
 * @see https://tools.ietf.org/html/rfc3986#section-3.4
 */
final class QueryString
{
    private const PAIR_VALUE_DECODED = 1;
    private const PAIR_VALUE_PRESERVED = 2;
    private const RECURSION_MARKER = "\0__RECURSION_INTERNAL_MARKER__\0";

    /**
     * @codeCoverageIgnore
     */
    private function __construct()
    {
    }

    /**
     * Build a query string from a list of pairs.
     *
     * @see QueryString::buildFromPairs()
     * @see https://datatracker.ietf.org/doc/html/rfc3986#section-2.2
     *
     * @param iterable<array{0:string, 1:mixed}> $pairs
     * @param non-empty-string $separator
     *
     * @throws SyntaxError If the encoding type is invalid
     * @throws SyntaxError If a pair is invalid
     */
    public static function build(iterable $pairs, string $separator = '&', int $encType = PHP_QUERY_RFC3986, StringCoercionMode $coercionMode = StringCoercionMode::Native): ?string
    {
        return self::buildFromPairs($pairs, Converter::fromEncodingType($encType)->withSeparator($separator), $coercionMode);
    }

    /**
     * Build a query string from a list of pairs.
     *
     * The method expects the return value from Query::parse to build
     * a valid query string. This method differs from PHP http_build_query as
     * it does not modify parameters keys.
     *
     *  If a reserved character is found in a URI component and
     *  no delimiting role is known for that character, then it must be
     *  interpreted as representing the data octet corresponding to that
     *  character's encoding in US-ASCII.
     *
     * @see https://datatracker.ietf.org/doc/html/rfc3986#section-2.2
     *
     * @param iterable<array{0:string, 1:mixed}> $pairs
     *
     * @throws SyntaxError If the encoding type is invalid
     * @throws SyntaxError If a pair is invalid
     */
    public static function buildFromPairs(iterable $pairs, ?Converter $converter = null, StringCoercionMode $coercionMode = StringCoercionMode::Native): ?string
    {
        $keyValuePairs = [];
        foreach ($pairs as $pair) {
            if (!is_array($pair) || [0, 1] !== array_keys($pair)) {
                throw new SyntaxError('A pair must be a sequential array starting at `0` and containing two elements.');
            }

            [$key, $value] = $pair;
            $coercionMode->isCoercible($value) || throw new SyntaxError('Converting a type `'.get_debug_type($value).'` into a string is not supported by the '.(StringCoercionMode::Native === $coercionMode ? 'PHP Native' : 'Ecmascript').' coercion mode.');

            try {
                $key = $coercionMode->coerce($key);
                $value = $coercionMode->coerce($value);
            } catch (TypeError $typeError) {
                throw new SyntaxError('The pair can not be converted to build a query string.', previous: $typeError);
            }

            $keyValuePairs[] = [Encoder::encodeQueryKeyValue($key), null === $value ? null : Encoder::encodeQueryKeyValue($value)];
        }

        return ($converter ?? Converter::fromRFC3986())->toValue($keyValuePairs);
    }

    /**
     * Build a query string from an object or an array like http_build_query without discarding values.
     * The method differs from http_build_query for the following behavior:
     *
     *  - if a resource is used, a TypeError is thrown.
     *  - if a recursion is detected a ValueError is thrown
     *  - the method preserves value with `null` value (http_build_query) skip the key.
     *  - the method does not handle prefix usage
     *
     * @param array<array-key, mixed> $data
     * @param non-empty-string $separator
     *
     * @throws TypeError if a resource is found it the input array
     * @throws ValueError if a recursion is detected
     */
    public static function compose(
        array|object $data,
        string $separator = '&',
        int $encType = PHP_QUERY_RFC1738,
        QueryComposeMode $composeMode = QueryComposeMode::Native
    ): ?string {
        if (QueryComposeMode::Native === $composeMode) {
            return http_build_query(data: $data, arg_separator: $separator, encoding_type: $encType);
        }

        $query = self::composeFromValue($data, Converter::fromEncodingType($encType)->withSeparator($separator), $composeMode);

        return QueryComposeMode::Safe !== $composeMode ? (string) $query : $query;
    }

    public static function composeFromValue(
        array|object $data,
        ?Converter $converter = null,
        QueryComposeMode $composeMode = QueryComposeMode::Native,
    ): ?string {
        if (QueryComposeMode::EnumLenient === $composeMode && $data instanceof UnitEnum && !$data instanceof BackedEnum) {
            return '';
        }

        QueryComposeMode::Safe !== $composeMode || is_array($data) || throw new TypeError('In safe mode only arrays are supported.');

        $converter ??= Converter::fromRFC3986();

        $pairs = QueryComposeMode::Native !== $composeMode
            ? self::composeRecursive($composeMode, $data)
            : self::parseFromValue(http_build_query(data: $data, arg_separator: '&'), Converter::fromRFC1738());

        return self::buildFromPairs($pairs, $converter);
    }

    /**
     * @param array<array-key, mixed>|object $data
     * @param SplObjectStorage<object, null> $seenObjects
     *
     * @throws TypeError if a resource is found it the input array
     * @throws ValueError if a recursion is detected
     * @throws ReflectionException if reflection is not possible on the Enum
     *
     * @return iterable<array{0: array-key, 1: string|int|float|bool|null}>
     */
    private static function composeRecursive(
        QueryComposeMode $composeMode,
        array|object $data,
        string|int $prefix = '',
        SplObjectStorage $seenObjects = new SplObjectStorage(),
    ): iterable {
        QueryComposeMode::Safe !== $composeMode || is_array($data) || throw new TypeError('In safe mode only arrays are supported.');
        in_array($composeMode, [QueryComposeMode::EnumCompatible, QueryComposeMode::EnumLenient], true) || !$data instanceof UnitEnum || throw new TypeError('Argument #1 ($data) must not be an enum, '.((new ReflectionEnum($data::class))->isBacked() ? 'Backed' : 'Pure').' given') ;

        if (is_object($data)) {
            if ($seenObjects->contains($data)) {
                QueryComposeMode::Safe !== $composeMode || throw new ValueError('composition failed; circular reference detected.');

                return;
            }

            $seenObjects->attach($data);
            $data = get_object_vars($data);
        }

        if (self::hasCircularReference($data)) {
            QueryComposeMode::Safe !== $composeMode || throw new ValueError('composition failed; circular reference detected.');

            return;
        }

        $stripIndices = QueryComposeMode::Safe === $composeMode && array_is_list($data);

        foreach ($data as $name => $value) {
            $name = $stripIndices ? '' : $name;
            if ('' !== $prefix) {
                $name = $prefix.'['.$name.']';
            }

            if (is_resource($value)) {
                QueryComposeMode::Safe !== $composeMode || throw new TypeError('composition failed; a resource has been detected and can not be converted.');
                continue;
            }

            if (is_scalar($value)) {
                yield [$name, $value];

                continue;
            }

            if (null === $value) {
                if (QueryComposeMode::Safe === $composeMode) {
                    yield [$name, $value];
                }

                continue;
            }

            if ($value instanceof BackedEnum) {
                if (QueryComposeMode::Compatible !== $composeMode) {
                    yield [$name, $value->value];

                    continue;
                }

                $value = get_object_vars($value);
            }

            if ($value instanceof UnitEnum) {
                if (QueryComposeMode::EnumLenient === $composeMode) {
                    continue;
                }

                QueryComposeMode::Compatible === $composeMode || throw new TypeError('Unbacked enum '.$value::class.' cannot be converted to a string');

                $value = get_object_vars($value);
            }

            if (QueryComposeMode::Safe === $composeMode && is_object($value)) {
                throw new ValueError('In conservative mode only arrays, scalar value or null are supported.');
            }

            yield from self::composeRecursive($composeMode, $value, $name, $seenObjects);
        }
    }

    /**
     * Array recursion detection.
     * @see https://stackoverflow.com/questions/9042142/detecting-infinite-array-recursion-in-php
     */
    private static function hasCircularReference(array &$arr): bool
    {
        if (isset($arr[self::RECURSION_MARKER])) {
            return true;
        }

        try {
            $arr[self::RECURSION_MARKER] = true;
            foreach ($arr as $key => &$value) {
                if (self::RECURSION_MARKER !== $key && is_array($value) && self::hasCircularReference($value)) {
                    return true;
                }
            }

            return false;
        } finally {
            unset($arr[self::RECURSION_MARKER]);
        }
    }

    /**
     * Parses the query string.
     *
     * The result depends on the query parsing mode
     *
     * @see QueryString::extractFromValue()
     *
     * @param non-empty-string $separator
     *
     * @throws SyntaxError
     */
    public static function extract(
        BackedEnum|Stringable|string|bool|null $query,
        string $separator = '&',
        int $encType = PHP_QUERY_RFC3986,
        QueryExtractMode $extractMode = QueryExtractMode::Unmangled,
    ): array {
        return self::extractFromValue(
            $query,
            Converter::fromEncodingType($encType)->withSeparator($separator),
            $extractMode,
        );
    }

    /**
     * Parses the query string.
     *
     * The result depends on the query parsing mode
     *
     * @throws SyntaxError
     */
    public static function extractFromValue(
        BackedEnum|Stringable|string|bool|null $query,
        ?Converter $converter = null,
        QueryExtractMode $extractMode = QueryExtractMode::Unmangled,
    ): array {
        $pairs = ($converter ?? Converter::fromRFC3986())->toPairs($query);
        if (QueryExtractMode::Native === $extractMode) {
            if ([] === $pairs) {
                return [];
            }

            $data = [];
            foreach ($pairs as [$key, $value]) {
                $key = str_replace('&', '%26', (string) $key);
                $data[] = null === $value ? $key : $key.'='.str_replace('&', '%26', $value);
            }

            parse_str(implode('&', $data), $result);

            return $result;
        }

        return self::convert(
            self::decodePairs($pairs, self::PAIR_VALUE_PRESERVED),
            $extractMode
        );
    }

    /**
     * Parses a query string into a collection of key/value pairs.
     *
     * @param non-empty-string $separator
     *
     * @throws SyntaxError
     *
     * @return array<int, array{0:string, 1:string|null}>
     */
    public static function parse(BackedEnum|Stringable|string|bool|null $query, string $separator = '&', int $encType = PHP_QUERY_RFC3986): array
    {
        return self::parseFromValue($query, Converter::fromEncodingType($encType)->withSeparator($separator));
    }

    /**
     * Parses a query string into a collection of key/value pairs.
     *
     * @throws SyntaxError
     *
     * @return array<int, array{0:string, 1:string|null}>
     */
    public static function parseFromValue(BackedEnum|Stringable|string|bool|null $query, ?Converter $converter = null): array
    {
        return self::decodePairs(
            ($converter ?? Converter::fromRFC3986())->toPairs($query),
            self::PAIR_VALUE_DECODED
        );
    }

    /**
     * @param list<non-empty-array<string|null>> $pairs
     *
     * @return array<int, array{0:string, 1:string|null}>
     */
    private static function decodePairs(array $pairs, int $pairValueState): array
    {
        $decodePair = static function (array $pair, int $pairValueState): array {
            [$key, $value] = $pair;

            return match ($pairValueState) {
                self::PAIR_VALUE_PRESERVED => [(string) Encoder::decodeAll($key), $value],
                default => [(string) Encoder::decodeAll($key), Encoder::decodeAll($value)],
            };
        };

        return array_reduce(
            $pairs,
            fn (array $carry, array $pair) => [...$carry, $decodePair($pair, $pairValueState)],
            []
        );
    }

    /**
     * Converts a collection of key/value pairs and returns
     * the store PHP variables as elements of an array.
     */
    public static function convert(iterable $pairs, QueryExtractMode $extractMode = QueryExtractMode::Unmangled): array
    {
        $returnedValue = [];
        foreach ($pairs as $pair) {
            $returnedValue = self::extractPhpVariable($returnedValue, $pair, extractMode: $extractMode);
        }

        return $returnedValue;
    }

    /**
     * Parses a query pair like parse_str without mangling the results array keys.
     *
     * <ul>
     * <li>empty name are not saved</li>
     * <li>If the value from name is duplicated its corresponding value will be overwritten</li>
     * <li>if no "[" is detected the value is added to the return array with the name as index</li>
     * <li>if no "]" is detected after detecting a "[" the value is added to the return array with the name as index</li>
     * <li>if there's a mismatch in bracket usage the remaining part is dropped</li>
     * <li>â€œ.â€� and â€œ â€� are not converted to â€œ_â€�</li>
     * <li>If there is no â€œ]â€�, then the first â€œ[â€� is not converted to becomes an â€œ_â€�</li>
     * <li>no whitespace trimming is done on the key value</li>
     * </ul>
     *
     * @see https://php.net/parse_str
     * @see https://wiki.php.net/rfc/on_demand_name_mangling
     * @see https://github.com/php/php-src/blob/master/ext/standard/tests/strings/parse_str_basic1.phpt
     * @see https://github.com/php/php-src/blob/master/ext/standard/tests/strings/parse_str_basic2.phpt
     * @see https://github.com/php/php-src/blob/master/ext/standard/tests/strings/parse_str_basic3.phpt
     * @see https://github.com/php/php-src/blob/master/ext/standard/tests/strings/parse_str_basic4.phpt
     *
     * @param array $data the submitted array
     * @param array|string $name the pair key
     * @param string $value the pair value
     */
    private static function extractPhpVariable(
        array $data,
        array|string $name,
        ?string $value = '',
        QueryExtractMode $extractMode = QueryExtractMode::Unmangled
    ): array {
        if (is_array($name)) {
            [$name, $value] = $name;
            if (null !== $value || QueryExtractMode::LossLess !== $extractMode) {
                $value = rawurldecode((string) $value);
            }
        }

        if ('' === $name) {
            return $data;
        }

        $leftBracketPosition = strpos($name, '[');
        if (false === $leftBracketPosition) {
            $data[$name] = $value;

            return $data;
        }

        $rightBracketPosition = strpos($name, ']', $leftBracketPosition);
        if (false === $rightBracketPosition) {
            $data[$name] = $value;

            return $data;
        }

        $key = substr($name, 0, $leftBracketPosition);
        if ('' === $key) {
            $key = '0';
        }

        if (!array_key_exists($key, $data) || !is_array($data[$key])) {
            $data[$key] = [];
        }

        $remaining = substr($name, $rightBracketPosition + 1);
        if (!str_starts_with($remaining, '[') || !str_contains($remaining, ']')) {
            $remaining = '';
        }

        $name = substr($name, $leftBracketPosition + 1, $rightBracketPosition - $leftBracketPosition - 1).$remaining;
        if ('' === $name) {
            $data[$key][] = $value;

            return $data;
        }

        $data[$key] = self::extractPhpVariable($data[$key], $name, $value, $extractMode);

        return $data;
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace League\Uri\Idna;

enum Error: int
{
    case NONE                   = 0;
    case EMPTY_LABEL            = 1;
    case LABEL_TOO_LONG         = 2;
    case DOMAIN_NAME_TOO_LONG   = 4;
    case LEADING_HYPHEN         = 8;
    case TRAILING_HYPHEN        = 0x10;
    case HYPHEN_3_4             = 0x20;
    case LEADING_COMBINING_MARK = 0x40;
    case DISALLOWED             = 0x80;
    case PUNYCODE               = 0x100;
    case LABEL_HAS_DOT          = 0x200;
    case INVALID_ACE_LABEL      = 0x400;
    case BIDI                   = 0x800;
    case CONTEXTJ               = 0x1000;
    case CONTEXTO_PUNCTUATION   = 0x2000;
    case CONTEXTO_DIGITS        = 0x4000;

    public function description(): string
    {
        return match ($this) {
            self::NONE => 'No error has occurred',
            self::EMPTY_LABEL => 'a non-final domain name label (or the whole domain name) is empty',
            self::LABEL_TOO_LONG => 'a domain name label is longer than 63 bytes',
            self::DOMAIN_NAME_TOO_LONG => 'a domain name is longer than 255 bytes in its storage form',
            self::LEADING_HYPHEN => 'a label starts with a hyphen-minus ("-")',
            self::TRAILING_HYPHEN => 'a label ends with a hyphen-minus ("-")',
            self::HYPHEN_3_4 => 'a label contains hyphen-minus ("-") in the third and fourth positions',
            self::LEADING_COMBINING_MARK => 'a label starts with a combining mark',
            self::DISALLOWED => 'a label or domain name contains disallowed characters',
            self::PUNYCODE => 'a label starts with "xn--" but does not contain valid Punycode',
            self::LABEL_HAS_DOT => 'a label contains a dot=full stop',
            self::INVALID_ACE_LABEL => 'An ACE label does not contain a valid label string',
            self::BIDI => 'a label does not meet the IDNA BiDi requirements (for right-to-left characters)',
            self::CONTEXTJ => 'a label does not meet the IDNA CONTEXTJ requirements',
            self::CONTEXTO_DIGITS => 'a label does not meet the IDNA CONTEXTO requirements for digits',
            self::CONTEXTO_PUNCTUATION => 'a label does not meet the IDNA CONTEXTO requirements for punctuation characters. Some punctuation characters "Would otherwise have been DISALLOWED" but are allowed in certain contexts',
        };
    }

    public static function filterByErrorBytes(int $errors): array
    {
        return array_values(
            array_filter(
                self::cases(),
                fn (self $error): bool => 0 !== ($error->value & $errors)
            )
        );
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\Idna;

/**
 * @see https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/uidna_8h.html
 */
final class Result
{
    private function __construct(
        private readonly string $domain,
        private readonly bool $isTransitionalDifferent,
        /** @var array<Error> */
        private readonly array $errors
    ) {
    }

    /**
     * @param array{result:string, isTransitionalDifferent:bool, errors:int} $infos
     */
    public static function fromIntl(array $infos): self
    {
        return new self($infos['result'], $infos['isTransitionalDifferent'], Error::filterByErrorBytes($infos['errors']));
    }

    public function domain(): string
    {
        return $this->domain;
    }

    public function isTransitionalDifferent(): bool
    {
        return $this->isTransitionalDifferent;
    }

    /**
     * @return array<Error>
     */
    public function errors(): array
    {
        return $this->errors;
    }

    public function hasErrors(): bool
    {
        return [] !== $this->errors;
    }

    public function hasError(Error $error): bool
    {
        return in_array($error, $this->errors, true);
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\Idna;

use ReflectionClass;
use ReflectionClassConstant;

/**
 * @see https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/uidna_8h.html
 */
final class Option
{
    private const DEFAULT                    = 0;
    private const ALLOW_UNASSIGNED           = 1;
    private const USE_STD3_RULES             = 2;
    private const CHECK_BIDI                 = 4;
    private const CHECK_CONTEXTJ             = 8;
    private const NONTRANSITIONAL_TO_ASCII   = 0x10;
    private const NONTRANSITIONAL_TO_UNICODE = 0x20;
    private const CHECK_CONTEXTO             = 0x40;

    private function __construct(private readonly int $value)
    {
    }

    private static function cases(): array
    {
        static $assoc;
        if (null === $assoc) {
            $assoc = [];
            $fooClass = new ReflectionClass(self::class);
            foreach ($fooClass->getConstants(ReflectionClassConstant::IS_PRIVATE) as $name => $value) {
                $assoc[$name] = $value;
            }
        }

        return $assoc;
    }

    public static function new(int $bytes = self::DEFAULT): self
    {
        return new self(array_reduce(
            self::cases(),
            fn (int $value, int $option) => 0 !== ($option & $bytes) ? ($value | $option) : $value,
            self::DEFAULT
        ));
    }

    public static function forIDNA2008Ascii(): self
    {
        return self::new()
            ->nonTransitionalToAscii()
            ->checkBidi()
            ->useSTD3Rules()
            ->checkContextJ();
    }

    public static function forIDNA2008Unicode(): self
    {
        return self::new()
            ->nonTransitionalToUnicode()
            ->checkBidi()
            ->useSTD3Rules()
            ->checkContextJ();
    }

    public function toBytes(): int
    {
        return $this->value;
    }

    /** array<string, int> */
    public function list(): array
    {
        return array_keys(array_filter(
            self::cases(),
            fn (int $value) => 0 !== ($value & $this->value)
        ));
    }

    public function allowUnassigned(): self
    {
        return $this->add(self::ALLOW_UNASSIGNED);
    }

    public function disallowUnassigned(): self
    {
        return $this->remove(self::ALLOW_UNASSIGNED);
    }

    public function useSTD3Rules(): self
    {
        return $this->add(self::USE_STD3_RULES);
    }

    public function prohibitSTD3Rules(): self
    {
        return $this->remove(self::USE_STD3_RULES);
    }

    public function checkBidi(): self
    {
        return $this->add(self::CHECK_BIDI);
    }

    public function ignoreBidi(): self
    {
        return $this->remove(self::CHECK_BIDI);
    }

    public function checkContextJ(): self
    {
        return $this->add(self::CHECK_CONTEXTJ);
    }

    public function ignoreContextJ(): self
    {
        return $this->remove(self::CHECK_CONTEXTJ);
    }

    public function checkContextO(): self
    {
        return $this->add(self::CHECK_CONTEXTO);
    }

    public function ignoreContextO(): self
    {
        return $this->remove(self::CHECK_CONTEXTO);
    }

    public function nonTransitionalToAscii(): self
    {
        return $this->add(self::NONTRANSITIONAL_TO_ASCII);
    }

    public function transitionalToAscii(): self
    {
        return $this->remove(self::NONTRANSITIONAL_TO_ASCII);
    }

    public function nonTransitionalToUnicode(): self
    {
        return $this->add(self::NONTRANSITIONAL_TO_UNICODE);
    }

    public function transitionalToUnicode(): self
    {
        return $this->remove(self::NONTRANSITIONAL_TO_UNICODE);
    }

    public function add(Option|int|null $option = null): self
    {
        return match (true) {
            null === $option => $this,
            $option instanceof self => self::new($this->value | $option->value),
            default => self::new($this->value | $option),
        };
    }

    public function remove(Option|int|null $option = null): self
    {
        return match (true) {
            null === $option => $this,
            $option instanceof self => self::new($this->value & ~$option->value),
            default => self::new($this->value & ~$option),
        };
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\Idna;

use BackedEnum;
use League\Uri\Exceptions\ConversionFailed;
use League\Uri\Exceptions\SyntaxError;
use League\Uri\FeatureDetection;
use Stringable;

use function idn_to_ascii;
use function idn_to_utf8;
use function rawurldecode;
use function strtolower;

use const INTL_IDNA_VARIANT_UTS46;

/**
 * @see https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/uidna_8h.html
 */
final class Converter
{
    private const REGEXP_IDNA_PATTERN = '/[^\x20-\x7f]/';
    private const MAX_DOMAIN_LENGTH = 253;
    private const MAX_LABEL_LENGTH = 63;

    /**
     * General registered name regular expression.
     *
     * @see https://tools.ietf.org/html/rfc3986#section-3.2.2
     * @see https://regex101.com/r/fptU8V/1
     */
    private const REGEXP_REGISTERED_NAME = '/
        (?(DEFINE)
            (?<unreserved>[a-z0-9_~\-])   # . is missing as it is used to separate labels
            (?<sub_delims>[!$&\'()*+,;=])
            (?<encoded>%[A-F0-9]{2})
            (?<reg_name>(?:(?&unreserved)|(?&sub_delims)|(?&encoded))*)
        )
            ^(?:(?&reg_name)\.)*(?&reg_name)\.?$
        /ix';

    /**
     * Converts the input to its IDNA ASCII form or throw on failure.
     *
     * @see Converter::toAscii()
     *
     * @throws SyntaxError if the string cannot be converted to UNICODE using IDN UTS46 algorithm
     * @throws ConversionFailed if the conversion returns error
     */
    public static function toAsciiOrFail(BackedEnum|Stringable|string $domain, Option|int|null $options = null): string
    {
        $result = self::toAscii($domain, $options);

        return match (true) {
            $result->hasErrors() => throw ConversionFailed::dueToIdnError($domain, $result),
            default => $result->domain(),
        };
    }

    /**
     * Converts the input to its IDNA ASCII form.
     *
     * This method returns the string converted to IDN ASCII form
     *
     * @throws SyntaxError if the string cannot be converted to ASCII using IDN UTS46 algorithm
     */
    public static function toAscii(BackedEnum|Stringable|string $domain, Option|int|null $options = null): Result
    {
        if ($domain instanceof BackedEnum) {
            $domain = $domain->value;
        }

        $domain = rawurldecode((string) $domain);

        if (1 === preg_match(self::REGEXP_IDNA_PATTERN, $domain)) {
            FeatureDetection::supportsIdn();

            $flags = match (true) {
                null === $options => Option::forIDNA2008Ascii(),
                $options instanceof Option => $options,
                default => Option::new($options),
            };

            idn_to_ascii($domain, $flags->toBytes(), INTL_IDNA_VARIANT_UTS46, $idnaInfo);

            if ([] === $idnaInfo) {
                return Result::fromIntl([
                    'result' => strtolower($domain),
                    'isTransitionalDifferent' => false,
                    'errors' => self::validateDomainAndLabelLength($domain),
                ]);
            }

            return Result::fromIntl($idnaInfo);
        }

        $error = Error::NONE->value;
        if (1 !== preg_match(self::REGEXP_REGISTERED_NAME, $domain)) {
            $error |= Error::DISALLOWED->value;
        }

        return Result::fromIntl([
            'result' => strtolower($domain),
            'isTransitionalDifferent' => false,
            'errors' => self::validateDomainAndLabelLength($domain) | $error,
        ]);
    }

    /**
     * Converts the input to its IDNA UNICODE form or throw on failure.
     *
     * @see Converter::toUnicode()
     *
     * @throws ConversionFailed if the conversion returns error
     */
    public static function toUnicodeOrFail(BackedEnum|Stringable|string $domain, Option|int|null $options = null): string
    {
        $result = self::toUnicode($domain, $options);

        return match (true) {
            $result->hasErrors() => throw ConversionFailed::dueToIdnError($domain, $result),
            default => $result->domain(),
        };
    }

    /**
     * Converts the input to its IDNA UNICODE form.
     *
     * This method returns the string converted to IDN UNICODE form
     *
     * @throws SyntaxError if the string cannot be converted to UNICODE using IDN UTS46 algorithm
     */
    public static function toUnicode(BackedEnum|Stringable|string $domain, Option|int|null $options = null): Result
    {
        if ($domain instanceof BackedEnum) {
            $domain = $domain->value;
        }

        $domain = rawurldecode((string) $domain);
        if (false === stripos($domain, 'xn--')) {
            return Result::fromIntl(['result' => strtolower($domain), 'isTransitionalDifferent' => false, 'errors' => Error::NONE->value]);
        }

        FeatureDetection::supportsIdn();

        $flags = match (true) {
            null === $options => Option::forIDNA2008Unicode(),
            $options instanceof Option => $options,
            default => Option::new($options),
        };

        idn_to_utf8($domain, $flags->toBytes(), INTL_IDNA_VARIANT_UTS46, $idnaInfo);

        if ([] === $idnaInfo) {
            return Result::fromIntl(['result' => strtolower($domain), 'isTransitionalDifferent' => false, 'errors' => Error::NONE->value]);
        }

        return Result::fromIntl($idnaInfo);
    }

    /**
     * Tells whether the submitted host is a valid IDN regardless of its format.
     *
     * Returns false if the host is invalid or if its conversion yields the same result
     */
    public static function isIdn(BackedEnum|Stringable|string|null $domain): bool
    {
        if ($domain instanceof BackedEnum) {
            $domain = $domain->value;
        }

        $domain = strtolower(rawurldecode((string) $domain));
        $result = match (1) {
            preg_match(self::REGEXP_IDNA_PATTERN, $domain) => self::toAscii($domain),
            default => self::toUnicode($domain),
        };

        return match (true) {
            $result->hasErrors() => false,
            default => $result->domain() !== $domain,
        };
    }

    /**
     * Adapted from https://github.com/TRowbotham/idna.
     *
     * @see https://github.com/TRowbotham/idna/blob/master/src/Idna.php#L236
     */
    private static function validateDomainAndLabelLength(string $domain): int
    {
        $error = Error::NONE->value;
        $labels = explode('.', $domain);
        $maxDomainSize = self::MAX_DOMAIN_LENGTH;
        $length = count($labels);

        // If the last label is empty, and it is not the first label, then it is the root label.
        // Increase the max size by 1, making it 254, to account for the root label's "."
        // delimiter. This also means we don't need to check the last label's length for being too
        // long.
        if ($length > 1 && '' === $labels[$length - 1]) {
            ++$maxDomainSize;
            array_pop($labels);
        }

        if (strlen($domain) > $maxDomainSize) {
            $error |= Error::DOMAIN_NAME_TOO_LONG->value;
        }

        foreach ($labels as $label) {
            if (strlen($label) > self::MAX_LABEL_LENGTH) {
                $error |= Error::LABEL_TOO_LONG->value;

                break;
            }
        }

        return $error;
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\Contracts;

use BackedEnum;
use Countable;
use IteratorAggregate;
use League\Uri\Exceptions\SyntaxError;
use Stringable;
use Traversable;

/**
 * @extends IteratorAggregate<int, string>
 *
 * @method bool isSubdomainOf(BackedEnum|Stringable|string|null $parentHost) Tells whether the current domain instance is a subdomain of the parent host.
 * @method bool hasSubdomain(BackedEnum|Stringable|string|null $childHost) Tells whether the submitted host is a subdomain of the current instance.
 * @method bool isSiblingOf(BackedEnum|Stringable|string|null $siblingHost) Tells whether the submitted host share the same parent domain as the current instance.
 * @method static commonAncestorWith(BackedEnum|Stringable|string|null $other) Returns the common longest ancestor between 2 domain. The returned domain is empty if no ancestor is found
 * @method static parentHost() Returns the current parent domain for the current instance. The returned domain is empty if no ancestor is found
 * @method bool isEmpty() Tells whether the domain contains any label.
 */
interface DomainHostInterface extends Countable, HostInterface, IteratorAggregate
{
    /**
     * Returns the labels total number.
     */
    public function count(): int;

    /**
     * Iterate over the Domain labels.
     *
     * @return Traversable<int, string>
     */
    public function getIterator(): Traversable;

    /**
     * Retrieves a single host label.
     *
     * If the label offset has not been set, returns the null value.
     */
    public function get(int $offset): ?string;

    /**
     * Returns the associated key for a specific label or all the keys.
     *
     * @return int[]
     */
    public function keys(?string $label = null): array;

    /**
     * Tells whether the domain is absolute.
     */
    public function isAbsolute(): bool;

    /**
     * Prepends a label to the host.
     */
    public function prepend(Stringable|string $label): self;

    /**
     * Appends a label to the host.
     */
    public function append(Stringable|string $label): self;

    /**
     * Extracts a slice of $length elements starting at position $offset from the host.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the selected slice.
     *
     * If $length is null it returns all elements from $offset to the end of the Domain.
     */
    public function slice(int $offset, ?int $length = null): self;

    /**
     * Returns an instance with its Root label.
     *
     * @see https://tools.ietf.org/html/rfc3986#section-3.2.2
     */
    public function withRootLabel(): self;

    /**
     * Returns an instance without its Root label.
     *
     * @see https://tools.ietf.org/html/rfc3986#section-3.2.2
     */
    public function withoutRootLabel(): self;

    /**
     * Returns an instance with the modified label.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the new label
     *
     * If $key is non-negative, the added label will be the label at $key position from the start.
     * If $key is negative, the added label will be the label at $key position from the end.
     *
     * @throws SyntaxError If the key is invalid
     */
    public function withLabel(int $key, Stringable|string $label): self;

    /**
     * Returns an instance without the specified label.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the modified component
     *
     * If $key is non-negative, the removed label will be the label at $key position from the start.
     * If $key is negative, the removed label will be the label at $key position from the end.
     *
     * @throws SyntaxError If the key is invalid
     */
    public function withoutLabel(int ...$keys): self;
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\Contracts;

use League\Uri\Exceptions\SyntaxError;

/**
 * @method static normalize() returns the normalized string representation of the component
 */
interface PathInterface extends UriComponentInterface
{
    /**
     * Returns the decoded path.
     */
    public function decoded(): string;

    /**
     * Tells whether the path is absolute or relative.
     */
    public function isAbsolute(): bool;

    /**
     * Tells whether the path has a trailing slash.
     */
    public function hasTrailingSlash(): bool;

    /**
     * Returns an instance without dot segments.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the path component normalized by removing
     * the dot segment.
     *
     * @throws SyntaxError for invalid component or transformations
     *                     that would result in a object in invalid state.
     */
    public function withoutDotSegments(): self;

    /**
     * Returns an instance with a leading slash.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the path component with a leading slash
     *
     * @throws SyntaxError for invalid component or transformations
     *                     that would result in a object in invalid state.
     */
    public function withLeadingSlash(): self;

    /**
     * Returns an instance without a leading slash.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the path component without a leading slash
     *
     * @throws SyntaxError for invalid component or transformations
     *                     that would result in a object in invalid state.
     */
    public function withoutLeadingSlash(): self;

    /**
     * Returns an instance with a trailing slash.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the path component with a trailing slash
     *
     * @throws SyntaxError for invalid component or transformations
     *                     that would result in a object in invalid state.
     */
    public function withTrailingSlash(): self;

    /**
     * Returns an instance without a trailing slash.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the path component without a trailing slash
     *
     * @throws SyntaxError for invalid component or transformations
     *                     that would result in a object in invalid state.
     */
    public function withoutTrailingSlash(): self;
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\Contracts;

use Stringable;

interface UserInfoInterface extends UriComponentInterface
{
    /**
     * Returns the user component part.
     */
    public function getUser(): ?string;

    /**
     * Returns the pass component part.
     */
    public function getPass(): ?string;

    /**
     * Returns an associative array containing all the User Info components.
     *
     * The returned a hashmap similar to PHP's parse_url return value
     *
     * @link https://tools.ietf.org/html/rfc3986
     *
     * @return array{user: ?string, pass : ?string}
     */
    public function components(): array;

    /**
     * Returns an instance with the specified user and/or pass.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the specified new username
     * otherwise it returns the same instance unchanged.
     *
     * A variable equal to null is equivalent to removing the complete user information.
     */
    public function withUser(Stringable|string|null $username): self;

    /**
     * Returns an instance with the specified user and/or pass.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the specified password if the user is specified
     * otherwise it returns the same instance unchanged.
     *
     * An empty user is equivalent to removing the user information.
     */
    public function withPass(Stringable|string|null $password): self;
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\Contracts;

use Stringable;

/**
 * @see https://wicg.github.io/scroll-to-text-fragment/#the-fragment-directive
 *
 * @method string toFragmentValue() returns the encoded string representation of the directive as a fragment string
 */
interface FragmentDirective extends Stringable
{
    /**
     * The decoded Directive name.
     *
     * @return non-empty-string
     */
    public function name(): string;

    /**
     * The decoded Directive value.
     */
    public function value(): ?string;

    /**
     * The encoded string representation of the directive.
     */
    public function toString(): string;

    /**
     * The encoded string representation of the fragment using
     * the Stringable interface.
     *
     * @see FragmentDirective::toString()
     */
    public function __toString(): string;

    /**
     * Tells whether the submitted value is equals to the string
     * representation of the given directive.
     */
    public function equals(mixed $directive): bool;
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\Contracts;

interface Conditionable
{
    /**
     * Apply the callback if the given "condition" is (or resolves to) true.
     *
     * @param (callable(static): bool)|bool $condition
     * @param callable(static): (static|null) $onSuccess
     * @param ?callable(static): (static|null) $onFail
     */
    public function when(callable|bool $condition, callable $onSuccess, ?callable $onFail = null): static;
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\Contracts;

interface IpHostInterface extends HostInterface
{
    /**
     * Tells whether the host is an IPv4 address.
     */
    public function isIpv4(): bool;

    /**
     * Tells whether the host is an IPv6 address.
     */
    public function isIpv6(): bool;

    /**
     * Tells whether the host is an IPv6 address.
     */
    public function isIpFuture(): bool;

    /**
     * Tells whether the host has a ZoneIdentifier.
     *
     * @see http://tools.ietf.org/html/rfc6874#section-4
     */
    public function hasZoneIdentifier(): bool;

    /**
     * Returns a host without its zone identifier according to RFC6874.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance without the host zone identifier according to RFC6874
     *
     * @see http://tools.ietf.org/html/rfc6874#section-4
     */
    public function withoutZoneIdentifier(): self;
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\Contracts;

use BackedEnum;
use Countable;
use Deprecated;
use Iterator;
use IteratorAggregate;
use League\Uri\QueryComposeMode;
use League\Uri\StringCoercionMode;
use Stringable;

/**
 * @extends IteratorAggregate<array{0:string, 1:string|null}>
 *
 * @method string|null toFormData() Returns the string representation using the application/www-form-urlencoded rules
 * @method string|null toRFC3986() Returns the string representation using RFC3986 rules
 * @method string|null first(string $key) Returns the first value associated with the given name
 * @method string|null last(string $key) Returns the first value associated with the given name
 * @method int|null indexOf(string $key, int $nth = 0) Returns the offset of the pair based on its key and its nth occurrence; negative occurrences are supported
 * @method int|null indexOfValue(?string $value, int $nth = 0) Returns the offset of the pair based on its value and its nth occurrence; negative occurrences are supported
 * @method array pair(int $offset) Returns the key/value pair at the given numeric offset; negative occurrences are supported
 * @method int countDistinctKeys() Returns the total number of distinct keys
 * @method string|null valueAt(int $offset): Returns the value at the given numeric offset; negative occurrences are supported
 * @method string keyAt(int $offset): Returns the key at the given numeric offset; negative occurrences are supported
 * @method self normalize() returns the normalized string representation of the component
 * @method self withoutPairByKey(string ...$keys) Returns an instance without pairs with the specified keys.
 * @method self withoutPairByValue(array|BackedEnum|Stringable|string|int|bool|null $values, StringCoercionMode $coercionMode = StringCoercionMode::Native) Returns an instance without pairs with the specified values.
 * @method self withoutPairByKeyValue(string $key, BackedEnum|Stringable|string|int|bool|null $value, StringCoercionMode $coercionMode = StringCoercionMode::Native) Returns an instance without pairs with the specified key/value pair
 * @method bool hasPair(string $key, ?string $value) Tells whether the pair exists in the query.
 * @method array getList(string $name) Returns the list associated with the given name or an empty array if it does not exist.
 * @method bool hasList(string ...$names) Tells whether the parameter list exists in the query.
 * @method self appendList(string $name, array $values, QueryComposeMode $composeMode = QueryComposeMode::Native) Appends a parameter to the query string
 * @method self withList(string $name, array $values, QueryComposeMode $composeMode = QueryComposeMode::Native) Adds a new parameter to the query string and remove any previously set values
 * @method self withoutList(string ...$names) Removes any given list associated with the given names
 * @method self withoutLists() Removes all lists from the query string
 * @method self onlyLists() Removes all pairs without a valid PHP's bracket notation
 */
interface QueryInterface extends Countable, IteratorAggregate, UriComponentInterface
{
    /**
     * Returns the query separator.
     *
     * @return non-empty-string
     */
    public function getSeparator(): string;

    /**
     * Returns the number of key/value pairs present in the object.
     */
    public function count(): int;

    /**
     * Returns an iterator allowing to go through all key/value pairs contained in this object.
     *
     * The pair is represented as an array where the first value is the pair key
     * and the second value the pair value.
     *
     * The key of each pair is a string
     * The value of each pair is a scalar or the null value
     *
     * @return Iterator<int, array{0:string, 1:string|null}>
     */
    public function getIterator(): Iterator;

    /**
     * Returns an iterator allowing to go through all key/value pairs contained in this object.
     *
     * The return type is as an Iterator where its offset is the pair key and its value the pair value.
     *
     * The key of each pair is a string
     * The value of each pair is a scalar or the null value
     *
     * @return iterable<string, string|null>
     */
    public function pairs(): iterable;

    /**
     * Tells whether a list of pair with a specific key exists.
     *
     * @see https://url.spec.whatwg.org/#dom-urlsearchparams-has
     */
    public function has(string ...$keys): bool;

    /**
     * Returns the first value associated to the given pair name.
     *
     * If no value is found null is returned
     *
     * @see https://url.spec.whatwg.org/#dom-urlsearchparams-get
     */
    public function get(string $key): ?string;

    /**
     * Returns all the values associated to the given pair name as an array or all
     * the instance pairs.
     *
     * If no value is found an empty array is returned
     *
     * @see https://url.spec.whatwg.org/#dom-urlsearchparams-getall
     *
     * @return array<int, string|null>
     */
    public function getAll(string $key): array;

    /**
     * Returns the store PHP variables as elements of an array.
     *
     * The result is similar as PHP parse_str when used with its
     * second argument with the difference that variable names are
     * not mangled.
     *
     * @see http://php.net/parse_str
     * @see https://wiki.php.net/rfc/on_demand_name_mangling
     *
     * @return array the collection of stored PHP variables or the empty array if no input is given,
     */
    public function parameters(): array;

    /**
     * Returns the value attached to the specific key.
     *
     * The result is similar to PHP parse_str with the difference that variable
     * names are not mangled.
     *
     * If a key is submitted it will return the value attached to it or null
     *
     * @see http://php.net/parse_str
     * @see https://wiki.php.net/rfc/on_demand_name_mangling
     *
     * @return mixed the collection of stored PHP variables or the empty array if no input is given,
     *               the single value of a stored PHP variable or null if the variable is not present in the collection
     */
    public function parameter(string $name): mixed;

    /**
     * Tells whether a list of variable with specific names exists.
     *
     * @see https://url.spec.whatwg.org/#dom-urlsearchparams-has
     */
    public function hasParameter(string ...$names): bool;

    /**
     * Returns the RFC1738 encoded query.
     */
    public function toRFC1738(): ?string;

    /**
     * Returns an instance with a different separator.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the query component with a different separator
     */
    public function withSeparator(string $separator): self;

    /**
     * Returns an instance with the new pairs set to it.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the modified query
     *
     * @see ::withPair
     */
    public function merge(Stringable|string $query): self;

    /**
     * Returns an instance with the new pairs appended to it.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the modified query
     *
     * If the pair already exists the value will be added to it.
     */
    public function append(Stringable|string $query): self;

    /**
     * Returns a new instance with a specified key/value pair appended as a new pair.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the modified query
     */
    public function appendTo(string $key, Stringable|string|int|bool|null $value): self;

    /**
     * Sorts the query string by offset, maintaining offset to data correlations.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the modified query
     *
     * @see https://url.spec.whatwg.org/#dom-urlsearchparams-sort
     */
    public function sort(): self;

    /**
     * Returns an instance without duplicate key/value pair.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the query component normalized by removing
     * duplicate pairs whose key/value are the same.
     */
    public function withoutDuplicates(): self;

    /**
     * Returns an instance without empty key/value where the value is the null value.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the query component normalized by removing
     * empty pairs.
     *
     * A pair is considered empty if its value is equal to the null value
     */
    public function withoutEmptyPairs(): self;

    /**
     * Returns an instance where numeric indices associated to PHP's array like key are removed.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the query component normalized so that numeric indexes
     * are removed from the pair key value.
     *
     * i.e.: toto[3]=bar[3]&foo=bar becomes toto[]=bar[3]&foo=bar
     */
    public function withoutNumericIndices(): self;

    /**
     * Returns an instance with a new key/value pair added to it.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the modified query
     *
     * If the pair already exists the value will replace the existing value.
     *
     * @see https://url.spec.whatwg.org/#dom-urlsearchparams-set
     */
    public function withPair(string $key, Stringable|string|int|float|bool|null $value): self;

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.3.0
     * @codeCoverageIgnore
     * @see QueryInterface::withoutPairByKey()
     *
     * Returns an instance without the specified keys.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the modified component
     */
    #[Deprecated(message:'use League\Uri\Contracts\QueryInterface::withoutPairByKey() instead', since:'league/uri-interfaces:7.3.0')]
    public function withoutPair(string ...$keys): self;

    /**
     * Returns an instance without the specified params.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the modified component without PHP's value.
     * PHP's mangled is not taken into account.
     */
    public function withoutParameters(string ...$names): self;
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\Contracts;

use JsonSerializable;
use League\Uri\Exceptions\MissingFeature;
use League\Uri\Exceptions\SyntaxError;
use League\Uri\UriString;
use Stringable;

/**
 * @phpstan-import-type ComponentMap from UriString
 *
 * @method string|null getUsername() returns the user component of the URI.
 * @method self withUsername(?string $user) returns a new URI instance with the user component updated.
 * @method string|null getPassword() returns the scheme-specific information about how to gain authorization to access the resource.
 * @method self withPassword(?string $password) returns a new URI instance with the password component updated.
 * @method string toAsciiString() returns the string representation of the URI in its RFC3986 form
 * @method string toUnicodeString() returns the string representation of the URI in its RFC3987 form (the host is in its IDN form)
 * @method array toComponents() returns an associative array containing all the URI components.
 * @method self normalize() returns a new URI instance with normalized components
 * @method self resolve(UriInterface $uri) resolves a URI against a base URI using RFC3986 rules
 * @method self relativize(UriInterface $uri) relativize a URI against a base URI using RFC3986 rules
 */
interface UriInterface extends JsonSerializable, Stringable
{
    /**
     * Returns the string representation as a URI reference.
     *
     * @see http://tools.ietf.org/html/rfc3986#section-4.1
     */
    public function __toString(): string;

    /**
     * Returns the string representation as a URI reference.
     *
     * @see http://tools.ietf.org/html/rfc3986#section-4.1
     */
    public function toString(): string;

    /**
     * Returns the string representation as a URI reference.
     *
     * @see http://tools.ietf.org/html/rfc3986#section-4.1
     * @see ::__toString
     */
    public function jsonSerialize(): string;

    /**
     * Retrieve the scheme component of the URI.
     *
     * If no scheme is present, this method MUST return a null value.
     *
     * The value returned MUST be normalized to lowercase, per RFC 3986
     * Section 3.1.
     *
     * The trailing ":" character is not part of the scheme and MUST NOT be
     * added.
     *
     * @see https://tools.ietf.org/html/rfc3986#section-3.1
     */
    public function getScheme(): ?string;

    /**
     * Retrieve the authority component of the URI.
     *
     * If no scheme is present, this method MUST return a null value.
     *
     * If the port component is not set or is the standard port for the current
     * scheme, it SHOULD NOT be included.
     *
     * @see https://tools.ietf.org/html/rfc3986#section-3.2
     */
    public function getAuthority(): ?string;

    /**
     * Retrieve the user information component of the URI.
     *
     * If no scheme is present, this method MUST return a null value.
     *
     * If a user is present in the URI, this will return that value;
     * additionally, if the password is also present, it will be appended to the
     * user value, with a colon (":") separating the values.
     *
     * The trailing "@" character is not part of the user information and MUST
     * NOT be added.
     */
    public function getUserInfo(): ?string;

    /**
     * Retrieve the host component of the URI.
     *
     * If no host is present this method MUST return a null value.
     *
     * The value returned MUST be normalized to lowercase, per RFC 3986
     * Section 3.2.2.
     *
     * @see http://tools.ietf.org/html/rfc3986#section-3.2.2
     */
    public function getHost(): ?string;

    /**
     * Retrieve the port component of the URI.
     *
     * If a port is present, and it is non-standard for the current scheme,
     * this method MUST return it as an integer. If the port is the standard port
     * used with the current scheme, this method SHOULD return null.
     *
     * If no port is present, and no scheme is present, this method MUST return
     * a null value.
     *
     * If no port is present, but a scheme is present, this method MAY return
     * the standard port for that scheme, but SHOULD return null.
     */
    public function getPort(): ?int;

    /**
     * Retrieve the path component of the URI.
     *
     * The path can either be empty or absolute (starting with a slash) or
     * rootless (not starting with a slash). Implementations MUST support all
     * three syntaxes.
     *
     * Normally, the empty path "" and absolute path "/" are considered equal as
     * defined in RFC 7230 Section 2.7.3. But this method MUST NOT automatically
     * do this normalization because in contexts with a trimmed base path, e.g.
     * the front controller, this difference becomes significant. It's the task
     * of the user to handle both "" and "/".
     *
     * The value returned MUST be percent-encoded, but MUST NOT double-encode
     * any characters. To determine what characters to encode, please refer to
     * RFC 3986, Sections 2 and 3.3.
     *
     * As an example, if the value should include a slash ("/") not intended as
     * delimiter between path segments, that value MUST be passed in encoded
     * form (e.g., "%2F") to the instance.
     *
     * @see https://tools.ietf.org/html/rfc3986#section-2
     * @see https://tools.ietf.org/html/rfc3986#section-3.3
     */
    public function getPath(): string;

    /**
     * Retrieve the query string of the URI.
     *
     * If no host is present this method MUST return a null value.
     *
     * The leading "?" character is not part of the query and MUST NOT be
     * added.
     *
     * The value returned MUST be percent-encoded, but MUST NOT double-encode
     * any characters. To determine what characters to encode, please refer to
     * RFC 3986, Sections 2 and 3.4.
     *
     * As an example, if a value in a key/value pair of the query string should
     * include an ampersand ("&") not intended as a delimiter between values,
     * that value MUST be passed in encoded form (e.g., "%26") to the instance.
     *
     * @see https://tools.ietf.org/html/rfc3986#section-2
     * @see https://tools.ietf.org/html/rfc3986#section-3.4
     */
    public function getQuery(): ?string;

    /**
     * Retrieve the fragment component of the URI.
     *
     * If no host is present this method MUST return a null value.
     *
     * The leading "#" character is not part of the fragment and MUST NOT be
     * added.
     *
     * The value returned MUST be percent-encoded, but MUST NOT double-encode
     * any characters. To determine what characters to encode, please refer to
     * RFC 3986, Sections 2 and 3.5.
     *
     * @see https://tools.ietf.org/html/rfc3986#section-2
     * @see https://tools.ietf.org/html/rfc3986#section-3.5
     */
    public function getFragment(): ?string;

    /**
     * Returns an associative array containing all the URI components.
     *
     * The returned array is similar to PHP's parse_url return value with the following
     * differences:
     *
     * <ul>
     * <li>All components are present in the returned array</li>
     * <li>Empty and undefined component are treated differently. And empty component is
     *   set to the empty string while an undefined component is set to the `null` value.</li>
     * </ul>
     *
     * @link https://tools.ietf.org/html/rfc3986
     *
     * @return ComponentMap
     */
    public function getComponents(): array;

    /**
     * Return an instance with the specified scheme.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the specified scheme.
     *
     * A null value provided for the scheme is equivalent to removing the scheme
     * information.
     *
     * @throws SyntaxError for invalid component or transformations
     *                     that would result in an object in invalid state.
     */
    public function withScheme(Stringable|string|null $scheme): self;

    /**
     * Return an instance with the specified user information.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the specified user information.
     *
     * Password is optional, but the user information MUST include the
     * user; a null value for the user is equivalent to removing user
     * information.
     *
     * @throws SyntaxError for invalid component or transformations
     *                     that would result in an object in invalid state.
     */
    public function withUserInfo(Stringable|string|null $user, Stringable|string|null $password = null): self;

    /**
     * Return an instance with the specified host.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the specified host.
     *
     * A null value provided for the host is equivalent to removing the host
     * information.
     *
     * @throws SyntaxError for invalid component or transformations
     *                     that would result in an object in invalid state.
     * @throws MissingFeature for component or transformations
     *                        requiring IDN support when IDN support is not present
     *                        or misconfigured.
     */
    public function withHost(Stringable|string|null $host): self;

    /**
     * Return an instance with the specified port.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the specified port.
     *
     * A null value provided for the port is equivalent to removing the port
     * information.
     *
     * @throws SyntaxError for invalid component or transformations
     *                     that would result in an object in invalid state.
     */
    public function withPort(?int $port): self;

    /**
     * Return an instance with the specified path.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the specified path.
     *
     * The path can either be empty or absolute (starting with a slash) or
     * rootless (not starting with a slash). Implementations MUST support all
     * three syntaxes.
     *
     * Users can provide both encoded and decoded path characters.
     * Implementations ensure the correct encoding as outlined in getPath().
     *
     * @throws SyntaxError for invalid component or transformations
     *                     that would result in an object in invalid state.
     */
    public function withPath(Stringable|string $path): self;

    /**
     * Return an instance with the specified query string.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the specified query string.
     *
     * Users can provide both encoded and decoded query characters.
     * Implementations ensure the correct encoding as outlined in getQuery().
     *
     * A null value provided for the query is equivalent to removing the query
     * information.
     *
     * @throws SyntaxError for invalid component or transformations
     *                     that would result in an object in invalid state.
     */
    public function withQuery(Stringable|string|null $query): self;

    /**
     * Return an instance with the specified URI fragment.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the specified URI fragment.
     *
     * Users can provide both encoded and decoded fragment characters.
     * Implementations ensure the correct encoding as outlined in getFragment().
     *
     * A null value provided for the fragment is equivalent to removing the fragment
     * information.
     *
     * @throws SyntaxError for invalid component or transformations
     *                     that would result in an object in invalid state.
     */
    public function withFragment(Stringable|string|null $fragment): self;
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\Contracts;

interface Transformable
{
    /**
     * Apply a transformation to this instance and return a new instance.
     *
     * This method MUST retain the state of the current instance, and return
     * a new instance of the same type.
     *
     * @param callable(static): static $callback
     */
    public function transform(callable $callback): static;
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\Contracts;

use Countable;
use Iterator;
use IteratorAggregate;
use League\Uri\Exceptions\SyntaxError;
use Stringable;

/**
 * @extends IteratorAggregate<string>
 */
interface SegmentedPathInterface extends Countable, IteratorAggregate, PathInterface
{
    /**
     * Returns the total number of segments in the path.
     */
    public function count(): int;

    /**
     * Iterate over the path segment.
     *
     * @return Iterator<string>
     */
    public function getIterator(): Iterator;

    /**
     * Returns parent directory's path.
     */
    public function getDirname(): string;

    /**
     * Returns the path basename.
     */
    public function getBasename(): string;

    /**
     * Returns the basename extension.
     */
    public function getExtension(): string;

    /**
     * Retrieves a single path segment.
     *
     * If the segment offset has not been set, returns null.
     */
    public function get(int $offset): ?string;

    /**
     * Returns the associated key for a specific segment.
     *
     * If a value is specified only the keys associated with
     * the given value will be returned
     *
     * @return array<int>
     */
    public function keys(Stringable|string|null $segment = null): array;

    /**
     * Appends a segment to the path.
     */
    public function append(Stringable|string $path): self;

    /**
     * Extracts a slice of $length elements starting at position $offset from the host.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the selected slice.
     *
     * If $length is null it returns all elements from $offset to the end of the Path.
     */
    public function slice(int $offset, ?int $length = null): self;

    /**
     * Prepends a segment to the path.
     */
    public function prepend(Stringable|string $path): self;

    /**
     * Returns an instance with the modified segment.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the new segment
     *
     * If $key is non-negative, the added segment will be the segment at $key position from the start.
     * If $key is negative, the added segment will be the segment at $key position from the end.
     *
     * @throws SyntaxError If the key is invalid
     */
    public function withSegment(int $key, Stringable|string $segment): self;

    /**
     * Returns an instance without the specified segment.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the modified component
     *
     * If $key is non-negative, the removed segment will be the segment at $key position from the start.
     * If $key is negative, the removed segment will be the segment at $key position from the end.
     *
     * @throws SyntaxError If the key is invalid
     */
    public function withoutSegment(int ...$keys): self;

    /**
     * Returns an instance without duplicate delimiters.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the path component normalized by removing
     * multiple consecutive empty segment
     */
    public function withoutEmptySegments(): self;

    /**
     * Returns an instance with the specified parent directory's path.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the extension basename modified.
     */
    public function withDirname(Stringable|string $path): self;

    /**
     * Returns an instance with the specified basename.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the extension basename modified.
     */
    public function withBasename(Stringable|string $basename): self;

    /**
     * Returns an instance with the specified basename extension.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the extension basename modified.
     */
    public function withExtension(Stringable|string $extension): self;
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\Contracts;

use Throwable;

interface UriException extends Throwable
{
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\Contracts;

/**
 * @method self normalize() returns the normalized string representation of the component
 */
interface FragmentInterface extends UriComponentInterface
{
    /**
     * Returns the decoded fragment.
     */
    public function decoded(): ?string;
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\Contracts;

use Psr\Http\Message\UriInterface as Psr7UriInterface;

/**
 * @deprecated since version 7.6.0
 */
interface UriAccess
{
    public function getUri(): UriInterface|Psr7UriInterface;

    /**
     * Returns the RFC3986 string representation of the complete URI.
     */
    public function getUriString(): string;
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\Contracts;

/**
 * @method string|null encoded() returns RFC3986 encoded host
 */
interface HostInterface extends UriComponentInterface
{
    /**
     * Returns the ascii representation.
     */
    public function toAscii(): ?string;

    /**
     * Returns the unicode representation.
     */
    public function toUnicode(): ?string;

    /**
     * Returns the IP version.
     *
     * If the host is a not an IP this method will return null
     */
    public function getIpVersion(): ?string;

    /**
     * Returns the IP component If the Host is an IP address.
     *
     * If the host is a not an IP this method will return null
     */
    public function getIp(): ?string;

    /**
     * Tells whether the host is a domain name.
     */
    public function isDomain(): bool;

    /**
     * Tells whether the host is an IP Address.
     */
    public function isIp(): bool;

    /**
     * Tells whether the host is a registered name.
     */
    public function isRegisteredName(): bool;
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\Contracts;

use League\Uri\Exceptions\MissingFeature;
use League\Uri\Exceptions\SyntaxError;
use Stringable;

interface AuthorityInterface extends UriComponentInterface
{
    /**
     * Returns the host component of the authority.
     */
    public function getHost(): ?string;

    /**
     * Returns the port component of the authority.
     */
    public function getPort(): ?int;

    /**
     * Returns the user information component of the authority.
     */
    public function getUserInfo(): ?string;

    /**
     * Returns an associative array containing all the Authority components.
     *
     * The returned a hashmap similar to PHP's parse_url return value
     *
     * @link https://tools.ietf.org/html/rfc3986
     *
     * @return array{user: ?string, pass : ?string, host: ?string, port: ?int}
     */
    public function components(): array;

    /**
     * Return an instance with the specified host.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the specified host.
     *
     * A null value provided for the host is equivalent to removing the host
     * information.
     *
     * @throws SyntaxError for invalid component or transformations
     *                     that would result in an object in invalid state.
     * @throws MissingFeature for component or transformations
     *                        requiring IDN support when IDN support is not present
     *                        or misconfigured.
     */
    public function withHost(Stringable|string|null $host): self;

    /**
     * Return an instance with the specified port.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the specified port.
     *
     * A null value provided for the port is equivalent to removing the port
     * information.
     *
     * @throws SyntaxError for invalid component or transformations
     *                     that would result in an object in invalid state.
     */
    public function withPort(?int $port): self;

    /**
     * Return an instance with the specified user information.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the specified user information.
     *
     * Password is optional, but the user information MUST include the
     * user; a null value for the user is equivalent to removing user
     * information.
     *
     * @throws SyntaxError for invalid component or transformations
     *                     that would result in an object in invalid state.
     */
    public function withUserInfo(Stringable|string|null $user, Stringable|string|null $password = null): self;
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\Contracts;

use JsonSerializable;
use Stringable;

/**
 * @method static when(callable|bool $condition, callable $onSuccess, ?callable $onFail = null) conditionally return a new instance
 * @method bool equals(mixed $value) tells whether the submitted value is equal to the current instance value
 */
interface UriComponentInterface extends JsonSerializable, Stringable
{
    /**
     * Returns the instance string representation.
     *
     * If the instance is defined, the value returned MUST be percent-encoded,
     * but MUST NOT double-encode any characters. To determine what characters
     * to encode, please refer to RFC 3986, Sections 2 and 3.
     *
     * If the instance is not defined null is returned
     */
    public function value(): ?string;

    /**
     * Returns the instance string representation.
     *
     * If the instance is defined, the value returned MUST be percent-encoded,
     * but MUST NOT double-encode any characters. To determine what characters
     * to encode, please refer to RFC 3986, Sections 2 and 3.
     *
     * If the instance is not defined, an empty string is returned
     */
    public function toString(): string;

    /**
     * Returns the instance string representation.
     *
     * If the instance is defined, the value returned MUST be percent-encoded,
     * but MUST NOT double-encode any characters. To determine what characters
     * to encode, please refer to RFC 3986, Sections 2 and 3.
     *
     * If the instance is not defined, an empty string is returned
     */
    public function __toString(): string;

    /**
     * Returns the instance json representation.
     *
     * If the instance is defined, the value returned MUST be percent-encoded,
     * but MUST NOT double-encode any characters. To determine what characters
     * to encode, please refer to RFC 3986 or RFC 1738.
     *
     * If the instance is not defined, null is returned
     */
    public function jsonSerialize(): ?string;

    /**
     * Returns the instance string representation with its optional URI delimiters.
     *
     * The value returned MUST be percent-encoded, but MUST NOT double-encode any
     * characters. To determine what characters to encode, please refer to RFC 3986,
     * Sections 2 and 3.
     *
     * If the instance is not defined, an empty string is returned
     */
    public function getUriComponent(): string;
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\Contracts;

use SplFileObject;
use Stringable;

interface DataPathInterface extends PathInterface
{
    /**
     * Retrieve the data mime type associated to the URI.
     *
     * If no mimetype is present, this method MUST return the default mimetype 'text/plain'.
     *
     * @see http://tools.ietf.org/html/rfc2397#section-2
     */
    public function getMimeType(): string;

    /**
     * Retrieve the parameters associated with the Mime Type of the URI.
     *
     * If no parameters is present, this method MUST return the default parameter 'charset=US-ASCII'.
     *
     * @see http://tools.ietf.org/html/rfc2397#section-2
     */
    public function getParameters(): string;

    /**
     * Retrieve the mediatype associated with the URI.
     *
     * If no mediatype is present, this method MUST return the default parameter 'text/plain;charset=US-ASCII'.
     *
     * @see http://tools.ietf.org/html/rfc2397#section-3
     *
     * @return string The URI scheme.
     */
    public function getMediaType(): string;

    /**
     * Retrieves the data string.
     *
     * Retrieves the data part of the path. If no data part is provided return
     * an empty string
     */
    public function getData(): string;

    /**
     * Tells whether the data is binary safe encoded.
     */
    public function isBinaryData(): bool;

    /**
     * Save the data to a specific file.
     */
    public function save(string $path, string $mode = 'w'): SplFileObject;

    /**
     * Returns an instance where the data part is base64 encoded.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance where the data part is base64 encoded
     */
    public function toBinary(): self;

    /**
     * Returns an instance where the data part is url encoded following RFC3986 rules.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance where the data part is url encoded
     */
    public function toAscii(): self;

    /**
     * Return an instance with the specified mediatype parameters.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the specified mediatype parameters.
     *
     * Users must provide encoded characters.
     *
     * An empty parameters value is equivalent to removing the parameter.
     */
    public function withParameters(Stringable|string $parameters): self;
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\Contracts;

interface PortInterface extends UriComponentInterface
{
    /**
     * Returns the integer representation of the Port.
     */
    public function toInt(): ?int;
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri;

use BackedEnum;
use Exception;
use JsonSerializable;
use League\Uri\Contracts\UriComponentInterface;
use League\Uri\Exceptions\SyntaxError;
use League\Uri\Idna\Converter as IdnConverter;
use Stringable;
use Throwable;

use function array_key_first;
use function count;
use function explode;
use function filter_var;
use function get_object_vars;
use function in_array;
use function inet_pton;
use function is_object;
use function preg_match;
use function rawurldecode;
use function strpos;
use function strtolower;
use function substr;

use const FILTER_FLAG_IPV4;
use const FILTER_FLAG_IPV6;
use const FILTER_VALIDATE_IP;

/**
 * @phpstan-type HostRecordSerializedShape array{0: array{host: ?string}, 1: array{}}
 */
final class HostRecord implements JsonSerializable
{
    /**
     * Maximum number of host cached.
     *
     * @var int
     */
    private const MAXIMUM_HOST_CACHED = 100;

    private const REGEXP_NON_ASCII_PATTERN = '/[^\x20-\x7f]/';

    /**
     * @see https://tools.ietf.org/html/rfc3986#section-3.2.2
     *
     * invalid characters in host regular expression
     */
    private const REGEXP_INVALID_HOST_CHARS = '/
        [:\/?#\[\]@ ]  # gen-delims characters as well as the space character
    /ix';

    /**
     * General registered name regular expression.
     *
     * @see https://tools.ietf.org/html/rfc3986#section-3.2.2
     * @see https://regex101.com/r/fptU8V/1
     */
    private const REGEXP_REGISTERED_NAME = '/
    (?(DEFINE)
        (?<unreserved>[a-z0-9_~\-])   # . is missing as it is used to separate labels
        (?<sub_delims>[!$&\'()*+,;=])
        (?<encoded>%[A-F0-9]{2})
        (?<reg_name>(?:(?&unreserved)|(?&sub_delims)|(?&encoded))*)
    )
        ^(?:(?&reg_name)\.)*(?&reg_name)\.?$
    /ix';

    /**
     * Domain name regular expression.
     *
     * Everything but the domain name length is validated
     *
     * @see https://tools.ietf.org/html/rfc1034#section-3.5
     * @see https://tools.ietf.org/html/rfc1123#section-2.1
     * @see https://regex101.com/r/71j6rt/1
     */
    private const REGEXP_DOMAIN_NAME = '/
    (?(DEFINE)
        (?<let_dig> [a-z0-9])                         # alpha digit
        (?<let_dig_hyp> [a-z0-9-])                    # alpha digit and hyphen
        (?<ldh_str> (?&let_dig_hyp){0,61}(?&let_dig)) # domain label end
        (?<label> (?&let_dig)((?&ldh_str))?)          # domain label
        (?<domain> (?&label)(\.(?&label)){0,126}\.?)  # domain name
    )
        ^(?&domain)$
    /ix';

    /**
     * @see https://tools.ietf.org/html/rfc3986#section-3.2.2
     *
     * IPvFuture regular expression
     */
    private const REGEXP_IP_FUTURE = '/^
        v(?<version>[A-F\d])+\.
        (?:
            (?<unreserved>[a-z\d_~\-\.])|
            (?<sub_delims>[!$&\'()*+,;=:])  # also include the : character
        )+
    $/ix';
    private const REGEXP_GEN_DELIMS = '/[:\/?#\[\]@ ]/';
    private const ADDRESS_BLOCK = "\xfe\x80";

    private ?bool $isDomainName = null;
    private ?bool $hasZoneIdentifier = null;
    private bool $asciiIsLoaded = false;
    private ?string $hostAsAscii = null;
    private bool $unicodeIsLoaded = false;
    private ?string $hostAsUnicode = null;
    private bool $isIpVersionLoaded = false;
    private ?string $ipVersion = null;
    private bool $isIpValueLoaded = false;
    private ?string $ipValue = null;

    private function __construct(
        public readonly ?string $value,
        public readonly HostType $type,
        public readonly HostFormat $format
    ) {
    }

    public function hasZoneIdentifier(): bool
    {
        return $this->hasZoneIdentifier ??= HostType::Ipv6 === $this->type && str_contains((string) $this->value, '%');
    }

    public function toAscii(): ?string
    {
        if (!$this->asciiIsLoaded) {
            $this->asciiIsLoaded = true;
            $this->hostAsAscii = (function (): ?string {
                if (HostType::RegisteredName !== $this->type || null === $this->value) {
                    return $this->value;
                }

                $formattedHost = rawurldecode($this->value);
                if ($formattedHost === $this->value) {
                    return $this->isDomainType() ? IdnConverter::toAscii($this->value)->domain() : strtolower($formattedHost);
                }

                return Encoder::normalizeHost($this->value);
            })();
        }

        return $this->hostAsAscii;
    }

    public function toUnicode(): ?string
    {
        if (!$this->unicodeIsLoaded) {
            $this->unicodeIsLoaded = true;
            $this->hostAsUnicode = $this->value;
            if ($this->isDomainType() && null !== $this->value) {
                $result = IdnConverter::toUnicode($this->value);
                if (!$result->hasErrors()) {
                    $this->hostAsUnicode = $result->domain();
                }
            }
        }

        return $this->hostAsUnicode;
    }

    public function isDomainType(): bool
    {
        return $this->isDomainName ??= match (true) {
            HostType::RegisteredName !== $this->type, '' === $this->value => false,
            null === $this->value => true,
            default => is_object($result = IdnConverter::toAscii($this->value))
                && !$result->hasErrors()
                && self::isValidDomain($result->domain()),
        };
    }

    public function ipVersion(): ?string
    {
        if (!$this->isIpVersionLoaded) {
            $this->isIpVersionLoaded = true;
            $this->ipVersion = match (true) {
                HostType::Ipv4 === $this->type => '4',
                HostType::Ipv6 === $this->type => '6',
                1 === preg_match(self::REGEXP_IP_FUTURE, substr((string) $this->value, 1, -1), $matches) => $matches['version'],
                default => null,
            };
        }

        return $this->ipVersion;
    }

    public function ipValue(): ?string
    {
        if (!$this->isIpValueLoaded) {
            $this->isIpValueLoaded = true;
            $this->ipValue = (function (): ?string {
                if (HostType::RegisteredName === $this->type) {
                    return null;
                }

                if (HostType::Ipv4 === $this->type) {
                    return $this->value;
                }

                $ip = substr((string) $this->value, 1, -1);
                if (HostType::Ipv6 !== $this->type) {
                    return substr($ip, (int) strpos($ip, '.') + 1);
                }

                $pos = strpos($ip, '%');
                if (false === $pos) {
                    return $ip;
                }

                return substr($ip, 0, $pos).'%'.rawurldecode(substr($ip, $pos + 3));
            })();
        }

        return $this->ipValue;
    }

    public static function isValid(BackedEnum|Stringable|string|null $host): bool
    {
        try {
            HostRecord::from($host);

            return true;
        } catch (Throwable) {
            return false;
        }
    }

    public static function isIpv4(Stringable|string|null $host): bool
    {
        try {
            return HostType::Ipv4 === HostRecord::from($host)->type;
        } catch (Throwable) {
            return false;
        }
    }

    public static function isIpv6(Stringable|string|null $host): bool
    {
        try {
            return HostType::Ipv6 === HostRecord::from($host)->type;
        } catch (Throwable) {
            return false;
        }
    }

    public static function isIpvFuture(Stringable|string|null $host): bool
    {
        try {
            return HostType::IpvFuture === HostRecord::from($host)->type;
        } catch (Throwable) {
            return false;
        }
    }

    public static function isIp(Stringable|string|null $host): bool
    {
        return self::isIpv4($host)
            || self::isIpv6($host)
            || self::isIpvFuture($host);
    }

    public static function isRegisteredName(Stringable|string|null $host): bool
    {
        try {
            return HostType::RegisteredName === HostRecord::from($host)->type;
        } catch (Throwable) {
            return false;
        }
    }

    public static function isDomain(Stringable|string|null $host): bool
    {
        try {
            return HostRecord::from($host)->isDomainType();
        } catch (Throwable) {
            return false;
        }
    }

    public static function parse(BackedEnum|Stringable|string|null $host): ?self
    {
        try {
            return self::from($host);
        } catch (Throwable) {
            return null;
        }
    }

    /**
     * @throws SyntaxError
     */
    public static function from(BackedEnum|Stringable|string|null $host): self
    {
        if ($host instanceof BackedEnum) {
            $host = $host->value;
        }

        if ($host instanceof UriComponentInterface) {
            $host = $host->value();
        }

        if (null === $host) {
            return new self(
                value: null,
                type: HostType::RegisteredName,
                format: HostFormat::Ascii,
            );
        }

        $host = (string) $host;
        if ('' === $host) {
            return new self(
                value: '',
                type: HostType::RegisteredName,
                format: HostFormat::Ascii,
            );
        }

        static $inMemoryCache = [];
        if (isset($inMemoryCache[$host])) {
            return $inMemoryCache[$host];
        }

        if (self::MAXIMUM_HOST_CACHED < count($inMemoryCache)) {
            unset($inMemoryCache[array_key_first($inMemoryCache)]);
        }

        if ($host === filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
            return $inMemoryCache[$host] = new self(
                value: $host,
                type: HostType::Ipv4,
                format: HostFormat::Ascii,
            );
        }

        if (str_starts_with($host, '[')) {
            str_ends_with($host, ']') || throw new SyntaxError('The host '.$host.' is not a valid IPv6 host.');

            $ipHost = substr($host, 1, -1);
            if (1 === preg_match(self::REGEXP_IP_FUTURE, $ipHost, $matches)) {
                return !in_array($matches['version'], ['4', '6'], true) ? ($inMemoryCache[$host] = new self(
                    value: $host,
                    type: HostType::IpvFuture,
                    format: HostFormat::Ascii,
                )) : throw new SyntaxError('The host '.$host.' is not a valid IPvFuture host.');
            }

            if (self::isValidIpv6Hostname($ipHost)) {
                return $inMemoryCache[$host] = new self(
                    value: $host,
                    type: HostType::Ipv6,
                    format: HostFormat::Ascii,
                );
            }

            throw new SyntaxError('The host '.$host.' is not a valid IPv6 host.');
        }

        $domainName = rawurldecode($host);
        $format = HostFormat::Unicode;
        if (1 !== preg_match(self::REGEXP_NON_ASCII_PATTERN, $domainName)) {
            $domainName = strtolower($domainName);
            $format = HostFormat::Ascii;
        }

        if (1 === preg_match(self::REGEXP_REGISTERED_NAME, $domainName)) {
            return $inMemoryCache[$host] = new self(
                value: $host,
                type: HostType::RegisteredName,
                format: $format,
            );
        }

        (HostFormat::Ascii !== $format && 1 !== preg_match(self::REGEXP_INVALID_HOST_CHARS, $domainName)) || throw new SyntaxError('`'.$host.'` is an invalid domain name : the host contains invalid characters.');
        IdnConverter::toAsciiOrFail($domainName);

        return $inMemoryCache[$host] = new self(
            value: $host,
            type: HostType::RegisteredName,
            format: $format,
        );
    }

    /**
     * Tells whether the registered name is a valid domain name according to RFC1123.
     *
     * @see http://man7.org/linux/man-pages/man7/hostname.7.html
     * @see https://tools.ietf.org/html/rfc1123#section-2.1
     */
    private static function isValidDomain(string $hostname): bool
    {
        $domainMaxLength = str_ends_with($hostname, '.') ? 254 : 253;

        return !isset($hostname[$domainMaxLength])
            && 1 === preg_match(self::REGEXP_DOMAIN_NAME, $hostname);
    }

    /**
     * Validates an Ipv6 as Host.
     *
     * @see http://tools.ietf.org/html/rfc6874#section-2
     * @see http://tools.ietf.org/html/rfc6874#section-4
     */
    private static function isValidIpv6Hostname(string $host): bool
    {
        [$ipv6, $scope] = explode('%', $host, 2) + [1 => null];
        if (null === $scope) {
            return (bool) filter_var($ipv6, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6);
        }

        $scope = rawurldecode('%'.$scope);

        return 1 !== preg_match(self::REGEXP_NON_ASCII_PATTERN, $scope)
            && 1 !== preg_match(self::REGEXP_GEN_DELIMS, $scope)
            && false !== filter_var($ipv6, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)
            && str_starts_with((string)inet_pton((string)$ipv6), self::ADDRESS_BLOCK);
    }

    public function jsonSerialize(): ?string
    {
        return $this->value;
    }

    /**
     * @return HostRecordSerializedShape
     */
    public function __serialize(): array
    {
        return [['host' => $this->value], []];
    }

    /**
     * @param HostRecordSerializedShape $data
     *
     * @throws Exception|SyntaxError
     */
    public function __unserialize(array $data): void
    {
        [$properties] = $data;
        $record = self::from($properties['host'] ?? throw new Exception('The `host` property is missing from the serialized object.'));
        //if the Host computed value are already cache this avoid recomputing them
        foreach (get_object_vars($record) as $prop => $value) {
            /* @phpstan-ignore-next-line */
            $this->{$prop} = $value;
        }
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\IPv4;

use function floor;
use function intval;

final class NativeCalculator implements Calculator
{
    public function baseConvert(mixed $value, int $base): int
    {
        return intval((string) $value, $base);
    }

    public function pow(mixed $value, int $exponent)
    {
        return $value ** $exponent;
    }

    public function compare(mixed $value1, mixed $value2): int
    {
        return $value1 <=> $value2;
    }

    public function multiply(mixed $value1, mixed $value2): int
    {
        return $value1 * $value2;
    }

    public function div(mixed $value, mixed $base): int
    {
        return (int) floor($value / $base);
    }

    public function mod(mixed $value, mixed $base): int
    {
        return $value % $base;
    }

    public function add(mixed $value1, mixed $value2): int
    {
        return $value1 + $value2;
    }

    public function sub(mixed $value1, mixed $value2): int
    {
        return $value1 - $value2;
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\IPv4;

use function bcadd;
use function bccomp;
use function bcdiv;
use function bcmod;
use function bcmul;
use function bcpow;
use function bcsub;
use function str_split;

final class BCMathCalculator implements Calculator
{
    private const SCALE = 0;
    private const CONVERSION_TABLE = [
        '0' => '0', '1' => '1', '2' => '2', '3' => '3',
        '4' => '4', '5' => '5', '6' => '6', '7' => '7',
        '8' => '8', '9' => '9', 'a' => '10', 'b' => '11',
        'c' => '12', 'd' => '13', 'e' => '14', 'f' => '15',
    ];

    public function baseConvert(mixed $value, int $base): string
    {
        $value = (string) $value;
        if (10 === $base) {
            return $value;
        }

        $base = (string) $base;
        $decimal = '0';
        foreach (str_split($value) as $char) {
            $decimal = bcadd($this->multiply($decimal, $base), self::CONVERSION_TABLE[$char], self::SCALE);
        }

        return $decimal;
    }

    public function pow(mixed $value, int $exponent): string
    {
        return bcpow((string) $value, (string) $exponent, self::SCALE);
    }

    public function compare(mixed $value1, mixed $value2): int
    {
        return bccomp((string) $value1, (string) $value2, self::SCALE);
    }

    public function multiply(mixed $value1, mixed $value2): string
    {
        return bcmul((string) $value1, (string) $value2, self::SCALE);
    }

    public function div(mixed $value, mixed $base): string
    {
        return bcdiv((string) $value, (string) $base, self::SCALE);
    }

    public function mod(mixed $value, mixed $base): string
    {
        return bcmod((string) $value, (string) $base, self::SCALE);
    }

    public function add(mixed $value1, mixed $value2): string
    {
        return bcadd((string) $value1, (string) $value2, self::SCALE);
    }

    public function sub(mixed $value1, mixed $value2): string
    {
        return bcsub((string) $value1, (string) $value2, self::SCALE);
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\IPv4;

interface Calculator
{
    /**
     * Add numbers.
     *
     * @param mixed $value1 a number that will be added to $value2
     * @param mixed $value2 a number that will be added to $value1
     *
     * @return mixed the addition result
     */
    public function add(mixed $value1, mixed $value2);

    /**
     * Subtract one number from another.
     *
     * @param mixed $value1 a number that will be subtracted of $value2
     * @param mixed $value2 a number that will be subtracted to $value1
     *
     * @return mixed the subtraction result
     */
    public function sub(mixed $value1, mixed $value2);

    /**
     * Multiply numbers.
     *
     * @param mixed $value1 a number that will be multiplied by $value2
     * @param mixed $value2 a number that will be multiplied by $value1
     *
     * @return mixed the multiplication result
     */
    public function multiply(mixed $value1, mixed $value2);

    /**
     * Divide numbers.
     *
     * @param mixed $value The number being divided.
     * @param mixed $base The number that $value is being divided by.
     *
     * @return mixed the result of the division
     */
    public function div(mixed $value, mixed $base);

    /**
     * Raise an number to the power of exponent.
     *
     * @param mixed $value scalar, the base to use
     *
     * @return mixed the value raised to the power of exp.
     */
    public function pow(mixed $value, int $exponent);

    /**
     * Returns the int point remainder (modulo) of the division of the arguments.
     *
     * @param mixed $value The dividend
     * @param mixed $base The divisor
     *
     * @return mixed the remainder
     */
    public function mod(mixed $value, mixed $base);

    /**
     * Number comparison.
     *
     * @param mixed $value1 the first value
     * @param mixed $value2 the second value
     *
     * @return int Returns < 0 if value1 is less than value2; > 0 if value1 is greater than value2, and 0 if they are equal.
     */
    public function compare(mixed $value1, mixed $value2): int;

    /**
     * Get the decimal integer value of a variable.
     *
     * @param mixed $value The scalar value being converted to an integer
     *
     * @return mixed the integer value
     */
    public function baseConvert(mixed $value, int $base);
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\IPv4;

use GMP;

use function gmp_add;
use function gmp_cmp;
use function gmp_div_q;
use function gmp_init;
use function gmp_mod;
use function gmp_mul;
use function gmp_pow;
use function gmp_sub;

use const GMP_ROUND_MINUSINF;

final class GMPCalculator implements Calculator
{
    public function baseConvert(mixed $value, int $base): GMP
    {
        return gmp_init($value, $base);
    }

    public function pow(mixed $value, int $exponent): GMP
    {
        return gmp_pow($value, $exponent);
    }

    public function compare(mixed $value1, mixed $value2): int
    {
        return gmp_cmp($value1, $value2);
    }

    public function multiply(mixed $value1, mixed $value2): GMP
    {
        return gmp_mul($value1, $value2);
    }

    public function div(mixed $value, mixed $base): GMP
    {
        return gmp_div_q($value, $base, GMP_ROUND_MINUSINF);
    }

    public function mod(mixed $value, mixed $base): GMP
    {
        return gmp_mod($value, $base);
    }

    public function add(mixed $value1, mixed $value2): GMP
    {
        return gmp_add($value1, $value2);
    }

    public function sub(mixed $value1, mixed $value2): GMP
    {
        return gmp_sub($value1, $value2);
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\IPv4;

use BackedEnum;
use League\Uri\Exceptions\MissingFeature;
use League\Uri\FeatureDetection;
use Stringable;

use function array_pop;
use function count;
use function explode;
use function extension_loaded;
use function hexdec;
use function long2ip;
use function ltrim;
use function preg_match;
use function str_ends_with;
use function substr;

use const FILTER_FLAG_IPV4;
use const FILTER_FLAG_IPV6;
use const FILTER_VALIDATE_IP;

final class Converter
{
    private const REGEXP_IPV4_HOST = '/
        (?(DEFINE) # . is missing as it is used to separate labels
            (?<hexadecimal>0x[[:xdigit:]]*)
            (?<octal>0[0-7]*)
            (?<decimal>\d+)
            (?<ipv4_part>(?:(?&hexadecimal)|(?&octal)|(?&decimal))*)
        )
        ^(?:(?&ipv4_part)\.){0,3}(?&ipv4_part)\.?$
    /x';
    private const REGEXP_IPV4_NUMBER_PER_BASE = [
        '/^0x(?<number>[[:xdigit:]]*)$/' => 16,
        '/^0(?<number>[0-7]*)$/' => 8,
        '/^(?<number>\d+)$/' => 10,
    ];

    private const IPV6_6TO4_PREFIX = '2002:';
    private const IPV4_MAPPED_PREFIX = '::ffff:';

    private readonly mixed $maxIPv4Number;

    public function __construct(
        private readonly Calculator $calculator
    ) {
        $this->maxIPv4Number = $calculator->sub($calculator->pow(2, 32), 1);
    }

    /**
     * Returns an instance using a GMP calculator.
     */
    public static function fromGMP(): self
    {
        return new self(new GMPCalculator());
    }

    /**
     * Returns an instance using a Bcmath calculator.
     */
    public static function fromBCMath(): self
    {
        return new self(new BCMathCalculator());
    }

    /**
     * Returns an instance using a PHP native calculator (requires 64bits PHP).
     */
    public static function fromNative(): self
    {
        return new self(new NativeCalculator());
    }

    /**
     * Returns an instance using a detected calculator depending on the PHP environment.
     *
     * @throws MissingFeature If no Calculator implementing object can be used on the platform
     *
     * @codeCoverageIgnore
     */
    public static function fromEnvironment(): self
    {
        FeatureDetection::supportsIPv4Conversion();

        return match (true) {
            extension_loaded('gmp') => self::fromGMP(),
            extension_loaded('bcmath') => self::fromBCMath(),
            default => self::fromNative(),
        };
    }

    public function isIpv4(BackedEnum|Stringable|string|null $host): bool
    {
        if ($host instanceof BackedEnum) {
            $host = (string) $host->value;
        }

        if (null === $host) {
            return false;
        }

        if (null !== $this->toDecimal($host)) {
            return true;
        }

        $host = (string) $host;
        if (false === filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
            return false;
        }

        $ipAddress = strtolower((string) inet_ntop((string) inet_pton($host)));
        if (str_starts_with($ipAddress, self::IPV4_MAPPED_PREFIX)) {
            return false !== filter_var(substr($ipAddress, 7), FILTER_VALIDATE_IP, FILTER_FLAG_IPV4);
        }

        if (!str_starts_with($ipAddress, self::IPV6_6TO4_PREFIX)) {
            return false;
        }

        $hexParts = explode(':', substr($ipAddress, 5, 9));
        if (count($hexParts) < 2) {
            return false;
        }

        $ipAddress = long2ip((int) hexdec($hexParts[0]) * 65536 + (int) hexdec($hexParts[1]));

        return '' !== ''.$ipAddress;
    }

    public function toIPv6Using6to4(BackedEnum|Stringable|string|null $host): ?string
    {
        $host = $this->toDecimal($host);
        if (null === $host) {
            return null;
        }

        /** @var non-empty-list<numeric-string> $parts */
        $parts = array_map(
            fn (string $part): string => sprintf('%02x', $part),
            explode('.', $host)
        );

        return '['.self::IPV6_6TO4_PREFIX.$parts[0].$parts[1].':'.$parts[2].$parts[3].'::]';
    }

    public function toIPv6UsingMapping(BackedEnum|Stringable|string|null $host): ?string
    {
        $host = $this->toDecimal($host);
        if (null === $host) {
            return null;
        }

        return '['.self::IPV4_MAPPED_PREFIX.$host.']';
    }

    public function toOctal(BackedEnum|Stringable|string|null $host): ?string
    {
        $host = $this->toDecimal($host);

        return match (null) {
            $host => null,
            default => implode('.', array_map(
                fn ($value) => str_pad(decoct((int) $value), 4, '0', STR_PAD_LEFT),
                explode('.', $host)
            )),
        };
    }

    public function toHexadecimal(BackedEnum|Stringable|string|null $host): ?string
    {
        $host = $this->toDecimal($host);

        return match (null) {
            $host => null,
            default => '0x'.implode('', array_map(
                fn ($value) => dechex((int) $value),
                explode('.', $host)
            )),
        };
    }

    /**
     * Tries to convert a IPv4 hexadecimal or a IPv4 octal notation into a IPv4 dot-decimal notation if possible
     * otherwise returns null.
     *
     * @see https://url.spec.whatwg.org/#concept-ipv4-parser
     */
    public function toDecimal(BackedEnum|Stringable|string|null $host): ?string
    {
        if ($host instanceof BackedEnum) {
            $host = $host->value;
        }

        $host = (string) $host;
        if (str_starts_with($host, '[') && str_ends_with($host, ']')) {
            $host = substr($host, 1, -1);
            if (false === filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
                return null;
            }

            $ipAddress = strtolower((string) inet_ntop((string) inet_pton($host)));
            if (str_starts_with($ipAddress, self::IPV4_MAPPED_PREFIX)) {
                return substr($ipAddress, 7);
            }

            if (!str_starts_with($ipAddress, self::IPV6_6TO4_PREFIX)) {
                return null;
            }

            $hexParts = explode(':', substr($ipAddress, 5, 9));

            return (string) match (true) {
                count($hexParts) < 2 => null,
                default => long2ip((int) hexdec($hexParts[0]) * 65536 + (int) hexdec($hexParts[1])),
            };
        }

        if (1 !== preg_match(self::REGEXP_IPV4_HOST, $host)) {
            return null;
        }

        if (str_ends_with($host, '.')) {
            $host = substr($host, 0, -1);
        }

        $numbers = [];
        foreach (explode('.', $host) as $label) {
            $number = $this->labelToNumber($label);
            if (null === $number) {
                return null;
            }

            $numbers[] = $number;
        }

        $ipv4 = array_pop($numbers);
        $max = $this->calculator->pow(256, 6 - count($numbers));
        if ($this->calculator->compare($ipv4, $max) > 0) {
            return null;
        }

        foreach ($numbers as $offset => $number) {
            if ($this->calculator->compare($number, 255) > 0) {
                return null;
            }

            $ipv4 = $this->calculator->add($ipv4, $this->calculator->multiply(
                $number,
                $this->calculator->pow(256, 3 - $offset)
            ));
        }

        return $this->long2Ip($ipv4);
    }

    /**
     * Converts a domain label into a IPv4 integer part.
     *
     * @see https://url.spec.whatwg.org/#ipv4-number-parser
     *
     * @return mixed returns null if it cannot correctly convert the label
     */
    private function labelToNumber(string $label): mixed
    {
        foreach (self::REGEXP_IPV4_NUMBER_PER_BASE as $regexp => $base) {
            if (1 !== preg_match($regexp, $label, $matches)) {
                continue;
            }

            $number = ltrim($matches['number'], '0');
            if ('' === $number) {
                return 0;
            }

            $number = $this->calculator->baseConvert($number, $base);
            if (0 <= $this->calculator->compare($number, 0) && 0 >= $this->calculator->compare($number, $this->maxIPv4Number)) {
                return $number;
            }
        }

        return null;
    }

    /**
     * Generates the dot-decimal notation for IPv4.
     *
     * @see https://url.spec.whatwg.org/#concept-ipv4-parser
     *
     * @param mixed $ipAddress the number representation of the IPV4address
     */
    private function long2Ip(mixed $ipAddress): string
    {
        $output = '';
        for ($offset = 0; $offset < 4; $offset++) {
            $output = $this->calculator->mod($ipAddress, 256).$output;
            if ($offset < 3) {
                $output = '.'.$output;
            }
            $ipAddress = $this->calculator->div($ipAddress, 256);
        }

        return $output;
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri;

enum UrnComparisonMode
{
    case IncludeComponents;
    case ExcludeComponents;
}
{
    "name": "league/uri-interfaces",
    "type": "library",
    "description" : "Common tools for parsing and resolving RFC3987/RFC3986 URI",
    "keywords": [
        "url",
        "uri",
        "rfc3986",
        "rfc3987",
        "rfc6570",
        "psr-7",
        "parse_url",
        "http",
        "https",
        "ws",
        "ftp",
        "data-uri",
        "file-uri",
        "parse_str",
        "query-string",
        "querystring",
        "hostname"
    ],
    "license": "MIT",
    "homepage": "https://uri.thephpleague.com",
    "authors": [
        {
            "name" : "Ignace Nyamagana Butera",
            "email" : "nyamsprod@gmail.com",
            "homepage" : "https://nyamsprod.com"
        }
    ],
    "funding": [
        {
            "type": "github",
            "url": "https://github.com/sponsors/nyamsprod"
        }
    ],
    "require": {
        "php" : "^8.1",
        "ext-filter": "*",
        "psr/http-message": "^1.1 || ^2.0"
    },
    "autoload": {
        "psr-4": {
            "League\\Uri\\": ""
        }
    },
    "suggest": {
        "ext-bcmath": "to improve IPV4 host parsing",
        "ext-gmp": "to improve IPV4 host parsing",
        "ext-intl": "to handle IDN host with the best performance",
        "php-64bit": "to improve IPV4 host parsing",
        "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present",
        "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification"
    },
    "extra": {
        "branch-alias": {
            "dev-master": "7.x-dev"
        }
    },
    "support": {
        "forum": "https://thephpleague.slack.com",
        "docs": "https://uri.thephpleague.com",
        "issues": "https://github.com/thephpleague/uri-src/issues"
    },
    "config": {
        "sort-packages": true
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\Exceptions;

use InvalidArgumentException;
use League\Uri\Contracts\UriException;

class SyntaxError extends InvalidArgumentException implements UriException
{
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\Exceptions;

use BackedEnum;
use League\Uri\Idna\Error;
use League\Uri\Idna\Result;
use Stringable;

final class ConversionFailed extends SyntaxError
{
    private function __construct(
        string $message,
        private readonly string $host,
        private readonly Result $result
    ) {
        parent::__construct($message);
    }

    public static function dueToIdnError(BackedEnum|Stringable|string $host, Result $result): self
    {
        $reasons = array_map(fn (Error $error): string => $error->description(), $result->errors());

        if ($host instanceof BackedEnum) {
            $host = (string) $host->value;
        }

        return new self('Host `'.$host.'` is invalid: '.implode('; ', $reasons).'.', (string) $host, $result);
    }

    public function getHost(): string
    {
        return $this->host;
    }

    public function getResult(): Result
    {
        return $this->result;
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\Exceptions;

class OffsetOutOfBounds extends SyntaxError
{
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\Exceptions;

use League\Uri\Contracts\UriException;
use RuntimeException;

class MissingFeature extends RuntimeException implements UriException
{
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri;

enum QueryExtractMode
{
    /**
     * Parses the query string using parse_str algorithm.
     */
    case Native;

    /**
     * Parses the query string like parse_str without mangling result keys.
     *
     * The result is similar to PHP parse_str when used with its second argument,
     * with the difference that variable names are not mangled.
     *
     * Behavior details:
     * - Empty names are ignored
     * - If a name is duplicated, the last value overwrites the previous one
     * - If no "[" is detected, the value is added using the name as the array key
     * - If "[" is detected but no matching "]" exists, the value is added using the name as the array key
     * - If bracket usage is malformed, the remaining part is dropped
     * - "." and " " are NOT converted to "_"
     * - If no "]" exists, the first "[" is not converted to "_"
     * - No whitespace trimming is performed on keys
     *
     * @see https://www.php.net/parse_str
     * @see https://wiki.php.net/rfc/on_demand_name_mangling
     */
    case Unmangled;

    /**
     * Same as QueryParsingMode::Unmangled and additionally
     * preserves null values instead of converting them
     * to empty strings.
     */
    case LossLess;
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri;

use ValueError;

/*
 *  Supported schemes and corresponding default port.
 *
 * @see https://github.com/python-hyper/hyperlink/blob/master/src/hyperlink/_url.py for the curating list definition
 * @see https://www.iana.org/assignments/uri-schemes/uri-schemes.xhtml
 * @see https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml
 */
enum UriScheme: string
{
    case About = 'about';
    case Acap = 'acap';
    case Bitcoin = 'bitcoin';
    case Geo = 'geo';
    case Blob = 'blob';
    case Afp = 'afp';
    case Data = 'data';
    case Dict = 'dict';
    case Dns = 'dns';
    case File = 'file';
    case Ftp = 'ftp';
    case Git = 'git';
    case Gopher = 'gopher';
    case Http = 'http';
    case Https = 'https';
    case Imap = 'imap';
    case Imaps = 'imaps';
    case Ipp = 'ipp';
    case Ipps = 'ipps';
    case Irc = 'irc';
    case Ircs = 'ircs';
    case Javascript = 'javascript';
    case Ldap = 'ldap';
    case Ldaps = 'ldaps';
    case Magnet = 'magnet';
    case Mailto = 'mailto';
    case Mms = 'mms';
    case Msrp = 'msrp';
    case Msrps = 'msrps';
    case Mtqp = 'mtqp';
    case News = 'news';
    case Nfs = 'nfs';
    case Nntp = 'nntp';
    case Nntps = 'nntps';
    case Pkcs11 = 'pkcs11';
    case Pop = 'pop';
    case Prospero = 'prospero';
    case Redis = 'redis';
    case Rsync = 'rsync';
    case Rtsp = 'rtsp';
    case Rtsps = 'rtsps';
    case Rtspu = 'rtspu';
    case Sftp = 'sftp';
    case Wss = 'wss';
    case Ws = 'ws';
    case Sip = 'sip';
    case Sips = 'sips';
    case Smb = 'smb';
    case Smtp = 'smtp';
    case Snmp = 'snmp';
    case Ssh = 'ssh';
    case Steam = 'steam';
    case Svn = 'svn';
    case Tel = 'tel';
    case Telnet = 'telnet';
    case Tn3270 = 'tn3270';
    case Urn = 'urn';
    case Ventrilo = 'ventrilo';
    case Vnc = 'vnc';
    case Wais = 'wais';
    case Xmpp = 'xmpp';

    public function port(): ?int
    {
        return match ($this) {
            self::Acap => 674,
            self::Afp => 548,
            self::Dict => 2628,
            self::Dns => 53,
            self::Ftp => 21,
            self::Http, self::Ws => 80,
            self::Https, self::Wss => 443,
            self::Git => 9418,
            self::Gopher => 70,
            self::Imap => 143,
            self::Imaps => 993,
            self::Ipp, self::Ipps => 631,
            self::Irc => 194,
            self::Ircs => 6697,
            self::Ldap => 389,
            self::Ldaps => 636,
            self::Mms => 1755,
            self::Msrp, self::Msrps => 2855,
            self::Mtqp => 1038,
            self::Nfs => 111,
            self::Nntp => 119,
            self::Nntps => 563,
            self::Pop => 110,
            self::Prospero => 1525,
            self::Redis => 6379,
            self::Rsync => 873,
            self::Rtsp => 554,
            self::Rtsps => 322,
            self::Rtspu => 5005,
            self::Sftp, self::Ssh => 22,
            self::Smb => 445,
            self::Smtp => 25,
            self::Snmp => 161,
            self::Svn => 3690,
            self::Telnet, self::Tn3270 => 23,
            self::Ventrilo => 3784,
            self::Vnc => 5900,
            self::Wais => 210,
            self::Xmpp => 80,
            default => null,
        };
    }

    public function type(): SchemeType
    {
        return match ($this) {
            self::Urn,
            self::About,
            self::Bitcoin,
            self::Blob,
            self::Data,
            self::Geo,
            self::Javascript,
            self::Magnet,
            self::Mailto,
            self::Pkcs11,
            self::Sip,
            self::Sips,
            self::Tel => SchemeType::Opaque,
            self::File => SchemeType::Hierarchical,
            self::News => SchemeType::Unknown,
            default => match (true) {
                null !== $this->port() => SchemeType::Hierarchical,
                default => SchemeType::Unknown,
            },
        };
    }

    public function isWhatWgSpecial(): bool
    {
        return match ($this) {
            self::Ftp,
            self::Http,
            self::Https,
            self::Ws,
            self::Wss => true,
            default => false,
        };
    }

    /**
     * @return list<self>
     */
    public static function fromPort(?int $port): array
    {
        null === $port || 0 <= $port || throw new ValueError('The submitted port cannot be negative.');

        static $reverse = [];
        if ([] === $reverse) {
            foreach (self::cases() as $case) {
                $defaultPort = $case->port();
                if (null === $defaultPort) {
                    continue;
                }
                $reverse[$defaultPort] ??= [];
                $reverse[$defaultPort][] = $case;

            }
        }

        return $reverse[$port] ?? [];
    }

    public function builder(): Builder
    {
        return new Builder(scheme: $this);
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\UrlPattern;

use TypeError;

use function array_key_exists;
use function count;
use function in_array;
use function min;
use function preg_match;
use function strtolower;
use function substr;

final class Parser
{
    private const SPECIAL_SCHEMES = ['ftp', 'file', 'http', 'https', 'ws', 'wss'];

    /** @var list<Token> */
    private array $tokenList = [];

    private int $tokenIndex = 0;

    private int $tokenIncrement = 1;

    private int $componentStart = 0;

    private State $state = State::Init;

    private int $groupDepth = 0;

    private int $hostnameIPv6BracketDepth = 0;

    private bool $shouldTreatAsStandardUrl = false;

    /** @var array<string, string> */
    private array $components = [];

    public function __construct(private string $input)
    {
        $this->parse();
    }

    public function components(): array
    {
        return $this->components;
    }

    public function hasRegexpGroup(): bool
    {
        //@todo
        return false;
    }

    private function parse(): array
    {
        $this->tokenList = PathToRegexp::lex($this->input, lenient: true);

        for (; $this->tokenIndex < count($this->tokenList); $this->tokenIndex += $this->tokenIncrement) {
            $this->tokenIncrement = 1;

            if (TokenType::End === $this->currentToken()->type) {
                if (State::Init === $this->state) {
                    $this->rewind();
                    if ($this->isHashPrefix()) {
                        $this->changeState(State::Fragment, 1);
                    } elseif ($this->isSearchPrefix()) {
                        $this->changeState(State::Query, 1);
                    } else {
                        $this->changeState(State::Path, 0);
                    }

                    continue;
                }

                if (State::Authority === $this->state) {
                    $this->rewindAndSetState(State::Host);

                    continue;
                }

                $this->changeState(State::Done, 0);

                break;
            }

            if ($this->groupDepth > 0) {
                if ($this->isGroupClose()) {
                    --$this->groupDepth;
                } else {
                    continue;
                }
            }

            if ($this->isGroupOpen()) {
                ++$this->groupDepth;

                continue;
            }

            switch ($this->state) {
                case State::Init:
                    if ($this->isProtocolSuffix()) {
                        $this->rewindAndSetState(State::Scheme);
                    }

                    break;

                case State::Scheme:
                    if ($this->isProtocolSuffix()) {
                        $this->computeShouldTreatAsStandardUrl();

                        $nextState = State::Path;
                        $skip = 1;

                        if ($this->nextIsAuthoritySlashes()) {
                            $nextState = State::Authority;
                            $skip = 3;
                        } elseif ($this->shouldTreatAsStandardUrl) {
                            $nextState = State::Authority;
                        }

                        $this->changeState($nextState, $skip);
                    }

                    break;

                case State::Authority:
                    if ($this->isIdentityTerminator()) {
                        $this->rewindAndSetState(State::Username);
                    } elseif (
                        $this->isPathnameStart()
                        || $this->isSearchPrefix()
                        || $this->isHashPrefix()
                    ) {
                        $this->rewindAndSetState(State::Host);
                    }

                    break;

                case State::Username:
                    if ($this->isPasswordPrefix()) {
                        $this->changeState(State::Password, 1);
                    } elseif ($this->isIdentityTerminator()) {
                        $this->changeState(State::Host, 1);
                    }

                    break;

                case State::Password:
                    if ($this->isIdentityTerminator()) {
                        $this->changeState(State::Host, 1);
                    }

                    break;

                case State::Host:
                    if ($this->isIPv6Open()) {
                        ++$this->hostnameIPv6BracketDepth;
                    } elseif ($this->isIPv6Close()) {
                        --$this->hostnameIPv6BracketDepth;
                    }

                    if (
                        $this->isPortPrefix()
                        && 0 === $this->hostnameIPv6BracketDepth
                    ) {
                        $this->changeState(State::Port, 1);
                    } elseif ($this->isPathnameStart()) {
                        $this->changeState(State::Path, 0);
                    } elseif ($this->isSearchPrefix()) {
                        $this->changeState(State::Query, 1);
                    } elseif ($this->isHashPrefix()) {
                        $this->changeState(State::Fragment, 1);
                    }

                    break;

                case State::Port:
                    if ($this->isPathnameStart()) {
                        $this->changeState(State::Path, 0);
                    } elseif ($this->isSearchPrefix()) {
                        $this->changeState(State::Query, 1);
                    } elseif ($this->isHashPrefix()) {
                        $this->changeState(State::Fragment, 1);
                    }

                    break;

                case State::Path:
                    if ($this->isSearchPrefix()) {
                        $this->changeState(State::Query, 1);
                    } elseif ($this->isHashPrefix()) {
                        $this->changeState(State::Fragment, 1);
                    }

                    break;

                case State::Query:
                    if ($this->isHashPrefix()) {
                        $this->changeState(State::Fragment, 1);
                    }

                    break;

                case State::Fragment:
                case State::Done:
                    break;
            }
        }

        // If hostname was specified but port wasn't, the parser produces
        // an empty port component.
        if ($this->hasComponent(State::Host->value) && ! $this->hasComponent(State::Port->value)) {
            $this->components[State::Port->value] = '';
        }

        return $this->components;
    }

    private function changeState(State $newState, int $skip): void
    {
        match ($this->state) {
            State::Init, State::Authority, State::Done => null,
            default => $this->setComponent($this->state),
        };

        if (State::Init !== $this->state && State::Done !== $newState) {
            if (
                in_array($this->state, [State::Scheme, State::Authority, State::Username, State::Password], true)
                && in_array($newState, [State::Port, State::Path, State::Query, State::Fragment], true)
            ) {
                $this->components[State::Host->value] ??= '';
            }

            if (
                in_array($this->state, [State::Scheme, State::Authority, State::Username, State::Password, State::Host, State::Port], true)
                && in_array($newState, [State::Query, State::Fragment], true)
            ) {
                $this->components[State::Path->value] ??= $this->shouldTreatAsStandardUrl ? '/' : '';
            }

            if (
                in_array($this->state, [State::Scheme, State::Authority, State::Username, State::Password, State::Host, State::Port, State::Path], true)
                && State::Fragment === $newState
            ) {
                $this->components[State::Query->value] ??= '';
            }
        }

        $this->changeStateWithoutSettingComponent($newState, $skip);
    }

    private function changeStateWithoutSettingComponent(
        State $newState,
        int $skip,
    ): void {
        $this->state = $newState;
        $this->componentStart = $this->tokenIndex + $skip;
        $this->tokenIndex += $skip;
        $this->tokenIncrement = 0;
    }

    private function rewind(): void
    {
        $this->tokenIndex = $this->componentStart;
        $this->tokenIncrement = 0;
    }

    private function rewindAndSetState(State $newState): void
    {
        $this->rewind();
        $this->state = $newState;
    }

    private function currentToken(): Token
    {
        return $this->safeToken($this->tokenIndex);
    }

    private function safeToken(int $index): Token
    {
        if ($index < 0) {
            $index = count($this->tokenList) + $index;
        }

        return $this->tokenList[
        min($index, count($this->tokenList) - 1)
        ];
    }

    private function isNonSpecialPatternChar(
        int $index,
        string $value,
    ): bool {
        $token = $this->safeToken($index);

        return $token->value === $value
            && in_array($token->type, [TokenType::Char, TokenType::EscapedChar, TokenType::InvalidChar], true);
    }

    private function isProtocolSuffix(): bool
    {
        return $this->isNonSpecialPatternChar($this->tokenIndex, ':');
    }

    private function nextIsAuthoritySlashes(): bool
    {
        return $this->isNonSpecialPatternChar($this->tokenIndex + 1, '/')
            && $this->isNonSpecialPatternChar($this->tokenIndex + 2, '/');
    }

    private function isIdentityTerminator(): bool
    {
        return $this->isNonSpecialPatternChar($this->tokenIndex, '@');
    }

    private function isPasswordPrefix(): bool
    {
        return $this->isNonSpecialPatternChar($this->tokenIndex, ':');
    }

    private function isPortPrefix(): bool
    {
        return $this->isNonSpecialPatternChar($this->tokenIndex, ':');
    }

    private function isPathnameStart(): bool
    {
        return $this->isNonSpecialPatternChar($this->tokenIndex, '/');
    }

    private function isSearchPrefix(): bool
    {
        if ($this->isNonSpecialPatternChar($this->tokenIndex, '?')) {
            return true;
        }

        if ('?' !== $this->currentToken()->value) {
            return false;
        }

        $previousToken = $this->safeToken($this->tokenIndex - 1);

        return ! in_array($previousToken->type, [
            TokenType::Name,
            TokenType::Regex,
            TokenType::Close,
            TokenType::Asterisk,
        ], true);
    }

    private function isHashPrefix(): bool
    {
        return $this->isNonSpecialPatternChar($this->tokenIndex, '#');
    }

    private function isGroupOpen(): bool
    {
        return TokenType::Open === $this->currentToken()->type;
    }

    private function isGroupClose(): bool
    {
        return TokenType::Close === $this->currentToken()->type;
    }

    private function isIPv6Open(): bool
    {
        return $this->isNonSpecialPatternChar($this->tokenIndex, '[');
    }

    private function isIPv6Close(): bool
    {
        return $this->isNonSpecialPatternChar($this->tokenIndex, ']');
    }

    private function setComponent(State $name): void
    {
        $this->components[$name->value] = Component::fromPattern($this->makeComponentString())->pattern;
    }

    private function makeComponentString(): string
    {
        $token = $this->currentToken();
        $componentCharStart = $this->safeToken($this->componentStart)->index;

        return substr(
            $this->input,
            $componentCharStart,
            $token->index - $componentCharStart,
        );
    }

    private function hasComponent(string $name): bool
    {
        return array_key_exists($name, $this->components);
    }

    private function computeShouldTreatAsStandardUrl(): void
    {
        $regexp = PathToRegexp::stringToRegexp(
            $this->makeComponentString(),
            options: [
                'delimiter' => '',
                'prefixes' => '',
                'sensitive' => true,
                'strict' => true,
                'encodePart' => self::protocolEncodeCallback(...),
            ],
        );

        $this->shouldTreatAsStandardUrl = self::isSpecialScheme($regexp);
    }

    private static function isSpecialScheme(string $regexp): bool
    {
        foreach (self::SPECIAL_SCHEMES as $scheme) {
            if (1 === preg_match('~'.$regexp.'~', $scheme)) {
                return true;
            }
        }

        return false;
    }

    private static function protocolEncodeCallback(string $input): string
    {
        return match (true) {
            '' === $input => '',
            1 === preg_match('/^[-+.A-Za-z0-9]*$/', $input) => strtolower($input),
            default => throw new TypeError("Invalid protocol '{$input}'."),
        };
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\UrlPattern;

enum ComponentName: string
{
    case Scheme = 'scheme';
    case Username = 'username';
    case Password = 'password';
    case Host = 'host';
    case Port = 'port';
    case Path = 'path';
    case Query = 'query';
    case Fragment = 'fragment';
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\UrlPattern;

use ArrayAccess;
use Countable;
use DateTimeImmutable;
use DateTimeInterface;
use DateTimeZone;
use Exception;
use League\Uri\Encoder;
use League\Uri\HostRecord;
use League\Uri\TypeConverter;
use LogicException;
use TypeError;
use UnitEnum;
use ValueError;

use function array_key_exists;
use function count;
use function is_int;
use function is_string;
use function preg_match;

/**
 * @implements ArrayAccess<array-key, string|null>
 */
final class ComponentResult implements ArrayAccess, Countable
{
    /**
     * @param array<array-key, string|null> $groups
     */
    private function __construct(
        public readonly string $input,
        private readonly array $groups
    ) {
    }

    public static function extract(
        string $input,
        Component $component,
        ComponentName $componentName,
        MatchMode $matchMode,
    ): ?self {
        $modifier = MatchMode::CaseInsensitive === $matchMode ? 'i' : '';
        $regexp = '~'.$component->regexp.'~'.$modifier;
        if (1 !== preg_match($regexp, $input, $matches)) {
            return null;
        }

        $data = [];
        $matchIndex = 1;
        foreach ($component->parts as $part) {
            if (PartType::Fixed === $part->type) {
                continue;
            }

            $content = $matches[$matchIndex++] ?? null;
            $data[$part->name] = ComponentName::Host === $componentName
                ? HostRecord::from($content)->toUnicode()
                : Encoder::decodeAll($content);
        }

        return new self($input, $data);
    }

    public static function empty(): self
    {
        return new self('', ['']);
    }

    public function count(): int
    {
        return count($this->groups);
    }

    public function hasValue(): bool
    {
        return [''] !== $this->groups && [] !== $this->groups;
    }

    /**
     * @return list<string>
     */
    public function names(): array
    {
        return array_keys($this->groups);
    }

    /**
     * @return array<array-key, string|null>
     */
    public function variables(): array
    {
        return $this->groups;
    }

    public function offsetGet(mixed $offset): null|string
    {
        return is_string($offset) || is_int($offset)
            ? ($this->groups[$offset] ?? null)
            : throw new TypeError('invalid offset type, only string or integer are allowed.');
    }

    public function offsetExists(mixed $offset): bool
    {
        return (is_string($offset) || is_int($offset))
            && array_key_exists($offset, $this->groups);
    }

    public function offsetUnset(mixed $offset): never
    {
        throw new LogicException(self::class.' is read-only.');
    }

    public function offsetSet(mixed $offset, mixed $value): never
    {
        throw new LogicException(self::class.' is read-only.');
    }

    public function string(int|string $name, ?string $default = null): ?string
    {
        return TypeConverter::toString($this->groups[$name] ?? null) ?? $default;
    }

    public function integer(int|string $name, ?int $default = null): ?int
    {
        return TypeConverter::toInteger($this->groups[$name] ?? null) ?? $default;
    }

    public function float(int|string $name, ?float $default = null): ?float
    {
        return TypeConverter::toFloat($this->groups[$name] ?? null) ?? $default;
    }

    public function boolean(int|string $name, ?bool $default = null): ?bool
    {
        return TypeConverter::toBoolean($this->groups[$name] ?? null) ?? $default;
    }

    /**
     * @param class-string<UnitEnum> $enumClass
     */
    public function enum(int|string $name, string $enumClass, ?UnitEnum $default = null): ?UnitEnum
    {
        null === $default || $default instanceof $enumClass || throw new ValueError('The default value must be an instance of '.$enumClass.'; '.get_debug_type($default).' given.');

        return TypeConverter::toEnum($this->groups[$name] ?? null, $enumClass) ?? $default;
    }

    /**
     * @param non-empty-string $format
     *
     * @throws Exception
     */
    public function date(
        int|string $name,
        string $format,
        DateTimeZone|string $timezone = 'UTC',
        ?DateTimeInterface $default = null
    ): ?DateTimeImmutable {

        return TypeConverter::toDateTimeImmutable($this->groups[$name] ?? null, $format, $timezone) ?? (
            null !== $default && !$default instanceof DateTimeImmutable
            ? DateTimeImmutable::createFromInterface($default)
            : $default
        );
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\UrlPattern;

final class Part
{
    public function __construct(
        public readonly PartType $type,
        public readonly string|int $name,
        public readonly string $prefix,
        public readonly string $value,
        public readonly string $suffix,
        public readonly Modifier $modifier,
    ) {
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\UrlPattern;

final class Token
{
    public function __construct(
        public readonly TokenType $type,
        public readonly string $value,
        public readonly int $index,
    ) {
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\UrlPattern;

enum PartType
{
    case FullWildcard;
    case SegmentWildcard;
    case Regex;
    case Fixed;

    public function hasVariable(): bool
    {
        return self::Fixed !== $this;
    }

    public function hasRegexpGroup(): bool
    {
        return self::Regex === $this;
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\UrlPattern;

enum Modifier: string
{
    case ZeroOrMore = '*';
    case Optional = '?';
    case OneOrMore = '+';
    case None = '';
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\UrlPattern;

enum MatchMode
{
    case CaseSensitive;
    case CaseInsensitive;
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\UrlPattern;

use BackedEnum;
use Stringable;
use Uri\Rfc3986\Uri as Rfc3986Uri;
use Uri\WhatWg\Url as WhatWgUrl;

final class Result
{
    private readonly bool $hasValue;

    /**
     * @param list<string> $inputs
     */
    private function __construct(
        public readonly ComponentResult $scheme,
        public readonly ComponentResult $username,
        public readonly ComponentResult $password,
        public readonly ComponentResult $host,
        public readonly ComponentResult $port,
        public readonly ComponentResult $path,
        public readonly ComponentResult $query,
        public readonly ComponentResult $fragment,
        public readonly array $inputs,
    ) {
        $this->hasValue = $this->scheme->hasValue()
            || $this->username->hasValue()
            || $this->password->hasValue()
            || $this->host->hasValue()
            || $this->port->hasValue()
            || $this->path->hasValue()
            || $this->query->hasValue()
            || $this->fragment->hasValue();
    }

    /**
     * Tells whether the Resul contain any variable.
     */
    public function hasValue(): bool
    {
        return $this->hasValue;
    }

    /**
     * @param array<non-empty-string, ComponentResult> $extraction
     */
    public static function tryFrom(array $extraction, Rfc3986Uri|WhatWgUrl|BackedEnum|Stringable|string $input): ?self
    {
        foreach ($extraction as $name => $value) {
            if (!$value instanceof ComponentResult || null === ComponentName::tryFrom($name)) {
                return null;
            }
        }

        $empty = ComponentResult::empty();

        return new self(
            scheme: $extraction[ComponentName::Scheme->value] ?? $empty,
            username: $extraction[ComponentName::Username->value] ?? $empty,
            password: $extraction[ComponentName::Password->value] ?? $empty,
            host: $extraction[ComponentName::Host->value] ?? $empty,
            port: $extraction[ComponentName::Port->value] ?? $empty,
            path: $extraction[ComponentName::Path->value] ?? $empty,
            query: $extraction[ComponentName::Query->value] ?? $empty,
            fragment: $extraction[ComponentName::Fragment->value] ?? $empty,
            inputs: [match (true) {
                $input instanceof Rfc3986Uri => $input->toString(),
                $input instanceof WhatWgUrl => $input->toAsciiString(),
                $input instanceof BackedEnum => (string) $input->value,
                default => (string) $input,
            }],
        );
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\UrlPattern;

enum State: string
{
    case Init = 'init';
    case Scheme = 'scheme';
    case Authority = 'authority';
    case Username = 'user';
    case Password = 'pass';
    case Host = 'host';
    case Port = 'port';
    case Path = 'path';
    case Query = 'query';
    case Fragment = 'fragment';
    case Done = 'done';
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\UrlPattern;

final class Component
{
    public readonly bool $hasRegexpGroup;
    public readonly bool $hasVariable;

    /**
     * @param list<Part> $parts
     */
    private function __construct(
        public readonly string $pattern,
        public readonly array $parts,
        public readonly string $regexp,
    ) {
        $hasRegexpGroup = false;
        $hasVariable = false;
        foreach ($this->parts as $part) {
            $hasRegexpGroup = $hasRegexpGroup || $part->type->hasRegexpGroup();
            $hasVariable = $hasVariable || $part->type->hasVariable();
        }

        $this->hasRegexpGroup = $hasRegexpGroup;
        $this->hasVariable = $hasVariable;
    }

    public static function fromAsterisk(): self
    {
        static $instance;
        $instance ??= self::fromPattern('*');

        return $instance;
    }

    public static function fromEmpty(): self
    {
        static $instance;
        $instance ??= self::fromPattern('');

        return $instance;
    }

    public static function fromPattern(string $pattern): self
    {
        return new self($pattern, PathToRegexp::parse($pattern), PathToRegexp::stringToRegexp($pattern));
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\UrlPattern;

use League\Uri\Exceptions\SyntaxError;

use function count;
use function ord;
use function preg_match;
use function preg_replace;
use function sprintf;
use function strlen;

final class PathToRegexp
{
    /**
     * @return list<Token>
     */
    public static function lex(string $input, bool $lenient = false): array
    {
        $tokens = [];
        $length = strlen($input);
        $i = 0;

        $errorOrInvalid = static function (string $message) use (&$tokens, &$i, $input, $lenient): void {
            $lenient || throw new SyntaxError($message);
            $tokens[] = new Token(TokenType::InvalidChar, $input[$i], $i++);
        };

        while ($i < $length) {
            $char = $input[$i];

            if ('*' === $char) {
                $tokens[] = new Token(TokenType::Asterisk, $input[$i], $i++);
                continue;
            }

            if ('+' === $char || '?' === $char) {
                $tokens[] = new Token(TokenType::OtherModifier, $input[$i], $i++);
                continue;
            }

            if ('\\' === $char) {
                $tokens[] = new Token(TokenType::EscapedChar, $input[$i + 1] ?? '', $i++);
                ++$i;
                continue;
            }

            if ('{' === $char) {
                $tokens[] = new Token(TokenType::Open, $input[$i], $i++);
                continue;
            }

            if ('}' === $char) {
                $tokens[] = new Token(TokenType::Close, $input[$i], $i++);
                continue;
            }

            if (':' === $char) {
                $name = '';
                $j = $i + 1;

                while ($j < $length) {
                    $code = $input[$j];

                    if (
                        ($j === $i + 1 && self::isIdentifierStart($code))
                        || ($j !== $i + 1 && self::isIdentifierPart($code))
                    ) {
                        $name .= $code;
                        ++$j;
                        continue;
                    }

                    break;
                }

                if ('' === $name) {
                    $errorOrInvalid("Missing parameter name at {$i}");
                    continue;
                }

                $tokens[] = new Token(TokenType::Name, $name, $i);
                $i = $j;

                continue;
            }

            if ('(' === $char) {
                $count = 1;
                $pattern = '';
                $j = $i + 1;
                $error = false;

                if ($j < $length && '?' === $input[$j]) {
                    $errorOrInvalid('Pattern cannot start with "?" at '.$j);
                    continue;
                }

                while ($j < $length) {
                    $char = $input[$j];

                    if (!self::isAscii($char)) {
                        $errorOrInvalid("Invalid character '{$char}' at {$j}.");
                        $error = true;
                        break;
                    }

                    if ('\\' === $char) {
                        $pattern .= $input[$j++];
                        if ($j < $length) {
                            $pattern .= $input[$j++];
                        }
                        continue;
                    }

                    if (')' === $char) {
                        --$count;

                        if (0 === $count) {
                            ++$j;
                            break;
                        }
                    } elseif ('(' === $char) {
                        ++$count;

                        if ($j + 1 < $length && '?' !== $input[$j + 1]) {
                            $errorOrInvalid("Capturing groups are not allowed at {$j}");
                            $error = true;
                            break;
                        }
                    }

                    $pattern .= $input[$j++];
                }

                if ($error) {
                    continue;
                }

                if ($count > 0) {
                    $errorOrInvalid("Unbalanced pattern at {$i}");
                    continue;
                }

                if ('' === $pattern) {
                    $errorOrInvalid("Missing pattern at {$i}");
                    continue;
                }

                $tokens[] = new Token(TokenType::Regex, $pattern, $i);
                $i = $j;

                continue;
            }

            $tokens[] = new Token(TokenType::Char, $input[$i], $i++);
        }

        $tokens[] = new Token(TokenType::End, '', $i);

        return $tokens;
    }
    /**
     * @return list<Part>
     */
    public static function parse(
        string $input,
        string $delimiter = '/#?',
        string $prefixes = './',
    ): array {
        $tokens = self::lex($input);

        $segmentWildcardRegex = '[^'.self::escapeString($delimiter).']+?';

        $result = [];
        $key = 0;
        $index = 0;
        $pendingFixedValue = '';
        $names = [];

        $tryConsume = static function (TokenType $type) use (&$tokens, &$index): ?string {
            if ($index < count($tokens) && $tokens[$index]->type === $type) {
                return $tokens[$index++]->value;
            }

            return null;
        };

        $tryConsumeModifier = static function () use (&$tryConsume): ?string {
            return $tryConsume(TokenType::OtherModifier)
                ?? $tryConsume(TokenType::Asterisk);
        };

        $mustConsume = static function (TokenType $type) use (
            &$tokens,
            &$index,
            &$tryConsume,
        ): string {
            $value = $tryConsume($type);

            if (null !== $value) {
                return $value;
            }

            $token = $tokens[$index];

            throw new SyntaxError(
                sprintf(
                    'Unexpected %s at %d, expected %s',
                    $token->type->name,
                    $token->index,
                    $type->name,
                ),
            );
        };

        $consumeText = static function () use (&$tryConsume): string {
            $result = '';

            while (null !== ($value = $tryConsume(TokenType::Char))
                || null !== ($value = $tryConsume(TokenType::EscapedChar))
            ) {
                $result .= $value;
            }

            return $result;
        };

        $maybeAddPartFromPendingFixedValue = static function () use (
            &$pendingFixedValue,
            &$result,
        ): void {
            /* @phpstan-ignore-next-line */
            if ('' !== $pendingFixedValue) {
                $result[] = new Part(PartType::Fixed, '', '', $pendingFixedValue, '', Modifier::None);
                $pendingFixedValue = '';
            }
        };

        $addPart = static function (
            string $prefix,
            ?string $nameToken,
            ?string $regexOrWildcardToken,
            string $suffix,
            ?string $modifierToken,
        ) use (
            &$result,
            &$key,
            &$names,
            &$pendingFixedValue,
            &$maybeAddPartFromPendingFixedValue,
            $segmentWildcardRegex,
        ): void {
            $modifier = match ($modifierToken) {
                '?' => Modifier::Optional,
                '*' => Modifier::ZeroOrMore,
                '+' => Modifier::OneOrMore,
                default => Modifier::None,
            };

            if (null === $nameToken && null === $regexOrWildcardToken && Modifier::None === $modifier) {
                $pendingFixedValue .= $prefix;

                return;
            }

            $maybeAddPartFromPendingFixedValue();

            if (null === $nameToken && null === $regexOrWildcardToken) {
                if ('' === $prefix) {
                    return;
                }

                $result[] = new Part(PartType::Fixed, '', '', $prefix, '', $modifier);

                return;
            }

            $regexValue = match (true) {
                null === $regexOrWildcardToken => $segmentWildcardRegex,
                '*' === $regexOrWildcardToken => '.*',
                default => $regexOrWildcardToken,
            };

            $type = match ($regexValue) {
                $segmentWildcardRegex => PartType::SegmentWildcard,
                '.*' => PartType::FullWildcard,
                default => PartType::Regex,
            };

            if (PartType::Regex !== $type) {
                $regexValue = '';
            }

            $name = $nameToken;

            if (null === $name && null !== $regexOrWildcardToken) {
                $name = $key++;
            }

            !isset($names[$name]) || throw new SyntaxError(sprintf("Duplicate name '%s'.", $name));

            $names[$name] = true;
            $result[] = new Part($type, (string) $name, $prefix, $regexValue, $suffix, $modifier);
        };

        while ($index < count($tokens)) {
            $charToken = $tryConsume(TokenType::Char);
            $nameToken = $tryConsume(TokenType::Name);
            $regexOrWildcardToken = $tryConsume(TokenType::Regex);

            if (null === $nameToken && null === $regexOrWildcardToken) {
                $regexOrWildcardToken = $tryConsume(TokenType::Asterisk);
            }

            if (null !== $nameToken || null !== $regexOrWildcardToken) {
                $prefix = $charToken ?? '';
                if (! str_contains($prefixes, $prefix)) {
                    $pendingFixedValue .= $prefix;
                    $prefix = '';
                }

                $maybeAddPartFromPendingFixedValue();
                $modifierToken = $tryConsumeModifier();
                $addPart($prefix, $nameToken, $regexOrWildcardToken, '', $modifierToken);

                continue;
            }

            $value = $charToken ?? $tryConsume(TokenType::EscapedChar);
            if (null !== $value) {
                $pendingFixedValue .= $value;

                continue;
            }

            $openToken = $tryConsume(TokenType::Open);

            if (null !== $openToken) {
                $prefix = $consumeText();
                $nameToken = $tryConsume(TokenType::Name);
                $regexOrWildcardToken = $tryConsume(TokenType::Regex);
                if (null === $nameToken && null === $regexOrWildcardToken) {
                    $regexOrWildcardToken = $tryConsume(TokenType::Asterisk);
                }

                $suffix = $consumeText();
                $mustConsume(TokenType::Close);
                $modifierToken = $tryConsumeModifier();
                $addPart($prefix, $nameToken, $regexOrWildcardToken, $suffix, $modifierToken);
                continue;
            }

            $maybeAddPartFromPendingFixedValue();
            $mustConsume(TokenType::End);
        }

        return $result;
    }

    /**
     * @param list<Part> $parts
     * @param list<string|int>|null $names
     */
    public static function partsToRegexp(
        array $parts,
        ?array &$names = null,
        array $options = [],
    ): string {
        $delimiter = $options['delimiter'] ?? '/#?';
        $strict = $options['strict'] ?? false;
        $end = $options['end'] ?? true;
        $start = $options['start'] ?? true;

        $endsWith = '';

        $result = $start ? '^' : '';

        foreach ($parts as $part) {
            if (PartType::Fixed === $part->type) {
                if (Modifier::None === $part->modifier) {
                    $result .= self::escapeString($part->value);
                } else {
                    $result .= '(?:'
                        .self::escapeString($part->value)
                        .')'
                        .$part->modifier->value;
                }

                continue;
            }

            if (null !== $names) {
                $names[] = $part->name;
            }

            $segmentWildcardRegex = '[^'
                .self::escapeString($delimiter)
                .']+?';

            $regexValue = match ($part->type) {
                PartType::SegmentWildcard => $segmentWildcardRegex,
                PartType::FullWildcard => '.*',
                PartType::Regex => $part->value,
            };

            if ('' === $part->prefix && '' === $part->suffix) {
                if (
                    Modifier::None === $part->modifier
                    || Modifier::Optional === $part->modifier
                ) {
                    $result .= '('
                        .$regexValue
                        .')'
                        .$part->modifier->value;
                } else {
                    $result .= '((?:'
                        .$regexValue
                        .')'
                        .$part->modifier->value
                        .')';
                }

                continue;
            }

            if (
                Modifier::None === $part->modifier
                || Modifier::Optional === $part->modifier
            ) {
                $result .= '(?:'
                    .self::escapeString($part->prefix)
                    .'('
                    .$regexValue
                    .')'
                    .self::escapeString($part->suffix)
                    .')'
                    .$part->modifier->value;

                continue;
            }

            $result .= '(?:'
                .self::escapeString($part->prefix)
                .'((?:'
                .$regexValue
                .')(?:'
                .self::escapeString($part->suffix)
                .self::escapeString($part->prefix)
                .'(?:'
                .$regexValue
                .'))*)'
                .self::escapeString($part->suffix)
                .')';

            if (Modifier::ZeroOrMore === $part->modifier) {
                $result .= '?';
            }
        }

        $endsWithRegex = '['.self::escapeString($endsWith).']|$';
        $delimiterRegex = '['.self::escapeString($delimiter).']';

        if ($end) {
            if (! $strict) {
                $result .= $delimiterRegex.'?';
            }

            /* @phpstan-ignore-next-line  */
            return '' === $endsWith ? $result.'$' : $result.'(?='.$endsWithRegex.')';
        }

        if (! $strict) {
            $result .= '(?:'
                .$delimiterRegex
                .'(?='
                .$endsWithRegex
                .'))?';
        }

        $isEndDelimited = false;

        if ([] !== $parts) {
            $lastPart = $parts[array_key_last($parts)];

            if (
                PartType::Fixed === $lastPart->type
                && Modifier::None === $lastPart->modifier
            ) {
                $isEndDelimited = str_contains(
                    $delimiter,
                    $lastPart->value,
                );
            }
        }

        if (! $isEndDelimited) {
            $result .= '(?='
                .$delimiterRegex
                .'|'
                .$endsWithRegex
                .')';
        }

        return $result;
    }

    /**
     * @param list<array-key>|null $names
     */
    public static function stringToRegexp(
        string $path,
        ?array &$names = null,
        array $options = [],
    ): string {
        return self::partsToRegexp(self::parse($path), $names, $options);
    }

    private static function isAscii(string $char): bool
    {
        return ord($char) <= 0x7f;
    }

    private static function isIdentifierStart(string $char): bool
    {
        return 1 === preg_match('/^[\p{L}_$]$/u', $char);
    }

    private static function isIdentifierPart(string $char): bool
    {
        return 1 === preg_match('/^[\p{L}\p{N}_$]$/u', $char);
    }

    private static function escapeString(string $value): string
    {
        return (string) preg_replace('/([.+*?^${}()[\]|\/\\\\])/', '\\\\$1', $value);
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\UrlPattern;

enum TokenType
{
    case Open;
    case Close;
    case Regex;
    case Name;
    case Char;
    case EscapedChar;
    case OtherModifier;
    case InvalidChar;
    case Asterisk;
    case End;
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\UrlPattern;

enum PatternType
{
    case Absolute;
    case Relative;
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri;

use Deprecated;
use JsonSerializable;
use League\Uri\Contracts\Conditionable;
use League\Uri\Contracts\Transformable;
use League\Uri\Contracts\UriException;
use League\Uri\Contracts\UriInterface;
use League\Uri\Exceptions\SyntaxError;
use League\Uri\UriTemplate\TemplateCanNotBeExpanded;
use Psr\Http\Message\UriInterface as Psr7UriInterface;
use Stringable;
use Uri\Rfc3986\Uri as Rfc3986Uri;
use Uri\WhatWg\Url as WhatWgUrl;

use function is_bool;
use function ltrim;

/**
 * @phpstan-import-type InputComponentMap from UriString
 */
final class Http implements Stringable, Psr7UriInterface, JsonSerializable, Conditionable, Transformable
{
    private readonly UriInterface $uri;

    private function __construct(UriInterface $uri)
    {
        if (null === $uri->getScheme() && '' === $uri->getHost()) {
            throw new SyntaxError('An URI without scheme cannot contain an empty host string according to PSR-7: '.$uri);
        }

        $port = $uri->getPort();
        if (null !== $port && ($port < 0 || $port > 65535)) {
            throw new SyntaxError('The URI port is outside the established TCP and UDP port ranges: '.$uri);
        }

        $this->uri = $this->normalizePsr7Uri($uri);
    }

    /**
     * PSR-7 UriInterface makes the following normalization.
     *
     * Safely stringify input when possible for League UriInterface compatibility.
     *
     * Query, Fragment and User Info when undefined are normalized to the empty string
     */
    private function normalizePsr7Uri(UriInterface $uri): UriInterface
    {
        $components = [];
        if ('' === $uri->getFragment()) {
            $components['fragment'] = null;
        }

        if ('' === $uri->getQuery()) {
            $components['query'] = null;
        }

        if ('' === $uri->getUserInfo()) {
            $components['user'] = null;
            $components['pass'] = null;
        }

        return match ($components) {
            [] => $uri,
            default => Uri::fromComponents([...$uri->toComponents(), ...$components]),
        };
    }

    /**
     * Create a new instance from a string or a stringable object.
     */
    public static function new(Rfc3986Uri|WhatwgUrl|Stringable|string $uri = ''): self
    {
        return new self(Uri::new($uri));
    }

    /**
     * Create a new instance from a string or a stringable structure or returns null on failure.
     */
    public static function tryNew(Rfc3986Uri|WhatwgUrl|Stringable|string $uri = ''): ?self
    {
        try {
            return self::new($uri);
        } catch (UriException) {
            return null;
        }
    }

    /**
     * Create a new instance from a hash of parse_url parts.
     *
     * @param InputComponentMap $components a hash representation of the URI similar
     *                                      to PHP parse_url function result
     */
    public static function fromComponents(array $components): self
    {
        $components += [
            'scheme' => null, 'user' => null, 'pass' => null, 'host' => null,
            'port' => null, 'path' => '', 'query' => null, 'fragment' => null,
        ];

        if ('' === $components['user']) {
            $components['user'] = null;
        }

        if ('' === $components['pass']) {
            $components['pass'] = null;
        }

        if ('' === $components['query']) {
            $components['query'] = null;
        }

        if ('' === $components['fragment']) {
            $components['fragment'] = null;
        }

        return new self(Uri::fromComponents($components));
    }

    /**
     * Create a new instance from the environment.
     */
    public static function fromServer(array $server): self
    {
        return new self(Uri::fromServer($server));
    }

    /**
     * Creates a new instance from a template.
     *
     * @throws TemplateCanNotBeExpanded if the variables are invalid or missing
     * @throws UriException if the variables are invalid or missing
     */
    public static function fromTemplate(Stringable|string $template, iterable $variables = []): self
    {
        return new self(Uri::fromTemplate($template, $variables));
    }

    /**
     * Returns a new instance from a URI and a Base URI.or null on failure.
     *
     * The returned URI must be absolute if a base URI is provided
     */
    public static function parse(WhatWgUrl|Rfc3986Uri|Stringable|string $uri, WhatWgUrl|Rfc3986Uri|Stringable|string|null $baseUri = null): ?self
    {
        return null !== ($uri = Uri::parse($uri, $baseUri)) ? new self($uri) : null;
    }

    public function getScheme(): string
    {
        return $this->uri->getScheme() ?? '';
    }

    public function getAuthority(): string
    {
        return $this->uri->getAuthority() ?? '';
    }

    public function getUserInfo(): string
    {
        return $this->uri->getUserInfo() ?? '';
    }

    public function getHost(): string
    {
        return $this->uri->getHost() ?? '';
    }

    public function getPort(): ?int
    {
        return $this->uri->getPort();
    }

    public function getPath(): string
    {
        $path = $this->uri->getPath();

        return match (true) {
            str_starts_with($path, '//') => '/'.ltrim($path, '/'),
            default => $path,
        };
    }

    public function getQuery(): string
    {
        return $this->uri->getQuery() ?? '';
    }

    public function getFragment(): string
    {
        return $this->uri->getFragment() ?? '';
    }

    public function __toString(): string
    {
        return $this->uri->toString();
    }

    public function jsonSerialize(): string
    {
        return $this->uri->toString();
    }

    /**
     * Safely stringify input when possible for League UriInterface compatibility.
     */
    private function filterInput(string $str): ?string
    {
        return match ('') {
            $str => null,
            default => $str,
        };
    }

    private function newInstance(UriInterface $uri): self
    {
        return match ($this->uri->toString()) {
            $uri->toString() => $this,
            default => new self($uri),
        };
    }

    public function when(callable|bool $condition, callable $onSuccess, ?callable $onFail = null): static
    {
        if (!is_bool($condition)) {
            $condition = $condition($this);
        }

        return match (true) {
            $condition => $onSuccess($this),
            null !== $onFail => $onFail($this),
            default => $this,
        } ?? $this;
    }

    public function transform(callable $callback): static
    {
        return $callback($this);
    }

    public function withScheme(string $scheme): self
    {
        return $this->newInstance($this->uri->withScheme($this->filterInput($scheme)));
    }

    public function withUserInfo(string $user, ?string $password = null): self
    {
        return $this->newInstance($this->uri->withUserInfo($this->filterInput($user), $password));
    }

    public function withHost(string $host): self
    {
        return $this->newInstance($this->uri->withHost($this->filterInput($host)));
    }

    public function withPort(?int $port): self
    {
        return $this->newInstance($this->uri->withPort($port));
    }

    public function withPath(string $path): self
    {
        return $this->newInstance($this->uri->withPath($path));
    }

    public function withQuery(string $query): self
    {
        return $this->newInstance($this->uri->withQuery($this->filterInput($query)));
    }

    public function withFragment(string $fragment): self
    {
        return $this->newInstance($this->uri->withFragment($this->filterInput($fragment)));
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.6.0
     * @codeCoverageIgnore
     * @see Http::parse()
     *
     * Create a new instance from a URI and a Base URI.
     *
     * The returned URI must be absolute.
     */
    #[Deprecated(message:'use League\Uri\Http::parse() instead', since:'league/uri:7.6.0')]
    public static function fromBaseUri(Rfc3986Uri|WhatwgUrl|Stringable|string $uri, Rfc3986Uri|WhatwgUrl|Stringable|string|null $baseUri = null): self
    {
        return new self(Uri::fromBaseUri($uri, $baseUri));
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @codeCoverageIgnore
     * @see Http::new()
     *
     * Create a new instance from a string.
     */
    #[Deprecated(message:'use League\Uri\Http::new() instead', since:'league/uri:7.0.0')]
    public static function createFromString(Stringable|string $uri = ''): self
    {
        return self::new($uri);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @codeCoverageIgnore
     * @see Http::fromComponents()
     *
     * Create a new instance from a hash of parse_url parts.
     *
     * @param InputComponentMap $components a hash representation of the URI similar
     *                                      to PHP parse_url function result
     */
    #[Deprecated(message:'use League\Uri\Http::fromComponents() instead', since:'league/uri:7.0.0')]
    public static function createFromComponents(array $components): self
    {
        return self::fromComponents($components);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @codeCoverageIgnore
     * @see Http::fromServer()
     *
     * Create a new instance from the environment.
     */
    #[Deprecated(message:'use League\Uri\Http::fromServer() instead', since:'league/uri:7.0.0')]
    public static function createFromServer(array $server): self
    {
        return self::fromServer($server);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @codeCoverageIgnore
     * @see Http::new()
     *
     * Create a new instance from a URI object.
     */
    #[Deprecated(message:'use League\Uri\Http::new() instead', since:'league/uri:7.0.0')]
    public static function createFromUri(Psr7UriInterface|UriInterface $uri): self
    {
        return self::new($uri);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @codeCoverageIgnore
     * @see Http::fromBaseUri()
     *
     * Create a new instance from a URI and a Base URI.
     *
     * The returned URI must be absolute.
     */
    #[Deprecated(message:'use League\Uri\Http::fromBaseUri() instead', since:'league/uri:7.0.0')]
    public static function createFromBaseUri(Stringable|string $uri, Stringable|string|null $baseUri = null): self
    {
        return self::fromBaseUri($uri, $baseUri);
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri;

use Psr\Http\Message\UriFactoryInterface;
use Psr\Http\Message\UriInterface;

final class HttpFactory implements UriFactoryInterface
{
    public function createUri(string $uri = ''): UriInterface
    {
        return Http::new($uri);
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri;

use BackedEnum;
use League\Uri\Contracts\Conditionable;
use League\Uri\Contracts\FragmentDirective;
use League\Uri\Contracts\Transformable;
use League\Uri\Contracts\UriComponentInterface;
use League\Uri\Exceptions\SyntaxError;
use SensitiveParameter;
use Stringable;
use Throwable;
use TypeError;
use Uri\Rfc3986\Uri as Rfc3986Uri;
use Uri\WhatWg\Url as WhatWgUrl;

use function is_bool;
use function str_replace;
use function strpos;

final class Builder implements Conditionable, Transformable
{
    private ?string $scheme = null;
    private ?string $username = null;
    private ?string $password = null;
    private ?string $host = null;
    private ?int $port = null;
    private ?string $path = null;
    private ?string $query = null;
    private ?string $fragment = null;

    public function __construct(
        BackedEnum|Stringable|string|null $scheme = null,
        BackedEnum|Stringable|string|null $username = null,
        #[SensitiveParameter] BackedEnum|Stringable|string|null $password = null,
        BackedEnum|Stringable|string|null $host = null,
        BackedEnum|int|null $port = null,
        BackedEnum|Stringable|string|null $path = null,
        BackedEnum|Stringable|string|null $query = null,
        BackedEnum|Stringable|string|null $fragment = null,
    ) {
        $this
            ->scheme($scheme)
            ->userInfo($username, $password)
            ->host($host)
            ->port($port)
            ->path($path)
            ->query($query)
            ->fragment($fragment);
    }

    /**
     * @throws SyntaxError
     */
    public function scheme(BackedEnum|Stringable|string|null $scheme): self
    {
        $scheme = $this->filterString($scheme);
        if ($scheme !== $this->scheme) {
            UriString::isValidScheme($scheme) || throw new SyntaxError('The scheme `'.$scheme.'` is invalid.');

            $this->scheme = $scheme;
        }

        return $this;
    }

    /**
     * @throws SyntaxError
     */
    public function userInfo(
        BackedEnum|Stringable|string|null $user,
        #[SensitiveParameter] BackedEnum|Stringable|string|null $password = null
    ): static {
        $username = Encoder::encodeUser($this->filterString($user));
        $password = Encoder::encodePassword($this->filterString($password));
        if ($username !== $this->username || $password !== $this->password) {
            $this->username = $username;
            $this->password = $password;
        }

        return $this;
    }

    /**
     * @throws SyntaxError
     */
    public function host(BackedEnum|Stringable|string|null $host): self
    {
        $host = $this->filterString($host);
        if ($host !== $this->host) {
            null === $host
            || HostRecord::isValid($host)
            || throw new SyntaxError('The host `'.$host.'` is invalid.');

            $this->host = $host;
        }

        return $this;
    }

    /**
     * @throws SyntaxError
     * @throws TypeError
     */
    public function port(BackedEnum|int|null $port): self
    {
        if ($port instanceof BackedEnum) {
            1 === preg_match('/^\d+$/', (string) $port->value)
            || throw new TypeError('The port must be a valid BackedEnum containing a number.');

            $port = (int) $port->value;
        }

        if ($port !== $this->port) {
            null === $port
            || ($port >= 0 && $port < 65535)
            || throw new SyntaxError('The port value must be null or an integer between 0 and 65535.');

            $this->port = $port;
        }

        return $this;
    }

    /**
     * @throws SyntaxError
     */
    public function authority(BackedEnum|Stringable|string|null $authority): self
    {
        ['user' => $user, 'pass' => $pass, 'host' => $host, 'port' => $port] = UriString::parseAuthority($authority);

        return $this
            ->userInfo($user, $pass)
            ->host($host)
            ->port($port);
    }

    /**
     * @throws SyntaxError
     */
    public function path(BackedEnum|Stringable|string|null $path): self
    {
        $path = $this->filterString($path);
        if ($path !== $this->path) {
            $this->path = null !== $path ? Encoder::encodePath($path) : null;
        }

        return $this;
    }

    /**
     * @throws SyntaxError
     */
    public function query(BackedEnum|Stringable|string|null $query): self
    {
        $query = $this->filterString($query);
        if ($query !== $this->query) {
            $this->query = Encoder::encodeQueryOrFragment($query);
        }

        return $this;
    }

    /**
     * @throws SyntaxError
     */
    public function fragment(BackedEnum|Stringable|string|null $fragment): self
    {
        $fragment = $this->filterString($fragment);
        if ($fragment !== $this->fragment) {
            $this->fragment = Encoder::encodeQueryOrFragment($fragment);
        }

        return $this;
    }

    /**
     * Puts back the Builder in a freshly created state.
     */
    public function reset(): self
    {
        $this->scheme = null;
        $this->username = null;
        $this->password = null;
        $this->host = null;
        $this->port = null;
        $this->path = null;
        $this->query = null;
        $this->fragment = null;

        return $this;
    }

    /**
     * Executes the given callback with the current instance
     * and returns the current instance.
     *
     * @param callable(self): self $callback
     */
    public function transform(callable $callback): static
    {
        return $callback($this);
    }

    public function when(callable|bool $condition, callable $onSuccess, ?callable $onFail = null): static
    {
        if (!is_bool($condition)) {
            $condition = $condition($this);
        }

        return match (true) {
            $condition => $onSuccess($this),
            null !== $onFail => $onFail($this),
            default => $this,
        } ?? $this;
    }

    /**
     * @throws SyntaxError if the URI can not be build with the current Builder state
     */
    public function guard(Rfc3986Uri|WhatWgUrl|BackedEnum|Stringable|string|null $baseUri = null): self
    {
        try {
            $this->build($baseUri);

            return $this;
        } catch (Throwable $exception) {
            throw new SyntaxError('The current builder cannot generate a valid URI.', previous: $exception);
        }
    }

    /**
     * Tells whether the URI can be built with the current Builder state.
     */
    public function validate(Rfc3986Uri|WhatWgUrl|BackedEnum|Stringable|string|null $baseUri = null): bool
    {
        try {
            $this->build($baseUri);

            return true;
        } catch (Throwable) {
            return false;
        }
    }

    public function build(Rfc3986Uri|WhatWgUrl|BackedEnum|Stringable|string|null $baseUri = null): Uri
    {
        $authority = $this->buildAuthority();
        $path = $this->buildPath($authority);
        $uriString = UriString::buildUri(
            $this->scheme,
            $authority,
            $path,
            Encoder::encodeQueryOrFragment($this->query),
            Encoder::encodeQueryOrFragment($this->fragment)
        );

        return Uri::new(null === $baseUri ? $uriString : UriString::resolve($uriString, match (true) {
            $baseUri instanceof Rfc3986Uri => $baseUri->toString(),
            $baseUri instanceof WhatWgUrl => $baseUri->toAsciiString(),
            default => $baseUri,
        }));
    }

    /**
     * @throws SyntaxError
     */
    private function buildAuthority(): ?string
    {
        if (null === $this->host) {
            (null === $this->username && null === $this->password && null === $this->port)
            || throw new SyntaxError('The User Information and/or the Port component(s) are set without a Host component being present.');

            return null;
        }

        $authority = $this->host;
        if (null !== $this->username || null !== $this->password) {
            $userInfo = Encoder::encodeUser($this->username);
            if (null !== $this->password) {
                $userInfo .= ':'.Encoder::encodePassword($this->password);
            }

            $authority = $userInfo.'@'.$authority;
        }

        if (null !== $this->port) {
            return $authority.':'.$this->port;
        }

        return $authority;
    }

    /**
     * @throws SyntaxError
     */
    private function buildPath(?string $authority): ?string
    {
        if (null === $this->path || '' === $this->path) {
            return $this->path;
        }

        $path = Encoder::encodePath($this->path);
        if (null !== $authority) {
            return str_starts_with($path, '/') ? $path : '/'.$path;
        }

        if (str_starts_with($path, '//')) {
            return '/.'.$path;
        }

        $colonPos = strpos($path, ':');
        if (false !== $colonPos && null === $this->scheme) {
            $slashPos = strpos($path, '/');
            (false !== $slashPos && $colonPos > $slashPos) || throw new SyntaxError('In absence of the scheme and authority components, the first path segment cannot contain a colon (":") character.');
        }

        return $path;
    }

    /**
     * Filter a string.
     *
     * @throws SyntaxError if the submitted data cannot be converted to string
     */
    private function filterString(BackedEnum|Stringable|string|null $str): ?string
    {
        $str = match (true) {
            $str instanceof FragmentDirective => $str->toFragmentValue(),
            $str instanceof UriComponentInterface => $str->value(),
            $str instanceof BackedEnum => (string) $str->value,
            null === $str => null,
            default => (string) $str,
        };

        if (null === $str) {
            return null;
        }

        $str = str_replace(' ', '%20', $str);

        return UriString::containsRfc3987Chars($str)
            ? $str
            : throw new SyntaxError('The component value `'.$str.'` contains invalid characters.');
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri;

use BackedEnum;
use League\Uri\Contracts\UriInterface;
use League\Uri\Exceptions\SyntaxError;
use League\Uri\UrlPattern\Component;
use League\Uri\UrlPattern\ComponentName;
use League\Uri\UrlPattern\ComponentResult;
use League\Uri\UrlPattern\MatchMode;
use League\Uri\UrlPattern\PatternType;
use League\Uri\UrlPattern\Result;
use Psr\Http\Message\UriInterface as Psr7UriInterface;
use Stringable;
use Throwable;
use Uri\Rfc3986\Uri as Rfc3986Uri;
use Uri\WhatWg\Url as WhatWgUrl;
use ValueError;

use function array_key_exists;
use function array_map;
use function get_debug_type;

/**
 * @property-read ?string $scheme
 * @property-read ?string $username
 * @property-read ?string $password
 * @property-read ?string $host
 * @property-read ?string $port
 * @property-read ?string $path
 * @property-read ?string $query
 * @property-read ?string $fragment
 */
final class UrlPattern
{
    /** @var array<value-of<ComponentName>, Component> $components */
    private readonly array $components;
    public readonly MatchMode $matchMode;
    public readonly bool $hasRegexpGroup;
    private readonly bool $hasVariable;
    public readonly PatternType $type;

    /**
     * @param array<non-empty-string, mixed> $patternComponents
     */
    public function __construct(array $patternComponents, MatchMode $matchMode = MatchMode::CaseSensitive)
    {
        $hasRegexpGroup = false;
        $hasVariable = false;
        $components = [];
        foreach (ComponentName::cases() as $name) {
            if (!array_key_exists($name->value, $patternComponents)) {
                if (ComponentName::Port !== $name) {
                    $components[$name->value] = Component::fromAsterisk();
                    continue;
                }

                $components[ComponentName::Port->value] = array_key_exists(ComponentName::Host->value, $patternComponents)
                    ? Component::fromEmpty()
                    : Component::fromAsterisk();
                continue;
            }

            $component = $patternComponents[$name->value];
            $component instanceof Component || throw new ValueError('the component must be a "'.Component::class.'"; '.get_debug_type($component).' given.');
            $components[$name->value] = $component;
            $hasRegexpGroup = $hasRegexpGroup || $component->hasRegexpGroup;
            $hasVariable = $hasVariable || $component->hasVariable;
        }

        $this->components = $components;
        $this->matchMode = $matchMode;
        $this->hasRegexpGroup = $hasRegexpGroup;
        $this->hasVariable = $hasVariable;
        $this->type = '*' !== $this->components[ComponentName::Scheme->value]->pattern ? PatternType::Absolute : PatternType::Relative;
    }

    public static function parse(
        Stringable|string $pattern,
        Rfc3986Uri|WhatWgUrl|BackedEnum|Stringable|string|null $baseUrl = null,
        MatchMode $matchMode = MatchMode::CaseSensitive
    ): ?self {
        try {
            return self::from($pattern, $baseUrl, $matchMode);
        } catch (Throwable) {
            return null;
        }
    }

    /**
     * @throws SyntaxError
     */
    public static function from(
        Stringable|string $pattern,
        Rfc3986Uri|WhatWgUrl|BackedEnum|Stringable|string|null $baseUrl = null,
        MatchMode $matchMode = MatchMode::CaseSensitive
    ): self {

        return UrlPatternBuilder::from($pattern, $baseUrl)
            ->build(matchMode: $matchMode, type: PatternType::Absolute);
    }

    public function match(Rfc3986Uri|WhatWgUrl|BackedEnum|Stringable|string $input): bool
    {
        try {
            return $this->extract($input) instanceof Result;
        } catch (Throwable) {
            return false;
        }
    }

    /**
     * @throws SyntaxError
     */
    public function extract(Rfc3986Uri|WhatWgUrl|BackedEnum|Stringable|string $input): ?Result
    {
        $components = self::components($input);
        $result = [];
        foreach ($this->components as $name => $component) {
            $found = ComponentResult::extract($components[$name], $component, ComponentName::from($name), $this->matchMode);
            if (null === $found) {
                return null;
            }

            $result[$name] = $found;
        }

        return Result::tryFrom($result, $input);
    }

    public function hasVariable(): bool
    {
        return $this->hasVariable;
    }

    public function __get(string $name): string
    {
        $key = ComponentName::tryFrom($name)?->value;
        null !== $key || throw new ValueError('the property named "'.$name.'" does not exist.');

        return $this->components[$key]->pattern;
    }

    /**
     * @return array<value-of<ComponentName>, string>
     */
    public function __debugInfo(): array
    {
        return array_map(static fn (Component $component): string => $component->pattern, $this->components);
    }

    /**
     * @throws SyntaxError
     *
     * @return array{
     *     scheme: string,
     *     username: string,
     *     password: string,
     *     host: string,
     *     port: string,
     *     path: string,
     *     query: string,
     *     fragment: string
     * }
     */
    private static function components(WhatWgUrl|Rfc3986Uri|Stringable|BackedEnum|string $input): array
    {
        if ($input instanceof WhatWgUrl || $input instanceof UriInterface || $input instanceof Rfc3986Uri) {
            return [
                ComponentName::Scheme->value => (string) $input->getScheme(),
                ComponentName::Username->value => (string) $input->getUsername(),
                ComponentName::Password->value => (string) $input->getPassword(),
                ComponentName::Host->value => (string) ($input instanceof WhatWgUrl ? $input->getAsciiHost() : $input->getHost()),
                ComponentName::Port->value => (string) $input->getPort(),
                ComponentName::Path->value => $input->getPath(),
                ComponentName::Query->value => (string) $input->getQuery(),
                ComponentName::Fragment->value => (string) $input->getFragment(),
            ];
        }

        if ($input instanceof Psr7UriInterface) {
            $username = '';
            $password = '';
            $userInfo = $input->getUserInfo();
            if ('' !== $userInfo) {
                [$username, $password] = explode(':', $userInfo, 2) + [1 => ''];
            }

            return [
                ComponentName::Scheme->value => $input->getScheme(),
                ComponentName::Username->value => $username,
                ComponentName::Password->value => $password,
                ComponentName::Host->value => $input->getHost(),
                ComponentName::Port->value => (string) $input->getPort(),
                ComponentName::Path->value => $input->getPath(),
                ComponentName::Query->value => $input->getQuery(),
                ComponentName::Fragment->value => $input->getFragment(),
            ];
        }

        $components = array_map(static fn (string|int|null $value): string => (string)$value, UriString::parse($input));
        $components[ComponentName::Username->value] = $components['user'];
        $components[ComponentName::Password->value] = $components['pass'];

        return $components;
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri;

use Deprecated;
use JsonSerializable;
use League\Uri\Contracts\UriAccess;
use League\Uri\Contracts\UriInterface;
use League\Uri\Exceptions\MissingFeature;
use League\Uri\Idna\Converter as IdnaConverter;
use League\Uri\IPv4\Converter as IPv4Converter;
use League\Uri\IPv6\Converter as IPv6Converter;
use Psr\Http\Message\UriFactoryInterface;
use Psr\Http\Message\UriInterface as Psr7UriInterface;
use Stringable;

use function array_pop;
use function array_reduce;
use function count;
use function explode;
use function implode;
use function in_array;
use function preg_match;
use function rawurldecode;
use function sort;
use function str_contains;
use function str_repeat;
use function str_replace;
use function strpos;
use function substr;

/**
 * @phpstan-import-type ComponentMap from UriInterface
 * @deprecated since version 7.6.0
 *
 * @see Modifier
 * @see Uri
 */
class BaseUri implements Stringable, JsonSerializable, UriAccess
{
    /** @var array<string,int> */
    final protected const WHATWG_SPECIAL_SCHEMES = ['ftp' => 1, 'http' => 1, 'https' => 1, 'ws' => 1, 'wss' => 1];

    /** @var array<string,int> */
    final protected const DOT_SEGMENTS = ['.' => 1, '..' => 1];

    protected readonly Psr7UriInterface|UriInterface|null $origin;
    protected readonly ?string $nullValue;

    /**
     * @param UriFactoryInterface|null $uriFactory Deprecated, will be removed in the next major release
     */
    final protected function __construct(
        protected readonly Psr7UriInterface|UriInterface $uri,
        protected readonly ?UriFactoryInterface $uriFactory
    ) {
        $this->nullValue = $this->uri instanceof Psr7UriInterface ? '' : null;
        $this->origin = $this->computeOrigin($this->uri, $this->nullValue);
    }

    public static function from(Stringable|string $uri, ?UriFactoryInterface $uriFactory = null): static
    {
        $uri = static::formatHost(static::filterUri($uri, $uriFactory));
        return new static($uri, $uriFactory);
    }

    public function withUriFactory(UriFactoryInterface $uriFactory): static
    {
        return new static($this->uri, $uriFactory);
    }

    public function withoutUriFactory(): static
    {
        return new static($this->uri, null);
    }

    public function getUri(): Psr7UriInterface|UriInterface
    {
        return $this->uri;
    }

    public function getUriString(): string
    {
        return $this->uri->__toString();
    }

    public function jsonSerialize(): string
    {
        return $this->uri->__toString();
    }

    public function __toString(): string
    {
        return $this->uri->__toString();
    }

    public function origin(): ?self
    {
        return match (null) {
            $this->origin => null,
            default => new self($this->origin, $this->uriFactory),
        };
    }

    /**
     * Returns the Unix filesystem path.
     *
     * The method will return null if a scheme is present and is not the `file` scheme
     */
    public function unixPath(): ?string
    {
        return match ($this->uri->getScheme()) {
            'file', $this->nullValue => rawurldecode($this->uri->getPath()),
            default => null,
        };
    }

    /**
     * Returns the Windows filesystem path.
     *
     * The method will return null if a scheme is present and is not the `file` scheme
     */
    public function windowsPath(): ?string
    {
        static $regexpWindowsPath = ',^(?<root>[a-zA-Z]:),';

        if (!in_array($this->uri->getScheme(), ['file', $this->nullValue], true)) {
            return null;
        }

        $originalPath = $this->uri->getPath();
        $path = $originalPath;
        if ('/' === ($path[0] ?? '')) {
            $path = substr($path, 1);
        }

        if (1 === preg_match($regexpWindowsPath, $path, $matches)) {
            $root = $matches['root'];
            $path = substr($path, strlen($root));

            return $root.str_replace('/', '\\', rawurldecode($path));
        }

        $host = $this->uri->getHost();

        return match ($this->nullValue) {
            $host => str_replace('/', '\\', rawurldecode($originalPath)),
            default => '\\\\'.$host.'\\'.str_replace('/', '\\', rawurldecode($path)),
        };
    }

    /**
     * Returns a string representation of a File URI according to RFC8089.
     *
     * The method will return null if the URI scheme is not the `file` scheme
     */
    public function toRfc8089(): ?string
    {
        $path = $this->uri->getPath();

        return match (true) {
            'file' !== $this->uri->getScheme() => null,
            in_array($this->uri->getAuthority(), ['', null, 'localhost'], true) => 'file:'.match (true) {
                '' === $path,
                '/' === $path[0] => $path,
                default => '/'.$path,
            },
            default => (string) $this->uri,
        };
    }

    /**
     * Tells whether the `file` scheme base URI represents a local file.
     */
    public function isLocalFile(): bool
    {
        return match (true) {
            'file' !== $this->uri->getScheme() => false,
            in_array($this->uri->getAuthority(), ['', null, 'localhost'], true) => true,
            default => false,
        };
    }

    /**
     * Tells whether the URI is opaque or not.
     *
     * A URI is opaque if and only if it is absolute
     * and does not have an authority path.
     */
    public function isOpaque(): bool
    {
        return $this->nullValue === $this->uri->getAuthority()
            && $this->isAbsolute();
    }

    /**
     * Tells whether two URI do not share the same origin.
     */
    public function isCrossOrigin(Stringable|string $uri): bool
    {
        if (null === $this->origin) {
            return true;
        }

        $uri = static::filterUri($uri);
        $uriOrigin = $this->computeOrigin($uri, $uri instanceof Psr7UriInterface ? '' : null);

        return match(true) {
            null === $uriOrigin,
            $uriOrigin->__toString() !== $this->origin->__toString() => true,
            default => false,
        };
    }

    /**
     * Tells whether the URI is absolute.
     */
    public function isAbsolute(): bool
    {
        return $this->nullValue !== $this->uri->getScheme();
    }

    /**
     * Tells whether the URI is a network path.
     */
    public function isNetworkPath(): bool
    {
        return $this->nullValue === $this->uri->getScheme()
            && $this->nullValue !== $this->uri->getAuthority();
    }

    /**
     * Tells whether the URI is an absolute path.
     */
    public function isAbsolutePath(): bool
    {
        return $this->nullValue === $this->uri->getScheme()
            && $this->nullValue === $this->uri->getAuthority()
            && '/' === ($this->uri->getPath()[0] ?? '');
    }

    /**
     * Tells whether the URI is a relative path.
     */
    public function isRelativePath(): bool
    {
        return $this->nullValue === $this->uri->getScheme()
            && $this->nullValue === $this->uri->getAuthority()
            && '/' !== ($this->uri->getPath()[0] ?? '');
    }

    /**
     * Tells whether both URI refers to the same document.
     */
    public function isSameDocument(Stringable|string $uri): bool
    {
        return self::normalizedUri($this->uri)->equals(self::normalizedUri($uri));
    }

    private static function normalizedUri(Stringable|string $uri): Uri
    {
        // Normalize the URI according to RFC3986
        $uri = ($uri instanceof Uri ? $uri : Uri::new($uri))->normalize();

        return $uri
            //Normalization as per WHATWG URL standard
            //only meaningful for WHATWG Special URI scheme protocol
            ->when(
                condition: '' === $uri->getPath() && null !== $uri->getAuthority(),
                onSuccess: fn (Uri $uri) => $uri->withPath('/'),
            )
            //Sorting as per WHATWG URLSearchParams class
            //not included on any equivalence algorithm
            ->when(
                condition: null !== ($query = $uri->getQuery()) && str_contains($query, '&'),
                onSuccess: function (Uri $uri) use ($query) {
                    $pairs = explode('&', (string) $query);
                    sort($pairs);

                    return $uri->withQuery(implode('&', $pairs));
                }
            );
    }

    /**
     * Tells whether the URI contains an Internationalized Domain Name (IDN).
     */
    public function hasIdn(): bool
    {
        return IdnaConverter::isIdn($this->uri->getHost());
    }

    /**
     * Tells whether the URI contains an IPv4 regardless if it is mapped or native.
     */
    public function hasIPv4(): bool
    {
        return IPv4Converter::fromEnvironment()->isIpv4($this->uri->getHost());
    }

    /**
     * Resolves a URI against a base URI using RFC3986 rules.
     *
     * This method MUST retain the state of the submitted URI instance, and return
     * a URI instance of the same type that contains the applied modifications.
     *
     * This method MUST be transparent when dealing with error and exceptions.
     * It MUST not alter or silence them apart from validating its own parameters.
     */
    public function resolve(Stringable|string $uri): static
    {
        $resolved = UriString::resolve($uri, $this->uri);

        return new static(match ($this->uriFactory) {
            null => Uri::new($resolved),
            default => $this->uriFactory->createUri($resolved),
        }, $this->uriFactory);
    }

    /**
     * Relativize a URI according to a base URI.
     *
     * This method MUST retain the state of the submitted URI instance, and return
     * a URI instance of the same type that contains the applied modifications.
     *
     * This method MUST be transparent when dealing with error and exceptions.
     * It MUST not alter of silence them apart from validating its own parameters.
     */
    public function relativize(Stringable|string $uri): static
    {
        $uri = static::formatHost(static::filterUri($uri, $this->uriFactory));
        if ($this->canNotBeRelativize($uri)) {
            return new static($uri, $this->uriFactory);
        }

        $null = $uri instanceof Psr7UriInterface ? '' : null;
        $uri = $uri->withScheme($null)->withPort(null)->withUserInfo($null)->withHost($null);
        $targetPath = $uri->getPath();
        $basePath = $this->uri->getPath();

        return new static(
            match (true) {
                $targetPath !== $basePath => $uri->withPath(static::relativizePath($targetPath, $basePath)),
                static::componentEquals('query', $uri) => $uri->withPath('')->withQuery($null),
                $null === $uri->getQuery() => $uri->withPath(static::formatPathWithEmptyBaseQuery($targetPath)),
                default => $uri->withPath(''),
            },
            $this->uriFactory
        );
    }

    final protected function computeOrigin(Psr7UriInterface|UriInterface $uri, ?string $nullValue): Psr7UriInterface|UriInterface|null
    {
        if ($uri instanceof Uri) {
            $origin = $uri->getOrigin();
            if (null === $origin) {
                return null;
            }

            return Uri::tryNew($origin);
        }

        $origin = Uri::tryNew($uri)?->getOrigin();
        if (null === $origin) {
            return null;
        }

        $components = UriString::parse($origin);

        return $uri
                ->withFragment($nullValue)
                ->withQuery($nullValue)
                ->withPath('')
                ->withScheme('localhost')
                ->withHost((string) $components['host'])
                ->withPort($components['port'])
                ->withScheme((string) $components['scheme'])
                ->withUserInfo($nullValue);
    }

    /**
     * Input URI normalization to allow Stringable and string URI.
     */
    final protected static function filterUri(Stringable|string $uri, UriFactoryInterface|null $uriFactory = null): Psr7UriInterface|UriInterface
    {
        return match (true) {
            $uri instanceof UriAccess => $uri->getUri(),
            $uri instanceof Psr7UriInterface,
            $uri instanceof UriInterface => $uri,
            $uriFactory instanceof UriFactoryInterface => $uriFactory->createUri((string) $uri),
            default => Uri::new($uri),
        };
    }

    /**
     * Tells whether the component value from both URI object equals.
     *
     * @pqram 'query'|'authority'|'scheme' $property
     */
    final protected function componentEquals(string $property, Psr7UriInterface|UriInterface $uri): bool
    {
        $getComponent = function (string $property, Psr7UriInterface|UriInterface $uri): ?string {
            $component = match ($property) {
                'query' => $uri->getQuery(),
                'authority' => $uri->getAuthority(),
                default => $uri->getScheme(),
            };

            return match (true) {
                $uri instanceof UriInterface, '' !== $component => $component,
                default => null,
            };
        };

        return $getComponent($property, $uri) === $getComponent($property, $this->uri);
    }

    /**
     * Filter the URI object.
     */
    final protected static function formatHost(Psr7UriInterface|UriInterface $uri): Psr7UriInterface|UriInterface
    {
        $host = $uri->getHost();
        try {
            $converted = IPv4Converter::fromEnvironment()->toDecimal($host);
        } catch (MissingFeature) {
            $converted = null;
        }

        if (false === filter_var($converted, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
            $converted = IPv6Converter::compress($host);
        }

        return match (true) {
            null !== $converted => $uri->withHost($converted),
            '' === $host,
            $uri instanceof UriInterface => $uri,
            default => $uri->withHost((string) Uri::fromComponents(['host' => $host])->getHost()),
        };
    }

    /**
     * Tells whether the submitted URI object can be relativized.
     */
    final protected function canNotBeRelativize(Psr7UriInterface|UriInterface $uri): bool
    {
        return !static::componentEquals('scheme', $uri)
            || !static::componentEquals('authority', $uri)
            || static::from($uri)->isRelativePath();
    }

    /**
     * Relatives the URI for an authority-less target URI.
     */
    final protected static function relativizePath(string $path, string $basePath): string
    {
        $baseSegments = static::getSegments($basePath);
        $targetSegments = static::getSegments($path);
        $targetBasename = array_pop($targetSegments);
        array_pop($baseSegments);
        foreach ($baseSegments as $offset => $segment) {
            if (!isset($targetSegments[$offset]) || $segment !== $targetSegments[$offset]) {
                break;
            }
            unset($baseSegments[$offset], $targetSegments[$offset]);
        }
        $targetSegments[] = $targetBasename;

        return static::formatPath(
            str_repeat('../', count($baseSegments)).implode('/', $targetSegments),
            $basePath
        );
    }

    /**
     * returns the path segments.
     *
     * @return string[]
     */
    final protected static function getSegments(string $path): array
    {
        return explode('/', match (true) {
            '' === $path,
            '/' !== $path[0] => $path,
            default => substr($path, 1),
        });
    }

    /**
     * Formatting the path to keep a valid URI.
     */
    final protected static function formatPath(string $path, string $basePath): string
    {
        $colonPosition = strpos($path, ':');
        $slashPosition = strpos($path, '/');

        return match (true) {
            '' === $path => match (true) {
                '' === $basePath,
                '/' === $basePath => $basePath,
                default => './',
            },
            false === $colonPosition => $path,
            false === $slashPosition,
            $colonPosition < $slashPosition  =>  "./$path",
            default => $path,
        };
    }

    /**
     * Formatting the path to keep a resolvable URI.
     */
    final protected static function formatPathWithEmptyBaseQuery(string $path): string
    {
        $targetSegments = static::getSegments($path);
        $basename = $targetSegments[array_key_last($targetSegments)];

        return '' === $basename ? './' : $basename;
    }

    /**
     * Normalizes a URI for comparison; this URI string representation is not suitable for usage as per RFC guidelines.
     *
     * @deprecated since version 7.6.0
     *
     * @codeCoverageIgnore
     */
    #[Deprecated(message:'no longer used by the isSameDocument method', since:'league/uri-interfaces:7.6.0')]
    final protected function normalize(Psr7UriInterface|UriInterface $uri): string
    {
        $newUri = $uri->withScheme($uri instanceof Psr7UriInterface ? '' : null);
        if ('' === $newUri->__toString()) {
            return '';
        }

        return UriString::normalize($newUri);
    }


    /**
     * Remove dot segments from the URI path as per RFC specification.
     *
     * @deprecated since version 7.6.0
     *
     * @codeCoverageIgnore
     */
    #[Deprecated(message:'no longer used by the isSameDocument method', since:'league/uri-interfaces:7.6.0')]
    final protected function removeDotSegments(string $path): string
    {
        if (!str_contains($path, '.')) {
            return $path;
        }

        $reducer = function (array $carry, string $segment): array {
            if ('..' === $segment) {
                array_pop($carry);

                return $carry;
            }

            if (!isset(static::DOT_SEGMENTS[$segment])) {
                $carry[] = $segment;
            }

            return $carry;
        };

        $oldSegments = explode('/', $path);
        $newPath = implode('/', array_reduce($oldSegments, $reducer(...), []));
        if (isset(static::DOT_SEGMENTS[$oldSegments[array_key_last($oldSegments)]])) {
            $newPath .= '/';
        }

        // @codeCoverageIgnoreStart
        // added because some PSR-7 implementations do not respect RFC3986
        if (str_starts_with($path, '/') && !str_starts_with($newPath, '/')) {
            return '/'.$newPath;
        }
        // @codeCoverageIgnoreEnd

        return $newPath;
    }

    /**
     * Resolves an URI path and query component.
     *
     * @return array{0:string, 1:string|null}
     *
     * @deprecated since version 7.6.0
     *
     * @codeCoverageIgnore
     */
    #[Deprecated(message:'no longer used by the isSameDocument method', since:'league/uri-interfaces:7.6.0')]
    final protected function resolvePathAndQuery(Psr7UriInterface|UriInterface $uri): array
    {
        $targetPath = $uri->getPath();
        $null = $uri instanceof Psr7UriInterface ? '' : null;

        if (str_starts_with($targetPath, '/')) {
            return [$targetPath, $uri->getQuery()];
        }

        if ('' === $targetPath) {
            $targetQuery = $uri->getQuery();
            if ($null === $targetQuery) {
                $targetQuery = $this->uri->getQuery();
            }

            $targetPath = $this->uri->getPath();
            //@codeCoverageIgnoreStart
            //because some PSR-7 Uri implementations allow this RFC3986 forbidden construction
            if (null !== $this->uri->getAuthority() && !str_starts_with($targetPath, '/')) {
                $targetPath = '/'.$targetPath;
            }
            //@codeCoverageIgnoreEnd

            return [$targetPath, $targetQuery];
        }

        $basePath = $this->uri->getPath();
        if (null !== $this->uri->getAuthority() && '' === $basePath) {
            $targetPath = '/'.$targetPath;
        }

        if ('' !== $basePath) {
            $segments = explode('/', $basePath);
            array_pop($segments);
            if ([] !== $segments) {
                $targetPath = implode('/', $segments).'/'.$targetPath;
            }
        }

        return [$targetPath, $uri->getQuery()];
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri;

use BackedEnum;
use DateTimeImmutable;
use DateTimeInterface;
use DateTimeZone;
use Exception;
use ReflectionEnum;
use Stringable;
use UnitEnum;
use ValueError;

use function enum_exists;
use function filter_var;
use function is_bool;
use function is_float;
use function is_int;
use function is_iterable;
use function is_scalar;
use function is_string;
use function trim;

use const FILTER_NULL_ON_FAILURE;
use const FILTER_VALIDATE_BOOLEAN;
use const FILTER_VALIDATE_FLOAT;
use const FILTER_VALIDATE_INT;

final class TypeConverter
{
    private function __construct()
    {
    }

    public static function toString(mixed $value): ?string
    {
        if ($value instanceof BackedEnum) {
            $value = $value->value;
        }

        return match (true) {
            is_bool($value) => true === $value ? '1' : '0',
            is_scalar($value),
            $value instanceof Stringable => (string) $value,
            default => null,
        };
    }

    public static function toInteger(mixed $value): ?int
    {
        if (!is_int($value) && !is_float($value)) {
            $value = self::toString($value);
        }

        return match (true) {
            is_int($value) => $value,
            is_string($value) => false !== ($res = filter_var($value, FILTER_VALIDATE_INT)) ? $res : null,
            default => null,
        };
    }

    public static function toFloat(mixed $value): ?float
    {
        if (!is_int($value) && !is_float($value)) {
            $value = self::toString($value);
        }

        return match (true) {
            is_float($value) => $value,
            is_int($value) => (float) $value,
            is_string($value) => false !== ($res = filter_var($value, FILTER_VALIDATE_FLOAT)) ? $res : null,
            default => null,
        };
    }

    public static function toBoolean(mixed $value): ?bool
    {
        if (!is_bool($value)) {
            $value = self::toString($value);
        }

        return match (true) {
            is_bool($value) => $value,
            is_string($value) => filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE),
            default => null,
        };
    }

    /**
     * @return array<string>
     */
    public static function toStrings(mixed $values, ?string $default = null): array
    {
        if (!is_iterable($values)) {
            return [];
        }

        $arr = [];
        foreach ($values as $index => $value) {
            $item = self::toString($value) ?? $default;
            if (null === $item) {
                return [];
            }

            $arr[$index] = $item;
        }

        return $arr;
    }

    /**
     * @return array<int>
     */
    public static function toIntegers(mixed $values, ?int $default = null): array
    {
        if (!is_iterable($values)) {
            return [];
        }

        $arr = [];
        foreach ($values as $index => $value) {
            $item = self::toInteger($value) ?? $default;
            if (null === $item) {
                return [];
            }

            $arr[$index] = $item;
        }

        return $arr;
    }

    /**
     * @return array<float>
     */
    public static function toFloats(mixed $values, ?float $default = null): array
    {
        if (!is_iterable($values)) {
            return [];
        }

        $arr = [];
        foreach ($values as $index => $value) {
            $item = self::toFloat($value) ?? $default;
            if (null === $item) {
                return [];
            }

            $arr[$index] = $item;
        }

        return $arr;
    }

    /**
     * @return array<bool>
     */
    public static function toBooleans(mixed $values, ?bool $default = null): array
    {
        if (!is_iterable($values)) {
            return [];
        }

        $arr = [];
        foreach ($values as $index => $value) {
            $item = self::toBoolean($value) ?? $default;
            if (null === $item) {
                return [];
            }

            $arr[$index] = $item;
        }

        return $arr;
    }

    /**
     * @param class-string<UnitEnum> $enumClass
     */
    public static function toEnum(mixed $value, string $enumClass): ?UnitEnum
    {
        enum_exists($enumClass) || throw new ValueError($enumClass.' does not exist or could not be found.');
        /** @var array<UnitEnum> $cases */
        $cases = $enumClass::cases();

        return (new ReflectionEnum($enumClass))->isBacked()
            ? self::findBackedEnum($value, $enumClass, $cases) /* @phpstan-ignore-line */
            : self::findEnum($value, $enumClass, $cases);
    }

    /**
     * @template T of UnitEnum
     *
     * @param class-string<T> $enumClass
     * @param ?T $default
    */
    public static function toEnums(mixed $values, string $enumClass, ?UnitEnum $default = null): array
    {
        if (!is_iterable($values)) {
            return [];
        }

        enum_exists($enumClass) || throw new ValueError($enumClass.' does not exist or could not be found.');
        null === $default || $default instanceof $enumClass || throw new ValueError('The default value must be an instance of '.$enumClass.'; '.get_debug_type($default).' given.');

        $arr = [];
        $cases = $enumClass::cases();
        /** @var ReflectionEnum<UnitEnum> $reflection */
        $reflection = new ReflectionEnum($enumClass);

        $createItem = $reflection->isBacked()
             ? fn ($item) => self::findBackedEnum($item, $enumClass, $cases) /* @phpstan-ignore-line */
             : fn ($item) => self::findEnum($item, $enumClass, $cases);

        foreach ($values as $index => $value) {
            $item = $createItem($value) ?? $default;
            if (null === $item) {
                return [];
            }

            $arr[$index] = $item;
        }

        return $arr;
    }

    /**
     * @template T of BackedEnum
     *
     * @param class-string<T> $enumClass
     * @param array<T> $cases
     *
     * @return ?T
     */
    private static function findBackedEnum(mixed $value, string $enumClass, array $cases): ?UnitEnum
    {
        if ($value instanceof $enumClass) {
            return $value;
        }

        if ($value instanceof UnitEnum || is_float($value) || is_bool($value)) {
            return null;
        }

        if (!is_string($value) && !is_int($value)) {
            $value = self::toString($value);
        }

        if (null === $value) {
            return null;
        }

        $intValue = is_int($value) ? $value : self::toInteger($value);
        $value = (string) $value;
        foreach ($cases as $case) {
            if ($intValue === $case->value || $value === $case->value) {
                return $case;
            }
        }

        return null;
    }

    /**
     * @template T of UnitEnum
     *
     * @param class-string<T> $enumClass
     * @param array<T> $cases
     *
     * @return ?T
     */
    private static function findEnum(mixed $value, string $enumClass, array $cases): ?UnitEnum
    {
        if ($value instanceof $enumClass) {
            return $value;
        }

        if ($value instanceof UnitEnum || (is_scalar($value) && !is_string($value))) {
            return null;
        }

        $value = self::toString($value);
        if (null === $value) {
            return null;
        }

        foreach ($cases as $case) {
            if ($case->name === $value) {
                return $case;
            }
        }

        return null;
    }

    /**
     * @param non-empty-string $format
     *
     * @throws Exception if the provided timezone is invalid
     */
    public static function toDateTimeImmutable(mixed $value, string $format, DateTimeZone|string $timezone = 'UTC'): ?DateTimeImmutable
    {
        $format = trim($format);
        '' !== $format || throw new ValueError('The date format must be a non-empty string.');
        $timezone = !$timezone instanceof DateTimeZone ? new DateTimeZone($timezone) : $timezone;

        return self::createDateTimeImmutable($value, $format, $timezone);
    }

    /**
     * @param non-empty-string $format
     *
     * @throws Exception
     *
     * @return array<DateTimeImmutable>
     */
    public static function toDateTimeImmutables(
        mixed $values,
        string $format,
        DateTimeZone|string $timezone = 'UTC',
        DateTimeInterface|null $default = null,
    ): array {
        if (!is_iterable($values)) {
            return [];
        }

        $format = trim($format);
        '' !== $format || throw new ValueError('The date format must be a non-empty string.');
        $timezone = !$timezone instanceof DateTimeZone ? new DateTimeZone($timezone) : $timezone;

        if (null !== $default && !$default instanceof DateTimeImmutable) {
            $default = DateTimeImmutable::createFromInterface($default);
        }

        $arr = [];
        foreach ($values as $index => $value) {
            $item = self::createDateTimeImmutable($value, $format, $timezone) ?? $default;
            if (null === $item) {
                return [];
            }

            $arr[$index] = $item;
        }

        return $arr;
    }

    /**
     * @param non-empty-string $format
     */
    private static function createDateTimeImmutable(mixed $value, string $format, DateTimeZone $timezone): ?DateTimeImmutable
    {
        if ($value instanceof DateTimeImmutable) {
            return $value;
        }

        if ($value instanceof DateTimeInterface) {
            return DateTimeImmutable::createFromInterface($value);
        }

        if (is_scalar($value) && !is_string($value)) {
            return null;
        }

        $value = self::toString($value);
        if (!is_string($value) || str_contains($value, "\0")) {
            return null;
        }

        $date = DateTimeImmutable::createFromFormat($format, $value, $timezone);
        $errors = DateTimeImmutable::getLastErrors();

        return false !== $date
            && (false === $errors || (0 === $errors['error_count'] && 0 === $errors['warning_count'])) ? $date : null;
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri;

use Deprecated;
use League\Uri\Contracts\UriInterface;
use Psr\Http\Message\UriInterface as Psr7UriInterface;

/**
 * @deprecated since version 7.0.0
 * @codeCoverageIgnore
 * @see BaseUri
 */
final class UriResolver
{
    /**
     * Resolves a URI against a base URI using RFC3986 rules.
     *
     * This method MUST retain the state of the submitted URI instance, and return
     * a URI instance of the same type that contains the applied modifications.
     *
     * This method MUST be transparent when dealing with error and exceptions.
     * It MUST not alter or silence them apart from validating its own parameters.
     */
    #[Deprecated(message:'use League\Uri\BaseUri::resolve() instead', since:'league/uri:7.0.0')]
    public static function resolve(Psr7UriInterface|UriInterface $uri, Psr7UriInterface|UriInterface $baseUri): Psr7UriInterface|UriInterface
    {
        return BaseUri::from($baseUri)->resolve($uri)->getUri();
    }

    /**
     * Relativizes a URI according to a base URI.
     *
     * This method MUST retain the state of the submitted URI instance, and return
     * a URI instance of the same type that contains the applied modifications.
     *
     * This method MUST be transparent when dealing with error and exceptions.
     * It MUST not alter or silence them apart from validating its own parameters.
     */
    #[Deprecated(message:'use League\Uri\BaseUri::relativize() instead', since:'league/uri:7.0.0')]
    public static function relativize(Psr7UriInterface|UriInterface $uri, Psr7UriInterface|UriInterface $baseUri): Psr7UriInterface|UriInterface
    {
        return BaseUri::from($baseUri)->relativize($uri)->getUri();
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri;

use BackedEnum;
use Closure;
use JsonSerializable;
use League\Uri\Contracts\Conditionable;
use League\Uri\Contracts\Transformable;
use League\Uri\Contracts\UriComponentInterface;
use League\Uri\Contracts\UriInterface;
use League\Uri\Exceptions\SyntaxError;
use League\Uri\UriTemplate\Template;
use Stringable;
use Uri\Rfc3986\Uri as Rfc3986Uri;
use Uri\WhatWg\Url as WhatWgUrl;

use function is_bool;
use function preg_match;
use function str_replace;
use function strtolower;

/**
 * @phpstan-type UrnSerialize array{0: array{urn: non-empty-string}, 1: array{}}
 * @phpstan-import-type InputComponentMap from UriString
 * @phpstan-type UrnMap array{
 *      scheme: 'urn',
 *      nid: string,
 *      nss: string,
 *      r_component: ?string,
 *      q_component: ?string,
 *      f_component: ?string,
 *  }
 */
final class Urn implements Conditionable, Stringable, JsonSerializable, Transformable
{
    /**
     * RFC8141 regular expression URN splitter.
     *
     * The regexp does not perform any look-ahead.
     * Not all invalid URN are caught. Some
     * post-regexp-validation checks
     * are mandatory.
     *
     * @link https://datatracker.ietf.org/doc/html/rfc8141#section-2
     *
     * @var string
     */
    private const REGEXP_URN_PARTS = '/^
        urn:
        (?<nid>[a-z0-9](?:[a-z0-9-]{0,30}[a-z0-9])?): # NID
        (?<nss>.*?)                                   # NSS
        (?<frc>\?\+(?<rcomponent>.*?))?               # r-component
        (?<fqc>\?\=(?<qcomponent>.*?))?               # q-component
        (?:\#(?<fcomponent>.*))?                      # f-component
    $/xi';

    /**
     * RFC8141 namespace identifier regular expression.
     *
     * @link https://datatracker.ietf.org/doc/html/rfc8141#section-2
     *
     * @var string
     */
    private const REGEX_NID_SEQUENCE = '/^[a-z0-9]([a-z0-9-]{0,30})[a-z0-9]$/xi';

    /** @var non-empty-string */
    private readonly string $uriString;
    /** @var non-empty-string */
    private readonly string $nid;
    /** @var non-empty-string */
    private readonly string $nss;
    /** @var non-empty-string|null */
    private readonly ?string $rComponent;
    /** @var non-empty-string|null */
    private readonly ?string $qComponent;
    /** @var non-empty-string|null */
    private readonly ?string $fComponent;

    /**
     * @param Rfc3986Uri|WhatWgUrl|BackedEnum|Stringable|string $urn the percent-encoded URN
     */
    public static function parse(Rfc3986Uri|WhatWgUrl|BackedEnum|Stringable|string $urn): ?Urn
    {
        try {
            return self::fromString($urn);
        } catch (SyntaxError) {
            return null;
        }
    }

    /**
     * @param Rfc3986Uri|WhatWgUrl|Stringable|string $urn the percent-encoded URN
     * @see self::fromString()
     *
     * @throws SyntaxError if the URN is invalid
     */
    public static function new(Rfc3986Uri|WhatWgUrl|Stringable|string $urn): self
    {
        return self::fromString($urn);
    }

    /**
     * @param Rfc3986Uri|WhatWgUrl|BackedEnum|Stringable|string $urn the percent-encoded URN
     *
     * @throws SyntaxError if the URN is invalid
     */
    public static function fromString(Rfc3986Uri|WhatWgUrl|BackedEnum|Stringable|string $urn): self
    {
        $urn = match (true) {
            $urn instanceof Rfc3986Uri => $urn->toRawString(),
            $urn instanceof WhatWgUrl => $urn->toAsciiString(),
            $urn instanceof BackedEnum => (string) $urn->value,
            default => (string) $urn,
        };

        UriString::containsRfc3986Chars($urn) || throw new SyntaxError('The URN is malformed, it contains invalid characters.');
        1 === preg_match(self::REGEXP_URN_PARTS, $urn, $matches) || throw new SyntaxError('The URN string is invalid.');

        return new self(
            nid: $matches['nid'],
            nss: $matches['nss'],
            rComponent: (isset($matches['frc'], $matches['rcomponent']) && '' !== $matches['frc']) ? $matches['rcomponent'] : null,
            qComponent: (isset($matches['fqc'], $matches['qcomponent']) && '' !== $matches['fqc']) ? $matches['qcomponent'] : null,
            fComponent: $matches['fcomponent'] ?? null,
        );
    }

    /**
     * Create a new instance from a hash representation of the URI similar
     * to PHP parse_url function result.
     *
     * @param InputComponentMap $components a hash representation of the URI similar to PHP parse_url function result
     */
    public static function fromComponents(array $components = []): self
    {
        $components += [
            'scheme' => null, 'user' => null, 'pass' => null, 'host' => null,
            'port' => null, 'path' => '', 'query' => null, 'fragment' => null,
        ];

        return self::fromString(UriString::build($components));
    }

    /**
     * @param Stringable|string $nss the percent-encoded NSS
     *
     * @throws SyntaxError if the URN is invalid
     */
    public static function fromRfc2141(BackedEnum|Stringable|string $nid, BackedEnum|Stringable|string $nss): self
    {
        if ($nid instanceof BackedEnum) {
            $nid = $nid->value;
        }

        if ($nss instanceof BackedEnum) {
            $nss = $nss->value;
        }

        return new self((string) $nid, (string) $nss);
    }

    /**
     * @param string $nss the percent-encoded NSS
     * @param ?string $rComponent the percent-encoded r-component
     * @param ?string $qComponent the percent-encoded q-component
     * @param ?string $fComponent the percent-encoded f-component
     *
     * @throws SyntaxError if one of the URN part is invalid
     */
    private function __construct(
        string $nid,
        string $nss,
        ?string $rComponent = null,
        ?string $qComponent = null,
        ?string $fComponent = null,
    ) {
        ('' !== $nid && 1 === preg_match(self::REGEX_NID_SEQUENCE, $nid)) || throw new SyntaxError('The URN is malformed, the NID is invalid.');
        ('' !== $nss && Encoder::isPathEncoded($nss)) || throw new SyntaxError('The URN is malformed, the NSS is invalid.');

        /** @param Closure(string): ?non-empty-string $closure */
        $validateComponent = static fn (?string $value, Closure $closure, string $name): ?string => match (true) {
            null === $value,
            ('' !== $value && 1 !== preg_match('/[#?]/', $value) && $closure($value)) => $value,
            default => throw new SyntaxError('The URN is malformed, the `'.$name.'` component is invalid.'),
        };

        $this->nid = $nid;
        $this->nss = $nss;
        $this->rComponent = $validateComponent($rComponent, Encoder::isPathEncoded(...), 'r-component');
        $this->qComponent = $validateComponent($qComponent, Encoder::isQueryEncoded(...), 'q-component');
        $this->fComponent = $validateComponent($fComponent, Encoder::isFragmentEncoded(...), 'f-component');
        $this->uriString = $this->setUriString();
    }

    /**
     * @return non-empty-string
     */
    private function setUriString(): string
    {
        $str = $this->toRfc2141();
        if (null !== $this->rComponent) {
            $str .= '?+'.$this->rComponent;
        }

        if (null !== $this->qComponent) {
            $str .= '?='.$this->qComponent;
        }

        if (null !== $this->fComponent) {
            $str .= '#'.$this->fComponent;
        }

        return $str;
    }

    /**
     * Returns the NID.
     *
     * @return non-empty-string
     */
    public function getNid(): string
    {
        return $this->nid;
    }

    /**
     * Returns the percent-encoded NSS.
     *
     * @return non-empty-string
     */
    public function getNss(): string
    {
        return $this->nss;
    }

    /**
     * Returns the percent-encoded r-component string or null if it is not set.
     *
     * @return ?non-empty-string
     */
    public function getRComponent(): ?string
    {
        return $this->rComponent;
    }

    /**
     * Returns the percent-encoded q-component string or null if it is not set.
     *
     * @return ?non-empty-string
     */
    public function getQComponent(): ?string
    {
        return $this->qComponent;
    }

    /**
     * Returns the percent-encoded f-component string or null if it is not set.
     *
     * @return ?non-empty-string
     */
    public function getFComponent(): ?string
    {
        return $this->fComponent;
    }

    /**
     * Returns the RFC8141 URN string representation.
     *
     * @return non-empty-string
     */
    public function toString(): string
    {
        return $this->uriString;
    }

    /**
     * Returns the RFC2141 URN string representation.
     *
     * @return non-empty-string
     */
    public function toRfc2141(): string
    {
        return 'urn:'.$this->nid.':'.$this->nss;
    }

    /**
     * Returns the human-readable string representation of the URN as an IRI.
     *
     * @see https://datatracker.ietf.org/doc/html/rfc3987
     */
    public function toDisplayString(): string
    {
        return UriString::toIriString($this->uriString);
    }

    /**
     * Returns the RFC8141 URN string representation.
     *
     * @see self::toString()
     *
     * @return non-empty-string
     */
    public function __toString(): string
    {
        return $this->toString();
    }

    /**
     * Returns the RFC8141 URN string representation.
     * @see self::toString()
     *
     * @return non-empty-string
     */
    public function jsonSerialize(): string
    {
        return $this->toString();
    }

    /**
     * Returns the RFC3986 representation of the current URN.
     *
     * If a template URI is used the following variables as present
     * {nid} for the namespace identifier
     * {nss} for the namespace specific string
     * {r_component} for the r-component without its delimiter
     * {q_component} for the q-component without its delimiter
     * {f_component} for the f-component without its delimiter
     */
    public function resolve(UriTemplate|Template|BackedEnum|string|null $template = null): UriInterface
    {
        return null !== $template ? Uri::fromTemplate($template, $this->toComponents()) : Uri::new($this->uriString);
    }

    public function hasRComponent(): bool
    {
        return null !== $this->rComponent;
    }

    public function hasQComponent(): bool
    {
        return null !== $this->qComponent;
    }

    public function hasFComponent(): bool
    {
        return null !== $this->fComponent;
    }

    public function hasOptionalComponent(): bool
    {
        return null !== $this->rComponent
            || null !== $this->qComponent
            || null !== $this->fComponent;
    }

    /**
     * Return an instance with the specified NID.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the specified NID.
     *
     * @throws SyntaxError for invalid component or transformations
     *                     that would result in an object in invalid state.
     */
    public function withNid(BackedEnum|Stringable|string $nid): self
    {
        if ($nid instanceof BackedEnum) {
            $nid = $nid->value;
        }

        $nid = (string) $nid;

        return $this->nid === $nid ? $this : new self(
            nid: $nid,
            nss: $this->nss,
            rComponent: $this->rComponent,
            qComponent: $this->qComponent,
            fComponent: $this->fComponent,
        );
    }

    /**
     * Return an instance with the specified NSS.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the specified NSS.
     *
     * @throws SyntaxError for invalid component or transformations
     *                     that would result in an object in invalid state.
     */
    public function withNss(BackedEnum|Stringable|string $nss): self
    {
        $nss = Encoder::encodePath($nss);

        return $this->nss === $nss ? $this : new self(
            nid: $this->nid,
            nss: $nss,
            rComponent: $this->rComponent,
            qComponent: $this->qComponent,
            fComponent: $this->fComponent,
        );
    }

    /**
     * Return an instance with the specified r-component.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the specified r-component.
     *
     * The component is removed if the value is null.
     *
     * @throws SyntaxError for invalid component or transformations
     *                     that would result in an object in invalid state.
     */
    public function withRComponent(BackedEnum|Stringable|string|null $component): self
    {
        if ($component instanceof BackedEnum) {
            $component = (string) $component->value;
        }

        if ($component instanceof UriComponentInterface) {
            $component = $component->value();
        }

        if (null !== $component) {
            $component = self::formatComponent(Encoder::encodePath($component));
        }

        return $this->rComponent === $component ? $this : new self(
            nid: $this->nid,
            nss: $this->nss,
            rComponent: $component,
            qComponent: $this->qComponent,
            fComponent: $this->fComponent,
        );
    }

    private static function formatComponent(?string $component): ?string
    {
        return null === $component ? null : str_replace(['?', '#'], ['%3F', '%23'], $component);
    }

    /**
     * Return an instance with the specified q-component.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the specified q-component.
     *
     * The component is removed if the value is null.
     *
     * @throws SyntaxError for invalid component or transformations
     *                     that would result in an object in invalid state.
     */
    public function withQComponent(BackedEnum|Stringable|string|null $component): self
    {
        if ($component instanceof UriComponentInterface) {
            $component = $component->value();
        }

        $component = self::formatComponent(Encoder::encodeQueryOrFragment($component));

        return $this->qComponent === $component ? $this : new self(
            nid: $this->nid,
            nss: $this->nss,
            rComponent: $this->rComponent,
            qComponent: $component,
            fComponent: $this->fComponent,
        );
    }

    /**
     * Return an instance with the specified f-component.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the specified f-component.
     *
     * The component is removed if the value is null.
     *
     * @throws SyntaxError for invalid component or transformations
     *                     that would result in an object in invalid state.
     */
    public function withFComponent(BackedEnum|Stringable|string|null $component): self
    {
        if ($component instanceof UriComponentInterface) {
            $component = $component->value();
        }

        $component = self::formatComponent(Encoder::encodeQueryOrFragment($component));

        return $this->fComponent === $component ? $this : new self(
            nid: $this->nid,
            nss: $this->nss,
            rComponent: $this->rComponent,
            qComponent: $this->qComponent,
            fComponent: $component,
        );
    }

    public function normalize(): self
    {
        $copy = new self(
            nid: strtolower($this->nid),
            nss: (string) Encoder::normalizePath($this->nss),
            rComponent: null === $this->rComponent ? $this->rComponent : Encoder::normalizePath($this->rComponent),
            qComponent: Encoder::normalizeQuery($this->qComponent),
            fComponent: Encoder::normalizeFragment($this->fComponent),
        );

        return $copy->uriString === $this->uriString ? $this : $copy;
    }

    public function equals(Urn|Rfc3986Uri|WhatWgUrl|BackedEnum|Stringable|string $other, UrnComparisonMode $urnComparisonMode = UrnComparisonMode::ExcludeComponents): bool
    {
        if (!$other instanceof Urn) {
            $other = self::parse($other);
        }

        return (null !== $other) && match ($urnComparisonMode) {
            UrnComparisonMode::ExcludeComponents => $other->normalize()->toRfc2141() === $this->normalize()->toRfc2141(),
            UrnComparisonMode::IncludeComponents => $other->normalize()->toString() === $this->normalize()->toString(),
        };
    }

    public function when(callable|bool $condition, callable $onSuccess, ?callable $onFail = null): static
    {
        if (!is_bool($condition)) {
            $condition = $condition($this);
        }

        return match (true) {
            $condition => $onSuccess($this),
            null !== $onFail => $onFail($this),
            default => $this,
        } ?? $this;
    }

    public function transform(callable $callback): static
    {
        return $callback($this);
    }

    /**
     * @return UrnSerialize
     */
    public function __serialize(): array
    {
        return [['urn' => $this->toString()], []];
    }

    /**
     * @param UrnSerialize $data
     *
     * @throws SyntaxError
     */
    public function __unserialize(array $data): void
    {
        [$properties] = $data;
        $uri = self::fromString($properties['urn'] ?? throw new SyntaxError('The `urn` property is missing from the serialized object.'));

        $this->nid = $uri->nid;
        $this->nss = $uri->nss;
        $this->rComponent = $uri->rComponent;
        $this->qComponent = $uri->qComponent;
        $this->fComponent = $uri->fComponent;
        $this->uriString = $uri->uriString;
    }

    /**
     * @return UrnMap
     */
    public function toComponents(): array
    {
        return [
            'scheme' => 'urn',
            'nid' => $this->nid,
            'nss' => $this->nss,
            'r_component' => $this->rComponent,
            'q_component' => $this->qComponent,
            'f_component' => $this->fComponent,
        ];
    }

    /**
     * @return UrnMap
     */
    public function __debugInfo(): array
    {
        return $this->toComponents();
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri;

use BackedEnum;
use Deprecated;
use League\Uri\Contracts\UriException;
use League\Uri\Contracts\UriInterface;
use League\Uri\Exceptions\MissingFeature;
use League\Uri\Exceptions\SyntaxError;
use League\Uri\UriTemplate\ExtractionResult;
use League\Uri\UriTemplate\Template;
use League\Uri\UriTemplate\TemplateCanNotBeExpanded;
use League\Uri\UriTemplate\VariableBag;
use League\Uri\UriTemplate\VariableCanNotBeExtracted;
use Psr\Http\Message\UriFactoryInterface;
use Psr\Http\Message\UriInterface as Psr7UriInterface;
use Stringable;
use Uri\InvalidUriException;
use Uri\Rfc3986\Uri as Rfc3986Uri;
use Uri\WhatWg\InvalidUrlException;
use Uri\WhatWg\Url as WhatWgUrl;
use Uri\WhatWg\UrlValidationError;

use function array_fill_keys;
use function array_key_exists;
use function class_exists;

/**
 * Defines the URI Template syntax and the process for expanding a URI Template into a URI reference.
 *
 * @link    https://tools.ietf.org/html/rfc6570
 * @package League\Uri
 * @author  Ignace Nyamagana Butera <nyamsprod@gmail.com>
 * @since   6.1.0
 *
 * @phpstan-import-type InputValue from VariableBag
 */
final class UriTemplate implements Stringable
{
    private readonly Template $template;
    private readonly VariableBag $defaultVariables;

    /**
     * @throws SyntaxError if the template syntax is invalid
     * @throws TemplateCanNotBeExpanded if the template or the variables are invalid
     */
    public function __construct(BackedEnum|Stringable|string $template, iterable|ExtractionResult $defaultVariables = [])
    {
        $this->template = $template instanceof Template ? $template : Template::new($template);
        $this->defaultVariables = $this->filterVariables($defaultVariables);
    }

    private function filterVariables(iterable|ExtractionResult $variables): VariableBag
    {
        if (!$variables instanceof VariableBag) {
            $variables = new VariableBag($variables);
        }

        return $variables
            ->filter(fn ($value, string|int $name) => array_key_exists(
                $name,
                array_fill_keys($this->template->variableNames, 1)
            ));
    }

    /**
     * Returns the string representation of the UriTemplate.
     */
    public function __toString(): string
    {
        return $this->template->value;
    }

    /**
     * Returns the distinct variables placeholders used in the template.
     *
     * @return array<string>
     */
    public function getVariableNames(): array
    {
        return $this->template->variableNames;
    }

    /**
     * @return array<string, InputValue>
     */
    public function getDefaultVariables(): array
    {
        return iterator_to_array($this->defaultVariables);
    }

    /**
     * Tells whether the UriTemplate contains variables.
     */
    public function hasVariable(): bool
    {
        return 0 < count($this->template->variableNames);
    }

    /**
     * Returns a new instance with the updated default variables.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the modified default variables.
     *
     * If present, variables whose name is not part of the current template
     * possible variable names are removed.
     *
     * @throws TemplateCanNotBeExpanded if the variables are invalid
     */
    public function withDefaultVariables(iterable|ExtractionResult $defaultVariables): self
    {
        $defaultVariables = $this->filterVariables($defaultVariables);
        if ($this->defaultVariables->equals($defaultVariables)) {
            return $this;
        }

        return new self($this->template, $defaultVariables);
    }

    private function templateExpanded(iterable|ExtractionResult $variables = []): string
    {
        return $this->template->expand($this->filterVariables($variables)->replace($this->defaultVariables));
    }

    private function templateExpandedOrFail(iterable|ExtractionResult $variables = []): string
    {
        return $this->template->expandOrFail($this->filterVariables($variables)->replace($this->defaultVariables));
    }

    /**
     * @throws TemplateCanNotBeExpanded if the variables are invalid
     * @throws UriException if the resulting expansion cannot be converted to a UriInterface instance
     */
    public function expand(iterable|ExtractionResult $variables = [], Rfc3986Uri|WhatWgUrl|BackedEnum|Stringable|string|null $baseUri = null): UriInterface
    {
        $expanded = $this->templateExpanded($variables);

        return null === $baseUri ? Uri::new($expanded) : (Uri::parse($expanded, $baseUri) ?? throw new SyntaxError('Unable to expand URI'));
    }

    /**
     * @throws MissingFeature if no Uri\Rfc3986\Uri class is found
     * @throws TemplateCanNotBeExpanded if the variables are invalid
     * @throws InvalidUriException if the base URI cannot be converted to a Uri\Rfc3986\Uri instance
     * @throws InvalidUriException if the resulting expansion cannot be converted to a Uri\Rfc3986\Uri instance
     */
    public function expandToUri(iterable|ExtractionResult $variables = [], Rfc3986Uri|WhatWgUrl|BackedEnum|Stringable|string|null $baseUri = null): Rfc3986Uri
    {
        class_exists(Rfc3986Uri::class) || throw new MissingFeature('Support for '.Rfc3986Uri::class.' requires PHP8.5+ or a polyfill. Run "composer require league/uri-polyfill" or use you own polyfill.');

        return new Rfc3986Uri($this->templateExpanded($variables), $this->newRfc3986Uri($baseUri));
    }

    /**
     * @param list<UrlValidationError>|null $errors
     *
     * @throws TemplateCanNotBeExpanded if the variables are invalid
     * @throws InvalidUrlException if the base URI cannot be converted to a Uri\Whatwg\Url instance
     * @throws InvalidUrlException if the resulting expansion cannot be converted to a Uri\Whatwg\Url instance
     * @throws MissingFeature if no Uri\Whatwg\Url class is found
     */
    public function expandToUrl(iterable|ExtractionResult $variables = [], Rfc3986Uri|WhatWgUrl|BackedEnum|Stringable|string|null $baseUrl = null, array|null &$errors = []): WhatWgUrl
    {
        class_exists(WhatWgUrl::class) || throw new MissingFeature('Support for '.WhatWgUrl::class.' requires PHP8.5+ or a polyfill. Run "composer require league/uri-polyfill" or use you own polyfill.');

        return new WhatWgUrl($this->templateExpanded($variables), $this->newWhatWgUrl($baseUrl), $errors);
    }

    /**
     * @throws TemplateCanNotBeExpanded if the variables are invalid
     * @throws UriException if the resulting expansion cannot be converted to a UriInterface instance
     */
    public function expandToPsr7Uri(
        iterable|ExtractionResult $variables = [],
        Rfc3986Uri|WhatWgUrl|BackedEnum|Stringable|string|null $baseUrl = null,
        UriFactoryInterface $uriFactory = new HttpFactory()
    ): Psr7UriInterface {
        $expanded = $this->templateExpanded($variables);

        return $uriFactory->createUri(
            null === $baseUrl
            ? $expanded
            : UriString::resolve($expanded, match (true) {
                $baseUrl instanceof Rfc3986Uri => $baseUrl->toRawString(),
                $baseUrl instanceof WhatWgUrl => $baseUrl->toUnicodeString(),
                default => $baseUrl,
            })
        );
    }

    /**
     * @throws TemplateCanNotBeExpanded if the variables are invalid or missing
     * @throws UriException if the resulting expansion cannot be converted to a UriInterface instance
     */
    public function expandOrFail(iterable|ExtractionResult $variables = [], Rfc3986Uri|WhatWgUrl|BackedEnum|Stringable|string|null $baseUri = null): UriInterface
    {
        $expanded = $this->templateExpandedOrFail($variables);

        return null === $baseUri ? Uri::new($expanded) : (Uri::parse($expanded, $baseUri) ?? throw new SyntaxError('Unable to expand URI'));
    }

    /**
     * @throws MissingFeature if no Uri\Rfc3986\Uri class is found
     * @throws TemplateCanNotBeExpanded if the variables are invalid
     * @throws InvalidUriException if the base URI cannot be converted to a Uri\Rfc3986\Uri instance
     * @throws InvalidUriException if the resulting expansion cannot be converted to a Uri\Rfc3986\Uri instance
     */
    public function expandToUriOrFail(iterable|ExtractionResult $variables = [], Rfc3986Uri|WhatWgUrl|BackedEnum|Stringable|string|null $baseUri = null): Rfc3986Uri
    {
        class_exists(Rfc3986Uri::class) || throw new MissingFeature('Support for '.Rfc3986Uri::class.' requires PHP8.5+ or a polyfill. Run "composer require league/uri-polyfill" or use you own polyfill.');

        return new Rfc3986Uri($this->templateExpandedOrFail($variables), $this->newRfc3986Uri($baseUri));
    }

    /**
     * @param list<UrlValidationError>|null $errors
     *
     * @throws MissingFeature if no Uri\Whatwg\Url class is found
     * @throws TemplateCanNotBeExpanded if the variables are invalid
     * @throws InvalidUrlException if the base URI cannot be converted to a Uri\Whatwg\Url instance
     * @throws InvalidUrlException if the resulting expansion cannot be converted to a Uri\Whatwg\Url instance
     */
    public function expandToUrlOrFail(iterable|ExtractionResult $variables = [], Rfc3986Uri|WhatWgUrl|BackedEnum|Stringable|string|null $baseUrl = null, array|null &$errors = []): WhatWgUrl
    {
        class_exists(WhatWgUrl::class) || throw new MissingFeature('Support for '.WhatWgUrl::class.' requires PHP8.5+ or a polyfill. Run "composer require league/uri-polyfill" or use you own polyfill.');

        return new WhatWgUrl($this->templateExpandedOrFail($variables), $this->newWhatWgUrl($baseUrl), $errors);
    }

    /**
     * @throws TemplateCanNotBeExpanded if the variables are invalid
     * @throws UriException if the resulting expansion cannot be converted to a UriInterface instance
     */
    public function expandToPsr7UriOrFail(
        iterable|ExtractionResult $variables = [],
        Rfc3986Uri|WhatWgUrl|BackedEnum|Stringable|string|null $baseUrl = null,
        UriFactoryInterface $uriFactory = new HttpFactory()
    ): Psr7UriInterface {
        $expanded = $this->templateExpandedOrFail($variables);

        return $uriFactory->createUri(
            null === $baseUrl
            ? $expanded
            : UriString::resolve($expanded, match (true) {
                $baseUrl instanceof Rfc3986Uri => $baseUrl->toRawString(),
                $baseUrl instanceof WhatWgUrl => $baseUrl->toUnicodeString(),
                default => $baseUrl,
            })
        );
    }

    /**
     * @throws InvalidUrlException
     */
    private function newWhatWgUrl(Rfc3986Uri|WhatWgUrl|BackedEnum|Stringable|string|null $url = null): ?WhatWgUrl
    {
        return match (true) {
            null === $url => null,
            $url instanceof WhatWgUrl => $url,
            $url instanceof Rfc3986Uri => new WhatWgUrl($url->toRawString()),
            $url instanceof BackedEnum => new WhatWgUrl((string) $url->value),
            default => new WhatWgUrl((string) $url),
        };
    }

    /**
     * @throws InvalidUriException
     */
    private function newRfc3986Uri(Rfc3986Uri|WhatWgUrl|BackedEnum|Stringable|string|null $uri = null): ?Rfc3986Uri
    {
        return match (true) {
            null === $uri => null,
            $uri instanceof Rfc3986Uri => $uri,
            $uri instanceof WhatWgUrl => new Rfc3986Uri($uri->toAsciiString()),
            $uri instanceof BackedEnum => new Rfc3986Uri((string) $uri->value),
            default => new Rfc3986Uri((string) $uri),
        };
    }

    /**
     * Extracts the variables from a URI using the template.
     */
    public function extract(Rfc3986Uri|WhatWgUrl|BackedEnum|Stringable|string $uri): ExtractionResult
    {
        return $this->template->extract($uri);
    }

    /**
     * @throws VariableCanNotBeExtracted if the URI cannot be extracted using the template
     */
    public function extractOrFail(Rfc3986Uri|WhatWgUrl|BackedEnum|Stringable|string $uri): ExtractionResult
    {
        return $this->template->extractOrFail($uri);
    }

    /**
     * Returns whether the URI matches the template.
     */
    public function match(Rfc3986Uri|WhatWgUrl|BackedEnum|Stringable|string $uri): bool
    {
        return $this->template->match($uri);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.6.0
     * @codeCoverageIgnore
     * @see UriTemplate::toString()
     *
     * Create a new instance from the environment.
     */
    #[Deprecated(message:'use League\Uri\UriTemplate::__toString() instead', since:'league/uri:7.6.0')]
    public function getTemplate(): string
    {
        return $this->__toString();
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri;

use BackedEnum;
use League\Uri\Contracts\Conditionable;
use League\Uri\Contracts\Transformable;
use League\Uri\Contracts\UriInterface;
use League\Uri\Exceptions\SyntaxError;
use League\Uri\UrlPattern\Component;
use League\Uri\UrlPattern\ComponentName;
use League\Uri\UrlPattern\MatchMode;
use League\Uri\UrlPattern\Parser;
use League\Uri\UrlPattern\PatternType;
use Psr\Http\Message\UriInterface as Psr7UriInterface;
use Stringable;
use Throwable;
use Uri\Rfc3986\Uri as Rfc3986Uri;
use Uri\WhatWg\Url as WhatWgUrl;

use function array_filter;
use function is_bool;
use function strrpos;
use function substr;

final class UrlPatternBuilder implements Conditionable, Transformable
{
    private ?Component $scheme = null;
    private ?Component $username = null;
    private ?Component $password = null;
    private ?Component $host = null;
    private ?Component $port = null;
    private ?Component $path = null;
    private ?Component $query = null;
    private ?Component $fragment = null;
    private Rfc3986Uri|WhatWgUrl|Psr7UriInterface|UriInterface|null $baseUrl = null;

    public function __construct(
        Component|BackedEnum|Stringable|string|null $scheme = null,
        Component|BackedEnum|Stringable|string|null $username = null,
        Component|BackedEnum|Stringable|string|null $password = null,
        Component|BackedEnum|Stringable|string|null $host = null,
        Component|BackedEnum|Stringable|string|null $port = null,
        Component|BackedEnum|Stringable|string|null $path = null,
        Component|BackedEnum|Stringable|string|null $query = null,
        Component|BackedEnum|Stringable|string|null $fragment = null,
        Rfc3986Uri|WhatWgUrl|BackedEnum|Stringable|string|null $baseUrl = null
    ) {
        $this
            ->scheme($scheme)
            ->username($username)
            ->password($password)
            ->host($host)
            ->port($port)
            ->path($path)
            ->query($query)
            ->fragment($fragment)
            ->baseUrl($baseUrl);
    }

    public static function from(
        Stringable|string $pattern,
        Rfc3986Uri|WhatWgUrl|BackedEnum|Stringable|string|null $baseUrl = null
    ): self {
        $components = (new Parser((string) $pattern))->components();

        return new self(
            scheme: $components[ComponentName::Scheme->value] ?? null,
            username: $components[ComponentName::Username->value] ?? null,
            password: $components[ComponentName::Password->value] ?? null,
            host: $components[ComponentName::Host->value] ?? null,
            port: $components[ComponentName::Port->value] ?? null,
            path: $components[ComponentName::Path->value] ?? null,
            query: $components[ComponentName::Query->value] ?? null,
            fragment: $components[ComponentName::Fragment->value] ?? null,
            baseUrl: $baseUrl,
        );
    }

    /**
     * Tells whether the URLPattern can be built with the current Builder state.
     */
    public function validate(?PatternType $type = null): bool
    {
        try {
            $this->build(type: $type);

            return true;
        } catch (Throwable) {
            return false;
        }
    }

    /**
     * @throws SyntaxError if the URLPattern can not be build with the current Builder state
     */
    public function guard(?PatternType $type = null): self
    {
        try {
            $this->build(type: $type);

            return $this;
        } catch (Throwable $exception) {
            throw new SyntaxError('The current builder cannot generate a valid URL Pattern.', previous: $exception);
        }
    }

    /**
     * @throws SyntaxError
     */
    public function build(
        MatchMode $matchMode = MatchMode::CaseSensitive,
        ?PatternType $type = null
    ): UrlPattern {
        $components = array_filter(
            $this->applyBaseUrl([
                ComponentName::Scheme->value => $this->scheme ?? null,
                ComponentName::Username->value => $this->username ?? null,
                ComponentName::Password->value => $this->password ?? null,
                ComponentName::Host->value => $this->host ?? null,
                ComponentName::Port->value => $this->port ?? null,
                ComponentName::Path->value => $this->path ?? null,
                ComponentName::Query->value => $this->query ?? null,
                ComponentName::Fragment->value => $this->fragment ?? null,
            ]),
            static fn (?Component $component): bool => $component instanceof Component
        );

        return match ($type) {
            null => new UrlPattern($components, $matchMode),
            PatternType::Relative => isset($components[ComponentName::Scheme->value])
                ? throw new SyntaxError('An relative URL pattern must not contain a scheme component.')
                : new UrlPattern($components, $matchMode),
            default => isset($components[ComponentName::Scheme->value])
                ? new UrlPattern($components, $matchMode)
                : throw new SyntaxError('An absolute URL pattern must contain a scheme component.'),
        };
    }

    /**
     * @return array<non-empty-string, Component>
     */
    private function applyBaseUrl(array $components): array
    {
        if (null === $this->baseUrl) {
            return $components;
        }

        $hasScheme = isset($components[ComponentName::Scheme->value]);
        $hasHost = isset($components[ComponentName::Host->value]);
        $hasPort = isset($components[ComponentName::Port->value]);

        if (! $hasScheme) {
            $baseScheme = (string) $this->baseUrl->getScheme();
            $components[ComponentName::Scheme->value] = '' !== $baseScheme
                ? Component::fromPattern($baseScheme)
                : Component::fromAsterisk();
        }

        if (! $hasScheme && ! $hasHost) {
            $baseHost = (string) ($this->baseUrl instanceof WhatWgUrl ? $this->baseUrl->getAsciiHost() : $this->baseUrl->getHost());
            $components[ComponentName::Host->value] = '' !== $baseHost
                ? Component::fromPattern($baseHost)
                : Component::fromAsterisk();
        }

        if (! $hasPort && ! $hasScheme && ! $hasHost) {
            $basePort = $this->baseUrl->getPort();
            $components[ComponentName::Port->value] = null !== $basePort
                ? Component::fromPattern((string) $basePort)
                : Component::fromEmpty();
        }

        $basePath = $this->baseUrl->getPath();
        $components[ComponentName::Path->value] = match (true) {
            !isset($components[ComponentName::Path->value]) => '' !== $basePath ? Component::fromPattern($basePath) : Component::fromAsterisk(),
            default => self::resolvePathname($components[ComponentName::Path->value], $basePath),
        };

        return $components;
    }

    private static function resolvePathname(Component $pathname, string $basePathname): Component
    {
        if (str_starts_with($pathname->pattern, '/')) {
            return $pathname;
        }

        $slash = strrpos($basePathname, '/');

        $pattern = false === $slash
            ? $pathname->pattern
            : substr($basePathname, 0, $slash + 1).$pathname->pattern;

        return Component::fromPattern($pattern);
    }

    private static function filter(Component|BackedEnum|Stringable|string|null $component): ?Component
    {
        return match (true) {
            $component instanceof Component => $component,
            null === $component => null,
            $component instanceof BackedEnum => Component::fromPattern((string) $component->value),
            default => Component::fromPattern((string) $component),
        };
    }

    public function scheme(Component|BackedEnum|Stringable|string|null $scheme): self
    {
        $this->scheme = self::filter($scheme);

        return $this;
    }

    public function username(Component|BackedEnum|Stringable|string|null $username): self
    {
        $this->username = self::filter($username);

        return $this;
    }

    public function password(Component|BackedEnum|Stringable|string|null $password): self
    {
        $this->password = self::filter($password);

        return $this;
    }

    public function host(Component|BackedEnum|Stringable|string|null $host): self
    {
        $this->host = self::filter($host);

        return $this;
    }

    public function port(Component|BackedEnum|Stringable|string|null $port): self
    {
        $this->port = self::filter($port);

        return $this;
    }

    public function path(Component|BackedEnum|Stringable|string|null $path): self
    {
        $this->path = self::filter($path);

        return $this;
    }

    public function query(Component|BackedEnum|Stringable|string|null $query): self
    {
        $this->query = self::filter($query);

        return $this;
    }

    public function fragment(Component|BackedEnum|Stringable|string|null $fragment): self
    {
        $this->fragment = self::filter($fragment);

        return $this;
    }

    public function baseUrl(Rfc3986Uri|WhatWgUrl|BackedEnum|Stringable|string|null $baseUrl = null): self
    {
        if (null === $baseUrl) {
            $this->baseUrl = null;

            return $this;
        }

        $baseUrl = match (true) {
            $baseUrl instanceof Rfc3986Uri,
            $baseUrl instanceof WhatWgUrl,
            $baseUrl instanceof UriInterface,
            $baseUrl instanceof Psr7UriInterface => $baseUrl,
            default => Uri::new($baseUrl),
        };

        '' !== (string) $baseUrl->getScheme() ||  throw new SyntaxError('The BaseUrl must represent an absolute URI.');

        $this->baseUrl = $baseUrl;

        return $this;
    }

    public function reset(): self
    {
        $this->scheme = null;
        $this->username = null;
        $this->password = null;
        $this->host = null;
        $this->port = null;
        $this->path = null;
        $this->query = null;
        $this->fragment = null;
        $this->baseUrl = null;

        return $this;
    }

    /**
     * Executes the given callback with the current instance
     * and returns the current instance.
     *
     * @param callable(self): self $callback
     */
    public function transform(callable $callback): static
    {
        return $callback($this);
    }

    public function when(callable|bool $condition, callable $onSuccess, ?callable $onFail = null): static
    {
        if (!is_bool($condition)) {
            $condition = $condition($this);
        }

        return match (true) {
            $condition => $onSuccess($this),
            null !== $onFail => $onFail($this),
            default => $this,
        } ?? $this;
    }
}
{
    "name": "league/uri",
    "type": "library",
    "description" : "URI manipulation library",
    "keywords": [
        "url",
        "uri",
        "urn",
        "uri-template",
        "rfc2141",
        "rfc3986",
        "rfc3987",
        "rfc8141",
        "rfc6570",
        "psr-7",
        "parse_url",
        "http",
        "https",
        "ws",
        "ftp",
        "data-uri",
        "file-uri",
        "middleware",
        "parse_str",
        "query-string",
        "querystring",
        "hostname"
    ],
    "license": "MIT",
    "homepage": "https://uri.thephpleague.com",
    "authors": [
        {
            "name" : "Ignace Nyamagana Butera",
            "email" : "nyamsprod@gmail.com",
            "homepage" : "https://nyamsprod.com"
        }
    ],
    "support": {
        "forum": "https://thephpleague.slack.com",
        "docs": "https://uri.thephpleague.com",
        "issues": "https://github.com/thephpleague/uri-src/issues"
    },
    "funding": [
        {
            "type": "github",
            "url": "https://github.com/sponsors/nyamsprod"
        }
    ],
    "require": {
        "php": "^8.1",
        "league/uri-interfaces": "^7.9.0",
        "psr/http-factory": "^1",
        "ext-mbstring": "*"
    },
    "autoload": {
        "psr-4": {
            "League\\Uri\\": ""
        }
    },
    "conflict": {
        "league/uri-schemes": "^1.0"
    },
    "suggest": {
        "ext-bcmath": "to improve IPV4 host parsing",
        "ext-dom": "to convert the URI into an HTML anchor tag",
        "ext-fileinfo": "to create Data URI from file contennts",
        "ext-gmp": "to improve IPV4 host parsing",
        "ext-intl": "to handle IDN host with the best performance",
        "ext-uri": "to use the PHP native URI class",
        "jeremykendall/php-domain-parser": "to further parse the URI host and resolve its Public Suffix and Top Level Domain",
        "league/uri-components" : "to provide additional tools to manipulate URI objects components",
        "league/uri-polyfill" : "to backport the PHP URI extension for older versions of PHP",
        "php-64bit": "to improve IPV4 host parsing",
        "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present",
        "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification"
    },
    "extra": {
        "branch-alias": {
            "dev-master": "7.x-dev"
        }
    },
    "config": {
        "sort-packages": true
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri;

enum SchemeType
{
    case Opaque;
    case Hierarchical;
    case Unknown;

    public function isOpaque(): bool
    {
        return self::Opaque === $this;
    }

    public function isHierarchical(): bool
    {
        return self::Hierarchical === $this;
    }

    public function isUnknown(): bool
    {
        return self::Unknown === $this;
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri;

use BackedEnum;
use Deprecated;
use finfo;
use League\Uri\Contracts\Conditionable;
use League\Uri\Contracts\FragmentDirective;
use League\Uri\Contracts\Transformable;
use League\Uri\Contracts\UriComponentInterface;
use League\Uri\Contracts\UriException;
use League\Uri\Contracts\UriInterface;
use League\Uri\Exceptions\MissingFeature;
use League\Uri\Exceptions\SyntaxError;
use League\Uri\Idna\Converter as IdnaConverter;
use League\Uri\IPv4\Converter as IPv4Converter;
use League\Uri\IPv6\Converter as IPv6Converter;
use League\Uri\UriTemplate\TemplateCanNotBeExpanded;
use Psr\Http\Message\UriInterface as Psr7UriInterface;
use RuntimeException;
use SensitiveParameter;
use SplFileInfo;
use SplFileObject;
use Stringable;
use Throwable;
use TypeError;
use Uri\Rfc3986\Uri as Rfc3986Uri;
use Uri\WhatWg\Url as WhatWgUrl;

use function array_filter;
use function array_key_last;
use function array_map;
use function array_pop;
use function array_shift;
use function base64_decode;
use function base64_encode;
use function basename;
use function count;
use function dirname;
use function explode;
use function fclose;
use function feof;
use function file_get_contents;
use function filter_var;
use function fopen;
use function fread;
use function fwrite;
use function gettype;
use function implode;
use function in_array;
use function is_bool;
use function is_object;
use function is_resource;
use function is_string;
use function preg_match;
use function preg_replace;
use function preg_replace_callback;
use function rawurldecode;
use function rawurlencode;
use function restore_error_handler;
use function set_error_handler;
use function sprintf;
use function str_contains;
use function str_repeat;
use function str_replace;
use function str_starts_with;
use function strlen;
use function strpos;
use function strspn;
use function strtolower;
use function substr;
use function trim;

use const FILEINFO_MIME;
use const FILEINFO_MIME_TYPE;
use const FILTER_FLAG_IPV4;
use const FILTER_NULL_ON_FAILURE;
use const FILTER_VALIDATE_BOOLEAN;
use const FILTER_VALIDATE_EMAIL;
use const FILTER_VALIDATE_IP;

/**
 * @phpstan-import-type ComponentMap from UriString
 * @phpstan-import-type InputComponentMap from UriString
 */
final class Uri implements Conditionable, UriInterface, Transformable
{
    /**
     * RFC3986 invalid characters.
     *
     * @link https://tools.ietf.org/html/rfc3986#section-2.2
     *
     * @var string
     */
    private const REGEXP_INVALID_CHARS = '/[\x00-\x1f\x7f]/';

    /**
     * RFC3986 IPvFuture host and port component.
     *
     * @var string
     */
    private const REGEXP_HOST_PORT = ',^(?<host>(\[.*]|[^:])*)(:(?<port>[^/?#]*))?$,x';

    /**
     * Regular expression pattern to for file URI.
     * <volume> contains the volume but not the volume separator.
     * The volume separator may be URL-encoded (`|` as `%7C`) by formatPath(),
     * so we account for that here.
     *
     * @var string
     */
    private const REGEXP_FILE_PATH = ',^(?<delim>/)?(?<volume>[a-zA-Z])(?:[:|\|]|%7C)(?<rest>.*)?,';

    /**
     * Mimetype regular expression pattern.
     *
     * @link https://tools.ietf.org/html/rfc2397
     *
     * @var string
     */
    private const REGEXP_MIMETYPE = ',^\w+/[-.\w]+(?:\+[-.\w]+)?$,';

    /**
     * Base64 content regular expression pattern.
     *
     * @link https://tools.ietf.org/html/rfc2397
     *
     * @var string
     */
    private const REGEXP_BINARY = ',(;|^)base64$,';

    /**
     * Windows filepath regular expression pattern.
     * <root> contains both the volume and volume separator.
     *
     * @var string
     */
    private const REGEXP_WINDOW_PATH = ',^(?<root>[a-zA-Z][:|\|]),';

    /**
     * Maximum number of cached items.
     *
     * @var int
     */
    private const MAXIMUM_CACHED_ITEMS = 100;

    /**
     * All ASCII letters sorted by typical frequency of occurrence.
     *
     * @var string
     */
    private const ASCII = "\x20\x65\x69\x61\x73\x6E\x74\x72\x6F\x6C\x75\x64\x5D\x5B\x63\x6D\x70\x27\x0A\x67\x7C\x68\x76\x2E\x66\x62\x2C\x3A\x3D\x2D\x71\x31\x30\x43\x32\x2A\x79\x78\x29\x28\x4C\x39\x41\x53\x2F\x50\x22\x45\x6A\x4D\x49\x6B\x33\x3E\x35\x54\x3C\x44\x34\x7D\x42\x7B\x38\x46\x77\x52\x36\x37\x55\x47\x4E\x3B\x4A\x7A\x56\x23\x48\x4F\x57\x5F\x26\x21\x4B\x3F\x58\x51\x25\x59\x5C\x09\x5A\x2B\x7E\x5E\x24\x40\x60\x7F\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F";

    private readonly ?string $scheme;
    private readonly ?string $user;
    private readonly ?string $pass;
    private readonly ?string $userInfo;
    private readonly ?string $host;
    private readonly ?int $port;
    private readonly ?string $authority;
    private readonly string $path;
    private readonly ?string $query;
    private readonly ?string $fragment;
    private readonly string $uriAsciiString;
    private readonly string $uriUnicodeString;
    private readonly ?string $origin;

    private function __construct(
        ?string $scheme,
        ?string $user,
        #[SensitiveParameter] ?string $pass,
        ?string $host,
        ?int $port,
        string $path,
        ?string $query,
        ?string $fragment
    ) {
        $this->scheme = $this->formatScheme($scheme);
        $this->user = Encoder::encodeUser($user);
        $this->pass = Encoder::encodePassword($pass);
        $this->host = $this->formatHost($host);
        $this->port = $this->formatPort($port);
        $this->authority = UriString::buildAuthority([
            'scheme' => $this->scheme,
            'user' => $this->user,
            'pass' => $this->pass,
            'host' => $this->host,
            'port' => $this->port,
        ]);
        $this->path = $this->formatPath($path);
        $this->query = Encoder::encodeQueryOrFragment($query);
        $this->fragment = Encoder::encodeQueryOrFragment($fragment);
        $this->userInfo = null !== $this->pass ? $this->user.':'.$this->pass : $this->user;
        $this->uriAsciiString = UriString::buildUri($this->scheme, $this->authority, $this->path, $this->query, $this->fragment);
        $this->assertValidRfc3986Uri();
        $this->assertValidState();
        $this->origin = $this->setOrigin();
        $host = $this->getUnicodeHost();
        $this->uriUnicodeString = $host === $this->host
            ? $this->uriAsciiString
            : UriString::buildUri(
                $this->scheme,
                UriString::buildAuthority([...$this->toComponents(), ...['host' => $host]]),
                $this->path,
                $this->query,
                $this->fragment
            );
    }

    /**
     * Format the Scheme and Host component.
     *
     * @throws SyntaxError if the scheme is invalid
     */
    private function formatScheme(?string $scheme): ?string
    {
        if (null === $scheme) {
            return null;
        }

        $formattedScheme = strtolower($scheme);
        static $cache = [];
        if (isset($cache[$formattedScheme])) {
            return $formattedScheme;
        }

        null !== UriScheme::tryFrom($formattedScheme)
        || UriString::isValidScheme($formattedScheme)
        || throw new SyntaxError('The scheme `'.$scheme.'` is invalid.');


        $cache[$formattedScheme] = 1;
        if (self::MAXIMUM_CACHED_ITEMS < count($cache)) {
            array_shift($cache);
        }

        return $formattedScheme;
    }

    /**
     * Validate and Format the Host component.
     */
    private function formatHost(?string $host): ?string
    {
        return HostRecord::from($host)->toAscii();
    }

    /**
     * Format the Port component.
     *
     * @throws SyntaxError
     */
    private function formatPort(BackedEnum|int|null $port = null): ?int
    {
        if ($port instanceof BackedEnum) {
            $port = (string) $port->value;
            1 === preg_match('/^\d+$/', $port) || throw new SyntaxError('The port `'.$port.'` is invalid.');
            $port = (int) $port;
        }

        $defaultPort = null !== $this->scheme
            ? UriScheme::tryFrom($this->scheme)?->port()
            : null;

        return match (true) {
            null === $port, $defaultPort === $port => null,
            0 > $port => throw new SyntaxError('The port `'.$port.'` is invalid.'),
            default => $port,
        };
    }

    /**
     * Create a new instance from a string or a stringable structure or returns null on failure.
     */
    public static function tryNew(Rfc3986Uri|WhatWgUrl|Urn|Stringable|string $uri = ''): ?self
    {
        try {
            return self::new($uri);
        } catch (Throwable) {
            return null;
        }
    }

    /**
     * Create a new instance from a string.
     */
    public static function new(Rfc3986Uri|WhatWgUrl|Urn|BackedEnum|Stringable|string $uri = ''): self
    {
        if ($uri instanceof Rfc3986Uri) {
            return new self(
                $uri->getRawScheme(),
                $uri->getRawUsername(),
                $uri->getRawPassword(),
                $uri->getRawHost(),
                $uri->getPort(),
                $uri->getRawPath(),
                $uri->getRawQuery(),
                $uri->getRawFragment()
            );
        }

        if ($uri instanceof WhatWgUrl) {
            return new self(
                $uri->getScheme(),
                $uri->getUsername(),
                $uri->getPassword(),
                $uri->getAsciiHost(),
                $uri->getPort(),
                $uri->getPath(),
                $uri->getQuery(),
                $uri->getFragment(),
            );
        }

        if ($uri instanceof BackedEnum) {
            $uri = $uri->value;
        }

        $uri = (string) $uri;
        trim($uri) === $uri || throw new SyntaxError(sprintf('The uri `%s` contains invalid characters', $uri));

        return new self(...UriString::parse(str_replace(' ', '%20', $uri)));
    }

    /**
     * Returns a new instance from a URI and a Base URI.or null on failure.
     *
     * The returned URI must be absolute if a base URI is provided
     */
    public static function parse(Rfc3986Uri|WhatWgUrl|Urn|BackedEnum|Stringable|string $uri, Rfc3986Uri|WhatWgUrl|Urn|BackedEnum|Stringable|string|null $baseUri = null): ?self
    {
        try {
            if (null === $baseUri) {
                return self::new($uri);
            }

            if ($uri instanceof Rfc3986Uri) {
                $uri = $uri->toRawString();
            }

            if ($uri instanceof WhatWgUrl) {
                $uri = $uri->toAsciiString();
            }

            if ($baseUri instanceof Rfc3986Uri) {
                $baseUri = $baseUri->toRawString();
            }

            if ($baseUri instanceof WhatWgUrl) {
                $baseUri = $baseUri->toAsciiString();
            }

            return self::new(UriString::resolve($uri, $baseUri));
        } catch (Throwable) {
            return null;
        }
    }

    /**
     * Creates a new instance from a template.
     *
     * @throws TemplateCanNotBeExpanded if the variables are invalid or missing
     * @throws UriException if the resulting expansion cannot be converted to a UriInterface instance
     */
    public static function fromTemplate(BackedEnum|UriTemplate|Stringable|string $template, iterable $variables = []): self
    {
        return match (true) {
            $template instanceof UriTemplate => self::new($template->expand($variables)),
            $template instanceof UriTemplate\Template => self::new($template->expand($variables)),
            default => self::new(UriTemplate\Template::new($template)->expand($variables)),
        };
    }

    /**
     * Create a new instance from a hash representation of the URI similar
     * to PHP parse_url function result.
     *
     * @param InputComponentMap $components a hash representation of the URI similar to PHP parse_url function result
     */
    public static function fromComponents(array $components = []): self
    {
        $components += [
            'scheme' => null, 'user' => null, 'pass' => null, 'host' => null,
            'port' => null, 'path' => '', 'query' => null, 'fragment' => null,
        ];

        if (null === $components['path']) {
            $components['path'] = '';
        }

        return new self(
            $components['scheme'],
            $components['user'],
            $components['pass'],
            $components['host'],
            $components['port'],
            $components['path'],
            $components['query'],
            $components['fragment']
        );
    }

    /**
     * Create a new instance from a data file path.
     *
     * @param SplFileInfo|SplFileObject|resource|Stringable|string $path
     * @param ?resource $context
     *
     * @throws MissingFeature If ext/fileinfo is not installed
     * @throws SyntaxError If the file does not exist or is not readable
     */
    public static function fromFileContents(mixed $path, $context = null): self
    {
        FeatureDetection::supportsFileDetection();
        $finfo = new finfo(FILEINFO_MIME_TYPE);
        $bufferSize = 8192;

        /**
         * @return array{0: string, 1: string}
         */
        $fromFileObject = function (SplFileObject $path) use ($finfo, $bufferSize): array {
            $raw = $path->fread($bufferSize);
            false !== $raw || throw new SyntaxError('The file `'.$path.'` does not exist or is not readable.');

            $mimetype = (string) $finfo->buffer($raw);
            while (!$path->eof()) {
                $raw .= $path->fread($bufferSize);
            }

            return [$mimetype, $raw];
        };

        /**
         * @param resource $stream
         * @return array{0: string, 1: string}
         */
        $fromResource = function ($stream) use ($finfo, $path, $bufferSize): array {
            set_error_handler(fn (int $errno, string $errstr, string $errfile, int $errline) => true);
            $raw = fread($stream, $bufferSize);
            false !== $raw || throw new SyntaxError('The file `'.$path.'` does not exist or is not readable.');

            $mimetype = (string) $finfo->buffer($raw);
            while (!feof($stream)) {
                $raw .= fread($stream, $bufferSize);
            }
            restore_error_handler();

            return [$mimetype, $raw];
        };

        /**
         * @param resource|null $context
         * @return array{0: string, 1: string}
         */
        $fromPath = function (Stringable|string $path, $context) use ($finfo): array {
            $path = (string) $path;
            set_error_handler(fn (int $errno, string $errstr, string $errfile, int $errline) => true);
            $raw = file_get_contents(filename: $path, context: $context);
            restore_error_handler();
            false !== $raw || throw new SyntaxError('The file `'.$path.'` does not exist or is not readable.');
            $mimetype = (string) $finfo->file(filename: $path, flags: FILEINFO_MIME, context: $context);

            return [$mimetype, $raw];
        };

        [$mimetype, $raw] = match (true) {
            $path instanceof SplFileObject => $fromFileObject($path),
            $path instanceof SplFileInfo => $fromFileObject($path->openFile(mode: 'rb', context: $context)),
            is_resource($path) => $fromResource($path),
            $path instanceof Stringable,
            is_string($path) => $fromPath($path, $context),
            default => throw new TypeError('The path `'.$path.'` is not a valid resource.'),
        };

        return Uri::fromComponents([
            'scheme' => 'data',
            'path' => str_replace(' ', '', $mimetype.';base64,'.base64_encode($raw)),
        ]);
    }

    /**
     * Create a new instance from a data URI string.
     *
     * @throws SyntaxError If the parameter syntax is invalid
     */
    public static function fromData(BackedEnum|Stringable|string $data, string $mimetype = '', string $parameters = ''): self
    {
        static $regexpMimetype = ',^\w+/[-.\w]+(?:\+[-.\w]+)?$,';

        $mimetype = match (true) {
            '' === $mimetype => 'text/plain',
            1 === preg_match($regexpMimetype, $mimetype) =>  $mimetype,
            default => throw new SyntaxError('Invalid mimeType, `'.$mimetype.'`.'),
        };

        if ($data instanceof BackedEnum) {
            $data = $data->value;
        }

        $data = (string) $data;
        if ('' === $parameters) {
            return self::fromComponents([
                'scheme' => 'data',
                'path' => self::formatDataPath($mimetype.','.rawurlencode($data)),
            ]);
        }

        $isInvalidParameter = static function (string $parameter): bool {
            $properties = explode('=', $parameter);

            return 2 !== count($properties) || 'base64' === strtolower($properties[0]);
        };

        if (str_starts_with($parameters, ';')) {
            $parameters = substr($parameters, 1);
        }

        return match ([]) {
            array_filter(explode(';', $parameters), $isInvalidParameter) => self::fromComponents([
                'scheme' => 'data',
                'path' => self::formatDataPath($mimetype.';'.$parameters.','.rawurlencode($data)),
            ]),
            default => throw new SyntaxError(sprintf('Invalid mediatype parameters, `%s`.', $parameters))
        };
    }

    /**
     * Create a new instance from a Unix path string.
     */
    public static function fromUnixPath(BackedEnum|Stringable|string $path): self
    {
        if ($path instanceof BackedEnum) {
            $path = $path->value;
        }

        $path = implode('/', array_map(rawurlencode(...), explode('/', (string) $path)));

        return Uri::fromComponents(match (true) {
            '/' !== ($path[0] ?? '') => ['path' => $path],
            default => ['path' => $path, 'scheme' => 'file', 'host' => ''],
        });
    }

    /**
     * Create a new instance from a local Windows path string.
     */
    public static function fromWindowsPath(BackedEnum|Stringable|string $path): self
    {
        if ($path instanceof BackedEnum) {
            $path = $path->value;
        }

        $root = '';
        $path = (string) $path;
        if (1 === preg_match(self::REGEXP_WINDOW_PATH, $path, $matches)) {
            $root = substr($matches['root'], 0, -1).':';
            $path = substr($path, strlen($root));
        }
        $path = str_replace('\\', '/', $path);
        $path = implode('/', array_map(rawurlencode(...), explode('/', $path)));

        //Local Windows absolute path
        if ('' !== $root) {
            return Uri::fromComponents(['path' => '/'.$root.$path, 'scheme' => 'file', 'host' => '']);
        }

        //UNC Windows Path
        if (!str_starts_with($path, '//')) {
            return Uri::fromComponents(['path' => $path]);
        }

        [$host, $path] = explode('/', substr($path, 2), 2) + [1 => ''];

        return Uri::fromComponents(['host' => $host, 'path' => '/'.$path, 'scheme' => 'file']);
    }

    /**
     * Creates a new instance from a RFC8089 compatible URI.
     *
     * @see https://datatracker.ietf.org/doc/html/rfc8089
     */
    public static function fromRfc8089(BackedEnum|Stringable|string $uri): static
    {
        if ($uri instanceof BackedEnum) {
            $uri = $uri->value;
        }

        $fileUri = self::new((string) preg_replace(',^(file:/)([^/].*)$,i', 'file:///$2', (string) $uri));
        $scheme = $fileUri->getScheme();

        return match (true) {
            'file' !== $scheme => throw new SyntaxError('As per RFC8089, the URI scheme must be `file`.'),
            'localhost' === $fileUri->getAuthority() => $fileUri->withHost(''),
            default => $fileUri,
        };
    }

    /**
     * Create a new instance from the environment.
     */
    public static function fromServer(array $server): self
    {
        $components = ['scheme' => self::fetchScheme($server)];
        [$components['user'], $components['pass']] = self::fetchUserInfo($server);
        [$components['host'], $components['port']] = self::fetchHostname($server);
        [$components['path'], $components['query']] = self::fetchRequestUri($server);

        return Uri::fromComponents($components);
    }

    /**
     * Returns the environment scheme.
     */
    private static function fetchScheme(array $server): string
    {
        $server += ['HTTPS' => ''];

        return match (true) {
            false !== filter_var($server['HTTPS'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) => 'https',
            default => 'http',
        };
    }

    /**
     * Returns the environment user info.
     *
     * @return non-empty-array {0: ?string, 1: ?string}
     */
    private static function fetchUserInfo(array $server): array
    {
        $server += ['PHP_AUTH_USER' => null, 'PHP_AUTH_PW' => null, 'HTTP_AUTHORIZATION' => ''];
        $user = $server['PHP_AUTH_USER'];
        $pass = $server['PHP_AUTH_PW'];
        if (str_starts_with(strtolower($server['HTTP_AUTHORIZATION']), 'basic')) {
            $userinfo = base64_decode(substr($server['HTTP_AUTHORIZATION'], 6), true);
            false !== $userinfo || throw new SyntaxError('The user info could not be detected');
            [$user, $pass] = explode(':', $userinfo, 2) + [1 => null];
        }

        if (null !== $user) {
            $user = rawurlencode($user);
        }

        if (null !== $pass) {
            $pass = rawurlencode($pass);
        }

        return [$user, $pass];
    }

    /**
     * Returns the environment host.
     *
     * @throws SyntaxError If the host cannot be detected
     *
     * @return array{0:string|null, 1:int|null}
     */
    private static function fetchHostname(array $server): array
    {
        $server += ['SERVER_PORT' => null];
        if (null !== $server['SERVER_PORT']) {
            $server['SERVER_PORT'] = (int) $server['SERVER_PORT'];
        }

        if (isset($server['HTTP_HOST']) && 1 === preg_match(self::REGEXP_HOST_PORT, $server['HTTP_HOST'], $matches)) {
            $matches += ['host' => null, 'port' => null];
            if (null !== $matches['port']) {
                $matches['port'] = (int) $matches['port'];
            }

            return [$matches['host'], $matches['port'] ?? $server['SERVER_PORT']];
        }

        isset($server['SERVER_ADDR']) || throw new SyntaxError('The host could not be detected');
        if (false === filter_var($server['SERVER_ADDR'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
            return ['['.$server['SERVER_ADDR'].']', $server['SERVER_PORT']];
        }

        return [$server['SERVER_ADDR'], $server['SERVER_PORT']];
    }

    /**
     * Returns the environment path.
     *
     * @return list<?string>
     */
    private static function fetchRequestUri(array $server): array
    {
        $server += ['IIS_WasUrlRewritten' => null, 'UNENCODED_URL' => '', 'PHP_SELF' => '', 'QUERY_STRING' => null];
        if ('1' === $server['IIS_WasUrlRewritten'] && '' !== $server['UNENCODED_URL']) {
            return array_values(explode('?', $server['UNENCODED_URL'], 2) + [1 => null]);
        }

        if (isset($server['REQUEST_URI'])) {
            [$path] = explode('?', $server['REQUEST_URI'], 2);
            $query = ('' !== $server['QUERY_STRING']) ? $server['QUERY_STRING'] : null;

            return [$path, $query];
        }

        return [$server['PHP_SELF'], $server['QUERY_STRING']];
    }

    /**
     * Format the Path component.
     */
    private function formatPath(string $path): string
    {
        $path = match ($this->scheme) {
            'data' => Encoder::encodePath(self::formatDataPath($path)),
            'file' => self::formatFilePath(Encoder::encodePath($path)),
            default => Encoder::encodePath($path),
        };

        if ('' === $path) {
            return $path;
        }

        if (null !== $this->authority) {
            // If there is an authority, the path must start with a `/`
            return str_starts_with($path, '/') ? $path : '/'.$path;
        }

        // If there is no authority, the path cannot start with `//`
        if (str_starts_with($path, '//')) {
            return '/.'.$path;
        }

        $colonPos = strpos($path, ':');
        if (false !== $colonPos && null === $this->scheme) {
            // In the absence of a scheme and of an authority,
            // the first path segment cannot contain a colon (":") character.'
            $slashPos = strpos($path, '/');
            (false !== $slashPos && $colonPos > $slashPos) || throw new SyntaxError(
                'In absence of the scheme and authority components, the first path segment cannot contain a colon (":") character.'
            );
        }

        return $path;
    }

    /**
     * Filter the Path component.
     *
     * @link https://tools.ietf.org/html/rfc2397
     *
     * @throws SyntaxError If the path is not compliant with RFC2397
     */
    private static function formatDataPath(string $path): string
    {
        if ('' === $path) {
            return 'text/plain;charset=us-ascii,';
        }

        if (strlen($path) !== strspn($path, self::ASCII) || !str_contains($path, ',')) {
            throw new SyntaxError('The path `'.$path.'` is invalid according to RFC2937.');
        }

        $parts = explode(',', $path, 2) + [1 => null];
        $mediatype = explode(';', (string) $parts[0], 2) + [1 => null];
        $data = (string) $parts[1];
        $mimetype = $mediatype[0];
        if (null === $mimetype || '' === $mimetype) {
            $mimetype = 'text/plain';
        }

        $parameters = $mediatype[1];
        if (null === $parameters || '' === $parameters) {
            $parameters = 'charset=us-ascii';
        }

        self::assertValidPath($mimetype, $parameters, $data);

        return $mimetype.';'.$parameters.','.$data;
    }

    /**
     * Assert the path is a compliant with RFC2397.
     *
     * @link https://tools.ietf.org/html/rfc2397
     *
     * @throws SyntaxError If the mediatype or the data are not compliant with the RFC2397
     */
    private static function assertValidPath(string $mimetype, string $parameters, string $data): void
    {
        1 === preg_match(self::REGEXP_MIMETYPE, $mimetype) || throw new SyntaxError('The path mimetype `'.$mimetype.'` is invalid.');
        $isBinary = 1 === preg_match(self::REGEXP_BINARY, $parameters, $matches);
        if ($isBinary) {
            $parameters = substr($parameters, 0, - strlen($matches[0]));
        }

        $res = array_filter(
            array_filter(explode(';', $parameters), self::validateParameter(...)),
            static fn ($v): bool => null !== $v,
        );
        [] === $res || throw new SyntaxError('The path parameters `'.$parameters.'` is invalid.');
        if (!$isBinary) {
            return;
        }

        $res = base64_decode($data, true);
        if (false === $res || $data !== base64_encode($res)) {
            throw new SyntaxError('The path data `'.$data.'` is invalid.');
        }
    }

    /**
     * Validate mediatype parameter.
     */
    private static function validateParameter(string $parameter): bool
    {
        $properties = explode('=', $parameter);

        return 2 !== count($properties) || 'base64' === strtolower($properties[0]);
    }

    /**
     * Format the path component for the URI scheme file.
     */
    private static function formatFilePath(string $path): string
    {
        return (string) preg_replace_callback(
            self::REGEXP_FILE_PATH,
            static fn (array $matches): string => $matches['delim'].$matches['volume'].(isset($matches['rest']) ? ':'.$matches['rest'] : ''),
            $path
        );
    }

    /**
     * assert the URI internal state is valid.
     *
     * @link https://tools.ietf.org/html/rfc3986#section-3
     * @link https://tools.ietf.org/html/rfc3986#section-3.3
     *
     * @throws SyntaxError if the URI is in an invalid state, according to RFC3986
     */
    private function assertValidRfc3986Uri(): void
    {
        if (null !== $this->authority && ('' !== $this->path && '/' !== $this->path[0])) {
            throw new SyntaxError('If an authority is present the path must be empty or start with a `/`.');
        }

        if (null === $this->authority && str_starts_with($this->path, '//')) {
            throw new SyntaxError('If there is no authority the path `'.$this->path.'` cannot start with a `//`.');
        }

        $pos = strpos($this->path, ':');
        if (null === $this->authority
            && null === $this->scheme
            && false !== $pos
            && !str_contains(substr($this->path, 0, $pos), '/')
        ) {
            throw new SyntaxError('In absence of a scheme and an authority the first path segment cannot contain a colon (":") character.');
        }
    }

    /**
     * assert the URI scheme is valid.
     *
     * @link https://w3c.github.io/FileAPI/#url
     * @link https://datatracker.ietf.org/doc/html/rfc2397
     * @link https://tools.ietf.org/html/rfc3986#section-3
     * @link https://tools.ietf.org/html/rfc3986#section-3.3
     *
     * @throws SyntaxError if the URI is in an invalid state, according to scheme-specific rules
     */
    private function assertValidState(): void
    {
        $scheme = UriScheme::tryFrom((string) $this->scheme);
        if (null === $scheme) {
            return;
        }

        $schemeType = $scheme->type();
        match ($scheme) {
            UriScheme::Blob => $this->isValidBlob(),
            UriScheme::Mailto => $this->isValidMailto(),
            UriScheme::Data,
            UriScheme::About,
            UriScheme::Javascript => $this->isUriWithSchemeAndPathOnly(),
            UriScheme::File => $this->isUriWithSchemeHostAndPathOnly(),
            UriScheme::Ftp,
            UriScheme::Gopher,
            UriScheme::Afp,
            UriScheme::Dict,
            UriScheme::Msrps,
            UriScheme::Msrp,
            UriScheme::Mtqp,
            UriScheme::Rsync,
            UriScheme::Ssh,
            UriScheme::Svn,
            UriScheme::Snmp => $this->isNonEmptyHostUriWithoutFragmentAndQuery(),
            UriScheme::Https,
            UriScheme::Http => $this->isNonEmptyHostUri(),
            UriScheme::Ws,
            UriScheme::Wss,
            UriScheme::Ipp,
            UriScheme::Ipps => $this->isNonEmptyHostUriWithoutFragment(),
            UriScheme::Ldap,
            UriScheme::Ldaps,
            UriScheme::Acap,
            UriScheme::Imaps,
            UriScheme::Imap,
            UriScheme::Redis => null === $this->fragment,
            UriScheme::Prospero => null === $this->fragment && null === $this->query && null === $this->userInfo,
            UriScheme::Urn => null !== Urn::parse($this->uriAsciiString),
            UriScheme::Telnet,
            UriScheme::Tn3270 => null === $this->fragment && null === $this->query && in_array($this->path, ['', '/'], true),
            UriScheme::Vnc => null !==  $this->authority && null === $this->fragment && '' === $this->path,
            default => $schemeType->isUnknown()
                || ($schemeType->isOpaque() && null === $this->authority)
                || ($schemeType->isHierarchical() && null !== $this->authority),
        } || throw new SyntaxError('The uri `'.$this->uriAsciiString.'` is invalid for the `'.$this->scheme.'` scheme.');
    }

    private function isValidBlob(): bool
    {
        static $regexpUuidRfc4122 = '/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i';

        if (!$this->isUriWithSchemeAndPathOnly()
            || '' === $this->path
            || !str_contains($this->path, '/')
            || str_ends_with($this->path, '/')
            || 1 !== preg_match($regexpUuidRfc4122, basename($this->path))
        ) {
            return false;
        }

        $origin = dirname($this->path);
        if ('null' === $origin) {
            return true;
        }

        try {
            $components = UriString::parse($origin);

            return '' === $components['path']
                && null === $components['query']
                && null === $components['fragment']
                && true === UriScheme::tryFrom((string) $components['scheme'])?->isWhatWgSpecial();
        } catch (UriException) {
            return false;
        }
    }

    private function isValidMailto(): bool
    {
        if (null !== $this->authority || null !== $this->fragment || str_contains((string) $this->query, '?')) {
            return false;
        }

        static $mailHeaders = [
            'to', 'cc', 'bcc', 'reply-to', 'from', 'sender',
            'resent-to', 'resent-cc', 'resent-bcc', 'resent-from', 'resent-sender',
            'return-path', 'delivery-to', 'site-owner',
        ];

        static $headerRegexp = '/^[a-zA-Z0-9\'`#$%&*+.^_|~!-]+$/D';
        $pairs = QueryString::parseFromValue($this->query);
        $hasTo = false;
        foreach ($pairs as [$name, $value]) {
            $headerName = strtolower($name);
            if (in_array($headerName, $mailHeaders, true)) {
                if (null === $value || !self::validateEmailList($value)) {
                    return false;
                }

                if (!$hasTo && 'to' === $headerName) {
                    $hasTo = true;
                }
                continue;
            }

            if (1 !== preg_match($headerRegexp, (string) Encoder::decodeAll($name))) {
                return false;
            }
        }

        return '' === $this->path ? $hasTo : self::validateEmailList($this->path);
    }

    private static function validateEmailList(string $emails): bool
    {
        foreach (explode(',', $emails) as $email) {
            if (false === filter_var((string) Encoder::decodeAll($email), FILTER_VALIDATE_EMAIL)) {
                return false;
            }
        }

        return '' !== $emails;
    }

    /**
     * Sets the URI origin.
     *
     * The origin read-only property of the URL interface returns a string containing
     * the Unicode serialization of the represented URL.
     */
    private function setOrigin(): ?string
    {
        try {
            if ('blob' !== $this->scheme) {
                if (!(UriScheme::tryFrom($this->scheme ?? '')?->isWhatWgSpecial() ?? false)) {
                    return null;
                }

                $host = $this->host;
                $converted = $host;
                if (null !== $converted) {
                    try {
                        $converted = IPv4Converter::fromEnvironment()->toDecimal($host);
                    } catch (MissingFeature) {
                        $converted = null;
                    }

                    if (false === filter_var($converted, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
                        $converted = IPv6Converter::compress($host);
                    }

                    /** @var string $converted */
                    if ($converted !== $host) {
                        $converted = Idna\Converter::toAscii($converted)->domain();
                    }
                }

                return $this
                    ->withFragment(null)
                    ->withQuery(null)
                    ->withPath('')
                    ->withUserInfo(null)
                    ->withHost($converted)
                    ->toString();
            }

            $components = UriString::parse($this->path);
            $scheme = strtolower($components['scheme'] ?? '');
            if (! (UriScheme::tryFrom($scheme)?->isWhatWgSpecial() ?? false)) {
                return null;
            }

            return self::fromComponents($components)->origin;
        } catch (UriException) {
            return null;
        }
    }

    /**
     * URI validation for URI schemes which allows only scheme and path components.
     */
    private function isUriWithSchemeAndPathOnly(): bool
    {
        return null === $this->authority
            && null === $this->query
            && null === $this->fragment;
    }

    /**
     * URI validation for URI schemes which allows only scheme, host and path components.
     */
    private function isUriWithSchemeHostAndPathOnly(): bool
    {
        return null === $this->userInfo
            && null === $this->port
            && null === $this->query
            && null === $this->fragment
            && !('' !== $this->scheme && null !== $this->scheme && null === $this->host);
    }

    /**
     * URI validation for URI schemes which disallow the empty '' host.
     */
    private function isNonEmptyHostUri(): bool
    {
        return '' !== $this->host
            && !(null !== $this->scheme && null === $this->host);
    }

    /**
     * URI validation for URIs schemes which disallow the empty '' host
     * and forbids the fragment component.
     */
    private function isNonEmptyHostUriWithoutFragment(): bool
    {
        return $this->isNonEmptyHostUri() && null === $this->fragment;
    }

    /**
     * URI validation for URIs schemes which disallow the empty '' host
     * and forbids fragment and query components.
     */
    private function isNonEmptyHostUriWithoutFragmentAndQuery(): bool
    {
        return $this->isNonEmptyHostUri() && null === $this->fragment && null === $this->query;
    }

    public function __toString(): string
    {
        return $this->toString();
    }

    /**
     * Returns the string representation as a URI reference.
     *
     * @see http://tools.ietf.org/html/rfc3986#section-4.1
     * @see ::toString
     */
    public function jsonSerialize(): string
    {
        return $this->toString();
    }

    /**
     * Returns the string representation as a URI reference.
     *
     * @see http://tools.ietf.org/html/rfc3986#section-4.1
     */
    public function toString(): string
    {
        return $this->toAsciiString();
    }

    /**
     * Returns the string representation as a URI reference.
     *
     * @see http://tools.ietf.org/html/rfc3986#section-4.1
     */
    public function toAsciiString(): string
    {
        return $this->uriAsciiString;
    }

    /**
     * Returns the string representation as a URI reference.
     *
     * The host is converted to its UNICODE representation if available
     */
    public function toUnicodeString(): string
    {
        return $this->uriUnicodeString;
    }

    /**
     * Returns the human-readable string representation of the URI as an IRI.
     *
     * @see https://datatracker.ietf.org/doc/html/rfc3987
     */
    public function toDisplayString(): string
    {
        return UriString::toIriString($this->toString());
    }

    /**
     * Returns the Unix filesystem path.
     *
     * The method will return null if a scheme is present and is not the `file` scheme
     */
    public function toUnixPath(): ?string
    {
        return match ($this->scheme) {
            'file', null => rawurldecode($this->path),
            default => null,
        };
    }

    /**
     * Returns the Windows filesystem path.
     *
     * The method will return null if a scheme is present and is not the `file` scheme
     */
    public function toWindowsPath(): ?string
    {
        static $regexpWindowsPath = ',^(?<root>[a-zA-Z]:),';

        if (!in_array($this->scheme, ['file', null], true)) {
            return null;
        }

        $originalPath = $this->path;
        $path = $originalPath;
        if ('/' === ($path[0] ?? '')) {
            $path = substr($path, 1);
        }

        if (1 === preg_match($regexpWindowsPath, $path, $matches)) {
            $root = $matches['root'];
            $path = substr($path, strlen($root));

            return $root.str_replace('/', '\\', rawurldecode($path));
        }

        $host = $this->host;

        return match (null) {
            $host => str_replace('/', '\\', rawurldecode($originalPath)),
            default => '\\\\'.$host.'\\'.str_replace('/', '\\', rawurldecode($path)),
        };
    }

    /**
     * Returns a string representation of a File URI according to RFC8089.
     *
     * The method will return null if the URI scheme is not the `file` scheme
     *
     * @see https://datatracker.ietf.org/doc/html/rfc8089
     */
    public function toRfc8089(): ?string
    {
        $path = $this->path;

        return match (true) {
            'file' !== $this->scheme => null,
            in_array($this->authority, ['', null, 'localhost'], true) => 'file:'.match (true) {
                '' === $path,
                '/' === $path[0] => $path,
                default => '/'.$path,
            },
            default => $this->toString(),
        };
    }

    /**
     * Save the data to a specific file.
     *
     * The method returns the number of bytes written to the file
     * or null for any other scheme except the data scheme
     *
     * @param SplFileInfo|SplFileObject|resource|Stringable|string $destination
     * @param ?resource $context
     *
     * @throws RuntimeException if the content cannot be stored.
     */
    public function toFileContents(mixed $destination, $context = null): ?int
    {
        if ('data' !== $this->scheme) {
            return null;
        }

        [$mediaType, $document] = explode(',', $this->path, 2) + [0 => '', 1 => null];
        null !== $document || throw new RuntimeException('Unable to extract the document part from the URI path.');

        $data = match (true) {
            str_ends_with($mediaType, ';base64') => (string) base64_decode($document, true),
            default => rawurldecode($document),
        };

        $res = match (true) {
            $destination instanceof SplFileObject => $destination->fwrite($data),
            $destination instanceof SplFileInfo => $destination->openFile(mode:'wb', context: $context)->fwrite($data),
            is_resource($destination) => fwrite($destination, $data),
            $destination instanceof Stringable,
            is_string($destination) => (function () use ($destination, $data, $context): int|false {
                set_error_handler(fn (int $errno, string $errstr, string $errfile, int $errline) => true);
                $rsrc = fopen((string) $destination, mode:'wb', context: $context);
                if (false === $rsrc) {
                    restore_error_handler();
                    throw new RuntimeException('Unable to open the destination file: '.$destination);
                }

                $bytes = fwrite($rsrc, $data);
                fclose($rsrc);
                restore_error_handler();

                return $bytes;
            })(),
            default => throw new TypeError('Unsupported destination type; expected SplFileObject, SplFileInfo, resource or a string; '.(is_object($destination) ? $destination::class : gettype($destination)).' given.'),
        };

        false !== $res || throw new RuntimeException('Unable to write to the destination file.');

        return $res;
    }

    /**
     * Returns an associative array containing all the URI components.
     *
     * @return ComponentMap
     */
    public function toComponents(): array
    {
        return [
            'scheme' => $this->scheme,
            'user' => $this->user,
            'pass' => $this->pass,
            'host' => $this->host,
            'port' => $this->port,
            'path' => $this->path,
            'query' => $this->query,
            'fragment' => $this->fragment,
        ];
    }

    public function getScheme(): ?string
    {
        return $this->scheme;
    }

    public function getAuthority(): ?string
    {
        return $this->authority;
    }

    /**
     * Returns the user component encoded value.
     *
     * @see https://wiki.php.net/rfc/url_parsing_api
     */
    public function getUsername(): ?string
    {
        return $this->user;
    }

    public function getPassword(): ?string
    {
        return $this->pass;
    }

    public function getUserInfo(): ?string
    {
        return $this->userInfo;
    }

    public function getHost(): ?string
    {
        return $this->host;
    }

    public function getUnicodeHost(): ?string
    {
        if (null === $this->host) {
            return null;
        }

        $host = IdnaConverter::toUnicode($this->host)->domain();
        if ($host === $this->host) {
            return $this->host;
        }

        return $host;
    }

    public function isIpv4Host(): bool
    {
        return HostRecord::isIpv4($this->host);
    }

    public function isIpv6Host(): bool
    {
        return HostRecord::isIpv6($this->host);
    }

    public function isIpvFutureHost(): bool
    {
        return HostRecord::isIpvFuture($this->host);
    }

    public function isIpHost(): bool
    {
        return HostRecord::isIp($this->host);
    }

    public function isRegisteredNameHost(): bool
    {
        return HostRecord::isRegisteredName($this->host);
    }

    public function isDomainHost(): bool
    {
        return HostRecord::isDomain($this->host);
    }

    public function getPort(): ?int
    {
        return $this->port;
    }

    public function getPath(): string
    {
        return $this->path;
    }

    public function getQuery(): ?string
    {
        return $this->query;
    }

    public function getFragment(): ?string
    {
        return $this->fragment;
    }

    public function getOrigin(): ?string
    {
        return $this->origin;
    }

    public function when(callable|bool $condition, callable $onSuccess, ?callable $onFail = null): static
    {
        if (!is_bool($condition)) {
            $condition = $condition($this);
        }

        return match (true) {
            $condition => $onSuccess($this),
            null !== $onFail => $onFail($this),
            default => $this,
        } ?? $this;
    }

    public function transform(callable $callback): static
    {
        return $callback($this);
    }

    public function withScheme(BackedEnum|Stringable|string|null $scheme): static
    {
        $scheme = $this->formatScheme($this->filterString($scheme));

        return match ($scheme) {
            $this->scheme => $this,
            default => new self($scheme, $this->user, $this->pass, $this->host, $this->port, $this->path, $this->query, $this->fragment),
        };
    }

    /**
     * Filter a string.
     *
     * @throws SyntaxError if the submitted data cannot be converted to string
     */
    private function filterString(BackedEnum|Stringable|string|null $str): ?string
    {
        $str = match (true) {
            $str instanceof FragmentDirective => $str->toFragmentValue(),
            $str instanceof UriComponentInterface => $str->value(),
            $str instanceof BackedEnum => (string) $str->value,
            null === $str => null,
            default => (string) $str,
        };

        return match (true) {
            null === $str => null,
            1 === preg_match(self::REGEXP_INVALID_CHARS, $str) => throw new SyntaxError('The component `'.$str.'` contains invalid characters.'),
            default => $str,
        };
    }

    public function withUserInfo(
        BackedEnum|Stringable|string|null $user,
        #[SensitiveParameter] BackedEnum|Stringable|string|null $password = null
    ): static {
        $user = Encoder::encodeUser($this->filterString($user));
        $pass = Encoder::encodePassword($this->filterString($password));
        $userInfo = $user;
        if (null !== $password) {
            $userInfo .= ':'.$pass;
        }

        return match ($userInfo) {
            $this->userInfo => $this,
            default => new self($this->scheme, $user, $pass, $this->host, $this->port, $this->path, $this->query, $this->fragment),
        };
    }

    public function withUsername(BackedEnum|Stringable|string|null $user): static
    {
        return $this->withUserInfo($user, $this->pass);
    }

    public function withPassword(#[SensitiveParameter] BackedEnum|Stringable|string|null $password): static
    {
        return $this->withUserInfo($this->user, $password);
    }

    public function withHost(BackedEnum|Stringable|string|null $host): static
    {
        $host = $this->formatHost($this->filterString($host));

        return match ($host) {
            $this->host => $this,
            default => new self($this->scheme, $this->user, $this->pass, $host, $this->port, $this->path, $this->query, $this->fragment),
        };
    }

    public function withPort(BackedEnum|int|null $port): static
    {
        $port = $this->formatPort($port);

        return match ($port) {
            $this->port => $this,
            default => new self($this->scheme, $this->user, $this->pass, $this->host, $port, $this->path, $this->query, $this->fragment),
        };
    }

    public function withPath(BackedEnum|Stringable|string $path): static
    {
        $path = $this->formatPath($this->filterString($path) ?? throw new SyntaxError('The path component cannot be null.'));

        return match ($path) {
            $this->path => $this,
            default => new self($this->scheme, $this->user, $this->pass, $this->host, $this->port, $path, $this->query, $this->fragment),
        };
    }

    public function withQuery(BackedEnum|Stringable|string|null $query): static
    {
        $query = Encoder::encodeQueryOrFragment($this->filterString($query));

        return match ($query) {
            $this->query => $this,
            default => new self($this->scheme, $this->user, $this->pass, $this->host, $this->port, $this->path, $query, $this->fragment),
        };
    }

    public function withFragment(BackedEnum|Stringable|string|null $fragment): static
    {
        $fragment = Encoder::encodeQueryOrFragment($this->filterString($fragment));

        return match ($fragment) {
            $this->fragment => $this,
            default => new self($this->scheme, $this->user, $this->pass, $this->host, $this->port, $this->path, $this->query, $fragment),
        };
    }

    /**
     * Tells whether the `file` scheme base URI represents a local file.
     */
    public function isLocalFile(): bool
    {
        return match (true) {
            'file' !== $this->scheme => false,
            in_array($this->authority, ['', null, 'localhost'], true) => true,
            default => false,
        };
    }

    /**
     * Tells whether the URI is opaque or not.
     *
     * A URI is opaque if and only if it is absolute
     * and does not have an authority path.
     */
    public function isOpaque(): bool
    {
        return null === $this->authority
            && null !== $this->scheme;
    }

    /**
     * Tells whether two URI do not share the same origin.
     */
    public function isCrossOrigin(Rfc3986Uri|WhatWgUrl|Urn|Stringable|string $uri): bool
    {
        if (null === $this->origin) {
            return true;
        }

        $uri = self::tryNew($uri);
        if (null === $uri || null === ($origin = $uri->getOrigin())) {
            return true;
        }

        return $this->origin !== $origin;
    }

    public function isSameOrigin(Rfc3986Uri|WhatWgUrl|Urn|Stringable|string $uri): bool
    {
        return ! $this->isCrossOrigin($uri);
    }

    /**
     * Tells whether the URI is absolute.
     */
    public function isAbsolute(): bool
    {
        return null !== $this->scheme;
    }

    /**
     * Tells whether the URI is a network path.
     */
    public function isNetworkPath(): bool
    {
        return null === $this->scheme
            && null !== $this->authority;
    }

    /**
     * Tells whether the URI is an absolute path.
     */
    public function isAbsolutePath(): bool
    {
        return null === $this->scheme
            && null === $this->authority
            && '/' === ($this->path[0] ?? '');
    }

    /**
     * Tells whether the URI is a relative path.
     */
    public function isRelativePath(): bool
    {
        return null === $this->scheme
            && null === $this->authority
            && '/' !== ($this->path[0] ?? '');
    }

    /**
     * Tells whether both URIs refer to the same document.
     */
    public function isSameDocument(Rfc3986Uri|WhatWgUrl|UriInterface|Stringable|Urn|string $uri): bool
    {
        return $this->equals($uri);
    }

    public function equals(Rfc3986Uri|WhatWgUrl|UriInterface|Stringable|Urn|string $uri, UriComparisonMode $uriComparisonMode = UriComparisonMode::ExcludeFragment): bool
    {
        if (!$uri instanceof UriInterface && !$uri instanceof Rfc3986Uri && !$uri instanceof WhatWgUrl) {
            $uri = self::tryNew($uri);
        }

        if (null === $uri) {
            return false;
        }

        $baseUri = $this;
        if (UriComparisonMode::ExcludeFragment === $uriComparisonMode) {
            $uri = $uri->withFragment(null);
            $baseUri = $baseUri->withFragment(null);
        }

        return $baseUri->normalize()->toString() === match (true) {
            $uri instanceof Rfc3986Uri => $uri->toString(),
            $uri instanceof WhatWgUrl => $uri->toAsciiString(),
            default => $uri->normalize()->toString(),
        };
    }

    /**
     * Normalize a URI by applying non-destructive and destructive normalization
     * rules as defined in RFC3986 and RFC3987.
     */
    public function normalize(): static
    {
        $uriString = $this->toString();
        if ('' === $uriString) {
            return $this;
        }

        $normalizedUriString = UriString::normalize($uriString);
        $normalizedUri = self::new($normalizedUriString);
        if (null !== $normalizedUri->getAuthority() && ('' === $normalizedUri->getPath() && (UriScheme::tryFrom($normalizedUri->getScheme() ?? '')?->isWhatWgSpecial() ?? false))) {
            $normalizedUri = $normalizedUri->withPath('/');
        }

        if ($normalizedUri->toString() === $uriString) {
            return $this;
        }

        return $normalizedUri;
    }

    /**
     * Resolves a URI against a base URI using RFC3986 rules.
     *
     * This method MUST retain the state of the submitted URI instance, and return
     * a URI instance of the same type that contains the applied modifications.
     *
     * This method MUST be transparent when dealing with errors and exceptions.
     * It MUST not alter or silence them apart from validating its own parameters.
     */
    public function resolve(Rfc3986Uri|WhatWgUrl|UriInterface|Stringable|Urn|BackedEnum|string $uri): static
    {
        return self::new(UriString::resolve(
            match (true) {
                $uri instanceof UriInterface,
                $uri instanceof Rfc3986Uri => $uri->toString(),
                $uri instanceof WhatWgUrl => $uri->toAsciiString(),
                $uri instanceof BackedEnum => (string) $uri->value,
                default => $uri,
            },
            $this->toString()
        ));
    }

    /**
     * Relativize a URI according to a base URI.
     *
     * This method MUST retain the state of the submitted URI instance, and return
     * a URI instance of the same type that contains the applied modifications.
     *
     * This method MUST be transparent when dealing with error and exceptions.
     * It MUST not alter of silence them apart from validating its own parameters.
     */
    public function relativize(Rfc3986Uri|WhatWgUrl|UriInterface|Stringable|Urn|BackedEnum|string $uri): static
    {
        $uri = self::new($uri);

        if (
            $this->scheme !== $uri->getScheme() ||
            $this->authority !== $uri->getAuthority() ||
            $uri->isRelativePath()) {
            return $uri;
        }

        $targetPath = $uri->getPath();
        $basePath = $this->path;

        $uri = $uri
            ->withScheme(null)
            ->withUserInfo(null)
            ->withPort(null)
            ->withHost(null);

        return match (true) {
            $targetPath !== $basePath => $uri->withPath(self::relativizePath($targetPath, $basePath)),
            $this->query === $uri->getQuery() => $uri->withPath('')->withQuery(null),
            null === $uri->getQuery() => $uri->withPath(self::formatPathWithEmptyBaseQuery($targetPath)),
            default => $uri->withPath(''),
        };
    }

    /**
     * Formatting the path to keep a resolvable URI.
     */
    private static function formatPathWithEmptyBaseQuery(string $path): string
    {
        $targetSegments = self::getSegments($path);
        $basename = $targetSegments[array_key_last($targetSegments)];

        return '' === $basename ? './' : $basename;
    }

    /**
     * Relatives the URI for an authority-less target URI.
     */
    private static function relativizePath(string $path, string $basePath): string
    {
        $baseSegments = self::getSegments($basePath);
        $targetSegments = self::getSegments($path);
        $targetBasename = array_pop($targetSegments);
        array_pop($baseSegments);
        foreach ($baseSegments as $offset => $segment) {
            if (!isset($targetSegments[$offset]) || $segment !== $targetSegments[$offset]) {
                break;
            }
            unset($baseSegments[$offset], $targetSegments[$offset]);
        }
        $targetSegments[] = $targetBasename;

        return static::formatRelativePath(
            str_repeat('../', count($baseSegments)).implode('/', $targetSegments),
            $basePath
        );
    }

    /**
     * Formatting the path to keep a valid URI.
     */
    private static function formatRelativePath(string $path, string $basePath): string
    {
        $colonPosition = strpos($path, ':');
        $slashPosition = strpos($path, '/');

        return match (true) {
            '' === $path => match (true) {
                '' === $basePath,
                '/' === $basePath => $basePath,
                default => './',
            },
            false === $colonPosition => $path,
            false === $slashPosition,
            $colonPosition < $slashPosition  =>  "./$path",
            default => $path,
        };
    }

    /**
     * returns the path segments.
     *
     * @return array<string>
     */
    private static function getSegments(string $path): array
    {
        return explode('/', match (true) {
            '' === $path,
            '/' !== $path[0] => $path,
            default => substr($path, 1),
        });
    }

    /**
     * @return ComponentMap
     */
    public function __debugInfo(): array
    {
        return $this->toComponents();
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.6.0
     * @codeCoverageIgnore
     * @see Uri::parse()
     *
     * Creates a new instance from a URI and a Base URI.
     *
     * The returned URI must be absolute.
     */
    #[Deprecated(message:'use League\Uri\Uri::parse() instead', since:'league/uri:7.6.0')]
    public static function fromBaseUri(WhatWgUrl|Rfc3986Uri|Stringable|string $uri, WhatWgUrl|Rfc3986Uri|Stringable|string|null $baseUri = null): self
    {
        $formatter = fn (WhatWgUrl|Rfc3986Uri|Stringable|string $uri): string => match (true) {
            $uri instanceof Rfc3986Uri => $uri->toRawString(),
            $uri instanceof WhatWgUrl => $uri->toAsciiString(),
            default => str_replace(' ', '%20', (string) $uri),
        };

        return self::new(
            UriString::resolve(
                uri: $formatter($uri),
                baseUri: null !== $baseUri ? $formatter($baseUri) : $baseUri
            )
        );
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.5.0
     * @codeCoverageIgnore
     * @see Uri::toComponents()
     *
     * @return ComponentMap
     */
    #[Deprecated(message:'use League\Uri\Uri::toComponents() instead', since:'league/uri:7.5.0')]
    public function getComponents(): array
    {
        return $this->toComponents();
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @codeCoverageIgnore
     * @see Uri::new()
     */
    #[Deprecated(message:'use League\Uri\Uri::new() instead', since:'league/uri:7.0.0')]
    public static function createFromString(Stringable|string $uri = ''): self
    {
        return self::new($uri);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @codeCoverageIgnore
     * @see Uri::fromComponents()
     *
     * @param InputComponentMap $components a hash representation of the URI similar to PHP parse_url function result
     */
    #[Deprecated(message:'use League\Uri\Uri::fromComponents() instead', since:'league/uri:7.0.0')]
    public static function createFromComponents(array $components = []): self
    {
        return self::fromComponents($components);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @param resource|null $context
     *
     * @throws MissingFeature If ext/fileinfo is not installed
     * @throws SyntaxError If the file does not exist or is not readable
     * @see Uri::fromFileContents()
     *
     * @deprecated Since version 7.0.0
     * @codeCoverageIgnore
     */
    #[Deprecated(message:'use League\Uri\Uri::fromDataPath() instead', since:'league/uri:7.0.0')]
    public static function createFromDataPath(string $path, $context = null): self
    {
        return self::fromFileContents($path, $context);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @codeCoverageIgnore
     * @see Uri::fromBaseUri()
     *
     * Creates a new instance from a URI and a Base URI.
     *
     * The returned URI must be absolute.
     */
    #[Deprecated(message:'use League\Uri\Uri::fromBaseUri() instead', since:'league/uri:7.0.0')]
    public static function createFromBaseUri(
        Stringable|UriInterface|String $uri,
        Stringable|UriInterface|String|null $baseUri = null
    ): static {
        return self::fromBaseUri($uri, $baseUri);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @codeCoverageIgnore
     * @see Uri::fromUnixPath()
     *
     * Create a new instance from a Unix path string.
     */
    #[Deprecated(message:'use League\Uri\Uri::fromUnixPath() instead', since:'league/uri:7.0.0')]
    public static function createFromUnixPath(string $uri = ''): self
    {
        return self::fromUnixPath($uri);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @codeCoverageIgnore
     * @see Uri::fromWindowsPath()
     *
     * Create a new instance from a local Windows path string.
     */
    #[Deprecated(message:'use League\Uri\Uri::fromWindowsPath() instead', since:'league/uri:7.0.0')]
    public static function createFromWindowsPath(string $uri = ''): self
    {
        return self::fromWindowsPath($uri);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @codeCoverageIgnore
     * @see Uri::new()
     *
     * Create a new instance from a URI object.
     */
    #[Deprecated(message:'use League\Uri\Uri::new() instead', since:'league/uri:7.0.0')]
    public static function createFromUri(Psr7UriInterface|UriInterface $uri): self
    {
        return self::new($uri);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @codeCoverageIgnore
     * @see Uri::fromServer()
     *
     * Create a new instance from the environment.
     */
    #[Deprecated(message:'use League\Uri\Uri::fromServer() instead', since:'league/uri:7.0.0')]
    public static function createFromServer(array $server): self
    {
        return self::fromServer($server);
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\UriTemplate;

use function array_chunk;
use function array_column;
use function array_key_exists;
use function array_map;
use function array_unique;
use function count;
use function explode;
use function in_array;
use function is_array;
use function is_string;

final class ExtractedValue
{
    public readonly bool $isPartial;

    /**
     * @throws VariableCanNotBeExtracted
     */
    private function __construct(
        public readonly array|string|null $value,
        private readonly int $maxLength = -1,
        public readonly array $asList = [],
    ) {
        if (is_array($value)) {
            foreach ($value as $key => $item) {
                is_string($item) || throw VariableCanNotBeExtracted::dueTo('The extracted value contains a list with an invalid value at key "'.$key.'".', ExtractionErrorReason::TypeMismatch);
            }
        }
        - 1 === $maxLength || (is_string($value) && 0 < $maxLength) || throw VariableCanNotBeExtracted::dueTo('A prefix position can only be associated with a string value.', ExtractionErrorReason::UnsupportedOperation);

        $this->isPartial = -1 !== $maxLength;
    }

    public static function fromNull(): self
    {
        return new self(null);
    }

    public static function fromString(string $value, int $maxLength = -1): self
    {
        return new self($value, $maxLength);
    }

    public static function fromArray(array $value): self
    {
        return new self($value);
    }

    /**
     * @throws VariableCanNotBeExtracted
     */
    public static function fromList(string $value, Operator $operator, VarSpecifier $varSpecifier): self
    {
        return $operator->isNamed()
             ? self::fromNamedList($value, $operator, $varSpecifier)
             : self::fromUnnamedList($value, $operator);
    }

    /**
     * Extracts an exploded variable from a positional representation.
     *
     * Positional exploded values may contain either plain values or name/value
     * pairs, but not both representations at the same time.
     */
    public static function fromUnnamedList(string $value, Operator $operator): ExtractedValue
    {
        return match ($operator) {
            Operator::ReservedChars,
            Operator::Fragment => self::fromPositionalPairs($value, $operator),
            default => self::fromGenericList($value, $operator),
        };
    }

    /**
     * Creates an extracted value from a path parameter containing alternating
     * key/value pairs.
     *
     * The value is split into comma-separated key/value pairs before decoding,
     * so percent-encoded commas (`%2C`) remain part of the corresponding key or value.
     *
     * @param non-empty-string $value
     *
     * @throws VariableCanNotBeExtracted if the value is the empty string
     */
    public static function fromNamedValue(string $value, Operator $operator): self
    {
        '' !== $value || throw VariableCanNotBeExtracted::dueTo('The named value is invalid.', ExtractionErrorReason::MalformedValue);

        // Path parameters can encode an associative array as alternating
        // key/value pairs. Split before decoding so that encoded commas
        // (%2C) remain part of the value.
        $parts = explode(',', $value);
        $parts = 0 === count($parts) % 2 ? $parts : [...$parts, ''];
        $result = [];
        foreach (array_chunk($parts, 2) as [$k, $v]) {
            $key = $operator->decode($k);
            !array_key_exists($key, $result) || throw VariableCanNotBeExtracted::dueTo('The variable cannot be extracted because the input contains multiple values for "'.$key.'".', ExtractionErrorReason::ReconciliationFailed);

            $result[$key] = $operator->decode($v);
        }

        return ExtractedValue::fromArray($result);
    }

    /**
     * Extracts an exploded variable from a positional representation.
     *
     * Positional exploded values may contain either plain values or name/value
     * pairs, but not both representations at the same time.
     */
    private static function fromGenericList(string $value, Operator $operator): ExtractedValue
    {
        /** @var non-empty-string $separator */
        $separator = $operator->separator();
        $values = explode($separator, $value);
        $hasPairs = false;
        $result = [];

        foreach ($values as $pValue) {
            if (str_contains($pValue, '=')) {
                $hasPairs = true;
                [$key, $qValue] = explode('=', $pValue, 2);
                $result[$operator->decode($key)] = $operator->decode($qValue);
                continue;
            }

            !$hasPairs || throw VariableCanNotBeExtracted::dueTo('The value "'.$value.'" is malformed.', ExtractionErrorReason::MalformedValue);
            $result[] = $operator->decode($pValue);
        }

        if (!$hasPairs && 1 === count($result)) {
            $result = $result[0];
        }

        return new self($result);
    }

    private static function fromPositionalPairs(string $value, Operator $operator): self
    {
        /** @var non-empty-string $separator */
        $separator = $operator->separator();
        $parts = explode($separator, $value);
        $result = [];
        for ($i = 0, $count = count($parts); $i < $count;) {
            $key = $parts[$i++];
            $i < $count || throw VariableCanNotBeExtracted::dueTo('The value "'.$value.'" is malformed.', ExtractionErrorReason::MalformedValue);
            $part = $parts[$i++];
            if ('' === $part) {
                ($i < $count && '' === $parts[$i]) || throw VariableCanNotBeExtracted::dueTo('The value "'.$value.'" is malformed.', ExtractionErrorReason::MalformedValue);
                ++$i;
                $part = $separator;
            }

            $result[$operator->decode($key)] = $operator->decode($part);
        }

        return new self($result);
    }

    /**
     * Extracts an exploded variable from a named representation.
     *
     * The value may consist of repeated occurrences of the variable name or of
     * name/value pairs. When all pairs use the variable's name, the values are
     * returned as a list. Otherwise, the complete name/value mapping is returned,
     * provided the variable's name is not mixed with other names.
     */
    public static function fromNamedList(string $value, Operator $operator, VarSpecifier $varSpecifier): self
    {
        /** @var non-empty-string $separator */
        $separator = $operator->separator();
        $items = explode($separator, $value);
        $pairs = [];
        foreach ($items as $item) {
            $pairs[] = str_contains($item, '=') ? explode('=', $item, 2) : [$varSpecifier->name, $item];
        }

        $names = array_unique(array_column($pairs, 0));
        if (1 === count($names) && $varSpecifier->name === $names[0]) {
            return new self(array_map(fn (array $pair): string => $operator->decode($pair[1]), $pairs));
        }

        !in_array($varSpecifier->name, $names, true) || throw VariableCanNotBeExtracted::dueTo('The value "'.$value.'" is malformed.', ExtractionErrorReason::MalformedValue);

        $result = [];
        foreach ($pairs as [$pName, $pValue]) {
            $key = $operator->decode($pName);
            !array_key_exists($key, $result) || throw VariableCanNotBeExtracted::dueTo('The variable cannot be extracted because the input contains multiple values for "'.$key.'".', ExtractionErrorReason::ReconciliationFailed);
            $result[$key] = $operator->decode($pValue);
        }

        return new self($result);
    }

    public static function fromValue(string $value, Operator $operator, VarSpecifier $varSpecifier): self
    {
        return new self(
            value: $operator->decode($value),
            maxLength: 0 === $varSpecifier->position ? -1 : $varSpecifier->position,
            asList: array_map(
                static fn (string $value): string => $operator->decode($value),
                '' !== $value && $operator->supportsListValue() ? explode(',', $value) : []
            ),
        );
    }

    public function equals(mixed $value): bool
    {
        return $value instanceof self
            && $value->maxLength === $this->maxLength
            && $value->value === $this->value
            && $value->asList === $this->asList;
    }

    public function hasKey(string|int $name): bool
    {
        return is_array($this->value) && array_key_exists($name, $this->value);
    }

    /**
     * @throws VariableCanNotBeExtracted
     */
    public function reconcile(self $other): self
    {
        if ($this->equals($other)) {
            return 1 < count($this->asList) ? new self($this->asList) : $this;
        }

        $thisValue = $this->value;
        $otherValue = $other->value;
        if ([] !== $this->asList && is_array($otherValue) && $this->asList === $otherValue) {
            return $other;
        }

        if ([] !== $other->asList && is_array($thisValue) && $other->asList === $thisValue) {
            return $this;
        }

        (!is_array($thisValue) || !is_array($otherValue)) || throw VariableCanNotBeExtracted::dueTo('The extracted lists contain different data.', ExtractionErrorReason::ListMismatch);
        (is_string($thisValue) && is_string($otherValue)) || throw VariableCanNotBeExtracted::dueTo('The extracted values have different types.', ExtractionErrorReason::TypeMismatch);
        if ($this->maxLength === $other->maxLength) {
            ($thisValue === $otherValue) || throw VariableCanNotBeExtracted::dueTo('The extracted values are different.', ExtractionErrorReason::StringMismatch);

            $value = [] === $this->asList ? $other : $this;

            return 1 < count($value->asList) ? new self($value->asList) : $value;
        }

        $result = $other;
        $resultValue = $otherValue;
        $prefixValue = $thisValue;
        if (-1 === $this->maxLength || (-1 !== $other->maxLength && $this->maxLength > $other->maxLength)) {
            $result = $this;
            $resultValue = $thisValue;
            $prefixValue = $otherValue;
        }

        return str_starts_with($resultValue, $prefixValue)
            ? $result
            : throw VariableCanNotBeExtracted::dueTo('The extracted value does not start with the other extracted value.', ExtractionErrorReason::StringMismatch);
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\UriTemplate;

use Exception;
use League\Uri\Contracts\UriException;
use Throwable;
use TypeError;

use function array_values;
use function get_debug_type;
use function implode;
use function is_string;

class VariableCanNotBeExtracted extends Exception implements UriException
{
    /** @var list<string> */
    public readonly array $names;
    /** @var list<string> */
    public readonly array $missingNames;
    /** @var list<ExtractionErrorReason> */
    public readonly array $reasons;

    protected function __construct(
        string $message,
        array $reasons,
        array $names = [],
        array $missingNames = [],
        ?Throwable $previous = null,
    ) {
        parent::__construct($message, previous: $previous);

        $this->reasons = self::uniqueReasons($reasons);
        $this->missingNames = self::uniqueNames($missingNames, 'Missing variable name');
        $this->names = self::uniqueNames($names, 'Expected variable name');
    }

    /**
     * @return list<ExtractionErrorReason>
     */
    private static function uniqueReasons(iterable $reasons): array
    {
        $unique = [];
        foreach ($reasons as $reason) {
            $reason instanceof ExtractionErrorReason || throw new TypeError('An extraction error value must be an '.ExtractionErrorReason::class.'; '.get_debug_type($reason).' received.');
            $unique[$reason->name] = $reason;
        }

        return array_values($unique);
    }

    /**
     * @return list<string>
     */
    private static function uniqueNames(iterable $names, string $message): array
    {
        $unique = [];
        foreach ($names as $name) {
            is_string($name) || throw new TypeError($message.' must be a string; '.get_debug_type($name).' received.');
            $unique[$name] = $name;
        }

        return array_values($unique);
    }

    public static function dueTo(string $message, ExtractionErrorReason ...$reasons): self
    {
        return new self($message, array_values($reasons));
    }

    public static function dueToMissingVariables(string $input, Template $template, ExtractionResult|iterable $missingNames): self
    {
        $missingNames = $missingNames instanceof ExtractionResult ? $missingNames->missingNames() : self::uniqueNames($missingNames, 'Missing variable name');

        return new self(
            message: 'The value "'.$input.'" does not provide all the variables defined by the template "'.$template->value.'"; Missing: "'.implode('", "', $missingNames).'".',
            reasons: [ExtractionErrorReason::MissingVariables],
            names: $template->variableNames,
            missingNames: $missingNames,
        );
    }

    public static function dueToExtractionFailure(string $input, Template $template, self $previous): self
    {
        return new self(
            message: 'The value "'.$input.'" could not be completely extracted using the template "'.$template->value.'"',
            reasons: $previous->reasons,
            names: $template->variableNames,
            missingNames: $previous->missingNames,
            previous: $previous,
        );
    }

    public static function dueToSuitableCandidateNotFound(string $input, array $reasons, array $missingNames = []): self
    {
        return new self(
            message: 'No suitable candidate was found to satisfy the complete extraction of "'.$input.'".',
            reasons: $reasons,
            missingNames: $missingNames,
        );
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\UriTemplate;

use function preg_replace_callback;
use function rawurlencode;
use function strlen;

/**
 * @internal The class exposes the internal representation of an Literal string
 * @link https://www.rfc-editor.org/rfc/rfc6570#section-3.1
 */
final class Literal
{
    private const REGEXP_CHARACTERS_TO_ENCODE = '/[^A-Za-z\d\-._~:\/?#\[\]@!$&\'()*+,;=%]+|%(?![A-Fa-f\d]{2})/';

    public readonly string $encoded;
    public readonly int $length;

    public function __construct(public readonly string $raw)
    {
        $this->encoded = self::encode($raw);
        $this->length = strlen($this->encoded);
    }

    private static function encode(string $raw): string
    {
        return (string) preg_replace_callback(
            self::REGEXP_CHARACTERS_TO_ENCODE,
            static fn (array $matches): string => rawurlencode($matches[0]),
            $raw,
        );
    }

    public function isPrefixOf(string $value): bool
    {
        return str_starts_with($value, $this->encoded);
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\UriTemplate;

use League\Uri\Exceptions\SyntaxError;

use function preg_match;

/**
 * @internal The class exposes the internal representation of a Var Specifier
 * @link https://www.rfc-editor.org/rfc/rfc6570#section-2.3
 */
final class VarSpecifier
{
    /**
     * Variables specification regular expression pattern.
     *
     * @link https://tools.ietf.org/html/rfc6570#section-2.3
     */
    private const REGEXP_VARSPEC = '/^
        (?<name>
            (?:[A-Za-z0-9_\[\]]|%[0-9a-fA-F]{2})+
            (?:\.(?:[A-Za-z0-9_\[\]]|%[0-9a-fA-F]{2})+)*
        )
        (?<modifier>\:(?<position>\d+)|\*)?
    $/x';

    private const MODIFIER_POSITION_MAX_POSITION = 10_000;

    private function __construct(
        public readonly string $name,
        public readonly string $modifier,
        public readonly int $position
    ) {
    }

    public static function new(string $specification): self
    {
        1 === preg_match(self::REGEXP_VARSPEC, $specification, $parsed) || throw new SyntaxError('The variable specification "'.$specification.'" is invalid.');
        $properties = ['name' => $parsed['name'], 'modifier' => $parsed['modifier'] ?? '', 'position' => $parsed['position'] ?? ''];

        if ('' !== $properties['position']) {
            1 === preg_match('/^(?:0|[1-9]\d*)$/', $properties['position']) || throw new SyntaxError('The variable specification "'.$specification.'" is invalid.');
            $properties['position'] = (int) $properties['position'];
            0 !== $properties['position'] || throw new SyntaxError('The variable specification "'.$specification.'" is invalid the position modifier must be greater than 0.');

            $properties['modifier'] = ':';
        }

        if ('' === $properties['position']) {
            $properties['position'] = 0;
        }

        self::MODIFIER_POSITION_MAX_POSITION > $properties['position'] || throw new SyntaxError('The variable specification "'.$specification.'" is invalid the position modifier must be lower than 10000.');

        return new self($properties['name'], $properties['modifier'], $properties['position']);
    }

    public function toString(): string
    {
        return $this->name.$this->modifier.match (true) {
            0 < $this->position => $this->position,
            default => '',
        };
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\UriTemplate;

use InvalidArgumentException;
use League\Uri\Contracts\UriException;

class TemplateCanNotBeExpanded extends InvalidArgumentException implements UriException
{
    public readonly array $variablesNames;

    public function __construct(string $message = '', string ...$variableNames)
    {
        parent::__construct($message, 0, null);

        $this->variablesNames = $variableNames;
    }

    public static function dueToUnableToProcessValueListWithPrefix(string $variableName): self
    {
        return new self('The ":" modifier cannot be applied on "'.$variableName.'" since it is a list of values.', $variableName);
    }

    public static function dueToNestedListOfValue(string $variableName): self
    {
        return new self('The "'.$variableName.'" cannot be a nested list.', $variableName);
    }

    public static function dueToMissingVariables(string ...$variableNames): self
    {
        return new self('The following required variables are missing: `'.implode('`, `', $variableNames).'`.', ...$variableNames);
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\UriTemplate;

enum ExtractionErrorReason
{
    // Matching
    case LiteralMismatch;
    case PrefixMismatch;
    case VariableMismatch;
    case TypeMismatch;
    case StringMismatch;
    case ListMismatch;

    // Extraction
    case MalformedValue;
    case PrefixLengthExceeded;
    case UnmatchedContent;
    case UndeterminedDelimiter;
    case UnsupportedOperation;

    // Reconciliation
    case ReconciliationFailed;
    case MissingVariables;
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\UriTemplate;

use ArrayAccess;
use Countable;
use DateTimeImmutable;
use DateTimeInterface;
use DateTimeZone;
use Exception;
use League\Uri\HostRecord;
use League\Uri\TypeConverter;
use LogicException;
use TypeError;
use UnitEnum;
use ValueError;

use function array_key_exists;
use function array_map;
use function array_values;
use function count;
use function is_array;
use function is_int;
use function is_string;

/**
 * @implements ArrayAccess<string, null|string|array<string|null>>
 */
final class ExtractionResult implements ArrayAccess, Countable
{
    /**
     * @param array<string, ExtractedValue> $variables
     * @param list<string> $names
     * @param list<string> $missingNames
     * @param list<ExtractionErrorReason> $reasons
     */
    private function __construct(
        private readonly array $variables,
        private readonly array $names,
        private readonly array $missingNames,
        private readonly array $reasons,
    ) {
    }

    public static function empty(): self
    {
        return new self(
            variables: [],
            names: [],
            missingNames: [],
            reasons: [],
        );
    }

    public static function failure(VariableCanNotBeExtracted $exception, Template $template): self
    {
        return new self(
            variables: [],
            names: array_values($template->variableNames),
            missingNames: $exception->missingNames,
            reasons: $exception->reasons,
        );
    }

    public static function success(iterable $variables = []): self
    {
        $vars = [];
        $names = [];
        $missing = [];
        foreach ($variables as $name => $variable) {
            is_string($name) || is_int($name) || throw new TypeError('An extraction variable name must be a string.');
            !array_key_exists($name, $vars) || throw new ValueError('The variable "'.$name.'" is already defined.');
            $variable instanceof ExtractedValue || throw new TypeError('An extraction result value must be an '.ExtractedValue::class.'.');

            $name = (string) $name;
            $vars[$name] = $variable;
            $names[$name] = $name;
            if (null === $variable->value) {
                $missing[$name] = $name;
            }
        }

        return new self(
            variables: $vars,
            names: array_values($names),
            missingNames: array_values($missing),
            reasons: [],
        );
    }

    /**
     * @throws VariableCanNotBeExtracted
     */
    public function reconcile(self $other): self
    {
        $result = $this->variables;

        // A variable declared by one expression must not be declared
        // in the other expression unless it is compatible
        foreach ($other->variables as $name => $value) {
            if (!array_key_exists($name, $result)) {
                $result[$name] = $value;
                continue;
            }

            try {
                $result[$name] = $result[$name]->reconcile($value);
            } catch (VariableCanNotBeExtracted $exception) {
                throw VariableCanNotBeExtracted::dueTo('The extracted values for variable "'.$name.'" could not be reconciled; '.$exception->getMessage(), ...$exception->reasons);
            }
        }

        // A variable declared by one expression must not be consumed as part of
        // an exploded variable from another expression.
        foreach ([...$this->missingNames, ...$other->missingNames] as $missingName) {
            foreach ($result as $name => $value) {
                !$value->hasKey($missingName) || throw VariableCanNotBeExtracted::dueTo('The variable "'.$missingName.'" was extracted as part of "'.$name.'".', ExtractionErrorReason::VariableMismatch);
            }
        }

        return self::success($result);
    }

    /**
     * Returns true if the extraction is successful.
     */
    public function isSuccessful(): bool
    {
        return [] === $this->reasons;
    }

    /**
     * @return list<ExtractionErrorReason>
     */
    public function reasons(): array
    {
        return $this->reasons;
    }

    /**
     * Returns the list of all variable names.
     *
     * @return list<string>
     */
    public function names(): array
    {
        return $this->names;
    }

    /**
     * Returns the list of variable names missing from the extraction.
     *
     * @return list<string>
     */
    public function missingNames(): array
    {
        return $this->missingNames;
    }

    /**
     * Tells whether some variables are attached to the result.
     */
    public function hasValue(): bool
    {
        return [] !== $this->variables;
    }

    /**
     * Returns the number of found variables.
     */
    public function count(): int
    {
        return count($this->variables);
    }

    /**
     * @return null|string|array<string|null>
     */
    public function offsetGet(mixed $offset): null|string|array
    {
        return is_string($offset) || is_int($offset)
            ? $this->fetch($offset)?->value
            : throw new TypeError('offset must be a string.');
    }

    public function offsetExists(mixed $offset): bool
    {
        return (is_string($offset) || is_int($offset))
            && array_key_exists($offset, $this->variables);
    }

    public function offsetUnset(mixed $offset): never
    {
        throw new LogicException(self::class.' is read-only.');
    }

    public function offsetSet(mixed $offset, mixed $value): never
    {
        throw new LogicException(self::class.' is read-only.');
    }

    /**
     * @return array<string, array<string>|string|null>
     */
    public function variables(): array
    {
        return array_map(
            static fn (ExtractedValue $val): array|string|null => 1 < count($val->asList) ? $val->asList : $val->value,
            $this->variables
        );
    }

    public function fetch(string|int $variableName): ?ExtractedValue
    {
        return $this->variables[$variableName] ?? null;
    }

    public function array(int|string $name): array
    {
        $variable = $this->fetch($name);

        return match (true) {
            null === $variable => [],
            [] !== $variable->asList => $variable->asList,
            is_array($variable->value) => $variable->value,
            default => [],
        };
    }

    public function string(int|string $name, ?string $default = null): ?string
    {
        return TypeConverter::toString($this->fetch($name)?->value) ?? $default;
    }

    public function host(int|string $name, ?string $default = null): ?string
    {
        return HostRecord::parse($this->string($name))?->toUnicode() ?? $default;
    }

    public function strings(int|string $name, ?string $default = null): array
    {
        return TypeConverter::toStrings($this->array($name), $default);
    }

    public function hosts(int|string $name, ?string $default = null): array
    {
        return array_map(
            static fn (?string $value): ?string => HostRecord::from($value)->toUnicode() ?? $default,
            $this->strings($name)
        );
    }

    public function integer(int|string $name, ?int $default = null): ?int
    {
        return TypeConverter::toInteger($this->fetch($name)?->value) ?? $default;
    }

    /**
     * @return array<int>
     */
    public function integers(int|string $name, ?int $default = null): array
    {
        return TypeConverter::toIntegers($this->array($name), $default);
    }

    public function float(int|string $name, ?float $default = null): ?float
    {
        return TypeConverter::toFloat($this->fetch($name)?->value) ?? $default;
    }

    /**
     * @return array<float>
     */
    public function floats(int|string $name, ?float $default = null): array
    {
        return TypeConverter::toFloats($this->array($name), $default);
    }

    public function boolean(int|string $name, ?bool $default = null): ?bool
    {
        return TypeConverter::toBoolean($this->fetch($name)?->value) ?? $default;
    }

    /**
     * @return array<bool>
     */
    public function booleans(int|string $name, ?bool $default = null): array
    {
        return TypeConverter::toBooleans($this->array($name), $default);
    }

    /**
     * @param class-string<UnitEnum> $enumClass
     */
    public function enum(int|string $name, string $enumClass, ?UnitEnum $default = null): ?UnitEnum
    {
        null === $default || $default instanceof $enumClass || throw new ValueError('The default value must be an instance of '.$enumClass.'; '.get_debug_type($default).' given.');

        return TypeConverter::toEnum($this->fetch($name)?->value, $enumClass) ?? $default;
    }

    /**
     * @param class-string<UnitEnum> $enumClass
     *
     * @return array<UnitEnum>
     */
    public function enums(int|string $name, string $enumClass, ?UnitEnum $default = null): array
    {
        return TypeConverter::toEnums($this->array($name), $enumClass, $default);
    }

    /**
     * @param non-empty-string $format
     *
     * @throws Exception
     */
    public function date(
        int|string $name,
        string $format,
        DateTimeZone|string $timezone = 'UTC',
        ?DateTimeInterface $default = null
    ): ?DateTimeImmutable {

        return TypeConverter::toDateTimeImmutable($this->fetch($name)?->value, $format, $timezone) ?? (
            null !== $default && !$default instanceof DateTimeImmutable
                ? DateTimeImmutable::createFromInterface($default)
                : $default
        );
    }

    /**
     * @param non-empty-string $format
     *
     * @throws Exception
     *
     * @return array<DateTimeImmutable>
     */
    public function dates(int|string $name, string $format, DateTimeZone|string $timezone = 'UTC', ?DateTimeInterface $default = null): array
    {
        return TypeConverter::toDateTimeImmutables($this->array($name), $format, $timezone, $default);
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\UriTemplate;

use Deprecated;
use Iterator;
use IteratorAggregate;
use League\Uri\Exceptions\SyntaxError;
use Stringable;
use ValueError;

use function array_filter;
use function array_map;
use function array_reverse;
use function array_unique;
use function array_values;
use function count;
use function explode;
use function implode;
use function is_string;
use function ksort;
use function strlen;
use function strpos;
use function substr;

/**
 * @internal The class exposes the internal representation of an Expression and its usage
 * @link https://www.rfc-editor.org/rfc/rfc6570#section-2.2
 * @implements IteratorAggregate<VarSpecifier>
 */
final class Expression implements IteratorAggregate
{
    /** @var array<VarSpecifier> */
    private readonly array $varSpecifiers;
    /** @var array<string> */
    public readonly array $variableNames;
    public readonly string $value;

    private function __construct(public readonly Operator $operator, VarSpecifier ...$varSpecifiers)
    {
        $this->varSpecifiers = $varSpecifiers;
        $this->variableNames = array_unique(
            array_map(
                static fn (VarSpecifier $varSpecifier): string => $varSpecifier->name,
                $varSpecifiers
            )
        );
        $this->value = '{'.$operator->value.implode(',', array_map(
            static fn (VarSpecifier $varSpecifier): string => $varSpecifier->toString(),
            $varSpecifiers
        )).'}';
    }

    /**
     * @throws SyntaxError if the expression is invalid
     */
    public static function new(Stringable|string $expression): self
    {
        $parts = Operator::parseExpression($expression);

        return new Expression($parts['operator'], ...array_map(
            static fn (string $varSpec): VarSpecifier => VarSpecifier::new($varSpec),
            explode(',', $parts['variables'])
        ));
    }

    /**
     * @return Iterator<VarSpecifier>
     */
    public function getIterator(): Iterator
    {
        yield from $this->varSpecifiers;
    }

    public function expand(VariableBag $variables): string
    {
        $expanded = implode(
            $this->operator->separator(),
            array_filter(
                array_map(
                    fn (VarSpecifier $varSpecifier): string => $this->operator->expand($varSpecifier, $variables),
                    $this->varSpecifiers
                ),
                static fn ($value): bool => '' !== $value
            )
        );

        return match ('') {
            $expanded => '',
            default => $this->operator->first().$expanded,
        };
    }

    /**
     * Extracts variables from a value according to this expression.
     *
     * Named expressions match variables by name, query expressions match query
     * parameters by name regardless of their order, while positional expressions
     * assign values according to the order of their variable specifiers.
     *
     * @throws VariableCanNotBeExtracted If the value cannot be extracted according
     *                                   to the expression.
     */
    public function extract(string $value): ExtractionResult
    {
        return $this->operator->isNamed()
            ? $this->extractNamedValues($value)
            : $this->extractPositionalValues($value);
    }

    /**
     * Extracts variables from a named expression value.
     *
     * Named variables are matched by name rather than by position. Non-exploded
     * variables may consume at most one component, while an exploded variable
     * may consume multiple components.
     *
     * @throws VariableCanNotBeExtracted If the value cannot be matched to the
     *                                   variable specifiers.
     */
    private function extractNamedValues(string $value): ExtractionResult
    {
        /** @var non-empty-string $separator */
        $separator = $this->operator->separator();
        $components = '' === $value ? [] : explode($separator, $value);
        $matched = $this->matchNamedValues($components);
        null !== $matched || throw VariableCanNotBeExtracted::dueTo('The value "'.$value.'" cannot be extracted from the expression "'.$this->value.'".', ExtractionErrorReason::MalformedValue);

        $variables = [];
        foreach ($this->varSpecifiers as $offset => $varSpecifier) {
            $extracted = $this->operator->extract($varSpecifier, $matched[$offset] ?? null);
            $this->assertPrefixLength($varSpecifier, $extracted);

            foreach ($extracted->names() as $name) {
                $variables[$name] = $extracted->fetch($name);
            }
        }

        return ExtractionResult::success($variables);
    }

    /**
     * Matches named components against the variable specifiers.
     *
     * Components are matched by variable name rather than by position.
     * Non-exploded variables may occur at most once, while exploded variables
     * may consume multiple components.
     *
     * @param list<string> $components
     *
     * @return list<string|null>|null
     */
    private function matchNamedValues(array $components): ?array
    {
        /** @var array<string, list<string>> $componentsByName */
        $componentsByName = [];

        foreach ($components as $component) {
            [$name] = explode('=', $component, 2);
            $componentsByName[$name] ??= [];
            $componentsByName[$name][] = $component;
        }

        $explodedSpecifiers = [];
        foreach ($this->varSpecifiers as $offset => $varSpecifier) {
            if ('*' === $varSpecifier->modifier) {
                $explodedSpecifiers[$offset] = $varSpecifier;
            }
        }

        $matched = [];
        foreach ($this->varSpecifiers as $offset => $varSpecifier) {
            $name = $varSpecifier->name;
            if (!isset($componentsByName[$name])) {
                $matched[$offset] = null;

                continue;
            }

            $occurrences = $componentsByName[$name];
            if ('*' !== $varSpecifier->modifier && 1 !== count($occurrences)) {
                return null;
            }

            $matched[$offset] = implode($this->operator->separator(), $occurrences);

            unset($componentsByName[$name]);
        }

        if ([] !== $componentsByName) {
            /*
             * Remaining components have names that are not explicitly declared.
             * They can only be consumed by a single exploded variable.
             */
            if (1 !== count($explodedSpecifiers)) {
                return null;
            }

            $explodedOffset = array_key_first($explodedSpecifiers);
            $remaining = [];
            foreach ($componentsByName as $occurrences) {
                foreach ($occurrences as $component) {
                    $remaining[] = $component;
                }
            }

            $matched[$explodedOffset] = implode($this->operator->separator(), $remaining);
        }

        ksort($matched);

        return array_values($matched);
    }

    /**
     * Extracts variables from a positional expression value.
     *
     * Values are assigned to variable specifiers in their declared order.
     * Exploded variables consume the remaining values required to leave enough
     * values for the following variable specifiers.
     *
     * @throws VariableCanNotBeExtracted If a value cannot be extracted or a value
     *                                   exceeds its variable's prefix length.
     */
    private function extractPositionalValues(string $value): ExtractionResult
    {
        return $this->extractPositionalCandidates(
            value: $value,
            varSpecifierOffset: 0,
            valueOffset: 0,
            previousResult: ExtractionResult::empty(),
        );
    }

    private function extractPositionalCandidates(
        string $value,
        int $varSpecifierOffset,
        int $valueOffset,
        ExtractionResult $previousResult,
    ): ExtractionResult {
        $varSpecifier = $this->varSpecifiers[$varSpecifierOffset];
        $lastVarSpecifier = $varSpecifierOffset + 1 === count($this->varSpecifiers);
        if ($lastVarSpecifier) {
            $extracted = $this->operator->extract(
                $varSpecifier,
                substr($value, $valueOffset),
            );
            $this->assertPrefixLength($varSpecifier, $extracted);

            return $previousResult->reconcile($extracted);
        }

        /** @var non-empty-string $separator */
        $separator = $this->operator->separator();
        $separatorLength = strlen($separator);
        $positions = $this->delimiterPositions($value, $valueOffset, $separator);

        $candidates = [];
        foreach ($positions as $position) {
            $candidates[] = [
                'end' => $position,
                'next' => $position + $separatorLength,
            ];
        }

        if ('*' === $varSpecifier->modifier) {
            $candidates = array_reverse($candidates);
        }

        $reasons = [];
        $missingNames = [];

        foreach ($candidates as ['end' => $end, 'next' => $next]) {
            try {
                $extracted = $this->operator->extract(
                    $varSpecifier,
                    substr($value, $valueOffset, $end - $valueOffset),
                );
                $this->assertPrefixLength($varSpecifier, $extracted);

                return $this->extractPositionalCandidates(
                    value: $value,
                    varSpecifierOffset: $varSpecifierOffset + 1,
                    valueOffset: $next,
                    previousResult: $previousResult->reconcile($extracted),
                );
            } catch (VariableCanNotBeExtracted $exception) {
                $reasons = [...$reasons, ...$exception->reasons];
                $missingNames = [...$missingNames, ...$exception->missingNames];
            }
        }

        throw VariableCanNotBeExtracted::dueToSuitableCandidateNotFound(
            $value,
            $reasons,
            $missingNames,
        );
    }

    /**
     * Finds all positions of a delimiter at or after the given offset.
     *
     * @param int $offset The position from which to search.
     *
     *
     * @throws ValueError If the delimiter is empty.
     * @return list<int> The positions at which the delimiter occurs.
     */
    private function delimiterPositions(string $value, int $offset, string $delimiter): array
    {
        '' !== $delimiter || throw new ValueError('The delimiter cannot be empty.');

        $positions = [];
        $position = $offset;

        while (false !== ($position = strpos($value, $delimiter, $position))) {
            $positions[] = $position;
            $position += strlen($delimiter);
        }

        return $positions;
    }

    /**
     * Ensures that an extracted value does not exceed its variable's prefix length.
     *
     * @throws VariableCanNotBeExtracted If the extracted value exceeds the
     *                                   variable's prefix length.
     */
    private function assertPrefixLength(VarSpecifier $varSpecifier, ExtractionResult $variables): void
    {
        if (0 === $varSpecifier->position) {
            return;
        }

        $value = $variables->fetch($varSpecifier->name);
        if (null === $value || !is_string($value->value)) {
            return;
        }

        (mb_strlen($value->value) <= $varSpecifier->position) || throw VariableCanNotBeExtracted::dueTo('The value for variable "'.$varSpecifier->name.'" exceeds the prefix length.', ExtractionErrorReason::PrefixLengthExceeded);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @throws SyntaxError if the expression is invalid
     * @see Expression::new()
     *
     * @deprecated Since version 7.0.0
     * @codeCoverageIgnore
     */
    #[Deprecated(message:'use League\Uri\UriTemplate\Exppression::new() instead', since:'league/uri:7.0.0')]
    public static function createFromString(Stringable|string $expression): self
    {
        return self::new($expression);
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\UriTemplate;

use BackedEnum;
use Deprecated;
use League\Uri\Exceptions\SyntaxError;
use Stringable;
use Uri\Rfc3986\Uri as Rfc3986Uri;
use Uri\WhatWg\Url as WhatWgUrl;
use ValueError;

use function array_filter;
use function array_map;
use function array_merge;
use function array_reverse;
use function array_unique;
use function array_values;
use function count;
use function implode;
use function preg_match_all;
use function preg_replace;
use function str_starts_with;
use function strlen;
use function strpbrk;
use function strpos;
use function substr;

use const PREG_OFFSET_CAPTURE;
use const PREG_SET_ORDER;

/**
 * @internal The class exposes the internal representation of a Template and its usage
 */
final class Template implements Stringable
{
    /**
     * Expression regular expression pattern.
     */
    private const REGEXP_EXPRESSION_DETECTOR = '/(?<expression>\{[^}]*})/x';

    /** @var array<string> */
    public readonly array $variableNames;

    /** @var list<Expression|Literal>  */
    private readonly array $parts;

    private function __construct(public readonly string $value, Expression|Literal ...$parts)
    {
        $this->parts = array_values($parts);
        $this->variableNames = array_unique(
            array_merge(
                ...array_map(
                    static fn (Expression $expression): array => $expression->variableNames,
                    array_filter($parts, fn (Expression|Literal $p): bool => $p instanceof Expression)
                )
            )
        );
    }

    /**
     * @throws SyntaxError if the template contains invalid expressions
     * @throws SyntaxError if the template contains invalid variable specification
     */
    public static function new(BackedEnum|Stringable|string $template): self
    {
        if ($template instanceof BackedEnum) {
            $template = $template->value;
        }

        $template = (string) $template;
        /** @var string $remainder */
        $remainder = preg_replace(self::REGEXP_EXPRESSION_DETECTOR, '', $template);
        false === strpbrk($remainder, '{}') || throw new SyntaxError('The template "'.$template.'" contains invalid expressions.');

        preg_match_all(self::REGEXP_EXPRESSION_DETECTOR, $template, $founds, PREG_SET_ORDER | PREG_OFFSET_CAPTURE);

        $parts = [];
        $offset = 0;
        foreach ($founds as $found) {
            $expression = $found['expression'][0];
            $position = $found['expression'][1];

            if ($position > $offset) {
                $parts[] = new Literal(substr($template, $offset, $position - $offset));
            }

            $parts[] = Expression::new($expression);
            $offset = $position + strlen($found[0][0]);
        }

        if ($offset < strlen($template)) {
            $parts[] = new Literal(substr($template, $offset));
        }

        return new self($template, ...$parts);
    }

    public function __toString(): string
    {
        return $this->value;
    }

    /**-----------
     * Expand API
    ------------*/

    /**
     * @throws TemplateCanNotBeExpanded if the variables are invalid
     */
    public function expand(iterable|ExtractionResult $variables = []): string
    {
        if (!$variables instanceof VariableBag) {
            $variables = new VariableBag($variables);
        }

        return $this->expandAll($variables);
    }

    /**
     * @throws TemplateCanNotBeExpanded if the variables are invalid or missing
     */
    public function expandOrFail(iterable|ExtractionResult $variables = []): string
    {
        if (!$variables instanceof VariableBag) {
            $variables = new VariableBag($variables);
        }

        $missing = array_filter($this->variableNames, static fn (string $name): bool => !isset($variables[$name]));
        [] === $missing || throw TemplateCanNotBeExpanded::dueToMissingVariables(...$missing);

        return $this->expandAll($variables);
    }

    private function expandAll(VariableBag $variables): string
    {
        return implode('', array_map(
            static fn (Literal|Expression $part): string => $part instanceof Literal ? $part->encoded : $part->expand($variables),
            $this->parts,
        ));
    }

    /**-----------
     * Extract API
    ------------*/

    /**
     * Tells whether the value matches this URI template.
     *
     * This method performs a strict extraction and returns `true` only when the
     * value can be completely matched and all variables can be extracted.
     *
     * @return bool true`if the value matches the template, `false` otherwise.
     */
    public function match(Rfc3986Uri|WhatWgUrl|BackedEnum|Stringable|string $value): bool
    {
        try {
            $this->extractOrFail($value);

            return true;
        } catch (VariableCanNotBeExtracted) {

            return false;
        }
    }

    /**
     * Extracts the variables from a value.
     *
     * Extraction is lenient: variables that are missing from the value are
     * represented in the result, while extraction failures are represented by
     * a failed result containing the reasons for the failure.
     *
     * @return ExtractionResult The extraction result.
     */
    public function extract(Rfc3986Uri|WhatWgUrl|BackedEnum|Stringable|string $value): ExtractionResult
    {
        try {
            return $this->extractAll(self::uriString($value));
        } catch (VariableCanNotBeExtracted $exception) {
            return ExtractionResult::failure($exception, $this);
        }
    }

    /**
     * Extracts the variables from a value.
     *
     * Unlike {@see extract()}, this method is strict and reports extraction
     * failures or missing variables by throwing an exception.
     *
     * @throws VariableCanNotBeExtracted If the value cannot be completely matched,
     *                                   a variable cannot be extracted, or a
     *                                   variable defined by the template is missing.
     *
     * @return ExtractionResult The extracted variables.
     */
    public function extractOrFail(Rfc3986Uri|WhatWgUrl|BackedEnum|Stringable|string $value): ExtractionResult
    {
        $value = self::uriString($value);
        try {
            $result = $this->extractAll($value);
        } catch (VariableCanNotBeExtracted $exception) {
            throw VariableCanNotBeExtracted::dueToExtractionFailure($value, $this, $exception);
        }

        return [] === $result->missingNames()
            ? $result
            : throw VariableCanNotBeExtracted::dueToMissingVariables($value, $this, $result);
    }

    private static function uriString(Rfc3986Uri|WhatWgUrl|BackedEnum|Stringable|string $uri): string
    {
        return match (true) {
            $uri instanceof Rfc3986Uri => $uri->toRawString(),
            $uri instanceof WhatWgUrl => $uri->toUnicodeString(),
            $uri instanceof BackedEnum => (string) $uri->value,
            default => (string) $uri,
        };
    }

    /**
     * @throws VariableCanNotBeExtracted
     */
    private function extractAll(string $value): ExtractionResult
    {
        return $this->extractParts(
            value: $value,
            partOffset: 0,
            valueOffset: 0,
            previousResult: ExtractionResult::success(),
        );
    }

    /**
     * Extracts variables by matching the remaining template parts against the value.
     *
     * @param int $partOffset The offset of the next template part to match.
     * @param int $valueOffset The offset of the next value character to match.
     * @param ExtractionResult $previousResult The result accumulated from the
     *                                         previously matched template parts.
     *
     * @throws VariableCanNotBeExtracted If the value cannot be matched against
     *                                   the remaining template parts.
     */
    private function extractParts(
        string $value,
        int $partOffset,
        int $valueOffset,
        ExtractionResult $previousResult,
    ): ExtractionResult {
        if ($partOffset === count($this->parts)) {
            $valueOffset === strlen($value) || throw VariableCanNotBeExtracted::dueTo('The value contains unmatched content: "'.substr($value, $valueOffset).'".', ExtractionErrorReason::UnmatchedContent);

            return $previousResult;
        }

        return $this->parts[$partOffset] instanceof Literal
            ? $this->extractLiteral($value, $partOffset, $valueOffset, $previousResult)
            : $this->extractExpression($value, $partOffset, $valueOffset, $previousResult);
    }

    /**
     * Matches the literal against the value and continues with the remaining template parts.
     *
     * @param int $partOffset The offset of the next template part to match.
     * @param int $valueOffset The offset of the next value character to match.
     * @param ExtractionResult $previousResult The result accumulated from the
     *                                         previously matched template parts.
     *
     * @throws VariableCanNotBeExtracted If the value cannot be matched against
     *                                   the remaining template parts.
     */
    private function extractLiteral(
        string $value,
        int $partOffset,
        int $valueOffset,
        ExtractionResult $previousResult,
    ): ExtractionResult {
        /** @var Literal $literal */
        $literal = $this->parts[$partOffset];
        $literal->isPrefixOf(substr($value, $valueOffset)) || throw VariableCanNotBeExtracted::dueTo('The literal "'.$literal->encoded.'" does not match the value at the expected position.', ExtractionErrorReason::LiteralMismatch);

        return $this->extractParts($value, $partOffset + 1, $valueOffset + $literal->length, $previousResult);
    }

    /**
     * Extracts the variables from the expression against the value and continues with the remaining template parts.
     *
     * @param int $partOffset The offset of the next template part to match.
     * @param int $valueOffset The offset of the next value character to match.
     * @param ExtractionResult $previousResult The result accumulated from the
     *                                         previously matched template parts.
     *
     * @throws VariableCanNotBeExtracted If the value cannot be matched against
     *                                   the remaining template parts.
     */
    private function extractExpression(
        string $value,
        int $partOffset,
        int $valueOffset,
        ExtractionResult $previousResult,
    ): ExtractionResult {
        /** @var Expression $expression */
        $expression = $this->parts[$partOffset];
        $prefix = $expression->operator->first();
        if ($expression->operator->allowEmpty() && '' !== $prefix && !str_starts_with(substr($value, $valueOffset), $prefix)) {
            return $this->extractParts($value, $partOffset + 1, $valueOffset, $previousResult->reconcile($expression->extract('')));
        }

        $expressionOffset = $this->expressionPrefix($expression, $value, $valueOffset);
        if ($partOffset + 1 === count($this->parts)) {
            return $this->extractExpressionRemainder($expression, $value, $expressionOffset, $previousResult);
        }

        $nextPart = $this->parts[$partOffset + 1];
        $delimiter = $nextPart instanceof Literal ? $nextPart->encoded : $nextPart->operator->first();

        return $this->extractExpressionCandidates($expression, $value, $partOffset, $expressionOffset, $delimiter, $previousResult);
    }

    /**
     * Extracts the last variables from the expression and matches the remaining value
     * against the end of the template.
     *
     * @param int $expressionOffset The offset of the expression value to extract.
     * @param ExtractionResult $previousResult The result accumulated from the
     *                                         previously matched template parts.
     *
     * @throws VariableCanNotBeExtracted If the expression cannot be extracted or
     *                                   the remaining value cannot be matched.
     */
    private function extractExpressionRemainder(
        Expression $expression,
        string $value,
        int $expressionOffset,
        ExtractionResult $previousResult,
    ): ExtractionResult {
        $expressionEnd = $this->expressionEnd($expression, $value, $expressionOffset);
        $lastVariables = $expression->extract(substr($value, $expressionOffset, $expressionEnd - $expressionOffset));

        return $this->extractParts($value, count($this->parts), $expressionEnd, $previousResult->reconcile($lastVariables));
    }

    /**
     * Extracts an expression by trying each possible delimiter position and
     * continues with the remaining template parts until a complete match is found.
     *
     * For an exploded expression, candidates are matched from the last delimiter
     * position to the first to prefer the longest possible value.
     *
     * When the next template part is an expression that allows an empty value and
     * its delimiter is absent, the end of the input is considered as a candidate
     * boundary for the current expression. This allows consecutive expressions
     * to be extracted when the following expression is missing.
     *
     * @param int $partOffset The offset of the expression in the template parts.
     * @param int $expressionOffset The offset of the expression value in the input.
     * @param string $delimiter The delimiter used to identify candidate expression boundaries.
     * @param ExtractionResult $previousResult The result accumulated from the
     *                                         previously matched template parts.
     *
     * @throws VariableCanNotBeExtracted If no suitable candidate can satisfy the
     *                                   expression and the remaining template parts.
     */
    private function extractExpressionCandidates(
        Expression $expression,
        string $value,
        int $partOffset,
        int $expressionOffset,
        string $delimiter,
        ExtractionResult $previousResult,
    ): ExtractionResult {
        '' !== $delimiter || throw VariableCanNotBeExtracted::dueTo('Unable to determine the delimiter for the expression "'.$expression->value.'".', ExtractionErrorReason::UndeterminedDelimiter);
        $positions = $this->delimiterPositions($value, $expressionOffset, $delimiter);
        if ([] === $positions) {
            $nextPart = $this->parts[$partOffset + 1] ?? null;
            if (!$nextPart instanceof Expression || !$nextPart->operator->allowEmpty()) {
                return $this->extractParts($value, $partOffset + 1, $expressionOffset, $previousResult->reconcile($expression->extract('')));
            }
            $positions[] = strlen($value);
        }

        if ($expression->operator->first() === $delimiter) {
            foreach ($expression as $varSpecifier) {
                if ('*' === $varSpecifier->modifier) {
                    $positions = array_reverse($positions);
                    break;
                }
            }
        }

        $reasons = [];
        $missingNames = [];
        foreach ($positions as $position) {
            try {
                $newVar = $expression->extract(substr($value, $expressionOffset, $position - $expressionOffset));

                return $this->extractParts($value, $partOffset + 1, $position, $previousResult->reconcile($newVar));
            } catch (VariableCanNotBeExtracted $exception) {
                $reasons = [...$reasons, ...$exception->reasons];
                $missingNames = [...$missingNames, ...$exception->missingNames];
            }
        }

        throw VariableCanNotBeExtracted::dueToSuitableCandidateNotFound($value, $reasons, $missingNames);
    }

    /**
     * Validates and consumes the operator prefix of an expression.
     *
     * @param int $valueOffset The offset of the expression value in the input.
     *
     *
     * @throws VariableCanNotBeExtracted If the expression prefix does not match
     *                                   the value at the expected position.
     * @return int The offset immediately after the expression prefix.
     */
    private function expressionPrefix(Expression $expression, string $value, int $valueOffset): int
    {
        $prefix = $expression->operator->first();

        return ('' === $prefix || str_starts_with(substr($value, $valueOffset), $prefix))
            ? $valueOffset + strlen($prefix)
            : throw VariableCanNotBeExtracted::dueTo('The prefix "'.$prefix.'" does not match the value for the expression "'.$expression->value.'".', ExtractionErrorReason::PrefixMismatch);
    }

    /**
     * Finds the end of an expression value according to the URI component
     * boundaries imposed by its operator.
     *
     * Query expressions stop at a fragment delimiter, while expressions in other
     * components stop at either a query or fragment delimiter. Fragment expressions
     * have no following component boundary.
     *
     * @param int $offset The offset at which the expression value starts.
     *
     * @return int The offset of the first component delimiter, or the end of the value
     *             when no delimiter is found.
     */
    private function expressionEnd(Expression $expression, string $value, int $offset): int
    {
        $delimiters = $expression->operator->nextDelimiter();
        if (null === $delimiters) {
            return strlen($value);
        }

        $length = strlen($value);

        for ($position = $offset; $position < $length; ++$position) {
            if (str_contains($delimiters, $value[$position])) {
                return $position;
            }
        }

        return $length;
    }

    /**
     * Finds all positions of a delimiter at or after the given offset.
     *
     * @param int $offset The position from which to search.
     *
     *
     * @throws ValueError If the delimiter is empty.
     * @return list<int> The positions at which the delimiter occurs.
     */
    private function delimiterPositions(string $value, int $offset, string $delimiter): array
    {
        '' !== $delimiter || throw new ValueError('The delimiter cannot be empty.');

        $positions = [];
        $position = $offset;

        while (false !== ($position = strpos($value, $delimiter, $position))) {
            $positions[] = $position;
            $position += strlen($delimiter);
        }

        return $positions;
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @throws SyntaxError if the template contains invalid expressions
     * @throws SyntaxError if the template contains invalid variable specification
     * @deprecated Since version 7.0.0
     * @codeCoverageIgnore
     * @see Template::new()
     *
     * Create a new instance from a string.
     *
     */
    #[Deprecated(message:'use League\Uri\UriTemplate\Template::new() instead', since:'league/uri:7.0.0')]
    public static function createFromString(Stringable|string $template): self
    {
        return self::new($template);
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\UriTemplate;

use League\Uri\Exceptions\SyntaxError;
use Stringable;

use function array_pad;
use function explode;
use function implode;
use function is_array;
use function mb_substr;
use function preg_match;
use function preg_replace_callback;
use function rawurldecode;
use function rawurlencode;
use function str_contains;

/**
 * Processing behavior according to the expression type operator.
 *
 * @internal The class exposes the internal representation of an Operator and its usage
 *
 * @link https://www.rfc-editor.org/rfc/rfc6570#section-2.2
 * @link https://tools.ietf.org/html/rfc6570#appendix-A
 */
enum Operator: string
{
    private const REGEXP_CHARS_INVALID = '/[\x00-\x1f\x7f]/';
    private const REGEXP_CHARS_NOT_UNRESERVED = '/[^A-Za-z0-9_\-.~]/';
    private const REGEXP_CHARS_ENCODED = ',%[A-Fa-f0-9]{2},';
    private const REGEXP_CHARS_TO_ENCODE = '/[^A-Za-z0-9_\-.~!$&\'()*+,;=%:@\/?]+|%(?![A-Fa-f0-9]{2})/';

    /**
     * Expression regular expression pattern.
     *
     * @link https://tools.ietf.org/html/rfc6570#section-2.2
     */
    private const REGEXP_EXPRESSION = '/^\{(?:(?<operator>[\.\/;\?&\=,\!@\|\+#])?(?<variables>[^\}]*))\}$/';

    /**
     * Reserved Operator characters.
     *
     * @link https://tools.ietf.org/html/rfc6570#section-2.2
     */
    private const RESERVED_OPERATOR = '=,!@|';

    case None = '';
    case ReservedChars = '+';
    case Label = '.';
    case Path = '/';
    case PathParam = ';';
    case Query = '?';
    case QueryPair = '&';
    case Fragment = '#';

    public function nextDelimiter(): ?string
    {
        return match ($this) {
            self::Query,
            self::QueryPair,
            self::ReservedChars => '#',
            self::Fragment => null,
            default => '?#',
        };
    }

    public function first(): string
    {
        return match ($this) {
            self::None, self::ReservedChars => '',
            default => $this->value,
        };
    }

    public function separator(): string
    {
        return match ($this) {
            self::None, self::ReservedChars, self::Fragment => ',',
            self::Query, self::QueryPair => '&',
            default => $this->value,
        };
    }

    public function supportsListValue(): bool
    {
        return match ($this) {
            self::None,
            self::Path,
            self::PathParam,
            self::Query,
            self::QueryPair => true,
            default => false,
        };
    }

    public function isNamed(): bool
    {
        return match ($this) {
            self::Query, self::PathParam, self::QueryPair => true,
            default => false,
        };
    }

    public function allowEmpty(): bool
    {
        return match ($this) {
            self::None, self::ReservedChars => false,
            default => true,
        };
    }

    /**
     * Removes percent encoding on reserved characters (used with + and # modifiers).
     */
    public function encode(string $value): string
    {
        if ('' === $value) {
            return $value;
        }

        $encoder = static fn (array $found): string => 1 === preg_match(self::REGEXP_CHARS_NOT_UNRESERVED, rawurldecode($found[0]))
            ? rawurlencode($found[0])
            : $found[0];

        return match ($this) {
            self::Fragment,
            self::ReservedChars => (string) preg_replace_callback(self::REGEXP_CHARS_TO_ENCODE, $encoder, $value),
            default => rawurlencode($value),
        };
    }

    /**
     * Decodes a URI template value.
     *
     * @throws SyntaxError if the value to decode contains invalid characters.
     */
    public function decode(string $value): string
    {
        if ('' === $value) {
            return '';
        }

        1 !== preg_match(self::REGEXP_CHARS_INVALID, $value) || throw new SyntaxError('The value contains invalid encoded characters; "'.$value.'".');

        $decoder = static fn (array $matches): string => match ($matches[0]) {
            '%20', '%2F' => $matches[0],
            default => rawurldecode($matches[0]),
        };

        return match ($this) {
            self::Fragment,
            self::ReservedChars => (string) preg_replace_callback(self::REGEXP_CHARS_ENCODED, $decoder, $value),
            default => rawurldecode($value),
        };
    }

    /**
     * @throws SyntaxError if the expression is invalid
     * @throws SyntaxError if the operator used in the expression is invalid
     * @throws SyntaxError if the contained variable specifiers are invalid
     *
     * @return array{operator:Operator, variables:string}
     */
    public static function parseExpression(Stringable|string $expression): array
    {
        $expression = (string) $expression;
        if (1 !== preg_match(self::REGEXP_EXPRESSION, $expression, $parts)) {
            throw new SyntaxError('The expression "'.$expression.'" is invalid.');
        }

        $parts = $parts + ['operator' => ''];
        if ('' !== $parts['operator'] && str_contains(self::RESERVED_OPERATOR, $parts['operator'])) {
            throw new SyntaxError('The operator used in the expression "'.$expression.'" is reserved.');
        }

        return [
            'operator' => self::from($parts['operator']),
            'variables' => $parts['variables'],
        ];
    }

    /**
     * Replaces an expression with the given variables.
     *
     * @throws TemplateCanNotBeExpanded if the variables is an array and a ":" modifier needs to be applied
     * @throws TemplateCanNotBeExpanded if the variables contains nested array values
     */
    public function expand(VarSpecifier $varSpecifier, VariableBag $variables): string
    {
        $value = $variables->fetch($varSpecifier->name);
        if (null === $value) {
            return '';
        }

        [$expanded, $actualQuery] = $this->inject($value, $varSpecifier);
        if (!$actualQuery) {
            return $expanded;
        }

        if ('&' !== $this->separator() && '' === $expanded) {
            return $varSpecifier->name;
        }

        return $varSpecifier->name.'='.$expanded;
    }

    /**
     * @param string|array<string> $value
     *
     * @return array{0:string, 1:bool}
     */
    private function inject(array|string $value, VarSpecifier $varSpec): array
    {
        if (is_array($value)) {
            return $this->replaceList($value, $varSpec);
        }

        if (':' === $varSpec->modifier) {
            $value = mb_substr($value, 0, $varSpec->position, 'UTF-8');
        }

        return [$this->encode($value), $this->isNamed()];
    }

    /**
     * Expands an expression using a list of values.
     *
     * @param array<string> $value
     *
     * @throws TemplateCanNotBeExpanded if the variables is an array and a ":" modifier needs to be applied
     *
     * @return array{0:string, 1:bool}
     */
    private function replaceList(array $value, VarSpecifier $varSpec): array
    {
        ':' !== $varSpec->modifier || throw TemplateCanNotBeExpanded::dueToUnableToProcessValueListWithPrefix($varSpec->name);

        if ([] === $value) {
            return ['', false];
        }

        $pairs = [];
        $isList = array_is_list($value);
        $useQuery = $this->isNamed();
        foreach ($value as $key => $var) {
            if (!$isList) {
                $key = rawurlencode((string) $key);
            }

            $var = $this->encode($var);
            if ('*' === $varSpec->modifier) {
                if (!$isList) {
                    $var = $key.'='.$var;
                } elseif ($key > 0 && $useQuery) {
                    $var = $varSpec->name.'='.$var;
                }
            }

            $pairs[$key] = $var;
        }

        if ('*' === $varSpec->modifier) {
            if (!$isList) {
                // Don't prepend the value name when using the `explode` modifier with an associative array.
                $useQuery = false;
            }

            return [implode($this->separator(), $pairs), $useQuery];
        }

        if (!$isList) {
            // When an associative array is encountered and the `explode` modifier is not set, then
            // the result must be a comma separated list of keys followed by their respective values.
            $retVal = [];
            foreach ($pairs as $offset => $data) {
                $retVal[$offset] = $offset.','.$data;
            }
            $pairs = $retVal;
        }

        return [implode(',', $pairs), $useQuery];
    }

    /**
     * Extracts a variable from an operator value.
     *
     * A null value represents an absent variable. Exploded variables are delegated
     * to list extraction, while named non-exploded variables must contain the
     * expected variable name. Path and fragment operators may represent non-exploded
     * values as key/value pairs. Extracted values are percent-decoded before being
     * returned.
     *
     * @throws VariableCanNotBeExtracted If the value cannot be extracted according
     *                                   to the variable specifier.
     */
    public function extract(VarSpecifier $varSpecifier, string|null $value): ExtractionResult
    {
        $content = match (true) {
            null === $value => ExtractedValue::fromNull(),
            '*' === $varSpecifier->modifier => ExtractedValue::fromList($value, $this, $varSpecifier),
            $this->isNamed() => $this->extractNamedValue($varSpecifier, $value),
            self::Fragment === $this => $this->extractFragmentValue($varSpecifier, $value),
            default => ExtractedValue::fromValue($value, $this, $varSpecifier),
        };

        return  ExtractionResult::success([$varSpecifier->name => $content]);
    }

    /**
     * @throws VariableCanNotBeExtracted
     */
    private function extractNamedValue(VarSpecifier $varSpecifier, string $value): ExtractedValue
    {
        [$name, $value] = array_pad(explode('=', $value, 2), 2, '');
        $name === $varSpecifier->name || throw VariableCanNotBeExtracted::dueTo('The named variable is invalid.', ExtractionErrorReason::VariableMismatch);

        return self::PathParam === $this && str_contains($value, ',')
            ? ExtractedValue::fromNamedValue($value, $this)
            : ExtractedValue::fromValue($value, $this, $varSpecifier);
    }

    private function extractFragmentValue(VarSpecifier $varSpecifier, string $value): ExtractedValue
    {
        return str_contains($value, ',')
            ? ExtractedValue::fromList($value, $this, $varSpecifier)
            : ExtractedValue::fromValue($value, $this, $varSpecifier);
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\UriTemplate;

use ArrayAccess;
use BackedEnum;
use Closure;
use Countable;
use IteratorAggregate;
use League\Uri\StringCoercionMode;
use Stringable;
use Traversable;

use function array_filter;
use function array_key_exists;
use function array_map;
use function count;
use function is_array;

use const ARRAY_FILTER_USE_BOTH;

/**
 * @internal The class exposes the internal representation of variable bags
 *
 * @phpstan-type InputValue string|bool|int|float|array<string|bool|int|float>
 *
 * @implements ArrayAccess<string, InputValue>
 * @implements IteratorAggregate<string, InputValue>
 */
final class VariableBag implements ArrayAccess, Countable, IteratorAggregate
{
    /**
     * @var array<string,string|array<string>>
     */
    private array $variables = [];

    /**
     * @param iterable<array-key, InputValue>|ExtractionResult $variables
     */
    public function __construct(iterable|ExtractionResult $variables = [])
    {
        if ($variables instanceof ExtractionResult) {
            $variables = array_filter($variables->variables(), static fn (array|string|null $value): bool => null !== $value);
        }

        foreach ($variables as $name => $value) {
            $this->assign((string) $name, $value);
        }
    }

    public function count(): int
    {
        return count($this->variables);
    }

    public function getIterator(): Traversable
    {
        yield from $this->variables;
    }

    public function offsetExists(mixed $offset): bool
    {
        return array_key_exists($offset, $this->variables);
    }

    public function offsetUnset(mixed $offset): void
    {
        unset($this->variables[$offset]);
    }

    public function offsetSet(mixed $offset, mixed $value): void
    {
        $this->assign($offset, $value); /* @phpstan-ignore-line */
    }

    /**
     * @return null|string|array<string>
     */
    public function offsetGet(mixed $offset): null|string|array
    {
        return $this->fetch($offset);
    }

    /**
     * Tells whether the bag is empty or not.
     */
    public function isEmpty(): bool
    {
        return [] === $this->variables;
    }

    /**
     * Tells whether the bag is empty or not.
     */
    public function isNotEmpty(): bool
    {
        return [] !== $this->variables;
    }

    public function equals(mixed $value): bool
    {
        return $value instanceof self
            && $this->variables === $value->variables;
    }

    /**
     * Fetches the variable value if none found returns null.
     *
     * @return null|string|array<string>
     */
    public function fetch(string $name): null|string|array
    {
        return $this->variables[$name] ?? null;
    }

    /**
     * @param Stringable|InputValue $value
     */
    public function assign(string $name, BackedEnum|Stringable|string|bool|int|float|array|null $value): void
    {
        $this->variables[$name] = self::normalizeValue($value, $name, isNestedListAllowed: true);
    }

    /**
     * @param Stringable|InputValue $value
     *
     * @throws TemplateCanNotBeExpanded if the value contains nested list
     */
    private static function normalizeValue(
        BackedEnum|Stringable|string|bool|int|float|array|null $value,
        string $name,
        bool $isNestedListAllowed
    ): array|string {
        return match (true) {
            !is_array($value) => (string) StringCoercionMode::Native->coerce($value),
            !$isNestedListAllowed => throw TemplateCanNotBeExpanded::dueToNestedListOfValue($name),
            default => array_map(fn ($var) => self::normalizeValue($var, $name, isNestedListAllowed: false), $value),
        };
    }

    /**
     * Replaces elements from passed variables into the current instance.
     */
    public function replace(VariableBag $variables): self
    {
        return new self($this->variables + $variables->variables);
    }

    /**
     * Filters elements using the closure.
     */
    public function filter(Closure $fn): self
    {
        return new self(array_filter($this->variables, $fn, ARRAY_FILTER_USE_BOTH));
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri;

use Deprecated;
use League\Uri\Contracts\UriInterface;
use Psr\Http\Message\UriInterface as Psr7UriInterface;

/**
 * @deprecated since version 7.0.0
 * @codeCoverageIgnore
 * @see BaseUri
 */
final class UriInfo
{
    /**
     * @codeCoverageIgnore
     */
    private function __construct()
    {
    }

    /**
     * Tells whether the URI represents an absolute URI.
     */
    #[Deprecated(message:'use League\Uri\BaseUri::isAbsolute() instead', since:'league/uri:7.0.0')]
    public static function isAbsolute(Psr7UriInterface|UriInterface $uri): bool
    {
        return BaseUri::from($uri)->isAbsolute();
    }

    /**
     * Tell whether the URI represents a network path.
     */
    #[Deprecated(message:'use League\Uri\BaseUri::isNetworkPath() instead', since:'league/uri:7.0.0')]
    public static function isNetworkPath(Psr7UriInterface|UriInterface $uri): bool
    {
        return BaseUri::from($uri)->isNetworkPath();
    }

    /**
     * Tells whether the URI represents an absolute path.
     */
    #[Deprecated(message:'use League\Uri\BaseUri::isAbsolutePath() instead', since:'league/uri:7.0.0')]
    public static function isAbsolutePath(Psr7UriInterface|UriInterface $uri): bool
    {
        return BaseUri::from($uri)->isAbsolutePath();
    }

    /**
     * Tell whether the URI represents a relative path.
     *
     */
    #[Deprecated(message:'use League\Uri\BaseUri::isRelativePath() instead', since:'league/uri:7.0.0')]
    public static function isRelativePath(Psr7UriInterface|UriInterface $uri): bool
    {
        return BaseUri::from($uri)->isRelativePath();
    }

    /**
     * Tells whether both URI refers to the same document.
     */
    #[Deprecated(message:'use League\Uri\BaseUri::isSameDocument() instead', since:'league/uri:7.0.0')]
    public static function isSameDocument(Psr7UriInterface|UriInterface $uri, Psr7UriInterface|UriInterface $baseUri): bool
    {
        return BaseUri::from($baseUri)->isSameDocument($uri);
    }

    /**
     * Returns the URI origin property as defined by WHATWG URL living standard.
     *
     * {@see https://url.spec.whatwg.org/#origin}
     *
     * For URI without a special scheme the method returns null
     * For URI with the file scheme the method will return null (as this is left to the implementation decision)
     * For URI with a special scheme the method returns the scheme followed by its authority (without the userinfo part)
     */
    #[Deprecated(message:'use League\Uri\BaseUri::origin() instead', since:'league/uri:7.0.0')]
    public static function getOrigin(Psr7UriInterface|UriInterface $uri): ?string
    {
        return BaseUri::from($uri)->origin()?->__toString();
    }

    /**
     * Tells whether two URI do not share the same origin.
     *
     * @see UriInfo::getOrigin()
     */
    #[Deprecated(message:'use League\Uri\BaseUri::isCrossOrigin() instead', since:'league/uri:7.0.0')]
    public static function isCrossOrigin(Psr7UriInterface|UriInterface $uri, Psr7UriInterface|UriInterface $baseUri): bool
    {
        return BaseUri::from($baseUri)->isCrossOrigin($uri);
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri;

use League\Uri\Components\Host;
use League\Uri\Contracts\AuthorityInterface;
use League\Uri\Contracts\HostInterface;
use League\Uri\Contracts\UriInterface;
use League\Uri\Exceptions\MissingFeature;
use League\Uri\IPv4\Calculator;
use League\Uri\IPv4\Converter;
use Psr\Http\Message\UriInterface as Psr7UriInterface;
use Stringable;

/**
 * DEPRECATION WARNING! This class will be removed in the next major point release.
 *
 * @deprecated Since version 7.0.0
 * @see Converter
 *
 * @codeCoverageIgnore
 */
final class IPv4Normalizer
{
    private readonly Converter $converter;

    public function __construct(
        Converter|Calculator $calculator
    ) {
        if (!$calculator instanceof Converter) {
            $calculator = new Converter($calculator);
        }

        $this->converter = $calculator;
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @see Converter::toDecimal()
     *
     * @codeCoverageIgnore
     *
     * Normalizes the host content to a IPv4 dot-decimal notation if possible
     * otherwise returns the Host instance unchanged.
     *
     * @see https://url.spec.whatwg.org/#concept-ipv4-parser
     */
    public function normalize(Stringable|string|null $host): ?string
    {
        return $this->converter->toDecimal($host);
    }

    /**
     * Returns an instance using a GMP calculator.
     */
    public static function createFromGMP(): self
    {
        return new self(Converter::fromGMP());
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @see Converter::fromBCMath()
     *
     * @codeCoverageIgnore
     *
     * Returns an instance using a Bcmath calculator.
     */
    public static function createFromBCMath(): self
    {
        return new self(Converter::fromBCMath());
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @see Converter::fromNative()
     *
     * @codeCoverageIgnore
     *
     * Returns an instance using a PHP native calculator (requires 64bits PHP).
     */
    public static function createFromNative(): self
    {
        return new self(Converter::fromNative());
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @throws MissingFeature If no IPv4Calculator implementing object can be used on the platform
     *
     * @codeCoverageIgnore
     * @see Converter::fromEnvironment()
     *
     * @codeCoverageIgnore
     *
     * Returns an instance using a detected calculator depending on the PHP environment.
     *
     * @deprecated Since version 7.0.0
     */
    public static function createFromServer(): self
    {
        return new self(Converter::fromEnvironment());
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @see Modifier::hostToDecimal()
     *
     * @codeCoverageIgnore
     *
     * Normalizes the URI host content to a IPv4 dot-decimal notation if possible
     * otherwise returns the uri instance unchanged.
     *
     * @see https://url.spec.whatwg.org/#concept-ipv4-parser
     */
    public function normalizeUri(UriInterface|Psr7UriInterface $uri): UriInterface|Psr7UriInterface
    {
        $host = $uri->getHost();
        $decimalIPv4 = $this->converter->toDecimal($host);

        return match (true) {
            null === $decimalIPv4,
            $decimalIPv4 === $host => $uri,
            default => $uri->withHost($decimalIPv4),
        };
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @see Modifier::hostToDecimal()
     *
     * @codeCoverageIgnore
     *
     * Normalizes the authority host content to a IPv4 dot-decimal notation if possible
     * otherwise returns the uri instance unchanged.
     *
     * @see https://url.spec.whatwg.org/#concept-ipv4-parser
     */
    public function normalizeAuthority(AuthorityInterface $authority): AuthorityInterface
    {
        $host = $authority->getHost();
        $decimalIpv4 = $this->converter->toDecimal($host);

        return match (true) {
            null === $decimalIpv4,
            $decimalIpv4 === $host => $authority,
            default => $authority->withHost($decimalIpv4),
        };
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @see Modifier::hostToDecimal()
     *
     * @codeCoverageIgnore
     *
     * Normalizes the host content to a IPv4 dot-decimal notation if possible
     * otherwise returns the Host instance unchanged.
     *
     * @see https://url.spec.whatwg.org/#concept-ipv4-parser
     */
    public function normalizeHost(HostInterface $host): HostInterface
    {
        $decimalIPv4 = $this->converter->toDecimal($host->value());

        return match (null) {
            $decimalIPv4 => $host,
            default => Host::new($decimalIPv4),
        };
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri;

use BackedEnum;
use Deprecated;
use Dom\HTMLDocument;
use DOMDocument;
use DOMException;
use JsonSerializable;
use League\Uri\Components\DataPath;
use League\Uri\Components\Domain;
use League\Uri\Components\Fragment;
use League\Uri\Components\FragmentDirectives;
use League\Uri\Components\HierarchicalPath;
use League\Uri\Components\Host;
use League\Uri\Components\Path;
use League\Uri\Components\Query;
use League\Uri\Components\URLSearchParams;
use League\Uri\Contracts\Conditionable;
use League\Uri\Contracts\FragmentDirective;
use League\Uri\Contracts\FragmentInterface;
use League\Uri\Contracts\PathInterface;
use League\Uri\Contracts\Transformable;
use League\Uri\Contracts\UriAccess;
use League\Uri\Contracts\UriInterface;
use League\Uri\Exceptions\MissingFeature;
use League\Uri\Exceptions\SyntaxError;
use League\Uri\Idna\Converter as IdnaConverter;
use League\Uri\IPv4\Converter as IPv4Converter;
use League\Uri\IPv6\Converter as IPv6Converter;
use League\Uri\KeyValuePair\Converter as KeyValuePairConverter;
use Psr\Http\Message\UriFactoryInterface;
use Psr\Http\Message\UriInterface as Psr7UriInterface;
use SensitiveParameter;
use Stringable;
use Uri\Rfc3986\Uri as Rfc3986Uri;
use Uri\WhatWg\Url as WhatWgUrl;
use ValueError;

use function array_keys;
use function class_exists;
use function count;
use function filter_var;
use function implode;
use function in_array;
use function is_array;
use function is_bool;
use function is_string;
use function ltrim;
use function rtrim;
use function str_ends_with;
use function str_starts_with;
use function strpos;
use function strtolower;
use function substr;
use function trim;

use const FILTER_FLAG_IPV4;
use const FILTER_VALIDATE_IP;

class Modifier implements Stringable, JsonSerializable, UriAccess, Conditionable, Transformable
{
    private const MASK = '*****';

    final public function __construct(protected readonly Rfc3986Uri|WhatWgUrl|Psr7UriInterface|UriInterface $uri)
    {
    }

    public static function wrap(Rfc3986Uri|WhatWgUrl|BackedEnum|Stringable|string $uri): static
    {
        return new static(match (true) {
            $uri instanceof self => $uri->uri,
            $uri instanceof Psr7UriInterface,
            $uri instanceof UriInterface,
            $uri instanceof Rfc3986Uri,
            $uri instanceof WhatWgUrl => $uri,
            default => Uri::new($uri),
        });
    }

    public function unwrap(): Rfc3986Uri|WhatWgUrl|Psr7UriInterface|UriInterface
    {
        return $this->uri;
    }

    public function jsonSerialize(): string
    {
        return $this->toString();
    }

    public function __toString(): string
    {
        return $this->toString();
    }

    public function toString(): string
    {
        return match (true) {
            $this->uri instanceof Rfc3986Uri,
            $this->uri instanceof UriInterface => $this->uri->toString(),
            $this->uri instanceof WhatWgUrl => $this->uri->toAsciiString(),
            $this->uri instanceof Psr7UriInterface => $this->uri->__toString(),
        };
    }

    public function toDisplayString(): string
    {
        return ($this->uri instanceof Uri ? $this->uri : Uri::new($this->toString()))->toDisplayString();
    }

    /**
     * Returns the Markdown string representation of the anchor tag with the current instance as its href attribute.
     */
    public function toMarkdownAnchor(?string $textContent = null): string
    {
        return '['.strtr($textContent ?? '{uri}', ['{uri}' => $this->toDisplayString()]).']('.$this->toString().')';
    }

    /**
     * Returns the HTML string representation of the anchor tag with the current instance as its href attribute.
     *
     * @param iterable<string, string|null|array<string>> $attributes an ordered map of key value. you must quote the value if needed
     *
     * @throws DOMException
     */
    public function toHtmlAnchor(Stringable|string|null $textContent = null, iterable $attributes = []): string
    {
        FeatureDetection::supportsDom();
        $uriString = $this->toString();
        $rfc3987String = UriString::toIriString($uriString);
        $doc = class_exists(HTMLDocument::class) ? HTMLDocument::createEmpty() : new DOMDocument(encoding:'utf-8');
        $element = $doc->createElement('a');
        $element->setAttribute('href', $uriString);
        $element->appendChild(match (true) {
            null === $textContent => $doc->createTextNode($rfc3987String),
            default => $doc->createTextNode(strtr((string) $textContent, ['{uri}' => $rfc3987String])),
        });

        foreach ($attributes as $name => $value) {
            if ('href' === strtolower($name) || null === $value) {
                continue;
            }

            if (is_array($value)) {
                $value = implode(' ', $value);
            }

            is_string($value) || throw new ValueError('The attribute `'.$name.'` contains an invalid value.');
            $value = trim($value);
            if ('' === $value) {
                continue;
            }

            $element->setAttribute($name, $value);
        }

        false !== ($html = $doc->saveHTML($element)) || throw new DOMException('The HTML generation failed.');

        return $html;
    }

    public function resolve(Rfc3986Uri|WhatWgUrl|Psr7UriInterface|UriInterface|BackedEnum|Stringable|string $uri): static
    {
        $uriString = match (true) {
            $uri instanceof Rfc3986Uri,
            $uri instanceof UriInterface => $uri->toString(),
            $uri instanceof WhatWgUrl => $uri->toAsciiString(),
            $uri instanceof BackedEnum => (string) $uri->value,
            default => (string) $uri,
        };

        if (!$this->uri instanceof Psr7UriInterface) {
            return new static($this->uri->resolve($uriString));
        }

        $components = UriString::parse(UriString::resolve($uriString, $this->toString()));

        return new static(
            $this->uri
                ->withFragment($components['fragment'] ?? '')
                ->withQuery($components['query'] ?? '')
                ->withPath($components['path'] ?? '')
                ->withHost($components['host'] ?? '')
                ->withPort($components['port'] ?? null)
                ->withUserInfo($components['user'] ?? '', $components['pass'] ?? null)
                ->withScheme($components['scheme'] ?? '')
        );
    }

    public function normalize(): static
    {
        if ($this->uri instanceof WhatWgUrl) {
            return $this;
        }

        if ($this->uri instanceof Rfc3986Uri) {
            return new static(new Rfc3986Uri($this->uri->toString()));
        }

        if ($this->uri instanceof UriInterface) {
            return new static($this->uri->normalize());
        }

        $uri = Uri::new($this->uri->__toString())->normalize();
        if ($uri->toString() === $this->uri->__toString()) {
            return $this;
        }

        return new static(
            $this->uri
                ->withPath($uri->getPath())
                ->withHost($uri->getHost() ?? '')
                ->withUserInfo($uri->getUsername() ?? '', $uri->getPassword())
        );
    }

    public function withScheme(BackedEnum|Stringable|string|null $scheme): static
    {
        return new static($this->uri->withScheme(self::normalizeComponent($scheme, $this->uri)));
    }

    public function withUserInfo(
        Stringable|string|null $username,
        #[SensitiveParameter] Stringable|string|null $password
    ): static {
        if ($this->uri instanceof Rfc3986Uri) {
            $userInfo = Encoder::encodeUser($username);
            if (null !== $password) {
                $userInfo .= ':'.Encoder::encodePassword($password);
            }

            return new static($this->uri->withUserInfo($userInfo));
        }

        if ($this->uri instanceof WhatWgUrl) {
            if (null !== $username) {
                if ($username instanceof BackedEnum) {
                    $username = $username->value;
                }
                $username = (string) $username;
            }

            if (null !== $password) {
                if ($password instanceof BackedEnum) {
                    $password = $password->value;
                }
                $password = (string) $password;
            }

            return new static($this->uri->withUsername($username)->withPassword($password));
        }

        if (null === $username && $this->uri instanceof Psr7UriInterface) {
            $username = '';
        }

        if ($username instanceof BackedEnum) {
            $username = (string) $username->value;
        }

        if ($password instanceof BackedEnum) {
            $password = (string) $password->value;
        }

        return new static($this->uri->withUserInfo(
            $username instanceof Stringable ? (string) $username : $username,
            $password instanceof Stringable ? (string) $password : $password,
        ));
    }

    /**
     * Returns a new instance with the entire UserInfo component redacted.
     *
     * Examples:
     *   http://user:pass@host â†’ http://[REDACTED]@host
     *   http://user@host      â†’ http://[REDACTED]@host
     */
    public function redactUserInfo(): static
    {
        if ($this->uri instanceof WhatWgUrl) {
            if (null !== $this->uri->getUsername() || null !== $this->uri->getPassword()) {
                return new static($this->uri->withUsername(self::MASK)->withPassword(null));
            }

            return $this;
        }

        if (null === $this->uri->getUserInfo()) {
            return $this;
        }

        return new static($this->uri->withUserInfo(self::MASK));
    }

    public function withHost(BackedEnum|Stringable|string|null $host): static
    {
        $host = self::normalizeComponent($host, $this->uri);
        if ($this->uri instanceof Rfc3986Uri) {
            if (null !== $host) {
                $host = IdnaConverter::toAscii($host)->domain();
            }
        }

        return new static($this->uri->withHost($host));
    }

    public function withFragment(BackedEnum|Stringable|string|null $fragment): static
    {
        if ($fragment instanceof FragmentDirective) {
            $fragment = new FragmentDirectives($fragment);
        }

        if ($fragment instanceof BackedEnum) {
            $fragment = (string) $fragment->value;
        }

        if (!$fragment instanceof FragmentInterface) {
            $fragment = str_starts_with((string) $fragment, FragmentDirectives::DELIMITER)
                ? FragmentDirectives::fromFragment($fragment)
                : Fragment::new($fragment);
        }

        return new static($this->uri->withFragment(
            $this->uri instanceof Psr7UriInterface
                ? $fragment->toString()
                : $fragment->value()
        ));
    }

    public function withPort(?int $port): static
    {
        return new static($this->uri->withPort($port));
    }

    public function withPath(BackedEnum|Stringable|string $path): static
    {
        if ($this->uri instanceof Rfc3986Uri) {
            $path = Encoder::encodePath($path);
        }

        return new static(self::normalizePath($this->uri, Path::new($path)));
    }

    final public function transform(callable $callback): static
    {
        return $callback($this);
    }

    final public function when(callable|bool $condition, callable $onSuccess, ?callable $onFail = null): static
    {
        if (!is_bool($condition)) {
            $condition = $condition($this);
        }

        return match (true) {
            $condition => $onSuccess($this),
            null !== $onFail => $onFail($this),
            default => $this,
        } ?? $this;
    }

    /*********************************
     * Query modifier methods
     *********************************/

    public function withQuery(BackedEnum|Stringable|string|null $query): static
    {
        $query = self::normalizeComponent($query, $this->uri);
        $query = match (true) {
            $this->uri instanceof Rfc3986Uri => match (true) {
                Encoder::isQueryEncoded($query) => $query,
                default => Encoder::encodeQueryOrFragment($query),
            },
            $this->uri instanceof WhatWgUrl => URLSearchParams::new($query)->value(),
            default => $query,
        };

        return match (true) {
            $this->uri instanceof Rfc3986Uri && $query === $this->uri->getRawQuery(),
            $query === $this->uri->getQuery() => $this,
            default => new static($this->uri->withQuery($query)),
        };
    }

    /**
     * Change the encoding of the query.
     */
    public function encodeQuery(KeyValuePairConverter|int $to, KeyValuePairConverter|int|null $from = null, StringCoercionMode $coercionMode = StringCoercionMode::Native): static
    {
        if (!$to instanceof KeyValuePairConverter) {
            $to = KeyValuePairConverter::fromEncodingType($to);
        }

        $from = match (true) {
            null === $from => KeyValuePairConverter::fromRFC3986(),
            !$from instanceof KeyValuePairConverter => KeyValuePairConverter::fromEncodingType($from),
            default => $from,
        };

        /* @phpstan-ignore-next-line */
        if ($to == $from) {
            return $this;
        }

        $originalQuery = $this->uri->getQuery();
        if (null === $originalQuery || '' === trim($originalQuery)) {
            return $this;
        }

        /** @var string $query */
        $query = QueryString::buildFromPairs(QueryString::parseFromValue($originalQuery, $from), $to, $coercionMode);
        if ($query === $originalQuery) {
            return $this;
        }

        return $this->withQuery($query);
    }

    /**
     * Sort the URI query by keys.
     */
    public function sortQuery(): static
    {
        return $this->withQuery(Query::fromUri($this->uri)->sort()->value());
    }

    /**
     * Append the new query data to the existing URI query.
     */
    public function appendQuery(BackedEnum|Stringable|string|null $query, StringCoercionMode $coercionMode = StringCoercionMode::Native): static
    {
        return $this->withQuery(Query::fromUri($this->uri)->append($query, $coercionMode)->value());
    }

    /**
     * Prepend the new query data to the existing URI query.
     */
    public function prependQuery(BackedEnum|Stringable|string|null $query, StringCoercionMode $coercionMode = StringCoercionMode::Native): static
    {
        return $this->withQuery(Query::fromUri($this->uri)->prepend($query, $coercionMode)->value());
    }

    /**
     * Merge query pairs with the existing URI query.
     *
     * @param iterable<int, array{0:string, 1:string|null}> $pairs
     */
    public function appendQueryPairs(iterable $pairs, string $prefix = '', StringCoercionMode $coercionMode = StringCoercionMode::Native): self
    {
        return $this->appendQuery(Query::fromPairs($pairs, prefix: $prefix, coercionMode: $coercionMode)->value());
    }

    public function prefixQueryPairs(string $prefix, StringCoercionMode $coercionMode = StringCoercionMode::Native): self
    {
        return $this->withQuery(Query::fromPairs(Query::fromUri($this->uri), prefix: $prefix, coercionMode: $coercionMode)->value());
    }

    public function prefixQueryParameters(string $prefix, QueryComposeMode $composeMode = QueryComposeMode::Native): self
    {
        return $this->withQuery(Query::fromVariable(Query::fromUri($this->uri)->parameters(), prefix: $prefix, composeMode: $composeMode));
    }

    /**
     * Append PHP query parameters to the existing URI query.
     */
    public function appendQueryParameters(object|array $parameters, string $prefix = '', QueryComposeMode $composeMode = QueryComposeMode::Native): self
    {
        return $this->appendQuery(Query::fromVariable($parameters, prefix: $prefix, composeMode: $composeMode)->value());
    }

    /**
     * Prepend PHP query parameters to the existing URI query.
     */
    public function prependQueryParameters(object|array $parameters, string $prefix = '', QueryComposeMode $composeMode = QueryComposeMode::Native): self
    {
        return $this->withQuery(Query::fromVariable($parameters, prefix: $prefix, composeMode: $composeMode)->append(Query::fromUri($this->uri)->value())->value());
    }

    public function replaceQueryParameter(string $name, mixed $value, QueryComposeMode $composeMode = QueryComposeMode::Native): self
    {
        return $this->withQuery(Query::fromUri($this->uri)->replaceParameter($name, $value, $composeMode)->value());
    }

    /**
     * Merge a new query with the existing URI query.
     */
    public function mergeQuery(BackedEnum|Stringable|string|null $query, StringCoercionMode $coercionMode = StringCoercionMode::Native): static
    {
        return $this->withQuery(Query::fromUri($this->uri)->merge($query, $coercionMode)->value());
    }

    /**
     * Returns a new instance with the specified query values redacted.
     *
     * Only values are redacted. Missing keys are ignored.
     *
     * Example: redactQueryPairs(token)
     *   ?token=abc&mode=edit  â†’ token=[REDACTED]&mode=edit (when 'token' is passed)
     */
    public function redactQueryPairs(string ...$keys): static
    {
        if ([] === $keys) {
            return $this;
        }

        $hasChanged = false;
        $pairs = [];
        foreach (QueryString::parse($this->uri->getQuery()) as $pair) {
            if (in_array($pair[0], $keys, true)) {
                $hasChanged = true;
                $pair[1] = self::MASK;
            }

            $pairs[] = $pair;
        }

        return $hasChanged ? $this->withQuery(QueryString::build($pairs)) : $this;
    }

    /**
     * Merge query pairs with the existing URI query.
     *
     * @param iterable<int, array{0:string, 1:string|null}> $pairs
     */
    public function mergeQueryPairs(iterable $pairs, string $prefix = '', StringCoercionMode $coercionMode = StringCoercionMode::Native): self
    {
        $currentPairs = [...Query::fromUri($this->uri)->pairs()];
        $pairs = [...$pairs];

        return match (true) {
            [] === $pairs,
            $currentPairs === $pairs => $this,
            default => $this->mergeQuery(Query::fromPairs($pairs, prefix: $prefix, coercionMode: $coercionMode)->value()),
        };
    }

    /**
     * Merge PHP query parameters with the existing URI query.
     */
    public function mergeQueryParameters(object|array $parameters, string $prefix = '', QueryComposeMode $composeMode = QueryComposeMode::Native): self
    {
        return $this->withQuery(Query::fromUri($this->uri)->mergeParameters($parameters, prefix: $prefix, composeMode: $composeMode)->value());
    }

    /**
     * Remove query data according to their key name.
     */
    public function removeQueryPairsByKey(string ...$keys): static
    {
        $query = Query::fromUri($this->uri);
        $newQuery = $query->withoutPairByKey(...$keys);

        return $newQuery->value() === $query->value() ? $this : $this->withQuery($newQuery);
    }

    /**
     * Remove query pair according to their value.
     */
    public function removeQueryPairsByValue(array|BackedEnum|Stringable|string|int|float|bool|null $values, StringCoercionMode $coercionMode = StringCoercionMode::Native): static
    {
        $query = Query::fromUri($this->uri);
        $newQuery = $query->withoutPairByValue($values, $coercionMode);

        return $newQuery->value() === $query->value() ? $this : $this->withQuery($newQuery);
    }

    /**
     * Remove query-pair according to their key/value name.
     */
    public function removeQueryPairsByKeyValue(string $key, BackedEnum|Stringable|string|int|bool|null $value, StringCoercionMode $coercionMode = StringCoercionMode::Native): static
    {
        $query = Query::fromUri($this->uri);
        $newQuery = $query->withoutPairByKeyValue($key, $value, $coercionMode);

        return $newQuery->value() === $query->value() ? $this : $this->withQuery($newQuery);
    }

    /**
     * Remove query data according to their PHP parameter key name.
     */
    public function removeQueryParameters(string ...$keys): static
    {
        $query = Query::fromUri($this->uri);
        $newQuery = $query->withoutParameters(...$keys);

        return $newQuery->value() === $query->value() ? $this : $this->withQuery($newQuery);
    }

    /**
     * Remove empty pairs from the URL query component.
     *
     * A pair is considered empty if its name is the empty string
     * and its value is either the empty string or the null value
     */
    public function removeEmptyQueryPairs(): static
    {
        return $this->withQuery(Query::fromUri($this->uri)->withoutEmptyPairs()->value());
    }

    /**
     * Returns an instance where numeric indices associated to PHP's array like key are removed.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the query component normalized so that numeric indexes
     * are removed from the pair key value.
     *
     * ie.: toto[3]=bar[3]&foo=bar becomes toto[]=bar[3]&foo=bar
     */
    public function removeQueryParameterIndices(): static
    {
        $query = Query::fromUri($this->uri);
        $newQuery = $query->withoutNumericIndices()->value();

        return match ($newQuery) {
            $query->value() => $this,
            default => $this->withQuery($newQuery),
        };
    }

    public function replaceQueryPair(int $offset, string $key, BackedEnum|Stringable|string|int|float|bool|null $value, StringCoercionMode $coercionMode = StringCoercionMode::Native): static
    {
        return $this->withQuery(Query::fromUri($this->uri)->replace($offset, $key, $value, $coercionMode)->value());
    }

    /*********************************
     * Host modifier methods
     *********************************/

    /**
     * Add the root label to the URI.
     */
    public function addRootLabel(): static
    {
        $host = $this->uri instanceof WhatWgUrl ? $this->uri->getAsciiHost() : $this->uri->getHost();

        return match (true) {
            null === $host,
            str_ends_with($host, '.') => $this,
            default => $this->withHost($host.'.'),
        };
    }

    /**
     * Append a label or a host to the current URI host.
     *
     * @throws SyntaxError If the host cannot be appended
     */
    public function appendLabel(BackedEnum|Stringable|string|null $label): static
    {
        $host = $this->uri instanceof WhatWgUrl ? $this->uri->getAsciiHost() : $this->uri->getHost();
        $isAsciiDomain = null === $host || IdnaConverter::toAscii($host)->domain() === $host;

        $host = Host::new($host);
        $label = Host::new($label);

        if (null === $label->value()) {
            return $this;
        }

        if ($host->isIpv4()) {
            return $this->withHost($host->value().'.'.ltrim($label->value(), '.'));
        }

        if (!$host->isDomain()) {
            throw new SyntaxError('The URI host '.$host->toString().' cannot be appended.');
        }

        $newHost = Domain::new($host)->append($label);
        $newHost = !$isAsciiDomain ? $newHost->toUnicode() : $newHost->toAscii();

        return $this->withHost($newHost);
    }

    /**
     * Convert the URI host part to its ASCII value.
     */
    public function hostToAscii(): static
    {
        $currentHost = $this->uri instanceof WhatWgUrl ? $this->uri->getAsciiHost() : $this->uri->getHost();
        $host = IdnaConverter::toAsciiOrFail((string) $currentHost);

        return match (true) {
            null === $currentHost,
            '' === $currentHost,
            $host === $currentHost => $this,
            default => $this->withHost($host),
        };
    }

    /**
     * Convert the URI host part to its Unicode value.
     */
    public function hostToUnicode(): static
    {
        $currentHost = $this->uri instanceof WhatWgUrl ? $this->uri->getAsciiHost() : $this->uri->getHost();
        $host = IdnaConverter::toUnicode((string) $currentHost)->domain();

        return match (true) {
            null === $currentHost,
            '' === $currentHost,
            $host === $currentHost => $this,
            default => $this->withHost($host),
        };
    }

    /**
     * Normalizes the URI host content to an IPv4 dot-decimal notation if possible
     * otherwise returns the uri instance unchanged.
     *
     * @see https://url.spec.whatwg.org/#concept-ipv4-parser
     */
    public function hostToDecimal(): static
    {
        $currentHost = $this->uri instanceof WhatWgUrl ? $this->uri->getAsciiHost() : $this->uri->getHost();
        $hostIp = self::ipv4Converter()->toDecimal($currentHost);

        return match (true) {
            null === $currentHost,
            '' === $currentHost,
            null === $hostIp,
            $currentHost === $hostIp => $this,
            default => $this->withHost($hostIp),
        };
    }

    /**
     * Normalizes the URI host content to a IPv4 octal notation if possible
     * otherwise returns the uri instance unchanged.
     *
     * @see https://url.spec.whatwg.org/#concept-ipv4-parser
     */
    public function hostToOctal(): static
    {
        $currentHost = $this->uri instanceof WhatWgUrl ? $this->uri->getAsciiHost() : $this->uri->getHost();
        $hostIp = self::ipv4Converter()->toOctal($currentHost);

        return match (true) {
            null === $currentHost,
            '' === $currentHost,
            null === $hostIp,
            $currentHost === $hostIp  => $this,
            default => $this->withHost($hostIp),
        };
    }

    /**
     * Normalizes the URI host content to a IPv4 octal notation if possible
     * otherwise returns the uri instance unchanged.
     *
     * @see https://url.spec.whatwg.org/#concept-ipv4-parser
     */
    public function hostToHexadecimal(): static
    {
        $currentHost = $this->uri instanceof WhatWgUrl ? $this->uri->getAsciiHost() : $this->uri->getHost();
        $hostIp = self::ipv4Converter()->toHexadecimal($currentHost);

        return match (true) {
            null === $currentHost,
            '' === $currentHost,
            null === $hostIp,
            $currentHost === $hostIp  => $this,
            default => $this->withHost($hostIp),
        };
    }

    public function hostToIpv6Compressed(): static
    {
        return $this->withHost(IPv6Converter::compress($this->uri instanceof WhatWgUrl ? $this->uri->getAsciiHost() : $this->uri->getHost()));
    }

    public function hostToIpv6Expanded(): static
    {
        return $this->withHost(IPv6Converter::expand($this->uri instanceof WhatWgUrl ? $this->uri->getAsciiHost() : $this->uri->getHost()));
    }

    /**
     * Prepend a label or a host to the current URI host.
     *
     * @throws SyntaxError If the host cannot be prepended
     */
    public function prependLabel(BackedEnum|Stringable|string|null $label): static
    {
        $host = $this->uri instanceof WhatWgUrl ? $this->uri->getAsciiHost() : $this->uri->getHost();
        $isAsciiDomain = null === $host || IdnaConverter::toAscii($host)->domain() === $host;

        $host = Host::new($host);
        $label = Host::new($label);

        if (null === $label->value()) {
            return $this;
        }

        if ($host->isIpv4()) {
            return $this->withHost(rtrim($label->value(), '.').'.'.$host->value());
        }

        if (!$host->isDomain()) {
            throw new SyntaxError('The URI host '.$host->toString().' cannot be prepended.');
        }

        $newHost = Domain::new($host)->prepend($label);
        $newHost = !$isAsciiDomain ? $newHost->toUnicode() : $newHost->toAscii();

        return $this->withHost($newHost);
    }

    /**
     * Remove host labels according to their offset.
     */
    public function removeLabels(int ...$keys): static
    {
        $host = $this->uri instanceof WhatWgUrl ? $this->uri->getAsciiHost() : $this->uri->getHost();
        if (null === $host || ('' === $host && $this->uri instanceof Psr7UriInterface)) {
            return $this;
        }

        $isAsciiDomain = IdnaConverter::toAscii($host)->domain() === $host;
        $newHost = Domain::new($host)->withoutLabel(...$keys);
        $newHost = !$isAsciiDomain ? $newHost->toUnicode() : $newHost->toAscii();

        return $this->withHost($newHost);
    }

    /**
     * Remove the root label to the URI.
     */
    public function removeRootLabel(): static
    {
        $host = $this->uri instanceof WhatWgUrl ? $this->uri->getAsciiHost() : $this->uri->getHost();

        return match (true) {
            null === $host,
            '' === $host,
            !str_ends_with($host, '.') => $this,
            default => $this->withHost(substr($host, 0, -1)),
        };
    }

    /**
     * Slice the host from the URI.
     */
    public function sliceLabels(int $offset, ?int $length = null): static
    {
        $currentHost = $this->uri instanceof WhatWgUrl ? $this->uri->getAsciiHost() : $this->uri->getHost();
        if (null === $currentHost || ('' === $currentHost && $this->uri instanceof Psr7UriInterface)) {
            return $this;
        }

        $isAsciiDomain = IdnaConverter::toAscii($currentHost)->domain() === $currentHost;
        $host = Domain::new($currentHost)->slice($offset, $length);
        $host = !$isAsciiDomain ? $host->toUnicode() : $host->toAscii();

        if ($currentHost === $host) {
            return $this;
        }

        return $this->withHost($host);
    }

    /**
     * Remove the host zone identifier.
     */
    public function removeZoneId(): static
    {
        $host = Host::fromUri($this->uri);

        return match (true) {
            $host->hasZoneIdentifier() => $this->withHost($host->withoutZoneIdentifier()->value()),
            default => $this,
        };
    }

    /**
     * Replace a label of the current URI host.
     */
    public function replaceLabel(int $offset, Stringable|string|null $label): static
    {
        $host = $this->uri instanceof WhatWgUrl ? $this->uri->getAsciiHost() : $this->uri->getHost();
        $isAsciiDomain = null === $host || IdnaConverter::toAscii($host)->domain() === $host;
        $newHost = Domain::new($host)->withLabel($offset, $label);
        $newHost = !$isAsciiDomain ? $newHost->toUnicode() : $newHost->toAscii();

        return $this->withHost($newHost);
    }

    public function normalizeIp(): static
    {
        $host = $this->uri instanceof WhatWgUrl ? $this->uri->getAsciiHost() : $this->uri->getHost();
        if (in_array($host, [null, ''], true)) {
            return $this;
        }

        try {
            $converted = IPv4Converter::fromEnvironment()->toDecimal($host);
        } catch (MissingFeature) {
            $converted = null;
        }

        if (false === filter_var($converted, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
            $converted = IPv6Converter::compress($host);
        }

        if ($converted !== $host) {
            return $this->withHost($converted);
        }

        return $this;
    }

    public function normalizeHost(): static
    {
        $host = $this->uri instanceof WhatWgUrl ? $this->uri->getAsciiHost() : $this->uri->getHost();
        if (in_array($host, [null, ''], true)) {
            return $this;
        }

        $new = $this->normalizeIp();
        $newHost = $new->uri instanceof WhatWgUrl ? $new->uri->getAsciiHost() : $new->uri->getHost();
        if ($newHost !== $host) {
            return $new;
        }

        return $this->withHost(Host::new($host)->toAscii());
    }

    /*********************************
     * Path modifier methods
     *********************************/

    /**
     * Add a new base path to the URI path.
     */
    public function addBasePath(BackedEnum|Stringable|string $path): static
    {
        /** @var HierarchicalPath $path */
        $path = HierarchicalPath::new($path)->withLeadingSlash();
        /** @var HierarchicalPath $currentPath */
        $currentPath = HierarchicalPath::fromUri($this->uri)->withLeadingSlash();

        return match (true) {
            !str_starts_with($currentPath->toString(), $path->toString()) => $this->withPath($path->append($currentPath)->toString()),
            default => $this->withPath($currentPath),
        };
    }

    /**
     * Add a leading slash to the URI path.
     */
    public function addLeadingSlash(): static
    {
        $path = $this->uri->getPath();

        return match (true) {
            str_starts_with($path, '/') => $this,
            default => $this->withPath('/'.$path),
        };
    }

    /**
     * Add a trailing slash to the URI path.
     */
    public function addTrailingSlash(): static
    {
        $path = $this->uri->getPath();

        return match (true) {
            str_ends_with($path, '/') => $this,
            default => $this->withPath($path.'/'),
        };
    }

    /**
     * Append a new path or add a path to the URI path.
     */
    public function appendPath(BackedEnum|Stringable|string $path): static
    {
        return $this->withPath(HierarchicalPath::fromUri($this->uri)->append($path));
    }

    /**
     * Prepend a path or add a new path to the URI path.
     */
    public function prependPath(BackedEnum|Stringable|string $path): static
    {
        return $this->withPath(HierarchicalPath::fromUri($this->uri)->prepend($path));
    }

    /**
     * Append a list of segments or a new path to the URI path.
     *
     * @param iterable<BackedEnum|Stringable|string> $segments
     */
    public function appendSegments(iterable $segments): static
    {
        return $this->withPath(HierarchicalPath::fromUri($this->uri)->appendSegments($segments));
    }

    /**
     * Prepend a list of segments or a new path to the URI path.
     *
     * @param iterable<BackedEnum|Stringable|string> $segments
     */
    public function prependSegments(iterable $segments): static
    {
        return $this->withPath(HierarchicalPath::fromUri($this->uri)->prependSegments($segments));
    }

    /**
     * Convert the Data URI path to its ascii form.
     */
    public function dataPathToAscii(): static
    {
        return $this->withPath(DataPath::fromUri($this->uri)->toAscii()->toString());
    }

    /**
     * Convert the Data URI path to its binary (base64encoded) form.
     */
    public function dataPathToBinary(): static
    {
        return $this->withPath(DataPath::fromUri($this->uri)->toBinary()->toString());
    }

    /**
     * Remove a base path from the URI path.
     */
    public function removeBasePath(BackedEnum|Stringable|string $path): static
    {
        $basePath = HierarchicalPath::new($path)->withLeadingSlash()->toString();
        $currentPath = HierarchicalPath::fromUri($this->uri)->withLeadingSlash()->toString();
        $newPath = substr($currentPath, strlen($basePath));

        return match (true) {
            '/' === $basePath,
            !str_starts_with($currentPath, $basePath),
            !str_starts_with($newPath, '/') => $this,
            default => $this->withPath($newPath),
        };
    }

    /**
     * Remove dot segments from the URI path.
     */
    public function removeDotSegments(): static
    {
        return $this->withPath(Path::fromUri($this->uri)->withoutDotSegments()->toString());
    }

    /**
     * Remove empty segments from the URI path.
     */
    public function removeEmptySegments(): static
    {
        return $this->withPath(HierarchicalPath::fromUri($this->uri)->withoutEmptySegments()->toString());
    }

    /**
     * Remove the leading slash from the URI path.
     */
    public function removeLeadingSlash(): static
    {
        return $this->withPath(Path::fromUri($this->uri)->withoutLeadingSlash());
    }

    /**
     * Remove the trailing slash from the URI path.
     */
    public function removeTrailingSlash(): static
    {
        $path = $this->uri->getPath();

        return match (true) {
            !str_ends_with($path, '/') => $this,
            default => $this->withPath(substr($path, 0, -1)),
        };
    }

    /**
     * Remove path segments from the URI path according to their offsets.
     */
    public function removeSegments(int ...$keys): static
    {
        return $this->withPath(HierarchicalPath::fromUri($this->uri)->withoutSegment(...$keys)->toString());
    }

    /**
     * Replace the URI path basename.
     */
    public function replaceBasename(BackedEnum|Stringable|string $basename): static
    {
        return $this->withPath(HierarchicalPath::fromUri($this->uri)->withBasename($basename));
    }

    /**
     * Replace the data URI path parameters.
     */
    public function replaceDataUriParameters(BackedEnum|Stringable|string $parameters): static
    {
        return $this->withPath(DataPath::fromUri($this->uri)->withParameters($parameters)->toString());
    }

    /**
     * Replace the URI path dirname.
     */
    public function replaceDirname(BackedEnum|Stringable|string $dirname): static
    {
        return $this->withPath(HierarchicalPath::fromUri($this->uri)->withDirname($dirname));
    }

    /**
     * Replace the URI path basename extension.
     */
    public function replaceExtension(BackedEnum|Stringable|string $extension): static
    {
        return $this->withPath(HierarchicalPath::fromUri($this->uri)->withExtension($extension)->toString());
    }

    /**
     * Replace a segment from the URI path according its offset.
     */
    public function replaceSegment(int $offset, BackedEnum|Stringable|string $segment): static
    {
        return $this->withPath(HierarchicalPath::fromUri($this->uri)->withSegment($offset, $segment)->toString());
    }

    /**
     * Slice the host from the URI.
     */
    public function sliceSegments(int $offset, ?int $length = null): static
    {
        return $this->withPath(HierarchicalPath::fromUri($this->uri)->slice($offset, $length));
    }

    /**
     * Returns a new instance with specific path segments redacted by index.
     *
     * Indexing starts at 0 for the first segment after the leading slash.
     * Negative indexing is supported>
     * Out-of-range offsets are ignored.
     *
     * Example: redactPathSegmentsByOffset(2, -2)
     *   /api/users/john/orders/55/details â†’ /api/users/[REDACTED]/orders/[REDACTED]/details
     */
    public function redactPathSegmentsByOffset(int ...$offsets): static
    {
        if ([] === $offsets || [] === ($path = [...HierarchicalPath::fromUri($this->uri)])) {
            return $this;
        }

        $nbSegments = count($path);
        $hasChanged = false;
        foreach ($offsets as $offset) {
            if ($offset < - $nbSegments - 1 || $offset > $nbSegments) {
                continue;
            }

            if (0 > $offset) {
                $offset += $nbSegments;
            }

            if (!in_array($path[$offset] ?? null, [null, self::MASK], true)) {
                $hasChanged = true;
                $path[$offset] = self::MASK;
            }
        }

        return !$hasChanged ? $this : $this->withPath(implode('/', $path));
    }

    /**
     * Returns a new instance with all path segments matching the given names redacted.
     *
     * Matching is strict string comparison on raw (decoded) segments.
     *
     * Example: redactPathSegments('john')
     *  /api/user/john/orders -> /api/user/[REDACTED]/orders
     */
    public function redactPathSegments(BackedEnum|Stringable|string ...$segments): static
    {
        if ([] === $segments || [] === ($path = [...HierarchicalPath::fromUri($this->uri)])) {
            return $this;
        }

        $hasChanged = false;
        foreach ($segments as $segment) {
            if ($segment instanceof BackedEnum) {
                $segment = $segment->value;
            }

            foreach (array_keys($path, (string) $segment, true) as $key) {
                $hasChanged = true;
                $path[$key] = self::MASK;
            }
        }

        return !$hasChanged ? $this : $this->withPath(implode('/', $path));
    }

    /**
     * Returns a new instance where, for each matched segment,
     * the **immediately following** segment is redacted.
     *
     * Only the next segment is masked â€” not all subsequent ones.
     * If no following segment exists, it is ignored.
     *
     * Example: redactPathNextSegments('john')
     *   /api/users/john/orders/55/details â†’ /api/users/john/[REDACTED]/55/details
     */
    public function redactPathNextSegments(BackedEnum|Stringable|string ...$segments): static
    {
        if ([] === $segments || [] === ($path = [...HierarchicalPath::fromUri($this->uri)])) {
            return $this;
        }

        $hasChanged = false;
        foreach ($segments as $segment) {
            if ($segment instanceof BackedEnum) {
                $segment = $segment->value;
            }
            foreach (array_keys($path, (string) $segment, true) as $key) {
                $nextKey = $key + 1;
                if (!in_array($path[$nextKey] ?? null, [null, self::MASK], true)) {
                    $hasChanged = true;
                    $path[$nextKey] = self::MASK;
                }
            }
        }

        return !$hasChanged ? $this : $this->withPath(implode('/', $path));
    }

    /**
     * Normalize a URI path.
     *
     * Make sure the path always has a leading slash if an authority is present
     * and the path is not the empty string.
     */
    final protected static function normalizePath(WhatWgUrl|Rfc3986Uri|Psr7UriInterface|UriInterface $uri, PathInterface $path): WhatWgUrl|Rfc3986Uri|Psr7UriInterface|UriInterface
    {
        if (!$uri instanceof Psr7UriInterface) {
            return $uri->withPath($path->toString());
        }

        $pathString = $path->toString();
        if ('' === $pathString) {
            return $uri->withPath($pathString);
        }

        $authority = $uri->getAuthority();
        if ('' !== $authority) {
            return $uri->withPath(str_starts_with($pathString, '/') ? $pathString : '/'.$pathString);
        }

        // If there is no authority, the path cannot start with `//`
        if (str_starts_with($pathString, '//')) {
            return $uri->withPath('/.'.$pathString);
        }

        $colonPos = strpos($pathString, ':');
        if (false !== $colonPos && '' === $uri->getScheme()) {
            // In the absence of a scheme and of an authority,
            // the first path segment cannot contain a colon (":") character.'
            $slashPos = strpos($pathString, '/');
            (false !== $slashPos && $colonPos > $slashPos) || throw new SyntaxError(
                'In absence of the scheme and authority components, the first path segment cannot contain a colon (":") character.'
            );
        }

        return $uri->withPath($pathString);
    }

    /**
     * Normalize the URI component value depending on the subject interface.
     *
     * null value MUST be converted to the empty string if a Psr7 UriInterface is being manipulated.
     */
    final protected static function normalizeComponent(BackedEnum|Stringable|string|null $component, Rfc3986Uri|WhatWgUrl|Psr7UriInterface|UriInterface $uri): ?string
    {
        return match (true) {
            $component instanceof BackedEnum => (string) $component->value,
            $uri instanceof Psr7UriInterface,
            $component instanceof Stringable => (string) $component,
            default => $component,
        };
    }

    final protected static function ipv4Converter(): IPv4Converter
    {
        static $converter;

        $converter = $converter ?? IPv4Converter::fromEnvironment();

        return $converter;
    }

    public function displayUriString(): string
    {
        if ($this->uri instanceof Uri) {
            return $this->uri->toDisplayString();
        }

        return Uri::new($this->uri)->toDisplayString();
    }

    /*********************************
     * Fragment modifier methods
     *********************************/

    public function appendFragmentDirectives(FragmentDirectives|FragmentDirective|BackedEnum|Stringable|string ...$directives): static
    {
        return $this->applyFragmentChanges(FragmentDirectives::fromUri($this->unwrap())->append(...$directives));
    }

    final protected function applyFragmentChanges(FragmentDirectives $fragmentDirectives): static
    {
        $fValue = Fragment::fromUri($this->unwrap())->value();
        if (null === $fValue) {
            return $this->withFragment($fragmentDirectives);
        }

        $pos = strpos($fValue, FragmentDirectives::DELIMITER);
        if (false === $pos) {
            return $this->withFragment($fValue.$fragmentDirectives->value());
        }

        return $this->withFragment(substr($fValue, 0, $pos).$fragmentDirectives->value());
    }

    public function prependFragmentDirectives(FragmentDirectives|FragmentDirective|Stringable|string ...$directives): static
    {
        return $this->applyFragmentChanges(FragmentDirectives::fromUri($this->unwrap())->prepend(...$directives));
    }

    public function removeFragmentDirectives(int ...$offset): static
    {
        return $this->applyFragmentChanges(FragmentDirectives::fromUri($this->unwrap())->remove(...$offset));
    }

    public function replaceFragmentDirective(int $offset, FragmentDirective|Stringable|string $directive): static
    {
        return $this->applyFragmentChanges(FragmentDirectives::fromUri($this->unwrap())->replace($offset, $directive));
    }

    public function sliceFragmentDirectives(int $offset, ?int $length): static
    {
        return $this->applyFragmentChanges(FragmentDirectives::fromUri($this->unwrap())->slice($offset, $length));
    }

    public function filterFragmentDirectives(callable $callback): static
    {
        return $this->applyFragmentChanges(FragmentDirectives::fromUri($this->unwrap())->filter($callback));
    }

    public function stripFragmentDirectives(): static
    {
        $fragment = Fragment::fromUri($this->unwrap())->value();
        if (null === $fragment || (false === ($pos = strpos($fragment, FragmentDirectives::DELIMITER)))) {
            return $this;
        }

        return $this->withFragment(substr($fragment, 0, $pos));
    }

    public function retainFragmentDirectives(): static
    {
        return $this->withFragment(FragmentDirectives::fromUri($this->unwrap()));
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.6.0
     * @codeCoverageIgnore
     * @see Modifier::displayUriString()
     *
     * Remove query data according to their key name.
     */
    #[Deprecated(message:'use League\Uri\Modifier::displayUriString() instead', since:'league/uri-components:7.6.0')]
    public function getIdnUriString(): string
    {
        if ($this->uri instanceof WhatWgUrl) {
            return $this->uri->toUnicodeString();
        }

        $currentHost = $this->uri->getHost();
        if (null === $currentHost || '' === $currentHost) {
            return $this->toString();
        }

        $host = IdnaConverter::toUnicode($currentHost)->domain();
        if ($host === $currentHost) {
            return $this->toString();
        }

        $components = match (true) {
            $this->uri instanceof Rfc3986Uri => UriString::parse($this->uri->toRawString()),
            $this->uri instanceof UriInterface => $this->uri->toComponents(),
            default => UriString::parse($this->uri),
        };
        $components['host'] = $host;

        return UriString::build($components);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.7.0
     * @codeCoverageIgnore
     * @see Modifier::appendPath
     */
    #[Deprecated(message:'use League\Uri\Modifier::appendPath() instead', since:'league/uri-components:7.7.0')]
    public function appendSegment(Stringable|string $segment): static
    {
        return $this->appendPath($segment);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.7.0
     * @codeCoverageIgnore
     * @see Modifier::prependPath
     */
    #[Deprecated(message:'use League\Uri\Modifier::prependPath() instead', since:'league/uri-components:7.7.0')]
    public function prependSegment(Stringable|string $segment): static
    {
        return $this->prependPath($segment);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.6.0
     * @codeCoverageIgnore
     * @see Modifier::wrap()
     *
     * @param UriFactoryInterface|null $uriFactory deprecated, will be removed in the next major release
     */
    #[Deprecated(message:'use League\Uri\Modifier::wrap() instead', since:'league/uri-components:7.6.0')]
    public static function from(Rfc3986Uri|WhatWgUrl|Stringable|string $uri, ?UriFactoryInterface $uriFactory = null): static
    {
        return new static(match (true) {
            $uri instanceof self => $uri->uri,
            $uri instanceof Psr7UriInterface,
            $uri instanceof UriInterface,
            $uri instanceof Rfc3986Uri,
            $uri instanceof WhatWgUrl => $uri,
            $uriFactory instanceof UriFactoryInterface => $uriFactory->createUri((string) $uri),  // using UriFactoryInterface is deprecated
            default => Uri::new($uri),
        });
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.2.0
     * @codeCoverageIgnore
     * @see Modifier::removeQueryParameters()
     *
     * Remove query data according to their key name.
     */
    #[Deprecated(message:'use League\Uri\Modifier::removeQueryParameters() instead', since:'league/uri-components:7.2.0')]
    public function removeParams(string ...$keys): static
    {
        return $this->removeQueryParameters(...$keys);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.2.0
     * @codeCoverageIgnore
     * @see Modifier::removeEmptyQueryPairs()
     *
     * Remove empty pairs from the URL query component.
     *
     * A pair is considered empty if it's name is the empty string
     * and its value is either the empty string or the null value
     */
    #[Deprecated(message:'use League\Uri\Modifier::removeEmptyQueryPairs() instead', since:'league/uri-components:7.2.0')]
    public function removeEmptyPairs(): static
    {
        return $this->removeEmptyQueryPairs();
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.2.0
     * @codeCoverageIgnore
     * @see Modifier::removeQueryPairsByKey()
     *
     * Remove query data according to their key name.
     */
    #[Deprecated(message:'use League\Uri\Modifier::removeQueryPairsByKey() instead', since:'league/uri-components:7.2.0')]
    public function removePairs(string ...$keys): static
    {
        return $this->removeQueryPairsByKey(...$keys);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.2.0
     * @codeCoverageIgnore
     * @see Modifier::removeQueryPairsByKey()
     *
     * Remove query data according to their key name.
     */
    #[Deprecated(message:'use League\Uri\Modifier::removeQueryPairsByKey() instead', since:'league/uri-components:7.2.0')]
    public function removeQueryPairs(string ...$keys): static
    {
        return $this->removeQueryPairsByKey(...$keys);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.6.0
     * @codeCoverageIgnore
     * @see Modifier::unwrap()
     *
     * Remove query data according to their key name.
     */
    #[Deprecated(message:'use League\Uri\Modifier::unwrap() instead', since:'league/uri-components:7.6.0')]
    public function getUri(): Psr7UriInterface|UriInterface
    {
        if ($this->uri instanceof Rfc3986Uri || $this->uri instanceof WhatWgUrl) {
            return Uri::new($this->uri);
        }

        return $this->uri;
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.6.0
     * @codeCoverageIgnore
     * @see Modifier::toString()
     *
     * Remove query data according to their key name.
     */
    #[Deprecated(message:'use League\Uri\Modifier::toString() instead', since:'league/uri-components:7.6.0')]
    public function getUriString(): string
    {
        return $this->toString();
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\Components;

use BackedEnum;
use Deprecated;
use League\Uri\Contracts\PathInterface;
use League\Uri\Contracts\UriComponentInterface;
use League\Uri\Contracts\UriInterface;
use League\Uri\Encoder;
use League\Uri\StringCoercionMode;
use League\Uri\UriString;
use Psr\Http\Message\UriInterface as Psr7UriInterface;
use Stringable;
use Throwable;
use Uri\Rfc3986\Uri as Rfc3986Uri;
use Uri\WhatWg\Url as WhatWgUrl;

use function substr;

final class Path extends Component implements PathInterface
{
    private const SEPARATOR = '/';

    private readonly string $path;

    /**
     * New instance.
     */
    private function __construct(BackedEnum|Stringable|string $path)
    {
        $this->path = $this->validate($path);
    }

    /**
     * Validate the component content.
     */
    private function validate(BackedEnum|Stringable|string $path): string
    {
        return (string) $this->validateComponent($path);
    }

    /**
     * Returns a new instance from a string or a stringable object.
     */
    public static function new(BackedEnum|Stringable|string $value = ''): self
    {
        return new self($value);
    }

    /**
     * Create a new instance from a string or a stringable structure or returns null on failure.
     */
    public static function tryNew(BackedEnum|Stringable|string $uri = ''): ?self
    {
        try {
            return self::new($uri);
        } catch (Throwable) {
            return null;
        }
    }

    /**
     * Create a new instance from a URI object.
     */
    public static function fromUri(WhatwgUrl|Rfc3986Uri|BackedEnum|Stringable|string $uri): self
    {
        $uri = self::filterUri($uri);
        if ($uri instanceof Rfc3986Uri) {
            return self::new($uri->getRawPath());
        }

        if ($uri instanceof WhatwgUrl) {
            return self::new($uri->getPath());
        }

        $path = $uri->getPath();
        $authority = $uri->getAuthority();

        return match (true) {
            null === $authority, '' === $authority, '' === $path, '/' === $path[0] => new self($path),
            default => new self('/'.$path),
        };
    }

    public function value(): string
    {
        return Encoder::encodePath($this->path);
    }

    public function equals(mixed $value): bool
    {
        if (null === $value || !StringCoercionMode::Native->isCoercible($value)) {
            return false;
        }

        if (!$value instanceof UriComponentInterface) {
            $value = self::tryNew((string) StringCoercionMode::Native->coerce($value));
            if (null === $value) {
                return false;
            }
        }

        return $value->getUriComponent() === $this->getUriComponent();
    }

    public function decoded(): string
    {
        return $this->path;
    }

    public function normalize(): self
    {
        return new self((string) Encoder::normalizePath($this->withoutDotSegments()));
    }

    public function isAbsolute(): bool
    {
        return self::SEPARATOR === ($this->path[0] ?? '');
    }

    public function hasTrailingSlash(): bool
    {
        return '' !== $this->path && self::SEPARATOR === substr($this->path, -1);
    }

    public function withoutDotSegments(): PathInterface
    {
        $current = $this->toString();
        $new = UriString::removeDotSegments($current);
        if ($current === $new) {
            return $this;
        }

        return new self($new);
    }

    /**
     * Returns an instance with a trailing slash.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the path component with a trailing slash
     */
    public function withTrailingSlash(): PathInterface
    {
        return $this->hasTrailingSlash() ? $this : new self($this->toString().self::SEPARATOR);
    }

    public function withoutTrailingSlash(): PathInterface
    {
        return !$this->hasTrailingSlash() ? $this : new self(substr($this->toString(), 0, -1));
    }

    public function withLeadingSlash(): PathInterface
    {
        return $this->isAbsolute() ? $this : new self(self::SEPARATOR.$this->toString());
    }

    public function withoutLeadingSlash(): PathInterface
    {
        return !$this->isAbsolute() ? $this : new self(substr($this->toString(), 1));
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @see HierarchicalPath::new()
     *
     * @codeCoverageIgnore
     *
     * Returns a new instance from a string or a stringable object.
     */
    #[Deprecated(message:'use League\Uri\Components\HierarchicalPath::new() instead', since:'league/uri-components:7.0.0')]
    public static function createFromString(Stringable|string|int $path): self
    {
        return self::new((string) $path);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @see HierarchicalPath::fromUri()
     *
     * @codeCoverageIgnore
     *
     * Create a new instance from a URI object.
     */
    #[Deprecated(message:'use League\Uri\Components\HierarchicalPath::fromUri() instead', since:'league/uri-components:7.0.0')]
    public static function createFromUri(Psr7UriInterface|UriInterface $uri): self
    {
        return self::fromUri($uri);
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\Components;

use BackedEnum;
use Deprecated;
use League\Uri\Contracts\AuthorityInterface;
use League\Uri\Contracts\PortInterface;
use League\Uri\Contracts\UriComponentInterface;
use League\Uri\Contracts\UriException;
use League\Uri\Contracts\UriInterface;
use League\Uri\Exceptions\SyntaxError;
use League\Uri\StringCoercionMode;
use League\Uri\UriScheme;
use Psr\Http\Message\UriInterface as Psr7UriInterface;
use Stringable;
use Uri\Rfc3986\Uri as Rfc3986Uri;
use Uri\WhatWg\Url as WhatWgUrl;

use function array_map;
use function array_values;
use function filter_var;

use const FILTER_VALIDATE_INT;

final class Port extends Component implements PortInterface
{
    private readonly ?int $port;

    private function __construct(BackedEnum|Stringable|string|int|null $port = null)
    {
        $this->port = $this->validate($port);
    }

    public static function new(BackedEnum|Stringable|string|int|null $value = null): self
    {
        return new self($value);
    }

    /**
     * Create a new instance from a string.or a stringable structure or returns null on failure.
     */
    public static function tryNew(BackedEnum|Stringable|string|int|null $uri = null): ?self
    {
        try {
            return self::new($uri);
        } catch (UriException) {
            return null;
        }
    }

    /**
     * Create a new instance from a URI object.
     */
    public static function fromUri(WhatWgUrl|Rfc3986Uri|BackedEnum|Stringable|string $uri): self
    {
        return new self(self::filterUri($uri)->getPort());
    }

    /**
     * Create a new instance from an Authority object.
     */
    public static function fromAuthority(BackedEnum|Stringable|string|null $authority): self
    {
        return match (true) {
            $authority instanceof AuthorityInterface => new self($authority->getPort()),
            default => new self(Authority::new($authority)->getPort()),
        };
    }

    /**
     * Validate a port.
     *
     * @throws SyntaxError if the port is invalid
     */
    private function validate(BackedEnum|Stringable|int|string|null $port): ?int
    {
        $port = self::filterComponent($port);
        if (null === $port) {
            return null;
        }

        $fport = filter_var($port, FILTER_VALIDATE_INT, ['options' => ['min_range' => 0]]);
        if (false !== $fport) {
            return $fport;
        }

        throw new SyntaxError('Expected port to be a positive integer or 0; received '.$port.'.');
    }

    public function value(): ?string
    {
        return match (null) {
            $this->port => $this->port,
            default => (string) $this->port,
        };
    }

    public function equals(mixed $value): bool
    {
        if (!StringCoercionMode::Native->isCoercible($value)) {
            return false;
        }

        if (!$value instanceof UriComponentInterface) {
            $value = self::tryNew(StringCoercionMode::Native->coerce($value));
            if (null === $value) {
                return false;
            }
        }

        return $value->getUriComponent() === $this->getUriComponent();
    }

    public function getUriComponent(): string
    {
        return match (null) {
            $this->port => '',
            default => ':'.$this->value(),
        };
    }

    public function toInt(): ?int
    {
        return $this->port;
    }

    public function defaultScheme(): ?Scheme
    {
        return $this->defaultSchemes()[0] ?? null;
    }

    /**
     * @return list<Scheme>
     */
    public function defaultSchemes(): array
    {
        static $defaultSchemes;

        $defaultSchemes ??= array_values(array_map(
            static fn (UriScheme $schemePort): Scheme => Scheme::new($schemePort->value),
            UriScheme::fromPort($this->port)
        ));

        return $defaultSchemes;
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @see Port::fromUri()
     *
     * @codeCoverageIgnore
     *
     * Create a new instance from a URI object.
     */
    #[Deprecated(message:'use League\Uri\Components\Port::fromUri() instead', since:'league/uri-components:7.0.0')]
    public static function createFromUri(Psr7UriInterface|UriInterface $uri): self
    {
        return self::fromUri($uri);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @see Port::fromAuthority()
     *
     * @codeCoverageIgnore
     *
     * Create a new instance from an Authority object.
     */
    #[Deprecated(message:'use League\Uri\Components\Port::fromAuthority() instead', since:'league/uri-components:7.0.0')]
    public static function createFromAuthority(AuthorityInterface|Stringable|string $authority): self
    {
        return self::fromAuthority($authority);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @see Port::new()
     *
     * @codeCoverageIgnore
     */
    #[Deprecated(message:'use League\Uri\Components\Port::new() instead', since:'league/uri-components:7.0.0')]
    public static function fromInt(int $port): self
    {
        return self::new($port);
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\Components;

use BackedEnum;
use Countable;
use IteratorAggregate;
use League\Uri\Components\FragmentDirectives\DirectiveString;
use League\Uri\Contracts\Conditionable;
use League\Uri\Contracts\FragmentDirective;
use League\Uri\Contracts\FragmentInterface;
use League\Uri\Contracts\Transformable;
use League\Uri\Contracts\UriComponentInterface;
use League\Uri\Contracts\UriInterface;
use League\Uri\Encoder;
use League\Uri\Exceptions\OffsetOutOfBounds;
use League\Uri\Modifier;
use League\Uri\StringCoercionMode;
use League\Uri\Uri;
use League\Uri\UriString;
use Psr\Http\Message\UriInterface as Psr7UriInterface;
use Stringable;
use Throwable;
use Traversable;
use Uri\Rfc3986\Uri as Rfc3986Uri;
use Uri\WhatWg\Url as WhatWgUrl;

use function array_count_values;
use function array_filter;
use function array_keys;
use function array_map;
use function array_slice;
use function array_values;
use function count;
use function explode;
use function implode;
use function in_array;
use function is_bool;
use function sprintf;
use function str_replace;
use function strpos;
use function substr;

use const ARRAY_FILTER_USE_BOTH;

/**
 * @see https://wicg.github.io/scroll-to-text-fragment/
 *
 * @implements IteratorAggregate<int, FragmentDirective>
 */
final class FragmentDirectives implements FragmentInterface, IteratorAggregate, Countable, Conditionable, Transformable
{
    public const DELIMITER = ':~:';
    public const SEPARATOR = '&';

    /** @var list<FragmentDirective> */
    private readonly array $directives;

    public function __construct(FragmentDirective|BackedEnum|Stringable|string ...$directives)
    {
        $this->directives = array_values(array_map(self::filterDirective(...), $directives));
    }

    /**
     * Create a new instance from a Fragment.
     *
     * If no delimiter is found, an empty collection is returned
     */
    public static function fromFragment(BackedEnum|Stringable|string|null $fragment): self
    {
        $fragment = StringCoercionMode::Native->coerce($fragment);
        if (null === $fragment) {
            return new self();
        }

        $pos = strpos($fragment, self::DELIMITER);
        if (false === $pos) {
            return new self();
        }

        return self::new(substr($fragment, $pos + 3));
    }

    /**
     * Create a new instance from a string which only contains directives.
     */
    public static function new(BackedEnum|Stringable|string|null $value): self
    {
        if ($value instanceof BackedEnum) {
            $value = $value->value;
        }

        return null === $value
             ? new self()
             : new self(...explode(self::SEPARATOR, (string) $value));
    }

    private static function filterDirective(FragmentDirective|BackedEnum|Stringable|string $directive): FragmentDirective
    {
        return $directive instanceof FragmentDirective ? $directive : DirectiveString::resolve($directive);
    }

    public static function tryNew(BackedEnum|Stringable|string|null $value): ?self
    {
        try {
            return self::new($value);
        } catch (Throwable) {
            return null;
        }
    }

    /**
     *  Create a new instance from a URI string or object.
     */
    public static function fromUri(WhatWgUrl|Rfc3986Uri|BackedEnum|Stringable|string $uri): self
    {
        if ($uri instanceof Modifier) {
            $uri = $uri->unwrap();
        }

        return self::fromFragment(match (true) {
            $uri instanceof Psr7UriInterface => UriString::parse($uri)['fragment'],
            $uri instanceof Rfc3986Uri => $uri->getRawFragment(),
            $uri instanceof UriInterface, $uri instanceof WhatWgUrl => $uri->getFragment(),
            default => Uri::new($uri)->getFragment(),
        });
    }

    public function count(): int
    {
        return count($this->directives);
    }

    public function getIterator(): Traversable
    {
        yield from $this->directives;
    }

    public function __toString(): string
    {
        return $this->toString();
    }

    public function jsonSerialize(): string
    {
        return $this->toString();
    }

    public function value(): ?string
    {
        return [] === $this->directives
            ? null
            : self::DELIMITER.implode(
                self::SEPARATOR,
                array_map(fn (FragmentDirective $directive): string => $directive->toString(), $this->directives)
            );
    }

    public function toString(): string
    {
        return (string) $this->value();
    }

    public function getUriComponent(): string
    {
        $fragment = $this->value();

        return (null === $fragment ? '' : '#').$fragment;
    }

    public function decoded(): ?string
    {
        return [] === $this->directives
            ? null
            : str_replace('%20', ' ', (string) Encoder::decodeFragment($this->toString()));
    }

    /**
     * Returns the Directive at a specified offset or null if none is defined.
     *
     * Negative offsets are supported.
     */
    public function nth(int $offset): ?FragmentDirective
    {
        if ($offset < 0) {
            $offset += count($this->directives);
        }

        return $this->directives[$offset] ?? null;
    }

    /**
     * The first Directive defined on the fragment or null if none are defined.
     */
    public function first(): ?FragmentDirective
    {
        return $this->nth(0);
    }

    /**
     * The last Directive defined on the fragment or null if none are defined.
     */
    public function last(): ?FragmentDirective
    {
        return $this->nth(-1);
    }

    /**
     * Tells whether all the submitted keys are present in the collection.
     *
     * Negative offsets are supported.
     */
    public function has(int ...$offsets): bool
    {
        $nbDirectives = count($this->directives);
        foreach ($offsets as $offset) {
            if ($offset < 0) {
                $offset += $nbDirectives;
            }

            if (! isset($this->directives[$offset])) {
                return false;
            }
        }

        return [] !== $offsets;
    }

    public function isEmpty(): bool
    {
        return [] === $this->directives;
    }

    public function equals(mixed $value): bool
    {
        if (!StringCoercionMode::Native->isCoercible($value)) {
            return false;
        }

        if (!$value instanceof UriComponentInterface) {
            $value = self::tryNew(StringCoercionMode::Native->coerce($value));
            if (null === $value) {
                return false;
            }
        }

        return $value->getUriComponent() === $this->getUriComponent();
    }

    public function indexOf(FragmentDirective|BackedEnum|Stringable|string $directive): ?int
    {
        $directive = self::filterDirective($directive);
        foreach ($this->directives as $offset => $innerDirective) {
            if ($innerDirective->equals($directive)) {
                return $offset;
            }
        }

        return null;
    }

    public function contains(FragmentDirective|BackedEnum|Stringable|string $directive): bool
    {
        return null !== $this->indexOf($directive);
    }

    /**
     * Append one or more Directives to the fragment.
     */
    public function append(FragmentDirectives|FragmentDirective|BackedEnum|Stringable|string ...$directives): self
    {
        $items = self::implodeDirectives(...$directives);

        return [] === $items ? $this : new self(...$this->directives, ...$items);
    }

    /**
     * Prepend one or more Directives to the fragment.
     */
    public function prepend(FragmentDirectives|FragmentDirective|Stringable|string ...$directives): self
    {
        $items = self::implodeDirectives(...$directives);

        return [] === $items ? $this : new self(...$items, ...$this->directives);
    }

    /**
     * @return list<FragmentDirective|BackedEnum|Stringable|string>
     */
    private static function implodeDirectives(FragmentDirectives|FragmentDirective|BackedEnum|Stringable|string ...$directives): array
    {
        return array_merge(...array_map(fn ($d) => $d instanceof FragmentDirectives ? [...$d] : [$d], $directives));
    }

    /**
     * Removes one or more Directives by offset from the fragment.
     */
    public function remove(int ...$keys): self
    {
        if ([] === $keys) {
            return $this;
        }

        $nbDirectives = count($this->directives);
        $deletedKeys = [];
        foreach ($keys as $key) {
            $value = $key;
            if ($value < 0) {
                $value += $nbDirectives;
            }

            isset($this->directives[$value]) || throw new OffsetOutOfBounds(sprintf('The key `%s` is invalid.', $key));
            $deletedKeys[] = $value;
        }

        $deletedKeys = array_keys(array_count_values($deletedKeys));

        return $this->filter(fn (FragmentDirective $directive, int $offset): bool => !in_array($offset, $deletedKeys, true)); /* @phpstan-ignore-line */
    }

    /**
     * Slices the fragment to remove Directives portions.
     */
    public function slice(int $offset, ?int $length = null): self
    {
        $nbDirectives = count($this->directives);
        ($offset >= -$nbDirectives && $offset <= $nbDirectives) || throw new OffsetOutOfBounds(sprintf('No directive can be found at : `%s`.', $offset));
        $directives = array_slice($this->directives, $offset, $length);

        return $directives === $this->directives ? $this : new self(...$directives);
    }

    /**
     * Filter the Directives to return a new instance based on the callback.
     *
     * @param callable(FragmentDirective, int=): bool $callback
     */
    public function filter(callable $callback): self
    {
        $directives = array_filter($this->directives, $callback, ARRAY_FILTER_USE_BOTH);

        return $directives === $this->directives ? $this : new self(...$directives);
    }

    /**
     * Replace the Directive define at a specific offset.
     * Negative offsets are supported.
     *
     * If no Directive is found to the specified offset, an exception is thrown
     */
    public function replace(int $offset, FragmentDirective|BackedEnum|Stringable|string $directive): self
    {
        $currentDirective = $this->nth($offset);
        null !== $currentDirective || throw new OffsetOutOfBounds(sprintf('The key `%s` is invalid.', $offset));

        $directive = self::filterDirective($directive);
        if ($directive::class === $currentDirective::class && $currentDirective->equals($directive)) {
            return $this;
        }

        if ($offset < 0) {
            $offset += count($this->directives);
        }

        $directives = $this->directives;
        $directives[$offset] = $directive;

        return new self(...$directives);
    }

    public function when(callable|bool $condition, callable $onSuccess, ?callable $onFail = null): static
    {
        if (!is_bool($condition)) {
            $condition = $condition($this);
        }

        return match (true) {
            $condition => $onSuccess($this),
            null !== $onFail => $onFail($this),
            default => $this,
        } ?? $this;
    }

    public function transform(callable $callback): static
    {
        return $callback($this);
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\Components;

use ArgumentCountError;
use BackedEnum;
use Closure;
use Countable;
use DateTimeImmutable;
use DateTimeInterface;
use DateTimeZone;
use Deprecated;
use Exception;
use Iterator;
use IteratorAggregate;
use League\Uri\Contracts\QueryInterface;
use League\Uri\Contracts\Transformable;
use League\Uri\Contracts\UriComponentInterface;
use League\Uri\Contracts\UriException;
use League\Uri\Contracts\UriInterface;
use League\Uri\Exceptions\SyntaxError;
use League\Uri\KeyValuePair\Converter;
use League\Uri\Occurrence;
use League\Uri\QueryString;
use League\Uri\StringCoercionMode;
use League\Uri\TypeConverter;
use League\Uri\Uri;
use League\Uri\UriString;
use Psr\Http\Message\UriInterface as Psr7UriInterface;
use Stringable;
use UnitEnum;
use Uri\Rfc3986\Uri as Rfc3986Uri;
use Uri\WhatWg\Url as WhatWgUrl;
use ValueError;

use function array_is_list;
use function array_key_exists;
use function array_keys;
use function array_map;
use function count;
use function func_get_arg;
use function func_num_args;
use function get_object_vars;
use function is_array;
use function is_bool;
use function is_iterable;
use function is_object;
use function is_scalar;
use function iterator_to_array;
use function spl_object_hash;
use function str_starts_with;
use function substr;

/**
 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
 *
 * @implements IteratorAggregate<array{0:string, 1:string}>
 */
final class URLSearchParams implements Countable, IteratorAggregate, UriComponentInterface, Transformable
{
    private QueryInterface $pairs;

    /**
     * New instance.
     *
     * A string, which will be parsed from application/x-www-form-urlencoded format. A leading '?' character is ignored.
     * A literal sequence of name-value string pairs, or any object with an iterator that produces a sequence of string pairs.
     * A record of string keys and string values. Note that nesting is not supported.
     */
    public function __construct(object|array|string|null $init = '')
    {
        $pairs = self::filterPairs(match (true) {
            $init instanceof self,
            $init instanceof QueryInterface => $init,
            $init instanceof BackedEnum => self::parsePairs($init),
            $init instanceof UriComponentInterface => self::parsePairs($init->value()),
            is_iterable($init) => self::formatIterable($init),
            $init instanceof Stringable, !is_object($init) => self::parsePairs(self::formatQuery($init)),
            default => self::yieldPairs($init),
        });

        $this->pairs = Query::fromPairs($pairs);
    }

    /**
     * @return array<int, array{0:string, 1:string|null}>
     */
    private static function parsePairs(BackedEnum|Stringable|string|null $query): array
    {
        return QueryString::parseFromValue($query, Converter::fromFormData());
    }

    /**
     * @return iterable<array{0:string, 1:string}>
     */
    private static function formatIterable(iterable $iterable): iterable
    {
        if (!is_array($iterable)) {
            $iterable = iterator_to_array($iterable);
        }

        return match (true) {
            array_is_list($iterable) => $iterable,
            default => self::yieldPairs($iterable)
        };
    }

    /**
     * Generates an Iterator containing pairs as items from an object or array.
     *
     * If an iterable is given, foreach will loop over the iterable structure
     * If an object is give, foreach will loop over the object public properties if they are defined
     *
     * @param object|iterable<array-key, mixed> $associative
     *
     * @return Iterator<int, array{0:string, 1:string}>
     */
    private static function yieldPairs(object|array $associative): Iterator
    {
        foreach ($associative as $key => $value) { /* @phpstan-ignore-line */
            yield [self::usvString($key), self::usvString($value)];
        }
    }

    /**
     * @return Iterator<int, array{0:string, 1:string}>
     */
    private static function filterPairs(iterable $pairs): iterable
    {
        $filter = static fn ($pair): ?array => match (true) {
            !is_array($pair),
            [0, 1] !== array_keys($pair) => throw new SyntaxError('A pair must be a sequential array starting at `0` and containing two elements.'),
            null !== $pair[1] => [self::usvString($pair[0]), self::usvString($pair[1])],
            '' !== $pair[0] => [self::usvString($pair[0]), ''],
            default => null,
        };

        foreach ($pairs as $pair) {
            if (null !== ($filteredPair = $filter($pair))) {
                yield $filteredPair;
            }
        }
    }

    private static function formatQuery(BackedEnum|Stringable|string|null $query): string
    {
        $query = (string) StringCoercionMode::Native->coerce($query);
        if (str_starts_with($query, '?')) {
            return substr($query, 1);
        }

        return $query;
    }

    /**
     * Normalizes type to USVString.
     *
     * @see https://webidl.spec.whatwg.org/#idl-USVString
     */
    private static function usvString(mixed $value): string
    {
        return (string) StringCoercionMode::Ecmascript->coerce($value);
    }

    /**
     * Returns a new instance from a string or a stringable object.
     *
     * The input will be parsed from application/x-www-form-urlencoded format.
     * The leading '?' character if present is ignored.
     */
    public static function new(BackedEnum|Stringable|string|null $query = null): self
    {
        return new self(Query::fromFormData(self::formatQuery($query)));
    }

    /**
     * Create a new instance from a string.or a stringable structure or returns null on failure.
     */
    public static function tryNew(BackedEnum|Stringable|string|null $uri = null): ?self
    {
        try {
            return self::new($uri);
        } catch (UriException) {
            return null;
        }
    }

    /**
     * Returns a new instance from a literal sequence of name-value string pairs,
     * or any object with an iterator that produces a sequence of string pairs.
     *
     * @param iterable<int, array{0:string, 1:string|null}> $pairs
     */
    public static function fromPairs(iterable $pairs): self
    {
        return new self(Query::fromPairs($pairs, coercionMode: StringCoercionMode::Ecmascript));
    }

    /**
     * Returns a new instance from a record of string keys and string values.
     *
     * A record can be, an iterable or any object with scalar or null public properties. Nesting is not supported.
     *
     * @param object|iterable<array-key, Stringable|string|float|int|bool|null> $associative
     */
    public static function fromAssociative(object|array $associative): self
    {
        return new self(Query::fromPairs(self::yieldPairs($associative), coercionMode: StringCoercionMode::Ecmascript));
    }

    /**
     * Returns a new instance from a URI.
     */
    public static function fromUri(WhatWgUrl|Rfc3986Uri|BackedEnum|Stringable|string $uri): self
    {
        $query = match (true) {
            $uri instanceof Rfc3986Uri => $uri->getRawQuery(),
            $uri instanceof WhatWgUrl, $uri instanceof UriInterface => $uri->getQuery(),
            $uri instanceof Psr7UriInterface => UriString::parse($uri)['query'],
            default => Uri::new($uri)->getQuery(),
        };

        return new self(Query::fromPairs(QueryString::parseFromValue($query, Converter::fromFormData()), coercionMode: StringCoercionMode::Ecmascript));
    }

    /**
     * Returns a new instance from the input of PHP's http_build_query.
     */
    public static function fromVariable(object|array $parameters): self
    {
        return self::fromPairs(self::parametersToPairs($parameters));
    }

    private static function parametersToPairs(array|object $data, string|int $prefix = '', array &$recursive = []): array
    {
        $yieldParameters = static fn (object|array $data): array => is_array($data) ? $data : get_object_vars($data);

        $pairs = [];
        foreach ($yieldParameters($data) as $name => $value) {
            if (is_object($data)) {
                $id = spl_object_hash($data);
                if (!array_key_exists($id, $recursive)) {
                    $recursive[$id] = 1;
                }
            }

            if (is_object($value)) {
                $id = spl_object_hash($value);
                if (array_key_exists($id, $recursive)) {
                    return [];
                }

                $recursive[$id] = 1;
            }

            if ('' !== $prefix) {
                $name = $prefix.'['.$name.']';
            }

            $pairs = match (true) {
                is_array($value),
                is_object($value) => [...$pairs, ...self::parametersToPairs($value, $name, $recursive)],
                is_scalar($value) => [...$pairs, [$name, self::usvString($value)]],
                default => $pairs,
            };
        }

        return $pairs;
    }

    public function value(): ?string
    {
        return $this->pairs->toFormData();
    }

    public function equals(mixed $value): bool
    {
        if (!StringCoercionMode::Ecmascript->isCoercible($value)) {
            return false;
        }

        if (!$value instanceof UriComponentInterface) {
            $value = self::tryNew(StringCoercionMode::Ecmascript->coerce($value));
            if (null === $value) {
                return false;
            }
        }

        return $value->getUriComponent() === $this->getUriComponent();
    }

    /**
     * Returns a query string suitable for use in a URL.
     */
    public function toString(): string
    {
        return $this->value() ?? '';
    }

    public function decoded(): string
    {
        return (string) Query::fromPairs($this->pairs)->decoded();
    }

    public function __toString(): string
    {
        return $this->toString();
    }

    public function jsonSerialize(): string
    {
        return $this->toString();
    }

    public function getUriComponent(): string
    {
        $value = $this->value() ?? '';

        return match ('') {
            $value => $value,
            default => '?'.$value,
        };
    }

    /**
     * Returns an iterator allowing iteration through all keys contained in this object.
     *
     * @return iterable<string>
     */
    public function keys(): iterable
    {
        foreach ($this->pairs as [$key, $__]) {
            yield $key;
        }
    }

    /**
     * Returns an iterator allowing iteration through all values contained in this object.
     *
     * @return iterable<string>
     */
    public function values(): iterable
    {
        foreach ($this->pairs as [$__, $value]) {
            yield $value ?? '';
        }
    }

    /**
     * Tells whether the specified parameter is in the search parameters.
     *
     * The method requires at least one parameter as the pair name (string or null)
     * and an optional second and last parameter as the pair value (Stringable|string|float|int|bool|null)
     * <code>
     * $params = new URLSearchParams('a=b&c);
     * $params->has('c');      // return true
     * $params->has('a', 'b'); // return true
     * $params->has('a', 'c'); // return false
     * </code>
     */
    public function has(?string $name): bool
    {
        $name = self::usvString($name);

        return match (func_num_args()) {
            1 => $this->pairs->has($name),
            2 => $this->pairs->hasPair($name, self::usvString(func_get_arg(1))),
            default => throw new ArgumentCountError(__METHOD__.' requires at least one argument as the pair name and a second optional argument as the pair value.'),
        };
    }

    /**
     * Tells whether the specified pair is in the search parameters.
     *
     * The method requires at least one parameter as the pair name (string or null)
     * and an optional second and last parameter as the pair value (Stringable|string|float|int|bool|null)
     * <code>
     * $params = new URLSearchParams('a=b&c);
     * $params->has('a', 'b'); // return true
     * </code>
     */
    public function hasValue(?string $name, mixed $value): bool
    {
        return $this->has($name, $value);
    }

    /**
     * Returns the first value associated to the given search parameter or null if none exists.
     */
    public function get(?string $name): ?string
    {
        return match (true) {
            $this->has($name) => $this->pairs->get(self::usvString($name)) ?? '',
            default => null,
        };
    }

    public function first(?string $name): ?string
    {
        return $this->get($name);
    }

    public function last(?string $name): ?string
    {
        if (!$this->has($name)) {
            return null;
        }

        $res = $this->pairs->getAll(self::usvString($name));

        return $res[count($res) - 1] ?? null;
    }

    /**
     * Returns all the values associated with a given search parameter as an array.
     *
     * @return array<string>
     */
    public function getAll(?string $name): array
    {
        return array_map(
            fn (?string $value): string => $value ?? '',
            $this->pairs->getAll(self::usvString($name))
        );
    }

    private function getValue(int|string $key, Occurrence $occurrence): ?string
    {
        $key = (string) $key;

        return match ($occurrence) {
            Occurrence::First => $this->first($key),
            Occurrence::Last => $this->last($key),
        };
    }

    public function string(int|string $key, ?string $default = null, Occurrence $occurrence = Occurrence::First): ?string
    {
        return TypeConverter::toString($this->getValue($key, $occurrence)) ?? $default;
    }

    public function strings(int|string $key, ?string $default = null): array
    {
        return TypeConverter::toStrings($this->getAll((string) $key), $default);
    }

    public function integer(int|string $key, ?int $default = null, Occurrence $occurrence = Occurrence::First): ?int
    {
        return TypeConverter::toInteger($this->getValue($key, $occurrence)) ?? $default;
    }

    /**
     * @return array<int>
     */
    public function integers(int|string $key, ?int $default = null): array
    {
        return TypeConverter::toIntegers($this->getAll((string) $key), $default);
    }

    public function float(int|string $key, ?float $default = null, Occurrence $occurrence = Occurrence::First): ?float
    {
        return TypeConverter::toFloat($this->getValue($key, $occurrence)) ?? $default;
    }

    /**
     * @return array<float>
     */
    public function floats(int|string $key, ?float $default = null): array
    {
        return TypeConverter::toFloats($this->getAll((string) $key), $default);
    }

    public function boolean(int|string $key, ?bool $default = null, Occurrence $occurrence = Occurrence::First): ?bool
    {
        return TypeConverter::toBoolean($this->get((string) $key)) ?? $default;
    }

    /**
     * @return array<bool>
     */
    public function booleans(int|string $key, ?bool $default = null): array
    {
        return TypeConverter::toBooleans($this->getAll((string) $key), $default);
    }

    /**
     * @param class-string<UnitEnum> $enumClass
     */
    public function enum(int|string $key, string $enumClass, ?UnitEnum $default = null, Occurrence $occurrence = Occurrence::First): ?UnitEnum
    {
        null === $default || $default instanceof $enumClass || throw new ValueError('The default value must be an instance of '.$enumClass.'; '.get_debug_type($default).' given.');

        return TypeConverter::toEnum($this->getValue($key, $occurrence), $enumClass) ?? $default;
    }

    /**
     * @param class-string<UnitEnum> $enumClass
     *
     * @return array<UnitEnum>
     */
    public function enums(int|string $key, string $enumClass, ?UnitEnum $default = null): array
    {
        return TypeConverter::toEnums($this->getAll((string) $key), $enumClass, $default);
    }

    /**
     * @param non-empty-string $format
     *
     * @throws Exception
     */
    public function date(
        int|string $key,
        string $format,
        DateTimeZone|string $timezone = 'UTC',
        ?DateTimeInterface $default = null,
        Occurrence $occurrence = Occurrence::First
    ): ?DateTimeImmutable {
        return TypeConverter::toDateTimeImmutable($this->getValue($key, $occurrence), $format, $timezone) ?? (
            null !== $default && !$default instanceof DateTimeImmutable
                ? DateTimeImmutable::createFromInterface($default)
                : null
        );
    }

    /**
     * @param non-empty-string $format
     *
     * @throws Exception
     *
     * @return array<DateTimeImmutable>
     */
    public function dates(
        int|string $key,
        string $format,
        DateTimeZone|string $timezone = 'UTC',
        ?DateTimeInterface $default = null,
    ): array {
        return TypeConverter::toDateTimeImmutables(
            values: $this->getAll((string) $key),
            format: $format,
            timezone: $timezone,
            default: $default,
        );
    }

    /**
     * Tells whether the instance has some parameters.
     */
    public function isNotEmpty(): bool
    {
        return ! $this->isEmpty();
    }

    /**
     * Tells whether the instance has no parameters.
     */
    public function isEmpty(): bool
    {
        return 0 === $this->size();
    }

    /**
     * Returns the total number of distinct search parameter keys.
     */
    public function countDistinctKeys(): int
    {
        return $this->pairs->countDistinctKeys();
    }

    /**
     * Returns the total number of search parameter entries.
     *
     * @return int<0, max>
     */
    public function size(): int
    {
        return count($this->pairs);
    }

    /**
     * @see URLSearchParams::size()
     */
    public function count(): int
    {
        return $this->size();
    }

    /**
     * Allowing iteration through all key/value pairs contained in this object.
     *
     * The iterator returns key/value pairs in the same order as they appear in the query string.
     * The key and value of each pair are string objects.
     */
    public function entries(): Iterator
    {
        yield from $this->pairs;
    }

    /**
     * @see URLSearchParams::entries()
     */
    public function getIterator(): Iterator
    {
        return $this->entries();
    }

    /**
     * Allows iteration through all values contained in this object via a callback function.
     *
     * @param Closure(string $value, string $key): void $callback
     */
    public function each(Closure $callback): void
    {
        foreach ($this->pairs->pairs() as $key => $value) {
            $callback($value ?? '', $key);
        }
    }

    private function updateQuery(QueryInterface $query): void
    {
        if ($query->value() !== $this->pairs->value()) {
            $this->pairs = $query;
        }
    }

    /**
     * Sets the value associated with a given search parameter to the given value.
     *
     * If there were several matching values, this method deletes the others.
     * If the search parameter doesn't exist, this method creates it.
     */
    public function set(?string $name, mixed $value): void
    {
        $this->updateQuery($this->pairs->withPair(self::usvString($name), self::usvString($value)));
    }

    /**
     * Appends a specified key/value pair as a new search parameter.
     */
    public function append(?string $name, mixed $value): void
    {
        $this->updateQuery($this->pairs->appendTo(self::usvString($name), self::usvString($value)));
    }

    /**
     * Deletes specified parameters and their associated value(s) from the list of all search parameters.
     *
     * The method requires at least one parameter as the pair name (string or null)
     * and an optional second and last parameter as the pair value (Stringable|string|float|int|bool|null)
     * <code>
     * $params = new URLSearchParams('a=b&c);
     * $params->delete('c'); //delete all parameters with the key 'c'
     * $params->delete('a', 'b') //delete all pairs with the key 'a' and the value 'b'
     * </code>
     */
    public function delete(?string $name): void
    {
        $name = self::usvString($name);

        $this->updateQuery(match (func_num_args()) {
            1 => $this->pairs->withoutPairByKey($name),
            2 => $this->pairs->withoutPairByKeyValue($name, self::usvString(func_get_arg(1))),
            default => throw new ArgumentCountError(__METHOD__.' requires at least one argument as the pair name and a second optional argument as the pair value.'),
        });
    }

    /**
     * Deletes all pair with the specified name and associated value from the list of all search parameters.
     *
     * <code>
     * $params->deleteValue('a', 'b') //delete all pairs with the key 'a' and the value 'b'
     * </code>
     */
    public function deleteValue(?string $name, mixed $value): void
    {
        $this->updateQuery(
            $this->pairs->withoutPairByKeyValue(
                self::usvString($name),
                self::usvString($value),
            )
        );
    }

    /**
     * Sorts all key/value pairs contained in this object in place and returns undefined.
     *
     * The sort order is according to Unicode code points of the keys. This method
     * uses a stable sorting algorithm (i.e. the relative order between
     * key/value pairs with equal keys will be preserved).
     */
    public function sort(): void
    {
        $this->updateQuery($this->pairs->sort());
    }

    public function when(callable|bool $condition, callable $onSuccess, ?callable $onFail = null): static
    {
        if (!is_bool($condition)) {
            $condition = $condition($this);
        }

        return match (true) {
            $condition => $onSuccess($this) ?? $this,
            null !== $onFail => $onFail($this) ?? $this,
            default => $this,
        };
    }

    /**
     * Executes the given callback with the current instance
     * and returns the current instance.
     *
     * @param callable(self): self $callback
     */
    public function transform(callable $callback): static
    {
        return $callback($this);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.8.0
     * @see URLSearchParams::countDistinctKeys()
     *
     * @codeCoverageIgnore
     */
    #[Deprecated(message:'use League\Uri\Components\URLSearchParams::countDistinctKeys() instead', since:'league/uri-components:7.8.0')]
    public function uniqueKeyCount(): int
    {
        return $this->countDistinctKeys();
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.4.0
     * @see URLSearchParams::fromVariable()
     *
     * @codeCoverageIgnore
     */
    #[Deprecated(message:'use League\Uri\Components\URLSearchParams::fromVariable() instead', since:'league/uri-components:7.4.0')]
    public static function fromParameters(object|array $parameters): self
    {
        return new self(Query::fromParameters($parameters));
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\Components;

use BackedEnum;
use Deprecated;
use League\Uri\Contracts\AuthorityInterface;
use League\Uri\Contracts\IpHostInterface;
use League\Uri\Contracts\UriComponentInterface;
use League\Uri\Contracts\UriException;
use League\Uri\Contracts\UriInterface;
use League\Uri\Exceptions\MissingFeature;
use League\Uri\Exceptions\SyntaxError;
use League\Uri\HostRecord;
use League\Uri\HostType;
use League\Uri\Idna\Converter as IdnConverter;
use League\Uri\IPv4\Converter as IPv4Converter;
use League\Uri\IPv4Normalizer;
use League\Uri\StringCoercionMode;
use League\Uri\UriString;
use Psr\Http\Message\UriInterface as Psr7UriInterface;
use Stringable;
use Uri\Rfc3986\Uri as Rfc3986Uri;
use Uri\WhatWg\Url as WhatWgUrl;

use function explode;
use function filter_var;
use function preg_replace_callback;
use function rawurldecode;
use function rawurlencode;
use function rtrim;
use function sprintf;
use function strtolower;
use function strtoupper;
use function substr;

use const FILTER_FLAG_IPV6;
use const FILTER_VALIDATE_IP;

final class Host extends Component implements IpHostInterface
{
    private readonly ?string $value;
    private readonly HostRecord $host;

    private function __construct(BackedEnum|Stringable|string|null $host)
    {
        $this->host = HostRecord::from($host);
        $this->value = $this->host->toAscii();
    }

    public static function new(BackedEnum|Stringable|string|null $value = null): self
    {
        return new self($value);
    }

    /**
     * Create a new instance from a string.or a stringable structure or returns null on failure.
     */
    public static function tryNew(BackedEnum|Stringable|string|null $uri = null): ?self
    {
        try {
            return self::new($uri);
        } catch (UriException) {
            return null;
        }
    }

    /**
     * Returns a host from an IP address.
     *
     * @throws MissingFeature If detecting IPv4 is not possible
     * @throws SyntaxError If the $ip cannot be converted into a Host
     */
    public static function fromIp(BackedEnum|Stringable|string $ip, string $version = ''): self
    {
        if ($ip instanceof BackedEnum) {
            $ip = $ip->value;
        }

        if ('' !== $version) {
            return new self('[v'.$version.'.'.$ip.']');
        }

        $ip = (string) $ip;
        if (false !== filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
            return new self('['.$ip.']');
        }

        if (str_contains($ip, '%')) {
            [$ipv6, $zoneId] = explode('%', rawurldecode($ip), 2) + [1 => ''];

            return new self('['.$ipv6.'%25'.rawurlencode($zoneId).']');
        }

        $host = IPv4Converter::fromEnvironment()->toDecimal($ip);
        if (null === $host) {
            throw new SyntaxError(sprintf('`%s` is an invalid IP Host.', $ip));
        }

        return new self($host);
    }

    /**
     * Create a new instance from a URI object.
     */
    public static function fromUri(WhatWgUrl|Rfc3986Uri|BackedEnum|Stringable|string $uri): self
    {
        $uri = self::filterUri($uri);

        return match (true) {
            $uri instanceof Rfc3986Uri => new self($uri->getRawHost()),
            $uri instanceof WhatWgUrl => new self($uri->getAsciiHost()),
            $uri instanceof Psr7UriInterface => new self(UriString::parse($uri)['host']),
            default => new self($uri->getHost()),
        };
    }

    /**
     * Create a new instance from an Authority object.
     */
    public static function fromAuthority(BackedEnum|Stringable|string $authority): self
    {
        return match (true) {
            $authority instanceof AuthorityInterface => new self($authority->getHost()),
            default => new self(Authority::new($authority)->getHost()),
        };
    }

    public function value(): ?string
    {
        return $this->value;
    }

    public function equals(mixed $value): bool
    {
        if (!StringCoercionMode::Native->isCoercible($value)) {
            return false;
        }

        if (!$value instanceof UriComponentInterface) {
            $value = self::tryNew(StringCoercionMode::Native->coerce($value));
            if (null === $value) {
                return false;
            }
        }

        return rtrim($value->getUriComponent(), '.') === rtrim($this->getUriComponent(), '.');
    }

    public function toAscii(): ?string
    {
        return $this->value();
    }

    public function toUnicode(): ?string
    {
        return $this->host->toUnicode();
    }

    public function encoded(): ?string
    {
        if (null === $this->value || '' === $this->value || HostType::RegisteredName !== $this->host->type) {
            return $this->value;
        }

        return (string) preg_replace_callback(
            '/%[0-9A-F]{2}/i',
            fn (array $matches): string => strtoupper($matches[0]),
            strtolower(rawurlencode(IdnConverter::toUnicode($this->value)->domain()))
        );
    }

    public function getIpVersion(): ?string
    {
        return $this->host->ipVersion();
    }

    public function getIp(): ?string
    {
        return $this->host->ipValue();
    }

    public function isRegisteredName(): bool
    {
        return HostType::RegisteredName === $this->host->type;
    }

    public function isDomain(): bool
    {
        return $this->host->isDomainType();
    }

    public function isIp(): bool
    {
        return HostType::RegisteredName !== $this->host->type;
    }

    public function isIpv4(): bool
    {
        return HostType::Ipv4 === $this->host->type;
    }

    public function isIpv6(): bool
    {
        return HostType::Ipv6 === $this->host->type;
    }

    public function isIpFuture(): bool
    {
        return HostType::IpvFuture === $this->host->type;
    }

    public function hasZoneIdentifier(): bool
    {
        return $this->host->hasZoneIdentifier();
    }

    public function withoutZoneIdentifier(): IpHostInterface
    {
        if (!$this->host->hasZoneIdentifier()) {
            return $this;
        }

        [$ipv6] = explode('%', substr((string) $this->value, 1, -1));

        return self::fromIp($ipv6);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @see Host::new()
     *
     * @codeCoverageIgnore
     */
    #[Deprecated(message:'use League\Uri\Components\Host::new() instead', since:'league/uri-components:7.0.0')]
    public static function createFromString(Stringable|string|null $host): self
    {
        return self::new($host);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @see Host::new()
     *
     * @codeCoverageIgnore
     *
     * Returns a new instance from null.
     */
    #[Deprecated(message:'use League\Uri\Components\Host::new() instead', since:'league/uri-components:7.0.0')]
    public static function createFromNull(): self
    {
        return self::new();
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @throws MissingFeature If detecting IPv4 is not possible
     * @throws SyntaxError If the $ip cannot be converted into a Host
     * @deprecated Since version 7.0.0
     * @see Host::fromIp()
     *
     * @codeCoverageIgnore
     *
     * Returns a host from an IP address.
     *
     */
    #[Deprecated(message:'use League\Uri\Components\Host::fromIp() instead', since:'league/uri-components:7.0.0')]
    public static function createFromIp(string $ip, string $version = '', ?IPv4Normalizer $normalizer = null): self
    {
        return self::fromIp($ip, $version);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @see Host::fromUri()
     *
     * @codeCoverageIgnore
     *
     * Create a new instance from a URI object.
     */
    #[Deprecated(message:'use League\Uri\Components\Host::fromUri() instead', since:'league/uri-components:7.0.0')]
    public static function createFromUri(Psr7UriInterface|UriInterface $uri): self
    {
        return self::fromUri($uri);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @see Host::fromAuthority()
     *
     * @codeCoverageIgnore
     *
     * Create a new instance from an Authority object.
     */
    #[Deprecated(message:'use League\Uri\Components\Host::fromAuthority() instead', since:'league/uri-components:7.0.0')]
    public static function createFromAuthority(Stringable|string $authority): self
    {
        return self::fromAuthority($authority);
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\Components;

use BackedEnum;
use DateTimeImmutable;
use DateTimeInterface;
use DateTimeZone;
use Deprecated;
use Exception;
use Iterator;
use League\Uri\Contracts\PathInterface;
use League\Uri\Contracts\SegmentedPathInterface;
use League\Uri\Contracts\UriException;
use League\Uri\Contracts\UriInterface;
use League\Uri\Encoder;
use League\Uri\Exceptions\OffsetOutOfBounds;
use League\Uri\Exceptions\SyntaxError;
use League\Uri\TypeConverter;
use Psr\Http\Message\UriInterface as Psr7UriInterface;
use Stringable;
use TypeError;
use UnitEnum;
use Uri\Rfc3986\Uri as Rfc3986Uri;
use Uri\WhatWg\Url as WhatWgUrl;
use ValueError;

use function array_count_values;
use function array_filter;
use function array_keys;
use function array_pop;
use function array_unshift;
use function count;
use function dirname;
use function explode;
use function implode;
use function ltrim;
use function rtrim;
use function sprintf;
use function str_contains;
use function str_replace;
use function str_starts_with;
use function strrpos;
use function substr;

use const ARRAY_FILTER_USE_KEY;
use const FILTER_VALIDATE_INT;
use const PATHINFO_EXTENSION;

final class HierarchicalPath extends Component implements SegmentedPathInterface
{
    private const SEPARATOR = '/';
    private const IS_ABSOLUTE = 1;
    private const IS_RELATIVE = 0;
    private readonly PathInterface $path;
    /** @var array<string> */
    private readonly array $segments;

    private function __construct(BackedEnum|Stringable|string $path)
    {
        if (!$path instanceof PathInterface) {
            $path = Path::new($path);
        }

        $this->path = $path;
        $segments = $this->path->decoded();
        if ($this->path->isAbsolute()) {
            $segments = substr($segments, 1);
        }

        $this->segments = explode(self::SEPARATOR, $segments);
    }

    /**
     * Returns a new instance from a string or a stringable object.
     */
    public static function new(BackedEnum|Stringable|string $value = ''): self
    {
        return new self($value);
    }

    /**
     * Create a new instance from a string.or a stringable structure or returns null on failure.
     */
    public static function tryNew(BackedEnum|Stringable|string $uri = ''): ?self
    {
        try {
            return self::new($uri);
        } catch (UriException) {
            return null;
        }
    }

    /**
     * Create a new instance from a URI object.
     */
    public static function fromUri(WhatWgUrl|Rfc3986Uri|BackedEnum|Stringable|string $uri): self
    {
        return new self(Path::fromUri($uri));
    }

    /**
     * Returns a new instance from an iterable structure.
     *
     * @throws TypeError If the segments are malformed
     */
    public static function fromRelative(string ...$segments): self
    {
        return self::fromSegments(self::IS_RELATIVE, $segments);
    }

    /**
     * Returns a new instance from an iterable structure.
     *
     * @throws TypeError If the segments are malformed
     */
    public static function fromAbsolute(string ...$segments): self
    {
        return self::fromSegments(self::IS_ABSOLUTE, $segments);
    }

    /**
     * @param array<string> $segments
     */
    private static function fromSegments(int $pathType, array $segments): self
    {
        $path = implode(self::SEPARATOR, $segments);

        return match (true) {
            self::IS_RELATIVE === $pathType => new self(ltrim($path, self::SEPARATOR)),
            self::SEPARATOR !== ($path[0] ?? '') => new self(self::SEPARATOR.$path),
            default => new self($path),
        };
    }

    public function count(): int
    {
        return count($this->segments);
    }

    public function getIterator(): Iterator
    {
        yield from $this->segments;
    }

    public function isAbsolute(): bool
    {
        return $this->path->isAbsolute();
    }

    public function hasTrailingSlash(): bool
    {
        return $this->path->hasTrailingSlash();
    }

    public function value(): ?string
    {
        return $this->path->value();
    }

    public function equals(mixed $value): bool
    {
        return $this->path->equals($value);
    }

    public function decoded(): string
    {
        return $this->path->decoded();
    }

    public function normalize(): self
    {
        return new self((string) $this->path->normalize()->value());
    }

    public function getDirname(): string
    {
        $path = $this->path->decoded();

        return str_replace(
            ['\\', "\0"],
            [self::SEPARATOR, '\\'],
            dirname(str_replace('\\', "\0", $path))
        );
    }

    public function getBasename(): string
    {
        $data = $this->segments;
        $basename = (string) array_pop($data);
        $pos = strpos($basename, ';');

        return match (false) {
            $pos => $basename,
            default => substr($basename, 0, $pos),
        };
    }

    public function getExtension(): string
    {
        [$basename] = explode(';', $this->getBasename(), 2);

        return pathinfo($basename, PATHINFO_EXTENSION);
    }

    public function first(): ?string
    {
        return $this->get(0);
    }

    public function last(): ?string
    {
        return $this->get(-1);
    }

    public function string(int $key, ?string $default = null): ?string
    {
        return TypeConverter::toString($this->get($key)) ?? $default;
    }

    public function integer(int $key, ?int $default = null): ?int
    {
        return TypeConverter::toInteger($this->get($key)) ?? $default;
    }

    public function float(int $key, ?float $default = null): ?float
    {
        return TypeConverter::toFloat($this->get($key)) ?? $default;
    }

    public function boolean(int $key, ?bool $default = null): ?bool
    {
        return TypeConverter::toBoolean($this->get($key)) ?? $default;
    }

    /**
     * @param class-string<UnitEnum> $enumClass
     */
    public function enum(int $key, string $enumClass, ?UnitEnum $default = null): ?UnitEnum
    {
        null === $default || $default instanceof $enumClass || throw new ValueError('The default value must be an instance of '.$enumClass.'; '.get_debug_type($default).' given.');

        return TypeConverter::toEnum($this->get($key), $enumClass) ?? $default;
    }

    /**
     * @param non-empty-string $format
     *
     * @throws Exception
     */
    public function date(
        int $key,
        string $format,
        DateTimeZone|string $timezone = 'UTC',
        ?DateTimeInterface $default = null,
    ): ?DateTimeImmutable {
        return TypeConverter::toDateTimeImmutable($this->get($key), $format, $timezone) ?? (
            null !== $default && !$default instanceof DateTimeImmutable
            ? DateTimeImmutable::createFromInterface($default)
            : null
        );
    }

    public function indexOf(BackedEnum|Stringable|string $segment): ?int
    {
        return $this->keys($segment)[0] ?? null;
    }

    public function lastIndexOf(BackedEnum|Stringable|string $segment): ?int
    {
        $res = $this->keys($segment);

        return $res[count($res) - 1] ?? null;
    }

    public function contains(BackedEnum|Stringable|string $segment): bool
    {
        return [] !== $this->keys($segment);
    }

    public function isEmpty(): bool
    {
        return '' === $this->path->value();
    }

    public function get(int $offset): ?string
    {
        if ($offset < 0) {
            $offset += count($this->segments);
        }

        return $this->segments[$offset] ?? null;
    }

    public function keys(BackedEnum|Stringable|string|null $segment = null): array
    {
        $segment = self::filterComponent($segment);

        return match (null) {
            $segment => array_keys($this->segments),
            default => array_keys($this->segments, $segment, true),
        };
    }

    public function withoutDotSegments(): PathInterface
    {
        $path = $this->path->withoutDotSegments();

        return match ($this->path) {
            $path => $this,
            default =>  new self($path),
        };
    }

    public function withLeadingSlash(): PathInterface
    {
        $path = $this->path->withLeadingSlash();

        return match ($this->path) {
            $path => $this,
            default =>  new self($path),
        };
    }

    public function withoutLeadingSlash(): PathInterface
    {
        $path = $this->path->withoutLeadingSlash();

        return match ($this->path) {
            $path => $this,
            default =>  new self($path),
        };
    }

    public function withoutTrailingSlash(): PathInterface
    {
        $path = $this->path->withoutTrailingSlash();

        return match ($this->path) {
            $path => $this,
            default =>  new self($path),
        };
    }

    public function withTrailingSlash(): PathInterface
    {
        $path = $this->path->withTrailingSlash();

        return match ($this->path) {
            $path => $this,
            default =>  new self($path),
        };
    }

    public function append(BackedEnum|Stringable|string $path): SegmentedPathInterface
    {
        /** @var string $path */
        $path = self::filterComponent($path);

        return new self(
            rtrim($this->path->toString(), self::SEPARATOR)
            .self::SEPARATOR
            .ltrim($path, self::SEPARATOR)
        );
    }

    /**
     * @param iterable<BackedEnum|Stringable|string> $segments
     *
     */
    public function appendSegments(iterable $segments): SegmentedPathInterface
    {
        $newSegments = [];
        foreach ($segments as $segment) {
            $newSegments[] = str_replace('/', '%2F', self::filterComponent($segment) ?? throw new ValueError('The segment can not be null.'));
        }

        return $this->append(implode('/', $newSegments));
    }

    public function prepend(BackedEnum|Stringable|string $path): SegmentedPathInterface
    {
        /** @var string $path */
        $path = self::filterComponent($path);

        return new self(
            rtrim($path, self::SEPARATOR)
            .self::SEPARATOR
            .ltrim($this->path->toString(), self::SEPARATOR)
        );
    }

    /**
     * @param iterable<BackedEnum|Stringable|string> $segments
     *
     */
    public function prependSegments(iterable $segments): SegmentedPathInterface
    {
        $newSegments = [];
        foreach ($segments as $segment) {
            $newSegments[] = str_replace('/', '%2F', self::filterComponent($segment) ?? throw new ValueError('The segment can not be null.'));
        }

        return $this->prepend(implode('/', $newSegments));
    }

    public function withSegment(int $key, BackedEnum|Stringable|string $segment): SegmentedPathInterface
    {
        $nbSegments = count($this->segments);
        if ($key < - $nbSegments - 1 || $key > $nbSegments) {
            throw new OffsetOutOfBounds(sprintf('The given key `%s` is invalid.', $key));
        }

        if (0 > $key) {
            $key += $nbSegments;
        }

        if ($nbSegments === $key) {
            return $this->append($segment);
        }

        if (-1 === $key) {
            return $this->prepend($segment);
        }

        if (!$segment instanceof PathInterface) {
            $segment = new self($segment);
        }

        $segment = Encoder::decodeAll($segment);
        if ($segment === ($this->segments[$key] ?? null)) {
            return $this;
        }

        $segments = $this->segments;
        $segments[$key] = $segment;
        if ($this->isAbsolute()) {
            array_unshift($segments, '');
        }

        return new self(implode(self::SEPARATOR, $segments));
    }

    public function withoutEmptySegments(): SegmentedPathInterface
    {
        /** @var string $path */
        $path = preg_replace(',/+,', self::SEPARATOR, $this->toString());

        return new self($path);
    }

    public function withoutSegment(int ...$keys): SegmentedPathInterface
    {
        if ([] === $keys) {
            return $this;
        }
        $nb_segments = count($this->segments);
        $options = ['options' => ['min_range' => - $nb_segments, 'max_range' => $nb_segments - 1]];
        $deleted_keys = [];
        foreach ($keys as $value) {
            /** @var false|int $offset */
            $offset = filter_var($value, FILTER_VALIDATE_INT, $options);
            if (false === $offset) {
                throw new OffsetOutOfBounds(sprintf('The key `%s` is invalid.', $value));
            }

            if ($offset < 0) {
                $offset += $nb_segments;
            }
            $deleted_keys[] = $offset;
        }

        $deleted_keys = array_keys(array_count_values($deleted_keys));
        $filter = static fn ($key): bool => !in_array($key, $deleted_keys, true);

        $path = implode(self::SEPARATOR, array_filter($this->segments, $filter, ARRAY_FILTER_USE_KEY));
        if ($this->isAbsolute()) {
            return new self(self::SEPARATOR.$path);
        }

        return new self($path);
    }

    public function slice(int $offset, ?int $length = null): self
    {
        $nbSegments = count($this->segments);
        if ($offset < -$nbSegments || $offset > $nbSegments) {
            throw new OffsetOutOfBounds(sprintf('No segment can be found with at : `%s`.', $offset));
        }

        $segments = array_slice($this->segments, $offset, $length, true);
        if ($this->hasTrailingSlash()) {
            $segments[] = '';
        }

        return match (true) {
            $segments === $this->segments => $this,
            $this->isAbsolute() => self::fromAbsolute(...$segments),
            default => self::fromRelative(...$segments),
        };
    }

    public function withDirname(BackedEnum|Stringable|string $path): SegmentedPathInterface
    {
        if (!$path instanceof PathInterface) {
            $path = Path::new($path);
        }

        if ($path->value() === $this->getDirname()) {
            return $this;
        }

        $segments = $this->segments;

        return new self(
            rtrim($path->toString(), self::SEPARATOR)
            .self::SEPARATOR
            .array_pop($segments)
        );
    }

    public function withBasename(BackedEnum|Stringable|string $basename): SegmentedPathInterface
    {
        /** @var string $basename */
        $basename = $this->validateComponent($basename);

        return match (true) {
            str_contains($basename, self::SEPARATOR) => throw new SyntaxError('The basename cannot contain the path separator.'),
            default => $this->withSegment(count($this->segments) - 1, $basename),
        };
    }

    public function withExtension(BackedEnum|Stringable|string $extension): SegmentedPathInterface
    {
        /** @var string $extension */
        $extension = $this->validateComponent($extension);
        if (str_contains($extension, self::SEPARATOR)) {
            throw new SyntaxError('An extension sequence cannot contain a path delimiter.');
        }

        if (str_starts_with($extension, '.')) {
            throw new SyntaxError('An extension sequence cannot contain a leading `.` character.');
        }

        /** @var string $basename */
        $basename = $this->segments[array_key_last($this->segments)];
        [$ext, $param] = explode(';', $basename, 2) + [1 => null];
        if ('' === $ext) {
            return $this;
        }

        return $this->withBasename($this->buildBasename($extension, (string) $ext, $param));
    }

    /**
     * Creates a new basename with a new extension.
     */
    private function buildBasename(string $extension, string $ext, ?string $param = null): string
    {
        $length = strrpos($ext, '.'.pathinfo($ext, PATHINFO_EXTENSION));
        if (false !== $length) {
            $ext = substr($ext, 0, $length);
        }

        if (null !== $param && '' !== $param) {
            $param = ';'.$param;
        }

        $extension = trim($extension);
        if ('' === $extension) {
            return $ext.$param;
        }

        return $ext.'.'.$extension.$param;
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @see HierarchicalPath::getIterator()
     *
     * @codeCoverageIgnore
     *
     * Returns a new instance from a string or a stringable object.
     */
    #[Deprecated(message:'use League\Uri\Components\HierarchicalPath::getIterator() instead', since:'league/uri-components:7.0.0')]
    public function segments(): array
    {
        return $this->segments;
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @see HierarchicalPath::new()
     *
     * @codeCoverageIgnore
     *
     * Returns a new instance from a string or a stringable object.
     */
    #[Deprecated(message:'use League\Uri\Components\HierarchicalPath::new() instead', since:'league/uri-components:7.0.0')]
    public static function createFromString(Stringable|string $path): self
    {
        return self::new($path);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @see HierarchicalPath::new()
     *
     * @codeCoverageIgnore
     */
    #[Deprecated(message:'use League\Uri\Components\HierarchicalPath::new() instead', since:'league/uri-components:7.0.0')]
    public static function createFromPath(PathInterface $path): self
    {
        return self::new($path);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @throws TypeError If the segments are malformed
     *@see HierarchicalPath::fromRelative()
     *
     * @codeCoverageIgnore
     *
     * Returns a new instance from an iterable structure.
     *
     * @deprecated Since version 7.0.0
     */
    #[Deprecated(message:'use League\Uri\Components\HierarchicalPath::fromRelative() instead', since:'league/uri-components:7.0.0')]
    public static function createRelativeFromSegments(iterable $segments): self
    {
        return self::fromRelative(...$segments);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @throws TypeError If the segments are malformed
     *@see HierarchicalPath::fromAbsolute()
     *
     * @codeCoverageIgnore
     *
     * Returns a new instance from an iterable structure.
     *
     * @deprecated Since version 7.0.0
     */
    #[Deprecated(message:'use League\Uri\Components\HierarchicalPath::fromAbsolute() instead', since:'league/uri-components:7.0.0')]
    public static function createAbsoluteFromSegments(iterable $segments): self
    {
        return self::fromAbsolute(...$segments);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @see HierarchicalPath::fromUri()
     *
     * @codeCoverageIgnore
     *
     * Create a new instance from a URI object.
     */
    #[Deprecated(message:'use League\Uri\Components\HierarchicalPath::fromUri() instead', since:'league/uri-components:7.0.0')]
    public static function createFromUri(Psr7UriInterface|UriInterface $uri): self
    {
        return self::fromUri($uri);
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\Components;

use BackedEnum;
use Deprecated;
use League\Uri\Contracts\UriComponentInterface;
use League\Uri\Contracts\UriInterface;
use League\Uri\Exceptions\SyntaxError;
use League\Uri\SchemeType;
use League\Uri\StringCoercionMode;
use League\Uri\UriScheme;
use League\Uri\UriString;
use Psr\Http\Message\UriInterface as Psr7UriInterface;
use Stringable;
use Throwable;
use Uri\Rfc3986\Uri as Rfc3986Uri;
use Uri\WhatWg\Url as WhatWgUrl;

use function count;
use function in_array;
use function preg_match;
use function sprintf;
use function strtolower;

final class Scheme extends Component
{
    private const REGEXP_SCHEME = ',^[a-z]([-a-z0-9+.]+)?$,i';

    private readonly ?string $scheme;
    private readonly ?UriScheme $uriScheme;

    private function __construct(BackedEnum|Stringable|string|null $scheme)
    {
        $this->scheme = $this->validate($scheme);
        $this->uriScheme = UriScheme::tryFrom((string) $this->scheme);
    }

    public function isWebsocket(): bool
    {
        return in_array($this->scheme, ['ws', 'wss'], true);
    }

    public function isHttp(): bool
    {
        return in_array($this->scheme, ['http', 'https'], true);
    }

    public function isSsl(): bool
    {
        return in_array($this->scheme, ['https', 'wss'], true);
    }

    public function isSpecial(): bool
    {
        return $this->isWhatWgSpecial() || in_array($this->scheme, ['data', 'file'], true);
    }

    public function isWhatWgSpecial(): bool
    {
        return $this->uriScheme?->isWhatWgSpecial() ?? false;
    }

    public function defaultPort(): Port
    {
        return Port::new($this->uriScheme?->port());
    }

    public function hasDefaultPort(): bool
    {
        static $emptyPort = null;
        $emptyPort ??= Port::new();

        return !$emptyPort->equals($this->defaultPort());
    }

    public function type(): SchemeType
    {
        return $this->uriScheme?->type() ?? SchemeType::Unknown;
    }

    /**
     * Validate a scheme.
     *
     * @throws SyntaxError if the scheme is invalid
     */
    private function validate(BackedEnum|Stringable|string|null $scheme): ?string
    {
        $scheme = self::filterComponent($scheme);
        if (null === $scheme) {
            return null;
        }

        $fScheme = strtolower($scheme);

        /** @var array<string> $inMemoryCache */
        static $inMemoryCache = [];
        if (isset($inMemoryCache[$fScheme])) {
            return $fScheme;
        }

        if (1 !== preg_match(self::REGEXP_SCHEME, $fScheme)) {
            throw new SyntaxError(sprintf("The scheme '%s' is invalid.", $scheme));
        }

        if (100 < count($inMemoryCache)) {
            unset($inMemoryCache[array_key_first($inMemoryCache)]);
        }
        $inMemoryCache[$fScheme] = 1;

        return $fScheme;
    }

    public static function new(BackedEnum|Stringable|string|null $value = null): self
    {
        return new self($value);
    }

    /**
     * Create a new instance from a string.or a stringable structure or returns null on failure.
     */
    public static function tryNew(BackedEnum|Stringable|string|null $uri = null): ?self
    {
        try {
            return self::new($uri);
        } catch (Throwable) {
            return null;
        }
    }

    /**
     * Create a new instance from a URI object.
     */
    public static function fromUri(WhatWgUrl|Rfc3986Uri|BackedEnum|Stringable|string $uri): self
    {
        $uri = self::filterUri($uri);

        return new self(
            $uri instanceof Psr7UriInterface
                ? UriString::parse($uri)['scheme']
                : $uri->getScheme()
        );
    }

    public function value(): ?string
    {
        return $this->scheme;
    }

    public function getUriComponent(): string
    {
        return $this->value().(null === $this->scheme ? '' : ':');
    }

    public function equals(mixed $value): bool
    {
        if (!StringCoercionMode::Native->isCoercible($value)) {
            return false;
        }

        if (!$value instanceof UriComponentInterface) {
            $value = self::tryNew(StringCoercionMode::Native->coerce($value));
            if (null === $value) {
                return false;
            }
        }

        return $value->getUriComponent() === $this->getUriComponent();
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @see Scheme::new()
     *
     * @codeCoverageIgnore
     */
    #[Deprecated(message:'use League\Uri\Components\Scheme::new() instead', since:'league/uri-components:7.0.0')]
    public static function createFromString(Stringable|string $scheme): self
    {
        return self::new($scheme);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @see Scheme::fromUri()
     *
     * @codeCoverageIgnore
     *
     * Create a new instance from a URI object.
     */
    #[Deprecated(message:'use League\Uri\Components\Scheme::fromUri() instead', since:'league/uri-components:7.0.0')]
    public static function createFromUri(Psr7UriInterface|UriInterface $uri): self
    {
        return self::fromUri($uri);
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\Components\FragmentDirectives;

use BackedEnum;
use League\Uri\Contracts\FragmentDirective;
use League\Uri\Exceptions\SyntaxError;
use League\Uri\StringCoercionMode;
use Stringable;
use Throwable;

use function explode;
use function preg_match;

final class TextDirective implements FragmentDirective
{
    private const NAME = 'text';

    private const REGEXP_PATTERN = '/^
        (?:(?<prefix>.+?)-,)?    # optional prefix up to first "-,"
        (?<start>[^,]+?)         # required start (up to "," or end)
        (?:,(?<end>[^,-]*),?)?   # optional end, stop before ",-" if present
        (?:,-(?<suffix>.+))?     # optional suffix (to end)
    $/x';

    /**
     * @param non-empty-string $start
     * @param ?non-empty-string $end
     * @param ?non-empty-string $prefix
     * @param ?non-empty-string $suffix
     */
    public function __construct(
        public readonly string $start,
        public readonly ?string $end = null,
        public readonly ?string $prefix = null,
        public readonly ?string $suffix = null,
    ) {
        ('' !== $this->start && '' !== $this->end && '' !== $this->prefix && '' !== $this->suffix)
        || throw new SyntaxError('The start part can not be the empty string.');
    }

    /**
     * Create a new instance from a string without the Directive delimiter (:~:) or a separator (&).
     */
    public static function fromString(BackedEnum|Stringable|string $value): self
    {
        if ($value instanceof BackedEnum) {
            $value = (string) $value->value;
        }

        [$name, $value] = explode('=', (string) $value, 2) + [1 => ''];
        self::NAME === $name || throw new SyntaxError('The submitted text is not a text directive.');

        return self::fromValue($value);
    }

    /**
     * Create a new instance from a string without the Directive name and the separator (=).
     */
    public static function fromValue(BackedEnum|Stringable|string $text): self
    {
        if ($text instanceof BackedEnum) {
            $text = (string) $text->value;
        }

        '' !== $text || throw new SyntaxError('The text directive value can not be the empty string.');
        1 === preg_match(self::REGEXP_PATTERN, (string) $text, $matches) || throw new SyntaxError('The text directive is malformed.');
        if ('' === $matches['prefix']) {
            $matches['prefix'] = null;
        }

        /** @var non-empty-string $start */
        $start = (string) Encoder::decode($matches['start']);
        /** @var ?non-empty-string $prefix */
        $prefix = Encoder::decode($matches['prefix']);
        /** @var ?non-empty-string $suffix */
        $suffix = Encoder::decode($matches['suffix'] ?? null);
        $matches['end'] ??= null;
        if ('' === $matches['end']) {
            $matches['end'] = null;
        }
        /** @var ?non-empty-string $end */
        $end = Encoder::decode($matches['end']);

        return new self($start, $end, $prefix, $suffix);
    }

    public function name(): string
    {
        return self::NAME;
    }

    public function value(): string
    {
        $str = $this->start;
        if (null !== $this->prefix) {
            $str = $this->prefix.'-,'.$str;
        }

        if (null !== $this->end) {
            $str .= ','.$this->end;
        }

        if (null !== $this->suffix) {
            $str .= ',-'.$this->suffix;
        }

        return $str;
    }

    public function toString(): string
    {
        $encodedValue = (string) Encoder::encode($this->start);
        $prefix = Encoder::encode($this->prefix);
        if (null !== $prefix) {
            $encodedValue = $prefix.'-,'.$encodedValue;
        }

        $end = Encoder::encode($this->end);
        if (null !== $end) {
            $encodedValue .= ','.$end;
        }

        $suffix = Encoder::encode($this->suffix);
        if (null !== $suffix) {
            $encodedValue .= ',-'.$suffix;
        }

        return self::NAME.'='.$encodedValue;
    }

    public function __toString(): string
    {
        return $this->toString();
    }

    public function toFragmentValue(): string
    {
        return ':~:'.$this->toString();
    }

    public function equals(mixed $directive): bool
    {
        if (null === $directive || ! StringCoercionMode::Native->isCoercible($directive)) {
            return false;
        }

        if (!$directive instanceof FragmentDirective) {
            try {
                $directive = self::fromString((string) StringCoercionMode::Native->coerce($directive));
            } catch (Throwable) {
                return false;
            }
        }

        return $directive->toString() === $this->toString();
    }

    /**
     * Returns a new instance with a new start portion.
     *
     * The submitted string must be in its decoded form
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the new start portion.
     *
     * @param BackedEnum|Stringable|non-empty-string $text
     */
    public function startsWith(BackedEnum|Stringable|string $text): self
    {
        if ($text instanceof BackedEnum) {
            $text = $text->value;
        }

        $text = (string) $text;
        if ($this->start === $text) {
            return $this;
        }

        '' !== $text || throw new SyntaxError('The start part can not be the empty string.');

        return new self($text, $this->end, $this->prefix, $this->suffix);
    }

    /**
     * Returns a new instance with a new end portion.
     *
     * The submitted string must be in its decoded form
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the new end portion.
     *
     * @param BackedEnum|Stringable|non-empty-string|null $text
     */
    public function endsWith(BackedEnum|Stringable|string|null $text): self
    {
        if ($text instanceof BackedEnum) {
            $text = $text->value;
        }

        $text = (string) $text;
        if ($this->end === $text) {
            return $this;
        }

        '' !== $text || throw new SyntaxError('The end part can not be the empty string.');

        return new self($this->start, $text, $this->prefix, $this->suffix);
    }

    /**
     * Returns a new instance with a new suffix portion.
     *
     * The submitted string must be in its decoded form
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the new suffix portion.
     *
     * @param BackedEnum|Stringable|non-empty-string|null $text
     */
    public function followedBy(BackedEnum|Stringable|string|null $text): self
    {
        if ($text instanceof BackedEnum) {
            $text = $text->value;
        }

        $text = (string) $text;
        if ($this->suffix === $text) {
            return $this;
        }

        '' !== $text || throw new SyntaxError('The suffix part can not be the empty string.');

        return new self($this->start, $this->end, $this->prefix, $text);
    }

    /**
     * Returns a new instance with a new prefix portion.
     *
     *  This method MUST retain the state of the current instance, and return
     *  an instance that contains the new prefix portion.
     */
    public function precededBy(BackedEnum|Stringable|string|null $text): self
    {
        if ($text instanceof BackedEnum) {
            $text = $text->value;
        }

        $text = (string) $text;
        if ($this->prefix === $text) {
            return $this;
        }

        '' !== $text || throw new SyntaxError('The prefix part can not be the empty string.');

        return new self($this->start, $this->end, $text, $this->suffix);
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\Components\FragmentDirectives;

use BackedEnum;
use League\Uri\Contracts\FragmentDirective;
use Stringable;

use function preg_match;

final class DirectiveString
{
    /**
     * Parse a Directive string representation.
     *
     * A Directive syntax is name[=value] where the
     * separator `=` is not present when no value
     * is attached to it
     */
    public static function resolve(BackedEnum|Stringable|string $directive): FragmentDirective
    {
        if ($directive instanceof BackedEnum) {
            $directive = $directive->value;
        }

        $directive = (string) $directive;

        return match (true) {
            1 === preg_match('/^text(?:=|$)/i', $directive) => TextDirective::fromString($directive),
            default => GenericDirective::fromString($directive),
        };
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\Components\FragmentDirectives;

use BackedEnum;
use League\Uri\Exceptions\SyntaxError;
use Stringable;

use function preg_match;
use function preg_replace_callback;
use function rawurldecode;
use function rawurlencode;
use function str_replace;

final class Encoder
{
    private const REGEXP_CHARS_INVALID = ',[\x00-\x1f\x7f],';
    private const REGEXP_CHARS_NOT_UNRESERVED = '/[^A-Za-z0-9_\-.~]/';
    private const REGEXP_CHARS_ENCODED = ',%[A-Fa-f0-9]{2},';
    private const REGEXP_CHARS_TO_ENCODE = '/[^A-Za-z0-9_\-.~!$&\'()*+,;=%:@\/?]+|%(?![A-Fa-f0-9]{2})/';

    private function __construct()
    {
    }

    public static function encode(BackedEnum|Stringable|string|null $value): ?string
    {
        $value = self::normalizeInput($value);
        if (null === $value || '' === $value) {
            return $value;
        }

        $encoder = static fn (array $matches): string => 1 === preg_match(self::REGEXP_CHARS_NOT_UNRESERVED, rawurldecode($matches[0]))
            ? rawurlencode($matches[0])
            : $matches[0];

        $encoded = (string) preg_replace_callback(self::REGEXP_CHARS_TO_ENCODE, $encoder, $value);

        return strtr($encoded, ['-' => '%2D', ',' => '%2C', '&' => '%26']);
    }

    /**
     * @throws SyntaxError if the value contains invalid encoded characters
     */
    public static function decode(BackedEnum|Stringable|string|null $value): ?string
    {
        $value = self::normalizeInput($value);
        if (null === $value || '' === $value) {
            return $value;
        }

        1 !== preg_match(self::REGEXP_CHARS_INVALID, $value) || throw new SyntaxError('The value contains invalid encoded characters; "'.$value.'".');

        $decoder = static fn (array $matches): string => match ($matches[0]) {
            '%20', '%2F' => $matches[0],
            default => rawurldecode($matches[0]),
        };

        $decoded = (string) preg_replace_callback(self::REGEXP_CHARS_ENCODED, $decoder, $value);

        return str_replace('%20', ' ', $decoded);
    }

    private static function normalizeInput(BackedEnum|Stringable|string|null $value): ?string
    {
        return match (true) {
            null === $value => null,
            $value instanceof BackedEnum => (string) $value->value,
            default => (string) $value,
        };
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\Components\FragmentDirectives;

use BackedEnum;
use League\Uri\Contracts\FragmentDirective;
use League\Uri\Exceptions\SyntaxError;
use League\Uri\StringCoercionMode;
use Stringable;
use Throwable;

use function explode;

final class GenericDirective implements FragmentDirective
{
    /**
     * @param non-empty-string $name
     */
    private function __construct(
        private readonly string $name,
        private readonly ?string $value = null,
    ) {
    }

    /**
     * Create a new instance from a string without the Directive delimiter (:~:) or a separator (&).
     */
    public static function fromString(BackedEnum|Stringable|string $value): self
    {
        if ($value instanceof BackedEnum) {
            $value = (string) $value->value;
        }

        [$name, $value] = explode('=', (string) $value, 2) + [1 => null];
        (null !== $name && '' !== $name && !str_contains($name, '&')) || throw new SyntaxError('The submitted text is not a valid directive.');

        return new self($name, $value);
    }

    public function name(): string
    {
        /** @var non-empty-string $name */
        $name = (string) Encoder::decode($this->name);

        return $name;
    }

    public function value(): ?string
    {
        return Encoder::decode($this->value);
    }

    public function toString(): string
    {
        $str = $this->name;
        if (null === $this->value) {
            return $str;
        }

        return $str.'='.$this->value;
    }

    public function __toString(): string
    {
        return $this->toString();
    }

    public function toFragmentValue(): string
    {
        return ':~:'.$this->toString();
    }

    public function equals(mixed $directive): bool
    {
        if (null === $directive || ! StringCoercionMode::Native->isCoercible($directive)) {
            return false;
        }

        if (!$directive instanceof FragmentDirective) {
            try {
                $directive = self::fromString((string) StringCoercionMode::Native->coerce($directive));
            } catch (Throwable) {
                return false;
            }
        }

        return $directive->toString() === $this->toString();
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\Components;

use BackedEnum;
use Deprecated;
use League\Uri\Contracts\AuthorityInterface;
use League\Uri\Contracts\UriComponentInterface;
use League\Uri\Contracts\UriException;
use League\Uri\Contracts\UriInterface;
use League\Uri\Contracts\UserInfoInterface;
use League\Uri\Encoder;
use League\Uri\StringCoercionMode;
use League\Uri\UriString;
use Psr\Http\Message\UriInterface as Psr7UriInterface;
use SensitiveParameter;
use Stringable;
use Uri\Rfc3986\Uri as Rfc3986Uri;
use Uri\WhatWg\Url as WhatWgUrl;

use function explode;

final class UserInfo extends Component implements UserInfoInterface
{
    private readonly ?string $username;
    private readonly ?string $password;

    /**
     * New instance.
     */
    public function __construct(
        BackedEnum|Stringable|string|null $username,
        #[SensitiveParameter] BackedEnum|Stringable|string|null $password = null,
    ) {
        $this->username = $this->validateComponent($username);
        $this->password = $this->validateComponent($password);
    }

    /**
     * Create a new instance from a URI object.
     */
    public static function fromUri(Rfc3986Uri|WhatWgUrl|BackedEnum|Stringable|string $uri): self
    {
        $uri = self::filterUri($uri);
        if ($uri instanceof Rfc3986Uri) {
            return new self($uri->getRawUsername(), $uri->getRawPassword());
        }

        if ($uri instanceof WhatWgUrl || $uri instanceof UriInterface) {
            return new self($uri->getUsername(), $uri->getPassword());
        }

        $components = UriString::parse($uri);

        return new self($components['user'], $components['pass']);
    }

    /**
     * Create a new instance from an Authority object.
     */
    public static function fromAuthority(BackedEnum|Stringable|string|null $authority): self
    {
        return match (true) {
            $authority instanceof AuthorityInterface => self::new($authority->getUserInfo()),
            default => self::new(Authority::new($authority)->getUserInfo()),
        };
    }

    /**
     * Create a new instance from a hash of parse_url parts.
     *
     * Create a new instance from a hash representation of the URI similar
     * to PHP parse_url function result
     *
     * @param array{user? : ?string, pass? : ?string} $components
     */
    public static function fromComponents(array $components): self
    {
        $components += ['user' => null, 'pass' => null];

        return match (null) {
            $components['user'] => new self(null),
            default => new self($components['user'], $components['pass']),
        };
    }

    /**
     * Creates a new instance from an encoded string.
     */
    public static function new(BackedEnum|Stringable|string|null $value = null): self
    {
        $value = StringCoercionMode::Native->coerce($value);
        if (null === $value) {
            return new self(null);
        }

        [$user, $pass] = explode(':', $value, 2) + [1 => null];

        return new self(Encoder::decodeAll($user), Encoder::decodeAll($pass));
    }

    /**
     * Create a new instance from a string or a stringable structure or returns null on failure.
     */
    public static function tryNew(BackedEnum|Stringable|string|null $uri = null): ?self
    {
        try {
            return self::new($uri);
        } catch (UriException) {
            return null;
        }
    }

    public function value(): ?string
    {
        return match (true) {
            null === $this->password => $this->getUsername(),
            default => $this->getUsername().':'.$this->getPassword(),
        };
    }

    public function equals(mixed $value): bool
    {
        if (!StringCoercionMode::Native->isCoercible($value)) {
            return false;
        }

        if (!$value instanceof UriComponentInterface) {
            $value = self::tryNew(StringCoercionMode::Native->coerce($value));
            if (null === $value) {
                return false;
            }
        }

        return $value->getUriComponent() === $this->getUriComponent();
    }

    public function getUriComponent(): string
    {
        return $this->value().(null === $this->username ? '' : '@');
    }

    public function getUser(): ?string
    {
        return $this->username;
    }

    public function getPass(): ?string
    {
        return $this->password;
    }

    public function getUsername(): ?string
    {
        return Encoder::encodeUser($this->username);
    }

    public function getPassword(): ?string
    {
        return Encoder::encodePassword($this->password);
    }

    /**
     * @return array{user: ?string, pass: ?string}
     */
    public function components(): array
    {
        return [
            'user' => $this->username,
            'pass' => $this->password,
        ];
    }

    public function withUser(BackedEnum|Stringable|string|null $username): self
    {
        $username = $this->validateComponent($username);
        if ($this->username === $username) {
            return $this;
        }

        return new self($username, $this->password);
    }

    public function withPass(#[SensitiveParameter] BackedEnum|Stringable|string|null $password): self
    {
        $password = $this->validateComponent($password);
        if ($password === $this->password) {
            return $this;
        }

        return new self($this->username, $password);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @see UserInfo::fromUri()
     *
     * @codeCoverageIgnore
     *
     * Create a new instance from a URI object.
     */
    #[Deprecated(message:'use League\Uri\Components\UserInfo::fromUri() instead', since:'league/uri-components:7.0.0')]
    public static function createFromUri(Rfc3986Uri|WhatWgUrl|Psr7UriInterface|UriInterface $uri): self
    {
        return self::fromUri($uri);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @see UserInfo::fromAuthority()
     *
     * @codeCoverageIgnore
     *
     * Create a new instance from an Authority object.
     */
    #[Deprecated(message:'use League\Uri\Components\UserInfo::fromAuthority() instead', since:'league/uri-components:7.0.0')]
    public static function createFromAuthority(AuthorityInterface|Stringable|string $authority): self
    {
        return self::fromAuthority($authority);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @see UserInfo::new()
     *
     * @codeCoverageIgnore
     *
     * Creates a new instance from an encoded string.
     */
    #[Deprecated(message:'use League\Uri\Components\UserInfo::new() instead', since:'league/uri-components:7.0.0')]
    public static function createFromString(Stringable|string $userInfo): self
    {
        return self::new($userInfo);
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\Components;

use BackedEnum;
use Deprecated;
use League\Uri\Contracts\AuthorityInterface;
use League\Uri\Contracts\DomainHostInterface;
use League\Uri\Contracts\HostInterface;
use League\Uri\Contracts\UriComponentInterface;
use League\Uri\Contracts\UriException;
use League\Uri\Contracts\UriInterface;
use League\Uri\Exceptions\OffsetOutOfBounds;
use League\Uri\Exceptions\SyntaxError;
use Psr\Http\Message\UriInterface as Psr7UriInterface;
use Stringable;
use Traversable;
use TypeError;
use Uri\Rfc3986\Uri as Rfc3986Uri;
use Uri\WhatWg\Url as WhatWgUrl;

use function array_count_values;
use function array_filter;
use function array_keys;
use function array_reverse;
use function array_shift;
use function count;
use function explode;
use function implode;
use function sprintf;
use function str_ends_with;

final class Domain extends Component implements DomainHostInterface
{
    private const SEPARATOR = '.';

    private readonly HostInterface $host;
    /** @var string[] */
    private readonly array $labels;

    private function __construct(BackedEnum|Stringable|string|null $host)
    {
        $host = match (true) {
            $host instanceof HostInterface => $host,
            $host instanceof UriComponentInterface => Host::new($host->value()),
            default => Host::new($host),
        };

        if (!$host->isDomain()) {
            throw new SyntaxError(sprintf('`%s` is an invalid domain name.', $host->value() ?? 'null'));
        }

        $this->host = $host;
        $this->labels = array_reverse(explode(self::SEPARATOR, $this->host->value() ?? ''));
    }

    /**
     * Returns a new instance from a string or a stringable object.
     */
    public static function new(BackedEnum|Stringable|string|null $value = null): self
    {
        return new self($value);
    }

    /**
     * Create a new instance from a string.or a stringable structure or returns null on failure.
     */
    public static function tryNew(BackedEnum|Stringable|string|null $uri = null): ?self
    {
        try {
            return self::new($uri);
        } catch (UriException) {
            return null;
        }
    }

    /**
     * Returns a new instance from an iterable structure.
     */
    public static function fromLabels(BackedEnum|Stringable|string ...$labels): self
    {
        return new self(match ([]) {
            $labels => null,
            default => implode(self::SEPARATOR, array_reverse(array_map(
                fn ($label) => self::filterComponent($label),
                $labels
            ))),
        });
    }

    /**
     * Create a new instance from a URI object.
     */
    public static function fromUri(WhatWgUrl|Rfc3986Uri|BackedEnum|Stringable|string $uri): self
    {
        return self::new(Host::fromUri($uri));
    }

    /**
     * Create a new instance from an Authority object.
     */
    public static function fromAuthority(BackedEnum|Stringable|string $authority): self
    {
        return self::new(Host::fromAuthority($authority));
    }

    public function value(): ?string
    {
        return $this->host->value();
    }

    public function equals(mixed $value): bool
    {
        return $this->host->equals($value);
    }

    public function toAscii(): ?string
    {
        return $this->host->toAscii();
    }

    public function toUnicode(): ?string
    {
        return $this->host->toUnicode();
    }

    public function encoded(): ?string
    {
        return $this->host->encoded();
    }

    public function isIp(): bool
    {
        return false;
    }

    public function isDomain(): bool
    {
        return true;
    }

    public function isRegisteredName(): bool
    {
        return true;
    }

    public function getIpVersion(): ?string
    {
        return null;
    }

    public function getIp(): ?string
    {
        return null;
    }

    public function count(): int
    {
        return count($this->labels);
    }

    /**
     * @return Traversable<int, string>
     */
    public function getIterator(): Traversable
    {
        yield from $this->labels;
    }

    public function first(): ?string
    {
        return $this->get(0);
    }

    public function last(): ?string
    {
        return $this->get(-1);
    }

    public function indexOf(BackedEnum|Stringable|string $label): ?int
    {
        return $this->keys($label)[0] ?? null;
    }

    public function lastIndexOf(BackedEnum|Stringable|string $label): ?int
    {
        $res = $this->keys($label);

        return $res[count($res) - 1] ?? null;
    }

    public function contains(BackedEnum|Stringable|string $label): bool
    {
        return [] !== $this->keys($label);
    }

    public function isEmpty(): bool
    {
        return null === $this->host->value();
    }

    public function get(int $offset): ?string
    {
        if ($offset < 0) {
            $offset += count($this->labels);
        }

        return $this->labels[$offset] ?? null;
    }

    public function keys(BackedEnum|Stringable|string|null $label = null): array
    {
        if ($label instanceof BackedEnum) {
            $label = (string) $label->value;
        }

        return match (null) {
            $label => array_keys($this->labels),
            default => array_keys($this->labels, $label, true),
        };
    }

    public function isAbsolute(): bool
    {
        return count($this->labels) > 1 && '' === $this->labels[array_key_first($this->labels)];
    }

    public function isSubdomainOf(BackedEnum|Stringable|string|null $parentHost): bool
    {
        if ($this->isEmpty()) {
            return false;
        }

        if (!$parentHost instanceof self) {
            $parentHost = self::tryNew($parentHost);
        }

        return null !== $parentHost
            && !$parentHost->isEmpty()
            && count($this) > count($parentHost)
            && str_ends_with(''.$this->withoutRootLabel()->toAscii(), '.'.$parentHost->withoutRootLabel()->toAscii());
    }

    public function hasSubdomain(BackedEnum|Stringable|string|null $childHost): bool
    {
        if (!$childHost instanceof self) {
            $childHost = self::tryNew($childHost);
        }

        return ($childHost?->isSubdomainOf($this)) ?? false;
    }

    public function isSiblingOf(BackedEnum|Stringable|string|null $siblingHost): bool
    {
        if (!$siblingHost instanceof self) {
            $siblingHost = self::tryNew($siblingHost);
        }

        return null !== $siblingHost
            && !$this->isEmpty()
            && !$siblingHost->isEmpty()
            && !$this->equals($siblingHost)
            && $this->parentHost()->equals($siblingHost->parentHost());
    }

    public function parentHost(): DomainHostInterface
    {
        return $this->withoutRootLabel()->slice(0, -1);
    }

    public function commonAncestorWith(BackedEnum|Stringable|string|null $other): DomainHostInterface
    {
        if (!$other instanceof self) {
            $other = self::tryNew($other);
        }

        if (null === $other) {
            return Domain::new();
        }

        $other = $other->withoutRootLabel();
        $current = $this->withoutRootLabel();
        $labels = [];
        /** @var int $offset */
        foreach ($current as $offset => $label) {
            if ($label !== $other->get($offset)) {
                break;
            }

            $labels[] = $label;
        }

        return Domain::fromLabels(...$labels);
    }

    public function prepend(BackedEnum|Stringable|string|int|null $label): DomainHostInterface
    {
        $label = self::filterComponent($label);
        $value = $this->value();

        return match (true) {
            null === $label => $this,
            null === $value => new self($label),
            str_ends_with($label, self::SEPARATOR) => new self($label.$value),
            default => new self($label.self::SEPARATOR.$value),
        };
    }

    public function append(BackedEnum|Stringable|string|int|null $label): DomainHostInterface
    {
        $label = self::filterComponent($label);
        $value = $this->value();

        return match (true) {
            null === $label => $this,
            null === $value => new self($label),
            !$this->isAbsolute() => new self($value.self::SEPARATOR.$label),
            str_ends_with($label, self::SEPARATOR) => new self($value.$label),
            default => new self($value.$label.self::SEPARATOR),
        };
    }

    public function withRootLabel(): DomainHostInterface
    {
        $key = array_key_first($this->labels);

        return match ($this->labels[$key]) {
            '' => $this,
            default => $this->append(''),
        };
    }

    public function slice(int $offset, ?int $length = null): self
    {
        $nbLabels = count($this->labels);
        if ($offset < -$nbLabels || $offset > $nbLabels) {
            throw new OffsetOutOfBounds(sprintf('No label can be found with at : `%s`.', $offset));
        }

        $labels = array_slice($this->labels, $offset, $length, true);

        return match ($labels) {
            $this->labels => $this,
            default => self::fromLabels(...$labels),
        };
    }

    public function withoutRootLabel(): DomainHostInterface
    {
        $key = array_key_first($this->labels);
        if ('' !== $this->labels[$key]) {
            return $this;
        }

        $labels = $this->labels;
        array_shift($labels);

        return self::fromLabels(...$labels);
    }

    /**
     * @throws OffsetOutOfBounds
     */
    public function withLabel(int $key, BackedEnum|Stringable|string|int|null $label): DomainHostInterface
    {
        $nbLabels = count($this->labels);
        if ($key < - $nbLabels - 1 || $key > $nbLabels) {
            throw new OffsetOutOfBounds(sprintf('No label can be added with the submitted key : `%s`.', $key));
        }

        if (0 > $key) {
            $key += $nbLabels;
        }

        if ($nbLabels === $key) {
            return $this->append($label);
        }

        if (-1 === $key) {
            return $this->prepend($label);
        }

        if (!$label instanceof HostInterface && null !== $label) {
            if (is_int($label)) {
                $label = (string) $label;
            }

            $label = Host::new($label)->value();
        }

        if ($label === ($this->labels[$key] ?? null)) {
            return $this;
        }

        $labels = $this->labels;
        $labels[$key] = $label;

        return new self(implode(self::SEPARATOR, array_reverse($labels)));
    }

    public function withoutLabel(int ...$keys): DomainHostInterface
    {
        if ([] === $keys) {
            return $this;
        }

        $nb_labels = count($this->labels);
        foreach ($keys as &$offset) {
            if (- $nb_labels > $offset || $nb_labels - 1 < $offset) {
                throw new OffsetOutOfBounds(sprintf('No label can be removed with the submitted key : `%s`.', $offset));
            }

            if (0 > $offset) {
                $offset += $nb_labels;
            }
        }
        unset($offset);

        $deleted_keys = array_keys(array_count_values($keys));
        $filter = static fn ($key): bool => !in_array($key, $deleted_keys, true);

        return self::fromLabels(...array_filter($this->labels, $filter, ARRAY_FILTER_USE_KEY));
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @see Domain::getIterator()
     *
     * @codeCoverageIgnore
     *
     * Returns a new instance from a string or a stringable object.
     */
    #[Deprecated(message:'use League\Uri\Components\Domain::getIterator() instead', since:'league/uri-components:7.0.0')]
    public function labels(): array
    {
        return $this->labels;
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @see Domain::new()
     *
     * @codeCoverageIgnore
     *
     * Returns a new instance from a string or a stringable object.
     */
    #[Deprecated(message:'use League\Uri\Components\Domain::new() instead', since:'league/uri-components:7.0.0')]
    public static function createFromString(Stringable|string $host): self
    {
        return self::new($host);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @see Domain::fromLabels()
     *
     * @codeCoverageIgnore
     *
     * Returns a new instance from an iterable structure.
     *
     * @throws TypeError If a label is the null value
     */
    #[Deprecated(message:'use League\Uri\Components\Domain::fromLabels() instead', since:'league/uri-components:7.0.0')]
    public static function createFromLabels(iterable $labels): self
    {
        return self::fromLabels(...$labels);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @see Domain::fromUri()
     *
     * @codeCoverageIgnore
     *
     * Create a new instance from a URI object.
     */
    #[Deprecated(message:'use League\Uri\Components\Domain::fromUri() instead', since:'league/uri-components:7.0.0')]
    public static function createFromUri(Psr7UriInterface|UriInterface $uri): self
    {
        return self::fromUri($uri);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @see Domain::fromAuthority()
     *
     * @codeCoverageIgnore
     *
     * Create a new instance from an Authority object.
     */
    #[Deprecated(message:'use League\Uri\Components\Domain::fromAuthority() instead', since:'league/uri-components:7.0.0')]
    public static function createFromAuthority(AuthorityInterface|Stringable|string $authority): self
    {
        return self::fromAuthority($authority);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @see Domain::new()
     *
     * @codeCoverageIgnore
     *
     * Returns a new instance from an iterable structure.
     */
    #[Deprecated(message:'use League\Uri\Components\Domain::new() instead', since:'league/uri-components:7.0.0')]
    public static function createFromHost(HostInterface $host): self
    {
        return self::new($host);
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\Components;

use BackedEnum;
use DateTimeImmutable;
use DateTimeInterface;
use DateTimeZone;
use Deprecated;
use Exception;
use Iterator;
use League\Uri\Contracts\QueryInterface;
use League\Uri\Contracts\UriComponentInterface;
use League\Uri\Contracts\UriException;
use League\Uri\Contracts\UriInterface;
use League\Uri\Encoder;
use League\Uri\Exceptions\OffsetOutOfBounds;
use League\Uri\Exceptions\SyntaxError;
use League\Uri\KeyValuePair\Converter;
use League\Uri\Occurrence;
use League\Uri\QueryComposeMode;
use League\Uri\QueryExtractMode;
use League\Uri\QueryString;
use League\Uri\StringCoercionMode;
use League\Uri\TypeConverter;
use League\Uri\UriString;
use OutOfBoundsException;
use Psr\Http\Message\UriInterface as Psr7UriInterface;
use Stringable;
use Traversable;
use TypeError;
use UnitEnum;
use Uri\Rfc3986\Uri as Rfc3986Uri;
use Uri\WhatWg\Url as WhatWgUrl;
use ValueError;

use function array_column;
use function array_filter;
use function array_flip;
use function array_intersect;
use function array_is_list;
use function array_map;
use function array_merge;
use function array_reduce;
use function count;
use function get_object_vars;
use function http_build_query;
use function implode;
use function in_array;
use function is_array;
use function is_int;
use function is_object;
use function iterator_to_array;
use function preg_match;
use function preg_quote;
use function preg_replace;
use function preg_split;
use function strcmp;
use function uksort;

use const ARRAY_FILTER_USE_BOTH;
use const PREG_SPLIT_NO_EMPTY;

final class Query extends Component implements QueryInterface
{
    private const REGEXP_NON_ASCII_PATTERN = '/[^\x20-\x7f]/';
    private const REGXP_FILTER_LIST = '/^
        [^\[\]]+        # base key (no [ or ])
        (?:\[[^\]]*\])+ # one or more bracket groups
    $/x';
    /** @var array<int, array{0:string, 1:string|null}> */
    private readonly array $pairs;
    /** @var non-empty-string */
    private readonly string $separator;
    private readonly array $parameters;
    private readonly array $list;

    /**
     * Returns a new instance.
     *
     * @throws SyntaxError
     */
    private function __construct(BackedEnum|Stringable|string|null $query, ?Converter $converter = null)
    {
        $converter ??= Converter::fromRFC3986();
        $this->pairs = QueryString::parseFromValue($query, $converter);
        $this->separator = $converter->separator();
        $this->parameters = QueryString::extractFromValue($query, $converter);
        $this->list = QueryString::convert(
            array_filter($this->pairs, static fn (array $pair): bool => 1 === preg_match(self::REGXP_FILTER_LIST, $pair[0])),
            QueryExtractMode::LossLess,
        );
    }

    /**
     * @throws SyntaxError
     */
    public static function new(BackedEnum|Stringable|string|null $value = null): self
    {
        return self::fromRFC3986($value);
    }

    /**
     * Create a new instance from a string.or a stringable structure or returns null on failure.
     */
    public static function tryNew(BackedEnum|Stringable|string|null $uri = null): ?self
    {
        try {
            return self::new($uri);
        } catch (UriException) {
            return null;
        }
    }

    /**
     * Returns a new instance from the input of http_build_query.
     *
     * @param non-empty-string $separator
     */
    public static function fromVariable(
        object|array $parameters,
        string $separator = '&',
        string $prefix = '',
        QueryComposeMode $composeMode = QueryComposeMode::Native
    ): self {
        if ($parameters instanceof UnitEnum && QueryComposeMode::Compatible !== $composeMode) {
            throw new TypeError('Enum can not be used as arguments.');
        }

        $params = is_object($parameters) ? get_object_vars($parameters) : $parameters;

        $data = [];
        foreach ($params as $name => $value) {
            $data[$prefix.$name] = $value;
        }

        return new self(
            QueryString::compose(data: $data, separator: $separator, composeMode: $composeMode),
            Converter::fromRFC1738($separator)
        );
    }

    /**
     * Returns a new instance from the result of QueryString::parse.
     *
     * @param iterable<int, array{0:string, 1:string|null}> $pairs
     * @param non-empty-string $separator
     */
    public static function fromPairs(iterable $pairs, string $separator = '&', string $prefix = '', StringCoercionMode $coercionMode = StringCoercionMode::Native): self
    {
        $data = [];
        foreach ($pairs as $pair) {
            if (!is_array($pair) || !array_is_list($pair) || 2 !== count($pair)) {
                throw new SyntaxError('A pair must be a sequential array starting at `0` and containing two elements.');
            }

            $data[] = [$prefix.$pair[0], $pair[1]];
        }

        $converter = Converter::fromRFC3986($separator);

        return new self(QueryString::buildFromPairs($data, $converter, $coercionMode), $converter);
    }

    /**
     * Create a new instance from a URI object.
     */
    public static function fromUri(WhatWgUrl|Rfc3986Uri|BackedEnum|Stringable|string $uri): self
    {
        return match (true) {
            $uri instanceof Rfc3986Uri => new self($uri->getRawQuery(), Converter::fromRFC3986()),
            $uri instanceof WhatWgUrl => new self($uri->getQuery(), Converter::fromFormData()),
            $uri instanceof UriInterface  => new self($uri->getQuery(), Converter::fromRFC3986()),
            $uri instanceof BackedEnum => new self($uri, Converter::fromRFC3986()),
            default => new self(UriString::parse($uri)['query'], Converter::fromRFC3986()),
        };
    }

    /**
     * Returns a new instance.
     *
     * @param non-empty-string $separator
     */
    public static function fromRFC3986(BackedEnum|Stringable|string|null $query = null, string $separator = '&'): self
    {
        return new self($query, Converter::fromRFC3986($separator));
    }

    /**
     * Returns a new instance.
     *
     * @param non-empty-string $separator
     */
    public static function fromRFC1738(BackedEnum|Stringable|string|null $query = null, string $separator = '&'): self
    {
        return new self($query, Converter::fromRFC1738($separator));
    }

    /**
     * Returns a new instance.
     *
     * @param non-empty-string $separator
     */
    public static function fromFormData(BackedEnum|Stringable|string|null $query = null, string $separator = '&'): self
    {
        return new self($query, Converter::fromFormData($separator));
    }

    public function getSeparator(): string
    {
        return $this->separator;
    }

    public function toRFC3986(): ?string
    {
        return QueryString::buildFromPairs($this->pairs, Converter::fromRFC3986($this->separator));
    }

    public function toRFC1738(): ?string
    {
        return QueryString::buildFromPairs($this->pairs, Converter::fromRFC1738($this->separator));
    }

    public function toFormData(): ?string
    {
        return QueryString::buildFromPairs($this->pairs, Converter::fromFormData($this->separator));
    }

    public function decoded(): ?string
    {
        return Converter::new($this->separator)->toValue($this);
    }

    public function normalize(): self
    {
        return self::new(Encoder::normalizeQuery($this->value()));
    }

    public function value(): ?string
    {
        return $this->toRFC3986();
    }

    public function getUriComponent(): string
    {
        return match ([]) {
            $this->pairs => '',
            default => '?'.$this->value(),
        };
    }

    public function isEmpty(): bool
    {
        return [] === $this->pairs;
    }

    public function isNotEmpty(): bool
    {
        return ! $this->isEmpty();
    }

    public function jsonSerialize(): ?string
    {
        return $this->toFormData();
    }

    public function count(): int
    {
        return count($this->pairs);
    }

    public function getIterator(): Iterator
    {
        yield from $this->pairs;
    }

    /**
     * Returns the total number of distinct keys.
     */
    public function countDistinctKeys(): int
    {
        return count(array_flip(array_column($this->pairs, 0)));
    }

    public function pairs(): iterable
    {
        foreach ($this->pairs as $pair) {
            yield $pair[0] => $pair[1];
        }
    }

    public function has(string ...$keys): bool
    {
        foreach ($keys as $key) {
            if (!isset(array_flip(array_column($this->pairs, 0))[$key])) {
                return false;
            }
        }

        return [] !== $keys;
    }

    public function hasPair(string $key, ?string $value): bool
    {
        return in_array([$key, $value], $this->pairs, true);
    }

    public function first(string $key): ?string
    {
        $offset = $this->indexOf($key);

        return null === $offset ? null : $this->valueAt($offset);
    }

    public function last(string $key): ?string
    {
        $offset = $this->indexOf($key, -1);

        return null === $offset ? null : $this->valueAt($offset);
    }

    public function get(string $key): ?string
    {
        return $this->first($key);
    }

    public function getAll(string $key): array
    {
        return array_column(array_filter($this->pairs, fn (array $pair): bool => $key === $pair[0]), 1);
    }

    private function getValue(int|string $key, Occurrence $occurrence): ?string
    {
        $key = (string) $key;

        return match ($occurrence) {
            Occurrence::First => $this->first($key),
            Occurrence::Last => $this->last($key),
        };
    }

    public function string(int|string $key, ?string $default = null, Occurrence $occurrence = Occurrence::First): ?string
    {
        return TypeConverter::toString($this->getValue($key, $occurrence)) ?? $default;
    }

    public function strings(int|string $key, ?string $default = null): array
    {
        return TypeConverter::toStrings($this->getAll((string) $key), $default);
    }

    public function integer(int|string $key, ?int $default = null, Occurrence $occurrence = Occurrence::First): ?int
    {
        return TypeConverter::toInteger($this->getValue($key, $occurrence)) ?? $default;
    }

    /**
     * @return array<int>
     */
    public function integers(int|string $key, ?int $default = null): array
    {
        return TypeConverter::toIntegers($this->getAll((string) $key), $default);
    }

    public function float(int|string $key, ?float $default = null, Occurrence $occurrence = Occurrence::First): ?float
    {
        return TypeConverter::toFloat($this->getValue($key, $occurrence)) ?? $default;
    }

    /**
     * @return array<float>
     */
    public function floats(int|string $key, ?float $default = null): array
    {
        return TypeConverter::toFloats($this->getAll((string) $key), $default);
    }

    public function boolean(int|string $key, ?bool $default = null, Occurrence $occurrence = Occurrence::First): ?bool
    {
        return TypeConverter::toBoolean($this->get((string) $key)) ?? $default;
    }

    /**
     * @return array<bool>
     */
    public function booleans(int|string $key, ?bool $default = null): array
    {
        return TypeConverter::toBooleans($this->getAll((string) $key), $default);
    }

    /**
     * @param class-string<UnitEnum> $enumClass
     */
    public function enum(int|string $key, string $enumClass, ?UnitEnum $default = null, Occurrence $occurrence = Occurrence::First): ?UnitEnum
    {
        null === $default || $default instanceof $enumClass || throw new ValueError('The default value must be an instance of '.$enumClass.'; '.get_debug_type($default).' given.');

        return TypeConverter::toEnum($this->getValue($key, $occurrence), $enumClass) ?? $default;
    }

    /**
     * @param class-string<UnitEnum> $enumClass
     *
     * @return array<UnitEnum>
     */
    public function enums(int|string $key, string $enumClass, ?UnitEnum $default = null): array
    {
        return TypeConverter::toEnums($this->getAll((string) $key), $enumClass, $default);
    }

    /**
     * @param non-empty-string $format
     *
     * @throws Exception
     */
    public function date(
        int|string $key,
        string $format,
        DateTimeZone|string $timezone = 'UTC',
        ?DateTimeInterface $default = null,
        Occurrence $occurrence = Occurrence::First
    ): ?DateTimeImmutable {
        return TypeConverter::toDateTimeImmutable($this->getValue($key, $occurrence), $format, $timezone) ?? (
            null !== $default && !$default instanceof DateTimeImmutable
                ? DateTimeImmutable::createFromInterface($default)
                : null
        );
    }

    /**
     * @param non-empty-string $format
     *
     * @throws Exception
     *
     * @return array<DateTimeImmutable>
     */
    public function dates(
        int|string $key,
        string $format,
        DateTimeZone|string $timezone = 'UTC',
        ?DateTimeInterface $default = null,
    ): array {
        return TypeConverter::toDateTimeImmutables(
            values: $this->getAll((string) $key),
            format: $format,
            timezone: $timezone,
            default: $default,
        );
    }

    public function indexOf(string $key, int $nth = 0): ?int
    {
        if ([] === $this->pairs) {
            return null;
        }

        if ($nth < 0) {
            $matchCount = 0;
            for ($offset = count($this->pairs) - 1; $offset >= 0; --$offset) {
                if ($this->pairs[$offset][0] === $key) {
                    if (++$matchCount === -$nth) {
                        return $offset;
                    }
                }
            }

            return null;
        }

        $matchCount = 0;
        foreach ($this->pairs as $offset => $pair) {
            if ($pair[0] === $key) {
                if ($nth === $matchCount) {
                    return $offset;
                }
                ++$matchCount;
            }
        }

        return null;
    }

    public function indexOfValue(?string $value, int $nth = 0): ?int
    {
        if ([] === $this->pairs) {
            return null;
        }

        if ($nth < 0) {
            $matchCount = 0;
            for ($offset = count($this->pairs) - 1; $offset >= 0; --$offset) {
                if ($this->pairs[$offset][1] === $value) {
                    if (++$matchCount === -$nth) {
                        return $offset;
                    }
                }
            }

            return null;
        }

        $matchCount = 0;
        foreach ($this->pairs as $offset => $pair) {
            if ($pair[1] === $value) {
                if ($nth === $matchCount) {
                    return $offset;
                }
                ++$matchCount;
            }
        }

        return null;
    }

    /**
     * Returns the key/value pair at the given numeric offset.
     *
     * Negative offsets are supported (counting from the end).
     *
     * @throws OutOfBoundsException If the offset is invalid
     *
     * @return array{0:string, 1:?string}
     */
    public function pair(int $offset): array
    {
        if ($offset < 0) {
            $offset += count($this->pairs);
        }

        return $this->pairs[$offset] ?? throw new OffsetOutOfBounds("Offset $offset does not exist");
    }

    /**
     * @throws OutOfBoundsException If the offset is invalid
     */
    public function valueAt(int $offset): ?string
    {
        return $this->pair($offset)[1];
    }

    /**
     * @throws OutOfBoundsException If the offset is invalid
     */
    public function keyAt(int $offset): string
    {
        return $this->pair($offset)[0];
    }

    public function getList(string $name): array
    {
        return $this->list[$name] ?? [];
    }

    public function hasList(string ...$names): bool
    {
        foreach ($names as $name) {
            if ([] === $this->getList($name)) {
                return false;
            }
        }

        return [] !== $names;
    }

    public function equals(mixed $value): bool
    {
        if (!StringCoercionMode::Native->isCoercible($value)) {
            return false;
        }

        if (!$value instanceof UriComponentInterface) {
            $value = self::tryNew(StringCoercionMode::Native->coerce($value));
            if (null === $value) {
                return false;
            }
        }

        return $value->getUriComponent() === $this->getUriComponent();
    }

    public function withSeparator(string $separator): self
    {
        return match ($separator) {
            $this->separator => $this,
            '' => throw new SyntaxError('The separator character cannot be the empty string.'),
            default => self::fromPairs($this->pairs, $separator),
        };
    }

    public function sort(): self
    {
        $codepoints = static function (?string $str): string {
            if (null === $str || '' === $str) {
                return '';
            }

            $characters = preg_split('//u', $str, flags: PREG_SPLIT_NO_EMPTY);
            false !== $characters || throw new ValueError('The string must contain valid UTF-8.');

            return implode('.', array_map(mb_ord(...), $characters));
        };

        $compare = fn (string $name1, string $name2): int => match (1) {
            preg_match(self::REGEXP_NON_ASCII_PATTERN, $name1.$name2) => strcmp($codepoints($name1), $codepoints($name2)),
            default => strcmp($name1, $name2),
        };

        $parameters = array_reduce($this->pairs, function (array $carry, array $pair) {
            $carry[$pair[0]] ??= [];
            $carry[$pair[0]][] = $pair[1];

            return $carry;
        }, []);

        uksort($parameters, $compare);

        $pairs = [];
        foreach ($parameters as $key => $values) {
            $pairs = [...$pairs, ...array_map(fn ($value) => [$key, $value], $values)];
        }

        return match ($this->pairs) {
            $pairs  => $this,
            default => self::fromPairs($pairs),
        };
    }

    public function withoutDuplicates(): self
    {
        if (count($this->pairs) === $this->countDistinctKeys()) {
            return $this;
        }

        $pairs = array_reduce($this->pairs, $this->removeDuplicates(...), []);
        if ($pairs === $this->pairs) {
            return $this;
        }

        return self::fromPairs($pairs, $this->separator);
    }

    /**
     * @template TInitial
     *
     * @param callable(TInitial|null, array{0:array-key, 1:mixed}, array-key=): TInitial $callback
     * @param TInitial|null $initial
     *
     * @return TInitial|null
     */
    public function reduce(callable $callback, mixed $initial = null): mixed
    {
        foreach ($this->pairs as $offset => $pair) {
            $initial = $callback($initial, $pair, $offset);
        }

        return $initial;
    }

    /**
     * @param callable(array{0:array-key, 1:mixed}, array-key=): bool $callback
     */
    public function filter(callable $callback): QueryInterface
    {
        $pairs = array_filter($this->pairs, $callback, ARRAY_FILTER_USE_BOTH);

        return $pairs === $this->pairs ? $this : self::fromPairs($pairs, $this->separator);
    }

    /**
     * @template TReturn
     *
     * @param callable(array{0:array-key, 1:mixed}, array-key=): TReturn $callback
     *
     * @return Iterator<TReturn>
     */
    public function map(callable $callback): Iterator
    {
        foreach ($this->pairs as $offset => $pair) {
            yield $offset => $callback($pair, $offset);
        }
    }

    /**
     * Adds a query pair only if it is not already present in a given array.
     */
    private function removeDuplicates(array $pairs, array $pair): array
    {
        return match (true) {
            in_array($pair, $pairs, true) => $pairs,
            default => [...$pairs, $pair],
        };
    }

    public function withoutEmptyPairs(): QueryInterface
    {
        return $this->filter(fn (array $pair): bool => '' !== $pair[0] && null !== $pair[1] && '' !== $pair[1]);
    }

    public function withoutNumericIndices(): self
    {
        $pairs = array_map($this->encodeNumericIndices(...), $this->pairs);

        return match ($this->pairs) {
            $pairs => $this,
            default => self::fromPairs($pairs, $this->separator),
        };
    }

    /**
     * Remove numeric indices from pairs.
     *
     * @param array{0:string, 1:string|null} $pair
     *
     * @return array{0:string, 1:string|null}
     */
    private function encodeNumericIndices(array $pair): array
    {
        static $regexp = ',\[\d+],';

        $pair[0] = (string) preg_replace($regexp, '[]', $pair[0]);

        return $pair;
    }

    public function withPair(string $key, array|BackedEnum|Stringable|string|int|float|bool|null $value, StringCoercionMode $coercionMode = StringCoercionMode::Native): QueryInterface
    {
        if (!is_array($value)) {
            $value = [$value];
        }

        [] !== $value || throw new ValueError('The value list can not be empty.');

        $found = false;
        $reducer = static function (array $pairs, array $srcPair) use ($key, $value, &$found): array {
            if ($key !== $srcPair[0]) {
                $pairs[] = $srcPair;

                return $pairs;
            }

            if ($found) {
                return $pairs;
            }

            foreach ($value as $val) {
                $val = is_array($val) ? $value : [$val];
                foreach ($val as $v) {
                    $pairs[] = [$key, $v];
                }
            }

            $found = true;

            return $pairs;
        };

        $pairs = array_reduce($this->pairs, $reducer, []);
        if (!$found) {
            foreach ($value as $val) {
                $pairs[] = [$key, $val];
            }
        }

        return match ($this->pairs) {
            $pairs => $this,
            default => self::fromPairs($pairs, $this->separator, coercionMode: $coercionMode),
        };
    }

    /**
     * Add a new pair to the query key/value list.
     *
     * If there are any key/value pair whose kay is kay, in the list,
     * set the value of the first such key/value pair to value and remove the others.
     * Otherwise, append a new key/value pair whose key is key and value is value, to the list.
     */
    private function addPair(array $list, array $pair): array
    {
        $found = false;
        $reducer = static function (array $pairs, array $srcPair) use ($pair, &$found): array {
            if ($pair[0] !== $srcPair[0]) {
                $pairs[] = $srcPair;

                return $pairs;
            }

            if (!$found) {
                $pairs[] = $pair;
                $found = true;

                return $pairs;
            }

            return $pairs;
        };

        $pairs = array_reduce($list, $reducer, []);
        if (!$found) {
            $pairs[] = $pair;
        }

        return $pairs;
    }

    public function merge(BackedEnum|Stringable|string|null $query, StringCoercionMode $coercionMode = StringCoercionMode::Native): QueryInterface
    {
        $pairs = $this->pairs;
        foreach (QueryString::parseFromValue(self::filterComponent($query), Converter::fromRFC3986($this->separator)) as $pair) {
            $pairs = $this->addPair($pairs, $pair);
        }

        return match ($this->pairs) {
            $pairs => $this,
            default => self::fromPairs($pairs, $this->separator, coercionMode: $coercionMode),
        };
    }

    public function withoutPairByKey(string ...$keys): QueryInterface
    {
        if ([] === $keys) {
            return $this;
        }

        $keysToRemove = array_intersect($keys, array_column($this->pairs, 0));
        if ([] === $keysToRemove) {
            return $this;
        }

        return $this->filter(fn (array $pair): bool => !in_array($pair[0], $keysToRemove, true));
    }

    public function withoutPairByValue(array|BackedEnum|Stringable|string|int|float|bool|null $values, StringCoercionMode $coercionMode = StringCoercionMode::Native): QueryInterface
    {
        if (!is_array($values)) {
            $values = [$values];
        }

        if ([] === $values) {
            return $this;
        }

        $values = array_map($coercionMode->coerce(...), $values);

        return $this->filter(fn (array $pair) => !in_array($pair[1], $values, true));
    }

    public function withoutPairByKeyValue(string $key, BackedEnum|Stringable|string|int|float|bool|null $value, StringCoercionMode $coercionMode = StringCoercionMode::Native): QueryInterface
    {
        $pair = [$key, $coercionMode->coerce($value)];

        return $this->filter(fn (array $currentPair) => $currentPair !== $pair);
    }

    public function appendTo(string $key, array|BackedEnum|Stringable|string|int|float|bool|null $value, StringCoercionMode $coercionMode = StringCoercionMode::Native): QueryInterface
    {
        if (!is_array($value)) {
            $value = [$value];
        }

        [] !== $value || throw new ValueError('Missing values to append');

        $converter = function (iterable $values) use ($key) {
            foreach ($values as $value) {
                yield [$key, $value];
            }
        };

        return self::fromPairs([...$this->pairs, ...$converter($value)], $this->separator, coercionMode: $coercionMode);
    }

    public function appendList(
        string $name,
        array $values,
        QueryComposeMode $composeMode = QueryComposeMode::Native
    ): QueryInterface {
        return $this->append(
            QueryString::composeFromValue(
                data: [$name => $values],
                converter: Converter::fromRFC3986($this->separator),
                composeMode: $composeMode,
            )
        );
    }

    public function append(BackedEnum|Stringable|string|null $query, StringCoercionMode $coercionMode = StringCoercionMode::Native): QueryInterface
    {
        return null === $query ? $this : self::fromPairs(
            array_filter(
                array_merge($this->pairs, QueryString::parseFromValue($query, Converter::fromRFC3986($this->separator))),
                static fn (array $pair): bool => '' !== $pair[0] || null !== $pair[1]
            ),
            $this->separator,
            coercionMode: $coercionMode,
        );
    }

    public function prepend(BackedEnum|Stringable|string|null $query, StringCoercionMode $coercionMode = StringCoercionMode::Native): QueryInterface
    {
        return Query::new($query)->append($this, $coercionMode);
    }

    /**
     * Replace a pair based on its offset.
     */
    public function replace(int $offset, string $key, BackedEnum|Stringable|string|int|float|bool|null $value, StringCoercionMode $coercionMode = StringCoercionMode::Native): QueryInterface
    {
        $index = $offset < 0 ? count($this->pairs) + $offset : $offset;
        $pair = $this->pairs[$index] ?? [];
        [] !== $pair || throw new ValueError('The given offset "'.$offset.'" does not exist');

        $newPair = [$key, $coercionMode->coerce($value)];
        if ($pair === $newPair) {
            return $this;
        }

        $newPairs = $this->pairs;
        $newPairs[$index] = $newPair;

        return self::fromPairs($newPairs, $this->separator);
    }

    public function withList(
        string $name,
        array $values,
        QueryComposeMode $composeMode = QueryComposeMode::Native
    ): QueryInterface {
        if ([] === $values) {
            return $this;
        }

        $data = QueryString::parseFromValue(
            QueryString::composeFromValue(
                data: [$name => $values],
                converter: Converter::fromRFC3986($this->separator),
                composeMode: $composeMode,
            ),
            Converter::fromRFC3986($this->separator),
        );
        $regexp = ','.preg_quote($name, ',').'(\[.*\].*),';
        $isRemoved = false;

        $pairs = array_reduce($this->pairs, function (array $pairs, array $pair) use ($data, $regexp, &$isRemoved): array {
            if (1 !== preg_match($regexp, $pair[0])) {
                $pairs[] = $pair;

                return $pairs;
            }

            if ($isRemoved) {
                return $pairs;
            }

            foreach ($data as $arr) {
                $pairs[] = $arr;
            }
            $isRemoved = true;

            return $pairs;
        }, []);

        if (!$isRemoved) {
            $pairs = array_merge($pairs, $data);
        }

        return $this->pairs === $pairs ? $this : self::fromPairs($pairs, $this->separator);
    }

    public function withoutList(string ...$names): QueryInterface
    {
        if ([] === $names) {
            return $this;
        }

        $mapper = static fn (string $offset): string => preg_quote($offset, ',').'(\[.*\].*)';
        $regexp = ',^('.implode('|', array_map($mapper, $names)).')?$,';

        return $this->filter(fn (array $pair): bool => 1 !== preg_match($regexp, $pair[0]));
    }

    public function onlyLists(): QueryInterface
    {
        return $this->filter(static fn (array $pair): bool => 1 === preg_match(self::REGXP_FILTER_LIST, $pair[0]));
    }

    public function withoutLists(): QueryInterface
    {
        return [] === $this->list ? $this : $this->filter(static fn (array $pair): bool => 1 !== preg_match(self::REGXP_FILTER_LIST, $pair[0]));
    }

    public function parameters(): array
    {
        return $this->parameters;
    }

    public function mergeParameters(object|array $parameter, string $prefix = '', QueryComposeMode $composeMode = QueryComposeMode::Native): self
    {
        $params = is_object($parameter) ? get_object_vars($parameter) : $parameter;
        $data = [];
        foreach ($params as $name => $value) {
            $data[$prefix.$name] = $value;
        }

        return in_array($data, [$this->parameters, []], true) ? $this : new self(
            QueryString::compose(data: array_merge($this->parameters, $data), separator: $this->separator, composeMode: $composeMode),
            Converter::fromRFC1738($this->separator)
        );
    }

    public function replaceParameter(string $name, mixed $parameter, QueryComposeMode $composeMode = QueryComposeMode::Native): self
    {
        $this->has($name) || $this->hasList($name) || throw new ValueError('The specified name does not exist');
        if ($parameter === $this->parameters[$name]) {
            return $this;
        }

        $parameters = $this->parameters;
        $parameters[$name] = $parameter;

        return new self(
            QueryString::compose(data: $parameters, separator: $this->separator, composeMode: $composeMode),
            Converter::fromRFC1738($this->separator)
        );
    }

    public function parameter(string $name): mixed
    {
        return $this->parameters[$name] ?? null;
    }

    public function hasParameter(string ...$names): bool
    {
        foreach ($names as $name) {
            if (!isset($this->parameters[$name])) {
                return false;
            }
        }

        return [] !== $names;
    }

    public function withoutParameters(string ...$names): QueryInterface
    {
        if ([] === $names) {
            return $this;
        }

        $mapper = static fn (string $offset): string => preg_quote($offset, ',').'(\[.*\].*)?';
        $regexp = ',^('.implode('|', array_map($mapper, $names)).')?$,';

        return $this->filter(fn (array $pair): bool => 1 !== preg_match($regexp, $pair[0]));
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @see Query::fromParameters()
     *
     * @codeCoverageIgnore
     *
     * @param non-empty-string $separator
     *
     * Returns a new instance from the result of PHP's parse_str.
     *
     * @deprecated Since version 7.0.0
     */
    #[Deprecated(message:'use League\Uri\Components\Query::fromVariables() instead', since:'league/uri-components:7.0.0')]
    public static function createFromParams(iterable|object $params, string $separator = '&'): self
    {
        return self::fromParameters($params, $separator);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @see Query::fromPairs()
     *
     * @codeCoverageIgnore
     *
     *
     * Returns a new instance from the result of QueryString::parse.
     *
     * @param iterable<int, array{0:string, 1:string|null}> $pairs
     * @param non-empty-string $separator
     */
    #[Deprecated(message:'use League\Uri\Components\Query::fromPairs() instead', since:'league/uri-components:7.0.0')]
    public static function createFromPairs(iterable $pairs, string $separator = '&'): self
    {
        return self::fromPairs($pairs, $separator);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @see Query::fromUri()
     *
     * @codeCoverageIgnore
     *
     * Create a new instance from a URI object.
     */
    #[Deprecated(message:'use League\Uri\Components\Query::fromUri() instead', since:'league/uri-components:7.0.0')]
    public static function createFromUri(Psr7UriInterface|UriInterface $uri): self
    {
        return self::fromUri($uri);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @see Query::fromRFC3986()
     *
     * @codeCoverageIgnore
     *
     * Returns a new instance.
     *
     * @param non-empty-string $separator
     */
    #[Deprecated(message:'use League\Uri\Components\Query::fromRFC3986() instead', since:'league/uri-components:7.0.0')]
    public static function createFromRFC3986(Stringable|string|int|null $query = '', string $separator = '&'): self
    {
        if (null !== $query) {
            $query = (string) $query;
        }

        return self::fromRFC3986($query, $separator);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @see Query::fromRFC1738()
     *
     * @codeCoverageIgnore
     *
     * Returns a new instance.
     *
     * @param non-empty-string $separator
     */
    #[Deprecated(message:'use League\Uri\Components\Query::fromRFC1738() instead', since:'league/uri-components:7.0.0')]
    public static function createFromRFC1738(Stringable|string|int|null $query = '', string $separator = '&'): self
    {
        if (is_int($query)) {
            $query = (string) $query;
        }

        return self::fromRFC1738($query, $separator);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @see Query::parameters()
     * @see Query::parameter()
     *
     * @codeCoverageIgnore
     *
     * Returns the query as a collection of PHP variables or a single variable assign to a specific key
     */
    #[Deprecated(message:'use League\Uri\Components\Query::parameter() or League\Uri\Components\Query::parameters() instead', since:'league/uri-components:7.0.0')]
    public function params(?string $key = null): mixed
    {
        return null === $key ? $this->parameters : $this->parameters[$key] ?? null;
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @see Query::withoutParameters()
     *
     * @codeCoverageIgnore
     */
    #[Deprecated(message:'use League\Uri\Components\Query::withoutParameters() instead', since:'league/uri-components:7.0.0')]
    public function withoutParams(string ...$names): QueryInterface
    {
        return $this->withoutPairByKey(...$names)->withoutList(...$names);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.3.0
     * @see Query::withoutPairByKey()
     *
     * @codeCoverageIgnore
     */
    #[Deprecated(message:'use League\Uri\Components\Query::withoutPairByKey() instead', since:'league/uri-components:7.3.0')]
    public function withoutPair(string ...$keys): QueryInterface
    {
        return $this->withoutPairByKey(...$keys);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @param non-empty-string $separator
     *
     * @see Query::fromVariable()
     *
     * @codeCoverageIgnore
     * Returns a new instance from the result of PHP's parse_str.
     *
     * @deprecated Since version 7.0.0
     */
    #[Deprecated(message:'use League\Uri\Components\Query::fromVariable() instead', since:'league/uri-components:7.0.0')]
    public static function fromParameters(object|array $parameters, string $separator = '&'): self
    {
        if ($parameters instanceof QueryInterface) {
            return self::fromPairs($parameters, $separator);
        }

        $parameters = match (true) {
            $parameters instanceof Traversable => iterator_to_array($parameters),
            default => $parameters,
        };

        $query = match ([]) {
            $parameters => null,
            default => http_build_query(data: $parameters, arg_separator: $separator),
        };

        return new self($query, Converter::fromRFC1738($separator));
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\Components;

use BackedEnum;
use League\Uri\Contracts\Conditionable;
use League\Uri\Contracts\Transformable;
use League\Uri\Contracts\UriComponentInterface;
use League\Uri\Contracts\UriInterface;
use League\Uri\Encoder;
use League\Uri\Exceptions\SyntaxError;
use League\Uri\Modifier;
use League\Uri\StringCoercionMode;
use League\Uri\Uri;
use Psr\Http\Message\UriInterface as Psr7UriInterface;
use Stringable;
use Uri\Rfc3986\Uri as Rfc3986Uri;
use Uri\WhatWg\Url as WhatWgUrl;

use function is_bool;
use function preg_match;
use function sprintf;

abstract class Component implements UriComponentInterface, Conditionable, Transformable
{
    protected const REGEXP_INVALID_URI_CHARS = '/[\x00-\x1f\x7f]/';

    abstract public function value(): ?string;

    public function jsonSerialize(): ?string
    {
        return $this->value();
    }

    public function toString(): string
    {
        return $this->value() ?? '';
    }

    public function __toString(): string
    {
        return $this->toString();
    }

    public function getUriComponent(): string
    {
        return $this->toString();
    }

    final protected static function filterUri(WhatWgUrl|Rfc3986Uri|BackedEnum|Stringable|string $uri): WhatWgUrl|Rfc3986Uri|UriInterface|Psr7UriInterface
    {
        if ($uri instanceof Modifier) {
            return $uri->unwrap();
        }

        if ($uri instanceof Rfc3986Uri
            || $uri instanceof WhatWgUrl
            || $uri instanceof PSR7UriInterface
            || $uri instanceof UriInterface
        ) {
            return $uri;
        }

        return Uri::new($uri);
    }

    /**
     * Validate the component content.
     */
    protected function validateComponent(BackedEnum|Stringable|int|string|null $component): ?string
    {
        return Encoder::decodeNecessary($component);
    }

    /**
     * Filter the input component.
     *
     * @throws SyntaxError If the component cannot be converted to a string or null
     */
    final protected static function filterComponent(BackedEnum|Stringable|int|string|null $component): ?string
    {
        $component = StringCoercionMode::Native->coerce($component);

        return match (true) {
            null === $component => null,
            1 === preg_match(self::REGEXP_INVALID_URI_CHARS, $component) => throw new SyntaxError(sprintf('Invalid component string: %s.', $component)),
            default => $component,
        };
    }

    final public function when(callable|bool $condition, callable $onSuccess, ?callable $onFail = null): static
    {
        if (!is_bool($condition)) {
            $condition = $condition($this);
        }

        return match (true) {
            $condition => $onSuccess($this),
            null !== $onFail => $onFail($this),
            default => $this,
        } ?? $this;
    }

    final public function transform(callable $callback): static
    {
        return $callback($this);
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\Components;

use BackedEnum;
use Deprecated;
use finfo;
use League\Uri\Contracts\DataPathInterface;
use League\Uri\Contracts\PathInterface;
use League\Uri\Contracts\UriInterface;
use League\Uri\Exceptions\SyntaxError;
use League\Uri\FeatureDetection;
use League\Uri\StringCoercionMode;
use Psr\Http\Message\UriInterface as Psr7UriInterface;
use SplFileObject;
use Stringable;
use Throwable;
use Uri\Rfc3986\Uri as Rfc3986Uri;
use Uri\WhatWg\Url as WhatWgUrl;

use function base64_decode;
use function base64_encode;
use function count;
use function explode;
use function file_get_contents;
use function implode;
use function preg_match;
use function preg_replace_callback;
use function rawurldecode;
use function rawurlencode;
use function sprintf;
use function str_replace;
use function strlen;
use function strtolower;

use const FILEINFO_MIME;

final class DataPath extends Component implements DataPathInterface
{
    /**
     * All ASCII letters sorted by typical frequency of occurrence.
     */
    private const ASCII = "\x20\x65\x69\x61\x73\x6E\x74\x72\x6F\x6C\x75\x64\x5D\x5B\x63\x6D\x70\x27\x0A\x67\x7C\x68\x76\x2E\x66\x62\x2C\x3A\x3D\x2D\x71\x31\x30\x43\x32\x2A\x79\x78\x29\x28\x4C\x39\x41\x53\x2F\x50\x22\x45\x6A\x4D\x49\x6B\x33\x3E\x35\x54\x3C\x44\x34\x7D\x42\x7B\x38\x46\x77\x52\x36\x37\x55\x47\x4E\x3B\x4A\x7A\x56\x23\x48\x4F\x57\x5F\x26\x21\x4B\x3F\x58\x51\x25\x59\x5C\x09\x5A\x2B\x7E\x5E\x24\x40\x60\x7F\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F";
    private const BINARY_PARAMETER = 'base64';
    private const DEFAULT_MIMETYPE = 'text/plain';
    private const DEFAULT_PARAMETER = 'charset=us-ascii';
    private const REGEXP_MIMETYPE = ',^\w+/[-.\w]+(?:\+[-.\w]+)?$,';
    private const REGEXP_DATAPATH = '/^\w+\/[-.\w]+(?:\+[-.\w]+)?;,$/';
    private const REGEXP_DATAPATH_ENCODING = '/[^A-Za-z0-9_\-.~!$&\'()*+,;=%:\/@]+|%(?![A-Fa-f0-9]{2})/x';

    private readonly PathInterface $path;
    private readonly string $mimetype;
    /** @var string[] */
    private readonly array $parameters;
    private readonly bool $isBinaryData;
    private readonly string $document;

    /**
     * New instance.
     */
    private function __construct(BackedEnum|Stringable|string $path)
    {
        /** @var string $path */
        $path = self::filterComponent($path);
        $this->path = Path::new($this->filterPath($path));
        [$mediaType, $this->document] = explode(',', $this->path->toString(), 2) + [1 => ''];
        [$mimetype, $parameters] = explode(';', $mediaType, 2) + [1 => ''];
        $this->mimetype = $this->filterMimeType($mimetype);
        [$this->parameters, $this->isBinaryData] = $this->filterParameters($parameters);
        $this->validateDocument();
    }

    /**
     * Filter the data path.
     *
     * @throws SyntaxError If the path is null
     * @throws SyntaxError If the path is not valid according to RFC2937
     */
    private function filterPath(string $path): string
    {
        if ('' === $path || ',' === $path) {
            return 'text/plain;charset=us-ascii,';
        }

        if (1 === preg_match(self::REGEXP_DATAPATH, $path)) {
            $path = substr($path, 0, -1).'charset=us-ascii,';
        }

        if (strlen($path) !== strspn($path, self::ASCII) || !str_contains($path, ',')) {
            throw new SyntaxError(sprintf('The path `%s` is invalid according to RFC2937.', $path));
        }

        return $path;
    }

    /**
     * Filter the mimeType property.
     *
     * @throws SyntaxError If the mimetype is invalid
     */
    private function filterMimeType(string $mimetype): string
    {
        return match (true) {
            '' === $mimetype => self::DEFAULT_MIMETYPE,
            1 === preg_match(self::REGEXP_MIMETYPE, $mimetype) =>  $mimetype,
            default => throw new SyntaxError(sprintf('Invalid mimeType, `%s`.', $mimetype)),
        };
    }

    /**
     * Extract and set the binary flag from the parameters if it exists.
     *
     * @throws SyntaxError If the mediatype parameters contain invalid data
     *
     * @return array{0:array<string>, 1:bool}
     */
    private function filterParameters(string $parameters): array
    {
        if ('' === $parameters) {
            return [[self::DEFAULT_PARAMETER], false];
        }

        $isBinaryData = false;
        if (1 === preg_match(',(;|^)'.self::BINARY_PARAMETER.'$,', $parameters, $matches)) {
            $parameters = substr($parameters, 0, - strlen($matches[0]));
            $isBinaryData = true;
        }

        $params = array_filter(explode(';', $parameters), fn (string $param) => '' !== $param);
        if ([] !== array_filter($params, $this->validateParameter(...))) {
            throw new SyntaxError(sprintf('Invalid mediatype parameters, `%s`.', $parameters));
        }

        return [$params, $isBinaryData];
    }

    /**
     * Validate mediatype parameter.
     */
    private function validateParameter(string $parameter): bool
    {
        $properties = explode('=', $parameter);

        return 2 !== count($properties) || self::BINARY_PARAMETER === strtolower($properties[0]);
    }

    /**
     * Validate the path document string representation.
     *
     * @throws SyntaxError If the data is invalid
     */
    private function validateDocument(): void
    {
        if (!$this->isBinaryData) {
            return;
        }

        $res = base64_decode($this->document, true);
        if (false === $res || $this->document !== base64_encode($res)) {
            throw new SyntaxError(sprintf('Invalid document, `%s`.', $this->document));
        }
    }

    /**
     * Returns a new instance from a string or a stringable object.
     */
    public static function new(BackedEnum|Stringable|string $value = ''): self
    {
        return new self($value);
    }

    /**
     * Create a new instance from a string.or a stringable structure or returns null on failure.
     */
    public static function tryNew(BackedEnum|Stringable|string $uri = ''): ?self
    {
        try {
            return self::new($uri);
        } catch (Throwable) {
            return null;
        }
    }

    /**
     * Creates a new instance from a file path.
     *
     * @param null|resource $context
     *
     * @throws SyntaxError If the File is not readable
     */
    public static function fromFileContents(string $path, $context = null): self
    {
        FeatureDetection::supportsFileDetection();

        $fileArgs = [$path, false];
        $mimeArgs = [$path, FILEINFO_MIME];
        if (null !== $context) {
            $fileArgs[] = $context;
            $mimeArgs[] = $context;
        }

        $content = @file_get_contents(...$fileArgs);
        if (false === $content) {
            throw new SyntaxError(sprintf('`%s` failed to open stream: No such file or directory.', $path));
        }

        $mimetype = (string) (new finfo(FILEINFO_MIME))->file(...$mimeArgs);

        return new self(
            str_replace(' ', '', $mimetype)
            .';base64,'.base64_encode($content)
        );
    }

    /**
     * Create a new instance from a URI object.
     */
    public static function fromUri(WhatWgUrl|Rfc3986Uri|BackedEnum|Stringable|string $uri): self
    {
        return self::new(Path::fromUri($uri)->toString());
    }

    public function value(): string
    {
        return $this->path->value();
    }

    public function equals(mixed $value): bool
    {
        return $this->path->equals($value);
    }

    public function getData(): string
    {
        return $this->document;
    }

    public function isBinaryData(): bool
    {
        return $this->isBinaryData;
    }

    public function getMimeType(): string
    {
        return $this->mimetype;
    }

    public function getParameters(): string
    {
        return implode(';', $this->parameters);
    }

    public function getMediaType(): string
    {
        return $this->getMimeType().';'.$this->getParameters();
    }

    public function isAbsolute(): bool
    {
        return $this->path->isAbsolute();
    }

    public function hasTrailingSlash(): bool
    {
        return $this->path->hasTrailingSlash();
    }

    public function decoded(): string
    {
        return $this->path->decoded();
    }

    public function normalize(): self
    {
        return new self($this->path->normalize()->value());
    }

    /**
     * @param ?resource $context
     */
    public function save(BackedEnum|Stringable|string $path, string $mode = 'w', $context = null): SplFileObject
    {
        if ($path instanceof BackedEnum) {
            $path = $path->value;
        }

        $data = $this->isBinaryData ? base64_decode($this->document, true) : rawurldecode($this->document);
        $file = new SplFileObject((string) $path, $mode, context: $context);
        $file->fwrite((string) $data);

        return $file;
    }

    public function toBinary(): DataPathInterface
    {
        if ($this->isBinaryData) {
            return $this;
        }

        return new self($this->formatComponent(
            $this->mimetype,
            $this->getParameters(),
            true,
            base64_encode(rawurldecode($this->document))
        ));
    }

    /**
     * Format the DataURI string.
     */
    private function formatComponent(
        string $mimetype,
        string $parameters,
        bool $isBinaryData,
        string $data
    ): string {
        if ('' !== $parameters) {
            $parameters = ';'.$parameters;
        }

        if ($isBinaryData) {
            $parameters .= ';base64';
        }

        $path = $mimetype.$parameters.','.$data;

        return preg_replace_callback(
            self::REGEXP_DATAPATH_ENCODING,
            static fn (array $matches): string => rawurlencode($matches[0]),
            $path
        ) ?? $path;
    }

    public function toAscii(): DataPathInterface
    {
        return match (false) {
            $this->isBinaryData => $this,
            default => new self($this->formatComponent(
                $this->mimetype,
                $this->getParameters(),
                false,
                rawurlencode((string)base64_decode($this->document, true))
            )),
        };
    }

    public function withoutDotSegments(): PathInterface
    {
        return $this;
    }

    public function withLeadingSlash(): PathInterface
    {
        return new self($this->path->withLeadingSlash());
    }

    public function withoutLeadingSlash(): PathInterface
    {
        return $this;
    }

    public function withoutTrailingSlash(): PathInterface
    {
        $path = $this->path->withoutTrailingSlash();

        return match ($this->path) {
            $path => $this,
            default => new self($path),
        };
    }

    public function withTrailingSlash(): PathInterface
    {
        $path = $this->path->withTrailingSlash();

        return match ($this->path) {
            $path => $this,
            default => new self($path),
        };
    }

    public function withParameters(BackedEnum|Stringable|string $parameters): DataPathInterface
    {
        $parameters = (string) StringCoercionMode::Native->coerce($parameters);

        return match ($this->getParameters()) {
            $parameters => $this,
            default => new self($this->formatComponent(
                $this->mimetype,
                $parameters,
                $this->isBinaryData,
                $this->document
            )),
        };
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @see DataPath::new()
     *
     * @codeCoverageIgnore
     *
     * Returns a new instance from a string or a stringable object.
     */
    #[Deprecated(message:'use League\Uri\Components\DataPath::new() instead', since:'league/uri-components:7.0.0')]
    public static function createFromString(Stringable|string $path): self
    {
        return self::new($path);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @see DataPath::fromFilePath()
     *
     * @codeCoverageIgnore
     *
     * Creates a new instance from a file path.
     *
     * @param null|resource $context
     *
     * @throws SyntaxError If the File is not readable
     */
    #[Deprecated(message:'use League\Uri\Components\DataPath::fromFilePath() instead', since:'league/uri-components:7.0.0')]
    public static function createFromFilePath(string $path, $context = null): self
    {
        return self::fromFileContents($path, $context);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @see DataPath::fromUri()
     *
     * @codeCoverageIgnore
     *
     * Create a new instance from a URI object.
     */
    #[Deprecated(message:'use League\Uri\Components\DataPath::fromUri() instead', since:'league/uri-components:7.0.0')]
    public static function createFromUri(Psr7UriInterface|UriInterface $uri): self
    {
        return self::fromUri($uri);
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\Components;

use BackedEnum;
use Deprecated;
use League\Uri\Contracts\FragmentInterface;
use League\Uri\Contracts\UriComponentInterface;
use League\Uri\Contracts\UriException;
use League\Uri\Contracts\UriInterface;
use League\Uri\Encoder;
use League\Uri\StringCoercionMode;
use League\Uri\UriString;
use Psr\Http\Message\UriInterface as Psr7UriInterface;
use Stringable;
use Uri\Rfc3986\Uri as Rfc3986Uri;
use Uri\WhatWg\Url as WhatWgUrl;

use function str_replace;

final class Fragment extends Component implements FragmentInterface
{
    private readonly ?string $fragment;

    /**
     * New instance.
     */
    private function __construct(BackedEnum|Stringable|string|null $fragment)
    {
        $this->fragment = $this->validateComponent($fragment);
    }

    public static function new(BackedEnum|Stringable|string|null $value = null): self
    {
        return new self($value);
    }

    /**
     * Create a new instance from a string.or a stringable structure or returns null on failure.
     */
    public static function tryNew(BackedEnum|Stringable|string|null $uri = null): ?self
    {
        try {
            return self::new($uri);
        } catch (UriException) {
            return null;
        }
    }

    /**
     * Create a new instance from a URI object.
     */
    public static function fromUri(WhatWgUrl|Rfc3986Uri|BackedEnum|Stringable|string $uri): self
    {
        $uri = self::filterUri($uri);

        return match (true) {
            $uri instanceof Rfc3986Uri => new self($uri->getRawFragment()),
            $uri instanceof Psr7UriInterface => new self(UriString::parse($uri)['fragment']),
            default => new self($uri->getFragment()),
        };
    }

    public function value(): ?string
    {
        return Encoder::encodeQueryOrFragment($this->fragment);
    }

    public function getUriComponent(): string
    {
        return (null === $this->fragment ? '' : '#').$this->value();
    }

    /**
     * Returns the decoded fragment.
     */
    public function decoded(): ?string
    {
        if (null === $this->fragment) {
            return null;
        }

        return  str_replace('%20', ' ', $this->fragment);
    }

    public function equals(mixed $value): bool
    {
        if (!StringCoercionMode::Native->isCoercible($value)) {
            return false;
        }

        if (!$value instanceof UriComponentInterface) {
            $value = self::tryNew(StringCoercionMode::Native->coerce($value));
            if (null === $value) {
                return false;
            }
        }

        return $value->getUriComponent() === $this->getUriComponent();
    }

    public function normalize(): self
    {
        return  new self(Encoder::normalizeFragment($this->value()));
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @see Fragment::new()
     *
     * @codeCoverageIgnore
     */
    #[Deprecated(message:'use League\Uri\Components\Fragment::new() instead', since:'league/uri-components:7.0.0')]
    public static function createFromString(Stringable|string $fragment): self
    {
        return self::new($fragment);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @see Fragment::fromUri()
     *
     * @codeCoverageIgnore
     *
     * Create a new instance from a URI object.
     */
    #[Deprecated(message:'use League\Uri\Components\Fragment::fromUri() instead', since:'league/uri-components:7.0.0')]
    public static function createFromUri(Psr7UriInterface|UriInterface $uri): self
    {
        return self::fromUri($uri);
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri\Components;

use BackedEnum;
use Deprecated;
use League\Uri\Contracts\AuthorityInterface;
use League\Uri\Contracts\HostInterface;
use League\Uri\Contracts\PortInterface;
use League\Uri\Contracts\UriComponentInterface;
use League\Uri\Contracts\UriException;
use League\Uri\Contracts\UriInterface;
use League\Uri\Contracts\UserInfoInterface;
use League\Uri\Exceptions\SyntaxError;
use League\Uri\StringCoercionMode;
use League\Uri\UriString;
use Psr\Http\Message\UriInterface as Psr7UriInterface;
use SensitiveParameter;
use Stringable;
use Uri\Rfc3986\Uri as Rfc3986Uri;
use Uri\WhatWg\Url as WhatWgUrl;

final class Authority extends Component implements AuthorityInterface
{
    private readonly HostInterface $host;
    private readonly PortInterface $port;
    private readonly UserInfoInterface $userInfo;

    public function __construct(
        BackedEnum|Stringable|string|null $host,
        BackedEnum|Stringable|string|int|null $port = null,
        #[SensitiveParameter] BackedEnum|Stringable|string|null $userInfo = null
    ) {
        $this->host = !$host instanceof HostInterface ? Host::new($host) : $host;
        $this->port = !$port instanceof PortInterface ? Port::new($port) : $port;
        $this->userInfo = !$userInfo instanceof UserInfoInterface ? UserInfo::new($userInfo) : $userInfo;
        if (null === $this->host->value() && null !== $this->value()) {
            throw new SyntaxError('A non-empty authority must contains a non null host.');
        }
    }

    /**
     * @throws SyntaxError If the component contains invalid HostInterface part.
     */
    public static function new(BackedEnum|Stringable|string|null $value = null): self
    {
        $components = UriString::parseAuthority(self::filterComponent($value));

        return new self(
            Host::new($components['host']),
            Port::new($components['port']),
            new UserInfo(
                $components['user'],
                $components['pass']
            )
        );
    }

    /**
     * Create a new instance from a string.or a stringable structure or returns null on failure.
     */
    public static function tryNew(BackedEnum|Stringable|string|null $uri = null): ?self
    {
        try {
            return self::new($uri);
        } catch (UriException) {
            return null;
        }
    }

    /**
     * Create a new instance from a URI object.
     */
    public static function fromUri(WhatwgUrl|Rfc3986Uri|BackedEnum|Stringable|string $uri): self
    {
        $uri = self::filterUri($uri);
        if ($uri instanceof Rfc3986Uri) {
            return new self($uri->getHost(), $uri->getPort(), $uri->getUserInfo());
        }

        if ($uri instanceof WhatWgUrl) {
            $userInfo = $uri->getUsername();
            if (null !== ($password = $uri->getPassword())) {
                $userInfo .= ':'.$password;
            }

            return new self($uri->getUnicodeHost(), $uri->getPort(), $userInfo);
        }

        if ($uri instanceof Psr7UriInterface) {
            $components = UriString::parse($uri);
            $userInfo = $components['user'];
            if (null !== ($password = $components['pass'])) {
                $userInfo .= ':'.$password;
            }

            return new self($components['host'], $components['port'], $userInfo);
        }

        return self::new($uri->getAuthority());
    }

    /**
     * Create a new instance from a hash of parse_url parts.
     *
     * Create a new instance from a hash representation of the URI similar
     * to PHP parse_url function result
     *
     * @param array{
     *     user? : ?string,
     *     pass? : ?string,
     *     host? : ?string,
     *     port? : ?int
     * } $components
     */
    public static function fromComponents(array $components): self
    {
        $components += ['user' => null, 'pass' => null, 'host' => null, 'port' => null];

        return match (true) {
            null === $components['user'] => new self($components['host'], $components['port']),
            null === $components['pass'] => new self($components['host'], $components['port'], $components['user']),
            default => new self($components['host'], $components['port'], $components['user'].':'.$components['pass']),
        };
    }

    public function value(): ?string
    {
        return self::getAuthorityValue($this->userInfo, $this->host, $this->port);
    }

    private static function getAuthorityValue(
        UserInfoInterface $userInfo,
        HostInterface $host,
        PortInterface $port
    ): ?string {
        $auth = $host->value();
        $port = $port->value();
        if (null !== $port) {
            $auth .= ':'.$port;
        }

        $userInfo = $userInfo->value();

        return match (null) {
            $userInfo => $auth,
            default => $userInfo.'@'.$auth,
        };
    }

    public function getUriComponent(): string
    {
        return match (null) {
            $this->host->value() => $this->toString(),
            default => '//'.$this->toString(),
        };
    }

    public function getHost(): ?string
    {
        return $this->host->value();
    }

    public function getPort(): ?int
    {
        return $this->port->toInt();
    }

    public function getUserInfo(): ?string
    {
        return $this->userInfo->value();
    }

    public function equals(mixed $value): bool
    {
        if (!StringCoercionMode::Native->isCoercible($value)) {
            return false;
        }

        if (!$value instanceof UriComponentInterface) {
            $value = self::tryNew(StringCoercionMode::Native->coerce($value));
            if (null === $value) {
                return false;
            }
        }

        return $value->getUriComponent() === $this->getUriComponent();
    }

    /**
     * @return array{user: ?string, pass: ?string, host: ?string, port: ?int}
     */
    public function components(): array
    {
        return  $this->userInfo->components() + [
            'host' => $this->host->value(),
            'port' => $this->port->toInt(),
        ];
    }

    public function withHost(BackedEnum|Stringable|string|null $host): AuthorityInterface
    {
        if (!$host instanceof HostInterface) {
            $host = Host::new($host);
        }

        return match ($this->host->value()) {
            $host->value() => $this,
            default => new self($host, $this->port, $this->userInfo),
        };
    }

    public function withPort(BackedEnum|Stringable|string|int|null $port): AuthorityInterface
    {
        if (!$port instanceof PortInterface) {
            $port = Port::new($port);
        }

        return match ($this->port->value()) {
            $port->value() => $this,
            default => new self($this->host, $port, $this->userInfo),
        };
    }

    public function withUserInfo(
        BackedEnum|Stringable|string|null $user,
        #[SensitiveParameter] BackedEnum|Stringable|string|null $password = null
    ): AuthorityInterface {
        $userInfo = new UserInfo($user, $password);

        return match ($this->userInfo->value()) {
            $userInfo->value() => $this,
            default => new self($this->host, $this->port, $userInfo),
        };
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @see Authority::fromUri()
     *
     * @codeCoverageIgnore
     *
     * Create a new instance from a URI object.
     */
    #[Deprecated(message:'use League\Uri\Components\Authority::fromUri() instead', since:'league/uri-components:7.0.0')]
    public static function createFromUri(UriInterface|Psr7UriInterface $uri): self
    {
        return self::fromUri($uri);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @see Authority::new()
     *
     * @codeCoverageIgnore
     *
     * Returns a new instance from a string or a stringable object.
     */
    #[Deprecated(message:'use League\Uri\Components\Authority::new() instead', since:'league/uri-components:7.0.0')]
    public static function createFromString(Stringable|string $authority): self
    {
        return self::new($authority);
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @see Authority::new()
     *
     * @codeCoverageIgnore
     *
     * Returns a new instance from null.
     */
    #[Deprecated(message:'use League\Uri\Components\Authority::new() instead', since:'league/uri-components:7.0.0')]
    public static function createFromNull(): self
    {
        return self::new();
    }

    /**
     * DEPRECATION WARNING! This method will be removed in the next major point release.
     *
     * @deprecated Since version 7.0.0
     * @see Authority::fromComponents()
     *
     * @codeCoverageIgnore
     *
     * Create a new instance from a hash of parse_url parts.
     *
     * Create a new instance from a hash representation of the URI similar
     * to PHP parse_url function result
     *
     * @param array{
     *     user? : ?string,
     *     pass? : ?string,
     *     host? : ?string,
     *     port? : ?int
     * } $components
     */
    #[Deprecated(message:'use League\Uri\Components\Authority::fromComponents() instead', since:'league/uri-components:7.0.0')]
    public static function createFromComponents(array $components): self
    {
        return self::fromComponents($components);
    }
}
<?php

/**
 * League.Uri (https://uri.thephpleague.com)
 *
 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace League\Uri;

use League\Uri\Contracts\UriInterface;
use League\Uri\Exceptions\SyntaxError;
use Psr\Http\Message\UriInterface as Psr7UriInterface;
use Stringable;

/**
 * @deprecated since version 7.0.0
 * @codeCoverageIgnore
 * @see Modifier
 */
class UriModifier
{
    /*********************************
     * Query resolution methods
     *********************************/

    /**
     * Add the new query data to the existing URI query.
     */
    public static function appendQuery(
        Psr7UriInterface|UriInterface $uri,
        Stringable|string|null $query
    ): Psr7UriInterface|UriInterface {
        return Modifier::from($uri)->appendQuery($query)->getUri();
    }

    /**
     * Merge a new query with the existing URI query.
     */
    public static function mergeQuery(
        Psr7UriInterface|UriInterface $uri,
        Stringable|string|null $query
    ): Psr7UriInterface|UriInterface {
        return Modifier::from($uri)->mergeQuery($query)->getUri();
    }

    /**
     * Remove query data according to their key name.
     */
    public static function removePairs(Psr7UriInterface|UriInterface $uri, string ...$keys): Psr7UriInterface|UriInterface
    {
        return Modifier::from($uri)->removeQueryPairsByKey(...$keys)->getUri();
    }

    /**
     * Remove empty pairs from the URL query component.
     *
     * A pair is considered empty if it's name is the empty string
     * and its value is either the empty string or the null value
     */
    public static function removeEmptyPairs(Psr7UriInterface|UriInterface $uri): Psr7UriInterface|UriInterface
    {
        return Modifier::from($uri)->removeEmptyQueryPairs()->getUri();
    }

    /**
     * Remove query data according to their key name.
     */
    public static function removeParams(Psr7UriInterface|UriInterface $uri, string ...$keys): Psr7UriInterface|UriInterface
    {
        return Modifier::from($uri)->removeQueryParameters(...$keys)->getUri();
    }

    /**
     * Sort the URI query by keys.
     */
    public static function sortQuery(Psr7UriInterface|UriInterface $uri): Psr7UriInterface|UriInterface
    {
        return Modifier::from($uri)->sortQuery()->getUri();
    }

    /*********************************
     * Host resolution methods
     *********************************/

    /**
     * Add the root label to the URI.
     */
    public static function addRootLabel(Psr7UriInterface|UriInterface $uri): Psr7UriInterface|UriInterface
    {
        return Modifier::from($uri)->addRootLabel()->getUri();
    }

    /**
     * Append a label or a host to the current URI host.
     *
     * @throws SyntaxError If the host cannot be appended
     */
    public static function appendLabel(Psr7UriInterface|UriInterface $uri, Stringable|string|null $label): Psr7UriInterface|UriInterface
    {
        return Modifier::from($uri)->appendLabel($label)->getUri();
    }

    /**
     * Convert the URI host part to its ascii value.
     */
    public static function hostToAscii(Psr7UriInterface|UriInterface $uri): Psr7UriInterface|UriInterface
    {
        return Modifier::from($uri)->hostToAscii()->getUri();
    }

    /**
     * Convert the URI host part to its unicode value.
     */
    public static function hostToUnicode(Psr7UriInterface|UriInterface $uri): Psr7UriInterface|UriInterface
    {
        return Modifier::from($uri)->hostToUnicode()->getUri();
    }

    /**
     * Prepend a label or a host to the current URI host.
     *
     * @throws SyntaxError If the host cannot be prepended
     */
    public static function prependLabel(Psr7UriInterface|UriInterface $uri, Stringable|string|null $label): Psr7UriInterface|UriInterface
    {
        return Modifier::from($uri)->prependLabel($label)->getUri();
    }

    /**
     * Remove host labels according to their offset.
     */
    public static function removeLabels(Psr7UriInterface|UriInterface $uri, int ...$keys): Psr7UriInterface|UriInterface
    {
        return Modifier::from($uri)->removeLabels(...$keys)->getUri();
    }

    /**
     * Remove the root label to the URI.
     */
    public static function removeRootLabel(Psr7UriInterface|UriInterface $uri): Psr7UriInterface|UriInterface
    {
        return Modifier::from($uri)->removeRootLabel()->getUri();
    }

    /**
     * Remove the host zone identifier.
     */
    public static function removeZoneId(Psr7UriInterface|UriInterface $uri): Psr7UriInterface|UriInterface
    {
        return Modifier::from($uri)->removeZoneId()->getUri();
    }

    /**
     * Replace a label of the current URI host.
     */
    public static function replaceLabel(
        Psr7UriInterface|UriInterface $uri,
        int $offset,
        Stringable|string|null $label
    ): Psr7UriInterface|UriInterface {
        return Modifier::from($uri)->replaceLabel($offset, $label)->getUri();
    }

    /*********************************
     * Path resolution methods
     *********************************/

    /**
     * Add a new basepath to the URI path.
     */
    public static function addBasePath(Psr7UriInterface|UriInterface $uri, Stringable|string $path): Psr7UriInterface|UriInterface
    {
        return Modifier::from($uri)->addBasePath($path)->getUri();
    }

    /**
     * Add a leading slash to the URI path.
     */
    public static function addLeadingSlash(Psr7UriInterface|UriInterface $uri): Psr7UriInterface|UriInterface
    {
        return Modifier::from($uri)->addLeadingSlash()->getUri();
    }

    /**
     * Add a trailing slash to the URI path.
     */
    public static function addTrailingSlash(Psr7UriInterface|UriInterface $uri): Psr7UriInterface|UriInterface
    {
        return Modifier::from($uri)->addTrailingSlash()->getUri();
    }

    /**
     * Append a new segment or a new path to the URI path.
     */
    public static function appendSegment(Psr7UriInterface|UriInterface $uri, Stringable|string $segment): Psr7UriInterface|UriInterface
    {
        return Modifier::from($uri)->appendSegment($segment)->getUri();
    }

    /**
     * Convert the Data URI path to its ascii form.
     */
    public static function dataPathToAscii(Psr7UriInterface|UriInterface $uri): Psr7UriInterface|UriInterface
    {
        return Modifier::from($uri)->dataPathToAscii()->getUri();
    }

    /**
     * Convert the Data URI path to its binary (base64encoded) form.
     */
    public static function dataPathToBinary(Psr7UriInterface|UriInterface $uri): Psr7UriInterface|UriInterface
    {
        return Modifier::from($uri)->dataPathToBinary()->getUri();
    }

    /**
     * Prepend an new segment or a new path to the URI path.
     */
    public static function prependSegment(
        Psr7UriInterface|UriInterface $uri,
        Stringable|string $segment
    ): Psr7UriInterface|UriInterface {
        return Modifier::from($uri)->prependSegment($segment)->getUri();
    }

    /**
     * Remove a basepath from the URI path.
     */
    public static function removeBasePath(
        Psr7UriInterface|UriInterface $uri,
        Stringable|string $path
    ): Psr7UriInterface|UriInterface {
        return Modifier::from($uri)->removeBasePath($path)->getUri();
    }

    /**
     * Remove dot segments from the URI path.
     */
    public static function removeDotSegments(Psr7UriInterface|UriInterface $uri): Psr7UriInterface|UriInterface
    {
        return Modifier::from($uri)->removeDotSegments()->getUri();
    }

    /**
     * Remove empty segments from the URI path.
     */
    public static function removeEmptySegments(Psr7UriInterface|UriInterface $uri): Psr7UriInterface|UriInterface
    {
        return Modifier::from($uri)->removeEmptySegments()->getUri();
    }

    /**
     * Remove the leading slash from the URI path.
     */
    public static function removeLeadingSlash(Psr7UriInterface|UriInterface $uri): Psr7UriInterface|UriInterface
    {
        return Modifier::from($uri)->removeLeadingSlash()->getUri();

    }

    /**
     * Remove the trailing slash from the URI path.
     */
    public static function removeTrailingSlash(Psr7UriInterface|UriInterface $uri): Psr7UriInterface|UriInterface
    {
        return Modifier::from($uri)->removeTrailingSlash()->getUri();
    }

    /**
     * Remove path segments from the URI path according to their offsets.
     */
    public static function removeSegments(Psr7UriInterface|UriInterface $uri, int ...$keys): Psr7UriInterface|UriInterface
    {
        return Modifier::from($uri)->removeSegments(...$keys)->getUri();
    }

    /**
     * Replace the URI path basename.
     */
    public static function replaceBasename(
        Psr7UriInterface|UriInterface $uri,
        Stringable|string $basename
    ): Psr7UriInterface|UriInterface {
        return Modifier::from($uri)->replaceBasename($basename)->getUri();
    }

    /**
     * Replace the data URI path parameters.
     */
    public static function replaceDataUriParameters(
        Psr7UriInterface|UriInterface $uri,
        Stringable|string $parameters
    ): Psr7UriInterface|UriInterface {
        return Modifier::from($uri)->replaceDataUriParameters($parameters)->getUri();
    }

    /**
     * Replace the URI path dirname.
     */
    public static function replaceDirname(
        Psr7UriInterface|UriInterface $uri,
        Stringable|string $dirname
    ): Psr7UriInterface|UriInterface {
        return Modifier::from($uri)->replaceDirname($dirname)->getUri();
    }

    /**
     * Replace the URI path basename extension.
     */
    public static function replaceExtension(
        Psr7UriInterface|UriInterface $uri,
        Stringable|string $extension
    ): Psr7UriInterface|UriInterface {
        return Modifier::from($uri)->replaceExtension($extension)->getUri();
    }

    /**
     * Replace a segment from the URI path according its offset.
     */
    public static function replaceSegment(
        Psr7UriInterface|UriInterface $uri,
        int $offset,
        Stringable|string $segment
    ): Psr7UriInterface|UriInterface {
        return Modifier::from($uri)->replaceSegment($offset, $segment)->getUri();
    }
}
{
    "name": "league/uri-components",
    "type": "library",
    "description" : "URI components manipulation library",
    "keywords": [
        "url",
        "uri",
        "rfc3986",
        "components",
        "scheme",
        "userinfo",
        "host",
        "port",
        "authority",
        "path",
        "query",
        "fragment",
        "modifier",
        "middleware"
    ],
    "license": "MIT",
    "homepage": "http://uri.thephpleague.com",
    "authors": [
        {
            "name" : "Ignace Nyamagana Butera",
            "email" : "nyamsprod@gmail.com",
            "homepage" : "https://nyamsprod.com"
        }
    ],
    "require": {
        "php": "^8.1",
        "league/uri": "^7.9.0"
    },
    "suggest": {
        "ext-bcmath": "to improve IPV4 host parsing",
        "ext-mbstring": "to use the sorting algorithm of URLSearchParams",
        "ext-fileinfo": "to create Data URI from file contennts",
        "ext-gmp": "to improve IPV4 host parsing",
        "ext-intl": "to handle IDN host with the best performance",
        "jeremykendall/php-domain-parser": "to further parse the URI host and resolve its Public Suffix and Top Level Domain",
        "league/uri-polyfill" : "to backport the PHP URI extension for older versions of PHP",
        "php-64bit": "to improve IPV4 host parsing",
        "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present",
        "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification"
    },
    "autoload": {
        "psr-4": {
            "League\\Uri\\": ""
        }
    },
    "extra": {
        "branch-alias": {
            "dev-master": "7.x-dev"
        }
    },
    "support": {
        "forum": "https://thephpleague.slack.com",
        "docs": "https://uri.thephpleague.com",
        "issues": "https://github.com/thephpleague/uri-src/issues"
    },
    "config": {
        "sort-packages": true
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Polyfill\Php82;

/**
 * @author Tim DÃ¼sterhus <duesterhus@woltlab.com>
 *
 * @internal
 */
class SensitiveParameterValue
{
    private $value;

    public function __construct($value)
    {
        $this->value = $value;
    }

    public function getValue()
    {
        return $this->value;
    }

    public function __debugInfo(): array
    {
        return [];
    }

    public function __sleep(): array
    {
        throw new \Exception("Serialization of 'SensitiveParameterValue' is not allowed");
    }

    public function __wakeup(): void
    {
        throw new \Exception("Unserialization of 'SensitiveParameterValue' is not allowed");
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use Symfony\Polyfill\Php82 as p;

if (\PHP_VERSION_ID >= 80200) {
    return;
}

if (\extension_loaded('curl') && !defined('CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256') && curl_version()['version_number'] >= 0x075000) {
    define('CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256', 10311);
}

if (extension_loaded('odbc')) {
    if (!function_exists('odbc_connection_string_is_quoted')) {
        function odbc_connection_string_is_quoted(string $str): bool { return p\Php82::odbc_connection_string_is_quoted($str); }
    }

    if (!function_exists('odbc_connection_string_should_quote')) {
        function odbc_connection_string_should_quote(string $str): bool { return p\Php82::odbc_connection_string_should_quote($str); }
    }

    if (!function_exists('odbc_connection_string_quote')) {
        function odbc_connection_string_quote(string $str): string { return p\Php82::odbc_connection_string_quote($str); }
    }
}

if (!function_exists('ini_parse_quantity')) {
    function ini_parse_quantity(string $shorthand): int { return p\Php82::ini_parse_quantity($shorthand); }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Polyfill\Php82\Random\Engine;

use Random\RandomException;
use Symfony\Polyfill\Php82\NoDynamicProperties;

/**
 * @author Tim DÃ¼sterhus <tim@bastelstu.be>
 * @author Anton Smirnov <sandfox@sandfox.me>
 *
 * @internal
 */
class Secure
{
    use NoDynamicProperties;

    public function generate(): string
    {
        try {
            return random_bytes(\PHP_INT_SIZE);
        } catch (\Exception $e) {
            throw new RandomException($e->getMessage(), $e->getCode(), $e->getPrevious());
        }
    }

    public function __sleep(): array
    {
        throw new \Exception("Serialization of 'Random\Engine\Secure' is not allowed");
    }

    public function __wakeup(): void
    {
        throw new \Exception("Unserialization of 'Random\Engine\Secure' is not allowed");
    }

    public function __clone()
    {
        throw new \Error('Trying to clone an uncloneable object of class Random\Engine\Secure');
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Polyfill\Php82;

/**
 * @author Alexander M. Turek <me@derrabus.de>
 * @author Greg Roach <greg@subaqua.co.uk>
 *
 * @internal
 */
class Php82
{
    /**
     * Determines if a string matches the ODBC quoting rules.
     *
     * A valid quoted string begins with a '{', ends with a '}', and has no '}'
     * inside of the string that aren't repeated (as to be escaped).
     *
     * These rules are what .NET also follows.
     *
     * @see https://github.com/php/php-src/blob/838f6bffff6363a204a2597cbfbaad1d7ee3f2b6/main/php_odbc_utils.c#L31-L57
     */
    public static function odbc_connection_string_is_quoted(string $str): bool
    {
        if ('' === $str || '{' !== $str[0]) {
            return false;
        }

        /* Check for } that aren't doubled up or at the end of the string */
        $length = \strlen($str) - 1;
        for ($i = 0; $i < $length; ++$i) {
            if ('}' !== $str[$i]) {
                continue;
            }

            if ('}' !== $str[++$i]) {
                return false;
            }
        }

        return true;
    }

    /**
     * Determines if a value for a connection string should be quoted.
     *
     * The ODBC specification mentions:
     * "Because of connection string and initialization file grammar, keywords and
     * attribute values that contain the characters []{}(),;?*=!@ not enclosed
     * with braces should be avoided."
     *
     * Note that it assumes that the string is *not* already quoted. You should
     * check beforehand.
     *
     * @see https://github.com/php/php-src/blob/838f6bffff6363a204a2597cbfbaad1d7ee3f2b6/main/php_odbc_utils.c#L59-L73
     */
    public static function odbc_connection_string_should_quote(string $str): bool
    {
        return false !== strpbrk($str, '[]{}(),;?*=!@ ');
    }

    public static function odbc_connection_string_quote(string $str): string
    {
        return '{'.str_replace('}', '}}', $str).'}';
    }

    /**
     * Implementation closely based on the original C code - including the GOTOs
     * and pointer-style string access.
     *
     * @see https://github.com/php/php-src/blob/master/Zend/zend_ini.c
     */
    public static function ini_parse_quantity(string $value): int
    {
        // Avoid dependency on ctype_space()
        $ctype_space = " \t\v\r\n\f";

        $str = 0;
        $str_end = \strlen($value);
        $digits = $str;
        $overflow = false;

        /* Ignore leading whitespace, but keep it for error messages. */
        while ($digits < $str_end && false !== strpos($ctype_space, $value[$digits])) {
            ++$digits;
        }

        /* Ignore trailing whitespace, but keep it for error messages. */
        while ($digits < $str_end && false !== strpos($ctype_space, $value[$str_end - 1])) {
            --$str_end;
        }

        if ($digits === $str_end) {
            return 0;
        }

        $is_negative = false;

        if ('+' === $value[$digits]) {
            ++$digits;
        } elseif ('-' === $value[$digits]) {
            $is_negative = true;
            ++$digits;
        }

        if ($digits === $str_end || $value[$digits] < '0' || $value[$digits] > '9') {
            $message = \sprintf(
                'Invalid quantity "%s": no valid leading digits, interpreting as "0" for backwards compatibility',
                self::escapeString($value)
            );

            trigger_error($message, \E_USER_WARNING);

            return 0;
        }

        $base = 10;
        $allowed_digits = '0123456789';

        if ('0' === $value[$digits] && ($digits + 1 === $str_end || false === strpos($allowed_digits, $value[$digits + 1]))) {
            if ($digits + 1 === $str_end) {
                return 0;
            }

            switch ($value[$digits + 1]) {
                case 'g':
                case 'G':
                case 'm':
                case 'M':
                case 'k':
                case 'K':
                    goto evaluation;
                case 'x':
                case 'X':
                    $base = 16;
                    $allowed_digits = '0123456789abcdefABCDEF';
                    break;
                case 'o':
                case 'O':
                    $base = 8;
                    $allowed_digits = '01234567';
                    break;
                case 'b':
                case 'B':
                    $base = 2;
                    $allowed_digits = '01';
                    break;
                default:
                    $message = \sprintf(
                        'Invalid prefix "0%s", interpreting as "0" for backwards compatibility',
                        $value[$digits + 1]
                    );
                    trigger_error($message, \E_USER_WARNING);

                    return 0;
            }

            $digits += 2;
            if ($digits === $str_end) {
                $message = \sprintf(
                    'Invalid quantity "%s": no digits after base prefix, interpreting as "0" for backwards compatibility',
                    self::escapeString($value)
                );
                trigger_error($message, \E_USER_WARNING);

                return 0;
            }

            $digits_consumed = $digits;
            /* Ignore leading whitespace. */
            while ($digits_consumed < $str_end && false !== strpos($ctype_space, $value[$digits_consumed])) {
                ++$digits_consumed;
            }
            if ($digits_consumed !== $str_end && ('+' === $value[$digits_consumed] || '-' === $value[$digits_consumed])) {
                ++$digits_consumed;
            }

            if ($digits_consumed + 1 < $str_end && '0' === $value[$digits_consumed]) {
                switch ($value[$digits_consumed + 1]) {
                    case 'x':
                    case 'X':
                    case 'o':
                    case 'O':
                        $digits_consumed += 2;
                        break;
                    case 'b':
                    case 'B':
                        // 0b is a valid pair of digits in base 16
                        if (16 !== $base) {
                            $digits_consumed += 2;
                        }
                        break;
                }
            }

            if ($digits !== $digits_consumed) {
                $message = \sprintf(
                    'Invalid quantity "%s": no digits after base prefix, interpreting as "0" for backwards compatibility',
                    self::escapeString($value)
                );
                trigger_error($message, \E_USER_WARNING);

                return 0;
            }
        }

        evaluation:

        if (10 === $base && '0' === $value[$digits]) {
            $base = 8;
            $allowed_digits = '01234567';
        }

        while ($digits < $str_end && ' ' === $value[$digits]) {
            ++$digits;
        }

        if ($digits < $str_end && '+' === $value[$digits]) {
            ++$digits;
        } elseif ($digits < $str_end && '-' === $value[$digits]) {
            $is_negative = true;
            $overflow = true;
            ++$digits;
        }

        $digits_end = $digits;

        while ($digits_end < $str_end && false !== strpos($allowed_digits, $value[$digits_end])) {
            ++$digits_end;
        }

        // Mimic strtoul() on a zend_ulong using two half-width limbs, as PHP has no unsigned integers
        $bits = \PHP_INT_SIZE << 2;
        $mask = (1 << $bits) - 1;
        $high = $low = 0;

        for ($i = $digits; $i < $digits_end; ++$i) {
            $low = $low * $base + hexdec($value[$i]);
            $high = $high * $base + ($low >> $bits);
            $low &= $mask;

            if ($high > $mask) {
                break;
            }
        }

        if ($high > $mask) {
            $retval = -1;
            $overflow = true;
        } else {
            $retval = ($high << $bits) | $low;

            if ($is_negative && 0 === $retval) {
                $is_negative = false;
                $overflow = false;
            } elseif (0 > $retval) {
                // -PHP_INT_MIN is PHP_INT_MIN itself
                $overflow = $overflow || !$is_negative || \PHP_INT_MIN !== $retval;
            } elseif ($is_negative) {
                $retval = -$retval;
            }
        }

        if ($digits_end === $digits) {
            $message = \sprintf(
                'Invalid quantity "%s": no valid leading digits, interpreting as "0" for backwards compatibility',
                self::escapeString($value)
            );
            trigger_error($message, \E_USER_WARNING);

            return 0;
        }

        /* Allow for whitespace between integer portion and any suffix character */
        while ($digits_end < $str_end && false !== strpos($ctype_space, $value[$digits_end])) {
            ++$digits_end;
        }

        /* No exponent suffix. */
        if ($digits_end === $str_end) {
            goto end;
        }

        switch ($value[$str_end - 1]) {
            case 'g':
            case 'G':
                $shift = 30;
                break;
            case 'm':
            case 'M':
                $shift = 20;
                break;
            case 'k':
            case 'K':
                $shift = 10;
                break;
            default:
                /* Unknown suffix */
                $invalid = self::escapeString($value);
                $interpreted = self::escapeString(substr($value, $str, $digits_end - $str));
                $chr = self::escapeString($value[$str_end - 1]);

                $message = \sprintf(
                    'Invalid quantity "%s": unknown multiplier "%s", interpreting as "%s" for backwards compatibility',
                    $invalid,
                    $chr,
                    $interpreted
                );

                trigger_error($message, \E_USER_WARNING);

                return $retval;
        }

        $factor = 1 << $shift;

        if (!$overflow) {
            if ($retval > 0) {
                $overflow = $retval > \PHP_INT_MAX / $factor;
            } else {
                $overflow = $retval < \PHP_INT_MIN / $factor;
            }
        }

        if (\is_float($retval * $factor)) {
            $overflow = true;
            $retval <<= $shift;
        } else {
            $retval *= $factor;
        }

        if ($digits_end !== $str_end - 1) {
            /* More than one character in suffix */
            $message = \sprintf(
                'Invalid quantity "%s", interpreting as "%s%s" for backwards compatibility',
                self::escapeString($value),
                self::escapeString(substr($value, $str, $digits_end - $str)),
                self::escapeString($value[$str_end - 1])
            );
            trigger_error($message, \E_USER_WARNING);

            return $retval;
        }

        end:

        if ($overflow) {
            /* Not specifying the resulting value here because the caller may make
             * additional conversions. Not specifying the allowed range
             * because the caller may do narrower range checks. */
            $message = \sprintf(
                'Invalid quantity "%s": value is out of range, using overflow result for backwards compatibility',
                self::escapeString($value)
            );
            trigger_error($message, \E_USER_WARNING);
        }

        return $retval;
    }

    /**
     * Escape the string to avoid null bytes and to make non-printable chars visible.
     */
    private static function escapeString(string $string): string
    {
        $escaped = '';

        for ($n = 0, $len = \strlen($string); $n < $len; ++$n) {
            $c = \ord($string[$n]);

            if ($c < 32 || '\\' === $string[$n] || $c > 126) {
                switch ($string[$n]) {
                    case "\n": $escaped .= '\\n'; break;
                    case "\r": $escaped .= '\\r'; break;
                    case "\t": $escaped .= '\\t'; break;
                    case "\f": $escaped .= '\\f'; break;
                    case "\v": $escaped .= '\\v'; break;
                    case '\\': $escaped .= '\\\\'; break;
                    case "\x1B": $escaped .= '\\e'; break;
                    default:
                        $escaped .= '\\x'.strtoupper(\sprintf('%02x', $c));
                }
            } else {
                $escaped .= $string[$n];
            }
        }

        return $escaped;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Polyfill\Php82;

/**
 * @internal
 */
trait NoDynamicProperties
{
    public function __set(string $name, $value): void
    {
        throw new \Error('Cannot create dynamic property '.self::class.'::$'.$name);
    }
}
{
    "name": "symfony/polyfill-php82",
    "type": "library",
    "description": "Symfony polyfill backporting some PHP 8.2+ features to lower PHP versions",
    "keywords": ["polyfill", "shim", "compatibility", "portable"],
    "homepage": "https://symfony.com",
    "license": "MIT",
    "authors": [
        {
            "name": "Nicolas Grekas",
            "email": "p@tchwork.com"
        },
        {
            "name": "Symfony Community",
            "homepage": "https://symfony.com/contributors"
        }
    ],
    "require": {
        "php": ">=7.2"
    },
    "autoload": {
        "psr-4": { "Symfony\\Polyfill\\Php82\\": "" },
        "files": [ "bootstrap.php" ],
        "classmap": [ "Resources/stubs" ]
    },
    "minimum-stability": "dev",
    "extra": {
        "thanks": {
            "name": "symfony/polyfill",
            "url": "https://github.com/symfony/polyfill"
        }
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

if (\PHP_VERSION_ID < 80200) {
    #[Attribute(Attribute::TARGET_PARAMETER)]
    final class SensitiveParameter
    {
        public function __construct()
        {
        }
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

if (\PHP_VERSION_ID < 80200) {
    final class SensitiveParameterValue extends Symfony\Polyfill\Php82\SensitiveParameterValue
    {
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Random;

use Symfony\Polyfill\Php82\NoDynamicProperties;

if (\PHP_VERSION_ID < 80200) {
    class RandomError extends \Error
    {
        use NoDynamicProperties;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Random;

use Symfony\Polyfill\Php82\NoDynamicProperties;

if (\PHP_VERSION_ID < 80200) {
    class RandomException extends \Exception
    {
        use NoDynamicProperties;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Random\Engine;

use Symfony\Polyfill\Php82 as p;

if (\PHP_VERSION_ID < 80200) {
    final class Secure extends p\Random\Engine\Secure implements \Random\CryptoSafeEngine
    {
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Random;

if (\PHP_VERSION_ID < 80200) {
    interface CryptoSafeEngine extends Engine
    {
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Random;

if (\PHP_VERSION_ID < 80200) {
    class BrokenRandomEngineError extends RandomError
    {
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Random;

if (\PHP_VERSION_ID < 80200) {
    interface Engine
    {
        public function generate(): string;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

if (\PHP_VERSION_ID < 80200) {
    #[Attribute(Attribute::TARGET_CLASS)]
    final class AllowDynamicProperties
    {
        public function __construct()
        {
        }
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use Symfony\Polyfill\Iconv as p;

if (extension_loaded('iconv')) {
    return;
}

if (\PHP_VERSION_ID >= 80000) {
    return require __DIR__.'/bootstrap80.php';
}

if (!defined('ICONV_IMPL')) {
    define('ICONV_IMPL', 'Symfony');
}
if (!defined('ICONV_VERSION')) {
    define('ICONV_VERSION', '1.0');
}
if (!defined('ICONV_MIME_DECODE_STRICT')) {
    define('ICONV_MIME_DECODE_STRICT', 1);
}
if (!defined('ICONV_MIME_DECODE_CONTINUE_ON_ERROR')) {
    define('ICONV_MIME_DECODE_CONTINUE_ON_ERROR', 2);
}

if (!function_exists('iconv')) {
    function iconv($from_encoding, $to_encoding, $string) { return p\Iconv::iconv($from_encoding, $to_encoding, $string); }
}
if (!function_exists('iconv_get_encoding')) {
    function iconv_get_encoding($type = 'all') { return p\Iconv::iconv_get_encoding($type); }
}
if (!function_exists('iconv_set_encoding')) {
    function iconv_set_encoding($type, $encoding) { return p\Iconv::iconv_set_encoding($type, $encoding); }
}
if (!function_exists('iconv_mime_encode')) {
    function iconv_mime_encode($field_name, $field_value, $options = []) { return p\Iconv::iconv_mime_encode($field_name, $field_value, $options); }
}
if (!function_exists('iconv_mime_decode_headers')) {
    function iconv_mime_decode_headers($headers, $mode = 0, $encoding = null) { return p\Iconv::iconv_mime_decode_headers($headers, $mode, $encoding); }
}

if (extension_loaded('mbstring')) {
    if (!function_exists('iconv_strlen')) {
        function iconv_strlen($string, $encoding = null) { null === $encoding && $encoding = p\Iconv::$internalEncoding; return mb_strlen($string, $encoding); }
    }
    if (!function_exists('iconv_strpos')) {
        function iconv_strpos($haystack, $needle, $offset = 0, $encoding = null) { null === $encoding && $encoding = p\Iconv::$internalEncoding; return mb_strpos($haystack, $needle, $offset, $encoding); }
    }
    if (!function_exists('iconv_strrpos')) {
        function iconv_strrpos($haystack, $needle, $encoding = null) { null === $encoding && $encoding = p\Iconv::$internalEncoding; return mb_strrpos($haystack, $needle, 0, $encoding); }
    }
    if (!function_exists('iconv_substr')) {
        function iconv_substr($string, $offset, $length = 2147483647, $encoding = null) { null === $encoding && $encoding = p\Iconv::$internalEncoding; return mb_substr($string, $offset, $length, $encoding); }
    }
    if (!function_exists('iconv_mime_decode')) {
        function iconv_mime_decode($string, $mode = 0, $encoding = null) { $currentMbEncoding = mb_internal_encoding(); null === $encoding && $encoding = p\Iconv::$internalEncoding; mb_internal_encoding($encoding); $decoded = mb_decode_mimeheader($string); mb_internal_encoding($currentMbEncoding); return $decoded; }
    }
} else {
    if (!function_exists('iconv_strlen')) {
        function iconv_strlen($string, $encoding = null) { return p\Iconv::iconv_strlen($string, $encoding); }
    }

    if (!function_exists('iconv_strpos')) {
        function iconv_strpos($haystack, $needle, $offset = 0, $encoding = null) { return p\Iconv::iconv_strpos($haystack, $needle, $offset, $encoding); }
    }
    if (!function_exists('iconv_strrpos')) {
        function iconv_strrpos($haystack, $needle, $encoding = null) { return p\Iconv::iconv_strrpos($haystack, $needle, $encoding); }
    }
    if (!function_exists('iconv_substr')) {
        function iconv_substr($string, $offset, $length = 2147483647, $encoding = null) { return p\Iconv::iconv_substr($string, $offset, $length, $encoding); }
    }
    if (!function_exists('iconv_mime_decode')) {
        function iconv_mime_decode($string, $mode = 0, $encoding = null) { return p\Iconv::iconv_mime_decode($string, $mode, $encoding); }
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use Symfony\Polyfill\Iconv as p;

if (!defined('ICONV_IMPL')) {
    define('ICONV_IMPL', 'Symfony');
}
if (!defined('ICONV_VERSION')) {
    define('ICONV_VERSION', '1.0');
}
if (!defined('ICONV_MIME_DECODE_STRICT')) {
    define('ICONV_MIME_DECODE_STRICT', 1);
}
if (!defined('ICONV_MIME_DECODE_CONTINUE_ON_ERROR')) {
    define('ICONV_MIME_DECODE_CONTINUE_ON_ERROR', 2);
}

if (!function_exists('iconv')) {
    function iconv(?string $from_encoding, ?string $to_encoding, ?string $string): string|false { return p\Iconv::iconv((string) $from_encoding, (string) $to_encoding, (string) $string); }
}
if (!function_exists('iconv_get_encoding')) {
    function iconv_get_encoding(?string $type = 'all'): array|string|false { return p\Iconv::iconv_get_encoding((string) $type); }
}
if (!function_exists('iconv_set_encoding')) {
    function iconv_set_encoding(?string $type, ?string $encoding): bool { return p\Iconv::iconv_set_encoding((string) $type, (string) $encoding); }
}
if (!function_exists('iconv_mime_encode')) {
    function iconv_mime_encode(?string $field_name, ?string $field_value, ?array $options = []): string|false { return p\Iconv::iconv_mime_encode((string) $field_name, (string) $field_value, (array) $options); }
}
if (!function_exists('iconv_mime_decode_headers')) {
    function iconv_mime_decode_headers(?string $headers, ?int $mode = 0, ?string $encoding = null): array|false { return p\Iconv::iconv_mime_decode_headers((string) $headers, (int) $mode, $encoding); }
}

if (extension_loaded('mbstring')) {
    if (!function_exists('iconv_strlen')) {
        function iconv_strlen(?string $string, ?string $encoding = null): int|false { null === $encoding && $encoding = p\Iconv::$internalEncoding; return mb_strlen((string) $string, $encoding); }
    }
    if (!function_exists('iconv_strpos')) {
        function iconv_strpos(?string $haystack, ?string $needle, ?int $offset = 0, ?string $encoding = null): int|false { null === $encoding && $encoding = p\Iconv::$internalEncoding; return mb_strpos((string) $haystack, (string) $needle, (int) $offset, $encoding); }
    }
    if (!function_exists('iconv_strrpos')) {
        function iconv_strrpos(?string $haystack, ?string $needle, ?string $encoding = null): int|false { null === $encoding && $encoding = p\Iconv::$internalEncoding; return mb_strrpos((string) $haystack, (string) $needle, 0, $encoding); }
    }
    if (!function_exists('iconv_substr')) {
        function iconv_substr(?string $string, ?int $offset, ?int $length = null, ?string $encoding = null): string|false { null === $encoding && $encoding = p\Iconv::$internalEncoding; return mb_substr((string) $string, (int) $offset, $length, $encoding); }
    }
    if (!function_exists('iconv_mime_decode')) {
        function iconv_mime_decode($string, $mode = 0, $encoding = null) { $currentMbEncoding = mb_internal_encoding(); null === $encoding && $encoding = p\Iconv::$internalEncoding; mb_internal_encoding($encoding); $decoded = mb_decode_mimeheader($string); mb_internal_encoding($currentMbEncoding); return $decoded; }
    }
} else {
    if (!function_exists('iconv_strlen')) {
        function iconv_strlen(?string $string, ?string $encoding = null): int|false { return p\Iconv::iconv_strlen((string) $string, $encoding); }
    }

    if (!function_exists('iconv_strpos')) {
        function iconv_strpos(?string $haystack, ?string $needle, ?int $offset = 0, ?string $encoding = null): int|false { return p\Iconv::iconv_strpos((string) $haystack, (string) $needle, (int) $offset, $encoding); }
    }
    if (!function_exists('iconv_strrpos')) {
        function iconv_strrpos(?string $haystack, ?string $needle, ?string $encoding = null): int|false { return p\Iconv::iconv_strrpos((string) $haystack, (string) $needle, $encoding); }
    }
    if (!function_exists('iconv_substr')) {
        function iconv_substr(?string $string, ?int $offset, ?int $length = null, ?string $encoding = null): string|false { return p\Iconv::iconv_substr((string) $string, (string) $offset, $length ?? 2147483647, $encoding); }
    }
    if (!function_exists('iconv_mime_decode')) {
        function iconv_mime_decode(?string $string, ?int $mode = 0, ?string $encoding = null): string|false { return p\Iconv::iconv_mime_decode((string) $string, (int) $mode, $encoding); }
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Polyfill\Iconv;

/**
 * iconv implementation in pure PHP, UTF-8 centric.
 *
 * Implemented:
 * - iconv              - Convert string to requested character encoding
 * - iconv_mime_decode  - Decodes a MIME header field
 * - iconv_mime_decode_headers - Decodes multiple MIME header fields at once
 * - iconv_get_encoding - Retrieve internal configuration variables of iconv extension
 * - iconv_set_encoding - Set current setting for character encoding conversion
 * - iconv_mime_encode  - Composes a MIME header field
 * - iconv_strlen       - Returns the character count of string
 * - iconv_strpos       - Finds position of first occurrence of a needle within a haystack
 * - iconv_strrpos      - Finds the last occurrence of a needle within a haystack
 * - iconv_substr       - Cut out part of a string
 *
 * Charsets available for conversion are defined by files
 * in the charset/ directory and by Iconv::$alias below.
 * You're welcome to send back any addition you make.
 *
 * @author Nicolas Grekas <p@tchwork.com>
 *
 * @internal
 */
final class Iconv
{
    public const ERROR_ILLEGAL_CHARACTER = 'iconv(): Detected an illegal character in input string';
    public const ERROR_WRONG_CHARSET = 'iconv(): Wrong charset, conversion from `%s\' to `%s\' is not allowed';

    public static $inputEncoding = 'utf-8';
    public static $outputEncoding = 'utf-8';
    public static $internalEncoding = 'utf-8';

    private static $alias = [
        'utf8' => 'utf-8',
        'ascii' => 'us-ascii',
        'tis-620' => 'iso-8859-11',
        'cp1250' => 'windows-1250',
        'cp1251' => 'windows-1251',
        'cp1252' => 'windows-1252',
        'cp1253' => 'windows-1253',
        'cp1254' => 'windows-1254',
        'cp1255' => 'windows-1255',
        'cp1256' => 'windows-1256',
        'cp1257' => 'windows-1257',
        'cp1258' => 'windows-1258',
        'shift-jis' => 'cp932',
        'shift_jis' => 'cp932',
        'latin1' => 'iso-8859-1',
        'latin2' => 'iso-8859-2',
        'latin3' => 'iso-8859-3',
        'latin4' => 'iso-8859-4',
        'latin5' => 'iso-8859-9',
        'latin6' => 'iso-8859-10',
        'latin7' => 'iso-8859-13',
        'latin8' => 'iso-8859-14',
        'latin9' => 'iso-8859-15',
        'latin10' => 'iso-8859-16',
        'iso8859-1' => 'iso-8859-1',
        'iso8859-2' => 'iso-8859-2',
        'iso8859-3' => 'iso-8859-3',
        'iso8859-4' => 'iso-8859-4',
        'iso8859-5' => 'iso-8859-5',
        'iso8859-6' => 'iso-8859-6',
        'iso8859-7' => 'iso-8859-7',
        'iso8859-8' => 'iso-8859-8',
        'iso8859-9' => 'iso-8859-9',
        'iso8859-10' => 'iso-8859-10',
        'iso8859-11' => 'iso-8859-11',
        'iso8859-12' => 'iso-8859-12',
        'iso8859-13' => 'iso-8859-13',
        'iso8859-14' => 'iso-8859-14',
        'iso8859-15' => 'iso-8859-15',
        'iso8859-16' => 'iso-8859-16',
        'iso_8859-1' => 'iso-8859-1',
        'iso_8859-2' => 'iso-8859-2',
        'iso_8859-3' => 'iso-8859-3',
        'iso_8859-4' => 'iso-8859-4',
        'iso_8859-5' => 'iso-8859-5',
        'iso_8859-6' => 'iso-8859-6',
        'iso_8859-7' => 'iso-8859-7',
        'iso_8859-8' => 'iso-8859-8',
        'iso_8859-9' => 'iso-8859-9',
        'iso_8859-10' => 'iso-8859-10',
        'iso_8859-11' => 'iso-8859-11',
        'iso_8859-12' => 'iso-8859-12',
        'iso_8859-13' => 'iso-8859-13',
        'iso_8859-14' => 'iso-8859-14',
        'iso_8859-15' => 'iso-8859-15',
        'iso_8859-16' => 'iso-8859-16',
        'iso88591' => 'iso-8859-1',
        'iso88592' => 'iso-8859-2',
        'iso88593' => 'iso-8859-3',
        'iso88594' => 'iso-8859-4',
        'iso88595' => 'iso-8859-5',
        'iso88596' => 'iso-8859-6',
        'iso88597' => 'iso-8859-7',
        'iso88598' => 'iso-8859-8',
        'iso88599' => 'iso-8859-9',
        'iso885910' => 'iso-8859-10',
        'iso885911' => 'iso-8859-11',
        'iso885912' => 'iso-8859-12',
        'iso885913' => 'iso-8859-13',
        'iso885914' => 'iso-8859-14',
        'iso885915' => 'iso-8859-15',
        'iso885916' => 'iso-8859-16',
    ];
    private static $translitMap = [];
    private static $convertMap = [];
    private static $errorHandler;
    private static $lastError;

    private static $ulenMask = ["\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4];
    private static $isValidUtf8;

    public static function iconv($inCharset, $outCharset, $str)
    {
        $str = (string) $str;
        if ('' === $str) {
            return '';
        }

        // Prepare for //IGNORE and //TRANSLIT

        $translit = $ignore = '';

        $outCharset = strtolower($outCharset);
        $inCharset = strtolower($inCharset);

        if ('' === $outCharset) {
            $outCharset = 'iso-8859-1';
        }
        if ('' === $inCharset) {
            $inCharset = 'iso-8859-1';
        }

        do {
            $loop = false;

            if ('//translit' === substr($outCharset, -10)) {
                $loop = $translit = true;
                $outCharset = substr($outCharset, 0, -10);
            }

            if ('//ignore' === substr($outCharset, -8)) {
                $loop = $ignore = true;
                $outCharset = substr($outCharset, 0, -8);
            }
        } while ($loop);

        do {
            $loop = false;

            if ('//translit' === substr($inCharset, -10)) {
                $loop = true;
                $inCharset = substr($inCharset, 0, -10);
            }

            if ('//ignore' === substr($inCharset, -8)) {
                $loop = true;
                $inCharset = substr($inCharset, 0, -8);
            }
        } while ($loop);

        if (isset(self::$alias[$inCharset])) {
            $inCharset = self::$alias[$inCharset];
        }
        if (isset(self::$alias[$outCharset])) {
            $outCharset = self::$alias[$outCharset];
        }

        // Load charset maps

        if (('utf-8' !== $inCharset && !self::loadMap('from.', $inCharset, $inMap))
          || ('utf-8' !== $outCharset && !self::loadMap('to.', $outCharset, $outMap))) {
            trigger_error(\sprintf(self::ERROR_WRONG_CHARSET, $inCharset, $outCharset));

            return false;
        }

        if ('utf-8' !== $inCharset) {
            // Convert input to UTF-8
            $result = '';
            if (self::mapToUtf8($result, $inMap, $str, $ignore)) {
                $str = $result;
            } else {
                $str = false;
            }
            self::$isValidUtf8 = true;
        } else {
            self::$isValidUtf8 = preg_match('//u', $str);

            if (!self::$isValidUtf8 && !$ignore) {
                trigger_error(self::ERROR_ILLEGAL_CHARACTER);

                return false;
            }

            if ('utf-8' === $outCharset) {
                // UTF-8 validation
                $str = self::utf8ToUtf8($str, $ignore);
            }
        }

        if ('utf-8' !== $outCharset && false !== $str) {
            // Convert output to UTF-8
            $result = '';
            if (self::mapFromUtf8($result, $outMap, $str, $ignore, $translit)) {
                return $result;
            }

            return false;
        }

        return $str;
    }

    public static function iconv_mime_decode_headers($str, $mode = 0, $charset = null)
    {
        if (null === $charset) {
            $charset = self::$internalEncoding;
        }

        if (false !== strpos($str, "\r")) {
            $str = strtr(str_replace("\r\n", "\n", $str), "\r", "\n");
        }
        $str = explode("\n\n", $str, 2);

        $headers = [];

        $str = preg_split('/\n(?![ \t])/', $str[0]);
        foreach ($str as $str) {
            $str = self::iconv_mime_decode($str, $mode, $charset);
            if (false === $str) {
                return false;
            }
            $str = explode(':', $str, 2);

            if (2 === \count($str)) {
                if (isset($headers[$str[0]])) {
                    if (!\is_array($headers[$str[0]])) {
                        $headers[$str[0]] = [$headers[$str[0]]];
                    }
                    $headers[$str[0]][] = ltrim($str[1]);
                } else {
                    $headers[$str[0]] = ltrim($str[1]);
                }
            }
        }

        return $headers;
    }

    public static function iconv_mime_decode($str, $mode = 0, $charset = null)
    {
        if (null === $charset) {
            $charset = self::$internalEncoding;
        }
        if (\ICONV_MIME_DECODE_CONTINUE_ON_ERROR & $mode) {
            $charset .= '//IGNORE';
        }

        if (false !== strpos($str, "\r")) {
            $str = strtr(str_replace("\r\n", "\n", $str), "\r", "\n");
        }
        $str = preg_split('/\n(?![ \t])/', rtrim($str), 2);
        $str = preg_replace('/[ \t]*\n[ \t]+/', ' ', rtrim($str[0]));
        $str = preg_split('/=\?([^?]+)\?([bqBQ])\?(.*?)\?=/', $str, -1, \PREG_SPLIT_DELIM_CAPTURE);

        $result = self::iconv('utf-8', $charset, $str[0]);
        if (false === $result) {
            return false;
        }

        $i = 1;
        $len = \count($str);

        while ($i < $len) {
            $c = strtolower($str[$i]);
            if ((\ICONV_MIME_DECODE_CONTINUE_ON_ERROR & $mode)
              && 'utf-8' !== $c
              && !isset(self::$alias[$c])
              && !self::loadMap('from.', $c, $d)) {
                $d = false;
            } elseif ('B' === strtoupper($str[$i + 1])) {
                $d = base64_decode($str[$i + 2]);
            } else {
                $d = rawurldecode(strtr(str_replace('%', '%25', $str[$i + 2]), '=_', '% '));
            }

            if (false !== $d) {
                if ('' !== $d) {
                    if ('' === $d = self::iconv($c, $charset, $d)) {
                        $str[$i + 3] = substr($str[$i + 3], 1);
                    } else {
                        $result .= $d;
                    }
                }
                $d = self::iconv('utf-8', $charset, $str[$i + 3]);
                if ('' !== trim($d)) {
                    $result .= $d;
                }
            } elseif (\ICONV_MIME_DECODE_CONTINUE_ON_ERROR & $mode) {
                $result .= "=?{$str[$i]}?{$str[$i + 1]}?{$str[$i + 2]}?={$str[$i + 3]}";
            } else {
                $result = false;
                break;
            }

            $i += 4;
        }

        return $result;
    }

    public static function iconv_get_encoding($type = 'all')
    {
        switch ($type) {
            case 'input_encoding': return self::$inputEncoding;
            case 'output_encoding': return self::$outputEncoding;
            case 'internal_encoding': return self::$internalEncoding;
        }

        return [
            'input_encoding' => self::$inputEncoding,
            'output_encoding' => self::$outputEncoding,
            'internal_encoding' => self::$internalEncoding,
        ];
    }

    public static function iconv_set_encoding($type, $charset)
    {
        switch ($type) {
            case 'input_encoding': self::$inputEncoding = $charset; break;
            case 'output_encoding': self::$outputEncoding = $charset; break;
            case 'internal_encoding': self::$internalEncoding = $charset; break;
            default: return false;
        }

        return true;
    }

    public static function iconv_mime_encode($fieldName, $fieldValue, $pref = null)
    {
        if (!\is_array($pref)) {
            $pref = [];
        }

        $pref += [
            'scheme' => 'B',
            'input-charset' => self::$internalEncoding,
            'output-charset' => self::$internalEncoding,
            'line-length' => 76,
            'line-break-chars' => "\r\n",
        ];

        if (preg_match('/[\x80-\xFF]/', $fieldName)) {
            $fieldName = '';
        }

        $scheme = strtoupper(substr($pref['scheme'], 0, 1));
        $in = strtolower($pref['input-charset']);
        $out = strtolower($pref['output-charset']);

        if ('utf-8' !== $in && false === $fieldValue = self::iconv($in, 'utf-8', $fieldValue)) {
            return false;
        }

        preg_match_all('/./us', $fieldValue, $chars);

        $chars = $chars[0] ?? [];

        $lineBreak = (int) $pref['line-length'];
        $lineStart = "=?{$pref['output-charset']}?{$scheme}?";
        $lineLength = \strlen($fieldName) + 2 + \strlen($lineStart) + 2;
        $lineOffset = \strlen($lineStart) + 3;
        $lineData = '';

        $fieldValue = [];

        $Q = 'Q' === $scheme;

        foreach ($chars as $c) {
            if ('utf-8' !== $out && false === $c = self::iconv('utf-8', $out, $c)) {
                return false;
            }

            $o = $Q
                ? $c = preg_replace_callback(
                    '/[=_\?\x00-\x1F\x80-\xFF]/',
                    [__CLASS__, 'qpByteCallback'],
                    $c
                )
                : base64_encode($lineData.$c);

            if (isset($o[$lineBreak - $lineLength])) {
                if (!$Q) {
                    $lineData = base64_encode($lineData);
                }
                $fieldValue[] = $lineStart.$lineData.'?=';
                $lineLength = $lineOffset;
                $lineData = '';
            }

            $lineData .= $c;
            $Q && $lineLength += \strlen($c);
        }

        if ('' !== $lineData) {
            if (!$Q) {
                $lineData = base64_encode($lineData);
            }
            $fieldValue[] = $lineStart.$lineData.'?=';
        }

        return $fieldName.': '.implode($pref['line-break-chars'].' ', $fieldValue);
    }

    public static function iconv_strlen($s, $encoding = null)
    {
        if (null === $encoding) {
            $encoding = self::$internalEncoding;
        }
        if (0 !== stripos($encoding, 'utf-8') && false === $s = self::iconv($encoding, 'utf-8', $s)) {
            return false;
        }

        $ulenMask = self::$ulenMask;

        $i = 0;
        $j = 0;
        $len = \strlen($s);

        while ($i < $len) {
            $u = $s[$i] & "\xF0";
            $i += $ulenMask[$u] ?? 1;
            ++$j;
        }

        return $j;
    }

    public static function iconv_strpos($haystack, $needle, $offset = 0, $encoding = null)
    {
        if (null === $encoding) {
            $encoding = self::$internalEncoding;
        }

        if (0 !== stripos($encoding, 'utf-8')) {
            if (false === $haystack = self::iconv($encoding, 'utf-8', $haystack)) {
                return false;
            }
            if (false === $needle = self::iconv($encoding, 'utf-8', $needle)) {
                return false;
            }
        }

        if ($offset = (int) $offset) {
            $haystack = self::iconv_substr($haystack, $offset, 2147483647, 'utf-8');
        }
        $pos = strpos($haystack, $needle);

        return false === $pos ? false : ($offset + ($pos ? self::iconv_strlen(substr($haystack, 0, $pos), 'utf-8') : 0));
    }

    public static function iconv_strrpos($haystack, $needle, $encoding = null)
    {
        if (null === $encoding) {
            $encoding = self::$internalEncoding;
        }

        if (0 !== stripos($encoding, 'utf-8')) {
            if (false === $haystack = self::iconv($encoding, 'utf-8', $haystack)) {
                return false;
            }
            if (false === $needle = self::iconv($encoding, 'utf-8', $needle)) {
                return false;
            }
        }

        $pos = isset($needle[0]) ? strrpos($haystack, $needle) : false;

        return false === $pos ? false : self::iconv_strlen(substr($haystack, 0, $pos), 'utf-8');
    }

    public static function iconv_substr($s, $start, $length = 2147483647, $encoding = null)
    {
        if (null === $encoding) {
            $encoding = self::$internalEncoding;
        }
        if (false === $s = self::iconv($encoding, 'utf-8', $s)) {
            return false;
        }
        if (0 === stripos($encoding, 'utf-8')) {
            $encoding = null;
        }

        $s = (string) $s;
        $slen = self::iconv_strlen($s, 'utf-8');
        $start = (int) $start;

        if (0 > $start) {
            $start += $slen;
        }
        if (0 > $start) {
            if (\PHP_VERSION_ID < 80000) {
                return false;
            }

            $start = 0;
        }
        if ($start >= $slen) {
            return \PHP_VERSION_ID >= 80000 ? '' : false;
        }

        $rx = $slen - $start;

        if (0 > $length) {
            $length += $rx;
        }
        if (0 === $length) {
            return '';
        }
        if (0 > $length) {
            return \PHP_VERSION_ID >= 80000 ? '' : false;
        }

        if ($length > $rx) {
            $length = $rx;
        }

        $rx = '/^'.($start ? self::pregOffset($start) : '').'('.self::pregOffset($length).')/u';

        $s = preg_match($rx, $s, $s) ? $s[1] : '';

        if (null === $encoding) {
            return $s;
        }

        return self::iconv('utf-8', $encoding, $s);
    }

    private static function loadMap($type, $charset, &$map)
    {
        if (!isset(self::$convertMap[$type.$charset])) {
            if (false === $map = self::getData($type.$charset)) {
                if ('to.' === $type && self::loadMap('from.', $charset, $map)) {
                    $map = array_flip($map);
                } else {
                    return false;
                }
            }

            self::$convertMap[$type.$charset] = $map;
        } else {
            $map = self::$convertMap[$type.$charset];
        }

        return true;
    }

    private static function utf8ToUtf8($str, $ignore)
    {
        $ulenMask = self::$ulenMask;
        $valid = self::$isValidUtf8;

        $u = $str;
        $i = $j = 0;
        $len = \strlen($str);

        while ($i < $len) {
            if ($str[$i] < "\x80") {
                $u[$j++] = $str[$i++];
            } else {
                $ulen = $str[$i] & "\xF0";
                $ulen = $ulenMask[$ulen] ?? 1;
                $uchr = substr($str, $i, $ulen);

                if (1 === $ulen || !($valid || preg_match('/^.$/us', $uchr))) {
                    if ($ignore) {
                        ++$i;
                        continue;
                    }

                    trigger_error(self::ERROR_ILLEGAL_CHARACTER);

                    return false;
                }

                $i += $ulen;

                $u[$j++] = $uchr[0];

                isset($uchr[1]) && 0 !== ($u[$j++] = $uchr[1])
                    && isset($uchr[2]) && 0 !== ($u[$j++] = $uchr[2])
                    && isset($uchr[3]) && 0 !== ($u[$j++] = $uchr[3]);
            }
        }

        return substr($u, 0, $j);
    }

    private static function mapToUtf8(&$result, array $map, $str, $ignore)
    {
        $len = \strlen($str);
        for ($i = 0; $i < $len; ++$i) {
            if (isset($str[$i + 1], $map[$str[$i].$str[$i + 1]])) {
                $result .= $map[$str[$i].$str[++$i]];
            } elseif (isset($map[$str[$i]])) {
                $result .= $map[$str[$i]];
            } elseif (!$ignore) {
                trigger_error(self::ERROR_ILLEGAL_CHARACTER);

                return false;
            }
        }

        return true;
    }

    private static function mapFromUtf8(&$result, array $map, $str, $ignore, $translit)
    {
        $ulenMask = self::$ulenMask;
        $valid = self::$isValidUtf8;

        if ($translit && !self::$translitMap) {
            self::$translitMap = self::getData('translit');
        }

        $i = 0;
        $len = \strlen($str);

        while ($i < $len) {
            if ($str[$i] < "\x80") {
                $uchr = $str[$i++];
            } else {
                $ulen = $str[$i] & "\xF0";
                $ulen = $ulenMask[$ulen] ?? 1;
                $uchr = substr($str, $i, $ulen);

                if ($ignore && (1 === $ulen || !($valid || preg_match('/^.$/us', $uchr)))) {
                    ++$i;
                    continue;
                }

                $i += $ulen;
            }

            if (isset($map[$uchr])) {
                $result .= $map[$uchr];
            } elseif ($translit) {
                if (isset(self::$translitMap[$uchr])) {
                    $uchr = self::$translitMap[$uchr];
                } elseif ($ignore) {
                    continue;
                } else {
                    return false;
                }

                $str = $uchr.substr($str, $i);
                $len = \strlen($str);
                $i = 0;
            } elseif (!$ignore) {
                return false;
            }
        }

        return true;
    }

    private static function qpByteCallback(array $m)
    {
        return '='.strtoupper(dechex(\ord($m[0])));
    }

    private static function pregOffset($offset)
    {
        $rx = [];
        $offset = (int) $offset;

        while ($offset > 65535) {
            $rx[] = '.{65535}';
            $offset -= 65535;
        }

        return implode('', $rx).'.{'.$offset.'}';
    }

    private static function getData($file)
    {
        if (preg_match('/^[a-z0-9._-]++$/D', $file) && file_exists($file = __DIR__.'/Resources/charset/'.$file.'.php')) {
            return require $file;
        }

        return false;
    }
}
{
    "name": "symfony/polyfill-iconv",
    "type": "library",
    "description": "Symfony polyfill for the Iconv extension",
    "keywords": ["polyfill", "shim", "compatibility", "portable", "iconv"],
    "homepage": "https://symfony.com",
    "license": "MIT",
    "authors": [
        {
            "name": "Nicolas Grekas",
            "email": "p@tchwork.com"
        },
        {
            "name": "Symfony Community",
            "homepage": "https://symfony.com/contributors"
        }
    ],
    "require": {
        "php": ">=7.2"
    },
    "provide": {
        "ext-iconv": "*"
    },
    "autoload": {
        "psr-4": { "Symfony\\Polyfill\\Iconv\\": "" },
        "files": [ "bootstrap.php" ]
    },
    "suggest": {
        "ext-iconv": "For best performance"
    },
    "minimum-stability": "dev",
    "extra": {
        "thanks": {
            "name": "symfony/polyfill",
            "url": "https://github.com/symfony/polyfill"
        }
    }
}
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => 'Ùª',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
  '€' => 'Â°',
  '�' => 'Â·',
  '‚' => 'âˆ™',
  'ƒ' => 'âˆš',
  '„' => 'â–’',
  '…' => 'â”€',
  '†' => 'â”‚',
  '‡' => 'â”¼',
  'ˆ' => 'â”¤',
  '‰' => 'â”¬',
  'Š' => 'â”œ',
  '‹' => 'â”´',
  'Œ' => 'â”�',
  '�' => 'â”Œ',
  'Ž' => 'â””',
  '�' => 'â”˜',
  '�' => 'Î²',
  '‘' => 'âˆž',
  '’' => 'Ï†',
  '“' => 'Â±',
  '”' => 'Â½',
  '•' => 'Â¼',
  '–' => 'â‰ˆ',
  '—' => 'Â«',
  '˜' => 'Â»',
  '™' => 'ï»·',
  'š' => 'ï»¸',
  '�' => 'ï»»',
  'ž' => 'ï»¼',
  ' ' => 'Â ',
  '¡' => 'Â­',
  '¢' => 'ïº‚',
  '£' => 'Â£',
  '¤' => 'Â¤',
  '¥' => 'ïº„',
  '¨' => 'ïºŽ',
  '©' => 'ïº�',
  'ª' => 'ïº•',
  '«' => 'ïº™',
  '¬' => 'ØŒ',
  '­' => 'ïº�',
  '®' => 'ïº¡',
  '¯' => 'ïº¥',
  '°' => 'Ù ',
  '±' => 'Ù¡',
  '²' => 'Ù¢',
  '³' => 'Ù£',
  '´' => 'Ù¤',
  'µ' => 'Ù¥',
  '¶' => 'Ù¦',
  '·' => 'Ù§',
  '¸' => 'Ù¨',
  '¹' => 'Ù©',
  'º' => 'ï»‘',
  '»' => 'Ø›',
  '¼' => 'ïº±',
  '½' => 'ïºµ',
  '¾' => 'ïº¹',
  '¿' => 'ØŸ',
  'À' => 'Â¢',
  'Á' => 'ïº€',
  'Â' => 'ïº�',
  'Ã' => 'ïºƒ',
  'Ä' => 'ïº…',
  'Å' => 'ï»Š',
  'Æ' => 'ïº‹',
  'Ç' => 'ïº�',
  'È' => 'ïº‘',
  'É' => 'ïº“',
  'Ê' => 'ïº—',
  'Ë' => 'ïº›',
  'Ì' => 'ïºŸ',
  'Í' => 'ïº£',
  'Î' => 'ïº§',
  'Ï' => 'ïº©',
  'Ð' => 'ïº«',
  'Ñ' => 'ïº­',
  'Ò' => 'ïº¯',
  'Ó' => 'ïº³',
  'Ô' => 'ïº·',
  'Õ' => 'ïº»',
  'Ö' => 'ïº¿',
  '×' => 'ï»�',
  'Ø' => 'ï»…',
  'Ù' => 'ï»‹',
  'Ú' => 'ï»�',
  'Û' => 'Â¦',
  'Ü' => 'Â¬',
  'Ý' => 'Ã·',
  'Þ' => 'Ã—',
  'ß' => 'ï»‰',
  'à' => 'Ù€',
  'á' => 'ï»“',
  'â' => 'ï»—',
  'ã' => 'ï»›',
  'ä' => 'ï»Ÿ',
  'å' => 'ï»£',
  'æ' => 'ï»§',
  'ç' => 'ï»«',
  'è' => 'ï»­',
  'é' => 'ï»¯',
  'ê' => 'ï»³',
  'ë' => 'ïº½',
  'ì' => 'ï»Œ',
  'í' => 'ï»Ž',
  'î' => 'ï»�',
  'ï' => 'ï»¡',
  'ð' => 'ï¹½',
  'ñ' => 'Ù‘',
  'ò' => 'ï»¥',
  'ó' => 'ï»©',
  'ô' => 'ï»¬',
  'õ' => 'ï»°',
  'ö' => 'ï»²',
  '÷' => 'ï»�',
  'ø' => 'ï»•',
  'ù' => 'ï»µ',
  'ú' => 'ï»¶',
  'û' => 'ï»�',
  'ü' => 'ï»™',
  'ý' => 'ï»±',
  'þ' => 'â– ',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => '%',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
  '€' => 'Ð�',
  '�' => 'Ð‘',
  '‚' => 'Ð’',
  'ƒ' => 'Ð“',
  '„' => 'Ð”',
  '…' => 'Ð•',
  '†' => 'Ð–',
  '‡' => 'Ð—',
  'ˆ' => 'Ð˜',
  '‰' => 'Ð™',
  'Š' => 'Ðš',
  '‹' => 'Ð›',
  'Œ' => 'Ðœ',
  '�' => 'Ð�',
  'Ž' => 'Ðž',
  '�' => 'ÐŸ',
  '�' => 'Ð ',
  '‘' => 'Ð¡',
  '’' => 'Ð¢',
  '“' => 'Ð£',
  '”' => 'Ð¤',
  '•' => 'Ð¥',
  '–' => 'Ð¦',
  '—' => 'Ð§',
  '˜' => 'Ð¨',
  '™' => 'Ð©',
  'š' => 'Ðª',
  '›' => 'Ð«',
  'œ' => 'Ð¬',
  '�' => 'Ð­',
  'ž' => 'Ð®',
  'Ÿ' => 'Ð¯',
  ' ' => 'Ð°',
  '¡' => 'Ð±',
  '¢' => 'Ð²',
  '£' => 'Ð³',
  '¤' => 'Ð´',
  '¥' => 'Ðµ',
  '¦' => 'Ð¶',
  '§' => 'Ð·',
  '¨' => 'Ð¸',
  '©' => 'Ð¹',
  'ª' => 'Ðº',
  '«' => 'Ð»',
  '¬' => 'Ð¼',
  '­' => 'Ð½',
  '®' => 'Ð¾',
  '¯' => 'Ð¿',
  '°' => 'â–‘',
  '±' => 'â–’',
  '²' => 'â–“',
  '³' => 'â”‚',
  '´' => 'â”¤',
  'µ' => 'â•¡',
  '¶' => 'â•¢',
  '·' => 'â•–',
  '¸' => 'â••',
  '¹' => 'â•£',
  'º' => 'â•‘',
  '»' => 'â•—',
  '¼' => 'â•�',
  '½' => 'â•œ',
  '¾' => 'â•›',
  '¿' => 'â”�',
  'À' => 'â””',
  'Á' => 'â”´',
  'Â' => 'â”¬',
  'Ã' => 'â”œ',
  'Ä' => 'â”€',
  'Å' => 'â”¼',
  'Æ' => 'â•ž',
  'Ç' => 'â•Ÿ',
  'È' => 'â•š',
  'É' => 'â•”',
  'Ê' => 'â•©',
  'Ë' => 'â•¦',
  'Ì' => 'â• ',
  'Í' => 'â•�',
  'Î' => 'â•¬',
  'Ï' => 'â•§',
  'Ð' => 'â•¨',
  'Ñ' => 'â•¤',
  'Ò' => 'â•¥',
  'Ó' => 'â•™',
  'Ô' => 'â•˜',
  'Õ' => 'â•’',
  'Ö' => 'â•“',
  '×' => 'â•«',
  'Ø' => 'â•ª',
  'Ù' => 'â”˜',
  'Ú' => 'â”Œ',
  'Û' => 'â–ˆ',
  'Ü' => 'â–„',
  'Ý' => 'â–Œ',
  'Þ' => 'â–�',
  'ß' => 'â–€',
  'à' => 'Ñ€',
  'á' => 'Ñ�',
  'â' => 'Ñ‚',
  'ã' => 'Ñƒ',
  'ä' => 'Ñ„',
  'å' => 'Ñ…',
  'æ' => 'Ñ†',
  'ç' => 'Ñ‡',
  'è' => 'Ñˆ',
  'é' => 'Ñ‰',
  'ê' => 'ÑŠ',
  'ë' => 'Ñ‹',
  'ì' => 'ÑŒ',
  'í' => 'Ñ�',
  'î' => 'ÑŽ',
  'ï' => 'Ñ�',
  'ð' => 'Ð�',
  'ñ' => 'Ñ‘',
  'ò' => 'Ð„',
  'ó' => 'Ñ”',
  'ô' => 'Ð‡',
  'õ' => 'Ñ—',
  'ö' => 'ÐŽ',
  '÷' => 'Ñž',
  'ø' => 'Â°',
  'ù' => 'âˆ™',
  'ú' => 'Â·',
  'û' => 'âˆš',
  'ü' => 'â„–',
  'ý' => 'Â¤',
  'þ' => 'â– ',
  'ÿ' => 'Â ',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => '%',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
  '€' => 'â‚¬',
  '…' => 'â€¦',
  '‘' => 'â€˜',
  '’' => 'â€™',
  '“' => 'â€œ',
  '”' => 'â€�',
  '•' => 'â€¢',
  '–' => 'â€“',
  '—' => 'â€”',
  ' ' => 'Â ',
  '¡' => 'à¸�',
  '¢' => 'à¸‚',
  '£' => 'à¸ƒ',
  '¤' => 'à¸„',
  '¥' => 'à¸…',
  '¦' => 'à¸†',
  '§' => 'à¸‡',
  '¨' => 'à¸ˆ',
  '©' => 'à¸‰',
  'ª' => 'à¸Š',
  '«' => 'à¸‹',
  '¬' => 'à¸Œ',
  '­' => 'à¸�',
  '®' => 'à¸Ž',
  '¯' => 'à¸�',
  '°' => 'à¸�',
  '±' => 'à¸‘',
  '²' => 'à¸’',
  '³' => 'à¸“',
  '´' => 'à¸”',
  'µ' => 'à¸•',
  '¶' => 'à¸–',
  '·' => 'à¸—',
  '¸' => 'à¸˜',
  '¹' => 'à¸™',
  'º' => 'à¸š',
  '»' => 'à¸›',
  '¼' => 'à¸œ',
  '½' => 'à¸�',
  '¾' => 'à¸ž',
  '¿' => 'à¸Ÿ',
  'À' => 'à¸ ',
  'Á' => 'à¸¡',
  'Â' => 'à¸¢',
  'Ã' => 'à¸£',
  'Ä' => 'à¸¤',
  'Å' => 'à¸¥',
  'Æ' => 'à¸¦',
  'Ç' => 'à¸§',
  'È' => 'à¸¨',
  'É' => 'à¸©',
  'Ê' => 'à¸ª',
  'Ë' => 'à¸«',
  'Ì' => 'à¸¬',
  'Í' => 'à¸­',
  'Î' => 'à¸®',
  'Ï' => 'à¸¯',
  'Ð' => 'à¸°',
  'Ñ' => 'à¸±',
  'Ò' => 'à¸²',
  'Ó' => 'à¸³',
  'Ô' => 'à¸´',
  'Õ' => 'à¸µ',
  'Ö' => 'à¸¶',
  '×' => 'à¸·',
  'Ø' => 'à¸¸',
  'Ù' => 'à¸¹',
  'Ú' => 'à¸º',
  'ß' => 'à¸¿',
  'à' => 'à¹€',
  'á' => 'à¹�',
  'â' => 'à¹‚',
  'ã' => 'à¹ƒ',
  'ä' => 'à¹„',
  'å' => 'à¹…',
  'æ' => 'à¹†',
  'ç' => 'à¹‡',
  'è' => 'à¹ˆ',
  'é' => 'à¹‰',
  'ê' => 'à¹Š',
  'ë' => 'à¹‹',
  'ì' => 'à¹Œ',
  'í' => 'à¹�',
  'î' => 'à¹Ž',
  'ï' => 'à¹�',
  'ð' => 'à¹�',
  'ñ' => 'à¹‘',
  'ò' => 'à¹’',
  'ó' => 'à¹“',
  'ô' => 'à¹”',
  'õ' => 'à¹•',
  'ö' => 'à¹–',
  '÷' => 'à¹—',
  'ø' => 'à¹˜',
  'ù' => 'à¹™',
  'ú' => 'à¹š',
  'û' => 'à¹›',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => '%',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
  '€' => 'Â€',
  '�' => 'Â�',
  '‚' => 'Â‚',
  'ƒ' => 'Âƒ',
  '„' => 'Â„',
  '…' => 'Â…',
  '†' => 'Â†',
  '‡' => 'Â‡',
  'ˆ' => 'Âˆ',
  '‰' => 'Â‰',
  'Š' => 'ÂŠ',
  '‹' => 'Â‹',
  'Œ' => 'ÂŒ',
  '�' => 'Â�',
  'Ž' => 'ÂŽ',
  '�' => 'Â�',
  '�' => 'Â�',
  '‘' => 'Â‘',
  '’' => 'Â’',
  '“' => 'Â“',
  '”' => 'Â”',
  '•' => 'Â•',
  '–' => 'Â–',
  '—' => 'Â—',
  '˜' => 'Â˜',
  '™' => 'Â™',
  'š' => 'Âš',
  '›' => 'Â›',
  'œ' => 'Âœ',
  '�' => 'Â�',
  'ž' => 'Âž',
  'Ÿ' => 'ÂŸ',
  ' ' => 'Â ',
  '¡' => 'Ä¦',
  '¢' => 'Ë˜',
  '£' => 'Â£',
  '¤' => 'Â¤',
  '¦' => 'Ä¤',
  '§' => 'Â§',
  '¨' => 'Â¨',
  '©' => 'Ä°',
  'ª' => 'Åž',
  '«' => 'Äž',
  '¬' => 'Ä´',
  '­' => 'Â­',
  '¯' => 'Å»',
  '°' => 'Â°',
  '±' => 'Ä§',
  '²' => 'Â²',
  '³' => 'Â³',
  '´' => 'Â´',
  'µ' => 'Âµ',
  '¶' => 'Ä¥',
  '·' => 'Â·',
  '¸' => 'Â¸',
  '¹' => 'Ä±',
  'º' => 'ÅŸ',
  '»' => 'ÄŸ',
  '¼' => 'Äµ',
  '½' => 'Â½',
  '¿' => 'Å¼',
  'À' => 'Ã€',
  'Á' => 'Ã�',
  'Â' => 'Ã‚',
  'Ä' => 'Ã„',
  'Å' => 'ÄŠ',
  'Æ' => 'Äˆ',
  'Ç' => 'Ã‡',
  'È' => 'Ãˆ',
  'É' => 'Ã‰',
  'Ê' => 'ÃŠ',
  'Ë' => 'Ã‹',
  'Ì' => 'ÃŒ',
  'Í' => 'Ã�',
  'Î' => 'ÃŽ',
  'Ï' => 'Ã�',
  'Ñ' => 'Ã‘',
  'Ò' => 'Ã’',
  'Ó' => 'Ã“',
  'Ô' => 'Ã”',
  'Õ' => 'Ä ',
  'Ö' => 'Ã–',
  '×' => 'Ã—',
  'Ø' => 'Äœ',
  'Ù' => 'Ã™',
  'Ú' => 'Ãš',
  'Û' => 'Ã›',
  'Ü' => 'Ãœ',
  'Ý' => 'Å¬',
  'Þ' => 'Åœ',
  'ß' => 'ÃŸ',
  'à' => 'Ã ',
  'á' => 'Ã¡',
  'â' => 'Ã¢',
  'ä' => 'Ã¤',
  'å' => 'Ä‹',
  'æ' => 'Ä‰',
  'ç' => 'Ã§',
  'è' => 'Ã¨',
  'é' => 'Ã©',
  'ê' => 'Ãª',
  'ë' => 'Ã«',
  'ì' => 'Ã¬',
  'í' => 'Ã­',
  'î' => 'Ã®',
  'ï' => 'Ã¯',
  'ñ' => 'Ã±',
  'ò' => 'Ã²',
  'ó' => 'Ã³',
  'ô' => 'Ã´',
  'õ' => 'Ä¡',
  'ö' => 'Ã¶',
  '÷' => 'Ã·',
  'ø' => 'Ä�',
  'ù' => 'Ã¹',
  'ú' => 'Ãº',
  'û' => 'Ã»',
  'ü' => 'Ã¼',
  'ý' => 'Å­',
  'þ' => 'Å�',
  'ÿ' => 'Ë™',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => '%',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
  '€' => 'Î‘',
  '�' => 'Î’',
  '‚' => 'Î“',
  'ƒ' => 'Î”',
  '„' => 'Î•',
  '…' => 'Î–',
  '†' => 'Î—',
  '‡' => 'Î˜',
  'ˆ' => 'Î™',
  '‰' => 'Îš',
  'Š' => 'Î›',
  '‹' => 'Îœ',
  'Œ' => 'Î�',
  '�' => 'Îž',
  'Ž' => 'ÎŸ',
  '�' => 'Î ',
  '�' => 'Î¡',
  '‘' => 'Î£',
  '’' => 'Î¤',
  '“' => 'Î¥',
  '”' => 'Î¦',
  '•' => 'Î§',
  '–' => 'Î¨',
  '—' => 'Î©',
  '˜' => 'Î±',
  '™' => 'Î²',
  'š' => 'Î³',
  '›' => 'Î´',
  'œ' => 'Îµ',
  '�' => 'Î¶',
  'ž' => 'Î·',
  'Ÿ' => 'Î¸',
  ' ' => 'Î¹',
  '¡' => 'Îº',
  '¢' => 'Î»',
  '£' => 'Î¼',
  '¤' => 'Î½',
  '¥' => 'Î¾',
  '¦' => 'Î¿',
  '§' => 'Ï€',
  '¨' => 'Ï�',
  '©' => 'Ïƒ',
  'ª' => 'Ï‚',
  '«' => 'Ï„',
  '¬' => 'Ï…',
  '­' => 'Ï†',
  '®' => 'Ï‡',
  '¯' => 'Ïˆ',
  '°' => 'â–‘',
  '±' => 'â–’',
  '²' => 'â–“',
  '³' => 'â”‚',
  '´' => 'â”¤',
  'µ' => 'â•¡',
  '¶' => 'â•¢',
  '·' => 'â•–',
  '¸' => 'â••',
  '¹' => 'â•£',
  'º' => 'â•‘',
  '»' => 'â•—',
  '¼' => 'â•�',
  '½' => 'â•œ',
  '¾' => 'â•›',
  '¿' => 'â”�',
  'À' => 'â””',
  'Á' => 'â”´',
  'Â' => 'â”¬',
  'Ã' => 'â”œ',
  'Ä' => 'â”€',
  'Å' => 'â”¼',
  'Æ' => 'â•ž',
  'Ç' => 'â•Ÿ',
  'È' => 'â•š',
  'É' => 'â•”',
  'Ê' => 'â•©',
  'Ë' => 'â•¦',
  'Ì' => 'â• ',
  'Í' => 'â•�',
  'Î' => 'â•¬',
  'Ï' => 'â•§',
  'Ð' => 'â•¨',
  'Ñ' => 'â•¤',
  'Ò' => 'â•¥',
  'Ó' => 'â•™',
  'Ô' => 'â•˜',
  'Õ' => 'â•’',
  'Ö' => 'â•“',
  '×' => 'â•«',
  'Ø' => 'â•ª',
  'Ù' => 'â”˜',
  'Ú' => 'â”Œ',
  'Û' => 'â–ˆ',
  'Ü' => 'â–„',
  'Ý' => 'â–Œ',
  'Þ' => 'â–�',
  'ß' => 'â–€',
  'à' => 'Ï‰',
  'á' => 'Î¬',
  'â' => 'Î­',
  'ã' => 'Î®',
  'ä' => 'ÏŠ',
  'å' => 'Î¯',
  'æ' => 'ÏŒ',
  'ç' => 'Ï�',
  'è' => 'Ï‹',
  'é' => 'ÏŽ',
  'ê' => 'Î†',
  'ë' => 'Îˆ',
  'ì' => 'Î‰',
  'í' => 'ÎŠ',
  'î' => 'ÎŒ',
  'ï' => 'ÎŽ',
  'ð' => 'Î�',
  'ñ' => 'Â±',
  'ò' => 'â‰¥',
  'ó' => 'â‰¤',
  'ô' => 'Îª',
  'õ' => 'Î«',
  'ö' => 'Ã·',
  '÷' => 'â‰ˆ',
  'ø' => 'Â°',
  'ù' => 'âˆ™',
  'ú' => 'Â·',
  'û' => 'âˆš',
  'ü' => 'â�¿',
  'ý' => 'Â²',
  'þ' => 'â– ',
  'ÿ' => 'Â ',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  '¡@' => 'ã€€',
  '¡A' => 'ï¼Œ',
  '¡B' => 'ã€�',
  '¡C' => 'ã€‚',
  '¡D' => 'ï¼Ž',
  '¡E' => 'â€¢',
  '¡F' => 'ï¼›',
  '¡G' => 'ï¼š',
  '¡H' => 'ï¼Ÿ',
  '¡I' => 'ï¼�',
  '¡J' => 'ï¸°',
  '¡K' => 'â€¦',
  '¡L' => 'â€¥',
  '¡M' => 'ï¹�',
  '¡N' => 'ï½¤',
  '¡O' => 'ï¹’',
  '¡P' => 'Â·',
  '¡Q' => 'ï¹”',
  '¡R' => 'ï¹•',
  '¡S' => 'ï¹–',
  '¡T' => 'ï¹—',
  '¡U' => 'ï½œ',
  '¡V' => 'â€“',
  '¡W' => 'ï¸±',
  '¡X' => 'â€”',
  '¡Y' => 'ï¸³',
  '¡Z' => 'ï¿½',
  '¡[' => 'ï¸´',
  '¡\\' => 'ï¹�',
  '¡]' => 'ï¼ˆ',
  '¡^' => 'ï¼‰',
  '¡_' => 'ï¸µ',
  '¡`' => 'ï¸¶',
  '¡a' => 'ï½›',
  '¡b' => 'ï½�',
  '¡c' => 'ï¸·',
  '¡d' => 'ï¸¸',
  '¡e' => 'ã€”',
  '¡f' => 'ã€•',
  '¡g' => 'ï¸¹',
  '¡h' => 'ï¸º',
  '¡i' => 'ã€�',
  '¡j' => 'ã€‘',
  '¡k' => 'ï¸»',
  '¡l' => 'ï¸¼',
  '¡m' => 'ã€Š',
  '¡n' => 'ã€‹',
  '¡o' => 'ï¸½',
  '¡p' => 'ï¸¾',
  '¡q' => 'ã€ˆ',
  '¡r' => 'ã€‰',
  '¡s' => 'ï¸¿',
  '¡t' => 'ï¹€',
  '¡u' => 'ã€Œ',
  '¡v' => 'ã€�',
  '¡w' => 'ï¹�',
  '¡x' => 'ï¹‚',
  '¡y' => 'ã€Ž',
  '¡z' => 'ã€�',
  '¡{' => 'ï¹ƒ',
  '¡|' => 'ï¹„',
  '¡}' => 'ï¹™',
  '¡~' => 'ï¹š',
  '¡¡' => 'ï¹›',
  '¡¢' => 'ï¹œ',
  '¡£' => 'ï¹�',
  '¡¤' => 'ï¹ž',
  '¡¥' => 'â€˜',
  '¡¦' => 'â€™',
  '¡§' => 'â€œ',
  '¡¨' => 'â€�',
  '¡©' => 'ã€�',
  '¡ª' => 'ã€ž',
  '¡«' => 'â€µ',
  '¡¬' => 'â€²',
  '¡­' => 'ï¼ƒ',
  '¡®' => 'ï¼†',
  '¡¯' => 'ï¼Š',
  '¡°' => 'â€»',
  '¡±' => 'Â§',
  '¡²' => 'ã€ƒ',
  '¡³' => 'â—‹',
  '¡´' => 'â—�',
  '¡µ' => 'â–³',
  '¡¶' => 'â–²',
  '¡·' => 'â—Ž',
  '¡¸' => 'â˜†',
  '¡¹' => 'â˜…',
  '¡º' => 'â—‡',
  '¡»' => 'â—†',
  '¡¼' => 'â–¡',
  '¡½' => 'â– ',
  '¡¾' => 'â–½',
  '¡¿' => 'â–¼',
  '¡À' => 'ãŠ£',
  '¡Á' => 'â„…',
  '¡Â' => 'â€¾',
  '¡Ã' => 'ï¿½',
  '¡Ä' => 'ï¼¿',
  '¡Å' => 'ï¿½',
  '¡Æ' => 'ï¹‰',
  '¡Ç' => 'ï¹Š',
  '¡È' => 'ï¹�',
  '¡É' => 'ï¹Ž',
  '¡Ê' => 'ï¹‹',
  '¡Ë' => 'ï¹Œ',
  '¡Ì' => 'ï¹Ÿ',
  '¡Í' => 'ï¹ ',
  '¡Î' => 'ï¹¡',
  '¡Ï' => 'ï¼‹',
  '¡Ð' => 'ï¼�',
  '¡Ñ' => 'Ã—',
  '¡Ò' => 'Ã·',
  '¡Ó' => 'Â±',
  '¡Ô' => 'âˆš',
  '¡Õ' => 'ï¼œ',
  '¡Ö' => 'ï¼ž',
  '¡×' => 'ï¼�',
  '¡Ø' => 'â‰¦',
  '¡Ù' => 'â‰§',
  '¡Ú' => 'â‰ ',
  '¡Û' => 'âˆž',
  '¡Ü' => 'â‰’',
  '¡Ý' => 'â‰¡',
  '¡Þ' => 'ï¹¢',
  '¡ß' => 'ï¹£',
  '¡à' => 'ï¹¤',
  '¡á' => 'ï¹¥',
  '¡â' => 'ï¹¦',
  '¡ã' => 'âˆ¼',
  '¡ä' => 'âˆ©',
  '¡å' => 'âˆª',
  '¡æ' => 'âŠ¥',
  '¡ç' => 'âˆ ',
  '¡è' => 'âˆŸ',
  '¡é' => 'âŠ¿',
  '¡ê' => 'ã�’',
  '¡ë' => 'ã�‘',
  '¡ì' => 'âˆ«',
  '¡í' => 'âˆ®',
  '¡î' => 'âˆµ',
  '¡ï' => 'âˆ´',
  '¡ð' => 'â™€',
  '¡ñ' => 'â™‚',
  '¡ò' => 'â™�',
  '¡ó' => 'â˜‰',
  '¡ô' => 'â†‘',
  '¡õ' => 'â†“',
  '¡ö' => 'â†�',
  '¡÷' => 'â†’',
  '¡ø' => 'â†–',
  '¡ù' => 'â†—',
  '¡ú' => 'â†™',
  '¡û' => 'â†˜',
  '¡ü' => 'âˆ¥',
  '¡ý' => 'âˆ£',
  '¡þ' => 'ï¿½',
  '¢@' => 'ï¿½',
  '¢A' => 'ï¼�',
  '¢B' => 'ï¼¼',
  '¢C' => 'ï¼„',
  '¢D' => 'Â¥',
  '¢E' => 'ã€’',
  '¢F' => 'Â¢',
  '¢G' => 'Â£',
  '¢H' => 'ï¼…',
  '¢I' => 'ï¼ ',
  '¢J' => 'â„ƒ',
  '¢K' => 'â„‰',
  '¢L' => 'ï¹©',
  '¢M' => 'ï¹ª',
  '¢N' => 'ï¹«',
  '¢O' => 'ã�•',
  '¢P' => 'ãŽœ',
  '¢Q' => 'ãŽ�',
  '¢R' => 'ãŽž',
  '¢S' => 'ã�Ž',
  '¢T' => 'ãŽ¡',
  '¢U' => 'ãŽŽ',
  '¢V' => 'ãŽ�',
  '¢W' => 'ã�„',
  '¢X' => 'Â°',
  '¢Y' => 'å…™',
  '¢Z' => 'å…›',
  '¢[' => 'å…ž',
  '¢\\' => 'å…�',
  '¢]' => 'å…¡',
  '¢^' => 'å…£',
  '¢_' => 'å—§',
  '¢`' => 'ç“©',
  '¢a' => 'ç³Ž',
  '¢b' => 'â–�',
  '¢c' => 'â–‚',
  '¢d' => 'â–ƒ',
  '¢e' => 'â–„',
  '¢f' => 'â–…',
  '¢g' => 'â–†',
  '¢h' => 'â–‡',
  '¢i' => 'â–ˆ',
  '¢j' => 'â–�',
  '¢k' => 'â–Ž',
  '¢l' => 'â–�',
  '¢m' => 'â–Œ',
  '¢n' => 'â–‹',
  '¢o' => 'â–Š',
  '¢p' => 'â–‰',
  '¢q' => 'â”¼',
  '¢r' => 'â”´',
  '¢s' => 'â”¬',
  '¢t' => 'â”¤',
  '¢u' => 'â”œ',
  '¢v' => 'â–”',
  '¢w' => 'â”€',
  '¢x' => 'â”‚',
  '¢y' => 'â–•',
  '¢z' => 'â”Œ',
  '¢{' => 'â”�',
  '¢|' => 'â””',
  '¢}' => 'â”˜',
  '¢~' => 'â•­',
  '¢¡' => 'â•®',
  '¢¢' => 'â•°',
  '¢£' => 'â•¯',
  '¢¤' => 'â•�',
  '¢¥' => 'â•ž',
  '¢¦' => 'â•ª',
  '¢§' => 'â•¡',
  '¢¨' => 'â—¢',
  '¢©' => 'â—£',
  '¢ª' => 'â—¥',
  '¢«' => 'â—¤',
  '¢¬' => 'â•±',
  '¢­' => 'â•²',
  '¢®' => 'â•³',
  '¢¯' => 'ï¼�',
  '¢°' => 'ï¼‘',
  '¢±' => 'ï¼’',
  '¢²' => 'ï¼“',
  '¢³' => 'ï¼”',
  '¢´' => 'ï¼•',
  '¢µ' => 'ï¼–',
  '¢¶' => 'ï¼—',
  '¢·' => 'ï¼˜',
  '¢¸' => 'ï¼™',
  '¢¹' => 'â… ',
  '¢º' => 'â…¡',
  '¢»' => 'â…¢',
  '¢¼' => 'â…£',
  '¢½' => 'â…¤',
  '¢¾' => 'â…¥',
  '¢¿' => 'â…¦',
  '¢À' => 'â…§',
  '¢Á' => 'â…¨',
  '¢Â' => 'â…©',
  '¢Ã' => 'ã€¡',
  '¢Ä' => 'ã€¢',
  '¢Å' => 'ã€£',
  '¢Æ' => 'ã€¤',
  '¢Ç' => 'ã€¥',
  '¢È' => 'ã€¦',
  '¢É' => 'ã€§',
  '¢Ê' => 'ã€¨',
  '¢Ë' => 'ã€©',
  '¢Ì' => 'ï¿½',
  '¢Í' => 'å�„',
  '¢Î' => 'ï¿½',
  '¢Ï' => 'ï¼¡',
  '¢Ð' => 'ï¼¢',
  '¢Ñ' => 'ï¼£',
  '¢Ò' => 'ï¼¤',
  '¢Ó' => 'ï¼¥',
  '¢Ô' => 'ï¼¦',
  '¢Õ' => 'ï¼§',
  '¢Ö' => 'ï¼¨',
  '¢×' => 'ï¼©',
  '¢Ø' => 'ï¼ª',
  '¢Ù' => 'ï¼«',
  '¢Ú' => 'ï¼¬',
  '¢Û' => 'ï¼­',
  '¢Ü' => 'ï¼®',
  '¢Ý' => 'ï¼¯',
  '¢Þ' => 'ï¼°',
  '¢ß' => 'ï¼±',
  '¢à' => 'ï¼²',
  '¢á' => 'ï¼³',
  '¢â' => 'ï¼´',
  '¢ã' => 'ï¼µ',
  '¢ä' => 'ï¼¶',
  '¢å' => 'ï¼·',
  '¢æ' => 'ï¼¸',
  '¢ç' => 'ï¼¹',
  '¢è' => 'ï¼º',
  '¢é' => 'ï½�',
  '¢ê' => 'ï½‚',
  '¢ë' => 'ï½ƒ',
  '¢ì' => 'ï½„',
  '¢í' => 'ï½…',
  '¢î' => 'ï½†',
  '¢ï' => 'ï½‡',
  '¢ð' => 'ï½ˆ',
  '¢ñ' => 'ï½‰',
  '¢ò' => 'ï½Š',
  '¢ó' => 'ï½‹',
  '¢ô' => 'ï½Œ',
  '¢õ' => 'ï½�',
  '¢ö' => 'ï½Ž',
  '¢÷' => 'ï½�',
  '¢ø' => 'ï½�',
  '¢ù' => 'ï½‘',
  '¢ú' => 'ï½’',
  '¢û' => 'ï½“',
  '¢ü' => 'ï½”',
  '¢ý' => 'ï½•',
  '¢þ' => 'ï½–',
  '£@' => 'ï½—',
  '£A' => 'ï½˜',
  '£B' => 'ï½™',
  '£C' => 'ï½š',
  '£D' => 'Î‘',
  '£E' => 'Î’',
  '£F' => 'Î“',
  '£G' => 'Î”',
  '£H' => 'Î•',
  '£I' => 'Î–',
  '£J' => 'Î—',
  '£K' => 'Î˜',
  '£L' => 'Î™',
  '£M' => 'Îš',
  '£N' => 'Î›',
  '£O' => 'Îœ',
  '£P' => 'Î�',
  '£Q' => 'Îž',
  '£R' => 'ÎŸ',
  '£S' => 'Î ',
  '£T' => 'Î¡',
  '£U' => 'Î£',
  '£V' => 'Î¤',
  '£W' => 'Î¥',
  '£X' => 'Î¦',
  '£Y' => 'Î§',
  '£Z' => 'Î¨',
  '£[' => 'Î©',
  '£\\' => 'Î±',
  '£]' => 'Î²',
  '£^' => 'Î³',
  '£_' => 'Î´',
  '£`' => 'Îµ',
  '£a' => 'Î¶',
  '£b' => 'Î·',
  '£c' => 'Î¸',
  '£d' => 'Î¹',
  '£e' => 'Îº',
  '£f' => 'Î»',
  '£g' => 'Î¼',
  '£h' => 'Î½',
  '£i' => 'Î¾',
  '£j' => 'Î¿',
  '£k' => 'Ï€',
  '£l' => 'Ï�',
  '£m' => 'Ïƒ',
  '£n' => 'Ï„',
  '£o' => 'Ï…',
  '£p' => 'Ï†',
  '£q' => 'Ï‡',
  '£r' => 'Ïˆ',
  '£s' => 'Ï‰',
  '£t' => 'ã„…',
  '£u' => 'ã„†',
  '£v' => 'ã„‡',
  '£w' => 'ã„ˆ',
  '£x' => 'ã„‰',
  '£y' => 'ã„Š',
  '£z' => 'ã„‹',
  '£{' => 'ã„Œ',
  '£|' => 'ã„�',
  '£}' => 'ã„Ž',
  '£~' => 'ã„�',
  '£¡' => 'ã„�',
  '£¢' => 'ã„‘',
  '££' => 'ã„’',
  '£¤' => 'ã„“',
  '£¥' => 'ã„”',
  '£¦' => 'ã„•',
  '£§' => 'ã„–',
  '£¨' => 'ã„—',
  '£©' => 'ã„˜',
  '£ª' => 'ã„™',
  '£«' => 'ã„š',
  '£¬' => 'ã„›',
  '£­' => 'ã„œ',
  '£®' => 'ã„�',
  '£¯' => 'ã„ž',
  '£°' => 'ã„Ÿ',
  '£±' => 'ã„ ',
  '£²' => 'ã„¡',
  '£³' => 'ã„¢',
  '£´' => 'ã„£',
  '£µ' => 'ã„¤',
  '£¶' => 'ã„¥',
  '£·' => 'ã„¦',
  '£¸' => 'ã„§',
  '£¹' => 'ã„¨',
  '£º' => 'ã„©',
  '£»' => 'Ë™',
  '£¼' => 'Ë‰',
  '£½' => 'ËŠ',
  '£¾' => 'Ë‡',
  '£¿' => 'Ë‹',
  '¤@' => 'ä¸€',
  '¤A' => 'ä¹™',
  '¤B' => 'ä¸�',
  '¤C' => 'ä¸ƒ',
  '¤D' => 'ä¹ƒ',
  '¤E' => 'ä¹�',
  '¤F' => 'äº†',
  '¤G' => 'äºŒ',
  '¤H' => 'äºº',
  '¤I' => 'å„¿',
  '¤J' => 'å…¥',
  '¤K' => 'å…«',
  '¤L' => 'å‡ ',
  '¤M' => 'åˆ€',
  '¤N' => 'åˆ�',
  '¤O' => 'åŠ›',
  '¤P' => 'åŒ•',
  '¤Q' => 'å��',
  '¤R' => 'å�œ',
  '¤S' => 'å�ˆ',
  '¤T' => 'ä¸‰',
  '¤U' => 'ä¸‹',
  '¤V' => 'ä¸ˆ',
  '¤W' => 'ä¸Š',
  '¤X' => 'ä¸«',
  '¤Y' => 'ä¸¸',
  '¤Z' => 'å‡¡',
  '¤[' => 'ä¹…',
  '¤\\' => 'ä¹ˆ',
  '¤]' => 'ä¹Ÿ',
  '¤^' => 'ä¹ž',
  '¤_' => 'äºŽ',
  '¤`' => 'äº¡',
  '¤a' => 'å…€',
  '¤b' => 'åˆƒ',
  '¤c' => 'å‹º',
  '¤d' => 'å�ƒ',
  '¤e' => 'å�‰',
  '¤f' => 'å�£',
  '¤g' => 'åœŸ',
  '¤h' => 'å£«',
  '¤i' => 'å¤•',
  '¤j' => 'å¤§',
  '¤k' => 'å¥³',
  '¤l' => 'å­�',
  '¤m' => 'å­‘',
  '¤n' => 'å­“',
  '¤o' => 'å¯¸',
  '¤p' => 'å°�',
  '¤q' => 'å°¢',
  '¤r' => 'å°¸',
  '¤s' => 'å±±',
  '¤t' => 'å·�',
  '¤u' => 'å·¥',
  '¤v' => 'å·±',
  '¤w' => 'å·²',
  '¤x' => 'å·³',
  '¤y' => 'å·¾',
  '¤z' => 'å¹²',
  '¤{' => 'å»¾',
  '¤|' => 'å¼‹',
  '¤}' => 'å¼“',
  '¤~' => 'æ‰�',
  '¤¡' => 'ä¸‘',
  '¤¢' => 'ä¸�',
  '¤£' => 'ä¸�',
  '¤¤' => 'ä¸­',
  '¤¥' => 'ä¸°',
  '¤¦' => 'ä¸¹',
  '¤§' => 'ä¹‹',
  '¤¨' => 'å°¹',
  '¤©' => 'äºˆ',
  '¤ª' => 'äº‘',
  '¤«' => 'äº•',
  '¤¬' => 'äº’',
  '¤­' => 'äº”',
  '¤®' => 'äº¢',
  '¤¯' => 'ä»�',
  '¤°' => 'ä»€',
  '¤±' => 'ä»ƒ',
  '¤²' => 'ä»†',
  '¤³' => 'ä»‡',
  '¤´' => 'ä»�',
  '¤µ' => 'ä»Š',
  '¤¶' => 'ä»‹',
  '¤·' => 'ä»„',
  '¤¸' => 'å…ƒ',
  '¤¹' => 'å…�',
  '¤º' => 'å…§',
  '¤»' => 'å…­',
  '¤¼' => 'å…®',
  '¤½' => 'å…¬',
  '¤¾' => 'å†—',
  '¤¿' => 'å‡¶',
  '¤À' => 'åˆ†',
  '¤Á' => 'åˆ‡',
  '¤Â' => 'åˆˆ',
  '¤Ã' => 'å‹»',
  '¤Ä' => 'å‹¾',
  '¤Å' => 'å‹¿',
  '¤Æ' => 'åŒ–',
  '¤Ç' => 'åŒ¹',
  '¤È' => 'å�ˆ',
  '¤É' => 'å�‡',
  '¤Ê' => 'å�…',
  '¤Ë' => 'å�ž',
  '¤Ì' => 'åŽ„',
  '¤Í' => 'å�‹',
  '¤Î' => 'å�Š',
  '¤Ï' => 'å��',
  '¤Ð' => 'å£¬',
  '¤Ñ' => 'å¤©',
  '¤Ò' => 'å¤«',
  '¤Ó' => 'å¤ª',
  '¤Ô' => 'å¤­',
  '¤Õ' => 'å­”',
  '¤Ö' => 'å°‘',
  '¤×' => 'å°¤',
  '¤Ø' => 'å°º',
  '¤Ù' => 'å±¯',
  '¤Ú' => 'å·´',
  '¤Û' => 'å¹»',
  '¤Ü' => 'å»¿',
  '¤Ý' => 'å¼”',
  '¤Þ' => 'å¼•',
  '¤ß' => 'å¿ƒ',
  '¤à' => 'æˆˆ',
  '¤á' => 'æˆ¶',
  '¤â' => 'æ‰‹',
  '¤ã' => 'æ‰Ž',
  '¤ä' => 'æ”¯',
  '¤å' => 'æ–‡',
  '¤æ' => 'æ–—',
  '¤ç' => 'æ–¤',
  '¤è' => 'æ–¹',
  '¤é' => 'æ—¥',
  '¤ê' => 'æ›°',
  '¤ë' => 'æœˆ',
  '¤ì' => 'æœ¨',
  '¤í' => 'æ¬ ',
  '¤î' => 'æ­¢',
  '¤ï' => 'æ­¹',
  '¤ð' => 'æ¯‹',
  '¤ñ' => 'æ¯”',
  '¤ò' => 'æ¯›',
  '¤ó' => 'æ°�',
  '¤ô' => 'æ°´',
  '¤õ' => 'ç�«',
  '¤ö' => 'çˆª',
  '¤÷' => 'çˆ¶',
  '¤ø' => 'çˆ»',
  '¤ù' => 'ç‰‡',
  '¤ú' => 'ç‰™',
  '¤û' => 'ç‰›',
  '¤ü' => 'çŠ¬',
  '¤ý' => 'çŽ‹',
  '¤þ' => 'ä¸™',
  '¥@' => 'ä¸–',
  '¥A' => 'ä¸•',
  '¥B' => 'ä¸”',
  '¥C' => 'ä¸˜',
  '¥D' => 'ä¸»',
  '¥E' => 'ä¹�',
  '¥F' => 'ä¹�',
  '¥G' => 'ä¹Ž',
  '¥H' => 'ä»¥',
  '¥I' => 'ä»˜',
  '¥J' => 'ä»”',
  '¥K' => 'ä»•',
  '¥L' => 'ä»–',
  '¥M' => 'ä»—',
  '¥N' => 'ä»£',
  '¥O' => 'ä»¤',
  '¥P' => 'ä»™',
  '¥Q' => 'ä»ž',
  '¥R' => 'å……',
  '¥S' => 'å…„',
  '¥T' => 'å†‰',
  '¥U' => 'å†Š',
  '¥V' => 'å†¬',
  '¥W' => 'å‡¹',
  '¥X' => 'å‡º',
  '¥Y' => 'å‡¸',
  '¥Z' => 'åˆŠ',
  '¥[' => 'åŠ ',
  '¥\\' => 'åŠŸ',
  '¥]' => 'åŒ…',
  '¥^' => 'åŒ†',
  '¥_' => 'åŒ—',
  '¥`' => 'åŒ�',
  '¥a' => 'ä»Ÿ',
  '¥b' => 'å�Š',
  '¥c' => 'å�‰',
  '¥d' => 'å�¡',
  '¥e' => 'å� ',
  '¥f' => 'å�¯',
  '¥g' => 'å�®',
  '¥h' => 'åŽ»',
  '¥i' => 'å�¯',
  '¥j' => 'å�¤',
  '¥k' => 'å�³',
  '¥l' => 'å�¬',
  '¥m' => 'å�®',
  '¥n' => 'å�©',
  '¥o' => 'å�¨',
  '¥p' => 'å�¼',
  '¥q' => 'å�¸',
  '¥r' => 'å�µ',
  '¥s' => 'å�«',
  '¥t' => 'å�¦',
  '¥u' => 'å�ª',
  '¥v' => 'å�²',
  '¥w' => 'å�±',
  '¥x' => 'å�°',
  '¥y' => 'å�¥',
  '¥z' => 'å�­',
  '¥{' => 'å�»',
  '¥|' => 'å››',
  '¥}' => 'å›š',
  '¥~' => 'å¤–',
  '¥¡' => 'å¤®',
  '¥¢' => 'å¤±',
  '¥£' => 'å¥´',
  '¥¤' => 'å¥¶',
  '¥¥' => 'å­•',
  '¥¦' => 'å®ƒ',
  '¥§' => 'å°¼',
  '¥¨' => 'å·¨',
  '¥©' => 'å·§',
  '¥ª' => 'å·¦',
  '¥«' => 'å¸‚',
  '¥¬' => 'å¸ƒ',
  '¥­' => 'å¹³',
  '¥®' => 'å¹¼',
  '¥¯' => 'å¼�',
  '¥°' => 'å¼˜',
  '¥±' => 'å¼—',
  '¥²' => 'å¿…',
  '¥³' => 'æˆŠ',
  '¥´' => 'æ‰“',
  '¥µ' => 'æ‰”',
  '¥¶' => 'æ‰’',
  '¥·' => 'æ‰‘',
  '¥¸' => 'æ–¥',
  '¥¹' => 'æ—¦',
  '¥º' => 'æœ®',
  '¥»' => 'æœ¬',
  '¥¼' => 'æœª',
  '¥½' => 'æœ«',
  '¥¾' => 'æœ­',
  '¥¿' => 'æ­£',
  '¥À' => 'æ¯�',
  '¥Á' => 'æ°‘',
  '¥Â' => 'æ°�',
  '¥Ã' => 'æ°¸',
  '¥Ä' => 'æ±�',
  '¥Å' => 'æ±€',
  '¥Æ' => 'æ°¾',
  '¥Ç' => 'çŠ¯',
  '¥È' => 'çŽ„',
  '¥É' => 'çŽ‰',
  '¥Ê' => 'ç“œ',
  '¥Ë' => 'ç“¦',
  '¥Ì' => 'ç”˜',
  '¥Í' => 'ç”Ÿ',
  '¥Î' => 'ç”¨',
  '¥Ï' => 'ç”©',
  '¥Ð' => 'ç”°',
  '¥Ñ' => 'ç”±',
  '¥Ò' => 'ç”²',
  '¥Ó' => 'ç”³',
  '¥Ô' => 'ç–‹',
  '¥Õ' => 'ç™½',
  '¥Ö' => 'çš®',
  '¥×' => 'çš¿',
  '¥Ø' => 'ç›®',
  '¥Ù' => 'çŸ›',
  '¥Ú' => 'çŸ¢',
  '¥Û' => 'çŸ³',
  '¥Ü' => 'ç¤º',
  '¥Ý' => 'ç¦¾',
  '¥Þ' => 'ç©´',
  '¥ß' => 'ç«‹',
  '¥à' => 'ä¸ž',
  '¥á' => 'ä¸Ÿ',
  '¥â' => 'ä¹’',
  '¥ã' => 'ä¹“',
  '¥ä' => 'ä¹©',
  '¥å' => 'äº™',
  '¥æ' => 'äº¤',
  '¥ç' => 'äº¦',
  '¥è' => 'äº¥',
  '¥é' => 'ä»¿',
  '¥ê' => 'ä¼‰',
  '¥ë' => 'ä¼™',
  '¥ì' => 'ä¼Š',
  '¥í' => 'ä¼•',
  '¥î' => 'ä¼�',
  '¥ï' => 'ä¼�',
  '¥ð' => 'ä¼‘',
  '¥ñ' => 'ä¼�',
  '¥ò' => 'ä»²',
  '¥ó' => 'ä»¶',
  '¥ô' => 'ä»»',
  '¥õ' => 'ä»°',
  '¥ö' => 'ä»³',
  '¥÷' => 'ä»½',
  '¥ø' => 'ä¼�',
  '¥ù' => 'ä¼‹',
  '¥ú' => 'å…‰',
  '¥û' => 'å…‡',
  '¥ü' => 'å…†',
  '¥ý' => 'å…ˆ',
  '¥þ' => 'å…¨',
  '¦@' => 'å…±',
  '¦A' => 'å†�',
  '¦B' => 'å†°',
  '¦C' => 'åˆ—',
  '¦D' => 'åˆ‘',
  '¦E' => 'åˆ’',
  '¦F' => 'åˆŽ',
  '¦G' => 'åˆ–',
  '¦H' => 'åŠ£',
  '¦I' => 'åŒˆ',
  '¦J' => 'åŒ¡',
  '¦K' => 'åŒ ',
  '¦L' => 'å�°',
  '¦M' => 'å�±',
  '¦N' => 'å�‰',
  '¦O' => 'å��',
  '¦P' => 'å�Œ',
  '¦Q' => 'å�Š',
  '¦R' => 'å��',
  '¦S' => 'å��',
  '¦T' => 'å�‹',
  '¦U' => 'å�„',
  '¦V' => 'å�‘',
  '¦W' => 'å��',
  '¦X' => 'å�ˆ',
  '¦Y' => 'å�ƒ',
  '¦Z' => 'å�Ž',
  '¦[' => 'å�†',
  '¦\\' => 'å�’',
  '¦]' => 'å› ',
  '¦^' => 'å›ž',
  '¦_' => 'å›�',
  '¦`' => 'åœ³',
  '¦a' => 'åœ°',
  '¦b' => 'åœ¨',
  '¦c' => 'åœ­',
  '¦d' => 'åœ¬',
  '¦e' => 'åœ¯',
  '¦f' => 'åœ©',
  '¦g' => 'å¤™',
  '¦h' => 'å¤š',
  '¦i' => 'å¤·',
  '¦j' => 'å¤¸',
  '¦k' => 'å¦„',
  '¦l' => 'å¥¸',
  '¦m' => 'å¦ƒ',
  '¦n' => 'å¥½',
  '¦o' => 'å¥¹',
  '¦p' => 'å¦‚',
  '¦q' => 'å¦�',
  '¦r' => 'å­—',
  '¦s' => 'å­˜',
  '¦t' => 'å®‡',
  '¦u' => 'å®ˆ',
  '¦v' => 'å®…',
  '¦w' => 'å®‰',
  '¦x' => 'å¯º',
  '¦y' => 'å°–',
  '¦z' => 'å±¹',
  '¦{' => 'å·ž',
  '¦|' => 'å¸†',
  '¦}' => 'å¹¶',
  '¦~' => 'å¹´',
  '¦¡' => 'å¼�',
  '¦¢' => 'å¼›',
  '¦£' => 'å¿™',
  '¦¤' => 'å¿–',
  '¦¥' => 'æˆŽ',
  '¦¦' => 'æˆŒ',
  '¦§' => 'æˆ�',
  '¦¨' => 'æˆ�',
  '¦©' => 'æ‰£',
  '¦ª' => 'æ‰›',
  '¦«' => 'æ‰˜',
  '¦¬' => 'æ”¶',
  '¦­' => 'æ—©',
  '¦®' => 'æ—¨',
  '¦¯' => 'æ—¬',
  '¦°' => 'æ—­',
  '¦±' => 'æ›²',
  '¦²' => 'æ›³',
  '¦³' => 'æœ‰',
  '¦´' => 'æœ½',
  '¦µ' => 'æœ´',
  '¦¶' => 'æœ±',
  '¦·' => 'æœµ',
  '¦¸' => 'æ¬¡',
  '¦¹' => 'æ­¤',
  '¦º' => 'æ­»',
  '¦»' => 'æ°–',
  '¦¼' => 'æ±�',
  '¦½' => 'æ±—',
  '¦¾' => 'æ±™',
  '¦¿' => 'æ±Ÿ',
  '¦À' => 'æ± ',
  '¦Á' => 'æ±�',
  '¦Â' => 'æ±•',
  '¦Ã' => 'æ±¡',
  '¦Ä' => 'æ±›',
  '¦Å' => 'æ±�',
  '¦Æ' => 'æ±Ž',
  '¦Ç' => 'ç�°',
  '¦È' => 'ç‰Ÿ',
  '¦É' => 'ç‰�',
  '¦Ê' => 'ç™¾',
  '¦Ë' => 'ç«¹',
  '¦Ì' => 'ç±³',
  '¦Í' => 'ç³¸',
  '¦Î' => 'ç¼¶',
  '¦Ï' => 'ç¾Š',
  '¦Ð' => 'ç¾½',
  '¦Ñ' => 'è€�',
  '¦Ò' => 'è€ƒ',
  '¦Ó' => 'è€Œ',
  '¦Ô' => 'è€’',
  '¦Õ' => 'è€³',
  '¦Ö' => 'è�¿',
  '¦×' => 'è‚‰',
  '¦Ø' => 'è‚‹',
  '¦Ù' => 'è‚Œ',
  '¦Ú' => 'è‡£',
  '¦Û' => 'è‡ª',
  '¦Ü' => 'è‡³',
  '¦Ý' => 'è‡¼',
  '¦Þ' => 'èˆŒ',
  '¦ß' => 'èˆ›',
  '¦à' => 'èˆŸ',
  '¦á' => 'è‰®',
  '¦â' => 'è‰²',
  '¦ã' => 'è‰¾',
  '¦ä' => 'è™«',
  '¦å' => 'è¡€',
  '¦æ' => 'è¡Œ',
  '¦ç' => 'è¡£',
  '¦è' => 'è¥¿',
  '¦é' => 'é˜¡',
  '¦ê' => 'ä¸²',
  '¦ë' => 'äº¨',
  '¦ì' => 'ä½�',
  '¦í' => 'ä½�',
  '¦î' => 'ä½‡',
  '¦ï' => 'ä½—',
  '¦ð' => 'ä½ž',
  '¦ñ' => 'ä¼´',
  '¦ò' => 'ä½›',
  '¦ó' => 'ä½•',
  '¦ô' => 'ä¼°',
  '¦õ' => 'ä½�',
  '¦ö' => 'ä½‘',
  '¦÷' => 'ä¼½',
  '¦ø' => 'ä¼º',
  '¦ù' => 'ä¼¸',
  '¦ú' => 'ä½ƒ',
  '¦û' => 'ä½”',
  '¦ü' => 'ä¼¼',
  '¦ý' => 'ä½†',
  '¦þ' => 'ä½£',
  '§@' => 'ä½œ',
  '§A' => 'ä½ ',
  '§B' => 'ä¼¯',
  '§C' => 'ä½Ž',
  '§D' => 'ä¼¶',
  '§E' => 'ä½™',
  '§F' => 'ä½�',
  '§G' => 'ä½ˆ',
  '§H' => 'ä½š',
  '§I' => 'å…Œ',
  '§J' => 'å…‹',
  '§K' => 'å…�',
  '§L' => 'å…µ',
  '§M' => 'å†¶',
  '§N' => 'å†·',
  '§O' => 'åˆ¥',
  '§P' => 'åˆ¤',
  '§Q' => 'åˆ©',
  '§R' => 'åˆª',
  '§S' => 'åˆ¨',
  '§T' => 'åŠ«',
  '§U' => 'åŠ©',
  '§V' => 'åŠª',
  '§W' => 'åŠ¬',
  '§X' => 'åŒ£',
  '§Y' => 'å�³',
  '§Z' => 'å�µ',
  '§[' => 'å��',
  '§\\' => 'å�­',
  '§]' => 'å�ž',
  '§^' => 'å�¾',
  '§_' => 'å�¦',
  '§`' => 'å‘Ž',
  '§a' => 'å�§',
  '§b' => 'å‘†',
  '§c' => 'å‘ƒ',
  '§d' => 'å�³',
  '§e' => 'å‘ˆ',
  '§f' => 'å‘‚',
  '§g' => 'å�›',
  '§h' => 'å�©',
  '§i' => 'å‘Š',
  '§j' => 'å�¹',
  '§k' => 'å�»',
  '§l' => 'å�¸',
  '§m' => 'å�®',
  '§n' => 'å�µ',
  '§o' => 'å�¶',
  '§p' => 'å� ',
  '§q' => 'å�¼',
  '§r' => 'å‘€',
  '§s' => 'å�±',
  '§t' => 'å�«',
  '§u' => 'å�Ÿ',
  '§v' => 'å�¬',
  '§w' => 'å›ª',
  '§x' => 'å›°',
  '§y' => 'å›¤',
  '§z' => 'å›«',
  '§{' => 'å�Š',
  '§|' => 'å�‘',
  '§}' => 'å�€',
  '§~' => 'å��',
  '§¡' => 'å�‡',
  '§¢' => 'å�Ž',
  '§£' => 'åœ¾',
  '§¤' => 'å��',
  '§¥' => 'å��',
  '§¦' => 'åœ»',
  '§§' => 'å£¯',
  '§¨' => 'å¤¾',
  '§©' => 'å¦�',
  '§ª' => 'å¦’',
  '§«' => 'å¦¨',
  '§¬' => 'å¦ž',
  '§­' => 'å¦£',
  '§®' => 'å¦™',
  '§¯' => 'å¦–',
  '§°' => 'å¦�',
  '§±' => 'å¦¤',
  '§²' => 'å¦“',
  '§³' => 'å¦Š',
  '§´' => 'å¦¥',
  '§µ' => 'å­�',
  '§¶' => 'å­œ',
  '§·' => 'å­š',
  '§¸' => 'å­›',
  '§¹' => 'å®Œ',
  '§º' => 'å®‹',
  '§»' => 'å®�',
  '§¼' => 'å°¬',
  '§½' => 'å±€',
  '§¾' => 'å±�',
  '§¿' => 'å°¿',
  '§À' => 'å°¾',
  '§Á' => 'å²�',
  '§Â' => 'å²‘',
  '§Ã' => 'å²”',
  '§Ä' => 'å²Œ',
  '§Å' => 'å·«',
  '§Æ' => 'å¸Œ',
  '§Ç' => 'åº�',
  '§È' => 'åº‡',
  '§É' => 'åºŠ',
  '§Ê' => 'å»·',
  '§Ë' => 'å¼„',
  '§Ì' => 'å¼Ÿ',
  '§Í' => 'å½¤',
  '§Î' => 'å½¢',
  '§Ï' => 'å½·',
  '§Ð' => 'å½¹',
  '§Ñ' => 'å¿˜',
  '§Ò' => 'å¿Œ',
  '§Ó' => 'å¿—',
  '§Ô' => 'å¿�',
  '§Õ' => 'å¿±',
  '§Ö' => 'å¿«',
  '§×' => 'å¿¸',
  '§Ø' => 'å¿ª',
  '§Ù' => 'æˆ’',
  '§Ú' => 'æˆ‘',
  '§Û' => 'æŠ„',
  '§Ü' => 'æŠ—',
  '§Ý' => 'æŠ–',
  '§Þ' => 'æŠ€',
  '§ß' => 'æ‰¶',
  '§à' => 'æŠ‰',
  '§á' => 'æ‰­',
  '§â' => 'æŠŠ',
  '§ã' => 'æ‰¼',
  '§ä' => 'æ‰¾',
  '§å' => 'æ‰¹',
  '§æ' => 'æ‰³',
  '§ç' => 'æŠ’',
  '§è' => 'æ‰¯',
  '§é' => 'æŠ˜',
  '§ê' => 'æ‰®',
  '§ë' => 'æŠ•',
  '§ì' => 'æŠ“',
  '§í' => 'æŠ‘',
  '§î' => 'æŠ†',
  '§ï' => 'æ”¹',
  '§ð' => 'æ”»',
  '§ñ' => 'æ”¸',
  '§ò' => 'æ—±',
  '§ó' => 'æ›´',
  '§ô' => 'æ�Ÿ',
  '§õ' => 'æ�Ž',
  '§ö' => 'æ��',
  '§÷' => 'æ��',
  '§ø' => 'æ�‘',
  '§ù' => 'æ�œ',
  '§ú' => 'æ�–',
  '§û' => 'æ�ž',
  '§ü' => 'æ�‰',
  '§ý' => 'æ�†',
  '§þ' => 'æ� ',
  '¨@' => 'æ�“',
  '¨A' => 'æ�—',
  '¨B' => 'æ­¥',
  '¨C' => 'æ¯�',
  '¨D' => 'æ±‚',
  '¨E' => 'æ±ž',
  '¨F' => 'æ²™',
  '¨G' => 'æ²�',
  '¨H' => 'æ²ˆ',
  '¨I' => 'æ²‰',
  '¨J' => 'æ²…',
  '¨K' => 'æ²›',
  '¨L' => 'æ±ª',
  '¨M' => 'æ±º',
  '¨N' => 'æ²�',
  '¨O' => 'æ±°',
  '¨P' => 'æ²Œ',
  '¨Q' => 'æ±¨',
  '¨R' => 'æ²–',
  '¨S' => 'æ²’',
  '¨T' => 'æ±½',
  '¨U' => 'æ²ƒ',
  '¨V' => 'æ±²',
  '¨W' => 'æ±¾',
  '¨X' => 'æ±´',
  '¨Y' => 'æ²†',
  '¨Z' => 'æ±¶',
  '¨[' => 'æ²�',
  '¨\\' => 'æ²”',
  '¨]' => 'æ²˜',
  '¨^' => 'æ²‚',
  '¨_' => 'ç�¶',
  '¨`' => 'ç�¼',
  '¨a' => 'ç�½',
  '¨b' => 'ç�¸',
  '¨c' => 'ç‰¢',
  '¨d' => 'ç‰¡',
  '¨e' => 'ç‰ ',
  '¨f' => 'ç‹„',
  '¨g' => 'ç‹‚',
  '¨h' => 'çŽ–',
  '¨i' => 'ç”¬',
  '¨j' => 'ç”«',
  '¨k' => 'ç”·',
  '¨l' => 'ç”¸',
  '¨m' => 'çš‚',
  '¨n' => 'ç›¯',
  '¨o' => 'çŸ£',
  '¨p' => 'ç§�',
  '¨q' => 'ç§€',
  '¨r' => 'ç¦¿',
  '¨s' => 'ç©¶',
  '¨t' => 'ç³»',
  '¨u' => 'ç½•',
  '¨v' => 'è‚–',
  '¨w' => 'è‚“',
  '¨x' => 'è‚�',
  '¨y' => 'è‚˜',
  '¨z' => 'è‚›',
  '¨{' => 'è‚š',
  '¨|' => 'è‚²',
  '¨}' => 'è‰¯',
  '¨~' => 'èŠ’',
  '¨¡' => 'èŠ‹',
  '¨¢' => 'èŠ�',
  '¨£' => 'è¦‹',
  '¨¤' => 'è§’',
  '¨¥' => 'è¨€',
  '¨¦' => 'è°·',
  '¨§' => 'è±†',
  '¨¨' => 'è±•',
  '¨©' => 'è²�',
  '¨ª' => 'èµ¤',
  '¨«' => 'èµ°',
  '¨¬' => 'è¶³',
  '¨­' => 'èº«',
  '¨®' => 'è»Š',
  '¨¯' => 'è¾›',
  '¨°' => 'è¾°',
  '¨±' => 'è¿‚',
  '¨²' => 'è¿†',
  '¨³' => 'è¿…',
  '¨´' => 'è¿„',
  '¨µ' => 'å·¡',
  '¨¶' => 'é‚‘',
  '¨·' => 'é‚¢',
  '¨¸' => 'é‚ª',
  '¨¹' => 'é‚¦',
  '¨º' => 'é‚£',
  '¨»' => 'é…‰',
  '¨¼' => 'é‡†',
  '¨½' => 'é‡Œ',
  '¨¾' => 'é˜²',
  '¨¿' => 'é˜®',
  '¨À' => 'é˜±',
  '¨Á' => 'é˜ª',
  '¨Â' => 'é˜¬',
  '¨Ã' => 'ä¸¦',
  '¨Ä' => 'ä¹–',
  '¨Å' => 'ä¹³',
  '¨Æ' => 'äº‹',
  '¨Ç' => 'äº›',
  '¨È' => 'äºž',
  '¨É' => 'äº«',
  '¨Ê' => 'äº¬',
  '¨Ë' => 'ä½¯',
  '¨Ì' => 'ä¾�',
  '¨Í' => 'ä¾�',
  '¨Î' => 'ä½³',
  '¨Ï' => 'ä½¿',
  '¨Ð' => 'ä½¬',
  '¨Ñ' => 'ä¾›',
  '¨Ò' => 'ä¾‹',
  '¨Ó' => 'ä¾†',
  '¨Ô' => 'ä¾ƒ',
  '¨Õ' => 'ä½°',
  '¨Ö' => 'ä½µ',
  '¨×' => 'ä¾ˆ',
  '¨Ø' => 'ä½©',
  '¨Ù' => 'ä½»',
  '¨Ú' => 'ä¾–',
  '¨Û' => 'ä½¾',
  '¨Ü' => 'ä¾�',
  '¨Ý' => 'ä¾‘',
  '¨Þ' => 'ä½º',
  '¨ß' => 'å…”',
  '¨à' => 'å…’',
  '¨á' => 'å…•',
  '¨â' => 'å…©',
  '¨ã' => 'å…·',
  '¨ä' => 'å…¶',
  '¨å' => 'å…¸',
  '¨æ' => 'å†½',
  '¨ç' => 'å‡½',
  '¨è' => 'åˆ»',
  '¨é' => 'åˆ¸',
  '¨ê' => 'åˆ·',
  '¨ë' => 'åˆº',
  '¨ì' => 'åˆ°',
  '¨í' => 'åˆ®',
  '¨î' => 'åˆ¶',
  '¨ï' => 'å‰�',
  '¨ð' => 'åŠ¾',
  '¨ñ' => 'åŠ»',
  '¨ò' => 'å�’',
  '¨ó' => 'å�”',
  '¨ô' => 'å�“',
  '¨õ' => 'å�‘',
  '¨ö' => 'å�¦',
  '¨÷' => 'å�·',
  '¨ø' => 'å�¸',
  '¨ù' => 'å�¹',
  '¨ú' => 'å�–',
  '¨û' => 'å�”',
  '¨ü' => 'å�—',
  '¨ý' => 'å‘³',
  '¨þ' => 'å‘µ',
  '©@' => 'å’–',
  '©A' => 'å‘¸',
  '©B' => 'å’•',
  '©C' => 'å’€',
  '©D' => 'å‘»',
  '©E' => 'å‘·',
  '©F' => 'å’„',
  '©G' => 'å’’',
  '©H' => 'å’†',
  '©I' => 'å‘¼',
  '©J' => 'å’�',
  '©K' => 'å‘±',
  '©L' => 'å‘¶',
  '©M' => 'å’Œ',
  '©N' => 'å’š',
  '©O' => 'å‘¢',
  '©P' => 'å‘¨',
  '©Q' => 'å’‹',
  '©R' => 'å‘½',
  '©S' => 'å’Ž',
  '©T' => 'å›º',
  '©U' => 'åžƒ',
  '©V' => 'å�·',
  '©W' => 'å�ª',
  '©X' => 'å�©',
  '©Y' => 'å�¡',
  '©Z' => 'å�¦',
  '©[' => 'å�¤',
  '©\\' => 'å�¼',
  '©]' => 'å¤œ',
  '©^' => 'å¥‰',
  '©_' => 'å¥‡',
  '©`' => 'å¥ˆ',
  '©a' => 'å¥„',
  '©b' => 'å¥”',
  '©c' => 'å¦¾',
  '©d' => 'å¦»',
  '©e' => 'å§”',
  '©f' => 'å¦¹',
  '©g' => 'å¦®',
  '©h' => 'å§‘',
  '©i' => 'å§†',
  '©j' => 'å§�',
  '©k' => 'å§�',
  '©l' => 'å§‹',
  '©m' => 'å§“',
  '©n' => 'å§Š',
  '©o' => 'å¦¯',
  '©p' => 'å¦³',
  '©q' => 'å§’',
  '©r' => 'å§…',
  '©s' => 'å­Ÿ',
  '©t' => 'å­¤',
  '©u' => 'å­£',
  '©v' => 'å®—',
  '©w' => 'å®š',
  '©x' => 'å®˜',
  '©y' => 'å®œ',
  '©z' => 'å®™',
  '©{' => 'å®›',
  '©|' => 'å°š',
  '©}' => 'å±ˆ',
  '©~' => 'å±…',
  '©¡' => 'å±†',
  '©¢' => 'å²·',
  '©£' => 'å²¡',
  '©¤' => 'å²¸',
  '©¥' => 'å²©',
  '©¦' => 'å²«',
  '©§' => 'å²±',
  '©¨' => 'å²³',
  '©©' => 'å¸˜',
  '©ª' => 'å¸š',
  '©«' => 'å¸–',
  '©¬' => 'å¸•',
  '©­' => 'å¸›',
  '©®' => 'å¸‘',
  '©¯' => 'å¹¸',
  '©°' => 'åºš',
  '©±' => 'åº—',
  '©²' => 'åºœ',
  '©³' => 'åº•',
  '©´' => 'åº–',
  '©µ' => 'å»¶',
  '©¶' => 'å¼¦',
  '©·' => 'å¼§',
  '©¸' => 'å¼©',
  '©¹' => 'å¾€',
  '©º' => 'å¾�',
  '©»' => 'å½¿',
  '©¼' => 'å½¼',
  '©½' => 'å¿�',
  '©¾' => 'å¿ ',
  '©¿' => 'å¿½',
  '©À' => 'å¿µ',
  '©Á' => 'å¿¿',
  '©Â' => 'æ€�',
  '©Ã' => 'æ€”',
  '©Ä' => 'æ€¯',
  '©Å' => 'æ€µ',
  '©Æ' => 'æ€–',
  '©Ç' => 'æ€ª',
  '©È' => 'æ€•',
  '©É' => 'æ€¡',
  '©Ê' => 'æ€§',
  '©Ë' => 'æ€©',
  '©Ì' => 'æ€«',
  '©Í' => 'æ€›',
  '©Î' => 'æˆ–',
  '©Ï' => 'æˆ•',
  '©Ð' => 'æˆ¿',
  '©Ñ' => 'æˆ¾',
  '©Ò' => 'æ‰€',
  '©Ó' => 'æ‰¿',
  '©Ô' => 'æ‹‰',
  '©Õ' => 'æ‹Œ',
  '©Ö' => 'æ‹„',
  '©×' => 'æŠ¿',
  '©Ø' => 'æ‹‚',
  '©Ù' => 'æŠ¹',
  '©Ú' => 'æ‹’',
  '©Û' => 'æ‹›',
  '©Ü' => 'æŠ«',
  '©Ý' => 'æ‹“',
  '©Þ' => 'æ‹”',
  '©ß' => 'æ‹‹',
  '©à' => 'æ‹ˆ',
  '©á' => 'æŠ¨',
  '©â' => 'æŠ½',
  '©ã' => 'æŠ¼',
  '©ä' => 'æ‹�',
  '©å' => 'æ‹™',
  '©æ' => 'æ‹‡',
  '©ç' => 'æ‹�',
  '©è' => 'æŠµ',
  '©é' => 'æ‹š',
  '©ê' => 'æŠ±',
  '©ë' => 'æ‹˜',
  '©ì' => 'æ‹–',
  '©í' => 'æ‹—',
  '©î' => 'æ‹†',
  '©ï' => 'æŠ¬',
  '©ð' => 'æ‹Ž',
  '©ñ' => 'æ”¾',
  '©ò' => 'æ–§',
  '©ó' => 'æ–¼',
  '©ô' => 'æ—º',
  '©õ' => 'æ˜”',
  '©ö' => 'æ˜“',
  '©÷' => 'æ˜Œ',
  '©ø' => 'æ˜†',
  '©ù' => 'æ˜‚',
  '©ú' => 'æ˜Ž',
  '©û' => 'æ˜€',
  '©ü' => 'æ˜�',
  '©ý' => 'æ˜•',
  '©þ' => 'æ˜Š',
  'ª@' => 'æ˜‡',
  'ªA' => 'æœ�',
  'ªB' => 'æœ‹',
  'ªC' => 'æ�­',
  'ªD' => 'æž‹',
  'ªE' => 'æž•',
  'ªF' => 'æ�±',
  'ªG' => 'æžœ',
  'ªH' => 'æ�³',
  'ªI' => 'æ�·',
  'ªJ' => 'æž‡',
  'ªK' => 'æž�',
  'ªL' => 'æž—',
  'ªM' => 'æ�¯',
  'ªN' => 'æ�°',
  'ªO' => 'æ�¿',
  'ªP' => 'æž‰',
  'ªQ' => 'æ�¾',
  'ªR' => 'æž�',
  'ªS' => 'æ�µ',
  'ªT' => 'æžš',
  'ªU' => 'æž“',
  'ªV' => 'æ�¼',
  'ªW' => 'æ�ª',
  'ªX' => 'æ�²',
  'ªY' => 'æ¬£',
  'ªZ' => 'æ­¦',
  'ª[' => 'æ­§',
  'ª\\' => 'æ­¿',
  'ª]' => 'æ°“',
  'ª^' => 'æ°›',
  'ª_' => 'æ³£',
  'ª`' => 'æ³¨',
  'ªa' => 'æ³³',
  'ªb' => 'æ²±',
  'ªc' => 'æ³Œ',
  'ªd' => 'æ³¥',
  'ªe' => 'æ²³',
  'ªf' => 'æ²½',
  'ªg' => 'æ²¾',
  'ªh' => 'æ²¼',
  'ªi' => 'æ³¢',
  'ªj' => 'æ²«',
  'ªk' => 'æ³•',
  'ªl' => 'æ³“',
  'ªm' => 'æ²¸',
  'ªn' => 'æ³„',
  'ªo' => 'æ²¹',
  'ªp' => 'æ³�',
  'ªq' => 'æ²®',
  'ªr' => 'æ³—',
  'ªs' => 'æ³…',
  'ªt' => 'æ³±',
  'ªu' => 'æ²¿',
  'ªv' => 'æ²»',
  'ªw' => 'æ³¡',
  'ªx' => 'æ³›',
  'ªy' => 'æ³Š',
  'ªz' => 'æ²¬',
  'ª{' => 'æ³¯',
  'ª|' => 'æ³œ',
  'ª}' => 'æ³–',
  'ª~' => 'æ³ ',
  'ª¡' => 'ç‚•',
  'ª¢' => 'ç‚Ž',
  'ª£' => 'ç‚’',
  'ª¤' => 'ç‚Š',
  'ª¥' => 'ç‚™',
  'ª¦' => 'çˆ¬',
  'ª§' => 'çˆ­',
  'ª¨' => 'çˆ¸',
  'ª©' => 'ç‰ˆ',
  'ªª' => 'ç‰§',
  'ª«' => 'ç‰©',
  'ª¬' => 'ç‹€',
  'ª­' => 'ç‹Ž',
  'ª®' => 'ç‹™',
  'ª¯' => 'ç‹—',
  'ª°' => 'ç‹�',
  'ª±' => 'çŽ©',
  'ª²' => 'çŽ¨',
  'ª³' => 'çŽŸ',
  'ª´' => 'çŽ«',
  'ªµ' => 'çŽ¥',
  'ª¶' => 'ç”½',
  'ª·' => 'ç–�',
  'ª¸' => 'ç–™',
  'ª¹' => 'ç–š',
  'ªº' => 'çš„',
  'ª»' => 'ç›‚',
  'ª¼' => 'ç›²',
  'ª½' => 'ç›´',
  'ª¾' => 'çŸ¥',
  'ª¿' => 'çŸ½',
  'ªÀ' => 'ç¤¾',
  'ªÁ' => 'ç¥€',
  'ªÂ' => 'ç¥�',
  'ªÃ' => 'ç§‰',
  'ªÄ' => 'ç§ˆ',
  'ªÅ' => 'ç©º',
  'ªÆ' => 'ç©¹',
  'ªÇ' => 'ç«º',
  'ªÈ' => 'ç³¾',
  'ªÉ' => 'ç½”',
  'ªÊ' => 'ç¾Œ',
  'ªË' => 'ç¾‹',
  'ªÌ' => 'è€…',
  'ªÍ' => 'è‚º',
  'ªÎ' => 'è‚¥',
  'ªÏ' => 'è‚¢',
  'ªÐ' => 'è‚±',
  'ªÑ' => 'è‚¡',
  'ªÒ' => 'è‚«',
  'ªÓ' => 'è‚©',
  'ªÔ' => 'è‚´',
  'ªÕ' => 'è‚ª',
  'ªÖ' => 'è‚¯',
  'ª×' => 'è‡¥',
  'ªØ' => 'è‡¾',
  'ªÙ' => 'èˆ�',
  'ªÚ' => 'èŠ³',
  'ªÛ' => 'èŠ�',
  'ªÜ' => 'èŠ™',
  'ªÝ' => 'èŠ­',
  'ªÞ' => 'èŠ½',
  'ªß' => 'èŠŸ',
  'ªà' => 'èŠ¹',
  'ªá' => 'èŠ±',
  'ªâ' => 'èŠ¬',
  'ªã' => 'èŠ¥',
  'ªä' => 'èŠ¯',
  'ªå' => 'èŠ¸',
  'ªæ' => 'èŠ£',
  'ªç' => 'èŠ°',
  'ªè' => 'èŠ¾',
  'ªé' => 'èŠ·',
  'ªê' => 'è™Ž',
  'ªë' => 'è™±',
  'ªì' => 'åˆ�',
  'ªí' => 'è¡¨',
  'ªî' => 'è»‹',
  'ªï' => 'è¿Ž',
  'ªð' => 'è¿”',
  'ªñ' => 'è¿‘',
  'ªò' => 'é‚µ',
  'ªó' => 'é‚¸',
  'ªô' => 'é‚±',
  'ªõ' => 'é‚¶',
  'ªö' => 'é‡‡',
  'ª÷' => 'é‡‘',
  'ªø' => 'é•·',
  'ªù' => 'é–€',
  'ªú' => 'é˜œ',
  'ªû' => 'é™€',
  'ªü' => 'é˜¿',
  'ªý' => 'é˜»',
  'ªþ' => 'é™„',
  '«@' => 'é™‚',
  '«A' => 'éš¹',
  '«B' => 'é›¨',
  '«C' => 'é�’',
  '«D' => 'é�ž',
  '«E' => 'äºŸ',
  '«F' => 'äº­',
  '«G' => 'äº®',
  '«H' => 'ä¿¡',
  '«I' => 'ä¾µ',
  '«J' => 'ä¾¯',
  '«K' => 'ä¾¿',
  '«L' => 'ä¿ ',
  '«M' => 'ä¿‘',
  '«N' => 'ä¿�',
  '«O' => 'ä¿�',
  '«P' => 'ä¿ƒ',
  '«Q' => 'ä¾¶',
  '«R' => 'ä¿˜',
  '«S' => 'ä¿Ÿ',
  '«T' => 'ä¿Š',
  '«U' => 'ä¿—',
  '«V' => 'ä¾®',
  '«W' => 'ä¿�',
  '«X' => 'ä¿„',
  '«Y' => 'ä¿‚',
  '«Z' => 'ä¿š',
  '«[' => 'ä¿Ž',
  '«\\' => 'ä¿ž',
  '«]' => 'ä¾·',
  '«^' => 'å…—',
  '«_' => 'å†’',
  '«`' => 'å†‘',
  '«a' => 'å† ',
  '«b' => 'å‰Ž',
  '«c' => 'å‰ƒ',
  '«d' => 'å‰Š',
  '«e' => 'å‰�',
  '«f' => 'å‰Œ',
  '«g' => 'å‰‹',
  '«h' => 'å‰‡',
  '«i' => 'å‹‡',
  '«j' => 'å‹‰',
  '«k' => 'å‹ƒ',
  '«l' => 'å‹�',
  '«m' => 'åŒ�',
  '«n' => 'å�—',
  '«o' => 'å�»',
  '«p' => 'åŽš',
  '«q' => 'å�›',
  '«r' => 'å’¬',
  '«s' => 'å“€',
  '«t' => 'å’¨',
  '«u' => 'å“Ž',
  '«v' => 'å“‰',
  '«w' => 'å’¸',
  '«x' => 'å’¦',
  '«y' => 'å’³',
  '«z' => 'å“‡',
  '«{' => 'å“‚',
  '«|' => 'å’½',
  '«}' => 'å’ª',
  '«~' => 'å“�',
  '«¡' => 'å“„',
  '«¢' => 'å“ˆ',
  '«£' => 'å’¯',
  '«¤' => 'å’«',
  '«¥' => 'å’±',
  '«¦' => 'å’»',
  '«§' => 'å’©',
  '«¨' => 'å’§',
  '«©' => 'å’¿',
  '«ª' => 'å›¿',
  '««' => 'åž‚',
  '«¬' => 'åž‹',
  '«­' => 'åž ',
  '«®' => 'åž£',
  '«¯' => 'åž¢',
  '«°' => 'åŸŽ',
  '«±' => 'åž®',
  '«²' => 'åž“',
  '«³' => 'å¥•',
  '«´' => 'å¥‘',
  '«µ' => 'å¥�',
  '«¶' => 'å¥Ž',
  '«·' => 'å¥�',
  '«¸' => 'å§œ',
  '«¹' => 'å§˜',
  '«º' => 'å§¿',
  '«»' => 'å§£',
  '«¼' => 'å§¨',
  '«½' => 'å¨ƒ',
  '«¾' => 'å§¥',
  '«¿' => 'å§ª',
  '«À' => 'å§š',
  '«Á' => 'å§¦',
  '«Â' => 'å¨�',
  '«Ã' => 'å§»',
  '«Ä' => 'å­©',
  '«Å' => 'å®£',
  '«Æ' => 'å®¦',
  '«Ç' => 'å®¤',
  '«È' => 'å®¢',
  '«É' => 'å®¥',
  '«Ê' => 'å°�',
  '«Ë' => 'å±Ž',
  '«Ì' => 'å±�',
  '«Í' => 'å±�',
  '«Î' => 'å±‹',
  '«Ï' => 'å³™',
  '«Ð' => 'å³’',
  '«Ñ' => 'å··',
  '«Ò' => 'å¸�',
  '«Ó' => 'å¸¥',
  '«Ô' => 'å¸Ÿ',
  '«Õ' => 'å¹½',
  '«Ö' => 'åº ',
  '«×' => 'åº¦',
  '«Ø' => 'å»º',
  '«Ù' => 'å¼ˆ',
  '«Ú' => 'å¼­',
  '«Û' => 'å½¥',
  '«Ü' => 'å¾ˆ',
  '«Ý' => 'å¾…',
  '«Þ' => 'å¾Š',
  '«ß' => 'å¾‹',
  '«à' => 'å¾‡',
  '«á' => 'å¾Œ',
  '«â' => 'å¾‰',
  '«ã' => 'æ€’',
  '«ä' => 'æ€�',
  '«å' => 'æ€ ',
  '«æ' => 'æ€¥',
  '«ç' => 'æ€Ž',
  '«è' => 'æ€¨',
  '«é' => 'æ��',
  '«ê' => 'æ�°',
  '«ë' => 'æ�¨',
  '«ì' => 'æ�¢',
  '«í' => 'æ�†',
  '«î' => 'æ�ƒ',
  '«ï' => 'æ�¬',
  '«ð' => 'æ�«',
  '«ñ' => 'æ�ª',
  '«ò' => 'æ�¤',
  '«ó' => 'æ‰�',
  '«ô' => 'æ‹œ',
  '«õ' => 'æŒ–',
  '«ö' => 'æŒ‰',
  '«÷' => 'æ‹¼',
  '«ø' => 'æ‹­',
  '«ù' => 'æŒ�',
  '«ú' => 'æ‹®',
  '«û' => 'æ‹½',
  '«ü' => 'æŒ‡',
  '«ý' => 'æ‹±',
  '«þ' => 'æ‹·',
  '¬@' => 'æ‹¯',
  '¬A' => 'æ‹¬',
  '¬B' => 'æ‹¾',
  '¬C' => 'æ‹´',
  '¬D' => 'æŒ‘',
  '¬E' => 'æŒ‚',
  '¬F' => 'æ”¿',
  '¬G' => 'æ•…',
  '¬H' => 'æ–«',
  '¬I' => 'æ–½',
  '¬J' => 'æ—¢',
  '¬K' => 'æ˜¥',
  '¬L' => 'æ˜­',
  '¬M' => 'æ˜ ',
  '¬N' => 'æ˜§',
  '¬O' => 'æ˜¯',
  '¬P' => 'æ˜Ÿ',
  '¬Q' => 'æ˜¨',
  '¬R' => 'æ˜±',
  '¬S' => 'æ˜¤',
  '¬T' => 'æ›·',
  '¬U' => 'æŸ¿',
  '¬V' => 'æŸ“',
  '¬W' => 'æŸ±',
  '¬X' => 'æŸ”',
  '¬Y' => 'æŸ�',
  '¬Z' => 'æŸ¬',
  '¬[' => 'æž¶',
  '¬\\' => 'æž¯',
  '¬]' => 'æŸµ',
  '¬^' => 'æŸ©',
  '¬_' => 'æŸ¯',
  '¬`' => 'æŸ„',
  '¬a' => 'æŸ‘',
  '¬b' => 'æž´',
  '¬c' => 'æŸš',
  '¬d' => 'æŸ¥',
  '¬e' => 'æž¸',
  '¬f' => 'æŸ�',
  '¬g' => 'æŸž',
  '¬h' => 'æŸ³',
  '¬i' => 'æž°',
  '¬j' => 'æŸ™',
  '¬k' => 'æŸ¢',
  '¬l' => 'æŸ�',
  '¬m' => 'æŸ’',
  '¬n' => 'æ­ª',
  '¬o' => 'æ®ƒ',
  '¬p' => 'æ®†',
  '¬q' => 'æ®µ',
  '¬r' => 'æ¯’',
  '¬s' => 'æ¯—',
  '¬t' => 'æ°Ÿ',
  '¬u' => 'æ³‰',
  '¬v' => 'æ´‹',
  '¬w' => 'æ´²',
  '¬x' => 'æ´ª',
  '¬y' => 'æµ�',
  '¬z' => 'æ´¥',
  '¬{' => 'æ´Œ',
  '¬|' => 'æ´±',
  '¬}' => 'æ´ž',
  '¬~' => 'æ´—',
  '¬¡' => 'æ´»',
  '¬¢' => 'æ´½',
  '¬£' => 'æ´¾',
  '¬¤' => 'æ´¶',
  '¬¥' => 'æ´›',
  '¬¦' => 'æ³µ',
  '¬§' => 'æ´¹',
  '¬¨' => 'æ´§',
  '¬©' => 'æ´¸',
  '¬ª' => 'æ´©',
  '¬«' => 'æ´®',
  '¬¬' => 'æ´µ',
  '¬­' => 'æ´Ž',
  '¬®' => 'æ´«',
  '¬¯' => 'ç‚«',
  '¬°' => 'ç‚º',
  '¬±' => 'ç‚³',
  '¬²' => 'ç‚¬',
  '¬³' => 'ç‚¯',
  '¬´' => 'ç‚­',
  '¬µ' => 'ç‚¸',
  '¬¶' => 'ç‚®',
  '¬·' => 'ç‚¤',
  '¬¸' => 'çˆ°',
  '¬¹' => 'ç‰²',
  '¬º' => 'ç‰¯',
  '¬»' => 'ç‰´',
  '¬¼' => 'ç‹©',
  '¬½' => 'ç‹ ',
  '¬¾' => 'ç‹¡',
  '¬¿' => 'çŽ·',
  '¬À' => 'ç�Š',
  '¬Á' => 'çŽ»',
  '¬Â' => 'çŽ²',
  '¬Ã' => 'ç��',
  '¬Ä' => 'ç�€',
  '¬Å' => 'çŽ³',
  '¬Æ' => 'ç”š',
  '¬Ç' => 'ç”­',
  '¬È' => 'ç•�',
  '¬É' => 'ç•Œ',
  '¬Ê' => 'ç•Ž',
  '¬Ë' => 'ç•‹',
  '¬Ì' => 'ç–«',
  '¬Í' => 'ç–¤',
  '¬Î' => 'ç–¥',
  '¬Ï' => 'ç–¢',
  '¬Ð' => 'ç–£',
  '¬Ñ' => 'ç™¸',
  '¬Ò' => 'çš†',
  '¬Ó' => 'çš‡',
  '¬Ô' => 'çšˆ',
  '¬Õ' => 'ç›ˆ',
  '¬Ö' => 'ç›†',
  '¬×' => 'ç›ƒ',
  '¬Ø' => 'ç›…',
  '¬Ù' => 'çœ�',
  '¬Ú' => 'ç›¹',
  '¬Û' => 'ç›¸',
  '¬Ü' => 'çœ‰',
  '¬Ý' => 'çœ‹',
  '¬Þ' => 'ç›¾',
  '¬ß' => 'ç›¼',
  '¬à' => 'çœ‡',
  '¬á' => 'çŸœ',
  '¬â' => 'ç ‚',
  '¬ã' => 'ç ”',
  '¬ä' => 'ç Œ',
  '¬å' => 'ç �',
  '¬æ' => 'ç¥†',
  '¬ç' => 'ç¥‰',
  '¬è' => 'ç¥ˆ',
  '¬é' => 'ç¥‡',
  '¬ê' => 'ç¦¹',
  '¬ë' => 'ç¦º',
  '¬ì' => 'ç§‘',
  '¬í' => 'ç§’',
  '¬î' => 'ç§‹',
  '¬ï' => 'ç©¿',
  '¬ð' => 'çª�',
  '¬ñ' => 'ç«¿',
  '¬ò' => 'ç«½',
  '¬ó' => 'ç±½',
  '¬ô' => 'ç´‚',
  '¬õ' => 'ç´…',
  '¬ö' => 'ç´€',
  '¬÷' => 'ç´‰',
  '¬ø' => 'ç´‡',
  '¬ù' => 'ç´„',
  '¬ú' => 'ç´†',
  '¬û' => 'ç¼¸',
  '¬ü' => 'ç¾Ž',
  '¬ý' => 'ç¾¿',
  '¬þ' => 'è€„',
  '­@' => 'è€�',
  '­A' => 'è€�',
  '­B' => 'è€‘',
  '­C' => 'è€¶',
  '­D' => 'èƒ–',
  '­E' => 'èƒ¥',
  '­F' => 'èƒš',
  '­G' => 'èƒƒ',
  '­H' => 'èƒ„',
  '­I' => 'èƒŒ',
  '­J' => 'èƒ¡',
  '­K' => 'èƒ›',
  '­L' => 'èƒŽ',
  '­M' => 'èƒž',
  '­N' => 'èƒ¤',
  '­O' => 'èƒ�',
  '­P' => 'è‡´',
  '­Q' => 'èˆ¢',
  '­R' => 'è‹§',
  '­S' => 'èŒƒ',
  '­T' => 'èŒ…',
  '­U' => 'è‹£',
  '­V' => 'è‹›',
  '­W' => 'è‹¦',
  '­X' => 'èŒ„',
  '­Y' => 'è‹¥',
  '­Z' => 'èŒ‚',
  '­[' => 'èŒ‰',
  '­\\' => 'è‹’',
  '­]' => 'è‹—',
  '­^' => 'è‹±',
  '­_' => 'èŒ�',
  '­`' => 'è‹œ',
  '­a' => 'è‹”',
  '­b' => 'è‹‘',
  '­c' => 'è‹ž',
  '­d' => 'è‹“',
  '­e' => 'è‹Ÿ',
  '­f' => 'è‹¯',
  '­g' => 'èŒ†',
  '­h' => 'è™�',
  '­i' => 'è™¹',
  '­j' => 'è™»',
  '­k' => 'è™º',
  '­l' => 'è¡�',
  '­m' => 'è¡«',
  '­n' => 'è¦�',
  '­o' => 'è§”',
  '­p' => 'è¨ˆ',
  '­q' => 'è¨‚',
  '­r' => 'è¨ƒ',
  '­s' => 'è²ž',
  '­t' => 'è² ',
  '­u' => 'èµ´',
  '­v' => 'èµ³',
  '­w' => 'è¶´',
  '­x' => 'è»�',
  '­y' => 'è»Œ',
  '­z' => 'è¿°',
  '­{' => 'è¿¦',
  '­|' => 'è¿¢',
  '­}' => 'è¿ª',
  '­~' => 'è¿¥',
  '­¡' => 'è¿­',
  '­¢' => 'è¿«',
  '­£' => 'è¿¤',
  '­¤' => 'è¿¨',
  '­¥' => 'éƒŠ',
  '­¦' => 'éƒŽ',
  '­§' => 'éƒ�',
  '­¨' => 'éƒƒ',
  '­©' => 'é…‹',
  '­ª' => 'é…Š',
  '­«' => 'é‡�',
  '­¬' => 'é–‚',
  '­­' => 'é™�',
  '­®' => 'é™‹',
  '­¯' => 'é™Œ',
  '­°' => 'é™�',
  '­±' => 'é�¢',
  '­²' => 'é�©',
  '­³' => 'éŸ‹',
  '­´' => 'éŸ­',
  '­µ' => 'éŸ³',
  '­¶' => 'é �',
  '­·' => 'é¢¨',
  '­¸' => 'é£›',
  '­¹' => 'é£Ÿ',
  '­º' => 'é¦–',
  '­»' => 'é¦™',
  '­¼' => 'ä¹˜',
  '­½' => 'äº³',
  '­¾' => 'å€Œ',
  '­¿' => 'å€�',
  '­À' => 'å€£',
  '­Á' => 'ä¿¯',
  '­Â' => 'å€¦',
  '­Ã' => 'å€¥',
  '­Ä' => 'ä¿¸',
  '­Å' => 'å€©',
  '­Æ' => 'å€–',
  '­Ç' => 'å€†',
  '­È' => 'å€¼',
  '­É' => 'å€Ÿ',
  '­Ê' => 'å€š',
  '­Ë' => 'å€’',
  '­Ì' => 'å€‘',
  '­Í' => 'ä¿º',
  '­Î' => 'å€€',
  '­Ï' => 'å€”',
  '­Ð' => 'å€¨',
  '­Ñ' => 'ä¿±',
  '­Ò' => 'å€¡',
  '­Ó' => 'å€‹',
  '­Ô' => 'å€™',
  '­Õ' => 'å€˜',
  '­Ö' => 'ä¿³',
  '­×' => 'ä¿®',
  '­Ø' => 'å€­',
  '­Ù' => 'å€ª',
  '­Ú' => 'ä¿¾',
  '­Û' => 'å€«',
  '­Ü' => 'å€‰',
  '­Ý' => 'å…¼',
  '­Þ' => 'å†¤',
  '­ß' => 'å†¥',
  '­à' => 'å†¢',
  '­á' => 'å‡�',
  '­â' => 'å‡Œ',
  '­ã' => 'å‡†',
  '­ä' => 'å‡‹',
  '­å' => 'å‰–',
  '­æ' => 'å‰œ',
  '­ç' => 'å‰”',
  '­è' => 'å‰›',
  '­é' => 'å‰�',
  '­ê' => 'åŒª',
  '­ë' => 'å�¿',
  '­ì' => 'åŽŸ',
  '­í' => 'åŽ�',
  '­î' => 'å�Ÿ',
  '­ï' => 'å“¨',
  '­ð' => 'å”�',
  '­ñ' => 'å”�',
  '­ò' => 'å”·',
  '­ó' => 'å“¼',
  '­ô' => 'å“¥',
  '­õ' => 'å“²',
  '­ö' => 'å”†',
  '­÷' => 'å“º',
  '­ø' => 'å””',
  '­ù' => 'å“©',
  '­ú' => 'å“­',
  '­û' => 'å“¡',
  '­ü' => 'å”‰',
  '­ý' => 'å“®',
  '­þ' => 'å“ª',
  '®@' => 'å“¦',
  '®A' => 'å”§',
  '®B' => 'å”‡',
  '®C' => 'å“½',
  '®D' => 'å”�',
  '®E' => 'åœƒ',
  '®F' => 'åœ„',
  '®G' => 'åŸ‚',
  '®H' => 'åŸ”',
  '®I' => 'åŸ‹',
  '®J' => 'åŸƒ',
  '®K' => 'å ‰',
  '®L' => 'å¤�',
  '®M' => 'å¥—',
  '®N' => 'å¥˜',
  '®O' => 'å¥š',
  '®P' => 'å¨‘',
  '®Q' => 'å¨˜',
  '®R' => 'å¨œ',
  '®S' => 'å¨Ÿ',
  '®T' => 'å¨›',
  '®U' => 'å¨“',
  '®V' => 'å§¬',
  '®W' => 'å¨ ',
  '®X' => 'å¨£',
  '®Y' => 'å¨©',
  '®Z' => 'å¨¥',
  '®[' => 'å¨Œ',
  '®\\' => 'å¨‰',
  '®]' => 'å­«',
  '®^' => 'å±˜',
  '®_' => 'å®°',
  '®`' => 'å®³',
  '®a' => 'å®¶',
  '®b' => 'å®´',
  '®c' => 'å®®',
  '®d' => 'å®µ',
  '®e' => 'å®¹',
  '®f' => 'å®¸',
  '®g' => 'å°„',
  '®h' => 'å±‘',
  '®i' => 'å±•',
  '®j' => 'å±�',
  '®k' => 'å³­',
  '®l' => 'å³½',
  '®m' => 'å³»',
  '®n' => 'å³ª',
  '®o' => 'å³¨',
  '®p' => 'å³°',
  '®q' => 'å³¶',
  '®r' => 'å´�',
  '®s' => 'å³´',
  '®t' => 'å·®',
  '®u' => 'å¸­',
  '®v' => 'å¸«',
  '®w' => 'åº«',
  '®x' => 'åº­',
  '®y' => 'åº§',
  '®z' => 'å¼±',
  '®{' => 'å¾’',
  '®|' => 'å¾‘',
  '®}' => 'å¾�',
  '®~' => 'æ�™',
  '®¡' => 'æ�£',
  '®¢' => 'æ�¥',
  '®£' => 'æ��',
  '®¤' => 'æ�•',
  '®¥' => 'æ�­',
  '®¦' => 'æ�©',
  '®§' => 'æ�¯',
  '®¨' => 'æ‚„',
  '®©' => 'æ‚Ÿ',
  '®ª' => 'æ‚š',
  '®«' => 'æ‚�',
  '®¬' => 'æ‚”',
  '®­' => 'æ‚Œ',
  '®®' => 'æ‚…',
  '®¯' => 'æ‚–',
  '®°' => 'æ‰‡',
  '®±' => 'æ‹³',
  '®²' => 'æŒˆ',
  '®³' => 'æ‹¿',
  '®´' => 'æ�Ž',
  '®µ' => 'æŒ¾',
  '®¶' => 'æŒ¯',
  '®·' => 'æ�•',
  '®¸' => 'æ�‚',
  '®¹' => 'æ�†',
  '®º' => 'æ��',
  '®»' => 'æ�‰',
  '®¼' => 'æŒº',
  '®½' => 'æ��',
  '®¾' => 'æŒ½',
  '®¿' => 'æŒª',
  '®À' => 'æŒ«',
  '®Á' => 'æŒ¨',
  '®Â' => 'æ��',
  '®Ã' => 'æ�Œ',
  '®Ä' => 'æ•ˆ',
  '®Å' => 'æ•‰',
  '®Æ' => 'æ–™',
  '®Ç' => 'æ—�',
  '®È' => 'æ—…',
  '®É' => 'æ™‚',
  '®Ê' => 'æ™‰',
  '®Ë' => 'æ™�',
  '®Ì' => 'æ™ƒ',
  '®Í' => 'æ™’',
  '®Î' => 'æ™Œ',
  '®Ï' => 'æ™…',
  '®Ð' => 'æ™�',
  '®Ñ' => 'æ›¸',
  '®Ò' => 'æœ”',
  '®Ó' => 'æœ•',
  '®Ô' => 'æœ—',
  '®Õ' => 'æ ¡',
  '®Ö' => 'æ ¸',
  '®×' => 'æ¡ˆ',
  '®Ø' => 'æ¡†',
  '®Ù' => 'æ¡“',
  '®Ú' => 'æ ¹',
  '®Û' => 'æ¡‚',
  '®Ü' => 'æ¡”',
  '®Ý' => 'æ ©',
  '®Þ' => 'æ¢³',
  '®ß' => 'æ —',
  '®à' => 'æ¡Œ',
  '®á' => 'æ¡‘',
  '®â' => 'æ ½',
  '®ã' => 'æŸ´',
  '®ä' => 'æ¡�',
  '®å' => 'æ¡€',
  '®æ' => 'æ ¼',
  '®ç' => 'æ¡ƒ',
  '®è' => 'æ ª',
  '®é' => 'æ¡…',
  '®ê' => 'æ “',
  '®ë' => 'æ ˜',
  '®ì' => 'æ¡�',
  '®í' => 'æ®Š',
  '®î' => 'æ®‰',
  '®ï' => 'æ®·',
  '®ð' => 'æ°£',
  '®ñ' => 'æ°§',
  '®ò' => 'æ°¨',
  '®ó' => 'æ°¦',
  '®ô' => 'æ°¤',
  '®õ' => 'æ³°',
  '®ö' => 'æµª',
  '®÷' => 'æ¶•',
  '®ø' => 'æ¶ˆ',
  '®ù' => 'æ¶‡',
  '®ú' => 'æµ¦',
  '®û' => 'æµ¸',
  '®ü' => 'æµ·',
  '®ý' => 'æµ™',
  '®þ' => 'æ¶“',
  '¯@' => 'æµ¬',
  '¯A' => 'æ¶‰',
  '¯B' => 'æµ®',
  '¯C' => 'æµš',
  '¯D' => 'æµ´',
  '¯E' => 'æµ©',
  '¯F' => 'æ¶Œ',
  '¯G' => 'æ¶Š',
  '¯H' => 'æµ¹',
  '¯I' => 'æ¶…',
  '¯J' => 'æµ¥',
  '¯K' => 'æ¶”',
  '¯L' => 'çƒŠ',
  '¯M' => 'çƒ˜',
  '¯N' => 'çƒ¤',
  '¯O' => 'çƒ™',
  '¯P' => 'çƒˆ',
  '¯Q' => 'çƒ�',
  '¯R' => 'çˆ¹',
  '¯S' => 'ç‰¹',
  '¯T' => 'ç‹¼',
  '¯U' => 'ç‹¹',
  '¯V' => 'ç‹½',
  '¯W' => 'ç‹¸',
  '¯X' => 'ç‹·',
  '¯Y' => 'çŽ†',
  '¯Z' => 'ç�­',
  '¯[' => 'ç�‰',
  '¯\\' => 'ç�®',
  '¯]' => 'ç� ',
  '¯^' => 'ç�ª',
  '¯_' => 'ç�ž',
  '¯`' => 'ç•”',
  '¯a' => 'ç•�',
  '¯b' => 'ç•œ',
  '¯c' => 'ç•š',
  '¯d' => 'ç•™',
  '¯e' => 'ç–¾',
  '¯f' => 'ç—…',
  '¯g' => 'ç—‡',
  '¯h' => 'ç–²',
  '¯i' => 'ç–³',
  '¯j' => 'ç–½',
  '¯k' => 'ç–¼',
  '¯l' => 'ç–¹',
  '¯m' => 'ç—‚',
  '¯n' => 'ç–¸',
  '¯o' => 'çš‹',
  '¯p' => 'çš°',
  '¯q' => 'ç›Š',
  '¯r' => 'ç›�',
  '¯s' => 'ç›Ž',
  '¯t' => 'çœ©',
  '¯u' => 'çœŸ',
  '¯v' => 'çœ ',
  '¯w' => 'çœ¨',
  '¯x' => 'çŸ©',
  '¯y' => 'ç °',
  '¯z' => 'ç §',
  '¯{' => 'ç ¸',
  '¯|' => 'ç �',
  '¯}' => 'ç ´',
  '¯~' => 'ç ·',
  '¯¡' => 'ç ¥',
  '¯¢' => 'ç ­',
  '¯£' => 'ç  ',
  '¯¤' => 'ç Ÿ',
  '¯¥' => 'ç ²',
  '¯¦' => 'ç¥•',
  '¯§' => 'ç¥�',
  '¯¨' => 'ç¥ ',
  '¯©' => 'ç¥Ÿ',
  '¯ª' => 'ç¥–',
  '¯«' => 'ç¥ž',
  '¯¬' => 'ç¥�',
  '¯­' => 'ç¥—',
  '¯®' => 'ç¥š',
  '¯¯' => 'ç§¤',
  '¯°' => 'ç§£',
  '¯±' => 'ç§§',
  '¯²' => 'ç§Ÿ',
  '¯³' => 'ç§¦',
  '¯´' => 'ç§©',
  '¯µ' => 'ç§˜',
  '¯¶' => 'çª„',
  '¯·' => 'çªˆ',
  '¯¸' => 'ç«™',
  '¯¹' => 'ç¬†',
  '¯º' => 'ç¬‘',
  '¯»' => 'ç²‰',
  '¯¼' => 'ç´¡',
  '¯½' => 'ç´—',
  '¯¾' => 'ç´‹',
  '¯¿' => 'ç´Š',
  '¯À' => 'ç´ ',
  '¯Á' => 'ç´¢',
  '¯Â' => 'ç´”',
  '¯Ã' => 'ç´�',
  '¯Ä' => 'ç´•',
  '¯Å' => 'ç´š',
  '¯Æ' => 'ç´œ',
  '¯Ç' => 'ç´�',
  '¯È' => 'ç´™',
  '¯É' => 'ç´›',
  '¯Ê' => 'ç¼º',
  '¯Ë' => 'ç½Ÿ',
  '¯Ì' => 'ç¾”',
  '¯Í' => 'ç¿…',
  '¯Î' => 'ç¿�',
  '¯Ï' => 'è€†',
  '¯Ð' => 'è€˜',
  '¯Ñ' => 'è€•',
  '¯Ò' => 'è€™',
  '¯Ó' => 'è€—',
  '¯Ô' => 'è€½',
  '¯Õ' => 'è€¿',
  '¯Ö' => 'èƒ±',
  '¯×' => 'è„‚',
  '¯Ø' => 'èƒ°',
  '¯Ù' => 'è„…',
  '¯Ú' => 'èƒ­',
  '¯Û' => 'èƒ´',
  '¯Ü' => 'è„†',
  '¯Ý' => 'èƒ¸',
  '¯Þ' => 'èƒ³',
  '¯ß' => 'è„ˆ',
  '¯à' => 'èƒ½',
  '¯á' => 'è„Š',
  '¯â' => 'èƒ¼',
  '¯ã' => 'èƒ¯',
  '¯ä' => 'è‡­',
  '¯å' => 'è‡¬',
  '¯æ' => 'èˆ€',
  '¯ç' => 'èˆ�',
  '¯è' => 'èˆª',
  '¯é' => 'èˆ«',
  '¯ê' => 'èˆ¨',
  '¯ë' => 'èˆ¬',
  '¯ì' => 'èŠ»',
  '¯í' => 'èŒ«',
  '¯î' => 'è�’',
  '¯ï' => 'è�”',
  '¯ð' => 'è�Š',
  '¯ñ' => 'èŒ¸',
  '¯ò' => 'è��',
  '¯ó' => 'è�‰',
  '¯ô' => 'èŒµ',
  '¯õ' => 'èŒ´',
  '¯ö' => 'è��',
  '¯÷' => 'èŒ²',
  '¯ø' => 'èŒ¹',
  '¯ù' => 'èŒ¶',
  '¯ú' => 'èŒ—',
  '¯û' => 'è�€',
  '¯ü' => 'èŒ±',
  '¯ý' => 'èŒ¨',
  '¯þ' => 'è�ƒ',
  '°@' => 'è™”',
  '°A' => 'èšŠ',
  '°B' => 'èšª',
  '°C' => 'èš“',
  '°D' => 'èš¤',
  '°E' => 'èš©',
  '°F' => 'èšŒ',
  '°G' => 'èš£',
  '°H' => 'èšœ',
  '°I' => 'è¡°',
  '°J' => 'è¡·',
  '°K' => 'è¢�',
  '°L' => 'è¢‚',
  '°M' => 'è¡½',
  '°N' => 'è¡¹',
  '°O' => 'è¨˜',
  '°P' => 'è¨�',
  '°Q' => 'è¨Ž',
  '°R' => 'è¨Œ',
  '°S' => 'è¨•',
  '°T' => 'è¨Š',
  '°U' => 'è¨—',
  '°V' => 'è¨“',
  '°W' => 'è¨–',
  '°X' => 'è¨�',
  '°Y' => 'è¨‘',
  '°Z' => 'è±ˆ',
  '°[' => 'è±º',
  '°\\' => 'è±¹',
  '°]' => 'è²¡',
  '°^' => 'è²¢',
  '°_' => 'èµ·',
  '°`' => 'èº¬',
  '°a' => 'è»’',
  '°b' => 'è»”',
  '°c' => 'è»�',
  '°d' => 'è¾±',
  '°e' => 'é€�',
  '°f' => 'é€†',
  '°g' => 'è¿·',
  '°h' => 'é€€',
  '°i' => 'è¿º',
  '°j' => 'è¿´',
  '°k' => 'é€ƒ',
  '°l' => 'è¿½',
  '°m' => 'é€…',
  '°n' => 'è¿¸',
  '°o' => 'é‚•',
  '°p' => 'éƒ¡',
  '°q' => 'éƒ�',
  '°r' => 'éƒ¢',
  '°s' => 'é…’',
  '°t' => 'é…�',
  '°u' => 'é…Œ',
  '°v' => 'é‡˜',
  '°w' => 'é‡�',
  '°x' => 'é‡—',
  '°y' => 'é‡œ',
  '°z' => 'é‡™',
  '°{' => 'é–ƒ',
  '°|' => 'é™¢',
  '°}' => 'é™£',
  '°~' => 'é™¡',
  '°¡' => 'é™›',
  '°¢' => 'é™�',
  '°£' => 'é™¤',
  '°¤' => 'é™˜',
  '°¥' => 'é™ž',
  '°¦' => 'éš»',
  '°§' => 'é£¢',
  '°¨' => 'é¦¬',
  '°©' => 'éª¨',
  '°ª' => 'é«˜',
  '°«' => 'é¬¥',
  '°¬' => 'é¬²',
  '°­' => 'é¬¼',
  '°®' => 'ä¹¾',
  '°¯' => 'å�º',
  '°°' => 'å�½',
  '°±' => 'å�œ',
  '°²' => 'å�‡',
  '°³' => 'å�ƒ',
  '°´' => 'å�Œ',
  '°µ' => 'å�š',
  '°¶' => 'å�‰',
  '°·' => 'å�¥',
  '°¸' => 'å�¶',
  '°¹' => 'å�Ž',
  '°º' => 'å�•',
  '°»' => 'å�µ',
  '°¼' => 'å�´',
  '°½' => 'å�·',
  '°¾' => 'å��',
  '°¿' => 'å€�',
  '°À' => 'å�¯',
  '°Á' => 'å�­',
  '°Â' => 'å…œ',
  '°Ã' => 'å†•',
  '°Ä' => 'å‡°',
  '°Å' => 'å‰ª',
  '°Æ' => 'å‰¯',
  '°Ç' => 'å‹’',
  '°È' => 'å‹™',
  '°É' => 'å‹˜',
  '°Ê' => 'å‹•',
  '°Ë' => 'åŒ�',
  '°Ì' => 'åŒ�',
  '°Í' => 'åŒ™',
  '°Î' => 'åŒ¿',
  '°Ï' => 'å�€',
  '°Ð' => 'åŒ¾',
  '°Ñ' => 'å�ƒ',
  '°Ò' => 'æ›¼',
  '°Ó' => 'å•†',
  '°Ô' => 'å•ª',
  '°Õ' => 'å•¦',
  '°Ö' => 'å•„',
  '°×' => 'å•ž',
  '°Ø' => 'å•¡',
  '°Ù' => 'å•ƒ',
  '°Ú' => 'å•Š',
  '°Û' => 'å”±',
  '°Ü' => 'å•–',
  '°Ý' => 'å•�',
  '°Þ' => 'å••',
  '°ß' => 'å”¯',
  '°à' => 'å•¤',
  '°á' => 'å”¸',
  '°â' => 'å”®',
  '°ã' => 'å•œ',
  '°ä' => 'å”¬',
  '°å' => 'å•£',
  '°æ' => 'å”³',
  '°ç' => 'å•�',
  '°è' => 'å•—',
  '°é' => 'åœˆ',
  '°ê' => 'åœ‹',
  '°ë' => 'åœ‰',
  '°ì' => 'åŸŸ',
  '°í' => 'å …',
  '°î' => 'å Š',
  '°ï' => 'å †',
  '°ð' => 'åŸ ',
  '°ñ' => 'åŸ¤',
  '°ò' => 'åŸº',
  '°ó' => 'å ‚',
  '°ô' => 'å µ',
  '°õ' => 'åŸ·',
  '°ö' => 'åŸ¹',
  '°÷' => 'å¤ ',
  '°ø' => 'å¥¢',
  '°ù' => 'å¨¶',
  '°ú' => 'å©�',
  '°û' => 'å©‰',
  '°ü' => 'å©¦',
  '°ý' => 'å©ª',
  '°þ' => 'å©€',
  '±@' => 'å¨¼',
  '±A' => 'å©¢',
  '±B' => 'å©š',
  '±C' => 'å©†',
  '±D' => 'å©Š',
  '±E' => 'å­°',
  '±F' => 'å¯‡',
  '±G' => 'å¯…',
  '±H' => 'å¯„',
  '±I' => 'å¯‚',
  '±J' => 'å®¿',
  '±K' => 'å¯†',
  '±L' => 'å°‰',
  '±M' => 'å°ˆ',
  '±N' => 'å°‡',
  '±O' => 'å± ',
  '±P' => 'å±œ',
  '±Q' => 'å±�',
  '±R' => 'å´‡',
  '±S' => 'å´†',
  '±T' => 'å´Ž',
  '±U' => 'å´›',
  '±V' => 'å´–',
  '±W' => 'å´¢',
  '±X' => 'å´‘',
  '±Y' => 'å´©',
  '±Z' => 'å´”',
  '±[' => 'å´™',
  '±\\' => 'å´¤',
  '±]' => 'å´§',
  '±^' => 'å´—',
  '±_' => 'å·¢',
  '±`' => 'å¸¸',
  '±a' => 'å¸¶',
  '±b' => 'å¸³',
  '±c' => 'å¸·',
  '±d' => 'åº·',
  '±e' => 'åº¸',
  '±f' => 'åº¶',
  '±g' => 'åºµ',
  '±h' => 'åº¾',
  '±i' => 'å¼µ',
  '±j' => 'å¼·',
  '±k' => 'å½—',
  '±l' => 'å½¬',
  '±m' => 'å½©',
  '±n' => 'å½«',
  '±o' => 'å¾—',
  '±p' => 'å¾™',
  '±q' => 'å¾ž',
  '±r' => 'å¾˜',
  '±s' => 'å¾¡',
  '±t' => 'å¾ ',
  '±u' => 'å¾œ',
  '±v' => 'æ�¿',
  '±w' => 'æ‚£',
  '±x' => 'æ‚‰',
  '±y' => 'æ‚ ',
  '±z' => 'æ‚¨',
  '±{' => 'æƒ‹',
  '±|' => 'æ‚´',
  '±}' => 'æƒ¦',
  '±~' => 'æ‚½',
  '±¡' => 'æƒ…',
  '±¢' => 'æ‚»',
  '±£' => 'æ‚µ',
  '±¤' => 'æƒœ',
  '±¥' => 'æ‚¼',
  '±¦' => 'æƒ˜',
  '±§' => 'æƒ•',
  '±¨' => 'æƒ†',
  '±©' => 'æƒŸ',
  '±ª' => 'æ‚¸',
  '±«' => 'æƒš',
  '±¬' => 'æƒ‡',
  '±­' => 'æˆš',
  '±®' => 'æˆ›',
  '±¯' => 'æ‰ˆ',
  '±°' => 'æŽ ',
  '±±' => 'æŽ§',
  '±²' => 'æ�²',
  '±³' => 'æŽ–',
  '±´' => 'æŽ¢',
  '±µ' => 'æŽ¥',
  '±¶' => 'æ�·',
  '±·' => 'æ�§',
  '±¸' => 'æŽ˜',
  '±¹' => 'æŽª',
  '±º' => 'æ�±',
  '±»' => 'æŽ©',
  '±¼' => 'æŽ‰',
  '±½' => 'æŽƒ',
  '±¾' => 'æŽ›',
  '±¿' => 'æ�«',
  '±À' => 'æŽ¨',
  '±Á' => 'æŽ„',
  '±Â' => 'æŽˆ',
  '±Ã' => 'æŽ™',
  '±Ä' => 'æŽ¡',
  '±Å' => 'æŽ¬',
  '±Æ' => 'æŽ’',
  '±Ç' => 'æŽ�',
  '±È' => 'æŽ€',
  '±É' => 'æ�»',
  '±Ê' => 'æ�©',
  '±Ë' => 'æ�¨',
  '±Ì' => 'æ�º',
  '±Í' => 'æ•�',
  '±Î' => 'æ•–',
  '±Ï' => 'æ•‘',
  '±Ð' => 'æ•™',
  '±Ñ' => 'æ•—',
  '±Ò' => 'å•Ÿ',
  '±Ó' => 'æ•�',
  '±Ô' => 'æ•˜',
  '±Õ' => 'æ••',
  '±Ö' => 'æ•”',
  '±×' => 'æ–œ',
  '±Ø' => 'æ–›',
  '±Ù' => 'æ–¬',
  '±Ú' => 'æ—�',
  '±Û' => 'æ—‹',
  '±Ü' => 'æ—Œ',
  '±Ý' => 'æ—Ž',
  '±Þ' => 'æ™�',
  '±ß' => 'æ™š',
  '±à' => 'æ™¤',
  '±á' => 'æ™¨',
  '±â' => 'æ™¦',
  '±ã' => 'æ™ž',
  '±ä' => 'æ›¹',
  '±å' => 'å‹—',
  '±æ' => 'æœ›',
  '±ç' => 'æ¢�',
  '±è' => 'æ¢¯',
  '±é' => 'æ¢¢',
  '±ê' => 'æ¢“',
  '±ë' => 'æ¢µ',
  '±ì' => 'æ¡¿',
  '±í' => 'æ¡¶',
  '±î' => 'æ¢±',
  '±ï' => 'æ¢§',
  '±ð' => 'æ¢—',
  '±ñ' => 'æ¢°',
  '±ò' => 'æ¢ƒ',
  '±ó' => 'æ£„',
  '±ô' => 'æ¢­',
  '±õ' => 'æ¢†',
  '±ö' => 'æ¢…',
  '±÷' => 'æ¢”',
  '±ø' => 'æ¢�',
  '±ù' => 'æ¢¨',
  '±ú' => 'æ¢Ÿ',
  '±û' => 'æ¢¡',
  '±ü' => 'æ¢‚',
  '±ý' => 'æ¬²',
  '±þ' => 'æ®º',
  '²@' => 'æ¯«',
  '²A' => 'æ¯¬',
  '²B' => 'æ°«',
  '²C' => 'æ¶Ž',
  '²D' => 'æ¶¼',
  '²E' => 'æ·³',
  '²F' => 'æ·™',
  '²G' => 'æ¶²',
  '²H' => 'æ·¡',
  '²I' => 'æ·Œ',
  '²J' => 'æ·¤',
  '²K' => 'æ·»',
  '²L' => 'æ·º',
  '²M' => 'æ¸…',
  '²N' => 'æ·‡',
  '²O' => 'æ·‹',
  '²P' => 'æ¶¯',
  '²Q' => 'æ·‘',
  '²R' => 'æ¶®',
  '²S' => 'æ·ž',
  '²T' => 'æ·¹',
  '²U' => 'æ¶¸',
  '²V' => 'æ··',
  '²W' => 'æ·µ',
  '²X' => 'æ·…',
  '²Y' => 'æ·’',
  '²Z' => 'æ¸š',
  '²[' => 'æ¶µ',
  '²\\' => 'æ·š',
  '²]' => 'æ·«',
  '²^' => 'æ·˜',
  '²_' => 'æ·ª',
  '²`' => 'æ·±',
  '²a' => 'æ·®',
  '²b' => 'æ·¨',
  '²c' => 'æ·†',
  '²d' => 'æ·„',
  '²e' => 'æ¶ª',
  '²f' => 'æ·¬',
  '²g' => 'æ¶¿',
  '²h' => 'æ·¦',
  '²i' => 'çƒ¹',
  '²j' => 'ç„‰',
  '²k' => 'ç„Š',
  '²l' => 'çƒ½',
  '²m' => 'çƒ¯',
  '²n' => 'çˆ½',
  '²o' => 'ç‰½',
  '²p' => 'çŠ�',
  '²q' => 'çŒœ',
  '²r' => 'çŒ›',
  '²s' => 'çŒ–',
  '²t' => 'çŒ“',
  '²u' => 'çŒ™',
  '²v' => 'çŽ‡',
  '²w' => 'ç�…',
  '²x' => 'ç�Š',
  '²y' => 'ç�ƒ',
  '²z' => 'ç�†',
  '²{' => 'ç�¾',
  '²|' => 'ç��',
  '²}' => 'ç“ ',
  '²~' => 'ç“¶',
  '²¡' => 'ç“·',
  '²¢' => 'ç”œ',
  '²£' => 'ç”¢',
  '²¤' => 'ç•¥',
  '²¥' => 'ç•¦',
  '²¦' => 'ç•¢',
  '²§' => 'ç•°',
  '²¨' => 'ç–�',
  '²©' => 'ç—”',
  '²ª' => 'ç—•',
  '²«' => 'ç–µ',
  '²¬' => 'ç—Š',
  '²­' => 'ç—�',
  '²®' => 'çšŽ',
  '²¯' => 'ç›”',
  '²°' => 'ç›’',
  '²±' => 'ç››',
  '²²' => 'çœ·',
  '²³' => 'çœ¾',
  '²´' => 'çœ¼',
  '²µ' => 'çœ¶',
  '²¶' => 'çœ¸',
  '²·' => 'çœº',
  '²¸' => 'ç¡«',
  '²¹' => 'ç¡ƒ',
  '²º' => 'ç¡Ž',
  '²»' => 'ç¥¥',
  '²¼' => 'ç¥¨',
  '²½' => 'ç¥­',
  '²¾' => 'ç§»',
  '²¿' => 'çª’',
  '²À' => 'çª•',
  '²Á' => 'ç¬ ',
  '²Â' => 'ç¬¨',
  '²Ã' => 'ç¬›',
  '²Ä' => 'ç¬¬',
  '²Å' => 'ç¬¦',
  '²Æ' => 'ç¬™',
  '²Ç' => 'ç¬ž',
  '²È' => 'ç¬®',
  '²É' => 'ç²’',
  '²Ê' => 'ç²—',
  '²Ë' => 'ç²•',
  '²Ì' => 'çµ†',
  '²Í' => 'çµƒ',
  '²Î' => 'çµ±',
  '²Ï' => 'ç´®',
  '²Ð' => 'ç´¹',
  '²Ñ' => 'ç´¼',
  '²Ò' => 'çµ€',
  '²Ó' => 'ç´°',
  '²Ô' => 'ç´³',
  '²Õ' => 'çµ„',
  '²Ö' => 'ç´¯',
  '²×' => 'çµ‚',
  '²Ø' => 'ç´²',
  '²Ù' => 'ç´±',
  '²Ú' => 'ç¼½',
  '²Û' => 'ç¾ž',
  '²Ü' => 'ç¾š',
  '²Ý' => 'ç¿Œ',
  '²Þ' => 'ç¿Ž',
  '²ß' => 'ç¿’',
  '²à' => 'è€œ',
  '²á' => 'è�Š',
  '²â' => 'è�†',
  '²ã' => 'è„¯',
  '²ä' => 'è„–',
  '²å' => 'è„£',
  '²æ' => 'è„«',
  '²ç' => 'è„©',
  '²è' => 'è„°',
  '²é' => 'è„¤',
  '²ê' => 'èˆ‚',
  '²ë' => 'èˆµ',
  '²ì' => 'èˆ·',
  '²í' => 'èˆ¶',
  '²î' => 'èˆ¹',
  '²ï' => 'èŽŽ',
  '²ð' => 'èŽž',
  '²ñ' => 'èŽ˜',
  '²ò' => 'è�¸',
  '²ó' => 'èŽ¢',
  '²ô' => 'èŽ–',
  '²õ' => 'èŽ½',
  '²ö' => 'èŽ«',
  '²÷' => 'èŽ’',
  '²ø' => 'èŽŠ',
  '²ù' => 'èŽ“',
  '²ú' => 'èŽ‰',
  '²û' => 'èŽ ',
  '²ü' => 'è�·',
  '²ý' => 'è�»',
  '²þ' => 'è�¼',
  '³@' => 'èŽ†',
  '³A' => 'èŽ§',
  '³B' => 'è™•',
  '³C' => 'å½ª',
  '³D' => 'è›‡',
  '³E' => 'è›€',
  '³F' => 'èš¶',
  '³G' => 'è›„',
  '³H' => 'èšµ',
  '³I' => 'è›†',
  '³J' => 'è›‹',
  '³K' => 'èš±',
  '³L' => 'èš¯',
  '³M' => 'è›‰',
  '³N' => 'è¡“',
  '³O' => 'è¢ž',
  '³P' => 'è¢ˆ',
  '³Q' => 'è¢«',
  '³R' => 'è¢’',
  '³S' => 'è¢–',
  '³T' => 'è¢�',
  '³U' => 'è¢‹',
  '³V' => 'è¦“',
  '³W' => 'è¦�',
  '³X' => 'è¨ª',
  '³Y' => 'è¨�',
  '³Z' => 'è¨£',
  '³[' => 'è¨¥',
  '³\\' => 'è¨±',
  '³]' => 'è¨­',
  '³^' => 'è¨Ÿ',
  '³_' => 'è¨›',
  '³`' => 'è¨¢',
  '³a' => 'è±‰',
  '³b' => 'è±š',
  '³c' => 'è²©',
  '³d' => 'è²¬',
  '³e' => 'è²«',
  '³f' => 'è²¨',
  '³g' => 'è²ª',
  '³h' => 'è²§',
  '³i' => 'èµ§',
  '³j' => 'èµ¦',
  '³k' => 'è¶¾',
  '³l' => 'è¶º',
  '³m' => 'è»›',
  '³n' => 'è»Ÿ',
  '³o' => 'é€™',
  '³p' => 'é€�',
  '³q' => 'é€š',
  '³r' => 'é€—',
  '³s' => 'é€£',
  '³t' => 'é€Ÿ',
  '³u' => 'é€�',
  '³v' => 'é€�',
  '³w' => 'é€•',
  '³x' => 'é€ž',
  '³y' => 'é€ ',
  '³z' => 'é€�',
  '³{' => 'é€¢',
  '³|' => 'é€–',
  '³}' => 'é€›',
  '³~' => 'é€”',
  '³¡' => 'éƒ¨',
  '³¢' => 'éƒ­',
  '³£' => 'éƒ½',
  '³¤' => 'é…—',
  '³¥' => 'é‡Ž',
  '³¦' => 'é‡µ',
  '³§' => 'é‡¦',
  '³¨' => 'é‡£',
  '³©' => 'é‡§',
  '³ª' => 'é‡­',
  '³«' => 'é‡©',
  '³¬' => 'é–‰',
  '³­' => 'é™ª',
  '³®' => 'é™µ',
  '³¯' => 'é™³',
  '³°' => 'é™¸',
  '³±' => 'é™°',
  '³²' => 'é™´',
  '³³' => 'é™¶',
  '³´' => 'é™·',
  '³µ' => 'é™¬',
  '³¶' => 'é›€',
  '³·' => 'é›ª',
  '³¸' => 'é›©',
  '³¹' => 'ç« ',
  '³º' => 'ç«Ÿ',
  '³»' => 'é ‚',
  '³¼' => 'é ƒ',
  '³½' => 'é­š',
  '³¾' => 'é³¥',
  '³¿' => 'é¹µ',
  '³À' => 'é¹¿',
  '³Á' => 'éº¥',
  '³Â' => 'éº»',
  '³Ã' => 'å‚¢',
  '³Ä' => 'å‚�',
  '³Å' => 'å‚…',
  '³Æ' => 'å‚™',
  '³Ç' => 'å‚‘',
  '³È' => 'å‚€',
  '³É' => 'å‚–',
  '³Ê' => 'å‚˜',
  '³Ë' => 'å‚š',
  '³Ì' => 'æœ€',
  '³Í' => 'å‡±',
  '³Î' => 'å‰²',
  '³Ï' => 'å‰´',
  '³Ð' => 'å‰µ',
  '³Ñ' => 'å‰©',
  '³Ò' => 'å‹ž',
  '³Ó' => 'å‹�',
  '³Ô' => 'å‹›',
  '³Õ' => 'å�š',
  '³Ö' => 'åŽ¥',
  '³×' => 'å•»',
  '³Ø' => 'å–€',
  '³Ù' => 'å–§',
  '³Ú' => 'å•¼',
  '³Û' => 'å–Š',
  '³Ü' => 'å–�',
  '³Ý' => 'å–˜',
  '³Þ' => 'å–‚',
  '³ß' => 'å–œ',
  '³à' => 'å–ª',
  '³á' => 'å–”',
  '³â' => 'å–‡',
  '³ã' => 'å–‹',
  '³ä' => 'å–ƒ',
  '³å' => 'å–³',
  '³æ' => 'å–®',
  '³ç' => 'å–Ÿ',
  '³è' => 'å”¾',
  '³é' => 'å–²',
  '³ê' => 'å–š',
  '³ë' => 'å–»',
  '³ì' => 'å–¬',
  '³í' => 'å–±',
  '³î' => 'å•¾',
  '³ï' => 'å–‰',
  '³ð' => 'å–«',
  '³ñ' => 'å–™',
  '³ò' => 'åœ�',
  '³ó' => 'å ¯',
  '³ô' => 'å ª',
  '³õ' => 'å ´',
  '³ö' => 'å ¤',
  '³÷' => 'å °',
  '³ø' => 'å ±',
  '³ù' => 'å ¡',
  '³ú' => 'å �',
  '³û' => 'å  ',
  '³ü' => 'å£¹',
  '³ý' => 'å£º',
  '³þ' => 'å¥ ',
  '´@' => 'å©·',
  '´A' => 'åªš',
  '´B' => 'å©¿',
  '´C' => 'åª’',
  '´D' => 'åª›',
  '´E' => 'åª§',
  '´F' => 'å­³',
  '´G' => 'å­±',
  '´H' => 'å¯’',
  '´I' => 'å¯Œ',
  '´J' => 'å¯“',
  '´K' => 'å¯�',
  '´L' => 'å°Š',
  '´M' => 'å°‹',
  '´N' => 'å°±',
  '´O' => 'åµŒ',
  '´P' => 'åµ�',
  '´Q' => 'å´´',
  '´R' => 'åµ‡',
  '´S' => 'å·½',
  '´T' => 'å¹…',
  '´U' => 'å¸½',
  '´V' => 'å¹€',
  '´W' => 'å¹ƒ',
  '´X' => 'å¹¾',
  '´Y' => 'å»Š',
  '´Z' => 'å»�',
  '´[' => 'å»‚',
  '´\\' => 'å»„',
  '´]' => 'å¼¼',
  '´^' => 'å½­',
  '´_' => 'å¾©',
  '´`' => 'å¾ª',
  '´a' => 'å¾¨',
  '´b' => 'æƒ‘',
  '´c' => 'æƒ¡',
  '´d' => 'æ‚²',
  '´e' => 'æ‚¶',
  '´f' => 'æƒ ',
  '´g' => 'æ„œ',
  '´h' => 'æ„£',
  '´i' => 'æƒº',
  '´j' => 'æ„•',
  '´k' => 'æƒ°',
  '´l' => 'æƒ»',
  '´m' => 'æƒ´',
  '´n' => 'æ…¨',
  '´o' => 'æƒ±',
  '´p' => 'æ„Ž',
  '´q' => 'æƒ¶',
  '´r' => 'æ„‰',
  '´s' => 'æ„€',
  '´t' => 'æ„’',
  '´u' => 'æˆŸ',
  '´v' => 'æ‰‰',
  '´w' => 'æŽ£',
  '´x' => 'æŽŒ',
  '´y' => 'æ��',
  '´z' => 'æ�€',
  '´{' => 'æ�©',
  '´|' => 'æ�‰',
  '´}' => 'æ�†',
  '´~' => 'æ��',
  '´¡' => 'æ�’',
  '´¢' => 'æ�£',
  '´£' => 'æ��',
  '´¤' => 'æ�¡',
  '´¥' => 'æ�–',
  '´¦' => 'æ�­',
  '´§' => 'æ�®',
  '´¨' => 'æ�¶',
  '´©' => 'æ�´',
  '´ª' => 'æ�ª',
  '´«' => 'æ�›',
  '´¬' => 'æ‘’',
  '´­' => 'æ�š',
  '´®' => 'æ�¹',
  '´¯' => 'æ•ž',
  '´°' => 'æ•¦',
  '´±' => 'æ•¢',
  '´²' => 'æ•£',
  '´³' => 'æ–‘',
  '´´' => 'æ–�',
  '´µ' => 'æ–¯',
  '´¶' => 'æ™®',
  '´·' => 'æ™°',
  '´¸' => 'æ™´',
  '´¹' => 'æ™¶',
  '´º' => 'æ™¯',
  '´»' => 'æš‘',
  '´¼' => 'æ™º',
  '´½' => 'æ™¾',
  '´¾' => 'æ™·',
  '´¿' => 'æ›¾',
  '´À' => 'æ›¿',
  '´Á' => 'æœŸ',
  '´Â' => 'æœ�',
  '´Ã' => 'æ£º',
  '´Ä' => 'æ£•',
  '´Å' => 'æ£ ',
  '´Æ' => 'æ£˜',
  '´Ç' => 'æ£—',
  '´È' => 'æ¤…',
  '´É' => 'æ£Ÿ',
  '´Ê' => 'æ£µ',
  '´Ë' => 'æ£®',
  '´Ì' => 'æ£§',
  '´Í' => 'æ£¹',
  '´Î' => 'æ£’',
  '´Ï' => 'æ£²',
  '´Ð' => 'æ££',
  '´Ñ' => 'æ£‹',
  '´Ò' => 'æ£�',
  '´Ó' => 'æ¤�',
  '´Ô' => 'æ¤’',
  '´Õ' => 'æ¤Ž',
  '´Ö' => 'æ£‰',
  '´×' => 'æ£š',
  '´Ø' => 'æ¥®',
  '´Ù' => 'æ£»',
  '´Ú' => 'æ¬¾',
  '´Û' => 'æ¬º',
  '´Ü' => 'æ¬½',
  '´Ý' => 'æ®˜',
  '´Þ' => 'æ®–',
  '´ß' => 'æ®¼',
  '´à' => 'æ¯¯',
  '´á' => 'æ°®',
  '´â' => 'æ°¯',
  '´ã' => 'æ°¬',
  '´ä' => 'æ¸¯',
  '´å' => 'æ¸¸',
  '´æ' => 'æ¹”',
  '´ç' => 'æ¸¡',
  '´è' => 'æ¸²',
  '´é' => 'æ¹§',
  '´ê' => 'æ¹Š',
  '´ë' => 'æ¸ ',
  '´ì' => 'æ¸¥',
  '´í' => 'æ¸£',
  '´î' => 'æ¸›',
  '´ï' => 'æ¹›',
  '´ð' => 'æ¹˜',
  '´ñ' => 'æ¸¤',
  '´ò' => 'æ¹–',
  '´ó' => 'æ¹®',
  '´ô' => 'æ¸­',
  '´õ' => 'æ¸¦',
  '´ö' => 'æ¹¯',
  '´÷' => 'æ¸´',
  '´ø' => 'æ¹�',
  '´ù' => 'æ¸º',
  '´ú' => 'æ¸¬',
  '´û' => 'æ¹ƒ',
  '´ü' => 'æ¸�',
  '´ý' => 'æ¸¾',
  '´þ' => 'æ»‹',
  'µ@' => 'æº‰',
  'µA' => 'æ¸™',
  'µB' => 'æ¹Ž',
  'µC' => 'æ¹£',
  'µD' => 'æ¹„',
  'µE' => 'æ¹²',
  'µF' => 'æ¹©',
  'µG' => 'æ¹Ÿ',
  'µH' => 'ç„™',
  'µI' => 'ç„š',
  'µJ' => 'ç„¦',
  'µK' => 'ç„°',
  'µL' => 'ç„¡',
  'µM' => 'ç„¶',
  'µN' => 'ç…®',
  'µO' => 'ç„œ',
  'µP' => 'ç‰Œ',
  'µQ' => 'çŠ„',
  'µR' => 'çŠ€',
  'µS' => 'çŒ¶',
  'µT' => 'çŒ¥',
  'µU' => 'çŒ´',
  'µV' => 'çŒ©',
  'µW' => 'ç�º',
  'µX' => 'ç�ª',
  'µY' => 'ç�³',
  'µZ' => 'ç�¢',
  'µ[' => 'ç�¥',
  'µ\\' => 'ç�µ',
  'µ]' => 'ç�¶',
  'µ^' => 'ç�´',
  'µ_' => 'ç�¯',
  'µ`' => 'ç�›',
  'µa' => 'ç�¦',
  'µb' => 'ç�¨',
  'µc' => 'ç”¥',
  'µd' => 'ç”¦',
  'µe' => 'ç•«',
  'µf' => 'ç•ª',
  'µg' => 'ç—¢',
  'µh' => 'ç—›',
  'µi' => 'ç—£',
  'µj' => 'ç—™',
  'µk' => 'ç—˜',
  'µl' => 'ç—ž',
  'µm' => 'ç— ',
  'µn' => 'ç™»',
  'µo' => 'ç™¼',
  'µp' => 'çš–',
  'µq' => 'çš“',
  'µr' => 'çš´',
  'µs' => 'ç›œ',
  'µt' => 'ç��',
  'µu' => 'çŸ­',
  'µv' => 'ç¡�',
  'µw' => 'ç¡¬',
  'µx' => 'ç¡¯',
  'µy' => 'ç¨�',
  'µz' => 'ç¨ˆ',
  'µ{' => 'ç¨‹',
  'µ|' => 'ç¨…',
  'µ}' => 'ç¨€',
  'µ~' => 'çª˜',
  'µ¡' => 'çª—',
  'µ¢' => 'çª–',
  'µ£' => 'ç«¥',
  'µ¤' => 'ç«£',
  'µ¥' => 'ç­‰',
  'µ¦' => 'ç­–',
  'µ§' => 'ç­†',
  'µ¨' => 'ç­�',
  'µ©' => 'ç­’',
  'µª' => 'ç­”',
  'µ«' => 'ç­�',
  'µ¬' => 'ç­‹',
  'µ­' => 'ç­�',
  'µ®' => 'ç­‘',
  'µ¯' => 'ç²Ÿ',
  'µ°' => 'ç²¥',
  'µ±' => 'çµž',
  'µ²' => 'çµ�',
  'µ³' => 'çµ¨',
  'µ´' => 'çµ•',
  'µµ' => 'ç´«',
  'µ¶' => 'çµ®',
  'µ·' => 'çµ²',
  'µ¸' => 'çµ¡',
  'µ¹' => 'çµ¦',
  'µº' => 'çµ¢',
  'µ»' => 'çµ°',
  'µ¼' => 'çµ³',
  'µ½' => 'å–„',
  'µ¾' => 'ç¿”',
  'µ¿' => 'ç¿•',
  'µÀ' => 'è€‹',
  'µÁ' => 'è�’',
  'µÂ' => 'è‚…',
  'µÃ' => 'è…•',
  'µÄ' => 'è…”',
  'µÅ' => 'è…‹',
  'µÆ' => 'è…‘',
  'µÇ' => 'è…Ž',
  'µÈ' => 'è„¹',
  'µÉ' => 'è…†',
  'µÊ' => 'è„¾',
  'µË' => 'è…Œ',
  'µÌ' => 'è…“',
  'µÍ' => 'è…´',
  'µÎ' => 'èˆ’',
  'µÏ' => 'èˆœ',
  'µÐ' => 'è�©',
  'µÑ' => 'è�ƒ',
  'µÒ' => 'è�¸',
  'µÓ' => 'è��',
  'µÔ' => 'è� ',
  'µÕ' => 'è�…',
  'µÖ' => 'è�‹',
  'µ×' => 'è��',
  'µØ' => 'è�¯',
  'µÙ' => 'è�±',
  'µÚ' => 'è�´',
  'µÛ' => 'è‘—',
  'µÜ' => 'è�Š',
  'µÝ' => 'è�°',
  'µÞ' => 'è�Œ',
  'µß' => 'è�Œ',
  'µà' => 'è�½',
  'µá' => 'è�²',
  'µâ' => 'è�Š',
  'µã' => 'è�¸',
  'µä' => 'è�Ž',
  'µå' => 'è�„',
  'µæ' => 'è�œ',
  'µç' => 'è�‡',
  'µè' => 'è�”',
  'µé' => 'è�Ÿ',
  'µê' => 'è™›',
  'µë' => 'è›Ÿ',
  'µì' => 'è›™',
  'µí' => 'è›­',
  'µî' => 'è›”',
  'µï' => 'è››',
  'µð' => 'è›¤',
  'µñ' => 'è›�',
  'µò' => 'è›ž',
  'µó' => 'è¡—',
  'µô' => 'è£�',
  'µõ' => 'è£‚',
  'µö' => 'è¢±',
  'µ÷' => 'è¦ƒ',
  'µø' => 'è¦–',
  'µù' => 'è¨»',
  'µú' => 'è© ',
  'µû' => 'è©•',
  'µü' => 'è©ž',
  'µý' => 'è¨¼',
  'µþ' => 'è©�',
  '¶@' => 'è©”',
  '¶A' => 'è©›',
  '¶B' => 'è©�',
  '¶C' => 'è©†',
  '¶D' => 'è¨´',
  '¶E' => 'è¨º',
  '¶F' => 'è¨¶',
  '¶G' => 'è©–',
  '¶H' => 'è±¡',
  '¶I' => 'è²‚',
  '¶J' => 'è²¯',
  '¶K' => 'è²¼',
  '¶L' => 'è²³',
  '¶M' => 'è²½',
  '¶N' => 'è³�',
  '¶O' => 'è²»',
  '¶P' => 'è³€',
  '¶Q' => 'è²´',
  '¶R' => 'è²·',
  '¶S' => 'è²¶',
  '¶T' => 'è²¿',
  '¶U' => 'è²¸',
  '¶V' => 'è¶Š',
  '¶W' => 'è¶…',
  '¶X' => 'è¶�',
  '¶Y' => 'è·Ž',
  '¶Z' => 'è·�',
  '¶[' => 'è·‹',
  '¶\\' => 'è·š',
  '¶]' => 'è·‘',
  '¶^' => 'è·Œ',
  '¶_' => 'è·›',
  '¶`' => 'è·†',
  '¶a' => 'è»»',
  '¶b' => 'è»¸',
  '¶c' => 'è»¼',
  '¶d' => 'è¾œ',
  '¶e' => 'é€®',
  '¶f' => 'é€µ',
  '¶g' => 'é€±',
  '¶h' => 'é€¸',
  '¶i' => 'é€²',
  '¶j' => 'é€¶',
  '¶k' => 'é„‚',
  '¶l' => 'éƒµ',
  '¶m' => 'é„‰',
  '¶n' => 'éƒ¾',
  '¶o' => 'é…£',
  '¶p' => 'é…¥',
  '¶q' => 'é‡�',
  '¶r' => 'éˆ”',
  '¶s' => 'éˆ•',
  '¶t' => 'éˆ£',
  '¶u' => 'éˆ‰',
  '¶v' => 'éˆž',
  '¶w' => 'éˆ�',
  '¶x' => 'éˆ�',
  '¶y' => 'éˆ‡',
  '¶z' => 'éˆ‘',
  '¶{' => 'é–”',
  '¶|' => 'é–�',
  '¶}' => 'é–‹',
  '¶~' => 'é–‘',
  '¶¡' => 'é–“',
  '¶¢' => 'é–’',
  '¶£' => 'é–Ž',
  '¶¤' => 'éšŠ',
  '¶¥' => 'éšŽ',
  '¶¦' => 'éš‹',
  '¶§' => 'é™½',
  '¶¨' => 'éš…',
  '¶©' => 'éš†',
  '¶ª' => 'éš�',
  '¶«' => 'é™²',
  '¶¬' => 'éš„',
  '¶­' => 'é›�',
  '¶®' => 'é›…',
  '¶¯' => 'é›„',
  '¶°' => 'é›†',
  '¶±' => 'é›‡',
  '¶²' => 'é›¯',
  '¶³' => 'é›²',
  '¶´' => 'éŸŒ',
  '¶µ' => 'é …',
  '¶¶' => 'é †',
  '¶·' => 'é ˆ',
  '¶¸' => 'é£§',
  '¶¹' => 'é£ª',
  '¶º' => 'é£¯',
  '¶»' => 'é£©',
  '¶¼' => 'é£²',
  '¶½' => 'é£­',
  '¶¾' => 'é¦®',
  '¶¿' => 'é¦­',
  '¶À' => 'é»ƒ',
  '¶Á' => 'é»�',
  '¶Â' => 'é»‘',
  '¶Ã' => 'äº‚',
  '¶Ä' => 'å‚­',
  '¶Å' => 'å‚µ',
  '¶Æ' => 'å‚²',
  '¶Ç' => 'å‚³',
  '¶È' => 'åƒ…',
  '¶É' => 'å‚¾',
  '¶Ê' => 'å‚¬',
  '¶Ë' => 'å‚·',
  '¶Ì' => 'å‚»',
  '¶Í' => 'å‚¯',
  '¶Î' => 'åƒ‡',
  '¶Ï' => 'å‰¿',
  '¶Ð' => 'å‰·',
  '¶Ñ' => 'å‰½',
  '¶Ò' => 'å‹Ÿ',
  '¶Ó' => 'å‹¦',
  '¶Ô' => 'å‹¤',
  '¶Õ' => 'å‹¢',
  '¶Ö' => 'å‹£',
  '¶×' => 'åŒ¯',
  '¶Ø' => 'å—Ÿ',
  '¶Ù' => 'å—¨',
  '¶Ú' => 'å—“',
  '¶Û' => 'å—¦',
  '¶Ü' => 'å—Ž',
  '¶Ý' => 'å—œ',
  '¶Þ' => 'å—‡',
  '¶ß' => 'å—‘',
  '¶à' => 'å—£',
  '¶á' => 'å—¤',
  '¶â' => 'å—¯',
  '¶ã' => 'å—š',
  '¶ä' => 'å—¡',
  '¶å' => 'å—…',
  '¶æ' => 'å—†',
  '¶ç' => 'å—¥',
  '¶è' => 'å—‰',
  '¶é' => 'åœ’',
  '¶ê' => 'åœ“',
  '¶ë' => 'å¡ž',
  '¶ì' => 'å¡‘',
  '¶í' => 'å¡˜',
  '¶î' => 'å¡—',
  '¶ï' => 'å¡š',
  '¶ð' => 'å¡”',
  '¶ñ' => 'å¡«',
  '¶ò' => 'å¡Œ',
  '¶ó' => 'å¡­',
  '¶ô' => 'å¡Š',
  '¶õ' => 'å¡¢',
  '¶ö' => 'å¡’',
  '¶÷' => 'å¡‹',
  '¶ø' => 'å¥§',
  '¶ù' => 'å«�',
  '¶ú' => 'å«‰',
  '¶û' => 'å«Œ',
  '¶ü' => 'åª¾',
  '¶ý' => 'åª½',
  '¶þ' => 'åª¼',
  '·@' => 'åª³',
  '·A' => 'å«‚',
  '·B' => 'åª²',
  '·C' => 'åµ©',
  '·D' => 'åµ¯',
  '·E' => 'å¹Œ',
  '·F' => 'å¹¹',
  '·G' => 'å»‰',
  '·H' => 'å»ˆ',
  '·I' => 'å¼’',
  '·J' => 'å½™',
  '·K' => 'å¾¬',
  '·L' => 'å¾®',
  '·M' => 'æ„š',
  '·N' => 'æ„�',
  '·O' => 'æ…ˆ',
  '·P' => 'æ„Ÿ',
  '·Q' => 'æƒ³',
  '·R' => 'æ„›',
  '·S' => 'æƒ¹',
  '·T' => 'æ„�',
  '·U' => 'æ„ˆ',
  '·V' => 'æ…Ž',
  '·W' => 'æ…Œ',
  '·X' => 'æ…„',
  '·Y' => 'æ…�',
  '·Z' => 'æ„¾',
  '·[' => 'æ„´',
  '·\\' => 'æ„§',
  '·]' => 'æ„�',
  '·^' => 'æ„†',
  '·_' => 'æ„·',
  '·`' => 'æˆ¡',
  '·a' => 'æˆ¢',
  '·b' => 'æ�“',
  '·c' => 'æ�¾',
  '·d' => 'æ�ž',
  '·e' => 'æ�ª',
  '·f' => 'æ�­',
  '·g' => 'æ�½',
  '·h' => 'æ�¬',
  '·i' => 'æ��',
  '·j' => 'æ�œ',
  '·k' => 'æ�”',
  '·l' => 'æ��',
  '·m' => 'æ�¶',
  '·n' => 'æ�–',
  '·o' => 'æ�—',
  '·p' => 'æ�†',
  '·q' => 'æ•¬',
  '·r' => 'æ–Ÿ',
  '·s' => 'æ–°',
  '·t' => 'æš—',
  '·u' => 'æš‰',
  '·v' => 'æš‡',
  '·w' => 'æšˆ',
  '·x' => 'æš–',
  '·y' => 'æš„',
  '·z' => 'æš˜',
  '·{' => 'æš�',
  '·|' => 'æœƒ',
  '·}' => 'æ¦”',
  '·~' => 'æ¥­',
  '·¡' => 'æ¥š',
  '·¢' => 'æ¥·',
  '·£' => 'æ¥ ',
  '·¤' => 'æ¥”',
  '·¥' => 'æ¥µ',
  '·¦' => 'æ¤°',
  '·§' => 'æ¦‚',
  '·¨' => 'æ¥Š',
  '·©' => 'æ¥¨',
  '·ª' => 'æ¥«',
  '·«' => 'æ¥ž',
  '·¬' => 'æ¥“',
  '·­' => 'æ¥¹',
  '·®' => 'æ¦†',
  '·¯' => 'æ¥�',
  '·°' => 'æ¥£',
  '·±' => 'æ¥›',
  '·²' => 'æ­‡',
  '·³' => 'æ­²',
  '·´' => 'æ¯€',
  '·µ' => 'æ®¿',
  '·¶' => 'æ¯“',
  '··' => 'æ¯½',
  '·¸' => 'æº¢',
  '·¹' => 'æº¯',
  '·º' => 'æ»“',
  '·»' => 'æº¶',
  '·¼' => 'æ»‚',
  '·½' => 'æº�',
  '·¾' => 'æº�',
  '·¿' => 'æ»‡',
  '·À' => 'æ»…',
  '·Á' => 'æº¥',
  '·Â' => 'æº˜',
  '·Ã' => 'æº¼',
  '·Ä' => 'æºº',
  '·Å' => 'æº«',
  '·Æ' => 'æ»‘',
  '·Ç' => 'æº–',
  '·È' => 'æºœ',
  '·É' => 'æ»„',
  '·Ê' => 'æ»”',
  '·Ë' => 'æºª',
  '·Ì' => 'æº§',
  '·Í' => 'æº´',
  '·Î' => 'ç…Ž',
  '·Ï' => 'ç…™',
  '·Ð' => 'ç…©',
  '·Ñ' => 'ç…¤',
  '·Ò' => 'ç…‰',
  '·Ó' => 'ç…§',
  '·Ô' => 'ç…œ',
  '·Õ' => 'ç…¬',
  '·Ö' => 'ç…¦',
  '·×' => 'ç…Œ',
  '·Ø' => 'ç…¥',
  '·Ù' => 'ç…ž',
  '·Ú' => 'ç…†',
  '·Û' => 'ç…¨',
  '·Ü' => 'ç…–',
  '·Ý' => 'çˆº',
  '·Þ' => 'ç‰’',
  '·ß' => 'çŒ·',
  '·à' => 'ç�…',
  '·á' => 'çŒ¿',
  '·â' => 'çŒ¾',
  '·ã' => 'ç‘¯',
  '·ä' => 'ç‘š',
  '·å' => 'ç‘•',
  '·æ' => 'ç‘Ÿ',
  '·ç' => 'ç‘ž',
  '·è' => 'ç‘�',
  '·é' => 'ç�¿',
  '·ê' => 'ç‘™',
  '·ë' => 'ç‘›',
  '·ì' => 'ç‘œ',
  '·í' => 'ç•¶',
  '·î' => 'ç•¸',
  '·ï' => 'ç˜€',
  '·ð' => 'ç—°',
  '·ñ' => 'ç˜�',
  '·ò' => 'ç—²',
  '·ó' => 'ç—±',
  '·ô' => 'ç—º',
  '·õ' => 'ç—¿',
  '·ö' => 'ç—´',
  '·÷' => 'ç—³',
  '·ø' => 'ç›ž',
  '·ù' => 'ç›Ÿ',
  '·ú' => 'ç�›',
  '·û' => 'ç�«',
  '·ü' => 'ç�¦',
  '·ý' => 'ç�ž',
  '·þ' => 'ç�£',
  '¸@' => 'ç�¹',
  '¸A' => 'ç�ª',
  '¸B' => 'ç�¬',
  '¸C' => 'ç�œ',
  '¸D' => 'ç�¥',
  '¸E' => 'ç�¨',
  '¸F' => 'ç�¢',
  '¸G' => 'çŸ®',
  '¸H' => 'ç¢Ž',
  '¸I' => 'ç¢°',
  '¸J' => 'ç¢—',
  '¸K' => 'ç¢˜',
  '¸L' => 'ç¢Œ',
  '¸M' => 'ç¢‰',
  '¸N' => 'ç¡¼',
  '¸O' => 'ç¢‘',
  '¸P' => 'ç¢“',
  '¸Q' => 'ç¡¿',
  '¸R' => 'ç¥º',
  '¸S' => 'ç¥¿',
  '¸T' => 'ç¦�',
  '¸U' => 'è�¬',
  '¸V' => 'ç¦½',
  '¸W' => 'ç¨œ',
  '¸X' => 'ç¨š',
  '¸Y' => 'ç¨ ',
  '¸Z' => 'ç¨”',
  '¸[' => 'ç¨Ÿ',
  '¸\\' => 'ç¨ž',
  '¸]' => 'çªŸ',
  '¸^' => 'çª ',
  '¸_' => 'ç­·',
  '¸`' => 'ç¯€',
  '¸a' => 'ç­ ',
  '¸b' => 'ç­®',
  '¸c' => 'ç­§',
  '¸d' => 'ç²±',
  '¸e' => 'ç²³',
  '¸f' => 'ç²µ',
  '¸g' => 'ç¶“',
  '¸h' => 'çµ¹',
  '¸i' => 'ç¶‘',
  '¸j' => 'ç¶�',
  '¸k' => 'ç¶�',
  '¸l' => 'çµ›',
  '¸m' => 'ç½®',
  '¸n' => 'ç½©',
  '¸o' => 'ç½ª',
  '¸p' => 'ç½²',
  '¸q' => 'ç¾©',
  '¸r' => 'ç¾¨',
  '¸s' => 'ç¾¤',
  '¸t' => 'è�–',
  '¸u' => 'è�˜',
  '¸v' => 'è‚†',
  '¸w' => 'è‚„',
  '¸x' => 'è…±',
  '¸y' => 'è…°',
  '¸z' => 'è…¸',
  '¸{' => 'è…¥',
  '¸|' => 'è…®',
  '¸}' => 'è…³',
  '¸~' => 'è…«',
  '¸¡' => 'è…¹',
  '¸¢' => 'è…º',
  '¸£' => 'è…¦',
  '¸¤' => 'èˆ…',
  '¸¥' => 'è‰‡',
  '¸¦' => 'è’‚',
  '¸§' => 'è‘·',
  '¸¨' => 'è�½',
  '¸©' => 'è�±',
  '¸ª' => 'è‘µ',
  '¸«' => 'è‘¦',
  '¸¬' => 'è‘«',
  '¸­' => 'è‘‰',
  '¸®' => 'è‘¬',
  '¸¯' => 'è‘›',
  '¸°' => 'è�¼',
  '¸±' => 'è�µ',
  '¸²' => 'è‘¡',
  '¸³' => 'è‘£',
  '¸´' => 'è‘©',
  '¸µ' => 'è‘­',
  '¸¶' => 'è‘†',
  '¸·' => 'è™ž',
  '¸¸' => 'è™œ',
  '¸¹' => 'è™Ÿ',
  '¸º' => 'è›¹',
  '¸»' => 'èœ“',
  '¸¼' => 'èœˆ',
  '¸½' => 'èœ‡',
  '¸¾' => 'èœ€',
  '¸¿' => 'è›¾',
  '¸À' => 'è›»',
  '¸Á' => 'èœ‚',
  '¸Â' => 'èœƒ',
  '¸Ã' => 'èœ†',
  '¸Ä' => 'èœŠ',
  '¸Å' => 'è¡™',
  '¸Æ' => 'è£Ÿ',
  '¸Ç' => 'è£”',
  '¸È' => 'è£™',
  '¸É' => 'è£œ',
  '¸Ê' => 'è£˜',
  '¸Ë' => 'è£�',
  '¸Ì' => 'è£¡',
  '¸Í' => 'è£Š',
  '¸Î' => 'è£•',
  '¸Ï' => 'è£’',
  '¸Ð' => 'è¦œ',
  '¸Ñ' => 'è§£',
  '¸Ò' => 'è©«',
  '¸Ó' => 'è©²',
  '¸Ô' => 'è©³',
  '¸Õ' => 'è©¦',
  '¸Ö' => 'è©©',
  '¸×' => 'è©°',
  '¸Ø' => 'èª‡',
  '¸Ù' => 'è©¼',
  '¸Ú' => 'è©£',
  '¸Û' => 'èª ',
  '¸Ü' => 'è©±',
  '¸Ý' => 'èª…',
  '¸Þ' => 'è©­',
  '¸ß' => 'è©¢',
  '¸à' => 'è©®',
  '¸á' => 'è©¬',
  '¸â' => 'è©¹',
  '¸ã' => 'è©»',
  '¸ä' => 'è¨¾',
  '¸å' => 'è©¨',
  '¸æ' => 'è±¢',
  '¸ç' => 'è²Š',
  '¸è' => 'è²‰',
  '¸é' => 'è³Š',
  '¸ê' => 'è³‡',
  '¸ë' => 'è³ˆ',
  '¸ì' => 'è³„',
  '¸í' => 'è²²',
  '¸î' => 'è³ƒ',
  '¸ï' => 'è³‚',
  '¸ð' => 'è³…',
  '¸ñ' => 'è·¡',
  '¸ò' => 'è·Ÿ',
  '¸ó' => 'è·¨',
  '¸ô' => 'è·¯',
  '¸õ' => 'è·³',
  '¸ö' => 'è·º',
  '¸÷' => 'è·ª',
  '¸ø' => 'è·¤',
  '¸ù' => 'è·¦',
  '¸ú' => 'èº²',
  '¸û' => 'è¼ƒ',
  '¸ü' => 'è¼‰',
  '¸ý' => 'è»¾',
  '¸þ' => 'è¼Š',
  '¹@' => 'è¾Ÿ',
  '¹A' => 'è¾²',
  '¹B' => 'é�‹',
  '¹C' => 'é�Š',
  '¹D' => 'é�“',
  '¹E' => 'é�‚',
  '¹F' => 'é�”',
  '¹G' => 'é€¼',
  '¹H' => 'é�•',
  '¹I' => 'é��',
  '¹J' => 'é�‡',
  '¹K' => 'é��',
  '¹L' => 'é�Ž',
  '¹M' => 'é��',
  '¹N' => 'é�‘',
  '¹O' => 'é€¾',
  '¹P' => 'é��',
  '¹Q' => 'é„’',
  '¹R' => 'é„—',
  '¹S' => 'é…¬',
  '¹T' => 'é…ª',
  '¹U' => 'é…©',
  '¹V' => 'é‡‰',
  '¹W' => 'éˆ·',
  '¹X' => 'é‰—',
  '¹Y' => 'éˆ¸',
  '¹Z' => 'éˆ½',
  '¹[' => 'é‰€',
  '¹\\' => 'éˆ¾',
  '¹]' => 'é‰›',
  '¹^' => 'é‰‹',
  '¹_' => 'é‰¤',
  '¹`' => 'é‰‘',
  '¹a' => 'éˆ´',
  '¹b' => 'é‰‰',
  '¹c' => 'é‰�',
  '¹d' => 'é‰…',
  '¹e' => 'éˆ¹',
  '¹f' => 'éˆ¿',
  '¹g' => 'é‰š',
  '¹h' => 'é–˜',
  '¹i' => 'éš˜',
  '¹j' => 'éš”',
  '¹k' => 'éš•',
  '¹l' => 'é›�',
  '¹m' => 'é›‹',
  '¹n' => 'é›‰',
  '¹o' => 'é›Š',
  '¹p' => 'é›·',
  '¹q' => 'é›»',
  '¹r' => 'é›¹',
  '¹s' => 'é›¶',
  '¹t' => 'é�–',
  '¹u' => 'é�´',
  '¹v' => 'é�¶',
  '¹w' => 'é �',
  '¹x' => 'é ‘',
  '¹y' => 'é “',
  '¹z' => 'é Š',
  '¹{' => 'é ’',
  '¹|' => 'é Œ',
  '¹}' => 'é£¼',
  '¹~' => 'é£´',
  '¹¡' => 'é£½',
  '¹¢' => 'é£¾',
  '¹£' => 'é¦³',
  '¹¤' => 'é¦±',
  '¹¥' => 'é¦´',
  '¹¦' => 'é«¡',
  '¹§' => 'é³©',
  '¹¨' => 'éº‚',
  '¹©' => 'é¼Ž',
  '¹ª' => 'é¼“',
  '¹«' => 'é¼ ',
  '¹¬' => 'åƒ§',
  '¹­' => 'åƒ®',
  '¹®' => 'åƒ¥',
  '¹¯' => 'åƒ–',
  '¹°' => 'åƒ­',
  '¹±' => 'åƒš',
  '¹²' => 'åƒ•',
  '¹³' => 'åƒ�',
  '¹´' => 'åƒ‘',
  '¹µ' => 'åƒ±',
  '¹¶' => 'åƒŽ',
  '¹·' => 'åƒ©',
  '¹¸' => 'å…¢',
  '¹¹' => 'å‡³',
  '¹º' => 'åŠƒ',
  '¹»' => 'åŠ‚',
  '¹¼' => 'åŒ±',
  '¹½' => 'åŽ­',
  '¹¾' => 'å—¾',
  '¹¿' => 'å˜€',
  '¹À' => 'å˜›',
  '¹Á' => 'å˜—',
  '¹Â' => 'å—½',
  '¹Ã' => 'å˜”',
  '¹Ä' => 'å˜†',
  '¹Å' => 'å˜‰',
  '¹Æ' => 'å˜�',
  '¹Ç' => 'å˜Ž',
  '¹È' => 'å—·',
  '¹É' => 'å˜–',
  '¹Ê' => 'å˜Ÿ',
  '¹Ë' => 'å˜ˆ',
  '¹Ì' => 'å˜�',
  '¹Í' => 'å—¶',
  '¹Î' => 'åœ˜',
  '¹Ï' => 'åœ–',
  '¹Ð' => 'å¡µ',
  '¹Ñ' => 'å¡¾',
  '¹Ò' => 'å¢ƒ',
  '¹Ó' => 'å¢“',
  '¹Ô' => 'å¢Š',
  '¹Õ' => 'å¡¹',
  '¹Ö' => 'å¢…',
  '¹×' => 'å¡½',
  '¹Ø' => 'å£½',
  '¹Ù' => 'å¤¥',
  '¹Ú' => 'å¤¢',
  '¹Û' => 'å¤¤',
  '¹Ü' => 'å¥ª',
  '¹Ý' => 'å¥©',
  '¹Þ' => 'å«¡',
  '¹ß' => 'å«¦',
  '¹à' => 'å«©',
  '¹á' => 'å«—',
  '¹â' => 'å«–',
  '¹ã' => 'å«˜',
  '¹ä' => 'å«£',
  '¹å' => 'å­µ',
  '¹æ' => 'å¯ž',
  '¹ç' => 'å¯§',
  '¹è' => 'å¯¡',
  '¹é' => 'å¯¥',
  '¹ê' => 'å¯¦',
  '¹ë' => 'å¯¨',
  '¹ì' => 'å¯¢',
  '¹í' => 'å¯¤',
  '¹î' => 'å¯Ÿ',
  '¹ï' => 'å°�',
  '¹ð' => 'å±¢',
  '¹ñ' => 'å¶„',
  '¹ò' => 'å¶‡',
  '¹ó' => 'å¹›',
  '¹ô' => 'å¹£',
  '¹õ' => 'å¹•',
  '¹ö' => 'å¹—',
  '¹÷' => 'å¹”',
  '¹ø' => 'å»“',
  '¹ù' => 'å»–',
  '¹ú' => 'å¼Š',
  '¹û' => 'å½†',
  '¹ü' => 'å½°',
  '¹ý' => 'å¾¹',
  '¹þ' => 'æ…‡',
  'º@' => 'æ„¿',
  'ºA' => 'æ…‹',
  'ºB' => 'æ…·',
  'ºC' => 'æ…¢',
  'ºD' => 'æ…£',
  'ºE' => 'æ…Ÿ',
  'ºF' => 'æ…š',
  'ºG' => 'æ…˜',
  'ºH' => 'æ…µ',
  'ºI' => 'æˆª',
  'ºJ' => 'æ’‡',
  'ºK' => 'æ‘˜',
  'ºL' => 'æ‘”',
  'ºM' => 'æ’¤',
  'ºN' => 'æ‘¸',
  'ºO' => 'æ‘Ÿ',
  'ºP' => 'æ‘º',
  'ºQ' => 'æ‘‘',
  'ºR' => 'æ‘§',
  'ºS' => 'æ�´',
  'ºT' => 'æ‘­',
  'ºU' => 'æ‘»',
  'ºV' => 'æ•²',
  'ºW' => 'æ–¡',
  'ºX' => 'æ——',
  'ºY' => 'æ—–',
  'ºZ' => 'æš¢',
  'º[' => 'æš¨',
  'º\\' => 'æš�',
  'º]' => 'æ¦œ',
  'º^' => 'æ¦¨',
  'º_' => 'æ¦•',
  'º`' => 'æ§�',
  'ºa' => 'æ¦®',
  'ºb' => 'æ§“',
  'ºc' => 'æ§‹',
  'ºd' => 'æ¦›',
  'ºe' => 'æ¦·',
  'ºf' => 'æ¦»',
  'ºg' => 'æ¦«',
  'ºh' => 'æ¦´',
  'ºi' => 'æ§�',
  'ºj' => 'æ§�',
  'ºk' => 'æ¦­',
  'ºl' => 'æ§Œ',
  'ºm' => 'æ¦¦',
  'ºn' => 'æ§ƒ',
  'ºo' => 'æ¦£',
  'ºp' => 'æ­‰',
  'ºq' => 'æ­Œ',
  'ºr' => 'æ°³',
  'ºs' => 'æ¼³',
  'ºt' => 'æ¼”',
  'ºu' => 'æ»¾',
  'ºv' => 'æ¼“',
  'ºw' => 'æ»´',
  'ºx' => 'æ¼©',
  'ºy' => 'æ¼¾',
  'ºz' => 'æ¼ ',
  'º{' => 'æ¼¬',
  'º|' => 'æ¼�',
  'º}' => 'æ¼‚',
  'º~' => 'æ¼¢',
  'º¡' => 'æ»¿',
  'º¢' => 'æ»¯',
  'º£' => 'æ¼†',
  'º¤' => 'æ¼±',
  'º¥' => 'æ¼¸',
  'º¦' => 'æ¼²',
  'º§' => 'æ¼£',
  'º¨' => 'æ¼•',
  'º©' => 'æ¼«',
  'ºª' => 'æ¼¯',
  'º«' => 'æ¾ˆ',
  'º¬' => 'æ¼ª',
  'º­' => 'æ»¬',
  'º®' => 'æ¼�',
  'º¯' => 'æ»²',
  'º°' => 'æ»Œ',
  'º±' => 'æ»·',
  'º²' => 'ç†”',
  'º³' => 'ç†™',
  'º´' => 'ç…½',
  'ºµ' => 'ç†Š',
  'º¶' => 'ç†„',
  'º·' => 'ç†’',
  'º¸' => 'çˆ¾',
  'º¹' => 'çŠ’',
  'ºº' => 'çŠ–',
  'º»' => 'ç�„',
  'º¼' => 'ç��',
  'º½' => 'ç‘¤',
  'º¾' => 'ç‘£',
  'º¿' => 'ç‘ª',
  'ºÀ' => 'ç‘°',
  'ºÁ' => 'ç‘­',
  'ºÂ' => 'ç”„',
  'ºÃ' => 'ç–‘',
  'ºÄ' => 'ç˜§',
  'ºÅ' => 'ç˜�',
  'ºÆ' => 'ç˜‹',
  'ºÇ' => 'ç˜‰',
  'ºÈ' => 'ç˜“',
  'ºÉ' => 'ç›¡',
  'ºÊ' => 'ç›£',
  'ºË' => 'çž„',
  'ºÌ' => 'ç�½',
  'ºÍ' => 'ç�¿',
  'ºÎ' => 'ç�¡',
  'ºÏ' => 'ç£�',
  'ºÐ' => 'ç¢Ÿ',
  'ºÑ' => 'ç¢§',
  'ºÒ' => 'ç¢³',
  'ºÓ' => 'ç¢©',
  'ºÔ' => 'ç¢£',
  'ºÕ' => 'ç¦Ž',
  'ºÖ' => 'ç¦�',
  'º×' => 'ç¦�',
  'ºØ' => 'ç¨®',
  'ºÙ' => 'ç¨±',
  'ºÚ' => 'çªª',
  'ºÛ' => 'çª©',
  'ºÜ' => 'ç«­',
  'ºÝ' => 'ç«¯',
  'ºÞ' => 'ç®¡',
  'ºß' => 'ç®•',
  'ºà' => 'ç®‹',
  'ºá' => 'ç­µ',
  'ºâ' => 'ç®—',
  'ºã' => 'ç®�',
  'ºä' => 'ç®”',
  'ºå' => 'ç®�',
  'ºæ' => 'ç®¸',
  'ºç' => 'ç®‡',
  'ºè' => 'ç®„',
  'ºé' => 'ç²¹',
  'ºê' => 'ç²½',
  'ºë' => 'ç²¾',
  'ºì' => 'ç¶»',
  'ºí' => 'ç¶°',
  'ºî' => 'ç¶œ',
  'ºï' => 'ç¶½',
  'ºð' => 'ç¶¾',
  'ºñ' => 'ç¶ ',
  'ºò' => 'ç·Š',
  'ºó' => 'ç¶´',
  'ºô' => 'ç¶²',
  'ºõ' => 'ç¶±',
  'ºö' => 'ç¶º',
  'º÷' => 'ç¶¢',
  'ºø' => 'ç¶¿',
  'ºù' => 'ç¶µ',
  'ºú' => 'ç¶¸',
  'ºû' => 'ç¶­',
  'ºü' => 'ç·’',
  'ºý' => 'ç·‡',
  'ºþ' => 'ç¶¬',
  '»@' => 'ç½°',
  '»A' => 'ç¿ ',
  '»B' => 'ç¿¡',
  '»C' => 'ç¿Ÿ',
  '»D' => 'è�ž',
  '»E' => 'è�š',
  '»F' => 'è‚‡',
  '»G' => 'è…�',
  '»H' => 'è†€',
  '»I' => 'è†�',
  '»J' => 'è†ˆ',
  '»K' => 'è†Š',
  '»L' => 'è…¿',
  '»M' => 'è†‚',
  '»N' => 'è‡§',
  '»O' => 'è‡º',
  '»P' => 'èˆ‡',
  '»Q' => 'èˆ”',
  '»R' => 'èˆž',
  '»S' => 'è‰‹',
  '»T' => 'è“‰',
  '»U' => 'è’¿',
  '»V' => 'è“†',
  '»W' => 'è“„',
  '»X' => 'è’™',
  '»Y' => 'è’ž',
  '»Z' => 'è’²',
  '»[' => 'è’œ',
  '»\\' => 'è“‹',
  '»]' => 'è’¸',
  '»^' => 'è“€',
  '»_' => 'è““',
  '»`' => 'è’�',
  '»a' => 'è’¼',
  '»b' => 'è“‘',
  '»c' => 'è“Š',
  '»d' => 'èœ¿',
  '»e' => 'èœœ',
  '»f' => 'èœ»',
  '»g' => 'èœ¢',
  '»h' => 'èœ¥',
  '»i' => 'èœ´',
  '»j' => 'èœ˜',
  '»k' => 'è�•',
  '»l' => 'èœ·',
  '»m' => 'èœ©',
  '»n' => 'è£³',
  '»o' => 'è¤‚',
  '»p' => 'è£´',
  '»q' => 'è£¹',
  '»r' => 'è£¸',
  '»s' => 'è£½',
  '»t' => 'è£¨',
  '»u' => 'è¤š',
  '»v' => 'è£¯',
  '»w' => 'èª¦',
  '»x' => 'èªŒ',
  '»y' => 'èªž',
  '»z' => 'èª£',
  '»{' => 'èª�',
  '»|' => 'èª¡',
  '»}' => 'èª“',
  '»~' => 'èª¤',
  '»¡' => 'èªª',
  '»¢' => 'èª¥',
  '»£' => 'èª¨',
  '»¤' => 'èª˜',
  '»¥' => 'èª‘',
  '»¦' => 'èªš',
  '»§' => 'èª§',
  '»¨' => 'è±ª',
  '»©' => 'è²�',
  '»ª' => 'è²Œ',
  '»«' => 'è³“',
  '»¬' => 'è³‘',
  '»­' => 'è³’',
  '»®' => 'èµ«',
  '»¯' => 'è¶™',
  '»°' => 'è¶•',
  '»±' => 'è·¼',
  '»²' => 'è¼”',
  '»³' => 'è¼’',
  '»´' => 'è¼•',
  '»µ' => 'è¼“',
  '»¶' => 'è¾£',
  '»·' => 'é� ',
  '»¸' => 'é�˜',
  '»¹' => 'é�œ',
  '»º' => 'é�£',
  '»»' => 'é�™',
  '»¼' => 'é�ž',
  '»½' => 'é�¢',
  '»¾' => 'é��',
  '»¿' => 'é�›',
  '»À' => 'é„™',
  '»Á' => 'é„˜',
  '»Â' => 'é„ž',
  '»Ã' => 'é…µ',
  '»Ä' => 'é…¸',
  '»Å' => 'é…·',
  '»Æ' => 'é…´',
  '»Ç' => 'é‰¸',
  '»È' => 'éŠ€',
  '»É' => 'éŠ…',
  '»Ê' => 'éŠ˜',
  '»Ë' => 'éŠ–',
  '»Ì' => 'é‰»',
  '»Í' => 'éŠ“',
  '»Î' => 'éŠœ',
  '»Ï' => 'éŠ¨',
  '»Ð' => 'é‰¼',
  '»Ñ' => 'éŠ‘',
  '»Ò' => 'é–¡',
  '»Ó' => 'é–¨',
  '»Ô' => 'é–©',
  '»Õ' => 'é–£',
  '»Ö' => 'é–¥',
  '»×' => 'é–¤',
  '»Ø' => 'éš™',
  '»Ù' => 'éšœ',
  '»Ú' => 'éš›',
  '»Û' => 'é›Œ',
  '»Ü' => 'é›’',
  '»Ý' => 'éœ€',
  '»Þ' => 'é�¼',
  '»ß' => 'éž…',
  '»à' => 'éŸ¶',
  '»á' => 'é —',
  '»â' => 'é ˜',
  '»ã' => 'é¢¯',
  '»ä' => 'é¢±',
  '»å' => 'é¤ƒ',
  '»æ' => 'é¤…',
  '»ç' => 'é¤Œ',
  '»è' => 'é¤‰',
  '»é' => 'é§�',
  '»ê' => 'éª¯',
  '»ë' => 'éª°',
  '»ì' => 'é«¦',
  '»í' => 'é­�',
  '»î' => 'é­‚',
  '»ï' => 'é³´',
  '»ð' => 'é³¶',
  '»ñ' => 'é³³',
  '»ò' => 'éº¼',
  '»ó' => 'é¼»',
  '»ô' => 'é½Š',
  '»õ' => 'å„„',
  '»ö' => 'å„€',
  '»÷' => 'åƒ»',
  '»ø' => 'åƒµ',
  '»ù' => 'åƒ¹',
  '»ú' => 'å„‚',
  '»û' => 'å„ˆ',
  '»ü' => 'å„‰',
  '»ý' => 'å„…',
  '»þ' => 'å‡œ',
  '¼@' => 'åŠ‡',
  '¼A' => 'åŠˆ',
  '¼B' => 'åŠ‰',
  '¼C' => 'åŠ�',
  '¼D' => 'åŠŠ',
  '¼E' => 'å‹°',
  '¼F' => 'åŽ²',
  '¼G' => 'å˜®',
  '¼H' => 'å˜»',
  '¼I' => 'å˜¹',
  '¼J' => 'å˜²',
  '¼K' => 'å˜¿',
  '¼L' => 'å˜´',
  '¼M' => 'å˜©',
  '¼N' => 'å™“',
  '¼O' => 'å™Ž',
  '¼P' => 'å™—',
  '¼Q' => 'å™´',
  '¼R' => 'å˜¶',
  '¼S' => 'å˜¯',
  '¼T' => 'å˜°',
  '¼U' => 'å¢€',
  '¼V' => 'å¢Ÿ',
  '¼W' => 'å¢ž',
  '¼X' => 'å¢³',
  '¼Y' => 'å¢œ',
  '¼Z' => 'å¢®',
  '¼[' => 'å¢©',
  '¼\\' => 'å¢¦',
  '¼]' => 'å¥­',
  '¼^' => 'å¬‰',
  '¼_' => 'å«»',
  '¼`' => 'å¬‹',
  '¼a' => 'å«µ',
  '¼b' => 'å¬Œ',
  '¼c' => 'å¬ˆ',
  '¼d' => 'å¯®',
  '¼e' => 'å¯¬',
  '¼f' => 'å¯©',
  '¼g' => 'å¯«',
  '¼h' => 'å±¤',
  '¼i' => 'å±¥',
  '¼j' => 'å¶�',
  '¼k' => 'å¶”',
  '¼l' => 'å¹¢',
  '¼m' => 'å¹Ÿ',
  '¼n' => 'å¹¡',
  '¼o' => 'å»¢',
  '¼p' => 'å»š',
  '¼q' => 'å»Ÿ',
  '¼r' => 'å»�',
  '¼s' => 'å»£',
  '¼t' => 'å» ',
  '¼u' => 'å½ˆ',
  '¼v' => 'å½±',
  '¼w' => 'å¾·',
  '¼x' => 'å¾µ',
  '¼y' => 'æ…¶',
  '¼z' => 'æ…§',
  '¼{' => 'æ…®',
  '¼|' => 'æ…�',
  '¼}' => 'æ…•',
  '¼~' => 'æ†‚',
  '¼¡' => 'æ…¼',
  '¼¢' => 'æ…°',
  '¼£' => 'æ…«',
  '¼¤' => 'æ…¾',
  '¼¥' => 'æ†§',
  '¼¦' => 'æ†�',
  '¼§' => 'æ†«',
  '¼¨' => 'æ†Ž',
  '¼©' => 'æ†¬',
  '¼ª' => 'æ†š',
  '¼«' => 'æ†¤',
  '¼¬' => 'æ†”',
  '¼­' => 'æ†®',
  '¼®' => 'æˆ®',
  '¼¯' => 'æ‘©',
  '¼°' => 'æ‘¯',
  '¼±' => 'æ‘¹',
  '¼²' => 'æ’ž',
  '¼³' => 'æ’²',
  '¼´' => 'æ’ˆ',
  '¼µ' => 'æ’�',
  '¼¶' => 'æ’°',
  '¼·' => 'æ’¥',
  '¼¸' => 'æ’“',
  '¼¹' => 'æ’•',
  '¼º' => 'æ’©',
  '¼»' => 'æ’’',
  '¼¼' => 'æ’®',
  '¼½' => 'æ’­',
  '¼¾' => 'æ’«',
  '¼¿' => 'æ’š',
  '¼À' => 'æ’¬',
  '¼Á' => 'æ’™',
  '¼Â' => 'æ’¢',
  '¼Ã' => 'æ’³',
  '¼Ä' => 'æ•µ',
  '¼Å' => 'æ•·',
  '¼Æ' => 'æ•¸',
  '¼Ç' => 'æš®',
  '¼È' => 'æš«',
  '¼É' => 'æš´',
  '¼Ê' => 'æš±',
  '¼Ë' => 'æ¨£',
  '¼Ì' => 'æ¨Ÿ',
  '¼Í' => 'æ§¨',
  '¼Î' => 'æ¨�',
  '¼Ï' => 'æ¨ž',
  '¼Ð' => 'æ¨™',
  '¼Ñ' => 'æ§½',
  '¼Ò' => 'æ¨¡',
  '¼Ó' => 'æ¨“',
  '¼Ô' => 'æ¨Š',
  '¼Õ' => 'æ§³',
  '¼Ö' => 'æ¨‚',
  '¼×' => 'æ¨…',
  '¼Ø' => 'æ§­',
  '¼Ù' => 'æ¨‘',
  '¼Ú' => 'æ­�',
  '¼Û' => 'æ­Ž',
  '¼Ü' => 'æ®¤',
  '¼Ý' => 'æ¯…',
  '¼Þ' => 'æ¯†',
  '¼ß' => 'æ¼¿',
  '¼à' => 'æ½¼',
  '¼á' => 'æ¾„',
  '¼â' => 'æ½‘',
  '¼ã' => 'æ½¦',
  '¼ä' => 'æ½”',
  '¼å' => 'æ¾†',
  '¼æ' => 'æ½­',
  '¼ç' => 'æ½›',
  '¼è' => 'æ½¸',
  '¼é' => 'æ½®',
  '¼ê' => 'æ¾Ž',
  '¼ë' => 'æ½º',
  '¼ì' => 'æ½°',
  '¼í' => 'æ½¤',
  '¼î' => 'æ¾—',
  '¼ï' => 'æ½˜',
  '¼ð' => 'æ»•',
  '¼ñ' => 'æ½¯',
  '¼ò' => 'æ½ ',
  '¼ó' => 'æ½Ÿ',
  '¼ô' => 'ç†Ÿ',
  '¼õ' => 'ç†¬',
  '¼ö' => 'ç†±',
  '¼÷' => 'ç†¨',
  '¼ø' => 'ç‰–',
  '¼ù' => 'çŠ›',
  '¼ú' => 'ç�Ž',
  '¼û' => 'ç�—',
  '¼ü' => 'ç‘©',
  '¼ý' => 'ç’‹',
  '¼þ' => 'ç’ƒ',
  '½@' => 'ç‘¾',
  '½A' => 'ç’€',
  '½B' => 'ç•¿',
  '½C' => 'ç˜ ',
  '½D' => 'ç˜©',
  '½E' => 'ç˜Ÿ',
  '½F' => 'ç˜¤',
  '½G' => 'ç˜¦',
  '½H' => 'ç˜¡',
  '½I' => 'ç˜¢',
  '½J' => 'çšš',
  '½K' => 'çšº',
  '½L' => 'ç›¤',
  '½M' => 'çžŽ',
  '½N' => 'çž‡',
  '½O' => 'çžŒ',
  '½P' => 'çž‘',
  '½Q' => 'çž‹',
  '½R' => 'ç£‹',
  '½S' => 'ç£…',
  '½T' => 'ç¢º',
  '½U' => 'ç£Š',
  '½V' => 'ç¢¾',
  '½W' => 'ç£•',
  '½X' => 'ç¢¼',
  '½Y' => 'ç£�',
  '½Z' => 'ç¨¿',
  '½[' => 'ç¨¼',
  '½\\' => 'ç©€',
  '½]' => 'ç¨½',
  '½^' => 'ç¨·',
  '½_' => 'ç¨»',
  '½`' => 'çª¯',
  '½a' => 'çª®',
  '½b' => 'ç®­',
  '½c' => 'ç®±',
  '½d' => 'ç¯„',
  '½e' => 'ç®´',
  '½f' => 'ç¯†',
  '½g' => 'ç¯‡',
  '½h' => 'ç¯�',
  '½i' => 'ç® ',
  '½j' => 'ç¯Œ',
  '½k' => 'ç³Š',
  '½l' => 'ç· ',
  '½m' => 'ç·´',
  '½n' => 'ç·¯',
  '½o' => 'ç·»',
  '½p' => 'ç·˜',
  '½q' => 'ç·¬',
  '½r' => 'ç·�',
  '½s' => 'ç·¨',
  '½t' => 'ç·£',
  '½u' => 'ç·š',
  '½v' => 'ç·ž',
  '½w' => 'ç·©',
  '½x' => 'ç¶ž',
  '½y' => 'ç·™',
  '½z' => 'ç·²',
  '½{' => 'ç·¹',
  '½|' => 'ç½µ',
  '½}' => 'ç½·',
  '½~' => 'ç¾¯',
  '½¡' => 'ç¿©',
  '½¢' => 'è€¦',
  '½£' => 'è†›',
  '½¤' => 'è†œ',
  '½¥' => 'è†�',
  '½¦' => 'è† ',
  '½§' => 'è†š',
  '½¨' => 'è†˜',
  '½©' => 'è”—',
  '½ª' => 'è”½',
  '½«' => 'è”š',
  '½¬' => 'è“®',
  '½­' => 'è”¬',
  '½®' => 'è”­',
  '½¯' => 'è”“',
  '½°' => 'è”‘',
  '½±' => 'è”£',
  '½²' => 'è”¡',
  '½³' => 'è””',
  '½´' => 'è“¬',
  '½µ' => 'è”¥',
  '½¶' => 'è“¿',
  '½·' => 'è”†',
  '½¸' => 'èž‚',
  '½¹' => 'è�´',
  '½º' => 'è�¶',
  '½»' => 'è� ',
  '½¼' => 'è�¦',
  '½½' => 'è�¸',
  '½¾' => 'è�¨',
  '½¿' => 'è�™',
  '½À' => 'è�—',
  '½Á' => 'è�Œ',
  '½Â' => 'è�“',
  '½Ã' => 'è¡›',
  '½Ä' => 'è¡�',
  '½Å' => 'è¤�',
  '½Æ' => 'è¤‡',
  '½Ç' => 'è¤’',
  '½È' => 'è¤“',
  '½É' => 'è¤•',
  '½Ê' => 'è¤Š',
  '½Ë' => 'èª¼',
  '½Ì' => 'è«’',
  '½Í' => 'è«‡',
  '½Î' => 'è«„',
  '½Ï' => 'èª•',
  '½Ð' => 'è«‹',
  '½Ñ' => 'è«¸',
  '½Ò' => 'èª²',
  '½Ó' => 'è«‰',
  '½Ô' => 'è«‚',
  '½Õ' => 'èª¿',
  '½Ö' => 'èª°',
  '½×' => 'è«–',
  '½Ø' => 'è«�',
  '½Ù' => 'èª¶',
  '½Ú' => 'èª¹',
  '½Û' => 'è«›',
  '½Ü' => 'è±Œ',
  '½Ý' => 'è±Ž',
  '½Þ' => 'è±¬',
  '½ß' => 'è³ ',
  '½à' => 'è³ž',
  '½á' => 'è³¦',
  '½â' => 'è³¤',
  '½ã' => 'è³¬',
  '½ä' => 'è³­',
  '½å' => 'è³¢',
  '½æ' => 'è³£',
  '½ç' => 'è³œ',
  '½è' => 'è³ª',
  '½é' => 'è³¡',
  '½ê' => 'èµ­',
  '½ë' => 'è¶Ÿ',
  '½ì' => 'è¶£',
  '½í' => 'è¸«',
  '½î' => 'è¸�',
  '½ï' => 'è¸�',
  '½ð' => 'è¸¢',
  '½ñ' => 'è¸�',
  '½ò' => 'è¸©',
  '½ó' => 'è¸Ÿ',
  '½ô' => 'è¸¡',
  '½õ' => 'è¸ž',
  '½ö' => 'èºº',
  '½÷' => 'è¼�',
  '½ø' => 'è¼›',
  '½ù' => 'è¼Ÿ',
  '½ú' => 'è¼©',
  '½û' => 'è¼¦',
  '½ü' => 'è¼ª',
  '½ý' => 'è¼œ',
  '½þ' => 'è¼ž',
  '¾@' => 'è¼¥',
  '¾A' => 'é�©',
  '¾B' => 'é�®',
  '¾C' => 'é�¨',
  '¾D' => 'é�­',
  '¾E' => 'é�·',
  '¾F' => 'é„°',
  '¾G' => 'é„­',
  '¾H' => 'é„§',
  '¾I' => 'é„±',
  '¾J' => 'é†‡',
  '¾K' => 'é†‰',
  '¾L' => 'é†‹',
  '¾M' => 'é†ƒ',
  '¾N' => 'é‹…',
  '¾O' => 'éŠ»',
  '¾P' => 'éŠ·',
  '¾Q' => 'é‹ª',
  '¾R' => 'éŠ¬',
  '¾S' => 'é‹¤',
  '¾T' => 'é‹�',
  '¾U' => 'éŠ³',
  '¾V' => 'éŠ¼',
  '¾W' => 'é‹’',
  '¾X' => 'é‹‡',
  '¾Y' => 'é‹°',
  '¾Z' => 'éŠ²',
  '¾[' => 'é–­',
  '¾\\' => 'é–±',
  '¾]' => 'éœ„',
  '¾^' => 'éœ†',
  '¾_' => 'éœ‡',
  '¾`' => 'éœ‰',
  '¾a' => 'é� ',
  '¾b' => 'éž�',
  '¾c' => 'éž‹',
  '¾d' => 'éž�',
  '¾e' => 'é ¡',
  '¾f' => 'é «',
  '¾g' => 'é œ',
  '¾h' => 'é¢³',
  '¾i' => 'é¤Š',
  '¾j' => 'é¤“',
  '¾k' => 'é¤’',
  '¾l' => 'é¤˜',
  '¾m' => 'é§�',
  '¾n' => 'é§�',
  '¾o' => 'é§Ÿ',
  '¾p' => 'é§›',
  '¾q' => 'é§‘',
  '¾r' => 'é§•',
  '¾s' => 'é§’',
  '¾t' => 'é§™',
  '¾u' => 'éª·',
  '¾v' => 'é«®',
  '¾w' => 'é«¯',
  '¾x' => 'é¬§',
  '¾y' => 'é­…',
  '¾z' => 'é­„',
  '¾{' => 'é­·',
  '¾|' => 'é­¯',
  '¾}' => 'é´†',
  '¾~' => 'é´‰',
  '¾¡' => 'é´ƒ',
  '¾¢' => 'éº©',
  '¾£' => 'éº¾',
  '¾¤' => 'é»Ž',
  '¾¥' => 'å¢¨',
  '¾¦' => 'é½’',
  '¾§' => 'å„’',
  '¾¨' => 'å„˜',
  '¾©' => 'å„”',
  '¾ª' => 'å„�',
  '¾«' => 'å„•',
  '¾¬' => 'å†€',
  '¾­' => 'å†ª',
  '¾®' => 'å‡�',
  '¾¯' => 'åŠ‘',
  '¾°' => 'åŠ“',
  '¾±' => 'å‹³',
  '¾²' => 'å™™',
  '¾³' => 'å™«',
  '¾´' => 'å™¹',
  '¾µ' => 'å™©',
  '¾¶' => 'å™¤',
  '¾·' => 'å™¸',
  '¾¸' => 'å™ª',
  '¾¹' => 'å™¨',
  '¾º' => 'å™¥',
  '¾»' => 'å™±',
  '¾¼' => 'å™¯',
  '¾½' => 'å™¬',
  '¾¾' => 'å™¢',
  '¾¿' => 'å™¶',
  '¾À' => 'å£�',
  '¾Á' => 'å¢¾',
  '¾Â' => 'å£‡',
  '¾Ã' => 'å£…',
  '¾Ä' => 'å¥®',
  '¾Å' => 'å¬�',
  '¾Æ' => 'å¬´',
  '¾Ç' => 'å­¸',
  '¾È' => 'å¯°',
  '¾É' => 'å°Ž',
  '¾Ê' => 'å½Š',
  '¾Ë' => 'æ†²',
  '¾Ì' => 'æ†‘',
  '¾Í' => 'æ†©',
  '¾Î' => 'æ†Š',
  '¾Ï' => 'æ‡�',
  '¾Ð' => 'æ†¶',
  '¾Ñ' => 'æ†¾',
  '¾Ò' => 'æ‡Š',
  '¾Ó' => 'æ‡ˆ',
  '¾Ô' => 'æˆ°',
  '¾Õ' => 'æ“…',
  '¾Ö' => 'æ“�',
  '¾×' => 'æ“‹',
  '¾Ø' => 'æ’»',
  '¾Ù' => 'æ’¼',
  '¾Ú' => 'æ“š',
  '¾Û' => 'æ“„',
  '¾Ü' => 'æ“‡',
  '¾Ý' => 'æ“‚',
  '¾Þ' => 'æ“�',
  '¾ß' => 'æ’¿',
  '¾à' => 'æ“’',
  '¾á' => 'æ“”',
  '¾â' => 'æ’¾',
  '¾ã' => 'æ•´',
  '¾ä' => 'æ›†',
  '¾å' => 'æ›‰',
  '¾æ' => 'æš¹',
  '¾ç' => 'æ›„',
  '¾è' => 'æ›‡',
  '¾é' => 'æš¸',
  '¾ê' => 'æ¨½',
  '¾ë' => 'æ¨¸',
  '¾ì' => 'æ¨º',
  '¾í' => 'æ©™',
  '¾î' => 'æ©«',
  '¾ï' => 'æ©˜',
  '¾ð' => 'æ¨¹',
  '¾ñ' => 'æ©„',
  '¾ò' => 'æ©¢',
  '¾ó' => 'æ©¡',
  '¾ô' => 'æ©‹',
  '¾õ' => 'æ©‡',
  '¾ö' => 'æ¨µ',
  '¾÷' => 'æ©Ÿ',
  '¾ø' => 'æ©ˆ',
  '¾ù' => 'æ­™',
  '¾ú' => 'æ­·',
  '¾û' => 'æ°…',
  '¾ü' => 'æ¿‚',
  '¾ý' => 'æ¾±',
  '¾þ' => 'æ¾¡',
  '¿@' => 'æ¿ƒ',
  '¿A' => 'æ¾¤',
  '¿B' => 'æ¿�',
  '¿C' => 'æ¾§',
  '¿D' => 'æ¾³',
  '¿E' => 'æ¿€',
  '¿F' => 'æ¾¹',
  '¿G' => 'æ¾¶',
  '¿H' => 'æ¾¦',
  '¿I' => 'æ¾ ',
  '¿J' => 'æ¾´',
  '¿K' => 'ç†¾',
  '¿L' => 'ç‡‰',
  '¿M' => 'ç‡�',
  '¿N' => 'ç‡’',
  '¿O' => 'ç‡ˆ',
  '¿P' => 'ç‡•',
  '¿Q' => 'ç†¹',
  '¿R' => 'ç‡Ž',
  '¿S' => 'ç‡™',
  '¿T' => 'ç‡œ',
  '¿U' => 'ç‡ƒ',
  '¿V' => 'ç‡„',
  '¿W' => 'ç�¨',
  '¿X' => 'ç’œ',
  '¿Y' => 'ç’£',
  '¿Z' => 'ç’˜',
  '¿[' => 'ç’Ÿ',
  '¿\\' => 'ç’ž',
  '¿]' => 'ç“¢',
  '¿^' => 'ç”Œ',
  '¿_' => 'ç”�',
  '¿`' => 'ç˜´',
  '¿a' => 'ç˜¸',
  '¿b' => 'ç˜º',
  '¿c' => 'ç›§',
  '¿d' => 'ç›¥',
  '¿e' => 'çž ',
  '¿f' => 'çžž',
  '¿g' => 'çžŸ',
  '¿h' => 'çž¥',
  '¿i' => 'ç£¨',
  '¿j' => 'ç£š',
  '¿k' => 'ç£¬',
  '¿l' => 'ç£§',
  '¿m' => 'ç¦¦',
  '¿n' => 'ç©�',
  '¿o' => 'ç©Ž',
  '¿p' => 'ç©†',
  '¿q' => 'ç©Œ',
  '¿r' => 'ç©‹',
  '¿s' => 'çªº',
  '¿t' => 'ç¯™',
  '¿u' => 'ç°‘',
  '¿v' => 'ç¯‰',
  '¿w' => 'ç¯¤',
  '¿x' => 'ç¯›',
  '¿y' => 'ç¯¡',
  '¿z' => 'ç¯©',
  '¿{' => 'ç¯¦',
  '¿|' => 'ç³•',
  '¿}' => 'ç³–',
  '¿~' => 'ç¸Š',
  '¿¡' => 'ç¸‘',
  '¿¢' => 'ç¸ˆ',
  '¿£' => 'ç¸›',
  '¿¤' => 'ç¸£',
  '¿¥' => 'ç¸ž',
  '¿¦' => 'ç¸�',
  '¿§' => 'ç¸‰',
  '¿¨' => 'ç¸�',
  '¿©' => 'ç½¹',
  '¿ª' => 'ç¾²',
  '¿«' => 'ç¿°',
  '¿¬' => 'ç¿±',
  '¿­' => 'ç¿®',
  '¿®' => 'è€¨',
  '¿¯' => 'è†³',
  '¿°' => 'è†©',
  '¿±' => 'è†¨',
  '¿²' => 'è‡»',
  '¿³' => 'èˆˆ',
  '¿´' => 'è‰˜',
  '¿µ' => 'è‰™',
  '¿¶' => 'è•Š',
  '¿·' => 'è•™',
  '¿¸' => 'è•ˆ',
  '¿¹' => 'è•¨',
  '¿º' => 'è•©',
  '¿»' => 'è•ƒ',
  '¿¼' => 'è•‰',
  '¿½' => 'è•­',
  '¿¾' => 'è•ª',
  '¿¿' => 'è•ž',
  '¿À' => 'èžƒ',
  '¿Á' => 'èžŸ',
  '¿Â' => 'èžž',
  '¿Ã' => 'èž¢',
  '¿Ä' => 'èž�',
  '¿Å' => 'è¡¡',
  '¿Æ' => 'è¤ª',
  '¿Ç' => 'è¤²',
  '¿È' => 'è¤¥',
  '¿É' => 'è¤«',
  '¿Ê' => 'è¤¡',
  '¿Ë' => 'è¦ª',
  '¿Ì' => 'è¦¦',
  '¿Í' => 'è«¦',
  '¿Î' => 'è«º',
  '¿Ï' => 'è««',
  '¿Ð' => 'è«±',
  '¿Ñ' => 'è¬€',
  '¿Ò' => 'è«œ',
  '¿Ó' => 'è«§',
  '¿Ô' => 'è«®',
  '¿Õ' => 'è«¾',
  '¿Ö' => 'è¬�',
  '¿×' => 'è¬‚',
  '¿Ø' => 'è«·',
  '¿Ù' => 'è«­',
  '¿Ú' => 'è«³',
  '¿Û' => 'è«¶',
  '¿Ü' => 'è«¼',
  '¿Ý' => 'è±«',
  '¿Þ' => 'è±­',
  '¿ß' => 'è²“',
  '¿à' => 'è³´',
  '¿á' => 'è¹„',
  '¿â' => 'è¸±',
  '¿ã' => 'è¸´',
  '¿ä' => 'è¹‚',
  '¿å' => 'è¸¹',
  '¿æ' => 'è¸µ',
  '¿ç' => 'è¼»',
  '¿è' => 'è¼¯',
  '¿é' => 'è¼¸',
  '¿ê' => 'è¼³',
  '¿ë' => 'è¾¨',
  '¿ì' => 'è¾¦',
  '¿í' => 'é�µ',
  '¿î' => 'é�´',
  '¿ï' => 'é�¸',
  '¿ð' => 'é�²',
  '¿ñ' => 'é�¼',
  '¿ò' => 'é�º',
  '¿ó' => 'é„´',
  '¿ô' => 'é†’',
  '¿õ' => 'éŒ ',
  '¿ö' => 'éŒ¶',
  '¿÷' => 'é‹¸',
  '¿ø' => 'éŒ³',
  '¿ù' => 'éŒ¯',
  '¿ú' => 'éŒ¢',
  '¿û' => 'é‹¼',
  '¿ü' => 'éŒ«',
  '¿ý' => 'éŒ„',
  '¿þ' => 'éŒš',
  'À@' => 'éŒ�',
  'ÀA' => 'éŒ¦',
  'ÀB' => 'éŒ¡',
  'ÀC' => 'éŒ•',
  'ÀD' => 'éŒ®',
  'ÀE' => 'éŒ™',
  'ÀF' => 'é–»',
  'ÀG' => 'éš§',
  'ÀH' => 'éš¨',
  'ÀI' => 'éšª',
  'ÀJ' => 'é›•',
  'ÀK' => 'éœŽ',
  'ÀL' => 'éœ‘',
  'ÀM' => 'éœ–',
  'ÀN' => 'éœ�',
  'ÀO' => 'éœ“',
  'ÀP' => 'éœ�',
  'ÀQ' => 'é�›',
  'ÀR' => 'é�œ',
  'ÀS' => 'é�¦',
  'ÀT' => 'éž˜',
  'ÀU' => 'é °',
  'ÀV' => 'é ¸',
  'ÀW' => 'é »',
  'ÀX' => 'é ·',
  'ÀY' => 'é ­',
  'ÀZ' => 'é ¹',
  'À[' => 'é ¤',
  'À\\' => 'é¤�',
  'À]' => 'é¤¨',
  'À^' => 'é¤ž',
  'À_' => 'é¤›',
  'À`' => 'é¤¡',
  'Àa' => 'é¤š',
  'Àb' => 'é§­',
  'Àc' => 'é§¢',
  'Àd' => 'é§±',
  'Àe' => 'éª¸',
  'Àf' => 'éª¼',
  'Àg' => 'é«»',
  'Àh' => 'é«­',
  'Ài' => 'é¬¨',
  'Àj' => 'é®‘',
  'Àk' => 'é´•',
  'Àl' => 'é´£',
  'Àm' => 'é´¦',
  'Àn' => 'é´¨',
  'Ào' => 'é´’',
  'Àp' => 'é´›',
  'Àq' => 'é»˜',
  'Àr' => 'é»”',
  'Às' => 'é¾�',
  'Àt' => 'é¾œ',
  'Àu' => 'å„ª',
  'Àv' => 'å„Ÿ',
  'Àw' => 'å„¡',
  'Àx' => 'å„²',
  'Ày' => 'å‹µ',
  'Àz' => 'åšŽ',
  'À{' => 'åš€',
  'À|' => 'åš�',
  'À}' => 'åš…',
  'À~' => 'åš‡',
  'À¡' => 'åš�',
  'À¢' => 'å£•',
  'À£' => 'å£“',
  'À¤' => 'å£‘',
  'À¥' => 'å£Ž',
  'À¦' => 'å¬°',
  'À§' => 'å¬ª',
  'À¨' => 'å¬¤',
  'À©' => 'å­º',
  'Àª' => 'å°·',
  'À«' => 'å±¨',
  'À¬' => 'å¶¼',
  'À­' => 'å¶º',
  'À®' => 'å¶½',
  'À¯' => 'å¶¸',
  'À°' => 'å¹«',
  'À±' => 'å½Œ',
  'À²' => 'å¾½',
  'À³' => 'æ‡‰',
  'À´' => 'æ‡‚',
  'Àµ' => 'æ‡‡',
  'À¶' => 'æ‡¦',
  'À·' => 'æ‡‹',
  'À¸' => 'æˆ²',
  'À¹' => 'æˆ´',
  'Àº' => 'æ“Ž',
  'À»' => 'æ“Š',
  'À¼' => 'æ“˜',
  'À½' => 'æ“ ',
  'À¾' => 'æ“°',
  'À¿' => 'æ“¦',
  'ÀÀ' => 'æ“¬',
  'ÀÁ' => 'æ“±',
  'ÀÂ' => 'æ“¢',
  'ÀÃ' => 'æ“­',
  'ÀÄ' => 'æ–‚',
  'ÀÅ' => 'æ–ƒ',
  'ÀÆ' => 'æ›™',
  'ÀÇ' => 'æ›–',
  'ÀÈ' => 'æª€',
  'ÀÉ' => 'æª”',
  'ÀÊ' => 'æª„',
  'ÀË' => 'æª¢',
  'ÀÌ' => 'æªœ',
  'ÀÍ' => 'æ«›',
  'ÀÎ' => 'æª£',
  'ÀÏ' => 'æ©¾',
  'ÀÐ' => 'æª—',
  'ÀÑ' => 'æª�',
  'ÀÒ' => 'æª ',
  'ÀÓ' => 'æ­œ',
  'ÀÔ' => 'æ®®',
  'ÀÕ' => 'æ¯š',
  'ÀÖ' => 'æ°ˆ',
  'À×' => 'æ¿˜',
  'ÀØ' => 'æ¿±',
  'ÀÙ' => 'æ¿Ÿ',
  'ÀÚ' => 'æ¿ ',
  'ÀÛ' => 'æ¿›',
  'ÀÜ' => 'æ¿¤',
  'ÀÝ' => 'æ¿«',
  'ÀÞ' => 'æ¿¯',
  'Àß' => 'æ¾€',
  'Àà' => 'æ¿¬',
  'Àá' => 'æ¿¡',
  'Àâ' => 'æ¿©',
  'Àã' => 'æ¿•',
  'Àä' => 'æ¿®',
  'Àå' => 'æ¿°',
  'Àæ' => 'ç‡§',
  'Àç' => 'ç‡Ÿ',
  'Àè' => 'ç‡®',
  'Àé' => 'ç‡¦',
  'Àê' => 'ç‡¥',
  'Àë' => 'ç‡­',
  'Àì' => 'ç‡¬',
  'Àí' => 'ç‡´',
  'Àî' => 'ç‡ ',
  'Àï' => 'çˆµ',
  'Àð' => 'ç‰†',
  'Àñ' => 'ç�°',
  'Àò' => 'ç�²',
  'Àó' => 'ç’©',
  'Àô' => 'ç’°',
  'Àõ' => 'ç’¦',
  'Àö' => 'ç’¨',
  'À÷' => 'ç™†',
  'Àø' => 'ç™‚',
  'Àù' => 'ç™Œ',
  'Àú' => 'ç›ª',
  'Àû' => 'çž³',
  'Àü' => 'çžª',
  'Àý' => 'çž°',
  'Àþ' => 'çž¬',
  'Á@' => 'çž§',
  'ÁA' => 'çž­',
  'ÁB' => 'çŸ¯',
  'ÁC' => 'ç£·',
  'ÁD' => 'ç£º',
  'ÁE' => 'ç£´',
  'ÁF' => 'ç£¯',
  'ÁG' => 'ç¤�',
  'ÁH' => 'ç¦§',
  'ÁI' => 'ç¦ª',
  'ÁJ' => 'ç©—',
  'ÁK' => 'çª¿',
  'ÁL' => 'ç°‡',
  'ÁM' => 'ç°�',
  'ÁN' => 'ç¯¾',
  'ÁO' => 'ç¯·',
  'ÁP' => 'ç°Œ',
  'ÁQ' => 'ç¯ ',
  'ÁR' => 'ç³ ',
  'ÁS' => 'ç³œ',
  'ÁT' => 'ç³ž',
  'ÁU' => 'ç³¢',
  'ÁV' => 'ç³Ÿ',
  'ÁW' => 'ç³™',
  'ÁX' => 'ç³�',
  'ÁY' => 'ç¸®',
  'ÁZ' => 'ç¸¾',
  'Á[' => 'ç¹†',
  'Á\\' => 'ç¸·',
  'Á]' => 'ç¸²',
  'Á^' => 'ç¹ƒ',
  'Á_' => 'ç¸«',
  'Á`' => 'ç¸½',
  'Áa' => 'ç¸±',
  'Áb' => 'ç¹…',
  'Ác' => 'ç¹�',
  'Ád' => 'ç¸´',
  'Áe' => 'ç¸¹',
  'Áf' => 'ç¹ˆ',
  'Ág' => 'ç¸µ',
  'Áh' => 'ç¸¿',
  'Ái' => 'ç¸¯',
  'Áj' => 'ç½„',
  'Ák' => 'ç¿³',
  'Ál' => 'ç¿¼',
  'Ám' => 'è�±',
  'Án' => 'è�²',
  'Áo' => 'è�°',
  'Áp' => 'è�¯',
  'Áq' => 'è�³',
  'Ár' => 'è‡†',
  'Ás' => 'è‡ƒ',
  'Át' => 'è†º',
  'Áu' => 'è‡‚',
  'Áv' => 'è‡€',
  'Áw' => 'è†¿',
  'Áx' => 'è†½',
  'Áy' => 'è‡‰',
  'Áz' => 'è†¾',
  'Á{' => 'è‡¨',
  'Á|' => 'èˆ‰',
  'Á}' => 'è‰±',
  'Á~' => 'è–ª',
  'Á¡' => 'è–„',
  'Á¢' => 'è•¾',
  'Á£' => 'è–œ',
  'Á¤' => 'è–‘',
  'Á¥' => 'è–”',
  'Á¦' => 'è–¯',
  'Á§' => 'è–›',
  'Á¨' => 'è–‡',
  'Á©' => 'è–¨',
  'Áª' => 'è–Š',
  'Á«' => 'è™§',
  'Á¬' => 'èŸ€',
  'Á­' => 'èŸ‘',
  'Á®' => 'èž³',
  'Á¯' => 'èŸ’',
  'Á°' => 'èŸ†',
  'Á±' => 'èž«',
  'Á²' => 'èž»',
  'Á³' => 'èžº',
  'Á´' => 'èŸˆ',
  'Áµ' => 'èŸ‹',
  'Á¶' => 'è¤»',
  'Á·' => 'è¤¶',
  'Á¸' => 'è¥„',
  'Á¹' => 'è¤¸',
  'Áº' => 'è¤½',
  'Á»' => 'è¦¬',
  'Á¼' => 'è¬Ž',
  'Á½' => 'è¬—',
  'Á¾' => 'è¬™',
  'Á¿' => 'è¬›',
  'ÁÀ' => 'è¬Š',
  'ÁÁ' => 'è¬ ',
  'ÁÂ' => 'è¬�',
  'ÁÃ' => 'è¬„',
  'ÁÄ' => 'è¬�',
  'ÁÅ' => 'è±�',
  'ÁÆ' => 'è°¿',
  'ÁÇ' => 'è±³',
  'ÁÈ' => 'è³º',
  'ÁÉ' => 'è³½',
  'ÁÊ' => 'è³¼',
  'ÁË' => 'è³¸',
  'ÁÌ' => 'è³»',
  'ÁÍ' => 'è¶¨',
  'ÁÎ' => 'è¹‰',
  'ÁÏ' => 'è¹‹',
  'ÁÐ' => 'è¹ˆ',
  'ÁÑ' => 'è¹Š',
  'ÁÒ' => 'è½„',
  'ÁÓ' => 'è¼¾',
  'ÁÔ' => 'è½‚',
  'ÁÕ' => 'è½…',
  'ÁÖ' => 'è¼¿',
  'Á×' => 'é�¿',
  'ÁØ' => 'é�½',
  'ÁÙ' => 'é‚„',
  'ÁÚ' => 'é‚�',
  'ÁÛ' => 'é‚‚',
  'ÁÜ' => 'é‚€',
  'ÁÝ' => 'é„¹',
  'ÁÞ' => 'é†£',
  'Áß' => 'é†ž',
  'Áà' => 'é†œ',
  'Áá' => 'é��',
  'Áâ' => 'éŽ‚',
  'Áã' => 'éŒ¨',
  'Áä' => 'é�µ',
  'Áå' => 'é�Š',
  'Áæ' => 'é�¥',
  'Áç' => 'é�‹',
  'Áè' => 'éŒ˜',
  'Áé' => 'é�¾',
  'Áê' => 'é�¬',
  'Áë' => 'é�›',
  'Áì' => 'é�°',
  'Áí' => 'é�š',
  'Áî' => 'é�”',
  'Áï' => 'é—Š',
  'Áð' => 'é—‹',
  'Áñ' => 'é—Œ',
  'Áò' => 'é—ˆ',
  'Áó' => 'é—†',
  'Áô' => 'éš±',
  'Áõ' => 'éš¸',
  'Áö' => 'é›–',
  'Á÷' => 'éœœ',
  'Áø' => 'éœž',
  'Áù' => 'éž ',
  'Áú' => 'éŸ“',
  'Áû' => 'é¡†',
  'Áü' => 'é¢¶',
  'Áý' => 'é¤µ',
  'Áþ' => 'é¨�',
  'Â@' => 'é§¿',
  'ÂA' => 'é®®',
  'ÂB' => 'é®«',
  'ÂC' => 'é®ª',
  'ÂD' => 'é®­',
  'ÂE' => 'é´»',
  'ÂF' => 'é´¿',
  'ÂG' => 'éº‹',
  'ÂH' => 'é»�',
  'ÂI' => 'é»ž',
  'ÂJ' => 'é»œ',
  'ÂK' => 'é»�',
  'ÂL' => 'é»›',
  'ÂM' => 'é¼¾',
  'ÂN' => 'é½‹',
  'ÂO' => 'å�¢',
  'ÂP' => 'åš•',
  'ÂQ' => 'åš®',
  'ÂR' => 'å£™',
  'ÂS' => 'å£˜',
  'ÂT' => 'å¬¸',
  'ÂU' => 'å½�',
  'ÂV' => 'æ‡£',
  'ÂW' => 'æˆ³',
  'ÂX' => 'æ“´',
  'ÂY' => 'æ“²',
  'ÂZ' => 'æ“¾',
  'Â[' => 'æ”†',
  'Â\\' => 'æ“º',
  'Â]' => 'æ“»',
  'Â^' => 'æ“·',
  'Â_' => 'æ–·',
  'Â`' => 'æ›œ',
  'Âa' => 'æœ¦',
  'Âb' => 'æª³',
  'Âc' => 'æª¬',
  'Âd' => 'æ«ƒ',
  'Âe' => 'æª»',
  'Âf' => 'æª¸',
  'Âg' => 'æ«‚',
  'Âh' => 'æª®',
  'Âi' => 'æª¯',
  'Âj' => 'æ­Ÿ',
  'Âk' => 'æ­¸',
  'Âl' => 'æ®¯',
  'Âm' => 'ç€‰',
  'Ân' => 'ç€‹',
  'Âo' => 'æ¿¾',
  'Âp' => 'ç€†',
  'Âq' => 'æ¿º',
  'Âr' => 'ç€‘',
  'Âs' => 'ç€�',
  'Ât' => 'ç‡»',
  'Âu' => 'ç‡¼',
  'Âv' => 'ç‡¾',
  'Âw' => 'ç‡¸',
  'Âx' => 'ç�·',
  'Ây' => 'ç�µ',
  'Âz' => 'ç’§',
  'Â{' => 'ç’¿',
  'Â|' => 'ç”•',
  'Â}' => 'ç™–',
  'Â~' => 'ç™˜',
  'Â¡' => 'ç™’',
  'Â¢' => 'çž½',
  'Â£' => 'çž¿',
  'Â¤' => 'çž»',
  'Â¥' => 'çž¼',
  'Â¦' => 'ç¤Ž',
  'Â§' => 'ç¦®',
  'Â¨' => 'ç©¡',
  'Â©' => 'ç©¢',
  'Âª' => 'ç© ',
  'Â«' => 'ç«„',
  'Â¬' => 'ç«…',
  'Â­' => 'ç°«',
  'Â®' => 'ç°§',
  'Â¯' => 'ç°ª',
  'Â°' => 'ç°ž',
  'Â±' => 'ç°£',
  'Â²' => 'ç°¡',
  'Â³' => 'ç³§',
  'Â´' => 'ç¹”',
  'Âµ' => 'ç¹•',
  'Â¶' => 'ç¹ž',
  'Â·' => 'ç¹š',
  'Â¸' => 'ç¹¡',
  'Â¹' => 'ç¹’',
  'Âº' => 'ç¹™',
  'Â»' => 'ç½ˆ',
  'Â¼' => 'ç¿¹',
  'Â½' => 'ç¿»',
  'Â¾' => 'è�·',
  'Â¿' => 'è�¶',
  'ÂÀ' => 'è‡�',
  'ÂÁ' => 'è‡�',
  'ÂÂ' => 'èˆŠ',
  'ÂÃ' => 'è—�',
  'ÂÄ' => 'è–©',
  'ÂÅ' => 'è—�',
  'ÂÆ' => 'è—�',
  'ÂÇ' => 'è—‰',
  'ÂÈ' => 'è–°',
  'ÂÉ' => 'è–º',
  'ÂÊ' => 'è–¹',
  'ÂË' => 'è–¦',
  'ÂÌ' => 'èŸ¯',
  'ÂÍ' => 'èŸ¬',
  'ÂÎ' => 'èŸ²',
  'ÂÏ' => 'èŸ ',
  'ÂÐ' => 'è¦†',
  'ÂÑ' => 'è¦²',
  'ÂÒ' => 'è§´',
  'ÂÓ' => 'è¬¨',
  'ÂÔ' => 'è¬¹',
  'ÂÕ' => 'è¬¬',
  'ÂÖ' => 'è¬«',
  'Â×' => 'è±�',
  'ÂØ' => 'è´…',
  'ÂÙ' => 'è¹™',
  'ÂÚ' => 'è¹£',
  'ÂÛ' => 'è¹¦',
  'ÂÜ' => 'è¹¤',
  'ÂÝ' => 'è¹Ÿ',
  'ÂÞ' => 'è¹•',
  'Âß' => 'è»€',
  'Âà' => 'è½‰',
  'Âá' => 'è½�',
  'Ââ' => 'é‚‡',
  'Âã' => 'é‚ƒ',
  'Âä' => 'é‚ˆ',
  'Âå' => 'é†«',
  'Âæ' => 'é†¬',
  'Âç' => 'é‡�',
  'Âè' => 'éŽ”',
  'Âé' => 'éŽŠ',
  'Âê' => 'éŽ–',
  'Âë' => 'éŽ¢',
  'Âì' => 'éŽ³',
  'Âí' => 'éŽ®',
  'Âî' => 'éŽ¬',
  'Âï' => 'éŽ°',
  'Âð' => 'éŽ˜',
  'Âñ' => 'éŽš',
  'Âò' => 'éŽ—',
  'Âó' => 'é—”',
  'Âô' => 'é—–',
  'Âõ' => 'é—�',
  'Âö' => 'é—•',
  'Â÷' => 'é›¢',
  'Âø' => 'é›œ',
  'Âù' => 'é›™',
  'Âú' => 'é››',
  'Âû' => 'é›ž',
  'Âü' => 'éœ¤',
  'Âý' => 'éž£',
  'Âþ' => 'éž¦',
  'Ã@' => 'éž­',
  'ÃA' => 'éŸ¹',
  'ÃB' => 'é¡�',
  'ÃC' => 'é¡�',
  'ÃD' => 'é¡Œ',
  'ÃE' => 'é¡Ž',
  'ÃF' => 'é¡“',
  'ÃG' => 'é¢º',
  'ÃH' => 'é¤¾',
  'ÃI' => 'é¤¿',
  'ÃJ' => 'é¤½',
  'ÃK' => 'é¤®',
  'ÃL' => 'é¦¥',
  'ÃM' => 'é¨Ž',
  'ÃN' => 'é«�',
  'ÃO' => 'é¬ƒ',
  'ÃP' => 'é¬†',
  'ÃQ' => 'é­�',
  'ÃR' => 'é­Ž',
  'ÃS' => 'é­�',
  'ÃT' => 'é¯Š',
  'ÃU' => 'é¯‰',
  'ÃV' => 'é¯½',
  'ÃW' => 'é¯ˆ',
  'ÃX' => 'é¯€',
  'ÃY' => 'éµ‘',
  'ÃZ' => 'éµ�',
  'Ã[' => 'éµ ',
  'Ã\\' => 'é» ',
  'Ã]' => 'é¼•',
  'Ã^' => 'é¼¬',
  'Ã_' => 'å„³',
  'Ã`' => 'åš¥',
  'Ãa' => 'å£ž',
  'Ãb' => 'å£Ÿ',
  'Ãc' => 'å£¢',
  'Ãd' => 'å¯µ',
  'Ãe' => 'é¾�',
  'Ãf' => 'å»¬',
  'Ãg' => 'æ‡²',
  'Ãh' => 'æ‡·',
  'Ãi' => 'æ‡¶',
  'Ãj' => 'æ‡µ',
  'Ãk' => 'æ”€',
  'Ãl' => 'æ”�',
  'Ãm' => 'æ› ',
  'Ãn' => 'æ›�',
  'Ão' => 'æ«¥',
  'Ãp' => 'æ«�',
  'Ãq' => 'æ«š',
  'Ãr' => 'æ«“',
  'Ãs' => 'ç€›',
  'Ãt' => 'ç€Ÿ',
  'Ãu' => 'ç€¨',
  'Ãv' => 'ç€š',
  'Ãw' => 'ç€�',
  'Ãx' => 'ç€•',
  'Ãy' => 'ç€˜',
  'Ãz' => 'çˆ†',
  'Ã{' => 'çˆ�',
  'Ã|' => 'ç‰˜',
  'Ã}' => 'çŠ¢',
  'Ã~' => 'ç�¸',
  'Ã¡' => 'ç�º',
  'Ã¢' => 'ç’½',
  'Ã£' => 'ç“Š',
  'Ã¤' => 'ç“£',
  'Ã¥' => 'ç–‡',
  'Ã¦' => 'ç–†',
  'Ã§' => 'ç™Ÿ',
  'Ã¨' => 'ç™¡',
  'Ã©' => 'çŸ‡',
  'Ãª' => 'ç¤™',
  'Ã«' => 'ç¦±',
  'Ã¬' => 'ç©«',
  'Ã­' => 'ç©©',
  'Ã®' => 'ç°¾',
  'Ã¯' => 'ç°¿',
  'Ã°' => 'ç°¸',
  'Ã±' => 'ç°½',
  'Ã²' => 'ç°·',
  'Ã³' => 'ç±€',
  'Ã´' => 'ç¹«',
  'Ãµ' => 'ç¹­',
  'Ã¶' => 'ç¹¹',
  'Ã·' => 'ç¹©',
  'Ã¸' => 'ç¹ª',
  'Ã¹' => 'ç¾…',
  'Ãº' => 'ç¹³',
  'Ã»' => 'ç¾¶',
  'Ã¼' => 'ç¾¹',
  'Ã½' => 'ç¾¸',
  'Ã¾' => 'è‡˜',
  'Ã¿' => 'è—©',
  'ÃÀ' => 'è—�',
  'ÃÁ' => 'è—ª',
  'ÃÂ' => 'è—•',
  'ÃÃ' => 'è—¤',
  'ÃÄ' => 'è—¥',
  'ÃÅ' => 'è—·',
  'ÃÆ' => 'èŸ»',
  'ÃÇ' => 'è …',
  'ÃÈ' => 'è �',
  'ÃÉ' => 'èŸ¹',
  'ÃÊ' => 'èŸ¾',
  'ÃË' => 'è¥ ',
  'ÃÌ' => 'è¥Ÿ',
  'ÃÍ' => 'è¥–',
  'ÃÎ' => 'è¥ž',
  'ÃÏ' => 'è­�',
  'ÃÐ' => 'è­œ',
  'ÃÑ' => 'è­˜',
  'ÃÒ' => 'è­‰',
  'ÃÓ' => 'è­š',
  'ÃÔ' => 'è­Ž',
  'ÃÕ' => 'è­�',
  'ÃÖ' => 'è­†',
  'Ã×' => 'è­™',
  'ÃØ' => 'è´ˆ',
  'ÃÙ' => 'è´Š',
  'ÃÚ' => 'è¹¼',
  'ÃÛ' => 'è¹²',
  'ÃÜ' => 'èº‡',
  'ÃÝ' => 'è¹¶',
  'ÃÞ' => 'è¹¬',
  'Ãß' => 'è¹º',
  'Ãà' => 'è¹´',
  'Ãá' => 'è½”',
  'Ãâ' => 'è½Ž',
  'Ãã' => 'è¾­',
  'Ãä' => 'é‚Š',
  'Ãå' => 'é‚‹',
  'Ãæ' => 'é†±',
  'Ãç' => 'é†®',
  'Ãè' => 'é�¡',
  'Ãé' => 'é�‘',
  'Ãê' => 'é�Ÿ',
  'Ãë' => 'é�ƒ',
  'Ãì' => 'é�ˆ',
  'Ãí' => 'é�œ',
  'Ãî' => 'é��',
  'Ãï' => 'é�–',
  'Ãð' => 'é�¢',
  'Ãñ' => 'é��',
  'Ãò' => 'é�˜',
  'Ãó' => 'é�¤',
  'Ãô' => 'é�—',
  'Ãõ' => 'é�¨',
  'Ãö' => 'é—œ',
  'Ã÷' => 'éš´',
  'Ãø' => 'é›£',
  'Ãù' => 'éœª',
  'Ãú' => 'éœ§',
  'Ãû' => 'é�¡',
  'Ãü' => 'éŸœ',
  'Ãý' => 'éŸ»',
  'Ãþ' => 'é¡ž',
  'Ä@' => 'é¡˜',
  'ÄA' => 'é¡›',
  'ÄB' => 'é¢¼',
  'ÄC' => 'é¥…',
  'ÄD' => 'é¥‰',
  'ÄE' => 'é¨–',
  'ÄF' => 'é¨™',
  'ÄG' => 'é¬�',
  'ÄH' => 'é¯¨',
  'ÄI' => 'é¯§',
  'ÄJ' => 'é¯–',
  'ÄK' => 'é¯›',
  'ÄL' => 'é¶‰',
  'ÄM' => 'éµ¡',
  'ÄN' => 'éµ²',
  'ÄO' => 'éµª',
  'ÄP' => 'éµ¬',
  'ÄQ' => 'éº’',
  'ÄR' => 'éº—',
  'ÄS' => 'éº“',
  'ÄT' => 'éº´',
  'ÄU' => 'å‹¸',
  'ÄV' => 'åš¨',
  'ÄW' => 'åš·',
  'ÄX' => 'åš¶',
  'ÄY' => 'åš´',
  'ÄZ' => 'åš¼',
  'Ä[' => 'å£¤',
  'Ä\\' => 'å­€',
  'Ä]' => 'å­ƒ',
  'Ä^' => 'å­½',
  'Ä_' => 'å¯¶',
  'Ä`' => 'å·‰',
  'Äa' => 'æ‡¸',
  'Äb' => 'æ‡º',
  'Äc' => 'æ”˜',
  'Äd' => 'æ””',
  'Äe' => 'æ”™',
  'Äf' => 'æ›¦',
  'Äg' => 'æœ§',
  'Äh' => 'æ«¬',
  'Äi' => 'ç€¾',
  'Äj' => 'ç€°',
  'Äk' => 'ç€²',
  'Äl' => 'çˆ�',
  'Äm' => 'ç�»',
  'Än' => 'ç“�',
  'Äo' => 'ç™¢',
  'Äp' => 'ç™¥',
  'Äq' => 'ç¤¦',
  'Är' => 'ç¤ª',
  'Äs' => 'ç¤¬',
  'Ät' => 'ç¤«',
  'Äu' => 'ç«‡',
  'Äv' => 'ç«¶',
  'Äw' => 'ç±Œ',
  'Äx' => 'ç±ƒ',
  'Äy' => 'ç±�',
  'Äz' => 'ç³¯',
  'Ä{' => 'ç³°',
  'Ä|' => 'è¾®',
  'Ä}' => 'ç¹½',
  'Ä~' => 'ç¹¼',
  'Ä¡' => 'çº‚',
  'Ä¢' => 'ç½Œ',
  'Ä£' => 'è€€',
  'Ä¤' => 'è‡š',
  'Ä¥' => 'è‰¦',
  'Ä¦' => 'è—»',
  'Ä§' => 'è—¹',
  'Ä¨' => 'è˜‘',
  'Ä©' => 'è—º',
  'Äª' => 'è˜†',
  'Ä«' => 'è˜‹',
  'Ä¬' => 'è˜‡',
  'Ä­' => 'è˜Š',
  'Ä®' => 'è ”',
  'Ä¯' => 'è •',
  'Ä°' => 'è¥¤',
  'Ä±' => 'è¦º',
  'Ä²' => 'è§¸',
  'Ä³' => 'è­°',
  'Ä´' => 'è­¬',
  'Äµ' => 'è­¦',
  'Ä¶' => 'è­¯',
  'Ä·' => 'è­Ÿ',
  'Ä¸' => 'è­«',
  'Ä¹' => 'è´�',
  'Äº' => 'è´�',
  'Ä»' => 'èº‰',
  'Ä¼' => 'èº�',
  'Ä½' => 'èº…',
  'Ä¾' => 'èº‚',
  'Ä¿' => 'é†´',
  'ÄÀ' => 'é‡‹',
  'ÄÁ' => 'é�˜',
  'ÄÂ' => 'é�ƒ',
  'ÄÃ' => 'é�½',
  'ÄÄ' => 'é—¡',
  'ÄÅ' => 'éœ°',
  'ÄÆ' => 'é£„',
  'ÄÇ' => 'é¥’',
  'ÄÈ' => 'é¥‘',
  'ÄÉ' => 'é¦¨',
  'ÄÊ' => 'é¨«',
  'ÄË' => 'é¨°',
  'ÄÌ' => 'é¨·',
  'ÄÍ' => 'é¨µ',
  'ÄÎ' => 'é°“',
  'ÄÏ' => 'é°�',
  'ÄÐ' => 'é¹¹',
  'ÄÑ' => 'éºµ',
  'ÄÒ' => 'é»¨',
  'ÄÓ' => 'é¼¯',
  'ÄÔ' => 'é½Ÿ',
  'ÄÕ' => 'é½£',
  'ÄÖ' => 'é½¡',
  'Ä×' => 'å„·',
  'ÄØ' => 'å„¸',
  'ÄÙ' => 'å›�',
  'ÄÚ' => 'å›€',
  'ÄÛ' => 'å›‚',
  'ÄÜ' => 'å¤”',
  'ÄÝ' => 'å±¬',
  'ÄÞ' => 'å·�',
  'Äß' => 'æ‡¼',
  'Äà' => 'æ‡¾',
  'Äá' => 'æ”�',
  'Äâ' => 'æ”œ',
  'Äã' => 'æ–•',
  'Ää' => 'æ›©',
  'Äå' => 'æ«»',
  'Äæ' => 'æ¬„',
  'Äç' => 'æ«º',
  'Äè' => 'æ®²',
  'Äé' => 'ç�Œ',
  'Äê' => 'çˆ›',
  'Äë' => 'çŠ§',
  'Äì' => 'ç“–',
  'Äí' => 'ç“”',
  'Äî' => 'ç™©',
  'Äï' => 'çŸ“',
  'Äð' => 'ç±�',
  'Äñ' => 'çº�',
  'Äò' => 'çºŒ',
  'Äó' => 'ç¾¼',
  'Äô' => 'è˜—',
  'Äõ' => 'è˜­',
  'Äö' => 'è˜š',
  'Ä÷' => 'è £',
  'Äø' => 'è ¢',
  'Äù' => 'è ¡',
  'Äú' => 'è Ÿ',
  'Äû' => 'è¥ª',
  'Äü' => 'è¥¬',
  'Äý' => 'è¦½',
  'Äþ' => 'è­´',
  'Å@' => 'è­·',
  'ÅA' => 'è­½',
  'ÅB' => 'è´“',
  'ÅC' => 'èºŠ',
  'ÅD' => 'èº�',
  'ÅE' => 'èº‹',
  'ÅF' => 'è½Ÿ',
  'ÅG' => 'è¾¯',
  'ÅH' => 'é†º',
  'ÅI' => 'é�®',
  'ÅJ' => 'é�³',
  'ÅK' => 'é�µ',
  'ÅL' => 'é�º',
  'ÅM' => 'é�¸',
  'ÅN' => 'é�²',
  'ÅO' => 'é�«',
  'ÅP' => 'é—¢',
  'ÅQ' => 'éœ¸',
  'ÅR' => 'éœ¹',
  'ÅS' => 'éœ²',
  'ÅT' => 'éŸ¿',
  'ÅU' => 'é¡§',
  'ÅV' => 'é¡¥',
  'ÅW' => 'é¥—',
  'ÅX' => 'é©…',
  'ÅY' => 'é©ƒ',
  'ÅZ' => 'é©€',
  'Å[' => 'é¨¾',
  'Å\\' => 'é«�',
  'Å]' => 'é­”',
  'Å^' => 'é­‘',
  'Å_' => 'é°­',
  'Å`' => 'é°¥',
  'Åa' => 'é¶¯',
  'Åb' => 'é¶´',
  'Åc' => 'é·‚',
  'Åd' => 'é¶¸',
  'Åe' => 'éº�',
  'Åf' => 'é»¯',
  'Åg' => 'é¼™',
  'Åh' => 'é½œ',
  'Åi' => 'é½¦',
  'Åj' => 'é½§',
  'Åk' => 'å„¼',
  'Ål' => 'å„»',
  'Åm' => 'å›ˆ',
  'Ån' => 'å›Š',
  'Åo' => 'å›‰',
  'Åp' => 'å­¿',
  'Åq' => 'å·”',
  'År' => 'å·’',
  'Ås' => 'å½Ž',
  'Åt' => 'æ‡¿',
  'Åu' => 'æ”¤',
  'Åv' => 'æ¬Š',
  'Åw' => 'æ­¡',
  'Åx' => 'ç�‘',
  'Åy' => 'ç�˜',
  'Åz' => 'çŽ€',
  'Å{' => 'ç“¤',
  'Å|' => 'ç–Š',
  'Å}' => 'ç™®',
  'Å~' => 'ç™¬',
  'Å¡' => 'ç¦³',
  'Å¢' => 'ç± ',
  'Å£' => 'ç±Ÿ',
  'Å¤' => 'è�¾',
  'Å¥' => 'è�½',
  'Å¦' => 'è‡Ÿ',
  'Å§' => 'è¥²',
  'Å¨' => 'è¥¯',
  'Å©' => 'è§¼',
  'Åª' => 'è®€',
  'Å«' => 'è´–',
  'Å¬' => 'è´—',
  'Å­' => 'èº‘',
  'Å®' => 'èº“',
  'Å¯' => 'è½¡',
  'Å°' => 'é…ˆ',
  'Å±' => 'é‘„',
  'Å²' => 'é‘‘',
  'Å³' => 'é‘’',
  'Å´' => 'éœ½',
  'Åµ' => 'éœ¾',
  'Å¶' => 'éŸƒ',
  'Å·' => 'éŸ�',
  'Å¸' => 'é¡«',
  'Å¹' => 'é¥•',
  'Åº' => 'é©•',
  'Å»' => 'é©�',
  'Å¼' => 'é«’',
  'Å½' => 'é¬š',
  'Å¾' => 'é±‰',
  'Å¿' => 'é°±',
  'ÅÀ' => 'é°¾',
  'ÅÁ' => 'é°»',
  'ÅÂ' => 'é·“',
  'ÅÃ' => 'é·—',
  'ÅÄ' => 'é¼´',
  'ÅÅ' => 'é½¬',
  'ÅÆ' => 'é½ª',
  'ÅÇ' => 'é¾”',
  'ÅÈ' => 'å›Œ',
  'ÅÉ' => 'å·–',
  'ÅÊ' => 'æˆ€',
  'ÅË' => 'æ”£',
  'ÅÌ' => 'æ”«',
  'ÅÍ' => 'æ”ª',
  'ÅÎ' => 'æ›¬',
  'ÅÏ' => 'æ¬�',
  'ÅÐ' => 'ç“š',
  'ÅÑ' => 'ç«Š',
  'ÅÒ' => 'ç±¤',
  'ÅÓ' => 'ç±£',
  'ÅÔ' => 'ç±¥',
  'ÅÕ' => 'çº“',
  'ÅÖ' => 'çº–',
  'Å×' => 'çº”',
  'ÅØ' => 'è‡¢',
  'ÅÙ' => 'è˜¸',
  'ÅÚ' => 'è˜¿',
  'ÅÛ' => 'è ±',
  'ÅÜ' => 'è®Š',
  'ÅÝ' => 'é‚�',
  'ÅÞ' => 'é‚�',
  'Åß' => 'é‘£',
  'Åà' => 'é‘ ',
  'Åá' => 'é‘¤',
  'Åâ' => 'é�¨',
  'Åã' => 'é¡¯',
  'Åä' => 'é¥œ',
  'Åå' => 'é©š',
  'Åæ' => 'é©›',
  'Åç' => 'é©—',
  'Åè' => 'é«“',
  'Åé' => 'é«”',
  'Åê' => 'é«‘',
  'Åë' => 'é±”',
  'Åì' => 'é±—',
  'Åí' => 'é±–',
  'Åî' => 'é·¥',
  'Åï' => 'éºŸ',
  'Åð' => 'é»´',
  'Åñ' => 'å›‘',
  'Åò' => 'å£©',
  'Åó' => 'æ”¬',
  'Åô' => 'ç�ž',
  'Åõ' => 'ç™±',
  'Åö' => 'ç™²',
  'Å÷' => 'çŸ—',
  'Åø' => 'ç½�',
  'Åù' => 'ç¾ˆ',
  'Åú' => 'è ¶',
  'Åû' => 'è ¹',
  'Åü' => 'è¡¢',
  'Åý' => 'è®“',
  'Åþ' => 'è®’',
  'Æ@' => 'è®–',
  'ÆA' => 'è‰·',
  'ÆB' => 'è´›',
  'ÆC' => 'é‡€',
  'ÆD' => 'é‘ª',
  'ÆE' => 'é�‚',
  'ÆF' => 'é�ˆ',
  'ÆG' => 'é�„',
  'ÆH' => 'éŸ†',
  'ÆI' => 'é¡°',
  'ÆJ' => 'é©Ÿ',
  'ÆK' => 'é¬¢',
  'ÆL' => 'é­˜',
  'ÆM' => 'é±Ÿ',
  'ÆN' => 'é·¹',
  'ÆO' => 'é·º',
  'ÆP' => 'é¹¼',
  'ÆQ' => 'é¹½',
  'ÆR' => 'é¼‡',
  'ÆS' => 'é½·',
  'ÆT' => 'é½²',
  'ÆU' => 'å»³',
  'ÆV' => 'æ¬–',
  'ÆW' => 'ç�£',
  'ÆX' => 'ç±¬',
  'ÆY' => 'ç±®',
  'ÆZ' => 'è »',
  'Æ[' => 'è§€',
  'Æ\\' => 'èº¡',
  'Æ]' => 'é‡�',
  'Æ^' => 'é‘²',
  'Æ_' => 'é‘°',
  'Æ`' => 'é¡±',
  'Æa' => 'é¥ž',
  'Æb' => 'é«–',
  'Æc' => 'é¬£',
  'Æd' => 'é»Œ',
  'Æe' => 'ç�¤',
  'Æf' => 'çŸš',
  'Æg' => 'è®š',
  'Æh' => 'é‘·',
  'Æi' => 'éŸ‰',
  'Æj' => 'é©¢',
  'Æk' => 'é©¥',
  'Æl' => 'çºœ',
  'Æm' => 'è®œ',
  'Æn' => 'èºª',
  'Æo' => 'é‡…',
  'Æp' => 'é‘½',
  'Æq' => 'é‘¾',
  'Ær' => 'é‘¼',
  'Æs' => 'é±·',
  'Æt' => 'é±¸',
  'Æu' => 'é»·',
  'Æv' => 'è±”',
  'Æw' => 'é‘¿',
  'Æx' => 'é¸š',
  'Æy' => 'çˆ¨',
  'Æz' => 'é©ª',
  'Æ{' => 'é¬±',
  'Æ|' => 'é¸›',
  'Æ}' => 'é¸ž',
  'Æ~' => 'ç±²',
  'Æ¡' => 'ãƒ¾',
  'Æ¢' => 'ã‚�',
  'Æ£' => 'ã‚ž',
  'Æ¤' => 'ã€…',
  'Æ¥' => 'ã��',
  'Æ¦' => 'ã�‚',
  'Æ§' => 'ã�ƒ',
  'Æ¨' => 'ã�„',
  'Æ©' => 'ã�…',
  'Æª' => 'ã�†',
  'Æ«' => 'ã�‡',
  'Æ¬' => 'ã�ˆ',
  'Æ­' => 'ã�‰',
  'Æ®' => 'ã�Š',
  'Æ¯' => 'ã�‹',
  'Æ°' => 'ã�Œ',
  'Æ±' => 'ã��',
  'Æ²' => 'ã�Ž',
  'Æ³' => 'ã��',
  'Æ´' => 'ã��',
  'Æµ' => 'ã�‘',
  'Æ¶' => 'ã�’',
  'Æ·' => 'ã�“',
  'Æ¸' => 'ã�”',
  'Æ¹' => 'ã�•',
  'Æº' => 'ã�–',
  'Æ»' => 'ã�—',
  'Æ¼' => 'ã�˜',
  'Æ½' => 'ã�™',
  'Æ¾' => 'ã�š',
  'Æ¿' => 'ã�›',
  'ÆÀ' => 'ã�œ',
  'ÆÁ' => 'ã��',
  'ÆÂ' => 'ã�ž',
  'ÆÃ' => 'ã�Ÿ',
  'ÆÄ' => 'ã� ',
  'ÆÅ' => 'ã�¡',
  'ÆÆ' => 'ã�¢',
  'ÆÇ' => 'ã�£',
  'ÆÈ' => 'ã�¤',
  'ÆÉ' => 'ã�¥',
  'ÆÊ' => 'ã�¦',
  'ÆË' => 'ã�§',
  'ÆÌ' => 'ã�¨',
  'ÆÍ' => 'ã�©',
  'ÆÎ' => 'ã�ª',
  'ÆÏ' => 'ã�«',
  'ÆÐ' => 'ã�¬',
  'ÆÑ' => 'ã�­',
  'ÆÒ' => 'ã�®',
  'ÆÓ' => 'ã�¯',
  'ÆÔ' => 'ã�°',
  'ÆÕ' => 'ã�±',
  'ÆÖ' => 'ã�²',
  'Æ×' => 'ã�³',
  'ÆØ' => 'ã�´',
  'ÆÙ' => 'ã�µ',
  'ÆÚ' => 'ã�¶',
  'ÆÛ' => 'ã�·',
  'ÆÜ' => 'ã�¸',
  'ÆÝ' => 'ã�¹',
  'ÆÞ' => 'ã�º',
  'Æß' => 'ã�»',
  'Æà' => 'ã�¼',
  'Æá' => 'ã�½',
  'Æâ' => 'ã�¾',
  'Æã' => 'ã�¿',
  'Æä' => 'ã‚€',
  'Æå' => 'ã‚�',
  'Ææ' => 'ã‚‚',
  'Æç' => 'ã‚ƒ',
  'Æè' => 'ã‚„',
  'Æé' => 'ã‚…',
  'Æê' => 'ã‚†',
  'Æë' => 'ã‚‡',
  'Æì' => 'ã‚ˆ',
  'Æí' => 'ã‚‰',
  'Æî' => 'ã‚Š',
  'Æï' => 'ã‚‹',
  'Æð' => 'ã‚Œ',
  'Æñ' => 'ã‚�',
  'Æò' => 'ã‚Ž',
  'Æó' => 'ã‚�',
  'Æô' => 'ã‚�',
  'Æõ' => 'ã‚‘',
  'Æö' => 'ã‚’',
  'Æ÷' => 'ã‚“',
  'Æø' => 'ã‚¡',
  'Æù' => 'ã‚¢',
  'Æú' => 'ã‚£',
  'Æû' => 'ã‚¤',
  'Æü' => 'ã‚¥',
  'Æý' => 'ã‚¦',
  'Æþ' => 'ã‚§',
  'Ç@' => 'ã‚¨',
  'ÇA' => 'ã‚©',
  'ÇB' => 'ã‚ª',
  'ÇC' => 'ã‚«',
  'ÇD' => 'ã‚¬',
  'ÇE' => 'ã‚­',
  'ÇF' => 'ã‚®',
  'ÇG' => 'ã‚¯',
  'ÇH' => 'ã‚°',
  'ÇI' => 'ã‚±',
  'ÇJ' => 'ã‚²',
  'ÇK' => 'ã‚³',
  'ÇL' => 'ã‚´',
  'ÇM' => 'ã‚µ',
  'ÇN' => 'ã‚¶',
  'ÇO' => 'ã‚·',
  'ÇP' => 'ã‚¸',
  'ÇQ' => 'ã‚¹',
  'ÇR' => 'ã‚º',
  'ÇS' => 'ã‚»',
  'ÇT' => 'ã‚¼',
  'ÇU' => 'ã‚½',
  'ÇV' => 'ã‚¾',
  'ÇW' => 'ã‚¿',
  'ÇX' => 'ãƒ€',
  'ÇY' => 'ãƒ�',
  'ÇZ' => 'ãƒ‚',
  'Ç[' => 'ãƒƒ',
  'Ç\\' => 'ãƒ„',
  'Ç]' => 'ãƒ…',
  'Ç^' => 'ãƒ†',
  'Ç_' => 'ãƒ‡',
  'Ç`' => 'ãƒˆ',
  'Ça' => 'ãƒ‰',
  'Çb' => 'ãƒŠ',
  'Çc' => 'ãƒ‹',
  'Çd' => 'ãƒŒ',
  'Çe' => 'ãƒ�',
  'Çf' => 'ãƒŽ',
  'Çg' => 'ãƒ�',
  'Çh' => 'ãƒ�',
  'Çi' => 'ãƒ‘',
  'Çj' => 'ãƒ’',
  'Çk' => 'ãƒ“',
  'Çl' => 'ãƒ”',
  'Çm' => 'ãƒ•',
  'Çn' => 'ãƒ–',
  'Ço' => 'ãƒ—',
  'Çp' => 'ãƒ˜',
  'Çq' => 'ãƒ™',
  'Çr' => 'ãƒš',
  'Çs' => 'ãƒ›',
  'Çt' => 'ãƒœ',
  'Çu' => 'ãƒ�',
  'Çv' => 'ãƒž',
  'Çw' => 'ãƒŸ',
  'Çx' => 'ãƒ ',
  'Çy' => 'ãƒ¡',
  'Çz' => 'ãƒ¢',
  'Ç{' => 'ãƒ£',
  'Ç|' => 'ãƒ¤',
  'Ç}' => 'ãƒ¥',
  'Ç~' => 'ãƒ¦',
  'Ç¡' => 'ãƒ§',
  'Ç¢' => 'ãƒ¨',
  'Ç£' => 'ãƒ©',
  'Ç¤' => 'ãƒª',
  'Ç¥' => 'ãƒ«',
  'Ç¦' => 'ãƒ¬',
  'Ç§' => 'ãƒ­',
  'Ç¨' => 'ãƒ®',
  'Ç©' => 'ãƒ¯',
  'Çª' => 'ãƒ°',
  'Ç«' => 'ãƒ±',
  'Ç¬' => 'ãƒ²',
  'Ç­' => 'ãƒ³',
  'Ç®' => 'ãƒ´',
  'Ç¯' => 'ãƒµ',
  'Ç°' => 'ãƒ¶',
  'Ç±' => 'Ð”',
  'Ç²' => 'Ð•',
  'Ç³' => 'Ð�',
  'Ç´' => 'Ð–',
  'Çµ' => 'Ð—',
  'Ç¶' => 'Ð˜',
  'Ç·' => 'Ð™',
  'Ç¸' => 'Ðš',
  'Ç¹' => 'Ð›',
  'Çº' => 'Ðœ',
  'Ç»' => 'Ð£',
  'Ç¼' => 'Ð¤',
  'Ç½' => 'Ð¥',
  'Ç¾' => 'Ð¦',
  'Ç¿' => 'Ð§',
  'ÇÀ' => 'Ð¨',
  'ÇÁ' => 'Ð©',
  'ÇÂ' => 'Ðª',
  'ÇÃ' => 'Ð«',
  'ÇÄ' => 'Ð¬',
  'ÇÅ' => 'Ð­',
  'ÇÆ' => 'Ð®',
  'ÇÇ' => 'Ð¯',
  'ÇÈ' => 'Ð°',
  'ÇÉ' => 'Ð±',
  'ÇÊ' => 'Ð²',
  'ÇË' => 'Ð³',
  'ÇÌ' => 'Ð´',
  'ÇÍ' => 'Ðµ',
  'ÇÎ' => 'Ñ‘',
  'ÇÏ' => 'Ð¶',
  'ÇÐ' => 'Ð·',
  'ÇÑ' => 'Ð¸',
  'ÇÒ' => 'Ð¹',
  'ÇÓ' => 'Ðº',
  'ÇÔ' => 'Ð»',
  'ÇÕ' => 'Ð¼',
  'ÇÖ' => 'Ð½',
  'Ç×' => 'Ð¾',
  'ÇØ' => 'Ð¿',
  'ÇÙ' => 'Ñ€',
  'ÇÚ' => 'Ñ�',
  'ÇÛ' => 'Ñ‚',
  'ÇÜ' => 'Ñƒ',
  'ÇÝ' => 'Ñ„',
  'ÇÞ' => 'Ñ…',
  'Çß' => 'Ñ†',
  'Çà' => 'Ñ‡',
  'Çá' => 'Ñˆ',
  'Çâ' => 'Ñ‰',
  'Çã' => 'ÑŠ',
  'Çä' => 'Ñ‹',
  'Çå' => 'ÑŒ',
  'Çæ' => 'Ñ�',
  'Çç' => 'ÑŽ',
  'Çè' => 'Ñ�',
  'Çé' => 'â‘ ',
  'Çê' => 'â‘¡',
  'Çë' => 'â‘¢',
  'Çì' => 'â‘£',
  'Çí' => 'â‘¤',
  'Çî' => 'â‘¥',
  'Çï' => 'â‘¦',
  'Çð' => 'â‘§',
  'Çñ' => 'â‘¨',
  'Çò' => 'â‘©',
  'Çó' => 'â‘´',
  'Çô' => 'â‘µ',
  'Çõ' => 'â‘¶',
  'Çö' => 'â‘·',
  'Ç÷' => 'â‘¸',
  'Çø' => 'â‘¹',
  'Çù' => 'â‘º',
  'Çú' => 'â‘»',
  'Çû' => 'â‘¼',
  'Çü' => 'â‘½',
  'É@' => 'ä¹‚',
  'ÉA' => 'ä¹œ',
  'ÉB' => 'å‡µ',
  'ÉC' => 'åŒš',
  'ÉD' => 'åŽ‚',
  'ÉE' => 'ä¸‡',
  'ÉF' => 'ä¸Œ',
  'ÉG' => 'ä¹‡',
  'ÉH' => 'äº�',
  'ÉI' => 'å›—',
  'ÉJ' => 'ï¨Œ',
  'ÉK' => 'å±®',
  'ÉL' => 'å½³',
  'ÉM' => 'ä¸�',
  'ÉN' => 'å†‡',
  'ÉO' => 'ä¸Ž',
  'ÉP' => 'ä¸®',
  'ÉQ' => 'äº“',
  'ÉR' => 'ä»‚',
  'ÉS' => 'ä»‰',
  'ÉT' => 'ä»ˆ',
  'ÉU' => 'å†˜',
  'ÉV' => 'å‹¼',
  'ÉW' => 'å�¬',
  'ÉX' => 'åŽ¹',
  'ÉY' => 'åœ ',
  'ÉZ' => 'å¤ƒ',
  'É[' => 'å¤¬',
  'É\\' => 'å°�',
  'É]' => 'å·¿',
  'É^' => 'æ—¡',
  'É_' => 'æ®³',
  'É`' => 'æ¯Œ',
  'Éa' => 'æ°”',
  'Éb' => 'çˆ¿',
  'Éc' => 'ä¸±',
  'Éd' => 'ä¸¼',
  'Ée' => 'ä»¨',
  'Éf' => 'ä»œ',
  'Ég' => 'ä»©',
  'Éh' => 'ä»¡',
  'Éi' => 'ä»�',
  'Éj' => 'ä»š',
  'Ék' => 'åˆŒ',
  'Él' => 'åŒœ',
  'Ém' => 'å�Œ',
  'Én' => 'åœ¢',
  'Éo' => 'åœ£',
  'Ép' => 'å¤—',
  'Éq' => 'å¤¯',
  'Ér' => 'å®�',
  'És' => 'å®„',
  'Ét' => 'å°’',
  'Éu' => 'å°»',
  'Év' => 'å±´',
  'Éw' => 'å±³',
  'Éx' => 'å¸„',
  'Éy' => 'åº€',
  'Éz' => 'åº‚',
  'É{' => 'å¿‰',
  'É|' => 'æˆ‰',
  'É}' => 'æ‰�',
  'É~' => 'æ°•',
  'É¡' => 'æ°¶',
  'É¢' => 'æ±ƒ',
  'É£' => 'æ°¿',
  'É¤' => 'æ°»',
  'É¥' => 'çŠ®',
  'É¦' => 'çŠ°',
  'É§' => 'çŽŠ',
  'É¨' => 'ç¦¸',
  'É©' => 'è‚Š',
  'Éª' => 'é˜ž',
  'É«' => 'ä¼Ž',
  'É¬' => 'ä¼˜',
  'É­' => 'ä¼¬',
  'É®' => 'ä»µ',
  'É¯' => 'ä¼”',
  'É°' => 'ä»±',
  'É±' => 'ä¼€',
  'É²' => 'ä»·',
  'É³' => 'ä¼ˆ',
  'É´' => 'ä¼�',
  'Éµ' => 'ä¼‚',
  'É¶' => 'ä¼…',
  'É·' => 'ä¼¢',
  'É¸' => 'ä¼“',
  'É¹' => 'ä¼„',
  'Éº' => 'ä»´',
  'É»' => 'ä¼’',
  'É¼' => 'å†±',
  'É½' => 'åˆ“',
  'É¾' => 'åˆ‰',
  'É¿' => 'åˆ�',
  'ÉÀ' => 'åŠ¦',
  'ÉÁ' => 'åŒ¢',
  'ÉÂ' => 'åŒŸ',
  'ÉÃ' => 'å��',
  'ÉÄ' => 'åŽŠ',
  'ÉÅ' => 'å�‡',
  'ÉÆ' => 'å›¡',
  'ÉÇ' => 'å›Ÿ',
  'ÉÈ' => 'åœ®',
  'ÉÉ' => 'åœª',
  'ÉÊ' => 'åœ´',
  'ÉË' => 'å¤¼',
  'ÉÌ' => 'å¦€',
  'ÉÍ' => 'å¥¼',
  'ÉÎ' => 'å¦…',
  'ÉÏ' => 'å¥»',
  'ÉÐ' => 'å¥¾',
  'ÉÑ' => 'å¥·',
  'ÉÒ' => 'å¥¿',
  'ÉÓ' => 'å­–',
  'ÉÔ' => 'å°•',
  'ÉÕ' => 'å°¥',
  'ÉÖ' => 'å±¼',
  'É×' => 'å±º',
  'ÉØ' => 'å±»',
  'ÉÙ' => 'å±¾',
  'ÉÚ' => 'å·Ÿ',
  'ÉÛ' => 'å¹µ',
  'ÉÜ' => 'åº„',
  'ÉÝ' => 'å¼‚',
  'ÉÞ' => 'å¼š',
  'Éß' => 'å½´',
  'Éà' => 'å¿•',
  'Éá' => 'å¿”',
  'Éâ' => 'å¿�',
  'Éã' => 'æ‰œ',
  'Éä' => 'æ‰ž',
  'Éå' => 'æ‰¤',
  'Éæ' => 'æ‰¡',
  'Éç' => 'æ‰¦',
  'Éè' => 'æ‰¢',
  'Éé' => 'æ‰™',
  'Éê' => 'æ‰ ',
  'Éë' => 'æ‰š',
  'Éì' => 'æ‰¥',
  'Éí' => 'æ—¯',
  'Éî' => 'æ—®',
  'Éï' => 'æœ¾',
  'Éð' => 'æœ¹',
  'Éñ' => 'æœ¸',
  'Éò' => 'æœ»',
  'Éó' => 'æœº',
  'Éô' => 'æœ¿',
  'Éõ' => 'æœ¼',
  'Éö' => 'æœ³',
  'É÷' => 'æ°˜',
  'Éø' => 'æ±†',
  'Éù' => 'æ±’',
  'Éú' => 'æ±œ',
  'Éû' => 'æ±�',
  'Éü' => 'æ±Š',
  'Éý' => 'æ±”',
  'Éþ' => 'æ±‹',
  'Ê@' => 'æ±Œ',
  'ÊA' => 'ç�±',
  'ÊB' => 'ç‰ž',
  'ÊC' => 'çŠ´',
  'ÊD' => 'çŠµ',
  'ÊE' => 'çŽŽ',
  'ÊF' => 'ç”ª',
  'ÊG' => 'ç™¿',
  'ÊH' => 'ç©µ',
  'ÊI' => 'ç½‘',
  'ÊJ' => 'è‰¸',
  'ÊK' => 'è‰¼',
  'ÊL' => 'èŠ€',
  'ÊM' => 'è‰½',
  'ÊN' => 'è‰¿',
  'ÊO' => 'è™�',
  'ÊP' => 'è¥¾',
  'ÊQ' => 'é‚™',
  'ÊR' => 'é‚—',
  'ÊS' => 'é‚˜',
  'ÊT' => 'é‚›',
  'ÊU' => 'é‚”',
  'ÊV' => 'é˜¢',
  'ÊW' => 'é˜¤',
  'ÊX' => 'é˜ ',
  'ÊY' => 'é˜£',
  'ÊZ' => 'ä½–',
  'Ê[' => 'ä¼»',
  'Ê\\' => 'ä½¢',
  'Ê]' => 'ä½‰',
  'Ê^' => 'ä½“',
  'Ê_' => 'ä½¤',
  'Ê`' => 'ä¼¾',
  'Êa' => 'ä½§',
  'Êb' => 'ä½’',
  'Êc' => 'ä½Ÿ',
  'Êd' => 'ä½�',
  'Êe' => 'ä½˜',
  'Êf' => 'ä¼­',
  'Êg' => 'ä¼³',
  'Êh' => 'ä¼¿',
  'Êi' => 'ä½¡',
  'Êj' => 'å†�',
  'Êk' => 'å†¹',
  'Êl' => 'åˆœ',
  'Êm' => 'åˆž',
  'Ên' => 'åˆ¡',
  'Êo' => 'åŠ­',
  'Êp' => 'åŠ®',
  'Êq' => 'åŒ‰',
  'Êr' => 'å�£',
  'Ês' => 'å�²',
  'Êt' => 'åŽŽ',
  'Êu' => 'åŽ�',
  'Êv' => 'å�°',
  'Êw' => 'å�·',
  'Êx' => 'å�ª',
  'Êy' => 'å‘”',
  'Êz' => 'å‘…',
  'Ê{' => 'å�™',
  'Ê|' => 'å�œ',
  'Ê}' => 'å�¥',
  'Ê~' => 'å�˜',
  'Ê¡' => 'å�½',
  'Ê¢' => 'å‘�',
  'Ê£' => 'å‘�',
  'Ê¤' => 'å�¨',
  'Ê¥' => 'å�¤',
  'Ê¦' => 'å‘‡',
  'Ê§' => 'å›®',
  'Ê¨' => 'å›§',
  'Ê©' => 'å›¥',
  'Êª' => 'å��',
  'Ê«' => 'å�…',
  'Ê¬' => 'å�Œ',
  'Ê­' => 'å�‰',
  'Ê®' => 'å�‹',
  'Ê¯' => 'å�’',
  'Ê°' => 'å¤†',
  'Ê±' => 'å¥€',
  'Ê²' => 'å¦¦',
  'Ê³' => 'å¦˜',
  'Ê´' => 'å¦ ',
  'Êµ' => 'å¦—',
  'Ê¶' => 'å¦Ž',
  'Ê·' => 'å¦¢',
  'Ê¸' => 'å¦�',
  'Ê¹' => 'å¦�',
  'Êº' => 'å¦§',
  'Ê»' => 'å¦¡',
  'Ê¼' => 'å®Ž',
  'Ê½' => 'å®’',
  'Ê¾' => 'å°¨',
  'Ê¿' => 'å°ª',
  'ÊÀ' => 'å²�',
  'ÊÁ' => 'å²�',
  'ÊÂ' => 'å²ˆ',
  'ÊÃ' => 'å²‹',
  'ÊÄ' => 'å²‰',
  'ÊÅ' => 'å²’',
  'ÊÆ' => 'å²Š',
  'ÊÇ' => 'å²†',
  'ÊÈ' => 'å²“',
  'ÊÉ' => 'å²•',
  'ÊÊ' => 'å· ',
  'ÊË' => 'å¸Š',
  'ÊÌ' => 'å¸Ž',
  'ÊÍ' => 'åº‹',
  'ÊÎ' => 'åº‰',
  'ÊÏ' => 'åºŒ',
  'ÊÐ' => 'åºˆ',
  'ÊÑ' => 'åº�',
  'ÊÒ' => 'å¼…',
  'ÊÓ' => 'å¼�',
  'ÊÔ' => 'å½¸',
  'ÊÕ' => 'å½¶',
  'ÊÖ' => 'å¿’',
  'Ê×' => 'å¿‘',
  'ÊØ' => 'å¿�',
  'ÊÙ' => 'å¿­',
  'ÊÚ' => 'å¿¨',
  'ÊÛ' => 'å¿®',
  'ÊÜ' => 'å¿³',
  'ÊÝ' => 'å¿¡',
  'ÊÞ' => 'å¿¤',
  'Êß' => 'å¿£',
  'Êà' => 'å¿º',
  'Êá' => 'å¿¯',
  'Êâ' => 'å¿·',
  'Êã' => 'å¿»',
  'Êä' => 'æ€€',
  'Êå' => 'å¿´',
  'Êæ' => 'æˆº',
  'Êç' => 'æŠƒ',
  'Êè' => 'æŠŒ',
  'Êé' => 'æŠŽ',
  'Êê' => 'æŠ�',
  'Êë' => 'æŠ”',
  'Êì' => 'æŠ‡',
  'Êí' => 'æ‰±',
  'Êî' => 'æ‰»',
  'Êï' => 'æ‰º',
  'Êð' => 'æ‰°',
  'Êñ' => 'æŠ�',
  'Êò' => 'æŠˆ',
  'Êó' => 'æ‰·',
  'Êô' => 'æ‰½',
  'Êõ' => 'æ‰²',
  'Êö' => 'æ‰´',
  'Ê÷' => 'æ”·',
  'Êø' => 'æ—°',
  'Êù' => 'æ—´',
  'Êú' => 'æ—³',
  'Êû' => 'æ—²',
  'Êü' => 'æ—µ',
  'Êý' => 'æ�…',
  'Êþ' => 'æ�‡',
  'Ë@' => 'æ�™',
  'ËA' => 'æ�•',
  'ËB' => 'æ�Œ',
  'ËC' => 'æ�ˆ',
  'ËD' => 'æ��',
  'ËE' => 'æ��',
  'ËF' => 'æ�š',
  'ËG' => 'æ�‹',
  'ËH' => 'æ¯�',
  'ËI' => 'æ°™',
  'ËJ' => 'æ°š',
  'ËK' => 'æ±¸',
  'ËL' => 'æ±§',
  'ËM' => 'æ±«',
  'ËN' => 'æ²„',
  'ËO' => 'æ²‹',
  'ËP' => 'æ²�',
  'ËQ' => 'æ±±',
  'ËR' => 'æ±¯',
  'ËS' => 'æ±©',
  'ËT' => 'æ²š',
  'ËU' => 'æ±­',
  'ËV' => 'æ²‡',
  'ËW' => 'æ²•',
  'ËX' => 'æ²œ',
  'ËY' => 'æ±¦',
  'ËZ' => 'æ±³',
  'Ë[' => 'æ±¥',
  'Ë\\' => 'æ±»',
  'Ë]' => 'æ²Ž',
  'Ë^' => 'ç�´',
  'Ë_' => 'ç�º',
  'Ë`' => 'ç‰£',
  'Ëa' => 'çŠ¿',
  'Ëb' => 'çŠ½',
  'Ëc' => 'ç‹ƒ',
  'Ëd' => 'ç‹†',
  'Ëe' => 'ç‹�',
  'Ëf' => 'çŠº',
  'Ëg' => 'ç‹…',
  'Ëh' => 'çŽ•',
  'Ëi' => 'çŽ—',
  'Ëj' => 'çŽ“',
  'Ëk' => 'çŽ”',
  'Ël' => 'çŽ’',
  'Ëm' => 'ç”º',
  'Ën' => 'ç”¹',
  'Ëo' => 'ç–”',
  'Ëp' => 'ç–•',
  'Ëq' => 'çš�',
  'Ër' => 'ç¤½',
  'Ës' => 'è€´',
  'Ët' => 'è‚•',
  'Ëu' => 'è‚™',
  'Ëv' => 'è‚�',
  'Ëw' => 'è‚’',
  'Ëx' => 'è‚œ',
  'Ëy' => 'èŠ�',
  'Ëz' => 'èŠ�',
  'Ë{' => 'èŠ…',
  'Ë|' => 'èŠŽ',
  'Ë}' => 'èŠ‘',
  'Ë~' => 'èŠ“',
  'Ë¡' => 'èŠŠ',
  'Ë¢' => 'èŠƒ',
  'Ë£' => 'èŠ„',
  'Ë¤' => 'è±¸',
  'Ë¥' => 'è¿‰',
  'Ë¦' => 'è¾¿',
  'Ë§' => 'é‚Ÿ',
  'Ë¨' => 'é‚¡',
  'Ë©' => 'é‚¥',
  'Ëª' => 'é‚ž',
  'Ë«' => 'é‚§',
  'Ë¬' => 'é‚ ',
  'Ë­' => 'é˜°',
  'Ë®' => 'é˜¨',
  'Ë¯' => 'é˜¯',
  'Ë°' => 'é˜­',
  'Ë±' => 'ä¸³',
  'Ë²' => 'ä¾˜',
  'Ë³' => 'ä½¼',
  'Ë´' => 'ä¾…',
  'Ëµ' => 'ä½½',
  'Ë¶' => 'ä¾€',
  'Ë·' => 'ä¾‡',
  'Ë¸' => 'ä½¶',
  'Ë¹' => 'ä½´',
  'Ëº' => 'ä¾‰',
  'Ë»' => 'ä¾„',
  'Ë¼' => 'ä½·',
  'Ë½' => 'ä½Œ',
  'Ë¾' => 'ä¾—',
  'Ë¿' => 'ä½ª',
  'ËÀ' => 'ä¾š',
  'ËÁ' => 'ä½¹',
  'ËÂ' => 'ä¾�',
  'ËÃ' => 'ä½¸',
  'ËÄ' => 'ä¾�',
  'ËÅ' => 'ä¾œ',
  'ËÆ' => 'ä¾”',
  'ËÇ' => 'ä¾ž',
  'ËÈ' => 'ä¾’',
  'ËÉ' => 'ä¾‚',
  'ËÊ' => 'ä¾•',
  'ËË' => 'ä½«',
  'ËÌ' => 'ä½®',
  'ËÍ' => 'å†ž',
  'ËÎ' => 'å†¼',
  'ËÏ' => 'å†¾',
  'ËÐ' => 'åˆµ',
  'ËÑ' => 'åˆ²',
  'ËÒ' => 'åˆ³',
  'ËÓ' => 'å‰†',
  'ËÔ' => 'åˆ±',
  'ËÕ' => 'åŠ¼',
  'ËÖ' => 'åŒŠ',
  'Ë×' => 'åŒ‹',
  'ËØ' => 'åŒ¼',
  'ËÙ' => 'åŽ’',
  'ËÚ' => 'åŽ”',
  'ËÛ' => 'å’‡',
  'ËÜ' => 'å‘¿',
  'ËÝ' => 'å’�',
  'ËÞ' => 'å’‘',
  'Ëß' => 'å’‚',
  'Ëà' => 'å’ˆ',
  'Ëá' => 'å‘«',
  'Ëâ' => 'å‘º',
  'Ëã' => 'å‘¾',
  'Ëä' => 'å‘¥',
  'Ëå' => 'å‘¬',
  'Ëæ' => 'å‘´',
  'Ëç' => 'å‘¦',
  'Ëè' => 'å’�',
  'Ëé' => 'å‘¯',
  'Ëê' => 'å‘¡',
  'Ëë' => 'å‘ ',
  'Ëì' => 'å’˜',
  'Ëí' => 'å‘£',
  'Ëî' => 'å‘§',
  'Ëï' => 'å‘¤',
  'Ëð' => 'å›·',
  'Ëñ' => 'å›¹',
  'Ëò' => 'å�¯',
  'Ëó' => 'å�²',
  'Ëô' => 'å�­',
  'Ëõ' => 'å�«',
  'Ëö' => 'å�±',
  'Ë÷' => 'å�°',
  'Ëø' => 'å�¶',
  'Ëù' => 'åž€',
  'Ëú' => 'å�µ',
  'Ëû' => 'å�»',
  'Ëü' => 'å�³',
  'Ëý' => 'å�´',
  'Ëþ' => 'å�¢',
  'Ì@' => 'å�¨',
  'ÌA' => 'å�½',
  'ÌB' => 'å¤Œ',
  'ÌC' => 'å¥…',
  'ÌD' => 'å¦µ',
  'ÌE' => 'å¦º',
  'ÌF' => 'å§�',
  'ÌG' => 'å§Ž',
  'ÌH' => 'å¦²',
  'ÌI' => 'å§Œ',
  'ÌJ' => 'å§�',
  'ÌK' => 'å¦¶',
  'ÌL' => 'å¦¼',
  'ÌM' => 'å§ƒ',
  'ÌN' => 'å§–',
  'ÌO' => 'å¦±',
  'ÌP' => 'å¦½',
  'ÌQ' => 'å§€',
  'ÌR' => 'å§ˆ',
  'ÌS' => 'å¦´',
  'ÌT' => 'å§‡',
  'ÌU' => 'å­¢',
  'ÌV' => 'å­¥',
  'ÌW' => 'å®“',
  'ÌX' => 'å®•',
  'ÌY' => 'å±„',
  'ÌZ' => 'å±‡',
  'Ì[' => 'å²®',
  'Ì\\' => 'å²¤',
  'Ì]' => 'å² ',
  'Ì^' => 'å²µ',
  'Ì_' => 'å²¯',
  'Ì`' => 'å²¨',
  'Ìa' => 'å²¬',
  'Ìb' => 'å²Ÿ',
  'Ìc' => 'å²£',
  'Ìd' => 'å²­',
  'Ìe' => 'å²¢',
  'Ìf' => 'å²ª',
  'Ìg' => 'å²§',
  'Ìh' => 'å²�',
  'Ìi' => 'å²¥',
  'Ìj' => 'å²¶',
  'Ìk' => 'å²°',
  'Ìl' => 'å²¦',
  'Ìm' => 'å¸—',
  'Ìn' => 'å¸”',
  'Ìo' => 'å¸™',
  'Ìp' => 'å¼¨',
  'Ìq' => 'å¼¢',
  'Ìr' => 'å¼£',
  'Ìs' => 'å¼¤',
  'Ìt' => 'å½”',
  'Ìu' => 'å¾‚',
  'Ìv' => 'å½¾',
  'Ìw' => 'å½½',
  'Ìx' => 'å¿ž',
  'Ìy' => 'å¿¥',
  'Ìz' => 'æ€­',
  'Ì{' => 'æ€¦',
  'Ì|' => 'æ€™',
  'Ì}' => 'æ€²',
  'Ì~' => 'æ€‹',
  'Ì¡' => 'æ€´',
  'Ì¢' => 'æ€Š',
  'Ì£' => 'æ€—',
  'Ì¤' => 'æ€³',
  'Ì¥' => 'æ€š',
  'Ì¦' => 'æ€ž',
  'Ì§' => 'æ€¬',
  'Ì¨' => 'æ€¢',
  'Ì©' => 'æ€�',
  'Ìª' => 'æ€�',
  'Ì«' => 'æ€®',
  'Ì¬' => 'æ€“',
  'Ì­' => 'æ€‘',
  'Ì®' => 'æ€Œ',
  'Ì¯' => 'æ€‰',
  'Ì°' => 'æ€œ',
  'Ì±' => 'æˆ”',
  'Ì²' => 'æˆ½',
  'Ì³' => 'æŠ­',
  'Ì´' => 'æŠ´',
  'Ìµ' => 'æ‹‘',
  'Ì¶' => 'æŠ¾',
  'Ì·' => 'æŠª',
  'Ì¸' => 'æŠ¶',
  'Ì¹' => 'æ‹Š',
  'Ìº' => 'æŠ®',
  'Ì»' => 'æŠ³',
  'Ì¼' => 'æŠ¯',
  'Ì½' => 'æŠ»',
  'Ì¾' => 'æŠ©',
  'Ì¿' => 'æŠ°',
  'ÌÀ' => 'æŠ¸',
  'ÌÁ' => 'æ”½',
  'ÌÂ' => 'æ–¨',
  'ÌÃ' => 'æ–»',
  'ÌÄ' => 'æ˜‰',
  'ÌÅ' => 'æ—¼',
  'ÌÆ' => 'æ˜„',
  'ÌÇ' => 'æ˜’',
  'ÌÈ' => 'æ˜ˆ',
  'ÌÉ' => 'æ—»',
  'ÌÊ' => 'æ˜ƒ',
  'ÌË' => 'æ˜‹',
  'ÌÌ' => 'æ˜�',
  'ÌÍ' => 'æ˜…',
  'ÌÎ' => 'æ—½',
  'ÌÏ' => 'æ˜‘',
  'ÌÐ' => 'æ˜�',
  'ÌÑ' => 'æ›¶',
  'ÌÒ' => 'æœŠ',
  'ÌÓ' => 'æž…',
  'ÌÔ' => 'æ�¬',
  'ÌÕ' => 'æžŽ',
  'ÌÖ' => 'æž’',
  'Ì×' => 'æ�¶',
  'ÌØ' => 'æ�»',
  'ÌÙ' => 'æž˜',
  'ÌÚ' => 'æž†',
  'ÌÛ' => 'æž„',
  'ÌÜ' => 'æ�´',
  'ÌÝ' => 'æž�',
  'ÌÞ' => 'æžŒ',
  'Ìß' => 'æ�º',
  'Ìà' => 'æžŸ',
  'Ìá' => 'æž‘',
  'Ìâ' => 'æž™',
  'Ìã' => 'æžƒ',
  'Ìä' => 'æ�½',
  'Ìå' => 'æž�',
  'Ìæ' => 'æ�¸',
  'Ìç' => 'æ�¹',
  'Ìè' => 'æž”',
  'Ìé' => 'æ¬¥',
  'Ìê' => 'æ®€',
  'Ìë' => 'æ­¾',
  'Ìì' => 'æ¯ž',
  'Ìí' => 'æ°�',
  'Ìî' => 'æ²“',
  'Ìï' => 'æ³¬',
  'Ìð' => 'æ³«',
  'Ìñ' => 'æ³®',
  'Ìò' => 'æ³™',
  'Ìó' => 'æ²¶',
  'Ìô' => 'æ³”',
  'Ìõ' => 'æ²­',
  'Ìö' => 'æ³§',
  'Ì÷' => 'æ²·',
  'Ìø' => 'æ³�',
  'Ìù' => 'æ³‚',
  'Ìú' => 'æ²º',
  'Ìû' => 'æ³ƒ',
  'Ìü' => 'æ³†',
  'Ìý' => 'æ³­',
  'Ìþ' => 'æ³²',
  'Í@' => 'æ³’',
  'ÍA' => 'æ³�',
  'ÍB' => 'æ²´',
  'ÍC' => 'æ²Š',
  'ÍD' => 'æ²�',
  'ÍE' => 'æ²€',
  'ÍF' => 'æ³ž',
  'ÍG' => 'æ³€',
  'ÍH' => 'æ´°',
  'ÍI' => 'æ³�',
  'ÍJ' => 'æ³‡',
  'ÍK' => 'æ²°',
  'ÍL' => 'æ³¹',
  'ÍM' => 'æ³�',
  'ÍN' => 'æ³©',
  'ÍO' => 'æ³‘',
  'ÍP' => 'ç‚”',
  'ÍQ' => 'ç‚˜',
  'ÍR' => 'ç‚…',
  'ÍS' => 'ç‚“',
  'ÍT' => 'ç‚†',
  'ÍU' => 'ç‚„',
  'ÍV' => 'ç‚‘',
  'ÍW' => 'ç‚–',
  'ÍX' => 'ç‚‚',
  'ÍY' => 'ç‚š',
  'ÍZ' => 'ç‚ƒ',
  'Í[' => 'ç‰ª',
  'Í\\' => 'ç‹–',
  'Í]' => 'ç‹‹',
  'Í^' => 'ç‹˜',
  'Í_' => 'ç‹‰',
  'Í`' => 'ç‹œ',
  'Ía' => 'ç‹’',
  'Íb' => 'ç‹”',
  'Íc' => 'ç‹š',
  'Íd' => 'ç‹Œ',
  'Íe' => 'ç‹‘',
  'Íf' => 'çŽ¤',
  'Íg' => 'çŽ¡',
  'Íh' => 'çŽ­',
  'Íi' => 'çŽ¦',
  'Íj' => 'çŽ¢',
  'Ík' => 'çŽ ',
  'Íl' => 'çŽ¬',
  'Ím' => 'çŽ�',
  'Ín' => 'ç“�',
  'Ío' => 'ç“¨',
  'Íp' => 'ç”¿',
  'Íq' => 'ç•€',
  'Ír' => 'ç”¾',
  'Ís' => 'ç–Œ',
  'Ít' => 'ç–˜',
  'Íu' => 'çš¯',
  'Ív' => 'ç›³',
  'Íw' => 'ç›±',
  'Íx' => 'ç›°',
  'Íy' => 'ç›µ',
  'Íz' => 'çŸ¸',
  'Í{' => 'çŸ¼',
  'Í|' => 'çŸ¹',
  'Í}' => 'çŸ»',
  'Í~' => 'çŸº',
  'Í¡' => 'çŸ·',
  'Í¢' => 'ç¥‚',
  'Í£' => 'ç¤¿',
  'Í¤' => 'ç§…',
  'Í¥' => 'ç©¸',
  'Í¦' => 'ç©»',
  'Í§' => 'ç«»',
  'Í¨' => 'ç±µ',
  'Í©' => 'ç³½',
  'Íª' => 'è€µ',
  'Í«' => 'è‚�',
  'Í¬' => 'è‚®',
  'Í­' => 'è‚£',
  'Í®' => 'è‚¸',
  'Í¯' => 'è‚µ',
  'Í°' => 'è‚­',
  'Í±' => 'èˆ ',
  'Í²' => 'èŠ ',
  'Í³' => 'è‹€',
  'Í´' => 'èŠ«',
  'Íµ' => 'èŠš',
  'Í¶' => 'èŠ˜',
  'Í·' => 'èŠ›',
  'Í¸' => 'èŠµ',
  'Í¹' => 'èŠ§',
  'Íº' => 'èŠ®',
  'Í»' => 'èŠ¼',
  'Í¼' => 'èŠž',
  'Í½' => 'èŠº',
  'Í¾' => 'èŠ´',
  'Í¿' => 'èŠ¨',
  'ÍÀ' => 'èŠ¡',
  'ÍÁ' => 'èŠ©',
  'ÍÂ' => 'è‹‚',
  'ÍÃ' => 'èŠ¤',
  'ÍÄ' => 'è‹ƒ',
  'ÍÅ' => 'èŠ¶',
  'ÍÆ' => 'èŠ¢',
  'ÍÇ' => 'è™°',
  'ÍÈ' => 'è™¯',
  'ÍÉ' => 'è™­',
  'ÍÊ' => 'è™®',
  'ÍË' => 'è±–',
  'ÍÌ' => 'è¿’',
  'ÍÍ' => 'è¿‹',
  'ÍÎ' => 'è¿“',
  'ÍÏ' => 'è¿�',
  'ÍÐ' => 'è¿–',
  'ÍÑ' => 'è¿•',
  'ÍÒ' => 'è¿—',
  'ÍÓ' => 'é‚²',
  'ÍÔ' => 'é‚´',
  'ÍÕ' => 'é‚¯',
  'ÍÖ' => 'é‚³',
  'Í×' => 'é‚°',
  'ÍØ' => 'é˜¹',
  'ÍÙ' => 'é˜½',
  'ÍÚ' => 'é˜¼',
  'ÍÛ' => 'é˜º',
  'ÍÜ' => 'é™ƒ',
  'ÍÝ' => 'ä¿�',
  'ÍÞ' => 'ä¿…',
  'Íß' => 'ä¿“',
  'Íà' => 'ä¾²',
  'Íá' => 'ä¿‰',
  'Íâ' => 'ä¿‹',
  'Íã' => 'ä¿�',
  'Íä' => 'ä¿”',
  'Íå' => 'ä¿œ',
  'Íæ' => 'ä¿™',
  'Íç' => 'ä¾»',
  'Íè' => 'ä¾³',
  'Íé' => 'ä¿›',
  'Íê' => 'ä¿‡',
  'Íë' => 'ä¿–',
  'Íì' => 'ä¾º',
  'Íí' => 'ä¿€',
  'Íî' => 'ä¾¹',
  'Íï' => 'ä¿¬',
  'Íð' => 'å‰„',
  'Íñ' => 'å‰‰',
  'Íò' => 'å‹€',
  'Íó' => 'å‹‚',
  'Íô' => 'åŒ½',
  'Íõ' => 'å�¼',
  'Íö' => 'åŽ—',
  'Í÷' => 'åŽ–',
  'Íø' => 'åŽ™',
  'Íù' => 'åŽ˜',
  'Íú' => 'å’º',
  'Íû' => 'å’¡',
  'Íü' => 'å’­',
  'Íý' => 'å’¥',
  'Íþ' => 'å“�',
  'Î@' => 'å“ƒ',
  'ÎA' => 'èŒ�',
  'ÎB' => 'å’·',
  'ÎC' => 'å’®',
  'ÎD' => 'å“–',
  'ÎE' => 'å’¶',
  'ÎF' => 'å“…',
  'ÎG' => 'å“†',
  'ÎH' => 'å’ ',
  'ÎI' => 'å‘°',
  'ÎJ' => 'å’¼',
  'ÎK' => 'å’¢',
  'ÎL' => 'å’¾',
  'ÎM' => 'å‘²',
  'ÎN' => 'å“ž',
  'ÎO' => 'å’°',
  'ÎP' => 'åžµ',
  'ÎQ' => 'åžž',
  'ÎR' => 'åžŸ',
  'ÎS' => 'åž¤',
  'ÎT' => 'åžŒ',
  'ÎU' => 'åž—',
  'ÎV' => 'åž�',
  'ÎW' => 'åž›',
  'ÎX' => 'åž”',
  'ÎY' => 'åž˜',
  'ÎZ' => 'åž�',
  'Î[' => 'åž™',
  'Î\\' => 'åž¥',
  'Î]' => 'åžš',
  'Î^' => 'åž•',
  'Î_' => 'å£´',
  'Î`' => 'å¤�',
  'Îa' => 'å¥“',
  'Îb' => 'å§¡',
  'Îc' => 'å§ž',
  'Îd' => 'å§®',
  'Îe' => 'å¨€',
  'Îf' => 'å§±',
  'Îg' => 'å§�',
  'Îh' => 'å§º',
  'Îi' => 'å§½',
  'Îj' => 'å§¼',
  'Îk' => 'å§¶',
  'Îl' => 'å§¤',
  'Îm' => 'å§²',
  'În' => 'å§·',
  'Îo' => 'å§›',
  'Îp' => 'å§©',
  'Îq' => 'å§³',
  'Îr' => 'å§µ',
  'Îs' => 'å§ ',
  'Ît' => 'å§¾',
  'Îu' => 'å§´',
  'Îv' => 'å§­',
  'Îw' => 'å®¨',
  'Îx' => 'å±Œ',
  'Îy' => 'å³�',
  'Îz' => 'å³˜',
  'Î{' => 'å³Œ',
  'Î|' => 'å³—',
  'Î}' => 'å³‹',
  'Î~' => 'å³›',
  'Î¡' => 'å³ž',
  'Î¢' => 'å³š',
  'Î£' => 'å³‰',
  'Î¤' => 'å³‡',
  'Î¥' => 'å³Š',
  'Î¦' => 'å³–',
  'Î§' => 'å³“',
  'Î¨' => 'å³”',
  'Î©' => 'å³�',
  'Îª' => 'å³ˆ',
  'Î«' => 'å³†',
  'Î¬' => 'å³Ž',
  'Î­' => 'å³Ÿ',
  'Î®' => 'å³¸',
  'Î¯' => 'å·¹',
  'Î°' => 'å¸¡',
  'Î±' => 'å¸¢',
  'Î²' => 'å¸£',
  'Î³' => 'å¸ ',
  'Î´' => 'å¸¤',
  'Îµ' => 'åº°',
  'Î¶' => 'åº¤',
  'Î·' => 'åº¢',
  'Î¸' => 'åº›',
  'Î¹' => 'åº£',
  'Îº' => 'åº¥',
  'Î»' => 'å¼‡',
  'Î¼' => 'å¼®',
  'Î½' => 'å½–',
  'Î¾' => 'å¾†',
  'Î¿' => 'æ€·',
  'ÎÀ' => 'æ€¹',
  'ÎÁ' => 'æ�”',
  'ÎÂ' => 'æ�²',
  'ÎÃ' => 'æ�ž',
  'ÎÄ' => 'æ�…',
  'ÎÅ' => 'æ�“',
  'ÎÆ' => 'æ�‡',
  'ÎÇ' => 'æ�‰',
  'ÎÈ' => 'æ�›',
  'ÎÉ' => 'æ�Œ',
  'ÎÊ' => 'æ�€',
  'ÎË' => 'æ�‚',
  'ÎÌ' => 'æ�Ÿ',
  'ÎÍ' => 'æ€¤',
  'ÎÎ' => 'æ�„',
  'ÎÏ' => 'æ�˜',
  'ÎÐ' => 'æ�¦',
  'ÎÑ' => 'æ�®',
  'ÎÒ' => 'æ‰‚',
  'ÎÓ' => 'æ‰ƒ',
  'ÎÔ' => 'æ‹�',
  'ÎÕ' => 'æŒ�',
  'ÎÖ' => 'æŒ‹',
  'Î×' => 'æ‹µ',
  'ÎØ' => 'æŒŽ',
  'ÎÙ' => 'æŒƒ',
  'ÎÚ' => 'æ‹«',
  'ÎÛ' => 'æ‹¹',
  'ÎÜ' => 'æŒ�',
  'ÎÝ' => 'æŒŒ',
  'ÎÞ' => 'æ‹¸',
  'Îß' => 'æ‹¶',
  'Îà' => 'æŒ€',
  'Îá' => 'æŒ“',
  'Îâ' => 'æŒ”',
  'Îã' => 'æ‹º',
  'Îä' => 'æŒ•',
  'Îå' => 'æ‹»',
  'Îæ' => 'æ‹°',
  'Îç' => 'æ•�',
  'Îè' => 'æ•ƒ',
  'Îé' => 'æ–ª',
  'Îê' => 'æ–¿',
  'Îë' => 'æ˜¶',
  'Îì' => 'æ˜¡',
  'Îí' => 'æ˜²',
  'Îî' => 'æ˜µ',
  'Îï' => 'æ˜œ',
  'Îð' => 'æ˜¦',
  'Îñ' => 'æ˜¢',
  'Îò' => 'æ˜³',
  'Îó' => 'æ˜«',
  'Îô' => 'æ˜º',
  'Îõ' => 'æ˜�',
  'Îö' => 'æ˜´',
  'Î÷' => 'æ˜¹',
  'Îø' => 'æ˜®',
  'Îù' => 'æœ�',
  'Îú' => 'æœ�',
  'Îû' => 'æŸ�',
  'Îü' => 'æŸ²',
  'Îý' => 'æŸˆ',
  'Îþ' => 'æžº',
  'Ï@' => 'æŸœ',
  'ÏA' => 'æž»',
  'ÏB' => 'æŸ¸',
  'ÏC' => 'æŸ˜',
  'ÏD' => 'æŸ€',
  'ÏE' => 'æž·',
  'ÏF' => 'æŸ…',
  'ÏG' => 'æŸ«',
  'ÏH' => 'æŸ¤',
  'ÏI' => 'æŸŸ',
  'ÏJ' => 'æžµ',
  'ÏK' => 'æŸ�',
  'ÏL' => 'æž³',
  'ÏM' => 'æŸ·',
  'ÏN' => 'æŸ¶',
  'ÏO' => 'æŸ®',
  'ÏP' => 'æŸ£',
  'ÏQ' => 'æŸ‚',
  'ÏR' => 'æž¹',
  'ÏS' => 'æŸŽ',
  'ÏT' => 'æŸ§',
  'ÏU' => 'æŸ°',
  'ÏV' => 'æž²',
  'ÏW' => 'æŸ¼',
  'ÏX' => 'æŸ†',
  'ÏY' => 'æŸ­',
  'ÏZ' => 'æŸŒ',
  'Ï[' => 'æž®',
  'Ï\\' => 'æŸ¦',
  'Ï]' => 'æŸ›',
  'Ï^' => 'æŸº',
  'Ï_' => 'æŸ‰',
  'Ï`' => 'æŸŠ',
  'Ïa' => 'æŸƒ',
  'Ïb' => 'æŸª',
  'Ïc' => 'æŸ‹',
  'Ïd' => 'æ¬¨',
  'Ïe' => 'æ®‚',
  'Ïf' => 'æ®„',
  'Ïg' => 'æ®¶',
  'Ïh' => 'æ¯–',
  'Ïi' => 'æ¯˜',
  'Ïj' => 'æ¯ ',
  'Ïk' => 'æ° ',
  'Ïl' => 'æ°¡',
  'Ïm' => 'æ´¨',
  'Ïn' => 'æ´´',
  'Ïo' => 'æ´­',
  'Ïp' => 'æ´Ÿ',
  'Ïq' => 'æ´¼',
  'Ïr' => 'æ´¿',
  'Ïs' => 'æ´’',
  'Ït' => 'æ´Š',
  'Ïu' => 'æ³š',
  'Ïv' => 'æ´³',
  'Ïw' => 'æ´„',
  'Ïx' => 'æ´™',
  'Ïy' => 'æ´º',
  'Ïz' => 'æ´š',
  'Ï{' => 'æ´‘',
  'Ï|' => 'æ´€',
  'Ï}' => 'æ´�',
  'Ï~' => 'æµ‚',
  'Ï¡' => 'æ´�',
  'Ï¢' => 'æ´˜',
  'Ï£' => 'æ´·',
  'Ï¤' => 'æ´ƒ',
  'Ï¥' => 'æ´�',
  'Ï¦' => 'æµ€',
  'Ï§' => 'æ´‡',
  'Ï¨' => 'æ´ ',
  'Ï©' => 'æ´¬',
  'Ïª' => 'æ´ˆ',
  'Ï«' => 'æ´¢',
  'Ï¬' => 'æ´‰',
  'Ï­' => 'æ´�',
  'Ï®' => 'ç‚·',
  'Ï¯' => 'ç‚Ÿ',
  'Ï°' => 'ç‚¾',
  'Ï±' => 'ç‚±',
  'Ï²' => 'ç‚°',
  'Ï³' => 'ç‚¡',
  'Ï´' => 'ç‚´',
  'Ïµ' => 'ç‚µ',
  'Ï¶' => 'ç‚©',
  'Ï·' => 'ç‰�',
  'Ï¸' => 'ç‰‰',
  'Ï¹' => 'ç‰Š',
  'Ïº' => 'ç‰¬',
  'Ï»' => 'ç‰°',
  'Ï¼' => 'ç‰³',
  'Ï½' => 'ç‰®',
  'Ï¾' => 'ç‹Š',
  'Ï¿' => 'ç‹¤',
  'ÏÀ' => 'ç‹¨',
  'ÏÁ' => 'ç‹«',
  'ÏÂ' => 'ç‹Ÿ',
  'ÏÃ' => 'ç‹ª',
  'ÏÄ' => 'ç‹¦',
  'ÏÅ' => 'ç‹£',
  'ÏÆ' => 'çŽ…',
  'ÏÇ' => 'ç�Œ',
  'ÏÈ' => 'ç�‚',
  'ÏÉ' => 'ç�ˆ',
  'ÏÊ' => 'ç�…',
  'ÏË' => 'çŽ¹',
  'ÏÌ' => 'çŽ¶',
  'ÏÍ' => 'çŽµ',
  'ÏÎ' => 'çŽ´',
  'ÏÏ' => 'ç�«',
  'ÏÐ' => 'çŽ¿',
  'ÏÑ' => 'ç�‡',
  'ÏÒ' => 'çŽ¾',
  'ÏÓ' => 'ç�ƒ',
  'ÏÔ' => 'ç�†',
  'ÏÕ' => 'çŽ¸',
  'ÏÖ' => 'ç�‹',
  'Ï×' => 'ç“¬',
  'ÏØ' => 'ç“®',
  'ÏÙ' => 'ç”®',
  'ÏÚ' => 'ç•‡',
  'ÏÛ' => 'ç•ˆ',
  'ÏÜ' => 'ç–§',
  'ÏÝ' => 'ç–ª',
  'ÏÞ' => 'ç™¹',
  'Ïß' => 'ç›„',
  'Ïà' => 'çœˆ',
  'Ïá' => 'çœƒ',
  'Ïâ' => 'çœ„',
  'Ïã' => 'çœ…',
  'Ïä' => 'çœŠ',
  'Ïå' => 'ç›·',
  'Ïæ' => 'ç›»',
  'Ïç' => 'ç›º',
  'Ïè' => 'çŸ§',
  'Ïé' => 'çŸ¨',
  'Ïê' => 'ç †',
  'Ïë' => 'ç ‘',
  'Ïì' => 'ç ’',
  'Ïí' => 'ç …',
  'Ïî' => 'ç �',
  'Ïï' => 'ç �',
  'Ïð' => 'ç Ž',
  'Ïñ' => 'ç ‰',
  'Ïò' => 'ç ƒ',
  'Ïó' => 'ç “',
  'Ïô' => 'ç¥Š',
  'Ïõ' => 'ç¥Œ',
  'Ïö' => 'ç¥‹',
  'Ï÷' => 'ç¥…',
  'Ïø' => 'ç¥„',
  'Ïù' => 'ç§•',
  'Ïú' => 'ç§�',
  'Ïû' => 'ç§�',
  'Ïü' => 'ç§–',
  'Ïý' => 'ç§Ž',
  'Ïþ' => 'çª€',
  'Ð@' => 'ç©¾',
  'ÐA' => 'ç«‘',
  'ÐB' => 'ç¬€',
  'ÐC' => 'ç¬�',
  'ÐD' => 'ç±º',
  'ÐE' => 'ç±¸',
  'ÐF' => 'ç±¹',
  'ÐG' => 'ç±¿',
  'ÐH' => 'ç²€',
  'ÐI' => 'ç²�',
  'ÐJ' => 'ç´ƒ',
  'ÐK' => 'ç´ˆ',
  'ÐL' => 'ç´�',
  'ÐM' => 'ç½˜',
  'ÐN' => 'ç¾‘',
  'ÐO' => 'ç¾�',
  'ÐP' => 'ç¾¾',
  'ÐQ' => 'è€‡',
  'ÐR' => 'è€Ž',
  'ÐS' => 'è€�',
  'ÐT' => 'è€”',
  'ÐU' => 'è€·',
  'ÐV' => 'èƒ˜',
  'ÐW' => 'èƒ‡',
  'ÐX' => 'èƒ ',
  'ÐY' => 'èƒ‘',
  'ÐZ' => 'èƒˆ',
  'Ð[' => 'èƒ‚',
  'Ð\\' => 'èƒ�',
  'Ð]' => 'èƒ…',
  'Ð^' => 'èƒ£',
  'Ð_' => 'èƒ™',
  'Ð`' => 'èƒœ',
  'Ða' => 'èƒŠ',
  'Ðb' => 'èƒ•',
  'Ðc' => 'èƒ‰',
  'Ðd' => 'èƒ�',
  'Ðe' => 'èƒ—',
  'Ðf' => 'èƒ¦',
  'Ðg' => 'èƒ�',
  'Ðh' => 'è‡¿',
  'Ði' => 'èˆ¡',
  'Ðj' => 'èŠ”',
  'Ðk' => 'è‹™',
  'Ðl' => 'è‹¾',
  'Ðm' => 'è‹¹',
  'Ðn' => 'èŒ‡',
  'Ðo' => 'è‹¨',
  'Ðp' => 'èŒ€',
  'Ðq' => 'è‹•',
  'Ðr' => 'èŒº',
  'Ðs' => 'è‹«',
  'Ðt' => 'è‹–',
  'Ðu' => 'è‹´',
  'Ðv' => 'è‹¬',
  'Ðw' => 'è‹¡',
  'Ðx' => 'è‹²',
  'Ðy' => 'è‹µ',
  'Ðz' => 'èŒŒ',
  'Ð{' => 'è‹»',
  'Ð|' => 'è‹¶',
  'Ð}' => 'è‹°',
  'Ð~' => 'è‹ª',
  'Ð¡' => 'è‹¤',
  'Ð¢' => 'è‹ ',
  'Ð£' => 'è‹º',
  'Ð¤' => 'è‹³',
  'Ð¥' => 'è‹­',
  'Ð¦' => 'è™·',
  'Ð§' => 'è™´',
  'Ð¨' => 'è™¼',
  'Ð©' => 'è™³',
  'Ðª' => 'è¡�',
  'Ð«' => 'è¡Ž',
  'Ð¬' => 'è¡§',
  'Ð­' => 'è¡ª',
  'Ð®' => 'è¡©',
  'Ð¯' => 'è§“',
  'Ð°' => 'è¨„',
  'Ð±' => 'è¨‡',
  'Ð²' => 'èµ²',
  'Ð³' => 'è¿£',
  'Ð´' => 'è¿¡',
  'Ðµ' => 'è¿®',
  'Ð¶' => 'è¿ ',
  'Ð·' => 'éƒ±',
  'Ð¸' => 'é‚½',
  'Ð¹' => 'é‚¿',
  'Ðº' => 'éƒ•',
  'Ð»' => 'éƒ…',
  'Ð¼' => 'é‚¾',
  'Ð½' => 'éƒ‡',
  'Ð¾' => 'éƒ‹',
  'Ð¿' => 'éƒˆ',
  'ÐÀ' => 'é‡”',
  'ÐÁ' => 'é‡“',
  'ÐÂ' => 'é™”',
  'ÐÃ' => 'é™�',
  'ÐÄ' => 'é™‘',
  'ÐÅ' => 'é™“',
  'ÐÆ' => 'é™Š',
  'ÐÇ' => 'é™Ž',
  'ÐÈ' => 'å€ž',
  'ÐÉ' => 'å€…',
  'ÐÊ' => 'å€‡',
  'ÐË' => 'å€“',
  'ÐÌ' => 'å€¢',
  'ÐÍ' => 'å€°',
  'ÐÎ' => 'å€›',
  'ÐÏ' => 'ä¿µ',
  'ÐÐ' => 'ä¿´',
  'ÐÑ' => 'å€³',
  'ÐÒ' => 'å€·',
  'ÐÓ' => 'å€¬',
  'ÐÔ' => 'ä¿¶',
  'ÐÕ' => 'ä¿·',
  'ÐÖ' => 'å€—',
  'Ð×' => 'å€œ',
  'ÐØ' => 'å€ ',
  'ÐÙ' => 'å€§',
  'ÐÚ' => 'å€µ',
  'ÐÛ' => 'å€¯',
  'ÐÜ' => 'å€±',
  'ÐÝ' => 'å€Ž',
  'ÐÞ' => 'å…š',
  'Ðß' => 'å†”',
  'Ðà' => 'å†“',
  'Ðá' => 'å‡Š',
  'Ðâ' => 'å‡„',
  'Ðã' => 'å‡…',
  'Ðä' => 'å‡ˆ',
  'Ðå' => 'å‡Ž',
  'Ðæ' => 'å‰¡',
  'Ðç' => 'å‰š',
  'Ðè' => 'å‰’',
  'Ðé' => 'å‰ž',
  'Ðê' => 'å‰Ÿ',
  'Ðë' => 'å‰•',
  'Ðì' => 'å‰¢',
  'Ðí' => 'å‹�',
  'Ðî' => 'åŒŽ',
  'Ðï' => 'åŽž',
  'Ðð' => 'å”¦',
  'Ðñ' => 'å“¢',
  'Ðò' => 'å”—',
  'Ðó' => 'å”’',
  'Ðô' => 'å“§',
  'Ðõ' => 'å“³',
  'Ðö' => 'å“¤',
  'Ð÷' => 'å”š',
  'Ðø' => 'å“¿',
  'Ðù' => 'å”„',
  'Ðú' => 'å”ˆ',
  'Ðû' => 'å“«',
  'Ðü' => 'å”‘',
  'Ðý' => 'å”…',
  'Ðþ' => 'å“±',
  'Ñ@' => 'å”Š',
  'ÑA' => 'å“»',
  'ÑB' => 'å“·',
  'ÑC' => 'å“¸',
  'ÑD' => 'å“ ',
  'ÑE' => 'å”Ž',
  'ÑF' => 'å”ƒ',
  'ÑG' => 'å”‹',
  'ÑH' => 'åœ�',
  'ÑI' => 'åœ‚',
  'ÑJ' => 'åŸŒ',
  'ÑK' => 'å ²',
  'ÑL' => 'åŸ•',
  'ÑM' => 'åŸ’',
  'ÑN' => 'åžº',
  'ÑO' => 'åŸ†',
  'ÑP' => 'åž½',
  'ÑQ' => 'åž¼',
  'ÑR' => 'åž¸',
  'ÑS' => 'åž¶',
  'ÑT' => 'åž¿',
  'ÑU' => 'åŸ‡',
  'ÑV' => 'åŸ�',
  'ÑW' => 'åž¹',
  'ÑX' => 'åŸ�',
  'ÑY' => 'å¤Ž',
  'ÑZ' => 'å¥Š',
  'Ñ[' => 'å¨™',
  'Ñ\\' => 'å¨–',
  'Ñ]' => 'å¨­',
  'Ñ^' => 'å¨®',
  'Ñ_' => 'å¨•',
  'Ñ`' => 'å¨�',
  'Ña' => 'å¨—',
  'Ñb' => 'å¨Š',
  'Ñc' => 'å¨ž',
  'Ñd' => 'å¨³',
  'Ñe' => 'å­¬',
  'Ñf' => 'å®§',
  'Ñg' => 'å®­',
  'Ñh' => 'å®¬',
  'Ñi' => 'å°ƒ',
  'Ñj' => 'å±–',
  'Ñk' => 'å±”',
  'Ñl' => 'å³¬',
  'Ñm' => 'å³¿',
  'Ñn' => 'å³®',
  'Ño' => 'å³±',
  'Ñp' => 'å³·',
  'Ñq' => 'å´€',
  'Ñr' => 'å³¹',
  'Ñs' => 'å¸©',
  'Ñt' => 'å¸¨',
  'Ñu' => 'åº¨',
  'Ñv' => 'åº®',
  'Ñw' => 'åºª',
  'Ñx' => 'åº¬',
  'Ñy' => 'å¼³',
  'Ñz' => 'å¼°',
  'Ñ{' => 'å½§',
  'Ñ|' => 'æ��',
  'Ñ}' => 'æ�š',
  'Ñ~' => 'æ�§',
  'Ñ¡' => 'æ��',
  'Ñ¢' => 'æ‚¢',
  'Ñ£' => 'æ‚ˆ',
  'Ñ¤' => 'æ‚€',
  'Ñ¥' => 'æ‚’',
  'Ñ¦' => 'æ‚�',
  'Ñ§' => 'æ‚�',
  'Ñ¨' => 'æ‚ƒ',
  'Ñ©' => 'æ‚•',
  'Ñª' => 'æ‚›',
  'Ñ«' => 'æ‚—',
  'Ñ¬' => 'æ‚‡',
  'Ñ­' => 'æ‚œ',
  'Ñ®' => 'æ‚Ž',
  'Ñ¯' => 'æˆ™',
  'Ñ°' => 'æ‰†',
  'Ñ±' => 'æ‹²',
  'Ñ²' => 'æŒ�',
  'Ñ³' => 'æ�–',
  'Ñ´' => 'æŒ¬',
  'Ñµ' => 'æ�„',
  'Ñ¶' => 'æ�…',
  'Ñ·' => 'æŒ¶',
  'Ñ¸' => 'æ�ƒ',
  'Ñ¹' => 'æ�¤',
  'Ñº' => 'æŒ¹',
  'Ñ»' => 'æ�‹',
  'Ñ¼' => 'æ�Š',
  'Ñ½' => 'æŒ¼',
  'Ñ¾' => 'æŒ©',
  'Ñ¿' => 'æ��',
  'ÑÀ' => 'æŒ´',
  'ÑÁ' => 'æ�˜',
  'ÑÂ' => 'æ�”',
  'ÑÃ' => 'æ�™',
  'ÑÄ' => 'æŒ­',
  'ÑÅ' => 'æ�‡',
  'ÑÆ' => 'æŒ³',
  'ÑÇ' => 'æ�š',
  'ÑÈ' => 'æ�‘',
  'ÑÉ' => 'æŒ¸',
  'ÑÊ' => 'æ�—',
  'ÑË' => 'æ�€',
  'ÑÌ' => 'æ�ˆ',
  'ÑÍ' => 'æ•Š',
  'ÑÎ' => 'æ•†',
  'ÑÏ' => 'æ—†',
  'ÑÐ' => 'æ—ƒ',
  'ÑÑ' => 'æ—„',
  'ÑÒ' => 'æ—‚',
  'ÑÓ' => 'æ™Š',
  'ÑÔ' => 'æ™Ÿ',
  'ÑÕ' => 'æ™‡',
  'ÑÖ' => 'æ™‘',
  'Ñ×' => 'æœ’',
  'ÑØ' => 'æœ“',
  'ÑÙ' => 'æ Ÿ',
  'ÑÚ' => 'æ š',
  'ÑÛ' => 'æ¡‰',
  'ÑÜ' => 'æ ²',
  'ÑÝ' => 'æ ³',
  'ÑÞ' => 'æ »',
  'Ñß' => 'æ¡‹',
  'Ñà' => 'æ¡�',
  'Ñá' => 'æ –',
  'Ñâ' => 'æ ±',
  'Ñã' => 'æ œ',
  'Ñä' => 'æ µ',
  'Ñå' => 'æ «',
  'Ñæ' => 'æ ­',
  'Ñç' => 'æ ¯',
  'Ñè' => 'æ¡Ž',
  'Ñé' => 'æ¡„',
  'Ñê' => 'æ ´',
  'Ñë' => 'æ �',
  'Ñì' => 'æ ’',
  'Ñí' => 'æ ”',
  'Ñî' => 'æ ¦',
  'Ñï' => 'æ ¨',
  'Ñð' => 'æ ®',
  'Ññ' => 'æ¡�',
  'Ñò' => 'æ º',
  'Ñó' => 'æ ¥',
  'Ñô' => 'æ  ',
  'Ñõ' => 'æ¬¬',
  'Ñö' => 'æ¬¯',
  'Ñ÷' => 'æ¬­',
  'Ñø' => 'æ¬±',
  'Ñù' => 'æ¬´',
  'Ñú' => 'æ­­',
  'Ñû' => 'è‚‚',
  'Ñü' => 'æ®ˆ',
  'Ñý' => 'æ¯¦',
  'Ñþ' => 'æ¯¤',
  'Ò@' => 'æ¯¨',
  'ÒA' => 'æ¯£',
  'ÒB' => 'æ¯¢',
  'ÒC' => 'æ¯§',
  'ÒD' => 'æ°¥',
  'ÒE' => 'æµº',
  'ÒF' => 'æµ£',
  'ÒG' => 'æµ¤',
  'ÒH' => 'æµ¶',
  'ÒI' => 'æ´�',
  'ÒJ' => 'æµ¡',
  'ÒK' => 'æ¶’',
  'ÒL' => 'æµ˜',
  'ÒM' => 'æµ¢',
  'ÒN' => 'æµ­',
  'ÒO' => 'æµ¯',
  'ÒP' => 'æ¶‘',
  'ÒQ' => 'æ¶�',
  'ÒR' => 'æ·¯',
  'ÒS' => 'æµ¿',
  'ÒT' => 'æ¶†',
  'ÒU' => 'æµž',
  'ÒV' => 'æµ§',
  'ÒW' => 'æµ ',
  'ÒX' => 'æ¶—',
  'ÒY' => 'æµ°',
  'ÒZ' => 'æµ¼',
  'Ò[' => 'æµŸ',
  'Ò\\' => 'æ¶‚',
  'Ò]' => 'æ¶˜',
  'Ò^' => 'æ´¯',
  'Ò_' => 'æµ¨',
  'Ò`' => 'æ¶‹',
  'Òa' => 'æµ¾',
  'Òb' => 'æ¶€',
  'Òc' => 'æ¶„',
  'Òd' => 'æ´–',
  'Òe' => 'æ¶ƒ',
  'Òf' => 'æµ»',
  'Òg' => 'æµ½',
  'Òh' => 'æµµ',
  'Òi' => 'æ¶�',
  'Òj' => 'çƒœ',
  'Òk' => 'çƒ“',
  'Òl' => 'çƒ‘',
  'Òm' => 'çƒ�',
  'Òn' => 'çƒ‹',
  'Òo' => 'ç¼¹',
  'Òp' => 'çƒ¢',
  'Òq' => 'çƒ—',
  'Òr' => 'çƒ’',
  'Òs' => 'çƒž',
  'Òt' => 'çƒ ',
  'Òu' => 'çƒ”',
  'Òv' => 'çƒ�',
  'Òw' => 'çƒ…',
  'Òx' => 'çƒ†',
  'Òy' => 'çƒ‡',
  'Òz' => 'çƒš',
  'Ò{' => 'çƒŽ',
  'Ò|' => 'çƒ¡',
  'Ò}' => 'ç‰‚',
  'Ò~' => 'ç‰¸',
  'Ò¡' => 'ç‰·',
  'Ò¢' => 'ç‰¶',
  'Ò£' => 'çŒ€',
  'Ò¤' => 'ç‹º',
  'Ò¥' => 'ç‹´',
  'Ò¦' => 'ç‹¾',
  'Ò§' => 'ç‹¶',
  'Ò¨' => 'ç‹³',
  'Ò©' => 'ç‹»',
  'Òª' => 'çŒ�',
  'Ò«' => 'ç�“',
  'Ò¬' => 'ç�™',
  'Ò­' => 'ç�¥',
  'Ò®' => 'ç�–',
  'Ò¯' => 'çŽ¼',
  'Ò°' => 'ç�§',
  'Ò±' => 'ç�£',
  'Ò²' => 'ç�©',
  'Ò³' => 'ç�œ',
  'Ò´' => 'ç�’',
  'Òµ' => 'ç�›',
  'Ò¶' => 'ç�”',
  'Ò·' => 'ç��',
  'Ò¸' => 'ç�š',
  'Ò¹' => 'ç�—',
  'Òº' => 'ç�˜',
  'Ò»' => 'ç�¨',
  'Ò¼' => 'ç“ž',
  'Ò½' => 'ç“Ÿ',
  'Ò¾' => 'ç“´',
  'Ò¿' => 'ç“µ',
  'ÒÀ' => 'ç”¡',
  'ÒÁ' => 'ç•›',
  'ÒÂ' => 'ç•Ÿ',
  'ÒÃ' => 'ç–°',
  'ÒÄ' => 'ç—�',
  'ÒÅ' => 'ç–»',
  'ÒÆ' => 'ç—„',
  'ÒÇ' => 'ç—€',
  'ÒÈ' => 'ç–¿',
  'ÒÉ' => 'ç–¶',
  'ÒÊ' => 'ç–º',
  'ÒË' => 'çšŠ',
  'ÒÌ' => 'ç›‰',
  'ÒÍ' => 'çœ�',
  'ÒÎ' => 'çœ›',
  'ÒÏ' => 'çœ�',
  'ÒÐ' => 'çœ“',
  'ÒÑ' => 'çœ’',
  'ÒÒ' => 'çœ£',
  'ÒÓ' => 'çœ‘',
  'ÒÔ' => 'çœ•',
  'ÒÕ' => 'çœ™',
  'ÒÖ' => 'çœš',
  'Ò×' => 'çœ¢',
  'ÒØ' => 'çœ§',
  'ÒÙ' => 'ç £',
  'ÒÚ' => 'ç ¬',
  'ÒÛ' => 'ç ¢',
  'ÒÜ' => 'ç µ',
  'ÒÝ' => 'ç ¯',
  'ÒÞ' => 'ç ¨',
  'Òß' => 'ç ®',
  'Òà' => 'ç «',
  'Òá' => 'ç ¡',
  'Òâ' => 'ç ©',
  'Òã' => 'ç ³',
  'Òä' => 'ç ª',
  'Òå' => 'ç ±',
  'Òæ' => 'ç¥”',
  'Òç' => 'ç¥›',
  'Òè' => 'ç¥�',
  'Òé' => 'ç¥œ',
  'Òê' => 'ç¥“',
  'Òë' => 'ç¥’',
  'Òì' => 'ç¥‘',
  'Òí' => 'ç§«',
  'Òî' => 'ç§¬',
  'Òï' => 'ç§ ',
  'Òð' => 'ç§®',
  'Òñ' => 'ç§­',
  'Òò' => 'ç§ª',
  'Òó' => 'ç§œ',
  'Òô' => 'ç§ž',
  'Òõ' => 'ç§�',
  'Òö' => 'çª†',
  'Ò÷' => 'çª‰',
  'Òø' => 'çª…',
  'Òù' => 'çª‹',
  'Òú' => 'çªŒ',
  'Òû' => 'çªŠ',
  'Òü' => 'çª‡',
  'Òý' => 'ç«˜',
  'Òþ' => 'ç¬�',
  'Ó@' => 'ç¬„',
  'ÓA' => 'ç¬“',
  'ÓB' => 'ç¬…',
  'ÓC' => 'ç¬�',
  'ÓD' => 'ç¬ˆ',
  'ÓE' => 'ç¬Š',
  'ÓF' => 'ç¬Ž',
  'ÓG' => 'ç¬‰',
  'ÓH' => 'ç¬’',
  'ÓI' => 'ç²„',
  'ÓJ' => 'ç²‘',
  'ÓK' => 'ç²Š',
  'ÓL' => 'ç²Œ',
  'ÓM' => 'ç²ˆ',
  'ÓN' => 'ç²�',
  'ÓO' => 'ç²…',
  'ÓP' => 'ç´ž',
  'ÓQ' => 'ç´�',
  'ÓR' => 'ç´‘',
  'ÓS' => 'ç´Ž',
  'ÓT' => 'ç´˜',
  'ÓU' => 'ç´–',
  'ÓV' => 'ç´“',
  'ÓW' => 'ç´Ÿ',
  'ÓX' => 'ç´’',
  'ÓY' => 'ç´�',
  'ÓZ' => 'ç´Œ',
  'Ó[' => 'ç½œ',
  'Ó\\' => 'ç½¡',
  'Ó]' => 'ç½ž',
  'Ó^' => 'ç½ ',
  'Ó_' => 'ç½�',
  'Ó`' => 'ç½›',
  'Óa' => 'ç¾–',
  'Ób' => 'ç¾’',
  'Óc' => 'ç¿ƒ',
  'Ód' => 'ç¿‚',
  'Óe' => 'ç¿€',
  'Óf' => 'è€–',
  'Óg' => 'è€¾',
  'Óh' => 'è€¹',
  'Ói' => 'èƒº',
  'Ój' => 'èƒ²',
  'Ók' => 'èƒ¹',
  'Ól' => 'èƒµ',
  'Óm' => 'è„�',
  'Ón' => 'èƒ»',
  'Óo' => 'è„€',
  'Óp' => 'èˆ�',
  'Óq' => 'èˆ¯',
  'Ór' => 'èˆ¥',
  'Ós' => 'èŒ³',
  'Ót' => 'èŒ­',
  'Óu' => 'è�„',
  'Óv' => 'èŒ™',
  'Ów' => 'è�‘',
  'Óx' => 'èŒ¥',
  'Óy' => 'è�–',
  'Óz' => 'èŒ¿',
  'Ó{' => 'è��',
  'Ó|' => 'èŒ¦',
  'Ó}' => 'èŒœ',
  'Ó~' => 'èŒ¢',
  'Ó¡' => 'è�‚',
  'Ó¢' => 'è�Ž',
  'Ó£' => 'èŒ›',
  'Ó¤' => 'èŒª',
  'Ó¥' => 'èŒˆ',
  'Ó¦' => 'èŒ¼',
  'Ó§' => 'è��',
  'Ó¨' => 'èŒ–',
  'Ó©' => 'èŒ¤',
  'Óª' => 'èŒ ',
  'Ó«' => 'èŒ·',
  'Ó¬' => 'èŒ¯',
  'Ó­' => 'èŒ©',
  'Ó®' => 'è�‡',
  'Ó¯' => 'è�…',
  'Ó°' => 'è�Œ',
  'Ó±' => 'è�“',
  'Ó²' => 'èŒž',
  'Ó³' => 'èŒ¬',
  'Ó´' => 'è�‹',
  'Óµ' => 'èŒ§',
  'Ó¶' => 'è�ˆ',
  'Ó·' => 'è™“',
  'Ó¸' => 'è™’',
  'Ó¹' => 'èš¢',
  'Óº' => 'èš¨',
  'Ó»' => 'èš–',
  'Ó¼' => 'èš�',
  'Ó½' => 'èš‘',
  'Ó¾' => 'èšž',
  'Ó¿' => 'èš‡',
  'ÓÀ' => 'èš—',
  'ÓÁ' => 'èš†',
  'ÓÂ' => 'èš‹',
  'ÓÃ' => 'èšš',
  'ÓÄ' => 'èš…',
  'ÓÅ' => 'èš¥',
  'ÓÆ' => 'èš™',
  'ÓÇ' => 'èš¡',
  'ÓÈ' => 'èš§',
  'ÓÉ' => 'èš•',
  'ÓÊ' => 'èš˜',
  'ÓË' => 'èšŽ',
  'ÓÌ' => 'èš�',
  'ÓÍ' => 'èš�',
  'ÓÎ' => 'èš”',
  'ÓÏ' => 'è¡ƒ',
  'ÓÐ' => 'è¡„',
  'ÓÑ' => 'è¡­',
  'ÓÒ' => 'è¡µ',
  'ÓÓ' => 'è¡¶',
  'ÓÔ' => 'è¡²',
  'ÓÕ' => 'è¢€',
  'ÓÖ' => 'è¡±',
  'Ó×' => 'è¡¿',
  'ÓØ' => 'è¡¯',
  'ÓÙ' => 'è¢ƒ',
  'ÓÚ' => 'è¡¾',
  'ÓÛ' => 'è¡´',
  'ÓÜ' => 'è¡¼',
  'ÓÝ' => 'è¨’',
  'ÓÞ' => 'è±‡',
  'Óß' => 'è±—',
  'Óà' => 'è±»',
  'Óá' => 'è²¤',
  'Óâ' => 'è²£',
  'Óã' => 'èµ¶',
  'Óä' => 'èµ¸',
  'Óå' => 'è¶µ',
  'Óæ' => 'è¶·',
  'Óç' => 'è¶¶',
  'Óè' => 'è»‘',
  'Óé' => 'è»“',
  'Óê' => 'è¿¾',
  'Óë' => 'è¿µ',
  'Óì' => 'é€‚',
  'Óí' => 'è¿¿',
  'Óî' => 'è¿»',
  'Óï' => 'é€„',
  'Óð' => 'è¿¼',
  'Óñ' => 'è¿¶',
  'Óò' => 'éƒ–',
  'Óó' => 'éƒ ',
  'Óô' => 'éƒ™',
  'Óõ' => 'éƒš',
  'Óö' => 'éƒ£',
  'Ó÷' => 'éƒŸ',
  'Óø' => 'éƒ¥',
  'Óù' => 'éƒ˜',
  'Óú' => 'éƒ›',
  'Óû' => 'éƒ—',
  'Óü' => 'éƒœ',
  'Óý' => 'éƒ¤',
  'Óþ' => 'é…�',
  'Ô@' => 'é…Ž',
  'ÔA' => 'é…�',
  'ÔB' => 'é‡•',
  'ÔC' => 'é‡¢',
  'ÔD' => 'é‡š',
  'ÔE' => 'é™œ',
  'ÔF' => 'é™Ÿ',
  'ÔG' => 'éš¼',
  'ÔH' => 'é££',
  'ÔI' => 'é«Ÿ',
  'ÔJ' => 'é¬¯',
  'ÔK' => 'ä¹¿',
  'ÔL' => 'å�°',
  'ÔM' => 'å�ª',
  'ÔN' => 'å�¡',
  'ÔO' => 'å�ž',
  'ÔP' => 'å� ',
  'ÔQ' => 'å�“',
  'ÔR' => 'å�‹',
  'ÔS' => 'å��',
  'ÔT' => 'å�²',
  'ÔU' => 'å�ˆ',
  'ÔV' => 'å��',
  'ÔW' => 'å��',
  'ÔX' => 'å�›',
  'ÔY' => 'å�Š',
  'ÔZ' => 'å�¢',
  'Ô[' => 'å€•',
  'Ô\\' => 'å�…',
  'Ô]' => 'å�Ÿ',
  'Ô^' => 'å�©',
  'Ô_' => 'å�«',
  'Ô`' => 'å�£',
  'Ôa' => 'å�¤',
  'Ôb' => 'å�†',
  'Ôc' => 'å�€',
  'Ôd' => 'å�®',
  'Ôe' => 'å�³',
  'Ôf' => 'å�—',
  'Ôg' => 'å�‘',
  'Ôh' => 'å‡�',
  'Ôi' => 'å‰«',
  'Ôj' => 'å‰­',
  'Ôk' => 'å‰¬',
  'Ôl' => 'å‰®',
  'Ôm' => 'å‹–',
  'Ôn' => 'å‹“',
  'Ôo' => 'åŒ­',
  'Ôp' => 'åŽœ',
  'Ôq' => 'å•µ',
  'Ôr' => 'å•¶',
  'Ôs' => 'å”¼',
  'Ôt' => 'å•�',
  'Ôu' => 'å•�',
  'Ôv' => 'å”´',
  'Ôw' => 'å”ª',
  'Ôx' => 'å•‘',
  'Ôy' => 'å•¢',
  'Ôz' => 'å”¶',
  'Ô{' => 'å”µ',
  'Ô|' => 'å”°',
  'Ô}' => 'å•’',
  'Ô~' => 'å•…',
  'Ô¡' => 'å”Œ',
  'Ô¢' => 'å”²',
  'Ô£' => 'å•¥',
  'Ô¤' => 'å•Ž',
  'Ô¥' => 'å”¹',
  'Ô¦' => 'å•ˆ',
  'Ô§' => 'å”­',
  'Ô¨' => 'å”»',
  'Ô©' => 'å•€',
  'Ôª' => 'å•‹',
  'Ô«' => 'åœŠ',
  'Ô¬' => 'åœ‡',
  'Ô­' => 'åŸ»',
  'Ô®' => 'å ”',
  'Ô¯' => 'åŸ¢',
  'Ô°' => 'åŸ¶',
  'Ô±' => 'åŸœ',
  'Ô²' => 'åŸ´',
  'Ô³' => 'å €',
  'Ô´' => 'åŸ­',
  'Ôµ' => 'åŸ½',
  'Ô¶' => 'å ˆ',
  'Ô·' => 'åŸ¸',
  'Ô¸' => 'å ‹',
  'Ô¹' => 'åŸ³',
  'Ôº' => 'åŸ�',
  'Ô»' => 'å ‡',
  'Ô¼' => 'åŸ®',
  'Ô½' => 'åŸ£',
  'Ô¾' => 'åŸ²',
  'Ô¿' => 'åŸ¥',
  'ÔÀ' => 'åŸ¬',
  'ÔÁ' => 'åŸ¡',
  'ÔÂ' => 'å Ž',
  'ÔÃ' => 'åŸ¼',
  'ÔÄ' => 'å �',
  'ÔÅ' => 'åŸ§',
  'ÔÆ' => 'å �',
  'ÔÇ' => 'å Œ',
  'ÔÈ' => 'åŸ±',
  'ÔÉ' => 'åŸ©',
  'ÔÊ' => 'åŸ°',
  'ÔË' => 'å �',
  'ÔÌ' => 'å „',
  'ÔÍ' => 'å¥œ',
  'ÔÎ' => 'å© ',
  'ÔÏ' => 'å©˜',
  'ÔÐ' => 'å©•',
  'ÔÑ' => 'å©§',
  'ÔÒ' => 'å©ž',
  'ÔÓ' => 'å¨¸',
  'ÔÔ' => 'å¨µ',
  'ÔÕ' => 'å©­',
  'ÔÖ' => 'å©�',
  'Ô×' => 'å©Ÿ',
  'ÔØ' => 'å©¥',
  'ÔÙ' => 'å©¬',
  'ÔÚ' => 'å©“',
  'ÔÛ' => 'å©¤',
  'ÔÜ' => 'å©—',
  'ÔÝ' => 'å©ƒ',
  'ÔÞ' => 'å©�',
  'Ôß' => 'å©’',
  'Ôà' => 'å©„',
  'Ôá' => 'å©›',
  'Ôâ' => 'å©ˆ',
  'Ôã' => 'åªŽ',
  'Ôä' => 'å¨¾',
  'Ôå' => 'å©�',
  'Ôæ' => 'å¨¹',
  'Ôç' => 'å©Œ',
  'Ôè' => 'å©°',
  'Ôé' => 'å©©',
  'Ôê' => 'å©‡',
  'Ôë' => 'å©‘',
  'Ôì' => 'å©–',
  'Ôí' => 'å©‚',
  'Ôî' => 'å©œ',
  'Ôï' => 'å­²',
  'Ôð' => 'å­®',
  'Ôñ' => 'å¯�',
  'Ôò' => 'å¯€',
  'Ôó' => 'å±™',
  'Ôô' => 'å´ž',
  'Ôõ' => 'å´‹',
  'Ôö' => 'å´�',
  'Ô÷' => 'å´š',
  'Ôø' => 'å´ ',
  'Ôù' => 'å´Œ',
  'Ôú' => 'å´¨',
  'Ôû' => 'å´�',
  'Ôü' => 'å´¦',
  'Ôý' => 'å´¥',
  'Ôþ' => 'å´�',
  'Õ@' => 'å´°',
  'ÕA' => 'å´’',
  'ÕB' => 'å´£',
  'ÕC' => 'å´Ÿ',
  'ÕD' => 'å´®',
  'ÕE' => 'å¸¾',
  'ÕF' => 'å¸´',
  'ÕG' => 'åº±',
  'ÕH' => 'åº´',
  'ÕI' => 'åº¹',
  'ÕJ' => 'åº²',
  'ÕK' => 'åº³',
  'ÕL' => 'å¼¶',
  'ÕM' => 'å¼¸',
  'ÕN' => 'å¾›',
  'ÕO' => 'å¾–',
  'ÕP' => 'å¾Ÿ',
  'ÕQ' => 'æ‚Š',
  'ÕR' => 'æ‚�',
  'ÕS' => 'æ‚†',
  'ÕT' => 'æ‚¾',
  'ÕU' => 'æ‚°',
  'ÕV' => 'æ‚º',
  'ÕW' => 'æƒ“',
  'ÕX' => 'æƒ”',
  'ÕY' => 'æƒ�',
  'ÕZ' => 'æƒ¤',
  'Õ[' => 'æƒ™',
  'Õ\\' => 'æƒ�',
  'Õ]' => 'æƒˆ',
  'Õ^' => 'æ‚±',
  'Õ_' => 'æƒ›',
  'Õ`' => 'æ‚·',
  'Õa' => 'æƒŠ',
  'Õb' => 'æ‚¿',
  'Õc' => 'æƒƒ',
  'Õd' => 'æƒ�',
  'Õe' => 'æƒ€',
  'Õf' => 'æŒ²',
  'Õg' => 'æ�¥',
  'Õh' => 'æŽŠ',
  'Õi' => 'æŽ‚',
  'Õj' => 'æ�½',
  'Õk' => 'æŽ½',
  'Õl' => 'æŽž',
  'Õm' => 'æŽ­',
  'Õn' => 'æŽ�',
  'Õo' => 'æŽ—',
  'Õp' => 'æŽ«',
  'Õq' => 'æŽŽ',
  'Õr' => 'æ�¯',
  'Õs' => 'æŽ‡',
  'Õt' => 'æŽ�',
  'Õu' => 'æ�®',
  'Õv' => 'æŽ¯',
  'Õw' => 'æ�µ',
  'Õx' => 'æŽœ',
  'Õy' => 'æ�­',
  'Õz' => 'æŽ®',
  'Õ{' => 'æ�¼',
  'Õ|' => 'æŽ¤',
  'Õ}' => 'æŒ»',
  'Õ~' => 'æŽŸ',
  'Õ¡' => 'æ�¸',
  'Õ¢' => 'æŽ…',
  'Õ£' => 'æŽ�',
  'Õ¤' => 'æŽ‘',
  'Õ¥' => 'æŽ�',
  'Õ¦' => 'æ�°',
  'Õ§' => 'æ•“',
  'Õ¨' => 'æ—�',
  'Õ©' => 'æ™¥',
  'Õª' => 'æ™¡',
  'Õ«' => 'æ™›',
  'Õ¬' => 'æ™™',
  'Õ­' => 'æ™œ',
  'Õ®' => 'æ™¢',
  'Õ¯' => 'æœ˜',
  'Õ°' => 'æ¡¹',
  'Õ±' => 'æ¢‡',
  'Õ²' => 'æ¢�',
  'Õ³' => 'æ¢œ',
  'Õ´' => 'æ¡­',
  'Õµ' => 'æ¡®',
  'Õ¶' => 'æ¢®',
  'Õ·' => 'æ¢«',
  'Õ¸' => 'æ¥–',
  'Õ¹' => 'æ¡¯',
  'Õº' => 'æ¢£',
  'Õ»' => 'æ¢¬',
  'Õ¼' => 'æ¢©',
  'Õ½' => 'æ¡µ',
  'Õ¾' => 'æ¡´',
  'Õ¿' => 'æ¢²',
  'ÕÀ' => 'æ¢�',
  'ÕÁ' => 'æ¡·',
  'ÕÂ' => 'æ¢’',
  'ÕÃ' => 'æ¡¼',
  'ÕÄ' => 'æ¡«',
  'ÕÅ' => 'æ¡²',
  'ÕÆ' => 'æ¢ª',
  'ÕÇ' => 'æ¢€',
  'ÕÈ' => 'æ¡±',
  'ÕÉ' => 'æ¡¾',
  'ÕÊ' => 'æ¢›',
  'ÕË' => 'æ¢–',
  'ÕÌ' => 'æ¢‹',
  'ÕÍ' => 'æ¢ ',
  'ÕÎ' => 'æ¢‰',
  'ÕÏ' => 'æ¢¤',
  'ÕÐ' => 'æ¡¸',
  'ÕÑ' => 'æ¡»',
  'ÕÒ' => 'æ¢‘',
  'ÕÓ' => 'æ¢Œ',
  'ÕÔ' => 'æ¢Š',
  'ÕÕ' => 'æ¡½',
  'ÕÖ' => 'æ¬¶',
  'Õ×' => 'æ¬³',
  'ÕØ' => 'æ¬·',
  'ÕÙ' => 'æ¬¸',
  'ÕÚ' => 'æ®‘',
  'ÕÛ' => 'æ®�',
  'ÕÜ' => 'æ®�',
  'ÕÝ' => 'æ®Ž',
  'ÕÞ' => 'æ®Œ',
  'Õß' => 'æ°ª',
  'Õà' => 'æ·€',
  'Õá' => 'æ¶«',
  'Õâ' => 'æ¶´',
  'Õã' => 'æ¶³',
  'Õä' => 'æ¹´',
  'Õå' => 'æ¶¬',
  'Õæ' => 'æ·©',
  'Õç' => 'æ·¢',
  'Õè' => 'æ¶·',
  'Õé' => 'æ·¶',
  'Õê' => 'æ·”',
  'Õë' => 'æ¸€',
  'Õì' => 'æ·ˆ',
  'Õí' => 'æ· ',
  'Õî' => 'æ·Ÿ',
  'Õï' => 'æ·–',
  'Õð' => 'æ¶¾',
  'Õñ' => 'æ·¥',
  'Õò' => 'æ·œ',
  'Õó' => 'æ·�',
  'Õô' => 'æ·›',
  'Õõ' => 'æ·´',
  'Õö' => 'æ·Š',
  'Õ÷' => 'æ¶½',
  'Õø' => 'æ·­',
  'Õù' => 'æ·°',
  'Õú' => 'æ¶º',
  'Õû' => 'æ·•',
  'Õü' => 'æ·‚',
  'Õý' => 'æ·�',
  'Õþ' => 'æ·‰',
  'Ö@' => 'æ·�',
  'ÖA' => 'æ·²',
  'ÖB' => 'æ·“',
  'ÖC' => 'æ·½',
  'ÖD' => 'æ·—',
  'ÖE' => 'æ·�',
  'ÖF' => 'æ·£',
  'ÖG' => 'æ¶»',
  'ÖH' => 'çƒº',
  'ÖI' => 'ç„�',
  'ÖJ' => 'çƒ·',
  'ÖK' => 'ç„—',
  'ÖL' => 'çƒ´',
  'ÖM' => 'ç„Œ',
  'ÖN' => 'çƒ°',
  'ÖO' => 'ç„„',
  'ÖP' => 'çƒ³',
  'ÖQ' => 'ç„�',
  'ÖR' => 'çƒ¼',
  'ÖS' => 'çƒ¿',
  'ÖT' => 'ç„†',
  'ÖU' => 'ç„“',
  'ÖV' => 'ç„€',
  'ÖW' => 'çƒ¸',
  'ÖX' => 'çƒ¶',
  'ÖY' => 'ç„‹',
  'ÖZ' => 'ç„‚',
  'Ö[' => 'ç„Ž',
  'Ö\\' => 'ç‰¾',
  'Ö]' => 'ç‰»',
  'Ö^' => 'ç‰¼',
  'Ö_' => 'ç‰¿',
  'Ö`' => 'çŒ�',
  'Öa' => 'çŒ—',
  'Öb' => 'çŒ‡',
  'Öc' => 'çŒ‘',
  'Öd' => 'çŒ˜',
  'Öe' => 'çŒŠ',
  'Öf' => 'çŒˆ',
  'Ög' => 'ç‹¿',
  'Öh' => 'çŒ�',
  'Öi' => 'çŒž',
  'Öj' => 'çŽˆ',
  'Ök' => 'ç�¶',
  'Öl' => 'ç�¸',
  'Öm' => 'ç�µ',
  'Ön' => 'ç�„',
  'Öo' => 'ç��',
  'Öp' => 'ç�½',
  'Öq' => 'ç�‡',
  'Ör' => 'ç�€',
  'Ös' => 'ç�º',
  'Öt' => 'ç�¼',
  'Öu' => 'ç�¿',
  'Öv' => 'ç�Œ',
  'Öw' => 'ç�‹',
  'Öx' => 'ç�´',
  'Öy' => 'ç�ˆ',
  'Öz' => 'ç•¤',
  'Ö{' => 'ç•£',
  'Ö|' => 'ç—Ž',
  'Ö}' => 'ç—’',
  'Ö~' => 'ç—�',
  'Ö¡' => 'ç—‹',
  'Ö¢' => 'ç—Œ',
  'Ö£' => 'ç—‘',
  'Ö¤' => 'ç—�',
  'Ö¥' => 'çš�',
  'Ö¦' => 'çš‰',
  'Ö§' => 'ç›“',
  'Ö¨' => 'çœ¹',
  'Ö©' => 'çœ¯',
  'Öª' => 'çœ­',
  'Ö«' => 'çœ±',
  'Ö¬' => 'çœ²',
  'Ö­' => 'çœ´',
  'Ö®' => 'çœ³',
  'Ö¯' => 'çœ½',
  'Ö°' => 'çœ¥',
  'Ö±' => 'çœ»',
  'Ö²' => 'çœµ',
  'Ö³' => 'ç¡ˆ',
  'Ö´' => 'ç¡’',
  'Öµ' => 'ç¡‰',
  'Ö¶' => 'ç¡�',
  'Ö·' => 'ç¡Š',
  'Ö¸' => 'ç¡Œ',
  'Ö¹' => 'ç ¦',
  'Öº' => 'ç¡…',
  'Ö»' => 'ç¡�',
  'Ö¼' => 'ç¥¤',
  'Ö½' => 'ç¥§',
  'Ö¾' => 'ç¥©',
  'Ö¿' => 'ç¥ª',
  'ÖÀ' => 'ç¥£',
  'ÖÁ' => 'ç¥«',
  'ÖÂ' => 'ç¥¡',
  'ÖÃ' => 'ç¦»',
  'ÖÄ' => 'ç§º',
  'ÖÅ' => 'ç§¸',
  'ÖÆ' => 'ç§¶',
  'ÖÇ' => 'ç§·',
  'ÖÈ' => 'çª�',
  'ÖÉ' => 'çª”',
  'ÖÊ' => 'çª�',
  'ÖË' => 'ç¬µ',
  'ÖÌ' => 'ç­‡',
  'ÖÍ' => 'ç¬´',
  'ÖÎ' => 'ç¬¥',
  'ÖÏ' => 'ç¬°',
  'ÖÐ' => 'ç¬¢',
  'ÖÑ' => 'ç¬¤',
  'ÖÒ' => 'ç¬³',
  'ÖÓ' => 'ç¬˜',
  'ÖÔ' => 'ç¬ª',
  'ÖÕ' => 'ç¬�',
  'ÖÖ' => 'ç¬±',
  'Ö×' => 'ç¬«',
  'ÖØ' => 'ç¬­',
  'ÖÙ' => 'ç¬¯',
  'ÖÚ' => 'ç¬²',
  'ÖÛ' => 'ç¬¸',
  'ÖÜ' => 'ç¬š',
  'ÖÝ' => 'ç¬£',
  'ÖÞ' => 'ç²”',
  'Öß' => 'ç²˜',
  'Öà' => 'ç²–',
  'Öá' => 'ç²£',
  'Öâ' => 'ç´µ',
  'Öã' => 'ç´½',
  'Öä' => 'ç´¸',
  'Öå' => 'ç´¶',
  'Öæ' => 'ç´º',
  'Öç' => 'çµ…',
  'Öè' => 'ç´¬',
  'Öé' => 'ç´©',
  'Öê' => 'çµ�',
  'Öë' => 'çµ‡',
  'Öì' => 'ç´¾',
  'Öí' => 'ç´¿',
  'Öî' => 'çµŠ',
  'Öï' => 'ç´»',
  'Öð' => 'ç´¨',
  'Öñ' => 'ç½£',
  'Öò' => 'ç¾•',
  'Öó' => 'ç¾œ',
  'Öô' => 'ç¾�',
  'Öõ' => 'ç¾›',
  'Öö' => 'ç¿Š',
  'Ö÷' => 'ç¿‹',
  'Öø' => 'ç¿�',
  'Öù' => 'ç¿�',
  'Öú' => 'ç¿‘',
  'Öû' => 'ç¿‡',
  'Öü' => 'ç¿�',
  'Öý' => 'ç¿‰',
  'Öþ' => 'è€Ÿ',
  '×@' => 'è€ž',
  '×A' => 'è€›',
  '×B' => 'è�‡',
  '×C' => 'è�ƒ',
  '×D' => 'è�ˆ',
  '×E' => 'è„˜',
  '×F' => 'è„¥',
  '×G' => 'è„™',
  '×H' => 'è„›',
  '×I' => 'è„­',
  '×J' => 'è„Ÿ',
  '×K' => 'è„¬',
  '×L' => 'è„ž',
  '×M' => 'è„¡',
  '×N' => 'è„•',
  '×O' => 'è„§',
  '×P' => 'è„�',
  '×Q' => 'è„¢',
  '×R' => 'èˆ‘',
  '×S' => 'èˆ¸',
  '×T' => 'èˆ³',
  '×U' => 'èˆº',
  '×V' => 'èˆ´',
  '×W' => 'èˆ²',
  '×X' => 'è‰´',
  '×Y' => 'èŽ�',
  '×Z' => 'èŽ£',
  '×[' => 'èŽ¨',
  '×\\' => 'èŽ�',
  '×]' => 'è�º',
  '×^' => 'è�³',
  '×_' => 'èŽ¤',
  '×`' => 'è�´',
  '×a' => 'èŽ�',
  '×b' => 'èŽ�',
  '×c' => 'èŽ•',
  '×d' => 'èŽ™',
  '×e' => 'è�µ',
  '×f' => 'èŽ”',
  '×g' => 'èŽ©',
  '×h' => 'è�½',
  '×i' => 'èŽƒ',
  '×j' => 'èŽŒ',
  '×k' => 'èŽ�',
  '×l' => 'èŽ›',
  '×m' => 'èŽª',
  '×n' => 'èŽ‹',
  '×o' => 'è�¾',
  '×p' => 'èŽ¥',
  '×q' => 'èŽ¯',
  '×r' => 'èŽˆ',
  '×s' => 'èŽ—',
  '×t' => 'èŽ°',
  '×u' => 'è�¿',
  '×v' => 'èŽ¦',
  '×w' => 'èŽ‡',
  '×x' => 'èŽ®',
  '×y' => 'è�¶',
  '×z' => 'èŽš',
  '×{' => 'è™™',
  '×|' => 'è™–',
  '×}' => 'èš¿',
  '×~' => 'èš·',
  '×¡' => 'è›‚',
  '×¢' => 'è›�',
  '×£' => 'è›…',
  '×¤' => 'èšº',
  '×¥' => 'èš°',
  '×¦' => 'è›ˆ',
  '×§' => 'èš¹',
  '×¨' => 'èš³',
  '×©' => 'èš¸',
  '×ª' => 'è›Œ',
  '×«' => 'èš´',
  '×¬' => 'èš»',
  '×­' => 'èš¼',
  '×®' => 'è›ƒ',
  '×¯' => 'èš½',
  '×°' => 'èš¾',
  '×±' => 'è¡’',
  '×²' => 'è¢‰',
  '×³' => 'è¢•',
  '×´' => 'è¢¨',
  '×µ' => 'è¢¢',
  '×¶' => 'è¢ª',
  '×·' => 'è¢š',
  '×¸' => 'è¢‘',
  '×¹' => 'è¢¡',
  '×º' => 'è¢Ÿ',
  '×»' => 'è¢˜',
  '×¼' => 'è¢§',
  '×½' => 'è¢™',
  '×¾' => 'è¢›',
  '×¿' => 'è¢—',
  '×À' => 'è¢¤',
  '×Á' => 'è¢¬',
  '×Â' => 'è¢Œ',
  '×Ã' => 'è¢“',
  '×Ä' => 'è¢Ž',
  '×Å' => 'è¦‚',
  '×Æ' => 'è§–',
  '×Ç' => 'è§™',
  '×È' => 'è§•',
  '×É' => 'è¨°',
  '×Ê' => 'è¨§',
  '×Ë' => 'è¨¬',
  '×Ì' => 'è¨ž',
  '×Í' => 'è°¹',
  '×Î' => 'è°»',
  '×Ï' => 'è±œ',
  '×Ð' => 'è±�',
  '×Ñ' => 'è±½',
  '×Ò' => 'è²¥',
  '×Ó' => 'èµ½',
  '×Ô' => 'èµ»',
  '×Õ' => 'èµ¹',
  '×Ö' => 'è¶¼',
  '××' => 'è·‚',
  '×Ø' => 'è¶¹',
  '×Ù' => 'è¶¿',
  '×Ú' => 'è·�',
  '×Û' => 'è»˜',
  '×Ü' => 'è»ž',
  '×Ý' => 'è»�',
  '×Þ' => 'è»œ',
  '×ß' => 'è»—',
  '×à' => 'è» ',
  '×á' => 'è»¡',
  '×â' => 'é€¤',
  '×ã' => 'é€‹',
  '×ä' => 'é€‘',
  '×å' => 'é€œ',
  '×æ' => 'é€Œ',
  '×ç' => 'é€¡',
  '×è' => 'éƒ¯',
  '×é' => 'éƒª',
  '×ê' => 'éƒ°',
  '×ë' => 'éƒ´',
  '×ì' => 'éƒ²',
  '×í' => 'éƒ³',
  '×î' => 'éƒ”',
  '×ï' => 'éƒ«',
  '×ð' => 'éƒ¬',
  '×ñ' => 'éƒ©',
  '×ò' => 'é…–',
  '×ó' => 'é…˜',
  '×ô' => 'é…š',
  '×õ' => 'é…“',
  '×ö' => 'é…•',
  '×÷' => 'é‡¬',
  '×ø' => 'é‡´',
  '×ù' => 'é‡±',
  '×ú' => 'é‡³',
  '×û' => 'é‡¸',
  '×ü' => 'é‡¤',
  '×ý' => 'é‡¹',
  '×þ' => 'é‡ª',
  'Ø@' => 'é‡«',
  'ØA' => 'é‡·',
  'ØB' => 'é‡¨',
  'ØC' => 'é‡®',
  'ØD' => 'é•º',
  'ØE' => 'é–†',
  'ØF' => 'é–ˆ',
  'ØG' => 'é™¼',
  'ØH' => 'é™­',
  'ØI' => 'é™«',
  'ØJ' => 'é™±',
  'ØK' => 'é™¯',
  'ØL' => 'éš¿',
  'ØM' => 'é�ª',
  'ØN' => 'é „',
  'ØO' => 'é£¥',
  'ØP' => 'é¦—',
  'ØQ' => 'å‚›',
  'ØR' => 'å‚•',
  'ØS' => 'å‚”',
  'ØT' => 'å‚ž',
  'ØU' => 'å‚‹',
  'ØV' => 'å‚£',
  'ØW' => 'å‚ƒ',
  'ØX' => 'å‚Œ',
  'ØY' => 'å‚Ž',
  'ØZ' => 'å‚�',
  'Ø[' => 'å�¨',
  'Ø\\' => 'å‚œ',
  'Ø]' => 'å‚’',
  'Ø^' => 'å‚‚',
  'Ø_' => 'å‚‡',
  'Ø`' => 'å…Ÿ',
  'Øa' => 'å‡”',
  'Øb' => 'åŒ’',
  'Øc' => 'åŒ‘',
  'Ød' => 'åŽ¤',
  'Øe' => 'åŽ§',
  'Øf' => 'å–‘',
  'Øg' => 'å–¨',
  'Øh' => 'å–¥',
  'Øi' => 'å–­',
  'Øj' => 'å•·',
  'Øk' => 'å™…',
  'Øl' => 'å–¢',
  'Øm' => 'å–“',
  'Øn' => 'å–ˆ',
  'Øo' => 'å–�',
  'Øp' => 'å–µ',
  'Øq' => 'å–�',
  'Ør' => 'å–£',
  'Øs' => 'å–’',
  'Øt' => 'å–¤',
  'Øu' => 'å•½',
  'Øv' => 'å–Œ',
  'Øw' => 'å–¦',
  'Øx' => 'å•¿',
  'Øy' => 'å–•',
  'Øz' => 'å–¡',
  'Ø{' => 'å–Ž',
  'Ø|' => 'åœŒ',
  'Ø}' => 'å ©',
  'Ø~' => 'å ·',
  'Ø¡' => 'å ™',
  'Ø¢' => 'å ž',
  'Ø£' => 'å §',
  'Ø¤' => 'å £',
  'Ø¥' => 'å ¨',
  'Ø¦' => 'åŸµ',
  'Ø§' => 'å¡ˆ',
  'Ø¨' => 'å ¥',
  'Ø©' => 'å œ',
  'Øª' => 'å ›',
  'Ø«' => 'å ³',
  'Ø¬' => 'å ¿',
  'Ø­' => 'å ¶',
  'Ø®' => 'å ®',
  'Ø¯' => 'å ¹',
  'Ø°' => 'å ¸',
  'Ø±' => 'å ­',
  'Ø²' => 'å ¬',
  'Ø³' => 'å »',
  'Ø´' => 'å¥¡',
  'Øµ' => 'åª¯',
  'Ø¶' => 'åª”',
  'Ø·' => 'åªŸ',
  'Ø¸' => 'å©º',
  'Ø¹' => 'åª¢',
  'Øº' => 'åªž',
  'Ø»' => 'å©¸',
  'Ø¼' => 'åª¦',
  'Ø½' => 'å©¼',
  'Ø¾' => 'åª¥',
  'Ø¿' => 'åª¬',
  'ØÀ' => 'åª•',
  'ØÁ' => 'åª®',
  'ØÂ' => 'å¨·',
  'ØÃ' => 'åª„',
  'ØÄ' => 'åªŠ',
  'ØÅ' => 'åª—',
  'ØÆ' => 'åªƒ',
  'ØÇ' => 'åª‹',
  'ØÈ' => 'åª©',
  'ØÉ' => 'å©»',
  'ØÊ' => 'å©½',
  'ØË' => 'åªŒ',
  'ØÌ' => 'åªœ',
  'ØÍ' => 'åª�',
  'ØÎ' => 'åª“',
  'ØÏ' => 'åª�',
  'ØÐ' => 'å¯ª',
  'ØÑ' => 'å¯�',
  'ØÒ' => 'å¯‹',
  'ØÓ' => 'å¯”',
  'ØÔ' => 'å¯‘',
  'ØÕ' => 'å¯Š',
  'ØÖ' => 'å¯Ž',
  'Ø×' => 'å°Œ',
  'ØØ' => 'å°°',
  'ØÙ' => 'å´·',
  'ØÚ' => 'åµƒ',
  'ØÛ' => 'åµ«',
  'ØÜ' => 'åµ�',
  'ØÝ' => 'åµ‹',
  'ØÞ' => 'å´¿',
  'Øß' => 'å´µ',
  'Øà' => 'åµ‘',
  'Øá' => 'åµŽ',
  'Øâ' => 'åµ•',
  'Øã' => 'å´³',
  'Øä' => 'å´º',
  'Øå' => 'åµ’',
  'Øæ' => 'å´½',
  'Øç' => 'å´±',
  'Øè' => 'åµ™',
  'Øé' => 'åµ‚',
  'Øê' => 'å´¹',
  'Øë' => 'åµ‰',
  'Øì' => 'å´¸',
  'Øí' => 'å´¼',
  'Øî' => 'å´²',
  'Øï' => 'å´¶',
  'Øð' => 'åµ€',
  'Øñ' => 'åµ…',
  'Øò' => 'å¹„',
  'Øó' => 'å¹�',
  'Øô' => 'å½˜',
  'Øõ' => 'å¾¦',
  'Øö' => 'å¾¥',
  'Ø÷' => 'å¾«',
  'Øø' => 'æƒ‰',
  'Øù' => 'æ‚¹',
  'Øú' => 'æƒŒ',
  'Øû' => 'æƒ¢',
  'Øü' => 'æƒŽ',
  'Øý' => 'æƒ„',
  'Øþ' => 'æ„”',
  'Ù@' => 'æƒ²',
  'ÙA' => 'æ„Š',
  'ÙB' => 'æ„–',
  'ÙC' => 'æ„…',
  'ÙD' => 'æƒµ',
  'ÙE' => 'æ„“',
  'ÙF' => 'æƒ¸',
  'ÙG' => 'æƒ¼',
  'ÙH' => 'æƒ¾',
  'ÙI' => 'æƒ�',
  'ÙJ' => 'æ„ƒ',
  'ÙK' => 'æ„˜',
  'ÙL' => 'æ„�',
  'ÙM' => 'æ„�',
  'ÙN' => 'æƒ¿',
  'ÙO' => 'æ„„',
  'ÙP' => 'æ„‹',
  'ÙQ' => 'æ‰Š',
  'ÙR' => 'æŽ”',
  'ÙS' => 'æŽ±',
  'ÙT' => 'æŽ°',
  'ÙU' => 'æ�Ž',
  'ÙV' => 'æ�¥',
  'ÙW' => 'æ�¨',
  'ÙX' => 'æ�¯',
  'ÙY' => 'æ�ƒ',
  'ÙZ' => 'æ’�',
  'Ù[' => 'æ�³',
  'Ù\\' => 'æ�Š',
  'Ù]' => 'æ� ',
  'Ù^' => 'æ�¶',
  'Ù_' => 'æ�•',
  'Ù`' => 'æ�²',
  'Ùa' => 'æ�µ',
  'Ùb' => 'æ‘¡',
  'Ùc' => 'æ�Ÿ',
  'Ùd' => 'æŽ¾',
  'Ùe' => 'æ��',
  'Ùf' => 'æ�œ',
  'Ùg' => 'æ�„',
  'Ùh' => 'æ�˜',
  'Ùi' => 'æ�“',
  'Ùj' => 'æ�‚',
  'Ùk' => 'æ�‡',
  'Ùl' => 'æ�Œ',
  'Ùm' => 'æ�‹',
  'Ùn' => 'æ�ˆ',
  'Ùo' => 'æ�°',
  'Ùp' => 'æ�—',
  'Ùq' => 'æ�™',
  'Ùr' => 'æ”²',
  'Ùs' => 'æ•§',
  'Ùt' => 'æ•ª',
  'Ùu' => 'æ•¤',
  'Ùv' => 'æ•œ',
  'Ùw' => 'æ•¨',
  'Ùx' => 'æ•¥',
  'Ùy' => 'æ–Œ',
  'Ùz' => 'æ–�',
  'Ù{' => 'æ–ž',
  'Ù|' => 'æ–®',
  'Ù}' => 'æ—�',
  'Ù~' => 'æ—’',
  'Ù¡' => 'æ™¼',
  'Ù¢' => 'æ™¬',
  'Ù£' => 'æ™»',
  'Ù¤' => 'æš€',
  'Ù¥' => 'æ™±',
  'Ù¦' => 'æ™¹',
  'Ù§' => 'æ™ª',
  'Ù¨' => 'æ™²',
  'Ù©' => 'æœ�',
  'Ùª' => 'æ¤Œ',
  'Ù«' => 'æ£“',
  'Ù¬' => 'æ¤„',
  'Ù­' => 'æ£œ',
  'Ù®' => 'æ¤ª',
  'Ù¯' => 'æ£¬',
  'Ù°' => 'æ£ª',
  'Ù±' => 'æ£±',
  'Ù²' => 'æ¤�',
  'Ù³' => 'æ£–',
  'Ù´' => 'æ£·',
  'Ùµ' => 'æ£«',
  'Ù¶' => 'æ£¤',
  'Ù·' => 'æ£¶',
  'Ù¸' => 'æ¤“',
  'Ù¹' => 'æ¤�',
  'Ùº' => 'æ£³',
  'Ù»' => 'æ£¡',
  'Ù¼' => 'æ¤‡',
  'Ù½' => 'æ£Œ',
  'Ù¾' => 'æ¤ˆ',
  'Ù¿' => 'æ¥°',
  'ÙÀ' => 'æ¢´',
  'ÙÁ' => 'æ¤‘',
  'ÙÂ' => 'æ£¯',
  'ÙÃ' => 'æ£†',
  'ÙÄ' => 'æ¤”',
  'ÙÅ' => 'æ£¸',
  'ÙÆ' => 'æ£�',
  'ÙÇ' => 'æ£½',
  'ÙÈ' => 'æ£¼',
  'ÙÉ' => 'æ£¨',
  'ÙÊ' => 'æ¤‹',
  'ÙË' => 'æ¤Š',
  'ÙÌ' => 'æ¤—',
  'ÙÍ' => 'æ£Ž',
  'ÙÎ' => 'æ£ˆ',
  'ÙÏ' => 'æ£�',
  'ÙÐ' => 'æ£ž',
  'ÙÑ' => 'æ£¦',
  'ÙÒ' => 'æ£´',
  'ÙÓ' => 'æ£‘',
  'ÙÔ' => 'æ¤†',
  'ÙÕ' => 'æ£”',
  'ÙÖ' => 'æ£©',
  'Ù×' => 'æ¤•',
  'ÙØ' => 'æ¤¥',
  'ÙÙ' => 'æ£‡',
  'ÙÚ' => 'æ¬¹',
  'ÙÛ' => 'æ¬»',
  'ÙÜ' => 'æ¬¿',
  'ÙÝ' => 'æ¬¼',
  'ÙÞ' => 'æ®”',
  'Ùß' => 'æ®—',
  'Ùà' => 'æ®™',
  'Ùá' => 'æ®•',
  'Ùâ' => 'æ®½',
  'Ùã' => 'æ¯°',
  'Ùä' => 'æ¯²',
  'Ùå' => 'æ¯³',
  'Ùæ' => 'æ°°',
  'Ùç' => 'æ·¼',
  'Ùè' => 'æ¹†',
  'Ùé' => 'æ¹‡',
  'Ùê' => 'æ¸Ÿ',
  'Ùë' => 'æ¹‰',
  'Ùì' => 'æºˆ',
  'Ùí' => 'æ¸¼',
  'Ùî' => 'æ¸½',
  'Ùï' => 'æ¹…',
  'Ùð' => 'æ¹¢',
  'Ùñ' => 'æ¸«',
  'Ùò' => 'æ¸¿',
  'Ùó' => 'æ¹�',
  'Ùô' => 'æ¹�',
  'Ùõ' => 'æ¹³',
  'Ùö' => 'æ¸œ',
  'Ù÷' => 'æ¸³',
  'Ùø' => 'æ¹‹',
  'Ùù' => 'æ¹€',
  'Ùú' => 'æ¹‘',
  'Ùû' => 'æ¸»',
  'Ùü' => 'æ¸ƒ',
  'Ùý' => 'æ¸®',
  'Ùþ' => 'æ¹ž',
  'Ú@' => 'æ¹¨',
  'ÚA' => 'æ¹œ',
  'ÚB' => 'æ¹¡',
  'ÚC' => 'æ¸±',
  'ÚD' => 'æ¸¨',
  'ÚE' => 'æ¹ ',
  'ÚF' => 'æ¹±',
  'ÚG' => 'æ¹«',
  'ÚH' => 'æ¸¹',
  'ÚI' => 'æ¸¢',
  'ÚJ' => 'æ¸°',
  'ÚK' => 'æ¹“',
  'ÚL' => 'æ¹¥',
  'ÚM' => 'æ¸§',
  'ÚN' => 'æ¹¸',
  'ÚO' => 'æ¹¤',
  'ÚP' => 'æ¹·',
  'ÚQ' => 'æ¹•',
  'ÚR' => 'æ¹¹',
  'ÚS' => 'æ¹’',
  'ÚT' => 'æ¹¦',
  'ÚU' => 'æ¸µ',
  'ÚV' => 'æ¸¶',
  'ÚW' => 'æ¹š',
  'ÚX' => 'ç„ ',
  'ÚY' => 'ç„ž',
  'ÚZ' => 'ç„¯',
  'Ú[' => 'çƒ»',
  'Ú\\' => 'ç„®',
  'Ú]' => 'ç„±',
  'Ú^' => 'ç„£',
  'Ú_' => 'ç„¥',
  'Ú`' => 'ç„¢',
  'Úa' => 'ç„²',
  'Úb' => 'ç„Ÿ',
  'Úc' => 'ç„¨',
  'Úd' => 'ç„º',
  'Úe' => 'ç„›',
  'Úf' => 'ç‰‹',
  'Úg' => 'ç‰š',
  'Úh' => 'çŠˆ',
  'Úi' => 'çŠ‰',
  'Új' => 'çŠ†',
  'Úk' => 'çŠ…',
  'Úl' => 'çŠ‹',
  'Úm' => 'çŒ’',
  'Ún' => 'çŒ‹',
  'Úo' => 'çŒ°',
  'Úp' => 'çŒ¢',
  'Úq' => 'çŒ±',
  'Úr' => 'çŒ³',
  'Ús' => 'çŒ§',
  'Út' => 'çŒ²',
  'Úu' => 'çŒ­',
  'Úv' => 'çŒ¦',
  'Úw' => 'çŒ£',
  'Úx' => 'çŒµ',
  'Úy' => 'çŒŒ',
  'Úz' => 'ç�®',
  'Ú{' => 'ç�¬',
  'Ú|' => 'ç�°',
  'Ú}' => 'ç�«',
  'Ú~' => 'ç�–',
  'Ú¡' => 'ç�š',
  'Ú¢' => 'ç�¡',
  'Ú£' => 'ç�­',
  'Ú¤' => 'ç�±',
  'Ú¥' => 'ç�¤',
  'Ú¦' => 'ç�£',
  'Ú§' => 'ç��',
  'Ú¨' => 'ç�©',
  'Ú©' => 'ç� ',
  'Úª' => 'ç�²',
  'Ú«' => 'ç“»',
  'Ú¬' => 'ç”¯',
  'Ú­' => 'ç•¯',
  'Ú®' => 'ç•¬',
  'Ú¯' => 'ç—§',
  'Ú°' => 'ç—š',
  'Ú±' => 'ç—¡',
  'Ú²' => 'ç—¦',
  'Ú³' => 'ç—�',
  'Ú´' => 'ç—Ÿ',
  'Úµ' => 'ç—¤',
  'Ú¶' => 'ç——',
  'Ú·' => 'çš•',
  'Ú¸' => 'çš’',
  'Ú¹' => 'ç›š',
  'Úº' => 'ç�†',
  'Ú»' => 'ç�‡',
  'Ú¼' => 'ç�„',
  'Ú½' => 'ç��',
  'Ú¾' => 'ç�…',
  'Ú¿' => 'ç�Š',
  'ÚÀ' => 'ç�Ž',
  'ÚÁ' => 'ç�‹',
  'ÚÂ' => 'ç�Œ',
  'ÚÃ' => 'çŸž',
  'ÚÄ' => 'çŸ¬',
  'ÚÅ' => 'ç¡ ',
  'ÚÆ' => 'ç¡¤',
  'ÚÇ' => 'ç¡¥',
  'ÚÈ' => 'ç¡œ',
  'ÚÉ' => 'ç¡­',
  'ÚÊ' => 'ç¡±',
  'ÚË' => 'ç¡ª',
  'ÚÌ' => 'ç¡®',
  'ÚÍ' => 'ç¡°',
  'ÚÎ' => 'ç¡©',
  'ÚÏ' => 'ç¡¨',
  'ÚÐ' => 'ç¡ž',
  'ÚÑ' => 'ç¡¢',
  'ÚÒ' => 'ç¥´',
  'ÚÓ' => 'ç¥³',
  'ÚÔ' => 'ç¥²',
  'ÚÕ' => 'ç¥°',
  'ÚÖ' => 'ç¨‚',
  'Ú×' => 'ç¨Š',
  'ÚØ' => 'ç¨ƒ',
  'ÚÙ' => 'ç¨Œ',
  'ÚÚ' => 'ç¨„',
  'ÚÛ' => 'çª™',
  'ÚÜ' => 'ç«¦',
  'ÚÝ' => 'ç«¤',
  'ÚÞ' => 'ç­Š',
  'Úß' => 'ç¬»',
  'Úà' => 'ç­„',
  'Úá' => 'ç­ˆ',
  'Úâ' => 'ç­Œ',
  'Úã' => 'ç­Ž',
  'Úä' => 'ç­€',
  'Úå' => 'ç­˜',
  'Úæ' => 'ç­…',
  'Úç' => 'ç²¢',
  'Úè' => 'ç²ž',
  'Úé' => 'ç²¨',
  'Úê' => 'ç²¡',
  'Úë' => 'çµ˜',
  'Úì' => 'çµ¯',
  'Úí' => 'çµ£',
  'Úî' => 'çµ“',
  'Úï' => 'çµ–',
  'Úð' => 'çµ§',
  'Úñ' => 'çµª',
  'Úò' => 'çµ�',
  'Úó' => 'çµ­',
  'Úô' => 'çµœ',
  'Úõ' => 'çµ«',
  'Úö' => 'çµ’',
  'Ú÷' => 'çµ”',
  'Úø' => 'çµ©',
  'Úù' => 'çµ‘',
  'Úú' => 'çµŸ',
  'Úû' => 'çµŽ',
  'Úü' => 'ç¼¾',
  'Úý' => 'ç¼¿',
  'Úþ' => 'ç½¥',
  'Û@' => 'ç½¦',
  'ÛA' => 'ç¾¢',
  'ÛB' => 'ç¾ ',
  'ÛC' => 'ç¾¡',
  'ÛD' => 'ç¿—',
  'ÛE' => 'è�‘',
  'ÛF' => 'è��',
  'ÛG' => 'è��',
  'ÛH' => 'èƒ¾',
  'ÛI' => 'èƒ”',
  'ÛJ' => 'è…ƒ',
  'ÛK' => 'è…Š',
  'ÛL' => 'è…’',
  'ÛM' => 'è…�',
  'ÛN' => 'è…‡',
  'ÛO' => 'è„½',
  'ÛP' => 'è…�',
  'ÛQ' => 'è„º',
  'ÛR' => 'è‡¦',
  'ÛS' => 'è‡®',
  'ÛT' => 'è‡·',
  'ÛU' => 'è‡¸',
  'ÛV' => 'è‡¹',
  'ÛW' => 'èˆ„',
  'ÛX' => 'èˆ¼',
  'ÛY' => 'èˆ½',
  'ÛZ' => 'èˆ¿',
  'Û[' => 'è‰µ',
  'Û\\' => 'èŒ»',
  'Û]' => 'è��',
  'Û^' => 'è�¹',
  'Û_' => 'è�£',
  'Û`' => 'è�€',
  'Ûa' => 'è�¨',
  'Ûb' => 'è�’',
  'Ûc' => 'è�§',
  'Ûd' => 'è�¤',
  'Ûe' => 'è�¼',
  'Ûf' => 'è�¶',
  'Ûg' => 'è��',
  'Ûh' => 'è�†',
  'Ûi' => 'è�ˆ',
  'Ûj' => 'è�«',
  'Ûk' => 'è�£',
  'Ûl' => 'èŽ¿',
  'Ûm' => 'è��',
  'Ûn' => 'è��',
  'Ûo' => 'è�¥',
  'Ûp' => 'è�˜',
  'Ûq' => 'è�¿',
  'Ûr' => 'è�¡',
  'Ûs' => 'è�‹',
  'Ût' => 'è�Ž',
  'Ûu' => 'è�–',
  'Ûv' => 'è�µ',
  'Ûw' => 'è�‰',
  'Ûx' => 'è�‰',
  'Ûy' => 'è��',
  'Ûz' => 'è�ž',
  'Û{' => 'è�‘',
  'Û|' => 'è�†',
  'Û}' => 'è�‚',
  'Û~' => 'è�³',
  'Û¡' => 'è�•',
  'Û¢' => 'è�º',
  'Û£' => 'è�‡',
  'Û¤' => 'è�‘',
  'Û¥' => 'è�ª',
  'Û¦' => 'è�“',
  'Û§' => 'è�ƒ',
  'Û¨' => 'è�¬',
  'Û©' => 'è�®',
  'Ûª' => 'è�„',
  'Û«' => 'è�»',
  'Û¬' => 'è�—',
  'Û­' => 'è�¢',
  'Û®' => 'è�›',
  'Û¯' => 'è�›',
  'Û°' => 'è�¾',
  'Û±' => 'è›˜',
  'Û²' => 'è›¢',
  'Û³' => 'è›¦',
  'Û´' => 'è›“',
  'Ûµ' => 'è›£',
  'Û¶' => 'è›š',
  'Û·' => 'è›ª',
  'Û¸' => 'è›�',
  'Û¹' => 'è›«',
  'Ûº' => 'è›œ',
  'Û»' => 'è›¬',
  'Û¼' => 'è›©',
  'Û½' => 'è›—',
  'Û¾' => 'è›¨',
  'Û¿' => 'è›‘',
  'ÛÀ' => 'è¡ˆ',
  'ÛÁ' => 'è¡–',
  'ÛÂ' => 'è¡•',
  'ÛÃ' => 'è¢º',
  'ÛÄ' => 'è£—',
  'ÛÅ' => 'è¢¹',
  'ÛÆ' => 'è¢¸',
  'ÛÇ' => 'è£€',
  'ÛÈ' => 'è¢¾',
  'ÛÉ' => 'è¢¶',
  'ÛÊ' => 'è¢¼',
  'ÛË' => 'è¢·',
  'ÛÌ' => 'è¢½',
  'ÛÍ' => 'è¢²',
  'ÛÎ' => 'è¤�',
  'ÛÏ' => 'è£‰',
  'ÛÐ' => 'è¦•',
  'ÛÑ' => 'è¦˜',
  'ÛÒ' => 'è¦—',
  'ÛÓ' => 'è§�',
  'ÛÔ' => 'è§š',
  'ÛÕ' => 'è§›',
  'ÛÖ' => 'è©Ž',
  'Û×' => 'è©�',
  'ÛØ' => 'è¨¹',
  'ÛÙ' => 'è©™',
  'ÛÚ' => 'è©€',
  'ÛÛ' => 'è©—',
  'ÛÜ' => 'è©˜',
  'ÛÝ' => 'è©„',
  'ÛÞ' => 'è©…',
  'Ûß' => 'è©’',
  'Ûà' => 'è©ˆ',
  'Ûá' => 'è©‘',
  'Ûâ' => 'è©Š',
  'Ûã' => 'è©Œ',
  'Ûä' => 'è©�',
  'Ûå' => 'è±Ÿ',
  'Ûæ' => 'è²�',
  'Ûç' => 'è²€',
  'Ûè' => 'è²º',
  'Ûé' => 'è²¾',
  'Ûê' => 'è²°',
  'Ûë' => 'è²¹',
  'Ûì' => 'è²µ',
  'Ûí' => 'è¶„',
  'Ûî' => 'è¶€',
  'Ûï' => 'è¶‰',
  'Ûð' => 'è·˜',
  'Ûñ' => 'è·“',
  'Ûò' => 'è·�',
  'Ûó' => 'è·‡',
  'Ûô' => 'è·–',
  'Ûõ' => 'è·œ',
  'Ûö' => 'è·�',
  'Û÷' => 'è·•',
  'Ûø' => 'è·™',
  'Ûù' => 'è·ˆ',
  'Ûú' => 'è·—',
  'Ûû' => 'è·…',
  'Ûü' => 'è»¯',
  'Ûý' => 'è»·',
  'Ûþ' => 'è»º',
  'Ü@' => 'è»¹',
  'ÜA' => 'è»¦',
  'ÜB' => 'è»®',
  'ÜC' => 'è»¥',
  'ÜD' => 'è»µ',
  'ÜE' => 'è»§',
  'ÜF' => 'è»¨',
  'ÜG' => 'è»¶',
  'ÜH' => 'è»«',
  'ÜI' => 'è»±',
  'ÜJ' => 'è»¬',
  'ÜK' => 'è»´',
  'ÜL' => 'è»©',
  'ÜM' => 'é€­',
  'ÜN' => 'é€´',
  'ÜO' => 'é€¯',
  'ÜP' => 'é„†',
  'ÜQ' => 'é„¬',
  'ÜR' => 'é„„',
  'ÜS' => 'éƒ¿',
  'ÜT' => 'éƒ¼',
  'ÜU' => 'é„ˆ',
  'ÜV' => 'éƒ¹',
  'ÜW' => 'éƒ»',
  'ÜX' => 'é„�',
  'ÜY' => 'é„€',
  'ÜZ' => 'é„‡',
  'Ü[' => 'é„…',
  'Ü\\' => 'é„ƒ',
  'Ü]' => 'é…¡',
  'Ü^' => 'é…¤',
  'Ü_' => 'é…Ÿ',
  'Ü`' => 'é…¢',
  'Üa' => 'é… ',
  'Üb' => 'éˆ�',
  'Üc' => 'éˆŠ',
  'Üd' => 'éˆ¥',
  'Üe' => 'éˆƒ',
  'Üf' => 'éˆš',
  'Üg' => 'éˆ¦',
  'Üh' => 'éˆ�',
  'Üi' => 'éˆŒ',
  'Üj' => 'éˆ€',
  'Ük' => 'éˆ’',
  'Ül' => 'é‡¿',
  'Üm' => 'é‡½',
  'Ün' => 'éˆ†',
  'Üo' => 'éˆ„',
  'Üp' => 'éˆ§',
  'Üq' => 'éˆ‚',
  'Ür' => 'éˆœ',
  'Üs' => 'éˆ¤',
  'Üt' => 'éˆ™',
  'Üu' => 'éˆ—',
  'Üv' => 'éˆ…',
  'Üw' => 'éˆ–',
  'Üx' => 'é•»',
  'Üy' => 'é–�',
  'Üz' => 'é–Œ',
  'Ü{' => 'é–�',
  'Ü|' => 'éš‡',
  'Ü}' => 'é™¾',
  'Ü~' => 'éšˆ',
  'Ü¡' => 'éš‰',
  'Ü¢' => 'éšƒ',
  'Ü£' => 'éš€',
  'Ü¤' => 'é›‚',
  'Ü¥' => 'é›ˆ',
  'Ü¦' => 'é›ƒ',
  'Ü§' => 'é›±',
  'Ü¨' => 'é›°',
  'Ü©' => 'é�¬',
  'Üª' => 'é�°',
  'Ü«' => 'é�®',
  'Ü¬' => 'é ‡',
  'Ü­' => 'é¢©',
  'Ü®' => 'é£«',
  'Ü¯' => 'é³¦',
  'Ü°' => 'é»¹',
  'Ü±' => 'äºƒ',
  'Ü²' => 'äº„',
  'Ü³' => 'äº¶',
  'Ü´' => 'å‚½',
  'Üµ' => 'å‚¿',
  'Ü¶' => 'åƒ†',
  'Ü·' => 'å‚®',
  'Ü¸' => 'åƒ„',
  'Ü¹' => 'åƒŠ',
  'Üº' => 'å‚´',
  'Ü»' => 'åƒˆ',
  'Ü¼' => 'åƒ‚',
  'Ü½' => 'å‚°',
  'Ü¾' => 'åƒ�',
  'Ü¿' => 'å‚º',
  'ÜÀ' => 'å‚±',
  'ÜÁ' => 'åƒ‹',
  'ÜÂ' => 'åƒ‰',
  'ÜÃ' => 'å‚¶',
  'ÜÄ' => 'å‚¸',
  'ÜÅ' => 'å‡—',
  'ÜÆ' => 'å‰º',
  'ÜÇ' => 'å‰¸',
  'ÜÈ' => 'å‰»',
  'ÜÉ' => 'å‰¼',
  'ÜÊ' => 'å—ƒ',
  'ÜË' => 'å—›',
  'ÜÌ' => 'å—Œ',
  'ÜÍ' => 'å—�',
  'ÜÎ' => 'å—‹',
  'ÜÏ' => 'å—Š',
  'ÜÐ' => 'å—�',
  'ÜÑ' => 'å—€',
  'ÜÒ' => 'å—”',
  'ÜÓ' => 'å—„',
  'ÜÔ' => 'å—©',
  'ÜÕ' => 'å–¿',
  'ÜÖ' => 'å—’',
  'Ü×' => 'å–�',
  'ÜØ' => 'å—�',
  'ÜÙ' => 'å—•',
  'ÜÚ' => 'å—¢',
  'ÜÛ' => 'å—–',
  'ÜÜ' => 'å—ˆ',
  'ÜÝ' => 'å—²',
  'ÜÞ' => 'å—�',
  'Üß' => 'å—™',
  'Üà' => 'å—‚',
  'Üá' => 'åœ”',
  'Üâ' => 'å¡“',
  'Üã' => 'å¡¨',
  'Üä' => 'å¡¤',
  'Üå' => 'å¡�',
  'Üæ' => 'å¡�',
  'Üç' => 'å¡‰',
  'Üè' => 'å¡¯',
  'Üé' => 'å¡•',
  'Üê' => 'å¡Ž',
  'Üë' => 'å¡�',
  'Üì' => 'å¡™',
  'Üí' => 'å¡¥',
  'Üî' => 'å¡›',
  'Üï' => 'å ½',
  'Üð' => 'å¡£',
  'Üñ' => 'å¡±',
  'Üò' => 'å£¼',
  'Üó' => 'å«‡',
  'Üô' => 'å«„',
  'Üõ' => 'å«‹',
  'Üö' => 'åªº',
  'Ü÷' => 'åª¸',
  'Üø' => 'åª±',
  'Üù' => 'åªµ',
  'Üú' => 'åª°',
  'Üû' => 'åª¿',
  'Üü' => 'å«ˆ',
  'Üý' => 'åª»',
  'Üþ' => 'å«†',
  'Ý@' => 'åª·',
  'ÝA' => 'å«€',
  'ÝB' => 'å«Š',
  'ÝC' => 'åª´',
  'ÝD' => 'åª¶',
  'ÝE' => 'å«�',
  'ÝF' => 'åª¹',
  'ÝG' => 'åª�',
  'ÝH' => 'å¯–',
  'ÝI' => 'å¯˜',
  'ÝJ' => 'å¯™',
  'ÝK' => 'å°Ÿ',
  'ÝL' => 'å°³',
  'ÝM' => 'åµ±',
  'ÝN' => 'åµ£',
  'ÝO' => 'åµŠ',
  'ÝP' => 'åµ¥',
  'ÝQ' => 'åµ²',
  'ÝR' => 'åµ¬',
  'ÝS' => 'åµž',
  'ÝT' => 'åµ¨',
  'ÝU' => 'åµ§',
  'ÝV' => 'åµ¢',
  'ÝW' => 'å·°',
  'ÝX' => 'å¹�',
  'ÝY' => 'å¹Ž',
  'ÝZ' => 'å¹Š',
  'Ý[' => 'å¹�',
  'Ý\\' => 'å¹‹',
  'Ý]' => 'å»…',
  'Ý^' => 'å»Œ',
  'Ý_' => 'å»†',
  'Ý`' => 'å»‹',
  'Ýa' => 'å»‡',
  'Ýb' => 'å½€',
  'Ýc' => 'å¾¯',
  'Ýd' => 'å¾­',
  'Ýe' => 'æƒ·',
  'Ýf' => 'æ…‰',
  'Ýg' => 'æ…Š',
  'Ýh' => 'æ„«',
  'Ýi' => 'æ……',
  'Ýj' => 'æ„¶',
  'Ýk' => 'æ„²',
  'Ýl' => 'æ„®',
  'Ým' => 'æ…†',
  'Ýn' => 'æ„¯',
  'Ýo' => 'æ…�',
  'Ýp' => 'æ„©',
  'Ýq' => 'æ…€',
  'Ýr' => 'æˆ ',
  'Ýs' => 'é…¨',
  'Ýt' => 'æˆ£',
  'Ýu' => 'æˆ¥',
  'Ýv' => 'æˆ¤',
  'Ýw' => 'æ�…',
  'Ýx' => 'æ�±',
  'Ýy' => 'æ�«',
  'Ýz' => 'æ��',
  'Ý{' => 'æ�’',
  'Ý|' => 'æ�‰',
  'Ý}' => 'æ� ',
  'Ý~' => 'æ�¤',
  'Ý¡' => 'æ�³',
  'Ý¢' => 'æ‘ƒ',
  'Ý£' => 'æ�Ÿ',
  'Ý¤' => 'æ�•',
  'Ý¥' => 'æ�˜',
  'Ý¦' => 'æ�¹',
  'Ý§' => 'æ�·',
  'Ý¨' => 'æ�¢',
  'Ý©' => 'æ�£',
  'Ýª' => 'æ�Œ',
  'Ý«' => 'æ�¦',
  'Ý¬' => 'æ�°',
  'Ý­' => 'æ�¨',
  'Ý®' => 'æ‘�',
  'Ý¯' => 'æ�µ',
  'Ý°' => 'æ�¯',
  'Ý±' => 'æ�Š',
  'Ý²' => 'æ�š',
  'Ý³' => 'æ‘€',
  'Ý´' => 'æ�¥',
  'Ýµ' => 'æ�§',
  'Ý¶' => 'æ�‹',
  'Ý·' => 'æ�§',
  'Ý¸' => 'æ�›',
  'Ý¹' => 'æ�®',
  'Ýº' => 'æ�¡',
  'Ý»' => 'æ�Ž',
  'Ý¼' => 'æ•¯',
  'Ý½' => 'æ–’',
  'Ý¾' => 'æ—“',
  'Ý¿' => 'æš†',
  'ÝÀ' => 'æšŒ',
  'ÝÁ' => 'æš•',
  'ÝÂ' => 'æš�',
  'ÝÃ' => 'æš‹',
  'ÝÄ' => 'æšŠ',
  'ÝÅ' => 'æš™',
  'ÝÆ' => 'æš”',
  'ÝÇ' => 'æ™¸',
  'ÝÈ' => 'æœ ',
  'ÝÉ' => 'æ¥¦',
  'ÝÊ' => 'æ¥Ÿ',
  'ÝË' => 'æ¤¸',
  'ÝÌ' => 'æ¥Ž',
  'ÝÍ' => 'æ¥¢',
  'ÝÎ' => 'æ¥±',
  'ÝÏ' => 'æ¤¿',
  'ÝÐ' => 'æ¥…',
  'ÝÑ' => 'æ¥ª',
  'ÝÒ' => 'æ¤¹',
  'ÝÓ' => 'æ¥‚',
  'ÝÔ' => 'æ¥—',
  'ÝÕ' => 'æ¥™',
  'ÝÖ' => 'æ¥º',
  'Ý×' => 'æ¥ˆ',
  'ÝØ' => 'æ¥‰',
  'ÝÙ' => 'æ¤µ',
  'ÝÚ' => 'æ¥¬',
  'ÝÛ' => 'æ¤³',
  'ÝÜ' => 'æ¤½',
  'ÝÝ' => 'æ¥¥',
  'ÝÞ' => 'æ£°',
  'Ýß' => 'æ¥¸',
  'Ýà' => 'æ¤´',
  'Ýá' => 'æ¥©',
  'Ýâ' => 'æ¥€',
  'Ýã' => 'æ¥¯',
  'Ýä' => 'æ¥„',
  'Ýå' => 'æ¥¶',
  'Ýæ' => 'æ¥˜',
  'Ýç' => 'æ¥�',
  'Ýè' => 'æ¥´',
  'Ýé' => 'æ¥Œ',
  'Ýê' => 'æ¤»',
  'Ýë' => 'æ¥‹',
  'Ýì' => 'æ¤·',
  'Ýí' => 'æ¥œ',
  'Ýî' => 'æ¥�',
  'Ýï' => 'æ¥‘',
  'Ýð' => 'æ¤²',
  'Ýñ' => 'æ¥’',
  'Ýò' => 'æ¤¯',
  'Ýó' => 'æ¥»',
  'Ýô' => 'æ¤¼',
  'Ýõ' => 'æ­†',
  'Ýö' => 'æ­…',
  'Ý÷' => 'æ­ƒ',
  'Ýø' => 'æ­‚',
  'Ýù' => 'æ­ˆ',
  'Ýú' => 'æ­�',
  'Ýû' => 'æ®›',
  'Ýü' => 'ï¨�',
  'Ýý' => 'æ¯»',
  'Ýþ' => 'æ¯¼',
  'Þ@' => 'æ¯¹',
  'ÞA' => 'æ¯·',
  'ÞB' => 'æ¯¸',
  'ÞC' => 'æº›',
  'ÞD' => 'æ»–',
  'ÞE' => 'æ»ˆ',
  'ÞF' => 'æº�',
  'ÞG' => 'æ»€',
  'ÞH' => 'æºŸ',
  'ÞI' => 'æº“',
  'ÞJ' => 'æº”',
  'ÞK' => 'æº ',
  'ÞL' => 'æº±',
  'ÞM' => 'æº¹',
  'ÞN' => 'æ»†',
  'ÞO' => 'æ»’',
  'ÞP' => 'æº½',
  'ÞQ' => 'æ»�',
  'ÞR' => 'æºž',
  'ÞS' => 'æ»‰',
  'ÞT' => 'æº·',
  'ÞU' => 'æº°',
  'ÞV' => 'æ»�',
  'ÞW' => 'æº¦',
  'ÞX' => 'æ»�',
  'ÞY' => 'æº²',
  'ÞZ' => 'æº¾',
  'Þ[' => 'æ»ƒ',
  'Þ\\' => 'æ»œ',
  'Þ]' => 'æ»˜',
  'Þ^' => 'æº™',
  'Þ_' => 'æº’',
  'Þ`' => 'æºŽ',
  'Þa' => 'æº�',
  'Þb' => 'æº¤',
  'Þc' => 'æº¡',
  'Þd' => 'æº¿',
  'Þe' => 'æº³',
  'Þf' => 'æ»�',
  'Þg' => 'æ»Š',
  'Þh' => 'æº—',
  'Þi' => 'æº®',
  'Þj' => 'æº£',
  'Þk' => 'ç…‡',
  'Þl' => 'ç…”',
  'Þm' => 'ç…’',
  'Þn' => 'ç…£',
  'Þo' => 'ç… ',
  'Þp' => 'ç…�',
  'Þq' => 'ç…�',
  'Þr' => 'ç…¢',
  'Þs' => 'ç…²',
  'Þt' => 'ç…¸',
  'Þu' => 'ç…ª',
  'Þv' => 'ç…¡',
  'Þw' => 'ç…‚',
  'Þx' => 'ç…˜',
  'Þy' => 'ç…ƒ',
  'Þz' => 'ç…‹',
  'Þ{' => 'ç…°',
  'Þ|' => 'ç…Ÿ',
  'Þ}' => 'ç…�',
  'Þ~' => 'ç…“',
  'Þ¡' => 'ç…„',
  'Þ¢' => 'ç…�',
  'Þ£' => 'ç…š',
  'Þ¤' => 'ç‰�',
  'Þ¥' => 'çŠ�',
  'Þ¦' => 'çŠŒ',
  'Þ§' => 'çŠ‘',
  'Þ¨' => 'çŠ�',
  'Þ©' => 'çŠŽ',
  'Þª' => 'çŒ¼',
  'Þ«' => 'ç�‚',
  'Þ¬' => 'çŒ»',
  'Þ­' => 'çŒº',
  'Þ®' => 'ç�€',
  'Þ¯' => 'ç�Š',
  'Þ°' => 'ç�‰',
  'Þ±' => 'ç‘„',
  'Þ²' => 'ç‘Š',
  'Þ³' => 'ç‘‹',
  'Þ´' => 'ç‘’',
  'Þµ' => 'ç‘‘',
  'Þ¶' => 'ç‘—',
  'Þ·' => 'ç‘€',
  'Þ¸' => 'ç‘�',
  'Þ¹' => 'ç‘�',
  'Þº' => 'ç‘Ž',
  'Þ»' => 'ç‘‚',
  'Þ¼' => 'ç‘†',
  'Þ½' => 'ç‘�',
  'Þ¾' => 'ç‘”',
  'Þ¿' => 'ç“¡',
  'ÞÀ' => 'ç“¿',
  'ÞÁ' => 'ç“¾',
  'ÞÂ' => 'ç“½',
  'ÞÃ' => 'ç”�',
  'ÞÄ' => 'ç•¹',
  'ÞÅ' => 'ç•·',
  'ÞÆ' => 'æ¦ƒ',
  'ÞÇ' => 'ç—¯',
  'ÞÈ' => 'ç˜�',
  'ÞÉ' => 'ç˜ƒ',
  'ÞÊ' => 'ç—·',
  'ÞË' => 'ç—¾',
  'ÞÌ' => 'ç—¼',
  'ÞÍ' => 'ç—¹',
  'ÞÎ' => 'ç—¸',
  'ÞÏ' => 'ç˜�',
  'ÞÐ' => 'ç—»',
  'ÞÑ' => 'ç—¶',
  'ÞÒ' => 'ç—­',
  'ÞÓ' => 'ç—µ',
  'ÞÔ' => 'ç—½',
  'ÞÕ' => 'çš™',
  'ÞÖ' => 'çšµ',
  'Þ×' => 'ç›�',
  'ÞØ' => 'ç�•',
  'ÞÙ' => 'ç�Ÿ',
  'ÞÚ' => 'ç� ',
  'ÞÛ' => 'ç�’',
  'ÞÜ' => 'ç�–',
  'ÞÝ' => 'ç�š',
  'ÞÞ' => 'ç�©',
  'Þß' => 'ç�§',
  'Þà' => 'ç�”',
  'Þá' => 'ç�™',
  'Þâ' => 'ç�­',
  'Þã' => 'çŸ ',
  'Þä' => 'ç¢‡',
  'Þå' => 'ç¢š',
  'Þæ' => 'ç¢”',
  'Þç' => 'ç¢�',
  'Þè' => 'ç¢„',
  'Þé' => 'ç¢•',
  'Þê' => 'ç¢…',
  'Þë' => 'ç¢†',
  'Þì' => 'ç¢¡',
  'Þí' => 'ç¢ƒ',
  'Þî' => 'ç¡¹',
  'Þï' => 'ç¢™',
  'Þð' => 'ç¢€',
  'Þñ' => 'ç¢–',
  'Þò' => 'ç¡»',
  'Þó' => 'ç¥¼',
  'Þô' => 'ç¦‚',
  'Þõ' => 'ç¥½',
  'Þö' => 'ç¥¹',
  'Þ÷' => 'ç¨‘',
  'Þø' => 'ç¨˜',
  'Þù' => 'ç¨™',
  'Þú' => 'ç¨’',
  'Þû' => 'ç¨—',
  'Þü' => 'ç¨•',
  'Þý' => 'ç¨¢',
  'Þþ' => 'ç¨“',
  'ß@' => 'ç¨›',
  'ßA' => 'ç¨�',
  'ßB' => 'çª£',
  'ßC' => 'çª¢',
  'ßD' => 'çªž',
  'ßE' => 'ç««',
  'ßF' => 'ç­¦',
  'ßG' => 'ç­¤',
  'ßH' => 'ç­­',
  'ßI' => 'ç­´',
  'ßJ' => 'ç­©',
  'ßK' => 'ç­²',
  'ßL' => 'ç­¥',
  'ßM' => 'ç­³',
  'ßN' => 'ç­±',
  'ßO' => 'ç­°',
  'ßP' => 'ç­¡',
  'ßQ' => 'ç­¸',
  'ßR' => 'ç­¶',
  'ßS' => 'ç­£',
  'ßT' => 'ç²²',
  'ßU' => 'ç²´',
  'ßV' => 'ç²¯',
  'ßW' => 'ç¶ˆ',
  'ßX' => 'ç¶†',
  'ßY' => 'ç¶€',
  'ßZ' => 'ç¶�',
  'ß[' => 'çµ¿',
  'ß\\' => 'ç¶…',
  'ß]' => 'çµº',
  'ß^' => 'ç¶Ž',
  'ß_' => 'çµ»',
  'ß`' => 'ç¶ƒ',
  'ßa' => 'çµ¼',
  'ßb' => 'ç¶Œ',
  'ßc' => 'ç¶”',
  'ßd' => 'ç¶„',
  'ße' => 'çµ½',
  'ßf' => 'ç¶’',
  'ßg' => 'ç½­',
  'ßh' => 'ç½«',
  'ßi' => 'ç½§',
  'ßj' => 'ç½¨',
  'ßk' => 'ç½¬',
  'ßl' => 'ç¾¦',
  'ßm' => 'ç¾¥',
  'ßn' => 'ç¾§',
  'ßo' => 'ç¿›',
  'ßp' => 'ç¿œ',
  'ßq' => 'è€¡',
  'ßr' => 'è…¤',
  'ßs' => 'è… ',
  'ßt' => 'è…·',
  'ßu' => 'è…œ',
  'ßv' => 'è…©',
  'ßw' => 'è…›',
  'ßx' => 'è…¢',
  'ßy' => 'è…²',
  'ßz' => 'æœ¡',
  'ß{' => 'è…ž',
  'ß|' => 'è…¶',
  'ß}' => 'è…§',
  'ß~' => 'è…¯',
  'ß¡' => 'è…„',
  'ß¢' => 'è…¡',
  'ß£' => 'èˆ�',
  'ß¤' => 'è‰‰',
  'ß¥' => 'è‰„',
  'ß¦' => 'è‰€',
  'ß§' => 'è‰‚',
  'ß¨' => 'è‰…',
  'ß©' => 'è“±',
  'ßª' => 'è�¿',
  'ß«' => 'è‘–',
  'ß¬' => 'è‘¶',
  'ß­' => 'è‘¹',
  'ß®' => 'è’�',
  'ß¯' => 'è’�',
  'ß°' => 'è‘¥',
  'ß±' => 'è‘‘',
  'ß²' => 'è‘€',
  'ß³' => 'è’†',
  'ß´' => 'è‘§',
  'ßµ' => 'è�°',
  'ß¶' => 'è‘�',
  'ß·' => 'è‘½',
  'ß¸' => 'è‘š',
  'ß¹' => 'è‘™',
  'ßº' => 'è‘´',
  'ß»' => 'è‘³',
  'ß¼' => 'è‘�',
  'ß½' => 'è”‡',
  'ß¾' => 'è‘ž',
  'ß¿' => 'è�·',
  'ßÀ' => 'è�º',
  'ßÁ' => 'è�´',
  'ßÂ' => 'è‘º',
  'ßÃ' => 'è‘ƒ',
  'ßÄ' => 'è‘¸',
  'ßÅ' => 'è�²',
  'ßÆ' => 'è‘…',
  'ßÇ' => 'è�©',
  'ßÈ' => 'è�™',
  'ßÉ' => 'è‘‹',
  'ßÊ' => 'è�¯',
  'ßË' => 'è‘‚',
  'ßÌ' => 'è�­',
  'ßÍ' => 'è‘Ÿ',
  'ßÎ' => 'è‘°',
  'ßÏ' => 'è�¹',
  'ßÐ' => 'è‘Ž',
  'ßÑ' => 'è‘Œ',
  'ßÒ' => 'è‘’',
  'ßÓ' => 'è‘¯',
  'ßÔ' => 'è“…',
  'ßÕ' => 'è’Ž',
  'ßÖ' => 'è�»',
  'ß×' => 'è‘‡',
  'ßØ' => 'è�¶',
  'ßÙ' => 'è�³',
  'ßÚ' => 'è‘¨',
  'ßÛ' => 'è‘¾',
  'ßÜ' => 'è‘„',
  'ßÝ' => 'è�«',
  'ßÞ' => 'è‘ ',
  'ßß' => 'è‘”',
  'ßà' => 'è‘®',
  'ßá' => 'è‘�',
  'ßâ' => 'èœ‹',
  'ßã' => 'èœ„',
  'ßä' => 'è›·',
  'ßå' => 'èœŒ',
  'ßæ' => 'è›º',
  'ßç' => 'è›–',
  'ßè' => 'è›µ',
  'ßé' => 'è��',
  'ßê' => 'è›¸',
  'ßë' => 'èœŽ',
  'ßì' => 'èœ‰',
  'ßí' => 'èœ�',
  'ßî' => 'è›¶',
  'ßï' => 'èœ�',
  'ßð' => 'èœ…',
  'ßñ' => 'è£–',
  'ßò' => 'è£‹',
  'ßó' => 'è£�',
  'ßô' => 'è£Ž',
  'ßõ' => 'è£ž',
  'ßö' => 'è£›',
  'ß÷' => 'è£š',
  'ßø' => 'è£Œ',
  'ßù' => 'è£�',
  'ßú' => 'è¦…',
  'ßû' => 'è¦›',
  'ßü' => 'è§Ÿ',
  'ßý' => 'è§¥',
  'ßþ' => 'è§¤',
  'à@' => 'è§¡',
  'àA' => 'è§ ',
  'àB' => 'è§¢',
  'àC' => 'è§œ',
  'àD' => 'è§¦',
  'àE' => 'è©¶',
  'àF' => 'èª†',
  'àG' => 'è©¿',
  'àH' => 'è©¡',
  'àI' => 'è¨¿',
  'àJ' => 'è©·',
  'àK' => 'èª‚',
  'àL' => 'èª„',
  'àM' => 'è©µ',
  'àN' => 'èªƒ',
  'àO' => 'èª�',
  'àP' => 'è©´',
  'àQ' => 'è©º',
  'àR' => 'è°¼',
  'àS' => 'è±‹',
  'àT' => 'è±Š',
  'àU' => 'è±¥',
  'àV' => 'è±¤',
  'àW' => 'è±¦',
  'àX' => 'è²†',
  'àY' => 'è²„',
  'àZ' => 'è²…',
  'à[' => 'è³Œ',
  'à\\' => 'èµ¨',
  'à]' => 'èµ©',
  'à^' => 'è¶‘',
  'à_' => 'è¶Œ',
  'à`' => 'è¶Ž',
  'àa' => 'è¶�',
  'àb' => 'è¶�',
  'àc' => 'è¶“',
  'àd' => 'è¶”',
  'àe' => 'è¶�',
  'àf' => 'è¶’',
  'àg' => 'è·°',
  'àh' => 'è· ',
  'ài' => 'è·¬',
  'àj' => 'è·±',
  'àk' => 'è·®',
  'àl' => 'è·�',
  'àm' => 'è·©',
  'àn' => 'è·£',
  'ào' => 'è·¢',
  'àp' => 'è·§',
  'àq' => 'è·²',
  'àr' => 'è·«',
  'às' => 'è·´',
  'àt' => 'è¼†',
  'àu' => 'è»¿',
  'àv' => 'è¼�',
  'àw' => 'è¼€',
  'àx' => 'è¼…',
  'ày' => 'è¼‡',
  'àz' => 'è¼ˆ',
  'à{' => 'è¼‚',
  'à|' => 'è¼‹',
  'à}' => 'é�’',
  'à~' => 'é€¿',
  'à¡' => 'é�„',
  'à¢' => 'é�‰',
  'à£' => 'é€½',
  'à¤' => 'é„�',
  'à¥' => 'é„�',
  'à¦' => 'é„�',
  'à§' => 'é„‘',
  'à¨' => 'é„–',
  'à©' => 'é„”',
  'àª' => 'é„‹',
  'à«' => 'é„Ž',
  'à¬' => 'é…®',
  'à­' => 'é…¯',
  'à®' => 'é‰ˆ',
  'à¯' => 'é‰’',
  'à°' => 'éˆ°',
  'à±' => 'éˆº',
  'à²' => 'é‰¦',
  'à³' => 'éˆ³',
  'à´' => 'é‰¥',
  'àµ' => 'é‰ž',
  'à¶' => 'éŠƒ',
  'à·' => 'éˆ®',
  'à¸' => 'é‰Š',
  'à¹' => 'é‰†',
  'àº' => 'é‰­',
  'à»' => 'é‰¬',
  'à¼' => 'é‰�',
  'à½' => 'é‰ ',
  'à¾' => 'é‰§',
  'à¿' => 'é‰¯',
  'àÀ' => 'éˆ¶',
  'àÁ' => 'é‰¡',
  'àÂ' => 'é‰°',
  'àÃ' => 'éˆ±',
  'àÄ' => 'é‰”',
  'àÅ' => 'é‰£',
  'àÆ' => 'é‰�',
  'àÇ' => 'é‰²',
  'àÈ' => 'é‰Ž',
  'àÉ' => 'é‰“',
  'àÊ' => 'é‰Œ',
  'àË' => 'é‰–',
  'àÌ' => 'éˆ²',
  'àÍ' => 'é–Ÿ',
  'àÎ' => 'é–œ',
  'àÏ' => 'é–ž',
  'àÐ' => 'é–›',
  'àÑ' => 'éš’',
  'àÒ' => 'éš“',
  'àÓ' => 'éš‘',
  'àÔ' => 'éš—',
  'àÕ' => 'é›Ž',
  'àÖ' => 'é›º',
  'à×' => 'é›½',
  'àØ' => 'é›¸',
  'àÙ' => 'é›µ',
  'àÚ' => 'é�³',
  'àÛ' => 'é�·',
  'àÜ' => 'é�¸',
  'àÝ' => 'é�²',
  'àÞ' => 'é �',
  'àß' => 'é �',
  'àà' => 'é Ž',
  'àá' => 'é¢¬',
  'àâ' => 'é£¶',
  'àã' => 'é£¹',
  'àä' => 'é¦¯',
  'àå' => 'é¦²',
  'àæ' => 'é¦°',
  'àç' => 'é¦µ',
  'àè' => 'éª­',
  'àé' => 'éª«',
  'àê' => 'é­›',
  'àë' => 'é³ª',
  'àì' => 'é³­',
  'àí' => 'é³§',
  'àî' => 'éº€',
  'àï' => 'é»½',
  'àð' => 'åƒ¦',
  'àñ' => 'åƒ”',
  'àò' => 'åƒ—',
  'àó' => 'åƒ¨',
  'àô' => 'åƒ³',
  'àõ' => 'åƒ›',
  'àö' => 'åƒª',
  'à÷' => 'åƒ�',
  'àø' => 'åƒ¤',
  'àù' => 'åƒ“',
  'àú' => 'åƒ¬',
  'àû' => 'åƒ°',
  'àü' => 'åƒ¯',
  'àý' => 'åƒ£',
  'àþ' => 'åƒ ',
  'á@' => 'å‡˜',
  'áA' => 'åŠ€',
  'áB' => 'åŠ�',
  'áC' => 'å‹©',
  'áD' => 'å‹«',
  'áE' => 'åŒ°',
  'áF' => 'åŽ¬',
  'áG' => 'å˜§',
  'áH' => 'å˜•',
  'áI' => 'å˜Œ',
  'áJ' => 'å˜’',
  'áK' => 'å—¼',
  'áL' => 'å˜�',
  'áM' => 'å˜œ',
  'áN' => 'å˜�',
  'áO' => 'å˜“',
  'áP' => 'å˜‚',
  'áQ' => 'å—º',
  'áR' => 'å˜�',
  'áS' => 'å˜„',
  'áT' => 'å—¿',
  'áU' => 'å—¹',
  'áV' => 'å¢‰',
  'áW' => 'å¡¼',
  'áX' => 'å¢�',
  'áY' => 'å¢˜',
  'áZ' => 'å¢†',
  'á[' => 'å¢�',
  'á\\' => 'å¡¿',
  'á]' => 'å¡´',
  'á^' => 'å¢‹',
  'á_' => 'å¡º',
  'á`' => 'å¢‡',
  'áa' => 'å¢‘',
  'áb' => 'å¢Ž',
  'ác' => 'å¡¶',
  'ád' => 'å¢‚',
  'áe' => 'å¢ˆ',
  'áf' => 'å¡»',
  'ág' => 'å¢”',
  'áh' => 'å¢�',
  'ái' => 'å£¾',
  'áj' => 'å¥«',
  'ák' => 'å«œ',
  'ál' => 'å«®',
  'ám' => 'å«¥',
  'án' => 'å«•',
  'áo' => 'å«ª',
  'áp' => 'å«š',
  'áq' => 'å«­',
  'ár' => 'å««',
  'ás' => 'å«³',
  'át' => 'å«¢',
  'áu' => 'å« ',
  'áv' => 'å«›',
  'áw' => 'å«¬',
  'áx' => 'å«ž',
  'áy' => 'å«�',
  'áz' => 'å«™',
  'á{' => 'å«¨',
  'á|' => 'å«Ÿ',
  'á}' => 'å­·',
  'á~' => 'å¯ ',
  'á¡' => 'å¯£',
  'á¢' => 'å±£',
  'á£' => 'å¶‚',
  'á¤' => 'å¶€',
  'á¥' => 'åµ½',
  'á¦' => 'å¶†',
  'á§' => 'åµº',
  'á¨' => 'å¶�',
  'á©' => 'åµ·',
  'áª' => 'å¶Š',
  'á«' => 'å¶‰',
  'á¬' => 'å¶ˆ',
  'á­' => 'åµ¾',
  'á®' => 'åµ¼',
  'á¯' => 'å¶�',
  'á°' => 'åµ¹',
  'á±' => 'åµ¿',
  'á²' => 'å¹˜',
  'á³' => 'å¹™',
  'á´' => 'å¹“',
  'áµ' => 'å»˜',
  'á¶' => 'å»‘',
  'á·' => 'å»—',
  'á¸' => 'å»Ž',
  'á¹' => 'å»œ',
  'áº' => 'å»•',
  'á»' => 'å»™',
  'á¼' => 'å»’',
  'á½' => 'å»”',
  'á¾' => 'å½„',
  'á¿' => 'å½ƒ',
  'áÀ' => 'å½¯',
  'áÁ' => 'å¾¶',
  'áÂ' => 'æ„¬',
  'áÃ' => 'æ„¨',
  'áÄ' => 'æ…�',
  'áÅ' => 'æ…ž',
  'áÆ' => 'æ…±',
  'áÇ' => 'æ…³',
  'áÈ' => 'æ…’',
  'áÉ' => 'æ…“',
  'áÊ' => 'æ…²',
  'áË' => 'æ…¬',
  'áÌ' => 'æ†€',
  'áÍ' => 'æ…´',
  'áÎ' => 'æ…”',
  'áÏ' => 'æ…º',
  'áÐ' => 'æ…›',
  'áÑ' => 'æ…¥',
  'áÒ' => 'æ„»',
  'áÓ' => 'æ…ª',
  'áÔ' => 'æ…¡',
  'áÕ' => 'æ…–',
  'áÖ' => 'æˆ©',
  'á×' => 'æˆ§',
  'áØ' => 'æˆ«',
  'áÙ' => 'æ�«',
  'áÚ' => 'æ‘�',
  'áÛ' => 'æ‘›',
  'áÜ' => 'æ‘�',
  'áÝ' => 'æ‘´',
  'áÞ' => 'æ‘¶',
  'áß' => 'æ‘²',
  'áà' => 'æ‘³',
  'áá' => 'æ‘½',
  'áâ' => 'æ‘µ',
  'áã' => 'æ‘¦',
  'áä' => 'æ’¦',
  'áå' => 'æ‘Ž',
  'áæ' => 'æ’‚',
  'áç' => 'æ‘ž',
  'áè' => 'æ‘œ',
  'áé' => 'æ‘‹',
  'áê' => 'æ‘“',
  'áë' => 'æ‘ ',
  'áì' => 'æ‘�',
  'áí' => 'æ‘¿',
  'áî' => 'æ�¿',
  'áï' => 'æ‘¬',
  'áð' => 'æ‘«',
  'áñ' => 'æ‘™',
  'áò' => 'æ‘¥',
  'áó' => 'æ‘·',
  'áô' => 'æ•³',
  'áõ' => 'æ– ',
  'áö' => 'æš¡',
  'á÷' => 'æš ',
  'áø' => 'æšŸ',
  'áù' => 'æœ…',
  'áú' => 'æœ„',
  'áû' => 'æœ¢',
  'áü' => 'æ¦±',
  'áý' => 'æ¦¶',
  'áþ' => 'æ§‰',
  'â@' => 'æ¦ ',
  'âA' => 'æ§Ž',
  'âB' => 'æ¦–',
  'âC' => 'æ¦°',
  'âD' => 'æ¦¬',
  'âE' => 'æ¦¼',
  'âF' => 'æ¦‘',
  'âG' => 'æ¦™',
  'âH' => 'æ¦Ž',
  'âI' => 'æ¦§',
  'âJ' => 'æ¦�',
  'âK' => 'æ¦©',
  'âL' => 'æ¦¾',
  'âM' => 'æ¦¯',
  'âN' => 'æ¦¿',
  'âO' => 'æ§„',
  'âP' => 'æ¦½',
  'âQ' => 'æ¦¤',
  'âR' => 'æ§”',
  'âS' => 'æ¦¹',
  'âT' => 'æ§Š',
  'âU' => 'æ¦š',
  'âV' => 'æ§�',
  'âW' => 'æ¦³',
  'âX' => 'æ¦“',
  'âY' => 'æ¦ª',
  'âZ' => 'æ¦¡',
  'â[' => 'æ¦ž',
  'â\\' => 'æ§™',
  'â]' => 'æ¦—',
  'â^' => 'æ¦�',
  'â_' => 'æ§‚',
  'â`' => 'æ¦µ',
  'âa' => 'æ¦¥',
  'âb' => 'æ§†',
  'âc' => 'æ­Š',
  'âd' => 'æ­�',
  'âe' => 'æ­‹',
  'âf' => 'æ®ž',
  'âg' => 'æ®Ÿ',
  'âh' => 'æ® ',
  'âi' => 'æ¯ƒ',
  'âj' => 'æ¯„',
  'âk' => 'æ¯¾',
  'âl' => 'æ»Ž',
  'âm' => 'æ»µ',
  'ân' => 'æ»±',
  'âo' => 'æ¼ƒ',
  'âp' => 'æ¼¥',
  'âq' => 'æ»¸',
  'âr' => 'æ¼·',
  'âs' => 'æ»»',
  'ât' => 'æ¼®',
  'âu' => 'æ¼‰',
  'âv' => 'æ½Ž',
  'âw' => 'æ¼™',
  'âx' => 'æ¼š',
  'ây' => 'æ¼§',
  'âz' => 'æ¼˜',
  'â{' => 'æ¼»',
  'â|' => 'æ¼’',
  'â}' => 'æ»­',
  'â~' => 'æ¼Š',
  'â¡' => 'æ¼¶',
  'â¢' => 'æ½³',
  'â£' => 'æ»¹',
  'â¤' => 'æ»®',
  'â¥' => 'æ¼­',
  'â¦' => 'æ½€',
  'â§' => 'æ¼°',
  'â¨' => 'æ¼¼',
  'â©' => 'æ¼µ',
  'âª' => 'æ»«',
  'â«' => 'æ¼‡',
  'â¬' => 'æ¼Ž',
  'â­' => 'æ½ƒ',
  'â®' => 'æ¼…',
  'â¯' => 'æ»½',
  'â°' => 'æ»¶',
  'â±' => 'æ¼¹',
  'â²' => 'æ¼œ',
  'â³' => 'æ»¼',
  'â´' => 'æ¼º',
  'âµ' => 'æ¼Ÿ',
  'â¶' => 'æ¼�',
  'â·' => 'æ¼ž',
  'â¸' => 'æ¼ˆ',
  'â¹' => 'æ¼¡',
  'âº' => 'ç†‡',
  'â»' => 'ç†�',
  'â¼' => 'ç†‰',
  'â½' => 'ç†€',
  'â¾' => 'ç†…',
  'â¿' => 'ç†‚',
  'âÀ' => 'ç†�',
  'âÁ' => 'ç…»',
  'âÂ' => 'ç††',
  'âÃ' => 'ç†�',
  'âÄ' => 'ç†—',
  'âÅ' => 'ç‰„',
  'âÆ' => 'ç‰“',
  'âÇ' => 'çŠ—',
  'âÈ' => 'çŠ•',
  'âÉ' => 'çŠ“',
  'âÊ' => 'ç�ƒ',
  'âË' => 'ç��',
  'âÌ' => 'ç�‘',
  'âÍ' => 'ç�Œ',
  'âÎ' => 'ç‘¢',
  'âÏ' => 'ç‘³',
  'âÐ' => 'ç‘±',
  'âÑ' => 'ç‘µ',
  'âÒ' => 'ç‘²',
  'âÓ' => 'ç‘§',
  'âÔ' => 'ç‘®',
  'âÕ' => 'ç”€',
  'âÖ' => 'ç”‚',
  'â×' => 'ç”ƒ',
  'âØ' => 'ç•½',
  'âÙ' => 'ç–�',
  'âÚ' => 'ç˜–',
  'âÛ' => 'ç˜ˆ',
  'âÜ' => 'ç˜Œ',
  'âÝ' => 'ç˜•',
  'âÞ' => 'ç˜‘',
  'âß' => 'ç˜Š',
  'âà' => 'ç˜”',
  'âá' => 'çš¸',
  'ââ' => 'çž�',
  'âã' => 'ç�¼',
  'âä' => 'çž…',
  'âå' => 'çž‚',
  'âæ' => 'ç�®',
  'âç' => 'çž€',
  'âè' => 'ç�¯',
  'âé' => 'ç�¾',
  'âê' => 'çžƒ',
  'âë' => 'ç¢²',
  'âì' => 'ç¢ª',
  'âí' => 'ç¢´',
  'âî' => 'ç¢­',
  'âï' => 'ç¢¨',
  'âð' => 'ç¡¾',
  'âñ' => 'ç¢«',
  'âò' => 'ç¢ž',
  'âó' => 'ç¢¥',
  'âô' => 'ç¢ ',
  'âõ' => 'ç¢¬',
  'âö' => 'ç¢¢',
  'â÷' => 'ç¢¤',
  'âø' => 'ç¦˜',
  'âù' => 'ç¦Š',
  'âú' => 'ç¦‹',
  'âû' => 'ç¦–',
  'âü' => 'ç¦•',
  'âý' => 'ç¦”',
  'âþ' => 'ç¦“',
  'ã@' => 'ç¦—',
  'ãA' => 'ç¦ˆ',
  'ãB' => 'ç¦’',
  'ãC' => 'ç¦�',
  'ãD' => 'ç¨«',
  'ãE' => 'ç©Š',
  'ãF' => 'ç¨°',
  'ãG' => 'ç¨¯',
  'ãH' => 'ç¨¨',
  'ãI' => 'ç¨¦',
  'ãJ' => 'çª¨',
  'ãK' => 'çª«',
  'ãL' => 'çª¬',
  'ãM' => 'ç«®',
  'ãN' => 'ç®ˆ',
  'ãO' => 'ç®œ',
  'ãP' => 'ç®Š',
  'ãQ' => 'ç®‘',
  'ãR' => 'ç®�',
  'ãS' => 'ç®–',
  'ãT' => 'ç®�',
  'ãU' => 'ç®Œ',
  'ãV' => 'ç®›',
  'ãW' => 'ç®Ž',
  'ãX' => 'ç®…',
  'ãY' => 'ç®˜',
  'ãZ' => 'åŠ„',
  'ã[' => 'ç®™',
  'ã\\' => 'ç®¤',
  'ã]' => 'ç®‚',
  'ã^' => 'ç²»',
  'ã_' => 'ç²¿',
  'ã`' => 'ç²¼',
  'ãa' => 'ç²º',
  'ãb' => 'ç¶§',
  'ãc' => 'ç¶·',
  'ãd' => 'ç·‚',
  'ãe' => 'ç¶£',
  'ãf' => 'ç¶ª',
  'ãg' => 'ç·�',
  'ãh' => 'ç·€',
  'ãi' => 'ç·…',
  'ãj' => 'ç¶�',
  'ãk' => 'ç·Ž',
  'ãl' => 'ç·„',
  'ãm' => 'ç·†',
  'ãn' => 'ç·‹',
  'ão' => 'ç·Œ',
  'ãp' => 'ç¶¯',
  'ãq' => 'ç¶¹',
  'ãr' => 'ç¶–',
  'ãs' => 'ç¶¼',
  'ãt' => 'ç¶Ÿ',
  'ãu' => 'ç¶¦',
  'ãv' => 'ç¶®',
  'ãw' => 'ç¶©',
  'ãx' => 'ç¶¡',
  'ãy' => 'ç·‰',
  'ãz' => 'ç½³',
  'ã{' => 'ç¿¢',
  'ã|' => 'ç¿£',
  'ã}' => 'ç¿¥',
  'ã~' => 'ç¿ž',
  'ã¡' => 'è€¤',
  'ã¢' => 'è��',
  'ã£' => 'è�œ',
  'ã¤' => 'è†‰',
  'ã¥' => 'è††',
  'ã¦' => 'è†ƒ',
  'ã§' => 'è†‡',
  'ã¨' => 'è†�',
  'ã©' => 'è†Œ',
  'ãª' => 'è†‹',
  'ã«' => 'èˆ•',
  'ã¬' => 'è’—',
  'ã­' => 'è’¤',
  'ã®' => 'è’¡',
  'ã¯' => 'è’Ÿ',
  'ã°' => 'è’º',
  'ã±' => 'è“Ž',
  'ã²' => 'è“‚',
  'ã³' => 'è’¬',
  'ã´' => 'è’®',
  'ãµ' => 'è’«',
  'ã¶' => 'è’¹',
  'ã·' => 'è’´',
  'ã¸' => 'è“�',
  'ã¹' => 'è“�',
  'ãº' => 'è’ª',
  'ã»' => 'è’š',
  'ã¼' => 'è’±',
  'ã½' => 'è“�',
  'ã¾' => 'è’�',
  'ã¿' => 'è’§',
  'ãÀ' => 'è’»',
  'ãÁ' => 'è’¢',
  'ãÂ' => 'è’”',
  'ãÃ' => 'è“‡',
  'ãÄ' => 'è“Œ',
  'ãÅ' => 'è’›',
  'ãÆ' => 'è’©',
  'ãÇ' => 'è’¯',
  'ãÈ' => 'è’¨',
  'ãÉ' => 'è“–',
  'ãÊ' => 'è’˜',
  'ãË' => 'è’¶',
  'ãÌ' => 'è“�',
  'ãÍ' => 'è’ ',
  'ãÎ' => 'è“—',
  'ãÏ' => 'è“”',
  'ãÐ' => 'è“’',
  'ãÑ' => 'è“›',
  'ãÒ' => 'è’°',
  'ãÓ' => 'è’‘',
  'ãÔ' => 'è™¡',
  'ãÕ' => 'èœ³',
  'ãÖ' => 'èœ£',
  'ã×' => 'èœ¨',
  'ãØ' => 'è�«',
  'ãÙ' => 'è�€',
  'ãÚ' => 'èœ®',
  'ãÛ' => 'èœž',
  'ãÜ' => 'èœ¡',
  'ãÝ' => 'èœ™',
  'ãÞ' => 'èœ›',
  'ãß' => 'è�ƒ',
  'ãà' => 'èœ¬',
  'ãá' => 'è��',
  'ãâ' => 'èœ¾',
  'ãã' => 'è�†',
  'ãä' => 'èœ ',
  'ãå' => 'èœ²',
  'ãæ' => 'èœª',
  'ãç' => 'èœ­',
  'ãè' => 'èœ¼',
  'ãé' => 'èœ’',
  'ãê' => 'èœº',
  'ãë' => 'èœ±',
  'ãì' => 'èœµ',
  'ãí' => 'è�‚',
  'ãî' => 'èœ¦',
  'ãï' => 'èœ§',
  'ãð' => 'èœ¸',
  'ãñ' => 'èœ¤',
  'ãò' => 'èœš',
  'ãó' => 'èœ°',
  'ãô' => 'èœ‘',
  'ãõ' => 'è£·',
  'ãö' => 'è£§',
  'ã÷' => 'è£±',
  'ãø' => 'è£²',
  'ãù' => 'è£º',
  'ãú' => 'è£¾',
  'ãû' => 'è£®',
  'ãü' => 'è£¼',
  'ãý' => 'è£¶',
  'ãþ' => 'è£»',
  'ä@' => 'è£°',
  'äA' => 'è£¬',
  'äB' => 'è£«',
  'äC' => 'è¦�',
  'äD' => 'è¦¡',
  'äE' => 'è¦Ÿ',
  'äF' => 'è¦ž',
  'äG' => 'è§©',
  'äH' => 'è§«',
  'äI' => 'è§¨',
  'äJ' => 'èª«',
  'äK' => 'èª™',
  'äL' => 'èª‹',
  'äM' => 'èª’',
  'äN' => 'èª�',
  'äO' => 'èª–',
  'äP' => 'è°½',
  'äQ' => 'è±¨',
  'äR' => 'è±©',
  'äS' => 'è³•',
  'äT' => 'è³�',
  'äU' => 'è³—',
  'äV' => 'è¶–',
  'äW' => 'è¸‰',
  'äX' => 'è¸‚',
  'äY' => 'è·¿',
  'äZ' => 'è¸�',
  'ä[' => 'è·½',
  'ä\\' => 'è¸Š',
  'ä]' => 'è¸ƒ',
  'ä^' => 'è¸‡',
  'ä_' => 'è¸†',
  'ä`' => 'è¸…',
  'äa' => 'è·¾',
  'äb' => 'è¸€',
  'äc' => 'è¸„',
  'äd' => 'è¼�',
  'äe' => 'è¼‘',
  'äf' => 'è¼Ž',
  'äg' => 'è¼�',
  'äh' => 'é„£',
  'äi' => 'é„œ',
  'äj' => 'é„ ',
  'äk' => 'é„¢',
  'äl' => 'é„Ÿ',
  'äm' => 'é„�',
  'än' => 'é„š',
  'äo' => 'é„¤',
  'äp' => 'é„¡',
  'äq' => 'é„›',
  'är' => 'é…º',
  'äs' => 'é…²',
  'ät' => 'é…¹',
  'äu' => 'é…³',
  'äv' => 'éŠ¥',
  'äw' => 'éŠ¤',
  'äx' => 'é‰¶',
  'äy' => 'éŠ›',
  'äz' => 'é‰º',
  'ä{' => 'éŠ ',
  'ä|' => 'éŠ”',
  'ä}' => 'éŠª',
  'ä~' => 'éŠ�',
  'ä¡' => 'éŠ¦',
  'ä¢' => 'éŠš',
  'ä£' => 'éŠ«',
  'ä¤' => 'é‰¹',
  'ä¥' => 'éŠ—',
  'ä¦' => 'é‰¿',
  'ä§' => 'éŠ£',
  'ä¨' => 'é‹®',
  'ä©' => 'éŠŽ',
  'äª' => 'éŠ‚',
  'ä«' => 'éŠ•',
  'ä¬' => 'éŠ¢',
  'ä­' => 'é‰½',
  'ä®' => 'éŠˆ',
  'ä¯' => 'éŠ¡',
  'ä°' => 'éŠŠ',
  'ä±' => 'éŠ†',
  'ä²' => 'éŠŒ',
  'ä³' => 'éŠ™',
  'ä´' => 'éŠ§',
  'äµ' => 'é‰¾',
  'ä¶' => 'éŠ‡',
  'ä·' => 'éŠ©',
  'ä¸' => 'éŠ�',
  'ä¹' => 'éŠ‹',
  'äº' => 'éˆ­',
  'ä»' => 'éšž',
  'ä¼' => 'éš¡',
  'ä½' => 'é›¿',
  'ä¾' => 'é�˜',
  'ä¿' => 'é�½',
  'äÀ' => 'é�º',
  'äÁ' => 'é�¾',
  'äÂ' => 'éžƒ',
  'äÃ' => 'éž€',
  'äÄ' => 'éž‚',
  'äÅ' => 'é�»',
  'äÆ' => 'éž„',
  'äÇ' => 'éž�',
  'äÈ' => 'é�¿',
  'äÉ' => 'éŸŽ',
  'äÊ' => 'éŸ�',
  'äË' => 'é –',
  'äÌ' => 'é¢­',
  'äÍ' => 'é¢®',
  'äÎ' => 'é¤‚',
  'äÏ' => 'é¤€',
  'äÐ' => 'é¤‡',
  'äÑ' => 'é¦�',
  'äÒ' => 'é¦œ',
  'äÓ' => 'é§ƒ',
  'äÔ' => 'é¦¹',
  'äÕ' => 'é¦»',
  'äÖ' => 'é¦º',
  'ä×' => 'é§‚',
  'äØ' => 'é¦½',
  'äÙ' => 'é§‡',
  'äÚ' => 'éª±',
  'äÛ' => 'é«£',
  'äÜ' => 'é«§',
  'äÝ' => 'é¬¾',
  'äÞ' => 'é¬¿',
  'äß' => 'é­ ',
  'äà' => 'é­¡',
  'äá' => 'é­Ÿ',
  'äâ' => 'é³±',
  'äã' => 'é³²',
  'ää' => 'é³µ',
  'äå' => 'éº§',
  'äæ' => 'åƒ¿',
  'äç' => 'å„ƒ',
  'äè' => 'å„°',
  'äé' => 'åƒ¸',
  'äê' => 'å„†',
  'äë' => 'å„‡',
  'äì' => 'åƒ¶',
  'äí' => 'åƒ¾',
  'äî' => 'å„‹',
  'äï' => 'å„Œ',
  'äð' => 'åƒ½',
  'äñ' => 'å„Š',
  'äò' => 'åŠ‹',
  'äó' => 'åŠŒ',
  'äô' => 'å‹±',
  'äõ' => 'å‹¯',
  'äö' => 'å™ˆ',
  'ä÷' => 'å™‚',
  'äø' => 'å™Œ',
  'äù' => 'å˜µ',
  'äú' => 'å™�',
  'äû' => 'å™Š',
  'äü' => 'å™‰',
  'äý' => 'å™†',
  'äþ' => 'å™˜',
  'å@' => 'å™š',
  'åA' => 'å™€',
  'åB' => 'å˜³',
  'åC' => 'å˜½',
  'åD' => 'å˜¬',
  'åE' => 'å˜¾',
  'åF' => 'å˜¸',
  'åG' => 'å˜ª',
  'åH' => 'å˜º',
  'åI' => 'åœš',
  'åJ' => 'å¢«',
  'åK' => 'å¢�',
  'åL' => 'å¢±',
  'åM' => 'å¢ ',
  'åN' => 'å¢£',
  'åO' => 'å¢¯',
  'åP' => 'å¢¬',
  'åQ' => 'å¢¥',
  'åR' => 'å¢¡',
  'åS' => 'å£¿',
  'åT' => 'å«¿',
  'åU' => 'å«´',
  'åV' => 'å«½',
  'åW' => 'å«·',
  'åX' => 'å«¶',
  'åY' => 'å¬ƒ',
  'åZ' => 'å«¸',
  'å[' => 'å¬‚',
  'å\\' => 'å«¹',
  'å]' => 'å¬�',
  'å^' => 'å¬‡',
  'å_' => 'å¬…',
  'å`' => 'å¬�',
  'åa' => 'å±§',
  'åb' => 'å¶™',
  'åc' => 'å¶—',
  'åd' => 'å¶Ÿ',
  'åe' => 'å¶’',
  'åf' => 'å¶¢',
  'åg' => 'å¶“',
  'åh' => 'å¶•',
  'åi' => 'å¶ ',
  'åj' => 'å¶œ',
  'åk' => 'å¶¡',
  'ål' => 'å¶š',
  'åm' => 'å¶ž',
  'ån' => 'å¹©',
  'åo' => 'å¹�',
  'åp' => 'å¹ ',
  'åq' => 'å¹œ',
  'år' => 'ç·³',
  'ås' => 'å»›',
  'åt' => 'å»ž',
  'åu' => 'å»¡',
  'åv' => 'å½‰',
  'åw' => 'å¾²',
  'åx' => 'æ†‹',
  'åy' => 'æ†ƒ',
  'åz' => 'æ…¹',
  'å{' => 'æ†±',
  'å|' => 'æ†°',
  'å}' => 'æ†¢',
  'å~' => 'æ†‰',
  'å¡' => 'æ†›',
  'å¢' => 'æ†“',
  'å£' => 'æ†¯',
  'å¤' => 'æ†­',
  'å¥' => 'æ†Ÿ',
  'å¦' => 'æ†’',
  'å§' => 'æ†ª',
  'å¨' => 'æ†¡',
  'å©' => 'æ†�',
  'åª' => 'æ…¦',
  'å«' => 'æ†³',
  'å¬' => 'æˆ­',
  'å­' => 'æ‘®',
  'å®' => 'æ‘°',
  'å¯' => 'æ’–',
  'å°' => 'æ’ ',
  'å±' => 'æ’…',
  'å²' => 'æ’—',
  'å³' => 'æ’œ',
  'å´' => 'æ’�',
  'åµ' => 'æ’‹',
  'å¶' => 'æ’Š',
  'å·' => 'æ’Œ',
  'å¸' => 'æ’£',
  'å¹' => 'æ’Ÿ',
  'åº' => 'æ‘¨',
  'å»' => 'æ’±',
  'å¼' => 'æ’˜',
  'å½' => 'æ•¶',
  'å¾' => 'æ•º',
  'å¿' => 'æ•¹',
  'åÀ' => 'æ•»',
  'åÁ' => 'æ–²',
  'åÂ' => 'æ–³',
  'åÃ' => 'æšµ',
  'åÄ' => 'æš°',
  'åÅ' => 'æš©',
  'åÆ' => 'æš²',
  'åÇ' => 'æš·',
  'åÈ' => 'æšª',
  'åÉ' => 'æš¯',
  'åÊ' => 'æ¨€',
  'åË' => 'æ¨†',
  'åÌ' => 'æ¨—',
  'åÍ' => 'æ§¥',
  'åÎ' => 'æ§¸',
  'åÏ' => 'æ¨•',
  'åÐ' => 'æ§±',
  'åÑ' => 'æ§¤',
  'åÒ' => 'æ¨ ',
  'åÓ' => 'æ§¿',
  'åÔ' => 'æ§¬',
  'åÕ' => 'æ§¢',
  'åÖ' => 'æ¨›',
  'å×' => 'æ¨�',
  'åØ' => 'æ§¾',
  'åÙ' => 'æ¨§',
  'åÚ' => 'æ§²',
  'åÛ' => 'æ§®',
  'åÜ' => 'æ¨”',
  'åÝ' => 'æ§·',
  'åÞ' => 'æ§§',
  'åß' => 'æ©€',
  'åà' => 'æ¨ˆ',
  'åá' => 'æ§¦',
  'åâ' => 'æ§»',
  'åã' => 'æ¨�',
  'åä' => 'æ§¼',
  'åå' => 'æ§«',
  'åæ' => 'æ¨‰',
  'åç' => 'æ¨„',
  'åè' => 'æ¨˜',
  'åé' => 'æ¨¥',
  'åê' => 'æ¨�',
  'åë' => 'æ§¶',
  'åì' => 'æ¨¦',
  'åí' => 'æ¨‡',
  'åî' => 'æ§´',
  'åï' => 'æ¨–',
  'åð' => 'æ­‘',
  'åñ' => 'æ®¥',
  'åò' => 'æ®£',
  'åó' => 'æ®¢',
  'åô' => 'æ®¦',
  'åõ' => 'æ°�',
  'åö' => 'æ°€',
  'å÷' => 'æ¯¿',
  'åø' => 'æ°‚',
  'åù' => 'æ½�',
  'åú' => 'æ¼¦',
  'åû' => 'æ½¾',
  'åü' => 'æ¾‡',
  'åý' => 'æ¿†',
  'åþ' => 'æ¾’',
  'æ@' => 'æ¾�',
  'æA' => 'æ¾‰',
  'æB' => 'æ¾Œ',
  'æC' => 'æ½¢',
  'æD' => 'æ½�',
  'æE' => 'æ¾…',
  'æF' => 'æ½š',
  'æG' => 'æ¾–',
  'æH' => 'æ½¶',
  'æI' => 'æ½¬',
  'æJ' => 'æ¾‚',
  'æK' => 'æ½•',
  'æL' => 'æ½²',
  'æM' => 'æ½’',
  'æN' => 'æ½�',
  'æO' => 'æ½—',
  'æP' => 'æ¾”',
  'æQ' => 'æ¾“',
  'æR' => 'æ½�',
  'æS' => 'æ¼€',
  'æT' => 'æ½¡',
  'æU' => 'æ½«',
  'æV' => 'æ½½',
  'æW' => 'æ½§',
  'æX' => 'æ¾�',
  'æY' => 'æ½“',
  'æZ' => 'æ¾‹',
  'æ[' => 'æ½©',
  'æ\\' => 'æ½¿',
  'æ]' => 'æ¾•',
  'æ^' => 'æ½£',
  'æ_' => 'æ½·',
  'æ`' => 'æ½ª',
  'æa' => 'æ½»',
  'æb' => 'ç†²',
  'æc' => 'ç†¯',
  'æd' => 'ç†›',
  'æe' => 'ç†°',
  'æf' => 'ç† ',
  'æg' => 'ç†š',
  'æh' => 'ç†©',
  'æi' => 'ç†µ',
  'æj' => 'ç†�',
  'æk' => 'ç†¥',
  'æl' => 'ç†ž',
  'æm' => 'ç†¤',
  'æn' => 'ç†¡',
  'æo' => 'ç†ª',
  'æp' => 'ç†œ',
  'æq' => 'ç†§',
  'ær' => 'ç†³',
  'æs' => 'çŠ˜',
  'æt' => 'çŠš',
  'æu' => 'ç�˜',
  'æv' => 'ç�’',
  'æw' => 'ç�ž',
  'æx' => 'ç�Ÿ',
  'æy' => 'ç� ',
  'æz' => 'ç��',
  'æ{' => 'ç�›',
  'æ|' => 'ç�¡',
  'æ}' => 'ç�š',
  'æ~' => 'ç�™',
  'æ¡' => 'ç�¢',
  'æ¢' => 'ç’‡',
  'æ£' => 'ç’‰',
  'æ¤' => 'ç’Š',
  'æ¥' => 'ç’†',
  'æ¦' => 'ç’�',
  'æ§' => 'ç‘½',
  'æ¨' => 'ç’…',
  'æ©' => 'ç’ˆ',
  'æª' => 'ç‘¼',
  'æ«' => 'ç‘¹',
  'æ¬' => 'ç”ˆ',
  'æ­' => 'ç”‡',
  'æ®' => 'ç•¾',
  'æ¯' => 'ç˜¥',
  'æ°' => 'ç˜ž',
  'æ±' => 'ç˜™',
  'æ²' => 'ç˜�',
  'æ³' => 'ç˜œ',
  'æ´' => 'ç˜£',
  'æµ' => 'ç˜š',
  'æ¶' => 'ç˜¨',
  'æ·' => 'ç˜›',
  'æ¸' => 'çšœ',
  'æ¹' => 'çš�',
  'æº' => 'çšž',
  'æ»' => 'çš›',
  'æ¼' => 'çž�',
  'æ½' => 'çž�',
  'æ¾' => 'çž‰',
  'æ¿' => 'çžˆ',
  'æÀ' => 'ç£�',
  'æÁ' => 'ç¢»',
  'æÂ' => 'ç£�',
  'æÃ' => 'ç£Œ',
  'æÄ' => 'ç£‘',
  'æÅ' => 'ç£Ž',
  'æÆ' => 'ç£”',
  'æÇ' => 'ç£ˆ',
  'æÈ' => 'ç£ƒ',
  'æÉ' => 'ç£„',
  'æÊ' => 'ç£‰',
  'æË' => 'ç¦š',
  'æÌ' => 'ç¦¡',
  'æÍ' => 'ç¦ ',
  'æÎ' => 'ç¦œ',
  'æÏ' => 'ç¦¢',
  'æÐ' => 'ç¦›',
  'æÑ' => 'æ­¶',
  'æÒ' => 'ç¨¹',
  'æÓ' => 'çª²',
  'æÔ' => 'çª´',
  'æÕ' => 'çª³',
  'æÖ' => 'ç®·',
  'æ×' => 'ç¯‹',
  'æØ' => 'ç®¾',
  'æÙ' => 'ç®¬',
  'æÚ' => 'ç¯Ž',
  'æÛ' => 'ç®¯',
  'æÜ' => 'ç®¹',
  'æÝ' => 'ç¯Š',
  'æÞ' => 'ç®µ',
  'æß' => 'ç³…',
  'æà' => 'ç³ˆ',
  'æá' => 'ç³Œ',
  'æâ' => 'ç³‹',
  'æã' => 'ç··',
  'æä' => 'ç·›',
  'æå' => 'ç·ª',
  'ææ' => 'ç·§',
  'æç' => 'ç·—',
  'æè' => 'ç·¡',
  'æé' => 'ç¸ƒ',
  'æê' => 'ç·º',
  'æë' => 'ç·¦',
  'æì' => 'ç·¶',
  'æí' => 'ç·±',
  'æî' => 'ç·°',
  'æï' => 'ç·®',
  'æð' => 'ç·Ÿ',
  'æñ' => 'ç½¶',
  'æò' => 'ç¾¬',
  'æó' => 'ç¾°',
  'æô' => 'ç¾­',
  'æõ' => 'ç¿­',
  'æö' => 'ç¿«',
  'æ÷' => 'ç¿ª',
  'æø' => 'ç¿¬',
  'æù' => 'ç¿¦',
  'æú' => 'ç¿¨',
  'æû' => 'è�¤',
  'æü' => 'è�§',
  'æý' => 'è†£',
  'æþ' => 'è†Ÿ',
  'ç@' => 'è†ž',
  'çA' => 'è†•',
  'çB' => 'è†¢',
  'çC' => 'è†™',
  'çD' => 'è†—',
  'çE' => 'èˆ–',
  'çF' => 'è‰�',
  'çG' => 'è‰“',
  'çH' => 'è‰’',
  'çI' => 'è‰�',
  'çJ' => 'è‰Ž',
  'çK' => 'è‰‘',
  'çL' => 'è”¤',
  'çM' => 'è”»',
  'çN' => 'è”�',
  'çO' => 'è”€',
  'çP' => 'è”©',
  'çQ' => 'è”Ž',
  'çR' => 'è”‰',
  'çS' => 'è”�',
  'çT' => 'è”Ÿ',
  'çU' => 'è”Š',
  'çV' => 'è”§',
  'çW' => 'è”œ',
  'çX' => 'è“»',
  'çY' => 'è”«',
  'çZ' => 'è“º',
  'ç[' => 'è”ˆ',
  'ç\\' => 'è”Œ',
  'ç]' => 'è“´',
  'ç^' => 'è”ª',
  'ç_' => 'è“²',
  'ç`' => 'è”•',
  'ça' => 'è“·',
  'çb' => 'è“«',
  'çc' => 'è“³',
  'çd' => 'è“¼',
  'çe' => 'è”’',
  'çf' => 'è“ª',
  'çg' => 'è“©',
  'çh' => 'è”–',
  'çi' => 'è“¾',
  'çj' => 'è”¨',
  'çk' => 'è”�',
  'çl' => 'è”®',
  'çm' => 'è”‚',
  'çn' => 'è“½',
  'ço' => 'è”ž',
  'çp' => 'è“¶',
  'çq' => 'è”±',
  'çr' => 'è”¦',
  'çs' => 'è“§',
  'çt' => 'è“¨',
  'çu' => 'è“°',
  'çv' => 'è“¯',
  'çw' => 'è“¹',
  'çx' => 'è”˜',
  'çy' => 'è” ',
  'çz' => 'è”°',
  'ç{' => 'è”‹',
  'ç|' => 'è”™',
  'ç}' => 'è”¯',
  'ç~' => 'è™¢',
  'ç¡' => 'è�–',
  'ç¢' => 'è�£',
  'ç£' => 'è�¤',
  'ç¤' => 'è�·',
  'ç¥' => 'èŸ¡',
  'ç¦' => 'è�³',
  'ç§' => 'è�˜',
  'ç¨' => 'è�”',
  'ç©' => 'è�›',
  'çª' => 'è�’',
  'ç«' => 'è�¡',
  'ç¬' => 'è�š',
  'ç­' => 'è�‘',
  'ç®' => 'è�ž',
  'ç¯' => 'è�­',
  'ç°' => 'è�ª',
  'ç±' => 'è��',
  'ç²' => 'è�Ž',
  'ç³' => 'è�Ÿ',
  'ç´' => 'è��',
  'çµ' => 'è�¯',
  'ç¶' => 'è�¬',
  'ç·' => 'è�º',
  'ç¸' => 'è�®',
  'ç¹' => 'è�œ',
  'çº' => 'è�¥',
  'ç»' => 'è��',
  'ç¼' => 'è�»',
  'ç½' => 'è�µ',
  'ç¾' => 'è�¢',
  'ç¿' => 'è�§',
  'çÀ' => 'è�©',
  'çÁ' => 'è¡š',
  'çÂ' => 'è¤…',
  'çÃ' => 'è¤Œ',
  'çÄ' => 'è¤”',
  'çÅ' => 'è¤‹',
  'çÆ' => 'è¤—',
  'çÇ' => 'è¤˜',
  'çÈ' => 'è¤™',
  'çÉ' => 'è¤†',
  'çÊ' => 'è¤–',
  'çË' => 'è¤‘',
  'çÌ' => 'è¤Ž',
  'çÍ' => 'è¤‰',
  'çÎ' => 'è¦¢',
  'çÏ' => 'è¦¤',
  'çÐ' => 'è¦£',
  'çÑ' => 'è§­',
  'çÒ' => 'è§°',
  'çÓ' => 'è§¬',
  'çÔ' => 'è«�',
  'çÕ' => 'è«†',
  'çÖ' => 'èª¸',
  'ç×' => 'è«“',
  'çØ' => 'è«‘',
  'çÙ' => 'è«”',
  'çÚ' => 'è«•',
  'çÛ' => 'èª»',
  'çÜ' => 'è«—',
  'çÝ' => 'èª¾',
  'çÞ' => 'è«€',
  'çß' => 'è«…',
  'çà' => 'è«˜',
  'çá' => 'è«ƒ',
  'çâ' => 'èªº',
  'çã' => 'èª½',
  'çä' => 'è«™',
  'çå' => 'è°¾',
  'çæ' => 'è±�',
  'çç' => 'è²�',
  'çè' => 'è³¥',
  'çé' => 'è³Ÿ',
  'çê' => 'è³™',
  'çë' => 'è³¨',
  'çì' => 'è³š',
  'çí' => 'è³�',
  'çî' => 'è³§',
  'çï' => 'è¶ ',
  'çð' => 'è¶œ',
  'çñ' => 'è¶¡',
  'çò' => 'è¶›',
  'çó' => 'è¸ ',
  'çô' => 'è¸£',
  'çõ' => 'è¸¥',
  'çö' => 'è¸¤',
  'ç÷' => 'è¸®',
  'çø' => 'è¸•',
  'çù' => 'è¸›',
  'çú' => 'è¸–',
  'çû' => 'è¸‘',
  'çü' => 'è¸™',
  'çý' => 'è¸¦',
  'çþ' => 'è¸§',
  'è@' => 'è¸”',
  'èA' => 'è¸’',
  'èB' => 'è¸˜',
  'èC' => 'è¸“',
  'èD' => 'è¸œ',
  'èE' => 'è¸—',
  'èF' => 'è¸š',
  'èG' => 'è¼¬',
  'èH' => 'è¼¤',
  'èI' => 'è¼˜',
  'èJ' => 'è¼š',
  'èK' => 'è¼ ',
  'èL' => 'è¼£',
  'èM' => 'è¼–',
  'èN' => 'è¼—',
  'èO' => 'é�³',
  'èP' => 'é�°',
  'èQ' => 'é�¯',
  'èR' => 'é�§',
  'èS' => 'é�«',
  'èT' => 'é„¯',
  'èU' => 'é„«',
  'èV' => 'é„©',
  'èW' => 'é„ª',
  'èX' => 'é„²',
  'èY' => 'é„¦',
  'èZ' => 'é„®',
  'è[' => 'é†…',
  'è\\' => 'é††',
  'è]' => 'é†Š',
  'è^' => 'é†�',
  'è_' => 'é†‚',
  'è`' => 'é†„',
  'èa' => 'é†€',
  'èb' => 'é‹�',
  'èc' => 'é‹ƒ',
  'èd' => 'é‹„',
  'èe' => 'é‹€',
  'èf' => 'é‹™',
  'èg' => 'éŠ¶',
  'èh' => 'é‹�',
  'èi' => 'é‹±',
  'èj' => 'é‹Ÿ',
  'èk' => 'é‹˜',
  'èl' => 'é‹©',
  'èm' => 'é‹—',
  'èn' => 'é‹�',
  'èo' => 'é‹Œ',
  'èp' => 'é‹¯',
  'èq' => 'é‹‚',
  'èr' => 'é‹¨',
  'ès' => 'é‹Š',
  'èt' => 'é‹ˆ',
  'èu' => 'é‹Ž',
  'èv' => 'é‹¦',
  'èw' => 'é‹�',
  'èx' => 'é‹•',
  'èy' => 'é‹‰',
  'èz' => 'é‹ ',
  'è{' => 'é‹ž',
  'è|' => 'é‹§',
  'è}' => 'é‹‘',
  'è~' => 'é‹“',
  'è¡' => 'éŠµ',
  'è¢' => 'é‹¡',
  'è£' => 'é‹†',
  'è¤' => 'éŠ´',
  'è¥' => 'é•¼',
  'è¦' => 'é–¬',
  'è§' => 'é–«',
  'è¨' => 'é–®',
  'è©' => 'é–°',
  'èª' => 'éš¤',
  'è«' => 'éš¢',
  'è¬' => 'é›“',
  'è­' => 'éœ…',
  'è®' => 'éœˆ',
  'è¯' => 'éœ‚',
  'è°' => 'é�š',
  'è±' => 'éžŠ',
  'è²' => 'éžŽ',
  'è³' => 'éžˆ',
  'è´' => 'éŸ�',
  'èµ' => 'éŸ�',
  'è¶' => 'é ž',
  'è·' => 'é �',
  'è¸' => 'é ¦',
  'è¹' => 'é ©',
  'èº' => 'é ¨',
  'è»' => 'é  ',
  'è¼' => 'é ›',
  'è½' => 'é §',
  'è¾' => 'é¢²',
  'è¿' => 'é¤ˆ',
  'èÀ' => 'é£º',
  'èÁ' => 'é¤‘',
  'èÂ' => 'é¤”',
  'èÃ' => 'é¤–',
  'èÄ' => 'é¤—',
  'èÅ' => 'é¤•',
  'èÆ' => 'é§œ',
  'èÇ' => 'é§�',
  'èÈ' => 'é§�',
  'èÉ' => 'é§“',
  'èÊ' => 'é§”',
  'èË' => 'é§Ž',
  'èÌ' => 'é§‰',
  'èÍ' => 'é§–',
  'èÎ' => 'é§˜',
  'èÏ' => 'é§‹',
  'èÐ' => 'é§—',
  'èÑ' => 'é§Œ',
  'èÒ' => 'éª³',
  'èÓ' => 'é«¬',
  'èÔ' => 'é««',
  'èÕ' => 'é«³',
  'èÖ' => 'é«²',
  'è×' => 'é«±',
  'èØ' => 'é­†',
  'èÙ' => 'é­ƒ',
  'èÚ' => 'é­§',
  'èÛ' => 'é­´',
  'èÜ' => 'é­±',
  'èÝ' => 'é­¦',
  'èÞ' => 'é­¶',
  'èß' => 'é­µ',
  'èà' => 'é­°',
  'èá' => 'é­¨',
  'èâ' => 'é­¤',
  'èã' => 'é­¬',
  'èä' => 'é³¼',
  'èå' => 'é³º',
  'èæ' => 'é³½',
  'èç' => 'é³¿',
  'èè' => 'é³·',
  'èé' => 'é´‡',
  'èê' => 'é´€',
  'èë' => 'é³¹',
  'èì' => 'é³»',
  'èí' => 'é´ˆ',
  'èî' => 'é´…',
  'èï' => 'é´„',
  'èð' => 'éºƒ',
  'èñ' => 'é»“',
  'èò' => 'é¼�',
  'èó' => 'é¼�',
  'èô' => 'å„œ',
  'èõ' => 'å„“',
  'èö' => 'å„—',
  'è÷' => 'å„š',
  'èø' => 'å„‘',
  'èù' => 'å‡ž',
  'èú' => 'åŒ´',
  'èû' => 'å�¡',
  'èü' => 'å™°',
  'èý' => 'å™ ',
  'èþ' => 'å™®',
  'é@' => 'å™³',
  'éA' => 'å™¦',
  'éB' => 'å™£',
  'éC' => 'å™­',
  'éD' => 'å™²',
  'éE' => 'å™ž',
  'éF' => 'å™·',
  'éG' => 'åœœ',
  'éH' => 'åœ›',
  'éI' => 'å£ˆ',
  'éJ' => 'å¢½',
  'éK' => 'å£‰',
  'éL' => 'å¢¿',
  'éM' => 'å¢º',
  'éN' => 'å£‚',
  'éO' => 'å¢¼',
  'éP' => 'å£†',
  'éQ' => 'å¬—',
  'éR' => 'å¬™',
  'éS' => 'å¬›',
  'éT' => 'å¬¡',
  'éU' => 'å¬”',
  'éV' => 'å¬“',
  'éW' => 'å¬�',
  'éX' => 'å¬–',
  'éY' => 'å¬¨',
  'éZ' => 'å¬š',
  'é[' => 'å¬ ',
  'é\\' => 'å¬ž',
  'é]' => 'å¯¯',
  'é^' => 'å¶¬',
  'é_' => 'å¶±',
  'é`' => 'å¶©',
  'éa' => 'å¶§',
  'éb' => 'å¶µ',
  'éc' => 'å¶°',
  'éd' => 'å¶®',
  'ée' => 'å¶ª',
  'éf' => 'å¶¨',
  'ég' => 'å¶²',
  'éh' => 'å¶­',
  'éi' => 'å¶¯',
  'éj' => 'å¶´',
  'ék' => 'å¹§',
  'él' => 'å¹¨',
  'ém' => 'å¹¦',
  'én' => 'å¹¯',
  'éo' => 'å»©',
  'ép' => 'å»§',
  'éq' => 'å»¦',
  'ér' => 'å»¨',
  'és' => 'å»¥',
  'ét' => 'å½‹',
  'éu' => 'å¾¼',
  'év' => 'æ†�',
  'éw' => 'æ†¨',
  'éx' => 'æ†–',
  'éy' => 'æ‡…',
  'éz' => 'æ†´',
  'é{' => 'æ‡†',
  'é|' => 'æ‡�',
  'é}' => 'æ‡Œ',
  'é~' => 'æ†º',
  'é¡' => 'æ†¿',
  'é¢' => 'æ†¸',
  'é£' => 'æ†Œ',
  'é¤' => 'æ“—',
  'é¥' => 'æ“–',
  'é¦' => 'æ“�',
  'é§' => 'æ“�',
  'é¨' => 'æ“‰',
  'é©' => 'æ’½',
  'éª' => 'æ’‰',
  'é«' => 'æ“ƒ',
  'é¬' => 'æ“›',
  'é­' => 'æ“³',
  'é®' => 'æ“™',
  'é¯' => 'æ”³',
  'é°' => 'æ•¿',
  'é±' => 'æ•¼',
  'é²' => 'æ–¢',
  'é³' => 'æ›ˆ',
  'é´' => 'æš¾',
  'éµ' => 'æ›€',
  'é¶' => 'æ›Š',
  'é·' => 'æ›‹',
  'é¸' => 'æ›�',
  'é¹' => 'æš½',
  'éº' => 'æš»',
  'é»' => 'æšº',
  'é¼' => 'æ›Œ',
  'é½' => 'æœ£',
  'é¾' => 'æ¨´',
  'é¿' => 'æ©¦',
  'éÀ' => 'æ©‰',
  'éÁ' => 'æ©§',
  'éÂ' => 'æ¨²',
  'éÃ' => 'æ©¨',
  'éÄ' => 'æ¨¾',
  'éÅ' => 'æ©�',
  'éÆ' => 'æ©­',
  'éÇ' => 'æ©¶',
  'éÈ' => 'æ©›',
  'éÉ' => 'æ©‘',
  'éÊ' => 'æ¨¨',
  'éË' => 'æ©š',
  'éÌ' => 'æ¨»',
  'éÍ' => 'æ¨¿',
  'éÎ' => 'æ©�',
  'éÏ' => 'æ©ª',
  'éÐ' => 'æ©¤',
  'éÑ' => 'æ©�',
  'éÒ' => 'æ©�',
  'éÓ' => 'æ©”',
  'éÔ' => 'æ©¯',
  'éÕ' => 'æ©©',
  'éÖ' => 'æ© ',
  'é×' => 'æ¨¼',
  'éØ' => 'æ©ž',
  'éÙ' => 'æ©–',
  'éÚ' => 'æ©•',
  'éÛ' => 'æ©�',
  'éÜ' => 'æ©Ž',
  'éÝ' => 'æ©†',
  'éÞ' => 'æ­•',
  'éß' => 'æ­”',
  'éà' => 'æ­–',
  'éá' => 'æ®§',
  'éâ' => 'æ®ª',
  'éã' => 'æ®«',
  'éä' => 'æ¯ˆ',
  'éå' => 'æ¯‡',
  'éæ' => 'æ°„',
  'éç' => 'æ°ƒ',
  'éè' => 'æ°†',
  'éé' => 'æ¾­',
  'éê' => 'æ¿‹',
  'éë' => 'æ¾£',
  'éì' => 'æ¿‡',
  'éí' => 'æ¾¼',
  'éî' => 'æ¿Ž',
  'éï' => 'æ¿ˆ',
  'éð' => 'æ½ž',
  'éñ' => 'æ¿„',
  'éò' => 'æ¾½',
  'éó' => 'æ¾ž',
  'éô' => 'æ¿Š',
  'éõ' => 'æ¾¨',
  'éö' => 'ç€„',
  'é÷' => 'æ¾¥',
  'éø' => 'æ¾®',
  'éù' => 'æ¾º',
  'éú' => 'æ¾¬',
  'éû' => 'æ¾ª',
  'éü' => 'æ¿�',
  'éý' => 'æ¾¿',
  'éþ' => 'æ¾¸',
  'ê@' => 'æ¾¢',
  'êA' => 'æ¿‰',
  'êB' => 'æ¾«',
  'êC' => 'æ¿�',
  'êD' => 'æ¾¯',
  'êE' => 'æ¾²',
  'êF' => 'æ¾°',
  'êG' => 'ç‡…',
  'êH' => 'ç‡‚',
  'êI' => 'ç†¿',
  'êJ' => 'ç†¸',
  'êK' => 'ç‡–',
  'êL' => 'ç‡€',
  'êM' => 'ç‡�',
  'êN' => 'ç‡‹',
  'êO' => 'ç‡”',
  'êP' => 'ç‡Š',
  'êQ' => 'ç‡‡',
  'êR' => 'ç‡�',
  'êS' => 'ç†½',
  'êT' => 'ç‡˜',
  'êU' => 'ç†¼',
  'êV' => 'ç‡†',
  'êW' => 'ç‡š',
  'êX' => 'ç‡›',
  'êY' => 'çŠ�',
  'êZ' => 'çŠž',
  'ê[' => 'ç�©',
  'ê\\' => 'ç�¦',
  'ê]' => 'ç�§',
  'ê^' => 'ç�¬',
  'ê_' => 'ç�¥',
  'ê`' => 'ç�«',
  'êa' => 'ç�ª',
  'êb' => 'ç‘¿',
  'êc' => 'ç’š',
  'êd' => 'ç’ ',
  'êe' => 'ç’”',
  'êf' => 'ç’’',
  'êg' => 'ç’•',
  'êh' => 'ç’¡',
  'êi' => 'ç”‹',
  'êj' => 'ç–€',
  'êk' => 'ç˜¯',
  'êl' => 'ç˜­',
  'êm' => 'ç˜±',
  'ên' => 'ç˜½',
  'êo' => 'ç˜³',
  'êp' => 'ç˜¼',
  'êq' => 'ç˜µ',
  'êr' => 'ç˜²',
  'ês' => 'ç˜°',
  'êt' => 'çš»',
  'êu' => 'ç›¦',
  'êv' => 'çžš',
  'êw' => 'çž�',
  'êx' => 'çž¡',
  'êy' => 'çžœ',
  'êz' => 'çž›',
  'ê{' => 'çž¢',
  'ê|' => 'çž£',
  'ê}' => 'çž•',
  'ê~' => 'çž™',
  'ê¡' => 'çž—',
  'ê¢' => 'ç£�',
  'ê£' => 'ç£©',
  'ê¤' => 'ç£¥',
  'ê¥' => 'ç£ª',
  'ê¦' => 'ç£ž',
  'ê§' => 'ç££',
  'ê¨' => 'ç£›',
  'ê©' => 'ç£¡',
  'êª' => 'ç£¢',
  'ê«' => 'ç£­',
  'ê¬' => 'ç£Ÿ',
  'ê­' => 'ç£ ',
  'ê®' => 'ç¦¤',
  'ê¯' => 'ç©„',
  'ê°' => 'ç©ˆ',
  'ê±' => 'ç©‡',
  'ê²' => 'çª¶',
  'ê³' => 'çª¸',
  'ê´' => 'çªµ',
  'êµ' => 'çª±',
  'ê¶' => 'çª·',
  'ê·' => 'ç¯ž',
  'ê¸' => 'ç¯£',
  'ê¹' => 'ç¯§',
  'êº' => 'ç¯�',
  'ê»' => 'ç¯•',
  'ê¼' => 'ç¯¥',
  'ê½' => 'ç¯š',
  'ê¾' => 'ç¯¨',
  'ê¿' => 'ç¯¹',
  'êÀ' => 'ç¯”',
  'êÁ' => 'ç¯ª',
  'êÂ' => 'ç¯¢',
  'êÃ' => 'ç¯œ',
  'êÄ' => 'ç¯«',
  'êÅ' => 'ç¯˜',
  'êÆ' => 'ç¯Ÿ',
  'êÇ' => 'ç³’',
  'êÈ' => 'ç³”',
  'êÉ' => 'ç³—',
  'êÊ' => 'ç³�',
  'êË' => 'ç³‘',
  'êÌ' => 'ç¸’',
  'êÍ' => 'ç¸¡',
  'êÎ' => 'ç¸—',
  'êÏ' => 'ç¸Œ',
  'êÐ' => 'ç¸Ÿ',
  'êÑ' => 'ç¸ ',
  'êÒ' => 'ç¸“',
  'êÓ' => 'ç¸Ž',
  'êÔ' => 'ç¸œ',
  'êÕ' => 'ç¸•',
  'êÖ' => 'ç¸š',
  'ê×' => 'ç¸¢',
  'êØ' => 'ç¸‹',
  'êÙ' => 'ç¸�',
  'êÚ' => 'ç¸–',
  'êÛ' => 'ç¸�',
  'êÜ' => 'ç¸”',
  'êÝ' => 'ç¸¥',
  'êÞ' => 'ç¸¤',
  'êß' => 'ç½ƒ',
  'êà' => 'ç½»',
  'êá' => 'ç½¼',
  'êâ' => 'ç½º',
  'êã' => 'ç¾±',
  'êä' => 'ç¿¯',
  'êå' => 'è€ª',
  'êæ' => 'è€©',
  'êç' => 'è�¬',
  'êè' => 'è†±',
  'êé' => 'è†¦',
  'êê' => 'è†®',
  'êë' => 'è†¹',
  'êì' => 'è†µ',
  'êí' => 'è†«',
  'êî' => 'è†°',
  'êï' => 'è†¬',
  'êð' => 'è†´',
  'êñ' => 'è†²',
  'êò' => 'è†·',
  'êó' => 'è†§',
  'êô' => 'è‡²',
  'êõ' => 'è‰•',
  'êö' => 'è‰–',
  'ê÷' => 'è‰—',
  'êø' => 'è•–',
  'êù' => 'è•…',
  'êú' => 'è•«',
  'êû' => 'è•�',
  'êü' => 'è•“',
  'êý' => 'è•¡',
  'êþ' => 'è•˜',
  'ë@' => 'è•€',
  'ëA' => 'è•†',
  'ëB' => 'è•¤',
  'ëC' => 'è•�',
  'ëD' => 'è•¢',
  'ëE' => 'è•„',
  'ëF' => 'è•‘',
  'ëG' => 'è•‡',
  'ëH' => 'è•£',
  'ëI' => 'è”¾',
  'ëJ' => 'è•›',
  'ëK' => 'è•±',
  'ëL' => 'è•Ž',
  'ëM' => 'è•®',
  'ëN' => 'è•µ',
  'ëO' => 'è••',
  'ëP' => 'è•§',
  'ëQ' => 'è• ',
  'ëR' => 'è–Œ',
  'ëS' => 'è•¦',
  'ëT' => 'è•�',
  'ëU' => 'è•”',
  'ëV' => 'è•¥',
  'ëW' => 'è•¬',
  'ëX' => 'è™£',
  'ëY' => 'è™¥',
  'ëZ' => 'è™¤',
  'ë[' => 'èž›',
  'ë\\' => 'èž�',
  'ë]' => 'èž—',
  'ë^' => 'èž“',
  'ë_' => 'èž’',
  'ë`' => 'èžˆ',
  'ëa' => 'èž�',
  'ëb' => 'èž–',
  'ëc' => 'èž˜',
  'ëd' => 'è�¹',
  'ëe' => 'èž‡',
  'ëf' => 'èž£',
  'ëg' => 'èž…',
  'ëh' => 'èž�',
  'ëi' => 'èž‘',
  'ëj' => 'èž�',
  'ëk' => 'èž„',
  'ël' => 'èž”',
  'ëm' => 'èžœ',
  'ën' => 'èžš',
  'ëo' => 'èž‰',
  'ëp' => 'è¤ž',
  'ëq' => 'è¤¦',
  'ër' => 'è¤°',
  'ës' => 'è¤­',
  'ët' => 'è¤®',
  'ëu' => 'è¤§',
  'ëv' => 'è¤±',
  'ëw' => 'è¤¢',
  'ëx' => 'è¤©',
  'ëy' => 'è¤£',
  'ëz' => 'è¤¯',
  'ë{' => 'è¤¬',
  'ë|' => 'è¤Ÿ',
  'ë}' => 'è§±',
  'ë~' => 'è« ',
  'ë¡' => 'è«¢',
  'ë¢' => 'è«²',
  'ë£' => 'è«´',
  'ë¤' => 'è«µ',
  'ë¥' => 'è«�',
  'ë¦' => 'è¬”',
  'ë§' => 'è«¤',
  'ë¨' => 'è«Ÿ',
  'ë©' => 'è«°',
  'ëª' => 'è«ˆ',
  'ë«' => 'è«ž',
  'ë¬' => 'è«¡',
  'ë­' => 'è«¨',
  'ë®' => 'è«¿',
  'ë¯' => 'è«¯',
  'ë°' => 'è«»',
  'ë±' => 'è²‘',
  'ë²' => 'è²’',
  'ë³' => 'è²�',
  'ë´' => 'è³µ',
  'ëµ' => 'è³®',
  'ë¶' => 'è³±',
  'ë·' => 'è³°',
  'ë¸' => 'è³³',
  'ë¹' => 'èµ¬',
  'ëº' => 'èµ®',
  'ë»' => 'è¶¥',
  'ë¼' => 'è¶§',
  'ë½' => 'è¸³',
  'ë¾' => 'è¸¾',
  'ë¿' => 'è¸¸',
  'ëÀ' => 'è¹€',
  'ëÁ' => 'è¹…',
  'ëÂ' => 'è¸¶',
  'ëÃ' => 'è¸¼',
  'ëÄ' => 'è¸½',
  'ëÅ' => 'è¹�',
  'ëÆ' => 'è¸°',
  'ëÇ' => 'è¸¿',
  'ëÈ' => 'èº½',
  'ëÉ' => 'è¼¶',
  'ëÊ' => 'è¼®',
  'ëË' => 'è¼µ',
  'ëÌ' => 'è¼²',
  'ëÍ' => 'è¼¹',
  'ëÎ' => 'è¼·',
  'ëÏ' => 'è¼´',
  'ëÐ' => 'é�¶',
  'ëÑ' => 'é�¹',
  'ëÒ' => 'é�»',
  'ëÓ' => 'é‚†',
  'ëÔ' => 'éƒº',
  'ëÕ' => 'é„³',
  'ëÖ' => 'é„µ',
  'ë×' => 'é„¶',
  'ëØ' => 'é†“',
  'ëÙ' => 'é†�',
  'ëÚ' => 'é†‘',
  'ëÛ' => 'é†�',
  'ëÜ' => 'é†�',
  'ëÝ' => 'éŒ§',
  'ëÞ' => 'éŒž',
  'ëß' => 'éŒˆ',
  'ëà' => 'éŒŸ',
  'ëá' => 'éŒ†',
  'ëâ' => 'éŒ�',
  'ëã' => 'é�º',
  'ëä' => 'éŒ¸',
  'ëå' => 'éŒ¼',
  'ëæ' => 'éŒ›',
  'ëç' => 'éŒ£',
  'ëè' => 'éŒ’',
  'ëé' => 'éŒ�',
  'ëê' => 'é�†',
  'ëë' => 'éŒ­',
  'ëì' => 'éŒŽ',
  'ëí' => 'éŒ�',
  'ëî' => 'é‹‹',
  'ëï' => 'éŒ�',
  'ëð' => 'é‹º',
  'ëñ' => 'éŒ¥',
  'ëò' => 'éŒ“',
  'ëó' => 'é‹¹',
  'ëô' => 'é‹·',
  'ëõ' => 'éŒ´',
  'ëö' => 'éŒ‚',
  'ë÷' => 'éŒ¤',
  'ëø' => 'é‹¿',
  'ëù' => 'éŒ©',
  'ëú' => 'éŒ¹',
  'ëû' => 'éŒµ',
  'ëü' => 'éŒª',
  'ëý' => 'éŒ”',
  'ëþ' => 'éŒŒ',
  'ì@' => 'éŒ‹',
  'ìA' => 'é‹¾',
  'ìB' => 'éŒ‰',
  'ìC' => 'éŒ€',
  'ìD' => 'é‹»',
  'ìE' => 'éŒ–',
  'ìF' => 'é–¼',
  'ìG' => 'é—�',
  'ìH' => 'é–¾',
  'ìI' => 'é–¹',
  'ìJ' => 'é–º',
  'ìK' => 'é–¶',
  'ìL' => 'é–¿',
  'ìM' => 'é–µ',
  'ìN' => 'é–½',
  'ìO' => 'éš©',
  'ìP' => 'é›”',
  'ìQ' => 'éœ‹',
  'ìR' => 'éœ’',
  'ìS' => 'éœ�',
  'ìT' => 'éž™',
  'ìU' => 'éž—',
  'ìV' => 'éž”',
  'ìW' => 'éŸ°',
  'ìX' => 'éŸ¸',
  'ìY' => 'é µ',
  'ìZ' => 'é ¯',
  'ì[' => 'é ²',
  'ì\\' => 'é¤¤',
  'ì]' => 'é¤Ÿ',
  'ì^' => 'é¤§',
  'ì_' => 'é¤©',
  'ì`' => 'é¦ž',
  'ìa' => 'é§®',
  'ìb' => 'é§¬',
  'ìc' => 'é§¥',
  'ìd' => 'é§¤',
  'ìe' => 'é§°',
  'ìf' => 'é§£',
  'ìg' => 'é§ª',
  'ìh' => 'é§©',
  'ìi' => 'é§§',
  'ìj' => 'éª¹',
  'ìk' => 'éª¿',
  'ìl' => 'éª´',
  'ìm' => 'éª»',
  'ìn' => 'é«¶',
  'ìo' => 'é«º',
  'ìp' => 'é«¹',
  'ìq' => 'é«·',
  'ìr' => 'é¬³',
  'ìs' => 'é®€',
  'ìt' => 'é®…',
  'ìu' => 'é®‡',
  'ìv' => 'é­¼',
  'ìw' => 'é­¾',
  'ìx' => 'é­»',
  'ìy' => 'é®‚',
  'ìz' => 'é®“',
  'ì{' => 'é®’',
  'ì|' => 'é®�',
  'ì}' => 'é­º',
  'ì~' => 'é®•',
  'ì¡' => 'é­½',
  'ì¢' => 'é®ˆ',
  'ì£' => 'é´¥',
  'ì¤' => 'é´—',
  'ì¥' => 'é´ ',
  'ì¦' => 'é´ž',
  'ì§' => 'é´”',
  'ì¨' => 'é´©',
  'ì©' => 'é´�',
  'ìª' => 'é´˜',
  'ì«' => 'é´¢',
  'ì¬' => 'é´�',
  'ì­' => 'é´™',
  'ì®' => 'é´Ÿ',
  'ì¯' => 'éºˆ',
  'ì°' => 'éº†',
  'ì±' => 'éº‡',
  'ì²' => 'éº®',
  'ì³' => 'éº­',
  'ì´' => 'é»•',
  'ìµ' => 'é»–',
  'ì¶' => 'é»º',
  'ì·' => 'é¼’',
  'ì¸' => 'é¼½',
  'ì¹' => 'å„¦',
  'ìº' => 'å„¥',
  'ì»' => 'å„¢',
  'ì¼' => 'å„¤',
  'ì½' => 'å„ ',
  'ì¾' => 'å„©',
  'ì¿' => 'å‹´',
  'ìÀ' => 'åš“',
  'ìÁ' => 'åšŒ',
  'ìÂ' => 'åš�',
  'ìÃ' => 'åš†',
  'ìÄ' => 'åš„',
  'ìÅ' => 'åšƒ',
  'ìÆ' => 'å™¾',
  'ìÇ' => 'åš‚',
  'ìÈ' => 'å™¿',
  'ìÉ' => 'åš�',
  'ìÊ' => 'å£–',
  'ìË' => 'å£”',
  'ìÌ' => 'å£�',
  'ìÍ' => 'å£’',
  'ìÎ' => 'å¬­',
  'ìÏ' => 'å¬¥',
  'ìÐ' => 'å¬²',
  'ìÑ' => 'å¬£',
  'ìÒ' => 'å¬¬',
  'ìÓ' => 'å¬§',
  'ìÔ' => 'å¬¦',
  'ìÕ' => 'å¬¯',
  'ìÖ' => 'å¬®',
  'ì×' => 'å­»',
  'ìØ' => 'å¯±',
  'ìÙ' => 'å¯²',
  'ìÚ' => 'å¶·',
  'ìÛ' => 'å¹¬',
  'ìÜ' => 'å¹ª',
  'ìÝ' => 'å¾¾',
  'ìÞ' => 'å¾»',
  'ìß' => 'æ‡ƒ',
  'ìà' => 'æ†µ',
  'ìá' => 'æ†¼',
  'ìâ' => 'æ‡§',
  'ìã' => 'æ‡ ',
  'ìä' => 'æ‡¥',
  'ìå' => 'æ‡¤',
  'ìæ' => 'æ‡¨',
  'ìç' => 'æ‡ž',
  'ìè' => 'æ“¯',
  'ìé' => 'æ“©',
  'ìê' => 'æ“£',
  'ìë' => 'æ“«',
  'ìì' => 'æ“¤',
  'ìí' => 'æ“¨',
  'ìî' => 'æ–�',
  'ìï' => 'æ–€',
  'ìð' => 'æ–¶',
  'ìñ' => 'æ—š',
  'ìò' => 'æ›’',
  'ìó' => 'æª�',
  'ìô' => 'æª–',
  'ìõ' => 'æª�',
  'ìö' => 'æª¥',
  'ì÷' => 'æª‰',
  'ìø' => 'æªŸ',
  'ìù' => 'æª›',
  'ìú' => 'æª¡',
  'ìû' => 'æªž',
  'ìü' => 'æª‡',
  'ìý' => 'æª“',
  'ìþ' => 'æªŽ',
  'í@' => 'æª•',
  'íA' => 'æªƒ',
  'íB' => 'æª¨',
  'íC' => 'æª¤',
  'íD' => 'æª‘',
  'íE' => 'æ©¿',
  'íF' => 'æª¦',
  'íG' => 'æªš',
  'íH' => 'æª…',
  'íI' => 'æªŒ',
  'íJ' => 'æª’',
  'íK' => 'æ­›',
  'íL' => 'æ®­',
  'íM' => 'æ°‰',
  'íN' => 'æ¿Œ',
  'íO' => 'æ¾©',
  'íP' => 'æ¿´',
  'íQ' => 'æ¿”',
  'íR' => 'æ¿£',
  'íS' => 'æ¿œ',
  'íT' => 'æ¿­',
  'íU' => 'æ¿§',
  'íV' => 'æ¿¦',
  'íW' => 'æ¿ž',
  'íX' => 'æ¿²',
  'íY' => 'æ¿�',
  'íZ' => 'æ¿¢',
  'í[' => 'æ¿¨',
  'í\\' => 'ç‡¡',
  'í]' => 'ç‡±',
  'í^' => 'ç‡¨',
  'í_' => 'ç‡²',
  'í`' => 'ç‡¤',
  'ía' => 'ç‡°',
  'íb' => 'ç‡¢',
  'íc' => 'ç�³',
  'íd' => 'ç�®',
  'íe' => 'ç�¯',
  'íf' => 'ç’—',
  'íg' => 'ç’²',
  'íh' => 'ç’«',
  'íi' => 'ç’�',
  'íj' => 'ç’ª',
  'ík' => 'ç’­',
  'íl' => 'ç’±',
  'ím' => 'ç’¥',
  'ín' => 'ç’¯',
  'ío' => 'ç”�',
  'íp' => 'ç”‘',
  'íq' => 'ç”’',
  'ír' => 'ç”�',
  'ís' => 'ç–„',
  'ít' => 'ç™ƒ',
  'íu' => 'ç™ˆ',
  'ív' => 'ç™‰',
  'íw' => 'ç™‡',
  'íx' => 'çš¤',
  'íy' => 'ç›©',
  'íz' => 'çžµ',
  'í{' => 'çž«',
  'í|' => 'çž²',
  'í}' => 'çž·',
  'í~' => 'çž¶',
  'í¡' => 'çž´',
  'í¢' => 'çž±',
  'í£' => 'çž¨',
  'í¤' => 'çŸ°',
  'í¥' => 'ç£³',
  'í¦' => 'ç£½',
  'í§' => 'ç¤‚',
  'í¨' => 'ç£»',
  'í©' => 'ç£¼',
  'íª' => 'ç£²',
  'í«' => 'ç¤…',
  'í¬' => 'ç£¹',
  'í­' => 'ç£¾',
  'í®' => 'ç¤„',
  'í¯' => 'ç¦«',
  'í°' => 'ç¦¨',
  'í±' => 'ç©œ',
  'í²' => 'ç©›',
  'í³' => 'ç©–',
  'í´' => 'ç©˜',
  'íµ' => 'ç©”',
  'í¶' => 'ç©š',
  'í·' => 'çª¾',
  'í¸' => 'ç«€',
  'í¹' => 'ç«�',
  'íº' => 'ç°…',
  'í»' => 'ç°�',
  'í¼' => 'ç¯²',
  'í½' => 'ç°€',
  'í¾' => 'ç¯¿',
  'í¿' => 'ç¯»',
  'íÀ' => 'ç°Ž',
  'íÁ' => 'ç¯´',
  'íÂ' => 'ç°‹',
  'íÃ' => 'ç¯³',
  'íÄ' => 'ç°‚',
  'íÅ' => 'ç°‰',
  'íÆ' => 'ç°ƒ',
  'íÇ' => 'ç°�',
  'íÈ' => 'ç¯¸',
  'íÉ' => 'ç¯½',
  'íÊ' => 'ç°†',
  'íË' => 'ç¯°',
  'íÌ' => 'ç¯±',
  'íÍ' => 'ç°�',
  'íÎ' => 'ç°Š',
  'íÏ' => 'ç³¨',
  'íÐ' => 'ç¸­',
  'íÑ' => 'ç¸¼',
  'íÒ' => 'ç¹‚',
  'íÓ' => 'ç¸³',
  'íÔ' => 'é¡ˆ',
  'íÕ' => 'ç¸¸',
  'íÖ' => 'ç¸ª',
  'í×' => 'ç¹‰',
  'íØ' => 'ç¹€',
  'íÙ' => 'ç¹‡',
  'íÚ' => 'ç¸©',
  'íÛ' => 'ç¹Œ',
  'íÜ' => 'ç¸°',
  'íÝ' => 'ç¸»',
  'íÞ' => 'ç¸¶',
  'íß' => 'ç¹„',
  'íà' => 'ç¸º',
  'íá' => 'ç½…',
  'íâ' => 'ç½¿',
  'íã' => 'ç½¾',
  'íä' => 'ç½½',
  'íå' => 'ç¿´',
  'íæ' => 'ç¿²',
  'íç' => 'è€¬',
  'íè' => 'è†»',
  'íé' => 'è‡„',
  'íê' => 'è‡Œ',
  'íë' => 'è‡Š',
  'íì' => 'è‡…',
  'íí' => 'è‡‡',
  'íî' => 'è†¼',
  'íï' => 'è‡©',
  'íð' => 'è‰›',
  'íñ' => 'è‰š',
  'íò' => 'è‰œ',
  'íó' => 'è–ƒ',
  'íô' => 'è–€',
  'íõ' => 'è–�',
  'íö' => 'è–§',
  'í÷' => 'è–•',
  'íø' => 'è– ',
  'íù' => 'è–‹',
  'íú' => 'è–£',
  'íû' => 'è•»',
  'íü' => 'è–¤',
  'íý' => 'è–š',
  'íþ' => 'è–ž',
  'î@' => 'è•·',
  'îA' => 'è•¼',
  'îB' => 'è–‰',
  'îC' => 'è–¡',
  'îD' => 'è•º',
  'îE' => 'è•¸',
  'îF' => 'è•—',
  'îG' => 'è–Ž',
  'îH' => 'è––',
  'îI' => 'è–†',
  'îJ' => 'è–�',
  'îK' => 'è–™',
  'îL' => 'è–�',
  'îM' => 'è–�',
  'îN' => 'è–¢',
  'îO' => 'è–‚',
  'îP' => 'è–ˆ',
  'îQ' => 'è–…',
  'îR' => 'è•¹',
  'îS' => 'è•¶',
  'îT' => 'è–˜',
  'îU' => 'è–�',
  'îV' => 'è–Ÿ',
  'îW' => 'è™¨',
  'îX' => 'èž¾',
  'îY' => 'èžª',
  'îZ' => 'èž­',
  'î[' => 'èŸ…',
  'î\\' => 'èž°',
  'î]' => 'èž¬',
  'î^' => 'èž¹',
  'î_' => 'èžµ',
  'î`' => 'èž¼',
  'îa' => 'èž®',
  'îb' => 'èŸ‰',
  'îc' => 'èŸƒ',
  'îd' => 'èŸ‚',
  'îe' => 'èŸŒ',
  'îf' => 'èž·',
  'îg' => 'èž¯',
  'îh' => 'èŸ„',
  'îi' => 'èŸŠ',
  'îj' => 'èž´',
  'îk' => 'èž¶',
  'îl' => 'èž¿',
  'îm' => 'èž¸',
  'în' => 'èž½',
  'îo' => 'èŸž',
  'îp' => 'èž²',
  'îq' => 'è¤µ',
  'îr' => 'è¤³',
  'îs' => 'è¤¼',
  'ît' => 'è¤¾',
  'îu' => 'è¥�',
  'îv' => 'è¥’',
  'îw' => 'è¤·',
  'îx' => 'è¥‚',
  'îy' => 'è¦­',
  'îz' => 'è¦¯',
  'î{' => 'è¦®',
  'î|' => 'è§²',
  'î}' => 'è§³',
  'î~' => 'è¬ž',
  'î¡' => 'è¬˜',
  'î¢' => 'è¬–',
  'î£' => 'è¬‘',
  'î¤' => 'è¬…',
  'î¥' => 'è¬‹',
  'î¦' => 'è¬¢',
  'î§' => 'è¬�',
  'î¨' => 'è¬’',
  'î©' => 'è¬•',
  'îª' => 'è¬‡',
  'î«' => 'è¬�',
  'î¬' => 'è¬ˆ',
  'î­' => 'è¬†',
  'î®' => 'è¬œ',
  'î¯' => 'è¬“',
  'î°' => 'è¬š',
  'î±' => 'è±�',
  'î²' => 'è±°',
  'î³' => 'è±²',
  'î´' => 'è±±',
  'îµ' => 'è±¯',
  'î¶' => 'è²•',
  'î·' => 'è²”',
  'î¸' => 'è³¹',
  'î¹' => 'èµ¯',
  'îº' => 'è¹Ž',
  'î»' => 'è¹�',
  'î¼' => 'è¹“',
  'î½' => 'è¹�',
  'î¾' => 'è¹Œ',
  'î¿' => 'è¹‡',
  'îÀ' => 'è½ƒ',
  'îÁ' => 'è½€',
  'îÂ' => 'é‚…',
  'îÃ' => 'é�¾',
  'îÄ' => 'é„¸',
  'îÅ' => 'é†š',
  'îÆ' => 'é†¢',
  'îÇ' => 'é†›',
  'îÈ' => 'é†™',
  'îÉ' => 'é†Ÿ',
  'îÊ' => 'é†¡',
  'îË' => 'é†�',
  'îÌ' => 'é† ',
  'îÍ' => 'éŽ¡',
  'îÎ' => 'éŽƒ',
  'îÏ' => 'éŽ¯',
  'îÐ' => 'é�¤',
  'îÑ' => 'é�–',
  'îÒ' => 'é�‡',
  'îÓ' => 'é�¼',
  'îÔ' => 'é�˜',
  'îÕ' => 'é�œ',
  'îÖ' => 'é�¶',
  'î×' => 'é�‰',
  'îØ' => 'é��',
  'îÙ' => 'é�‘',
  'îÚ' => 'é� ',
  'îÛ' => 'é�­',
  'îÜ' => 'éŽ�',
  'îÝ' => 'é�Œ',
  'îÞ' => 'é�ª',
  'îß' => 'é�¹',
  'îà' => 'é�—',
  'îá' => 'é�•',
  'îâ' => 'é�’',
  'îã' => 'é��',
  'îä' => 'é�±',
  'îå' => 'é�·',
  'îæ' => 'é�»',
  'îç' => 'é�¡',
  'îè' => 'é�ž',
  'îé' => 'é�£',
  'îê' => 'é�§',
  'îë' => 'éŽ€',
  'îì' => 'é�Ž',
  'îí' => 'é�™',
  'îî' => 'é—‡',
  'îï' => 'é—€',
  'îð' => 'é—‰',
  'îñ' => 'é—ƒ',
  'îò' => 'é—…',
  'îó' => 'é–·',
  'îô' => 'éš®',
  'îõ' => 'éš°',
  'îö' => 'éš¬',
  'î÷' => 'éœ ',
  'îø' => 'éœŸ',
  'îù' => 'éœ˜',
  'îú' => 'éœ�',
  'îû' => 'éœ™',
  'îü' => 'éžš',
  'îý' => 'éž¡',
  'îþ' => 'éžœ',
  'ï@' => 'éžž',
  'ïA' => 'éž�',
  'ïB' => 'éŸ•',
  'ïC' => 'éŸ”',
  'ïD' => 'éŸ±',
  'ïE' => 'é¡�',
  'ïF' => 'é¡„',
  'ïG' => 'é¡Š',
  'ïH' => 'é¡‰',
  'ïI' => 'é¡…',
  'ïJ' => 'é¡ƒ',
  'ïK' => 'é¤¥',
  'ïL' => 'é¤«',
  'ïM' => 'é¤¬',
  'ïN' => 'é¤ª',
  'ïO' => 'é¤³',
  'ïP' => 'é¤²',
  'ïQ' => 'é¤¯',
  'ïR' => 'é¤­',
  'ïS' => 'é¤±',
  'ïT' => 'é¤°',
  'ïU' => 'é¦˜',
  'ïV' => 'é¦£',
  'ïW' => 'é¦¡',
  'ïX' => 'é¨‚',
  'ïY' => 'é§º',
  'ïZ' => 'é§´',
  'ï[' => 'é§·',
  'ï\\' => 'é§¹',
  'ï]' => 'é§¸',
  'ï^' => 'é§¶',
  'ï_' => 'é§»',
  'ï`' => 'é§½',
  'ïa' => 'é§¾',
  'ïb' => 'é§¼',
  'ïc' => 'é¨ƒ',
  'ïd' => 'éª¾',
  'ïe' => 'é«¾',
  'ïf' => 'é«½',
  'ïg' => 'é¬�',
  'ïh' => 'é«¼',
  'ïi' => 'é­ˆ',
  'ïj' => 'é®š',
  'ïk' => 'é®¨',
  'ïl' => 'é®ž',
  'ïm' => 'é®›',
  'ïn' => 'é®¦',
  'ïo' => 'é®¡',
  'ïp' => 'é®¥',
  'ïq' => 'é®¤',
  'ïr' => 'é®†',
  'ïs' => 'é®¢',
  'ït' => 'é® ',
  'ïu' => 'é®¯',
  'ïv' => 'é´³',
  'ïw' => 'éµ�',
  'ïx' => 'éµ§',
  'ïy' => 'é´¶',
  'ïz' => 'é´®',
  'ï{' => 'é´¯',
  'ï|' => 'é´±',
  'ï}' => 'é´¸',
  'ï~' => 'é´°',
  'ï¡' => 'éµ…',
  'ï¢' => 'éµ‚',
  'ï£' => 'éµƒ',
  'ï¤' => 'é´¾',
  'ï¥' => 'é´·',
  'ï¦' => 'éµ€',
  'ï§' => 'é´½',
  'ï¨' => 'ç¿µ',
  'ï©' => 'é´­',
  'ïª' => 'éºŠ',
  'ï«' => 'éº‰',
  'ï¬' => 'éº�',
  'ï­' => 'éº°',
  'ï®' => 'é»ˆ',
  'ï¯' => 'é»š',
  'ï°' => 'é»»',
  'ï±' => 'é»¿',
  'ï²' => 'é¼¤',
  'ï³' => 'é¼£',
  'ï´' => 'é¼¢',
  'ïµ' => 'é½”',
  'ï¶' => 'é¾ ',
  'ï·' => 'å„±',
  'ï¸' => 'å„­',
  'ï¹' => 'å„®',
  'ïº' => 'åš˜',
  'ï»' => 'åšœ',
  'ï¼' => 'åš—',
  'ï½' => 'åšš',
  'ï¾' => 'åš�',
  'ï¿' => 'åš™',
  'ïÀ' => 'å¥°',
  'ïÁ' => 'å¬¼',
  'ïÂ' => 'å±©',
  'ïÃ' => 'å±ª',
  'ïÄ' => 'å·€',
  'ïÅ' => 'å¹­',
  'ïÆ' => 'å¹®',
  'ïÇ' => 'æ‡˜',
  'ïÈ' => 'æ‡Ÿ',
  'ïÉ' => 'æ‡­',
  'ïÊ' => 'æ‡®',
  'ïË' => 'æ‡±',
  'ïÌ' => 'æ‡ª',
  'ïÍ' => 'æ‡°',
  'ïÎ' => 'æ‡«',
  'ïÏ' => 'æ‡–',
  'ïÐ' => 'æ‡©',
  'ïÑ' => 'æ“¿',
  'ïÒ' => 'æ”„',
  'ïÓ' => 'æ“½',
  'ïÔ' => 'æ“¸',
  'ïÕ' => 'æ”�',
  'ïÖ' => 'æ”ƒ',
  'ï×' => 'æ“¼',
  'ïØ' => 'æ–”',
  'ïÙ' => 'æ—›',
  'ïÚ' => 'æ›š',
  'ïÛ' => 'æ››',
  'ïÜ' => 'æ›˜',
  'ïÝ' => 'æ«…',
  'ïÞ' => 'æª¹',
  'ïß' => 'æª½',
  'ïà' => 'æ«¡',
  'ïá' => 'æ«†',
  'ïâ' => 'æªº',
  'ïã' => 'æª¶',
  'ïä' => 'æª·',
  'ïå' => 'æ«‡',
  'ïæ' => 'æª´',
  'ïç' => 'æª­',
  'ïè' => 'æ­ž',
  'ïé' => 'æ¯‰',
  'ïê' => 'æ°‹',
  'ïë' => 'ç€‡',
  'ïì' => 'ç€Œ',
  'ïí' => 'ç€�',
  'ïî' => 'ç€�',
  'ïï' => 'ç€…',
  'ïð' => 'ç€”',
  'ïñ' => 'ç€Ž',
  'ïò' => 'æ¿¿',
  'ïó' => 'ç€€',
  'ïô' => 'æ¿»',
  'ïõ' => 'ç€¦',
  'ïö' => 'æ¿¼',
  'ï÷' => 'æ¿·',
  'ïø' => 'ç€Š',
  'ïù' => 'çˆ�',
  'ïú' => 'ç‡¿',
  'ïû' => 'ç‡¹',
  'ïü' => 'çˆƒ',
  'ïý' => 'ç‡½',
  'ïþ' => 'ç�¶',
  'ð@' => 'ç’¸',
  'ðA' => 'ç“€',
  'ðB' => 'ç’µ',
  'ðC' => 'ç“�',
  'ðD' => 'ç’¾',
  'ðE' => 'ç’¶',
  'ðF' => 'ç’»',
  'ðG' => 'ç“‚',
  'ðH' => 'ç””',
  'ðI' => 'ç”“',
  'ðJ' => 'ç™œ',
  'ðK' => 'ç™¤',
  'ðL' => 'ç™™',
  'ðM' => 'ç™�',
  'ðN' => 'ç™“',
  'ðO' => 'ç™—',
  'ðP' => 'ç™š',
  'ðQ' => 'çš¦',
  'ðR' => 'çš½',
  'ðS' => 'ç›¬',
  'ðT' => 'çŸ‚',
  'ðU' => 'çžº',
  'ðV' => 'ç£¿',
  'ðW' => 'ç¤Œ',
  'ðX' => 'ç¤“',
  'ðY' => 'ç¤”',
  'ðZ' => 'ç¤‰',
  'ð[' => 'ç¤�',
  'ð\\' => 'ç¤’',
  'ð]' => 'ç¤‘',
  'ð^' => 'ç¦­',
  'ð_' => 'ç¦¬',
  'ð`' => 'ç©Ÿ',
  'ða' => 'ç°œ',
  'ðb' => 'ç°©',
  'ðc' => 'ç°™',
  'ðd' => 'ç° ',
  'ðe' => 'ç°Ÿ',
  'ðf' => 'ç°­',
  'ðg' => 'ç°�',
  'ðh' => 'ç°¦',
  'ði' => 'ç°¨',
  'ðj' => 'ç°¢',
  'ðk' => 'ç°¥',
  'ðl' => 'ç°°',
  'ðm' => 'ç¹œ',
  'ðn' => 'ç¹�',
  'ðo' => 'ç¹–',
  'ðp' => 'ç¹£',
  'ðq' => 'ç¹˜',
  'ðr' => 'ç¹¢',
  'ðs' => 'ç¹Ÿ',
  'ðt' => 'ç¹‘',
  'ðu' => 'ç¹ ',
  'ðv' => 'ç¹—',
  'ðw' => 'ç¹“',
  'ðx' => 'ç¾µ',
  'ðy' => 'ç¾³',
  'ðz' => 'ç¿·',
  'ð{' => 'ç¿¸',
  'ð|' => 'è�µ',
  'ð}' => 'è‡‘',
  'ð~' => 'è‡’',
  'ð¡' => 'è‡�',
  'ð¢' => 'è‰Ÿ',
  'ð£' => 'è‰ž',
  'ð¤' => 'è–´',
  'ð¥' => 'è—†',
  'ð¦' => 'è—€',
  'ð§' => 'è—ƒ',
  'ð¨' => 'è—‚',
  'ð©' => 'è–³',
  'ðª' => 'è–µ',
  'ð«' => 'è–½',
  'ð¬' => 'è—‡',
  'ð­' => 'è—„',
  'ð®' => 'è–¿',
  'ð¯' => 'è—‹',
  'ð°' => 'è—Ž',
  'ð±' => 'è—ˆ',
  'ð²' => 'è—…',
  'ð³' => 'è–±',
  'ð´' => 'è–¶',
  'ðµ' => 'è—’',
  'ð¶' => 'è˜¤',
  'ð·' => 'è–¸',
  'ð¸' => 'è–·',
  'ð¹' => 'è–¾',
  'ðº' => 'è™©',
  'ð»' => 'èŸ§',
  'ð¼' => 'èŸ¦',
  'ð½' => 'èŸ¢',
  'ð¾' => 'èŸ›',
  'ð¿' => 'èŸ«',
  'ðÀ' => 'èŸª',
  'ðÁ' => 'èŸ¥',
  'ðÂ' => 'èŸŸ',
  'ðÃ' => 'èŸ³',
  'ðÄ' => 'èŸ¤',
  'ðÅ' => 'èŸ”',
  'ðÆ' => 'èŸœ',
  'ðÇ' => 'èŸ“',
  'ðÈ' => 'èŸ­',
  'ðÉ' => 'èŸ˜',
  'ðÊ' => 'èŸ£',
  'ðË' => 'èž¤',
  'ðÌ' => 'èŸ—',
  'ðÍ' => 'èŸ™',
  'ðÎ' => 'è �',
  'ðÏ' => 'èŸ´',
  'ðÐ' => 'èŸ¨',
  'ðÑ' => 'èŸ�',
  'ðÒ' => 'è¥“',
  'ðÓ' => 'è¥‹',
  'ðÔ' => 'è¥�',
  'ðÕ' => 'è¥Œ',
  'ðÖ' => 'è¥†',
  'ð×' => 'è¥�',
  'ðØ' => 'è¥‘',
  'ðÙ' => 'è¥‰',
  'ðÚ' => 'è¬ª',
  'ðÛ' => 'è¬§',
  'ðÜ' => 'è¬£',
  'ðÝ' => 'è¬³',
  'ðÞ' => 'è¬°',
  'ðß' => 'è¬µ',
  'ðà' => 'è­‡',
  'ðá' => 'è¬¯',
  'ðâ' => 'è¬¼',
  'ðã' => 'è¬¾',
  'ðä' => 'è¬±',
  'ðå' => 'è¬¥',
  'ðæ' => 'è¬·',
  'ðç' => 'è¬¦',
  'ðè' => 'è¬¶',
  'ðé' => 'è¬®',
  'ðê' => 'è¬¤',
  'ðë' => 'è¬»',
  'ðì' => 'è¬½',
  'ðí' => 'è¬º',
  'ðî' => 'è±‚',
  'ðï' => 'è±µ',
  'ðð' => 'è²™',
  'ðñ' => 'è²˜',
  'ðò' => 'è²—',
  'ðó' => 'è³¾',
  'ðô' => 'è´„',
  'ðõ' => 'è´‚',
  'ðö' => 'è´€',
  'ð÷' => 'è¹œ',
  'ðø' => 'è¹¢',
  'ðù' => 'è¹ ',
  'ðú' => 'è¹—',
  'ðû' => 'è¹–',
  'ðü' => 'è¹ž',
  'ðý' => 'è¹¥',
  'ðþ' => 'è¹§',
  'ñ@' => 'è¹›',
  'ñA' => 'è¹š',
  'ñB' => 'è¹¡',
  'ñC' => 'è¹�',
  'ñD' => 'è¹©',
  'ñE' => 'è¹”',
  'ñF' => 'è½†',
  'ñG' => 'è½‡',
  'ñH' => 'è½ˆ',
  'ñI' => 'è½‹',
  'ñJ' => 'é„¨',
  'ñK' => 'é„º',
  'ñL' => 'é„»',
  'ñM' => 'é„¾',
  'ñN' => 'é†¨',
  'ñO' => 'é†¥',
  'ñP' => 'é†§',
  'ñQ' => 'é†¯',
  'ñR' => 'é†ª',
  'ñS' => 'éŽµ',
  'ñT' => 'éŽŒ',
  'ñU' => 'éŽ’',
  'ñV' => 'éŽ·',
  'ñW' => 'éŽ›',
  'ñX' => 'éŽ�',
  'ñY' => 'éŽ‰',
  'ñZ' => 'éŽ§',
  'ñ[' => 'éŽŽ',
  'ñ\\' => 'éŽª',
  'ñ]' => 'éŽž',
  'ñ^' => 'éŽ¦',
  'ñ_' => 'éŽ•',
  'ñ`' => 'éŽˆ',
  'ña' => 'éŽ™',
  'ñb' => 'éŽŸ',
  'ñc' => 'éŽ�',
  'ñd' => 'éŽ±',
  'ñe' => 'éŽ‘',
  'ñf' => 'éŽ²',
  'ñg' => 'éŽ¤',
  'ñh' => 'éŽ¨',
  'ñi' => 'éŽ´',
  'ñj' => 'éŽ£',
  'ñk' => 'éŽ¥',
  'ñl' => 'é—’',
  'ñm' => 'é—“',
  'ñn' => 'é—‘',
  'ño' => 'éš³',
  'ñp' => 'é›—',
  'ñq' => 'é›š',
  'ñr' => 'å·‚',
  'ñs' => 'é›Ÿ',
  'ñt' => 'é›˜',
  'ñu' => 'é›�',
  'ñv' => 'éœ£',
  'ñw' => 'éœ¢',
  'ñx' => 'éœ¥',
  'ñy' => 'éž¬',
  'ñz' => 'éž®',
  'ñ{' => 'éž¨',
  'ñ|' => 'éž«',
  'ñ}' => 'éž¤',
  'ñ~' => 'éžª',
  'ñ¡' => 'éž¢',
  'ñ¢' => 'éž¥',
  'ñ£' => 'éŸ—',
  'ñ¤' => 'éŸ™',
  'ñ¥' => 'éŸ–',
  'ñ¦' => 'éŸ˜',
  'ñ§' => 'éŸº',
  'ñ¨' => 'é¡�',
  'ñ©' => 'é¡‘',
  'ñª' => 'é¡’',
  'ñ«' => 'é¢¸',
  'ñ¬' => 'é¥�',
  'ñ­' => 'é¤¼',
  'ñ®' => 'é¤º',
  'ñ¯' => 'é¨�',
  'ñ°' => 'é¨‹',
  'ñ±' => 'é¨‰',
  'ñ²' => 'é¨�',
  'ñ³' => 'é¨„',
  'ñ´' => 'é¨‘',
  'ñµ' => 'é¨Š',
  'ñ¶' => 'é¨…',
  'ñ·' => 'é¨‡',
  'ñ¸' => 'é¨†',
  'ñ¹' => 'é«€',
  'ñº' => 'é«œ',
  'ñ»' => 'é¬ˆ',
  'ñ¼' => 'é¬„',
  'ñ½' => 'é¬…',
  'ñ¾' => 'é¬©',
  'ñ¿' => 'é¬µ',
  'ñÀ' => 'é­Š',
  'ñÁ' => 'é­Œ',
  'ñÂ' => 'é­‹',
  'ñÃ' => 'é¯‡',
  'ñÄ' => 'é¯†',
  'ñÅ' => 'é¯ƒ',
  'ñÆ' => 'é®¿',
  'ñÇ' => 'é¯�',
  'ñÈ' => 'é®µ',
  'ñÉ' => 'é®¸',
  'ñÊ' => 'é¯“',
  'ñË' => 'é®¶',
  'ñÌ' => 'é¯„',
  'ñÍ' => 'é®¹',
  'ñÎ' => 'é®½',
  'ñÏ' => 'éµœ',
  'ñÐ' => 'éµ“',
  'ñÑ' => 'éµ�',
  'ñÒ' => 'éµŠ',
  'ñÓ' => 'éµ›',
  'ñÔ' => 'éµ‹',
  'ñÕ' => 'éµ™',
  'ñÖ' => 'éµ–',
  'ñ×' => 'éµŒ',
  'ñØ' => 'éµ—',
  'ñÙ' => 'éµ’',
  'ñÚ' => 'éµ”',
  'ñÛ' => 'éµŸ',
  'ñÜ' => 'éµ˜',
  'ñÝ' => 'éµš',
  'ñÞ' => 'éºŽ',
  'ñß' => 'éºŒ',
  'ñà' => 'é»Ÿ',
  'ñá' => 'é¼�',
  'ñâ' => 'é¼€',
  'ñã' => 'é¼–',
  'ñä' => 'é¼¥',
  'ñå' => 'é¼«',
  'ñæ' => 'é¼ª',
  'ñç' => 'é¼©',
  'ñè' => 'é¼¨',
  'ñé' => 'é½Œ',
  'ñê' => 'é½•',
  'ñë' => 'å„´',
  'ñì' => 'å„µ',
  'ñí' => 'åŠ–',
  'ñî' => 'å‹·',
  'ñï' => 'åŽ´',
  'ñð' => 'åš«',
  'ññ' => 'åš­',
  'ñò' => 'åš¦',
  'ñó' => 'åš§',
  'ñô' => 'åšª',
  'ñõ' => 'åš¬',
  'ñö' => 'å£š',
  'ñ÷' => 'å£�',
  'ñø' => 'å£›',
  'ñù' => 'å¤’',
  'ñú' => 'å¬½',
  'ñû' => 'å¬¾',
  'ñü' => 'å¬¿',
  'ñý' => 'å·ƒ',
  'ñþ' => 'å¹°',
  'ò@' => 'å¾¿',
  'òA' => 'æ‡»',
  'òB' => 'æ”‡',
  'òC' => 'æ”�',
  'òD' => 'æ”�',
  'òE' => 'æ”‰',
  'òF' => 'æ”Œ',
  'òG' => 'æ”Ž',
  'òH' => 'æ–„',
  'òI' => 'æ—ž',
  'òJ' => 'æ—�',
  'òK' => 'æ›ž',
  'òL' => 'æ«§',
  'òM' => 'æ« ',
  'òN' => 'æ«Œ',
  'òO' => 'æ«‘',
  'òP' => 'æ«™',
  'òQ' => 'æ«‹',
  'òR' => 'æ«Ÿ',
  'òS' => 'æ«œ',
  'òT' => 'æ«�',
  'òU' => 'æ««',
  'òV' => 'æ«�',
  'òW' => 'æ«�',
  'òX' => 'æ«ž',
  'òY' => 'æ­ ',
  'òZ' => 'æ®°',
  'ò[' => 'æ°Œ',
  'ò\\' => 'ç€™',
  'ò]' => 'ç€§',
  'ò^' => 'ç€ ',
  'ò_' => 'ç€–',
  'ò`' => 'ç€«',
  'òa' => 'ç€¡',
  'òb' => 'ç€¢',
  'òc' => 'ç€£',
  'òd' => 'ç€©',
  'òe' => 'ç€—',
  'òf' => 'ç€¤',
  'òg' => 'ç€œ',
  'òh' => 'ç€ª',
  'òi' => 'çˆŒ',
  'òj' => 'çˆŠ',
  'òk' => 'çˆ‡',
  'òl' => 'çˆ‚',
  'òm' => 'çˆ…',
  'òn' => 'çŠ¥',
  'òo' => 'çŠ¦',
  'òp' => 'çŠ¤',
  'òq' => 'çŠ£',
  'òr' => 'çŠ¡',
  'òs' => 'ç“‹',
  'òt' => 'ç“…',
  'òu' => 'ç’·',
  'òv' => 'ç“ƒ',
  'òw' => 'ç”–',
  'òx' => 'ç™ ',
  'òy' => 'çŸ‰',
  'òz' => 'çŸŠ',
  'ò{' => 'çŸ„',
  'ò|' => 'çŸ±',
  'ò}' => 'ç¤�',
  'ò~' => 'ç¤›',
  'ò¡' => 'ç¤¡',
  'ò¢' => 'ç¤œ',
  'ò£' => 'ç¤—',
  'ò¤' => 'ç¤ž',
  'ò¥' => 'ç¦°',
  'ò¦' => 'ç©§',
  'ò§' => 'ç©¨',
  'ò¨' => 'ç°³',
  'ò©' => 'ç°¼',
  'òª' => 'ç°¹',
  'ò«' => 'ç°¬',
  'ò¬' => 'ç°»',
  'ò­' => 'ç³¬',
  'ò®' => 'ç³ª',
  'ò¯' => 'ç¹¶',
  'ò°' => 'ç¹µ',
  'ò±' => 'ç¹¸',
  'ò²' => 'ç¹°',
  'ò³' => 'ç¹·',
  'ò´' => 'ç¹¯',
  'òµ' => 'ç¹º',
  'ò¶' => 'ç¹²',
  'ò·' => 'ç¹´',
  'ò¸' => 'ç¹¨',
  'ò¹' => 'ç½‹',
  'òº' => 'ç½Š',
  'ò»' => 'ç¾ƒ',
  'ò¼' => 'ç¾†',
  'ò½' => 'ç¾·',
  'ò¾' => 'ç¿½',
  'ò¿' => 'ç¿¾',
  'òÀ' => 'è�¸',
  'òÁ' => 'è‡—',
  'òÂ' => 'è‡•',
  'òÃ' => 'è‰¤',
  'òÄ' => 'è‰¡',
  'òÅ' => 'è‰£',
  'òÆ' => 'è—«',
  'òÇ' => 'è—±',
  'òÈ' => 'è—­',
  'òÉ' => 'è—™',
  'òÊ' => 'è—¡',
  'òË' => 'è—¨',
  'òÌ' => 'è—š',
  'òÍ' => 'è——',
  'òÎ' => 'è—¬',
  'òÏ' => 'è—²',
  'òÐ' => 'è—¸',
  'òÑ' => 'è—˜',
  'òÒ' => 'è—Ÿ',
  'òÓ' => 'è—£',
  'òÔ' => 'è—œ',
  'òÕ' => 'è—‘',
  'òÖ' => 'è—°',
  'ò×' => 'è—¦',
  'òØ' => 'è—¯',
  'òÙ' => 'è—ž',
  'òÚ' => 'è—¢',
  'òÛ' => 'è €',
  'òÜ' => 'èŸº',
  'òÝ' => 'è ƒ',
  'òÞ' => 'èŸ¶',
  'òß' => 'èŸ·',
  'òà' => 'è ‰',
  'òá' => 'è Œ',
  'òâ' => 'è ‹',
  'òã' => 'è †',
  'òä' => 'èŸ¼',
  'òå' => 'è ˆ',
  'òæ' => 'èŸ¿',
  'òç' => 'è Š',
  'òè' => 'è ‚',
  'òé' => 'è¥¢',
  'òê' => 'è¥š',
  'òë' => 'è¥›',
  'òì' => 'è¥—',
  'òí' => 'è¥¡',
  'òî' => 'è¥œ',
  'òï' => 'è¥˜',
  'òð' => 'è¥�',
  'òñ' => 'è¥™',
  'òò' => 'è¦ˆ',
  'òó' => 'è¦·',
  'òô' => 'è¦¶',
  'òõ' => 'è§¶',
  'òö' => 'è­�',
  'ò÷' => 'è­ˆ',
  'òø' => 'è­Š',
  'òù' => 'è­€',
  'òú' => 'è­“',
  'òû' => 'è­–',
  'òü' => 'è­”',
  'òý' => 'è­‹',
  'òþ' => 'è­•',
  'ó@' => 'è­‘',
  'óA' => 'è­‚',
  'óB' => 'è­’',
  'óC' => 'è­—',
  'óD' => 'è±ƒ',
  'óE' => 'è±·',
  'óF' => 'è±¶',
  'óG' => 'è²š',
  'óH' => 'è´†',
  'óI' => 'è´‡',
  'óJ' => 'è´‰',
  'óK' => 'è¶¬',
  'óL' => 'è¶ª',
  'óM' => 'è¶­',
  'óN' => 'è¶«',
  'óO' => 'è¹­',
  'óP' => 'è¹¸',
  'óQ' => 'è¹³',
  'óR' => 'è¹ª',
  'óS' => 'è¹¯',
  'óT' => 'è¹»',
  'óU' => 'è»‚',
  'óV' => 'è½’',
  'óW' => 'è½‘',
  'óX' => 'è½�',
  'óY' => 'è½�',
  'óZ' => 'è½“',
  'ó[' => 'è¾´',
  'ó\\' => 'é…€',
  'ó]' => 'é„¿',
  'ó^' => 'é†°',
  'ó_' => 'é†­',
  'ó`' => 'é�ž',
  'óa' => 'é�‡',
  'ób' => 'é��',
  'óc' => 'é�‚',
  'ód' => 'é�š',
  'óe' => 'é��',
  'óf' => 'é�¹',
  'óg' => 'é�¬',
  'óh' => 'é�Œ',
  'ói' => 'é�™',
  'ój' => 'éŽ©',
  'ók' => 'é�¦',
  'ól' => 'é�Š',
  'óm' => 'é�”',
  'ón' => 'é�®',
  'óo' => 'é�£',
  'óp' => 'é�•',
  'óq' => 'é�„',
  'ór' => 'é�Ž',
  'ós' => 'é�€',
  'ót' => 'é�’',
  'óu' => 'é�§',
  'óv' => 'é•½',
  'ów' => 'é—š',
  'óx' => 'é—›',
  'óy' => 'é›¡',
  'óz' => 'éœ©',
  'ó{' => 'éœ«',
  'ó|' => 'éœ¬',
  'ó}' => 'éœ¨',
  'ó~' => 'éœ¦',
  'ó¡' => 'éž³',
  'ó¢' => 'éž·',
  'ó£' => 'éž¶',
  'ó¤' => 'éŸ�',
  'ó¥' => 'éŸž',
  'ó¦' => 'éŸŸ',
  'ó§' => 'é¡œ',
  'ó¨' => 'é¡™',
  'ó©' => 'é¡�',
  'óª' => 'é¡—',
  'ó«' => 'é¢¿',
  'ó¬' => 'é¢½',
  'ó­' => 'é¢»',
  'ó®' => 'é¢¾',
  'ó¯' => 'é¥ˆ',
  'ó°' => 'é¥‡',
  'ó±' => 'é¥ƒ',
  'ó²' => 'é¦¦',
  'ó³' => 'é¦§',
  'ó´' => 'é¨š',
  'óµ' => 'é¨•',
  'ó¶' => 'é¨¥',
  'ó·' => 'é¨�',
  'ó¸' => 'é¨¤',
  'ó¹' => 'é¨›',
  'óº' => 'é¨¢',
  'ó»' => 'é¨ ',
  'ó¼' => 'é¨§',
  'ó½' => 'é¨£',
  'ó¾' => 'é¨ž',
  'ó¿' => 'é¨œ',
  'óÀ' => 'é¨”',
  'óÁ' => 'é«‚',
  'óÂ' => 'é¬‹',
  'óÃ' => 'é¬Š',
  'óÄ' => 'é¬Ž',
  'óÅ' => 'é¬Œ',
  'óÆ' => 'é¬·',
  'óÇ' => 'é¯ª',
  'óÈ' => 'é¯«',
  'óÉ' => 'é¯ ',
  'óÊ' => 'é¯ž',
  'óË' => 'é¯¤',
  'óÌ' => 'é¯¦',
  'óÍ' => 'é¯¢',
  'óÎ' => 'é¯°',
  'óÏ' => 'é¯”',
  'óÐ' => 'é¯—',
  'óÑ' => 'é¯¬',
  'óÒ' => 'é¯œ',
  'óÓ' => 'é¯™',
  'óÔ' => 'é¯¥',
  'óÕ' => 'é¯•',
  'óÖ' => 'é¯¡',
  'ó×' => 'é¯š',
  'óØ' => 'éµ·',
  'óÙ' => 'é¶�',
  'óÚ' => 'é¶Š',
  'óÛ' => 'é¶„',
  'óÜ' => 'é¶ˆ',
  'óÝ' => 'éµ±',
  'óÞ' => 'é¶€',
  'óß' => 'éµ¸',
  'óà' => 'é¶†',
  'óá' => 'é¶‹',
  'óâ' => 'é¶Œ',
  'óã' => 'éµ½',
  'óä' => 'éµ«',
  'óå' => 'éµ´',
  'óæ' => 'éµµ',
  'óç' => 'éµ°',
  'óè' => 'éµ©',
  'óé' => 'é¶…',
  'óê' => 'éµ³',
  'óë' => 'éµ»',
  'óì' => 'é¶‚',
  'óí' => 'éµ¯',
  'óî' => 'éµ¹',
  'óï' => 'éµ¿',
  'óð' => 'é¶‡',
  'óñ' => 'éµ¨',
  'óò' => 'éº”',
  'óó' => 'éº‘',
  'óô' => 'é»€',
  'óõ' => 'é»¼',
  'óö' => 'é¼­',
  'ó÷' => 'é½€',
  'óø' => 'é½�',
  'óù' => 'é½�',
  'óú' => 'é½–',
  'óû' => 'é½—',
  'óü' => 'é½˜',
  'óý' => 'åŒ·',
  'óþ' => 'åš²',
  'ô@' => 'åšµ',
  'ôA' => 'åš³',
  'ôB' => 'å££',
  'ôC' => 'å­…',
  'ôD' => 'å·†',
  'ôE' => 'å·‡',
  'ôF' => 'å»®',
  'ôG' => 'å»¯',
  'ôH' => 'å¿€',
  'ôI' => 'å¿�',
  'ôJ' => 'æ‡¹',
  'ôK' => 'æ”—',
  'ôL' => 'æ”–',
  'ôM' => 'æ”•',
  'ôN' => 'æ”“',
  'ôO' => 'æ—Ÿ',
  'ôP' => 'æ›¨',
  'ôQ' => 'æ›£',
  'ôR' => 'æ›¤',
  'ôS' => 'æ«³',
  'ôT' => 'æ«°',
  'ôU' => 'æ«ª',
  'ôV' => 'æ«¨',
  'ôW' => 'æ«¹',
  'ôX' => 'æ«±',
  'ôY' => 'æ«®',
  'ôZ' => 'æ«¯',
  'ô[' => 'ç€¼',
  'ô\\' => 'ç€µ',
  'ô]' => 'ç€¯',
  'ô^' => 'ç€·',
  'ô_' => 'ç€´',
  'ô`' => 'ç€±',
  'ôa' => 'ç�‚',
  'ôb' => 'ç€¸',
  'ôc' => 'ç€¿',
  'ôd' => 'ç€º',
  'ôe' => 'ç€¹',
  'ôf' => 'ç�€',
  'ôg' => 'ç€»',
  'ôh' => 'ç€³',
  'ôi' => 'ç��',
  'ôj' => 'çˆ“',
  'ôk' => 'çˆ”',
  'ôl' => 'çŠ¨',
  'ôm' => 'ç�½',
  'ôn' => 'ç�¼',
  'ôo' => 'ç’º',
  'ôp' => 'çš«',
  'ôq' => 'çšª',
  'ôr' => 'çš¾',
  'ôs' => 'ç›­',
  'ôt' => 'çŸŒ',
  'ôu' => 'çŸŽ',
  'ôv' => 'çŸ�',
  'ôw' => 'çŸ�',
  'ôx' => 'çŸ²',
  'ôy' => 'ç¤¥',
  'ôz' => 'ç¤£',
  'ô{' => 'ç¤§',
  'ô|' => 'ç¤¨',
  'ô}' => 'ç¤¤',
  'ô~' => 'ç¤©',
  'ô¡' => 'ç¦²',
  'ô¢' => 'ç©®',
  'ô£' => 'ç©¬',
  'ô¤' => 'ç©­',
  'ô¥' => 'ç«·',
  'ô¦' => 'ç±‰',
  'ô§' => 'ç±ˆ',
  'ô¨' => 'ç±Š',
  'ô©' => 'ç±‡',
  'ôª' => 'ç±…',
  'ô«' => 'ç³®',
  'ô¬' => 'ç¹»',
  'ô­' => 'ç¹¾',
  'ô®' => 'çº�',
  'ô¯' => 'çº€',
  'ô°' => 'ç¾º',
  'ô±' => 'ç¿¿',
  'ô²' => 'è�¹',
  'ô³' => 'è‡›',
  'ô´' => 'è‡™',
  'ôµ' => 'èˆ‹',
  'ô¶' => 'è‰¨',
  'ô·' => 'è‰©',
  'ô¸' => 'è˜¢',
  'ô¹' => 'è—¿',
  'ôº' => 'è˜�',
  'ô»' => 'è—¾',
  'ô¼' => 'è˜›',
  'ô½' => 'è˜€',
  'ô¾' => 'è—¶',
  'ô¿' => 'è˜„',
  'ôÀ' => 'è˜‰',
  'ôÁ' => 'è˜…',
  'ôÂ' => 'è˜Œ',
  'ôÃ' => 'è—½',
  'ôÄ' => 'è ™',
  'ôÅ' => 'è �',
  'ôÆ' => 'è ‘',
  'ôÇ' => 'è —',
  'ôÈ' => 'è “',
  'ôÉ' => 'è –',
  'ôÊ' => 'è¥£',
  'ôË' => 'è¥¦',
  'ôÌ' => 'è¦¹',
  'ôÍ' => 'è§·',
  'ôÎ' => 'è­ ',
  'ôÏ' => 'è­ª',
  'ôÐ' => 'è­�',
  'ôÑ' => 'è­¨',
  'ôÒ' => 'è­£',
  'ôÓ' => 'è­¥',
  'ôÔ' => 'è­§',
  'ôÕ' => 'è­­',
  'ôÖ' => 'è¶®',
  'ô×' => 'èº†',
  'ôØ' => 'èºˆ',
  'ôÙ' => 'èº„',
  'ôÚ' => 'è½™',
  'ôÛ' => 'è½–',
  'ôÜ' => 'è½—',
  'ôÝ' => 'è½•',
  'ôÞ' => 'è½˜',
  'ôß' => 'è½š',
  'ôà' => 'é‚�',
  'ôá' => 'é…ƒ',
  'ôâ' => 'é…�',
  'ôã' => 'é†·',
  'ôä' => 'é†µ',
  'ôå' => 'é†²',
  'ôæ' => 'é†³',
  'ôç' => 'é�‹',
  'ôè' => 'é�“',
  'ôé' => 'é�»',
  'ôê' => 'é� ',
  'ôë' => 'é��',
  'ôì' => 'é�”',
  'ôí' => 'é�¾',
  'ôî' => 'é�•',
  'ôï' => 'é��',
  'ôð' => 'é�¨',
  'ôñ' => 'é�™',
  'ôò' => 'é��',
  'ôó' => 'é�µ',
  'ôô' => 'é�€',
  'ôõ' => 'é�·',
  'ôö' => 'é�‡',
  'ô÷' => 'é�Ž',
  'ôø' => 'é�–',
  'ôù' => 'é�’',
  'ôú' => 'é�º',
  'ôû' => 'é�‰',
  'ôü' => 'é�¸',
  'ôý' => 'é�Š',
  'ôþ' => 'é�¿',
  'õ@' => 'é�¼',
  'õA' => 'é�Œ',
  'õB' => 'é�¶',
  'õC' => 'é�‘',
  'õD' => 'é�†',
  'õE' => 'é—ž',
  'õF' => 'é— ',
  'õG' => 'é—Ÿ',
  'õH' => 'éœ®',
  'õI' => 'éœ¯',
  'õJ' => 'éž¹',
  'õK' => 'éž»',
  'õL' => 'éŸ½',
  'õM' => 'éŸ¾',
  'õN' => 'é¡ ',
  'õO' => 'é¡¢',
  'õP' => 'é¡£',
  'õQ' => 'é¡Ÿ',
  'õR' => 'é£�',
  'õS' => 'é£‚',
  'õT' => 'é¥�',
  'õU' => 'é¥Ž',
  'õV' => 'é¥™',
  'õW' => 'é¥Œ',
  'õX' => 'é¥‹',
  'õY' => 'é¥“',
  'õZ' => 'é¨²',
  'õ[' => 'é¨´',
  'õ\\' => 'é¨±',
  'õ]' => 'é¨¬',
  'õ^' => 'é¨ª',
  'õ_' => 'é¨¶',
  'õ`' => 'é¨©',
  'õa' => 'é¨®',
  'õb' => 'é¨¸',
  'õc' => 'é¨­',
  'õd' => 'é«‡',
  'õe' => 'é«Š',
  'õf' => 'é«†',
  'õg' => 'é¬�',
  'õh' => 'é¬’',
  'õi' => 'é¬‘',
  'õj' => 'é°‹',
  'õk' => 'é°ˆ',
  'õl' => 'é¯·',
  'õm' => 'é°…',
  'õn' => 'é°’',
  'õo' => 'é¯¸',
  'õp' => 'é±€',
  'õq' => 'é°‡',
  'õr' => 'é°Ž',
  'õs' => 'é°†',
  'õt' => 'é°—',
  'õu' => 'é°”',
  'õv' => 'é°‰',
  'õw' => 'é¶Ÿ',
  'õx' => 'é¶™',
  'õy' => 'é¶¤',
  'õz' => 'é¶�',
  'õ{' => 'é¶’',
  'õ|' => 'é¶˜',
  'õ}' => 'é¶�',
  'õ~' => 'é¶›',
  'õ¡' => 'é¶ ',
  'õ¢' => 'é¶”',
  'õ£' => 'é¶œ',
  'õ¤' => 'é¶ª',
  'õ¥' => 'é¶—',
  'õ¦' => 'é¶¡',
  'õ§' => 'é¶š',
  'õ¨' => 'é¶¢',
  'õ©' => 'é¶¨',
  'õª' => 'é¶ž',
  'õ«' => 'é¶£',
  'õ¬' => 'é¶¿',
  'õ­' => 'é¶©',
  'õ®' => 'é¶–',
  'õ¯' => 'é¶¦',
  'õ°' => 'é¶§',
  'õ±' => 'éº™',
  'õ²' => 'éº›',
  'õ³' => 'éºš',
  'õ´' => 'é»¥',
  'õµ' => 'é»¤',
  'õ¶' => 'é»§',
  'õ·' => 'é»¦',
  'õ¸' => 'é¼°',
  'õ¹' => 'é¼®',
  'õº' => 'é½›',
  'õ»' => 'é½ ',
  'õ¼' => 'é½ž',
  'õ½' => 'é½�',
  'õ¾' => 'é½™',
  'õ¿' => 'é¾‘',
  'õÀ' => 'å„º',
  'õÁ' => 'å„¹',
  'õÂ' => 'åŠ˜',
  'õÃ' => 'åŠ—',
  'õÄ' => 'å›ƒ',
  'õÅ' => 'åš½',
  'õÆ' => 'åš¾',
  'õÇ' => 'å­ˆ',
  'õÈ' => 'å­‡',
  'õÉ' => 'å·‹',
  'õÊ' => 'å·�',
  'õË' => 'å»±',
  'õÌ' => 'æ‡½',
  'õÍ' => 'æ”›',
  'õÎ' => 'æ¬‚',
  'õÏ' => 'æ«¼',
  'õÐ' => 'æ¬ƒ',
  'õÑ' => 'æ«¸',
  'õÒ' => 'æ¬€',
  'õÓ' => 'ç�ƒ',
  'õÔ' => 'ç�„',
  'õÕ' => 'ç�Š',
  'õÖ' => 'ç�ˆ',
  'õ×' => 'ç�‰',
  'õØ' => 'ç�…',
  'õÙ' => 'ç�†',
  'õÚ' => 'çˆ�',
  'õÛ' => 'çˆš',
  'õÜ' => 'çˆ™',
  'õÝ' => 'ç�¾',
  'õÞ' => 'ç”—',
  'õß' => 'ç™ª',
  'õà' => 'çŸ�',
  'õá' => 'ç¤­',
  'õâ' => 'ç¤±',
  'õã' => 'ç¤¯',
  'õä' => 'ç±”',
  'õå' => 'ç±“',
  'õæ' => 'ç³²',
  'õç' => 'çºŠ',
  'õè' => 'çº‡',
  'õé' => 'çºˆ',
  'õê' => 'çº‹',
  'õë' => 'çº†',
  'õì' => 'çº�',
  'õí' => 'ç½�',
  'õî' => 'ç¾»',
  'õï' => 'è€°',
  'õð' => 'è‡�',
  'õñ' => 'è˜˜',
  'õò' => 'è˜ª',
  'õó' => 'è˜¦',
  'õô' => 'è˜Ÿ',
  'õõ' => 'è˜£',
  'õö' => 'è˜œ',
  'õ÷' => 'è˜™',
  'õø' => 'è˜§',
  'õù' => 'è˜®',
  'õú' => 'è˜¡',
  'õû' => 'è˜ ',
  'õü' => 'è˜©',
  'õý' => 'è˜ž',
  'õþ' => 'è˜¥',
  'ö@' => 'è ©',
  'öA' => 'è �',
  'öB' => 'è ›',
  'öC' => 'è  ',
  'öD' => 'è ¤',
  'öE' => 'è œ',
  'öF' => 'è «',
  'öG' => 'è¡Š',
  'öH' => 'è¥­',
  'öI' => 'è¥©',
  'öJ' => 'è¥®',
  'öK' => 'è¥«',
  'öL' => 'è§º',
  'öM' => 'è­¹',
  'öN' => 'è­¸',
  'öO' => 'è­…',
  'öP' => 'è­º',
  'öQ' => 'è­»',
  'öR' => 'è´�',
  'öS' => 'è´”',
  'öT' => 'è¶¯',
  'öU' => 'èºŽ',
  'öV' => 'èºŒ',
  'öW' => 'è½ž',
  'öX' => 'è½›',
  'öY' => 'è½�',
  'öZ' => 'é…†',
  'ö[' => 'é…„',
  'ö\\' => 'é……',
  'ö]' => 'é†¹',
  'ö^' => 'é�¿',
  'ö_' => 'é�»',
  'ö`' => 'é�¶',
  'öa' => 'é�©',
  'öb' => 'é�½',
  'öc' => 'é�¼',
  'öd' => 'é�°',
  'öe' => 'é�¹',
  'öf' => 'é�ª',
  'ög' => 'é�·',
  'öh' => 'é�¬',
  'öi' => 'é‘€',
  'öj' => 'é�±',
  'ök' => 'é—¥',
  'öl' => 'é—¤',
  'öm' => 'é—£',
  'ön' => 'éœµ',
  'öo' => 'éœº',
  'öp' => 'éž¿',
  'öq' => 'éŸ¡',
  'ör' => 'é¡¤',
  'ös' => 'é£‰',
  'öt' => 'é£†',
  'öu' => 'é£€',
  'öv' => 'é¥˜',
  'öw' => 'é¥–',
  'öx' => 'é¨¹',
  'öy' => 'é¨½',
  'öz' => 'é©†',
  'ö{' => 'é©„',
  'ö|' => 'é©‚',
  'ö}' => 'é©�',
  'ö~' => 'é¨º',
  'ö¡' => 'é¨¿',
  'ö¢' => 'é«�',
  'ö£' => 'é¬•',
  'ö¤' => 'é¬—',
  'ö¥' => 'é¬˜',
  'ö¦' => 'é¬–',
  'ö§' => 'é¬º',
  'ö¨' => 'é­’',
  'ö©' => 'é°«',
  'öª' => 'é°�',
  'ö«' => 'é°œ',
  'ö¬' => 'é°¬',
  'ö­' => 'é°£',
  'ö®' => 'é°¨',
  'ö¯' => 'é°©',
  'ö°' => 'é°¤',
  'ö±' => 'é°¡',
  'ö²' => 'é¶·',
  'ö³' => 'é¶¶',
  'ö´' => 'é¶¼',
  'öµ' => 'é·�',
  'ö¶' => 'é·‡',
  'ö·' => 'é·Š',
  'ö¸' => 'é·�',
  'ö¹' => 'é¶¾',
  'öº' => 'é·…',
  'ö»' => 'é·ƒ',
  'ö¼' => 'é¶»',
  'ö½' => 'é¶µ',
  'ö¾' => 'é·Ž',
  'ö¿' => 'é¶¹',
  'öÀ' => 'é¶º',
  'öÁ' => 'é¶¬',
  'öÂ' => 'é·ˆ',
  'öÃ' => 'é¶±',
  'öÄ' => 'é¶­',
  'öÅ' => 'é·Œ',
  'öÆ' => 'é¶³',
  'öÇ' => 'é·�',
  'öÈ' => 'é¶²',
  'öÉ' => 'é¹º',
  'öÊ' => 'éºœ',
  'öË' => 'é»«',
  'öÌ' => 'é»®',
  'öÍ' => 'é»­',
  'öÎ' => 'é¼›',
  'öÏ' => 'é¼˜',
  'öÐ' => 'é¼š',
  'öÑ' => 'é¼±',
  'öÒ' => 'é½Ž',
  'öÓ' => 'é½¥',
  'öÔ' => 'é½¤',
  'öÕ' => 'é¾’',
  'öÖ' => 'äº¹',
  'ö×' => 'å›†',
  'öØ' => 'å›…',
  'öÙ' => 'å›‹',
  'öÚ' => 'å¥±',
  'öÛ' => 'å­‹',
  'öÜ' => 'å­Œ',
  'öÝ' => 'å·•',
  'öÞ' => 'å·‘',
  'öß' => 'å»²',
  'öà' => 'æ”¡',
  'öá' => 'æ” ',
  'öâ' => 'æ”¦',
  'öã' => 'æ”¢',
  'öä' => 'æ¬‹',
  'öå' => 'æ¬ˆ',
  'öæ' => 'æ¬‰',
  'öç' => 'æ°�',
  'öè' => 'ç�•',
  'öé' => 'ç�–',
  'öê' => 'ç�—',
  'öë' => 'ç�’',
  'öì' => 'çˆž',
  'öí' => 'çˆŸ',
  'öî' => 'çŠ©',
  'öï' => 'ç�¿',
  'öð' => 'ç“˜',
  'öñ' => 'ç“•',
  'öò' => 'ç“™',
  'öó' => 'ç“—',
  'öô' => 'ç™­',
  'öõ' => 'çš­',
  'öö' => 'ç¤µ',
  'ö÷' => 'ç¦´',
  'öø' => 'ç©°',
  'öù' => 'ç©±',
  'öú' => 'ç±—',
  'öû' => 'ç±œ',
  'öü' => 'ç±™',
  'öý' => 'ç±›',
  'öþ' => 'ç±š',
  '÷@' => 'ç³´',
  '÷A' => 'ç³±',
  '÷B' => 'çº‘',
  '÷C' => 'ç½�',
  '÷D' => 'ç¾‡',
  '÷E' => 'è‡ž',
  '÷F' => 'è‰«',
  '÷G' => 'è˜´',
  '÷H' => 'è˜µ',
  '÷I' => 'è˜³',
  '÷J' => 'è˜¬',
  '÷K' => 'è˜²',
  '÷L' => 'è˜¶',
  '÷M' => 'è ¬',
  '÷N' => 'è ¨',
  '÷O' => 'è ¦',
  '÷P' => 'è ª',
  '÷Q' => 'è ¥',
  '÷R' => 'è¥±',
  '÷S' => 'è¦¿',
  '÷T' => 'è¦¾',
  '÷U' => 'è§»',
  '÷V' => 'è­¾',
  '÷W' => 'è®„',
  '÷X' => 'è®‚',
  '÷Y' => 'è®†',
  '÷Z' => 'è®…',
  '÷[' => 'è­¿',
  '÷\\' => 'è´•',
  '÷]' => 'èº•',
  '÷^' => 'èº”',
  '÷_' => 'èºš',
  '÷`' => 'èº’',
  '÷a' => 'èº�',
  '÷b' => 'èº–',
  '÷c' => 'èº—',
  '÷d' => 'è½ ',
  '÷e' => 'è½¢',
  '÷f' => 'é…‡',
  '÷g' => 'é‘Œ',
  '÷h' => 'é‘�',
  '÷i' => 'é‘Š',
  '÷j' => 'é‘‹',
  '÷k' => 'é‘�',
  '÷l' => 'é‘‡',
  '÷m' => 'é‘…',
  '÷n' => 'é‘ˆ',
  '÷o' => 'é‘‰',
  '÷p' => 'é‘†',
  '÷q' => 'éœ¿',
  '÷r' => 'éŸ£',
  '÷s' => 'é¡ª',
  '÷t' => 'é¡©',
  '÷u' => 'é£‹',
  '÷v' => 'é¥”',
  '÷w' => 'é¥›',
  '÷x' => 'é©Ž',
  '÷y' => 'é©“',
  '÷z' => 'é©”',
  '÷{' => 'é©Œ',
  '÷|' => 'é©�',
  '÷}' => 'é©ˆ',
  '÷~' => 'é©Š',
  '÷¡' => 'é©‰',
  '÷¢' => 'é©’',
  '÷£' => 'é©�',
  '÷¤' => 'é«�',
  '÷¥' => 'é¬™',
  '÷¦' => 'é¬«',
  '÷§' => 'é¬»',
  '÷¨' => 'é­–',
  '÷©' => 'é­•',
  '÷ª' => 'é±†',
  '÷«' => 'é±ˆ',
  '÷¬' => 'é°¿',
  '÷­' => 'é±„',
  '÷®' => 'é°¹',
  '÷¯' => 'é°³',
  '÷°' => 'é±�',
  '÷±' => 'é°¼',
  '÷²' => 'é°·',
  '÷³' => 'é°´',
  '÷´' => 'é°²',
  '÷µ' => 'é°½',
  '÷¶' => 'é°¶',
  '÷·' => 'é·›',
  '÷¸' => 'é·’',
  '÷¹' => 'é·ž',
  '÷º' => 'é·š',
  '÷»' => 'é·‹',
  '÷¼' => 'é·�',
  '÷½' => 'é·œ',
  '÷¾' => 'é·‘',
  '÷¿' => 'é·Ÿ',
  '÷À' => 'é·©',
  '÷Á' => 'é·™',
  '÷Â' => 'é·˜',
  '÷Ã' => 'é·–',
  '÷Ä' => 'é·µ',
  '÷Å' => 'é·•',
  '÷Æ' => 'é·�',
  '÷Ç' => 'éº¶',
  '÷È' => 'é»°',
  '÷É' => 'é¼µ',
  '÷Ê' => 'é¼³',
  '÷Ë' => 'é¼²',
  '÷Ì' => 'é½‚',
  '÷Í' => 'é½«',
  '÷Î' => 'é¾•',
  '÷Ï' => 'é¾¢',
  '÷Ð' => 'å„½',
  '÷Ñ' => 'åŠ™',
  '÷Ò' => 'å£¨',
  '÷Ó' => 'å£§',
  '÷Ô' => 'å¥²',
  '÷Õ' => 'å­�',
  '÷Ö' => 'å·˜',
  '÷×' => 'è ¯',
  '÷Ø' => 'å½�',
  '÷Ù' => 'æˆ�',
  '÷Ú' => 'æˆƒ',
  '÷Û' => 'æˆ„',
  '÷Ü' => 'æ”©',
  '÷Ý' => 'æ”¥',
  '÷Þ' => 'æ––',
  '÷ß' => 'æ›«',
  '÷à' => 'æ¬‘',
  '÷á' => 'æ¬’',
  '÷â' => 'æ¬�',
  '÷ã' => 'æ¯Š',
  '÷ä' => 'ç�›',
  '÷å' => 'ç�š',
  '÷æ' => 'çˆ¢',
  '÷ç' => 'çŽ‚',
  '÷è' => 'çŽ�',
  '÷é' => 'çŽƒ',
  '÷ê' => 'ç™°',
  '÷ë' => 'çŸ”',
  '÷ì' => 'ç±§',
  '÷í' => 'ç±¦',
  '÷î' => 'çº•',
  '÷ï' => 'è‰¬',
  '÷ð' => 'è˜º',
  '÷ñ' => 'è™€',
  '÷ò' => 'è˜¹',
  '÷ó' => 'è˜¼',
  '÷ô' => 'è˜±',
  '÷õ' => 'è˜»',
  '÷ö' => 'è˜¾',
  '÷÷' => 'è °',
  '÷ø' => 'è ²',
  '÷ù' => 'è ®',
  '÷ú' => 'è ³',
  '÷û' => 'è¥¶',
  '÷ü' => 'è¥´',
  '÷ý' => 'è¥³',
  '÷þ' => 'è§¾',
  'ø@' => 'è®Œ',
  'øA' => 'è®Ž',
  'øB' => 'è®‹',
  'øC' => 'è®ˆ',
  'øD' => 'è±…',
  'øE' => 'è´™',
  'øF' => 'èº˜',
  'øG' => 'è½¤',
  'øH' => 'è½£',
  'øI' => 'é†¼',
  'øJ' => 'é‘¢',
  'øK' => 'é‘•',
  'øL' => 'é‘�',
  'øM' => 'é‘—',
  'øN' => 'é‘ž',
  'øO' => 'éŸ„',
  'øP' => 'éŸ…',
  'øQ' => 'é €',
  'øR' => 'é©–',
  'øS' => 'é©™',
  'øT' => 'é¬ž',
  'øU' => 'é¬Ÿ',
  'øV' => 'é¬ ',
  'øW' => 'é±’',
  'øX' => 'é±˜',
  'øY' => 'é±�',
  'øZ' => 'é±Š',
  'ø[' => 'é±�',
  'ø\\' => 'é±‹',
  'ø]' => 'é±•',
  'ø^' => 'é±™',
  'ø_' => 'é±Œ',
  'ø`' => 'é±Ž',
  'øa' => 'é·»',
  'øb' => 'é··',
  'øc' => 'é·¯',
  'ød' => 'é·£',
  'øe' => 'é·«',
  'øf' => 'é·¸',
  'øg' => 'é·¤',
  'øh' => 'é·¶',
  'øi' => 'é·¡',
  'øj' => 'é·®',
  'øk' => 'é·¦',
  'øl' => 'é·²',
  'øm' => 'é·°',
  'øn' => 'é·¢',
  'øo' => 'é·¬',
  'øp' => 'é·´',
  'øq' => 'é·³',
  'ør' => 'é·¨',
  'øs' => 'é·­',
  'øt' => 'é»‚',
  'øu' => 'é»�',
  'øv' => 'é»²',
  'øw' => 'é»³',
  'øx' => 'é¼†',
  'øy' => 'é¼œ',
  'øz' => 'é¼¸',
  'ø{' => 'é¼·',
  'ø|' => 'é¼¶',
  'ø}' => 'é½ƒ',
  'ø~' => 'é½�',
  'ø¡' => 'é½±',
  'ø¢' => 'é½°',
  'ø£' => 'é½®',
  'ø¤' => 'é½¯',
  'ø¥' => 'å›“',
  'ø¦' => 'å›�',
  'ø§' => 'å­Ž',
  'ø¨' => 'å±­',
  'ø©' => 'æ”­',
  'øª' => 'æ›­',
  'ø«' => 'æ›®',
  'ø¬' => 'æ¬“',
  'ø­' => 'ç�Ÿ',
  'ø®' => 'ç�¡',
  'ø¯' => 'ç��',
  'ø°' => 'ç� ',
  'ø±' => 'çˆ£',
  'ø²' => 'ç“›',
  'ø³' => 'ç“¥',
  'ø´' => 'çŸ•',
  'øµ' => 'ç¤¸',
  'ø¶' => 'ç¦·',
  'ø·' => 'ç¦¶',
  'ø¸' => 'ç±ª',
  'ø¹' => 'çº—',
  'øº' => 'ç¾‰',
  'ø»' => 'è‰­',
  'ø¼' => 'è™ƒ',
  'ø½' => 'è ¸',
  'ø¾' => 'è ·',
  'ø¿' => 'è µ',
  'øÀ' => 'è¡‹',
  'øÁ' => 'è®”',
  'øÂ' => 'è®•',
  'øÃ' => 'èºž',
  'øÄ' => 'èºŸ',
  'øÅ' => 'èº ',
  'øÆ' => 'èº�',
  'øÇ' => 'é†¾',
  'øÈ' => 'é†½',
  'øÉ' => 'é‡‚',
  'øÊ' => 'é‘«',
  'øË' => 'é‘¨',
  'øÌ' => 'é‘©',
  'øÍ' => 'é›¥',
  'øÎ' => 'é�†',
  'øÏ' => 'é�ƒ',
  'øÐ' => 'é�‡',
  'øÑ' => 'éŸ‡',
  'øÒ' => 'éŸ¥',
  'øÓ' => 'é©ž',
  'øÔ' => 'é«•',
  'øÕ' => 'é­™',
  'øÖ' => 'é±£',
  'ø×' => 'é±§',
  'øØ' => 'é±¦',
  'øÙ' => 'é±¢',
  'øÚ' => 'é±ž',
  'øÛ' => 'é± ',
  'øÜ' => 'é¸‚',
  'øÝ' => 'é·¾',
  'øÞ' => 'é¸‡',
  'øß' => 'é¸ƒ',
  'øà' => 'é¸†',
  'øá' => 'é¸…',
  'øâ' => 'é¸€',
  'øã' => 'é¸�',
  'øä' => 'é¸‰',
  'øå' => 'é·¿',
  'øæ' => 'é·½',
  'øç' => 'é¸„',
  'øè' => 'éº ',
  'øé' => 'é¼ž',
  'øê' => 'é½†',
  'øë' => 'é½´',
  'øì' => 'é½µ',
  'øí' => 'é½¶',
  'øî' => 'å›”',
  'øï' => 'æ”®',
  'øð' => 'æ–¸',
  'øñ' => 'æ¬˜',
  'øò' => 'æ¬™',
  'øó' => 'æ¬—',
  'øô' => 'æ¬š',
  'øõ' => 'ç�¢',
  'øö' => 'çˆ¦',
  'ø÷' => 'çŠª',
  'øø' => 'çŸ˜',
  'øù' => 'çŸ™',
  'øú' => 'ç¤¹',
  'øû' => 'ç±©',
  'øü' => 'ç±«',
  'øý' => 'ç³¶',
  'øþ' => 'çºš',
  'ù@' => 'çº˜',
  'ùA' => 'çº›',
  'ùB' => 'çº™',
  'ùC' => 'è‡ ',
  'ùD' => 'è‡¡',
  'ùE' => 'è™†',
  'ùF' => 'è™‡',
  'ùG' => 'è™ˆ',
  'ùH' => 'è¥¹',
  'ùI' => 'è¥º',
  'ùJ' => 'è¥¼',
  'ùK' => 'è¥»',
  'ùL' => 'è§¿',
  'ùM' => 'è®˜',
  'ùN' => 'è®™',
  'ùO' => 'èº¥',
  'ùP' => 'èº¤',
  'ùQ' => 'èº£',
  'ùR' => 'é‘®',
  'ùS' => 'é‘­',
  'ùT' => 'é‘¯',
  'ùU' => 'é‘±',
  'ùV' => 'é‘³',
  'ùW' => 'é�‰',
  'ùX' => 'é¡²',
  'ùY' => 'é¥Ÿ',
  'ùZ' => 'é±¨',
  'ù[' => 'é±®',
  'ù\\' => 'é±­',
  'ù]' => 'é¸‹',
  'ù^' => 'é¸�',
  'ù_' => 'é¸�',
  'ù`' => 'é¸�',
  'ùa' => 'é¸’',
  'ùb' => 'é¸‘',
  'ùc' => 'éº¡',
  'ùd' => 'é»µ',
  'ùe' => 'é¼‰',
  'ùf' => 'é½‡',
  'ùg' => 'é½¸',
  'ùh' => 'é½»',
  'ùi' => 'é½º',
  'ùj' => 'é½¹',
  'ùk' => 'åœž',
  'ùl' => 'ç�¦',
  'ùm' => 'ç±¯',
  'ùn' => 'è ¼',
  'ùo' => 'è¶²',
  'ùp' => 'èº¦',
  'ùq' => 'é‡ƒ',
  'ùr' => 'é‘´',
  'ùs' => 'é‘¸',
  'ùt' => 'é‘¶',
  'ùu' => 'é‘µ',
  'ùv' => 'é© ',
  'ùw' => 'é±´',
  'ùx' => 'é±³',
  'ùy' => 'é±±',
  'ùz' => 'é±µ',
  'ù{' => 'é¸”',
  'ù|' => 'é¸“',
  'ù}' => 'é»¶',
  'ù~' => 'é¼Š',
  'ù¡' => 'é¾¤',
  'ù¢' => 'ç�¨',
  'ù£' => 'ç�¥',
  'ù¤' => 'ç³·',
  'ù¥' => 'è™ª',
  'ù¦' => 'è ¾',
  'ù§' => 'è ½',
  'ù¨' => 'è ¿',
  'ù©' => 'è®ž',
  'ùª' => 'è²œ',
  'ù«' => 'èº©',
  'ù¬' => 'è»‰',
  'ù­' => 'é�‹',
  'ù®' => 'é¡³',
  'ù¯' => 'é¡´',
  'ù°' => 'é£Œ',
  'ù±' => 'é¥¡',
  'ù²' => 'é¦«',
  'ù³' => 'é©¤',
  'ù´' => 'é©¦',
  'ùµ' => 'é©§',
  'ù¶' => 'é¬¤',
  'ù·' => 'é¸•',
  'ù¸' => 'é¸—',
  'ù¹' => 'é½ˆ',
  'ùº' => 'æˆ‡',
  'ù»' => 'æ¬ž',
  'ù¼' => 'çˆ§',
  'ù½' => 'è™Œ',
  'ù¾' => 'èº¨',
  'ù¿' => 'é’‚',
  'ùÀ' => 'é’€',
  'ùÁ' => 'é’�',
  'ùÂ' => 'é©©',
  'ùÃ' => 'é©¨',
  'ùÄ' => 'é¬®',
  'ùÅ' => 'é¸™',
  'ùÆ' => 'çˆ©',
  'ùÇ' => 'è™‹',
  'ùÈ' => 'è®Ÿ',
  'ùÉ' => 'é’ƒ',
  'ùÊ' => 'é±¹',
  'ùË' => 'éº·',
  'ùÌ' => 'ç™µ',
  'ùÍ' => 'é©«',
  'ùÎ' => 'é±º',
  'ùÏ' => 'é¸�',
  'ùÐ' => 'ç�©',
  'ùÑ' => 'ç�ª',
  'ùÒ' => 'éº¤',
  'ùÓ' => 'é½¾',
  'ùÔ' => 'é½‰',
  'ùÕ' => 'é¾˜',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => '%',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
  '€' => 'Â€',
  '�' => 'Â�',
  '‚' => 'Â‚',
  'ƒ' => 'Âƒ',
  '„' => 'Â„',
  '…' => 'Â…',
  '†' => 'Â†',
  '‡' => 'Â‡',
  'ˆ' => 'Âˆ',
  '‰' => 'Â‰',
  'Š' => 'ÂŠ',
  '‹' => 'Â‹',
  'Œ' => 'ÂŒ',
  '�' => 'Â�',
  'Ž' => 'ÂŽ',
  '�' => 'Â�',
  '�' => 'Â�',
  '‘' => 'Â‘',
  '’' => 'Â’',
  '“' => 'Â“',
  '”' => 'Â”',
  '•' => 'Â•',
  '–' => 'Â–',
  '—' => 'Â—',
  '˜' => 'Â˜',
  '™' => 'Â™',
  'š' => 'Âš',
  '›' => 'Â›',
  'œ' => 'Âœ',
  '�' => 'Â�',
  'ž' => 'Âž',
  'Ÿ' => 'ÂŸ',
  ' ' => 'Â ',
  '¡' => 'Â¡',
  '¢' => 'Â¢',
  '£' => 'Â£',
  '¤' => 'â‚¬',
  '¥' => 'Â¥',
  '¦' => 'Å ',
  '§' => 'Â§',
  '¨' => 'Å¡',
  '©' => 'Â©',
  'ª' => 'Âª',
  '«' => 'Â«',
  '¬' => 'Â¬',
  '­' => 'Â­',
  '®' => 'Â®',
  '¯' => 'Â¯',
  '°' => 'Â°',
  '±' => 'Â±',
  '²' => 'Â²',
  '³' => 'Â³',
  '´' => 'Å½',
  'µ' => 'Âµ',
  '¶' => 'Â¶',
  '·' => 'Â·',
  '¸' => 'Å¾',
  '¹' => 'Â¹',
  'º' => 'Âº',
  '»' => 'Â»',
  '¼' => 'Å’',
  '½' => 'Å“',
  '¾' => 'Å¸',
  '¿' => 'Â¿',
  'À' => 'Ã€',
  'Á' => 'Ã�',
  'Â' => 'Ã‚',
  'Ã' => 'Ãƒ',
  'Ä' => 'Ã„',
  'Å' => 'Ã…',
  'Æ' => 'Ã†',
  'Ç' => 'Ã‡',
  'È' => 'Ãˆ',
  'É' => 'Ã‰',
  'Ê' => 'ÃŠ',
  'Ë' => 'Ã‹',
  'Ì' => 'ÃŒ',
  'Í' => 'Ã�',
  'Î' => 'ÃŽ',
  'Ï' => 'Ã�',
  'Ð' => 'Ã�',
  'Ñ' => 'Ã‘',
  'Ò' => 'Ã’',
  'Ó' => 'Ã“',
  'Ô' => 'Ã”',
  'Õ' => 'Ã•',
  'Ö' => 'Ã–',
  '×' => 'Ã—',
  'Ø' => 'Ã˜',
  'Ù' => 'Ã™',
  'Ú' => 'Ãš',
  'Û' => 'Ã›',
  'Ü' => 'Ãœ',
  'Ý' => 'Ã�',
  'Þ' => 'Ãž',
  'ß' => 'ÃŸ',
  'à' => 'Ã ',
  'á' => 'Ã¡',
  'â' => 'Ã¢',
  'ã' => 'Ã£',
  'ä' => 'Ã¤',
  'å' => 'Ã¥',
  'æ' => 'Ã¦',
  'ç' => 'Ã§',
  'è' => 'Ã¨',
  'é' => 'Ã©',
  'ê' => 'Ãª',
  'ë' => 'Ã«',
  'ì' => 'Ã¬',
  'í' => 'Ã­',
  'î' => 'Ã®',
  'ï' => 'Ã¯',
  'ð' => 'Ã°',
  'ñ' => 'Ã±',
  'ò' => 'Ã²',
  'ó' => 'Ã³',
  'ô' => 'Ã´',
  'õ' => 'Ãµ',
  'ö' => 'Ã¶',
  '÷' => 'Ã·',
  'ø' => 'Ã¸',
  'ù' => 'Ã¹',
  'ú' => 'Ãº',
  'û' => 'Ã»',
  'ü' => 'Ã¼',
  'ý' => 'Ã½',
  'þ' => 'Ã¾',
  'ÿ' => 'Ã¿',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => '%',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
  '€' => 'Â€',
  '�' => 'Â�',
  '‚' => 'Â‚',
  'ƒ' => 'Âƒ',
  '„' => 'Â„',
  '…' => 'Â…',
  '†' => 'Â†',
  '‡' => 'Â‡',
  'ˆ' => 'Âˆ',
  '‰' => 'Â‰',
  'Š' => 'ÂŠ',
  '‹' => 'Â‹',
  'Œ' => 'ÂŒ',
  '�' => 'Â�',
  'Ž' => 'ÂŽ',
  '�' => 'Â�',
  '�' => 'Â�',
  '‘' => 'Â‘',
  '’' => 'Â’',
  '“' => 'Â“',
  '”' => 'Â”',
  '•' => 'Â•',
  '–' => 'Â–',
  '—' => 'Â—',
  '˜' => 'Â˜',
  '™' => 'Â™',
  'š' => 'Âš',
  '›' => 'Â›',
  'œ' => 'Âœ',
  '�' => 'Â�',
  'ž' => 'Âž',
  'Ÿ' => 'ÂŸ',
  ' ' => 'Â ',
  '¡' => 'Ä„',
  '¢' => 'Ä’',
  '£' => 'Ä¢',
  '¤' => 'Äª',
  '¥' => 'Ä¨',
  '¦' => 'Ä¶',
  '§' => 'Â§',
  '¨' => 'Ä»',
  '©' => 'Ä�',
  'ª' => 'Å ',
  '«' => 'Å¦',
  '¬' => 'Å½',
  '­' => 'Â­',
  '®' => 'Åª',
  '¯' => 'ÅŠ',
  '°' => 'Â°',
  '±' => 'Ä…',
  '²' => 'Ä“',
  '³' => 'Ä£',
  '´' => 'Ä«',
  'µ' => 'Ä©',
  '¶' => 'Ä·',
  '·' => 'Â·',
  '¸' => 'Ä¼',
  '¹' => 'Ä‘',
  'º' => 'Å¡',
  '»' => 'Å§',
  '¼' => 'Å¾',
  '½' => 'â€•',
  '¾' => 'Å«',
  '¿' => 'Å‹',
  'À' => 'Ä€',
  'Á' => 'Ã�',
  'Â' => 'Ã‚',
  'Ã' => 'Ãƒ',
  'Ä' => 'Ã„',
  'Å' => 'Ã…',
  'Æ' => 'Ã†',
  'Ç' => 'Ä®',
  'È' => 'ÄŒ',
  'É' => 'Ã‰',
  'Ê' => 'Ä˜',
  'Ë' => 'Ã‹',
  'Ì' => 'Ä–',
  'Í' => 'Ã�',
  'Î' => 'ÃŽ',
  'Ï' => 'Ã�',
  'Ð' => 'Ã�',
  'Ñ' => 'Å…',
  'Ò' => 'ÅŒ',
  'Ó' => 'Ã“',
  'Ô' => 'Ã”',
  'Õ' => 'Ã•',
  'Ö' => 'Ã–',
  '×' => 'Å¨',
  'Ø' => 'Ã˜',
  'Ù' => 'Å²',
  'Ú' => 'Ãš',
  'Û' => 'Ã›',
  'Ü' => 'Ãœ',
  'Ý' => 'Ã�',
  'Þ' => 'Ãž',
  'ß' => 'ÃŸ',
  'à' => 'Ä�',
  'á' => 'Ã¡',
  'â' => 'Ã¢',
  'ã' => 'Ã£',
  'ä' => 'Ã¤',
  'å' => 'Ã¥',
  'æ' => 'Ã¦',
  'ç' => 'Ä¯',
  'è' => 'Ä�',
  'é' => 'Ã©',
  'ê' => 'Ä™',
  'ë' => 'Ã«',
  'ì' => 'Ä—',
  'í' => 'Ã­',
  'î' => 'Ã®',
  'ï' => 'Ã¯',
  'ð' => 'Ã°',
  'ñ' => 'Å†',
  'ò' => 'Å�',
  'ó' => 'Ã³',
  'ô' => 'Ã´',
  'õ' => 'Ãµ',
  'ö' => 'Ã¶',
  '÷' => 'Å©',
  'ø' => 'Ã¸',
  'ù' => 'Å³',
  'ú' => 'Ãº',
  'û' => 'Ã»',
  'ü' => 'Ã¼',
  'ý' => 'Ã½',
  'þ' => 'Ã¾',
  'ÿ' => 'Ä¸',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => '%',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
  '€' => 'Ñ’',
  '�' => 'Ð‚',
  '‚' => 'Ñ“',
  'ƒ' => 'Ðƒ',
  '„' => 'Ñ‘',
  '…' => 'Ð�',
  '†' => 'Ñ”',
  '‡' => 'Ð„',
  'ˆ' => 'Ñ•',
  '‰' => 'Ð…',
  'Š' => 'Ñ–',
  '‹' => 'Ð†',
  'Œ' => 'Ñ—',
  '�' => 'Ð‡',
  'Ž' => 'Ñ˜',
  '�' => 'Ðˆ',
  '�' => 'Ñ™',
  '‘' => 'Ð‰',
  '’' => 'Ñš',
  '“' => 'ÐŠ',
  '”' => 'Ñ›',
  '•' => 'Ð‹',
  '–' => 'Ñœ',
  '—' => 'ÐŒ',
  '˜' => 'Ñž',
  '™' => 'ÐŽ',
  'š' => 'ÑŸ',
  '›' => 'Ð�',
  'œ' => 'ÑŽ',
  '�' => 'Ð®',
  'ž' => 'ÑŠ',
  'Ÿ' => 'Ðª',
  ' ' => 'Ð°',
  '¡' => 'Ð�',
  '¢' => 'Ð±',
  '£' => 'Ð‘',
  '¤' => 'Ñ†',
  '¥' => 'Ð¦',
  '¦' => 'Ð´',
  '§' => 'Ð”',
  '¨' => 'Ðµ',
  '©' => 'Ð•',
  'ª' => 'Ñ„',
  '«' => 'Ð¤',
  '¬' => 'Ð³',
  '­' => 'Ð“',
  '®' => 'Â«',
  '¯' => 'Â»',
  '°' => 'â–‘',
  '±' => 'â–’',
  '²' => 'â–“',
  '³' => 'â”‚',
  '´' => 'â”¤',
  'µ' => 'Ñ…',
  '¶' => 'Ð¥',
  '·' => 'Ð¸',
  '¸' => 'Ð˜',
  '¹' => 'â•£',
  'º' => 'â•‘',
  '»' => 'â•—',
  '¼' => 'â•�',
  '½' => 'Ð¹',
  '¾' => 'Ð™',
  '¿' => 'â”�',
  'À' => 'â””',
  'Á' => 'â”´',
  'Â' => 'â”¬',
  'Ã' => 'â”œ',
  'Ä' => 'â”€',
  'Å' => 'â”¼',
  'Æ' => 'Ðº',
  'Ç' => 'Ðš',
  'È' => 'â•š',
  'É' => 'â•”',
  'Ê' => 'â•©',
  'Ë' => 'â•¦',
  'Ì' => 'â• ',
  'Í' => 'â•�',
  'Î' => 'â•¬',
  'Ï' => 'Â¤',
  'Ð' => 'Ð»',
  'Ñ' => 'Ð›',
  'Ò' => 'Ð¼',
  'Ó' => 'Ðœ',
  'Ô' => 'Ð½',
  'Õ' => 'Ð�',
  'Ö' => 'Ð¾',
  '×' => 'Ðž',
  'Ø' => 'Ð¿',
  'Ù' => 'â”˜',
  'Ú' => 'â”Œ',
  'Û' => 'â–ˆ',
  'Ü' => 'â–„',
  'Ý' => 'ÐŸ',
  'Þ' => 'Ñ�',
  'ß' => 'â–€',
  'à' => 'Ð¯',
  'á' => 'Ñ€',
  'â' => 'Ð ',
  'ã' => 'Ñ�',
  'ä' => 'Ð¡',
  'å' => 'Ñ‚',
  'æ' => 'Ð¢',
  'ç' => 'Ñƒ',
  'è' => 'Ð£',
  'é' => 'Ð¶',
  'ê' => 'Ð–',
  'ë' => 'Ð²',
  'ì' => 'Ð’',
  'í' => 'ÑŒ',
  'î' => 'Ð¬',
  'ï' => 'â„–',
  'ð' => 'Â­',
  'ñ' => 'Ñ‹',
  'ò' => 'Ð«',
  'ó' => 'Ð·',
  'ô' => 'Ð—',
  'õ' => 'Ñˆ',
  'ö' => 'Ð¨',
  '÷' => 'Ñ�',
  'ø' => 'Ð­',
  'ù' => 'Ñ‰',
  'ú' => 'Ð©',
  'û' => 'Ñ‡',
  'ü' => 'Ð§',
  'ý' => 'Â§',
  'þ' => 'â– ',
  'ÿ' => 'Â ',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => '%',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
  '€' => 'Ã‡',
  '�' => 'Ã¼',
  '‚' => 'Ã©',
  'ƒ' => 'Ã¢',
  '„' => 'Ã¤',
  '…' => 'Ã ',
  '†' => 'Ã¥',
  '‡' => 'Ã§',
  'ˆ' => 'Ãª',
  '‰' => 'Ã«',
  'Š' => 'Ã¨',
  '‹' => 'Ã¯',
  'Œ' => 'Ã®',
  '�' => 'Ã¬',
  'Ž' => 'Ã„',
  '�' => 'Ã…',
  '�' => 'Ã‰',
  '‘' => 'Ã¦',
  '’' => 'Ã†',
  '“' => 'Ã´',
  '”' => 'Ã¶',
  '•' => 'Ã²',
  '–' => 'Ã»',
  '—' => 'Ã¹',
  '˜' => 'Ã¿',
  '™' => 'Ã–',
  'š' => 'Ãœ',
  '›' => 'Â¢',
  'œ' => 'Â£',
  '�' => 'Â¥',
  'ž' => 'â‚§',
  'Ÿ' => 'Æ’',
  ' ' => 'Ã¡',
  '¡' => 'Ã­',
  '¢' => 'Ã³',
  '£' => 'Ãº',
  '¤' => 'Ã±',
  '¥' => 'Ã‘',
  '¦' => 'Âª',
  '§' => 'Âº',
  '¨' => 'Â¿',
  '©' => 'âŒ�',
  'ª' => 'Â¬',
  '«' => 'Â½',
  '¬' => 'Â¼',
  '­' => 'Â¡',
  '®' => 'Â«',
  '¯' => 'Â»',
  '°' => 'â–‘',
  '±' => 'â–’',
  '²' => 'â–“',
  '³' => 'â”‚',
  '´' => 'â”¤',
  'µ' => 'â•¡',
  '¶' => 'â•¢',
  '·' => 'â•–',
  '¸' => 'â••',
  '¹' => 'â•£',
  'º' => 'â•‘',
  '»' => 'â•—',
  '¼' => 'â•�',
  '½' => 'â•œ',
  '¾' => 'â•›',
  '¿' => 'â”�',
  'À' => 'â””',
  'Á' => 'â”´',
  'Â' => 'â”¬',
  'Ã' => 'â”œ',
  'Ä' => 'â”€',
  'Å' => 'â”¼',
  'Æ' => 'â•ž',
  'Ç' => 'â•Ÿ',
  'È' => 'â•š',
  'É' => 'â•”',
  'Ê' => 'â•©',
  'Ë' => 'â•¦',
  'Ì' => 'â• ',
  'Í' => 'â•�',
  'Î' => 'â•¬',
  'Ï' => 'â•§',
  'Ð' => 'â•¨',
  'Ñ' => 'â•¤',
  'Ò' => 'â•¥',
  'Ó' => 'â•™',
  'Ô' => 'â•˜',
  'Õ' => 'â•’',
  'Ö' => 'â•“',
  '×' => 'â•«',
  'Ø' => 'â•ª',
  'Ù' => 'â”˜',
  'Ú' => 'â”Œ',
  'Û' => 'â–ˆ',
  'Ü' => 'â–„',
  'Ý' => 'â–Œ',
  'Þ' => 'â–�',
  'ß' => 'â–€',
  'à' => 'Î±',
  'á' => 'ÃŸ',
  'â' => 'Î“',
  'ã' => 'Ï€',
  'ä' => 'Î£',
  'å' => 'Ïƒ',
  'æ' => 'Âµ',
  'ç' => 'Ï„',
  'è' => 'Î¦',
  'é' => 'Î˜',
  'ê' => 'Î©',
  'ë' => 'Î´',
  'ì' => 'âˆž',
  'í' => 'Ï†',
  'î' => 'Îµ',
  'ï' => 'âˆ©',
  'ð' => 'â‰¡',
  'ñ' => 'Â±',
  'ò' => 'â‰¥',
  'ó' => 'â‰¤',
  'ô' => 'âŒ ',
  'õ' => 'âŒ¡',
  'ö' => 'Ã·',
  '÷' => 'â‰ˆ',
  'ø' => 'Â°',
  'ù' => 'âˆ™',
  'ú' => 'Â·',
  'û' => 'âˆš',
  'ü' => 'â�¿',
  'ý' => 'Â²',
  'þ' => 'â– ',
  'ÿ' => 'Â ',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => '%',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
  '€' => 'Ã‡',
  '�' => 'Ã¼',
  '‚' => 'Ã©',
  'ƒ' => 'Ã¢',
  '„' => 'Ã¤',
  '…' => 'Ã ',
  '†' => 'Ã¥',
  '‡' => 'Ã§',
  'ˆ' => 'Ãª',
  '‰' => 'Ã«',
  'Š' => 'Ã¨',
  '‹' => 'Ã�',
  'Œ' => 'Ã°',
  '�' => 'Ãž',
  'Ž' => 'Ã„',
  '�' => 'Ã…',
  '�' => 'Ã‰',
  '‘' => 'Ã¦',
  '’' => 'Ã†',
  '“' => 'Ã´',
  '”' => 'Ã¶',
  '•' => 'Ã¾',
  '–' => 'Ã»',
  '—' => 'Ã�',
  '˜' => 'Ã½',
  '™' => 'Ã–',
  'š' => 'Ãœ',
  '›' => 'Ã¸',
  'œ' => 'Â£',
  '�' => 'Ã˜',
  'ž' => 'â‚§',
  'Ÿ' => 'Æ’',
  ' ' => 'Ã¡',
  '¡' => 'Ã­',
  '¢' => 'Ã³',
  '£' => 'Ãº',
  '¤' => 'Ã�',
  '¥' => 'Ã�',
  '¦' => 'Ã“',
  '§' => 'Ãš',
  '¨' => 'Â¿',
  '©' => 'âŒ�',
  'ª' => 'Â¬',
  '«' => 'Â½',
  '¬' => 'Â¼',
  '­' => 'Â¡',
  '®' => 'Â«',
  '¯' => 'Â»',
  '°' => 'â–‘',
  '±' => 'â–’',
  '²' => 'â–“',
  '³' => 'â”‚',
  '´' => 'â”¤',
  'µ' => 'â•¡',
  '¶' => 'â•¢',
  '·' => 'â•–',
  '¸' => 'â••',
  '¹' => 'â•£',
  'º' => 'â•‘',
  '»' => 'â•—',
  '¼' => 'â•�',
  '½' => 'â•œ',
  '¾' => 'â•›',
  '¿' => 'â”�',
  'À' => 'â””',
  'Á' => 'â”´',
  'Â' => 'â”¬',
  'Ã' => 'â”œ',
  'Ä' => 'â”€',
  'Å' => 'â”¼',
  'Æ' => 'â•ž',
  'Ç' => 'â•Ÿ',
  'È' => 'â•š',
  'É' => 'â•”',
  'Ê' => 'â•©',
  'Ë' => 'â•¦',
  'Ì' => 'â• ',
  'Í' => 'â•�',
  'Î' => 'â•¬',
  'Ï' => 'â•§',
  'Ð' => 'â•¨',
  'Ñ' => 'â•¤',
  'Ò' => 'â•¥',
  'Ó' => 'â•™',
  'Ô' => 'â•˜',
  'Õ' => 'â•’',
  'Ö' => 'â•“',
  '×' => 'â•«',
  'Ø' => 'â•ª',
  'Ù' => 'â”˜',
  'Ú' => 'â”Œ',
  'Û' => 'â–ˆ',
  'Ü' => 'â–„',
  'Ý' => 'â–Œ',
  'Þ' => 'â–�',
  'ß' => 'â–€',
  'à' => 'Î±',
  'á' => 'ÃŸ',
  'â' => 'Î“',
  'ã' => 'Ï€',
  'ä' => 'Î£',
  'å' => 'Ïƒ',
  'æ' => 'Âµ',
  'ç' => 'Ï„',
  'è' => 'Î¦',
  'é' => 'Î˜',
  'ê' => 'Î©',
  'ë' => 'Î´',
  'ì' => 'âˆž',
  'í' => 'Ï†',
  'î' => 'Îµ',
  'ï' => 'âˆ©',
  'ð' => 'â‰¡',
  'ñ' => 'Â±',
  'ò' => 'â‰¥',
  'ó' => 'â‰¤',
  'ô' => 'âŒ ',
  'õ' => 'âŒ¡',
  'ö' => 'Ã·',
  '÷' => 'â‰ˆ',
  'ø' => 'Â°',
  'ù' => 'âˆ™',
  'ú' => 'Â·',
  'û' => 'âˆš',
  'ü' => 'â�¿',
  'ý' => 'Â²',
  'þ' => 'â– ',
  'ÿ' => 'Â ',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => '%',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
  '€' => 'Ã‡',
  '�' => 'Ã¼',
  '‚' => 'Ã©',
  'ƒ' => 'Ã¢',
  '„' => 'Ã£',
  '…' => 'Ã ',
  '†' => 'Ã�',
  '‡' => 'Ã§',
  'ˆ' => 'Ãª',
  '‰' => 'ÃŠ',
  'Š' => 'Ã¨',
  '‹' => 'Ã�',
  'Œ' => 'Ã”',
  '�' => 'Ã¬',
  'Ž' => 'Ãƒ',
  '�' => 'Ã‚',
  '�' => 'Ã‰',
  '‘' => 'Ã€',
  '’' => 'Ãˆ',
  '“' => 'Ã´',
  '”' => 'Ãµ',
  '•' => 'Ã²',
  '–' => 'Ãš',
  '—' => 'Ã¹',
  '˜' => 'ÃŒ',
  '™' => 'Ã•',
  'š' => 'Ãœ',
  '›' => 'Â¢',
  'œ' => 'Â£',
  '�' => 'Ã™',
  'ž' => 'â‚§',
  'Ÿ' => 'Ã“',
  ' ' => 'Ã¡',
  '¡' => 'Ã­',
  '¢' => 'Ã³',
  '£' => 'Ãº',
  '¤' => 'Ã±',
  '¥' => 'Ã‘',
  '¦' => 'Âª',
  '§' => 'Âº',
  '¨' => 'Â¿',
  '©' => 'Ã’',
  'ª' => 'Â¬',
  '«' => 'Â½',
  '¬' => 'Â¼',
  '­' => 'Â¡',
  '®' => 'Â«',
  '¯' => 'Â»',
  '°' => 'â–‘',
  '±' => 'â–’',
  '²' => 'â–“',
  '³' => 'â”‚',
  '´' => 'â”¤',
  'µ' => 'â•¡',
  '¶' => 'â•¢',
  '·' => 'â•–',
  '¸' => 'â••',
  '¹' => 'â•£',
  'º' => 'â•‘',
  '»' => 'â•—',
  '¼' => 'â•�',
  '½' => 'â•œ',
  '¾' => 'â•›',
  '¿' => 'â”�',
  'À' => 'â””',
  'Á' => 'â”´',
  'Â' => 'â”¬',
  'Ã' => 'â”œ',
  'Ä' => 'â”€',
  'Å' => 'â”¼',
  'Æ' => 'â•ž',
  'Ç' => 'â•Ÿ',
  'È' => 'â•š',
  'É' => 'â•”',
  'Ê' => 'â•©',
  'Ë' => 'â•¦',
  'Ì' => 'â• ',
  'Í' => 'â•�',
  'Î' => 'â•¬',
  'Ï' => 'â•§',
  'Ð' => 'â•¨',
  'Ñ' => 'â•¤',
  'Ò' => 'â•¥',
  'Ó' => 'â•™',
  'Ô' => 'â•˜',
  'Õ' => 'â•’',
  'Ö' => 'â•“',
  '×' => 'â•«',
  'Ø' => 'â•ª',
  'Ù' => 'â”˜',
  'Ú' => 'â”Œ',
  'Û' => 'â–ˆ',
  'Ü' => 'â–„',
  'Ý' => 'â–Œ',
  'Þ' => 'â–�',
  'ß' => 'â–€',
  'à' => 'Î±',
  'á' => 'ÃŸ',
  'â' => 'Î“',
  'ã' => 'Ï€',
  'ä' => 'Î£',
  'å' => 'Ïƒ',
  'æ' => 'Âµ',
  'ç' => 'Ï„',
  'è' => 'Î¦',
  'é' => 'Î˜',
  'ê' => 'Î©',
  'ë' => 'Î´',
  'ì' => 'âˆž',
  'í' => 'Ï†',
  'î' => 'Îµ',
  'ï' => 'âˆ©',
  'ð' => 'â‰¡',
  'ñ' => 'Â±',
  'ò' => 'â‰¥',
  'ó' => 'â‰¤',
  'ô' => 'âŒ ',
  'õ' => 'âŒ¡',
  'ö' => 'Ã·',
  '÷' => 'â‰ˆ',
  'ø' => 'Â°',
  'ù' => 'âˆ™',
  'ú' => 'Â·',
  'û' => 'âˆš',
  'ü' => 'â�¿',
  'ý' => 'Â²',
  'þ' => 'â– ',
  'ÿ' => 'Â ',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => '%',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
  '€' => 'Â€',
  '�' => 'Â�',
  '‚' => 'Â‚',
  'ƒ' => 'Âƒ',
  '„' => 'Â„',
  '…' => 'Â…',
  '†' => 'Â†',
  '‡' => 'Â‡',
  'ˆ' => 'Âˆ',
  '‰' => 'Â‰',
  'Š' => 'ÂŠ',
  '‹' => 'Â‹',
  'Œ' => 'ÂŒ',
  '�' => 'Â�',
  'Ž' => 'ÂŽ',
  '�' => 'Â�',
  '�' => 'Â�',
  '‘' => 'Â‘',
  '’' => 'Â’',
  '“' => 'Â“',
  '”' => 'Â”',
  '•' => 'Â•',
  '–' => 'Â–',
  '—' => 'Â—',
  '˜' => 'Â˜',
  '™' => 'Â™',
  'š' => 'Âš',
  '›' => 'Â›',
  'œ' => 'Âœ',
  '�' => 'Â�',
  'ž' => 'Âž',
  'Ÿ' => 'ÂŸ',
  ' ' => 'Â ',
  '¡' => 'Ð�',
  '¢' => 'Ð‚',
  '£' => 'Ðƒ',
  '¤' => 'Ð„',
  '¥' => 'Ð…',
  '¦' => 'Ð†',
  '§' => 'Ð‡',
  '¨' => 'Ðˆ',
  '©' => 'Ð‰',
  'ª' => 'ÐŠ',
  '«' => 'Ð‹',
  '¬' => 'ÐŒ',
  '­' => 'Â­',
  '®' => 'ÐŽ',
  '¯' => 'Ð�',
  '°' => 'Ð�',
  '±' => 'Ð‘',
  '²' => 'Ð’',
  '³' => 'Ð“',
  '´' => 'Ð”',
  'µ' => 'Ð•',
  '¶' => 'Ð–',
  '·' => 'Ð—',
  '¸' => 'Ð˜',
  '¹' => 'Ð™',
  'º' => 'Ðš',
  '»' => 'Ð›',
  '¼' => 'Ðœ',
  '½' => 'Ð�',
  '¾' => 'Ðž',
  '¿' => 'ÐŸ',
  'À' => 'Ð ',
  'Á' => 'Ð¡',
  'Â' => 'Ð¢',
  'Ã' => 'Ð£',
  'Ä' => 'Ð¤',
  'Å' => 'Ð¥',
  'Æ' => 'Ð¦',
  'Ç' => 'Ð§',
  'È' => 'Ð¨',
  'É' => 'Ð©',
  'Ê' => 'Ðª',
  'Ë' => 'Ð«',
  'Ì' => 'Ð¬',
  'Í' => 'Ð­',
  'Î' => 'Ð®',
  'Ï' => 'Ð¯',
  'Ð' => 'Ð°',
  'Ñ' => 'Ð±',
  'Ò' => 'Ð²',
  'Ó' => 'Ð³',
  'Ô' => 'Ð´',
  'Õ' => 'Ðµ',
  'Ö' => 'Ð¶',
  '×' => 'Ð·',
  'Ø' => 'Ð¸',
  'Ù' => 'Ð¹',
  'Ú' => 'Ðº',
  'Û' => 'Ð»',
  'Ü' => 'Ð¼',
  'Ý' => 'Ð½',
  'Þ' => 'Ð¾',
  'ß' => 'Ð¿',
  'à' => 'Ñ€',
  'á' => 'Ñ�',
  'â' => 'Ñ‚',
  'ã' => 'Ñƒ',
  'ä' => 'Ñ„',
  'å' => 'Ñ…',
  'æ' => 'Ñ†',
  'ç' => 'Ñ‡',
  'è' => 'Ñˆ',
  'é' => 'Ñ‰',
  'ê' => 'ÑŠ',
  'ë' => 'Ñ‹',
  'ì' => 'ÑŒ',
  'í' => 'Ñ�',
  'î' => 'ÑŽ',
  'ï' => 'Ñ�',
  'ð' => 'â„–',
  'ñ' => 'Ñ‘',
  'ò' => 'Ñ’',
  'ó' => 'Ñ“',
  'ô' => 'Ñ”',
  'õ' => 'Ñ•',
  'ö' => 'Ñ–',
  '÷' => 'Ñ—',
  'ø' => 'Ñ˜',
  'ù' => 'Ñ™',
  'ú' => 'Ñš',
  'û' => 'Ñ›',
  'ü' => 'Ñœ',
  'ý' => 'Â§',
  'þ' => 'Ñž',
  'ÿ' => 'ÑŸ',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => '%',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
  '€' => 'Â€',
  '�' => 'Â�',
  '‚' => 'Â‚',
  'ƒ' => 'Âƒ',
  '„' => 'Â„',
  '…' => 'Â…',
  '†' => 'Â†',
  '‡' => 'Â‡',
  'ˆ' => 'Âˆ',
  '‰' => 'Â‰',
  'Š' => 'ÂŠ',
  '‹' => 'Â‹',
  'Œ' => 'ÂŒ',
  '�' => 'Â�',
  'Ž' => 'ÂŽ',
  '�' => 'Â�',
  '�' => 'Â�',
  '‘' => 'Â‘',
  '’' => 'Â’',
  '“' => 'Â“',
  '”' => 'Â”',
  '•' => 'Â•',
  '–' => 'Â–',
  '—' => 'Â—',
  '˜' => 'Â˜',
  '™' => 'Â™',
  'š' => 'Âš',
  '›' => 'Â›',
  'œ' => 'Âœ',
  '�' => 'Â�',
  'ž' => 'Âž',
  'Ÿ' => 'ÂŸ',
  ' ' => 'Â ',
  '¡' => 'Û°',
  '¢' => 'Û±',
  '£' => 'Û²',
  '¤' => 'Û³',
  '¥' => 'Û´',
  '¦' => 'Ûµ',
  '§' => 'Û¶',
  '¨' => 'Û·',
  '©' => 'Û¸',
  'ª' => 'Û¹',
  '«' => 'ØŒ',
  '¬' => 'Ø›',
  '­' => 'Â­',
  '®' => 'ØŸ',
  '¯' => 'ïº�',
  '°' => 'ïº�',
  '±' => 'ïºŽ',
  '²' => 'ïºŽ',
  '³' => 'ïº�',
  '´' => 'ïº‘',
  'µ' => 'ï­–',
  '¶' => 'ï­˜',
  '·' => 'ïº“',
  '¸' => 'ïº•',
  '¹' => 'ïº—',
  'º' => 'ï­¦',
  '»' => 'ï­¨',
  '¼' => 'ïº™',
  '½' => 'ïº›',
  '¾' => 'ïº�',
  '¿' => 'ïºŸ',
  'À' => 'ï­º',
  'Á' => 'ï­¼',
  'Â' => 'ïº¡',
  'Ã' => 'ïº£',
  'Ä' => 'ïº¥',
  'Å' => 'ïº§',
  'Æ' => 'ïº©',
  'Ç' => 'ï®„',
  'È' => 'ïº«',
  'É' => 'ïº­',
  'Ê' => 'ï®Œ',
  'Ë' => 'ïº¯',
  'Ì' => 'ï®Š',
  'Í' => 'ïº±',
  'Î' => 'ïº³',
  'Ï' => 'ïºµ',
  'Ð' => 'ïº·',
  'Ñ' => 'ïº¹',
  'Ò' => 'ïº»',
  'Ó' => 'ïº½',
  'Ô' => 'ïº¿',
  'Õ' => 'ï»�',
  'Ö' => 'ï»…',
  '×' => 'ï»‰',
  'Ø' => 'ï»Š',
  'Ù' => 'ï»‹',
  'Ú' => 'ï»Œ',
  'Û' => 'ï»�',
  'Ü' => 'ï»Ž',
  'Ý' => 'ï»�',
  'Þ' => 'ï»�',
  'ß' => 'ï»‘',
  'à' => 'ï»“',
  'á' => 'ï»•',
  'â' => 'ï»—',
  'ã' => 'ï»™',
  'ä' => 'ï»›',
  'å' => 'ï®’',
  'æ' => 'ï®”',
  'ç' => 'ï»�',
  'è' => 'ï»Ÿ',
  'é' => 'ï» ',
  'ê' => 'ï»¡',
  'ë' => 'ï»£',
  'ì' => 'ï®ž',
  'í' => 'ï»¥',
  'î' => 'ï»§',
  'ï' => 'ïº…',
  'ð' => 'ï»­',
  'ñ' => 'ï®¦',
  'ò' => 'ï®¨',
  'ó' => 'ï®©',
  'ô' => 'ï®ª',
  'õ' => 'ïº€',
  'ö' => 'ïº‰',
  '÷' => 'ïºŠ',
  'ø' => 'ïº‹',
  'ù' => 'ï»±',
  'ú' => 'ï»²',
  'û' => 'ï»³',
  'ü' => 'ï®°',
  'ý' => 'ï®®',
  'þ' => 'ï¹¼',
  'ÿ' => 'ï¹½',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => '%',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
  '€' => 'Ã‡',
  '�' => 'Ã¼',
  '‚' => 'Ã©',
  'ƒ' => 'Ã¢',
  '„' => 'Ã¤',
  '…' => 'Ã ',
  '†' => 'Ã¥',
  '‡' => 'Ã§',
  'ˆ' => 'Ãª',
  '‰' => 'Ã«',
  'Š' => 'Ã¨',
  '‹' => 'Ã¯',
  'Œ' => 'Ã®',
  '�' => 'Ã¬',
  'Ž' => 'Ã„',
  '�' => 'Ã…',
  '�' => 'Ã‰',
  '‘' => 'Ã¦',
  '’' => 'Ã†',
  '“' => 'Ã´',
  '”' => 'Ã¶',
  '•' => 'Ã²',
  '–' => 'Ã»',
  '—' => 'Ã¹',
  '˜' => 'Ã¿',
  '™' => 'Ã–',
  'š' => 'Ãœ',
  '›' => 'Ã¸',
  'œ' => 'Â£',
  '�' => 'Ã˜',
  'ž' => 'â‚§',
  'Ÿ' => 'Æ’',
  ' ' => 'Ã¡',
  '¡' => 'Ã­',
  '¢' => 'Ã³',
  '£' => 'Ãº',
  '¤' => 'Ã±',
  '¥' => 'Ã‘',
  '¦' => 'Âª',
  '§' => 'Âº',
  '¨' => 'Â¿',
  '©' => 'âŒ�',
  'ª' => 'Â¬',
  '«' => 'Â½',
  '¬' => 'Â¼',
  '­' => 'Â¡',
  '®' => 'Â«',
  '¯' => 'Â¤',
  '°' => 'â–‘',
  '±' => 'â–’',
  '²' => 'â–“',
  '³' => 'â”‚',
  '´' => 'â”¤',
  'µ' => 'â•¡',
  '¶' => 'â•¢',
  '·' => 'â•–',
  '¸' => 'â••',
  '¹' => 'â•£',
  'º' => 'â•‘',
  '»' => 'â•—',
  '¼' => 'â•�',
  '½' => 'â•œ',
  '¾' => 'â•›',
  '¿' => 'â”�',
  'À' => 'â””',
  'Á' => 'â”´',
  'Â' => 'â”¬',
  'Ã' => 'â”œ',
  'Ä' => 'â”€',
  'Å' => 'â”¼',
  'Æ' => 'â•ž',
  'Ç' => 'â•Ÿ',
  'È' => 'â•š',
  'É' => 'â•”',
  'Ê' => 'â•©',
  'Ë' => 'â•¦',
  'Ì' => 'â• ',
  'Í' => 'â•�',
  'Î' => 'â•¬',
  'Ï' => 'â•§',
  'Ð' => 'â•¨',
  'Ñ' => 'â•¤',
  'Ò' => 'â•¥',
  'Ó' => 'â•™',
  'Ô' => 'â•˜',
  'Õ' => 'â•’',
  'Ö' => 'â•“',
  '×' => 'â•«',
  'Ø' => 'â•ª',
  'Ù' => 'â”˜',
  'Ú' => 'â”Œ',
  'Û' => 'â–ˆ',
  'Ü' => 'â–„',
  'Ý' => 'â–Œ',
  'Þ' => 'â–�',
  'ß' => 'â–€',
  'à' => 'Î±',
  'á' => 'ÃŸ',
  'â' => 'Î“',
  'ã' => 'Ï€',
  'ä' => 'Î£',
  'å' => 'Ïƒ',
  'æ' => 'Âµ',
  'ç' => 'Ï„',
  'è' => 'Î¦',
  'é' => 'Î˜',
  'ê' => 'Î©',
  'ë' => 'Î´',
  'ì' => 'âˆž',
  'í' => 'Ï†',
  'î' => 'Îµ',
  'ï' => 'âˆ©',
  'ð' => 'â‰¡',
  'ñ' => 'Â±',
  'ò' => 'â‰¥',
  'ó' => 'â‰¤',
  'ô' => 'âŒ ',
  'õ' => 'âŒ¡',
  'ö' => 'Ã·',
  '÷' => 'â‰ˆ',
  'ø' => 'Â°',
  'ù' => 'âˆ™',
  'ú' => 'Â·',
  'û' => 'âˆš',
  'ü' => 'â�¿',
  'ý' => 'Â²',
  'þ' => 'â– ',
  'ÿ' => 'Â ',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => '%',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
  '€' => 'â”€',
  '�' => 'â”‚',
  '‚' => 'â”Œ',
  'ƒ' => 'â”�',
  '„' => 'â””',
  '…' => 'â”˜',
  '†' => 'â”œ',
  '‡' => 'â”¤',
  'ˆ' => 'â”¬',
  '‰' => 'â”´',
  'Š' => 'â”¼',
  '‹' => 'â–€',
  'Œ' => 'â–„',
  '�' => 'â–ˆ',
  'Ž' => 'â–Œ',
  '�' => 'â–�',
  '�' => 'â–‘',
  '‘' => 'â–’',
  '’' => 'â–“',
  '“' => 'âŒ ',
  '”' => 'â– ',
  '•' => 'â€¢',
  '–' => 'âˆš',
  '—' => 'â‰ˆ',
  '˜' => 'â‰¤',
  '™' => 'â‰¥',
  'š' => 'Â ',
  '›' => 'âŒ¡',
  'œ' => 'Â°',
  '�' => 'Â²',
  'ž' => 'Â·',
  'Ÿ' => 'Ã·',
  ' ' => 'â•�',
  '¡' => 'â•‘',
  '¢' => 'â•’',
  '£' => 'Ñ‘',
  '¤' => 'Ñ”',
  '¥' => 'â•”',
  '¦' => 'Ñ–',
  '§' => 'Ñ—',
  '¨' => 'â•—',
  '©' => 'â•˜',
  'ª' => 'â•™',
  '«' => 'â•š',
  '¬' => 'â•›',
  '­' => 'Ò‘',
  '®' => 'â•�',
  '¯' => 'â•ž',
  '°' => 'â•Ÿ',
  '±' => 'â• ',
  '²' => 'â•¡',
  '³' => 'Ð�',
  '´' => 'Ðƒ',
  'µ' => 'â•£',
  '¶' => 'Ð†',
  '·' => 'Ð‡',
  '¸' => 'â•¦',
  '¹' => 'â•§',
  'º' => 'â•¨',
  '»' => 'â•©',
  '¼' => 'â•ª',
  '½' => 'Ò�',
  '¾' => 'â•¬',
  '¿' => 'Â©',
  'À' => 'ÑŽ',
  'Á' => 'Ð°',
  'Â' => 'Ð±',
  'Ã' => 'Ñ†',
  'Ä' => 'Ð´',
  'Å' => 'Ðµ',
  'Æ' => 'Ñ„',
  'Ç' => 'Ð³',
  'È' => 'Ñ…',
  'É' => 'Ð¸',
  'Ê' => 'Ð¹',
  'Ë' => 'Ðº',
  'Ì' => 'Ð»',
  'Í' => 'Ð¼',
  'Î' => 'Ð½',
  'Ï' => 'Ð¾',
  'Ð' => 'Ð¿',
  'Ñ' => 'Ñ�',
  'Ò' => 'Ñ€',
  'Ó' => 'Ñ�',
  'Ô' => 'Ñ‚',
  'Õ' => 'Ñƒ',
  'Ö' => 'Ð¶',
  '×' => 'Ð²',
  'Ø' => 'ÑŒ',
  'Ù' => 'Ñ‹',
  'Ú' => 'Ð·',
  'Û' => 'Ñˆ',
  'Ü' => 'Ñ�',
  'Ý' => 'Ñ‰',
  'Þ' => 'Ñ‡',
  'ß' => 'ÑŠ',
  'à' => 'Ð®',
  'á' => 'Ð�',
  'â' => 'Ð‘',
  'ã' => 'Ð¦',
  'ä' => 'Ð”',
  'å' => 'Ð•',
  'æ' => 'Ð¤',
  'ç' => 'Ð“',
  'è' => 'Ð¥',
  'é' => 'Ð˜',
  'ê' => 'Ð™',
  'ë' => 'Ðš',
  'ì' => 'Ð›',
  'í' => 'Ðœ',
  'î' => 'Ð�',
  'ï' => 'Ðž',
  'ð' => 'ÐŸ',
  'ñ' => 'Ð¯',
  'ò' => 'Ð ',
  'ó' => 'Ð¡',
  'ô' => 'Ð¢',
  'õ' => 'Ð£',
  'ö' => 'Ð–',
  '÷' => 'Ð’',
  'ø' => 'Ð¬',
  'ù' => 'Ð«',
  'ú' => 'Ð—',
  'û' => 'Ð¨',
  'ü' => 'Ð­',
  'ý' => 'Ð©',
  'þ' => 'Ð§',
  'ÿ' => 'Ðª',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => '%',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
  '¡@' => 'ã€€',
  '¡A' => 'ï¼Œ',
  '¡B' => 'ã€�',
  '¡C' => 'ã€‚',
  '¡D' => 'ï¼Ž',
  '¡E' => 'â€§',
  '¡F' => 'ï¼›',
  '¡G' => 'ï¼š',
  '¡H' => 'ï¼Ÿ',
  '¡I' => 'ï¼�',
  '¡J' => 'ï¸°',
  '¡K' => 'â€¦',
  '¡L' => 'â€¥',
  '¡M' => 'ï¹�',
  '¡N' => 'ï¹‘',
  '¡O' => 'ï¹’',
  '¡P' => 'Â·',
  '¡Q' => 'ï¹”',
  '¡R' => 'ï¹•',
  '¡S' => 'ï¹–',
  '¡T' => 'ï¹—',
  '¡U' => 'ï½œ',
  '¡V' => 'â€“',
  '¡W' => 'ï¸±',
  '¡X' => 'â€”',
  '¡Y' => 'ï¸³',
  '¡Z' => 'â•´',
  '¡[' => 'ï¸´',
  '¡\\' => 'ï¹�',
  '¡]' => 'ï¼ˆ',
  '¡^' => 'ï¼‰',
  '¡_' => 'ï¸µ',
  '¡`' => 'ï¸¶',
  '¡a' => 'ï½›',
  '¡b' => 'ï½�',
  '¡c' => 'ï¸·',
  '¡d' => 'ï¸¸',
  '¡e' => 'ã€”',
  '¡f' => 'ã€•',
  '¡g' => 'ï¸¹',
  '¡h' => 'ï¸º',
  '¡i' => 'ã€�',
  '¡j' => 'ã€‘',
  '¡k' => 'ï¸»',
  '¡l' => 'ï¸¼',
  '¡m' => 'ã€Š',
  '¡n' => 'ã€‹',
  '¡o' => 'ï¸½',
  '¡p' => 'ï¸¾',
  '¡q' => 'ã€ˆ',
  '¡r' => 'ã€‰',
  '¡s' => 'ï¸¿',
  '¡t' => 'ï¹€',
  '¡u' => 'ã€Œ',
  '¡v' => 'ã€�',
  '¡w' => 'ï¹�',
  '¡x' => 'ï¹‚',
  '¡y' => 'ã€Ž',
  '¡z' => 'ã€�',
  '¡{' => 'ï¹ƒ',
  '¡|' => 'ï¹„',
  '¡}' => 'ï¹™',
  '¡~' => 'ï¹š',
  '¡¡' => 'ï¹›',
  '¡¢' => 'ï¹œ',
  '¡£' => 'ï¹�',
  '¡¤' => 'ï¹ž',
  '¡¥' => 'â€˜',
  '¡¦' => 'â€™',
  '¡§' => 'â€œ',
  '¡¨' => 'â€�',
  '¡©' => 'ã€�',
  '¡ª' => 'ã€ž',
  '¡«' => 'â€µ',
  '¡¬' => 'â€²',
  '¡­' => 'ï¼ƒ',
  '¡®' => 'ï¼†',
  '¡¯' => 'ï¼Š',
  '¡°' => 'â€»',
  '¡±' => 'Â§',
  '¡²' => 'ã€ƒ',
  '¡³' => 'â—‹',
  '¡´' => 'â—�',
  '¡µ' => 'â–³',
  '¡¶' => 'â–²',
  '¡·' => 'â—Ž',
  '¡¸' => 'â˜†',
  '¡¹' => 'â˜…',
  '¡º' => 'â—‡',
  '¡»' => 'â—†',
  '¡¼' => 'â–¡',
  '¡½' => 'â– ',
  '¡¾' => 'â–½',
  '¡¿' => 'â–¼',
  '¡À' => 'ãŠ£',
  '¡Á' => 'â„…',
  '¡Â' => 'Â¯',
  '¡Ã' => 'ï¿£',
  '¡Ä' => 'ï¼¿',
  '¡Å' => 'Ë�',
  '¡Æ' => 'ï¹‰',
  '¡Ç' => 'ï¹Š',
  '¡È' => 'ï¹�',
  '¡É' => 'ï¹Ž',
  '¡Ê' => 'ï¹‹',
  '¡Ë' => 'ï¹Œ',
  '¡Ì' => 'ï¹Ÿ',
  '¡Í' => 'ï¹ ',
  '¡Î' => 'ï¹¡',
  '¡Ï' => 'ï¼‹',
  '¡Ð' => 'ï¼�',
  '¡Ñ' => 'Ã—',
  '¡Ò' => 'Ã·',
  '¡Ó' => 'Â±',
  '¡Ô' => 'âˆš',
  '¡Õ' => 'ï¼œ',
  '¡Ö' => 'ï¼ž',
  '¡×' => 'ï¼�',
  '¡Ø' => 'â‰¦',
  '¡Ù' => 'â‰§',
  '¡Ú' => 'â‰ ',
  '¡Û' => 'âˆž',
  '¡Ü' => 'â‰’',
  '¡Ý' => 'â‰¡',
  '¡Þ' => 'ï¹¢',
  '¡ß' => 'ï¹£',
  '¡à' => 'ï¹¤',
  '¡á' => 'ï¹¥',
  '¡â' => 'ï¹¦',
  '¡ã' => 'ï½ž',
  '¡ä' => 'âˆ©',
  '¡å' => 'âˆª',
  '¡æ' => 'âŠ¥',
  '¡ç' => 'âˆ ',
  '¡è' => 'âˆŸ',
  '¡é' => 'âŠ¿',
  '¡ê' => 'ã�’',
  '¡ë' => 'ã�‘',
  '¡ì' => 'âˆ«',
  '¡í' => 'âˆ®',
  '¡î' => 'âˆµ',
  '¡ï' => 'âˆ´',
  '¡ð' => 'â™€',
  '¡ñ' => 'â™‚',
  '¡ò' => 'âŠ•',
  '¡ó' => 'âŠ™',
  '¡ô' => 'â†‘',
  '¡õ' => 'â†“',
  '¡ö' => 'â†�',
  '¡÷' => 'â†’',
  '¡ø' => 'â†–',
  '¡ù' => 'â†—',
  '¡ú' => 'â†™',
  '¡û' => 'â†˜',
  '¡ü' => 'âˆ¥',
  '¡ý' => 'âˆ£',
  '¡þ' => 'ï¼�',
  '¢@' => 'ï¼¼',
  '¢A' => 'âˆ•',
  '¢B' => 'ï¹¨',
  '¢C' => 'ï¼„',
  '¢D' => 'ï¿¥',
  '¢E' => 'ã€’',
  '¢F' => 'ï¿ ',
  '¢G' => 'ï¿¡',
  '¢H' => 'ï¼…',
  '¢I' => 'ï¼ ',
  '¢J' => 'â„ƒ',
  '¢K' => 'â„‰',
  '¢L' => 'ï¹©',
  '¢M' => 'ï¹ª',
  '¢N' => 'ï¹«',
  '¢O' => 'ã�•',
  '¢P' => 'ãŽœ',
  '¢Q' => 'ãŽ�',
  '¢R' => 'ãŽž',
  '¢S' => 'ã�Ž',
  '¢T' => 'ãŽ¡',
  '¢U' => 'ãŽŽ',
  '¢V' => 'ãŽ�',
  '¢W' => 'ã�„',
  '¢X' => 'Â°',
  '¢Y' => 'å…™',
  '¢Z' => 'å…›',
  '¢[' => 'å…ž',
  '¢\\' => 'å…�',
  '¢]' => 'å…¡',
  '¢^' => 'å…£',
  '¢_' => 'å—§',
  '¢`' => 'ç“©',
  '¢a' => 'ç³Ž',
  '¢b' => 'â–�',
  '¢c' => 'â–‚',
  '¢d' => 'â–ƒ',
  '¢e' => 'â–„',
  '¢f' => 'â–…',
  '¢g' => 'â–†',
  '¢h' => 'â–‡',
  '¢i' => 'â–ˆ',
  '¢j' => 'â–�',
  '¢k' => 'â–Ž',
  '¢l' => 'â–�',
  '¢m' => 'â–Œ',
  '¢n' => 'â–‹',
  '¢o' => 'â–Š',
  '¢p' => 'â–‰',
  '¢q' => 'â”¼',
  '¢r' => 'â”´',
  '¢s' => 'â”¬',
  '¢t' => 'â”¤',
  '¢u' => 'â”œ',
  '¢v' => 'â–”',
  '¢w' => 'â”€',
  '¢x' => 'â”‚',
  '¢y' => 'â–•',
  '¢z' => 'â”Œ',
  '¢{' => 'â”�',
  '¢|' => 'â””',
  '¢}' => 'â”˜',
  '¢~' => 'â•­',
  '¢¡' => 'â•®',
  '¢¢' => 'â•°',
  '¢£' => 'â•¯',
  '¢¤' => 'â•�',
  '¢¥' => 'â•ž',
  '¢¦' => 'â•ª',
  '¢§' => 'â•¡',
  '¢¨' => 'â—¢',
  '¢©' => 'â—£',
  '¢ª' => 'â—¥',
  '¢«' => 'â—¤',
  '¢¬' => 'â•±',
  '¢­' => 'â•²',
  '¢®' => 'â•³',
  '¢¯' => 'ï¼�',
  '¢°' => 'ï¼‘',
  '¢±' => 'ï¼’',
  '¢²' => 'ï¼“',
  '¢³' => 'ï¼”',
  '¢´' => 'ï¼•',
  '¢µ' => 'ï¼–',
  '¢¶' => 'ï¼—',
  '¢·' => 'ï¼˜',
  '¢¸' => 'ï¼™',
  '¢¹' => 'â… ',
  '¢º' => 'â…¡',
  '¢»' => 'â…¢',
  '¢¼' => 'â…£',
  '¢½' => 'â…¤',
  '¢¾' => 'â…¥',
  '¢¿' => 'â…¦',
  '¢À' => 'â…§',
  '¢Á' => 'â…¨',
  '¢Â' => 'â…©',
  '¢Ã' => 'ã€¡',
  '¢Ä' => 'ã€¢',
  '¢Å' => 'ã€£',
  '¢Æ' => 'ã€¤',
  '¢Ç' => 'ã€¥',
  '¢È' => 'ã€¦',
  '¢É' => 'ã€§',
  '¢Ê' => 'ã€¨',
  '¢Ë' => 'ã€©',
  '¢Ì' => 'å��',
  '¢Í' => 'å�„',
  '¢Î' => 'å�…',
  '¢Ï' => 'ï¼¡',
  '¢Ð' => 'ï¼¢',
  '¢Ñ' => 'ï¼£',
  '¢Ò' => 'ï¼¤',
  '¢Ó' => 'ï¼¥',
  '¢Ô' => 'ï¼¦',
  '¢Õ' => 'ï¼§',
  '¢Ö' => 'ï¼¨',
  '¢×' => 'ï¼©',
  '¢Ø' => 'ï¼ª',
  '¢Ù' => 'ï¼«',
  '¢Ú' => 'ï¼¬',
  '¢Û' => 'ï¼­',
  '¢Ü' => 'ï¼®',
  '¢Ý' => 'ï¼¯',
  '¢Þ' => 'ï¼°',
  '¢ß' => 'ï¼±',
  '¢à' => 'ï¼²',
  '¢á' => 'ï¼³',
  '¢â' => 'ï¼´',
  '¢ã' => 'ï¼µ',
  '¢ä' => 'ï¼¶',
  '¢å' => 'ï¼·',
  '¢æ' => 'ï¼¸',
  '¢ç' => 'ï¼¹',
  '¢è' => 'ï¼º',
  '¢é' => 'ï½�',
  '¢ê' => 'ï½‚',
  '¢ë' => 'ï½ƒ',
  '¢ì' => 'ï½„',
  '¢í' => 'ï½…',
  '¢î' => 'ï½†',
  '¢ï' => 'ï½‡',
  '¢ð' => 'ï½ˆ',
  '¢ñ' => 'ï½‰',
  '¢ò' => 'ï½Š',
  '¢ó' => 'ï½‹',
  '¢ô' => 'ï½Œ',
  '¢õ' => 'ï½�',
  '¢ö' => 'ï½Ž',
  '¢÷' => 'ï½�',
  '¢ø' => 'ï½�',
  '¢ù' => 'ï½‘',
  '¢ú' => 'ï½’',
  '¢û' => 'ï½“',
  '¢ü' => 'ï½”',
  '¢ý' => 'ï½•',
  '¢þ' => 'ï½–',
  '£@' => 'ï½—',
  '£A' => 'ï½˜',
  '£B' => 'ï½™',
  '£C' => 'ï½š',
  '£D' => 'Î‘',
  '£E' => 'Î’',
  '£F' => 'Î“',
  '£G' => 'Î”',
  '£H' => 'Î•',
  '£I' => 'Î–',
  '£J' => 'Î—',
  '£K' => 'Î˜',
  '£L' => 'Î™',
  '£M' => 'Îš',
  '£N' => 'Î›',
  '£O' => 'Îœ',
  '£P' => 'Î�',
  '£Q' => 'Îž',
  '£R' => 'ÎŸ',
  '£S' => 'Î ',
  '£T' => 'Î¡',
  '£U' => 'Î£',
  '£V' => 'Î¤',
  '£W' => 'Î¥',
  '£X' => 'Î¦',
  '£Y' => 'Î§',
  '£Z' => 'Î¨',
  '£[' => 'Î©',
  '£\\' => 'Î±',
  '£]' => 'Î²',
  '£^' => 'Î³',
  '£_' => 'Î´',
  '£`' => 'Îµ',
  '£a' => 'Î¶',
  '£b' => 'Î·',
  '£c' => 'Î¸',
  '£d' => 'Î¹',
  '£e' => 'Îº',
  '£f' => 'Î»',
  '£g' => 'Î¼',
  '£h' => 'Î½',
  '£i' => 'Î¾',
  '£j' => 'Î¿',
  '£k' => 'Ï€',
  '£l' => 'Ï�',
  '£m' => 'Ïƒ',
  '£n' => 'Ï„',
  '£o' => 'Ï…',
  '£p' => 'Ï†',
  '£q' => 'Ï‡',
  '£r' => 'Ïˆ',
  '£s' => 'Ï‰',
  '£t' => 'ã„…',
  '£u' => 'ã„†',
  '£v' => 'ã„‡',
  '£w' => 'ã„ˆ',
  '£x' => 'ã„‰',
  '£y' => 'ã„Š',
  '£z' => 'ã„‹',
  '£{' => 'ã„Œ',
  '£|' => 'ã„�',
  '£}' => 'ã„Ž',
  '£~' => 'ã„�',
  '£¡' => 'ã„�',
  '£¢' => 'ã„‘',
  '££' => 'ã„’',
  '£¤' => 'ã„“',
  '£¥' => 'ã„”',
  '£¦' => 'ã„•',
  '£§' => 'ã„–',
  '£¨' => 'ã„—',
  '£©' => 'ã„˜',
  '£ª' => 'ã„™',
  '£«' => 'ã„š',
  '£¬' => 'ã„›',
  '£­' => 'ã„œ',
  '£®' => 'ã„�',
  '£¯' => 'ã„ž',
  '£°' => 'ã„Ÿ',
  '£±' => 'ã„ ',
  '£²' => 'ã„¡',
  '£³' => 'ã„¢',
  '£´' => 'ã„£',
  '£µ' => 'ã„¤',
  '£¶' => 'ã„¥',
  '£·' => 'ã„¦',
  '£¸' => 'ã„§',
  '£¹' => 'ã„¨',
  '£º' => 'ã„©',
  '£»' => 'Ë™',
  '£¼' => 'Ë‰',
  '£½' => 'ËŠ',
  '£¾' => 'Ë‡',
  '£¿' => 'Ë‹',
  '£á' => 'â‚¬',
  '¤@' => 'ä¸€',
  '¤A' => 'ä¹™',
  '¤B' => 'ä¸�',
  '¤C' => 'ä¸ƒ',
  '¤D' => 'ä¹ƒ',
  '¤E' => 'ä¹�',
  '¤F' => 'äº†',
  '¤G' => 'äºŒ',
  '¤H' => 'äºº',
  '¤I' => 'å„¿',
  '¤J' => 'å…¥',
  '¤K' => 'å…«',
  '¤L' => 'å‡ ',
  '¤M' => 'åˆ€',
  '¤N' => 'åˆ�',
  '¤O' => 'åŠ›',
  '¤P' => 'åŒ•',
  '¤Q' => 'å��',
  '¤R' => 'å�œ',
  '¤S' => 'å�ˆ',
  '¤T' => 'ä¸‰',
  '¤U' => 'ä¸‹',
  '¤V' => 'ä¸ˆ',
  '¤W' => 'ä¸Š',
  '¤X' => 'ä¸«',
  '¤Y' => 'ä¸¸',
  '¤Z' => 'å‡¡',
  '¤[' => 'ä¹…',
  '¤\\' => 'ä¹ˆ',
  '¤]' => 'ä¹Ÿ',
  '¤^' => 'ä¹ž',
  '¤_' => 'äºŽ',
  '¤`' => 'äº¡',
  '¤a' => 'å…€',
  '¤b' => 'åˆƒ',
  '¤c' => 'å‹º',
  '¤d' => 'å�ƒ',
  '¤e' => 'å�‰',
  '¤f' => 'å�£',
  '¤g' => 'åœŸ',
  '¤h' => 'å£«',
  '¤i' => 'å¤•',
  '¤j' => 'å¤§',
  '¤k' => 'å¥³',
  '¤l' => 'å­�',
  '¤m' => 'å­‘',
  '¤n' => 'å­“',
  '¤o' => 'å¯¸',
  '¤p' => 'å°�',
  '¤q' => 'å°¢',
  '¤r' => 'å°¸',
  '¤s' => 'å±±',
  '¤t' => 'å·�',
  '¤u' => 'å·¥',
  '¤v' => 'å·±',
  '¤w' => 'å·²',
  '¤x' => 'å·³',
  '¤y' => 'å·¾',
  '¤z' => 'å¹²',
  '¤{' => 'å»¾',
  '¤|' => 'å¼‹',
  '¤}' => 'å¼“',
  '¤~' => 'æ‰�',
  '¤¡' => 'ä¸‘',
  '¤¢' => 'ä¸�',
  '¤£' => 'ä¸�',
  '¤¤' => 'ä¸­',
  '¤¥' => 'ä¸°',
  '¤¦' => 'ä¸¹',
  '¤§' => 'ä¹‹',
  '¤¨' => 'å°¹',
  '¤©' => 'äºˆ',
  '¤ª' => 'äº‘',
  '¤«' => 'äº•',
  '¤¬' => 'äº’',
  '¤­' => 'äº”',
  '¤®' => 'äº¢',
  '¤¯' => 'ä»�',
  '¤°' => 'ä»€',
  '¤±' => 'ä»ƒ',
  '¤²' => 'ä»†',
  '¤³' => 'ä»‡',
  '¤´' => 'ä»�',
  '¤µ' => 'ä»Š',
  '¤¶' => 'ä»‹',
  '¤·' => 'ä»„',
  '¤¸' => 'å…ƒ',
  '¤¹' => 'å…�',
  '¤º' => 'å…§',
  '¤»' => 'å…­',
  '¤¼' => 'å…®',
  '¤½' => 'å…¬',
  '¤¾' => 'å†—',
  '¤¿' => 'å‡¶',
  '¤À' => 'åˆ†',
  '¤Á' => 'åˆ‡',
  '¤Â' => 'åˆˆ',
  '¤Ã' => 'å‹»',
  '¤Ä' => 'å‹¾',
  '¤Å' => 'å‹¿',
  '¤Æ' => 'åŒ–',
  '¤Ç' => 'åŒ¹',
  '¤È' => 'å�ˆ',
  '¤É' => 'å�‡',
  '¤Ê' => 'å�…',
  '¤Ë' => 'å�ž',
  '¤Ì' => 'åŽ„',
  '¤Í' => 'å�‹',
  '¤Î' => 'å�Š',
  '¤Ï' => 'å��',
  '¤Ð' => 'å£¬',
  '¤Ñ' => 'å¤©',
  '¤Ò' => 'å¤«',
  '¤Ó' => 'å¤ª',
  '¤Ô' => 'å¤­',
  '¤Õ' => 'å­”',
  '¤Ö' => 'å°‘',
  '¤×' => 'å°¤',
  '¤Ø' => 'å°º',
  '¤Ù' => 'å±¯',
  '¤Ú' => 'å·´',
  '¤Û' => 'å¹»',
  '¤Ü' => 'å»¿',
  '¤Ý' => 'å¼”',
  '¤Þ' => 'å¼•',
  '¤ß' => 'å¿ƒ',
  '¤à' => 'æˆˆ',
  '¤á' => 'æˆ¶',
  '¤â' => 'æ‰‹',
  '¤ã' => 'æ‰Ž',
  '¤ä' => 'æ”¯',
  '¤å' => 'æ–‡',
  '¤æ' => 'æ–—',
  '¤ç' => 'æ–¤',
  '¤è' => 'æ–¹',
  '¤é' => 'æ—¥',
  '¤ê' => 'æ›°',
  '¤ë' => 'æœˆ',
  '¤ì' => 'æœ¨',
  '¤í' => 'æ¬ ',
  '¤î' => 'æ­¢',
  '¤ï' => 'æ­¹',
  '¤ð' => 'æ¯‹',
  '¤ñ' => 'æ¯”',
  '¤ò' => 'æ¯›',
  '¤ó' => 'æ°�',
  '¤ô' => 'æ°´',
  '¤õ' => 'ç�«',
  '¤ö' => 'çˆª',
  '¤÷' => 'çˆ¶',
  '¤ø' => 'çˆ»',
  '¤ù' => 'ç‰‡',
  '¤ú' => 'ç‰™',
  '¤û' => 'ç‰›',
  '¤ü' => 'çŠ¬',
  '¤ý' => 'çŽ‹',
  '¤þ' => 'ä¸™',
  '¥@' => 'ä¸–',
  '¥A' => 'ä¸•',
  '¥B' => 'ä¸”',
  '¥C' => 'ä¸˜',
  '¥D' => 'ä¸»',
  '¥E' => 'ä¹�',
  '¥F' => 'ä¹�',
  '¥G' => 'ä¹Ž',
  '¥H' => 'ä»¥',
  '¥I' => 'ä»˜',
  '¥J' => 'ä»”',
  '¥K' => 'ä»•',
  '¥L' => 'ä»–',
  '¥M' => 'ä»—',
  '¥N' => 'ä»£',
  '¥O' => 'ä»¤',
  '¥P' => 'ä»™',
  '¥Q' => 'ä»ž',
  '¥R' => 'å……',
  '¥S' => 'å…„',
  '¥T' => 'å†‰',
  '¥U' => 'å†Š',
  '¥V' => 'å†¬',
  '¥W' => 'å‡¹',
  '¥X' => 'å‡º',
  '¥Y' => 'å‡¸',
  '¥Z' => 'åˆŠ',
  '¥[' => 'åŠ ',
  '¥\\' => 'åŠŸ',
  '¥]' => 'åŒ…',
  '¥^' => 'åŒ†',
  '¥_' => 'åŒ—',
  '¥`' => 'åŒ�',
  '¥a' => 'ä»Ÿ',
  '¥b' => 'å�Š',
  '¥c' => 'å�‰',
  '¥d' => 'å�¡',
  '¥e' => 'å� ',
  '¥f' => 'å�¯',
  '¥g' => 'å�®',
  '¥h' => 'åŽ»',
  '¥i' => 'å�¯',
  '¥j' => 'å�¤',
  '¥k' => 'å�³',
  '¥l' => 'å�¬',
  '¥m' => 'å�®',
  '¥n' => 'å�©',
  '¥o' => 'å�¨',
  '¥p' => 'å�¼',
  '¥q' => 'å�¸',
  '¥r' => 'å�µ',
  '¥s' => 'å�«',
  '¥t' => 'å�¦',
  '¥u' => 'å�ª',
  '¥v' => 'å�²',
  '¥w' => 'å�±',
  '¥x' => 'å�°',
  '¥y' => 'å�¥',
  '¥z' => 'å�­',
  '¥{' => 'å�»',
  '¥|' => 'å››',
  '¥}' => 'å›š',
  '¥~' => 'å¤–',
  '¥¡' => 'å¤®',
  '¥¢' => 'å¤±',
  '¥£' => 'å¥´',
  '¥¤' => 'å¥¶',
  '¥¥' => 'å­•',
  '¥¦' => 'å®ƒ',
  '¥§' => 'å°¼',
  '¥¨' => 'å·¨',
  '¥©' => 'å·§',
  '¥ª' => 'å·¦',
  '¥«' => 'å¸‚',
  '¥¬' => 'å¸ƒ',
  '¥­' => 'å¹³',
  '¥®' => 'å¹¼',
  '¥¯' => 'å¼�',
  '¥°' => 'å¼˜',
  '¥±' => 'å¼—',
  '¥²' => 'å¿…',
  '¥³' => 'æˆŠ',
  '¥´' => 'æ‰“',
  '¥µ' => 'æ‰”',
  '¥¶' => 'æ‰’',
  '¥·' => 'æ‰‘',
  '¥¸' => 'æ–¥',
  '¥¹' => 'æ—¦',
  '¥º' => 'æœ®',
  '¥»' => 'æœ¬',
  '¥¼' => 'æœª',
  '¥½' => 'æœ«',
  '¥¾' => 'æœ­',
  '¥¿' => 'æ­£',
  '¥À' => 'æ¯�',
  '¥Á' => 'æ°‘',
  '¥Â' => 'æ°�',
  '¥Ã' => 'æ°¸',
  '¥Ä' => 'æ±�',
  '¥Å' => 'æ±€',
  '¥Æ' => 'æ°¾',
  '¥Ç' => 'çŠ¯',
  '¥È' => 'çŽ„',
  '¥É' => 'çŽ‰',
  '¥Ê' => 'ç“œ',
  '¥Ë' => 'ç“¦',
  '¥Ì' => 'ç”˜',
  '¥Í' => 'ç”Ÿ',
  '¥Î' => 'ç”¨',
  '¥Ï' => 'ç”©',
  '¥Ð' => 'ç”°',
  '¥Ñ' => 'ç”±',
  '¥Ò' => 'ç”²',
  '¥Ó' => 'ç”³',
  '¥Ô' => 'ç–‹',
  '¥Õ' => 'ç™½',
  '¥Ö' => 'çš®',
  '¥×' => 'çš¿',
  '¥Ø' => 'ç›®',
  '¥Ù' => 'çŸ›',
  '¥Ú' => 'çŸ¢',
  '¥Û' => 'çŸ³',
  '¥Ü' => 'ç¤º',
  '¥Ý' => 'ç¦¾',
  '¥Þ' => 'ç©´',
  '¥ß' => 'ç«‹',
  '¥à' => 'ä¸ž',
  '¥á' => 'ä¸Ÿ',
  '¥â' => 'ä¹’',
  '¥ã' => 'ä¹“',
  '¥ä' => 'ä¹©',
  '¥å' => 'äº™',
  '¥æ' => 'äº¤',
  '¥ç' => 'äº¦',
  '¥è' => 'äº¥',
  '¥é' => 'ä»¿',
  '¥ê' => 'ä¼‰',
  '¥ë' => 'ä¼™',
  '¥ì' => 'ä¼Š',
  '¥í' => 'ä¼•',
  '¥î' => 'ä¼�',
  '¥ï' => 'ä¼�',
  '¥ð' => 'ä¼‘',
  '¥ñ' => 'ä¼�',
  '¥ò' => 'ä»²',
  '¥ó' => 'ä»¶',
  '¥ô' => 'ä»»',
  '¥õ' => 'ä»°',
  '¥ö' => 'ä»³',
  '¥÷' => 'ä»½',
  '¥ø' => 'ä¼�',
  '¥ù' => 'ä¼‹',
  '¥ú' => 'å…‰',
  '¥û' => 'å…‡',
  '¥ü' => 'å…†',
  '¥ý' => 'å…ˆ',
  '¥þ' => 'å…¨',
  '¦@' => 'å…±',
  '¦A' => 'å†�',
  '¦B' => 'å†°',
  '¦C' => 'åˆ—',
  '¦D' => 'åˆ‘',
  '¦E' => 'åˆ’',
  '¦F' => 'åˆŽ',
  '¦G' => 'åˆ–',
  '¦H' => 'åŠ£',
  '¦I' => 'åŒˆ',
  '¦J' => 'åŒ¡',
  '¦K' => 'åŒ ',
  '¦L' => 'å�°',
  '¦M' => 'å�±',
  '¦N' => 'å�‰',
  '¦O' => 'å��',
  '¦P' => 'å�Œ',
  '¦Q' => 'å�Š',
  '¦R' => 'å��',
  '¦S' => 'å��',
  '¦T' => 'å�‹',
  '¦U' => 'å�„',
  '¦V' => 'å�‘',
  '¦W' => 'å��',
  '¦X' => 'å�ˆ',
  '¦Y' => 'å�ƒ',
  '¦Z' => 'å�Ž',
  '¦[' => 'å�†',
  '¦\\' => 'å�’',
  '¦]' => 'å› ',
  '¦^' => 'å›ž',
  '¦_' => 'å›�',
  '¦`' => 'åœ³',
  '¦a' => 'åœ°',
  '¦b' => 'åœ¨',
  '¦c' => 'åœ­',
  '¦d' => 'åœ¬',
  '¦e' => 'åœ¯',
  '¦f' => 'åœ©',
  '¦g' => 'å¤™',
  '¦h' => 'å¤š',
  '¦i' => 'å¤·',
  '¦j' => 'å¤¸',
  '¦k' => 'å¦„',
  '¦l' => 'å¥¸',
  '¦m' => 'å¦ƒ',
  '¦n' => 'å¥½',
  '¦o' => 'å¥¹',
  '¦p' => 'å¦‚',
  '¦q' => 'å¦�',
  '¦r' => 'å­—',
  '¦s' => 'å­˜',
  '¦t' => 'å®‡',
  '¦u' => 'å®ˆ',
  '¦v' => 'å®…',
  '¦w' => 'å®‰',
  '¦x' => 'å¯º',
  '¦y' => 'å°–',
  '¦z' => 'å±¹',
  '¦{' => 'å·ž',
  '¦|' => 'å¸†',
  '¦}' => 'å¹¶',
  '¦~' => 'å¹´',
  '¦¡' => 'å¼�',
  '¦¢' => 'å¼›',
  '¦£' => 'å¿™',
  '¦¤' => 'å¿–',
  '¦¥' => 'æˆŽ',
  '¦¦' => 'æˆŒ',
  '¦§' => 'æˆ�',
  '¦¨' => 'æˆ�',
  '¦©' => 'æ‰£',
  '¦ª' => 'æ‰›',
  '¦«' => 'æ‰˜',
  '¦¬' => 'æ”¶',
  '¦­' => 'æ—©',
  '¦®' => 'æ—¨',
  '¦¯' => 'æ—¬',
  '¦°' => 'æ—­',
  '¦±' => 'æ›²',
  '¦²' => 'æ›³',
  '¦³' => 'æœ‰',
  '¦´' => 'æœ½',
  '¦µ' => 'æœ´',
  '¦¶' => 'æœ±',
  '¦·' => 'æœµ',
  '¦¸' => 'æ¬¡',
  '¦¹' => 'æ­¤',
  '¦º' => 'æ­»',
  '¦»' => 'æ°–',
  '¦¼' => 'æ±�',
  '¦½' => 'æ±—',
  '¦¾' => 'æ±™',
  '¦¿' => 'æ±Ÿ',
  '¦À' => 'æ± ',
  '¦Á' => 'æ±�',
  '¦Â' => 'æ±•',
  '¦Ã' => 'æ±¡',
  '¦Ä' => 'æ±›',
  '¦Å' => 'æ±�',
  '¦Æ' => 'æ±Ž',
  '¦Ç' => 'ç�°',
  '¦È' => 'ç‰Ÿ',
  '¦É' => 'ç‰�',
  '¦Ê' => 'ç™¾',
  '¦Ë' => 'ç«¹',
  '¦Ì' => 'ç±³',
  '¦Í' => 'ç³¸',
  '¦Î' => 'ç¼¶',
  '¦Ï' => 'ç¾Š',
  '¦Ð' => 'ç¾½',
  '¦Ñ' => 'è€�',
  '¦Ò' => 'è€ƒ',
  '¦Ó' => 'è€Œ',
  '¦Ô' => 'è€’',
  '¦Õ' => 'è€³',
  '¦Ö' => 'è�¿',
  '¦×' => 'è‚‰',
  '¦Ø' => 'è‚‹',
  '¦Ù' => 'è‚Œ',
  '¦Ú' => 'è‡£',
  '¦Û' => 'è‡ª',
  '¦Ü' => 'è‡³',
  '¦Ý' => 'è‡¼',
  '¦Þ' => 'èˆŒ',
  '¦ß' => 'èˆ›',
  '¦à' => 'èˆŸ',
  '¦á' => 'è‰®',
  '¦â' => 'è‰²',
  '¦ã' => 'è‰¾',
  '¦ä' => 'è™«',
  '¦å' => 'è¡€',
  '¦æ' => 'è¡Œ',
  '¦ç' => 'è¡£',
  '¦è' => 'è¥¿',
  '¦é' => 'é˜¡',
  '¦ê' => 'ä¸²',
  '¦ë' => 'äº¨',
  '¦ì' => 'ä½�',
  '¦í' => 'ä½�',
  '¦î' => 'ä½‡',
  '¦ï' => 'ä½—',
  '¦ð' => 'ä½ž',
  '¦ñ' => 'ä¼´',
  '¦ò' => 'ä½›',
  '¦ó' => 'ä½•',
  '¦ô' => 'ä¼°',
  '¦õ' => 'ä½�',
  '¦ö' => 'ä½‘',
  '¦÷' => 'ä¼½',
  '¦ø' => 'ä¼º',
  '¦ù' => 'ä¼¸',
  '¦ú' => 'ä½ƒ',
  '¦û' => 'ä½”',
  '¦ü' => 'ä¼¼',
  '¦ý' => 'ä½†',
  '¦þ' => 'ä½£',
  '§@' => 'ä½œ',
  '§A' => 'ä½ ',
  '§B' => 'ä¼¯',
  '§C' => 'ä½Ž',
  '§D' => 'ä¼¶',
  '§E' => 'ä½™',
  '§F' => 'ä½�',
  '§G' => 'ä½ˆ',
  '§H' => 'ä½š',
  '§I' => 'å…Œ',
  '§J' => 'å…‹',
  '§K' => 'å…�',
  '§L' => 'å…µ',
  '§M' => 'å†¶',
  '§N' => 'å†·',
  '§O' => 'åˆ¥',
  '§P' => 'åˆ¤',
  '§Q' => 'åˆ©',
  '§R' => 'åˆª',
  '§S' => 'åˆ¨',
  '§T' => 'åŠ«',
  '§U' => 'åŠ©',
  '§V' => 'åŠª',
  '§W' => 'åŠ¬',
  '§X' => 'åŒ£',
  '§Y' => 'å�³',
  '§Z' => 'å�µ',
  '§[' => 'å��',
  '§\\' => 'å�­',
  '§]' => 'å�ž',
  '§^' => 'å�¾',
  '§_' => 'å�¦',
  '§`' => 'å‘Ž',
  '§a' => 'å�§',
  '§b' => 'å‘†',
  '§c' => 'å‘ƒ',
  '§d' => 'å�³',
  '§e' => 'å‘ˆ',
  '§f' => 'å‘‚',
  '§g' => 'å�›',
  '§h' => 'å�©',
  '§i' => 'å‘Š',
  '§j' => 'å�¹',
  '§k' => 'å�»',
  '§l' => 'å�¸',
  '§m' => 'å�®',
  '§n' => 'å�µ',
  '§o' => 'å�¶',
  '§p' => 'å� ',
  '§q' => 'å�¼',
  '§r' => 'å‘€',
  '§s' => 'å�±',
  '§t' => 'å�«',
  '§u' => 'å�Ÿ',
  '§v' => 'å�¬',
  '§w' => 'å›ª',
  '§x' => 'å›°',
  '§y' => 'å›¤',
  '§z' => 'å›«',
  '§{' => 'å�Š',
  '§|' => 'å�‘',
  '§}' => 'å�€',
  '§~' => 'å��',
  '§¡' => 'å�‡',
  '§¢' => 'å�Ž',
  '§£' => 'åœ¾',
  '§¤' => 'å��',
  '§¥' => 'å��',
  '§¦' => 'åœ»',
  '§§' => 'å£¯',
  '§¨' => 'å¤¾',
  '§©' => 'å¦�',
  '§ª' => 'å¦’',
  '§«' => 'å¦¨',
  '§¬' => 'å¦ž',
  '§­' => 'å¦£',
  '§®' => 'å¦™',
  '§¯' => 'å¦–',
  '§°' => 'å¦�',
  '§±' => 'å¦¤',
  '§²' => 'å¦“',
  '§³' => 'å¦Š',
  '§´' => 'å¦¥',
  '§µ' => 'å­�',
  '§¶' => 'å­œ',
  '§·' => 'å­š',
  '§¸' => 'å­›',
  '§¹' => 'å®Œ',
  '§º' => 'å®‹',
  '§»' => 'å®�',
  '§¼' => 'å°¬',
  '§½' => 'å±€',
  '§¾' => 'å±�',
  '§¿' => 'å°¿',
  '§À' => 'å°¾',
  '§Á' => 'å²�',
  '§Â' => 'å²‘',
  '§Ã' => 'å²”',
  '§Ä' => 'å²Œ',
  '§Å' => 'å·«',
  '§Æ' => 'å¸Œ',
  '§Ç' => 'åº�',
  '§È' => 'åº‡',
  '§É' => 'åºŠ',
  '§Ê' => 'å»·',
  '§Ë' => 'å¼„',
  '§Ì' => 'å¼Ÿ',
  '§Í' => 'å½¤',
  '§Î' => 'å½¢',
  '§Ï' => 'å½·',
  '§Ð' => 'å½¹',
  '§Ñ' => 'å¿˜',
  '§Ò' => 'å¿Œ',
  '§Ó' => 'å¿—',
  '§Ô' => 'å¿�',
  '§Õ' => 'å¿±',
  '§Ö' => 'å¿«',
  '§×' => 'å¿¸',
  '§Ø' => 'å¿ª',
  '§Ù' => 'æˆ’',
  '§Ú' => 'æˆ‘',
  '§Û' => 'æŠ„',
  '§Ü' => 'æŠ—',
  '§Ý' => 'æŠ–',
  '§Þ' => 'æŠ€',
  '§ß' => 'æ‰¶',
  '§à' => 'æŠ‰',
  '§á' => 'æ‰­',
  '§â' => 'æŠŠ',
  '§ã' => 'æ‰¼',
  '§ä' => 'æ‰¾',
  '§å' => 'æ‰¹',
  '§æ' => 'æ‰³',
  '§ç' => 'æŠ’',
  '§è' => 'æ‰¯',
  '§é' => 'æŠ˜',
  '§ê' => 'æ‰®',
  '§ë' => 'æŠ•',
  '§ì' => 'æŠ“',
  '§í' => 'æŠ‘',
  '§î' => 'æŠ†',
  '§ï' => 'æ”¹',
  '§ð' => 'æ”»',
  '§ñ' => 'æ”¸',
  '§ò' => 'æ—±',
  '§ó' => 'æ›´',
  '§ô' => 'æ�Ÿ',
  '§õ' => 'æ�Ž',
  '§ö' => 'æ��',
  '§÷' => 'æ��',
  '§ø' => 'æ�‘',
  '§ù' => 'æ�œ',
  '§ú' => 'æ�–',
  '§û' => 'æ�ž',
  '§ü' => 'æ�‰',
  '§ý' => 'æ�†',
  '§þ' => 'æ� ',
  '¨@' => 'æ�“',
  '¨A' => 'æ�—',
  '¨B' => 'æ­¥',
  '¨C' => 'æ¯�',
  '¨D' => 'æ±‚',
  '¨E' => 'æ±ž',
  '¨F' => 'æ²™',
  '¨G' => 'æ²�',
  '¨H' => 'æ²ˆ',
  '¨I' => 'æ²‰',
  '¨J' => 'æ²…',
  '¨K' => 'æ²›',
  '¨L' => 'æ±ª',
  '¨M' => 'æ±º',
  '¨N' => 'æ²�',
  '¨O' => 'æ±°',
  '¨P' => 'æ²Œ',
  '¨Q' => 'æ±¨',
  '¨R' => 'æ²–',
  '¨S' => 'æ²’',
  '¨T' => 'æ±½',
  '¨U' => 'æ²ƒ',
  '¨V' => 'æ±²',
  '¨W' => 'æ±¾',
  '¨X' => 'æ±´',
  '¨Y' => 'æ²†',
  '¨Z' => 'æ±¶',
  '¨[' => 'æ²�',
  '¨\\' => 'æ²”',
  '¨]' => 'æ²˜',
  '¨^' => 'æ²‚',
  '¨_' => 'ç�¶',
  '¨`' => 'ç�¼',
  '¨a' => 'ç�½',
  '¨b' => 'ç�¸',
  '¨c' => 'ç‰¢',
  '¨d' => 'ç‰¡',
  '¨e' => 'ç‰ ',
  '¨f' => 'ç‹„',
  '¨g' => 'ç‹‚',
  '¨h' => 'çŽ–',
  '¨i' => 'ç”¬',
  '¨j' => 'ç”«',
  '¨k' => 'ç”·',
  '¨l' => 'ç”¸',
  '¨m' => 'çš‚',
  '¨n' => 'ç›¯',
  '¨o' => 'çŸ£',
  '¨p' => 'ç§�',
  '¨q' => 'ç§€',
  '¨r' => 'ç¦¿',
  '¨s' => 'ç©¶',
  '¨t' => 'ç³»',
  '¨u' => 'ç½•',
  '¨v' => 'è‚–',
  '¨w' => 'è‚“',
  '¨x' => 'è‚�',
  '¨y' => 'è‚˜',
  '¨z' => 'è‚›',
  '¨{' => 'è‚š',
  '¨|' => 'è‚²',
  '¨}' => 'è‰¯',
  '¨~' => 'èŠ’',
  '¨¡' => 'èŠ‹',
  '¨¢' => 'èŠ�',
  '¨£' => 'è¦‹',
  '¨¤' => 'è§’',
  '¨¥' => 'è¨€',
  '¨¦' => 'è°·',
  '¨§' => 'è±†',
  '¨¨' => 'è±•',
  '¨©' => 'è²�',
  '¨ª' => 'èµ¤',
  '¨«' => 'èµ°',
  '¨¬' => 'è¶³',
  '¨­' => 'èº«',
  '¨®' => 'è»Š',
  '¨¯' => 'è¾›',
  '¨°' => 'è¾°',
  '¨±' => 'è¿‚',
  '¨²' => 'è¿†',
  '¨³' => 'è¿…',
  '¨´' => 'è¿„',
  '¨µ' => 'å·¡',
  '¨¶' => 'é‚‘',
  '¨·' => 'é‚¢',
  '¨¸' => 'é‚ª',
  '¨¹' => 'é‚¦',
  '¨º' => 'é‚£',
  '¨»' => 'é…‰',
  '¨¼' => 'é‡†',
  '¨½' => 'é‡Œ',
  '¨¾' => 'é˜²',
  '¨¿' => 'é˜®',
  '¨À' => 'é˜±',
  '¨Á' => 'é˜ª',
  '¨Â' => 'é˜¬',
  '¨Ã' => 'ä¸¦',
  '¨Ä' => 'ä¹–',
  '¨Å' => 'ä¹³',
  '¨Æ' => 'äº‹',
  '¨Ç' => 'äº›',
  '¨È' => 'äºž',
  '¨É' => 'äº«',
  '¨Ê' => 'äº¬',
  '¨Ë' => 'ä½¯',
  '¨Ì' => 'ä¾�',
  '¨Í' => 'ä¾�',
  '¨Î' => 'ä½³',
  '¨Ï' => 'ä½¿',
  '¨Ð' => 'ä½¬',
  '¨Ñ' => 'ä¾›',
  '¨Ò' => 'ä¾‹',
  '¨Ó' => 'ä¾†',
  '¨Ô' => 'ä¾ƒ',
  '¨Õ' => 'ä½°',
  '¨Ö' => 'ä½µ',
  '¨×' => 'ä¾ˆ',
  '¨Ø' => 'ä½©',
  '¨Ù' => 'ä½»',
  '¨Ú' => 'ä¾–',
  '¨Û' => 'ä½¾',
  '¨Ü' => 'ä¾�',
  '¨Ý' => 'ä¾‘',
  '¨Þ' => 'ä½º',
  '¨ß' => 'å…”',
  '¨à' => 'å…’',
  '¨á' => 'å…•',
  '¨â' => 'å…©',
  '¨ã' => 'å…·',
  '¨ä' => 'å…¶',
  '¨å' => 'å…¸',
  '¨æ' => 'å†½',
  '¨ç' => 'å‡½',
  '¨è' => 'åˆ»',
  '¨é' => 'åˆ¸',
  '¨ê' => 'åˆ·',
  '¨ë' => 'åˆº',
  '¨ì' => 'åˆ°',
  '¨í' => 'åˆ®',
  '¨î' => 'åˆ¶',
  '¨ï' => 'å‰�',
  '¨ð' => 'åŠ¾',
  '¨ñ' => 'åŠ»',
  '¨ò' => 'å�’',
  '¨ó' => 'å�”',
  '¨ô' => 'å�“',
  '¨õ' => 'å�‘',
  '¨ö' => 'å�¦',
  '¨÷' => 'å�·',
  '¨ø' => 'å�¸',
  '¨ù' => 'å�¹',
  '¨ú' => 'å�–',
  '¨û' => 'å�”',
  '¨ü' => 'å�—',
  '¨ý' => 'å‘³',
  '¨þ' => 'å‘µ',
  '©@' => 'å’–',
  '©A' => 'å‘¸',
  '©B' => 'å’•',
  '©C' => 'å’€',
  '©D' => 'å‘»',
  '©E' => 'å‘·',
  '©F' => 'å’„',
  '©G' => 'å’’',
  '©H' => 'å’†',
  '©I' => 'å‘¼',
  '©J' => 'å’�',
  '©K' => 'å‘±',
  '©L' => 'å‘¶',
  '©M' => 'å’Œ',
  '©N' => 'å’š',
  '©O' => 'å‘¢',
  '©P' => 'å‘¨',
  '©Q' => 'å’‹',
  '©R' => 'å‘½',
  '©S' => 'å’Ž',
  '©T' => 'å›º',
  '©U' => 'åžƒ',
  '©V' => 'å�·',
  '©W' => 'å�ª',
  '©X' => 'å�©',
  '©Y' => 'å�¡',
  '©Z' => 'å�¦',
  '©[' => 'å�¤',
  '©\\' => 'å�¼',
  '©]' => 'å¤œ',
  '©^' => 'å¥‰',
  '©_' => 'å¥‡',
  '©`' => 'å¥ˆ',
  '©a' => 'å¥„',
  '©b' => 'å¥”',
  '©c' => 'å¦¾',
  '©d' => 'å¦»',
  '©e' => 'å§”',
  '©f' => 'å¦¹',
  '©g' => 'å¦®',
  '©h' => 'å§‘',
  '©i' => 'å§†',
  '©j' => 'å§�',
  '©k' => 'å§�',
  '©l' => 'å§‹',
  '©m' => 'å§“',
  '©n' => 'å§Š',
  '©o' => 'å¦¯',
  '©p' => 'å¦³',
  '©q' => 'å§’',
  '©r' => 'å§…',
  '©s' => 'å­Ÿ',
  '©t' => 'å­¤',
  '©u' => 'å­£',
  '©v' => 'å®—',
  '©w' => 'å®š',
  '©x' => 'å®˜',
  '©y' => 'å®œ',
  '©z' => 'å®™',
  '©{' => 'å®›',
  '©|' => 'å°š',
  '©}' => 'å±ˆ',
  '©~' => 'å±…',
  '©¡' => 'å±†',
  '©¢' => 'å²·',
  '©£' => 'å²¡',
  '©¤' => 'å²¸',
  '©¥' => 'å²©',
  '©¦' => 'å²«',
  '©§' => 'å²±',
  '©¨' => 'å²³',
  '©©' => 'å¸˜',
  '©ª' => 'å¸š',
  '©«' => 'å¸–',
  '©¬' => 'å¸•',
  '©­' => 'å¸›',
  '©®' => 'å¸‘',
  '©¯' => 'å¹¸',
  '©°' => 'åºš',
  '©±' => 'åº—',
  '©²' => 'åºœ',
  '©³' => 'åº•',
  '©´' => 'åº–',
  '©µ' => 'å»¶',
  '©¶' => 'å¼¦',
  '©·' => 'å¼§',
  '©¸' => 'å¼©',
  '©¹' => 'å¾€',
  '©º' => 'å¾�',
  '©»' => 'å½¿',
  '©¼' => 'å½¼',
  '©½' => 'å¿�',
  '©¾' => 'å¿ ',
  '©¿' => 'å¿½',
  '©À' => 'å¿µ',
  '©Á' => 'å¿¿',
  '©Â' => 'æ€�',
  '©Ã' => 'æ€”',
  '©Ä' => 'æ€¯',
  '©Å' => 'æ€µ',
  '©Æ' => 'æ€–',
  '©Ç' => 'æ€ª',
  '©È' => 'æ€•',
  '©É' => 'æ€¡',
  '©Ê' => 'æ€§',
  '©Ë' => 'æ€©',
  '©Ì' => 'æ€«',
  '©Í' => 'æ€›',
  '©Î' => 'æˆ–',
  '©Ï' => 'æˆ•',
  '©Ð' => 'æˆ¿',
  '©Ñ' => 'æˆ¾',
  '©Ò' => 'æ‰€',
  '©Ó' => 'æ‰¿',
  '©Ô' => 'æ‹‰',
  '©Õ' => 'æ‹Œ',
  '©Ö' => 'æ‹„',
  '©×' => 'æŠ¿',
  '©Ø' => 'æ‹‚',
  '©Ù' => 'æŠ¹',
  '©Ú' => 'æ‹’',
  '©Û' => 'æ‹›',
  '©Ü' => 'æŠ«',
  '©Ý' => 'æ‹“',
  '©Þ' => 'æ‹”',
  '©ß' => 'æ‹‹',
  '©à' => 'æ‹ˆ',
  '©á' => 'æŠ¨',
  '©â' => 'æŠ½',
  '©ã' => 'æŠ¼',
  '©ä' => 'æ‹�',
  '©å' => 'æ‹™',
  '©æ' => 'æ‹‡',
  '©ç' => 'æ‹�',
  '©è' => 'æŠµ',
  '©é' => 'æ‹š',
  '©ê' => 'æŠ±',
  '©ë' => 'æ‹˜',
  '©ì' => 'æ‹–',
  '©í' => 'æ‹—',
  '©î' => 'æ‹†',
  '©ï' => 'æŠ¬',
  '©ð' => 'æ‹Ž',
  '©ñ' => 'æ”¾',
  '©ò' => 'æ–§',
  '©ó' => 'æ–¼',
  '©ô' => 'æ—º',
  '©õ' => 'æ˜”',
  '©ö' => 'æ˜“',
  '©÷' => 'æ˜Œ',
  '©ø' => 'æ˜†',
  '©ù' => 'æ˜‚',
  '©ú' => 'æ˜Ž',
  '©û' => 'æ˜€',
  '©ü' => 'æ˜�',
  '©ý' => 'æ˜•',
  '©þ' => 'æ˜Š',
  'ª@' => 'æ˜‡',
  'ªA' => 'æœ�',
  'ªB' => 'æœ‹',
  'ªC' => 'æ�­',
  'ªD' => 'æž‹',
  'ªE' => 'æž•',
  'ªF' => 'æ�±',
  'ªG' => 'æžœ',
  'ªH' => 'æ�³',
  'ªI' => 'æ�·',
  'ªJ' => 'æž‡',
  'ªK' => 'æž�',
  'ªL' => 'æž—',
  'ªM' => 'æ�¯',
  'ªN' => 'æ�°',
  'ªO' => 'æ�¿',
  'ªP' => 'æž‰',
  'ªQ' => 'æ�¾',
  'ªR' => 'æž�',
  'ªS' => 'æ�µ',
  'ªT' => 'æžš',
  'ªU' => 'æž“',
  'ªV' => 'æ�¼',
  'ªW' => 'æ�ª',
  'ªX' => 'æ�²',
  'ªY' => 'æ¬£',
  'ªZ' => 'æ­¦',
  'ª[' => 'æ­§',
  'ª\\' => 'æ­¿',
  'ª]' => 'æ°“',
  'ª^' => 'æ°›',
  'ª_' => 'æ³£',
  'ª`' => 'æ³¨',
  'ªa' => 'æ³³',
  'ªb' => 'æ²±',
  'ªc' => 'æ³Œ',
  'ªd' => 'æ³¥',
  'ªe' => 'æ²³',
  'ªf' => 'æ²½',
  'ªg' => 'æ²¾',
  'ªh' => 'æ²¼',
  'ªi' => 'æ³¢',
  'ªj' => 'æ²«',
  'ªk' => 'æ³•',
  'ªl' => 'æ³“',
  'ªm' => 'æ²¸',
  'ªn' => 'æ³„',
  'ªo' => 'æ²¹',
  'ªp' => 'æ³�',
  'ªq' => 'æ²®',
  'ªr' => 'æ³—',
  'ªs' => 'æ³…',
  'ªt' => 'æ³±',
  'ªu' => 'æ²¿',
  'ªv' => 'æ²»',
  'ªw' => 'æ³¡',
  'ªx' => 'æ³›',
  'ªy' => 'æ³Š',
  'ªz' => 'æ²¬',
  'ª{' => 'æ³¯',
  'ª|' => 'æ³œ',
  'ª}' => 'æ³–',
  'ª~' => 'æ³ ',
  'ª¡' => 'ç‚•',
  'ª¢' => 'ç‚Ž',
  'ª£' => 'ç‚’',
  'ª¤' => 'ç‚Š',
  'ª¥' => 'ç‚™',
  'ª¦' => 'çˆ¬',
  'ª§' => 'çˆ­',
  'ª¨' => 'çˆ¸',
  'ª©' => 'ç‰ˆ',
  'ªª' => 'ç‰§',
  'ª«' => 'ç‰©',
  'ª¬' => 'ç‹€',
  'ª­' => 'ç‹Ž',
  'ª®' => 'ç‹™',
  'ª¯' => 'ç‹—',
  'ª°' => 'ç‹�',
  'ª±' => 'çŽ©',
  'ª²' => 'çŽ¨',
  'ª³' => 'çŽŸ',
  'ª´' => 'çŽ«',
  'ªµ' => 'çŽ¥',
  'ª¶' => 'ç”½',
  'ª·' => 'ç–�',
  'ª¸' => 'ç–™',
  'ª¹' => 'ç–š',
  'ªº' => 'çš„',
  'ª»' => 'ç›‚',
  'ª¼' => 'ç›²',
  'ª½' => 'ç›´',
  'ª¾' => 'çŸ¥',
  'ª¿' => 'çŸ½',
  'ªÀ' => 'ç¤¾',
  'ªÁ' => 'ç¥€',
  'ªÂ' => 'ç¥�',
  'ªÃ' => 'ç§‰',
  'ªÄ' => 'ç§ˆ',
  'ªÅ' => 'ç©º',
  'ªÆ' => 'ç©¹',
  'ªÇ' => 'ç«º',
  'ªÈ' => 'ç³¾',
  'ªÉ' => 'ç½”',
  'ªÊ' => 'ç¾Œ',
  'ªË' => 'ç¾‹',
  'ªÌ' => 'è€…',
  'ªÍ' => 'è‚º',
  'ªÎ' => 'è‚¥',
  'ªÏ' => 'è‚¢',
  'ªÐ' => 'è‚±',
  'ªÑ' => 'è‚¡',
  'ªÒ' => 'è‚«',
  'ªÓ' => 'è‚©',
  'ªÔ' => 'è‚´',
  'ªÕ' => 'è‚ª',
  'ªÖ' => 'è‚¯',
  'ª×' => 'è‡¥',
  'ªØ' => 'è‡¾',
  'ªÙ' => 'èˆ�',
  'ªÚ' => 'èŠ³',
  'ªÛ' => 'èŠ�',
  'ªÜ' => 'èŠ™',
  'ªÝ' => 'èŠ­',
  'ªÞ' => 'èŠ½',
  'ªß' => 'èŠŸ',
  'ªà' => 'èŠ¹',
  'ªá' => 'èŠ±',
  'ªâ' => 'èŠ¬',
  'ªã' => 'èŠ¥',
  'ªä' => 'èŠ¯',
  'ªå' => 'èŠ¸',
  'ªæ' => 'èŠ£',
  'ªç' => 'èŠ°',
  'ªè' => 'èŠ¾',
  'ªé' => 'èŠ·',
  'ªê' => 'è™Ž',
  'ªë' => 'è™±',
  'ªì' => 'åˆ�',
  'ªí' => 'è¡¨',
  'ªî' => 'è»‹',
  'ªï' => 'è¿Ž',
  'ªð' => 'è¿”',
  'ªñ' => 'è¿‘',
  'ªò' => 'é‚µ',
  'ªó' => 'é‚¸',
  'ªô' => 'é‚±',
  'ªõ' => 'é‚¶',
  'ªö' => 'é‡‡',
  'ª÷' => 'é‡‘',
  'ªø' => 'é•·',
  'ªù' => 'é–€',
  'ªú' => 'é˜œ',
  'ªû' => 'é™€',
  'ªü' => 'é˜¿',
  'ªý' => 'é˜»',
  'ªþ' => 'é™„',
  '«@' => 'é™‚',
  '«A' => 'éš¹',
  '«B' => 'é›¨',
  '«C' => 'é�’',
  '«D' => 'é�ž',
  '«E' => 'äºŸ',
  '«F' => 'äº­',
  '«G' => 'äº®',
  '«H' => 'ä¿¡',
  '«I' => 'ä¾µ',
  '«J' => 'ä¾¯',
  '«K' => 'ä¾¿',
  '«L' => 'ä¿ ',
  '«M' => 'ä¿‘',
  '«N' => 'ä¿�',
  '«O' => 'ä¿�',
  '«P' => 'ä¿ƒ',
  '«Q' => 'ä¾¶',
  '«R' => 'ä¿˜',
  '«S' => 'ä¿Ÿ',
  '«T' => 'ä¿Š',
  '«U' => 'ä¿—',
  '«V' => 'ä¾®',
  '«W' => 'ä¿�',
  '«X' => 'ä¿„',
  '«Y' => 'ä¿‚',
  '«Z' => 'ä¿š',
  '«[' => 'ä¿Ž',
  '«\\' => 'ä¿ž',
  '«]' => 'ä¾·',
  '«^' => 'å…—',
  '«_' => 'å†’',
  '«`' => 'å†‘',
  '«a' => 'å† ',
  '«b' => 'å‰Ž',
  '«c' => 'å‰ƒ',
  '«d' => 'å‰Š',
  '«e' => 'å‰�',
  '«f' => 'å‰Œ',
  '«g' => 'å‰‹',
  '«h' => 'å‰‡',
  '«i' => 'å‹‡',
  '«j' => 'å‹‰',
  '«k' => 'å‹ƒ',
  '«l' => 'å‹�',
  '«m' => 'åŒ�',
  '«n' => 'å�—',
  '«o' => 'å�»',
  '«p' => 'åŽš',
  '«q' => 'å�›',
  '«r' => 'å’¬',
  '«s' => 'å“€',
  '«t' => 'å’¨',
  '«u' => 'å“Ž',
  '«v' => 'å“‰',
  '«w' => 'å’¸',
  '«x' => 'å’¦',
  '«y' => 'å’³',
  '«z' => 'å“‡',
  '«{' => 'å“‚',
  '«|' => 'å’½',
  '«}' => 'å’ª',
  '«~' => 'å“�',
  '«¡' => 'å“„',
  '«¢' => 'å“ˆ',
  '«£' => 'å’¯',
  '«¤' => 'å’«',
  '«¥' => 'å’±',
  '«¦' => 'å’»',
  '«§' => 'å’©',
  '«¨' => 'å’§',
  '«©' => 'å’¿',
  '«ª' => 'å›¿',
  '««' => 'åž‚',
  '«¬' => 'åž‹',
  '«­' => 'åž ',
  '«®' => 'åž£',
  '«¯' => 'åž¢',
  '«°' => 'åŸŽ',
  '«±' => 'åž®',
  '«²' => 'åž“',
  '«³' => 'å¥•',
  '«´' => 'å¥‘',
  '«µ' => 'å¥�',
  '«¶' => 'å¥Ž',
  '«·' => 'å¥�',
  '«¸' => 'å§œ',
  '«¹' => 'å§˜',
  '«º' => 'å§¿',
  '«»' => 'å§£',
  '«¼' => 'å§¨',
  '«½' => 'å¨ƒ',
  '«¾' => 'å§¥',
  '«¿' => 'å§ª',
  '«À' => 'å§š',
  '«Á' => 'å§¦',
  '«Â' => 'å¨�',
  '«Ã' => 'å§»',
  '«Ä' => 'å­©',
  '«Å' => 'å®£',
  '«Æ' => 'å®¦',
  '«Ç' => 'å®¤',
  '«È' => 'å®¢',
  '«É' => 'å®¥',
  '«Ê' => 'å°�',
  '«Ë' => 'å±Ž',
  '«Ì' => 'å±�',
  '«Í' => 'å±�',
  '«Î' => 'å±‹',
  '«Ï' => 'å³™',
  '«Ð' => 'å³’',
  '«Ñ' => 'å··',
  '«Ò' => 'å¸�',
  '«Ó' => 'å¸¥',
  '«Ô' => 'å¸Ÿ',
  '«Õ' => 'å¹½',
  '«Ö' => 'åº ',
  '«×' => 'åº¦',
  '«Ø' => 'å»º',
  '«Ù' => 'å¼ˆ',
  '«Ú' => 'å¼­',
  '«Û' => 'å½¥',
  '«Ü' => 'å¾ˆ',
  '«Ý' => 'å¾…',
  '«Þ' => 'å¾Š',
  '«ß' => 'å¾‹',
  '«à' => 'å¾‡',
  '«á' => 'å¾Œ',
  '«â' => 'å¾‰',
  '«ã' => 'æ€’',
  '«ä' => 'æ€�',
  '«å' => 'æ€ ',
  '«æ' => 'æ€¥',
  '«ç' => 'æ€Ž',
  '«è' => 'æ€¨',
  '«é' => 'æ��',
  '«ê' => 'æ�°',
  '«ë' => 'æ�¨',
  '«ì' => 'æ�¢',
  '«í' => 'æ�†',
  '«î' => 'æ�ƒ',
  '«ï' => 'æ�¬',
  '«ð' => 'æ�«',
  '«ñ' => 'æ�ª',
  '«ò' => 'æ�¤',
  '«ó' => 'æ‰�',
  '«ô' => 'æ‹œ',
  '«õ' => 'æŒ–',
  '«ö' => 'æŒ‰',
  '«÷' => 'æ‹¼',
  '«ø' => 'æ‹­',
  '«ù' => 'æŒ�',
  '«ú' => 'æ‹®',
  '«û' => 'æ‹½',
  '«ü' => 'æŒ‡',
  '«ý' => 'æ‹±',
  '«þ' => 'æ‹·',
  '¬@' => 'æ‹¯',
  '¬A' => 'æ‹¬',
  '¬B' => 'æ‹¾',
  '¬C' => 'æ‹´',
  '¬D' => 'æŒ‘',
  '¬E' => 'æŒ‚',
  '¬F' => 'æ”¿',
  '¬G' => 'æ•…',
  '¬H' => 'æ–«',
  '¬I' => 'æ–½',
  '¬J' => 'æ—¢',
  '¬K' => 'æ˜¥',
  '¬L' => 'æ˜­',
  '¬M' => 'æ˜ ',
  '¬N' => 'æ˜§',
  '¬O' => 'æ˜¯',
  '¬P' => 'æ˜Ÿ',
  '¬Q' => 'æ˜¨',
  '¬R' => 'æ˜±',
  '¬S' => 'æ˜¤',
  '¬T' => 'æ›·',
  '¬U' => 'æŸ¿',
  '¬V' => 'æŸ“',
  '¬W' => 'æŸ±',
  '¬X' => 'æŸ”',
  '¬Y' => 'æŸ�',
  '¬Z' => 'æŸ¬',
  '¬[' => 'æž¶',
  '¬\\' => 'æž¯',
  '¬]' => 'æŸµ',
  '¬^' => 'æŸ©',
  '¬_' => 'æŸ¯',
  '¬`' => 'æŸ„',
  '¬a' => 'æŸ‘',
  '¬b' => 'æž´',
  '¬c' => 'æŸš',
  '¬d' => 'æŸ¥',
  '¬e' => 'æž¸',
  '¬f' => 'æŸ�',
  '¬g' => 'æŸž',
  '¬h' => 'æŸ³',
  '¬i' => 'æž°',
  '¬j' => 'æŸ™',
  '¬k' => 'æŸ¢',
  '¬l' => 'æŸ�',
  '¬m' => 'æŸ’',
  '¬n' => 'æ­ª',
  '¬o' => 'æ®ƒ',
  '¬p' => 'æ®†',
  '¬q' => 'æ®µ',
  '¬r' => 'æ¯’',
  '¬s' => 'æ¯—',
  '¬t' => 'æ°Ÿ',
  '¬u' => 'æ³‰',
  '¬v' => 'æ´‹',
  '¬w' => 'æ´²',
  '¬x' => 'æ´ª',
  '¬y' => 'æµ�',
  '¬z' => 'æ´¥',
  '¬{' => 'æ´Œ',
  '¬|' => 'æ´±',
  '¬}' => 'æ´ž',
  '¬~' => 'æ´—',
  '¬¡' => 'æ´»',
  '¬¢' => 'æ´½',
  '¬£' => 'æ´¾',
  '¬¤' => 'æ´¶',
  '¬¥' => 'æ´›',
  '¬¦' => 'æ³µ',
  '¬§' => 'æ´¹',
  '¬¨' => 'æ´§',
  '¬©' => 'æ´¸',
  '¬ª' => 'æ´©',
  '¬«' => 'æ´®',
  '¬¬' => 'æ´µ',
  '¬­' => 'æ´Ž',
  '¬®' => 'æ´«',
  '¬¯' => 'ç‚«',
  '¬°' => 'ç‚º',
  '¬±' => 'ç‚³',
  '¬²' => 'ç‚¬',
  '¬³' => 'ç‚¯',
  '¬´' => 'ç‚­',
  '¬µ' => 'ç‚¸',
  '¬¶' => 'ç‚®',
  '¬·' => 'ç‚¤',
  '¬¸' => 'çˆ°',
  '¬¹' => 'ç‰²',
  '¬º' => 'ç‰¯',
  '¬»' => 'ç‰´',
  '¬¼' => 'ç‹©',
  '¬½' => 'ç‹ ',
  '¬¾' => 'ç‹¡',
  '¬¿' => 'çŽ·',
  '¬À' => 'ç�Š',
  '¬Á' => 'çŽ»',
  '¬Â' => 'çŽ²',
  '¬Ã' => 'ç��',
  '¬Ä' => 'ç�€',
  '¬Å' => 'çŽ³',
  '¬Æ' => 'ç”š',
  '¬Ç' => 'ç”­',
  '¬È' => 'ç•�',
  '¬É' => 'ç•Œ',
  '¬Ê' => 'ç•Ž',
  '¬Ë' => 'ç•‹',
  '¬Ì' => 'ç–«',
  '¬Í' => 'ç–¤',
  '¬Î' => 'ç–¥',
  '¬Ï' => 'ç–¢',
  '¬Ð' => 'ç–£',
  '¬Ñ' => 'ç™¸',
  '¬Ò' => 'çš†',
  '¬Ó' => 'çš‡',
  '¬Ô' => 'çšˆ',
  '¬Õ' => 'ç›ˆ',
  '¬Ö' => 'ç›†',
  '¬×' => 'ç›ƒ',
  '¬Ø' => 'ç›…',
  '¬Ù' => 'çœ�',
  '¬Ú' => 'ç›¹',
  '¬Û' => 'ç›¸',
  '¬Ü' => 'çœ‰',
  '¬Ý' => 'çœ‹',
  '¬Þ' => 'ç›¾',
  '¬ß' => 'ç›¼',
  '¬à' => 'çœ‡',
  '¬á' => 'çŸœ',
  '¬â' => 'ç ‚',
  '¬ã' => 'ç ”',
  '¬ä' => 'ç Œ',
  '¬å' => 'ç �',
  '¬æ' => 'ç¥†',
  '¬ç' => 'ç¥‰',
  '¬è' => 'ç¥ˆ',
  '¬é' => 'ç¥‡',
  '¬ê' => 'ç¦¹',
  '¬ë' => 'ç¦º',
  '¬ì' => 'ç§‘',
  '¬í' => 'ç§’',
  '¬î' => 'ç§‹',
  '¬ï' => 'ç©¿',
  '¬ð' => 'çª�',
  '¬ñ' => 'ç«¿',
  '¬ò' => 'ç«½',
  '¬ó' => 'ç±½',
  '¬ô' => 'ç´‚',
  '¬õ' => 'ç´…',
  '¬ö' => 'ç´€',
  '¬÷' => 'ç´‰',
  '¬ø' => 'ç´‡',
  '¬ù' => 'ç´„',
  '¬ú' => 'ç´†',
  '¬û' => 'ç¼¸',
  '¬ü' => 'ç¾Ž',
  '¬ý' => 'ç¾¿',
  '¬þ' => 'è€„',
  '­@' => 'è€�',
  '­A' => 'è€�',
  '­B' => 'è€‘',
  '­C' => 'è€¶',
  '­D' => 'èƒ–',
  '­E' => 'èƒ¥',
  '­F' => 'èƒš',
  '­G' => 'èƒƒ',
  '­H' => 'èƒ„',
  '­I' => 'èƒŒ',
  '­J' => 'èƒ¡',
  '­K' => 'èƒ›',
  '­L' => 'èƒŽ',
  '­M' => 'èƒž',
  '­N' => 'èƒ¤',
  '­O' => 'èƒ�',
  '­P' => 'è‡´',
  '­Q' => 'èˆ¢',
  '­R' => 'è‹§',
  '­S' => 'èŒƒ',
  '­T' => 'èŒ…',
  '­U' => 'è‹£',
  '­V' => 'è‹›',
  '­W' => 'è‹¦',
  '­X' => 'èŒ„',
  '­Y' => 'è‹¥',
  '­Z' => 'èŒ‚',
  '­[' => 'èŒ‰',
  '­\\' => 'è‹’',
  '­]' => 'è‹—',
  '­^' => 'è‹±',
  '­_' => 'èŒ�',
  '­`' => 'è‹œ',
  '­a' => 'è‹”',
  '­b' => 'è‹‘',
  '­c' => 'è‹ž',
  '­d' => 'è‹“',
  '­e' => 'è‹Ÿ',
  '­f' => 'è‹¯',
  '­g' => 'èŒ†',
  '­h' => 'è™�',
  '­i' => 'è™¹',
  '­j' => 'è™»',
  '­k' => 'è™º',
  '­l' => 'è¡�',
  '­m' => 'è¡«',
  '­n' => 'è¦�',
  '­o' => 'è§”',
  '­p' => 'è¨ˆ',
  '­q' => 'è¨‚',
  '­r' => 'è¨ƒ',
  '­s' => 'è²ž',
  '­t' => 'è² ',
  '­u' => 'èµ´',
  '­v' => 'èµ³',
  '­w' => 'è¶´',
  '­x' => 'è»�',
  '­y' => 'è»Œ',
  '­z' => 'è¿°',
  '­{' => 'è¿¦',
  '­|' => 'è¿¢',
  '­}' => 'è¿ª',
  '­~' => 'è¿¥',
  '­¡' => 'è¿­',
  '­¢' => 'è¿«',
  '­£' => 'è¿¤',
  '­¤' => 'è¿¨',
  '­¥' => 'éƒŠ',
  '­¦' => 'éƒŽ',
  '­§' => 'éƒ�',
  '­¨' => 'éƒƒ',
  '­©' => 'é…‹',
  '­ª' => 'é…Š',
  '­«' => 'é‡�',
  '­¬' => 'é–‚',
  '­­' => 'é™�',
  '­®' => 'é™‹',
  '­¯' => 'é™Œ',
  '­°' => 'é™�',
  '­±' => 'é�¢',
  '­²' => 'é�©',
  '­³' => 'éŸ‹',
  '­´' => 'éŸ­',
  '­µ' => 'éŸ³',
  '­¶' => 'é �',
  '­·' => 'é¢¨',
  '­¸' => 'é£›',
  '­¹' => 'é£Ÿ',
  '­º' => 'é¦–',
  '­»' => 'é¦™',
  '­¼' => 'ä¹˜',
  '­½' => 'äº³',
  '­¾' => 'å€Œ',
  '­¿' => 'å€�',
  '­À' => 'å€£',
  '­Á' => 'ä¿¯',
  '­Â' => 'å€¦',
  '­Ã' => 'å€¥',
  '­Ä' => 'ä¿¸',
  '­Å' => 'å€©',
  '­Æ' => 'å€–',
  '­Ç' => 'å€†',
  '­È' => 'å€¼',
  '­É' => 'å€Ÿ',
  '­Ê' => 'å€š',
  '­Ë' => 'å€’',
  '­Ì' => 'å€‘',
  '­Í' => 'ä¿º',
  '­Î' => 'å€€',
  '­Ï' => 'å€”',
  '­Ð' => 'å€¨',
  '­Ñ' => 'ä¿±',
  '­Ò' => 'å€¡',
  '­Ó' => 'å€‹',
  '­Ô' => 'å€™',
  '­Õ' => 'å€˜',
  '­Ö' => 'ä¿³',
  '­×' => 'ä¿®',
  '­Ø' => 'å€­',
  '­Ù' => 'å€ª',
  '­Ú' => 'ä¿¾',
  '­Û' => 'å€«',
  '­Ü' => 'å€‰',
  '­Ý' => 'å…¼',
  '­Þ' => 'å†¤',
  '­ß' => 'å†¥',
  '­à' => 'å†¢',
  '­á' => 'å‡�',
  '­â' => 'å‡Œ',
  '­ã' => 'å‡†',
  '­ä' => 'å‡‹',
  '­å' => 'å‰–',
  '­æ' => 'å‰œ',
  '­ç' => 'å‰”',
  '­è' => 'å‰›',
  '­é' => 'å‰�',
  '­ê' => 'åŒª',
  '­ë' => 'å�¿',
  '­ì' => 'åŽŸ',
  '­í' => 'åŽ�',
  '­î' => 'å�Ÿ',
  '­ï' => 'å“¨',
  '­ð' => 'å”�',
  '­ñ' => 'å”�',
  '­ò' => 'å”·',
  '­ó' => 'å“¼',
  '­ô' => 'å“¥',
  '­õ' => 'å“²',
  '­ö' => 'å”†',
  '­÷' => 'å“º',
  '­ø' => 'å””',
  '­ù' => 'å“©',
  '­ú' => 'å“­',
  '­û' => 'å“¡',
  '­ü' => 'å”‰',
  '­ý' => 'å“®',
  '­þ' => 'å“ª',
  '®@' => 'å“¦',
  '®A' => 'å”§',
  '®B' => 'å”‡',
  '®C' => 'å“½',
  '®D' => 'å”�',
  '®E' => 'åœƒ',
  '®F' => 'åœ„',
  '®G' => 'åŸ‚',
  '®H' => 'åŸ”',
  '®I' => 'åŸ‹',
  '®J' => 'åŸƒ',
  '®K' => 'å ‰',
  '®L' => 'å¤�',
  '®M' => 'å¥—',
  '®N' => 'å¥˜',
  '®O' => 'å¥š',
  '®P' => 'å¨‘',
  '®Q' => 'å¨˜',
  '®R' => 'å¨œ',
  '®S' => 'å¨Ÿ',
  '®T' => 'å¨›',
  '®U' => 'å¨“',
  '®V' => 'å§¬',
  '®W' => 'å¨ ',
  '®X' => 'å¨£',
  '®Y' => 'å¨©',
  '®Z' => 'å¨¥',
  '®[' => 'å¨Œ',
  '®\\' => 'å¨‰',
  '®]' => 'å­«',
  '®^' => 'å±˜',
  '®_' => 'å®°',
  '®`' => 'å®³',
  '®a' => 'å®¶',
  '®b' => 'å®´',
  '®c' => 'å®®',
  '®d' => 'å®µ',
  '®e' => 'å®¹',
  '®f' => 'å®¸',
  '®g' => 'å°„',
  '®h' => 'å±‘',
  '®i' => 'å±•',
  '®j' => 'å±�',
  '®k' => 'å³­',
  '®l' => 'å³½',
  '®m' => 'å³»',
  '®n' => 'å³ª',
  '®o' => 'å³¨',
  '®p' => 'å³°',
  '®q' => 'å³¶',
  '®r' => 'å´�',
  '®s' => 'å³´',
  '®t' => 'å·®',
  '®u' => 'å¸­',
  '®v' => 'å¸«',
  '®w' => 'åº«',
  '®x' => 'åº­',
  '®y' => 'åº§',
  '®z' => 'å¼±',
  '®{' => 'å¾’',
  '®|' => 'å¾‘',
  '®}' => 'å¾�',
  '®~' => 'æ�™',
  '®¡' => 'æ�£',
  '®¢' => 'æ�¥',
  '®£' => 'æ��',
  '®¤' => 'æ�•',
  '®¥' => 'æ�­',
  '®¦' => 'æ�©',
  '®§' => 'æ�¯',
  '®¨' => 'æ‚„',
  '®©' => 'æ‚Ÿ',
  '®ª' => 'æ‚š',
  '®«' => 'æ‚�',
  '®¬' => 'æ‚”',
  '®­' => 'æ‚Œ',
  '®®' => 'æ‚…',
  '®¯' => 'æ‚–',
  '®°' => 'æ‰‡',
  '®±' => 'æ‹³',
  '®²' => 'æŒˆ',
  '®³' => 'æ‹¿',
  '®´' => 'æ�Ž',
  '®µ' => 'æŒ¾',
  '®¶' => 'æŒ¯',
  '®·' => 'æ�•',
  '®¸' => 'æ�‚',
  '®¹' => 'æ�†',
  '®º' => 'æ��',
  '®»' => 'æ�‰',
  '®¼' => 'æŒº',
  '®½' => 'æ��',
  '®¾' => 'æŒ½',
  '®¿' => 'æŒª',
  '®À' => 'æŒ«',
  '®Á' => 'æŒ¨',
  '®Â' => 'æ��',
  '®Ã' => 'æ�Œ',
  '®Ä' => 'æ•ˆ',
  '®Å' => 'æ•‰',
  '®Æ' => 'æ–™',
  '®Ç' => 'æ—�',
  '®È' => 'æ—…',
  '®É' => 'æ™‚',
  '®Ê' => 'æ™‰',
  '®Ë' => 'æ™�',
  '®Ì' => 'æ™ƒ',
  '®Í' => 'æ™’',
  '®Î' => 'æ™Œ',
  '®Ï' => 'æ™…',
  '®Ð' => 'æ™�',
  '®Ñ' => 'æ›¸',
  '®Ò' => 'æœ”',
  '®Ó' => 'æœ•',
  '®Ô' => 'æœ—',
  '®Õ' => 'æ ¡',
  '®Ö' => 'æ ¸',
  '®×' => 'æ¡ˆ',
  '®Ø' => 'æ¡†',
  '®Ù' => 'æ¡“',
  '®Ú' => 'æ ¹',
  '®Û' => 'æ¡‚',
  '®Ü' => 'æ¡”',
  '®Ý' => 'æ ©',
  '®Þ' => 'æ¢³',
  '®ß' => 'æ —',
  '®à' => 'æ¡Œ',
  '®á' => 'æ¡‘',
  '®â' => 'æ ½',
  '®ã' => 'æŸ´',
  '®ä' => 'æ¡�',
  '®å' => 'æ¡€',
  '®æ' => 'æ ¼',
  '®ç' => 'æ¡ƒ',
  '®è' => 'æ ª',
  '®é' => 'æ¡…',
  '®ê' => 'æ “',
  '®ë' => 'æ ˜',
  '®ì' => 'æ¡�',
  '®í' => 'æ®Š',
  '®î' => 'æ®‰',
  '®ï' => 'æ®·',
  '®ð' => 'æ°£',
  '®ñ' => 'æ°§',
  '®ò' => 'æ°¨',
  '®ó' => 'æ°¦',
  '®ô' => 'æ°¤',
  '®õ' => 'æ³°',
  '®ö' => 'æµª',
  '®÷' => 'æ¶•',
  '®ø' => 'æ¶ˆ',
  '®ù' => 'æ¶‡',
  '®ú' => 'æµ¦',
  '®û' => 'æµ¸',
  '®ü' => 'æµ·',
  '®ý' => 'æµ™',
  '®þ' => 'æ¶“',
  '¯@' => 'æµ¬',
  '¯A' => 'æ¶‰',
  '¯B' => 'æµ®',
  '¯C' => 'æµš',
  '¯D' => 'æµ´',
  '¯E' => 'æµ©',
  '¯F' => 'æ¶Œ',
  '¯G' => 'æ¶Š',
  '¯H' => 'æµ¹',
  '¯I' => 'æ¶…',
  '¯J' => 'æµ¥',
  '¯K' => 'æ¶”',
  '¯L' => 'çƒŠ',
  '¯M' => 'çƒ˜',
  '¯N' => 'çƒ¤',
  '¯O' => 'çƒ™',
  '¯P' => 'çƒˆ',
  '¯Q' => 'çƒ�',
  '¯R' => 'çˆ¹',
  '¯S' => 'ç‰¹',
  '¯T' => 'ç‹¼',
  '¯U' => 'ç‹¹',
  '¯V' => 'ç‹½',
  '¯W' => 'ç‹¸',
  '¯X' => 'ç‹·',
  '¯Y' => 'çŽ†',
  '¯Z' => 'ç�­',
  '¯[' => 'ç�‰',
  '¯\\' => 'ç�®',
  '¯]' => 'ç� ',
  '¯^' => 'ç�ª',
  '¯_' => 'ç�ž',
  '¯`' => 'ç•”',
  '¯a' => 'ç•�',
  '¯b' => 'ç•œ',
  '¯c' => 'ç•š',
  '¯d' => 'ç•™',
  '¯e' => 'ç–¾',
  '¯f' => 'ç—…',
  '¯g' => 'ç—‡',
  '¯h' => 'ç–²',
  '¯i' => 'ç–³',
  '¯j' => 'ç–½',
  '¯k' => 'ç–¼',
  '¯l' => 'ç–¹',
  '¯m' => 'ç—‚',
  '¯n' => 'ç–¸',
  '¯o' => 'çš‹',
  '¯p' => 'çš°',
  '¯q' => 'ç›Š',
  '¯r' => 'ç›�',
  '¯s' => 'ç›Ž',
  '¯t' => 'çœ©',
  '¯u' => 'çœŸ',
  '¯v' => 'çœ ',
  '¯w' => 'çœ¨',
  '¯x' => 'çŸ©',
  '¯y' => 'ç °',
  '¯z' => 'ç §',
  '¯{' => 'ç ¸',
  '¯|' => 'ç �',
  '¯}' => 'ç ´',
  '¯~' => 'ç ·',
  '¯¡' => 'ç ¥',
  '¯¢' => 'ç ­',
  '¯£' => 'ç  ',
  '¯¤' => 'ç Ÿ',
  '¯¥' => 'ç ²',
  '¯¦' => 'ç¥•',
  '¯§' => 'ç¥�',
  '¯¨' => 'ç¥ ',
  '¯©' => 'ç¥Ÿ',
  '¯ª' => 'ç¥–',
  '¯«' => 'ç¥ž',
  '¯¬' => 'ç¥�',
  '¯­' => 'ç¥—',
  '¯®' => 'ç¥š',
  '¯¯' => 'ç§¤',
  '¯°' => 'ç§£',
  '¯±' => 'ç§§',
  '¯²' => 'ç§Ÿ',
  '¯³' => 'ç§¦',
  '¯´' => 'ç§©',
  '¯µ' => 'ç§˜',
  '¯¶' => 'çª„',
  '¯·' => 'çªˆ',
  '¯¸' => 'ç«™',
  '¯¹' => 'ç¬†',
  '¯º' => 'ç¬‘',
  '¯»' => 'ç²‰',
  '¯¼' => 'ç´¡',
  '¯½' => 'ç´—',
  '¯¾' => 'ç´‹',
  '¯¿' => 'ç´Š',
  '¯À' => 'ç´ ',
  '¯Á' => 'ç´¢',
  '¯Â' => 'ç´”',
  '¯Ã' => 'ç´�',
  '¯Ä' => 'ç´•',
  '¯Å' => 'ç´š',
  '¯Æ' => 'ç´œ',
  '¯Ç' => 'ç´�',
  '¯È' => 'ç´™',
  '¯É' => 'ç´›',
  '¯Ê' => 'ç¼º',
  '¯Ë' => 'ç½Ÿ',
  '¯Ì' => 'ç¾”',
  '¯Í' => 'ç¿…',
  '¯Î' => 'ç¿�',
  '¯Ï' => 'è€†',
  '¯Ð' => 'è€˜',
  '¯Ñ' => 'è€•',
  '¯Ò' => 'è€™',
  '¯Ó' => 'è€—',
  '¯Ô' => 'è€½',
  '¯Õ' => 'è€¿',
  '¯Ö' => 'èƒ±',
  '¯×' => 'è„‚',
  '¯Ø' => 'èƒ°',
  '¯Ù' => 'è„…',
  '¯Ú' => 'èƒ­',
  '¯Û' => 'èƒ´',
  '¯Ü' => 'è„†',
  '¯Ý' => 'èƒ¸',
  '¯Þ' => 'èƒ³',
  '¯ß' => 'è„ˆ',
  '¯à' => 'èƒ½',
  '¯á' => 'è„Š',
  '¯â' => 'èƒ¼',
  '¯ã' => 'èƒ¯',
  '¯ä' => 'è‡­',
  '¯å' => 'è‡¬',
  '¯æ' => 'èˆ€',
  '¯ç' => 'èˆ�',
  '¯è' => 'èˆª',
  '¯é' => 'èˆ«',
  '¯ê' => 'èˆ¨',
  '¯ë' => 'èˆ¬',
  '¯ì' => 'èŠ»',
  '¯í' => 'èŒ«',
  '¯î' => 'è�’',
  '¯ï' => 'è�”',
  '¯ð' => 'è�Š',
  '¯ñ' => 'èŒ¸',
  '¯ò' => 'è��',
  '¯ó' => 'è�‰',
  '¯ô' => 'èŒµ',
  '¯õ' => 'èŒ´',
  '¯ö' => 'è��',
  '¯÷' => 'èŒ²',
  '¯ø' => 'èŒ¹',
  '¯ù' => 'èŒ¶',
  '¯ú' => 'èŒ—',
  '¯û' => 'è�€',
  '¯ü' => 'èŒ±',
  '¯ý' => 'èŒ¨',
  '¯þ' => 'è�ƒ',
  '°@' => 'è™”',
  '°A' => 'èšŠ',
  '°B' => 'èšª',
  '°C' => 'èš“',
  '°D' => 'èš¤',
  '°E' => 'èš©',
  '°F' => 'èšŒ',
  '°G' => 'èš£',
  '°H' => 'èšœ',
  '°I' => 'è¡°',
  '°J' => 'è¡·',
  '°K' => 'è¢�',
  '°L' => 'è¢‚',
  '°M' => 'è¡½',
  '°N' => 'è¡¹',
  '°O' => 'è¨˜',
  '°P' => 'è¨�',
  '°Q' => 'è¨Ž',
  '°R' => 'è¨Œ',
  '°S' => 'è¨•',
  '°T' => 'è¨Š',
  '°U' => 'è¨—',
  '°V' => 'è¨“',
  '°W' => 'è¨–',
  '°X' => 'è¨�',
  '°Y' => 'è¨‘',
  '°Z' => 'è±ˆ',
  '°[' => 'è±º',
  '°\\' => 'è±¹',
  '°]' => 'è²¡',
  '°^' => 'è²¢',
  '°_' => 'èµ·',
  '°`' => 'èº¬',
  '°a' => 'è»’',
  '°b' => 'è»”',
  '°c' => 'è»�',
  '°d' => 'è¾±',
  '°e' => 'é€�',
  '°f' => 'é€†',
  '°g' => 'è¿·',
  '°h' => 'é€€',
  '°i' => 'è¿º',
  '°j' => 'è¿´',
  '°k' => 'é€ƒ',
  '°l' => 'è¿½',
  '°m' => 'é€…',
  '°n' => 'è¿¸',
  '°o' => 'é‚•',
  '°p' => 'éƒ¡',
  '°q' => 'éƒ�',
  '°r' => 'éƒ¢',
  '°s' => 'é…’',
  '°t' => 'é…�',
  '°u' => 'é…Œ',
  '°v' => 'é‡˜',
  '°w' => 'é‡�',
  '°x' => 'é‡—',
  '°y' => 'é‡œ',
  '°z' => 'é‡™',
  '°{' => 'é–ƒ',
  '°|' => 'é™¢',
  '°}' => 'é™£',
  '°~' => 'é™¡',
  '°¡' => 'é™›',
  '°¢' => 'é™�',
  '°£' => 'é™¤',
  '°¤' => 'é™˜',
  '°¥' => 'é™ž',
  '°¦' => 'éš»',
  '°§' => 'é£¢',
  '°¨' => 'é¦¬',
  '°©' => 'éª¨',
  '°ª' => 'é«˜',
  '°«' => 'é¬¥',
  '°¬' => 'é¬²',
  '°­' => 'é¬¼',
  '°®' => 'ä¹¾',
  '°¯' => 'å�º',
  '°°' => 'å�½',
  '°±' => 'å�œ',
  '°²' => 'å�‡',
  '°³' => 'å�ƒ',
  '°´' => 'å�Œ',
  '°µ' => 'å�š',
  '°¶' => 'å�‰',
  '°·' => 'å�¥',
  '°¸' => 'å�¶',
  '°¹' => 'å�Ž',
  '°º' => 'å�•',
  '°»' => 'å�µ',
  '°¼' => 'å�´',
  '°½' => 'å�·',
  '°¾' => 'å��',
  '°¿' => 'å€�',
  '°À' => 'å�¯',
  '°Á' => 'å�­',
  '°Â' => 'å…œ',
  '°Ã' => 'å†•',
  '°Ä' => 'å‡°',
  '°Å' => 'å‰ª',
  '°Æ' => 'å‰¯',
  '°Ç' => 'å‹’',
  '°È' => 'å‹™',
  '°É' => 'å‹˜',
  '°Ê' => 'å‹•',
  '°Ë' => 'åŒ�',
  '°Ì' => 'åŒ�',
  '°Í' => 'åŒ™',
  '°Î' => 'åŒ¿',
  '°Ï' => 'å�€',
  '°Ð' => 'åŒ¾',
  '°Ñ' => 'å�ƒ',
  '°Ò' => 'æ›¼',
  '°Ó' => 'å•†',
  '°Ô' => 'å•ª',
  '°Õ' => 'å•¦',
  '°Ö' => 'å•„',
  '°×' => 'å•ž',
  '°Ø' => 'å•¡',
  '°Ù' => 'å•ƒ',
  '°Ú' => 'å•Š',
  '°Û' => 'å”±',
  '°Ü' => 'å•–',
  '°Ý' => 'å•�',
  '°Þ' => 'å••',
  '°ß' => 'å”¯',
  '°à' => 'å•¤',
  '°á' => 'å”¸',
  '°â' => 'å”®',
  '°ã' => 'å•œ',
  '°ä' => 'å”¬',
  '°å' => 'å•£',
  '°æ' => 'å”³',
  '°ç' => 'å•�',
  '°è' => 'å•—',
  '°é' => 'åœˆ',
  '°ê' => 'åœ‹',
  '°ë' => 'åœ‰',
  '°ì' => 'åŸŸ',
  '°í' => 'å …',
  '°î' => 'å Š',
  '°ï' => 'å †',
  '°ð' => 'åŸ ',
  '°ñ' => 'åŸ¤',
  '°ò' => 'åŸº',
  '°ó' => 'å ‚',
  '°ô' => 'å µ',
  '°õ' => 'åŸ·',
  '°ö' => 'åŸ¹',
  '°÷' => 'å¤ ',
  '°ø' => 'å¥¢',
  '°ù' => 'å¨¶',
  '°ú' => 'å©�',
  '°û' => 'å©‰',
  '°ü' => 'å©¦',
  '°ý' => 'å©ª',
  '°þ' => 'å©€',
  '±@' => 'å¨¼',
  '±A' => 'å©¢',
  '±B' => 'å©š',
  '±C' => 'å©†',
  '±D' => 'å©Š',
  '±E' => 'å­°',
  '±F' => 'å¯‡',
  '±G' => 'å¯…',
  '±H' => 'å¯„',
  '±I' => 'å¯‚',
  '±J' => 'å®¿',
  '±K' => 'å¯†',
  '±L' => 'å°‰',
  '±M' => 'å°ˆ',
  '±N' => 'å°‡',
  '±O' => 'å± ',
  '±P' => 'å±œ',
  '±Q' => 'å±�',
  '±R' => 'å´‡',
  '±S' => 'å´†',
  '±T' => 'å´Ž',
  '±U' => 'å´›',
  '±V' => 'å´–',
  '±W' => 'å´¢',
  '±X' => 'å´‘',
  '±Y' => 'å´©',
  '±Z' => 'å´”',
  '±[' => 'å´™',
  '±\\' => 'å´¤',
  '±]' => 'å´§',
  '±^' => 'å´—',
  '±_' => 'å·¢',
  '±`' => 'å¸¸',
  '±a' => 'å¸¶',
  '±b' => 'å¸³',
  '±c' => 'å¸·',
  '±d' => 'åº·',
  '±e' => 'åº¸',
  '±f' => 'åº¶',
  '±g' => 'åºµ',
  '±h' => 'åº¾',
  '±i' => 'å¼µ',
  '±j' => 'å¼·',
  '±k' => 'å½—',
  '±l' => 'å½¬',
  '±m' => 'å½©',
  '±n' => 'å½«',
  '±o' => 'å¾—',
  '±p' => 'å¾™',
  '±q' => 'å¾ž',
  '±r' => 'å¾˜',
  '±s' => 'å¾¡',
  '±t' => 'å¾ ',
  '±u' => 'å¾œ',
  '±v' => 'æ�¿',
  '±w' => 'æ‚£',
  '±x' => 'æ‚‰',
  '±y' => 'æ‚ ',
  '±z' => 'æ‚¨',
  '±{' => 'æƒ‹',
  '±|' => 'æ‚´',
  '±}' => 'æƒ¦',
  '±~' => 'æ‚½',
  '±¡' => 'æƒ…',
  '±¢' => 'æ‚»',
  '±£' => 'æ‚µ',
  '±¤' => 'æƒœ',
  '±¥' => 'æ‚¼',
  '±¦' => 'æƒ˜',
  '±§' => 'æƒ•',
  '±¨' => 'æƒ†',
  '±©' => 'æƒŸ',
  '±ª' => 'æ‚¸',
  '±«' => 'æƒš',
  '±¬' => 'æƒ‡',
  '±­' => 'æˆš',
  '±®' => 'æˆ›',
  '±¯' => 'æ‰ˆ',
  '±°' => 'æŽ ',
  '±±' => 'æŽ§',
  '±²' => 'æ�²',
  '±³' => 'æŽ–',
  '±´' => 'æŽ¢',
  '±µ' => 'æŽ¥',
  '±¶' => 'æ�·',
  '±·' => 'æ�§',
  '±¸' => 'æŽ˜',
  '±¹' => 'æŽª',
  '±º' => 'æ�±',
  '±»' => 'æŽ©',
  '±¼' => 'æŽ‰',
  '±½' => 'æŽƒ',
  '±¾' => 'æŽ›',
  '±¿' => 'æ�«',
  '±À' => 'æŽ¨',
  '±Á' => 'æŽ„',
  '±Â' => 'æŽˆ',
  '±Ã' => 'æŽ™',
  '±Ä' => 'æŽ¡',
  '±Å' => 'æŽ¬',
  '±Æ' => 'æŽ’',
  '±Ç' => 'æŽ�',
  '±È' => 'æŽ€',
  '±É' => 'æ�»',
  '±Ê' => 'æ�©',
  '±Ë' => 'æ�¨',
  '±Ì' => 'æ�º',
  '±Í' => 'æ•�',
  '±Î' => 'æ•–',
  '±Ï' => 'æ•‘',
  '±Ð' => 'æ•™',
  '±Ñ' => 'æ•—',
  '±Ò' => 'å•Ÿ',
  '±Ó' => 'æ•�',
  '±Ô' => 'æ•˜',
  '±Õ' => 'æ••',
  '±Ö' => 'æ•”',
  '±×' => 'æ–œ',
  '±Ø' => 'æ–›',
  '±Ù' => 'æ–¬',
  '±Ú' => 'æ—�',
  '±Û' => 'æ—‹',
  '±Ü' => 'æ—Œ',
  '±Ý' => 'æ—Ž',
  '±Þ' => 'æ™�',
  '±ß' => 'æ™š',
  '±à' => 'æ™¤',
  '±á' => 'æ™¨',
  '±â' => 'æ™¦',
  '±ã' => 'æ™ž',
  '±ä' => 'æ›¹',
  '±å' => 'å‹—',
  '±æ' => 'æœ›',
  '±ç' => 'æ¢�',
  '±è' => 'æ¢¯',
  '±é' => 'æ¢¢',
  '±ê' => 'æ¢“',
  '±ë' => 'æ¢µ',
  '±ì' => 'æ¡¿',
  '±í' => 'æ¡¶',
  '±î' => 'æ¢±',
  '±ï' => 'æ¢§',
  '±ð' => 'æ¢—',
  '±ñ' => 'æ¢°',
  '±ò' => 'æ¢ƒ',
  '±ó' => 'æ£„',
  '±ô' => 'æ¢­',
  '±õ' => 'æ¢†',
  '±ö' => 'æ¢…',
  '±÷' => 'æ¢”',
  '±ø' => 'æ¢�',
  '±ù' => 'æ¢¨',
  '±ú' => 'æ¢Ÿ',
  '±û' => 'æ¢¡',
  '±ü' => 'æ¢‚',
  '±ý' => 'æ¬²',
  '±þ' => 'æ®º',
  '²@' => 'æ¯«',
  '²A' => 'æ¯¬',
  '²B' => 'æ°«',
  '²C' => 'æ¶Ž',
  '²D' => 'æ¶¼',
  '²E' => 'æ·³',
  '²F' => 'æ·™',
  '²G' => 'æ¶²',
  '²H' => 'æ·¡',
  '²I' => 'æ·Œ',
  '²J' => 'æ·¤',
  '²K' => 'æ·»',
  '²L' => 'æ·º',
  '²M' => 'æ¸…',
  '²N' => 'æ·‡',
  '²O' => 'æ·‹',
  '²P' => 'æ¶¯',
  '²Q' => 'æ·‘',
  '²R' => 'æ¶®',
  '²S' => 'æ·ž',
  '²T' => 'æ·¹',
  '²U' => 'æ¶¸',
  '²V' => 'æ··',
  '²W' => 'æ·µ',
  '²X' => 'æ·…',
  '²Y' => 'æ·’',
  '²Z' => 'æ¸š',
  '²[' => 'æ¶µ',
  '²\\' => 'æ·š',
  '²]' => 'æ·«',
  '²^' => 'æ·˜',
  '²_' => 'æ·ª',
  '²`' => 'æ·±',
  '²a' => 'æ·®',
  '²b' => 'æ·¨',
  '²c' => 'æ·†',
  '²d' => 'æ·„',
  '²e' => 'æ¶ª',
  '²f' => 'æ·¬',
  '²g' => 'æ¶¿',
  '²h' => 'æ·¦',
  '²i' => 'çƒ¹',
  '²j' => 'ç„‰',
  '²k' => 'ç„Š',
  '²l' => 'çƒ½',
  '²m' => 'çƒ¯',
  '²n' => 'çˆ½',
  '²o' => 'ç‰½',
  '²p' => 'çŠ�',
  '²q' => 'çŒœ',
  '²r' => 'çŒ›',
  '²s' => 'çŒ–',
  '²t' => 'çŒ“',
  '²u' => 'çŒ™',
  '²v' => 'çŽ‡',
  '²w' => 'ç�…',
  '²x' => 'ç�Š',
  '²y' => 'ç�ƒ',
  '²z' => 'ç�†',
  '²{' => 'ç�¾',
  '²|' => 'ç��',
  '²}' => 'ç“ ',
  '²~' => 'ç“¶',
  '²¡' => 'ç“·',
  '²¢' => 'ç”œ',
  '²£' => 'ç”¢',
  '²¤' => 'ç•¥',
  '²¥' => 'ç•¦',
  '²¦' => 'ç•¢',
  '²§' => 'ç•°',
  '²¨' => 'ç–�',
  '²©' => 'ç—”',
  '²ª' => 'ç—•',
  '²«' => 'ç–µ',
  '²¬' => 'ç—Š',
  '²­' => 'ç—�',
  '²®' => 'çšŽ',
  '²¯' => 'ç›”',
  '²°' => 'ç›’',
  '²±' => 'ç››',
  '²²' => 'çœ·',
  '²³' => 'çœ¾',
  '²´' => 'çœ¼',
  '²µ' => 'çœ¶',
  '²¶' => 'çœ¸',
  '²·' => 'çœº',
  '²¸' => 'ç¡«',
  '²¹' => 'ç¡ƒ',
  '²º' => 'ç¡Ž',
  '²»' => 'ç¥¥',
  '²¼' => 'ç¥¨',
  '²½' => 'ç¥­',
  '²¾' => 'ç§»',
  '²¿' => 'çª’',
  '²À' => 'çª•',
  '²Á' => 'ç¬ ',
  '²Â' => 'ç¬¨',
  '²Ã' => 'ç¬›',
  '²Ä' => 'ç¬¬',
  '²Å' => 'ç¬¦',
  '²Æ' => 'ç¬™',
  '²Ç' => 'ç¬ž',
  '²È' => 'ç¬®',
  '²É' => 'ç²’',
  '²Ê' => 'ç²—',
  '²Ë' => 'ç²•',
  '²Ì' => 'çµ†',
  '²Í' => 'çµƒ',
  '²Î' => 'çµ±',
  '²Ï' => 'ç´®',
  '²Ð' => 'ç´¹',
  '²Ñ' => 'ç´¼',
  '²Ò' => 'çµ€',
  '²Ó' => 'ç´°',
  '²Ô' => 'ç´³',
  '²Õ' => 'çµ„',
  '²Ö' => 'ç´¯',
  '²×' => 'çµ‚',
  '²Ø' => 'ç´²',
  '²Ù' => 'ç´±',
  '²Ú' => 'ç¼½',
  '²Û' => 'ç¾ž',
  '²Ü' => 'ç¾š',
  '²Ý' => 'ç¿Œ',
  '²Þ' => 'ç¿Ž',
  '²ß' => 'ç¿’',
  '²à' => 'è€œ',
  '²á' => 'è�Š',
  '²â' => 'è�†',
  '²ã' => 'è„¯',
  '²ä' => 'è„–',
  '²å' => 'è„£',
  '²æ' => 'è„«',
  '²ç' => 'è„©',
  '²è' => 'è„°',
  '²é' => 'è„¤',
  '²ê' => 'èˆ‚',
  '²ë' => 'èˆµ',
  '²ì' => 'èˆ·',
  '²í' => 'èˆ¶',
  '²î' => 'èˆ¹',
  '²ï' => 'èŽŽ',
  '²ð' => 'èŽž',
  '²ñ' => 'èŽ˜',
  '²ò' => 'è�¸',
  '²ó' => 'èŽ¢',
  '²ô' => 'èŽ–',
  '²õ' => 'èŽ½',
  '²ö' => 'èŽ«',
  '²÷' => 'èŽ’',
  '²ø' => 'èŽŠ',
  '²ù' => 'èŽ“',
  '²ú' => 'èŽ‰',
  '²û' => 'èŽ ',
  '²ü' => 'è�·',
  '²ý' => 'è�»',
  '²þ' => 'è�¼',
  '³@' => 'èŽ†',
  '³A' => 'èŽ§',
  '³B' => 'è™•',
  '³C' => 'å½ª',
  '³D' => 'è›‡',
  '³E' => 'è›€',
  '³F' => 'èš¶',
  '³G' => 'è›„',
  '³H' => 'èšµ',
  '³I' => 'è›†',
  '³J' => 'è›‹',
  '³K' => 'èš±',
  '³L' => 'èš¯',
  '³M' => 'è›‰',
  '³N' => 'è¡“',
  '³O' => 'è¢ž',
  '³P' => 'è¢ˆ',
  '³Q' => 'è¢«',
  '³R' => 'è¢’',
  '³S' => 'è¢–',
  '³T' => 'è¢�',
  '³U' => 'è¢‹',
  '³V' => 'è¦“',
  '³W' => 'è¦�',
  '³X' => 'è¨ª',
  '³Y' => 'è¨�',
  '³Z' => 'è¨£',
  '³[' => 'è¨¥',
  '³\\' => 'è¨±',
  '³]' => 'è¨­',
  '³^' => 'è¨Ÿ',
  '³_' => 'è¨›',
  '³`' => 'è¨¢',
  '³a' => 'è±‰',
  '³b' => 'è±š',
  '³c' => 'è²©',
  '³d' => 'è²¬',
  '³e' => 'è²«',
  '³f' => 'è²¨',
  '³g' => 'è²ª',
  '³h' => 'è²§',
  '³i' => 'èµ§',
  '³j' => 'èµ¦',
  '³k' => 'è¶¾',
  '³l' => 'è¶º',
  '³m' => 'è»›',
  '³n' => 'è»Ÿ',
  '³o' => 'é€™',
  '³p' => 'é€�',
  '³q' => 'é€š',
  '³r' => 'é€—',
  '³s' => 'é€£',
  '³t' => 'é€Ÿ',
  '³u' => 'é€�',
  '³v' => 'é€�',
  '³w' => 'é€•',
  '³x' => 'é€ž',
  '³y' => 'é€ ',
  '³z' => 'é€�',
  '³{' => 'é€¢',
  '³|' => 'é€–',
  '³}' => 'é€›',
  '³~' => 'é€”',
  '³¡' => 'éƒ¨',
  '³¢' => 'éƒ­',
  '³£' => 'éƒ½',
  '³¤' => 'é…—',
  '³¥' => 'é‡Ž',
  '³¦' => 'é‡µ',
  '³§' => 'é‡¦',
  '³¨' => 'é‡£',
  '³©' => 'é‡§',
  '³ª' => 'é‡­',
  '³«' => 'é‡©',
  '³¬' => 'é–‰',
  '³­' => 'é™ª',
  '³®' => 'é™µ',
  '³¯' => 'é™³',
  '³°' => 'é™¸',
  '³±' => 'é™°',
  '³²' => 'é™´',
  '³³' => 'é™¶',
  '³´' => 'é™·',
  '³µ' => 'é™¬',
  '³¶' => 'é›€',
  '³·' => 'é›ª',
  '³¸' => 'é›©',
  '³¹' => 'ç« ',
  '³º' => 'ç«Ÿ',
  '³»' => 'é ‚',
  '³¼' => 'é ƒ',
  '³½' => 'é­š',
  '³¾' => 'é³¥',
  '³¿' => 'é¹µ',
  '³À' => 'é¹¿',
  '³Á' => 'éº¥',
  '³Â' => 'éº»',
  '³Ã' => 'å‚¢',
  '³Ä' => 'å‚�',
  '³Å' => 'å‚…',
  '³Æ' => 'å‚™',
  '³Ç' => 'å‚‘',
  '³È' => 'å‚€',
  '³É' => 'å‚–',
  '³Ê' => 'å‚˜',
  '³Ë' => 'å‚š',
  '³Ì' => 'æœ€',
  '³Í' => 'å‡±',
  '³Î' => 'å‰²',
  '³Ï' => 'å‰´',
  '³Ð' => 'å‰µ',
  '³Ñ' => 'å‰©',
  '³Ò' => 'å‹ž',
  '³Ó' => 'å‹�',
  '³Ô' => 'å‹›',
  '³Õ' => 'å�š',
  '³Ö' => 'åŽ¥',
  '³×' => 'å•»',
  '³Ø' => 'å–€',
  '³Ù' => 'å–§',
  '³Ú' => 'å•¼',
  '³Û' => 'å–Š',
  '³Ü' => 'å–�',
  '³Ý' => 'å–˜',
  '³Þ' => 'å–‚',
  '³ß' => 'å–œ',
  '³à' => 'å–ª',
  '³á' => 'å–”',
  '³â' => 'å–‡',
  '³ã' => 'å–‹',
  '³ä' => 'å–ƒ',
  '³å' => 'å–³',
  '³æ' => 'å–®',
  '³ç' => 'å–Ÿ',
  '³è' => 'å”¾',
  '³é' => 'å–²',
  '³ê' => 'å–š',
  '³ë' => 'å–»',
  '³ì' => 'å–¬',
  '³í' => 'å–±',
  '³î' => 'å•¾',
  '³ï' => 'å–‰',
  '³ð' => 'å–«',
  '³ñ' => 'å–™',
  '³ò' => 'åœ�',
  '³ó' => 'å ¯',
  '³ô' => 'å ª',
  '³õ' => 'å ´',
  '³ö' => 'å ¤',
  '³÷' => 'å °',
  '³ø' => 'å ±',
  '³ù' => 'å ¡',
  '³ú' => 'å �',
  '³û' => 'å  ',
  '³ü' => 'å£¹',
  '³ý' => 'å£º',
  '³þ' => 'å¥ ',
  '´@' => 'å©·',
  '´A' => 'åªš',
  '´B' => 'å©¿',
  '´C' => 'åª’',
  '´D' => 'åª›',
  '´E' => 'åª§',
  '´F' => 'å­³',
  '´G' => 'å­±',
  '´H' => 'å¯’',
  '´I' => 'å¯Œ',
  '´J' => 'å¯“',
  '´K' => 'å¯�',
  '´L' => 'å°Š',
  '´M' => 'å°‹',
  '´N' => 'å°±',
  '´O' => 'åµŒ',
  '´P' => 'åµ�',
  '´Q' => 'å´´',
  '´R' => 'åµ‡',
  '´S' => 'å·½',
  '´T' => 'å¹…',
  '´U' => 'å¸½',
  '´V' => 'å¹€',
  '´W' => 'å¹ƒ',
  '´X' => 'å¹¾',
  '´Y' => 'å»Š',
  '´Z' => 'å»�',
  '´[' => 'å»‚',
  '´\\' => 'å»„',
  '´]' => 'å¼¼',
  '´^' => 'å½­',
  '´_' => 'å¾©',
  '´`' => 'å¾ª',
  '´a' => 'å¾¨',
  '´b' => 'æƒ‘',
  '´c' => 'æƒ¡',
  '´d' => 'æ‚²',
  '´e' => 'æ‚¶',
  '´f' => 'æƒ ',
  '´g' => 'æ„œ',
  '´h' => 'æ„£',
  '´i' => 'æƒº',
  '´j' => 'æ„•',
  '´k' => 'æƒ°',
  '´l' => 'æƒ»',
  '´m' => 'æƒ´',
  '´n' => 'æ…¨',
  '´o' => 'æƒ±',
  '´p' => 'æ„Ž',
  '´q' => 'æƒ¶',
  '´r' => 'æ„‰',
  '´s' => 'æ„€',
  '´t' => 'æ„’',
  '´u' => 'æˆŸ',
  '´v' => 'æ‰‰',
  '´w' => 'æŽ£',
  '´x' => 'æŽŒ',
  '´y' => 'æ��',
  '´z' => 'æ�€',
  '´{' => 'æ�©',
  '´|' => 'æ�‰',
  '´}' => 'æ�†',
  '´~' => 'æ��',
  '´¡' => 'æ�’',
  '´¢' => 'æ�£',
  '´£' => 'æ��',
  '´¤' => 'æ�¡',
  '´¥' => 'æ�–',
  '´¦' => 'æ�­',
  '´§' => 'æ�®',
  '´¨' => 'æ�¶',
  '´©' => 'æ�´',
  '´ª' => 'æ�ª',
  '´«' => 'æ�›',
  '´¬' => 'æ‘’',
  '´­' => 'æ�š',
  '´®' => 'æ�¹',
  '´¯' => 'æ•ž',
  '´°' => 'æ•¦',
  '´±' => 'æ•¢',
  '´²' => 'æ•£',
  '´³' => 'æ–‘',
  '´´' => 'æ–�',
  '´µ' => 'æ–¯',
  '´¶' => 'æ™®',
  '´·' => 'æ™°',
  '´¸' => 'æ™´',
  '´¹' => 'æ™¶',
  '´º' => 'æ™¯',
  '´»' => 'æš‘',
  '´¼' => 'æ™º',
  '´½' => 'æ™¾',
  '´¾' => 'æ™·',
  '´¿' => 'æ›¾',
  '´À' => 'æ›¿',
  '´Á' => 'æœŸ',
  '´Â' => 'æœ�',
  '´Ã' => 'æ£º',
  '´Ä' => 'æ£•',
  '´Å' => 'æ£ ',
  '´Æ' => 'æ£˜',
  '´Ç' => 'æ£—',
  '´È' => 'æ¤…',
  '´É' => 'æ£Ÿ',
  '´Ê' => 'æ£µ',
  '´Ë' => 'æ£®',
  '´Ì' => 'æ£§',
  '´Í' => 'æ£¹',
  '´Î' => 'æ£’',
  '´Ï' => 'æ£²',
  '´Ð' => 'æ££',
  '´Ñ' => 'æ£‹',
  '´Ò' => 'æ£�',
  '´Ó' => 'æ¤�',
  '´Ô' => 'æ¤’',
  '´Õ' => 'æ¤Ž',
  '´Ö' => 'æ£‰',
  '´×' => 'æ£š',
  '´Ø' => 'æ¥®',
  '´Ù' => 'æ£»',
  '´Ú' => 'æ¬¾',
  '´Û' => 'æ¬º',
  '´Ü' => 'æ¬½',
  '´Ý' => 'æ®˜',
  '´Þ' => 'æ®–',
  '´ß' => 'æ®¼',
  '´à' => 'æ¯¯',
  '´á' => 'æ°®',
  '´â' => 'æ°¯',
  '´ã' => 'æ°¬',
  '´ä' => 'æ¸¯',
  '´å' => 'æ¸¸',
  '´æ' => 'æ¹”',
  '´ç' => 'æ¸¡',
  '´è' => 'æ¸²',
  '´é' => 'æ¹§',
  '´ê' => 'æ¹Š',
  '´ë' => 'æ¸ ',
  '´ì' => 'æ¸¥',
  '´í' => 'æ¸£',
  '´î' => 'æ¸›',
  '´ï' => 'æ¹›',
  '´ð' => 'æ¹˜',
  '´ñ' => 'æ¸¤',
  '´ò' => 'æ¹–',
  '´ó' => 'æ¹®',
  '´ô' => 'æ¸­',
  '´õ' => 'æ¸¦',
  '´ö' => 'æ¹¯',
  '´÷' => 'æ¸´',
  '´ø' => 'æ¹�',
  '´ù' => 'æ¸º',
  '´ú' => 'æ¸¬',
  '´û' => 'æ¹ƒ',
  '´ü' => 'æ¸�',
  '´ý' => 'æ¸¾',
  '´þ' => 'æ»‹',
  'µ@' => 'æº‰',
  'µA' => 'æ¸™',
  'µB' => 'æ¹Ž',
  'µC' => 'æ¹£',
  'µD' => 'æ¹„',
  'µE' => 'æ¹²',
  'µF' => 'æ¹©',
  'µG' => 'æ¹Ÿ',
  'µH' => 'ç„™',
  'µI' => 'ç„š',
  'µJ' => 'ç„¦',
  'µK' => 'ç„°',
  'µL' => 'ç„¡',
  'µM' => 'ç„¶',
  'µN' => 'ç…®',
  'µO' => 'ç„œ',
  'µP' => 'ç‰Œ',
  'µQ' => 'çŠ„',
  'µR' => 'çŠ€',
  'µS' => 'çŒ¶',
  'µT' => 'çŒ¥',
  'µU' => 'çŒ´',
  'µV' => 'çŒ©',
  'µW' => 'ç�º',
  'µX' => 'ç�ª',
  'µY' => 'ç�³',
  'µZ' => 'ç�¢',
  'µ[' => 'ç�¥',
  'µ\\' => 'ç�µ',
  'µ]' => 'ç�¶',
  'µ^' => 'ç�´',
  'µ_' => 'ç�¯',
  'µ`' => 'ç�›',
  'µa' => 'ç�¦',
  'µb' => 'ç�¨',
  'µc' => 'ç”¥',
  'µd' => 'ç”¦',
  'µe' => 'ç•«',
  'µf' => 'ç•ª',
  'µg' => 'ç—¢',
  'µh' => 'ç—›',
  'µi' => 'ç—£',
  'µj' => 'ç—™',
  'µk' => 'ç—˜',
  'µl' => 'ç—ž',
  'µm' => 'ç— ',
  'µn' => 'ç™»',
  'µo' => 'ç™¼',
  'µp' => 'çš–',
  'µq' => 'çš“',
  'µr' => 'çš´',
  'µs' => 'ç›œ',
  'µt' => 'ç��',
  'µu' => 'çŸ­',
  'µv' => 'ç¡�',
  'µw' => 'ç¡¬',
  'µx' => 'ç¡¯',
  'µy' => 'ç¨�',
  'µz' => 'ç¨ˆ',
  'µ{' => 'ç¨‹',
  'µ|' => 'ç¨…',
  'µ}' => 'ç¨€',
  'µ~' => 'çª˜',
  'µ¡' => 'çª—',
  'µ¢' => 'çª–',
  'µ£' => 'ç«¥',
  'µ¤' => 'ç«£',
  'µ¥' => 'ç­‰',
  'µ¦' => 'ç­–',
  'µ§' => 'ç­†',
  'µ¨' => 'ç­�',
  'µ©' => 'ç­’',
  'µª' => 'ç­”',
  'µ«' => 'ç­�',
  'µ¬' => 'ç­‹',
  'µ­' => 'ç­�',
  'µ®' => 'ç­‘',
  'µ¯' => 'ç²Ÿ',
  'µ°' => 'ç²¥',
  'µ±' => 'çµž',
  'µ²' => 'çµ�',
  'µ³' => 'çµ¨',
  'µ´' => 'çµ•',
  'µµ' => 'ç´«',
  'µ¶' => 'çµ®',
  'µ·' => 'çµ²',
  'µ¸' => 'çµ¡',
  'µ¹' => 'çµ¦',
  'µº' => 'çµ¢',
  'µ»' => 'çµ°',
  'µ¼' => 'çµ³',
  'µ½' => 'å–„',
  'µ¾' => 'ç¿”',
  'µ¿' => 'ç¿•',
  'µÀ' => 'è€‹',
  'µÁ' => 'è�’',
  'µÂ' => 'è‚…',
  'µÃ' => 'è…•',
  'µÄ' => 'è…”',
  'µÅ' => 'è…‹',
  'µÆ' => 'è…‘',
  'µÇ' => 'è…Ž',
  'µÈ' => 'è„¹',
  'µÉ' => 'è…†',
  'µÊ' => 'è„¾',
  'µË' => 'è…Œ',
  'µÌ' => 'è…“',
  'µÍ' => 'è…´',
  'µÎ' => 'èˆ’',
  'µÏ' => 'èˆœ',
  'µÐ' => 'è�©',
  'µÑ' => 'è�ƒ',
  'µÒ' => 'è�¸',
  'µÓ' => 'è��',
  'µÔ' => 'è� ',
  'µÕ' => 'è�…',
  'µÖ' => 'è�‹',
  'µ×' => 'è��',
  'µØ' => 'è�¯',
  'µÙ' => 'è�±',
  'µÚ' => 'è�´',
  'µÛ' => 'è‘—',
  'µÜ' => 'è�Š',
  'µÝ' => 'è�°',
  'µÞ' => 'è�Œ',
  'µß' => 'è�Œ',
  'µà' => 'è�½',
  'µá' => 'è�²',
  'µâ' => 'è�Š',
  'µã' => 'è�¸',
  'µä' => 'è�Ž',
  'µå' => 'è�„',
  'µæ' => 'è�œ',
  'µç' => 'è�‡',
  'µè' => 'è�”',
  'µé' => 'è�Ÿ',
  'µê' => 'è™›',
  'µë' => 'è›Ÿ',
  'µì' => 'è›™',
  'µí' => 'è›­',
  'µî' => 'è›”',
  'µï' => 'è››',
  'µð' => 'è›¤',
  'µñ' => 'è›�',
  'µò' => 'è›ž',
  'µó' => 'è¡—',
  'µô' => 'è£�',
  'µõ' => 'è£‚',
  'µö' => 'è¢±',
  'µ÷' => 'è¦ƒ',
  'µø' => 'è¦–',
  'µù' => 'è¨»',
  'µú' => 'è© ',
  'µû' => 'è©•',
  'µü' => 'è©ž',
  'µý' => 'è¨¼',
  'µþ' => 'è©�',
  '¶@' => 'è©”',
  '¶A' => 'è©›',
  '¶B' => 'è©�',
  '¶C' => 'è©†',
  '¶D' => 'è¨´',
  '¶E' => 'è¨º',
  '¶F' => 'è¨¶',
  '¶G' => 'è©–',
  '¶H' => 'è±¡',
  '¶I' => 'è²‚',
  '¶J' => 'è²¯',
  '¶K' => 'è²¼',
  '¶L' => 'è²³',
  '¶M' => 'è²½',
  '¶N' => 'è³�',
  '¶O' => 'è²»',
  '¶P' => 'è³€',
  '¶Q' => 'è²´',
  '¶R' => 'è²·',
  '¶S' => 'è²¶',
  '¶T' => 'è²¿',
  '¶U' => 'è²¸',
  '¶V' => 'è¶Š',
  '¶W' => 'è¶…',
  '¶X' => 'è¶�',
  '¶Y' => 'è·Ž',
  '¶Z' => 'è·�',
  '¶[' => 'è·‹',
  '¶\\' => 'è·š',
  '¶]' => 'è·‘',
  '¶^' => 'è·Œ',
  '¶_' => 'è·›',
  '¶`' => 'è·†',
  '¶a' => 'è»»',
  '¶b' => 'è»¸',
  '¶c' => 'è»¼',
  '¶d' => 'è¾œ',
  '¶e' => 'é€®',
  '¶f' => 'é€µ',
  '¶g' => 'é€±',
  '¶h' => 'é€¸',
  '¶i' => 'é€²',
  '¶j' => 'é€¶',
  '¶k' => 'é„‚',
  '¶l' => 'éƒµ',
  '¶m' => 'é„‰',
  '¶n' => 'éƒ¾',
  '¶o' => 'é…£',
  '¶p' => 'é…¥',
  '¶q' => 'é‡�',
  '¶r' => 'éˆ”',
  '¶s' => 'éˆ•',
  '¶t' => 'éˆ£',
  '¶u' => 'éˆ‰',
  '¶v' => 'éˆž',
  '¶w' => 'éˆ�',
  '¶x' => 'éˆ�',
  '¶y' => 'éˆ‡',
  '¶z' => 'éˆ‘',
  '¶{' => 'é–”',
  '¶|' => 'é–�',
  '¶}' => 'é–‹',
  '¶~' => 'é–‘',
  '¶¡' => 'é–“',
  '¶¢' => 'é–’',
  '¶£' => 'é–Ž',
  '¶¤' => 'éšŠ',
  '¶¥' => 'éšŽ',
  '¶¦' => 'éš‹',
  '¶§' => 'é™½',
  '¶¨' => 'éš…',
  '¶©' => 'éš†',
  '¶ª' => 'éš�',
  '¶«' => 'é™²',
  '¶¬' => 'éš„',
  '¶­' => 'é›�',
  '¶®' => 'é›…',
  '¶¯' => 'é›„',
  '¶°' => 'é›†',
  '¶±' => 'é›‡',
  '¶²' => 'é›¯',
  '¶³' => 'é›²',
  '¶´' => 'éŸŒ',
  '¶µ' => 'é …',
  '¶¶' => 'é †',
  '¶·' => 'é ˆ',
  '¶¸' => 'é£§',
  '¶¹' => 'é£ª',
  '¶º' => 'é£¯',
  '¶»' => 'é£©',
  '¶¼' => 'é£²',
  '¶½' => 'é£­',
  '¶¾' => 'é¦®',
  '¶¿' => 'é¦­',
  '¶À' => 'é»ƒ',
  '¶Á' => 'é»�',
  '¶Â' => 'é»‘',
  '¶Ã' => 'äº‚',
  '¶Ä' => 'å‚­',
  '¶Å' => 'å‚µ',
  '¶Æ' => 'å‚²',
  '¶Ç' => 'å‚³',
  '¶È' => 'åƒ…',
  '¶É' => 'å‚¾',
  '¶Ê' => 'å‚¬',
  '¶Ë' => 'å‚·',
  '¶Ì' => 'å‚»',
  '¶Í' => 'å‚¯',
  '¶Î' => 'åƒ‡',
  '¶Ï' => 'å‰¿',
  '¶Ð' => 'å‰·',
  '¶Ñ' => 'å‰½',
  '¶Ò' => 'å‹Ÿ',
  '¶Ó' => 'å‹¦',
  '¶Ô' => 'å‹¤',
  '¶Õ' => 'å‹¢',
  '¶Ö' => 'å‹£',
  '¶×' => 'åŒ¯',
  '¶Ø' => 'å—Ÿ',
  '¶Ù' => 'å—¨',
  '¶Ú' => 'å—“',
  '¶Û' => 'å—¦',
  '¶Ü' => 'å—Ž',
  '¶Ý' => 'å—œ',
  '¶Þ' => 'å—‡',
  '¶ß' => 'å—‘',
  '¶à' => 'å—£',
  '¶á' => 'å—¤',
  '¶â' => 'å—¯',
  '¶ã' => 'å—š',
  '¶ä' => 'å—¡',
  '¶å' => 'å—…',
  '¶æ' => 'å—†',
  '¶ç' => 'å—¥',
  '¶è' => 'å—‰',
  '¶é' => 'åœ’',
  '¶ê' => 'åœ“',
  '¶ë' => 'å¡ž',
  '¶ì' => 'å¡‘',
  '¶í' => 'å¡˜',
  '¶î' => 'å¡—',
  '¶ï' => 'å¡š',
  '¶ð' => 'å¡”',
  '¶ñ' => 'å¡«',
  '¶ò' => 'å¡Œ',
  '¶ó' => 'å¡­',
  '¶ô' => 'å¡Š',
  '¶õ' => 'å¡¢',
  '¶ö' => 'å¡’',
  '¶÷' => 'å¡‹',
  '¶ø' => 'å¥§',
  '¶ù' => 'å«�',
  '¶ú' => 'å«‰',
  '¶û' => 'å«Œ',
  '¶ü' => 'åª¾',
  '¶ý' => 'åª½',
  '¶þ' => 'åª¼',
  '·@' => 'åª³',
  '·A' => 'å«‚',
  '·B' => 'åª²',
  '·C' => 'åµ©',
  '·D' => 'åµ¯',
  '·E' => 'å¹Œ',
  '·F' => 'å¹¹',
  '·G' => 'å»‰',
  '·H' => 'å»ˆ',
  '·I' => 'å¼’',
  '·J' => 'å½™',
  '·K' => 'å¾¬',
  '·L' => 'å¾®',
  '·M' => 'æ„š',
  '·N' => 'æ„�',
  '·O' => 'æ…ˆ',
  '·P' => 'æ„Ÿ',
  '·Q' => 'æƒ³',
  '·R' => 'æ„›',
  '·S' => 'æƒ¹',
  '·T' => 'æ„�',
  '·U' => 'æ„ˆ',
  '·V' => 'æ…Ž',
  '·W' => 'æ…Œ',
  '·X' => 'æ…„',
  '·Y' => 'æ…�',
  '·Z' => 'æ„¾',
  '·[' => 'æ„´',
  '·\\' => 'æ„§',
  '·]' => 'æ„�',
  '·^' => 'æ„†',
  '·_' => 'æ„·',
  '·`' => 'æˆ¡',
  '·a' => 'æˆ¢',
  '·b' => 'æ�“',
  '·c' => 'æ�¾',
  '·d' => 'æ�ž',
  '·e' => 'æ�ª',
  '·f' => 'æ�­',
  '·g' => 'æ�½',
  '·h' => 'æ�¬',
  '·i' => 'æ��',
  '·j' => 'æ�œ',
  '·k' => 'æ�”',
  '·l' => 'æ��',
  '·m' => 'æ�¶',
  '·n' => 'æ�–',
  '·o' => 'æ�—',
  '·p' => 'æ�†',
  '·q' => 'æ•¬',
  '·r' => 'æ–Ÿ',
  '·s' => 'æ–°',
  '·t' => 'æš—',
  '·u' => 'æš‰',
  '·v' => 'æš‡',
  '·w' => 'æšˆ',
  '·x' => 'æš–',
  '·y' => 'æš„',
  '·z' => 'æš˜',
  '·{' => 'æš�',
  '·|' => 'æœƒ',
  '·}' => 'æ¦”',
  '·~' => 'æ¥­',
  '·¡' => 'æ¥š',
  '·¢' => 'æ¥·',
  '·£' => 'æ¥ ',
  '·¤' => 'æ¥”',
  '·¥' => 'æ¥µ',
  '·¦' => 'æ¤°',
  '·§' => 'æ¦‚',
  '·¨' => 'æ¥Š',
  '·©' => 'æ¥¨',
  '·ª' => 'æ¥«',
  '·«' => 'æ¥ž',
  '·¬' => 'æ¥“',
  '·­' => 'æ¥¹',
  '·®' => 'æ¦†',
  '·¯' => 'æ¥�',
  '·°' => 'æ¥£',
  '·±' => 'æ¥›',
  '·²' => 'æ­‡',
  '·³' => 'æ­²',
  '·´' => 'æ¯€',
  '·µ' => 'æ®¿',
  '·¶' => 'æ¯“',
  '··' => 'æ¯½',
  '·¸' => 'æº¢',
  '·¹' => 'æº¯',
  '·º' => 'æ»“',
  '·»' => 'æº¶',
  '·¼' => 'æ»‚',
  '·½' => 'æº�',
  '·¾' => 'æº�',
  '·¿' => 'æ»‡',
  '·À' => 'æ»…',
  '·Á' => 'æº¥',
  '·Â' => 'æº˜',
  '·Ã' => 'æº¼',
  '·Ä' => 'æºº',
  '·Å' => 'æº«',
  '·Æ' => 'æ»‘',
  '·Ç' => 'æº–',
  '·È' => 'æºœ',
  '·É' => 'æ»„',
  '·Ê' => 'æ»”',
  '·Ë' => 'æºª',
  '·Ì' => 'æº§',
  '·Í' => 'æº´',
  '·Î' => 'ç…Ž',
  '·Ï' => 'ç…™',
  '·Ð' => 'ç…©',
  '·Ñ' => 'ç…¤',
  '·Ò' => 'ç…‰',
  '·Ó' => 'ç…§',
  '·Ô' => 'ç…œ',
  '·Õ' => 'ç…¬',
  '·Ö' => 'ç…¦',
  '·×' => 'ç…Œ',
  '·Ø' => 'ç…¥',
  '·Ù' => 'ç…ž',
  '·Ú' => 'ç…†',
  '·Û' => 'ç…¨',
  '·Ü' => 'ç…–',
  '·Ý' => 'çˆº',
  '·Þ' => 'ç‰’',
  '·ß' => 'çŒ·',
  '·à' => 'ç�…',
  '·á' => 'çŒ¿',
  '·â' => 'çŒ¾',
  '·ã' => 'ç‘¯',
  '·ä' => 'ç‘š',
  '·å' => 'ç‘•',
  '·æ' => 'ç‘Ÿ',
  '·ç' => 'ç‘ž',
  '·è' => 'ç‘�',
  '·é' => 'ç�¿',
  '·ê' => 'ç‘™',
  '·ë' => 'ç‘›',
  '·ì' => 'ç‘œ',
  '·í' => 'ç•¶',
  '·î' => 'ç•¸',
  '·ï' => 'ç˜€',
  '·ð' => 'ç—°',
  '·ñ' => 'ç˜�',
  '·ò' => 'ç—²',
  '·ó' => 'ç—±',
  '·ô' => 'ç—º',
  '·õ' => 'ç—¿',
  '·ö' => 'ç—´',
  '·÷' => 'ç—³',
  '·ø' => 'ç›ž',
  '·ù' => 'ç›Ÿ',
  '·ú' => 'ç�›',
  '·û' => 'ç�«',
  '·ü' => 'ç�¦',
  '·ý' => 'ç�ž',
  '·þ' => 'ç�£',
  '¸@' => 'ç�¹',
  '¸A' => 'ç�ª',
  '¸B' => 'ç�¬',
  '¸C' => 'ç�œ',
  '¸D' => 'ç�¥',
  '¸E' => 'ç�¨',
  '¸F' => 'ç�¢',
  '¸G' => 'çŸ®',
  '¸H' => 'ç¢Ž',
  '¸I' => 'ç¢°',
  '¸J' => 'ç¢—',
  '¸K' => 'ç¢˜',
  '¸L' => 'ç¢Œ',
  '¸M' => 'ç¢‰',
  '¸N' => 'ç¡¼',
  '¸O' => 'ç¢‘',
  '¸P' => 'ç¢“',
  '¸Q' => 'ç¡¿',
  '¸R' => 'ç¥º',
  '¸S' => 'ç¥¿',
  '¸T' => 'ç¦�',
  '¸U' => 'è�¬',
  '¸V' => 'ç¦½',
  '¸W' => 'ç¨œ',
  '¸X' => 'ç¨š',
  '¸Y' => 'ç¨ ',
  '¸Z' => 'ç¨”',
  '¸[' => 'ç¨Ÿ',
  '¸\\' => 'ç¨ž',
  '¸]' => 'çªŸ',
  '¸^' => 'çª ',
  '¸_' => 'ç­·',
  '¸`' => 'ç¯€',
  '¸a' => 'ç­ ',
  '¸b' => 'ç­®',
  '¸c' => 'ç­§',
  '¸d' => 'ç²±',
  '¸e' => 'ç²³',
  '¸f' => 'ç²µ',
  '¸g' => 'ç¶“',
  '¸h' => 'çµ¹',
  '¸i' => 'ç¶‘',
  '¸j' => 'ç¶�',
  '¸k' => 'ç¶�',
  '¸l' => 'çµ›',
  '¸m' => 'ç½®',
  '¸n' => 'ç½©',
  '¸o' => 'ç½ª',
  '¸p' => 'ç½²',
  '¸q' => 'ç¾©',
  '¸r' => 'ç¾¨',
  '¸s' => 'ç¾¤',
  '¸t' => 'è�–',
  '¸u' => 'è�˜',
  '¸v' => 'è‚†',
  '¸w' => 'è‚„',
  '¸x' => 'è…±',
  '¸y' => 'è…°',
  '¸z' => 'è…¸',
  '¸{' => 'è…¥',
  '¸|' => 'è…®',
  '¸}' => 'è…³',
  '¸~' => 'è…«',
  '¸¡' => 'è…¹',
  '¸¢' => 'è…º',
  '¸£' => 'è…¦',
  '¸¤' => 'èˆ…',
  '¸¥' => 'è‰‡',
  '¸¦' => 'è’‚',
  '¸§' => 'è‘·',
  '¸¨' => 'è�½',
  '¸©' => 'è�±',
  '¸ª' => 'è‘µ',
  '¸«' => 'è‘¦',
  '¸¬' => 'è‘«',
  '¸­' => 'è‘‰',
  '¸®' => 'è‘¬',
  '¸¯' => 'è‘›',
  '¸°' => 'è�¼',
  '¸±' => 'è�µ',
  '¸²' => 'è‘¡',
  '¸³' => 'è‘£',
  '¸´' => 'è‘©',
  '¸µ' => 'è‘­',
  '¸¶' => 'è‘†',
  '¸·' => 'è™ž',
  '¸¸' => 'è™œ',
  '¸¹' => 'è™Ÿ',
  '¸º' => 'è›¹',
  '¸»' => 'èœ“',
  '¸¼' => 'èœˆ',
  '¸½' => 'èœ‡',
  '¸¾' => 'èœ€',
  '¸¿' => 'è›¾',
  '¸À' => 'è›»',
  '¸Á' => 'èœ‚',
  '¸Â' => 'èœƒ',
  '¸Ã' => 'èœ†',
  '¸Ä' => 'èœŠ',
  '¸Å' => 'è¡™',
  '¸Æ' => 'è£Ÿ',
  '¸Ç' => 'è£”',
  '¸È' => 'è£™',
  '¸É' => 'è£œ',
  '¸Ê' => 'è£˜',
  '¸Ë' => 'è£�',
  '¸Ì' => 'è£¡',
  '¸Í' => 'è£Š',
  '¸Î' => 'è£•',
  '¸Ï' => 'è£’',
  '¸Ð' => 'è¦œ',
  '¸Ñ' => 'è§£',
  '¸Ò' => 'è©«',
  '¸Ó' => 'è©²',
  '¸Ô' => 'è©³',
  '¸Õ' => 'è©¦',
  '¸Ö' => 'è©©',
  '¸×' => 'è©°',
  '¸Ø' => 'èª‡',
  '¸Ù' => 'è©¼',
  '¸Ú' => 'è©£',
  '¸Û' => 'èª ',
  '¸Ü' => 'è©±',
  '¸Ý' => 'èª…',
  '¸Þ' => 'è©­',
  '¸ß' => 'è©¢',
  '¸à' => 'è©®',
  '¸á' => 'è©¬',
  '¸â' => 'è©¹',
  '¸ã' => 'è©»',
  '¸ä' => 'è¨¾',
  '¸å' => 'è©¨',
  '¸æ' => 'è±¢',
  '¸ç' => 'è²Š',
  '¸è' => 'è²‰',
  '¸é' => 'è³Š',
  '¸ê' => 'è³‡',
  '¸ë' => 'è³ˆ',
  '¸ì' => 'è³„',
  '¸í' => 'è²²',
  '¸î' => 'è³ƒ',
  '¸ï' => 'è³‚',
  '¸ð' => 'è³…',
  '¸ñ' => 'è·¡',
  '¸ò' => 'è·Ÿ',
  '¸ó' => 'è·¨',
  '¸ô' => 'è·¯',
  '¸õ' => 'è·³',
  '¸ö' => 'è·º',
  '¸÷' => 'è·ª',
  '¸ø' => 'è·¤',
  '¸ù' => 'è·¦',
  '¸ú' => 'èº²',
  '¸û' => 'è¼ƒ',
  '¸ü' => 'è¼‰',
  '¸ý' => 'è»¾',
  '¸þ' => 'è¼Š',
  '¹@' => 'è¾Ÿ',
  '¹A' => 'è¾²',
  '¹B' => 'é�‹',
  '¹C' => 'é�Š',
  '¹D' => 'é�“',
  '¹E' => 'é�‚',
  '¹F' => 'é�”',
  '¹G' => 'é€¼',
  '¹H' => 'é�•',
  '¹I' => 'é��',
  '¹J' => 'é�‡',
  '¹K' => 'é��',
  '¹L' => 'é�Ž',
  '¹M' => 'é��',
  '¹N' => 'é�‘',
  '¹O' => 'é€¾',
  '¹P' => 'é��',
  '¹Q' => 'é„’',
  '¹R' => 'é„—',
  '¹S' => 'é…¬',
  '¹T' => 'é…ª',
  '¹U' => 'é…©',
  '¹V' => 'é‡‰',
  '¹W' => 'éˆ·',
  '¹X' => 'é‰—',
  '¹Y' => 'éˆ¸',
  '¹Z' => 'éˆ½',
  '¹[' => 'é‰€',
  '¹\\' => 'éˆ¾',
  '¹]' => 'é‰›',
  '¹^' => 'é‰‹',
  '¹_' => 'é‰¤',
  '¹`' => 'é‰‘',
  '¹a' => 'éˆ´',
  '¹b' => 'é‰‰',
  '¹c' => 'é‰�',
  '¹d' => 'é‰…',
  '¹e' => 'éˆ¹',
  '¹f' => 'éˆ¿',
  '¹g' => 'é‰š',
  '¹h' => 'é–˜',
  '¹i' => 'éš˜',
  '¹j' => 'éš”',
  '¹k' => 'éš•',
  '¹l' => 'é›�',
  '¹m' => 'é›‹',
  '¹n' => 'é›‰',
  '¹o' => 'é›Š',
  '¹p' => 'é›·',
  '¹q' => 'é›»',
  '¹r' => 'é›¹',
  '¹s' => 'é›¶',
  '¹t' => 'é�–',
  '¹u' => 'é�´',
  '¹v' => 'é�¶',
  '¹w' => 'é �',
  '¹x' => 'é ‘',
  '¹y' => 'é “',
  '¹z' => 'é Š',
  '¹{' => 'é ’',
  '¹|' => 'é Œ',
  '¹}' => 'é£¼',
  '¹~' => 'é£´',
  '¹¡' => 'é£½',
  '¹¢' => 'é£¾',
  '¹£' => 'é¦³',
  '¹¤' => 'é¦±',
  '¹¥' => 'é¦´',
  '¹¦' => 'é«¡',
  '¹§' => 'é³©',
  '¹¨' => 'éº‚',
  '¹©' => 'é¼Ž',
  '¹ª' => 'é¼“',
  '¹«' => 'é¼ ',
  '¹¬' => 'åƒ§',
  '¹­' => 'åƒ®',
  '¹®' => 'åƒ¥',
  '¹¯' => 'åƒ–',
  '¹°' => 'åƒ­',
  '¹±' => 'åƒš',
  '¹²' => 'åƒ•',
  '¹³' => 'åƒ�',
  '¹´' => 'åƒ‘',
  '¹µ' => 'åƒ±',
  '¹¶' => 'åƒŽ',
  '¹·' => 'åƒ©',
  '¹¸' => 'å…¢',
  '¹¹' => 'å‡³',
  '¹º' => 'åŠƒ',
  '¹»' => 'åŠ‚',
  '¹¼' => 'åŒ±',
  '¹½' => 'åŽ­',
  '¹¾' => 'å—¾',
  '¹¿' => 'å˜€',
  '¹À' => 'å˜›',
  '¹Á' => 'å˜—',
  '¹Â' => 'å—½',
  '¹Ã' => 'å˜”',
  '¹Ä' => 'å˜†',
  '¹Å' => 'å˜‰',
  '¹Æ' => 'å˜�',
  '¹Ç' => 'å˜Ž',
  '¹È' => 'å—·',
  '¹É' => 'å˜–',
  '¹Ê' => 'å˜Ÿ',
  '¹Ë' => 'å˜ˆ',
  '¹Ì' => 'å˜�',
  '¹Í' => 'å—¶',
  '¹Î' => 'åœ˜',
  '¹Ï' => 'åœ–',
  '¹Ð' => 'å¡µ',
  '¹Ñ' => 'å¡¾',
  '¹Ò' => 'å¢ƒ',
  '¹Ó' => 'å¢“',
  '¹Ô' => 'å¢Š',
  '¹Õ' => 'å¡¹',
  '¹Ö' => 'å¢…',
  '¹×' => 'å¡½',
  '¹Ø' => 'å£½',
  '¹Ù' => 'å¤¥',
  '¹Ú' => 'å¤¢',
  '¹Û' => 'å¤¤',
  '¹Ü' => 'å¥ª',
  '¹Ý' => 'å¥©',
  '¹Þ' => 'å«¡',
  '¹ß' => 'å«¦',
  '¹à' => 'å«©',
  '¹á' => 'å«—',
  '¹â' => 'å«–',
  '¹ã' => 'å«˜',
  '¹ä' => 'å«£',
  '¹å' => 'å­µ',
  '¹æ' => 'å¯ž',
  '¹ç' => 'å¯§',
  '¹è' => 'å¯¡',
  '¹é' => 'å¯¥',
  '¹ê' => 'å¯¦',
  '¹ë' => 'å¯¨',
  '¹ì' => 'å¯¢',
  '¹í' => 'å¯¤',
  '¹î' => 'å¯Ÿ',
  '¹ï' => 'å°�',
  '¹ð' => 'å±¢',
  '¹ñ' => 'å¶„',
  '¹ò' => 'å¶‡',
  '¹ó' => 'å¹›',
  '¹ô' => 'å¹£',
  '¹õ' => 'å¹•',
  '¹ö' => 'å¹—',
  '¹÷' => 'å¹”',
  '¹ø' => 'å»“',
  '¹ù' => 'å»–',
  '¹ú' => 'å¼Š',
  '¹û' => 'å½†',
  '¹ü' => 'å½°',
  '¹ý' => 'å¾¹',
  '¹þ' => 'æ…‡',
  'º@' => 'æ„¿',
  'ºA' => 'æ…‹',
  'ºB' => 'æ…·',
  'ºC' => 'æ…¢',
  'ºD' => 'æ…£',
  'ºE' => 'æ…Ÿ',
  'ºF' => 'æ…š',
  'ºG' => 'æ…˜',
  'ºH' => 'æ…µ',
  'ºI' => 'æˆª',
  'ºJ' => 'æ’‡',
  'ºK' => 'æ‘˜',
  'ºL' => 'æ‘”',
  'ºM' => 'æ’¤',
  'ºN' => 'æ‘¸',
  'ºO' => 'æ‘Ÿ',
  'ºP' => 'æ‘º',
  'ºQ' => 'æ‘‘',
  'ºR' => 'æ‘§',
  'ºS' => 'æ�´',
  'ºT' => 'æ‘­',
  'ºU' => 'æ‘»',
  'ºV' => 'æ•²',
  'ºW' => 'æ–¡',
  'ºX' => 'æ——',
  'ºY' => 'æ—–',
  'ºZ' => 'æš¢',
  'º[' => 'æš¨',
  'º\\' => 'æš�',
  'º]' => 'æ¦œ',
  'º^' => 'æ¦¨',
  'º_' => 'æ¦•',
  'º`' => 'æ§�',
  'ºa' => 'æ¦®',
  'ºb' => 'æ§“',
  'ºc' => 'æ§‹',
  'ºd' => 'æ¦›',
  'ºe' => 'æ¦·',
  'ºf' => 'æ¦»',
  'ºg' => 'æ¦«',
  'ºh' => 'æ¦´',
  'ºi' => 'æ§�',
  'ºj' => 'æ§�',
  'ºk' => 'æ¦­',
  'ºl' => 'æ§Œ',
  'ºm' => 'æ¦¦',
  'ºn' => 'æ§ƒ',
  'ºo' => 'æ¦£',
  'ºp' => 'æ­‰',
  'ºq' => 'æ­Œ',
  'ºr' => 'æ°³',
  'ºs' => 'æ¼³',
  'ºt' => 'æ¼”',
  'ºu' => 'æ»¾',
  'ºv' => 'æ¼“',
  'ºw' => 'æ»´',
  'ºx' => 'æ¼©',
  'ºy' => 'æ¼¾',
  'ºz' => 'æ¼ ',
  'º{' => 'æ¼¬',
  'º|' => 'æ¼�',
  'º}' => 'æ¼‚',
  'º~' => 'æ¼¢',
  'º¡' => 'æ»¿',
  'º¢' => 'æ»¯',
  'º£' => 'æ¼†',
  'º¤' => 'æ¼±',
  'º¥' => 'æ¼¸',
  'º¦' => 'æ¼²',
  'º§' => 'æ¼£',
  'º¨' => 'æ¼•',
  'º©' => 'æ¼«',
  'ºª' => 'æ¼¯',
  'º«' => 'æ¾ˆ',
  'º¬' => 'æ¼ª',
  'º­' => 'æ»¬',
  'º®' => 'æ¼�',
  'º¯' => 'æ»²',
  'º°' => 'æ»Œ',
  'º±' => 'æ»·',
  'º²' => 'ç†”',
  'º³' => 'ç†™',
  'º´' => 'ç…½',
  'ºµ' => 'ç†Š',
  'º¶' => 'ç†„',
  'º·' => 'ç†’',
  'º¸' => 'çˆ¾',
  'º¹' => 'çŠ’',
  'ºº' => 'çŠ–',
  'º»' => 'ç�„',
  'º¼' => 'ç��',
  'º½' => 'ç‘¤',
  'º¾' => 'ç‘£',
  'º¿' => 'ç‘ª',
  'ºÀ' => 'ç‘°',
  'ºÁ' => 'ç‘­',
  'ºÂ' => 'ç”„',
  'ºÃ' => 'ç–‘',
  'ºÄ' => 'ç˜§',
  'ºÅ' => 'ç˜�',
  'ºÆ' => 'ç˜‹',
  'ºÇ' => 'ç˜‰',
  'ºÈ' => 'ç˜“',
  'ºÉ' => 'ç›¡',
  'ºÊ' => 'ç›£',
  'ºË' => 'çž„',
  'ºÌ' => 'ç�½',
  'ºÍ' => 'ç�¿',
  'ºÎ' => 'ç�¡',
  'ºÏ' => 'ç£�',
  'ºÐ' => 'ç¢Ÿ',
  'ºÑ' => 'ç¢§',
  'ºÒ' => 'ç¢³',
  'ºÓ' => 'ç¢©',
  'ºÔ' => 'ç¢£',
  'ºÕ' => 'ç¦Ž',
  'ºÖ' => 'ç¦�',
  'º×' => 'ç¦�',
  'ºØ' => 'ç¨®',
  'ºÙ' => 'ç¨±',
  'ºÚ' => 'çªª',
  'ºÛ' => 'çª©',
  'ºÜ' => 'ç«­',
  'ºÝ' => 'ç«¯',
  'ºÞ' => 'ç®¡',
  'ºß' => 'ç®•',
  'ºà' => 'ç®‹',
  'ºá' => 'ç­µ',
  'ºâ' => 'ç®—',
  'ºã' => 'ç®�',
  'ºä' => 'ç®”',
  'ºå' => 'ç®�',
  'ºæ' => 'ç®¸',
  'ºç' => 'ç®‡',
  'ºè' => 'ç®„',
  'ºé' => 'ç²¹',
  'ºê' => 'ç²½',
  'ºë' => 'ç²¾',
  'ºì' => 'ç¶»',
  'ºí' => 'ç¶°',
  'ºî' => 'ç¶œ',
  'ºï' => 'ç¶½',
  'ºð' => 'ç¶¾',
  'ºñ' => 'ç¶ ',
  'ºò' => 'ç·Š',
  'ºó' => 'ç¶´',
  'ºô' => 'ç¶²',
  'ºõ' => 'ç¶±',
  'ºö' => 'ç¶º',
  'º÷' => 'ç¶¢',
  'ºø' => 'ç¶¿',
  'ºù' => 'ç¶µ',
  'ºú' => 'ç¶¸',
  'ºû' => 'ç¶­',
  'ºü' => 'ç·’',
  'ºý' => 'ç·‡',
  'ºþ' => 'ç¶¬',
  '»@' => 'ç½°',
  '»A' => 'ç¿ ',
  '»B' => 'ç¿¡',
  '»C' => 'ç¿Ÿ',
  '»D' => 'è�ž',
  '»E' => 'è�š',
  '»F' => 'è‚‡',
  '»G' => 'è…�',
  '»H' => 'è†€',
  '»I' => 'è†�',
  '»J' => 'è†ˆ',
  '»K' => 'è†Š',
  '»L' => 'è…¿',
  '»M' => 'è†‚',
  '»N' => 'è‡§',
  '»O' => 'è‡º',
  '»P' => 'èˆ‡',
  '»Q' => 'èˆ”',
  '»R' => 'èˆž',
  '»S' => 'è‰‹',
  '»T' => 'è“‰',
  '»U' => 'è’¿',
  '»V' => 'è“†',
  '»W' => 'è“„',
  '»X' => 'è’™',
  '»Y' => 'è’ž',
  '»Z' => 'è’²',
  '»[' => 'è’œ',
  '»\\' => 'è“‹',
  '»]' => 'è’¸',
  '»^' => 'è“€',
  '»_' => 'è““',
  '»`' => 'è’�',
  '»a' => 'è’¼',
  '»b' => 'è“‘',
  '»c' => 'è“Š',
  '»d' => 'èœ¿',
  '»e' => 'èœœ',
  '»f' => 'èœ»',
  '»g' => 'èœ¢',
  '»h' => 'èœ¥',
  '»i' => 'èœ´',
  '»j' => 'èœ˜',
  '»k' => 'è�•',
  '»l' => 'èœ·',
  '»m' => 'èœ©',
  '»n' => 'è£³',
  '»o' => 'è¤‚',
  '»p' => 'è£´',
  '»q' => 'è£¹',
  '»r' => 'è£¸',
  '»s' => 'è£½',
  '»t' => 'è£¨',
  '»u' => 'è¤š',
  '»v' => 'è£¯',
  '»w' => 'èª¦',
  '»x' => 'èªŒ',
  '»y' => 'èªž',
  '»z' => 'èª£',
  '»{' => 'èª�',
  '»|' => 'èª¡',
  '»}' => 'èª“',
  '»~' => 'èª¤',
  '»¡' => 'èªª',
  '»¢' => 'èª¥',
  '»£' => 'èª¨',
  '»¤' => 'èª˜',
  '»¥' => 'èª‘',
  '»¦' => 'èªš',
  '»§' => 'èª§',
  '»¨' => 'è±ª',
  '»©' => 'è²�',
  '»ª' => 'è²Œ',
  '»«' => 'è³“',
  '»¬' => 'è³‘',
  '»­' => 'è³’',
  '»®' => 'èµ«',
  '»¯' => 'è¶™',
  '»°' => 'è¶•',
  '»±' => 'è·¼',
  '»²' => 'è¼”',
  '»³' => 'è¼’',
  '»´' => 'è¼•',
  '»µ' => 'è¼“',
  '»¶' => 'è¾£',
  '»·' => 'é� ',
  '»¸' => 'é�˜',
  '»¹' => 'é�œ',
  '»º' => 'é�£',
  '»»' => 'é�™',
  '»¼' => 'é�ž',
  '»½' => 'é�¢',
  '»¾' => 'é��',
  '»¿' => 'é�›',
  '»À' => 'é„™',
  '»Á' => 'é„˜',
  '»Â' => 'é„ž',
  '»Ã' => 'é…µ',
  '»Ä' => 'é…¸',
  '»Å' => 'é…·',
  '»Æ' => 'é…´',
  '»Ç' => 'é‰¸',
  '»È' => 'éŠ€',
  '»É' => 'éŠ…',
  '»Ê' => 'éŠ˜',
  '»Ë' => 'éŠ–',
  '»Ì' => 'é‰»',
  '»Í' => 'éŠ“',
  '»Î' => 'éŠœ',
  '»Ï' => 'éŠ¨',
  '»Ð' => 'é‰¼',
  '»Ñ' => 'éŠ‘',
  '»Ò' => 'é–¡',
  '»Ó' => 'é–¨',
  '»Ô' => 'é–©',
  '»Õ' => 'é–£',
  '»Ö' => 'é–¥',
  '»×' => 'é–¤',
  '»Ø' => 'éš™',
  '»Ù' => 'éšœ',
  '»Ú' => 'éš›',
  '»Û' => 'é›Œ',
  '»Ü' => 'é›’',
  '»Ý' => 'éœ€',
  '»Þ' => 'é�¼',
  '»ß' => 'éž…',
  '»à' => 'éŸ¶',
  '»á' => 'é —',
  '»â' => 'é ˜',
  '»ã' => 'é¢¯',
  '»ä' => 'é¢±',
  '»å' => 'é¤ƒ',
  '»æ' => 'é¤…',
  '»ç' => 'é¤Œ',
  '»è' => 'é¤‰',
  '»é' => 'é§�',
  '»ê' => 'éª¯',
  '»ë' => 'éª°',
  '»ì' => 'é«¦',
  '»í' => 'é­�',
  '»î' => 'é­‚',
  '»ï' => 'é³´',
  '»ð' => 'é³¶',
  '»ñ' => 'é³³',
  '»ò' => 'éº¼',
  '»ó' => 'é¼»',
  '»ô' => 'é½Š',
  '»õ' => 'å„„',
  '»ö' => 'å„€',
  '»÷' => 'åƒ»',
  '»ø' => 'åƒµ',
  '»ù' => 'åƒ¹',
  '»ú' => 'å„‚',
  '»û' => 'å„ˆ',
  '»ü' => 'å„‰',
  '»ý' => 'å„…',
  '»þ' => 'å‡œ',
  '¼@' => 'åŠ‡',
  '¼A' => 'åŠˆ',
  '¼B' => 'åŠ‰',
  '¼C' => 'åŠ�',
  '¼D' => 'åŠŠ',
  '¼E' => 'å‹°',
  '¼F' => 'åŽ²',
  '¼G' => 'å˜®',
  '¼H' => 'å˜»',
  '¼I' => 'å˜¹',
  '¼J' => 'å˜²',
  '¼K' => 'å˜¿',
  '¼L' => 'å˜´',
  '¼M' => 'å˜©',
  '¼N' => 'å™“',
  '¼O' => 'å™Ž',
  '¼P' => 'å™—',
  '¼Q' => 'å™´',
  '¼R' => 'å˜¶',
  '¼S' => 'å˜¯',
  '¼T' => 'å˜°',
  '¼U' => 'å¢€',
  '¼V' => 'å¢Ÿ',
  '¼W' => 'å¢ž',
  '¼X' => 'å¢³',
  '¼Y' => 'å¢œ',
  '¼Z' => 'å¢®',
  '¼[' => 'å¢©',
  '¼\\' => 'å¢¦',
  '¼]' => 'å¥­',
  '¼^' => 'å¬‰',
  '¼_' => 'å«»',
  '¼`' => 'å¬‹',
  '¼a' => 'å«µ',
  '¼b' => 'å¬Œ',
  '¼c' => 'å¬ˆ',
  '¼d' => 'å¯®',
  '¼e' => 'å¯¬',
  '¼f' => 'å¯©',
  '¼g' => 'å¯«',
  '¼h' => 'å±¤',
  '¼i' => 'å±¥',
  '¼j' => 'å¶�',
  '¼k' => 'å¶”',
  '¼l' => 'å¹¢',
  '¼m' => 'å¹Ÿ',
  '¼n' => 'å¹¡',
  '¼o' => 'å»¢',
  '¼p' => 'å»š',
  '¼q' => 'å»Ÿ',
  '¼r' => 'å»�',
  '¼s' => 'å»£',
  '¼t' => 'å» ',
  '¼u' => 'å½ˆ',
  '¼v' => 'å½±',
  '¼w' => 'å¾·',
  '¼x' => 'å¾µ',
  '¼y' => 'æ…¶',
  '¼z' => 'æ…§',
  '¼{' => 'æ…®',
  '¼|' => 'æ…�',
  '¼}' => 'æ…•',
  '¼~' => 'æ†‚',
  '¼¡' => 'æ…¼',
  '¼¢' => 'æ…°',
  '¼£' => 'æ…«',
  '¼¤' => 'æ…¾',
  '¼¥' => 'æ†§',
  '¼¦' => 'æ†�',
  '¼§' => 'æ†«',
  '¼¨' => 'æ†Ž',
  '¼©' => 'æ†¬',
  '¼ª' => 'æ†š',
  '¼«' => 'æ†¤',
  '¼¬' => 'æ†”',
  '¼­' => 'æ†®',
  '¼®' => 'æˆ®',
  '¼¯' => 'æ‘©',
  '¼°' => 'æ‘¯',
  '¼±' => 'æ‘¹',
  '¼²' => 'æ’ž',
  '¼³' => 'æ’²',
  '¼´' => 'æ’ˆ',
  '¼µ' => 'æ’�',
  '¼¶' => 'æ’°',
  '¼·' => 'æ’¥',
  '¼¸' => 'æ’“',
  '¼¹' => 'æ’•',
  '¼º' => 'æ’©',
  '¼»' => 'æ’’',
  '¼¼' => 'æ’®',
  '¼½' => 'æ’­',
  '¼¾' => 'æ’«',
  '¼¿' => 'æ’š',
  '¼À' => 'æ’¬',
  '¼Á' => 'æ’™',
  '¼Â' => 'æ’¢',
  '¼Ã' => 'æ’³',
  '¼Ä' => 'æ•µ',
  '¼Å' => 'æ•·',
  '¼Æ' => 'æ•¸',
  '¼Ç' => 'æš®',
  '¼È' => 'æš«',
  '¼É' => 'æš´',
  '¼Ê' => 'æš±',
  '¼Ë' => 'æ¨£',
  '¼Ì' => 'æ¨Ÿ',
  '¼Í' => 'æ§¨',
  '¼Î' => 'æ¨�',
  '¼Ï' => 'æ¨ž',
  '¼Ð' => 'æ¨™',
  '¼Ñ' => 'æ§½',
  '¼Ò' => 'æ¨¡',
  '¼Ó' => 'æ¨“',
  '¼Ô' => 'æ¨Š',
  '¼Õ' => 'æ§³',
  '¼Ö' => 'æ¨‚',
  '¼×' => 'æ¨…',
  '¼Ø' => 'æ§­',
  '¼Ù' => 'æ¨‘',
  '¼Ú' => 'æ­�',
  '¼Û' => 'æ­Ž',
  '¼Ü' => 'æ®¤',
  '¼Ý' => 'æ¯…',
  '¼Þ' => 'æ¯†',
  '¼ß' => 'æ¼¿',
  '¼à' => 'æ½¼',
  '¼á' => 'æ¾„',
  '¼â' => 'æ½‘',
  '¼ã' => 'æ½¦',
  '¼ä' => 'æ½”',
  '¼å' => 'æ¾†',
  '¼æ' => 'æ½­',
  '¼ç' => 'æ½›',
  '¼è' => 'æ½¸',
  '¼é' => 'æ½®',
  '¼ê' => 'æ¾Ž',
  '¼ë' => 'æ½º',
  '¼ì' => 'æ½°',
  '¼í' => 'æ½¤',
  '¼î' => 'æ¾—',
  '¼ï' => 'æ½˜',
  '¼ð' => 'æ»•',
  '¼ñ' => 'æ½¯',
  '¼ò' => 'æ½ ',
  '¼ó' => 'æ½Ÿ',
  '¼ô' => 'ç†Ÿ',
  '¼õ' => 'ç†¬',
  '¼ö' => 'ç†±',
  '¼÷' => 'ç†¨',
  '¼ø' => 'ç‰–',
  '¼ù' => 'çŠ›',
  '¼ú' => 'ç�Ž',
  '¼û' => 'ç�—',
  '¼ü' => 'ç‘©',
  '¼ý' => 'ç’‹',
  '¼þ' => 'ç’ƒ',
  '½@' => 'ç‘¾',
  '½A' => 'ç’€',
  '½B' => 'ç•¿',
  '½C' => 'ç˜ ',
  '½D' => 'ç˜©',
  '½E' => 'ç˜Ÿ',
  '½F' => 'ç˜¤',
  '½G' => 'ç˜¦',
  '½H' => 'ç˜¡',
  '½I' => 'ç˜¢',
  '½J' => 'çšš',
  '½K' => 'çšº',
  '½L' => 'ç›¤',
  '½M' => 'çžŽ',
  '½N' => 'çž‡',
  '½O' => 'çžŒ',
  '½P' => 'çž‘',
  '½Q' => 'çž‹',
  '½R' => 'ç£‹',
  '½S' => 'ç£…',
  '½T' => 'ç¢º',
  '½U' => 'ç£Š',
  '½V' => 'ç¢¾',
  '½W' => 'ç£•',
  '½X' => 'ç¢¼',
  '½Y' => 'ç£�',
  '½Z' => 'ç¨¿',
  '½[' => 'ç¨¼',
  '½\\' => 'ç©€',
  '½]' => 'ç¨½',
  '½^' => 'ç¨·',
  '½_' => 'ç¨»',
  '½`' => 'çª¯',
  '½a' => 'çª®',
  '½b' => 'ç®­',
  '½c' => 'ç®±',
  '½d' => 'ç¯„',
  '½e' => 'ç®´',
  '½f' => 'ç¯†',
  '½g' => 'ç¯‡',
  '½h' => 'ç¯�',
  '½i' => 'ç® ',
  '½j' => 'ç¯Œ',
  '½k' => 'ç³Š',
  '½l' => 'ç· ',
  '½m' => 'ç·´',
  '½n' => 'ç·¯',
  '½o' => 'ç·»',
  '½p' => 'ç·˜',
  '½q' => 'ç·¬',
  '½r' => 'ç·�',
  '½s' => 'ç·¨',
  '½t' => 'ç·£',
  '½u' => 'ç·š',
  '½v' => 'ç·ž',
  '½w' => 'ç·©',
  '½x' => 'ç¶ž',
  '½y' => 'ç·™',
  '½z' => 'ç·²',
  '½{' => 'ç·¹',
  '½|' => 'ç½µ',
  '½}' => 'ç½·',
  '½~' => 'ç¾¯',
  '½¡' => 'ç¿©',
  '½¢' => 'è€¦',
  '½£' => 'è†›',
  '½¤' => 'è†œ',
  '½¥' => 'è†�',
  '½¦' => 'è† ',
  '½§' => 'è†š',
  '½¨' => 'è†˜',
  '½©' => 'è”—',
  '½ª' => 'è”½',
  '½«' => 'è”š',
  '½¬' => 'è“®',
  '½­' => 'è”¬',
  '½®' => 'è”­',
  '½¯' => 'è”“',
  '½°' => 'è”‘',
  '½±' => 'è”£',
  '½²' => 'è”¡',
  '½³' => 'è””',
  '½´' => 'è“¬',
  '½µ' => 'è”¥',
  '½¶' => 'è“¿',
  '½·' => 'è”†',
  '½¸' => 'èž‚',
  '½¹' => 'è�´',
  '½º' => 'è�¶',
  '½»' => 'è� ',
  '½¼' => 'è�¦',
  '½½' => 'è�¸',
  '½¾' => 'è�¨',
  '½¿' => 'è�™',
  '½À' => 'è�—',
  '½Á' => 'è�Œ',
  '½Â' => 'è�“',
  '½Ã' => 'è¡›',
  '½Ä' => 'è¡�',
  '½Å' => 'è¤�',
  '½Æ' => 'è¤‡',
  '½Ç' => 'è¤’',
  '½È' => 'è¤“',
  '½É' => 'è¤•',
  '½Ê' => 'è¤Š',
  '½Ë' => 'èª¼',
  '½Ì' => 'è«’',
  '½Í' => 'è«‡',
  '½Î' => 'è«„',
  '½Ï' => 'èª•',
  '½Ð' => 'è«‹',
  '½Ñ' => 'è«¸',
  '½Ò' => 'èª²',
  '½Ó' => 'è«‰',
  '½Ô' => 'è«‚',
  '½Õ' => 'èª¿',
  '½Ö' => 'èª°',
  '½×' => 'è«–',
  '½Ø' => 'è«�',
  '½Ù' => 'èª¶',
  '½Ú' => 'èª¹',
  '½Û' => 'è«›',
  '½Ü' => 'è±Œ',
  '½Ý' => 'è±Ž',
  '½Þ' => 'è±¬',
  '½ß' => 'è³ ',
  '½à' => 'è³ž',
  '½á' => 'è³¦',
  '½â' => 'è³¤',
  '½ã' => 'è³¬',
  '½ä' => 'è³­',
  '½å' => 'è³¢',
  '½æ' => 'è³£',
  '½ç' => 'è³œ',
  '½è' => 'è³ª',
  '½é' => 'è³¡',
  '½ê' => 'èµ­',
  '½ë' => 'è¶Ÿ',
  '½ì' => 'è¶£',
  '½í' => 'è¸«',
  '½î' => 'è¸�',
  '½ï' => 'è¸�',
  '½ð' => 'è¸¢',
  '½ñ' => 'è¸�',
  '½ò' => 'è¸©',
  '½ó' => 'è¸Ÿ',
  '½ô' => 'è¸¡',
  '½õ' => 'è¸ž',
  '½ö' => 'èºº',
  '½÷' => 'è¼�',
  '½ø' => 'è¼›',
  '½ù' => 'è¼Ÿ',
  '½ú' => 'è¼©',
  '½û' => 'è¼¦',
  '½ü' => 'è¼ª',
  '½ý' => 'è¼œ',
  '½þ' => 'è¼ž',
  '¾@' => 'è¼¥',
  '¾A' => 'é�©',
  '¾B' => 'é�®',
  '¾C' => 'é�¨',
  '¾D' => 'é�­',
  '¾E' => 'é�·',
  '¾F' => 'é„°',
  '¾G' => 'é„­',
  '¾H' => 'é„§',
  '¾I' => 'é„±',
  '¾J' => 'é†‡',
  '¾K' => 'é†‰',
  '¾L' => 'é†‹',
  '¾M' => 'é†ƒ',
  '¾N' => 'é‹…',
  '¾O' => 'éŠ»',
  '¾P' => 'éŠ·',
  '¾Q' => 'é‹ª',
  '¾R' => 'éŠ¬',
  '¾S' => 'é‹¤',
  '¾T' => 'é‹�',
  '¾U' => 'éŠ³',
  '¾V' => 'éŠ¼',
  '¾W' => 'é‹’',
  '¾X' => 'é‹‡',
  '¾Y' => 'é‹°',
  '¾Z' => 'éŠ²',
  '¾[' => 'é–­',
  '¾\\' => 'é–±',
  '¾]' => 'éœ„',
  '¾^' => 'éœ†',
  '¾_' => 'éœ‡',
  '¾`' => 'éœ‰',
  '¾a' => 'é� ',
  '¾b' => 'éž�',
  '¾c' => 'éž‹',
  '¾d' => 'éž�',
  '¾e' => 'é ¡',
  '¾f' => 'é «',
  '¾g' => 'é œ',
  '¾h' => 'é¢³',
  '¾i' => 'é¤Š',
  '¾j' => 'é¤“',
  '¾k' => 'é¤’',
  '¾l' => 'é¤˜',
  '¾m' => 'é§�',
  '¾n' => 'é§�',
  '¾o' => 'é§Ÿ',
  '¾p' => 'é§›',
  '¾q' => 'é§‘',
  '¾r' => 'é§•',
  '¾s' => 'é§’',
  '¾t' => 'é§™',
  '¾u' => 'éª·',
  '¾v' => 'é«®',
  '¾w' => 'é«¯',
  '¾x' => 'é¬§',
  '¾y' => 'é­…',
  '¾z' => 'é­„',
  '¾{' => 'é­·',
  '¾|' => 'é­¯',
  '¾}' => 'é´†',
  '¾~' => 'é´‰',
  '¾¡' => 'é´ƒ',
  '¾¢' => 'éº©',
  '¾£' => 'éº¾',
  '¾¤' => 'é»Ž',
  '¾¥' => 'å¢¨',
  '¾¦' => 'é½’',
  '¾§' => 'å„’',
  '¾¨' => 'å„˜',
  '¾©' => 'å„”',
  '¾ª' => 'å„�',
  '¾«' => 'å„•',
  '¾¬' => 'å†€',
  '¾­' => 'å†ª',
  '¾®' => 'å‡�',
  '¾¯' => 'åŠ‘',
  '¾°' => 'åŠ“',
  '¾±' => 'å‹³',
  '¾²' => 'å™™',
  '¾³' => 'å™«',
  '¾´' => 'å™¹',
  '¾µ' => 'å™©',
  '¾¶' => 'å™¤',
  '¾·' => 'å™¸',
  '¾¸' => 'å™ª',
  '¾¹' => 'å™¨',
  '¾º' => 'å™¥',
  '¾»' => 'å™±',
  '¾¼' => 'å™¯',
  '¾½' => 'å™¬',
  '¾¾' => 'å™¢',
  '¾¿' => 'å™¶',
  '¾À' => 'å£�',
  '¾Á' => 'å¢¾',
  '¾Â' => 'å£‡',
  '¾Ã' => 'å£…',
  '¾Ä' => 'å¥®',
  '¾Å' => 'å¬�',
  '¾Æ' => 'å¬´',
  '¾Ç' => 'å­¸',
  '¾È' => 'å¯°',
  '¾É' => 'å°Ž',
  '¾Ê' => 'å½Š',
  '¾Ë' => 'æ†²',
  '¾Ì' => 'æ†‘',
  '¾Í' => 'æ†©',
  '¾Î' => 'æ†Š',
  '¾Ï' => 'æ‡�',
  '¾Ð' => 'æ†¶',
  '¾Ñ' => 'æ†¾',
  '¾Ò' => 'æ‡Š',
  '¾Ó' => 'æ‡ˆ',
  '¾Ô' => 'æˆ°',
  '¾Õ' => 'æ“…',
  '¾Ö' => 'æ“�',
  '¾×' => 'æ“‹',
  '¾Ø' => 'æ’»',
  '¾Ù' => 'æ’¼',
  '¾Ú' => 'æ“š',
  '¾Û' => 'æ“„',
  '¾Ü' => 'æ“‡',
  '¾Ý' => 'æ“‚',
  '¾Þ' => 'æ“�',
  '¾ß' => 'æ’¿',
  '¾à' => 'æ“’',
  '¾á' => 'æ“”',
  '¾â' => 'æ’¾',
  '¾ã' => 'æ•´',
  '¾ä' => 'æ›†',
  '¾å' => 'æ›‰',
  '¾æ' => 'æš¹',
  '¾ç' => 'æ›„',
  '¾è' => 'æ›‡',
  '¾é' => 'æš¸',
  '¾ê' => 'æ¨½',
  '¾ë' => 'æ¨¸',
  '¾ì' => 'æ¨º',
  '¾í' => 'æ©™',
  '¾î' => 'æ©«',
  '¾ï' => 'æ©˜',
  '¾ð' => 'æ¨¹',
  '¾ñ' => 'æ©„',
  '¾ò' => 'æ©¢',
  '¾ó' => 'æ©¡',
  '¾ô' => 'æ©‹',
  '¾õ' => 'æ©‡',
  '¾ö' => 'æ¨µ',
  '¾÷' => 'æ©Ÿ',
  '¾ø' => 'æ©ˆ',
  '¾ù' => 'æ­™',
  '¾ú' => 'æ­·',
  '¾û' => 'æ°…',
  '¾ü' => 'æ¿‚',
  '¾ý' => 'æ¾±',
  '¾þ' => 'æ¾¡',
  '¿@' => 'æ¿ƒ',
  '¿A' => 'æ¾¤',
  '¿B' => 'æ¿�',
  '¿C' => 'æ¾§',
  '¿D' => 'æ¾³',
  '¿E' => 'æ¿€',
  '¿F' => 'æ¾¹',
  '¿G' => 'æ¾¶',
  '¿H' => 'æ¾¦',
  '¿I' => 'æ¾ ',
  '¿J' => 'æ¾´',
  '¿K' => 'ç†¾',
  '¿L' => 'ç‡‰',
  '¿M' => 'ç‡�',
  '¿N' => 'ç‡’',
  '¿O' => 'ç‡ˆ',
  '¿P' => 'ç‡•',
  '¿Q' => 'ç†¹',
  '¿R' => 'ç‡Ž',
  '¿S' => 'ç‡™',
  '¿T' => 'ç‡œ',
  '¿U' => 'ç‡ƒ',
  '¿V' => 'ç‡„',
  '¿W' => 'ç�¨',
  '¿X' => 'ç’œ',
  '¿Y' => 'ç’£',
  '¿Z' => 'ç’˜',
  '¿[' => 'ç’Ÿ',
  '¿\\' => 'ç’ž',
  '¿]' => 'ç“¢',
  '¿^' => 'ç”Œ',
  '¿_' => 'ç”�',
  '¿`' => 'ç˜´',
  '¿a' => 'ç˜¸',
  '¿b' => 'ç˜º',
  '¿c' => 'ç›§',
  '¿d' => 'ç›¥',
  '¿e' => 'çž ',
  '¿f' => 'çžž',
  '¿g' => 'çžŸ',
  '¿h' => 'çž¥',
  '¿i' => 'ç£¨',
  '¿j' => 'ç£š',
  '¿k' => 'ç£¬',
  '¿l' => 'ç£§',
  '¿m' => 'ç¦¦',
  '¿n' => 'ç©�',
  '¿o' => 'ç©Ž',
  '¿p' => 'ç©†',
  '¿q' => 'ç©Œ',
  '¿r' => 'ç©‹',
  '¿s' => 'çªº',
  '¿t' => 'ç¯™',
  '¿u' => 'ç°‘',
  '¿v' => 'ç¯‰',
  '¿w' => 'ç¯¤',
  '¿x' => 'ç¯›',
  '¿y' => 'ç¯¡',
  '¿z' => 'ç¯©',
  '¿{' => 'ç¯¦',
  '¿|' => 'ç³•',
  '¿}' => 'ç³–',
  '¿~' => 'ç¸Š',
  '¿¡' => 'ç¸‘',
  '¿¢' => 'ç¸ˆ',
  '¿£' => 'ç¸›',
  '¿¤' => 'ç¸£',
  '¿¥' => 'ç¸ž',
  '¿¦' => 'ç¸�',
  '¿§' => 'ç¸‰',
  '¿¨' => 'ç¸�',
  '¿©' => 'ç½¹',
  '¿ª' => 'ç¾²',
  '¿«' => 'ç¿°',
  '¿¬' => 'ç¿±',
  '¿­' => 'ç¿®',
  '¿®' => 'è€¨',
  '¿¯' => 'è†³',
  '¿°' => 'è†©',
  '¿±' => 'è†¨',
  '¿²' => 'è‡»',
  '¿³' => 'èˆˆ',
  '¿´' => 'è‰˜',
  '¿µ' => 'è‰™',
  '¿¶' => 'è•Š',
  '¿·' => 'è•™',
  '¿¸' => 'è•ˆ',
  '¿¹' => 'è•¨',
  '¿º' => 'è•©',
  '¿»' => 'è•ƒ',
  '¿¼' => 'è•‰',
  '¿½' => 'è•­',
  '¿¾' => 'è•ª',
  '¿¿' => 'è•ž',
  '¿À' => 'èžƒ',
  '¿Á' => 'èžŸ',
  '¿Â' => 'èžž',
  '¿Ã' => 'èž¢',
  '¿Ä' => 'èž�',
  '¿Å' => 'è¡¡',
  '¿Æ' => 'è¤ª',
  '¿Ç' => 'è¤²',
  '¿È' => 'è¤¥',
  '¿É' => 'è¤«',
  '¿Ê' => 'è¤¡',
  '¿Ë' => 'è¦ª',
  '¿Ì' => 'è¦¦',
  '¿Í' => 'è«¦',
  '¿Î' => 'è«º',
  '¿Ï' => 'è««',
  '¿Ð' => 'è«±',
  '¿Ñ' => 'è¬€',
  '¿Ò' => 'è«œ',
  '¿Ó' => 'è«§',
  '¿Ô' => 'è«®',
  '¿Õ' => 'è«¾',
  '¿Ö' => 'è¬�',
  '¿×' => 'è¬‚',
  '¿Ø' => 'è«·',
  '¿Ù' => 'è«­',
  '¿Ú' => 'è«³',
  '¿Û' => 'è«¶',
  '¿Ü' => 'è«¼',
  '¿Ý' => 'è±«',
  '¿Þ' => 'è±­',
  '¿ß' => 'è²“',
  '¿à' => 'è³´',
  '¿á' => 'è¹„',
  '¿â' => 'è¸±',
  '¿ã' => 'è¸´',
  '¿ä' => 'è¹‚',
  '¿å' => 'è¸¹',
  '¿æ' => 'è¸µ',
  '¿ç' => 'è¼»',
  '¿è' => 'è¼¯',
  '¿é' => 'è¼¸',
  '¿ê' => 'è¼³',
  '¿ë' => 'è¾¨',
  '¿ì' => 'è¾¦',
  '¿í' => 'é�µ',
  '¿î' => 'é�´',
  '¿ï' => 'é�¸',
  '¿ð' => 'é�²',
  '¿ñ' => 'é�¼',
  '¿ò' => 'é�º',
  '¿ó' => 'é„´',
  '¿ô' => 'é†’',
  '¿õ' => 'éŒ ',
  '¿ö' => 'éŒ¶',
  '¿÷' => 'é‹¸',
  '¿ø' => 'éŒ³',
  '¿ù' => 'éŒ¯',
  '¿ú' => 'éŒ¢',
  '¿û' => 'é‹¼',
  '¿ü' => 'éŒ«',
  '¿ý' => 'éŒ„',
  '¿þ' => 'éŒš',
  'À@' => 'éŒ�',
  'ÀA' => 'éŒ¦',
  'ÀB' => 'éŒ¡',
  'ÀC' => 'éŒ•',
  'ÀD' => 'éŒ®',
  'ÀE' => 'éŒ™',
  'ÀF' => 'é–»',
  'ÀG' => 'éš§',
  'ÀH' => 'éš¨',
  'ÀI' => 'éšª',
  'ÀJ' => 'é›•',
  'ÀK' => 'éœŽ',
  'ÀL' => 'éœ‘',
  'ÀM' => 'éœ–',
  'ÀN' => 'éœ�',
  'ÀO' => 'éœ“',
  'ÀP' => 'éœ�',
  'ÀQ' => 'é�›',
  'ÀR' => 'é�œ',
  'ÀS' => 'é�¦',
  'ÀT' => 'éž˜',
  'ÀU' => 'é °',
  'ÀV' => 'é ¸',
  'ÀW' => 'é »',
  'ÀX' => 'é ·',
  'ÀY' => 'é ­',
  'ÀZ' => 'é ¹',
  'À[' => 'é ¤',
  'À\\' => 'é¤�',
  'À]' => 'é¤¨',
  'À^' => 'é¤ž',
  'À_' => 'é¤›',
  'À`' => 'é¤¡',
  'Àa' => 'é¤š',
  'Àb' => 'é§­',
  'Àc' => 'é§¢',
  'Àd' => 'é§±',
  'Àe' => 'éª¸',
  'Àf' => 'éª¼',
  'Àg' => 'é«»',
  'Àh' => 'é«­',
  'Ài' => 'é¬¨',
  'Àj' => 'é®‘',
  'Àk' => 'é´•',
  'Àl' => 'é´£',
  'Àm' => 'é´¦',
  'Àn' => 'é´¨',
  'Ào' => 'é´’',
  'Àp' => 'é´›',
  'Àq' => 'é»˜',
  'Àr' => 'é»”',
  'Às' => 'é¾�',
  'Àt' => 'é¾œ',
  'Àu' => 'å„ª',
  'Àv' => 'å„Ÿ',
  'Àw' => 'å„¡',
  'Àx' => 'å„²',
  'Ày' => 'å‹µ',
  'Àz' => 'åšŽ',
  'À{' => 'åš€',
  'À|' => 'åš�',
  'À}' => 'åš…',
  'À~' => 'åš‡',
  'À¡' => 'åš�',
  'À¢' => 'å£•',
  'À£' => 'å£“',
  'À¤' => 'å£‘',
  'À¥' => 'å£Ž',
  'À¦' => 'å¬°',
  'À§' => 'å¬ª',
  'À¨' => 'å¬¤',
  'À©' => 'å­º',
  'Àª' => 'å°·',
  'À«' => 'å±¨',
  'À¬' => 'å¶¼',
  'À­' => 'å¶º',
  'À®' => 'å¶½',
  'À¯' => 'å¶¸',
  'À°' => 'å¹«',
  'À±' => 'å½Œ',
  'À²' => 'å¾½',
  'À³' => 'æ‡‰',
  'À´' => 'æ‡‚',
  'Àµ' => 'æ‡‡',
  'À¶' => 'æ‡¦',
  'À·' => 'æ‡‹',
  'À¸' => 'æˆ²',
  'À¹' => 'æˆ´',
  'Àº' => 'æ“Ž',
  'À»' => 'æ“Š',
  'À¼' => 'æ“˜',
  'À½' => 'æ“ ',
  'À¾' => 'æ“°',
  'À¿' => 'æ“¦',
  'ÀÀ' => 'æ“¬',
  'ÀÁ' => 'æ“±',
  'ÀÂ' => 'æ“¢',
  'ÀÃ' => 'æ“­',
  'ÀÄ' => 'æ–‚',
  'ÀÅ' => 'æ–ƒ',
  'ÀÆ' => 'æ›™',
  'ÀÇ' => 'æ›–',
  'ÀÈ' => 'æª€',
  'ÀÉ' => 'æª”',
  'ÀÊ' => 'æª„',
  'ÀË' => 'æª¢',
  'ÀÌ' => 'æªœ',
  'ÀÍ' => 'æ«›',
  'ÀÎ' => 'æª£',
  'ÀÏ' => 'æ©¾',
  'ÀÐ' => 'æª—',
  'ÀÑ' => 'æª�',
  'ÀÒ' => 'æª ',
  'ÀÓ' => 'æ­œ',
  'ÀÔ' => 'æ®®',
  'ÀÕ' => 'æ¯š',
  'ÀÖ' => 'æ°ˆ',
  'À×' => 'æ¿˜',
  'ÀØ' => 'æ¿±',
  'ÀÙ' => 'æ¿Ÿ',
  'ÀÚ' => 'æ¿ ',
  'ÀÛ' => 'æ¿›',
  'ÀÜ' => 'æ¿¤',
  'ÀÝ' => 'æ¿«',
  'ÀÞ' => 'æ¿¯',
  'Àß' => 'æ¾€',
  'Àà' => 'æ¿¬',
  'Àá' => 'æ¿¡',
  'Àâ' => 'æ¿©',
  'Àã' => 'æ¿•',
  'Àä' => 'æ¿®',
  'Àå' => 'æ¿°',
  'Àæ' => 'ç‡§',
  'Àç' => 'ç‡Ÿ',
  'Àè' => 'ç‡®',
  'Àé' => 'ç‡¦',
  'Àê' => 'ç‡¥',
  'Àë' => 'ç‡­',
  'Àì' => 'ç‡¬',
  'Àí' => 'ç‡´',
  'Àî' => 'ç‡ ',
  'Àï' => 'çˆµ',
  'Àð' => 'ç‰†',
  'Àñ' => 'ç�°',
  'Àò' => 'ç�²',
  'Àó' => 'ç’©',
  'Àô' => 'ç’°',
  'Àõ' => 'ç’¦',
  'Àö' => 'ç’¨',
  'À÷' => 'ç™†',
  'Àø' => 'ç™‚',
  'Àù' => 'ç™Œ',
  'Àú' => 'ç›ª',
  'Àû' => 'çž³',
  'Àü' => 'çžª',
  'Àý' => 'çž°',
  'Àþ' => 'çž¬',
  'Á@' => 'çž§',
  'ÁA' => 'çž­',
  'ÁB' => 'çŸ¯',
  'ÁC' => 'ç£·',
  'ÁD' => 'ç£º',
  'ÁE' => 'ç£´',
  'ÁF' => 'ç£¯',
  'ÁG' => 'ç¤�',
  'ÁH' => 'ç¦§',
  'ÁI' => 'ç¦ª',
  'ÁJ' => 'ç©—',
  'ÁK' => 'çª¿',
  'ÁL' => 'ç°‡',
  'ÁM' => 'ç°�',
  'ÁN' => 'ç¯¾',
  'ÁO' => 'ç¯·',
  'ÁP' => 'ç°Œ',
  'ÁQ' => 'ç¯ ',
  'ÁR' => 'ç³ ',
  'ÁS' => 'ç³œ',
  'ÁT' => 'ç³ž',
  'ÁU' => 'ç³¢',
  'ÁV' => 'ç³Ÿ',
  'ÁW' => 'ç³™',
  'ÁX' => 'ç³�',
  'ÁY' => 'ç¸®',
  'ÁZ' => 'ç¸¾',
  'Á[' => 'ç¹†',
  'Á\\' => 'ç¸·',
  'Á]' => 'ç¸²',
  'Á^' => 'ç¹ƒ',
  'Á_' => 'ç¸«',
  'Á`' => 'ç¸½',
  'Áa' => 'ç¸±',
  'Áb' => 'ç¹…',
  'Ác' => 'ç¹�',
  'Ád' => 'ç¸´',
  'Áe' => 'ç¸¹',
  'Áf' => 'ç¹ˆ',
  'Ág' => 'ç¸µ',
  'Áh' => 'ç¸¿',
  'Ái' => 'ç¸¯',
  'Áj' => 'ç½„',
  'Ák' => 'ç¿³',
  'Ál' => 'ç¿¼',
  'Ám' => 'è�±',
  'Án' => 'è�²',
  'Áo' => 'è�°',
  'Áp' => 'è�¯',
  'Áq' => 'è�³',
  'Ár' => 'è‡†',
  'Ás' => 'è‡ƒ',
  'Át' => 'è†º',
  'Áu' => 'è‡‚',
  'Áv' => 'è‡€',
  'Áw' => 'è†¿',
  'Áx' => 'è†½',
  'Áy' => 'è‡‰',
  'Áz' => 'è†¾',
  'Á{' => 'è‡¨',
  'Á|' => 'èˆ‰',
  'Á}' => 'è‰±',
  'Á~' => 'è–ª',
  'Á¡' => 'è–„',
  'Á¢' => 'è•¾',
  'Á£' => 'è–œ',
  'Á¤' => 'è–‘',
  'Á¥' => 'è–”',
  'Á¦' => 'è–¯',
  'Á§' => 'è–›',
  'Á¨' => 'è–‡',
  'Á©' => 'è–¨',
  'Áª' => 'è–Š',
  'Á«' => 'è™§',
  'Á¬' => 'èŸ€',
  'Á­' => 'èŸ‘',
  'Á®' => 'èž³',
  'Á¯' => 'èŸ’',
  'Á°' => 'èŸ†',
  'Á±' => 'èž«',
  'Á²' => 'èž»',
  'Á³' => 'èžº',
  'Á´' => 'èŸˆ',
  'Áµ' => 'èŸ‹',
  'Á¶' => 'è¤»',
  'Á·' => 'è¤¶',
  'Á¸' => 'è¥„',
  'Á¹' => 'è¤¸',
  'Áº' => 'è¤½',
  'Á»' => 'è¦¬',
  'Á¼' => 'è¬Ž',
  'Á½' => 'è¬—',
  'Á¾' => 'è¬™',
  'Á¿' => 'è¬›',
  'ÁÀ' => 'è¬Š',
  'ÁÁ' => 'è¬ ',
  'ÁÂ' => 'è¬�',
  'ÁÃ' => 'è¬„',
  'ÁÄ' => 'è¬�',
  'ÁÅ' => 'è±�',
  'ÁÆ' => 'è°¿',
  'ÁÇ' => 'è±³',
  'ÁÈ' => 'è³º',
  'ÁÉ' => 'è³½',
  'ÁÊ' => 'è³¼',
  'ÁË' => 'è³¸',
  'ÁÌ' => 'è³»',
  'ÁÍ' => 'è¶¨',
  'ÁÎ' => 'è¹‰',
  'ÁÏ' => 'è¹‹',
  'ÁÐ' => 'è¹ˆ',
  'ÁÑ' => 'è¹Š',
  'ÁÒ' => 'è½„',
  'ÁÓ' => 'è¼¾',
  'ÁÔ' => 'è½‚',
  'ÁÕ' => 'è½…',
  'ÁÖ' => 'è¼¿',
  'Á×' => 'é�¿',
  'ÁØ' => 'é�½',
  'ÁÙ' => 'é‚„',
  'ÁÚ' => 'é‚�',
  'ÁÛ' => 'é‚‚',
  'ÁÜ' => 'é‚€',
  'ÁÝ' => 'é„¹',
  'ÁÞ' => 'é†£',
  'Áß' => 'é†ž',
  'Áà' => 'é†œ',
  'Áá' => 'é��',
  'Áâ' => 'éŽ‚',
  'Áã' => 'éŒ¨',
  'Áä' => 'é�µ',
  'Áå' => 'é�Š',
  'Áæ' => 'é�¥',
  'Áç' => 'é�‹',
  'Áè' => 'éŒ˜',
  'Áé' => 'é�¾',
  'Áê' => 'é�¬',
  'Áë' => 'é�›',
  'Áì' => 'é�°',
  'Áí' => 'é�š',
  'Áî' => 'é�”',
  'Áï' => 'é—Š',
  'Áð' => 'é—‹',
  'Áñ' => 'é—Œ',
  'Áò' => 'é—ˆ',
  'Áó' => 'é—†',
  'Áô' => 'éš±',
  'Áõ' => 'éš¸',
  'Áö' => 'é›–',
  'Á÷' => 'éœœ',
  'Áø' => 'éœž',
  'Áù' => 'éž ',
  'Áú' => 'éŸ“',
  'Áû' => 'é¡†',
  'Áü' => 'é¢¶',
  'Áý' => 'é¤µ',
  'Áþ' => 'é¨�',
  'Â@' => 'é§¿',
  'ÂA' => 'é®®',
  'ÂB' => 'é®«',
  'ÂC' => 'é®ª',
  'ÂD' => 'é®­',
  'ÂE' => 'é´»',
  'ÂF' => 'é´¿',
  'ÂG' => 'éº‹',
  'ÂH' => 'é»�',
  'ÂI' => 'é»ž',
  'ÂJ' => 'é»œ',
  'ÂK' => 'é»�',
  'ÂL' => 'é»›',
  'ÂM' => 'é¼¾',
  'ÂN' => 'é½‹',
  'ÂO' => 'å�¢',
  'ÂP' => 'åš•',
  'ÂQ' => 'åš®',
  'ÂR' => 'å£™',
  'ÂS' => 'å£˜',
  'ÂT' => 'å¬¸',
  'ÂU' => 'å½�',
  'ÂV' => 'æ‡£',
  'ÂW' => 'æˆ³',
  'ÂX' => 'æ“´',
  'ÂY' => 'æ“²',
  'ÂZ' => 'æ“¾',
  'Â[' => 'æ”†',
  'Â\\' => 'æ“º',
  'Â]' => 'æ“»',
  'Â^' => 'æ“·',
  'Â_' => 'æ–·',
  'Â`' => 'æ›œ',
  'Âa' => 'æœ¦',
  'Âb' => 'æª³',
  'Âc' => 'æª¬',
  'Âd' => 'æ«ƒ',
  'Âe' => 'æª»',
  'Âf' => 'æª¸',
  'Âg' => 'æ«‚',
  'Âh' => 'æª®',
  'Âi' => 'æª¯',
  'Âj' => 'æ­Ÿ',
  'Âk' => 'æ­¸',
  'Âl' => 'æ®¯',
  'Âm' => 'ç€‰',
  'Ân' => 'ç€‹',
  'Âo' => 'æ¿¾',
  'Âp' => 'ç€†',
  'Âq' => 'æ¿º',
  'Âr' => 'ç€‘',
  'Âs' => 'ç€�',
  'Ât' => 'ç‡»',
  'Âu' => 'ç‡¼',
  'Âv' => 'ç‡¾',
  'Âw' => 'ç‡¸',
  'Âx' => 'ç�·',
  'Ây' => 'ç�µ',
  'Âz' => 'ç’§',
  'Â{' => 'ç’¿',
  'Â|' => 'ç”•',
  'Â}' => 'ç™–',
  'Â~' => 'ç™˜',
  'Â¡' => 'ç™’',
  'Â¢' => 'çž½',
  'Â£' => 'çž¿',
  'Â¤' => 'çž»',
  'Â¥' => 'çž¼',
  'Â¦' => 'ç¤Ž',
  'Â§' => 'ç¦®',
  'Â¨' => 'ç©¡',
  'Â©' => 'ç©¢',
  'Âª' => 'ç© ',
  'Â«' => 'ç«„',
  'Â¬' => 'ç«…',
  'Â­' => 'ç°«',
  'Â®' => 'ç°§',
  'Â¯' => 'ç°ª',
  'Â°' => 'ç°ž',
  'Â±' => 'ç°£',
  'Â²' => 'ç°¡',
  'Â³' => 'ç³§',
  'Â´' => 'ç¹”',
  'Âµ' => 'ç¹•',
  'Â¶' => 'ç¹ž',
  'Â·' => 'ç¹š',
  'Â¸' => 'ç¹¡',
  'Â¹' => 'ç¹’',
  'Âº' => 'ç¹™',
  'Â»' => 'ç½ˆ',
  'Â¼' => 'ç¿¹',
  'Â½' => 'ç¿»',
  'Â¾' => 'è�·',
  'Â¿' => 'è�¶',
  'ÂÀ' => 'è‡�',
  'ÂÁ' => 'è‡�',
  'ÂÂ' => 'èˆŠ',
  'ÂÃ' => 'è—�',
  'ÂÄ' => 'è–©',
  'ÂÅ' => 'è—�',
  'ÂÆ' => 'è—�',
  'ÂÇ' => 'è—‰',
  'ÂÈ' => 'è–°',
  'ÂÉ' => 'è–º',
  'ÂÊ' => 'è–¹',
  'ÂË' => 'è–¦',
  'ÂÌ' => 'èŸ¯',
  'ÂÍ' => 'èŸ¬',
  'ÂÎ' => 'èŸ²',
  'ÂÏ' => 'èŸ ',
  'ÂÐ' => 'è¦†',
  'ÂÑ' => 'è¦²',
  'ÂÒ' => 'è§´',
  'ÂÓ' => 'è¬¨',
  'ÂÔ' => 'è¬¹',
  'ÂÕ' => 'è¬¬',
  'ÂÖ' => 'è¬«',
  'Â×' => 'è±�',
  'ÂØ' => 'è´…',
  'ÂÙ' => 'è¹™',
  'ÂÚ' => 'è¹£',
  'ÂÛ' => 'è¹¦',
  'ÂÜ' => 'è¹¤',
  'ÂÝ' => 'è¹Ÿ',
  'ÂÞ' => 'è¹•',
  'Âß' => 'è»€',
  'Âà' => 'è½‰',
  'Âá' => 'è½�',
  'Ââ' => 'é‚‡',
  'Âã' => 'é‚ƒ',
  'Âä' => 'é‚ˆ',
  'Âå' => 'é†«',
  'Âæ' => 'é†¬',
  'Âç' => 'é‡�',
  'Âè' => 'éŽ”',
  'Âé' => 'éŽŠ',
  'Âê' => 'éŽ–',
  'Âë' => 'éŽ¢',
  'Âì' => 'éŽ³',
  'Âí' => 'éŽ®',
  'Âî' => 'éŽ¬',
  'Âï' => 'éŽ°',
  'Âð' => 'éŽ˜',
  'Âñ' => 'éŽš',
  'Âò' => 'éŽ—',
  'Âó' => 'é—”',
  'Âô' => 'é—–',
  'Âõ' => 'é—�',
  'Âö' => 'é—•',
  'Â÷' => 'é›¢',
  'Âø' => 'é›œ',
  'Âù' => 'é›™',
  'Âú' => 'é››',
  'Âû' => 'é›ž',
  'Âü' => 'éœ¤',
  'Âý' => 'éž£',
  'Âþ' => 'éž¦',
  'Ã@' => 'éž­',
  'ÃA' => 'éŸ¹',
  'ÃB' => 'é¡�',
  'ÃC' => 'é¡�',
  'ÃD' => 'é¡Œ',
  'ÃE' => 'é¡Ž',
  'ÃF' => 'é¡“',
  'ÃG' => 'é¢º',
  'ÃH' => 'é¤¾',
  'ÃI' => 'é¤¿',
  'ÃJ' => 'é¤½',
  'ÃK' => 'é¤®',
  'ÃL' => 'é¦¥',
  'ÃM' => 'é¨Ž',
  'ÃN' => 'é«�',
  'ÃO' => 'é¬ƒ',
  'ÃP' => 'é¬†',
  'ÃQ' => 'é­�',
  'ÃR' => 'é­Ž',
  'ÃS' => 'é­�',
  'ÃT' => 'é¯Š',
  'ÃU' => 'é¯‰',
  'ÃV' => 'é¯½',
  'ÃW' => 'é¯ˆ',
  'ÃX' => 'é¯€',
  'ÃY' => 'éµ‘',
  'ÃZ' => 'éµ�',
  'Ã[' => 'éµ ',
  'Ã\\' => 'é» ',
  'Ã]' => 'é¼•',
  'Ã^' => 'é¼¬',
  'Ã_' => 'å„³',
  'Ã`' => 'åš¥',
  'Ãa' => 'å£ž',
  'Ãb' => 'å£Ÿ',
  'Ãc' => 'å£¢',
  'Ãd' => 'å¯µ',
  'Ãe' => 'é¾�',
  'Ãf' => 'å»¬',
  'Ãg' => 'æ‡²',
  'Ãh' => 'æ‡·',
  'Ãi' => 'æ‡¶',
  'Ãj' => 'æ‡µ',
  'Ãk' => 'æ”€',
  'Ãl' => 'æ”�',
  'Ãm' => 'æ› ',
  'Ãn' => 'æ›�',
  'Ão' => 'æ«¥',
  'Ãp' => 'æ«�',
  'Ãq' => 'æ«š',
  'Ãr' => 'æ«“',
  'Ãs' => 'ç€›',
  'Ãt' => 'ç€Ÿ',
  'Ãu' => 'ç€¨',
  'Ãv' => 'ç€š',
  'Ãw' => 'ç€�',
  'Ãx' => 'ç€•',
  'Ãy' => 'ç€˜',
  'Ãz' => 'çˆ†',
  'Ã{' => 'çˆ�',
  'Ã|' => 'ç‰˜',
  'Ã}' => 'çŠ¢',
  'Ã~' => 'ç�¸',
  'Ã¡' => 'ç�º',
  'Ã¢' => 'ç’½',
  'Ã£' => 'ç“Š',
  'Ã¤' => 'ç“£',
  'Ã¥' => 'ç–‡',
  'Ã¦' => 'ç–†',
  'Ã§' => 'ç™Ÿ',
  'Ã¨' => 'ç™¡',
  'Ã©' => 'çŸ‡',
  'Ãª' => 'ç¤™',
  'Ã«' => 'ç¦±',
  'Ã¬' => 'ç©«',
  'Ã­' => 'ç©©',
  'Ã®' => 'ç°¾',
  'Ã¯' => 'ç°¿',
  'Ã°' => 'ç°¸',
  'Ã±' => 'ç°½',
  'Ã²' => 'ç°·',
  'Ã³' => 'ç±€',
  'Ã´' => 'ç¹«',
  'Ãµ' => 'ç¹­',
  'Ã¶' => 'ç¹¹',
  'Ã·' => 'ç¹©',
  'Ã¸' => 'ç¹ª',
  'Ã¹' => 'ç¾…',
  'Ãº' => 'ç¹³',
  'Ã»' => 'ç¾¶',
  'Ã¼' => 'ç¾¹',
  'Ã½' => 'ç¾¸',
  'Ã¾' => 'è‡˜',
  'Ã¿' => 'è—©',
  'ÃÀ' => 'è—�',
  'ÃÁ' => 'è—ª',
  'ÃÂ' => 'è—•',
  'ÃÃ' => 'è—¤',
  'ÃÄ' => 'è—¥',
  'ÃÅ' => 'è—·',
  'ÃÆ' => 'èŸ»',
  'ÃÇ' => 'è …',
  'ÃÈ' => 'è �',
  'ÃÉ' => 'èŸ¹',
  'ÃÊ' => 'èŸ¾',
  'ÃË' => 'è¥ ',
  'ÃÌ' => 'è¥Ÿ',
  'ÃÍ' => 'è¥–',
  'ÃÎ' => 'è¥ž',
  'ÃÏ' => 'è­�',
  'ÃÐ' => 'è­œ',
  'ÃÑ' => 'è­˜',
  'ÃÒ' => 'è­‰',
  'ÃÓ' => 'è­š',
  'ÃÔ' => 'è­Ž',
  'ÃÕ' => 'è­�',
  'ÃÖ' => 'è­†',
  'Ã×' => 'è­™',
  'ÃØ' => 'è´ˆ',
  'ÃÙ' => 'è´Š',
  'ÃÚ' => 'è¹¼',
  'ÃÛ' => 'è¹²',
  'ÃÜ' => 'èº‡',
  'ÃÝ' => 'è¹¶',
  'ÃÞ' => 'è¹¬',
  'Ãß' => 'è¹º',
  'Ãà' => 'è¹´',
  'Ãá' => 'è½”',
  'Ãâ' => 'è½Ž',
  'Ãã' => 'è¾­',
  'Ãä' => 'é‚Š',
  'Ãå' => 'é‚‹',
  'Ãæ' => 'é†±',
  'Ãç' => 'é†®',
  'Ãè' => 'é�¡',
  'Ãé' => 'é�‘',
  'Ãê' => 'é�Ÿ',
  'Ãë' => 'é�ƒ',
  'Ãì' => 'é�ˆ',
  'Ãí' => 'é�œ',
  'Ãî' => 'é��',
  'Ãï' => 'é�–',
  'Ãð' => 'é�¢',
  'Ãñ' => 'é��',
  'Ãò' => 'é�˜',
  'Ãó' => 'é�¤',
  'Ãô' => 'é�—',
  'Ãõ' => 'é�¨',
  'Ãö' => 'é—œ',
  'Ã÷' => 'éš´',
  'Ãø' => 'é›£',
  'Ãù' => 'éœª',
  'Ãú' => 'éœ§',
  'Ãû' => 'é�¡',
  'Ãü' => 'éŸœ',
  'Ãý' => 'éŸ»',
  'Ãþ' => 'é¡ž',
  'Ä@' => 'é¡˜',
  'ÄA' => 'é¡›',
  'ÄB' => 'é¢¼',
  'ÄC' => 'é¥…',
  'ÄD' => 'é¥‰',
  'ÄE' => 'é¨–',
  'ÄF' => 'é¨™',
  'ÄG' => 'é¬�',
  'ÄH' => 'é¯¨',
  'ÄI' => 'é¯§',
  'ÄJ' => 'é¯–',
  'ÄK' => 'é¯›',
  'ÄL' => 'é¶‰',
  'ÄM' => 'éµ¡',
  'ÄN' => 'éµ²',
  'ÄO' => 'éµª',
  'ÄP' => 'éµ¬',
  'ÄQ' => 'éº’',
  'ÄR' => 'éº—',
  'ÄS' => 'éº“',
  'ÄT' => 'éº´',
  'ÄU' => 'å‹¸',
  'ÄV' => 'åš¨',
  'ÄW' => 'åš·',
  'ÄX' => 'åš¶',
  'ÄY' => 'åš´',
  'ÄZ' => 'åš¼',
  'Ä[' => 'å£¤',
  'Ä\\' => 'å­€',
  'Ä]' => 'å­ƒ',
  'Ä^' => 'å­½',
  'Ä_' => 'å¯¶',
  'Ä`' => 'å·‰',
  'Äa' => 'æ‡¸',
  'Äb' => 'æ‡º',
  'Äc' => 'æ”˜',
  'Äd' => 'æ””',
  'Äe' => 'æ”™',
  'Äf' => 'æ›¦',
  'Äg' => 'æœ§',
  'Äh' => 'æ«¬',
  'Äi' => 'ç€¾',
  'Äj' => 'ç€°',
  'Äk' => 'ç€²',
  'Äl' => 'çˆ�',
  'Äm' => 'ç�»',
  'Än' => 'ç“�',
  'Äo' => 'ç™¢',
  'Äp' => 'ç™¥',
  'Äq' => 'ç¤¦',
  'Är' => 'ç¤ª',
  'Äs' => 'ç¤¬',
  'Ät' => 'ç¤«',
  'Äu' => 'ç«‡',
  'Äv' => 'ç«¶',
  'Äw' => 'ç±Œ',
  'Äx' => 'ç±ƒ',
  'Äy' => 'ç±�',
  'Äz' => 'ç³¯',
  'Ä{' => 'ç³°',
  'Ä|' => 'è¾®',
  'Ä}' => 'ç¹½',
  'Ä~' => 'ç¹¼',
  'Ä¡' => 'çº‚',
  'Ä¢' => 'ç½Œ',
  'Ä£' => 'è€€',
  'Ä¤' => 'è‡š',
  'Ä¥' => 'è‰¦',
  'Ä¦' => 'è—»',
  'Ä§' => 'è—¹',
  'Ä¨' => 'è˜‘',
  'Ä©' => 'è—º',
  'Äª' => 'è˜†',
  'Ä«' => 'è˜‹',
  'Ä¬' => 'è˜‡',
  'Ä­' => 'è˜Š',
  'Ä®' => 'è ”',
  'Ä¯' => 'è •',
  'Ä°' => 'è¥¤',
  'Ä±' => 'è¦º',
  'Ä²' => 'è§¸',
  'Ä³' => 'è­°',
  'Ä´' => 'è­¬',
  'Äµ' => 'è­¦',
  'Ä¶' => 'è­¯',
  'Ä·' => 'è­Ÿ',
  'Ä¸' => 'è­«',
  'Ä¹' => 'è´�',
  'Äº' => 'è´�',
  'Ä»' => 'èº‰',
  'Ä¼' => 'èº�',
  'Ä½' => 'èº…',
  'Ä¾' => 'èº‚',
  'Ä¿' => 'é†´',
  'ÄÀ' => 'é‡‹',
  'ÄÁ' => 'é�˜',
  'ÄÂ' => 'é�ƒ',
  'ÄÃ' => 'é�½',
  'ÄÄ' => 'é—¡',
  'ÄÅ' => 'éœ°',
  'ÄÆ' => 'é£„',
  'ÄÇ' => 'é¥’',
  'ÄÈ' => 'é¥‘',
  'ÄÉ' => 'é¦¨',
  'ÄÊ' => 'é¨«',
  'ÄË' => 'é¨°',
  'ÄÌ' => 'é¨·',
  'ÄÍ' => 'é¨µ',
  'ÄÎ' => 'é°“',
  'ÄÏ' => 'é°�',
  'ÄÐ' => 'é¹¹',
  'ÄÑ' => 'éºµ',
  'ÄÒ' => 'é»¨',
  'ÄÓ' => 'é¼¯',
  'ÄÔ' => 'é½Ÿ',
  'ÄÕ' => 'é½£',
  'ÄÖ' => 'é½¡',
  'Ä×' => 'å„·',
  'ÄØ' => 'å„¸',
  'ÄÙ' => 'å›�',
  'ÄÚ' => 'å›€',
  'ÄÛ' => 'å›‚',
  'ÄÜ' => 'å¤”',
  'ÄÝ' => 'å±¬',
  'ÄÞ' => 'å·�',
  'Äß' => 'æ‡¼',
  'Äà' => 'æ‡¾',
  'Äá' => 'æ”�',
  'Äâ' => 'æ”œ',
  'Äã' => 'æ–•',
  'Ää' => 'æ›©',
  'Äå' => 'æ«»',
  'Äæ' => 'æ¬„',
  'Äç' => 'æ«º',
  'Äè' => 'æ®²',
  'Äé' => 'ç�Œ',
  'Äê' => 'çˆ›',
  'Äë' => 'çŠ§',
  'Äì' => 'ç“–',
  'Äí' => 'ç“”',
  'Äî' => 'ç™©',
  'Äï' => 'çŸ“',
  'Äð' => 'ç±�',
  'Äñ' => 'çº�',
  'Äò' => 'çºŒ',
  'Äó' => 'ç¾¼',
  'Äô' => 'è˜—',
  'Äõ' => 'è˜­',
  'Äö' => 'è˜š',
  'Ä÷' => 'è £',
  'Äø' => 'è ¢',
  'Äù' => 'è ¡',
  'Äú' => 'è Ÿ',
  'Äû' => 'è¥ª',
  'Äü' => 'è¥¬',
  'Äý' => 'è¦½',
  'Äþ' => 'è­´',
  'Å@' => 'è­·',
  'ÅA' => 'è­½',
  'ÅB' => 'è´“',
  'ÅC' => 'èºŠ',
  'ÅD' => 'èº�',
  'ÅE' => 'èº‹',
  'ÅF' => 'è½Ÿ',
  'ÅG' => 'è¾¯',
  'ÅH' => 'é†º',
  'ÅI' => 'é�®',
  'ÅJ' => 'é�³',
  'ÅK' => 'é�µ',
  'ÅL' => 'é�º',
  'ÅM' => 'é�¸',
  'ÅN' => 'é�²',
  'ÅO' => 'é�«',
  'ÅP' => 'é—¢',
  'ÅQ' => 'éœ¸',
  'ÅR' => 'éœ¹',
  'ÅS' => 'éœ²',
  'ÅT' => 'éŸ¿',
  'ÅU' => 'é¡§',
  'ÅV' => 'é¡¥',
  'ÅW' => 'é¥—',
  'ÅX' => 'é©…',
  'ÅY' => 'é©ƒ',
  'ÅZ' => 'é©€',
  'Å[' => 'é¨¾',
  'Å\\' => 'é«�',
  'Å]' => 'é­”',
  'Å^' => 'é­‘',
  'Å_' => 'é°­',
  'Å`' => 'é°¥',
  'Åa' => 'é¶¯',
  'Åb' => 'é¶´',
  'Åc' => 'é·‚',
  'Åd' => 'é¶¸',
  'Åe' => 'éº�',
  'Åf' => 'é»¯',
  'Åg' => 'é¼™',
  'Åh' => 'é½œ',
  'Åi' => 'é½¦',
  'Åj' => 'é½§',
  'Åk' => 'å„¼',
  'Ål' => 'å„»',
  'Åm' => 'å›ˆ',
  'Ån' => 'å›Š',
  'Åo' => 'å›‰',
  'Åp' => 'å­¿',
  'Åq' => 'å·”',
  'År' => 'å·’',
  'Ås' => 'å½Ž',
  'Åt' => 'æ‡¿',
  'Åu' => 'æ”¤',
  'Åv' => 'æ¬Š',
  'Åw' => 'æ­¡',
  'Åx' => 'ç�‘',
  'Åy' => 'ç�˜',
  'Åz' => 'çŽ€',
  'Å{' => 'ç“¤',
  'Å|' => 'ç–Š',
  'Å}' => 'ç™®',
  'Å~' => 'ç™¬',
  'Å¡' => 'ç¦³',
  'Å¢' => 'ç± ',
  'Å£' => 'ç±Ÿ',
  'Å¤' => 'è�¾',
  'Å¥' => 'è�½',
  'Å¦' => 'è‡Ÿ',
  'Å§' => 'è¥²',
  'Å¨' => 'è¥¯',
  'Å©' => 'è§¼',
  'Åª' => 'è®€',
  'Å«' => 'è´–',
  'Å¬' => 'è´—',
  'Å­' => 'èº‘',
  'Å®' => 'èº“',
  'Å¯' => 'è½¡',
  'Å°' => 'é…ˆ',
  'Å±' => 'é‘„',
  'Å²' => 'é‘‘',
  'Å³' => 'é‘’',
  'Å´' => 'éœ½',
  'Åµ' => 'éœ¾',
  'Å¶' => 'éŸƒ',
  'Å·' => 'éŸ�',
  'Å¸' => 'é¡«',
  'Å¹' => 'é¥•',
  'Åº' => 'é©•',
  'Å»' => 'é©�',
  'Å¼' => 'é«’',
  'Å½' => 'é¬š',
  'Å¾' => 'é±‰',
  'Å¿' => 'é°±',
  'ÅÀ' => 'é°¾',
  'ÅÁ' => 'é°»',
  'ÅÂ' => 'é·“',
  'ÅÃ' => 'é·—',
  'ÅÄ' => 'é¼´',
  'ÅÅ' => 'é½¬',
  'ÅÆ' => 'é½ª',
  'ÅÇ' => 'é¾”',
  'ÅÈ' => 'å›Œ',
  'ÅÉ' => 'å·–',
  'ÅÊ' => 'æˆ€',
  'ÅË' => 'æ”£',
  'ÅÌ' => 'æ”«',
  'ÅÍ' => 'æ”ª',
  'ÅÎ' => 'æ›¬',
  'ÅÏ' => 'æ¬�',
  'ÅÐ' => 'ç“š',
  'ÅÑ' => 'ç«Š',
  'ÅÒ' => 'ç±¤',
  'ÅÓ' => 'ç±£',
  'ÅÔ' => 'ç±¥',
  'ÅÕ' => 'çº“',
  'ÅÖ' => 'çº–',
  'Å×' => 'çº”',
  'ÅØ' => 'è‡¢',
  'ÅÙ' => 'è˜¸',
  'ÅÚ' => 'è˜¿',
  'ÅÛ' => 'è ±',
  'ÅÜ' => 'è®Š',
  'ÅÝ' => 'é‚�',
  'ÅÞ' => 'é‚�',
  'Åß' => 'é‘£',
  'Åà' => 'é‘ ',
  'Åá' => 'é‘¤',
  'Åâ' => 'é�¨',
  'Åã' => 'é¡¯',
  'Åä' => 'é¥œ',
  'Åå' => 'é©š',
  'Åæ' => 'é©›',
  'Åç' => 'é©—',
  'Åè' => 'é«“',
  'Åé' => 'é«”',
  'Åê' => 'é«‘',
  'Åë' => 'é±”',
  'Åì' => 'é±—',
  'Åí' => 'é±–',
  'Åî' => 'é·¥',
  'Åï' => 'éºŸ',
  'Åð' => 'é»´',
  'Åñ' => 'å›‘',
  'Åò' => 'å£©',
  'Åó' => 'æ”¬',
  'Åô' => 'ç�ž',
  'Åõ' => 'ç™±',
  'Åö' => 'ç™²',
  'Å÷' => 'çŸ—',
  'Åø' => 'ç½�',
  'Åù' => 'ç¾ˆ',
  'Åú' => 'è ¶',
  'Åû' => 'è ¹',
  'Åü' => 'è¡¢',
  'Åý' => 'è®“',
  'Åþ' => 'è®’',
  'Æ@' => 'è®–',
  'ÆA' => 'è‰·',
  'ÆB' => 'è´›',
  'ÆC' => 'é‡€',
  'ÆD' => 'é‘ª',
  'ÆE' => 'é�‚',
  'ÆF' => 'é�ˆ',
  'ÆG' => 'é�„',
  'ÆH' => 'éŸ†',
  'ÆI' => 'é¡°',
  'ÆJ' => 'é©Ÿ',
  'ÆK' => 'é¬¢',
  'ÆL' => 'é­˜',
  'ÆM' => 'é±Ÿ',
  'ÆN' => 'é·¹',
  'ÆO' => 'é·º',
  'ÆP' => 'é¹¼',
  'ÆQ' => 'é¹½',
  'ÆR' => 'é¼‡',
  'ÆS' => 'é½·',
  'ÆT' => 'é½²',
  'ÆU' => 'å»³',
  'ÆV' => 'æ¬–',
  'ÆW' => 'ç�£',
  'ÆX' => 'ç±¬',
  'ÆY' => 'ç±®',
  'ÆZ' => 'è »',
  'Æ[' => 'è§€',
  'Æ\\' => 'èº¡',
  'Æ]' => 'é‡�',
  'Æ^' => 'é‘²',
  'Æ_' => 'é‘°',
  'Æ`' => 'é¡±',
  'Æa' => 'é¥ž',
  'Æb' => 'é«–',
  'Æc' => 'é¬£',
  'Æd' => 'é»Œ',
  'Æe' => 'ç�¤',
  'Æf' => 'çŸš',
  'Æg' => 'è®š',
  'Æh' => 'é‘·',
  'Æi' => 'éŸ‰',
  'Æj' => 'é©¢',
  'Æk' => 'é©¥',
  'Æl' => 'çºœ',
  'Æm' => 'è®œ',
  'Æn' => 'èºª',
  'Æo' => 'é‡…',
  'Æp' => 'é‘½',
  'Æq' => 'é‘¾',
  'Ær' => 'é‘¼',
  'Æs' => 'é±·',
  'Æt' => 'é±¸',
  'Æu' => 'é»·',
  'Æv' => 'è±”',
  'Æw' => 'é‘¿',
  'Æx' => 'é¸š',
  'Æy' => 'çˆ¨',
  'Æz' => 'é©ª',
  'Æ{' => 'é¬±',
  'Æ|' => 'é¸›',
  'Æ}' => 'é¸ž',
  'Æ~' => 'ç±²',
  'É@' => 'ä¹‚',
  'ÉA' => 'ä¹œ',
  'ÉB' => 'å‡µ',
  'ÉC' => 'åŒš',
  'ÉD' => 'åŽ‚',
  'ÉE' => 'ä¸‡',
  'ÉF' => 'ä¸Œ',
  'ÉG' => 'ä¹‡',
  'ÉH' => 'äº�',
  'ÉI' => 'å›—',
  'ÉJ' => 'ï¨Œ',
  'ÉK' => 'å±®',
  'ÉL' => 'å½³',
  'ÉM' => 'ä¸�',
  'ÉN' => 'å†‡',
  'ÉO' => 'ä¸Ž',
  'ÉP' => 'ä¸®',
  'ÉQ' => 'äº“',
  'ÉR' => 'ä»‚',
  'ÉS' => 'ä»‰',
  'ÉT' => 'ä»ˆ',
  'ÉU' => 'å†˜',
  'ÉV' => 'å‹¼',
  'ÉW' => 'å�¬',
  'ÉX' => 'åŽ¹',
  'ÉY' => 'åœ ',
  'ÉZ' => 'å¤ƒ',
  'É[' => 'å¤¬',
  'É\\' => 'å°�',
  'É]' => 'å·¿',
  'É^' => 'æ—¡',
  'É_' => 'æ®³',
  'É`' => 'æ¯Œ',
  'Éa' => 'æ°”',
  'Éb' => 'çˆ¿',
  'Éc' => 'ä¸±',
  'Éd' => 'ä¸¼',
  'Ée' => 'ä»¨',
  'Éf' => 'ä»œ',
  'Ég' => 'ä»©',
  'Éh' => 'ä»¡',
  'Éi' => 'ä»�',
  'Éj' => 'ä»š',
  'Ék' => 'åˆŒ',
  'Él' => 'åŒœ',
  'Ém' => 'å�Œ',
  'Én' => 'åœ¢',
  'Éo' => 'åœ£',
  'Ép' => 'å¤—',
  'Éq' => 'å¤¯',
  'Ér' => 'å®�',
  'És' => 'å®„',
  'Ét' => 'å°’',
  'Éu' => 'å°»',
  'Év' => 'å±´',
  'Éw' => 'å±³',
  'Éx' => 'å¸„',
  'Éy' => 'åº€',
  'Éz' => 'åº‚',
  'É{' => 'å¿‰',
  'É|' => 'æˆ‰',
  'É}' => 'æ‰�',
  'É~' => 'æ°•',
  'É¡' => 'æ°¶',
  'É¢' => 'æ±ƒ',
  'É£' => 'æ°¿',
  'É¤' => 'æ°»',
  'É¥' => 'çŠ®',
  'É¦' => 'çŠ°',
  'É§' => 'çŽŠ',
  'É¨' => 'ç¦¸',
  'É©' => 'è‚Š',
  'Éª' => 'é˜ž',
  'É«' => 'ä¼Ž',
  'É¬' => 'ä¼˜',
  'É­' => 'ä¼¬',
  'É®' => 'ä»µ',
  'É¯' => 'ä¼”',
  'É°' => 'ä»±',
  'É±' => 'ä¼€',
  'É²' => 'ä»·',
  'É³' => 'ä¼ˆ',
  'É´' => 'ä¼�',
  'Éµ' => 'ä¼‚',
  'É¶' => 'ä¼…',
  'É·' => 'ä¼¢',
  'É¸' => 'ä¼“',
  'É¹' => 'ä¼„',
  'Éº' => 'ä»´',
  'É»' => 'ä¼’',
  'É¼' => 'å†±',
  'É½' => 'åˆ“',
  'É¾' => 'åˆ‰',
  'É¿' => 'åˆ�',
  'ÉÀ' => 'åŠ¦',
  'ÉÁ' => 'åŒ¢',
  'ÉÂ' => 'åŒŸ',
  'ÉÃ' => 'å��',
  'ÉÄ' => 'åŽŠ',
  'ÉÅ' => 'å�‡',
  'ÉÆ' => 'å›¡',
  'ÉÇ' => 'å›Ÿ',
  'ÉÈ' => 'åœ®',
  'ÉÉ' => 'åœª',
  'ÉÊ' => 'åœ´',
  'ÉË' => 'å¤¼',
  'ÉÌ' => 'å¦€',
  'ÉÍ' => 'å¥¼',
  'ÉÎ' => 'å¦…',
  'ÉÏ' => 'å¥»',
  'ÉÐ' => 'å¥¾',
  'ÉÑ' => 'å¥·',
  'ÉÒ' => 'å¥¿',
  'ÉÓ' => 'å­–',
  'ÉÔ' => 'å°•',
  'ÉÕ' => 'å°¥',
  'ÉÖ' => 'å±¼',
  'É×' => 'å±º',
  'ÉØ' => 'å±»',
  'ÉÙ' => 'å±¾',
  'ÉÚ' => 'å·Ÿ',
  'ÉÛ' => 'å¹µ',
  'ÉÜ' => 'åº„',
  'ÉÝ' => 'å¼‚',
  'ÉÞ' => 'å¼š',
  'Éß' => 'å½´',
  'Éà' => 'å¿•',
  'Éá' => 'å¿”',
  'Éâ' => 'å¿�',
  'Éã' => 'æ‰œ',
  'Éä' => 'æ‰ž',
  'Éå' => 'æ‰¤',
  'Éæ' => 'æ‰¡',
  'Éç' => 'æ‰¦',
  'Éè' => 'æ‰¢',
  'Éé' => 'æ‰™',
  'Éê' => 'æ‰ ',
  'Éë' => 'æ‰š',
  'Éì' => 'æ‰¥',
  'Éí' => 'æ—¯',
  'Éî' => 'æ—®',
  'Éï' => 'æœ¾',
  'Éð' => 'æœ¹',
  'Éñ' => 'æœ¸',
  'Éò' => 'æœ»',
  'Éó' => 'æœº',
  'Éô' => 'æœ¿',
  'Éõ' => 'æœ¼',
  'Éö' => 'æœ³',
  'É÷' => 'æ°˜',
  'Éø' => 'æ±†',
  'Éù' => 'æ±’',
  'Éú' => 'æ±œ',
  'Éû' => 'æ±�',
  'Éü' => 'æ±Š',
  'Éý' => 'æ±”',
  'Éþ' => 'æ±‹',
  'Ê@' => 'æ±Œ',
  'ÊA' => 'ç�±',
  'ÊB' => 'ç‰ž',
  'ÊC' => 'çŠ´',
  'ÊD' => 'çŠµ',
  'ÊE' => 'çŽŽ',
  'ÊF' => 'ç”ª',
  'ÊG' => 'ç™¿',
  'ÊH' => 'ç©µ',
  'ÊI' => 'ç½‘',
  'ÊJ' => 'è‰¸',
  'ÊK' => 'è‰¼',
  'ÊL' => 'èŠ€',
  'ÊM' => 'è‰½',
  'ÊN' => 'è‰¿',
  'ÊO' => 'è™�',
  'ÊP' => 'è¥¾',
  'ÊQ' => 'é‚™',
  'ÊR' => 'é‚—',
  'ÊS' => 'é‚˜',
  'ÊT' => 'é‚›',
  'ÊU' => 'é‚”',
  'ÊV' => 'é˜¢',
  'ÊW' => 'é˜¤',
  'ÊX' => 'é˜ ',
  'ÊY' => 'é˜£',
  'ÊZ' => 'ä½–',
  'Ê[' => 'ä¼»',
  'Ê\\' => 'ä½¢',
  'Ê]' => 'ä½‰',
  'Ê^' => 'ä½“',
  'Ê_' => 'ä½¤',
  'Ê`' => 'ä¼¾',
  'Êa' => 'ä½§',
  'Êb' => 'ä½’',
  'Êc' => 'ä½Ÿ',
  'Êd' => 'ä½�',
  'Êe' => 'ä½˜',
  'Êf' => 'ä¼­',
  'Êg' => 'ä¼³',
  'Êh' => 'ä¼¿',
  'Êi' => 'ä½¡',
  'Êj' => 'å†�',
  'Êk' => 'å†¹',
  'Êl' => 'åˆœ',
  'Êm' => 'åˆž',
  'Ên' => 'åˆ¡',
  'Êo' => 'åŠ­',
  'Êp' => 'åŠ®',
  'Êq' => 'åŒ‰',
  'Êr' => 'å�£',
  'Ês' => 'å�²',
  'Êt' => 'åŽŽ',
  'Êu' => 'åŽ�',
  'Êv' => 'å�°',
  'Êw' => 'å�·',
  'Êx' => 'å�ª',
  'Êy' => 'å‘”',
  'Êz' => 'å‘…',
  'Ê{' => 'å�™',
  'Ê|' => 'å�œ',
  'Ê}' => 'å�¥',
  'Ê~' => 'å�˜',
  'Ê¡' => 'å�½',
  'Ê¢' => 'å‘�',
  'Ê£' => 'å‘�',
  'Ê¤' => 'å�¨',
  'Ê¥' => 'å�¤',
  'Ê¦' => 'å‘‡',
  'Ê§' => 'å›®',
  'Ê¨' => 'å›§',
  'Ê©' => 'å›¥',
  'Êª' => 'å��',
  'Ê«' => 'å�…',
  'Ê¬' => 'å�Œ',
  'Ê­' => 'å�‰',
  'Ê®' => 'å�‹',
  'Ê¯' => 'å�’',
  'Ê°' => 'å¤†',
  'Ê±' => 'å¥€',
  'Ê²' => 'å¦¦',
  'Ê³' => 'å¦˜',
  'Ê´' => 'å¦ ',
  'Êµ' => 'å¦—',
  'Ê¶' => 'å¦Ž',
  'Ê·' => 'å¦¢',
  'Ê¸' => 'å¦�',
  'Ê¹' => 'å¦�',
  'Êº' => 'å¦§',
  'Ê»' => 'å¦¡',
  'Ê¼' => 'å®Ž',
  'Ê½' => 'å®’',
  'Ê¾' => 'å°¨',
  'Ê¿' => 'å°ª',
  'ÊÀ' => 'å²�',
  'ÊÁ' => 'å²�',
  'ÊÂ' => 'å²ˆ',
  'ÊÃ' => 'å²‹',
  'ÊÄ' => 'å²‰',
  'ÊÅ' => 'å²’',
  'ÊÆ' => 'å²Š',
  'ÊÇ' => 'å²†',
  'ÊÈ' => 'å²“',
  'ÊÉ' => 'å²•',
  'ÊÊ' => 'å· ',
  'ÊË' => 'å¸Š',
  'ÊÌ' => 'å¸Ž',
  'ÊÍ' => 'åº‹',
  'ÊÎ' => 'åº‰',
  'ÊÏ' => 'åºŒ',
  'ÊÐ' => 'åºˆ',
  'ÊÑ' => 'åº�',
  'ÊÒ' => 'å¼…',
  'ÊÓ' => 'å¼�',
  'ÊÔ' => 'å½¸',
  'ÊÕ' => 'å½¶',
  'ÊÖ' => 'å¿’',
  'Ê×' => 'å¿‘',
  'ÊØ' => 'å¿�',
  'ÊÙ' => 'å¿­',
  'ÊÚ' => 'å¿¨',
  'ÊÛ' => 'å¿®',
  'ÊÜ' => 'å¿³',
  'ÊÝ' => 'å¿¡',
  'ÊÞ' => 'å¿¤',
  'Êß' => 'å¿£',
  'Êà' => 'å¿º',
  'Êá' => 'å¿¯',
  'Êâ' => 'å¿·',
  'Êã' => 'å¿»',
  'Êä' => 'æ€€',
  'Êå' => 'å¿´',
  'Êæ' => 'æˆº',
  'Êç' => 'æŠƒ',
  'Êè' => 'æŠŒ',
  'Êé' => 'æŠŽ',
  'Êê' => 'æŠ�',
  'Êë' => 'æŠ”',
  'Êì' => 'æŠ‡',
  'Êí' => 'æ‰±',
  'Êî' => 'æ‰»',
  'Êï' => 'æ‰º',
  'Êð' => 'æ‰°',
  'Êñ' => 'æŠ�',
  'Êò' => 'æŠˆ',
  'Êó' => 'æ‰·',
  'Êô' => 'æ‰½',
  'Êõ' => 'æ‰²',
  'Êö' => 'æ‰´',
  'Ê÷' => 'æ”·',
  'Êø' => 'æ—°',
  'Êù' => 'æ—´',
  'Êú' => 'æ—³',
  'Êû' => 'æ—²',
  'Êü' => 'æ—µ',
  'Êý' => 'æ�…',
  'Êþ' => 'æ�‡',
  'Ë@' => 'æ�™',
  'ËA' => 'æ�•',
  'ËB' => 'æ�Œ',
  'ËC' => 'æ�ˆ',
  'ËD' => 'æ��',
  'ËE' => 'æ��',
  'ËF' => 'æ�š',
  'ËG' => 'æ�‹',
  'ËH' => 'æ¯�',
  'ËI' => 'æ°™',
  'ËJ' => 'æ°š',
  'ËK' => 'æ±¸',
  'ËL' => 'æ±§',
  'ËM' => 'æ±«',
  'ËN' => 'æ²„',
  'ËO' => 'æ²‹',
  'ËP' => 'æ²�',
  'ËQ' => 'æ±±',
  'ËR' => 'æ±¯',
  'ËS' => 'æ±©',
  'ËT' => 'æ²š',
  'ËU' => 'æ±­',
  'ËV' => 'æ²‡',
  'ËW' => 'æ²•',
  'ËX' => 'æ²œ',
  'ËY' => 'æ±¦',
  'ËZ' => 'æ±³',
  'Ë[' => 'æ±¥',
  'Ë\\' => 'æ±»',
  'Ë]' => 'æ²Ž',
  'Ë^' => 'ç�´',
  'Ë_' => 'ç�º',
  'Ë`' => 'ç‰£',
  'Ëa' => 'çŠ¿',
  'Ëb' => 'çŠ½',
  'Ëc' => 'ç‹ƒ',
  'Ëd' => 'ç‹†',
  'Ëe' => 'ç‹�',
  'Ëf' => 'çŠº',
  'Ëg' => 'ç‹…',
  'Ëh' => 'çŽ•',
  'Ëi' => 'çŽ—',
  'Ëj' => 'çŽ“',
  'Ëk' => 'çŽ”',
  'Ël' => 'çŽ’',
  'Ëm' => 'ç”º',
  'Ën' => 'ç”¹',
  'Ëo' => 'ç–”',
  'Ëp' => 'ç–•',
  'Ëq' => 'çš�',
  'Ër' => 'ç¤½',
  'Ës' => 'è€´',
  'Ët' => 'è‚•',
  'Ëu' => 'è‚™',
  'Ëv' => 'è‚�',
  'Ëw' => 'è‚’',
  'Ëx' => 'è‚œ',
  'Ëy' => 'èŠ�',
  'Ëz' => 'èŠ�',
  'Ë{' => 'èŠ…',
  'Ë|' => 'èŠŽ',
  'Ë}' => 'èŠ‘',
  'Ë~' => 'èŠ“',
  'Ë¡' => 'èŠŠ',
  'Ë¢' => 'èŠƒ',
  'Ë£' => 'èŠ„',
  'Ë¤' => 'è±¸',
  'Ë¥' => 'è¿‰',
  'Ë¦' => 'è¾¿',
  'Ë§' => 'é‚Ÿ',
  'Ë¨' => 'é‚¡',
  'Ë©' => 'é‚¥',
  'Ëª' => 'é‚ž',
  'Ë«' => 'é‚§',
  'Ë¬' => 'é‚ ',
  'Ë­' => 'é˜°',
  'Ë®' => 'é˜¨',
  'Ë¯' => 'é˜¯',
  'Ë°' => 'é˜­',
  'Ë±' => 'ä¸³',
  'Ë²' => 'ä¾˜',
  'Ë³' => 'ä½¼',
  'Ë´' => 'ä¾…',
  'Ëµ' => 'ä½½',
  'Ë¶' => 'ä¾€',
  'Ë·' => 'ä¾‡',
  'Ë¸' => 'ä½¶',
  'Ë¹' => 'ä½´',
  'Ëº' => 'ä¾‰',
  'Ë»' => 'ä¾„',
  'Ë¼' => 'ä½·',
  'Ë½' => 'ä½Œ',
  'Ë¾' => 'ä¾—',
  'Ë¿' => 'ä½ª',
  'ËÀ' => 'ä¾š',
  'ËÁ' => 'ä½¹',
  'ËÂ' => 'ä¾�',
  'ËÃ' => 'ä½¸',
  'ËÄ' => 'ä¾�',
  'ËÅ' => 'ä¾œ',
  'ËÆ' => 'ä¾”',
  'ËÇ' => 'ä¾ž',
  'ËÈ' => 'ä¾’',
  'ËÉ' => 'ä¾‚',
  'ËÊ' => 'ä¾•',
  'ËË' => 'ä½«',
  'ËÌ' => 'ä½®',
  'ËÍ' => 'å†ž',
  'ËÎ' => 'å†¼',
  'ËÏ' => 'å†¾',
  'ËÐ' => 'åˆµ',
  'ËÑ' => 'åˆ²',
  'ËÒ' => 'åˆ³',
  'ËÓ' => 'å‰†',
  'ËÔ' => 'åˆ±',
  'ËÕ' => 'åŠ¼',
  'ËÖ' => 'åŒŠ',
  'Ë×' => 'åŒ‹',
  'ËØ' => 'åŒ¼',
  'ËÙ' => 'åŽ’',
  'ËÚ' => 'åŽ”',
  'ËÛ' => 'å’‡',
  'ËÜ' => 'å‘¿',
  'ËÝ' => 'å’�',
  'ËÞ' => 'å’‘',
  'Ëß' => 'å’‚',
  'Ëà' => 'å’ˆ',
  'Ëá' => 'å‘«',
  'Ëâ' => 'å‘º',
  'Ëã' => 'å‘¾',
  'Ëä' => 'å‘¥',
  'Ëå' => 'å‘¬',
  'Ëæ' => 'å‘´',
  'Ëç' => 'å‘¦',
  'Ëè' => 'å’�',
  'Ëé' => 'å‘¯',
  'Ëê' => 'å‘¡',
  'Ëë' => 'å‘ ',
  'Ëì' => 'å’˜',
  'Ëí' => 'å‘£',
  'Ëî' => 'å‘§',
  'Ëï' => 'å‘¤',
  'Ëð' => 'å›·',
  'Ëñ' => 'å›¹',
  'Ëò' => 'å�¯',
  'Ëó' => 'å�²',
  'Ëô' => 'å�­',
  'Ëõ' => 'å�«',
  'Ëö' => 'å�±',
  'Ë÷' => 'å�°',
  'Ëø' => 'å�¶',
  'Ëù' => 'åž€',
  'Ëú' => 'å�µ',
  'Ëû' => 'å�»',
  'Ëü' => 'å�³',
  'Ëý' => 'å�´',
  'Ëþ' => 'å�¢',
  'Ì@' => 'å�¨',
  'ÌA' => 'å�½',
  'ÌB' => 'å¤Œ',
  'ÌC' => 'å¥…',
  'ÌD' => 'å¦µ',
  'ÌE' => 'å¦º',
  'ÌF' => 'å§�',
  'ÌG' => 'å§Ž',
  'ÌH' => 'å¦²',
  'ÌI' => 'å§Œ',
  'ÌJ' => 'å§�',
  'ÌK' => 'å¦¶',
  'ÌL' => 'å¦¼',
  'ÌM' => 'å§ƒ',
  'ÌN' => 'å§–',
  'ÌO' => 'å¦±',
  'ÌP' => 'å¦½',
  'ÌQ' => 'å§€',
  'ÌR' => 'å§ˆ',
  'ÌS' => 'å¦´',
  'ÌT' => 'å§‡',
  'ÌU' => 'å­¢',
  'ÌV' => 'å­¥',
  'ÌW' => 'å®“',
  'ÌX' => 'å®•',
  'ÌY' => 'å±„',
  'ÌZ' => 'å±‡',
  'Ì[' => 'å²®',
  'Ì\\' => 'å²¤',
  'Ì]' => 'å² ',
  'Ì^' => 'å²µ',
  'Ì_' => 'å²¯',
  'Ì`' => 'å²¨',
  'Ìa' => 'å²¬',
  'Ìb' => 'å²Ÿ',
  'Ìc' => 'å²£',
  'Ìd' => 'å²­',
  'Ìe' => 'å²¢',
  'Ìf' => 'å²ª',
  'Ìg' => 'å²§',
  'Ìh' => 'å²�',
  'Ìi' => 'å²¥',
  'Ìj' => 'å²¶',
  'Ìk' => 'å²°',
  'Ìl' => 'å²¦',
  'Ìm' => 'å¸—',
  'Ìn' => 'å¸”',
  'Ìo' => 'å¸™',
  'Ìp' => 'å¼¨',
  'Ìq' => 'å¼¢',
  'Ìr' => 'å¼£',
  'Ìs' => 'å¼¤',
  'Ìt' => 'å½”',
  'Ìu' => 'å¾‚',
  'Ìv' => 'å½¾',
  'Ìw' => 'å½½',
  'Ìx' => 'å¿ž',
  'Ìy' => 'å¿¥',
  'Ìz' => 'æ€­',
  'Ì{' => 'æ€¦',
  'Ì|' => 'æ€™',
  'Ì}' => 'æ€²',
  'Ì~' => 'æ€‹',
  'Ì¡' => 'æ€´',
  'Ì¢' => 'æ€Š',
  'Ì£' => 'æ€—',
  'Ì¤' => 'æ€³',
  'Ì¥' => 'æ€š',
  'Ì¦' => 'æ€ž',
  'Ì§' => 'æ€¬',
  'Ì¨' => 'æ€¢',
  'Ì©' => 'æ€�',
  'Ìª' => 'æ€�',
  'Ì«' => 'æ€®',
  'Ì¬' => 'æ€“',
  'Ì­' => 'æ€‘',
  'Ì®' => 'æ€Œ',
  'Ì¯' => 'æ€‰',
  'Ì°' => 'æ€œ',
  'Ì±' => 'æˆ”',
  'Ì²' => 'æˆ½',
  'Ì³' => 'æŠ­',
  'Ì´' => 'æŠ´',
  'Ìµ' => 'æ‹‘',
  'Ì¶' => 'æŠ¾',
  'Ì·' => 'æŠª',
  'Ì¸' => 'æŠ¶',
  'Ì¹' => 'æ‹Š',
  'Ìº' => 'æŠ®',
  'Ì»' => 'æŠ³',
  'Ì¼' => 'æŠ¯',
  'Ì½' => 'æŠ»',
  'Ì¾' => 'æŠ©',
  'Ì¿' => 'æŠ°',
  'ÌÀ' => 'æŠ¸',
  'ÌÁ' => 'æ”½',
  'ÌÂ' => 'æ–¨',
  'ÌÃ' => 'æ–»',
  'ÌÄ' => 'æ˜‰',
  'ÌÅ' => 'æ—¼',
  'ÌÆ' => 'æ˜„',
  'ÌÇ' => 'æ˜’',
  'ÌÈ' => 'æ˜ˆ',
  'ÌÉ' => 'æ—»',
  'ÌÊ' => 'æ˜ƒ',
  'ÌË' => 'æ˜‹',
  'ÌÌ' => 'æ˜�',
  'ÌÍ' => 'æ˜…',
  'ÌÎ' => 'æ—½',
  'ÌÏ' => 'æ˜‘',
  'ÌÐ' => 'æ˜�',
  'ÌÑ' => 'æ›¶',
  'ÌÒ' => 'æœŠ',
  'ÌÓ' => 'æž…',
  'ÌÔ' => 'æ�¬',
  'ÌÕ' => 'æžŽ',
  'ÌÖ' => 'æž’',
  'Ì×' => 'æ�¶',
  'ÌØ' => 'æ�»',
  'ÌÙ' => 'æž˜',
  'ÌÚ' => 'æž†',
  'ÌÛ' => 'æž„',
  'ÌÜ' => 'æ�´',
  'ÌÝ' => 'æž�',
  'ÌÞ' => 'æžŒ',
  'Ìß' => 'æ�º',
  'Ìà' => 'æžŸ',
  'Ìá' => 'æž‘',
  'Ìâ' => 'æž™',
  'Ìã' => 'æžƒ',
  'Ìä' => 'æ�½',
  'Ìå' => 'æž�',
  'Ìæ' => 'æ�¸',
  'Ìç' => 'æ�¹',
  'Ìè' => 'æž”',
  'Ìé' => 'æ¬¥',
  'Ìê' => 'æ®€',
  'Ìë' => 'æ­¾',
  'Ìì' => 'æ¯ž',
  'Ìí' => 'æ°�',
  'Ìî' => 'æ²“',
  'Ìï' => 'æ³¬',
  'Ìð' => 'æ³«',
  'Ìñ' => 'æ³®',
  'Ìò' => 'æ³™',
  'Ìó' => 'æ²¶',
  'Ìô' => 'æ³”',
  'Ìõ' => 'æ²­',
  'Ìö' => 'æ³§',
  'Ì÷' => 'æ²·',
  'Ìø' => 'æ³�',
  'Ìù' => 'æ³‚',
  'Ìú' => 'æ²º',
  'Ìû' => 'æ³ƒ',
  'Ìü' => 'æ³†',
  'Ìý' => 'æ³­',
  'Ìþ' => 'æ³²',
  'Í@' => 'æ³’',
  'ÍA' => 'æ³�',
  'ÍB' => 'æ²´',
  'ÍC' => 'æ²Š',
  'ÍD' => 'æ²�',
  'ÍE' => 'æ²€',
  'ÍF' => 'æ³ž',
  'ÍG' => 'æ³€',
  'ÍH' => 'æ´°',
  'ÍI' => 'æ³�',
  'ÍJ' => 'æ³‡',
  'ÍK' => 'æ²°',
  'ÍL' => 'æ³¹',
  'ÍM' => 'æ³�',
  'ÍN' => 'æ³©',
  'ÍO' => 'æ³‘',
  'ÍP' => 'ç‚”',
  'ÍQ' => 'ç‚˜',
  'ÍR' => 'ç‚…',
  'ÍS' => 'ç‚“',
  'ÍT' => 'ç‚†',
  'ÍU' => 'ç‚„',
  'ÍV' => 'ç‚‘',
  'ÍW' => 'ç‚–',
  'ÍX' => 'ç‚‚',
  'ÍY' => 'ç‚š',
  'ÍZ' => 'ç‚ƒ',
  'Í[' => 'ç‰ª',
  'Í\\' => 'ç‹–',
  'Í]' => 'ç‹‹',
  'Í^' => 'ç‹˜',
  'Í_' => 'ç‹‰',
  'Í`' => 'ç‹œ',
  'Ía' => 'ç‹’',
  'Íb' => 'ç‹”',
  'Íc' => 'ç‹š',
  'Íd' => 'ç‹Œ',
  'Íe' => 'ç‹‘',
  'Íf' => 'çŽ¤',
  'Íg' => 'çŽ¡',
  'Íh' => 'çŽ­',
  'Íi' => 'çŽ¦',
  'Íj' => 'çŽ¢',
  'Ík' => 'çŽ ',
  'Íl' => 'çŽ¬',
  'Ím' => 'çŽ�',
  'Ín' => 'ç“�',
  'Ío' => 'ç“¨',
  'Íp' => 'ç”¿',
  'Íq' => 'ç•€',
  'Ír' => 'ç”¾',
  'Ís' => 'ç–Œ',
  'Ít' => 'ç–˜',
  'Íu' => 'çš¯',
  'Ív' => 'ç›³',
  'Íw' => 'ç›±',
  'Íx' => 'ç›°',
  'Íy' => 'ç›µ',
  'Íz' => 'çŸ¸',
  'Í{' => 'çŸ¼',
  'Í|' => 'çŸ¹',
  'Í}' => 'çŸ»',
  'Í~' => 'çŸº',
  'Í¡' => 'çŸ·',
  'Í¢' => 'ç¥‚',
  'Í£' => 'ç¤¿',
  'Í¤' => 'ç§…',
  'Í¥' => 'ç©¸',
  'Í¦' => 'ç©»',
  'Í§' => 'ç«»',
  'Í¨' => 'ç±µ',
  'Í©' => 'ç³½',
  'Íª' => 'è€µ',
  'Í«' => 'è‚�',
  'Í¬' => 'è‚®',
  'Í­' => 'è‚£',
  'Í®' => 'è‚¸',
  'Í¯' => 'è‚µ',
  'Í°' => 'è‚­',
  'Í±' => 'èˆ ',
  'Í²' => 'èŠ ',
  'Í³' => 'è‹€',
  'Í´' => 'èŠ«',
  'Íµ' => 'èŠš',
  'Í¶' => 'èŠ˜',
  'Í·' => 'èŠ›',
  'Í¸' => 'èŠµ',
  'Í¹' => 'èŠ§',
  'Íº' => 'èŠ®',
  'Í»' => 'èŠ¼',
  'Í¼' => 'èŠž',
  'Í½' => 'èŠº',
  'Í¾' => 'èŠ´',
  'Í¿' => 'èŠ¨',
  'ÍÀ' => 'èŠ¡',
  'ÍÁ' => 'èŠ©',
  'ÍÂ' => 'è‹‚',
  'ÍÃ' => 'èŠ¤',
  'ÍÄ' => 'è‹ƒ',
  'ÍÅ' => 'èŠ¶',
  'ÍÆ' => 'èŠ¢',
  'ÍÇ' => 'è™°',
  'ÍÈ' => 'è™¯',
  'ÍÉ' => 'è™­',
  'ÍÊ' => 'è™®',
  'ÍË' => 'è±–',
  'ÍÌ' => 'è¿’',
  'ÍÍ' => 'è¿‹',
  'ÍÎ' => 'è¿“',
  'ÍÏ' => 'è¿�',
  'ÍÐ' => 'è¿–',
  'ÍÑ' => 'è¿•',
  'ÍÒ' => 'è¿—',
  'ÍÓ' => 'é‚²',
  'ÍÔ' => 'é‚´',
  'ÍÕ' => 'é‚¯',
  'ÍÖ' => 'é‚³',
  'Í×' => 'é‚°',
  'ÍØ' => 'é˜¹',
  'ÍÙ' => 'é˜½',
  'ÍÚ' => 'é˜¼',
  'ÍÛ' => 'é˜º',
  'ÍÜ' => 'é™ƒ',
  'ÍÝ' => 'ä¿�',
  'ÍÞ' => 'ä¿…',
  'Íß' => 'ä¿“',
  'Íà' => 'ä¾²',
  'Íá' => 'ä¿‰',
  'Íâ' => 'ä¿‹',
  'Íã' => 'ä¿�',
  'Íä' => 'ä¿”',
  'Íå' => 'ä¿œ',
  'Íæ' => 'ä¿™',
  'Íç' => 'ä¾»',
  'Íè' => 'ä¾³',
  'Íé' => 'ä¿›',
  'Íê' => 'ä¿‡',
  'Íë' => 'ä¿–',
  'Íì' => 'ä¾º',
  'Íí' => 'ä¿€',
  'Íî' => 'ä¾¹',
  'Íï' => 'ä¿¬',
  'Íð' => 'å‰„',
  'Íñ' => 'å‰‰',
  'Íò' => 'å‹€',
  'Íó' => 'å‹‚',
  'Íô' => 'åŒ½',
  'Íõ' => 'å�¼',
  'Íö' => 'åŽ—',
  'Í÷' => 'åŽ–',
  'Íø' => 'åŽ™',
  'Íù' => 'åŽ˜',
  'Íú' => 'å’º',
  'Íû' => 'å’¡',
  'Íü' => 'å’­',
  'Íý' => 'å’¥',
  'Íþ' => 'å“�',
  'Î@' => 'å“ƒ',
  'ÎA' => 'èŒ�',
  'ÎB' => 'å’·',
  'ÎC' => 'å’®',
  'ÎD' => 'å“–',
  'ÎE' => 'å’¶',
  'ÎF' => 'å“…',
  'ÎG' => 'å“†',
  'ÎH' => 'å’ ',
  'ÎI' => 'å‘°',
  'ÎJ' => 'å’¼',
  'ÎK' => 'å’¢',
  'ÎL' => 'å’¾',
  'ÎM' => 'å‘²',
  'ÎN' => 'å“ž',
  'ÎO' => 'å’°',
  'ÎP' => 'åžµ',
  'ÎQ' => 'åžž',
  'ÎR' => 'åžŸ',
  'ÎS' => 'åž¤',
  'ÎT' => 'åžŒ',
  'ÎU' => 'åž—',
  'ÎV' => 'åž�',
  'ÎW' => 'åž›',
  'ÎX' => 'åž”',
  'ÎY' => 'åž˜',
  'ÎZ' => 'åž�',
  'Î[' => 'åž™',
  'Î\\' => 'åž¥',
  'Î]' => 'åžš',
  'Î^' => 'åž•',
  'Î_' => 'å£´',
  'Î`' => 'å¤�',
  'Îa' => 'å¥“',
  'Îb' => 'å§¡',
  'Îc' => 'å§ž',
  'Îd' => 'å§®',
  'Îe' => 'å¨€',
  'Îf' => 'å§±',
  'Îg' => 'å§�',
  'Îh' => 'å§º',
  'Îi' => 'å§½',
  'Îj' => 'å§¼',
  'Îk' => 'å§¶',
  'Îl' => 'å§¤',
  'Îm' => 'å§²',
  'În' => 'å§·',
  'Îo' => 'å§›',
  'Îp' => 'å§©',
  'Îq' => 'å§³',
  'Îr' => 'å§µ',
  'Îs' => 'å§ ',
  'Ît' => 'å§¾',
  'Îu' => 'å§´',
  'Îv' => 'å§­',
  'Îw' => 'å®¨',
  'Îx' => 'å±Œ',
  'Îy' => 'å³�',
  'Îz' => 'å³˜',
  'Î{' => 'å³Œ',
  'Î|' => 'å³—',
  'Î}' => 'å³‹',
  'Î~' => 'å³›',
  'Î¡' => 'å³ž',
  'Î¢' => 'å³š',
  'Î£' => 'å³‰',
  'Î¤' => 'å³‡',
  'Î¥' => 'å³Š',
  'Î¦' => 'å³–',
  'Î§' => 'å³“',
  'Î¨' => 'å³”',
  'Î©' => 'å³�',
  'Îª' => 'å³ˆ',
  'Î«' => 'å³†',
  'Î¬' => 'å³Ž',
  'Î­' => 'å³Ÿ',
  'Î®' => 'å³¸',
  'Î¯' => 'å·¹',
  'Î°' => 'å¸¡',
  'Î±' => 'å¸¢',
  'Î²' => 'å¸£',
  'Î³' => 'å¸ ',
  'Î´' => 'å¸¤',
  'Îµ' => 'åº°',
  'Î¶' => 'åº¤',
  'Î·' => 'åº¢',
  'Î¸' => 'åº›',
  'Î¹' => 'åº£',
  'Îº' => 'åº¥',
  'Î»' => 'å¼‡',
  'Î¼' => 'å¼®',
  'Î½' => 'å½–',
  'Î¾' => 'å¾†',
  'Î¿' => 'æ€·',
  'ÎÀ' => 'æ€¹',
  'ÎÁ' => 'æ�”',
  'ÎÂ' => 'æ�²',
  'ÎÃ' => 'æ�ž',
  'ÎÄ' => 'æ�…',
  'ÎÅ' => 'æ�“',
  'ÎÆ' => 'æ�‡',
  'ÎÇ' => 'æ�‰',
  'ÎÈ' => 'æ�›',
  'ÎÉ' => 'æ�Œ',
  'ÎÊ' => 'æ�€',
  'ÎË' => 'æ�‚',
  'ÎÌ' => 'æ�Ÿ',
  'ÎÍ' => 'æ€¤',
  'ÎÎ' => 'æ�„',
  'ÎÏ' => 'æ�˜',
  'ÎÐ' => 'æ�¦',
  'ÎÑ' => 'æ�®',
  'ÎÒ' => 'æ‰‚',
  'ÎÓ' => 'æ‰ƒ',
  'ÎÔ' => 'æ‹�',
  'ÎÕ' => 'æŒ�',
  'ÎÖ' => 'æŒ‹',
  'Î×' => 'æ‹µ',
  'ÎØ' => 'æŒŽ',
  'ÎÙ' => 'æŒƒ',
  'ÎÚ' => 'æ‹«',
  'ÎÛ' => 'æ‹¹',
  'ÎÜ' => 'æŒ�',
  'ÎÝ' => 'æŒŒ',
  'ÎÞ' => 'æ‹¸',
  'Îß' => 'æ‹¶',
  'Îà' => 'æŒ€',
  'Îá' => 'æŒ“',
  'Îâ' => 'æŒ”',
  'Îã' => 'æ‹º',
  'Îä' => 'æŒ•',
  'Îå' => 'æ‹»',
  'Îæ' => 'æ‹°',
  'Îç' => 'æ•�',
  'Îè' => 'æ•ƒ',
  'Îé' => 'æ–ª',
  'Îê' => 'æ–¿',
  'Îë' => 'æ˜¶',
  'Îì' => 'æ˜¡',
  'Îí' => 'æ˜²',
  'Îî' => 'æ˜µ',
  'Îï' => 'æ˜œ',
  'Îð' => 'æ˜¦',
  'Îñ' => 'æ˜¢',
  'Îò' => 'æ˜³',
  'Îó' => 'æ˜«',
  'Îô' => 'æ˜º',
  'Îõ' => 'æ˜�',
  'Îö' => 'æ˜´',
  'Î÷' => 'æ˜¹',
  'Îø' => 'æ˜®',
  'Îù' => 'æœ�',
  'Îú' => 'æœ�',
  'Îû' => 'æŸ�',
  'Îü' => 'æŸ²',
  'Îý' => 'æŸˆ',
  'Îþ' => 'æžº',
  'Ï@' => 'æŸœ',
  'ÏA' => 'æž»',
  'ÏB' => 'æŸ¸',
  'ÏC' => 'æŸ˜',
  'ÏD' => 'æŸ€',
  'ÏE' => 'æž·',
  'ÏF' => 'æŸ…',
  'ÏG' => 'æŸ«',
  'ÏH' => 'æŸ¤',
  'ÏI' => 'æŸŸ',
  'ÏJ' => 'æžµ',
  'ÏK' => 'æŸ�',
  'ÏL' => 'æž³',
  'ÏM' => 'æŸ·',
  'ÏN' => 'æŸ¶',
  'ÏO' => 'æŸ®',
  'ÏP' => 'æŸ£',
  'ÏQ' => 'æŸ‚',
  'ÏR' => 'æž¹',
  'ÏS' => 'æŸŽ',
  'ÏT' => 'æŸ§',
  'ÏU' => 'æŸ°',
  'ÏV' => 'æž²',
  'ÏW' => 'æŸ¼',
  'ÏX' => 'æŸ†',
  'ÏY' => 'æŸ­',
  'ÏZ' => 'æŸŒ',
  'Ï[' => 'æž®',
  'Ï\\' => 'æŸ¦',
  'Ï]' => 'æŸ›',
  'Ï^' => 'æŸº',
  'Ï_' => 'æŸ‰',
  'Ï`' => 'æŸŠ',
  'Ïa' => 'æŸƒ',
  'Ïb' => 'æŸª',
  'Ïc' => 'æŸ‹',
  'Ïd' => 'æ¬¨',
  'Ïe' => 'æ®‚',
  'Ïf' => 'æ®„',
  'Ïg' => 'æ®¶',
  'Ïh' => 'æ¯–',
  'Ïi' => 'æ¯˜',
  'Ïj' => 'æ¯ ',
  'Ïk' => 'æ° ',
  'Ïl' => 'æ°¡',
  'Ïm' => 'æ´¨',
  'Ïn' => 'æ´´',
  'Ïo' => 'æ´­',
  'Ïp' => 'æ´Ÿ',
  'Ïq' => 'æ´¼',
  'Ïr' => 'æ´¿',
  'Ïs' => 'æ´’',
  'Ït' => 'æ´Š',
  'Ïu' => 'æ³š',
  'Ïv' => 'æ´³',
  'Ïw' => 'æ´„',
  'Ïx' => 'æ´™',
  'Ïy' => 'æ´º',
  'Ïz' => 'æ´š',
  'Ï{' => 'æ´‘',
  'Ï|' => 'æ´€',
  'Ï}' => 'æ´�',
  'Ï~' => 'æµ‚',
  'Ï¡' => 'æ´�',
  'Ï¢' => 'æ´˜',
  'Ï£' => 'æ´·',
  'Ï¤' => 'æ´ƒ',
  'Ï¥' => 'æ´�',
  'Ï¦' => 'æµ€',
  'Ï§' => 'æ´‡',
  'Ï¨' => 'æ´ ',
  'Ï©' => 'æ´¬',
  'Ïª' => 'æ´ˆ',
  'Ï«' => 'æ´¢',
  'Ï¬' => 'æ´‰',
  'Ï­' => 'æ´�',
  'Ï®' => 'ç‚·',
  'Ï¯' => 'ç‚Ÿ',
  'Ï°' => 'ç‚¾',
  'Ï±' => 'ç‚±',
  'Ï²' => 'ç‚°',
  'Ï³' => 'ç‚¡',
  'Ï´' => 'ç‚´',
  'Ïµ' => 'ç‚µ',
  'Ï¶' => 'ç‚©',
  'Ï·' => 'ç‰�',
  'Ï¸' => 'ç‰‰',
  'Ï¹' => 'ç‰Š',
  'Ïº' => 'ç‰¬',
  'Ï»' => 'ç‰°',
  'Ï¼' => 'ç‰³',
  'Ï½' => 'ç‰®',
  'Ï¾' => 'ç‹Š',
  'Ï¿' => 'ç‹¤',
  'ÏÀ' => 'ç‹¨',
  'ÏÁ' => 'ç‹«',
  'ÏÂ' => 'ç‹Ÿ',
  'ÏÃ' => 'ç‹ª',
  'ÏÄ' => 'ç‹¦',
  'ÏÅ' => 'ç‹£',
  'ÏÆ' => 'çŽ…',
  'ÏÇ' => 'ç�Œ',
  'ÏÈ' => 'ç�‚',
  'ÏÉ' => 'ç�ˆ',
  'ÏÊ' => 'ç�…',
  'ÏË' => 'çŽ¹',
  'ÏÌ' => 'çŽ¶',
  'ÏÍ' => 'çŽµ',
  'ÏÎ' => 'çŽ´',
  'ÏÏ' => 'ç�«',
  'ÏÐ' => 'çŽ¿',
  'ÏÑ' => 'ç�‡',
  'ÏÒ' => 'çŽ¾',
  'ÏÓ' => 'ç�ƒ',
  'ÏÔ' => 'ç�†',
  'ÏÕ' => 'çŽ¸',
  'ÏÖ' => 'ç�‹',
  'Ï×' => 'ç“¬',
  'ÏØ' => 'ç“®',
  'ÏÙ' => 'ç”®',
  'ÏÚ' => 'ç•‡',
  'ÏÛ' => 'ç•ˆ',
  'ÏÜ' => 'ç–§',
  'ÏÝ' => 'ç–ª',
  'ÏÞ' => 'ç™¹',
  'Ïß' => 'ç›„',
  'Ïà' => 'çœˆ',
  'Ïá' => 'çœƒ',
  'Ïâ' => 'çœ„',
  'Ïã' => 'çœ…',
  'Ïä' => 'çœŠ',
  'Ïå' => 'ç›·',
  'Ïæ' => 'ç›»',
  'Ïç' => 'ç›º',
  'Ïè' => 'çŸ§',
  'Ïé' => 'çŸ¨',
  'Ïê' => 'ç †',
  'Ïë' => 'ç ‘',
  'Ïì' => 'ç ’',
  'Ïí' => 'ç …',
  'Ïî' => 'ç �',
  'Ïï' => 'ç �',
  'Ïð' => 'ç Ž',
  'Ïñ' => 'ç ‰',
  'Ïò' => 'ç ƒ',
  'Ïó' => 'ç “',
  'Ïô' => 'ç¥Š',
  'Ïõ' => 'ç¥Œ',
  'Ïö' => 'ç¥‹',
  'Ï÷' => 'ç¥…',
  'Ïø' => 'ç¥„',
  'Ïù' => 'ç§•',
  'Ïú' => 'ç§�',
  'Ïû' => 'ç§�',
  'Ïü' => 'ç§–',
  'Ïý' => 'ç§Ž',
  'Ïþ' => 'çª€',
  'Ð@' => 'ç©¾',
  'ÐA' => 'ç«‘',
  'ÐB' => 'ç¬€',
  'ÐC' => 'ç¬�',
  'ÐD' => 'ç±º',
  'ÐE' => 'ç±¸',
  'ÐF' => 'ç±¹',
  'ÐG' => 'ç±¿',
  'ÐH' => 'ç²€',
  'ÐI' => 'ç²�',
  'ÐJ' => 'ç´ƒ',
  'ÐK' => 'ç´ˆ',
  'ÐL' => 'ç´�',
  'ÐM' => 'ç½˜',
  'ÐN' => 'ç¾‘',
  'ÐO' => 'ç¾�',
  'ÐP' => 'ç¾¾',
  'ÐQ' => 'è€‡',
  'ÐR' => 'è€Ž',
  'ÐS' => 'è€�',
  'ÐT' => 'è€”',
  'ÐU' => 'è€·',
  'ÐV' => 'èƒ˜',
  'ÐW' => 'èƒ‡',
  'ÐX' => 'èƒ ',
  'ÐY' => 'èƒ‘',
  'ÐZ' => 'èƒˆ',
  'Ð[' => 'èƒ‚',
  'Ð\\' => 'èƒ�',
  'Ð]' => 'èƒ…',
  'Ð^' => 'èƒ£',
  'Ð_' => 'èƒ™',
  'Ð`' => 'èƒœ',
  'Ða' => 'èƒŠ',
  'Ðb' => 'èƒ•',
  'Ðc' => 'èƒ‰',
  'Ðd' => 'èƒ�',
  'Ðe' => 'èƒ—',
  'Ðf' => 'èƒ¦',
  'Ðg' => 'èƒ�',
  'Ðh' => 'è‡¿',
  'Ði' => 'èˆ¡',
  'Ðj' => 'èŠ”',
  'Ðk' => 'è‹™',
  'Ðl' => 'è‹¾',
  'Ðm' => 'è‹¹',
  'Ðn' => 'èŒ‡',
  'Ðo' => 'è‹¨',
  'Ðp' => 'èŒ€',
  'Ðq' => 'è‹•',
  'Ðr' => 'èŒº',
  'Ðs' => 'è‹«',
  'Ðt' => 'è‹–',
  'Ðu' => 'è‹´',
  'Ðv' => 'è‹¬',
  'Ðw' => 'è‹¡',
  'Ðx' => 'è‹²',
  'Ðy' => 'è‹µ',
  'Ðz' => 'èŒŒ',
  'Ð{' => 'è‹»',
  'Ð|' => 'è‹¶',
  'Ð}' => 'è‹°',
  'Ð~' => 'è‹ª',
  'Ð¡' => 'è‹¤',
  'Ð¢' => 'è‹ ',
  'Ð£' => 'è‹º',
  'Ð¤' => 'è‹³',
  'Ð¥' => 'è‹­',
  'Ð¦' => 'è™·',
  'Ð§' => 'è™´',
  'Ð¨' => 'è™¼',
  'Ð©' => 'è™³',
  'Ðª' => 'è¡�',
  'Ð«' => 'è¡Ž',
  'Ð¬' => 'è¡§',
  'Ð­' => 'è¡ª',
  'Ð®' => 'è¡©',
  'Ð¯' => 'è§“',
  'Ð°' => 'è¨„',
  'Ð±' => 'è¨‡',
  'Ð²' => 'èµ²',
  'Ð³' => 'è¿£',
  'Ð´' => 'è¿¡',
  'Ðµ' => 'è¿®',
  'Ð¶' => 'è¿ ',
  'Ð·' => 'éƒ±',
  'Ð¸' => 'é‚½',
  'Ð¹' => 'é‚¿',
  'Ðº' => 'éƒ•',
  'Ð»' => 'éƒ…',
  'Ð¼' => 'é‚¾',
  'Ð½' => 'éƒ‡',
  'Ð¾' => 'éƒ‹',
  'Ð¿' => 'éƒˆ',
  'ÐÀ' => 'é‡”',
  'ÐÁ' => 'é‡“',
  'ÐÂ' => 'é™”',
  'ÐÃ' => 'é™�',
  'ÐÄ' => 'é™‘',
  'ÐÅ' => 'é™“',
  'ÐÆ' => 'é™Š',
  'ÐÇ' => 'é™Ž',
  'ÐÈ' => 'å€ž',
  'ÐÉ' => 'å€…',
  'ÐÊ' => 'å€‡',
  'ÐË' => 'å€“',
  'ÐÌ' => 'å€¢',
  'ÐÍ' => 'å€°',
  'ÐÎ' => 'å€›',
  'ÐÏ' => 'ä¿µ',
  'ÐÐ' => 'ä¿´',
  'ÐÑ' => 'å€³',
  'ÐÒ' => 'å€·',
  'ÐÓ' => 'å€¬',
  'ÐÔ' => 'ä¿¶',
  'ÐÕ' => 'ä¿·',
  'ÐÖ' => 'å€—',
  'Ð×' => 'å€œ',
  'ÐØ' => 'å€ ',
  'ÐÙ' => 'å€§',
  'ÐÚ' => 'å€µ',
  'ÐÛ' => 'å€¯',
  'ÐÜ' => 'å€±',
  'ÐÝ' => 'å€Ž',
  'ÐÞ' => 'å…š',
  'Ðß' => 'å†”',
  'Ðà' => 'å†“',
  'Ðá' => 'å‡Š',
  'Ðâ' => 'å‡„',
  'Ðã' => 'å‡…',
  'Ðä' => 'å‡ˆ',
  'Ðå' => 'å‡Ž',
  'Ðæ' => 'å‰¡',
  'Ðç' => 'å‰š',
  'Ðè' => 'å‰’',
  'Ðé' => 'å‰ž',
  'Ðê' => 'å‰Ÿ',
  'Ðë' => 'å‰•',
  'Ðì' => 'å‰¢',
  'Ðí' => 'å‹�',
  'Ðî' => 'åŒŽ',
  'Ðï' => 'åŽž',
  'Ðð' => 'å”¦',
  'Ðñ' => 'å“¢',
  'Ðò' => 'å”—',
  'Ðó' => 'å”’',
  'Ðô' => 'å“§',
  'Ðõ' => 'å“³',
  'Ðö' => 'å“¤',
  'Ð÷' => 'å”š',
  'Ðø' => 'å“¿',
  'Ðù' => 'å”„',
  'Ðú' => 'å”ˆ',
  'Ðû' => 'å“«',
  'Ðü' => 'å”‘',
  'Ðý' => 'å”…',
  'Ðþ' => 'å“±',
  'Ñ@' => 'å”Š',
  'ÑA' => 'å“»',
  'ÑB' => 'å“·',
  'ÑC' => 'å“¸',
  'ÑD' => 'å“ ',
  'ÑE' => 'å”Ž',
  'ÑF' => 'å”ƒ',
  'ÑG' => 'å”‹',
  'ÑH' => 'åœ�',
  'ÑI' => 'åœ‚',
  'ÑJ' => 'åŸŒ',
  'ÑK' => 'å ²',
  'ÑL' => 'åŸ•',
  'ÑM' => 'åŸ’',
  'ÑN' => 'åžº',
  'ÑO' => 'åŸ†',
  'ÑP' => 'åž½',
  'ÑQ' => 'åž¼',
  'ÑR' => 'åž¸',
  'ÑS' => 'åž¶',
  'ÑT' => 'åž¿',
  'ÑU' => 'åŸ‡',
  'ÑV' => 'åŸ�',
  'ÑW' => 'åž¹',
  'ÑX' => 'åŸ�',
  'ÑY' => 'å¤Ž',
  'ÑZ' => 'å¥Š',
  'Ñ[' => 'å¨™',
  'Ñ\\' => 'å¨–',
  'Ñ]' => 'å¨­',
  'Ñ^' => 'å¨®',
  'Ñ_' => 'å¨•',
  'Ñ`' => 'å¨�',
  'Ña' => 'å¨—',
  'Ñb' => 'å¨Š',
  'Ñc' => 'å¨ž',
  'Ñd' => 'å¨³',
  'Ñe' => 'å­¬',
  'Ñf' => 'å®§',
  'Ñg' => 'å®­',
  'Ñh' => 'å®¬',
  'Ñi' => 'å°ƒ',
  'Ñj' => 'å±–',
  'Ñk' => 'å±”',
  'Ñl' => 'å³¬',
  'Ñm' => 'å³¿',
  'Ñn' => 'å³®',
  'Ño' => 'å³±',
  'Ñp' => 'å³·',
  'Ñq' => 'å´€',
  'Ñr' => 'å³¹',
  'Ñs' => 'å¸©',
  'Ñt' => 'å¸¨',
  'Ñu' => 'åº¨',
  'Ñv' => 'åº®',
  'Ñw' => 'åºª',
  'Ñx' => 'åº¬',
  'Ñy' => 'å¼³',
  'Ñz' => 'å¼°',
  'Ñ{' => 'å½§',
  'Ñ|' => 'æ��',
  'Ñ}' => 'æ�š',
  'Ñ~' => 'æ�§',
  'Ñ¡' => 'æ��',
  'Ñ¢' => 'æ‚¢',
  'Ñ£' => 'æ‚ˆ',
  'Ñ¤' => 'æ‚€',
  'Ñ¥' => 'æ‚’',
  'Ñ¦' => 'æ‚�',
  'Ñ§' => 'æ‚�',
  'Ñ¨' => 'æ‚ƒ',
  'Ñ©' => 'æ‚•',
  'Ñª' => 'æ‚›',
  'Ñ«' => 'æ‚—',
  'Ñ¬' => 'æ‚‡',
  'Ñ­' => 'æ‚œ',
  'Ñ®' => 'æ‚Ž',
  'Ñ¯' => 'æˆ™',
  'Ñ°' => 'æ‰†',
  'Ñ±' => 'æ‹²',
  'Ñ²' => 'æŒ�',
  'Ñ³' => 'æ�–',
  'Ñ´' => 'æŒ¬',
  'Ñµ' => 'æ�„',
  'Ñ¶' => 'æ�…',
  'Ñ·' => 'æŒ¶',
  'Ñ¸' => 'æ�ƒ',
  'Ñ¹' => 'æ�¤',
  'Ñº' => 'æŒ¹',
  'Ñ»' => 'æ�‹',
  'Ñ¼' => 'æ�Š',
  'Ñ½' => 'æŒ¼',
  'Ñ¾' => 'æŒ©',
  'Ñ¿' => 'æ��',
  'ÑÀ' => 'æŒ´',
  'ÑÁ' => 'æ�˜',
  'ÑÂ' => 'æ�”',
  'ÑÃ' => 'æ�™',
  'ÑÄ' => 'æŒ­',
  'ÑÅ' => 'æ�‡',
  'ÑÆ' => 'æŒ³',
  'ÑÇ' => 'æ�š',
  'ÑÈ' => 'æ�‘',
  'ÑÉ' => 'æŒ¸',
  'ÑÊ' => 'æ�—',
  'ÑË' => 'æ�€',
  'ÑÌ' => 'æ�ˆ',
  'ÑÍ' => 'æ•Š',
  'ÑÎ' => 'æ•†',
  'ÑÏ' => 'æ—†',
  'ÑÐ' => 'æ—ƒ',
  'ÑÑ' => 'æ—„',
  'ÑÒ' => 'æ—‚',
  'ÑÓ' => 'æ™Š',
  'ÑÔ' => 'æ™Ÿ',
  'ÑÕ' => 'æ™‡',
  'ÑÖ' => 'æ™‘',
  'Ñ×' => 'æœ’',
  'ÑØ' => 'æœ“',
  'ÑÙ' => 'æ Ÿ',
  'ÑÚ' => 'æ š',
  'ÑÛ' => 'æ¡‰',
  'ÑÜ' => 'æ ²',
  'ÑÝ' => 'æ ³',
  'ÑÞ' => 'æ »',
  'Ñß' => 'æ¡‹',
  'Ñà' => 'æ¡�',
  'Ñá' => 'æ –',
  'Ñâ' => 'æ ±',
  'Ñã' => 'æ œ',
  'Ñä' => 'æ µ',
  'Ñå' => 'æ «',
  'Ñæ' => 'æ ­',
  'Ñç' => 'æ ¯',
  'Ñè' => 'æ¡Ž',
  'Ñé' => 'æ¡„',
  'Ñê' => 'æ ´',
  'Ñë' => 'æ �',
  'Ñì' => 'æ ’',
  'Ñí' => 'æ ”',
  'Ñî' => 'æ ¦',
  'Ñï' => 'æ ¨',
  'Ñð' => 'æ ®',
  'Ññ' => 'æ¡�',
  'Ñò' => 'æ º',
  'Ñó' => 'æ ¥',
  'Ñô' => 'æ  ',
  'Ñõ' => 'æ¬¬',
  'Ñö' => 'æ¬¯',
  'Ñ÷' => 'æ¬­',
  'Ñø' => 'æ¬±',
  'Ñù' => 'æ¬´',
  'Ñú' => 'æ­­',
  'Ñû' => 'è‚‚',
  'Ñü' => 'æ®ˆ',
  'Ñý' => 'æ¯¦',
  'Ñþ' => 'æ¯¤',
  'Ò@' => 'æ¯¨',
  'ÒA' => 'æ¯£',
  'ÒB' => 'æ¯¢',
  'ÒC' => 'æ¯§',
  'ÒD' => 'æ°¥',
  'ÒE' => 'æµº',
  'ÒF' => 'æµ£',
  'ÒG' => 'æµ¤',
  'ÒH' => 'æµ¶',
  'ÒI' => 'æ´�',
  'ÒJ' => 'æµ¡',
  'ÒK' => 'æ¶’',
  'ÒL' => 'æµ˜',
  'ÒM' => 'æµ¢',
  'ÒN' => 'æµ­',
  'ÒO' => 'æµ¯',
  'ÒP' => 'æ¶‘',
  'ÒQ' => 'æ¶�',
  'ÒR' => 'æ·¯',
  'ÒS' => 'æµ¿',
  'ÒT' => 'æ¶†',
  'ÒU' => 'æµž',
  'ÒV' => 'æµ§',
  'ÒW' => 'æµ ',
  'ÒX' => 'æ¶—',
  'ÒY' => 'æµ°',
  'ÒZ' => 'æµ¼',
  'Ò[' => 'æµŸ',
  'Ò\\' => 'æ¶‚',
  'Ò]' => 'æ¶˜',
  'Ò^' => 'æ´¯',
  'Ò_' => 'æµ¨',
  'Ò`' => 'æ¶‹',
  'Òa' => 'æµ¾',
  'Òb' => 'æ¶€',
  'Òc' => 'æ¶„',
  'Òd' => 'æ´–',
  'Òe' => 'æ¶ƒ',
  'Òf' => 'æµ»',
  'Òg' => 'æµ½',
  'Òh' => 'æµµ',
  'Òi' => 'æ¶�',
  'Òj' => 'çƒœ',
  'Òk' => 'çƒ“',
  'Òl' => 'çƒ‘',
  'Òm' => 'çƒ�',
  'Òn' => 'çƒ‹',
  'Òo' => 'ç¼¹',
  'Òp' => 'çƒ¢',
  'Òq' => 'çƒ—',
  'Òr' => 'çƒ’',
  'Òs' => 'çƒž',
  'Òt' => 'çƒ ',
  'Òu' => 'çƒ”',
  'Òv' => 'çƒ�',
  'Òw' => 'çƒ…',
  'Òx' => 'çƒ†',
  'Òy' => 'çƒ‡',
  'Òz' => 'çƒš',
  'Ò{' => 'çƒŽ',
  'Ò|' => 'çƒ¡',
  'Ò}' => 'ç‰‚',
  'Ò~' => 'ç‰¸',
  'Ò¡' => 'ç‰·',
  'Ò¢' => 'ç‰¶',
  'Ò£' => 'çŒ€',
  'Ò¤' => 'ç‹º',
  'Ò¥' => 'ç‹´',
  'Ò¦' => 'ç‹¾',
  'Ò§' => 'ç‹¶',
  'Ò¨' => 'ç‹³',
  'Ò©' => 'ç‹»',
  'Òª' => 'çŒ�',
  'Ò«' => 'ç�“',
  'Ò¬' => 'ç�™',
  'Ò­' => 'ç�¥',
  'Ò®' => 'ç�–',
  'Ò¯' => 'çŽ¼',
  'Ò°' => 'ç�§',
  'Ò±' => 'ç�£',
  'Ò²' => 'ç�©',
  'Ò³' => 'ç�œ',
  'Ò´' => 'ç�’',
  'Òµ' => 'ç�›',
  'Ò¶' => 'ç�”',
  'Ò·' => 'ç��',
  'Ò¸' => 'ç�š',
  'Ò¹' => 'ç�—',
  'Òº' => 'ç�˜',
  'Ò»' => 'ç�¨',
  'Ò¼' => 'ç“ž',
  'Ò½' => 'ç“Ÿ',
  'Ò¾' => 'ç“´',
  'Ò¿' => 'ç“µ',
  'ÒÀ' => 'ç”¡',
  'ÒÁ' => 'ç•›',
  'ÒÂ' => 'ç•Ÿ',
  'ÒÃ' => 'ç–°',
  'ÒÄ' => 'ç—�',
  'ÒÅ' => 'ç–»',
  'ÒÆ' => 'ç—„',
  'ÒÇ' => 'ç—€',
  'ÒÈ' => 'ç–¿',
  'ÒÉ' => 'ç–¶',
  'ÒÊ' => 'ç–º',
  'ÒË' => 'çšŠ',
  'ÒÌ' => 'ç›‰',
  'ÒÍ' => 'çœ�',
  'ÒÎ' => 'çœ›',
  'ÒÏ' => 'çœ�',
  'ÒÐ' => 'çœ“',
  'ÒÑ' => 'çœ’',
  'ÒÒ' => 'çœ£',
  'ÒÓ' => 'çœ‘',
  'ÒÔ' => 'çœ•',
  'ÒÕ' => 'çœ™',
  'ÒÖ' => 'çœš',
  'Ò×' => 'çœ¢',
  'ÒØ' => 'çœ§',
  'ÒÙ' => 'ç £',
  'ÒÚ' => 'ç ¬',
  'ÒÛ' => 'ç ¢',
  'ÒÜ' => 'ç µ',
  'ÒÝ' => 'ç ¯',
  'ÒÞ' => 'ç ¨',
  'Òß' => 'ç ®',
  'Òà' => 'ç «',
  'Òá' => 'ç ¡',
  'Òâ' => 'ç ©',
  'Òã' => 'ç ³',
  'Òä' => 'ç ª',
  'Òå' => 'ç ±',
  'Òæ' => 'ç¥”',
  'Òç' => 'ç¥›',
  'Òè' => 'ç¥�',
  'Òé' => 'ç¥œ',
  'Òê' => 'ç¥“',
  'Òë' => 'ç¥’',
  'Òì' => 'ç¥‘',
  'Òí' => 'ç§«',
  'Òî' => 'ç§¬',
  'Òï' => 'ç§ ',
  'Òð' => 'ç§®',
  'Òñ' => 'ç§­',
  'Òò' => 'ç§ª',
  'Òó' => 'ç§œ',
  'Òô' => 'ç§ž',
  'Òõ' => 'ç§�',
  'Òö' => 'çª†',
  'Ò÷' => 'çª‰',
  'Òø' => 'çª…',
  'Òù' => 'çª‹',
  'Òú' => 'çªŒ',
  'Òû' => 'çªŠ',
  'Òü' => 'çª‡',
  'Òý' => 'ç«˜',
  'Òþ' => 'ç¬�',
  'Ó@' => 'ç¬„',
  'ÓA' => 'ç¬“',
  'ÓB' => 'ç¬…',
  'ÓC' => 'ç¬�',
  'ÓD' => 'ç¬ˆ',
  'ÓE' => 'ç¬Š',
  'ÓF' => 'ç¬Ž',
  'ÓG' => 'ç¬‰',
  'ÓH' => 'ç¬’',
  'ÓI' => 'ç²„',
  'ÓJ' => 'ç²‘',
  'ÓK' => 'ç²Š',
  'ÓL' => 'ç²Œ',
  'ÓM' => 'ç²ˆ',
  'ÓN' => 'ç²�',
  'ÓO' => 'ç²…',
  'ÓP' => 'ç´ž',
  'ÓQ' => 'ç´�',
  'ÓR' => 'ç´‘',
  'ÓS' => 'ç´Ž',
  'ÓT' => 'ç´˜',
  'ÓU' => 'ç´–',
  'ÓV' => 'ç´“',
  'ÓW' => 'ç´Ÿ',
  'ÓX' => 'ç´’',
  'ÓY' => 'ç´�',
  'ÓZ' => 'ç´Œ',
  'Ó[' => 'ç½œ',
  'Ó\\' => 'ç½¡',
  'Ó]' => 'ç½ž',
  'Ó^' => 'ç½ ',
  'Ó_' => 'ç½�',
  'Ó`' => 'ç½›',
  'Óa' => 'ç¾–',
  'Ób' => 'ç¾’',
  'Óc' => 'ç¿ƒ',
  'Ód' => 'ç¿‚',
  'Óe' => 'ç¿€',
  'Óf' => 'è€–',
  'Óg' => 'è€¾',
  'Óh' => 'è€¹',
  'Ói' => 'èƒº',
  'Ój' => 'èƒ²',
  'Ók' => 'èƒ¹',
  'Ól' => 'èƒµ',
  'Óm' => 'è„�',
  'Ón' => 'èƒ»',
  'Óo' => 'è„€',
  'Óp' => 'èˆ�',
  'Óq' => 'èˆ¯',
  'Ór' => 'èˆ¥',
  'Ós' => 'èŒ³',
  'Ót' => 'èŒ­',
  'Óu' => 'è�„',
  'Óv' => 'èŒ™',
  'Ów' => 'è�‘',
  'Óx' => 'èŒ¥',
  'Óy' => 'è�–',
  'Óz' => 'èŒ¿',
  'Ó{' => 'è��',
  'Ó|' => 'èŒ¦',
  'Ó}' => 'èŒœ',
  'Ó~' => 'èŒ¢',
  'Ó¡' => 'è�‚',
  'Ó¢' => 'è�Ž',
  'Ó£' => 'èŒ›',
  'Ó¤' => 'èŒª',
  'Ó¥' => 'èŒˆ',
  'Ó¦' => 'èŒ¼',
  'Ó§' => 'è��',
  'Ó¨' => 'èŒ–',
  'Ó©' => 'èŒ¤',
  'Óª' => 'èŒ ',
  'Ó«' => 'èŒ·',
  'Ó¬' => 'èŒ¯',
  'Ó­' => 'èŒ©',
  'Ó®' => 'è�‡',
  'Ó¯' => 'è�…',
  'Ó°' => 'è�Œ',
  'Ó±' => 'è�“',
  'Ó²' => 'èŒž',
  'Ó³' => 'èŒ¬',
  'Ó´' => 'è�‹',
  'Óµ' => 'èŒ§',
  'Ó¶' => 'è�ˆ',
  'Ó·' => 'è™“',
  'Ó¸' => 'è™’',
  'Ó¹' => 'èš¢',
  'Óº' => 'èš¨',
  'Ó»' => 'èš–',
  'Ó¼' => 'èš�',
  'Ó½' => 'èš‘',
  'Ó¾' => 'èšž',
  'Ó¿' => 'èš‡',
  'ÓÀ' => 'èš—',
  'ÓÁ' => 'èš†',
  'ÓÂ' => 'èš‹',
  'ÓÃ' => 'èšš',
  'ÓÄ' => 'èš…',
  'ÓÅ' => 'èš¥',
  'ÓÆ' => 'èš™',
  'ÓÇ' => 'èš¡',
  'ÓÈ' => 'èš§',
  'ÓÉ' => 'èš•',
  'ÓÊ' => 'èš˜',
  'ÓË' => 'èšŽ',
  'ÓÌ' => 'èš�',
  'ÓÍ' => 'èš�',
  'ÓÎ' => 'èš”',
  'ÓÏ' => 'è¡ƒ',
  'ÓÐ' => 'è¡„',
  'ÓÑ' => 'è¡­',
  'ÓÒ' => 'è¡µ',
  'ÓÓ' => 'è¡¶',
  'ÓÔ' => 'è¡²',
  'ÓÕ' => 'è¢€',
  'ÓÖ' => 'è¡±',
  'Ó×' => 'è¡¿',
  'ÓØ' => 'è¡¯',
  'ÓÙ' => 'è¢ƒ',
  'ÓÚ' => 'è¡¾',
  'ÓÛ' => 'è¡´',
  'ÓÜ' => 'è¡¼',
  'ÓÝ' => 'è¨’',
  'ÓÞ' => 'è±‡',
  'Óß' => 'è±—',
  'Óà' => 'è±»',
  'Óá' => 'è²¤',
  'Óâ' => 'è²£',
  'Óã' => 'èµ¶',
  'Óä' => 'èµ¸',
  'Óå' => 'è¶µ',
  'Óæ' => 'è¶·',
  'Óç' => 'è¶¶',
  'Óè' => 'è»‘',
  'Óé' => 'è»“',
  'Óê' => 'è¿¾',
  'Óë' => 'è¿µ',
  'Óì' => 'é€‚',
  'Óí' => 'è¿¿',
  'Óî' => 'è¿»',
  'Óï' => 'é€„',
  'Óð' => 'è¿¼',
  'Óñ' => 'è¿¶',
  'Óò' => 'éƒ–',
  'Óó' => 'éƒ ',
  'Óô' => 'éƒ™',
  'Óõ' => 'éƒš',
  'Óö' => 'éƒ£',
  'Ó÷' => 'éƒŸ',
  'Óø' => 'éƒ¥',
  'Óù' => 'éƒ˜',
  'Óú' => 'éƒ›',
  'Óû' => 'éƒ—',
  'Óü' => 'éƒœ',
  'Óý' => 'éƒ¤',
  'Óþ' => 'é…�',
  'Ô@' => 'é…Ž',
  'ÔA' => 'é…�',
  'ÔB' => 'é‡•',
  'ÔC' => 'é‡¢',
  'ÔD' => 'é‡š',
  'ÔE' => 'é™œ',
  'ÔF' => 'é™Ÿ',
  'ÔG' => 'éš¼',
  'ÔH' => 'é££',
  'ÔI' => 'é«Ÿ',
  'ÔJ' => 'é¬¯',
  'ÔK' => 'ä¹¿',
  'ÔL' => 'å�°',
  'ÔM' => 'å�ª',
  'ÔN' => 'å�¡',
  'ÔO' => 'å�ž',
  'ÔP' => 'å� ',
  'ÔQ' => 'å�“',
  'ÔR' => 'å�‹',
  'ÔS' => 'å��',
  'ÔT' => 'å�²',
  'ÔU' => 'å�ˆ',
  'ÔV' => 'å��',
  'ÔW' => 'å��',
  'ÔX' => 'å�›',
  'ÔY' => 'å�Š',
  'ÔZ' => 'å�¢',
  'Ô[' => 'å€•',
  'Ô\\' => 'å�…',
  'Ô]' => 'å�Ÿ',
  'Ô^' => 'å�©',
  'Ô_' => 'å�«',
  'Ô`' => 'å�£',
  'Ôa' => 'å�¤',
  'Ôb' => 'å�†',
  'Ôc' => 'å�€',
  'Ôd' => 'å�®',
  'Ôe' => 'å�³',
  'Ôf' => 'å�—',
  'Ôg' => 'å�‘',
  'Ôh' => 'å‡�',
  'Ôi' => 'å‰«',
  'Ôj' => 'å‰­',
  'Ôk' => 'å‰¬',
  'Ôl' => 'å‰®',
  'Ôm' => 'å‹–',
  'Ôn' => 'å‹“',
  'Ôo' => 'åŒ­',
  'Ôp' => 'åŽœ',
  'Ôq' => 'å•µ',
  'Ôr' => 'å•¶',
  'Ôs' => 'å”¼',
  'Ôt' => 'å•�',
  'Ôu' => 'å•�',
  'Ôv' => 'å”´',
  'Ôw' => 'å”ª',
  'Ôx' => 'å•‘',
  'Ôy' => 'å•¢',
  'Ôz' => 'å”¶',
  'Ô{' => 'å”µ',
  'Ô|' => 'å”°',
  'Ô}' => 'å•’',
  'Ô~' => 'å•…',
  'Ô¡' => 'å”Œ',
  'Ô¢' => 'å”²',
  'Ô£' => 'å•¥',
  'Ô¤' => 'å•Ž',
  'Ô¥' => 'å”¹',
  'Ô¦' => 'å•ˆ',
  'Ô§' => 'å”­',
  'Ô¨' => 'å”»',
  'Ô©' => 'å•€',
  'Ôª' => 'å•‹',
  'Ô«' => 'åœŠ',
  'Ô¬' => 'åœ‡',
  'Ô­' => 'åŸ»',
  'Ô®' => 'å ”',
  'Ô¯' => 'åŸ¢',
  'Ô°' => 'åŸ¶',
  'Ô±' => 'åŸœ',
  'Ô²' => 'åŸ´',
  'Ô³' => 'å €',
  'Ô´' => 'åŸ­',
  'Ôµ' => 'åŸ½',
  'Ô¶' => 'å ˆ',
  'Ô·' => 'åŸ¸',
  'Ô¸' => 'å ‹',
  'Ô¹' => 'åŸ³',
  'Ôº' => 'åŸ�',
  'Ô»' => 'å ‡',
  'Ô¼' => 'åŸ®',
  'Ô½' => 'åŸ£',
  'Ô¾' => 'åŸ²',
  'Ô¿' => 'åŸ¥',
  'ÔÀ' => 'åŸ¬',
  'ÔÁ' => 'åŸ¡',
  'ÔÂ' => 'å Ž',
  'ÔÃ' => 'åŸ¼',
  'ÔÄ' => 'å �',
  'ÔÅ' => 'åŸ§',
  'ÔÆ' => 'å �',
  'ÔÇ' => 'å Œ',
  'ÔÈ' => 'åŸ±',
  'ÔÉ' => 'åŸ©',
  'ÔÊ' => 'åŸ°',
  'ÔË' => 'å �',
  'ÔÌ' => 'å „',
  'ÔÍ' => 'å¥œ',
  'ÔÎ' => 'å© ',
  'ÔÏ' => 'å©˜',
  'ÔÐ' => 'å©•',
  'ÔÑ' => 'å©§',
  'ÔÒ' => 'å©ž',
  'ÔÓ' => 'å¨¸',
  'ÔÔ' => 'å¨µ',
  'ÔÕ' => 'å©­',
  'ÔÖ' => 'å©�',
  'Ô×' => 'å©Ÿ',
  'ÔØ' => 'å©¥',
  'ÔÙ' => 'å©¬',
  'ÔÚ' => 'å©“',
  'ÔÛ' => 'å©¤',
  'ÔÜ' => 'å©—',
  'ÔÝ' => 'å©ƒ',
  'ÔÞ' => 'å©�',
  'Ôß' => 'å©’',
  'Ôà' => 'å©„',
  'Ôá' => 'å©›',
  'Ôâ' => 'å©ˆ',
  'Ôã' => 'åªŽ',
  'Ôä' => 'å¨¾',
  'Ôå' => 'å©�',
  'Ôæ' => 'å¨¹',
  'Ôç' => 'å©Œ',
  'Ôè' => 'å©°',
  'Ôé' => 'å©©',
  'Ôê' => 'å©‡',
  'Ôë' => 'å©‘',
  'Ôì' => 'å©–',
  'Ôí' => 'å©‚',
  'Ôî' => 'å©œ',
  'Ôï' => 'å­²',
  'Ôð' => 'å­®',
  'Ôñ' => 'å¯�',
  'Ôò' => 'å¯€',
  'Ôó' => 'å±™',
  'Ôô' => 'å´ž',
  'Ôõ' => 'å´‹',
  'Ôö' => 'å´�',
  'Ô÷' => 'å´š',
  'Ôø' => 'å´ ',
  'Ôù' => 'å´Œ',
  'Ôú' => 'å´¨',
  'Ôû' => 'å´�',
  'Ôü' => 'å´¦',
  'Ôý' => 'å´¥',
  'Ôþ' => 'å´�',
  'Õ@' => 'å´°',
  'ÕA' => 'å´’',
  'ÕB' => 'å´£',
  'ÕC' => 'å´Ÿ',
  'ÕD' => 'å´®',
  'ÕE' => 'å¸¾',
  'ÕF' => 'å¸´',
  'ÕG' => 'åº±',
  'ÕH' => 'åº´',
  'ÕI' => 'åº¹',
  'ÕJ' => 'åº²',
  'ÕK' => 'åº³',
  'ÕL' => 'å¼¶',
  'ÕM' => 'å¼¸',
  'ÕN' => 'å¾›',
  'ÕO' => 'å¾–',
  'ÕP' => 'å¾Ÿ',
  'ÕQ' => 'æ‚Š',
  'ÕR' => 'æ‚�',
  'ÕS' => 'æ‚†',
  'ÕT' => 'æ‚¾',
  'ÕU' => 'æ‚°',
  'ÕV' => 'æ‚º',
  'ÕW' => 'æƒ“',
  'ÕX' => 'æƒ”',
  'ÕY' => 'æƒ�',
  'ÕZ' => 'æƒ¤',
  'Õ[' => 'æƒ™',
  'Õ\\' => 'æƒ�',
  'Õ]' => 'æƒˆ',
  'Õ^' => 'æ‚±',
  'Õ_' => 'æƒ›',
  'Õ`' => 'æ‚·',
  'Õa' => 'æƒŠ',
  'Õb' => 'æ‚¿',
  'Õc' => 'æƒƒ',
  'Õd' => 'æƒ�',
  'Õe' => 'æƒ€',
  'Õf' => 'æŒ²',
  'Õg' => 'æ�¥',
  'Õh' => 'æŽŠ',
  'Õi' => 'æŽ‚',
  'Õj' => 'æ�½',
  'Õk' => 'æŽ½',
  'Õl' => 'æŽž',
  'Õm' => 'æŽ­',
  'Õn' => 'æŽ�',
  'Õo' => 'æŽ—',
  'Õp' => 'æŽ«',
  'Õq' => 'æŽŽ',
  'Õr' => 'æ�¯',
  'Õs' => 'æŽ‡',
  'Õt' => 'æŽ�',
  'Õu' => 'æ�®',
  'Õv' => 'æŽ¯',
  'Õw' => 'æ�µ',
  'Õx' => 'æŽœ',
  'Õy' => 'æ�­',
  'Õz' => 'æŽ®',
  'Õ{' => 'æ�¼',
  'Õ|' => 'æŽ¤',
  'Õ}' => 'æŒ»',
  'Õ~' => 'æŽŸ',
  'Õ¡' => 'æ�¸',
  'Õ¢' => 'æŽ…',
  'Õ£' => 'æŽ�',
  'Õ¤' => 'æŽ‘',
  'Õ¥' => 'æŽ�',
  'Õ¦' => 'æ�°',
  'Õ§' => 'æ•“',
  'Õ¨' => 'æ—�',
  'Õ©' => 'æ™¥',
  'Õª' => 'æ™¡',
  'Õ«' => 'æ™›',
  'Õ¬' => 'æ™™',
  'Õ­' => 'æ™œ',
  'Õ®' => 'æ™¢',
  'Õ¯' => 'æœ˜',
  'Õ°' => 'æ¡¹',
  'Õ±' => 'æ¢‡',
  'Õ²' => 'æ¢�',
  'Õ³' => 'æ¢œ',
  'Õ´' => 'æ¡­',
  'Õµ' => 'æ¡®',
  'Õ¶' => 'æ¢®',
  'Õ·' => 'æ¢«',
  'Õ¸' => 'æ¥–',
  'Õ¹' => 'æ¡¯',
  'Õº' => 'æ¢£',
  'Õ»' => 'æ¢¬',
  'Õ¼' => 'æ¢©',
  'Õ½' => 'æ¡µ',
  'Õ¾' => 'æ¡´',
  'Õ¿' => 'æ¢²',
  'ÕÀ' => 'æ¢�',
  'ÕÁ' => 'æ¡·',
  'ÕÂ' => 'æ¢’',
  'ÕÃ' => 'æ¡¼',
  'ÕÄ' => 'æ¡«',
  'ÕÅ' => 'æ¡²',
  'ÕÆ' => 'æ¢ª',
  'ÕÇ' => 'æ¢€',
  'ÕÈ' => 'æ¡±',
  'ÕÉ' => 'æ¡¾',
  'ÕÊ' => 'æ¢›',
  'ÕË' => 'æ¢–',
  'ÕÌ' => 'æ¢‹',
  'ÕÍ' => 'æ¢ ',
  'ÕÎ' => 'æ¢‰',
  'ÕÏ' => 'æ¢¤',
  'ÕÐ' => 'æ¡¸',
  'ÕÑ' => 'æ¡»',
  'ÕÒ' => 'æ¢‘',
  'ÕÓ' => 'æ¢Œ',
  'ÕÔ' => 'æ¢Š',
  'ÕÕ' => 'æ¡½',
  'ÕÖ' => 'æ¬¶',
  'Õ×' => 'æ¬³',
  'ÕØ' => 'æ¬·',
  'ÕÙ' => 'æ¬¸',
  'ÕÚ' => 'æ®‘',
  'ÕÛ' => 'æ®�',
  'ÕÜ' => 'æ®�',
  'ÕÝ' => 'æ®Ž',
  'ÕÞ' => 'æ®Œ',
  'Õß' => 'æ°ª',
  'Õà' => 'æ·€',
  'Õá' => 'æ¶«',
  'Õâ' => 'æ¶´',
  'Õã' => 'æ¶³',
  'Õä' => 'æ¹´',
  'Õå' => 'æ¶¬',
  'Õæ' => 'æ·©',
  'Õç' => 'æ·¢',
  'Õè' => 'æ¶·',
  'Õé' => 'æ·¶',
  'Õê' => 'æ·”',
  'Õë' => 'æ¸€',
  'Õì' => 'æ·ˆ',
  'Õí' => 'æ· ',
  'Õî' => 'æ·Ÿ',
  'Õï' => 'æ·–',
  'Õð' => 'æ¶¾',
  'Õñ' => 'æ·¥',
  'Õò' => 'æ·œ',
  'Õó' => 'æ·�',
  'Õô' => 'æ·›',
  'Õõ' => 'æ·´',
  'Õö' => 'æ·Š',
  'Õ÷' => 'æ¶½',
  'Õø' => 'æ·­',
  'Õù' => 'æ·°',
  'Õú' => 'æ¶º',
  'Õû' => 'æ·•',
  'Õü' => 'æ·‚',
  'Õý' => 'æ·�',
  'Õþ' => 'æ·‰',
  'Ö@' => 'æ·�',
  'ÖA' => 'æ·²',
  'ÖB' => 'æ·“',
  'ÖC' => 'æ·½',
  'ÖD' => 'æ·—',
  'ÖE' => 'æ·�',
  'ÖF' => 'æ·£',
  'ÖG' => 'æ¶»',
  'ÖH' => 'çƒº',
  'ÖI' => 'ç„�',
  'ÖJ' => 'çƒ·',
  'ÖK' => 'ç„—',
  'ÖL' => 'çƒ´',
  'ÖM' => 'ç„Œ',
  'ÖN' => 'çƒ°',
  'ÖO' => 'ç„„',
  'ÖP' => 'çƒ³',
  'ÖQ' => 'ç„�',
  'ÖR' => 'çƒ¼',
  'ÖS' => 'çƒ¿',
  'ÖT' => 'ç„†',
  'ÖU' => 'ç„“',
  'ÖV' => 'ç„€',
  'ÖW' => 'çƒ¸',
  'ÖX' => 'çƒ¶',
  'ÖY' => 'ç„‹',
  'ÖZ' => 'ç„‚',
  'Ö[' => 'ç„Ž',
  'Ö\\' => 'ç‰¾',
  'Ö]' => 'ç‰»',
  'Ö^' => 'ç‰¼',
  'Ö_' => 'ç‰¿',
  'Ö`' => 'çŒ�',
  'Öa' => 'çŒ—',
  'Öb' => 'çŒ‡',
  'Öc' => 'çŒ‘',
  'Öd' => 'çŒ˜',
  'Öe' => 'çŒŠ',
  'Öf' => 'çŒˆ',
  'Ög' => 'ç‹¿',
  'Öh' => 'çŒ�',
  'Öi' => 'çŒž',
  'Öj' => 'çŽˆ',
  'Ök' => 'ç�¶',
  'Öl' => 'ç�¸',
  'Öm' => 'ç�µ',
  'Ön' => 'ç�„',
  'Öo' => 'ç��',
  'Öp' => 'ç�½',
  'Öq' => 'ç�‡',
  'Ör' => 'ç�€',
  'Ös' => 'ç�º',
  'Öt' => 'ç�¼',
  'Öu' => 'ç�¿',
  'Öv' => 'ç�Œ',
  'Öw' => 'ç�‹',
  'Öx' => 'ç�´',
  'Öy' => 'ç�ˆ',
  'Öz' => 'ç•¤',
  'Ö{' => 'ç•£',
  'Ö|' => 'ç—Ž',
  'Ö}' => 'ç—’',
  'Ö~' => 'ç—�',
  'Ö¡' => 'ç—‹',
  'Ö¢' => 'ç—Œ',
  'Ö£' => 'ç—‘',
  'Ö¤' => 'ç—�',
  'Ö¥' => 'çš�',
  'Ö¦' => 'çš‰',
  'Ö§' => 'ç›“',
  'Ö¨' => 'çœ¹',
  'Ö©' => 'çœ¯',
  'Öª' => 'çœ­',
  'Ö«' => 'çœ±',
  'Ö¬' => 'çœ²',
  'Ö­' => 'çœ´',
  'Ö®' => 'çœ³',
  'Ö¯' => 'çœ½',
  'Ö°' => 'çœ¥',
  'Ö±' => 'çœ»',
  'Ö²' => 'çœµ',
  'Ö³' => 'ç¡ˆ',
  'Ö´' => 'ç¡’',
  'Öµ' => 'ç¡‰',
  'Ö¶' => 'ç¡�',
  'Ö·' => 'ç¡Š',
  'Ö¸' => 'ç¡Œ',
  'Ö¹' => 'ç ¦',
  'Öº' => 'ç¡…',
  'Ö»' => 'ç¡�',
  'Ö¼' => 'ç¥¤',
  'Ö½' => 'ç¥§',
  'Ö¾' => 'ç¥©',
  'Ö¿' => 'ç¥ª',
  'ÖÀ' => 'ç¥£',
  'ÖÁ' => 'ç¥«',
  'ÖÂ' => 'ç¥¡',
  'ÖÃ' => 'ç¦»',
  'ÖÄ' => 'ç§º',
  'ÖÅ' => 'ç§¸',
  'ÖÆ' => 'ç§¶',
  'ÖÇ' => 'ç§·',
  'ÖÈ' => 'çª�',
  'ÖÉ' => 'çª”',
  'ÖÊ' => 'çª�',
  'ÖË' => 'ç¬µ',
  'ÖÌ' => 'ç­‡',
  'ÖÍ' => 'ç¬´',
  'ÖÎ' => 'ç¬¥',
  'ÖÏ' => 'ç¬°',
  'ÖÐ' => 'ç¬¢',
  'ÖÑ' => 'ç¬¤',
  'ÖÒ' => 'ç¬³',
  'ÖÓ' => 'ç¬˜',
  'ÖÔ' => 'ç¬ª',
  'ÖÕ' => 'ç¬�',
  'ÖÖ' => 'ç¬±',
  'Ö×' => 'ç¬«',
  'ÖØ' => 'ç¬­',
  'ÖÙ' => 'ç¬¯',
  'ÖÚ' => 'ç¬²',
  'ÖÛ' => 'ç¬¸',
  'ÖÜ' => 'ç¬š',
  'ÖÝ' => 'ç¬£',
  'ÖÞ' => 'ç²”',
  'Öß' => 'ç²˜',
  'Öà' => 'ç²–',
  'Öá' => 'ç²£',
  'Öâ' => 'ç´µ',
  'Öã' => 'ç´½',
  'Öä' => 'ç´¸',
  'Öå' => 'ç´¶',
  'Öæ' => 'ç´º',
  'Öç' => 'çµ…',
  'Öè' => 'ç´¬',
  'Öé' => 'ç´©',
  'Öê' => 'çµ�',
  'Öë' => 'çµ‡',
  'Öì' => 'ç´¾',
  'Öí' => 'ç´¿',
  'Öî' => 'çµŠ',
  'Öï' => 'ç´»',
  'Öð' => 'ç´¨',
  'Öñ' => 'ç½£',
  'Öò' => 'ç¾•',
  'Öó' => 'ç¾œ',
  'Öô' => 'ç¾�',
  'Öõ' => 'ç¾›',
  'Öö' => 'ç¿Š',
  'Ö÷' => 'ç¿‹',
  'Öø' => 'ç¿�',
  'Öù' => 'ç¿�',
  'Öú' => 'ç¿‘',
  'Öû' => 'ç¿‡',
  'Öü' => 'ç¿�',
  'Öý' => 'ç¿‰',
  'Öþ' => 'è€Ÿ',
  '×@' => 'è€ž',
  '×A' => 'è€›',
  '×B' => 'è�‡',
  '×C' => 'è�ƒ',
  '×D' => 'è�ˆ',
  '×E' => 'è„˜',
  '×F' => 'è„¥',
  '×G' => 'è„™',
  '×H' => 'è„›',
  '×I' => 'è„­',
  '×J' => 'è„Ÿ',
  '×K' => 'è„¬',
  '×L' => 'è„ž',
  '×M' => 'è„¡',
  '×N' => 'è„•',
  '×O' => 'è„§',
  '×P' => 'è„�',
  '×Q' => 'è„¢',
  '×R' => 'èˆ‘',
  '×S' => 'èˆ¸',
  '×T' => 'èˆ³',
  '×U' => 'èˆº',
  '×V' => 'èˆ´',
  '×W' => 'èˆ²',
  '×X' => 'è‰´',
  '×Y' => 'èŽ�',
  '×Z' => 'èŽ£',
  '×[' => 'èŽ¨',
  '×\\' => 'èŽ�',
  '×]' => 'è�º',
  '×^' => 'è�³',
  '×_' => 'èŽ¤',
  '×`' => 'è�´',
  '×a' => 'èŽ�',
  '×b' => 'èŽ�',
  '×c' => 'èŽ•',
  '×d' => 'èŽ™',
  '×e' => 'è�µ',
  '×f' => 'èŽ”',
  '×g' => 'èŽ©',
  '×h' => 'è�½',
  '×i' => 'èŽƒ',
  '×j' => 'èŽŒ',
  '×k' => 'èŽ�',
  '×l' => 'èŽ›',
  '×m' => 'èŽª',
  '×n' => 'èŽ‹',
  '×o' => 'è�¾',
  '×p' => 'èŽ¥',
  '×q' => 'èŽ¯',
  '×r' => 'èŽˆ',
  '×s' => 'èŽ—',
  '×t' => 'èŽ°',
  '×u' => 'è�¿',
  '×v' => 'èŽ¦',
  '×w' => 'èŽ‡',
  '×x' => 'èŽ®',
  '×y' => 'è�¶',
  '×z' => 'èŽš',
  '×{' => 'è™™',
  '×|' => 'è™–',
  '×}' => 'èš¿',
  '×~' => 'èš·',
  '×¡' => 'è›‚',
  '×¢' => 'è›�',
  '×£' => 'è›…',
  '×¤' => 'èšº',
  '×¥' => 'èš°',
  '×¦' => 'è›ˆ',
  '×§' => 'èš¹',
  '×¨' => 'èš³',
  '×©' => 'èš¸',
  '×ª' => 'è›Œ',
  '×«' => 'èš´',
  '×¬' => 'èš»',
  '×­' => 'èš¼',
  '×®' => 'è›ƒ',
  '×¯' => 'èš½',
  '×°' => 'èš¾',
  '×±' => 'è¡’',
  '×²' => 'è¢‰',
  '×³' => 'è¢•',
  '×´' => 'è¢¨',
  '×µ' => 'è¢¢',
  '×¶' => 'è¢ª',
  '×·' => 'è¢š',
  '×¸' => 'è¢‘',
  '×¹' => 'è¢¡',
  '×º' => 'è¢Ÿ',
  '×»' => 'è¢˜',
  '×¼' => 'è¢§',
  '×½' => 'è¢™',
  '×¾' => 'è¢›',
  '×¿' => 'è¢—',
  '×À' => 'è¢¤',
  '×Á' => 'è¢¬',
  '×Â' => 'è¢Œ',
  '×Ã' => 'è¢“',
  '×Ä' => 'è¢Ž',
  '×Å' => 'è¦‚',
  '×Æ' => 'è§–',
  '×Ç' => 'è§™',
  '×È' => 'è§•',
  '×É' => 'è¨°',
  '×Ê' => 'è¨§',
  '×Ë' => 'è¨¬',
  '×Ì' => 'è¨ž',
  '×Í' => 'è°¹',
  '×Î' => 'è°»',
  '×Ï' => 'è±œ',
  '×Ð' => 'è±�',
  '×Ñ' => 'è±½',
  '×Ò' => 'è²¥',
  '×Ó' => 'èµ½',
  '×Ô' => 'èµ»',
  '×Õ' => 'èµ¹',
  '×Ö' => 'è¶¼',
  '××' => 'è·‚',
  '×Ø' => 'è¶¹',
  '×Ù' => 'è¶¿',
  '×Ú' => 'è·�',
  '×Û' => 'è»˜',
  '×Ü' => 'è»ž',
  '×Ý' => 'è»�',
  '×Þ' => 'è»œ',
  '×ß' => 'è»—',
  '×à' => 'è» ',
  '×á' => 'è»¡',
  '×â' => 'é€¤',
  '×ã' => 'é€‹',
  '×ä' => 'é€‘',
  '×å' => 'é€œ',
  '×æ' => 'é€Œ',
  '×ç' => 'é€¡',
  '×è' => 'éƒ¯',
  '×é' => 'éƒª',
  '×ê' => 'éƒ°',
  '×ë' => 'éƒ´',
  '×ì' => 'éƒ²',
  '×í' => 'éƒ³',
  '×î' => 'éƒ”',
  '×ï' => 'éƒ«',
  '×ð' => 'éƒ¬',
  '×ñ' => 'éƒ©',
  '×ò' => 'é…–',
  '×ó' => 'é…˜',
  '×ô' => 'é…š',
  '×õ' => 'é…“',
  '×ö' => 'é…•',
  '×÷' => 'é‡¬',
  '×ø' => 'é‡´',
  '×ù' => 'é‡±',
  '×ú' => 'é‡³',
  '×û' => 'é‡¸',
  '×ü' => 'é‡¤',
  '×ý' => 'é‡¹',
  '×þ' => 'é‡ª',
  'Ø@' => 'é‡«',
  'ØA' => 'é‡·',
  'ØB' => 'é‡¨',
  'ØC' => 'é‡®',
  'ØD' => 'é•º',
  'ØE' => 'é–†',
  'ØF' => 'é–ˆ',
  'ØG' => 'é™¼',
  'ØH' => 'é™­',
  'ØI' => 'é™«',
  'ØJ' => 'é™±',
  'ØK' => 'é™¯',
  'ØL' => 'éš¿',
  'ØM' => 'é�ª',
  'ØN' => 'é „',
  'ØO' => 'é£¥',
  'ØP' => 'é¦—',
  'ØQ' => 'å‚›',
  'ØR' => 'å‚•',
  'ØS' => 'å‚”',
  'ØT' => 'å‚ž',
  'ØU' => 'å‚‹',
  'ØV' => 'å‚£',
  'ØW' => 'å‚ƒ',
  'ØX' => 'å‚Œ',
  'ØY' => 'å‚Ž',
  'ØZ' => 'å‚�',
  'Ø[' => 'å�¨',
  'Ø\\' => 'å‚œ',
  'Ø]' => 'å‚’',
  'Ø^' => 'å‚‚',
  'Ø_' => 'å‚‡',
  'Ø`' => 'å…Ÿ',
  'Øa' => 'å‡”',
  'Øb' => 'åŒ’',
  'Øc' => 'åŒ‘',
  'Ød' => 'åŽ¤',
  'Øe' => 'åŽ§',
  'Øf' => 'å–‘',
  'Øg' => 'å–¨',
  'Øh' => 'å–¥',
  'Øi' => 'å–­',
  'Øj' => 'å•·',
  'Øk' => 'å™…',
  'Øl' => 'å–¢',
  'Øm' => 'å–“',
  'Øn' => 'å–ˆ',
  'Øo' => 'å–�',
  'Øp' => 'å–µ',
  'Øq' => 'å–�',
  'Ør' => 'å–£',
  'Øs' => 'å–’',
  'Øt' => 'å–¤',
  'Øu' => 'å•½',
  'Øv' => 'å–Œ',
  'Øw' => 'å–¦',
  'Øx' => 'å•¿',
  'Øy' => 'å–•',
  'Øz' => 'å–¡',
  'Ø{' => 'å–Ž',
  'Ø|' => 'åœŒ',
  'Ø}' => 'å ©',
  'Ø~' => 'å ·',
  'Ø¡' => 'å ™',
  'Ø¢' => 'å ž',
  'Ø£' => 'å §',
  'Ø¤' => 'å £',
  'Ø¥' => 'å ¨',
  'Ø¦' => 'åŸµ',
  'Ø§' => 'å¡ˆ',
  'Ø¨' => 'å ¥',
  'Ø©' => 'å œ',
  'Øª' => 'å ›',
  'Ø«' => 'å ³',
  'Ø¬' => 'å ¿',
  'Ø­' => 'å ¶',
  'Ø®' => 'å ®',
  'Ø¯' => 'å ¹',
  'Ø°' => 'å ¸',
  'Ø±' => 'å ­',
  'Ø²' => 'å ¬',
  'Ø³' => 'å »',
  'Ø´' => 'å¥¡',
  'Øµ' => 'åª¯',
  'Ø¶' => 'åª”',
  'Ø·' => 'åªŸ',
  'Ø¸' => 'å©º',
  'Ø¹' => 'åª¢',
  'Øº' => 'åªž',
  'Ø»' => 'å©¸',
  'Ø¼' => 'åª¦',
  'Ø½' => 'å©¼',
  'Ø¾' => 'åª¥',
  'Ø¿' => 'åª¬',
  'ØÀ' => 'åª•',
  'ØÁ' => 'åª®',
  'ØÂ' => 'å¨·',
  'ØÃ' => 'åª„',
  'ØÄ' => 'åªŠ',
  'ØÅ' => 'åª—',
  'ØÆ' => 'åªƒ',
  'ØÇ' => 'åª‹',
  'ØÈ' => 'åª©',
  'ØÉ' => 'å©»',
  'ØÊ' => 'å©½',
  'ØË' => 'åªŒ',
  'ØÌ' => 'åªœ',
  'ØÍ' => 'åª�',
  'ØÎ' => 'åª“',
  'ØÏ' => 'åª�',
  'ØÐ' => 'å¯ª',
  'ØÑ' => 'å¯�',
  'ØÒ' => 'å¯‹',
  'ØÓ' => 'å¯”',
  'ØÔ' => 'å¯‘',
  'ØÕ' => 'å¯Š',
  'ØÖ' => 'å¯Ž',
  'Ø×' => 'å°Œ',
  'ØØ' => 'å°°',
  'ØÙ' => 'å´·',
  'ØÚ' => 'åµƒ',
  'ØÛ' => 'åµ«',
  'ØÜ' => 'åµ�',
  'ØÝ' => 'åµ‹',
  'ØÞ' => 'å´¿',
  'Øß' => 'å´µ',
  'Øà' => 'åµ‘',
  'Øá' => 'åµŽ',
  'Øâ' => 'åµ•',
  'Øã' => 'å´³',
  'Øä' => 'å´º',
  'Øå' => 'åµ’',
  'Øæ' => 'å´½',
  'Øç' => 'å´±',
  'Øè' => 'åµ™',
  'Øé' => 'åµ‚',
  'Øê' => 'å´¹',
  'Øë' => 'åµ‰',
  'Øì' => 'å´¸',
  'Øí' => 'å´¼',
  'Øî' => 'å´²',
  'Øï' => 'å´¶',
  'Øð' => 'åµ€',
  'Øñ' => 'åµ…',
  'Øò' => 'å¹„',
  'Øó' => 'å¹�',
  'Øô' => 'å½˜',
  'Øõ' => 'å¾¦',
  'Øö' => 'å¾¥',
  'Ø÷' => 'å¾«',
  'Øø' => 'æƒ‰',
  'Øù' => 'æ‚¹',
  'Øú' => 'æƒŒ',
  'Øû' => 'æƒ¢',
  'Øü' => 'æƒŽ',
  'Øý' => 'æƒ„',
  'Øþ' => 'æ„”',
  'Ù@' => 'æƒ²',
  'ÙA' => 'æ„Š',
  'ÙB' => 'æ„–',
  'ÙC' => 'æ„…',
  'ÙD' => 'æƒµ',
  'ÙE' => 'æ„“',
  'ÙF' => 'æƒ¸',
  'ÙG' => 'æƒ¼',
  'ÙH' => 'æƒ¾',
  'ÙI' => 'æƒ�',
  'ÙJ' => 'æ„ƒ',
  'ÙK' => 'æ„˜',
  'ÙL' => 'æ„�',
  'ÙM' => 'æ„�',
  'ÙN' => 'æƒ¿',
  'ÙO' => 'æ„„',
  'ÙP' => 'æ„‹',
  'ÙQ' => 'æ‰Š',
  'ÙR' => 'æŽ”',
  'ÙS' => 'æŽ±',
  'ÙT' => 'æŽ°',
  'ÙU' => 'æ�Ž',
  'ÙV' => 'æ�¥',
  'ÙW' => 'æ�¨',
  'ÙX' => 'æ�¯',
  'ÙY' => 'æ�ƒ',
  'ÙZ' => 'æ’�',
  'Ù[' => 'æ�³',
  'Ù\\' => 'æ�Š',
  'Ù]' => 'æ� ',
  'Ù^' => 'æ�¶',
  'Ù_' => 'æ�•',
  'Ù`' => 'æ�²',
  'Ùa' => 'æ�µ',
  'Ùb' => 'æ‘¡',
  'Ùc' => 'æ�Ÿ',
  'Ùd' => 'æŽ¾',
  'Ùe' => 'æ��',
  'Ùf' => 'æ�œ',
  'Ùg' => 'æ�„',
  'Ùh' => 'æ�˜',
  'Ùi' => 'æ�“',
  'Ùj' => 'æ�‚',
  'Ùk' => 'æ�‡',
  'Ùl' => 'æ�Œ',
  'Ùm' => 'æ�‹',
  'Ùn' => 'æ�ˆ',
  'Ùo' => 'æ�°',
  'Ùp' => 'æ�—',
  'Ùq' => 'æ�™',
  'Ùr' => 'æ”²',
  'Ùs' => 'æ•§',
  'Ùt' => 'æ•ª',
  'Ùu' => 'æ•¤',
  'Ùv' => 'æ•œ',
  'Ùw' => 'æ•¨',
  'Ùx' => 'æ•¥',
  'Ùy' => 'æ–Œ',
  'Ùz' => 'æ–�',
  'Ù{' => 'æ–ž',
  'Ù|' => 'æ–®',
  'Ù}' => 'æ—�',
  'Ù~' => 'æ—’',
  'Ù¡' => 'æ™¼',
  'Ù¢' => 'æ™¬',
  'Ù£' => 'æ™»',
  'Ù¤' => 'æš€',
  'Ù¥' => 'æ™±',
  'Ù¦' => 'æ™¹',
  'Ù§' => 'æ™ª',
  'Ù¨' => 'æ™²',
  'Ù©' => 'æœ�',
  'Ùª' => 'æ¤Œ',
  'Ù«' => 'æ£“',
  'Ù¬' => 'æ¤„',
  'Ù­' => 'æ£œ',
  'Ù®' => 'æ¤ª',
  'Ù¯' => 'æ£¬',
  'Ù°' => 'æ£ª',
  'Ù±' => 'æ£±',
  'Ù²' => 'æ¤�',
  'Ù³' => 'æ£–',
  'Ù´' => 'æ£·',
  'Ùµ' => 'æ£«',
  'Ù¶' => 'æ£¤',
  'Ù·' => 'æ£¶',
  'Ù¸' => 'æ¤“',
  'Ù¹' => 'æ¤�',
  'Ùº' => 'æ£³',
  'Ù»' => 'æ£¡',
  'Ù¼' => 'æ¤‡',
  'Ù½' => 'æ£Œ',
  'Ù¾' => 'æ¤ˆ',
  'Ù¿' => 'æ¥°',
  'ÙÀ' => 'æ¢´',
  'ÙÁ' => 'æ¤‘',
  'ÙÂ' => 'æ£¯',
  'ÙÃ' => 'æ£†',
  'ÙÄ' => 'æ¤”',
  'ÙÅ' => 'æ£¸',
  'ÙÆ' => 'æ£�',
  'ÙÇ' => 'æ£½',
  'ÙÈ' => 'æ£¼',
  'ÙÉ' => 'æ£¨',
  'ÙÊ' => 'æ¤‹',
  'ÙË' => 'æ¤Š',
  'ÙÌ' => 'æ¤—',
  'ÙÍ' => 'æ£Ž',
  'ÙÎ' => 'æ£ˆ',
  'ÙÏ' => 'æ£�',
  'ÙÐ' => 'æ£ž',
  'ÙÑ' => 'æ£¦',
  'ÙÒ' => 'æ£´',
  'ÙÓ' => 'æ£‘',
  'ÙÔ' => 'æ¤†',
  'ÙÕ' => 'æ£”',
  'ÙÖ' => 'æ£©',
  'Ù×' => 'æ¤•',
  'ÙØ' => 'æ¤¥',
  'ÙÙ' => 'æ£‡',
  'ÙÚ' => 'æ¬¹',
  'ÙÛ' => 'æ¬»',
  'ÙÜ' => 'æ¬¿',
  'ÙÝ' => 'æ¬¼',
  'ÙÞ' => 'æ®”',
  'Ùß' => 'æ®—',
  'Ùà' => 'æ®™',
  'Ùá' => 'æ®•',
  'Ùâ' => 'æ®½',
  'Ùã' => 'æ¯°',
  'Ùä' => 'æ¯²',
  'Ùå' => 'æ¯³',
  'Ùæ' => 'æ°°',
  'Ùç' => 'æ·¼',
  'Ùè' => 'æ¹†',
  'Ùé' => 'æ¹‡',
  'Ùê' => 'æ¸Ÿ',
  'Ùë' => 'æ¹‰',
  'Ùì' => 'æºˆ',
  'Ùí' => 'æ¸¼',
  'Ùî' => 'æ¸½',
  'Ùï' => 'æ¹…',
  'Ùð' => 'æ¹¢',
  'Ùñ' => 'æ¸«',
  'Ùò' => 'æ¸¿',
  'Ùó' => 'æ¹�',
  'Ùô' => 'æ¹�',
  'Ùõ' => 'æ¹³',
  'Ùö' => 'æ¸œ',
  'Ù÷' => 'æ¸³',
  'Ùø' => 'æ¹‹',
  'Ùù' => 'æ¹€',
  'Ùú' => 'æ¹‘',
  'Ùû' => 'æ¸»',
  'Ùü' => 'æ¸ƒ',
  'Ùý' => 'æ¸®',
  'Ùþ' => 'æ¹ž',
  'Ú@' => 'æ¹¨',
  'ÚA' => 'æ¹œ',
  'ÚB' => 'æ¹¡',
  'ÚC' => 'æ¸±',
  'ÚD' => 'æ¸¨',
  'ÚE' => 'æ¹ ',
  'ÚF' => 'æ¹±',
  'ÚG' => 'æ¹«',
  'ÚH' => 'æ¸¹',
  'ÚI' => 'æ¸¢',
  'ÚJ' => 'æ¸°',
  'ÚK' => 'æ¹“',
  'ÚL' => 'æ¹¥',
  'ÚM' => 'æ¸§',
  'ÚN' => 'æ¹¸',
  'ÚO' => 'æ¹¤',
  'ÚP' => 'æ¹·',
  'ÚQ' => 'æ¹•',
  'ÚR' => 'æ¹¹',
  'ÚS' => 'æ¹’',
  'ÚT' => 'æ¹¦',
  'ÚU' => 'æ¸µ',
  'ÚV' => 'æ¸¶',
  'ÚW' => 'æ¹š',
  'ÚX' => 'ç„ ',
  'ÚY' => 'ç„ž',
  'ÚZ' => 'ç„¯',
  'Ú[' => 'çƒ»',
  'Ú\\' => 'ç„®',
  'Ú]' => 'ç„±',
  'Ú^' => 'ç„£',
  'Ú_' => 'ç„¥',
  'Ú`' => 'ç„¢',
  'Úa' => 'ç„²',
  'Úb' => 'ç„Ÿ',
  'Úc' => 'ç„¨',
  'Úd' => 'ç„º',
  'Úe' => 'ç„›',
  'Úf' => 'ç‰‹',
  'Úg' => 'ç‰š',
  'Úh' => 'çŠˆ',
  'Úi' => 'çŠ‰',
  'Új' => 'çŠ†',
  'Úk' => 'çŠ…',
  'Úl' => 'çŠ‹',
  'Úm' => 'çŒ’',
  'Ún' => 'çŒ‹',
  'Úo' => 'çŒ°',
  'Úp' => 'çŒ¢',
  'Úq' => 'çŒ±',
  'Úr' => 'çŒ³',
  'Ús' => 'çŒ§',
  'Út' => 'çŒ²',
  'Úu' => 'çŒ­',
  'Úv' => 'çŒ¦',
  'Úw' => 'çŒ£',
  'Úx' => 'çŒµ',
  'Úy' => 'çŒŒ',
  'Úz' => 'ç�®',
  'Ú{' => 'ç�¬',
  'Ú|' => 'ç�°',
  'Ú}' => 'ç�«',
  'Ú~' => 'ç�–',
  'Ú¡' => 'ç�š',
  'Ú¢' => 'ç�¡',
  'Ú£' => 'ç�­',
  'Ú¤' => 'ç�±',
  'Ú¥' => 'ç�¤',
  'Ú¦' => 'ç�£',
  'Ú§' => 'ç��',
  'Ú¨' => 'ç�©',
  'Ú©' => 'ç� ',
  'Úª' => 'ç�²',
  'Ú«' => 'ç“»',
  'Ú¬' => 'ç”¯',
  'Ú­' => 'ç•¯',
  'Ú®' => 'ç•¬',
  'Ú¯' => 'ç—§',
  'Ú°' => 'ç—š',
  'Ú±' => 'ç—¡',
  'Ú²' => 'ç—¦',
  'Ú³' => 'ç—�',
  'Ú´' => 'ç—Ÿ',
  'Úµ' => 'ç—¤',
  'Ú¶' => 'ç——',
  'Ú·' => 'çš•',
  'Ú¸' => 'çš’',
  'Ú¹' => 'ç›š',
  'Úº' => 'ç�†',
  'Ú»' => 'ç�‡',
  'Ú¼' => 'ç�„',
  'Ú½' => 'ç��',
  'Ú¾' => 'ç�…',
  'Ú¿' => 'ç�Š',
  'ÚÀ' => 'ç�Ž',
  'ÚÁ' => 'ç�‹',
  'ÚÂ' => 'ç�Œ',
  'ÚÃ' => 'çŸž',
  'ÚÄ' => 'çŸ¬',
  'ÚÅ' => 'ç¡ ',
  'ÚÆ' => 'ç¡¤',
  'ÚÇ' => 'ç¡¥',
  'ÚÈ' => 'ç¡œ',
  'ÚÉ' => 'ç¡­',
  'ÚÊ' => 'ç¡±',
  'ÚË' => 'ç¡ª',
  'ÚÌ' => 'ç¡®',
  'ÚÍ' => 'ç¡°',
  'ÚÎ' => 'ç¡©',
  'ÚÏ' => 'ç¡¨',
  'ÚÐ' => 'ç¡ž',
  'ÚÑ' => 'ç¡¢',
  'ÚÒ' => 'ç¥´',
  'ÚÓ' => 'ç¥³',
  'ÚÔ' => 'ç¥²',
  'ÚÕ' => 'ç¥°',
  'ÚÖ' => 'ç¨‚',
  'Ú×' => 'ç¨Š',
  'ÚØ' => 'ç¨ƒ',
  'ÚÙ' => 'ç¨Œ',
  'ÚÚ' => 'ç¨„',
  'ÚÛ' => 'çª™',
  'ÚÜ' => 'ç«¦',
  'ÚÝ' => 'ç«¤',
  'ÚÞ' => 'ç­Š',
  'Úß' => 'ç¬»',
  'Úà' => 'ç­„',
  'Úá' => 'ç­ˆ',
  'Úâ' => 'ç­Œ',
  'Úã' => 'ç­Ž',
  'Úä' => 'ç­€',
  'Úå' => 'ç­˜',
  'Úæ' => 'ç­…',
  'Úç' => 'ç²¢',
  'Úè' => 'ç²ž',
  'Úé' => 'ç²¨',
  'Úê' => 'ç²¡',
  'Úë' => 'çµ˜',
  'Úì' => 'çµ¯',
  'Úí' => 'çµ£',
  'Úî' => 'çµ“',
  'Úï' => 'çµ–',
  'Úð' => 'çµ§',
  'Úñ' => 'çµª',
  'Úò' => 'çµ�',
  'Úó' => 'çµ­',
  'Úô' => 'çµœ',
  'Úõ' => 'çµ«',
  'Úö' => 'çµ’',
  'Ú÷' => 'çµ”',
  'Úø' => 'çµ©',
  'Úù' => 'çµ‘',
  'Úú' => 'çµŸ',
  'Úû' => 'çµŽ',
  'Úü' => 'ç¼¾',
  'Úý' => 'ç¼¿',
  'Úþ' => 'ç½¥',
  'Û@' => 'ç½¦',
  'ÛA' => 'ç¾¢',
  'ÛB' => 'ç¾ ',
  'ÛC' => 'ç¾¡',
  'ÛD' => 'ç¿—',
  'ÛE' => 'è�‘',
  'ÛF' => 'è��',
  'ÛG' => 'è��',
  'ÛH' => 'èƒ¾',
  'ÛI' => 'èƒ”',
  'ÛJ' => 'è…ƒ',
  'ÛK' => 'è…Š',
  'ÛL' => 'è…’',
  'ÛM' => 'è…�',
  'ÛN' => 'è…‡',
  'ÛO' => 'è„½',
  'ÛP' => 'è…�',
  'ÛQ' => 'è„º',
  'ÛR' => 'è‡¦',
  'ÛS' => 'è‡®',
  'ÛT' => 'è‡·',
  'ÛU' => 'è‡¸',
  'ÛV' => 'è‡¹',
  'ÛW' => 'èˆ„',
  'ÛX' => 'èˆ¼',
  'ÛY' => 'èˆ½',
  'ÛZ' => 'èˆ¿',
  'Û[' => 'è‰µ',
  'Û\\' => 'èŒ»',
  'Û]' => 'è��',
  'Û^' => 'è�¹',
  'Û_' => 'è�£',
  'Û`' => 'è�€',
  'Ûa' => 'è�¨',
  'Ûb' => 'è�’',
  'Ûc' => 'è�§',
  'Ûd' => 'è�¤',
  'Ûe' => 'è�¼',
  'Ûf' => 'è�¶',
  'Ûg' => 'è��',
  'Ûh' => 'è�†',
  'Ûi' => 'è�ˆ',
  'Ûj' => 'è�«',
  'Ûk' => 'è�£',
  'Ûl' => 'èŽ¿',
  'Ûm' => 'è��',
  'Ûn' => 'è��',
  'Ûo' => 'è�¥',
  'Ûp' => 'è�˜',
  'Ûq' => 'è�¿',
  'Ûr' => 'è�¡',
  'Ûs' => 'è�‹',
  'Ût' => 'è�Ž',
  'Ûu' => 'è�–',
  'Ûv' => 'è�µ',
  'Ûw' => 'è�‰',
  'Ûx' => 'è�‰',
  'Ûy' => 'è��',
  'Ûz' => 'è�ž',
  'Û{' => 'è�‘',
  'Û|' => 'è�†',
  'Û}' => 'è�‚',
  'Û~' => 'è�³',
  'Û¡' => 'è�•',
  'Û¢' => 'è�º',
  'Û£' => 'è�‡',
  'Û¤' => 'è�‘',
  'Û¥' => 'è�ª',
  'Û¦' => 'è�“',
  'Û§' => 'è�ƒ',
  'Û¨' => 'è�¬',
  'Û©' => 'è�®',
  'Ûª' => 'è�„',
  'Û«' => 'è�»',
  'Û¬' => 'è�—',
  'Û­' => 'è�¢',
  'Û®' => 'è�›',
  'Û¯' => 'è�›',
  'Û°' => 'è�¾',
  'Û±' => 'è›˜',
  'Û²' => 'è›¢',
  'Û³' => 'è›¦',
  'Û´' => 'è›“',
  'Ûµ' => 'è›£',
  'Û¶' => 'è›š',
  'Û·' => 'è›ª',
  'Û¸' => 'è›�',
  'Û¹' => 'è›«',
  'Ûº' => 'è›œ',
  'Û»' => 'è›¬',
  'Û¼' => 'è›©',
  'Û½' => 'è›—',
  'Û¾' => 'è›¨',
  'Û¿' => 'è›‘',
  'ÛÀ' => 'è¡ˆ',
  'ÛÁ' => 'è¡–',
  'ÛÂ' => 'è¡•',
  'ÛÃ' => 'è¢º',
  'ÛÄ' => 'è£—',
  'ÛÅ' => 'è¢¹',
  'ÛÆ' => 'è¢¸',
  'ÛÇ' => 'è£€',
  'ÛÈ' => 'è¢¾',
  'ÛÉ' => 'è¢¶',
  'ÛÊ' => 'è¢¼',
  'ÛË' => 'è¢·',
  'ÛÌ' => 'è¢½',
  'ÛÍ' => 'è¢²',
  'ÛÎ' => 'è¤�',
  'ÛÏ' => 'è£‰',
  'ÛÐ' => 'è¦•',
  'ÛÑ' => 'è¦˜',
  'ÛÒ' => 'è¦—',
  'ÛÓ' => 'è§�',
  'ÛÔ' => 'è§š',
  'ÛÕ' => 'è§›',
  'ÛÖ' => 'è©Ž',
  'Û×' => 'è©�',
  'ÛØ' => 'è¨¹',
  'ÛÙ' => 'è©™',
  'ÛÚ' => 'è©€',
  'ÛÛ' => 'è©—',
  'ÛÜ' => 'è©˜',
  'ÛÝ' => 'è©„',
  'ÛÞ' => 'è©…',
  'Ûß' => 'è©’',
  'Ûà' => 'è©ˆ',
  'Ûá' => 'è©‘',
  'Ûâ' => 'è©Š',
  'Ûã' => 'è©Œ',
  'Ûä' => 'è©�',
  'Ûå' => 'è±Ÿ',
  'Ûæ' => 'è²�',
  'Ûç' => 'è²€',
  'Ûè' => 'è²º',
  'Ûé' => 'è²¾',
  'Ûê' => 'è²°',
  'Ûë' => 'è²¹',
  'Ûì' => 'è²µ',
  'Ûí' => 'è¶„',
  'Ûî' => 'è¶€',
  'Ûï' => 'è¶‰',
  'Ûð' => 'è·˜',
  'Ûñ' => 'è·“',
  'Ûò' => 'è·�',
  'Ûó' => 'è·‡',
  'Ûô' => 'è·–',
  'Ûõ' => 'è·œ',
  'Ûö' => 'è·�',
  'Û÷' => 'è·•',
  'Ûø' => 'è·™',
  'Ûù' => 'è·ˆ',
  'Ûú' => 'è·—',
  'Ûû' => 'è·…',
  'Ûü' => 'è»¯',
  'Ûý' => 'è»·',
  'Ûþ' => 'è»º',
  'Ü@' => 'è»¹',
  'ÜA' => 'è»¦',
  'ÜB' => 'è»®',
  'ÜC' => 'è»¥',
  'ÜD' => 'è»µ',
  'ÜE' => 'è»§',
  'ÜF' => 'è»¨',
  'ÜG' => 'è»¶',
  'ÜH' => 'è»«',
  'ÜI' => 'è»±',
  'ÜJ' => 'è»¬',
  'ÜK' => 'è»´',
  'ÜL' => 'è»©',
  'ÜM' => 'é€­',
  'ÜN' => 'é€´',
  'ÜO' => 'é€¯',
  'ÜP' => 'é„†',
  'ÜQ' => 'é„¬',
  'ÜR' => 'é„„',
  'ÜS' => 'éƒ¿',
  'ÜT' => 'éƒ¼',
  'ÜU' => 'é„ˆ',
  'ÜV' => 'éƒ¹',
  'ÜW' => 'éƒ»',
  'ÜX' => 'é„�',
  'ÜY' => 'é„€',
  'ÜZ' => 'é„‡',
  'Ü[' => 'é„…',
  'Ü\\' => 'é„ƒ',
  'Ü]' => 'é…¡',
  'Ü^' => 'é…¤',
  'Ü_' => 'é…Ÿ',
  'Ü`' => 'é…¢',
  'Üa' => 'é… ',
  'Üb' => 'éˆ�',
  'Üc' => 'éˆŠ',
  'Üd' => 'éˆ¥',
  'Üe' => 'éˆƒ',
  'Üf' => 'éˆš',
  'Üg' => 'éˆ¦',
  'Üh' => 'éˆ�',
  'Üi' => 'éˆŒ',
  'Üj' => 'éˆ€',
  'Ük' => 'éˆ’',
  'Ül' => 'é‡¿',
  'Üm' => 'é‡½',
  'Ün' => 'éˆ†',
  'Üo' => 'éˆ„',
  'Üp' => 'éˆ§',
  'Üq' => 'éˆ‚',
  'Ür' => 'éˆœ',
  'Üs' => 'éˆ¤',
  'Üt' => 'éˆ™',
  'Üu' => 'éˆ—',
  'Üv' => 'éˆ…',
  'Üw' => 'éˆ–',
  'Üx' => 'é•»',
  'Üy' => 'é–�',
  'Üz' => 'é–Œ',
  'Ü{' => 'é–�',
  'Ü|' => 'éš‡',
  'Ü}' => 'é™¾',
  'Ü~' => 'éšˆ',
  'Ü¡' => 'éš‰',
  'Ü¢' => 'éšƒ',
  'Ü£' => 'éš€',
  'Ü¤' => 'é›‚',
  'Ü¥' => 'é›ˆ',
  'Ü¦' => 'é›ƒ',
  'Ü§' => 'é›±',
  'Ü¨' => 'é›°',
  'Ü©' => 'é�¬',
  'Üª' => 'é�°',
  'Ü«' => 'é�®',
  'Ü¬' => 'é ‡',
  'Ü­' => 'é¢©',
  'Ü®' => 'é£«',
  'Ü¯' => 'é³¦',
  'Ü°' => 'é»¹',
  'Ü±' => 'äºƒ',
  'Ü²' => 'äº„',
  'Ü³' => 'äº¶',
  'Ü´' => 'å‚½',
  'Üµ' => 'å‚¿',
  'Ü¶' => 'åƒ†',
  'Ü·' => 'å‚®',
  'Ü¸' => 'åƒ„',
  'Ü¹' => 'åƒŠ',
  'Üº' => 'å‚´',
  'Ü»' => 'åƒˆ',
  'Ü¼' => 'åƒ‚',
  'Ü½' => 'å‚°',
  'Ü¾' => 'åƒ�',
  'Ü¿' => 'å‚º',
  'ÜÀ' => 'å‚±',
  'ÜÁ' => 'åƒ‹',
  'ÜÂ' => 'åƒ‰',
  'ÜÃ' => 'å‚¶',
  'ÜÄ' => 'å‚¸',
  'ÜÅ' => 'å‡—',
  'ÜÆ' => 'å‰º',
  'ÜÇ' => 'å‰¸',
  'ÜÈ' => 'å‰»',
  'ÜÉ' => 'å‰¼',
  'ÜÊ' => 'å—ƒ',
  'ÜË' => 'å—›',
  'ÜÌ' => 'å—Œ',
  'ÜÍ' => 'å—�',
  'ÜÎ' => 'å—‹',
  'ÜÏ' => 'å—Š',
  'ÜÐ' => 'å—�',
  'ÜÑ' => 'å—€',
  'ÜÒ' => 'å—”',
  'ÜÓ' => 'å—„',
  'ÜÔ' => 'å—©',
  'ÜÕ' => 'å–¿',
  'ÜÖ' => 'å—’',
  'Ü×' => 'å–�',
  'ÜØ' => 'å—�',
  'ÜÙ' => 'å—•',
  'ÜÚ' => 'å—¢',
  'ÜÛ' => 'å—–',
  'ÜÜ' => 'å—ˆ',
  'ÜÝ' => 'å—²',
  'ÜÞ' => 'å—�',
  'Üß' => 'å—™',
  'Üà' => 'å—‚',
  'Üá' => 'åœ”',
  'Üâ' => 'å¡“',
  'Üã' => 'å¡¨',
  'Üä' => 'å¡¤',
  'Üå' => 'å¡�',
  'Üæ' => 'å¡�',
  'Üç' => 'å¡‰',
  'Üè' => 'å¡¯',
  'Üé' => 'å¡•',
  'Üê' => 'å¡Ž',
  'Üë' => 'å¡�',
  'Üì' => 'å¡™',
  'Üí' => 'å¡¥',
  'Üî' => 'å¡›',
  'Üï' => 'å ½',
  'Üð' => 'å¡£',
  'Üñ' => 'å¡±',
  'Üò' => 'å£¼',
  'Üó' => 'å«‡',
  'Üô' => 'å«„',
  'Üõ' => 'å«‹',
  'Üö' => 'åªº',
  'Ü÷' => 'åª¸',
  'Üø' => 'åª±',
  'Üù' => 'åªµ',
  'Üú' => 'åª°',
  'Üû' => 'åª¿',
  'Üü' => 'å«ˆ',
  'Üý' => 'åª»',
  'Üþ' => 'å«†',
  'Ý@' => 'åª·',
  'ÝA' => 'å«€',
  'ÝB' => 'å«Š',
  'ÝC' => 'åª´',
  'ÝD' => 'åª¶',
  'ÝE' => 'å«�',
  'ÝF' => 'åª¹',
  'ÝG' => 'åª�',
  'ÝH' => 'å¯–',
  'ÝI' => 'å¯˜',
  'ÝJ' => 'å¯™',
  'ÝK' => 'å°Ÿ',
  'ÝL' => 'å°³',
  'ÝM' => 'åµ±',
  'ÝN' => 'åµ£',
  'ÝO' => 'åµŠ',
  'ÝP' => 'åµ¥',
  'ÝQ' => 'åµ²',
  'ÝR' => 'åµ¬',
  'ÝS' => 'åµž',
  'ÝT' => 'åµ¨',
  'ÝU' => 'åµ§',
  'ÝV' => 'åµ¢',
  'ÝW' => 'å·°',
  'ÝX' => 'å¹�',
  'ÝY' => 'å¹Ž',
  'ÝZ' => 'å¹Š',
  'Ý[' => 'å¹�',
  'Ý\\' => 'å¹‹',
  'Ý]' => 'å»…',
  'Ý^' => 'å»Œ',
  'Ý_' => 'å»†',
  'Ý`' => 'å»‹',
  'Ýa' => 'å»‡',
  'Ýb' => 'å½€',
  'Ýc' => 'å¾¯',
  'Ýd' => 'å¾­',
  'Ýe' => 'æƒ·',
  'Ýf' => 'æ…‰',
  'Ýg' => 'æ…Š',
  'Ýh' => 'æ„«',
  'Ýi' => 'æ……',
  'Ýj' => 'æ„¶',
  'Ýk' => 'æ„²',
  'Ýl' => 'æ„®',
  'Ým' => 'æ…†',
  'Ýn' => 'æ„¯',
  'Ýo' => 'æ…�',
  'Ýp' => 'æ„©',
  'Ýq' => 'æ…€',
  'Ýr' => 'æˆ ',
  'Ýs' => 'é…¨',
  'Ýt' => 'æˆ£',
  'Ýu' => 'æˆ¥',
  'Ýv' => 'æˆ¤',
  'Ýw' => 'æ�…',
  'Ýx' => 'æ�±',
  'Ýy' => 'æ�«',
  'Ýz' => 'æ��',
  'Ý{' => 'æ�’',
  'Ý|' => 'æ�‰',
  'Ý}' => 'æ� ',
  'Ý~' => 'æ�¤',
  'Ý¡' => 'æ�³',
  'Ý¢' => 'æ‘ƒ',
  'Ý£' => 'æ�Ÿ',
  'Ý¤' => 'æ�•',
  'Ý¥' => 'æ�˜',
  'Ý¦' => 'æ�¹',
  'Ý§' => 'æ�·',
  'Ý¨' => 'æ�¢',
  'Ý©' => 'æ�£',
  'Ýª' => 'æ�Œ',
  'Ý«' => 'æ�¦',
  'Ý¬' => 'æ�°',
  'Ý­' => 'æ�¨',
  'Ý®' => 'æ‘�',
  'Ý¯' => 'æ�µ',
  'Ý°' => 'æ�¯',
  'Ý±' => 'æ�Š',
  'Ý²' => 'æ�š',
  'Ý³' => 'æ‘€',
  'Ý´' => 'æ�¥',
  'Ýµ' => 'æ�§',
  'Ý¶' => 'æ�‹',
  'Ý·' => 'æ�§',
  'Ý¸' => 'æ�›',
  'Ý¹' => 'æ�®',
  'Ýº' => 'æ�¡',
  'Ý»' => 'æ�Ž',
  'Ý¼' => 'æ•¯',
  'Ý½' => 'æ–’',
  'Ý¾' => 'æ—“',
  'Ý¿' => 'æš†',
  'ÝÀ' => 'æšŒ',
  'ÝÁ' => 'æš•',
  'ÝÂ' => 'æš�',
  'ÝÃ' => 'æš‹',
  'ÝÄ' => 'æšŠ',
  'ÝÅ' => 'æš™',
  'ÝÆ' => 'æš”',
  'ÝÇ' => 'æ™¸',
  'ÝÈ' => 'æœ ',
  'ÝÉ' => 'æ¥¦',
  'ÝÊ' => 'æ¥Ÿ',
  'ÝË' => 'æ¤¸',
  'ÝÌ' => 'æ¥Ž',
  'ÝÍ' => 'æ¥¢',
  'ÝÎ' => 'æ¥±',
  'ÝÏ' => 'æ¤¿',
  'ÝÐ' => 'æ¥…',
  'ÝÑ' => 'æ¥ª',
  'ÝÒ' => 'æ¤¹',
  'ÝÓ' => 'æ¥‚',
  'ÝÔ' => 'æ¥—',
  'ÝÕ' => 'æ¥™',
  'ÝÖ' => 'æ¥º',
  'Ý×' => 'æ¥ˆ',
  'ÝØ' => 'æ¥‰',
  'ÝÙ' => 'æ¤µ',
  'ÝÚ' => 'æ¥¬',
  'ÝÛ' => 'æ¤³',
  'ÝÜ' => 'æ¤½',
  'ÝÝ' => 'æ¥¥',
  'ÝÞ' => 'æ£°',
  'Ýß' => 'æ¥¸',
  'Ýà' => 'æ¤´',
  'Ýá' => 'æ¥©',
  'Ýâ' => 'æ¥€',
  'Ýã' => 'æ¥¯',
  'Ýä' => 'æ¥„',
  'Ýå' => 'æ¥¶',
  'Ýæ' => 'æ¥˜',
  'Ýç' => 'æ¥�',
  'Ýè' => 'æ¥´',
  'Ýé' => 'æ¥Œ',
  'Ýê' => 'æ¤»',
  'Ýë' => 'æ¥‹',
  'Ýì' => 'æ¤·',
  'Ýí' => 'æ¥œ',
  'Ýî' => 'æ¥�',
  'Ýï' => 'æ¥‘',
  'Ýð' => 'æ¤²',
  'Ýñ' => 'æ¥’',
  'Ýò' => 'æ¤¯',
  'Ýó' => 'æ¥»',
  'Ýô' => 'æ¤¼',
  'Ýõ' => 'æ­†',
  'Ýö' => 'æ­…',
  'Ý÷' => 'æ­ƒ',
  'Ýø' => 'æ­‚',
  'Ýù' => 'æ­ˆ',
  'Ýú' => 'æ­�',
  'Ýû' => 'æ®›',
  'Ýü' => 'ï¨�',
  'Ýý' => 'æ¯»',
  'Ýþ' => 'æ¯¼',
  'Þ@' => 'æ¯¹',
  'ÞA' => 'æ¯·',
  'ÞB' => 'æ¯¸',
  'ÞC' => 'æº›',
  'ÞD' => 'æ»–',
  'ÞE' => 'æ»ˆ',
  'ÞF' => 'æº�',
  'ÞG' => 'æ»€',
  'ÞH' => 'æºŸ',
  'ÞI' => 'æº“',
  'ÞJ' => 'æº”',
  'ÞK' => 'æº ',
  'ÞL' => 'æº±',
  'ÞM' => 'æº¹',
  'ÞN' => 'æ»†',
  'ÞO' => 'æ»’',
  'ÞP' => 'æº½',
  'ÞQ' => 'æ»�',
  'ÞR' => 'æºž',
  'ÞS' => 'æ»‰',
  'ÞT' => 'æº·',
  'ÞU' => 'æº°',
  'ÞV' => 'æ»�',
  'ÞW' => 'æº¦',
  'ÞX' => 'æ»�',
  'ÞY' => 'æº²',
  'ÞZ' => 'æº¾',
  'Þ[' => 'æ»ƒ',
  'Þ\\' => 'æ»œ',
  'Þ]' => 'æ»˜',
  'Þ^' => 'æº™',
  'Þ_' => 'æº’',
  'Þ`' => 'æºŽ',
  'Þa' => 'æº�',
  'Þb' => 'æº¤',
  'Þc' => 'æº¡',
  'Þd' => 'æº¿',
  'Þe' => 'æº³',
  'Þf' => 'æ»�',
  'Þg' => 'æ»Š',
  'Þh' => 'æº—',
  'Þi' => 'æº®',
  'Þj' => 'æº£',
  'Þk' => 'ç…‡',
  'Þl' => 'ç…”',
  'Þm' => 'ç…’',
  'Þn' => 'ç…£',
  'Þo' => 'ç… ',
  'Þp' => 'ç…�',
  'Þq' => 'ç…�',
  'Þr' => 'ç…¢',
  'Þs' => 'ç…²',
  'Þt' => 'ç…¸',
  'Þu' => 'ç…ª',
  'Þv' => 'ç…¡',
  'Þw' => 'ç…‚',
  'Þx' => 'ç…˜',
  'Þy' => 'ç…ƒ',
  'Þz' => 'ç…‹',
  'Þ{' => 'ç…°',
  'Þ|' => 'ç…Ÿ',
  'Þ}' => 'ç…�',
  'Þ~' => 'ç…“',
  'Þ¡' => 'ç…„',
  'Þ¢' => 'ç…�',
  'Þ£' => 'ç…š',
  'Þ¤' => 'ç‰�',
  'Þ¥' => 'çŠ�',
  'Þ¦' => 'çŠŒ',
  'Þ§' => 'çŠ‘',
  'Þ¨' => 'çŠ�',
  'Þ©' => 'çŠŽ',
  'Þª' => 'çŒ¼',
  'Þ«' => 'ç�‚',
  'Þ¬' => 'çŒ»',
  'Þ­' => 'çŒº',
  'Þ®' => 'ç�€',
  'Þ¯' => 'ç�Š',
  'Þ°' => 'ç�‰',
  'Þ±' => 'ç‘„',
  'Þ²' => 'ç‘Š',
  'Þ³' => 'ç‘‹',
  'Þ´' => 'ç‘’',
  'Þµ' => 'ç‘‘',
  'Þ¶' => 'ç‘—',
  'Þ·' => 'ç‘€',
  'Þ¸' => 'ç‘�',
  'Þ¹' => 'ç‘�',
  'Þº' => 'ç‘Ž',
  'Þ»' => 'ç‘‚',
  'Þ¼' => 'ç‘†',
  'Þ½' => 'ç‘�',
  'Þ¾' => 'ç‘”',
  'Þ¿' => 'ç“¡',
  'ÞÀ' => 'ç“¿',
  'ÞÁ' => 'ç“¾',
  'ÞÂ' => 'ç“½',
  'ÞÃ' => 'ç”�',
  'ÞÄ' => 'ç•¹',
  'ÞÅ' => 'ç•·',
  'ÞÆ' => 'æ¦ƒ',
  'ÞÇ' => 'ç—¯',
  'ÞÈ' => 'ç˜�',
  'ÞÉ' => 'ç˜ƒ',
  'ÞÊ' => 'ç—·',
  'ÞË' => 'ç—¾',
  'ÞÌ' => 'ç—¼',
  'ÞÍ' => 'ç—¹',
  'ÞÎ' => 'ç—¸',
  'ÞÏ' => 'ç˜�',
  'ÞÐ' => 'ç—»',
  'ÞÑ' => 'ç—¶',
  'ÞÒ' => 'ç—­',
  'ÞÓ' => 'ç—µ',
  'ÞÔ' => 'ç—½',
  'ÞÕ' => 'çš™',
  'ÞÖ' => 'çšµ',
  'Þ×' => 'ç›�',
  'ÞØ' => 'ç�•',
  'ÞÙ' => 'ç�Ÿ',
  'ÞÚ' => 'ç� ',
  'ÞÛ' => 'ç�’',
  'ÞÜ' => 'ç�–',
  'ÞÝ' => 'ç�š',
  'ÞÞ' => 'ç�©',
  'Þß' => 'ç�§',
  'Þà' => 'ç�”',
  'Þá' => 'ç�™',
  'Þâ' => 'ç�­',
  'Þã' => 'çŸ ',
  'Þä' => 'ç¢‡',
  'Þå' => 'ç¢š',
  'Þæ' => 'ç¢”',
  'Þç' => 'ç¢�',
  'Þè' => 'ç¢„',
  'Þé' => 'ç¢•',
  'Þê' => 'ç¢…',
  'Þë' => 'ç¢†',
  'Þì' => 'ç¢¡',
  'Þí' => 'ç¢ƒ',
  'Þî' => 'ç¡¹',
  'Þï' => 'ç¢™',
  'Þð' => 'ç¢€',
  'Þñ' => 'ç¢–',
  'Þò' => 'ç¡»',
  'Þó' => 'ç¥¼',
  'Þô' => 'ç¦‚',
  'Þõ' => 'ç¥½',
  'Þö' => 'ç¥¹',
  'Þ÷' => 'ç¨‘',
  'Þø' => 'ç¨˜',
  'Þù' => 'ç¨™',
  'Þú' => 'ç¨’',
  'Þû' => 'ç¨—',
  'Þü' => 'ç¨•',
  'Þý' => 'ç¨¢',
  'Þþ' => 'ç¨“',
  'ß@' => 'ç¨›',
  'ßA' => 'ç¨�',
  'ßB' => 'çª£',
  'ßC' => 'çª¢',
  'ßD' => 'çªž',
  'ßE' => 'ç««',
  'ßF' => 'ç­¦',
  'ßG' => 'ç­¤',
  'ßH' => 'ç­­',
  'ßI' => 'ç­´',
  'ßJ' => 'ç­©',
  'ßK' => 'ç­²',
  'ßL' => 'ç­¥',
  'ßM' => 'ç­³',
  'ßN' => 'ç­±',
  'ßO' => 'ç­°',
  'ßP' => 'ç­¡',
  'ßQ' => 'ç­¸',
  'ßR' => 'ç­¶',
  'ßS' => 'ç­£',
  'ßT' => 'ç²²',
  'ßU' => 'ç²´',
  'ßV' => 'ç²¯',
  'ßW' => 'ç¶ˆ',
  'ßX' => 'ç¶†',
  'ßY' => 'ç¶€',
  'ßZ' => 'ç¶�',
  'ß[' => 'çµ¿',
  'ß\\' => 'ç¶…',
  'ß]' => 'çµº',
  'ß^' => 'ç¶Ž',
  'ß_' => 'çµ»',
  'ß`' => 'ç¶ƒ',
  'ßa' => 'çµ¼',
  'ßb' => 'ç¶Œ',
  'ßc' => 'ç¶”',
  'ßd' => 'ç¶„',
  'ße' => 'çµ½',
  'ßf' => 'ç¶’',
  'ßg' => 'ç½­',
  'ßh' => 'ç½«',
  'ßi' => 'ç½§',
  'ßj' => 'ç½¨',
  'ßk' => 'ç½¬',
  'ßl' => 'ç¾¦',
  'ßm' => 'ç¾¥',
  'ßn' => 'ç¾§',
  'ßo' => 'ç¿›',
  'ßp' => 'ç¿œ',
  'ßq' => 'è€¡',
  'ßr' => 'è…¤',
  'ßs' => 'è… ',
  'ßt' => 'è…·',
  'ßu' => 'è…œ',
  'ßv' => 'è…©',
  'ßw' => 'è…›',
  'ßx' => 'è…¢',
  'ßy' => 'è…²',
  'ßz' => 'æœ¡',
  'ß{' => 'è…ž',
  'ß|' => 'è…¶',
  'ß}' => 'è…§',
  'ß~' => 'è…¯',
  'ß¡' => 'è…„',
  'ß¢' => 'è…¡',
  'ß£' => 'èˆ�',
  'ß¤' => 'è‰‰',
  'ß¥' => 'è‰„',
  'ß¦' => 'è‰€',
  'ß§' => 'è‰‚',
  'ß¨' => 'è‰…',
  'ß©' => 'è“±',
  'ßª' => 'è�¿',
  'ß«' => 'è‘–',
  'ß¬' => 'è‘¶',
  'ß­' => 'è‘¹',
  'ß®' => 'è’�',
  'ß¯' => 'è’�',
  'ß°' => 'è‘¥',
  'ß±' => 'è‘‘',
  'ß²' => 'è‘€',
  'ß³' => 'è’†',
  'ß´' => 'è‘§',
  'ßµ' => 'è�°',
  'ß¶' => 'è‘�',
  'ß·' => 'è‘½',
  'ß¸' => 'è‘š',
  'ß¹' => 'è‘™',
  'ßº' => 'è‘´',
  'ß»' => 'è‘³',
  'ß¼' => 'è‘�',
  'ß½' => 'è”‡',
  'ß¾' => 'è‘ž',
  'ß¿' => 'è�·',
  'ßÀ' => 'è�º',
  'ßÁ' => 'è�´',
  'ßÂ' => 'è‘º',
  'ßÃ' => 'è‘ƒ',
  'ßÄ' => 'è‘¸',
  'ßÅ' => 'è�²',
  'ßÆ' => 'è‘…',
  'ßÇ' => 'è�©',
  'ßÈ' => 'è�™',
  'ßÉ' => 'è‘‹',
  'ßÊ' => 'è�¯',
  'ßË' => 'è‘‚',
  'ßÌ' => 'è�­',
  'ßÍ' => 'è‘Ÿ',
  'ßÎ' => 'è‘°',
  'ßÏ' => 'è�¹',
  'ßÐ' => 'è‘Ž',
  'ßÑ' => 'è‘Œ',
  'ßÒ' => 'è‘’',
  'ßÓ' => 'è‘¯',
  'ßÔ' => 'è“…',
  'ßÕ' => 'è’Ž',
  'ßÖ' => 'è�»',
  'ß×' => 'è‘‡',
  'ßØ' => 'è�¶',
  'ßÙ' => 'è�³',
  'ßÚ' => 'è‘¨',
  'ßÛ' => 'è‘¾',
  'ßÜ' => 'è‘„',
  'ßÝ' => 'è�«',
  'ßÞ' => 'è‘ ',
  'ßß' => 'è‘”',
  'ßà' => 'è‘®',
  'ßá' => 'è‘�',
  'ßâ' => 'èœ‹',
  'ßã' => 'èœ„',
  'ßä' => 'è›·',
  'ßå' => 'èœŒ',
  'ßæ' => 'è›º',
  'ßç' => 'è›–',
  'ßè' => 'è›µ',
  'ßé' => 'è��',
  'ßê' => 'è›¸',
  'ßë' => 'èœŽ',
  'ßì' => 'èœ‰',
  'ßí' => 'èœ�',
  'ßî' => 'è›¶',
  'ßï' => 'èœ�',
  'ßð' => 'èœ…',
  'ßñ' => 'è£–',
  'ßò' => 'è£‹',
  'ßó' => 'è£�',
  'ßô' => 'è£Ž',
  'ßõ' => 'è£ž',
  'ßö' => 'è£›',
  'ß÷' => 'è£š',
  'ßø' => 'è£Œ',
  'ßù' => 'è£�',
  'ßú' => 'è¦…',
  'ßû' => 'è¦›',
  'ßü' => 'è§Ÿ',
  'ßý' => 'è§¥',
  'ßþ' => 'è§¤',
  'à@' => 'è§¡',
  'àA' => 'è§ ',
  'àB' => 'è§¢',
  'àC' => 'è§œ',
  'àD' => 'è§¦',
  'àE' => 'è©¶',
  'àF' => 'èª†',
  'àG' => 'è©¿',
  'àH' => 'è©¡',
  'àI' => 'è¨¿',
  'àJ' => 'è©·',
  'àK' => 'èª‚',
  'àL' => 'èª„',
  'àM' => 'è©µ',
  'àN' => 'èªƒ',
  'àO' => 'èª�',
  'àP' => 'è©´',
  'àQ' => 'è©º',
  'àR' => 'è°¼',
  'àS' => 'è±‹',
  'àT' => 'è±Š',
  'àU' => 'è±¥',
  'àV' => 'è±¤',
  'àW' => 'è±¦',
  'àX' => 'è²†',
  'àY' => 'è²„',
  'àZ' => 'è²…',
  'à[' => 'è³Œ',
  'à\\' => 'èµ¨',
  'à]' => 'èµ©',
  'à^' => 'è¶‘',
  'à_' => 'è¶Œ',
  'à`' => 'è¶Ž',
  'àa' => 'è¶�',
  'àb' => 'è¶�',
  'àc' => 'è¶“',
  'àd' => 'è¶”',
  'àe' => 'è¶�',
  'àf' => 'è¶’',
  'àg' => 'è·°',
  'àh' => 'è· ',
  'ài' => 'è·¬',
  'àj' => 'è·±',
  'àk' => 'è·®',
  'àl' => 'è·�',
  'àm' => 'è·©',
  'àn' => 'è·£',
  'ào' => 'è·¢',
  'àp' => 'è·§',
  'àq' => 'è·²',
  'àr' => 'è·«',
  'às' => 'è·´',
  'àt' => 'è¼†',
  'àu' => 'è»¿',
  'àv' => 'è¼�',
  'àw' => 'è¼€',
  'àx' => 'è¼…',
  'ày' => 'è¼‡',
  'àz' => 'è¼ˆ',
  'à{' => 'è¼‚',
  'à|' => 'è¼‹',
  'à}' => 'é�’',
  'à~' => 'é€¿',
  'à¡' => 'é�„',
  'à¢' => 'é�‰',
  'à£' => 'é€½',
  'à¤' => 'é„�',
  'à¥' => 'é„�',
  'à¦' => 'é„�',
  'à§' => 'é„‘',
  'à¨' => 'é„–',
  'à©' => 'é„”',
  'àª' => 'é„‹',
  'à«' => 'é„Ž',
  'à¬' => 'é…®',
  'à­' => 'é…¯',
  'à®' => 'é‰ˆ',
  'à¯' => 'é‰’',
  'à°' => 'éˆ°',
  'à±' => 'éˆº',
  'à²' => 'é‰¦',
  'à³' => 'éˆ³',
  'à´' => 'é‰¥',
  'àµ' => 'é‰ž',
  'à¶' => 'éŠƒ',
  'à·' => 'éˆ®',
  'à¸' => 'é‰Š',
  'à¹' => 'é‰†',
  'àº' => 'é‰­',
  'à»' => 'é‰¬',
  'à¼' => 'é‰�',
  'à½' => 'é‰ ',
  'à¾' => 'é‰§',
  'à¿' => 'é‰¯',
  'àÀ' => 'éˆ¶',
  'àÁ' => 'é‰¡',
  'àÂ' => 'é‰°',
  'àÃ' => 'éˆ±',
  'àÄ' => 'é‰”',
  'àÅ' => 'é‰£',
  'àÆ' => 'é‰�',
  'àÇ' => 'é‰²',
  'àÈ' => 'é‰Ž',
  'àÉ' => 'é‰“',
  'àÊ' => 'é‰Œ',
  'àË' => 'é‰–',
  'àÌ' => 'éˆ²',
  'àÍ' => 'é–Ÿ',
  'àÎ' => 'é–œ',
  'àÏ' => 'é–ž',
  'àÐ' => 'é–›',
  'àÑ' => 'éš’',
  'àÒ' => 'éš“',
  'àÓ' => 'éš‘',
  'àÔ' => 'éš—',
  'àÕ' => 'é›Ž',
  'àÖ' => 'é›º',
  'à×' => 'é›½',
  'àØ' => 'é›¸',
  'àÙ' => 'é›µ',
  'àÚ' => 'é�³',
  'àÛ' => 'é�·',
  'àÜ' => 'é�¸',
  'àÝ' => 'é�²',
  'àÞ' => 'é �',
  'àß' => 'é �',
  'àà' => 'é Ž',
  'àá' => 'é¢¬',
  'àâ' => 'é£¶',
  'àã' => 'é£¹',
  'àä' => 'é¦¯',
  'àå' => 'é¦²',
  'àæ' => 'é¦°',
  'àç' => 'é¦µ',
  'àè' => 'éª­',
  'àé' => 'éª«',
  'àê' => 'é­›',
  'àë' => 'é³ª',
  'àì' => 'é³­',
  'àí' => 'é³§',
  'àî' => 'éº€',
  'àï' => 'é»½',
  'àð' => 'åƒ¦',
  'àñ' => 'åƒ”',
  'àò' => 'åƒ—',
  'àó' => 'åƒ¨',
  'àô' => 'åƒ³',
  'àõ' => 'åƒ›',
  'àö' => 'åƒª',
  'à÷' => 'åƒ�',
  'àø' => 'åƒ¤',
  'àù' => 'åƒ“',
  'àú' => 'åƒ¬',
  'àû' => 'åƒ°',
  'àü' => 'åƒ¯',
  'àý' => 'åƒ£',
  'àþ' => 'åƒ ',
  'á@' => 'å‡˜',
  'áA' => 'åŠ€',
  'áB' => 'åŠ�',
  'áC' => 'å‹©',
  'áD' => 'å‹«',
  'áE' => 'åŒ°',
  'áF' => 'åŽ¬',
  'áG' => 'å˜§',
  'áH' => 'å˜•',
  'áI' => 'å˜Œ',
  'áJ' => 'å˜’',
  'áK' => 'å—¼',
  'áL' => 'å˜�',
  'áM' => 'å˜œ',
  'áN' => 'å˜�',
  'áO' => 'å˜“',
  'áP' => 'å˜‚',
  'áQ' => 'å—º',
  'áR' => 'å˜�',
  'áS' => 'å˜„',
  'áT' => 'å—¿',
  'áU' => 'å—¹',
  'áV' => 'å¢‰',
  'áW' => 'å¡¼',
  'áX' => 'å¢�',
  'áY' => 'å¢˜',
  'áZ' => 'å¢†',
  'á[' => 'å¢�',
  'á\\' => 'å¡¿',
  'á]' => 'å¡´',
  'á^' => 'å¢‹',
  'á_' => 'å¡º',
  'á`' => 'å¢‡',
  'áa' => 'å¢‘',
  'áb' => 'å¢Ž',
  'ác' => 'å¡¶',
  'ád' => 'å¢‚',
  'áe' => 'å¢ˆ',
  'áf' => 'å¡»',
  'ág' => 'å¢”',
  'áh' => 'å¢�',
  'ái' => 'å£¾',
  'áj' => 'å¥«',
  'ák' => 'å«œ',
  'ál' => 'å«®',
  'ám' => 'å«¥',
  'án' => 'å«•',
  'áo' => 'å«ª',
  'áp' => 'å«š',
  'áq' => 'å«­',
  'ár' => 'å««',
  'ás' => 'å«³',
  'át' => 'å«¢',
  'áu' => 'å« ',
  'áv' => 'å«›',
  'áw' => 'å«¬',
  'áx' => 'å«ž',
  'áy' => 'å«�',
  'áz' => 'å«™',
  'á{' => 'å«¨',
  'á|' => 'å«Ÿ',
  'á}' => 'å­·',
  'á~' => 'å¯ ',
  'á¡' => 'å¯£',
  'á¢' => 'å±£',
  'á£' => 'å¶‚',
  'á¤' => 'å¶€',
  'á¥' => 'åµ½',
  'á¦' => 'å¶†',
  'á§' => 'åµº',
  'á¨' => 'å¶�',
  'á©' => 'åµ·',
  'áª' => 'å¶Š',
  'á«' => 'å¶‰',
  'á¬' => 'å¶ˆ',
  'á­' => 'åµ¾',
  'á®' => 'åµ¼',
  'á¯' => 'å¶�',
  'á°' => 'åµ¹',
  'á±' => 'åµ¿',
  'á²' => 'å¹˜',
  'á³' => 'å¹™',
  'á´' => 'å¹“',
  'áµ' => 'å»˜',
  'á¶' => 'å»‘',
  'á·' => 'å»—',
  'á¸' => 'å»Ž',
  'á¹' => 'å»œ',
  'áº' => 'å»•',
  'á»' => 'å»™',
  'á¼' => 'å»’',
  'á½' => 'å»”',
  'á¾' => 'å½„',
  'á¿' => 'å½ƒ',
  'áÀ' => 'å½¯',
  'áÁ' => 'å¾¶',
  'áÂ' => 'æ„¬',
  'áÃ' => 'æ„¨',
  'áÄ' => 'æ…�',
  'áÅ' => 'æ…ž',
  'áÆ' => 'æ…±',
  'áÇ' => 'æ…³',
  'áÈ' => 'æ…’',
  'áÉ' => 'æ…“',
  'áÊ' => 'æ…²',
  'áË' => 'æ…¬',
  'áÌ' => 'æ†€',
  'áÍ' => 'æ…´',
  'áÎ' => 'æ…”',
  'áÏ' => 'æ…º',
  'áÐ' => 'æ…›',
  'áÑ' => 'æ…¥',
  'áÒ' => 'æ„»',
  'áÓ' => 'æ…ª',
  'áÔ' => 'æ…¡',
  'áÕ' => 'æ…–',
  'áÖ' => 'æˆ©',
  'á×' => 'æˆ§',
  'áØ' => 'æˆ«',
  'áÙ' => 'æ�«',
  'áÚ' => 'æ‘�',
  'áÛ' => 'æ‘›',
  'áÜ' => 'æ‘�',
  'áÝ' => 'æ‘´',
  'áÞ' => 'æ‘¶',
  'áß' => 'æ‘²',
  'áà' => 'æ‘³',
  'áá' => 'æ‘½',
  'áâ' => 'æ‘µ',
  'áã' => 'æ‘¦',
  'áä' => 'æ’¦',
  'áå' => 'æ‘Ž',
  'áæ' => 'æ’‚',
  'áç' => 'æ‘ž',
  'áè' => 'æ‘œ',
  'áé' => 'æ‘‹',
  'áê' => 'æ‘“',
  'áë' => 'æ‘ ',
  'áì' => 'æ‘�',
  'áí' => 'æ‘¿',
  'áî' => 'æ�¿',
  'áï' => 'æ‘¬',
  'áð' => 'æ‘«',
  'áñ' => 'æ‘™',
  'áò' => 'æ‘¥',
  'áó' => 'æ‘·',
  'áô' => 'æ•³',
  'áõ' => 'æ– ',
  'áö' => 'æš¡',
  'á÷' => 'æš ',
  'áø' => 'æšŸ',
  'áù' => 'æœ…',
  'áú' => 'æœ„',
  'áû' => 'æœ¢',
  'áü' => 'æ¦±',
  'áý' => 'æ¦¶',
  'áþ' => 'æ§‰',
  'â@' => 'æ¦ ',
  'âA' => 'æ§Ž',
  'âB' => 'æ¦–',
  'âC' => 'æ¦°',
  'âD' => 'æ¦¬',
  'âE' => 'æ¦¼',
  'âF' => 'æ¦‘',
  'âG' => 'æ¦™',
  'âH' => 'æ¦Ž',
  'âI' => 'æ¦§',
  'âJ' => 'æ¦�',
  'âK' => 'æ¦©',
  'âL' => 'æ¦¾',
  'âM' => 'æ¦¯',
  'âN' => 'æ¦¿',
  'âO' => 'æ§„',
  'âP' => 'æ¦½',
  'âQ' => 'æ¦¤',
  'âR' => 'æ§”',
  'âS' => 'æ¦¹',
  'âT' => 'æ§Š',
  'âU' => 'æ¦š',
  'âV' => 'æ§�',
  'âW' => 'æ¦³',
  'âX' => 'æ¦“',
  'âY' => 'æ¦ª',
  'âZ' => 'æ¦¡',
  'â[' => 'æ¦ž',
  'â\\' => 'æ§™',
  'â]' => 'æ¦—',
  'â^' => 'æ¦�',
  'â_' => 'æ§‚',
  'â`' => 'æ¦µ',
  'âa' => 'æ¦¥',
  'âb' => 'æ§†',
  'âc' => 'æ­Š',
  'âd' => 'æ­�',
  'âe' => 'æ­‹',
  'âf' => 'æ®ž',
  'âg' => 'æ®Ÿ',
  'âh' => 'æ® ',
  'âi' => 'æ¯ƒ',
  'âj' => 'æ¯„',
  'âk' => 'æ¯¾',
  'âl' => 'æ»Ž',
  'âm' => 'æ»µ',
  'ân' => 'æ»±',
  'âo' => 'æ¼ƒ',
  'âp' => 'æ¼¥',
  'âq' => 'æ»¸',
  'âr' => 'æ¼·',
  'âs' => 'æ»»',
  'ât' => 'æ¼®',
  'âu' => 'æ¼‰',
  'âv' => 'æ½Ž',
  'âw' => 'æ¼™',
  'âx' => 'æ¼š',
  'ây' => 'æ¼§',
  'âz' => 'æ¼˜',
  'â{' => 'æ¼»',
  'â|' => 'æ¼’',
  'â}' => 'æ»­',
  'â~' => 'æ¼Š',
  'â¡' => 'æ¼¶',
  'â¢' => 'æ½³',
  'â£' => 'æ»¹',
  'â¤' => 'æ»®',
  'â¥' => 'æ¼­',
  'â¦' => 'æ½€',
  'â§' => 'æ¼°',
  'â¨' => 'æ¼¼',
  'â©' => 'æ¼µ',
  'âª' => 'æ»«',
  'â«' => 'æ¼‡',
  'â¬' => 'æ¼Ž',
  'â­' => 'æ½ƒ',
  'â®' => 'æ¼…',
  'â¯' => 'æ»½',
  'â°' => 'æ»¶',
  'â±' => 'æ¼¹',
  'â²' => 'æ¼œ',
  'â³' => 'æ»¼',
  'â´' => 'æ¼º',
  'âµ' => 'æ¼Ÿ',
  'â¶' => 'æ¼�',
  'â·' => 'æ¼ž',
  'â¸' => 'æ¼ˆ',
  'â¹' => 'æ¼¡',
  'âº' => 'ç†‡',
  'â»' => 'ç†�',
  'â¼' => 'ç†‰',
  'â½' => 'ç†€',
  'â¾' => 'ç†…',
  'â¿' => 'ç†‚',
  'âÀ' => 'ç†�',
  'âÁ' => 'ç…»',
  'âÂ' => 'ç††',
  'âÃ' => 'ç†�',
  'âÄ' => 'ç†—',
  'âÅ' => 'ç‰„',
  'âÆ' => 'ç‰“',
  'âÇ' => 'çŠ—',
  'âÈ' => 'çŠ•',
  'âÉ' => 'çŠ“',
  'âÊ' => 'ç�ƒ',
  'âË' => 'ç��',
  'âÌ' => 'ç�‘',
  'âÍ' => 'ç�Œ',
  'âÎ' => 'ç‘¢',
  'âÏ' => 'ç‘³',
  'âÐ' => 'ç‘±',
  'âÑ' => 'ç‘µ',
  'âÒ' => 'ç‘²',
  'âÓ' => 'ç‘§',
  'âÔ' => 'ç‘®',
  'âÕ' => 'ç”€',
  'âÖ' => 'ç”‚',
  'â×' => 'ç”ƒ',
  'âØ' => 'ç•½',
  'âÙ' => 'ç–�',
  'âÚ' => 'ç˜–',
  'âÛ' => 'ç˜ˆ',
  'âÜ' => 'ç˜Œ',
  'âÝ' => 'ç˜•',
  'âÞ' => 'ç˜‘',
  'âß' => 'ç˜Š',
  'âà' => 'ç˜”',
  'âá' => 'çš¸',
  'ââ' => 'çž�',
  'âã' => 'ç�¼',
  'âä' => 'çž…',
  'âå' => 'çž‚',
  'âæ' => 'ç�®',
  'âç' => 'çž€',
  'âè' => 'ç�¯',
  'âé' => 'ç�¾',
  'âê' => 'çžƒ',
  'âë' => 'ç¢²',
  'âì' => 'ç¢ª',
  'âí' => 'ç¢´',
  'âî' => 'ç¢­',
  'âï' => 'ç¢¨',
  'âð' => 'ç¡¾',
  'âñ' => 'ç¢«',
  'âò' => 'ç¢ž',
  'âó' => 'ç¢¥',
  'âô' => 'ç¢ ',
  'âõ' => 'ç¢¬',
  'âö' => 'ç¢¢',
  'â÷' => 'ç¢¤',
  'âø' => 'ç¦˜',
  'âù' => 'ç¦Š',
  'âú' => 'ç¦‹',
  'âû' => 'ç¦–',
  'âü' => 'ç¦•',
  'âý' => 'ç¦”',
  'âþ' => 'ç¦“',
  'ã@' => 'ç¦—',
  'ãA' => 'ç¦ˆ',
  'ãB' => 'ç¦’',
  'ãC' => 'ç¦�',
  'ãD' => 'ç¨«',
  'ãE' => 'ç©Š',
  'ãF' => 'ç¨°',
  'ãG' => 'ç¨¯',
  'ãH' => 'ç¨¨',
  'ãI' => 'ç¨¦',
  'ãJ' => 'çª¨',
  'ãK' => 'çª«',
  'ãL' => 'çª¬',
  'ãM' => 'ç«®',
  'ãN' => 'ç®ˆ',
  'ãO' => 'ç®œ',
  'ãP' => 'ç®Š',
  'ãQ' => 'ç®‘',
  'ãR' => 'ç®�',
  'ãS' => 'ç®–',
  'ãT' => 'ç®�',
  'ãU' => 'ç®Œ',
  'ãV' => 'ç®›',
  'ãW' => 'ç®Ž',
  'ãX' => 'ç®…',
  'ãY' => 'ç®˜',
  'ãZ' => 'åŠ„',
  'ã[' => 'ç®™',
  'ã\\' => 'ç®¤',
  'ã]' => 'ç®‚',
  'ã^' => 'ç²»',
  'ã_' => 'ç²¿',
  'ã`' => 'ç²¼',
  'ãa' => 'ç²º',
  'ãb' => 'ç¶§',
  'ãc' => 'ç¶·',
  'ãd' => 'ç·‚',
  'ãe' => 'ç¶£',
  'ãf' => 'ç¶ª',
  'ãg' => 'ç·�',
  'ãh' => 'ç·€',
  'ãi' => 'ç·…',
  'ãj' => 'ç¶�',
  'ãk' => 'ç·Ž',
  'ãl' => 'ç·„',
  'ãm' => 'ç·†',
  'ãn' => 'ç·‹',
  'ão' => 'ç·Œ',
  'ãp' => 'ç¶¯',
  'ãq' => 'ç¶¹',
  'ãr' => 'ç¶–',
  'ãs' => 'ç¶¼',
  'ãt' => 'ç¶Ÿ',
  'ãu' => 'ç¶¦',
  'ãv' => 'ç¶®',
  'ãw' => 'ç¶©',
  'ãx' => 'ç¶¡',
  'ãy' => 'ç·‰',
  'ãz' => 'ç½³',
  'ã{' => 'ç¿¢',
  'ã|' => 'ç¿£',
  'ã}' => 'ç¿¥',
  'ã~' => 'ç¿ž',
  'ã¡' => 'è€¤',
  'ã¢' => 'è��',
  'ã£' => 'è�œ',
  'ã¤' => 'è†‰',
  'ã¥' => 'è††',
  'ã¦' => 'è†ƒ',
  'ã§' => 'è†‡',
  'ã¨' => 'è†�',
  'ã©' => 'è†Œ',
  'ãª' => 'è†‹',
  'ã«' => 'èˆ•',
  'ã¬' => 'è’—',
  'ã­' => 'è’¤',
  'ã®' => 'è’¡',
  'ã¯' => 'è’Ÿ',
  'ã°' => 'è’º',
  'ã±' => 'è“Ž',
  'ã²' => 'è“‚',
  'ã³' => 'è’¬',
  'ã´' => 'è’®',
  'ãµ' => 'è’«',
  'ã¶' => 'è’¹',
  'ã·' => 'è’´',
  'ã¸' => 'è“�',
  'ã¹' => 'è“�',
  'ãº' => 'è’ª',
  'ã»' => 'è’š',
  'ã¼' => 'è’±',
  'ã½' => 'è“�',
  'ã¾' => 'è’�',
  'ã¿' => 'è’§',
  'ãÀ' => 'è’»',
  'ãÁ' => 'è’¢',
  'ãÂ' => 'è’”',
  'ãÃ' => 'è“‡',
  'ãÄ' => 'è“Œ',
  'ãÅ' => 'è’›',
  'ãÆ' => 'è’©',
  'ãÇ' => 'è’¯',
  'ãÈ' => 'è’¨',
  'ãÉ' => 'è“–',
  'ãÊ' => 'è’˜',
  'ãË' => 'è’¶',
  'ãÌ' => 'è“�',
  'ãÍ' => 'è’ ',
  'ãÎ' => 'è“—',
  'ãÏ' => 'è“”',
  'ãÐ' => 'è“’',
  'ãÑ' => 'è“›',
  'ãÒ' => 'è’°',
  'ãÓ' => 'è’‘',
  'ãÔ' => 'è™¡',
  'ãÕ' => 'èœ³',
  'ãÖ' => 'èœ£',
  'ã×' => 'èœ¨',
  'ãØ' => 'è�«',
  'ãÙ' => 'è�€',
  'ãÚ' => 'èœ®',
  'ãÛ' => 'èœž',
  'ãÜ' => 'èœ¡',
  'ãÝ' => 'èœ™',
  'ãÞ' => 'èœ›',
  'ãß' => 'è�ƒ',
  'ãà' => 'èœ¬',
  'ãá' => 'è��',
  'ãâ' => 'èœ¾',
  'ãã' => 'è�†',
  'ãä' => 'èœ ',
  'ãå' => 'èœ²',
  'ãæ' => 'èœª',
  'ãç' => 'èœ­',
  'ãè' => 'èœ¼',
  'ãé' => 'èœ’',
  'ãê' => 'èœº',
  'ãë' => 'èœ±',
  'ãì' => 'èœµ',
  'ãí' => 'è�‚',
  'ãî' => 'èœ¦',
  'ãï' => 'èœ§',
  'ãð' => 'èœ¸',
  'ãñ' => 'èœ¤',
  'ãò' => 'èœš',
  'ãó' => 'èœ°',
  'ãô' => 'èœ‘',
  'ãõ' => 'è£·',
  'ãö' => 'è£§',
  'ã÷' => 'è£±',
  'ãø' => 'è£²',
  'ãù' => 'è£º',
  'ãú' => 'è£¾',
  'ãû' => 'è£®',
  'ãü' => 'è£¼',
  'ãý' => 'è£¶',
  'ãþ' => 'è£»',
  'ä@' => 'è£°',
  'äA' => 'è£¬',
  'äB' => 'è£«',
  'äC' => 'è¦�',
  'äD' => 'è¦¡',
  'äE' => 'è¦Ÿ',
  'äF' => 'è¦ž',
  'äG' => 'è§©',
  'äH' => 'è§«',
  'äI' => 'è§¨',
  'äJ' => 'èª«',
  'äK' => 'èª™',
  'äL' => 'èª‹',
  'äM' => 'èª’',
  'äN' => 'èª�',
  'äO' => 'èª–',
  'äP' => 'è°½',
  'äQ' => 'è±¨',
  'äR' => 'è±©',
  'äS' => 'è³•',
  'äT' => 'è³�',
  'äU' => 'è³—',
  'äV' => 'è¶–',
  'äW' => 'è¸‰',
  'äX' => 'è¸‚',
  'äY' => 'è·¿',
  'äZ' => 'è¸�',
  'ä[' => 'è·½',
  'ä\\' => 'è¸Š',
  'ä]' => 'è¸ƒ',
  'ä^' => 'è¸‡',
  'ä_' => 'è¸†',
  'ä`' => 'è¸…',
  'äa' => 'è·¾',
  'äb' => 'è¸€',
  'äc' => 'è¸„',
  'äd' => 'è¼�',
  'äe' => 'è¼‘',
  'äf' => 'è¼Ž',
  'äg' => 'è¼�',
  'äh' => 'é„£',
  'äi' => 'é„œ',
  'äj' => 'é„ ',
  'äk' => 'é„¢',
  'äl' => 'é„Ÿ',
  'äm' => 'é„�',
  'än' => 'é„š',
  'äo' => 'é„¤',
  'äp' => 'é„¡',
  'äq' => 'é„›',
  'är' => 'é…º',
  'äs' => 'é…²',
  'ät' => 'é…¹',
  'äu' => 'é…³',
  'äv' => 'éŠ¥',
  'äw' => 'éŠ¤',
  'äx' => 'é‰¶',
  'äy' => 'éŠ›',
  'äz' => 'é‰º',
  'ä{' => 'éŠ ',
  'ä|' => 'éŠ”',
  'ä}' => 'éŠª',
  'ä~' => 'éŠ�',
  'ä¡' => 'éŠ¦',
  'ä¢' => 'éŠš',
  'ä£' => 'éŠ«',
  'ä¤' => 'é‰¹',
  'ä¥' => 'éŠ—',
  'ä¦' => 'é‰¿',
  'ä§' => 'éŠ£',
  'ä¨' => 'é‹®',
  'ä©' => 'éŠŽ',
  'äª' => 'éŠ‚',
  'ä«' => 'éŠ•',
  'ä¬' => 'éŠ¢',
  'ä­' => 'é‰½',
  'ä®' => 'éŠˆ',
  'ä¯' => 'éŠ¡',
  'ä°' => 'éŠŠ',
  'ä±' => 'éŠ†',
  'ä²' => 'éŠŒ',
  'ä³' => 'éŠ™',
  'ä´' => 'éŠ§',
  'äµ' => 'é‰¾',
  'ä¶' => 'éŠ‡',
  'ä·' => 'éŠ©',
  'ä¸' => 'éŠ�',
  'ä¹' => 'éŠ‹',
  'äº' => 'éˆ­',
  'ä»' => 'éšž',
  'ä¼' => 'éš¡',
  'ä½' => 'é›¿',
  'ä¾' => 'é�˜',
  'ä¿' => 'é�½',
  'äÀ' => 'é�º',
  'äÁ' => 'é�¾',
  'äÂ' => 'éžƒ',
  'äÃ' => 'éž€',
  'äÄ' => 'éž‚',
  'äÅ' => 'é�»',
  'äÆ' => 'éž„',
  'äÇ' => 'éž�',
  'äÈ' => 'é�¿',
  'äÉ' => 'éŸŽ',
  'äÊ' => 'éŸ�',
  'äË' => 'é –',
  'äÌ' => 'é¢­',
  'äÍ' => 'é¢®',
  'äÎ' => 'é¤‚',
  'äÏ' => 'é¤€',
  'äÐ' => 'é¤‡',
  'äÑ' => 'é¦�',
  'äÒ' => 'é¦œ',
  'äÓ' => 'é§ƒ',
  'äÔ' => 'é¦¹',
  'äÕ' => 'é¦»',
  'äÖ' => 'é¦º',
  'ä×' => 'é§‚',
  'äØ' => 'é¦½',
  'äÙ' => 'é§‡',
  'äÚ' => 'éª±',
  'äÛ' => 'é«£',
  'äÜ' => 'é«§',
  'äÝ' => 'é¬¾',
  'äÞ' => 'é¬¿',
  'äß' => 'é­ ',
  'äà' => 'é­¡',
  'äá' => 'é­Ÿ',
  'äâ' => 'é³±',
  'äã' => 'é³²',
  'ää' => 'é³µ',
  'äå' => 'éº§',
  'äæ' => 'åƒ¿',
  'äç' => 'å„ƒ',
  'äè' => 'å„°',
  'äé' => 'åƒ¸',
  'äê' => 'å„†',
  'äë' => 'å„‡',
  'äì' => 'åƒ¶',
  'äí' => 'åƒ¾',
  'äî' => 'å„‹',
  'äï' => 'å„Œ',
  'äð' => 'åƒ½',
  'äñ' => 'å„Š',
  'äò' => 'åŠ‹',
  'äó' => 'åŠŒ',
  'äô' => 'å‹±',
  'äõ' => 'å‹¯',
  'äö' => 'å™ˆ',
  'ä÷' => 'å™‚',
  'äø' => 'å™Œ',
  'äù' => 'å˜µ',
  'äú' => 'å™�',
  'äû' => 'å™Š',
  'äü' => 'å™‰',
  'äý' => 'å™†',
  'äþ' => 'å™˜',
  'å@' => 'å™š',
  'åA' => 'å™€',
  'åB' => 'å˜³',
  'åC' => 'å˜½',
  'åD' => 'å˜¬',
  'åE' => 'å˜¾',
  'åF' => 'å˜¸',
  'åG' => 'å˜ª',
  'åH' => 'å˜º',
  'åI' => 'åœš',
  'åJ' => 'å¢«',
  'åK' => 'å¢�',
  'åL' => 'å¢±',
  'åM' => 'å¢ ',
  'åN' => 'å¢£',
  'åO' => 'å¢¯',
  'åP' => 'å¢¬',
  'åQ' => 'å¢¥',
  'åR' => 'å¢¡',
  'åS' => 'å£¿',
  'åT' => 'å«¿',
  'åU' => 'å«´',
  'åV' => 'å«½',
  'åW' => 'å«·',
  'åX' => 'å«¶',
  'åY' => 'å¬ƒ',
  'åZ' => 'å«¸',
  'å[' => 'å¬‚',
  'å\\' => 'å«¹',
  'å]' => 'å¬�',
  'å^' => 'å¬‡',
  'å_' => 'å¬…',
  'å`' => 'å¬�',
  'åa' => 'å±§',
  'åb' => 'å¶™',
  'åc' => 'å¶—',
  'åd' => 'å¶Ÿ',
  'åe' => 'å¶’',
  'åf' => 'å¶¢',
  'åg' => 'å¶“',
  'åh' => 'å¶•',
  'åi' => 'å¶ ',
  'åj' => 'å¶œ',
  'åk' => 'å¶¡',
  'ål' => 'å¶š',
  'åm' => 'å¶ž',
  'ån' => 'å¹©',
  'åo' => 'å¹�',
  'åp' => 'å¹ ',
  'åq' => 'å¹œ',
  'år' => 'ç·³',
  'ås' => 'å»›',
  'åt' => 'å»ž',
  'åu' => 'å»¡',
  'åv' => 'å½‰',
  'åw' => 'å¾²',
  'åx' => 'æ†‹',
  'åy' => 'æ†ƒ',
  'åz' => 'æ…¹',
  'å{' => 'æ†±',
  'å|' => 'æ†°',
  'å}' => 'æ†¢',
  'å~' => 'æ†‰',
  'å¡' => 'æ†›',
  'å¢' => 'æ†“',
  'å£' => 'æ†¯',
  'å¤' => 'æ†­',
  'å¥' => 'æ†Ÿ',
  'å¦' => 'æ†’',
  'å§' => 'æ†ª',
  'å¨' => 'æ†¡',
  'å©' => 'æ†�',
  'åª' => 'æ…¦',
  'å«' => 'æ†³',
  'å¬' => 'æˆ­',
  'å­' => 'æ‘®',
  'å®' => 'æ‘°',
  'å¯' => 'æ’–',
  'å°' => 'æ’ ',
  'å±' => 'æ’…',
  'å²' => 'æ’—',
  'å³' => 'æ’œ',
  'å´' => 'æ’�',
  'åµ' => 'æ’‹',
  'å¶' => 'æ’Š',
  'å·' => 'æ’Œ',
  'å¸' => 'æ’£',
  'å¹' => 'æ’Ÿ',
  'åº' => 'æ‘¨',
  'å»' => 'æ’±',
  'å¼' => 'æ’˜',
  'å½' => 'æ•¶',
  'å¾' => 'æ•º',
  'å¿' => 'æ•¹',
  'åÀ' => 'æ•»',
  'åÁ' => 'æ–²',
  'åÂ' => 'æ–³',
  'åÃ' => 'æšµ',
  'åÄ' => 'æš°',
  'åÅ' => 'æš©',
  'åÆ' => 'æš²',
  'åÇ' => 'æš·',
  'åÈ' => 'æšª',
  'åÉ' => 'æš¯',
  'åÊ' => 'æ¨€',
  'åË' => 'æ¨†',
  'åÌ' => 'æ¨—',
  'åÍ' => 'æ§¥',
  'åÎ' => 'æ§¸',
  'åÏ' => 'æ¨•',
  'åÐ' => 'æ§±',
  'åÑ' => 'æ§¤',
  'åÒ' => 'æ¨ ',
  'åÓ' => 'æ§¿',
  'åÔ' => 'æ§¬',
  'åÕ' => 'æ§¢',
  'åÖ' => 'æ¨›',
  'å×' => 'æ¨�',
  'åØ' => 'æ§¾',
  'åÙ' => 'æ¨§',
  'åÚ' => 'æ§²',
  'åÛ' => 'æ§®',
  'åÜ' => 'æ¨”',
  'åÝ' => 'æ§·',
  'åÞ' => 'æ§§',
  'åß' => 'æ©€',
  'åà' => 'æ¨ˆ',
  'åá' => 'æ§¦',
  'åâ' => 'æ§»',
  'åã' => 'æ¨�',
  'åä' => 'æ§¼',
  'åå' => 'æ§«',
  'åæ' => 'æ¨‰',
  'åç' => 'æ¨„',
  'åè' => 'æ¨˜',
  'åé' => 'æ¨¥',
  'åê' => 'æ¨�',
  'åë' => 'æ§¶',
  'åì' => 'æ¨¦',
  'åí' => 'æ¨‡',
  'åî' => 'æ§´',
  'åï' => 'æ¨–',
  'åð' => 'æ­‘',
  'åñ' => 'æ®¥',
  'åò' => 'æ®£',
  'åó' => 'æ®¢',
  'åô' => 'æ®¦',
  'åõ' => 'æ°�',
  'åö' => 'æ°€',
  'å÷' => 'æ¯¿',
  'åø' => 'æ°‚',
  'åù' => 'æ½�',
  'åú' => 'æ¼¦',
  'åû' => 'æ½¾',
  'åü' => 'æ¾‡',
  'åý' => 'æ¿†',
  'åþ' => 'æ¾’',
  'æ@' => 'æ¾�',
  'æA' => 'æ¾‰',
  'æB' => 'æ¾Œ',
  'æC' => 'æ½¢',
  'æD' => 'æ½�',
  'æE' => 'æ¾…',
  'æF' => 'æ½š',
  'æG' => 'æ¾–',
  'æH' => 'æ½¶',
  'æI' => 'æ½¬',
  'æJ' => 'æ¾‚',
  'æK' => 'æ½•',
  'æL' => 'æ½²',
  'æM' => 'æ½’',
  'æN' => 'æ½�',
  'æO' => 'æ½—',
  'æP' => 'æ¾”',
  'æQ' => 'æ¾“',
  'æR' => 'æ½�',
  'æS' => 'æ¼€',
  'æT' => 'æ½¡',
  'æU' => 'æ½«',
  'æV' => 'æ½½',
  'æW' => 'æ½§',
  'æX' => 'æ¾�',
  'æY' => 'æ½“',
  'æZ' => 'æ¾‹',
  'æ[' => 'æ½©',
  'æ\\' => 'æ½¿',
  'æ]' => 'æ¾•',
  'æ^' => 'æ½£',
  'æ_' => 'æ½·',
  'æ`' => 'æ½ª',
  'æa' => 'æ½»',
  'æb' => 'ç†²',
  'æc' => 'ç†¯',
  'æd' => 'ç†›',
  'æe' => 'ç†°',
  'æf' => 'ç† ',
  'æg' => 'ç†š',
  'æh' => 'ç†©',
  'æi' => 'ç†µ',
  'æj' => 'ç†�',
  'æk' => 'ç†¥',
  'æl' => 'ç†ž',
  'æm' => 'ç†¤',
  'æn' => 'ç†¡',
  'æo' => 'ç†ª',
  'æp' => 'ç†œ',
  'æq' => 'ç†§',
  'ær' => 'ç†³',
  'æs' => 'çŠ˜',
  'æt' => 'çŠš',
  'æu' => 'ç�˜',
  'æv' => 'ç�’',
  'æw' => 'ç�ž',
  'æx' => 'ç�Ÿ',
  'æy' => 'ç� ',
  'æz' => 'ç��',
  'æ{' => 'ç�›',
  'æ|' => 'ç�¡',
  'æ}' => 'ç�š',
  'æ~' => 'ç�™',
  'æ¡' => 'ç�¢',
  'æ¢' => 'ç’‡',
  'æ£' => 'ç’‰',
  'æ¤' => 'ç’Š',
  'æ¥' => 'ç’†',
  'æ¦' => 'ç’�',
  'æ§' => 'ç‘½',
  'æ¨' => 'ç’…',
  'æ©' => 'ç’ˆ',
  'æª' => 'ç‘¼',
  'æ«' => 'ç‘¹',
  'æ¬' => 'ç”ˆ',
  'æ­' => 'ç”‡',
  'æ®' => 'ç•¾',
  'æ¯' => 'ç˜¥',
  'æ°' => 'ç˜ž',
  'æ±' => 'ç˜™',
  'æ²' => 'ç˜�',
  'æ³' => 'ç˜œ',
  'æ´' => 'ç˜£',
  'æµ' => 'ç˜š',
  'æ¶' => 'ç˜¨',
  'æ·' => 'ç˜›',
  'æ¸' => 'çšœ',
  'æ¹' => 'çš�',
  'æº' => 'çšž',
  'æ»' => 'çš›',
  'æ¼' => 'çž�',
  'æ½' => 'çž�',
  'æ¾' => 'çž‰',
  'æ¿' => 'çžˆ',
  'æÀ' => 'ç£�',
  'æÁ' => 'ç¢»',
  'æÂ' => 'ç£�',
  'æÃ' => 'ç£Œ',
  'æÄ' => 'ç£‘',
  'æÅ' => 'ç£Ž',
  'æÆ' => 'ç£”',
  'æÇ' => 'ç£ˆ',
  'æÈ' => 'ç£ƒ',
  'æÉ' => 'ç£„',
  'æÊ' => 'ç£‰',
  'æË' => 'ç¦š',
  'æÌ' => 'ç¦¡',
  'æÍ' => 'ç¦ ',
  'æÎ' => 'ç¦œ',
  'æÏ' => 'ç¦¢',
  'æÐ' => 'ç¦›',
  'æÑ' => 'æ­¶',
  'æÒ' => 'ç¨¹',
  'æÓ' => 'çª²',
  'æÔ' => 'çª´',
  'æÕ' => 'çª³',
  'æÖ' => 'ç®·',
  'æ×' => 'ç¯‹',
  'æØ' => 'ç®¾',
  'æÙ' => 'ç®¬',
  'æÚ' => 'ç¯Ž',
  'æÛ' => 'ç®¯',
  'æÜ' => 'ç®¹',
  'æÝ' => 'ç¯Š',
  'æÞ' => 'ç®µ',
  'æß' => 'ç³…',
  'æà' => 'ç³ˆ',
  'æá' => 'ç³Œ',
  'æâ' => 'ç³‹',
  'æã' => 'ç··',
  'æä' => 'ç·›',
  'æå' => 'ç·ª',
  'ææ' => 'ç·§',
  'æç' => 'ç·—',
  'æè' => 'ç·¡',
  'æé' => 'ç¸ƒ',
  'æê' => 'ç·º',
  'æë' => 'ç·¦',
  'æì' => 'ç·¶',
  'æí' => 'ç·±',
  'æî' => 'ç·°',
  'æï' => 'ç·®',
  'æð' => 'ç·Ÿ',
  'æñ' => 'ç½¶',
  'æò' => 'ç¾¬',
  'æó' => 'ç¾°',
  'æô' => 'ç¾­',
  'æõ' => 'ç¿­',
  'æö' => 'ç¿«',
  'æ÷' => 'ç¿ª',
  'æø' => 'ç¿¬',
  'æù' => 'ç¿¦',
  'æú' => 'ç¿¨',
  'æû' => 'è�¤',
  'æü' => 'è�§',
  'æý' => 'è†£',
  'æþ' => 'è†Ÿ',
  'ç@' => 'è†ž',
  'çA' => 'è†•',
  'çB' => 'è†¢',
  'çC' => 'è†™',
  'çD' => 'è†—',
  'çE' => 'èˆ–',
  'çF' => 'è‰�',
  'çG' => 'è‰“',
  'çH' => 'è‰’',
  'çI' => 'è‰�',
  'çJ' => 'è‰Ž',
  'çK' => 'è‰‘',
  'çL' => 'è”¤',
  'çM' => 'è”»',
  'çN' => 'è”�',
  'çO' => 'è”€',
  'çP' => 'è”©',
  'çQ' => 'è”Ž',
  'çR' => 'è”‰',
  'çS' => 'è”�',
  'çT' => 'è”Ÿ',
  'çU' => 'è”Š',
  'çV' => 'è”§',
  'çW' => 'è”œ',
  'çX' => 'è“»',
  'çY' => 'è”«',
  'çZ' => 'è“º',
  'ç[' => 'è”ˆ',
  'ç\\' => 'è”Œ',
  'ç]' => 'è“´',
  'ç^' => 'è”ª',
  'ç_' => 'è“²',
  'ç`' => 'è”•',
  'ça' => 'è“·',
  'çb' => 'è“«',
  'çc' => 'è“³',
  'çd' => 'è“¼',
  'çe' => 'è”’',
  'çf' => 'è“ª',
  'çg' => 'è“©',
  'çh' => 'è”–',
  'çi' => 'è“¾',
  'çj' => 'è”¨',
  'çk' => 'è”�',
  'çl' => 'è”®',
  'çm' => 'è”‚',
  'çn' => 'è“½',
  'ço' => 'è”ž',
  'çp' => 'è“¶',
  'çq' => 'è”±',
  'çr' => 'è”¦',
  'çs' => 'è“§',
  'çt' => 'è“¨',
  'çu' => 'è“°',
  'çv' => 'è“¯',
  'çw' => 'è“¹',
  'çx' => 'è”˜',
  'çy' => 'è” ',
  'çz' => 'è”°',
  'ç{' => 'è”‹',
  'ç|' => 'è”™',
  'ç}' => 'è”¯',
  'ç~' => 'è™¢',
  'ç¡' => 'è�–',
  'ç¢' => 'è�£',
  'ç£' => 'è�¤',
  'ç¤' => 'è�·',
  'ç¥' => 'èŸ¡',
  'ç¦' => 'è�³',
  'ç§' => 'è�˜',
  'ç¨' => 'è�”',
  'ç©' => 'è�›',
  'çª' => 'è�’',
  'ç«' => 'è�¡',
  'ç¬' => 'è�š',
  'ç­' => 'è�‘',
  'ç®' => 'è�ž',
  'ç¯' => 'è�­',
  'ç°' => 'è�ª',
  'ç±' => 'è��',
  'ç²' => 'è�Ž',
  'ç³' => 'è�Ÿ',
  'ç´' => 'è��',
  'çµ' => 'è�¯',
  'ç¶' => 'è�¬',
  'ç·' => 'è�º',
  'ç¸' => 'è�®',
  'ç¹' => 'è�œ',
  'çº' => 'è�¥',
  'ç»' => 'è��',
  'ç¼' => 'è�»',
  'ç½' => 'è�µ',
  'ç¾' => 'è�¢',
  'ç¿' => 'è�§',
  'çÀ' => 'è�©',
  'çÁ' => 'è¡š',
  'çÂ' => 'è¤…',
  'çÃ' => 'è¤Œ',
  'çÄ' => 'è¤”',
  'çÅ' => 'è¤‹',
  'çÆ' => 'è¤—',
  'çÇ' => 'è¤˜',
  'çÈ' => 'è¤™',
  'çÉ' => 'è¤†',
  'çÊ' => 'è¤–',
  'çË' => 'è¤‘',
  'çÌ' => 'è¤Ž',
  'çÍ' => 'è¤‰',
  'çÎ' => 'è¦¢',
  'çÏ' => 'è¦¤',
  'çÐ' => 'è¦£',
  'çÑ' => 'è§­',
  'çÒ' => 'è§°',
  'çÓ' => 'è§¬',
  'çÔ' => 'è«�',
  'çÕ' => 'è«†',
  'çÖ' => 'èª¸',
  'ç×' => 'è«“',
  'çØ' => 'è«‘',
  'çÙ' => 'è«”',
  'çÚ' => 'è«•',
  'çÛ' => 'èª»',
  'çÜ' => 'è«—',
  'çÝ' => 'èª¾',
  'çÞ' => 'è«€',
  'çß' => 'è«…',
  'çà' => 'è«˜',
  'çá' => 'è«ƒ',
  'çâ' => 'èªº',
  'çã' => 'èª½',
  'çä' => 'è«™',
  'çå' => 'è°¾',
  'çæ' => 'è±�',
  'çç' => 'è²�',
  'çè' => 'è³¥',
  'çé' => 'è³Ÿ',
  'çê' => 'è³™',
  'çë' => 'è³¨',
  'çì' => 'è³š',
  'çí' => 'è³�',
  'çî' => 'è³§',
  'çï' => 'è¶ ',
  'çð' => 'è¶œ',
  'çñ' => 'è¶¡',
  'çò' => 'è¶›',
  'çó' => 'è¸ ',
  'çô' => 'è¸£',
  'çõ' => 'è¸¥',
  'çö' => 'è¸¤',
  'ç÷' => 'è¸®',
  'çø' => 'è¸•',
  'çù' => 'è¸›',
  'çú' => 'è¸–',
  'çû' => 'è¸‘',
  'çü' => 'è¸™',
  'çý' => 'è¸¦',
  'çþ' => 'è¸§',
  'è@' => 'è¸”',
  'èA' => 'è¸’',
  'èB' => 'è¸˜',
  'èC' => 'è¸“',
  'èD' => 'è¸œ',
  'èE' => 'è¸—',
  'èF' => 'è¸š',
  'èG' => 'è¼¬',
  'èH' => 'è¼¤',
  'èI' => 'è¼˜',
  'èJ' => 'è¼š',
  'èK' => 'è¼ ',
  'èL' => 'è¼£',
  'èM' => 'è¼–',
  'èN' => 'è¼—',
  'èO' => 'é�³',
  'èP' => 'é�°',
  'èQ' => 'é�¯',
  'èR' => 'é�§',
  'èS' => 'é�«',
  'èT' => 'é„¯',
  'èU' => 'é„«',
  'èV' => 'é„©',
  'èW' => 'é„ª',
  'èX' => 'é„²',
  'èY' => 'é„¦',
  'èZ' => 'é„®',
  'è[' => 'é†…',
  'è\\' => 'é††',
  'è]' => 'é†Š',
  'è^' => 'é†�',
  'è_' => 'é†‚',
  'è`' => 'é†„',
  'èa' => 'é†€',
  'èb' => 'é‹�',
  'èc' => 'é‹ƒ',
  'èd' => 'é‹„',
  'èe' => 'é‹€',
  'èf' => 'é‹™',
  'èg' => 'éŠ¶',
  'èh' => 'é‹�',
  'èi' => 'é‹±',
  'èj' => 'é‹Ÿ',
  'èk' => 'é‹˜',
  'èl' => 'é‹©',
  'èm' => 'é‹—',
  'èn' => 'é‹�',
  'èo' => 'é‹Œ',
  'èp' => 'é‹¯',
  'èq' => 'é‹‚',
  'èr' => 'é‹¨',
  'ès' => 'é‹Š',
  'èt' => 'é‹ˆ',
  'èu' => 'é‹Ž',
  'èv' => 'é‹¦',
  'èw' => 'é‹�',
  'èx' => 'é‹•',
  'èy' => 'é‹‰',
  'èz' => 'é‹ ',
  'è{' => 'é‹ž',
  'è|' => 'é‹§',
  'è}' => 'é‹‘',
  'è~' => 'é‹“',
  'è¡' => 'éŠµ',
  'è¢' => 'é‹¡',
  'è£' => 'é‹†',
  'è¤' => 'éŠ´',
  'è¥' => 'é•¼',
  'è¦' => 'é–¬',
  'è§' => 'é–«',
  'è¨' => 'é–®',
  'è©' => 'é–°',
  'èª' => 'éš¤',
  'è«' => 'éš¢',
  'è¬' => 'é›“',
  'è­' => 'éœ…',
  'è®' => 'éœˆ',
  'è¯' => 'éœ‚',
  'è°' => 'é�š',
  'è±' => 'éžŠ',
  'è²' => 'éžŽ',
  'è³' => 'éžˆ',
  'è´' => 'éŸ�',
  'èµ' => 'éŸ�',
  'è¶' => 'é ž',
  'è·' => 'é �',
  'è¸' => 'é ¦',
  'è¹' => 'é ©',
  'èº' => 'é ¨',
  'è»' => 'é  ',
  'è¼' => 'é ›',
  'è½' => 'é §',
  'è¾' => 'é¢²',
  'è¿' => 'é¤ˆ',
  'èÀ' => 'é£º',
  'èÁ' => 'é¤‘',
  'èÂ' => 'é¤”',
  'èÃ' => 'é¤–',
  'èÄ' => 'é¤—',
  'èÅ' => 'é¤•',
  'èÆ' => 'é§œ',
  'èÇ' => 'é§�',
  'èÈ' => 'é§�',
  'èÉ' => 'é§“',
  'èÊ' => 'é§”',
  'èË' => 'é§Ž',
  'èÌ' => 'é§‰',
  'èÍ' => 'é§–',
  'èÎ' => 'é§˜',
  'èÏ' => 'é§‹',
  'èÐ' => 'é§—',
  'èÑ' => 'é§Œ',
  'èÒ' => 'éª³',
  'èÓ' => 'é«¬',
  'èÔ' => 'é««',
  'èÕ' => 'é«³',
  'èÖ' => 'é«²',
  'è×' => 'é«±',
  'èØ' => 'é­†',
  'èÙ' => 'é­ƒ',
  'èÚ' => 'é­§',
  'èÛ' => 'é­´',
  'èÜ' => 'é­±',
  'èÝ' => 'é­¦',
  'èÞ' => 'é­¶',
  'èß' => 'é­µ',
  'èà' => 'é­°',
  'èá' => 'é­¨',
  'èâ' => 'é­¤',
  'èã' => 'é­¬',
  'èä' => 'é³¼',
  'èå' => 'é³º',
  'èæ' => 'é³½',
  'èç' => 'é³¿',
  'èè' => 'é³·',
  'èé' => 'é´‡',
  'èê' => 'é´€',
  'èë' => 'é³¹',
  'èì' => 'é³»',
  'èí' => 'é´ˆ',
  'èî' => 'é´…',
  'èï' => 'é´„',
  'èð' => 'éºƒ',
  'èñ' => 'é»“',
  'èò' => 'é¼�',
  'èó' => 'é¼�',
  'èô' => 'å„œ',
  'èõ' => 'å„“',
  'èö' => 'å„—',
  'è÷' => 'å„š',
  'èø' => 'å„‘',
  'èù' => 'å‡ž',
  'èú' => 'åŒ´',
  'èû' => 'å�¡',
  'èü' => 'å™°',
  'èý' => 'å™ ',
  'èþ' => 'å™®',
  'é@' => 'å™³',
  'éA' => 'å™¦',
  'éB' => 'å™£',
  'éC' => 'å™­',
  'éD' => 'å™²',
  'éE' => 'å™ž',
  'éF' => 'å™·',
  'éG' => 'åœœ',
  'éH' => 'åœ›',
  'éI' => 'å£ˆ',
  'éJ' => 'å¢½',
  'éK' => 'å£‰',
  'éL' => 'å¢¿',
  'éM' => 'å¢º',
  'éN' => 'å£‚',
  'éO' => 'å¢¼',
  'éP' => 'å£†',
  'éQ' => 'å¬—',
  'éR' => 'å¬™',
  'éS' => 'å¬›',
  'éT' => 'å¬¡',
  'éU' => 'å¬”',
  'éV' => 'å¬“',
  'éW' => 'å¬�',
  'éX' => 'å¬–',
  'éY' => 'å¬¨',
  'éZ' => 'å¬š',
  'é[' => 'å¬ ',
  'é\\' => 'å¬ž',
  'é]' => 'å¯¯',
  'é^' => 'å¶¬',
  'é_' => 'å¶±',
  'é`' => 'å¶©',
  'éa' => 'å¶§',
  'éb' => 'å¶µ',
  'éc' => 'å¶°',
  'éd' => 'å¶®',
  'ée' => 'å¶ª',
  'éf' => 'å¶¨',
  'ég' => 'å¶²',
  'éh' => 'å¶­',
  'éi' => 'å¶¯',
  'éj' => 'å¶´',
  'ék' => 'å¹§',
  'él' => 'å¹¨',
  'ém' => 'å¹¦',
  'én' => 'å¹¯',
  'éo' => 'å»©',
  'ép' => 'å»§',
  'éq' => 'å»¦',
  'ér' => 'å»¨',
  'és' => 'å»¥',
  'ét' => 'å½‹',
  'éu' => 'å¾¼',
  'év' => 'æ†�',
  'éw' => 'æ†¨',
  'éx' => 'æ†–',
  'éy' => 'æ‡…',
  'éz' => 'æ†´',
  'é{' => 'æ‡†',
  'é|' => 'æ‡�',
  'é}' => 'æ‡Œ',
  'é~' => 'æ†º',
  'é¡' => 'æ†¿',
  'é¢' => 'æ†¸',
  'é£' => 'æ†Œ',
  'é¤' => 'æ“—',
  'é¥' => 'æ“–',
  'é¦' => 'æ“�',
  'é§' => 'æ“�',
  'é¨' => 'æ“‰',
  'é©' => 'æ’½',
  'éª' => 'æ’‰',
  'é«' => 'æ“ƒ',
  'é¬' => 'æ“›',
  'é­' => 'æ“³',
  'é®' => 'æ“™',
  'é¯' => 'æ”³',
  'é°' => 'æ•¿',
  'é±' => 'æ•¼',
  'é²' => 'æ–¢',
  'é³' => 'æ›ˆ',
  'é´' => 'æš¾',
  'éµ' => 'æ›€',
  'é¶' => 'æ›Š',
  'é·' => 'æ›‹',
  'é¸' => 'æ›�',
  'é¹' => 'æš½',
  'éº' => 'æš»',
  'é»' => 'æšº',
  'é¼' => 'æ›Œ',
  'é½' => 'æœ£',
  'é¾' => 'æ¨´',
  'é¿' => 'æ©¦',
  'éÀ' => 'æ©‰',
  'éÁ' => 'æ©§',
  'éÂ' => 'æ¨²',
  'éÃ' => 'æ©¨',
  'éÄ' => 'æ¨¾',
  'éÅ' => 'æ©�',
  'éÆ' => 'æ©­',
  'éÇ' => 'æ©¶',
  'éÈ' => 'æ©›',
  'éÉ' => 'æ©‘',
  'éÊ' => 'æ¨¨',
  'éË' => 'æ©š',
  'éÌ' => 'æ¨»',
  'éÍ' => 'æ¨¿',
  'éÎ' => 'æ©�',
  'éÏ' => 'æ©ª',
  'éÐ' => 'æ©¤',
  'éÑ' => 'æ©�',
  'éÒ' => 'æ©�',
  'éÓ' => 'æ©”',
  'éÔ' => 'æ©¯',
  'éÕ' => 'æ©©',
  'éÖ' => 'æ© ',
  'é×' => 'æ¨¼',
  'éØ' => 'æ©ž',
  'éÙ' => 'æ©–',
  'éÚ' => 'æ©•',
  'éÛ' => 'æ©�',
  'éÜ' => 'æ©Ž',
  'éÝ' => 'æ©†',
  'éÞ' => 'æ­•',
  'éß' => 'æ­”',
  'éà' => 'æ­–',
  'éá' => 'æ®§',
  'éâ' => 'æ®ª',
  'éã' => 'æ®«',
  'éä' => 'æ¯ˆ',
  'éå' => 'æ¯‡',
  'éæ' => 'æ°„',
  'éç' => 'æ°ƒ',
  'éè' => 'æ°†',
  'éé' => 'æ¾­',
  'éê' => 'æ¿‹',
  'éë' => 'æ¾£',
  'éì' => 'æ¿‡',
  'éí' => 'æ¾¼',
  'éî' => 'æ¿Ž',
  'éï' => 'æ¿ˆ',
  'éð' => 'æ½ž',
  'éñ' => 'æ¿„',
  'éò' => 'æ¾½',
  'éó' => 'æ¾ž',
  'éô' => 'æ¿Š',
  'éõ' => 'æ¾¨',
  'éö' => 'ç€„',
  'é÷' => 'æ¾¥',
  'éø' => 'æ¾®',
  'éù' => 'æ¾º',
  'éú' => 'æ¾¬',
  'éû' => 'æ¾ª',
  'éü' => 'æ¿�',
  'éý' => 'æ¾¿',
  'éþ' => 'æ¾¸',
  'ê@' => 'æ¾¢',
  'êA' => 'æ¿‰',
  'êB' => 'æ¾«',
  'êC' => 'æ¿�',
  'êD' => 'æ¾¯',
  'êE' => 'æ¾²',
  'êF' => 'æ¾°',
  'êG' => 'ç‡…',
  'êH' => 'ç‡‚',
  'êI' => 'ç†¿',
  'êJ' => 'ç†¸',
  'êK' => 'ç‡–',
  'êL' => 'ç‡€',
  'êM' => 'ç‡�',
  'êN' => 'ç‡‹',
  'êO' => 'ç‡”',
  'êP' => 'ç‡Š',
  'êQ' => 'ç‡‡',
  'êR' => 'ç‡�',
  'êS' => 'ç†½',
  'êT' => 'ç‡˜',
  'êU' => 'ç†¼',
  'êV' => 'ç‡†',
  'êW' => 'ç‡š',
  'êX' => 'ç‡›',
  'êY' => 'çŠ�',
  'êZ' => 'çŠž',
  'ê[' => 'ç�©',
  'ê\\' => 'ç�¦',
  'ê]' => 'ç�§',
  'ê^' => 'ç�¬',
  'ê_' => 'ç�¥',
  'ê`' => 'ç�«',
  'êa' => 'ç�ª',
  'êb' => 'ç‘¿',
  'êc' => 'ç’š',
  'êd' => 'ç’ ',
  'êe' => 'ç’”',
  'êf' => 'ç’’',
  'êg' => 'ç’•',
  'êh' => 'ç’¡',
  'êi' => 'ç”‹',
  'êj' => 'ç–€',
  'êk' => 'ç˜¯',
  'êl' => 'ç˜­',
  'êm' => 'ç˜±',
  'ên' => 'ç˜½',
  'êo' => 'ç˜³',
  'êp' => 'ç˜¼',
  'êq' => 'ç˜µ',
  'êr' => 'ç˜²',
  'ês' => 'ç˜°',
  'êt' => 'çš»',
  'êu' => 'ç›¦',
  'êv' => 'çžš',
  'êw' => 'çž�',
  'êx' => 'çž¡',
  'êy' => 'çžœ',
  'êz' => 'çž›',
  'ê{' => 'çž¢',
  'ê|' => 'çž£',
  'ê}' => 'çž•',
  'ê~' => 'çž™',
  'ê¡' => 'çž—',
  'ê¢' => 'ç£�',
  'ê£' => 'ç£©',
  'ê¤' => 'ç£¥',
  'ê¥' => 'ç£ª',
  'ê¦' => 'ç£ž',
  'ê§' => 'ç££',
  'ê¨' => 'ç£›',
  'ê©' => 'ç£¡',
  'êª' => 'ç£¢',
  'ê«' => 'ç£­',
  'ê¬' => 'ç£Ÿ',
  'ê­' => 'ç£ ',
  'ê®' => 'ç¦¤',
  'ê¯' => 'ç©„',
  'ê°' => 'ç©ˆ',
  'ê±' => 'ç©‡',
  'ê²' => 'çª¶',
  'ê³' => 'çª¸',
  'ê´' => 'çªµ',
  'êµ' => 'çª±',
  'ê¶' => 'çª·',
  'ê·' => 'ç¯ž',
  'ê¸' => 'ç¯£',
  'ê¹' => 'ç¯§',
  'êº' => 'ç¯�',
  'ê»' => 'ç¯•',
  'ê¼' => 'ç¯¥',
  'ê½' => 'ç¯š',
  'ê¾' => 'ç¯¨',
  'ê¿' => 'ç¯¹',
  'êÀ' => 'ç¯”',
  'êÁ' => 'ç¯ª',
  'êÂ' => 'ç¯¢',
  'êÃ' => 'ç¯œ',
  'êÄ' => 'ç¯«',
  'êÅ' => 'ç¯˜',
  'êÆ' => 'ç¯Ÿ',
  'êÇ' => 'ç³’',
  'êÈ' => 'ç³”',
  'êÉ' => 'ç³—',
  'êÊ' => 'ç³�',
  'êË' => 'ç³‘',
  'êÌ' => 'ç¸’',
  'êÍ' => 'ç¸¡',
  'êÎ' => 'ç¸—',
  'êÏ' => 'ç¸Œ',
  'êÐ' => 'ç¸Ÿ',
  'êÑ' => 'ç¸ ',
  'êÒ' => 'ç¸“',
  'êÓ' => 'ç¸Ž',
  'êÔ' => 'ç¸œ',
  'êÕ' => 'ç¸•',
  'êÖ' => 'ç¸š',
  'ê×' => 'ç¸¢',
  'êØ' => 'ç¸‹',
  'êÙ' => 'ç¸�',
  'êÚ' => 'ç¸–',
  'êÛ' => 'ç¸�',
  'êÜ' => 'ç¸”',
  'êÝ' => 'ç¸¥',
  'êÞ' => 'ç¸¤',
  'êß' => 'ç½ƒ',
  'êà' => 'ç½»',
  'êá' => 'ç½¼',
  'êâ' => 'ç½º',
  'êã' => 'ç¾±',
  'êä' => 'ç¿¯',
  'êå' => 'è€ª',
  'êæ' => 'è€©',
  'êç' => 'è�¬',
  'êè' => 'è†±',
  'êé' => 'è†¦',
  'êê' => 'è†®',
  'êë' => 'è†¹',
  'êì' => 'è†µ',
  'êí' => 'è†«',
  'êî' => 'è†°',
  'êï' => 'è†¬',
  'êð' => 'è†´',
  'êñ' => 'è†²',
  'êò' => 'è†·',
  'êó' => 'è†§',
  'êô' => 'è‡²',
  'êõ' => 'è‰•',
  'êö' => 'è‰–',
  'ê÷' => 'è‰—',
  'êø' => 'è•–',
  'êù' => 'è•…',
  'êú' => 'è•«',
  'êû' => 'è•�',
  'êü' => 'è•“',
  'êý' => 'è•¡',
  'êþ' => 'è•˜',
  'ë@' => 'è•€',
  'ëA' => 'è•†',
  'ëB' => 'è•¤',
  'ëC' => 'è•�',
  'ëD' => 'è•¢',
  'ëE' => 'è•„',
  'ëF' => 'è•‘',
  'ëG' => 'è•‡',
  'ëH' => 'è•£',
  'ëI' => 'è”¾',
  'ëJ' => 'è•›',
  'ëK' => 'è•±',
  'ëL' => 'è•Ž',
  'ëM' => 'è•®',
  'ëN' => 'è•µ',
  'ëO' => 'è••',
  'ëP' => 'è•§',
  'ëQ' => 'è• ',
  'ëR' => 'è–Œ',
  'ëS' => 'è•¦',
  'ëT' => 'è•�',
  'ëU' => 'è•”',
  'ëV' => 'è•¥',
  'ëW' => 'è•¬',
  'ëX' => 'è™£',
  'ëY' => 'è™¥',
  'ëZ' => 'è™¤',
  'ë[' => 'èž›',
  'ë\\' => 'èž�',
  'ë]' => 'èž—',
  'ë^' => 'èž“',
  'ë_' => 'èž’',
  'ë`' => 'èžˆ',
  'ëa' => 'èž�',
  'ëb' => 'èž–',
  'ëc' => 'èž˜',
  'ëd' => 'è�¹',
  'ëe' => 'èž‡',
  'ëf' => 'èž£',
  'ëg' => 'èž…',
  'ëh' => 'èž�',
  'ëi' => 'èž‘',
  'ëj' => 'èž�',
  'ëk' => 'èž„',
  'ël' => 'èž”',
  'ëm' => 'èžœ',
  'ën' => 'èžš',
  'ëo' => 'èž‰',
  'ëp' => 'è¤ž',
  'ëq' => 'è¤¦',
  'ër' => 'è¤°',
  'ës' => 'è¤­',
  'ët' => 'è¤®',
  'ëu' => 'è¤§',
  'ëv' => 'è¤±',
  'ëw' => 'è¤¢',
  'ëx' => 'è¤©',
  'ëy' => 'è¤£',
  'ëz' => 'è¤¯',
  'ë{' => 'è¤¬',
  'ë|' => 'è¤Ÿ',
  'ë}' => 'è§±',
  'ë~' => 'è« ',
  'ë¡' => 'è«¢',
  'ë¢' => 'è«²',
  'ë£' => 'è«´',
  'ë¤' => 'è«µ',
  'ë¥' => 'è«�',
  'ë¦' => 'è¬”',
  'ë§' => 'è«¤',
  'ë¨' => 'è«Ÿ',
  'ë©' => 'è«°',
  'ëª' => 'è«ˆ',
  'ë«' => 'è«ž',
  'ë¬' => 'è«¡',
  'ë­' => 'è«¨',
  'ë®' => 'è«¿',
  'ë¯' => 'è«¯',
  'ë°' => 'è«»',
  'ë±' => 'è²‘',
  'ë²' => 'è²’',
  'ë³' => 'è²�',
  'ë´' => 'è³µ',
  'ëµ' => 'è³®',
  'ë¶' => 'è³±',
  'ë·' => 'è³°',
  'ë¸' => 'è³³',
  'ë¹' => 'èµ¬',
  'ëº' => 'èµ®',
  'ë»' => 'è¶¥',
  'ë¼' => 'è¶§',
  'ë½' => 'è¸³',
  'ë¾' => 'è¸¾',
  'ë¿' => 'è¸¸',
  'ëÀ' => 'è¹€',
  'ëÁ' => 'è¹…',
  'ëÂ' => 'è¸¶',
  'ëÃ' => 'è¸¼',
  'ëÄ' => 'è¸½',
  'ëÅ' => 'è¹�',
  'ëÆ' => 'è¸°',
  'ëÇ' => 'è¸¿',
  'ëÈ' => 'èº½',
  'ëÉ' => 'è¼¶',
  'ëÊ' => 'è¼®',
  'ëË' => 'è¼µ',
  'ëÌ' => 'è¼²',
  'ëÍ' => 'è¼¹',
  'ëÎ' => 'è¼·',
  'ëÏ' => 'è¼´',
  'ëÐ' => 'é�¶',
  'ëÑ' => 'é�¹',
  'ëÒ' => 'é�»',
  'ëÓ' => 'é‚†',
  'ëÔ' => 'éƒº',
  'ëÕ' => 'é„³',
  'ëÖ' => 'é„µ',
  'ë×' => 'é„¶',
  'ëØ' => 'é†“',
  'ëÙ' => 'é†�',
  'ëÚ' => 'é†‘',
  'ëÛ' => 'é†�',
  'ëÜ' => 'é†�',
  'ëÝ' => 'éŒ§',
  'ëÞ' => 'éŒž',
  'ëß' => 'éŒˆ',
  'ëà' => 'éŒŸ',
  'ëá' => 'éŒ†',
  'ëâ' => 'éŒ�',
  'ëã' => 'é�º',
  'ëä' => 'éŒ¸',
  'ëå' => 'éŒ¼',
  'ëæ' => 'éŒ›',
  'ëç' => 'éŒ£',
  'ëè' => 'éŒ’',
  'ëé' => 'éŒ�',
  'ëê' => 'é�†',
  'ëë' => 'éŒ­',
  'ëì' => 'éŒŽ',
  'ëí' => 'éŒ�',
  'ëî' => 'é‹‹',
  'ëï' => 'éŒ�',
  'ëð' => 'é‹º',
  'ëñ' => 'éŒ¥',
  'ëò' => 'éŒ“',
  'ëó' => 'é‹¹',
  'ëô' => 'é‹·',
  'ëõ' => 'éŒ´',
  'ëö' => 'éŒ‚',
  'ë÷' => 'éŒ¤',
  'ëø' => 'é‹¿',
  'ëù' => 'éŒ©',
  'ëú' => 'éŒ¹',
  'ëû' => 'éŒµ',
  'ëü' => 'éŒª',
  'ëý' => 'éŒ”',
  'ëþ' => 'éŒŒ',
  'ì@' => 'éŒ‹',
  'ìA' => 'é‹¾',
  'ìB' => 'éŒ‰',
  'ìC' => 'éŒ€',
  'ìD' => 'é‹»',
  'ìE' => 'éŒ–',
  'ìF' => 'é–¼',
  'ìG' => 'é—�',
  'ìH' => 'é–¾',
  'ìI' => 'é–¹',
  'ìJ' => 'é–º',
  'ìK' => 'é–¶',
  'ìL' => 'é–¿',
  'ìM' => 'é–µ',
  'ìN' => 'é–½',
  'ìO' => 'éš©',
  'ìP' => 'é›”',
  'ìQ' => 'éœ‹',
  'ìR' => 'éœ’',
  'ìS' => 'éœ�',
  'ìT' => 'éž™',
  'ìU' => 'éž—',
  'ìV' => 'éž”',
  'ìW' => 'éŸ°',
  'ìX' => 'éŸ¸',
  'ìY' => 'é µ',
  'ìZ' => 'é ¯',
  'ì[' => 'é ²',
  'ì\\' => 'é¤¤',
  'ì]' => 'é¤Ÿ',
  'ì^' => 'é¤§',
  'ì_' => 'é¤©',
  'ì`' => 'é¦ž',
  'ìa' => 'é§®',
  'ìb' => 'é§¬',
  'ìc' => 'é§¥',
  'ìd' => 'é§¤',
  'ìe' => 'é§°',
  'ìf' => 'é§£',
  'ìg' => 'é§ª',
  'ìh' => 'é§©',
  'ìi' => 'é§§',
  'ìj' => 'éª¹',
  'ìk' => 'éª¿',
  'ìl' => 'éª´',
  'ìm' => 'éª»',
  'ìn' => 'é«¶',
  'ìo' => 'é«º',
  'ìp' => 'é«¹',
  'ìq' => 'é«·',
  'ìr' => 'é¬³',
  'ìs' => 'é®€',
  'ìt' => 'é®…',
  'ìu' => 'é®‡',
  'ìv' => 'é­¼',
  'ìw' => 'é­¾',
  'ìx' => 'é­»',
  'ìy' => 'é®‚',
  'ìz' => 'é®“',
  'ì{' => 'é®’',
  'ì|' => 'é®�',
  'ì}' => 'é­º',
  'ì~' => 'é®•',
  'ì¡' => 'é­½',
  'ì¢' => 'é®ˆ',
  'ì£' => 'é´¥',
  'ì¤' => 'é´—',
  'ì¥' => 'é´ ',
  'ì¦' => 'é´ž',
  'ì§' => 'é´”',
  'ì¨' => 'é´©',
  'ì©' => 'é´�',
  'ìª' => 'é´˜',
  'ì«' => 'é´¢',
  'ì¬' => 'é´�',
  'ì­' => 'é´™',
  'ì®' => 'é´Ÿ',
  'ì¯' => 'éºˆ',
  'ì°' => 'éº†',
  'ì±' => 'éº‡',
  'ì²' => 'éº®',
  'ì³' => 'éº­',
  'ì´' => 'é»•',
  'ìµ' => 'é»–',
  'ì¶' => 'é»º',
  'ì·' => 'é¼’',
  'ì¸' => 'é¼½',
  'ì¹' => 'å„¦',
  'ìº' => 'å„¥',
  'ì»' => 'å„¢',
  'ì¼' => 'å„¤',
  'ì½' => 'å„ ',
  'ì¾' => 'å„©',
  'ì¿' => 'å‹´',
  'ìÀ' => 'åš“',
  'ìÁ' => 'åšŒ',
  'ìÂ' => 'åš�',
  'ìÃ' => 'åš†',
  'ìÄ' => 'åš„',
  'ìÅ' => 'åšƒ',
  'ìÆ' => 'å™¾',
  'ìÇ' => 'åš‚',
  'ìÈ' => 'å™¿',
  'ìÉ' => 'åš�',
  'ìÊ' => 'å£–',
  'ìË' => 'å£”',
  'ìÌ' => 'å£�',
  'ìÍ' => 'å£’',
  'ìÎ' => 'å¬­',
  'ìÏ' => 'å¬¥',
  'ìÐ' => 'å¬²',
  'ìÑ' => 'å¬£',
  'ìÒ' => 'å¬¬',
  'ìÓ' => 'å¬§',
  'ìÔ' => 'å¬¦',
  'ìÕ' => 'å¬¯',
  'ìÖ' => 'å¬®',
  'ì×' => 'å­»',
  'ìØ' => 'å¯±',
  'ìÙ' => 'å¯²',
  'ìÚ' => 'å¶·',
  'ìÛ' => 'å¹¬',
  'ìÜ' => 'å¹ª',
  'ìÝ' => 'å¾¾',
  'ìÞ' => 'å¾»',
  'ìß' => 'æ‡ƒ',
  'ìà' => 'æ†µ',
  'ìá' => 'æ†¼',
  'ìâ' => 'æ‡§',
  'ìã' => 'æ‡ ',
  'ìä' => 'æ‡¥',
  'ìå' => 'æ‡¤',
  'ìæ' => 'æ‡¨',
  'ìç' => 'æ‡ž',
  'ìè' => 'æ“¯',
  'ìé' => 'æ“©',
  'ìê' => 'æ“£',
  'ìë' => 'æ“«',
  'ìì' => 'æ“¤',
  'ìí' => 'æ“¨',
  'ìî' => 'æ–�',
  'ìï' => 'æ–€',
  'ìð' => 'æ–¶',
  'ìñ' => 'æ—š',
  'ìò' => 'æ›’',
  'ìó' => 'æª�',
  'ìô' => 'æª–',
  'ìõ' => 'æª�',
  'ìö' => 'æª¥',
  'ì÷' => 'æª‰',
  'ìø' => 'æªŸ',
  'ìù' => 'æª›',
  'ìú' => 'æª¡',
  'ìû' => 'æªž',
  'ìü' => 'æª‡',
  'ìý' => 'æª“',
  'ìþ' => 'æªŽ',
  'í@' => 'æª•',
  'íA' => 'æªƒ',
  'íB' => 'æª¨',
  'íC' => 'æª¤',
  'íD' => 'æª‘',
  'íE' => 'æ©¿',
  'íF' => 'æª¦',
  'íG' => 'æªš',
  'íH' => 'æª…',
  'íI' => 'æªŒ',
  'íJ' => 'æª’',
  'íK' => 'æ­›',
  'íL' => 'æ®­',
  'íM' => 'æ°‰',
  'íN' => 'æ¿Œ',
  'íO' => 'æ¾©',
  'íP' => 'æ¿´',
  'íQ' => 'æ¿”',
  'íR' => 'æ¿£',
  'íS' => 'æ¿œ',
  'íT' => 'æ¿­',
  'íU' => 'æ¿§',
  'íV' => 'æ¿¦',
  'íW' => 'æ¿ž',
  'íX' => 'æ¿²',
  'íY' => 'æ¿�',
  'íZ' => 'æ¿¢',
  'í[' => 'æ¿¨',
  'í\\' => 'ç‡¡',
  'í]' => 'ç‡±',
  'í^' => 'ç‡¨',
  'í_' => 'ç‡²',
  'í`' => 'ç‡¤',
  'ía' => 'ç‡°',
  'íb' => 'ç‡¢',
  'íc' => 'ç�³',
  'íd' => 'ç�®',
  'íe' => 'ç�¯',
  'íf' => 'ç’—',
  'íg' => 'ç’²',
  'íh' => 'ç’«',
  'íi' => 'ç’�',
  'íj' => 'ç’ª',
  'ík' => 'ç’­',
  'íl' => 'ç’±',
  'ím' => 'ç’¥',
  'ín' => 'ç’¯',
  'ío' => 'ç”�',
  'íp' => 'ç”‘',
  'íq' => 'ç”’',
  'ír' => 'ç”�',
  'ís' => 'ç–„',
  'ít' => 'ç™ƒ',
  'íu' => 'ç™ˆ',
  'ív' => 'ç™‰',
  'íw' => 'ç™‡',
  'íx' => 'çš¤',
  'íy' => 'ç›©',
  'íz' => 'çžµ',
  'í{' => 'çž«',
  'í|' => 'çž²',
  'í}' => 'çž·',
  'í~' => 'çž¶',
  'í¡' => 'çž´',
  'í¢' => 'çž±',
  'í£' => 'çž¨',
  'í¤' => 'çŸ°',
  'í¥' => 'ç£³',
  'í¦' => 'ç£½',
  'í§' => 'ç¤‚',
  'í¨' => 'ç£»',
  'í©' => 'ç£¼',
  'íª' => 'ç£²',
  'í«' => 'ç¤…',
  'í¬' => 'ç£¹',
  'í­' => 'ç£¾',
  'í®' => 'ç¤„',
  'í¯' => 'ç¦«',
  'í°' => 'ç¦¨',
  'í±' => 'ç©œ',
  'í²' => 'ç©›',
  'í³' => 'ç©–',
  'í´' => 'ç©˜',
  'íµ' => 'ç©”',
  'í¶' => 'ç©š',
  'í·' => 'çª¾',
  'í¸' => 'ç«€',
  'í¹' => 'ç«�',
  'íº' => 'ç°…',
  'í»' => 'ç°�',
  'í¼' => 'ç¯²',
  'í½' => 'ç°€',
  'í¾' => 'ç¯¿',
  'í¿' => 'ç¯»',
  'íÀ' => 'ç°Ž',
  'íÁ' => 'ç¯´',
  'íÂ' => 'ç°‹',
  'íÃ' => 'ç¯³',
  'íÄ' => 'ç°‚',
  'íÅ' => 'ç°‰',
  'íÆ' => 'ç°ƒ',
  'íÇ' => 'ç°�',
  'íÈ' => 'ç¯¸',
  'íÉ' => 'ç¯½',
  'íÊ' => 'ç°†',
  'íË' => 'ç¯°',
  'íÌ' => 'ç¯±',
  'íÍ' => 'ç°�',
  'íÎ' => 'ç°Š',
  'íÏ' => 'ç³¨',
  'íÐ' => 'ç¸­',
  'íÑ' => 'ç¸¼',
  'íÒ' => 'ç¹‚',
  'íÓ' => 'ç¸³',
  'íÔ' => 'é¡ˆ',
  'íÕ' => 'ç¸¸',
  'íÖ' => 'ç¸ª',
  'í×' => 'ç¹‰',
  'íØ' => 'ç¹€',
  'íÙ' => 'ç¹‡',
  'íÚ' => 'ç¸©',
  'íÛ' => 'ç¹Œ',
  'íÜ' => 'ç¸°',
  'íÝ' => 'ç¸»',
  'íÞ' => 'ç¸¶',
  'íß' => 'ç¹„',
  'íà' => 'ç¸º',
  'íá' => 'ç½…',
  'íâ' => 'ç½¿',
  'íã' => 'ç½¾',
  'íä' => 'ç½½',
  'íå' => 'ç¿´',
  'íæ' => 'ç¿²',
  'íç' => 'è€¬',
  'íè' => 'è†»',
  'íé' => 'è‡„',
  'íê' => 'è‡Œ',
  'íë' => 'è‡Š',
  'íì' => 'è‡…',
  'íí' => 'è‡‡',
  'íî' => 'è†¼',
  'íï' => 'è‡©',
  'íð' => 'è‰›',
  'íñ' => 'è‰š',
  'íò' => 'è‰œ',
  'íó' => 'è–ƒ',
  'íô' => 'è–€',
  'íõ' => 'è–�',
  'íö' => 'è–§',
  'í÷' => 'è–•',
  'íø' => 'è– ',
  'íù' => 'è–‹',
  'íú' => 'è–£',
  'íû' => 'è•»',
  'íü' => 'è–¤',
  'íý' => 'è–š',
  'íþ' => 'è–ž',
  'î@' => 'è•·',
  'îA' => 'è•¼',
  'îB' => 'è–‰',
  'îC' => 'è–¡',
  'îD' => 'è•º',
  'îE' => 'è•¸',
  'îF' => 'è•—',
  'îG' => 'è–Ž',
  'îH' => 'è––',
  'îI' => 'è–†',
  'îJ' => 'è–�',
  'îK' => 'è–™',
  'îL' => 'è–�',
  'îM' => 'è–�',
  'îN' => 'è–¢',
  'îO' => 'è–‚',
  'îP' => 'è–ˆ',
  'îQ' => 'è–…',
  'îR' => 'è•¹',
  'îS' => 'è•¶',
  'îT' => 'è–˜',
  'îU' => 'è–�',
  'îV' => 'è–Ÿ',
  'îW' => 'è™¨',
  'îX' => 'èž¾',
  'îY' => 'èžª',
  'îZ' => 'èž­',
  'î[' => 'èŸ…',
  'î\\' => 'èž°',
  'î]' => 'èž¬',
  'î^' => 'èž¹',
  'î_' => 'èžµ',
  'î`' => 'èž¼',
  'îa' => 'èž®',
  'îb' => 'èŸ‰',
  'îc' => 'èŸƒ',
  'îd' => 'èŸ‚',
  'îe' => 'èŸŒ',
  'îf' => 'èž·',
  'îg' => 'èž¯',
  'îh' => 'èŸ„',
  'îi' => 'èŸŠ',
  'îj' => 'èž´',
  'îk' => 'èž¶',
  'îl' => 'èž¿',
  'îm' => 'èž¸',
  'în' => 'èž½',
  'îo' => 'èŸž',
  'îp' => 'èž²',
  'îq' => 'è¤µ',
  'îr' => 'è¤³',
  'îs' => 'è¤¼',
  'ît' => 'è¤¾',
  'îu' => 'è¥�',
  'îv' => 'è¥’',
  'îw' => 'è¤·',
  'îx' => 'è¥‚',
  'îy' => 'è¦­',
  'îz' => 'è¦¯',
  'î{' => 'è¦®',
  'î|' => 'è§²',
  'î}' => 'è§³',
  'î~' => 'è¬ž',
  'î¡' => 'è¬˜',
  'î¢' => 'è¬–',
  'î£' => 'è¬‘',
  'î¤' => 'è¬…',
  'î¥' => 'è¬‹',
  'î¦' => 'è¬¢',
  'î§' => 'è¬�',
  'î¨' => 'è¬’',
  'î©' => 'è¬•',
  'îª' => 'è¬‡',
  'î«' => 'è¬�',
  'î¬' => 'è¬ˆ',
  'î­' => 'è¬†',
  'î®' => 'è¬œ',
  'î¯' => 'è¬“',
  'î°' => 'è¬š',
  'î±' => 'è±�',
  'î²' => 'è±°',
  'î³' => 'è±²',
  'î´' => 'è±±',
  'îµ' => 'è±¯',
  'î¶' => 'è²•',
  'î·' => 'è²”',
  'î¸' => 'è³¹',
  'î¹' => 'èµ¯',
  'îº' => 'è¹Ž',
  'î»' => 'è¹�',
  'î¼' => 'è¹“',
  'î½' => 'è¹�',
  'î¾' => 'è¹Œ',
  'î¿' => 'è¹‡',
  'îÀ' => 'è½ƒ',
  'îÁ' => 'è½€',
  'îÂ' => 'é‚…',
  'îÃ' => 'é�¾',
  'îÄ' => 'é„¸',
  'îÅ' => 'é†š',
  'îÆ' => 'é†¢',
  'îÇ' => 'é†›',
  'îÈ' => 'é†™',
  'îÉ' => 'é†Ÿ',
  'îÊ' => 'é†¡',
  'îË' => 'é†�',
  'îÌ' => 'é† ',
  'îÍ' => 'éŽ¡',
  'îÎ' => 'éŽƒ',
  'îÏ' => 'éŽ¯',
  'îÐ' => 'é�¤',
  'îÑ' => 'é�–',
  'îÒ' => 'é�‡',
  'îÓ' => 'é�¼',
  'îÔ' => 'é�˜',
  'îÕ' => 'é�œ',
  'îÖ' => 'é�¶',
  'î×' => 'é�‰',
  'îØ' => 'é��',
  'îÙ' => 'é�‘',
  'îÚ' => 'é� ',
  'îÛ' => 'é�­',
  'îÜ' => 'éŽ�',
  'îÝ' => 'é�Œ',
  'îÞ' => 'é�ª',
  'îß' => 'é�¹',
  'îà' => 'é�—',
  'îá' => 'é�•',
  'îâ' => 'é�’',
  'îã' => 'é��',
  'îä' => 'é�±',
  'îå' => 'é�·',
  'îæ' => 'é�»',
  'îç' => 'é�¡',
  'îè' => 'é�ž',
  'îé' => 'é�£',
  'îê' => 'é�§',
  'îë' => 'éŽ€',
  'îì' => 'é�Ž',
  'îí' => 'é�™',
  'îî' => 'é—‡',
  'îï' => 'é—€',
  'îð' => 'é—‰',
  'îñ' => 'é—ƒ',
  'îò' => 'é—…',
  'îó' => 'é–·',
  'îô' => 'éš®',
  'îõ' => 'éš°',
  'îö' => 'éš¬',
  'î÷' => 'éœ ',
  'îø' => 'éœŸ',
  'îù' => 'éœ˜',
  'îú' => 'éœ�',
  'îû' => 'éœ™',
  'îü' => 'éžš',
  'îý' => 'éž¡',
  'îþ' => 'éžœ',
  'ï@' => 'éžž',
  'ïA' => 'éž�',
  'ïB' => 'éŸ•',
  'ïC' => 'éŸ”',
  'ïD' => 'éŸ±',
  'ïE' => 'é¡�',
  'ïF' => 'é¡„',
  'ïG' => 'é¡Š',
  'ïH' => 'é¡‰',
  'ïI' => 'é¡…',
  'ïJ' => 'é¡ƒ',
  'ïK' => 'é¤¥',
  'ïL' => 'é¤«',
  'ïM' => 'é¤¬',
  'ïN' => 'é¤ª',
  'ïO' => 'é¤³',
  'ïP' => 'é¤²',
  'ïQ' => 'é¤¯',
  'ïR' => 'é¤­',
  'ïS' => 'é¤±',
  'ïT' => 'é¤°',
  'ïU' => 'é¦˜',
  'ïV' => 'é¦£',
  'ïW' => 'é¦¡',
  'ïX' => 'é¨‚',
  'ïY' => 'é§º',
  'ïZ' => 'é§´',
  'ï[' => 'é§·',
  'ï\\' => 'é§¹',
  'ï]' => 'é§¸',
  'ï^' => 'é§¶',
  'ï_' => 'é§»',
  'ï`' => 'é§½',
  'ïa' => 'é§¾',
  'ïb' => 'é§¼',
  'ïc' => 'é¨ƒ',
  'ïd' => 'éª¾',
  'ïe' => 'é«¾',
  'ïf' => 'é«½',
  'ïg' => 'é¬�',
  'ïh' => 'é«¼',
  'ïi' => 'é­ˆ',
  'ïj' => 'é®š',
  'ïk' => 'é®¨',
  'ïl' => 'é®ž',
  'ïm' => 'é®›',
  'ïn' => 'é®¦',
  'ïo' => 'é®¡',
  'ïp' => 'é®¥',
  'ïq' => 'é®¤',
  'ïr' => 'é®†',
  'ïs' => 'é®¢',
  'ït' => 'é® ',
  'ïu' => 'é®¯',
  'ïv' => 'é´³',
  'ïw' => 'éµ�',
  'ïx' => 'éµ§',
  'ïy' => 'é´¶',
  'ïz' => 'é´®',
  'ï{' => 'é´¯',
  'ï|' => 'é´±',
  'ï}' => 'é´¸',
  'ï~' => 'é´°',
  'ï¡' => 'éµ…',
  'ï¢' => 'éµ‚',
  'ï£' => 'éµƒ',
  'ï¤' => 'é´¾',
  'ï¥' => 'é´·',
  'ï¦' => 'éµ€',
  'ï§' => 'é´½',
  'ï¨' => 'ç¿µ',
  'ï©' => 'é´­',
  'ïª' => 'éºŠ',
  'ï«' => 'éº‰',
  'ï¬' => 'éº�',
  'ï­' => 'éº°',
  'ï®' => 'é»ˆ',
  'ï¯' => 'é»š',
  'ï°' => 'é»»',
  'ï±' => 'é»¿',
  'ï²' => 'é¼¤',
  'ï³' => 'é¼£',
  'ï´' => 'é¼¢',
  'ïµ' => 'é½”',
  'ï¶' => 'é¾ ',
  'ï·' => 'å„±',
  'ï¸' => 'å„­',
  'ï¹' => 'å„®',
  'ïº' => 'åš˜',
  'ï»' => 'åšœ',
  'ï¼' => 'åš—',
  'ï½' => 'åšš',
  'ï¾' => 'åš�',
  'ï¿' => 'åš™',
  'ïÀ' => 'å¥°',
  'ïÁ' => 'å¬¼',
  'ïÂ' => 'å±©',
  'ïÃ' => 'å±ª',
  'ïÄ' => 'å·€',
  'ïÅ' => 'å¹­',
  'ïÆ' => 'å¹®',
  'ïÇ' => 'æ‡˜',
  'ïÈ' => 'æ‡Ÿ',
  'ïÉ' => 'æ‡­',
  'ïÊ' => 'æ‡®',
  'ïË' => 'æ‡±',
  'ïÌ' => 'æ‡ª',
  'ïÍ' => 'æ‡°',
  'ïÎ' => 'æ‡«',
  'ïÏ' => 'æ‡–',
  'ïÐ' => 'æ‡©',
  'ïÑ' => 'æ“¿',
  'ïÒ' => 'æ”„',
  'ïÓ' => 'æ“½',
  'ïÔ' => 'æ“¸',
  'ïÕ' => 'æ”�',
  'ïÖ' => 'æ”ƒ',
  'ï×' => 'æ“¼',
  'ïØ' => 'æ–”',
  'ïÙ' => 'æ—›',
  'ïÚ' => 'æ›š',
  'ïÛ' => 'æ››',
  'ïÜ' => 'æ›˜',
  'ïÝ' => 'æ«…',
  'ïÞ' => 'æª¹',
  'ïß' => 'æª½',
  'ïà' => 'æ«¡',
  'ïá' => 'æ«†',
  'ïâ' => 'æªº',
  'ïã' => 'æª¶',
  'ïä' => 'æª·',
  'ïå' => 'æ«‡',
  'ïæ' => 'æª´',
  'ïç' => 'æª­',
  'ïè' => 'æ­ž',
  'ïé' => 'æ¯‰',
  'ïê' => 'æ°‹',
  'ïë' => 'ç€‡',
  'ïì' => 'ç€Œ',
  'ïí' => 'ç€�',
  'ïî' => 'ç€�',
  'ïï' => 'ç€…',
  'ïð' => 'ç€”',
  'ïñ' => 'ç€Ž',
  'ïò' => 'æ¿¿',
  'ïó' => 'ç€€',
  'ïô' => 'æ¿»',
  'ïõ' => 'ç€¦',
  'ïö' => 'æ¿¼',
  'ï÷' => 'æ¿·',
  'ïø' => 'ç€Š',
  'ïù' => 'çˆ�',
  'ïú' => 'ç‡¿',
  'ïû' => 'ç‡¹',
  'ïü' => 'çˆƒ',
  'ïý' => 'ç‡½',
  'ïþ' => 'ç�¶',
  'ð@' => 'ç’¸',
  'ðA' => 'ç“€',
  'ðB' => 'ç’µ',
  'ðC' => 'ç“�',
  'ðD' => 'ç’¾',
  'ðE' => 'ç’¶',
  'ðF' => 'ç’»',
  'ðG' => 'ç“‚',
  'ðH' => 'ç””',
  'ðI' => 'ç”“',
  'ðJ' => 'ç™œ',
  'ðK' => 'ç™¤',
  'ðL' => 'ç™™',
  'ðM' => 'ç™�',
  'ðN' => 'ç™“',
  'ðO' => 'ç™—',
  'ðP' => 'ç™š',
  'ðQ' => 'çš¦',
  'ðR' => 'çš½',
  'ðS' => 'ç›¬',
  'ðT' => 'çŸ‚',
  'ðU' => 'çžº',
  'ðV' => 'ç£¿',
  'ðW' => 'ç¤Œ',
  'ðX' => 'ç¤“',
  'ðY' => 'ç¤”',
  'ðZ' => 'ç¤‰',
  'ð[' => 'ç¤�',
  'ð\\' => 'ç¤’',
  'ð]' => 'ç¤‘',
  'ð^' => 'ç¦­',
  'ð_' => 'ç¦¬',
  'ð`' => 'ç©Ÿ',
  'ða' => 'ç°œ',
  'ðb' => 'ç°©',
  'ðc' => 'ç°™',
  'ðd' => 'ç° ',
  'ðe' => 'ç°Ÿ',
  'ðf' => 'ç°­',
  'ðg' => 'ç°�',
  'ðh' => 'ç°¦',
  'ði' => 'ç°¨',
  'ðj' => 'ç°¢',
  'ðk' => 'ç°¥',
  'ðl' => 'ç°°',
  'ðm' => 'ç¹œ',
  'ðn' => 'ç¹�',
  'ðo' => 'ç¹–',
  'ðp' => 'ç¹£',
  'ðq' => 'ç¹˜',
  'ðr' => 'ç¹¢',
  'ðs' => 'ç¹Ÿ',
  'ðt' => 'ç¹‘',
  'ðu' => 'ç¹ ',
  'ðv' => 'ç¹—',
  'ðw' => 'ç¹“',
  'ðx' => 'ç¾µ',
  'ðy' => 'ç¾³',
  'ðz' => 'ç¿·',
  'ð{' => 'ç¿¸',
  'ð|' => 'è�µ',
  'ð}' => 'è‡‘',
  'ð~' => 'è‡’',
  'ð¡' => 'è‡�',
  'ð¢' => 'è‰Ÿ',
  'ð£' => 'è‰ž',
  'ð¤' => 'è–´',
  'ð¥' => 'è—†',
  'ð¦' => 'è—€',
  'ð§' => 'è—ƒ',
  'ð¨' => 'è—‚',
  'ð©' => 'è–³',
  'ðª' => 'è–µ',
  'ð«' => 'è–½',
  'ð¬' => 'è—‡',
  'ð­' => 'è—„',
  'ð®' => 'è–¿',
  'ð¯' => 'è—‹',
  'ð°' => 'è—Ž',
  'ð±' => 'è—ˆ',
  'ð²' => 'è—…',
  'ð³' => 'è–±',
  'ð´' => 'è–¶',
  'ðµ' => 'è—’',
  'ð¶' => 'è˜¤',
  'ð·' => 'è–¸',
  'ð¸' => 'è–·',
  'ð¹' => 'è–¾',
  'ðº' => 'è™©',
  'ð»' => 'èŸ§',
  'ð¼' => 'èŸ¦',
  'ð½' => 'èŸ¢',
  'ð¾' => 'èŸ›',
  'ð¿' => 'èŸ«',
  'ðÀ' => 'èŸª',
  'ðÁ' => 'èŸ¥',
  'ðÂ' => 'èŸŸ',
  'ðÃ' => 'èŸ³',
  'ðÄ' => 'èŸ¤',
  'ðÅ' => 'èŸ”',
  'ðÆ' => 'èŸœ',
  'ðÇ' => 'èŸ“',
  'ðÈ' => 'èŸ­',
  'ðÉ' => 'èŸ˜',
  'ðÊ' => 'èŸ£',
  'ðË' => 'èž¤',
  'ðÌ' => 'èŸ—',
  'ðÍ' => 'èŸ™',
  'ðÎ' => 'è �',
  'ðÏ' => 'èŸ´',
  'ðÐ' => 'èŸ¨',
  'ðÑ' => 'èŸ�',
  'ðÒ' => 'è¥“',
  'ðÓ' => 'è¥‹',
  'ðÔ' => 'è¥�',
  'ðÕ' => 'è¥Œ',
  'ðÖ' => 'è¥†',
  'ð×' => 'è¥�',
  'ðØ' => 'è¥‘',
  'ðÙ' => 'è¥‰',
  'ðÚ' => 'è¬ª',
  'ðÛ' => 'è¬§',
  'ðÜ' => 'è¬£',
  'ðÝ' => 'è¬³',
  'ðÞ' => 'è¬°',
  'ðß' => 'è¬µ',
  'ðà' => 'è­‡',
  'ðá' => 'è¬¯',
  'ðâ' => 'è¬¼',
  'ðã' => 'è¬¾',
  'ðä' => 'è¬±',
  'ðå' => 'è¬¥',
  'ðæ' => 'è¬·',
  'ðç' => 'è¬¦',
  'ðè' => 'è¬¶',
  'ðé' => 'è¬®',
  'ðê' => 'è¬¤',
  'ðë' => 'è¬»',
  'ðì' => 'è¬½',
  'ðí' => 'è¬º',
  'ðî' => 'è±‚',
  'ðï' => 'è±µ',
  'ðð' => 'è²™',
  'ðñ' => 'è²˜',
  'ðò' => 'è²—',
  'ðó' => 'è³¾',
  'ðô' => 'è´„',
  'ðõ' => 'è´‚',
  'ðö' => 'è´€',
  'ð÷' => 'è¹œ',
  'ðø' => 'è¹¢',
  'ðù' => 'è¹ ',
  'ðú' => 'è¹—',
  'ðû' => 'è¹–',
  'ðü' => 'è¹ž',
  'ðý' => 'è¹¥',
  'ðþ' => 'è¹§',
  'ñ@' => 'è¹›',
  'ñA' => 'è¹š',
  'ñB' => 'è¹¡',
  'ñC' => 'è¹�',
  'ñD' => 'è¹©',
  'ñE' => 'è¹”',
  'ñF' => 'è½†',
  'ñG' => 'è½‡',
  'ñH' => 'è½ˆ',
  'ñI' => 'è½‹',
  'ñJ' => 'é„¨',
  'ñK' => 'é„º',
  'ñL' => 'é„»',
  'ñM' => 'é„¾',
  'ñN' => 'é†¨',
  'ñO' => 'é†¥',
  'ñP' => 'é†§',
  'ñQ' => 'é†¯',
  'ñR' => 'é†ª',
  'ñS' => 'éŽµ',
  'ñT' => 'éŽŒ',
  'ñU' => 'éŽ’',
  'ñV' => 'éŽ·',
  'ñW' => 'éŽ›',
  'ñX' => 'éŽ�',
  'ñY' => 'éŽ‰',
  'ñZ' => 'éŽ§',
  'ñ[' => 'éŽŽ',
  'ñ\\' => 'éŽª',
  'ñ]' => 'éŽž',
  'ñ^' => 'éŽ¦',
  'ñ_' => 'éŽ•',
  'ñ`' => 'éŽˆ',
  'ña' => 'éŽ™',
  'ñb' => 'éŽŸ',
  'ñc' => 'éŽ�',
  'ñd' => 'éŽ±',
  'ñe' => 'éŽ‘',
  'ñf' => 'éŽ²',
  'ñg' => 'éŽ¤',
  'ñh' => 'éŽ¨',
  'ñi' => 'éŽ´',
  'ñj' => 'éŽ£',
  'ñk' => 'éŽ¥',
  'ñl' => 'é—’',
  'ñm' => 'é—“',
  'ñn' => 'é—‘',
  'ño' => 'éš³',
  'ñp' => 'é›—',
  'ñq' => 'é›š',
  'ñr' => 'å·‚',
  'ñs' => 'é›Ÿ',
  'ñt' => 'é›˜',
  'ñu' => 'é›�',
  'ñv' => 'éœ£',
  'ñw' => 'éœ¢',
  'ñx' => 'éœ¥',
  'ñy' => 'éž¬',
  'ñz' => 'éž®',
  'ñ{' => 'éž¨',
  'ñ|' => 'éž«',
  'ñ}' => 'éž¤',
  'ñ~' => 'éžª',
  'ñ¡' => 'éž¢',
  'ñ¢' => 'éž¥',
  'ñ£' => 'éŸ—',
  'ñ¤' => 'éŸ™',
  'ñ¥' => 'éŸ–',
  'ñ¦' => 'éŸ˜',
  'ñ§' => 'éŸº',
  'ñ¨' => 'é¡�',
  'ñ©' => 'é¡‘',
  'ñª' => 'é¡’',
  'ñ«' => 'é¢¸',
  'ñ¬' => 'é¥�',
  'ñ­' => 'é¤¼',
  'ñ®' => 'é¤º',
  'ñ¯' => 'é¨�',
  'ñ°' => 'é¨‹',
  'ñ±' => 'é¨‰',
  'ñ²' => 'é¨�',
  'ñ³' => 'é¨„',
  'ñ´' => 'é¨‘',
  'ñµ' => 'é¨Š',
  'ñ¶' => 'é¨…',
  'ñ·' => 'é¨‡',
  'ñ¸' => 'é¨†',
  'ñ¹' => 'é«€',
  'ñº' => 'é«œ',
  'ñ»' => 'é¬ˆ',
  'ñ¼' => 'é¬„',
  'ñ½' => 'é¬…',
  'ñ¾' => 'é¬©',
  'ñ¿' => 'é¬µ',
  'ñÀ' => 'é­Š',
  'ñÁ' => 'é­Œ',
  'ñÂ' => 'é­‹',
  'ñÃ' => 'é¯‡',
  'ñÄ' => 'é¯†',
  'ñÅ' => 'é¯ƒ',
  'ñÆ' => 'é®¿',
  'ñÇ' => 'é¯�',
  'ñÈ' => 'é®µ',
  'ñÉ' => 'é®¸',
  'ñÊ' => 'é¯“',
  'ñË' => 'é®¶',
  'ñÌ' => 'é¯„',
  'ñÍ' => 'é®¹',
  'ñÎ' => 'é®½',
  'ñÏ' => 'éµœ',
  'ñÐ' => 'éµ“',
  'ñÑ' => 'éµ�',
  'ñÒ' => 'éµŠ',
  'ñÓ' => 'éµ›',
  'ñÔ' => 'éµ‹',
  'ñÕ' => 'éµ™',
  'ñÖ' => 'éµ–',
  'ñ×' => 'éµŒ',
  'ñØ' => 'éµ—',
  'ñÙ' => 'éµ’',
  'ñÚ' => 'éµ”',
  'ñÛ' => 'éµŸ',
  'ñÜ' => 'éµ˜',
  'ñÝ' => 'éµš',
  'ñÞ' => 'éºŽ',
  'ñß' => 'éºŒ',
  'ñà' => 'é»Ÿ',
  'ñá' => 'é¼�',
  'ñâ' => 'é¼€',
  'ñã' => 'é¼–',
  'ñä' => 'é¼¥',
  'ñå' => 'é¼«',
  'ñæ' => 'é¼ª',
  'ñç' => 'é¼©',
  'ñè' => 'é¼¨',
  'ñé' => 'é½Œ',
  'ñê' => 'é½•',
  'ñë' => 'å„´',
  'ñì' => 'å„µ',
  'ñí' => 'åŠ–',
  'ñî' => 'å‹·',
  'ñï' => 'åŽ´',
  'ñð' => 'åš«',
  'ññ' => 'åš­',
  'ñò' => 'åš¦',
  'ñó' => 'åš§',
  'ñô' => 'åšª',
  'ñõ' => 'åš¬',
  'ñö' => 'å£š',
  'ñ÷' => 'å£�',
  'ñø' => 'å£›',
  'ñù' => 'å¤’',
  'ñú' => 'å¬½',
  'ñû' => 'å¬¾',
  'ñü' => 'å¬¿',
  'ñý' => 'å·ƒ',
  'ñþ' => 'å¹°',
  'ò@' => 'å¾¿',
  'òA' => 'æ‡»',
  'òB' => 'æ”‡',
  'òC' => 'æ”�',
  'òD' => 'æ”�',
  'òE' => 'æ”‰',
  'òF' => 'æ”Œ',
  'òG' => 'æ”Ž',
  'òH' => 'æ–„',
  'òI' => 'æ—ž',
  'òJ' => 'æ—�',
  'òK' => 'æ›ž',
  'òL' => 'æ«§',
  'òM' => 'æ« ',
  'òN' => 'æ«Œ',
  'òO' => 'æ«‘',
  'òP' => 'æ«™',
  'òQ' => 'æ«‹',
  'òR' => 'æ«Ÿ',
  'òS' => 'æ«œ',
  'òT' => 'æ«�',
  'òU' => 'æ««',
  'òV' => 'æ«�',
  'òW' => 'æ«�',
  'òX' => 'æ«ž',
  'òY' => 'æ­ ',
  'òZ' => 'æ®°',
  'ò[' => 'æ°Œ',
  'ò\\' => 'ç€™',
  'ò]' => 'ç€§',
  'ò^' => 'ç€ ',
  'ò_' => 'ç€–',
  'ò`' => 'ç€«',
  'òa' => 'ç€¡',
  'òb' => 'ç€¢',
  'òc' => 'ç€£',
  'òd' => 'ç€©',
  'òe' => 'ç€—',
  'òf' => 'ç€¤',
  'òg' => 'ç€œ',
  'òh' => 'ç€ª',
  'òi' => 'çˆŒ',
  'òj' => 'çˆŠ',
  'òk' => 'çˆ‡',
  'òl' => 'çˆ‚',
  'òm' => 'çˆ…',
  'òn' => 'çŠ¥',
  'òo' => 'çŠ¦',
  'òp' => 'çŠ¤',
  'òq' => 'çŠ£',
  'òr' => 'çŠ¡',
  'òs' => 'ç“‹',
  'òt' => 'ç“…',
  'òu' => 'ç’·',
  'òv' => 'ç“ƒ',
  'òw' => 'ç”–',
  'òx' => 'ç™ ',
  'òy' => 'çŸ‰',
  'òz' => 'çŸŠ',
  'ò{' => 'çŸ„',
  'ò|' => 'çŸ±',
  'ò}' => 'ç¤�',
  'ò~' => 'ç¤›',
  'ò¡' => 'ç¤¡',
  'ò¢' => 'ç¤œ',
  'ò£' => 'ç¤—',
  'ò¤' => 'ç¤ž',
  'ò¥' => 'ç¦°',
  'ò¦' => 'ç©§',
  'ò§' => 'ç©¨',
  'ò¨' => 'ç°³',
  'ò©' => 'ç°¼',
  'òª' => 'ç°¹',
  'ò«' => 'ç°¬',
  'ò¬' => 'ç°»',
  'ò­' => 'ç³¬',
  'ò®' => 'ç³ª',
  'ò¯' => 'ç¹¶',
  'ò°' => 'ç¹µ',
  'ò±' => 'ç¹¸',
  'ò²' => 'ç¹°',
  'ò³' => 'ç¹·',
  'ò´' => 'ç¹¯',
  'òµ' => 'ç¹º',
  'ò¶' => 'ç¹²',
  'ò·' => 'ç¹´',
  'ò¸' => 'ç¹¨',
  'ò¹' => 'ç½‹',
  'òº' => 'ç½Š',
  'ò»' => 'ç¾ƒ',
  'ò¼' => 'ç¾†',
  'ò½' => 'ç¾·',
  'ò¾' => 'ç¿½',
  'ò¿' => 'ç¿¾',
  'òÀ' => 'è�¸',
  'òÁ' => 'è‡—',
  'òÂ' => 'è‡•',
  'òÃ' => 'è‰¤',
  'òÄ' => 'è‰¡',
  'òÅ' => 'è‰£',
  'òÆ' => 'è—«',
  'òÇ' => 'è—±',
  'òÈ' => 'è—­',
  'òÉ' => 'è—™',
  'òÊ' => 'è—¡',
  'òË' => 'è—¨',
  'òÌ' => 'è—š',
  'òÍ' => 'è——',
  'òÎ' => 'è—¬',
  'òÏ' => 'è—²',
  'òÐ' => 'è—¸',
  'òÑ' => 'è—˜',
  'òÒ' => 'è—Ÿ',
  'òÓ' => 'è—£',
  'òÔ' => 'è—œ',
  'òÕ' => 'è—‘',
  'òÖ' => 'è—°',
  'ò×' => 'è—¦',
  'òØ' => 'è—¯',
  'òÙ' => 'è—ž',
  'òÚ' => 'è—¢',
  'òÛ' => 'è €',
  'òÜ' => 'èŸº',
  'òÝ' => 'è ƒ',
  'òÞ' => 'èŸ¶',
  'òß' => 'èŸ·',
  'òà' => 'è ‰',
  'òá' => 'è Œ',
  'òâ' => 'è ‹',
  'òã' => 'è †',
  'òä' => 'èŸ¼',
  'òå' => 'è ˆ',
  'òæ' => 'èŸ¿',
  'òç' => 'è Š',
  'òè' => 'è ‚',
  'òé' => 'è¥¢',
  'òê' => 'è¥š',
  'òë' => 'è¥›',
  'òì' => 'è¥—',
  'òí' => 'è¥¡',
  'òî' => 'è¥œ',
  'òï' => 'è¥˜',
  'òð' => 'è¥�',
  'òñ' => 'è¥™',
  'òò' => 'è¦ˆ',
  'òó' => 'è¦·',
  'òô' => 'è¦¶',
  'òõ' => 'è§¶',
  'òö' => 'è­�',
  'ò÷' => 'è­ˆ',
  'òø' => 'è­Š',
  'òù' => 'è­€',
  'òú' => 'è­“',
  'òû' => 'è­–',
  'òü' => 'è­”',
  'òý' => 'è­‹',
  'òþ' => 'è­•',
  'ó@' => 'è­‘',
  'óA' => 'è­‚',
  'óB' => 'è­’',
  'óC' => 'è­—',
  'óD' => 'è±ƒ',
  'óE' => 'è±·',
  'óF' => 'è±¶',
  'óG' => 'è²š',
  'óH' => 'è´†',
  'óI' => 'è´‡',
  'óJ' => 'è´‰',
  'óK' => 'è¶¬',
  'óL' => 'è¶ª',
  'óM' => 'è¶­',
  'óN' => 'è¶«',
  'óO' => 'è¹­',
  'óP' => 'è¹¸',
  'óQ' => 'è¹³',
  'óR' => 'è¹ª',
  'óS' => 'è¹¯',
  'óT' => 'è¹»',
  'óU' => 'è»‚',
  'óV' => 'è½’',
  'óW' => 'è½‘',
  'óX' => 'è½�',
  'óY' => 'è½�',
  'óZ' => 'è½“',
  'ó[' => 'è¾´',
  'ó\\' => 'é…€',
  'ó]' => 'é„¿',
  'ó^' => 'é†°',
  'ó_' => 'é†­',
  'ó`' => 'é�ž',
  'óa' => 'é�‡',
  'ób' => 'é��',
  'óc' => 'é�‚',
  'ód' => 'é�š',
  'óe' => 'é��',
  'óf' => 'é�¹',
  'óg' => 'é�¬',
  'óh' => 'é�Œ',
  'ói' => 'é�™',
  'ój' => 'éŽ©',
  'ók' => 'é�¦',
  'ól' => 'é�Š',
  'óm' => 'é�”',
  'ón' => 'é�®',
  'óo' => 'é�£',
  'óp' => 'é�•',
  'óq' => 'é�„',
  'ór' => 'é�Ž',
  'ós' => 'é�€',
  'ót' => 'é�’',
  'óu' => 'é�§',
  'óv' => 'é•½',
  'ów' => 'é—š',
  'óx' => 'é—›',
  'óy' => 'é›¡',
  'óz' => 'éœ©',
  'ó{' => 'éœ«',
  'ó|' => 'éœ¬',
  'ó}' => 'éœ¨',
  'ó~' => 'éœ¦',
  'ó¡' => 'éž³',
  'ó¢' => 'éž·',
  'ó£' => 'éž¶',
  'ó¤' => 'éŸ�',
  'ó¥' => 'éŸž',
  'ó¦' => 'éŸŸ',
  'ó§' => 'é¡œ',
  'ó¨' => 'é¡™',
  'ó©' => 'é¡�',
  'óª' => 'é¡—',
  'ó«' => 'é¢¿',
  'ó¬' => 'é¢½',
  'ó­' => 'é¢»',
  'ó®' => 'é¢¾',
  'ó¯' => 'é¥ˆ',
  'ó°' => 'é¥‡',
  'ó±' => 'é¥ƒ',
  'ó²' => 'é¦¦',
  'ó³' => 'é¦§',
  'ó´' => 'é¨š',
  'óµ' => 'é¨•',
  'ó¶' => 'é¨¥',
  'ó·' => 'é¨�',
  'ó¸' => 'é¨¤',
  'ó¹' => 'é¨›',
  'óº' => 'é¨¢',
  'ó»' => 'é¨ ',
  'ó¼' => 'é¨§',
  'ó½' => 'é¨£',
  'ó¾' => 'é¨ž',
  'ó¿' => 'é¨œ',
  'óÀ' => 'é¨”',
  'óÁ' => 'é«‚',
  'óÂ' => 'é¬‹',
  'óÃ' => 'é¬Š',
  'óÄ' => 'é¬Ž',
  'óÅ' => 'é¬Œ',
  'óÆ' => 'é¬·',
  'óÇ' => 'é¯ª',
  'óÈ' => 'é¯«',
  'óÉ' => 'é¯ ',
  'óÊ' => 'é¯ž',
  'óË' => 'é¯¤',
  'óÌ' => 'é¯¦',
  'óÍ' => 'é¯¢',
  'óÎ' => 'é¯°',
  'óÏ' => 'é¯”',
  'óÐ' => 'é¯—',
  'óÑ' => 'é¯¬',
  'óÒ' => 'é¯œ',
  'óÓ' => 'é¯™',
  'óÔ' => 'é¯¥',
  'óÕ' => 'é¯•',
  'óÖ' => 'é¯¡',
  'ó×' => 'é¯š',
  'óØ' => 'éµ·',
  'óÙ' => 'é¶�',
  'óÚ' => 'é¶Š',
  'óÛ' => 'é¶„',
  'óÜ' => 'é¶ˆ',
  'óÝ' => 'éµ±',
  'óÞ' => 'é¶€',
  'óß' => 'éµ¸',
  'óà' => 'é¶†',
  'óá' => 'é¶‹',
  'óâ' => 'é¶Œ',
  'óã' => 'éµ½',
  'óä' => 'éµ«',
  'óå' => 'éµ´',
  'óæ' => 'éµµ',
  'óç' => 'éµ°',
  'óè' => 'éµ©',
  'óé' => 'é¶…',
  'óê' => 'éµ³',
  'óë' => 'éµ»',
  'óì' => 'é¶‚',
  'óí' => 'éµ¯',
  'óî' => 'éµ¹',
  'óï' => 'éµ¿',
  'óð' => 'é¶‡',
  'óñ' => 'éµ¨',
  'óò' => 'éº”',
  'óó' => 'éº‘',
  'óô' => 'é»€',
  'óõ' => 'é»¼',
  'óö' => 'é¼­',
  'ó÷' => 'é½€',
  'óø' => 'é½�',
  'óù' => 'é½�',
  'óú' => 'é½–',
  'óû' => 'é½—',
  'óü' => 'é½˜',
  'óý' => 'åŒ·',
  'óþ' => 'åš²',
  'ô@' => 'åšµ',
  'ôA' => 'åš³',
  'ôB' => 'å££',
  'ôC' => 'å­…',
  'ôD' => 'å·†',
  'ôE' => 'å·‡',
  'ôF' => 'å»®',
  'ôG' => 'å»¯',
  'ôH' => 'å¿€',
  'ôI' => 'å¿�',
  'ôJ' => 'æ‡¹',
  'ôK' => 'æ”—',
  'ôL' => 'æ”–',
  'ôM' => 'æ”•',
  'ôN' => 'æ”“',
  'ôO' => 'æ—Ÿ',
  'ôP' => 'æ›¨',
  'ôQ' => 'æ›£',
  'ôR' => 'æ›¤',
  'ôS' => 'æ«³',
  'ôT' => 'æ«°',
  'ôU' => 'æ«ª',
  'ôV' => 'æ«¨',
  'ôW' => 'æ«¹',
  'ôX' => 'æ«±',
  'ôY' => 'æ«®',
  'ôZ' => 'æ«¯',
  'ô[' => 'ç€¼',
  'ô\\' => 'ç€µ',
  'ô]' => 'ç€¯',
  'ô^' => 'ç€·',
  'ô_' => 'ç€´',
  'ô`' => 'ç€±',
  'ôa' => 'ç�‚',
  'ôb' => 'ç€¸',
  'ôc' => 'ç€¿',
  'ôd' => 'ç€º',
  'ôe' => 'ç€¹',
  'ôf' => 'ç�€',
  'ôg' => 'ç€»',
  'ôh' => 'ç€³',
  'ôi' => 'ç��',
  'ôj' => 'çˆ“',
  'ôk' => 'çˆ”',
  'ôl' => 'çŠ¨',
  'ôm' => 'ç�½',
  'ôn' => 'ç�¼',
  'ôo' => 'ç’º',
  'ôp' => 'çš«',
  'ôq' => 'çšª',
  'ôr' => 'çš¾',
  'ôs' => 'ç›­',
  'ôt' => 'çŸŒ',
  'ôu' => 'çŸŽ',
  'ôv' => 'çŸ�',
  'ôw' => 'çŸ�',
  'ôx' => 'çŸ²',
  'ôy' => 'ç¤¥',
  'ôz' => 'ç¤£',
  'ô{' => 'ç¤§',
  'ô|' => 'ç¤¨',
  'ô}' => 'ç¤¤',
  'ô~' => 'ç¤©',
  'ô¡' => 'ç¦²',
  'ô¢' => 'ç©®',
  'ô£' => 'ç©¬',
  'ô¤' => 'ç©­',
  'ô¥' => 'ç«·',
  'ô¦' => 'ç±‰',
  'ô§' => 'ç±ˆ',
  'ô¨' => 'ç±Š',
  'ô©' => 'ç±‡',
  'ôª' => 'ç±…',
  'ô«' => 'ç³®',
  'ô¬' => 'ç¹»',
  'ô­' => 'ç¹¾',
  'ô®' => 'çº�',
  'ô¯' => 'çº€',
  'ô°' => 'ç¾º',
  'ô±' => 'ç¿¿',
  'ô²' => 'è�¹',
  'ô³' => 'è‡›',
  'ô´' => 'è‡™',
  'ôµ' => 'èˆ‹',
  'ô¶' => 'è‰¨',
  'ô·' => 'è‰©',
  'ô¸' => 'è˜¢',
  'ô¹' => 'è—¿',
  'ôº' => 'è˜�',
  'ô»' => 'è—¾',
  'ô¼' => 'è˜›',
  'ô½' => 'è˜€',
  'ô¾' => 'è—¶',
  'ô¿' => 'è˜„',
  'ôÀ' => 'è˜‰',
  'ôÁ' => 'è˜…',
  'ôÂ' => 'è˜Œ',
  'ôÃ' => 'è—½',
  'ôÄ' => 'è ™',
  'ôÅ' => 'è �',
  'ôÆ' => 'è ‘',
  'ôÇ' => 'è —',
  'ôÈ' => 'è “',
  'ôÉ' => 'è –',
  'ôÊ' => 'è¥£',
  'ôË' => 'è¥¦',
  'ôÌ' => 'è¦¹',
  'ôÍ' => 'è§·',
  'ôÎ' => 'è­ ',
  'ôÏ' => 'è­ª',
  'ôÐ' => 'è­�',
  'ôÑ' => 'è­¨',
  'ôÒ' => 'è­£',
  'ôÓ' => 'è­¥',
  'ôÔ' => 'è­§',
  'ôÕ' => 'è­­',
  'ôÖ' => 'è¶®',
  'ô×' => 'èº†',
  'ôØ' => 'èºˆ',
  'ôÙ' => 'èº„',
  'ôÚ' => 'è½™',
  'ôÛ' => 'è½–',
  'ôÜ' => 'è½—',
  'ôÝ' => 'è½•',
  'ôÞ' => 'è½˜',
  'ôß' => 'è½š',
  'ôà' => 'é‚�',
  'ôá' => 'é…ƒ',
  'ôâ' => 'é…�',
  'ôã' => 'é†·',
  'ôä' => 'é†µ',
  'ôå' => 'é†²',
  'ôæ' => 'é†³',
  'ôç' => 'é�‹',
  'ôè' => 'é�“',
  'ôé' => 'é�»',
  'ôê' => 'é� ',
  'ôë' => 'é��',
  'ôì' => 'é�”',
  'ôí' => 'é�¾',
  'ôî' => 'é�•',
  'ôï' => 'é��',
  'ôð' => 'é�¨',
  'ôñ' => 'é�™',
  'ôò' => 'é��',
  'ôó' => 'é�µ',
  'ôô' => 'é�€',
  'ôõ' => 'é�·',
  'ôö' => 'é�‡',
  'ô÷' => 'é�Ž',
  'ôø' => 'é�–',
  'ôù' => 'é�’',
  'ôú' => 'é�º',
  'ôû' => 'é�‰',
  'ôü' => 'é�¸',
  'ôý' => 'é�Š',
  'ôþ' => 'é�¿',
  'õ@' => 'é�¼',
  'õA' => 'é�Œ',
  'õB' => 'é�¶',
  'õC' => 'é�‘',
  'õD' => 'é�†',
  'õE' => 'é—ž',
  'õF' => 'é— ',
  'õG' => 'é—Ÿ',
  'õH' => 'éœ®',
  'õI' => 'éœ¯',
  'õJ' => 'éž¹',
  'õK' => 'éž»',
  'õL' => 'éŸ½',
  'õM' => 'éŸ¾',
  'õN' => 'é¡ ',
  'õO' => 'é¡¢',
  'õP' => 'é¡£',
  'õQ' => 'é¡Ÿ',
  'õR' => 'é£�',
  'õS' => 'é£‚',
  'õT' => 'é¥�',
  'õU' => 'é¥Ž',
  'õV' => 'é¥™',
  'õW' => 'é¥Œ',
  'õX' => 'é¥‹',
  'õY' => 'é¥“',
  'õZ' => 'é¨²',
  'õ[' => 'é¨´',
  'õ\\' => 'é¨±',
  'õ]' => 'é¨¬',
  'õ^' => 'é¨ª',
  'õ_' => 'é¨¶',
  'õ`' => 'é¨©',
  'õa' => 'é¨®',
  'õb' => 'é¨¸',
  'õc' => 'é¨­',
  'õd' => 'é«‡',
  'õe' => 'é«Š',
  'õf' => 'é«†',
  'õg' => 'é¬�',
  'õh' => 'é¬’',
  'õi' => 'é¬‘',
  'õj' => 'é°‹',
  'õk' => 'é°ˆ',
  'õl' => 'é¯·',
  'õm' => 'é°…',
  'õn' => 'é°’',
  'õo' => 'é¯¸',
  'õp' => 'é±€',
  'õq' => 'é°‡',
  'õr' => 'é°Ž',
  'õs' => 'é°†',
  'õt' => 'é°—',
  'õu' => 'é°”',
  'õv' => 'é°‰',
  'õw' => 'é¶Ÿ',
  'õx' => 'é¶™',
  'õy' => 'é¶¤',
  'õz' => 'é¶�',
  'õ{' => 'é¶’',
  'õ|' => 'é¶˜',
  'õ}' => 'é¶�',
  'õ~' => 'é¶›',
  'õ¡' => 'é¶ ',
  'õ¢' => 'é¶”',
  'õ£' => 'é¶œ',
  'õ¤' => 'é¶ª',
  'õ¥' => 'é¶—',
  'õ¦' => 'é¶¡',
  'õ§' => 'é¶š',
  'õ¨' => 'é¶¢',
  'õ©' => 'é¶¨',
  'õª' => 'é¶ž',
  'õ«' => 'é¶£',
  'õ¬' => 'é¶¿',
  'õ­' => 'é¶©',
  'õ®' => 'é¶–',
  'õ¯' => 'é¶¦',
  'õ°' => 'é¶§',
  'õ±' => 'éº™',
  'õ²' => 'éº›',
  'õ³' => 'éºš',
  'õ´' => 'é»¥',
  'õµ' => 'é»¤',
  'õ¶' => 'é»§',
  'õ·' => 'é»¦',
  'õ¸' => 'é¼°',
  'õ¹' => 'é¼®',
  'õº' => 'é½›',
  'õ»' => 'é½ ',
  'õ¼' => 'é½ž',
  'õ½' => 'é½�',
  'õ¾' => 'é½™',
  'õ¿' => 'é¾‘',
  'õÀ' => 'å„º',
  'õÁ' => 'å„¹',
  'õÂ' => 'åŠ˜',
  'õÃ' => 'åŠ—',
  'õÄ' => 'å›ƒ',
  'õÅ' => 'åš½',
  'õÆ' => 'åš¾',
  'õÇ' => 'å­ˆ',
  'õÈ' => 'å­‡',
  'õÉ' => 'å·‹',
  'õÊ' => 'å·�',
  'õË' => 'å»±',
  'õÌ' => 'æ‡½',
  'õÍ' => 'æ”›',
  'õÎ' => 'æ¬‚',
  'õÏ' => 'æ«¼',
  'õÐ' => 'æ¬ƒ',
  'õÑ' => 'æ«¸',
  'õÒ' => 'æ¬€',
  'õÓ' => 'ç�ƒ',
  'õÔ' => 'ç�„',
  'õÕ' => 'ç�Š',
  'õÖ' => 'ç�ˆ',
  'õ×' => 'ç�‰',
  'õØ' => 'ç�…',
  'õÙ' => 'ç�†',
  'õÚ' => 'çˆ�',
  'õÛ' => 'çˆš',
  'õÜ' => 'çˆ™',
  'õÝ' => 'ç�¾',
  'õÞ' => 'ç”—',
  'õß' => 'ç™ª',
  'õà' => 'çŸ�',
  'õá' => 'ç¤­',
  'õâ' => 'ç¤±',
  'õã' => 'ç¤¯',
  'õä' => 'ç±”',
  'õå' => 'ç±“',
  'õæ' => 'ç³²',
  'õç' => 'çºŠ',
  'õè' => 'çº‡',
  'õé' => 'çºˆ',
  'õê' => 'çº‹',
  'õë' => 'çº†',
  'õì' => 'çº�',
  'õí' => 'ç½�',
  'õî' => 'ç¾»',
  'õï' => 'è€°',
  'õð' => 'è‡�',
  'õñ' => 'è˜˜',
  'õò' => 'è˜ª',
  'õó' => 'è˜¦',
  'õô' => 'è˜Ÿ',
  'õõ' => 'è˜£',
  'õö' => 'è˜œ',
  'õ÷' => 'è˜™',
  'õø' => 'è˜§',
  'õù' => 'è˜®',
  'õú' => 'è˜¡',
  'õû' => 'è˜ ',
  'õü' => 'è˜©',
  'õý' => 'è˜ž',
  'õþ' => 'è˜¥',
  'ö@' => 'è ©',
  'öA' => 'è �',
  'öB' => 'è ›',
  'öC' => 'è  ',
  'öD' => 'è ¤',
  'öE' => 'è œ',
  'öF' => 'è «',
  'öG' => 'è¡Š',
  'öH' => 'è¥­',
  'öI' => 'è¥©',
  'öJ' => 'è¥®',
  'öK' => 'è¥«',
  'öL' => 'è§º',
  'öM' => 'è­¹',
  'öN' => 'è­¸',
  'öO' => 'è­…',
  'öP' => 'è­º',
  'öQ' => 'è­»',
  'öR' => 'è´�',
  'öS' => 'è´”',
  'öT' => 'è¶¯',
  'öU' => 'èºŽ',
  'öV' => 'èºŒ',
  'öW' => 'è½ž',
  'öX' => 'è½›',
  'öY' => 'è½�',
  'öZ' => 'é…†',
  'ö[' => 'é…„',
  'ö\\' => 'é……',
  'ö]' => 'é†¹',
  'ö^' => 'é�¿',
  'ö_' => 'é�»',
  'ö`' => 'é�¶',
  'öa' => 'é�©',
  'öb' => 'é�½',
  'öc' => 'é�¼',
  'öd' => 'é�°',
  'öe' => 'é�¹',
  'öf' => 'é�ª',
  'ög' => 'é�·',
  'öh' => 'é�¬',
  'öi' => 'é‘€',
  'öj' => 'é�±',
  'ök' => 'é—¥',
  'öl' => 'é—¤',
  'öm' => 'é—£',
  'ön' => 'éœµ',
  'öo' => 'éœº',
  'öp' => 'éž¿',
  'öq' => 'éŸ¡',
  'ör' => 'é¡¤',
  'ös' => 'é£‰',
  'öt' => 'é£†',
  'öu' => 'é£€',
  'öv' => 'é¥˜',
  'öw' => 'é¥–',
  'öx' => 'é¨¹',
  'öy' => 'é¨½',
  'öz' => 'é©†',
  'ö{' => 'é©„',
  'ö|' => 'é©‚',
  'ö}' => 'é©�',
  'ö~' => 'é¨º',
  'ö¡' => 'é¨¿',
  'ö¢' => 'é«�',
  'ö£' => 'é¬•',
  'ö¤' => 'é¬—',
  'ö¥' => 'é¬˜',
  'ö¦' => 'é¬–',
  'ö§' => 'é¬º',
  'ö¨' => 'é­’',
  'ö©' => 'é°«',
  'öª' => 'é°�',
  'ö«' => 'é°œ',
  'ö¬' => 'é°¬',
  'ö­' => 'é°£',
  'ö®' => 'é°¨',
  'ö¯' => 'é°©',
  'ö°' => 'é°¤',
  'ö±' => 'é°¡',
  'ö²' => 'é¶·',
  'ö³' => 'é¶¶',
  'ö´' => 'é¶¼',
  'öµ' => 'é·�',
  'ö¶' => 'é·‡',
  'ö·' => 'é·Š',
  'ö¸' => 'é·�',
  'ö¹' => 'é¶¾',
  'öº' => 'é·…',
  'ö»' => 'é·ƒ',
  'ö¼' => 'é¶»',
  'ö½' => 'é¶µ',
  'ö¾' => 'é·Ž',
  'ö¿' => 'é¶¹',
  'öÀ' => 'é¶º',
  'öÁ' => 'é¶¬',
  'öÂ' => 'é·ˆ',
  'öÃ' => 'é¶±',
  'öÄ' => 'é¶­',
  'öÅ' => 'é·Œ',
  'öÆ' => 'é¶³',
  'öÇ' => 'é·�',
  'öÈ' => 'é¶²',
  'öÉ' => 'é¹º',
  'öÊ' => 'éºœ',
  'öË' => 'é»«',
  'öÌ' => 'é»®',
  'öÍ' => 'é»­',
  'öÎ' => 'é¼›',
  'öÏ' => 'é¼˜',
  'öÐ' => 'é¼š',
  'öÑ' => 'é¼±',
  'öÒ' => 'é½Ž',
  'öÓ' => 'é½¥',
  'öÔ' => 'é½¤',
  'öÕ' => 'é¾’',
  'öÖ' => 'äº¹',
  'ö×' => 'å›†',
  'öØ' => 'å›…',
  'öÙ' => 'å›‹',
  'öÚ' => 'å¥±',
  'öÛ' => 'å­‹',
  'öÜ' => 'å­Œ',
  'öÝ' => 'å·•',
  'öÞ' => 'å·‘',
  'öß' => 'å»²',
  'öà' => 'æ”¡',
  'öá' => 'æ” ',
  'öâ' => 'æ”¦',
  'öã' => 'æ”¢',
  'öä' => 'æ¬‹',
  'öå' => 'æ¬ˆ',
  'öæ' => 'æ¬‰',
  'öç' => 'æ°�',
  'öè' => 'ç�•',
  'öé' => 'ç�–',
  'öê' => 'ç�—',
  'öë' => 'ç�’',
  'öì' => 'çˆž',
  'öí' => 'çˆŸ',
  'öî' => 'çŠ©',
  'öï' => 'ç�¿',
  'öð' => 'ç“˜',
  'öñ' => 'ç“•',
  'öò' => 'ç“™',
  'öó' => 'ç“—',
  'öô' => 'ç™­',
  'öõ' => 'çš­',
  'öö' => 'ç¤µ',
  'ö÷' => 'ç¦´',
  'öø' => 'ç©°',
  'öù' => 'ç©±',
  'öú' => 'ç±—',
  'öû' => 'ç±œ',
  'öü' => 'ç±™',
  'öý' => 'ç±›',
  'öþ' => 'ç±š',
  '÷@' => 'ç³´',
  '÷A' => 'ç³±',
  '÷B' => 'çº‘',
  '÷C' => 'ç½�',
  '÷D' => 'ç¾‡',
  '÷E' => 'è‡ž',
  '÷F' => 'è‰«',
  '÷G' => 'è˜´',
  '÷H' => 'è˜µ',
  '÷I' => 'è˜³',
  '÷J' => 'è˜¬',
  '÷K' => 'è˜²',
  '÷L' => 'è˜¶',
  '÷M' => 'è ¬',
  '÷N' => 'è ¨',
  '÷O' => 'è ¦',
  '÷P' => 'è ª',
  '÷Q' => 'è ¥',
  '÷R' => 'è¥±',
  '÷S' => 'è¦¿',
  '÷T' => 'è¦¾',
  '÷U' => 'è§»',
  '÷V' => 'è­¾',
  '÷W' => 'è®„',
  '÷X' => 'è®‚',
  '÷Y' => 'è®†',
  '÷Z' => 'è®…',
  '÷[' => 'è­¿',
  '÷\\' => 'è´•',
  '÷]' => 'èº•',
  '÷^' => 'èº”',
  '÷_' => 'èºš',
  '÷`' => 'èº’',
  '÷a' => 'èº�',
  '÷b' => 'èº–',
  '÷c' => 'èº—',
  '÷d' => 'è½ ',
  '÷e' => 'è½¢',
  '÷f' => 'é…‡',
  '÷g' => 'é‘Œ',
  '÷h' => 'é‘�',
  '÷i' => 'é‘Š',
  '÷j' => 'é‘‹',
  '÷k' => 'é‘�',
  '÷l' => 'é‘‡',
  '÷m' => 'é‘…',
  '÷n' => 'é‘ˆ',
  '÷o' => 'é‘‰',
  '÷p' => 'é‘†',
  '÷q' => 'éœ¿',
  '÷r' => 'éŸ£',
  '÷s' => 'é¡ª',
  '÷t' => 'é¡©',
  '÷u' => 'é£‹',
  '÷v' => 'é¥”',
  '÷w' => 'é¥›',
  '÷x' => 'é©Ž',
  '÷y' => 'é©“',
  '÷z' => 'é©”',
  '÷{' => 'é©Œ',
  '÷|' => 'é©�',
  '÷}' => 'é©ˆ',
  '÷~' => 'é©Š',
  '÷¡' => 'é©‰',
  '÷¢' => 'é©’',
  '÷£' => 'é©�',
  '÷¤' => 'é«�',
  '÷¥' => 'é¬™',
  '÷¦' => 'é¬«',
  '÷§' => 'é¬»',
  '÷¨' => 'é­–',
  '÷©' => 'é­•',
  '÷ª' => 'é±†',
  '÷«' => 'é±ˆ',
  '÷¬' => 'é°¿',
  '÷­' => 'é±„',
  '÷®' => 'é°¹',
  '÷¯' => 'é°³',
  '÷°' => 'é±�',
  '÷±' => 'é°¼',
  '÷²' => 'é°·',
  '÷³' => 'é°´',
  '÷´' => 'é°²',
  '÷µ' => 'é°½',
  '÷¶' => 'é°¶',
  '÷·' => 'é·›',
  '÷¸' => 'é·’',
  '÷¹' => 'é·ž',
  '÷º' => 'é·š',
  '÷»' => 'é·‹',
  '÷¼' => 'é·�',
  '÷½' => 'é·œ',
  '÷¾' => 'é·‘',
  '÷¿' => 'é·Ÿ',
  '÷À' => 'é·©',
  '÷Á' => 'é·™',
  '÷Â' => 'é·˜',
  '÷Ã' => 'é·–',
  '÷Ä' => 'é·µ',
  '÷Å' => 'é·•',
  '÷Æ' => 'é·�',
  '÷Ç' => 'éº¶',
  '÷È' => 'é»°',
  '÷É' => 'é¼µ',
  '÷Ê' => 'é¼³',
  '÷Ë' => 'é¼²',
  '÷Ì' => 'é½‚',
  '÷Í' => 'é½«',
  '÷Î' => 'é¾•',
  '÷Ï' => 'é¾¢',
  '÷Ð' => 'å„½',
  '÷Ñ' => 'åŠ™',
  '÷Ò' => 'å£¨',
  '÷Ó' => 'å£§',
  '÷Ô' => 'å¥²',
  '÷Õ' => 'å­�',
  '÷Ö' => 'å·˜',
  '÷×' => 'è ¯',
  '÷Ø' => 'å½�',
  '÷Ù' => 'æˆ�',
  '÷Ú' => 'æˆƒ',
  '÷Û' => 'æˆ„',
  '÷Ü' => 'æ”©',
  '÷Ý' => 'æ”¥',
  '÷Þ' => 'æ––',
  '÷ß' => 'æ›«',
  '÷à' => 'æ¬‘',
  '÷á' => 'æ¬’',
  '÷â' => 'æ¬�',
  '÷ã' => 'æ¯Š',
  '÷ä' => 'ç�›',
  '÷å' => 'ç�š',
  '÷æ' => 'çˆ¢',
  '÷ç' => 'çŽ‚',
  '÷è' => 'çŽ�',
  '÷é' => 'çŽƒ',
  '÷ê' => 'ç™°',
  '÷ë' => 'çŸ”',
  '÷ì' => 'ç±§',
  '÷í' => 'ç±¦',
  '÷î' => 'çº•',
  '÷ï' => 'è‰¬',
  '÷ð' => 'è˜º',
  '÷ñ' => 'è™€',
  '÷ò' => 'è˜¹',
  '÷ó' => 'è˜¼',
  '÷ô' => 'è˜±',
  '÷õ' => 'è˜»',
  '÷ö' => 'è˜¾',
  '÷÷' => 'è °',
  '÷ø' => 'è ²',
  '÷ù' => 'è ®',
  '÷ú' => 'è ³',
  '÷û' => 'è¥¶',
  '÷ü' => 'è¥´',
  '÷ý' => 'è¥³',
  '÷þ' => 'è§¾',
  'ø@' => 'è®Œ',
  'øA' => 'è®Ž',
  'øB' => 'è®‹',
  'øC' => 'è®ˆ',
  'øD' => 'è±…',
  'øE' => 'è´™',
  'øF' => 'èº˜',
  'øG' => 'è½¤',
  'øH' => 'è½£',
  'øI' => 'é†¼',
  'øJ' => 'é‘¢',
  'øK' => 'é‘•',
  'øL' => 'é‘�',
  'øM' => 'é‘—',
  'øN' => 'é‘ž',
  'øO' => 'éŸ„',
  'øP' => 'éŸ…',
  'øQ' => 'é €',
  'øR' => 'é©–',
  'øS' => 'é©™',
  'øT' => 'é¬ž',
  'øU' => 'é¬Ÿ',
  'øV' => 'é¬ ',
  'øW' => 'é±’',
  'øX' => 'é±˜',
  'øY' => 'é±�',
  'øZ' => 'é±Š',
  'ø[' => 'é±�',
  'ø\\' => 'é±‹',
  'ø]' => 'é±•',
  'ø^' => 'é±™',
  'ø_' => 'é±Œ',
  'ø`' => 'é±Ž',
  'øa' => 'é·»',
  'øb' => 'é··',
  'øc' => 'é·¯',
  'ød' => 'é·£',
  'øe' => 'é·«',
  'øf' => 'é·¸',
  'øg' => 'é·¤',
  'øh' => 'é·¶',
  'øi' => 'é·¡',
  'øj' => 'é·®',
  'øk' => 'é·¦',
  'øl' => 'é·²',
  'øm' => 'é·°',
  'øn' => 'é·¢',
  'øo' => 'é·¬',
  'øp' => 'é·´',
  'øq' => 'é·³',
  'ør' => 'é·¨',
  'øs' => 'é·­',
  'øt' => 'é»‚',
  'øu' => 'é»�',
  'øv' => 'é»²',
  'øw' => 'é»³',
  'øx' => 'é¼†',
  'øy' => 'é¼œ',
  'øz' => 'é¼¸',
  'ø{' => 'é¼·',
  'ø|' => 'é¼¶',
  'ø}' => 'é½ƒ',
  'ø~' => 'é½�',
  'ø¡' => 'é½±',
  'ø¢' => 'é½°',
  'ø£' => 'é½®',
  'ø¤' => 'é½¯',
  'ø¥' => 'å›“',
  'ø¦' => 'å›�',
  'ø§' => 'å­Ž',
  'ø¨' => 'å±­',
  'ø©' => 'æ”­',
  'øª' => 'æ›­',
  'ø«' => 'æ›®',
  'ø¬' => 'æ¬“',
  'ø­' => 'ç�Ÿ',
  'ø®' => 'ç�¡',
  'ø¯' => 'ç��',
  'ø°' => 'ç� ',
  'ø±' => 'çˆ£',
  'ø²' => 'ç“›',
  'ø³' => 'ç“¥',
  'ø´' => 'çŸ•',
  'øµ' => 'ç¤¸',
  'ø¶' => 'ç¦·',
  'ø·' => 'ç¦¶',
  'ø¸' => 'ç±ª',
  'ø¹' => 'çº—',
  'øº' => 'ç¾‰',
  'ø»' => 'è‰­',
  'ø¼' => 'è™ƒ',
  'ø½' => 'è ¸',
  'ø¾' => 'è ·',
  'ø¿' => 'è µ',
  'øÀ' => 'è¡‹',
  'øÁ' => 'è®”',
  'øÂ' => 'è®•',
  'øÃ' => 'èºž',
  'øÄ' => 'èºŸ',
  'øÅ' => 'èº ',
  'øÆ' => 'èº�',
  'øÇ' => 'é†¾',
  'øÈ' => 'é†½',
  'øÉ' => 'é‡‚',
  'øÊ' => 'é‘«',
  'øË' => 'é‘¨',
  'øÌ' => 'é‘©',
  'øÍ' => 'é›¥',
  'øÎ' => 'é�†',
  'øÏ' => 'é�ƒ',
  'øÐ' => 'é�‡',
  'øÑ' => 'éŸ‡',
  'øÒ' => 'éŸ¥',
  'øÓ' => 'é©ž',
  'øÔ' => 'é«•',
  'øÕ' => 'é­™',
  'øÖ' => 'é±£',
  'ø×' => 'é±§',
  'øØ' => 'é±¦',
  'øÙ' => 'é±¢',
  'øÚ' => 'é±ž',
  'øÛ' => 'é± ',
  'øÜ' => 'é¸‚',
  'øÝ' => 'é·¾',
  'øÞ' => 'é¸‡',
  'øß' => 'é¸ƒ',
  'øà' => 'é¸†',
  'øá' => 'é¸…',
  'øâ' => 'é¸€',
  'øã' => 'é¸�',
  'øä' => 'é¸‰',
  'øå' => 'é·¿',
  'øæ' => 'é·½',
  'øç' => 'é¸„',
  'øè' => 'éº ',
  'øé' => 'é¼ž',
  'øê' => 'é½†',
  'øë' => 'é½´',
  'øì' => 'é½µ',
  'øí' => 'é½¶',
  'øî' => 'å›”',
  'øï' => 'æ”®',
  'øð' => 'æ–¸',
  'øñ' => 'æ¬˜',
  'øò' => 'æ¬™',
  'øó' => 'æ¬—',
  'øô' => 'æ¬š',
  'øõ' => 'ç�¢',
  'øö' => 'çˆ¦',
  'ø÷' => 'çŠª',
  'øø' => 'çŸ˜',
  'øù' => 'çŸ™',
  'øú' => 'ç¤¹',
  'øû' => 'ç±©',
  'øü' => 'ç±«',
  'øý' => 'ç³¶',
  'øþ' => 'çºš',
  'ù@' => 'çº˜',
  'ùA' => 'çº›',
  'ùB' => 'çº™',
  'ùC' => 'è‡ ',
  'ùD' => 'è‡¡',
  'ùE' => 'è™†',
  'ùF' => 'è™‡',
  'ùG' => 'è™ˆ',
  'ùH' => 'è¥¹',
  'ùI' => 'è¥º',
  'ùJ' => 'è¥¼',
  'ùK' => 'è¥»',
  'ùL' => 'è§¿',
  'ùM' => 'è®˜',
  'ùN' => 'è®™',
  'ùO' => 'èº¥',
  'ùP' => 'èº¤',
  'ùQ' => 'èº£',
  'ùR' => 'é‘®',
  'ùS' => 'é‘­',
  'ùT' => 'é‘¯',
  'ùU' => 'é‘±',
  'ùV' => 'é‘³',
  'ùW' => 'é�‰',
  'ùX' => 'é¡²',
  'ùY' => 'é¥Ÿ',
  'ùZ' => 'é±¨',
  'ù[' => 'é±®',
  'ù\\' => 'é±­',
  'ù]' => 'é¸‹',
  'ù^' => 'é¸�',
  'ù_' => 'é¸�',
  'ù`' => 'é¸�',
  'ùa' => 'é¸’',
  'ùb' => 'é¸‘',
  'ùc' => 'éº¡',
  'ùd' => 'é»µ',
  'ùe' => 'é¼‰',
  'ùf' => 'é½‡',
  'ùg' => 'é½¸',
  'ùh' => 'é½»',
  'ùi' => 'é½º',
  'ùj' => 'é½¹',
  'ùk' => 'åœž',
  'ùl' => 'ç�¦',
  'ùm' => 'ç±¯',
  'ùn' => 'è ¼',
  'ùo' => 'è¶²',
  'ùp' => 'èº¦',
  'ùq' => 'é‡ƒ',
  'ùr' => 'é‘´',
  'ùs' => 'é‘¸',
  'ùt' => 'é‘¶',
  'ùu' => 'é‘µ',
  'ùv' => 'é© ',
  'ùw' => 'é±´',
  'ùx' => 'é±³',
  'ùy' => 'é±±',
  'ùz' => 'é±µ',
  'ù{' => 'é¸”',
  'ù|' => 'é¸“',
  'ù}' => 'é»¶',
  'ù~' => 'é¼Š',
  'ù¡' => 'é¾¤',
  'ù¢' => 'ç�¨',
  'ù£' => 'ç�¥',
  'ù¤' => 'ç³·',
  'ù¥' => 'è™ª',
  'ù¦' => 'è ¾',
  'ù§' => 'è ½',
  'ù¨' => 'è ¿',
  'ù©' => 'è®ž',
  'ùª' => 'è²œ',
  'ù«' => 'èº©',
  'ù¬' => 'è»‰',
  'ù­' => 'é�‹',
  'ù®' => 'é¡³',
  'ù¯' => 'é¡´',
  'ù°' => 'é£Œ',
  'ù±' => 'é¥¡',
  'ù²' => 'é¦«',
  'ù³' => 'é©¤',
  'ù´' => 'é©¦',
  'ùµ' => 'é©§',
  'ù¶' => 'é¬¤',
  'ù·' => 'é¸•',
  'ù¸' => 'é¸—',
  'ù¹' => 'é½ˆ',
  'ùº' => 'æˆ‡',
  'ù»' => 'æ¬ž',
  'ù¼' => 'çˆ§',
  'ù½' => 'è™Œ',
  'ù¾' => 'èº¨',
  'ù¿' => 'é’‚',
  'ùÀ' => 'é’€',
  'ùÁ' => 'é’�',
  'ùÂ' => 'é©©',
  'ùÃ' => 'é©¨',
  'ùÄ' => 'é¬®',
  'ùÅ' => 'é¸™',
  'ùÆ' => 'çˆ©',
  'ùÇ' => 'è™‹',
  'ùÈ' => 'è®Ÿ',
  'ùÉ' => 'é’ƒ',
  'ùÊ' => 'é±¹',
  'ùË' => 'éº·',
  'ùÌ' => 'ç™µ',
  'ùÍ' => 'é©«',
  'ùÎ' => 'é±º',
  'ùÏ' => 'é¸�',
  'ùÐ' => 'ç�©',
  'ùÑ' => 'ç�ª',
  'ùÒ' => 'éº¤',
  'ùÓ' => 'é½¾',
  'ùÔ' => 'é½‰',
  'ùÕ' => 'é¾˜',
  'ùÖ' => 'ç¢�',
  'ù×' => 'éŠ¹',
  'ùØ' => 'è£�',
  'ùÙ' => 'å¢»',
  'ùÚ' => 'æ�’',
  'ùÛ' => 'ç²§',
  'ùÜ' => 'å«º',
  'ùÝ' => 'â•”',
  'ùÞ' => 'â•¦',
  'ùß' => 'â•—',
  'ùà' => 'â• ',
  'ùá' => 'â•¬',
  'ùâ' => 'â•£',
  'ùã' => 'â•š',
  'ùä' => 'â•©',
  'ùå' => 'â•�',
  'ùæ' => 'â•’',
  'ùç' => 'â•¤',
  'ùè' => 'â••',
  'ùé' => 'â•ž',
  'ùê' => 'â•ª',
  'ùë' => 'â•¡',
  'ùì' => 'â•˜',
  'ùí' => 'â•§',
  'ùî' => 'â•›',
  'ùï' => 'â•“',
  'ùð' => 'â•¥',
  'ùñ' => 'â•–',
  'ùò' => 'â•Ÿ',
  'ùó' => 'â•«',
  'ùô' => 'â•¢',
  'ùõ' => 'â•™',
  'ùö' => 'â•¨',
  'ù÷' => 'â•œ',
  'ùø' => 'â•‘',
  'ùù' => 'â•�',
  'ùú' => 'â•­',
  'ùû' => 'â•®',
  'ùü' => 'â•°',
  'ùý' => 'â•¯',
  'ùþ' => 'â–“',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => 'Âœ',
  '' => '	',
  '' => 'Â†',
  '' => '',
  '' => 'Â—',
  '	' => 'Â�',
  '
' => 'ÂŽ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => 'Â�',
  '' => 'Â…',
  '' => '',
  '' => 'Â‡',
  '' => '',
  '' => '',
  '' => 'Â’',
  '' => 'Â�',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => 'Â€',
  '!' => 'Â�',
  '"' => 'Â‚',
  '#' => 'Âƒ',
  '$' => 'Â„',
  '%' => '
',
  '&' => '',
  '\'' => '',
  '(' => 'Âˆ',
  ')' => 'Â‰',
  '*' => 'ÂŠ',
  '+' => 'Â‹',
  ',' => 'ÂŒ',
  '-' => '',
  '.' => '',
  '/' => '',
  0 => 'Â�',
  1 => 'Â‘',
  2 => '',
  3 => 'Â“',
  4 => 'Â”',
  5 => 'Â•',
  6 => 'Â–',
  7 => '',
  8 => 'Â˜',
  9 => 'Â™',
  ':' => 'Âš',
  ';' => 'Â›',
  '<' => '',
  '=' => '',
  '>' => 'Âž',
  '?' => '',
  '@' => ' ',
  'A' => 'Â ',
  'B' => 'Ã¢',
  'C' => 'Ã¤',
  'D' => 'Ã ',
  'E' => 'Ã¡',
  'F' => 'Ã£',
  'G' => 'Ã¥',
  'H' => '{',
  'I' => 'Ã±',
  'J' => 'Ã‡',
  'K' => '.',
  'L' => '<',
  'M' => '(',
  'N' => '+',
  'O' => '!',
  'P' => '&',
  'Q' => 'Ã©',
  'R' => 'Ãª',
  'S' => 'Ã«',
  'T' => 'Ã¨',
  'U' => 'Ã­',
  'V' => 'Ã®',
  'W' => 'Ã¯',
  'X' => 'Ã¬',
  'Y' => 'ÃŸ',
  'Z' => 'Äž',
  '[' => 'Ä°',
  '\\' => '*',
  ']' => ')',
  '^' => ';',
  '_' => '^',
  '`' => '-',
  'a' => '/',
  'b' => 'Ã‚',
  'c' => 'Ã„',
  'd' => 'Ã€',
  'e' => 'Ã�',
  'f' => 'Ãƒ',
  'g' => 'Ã…',
  'h' => '[',
  'i' => 'Ã‘',
  'j' => 'ÅŸ',
  'k' => ',',
  'l' => '%',
  'm' => '_',
  'n' => '>',
  'o' => '?',
  'p' => 'Ã¸',
  'q' => 'Ã‰',
  'r' => 'ÃŠ',
  's' => 'Ã‹',
  't' => 'Ãˆ',
  'u' => 'Ã�',
  'v' => 'ÃŽ',
  'w' => 'Ã�',
  'x' => 'ÃŒ',
  'y' => 'Ä±',
  'z' => ':',
  '{' => 'Ã–',
  '|' => 'Åž',
  '}' => '\'',
  '~' => '=',
  '' => 'Ãœ',
  '€' => 'Ã˜',
  '�' => 'a',
  '‚' => 'b',
  'ƒ' => 'c',
  '„' => 'd',
  '…' => 'e',
  '†' => 'f',
  '‡' => 'g',
  'ˆ' => 'h',
  '‰' => 'i',
  'Š' => 'Â«',
  '‹' => 'Â»',
  'Œ' => '}',
  '�' => '`',
  'Ž' => 'Â¦',
  '�' => 'Â±',
  '�' => 'Â°',
  '‘' => 'j',
  '’' => 'k',
  '“' => 'l',
  '”' => 'm',
  '•' => 'n',
  '–' => 'o',
  '—' => 'p',
  '˜' => 'q',
  '™' => 'r',
  'š' => 'Âª',
  '›' => 'Âº',
  'œ' => 'Ã¦',
  '�' => 'Â¸',
  'ž' => 'Ã†',
  'Ÿ' => 'Â¤',
  ' ' => 'Âµ',
  '¡' => 'Ã¶',
  '¢' => 's',
  '£' => 't',
  '¤' => 'u',
  '¥' => 'v',
  '¦' => 'w',
  '§' => 'x',
  '¨' => 'y',
  '©' => 'z',
  'ª' => 'Â¡',
  '«' => 'Â¿',
  '¬' => ']',
  '­' => '$',
  '®' => '@',
  '¯' => 'Â®',
  '°' => 'Â¢',
  '±' => 'Â£',
  '²' => 'Â¥',
  '³' => 'Â·',
  '´' => 'Â©',
  'µ' => 'Â§',
  '¶' => 'Â¶',
  '·' => 'Â¼',
  '¸' => 'Â½',
  '¹' => 'Â¾',
  'º' => 'Â¬',
  '»' => '|',
  '¼' => 'Â¯',
  '½' => 'Â¨',
  '¾' => 'Â´',
  '¿' => 'Ã—',
  'À' => 'Ã§',
  'Á' => 'A',
  'Â' => 'B',
  'Ã' => 'C',
  'Ä' => 'D',
  'Å' => 'E',
  'Æ' => 'F',
  'Ç' => 'G',
  'È' => 'H',
  'É' => 'I',
  'Ê' => 'Â­',
  'Ë' => 'Ã´',
  'Ì' => '~',
  'Í' => 'Ã²',
  'Î' => 'Ã³',
  'Ï' => 'Ãµ',
  'Ð' => 'ÄŸ',
  'Ñ' => 'J',
  'Ò' => 'K',
  'Ó' => 'L',
  'Ô' => 'M',
  'Õ' => 'N',
  'Ö' => 'O',
  '×' => 'P',
  'Ø' => 'Q',
  'Ù' => 'R',
  'Ú' => 'Â¹',
  'Û' => 'Ã»',
  'Ü' => '\\',
  'Ý' => 'Ã¹',
  'Þ' => 'Ãº',
  'ß' => 'Ã¿',
  'à' => 'Ã¼',
  'á' => 'Ã·',
  'â' => 'S',
  'ã' => 'T',
  'ä' => 'U',
  'å' => 'V',
  'æ' => 'W',
  'ç' => 'X',
  'è' => 'Y',
  'é' => 'Z',
  'ê' => 'Â²',
  'ë' => 'Ã”',
  'ì' => '#',
  'í' => 'Ã’',
  'î' => 'Ã“',
  'ï' => 'Ã•',
  'ð' => '0',
  'ñ' => '1',
  'ò' => '2',
  'ó' => '3',
  'ô' => '4',
  'õ' => '5',
  'ö' => '6',
  '÷' => '7',
  'ø' => '8',
  'ù' => '9',
  'ú' => 'Â³',
  'û' => 'Ã›',
  'ü' => '"',
  'ý' => 'Ã™',
  'þ' => 'Ãš',
  'ÿ' => 'ÂŸ',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => 'Âœ',
  '' => '	',
  '' => 'Â†',
  '' => '',
  '' => 'Â—',
  '	' => 'Â�',
  '
' => 'ÂŽ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => 'Â�',
  '' => 'Â…',
  '' => '',
  '' => 'Â‡',
  '' => '',
  '' => '',
  '' => 'Â’',
  '' => 'Â�',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => 'Â€',
  '!' => 'Â�',
  '"' => 'Â‚',
  '#' => 'Âƒ',
  '$' => 'Â„',
  '%' => '
',
  '&' => '',
  '\'' => '',
  '(' => 'Âˆ',
  ')' => 'Â‰',
  '*' => 'ÂŠ',
  '+' => 'Â‹',
  ',' => 'ÂŒ',
  '-' => '',
  '.' => '',
  '/' => '',
  0 => 'Â�',
  1 => 'Â‘',
  2 => '',
  3 => 'Â“',
  4 => 'Â”',
  5 => 'Â•',
  6 => 'Â–',
  7 => '',
  8 => 'Â˜',
  9 => 'Â™',
  ':' => 'Âš',
  ';' => 'Â›',
  '<' => '',
  '=' => '',
  '>' => 'Âž',
  '?' => '',
  '@' => ' ',
  'A' => '×�',
  'B' => '×‘',
  'C' => '×’',
  'D' => '×“',
  'E' => '×”',
  'F' => '×•',
  'G' => '×–',
  'H' => '×—',
  'I' => '×˜',
  'J' => 'Â¢',
  'K' => '.',
  'L' => '<',
  'M' => '(',
  'N' => '+',
  'O' => '|',
  'P' => '&',
  'Q' => '×™',
  'R' => '×š',
  'S' => '×›',
  'T' => '×œ',
  'U' => '×�',
  'V' => '×ž',
  'W' => '×Ÿ',
  'X' => '× ',
  'Y' => '×¡',
  'Z' => '!',
  '[' => '$',
  '\\' => '*',
  ']' => ')',
  '^' => ';',
  '_' => 'Â¬',
  '`' => '-',
  'a' => '/',
  'b' => '×¢',
  'c' => '×£',
  'd' => '×¤',
  'e' => '×¥',
  'f' => '×¦',
  'g' => '×§',
  'h' => '×¨',
  'i' => '×©',
  'j' => 'Â¦',
  'k' => ',',
  'l' => '%',
  'm' => '_',
  'n' => '>',
  'o' => '?',
  'q' => '×ª',
  't' => 'Â ',
  'x' => 'â€—',
  'y' => '`',
  'z' => ':',
  '{' => '#',
  '|' => '@',
  '}' => '\'',
  '~' => '=',
  '' => '"',
  '�' => 'a',
  '‚' => 'b',
  'ƒ' => 'c',
  '„' => 'd',
  '…' => 'e',
  '†' => 'f',
  '‡' => 'g',
  'ˆ' => 'h',
  '‰' => 'i',
  'Š' => 'Â«',
  '‹' => 'Â»',
  '�' => 'Â±',
  '�' => 'Â°',
  '‘' => 'j',
  '’' => 'k',
  '“' => 'l',
  '”' => 'm',
  '•' => 'n',
  '–' => 'o',
  '—' => 'p',
  '˜' => 'q',
  '™' => 'r',
  '�' => 'Â¸',
  'Ÿ' => 'Â¤',
  ' ' => 'Âµ',
  '¡' => '~',
  '¢' => 's',
  '£' => 't',
  '¤' => 'u',
  '¥' => 'v',
  '¦' => 'w',
  '§' => 'x',
  '¨' => 'y',
  '©' => 'z',
  '¯' => 'Â®',
  '°' => '^',
  '±' => 'Â£',
  '²' => 'Â¥',
  '³' => 'Â·',
  '´' => 'Â©',
  'µ' => 'Â§',
  '¶' => 'Â¶',
  '·' => 'Â¼',
  '¸' => 'Â½',
  '¹' => 'Â¾',
  'º' => '[',
  '»' => ']',
  '¼' => 'Â¯',
  '½' => 'Â¨',
  '¾' => 'Â´',
  '¿' => 'Ã—',
  'À' => '{',
  'Á' => 'A',
  'Â' => 'B',
  'Ã' => 'C',
  'Ä' => 'D',
  'Å' => 'E',
  'Æ' => 'F',
  'Ç' => 'G',
  'È' => 'H',
  'É' => 'I',
  'Ê' => 'Â­',
  'Ð' => '}',
  'Ñ' => 'J',
  'Ò' => 'K',
  'Ó' => 'L',
  'Ô' => 'M',
  'Õ' => 'N',
  'Ö' => 'O',
  '×' => 'P',
  'Ø' => 'Q',
  'Ù' => 'R',
  'Ú' => 'Â¹',
  'à' => '\\',
  'á' => 'Ã·',
  'â' => 'S',
  'ã' => 'T',
  'ä' => 'U',
  'å' => 'V',
  'æ' => 'W',
  'ç' => 'X',
  'è' => 'Y',
  'é' => 'Z',
  'ê' => 'Â²',
  'ð' => '0',
  'ñ' => '1',
  'ò' => '2',
  'ó' => '3',
  'ô' => '4',
  'õ' => '5',
  'ö' => '6',
  '÷' => '7',
  'ø' => '8',
  'ù' => '9',
  'ú' => 'Â³',
  'ÿ' => 'ÂŸ',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => '%',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
  '€' => 'Â€',
  '�' => 'Â�',
  '‚' => 'Â‚',
  'ƒ' => 'Âƒ',
  '„' => 'Â„',
  '…' => 'Â…',
  '†' => 'Â†',
  '‡' => 'Â‡',
  'ˆ' => 'Âˆ',
  '‰' => 'Â‰',
  'Š' => 'ÂŠ',
  '‹' => 'Â‹',
  'Œ' => 'ÂŒ',
  '�' => 'Â�',
  'Ž' => 'ÂŽ',
  '�' => 'Â�',
  '�' => 'Â�',
  '‘' => 'Â‘',
  '’' => 'Â’',
  '“' => 'Â“',
  '”' => 'Â”',
  '•' => 'Â•',
  '–' => 'Â–',
  '—' => 'Â—',
  '˜' => 'Â˜',
  '™' => 'Â™',
  'š' => 'Âš',
  '›' => 'Â›',
  'œ' => 'Âœ',
  '�' => 'Â�',
  'ž' => 'Âž',
  'Ÿ' => 'ÂŸ',
  ' ' => 'Â ',
  '¤' => 'Â¤',
  '¬' => 'ØŒ',
  '­' => 'Â­',
  '»' => 'Ø›',
  '¿' => 'ØŸ',
  'Á' => 'Ø¡',
  'Â' => 'Ø¢',
  'Ã' => 'Ø£',
  'Ä' => 'Ø¤',
  'Å' => 'Ø¥',
  'Æ' => 'Ø¦',
  'Ç' => 'Ø§',
  'È' => 'Ø¨',
  'É' => 'Ø©',
  'Ê' => 'Øª',
  'Ë' => 'Ø«',
  'Ì' => 'Ø¬',
  'Í' => 'Ø­',
  'Î' => 'Ø®',
  'Ï' => 'Ø¯',
  'Ð' => 'Ø°',
  'Ñ' => 'Ø±',
  'Ò' => 'Ø²',
  'Ó' => 'Ø³',
  'Ô' => 'Ø´',
  'Õ' => 'Øµ',
  'Ö' => 'Ø¶',
  '×' => 'Ø·',
  'Ø' => 'Ø¸',
  'Ù' => 'Ø¹',
  'Ú' => 'Øº',
  'à' => 'Ù€',
  'á' => 'Ù�',
  'â' => 'Ù‚',
  'ã' => 'Ùƒ',
  'ä' => 'Ù„',
  'å' => 'Ù…',
  'æ' => 'Ù†',
  'ç' => 'Ù‡',
  'è' => 'Ùˆ',
  'é' => 'Ù‰',
  'ê' => 'ÙŠ',
  'ë' => 'Ù‹',
  'ì' => 'ÙŒ',
  'í' => 'Ù�',
  'î' => 'ÙŽ',
  'ï' => 'Ù�',
  'ð' => 'Ù�',
  'ñ' => 'Ù‘',
  'ò' => 'Ù’',
);

$result =& $data;
unset($data);

return $result;
<?php

return array (
  'Âµ' => 'Î¼',
  'Â¼' => ' 1â�„4 ',
  'Â½' => ' 1â�„2 ',
  'Â¾' => ' 3â�„4 ',
  'Ä²' => 'IJ',
  'Ä³' => 'ij',
  'Ä¿' => 'LÂ·',
  'Å€' => 'lÂ·',
  'Å‰' => 'Ê¼n',
  'Å¿' => 's',
  'Ç„' => 'DÅ½',
  'Ç…' => 'DÅ¾',
  'Ç†' => 'dÅ¾',
  'Ç‡' => 'LJ',
  'Çˆ' => 'Lj',
  'Ç‰' => 'lj',
  'ÇŠ' => 'NJ',
  'Ç‹' => 'Nj',
  'ÇŒ' => 'nj',
  'Ç±' => 'DZ',
  'Ç²' => 'Dz',
  'Ç³' => 'dz',
  'Ï�' => 'Î²',
  'Ï‘' => 'Î¸',
  'Ï’' => 'Î¥',
  'Ï•' => 'Ï†',
  'Ï–' => 'Ï€',
  'Ï°' => 'Îº',
  'Ï±' => 'Ï�',
  'Ï²' => 'Ï‚',
  'Ï´' => 'Î˜',
  'Ïµ' => 'Îµ',
  'Ï¹' => 'Î£',
  'Ö‡' => 'Õ¥Ö‚',
  'Ùµ' => 'Ø§Ù´',
  'Ù¶' => 'ÙˆÙ´',
  'Ù·' => 'Û‡Ù´',
  'Ù¸' => 'ÙŠÙ´',
  'à¸³' => 'à¹�à¸²',
  'àº³' => 'à»�àº²',
  'à»œ' => 'àº«àº™',
  'à»�' => 'àº«àº¡',
  'à½·' => 'à¾²à¾�',
  'à½¹' => 'à¾³à¾�',
  'áºš' => 'aÊ¾',
  'â€¤' => '.',
  'â€¥' => '..',
  'â€¦' => '...',
  'â€³' => 'â€²â€²',
  'â€´' => 'â€²â€²â€²',
  'â€¶' => 'â€µâ€µ',
  'â€·' => 'â€µâ€µâ€µ',
  'â€¼' => '!!',
  'â�‡' => '??',
  'â�ˆ' => '?!',
  'â�‰' => '!?',
  'â�—' => 'â€²â€²â€²â€²',
  'â‚¨' => 'Rs',
  'â„€' => 'a/c',
  'â„�' => 'a/s',
  'â„‚' => 'C',
  'â„ƒ' => 'Â°C',
  'â„…' => 'c/o',
  'â„†' => 'c/u',
  'â„‡' => 'Æ�',
  'â„‰' => 'Â°F',
  'â„Š' => 'g',
  'â„‹' => 'H',
  'â„Œ' => 'H',
  'â„�' => 'H',
  'â„Ž' => 'h',
  'â„�' => 'Ä§',
  'â„�' => 'I',
  'â„‘' => 'I',
  'â„’' => 'L',
  'â„“' => 'l',
  'â„•' => 'N',
  'â„–' => 'No',
  'â„™' => 'P',
  'â„š' => 'Q',
  'â„›' => 'R',
  'â„œ' => 'R',
  'â„�' => 'R',
  'â„¡' => 'TEL',
  'â„¤' => 'Z',
  'â„¨' => 'Z',
  'â„¬' => 'B',
  'â„­' => 'C',
  'â„¯' => 'e',
  'â„°' => 'E',
  'â„±' => 'F',
  'â„³' => 'M',
  'â„´' => 'o',
  'â„µ' => '×�',
  'â„¶' => '×‘',
  'â„·' => '×’',
  'â„¸' => '×“',
  'â„¹' => 'i',
  'â„»' => 'FAX',
  'â„¼' => 'Ï€',
  'â„½' => 'Î³',
  'â„¾' => 'Î“',
  'â„¿' => 'Î ',
  'â…€' => 'âˆ‘',
  'â……' => 'D',
  'â…†' => 'd',
  'â…‡' => 'e',
  'â…ˆ' => 'i',
  'â…‰' => 'j',
  'â…�' => ' 1â�„7 ',
  'â…‘' => ' 1â�„9 ',
  'â…’' => ' 1â�„10 ',
  'â…“' => ' 1â�„3 ',
  'â…”' => ' 2â�„3 ',
  'â…•' => ' 1â�„5 ',
  'â…–' => ' 2â�„5 ',
  'â…—' => ' 3â�„5 ',
  'â…˜' => ' 4â�„5 ',
  'â…™' => ' 1â�„6 ',
  'â…š' => ' 5â�„6 ',
  'â…›' => ' 1â�„8 ',
  'â…œ' => ' 3â�„8 ',
  'â…�' => ' 5â�„8 ',
  'â…ž' => ' 7â�„8 ',
  'â…Ÿ' => ' 1â�„ ',
  'â… ' => 'I',
  'â…¡' => 'II',
  'â…¢' => 'III',
  'â…£' => 'IV',
  'â…¤' => 'V',
  'â…¥' => 'VI',
  'â…¦' => 'VII',
  'â…§' => 'VIII',
  'â…¨' => 'IX',
  'â…©' => 'X',
  'â…ª' => 'XI',
  'â…«' => 'XII',
  'â…¬' => 'L',
  'â…­' => 'C',
  'â…®' => 'D',
  'â…¯' => 'M',
  'â…°' => 'i',
  'â…±' => 'ii',
  'â…²' => 'iii',
  'â…³' => 'iv',
  'â…´' => 'v',
  'â…µ' => 'vi',
  'â…¶' => 'vii',
  'â…·' => 'viii',
  'â…¸' => 'ix',
  'â…¹' => 'x',
  'â…º' => 'xi',
  'â…»' => 'xii',
  'â…¼' => 'l',
  'â…½' => 'c',
  'â…¾' => 'd',
  'â…¿' => 'm',
  'â†‰' => ' 0â�„3 ',
  'âˆ¬' => 'âˆ«âˆ«',
  'âˆ­' => 'âˆ«âˆ«âˆ«',
  'âˆ¯' => 'âˆ®âˆ®',
  'âˆ°' => 'âˆ®âˆ®âˆ®',
  'â‘ ' => '(1)',
  'â‘¡' => '(2)',
  'â‘¢' => '(3)',
  'â‘£' => '(4)',
  'â‘¤' => '(5)',
  'â‘¥' => '(6)',
  'â‘¦' => '(7)',
  'â‘§' => '(8)',
  'â‘¨' => '(9)',
  'â‘©' => '(10)',
  'â‘ª' => '(11)',
  'â‘«' => '(12)',
  'â‘¬' => '(13)',
  'â‘­' => '(14)',
  'â‘®' => '(15)',
  'â‘¯' => '(16)',
  'â‘°' => '(17)',
  'â‘±' => '(18)',
  'â‘²' => '(19)',
  'â‘³' => '(20)',
  'â‘´' => '(1)',
  'â‘µ' => '(2)',
  'â‘¶' => '(3)',
  'â‘·' => '(4)',
  'â‘¸' => '(5)',
  'â‘¹' => '(6)',
  'â‘º' => '(7)',
  'â‘»' => '(8)',
  'â‘¼' => '(9)',
  'â‘½' => '(10)',
  'â‘¾' => '(11)',
  'â‘¿' => '(12)',
  'â’€' => '(13)',
  'â’�' => '(14)',
  'â’‚' => '(15)',
  'â’ƒ' => '(16)',
  'â’„' => '(17)',
  'â’…' => '(18)',
  'â’†' => '(19)',
  'â’‡' => '(20)',
  'â’ˆ' => '1.',
  'â’‰' => '2.',
  'â’Š' => '3.',
  'â’‹' => '4.',
  'â’Œ' => '5.',
  'â’�' => '6.',
  'â’Ž' => '7.',
  'â’�' => '8.',
  'â’�' => '9.',
  'â’‘' => '10.',
  'â’’' => '11.',
  'â’“' => '12.',
  'â’”' => '13.',
  'â’•' => '14.',
  'â’–' => '15.',
  'â’—' => '16.',
  'â’˜' => '17.',
  'â’™' => '18.',
  'â’š' => '19.',
  'â’›' => '20.',
  'â’œ' => '(a)',
  'â’�' => '(b)',
  'â’ž' => '(c)',
  'â’Ÿ' => '(d)',
  'â’ ' => '(e)',
  'â’¡' => '(f)',
  'â’¢' => '(g)',
  'â’£' => '(h)',
  'â’¤' => '(i)',
  'â’¥' => '(j)',
  'â’¦' => '(k)',
  'â’§' => '(l)',
  'â’¨' => '(m)',
  'â’©' => '(n)',
  'â’ª' => '(o)',
  'â’«' => '(p)',
  'â’¬' => '(q)',
  'â’­' => '(r)',
  'â’®' => '(s)',
  'â’¯' => '(t)',
  'â’°' => '(u)',
  'â’±' => '(v)',
  'â’²' => '(w)',
  'â’³' => '(x)',
  'â’´' => '(y)',
  'â’µ' => '(z)',
  'â’¶' => '(A)',
  'â’·' => '(B)',
  'â’¸' => '(C)',
  'â’¹' => '(D)',
  'â’º' => '(E)',
  'â’»' => '(F)',
  'â’¼' => '(G)',
  'â’½' => '(H)',
  'â’¾' => '(I)',
  'â’¿' => '(J)',
  'â“€' => '(K)',
  'â“�' => '(L)',
  'â“‚' => '(M)',
  'â“ƒ' => '(N)',
  'â“„' => '(O)',
  'â“…' => '(P)',
  'â“†' => '(Q)',
  'â“‡' => '(R)',
  'â“ˆ' => '(S)',
  'â“‰' => '(T)',
  'â“Š' => '(U)',
  'â“‹' => '(V)',
  'â“Œ' => '(W)',
  'â“�' => '(X)',
  'â“Ž' => '(Y)',
  'â“�' => '(Z)',
  'â“�' => '(a)',
  'â“‘' => '(b)',
  'â“’' => '(c)',
  'â““' => '(d)',
  'â“”' => '(e)',
  'â“•' => '(f)',
  'â“–' => '(g)',
  'â“—' => '(h)',
  'â“˜' => '(i)',
  'â“™' => '(j)',
  'â“š' => '(k)',
  'â“›' => '(l)',
  'â“œ' => '(m)',
  'â“�' => '(n)',
  'â“ž' => '(o)',
  'â“Ÿ' => '(p)',
  'â“ ' => '(q)',
  'â“¡' => '(r)',
  'â“¢' => '(s)',
  'â“£' => '(t)',
  'â“¤' => '(u)',
  'â“¥' => '(v)',
  'â“¦' => '(w)',
  'â“§' => '(x)',
  'â“¨' => '(y)',
  'â“©' => '(z)',
  'â“ª' => '(0)',
  'â¨Œ' => 'âˆ«âˆ«âˆ«âˆ«',
  'â©´' => '::=',
  'â©µ' => '==',
  'â©¶' => '===',
  'âºŸ' => 'æ¯�',
  'â»³' => 'é¾Ÿ',
  'â¼€' => 'ä¸€',
  'â¼�' => 'ä¸¨',
  'â¼‚' => 'ä¸¶',
  'â¼ƒ' => 'ä¸¿',
  'â¼„' => 'ä¹™',
  'â¼…' => 'äº…',
  'â¼†' => 'äºŒ',
  'â¼‡' => 'äº ',
  'â¼ˆ' => 'äºº',
  'â¼‰' => 'å„¿',
  'â¼Š' => 'å…¥',
  'â¼‹' => 'å…«',
  'â¼Œ' => 'å†‚',
  'â¼�' => 'å†–',
  'â¼Ž' => 'å†«',
  'â¼�' => 'å‡ ',
  'â¼�' => 'å‡µ',
  'â¼‘' => 'åˆ€',
  'â¼’' => 'åŠ›',
  'â¼“' => 'å‹¹',
  'â¼”' => 'åŒ•',
  'â¼•' => 'åŒš',
  'â¼–' => 'åŒ¸',
  'â¼—' => 'å��',
  'â¼˜' => 'å�œ',
  'â¼™' => 'å�©',
  'â¼š' => 'åŽ‚',
  'â¼›' => 'åŽ¶',
  'â¼œ' => 'å�ˆ',
  'â¼�' => 'å�£',
  'â¼ž' => 'å›—',
  'â¼Ÿ' => 'åœŸ',
  'â¼ ' => 'å£«',
  'â¼¡' => 'å¤‚',
  'â¼¢' => 'å¤Š',
  'â¼£' => 'å¤•',
  'â¼¤' => 'å¤§',
  'â¼¥' => 'å¥³',
  'â¼¦' => 'å­�',
  'â¼§' => 'å®€',
  'â¼¨' => 'å¯¸',
  'â¼©' => 'å°�',
  'â¼ª' => 'å°¢',
  'â¼«' => 'å°¸',
  'â¼¬' => 'å±®',
  'â¼­' => 'å±±',
  'â¼®' => 'å·›',
  'â¼¯' => 'å·¥',
  'â¼°' => 'å·±',
  'â¼±' => 'å·¾',
  'â¼²' => 'å¹²',
  'â¼³' => 'å¹º',
  'â¼´' => 'å¹¿',
  'â¼µ' => 'å»´',
  'â¼¶' => 'å»¾',
  'â¼·' => 'å¼‹',
  'â¼¸' => 'å¼“',
  'â¼¹' => 'å½�',
  'â¼º' => 'å½¡',
  'â¼»' => 'å½³',
  'â¼¼' => 'å¿ƒ',
  'â¼½' => 'æˆˆ',
  'â¼¾' => 'æˆ¶',
  'â¼¿' => 'æ‰‹',
  'â½€' => 'æ”¯',
  'â½�' => 'æ”´',
  'â½‚' => 'æ–‡',
  'â½ƒ' => 'æ–—',
  'â½„' => 'æ–¤',
  'â½…' => 'æ–¹',
  'â½†' => 'æ— ',
  'â½‡' => 'æ—¥',
  'â½ˆ' => 'æ›°',
  'â½‰' => 'æœˆ',
  'â½Š' => 'æœ¨',
  'â½‹' => 'æ¬ ',
  'â½Œ' => 'æ­¢',
  'â½�' => 'æ­¹',
  'â½Ž' => 'æ®³',
  'â½�' => 'æ¯‹',
  'â½�' => 'æ¯”',
  'â½‘' => 'æ¯›',
  'â½’' => 'æ°�',
  'â½“' => 'æ°”',
  'â½”' => 'æ°´',
  'â½•' => 'ç�«',
  'â½–' => 'çˆª',
  'â½—' => 'çˆ¶',
  'â½˜' => 'çˆ»',
  'â½™' => 'çˆ¿',
  'â½š' => 'ç‰‡',
  'â½›' => 'ç‰™',
  'â½œ' => 'ç‰›',
  'â½�' => 'çŠ¬',
  'â½ž' => 'çŽ„',
  'â½Ÿ' => 'çŽ‰',
  'â½ ' => 'ç“œ',
  'â½¡' => 'ç“¦',
  'â½¢' => 'ç”˜',
  'â½£' => 'ç”Ÿ',
  'â½¤' => 'ç”¨',
  'â½¥' => 'ç”°',
  'â½¦' => 'ç–‹',
  'â½§' => 'ç–’',
  'â½¨' => 'ç™¶',
  'â½©' => 'ç™½',
  'â½ª' => 'çš®',
  'â½«' => 'çš¿',
  'â½¬' => 'ç›®',
  'â½­' => 'çŸ›',
  'â½®' => 'çŸ¢',
  'â½¯' => 'çŸ³',
  'â½°' => 'ç¤º',
  'â½±' => 'ç¦¸',
  'â½²' => 'ç¦¾',
  'â½³' => 'ç©´',
  'â½´' => 'ç«‹',
  'â½µ' => 'ç«¹',
  'â½¶' => 'ç±³',
  'â½·' => 'ç³¸',
  'â½¸' => 'ç¼¶',
  'â½¹' => 'ç½‘',
  'â½º' => 'ç¾Š',
  'â½»' => 'ç¾½',
  'â½¼' => 'è€�',
  'â½½' => 'è€Œ',
  'â½¾' => 'è€’',
  'â½¿' => 'è€³',
  'â¾€' => 'è�¿',
  'â¾�' => 'è‚‰',
  'â¾‚' => 'è‡£',
  'â¾ƒ' => 'è‡ª',
  'â¾„' => 'è‡³',
  'â¾…' => 'è‡¼',
  'â¾†' => 'èˆŒ',
  'â¾‡' => 'èˆ›',
  'â¾ˆ' => 'èˆŸ',
  'â¾‰' => 'è‰®',
  'â¾Š' => 'è‰²',
  'â¾‹' => 'è‰¸',
  'â¾Œ' => 'è™�',
  'â¾�' => 'è™«',
  'â¾Ž' => 'è¡€',
  'â¾�' => 'è¡Œ',
  'â¾�' => 'è¡£',
  'â¾‘' => 'è¥¾',
  'â¾’' => 'è¦‹',
  'â¾“' => 'è§’',
  'â¾”' => 'è¨€',
  'â¾•' => 'è°·',
  'â¾–' => 'è±†',
  'â¾—' => 'è±•',
  'â¾˜' => 'è±¸',
  'â¾™' => 'è²�',
  'â¾š' => 'èµ¤',
  'â¾›' => 'èµ°',
  'â¾œ' => 'è¶³',
  'â¾�' => 'èº«',
  'â¾ž' => 'è»Š',
  'â¾Ÿ' => 'è¾›',
  'â¾ ' => 'è¾°',
  'â¾¡' => 'è¾µ',
  'â¾¢' => 'é‚‘',
  'â¾£' => 'é…‰',
  'â¾¤' => 'é‡†',
  'â¾¥' => 'é‡Œ',
  'â¾¦' => 'é‡‘',
  'â¾§' => 'é•·',
  'â¾¨' => 'é–€',
  'â¾©' => 'é˜œ',
  'â¾ª' => 'éš¶',
  'â¾«' => 'éš¹',
  'â¾¬' => 'é›¨',
  'â¾­' => 'é�‘',
  'â¾®' => 'é�ž',
  'â¾¯' => 'é�¢',
  'â¾°' => 'é�©',
  'â¾±' => 'éŸ‹',
  'â¾²' => 'éŸ­',
  'â¾³' => 'éŸ³',
  'â¾´' => 'é �',
  'â¾µ' => 'é¢¨',
  'â¾¶' => 'é£›',
  'â¾·' => 'é£Ÿ',
  'â¾¸' => 'é¦–',
  'â¾¹' => 'é¦™',
  'â¾º' => 'é¦¬',
  'â¾»' => 'éª¨',
  'â¾¼' => 'é«˜',
  'â¾½' => 'é«Ÿ',
  'â¾¾' => 'é¬¥',
  'â¾¿' => 'é¬¯',
  'â¿€' => 'é¬²',
  'â¿�' => 'é¬¼',
  'â¿‚' => 'é­š',
  'â¿ƒ' => 'é³¥',
  'â¿„' => 'é¹µ',
  'â¿…' => 'é¹¿',
  'â¿†' => 'éº¥',
  'â¿‡' => 'éº»',
  'â¿ˆ' => 'é»ƒ',
  'â¿‰' => 'é»�',
  'â¿Š' => 'é»‘',
  'â¿‹' => 'é»¹',
  'â¿Œ' => 'é»½',
  'â¿�' => 'é¼Ž',
  'â¿Ž' => 'é¼“',
  'â¿�' => 'é¼ ',
  'â¿�' => 'é¼»',
  'â¿‘' => 'é½Š',
  'â¿’' => 'é½’',
  'â¿“' => 'é¾�',
  'â¿”' => 'é¾œ',
  'â¿•' => 'é¾ ',
  'ã€€' => ' ',
  'ã€¶' => 'ã€’',
  'ã€¸' => 'å��',
  'ã€¹' => 'å�„',
  'ã€º' => 'å�…',
  'ã„±' => 'á„€',
  'ã„²' => 'á„�',
  'ã„³' => 'á†ª',
  'ã„´' => 'á„‚',
  'ã„µ' => 'á†¬',
  'ã„¶' => 'á†­',
  'ã„·' => 'á„ƒ',
  'ã„¸' => 'á„„',
  'ã„¹' => 'á„…',
  'ã„º' => 'á†°',
  'ã„»' => 'á†±',
  'ã„¼' => 'á†²',
  'ã„½' => 'á†³',
  'ã„¾' => 'á†´',
  'ã„¿' => 'á†µ',
  'ã…€' => 'á„š',
  'ã…�' => 'á„†',
  'ã…‚' => 'á„‡',
  'ã…ƒ' => 'á„ˆ',
  'ã…„' => 'á„¡',
  'ã……' => 'á„‰',
  'ã…†' => 'á„Š',
  'ã…‡' => 'á„‹',
  'ã…ˆ' => 'á„Œ',
  'ã…‰' => 'á„�',
  'ã…Š' => 'á„Ž',
  'ã…‹' => 'á„�',
  'ã…Œ' => 'á„�',
  'ã…�' => 'á„‘',
  'ã…Ž' => 'á„’',
  'ã…�' => 'á…¡',
  'ã…�' => 'á…¢',
  'ã…‘' => 'á…£',
  'ã…’' => 'á…¤',
  'ã…“' => 'á…¥',
  'ã…”' => 'á…¦',
  'ã…•' => 'á…§',
  'ã…–' => 'á…¨',
  'ã…—' => 'á…©',
  'ã…˜' => 'á…ª',
  'ã…™' => 'á…«',
  'ã…š' => 'á…¬',
  'ã…›' => 'á…­',
  'ã…œ' => 'á…®',
  'ã…�' => 'á…¯',
  'ã…ž' => 'á…°',
  'ã…Ÿ' => 'á…±',
  'ã… ' => 'á…²',
  'ã…¡' => 'á…³',
  'ã…¢' => 'á…´',
  'ã…£' => 'á…µ',
  'ã…¤' => 'á… ',
  'ã…¥' => 'á„”',
  'ã…¦' => 'á„•',
  'ã…§' => 'á‡‡',
  'ã…¨' => 'á‡ˆ',
  'ã…©' => 'á‡Œ',
  'ã…ª' => 'á‡Ž',
  'ã…«' => 'á‡“',
  'ã…¬' => 'á‡—',
  'ã…­' => 'á‡™',
  'ã…®' => 'á„œ',
  'ã…¯' => 'á‡�',
  'ã…°' => 'á‡Ÿ',
  'ã…±' => 'á„�',
  'ã…²' => 'á„ž',
  'ã…³' => 'á„ ',
  'ã…´' => 'á„¢',
  'ã…µ' => 'á„£',
  'ã…¶' => 'á„§',
  'ã…·' => 'á„©',
  'ã…¸' => 'á„«',
  'ã…¹' => 'á„¬',
  'ã…º' => 'á„­',
  'ã…»' => 'á„®',
  'ã…¼' => 'á„¯',
  'ã…½' => 'á„²',
  'ã…¾' => 'á„¶',
  'ã…¿' => 'á…€',
  'ã†€' => 'á…‡',
  'ã†�' => 'á…Œ',
  'ã†‚' => 'á‡±',
  'ã†ƒ' => 'á‡²',
  'ã†„' => 'á…—',
  'ã†…' => 'á…˜',
  'ã††' => 'á…™',
  'ã†‡' => 'á†„',
  'ã†ˆ' => 'á†…',
  'ã†‰' => 'á†ˆ',
  'ã†Š' => 'á†‘',
  'ã†‹' => 'á†’',
  'ã†Œ' => 'á†”',
  'ã†�' => 'á†ž',
  'ã†Ž' => 'á†¡',
  'ãˆ€' => '(á„€)',
  'ãˆ�' => '(á„‚)',
  'ãˆ‚' => '(á„ƒ)',
  'ãˆƒ' => '(á„…)',
  'ãˆ„' => '(á„†)',
  'ãˆ…' => '(á„‡)',
  'ãˆ†' => '(á„‰)',
  'ãˆ‡' => '(á„‹)',
  'ãˆˆ' => '(á„Œ)',
  'ãˆ‰' => '(á„Ž)',
  'ãˆŠ' => '(á„�)',
  'ãˆ‹' => '(á„�)',
  'ãˆŒ' => '(á„‘)',
  'ãˆ�' => '(á„’)',
  'ãˆŽ' => '(á„€á…¡)',
  'ãˆ�' => '(á„‚á…¡)',
  'ãˆ�' => '(á„ƒá…¡)',
  'ãˆ‘' => '(á„…á…¡)',
  'ãˆ’' => '(á„†á…¡)',
  'ãˆ“' => '(á„‡á…¡)',
  'ãˆ”' => '(á„‰á…¡)',
  'ãˆ•' => '(á„‹á…¡)',
  'ãˆ–' => '(á„Œá…¡)',
  'ãˆ—' => '(á„Žá…¡)',
  'ãˆ˜' => '(á„�á…¡)',
  'ãˆ™' => '(á„�á…¡)',
  'ãˆš' => '(á„‘á…¡)',
  'ãˆ›' => '(á„’á…¡)',
  'ãˆœ' => '(á„Œá…®)',
  'ãˆ�' => '(á„‹á…©á„Œá…¥á†«)',
  'ãˆž' => '(á„‹á…©á„’á…®)',
  'ãˆ ' => '(ä¸€)',
  'ãˆ¡' => '(äºŒ)',
  'ãˆ¢' => '(ä¸‰)',
  'ãˆ£' => '(å››)',
  'ãˆ¤' => '(äº”)',
  'ãˆ¥' => '(å…­)',
  'ãˆ¦' => '(ä¸ƒ)',
  'ãˆ§' => '(å…«)',
  'ãˆ¨' => '(ä¹�)',
  'ãˆ©' => '(å��)',
  'ãˆª' => '(æœˆ)',
  'ãˆ«' => '(ç�«)',
  'ãˆ¬' => '(æ°´)',
  'ãˆ­' => '(æœ¨)',
  'ãˆ®' => '(é‡‘)',
  'ãˆ¯' => '(åœŸ)',
  'ãˆ°' => '(æ—¥)',
  'ãˆ±' => '(æ ª)',
  'ãˆ²' => '(æœ‰)',
  'ãˆ³' => '(ç¤¾)',
  'ãˆ´' => '(å��)',
  'ãˆµ' => '(ç‰¹)',
  'ãˆ¶' => '(è²¡)',
  'ãˆ·' => '(ç¥�)',
  'ãˆ¸' => '(åŠ´)',
  'ãˆ¹' => '(ä»£)',
  'ãˆº' => '(å‘¼)',
  'ãˆ»' => '(å­¦)',
  'ãˆ¼' => '(ç›£)',
  'ãˆ½' => '(ä¼�)',
  'ãˆ¾' => '(è³‡)',
  'ãˆ¿' => '(å�”)',
  'ã‰€' => '(ç¥­)',
  'ã‰�' => '(ä¼‘)',
  'ã‰‚' => '(è‡ª)',
  'ã‰ƒ' => '(è‡³)',
  'ã‰„' => '(å•�)',
  'ã‰…' => '(å¹¼)',
  'ã‰†' => '(æ–‡)',
  'ã‰‡' => '(ç®�)',
  'ã‰�' => 'PTE',
  'ã‰‘' => '(21)',
  'ã‰’' => '(22)',
  'ã‰“' => '(23)',
  'ã‰”' => '(24)',
  'ã‰•' => '(25)',
  'ã‰–' => '(26)',
  'ã‰—' => '(27)',
  'ã‰˜' => '(28)',
  'ã‰™' => '(29)',
  'ã‰š' => '(30)',
  'ã‰›' => '(31)',
  'ã‰œ' => '(32)',
  'ã‰�' => '(33)',
  'ã‰ž' => '(34)',
  'ã‰Ÿ' => '(35)',
  'ã‰ ' => '(á„€)',
  'ã‰¡' => '(á„‚)',
  'ã‰¢' => '(á„ƒ)',
  'ã‰£' => '(á„…)',
  'ã‰¤' => '(á„†)',
  'ã‰¥' => '(á„‡)',
  'ã‰¦' => '(á„‰)',
  'ã‰§' => '(á„‹)',
  'ã‰¨' => '(á„Œ)',
  'ã‰©' => '(á„Ž)',
  'ã‰ª' => '(á„�)',
  'ã‰«' => '(á„�)',
  'ã‰¬' => '(á„‘)',
  'ã‰­' => '(á„’)',
  'ã‰®' => '(á„€á…¡)',
  'ã‰¯' => '(á„‚á…¡)',
  'ã‰°' => '(á„ƒá…¡)',
  'ã‰±' => '(á„…á…¡)',
  'ã‰²' => '(á„†á…¡)',
  'ã‰³' => '(á„‡á…¡)',
  'ã‰´' => '(á„‰á…¡)',
  'ã‰µ' => '(á„‹á…¡)',
  'ã‰¶' => '(á„Œá…¡)',
  'ã‰·' => '(á„Žá…¡)',
  'ã‰¸' => '(á„�á…¡)',
  'ã‰¹' => '(á„�á…¡)',
  'ã‰º' => '(á„‘á…¡)',
  'ã‰»' => '(á„’á…¡)',
  'ã‰¼' => '(á„Žá…¡á†·á„€á…©)',
  'ã‰½' => '(á„Œá…®á„‹á…´)',
  'ã‰¾' => '(á„‹á…®)',
  'ãŠ€' => '(ä¸€)',
  'ãŠ�' => '(äºŒ)',
  'ãŠ‚' => '(ä¸‰)',
  'ãŠƒ' => '(å››)',
  'ãŠ„' => '(äº”)',
  'ãŠ…' => '(å…­)',
  'ãŠ†' => '(ä¸ƒ)',
  'ãŠ‡' => '(å…«)',
  'ãŠˆ' => '(ä¹�)',
  'ãŠ‰' => '(å��)',
  'ãŠŠ' => '(æœˆ)',
  'ãŠ‹' => '(ç�«)',
  'ãŠŒ' => '(æ°´)',
  'ãŠ�' => '(æœ¨)',
  'ãŠŽ' => '(é‡‘)',
  'ãŠ�' => '(åœŸ)',
  'ãŠ�' => '(æ—¥)',
  'ãŠ‘' => '(æ ª)',
  'ãŠ’' => '(æœ‰)',
  'ãŠ“' => '(ç¤¾)',
  'ãŠ”' => '(å��)',
  'ãŠ•' => '(ç‰¹)',
  'ãŠ–' => '(è²¡)',
  'ãŠ—' => '(ç¥�)',
  'ãŠ˜' => '(åŠ´)',
  'ãŠ™' => '(ç§˜)',
  'ãŠš' => '(ç”·)',
  'ãŠ›' => '(å¥³)',
  'ãŠœ' => '(é�©)',
  'ãŠ�' => '(å„ª)',
  'ãŠž' => '(å�°)',
  'ãŠŸ' => '(æ³¨)',
  'ãŠ ' => '(é …)',
  'ãŠ¡' => '(ä¼‘)',
  'ãŠ¢' => '(å†™)',
  'ãŠ£' => '(æ­£)',
  'ãŠ¤' => '(ä¸Š)',
  'ãŠ¥' => '(ä¸­)',
  'ãŠ¦' => '(ä¸‹)',
  'ãŠ§' => '(å·¦)',
  'ãŠ¨' => '(å�³)',
  'ãŠ©' => '(åŒ»)',
  'ãŠª' => '(å®—)',
  'ãŠ«' => '(å­¦)',
  'ãŠ¬' => '(ç›£)',
  'ãŠ­' => '(ä¼�)',
  'ãŠ®' => '(è³‡)',
  'ãŠ¯' => '(å�”)',
  'ãŠ°' => '(å¤œ)',
  'ãŠ±' => '(36)',
  'ãŠ²' => '(37)',
  'ãŠ³' => '(38)',
  'ãŠ´' => '(39)',
  'ãŠµ' => '(40)',
  'ãŠ¶' => '(41)',
  'ãŠ·' => '(42)',
  'ãŠ¸' => '(43)',
  'ãŠ¹' => '(44)',
  'ãŠº' => '(45)',
  'ãŠ»' => '(46)',
  'ãŠ¼' => '(47)',
  'ãŠ½' => '(48)',
  'ãŠ¾' => '(49)',
  'ãŠ¿' => '(50)',
  'ã‹€' => '1æœˆ',
  'ã‹�' => '2æœˆ',
  'ã‹‚' => '3æœˆ',
  'ã‹ƒ' => '4æœˆ',
  'ã‹„' => '5æœˆ',
  'ã‹…' => '6æœˆ',
  'ã‹†' => '7æœˆ',
  'ã‹‡' => '8æœˆ',
  'ã‹ˆ' => '9æœˆ',
  'ã‹‰' => '10æœˆ',
  'ã‹Š' => '11æœˆ',
  'ã‹‹' => '12æœˆ',
  'ã‹Œ' => 'Hg',
  'ã‹�' => 'erg',
  'ã‹Ž' => 'eV',
  'ã‹�' => 'LTD',
  'ã‹�' => '(ã‚¢)',
  'ã‹‘' => '(ã‚¤)',
  'ã‹’' => '(ã‚¦)',
  'ã‹“' => '(ã‚¨)',
  'ã‹”' => '(ã‚ª)',
  'ã‹•' => '(ã‚«)',
  'ã‹–' => '(ã‚­)',
  'ã‹—' => '(ã‚¯)',
  'ã‹˜' => '(ã‚±)',
  'ã‹™' => '(ã‚³)',
  'ã‹š' => '(ã‚µ)',
  'ã‹›' => '(ã‚·)',
  'ã‹œ' => '(ã‚¹)',
  'ã‹�' => '(ã‚»)',
  'ã‹ž' => '(ã‚½)',
  'ã‹Ÿ' => '(ã‚¿)',
  'ã‹ ' => '(ãƒ�)',
  'ã‹¡' => '(ãƒ„)',
  'ã‹¢' => '(ãƒ†)',
  'ã‹£' => '(ãƒˆ)',
  'ã‹¤' => '(ãƒŠ)',
  'ã‹¥' => '(ãƒ‹)',
  'ã‹¦' => '(ãƒŒ)',
  'ã‹§' => '(ãƒ�)',
  'ã‹¨' => '(ãƒŽ)',
  'ã‹©' => '(ãƒ�)',
  'ã‹ª' => '(ãƒ’)',
  'ã‹«' => '(ãƒ•)',
  'ã‹¬' => '(ãƒ˜)',
  'ã‹­' => '(ãƒ›)',
  'ã‹®' => '(ãƒž)',
  'ã‹¯' => '(ãƒŸ)',
  'ã‹°' => '(ãƒ )',
  'ã‹±' => '(ãƒ¡)',
  'ã‹²' => '(ãƒ¢)',
  'ã‹³' => '(ãƒ¤)',
  'ã‹´' => '(ãƒ¦)',
  'ã‹µ' => '(ãƒ¨)',
  'ã‹¶' => '(ãƒ©)',
  'ã‹·' => '(ãƒª)',
  'ã‹¸' => '(ãƒ«)',
  'ã‹¹' => '(ãƒ¬)',
  'ã‹º' => '(ãƒ­)',
  'ã‹»' => '(ãƒ¯)',
  'ã‹¼' => '(ãƒ°)',
  'ã‹½' => '(ãƒ±)',
  'ã‹¾' => '(ãƒ²)',
  'ã‹¿' => 'ä»¤å’Œ',
  'ãŒ€' => 'ã‚¢ãƒ‘ãƒ¼ãƒˆ',
  'ãŒ�' => 'ã‚¢ãƒ«ãƒ•ã‚¡',
  'ãŒ‚' => 'ã‚¢ãƒ³ãƒšã‚¢',
  'ãŒƒ' => 'ã‚¢ãƒ¼ãƒ«',
  'ãŒ„' => 'ã‚¤ãƒ‹ãƒ³ã‚°',
  'ãŒ…' => 'ã‚¤ãƒ³ãƒ�',
  'ãŒ†' => 'ã‚¦ã‚©ãƒ³',
  'ãŒ‡' => 'ã‚¨ã‚¹ã‚¯ãƒ¼ãƒ‰',
  'ãŒˆ' => 'ã‚¨ãƒ¼ã‚«ãƒ¼',
  'ãŒ‰' => 'ã‚ªãƒ³ã‚¹',
  'ãŒŠ' => 'ã‚ªãƒ¼ãƒ ',
  'ãŒ‹' => 'ã‚«ã‚¤ãƒª',
  'ãŒŒ' => 'ã‚«ãƒ©ãƒƒãƒˆ',
  'ãŒ�' => 'ã‚«ãƒ­ãƒªãƒ¼',
  'ãŒŽ' => 'ã‚¬ãƒ­ãƒ³',
  'ãŒ�' => 'ã‚¬ãƒ³ãƒž',
  'ãŒ�' => 'ã‚®ã‚¬',
  'ãŒ‘' => 'ã‚®ãƒ‹ãƒ¼',
  'ãŒ’' => 'ã‚­ãƒ¥ãƒªãƒ¼',
  'ãŒ“' => 'ã‚®ãƒ«ãƒ€ãƒ¼',
  'ãŒ”' => 'ã‚­ãƒ­',
  'ãŒ•' => 'ã‚­ãƒ­ã‚°ãƒ©ãƒ ',
  'ãŒ–' => 'ã‚­ãƒ­ãƒ¡ãƒ¼ãƒˆãƒ«',
  'ãŒ—' => 'ã‚­ãƒ­ãƒ¯ãƒƒãƒˆ',
  'ãŒ˜' => 'ã‚°ãƒ©ãƒ ',
  'ãŒ™' => 'ã‚°ãƒ©ãƒ ãƒˆãƒ³',
  'ãŒš' => 'ã‚¯ãƒ«ã‚¼ã‚¤ãƒ­',
  'ãŒ›' => 'ã‚¯ãƒ­ãƒ¼ãƒ�',
  'ãŒœ' => 'ã‚±ãƒ¼ã‚¹',
  'ãŒ�' => 'ã‚³ãƒ«ãƒŠ',
  'ãŒž' => 'ã‚³ãƒ¼ãƒ�',
  'ãŒŸ' => 'ã‚µã‚¤ã‚¯ãƒ«',
  'ãŒ ' => 'ã‚µãƒ³ãƒ�ãƒ¼ãƒ ',
  'ãŒ¡' => 'ã‚·ãƒªãƒ³ã‚°',
  'ãŒ¢' => 'ã‚»ãƒ³ãƒ�',
  'ãŒ£' => 'ã‚»ãƒ³ãƒˆ',
  'ãŒ¤' => 'ãƒ€ãƒ¼ã‚¹',
  'ãŒ¥' => 'ãƒ‡ã‚·',
  'ãŒ¦' => 'ãƒ‰ãƒ«',
  'ãŒ§' => 'ãƒˆãƒ³',
  'ãŒ¨' => 'ãƒŠãƒŽ',
  'ãŒ©' => 'ãƒŽãƒƒãƒˆ',
  'ãŒª' => 'ãƒ�ã‚¤ãƒ„',
  'ãŒ«' => 'ãƒ‘ãƒ¼ã‚»ãƒ³ãƒˆ',
  'ãŒ¬' => 'ãƒ‘ãƒ¼ãƒ„',
  'ãŒ­' => 'ãƒ�ãƒ¼ãƒ¬ãƒ«',
  'ãŒ®' => 'ãƒ”ã‚¢ã‚¹ãƒˆãƒ«',
  'ãŒ¯' => 'ãƒ”ã‚¯ãƒ«',
  'ãŒ°' => 'ãƒ”ã‚³',
  'ãŒ±' => 'ãƒ“ãƒ«',
  'ãŒ²' => 'ãƒ•ã‚¡ãƒ©ãƒƒãƒ‰',
  'ãŒ³' => 'ãƒ•ã‚£ãƒ¼ãƒˆ',
  'ãŒ´' => 'ãƒ–ãƒƒã‚·ã‚§ãƒ«',
  'ãŒµ' => 'ãƒ•ãƒ©ãƒ³',
  'ãŒ¶' => 'ãƒ˜ã‚¯ã‚¿ãƒ¼ãƒ«',
  'ãŒ·' => 'ãƒšã‚½',
  'ãŒ¸' => 'ãƒšãƒ‹ãƒ’',
  'ãŒ¹' => 'ãƒ˜ãƒ«ãƒ„',
  'ãŒº' => 'ãƒšãƒ³ã‚¹',
  'ãŒ»' => 'ãƒšãƒ¼ã‚¸',
  'ãŒ¼' => 'ãƒ™ãƒ¼ã‚¿',
  'ãŒ½' => 'ãƒ�ã‚¤ãƒ³ãƒˆ',
  'ãŒ¾' => 'ãƒœãƒ«ãƒˆ',
  'ãŒ¿' => 'ãƒ›ãƒ³',
  'ã�€' => 'ãƒ�ãƒ³ãƒ‰',
  'ã��' => 'ãƒ›ãƒ¼ãƒ«',
  'ã�‚' => 'ãƒ›ãƒ¼ãƒ³',
  'ã�ƒ' => 'ãƒžã‚¤ã‚¯ãƒ­',
  'ã�„' => 'ãƒžã‚¤ãƒ«',
  'ã�…' => 'ãƒžãƒƒãƒ�',
  'ã�†' => 'ãƒžãƒ«ã‚¯',
  'ã�‡' => 'ãƒžãƒ³ã‚·ãƒ§ãƒ³',
  'ã�ˆ' => 'ãƒŸã‚¯ãƒ­ãƒ³',
  'ã�‰' => 'ãƒŸãƒª',
  'ã�Š' => 'ãƒŸãƒªãƒ�ãƒ¼ãƒ«',
  'ã�‹' => 'ãƒ¡ã‚¬',
  'ã�Œ' => 'ãƒ¡ã‚¬ãƒˆãƒ³',
  'ã��' => 'ãƒ¡ãƒ¼ãƒˆãƒ«',
  'ã�Ž' => 'ãƒ¤ãƒ¼ãƒ‰',
  'ã��' => 'ãƒ¤ãƒ¼ãƒ«',
  'ã��' => 'ãƒ¦ã‚¢ãƒ³',
  'ã�‘' => 'ãƒªãƒƒãƒˆãƒ«',
  'ã�’' => 'ãƒªãƒ©',
  'ã�“' => 'ãƒ«ãƒ”ãƒ¼',
  'ã�”' => 'ãƒ«ãƒ¼ãƒ–ãƒ«',
  'ã�•' => 'ãƒ¬ãƒ ',
  'ã�–' => 'ãƒ¬ãƒ³ãƒˆã‚²ãƒ³',
  'ã�—' => 'ãƒ¯ãƒƒãƒˆ',
  'ã�˜' => '0ç‚¹',
  'ã�™' => '1ç‚¹',
  'ã�š' => '2ç‚¹',
  'ã�›' => '3ç‚¹',
  'ã�œ' => '4ç‚¹',
  'ã��' => '5ç‚¹',
  'ã�ž' => '6ç‚¹',
  'ã�Ÿ' => '7ç‚¹',
  'ã� ' => '8ç‚¹',
  'ã�¡' => '9ç‚¹',
  'ã�¢' => '10ç‚¹',
  'ã�£' => '11ç‚¹',
  'ã�¤' => '12ç‚¹',
  'ã�¥' => '13ç‚¹',
  'ã�¦' => '14ç‚¹',
  'ã�§' => '15ç‚¹',
  'ã�¨' => '16ç‚¹',
  'ã�©' => '17ç‚¹',
  'ã�ª' => '18ç‚¹',
  'ã�«' => '19ç‚¹',
  'ã�¬' => '20ç‚¹',
  'ã�­' => '21ç‚¹',
  'ã�®' => '22ç‚¹',
  'ã�¯' => '23ç‚¹',
  'ã�°' => '24ç‚¹',
  'ã�±' => 'hPa',
  'ã�²' => 'da',
  'ã�³' => 'AU',
  'ã�´' => 'bar',
  'ã�µ' => 'oV',
  'ã�¶' => 'pc',
  'ã�·' => 'dm',
  'ã�¸' => 'dmÂ²',
  'ã�¹' => 'dmÂ³',
  'ã�º' => 'IU',
  'ã�»' => 'å¹³æˆ�',
  'ã�¼' => 'æ˜­å’Œ',
  'ã�½' => 'å¤§æ­£',
  'ã�¾' => 'æ˜Žæ²»',
  'ã�¿' => 'æ ªå¼�ä¼šç¤¾',
  'ãŽ€' => 'pA',
  'ãŽ�' => 'nA',
  'ãŽ‚' => 'Î¼A',
  'ãŽƒ' => 'mA',
  'ãŽ„' => 'kA',
  'ãŽ…' => 'KB',
  'ãŽ†' => 'MB',
  'ãŽ‡' => 'GB',
  'ãŽˆ' => 'cal',
  'ãŽ‰' => 'kcal',
  'ãŽŠ' => 'pF',
  'ãŽ‹' => 'nF',
  'ãŽŒ' => 'Î¼F',
  'ãŽ�' => 'Î¼g',
  'ãŽŽ' => 'mg',
  'ãŽ�' => 'kg',
  'ãŽ�' => 'Hz',
  'ãŽ‘' => 'kHz',
  'ãŽ’' => 'MHz',
  'ãŽ“' => 'GHz',
  'ãŽ”' => 'THz',
  'ãŽ•' => 'Î¼â„“',
  'ãŽ–' => 'mâ„“',
  'ãŽ—' => 'dâ„“',
  'ãŽ˜' => 'kâ„“',
  'ãŽ™' => 'fm',
  'ãŽš' => 'nm',
  'ãŽ›' => 'Î¼m',
  'ãŽœ' => 'mm',
  'ãŽ�' => 'cm',
  'ãŽž' => 'km',
  'ãŽŸ' => 'mmÂ²',
  'ãŽ ' => 'cmÂ²',
  'ãŽ¡' => 'mÂ²',
  'ãŽ¢' => 'kmÂ²',
  'ãŽ£' => 'mmÂ³',
  'ãŽ¤' => 'cmÂ³',
  'ãŽ¥' => 'mÂ³',
  'ãŽ¦' => 'kmÂ³',
  'ãŽ§' => 'mâˆ•s',
  'ãŽ¨' => 'mâˆ•sÂ²',
  'ãŽ©' => 'Pa',
  'ãŽª' => 'kPa',
  'ãŽ«' => 'MPa',
  'ãŽ¬' => 'GPa',
  'ãŽ­' => 'rad',
  'ãŽ®' => 'radâˆ•s',
  'ãŽ¯' => 'radâˆ•sÂ²',
  'ãŽ°' => 'ps',
  'ãŽ±' => 'ns',
  'ãŽ²' => 'Î¼s',
  'ãŽ³' => 'ms',
  'ãŽ´' => 'pV',
  'ãŽµ' => 'nV',
  'ãŽ¶' => 'Î¼V',
  'ãŽ·' => 'mV',
  'ãŽ¸' => 'kV',
  'ãŽ¹' => 'MV',
  'ãŽº' => 'pW',
  'ãŽ»' => 'nW',
  'ãŽ¼' => 'Î¼W',
  'ãŽ½' => 'mW',
  'ãŽ¾' => 'kW',
  'ãŽ¿' => 'MW',
  'ã�€' => 'kÎ©',
  'ã��' => 'MÎ©',
  'ã�‚' => 'a.m.',
  'ã�ƒ' => 'Bq',
  'ã�„' => 'cc',
  'ã�…' => 'cd',
  'ã�†' => 'Câˆ•kg',
  'ã�‡' => 'Co.',
  'ã�ˆ' => 'dB',
  'ã�‰' => 'Gy',
  'ã�Š' => 'ha',
  'ã�‹' => 'HP',
  'ã�Œ' => 'in',
  'ã��' => 'KK',
  'ã�Ž' => 'KM',
  'ã��' => 'kt',
  'ã��' => 'lm',
  'ã�‘' => 'ln',
  'ã�’' => 'log',
  'ã�“' => 'lx',
  'ã�”' => 'mb',
  'ã�•' => 'mil',
  'ã�–' => 'mol',
  'ã�—' => 'PH',
  'ã�˜' => 'p.m.',
  'ã�™' => 'PPM',
  'ã�š' => 'PR',
  'ã�›' => 'sr',
  'ã�œ' => 'Sv',
  'ã��' => 'Wb',
  'ã�ž' => 'Vâˆ•m',
  'ã�Ÿ' => 'Aâˆ•m',
  'ã� ' => '1æ—¥',
  'ã�¡' => '2æ—¥',
  'ã�¢' => '3æ—¥',
  'ã�£' => '4æ—¥',
  'ã�¤' => '5æ—¥',
  'ã�¥' => '6æ—¥',
  'ã�¦' => '7æ—¥',
  'ã�§' => '8æ—¥',
  'ã�¨' => '9æ—¥',
  'ã�©' => '10æ—¥',
  'ã�ª' => '11æ—¥',
  'ã�«' => '12æ—¥',
  'ã�¬' => '13æ—¥',
  'ã�­' => '14æ—¥',
  'ã�®' => '15æ—¥',
  'ã�¯' => '16æ—¥',
  'ã�°' => '17æ—¥',
  'ã�±' => '18æ—¥',
  'ã�²' => '19æ—¥',
  'ã�³' => '20æ—¥',
  'ã�´' => '21æ—¥',
  'ã�µ' => '22æ—¥',
  'ã�¶' => '23æ—¥',
  'ã�·' => '24æ—¥',
  'ã�¸' => '25æ—¥',
  'ã�¹' => '26æ—¥',
  'ã�º' => '27æ—¥',
  'ã�»' => '28æ—¥',
  'ã�¼' => '29æ—¥',
  'ã�½' => '30æ—¥',
  'ã�¾' => '31æ—¥',
  'ã�¿' => 'gal',
  'ï¤€' => 'è±ˆ',
  'ï¤�' => 'æ›´',
  'ï¤‚' => 'è»Š',
  'ï¤ƒ' => 'è³ˆ',
  'ï¤„' => 'æ»‘',
  'ï¤…' => 'ä¸²',
  'ï¤†' => 'å�¥',
  'ï¤‡' => 'é¾œ',
  'ï¤ˆ' => 'é¾œ',
  'ï¤‰' => 'å¥‘',
  'ï¤Š' => 'é‡‘',
  'ï¤‹' => 'å–‡',
  'ï¤Œ' => 'å¥ˆ',
  'ï¤�' => 'æ‡¶',
  'ï¤Ž' => 'ç™©',
  'ï¤�' => 'ç¾…',
  'ï¤�' => 'è˜¿',
  'ï¤‘' => 'èžº',
  'ï¤’' => 'è£¸',
  'ï¤“' => 'é‚�',
  'ï¤”' => 'æ¨‚',
  'ï¤•' => 'æ´›',
  'ï¤–' => 'çƒ™',
  'ï¤—' => 'ç�ž',
  'ï¤˜' => 'è�½',
  'ï¤™' => 'é…ª',
  'ï¤š' => 'é§±',
  'ï¤›' => 'äº‚',
  'ï¤œ' => 'å�µ',
  'ï¤�' => 'æ¬„',
  'ï¤ž' => 'çˆ›',
  'ï¤Ÿ' => 'è˜­',
  'ï¤ ' => 'é¸ž',
  'ï¤¡' => 'åµ�',
  'ï¤¢' => 'æ¿«',
  'ï¤£' => 'è—�',
  'ï¤¤' => 'è¥¤',
  'ï¤¥' => 'æ‹‰',
  'ï¤¦' => 'è‡˜',
  'ï¤§' => 'è Ÿ',
  'ï¤¨' => 'å»Š',
  'ï¤©' => 'æœ—',
  'ï¤ª' => 'æµª',
  'ï¤«' => 'ç‹¼',
  'ï¤¬' => 'éƒŽ',
  'ï¤­' => 'ä¾†',
  'ï¤®' => 'å†·',
  'ï¤¯' => 'å‹ž',
  'ï¤°' => 'æ“„',
  'ï¤±' => 'æ«“',
  'ï¤²' => 'çˆ�',
  'ï¤³' => 'ç›§',
  'ï¤´' => 'è€�',
  'ï¤µ' => 'è˜†',
  'ï¤¶' => 'è™œ',
  'ï¤·' => 'è·¯',
  'ï¤¸' => 'éœ²',
  'ï¤¹' => 'é­¯',
  'ï¤º' => 'é·º',
  'ï¤»' => 'ç¢Œ',
  'ï¤¼' => 'ç¥¿',
  'ï¤½' => 'ç¶ ',
  'ï¤¾' => 'è�‰',
  'ï¤¿' => 'éŒ„',
  'ï¥€' => 'é¹¿',
  'ï¥�' => 'è«–',
  'ï¥‚' => 'å£Ÿ',
  'ï¥ƒ' => 'å¼„',
  'ï¥„' => 'ç± ',
  'ï¥…' => 'è�¾',
  'ï¥†' => 'ç‰¢',
  'ï¥‡' => 'ç£Š',
  'ï¥ˆ' => 'è³‚',
  'ï¥‰' => 'é›·',
  'ï¥Š' => 'å£˜',
  'ï¥‹' => 'å±¢',
  'ï¥Œ' => 'æ¨“',
  'ï¥�' => 'æ·š',
  'ï¥Ž' => 'æ¼�',
  'ï¥�' => 'ç´¯',
  'ï¥�' => 'ç¸·',
  'ï¥‘' => 'é™‹',
  'ï¥’' => 'å‹’',
  'ï¥“' => 'è‚‹',
  'ï¥”' => 'å‡œ',
  'ï¥•' => 'å‡Œ',
  'ï¥–' => 'ç¨œ',
  'ï¥—' => 'ç¶¾',
  'ï¥˜' => 'è�±',
  'ï¥™' => 'é™µ',
  'ï¥š' => 'è®€',
  'ï¥›' => 'æ‹�',
  'ï¥œ' => 'æ¨‚',
  'ï¥�' => 'è«¾',
  'ï¥ž' => 'ä¸¹',
  'ï¥Ÿ' => 'å¯§',
  'ï¥ ' => 'æ€’',
  'ï¥¡' => 'çŽ‡',
  'ï¥¢' => 'ç•°',
  'ï¥£' => 'åŒ—',
  'ï¥¤' => 'ç£»',
  'ï¥¥' => 'ä¾¿',
  'ï¥¦' => 'å¾©',
  'ï¥§' => 'ä¸�',
  'ï¥¨' => 'æ³Œ',
  'ï¥©' => 'æ•¸',
  'ï¥ª' => 'ç´¢',
  'ï¥«' => 'å�ƒ',
  'ï¥¬' => 'å¡ž',
  'ï¥­' => 'çœ�',
  'ï¥®' => 'è‘‰',
  'ï¥¯' => 'èªª',
  'ï¥°' => 'æ®º',
  'ï¥±' => 'è¾°',
  'ï¥²' => 'æ²ˆ',
  'ï¥³' => 'æ‹¾',
  'ï¥´' => 'è‹¥',
  'ï¥µ' => 'æŽ ',
  'ï¥¶' => 'ç•¥',
  'ï¥·' => 'äº®',
  'ï¥¸' => 'å…©',
  'ï¥¹' => 'å‡‰',
  'ï¥º' => 'æ¢�',
  'ï¥»' => 'ç³§',
  'ï¥¼' => 'è‰¯',
  'ï¥½' => 'è«’',
  'ï¥¾' => 'é‡�',
  'ï¥¿' => 'å‹µ',
  'ï¦€' => 'å‘‚',
  'ï¦�' => 'å¥³',
  'ï¦‚' => 'å»¬',
  'ï¦ƒ' => 'æ—…',
  'ï¦„' => 'æ¿¾',
  'ï¦…' => 'ç¤ª',
  'ï¦†' => 'é–­',
  'ï¦‡' => 'é©ª',
  'ï¦ˆ' => 'éº—',
  'ï¦‰' => 'é»Ž',
  'ï¦Š' => 'åŠ›',
  'ï¦‹' => 'æ›†',
  'ï¦Œ' => 'æ­·',
  'ï¦�' => 'è½¢',
  'ï¦Ž' => 'å¹´',
  'ï¦�' => 'æ†�',
  'ï¦�' => 'æˆ€',
  'ï¦‘' => 'æ’š',
  'ï¦’' => 'æ¼£',
  'ï¦“' => 'ç…‰',
  'ï¦”' => 'ç’‰',
  'ï¦•' => 'ç§Š',
  'ï¦–' => 'ç·´',
  'ï¦—' => 'è�¯',
  'ï¦˜' => 'è¼¦',
  'ï¦™' => 'è“®',
  'ï¦š' => 'é€£',
  'ï¦›' => 'é�Š',
  'ï¦œ' => 'åˆ—',
  'ï¦�' => 'åŠ£',
  'ï¦ž' => 'å’½',
  'ï¦Ÿ' => 'çƒˆ',
  'ï¦ ' => 'è£‚',
  'ï¦¡' => 'èªª',
  'ï¦¢' => 'å»‰',
  'ï¦£' => 'å¿µ',
  'ï¦¤' => 'æ�»',
  'ï¦¥' => 'æ®®',
  'ï¦¦' => 'ç°¾',
  'ï¦§' => 'ç�µ',
  'ï¦¨' => 'ä»¤',
  'ï¦©' => 'å›¹',
  'ï¦ª' => 'å¯§',
  'ï¦«' => 'å¶º',
  'ï¦¬' => 'æ€œ',
  'ï¦­' => 'çŽ²',
  'ï¦®' => 'ç‘©',
  'ï¦¯' => 'ç¾š',
  'ï¦°' => 'è�†',
  'ï¦±' => 'éˆ´',
  'ï¦²' => 'é›¶',
  'ï¦³' => 'é�ˆ',
  'ï¦´' => 'é ˜',
  'ï¦µ' => 'ä¾‹',
  'ï¦¶' => 'ç¦®',
  'ï¦·' => 'é†´',
  'ï¦¸' => 'éš¸',
  'ï¦¹' => 'æƒ¡',
  'ï¦º' => 'äº†',
  'ï¦»' => 'åƒš',
  'ï¦¼' => 'å¯®',
  'ï¦½' => 'å°¿',
  'ï¦¾' => 'æ–™',
  'ï¦¿' => 'æ¨‚',
  'ï§€' => 'ç‡Ž',
  'ï§�' => 'ç™‚',
  'ï§‚' => 'è“¼',
  'ï§ƒ' => 'é�¼',
  'ï§„' => 'é¾�',
  'ï§…' => 'æšˆ',
  'ï§†' => 'é˜®',
  'ï§‡' => 'åŠ‰',
  'ï§ˆ' => 'æ�»',
  'ï§‰' => 'æŸ³',
  'ï§Š' => 'æµ�',
  'ï§‹' => 'æºœ',
  'ï§Œ' => 'ç�‰',
  'ï§�' => 'ç•™',
  'ï§Ž' => 'ç¡«',
  'ï§�' => 'ç´�',
  'ï§�' => 'é¡ž',
  'ï§‘' => 'å…­',
  'ï§’' => 'æˆ®',
  'ï§“' => 'é™¸',
  'ï§”' => 'å€«',
  'ï§•' => 'å´™',
  'ï§–' => 'æ·ª',
  'ï§—' => 'è¼ª',
  'ï§˜' => 'å¾‹',
  'ï§™' => 'æ…„',
  'ï§š' => 'æ —',
  'ï§›' => 'çŽ‡',
  'ï§œ' => 'éš†',
  'ï§�' => 'åˆ©',
  'ï§ž' => 'å��',
  'ï§Ÿ' => 'å±¥',
  'ï§ ' => 'æ˜“',
  'ï§¡' => 'æ�Ž',
  'ï§¢' => 'æ¢¨',
  'ï§£' => 'æ³¥',
  'ï§¤' => 'ç�†',
  'ï§¥' => 'ç—¢',
  'ï§¦' => 'ç½¹',
  'ï§§' => 'è£�',
  'ï§¨' => 'è£¡',
  'ï§©' => 'é‡Œ',
  'ï§ª' => 'é›¢',
  'ï§«' => 'åŒ¿',
  'ï§¬' => 'æºº',
  'ï§­' => 'å��',
  'ï§®' => 'ç‡�',
  'ï§¯' => 'ç’˜',
  'ï§°' => 'è—º',
  'ï§±' => 'éš£',
  'ï§²' => 'é±—',
  'ï§³' => 'éºŸ',
  'ï§´' => 'æž—',
  'ï§µ' => 'æ·‹',
  'ï§¶' => 'è‡¨',
  'ï§·' => 'ç«‹',
  'ï§¸' => 'ç¬ ',
  'ï§¹' => 'ç²’',
  'ï§º' => 'ç‹€',
  'ï§»' => 'ç‚™',
  'ï§¼' => 'è­˜',
  'ï§½' => 'ä»€',
  'ï§¾' => 'èŒ¶',
  'ï§¿' => 'åˆº',
  'ï¨€' => 'åˆ‡',
  'ï¨�' => 'åº¦',
  'ï¨‚' => 'æ‹“',
  'ï¨ƒ' => 'ç³–',
  'ï¨„' => 'å®…',
  'ï¨…' => 'æ´ž',
  'ï¨†' => 'æš´',
  'ï¨‡' => 'è¼»',
  'ï¨ˆ' => 'è¡Œ',
  'ï¨‰' => 'é™�',
  'ï¨Š' => 'è¦‹',
  'ï¨‹' => 'å»“',
  'ï¨Œ' => 'å…€',
  'ï¨�' => 'å—€',
  'ï¨Ž' => '' . "\0" . '',
  'ï¨�' => '' . "\0" . '',
  'ï¨�' => 'å¡š',
  'ï¨‘' => '' . "\0" . '',
  'ï¨’' => 'æ™´',
  'ï¨“' => '' . "\0" . '',
  'ï¨”' => '' . "\0" . '',
  'ï¨•' => 'å‡ž',
  'ï¨–' => 'çŒª',
  'ï¨—' => 'ç›Š',
  'ï¨˜' => 'ç¤¼',
  'ï¨™' => 'ç¥ž',
  'ï¨š' => 'ç¥¥',
  'ï¨›' => 'ç¦�',
  'ï¨œ' => 'é�–',
  'ï¨�' => 'ç²¾',
  'ï¨ž' => 'ç¾½',
  'ï¨Ÿ' => '' . "\0" . '',
  'ï¨ ' => 'è˜’',
  'ï¨¡' => '' . "\0" . '',
  'ï¨¢' => 'è«¸',
  'ï¨£' => '' . "\0" . '',
  'ï¨¤' => '' . "\0" . '',
  'ï¨¥' => 'é€¸',
  'ï¨¦' => 'éƒ½',
  'ï¨§' => '' . "\0" . '',
  'ï¨¨' => '' . "\0" . '',
  'ï¨©' => '' . "\0" . '',
  'ï¨ª' => 'é£¯',
  'ï¨«' => 'é£¼',
  'ï¨¬' => 'é¤¨',
  'ï¨­' => 'é¶´',
  'ï¨®' => 'éƒž',
  'ï¨¯' => 'éš·',
  'ï¨°' => 'ä¾®',
  'ï¨±' => 'åƒ§',
  'ï¨²' => 'å…�',
  'ï¨³' => 'å‹‰',
  'ï¨´' => 'å‹¤',
  'ï¨µ' => 'å�‘',
  'ï¨¶' => 'å–�',
  'ï¨·' => 'å˜†',
  'ï¨¸' => 'å™¨',
  'ï¨¹' => 'å¡€',
  'ï¨º' => 'å¢¨',
  'ï¨»' => 'å±¤',
  'ï¨¼' => 'å±®',
  'ï¨½' => 'æ‚”',
  'ï¨¾' => 'æ…¨',
  'ï¨¿' => 'æ†Ž',
  'ï©€' => 'æ‡²',
  'ï©�' => 'æ•�',
  'ï©‚' => 'æ—¢',
  'ï©ƒ' => 'æš‘',
  'ï©„' => 'æ¢…',
  'ï©…' => 'æµ·',
  'ï©†' => 'æ¸š',
  'ï©‡' => 'æ¼¢',
  'ï©ˆ' => 'ç…®',
  'ï©‰' => 'çˆ«',
  'ï©Š' => 'ç�¢',
  'ï©‹' => 'ç¢‘',
  'ï©Œ' => 'ç¤¾',
  'ï©�' => 'ç¥‰',
  'ï©Ž' => 'ç¥ˆ',
  'ï©�' => 'ç¥�',
  'ï©�' => 'ç¥–',
  'ï©‘' => 'ç¥�',
  'ï©’' => 'ç¦�',
  'ï©“' => 'ç¦Ž',
  'ï©”' => 'ç©€',
  'ï©•' => 'çª�',
  'ï©–' => 'ç¯€',
  'ï©—' => 'ç·´',
  'ï©˜' => 'ç¸‰',
  'ï©™' => 'ç¹�',
  'ï©š' => 'ç½²',
  'ï©›' => 'è€…',
  'ï©œ' => 'è‡­',
  'ï©�' => 'è‰¹',
  'ï©ž' => 'è‰¹',
  'ï©Ÿ' => 'è‘—',
  'ï© ' => 'è¤�',
  'ï©¡' => 'è¦–',
  'ï©¢' => 'è¬�',
  'ï©£' => 'è¬¹',
  'ï©¤' => 'è³“',
  'ï©¥' => 'è´ˆ',
  'ï©¦' => 'è¾¶',
  'ï©§' => 'é€¸',
  'ï©¨' => 'é›£',
  'ï©©' => 'éŸ¿',
  'ï©ª' => 'é »',
  'ï©«' => 'æ�µ',
  'ï©¬' => 'ð¤‹®',
  'ï©­' => 'èˆ˜',
  'ï©°' => 'ä¸¦',
  'ï©±' => 'å†µ',
  'ï©²' => 'å…¨',
  'ï©³' => 'ä¾€',
  'ï©´' => 'å……',
  'ï©µ' => 'å†€',
  'ï©¶' => 'å‹‡',
  'ï©·' => 'å‹º',
  'ï©¸' => 'å–�',
  'ï©¹' => 'å••',
  'ï©º' => 'å–™',
  'ï©»' => 'å—¢',
  'ï©¼' => 'å¡š',
  'ï©½' => 'å¢³',
  'ï©¾' => 'å¥„',
  'ï©¿' => 'å¥”',
  'ïª€' => 'å©¢',
  'ïª�' => 'å¬¨',
  'ïª‚' => 'å»’',
  'ïªƒ' => 'å»™',
  'ïª„' => 'å½©',
  'ïª…' => 'å¾­',
  'ïª†' => 'æƒ˜',
  'ïª‡' => 'æ…Ž',
  'ïªˆ' => 'æ„ˆ',
  'ïª‰' => 'æ†Ž',
  'ïªŠ' => 'æ… ',
  'ïª‹' => 'æ‡²',
  'ïªŒ' => 'æˆ´',
  'ïª�' => 'æ�„',
  'ïªŽ' => 'æ�œ',
  'ïª�' => 'æ‘’',
  'ïª�' => 'æ•–',
  'ïª‘' => 'æ™´',
  'ïª’' => 'æœ—',
  'ïª“' => 'æœ›',
  'ïª”' => 'æ�–',
  'ïª•' => 'æ­¹',
  'ïª–' => 'æ®º',
  'ïª—' => 'æµ�',
  'ïª˜' => 'æ»›',
  'ïª™' => 'æ»‹',
  'ïªš' => 'æ¼¢',
  'ïª›' => 'ç€ž',
  'ïªœ' => 'ç…®',
  'ïª�' => 'çž§',
  'ïªž' => 'çˆµ',
  'ïªŸ' => 'çŠ¯',
  'ïª ' => 'çŒª',
  'ïª¡' => 'ç‘±',
  'ïª¢' => 'ç”†',
  'ïª£' => 'ç”»',
  'ïª¤' => 'ç˜�',
  'ïª¥' => 'ç˜Ÿ',
  'ïª¦' => 'ç›Š',
  'ïª§' => 'ç››',
  'ïª¨' => 'ç›´',
  'ïª©' => 'ç�Š',
  'ïªª' => 'ç�€',
  'ïª«' => 'ç£Œ',
  'ïª¬' => 'çª±',
  'ïª­' => 'ç¯€',
  'ïª®' => 'ç±»',
  'ïª¯' => 'çµ›',
  'ïª°' => 'ç·´',
  'ïª±' => 'ç¼¾',
  'ïª²' => 'è€…',
  'ïª³' => 'è�’',
  'ïª´' => 'è�¯',
  'ïªµ' => 'è�¹',
  'ïª¶' => 'è¥�',
  'ïª·' => 'è¦†',
  'ïª¸' => 'è¦–',
  'ïª¹' => 'èª¿',
  'ïªº' => 'è«¸',
  'ïª»' => 'è«‹',
  'ïª¼' => 'è¬�',
  'ïª½' => 'è«¾',
  'ïª¾' => 'è«­',
  'ïª¿' => 'è¬¹',
  'ï«€' => 'è®Š',
  'ï«�' => 'è´ˆ',
  'ï«‚' => 'è¼¸',
  'ï«ƒ' => 'é�²',
  'ï«„' => 'é†™',
  'ï«…' => 'é‰¶',
  'ï«†' => 'é™¼',
  'ï«‡' => 'é›£',
  'ï«ˆ' => 'é�–',
  'ï«‰' => 'éŸ›',
  'ï«Š' => 'éŸ¿',
  'ï«‹' => 'é ‹',
  'ï«Œ' => 'é »',
  'ï«�' => 'é¬’',
  'ï«Ž' => 'é¾œ',
  'ï«�' => 'ð¢¡Š',
  'ï«�' => 'ð¢¡„',
  'ï«‘' => 'ð£�•',
  'ï«’' => 'ã®�',
  'ï«“' => 'ä€˜',
  'ï«”' => 'ä€¹',
  'ï«•' => 'ð¥‰‰',
  'ï«–' => 'ð¥³�',
  'ï«—' => 'ð§»“',
  'ï«˜' => 'é½ƒ',
  'ï«™' => 'é¾Ž',
  'ï¬€' => 'ff',
  'ï¬�' => 'fi',
  'ï¬‚' => 'fl',
  'ï¬ƒ' => 'ffi',
  'ï¬„' => 'ffl',
  'ï¬…' => 'Å¿t',
  'ï¬†' => 'st',
  'ï¬“' => 'Õ´Õ¶',
  'ï¬”' => 'Õ´Õ¥',
  'ï¬•' => 'Õ´Õ«',
  'ï¬–' => 'Õ¾Õ¶',
  'ï¬—' => 'Õ´Õ­',
  'ï¬ ' => '×¢',
  'ï¬¡' => '×�',
  'ï¬¢' => '×“',
  'ï¬£' => '×”',
  'ï¬¤' => '×›',
  'ï¬¥' => '×œ',
  'ï¬¦' => '×�',
  'ï¬§' => '×¨',
  'ï¬¨' => '×ª',
  'ï¬©' => '+',
  'ï­�' => '×�×œ',
  'ï¹‰' => 'â€¾',
  'ï¹Š' => 'â€¾',
  'ï¹‹' => 'â€¾',
  'ï¹Œ' => 'â€¾',
  'ï¹�' => '_',
  'ï¹Ž' => '_',
  'ï¹�' => '_',
  'ï¹�' => ',',
  'ï¹‘' => 'ã€�',
  'ï¹’' => '.',
  'ï¹”' => ';',
  'ï¹•' => ':',
  'ï¹–' => '?',
  'ï¹—' => '!',
  'ï¹˜' => 'â€”',
  'ï¹™' => '(',
  'ï¹š' => ')',
  'ï¹›' => '{',
  'ï¹œ' => '}',
  'ï¹�' => 'ã€”',
  'ï¹ž' => 'ã€•',
  'ï¹Ÿ' => '#',
  'ï¹ ' => '&',
  'ï¹¡' => '*',
  'ï¹¢' => '+',
  'ï¹£' => '-',
  'ï¹¤' => '<',
  'ï¹¥' => '>',
  'ï¹¦' => '=',
  'ï¹¨' => '\\',
  'ï¹©' => '$',
  'ï¹ª' => '%',
  'ï¹«' => '@',
  'ï¼�' => '!',
  'ï¼‚' => '"',
  'ï¼ƒ' => '#',
  'ï¼„' => '$',
  'ï¼…' => '%',
  'ï¼†' => '&',
  'ï¼‡' => '\'',
  'ï¼ˆ' => '(',
  'ï¼‰' => ')',
  'ï¼Š' => '*',
  'ï¼‹' => '+',
  'ï¼Œ' => ',',
  'ï¼�' => '-',
  'ï¼Ž' => '.',
  'ï¼�' => '/',
  'ï¼�' => '0',
  'ï¼‘' => '1',
  'ï¼’' => '2',
  'ï¼“' => '3',
  'ï¼”' => '4',
  'ï¼•' => '5',
  'ï¼–' => '6',
  'ï¼—' => '7',
  'ï¼˜' => '8',
  'ï¼™' => '9',
  'ï¼š' => ':',
  'ï¼›' => ';',
  'ï¼œ' => '<',
  'ï¼�' => '=',
  'ï¼ž' => '>',
  'ï¼Ÿ' => '?',
  'ï¼ ' => '@',
  'ï¼¡' => 'A',
  'ï¼¢' => 'B',
  'ï¼£' => 'C',
  'ï¼¤' => 'D',
  'ï¼¥' => 'E',
  'ï¼¦' => 'F',
  'ï¼§' => 'G',
  'ï¼¨' => 'H',
  'ï¼©' => 'I',
  'ï¼ª' => 'J',
  'ï¼«' => 'K',
  'ï¼¬' => 'L',
  'ï¼­' => 'M',
  'ï¼®' => 'N',
  'ï¼¯' => 'O',
  'ï¼°' => 'P',
  'ï¼±' => 'Q',
  'ï¼²' => 'R',
  'ï¼³' => 'S',
  'ï¼´' => 'T',
  'ï¼µ' => 'U',
  'ï¼¶' => 'V',
  'ï¼·' => 'W',
  'ï¼¸' => 'X',
  'ï¼¹' => 'Y',
  'ï¼º' => 'Z',
  'ï¼»' => '[',
  'ï¼¼' => '\\',
  'ï¼½' => ']',
  'ï¼¾' => '^',
  'ï¼¿' => '_',
  'ï½€' => '`',
  'ï½�' => 'a',
  'ï½‚' => 'b',
  'ï½ƒ' => 'c',
  'ï½„' => 'd',
  'ï½…' => 'e',
  'ï½†' => 'f',
  'ï½‡' => 'g',
  'ï½ˆ' => 'h',
  'ï½‰' => 'i',
  'ï½Š' => 'j',
  'ï½‹' => 'k',
  'ï½Œ' => 'l',
  'ï½�' => 'm',
  'ï½Ž' => 'n',
  'ï½�' => 'o',
  'ï½�' => 'p',
  'ï½‘' => 'q',
  'ï½’' => 'r',
  'ï½“' => 's',
  'ï½”' => 't',
  'ï½•' => 'u',
  'ï½–' => 'v',
  'ï½—' => 'w',
  'ï½˜' => 'x',
  'ï½™' => 'y',
  'ï½š' => 'z',
  'ï½›' => '{',
  'ï½œ' => '|',
  'ï½�' => '}',
  'ï½ž' => '~',
  'ï½Ÿ' => 'â¦…',
  'ï½ ' => 'â¦†',
  'ï½¡' => 'ã€‚',
  'ï½¢' => 'ã€Œ',
  'ï½£' => 'ã€�',
  'ï½¤' => 'ã€�',
  'ï½¥' => 'ãƒ»',
  'ï½¦' => 'ãƒ²',
  'ï½§' => 'ã‚¡',
  'ï½¨' => 'ã‚£',
  'ï½©' => 'ã‚¥',
  'ï½ª' => 'ã‚§',
  'ï½«' => 'ã‚©',
  'ï½¬' => 'ãƒ£',
  'ï½­' => 'ãƒ¥',
  'ï½®' => 'ãƒ§',
  'ï½¯' => 'ãƒƒ',
  'ï½°' => 'ãƒ¼',
  'ï½±' => 'ã‚¢',
  'ï½²' => 'ã‚¤',
  'ï½³' => 'ã‚¦',
  'ï½´' => 'ã‚¨',
  'ï½µ' => 'ã‚ª',
  'ï½¶' => 'ã‚«',
  'ï½·' => 'ã‚­',
  'ï½¸' => 'ã‚¯',
  'ï½¹' => 'ã‚±',
  'ï½º' => 'ã‚³',
  'ï½»' => 'ã‚µ',
  'ï½¼' => 'ã‚·',
  'ï½½' => 'ã‚¹',
  'ï½¾' => 'ã‚»',
  'ï½¿' => 'ã‚½',
  'ï¾€' => 'ã‚¿',
  'ï¾�' => 'ãƒ�',
  'ï¾‚' => 'ãƒ„',
  'ï¾ƒ' => 'ãƒ†',
  'ï¾„' => 'ãƒˆ',
  'ï¾…' => 'ãƒŠ',
  'ï¾†' => 'ãƒ‹',
  'ï¾‡' => 'ãƒŒ',
  'ï¾ˆ' => 'ãƒ�',
  'ï¾‰' => 'ãƒŽ',
  'ï¾Š' => 'ãƒ�',
  'ï¾‹' => 'ãƒ’',
  'ï¾Œ' => 'ãƒ•',
  'ï¾�' => 'ãƒ˜',
  'ï¾Ž' => 'ãƒ›',
  'ï¾�' => 'ãƒž',
  'ï¾�' => 'ãƒŸ',
  'ï¾‘' => 'ãƒ ',
  'ï¾’' => 'ãƒ¡',
  'ï¾“' => 'ãƒ¢',
  'ï¾”' => 'ãƒ¤',
  'ï¾•' => 'ãƒ¦',
  'ï¾–' => 'ãƒ¨',
  'ï¾—' => 'ãƒ©',
  'ï¾˜' => 'ãƒª',
  'ï¾™' => 'ãƒ«',
  'ï¾š' => 'ãƒ¬',
  'ï¾›' => 'ãƒ­',
  'ï¾œ' => 'ãƒ¯',
  'ï¾�' => 'ãƒ³',
  'ï¾ž' => 'ã‚™',
  'ï¾Ÿ' => 'ã‚š',
  'ï¾ ' => 'ã…¤',
  'ï¾¡' => 'ã„±',
  'ï¾¢' => 'ã„²',
  'ï¾£' => 'ã„³',
  'ï¾¤' => 'ã„´',
  'ï¾¥' => 'ã„µ',
  'ï¾¦' => 'ã„¶',
  'ï¾§' => 'ã„·',
  'ï¾¨' => 'ã„¸',
  'ï¾©' => 'ã„¹',
  'ï¾ª' => 'ã„º',
  'ï¾«' => 'ã„»',
  'ï¾¬' => 'ã„¼',
  'ï¾­' => 'ã„½',
  'ï¾®' => 'ã„¾',
  'ï¾¯' => 'ã„¿',
  'ï¾°' => 'ã…€',
  'ï¾±' => 'ã…�',
  'ï¾²' => 'ã…‚',
  'ï¾³' => 'ã…ƒ',
  'ï¾´' => 'ã…„',
  'ï¾µ' => 'ã……',
  'ï¾¶' => 'ã…†',
  'ï¾·' => 'ã…‡',
  'ï¾¸' => 'ã…ˆ',
  'ï¾¹' => 'ã…‰',
  'ï¾º' => 'ã…Š',
  'ï¾»' => 'ã…‹',
  'ï¾¼' => 'ã…Œ',
  'ï¾½' => 'ã…�',
  'ï¾¾' => 'ã…Ž',
  'ï¿‚' => 'ã…�',
  'ï¿ƒ' => 'ã…�',
  'ï¿„' => 'ã…‘',
  'ï¿…' => 'ã…’',
  'ï¿†' => 'ã…“',
  'ï¿‡' => 'ã…”',
  'ï¿Š' => 'ã…•',
  'ï¿‹' => 'ã…–',
  'ï¿Œ' => 'ã…—',
  'ï¿�' => 'ã…˜',
  'ï¿Ž' => 'ã…™',
  'ï¿�' => 'ã…š',
  'ï¿’' => 'ã…›',
  'ï¿“' => 'ã…œ',
  'ï¿”' => 'ã…�',
  'ï¿•' => 'ã…ž',
  'ï¿–' => 'ã…Ÿ',
  'ï¿—' => 'ã… ',
  'ï¿š' => 'ã…¡',
  'ï¿›' => 'ã…¢',
  'ï¿œ' => 'ã…£',
  'ï¿ ' => 'Â¢',
  'ï¿¡' => 'Â£',
  'ï¿¢' => 'Â¬',
  'ï¿£' => 'Â¯',
  'ï¿¤' => 'Â¦',
  'ï¿¥' => 'Â¥',
  'ï¿¦' => 'â‚©',
  'ï¿¨' => 'â”‚',
  'ï¿©' => 'â†�',
  'ï¿ª' => 'â†‘',
  'ï¿«' => 'â†’',
  'ï¿¬' => 'â†“',
  'ï¿­' => 'â– ',
  'ï¿®' => 'â—‹',
  'ð��€' => 'A',
  'ð���' => 'B',
  'ð��‚' => 'C',
  'ð��ƒ' => 'D',
  'ð��„' => 'E',
  'ð��…' => 'F',
  'ð��†' => 'G',
  'ð��‡' => 'H',
  'ð��ˆ' => 'I',
  'ð��‰' => 'J',
  'ð��Š' => 'K',
  'ð��‹' => 'L',
  'ð��Œ' => 'M',
  'ð���' => 'N',
  'ð��Ž' => 'O',
  'ð���' => 'P',
  'ð���' => 'Q',
  'ð��‘' => 'R',
  'ð��’' => 'S',
  'ð��“' => 'T',
  'ð��”' => 'U',
  'ð��•' => 'V',
  'ð��–' => 'W',
  'ð��—' => 'X',
  'ð��˜' => 'Y',
  'ð��™' => 'Z',
  'ð��š' => 'a',
  'ð��›' => 'b',
  'ð��œ' => 'c',
  'ð���' => 'd',
  'ð��ž' => 'e',
  'ð��Ÿ' => 'f',
  'ð�� ' => 'g',
  'ð��¡' => 'h',
  'ð��¢' => 'i',
  'ð��£' => 'j',
  'ð��¤' => 'k',
  'ð��¥' => 'l',
  'ð��¦' => 'm',
  'ð��§' => 'n',
  'ð��¨' => 'o',
  'ð��©' => 'p',
  'ð��ª' => 'q',
  'ð��«' => 'r',
  'ð��¬' => 's',
  'ð��­' => 't',
  'ð��®' => 'u',
  'ð��¯' => 'v',
  'ð��°' => 'w',
  'ð��±' => 'x',
  'ð��²' => 'y',
  'ð��³' => 'z',
  'ð��´' => 'A',
  'ð��µ' => 'B',
  'ð��¶' => 'C',
  'ð��·' => 'D',
  'ð��¸' => 'E',
  'ð��¹' => 'F',
  'ð��º' => 'G',
  'ð��»' => 'H',
  'ð��¼' => 'I',
  'ð��½' => 'J',
  'ð��¾' => 'K',
  'ð��¿' => 'L',
  'ð�‘€' => 'M',
  'ð�‘�' => 'N',
  'ð�‘‚' => 'O',
  'ð�‘ƒ' => 'P',
  'ð�‘„' => 'Q',
  'ð�‘…' => 'R',
  'ð�‘†' => 'S',
  'ð�‘‡' => 'T',
  'ð�‘ˆ' => 'U',
  'ð�‘‰' => 'V',
  'ð�‘Š' => 'W',
  'ð�‘‹' => 'X',
  'ð�‘Œ' => 'Y',
  'ð�‘�' => 'Z',
  'ð�‘Ž' => 'a',
  'ð�‘�' => 'b',
  'ð�‘�' => 'c',
  'ð�‘‘' => 'd',
  'ð�‘’' => 'e',
  'ð�‘“' => 'f',
  'ð�‘”' => 'g',
  'ð�‘–' => 'i',
  'ð�‘—' => 'j',
  'ð�‘˜' => 'k',
  'ð�‘™' => 'l',
  'ð�‘š' => 'm',
  'ð�‘›' => 'n',
  'ð�‘œ' => 'o',
  'ð�‘�' => 'p',
  'ð�‘ž' => 'q',
  'ð�‘Ÿ' => 'r',
  'ð�‘ ' => 's',
  'ð�‘¡' => 't',
  'ð�‘¢' => 'u',
  'ð�‘£' => 'v',
  'ð�‘¤' => 'w',
  'ð�‘¥' => 'x',
  'ð�‘¦' => 'y',
  'ð�‘§' => 'z',
  'ð�‘¨' => 'A',
  'ð�‘©' => 'B',
  'ð�‘ª' => 'C',
  'ð�‘«' => 'D',
  'ð�‘¬' => 'E',
  'ð�‘­' => 'F',
  'ð�‘®' => 'G',
  'ð�‘¯' => 'H',
  'ð�‘°' => 'I',
  'ð�‘±' => 'J',
  'ð�‘²' => 'K',
  'ð�‘³' => 'L',
  'ð�‘´' => 'M',
  'ð�‘µ' => 'N',
  'ð�‘¶' => 'O',
  'ð�‘·' => 'P',
  'ð�‘¸' => 'Q',
  'ð�‘¹' => 'R',
  'ð�‘º' => 'S',
  'ð�‘»' => 'T',
  'ð�‘¼' => 'U',
  'ð�‘½' => 'V',
  'ð�‘¾' => 'W',
  'ð�‘¿' => 'X',
  'ð�’€' => 'Y',
  'ð�’�' => 'Z',
  'ð�’‚' => 'a',
  'ð�’ƒ' => 'b',
  'ð�’„' => 'c',
  'ð�’…' => 'd',
  'ð�’†' => 'e',
  'ð�’‡' => 'f',
  'ð�’ˆ' => 'g',
  'ð�’‰' => 'h',
  'ð�’Š' => 'i',
  'ð�’‹' => 'j',
  'ð�’Œ' => 'k',
  'ð�’�' => 'l',
  'ð�’Ž' => 'm',
  'ð�’�' => 'n',
  'ð�’�' => 'o',
  'ð�’‘' => 'p',
  'ð�’’' => 'q',
  'ð�’“' => 'r',
  'ð�’”' => 's',
  'ð�’•' => 't',
  'ð�’–' => 'u',
  'ð�’—' => 'v',
  'ð�’˜' => 'w',
  'ð�’™' => 'x',
  'ð�’š' => 'y',
  'ð�’›' => 'z',
  'ð�’œ' => 'A',
  'ð�’ž' => 'C',
  'ð�’Ÿ' => 'D',
  'ð�’¢' => 'G',
  'ð�’¥' => 'J',
  'ð�’¦' => 'K',
  'ð�’©' => 'N',
  'ð�’ª' => 'O',
  'ð�’«' => 'P',
  'ð�’¬' => 'Q',
  'ð�’®' => 'S',
  'ð�’¯' => 'T',
  'ð�’°' => 'U',
  'ð�’±' => 'V',
  'ð�’²' => 'W',
  'ð�’³' => 'X',
  'ð�’´' => 'Y',
  'ð�’µ' => 'Z',
  'ð�’¶' => 'a',
  'ð�’·' => 'b',
  'ð�’¸' => 'c',
  'ð�’¹' => 'd',
  'ð�’»' => 'f',
  'ð�’½' => 'h',
  'ð�’¾' => 'i',
  'ð�’¿' => 'j',
  'ð�“€' => 'k',
  'ð�“�' => 'l',
  'ð�“‚' => 'm',
  'ð�“ƒ' => 'n',
  'ð�“…' => 'p',
  'ð�“†' => 'q',
  'ð�“‡' => 'r',
  'ð�“ˆ' => 's',
  'ð�“‰' => 't',
  'ð�“Š' => 'u',
  'ð�“‹' => 'v',
  'ð�“Œ' => 'w',
  'ð�“�' => 'x',
  'ð�“Ž' => 'y',
  'ð�“�' => 'z',
  'ð�“�' => 'A',
  'ð�“‘' => 'B',
  'ð�“’' => 'C',
  'ð�““' => 'D',
  'ð�“”' => 'E',
  'ð�“•' => 'F',
  'ð�“–' => 'G',
  'ð�“—' => 'H',
  'ð�“˜' => 'I',
  'ð�“™' => 'J',
  'ð�“š' => 'K',
  'ð�“›' => 'L',
  'ð�“œ' => 'M',
  'ð�“�' => 'N',
  'ð�“ž' => 'O',
  'ð�“Ÿ' => 'P',
  'ð�“ ' => 'Q',
  'ð�“¡' => 'R',
  'ð�“¢' => 'S',
  'ð�“£' => 'T',
  'ð�“¤' => 'U',
  'ð�“¥' => 'V',
  'ð�“¦' => 'W',
  'ð�“§' => 'X',
  'ð�“¨' => 'Y',
  'ð�“©' => 'Z',
  'ð�“ª' => 'a',
  'ð�“«' => 'b',
  'ð�“¬' => 'c',
  'ð�“­' => 'd',
  'ð�“®' => 'e',
  'ð�“¯' => 'f',
  'ð�“°' => 'g',
  'ð�“±' => 'h',
  'ð�“²' => 'i',
  'ð�“³' => 'j',
  'ð�“´' => 'k',
  'ð�“µ' => 'l',
  'ð�“¶' => 'm',
  'ð�“·' => 'n',
  'ð�“¸' => 'o',
  'ð�“¹' => 'p',
  'ð�“º' => 'q',
  'ð�“»' => 'r',
  'ð�“¼' => 's',
  'ð�“½' => 't',
  'ð�“¾' => 'u',
  'ð�“¿' => 'v',
  'ð�”€' => 'w',
  'ð�”�' => 'x',
  'ð�”‚' => 'y',
  'ð�”ƒ' => 'z',
  'ð�”„' => 'A',
  'ð�”…' => 'B',
  'ð�”‡' => 'D',
  'ð�”ˆ' => 'E',
  'ð�”‰' => 'F',
  'ð�”Š' => 'G',
  'ð�”�' => 'J',
  'ð�”Ž' => 'K',
  'ð�”�' => 'L',
  'ð�”�' => 'M',
  'ð�”‘' => 'N',
  'ð�”’' => 'O',
  'ð�”“' => 'P',
  'ð�””' => 'Q',
  'ð�”–' => 'S',
  'ð�”—' => 'T',
  'ð�”˜' => 'U',
  'ð�”™' => 'V',
  'ð�”š' => 'W',
  'ð�”›' => 'X',
  'ð�”œ' => 'Y',
  'ð�”ž' => 'a',
  'ð�”Ÿ' => 'b',
  'ð�” ' => 'c',
  'ð�”¡' => 'd',
  'ð�”¢' => 'e',
  'ð�”£' => 'f',
  'ð�”¤' => 'g',
  'ð�”¥' => 'h',
  'ð�”¦' => 'i',
  'ð�”§' => 'j',
  'ð�”¨' => 'k',
  'ð�”©' => 'l',
  'ð�”ª' => 'm',
  'ð�”«' => 'n',
  'ð�”¬' => 'o',
  'ð�”­' => 'p',
  'ð�”®' => 'q',
  'ð�”¯' => 'r',
  'ð�”°' => 's',
  'ð�”±' => 't',
  'ð�”²' => 'u',
  'ð�”³' => 'v',
  'ð�”´' => 'w',
  'ð�”µ' => 'x',
  'ð�”¶' => 'y',
  'ð�”·' => 'z',
  'ð�”¸' => 'A',
  'ð�”¹' => 'B',
  'ð�”»' => 'D',
  'ð�”¼' => 'E',
  'ð�”½' => 'F',
  'ð�”¾' => 'G',
  'ð�•€' => 'I',
  'ð�•�' => 'J',
  'ð�•‚' => 'K',
  'ð�•ƒ' => 'L',
  'ð�•„' => 'M',
  'ð�•†' => 'O',
  'ð�•Š' => 'S',
  'ð�•‹' => 'T',
  'ð�•Œ' => 'U',
  'ð�•�' => 'V',
  'ð�•Ž' => 'W',
  'ð�•�' => 'X',
  'ð�•�' => 'Y',
  'ð�•’' => 'a',
  'ð�•“' => 'b',
  'ð�•”' => 'c',
  'ð�••' => 'd',
  'ð�•–' => 'e',
  'ð�•—' => 'f',
  'ð�•˜' => 'g',
  'ð�•™' => 'h',
  'ð�•š' => 'i',
  'ð�•›' => 'j',
  'ð�•œ' => 'k',
  'ð�•�' => 'l',
  'ð�•ž' => 'm',
  'ð�•Ÿ' => 'n',
  'ð�• ' => 'o',
  'ð�•¡' => 'p',
  'ð�•¢' => 'q',
  'ð�•£' => 'r',
  'ð�•¤' => 's',
  'ð�•¥' => 't',
  'ð�•¦' => 'u',
  'ð�•§' => 'v',
  'ð�•¨' => 'w',
  'ð�•©' => 'x',
  'ð�•ª' => 'y',
  'ð�•«' => 'z',
  'ð�•¬' => 'A',
  'ð�•­' => 'B',
  'ð�•®' => 'C',
  'ð�•¯' => 'D',
  'ð�•°' => 'E',
  'ð�•±' => 'F',
  'ð�•²' => 'G',
  'ð�•³' => 'H',
  'ð�•´' => 'I',
  'ð�•µ' => 'J',
  'ð�•¶' => 'K',
  'ð�•·' => 'L',
  'ð�•¸' => 'M',
  'ð�•¹' => 'N',
  'ð�•º' => 'O',
  'ð�•»' => 'P',
  'ð�•¼' => 'Q',
  'ð�•½' => 'R',
  'ð�•¾' => 'S',
  'ð�•¿' => 'T',
  'ð�–€' => 'U',
  'ð�–�' => 'V',
  'ð�–‚' => 'W',
  'ð�–ƒ' => 'X',
  'ð�–„' => 'Y',
  'ð�–…' => 'Z',
  'ð�–†' => 'a',
  'ð�–‡' => 'b',
  'ð�–ˆ' => 'c',
  'ð�–‰' => 'd',
  'ð�–Š' => 'e',
  'ð�–‹' => 'f',
  'ð�–Œ' => 'g',
  'ð�–�' => 'h',
  'ð�–Ž' => 'i',
  'ð�–�' => 'j',
  'ð�–�' => 'k',
  'ð�–‘' => 'l',
  'ð�–’' => 'm',
  'ð�–“' => 'n',
  'ð�–”' => 'o',
  'ð�–•' => 'p',
  'ð�––' => 'q',
  'ð�–—' => 'r',
  'ð�–˜' => 's',
  'ð�–™' => 't',
  'ð�–š' => 'u',
  'ð�–›' => 'v',
  'ð�–œ' => 'w',
  'ð�–�' => 'x',
  'ð�–ž' => 'y',
  'ð�–Ÿ' => 'z',
  'ð�– ' => 'A',
  'ð�–¡' => 'B',
  'ð�–¢' => 'C',
  'ð�–£' => 'D',
  'ð�–¤' => 'E',
  'ð�–¥' => 'F',
  'ð�–¦' => 'G',
  'ð�–§' => 'H',
  'ð�–¨' => 'I',
  'ð�–©' => 'J',
  'ð�–ª' => 'K',
  'ð�–«' => 'L',
  'ð�–¬' => 'M',
  'ð�–­' => 'N',
  'ð�–®' => 'O',
  'ð�–¯' => 'P',
  'ð�–°' => 'Q',
  'ð�–±' => 'R',
  'ð�–²' => 'S',
  'ð�–³' => 'T',
  'ð�–´' => 'U',
  'ð�–µ' => 'V',
  'ð�–¶' => 'W',
  'ð�–·' => 'X',
  'ð�–¸' => 'Y',
  'ð�–¹' => 'Z',
  'ð�–º' => 'a',
  'ð�–»' => 'b',
  'ð�–¼' => 'c',
  'ð�–½' => 'd',
  'ð�–¾' => 'e',
  'ð�–¿' => 'f',
  'ð�—€' => 'g',
  'ð�—�' => 'h',
  'ð�—‚' => 'i',
  'ð�—ƒ' => 'j',
  'ð�—„' => 'k',
  'ð�—…' => 'l',
  'ð�—†' => 'm',
  'ð�—‡' => 'n',
  'ð�—ˆ' => 'o',
  'ð�—‰' => 'p',
  'ð�—Š' => 'q',
  'ð�—‹' => 'r',
  'ð�—Œ' => 's',
  'ð�—�' => 't',
  'ð�—Ž' => 'u',
  'ð�—�' => 'v',
  'ð�—�' => 'w',
  'ð�—‘' => 'x',
  'ð�—’' => 'y',
  'ð�—“' => 'z',
  'ð�—”' => 'A',
  'ð�—•' => 'B',
  'ð�—–' => 'C',
  'ð�——' => 'D',
  'ð�—˜' => 'E',
  'ð�—™' => 'F',
  'ð�—š' => 'G',
  'ð�—›' => 'H',
  'ð�—œ' => 'I',
  'ð�—�' => 'J',
  'ð�—ž' => 'K',
  'ð�—Ÿ' => 'L',
  'ð�— ' => 'M',
  'ð�—¡' => 'N',
  'ð�—¢' => 'O',
  'ð�—£' => 'P',
  'ð�—¤' => 'Q',
  'ð�—¥' => 'R',
  'ð�—¦' => 'S',
  'ð�—§' => 'T',
  'ð�—¨' => 'U',
  'ð�—©' => 'V',
  'ð�—ª' => 'W',
  'ð�—«' => 'X',
  'ð�—¬' => 'Y',
  'ð�—­' => 'Z',
  'ð�—®' => 'a',
  'ð�—¯' => 'b',
  'ð�—°' => 'c',
  'ð�—±' => 'd',
  'ð�—²' => 'e',
  'ð�—³' => 'f',
  'ð�—´' => 'g',
  'ð�—µ' => 'h',
  'ð�—¶' => 'i',
  'ð�—·' => 'j',
  'ð�—¸' => 'k',
  'ð�—¹' => 'l',
  'ð�—º' => 'm',
  'ð�—»' => 'n',
  'ð�—¼' => 'o',
  'ð�—½' => 'p',
  'ð�—¾' => 'q',
  'ð�—¿' => 'r',
  'ð�˜€' => 's',
  'ð�˜�' => 't',
  'ð�˜‚' => 'u',
  'ð�˜ƒ' => 'v',
  'ð�˜„' => 'w',
  'ð�˜…' => 'x',
  'ð�˜†' => 'y',
  'ð�˜‡' => 'z',
  'ð�˜ˆ' => 'A',
  'ð�˜‰' => 'B',
  'ð�˜Š' => 'C',
  'ð�˜‹' => 'D',
  'ð�˜Œ' => 'E',
  'ð�˜�' => 'F',
  'ð�˜Ž' => 'G',
  'ð�˜�' => 'H',
  'ð�˜�' => 'I',
  'ð�˜‘' => 'J',
  'ð�˜’' => 'K',
  'ð�˜“' => 'L',
  'ð�˜”' => 'M',
  'ð�˜•' => 'N',
  'ð�˜–' => 'O',
  'ð�˜—' => 'P',
  'ð�˜˜' => 'Q',
  'ð�˜™' => 'R',
  'ð�˜š' => 'S',
  'ð�˜›' => 'T',
  'ð�˜œ' => 'U',
  'ð�˜�' => 'V',
  'ð�˜ž' => 'W',
  'ð�˜Ÿ' => 'X',
  'ð�˜ ' => 'Y',
  'ð�˜¡' => 'Z',
  'ð�˜¢' => 'a',
  'ð�˜£' => 'b',
  'ð�˜¤' => 'c',
  'ð�˜¥' => 'd',
  'ð�˜¦' => 'e',
  'ð�˜§' => 'f',
  'ð�˜¨' => 'g',
  'ð�˜©' => 'h',
  'ð�˜ª' => 'i',
  'ð�˜«' => 'j',
  'ð�˜¬' => 'k',
  'ð�˜­' => 'l',
  'ð�˜®' => 'm',
  'ð�˜¯' => 'n',
  'ð�˜°' => 'o',
  'ð�˜±' => 'p',
  'ð�˜²' => 'q',
  'ð�˜³' => 'r',
  'ð�˜´' => 's',
  'ð�˜µ' => 't',
  'ð�˜¶' => 'u',
  'ð�˜·' => 'v',
  'ð�˜¸' => 'w',
  'ð�˜¹' => 'x',
  'ð�˜º' => 'y',
  'ð�˜»' => 'z',
  'ð�˜¼' => 'A',
  'ð�˜½' => 'B',
  'ð�˜¾' => 'C',
  'ð�˜¿' => 'D',
  'ð�™€' => 'E',
  'ð�™�' => 'F',
  'ð�™‚' => 'G',
  'ð�™ƒ' => 'H',
  'ð�™„' => 'I',
  'ð�™…' => 'J',
  'ð�™†' => 'K',
  'ð�™‡' => 'L',
  'ð�™ˆ' => 'M',
  'ð�™‰' => 'N',
  'ð�™Š' => 'O',
  'ð�™‹' => 'P',
  'ð�™Œ' => 'Q',
  'ð�™�' => 'R',
  'ð�™Ž' => 'S',
  'ð�™�' => 'T',
  'ð�™�' => 'U',
  'ð�™‘' => 'V',
  'ð�™’' => 'W',
  'ð�™“' => 'X',
  'ð�™”' => 'Y',
  'ð�™•' => 'Z',
  'ð�™–' => 'a',
  'ð�™—' => 'b',
  'ð�™˜' => 'c',
  'ð�™™' => 'd',
  'ð�™š' => 'e',
  'ð�™›' => 'f',
  'ð�™œ' => 'g',
  'ð�™�' => 'h',
  'ð�™ž' => 'i',
  'ð�™Ÿ' => 'j',
  'ð�™ ' => 'k',
  'ð�™¡' => 'l',
  'ð�™¢' => 'm',
  'ð�™£' => 'n',
  'ð�™¤' => 'o',
  'ð�™¥' => 'p',
  'ð�™¦' => 'q',
  'ð�™§' => 'r',
  'ð�™¨' => 's',
  'ð�™©' => 't',
  'ð�™ª' => 'u',
  'ð�™«' => 'v',
  'ð�™¬' => 'w',
  'ð�™­' => 'x',
  'ð�™®' => 'y',
  'ð�™¯' => 'z',
  'ð�™°' => 'A',
  'ð�™±' => 'B',
  'ð�™²' => 'C',
  'ð�™³' => 'D',
  'ð�™´' => 'E',
  'ð�™µ' => 'F',
  'ð�™¶' => 'G',
  'ð�™·' => 'H',
  'ð�™¸' => 'I',
  'ð�™¹' => 'J',
  'ð�™º' => 'K',
  'ð�™»' => 'L',
  'ð�™¼' => 'M',
  'ð�™½' => 'N',
  'ð�™¾' => 'O',
  'ð�™¿' => 'P',
  'ð�š€' => 'Q',
  'ð�š�' => 'R',
  'ð�š‚' => 'S',
  'ð�šƒ' => 'T',
  'ð�š„' => 'U',
  'ð�š…' => 'V',
  'ð�š†' => 'W',
  'ð�š‡' => 'X',
  'ð�šˆ' => 'Y',
  'ð�š‰' => 'Z',
  'ð�šŠ' => 'a',
  'ð�š‹' => 'b',
  'ð�šŒ' => 'c',
  'ð�š�' => 'd',
  'ð�šŽ' => 'e',
  'ð�š�' => 'f',
  'ð�š�' => 'g',
  'ð�š‘' => 'h',
  'ð�š’' => 'i',
  'ð�š“' => 'j',
  'ð�š”' => 'k',
  'ð�š•' => 'l',
  'ð�š–' => 'm',
  'ð�š—' => 'n',
  'ð�š˜' => 'o',
  'ð�š™' => 'p',
  'ð�šš' => 'q',
  'ð�š›' => 'r',
  'ð�šœ' => 's',
  'ð�š�' => 't',
  'ð�šž' => 'u',
  'ð�šŸ' => 'v',
  'ð�š ' => 'w',
  'ð�š¡' => 'x',
  'ð�š¢' => 'y',
  'ð�š£' => 'z',
  'ð�š¤' => 'Ä±',
  'ð�š¥' => 'È·',
  'ð�š¨' => 'Î‘',
  'ð�š©' => 'Î’',
  'ð�šª' => 'Î“',
  'ð�š«' => 'Î”',
  'ð�š¬' => 'Î•',
  'ð�š­' => 'Î–',
  'ð�š®' => 'Î—',
  'ð�š¯' => 'Î˜',
  'ð�š°' => 'Î™',
  'ð�š±' => 'Îš',
  'ð�š²' => 'Î›',
  'ð�š³' => 'Îœ',
  'ð�š´' => 'Î�',
  'ð�šµ' => 'Îž',
  'ð�š¶' => 'ÎŸ',
  'ð�š·' => 'Î ',
  'ð�š¸' => 'Î¡',
  'ð�š¹' => 'Ï´',
  'ð�šº' => 'Î£',
  'ð�š»' => 'Î¤',
  'ð�š¼' => 'Î¥',
  'ð�š½' => 'Î¦',
  'ð�š¾' => 'Î§',
  'ð�š¿' => 'Î¨',
  'ð�›€' => 'Î©',
  'ð�›�' => 'âˆ‡',
  'ð�›‚' => 'Î±',
  'ð�›ƒ' => 'Î²',
  'ð�›„' => 'Î³',
  'ð�›…' => 'Î´',
  'ð�›†' => 'Îµ',
  'ð�›‡' => 'Î¶',
  'ð�›ˆ' => 'Î·',
  'ð�›‰' => 'Î¸',
  'ð�›Š' => 'Î¹',
  'ð�›‹' => 'Îº',
  'ð�›Œ' => 'Î»',
  'ð�›�' => 'Î¼',
  'ð�›Ž' => 'Î½',
  'ð�›�' => 'Î¾',
  'ð�›�' => 'Î¿',
  'ð�›‘' => 'Ï€',
  'ð�›’' => 'Ï�',
  'ð�›“' => 'Ï‚',
  'ð�›”' => 'Ïƒ',
  'ð�›•' => 'Ï„',
  'ð�›–' => 'Ï…',
  'ð�›—' => 'Ï†',
  'ð�›˜' => 'Ï‡',
  'ð�›™' => 'Ïˆ',
  'ð�›š' => 'Ï‰',
  'ð�››' => 'âˆ‚',
  'ð�›œ' => 'Ïµ',
  'ð�›�' => 'Ï‘',
  'ð�›ž' => 'Ï°',
  'ð�›Ÿ' => 'Ï•',
  'ð�› ' => 'Ï±',
  'ð�›¡' => 'Ï–',
  'ð�›¢' => 'Î‘',
  'ð�›£' => 'Î’',
  'ð�›¤' => 'Î“',
  'ð�›¥' => 'Î”',
  'ð�›¦' => 'Î•',
  'ð�›§' => 'Î–',
  'ð�›¨' => 'Î—',
  'ð�›©' => 'Î˜',
  'ð�›ª' => 'Î™',
  'ð�›«' => 'Îš',
  'ð�›¬' => 'Î›',
  'ð�›­' => 'Îœ',
  'ð�›®' => 'Î�',
  'ð�›¯' => 'Îž',
  'ð�›°' => 'ÎŸ',
  'ð�›±' => 'Î ',
  'ð�›²' => 'Î¡',
  'ð�›³' => 'Ï´',
  'ð�›´' => 'Î£',
  'ð�›µ' => 'Î¤',
  'ð�›¶' => 'Î¥',
  'ð�›·' => 'Î¦',
  'ð�›¸' => 'Î§',
  'ð�›¹' => 'Î¨',
  'ð�›º' => 'Î©',
  'ð�›»' => 'âˆ‡',
  'ð�›¼' => 'Î±',
  'ð�›½' => 'Î²',
  'ð�›¾' => 'Î³',
  'ð�›¿' => 'Î´',
  'ð�œ€' => 'Îµ',
  'ð�œ�' => 'Î¶',
  'ð�œ‚' => 'Î·',
  'ð�œƒ' => 'Î¸',
  'ð�œ„' => 'Î¹',
  'ð�œ…' => 'Îº',
  'ð�œ†' => 'Î»',
  'ð�œ‡' => 'Î¼',
  'ð�œˆ' => 'Î½',
  'ð�œ‰' => 'Î¾',
  'ð�œŠ' => 'Î¿',
  'ð�œ‹' => 'Ï€',
  'ð�œŒ' => 'Ï�',
  'ð�œ�' => 'Ï‚',
  'ð�œŽ' => 'Ïƒ',
  'ð�œ�' => 'Ï„',
  'ð�œ�' => 'Ï…',
  'ð�œ‘' => 'Ï†',
  'ð�œ’' => 'Ï‡',
  'ð�œ“' => 'Ïˆ',
  'ð�œ”' => 'Ï‰',
  'ð�œ•' => 'âˆ‚',
  'ð�œ–' => 'Ïµ',
  'ð�œ—' => 'Ï‘',
  'ð�œ˜' => 'Ï°',
  'ð�œ™' => 'Ï•',
  'ð�œš' => 'Ï±',
  'ð�œ›' => 'Ï–',
  'ð�œœ' => 'Î‘',
  'ð�œ�' => 'Î’',
  'ð�œž' => 'Î“',
  'ð�œŸ' => 'Î”',
  'ð�œ ' => 'Î•',
  'ð�œ¡' => 'Î–',
  'ð�œ¢' => 'Î—',
  'ð�œ£' => 'Î˜',
  'ð�œ¤' => 'Î™',
  'ð�œ¥' => 'Îš',
  'ð�œ¦' => 'Î›',
  'ð�œ§' => 'Îœ',
  'ð�œ¨' => 'Î�',
  'ð�œ©' => 'Îž',
  'ð�œª' => 'ÎŸ',
  'ð�œ«' => 'Î ',
  'ð�œ¬' => 'Î¡',
  'ð�œ­' => 'Ï´',
  'ð�œ®' => 'Î£',
  'ð�œ¯' => 'Î¤',
  'ð�œ°' => 'Î¥',
  'ð�œ±' => 'Î¦',
  'ð�œ²' => 'Î§',
  'ð�œ³' => 'Î¨',
  'ð�œ´' => 'Î©',
  'ð�œµ' => 'âˆ‡',
  'ð�œ¶' => 'Î±',
  'ð�œ·' => 'Î²',
  'ð�œ¸' => 'Î³',
  'ð�œ¹' => 'Î´',
  'ð�œº' => 'Îµ',
  'ð�œ»' => 'Î¶',
  'ð�œ¼' => 'Î·',
  'ð�œ½' => 'Î¸',
  'ð�œ¾' => 'Î¹',
  'ð�œ¿' => 'Îº',
  'ð��€' => 'Î»',
  'ð���' => 'Î¼',
  'ð��‚' => 'Î½',
  'ð��ƒ' => 'Î¾',
  'ð��„' => 'Î¿',
  'ð��…' => 'Ï€',
  'ð��†' => 'Ï�',
  'ð��‡' => 'Ï‚',
  'ð��ˆ' => 'Ïƒ',
  'ð��‰' => 'Ï„',
  'ð��Š' => 'Ï…',
  'ð��‹' => 'Ï†',
  'ð��Œ' => 'Ï‡',
  'ð���' => 'Ïˆ',
  'ð��Ž' => 'Ï‰',
  'ð���' => 'âˆ‚',
  'ð���' => 'Ïµ',
  'ð��‘' => 'Ï‘',
  'ð��’' => 'Ï°',
  'ð��“' => 'Ï•',
  'ð��”' => 'Ï±',
  'ð��•' => 'Ï–',
  'ð��–' => 'Î‘',
  'ð��—' => 'Î’',
  'ð��˜' => 'Î“',
  'ð��™' => 'Î”',
  'ð��š' => 'Î•',
  'ð��›' => 'Î–',
  'ð��œ' => 'Î—',
  'ð���' => 'Î˜',
  'ð��ž' => 'Î™',
  'ð��Ÿ' => 'Îš',
  'ð�� ' => 'Î›',
  'ð��¡' => 'Îœ',
  'ð��¢' => 'Î�',
  'ð��£' => 'Îž',
  'ð��¤' => 'ÎŸ',
  'ð��¥' => 'Î ',
  'ð��¦' => 'Î¡',
  'ð��§' => 'Ï´',
  'ð��¨' => 'Î£',
  'ð��©' => 'Î¤',
  'ð��ª' => 'Î¥',
  'ð��«' => 'Î¦',
  'ð��¬' => 'Î§',
  'ð��­' => 'Î¨',
  'ð��®' => 'Î©',
  'ð��¯' => 'âˆ‡',
  'ð��°' => 'Î±',
  'ð��±' => 'Î²',
  'ð��²' => 'Î³',
  'ð��³' => 'Î´',
  'ð��´' => 'Îµ',
  'ð��µ' => 'Î¶',
  'ð��¶' => 'Î·',
  'ð��·' => 'Î¸',
  'ð��¸' => 'Î¹',
  'ð��¹' => 'Îº',
  'ð��º' => 'Î»',
  'ð��»' => 'Î¼',
  'ð��¼' => 'Î½',
  'ð��½' => 'Î¾',
  'ð��¾' => 'Î¿',
  'ð��¿' => 'Ï€',
  'ð�ž€' => 'Ï�',
  'ð�ž�' => 'Ï‚',
  'ð�ž‚' => 'Ïƒ',
  'ð�žƒ' => 'Ï„',
  'ð�ž„' => 'Ï…',
  'ð�ž…' => 'Ï†',
  'ð�ž†' => 'Ï‡',
  'ð�ž‡' => 'Ïˆ',
  'ð�žˆ' => 'Ï‰',
  'ð�ž‰' => 'âˆ‚',
  'ð�žŠ' => 'Ïµ',
  'ð�ž‹' => 'Ï‘',
  'ð�žŒ' => 'Ï°',
  'ð�ž�' => 'Ï•',
  'ð�žŽ' => 'Ï±',
  'ð�ž�' => 'Ï–',
  'ð�ž�' => 'Î‘',
  'ð�ž‘' => 'Î’',
  'ð�ž’' => 'Î“',
  'ð�ž“' => 'Î”',
  'ð�ž”' => 'Î•',
  'ð�ž•' => 'Î–',
  'ð�ž–' => 'Î—',
  'ð�ž—' => 'Î˜',
  'ð�ž˜' => 'Î™',
  'ð�ž™' => 'Îš',
  'ð�žš' => 'Î›',
  'ð�ž›' => 'Îœ',
  'ð�žœ' => 'Î�',
  'ð�ž�' => 'Îž',
  'ð�žž' => 'ÎŸ',
  'ð�žŸ' => 'Î ',
  'ð�ž ' => 'Î¡',
  'ð�ž¡' => 'Ï´',
  'ð�ž¢' => 'Î£',
  'ð�ž£' => 'Î¤',
  'ð�ž¤' => 'Î¥',
  'ð�ž¥' => 'Î¦',
  'ð�ž¦' => 'Î§',
  'ð�ž§' => 'Î¨',
  'ð�ž¨' => 'Î©',
  'ð�ž©' => 'âˆ‡',
  'ð�žª' => 'Î±',
  'ð�ž«' => 'Î²',
  'ð�ž¬' => 'Î³',
  'ð�ž­' => 'Î´',
  'ð�ž®' => 'Îµ',
  'ð�ž¯' => 'Î¶',
  'ð�ž°' => 'Î·',
  'ð�ž±' => 'Î¸',
  'ð�ž²' => 'Î¹',
  'ð�ž³' => 'Îº',
  'ð�ž´' => 'Î»',
  'ð�žµ' => 'Î¼',
  'ð�ž¶' => 'Î½',
  'ð�ž·' => 'Î¾',
  'ð�ž¸' => 'Î¿',
  'ð�ž¹' => 'Ï€',
  'ð�žº' => 'Ï�',
  'ð�ž»' => 'Ï‚',
  'ð�ž¼' => 'Ïƒ',
  'ð�ž½' => 'Ï„',
  'ð�ž¾' => 'Ï…',
  'ð�ž¿' => 'Ï†',
  'ð�Ÿ€' => 'Ï‡',
  'ð�Ÿ�' => 'Ïˆ',
  'ð�Ÿ‚' => 'Ï‰',
  'ð�Ÿƒ' => 'âˆ‚',
  'ð�Ÿ„' => 'Ïµ',
  'ð�Ÿ…' => 'Ï‘',
  'ð�Ÿ†' => 'Ï°',
  'ð�Ÿ‡' => 'Ï•',
  'ð�Ÿˆ' => 'Ï±',
  'ð�Ÿ‰' => 'Ï–',
  'ð�ŸŠ' => 'Ïœ',
  'ð�Ÿ‹' => 'Ï�',
  'ð�ŸŽ' => '0',
  'ð�Ÿ�' => '1',
  'ð�Ÿ�' => '2',
  'ð�Ÿ‘' => '3',
  'ð�Ÿ’' => '4',
  'ð�Ÿ“' => '5',
  'ð�Ÿ”' => '6',
  'ð�Ÿ•' => '7',
  'ð�Ÿ–' => '8',
  'ð�Ÿ—' => '9',
  'ð�Ÿ˜' => '0',
  'ð�Ÿ™' => '1',
  'ð�Ÿš' => '2',
  'ð�Ÿ›' => '3',
  'ð�Ÿœ' => '4',
  'ð�Ÿ�' => '5',
  'ð�Ÿž' => '6',
  'ð�ŸŸ' => '7',
  'ð�Ÿ ' => '8',
  'ð�Ÿ¡' => '9',
  'ð�Ÿ¢' => '0',
  'ð�Ÿ£' => '1',
  'ð�Ÿ¤' => '2',
  'ð�Ÿ¥' => '3',
  'ð�Ÿ¦' => '4',
  'ð�Ÿ§' => '5',
  'ð�Ÿ¨' => '6',
  'ð�Ÿ©' => '7',
  'ð�Ÿª' => '8',
  'ð�Ÿ«' => '9',
  'ð�Ÿ¬' => '0',
  'ð�Ÿ­' => '1',
  'ð�Ÿ®' => '2',
  'ð�Ÿ¯' => '3',
  'ð�Ÿ°' => '4',
  'ð�Ÿ±' => '5',
  'ð�Ÿ²' => '6',
  'ð�Ÿ³' => '7',
  'ð�Ÿ´' => '8',
  'ð�Ÿµ' => '9',
  'ð�Ÿ¶' => '0',
  'ð�Ÿ·' => '1',
  'ð�Ÿ¸' => '2',
  'ð�Ÿ¹' => '3',
  'ð�Ÿº' => '4',
  'ð�Ÿ»' => '5',
  'ð�Ÿ¼' => '6',
  'ð�Ÿ½' => '7',
  'ð�Ÿ¾' => '8',
  'ð�Ÿ¿' => '9',
  'ðž¸€' => 'Ø§',
  'ðž¸�' => 'Ø¨',
  'ðž¸‚' => 'Ø¬',
  'ðž¸ƒ' => 'Ø¯',
  'ðž¸…' => 'Ùˆ',
  'ðž¸†' => 'Ø²',
  'ðž¸‡' => 'Ø­',
  'ðž¸ˆ' => 'Ø·',
  'ðž¸‰' => 'ÙŠ',
  'ðž¸Š' => 'Ùƒ',
  'ðž¸‹' => 'Ù„',
  'ðž¸Œ' => 'Ù…',
  'ðž¸�' => 'Ù†',
  'ðž¸Ž' => 'Ø³',
  'ðž¸�' => 'Ø¹',
  'ðž¸�' => 'Ù�',
  'ðž¸‘' => 'Øµ',
  'ðž¸’' => 'Ù‚',
  'ðž¸“' => 'Ø±',
  'ðž¸”' => 'Ø´',
  'ðž¸•' => 'Øª',
  'ðž¸–' => 'Ø«',
  'ðž¸—' => 'Ø®',
  'ðž¸˜' => 'Ø°',
  'ðž¸™' => 'Ø¶',
  'ðž¸š' => 'Ø¸',
  'ðž¸›' => 'Øº',
  'ðž¸œ' => 'Ù®',
  'ðž¸�' => 'Úº',
  'ðž¸ž' => 'Ú¡',
  'ðž¸Ÿ' => 'Ù¯',
  'ðž¸¡' => 'Ø¨',
  'ðž¸¢' => 'Ø¬',
  'ðž¸¤' => 'Ù‡',
  'ðž¸§' => 'Ø­',
  'ðž¸©' => 'ÙŠ',
  'ðž¸ª' => 'Ùƒ',
  'ðž¸«' => 'Ù„',
  'ðž¸¬' => 'Ù…',
  'ðž¸­' => 'Ù†',
  'ðž¸®' => 'Ø³',
  'ðž¸¯' => 'Ø¹',
  'ðž¸°' => 'Ù�',
  'ðž¸±' => 'Øµ',
  'ðž¸²' => 'Ù‚',
  'ðž¸´' => 'Ø´',
  'ðž¸µ' => 'Øª',
  'ðž¸¶' => 'Ø«',
  'ðž¸·' => 'Ø®',
  'ðž¸¹' => 'Ø¶',
  'ðž¸»' => 'Øº',
  'ðž¹‚' => 'Ø¬',
  'ðž¹‡' => 'Ø­',
  'ðž¹‰' => 'ÙŠ',
  'ðž¹‹' => 'Ù„',
  'ðž¹�' => 'Ù†',
  'ðž¹Ž' => 'Ø³',
  'ðž¹�' => 'Ø¹',
  'ðž¹‘' => 'Øµ',
  'ðž¹’' => 'Ù‚',
  'ðž¹”' => 'Ø´',
  'ðž¹—' => 'Ø®',
  'ðž¹™' => 'Ø¶',
  'ðž¹›' => 'Øº',
  'ðž¹�' => 'Úº',
  'ðž¹Ÿ' => 'Ù¯',
  'ðž¹¡' => 'Ø¨',
  'ðž¹¢' => 'Ø¬',
  'ðž¹¤' => 'Ù‡',
  'ðž¹§' => 'Ø­',
  'ðž¹¨' => 'Ø·',
  'ðž¹©' => 'ÙŠ',
  'ðž¹ª' => 'Ùƒ',
  'ðž¹¬' => 'Ù…',
  'ðž¹­' => 'Ù†',
  'ðž¹®' => 'Ø³',
  'ðž¹¯' => 'Ø¹',
  'ðž¹°' => 'Ù�',
  'ðž¹±' => 'Øµ',
  'ðž¹²' => 'Ù‚',
  'ðž¹´' => 'Ø´',
  'ðž¹µ' => 'Øª',
  'ðž¹¶' => 'Ø«',
  'ðž¹·' => 'Ø®',
  'ðž¹¹' => 'Ø¶',
  'ðž¹º' => 'Ø¸',
  'ðž¹»' => 'Øº',
  'ðž¹¼' => 'Ù®',
  'ðž¹¾' => 'Ú¡',
  'ðžº€' => 'Ø§',
  'ðžº�' => 'Ø¨',
  'ðžº‚' => 'Ø¬',
  'ðžºƒ' => 'Ø¯',
  'ðžº„' => 'Ù‡',
  'ðžº…' => 'Ùˆ',
  'ðžº†' => 'Ø²',
  'ðžº‡' => 'Ø­',
  'ðžºˆ' => 'Ø·',
  'ðžº‰' => 'ÙŠ',
  'ðžº‹' => 'Ù„',
  'ðžºŒ' => 'Ù…',
  'ðžº�' => 'Ù†',
  'ðžºŽ' => 'Ø³',
  'ðžº�' => 'Ø¹',
  'ðžº�' => 'Ù�',
  'ðžº‘' => 'Øµ',
  'ðžº’' => 'Ù‚',
  'ðžº“' => 'Ø±',
  'ðžº”' => 'Ø´',
  'ðžº•' => 'Øª',
  'ðžº–' => 'Ø«',
  'ðžº—' => 'Ø®',
  'ðžº˜' => 'Ø°',
  'ðžº™' => 'Ø¶',
  'ðžºš' => 'Ø¸',
  'ðžº›' => 'Øº',
  'ðžº¡' => 'Ø¨',
  'ðžº¢' => 'Ø¬',
  'ðžº£' => 'Ø¯',
  'ðžº¥' => 'Ùˆ',
  'ðžº¦' => 'Ø²',
  'ðžº§' => 'Ø­',
  'ðžº¨' => 'Ø·',
  'ðžº©' => 'ÙŠ',
  'ðžº«' => 'Ù„',
  'ðžº¬' => 'Ù…',
  'ðžº­' => 'Ù†',
  'ðžº®' => 'Ø³',
  'ðžº¯' => 'Ø¹',
  'ðžº°' => 'Ù�',
  'ðžº±' => 'Øµ',
  'ðžº²' => 'Ù‚',
  'ðžº³' => 'Ø±',
  'ðžº´' => 'Ø´',
  'ðžºµ' => 'Øª',
  'ðžº¶' => 'Ø«',
  'ðžº·' => 'Ø®',
  'ðžº¸' => 'Ø°',
  'ðžº¹' => 'Ø¶',
  'ðžºº' => 'Ø¸',
  'ðžº»' => 'Øº',
  'ðŸ„€' => '0.',
  'ðŸ„�' => '0,',
  'ðŸ„‚' => '1,',
  'ðŸ„ƒ' => '2,',
  'ðŸ„„' => '3,',
  'ðŸ„…' => '4,',
  'ðŸ„†' => '5,',
  'ðŸ„‡' => '6,',
  'ðŸ„ˆ' => '7,',
  'ðŸ„‰' => '8,',
  'ðŸ„Š' => '9,',
  'ðŸ„�' => '(A)',
  'ðŸ„‘' => '(B)',
  'ðŸ„’' => '(C)',
  'ðŸ„“' => '(D)',
  'ðŸ„”' => '(E)',
  'ðŸ„•' => '(F)',
  'ðŸ„–' => '(G)',
  'ðŸ„—' => '(H)',
  'ðŸ„˜' => '(I)',
  'ðŸ„™' => '(J)',
  'ðŸ„š' => '(K)',
  'ðŸ„›' => '(L)',
  'ðŸ„œ' => '(M)',
  'ðŸ„�' => '(N)',
  'ðŸ„ž' => '(O)',
  'ðŸ„Ÿ' => '(P)',
  'ðŸ„ ' => '(Q)',
  'ðŸ„¡' => '(R)',
  'ðŸ„¢' => '(S)',
  'ðŸ„£' => '(T)',
  'ðŸ„¤' => '(U)',
  'ðŸ„¥' => '(V)',
  'ðŸ„¦' => '(W)',
  'ðŸ„§' => '(X)',
  'ðŸ„¨' => '(Y)',
  'ðŸ„©' => '(Z)',
  'ðŸ„ª' => 'ã€”Sã€•',
  'ðŸ„«' => '(C)',
  'ðŸ„¬' => '(R)',
  'ðŸ„­' => '(CD)',
  'ðŸ„®' => '(WZ)',
  'ðŸ„°' => 'A',
  'ðŸ„±' => 'B',
  'ðŸ„²' => 'C',
  'ðŸ„³' => 'D',
  'ðŸ„´' => 'E',
  'ðŸ„µ' => 'F',
  'ðŸ„¶' => 'G',
  'ðŸ„·' => 'H',
  'ðŸ„¸' => 'I',
  'ðŸ„¹' => 'J',
  'ðŸ„º' => 'K',
  'ðŸ„»' => 'L',
  'ðŸ„¼' => 'M',
  'ðŸ„½' => 'N',
  'ðŸ„¾' => 'O',
  'ðŸ„¿' => 'P',
  'ðŸ…€' => 'Q',
  'ðŸ…�' => 'R',
  'ðŸ…‚' => 'S',
  'ðŸ…ƒ' => 'T',
  'ðŸ…„' => 'U',
  'ðŸ……' => 'V',
  'ðŸ…†' => 'W',
  'ðŸ…‡' => 'X',
  'ðŸ…ˆ' => 'Y',
  'ðŸ…‰' => 'Z',
  'ðŸ…Š' => 'HV',
  'ðŸ…‹' => 'MV',
  'ðŸ…Œ' => 'SD',
  'ðŸ…�' => 'SS',
  'ðŸ…Ž' => 'PPV',
  'ðŸ…�' => 'WC',
  'ðŸ†�' => 'DJ',
  'ðŸˆ€' => 'ã�»ã�‹',
  'ðŸˆ�' => 'ã‚³ã‚³',
  'ðŸˆ‚' => 'ã‚µ',
  'ðŸˆ�' => 'æ‰‹',
  'ðŸˆ‘' => 'å­—',
  'ðŸˆ’' => 'å�Œ',
  'ðŸˆ“' => 'ãƒ‡',
  'ðŸˆ”' => 'äºŒ',
  'ðŸˆ•' => 'å¤š',
  'ðŸˆ–' => 'è§£',
  'ðŸˆ—' => 'å¤©',
  'ðŸˆ˜' => 'äº¤',
  'ðŸˆ™' => 'æ˜ ',
  'ðŸˆš' => 'ç„¡',
  'ðŸˆ›' => 'æ–™',
  'ðŸˆœ' => 'å‰�',
  'ðŸˆ�' => 'å¾Œ',
  'ðŸˆž' => 'å†�',
  'ðŸˆŸ' => 'æ–°',
  'ðŸˆ ' => 'åˆ�',
  'ðŸˆ¡' => 'çµ‚',
  'ðŸˆ¢' => 'ç”Ÿ',
  'ðŸˆ£' => 'è²©',
  'ðŸˆ¤' => 'å£°',
  'ðŸˆ¥' => 'å�¹',
  'ðŸˆ¦' => 'æ¼”',
  'ðŸˆ§' => 'æŠ•',
  'ðŸˆ¨' => 'æ�•',
  'ðŸˆ©' => 'ä¸€',
  'ðŸˆª' => 'ä¸‰',
  'ðŸˆ«' => 'é�Š',
  'ðŸˆ¬' => 'å·¦',
  'ðŸˆ­' => 'ä¸­',
  'ðŸˆ®' => 'å�³',
  'ðŸˆ¯' => 'æŒ‡',
  'ðŸˆ°' => 'èµ°',
  'ðŸˆ±' => 'æ‰“',
  'ðŸˆ²' => 'ç¦�',
  'ðŸˆ³' => 'ç©º',
  'ðŸˆ´' => 'å�ˆ',
  'ðŸˆµ' => 'æº€',
  'ðŸˆ¶' => 'æœ‰',
  'ðŸˆ·' => 'æœˆ',
  'ðŸˆ¸' => 'ç”³',
  'ðŸˆ¹' => 'å‰²',
  'ðŸˆº' => 'å–¶',
  'ðŸˆ»' => 'é…�',
  'ðŸ‰€' => 'ã€”æœ¬ã€•',
  'ðŸ‰�' => 'ã€”ä¸‰ã€•',
  'ðŸ‰‚' => 'ã€”äºŒã€•',
  'ðŸ‰ƒ' => 'ã€”å®‰ã€•',
  'ðŸ‰„' => 'ã€”ç‚¹ã€•',
  'ðŸ‰…' => 'ã€”æ‰“ã€•',
  'ðŸ‰†' => 'ã€”ç›—ã€•',
  'ðŸ‰‡' => 'ã€”å‹�ã€•',
  'ðŸ‰ˆ' => 'ã€”æ•—ã€•',
  'ðŸ‰�' => '(å¾—)',
  'ðŸ‰‘' => '(å�¯)',
  'ðŸ¯°' => '0',
  'ðŸ¯±' => '1',
  'ðŸ¯²' => '2',
  'ðŸ¯³' => '3',
  'ðŸ¯´' => '4',
  'ðŸ¯µ' => '5',
  'ðŸ¯¶' => '6',
  'ðŸ¯·' => '7',
  'ðŸ¯¸' => '8',
  'ðŸ¯¹' => '9',
  'ð¯ €' => 'ä¸½',
  'ð¯ �' => 'ä¸¸',
  'ð¯ ‚' => 'ä¹�',
  'ð¯ ƒ' => 'ð „¢',
  'ð¯ „' => 'ä½ ',
  'ð¯ …' => 'ä¾®',
  'ð¯ †' => 'ä¾»',
  'ð¯ ‡' => 'å€‚',
  'ð¯ ˆ' => 'å�º',
  'ð¯ ‰' => 'å‚™',
  'ð¯ Š' => 'åƒ§',
  'ð¯ ‹' => 'åƒ�',
  'ð¯ Œ' => 'ã’ž',
  'ð¯ �' => 'ð ˜º',
  'ð¯ Ž' => 'å…�',
  'ð¯ �' => 'å…”',
  'ð¯ �' => 'å…¤',
  'ð¯ ‘' => 'å…·',
  'ð¯ ’' => 'ð ”œ',
  'ð¯ “' => 'ã’¹',
  'ð¯ ”' => 'å…§',
  'ð¯ •' => 'å†�',
  'ð¯ –' => 'ð •‹',
  'ð¯ —' => 'å†—',
  'ð¯ ˜' => 'å†¤',
  'ð¯ ™' => 'ä»Œ',
  'ð¯ š' => 'å†¬',
  'ð¯ ›' => 'å†µ',
  'ð¯ œ' => 'ð©‡Ÿ',
  'ð¯ �' => 'å‡µ',
  'ð¯ ž' => 'åˆƒ',
  'ð¯ Ÿ' => 'ã“Ÿ',
  'ð¯  ' => 'åˆ»',
  'ð¯ ¡' => 'å‰†',
  'ð¯ ¢' => 'å‰²',
  'ð¯ £' => 'å‰·',
  'ð¯ ¤' => 'ã”•',
  'ð¯ ¥' => 'å‹‡',
  'ð¯ ¦' => 'å‹‰',
  'ð¯ §' => 'å‹¤',
  'ð¯ ¨' => 'å‹º',
  'ð¯ ©' => 'åŒ…',
  'ð¯ ª' => 'åŒ†',
  'ð¯ «' => 'åŒ—',
  'ð¯ ¬' => 'å�‰',
  'ð¯ ­' => 'å�‘',
  'ð¯ ®' => 'å�š',
  'ð¯ ¯' => 'å�³',
  'ð¯ °' => 'å�½',
  'ð¯ ±' => 'å�¿',
  'ð¯ ²' => 'å�¿',
  'ð¯ ³' => 'å�¿',
  'ð¯ ´' => 'ð ¨¬',
  'ð¯ µ' => 'ç�°',
  'ð¯ ¶' => 'å�Š',
  'ð¯ ·' => 'å�Ÿ',
  'ð¯ ¸' => 'ð ­£',
  'ð¯ ¹' => 'å�«',
  'ð¯ º' => 'å�±',
  'ð¯ »' => 'å�†',
  'ð¯ ¼' => 'å’ž',
  'ð¯ ½' => 'å�¸',
  'ð¯ ¾' => 'å‘ˆ',
  'ð¯ ¿' => 'å‘¨',
  'ð¯¡€' => 'å’¢',
  'ð¯¡�' => 'å“¶',
  'ð¯¡‚' => 'å”�',
  'ð¯¡ƒ' => 'å•“',
  'ð¯¡„' => 'å•£',
  'ð¯¡…' => 'å–„',
  'ð¯¡†' => 'å–„',
  'ð¯¡‡' => 'å–™',
  'ð¯¡ˆ' => 'å–«',
  'ð¯¡‰' => 'å–³',
  'ð¯¡Š' => 'å—‚',
  'ð¯¡‹' => 'åœ–',
  'ð¯¡Œ' => 'å˜†',
  'ð¯¡�' => 'åœ—',
  'ð¯¡Ž' => 'å™‘',
  'ð¯¡�' => 'å™´',
  'ð¯¡�' => 'åˆ‡',
  'ð¯¡‘' => 'å£®',
  'ð¯¡’' => 'åŸŽ',
  'ð¯¡“' => 'åŸ´',
  'ð¯¡”' => 'å �',
  'ð¯¡•' => 'åž‹',
  'ð¯¡–' => 'å ²',
  'ð¯¡—' => 'å ±',
  'ð¯¡˜' => 'å¢¬',
  'ð¯¡™' => 'ð¡“¤',
  'ð¯¡š' => 'å£²',
  'ð¯¡›' => 'å£·',
  'ð¯¡œ' => 'å¤†',
  'ð¯¡�' => 'å¤š',
  'ð¯¡ž' => 'å¤¢',
  'ð¯¡Ÿ' => 'å¥¢',
  'ð¯¡ ' => 'ð¡š¨',
  'ð¯¡¡' => 'ð¡›ª',
  'ð¯¡¢' => 'å§¬',
  'ð¯¡£' => 'å¨›',
  'ð¯¡¤' => 'å¨§',
  'ð¯¡¥' => 'å§˜',
  'ð¯¡¦' => 'å©¦',
  'ð¯¡§' => 'ã›®',
  'ð¯¡¨' => 'ã›¼',
  'ð¯¡©' => 'å¬ˆ',
  'ð¯¡ª' => 'å¬¾',
  'ð¯¡«' => 'å¬¾',
  'ð¯¡¬' => 'ð¡§ˆ',
  'ð¯¡­' => 'å¯ƒ',
  'ð¯¡®' => 'å¯˜',
  'ð¯¡¯' => 'å¯§',
  'ð¯¡°' => 'å¯³',
  'ð¯¡±' => 'ð¡¬˜',
  'ð¯¡²' => 'å¯¿',
  'ð¯¡³' => 'å°†',
  'ð¯¡´' => 'å½“',
  'ð¯¡µ' => 'å°¢',
  'ð¯¡¶' => 'ãž�',
  'ð¯¡·' => 'å± ',
  'ð¯¡¸' => 'å±®',
  'ð¯¡¹' => 'å³€',
  'ð¯¡º' => 'å²�',
  'ð¯¡»' => 'ð¡·¤',
  'ð¯¡¼' => 'åµƒ',
  'ð¯¡½' => 'ð¡·¦',
  'ð¯¡¾' => 'åµ®',
  'ð¯¡¿' => 'åµ«',
  'ð¯¢€' => 'åµ¼',
  'ð¯¢�' => 'å·¡',
  'ð¯¢‚' => 'å·¢',
  'ð¯¢ƒ' => 'ã ¯',
  'ð¯¢„' => 'å·½',
  'ð¯¢…' => 'å¸¨',
  'ð¯¢†' => 'å¸½',
  'ð¯¢‡' => 'å¹©',
  'ð¯¢ˆ' => 'ã¡¢',
  'ð¯¢‰' => 'ð¢†ƒ',
  'ð¯¢Š' => 'ã¡¼',
  'ð¯¢‹' => 'åº°',
  'ð¯¢Œ' => 'åº³',
  'ð¯¢�' => 'åº¶',
  'ð¯¢Ž' => 'å»Š',
  'ð¯¢�' => 'ðªŽ’',
  'ð¯¢�' => 'å»¾',
  'ð¯¢‘' => 'ð¢Œ±',
  'ð¯¢’' => 'ð¢Œ±',
  'ð¯¢“' => 'èˆ�',
  'ð¯¢”' => 'å¼¢',
  'ð¯¢•' => 'å¼¢',
  'ð¯¢–' => 'ã£‡',
  'ð¯¢—' => 'ð£Š¸',
  'ð¯¢˜' => 'ð¦‡š',
  'ð¯¢™' => 'å½¢',
  'ð¯¢š' => 'å½«',
  'ð¯¢›' => 'ã££',
  'ð¯¢œ' => 'å¾š',
  'ð¯¢�' => 'å¿�',
  'ð¯¢ž' => 'å¿—',
  'ð¯¢Ÿ' => 'å¿¹',
  'ð¯¢ ' => 'æ‚�',
  'ð¯¢¡' => 'ã¤º',
  'ð¯¢¢' => 'ã¤œ',
  'ð¯¢£' => 'æ‚”',
  'ð¯¢¤' => 'ð¢›”',
  'ð¯¢¥' => 'æƒ‡',
  'ð¯¢¦' => 'æ…ˆ',
  'ð¯¢§' => 'æ…Œ',
  'ð¯¢¨' => 'æ…Ž',
  'ð¯¢©' => 'æ…Œ',
  'ð¯¢ª' => 'æ…º',
  'ð¯¢«' => 'æ†Ž',
  'ð¯¢¬' => 'æ†²',
  'ð¯¢­' => 'æ†¤',
  'ð¯¢®' => 'æ†¯',
  'ð¯¢¯' => 'æ‡ž',
  'ð¯¢°' => 'æ‡²',
  'ð¯¢±' => 'æ‡¶',
  'ð¯¢²' => 'æˆ�',
  'ð¯¢³' => 'æˆ›',
  'ð¯¢´' => 'æ‰�',
  'ð¯¢µ' => 'æŠ±',
  'ð¯¢¶' => 'æ‹”',
  'ð¯¢·' => 'æ��',
  'ð¯¢¸' => 'ð¢¬Œ',
  'ð¯¢¹' => 'æŒ½',
  'ð¯¢º' => 'æ‹¼',
  'ð¯¢»' => 'æ�¨',
  'ð¯¢¼' => 'æŽƒ',
  'ð¯¢½' => 'æ�¤',
  'ð¯¢¾' => 'ð¢¯±',
  'ð¯¢¿' => 'æ�¢',
  'ð¯£€' => 'æ�…',
  'ð¯£�' => 'æŽ©',
  'ð¯£‚' => 'ã¨®',
  'ð¯£ƒ' => 'æ‘©',
  'ð¯£„' => 'æ‘¾',
  'ð¯£…' => 'æ’�',
  'ð¯£†' => 'æ‘·',
  'ð¯£‡' => 'ã©¬',
  'ð¯£ˆ' => 'æ•�',
  'ð¯£‰' => 'æ•¬',
  'ð¯£Š' => 'ð£€Š',
  'ð¯£‹' => 'æ—£',
  'ð¯£Œ' => 'æ›¸',
  'ð¯£�' => 'æ™‰',
  'ð¯£Ž' => 'ã¬™',
  'ð¯£�' => 'æš‘',
  'ð¯£�' => 'ã¬ˆ',
  'ð¯£‘' => 'ã«¤',
  'ð¯£’' => 'å†’',
  'ð¯£“' => 'å†•',
  'ð¯£”' => 'æœ€',
  'ð¯£•' => 'æšœ',
  'ð¯£–' => 'è‚­',
  'ð¯£—' => 'ä�™',
  'ð¯£˜' => 'æœ—',
  'ð¯£™' => 'æœ›',
  'ð¯£š' => 'æœ¡',
  'ð¯£›' => 'æ�ž',
  'ð¯£œ' => 'æ�“',
  'ð¯£�' => 'ð£�ƒ',
  'ð¯£ž' => 'ã­‰',
  'ð¯£Ÿ' => 'æŸº',
  'ð¯£ ' => 'æž…',
  'ð¯£¡' => 'æ¡’',
  'ð¯£¢' => 'æ¢…',
  'ð¯££' => 'ð£‘­',
  'ð¯£¤' => 'æ¢Ž',
  'ð¯£¥' => 'æ Ÿ',
  'ð¯£¦' => 'æ¤”',
  'ð¯£§' => 'ã®�',
  'ð¯£¨' => 'æ¥‚',
  'ð¯£©' => 'æ¦£',
  'ð¯£ª' => 'æ§ª',
  'ð¯£«' => 'æª¨',
  'ð¯£¬' => 'ð£š£',
  'ð¯£­' => 'æ«›',
  'ð¯£®' => 'ã°˜',
  'ð¯£¯' => 'æ¬¡',
  'ð¯£°' => 'ð£¢§',
  'ð¯£±' => 'æ­”',
  'ð¯£²' => 'ã±Ž',
  'ð¯£³' => 'æ­²',
  'ð¯£´' => 'æ®Ÿ',
  'ð¯£µ' => 'æ®º',
  'ð¯£¶' => 'æ®»',
  'ð¯£·' => 'ð£ª�',
  'ð¯£¸' => 'ð¡´‹',
  'ð¯£¹' => 'ð£«º',
  'ð¯£º' => 'æ±Ž',
  'ð¯£»' => 'ð£²¼',
  'ð¯£¼' => 'æ²¿',
  'ð¯£½' => 'æ³�',
  'ð¯£¾' => 'æ±§',
  'ð¯£¿' => 'æ´–',
  'ð¯¤€' => 'æ´¾',
  'ð¯¤�' => 'æµ·',
  'ð¯¤‚' => 'æµ�',
  'ð¯¤ƒ' => 'æµ©',
  'ð¯¤„' => 'æµ¸',
  'ð¯¤…' => 'æ¶…',
  'ð¯¤†' => 'ð£´ž',
  'ð¯¤‡' => 'æ´´',
  'ð¯¤ˆ' => 'æ¸¯',
  'ð¯¤‰' => 'æ¹®',
  'ð¯¤Š' => 'ã´³',
  'ð¯¤‹' => 'æ»‹',
  'ð¯¤Œ' => 'æ»‡',
  'ð¯¤�' => 'ð£»‘',
  'ð¯¤Ž' => 'æ·¹',
  'ð¯¤�' => 'æ½®',
  'ð¯¤�' => 'ð£½ž',
  'ð¯¤‘' => 'ð£¾Ž',
  'ð¯¤’' => 'æ¿†',
  'ð¯¤“' => 'ç€¹',
  'ð¯¤”' => 'ç€ž',
  'ð¯¤•' => 'ç€›',
  'ð¯¤–' => 'ã¶–',
  'ð¯¤—' => 'ç�Š',
  'ð¯¤˜' => 'ç�½',
  'ð¯¤™' => 'ç�·',
  'ð¯¤š' => 'ç‚­',
  'ð¯¤›' => 'ð ”¥',
  'ð¯¤œ' => 'ç……',
  'ð¯¤�' => 'ð¤‰£',
  'ð¯¤ž' => 'ç†œ',
  'ð¯¤Ÿ' => 'ð¤Ž«',
  'ð¯¤ ' => 'çˆ¨',
  'ð¯¤¡' => 'çˆµ',
  'ð¯¤¢' => 'ç‰�',
  'ð¯¤£' => 'ð¤˜ˆ',
  'ð¯¤¤' => 'çŠ€',
  'ð¯¤¥' => 'çŠ•',
  'ð¯¤¦' => 'ð¤œµ',
  'ð¯¤§' => 'ð¤ ”',
  'ð¯¤¨' => 'ç�º',
  'ð¯¤©' => 'çŽ‹',
  'ð¯¤ª' => 'ãº¬',
  'ð¯¤«' => 'çŽ¥',
  'ð¯¤¬' => 'ãº¸',
  'ð¯¤­' => 'ãº¸',
  'ð¯¤®' => 'ç‘‡',
  'ð¯¤¯' => 'ç‘œ',
  'ð¯¤°' => 'ç‘±',
  'ð¯¤±' => 'ç’…',
  'ð¯¤²' => 'ç“Š',
  'ð¯¤³' => 'ã¼›',
  'ð¯¤´' => 'ç”¤',
  'ð¯¤µ' => 'ð¤°¶',
  'ð¯¤¶' => 'ç”¾',
  'ð¯¤·' => 'ð¤²’',
  'ð¯¤¸' => 'ç•°',
  'ð¯¤¹' => 'ð¢†Ÿ',
  'ð¯¤º' => 'ç˜�',
  'ð¯¤»' => 'ð¤¾¡',
  'ð¯¤¼' => 'ð¤¾¸',
  'ð¯¤½' => 'ð¥�„',
  'ð¯¤¾' => 'ã¿¼',
  'ð¯¤¿' => 'ä€ˆ',
  'ð¯¥€' => 'ç›´',
  'ð¯¥�' => 'ð¥ƒ³',
  'ð¯¥‚' => 'ð¥ƒ²',
  'ð¯¥ƒ' => 'ð¥„™',
  'ð¯¥„' => 'ð¥„³',
  'ð¯¥…' => 'çœž',
  'ð¯¥†' => 'çœŸ',
  'ð¯¥‡' => 'çœŸ',
  'ð¯¥ˆ' => 'ç�Š',
  'ð¯¥‰' => 'ä€¹',
  'ð¯¥Š' => 'çž‹',
  'ð¯¥‹' => 'ä�†',
  'ð¯¥Œ' => 'ä‚–',
  'ð¯¥�' => 'ð¥��',
  'ð¯¥Ž' => 'ç¡Ž',
  'ð¯¥�' => 'ç¢Œ',
  'ð¯¥�' => 'ç£Œ',
  'ð¯¥‘' => 'äƒ£',
  'ð¯¥’' => 'ð¥˜¦',
  'ð¯¥“' => 'ç¥–',
  'ð¯¥”' => 'ð¥šš',
  'ð¯¥•' => 'ð¥›…',
  'ð¯¥–' => 'ç¦�',
  'ð¯¥—' => 'ç§«',
  'ð¯¥˜' => 'ä„¯',
  'ð¯¥™' => 'ç©€',
  'ð¯¥š' => 'ç©Š',
  'ð¯¥›' => 'ç©�',
  'ð¯¥œ' => 'ð¥¥¼',
  'ð¯¥�' => 'ð¥ª§',
  'ð¯¥ž' => 'ð¥ª§',
  'ð¯¥Ÿ' => 'ç«®',
  'ð¯¥ ' => 'äˆ‚',
  'ð¯¥¡' => 'ð¥®«',
  'ð¯¥¢' => 'ç¯†',
  'ð¯¥£' => 'ç¯‰',
  'ð¯¥¤' => 'äˆ§',
  'ð¯¥¥' => 'ð¥²€',
  'ð¯¥¦' => 'ç³’',
  'ð¯¥§' => 'äŠ ',
  'ð¯¥¨' => 'ç³¨',
  'ð¯¥©' => 'ç³£',
  'ð¯¥ª' => 'ç´€',
  'ð¯¥«' => 'ð¥¾†',
  'ð¯¥¬' => 'çµ£',
  'ð¯¥­' => 'äŒ�',
  'ð¯¥®' => 'ç·‡',
  'ð¯¥¯' => 'ç¸‚',
  'ð¯¥°' => 'ç¹…',
  'ð¯¥±' => 'äŒ´',
  'ð¯¥²' => 'ð¦ˆ¨',
  'ð¯¥³' => 'ð¦‰‡',
  'ð¯¥´' => 'ä�™',
  'ð¯¥µ' => 'ð¦‹™',
  'ð¯¥¶' => 'ç½º',
  'ð¯¥·' => 'ð¦Œ¾',
  'ð¯¥¸' => 'ç¾•',
  'ð¯¥¹' => 'ç¿º',
  'ð¯¥º' => 'è€…',
  'ð¯¥»' => 'ð¦“š',
  'ð¯¥¼' => 'ð¦”£',
  'ð¯¥½' => 'è� ',
  'ð¯¥¾' => 'ð¦–¨',
  'ð¯¥¿' => 'è�°',
  'ð¯¦€' => 'ð£�Ÿ',
  'ð¯¦�' => 'ä�•',
  'ð¯¦‚' => 'è‚²',
  'ð¯¦ƒ' => 'è„ƒ',
  'ð¯¦„' => 'ä�‹',
  'ð¯¦…' => 'è„¾',
  'ð¯¦†' => 'åªµ',
  'ð¯¦‡' => 'ð¦ž§',
  'ð¯¦ˆ' => 'ð¦žµ',
  'ð¯¦‰' => 'ð£Ž“',
  'ð¯¦Š' => 'ð£Žœ',
  'ð¯¦‹' => 'èˆ�',
  'ð¯¦Œ' => 'èˆ„',
  'ð¯¦�' => 'è¾ž',
  'ð¯¦Ž' => 'ä‘«',
  'ð¯¦�' => 'èŠ‘',
  'ð¯¦�' => 'èŠ‹',
  'ð¯¦‘' => 'èŠ�',
  'ð¯¦’' => 'åŠ³',
  'ð¯¦“' => 'èŠ±',
  'ð¯¦”' => 'èŠ³',
  'ð¯¦•' => 'èŠ½',
  'ð¯¦–' => 'è‹¦',
  'ð¯¦—' => 'ð¦¬¼',
  'ð¯¦˜' => 'è‹¥',
  'ð¯¦™' => 'èŒ�',
  'ð¯¦š' => 'è�£',
  'ð¯¦›' => 'èŽ­',
  'ð¯¦œ' => 'èŒ£',
  'ð¯¦�' => 'èŽ½',
  'ð¯¦ž' => 'è�§',
  'ð¯¦Ÿ' => 'è‘—',
  'ð¯¦ ' => 'è�“',
  'ð¯¦¡' => 'è�Š',
  'ð¯¦¢' => 'è�Œ',
  'ð¯¦£' => 'è�œ',
  'ð¯¦¤' => 'ð¦°¶',
  'ð¯¦¥' => 'ð¦µ«',
  'ð¯¦¦' => 'ð¦³•',
  'ð¯¦§' => 'ä”«',
  'ð¯¦¨' => 'è“±',
  'ð¯¦©' => 'è“³',
  'ð¯¦ª' => 'è”–',
  'ð¯¦«' => 'ð§�Š',
  'ð¯¦¬' => 'è•¤',
  'ð¯¦­' => 'ð¦¼¬',
  'ð¯¦®' => 'ä•�',
  'ð¯¦¯' => 'ä•¡',
  'ð¯¦°' => 'ð¦¾±',
  'ð¯¦±' => 'ð§ƒ’',
  'ð¯¦²' => 'ä•«',
  'ð¯¦³' => 'è™�',
  'ð¯¦´' => 'è™œ',
  'ð¯¦µ' => 'è™§',
  'ð¯¦¶' => 'è™©',
  'ð¯¦·' => 'èš©',
  'ð¯¦¸' => 'èšˆ',
  'ð¯¦¹' => 'èœŽ',
  'ð¯¦º' => 'è›¢',
  'ð¯¦»' => 'è�¹',
  'ð¯¦¼' => 'èœ¨',
  'ð¯¦½' => 'è�«',
  'ð¯¦¾' => 'èž†',
  'ð¯¦¿' => 'ä——',
  'ð¯§€' => 'èŸ¡',
  'ð¯§�' => 'è �',
  'ð¯§‚' => 'ä—¹',
  'ð¯§ƒ' => 'è¡ ',
  'ð¯§„' => 'è¡£',
  'ð¯§…' => 'ð§™§',
  'ð¯§†' => 'è£—',
  'ð¯§‡' => 'è£ž',
  'ð¯§ˆ' => 'ä˜µ',
  'ð¯§‰' => 'è£º',
  'ð¯§Š' => 'ã’»',
  'ð¯§‹' => 'ð§¢®',
  'ð¯§Œ' => 'ð§¥¦',
  'ð¯§�' => 'äš¾',
  'ð¯§Ž' => 'ä›‡',
  'ð¯§�' => 'èª ',
  'ð¯§�' => 'è«­',
  'ð¯§‘' => 'è®Š',
  'ð¯§’' => 'è±•',
  'ð¯§“' => 'ð§²¨',
  'ð¯§”' => 'è²«',
  'ð¯§•' => 'è³�',
  'ð¯§–' => 'è´›',
  'ð¯§—' => 'èµ·',
  'ð¯§˜' => 'ð§¼¯',
  'ð¯§™' => 'ð  „',
  'ð¯§š' => 'è·‹',
  'ð¯§›' => 'è¶¼',
  'ð¯§œ' => 'è·°',
  'ð¯§�' => 'ð £ž',
  'ð¯§ž' => 'è»”',
  'ð¯§Ÿ' => 'è¼¸',
  'ð¯§ ' => 'ð¨—’',
  'ð¯§¡' => 'ð¨—­',
  'ð¯§¢' => 'é‚”',
  'ð¯§£' => 'éƒ±',
  'ð¯§¤' => 'é„‘',
  'ð¯§¥' => 'ð¨œ®',
  'ð¯§¦' => 'é„›',
  'ð¯§§' => 'éˆ¸',
  'ð¯§¨' => 'é‹—',
  'ð¯§©' => 'é‹˜',
  'ð¯§ª' => 'é‰¼',
  'ð¯§«' => 'é�¹',
  'ð¯§¬' => 'é�•',
  'ð¯§­' => 'ð¨¯º',
  'ð¯§®' => 'é–‹',
  'ð¯§¯' => 'ä¦•',
  'ð¯§°' => 'é–·',
  'ð¯§±' => 'ð¨µ·',
  'ð¯§²' => 'ä§¦',
  'ð¯§³' => 'é›ƒ',
  'ð¯§´' => 'å¶²',
  'ð¯§µ' => 'éœ£',
  'ð¯§¶' => 'ð©……',
  'ð¯§·' => 'ð©ˆš',
  'ð¯§¸' => 'ä©®',
  'ð¯§¹' => 'ä©¶',
  'ð¯§º' => 'éŸ ',
  'ð¯§»' => 'ð©�Š',
  'ð¯§¼' => 'äª²',
  'ð¯§½' => 'ð©’–',
  'ð¯§¾' => 'é ‹',
  'ð¯§¿' => 'é ‹',
  'ð¯¨€' => 'é ©',
  'ð¯¨�' => 'ð©–¶',
  'ð¯¨‚' => 'é£¢',
  'ð¯¨ƒ' => 'ä¬³',
  'ð¯¨„' => 'é¤©',
  'ð¯¨…' => 'é¦§',
  'ð¯¨†' => 'é§‚',
  'ð¯¨‡' => 'é§¾',
  'ð¯¨ˆ' => 'ä¯Ž',
  'ð¯¨‰' => 'ð©¬°',
  'ð¯¨Š' => 'é¬’',
  'ð¯¨‹' => 'é±€',
  'ð¯¨Œ' => 'é³½',
  'ð¯¨�' => 'ä³Ž',
  'ð¯¨Ž' => 'ä³­',
  'ð¯¨�' => 'éµ§',
  'ð¯¨�' => 'ðªƒŽ',
  'ð¯¨‘' => 'ä³¸',
  'ð¯¨’' => 'ðª„…',
  'ð¯¨“' => 'ðªˆŽ',
  'ð¯¨”' => 'ðªŠ‘',
  'ð¯¨•' => 'éº»',
  'ð¯¨–' => 'äµ–',
  'ð¯¨—' => 'é»¹',
  'ð¯¨˜' => 'é»¾',
  'ð¯¨™' => 'é¼…',
  'ð¯¨š' => 'é¼�',
  'ð¯¨›' => 'é¼–',
  'ð¯¨œ' => 'é¼»',
  'ð¯¨�' => 'ðª˜€',
  'Ã†' => 'AE',
  'Ã�' => 'D',
  'Ã˜' => 'O',
  'Ãž' => 'TH',
  'ÃŸ' => 'ss',
  'Ã¦' => 'ae',
  'Ã°' => 'd',
  'Ã¸' => 'o',
  'Ã¾' => 'th',
  'Ä�' => 'D',
  'Ä‘' => 'd',
  'Ä¦' => 'H',
  'Ä§' => 'h',
  'Ä±' => 'i',
  'Ä¸' => 'q',
  'Å�' => 'L',
  'Å‚' => 'l',
  'ÅŠ' => 'N',
  'Å‹' => 'n',
  'Å’' => 'OE',
  'Å“' => 'oe',
  'Å¦' => 'T',
  'Å§' => 't',
  'Æ€' => 'b',
  'Æ�' => 'B',
  'Æ‚' => 'B',
  'Æƒ' => 'b',
  'Æ‡' => 'C',
  'Æˆ' => 'c',
  'Æ‰' => 'D',
  'ÆŠ' => 'D',
  'Æ‹' => 'D',
  'ÆŒ' => 'd',
  'Æ�' => 'E',
  'Æ‘' => 'F',
  'Æ’' => 'f',
  'Æ“' => 'G',
  'Æ•' => 'hv',
  'Æ–' => 'I',
  'Æ—' => 'I',
  'Æ˜' => 'K',
  'Æ™' => 'k',
  'Æš' => 'l',
  'Æ�' => 'N',
  'Æž' => 'n',
  'Æ¢' => 'OI',
  'Æ£' => 'oi',
  'Æ¤' => 'P',
  'Æ¥' => 'p',
  'Æ«' => 't',
  'Æ¬' => 'T',
  'Æ­' => 't',
  'Æ®' => 'T',
  'Æ²' => 'V',
  'Æ³' => 'Y',
  'Æ´' => 'y',
  'Æµ' => 'Z',
  'Æ¶' => 'z',
  'Ç¤' => 'G',
  'Ç¥' => 'g',
  'È¡' => 'd',
  'È¤' => 'Z',
  'È¥' => 'z',
  'È´' => 'l',
  'Èµ' => 'n',
  'È¶' => 't',
  'È·' => 'j',
  'È¸' => 'db',
  'È¹' => 'qp',
  'Èº' => 'A',
  'È»' => 'C',
  'È¼' => 'c',
  'È½' => 'L',
  'È¾' => 'T',
  'È¿' => 's',
  'É€' => 'z',
  'Éƒ' => 'B',
  'É„' => 'U',
  'É†' => 'E',
  'É‡' => 'e',
  'Éˆ' => 'J',
  'É‰' => 'j',
  'ÉŒ' => 'R',
  'É�' => 'r',
  'ÉŽ' => 'Y',
  'É�' => 'y',
  'É“' => 'b',
  'É•' => 'c',
  'É–' => 'd',
  'É—' => 'd',
  'É›' => 'e',
  'ÉŸ' => 'j',
  'É ' => 'g',
  'É¡' => 'g',
  'É¢' => 'G',
  'É¦' => 'h',
  'É§' => 'h',
  'É¨' => 'i',
  'Éª' => 'I',
  'É«' => 'l',
  'É¬' => 'l',
  'É­' => 'l',
  'É±' => 'm',
  'É²' => 'n',
  'É³' => 'n',
  'É´' => 'N',
  'É¶' => 'OE',
  'É¼' => 'r',
  'É½' => 'r',
  'É¾' => 'r',
  'Ê€' => 'R',
  'Ê‚' => 's',
  'Êˆ' => 't',
  'Ê‰' => 'u',
  'Ê‹' => 'v',
  'Ê�' => 'Y',
  'Ê�' => 'z',
  'Ê‘' => 'z',
  'Ê™' => 'B',
  'Ê›' => 'G',
  'Êœ' => 'H',
  'Ê�' => 'j',
  'ÊŸ' => 'L',
  'Ê ' => 'q',
  'Ê£' => 'dz',
  'Ê¥' => 'dz',
  'Ê¦' => 'ts',
  'Êª' => 'ls',
  'Ê«' => 'lz',
  'á´€' => 'A',
  'á´�' => 'AE',
  'á´ƒ' => 'B',
  'á´„' => 'C',
  'á´…' => 'D',
  'á´†' => 'D',
  'á´‡' => 'E',
  'á´Š' => 'J',
  'á´‹' => 'K',
  'á´Œ' => 'L',
  'á´�' => 'M',
  'á´�' => 'O',
  'á´˜' => 'P',
  'á´›' => 'T',
  'á´œ' => 'U',
  'á´ ' => 'V',
  'á´¡' => 'W',
  'á´¢' => 'Z',
  'áµ«' => 'ue',
  'áµ¬' => 'b',
  'áµ­' => 'd',
  'áµ®' => 'f',
  'áµ¯' => 'm',
  'áµ°' => 'n',
  'áµ±' => 'p',
  'áµ²' => 'r',
  'áµ³' => 'r',
  'áµ´' => 's',
  'áµµ' => 't',
  'áµ¶' => 'z',
  'áµº' => 'th',
  'áµ»' => 'I',
  'áµ½' => 'p',
  'áµ¾' => 'U',
  'á¶€' => 'b',
  'á¶�' => 'd',
  'á¶‚' => 'f',
  'á¶ƒ' => 'g',
  'á¶„' => 'k',
  'á¶…' => 'l',
  'á¶†' => 'm',
  'á¶‡' => 'n',
  'á¶ˆ' => 'p',
  'á¶‰' => 'r',
  'á¶Š' => 's',
  'á¶Œ' => 'v',
  'á¶�' => 'x',
  'á¶Ž' => 'z',
  'á¶�' => 'a',
  'á¶‘' => 'd',
  'á¶’' => 'e',
  'á¶“' => 'e',
  'á¶–' => 'i',
  'á¶™' => 'u',
  'áºœ' => 's',
  'áº�' => 's',
  'áºž' => 'SS',
  'á»º' => 'LL',
  'á»»' => 'll',
  'á»¼' => 'V',
  'á»½' => 'v',
  'á»¾' => 'Y',
  'á»¿' => 'y',
  'â± ' => 'L',
  'â±¡' => 'l',
  'â±¢' => 'L',
  'â±£' => 'P',
  'â±¤' => 'R',
  'â±¥' => 'a',
  'â±¦' => 't',
  'â±§' => 'H',
  'â±¨' => 'h',
  'â±©' => 'K',
  'â±ª' => 'k',
  'â±«' => 'Z',
  'â±¬' => 'z',
  'â±®' => 'M',
  'â±±' => 'v',
  'â±²' => 'W',
  'â±³' => 'w',
  'â±´' => 'v',
  'â±¸' => 'e',
  'â±º' => 'o',
  'â±¾' => 'S',
  'â±¿' => 'Z',
  'êœ°' => 'F',
  'êœ±' => 'S',
  'êœ²' => 'AA',
  'êœ³' => 'aa',
  'êœ´' => 'AO',
  'êœµ' => 'ao',
  'êœ¶' => 'AU',
  'êœ·' => 'au',
  'êœ¸' => 'AV',
  'êœ¹' => 'av',
  'êœº' => 'AV',
  'êœ»' => 'av',
  'êœ¼' => 'AY',
  'êœ½' => 'ay',
  'ê�€' => 'K',
  'ê��' => 'k',
  'ê�‚' => 'K',
  'ê�ƒ' => 'k',
  'ê�„' => 'K',
  'ê�…' => 'k',
  'ê�†' => 'L',
  'ê�‡' => 'l',
  'ê�ˆ' => 'L',
  'ê�‰' => 'l',
  'ê�Š' => 'O',
  'ê�‹' => 'o',
  'ê�Œ' => 'O',
  'ê��' => 'o',
  'ê�Ž' => 'OO',
  'ê��' => 'oo',
  'ê��' => 'P',
  'ê�‘' => 'p',
  'ê�’' => 'P',
  'ê�“' => 'p',
  'ê�”' => 'P',
  'ê�•' => 'p',
  'ê�–' => 'Q',
  'ê�—' => 'q',
  'ê�˜' => 'Q',
  'ê�™' => 'q',
  'ê�ž' => 'V',
  'ê�Ÿ' => 'v',
  'ê� ' => 'VY',
  'ê�¡' => 'vy',
  'ê�¤' => 'TH',
  'ê�¥' => 'th',
  'ê�¦' => 'TH',
  'ê�§' => 'th',
  'ê�±' => 'd',
  'ê�²' => 'l',
  'ê�³' => 'm',
  'ê�´' => 'n',
  'ê�µ' => 'r',
  'ê�¶' => 'R',
  'ê�·' => 't',
  'ê�¹' => 'D',
  'ê�º' => 'd',
  'ê�»' => 'F',
  'ê�¼' => 'f',
  'êž†' => 'T',
  'êž‡' => 't',
  'êž�' => 'N',
  'êž‘' => 'n',
  'êž’' => 'C',
  'êž“' => 'c',
  'êž ' => 'G',
  'êž¡' => 'g',
  'êž¢' => 'K',
  'êž£' => 'k',
  'êž¤' => 'N',
  'êž¥' => 'n',
  'êž¦' => 'R',
  'êž§' => 'r',
  'êž¨' => 'S',
  'êž©' => 's',
  'êžª' => 'H',
  'Â©' => '(C)',
  'Â®' => '(R)',
  'â‚ ' => 'CE',
  'â‚¢' => 'Cr',
  'â‚£' => 'Fr.',
  'â‚¤' => 'L.',
  'â‚§' => 'Pts',
  'â‚¹' => 'Rs',
  'â‚º' => 'TL',
  'â„—' => '(P)',
  'â„˜' => 'P',
  'â„ž' => 'Rx',
  'ã€‡' => '0',
  'Â ' => ' ',
  'â€‚' => ' ',
  'â€ƒ' => ' ',
  'â€„' => ' ',
  'â€…' => ' ',
  'â€†' => ' ',
  'â€‡' => ' ',
  'â€ˆ' => ' ',
  'â€‰' => ' ',
  'â€Š' => ' ',
  'â�Ÿ' => ' ',
  'Ê¹' => '\'',
  'Êº' => '"',
  'Ê»' => '\'',
  'Ê¼' => '\'',
  'Ê½' => '\'',
  'Ëˆ' => '\'',
  'Ë‹' => '`',
  'â€˜' => '\'',
  'â€™' => '\'',
  'â€š' => ',',
  'â€›' => '\'',
  'â€œ' => '"',
  'â€�' => '"',
  'â€ž' => ',,',
  'â€Ÿ' => '"',
  'â€²' => '\'',
  'ã€�' => '"',
  'ã€ž' => '"',
  'Â«' => '<<',
  'Â»' => '>>',
  'â€¹' => '<',
  'â€º' => '>',
  'Â­' => '-',
  'â€�' => '-',
  'â€‘' => '-',
  'â€’' => '-',
  'â€“' => '-',
  'â€”' => '-',
  'â€•' => '-',
  'ï¸±' => '-',
  'ï¸²' => '-',
  'Â¡' => '!',
  'Â¿' => '?',
  'Ë‚' => '<',
  'Ëƒ' => '>',
  'Ë„' => '^',
  'Ë†' => '^',
  'Ë�' => ':',
  'Ëœ' => '~',
  'â€–' => '||',
  'â�„' => '/',
  'â�…' => '[',
  'â�†' => ']',
  'â�Ž' => '*',
  'ã€�' => ',',
  'ã€‚' => '.',
  'ã€ˆ' => '<',
  'ã€‰' => '>',
  'ã€Š' => '<<',
  'ã€‹' => '>>',
  'ã€”' => '[',
  'ã€•' => ']',
  'ã€˜' => '[',
  'ã€™' => ']',
  'ã€š' => '[',
  'ã€›' => ']',
  'ï¸�' => ',',
  'ï¸‘' => ',',
  'ï¸’' => '.',
  'ï¸“' => ':',
  'ï¸”' => ';',
  'ï¸•' => '!',
  'ï¸–' => '?',
  'ï¸™' => '...',
  'ï¸°' => '..',
  'ï¸µ' => '(',
  'ï¸¶' => ')',
  'ï¸·' => '{',
  'ï¸¸' => '}',
  'ï¸¹' => '[',
  'ï¸º' => ']',
  'ï¸½' => '<<',
  'ï¸¾' => '>>',
  'ï¸¿' => '<',
  'ï¹€' => '>',
  'ï¹‡' => '[',
  'ï¹ˆ' => ']',
  'Â±' => '+/-',
  'Ã—' => '*',
  'Ã·' => '/',
  'Ë–' => '+',
  'Ë—' => '-',
  'âˆ’' => '-',
  'âˆ•' => '/',
  'âˆ–' => '\\',
  'âˆ£' => '|',
  'âˆ¥' => '||',
  'â‰ª' => '<<',
  'â‰«' => '>>',
  'â¦…' => '((',
  'â¦†' => '))',
  'Ã€' => 'A',
  'Ã�' => 'A',
  'Ã‚' => 'A',
  'Ãƒ' => 'A',
  'Ã„' => 'A',
  'Ã…' => 'A',
  'Ã‡' => 'C',
  'Ãˆ' => 'E',
  'Ã‰' => 'E',
  'ÃŠ' => 'E',
  'Ã‹' => 'E',
  'ÃŒ' => 'I',
  'Ã�' => 'I',
  'ÃŽ' => 'I',
  'Ã�' => 'I',
  'Ã‘' => 'N',
  'Ã’' => 'O',
  'Ã“' => 'O',
  'Ã”' => 'O',
  'Ã•' => 'O',
  'Ã–' => 'O',
  'Ã™' => 'U',
  'Ãš' => 'U',
  'Ã›' => 'U',
  'Ãœ' => 'U',
  'Ã�' => 'Y',
  'Ã ' => 'a',
  'Ã¡' => 'a',
  'Ã¢' => 'a',
  'Ã£' => 'a',
  'Ã¤' => 'a',
  'Ã¥' => 'a',
  'Ã§' => 'c',
  'Ã¨' => 'e',
  'Ã©' => 'e',
  'Ãª' => 'e',
  'Ã«' => 'e',
  'Ã¬' => 'i',
  'Ã­' => 'i',
  'Ã®' => 'i',
  'Ã¯' => 'i',
  'Ã±' => 'n',
  'Ã²' => 'o',
  'Ã³' => 'o',
  'Ã´' => 'o',
  'Ãµ' => 'o',
  'Ã¶' => 'o',
  'Ã¹' => 'u',
  'Ãº' => 'u',
  'Ã»' => 'u',
  'Ã¼' => 'u',
  'Ã½' => 'y',
  'Ã¿' => 'y',
  'Ä€' => 'A',
  'Ä�' => 'a',
  'Ä‚' => 'A',
  'Äƒ' => 'a',
  'Ä„' => 'A',
  'Ä…' => 'a',
  'Ä†' => 'C',
  'Ä‡' => 'c',
  'Äˆ' => 'C',
  'Ä‰' => 'c',
  'ÄŠ' => 'C',
  'Ä‹' => 'c',
  'ÄŒ' => 'C',
  'Ä�' => 'c',
  'ÄŽ' => 'D',
  'Ä�' => 'd',
  'Ä’' => 'E',
  'Ä“' => 'e',
  'Ä”' => 'E',
  'Ä•' => 'e',
  'Ä–' => 'E',
  'Ä—' => 'e',
  'Ä˜' => 'E',
  'Ä™' => 'e',
  'Äš' => 'E',
  'Ä›' => 'e',
  'Äœ' => 'G',
  'Ä�' => 'g',
  'Äž' => 'G',
  'ÄŸ' => 'g',
  'Ä ' => 'G',
  'Ä¡' => 'g',
  'Ä¢' => 'G',
  'Ä£' => 'g',
  'Ä¤' => 'H',
  'Ä¥' => 'h',
  'Ä¨' => 'I',
  'Ä©' => 'i',
  'Äª' => 'I',
  'Ä«' => 'i',
  'Ä¬' => 'I',
  'Ä­' => 'i',
  'Ä®' => 'I',
  'Ä¯' => 'i',
  'Ä°' => 'I',
  'Ä´' => 'J',
  'Äµ' => 'j',
  'Ä¶' => 'K',
  'Ä·' => 'k',
  'Ä¹' => 'L',
  'Äº' => 'l',
  'Ä»' => 'L',
  'Ä¼' => 'l',
  'Ä½' => 'L',
  'Ä¾' => 'l',
  'Åƒ' => 'N',
  'Å„' => 'n',
  'Å…' => 'N',
  'Å†' => 'n',
  'Å‡' => 'N',
  'Åˆ' => 'n',
  'ÅŒ' => 'O',
  'Å�' => 'o',
  'ÅŽ' => 'O',
  'Å�' => 'o',
  'Å�' => 'O',
  'Å‘' => 'o',
  'Å”' => 'R',
  'Å•' => 'r',
  'Å–' => 'R',
  'Å—' => 'r',
  'Å˜' => 'R',
  'Å™' => 'r',
  'Åš' => 'S',
  'Å›' => 's',
  'Åœ' => 'S',
  'Å�' => 's',
  'Åž' => 'S',
  'ÅŸ' => 's',
  'Å ' => 'S',
  'Å¡' => 's',
  'Å¢' => 'T',
  'Å£' => 't',
  'Å¤' => 'T',
  'Å¥' => 't',
  'Å¨' => 'U',
  'Å©' => 'u',
  'Åª' => 'U',
  'Å«' => 'u',
  'Å¬' => 'U',
  'Å­' => 'u',
  'Å®' => 'U',
  'Å¯' => 'u',
  'Å°' => 'U',
  'Å±' => 'u',
  'Å²' => 'U',
  'Å³' => 'u',
  'Å´' => 'W',
  'Åµ' => 'w',
  'Å¶' => 'Y',
  'Å·' => 'y',
  'Å¸' => 'Y',
  'Å¹' => 'Z',
  'Åº' => 'z',
  'Å»' => 'Z',
  'Å¼' => 'z',
  'Å½' => 'Z',
  'Å¾' => 'z',
  'Æ ' => 'O',
  'Æ¡' => 'o',
  'Æ¯' => 'U',
  'Æ°' => 'u',
  'Ç�' => 'A',
  'ÇŽ' => 'a',
  'Ç�' => 'I',
  'Ç�' => 'i',
  'Ç‘' => 'O',
  'Ç’' => 'o',
  'Ç“' => 'U',
  'Ç”' => 'u',
  'Ç•' => 'U',
  'Ç–' => 'u',
  'Ç—' => 'U',
  'Ç˜' => 'u',
  'Ç™' => 'U',
  'Çš' => 'u',
  'Ç›' => 'U',
  'Çœ' => 'u',
  'Çž' => 'A',
  'ÇŸ' => 'a',
  'Ç ' => 'A',
  'Ç¡' => 'a',
  'Ç¦' => 'G',
  'Ç§' => 'g',
  'Ç¨' => 'K',
  'Ç©' => 'k',
  'Çª' => 'O',
  'Ç«' => 'o',
  'Ç¬' => 'O',
  'Ç­' => 'o',
  'Ç°' => 'j',
  'Ç´' => 'G',
  'Çµ' => 'g',
  'Ç¸' => 'N',
  'Ç¹' => 'n',
  'Çº' => 'A',
  'Ç»' => 'a',
  'È€' => 'A',
  'È�' => 'a',
  'È‚' => 'A',
  'Èƒ' => 'a',
  'È„' => 'E',
  'È…' => 'e',
  'È†' => 'E',
  'È‡' => 'e',
  'Èˆ' => 'I',
  'È‰' => 'i',
  'ÈŠ' => 'I',
  'È‹' => 'i',
  'ÈŒ' => 'O',
  'È�' => 'o',
  'ÈŽ' => 'O',
  'È�' => 'o',
  'È�' => 'R',
  'È‘' => 'r',
  'È’' => 'R',
  'È“' => 'r',
  'È”' => 'U',
  'È•' => 'u',
  'È–' => 'U',
  'È—' => 'u',
  'È˜' => 'S',
  'È™' => 's',
  'Èš' => 'T',
  'È›' => 't',
  'Èž' => 'H',
  'ÈŸ' => 'h',
  'È¦' => 'A',
  'È§' => 'a',
  'È¨' => 'E',
  'È©' => 'e',
  'Èª' => 'O',
  'È«' => 'o',
  'È¬' => 'O',
  'È­' => 'o',
  'È®' => 'O',
  'È¯' => 'o',
  'È°' => 'O',
  'È±' => 'o',
  'È²' => 'Y',
  'È³' => 'y',
  'Í¾' => ';',
  'á¸€' => 'A',
  'á¸�' => 'a',
  'á¸‚' => 'B',
  'á¸ƒ' => 'b',
  'á¸„' => 'B',
  'á¸…' => 'b',
  'á¸†' => 'B',
  'á¸‡' => 'b',
  'á¸ˆ' => 'C',
  'á¸‰' => 'c',
  'á¸Š' => 'D',
  'á¸‹' => 'd',
  'á¸Œ' => 'D',
  'á¸�' => 'd',
  'á¸Ž' => 'D',
  'á¸�' => 'd',
  'á¸�' => 'D',
  'á¸‘' => 'd',
  'á¸’' => 'D',
  'á¸“' => 'd',
  'á¸”' => 'E',
  'á¸•' => 'e',
  'á¸–' => 'E',
  'á¸—' => 'e',
  'á¸˜' => 'E',
  'á¸™' => 'e',
  'á¸š' => 'E',
  'á¸›' => 'e',
  'á¸œ' => 'E',
  'á¸�' => 'e',
  'á¸ž' => 'F',
  'á¸Ÿ' => 'f',
  'á¸ ' => 'G',
  'á¸¡' => 'g',
  'á¸¢' => 'H',
  'á¸£' => 'h',
  'á¸¤' => 'H',
  'á¸¥' => 'h',
  'á¸¦' => 'H',
  'á¸§' => 'h',
  'á¸¨' => 'H',
  'á¸©' => 'h',
  'á¸ª' => 'H',
  'á¸«' => 'h',
  'á¸¬' => 'I',
  'á¸­' => 'i',
  'á¸®' => 'I',
  'á¸¯' => 'i',
  'á¸°' => 'K',
  'á¸±' => 'k',
  'á¸²' => 'K',
  'á¸³' => 'k',
  'á¸´' => 'K',
  'á¸µ' => 'k',
  'á¸¶' => 'L',
  'á¸·' => 'l',
  'á¸¸' => 'L',
  'á¸¹' => 'l',
  'á¸º' => 'L',
  'á¸»' => 'l',
  'á¸¼' => 'L',
  'á¸½' => 'l',
  'á¸¾' => 'M',
  'á¸¿' => 'm',
  'á¹€' => 'M',
  'á¹�' => 'm',
  'á¹‚' => 'M',
  'á¹ƒ' => 'm',
  'á¹„' => 'N',
  'á¹…' => 'n',
  'á¹†' => 'N',
  'á¹‡' => 'n',
  'á¹ˆ' => 'N',
  'á¹‰' => 'n',
  'á¹Š' => 'N',
  'á¹‹' => 'n',
  'á¹Œ' => 'O',
  'á¹�' => 'o',
  'á¹Ž' => 'O',
  'á¹�' => 'o',
  'á¹�' => 'O',
  'á¹‘' => 'o',
  'á¹’' => 'O',
  'á¹“' => 'o',
  'á¹”' => 'P',
  'á¹•' => 'p',
  'á¹–' => 'P',
  'á¹—' => 'p',
  'á¹˜' => 'R',
  'á¹™' => 'r',
  'á¹š' => 'R',
  'á¹›' => 'r',
  'á¹œ' => 'R',
  'á¹�' => 'r',
  'á¹ž' => 'R',
  'á¹Ÿ' => 'r',
  'á¹ ' => 'S',
  'á¹¡' => 's',
  'á¹¢' => 'S',
  'á¹£' => 's',
  'á¹¤' => 'S',
  'á¹¥' => 's',
  'á¹¦' => 'S',
  'á¹§' => 's',
  'á¹¨' => 'S',
  'á¹©' => 's',
  'á¹ª' => 'T',
  'á¹«' => 't',
  'á¹¬' => 'T',
  'á¹­' => 't',
  'á¹®' => 'T',
  'á¹¯' => 't',
  'á¹°' => 'T',
  'á¹±' => 't',
  'á¹²' => 'U',
  'á¹³' => 'u',
  'á¹´' => 'U',
  'á¹µ' => 'u',
  'á¹¶' => 'U',
  'á¹·' => 'u',
  'á¹¸' => 'U',
  'á¹¹' => 'u',
  'á¹º' => 'U',
  'á¹»' => 'u',
  'á¹¼' => 'V',
  'á¹½' => 'v',
  'á¹¾' => 'V',
  'á¹¿' => 'v',
  'áº€' => 'W',
  'áº�' => 'w',
  'áº‚' => 'W',
  'áºƒ' => 'w',
  'áº„' => 'W',
  'áº…' => 'w',
  'áº†' => 'W',
  'áº‡' => 'w',
  'áºˆ' => 'W',
  'áº‰' => 'w',
  'áºŠ' => 'X',
  'áº‹' => 'x',
  'áºŒ' => 'X',
  'áº�' => 'x',
  'áºŽ' => 'Y',
  'áº�' => 'y',
  'áº�' => 'Z',
  'áº‘' => 'z',
  'áº’' => 'Z',
  'áº“' => 'z',
  'áº”' => 'Z',
  'áº•' => 'z',
  'áº–' => 'h',
  'áº—' => 't',
  'áº˜' => 'w',
  'áº™' => 'y',
  'áº ' => 'A',
  'áº¡' => 'a',
  'áº¢' => 'A',
  'áº£' => 'a',
  'áº¤' => 'A',
  'áº¥' => 'a',
  'áº¦' => 'A',
  'áº§' => 'a',
  'áº¨' => 'A',
  'áº©' => 'a',
  'áºª' => 'A',
  'áº«' => 'a',
  'áº¬' => 'A',
  'áº­' => 'a',
  'áº®' => 'A',
  'áº¯' => 'a',
  'áº°' => 'A',
  'áº±' => 'a',
  'áº²' => 'A',
  'áº³' => 'a',
  'áº´' => 'A',
  'áºµ' => 'a',
  'áº¶' => 'A',
  'áº·' => 'a',
  'áº¸' => 'E',
  'áº¹' => 'e',
  'áºº' => 'E',
  'áº»' => 'e',
  'áº¼' => 'E',
  'áº½' => 'e',
  'áº¾' => 'E',
  'áº¿' => 'e',
  'á»€' => 'E',
  'á»�' => 'e',
  'á»‚' => 'E',
  'á»ƒ' => 'e',
  'á»„' => 'E',
  'á»…' => 'e',
  'á»†' => 'E',
  'á»‡' => 'e',
  'á»ˆ' => 'I',
  'á»‰' => 'i',
  'á»Š' => 'I',
  'á»‹' => 'i',
  'á»Œ' => 'O',
  'á»�' => 'o',
  'á»Ž' => 'O',
  'á»�' => 'o',
  'á»�' => 'O',
  'á»‘' => 'o',
  'á»’' => 'O',
  'á»“' => 'o',
  'á»”' => 'O',
  'á»•' => 'o',
  'á»–' => 'O',
  'á»—' => 'o',
  'á»˜' => 'O',
  'á»™' => 'o',
  'á»š' => 'O',
  'á»›' => 'o',
  'á»œ' => 'O',
  'á»�' => 'o',
  'á»ž' => 'O',
  'á»Ÿ' => 'o',
  'á» ' => 'O',
  'á»¡' => 'o',
  'á»¢' => 'O',
  'á»£' => 'o',
  'á»¤' => 'U',
  'á»¥' => 'u',
  'á»¦' => 'U',
  'á»§' => 'u',
  'á»¨' => 'U',
  'á»©' => 'u',
  'á»ª' => 'U',
  'á»«' => 'u',
  'á»¬' => 'U',
  'á»­' => 'u',
  'á»®' => 'U',
  'á»¯' => 'u',
  'á»°' => 'U',
  'á»±' => 'u',
  'á»²' => 'Y',
  'á»³' => 'y',
  'á»´' => 'Y',
  'á»µ' => 'y',
  'á»¶' => 'Y',
  'á»·' => 'y',
  'á»¸' => 'Y',
  'á»¹' => 'y',
  'á¿¯' => '`',
  'â„ª' => 'K',
  'â„«' => 'A',
  'â‰ ' => '=',
  'â‰®' => '<',
  'â‰¯' => '>',
);
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => '%',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
  '€' => 'Â€',
  '�' => 'Â�',
  '‚' => 'Â‚',
  'ƒ' => 'Âƒ',
  '„' => 'Â„',
  '…' => 'Â…',
  '†' => 'Â†',
  '‡' => 'Â‡',
  'ˆ' => 'Âˆ',
  '‰' => 'Â‰',
  'Š' => 'ÂŠ',
  '‹' => 'Â‹',
  'Œ' => 'ÂŒ',
  '�' => 'Â�',
  'Ž' => 'ÂŽ',
  '�' => 'Â�',
  '�' => 'Â�',
  '‘' => 'Â‘',
  '’' => 'Â’',
  '“' => 'Â“',
  '”' => 'Â”',
  '•' => 'Â•',
  '–' => 'Â–',
  '—' => 'Â—',
  '˜' => 'Â˜',
  '™' => 'Â™',
  'š' => 'Âš',
  '›' => 'Â›',
  'œ' => 'Âœ',
  '�' => 'Â�',
  'ž' => 'Âž',
  'Ÿ' => 'ÂŸ',
  ' ' => 'Â ',
  '¢' => 'Â¢',
  '£' => 'Â£',
  '¤' => 'Â¤',
  '¥' => 'Â¥',
  '¦' => 'Â¦',
  '§' => 'Â§',
  '¨' => 'Â¨',
  '©' => 'Â©',
  'ª' => 'Ã—',
  '«' => 'Â«',
  '¬' => 'Â¬',
  '­' => 'Â­',
  '®' => 'Â®',
  '¯' => 'Â¯',
  '°' => 'Â°',
  '±' => 'Â±',
  '²' => 'Â²',
  '³' => 'Â³',
  '´' => 'Â´',
  'µ' => 'Âµ',
  '¶' => 'Â¶',
  '·' => 'Â·',
  '¸' => 'Â¸',
  '¹' => 'Â¹',
  'º' => 'Ã·',
  '»' => 'Â»',
  '¼' => 'Â¼',
  '½' => 'Â½',
  '¾' => 'Â¾',
  'ß' => 'â€—',
  'à' => '×�',
  'á' => '×‘',
  'â' => '×’',
  'ã' => '×“',
  'ä' => '×”',
  'å' => '×•',
  'æ' => '×–',
  'ç' => '×—',
  'è' => '×˜',
  'é' => '×™',
  'ê' => '×š',
  'ë' => '×›',
  'ì' => '×œ',
  'í' => '×�',
  'î' => '×ž',
  'ï' => '×Ÿ',
  'ð' => '× ',
  'ñ' => '×¡',
  'ò' => '×¢',
  'ó' => '×£',
  'ô' => '×¤',
  'õ' => '×¥',
  'ö' => '×¦',
  '÷' => '×§',
  'ø' => '×¨',
  'ù' => '×©',
  'ú' => '×ª',
  'ý' => 'â€Ž',
  'þ' => 'â€�',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => '%',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
  '€' => 'Â€',
  '�' => 'Â�',
  '‚' => 'Â‚',
  'ƒ' => 'Âƒ',
  '„' => 'Â„',
  '…' => 'Â…',
  '†' => 'Â†',
  '‡' => 'Â‡',
  'ˆ' => 'Âˆ',
  '‰' => 'Â‰',
  'Š' => 'ÂŠ',
  '‹' => 'Â‹',
  'Œ' => 'ÂŒ',
  '�' => 'Â�',
  'Ž' => 'ÂŽ',
  '�' => 'Â�',
  '�' => 'Â�',
  '‘' => 'Â‘',
  '’' => 'Â’',
  '“' => 'Â“',
  '”' => 'Â”',
  '•' => 'Â•',
  '–' => 'Â–',
  '—' => 'Â—',
  '˜' => 'Â˜',
  '™' => 'Â™',
  'š' => 'Âš',
  '›' => 'Â›',
  'œ' => 'Âœ',
  '�' => 'Â�',
  'ž' => 'Âž',
  'Ÿ' => 'ÂŸ',
  ' ' => 'Â ',
  '¡' => 'à¸�',
  '¢' => 'à¸‚',
  '£' => 'à¸ƒ',
  '¤' => 'à¸„',
  '¥' => 'à¸…',
  '¦' => 'à¸†',
  '§' => 'à¸‡',
  '¨' => 'à¸ˆ',
  '©' => 'à¸‰',
  'ª' => 'à¸Š',
  '«' => 'à¸‹',
  '¬' => 'à¸Œ',
  '­' => 'à¸�',
  '®' => 'à¸Ž',
  '¯' => 'à¸�',
  '°' => 'à¸�',
  '±' => 'à¸‘',
  '²' => 'à¸’',
  '³' => 'à¸“',
  '´' => 'à¸”',
  'µ' => 'à¸•',
  '¶' => 'à¸–',
  '·' => 'à¸—',
  '¸' => 'à¸˜',
  '¹' => 'à¸™',
  'º' => 'à¸š',
  '»' => 'à¸›',
  '¼' => 'à¸œ',
  '½' => 'à¸�',
  '¾' => 'à¸ž',
  '¿' => 'à¸Ÿ',
  'À' => 'à¸ ',
  'Á' => 'à¸¡',
  'Â' => 'à¸¢',
  'Ã' => 'à¸£',
  'Ä' => 'à¸¤',
  'Å' => 'à¸¥',
  'Æ' => 'à¸¦',
  'Ç' => 'à¸§',
  'È' => 'à¸¨',
  'É' => 'à¸©',
  'Ê' => 'à¸ª',
  'Ë' => 'à¸«',
  'Ì' => 'à¸¬',
  'Í' => 'à¸­',
  'Î' => 'à¸®',
  'Ï' => 'à¸¯',
  'Ð' => 'à¸°',
  'Ñ' => 'à¸±',
  'Ò' => 'à¸²',
  'Ó' => 'à¸³',
  'Ô' => 'à¸´',
  'Õ' => 'à¸µ',
  'Ö' => 'à¸¶',
  '×' => 'à¸·',
  'Ø' => 'à¸¸',
  'Ù' => 'à¸¹',
  'Ú' => 'à¸º',
  'ß' => 'à¸¿',
  'à' => 'à¹€',
  'á' => 'à¹�',
  'â' => 'à¹‚',
  'ã' => 'à¹ƒ',
  'ä' => 'à¹„',
  'å' => 'à¹…',
  'æ' => 'à¹†',
  'ç' => 'à¹‡',
  'è' => 'à¹ˆ',
  'é' => 'à¹‰',
  'ê' => 'à¹Š',
  'ë' => 'à¹‹',
  'ì' => 'à¹Œ',
  'í' => 'à¹�',
  'î' => 'à¹Ž',
  'ï' => 'à¹�',
  'ð' => 'à¹�',
  'ñ' => 'à¹‘',
  'ò' => 'à¹’',
  'ó' => 'à¹“',
  'ô' => 'à¹”',
  'õ' => 'à¹•',
  'ö' => 'à¹–',
  '÷' => 'à¹—',
  'ø' => 'à¹˜',
  'ù' => 'à¹™',
  'ú' => 'à¹š',
  'û' => 'à¹›',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => '%',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
  '€' => 'â‚¬',
  '‚' => 'â€š',
  'ƒ' => 'Æ’',
  '„' => 'â€ž',
  '…' => 'â€¦',
  '†' => 'â€ ',
  '‡' => 'â€¡',
  'ˆ' => 'Ë†',
  '‰' => 'â€°',
  '‹' => 'â€¹',
  'Œ' => 'Å’',
  '‘' => 'â€˜',
  '’' => 'â€™',
  '“' => 'â€œ',
  '”' => 'â€�',
  '•' => 'â€¢',
  '–' => 'â€“',
  '—' => 'â€”',
  '˜' => 'Ëœ',
  '™' => 'â„¢',
  '›' => 'â€º',
  'œ' => 'Å“',
  'Ÿ' => 'Å¸',
  ' ' => 'Â ',
  '¡' => 'Â¡',
  '¢' => 'Â¢',
  '£' => 'Â£',
  '¤' => 'Â¤',
  '¥' => 'Â¥',
  '¦' => 'Â¦',
  '§' => 'Â§',
  '¨' => 'Â¨',
  '©' => 'Â©',
  'ª' => 'Âª',
  '«' => 'Â«',
  '¬' => 'Â¬',
  '­' => 'Â­',
  '®' => 'Â®',
  '¯' => 'Â¯',
  '°' => 'Â°',
  '±' => 'Â±',
  '²' => 'Â²',
  '³' => 'Â³',
  '´' => 'Â´',
  'µ' => 'Âµ',
  '¶' => 'Â¶',
  '·' => 'Â·',
  '¸' => 'Â¸',
  '¹' => 'Â¹',
  'º' => 'Âº',
  '»' => 'Â»',
  '¼' => 'Â¼',
  '½' => 'Â½',
  '¾' => 'Â¾',
  '¿' => 'Â¿',
  'À' => 'Ã€',
  'Á' => 'Ã�',
  'Â' => 'Ã‚',
  'Ã' => 'Ä‚',
  'Ä' => 'Ã„',
  'Å' => 'Ã…',
  'Æ' => 'Ã†',
  'Ç' => 'Ã‡',
  'È' => 'Ãˆ',
  'É' => 'Ã‰',
  'Ê' => 'ÃŠ',
  'Ë' => 'Ã‹',
  'Ì' => 'Ì€',
  'Í' => 'Ã�',
  'Î' => 'ÃŽ',
  'Ï' => 'Ã�',
  'Ð' => 'Ä�',
  'Ñ' => 'Ã‘',
  'Ò' => 'Ì‰',
  'Ó' => 'Ã“',
  'Ô' => 'Ã”',
  'Õ' => 'Æ ',
  'Ö' => 'Ã–',
  '×' => 'Ã—',
  'Ø' => 'Ã˜',
  'Ù' => 'Ã™',
  'Ú' => 'Ãš',
  'Û' => 'Ã›',
  'Ü' => 'Ãœ',
  'Ý' => 'Æ¯',
  'Þ' => 'Ìƒ',
  'ß' => 'ÃŸ',
  'à' => 'Ã ',
  'á' => 'Ã¡',
  'â' => 'Ã¢',
  'ã' => 'Äƒ',
  'ä' => 'Ã¤',
  'å' => 'Ã¥',
  'æ' => 'Ã¦',
  'ç' => 'Ã§',
  'è' => 'Ã¨',
  'é' => 'Ã©',
  'ê' => 'Ãª',
  'ë' => 'Ã«',
  'ì' => 'Ì�',
  'í' => 'Ã­',
  'î' => 'Ã®',
  'ï' => 'Ã¯',
  'ð' => 'Ä‘',
  'ñ' => 'Ã±',
  'ò' => 'Ì£',
  'ó' => 'Ã³',
  'ô' => 'Ã´',
  'õ' => 'Æ¡',
  'ö' => 'Ã¶',
  '÷' => 'Ã·',
  'ø' => 'Ã¸',
  'ù' => 'Ã¹',
  'ú' => 'Ãº',
  'û' => 'Ã»',
  'ü' => 'Ã¼',
  'ý' => 'Æ°',
  'þ' => 'â‚«',
  'ÿ' => 'Ã¿',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => '%',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
  '€' => 'Ä†',
  '�' => 'Ã¼',
  '‚' => 'Ã©',
  'ƒ' => 'Ä�',
  '„' => 'Ã¤',
  '…' => 'Ä£',
  '†' => 'Ã¥',
  '‡' => 'Ä‡',
  'ˆ' => 'Å‚',
  '‰' => 'Ä“',
  'Š' => 'Å–',
  '‹' => 'Å—',
  'Œ' => 'Ä«',
  '�' => 'Å¹',
  'Ž' => 'Ã„',
  '�' => 'Ã…',
  '�' => 'Ã‰',
  '‘' => 'Ã¦',
  '’' => 'Ã†',
  '“' => 'Å�',
  '”' => 'Ã¶',
  '•' => 'Ä¢',
  '–' => 'Â¢',
  '—' => 'Åš',
  '˜' => 'Å›',
  '™' => 'Ã–',
  'š' => 'Ãœ',
  '›' => 'Ã¸',
  'œ' => 'Â£',
  '�' => 'Ã˜',
  'ž' => 'Ã—',
  'Ÿ' => 'Â¤',
  ' ' => 'Ä€',
  '¡' => 'Äª',
  '¢' => 'Ã³',
  '£' => 'Å»',
  '¤' => 'Å¼',
  '¥' => 'Åº',
  '¦' => 'â€�',
  '§' => 'Â¦',
  '¨' => 'Â©',
  '©' => 'Â®',
  'ª' => 'Â¬',
  '«' => 'Â½',
  '¬' => 'Â¼',
  '­' => 'Å�',
  '®' => 'Â«',
  '¯' => 'Â»',
  '°' => 'â–‘',
  '±' => 'â–’',
  '²' => 'â–“',
  '³' => 'â”‚',
  '´' => 'â”¤',
  'µ' => 'Ä„',
  '¶' => 'ÄŒ',
  '·' => 'Ä˜',
  '¸' => 'Ä–',
  '¹' => 'â•£',
  'º' => 'â•‘',
  '»' => 'â•—',
  '¼' => 'â•�',
  '½' => 'Ä®',
  '¾' => 'Å ',
  '¿' => 'â”�',
  'À' => 'â””',
  'Á' => 'â”´',
  'Â' => 'â”¬',
  'Ã' => 'â”œ',
  'Ä' => 'â”€',
  'Å' => 'â”¼',
  'Æ' => 'Å²',
  'Ç' => 'Åª',
  'È' => 'â•š',
  'É' => 'â•”',
  'Ê' => 'â•©',
  'Ë' => 'â•¦',
  'Ì' => 'â• ',
  'Í' => 'â•�',
  'Î' => 'â•¬',
  'Ï' => 'Å½',
  'Ð' => 'Ä…',
  'Ñ' => 'Ä�',
  'Ò' => 'Ä™',
  'Ó' => 'Ä—',
  'Ô' => 'Ä¯',
  'Õ' => 'Å¡',
  'Ö' => 'Å³',
  '×' => 'Å«',
  'Ø' => 'Å¾',
  'Ù' => 'â”˜',
  'Ú' => 'â”Œ',
  'Û' => 'â–ˆ',
  'Ü' => 'â–„',
  'Ý' => 'â–Œ',
  'Þ' => 'â–�',
  'ß' => 'â–€',
  'à' => 'Ã“',
  'á' => 'ÃŸ',
  'â' => 'ÅŒ',
  'ã' => 'Åƒ',
  'ä' => 'Ãµ',
  'å' => 'Ã•',
  'æ' => 'Âµ',
  'ç' => 'Å„',
  'è' => 'Ä¶',
  'é' => 'Ä·',
  'ê' => 'Ä»',
  'ë' => 'Ä¼',
  'ì' => 'Å†',
  'í' => 'Ä’',
  'î' => 'Å…',
  'ï' => 'â€™',
  'ð' => 'Â­',
  'ñ' => 'Â±',
  'ò' => 'â€œ',
  'ó' => 'Â¾',
  'ô' => 'Â¶',
  'õ' => 'Â§',
  'ö' => 'Ã·',
  '÷' => 'â€ž',
  'ø' => 'Â°',
  'ù' => 'âˆ™',
  'ú' => 'Â·',
  'û' => 'Â¹',
  'ü' => 'Â³',
  'ý' => 'Â²',
  'þ' => 'â– ',
  'ÿ' => 'Â ',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => '%',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
  '€' => 'â‚¬',
  '�' => 'Ù¾',
  '‚' => 'â€š',
  'ƒ' => 'Æ’',
  '„' => 'â€ž',
  '…' => 'â€¦',
  '†' => 'â€ ',
  '‡' => 'â€¡',
  'ˆ' => 'Ë†',
  '‰' => 'â€°',
  'Š' => 'Ù¹',
  '‹' => 'â€¹',
  'Œ' => 'Å’',
  '�' => 'Ú†',
  'Ž' => 'Ú˜',
  '�' => 'Úˆ',
  '�' => 'Ú¯',
  '‘' => 'â€˜',
  '’' => 'â€™',
  '“' => 'â€œ',
  '”' => 'â€�',
  '•' => 'â€¢',
  '–' => 'â€“',
  '—' => 'â€”',
  '˜' => 'Ú©',
  '™' => 'â„¢',
  'š' => 'Ú‘',
  '›' => 'â€º',
  'œ' => 'Å“',
  '�' => 'â€Œ',
  'ž' => 'â€�',
  'Ÿ' => 'Úº',
  ' ' => 'Â ',
  '¡' => 'ØŒ',
  '¢' => 'Â¢',
  '£' => 'Â£',
  '¤' => 'Â¤',
  '¥' => 'Â¥',
  '¦' => 'Â¦',
  '§' => 'Â§',
  '¨' => 'Â¨',
  '©' => 'Â©',
  'ª' => 'Ú¾',
  '«' => 'Â«',
  '¬' => 'Â¬',
  '­' => 'Â­',
  '®' => 'Â®',
  '¯' => 'Â¯',
  '°' => 'Â°',
  '±' => 'Â±',
  '²' => 'Â²',
  '³' => 'Â³',
  '´' => 'Â´',
  'µ' => 'Âµ',
  '¶' => 'Â¶',
  '·' => 'Â·',
  '¸' => 'Â¸',
  '¹' => 'Â¹',
  'º' => 'Ø›',
  '»' => 'Â»',
  '¼' => 'Â¼',
  '½' => 'Â½',
  '¾' => 'Â¾',
  '¿' => 'ØŸ',
  'À' => 'Û�',
  'Á' => 'Ø¡',
  'Â' => 'Ø¢',
  'Ã' => 'Ø£',
  'Ä' => 'Ø¤',
  'Å' => 'Ø¥',
  'Æ' => 'Ø¦',
  'Ç' => 'Ø§',
  'È' => 'Ø¨',
  'É' => 'Ø©',
  'Ê' => 'Øª',
  'Ë' => 'Ø«',
  'Ì' => 'Ø¬',
  'Í' => 'Ø­',
  'Î' => 'Ø®',
  'Ï' => 'Ø¯',
  'Ð' => 'Ø°',
  'Ñ' => 'Ø±',
  'Ò' => 'Ø²',
  'Ó' => 'Ø³',
  'Ô' => 'Ø´',
  'Õ' => 'Øµ',
  'Ö' => 'Ø¶',
  '×' => 'Ã—',
  'Ø' => 'Ø·',
  'Ù' => 'Ø¸',
  'Ú' => 'Ø¹',
  'Û' => 'Øº',
  'Ü' => 'Ù€',
  'Ý' => 'Ù�',
  'Þ' => 'Ù‚',
  'ß' => 'Ùƒ',
  'à' => 'Ã ',
  'á' => 'Ù„',
  'â' => 'Ã¢',
  'ã' => 'Ù…',
  'ä' => 'Ù†',
  'å' => 'Ù‡',
  'æ' => 'Ùˆ',
  'ç' => 'Ã§',
  'è' => 'Ã¨',
  'é' => 'Ã©',
  'ê' => 'Ãª',
  'ë' => 'Ã«',
  'ì' => 'Ù‰',
  'í' => 'ÙŠ',
  'î' => 'Ã®',
  'ï' => 'Ã¯',
  'ð' => 'Ù‹',
  'ñ' => 'ÙŒ',
  'ò' => 'Ù�',
  'ó' => 'ÙŽ',
  'ô' => 'Ã´',
  'õ' => 'Ù�',
  'ö' => 'Ù�',
  '÷' => 'Ã·',
  'ø' => 'Ù‘',
  'ù' => 'Ã¹',
  'ú' => 'Ù’',
  'û' => 'Ã»',
  'ü' => 'Ã¼',
  'ý' => 'â€Ž',
  'þ' => 'â€�',
  'ÿ' => 'Û’',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => '%',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
  '€' => 'â‚¬',
  '‚' => 'â€š',
  'ƒ' => 'Æ’',
  '„' => 'â€ž',
  '…' => 'â€¦',
  '†' => 'â€ ',
  '‡' => 'â€¡',
  'ˆ' => 'Ë†',
  '‰' => 'â€°',
  '‹' => 'â€¹',
  '‘' => 'â€˜',
  '’' => 'â€™',
  '“' => 'â€œ',
  '”' => 'â€�',
  '•' => 'â€¢',
  '–' => 'â€“',
  '—' => 'â€”',
  '˜' => 'Ëœ',
  '™' => 'â„¢',
  '›' => 'â€º',
  ' ' => 'Â ',
  '¡' => 'Â¡',
  '¢' => 'Â¢',
  '£' => 'Â£',
  '¤' => 'â‚ª',
  '¥' => 'Â¥',
  '¦' => 'Â¦',
  '§' => 'Â§',
  '¨' => 'Â¨',
  '©' => 'Â©',
  'ª' => 'Ã—',
  '«' => 'Â«',
  '¬' => 'Â¬',
  '­' => 'Â­',
  '®' => 'Â®',
  '¯' => 'Â¯',
  '°' => 'Â°',
  '±' => 'Â±',
  '²' => 'Â²',
  '³' => 'Â³',
  '´' => 'Â´',
  'µ' => 'Âµ',
  '¶' => 'Â¶',
  '·' => 'Â·',
  '¸' => 'Â¸',
  '¹' => 'Â¹',
  'º' => 'Ã·',
  '»' => 'Â»',
  '¼' => 'Â¼',
  '½' => 'Â½',
  '¾' => 'Â¾',
  '¿' => 'Â¿',
  'À' => 'Ö°',
  'Á' => 'Ö±',
  'Â' => 'Ö²',
  'Ã' => 'Ö³',
  'Ä' => 'Ö´',
  'Å' => 'Öµ',
  'Æ' => 'Ö¶',
  'Ç' => 'Ö·',
  'È' => 'Ö¸',
  'É' => 'Ö¹',
  'Ë' => 'Ö»',
  'Ì' => 'Ö¼',
  'Í' => 'Ö½',
  'Î' => 'Ö¾',
  'Ï' => 'Ö¿',
  'Ð' => '×€',
  'Ñ' => '×�',
  'Ò' => '×‚',
  'Ó' => '×ƒ',
  'Ô' => '×°',
  'Õ' => '×±',
  'Ö' => '×²',
  '×' => '×³',
  'Ø' => '×´',
  'à' => '×�',
  'á' => '×‘',
  'â' => '×’',
  'ã' => '×“',
  'ä' => '×”',
  'å' => '×•',
  'æ' => '×–',
  'ç' => '×—',
  'è' => '×˜',
  'é' => '×™',
  'ê' => '×š',
  'ë' => '×›',
  'ì' => '×œ',
  'í' => '×�',
  'î' => '×ž',
  'ï' => '×Ÿ',
  'ð' => '× ',
  'ñ' => '×¡',
  'ò' => '×¢',
  'ó' => '×£',
  'ô' => '×¤',
  'õ' => '×¥',
  'ö' => '×¦',
  '÷' => '×§',
  'ø' => '×¨',
  'ù' => '×©',
  'ú' => '×ª',
  'ý' => 'â€Ž',
  'þ' => 'â€�',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => '%',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
  '€' => 'â‚¬',
  '‚' => 'â€š',
  '„' => 'â€ž',
  '…' => 'â€¦',
  '†' => 'â€ ',
  '‡' => 'â€¡',
  '‰' => 'â€°',
  '‹' => 'â€¹',
  '�' => 'Â¨',
  'Ž' => 'Ë‡',
  '�' => 'Â¸',
  '‘' => 'â€˜',
  '’' => 'â€™',
  '“' => 'â€œ',
  '”' => 'â€�',
  '•' => 'â€¢',
  '–' => 'â€“',
  '—' => 'â€”',
  '™' => 'â„¢',
  '›' => 'â€º',
  '�' => 'Â¯',
  'ž' => 'Ë›',
  ' ' => 'Â ',
  '¢' => 'Â¢',
  '£' => 'Â£',
  '¤' => 'Â¤',
  '¦' => 'Â¦',
  '§' => 'Â§',
  '¨' => 'Ã˜',
  '©' => 'Â©',
  'ª' => 'Å–',
  '«' => 'Â«',
  '¬' => 'Â¬',
  '­' => 'Â­',
  '®' => 'Â®',
  '¯' => 'Ã†',
  '°' => 'Â°',
  '±' => 'Â±',
  '²' => 'Â²',
  '³' => 'Â³',
  '´' => 'Â´',
  'µ' => 'Âµ',
  '¶' => 'Â¶',
  '·' => 'Â·',
  '¸' => 'Ã¸',
  '¹' => 'Â¹',
  'º' => 'Å—',
  '»' => 'Â»',
  '¼' => 'Â¼',
  '½' => 'Â½',
  '¾' => 'Â¾',
  '¿' => 'Ã¦',
  'À' => 'Ä„',
  'Á' => 'Ä®',
  'Â' => 'Ä€',
  'Ã' => 'Ä†',
  'Ä' => 'Ã„',
  'Å' => 'Ã…',
  'Æ' => 'Ä˜',
  'Ç' => 'Ä’',
  'È' => 'ÄŒ',
  'É' => 'Ã‰',
  'Ê' => 'Å¹',
  'Ë' => 'Ä–',
  'Ì' => 'Ä¢',
  'Í' => 'Ä¶',
  'Î' => 'Äª',
  'Ï' => 'Ä»',
  'Ð' => 'Å ',
  'Ñ' => 'Åƒ',
  'Ò' => 'Å…',
  'Ó' => 'Ã“',
  'Ô' => 'ÅŒ',
  'Õ' => 'Ã•',
  'Ö' => 'Ã–',
  '×' => 'Ã—',
  'Ø' => 'Å²',
  'Ù' => 'Å�',
  'Ú' => 'Åš',
  'Û' => 'Åª',
  'Ü' => 'Ãœ',
  'Ý' => 'Å»',
  'Þ' => 'Å½',
  'ß' => 'ÃŸ',
  'à' => 'Ä…',
  'á' => 'Ä¯',
  'â' => 'Ä�',
  'ã' => 'Ä‡',
  'ä' => 'Ã¤',
  'å' => 'Ã¥',
  'æ' => 'Ä™',
  'ç' => 'Ä“',
  'è' => 'Ä�',
  'é' => 'Ã©',
  'ê' => 'Åº',
  'ë' => 'Ä—',
  'ì' => 'Ä£',
  'í' => 'Ä·',
  'î' => 'Ä«',
  'ï' => 'Ä¼',
  'ð' => 'Å¡',
  'ñ' => 'Å„',
  'ò' => 'Å†',
  'ó' => 'Ã³',
  'ô' => 'Å�',
  'õ' => 'Ãµ',
  'ö' => 'Ã¶',
  '÷' => 'Ã·',
  'ø' => 'Å³',
  'ù' => 'Å‚',
  'ú' => 'Å›',
  'û' => 'Å«',
  'ü' => 'Ã¼',
  'ý' => 'Å¼',
  'þ' => 'Å¾',
  'ÿ' => 'Ë™',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => '%',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
  '€' => 'Â€',
  '�' => 'Â�',
  '‚' => 'Â‚',
  'ƒ' => 'Âƒ',
  '„' => 'Â„',
  '…' => 'Â…',
  '†' => 'Â†',
  '‡' => 'Â‡',
  'ˆ' => 'Âˆ',
  '‰' => 'Â‰',
  'Š' => 'ÂŠ',
  '‹' => 'Â‹',
  'Œ' => 'ÂŒ',
  '�' => 'Â�',
  'Ž' => 'ÂŽ',
  '�' => 'Â�',
  '�' => 'Â�',
  '‘' => 'Â‘',
  '’' => 'Â’',
  '“' => 'Â“',
  '”' => 'Â”',
  '•' => 'Â•',
  '–' => 'Â–',
  '—' => 'Â—',
  '˜' => 'Â˜',
  '™' => 'Â™',
  'š' => 'Âš',
  '›' => 'Â›',
  'œ' => 'Âœ',
  '�' => 'Â�',
  'ž' => 'Âž',
  'Ÿ' => 'ÂŸ',
  ' ' => 'Â ',
  '¡' => 'â€�',
  '¢' => 'Â¢',
  '£' => 'Â£',
  '¤' => 'Â¤',
  '¥' => 'â€ž',
  '¦' => 'Â¦',
  '§' => 'Â§',
  '¨' => 'Ã˜',
  '©' => 'Â©',
  'ª' => 'Å–',
  '«' => 'Â«',
  '¬' => 'Â¬',
  '­' => 'Â­',
  '®' => 'Â®',
  '¯' => 'Ã†',
  '°' => 'Â°',
  '±' => 'Â±',
  '²' => 'Â²',
  '³' => 'Â³',
  '´' => 'â€œ',
  'µ' => 'Âµ',
  '¶' => 'Â¶',
  '·' => 'Â·',
  '¸' => 'Ã¸',
  '¹' => 'Â¹',
  'º' => 'Å—',
  '»' => 'Â»',
  '¼' => 'Â¼',
  '½' => 'Â½',
  '¾' => 'Â¾',
  '¿' => 'Ã¦',
  'À' => 'Ä„',
  'Á' => 'Ä®',
  'Â' => 'Ä€',
  'Ã' => 'Ä†',
  'Ä' => 'Ã„',
  'Å' => 'Ã…',
  'Æ' => 'Ä˜',
  'Ç' => 'Ä’',
  'È' => 'ÄŒ',
  'É' => 'Ã‰',
  'Ê' => 'Å¹',
  'Ë' => 'Ä–',
  'Ì' => 'Ä¢',
  'Í' => 'Ä¶',
  'Î' => 'Äª',
  'Ï' => 'Ä»',
  'Ð' => 'Å ',
  'Ñ' => 'Åƒ',
  'Ò' => 'Å…',
  'Ó' => 'Ã“',
  'Ô' => 'ÅŒ',
  'Õ' => 'Ã•',
  'Ö' => 'Ã–',
  '×' => 'Ã—',
  'Ø' => 'Å²',
  'Ù' => 'Å�',
  'Ú' => 'Åš',
  'Û' => 'Åª',
  'Ü' => 'Ãœ',
  'Ý' => 'Å»',
  'Þ' => 'Å½',
  'ß' => 'ÃŸ',
  'à' => 'Ä…',
  'á' => 'Ä¯',
  'â' => 'Ä�',
  'ã' => 'Ä‡',
  'ä' => 'Ã¤',
  'å' => 'Ã¥',
  'æ' => 'Ä™',
  'ç' => 'Ä“',
  'è' => 'Ä�',
  'é' => 'Ã©',
  'ê' => 'Åº',
  'ë' => 'Ä—',
  'ì' => 'Ä£',
  'í' => 'Ä·',
  'î' => 'Ä«',
  'ï' => 'Ä¼',
  'ð' => 'Å¡',
  'ñ' => 'Å„',
  'ò' => 'Å†',
  'ó' => 'Ã³',
  'ô' => 'Å�',
  'õ' => 'Ãµ',
  'ö' => 'Ã¶',
  '÷' => 'Ã·',
  'ø' => 'Å³',
  'ù' => 'Å‚',
  'ú' => 'Å›',
  'û' => 'Å«',
  'ü' => 'Ã¼',
  'ý' => 'Å¼',
  'þ' => 'Å¾',
  'ÿ' => 'â€™',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => '%',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
  '€' => 'Â€',
  '�' => 'Â�',
  '‚' => 'Â‚',
  'ƒ' => 'Âƒ',
  '„' => 'Â„',
  '…' => 'Â…',
  '†' => 'Â†',
  '‡' => 'Â‡',
  'ˆ' => 'Âˆ',
  '‰' => 'Â‰',
  'Š' => 'ÂŠ',
  '‹' => 'Â‹',
  'Œ' => 'ÂŒ',
  '�' => 'Â�',
  'Ž' => 'ÂŽ',
  '�' => 'Â�',
  '�' => 'Â�',
  '‘' => 'Â‘',
  '’' => 'Â’',
  '“' => 'Â“',
  '”' => 'Â”',
  '•' => 'Â•',
  '–' => 'Â–',
  '—' => 'Â—',
  '˜' => 'Â˜',
  '™' => 'Â™',
  'š' => 'Âš',
  '›' => 'Â›',
  'œ' => 'Âœ',
  '�' => 'Â�',
  'ž' => 'Âž',
  'Ÿ' => 'ÂŸ',
  ' ' => 'Â ',
  '¡' => 'Â¡',
  '¢' => 'Â¢',
  '£' => 'Â£',
  '¤' => 'Â¤',
  '¥' => 'Â¥',
  '¦' => 'Â¦',
  '§' => 'Â§',
  '¨' => 'Â¨',
  '©' => 'Â©',
  'ª' => 'Âª',
  '«' => 'Â«',
  '¬' => 'Â¬',
  '­' => 'Â­',
  '®' => 'Â®',
  '¯' => 'Â¯',
  '°' => 'Â°',
  '±' => 'Â±',
  '²' => 'Â²',
  '³' => 'Â³',
  '´' => 'Â´',
  'µ' => 'Âµ',
  '¶' => 'Â¶',
  '·' => 'Â·',
  '¸' => 'Â¸',
  '¹' => 'Â¹',
  'º' => 'Âº',
  '»' => 'Â»',
  '¼' => 'Â¼',
  '½' => 'Â½',
  '¾' => 'Â¾',
  '¿' => 'Â¿',
  'À' => 'Ã€',
  'Á' => 'Ã�',
  'Â' => 'Ã‚',
  'Ã' => 'Ãƒ',
  'Ä' => 'Ã„',
  'Å' => 'Ã…',
  'Æ' => 'Ã†',
  'Ç' => 'Ã‡',
  'È' => 'Ãˆ',
  'É' => 'Ã‰',
  'Ê' => 'ÃŠ',
  'Ë' => 'Ã‹',
  'Ì' => 'ÃŒ',
  'Í' => 'Ã�',
  'Î' => 'ÃŽ',
  'Ï' => 'Ã�',
  'Ð' => 'Ã�',
  'Ñ' => 'Ã‘',
  'Ò' => 'Ã’',
  'Ó' => 'Ã“',
  'Ô' => 'Ã”',
  'Õ' => 'Ã•',
  'Ö' => 'Ã–',
  '×' => 'Ã—',
  'Ø' => 'Ã˜',
  'Ù' => 'Ã™',
  'Ú' => 'Ãš',
  'Û' => 'Ã›',
  'Ü' => 'Ãœ',
  'Ý' => 'Ã�',
  'Þ' => 'Ãž',
  'ß' => 'ÃŸ',
  'à' => 'Ã ',
  'á' => 'Ã¡',
  'â' => 'Ã¢',
  'ã' => 'Ã£',
  'ä' => 'Ã¤',
  'å' => 'Ã¥',
  'æ' => 'Ã¦',
  'ç' => 'Ã§',
  'è' => 'Ã¨',
  'é' => 'Ã©',
  'ê' => 'Ãª',
  'ë' => 'Ã«',
  'ì' => 'Ã¬',
  'í' => 'Ã­',
  'î' => 'Ã®',
  'ï' => 'Ã¯',
  'ð' => 'Ã°',
  'ñ' => 'Ã±',
  'ò' => 'Ã²',
  'ó' => 'Ã³',
  'ô' => 'Ã´',
  'õ' => 'Ãµ',
  'ö' => 'Ã¶',
  '÷' => 'Ã·',
  'ø' => 'Ã¸',
  'ù' => 'Ã¹',
  'ú' => 'Ãº',
  'û' => 'Ã»',
  'ü' => 'Ã¼',
  'ý' => 'Ã½',
  'þ' => 'Ã¾',
  'ÿ' => 'Ã¿',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => '%',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
  '€' => 'Ã‡',
  '�' => 'Ã¼',
  '‚' => 'Ã©',
  'ƒ' => 'Ã¢',
  '„' => 'Ã‚',
  '…' => 'Ã ',
  '†' => 'Â¶',
  '‡' => 'Ã§',
  'ˆ' => 'Ãª',
  '‰' => 'Ã«',
  'Š' => 'Ã¨',
  '‹' => 'Ã¯',
  'Œ' => 'Ã®',
  '�' => 'â€—',
  'Ž' => 'Ã€',
  '�' => 'Â§',
  '�' => 'Ã‰',
  '‘' => 'Ãˆ',
  '’' => 'ÃŠ',
  '“' => 'Ã´',
  '”' => 'Ã‹',
  '•' => 'Ã�',
  '–' => 'Ã»',
  '—' => 'Ã¹',
  '˜' => 'Â¤',
  '™' => 'Ã”',
  'š' => 'Ãœ',
  '›' => 'Â¢',
  'œ' => 'Â£',
  '�' => 'Ã™',
  'ž' => 'Ã›',
  'Ÿ' => 'Æ’',
  ' ' => 'Â¦',
  '¡' => 'Â´',
  '¢' => 'Ã³',
  '£' => 'Ãº',
  '¤' => 'Â¨',
  '¥' => 'Â¸',
  '¦' => 'Â³',
  '§' => 'Â¯',
  '¨' => 'ÃŽ',
  '©' => 'âŒ�',
  'ª' => 'Â¬',
  '«' => 'Â½',
  '¬' => 'Â¼',
  '­' => 'Â¾',
  '®' => 'Â«',
  '¯' => 'Â»',
  '°' => 'â–‘',
  '±' => 'â–’',
  '²' => 'â–“',
  '³' => 'â”‚',
  '´' => 'â”¤',
  'µ' => 'â•¡',
  '¶' => 'â•¢',
  '·' => 'â•–',
  '¸' => 'â••',
  '¹' => 'â•£',
  'º' => 'â•‘',
  '»' => 'â•—',
  '¼' => 'â•�',
  '½' => 'â•œ',
  '¾' => 'â•›',
  '¿' => 'â”�',
  'À' => 'â””',
  'Á' => 'â”´',
  'Â' => 'â”¬',
  'Ã' => 'â”œ',
  'Ä' => 'â”€',
  'Å' => 'â”¼',
  'Æ' => 'â•ž',
  'Ç' => 'â•Ÿ',
  'È' => 'â•š',
  'É' => 'â•”',
  'Ê' => 'â•©',
  'Ë' => 'â•¦',
  'Ì' => 'â• ',
  'Í' => 'â•�',
  'Î' => 'â•¬',
  'Ï' => 'â•§',
  'Ð' => 'â•¨',
  'Ñ' => 'â•¤',
  'Ò' => 'â•¥',
  'Ó' => 'â•™',
  'Ô' => 'â•˜',
  'Õ' => 'â•’',
  'Ö' => 'â•“',
  '×' => 'â•«',
  'Ø' => 'â•ª',
  'Ù' => 'â”˜',
  'Ú' => 'â”Œ',
  'Û' => 'â–ˆ',
  'Ü' => 'â–„',
  'Ý' => 'â–Œ',
  'Þ' => 'â–�',
  'ß' => 'â–€',
  'à' => 'Î±',
  'á' => 'ÃŸ',
  'â' => 'Î“',
  'ã' => 'Ï€',
  'ä' => 'Î£',
  'å' => 'Ïƒ',
  'æ' => 'Âµ',
  'ç' => 'Ï„',
  'è' => 'Î¦',
  'é' => 'Î˜',
  'ê' => 'Î©',
  'ë' => 'Î´',
  'ì' => 'âˆž',
  'í' => 'Ï†',
  'î' => 'Îµ',
  'ï' => 'âˆ©',
  'ð' => 'â‰¡',
  'ñ' => 'Â±',
  'ò' => 'â‰¥',
  'ó' => 'â‰¤',
  'ô' => 'âŒ ',
  'õ' => 'âŒ¡',
  'ö' => 'Ã·',
  '÷' => 'â‰ˆ',
  'ø' => 'Â°',
  'ù' => 'âˆ™',
  'ú' => 'Â·',
  'û' => 'âˆš',
  'ü' => 'â�¿',
  'ý' => 'Â²',
  'þ' => 'â– ',
  'ÿ' => 'Â ',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => '%',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
  '�A' => 'ê°‚',
  '�B' => 'ê°ƒ',
  '�C' => 'ê°…',
  '�D' => 'ê°†',
  '�E' => 'ê°‹',
  '�F' => 'ê°Œ',
  '�G' => 'ê°�',
  '�H' => 'ê°Ž',
  '�I' => 'ê°�',
  '�J' => 'ê°˜',
  '�K' => 'ê°ž',
  '�L' => 'ê°Ÿ',
  '�M' => 'ê°¡',
  '�N' => 'ê°¢',
  '�O' => 'ê°£',
  '�P' => 'ê°¥',
  '�Q' => 'ê°¦',
  '�R' => 'ê°§',
  '�S' => 'ê°¨',
  '�T' => 'ê°©',
  '�U' => 'ê°ª',
  '�V' => 'ê°«',
  '�W' => 'ê°®',
  '�X' => 'ê°²',
  '�Y' => 'ê°³',
  '�Z' => 'ê°´',
  '�a' => 'ê°µ',
  '�b' => 'ê°¶',
  '�c' => 'ê°·',
  '�d' => 'ê°º',
  '�e' => 'ê°»',
  '�f' => 'ê°½',
  '�g' => 'ê°¾',
  '�h' => 'ê°¿',
  '�i' => 'ê±�',
  '�j' => 'ê±‚',
  '�k' => 'ê±ƒ',
  '�l' => 'ê±„',
  '�m' => 'ê±…',
  '�n' => 'ê±†',
  '�o' => 'ê±‡',
  '�p' => 'ê±ˆ',
  '�q' => 'ê±‰',
  '�r' => 'ê±Š',
  '�s' => 'ê±Œ',
  '�t' => 'ê±Ž',
  '�u' => 'ê±�',
  '�v' => 'ê±�',
  '�w' => 'ê±‘',
  '�x' => 'ê±’',
  '�y' => 'ê±“',
  '�z' => 'ê±•',
  '��' => 'ê±–',
  '�‚' => 'ê±—',
  '�ƒ' => 'ê±™',
  '�„' => 'ê±š',
  '�…' => 'ê±›',
  '�†' => 'ê±�',
  '�‡' => 'ê±ž',
  '�ˆ' => 'ê±Ÿ',
  '�‰' => 'ê± ',
  '�Š' => 'ê±¡',
  '�‹' => 'ê±¢',
  '�Œ' => 'ê±£',
  '��' => 'ê±¤',
  '�Ž' => 'ê±¥',
  '��' => 'ê±¦',
  '��' => 'ê±§',
  '�‘' => 'ê±¨',
  '�’' => 'ê±©',
  '�“' => 'ê±ª',
  '�”' => 'ê±«',
  '�•' => 'ê±¬',
  '�–' => 'ê±­',
  '�—' => 'ê±®',
  '�˜' => 'ê±¯',
  '�™' => 'ê±²',
  '�š' => 'ê±³',
  '�›' => 'ê±µ',
  '�œ' => 'ê±¶',
  '��' => 'ê±¹',
  '�ž' => 'ê±»',
  '�Ÿ' => 'ê±¼',
  '� ' => 'ê±½',
  '�¡' => 'ê±¾',
  '�¢' => 'ê±¿',
  '�£' => 'ê²‚',
  '�¤' => 'ê²‡',
  '�¥' => 'ê²ˆ',
  '�¦' => 'ê²�',
  '�§' => 'ê²Ž',
  '�¨' => 'ê²�',
  '�©' => 'ê²‘',
  '�ª' => 'ê²’',
  '�«' => 'ê²“',
  '�¬' => 'ê²•',
  '�­' => 'ê²–',
  '�®' => 'ê²—',
  '�¯' => 'ê²˜',
  '�°' => 'ê²™',
  '�±' => 'ê²š',
  '�²' => 'ê²›',
  '�³' => 'ê²ž',
  '�´' => 'ê²¢',
  '�µ' => 'ê²£',
  '�¶' => 'ê²¤',
  '�·' => 'ê²¥',
  '�¸' => 'ê²¦',
  '�¹' => 'ê²§',
  '�º' => 'ê²«',
  '�»' => 'ê²­',
  '�¼' => 'ê²®',
  '�½' => 'ê²±',
  '�¾' => 'ê²²',
  '�¿' => 'ê²³',
  '�À' => 'ê²´',
  '�Á' => 'ê²µ',
  '�Â' => 'ê²¶',
  '�Ã' => 'ê²·',
  '�Ä' => 'ê²º',
  '�Å' => 'ê²¾',
  '�Æ' => 'ê²¿',
  '�Ç' => 'ê³€',
  '�È' => 'ê³‚',
  '�É' => 'ê³ƒ',
  '�Ê' => 'ê³…',
  '�Ë' => 'ê³†',
  '�Ì' => 'ê³‡',
  '�Í' => 'ê³‰',
  '�Î' => 'ê³Š',
  '�Ï' => 'ê³‹',
  '�Ð' => 'ê³�',
  '�Ñ' => 'ê³Ž',
  '�Ò' => 'ê³�',
  '�Ó' => 'ê³�',
  '�Ô' => 'ê³‘',
  '�Õ' => 'ê³’',
  '�Ö' => 'ê³“',
  '�×' => 'ê³”',
  '�Ø' => 'ê³–',
  '�Ù' => 'ê³˜',
  '�Ú' => 'ê³™',
  '�Û' => 'ê³š',
  '�Ü' => 'ê³›',
  '�Ý' => 'ê³œ',
  '�Þ' => 'ê³�',
  '�ß' => 'ê³ž',
  '�à' => 'ê³Ÿ',
  '�á' => 'ê³¢',
  '�â' => 'ê³£',
  '�ã' => 'ê³¥',
  '�ä' => 'ê³¦',
  '�å' => 'ê³©',
  '�æ' => 'ê³«',
  '�ç' => 'ê³­',
  '�è' => 'ê³®',
  '�é' => 'ê³²',
  '�ê' => 'ê³´',
  '�ë' => 'ê³·',
  '�ì' => 'ê³¸',
  '�í' => 'ê³¹',
  '�î' => 'ê³º',
  '�ï' => 'ê³»',
  '�ð' => 'ê³¾',
  '�ñ' => 'ê³¿',
  '�ò' => 'ê´�',
  '�ó' => 'ê´‚',
  '�ô' => 'ê´ƒ',
  '�õ' => 'ê´…',
  '�ö' => 'ê´‡',
  '�÷' => 'ê´ˆ',
  '�ø' => 'ê´‰',
  '�ù' => 'ê´Š',
  '�ú' => 'ê´‹',
  '�û' => 'ê´Ž',
  '�ü' => 'ê´�',
  '�ý' => 'ê´’',
  '�þ' => 'ê´“',
  '‚A' => 'ê´”',
  '‚B' => 'ê´•',
  '‚C' => 'ê´–',
  '‚D' => 'ê´—',
  '‚E' => 'ê´™',
  '‚F' => 'ê´š',
  '‚G' => 'ê´›',
  '‚H' => 'ê´�',
  '‚I' => 'ê´ž',
  '‚J' => 'ê´Ÿ',
  '‚K' => 'ê´¡',
  '‚L' => 'ê´¢',
  '‚M' => 'ê´£',
  '‚N' => 'ê´¤',
  '‚O' => 'ê´¥',
  '‚P' => 'ê´¦',
  '‚Q' => 'ê´§',
  '‚R' => 'ê´¨',
  '‚S' => 'ê´ª',
  '‚T' => 'ê´«',
  '‚U' => 'ê´®',
  '‚V' => 'ê´¯',
  '‚W' => 'ê´°',
  '‚X' => 'ê´±',
  '‚Y' => 'ê´²',
  '‚Z' => 'ê´³',
  '‚a' => 'ê´¶',
  '‚b' => 'ê´·',
  '‚c' => 'ê´¹',
  '‚d' => 'ê´º',
  '‚e' => 'ê´»',
  '‚f' => 'ê´½',
  '‚g' => 'ê´¾',
  '‚h' => 'ê´¿',
  '‚i' => 'êµ€',
  '‚j' => 'êµ�',
  '‚k' => 'êµ‚',
  '‚l' => 'êµƒ',
  '‚m' => 'êµ†',
  '‚n' => 'êµˆ',
  '‚o' => 'êµŠ',
  '‚p' => 'êµ‹',
  '‚q' => 'êµŒ',
  '‚r' => 'êµ�',
  '‚s' => 'êµŽ',
  '‚t' => 'êµ�',
  '‚u' => 'êµ‘',
  '‚v' => 'êµ’',
  '‚w' => 'êµ“',
  '‚x' => 'êµ•',
  '‚y' => 'êµ–',
  '‚z' => 'êµ—',
  '‚�' => 'êµ™',
  '‚‚' => 'êµš',
  '‚ƒ' => 'êµ›',
  '‚„' => 'êµœ',
  '‚…' => 'êµ�',
  '‚†' => 'êµž',
  '‚‡' => 'êµŸ',
  '‚ˆ' => 'êµ ',
  '‚‰' => 'êµ¢',
  '‚Š' => 'êµ¤',
  '‚‹' => 'êµ¥',
  '‚Œ' => 'êµ¦',
  '‚�' => 'êµ§',
  '‚Ž' => 'êµ¨',
  '‚�' => 'êµ©',
  '‚�' => 'êµª',
  '‚‘' => 'êµ«',
  '‚’' => 'êµ®',
  '‚“' => 'êµ¯',
  '‚”' => 'êµ±',
  '‚•' => 'êµ²',
  '‚–' => 'êµ·',
  '‚—' => 'êµ¸',
  '‚˜' => 'êµ¹',
  '‚™' => 'êµº',
  '‚š' => 'êµ¾',
  '‚›' => 'ê¶€',
  '‚œ' => 'ê¶ƒ',
  '‚�' => 'ê¶„',
  '‚ž' => 'ê¶…',
  '‚Ÿ' => 'ê¶†',
  '‚ ' => 'ê¶‡',
  '‚¡' => 'ê¶Š',
  '‚¢' => 'ê¶‹',
  '‚£' => 'ê¶�',
  '‚¤' => 'ê¶Ž',
  '‚¥' => 'ê¶�',
  '‚¦' => 'ê¶‘',
  '‚§' => 'ê¶’',
  '‚¨' => 'ê¶“',
  '‚©' => 'ê¶”',
  '‚ª' => 'ê¶•',
  '‚«' => 'ê¶–',
  '‚¬' => 'ê¶—',
  '‚­' => 'ê¶˜',
  '‚®' => 'ê¶™',
  '‚¯' => 'ê¶š',
  '‚°' => 'ê¶›',
  '‚±' => 'ê¶ž',
  '‚²' => 'ê¶Ÿ',
  '‚³' => 'ê¶ ',
  '‚´' => 'ê¶¡',
  '‚µ' => 'ê¶¢',
  '‚¶' => 'ê¶£',
  '‚·' => 'ê¶¥',
  '‚¸' => 'ê¶¦',
  '‚¹' => 'ê¶§',
  '‚º' => 'ê¶¨',
  '‚»' => 'ê¶©',
  '‚¼' => 'ê¶ª',
  '‚½' => 'ê¶«',
  '‚¾' => 'ê¶¬',
  '‚¿' => 'ê¶­',
  '‚À' => 'ê¶®',
  '‚Á' => 'ê¶¯',
  '‚Â' => 'ê¶°',
  '‚Ã' => 'ê¶±',
  '‚Ä' => 'ê¶²',
  '‚Å' => 'ê¶³',
  '‚Æ' => 'ê¶´',
  '‚Ç' => 'ê¶µ',
  '‚È' => 'ê¶¶',
  '‚É' => 'ê¶¸',
  '‚Ê' => 'ê¶¹',
  '‚Ë' => 'ê¶º',
  '‚Ì' => 'ê¶»',
  '‚Í' => 'ê¶¼',
  '‚Î' => 'ê¶½',
  '‚Ï' => 'ê¶¾',
  '‚Ð' => 'ê¶¿',
  '‚Ñ' => 'ê·‚',
  '‚Ò' => 'ê·ƒ',
  '‚Ó' => 'ê·…',
  '‚Ô' => 'ê·†',
  '‚Õ' => 'ê·‡',
  '‚Ö' => 'ê·‰',
  '‚×' => 'ê·Š',
  '‚Ø' => 'ê·‹',
  '‚Ù' => 'ê·Œ',
  '‚Ú' => 'ê·�',
  '‚Û' => 'ê·Ž',
  '‚Ü' => 'ê·�',
  '‚Ý' => 'ê·’',
  '‚Þ' => 'ê·”',
  '‚ß' => 'ê·•',
  '‚à' => 'ê·–',
  '‚á' => 'ê·—',
  '‚â' => 'ê·˜',
  '‚ã' => 'ê·™',
  '‚ä' => 'ê·š',
  '‚å' => 'ê·›',
  '‚æ' => 'ê·�',
  '‚ç' => 'ê·ž',
  '‚è' => 'ê·Ÿ',
  '‚é' => 'ê·¡',
  '‚ê' => 'ê·¢',
  '‚ë' => 'ê·£',
  '‚ì' => 'ê·¥',
  '‚í' => 'ê·¦',
  '‚î' => 'ê·§',
  '‚ï' => 'ê·¨',
  '‚ð' => 'ê·©',
  '‚ñ' => 'ê·ª',
  '‚ò' => 'ê·«',
  '‚ó' => 'ê·¬',
  '‚ô' => 'ê·­',
  '‚õ' => 'ê·®',
  '‚ö' => 'ê·¯',
  '‚÷' => 'ê·°',
  '‚ø' => 'ê·±',
  '‚ù' => 'ê·²',
  '‚ú' => 'ê·³',
  '‚û' => 'ê·´',
  '‚ü' => 'ê·µ',
  '‚ý' => 'ê·¶',
  '‚þ' => 'ê··',
  'ƒA' => 'ê·º',
  'ƒB' => 'ê·»',
  'ƒC' => 'ê·½',
  'ƒD' => 'ê·¾',
  'ƒE' => 'ê¸‚',
  'ƒF' => 'ê¸ƒ',
  'ƒG' => 'ê¸„',
  'ƒH' => 'ê¸…',
  'ƒI' => 'ê¸†',
  'ƒJ' => 'ê¸‡',
  'ƒK' => 'ê¸Š',
  'ƒL' => 'ê¸Œ',
  'ƒM' => 'ê¸Ž',
  'ƒN' => 'ê¸�',
  'ƒO' => 'ê¸�',
  'ƒP' => 'ê¸‘',
  'ƒQ' => 'ê¸’',
  'ƒR' => 'ê¸“',
  'ƒS' => 'ê¸•',
  'ƒT' => 'ê¸–',
  'ƒU' => 'ê¸—',
  'ƒV' => 'ê¸˜',
  'ƒW' => 'ê¸™',
  'ƒX' => 'ê¸š',
  'ƒY' => 'ê¸›',
  'ƒZ' => 'ê¸œ',
  'ƒa' => 'ê¸�',
  'ƒb' => 'ê¸ž',
  'ƒc' => 'ê¸Ÿ',
  'ƒd' => 'ê¸ ',
  'ƒe' => 'ê¸¡',
  'ƒf' => 'ê¸¢',
  'ƒg' => 'ê¸£',
  'ƒh' => 'ê¸¤',
  'ƒi' => 'ê¸¥',
  'ƒj' => 'ê¸¦',
  'ƒk' => 'ê¸§',
  'ƒl' => 'ê¸¨',
  'ƒm' => 'ê¸©',
  'ƒn' => 'ê¸ª',
  'ƒo' => 'ê¸«',
  'ƒp' => 'ê¸¬',
  'ƒq' => 'ê¸­',
  'ƒr' => 'ê¸®',
  'ƒs' => 'ê¸¯',
  'ƒt' => 'ê¸²',
  'ƒu' => 'ê¸³',
  'ƒv' => 'ê¸µ',
  'ƒw' => 'ê¸¶',
  'ƒx' => 'ê¸¹',
  'ƒy' => 'ê¸»',
  'ƒz' => 'ê¸¼',
  'ƒ�' => 'ê¸½',
  'ƒ‚' => 'ê¸¾',
  'ƒƒ' => 'ê¸¿',
  'ƒ„' => 'ê¹‚',
  'ƒ…' => 'ê¹„',
  'ƒ†' => 'ê¹‡',
  'ƒ‡' => 'ê¹ˆ',
  'ƒˆ' => 'ê¹‰',
  'ƒ‰' => 'ê¹‹',
  'ƒŠ' => 'ê¹�',
  'ƒ‹' => 'ê¹‘',
  'ƒŒ' => 'ê¹’',
  'ƒ�' => 'ê¹“',
  'ƒŽ' => 'ê¹•',
  'ƒ�' => 'ê¹—',
  'ƒ�' => 'ê¹˜',
  'ƒ‘' => 'ê¹™',
  'ƒ’' => 'ê¹š',
  'ƒ“' => 'ê¹›',
  'ƒ”' => 'ê¹ž',
  'ƒ•' => 'ê¹¢',
  'ƒ–' => 'ê¹£',
  'ƒ—' => 'ê¹¤',
  'ƒ˜' => 'ê¹¦',
  'ƒ™' => 'ê¹§',
  'ƒš' => 'ê¹ª',
  'ƒ›' => 'ê¹«',
  'ƒœ' => 'ê¹­',
  'ƒ�' => 'ê¹®',
  'ƒž' => 'ê¹¯',
  'ƒŸ' => 'ê¹±',
  'ƒ ' => 'ê¹²',
  'ƒ¡' => 'ê¹³',
  'ƒ¢' => 'ê¹´',
  'ƒ£' => 'ê¹µ',
  'ƒ¤' => 'ê¹¶',
  'ƒ¥' => 'ê¹·',
  'ƒ¦' => 'ê¹º',
  'ƒ§' => 'ê¹¾',
  'ƒ¨' => 'ê¹¿',
  'ƒ©' => 'êº€',
  'ƒª' => 'êº�',
  'ƒ«' => 'êº‚',
  'ƒ¬' => 'êºƒ',
  'ƒ­' => 'êº†',
  'ƒ®' => 'êº‡',
  'ƒ¯' => 'êºˆ',
  'ƒ°' => 'êº‰',
  'ƒ±' => 'êºŠ',
  'ƒ²' => 'êº‹',
  'ƒ³' => 'êº�',
  'ƒ´' => 'êºŽ',
  'ƒµ' => 'êº�',
  'ƒ¶' => 'êº�',
  'ƒ·' => 'êº‘',
  'ƒ¸' => 'êº’',
  'ƒ¹' => 'êº“',
  'ƒº' => 'êº”',
  'ƒ»' => 'êº•',
  'ƒ¼' => 'êº–',
  'ƒ½' => 'êº—',
  'ƒ¾' => 'êº˜',
  'ƒ¿' => 'êº™',
  'ƒÀ' => 'êºš',
  'ƒÁ' => 'êº›',
  'ƒÂ' => 'êºœ',
  'ƒÃ' => 'êº�',
  'ƒÄ' => 'êºž',
  'ƒÅ' => 'êºŸ',
  'ƒÆ' => 'êº ',
  'ƒÇ' => 'êº¡',
  'ƒÈ' => 'êº¢',
  'ƒÉ' => 'êº£',
  'ƒÊ' => 'êº¤',
  'ƒË' => 'êº¥',
  'ƒÌ' => 'êº¦',
  'ƒÍ' => 'êº§',
  'ƒÎ' => 'êº¨',
  'ƒÏ' => 'êº©',
  'ƒÐ' => 'êºª',
  'ƒÑ' => 'êº«',
  'ƒÒ' => 'êº¬',
  'ƒÓ' => 'êº­',
  'ƒÔ' => 'êº®',
  'ƒÕ' => 'êº¯',
  'ƒÖ' => 'êº°',
  'ƒ×' => 'êº±',
  'ƒØ' => 'êº²',
  'ƒÙ' => 'êº³',
  'ƒÚ' => 'êº´',
  'ƒÛ' => 'êºµ',
  'ƒÜ' => 'êº¶',
  'ƒÝ' => 'êº·',
  'ƒÞ' => 'êº¸',
  'ƒß' => 'êº¹',
  'ƒà' => 'êºº',
  'ƒá' => 'êº»',
  'ƒâ' => 'êº¿',
  'ƒã' => 'ê»�',
  'ƒä' => 'ê»‚',
  'ƒå' => 'ê»ƒ',
  'ƒæ' => 'ê»…',
  'ƒç' => 'ê»†',
  'ƒè' => 'ê»‡',
  'ƒé' => 'ê»ˆ',
  'ƒê' => 'ê»‰',
  'ƒë' => 'ê»Š',
  'ƒì' => 'ê»‹',
  'ƒí' => 'ê»Ž',
  'ƒî' => 'ê»’',
  'ƒï' => 'ê»“',
  'ƒð' => 'ê»”',
  'ƒñ' => 'ê»•',
  'ƒò' => 'ê»–',
  'ƒó' => 'ê»—',
  'ƒô' => 'ê»š',
  'ƒõ' => 'ê»›',
  'ƒö' => 'ê»�',
  'ƒ÷' => 'ê»ž',
  'ƒø' => 'ê»Ÿ',
  'ƒù' => 'ê» ',
  'ƒú' => 'ê»¡',
  'ƒû' => 'ê»¢',
  'ƒü' => 'ê»£',
  'ƒý' => 'ê»¤',
  'ƒþ' => 'ê»¥',
  '„A' => 'ê»¦',
  '„B' => 'ê»§',
  '„C' => 'ê»©',
  '„D' => 'ê»ª',
  '„E' => 'ê»¬',
  '„F' => 'ê»®',
  '„G' => 'ê»¯',
  '„H' => 'ê»°',
  '„I' => 'ê»±',
  '„J' => 'ê»²',
  '„K' => 'ê»³',
  '„L' => 'ê»µ',
  '„M' => 'ê»¶',
  '„N' => 'ê»·',
  '„O' => 'ê»¹',
  '„P' => 'ê»º',
  '„Q' => 'ê»»',
  '„R' => 'ê»½',
  '„S' => 'ê»¾',
  '„T' => 'ê»¿',
  '„U' => 'ê¼€',
  '„V' => 'ê¼�',
  '„W' => 'ê¼‚',
  '„X' => 'ê¼ƒ',
  '„Y' => 'ê¼„',
  '„Z' => 'ê¼…',
  '„a' => 'ê¼†',
  '„b' => 'ê¼‰',
  '„c' => 'ê¼Š',
  '„d' => 'ê¼‹',
  '„e' => 'ê¼Œ',
  '„f' => 'ê¼Ž',
  '„g' => 'ê¼�',
  '„h' => 'ê¼‘',
  '„i' => 'ê¼’',
  '„j' => 'ê¼“',
  '„k' => 'ê¼”',
  '„l' => 'ê¼•',
  '„m' => 'ê¼–',
  '„n' => 'ê¼—',
  '„o' => 'ê¼˜',
  '„p' => 'ê¼™',
  '„q' => 'ê¼š',
  '„r' => 'ê¼›',
  '„s' => 'ê¼œ',
  '„t' => 'ê¼�',
  '„u' => 'ê¼ž',
  '„v' => 'ê¼Ÿ',
  '„w' => 'ê¼ ',
  '„x' => 'ê¼¡',
  '„y' => 'ê¼¢',
  '„z' => 'ê¼£',
  '„�' => 'ê¼¤',
  '„‚' => 'ê¼¥',
  '„ƒ' => 'ê¼¦',
  '„„' => 'ê¼§',
  '„…' => 'ê¼¨',
  '„†' => 'ê¼©',
  '„‡' => 'ê¼ª',
  '„ˆ' => 'ê¼«',
  '„‰' => 'ê¼®',
  '„Š' => 'ê¼¯',
  '„‹' => 'ê¼±',
  '„Œ' => 'ê¼³',
  '„�' => 'ê¼µ',
  '„Ž' => 'ê¼¶',
  '„�' => 'ê¼·',
  '„�' => 'ê¼¸',
  '„‘' => 'ê¼¹',
  '„’' => 'ê¼º',
  '„“' => 'ê¼»',
  '„”' => 'ê¼¾',
  '„•' => 'ê½€',
  '„–' => 'ê½„',
  '„—' => 'ê½…',
  '„˜' => 'ê½†',
  '„™' => 'ê½‡',
  '„š' => 'ê½Š',
  '„›' => 'ê½‹',
  '„œ' => 'ê½Œ',
  '„�' => 'ê½�',
  '„ž' => 'ê½Ž',
  '„Ÿ' => 'ê½�',
  '„ ' => 'ê½‘',
  '„¡' => 'ê½’',
  '„¢' => 'ê½“',
  '„£' => 'ê½”',
  '„¤' => 'ê½•',
  '„¥' => 'ê½–',
  '„¦' => 'ê½—',
  '„§' => 'ê½˜',
  '„¨' => 'ê½™',
  '„©' => 'ê½š',
  '„ª' => 'ê½›',
  '„«' => 'ê½ž',
  '„¬' => 'ê½Ÿ',
  '„­' => 'ê½ ',
  '„®' => 'ê½¡',
  '„¯' => 'ê½¢',
  '„°' => 'ê½£',
  '„±' => 'ê½¦',
  '„²' => 'ê½§',
  '„³' => 'ê½¨',
  '„´' => 'ê½©',
  '„µ' => 'ê½ª',
  '„¶' => 'ê½«',
  '„·' => 'ê½¬',
  '„¸' => 'ê½­',
  '„¹' => 'ê½®',
  '„º' => 'ê½¯',
  '„»' => 'ê½°',
  '„¼' => 'ê½±',
  '„½' => 'ê½²',
  '„¾' => 'ê½³',
  '„¿' => 'ê½´',
  '„À' => 'ê½µ',
  '„Á' => 'ê½¶',
  '„Â' => 'ê½·',
  '„Ã' => 'ê½¸',
  '„Ä' => 'ê½º',
  '„Å' => 'ê½»',
  '„Æ' => 'ê½¼',
  '„Ç' => 'ê½½',
  '„È' => 'ê½¾',
  '„É' => 'ê½¿',
  '„Ê' => 'ê¾�',
  '„Ë' => 'ê¾‚',
  '„Ì' => 'ê¾ƒ',
  '„Í' => 'ê¾…',
  '„Î' => 'ê¾†',
  '„Ï' => 'ê¾‡',
  '„Ð' => 'ê¾‰',
  '„Ñ' => 'ê¾Š',
  '„Ò' => 'ê¾‹',
  '„Ó' => 'ê¾Œ',
  '„Ô' => 'ê¾�',
  '„Õ' => 'ê¾Ž',
  '„Ö' => 'ê¾�',
  '„×' => 'ê¾’',
  '„Ø' => 'ê¾“',
  '„Ù' => 'ê¾”',
  '„Ú' => 'ê¾–',
  '„Û' => 'ê¾—',
  '„Ü' => 'ê¾˜',
  '„Ý' => 'ê¾™',
  '„Þ' => 'ê¾š',
  '„ß' => 'ê¾›',
  '„à' => 'ê¾�',
  '„á' => 'ê¾ž',
  '„â' => 'ê¾Ÿ',
  '„ã' => 'ê¾ ',
  '„ä' => 'ê¾¡',
  '„å' => 'ê¾¢',
  '„æ' => 'ê¾£',
  '„ç' => 'ê¾¤',
  '„è' => 'ê¾¥',
  '„é' => 'ê¾¦',
  '„ê' => 'ê¾§',
  '„ë' => 'ê¾¨',
  '„ì' => 'ê¾©',
  '„í' => 'ê¾ª',
  '„î' => 'ê¾«',
  '„ï' => 'ê¾¬',
  '„ð' => 'ê¾­',
  '„ñ' => 'ê¾®',
  '„ò' => 'ê¾¯',
  '„ó' => 'ê¾°',
  '„ô' => 'ê¾±',
  '„õ' => 'ê¾²',
  '„ö' => 'ê¾³',
  '„÷' => 'ê¾´',
  '„ø' => 'ê¾µ',
  '„ù' => 'ê¾¶',
  '„ú' => 'ê¾·',
  '„û' => 'ê¾º',
  '„ü' => 'ê¾»',
  '„ý' => 'ê¾½',
  '„þ' => 'ê¾¾',
  '…A' => 'ê¾¿',
  '…B' => 'ê¿�',
  '…C' => 'ê¿‚',
  '…D' => 'ê¿ƒ',
  '…E' => 'ê¿„',
  '…F' => 'ê¿…',
  '…G' => 'ê¿†',
  '…H' => 'ê¿Š',
  '…I' => 'ê¿Œ',
  '…J' => 'ê¿�',
  '…K' => 'ê¿�',
  '…L' => 'ê¿‘',
  '…M' => 'ê¿’',
  '…N' => 'ê¿“',
  '…O' => 'ê¿•',
  '…P' => 'ê¿–',
  '…Q' => 'ê¿—',
  '…R' => 'ê¿˜',
  '…S' => 'ê¿™',
  '…T' => 'ê¿š',
  '…U' => 'ê¿›',
  '…V' => 'ê¿�',
  '…W' => 'ê¿ž',
  '…X' => 'ê¿Ÿ',
  '…Y' => 'ê¿ ',
  '…Z' => 'ê¿¡',
  '…a' => 'ê¿¢',
  '…b' => 'ê¿£',
  '…c' => 'ê¿¤',
  '…d' => 'ê¿¥',
  '…e' => 'ê¿¦',
  '…f' => 'ê¿§',
  '…g' => 'ê¿ª',
  '…h' => 'ê¿«',
  '…i' => 'ê¿¬',
  '…j' => 'ê¿­',
  '…k' => 'ê¿®',
  '…l' => 'ê¿¯',
  '…m' => 'ê¿²',
  '…n' => 'ê¿³',
  '…o' => 'ê¿µ',
  '…p' => 'ê¿¶',
  '…q' => 'ê¿·',
  '…r' => 'ê¿¹',
  '…s' => 'ê¿º',
  '…t' => 'ê¿»',
  '…u' => 'ê¿¼',
  '…v' => 'ê¿½',
  '…w' => 'ê¿¾',
  '…x' => 'ê¿¿',
  '…y' => 'ë€‚',
  '…z' => 'ë€ƒ',
  '…�' => 'ë€…',
  '…‚' => 'ë€†',
  '…ƒ' => 'ë€‡',
  '…„' => 'ë€ˆ',
  '……' => 'ë€‰',
  '…†' => 'ë€Š',
  '…‡' => 'ë€‹',
  '…ˆ' => 'ë€�',
  '…‰' => 'ë€Ž',
  '…Š' => 'ë€�',
  '…‹' => 'ë€‘',
  '…Œ' => 'ë€’',
  '…�' => 'ë€“',
  '…Ž' => 'ë€•',
  '…�' => 'ë€–',
  '…�' => 'ë€—',
  '…‘' => 'ë€˜',
  '…’' => 'ë€™',
  '…“' => 'ë€š',
  '…”' => 'ë€›',
  '…•' => 'ë€ž',
  '…–' => 'ë€Ÿ',
  '…—' => 'ë€ ',
  '…˜' => 'ë€¡',
  '…™' => 'ë€¢',
  '…š' => 'ë€£',
  '…›' => 'ë€¤',
  '…œ' => 'ë€¥',
  '…�' => 'ë€¦',
  '…ž' => 'ë€§',
  '…Ÿ' => 'ë€©',
  '… ' => 'ë€ª',
  '…¡' => 'ë€«',
  '…¢' => 'ë€¬',
  '…£' => 'ë€­',
  '…¤' => 'ë€®',
  '…¥' => 'ë€¯',
  '…¦' => 'ë€°',
  '…§' => 'ë€±',
  '…¨' => 'ë€²',
  '…©' => 'ë€³',
  '…ª' => 'ë€´',
  '…«' => 'ë€µ',
  '…¬' => 'ë€¶',
  '…­' => 'ë€·',
  '…®' => 'ë€¸',
  '…¯' => 'ë€¹',
  '…°' => 'ë€º',
  '…±' => 'ë€»',
  '…²' => 'ë€¼',
  '…³' => 'ë€½',
  '…´' => 'ë€¾',
  '…µ' => 'ë€¿',
  '…¶' => 'ë�€',
  '…·' => 'ë��',
  '…¸' => 'ë�‚',
  '…¹' => 'ë�ƒ',
  '…º' => 'ë�†',
  '…»' => 'ë�‡',
  '…¼' => 'ë�‰',
  '…½' => 'ë�‹',
  '…¾' => 'ë��',
  '…¿' => 'ë��',
  '…À' => 'ë��',
  '…Á' => 'ë�‘',
  '…Â' => 'ë�’',
  '…Ã' => 'ë�–',
  '…Ä' => 'ë�˜',
  '…Å' => 'ë�š',
  '…Æ' => 'ë�›',
  '…Ç' => 'ë�œ',
  '…È' => 'ë�ž',
  '…É' => 'ë�Ÿ',
  '…Ê' => 'ë� ',
  '…Ë' => 'ë�¡',
  '…Ì' => 'ë�¢',
  '…Í' => 'ë�£',
  '…Î' => 'ë�¤',
  '…Ï' => 'ë�¥',
  '…Ð' => 'ë�¦',
  '…Ñ' => 'ë�§',
  '…Ò' => 'ë�¨',
  '…Ó' => 'ë�©',
  '…Ô' => 'ë�ª',
  '…Õ' => 'ë�«',
  '…Ö' => 'ë�¬',
  '…×' => 'ë�­',
  '…Ø' => 'ë�®',
  '…Ù' => 'ë�¯',
  '…Ú' => 'ë�°',
  '…Û' => 'ë�±',
  '…Ü' => 'ë�²',
  '…Ý' => 'ë�³',
  '…Þ' => 'ë�´',
  '…ß' => 'ë�µ',
  '…à' => 'ë�¶',
  '…á' => 'ë�·',
  '…â' => 'ë�¸',
  '…ã' => 'ë�¹',
  '…ä' => 'ë�º',
  '…å' => 'ë�»',
  '…æ' => 'ë�¾',
  '…ç' => 'ë�¿',
  '…è' => 'ë‚�',
  '…é' => 'ë‚‚',
  '…ê' => 'ë‚ƒ',
  '…ë' => 'ë‚…',
  '…ì' => 'ë‚†',
  '…í' => 'ë‚‡',
  '…î' => 'ë‚ˆ',
  '…ï' => 'ë‚‰',
  '…ð' => 'ë‚Š',
  '…ñ' => 'ë‚‹',
  '…ò' => 'ë‚Ž',
  '…ó' => 'ë‚�',
  '…ô' => 'ë‚’',
  '…õ' => 'ë‚“',
  '…ö' => 'ë‚”',
  '…÷' => 'ë‚•',
  '…ø' => 'ë‚–',
  '…ù' => 'ë‚—',
  '…ú' => 'ë‚›',
  '…û' => 'ë‚�',
  '…ü' => 'ë‚ž',
  '…ý' => 'ë‚£',
  '…þ' => 'ë‚¤',
  '†A' => 'ë‚¥',
  '†B' => 'ë‚¦',
  '†C' => 'ë‚§',
  '†D' => 'ë‚ª',
  '†E' => 'ë‚°',
  '†F' => 'ë‚²',
  '†G' => 'ë‚¶',
  '†H' => 'ë‚·',
  '†I' => 'ë‚¹',
  '†J' => 'ë‚º',
  '†K' => 'ë‚»',
  '†L' => 'ë‚½',
  '†M' => 'ë‚¾',
  '†N' => 'ë‚¿',
  '†O' => 'ëƒ€',
  '†P' => 'ëƒ�',
  '†Q' => 'ëƒ‚',
  '†R' => 'ëƒƒ',
  '†S' => 'ëƒ†',
  '†T' => 'ëƒŠ',
  '†U' => 'ëƒ‹',
  '†V' => 'ëƒŒ',
  '†W' => 'ëƒ�',
  '†X' => 'ëƒŽ',
  '†Y' => 'ëƒ�',
  '†Z' => 'ëƒ’',
  '†a' => 'ëƒ“',
  '†b' => 'ëƒ•',
  '†c' => 'ëƒ–',
  '†d' => 'ëƒ—',
  '†e' => 'ëƒ™',
  '†f' => 'ëƒš',
  '†g' => 'ëƒ›',
  '†h' => 'ëƒœ',
  '†i' => 'ëƒ�',
  '†j' => 'ëƒž',
  '†k' => 'ëƒŸ',
  '†l' => 'ëƒ¡',
  '†m' => 'ëƒ¢',
  '†n' => 'ëƒ£',
  '†o' => 'ëƒ¤',
  '†p' => 'ëƒ¦',
  '†q' => 'ëƒ§',
  '†r' => 'ëƒ¨',
  '†s' => 'ëƒ©',
  '†t' => 'ëƒª',
  '†u' => 'ëƒ«',
  '†v' => 'ëƒ¬',
  '†w' => 'ëƒ­',
  '†x' => 'ëƒ®',
  '†y' => 'ëƒ¯',
  '†z' => 'ëƒ°',
  '†�' => 'ëƒ±',
  '†‚' => 'ëƒ²',
  '†ƒ' => 'ëƒ³',
  '†„' => 'ëƒ´',
  '†…' => 'ëƒµ',
  '††' => 'ëƒ¶',
  '†‡' => 'ëƒ·',
  '†ˆ' => 'ëƒ¸',
  '†‰' => 'ëƒ¹',
  '†Š' => 'ëƒº',
  '†‹' => 'ëƒ»',
  '†Œ' => 'ëƒ¼',
  '†�' => 'ëƒ½',
  '†Ž' => 'ëƒ¾',
  '†�' => 'ëƒ¿',
  '†�' => 'ë„€',
  '†‘' => 'ë„�',
  '†’' => 'ë„‚',
  '†“' => 'ë„ƒ',
  '†”' => 'ë„„',
  '†•' => 'ë„…',
  '†–' => 'ë„†',
  '†—' => 'ë„‡',
  '†˜' => 'ë„Š',
  '†™' => 'ë„�',
  '†š' => 'ë„Ž',
  '†›' => 'ë„�',
  '†œ' => 'ë„‘',
  '†�' => 'ë„”',
  '†ž' => 'ë„•',
  '†Ÿ' => 'ë„–',
  '† ' => 'ë„—',
  '†¡' => 'ë„š',
  '†¢' => 'ë„ž',
  '†£' => 'ë„Ÿ',
  '†¤' => 'ë„ ',
  '†¥' => 'ë„¡',
  '†¦' => 'ë„¢',
  '†§' => 'ë„¦',
  '†¨' => 'ë„§',
  '†©' => 'ë„©',
  '†ª' => 'ë„ª',
  '†«' => 'ë„«',
  '†¬' => 'ë„­',
  '†­' => 'ë„®',
  '†®' => 'ë„¯',
  '†¯' => 'ë„°',
  '†°' => 'ë„±',
  '†±' => 'ë„²',
  '†²' => 'ë„³',
  '†³' => 'ë„¶',
  '†´' => 'ë„º',
  '†µ' => 'ë„»',
  '†¶' => 'ë„¼',
  '†·' => 'ë„½',
  '†¸' => 'ë„¾',
  '†¹' => 'ë„¿',
  '†º' => 'ë…‚',
  '†»' => 'ë…ƒ',
  '†¼' => 'ë……',
  '†½' => 'ë…†',
  '†¾' => 'ë…‡',
  '†¿' => 'ë…‰',
  '†À' => 'ë…Š',
  '†Á' => 'ë…‹',
  '†Â' => 'ë…Œ',
  '†Ã' => 'ë…�',
  '†Ä' => 'ë…Ž',
  '†Å' => 'ë…�',
  '†Æ' => 'ë…’',
  '†Ç' => 'ë…“',
  '†È' => 'ë…–',
  '†É' => 'ë…—',
  '†Ê' => 'ë…™',
  '†Ë' => 'ë…š',
  '†Ì' => 'ë…›',
  '†Í' => 'ë…�',
  '†Î' => 'ë…ž',
  '†Ï' => 'ë…Ÿ',
  '†Ð' => 'ë…¡',
  '†Ñ' => 'ë…¢',
  '†Ò' => 'ë…£',
  '†Ó' => 'ë…¤',
  '†Ô' => 'ë…¥',
  '†Õ' => 'ë…¦',
  '†Ö' => 'ë…§',
  '†×' => 'ë…¨',
  '†Ø' => 'ë…©',
  '†Ù' => 'ë…ª',
  '†Ú' => 'ë…«',
  '†Û' => 'ë…¬',
  '†Ü' => 'ë…­',
  '†Ý' => 'ë…®',
  '†Þ' => 'ë…¯',
  '†ß' => 'ë…°',
  '†à' => 'ë…±',
  '†á' => 'ë…²',
  '†â' => 'ë…³',
  '†ã' => 'ë…´',
  '†ä' => 'ë…µ',
  '†å' => 'ë…¶',
  '†æ' => 'ë…·',
  '†ç' => 'ë…º',
  '†è' => 'ë…»',
  '†é' => 'ë…½',
  '†ê' => 'ë…¾',
  '†ë' => 'ë…¿',
  '†ì' => 'ë†�',
  '†í' => 'ë†ƒ',
  '†î' => 'ë†„',
  '†ï' => 'ë†…',
  '†ð' => 'ë††',
  '†ñ' => 'ë†‡',
  '†ò' => 'ë†Š',
  '†ó' => 'ë†Œ',
  '†ô' => 'ë†Ž',
  '†õ' => 'ë†�',
  '†ö' => 'ë†�',
  '†÷' => 'ë†‘',
  '†ø' => 'ë†•',
  '†ù' => 'ë†–',
  '†ú' => 'ë†—',
  '†û' => 'ë†™',
  '†ü' => 'ë†š',
  '†ý' => 'ë†›',
  '†þ' => 'ë†�',
  '‡A' => 'ë†ž',
  '‡B' => 'ë†Ÿ',
  '‡C' => 'ë† ',
  '‡D' => 'ë†¡',
  '‡E' => 'ë†¢',
  '‡F' => 'ë†£',
  '‡G' => 'ë†¤',
  '‡H' => 'ë†¥',
  '‡I' => 'ë†¦',
  '‡J' => 'ë†§',
  '‡K' => 'ë†©',
  '‡L' => 'ë†ª',
  '‡M' => 'ë†«',
  '‡N' => 'ë†¬',
  '‡O' => 'ë†­',
  '‡P' => 'ë†®',
  '‡Q' => 'ë†¯',
  '‡R' => 'ë†°',
  '‡S' => 'ë†±',
  '‡T' => 'ë†²',
  '‡U' => 'ë†³',
  '‡V' => 'ë†´',
  '‡W' => 'ë†µ',
  '‡X' => 'ë†¶',
  '‡Y' => 'ë†·',
  '‡Z' => 'ë†¸',
  '‡a' => 'ë†¹',
  '‡b' => 'ë†º',
  '‡c' => 'ë†»',
  '‡d' => 'ë†¼',
  '‡e' => 'ë†½',
  '‡f' => 'ë†¾',
  '‡g' => 'ë†¿',
  '‡h' => 'ë‡€',
  '‡i' => 'ë‡�',
  '‡j' => 'ë‡‚',
  '‡k' => 'ë‡ƒ',
  '‡l' => 'ë‡„',
  '‡m' => 'ë‡…',
  '‡n' => 'ë‡†',
  '‡o' => 'ë‡‡',
  '‡p' => 'ë‡ˆ',
  '‡q' => 'ë‡‰',
  '‡r' => 'ë‡Š',
  '‡s' => 'ë‡‹',
  '‡t' => 'ë‡�',
  '‡u' => 'ë‡Ž',
  '‡v' => 'ë‡�',
  '‡w' => 'ë‡‘',
  '‡x' => 'ë‡’',
  '‡y' => 'ë‡“',
  '‡z' => 'ë‡•',
  '‡�' => 'ë‡–',
  '‡‚' => 'ë‡—',
  '‡ƒ' => 'ë‡˜',
  '‡„' => 'ë‡™',
  '‡…' => 'ë‡š',
  '‡†' => 'ë‡›',
  '‡‡' => 'ë‡ž',
  '‡ˆ' => 'ë‡ ',
  '‡‰' => 'ë‡¡',
  '‡Š' => 'ë‡¢',
  '‡‹' => 'ë‡£',
  '‡Œ' => 'ë‡¤',
  '‡�' => 'ë‡¥',
  '‡Ž' => 'ë‡¦',
  '‡�' => 'ë‡§',
  '‡�' => 'ë‡ª',
  '‡‘' => 'ë‡«',
  '‡’' => 'ë‡­',
  '‡“' => 'ë‡®',
  '‡”' => 'ë‡¯',
  '‡•' => 'ë‡±',
  '‡–' => 'ë‡²',
  '‡—' => 'ë‡³',
  '‡˜' => 'ë‡´',
  '‡™' => 'ë‡µ',
  '‡š' => 'ë‡¶',
  '‡›' => 'ë‡·',
  '‡œ' => 'ë‡¸',
  '‡�' => 'ë‡º',
  '‡ž' => 'ë‡¼',
  '‡Ÿ' => 'ë‡¾',
  '‡ ' => 'ë‡¿',
  '‡¡' => 'ëˆ€',
  '‡¢' => 'ëˆ�',
  '‡£' => 'ëˆ‚',
  '‡¤' => 'ëˆƒ',
  '‡¥' => 'ëˆ†',
  '‡¦' => 'ëˆ‡',
  '‡§' => 'ëˆ‰',
  '‡¨' => 'ëˆŠ',
  '‡©' => 'ëˆ�',
  '‡ª' => 'ëˆŽ',
  '‡«' => 'ëˆ�',
  '‡¬' => 'ëˆ�',
  '‡­' => 'ëˆ‘',
  '‡®' => 'ëˆ’',
  '‡¯' => 'ëˆ“',
  '‡°' => 'ëˆ–',
  '‡±' => 'ëˆ˜',
  '‡²' => 'ëˆš',
  '‡³' => 'ëˆ›',
  '‡´' => 'ëˆœ',
  '‡µ' => 'ëˆ�',
  '‡¶' => 'ëˆž',
  '‡·' => 'ëˆŸ',
  '‡¸' => 'ëˆ¡',
  '‡¹' => 'ëˆ¢',
  '‡º' => 'ëˆ£',
  '‡»' => 'ëˆ¤',
  '‡¼' => 'ëˆ¥',
  '‡½' => 'ëˆ¦',
  '‡¾' => 'ëˆ§',
  '‡¿' => 'ëˆ¨',
  '‡À' => 'ëˆ©',
  '‡Á' => 'ëˆª',
  '‡Â' => 'ëˆ«',
  '‡Ã' => 'ëˆ¬',
  '‡Ä' => 'ëˆ­',
  '‡Å' => 'ëˆ®',
  '‡Æ' => 'ëˆ¯',
  '‡Ç' => 'ëˆ°',
  '‡È' => 'ëˆ±',
  '‡É' => 'ëˆ²',
  '‡Ê' => 'ëˆ³',
  '‡Ë' => 'ëˆµ',
  '‡Ì' => 'ëˆ¶',
  '‡Í' => 'ëˆ·',
  '‡Î' => 'ëˆ¸',
  '‡Ï' => 'ëˆ¹',
  '‡Ð' => 'ëˆº',
  '‡Ñ' => 'ëˆ»',
  '‡Ò' => 'ëˆ½',
  '‡Ó' => 'ëˆ¾',
  '‡Ô' => 'ëˆ¿',
  '‡Õ' => 'ë‰€',
  '‡Ö' => 'ë‰�',
  '‡×' => 'ë‰‚',
  '‡Ø' => 'ë‰ƒ',
  '‡Ù' => 'ë‰„',
  '‡Ú' => 'ë‰…',
  '‡Û' => 'ë‰†',
  '‡Ü' => 'ë‰‡',
  '‡Ý' => 'ë‰ˆ',
  '‡Þ' => 'ë‰‰',
  '‡ß' => 'ë‰Š',
  '‡à' => 'ë‰‹',
  '‡á' => 'ë‰Œ',
  '‡â' => 'ë‰�',
  '‡ã' => 'ë‰Ž',
  '‡ä' => 'ë‰�',
  '‡å' => 'ë‰�',
  '‡æ' => 'ë‰‘',
  '‡ç' => 'ë‰’',
  '‡è' => 'ë‰“',
  '‡é' => 'ë‰”',
  '‡ê' => 'ë‰•',
  '‡ë' => 'ë‰–',
  '‡ì' => 'ë‰—',
  '‡í' => 'ë‰™',
  '‡î' => 'ë‰š',
  '‡ï' => 'ë‰›',
  '‡ð' => 'ë‰�',
  '‡ñ' => 'ë‰ž',
  '‡ò' => 'ë‰Ÿ',
  '‡ó' => 'ë‰¡',
  '‡ô' => 'ë‰¢',
  '‡õ' => 'ë‰£',
  '‡ö' => 'ë‰¤',
  '‡÷' => 'ë‰¥',
  '‡ø' => 'ë‰¦',
  '‡ù' => 'ë‰§',
  '‡ú' => 'ë‰ª',
  '‡û' => 'ë‰«',
  '‡ü' => 'ë‰¬',
  '‡ý' => 'ë‰­',
  '‡þ' => 'ë‰®',
  'ˆA' => 'ë‰¯',
  'ˆB' => 'ë‰°',
  'ˆC' => 'ë‰±',
  'ˆD' => 'ë‰²',
  'ˆE' => 'ë‰³',
  'ˆF' => 'ë‰¶',
  'ˆG' => 'ë‰·',
  'ˆH' => 'ë‰¸',
  'ˆI' => 'ë‰¹',
  'ˆJ' => 'ë‰º',
  'ˆK' => 'ë‰»',
  'ˆL' => 'ë‰½',
  'ˆM' => 'ë‰¾',
  'ˆN' => 'ë‰¿',
  'ˆO' => 'ëŠ€',
  'ˆP' => 'ëŠ�',
  'ˆQ' => 'ëŠ‚',
  'ˆR' => 'ëŠƒ',
  'ˆS' => 'ëŠ†',
  'ˆT' => 'ëŠ‡',
  'ˆU' => 'ëŠˆ',
  'ˆV' => 'ëŠŠ',
  'ˆW' => 'ëŠ‹',
  'ˆX' => 'ëŠŒ',
  'ˆY' => 'ëŠ�',
  'ˆZ' => 'ëŠŽ',
  'ˆa' => 'ëŠ�',
  'ˆb' => 'ëŠ’',
  'ˆc' => 'ëŠ“',
  'ˆd' => 'ëŠ•',
  'ˆe' => 'ëŠ–',
  'ˆf' => 'ëŠ—',
  'ˆg' => 'ëŠ›',
  'ˆh' => 'ëŠœ',
  'ˆi' => 'ëŠ�',
  'ˆj' => 'ëŠž',
  'ˆk' => 'ëŠŸ',
  'ˆl' => 'ëŠ¢',
  'ˆm' => 'ëŠ¤',
  'ˆn' => 'ëŠ§',
  'ˆo' => 'ëŠ¨',
  'ˆp' => 'ëŠ©',
  'ˆq' => 'ëŠ«',
  'ˆr' => 'ëŠ­',
  'ˆs' => 'ëŠ®',
  'ˆt' => 'ëŠ¯',
  'ˆu' => 'ëŠ±',
  'ˆv' => 'ëŠ²',
  'ˆw' => 'ëŠ³',
  'ˆx' => 'ëŠµ',
  'ˆy' => 'ëŠ¶',
  'ˆz' => 'ëŠ·',
  'ˆ�' => 'ëŠ¸',
  'ˆ‚' => 'ëŠ¹',
  'ˆƒ' => 'ëŠº',
  'ˆ„' => 'ëŠ»',
  'ˆ…' => 'ëŠ¼',
  'ˆ†' => 'ëŠ½',
  'ˆ‡' => 'ëŠ¾',
  'ˆˆ' => 'ëŠ¿',
  'ˆ‰' => 'ë‹€',
  'ˆŠ' => 'ë‹�',
  'ˆ‹' => 'ë‹‚',
  'ˆŒ' => 'ë‹ƒ',
  'ˆ�' => 'ë‹„',
  'ˆŽ' => 'ë‹…',
  'ˆ�' => 'ë‹†',
  'ˆ�' => 'ë‹‡',
  'ˆ‘' => 'ë‹Š',
  'ˆ’' => 'ë‹‹',
  'ˆ“' => 'ë‹�',
  'ˆ”' => 'ë‹Ž',
  'ˆ•' => 'ë‹�',
  'ˆ–' => 'ë‹‘',
  'ˆ—' => 'ë‹“',
  'ˆ˜' => 'ë‹”',
  'ˆ™' => 'ë‹•',
  'ˆš' => 'ë‹–',
  'ˆ›' => 'ë‹—',
  'ˆœ' => 'ë‹š',
  'ˆ�' => 'ë‹œ',
  'ˆž' => 'ë‹ž',
  'ˆŸ' => 'ë‹Ÿ',
  'ˆ ' => 'ë‹ ',
  'ˆ¡' => 'ë‹¡',
  'ˆ¢' => 'ë‹£',
  'ˆ£' => 'ë‹§',
  'ˆ¤' => 'ë‹©',
  'ˆ¥' => 'ë‹ª',
  'ˆ¦' => 'ë‹°',
  'ˆ§' => 'ë‹±',
  'ˆ¨' => 'ë‹²',
  'ˆ©' => 'ë‹¶',
  'ˆª' => 'ë‹¼',
  'ˆ«' => 'ë‹½',
  'ˆ¬' => 'ë‹¾',
  'ˆ­' => 'ëŒ‚',
  'ˆ®' => 'ëŒƒ',
  'ˆ¯' => 'ëŒ…',
  'ˆ°' => 'ëŒ†',
  'ˆ±' => 'ëŒ‡',
  'ˆ²' => 'ëŒ‰',
  'ˆ³' => 'ëŒŠ',
  'ˆ´' => 'ëŒ‹',
  'ˆµ' => 'ëŒŒ',
  'ˆ¶' => 'ëŒ�',
  'ˆ·' => 'ëŒŽ',
  'ˆ¸' => 'ëŒ�',
  'ˆ¹' => 'ëŒ’',
  'ˆº' => 'ëŒ–',
  'ˆ»' => 'ëŒ—',
  'ˆ¼' => 'ëŒ˜',
  'ˆ½' => 'ëŒ™',
  'ˆ¾' => 'ëŒš',
  'ˆ¿' => 'ëŒ›',
  'ˆÀ' => 'ëŒ�',
  'ˆÁ' => 'ëŒž',
  'ˆÂ' => 'ëŒŸ',
  'ˆÃ' => 'ëŒ ',
  'ˆÄ' => 'ëŒ¡',
  'ˆÅ' => 'ëŒ¢',
  'ˆÆ' => 'ëŒ£',
  'ˆÇ' => 'ëŒ¤',
  'ˆÈ' => 'ëŒ¥',
  'ˆÉ' => 'ëŒ¦',
  'ˆÊ' => 'ëŒ§',
  'ˆË' => 'ëŒ¨',
  'ˆÌ' => 'ëŒ©',
  'ˆÍ' => 'ëŒª',
  'ˆÎ' => 'ëŒ«',
  'ˆÏ' => 'ëŒ¬',
  'ˆÐ' => 'ëŒ­',
  'ˆÑ' => 'ëŒ®',
  'ˆÒ' => 'ëŒ¯',
  'ˆÓ' => 'ëŒ°',
  'ˆÔ' => 'ëŒ±',
  'ˆÕ' => 'ëŒ²',
  'ˆÖ' => 'ëŒ³',
  'ˆ×' => 'ëŒ´',
  'ˆØ' => 'ëŒµ',
  'ˆÙ' => 'ëŒ¶',
  'ˆÚ' => 'ëŒ·',
  'ˆÛ' => 'ëŒ¸',
  'ˆÜ' => 'ëŒ¹',
  'ˆÝ' => 'ëŒº',
  'ˆÞ' => 'ëŒ»',
  'ˆß' => 'ëŒ¼',
  'ˆà' => 'ëŒ½',
  'ˆá' => 'ëŒ¾',
  'ˆâ' => 'ëŒ¿',
  'ˆã' => 'ë�€',
  'ˆä' => 'ë��',
  'ˆå' => 'ë�‚',
  'ˆæ' => 'ë�ƒ',
  'ˆç' => 'ë�„',
  'ˆè' => 'ë�…',
  'ˆé' => 'ë�†',
  'ˆê' => 'ë�‡',
  'ˆë' => 'ë�ˆ',
  'ˆì' => 'ë�‰',
  'ˆí' => 'ë�Š',
  'ˆî' => 'ë�‹',
  'ˆï' => 'ë�Œ',
  'ˆð' => 'ë��',
  'ˆñ' => 'ë�Ž',
  'ˆò' => 'ë��',
  'ˆó' => 'ë��',
  'ˆô' => 'ë�‘',
  'ˆõ' => 'ë�’',
  'ˆö' => 'ë�“',
  'ˆ÷' => 'ë�—',
  'ˆø' => 'ë�™',
  'ˆù' => 'ë�š',
  'ˆú' => 'ë��',
  'ˆû' => 'ë� ',
  'ˆü' => 'ë�¡',
  'ˆý' => 'ë�¢',
  'ˆþ' => 'ë�£',
  '‰A' => 'ë�¦',
  '‰B' => 'ë�¨',
  '‰C' => 'ë�ª',
  '‰D' => 'ë�¬',
  '‰E' => 'ë�­',
  '‰F' => 'ë�¯',
  '‰G' => 'ë�²',
  '‰H' => 'ë�³',
  '‰I' => 'ë�µ',
  '‰J' => 'ë�¶',
  '‰K' => 'ë�·',
  '‰L' => 'ë�¹',
  '‰M' => 'ë�º',
  '‰N' => 'ë�»',
  '‰O' => 'ë�¼',
  '‰P' => 'ë�½',
  '‰Q' => 'ë�¾',
  '‰R' => 'ë�¿',
  '‰S' => 'ëŽ‚',
  '‰T' => 'ëŽ†',
  '‰U' => 'ëŽ‡',
  '‰V' => 'ëŽˆ',
  '‰W' => 'ëŽ‰',
  '‰X' => 'ëŽŠ',
  '‰Y' => 'ëŽ‹',
  '‰Z' => 'ëŽ�',
  '‰a' => 'ëŽŽ',
  '‰b' => 'ëŽ�',
  '‰c' => 'ëŽ‘',
  '‰d' => 'ëŽ’',
  '‰e' => 'ëŽ“',
  '‰f' => 'ëŽ•',
  '‰g' => 'ëŽ–',
  '‰h' => 'ëŽ—',
  '‰i' => 'ëŽ˜',
  '‰j' => 'ëŽ™',
  '‰k' => 'ëŽš',
  '‰l' => 'ëŽ›',
  '‰m' => 'ëŽœ',
  '‰n' => 'ëŽ�',
  '‰o' => 'ëŽž',
  '‰p' => 'ëŽŸ',
  '‰q' => 'ëŽ¢',
  '‰r' => 'ëŽ£',
  '‰s' => 'ëŽ¤',
  '‰t' => 'ëŽ¥',
  '‰u' => 'ëŽ¦',
  '‰v' => 'ëŽ§',
  '‰w' => 'ëŽ©',
  '‰x' => 'ëŽª',
  '‰y' => 'ëŽ«',
  '‰z' => 'ëŽ­',
  '‰�' => 'ëŽ®',
  '‰‚' => 'ëŽ¯',
  '‰ƒ' => 'ëŽ°',
  '‰„' => 'ëŽ±',
  '‰…' => 'ëŽ²',
  '‰†' => 'ëŽ³',
  '‰‡' => 'ëŽ´',
  '‰ˆ' => 'ëŽµ',
  '‰‰' => 'ëŽ¶',
  '‰Š' => 'ëŽ·',
  '‰‹' => 'ëŽ¸',
  '‰Œ' => 'ëŽ¹',
  '‰�' => 'ëŽº',
  '‰Ž' => 'ëŽ»',
  '‰�' => 'ëŽ¼',
  '‰�' => 'ëŽ½',
  '‰‘' => 'ëŽ¾',
  '‰’' => 'ëŽ¿',
  '‰“' => 'ë�€',
  '‰”' => 'ë��',
  '‰•' => 'ë�‚',
  '‰–' => 'ë�ƒ',
  '‰—' => 'ë�†',
  '‰˜' => 'ë�‡',
  '‰™' => 'ë�‰',
  '‰š' => 'ë�Š',
  '‰›' => 'ë��',
  '‰œ' => 'ë��',
  '‰�' => 'ë�‘',
  '‰ž' => 'ë�’',
  '‰Ÿ' => 'ë�“',
  '‰ ' => 'ë�–',
  '‰¡' => 'ë�˜',
  '‰¢' => 'ë�š',
  '‰£' => 'ë�œ',
  '‰¤' => 'ë�ž',
  '‰¥' => 'ë�Ÿ',
  '‰¦' => 'ë�¡',
  '‰§' => 'ë�¢',
  '‰¨' => 'ë�£',
  '‰©' => 'ë�¥',
  '‰ª' => 'ë�¦',
  '‰«' => 'ë�§',
  '‰¬' => 'ë�©',
  '‰­' => 'ë�ª',
  '‰®' => 'ë�«',
  '‰¯' => 'ë�¬',
  '‰°' => 'ë�­',
  '‰±' => 'ë�®',
  '‰²' => 'ë�¯',
  '‰³' => 'ë�°',
  '‰´' => 'ë�±',
  '‰µ' => 'ë�²',
  '‰¶' => 'ë�³',
  '‰·' => 'ë�´',
  '‰¸' => 'ë�µ',
  '‰¹' => 'ë�¶',
  '‰º' => 'ë�·',
  '‰»' => 'ë�¸',
  '‰¼' => 'ë�¹',
  '‰½' => 'ë�º',
  '‰¾' => 'ë�»',
  '‰¿' => 'ë�½',
  '‰À' => 'ë�¾',
  '‰Á' => 'ë�¿',
  '‰Â' => 'ë�€',
  '‰Ã' => 'ë��',
  '‰Ä' => 'ë�‚',
  '‰Å' => 'ë�ƒ',
  '‰Æ' => 'ë�„',
  '‰Ç' => 'ë�…',
  '‰È' => 'ë�†',
  '‰É' => 'ë�‡',
  '‰Ê' => 'ë�ˆ',
  '‰Ë' => 'ë�‰',
  '‰Ì' => 'ë�Š',
  '‰Í' => 'ë�‹',
  '‰Î' => 'ë�Œ',
  '‰Ï' => 'ë��',
  '‰Ð' => 'ë�Ž',
  '‰Ñ' => 'ë��',
  '‰Ò' => 'ë�‘',
  '‰Ó' => 'ë�’',
  '‰Ô' => 'ë�“',
  '‰Õ' => 'ë�”',
  '‰Ö' => 'ë�•',
  '‰×' => 'ë�–',
  '‰Ø' => 'ë�—',
  '‰Ù' => 'ë�™',
  '‰Ú' => 'ë�š',
  '‰Û' => 'ë�›',
  '‰Ü' => 'ë��',
  '‰Ý' => 'ë�ž',
  '‰Þ' => 'ë�Ÿ',
  '‰ß' => 'ë�¡',
  '‰à' => 'ë�¢',
  '‰á' => 'ë�£',
  '‰â' => 'ë�¤',
  '‰ã' => 'ë�¥',
  '‰ä' => 'ë�¦',
  '‰å' => 'ë�§',
  '‰æ' => 'ë�ª',
  '‰ç' => 'ë�¬',
  '‰è' => 'ë�­',
  '‰é' => 'ë�®',
  '‰ê' => 'ë�¯',
  '‰ë' => 'ë�°',
  '‰ì' => 'ë�±',
  '‰í' => 'ë�²',
  '‰î' => 'ë�³',
  '‰ï' => 'ë�µ',
  '‰ð' => 'ë�¶',
  '‰ñ' => 'ë�·',
  '‰ò' => 'ë�¸',
  '‰ó' => 'ë�¹',
  '‰ô' => 'ë�º',
  '‰õ' => 'ë�»',
  '‰ö' => 'ë�¼',
  '‰÷' => 'ë�½',
  '‰ø' => 'ë�¾',
  '‰ù' => 'ë�¿',
  '‰ú' => 'ë‘€',
  '‰û' => 'ë‘�',
  '‰ü' => 'ë‘‚',
  '‰ý' => 'ë‘ƒ',
  '‰þ' => 'ë‘„',
  'ŠA' => 'ë‘…',
  'ŠB' => 'ë‘†',
  'ŠC' => 'ë‘‡',
  'ŠD' => 'ë‘ˆ',
  'ŠE' => 'ë‘‰',
  'ŠF' => 'ë‘Š',
  'ŠG' => 'ë‘‹',
  'ŠH' => 'ë‘Œ',
  'ŠI' => 'ë‘�',
  'ŠJ' => 'ë‘Ž',
  'ŠK' => 'ë‘�',
  'ŠL' => 'ë‘’',
  'ŠM' => 'ë‘“',
  'ŠN' => 'ë‘•',
  'ŠO' => 'ë‘–',
  'ŠP' => 'ë‘—',
  'ŠQ' => 'ë‘™',
  'ŠR' => 'ë‘š',
  'ŠS' => 'ë‘›',
  'ŠT' => 'ë‘œ',
  'ŠU' => 'ë‘�',
  'ŠV' => 'ë‘ž',
  'ŠW' => 'ë‘Ÿ',
  'ŠX' => 'ë‘¢',
  'ŠY' => 'ë‘¤',
  'ŠZ' => 'ë‘¦',
  'Ša' => 'ë‘§',
  'Šb' => 'ë‘¨',
  'Šc' => 'ë‘©',
  'Šd' => 'ë‘ª',
  'Še' => 'ë‘«',
  'Šf' => 'ë‘­',
  'Šg' => 'ë‘®',
  'Šh' => 'ë‘¯',
  'Ši' => 'ë‘°',
  'Šj' => 'ë‘±',
  'Šk' => 'ë‘²',
  'Šl' => 'ë‘³',
  'Šm' => 'ë‘´',
  'Šn' => 'ë‘µ',
  'Šo' => 'ë‘¶',
  'Šp' => 'ë‘·',
  'Šq' => 'ë‘¸',
  'Šr' => 'ë‘¹',
  'Šs' => 'ë‘º',
  'Št' => 'ë‘»',
  'Šu' => 'ë‘¼',
  'Šv' => 'ë‘½',
  'Šw' => 'ë‘¾',
  'Šx' => 'ë‘¿',
  'Šy' => 'ë’�',
  'Šz' => 'ë’‚',
  'Š�' => 'ë’ƒ',
  'Š‚' => 'ë’„',
  'Šƒ' => 'ë’…',
  'Š„' => 'ë’†',
  'Š…' => 'ë’‡',
  'Š†' => 'ë’‰',
  'Š‡' => 'ë’Š',
  'Šˆ' => 'ë’‹',
  'Š‰' => 'ë’Œ',
  'ŠŠ' => 'ë’�',
  'Š‹' => 'ë’Ž',
  'ŠŒ' => 'ë’�',
  'Š�' => 'ë’�',
  'ŠŽ' => 'ë’‘',
  'Š�' => 'ë’’',
  'Š�' => 'ë’“',
  'Š‘' => 'ë’”',
  'Š’' => 'ë’•',
  'Š“' => 'ë’–',
  'Š”' => 'ë’—',
  'Š•' => 'ë’˜',
  'Š–' => 'ë’™',
  'Š—' => 'ë’š',
  'Š˜' => 'ë’›',
  'Š™' => 'ë’œ',
  'Šš' => 'ë’ž',
  'Š›' => 'ë’Ÿ',
  'Šœ' => 'ë’ ',
  'Š�' => 'ë’¡',
  'Šž' => 'ë’¢',
  'ŠŸ' => 'ë’£',
  'Š ' => 'ë’¥',
  'Š¡' => 'ë’¦',
  'Š¢' => 'ë’§',
  'Š£' => 'ë’©',
  'Š¤' => 'ë’ª',
  'Š¥' => 'ë’«',
  'Š¦' => 'ë’­',
  'Š§' => 'ë’®',
  'Š¨' => 'ë’¯',
  'Š©' => 'ë’°',
  'Šª' => 'ë’±',
  'Š«' => 'ë’²',
  'Š¬' => 'ë’³',
  'Š­' => 'ë’´',
  'Š®' => 'ë’¶',
  'Š¯' => 'ë’¸',
  'Š°' => 'ë’º',
  'Š±' => 'ë’»',
  'Š²' => 'ë’¼',
  'Š³' => 'ë’½',
  'Š´' => 'ë’¾',
  'Šµ' => 'ë’¿',
  'Š¶' => 'ë“�',
  'Š·' => 'ë“‚',
  'Š¸' => 'ë“ƒ',
  'Š¹' => 'ë“…',
  'Šº' => 'ë“†',
  'Š»' => 'ë“‡',
  'Š¼' => 'ë“‰',
  'Š½' => 'ë“Š',
  'Š¾' => 'ë“‹',
  'Š¿' => 'ë“Œ',
  'ŠÀ' => 'ë“�',
  'ŠÁ' => 'ë“Ž',
  'ŠÂ' => 'ë“�',
  'ŠÃ' => 'ë“‘',
  'ŠÄ' => 'ë“’',
  'ŠÅ' => 'ë““',
  'ŠÆ' => 'ë“”',
  'ŠÇ' => 'ë“–',
  'ŠÈ' => 'ë“—',
  'ŠÉ' => 'ë“˜',
  'ŠÊ' => 'ë“™',
  'ŠË' => 'ë“š',
  'ŠÌ' => 'ë“›',
  'ŠÍ' => 'ë“ž',
  'ŠÎ' => 'ë“Ÿ',
  'ŠÏ' => 'ë“¡',
  'ŠÐ' => 'ë“¢',
  'ŠÑ' => 'ë“¥',
  'ŠÒ' => 'ë“§',
  'ŠÓ' => 'ë“¨',
  'ŠÔ' => 'ë“©',
  'ŠÕ' => 'ë“ª',
  'ŠÖ' => 'ë“«',
  'Š×' => 'ë“®',
  'ŠØ' => 'ë“°',
  'ŠÙ' => 'ë“²',
  'ŠÚ' => 'ë“³',
  'ŠÛ' => 'ë“´',
  'ŠÜ' => 'ë“µ',
  'ŠÝ' => 'ë“¶',
  'ŠÞ' => 'ë“·',
  'Šß' => 'ë“¹',
  'Šà' => 'ë“º',
  'Šá' => 'ë“»',
  'Šâ' => 'ë“¼',
  'Šã' => 'ë“½',
  'Šä' => 'ë“¾',
  'Šå' => 'ë“¿',
  'Šæ' => 'ë”€',
  'Šç' => 'ë”�',
  'Šè' => 'ë”‚',
  'Šé' => 'ë”ƒ',
  'Šê' => 'ë”„',
  'Šë' => 'ë”…',
  'Šì' => 'ë”†',
  'Ší' => 'ë”‡',
  'Šî' => 'ë”ˆ',
  'Šï' => 'ë”‰',
  'Šð' => 'ë”Š',
  'Šñ' => 'ë”‹',
  'Šò' => 'ë”Œ',
  'Šó' => 'ë”�',
  'Šô' => 'ë”Ž',
  'Šõ' => 'ë”�',
  'Šö' => 'ë”�',
  'Š÷' => 'ë”‘',
  'Šø' => 'ë”’',
  'Šù' => 'ë”“',
  'Šú' => 'ë”–',
  'Šû' => 'ë”—',
  'Šü' => 'ë”™',
  'Šý' => 'ë”š',
  'Šþ' => 'ë”�',
  '‹A' => 'ë”ž',
  '‹B' => 'ë”Ÿ',
  '‹C' => 'ë” ',
  '‹D' => 'ë”¡',
  '‹E' => 'ë”¢',
  '‹F' => 'ë”£',
  '‹G' => 'ë”¦',
  '‹H' => 'ë”«',
  '‹I' => 'ë”¬',
  '‹J' => 'ë”­',
  '‹K' => 'ë”®',
  '‹L' => 'ë”¯',
  '‹M' => 'ë”²',
  '‹N' => 'ë”³',
  '‹O' => 'ë”µ',
  '‹P' => 'ë”¶',
  '‹Q' => 'ë”·',
  '‹R' => 'ë”¹',
  '‹S' => 'ë”º',
  '‹T' => 'ë”»',
  '‹U' => 'ë”¼',
  '‹V' => 'ë”½',
  '‹W' => 'ë”¾',
  '‹X' => 'ë”¿',
  '‹Y' => 'ë•‚',
  '‹Z' => 'ë•†',
  '‹a' => 'ë•‡',
  '‹b' => 'ë•ˆ',
  '‹c' => 'ë•‰',
  '‹d' => 'ë•Š',
  '‹e' => 'ë•Ž',
  '‹f' => 'ë•�',
  '‹g' => 'ë•‘',
  '‹h' => 'ë•’',
  '‹i' => 'ë•“',
  '‹j' => 'ë••',
  '‹k' => 'ë•–',
  '‹l' => 'ë•—',
  '‹m' => 'ë•˜',
  '‹n' => 'ë•™',
  '‹o' => 'ë•š',
  '‹p' => 'ë•›',
  '‹q' => 'ë•ž',
  '‹r' => 'ë•¢',
  '‹s' => 'ë•£',
  '‹t' => 'ë•¤',
  '‹u' => 'ë•¥',
  '‹v' => 'ë•¦',
  '‹w' => 'ë•§',
  '‹x' => 'ë•¨',
  '‹y' => 'ë•©',
  '‹z' => 'ë•ª',
  '‹�' => 'ë•«',
  '‹‚' => 'ë•¬',
  '‹ƒ' => 'ë•­',
  '‹„' => 'ë•®',
  '‹…' => 'ë•¯',
  '‹†' => 'ë•°',
  '‹‡' => 'ë•±',
  '‹ˆ' => 'ë•²',
  '‹‰' => 'ë•³',
  '‹Š' => 'ë•´',
  '‹‹' => 'ë•µ',
  '‹Œ' => 'ë•¶',
  '‹�' => 'ë•·',
  '‹Ž' => 'ë•¸',
  '‹�' => 'ë•¹',
  '‹�' => 'ë•º',
  '‹‘' => 'ë•»',
  '‹’' => 'ë•¼',
  '‹“' => 'ë•½',
  '‹”' => 'ë•¾',
  '‹•' => 'ë•¿',
  '‹–' => 'ë–€',
  '‹—' => 'ë–�',
  '‹˜' => 'ë–‚',
  '‹™' => 'ë–ƒ',
  '‹š' => 'ë–„',
  '‹›' => 'ë–…',
  '‹œ' => 'ë–†',
  '‹�' => 'ë–‡',
  '‹ž' => 'ë–ˆ',
  '‹Ÿ' => 'ë–‰',
  '‹ ' => 'ë–Š',
  '‹¡' => 'ë–‹',
  '‹¢' => 'ë–Œ',
  '‹£' => 'ë–�',
  '‹¤' => 'ë–Ž',
  '‹¥' => 'ë–�',
  '‹¦' => 'ë–�',
  '‹§' => 'ë–‘',
  '‹¨' => 'ë–’',
  '‹©' => 'ë–“',
  '‹ª' => 'ë–”',
  '‹«' => 'ë–•',
  '‹¬' => 'ë––',
  '‹­' => 'ë–—',
  '‹®' => 'ë–˜',
  '‹¯' => 'ë–™',
  '‹°' => 'ë–š',
  '‹±' => 'ë–›',
  '‹²' => 'ë–œ',
  '‹³' => 'ë–�',
  '‹´' => 'ë–ž',
  '‹µ' => 'ë–Ÿ',
  '‹¶' => 'ë–¢',
  '‹·' => 'ë–£',
  '‹¸' => 'ë–¥',
  '‹¹' => 'ë–¦',
  '‹º' => 'ë–§',
  '‹»' => 'ë–©',
  '‹¼' => 'ë–¬',
  '‹½' => 'ë–­',
  '‹¾' => 'ë–®',
  '‹¿' => 'ë–¯',
  '‹À' => 'ë–²',
  '‹Á' => 'ë–¶',
  '‹Â' => 'ë–·',
  '‹Ã' => 'ë–¸',
  '‹Ä' => 'ë–¹',
  '‹Å' => 'ë–º',
  '‹Æ' => 'ë–¾',
  '‹Ç' => 'ë–¿',
  '‹È' => 'ë—�',
  '‹É' => 'ë—‚',
  '‹Ê' => 'ë—ƒ',
  '‹Ë' => 'ë—…',
  '‹Ì' => 'ë—†',
  '‹Í' => 'ë—‡',
  '‹Î' => 'ë—ˆ',
  '‹Ï' => 'ë—‰',
  '‹Ð' => 'ë—Š',
  '‹Ñ' => 'ë—‹',
  '‹Ò' => 'ë—Ž',
  '‹Ó' => 'ë—’',
  '‹Ô' => 'ë—“',
  '‹Õ' => 'ë—”',
  '‹Ö' => 'ë—•',
  '‹×' => 'ë—–',
  '‹Ø' => 'ë——',
  '‹Ù' => 'ë—™',
  '‹Ú' => 'ë—š',
  '‹Û' => 'ë—›',
  '‹Ü' => 'ë—œ',
  '‹Ý' => 'ë—�',
  '‹Þ' => 'ë—ž',
  '‹ß' => 'ë—Ÿ',
  '‹à' => 'ë— ',
  '‹á' => 'ë—¡',
  '‹â' => 'ë—¢',
  '‹ã' => 'ë—£',
  '‹ä' => 'ë—¤',
  '‹å' => 'ë—¥',
  '‹æ' => 'ë—¦',
  '‹ç' => 'ë—§',
  '‹è' => 'ë—¨',
  '‹é' => 'ë—©',
  '‹ê' => 'ë—ª',
  '‹ë' => 'ë—«',
  '‹ì' => 'ë—­',
  '‹í' => 'ë—®',
  '‹î' => 'ë—¯',
  '‹ï' => 'ë—°',
  '‹ð' => 'ë—±',
  '‹ñ' => 'ë—²',
  '‹ò' => 'ë—³',
  '‹ó' => 'ë—´',
  '‹ô' => 'ë—µ',
  '‹õ' => 'ë—¶',
  '‹ö' => 'ë—·',
  '‹÷' => 'ë—¸',
  '‹ø' => 'ë—¹',
  '‹ù' => 'ë—º',
  '‹ú' => 'ë—»',
  '‹û' => 'ë—¼',
  '‹ü' => 'ë—½',
  '‹ý' => 'ë—¾',
  '‹þ' => 'ë—¿',
  'ŒA' => 'ë˜€',
  'ŒB' => 'ë˜�',
  'ŒC' => 'ë˜‚',
  'ŒD' => 'ë˜ƒ',
  'ŒE' => 'ë˜„',
  'ŒF' => 'ë˜…',
  'ŒG' => 'ë˜†',
  'ŒH' => 'ë˜‡',
  'ŒI' => 'ë˜ˆ',
  'ŒJ' => 'ë˜‰',
  'ŒK' => 'ë˜Š',
  'ŒL' => 'ë˜‹',
  'ŒM' => 'ë˜Œ',
  'ŒN' => 'ë˜�',
  'ŒO' => 'ë˜Ž',
  'ŒP' => 'ë˜�',
  'ŒQ' => 'ë˜’',
  'ŒR' => 'ë˜“',
  'ŒS' => 'ë˜•',
  'ŒT' => 'ë˜–',
  'ŒU' => 'ë˜—',
  'ŒV' => 'ë˜™',
  'ŒW' => 'ë˜š',
  'ŒX' => 'ë˜›',
  'ŒY' => 'ë˜œ',
  'ŒZ' => 'ë˜�',
  'Œa' => 'ë˜ž',
  'Œb' => 'ë˜Ÿ',
  'Œc' => 'ë˜ ',
  'Œd' => 'ë˜¡',
  'Œe' => 'ë˜¢',
  'Œf' => 'ë˜£',
  'Œg' => 'ë˜¤',
  'Œh' => 'ë˜¦',
  'Œi' => 'ë˜§',
  'Œj' => 'ë˜¨',
  'Œk' => 'ë˜©',
  'Œl' => 'ë˜ª',
  'Œm' => 'ë˜«',
  'Œn' => 'ë˜­',
  'Œo' => 'ë˜®',
  'Œp' => 'ë˜¯',
  'Œq' => 'ë˜°',
  'Œr' => 'ë˜±',
  'Œs' => 'ë˜²',
  'Œt' => 'ë˜³',
  'Œu' => 'ë˜µ',
  'Œv' => 'ë˜¶',
  'Œw' => 'ë˜·',
  'Œx' => 'ë˜¸',
  'Œy' => 'ë˜¹',
  'Œz' => 'ë˜º',
  'Œ�' => 'ë˜»',
  'Œ‚' => 'ë˜¼',
  'Œƒ' => 'ë˜½',
  'Œ„' => 'ë˜¾',
  'Œ…' => 'ë˜¿',
  'Œ†' => 'ë™€',
  'Œ‡' => 'ë™�',
  'Œˆ' => 'ë™‚',
  'Œ‰' => 'ë™ƒ',
  'ŒŠ' => 'ë™„',
  'Œ‹' => 'ë™…',
  'ŒŒ' => 'ë™†',
  'Œ�' => 'ë™‡',
  'ŒŽ' => 'ë™‰',
  'Œ�' => 'ë™Š',
  'Œ�' => 'ë™‹',
  'Œ‘' => 'ë™Œ',
  'Œ’' => 'ë™�',
  'Œ“' => 'ë™Ž',
  'Œ”' => 'ë™�',
  'Œ•' => 'ë™�',
  'Œ–' => 'ë™‘',
  'Œ—' => 'ë™’',
  'Œ˜' => 'ë™“',
  'Œ™' => 'ë™”',
  'Œš' => 'ë™•',
  'Œ›' => 'ë™–',
  'Œœ' => 'ë™—',
  'Œ�' => 'ë™˜',
  'Œž' => 'ë™™',
  'ŒŸ' => 'ë™š',
  'Œ ' => 'ë™›',
  'Œ¡' => 'ë™œ',
  'Œ¢' => 'ë™�',
  'Œ£' => 'ë™ž',
  'Œ¤' => 'ë™Ÿ',
  'Œ¥' => 'ë™ ',
  'Œ¦' => 'ë™¡',
  'Œ§' => 'ë™¢',
  'Œ¨' => 'ë™£',
  'Œ©' => 'ë™¥',
  'Œª' => 'ë™¦',
  'Œ«' => 'ë™§',
  'Œ¬' => 'ë™©',
  'Œ­' => 'ë™ª',
  'Œ®' => 'ë™«',
  'Œ¯' => 'ë™¬',
  'Œ°' => 'ë™­',
  'Œ±' => 'ë™®',
  'Œ²' => 'ë™¯',
  'Œ³' => 'ë™°',
  'Œ´' => 'ë™±',
  'Œµ' => 'ë™²',
  'Œ¶' => 'ë™³',
  'Œ·' => 'ë™´',
  'Œ¸' => 'ë™µ',
  'Œ¹' => 'ë™¶',
  'Œº' => 'ë™·',
  'Œ»' => 'ë™¸',
  'Œ¼' => 'ë™¹',
  'Œ½' => 'ë™º',
  'Œ¾' => 'ë™»',
  'Œ¿' => 'ë™¼',
  'ŒÀ' => 'ë™½',
  'ŒÁ' => 'ë™¾',
  'ŒÂ' => 'ë™¿',
  'ŒÃ' => 'ëš€',
  'ŒÄ' => 'ëš�',
  'ŒÅ' => 'ëš‚',
  'ŒÆ' => 'ëšƒ',
  'ŒÇ' => 'ëš„',
  'ŒÈ' => 'ëš…',
  'ŒÉ' => 'ëš†',
  'ŒÊ' => 'ëš‡',
  'ŒË' => 'ëšˆ',
  'ŒÌ' => 'ëš‰',
  'ŒÍ' => 'ëšŠ',
  'ŒÎ' => 'ëš‹',
  'ŒÏ' => 'ëšŒ',
  'ŒÐ' => 'ëš�',
  'ŒÑ' => 'ëšŽ',
  'ŒÒ' => 'ëš�',
  'ŒÓ' => 'ëš�',
  'ŒÔ' => 'ëš‘',
  'ŒÕ' => 'ëš’',
  'ŒÖ' => 'ëš“',
  'Œ×' => 'ëš”',
  'ŒØ' => 'ëš•',
  'ŒÙ' => 'ëš–',
  'ŒÚ' => 'ëš—',
  'ŒÛ' => 'ëš˜',
  'ŒÜ' => 'ëš™',
  'ŒÝ' => 'ëšš',
  'ŒÞ' => 'ëš›',
  'Œß' => 'ëšž',
  'Œà' => 'ëšŸ',
  'Œá' => 'ëš¡',
  'Œâ' => 'ëš¢',
  'Œã' => 'ëš£',
  'Œä' => 'ëš¥',
  'Œå' => 'ëš¦',
  'Œæ' => 'ëš§',
  'Œç' => 'ëš¨',
  'Œè' => 'ëš©',
  'Œé' => 'ëšª',
  'Œê' => 'ëš­',
  'Œë' => 'ëš®',
  'Œì' => 'ëš¯',
  'Œí' => 'ëš°',
  'Œî' => 'ëš²',
  'Œï' => 'ëš³',
  'Œð' => 'ëš´',
  'Œñ' => 'ëšµ',
  'Œò' => 'ëš¶',
  'Œó' => 'ëš·',
  'Œô' => 'ëš¸',
  'Œõ' => 'ëš¹',
  'Œö' => 'ëšº',
  'Œ÷' => 'ëš»',
  'Œø' => 'ëš¼',
  'Œù' => 'ëš½',
  'Œú' => 'ëš¾',
  'Œû' => 'ëš¿',
  'Œü' => 'ë›€',
  'Œý' => 'ë›�',
  'Œþ' => 'ë›‚',
  '�A' => 'ë›ƒ',
  '�B' => 'ë›„',
  '�C' => 'ë›…',
  '�D' => 'ë›†',
  '�E' => 'ë›‡',
  '�F' => 'ë›ˆ',
  '�G' => 'ë›‰',
  '�H' => 'ë›Š',
  '�I' => 'ë›‹',
  '�J' => 'ë›Œ',
  '�K' => 'ë›�',
  '�L' => 'ë›Ž',
  '�M' => 'ë›�',
  '�N' => 'ë›�',
  '�O' => 'ë›‘',
  '�P' => 'ë›’',
  '�Q' => 'ë›“',
  '�R' => 'ë›•',
  '�S' => 'ë›–',
  '�T' => 'ë›—',
  '�U' => 'ë›˜',
  '�V' => 'ë›™',
  '�W' => 'ë›š',
  '�X' => 'ë››',
  '�Y' => 'ë›œ',
  '�Z' => 'ë›�',
  '�a' => 'ë›ž',
  '�b' => 'ë›Ÿ',
  '�c' => 'ë› ',
  '�d' => 'ë›¡',
  '�e' => 'ë›¢',
  '�f' => 'ë›£',
  '�g' => 'ë›¤',
  '�h' => 'ë›¥',
  '�i' => 'ë›¦',
  '�j' => 'ë›§',
  '�k' => 'ë›¨',
  '�l' => 'ë›©',
  '�m' => 'ë›ª',
  '�n' => 'ë›«',
  '�o' => 'ë›¬',
  '�p' => 'ë›­',
  '�q' => 'ë›®',
  '�r' => 'ë›¯',
  '�s' => 'ë›±',
  '�t' => 'ë›²',
  '�u' => 'ë›³',
  '�v' => 'ë›µ',
  '�w' => 'ë›¶',
  '�x' => 'ë›·',
  '�y' => 'ë›¹',
  '�z' => 'ë›º',
  '��' => 'ë›»',
  '�‚' => 'ë›¼',
  '�ƒ' => 'ë›½',
  '�„' => 'ë›¾',
  '�…' => 'ë›¿',
  '�†' => 'ëœ‚',
  '�‡' => 'ëœƒ',
  '�ˆ' => 'ëœ„',
  '�‰' => 'ëœ†',
  '�Š' => 'ëœ‡',
  '�‹' => 'ëœˆ',
  '�Œ' => 'ëœ‰',
  '��' => 'ëœŠ',
  '�Ž' => 'ëœ‹',
  '��' => 'ëœŒ',
  '��' => 'ëœ�',
  '�‘' => 'ëœŽ',
  '�’' => 'ëœ�',
  '�“' => 'ëœ�',
  '�”' => 'ëœ‘',
  '�•' => 'ëœ’',
  '�–' => 'ëœ“',
  '�—' => 'ëœ”',
  '�˜' => 'ëœ•',
  '�™' => 'ëœ–',
  '�š' => 'ëœ—',
  '�›' => 'ëœ˜',
  '�œ' => 'ëœ™',
  '��' => 'ëœš',
  '�ž' => 'ëœ›',
  '�Ÿ' => 'ëœœ',
  '� ' => 'ëœ�',
  '�¡' => 'ëœž',
  '�¢' => 'ëœŸ',
  '�£' => 'ëœ ',
  '�¤' => 'ëœ¡',
  '�¥' => 'ëœ¢',
  '�¦' => 'ëœ£',
  '�§' => 'ëœ¤',
  '�¨' => 'ëœ¥',
  '�©' => 'ëœ¦',
  '�ª' => 'ëœ§',
  '�«' => 'ëœª',
  '�¬' => 'ëœ«',
  '�­' => 'ëœ­',
  '�®' => 'ëœ®',
  '�¯' => 'ëœ±',
  '�°' => 'ëœ²',
  '�±' => 'ëœ³',
  '�²' => 'ëœ´',
  '�³' => 'ëœµ',
  '�´' => 'ëœ¶',
  '�µ' => 'ëœ·',
  '�¶' => 'ëœº',
  '�·' => 'ëœ¼',
  '�¸' => 'ëœ½',
  '�¹' => 'ëœ¾',
  '�º' => 'ëœ¿',
  '�»' => 'ë�€',
  '�¼' => 'ë��',
  '�½' => 'ë�‚',
  '�¾' => 'ë�ƒ',
  '�¿' => 'ë�…',
  '�À' => 'ë�†',
  '�Á' => 'ë�‡',
  '�Â' => 'ë�‰',
  '�Ã' => 'ë�Š',
  '�Ä' => 'ë�‹',
  '�Å' => 'ë��',
  '�Æ' => 'ë�Ž',
  '�Ç' => 'ë��',
  '�È' => 'ë��',
  '�É' => 'ë�‘',
  '�Ê' => 'ë�’',
  '�Ë' => 'ë�“',
  '�Ì' => 'ë�–',
  '�Í' => 'ë�—',
  '�Î' => 'ë�˜',
  '�Ï' => 'ë�™',
  '�Ð' => 'ë�š',
  '�Ñ' => 'ë�›',
  '�Ò' => 'ë�œ',
  '�Ó' => 'ë��',
  '�Ô' => 'ë�ž',
  '�Õ' => 'ë�Ÿ',
  '�Ö' => 'ë�¡',
  '�×' => 'ë�¢',
  '�Ø' => 'ë�£',
  '�Ù' => 'ë�¥',
  '�Ú' => 'ë�¦',
  '�Û' => 'ë�§',
  '�Ü' => 'ë�©',
  '�Ý' => 'ë�ª',
  '�Þ' => 'ë�«',
  '�ß' => 'ë�¬',
  '�à' => 'ë�­',
  '�á' => 'ë�®',
  '�â' => 'ë�¯',
  '�ã' => 'ë�²',
  '�ä' => 'ë�´',
  '�å' => 'ë�¶',
  '�æ' => 'ë�·',
  '�ç' => 'ë�¸',
  '�è' => 'ë�¹',
  '�é' => 'ë�º',
  '�ê' => 'ë�»',
  '�ë' => 'ë�¾',
  '�ì' => 'ë�¿',
  '�í' => 'ëž�',
  '�î' => 'ëž‚',
  '�ï' => 'ëžƒ',
  '�ð' => 'ëž…',
  '�ñ' => 'ëž†',
  '�ò' => 'ëž‡',
  '�ó' => 'ëžˆ',
  '�ô' => 'ëž‰',
  '�õ' => 'ëžŠ',
  '�ö' => 'ëž‹',
  '�÷' => 'ëžŽ',
  '�ø' => 'ëž“',
  '�ù' => 'ëž”',
  '�ú' => 'ëž•',
  '�û' => 'ëžš',
  '�ü' => 'ëž›',
  '�ý' => 'ëž�',
  '�þ' => 'ëžž',
  'ŽA' => 'ëžŸ',
  'ŽB' => 'ëž¡',
  'ŽC' => 'ëž¢',
  'ŽD' => 'ëž£',
  'ŽE' => 'ëž¤',
  'ŽF' => 'ëž¥',
  'ŽG' => 'ëž¦',
  'ŽH' => 'ëž§',
  'ŽI' => 'ëžª',
  'ŽJ' => 'ëž®',
  'ŽK' => 'ëž¯',
  'ŽL' => 'ëž°',
  'ŽM' => 'ëž±',
  'ŽN' => 'ëž²',
  'ŽO' => 'ëž³',
  'ŽP' => 'ëž¶',
  'ŽQ' => 'ëž·',
  'ŽR' => 'ëž¹',
  'ŽS' => 'ëžº',
  'ŽT' => 'ëž»',
  'ŽU' => 'ëž¼',
  'ŽV' => 'ëž½',
  'ŽW' => 'ëž¾',
  'ŽX' => 'ëž¿',
  'ŽY' => 'ëŸ€',
  'ŽZ' => 'ëŸ�',
  'Ža' => 'ëŸ‚',
  'Žb' => 'ëŸƒ',
  'Žc' => 'ëŸ„',
  'Žd' => 'ëŸ…',
  'Že' => 'ëŸ†',
  'Žf' => 'ëŸˆ',
  'Žg' => 'ëŸŠ',
  'Žh' => 'ëŸ‹',
  'Ži' => 'ëŸŒ',
  'Žj' => 'ëŸ�',
  'Žk' => 'ëŸŽ',
  'Žl' => 'ëŸ�',
  'Žm' => 'ëŸ�',
  'Žn' => 'ëŸ‘',
  'Žo' => 'ëŸ’',
  'Žp' => 'ëŸ“',
  'Žq' => 'ëŸ”',
  'Žr' => 'ëŸ•',
  'Žs' => 'ëŸ–',
  'Žt' => 'ëŸ—',
  'Žu' => 'ëŸ˜',
  'Žv' => 'ëŸ™',
  'Žw' => 'ëŸš',
  'Žx' => 'ëŸ›',
  'Žy' => 'ëŸœ',
  'Žz' => 'ëŸ�',
  'Ž�' => 'ëŸž',
  'Ž‚' => 'ëŸŸ',
  'Žƒ' => 'ëŸ ',
  'Ž„' => 'ëŸ¡',
  'Ž…' => 'ëŸ¢',
  'Ž†' => 'ëŸ£',
  'Ž‡' => 'ëŸ¤',
  'Žˆ' => 'ëŸ¥',
  'Ž‰' => 'ëŸ¦',
  'ŽŠ' => 'ëŸ§',
  'Ž‹' => 'ëŸ¨',
  'ŽŒ' => 'ëŸ©',
  'Ž�' => 'ëŸª',
  'ŽŽ' => 'ëŸ«',
  'Ž�' => 'ëŸ®',
  'Ž�' => 'ëŸ¯',
  'Ž‘' => 'ëŸ±',
  'Ž’' => 'ëŸ²',
  'Ž“' => 'ëŸ³',
  'Ž”' => 'ëŸµ',
  'Ž•' => 'ëŸ¶',
  'Ž–' => 'ëŸ·',
  'Ž—' => 'ëŸ¸',
  'Ž˜' => 'ëŸ¹',
  'Ž™' => 'ëŸº',
  'Žš' => 'ëŸ»',
  'Ž›' => 'ëŸ¾',
  'Žœ' => 'ë ‚',
  'Ž�' => 'ë ƒ',
  'Žž' => 'ë „',
  'ŽŸ' => 'ë …',
  'Ž ' => 'ë †',
  'Ž¡' => 'ë Š',
  'Ž¢' => 'ë ‹',
  'Ž£' => 'ë �',
  'Ž¤' => 'ë Ž',
  'Ž¥' => 'ë �',
  'Ž¦' => 'ë ‘',
  'Ž§' => 'ë ’',
  'Ž¨' => 'ë “',
  'Ž©' => 'ë ”',
  'Žª' => 'ë •',
  'Ž«' => 'ë –',
  'Ž¬' => 'ë —',
  'Ž­' => 'ë š',
  'Ž®' => 'ë œ',
  'Ž¯' => 'ë ž',
  'Ž°' => 'ë Ÿ',
  'Ž±' => 'ë  ',
  'Ž²' => 'ë ¡',
  'Ž³' => 'ë ¢',
  'Ž´' => 'ë £',
  'Žµ' => 'ë ¦',
  'Ž¶' => 'ë §',
  'Ž·' => 'ë ©',
  'Ž¸' => 'ë ª',
  'Ž¹' => 'ë «',
  'Žº' => 'ë ­',
  'Ž»' => 'ë ®',
  'Ž¼' => 'ë ¯',
  'Ž½' => 'ë °',
  'Ž¾' => 'ë ±',
  'Ž¿' => 'ë ²',
  'ŽÀ' => 'ë ³',
  'ŽÁ' => 'ë ¶',
  'ŽÂ' => 'ë º',
  'ŽÃ' => 'ë »',
  'ŽÄ' => 'ë ¼',
  'ŽÅ' => 'ë ½',
  'ŽÆ' => 'ë ¾',
  'ŽÇ' => 'ë ¿',
  'ŽÈ' => 'ë¡�',
  'ŽÉ' => 'ë¡‚',
  'ŽÊ' => 'ë¡ƒ',
  'ŽË' => 'ë¡…',
  'ŽÌ' => 'ë¡†',
  'ŽÍ' => 'ë¡‡',
  'ŽÎ' => 'ë¡ˆ',
  'ŽÏ' => 'ë¡‰',
  'ŽÐ' => 'ë¡Š',
  'ŽÑ' => 'ë¡‹',
  'ŽÒ' => 'ë¡Œ',
  'ŽÓ' => 'ë¡�',
  'ŽÔ' => 'ë¡Ž',
  'ŽÕ' => 'ë¡�',
  'ŽÖ' => 'ë¡�',
  'Ž×' => 'ë¡’',
  'ŽØ' => 'ë¡”',
  'ŽÙ' => 'ë¡•',
  'ŽÚ' => 'ë¡–',
  'ŽÛ' => 'ë¡—',
  'ŽÜ' => 'ë¡˜',
  'ŽÝ' => 'ë¡™',
  'ŽÞ' => 'ë¡š',
  'Žß' => 'ë¡›',
  'Žà' => 'ë¡ž',
  'Žá' => 'ë¡Ÿ',
  'Žâ' => 'ë¡¡',
  'Žã' => 'ë¡¢',
  'Žä' => 'ë¡£',
  'Žå' => 'ë¡¥',
  'Žæ' => 'ë¡¦',
  'Žç' => 'ë¡§',
  'Žè' => 'ë¡¨',
  'Žé' => 'ë¡©',
  'Žê' => 'ë¡ª',
  'Žë' => 'ë¡«',
  'Žì' => 'ë¡®',
  'Ží' => 'ë¡°',
  'Žî' => 'ë¡²',
  'Žï' => 'ë¡³',
  'Žð' => 'ë¡´',
  'Žñ' => 'ë¡µ',
  'Žò' => 'ë¡¶',
  'Žó' => 'ë¡·',
  'Žô' => 'ë¡¹',
  'Žõ' => 'ë¡º',
  'Žö' => 'ë¡»',
  'Ž÷' => 'ë¡½',
  'Žø' => 'ë¡¾',
  'Žù' => 'ë¡¿',
  'Žú' => 'ë¢€',
  'Žû' => 'ë¢�',
  'Žü' => 'ë¢‚',
  'Žý' => 'ë¢ƒ',
  'Žþ' => 'ë¢„',
  '�A' => 'ë¢…',
  '�B' => 'ë¢†',
  '�C' => 'ë¢‡',
  '�D' => 'ë¢ˆ',
  '�E' => 'ë¢‰',
  '�F' => 'ë¢Š',
  '�G' => 'ë¢‹',
  '�H' => 'ë¢Œ',
  '�I' => 'ë¢Ž',
  '�J' => 'ë¢�',
  '�K' => 'ë¢�',
  '�L' => 'ë¢‘',
  '�M' => 'ë¢’',
  '�N' => 'ë¢“',
  '�O' => 'ë¢”',
  '�P' => 'ë¢•',
  '�Q' => 'ë¢–',
  '�R' => 'ë¢—',
  '�S' => 'ë¢˜',
  '�T' => 'ë¢™',
  '�U' => 'ë¢š',
  '�V' => 'ë¢›',
  '�W' => 'ë¢œ',
  '�X' => 'ë¢�',
  '�Y' => 'ë¢ž',
  '�Z' => 'ë¢Ÿ',
  '�a' => 'ë¢ ',
  '�b' => 'ë¢¡',
  '�c' => 'ë¢¢',
  '�d' => 'ë¢£',
  '�e' => 'ë¢¤',
  '�f' => 'ë¢¥',
  '�g' => 'ë¢¦',
  '�h' => 'ë¢§',
  '�i' => 'ë¢©',
  '�j' => 'ë¢ª',
  '�k' => 'ë¢«',
  '�l' => 'ë¢¬',
  '�m' => 'ë¢­',
  '�n' => 'ë¢®',
  '�o' => 'ë¢¯',
  '�p' => 'ë¢±',
  '�q' => 'ë¢²',
  '�r' => 'ë¢³',
  '�s' => 'ë¢µ',
  '�t' => 'ë¢¶',
  '�u' => 'ë¢·',
  '�v' => 'ë¢¹',
  '�w' => 'ë¢º',
  '�x' => 'ë¢»',
  '�y' => 'ë¢¼',
  '�z' => 'ë¢½',
  '��' => 'ë¢¾',
  '�‚' => 'ë¢¿',
  '�ƒ' => 'ë£‚',
  '�„' => 'ë£„',
  '�…' => 'ë£†',
  '�†' => 'ë£‡',
  '�‡' => 'ë£ˆ',
  '�ˆ' => 'ë£‰',
  '�‰' => 'ë£Š',
  '�Š' => 'ë£‹',
  '�‹' => 'ë£�',
  '�Œ' => 'ë£Ž',
  '��' => 'ë£�',
  '�Ž' => 'ë£‘',
  '��' => 'ë£’',
  '��' => 'ë£“',
  '�‘' => 'ë£•',
  '�’' => 'ë£–',
  '�“' => 'ë£—',
  '�”' => 'ë£˜',
  '�•' => 'ë£™',
  '�–' => 'ë£š',
  '�—' => 'ë£›',
  '�˜' => 'ë£œ',
  '�™' => 'ë£ž',
  '�š' => 'ë£ ',
  '�›' => 'ë£¢',
  '�œ' => 'ë££',
  '��' => 'ë£¤',
  '�ž' => 'ë£¥',
  '�Ÿ' => 'ë£¦',
  '� ' => 'ë£§',
  '�¡' => 'ë£ª',
  '�¢' => 'ë£«',
  '�£' => 'ë£­',
  '�¤' => 'ë£®',
  '�¥' => 'ë£¯',
  '�¦' => 'ë£±',
  '�§' => 'ë£²',
  '�¨' => 'ë£³',
  '�©' => 'ë£´',
  '�ª' => 'ë£µ',
  '�«' => 'ë£¶',
  '�¬' => 'ë£·',
  '�­' => 'ë£º',
  '�®' => 'ë£¼',
  '�¯' => 'ë£¾',
  '�°' => 'ë£¿',
  '�±' => 'ë¤€',
  '�²' => 'ë¤�',
  '�³' => 'ë¤‚',
  '�´' => 'ë¤ƒ',
  '�µ' => 'ë¤…',
  '�¶' => 'ë¤†',
  '�·' => 'ë¤‡',
  '�¸' => 'ë¤ˆ',
  '�¹' => 'ë¤‰',
  '�º' => 'ë¤Š',
  '�»' => 'ë¤‹',
  '�¼' => 'ë¤Œ',
  '�½' => 'ë¤�',
  '�¾' => 'ë¤Ž',
  '�¿' => 'ë¤�',
  '�À' => 'ë¤�',
  '�Á' => 'ë¤‘',
  '�Â' => 'ë¤’',
  '�Ã' => 'ë¤“',
  '�Ä' => 'ë¤”',
  '�Å' => 'ë¤•',
  '�Æ' => 'ë¤–',
  '�Ç' => 'ë¤—',
  '�È' => 'ë¤™',
  '�É' => 'ë¤š',
  '�Ê' => 'ë¤›',
  '�Ë' => 'ë¤œ',
  '�Ì' => 'ë¤�',
  '�Í' => 'ë¤ž',
  '�Î' => 'ë¤Ÿ',
  '�Ï' => 'ë¤¡',
  '�Ð' => 'ë¤¢',
  '�Ñ' => 'ë¤£',
  '�Ò' => 'ë¤¤',
  '�Ó' => 'ë¤¥',
  '�Ô' => 'ë¤¦',
  '�Õ' => 'ë¤§',
  '�Ö' => 'ë¤¨',
  '�×' => 'ë¤©',
  '�Ø' => 'ë¤ª',
  '�Ù' => 'ë¤«',
  '�Ú' => 'ë¤¬',
  '�Û' => 'ë¤­',
  '�Ü' => 'ë¤®',
  '�Ý' => 'ë¤¯',
  '�Þ' => 'ë¤°',
  '�ß' => 'ë¤±',
  '�à' => 'ë¤²',
  '�á' => 'ë¤³',
  '�â' => 'ë¤´',
  '�ã' => 'ë¤µ',
  '�ä' => 'ë¤¶',
  '�å' => 'ë¤·',
  '�æ' => 'ë¤¸',
  '�ç' => 'ë¤¹',
  '�è' => 'ë¤º',
  '�é' => 'ë¤»',
  '�ê' => 'ë¤¾',
  '�ë' => 'ë¤¿',
  '�ì' => 'ë¥�',
  '�í' => 'ë¥‚',
  '�î' => 'ë¥ƒ',
  '�ï' => 'ë¥…',
  '�ð' => 'ë¥†',
  '�ñ' => 'ë¥‡',
  '�ò' => 'ë¥ˆ',
  '�ó' => 'ë¥‰',
  '�ô' => 'ë¥Š',
  '�õ' => 'ë¥‹',
  '�ö' => 'ë¥�',
  '�÷' => 'ë¥Ž',
  '�ø' => 'ë¥�',
  '�ù' => 'ë¥’',
  '�ú' => 'ë¥“',
  '�û' => 'ë¥”',
  '�ü' => 'ë¥•',
  '�ý' => 'ë¥–',
  '�þ' => 'ë¥—',
  '�A' => 'ë¥š',
  '�B' => 'ë¥›',
  '�C' => 'ë¥�',
  '�D' => 'ë¥ž',
  '�E' => 'ë¥Ÿ',
  '�F' => 'ë¥¡',
  '�G' => 'ë¥¢',
  '�H' => 'ë¥£',
  '�I' => 'ë¥¤',
  '�J' => 'ë¥¥',
  '�K' => 'ë¥¦',
  '�L' => 'ë¥§',
  '�M' => 'ë¥ª',
  '�N' => 'ë¥¬',
  '�O' => 'ë¥®',
  '�P' => 'ë¥¯',
  '�Q' => 'ë¥°',
  '�R' => 'ë¥±',
  '�S' => 'ë¥²',
  '�T' => 'ë¥³',
  '�U' => 'ë¥¶',
  '�V' => 'ë¥·',
  '�W' => 'ë¥¹',
  '�X' => 'ë¥º',
  '�Y' => 'ë¥»',
  '�Z' => 'ë¥½',
  '�a' => 'ë¥¾',
  '�b' => 'ë¥¿',
  '�c' => 'ë¦€',
  '�d' => 'ë¦�',
  '�e' => 'ë¦‚',
  '�f' => 'ë¦ƒ',
  '�g' => 'ë¦†',
  '�h' => 'ë¦ˆ',
  '�i' => 'ë¦‹',
  '�j' => 'ë¦Œ',
  '�k' => 'ë¦�',
  '�l' => 'ë¦�',
  '�m' => 'ë¦‘',
  '�n' => 'ë¦’',
  '�o' => 'ë¦“',
  '�p' => 'ë¦”',
  '�q' => 'ë¦•',
  '�r' => 'ë¦–',
  '�s' => 'ë¦—',
  '�t' => 'ë¦˜',
  '�u' => 'ë¦™',
  '�v' => 'ë¦š',
  '�w' => 'ë¦›',
  '�x' => 'ë¦œ',
  '�y' => 'ë¦�',
  '�z' => 'ë¦ž',
  '��' => 'ë¦Ÿ',
  '�‚' => 'ë¦ ',
  '�ƒ' => 'ë¦¡',
  '�„' => 'ë¦¢',
  '�…' => 'ë¦£',
  '�†' => 'ë¦¤',
  '�‡' => 'ë¦¥',
  '�ˆ' => 'ë¦¦',
  '�‰' => 'ë¦§',
  '�Š' => 'ë¦¨',
  '�‹' => 'ë¦©',
  '�Œ' => 'ë¦ª',
  '��' => 'ë¦«',
  '�Ž' => 'ë¦®',
  '��' => 'ë¦¯',
  '��' => 'ë¦±',
  '�‘' => 'ë¦²',
  '�’' => 'ë¦³',
  '�“' => 'ë¦µ',
  '�”' => 'ë¦¶',
  '�•' => 'ë¦·',
  '�–' => 'ë¦¸',
  '�—' => 'ë¦¹',
  '�˜' => 'ë¦º',
  '�™' => 'ë¦»',
  '�š' => 'ë¦¾',
  '�›' => 'ë§€',
  '�œ' => 'ë§‚',
  '��' => 'ë§ƒ',
  '�ž' => 'ë§„',
  '�Ÿ' => 'ë§…',
  '� ' => 'ë§†',
  '�¡' => 'ë§‡',
  '�¢' => 'ë§Š',
  '�£' => 'ë§‹',
  '�¤' => 'ë§�',
  '�¥' => 'ë§“',
  '�¦' => 'ë§”',
  '�§' => 'ë§•',
  '�¨' => 'ë§–',
  '�©' => 'ë§—',
  '�ª' => 'ë§š',
  '�«' => 'ë§œ',
  '�¬' => 'ë§Ÿ',
  '�­' => 'ë§ ',
  '�®' => 'ë§¢',
  '�¯' => 'ë§¦',
  '�°' => 'ë§§',
  '�±' => 'ë§©',
  '�²' => 'ë§ª',
  '�³' => 'ë§«',
  '�´' => 'ë§­',
  '�µ' => 'ë§®',
  '�¶' => 'ë§¯',
  '�·' => 'ë§°',
  '�¸' => 'ë§±',
  '�¹' => 'ë§²',
  '�º' => 'ë§³',
  '�»' => 'ë§¶',
  '�¼' => 'ë§»',
  '�½' => 'ë§¼',
  '�¾' => 'ë§½',
  '�¿' => 'ë§¾',
  '�À' => 'ë§¿',
  '�Á' => 'ë¨‚',
  '�Â' => 'ë¨ƒ',
  '�Ã' => 'ë¨„',
  '�Ä' => 'ë¨…',
  '�Å' => 'ë¨†',
  '�Æ' => 'ë¨‡',
  '�Ç' => 'ë¨‰',
  '�È' => 'ë¨Š',
  '�É' => 'ë¨‹',
  '�Ê' => 'ë¨Œ',
  '�Ë' => 'ë¨�',
  '�Ì' => 'ë¨Ž',
  '�Í' => 'ë¨�',
  '�Î' => 'ë¨�',
  '�Ï' => 'ë¨‘',
  '�Ð' => 'ë¨’',
  '�Ñ' => 'ë¨“',
  '�Ò' => 'ë¨”',
  '�Ó' => 'ë¨–',
  '�Ô' => 'ë¨—',
  '�Õ' => 'ë¨˜',
  '�Ö' => 'ë¨™',
  '�×' => 'ë¨š',
  '�Ø' => 'ë¨›',
  '�Ù' => 'ë¨œ',
  '�Ú' => 'ë¨�',
  '�Û' => 'ë¨ž',
  '�Ü' => 'ë¨Ÿ',
  '�Ý' => 'ë¨ ',
  '�Þ' => 'ë¨¡',
  '�ß' => 'ë¨¢',
  '�à' => 'ë¨£',
  '�á' => 'ë¨¤',
  '�â' => 'ë¨¥',
  '�ã' => 'ë¨¦',
  '�ä' => 'ë¨§',
  '�å' => 'ë¨¨',
  '�æ' => 'ë¨©',
  '�ç' => 'ë¨ª',
  '�è' => 'ë¨«',
  '�é' => 'ë¨¬',
  '�ê' => 'ë¨­',
  '�ë' => 'ë¨®',
  '�ì' => 'ë¨¯',
  '�í' => 'ë¨°',
  '�î' => 'ë¨±',
  '�ï' => 'ë¨²',
  '�ð' => 'ë¨³',
  '�ñ' => 'ë¨´',
  '�ò' => 'ë¨µ',
  '�ó' => 'ë¨¶',
  '�ô' => 'ë¨·',
  '�õ' => 'ë¨º',
  '�ö' => 'ë¨»',
  '�÷' => 'ë¨½',
  '�ø' => 'ë¨¾',
  '�ù' => 'ë¨¿',
  '�ú' => 'ë©�',
  '�û' => 'ë©ƒ',
  '�ü' => 'ë©„',
  '�ý' => 'ë©…',
  '�þ' => 'ë©†',
  '‘A' => 'ë©‡',
  '‘B' => 'ë©Š',
  '‘C' => 'ë©Œ',
  '‘D' => 'ë©�',
  '‘E' => 'ë©�',
  '‘F' => 'ë©‘',
  '‘G' => 'ë©’',
  '‘H' => 'ë©–',
  '‘I' => 'ë©—',
  '‘J' => 'ë©™',
  '‘K' => 'ë©š',
  '‘L' => 'ë©›',
  '‘M' => 'ë©�',
  '‘N' => 'ë©ž',
  '‘O' => 'ë©Ÿ',
  '‘P' => 'ë© ',
  '‘Q' => 'ë©¡',
  '‘R' => 'ë©¢',
  '‘S' => 'ë©£',
  '‘T' => 'ë©¦',
  '‘U' => 'ë©ª',
  '‘V' => 'ë©«',
  '‘W' => 'ë©¬',
  '‘X' => 'ë©­',
  '‘Y' => 'ë©®',
  '‘Z' => 'ë©¯',
  '‘a' => 'ë©²',
  '‘b' => 'ë©³',
  '‘c' => 'ë©µ',
  '‘d' => 'ë©¶',
  '‘e' => 'ë©·',
  '‘f' => 'ë©¹',
  '‘g' => 'ë©º',
  '‘h' => 'ë©»',
  '‘i' => 'ë©¼',
  '‘j' => 'ë©½',
  '‘k' => 'ë©¾',
  '‘l' => 'ë©¿',
  '‘m' => 'ëª€',
  '‘n' => 'ëª�',
  '‘o' => 'ëª‚',
  '‘p' => 'ëª†',
  '‘q' => 'ëªˆ',
  '‘r' => 'ëª‰',
  '‘s' => 'ëªŠ',
  '‘t' => 'ëª‹',
  '‘u' => 'ëª�',
  '‘v' => 'ëªŽ',
  '‘w' => 'ëª�',
  '‘x' => 'ëª�',
  '‘y' => 'ëª‘',
  '‘z' => 'ëª’',
  '‘�' => 'ëª“',
  '‘‚' => 'ëª”',
  '‘ƒ' => 'ëª•',
  '‘„' => 'ëª–',
  '‘…' => 'ëª—',
  '‘†' => 'ëª˜',
  '‘‡' => 'ëª™',
  '‘ˆ' => 'ëªš',
  '‘‰' => 'ëª›',
  '‘Š' => 'ëªœ',
  '‘‹' => 'ëª�',
  '‘Œ' => 'ëªž',
  '‘�' => 'ëªŸ',
  '‘Ž' => 'ëª ',
  '‘�' => 'ëª¡',
  '‘�' => 'ëª¢',
  '‘‘' => 'ëª£',
  '‘’' => 'ëª¤',
  '‘“' => 'ëª¥',
  '‘”' => 'ëª¦',
  '‘•' => 'ëª§',
  '‘–' => 'ëªª',
  '‘—' => 'ëª­',
  '‘˜' => 'ëª®',
  '‘™' => 'ëª¯',
  '‘š' => 'ëª±',
  '‘›' => 'ëª³',
  '‘œ' => 'ëª´',
  '‘�' => 'ëªµ',
  '‘ž' => 'ëª¶',
  '‘Ÿ' => 'ëª·',
  '‘ ' => 'ëªº',
  '‘¡' => 'ëª¼',
  '‘¢' => 'ëª¾',
  '‘£' => 'ëª¿',
  '‘¤' => 'ë«€',
  '‘¥' => 'ë«�',
  '‘¦' => 'ë«‚',
  '‘§' => 'ë«ƒ',
  '‘¨' => 'ë«…',
  '‘©' => 'ë«†',
  '‘ª' => 'ë«‡',
  '‘«' => 'ë«‰',
  '‘¬' => 'ë«Š',
  '‘­' => 'ë«‹',
  '‘®' => 'ë«Œ',
  '‘¯' => 'ë«�',
  '‘°' => 'ë«Ž',
  '‘±' => 'ë«�',
  '‘²' => 'ë«�',
  '‘³' => 'ë«‘',
  '‘´' => 'ë«’',
  '‘µ' => 'ë«“',
  '‘¶' => 'ë«”',
  '‘·' => 'ë«•',
  '‘¸' => 'ë«–',
  '‘¹' => 'ë«—',
  '‘º' => 'ë«š',
  '‘»' => 'ë«›',
  '‘¼' => 'ë«œ',
  '‘½' => 'ë«�',
  '‘¾' => 'ë«ž',
  '‘¿' => 'ë«Ÿ',
  '‘À' => 'ë« ',
  '‘Á' => 'ë«¡',
  '‘Â' => 'ë«¢',
  '‘Ã' => 'ë«£',
  '‘Ä' => 'ë«¤',
  '‘Å' => 'ë«¥',
  '‘Æ' => 'ë«¦',
  '‘Ç' => 'ë«§',
  '‘È' => 'ë«¨',
  '‘É' => 'ë«©',
  '‘Ê' => 'ë«ª',
  '‘Ë' => 'ë««',
  '‘Ì' => 'ë«¬',
  '‘Í' => 'ë«­',
  '‘Î' => 'ë«®',
  '‘Ï' => 'ë«¯',
  '‘Ð' => 'ë«°',
  '‘Ñ' => 'ë«±',
  '‘Ò' => 'ë«²',
  '‘Ó' => 'ë«³',
  '‘Ô' => 'ë«´',
  '‘Õ' => 'ë«µ',
  '‘Ö' => 'ë«¶',
  '‘×' => 'ë«·',
  '‘Ø' => 'ë«¸',
  '‘Ù' => 'ë«¹',
  '‘Ú' => 'ë«º',
  '‘Û' => 'ë«»',
  '‘Ü' => 'ë«½',
  '‘Ý' => 'ë«¾',
  '‘Þ' => 'ë«¿',
  '‘ß' => 'ë¬�',
  '‘à' => 'ë¬‚',
  '‘á' => 'ë¬ƒ',
  '‘â' => 'ë¬…',
  '‘ã' => 'ë¬†',
  '‘ä' => 'ë¬‡',
  '‘å' => 'ë¬ˆ',
  '‘æ' => 'ë¬‰',
  '‘ç' => 'ë¬Š',
  '‘è' => 'ë¬‹',
  '‘é' => 'ë¬Œ',
  '‘ê' => 'ë¬Ž',
  '‘ë' => 'ë¬�',
  '‘ì' => 'ë¬’',
  '‘í' => 'ë¬“',
  '‘î' => 'ë¬”',
  '‘ï' => 'ë¬•',
  '‘ð' => 'ë¬–',
  '‘ñ' => 'ë¬—',
  '‘ò' => 'ë¬™',
  '‘ó' => 'ë¬š',
  '‘ô' => 'ë¬›',
  '‘õ' => 'ë¬�',
  '‘ö' => 'ë¬ž',
  '‘÷' => 'ë¬Ÿ',
  '‘ø' => 'ë¬¡',
  '‘ù' => 'ë¬¢',
  '‘ú' => 'ë¬£',
  '‘û' => 'ë¬¤',
  '‘ü' => 'ë¬¥',
  '‘ý' => 'ë¬¦',
  '‘þ' => 'ë¬§',
  '’A' => 'ë¬¨',
  '’B' => 'ë¬ª',
  '’C' => 'ë¬¬',
  '’D' => 'ë¬­',
  '’E' => 'ë¬®',
  '’F' => 'ë¬¯',
  '’G' => 'ë¬°',
  '’H' => 'ë¬±',
  '’I' => 'ë¬²',
  '’J' => 'ë¬³',
  '’K' => 'ë¬·',
  '’L' => 'ë¬¹',
  '’M' => 'ë¬º',
  '’N' => 'ë¬¿',
  '’O' => 'ë­€',
  '’P' => 'ë­�',
  '’Q' => 'ë­‚',
  '’R' => 'ë­ƒ',
  '’S' => 'ë­†',
  '’T' => 'ë­ˆ',
  '’U' => 'ë­Š',
  '’V' => 'ë­‹',
  '’W' => 'ë­Œ',
  '’X' => 'ë­Ž',
  '’Y' => 'ë­‘',
  '’Z' => 'ë­’',
  '’a' => 'ë­“',
  '’b' => 'ë­•',
  '’c' => 'ë­–',
  '’d' => 'ë­—',
  '’e' => 'ë­™',
  '’f' => 'ë­š',
  '’g' => 'ë­›',
  '’h' => 'ë­œ',
  '’i' => 'ë­�',
  '’j' => 'ë­ž',
  '’k' => 'ë­Ÿ',
  '’l' => 'ë­ ',
  '’m' => 'ë­¢',
  '’n' => 'ë­¤',
  '’o' => 'ë­¥',
  '’p' => 'ë­¦',
  '’q' => 'ë­§',
  '’r' => 'ë­¨',
  '’s' => 'ë­©',
  '’t' => 'ë­ª',
  '’u' => 'ë­«',
  '’v' => 'ë­­',
  '’w' => 'ë­®',
  '’x' => 'ë­¯',
  '’y' => 'ë­°',
  '’z' => 'ë­±',
  '’�' => 'ë­²',
  '’‚' => 'ë­³',
  '’ƒ' => 'ë­´',
  '’„' => 'ë­µ',
  '’…' => 'ë­¶',
  '’†' => 'ë­·',
  '’‡' => 'ë­¸',
  '’ˆ' => 'ë­¹',
  '’‰' => 'ë­º',
  '’Š' => 'ë­»',
  '’‹' => 'ë­¼',
  '’Œ' => 'ë­½',
  '’�' => 'ë­¾',
  '’Ž' => 'ë­¿',
  '’�' => 'ë®€',
  '’�' => 'ë®�',
  '’‘' => 'ë®‚',
  '’’' => 'ë®ƒ',
  '’“' => 'ë®„',
  '’”' => 'ë®…',
  '’•' => 'ë®†',
  '’–' => 'ë®‡',
  '’—' => 'ë®‰',
  '’˜' => 'ë®Š',
  '’™' => 'ë®‹',
  '’š' => 'ë®�',
  '’›' => 'ë®Ž',
  '’œ' => 'ë®�',
  '’�' => 'ë®‘',
  '’ž' => 'ë®’',
  '’Ÿ' => 'ë®“',
  '’ ' => 'ë®”',
  '’¡' => 'ë®•',
  '’¢' => 'ë®–',
  '’£' => 'ë®—',
  '’¤' => 'ë®˜',
  '’¥' => 'ë®™',
  '’¦' => 'ë®š',
  '’§' => 'ë®›',
  '’¨' => 'ë®œ',
  '’©' => 'ë®�',
  '’ª' => 'ë®ž',
  '’«' => 'ë®Ÿ',
  '’¬' => 'ë® ',
  '’­' => 'ë®¡',
  '’®' => 'ë®¢',
  '’¯' => 'ë®£',
  '’°' => 'ë®¥',
  '’±' => 'ë®¦',
  '’²' => 'ë®§',
  '’³' => 'ë®©',
  '’´' => 'ë®ª',
  '’µ' => 'ë®«',
  '’¶' => 'ë®­',
  '’·' => 'ë®®',
  '’¸' => 'ë®¯',
  '’¹' => 'ë®°',
  '’º' => 'ë®±',
  '’»' => 'ë®²',
  '’¼' => 'ë®³',
  '’½' => 'ë®µ',
  '’¾' => 'ë®¶',
  '’¿' => 'ë®¸',
  '’À' => 'ë®¹',
  '’Á' => 'ë®º',
  '’Â' => 'ë®»',
  '’Ã' => 'ë®¼',
  '’Ä' => 'ë®½',
  '’Å' => 'ë®¾',
  '’Æ' => 'ë®¿',
  '’Ç' => 'ë¯�',
  '’È' => 'ë¯‚',
  '’É' => 'ë¯ƒ',
  '’Ê' => 'ë¯…',
  '’Ë' => 'ë¯†',
  '’Ì' => 'ë¯‡',
  '’Í' => 'ë¯‰',
  '’Î' => 'ë¯Š',
  '’Ï' => 'ë¯‹',
  '’Ð' => 'ë¯Œ',
  '’Ñ' => 'ë¯�',
  '’Ò' => 'ë¯Ž',
  '’Ó' => 'ë¯�',
  '’Ô' => 'ë¯‘',
  '’Õ' => 'ë¯’',
  '’Ö' => 'ë¯”',
  '’×' => 'ë¯•',
  '’Ø' => 'ë¯–',
  '’Ù' => 'ë¯—',
  '’Ú' => 'ë¯˜',
  '’Û' => 'ë¯™',
  '’Ü' => 'ë¯š',
  '’Ý' => 'ë¯›',
  '’Þ' => 'ë¯œ',
  '’ß' => 'ë¯�',
  '’à' => 'ë¯ž',
  '’á' => 'ë¯Ÿ',
  '’â' => 'ë¯ ',
  '’ã' => 'ë¯¡',
  '’ä' => 'ë¯¢',
  '’å' => 'ë¯£',
  '’æ' => 'ë¯¤',
  '’ç' => 'ë¯¥',
  '’è' => 'ë¯¦',
  '’é' => 'ë¯§',
  '’ê' => 'ë¯¨',
  '’ë' => 'ë¯©',
  '’ì' => 'ë¯ª',
  '’í' => 'ë¯«',
  '’î' => 'ë¯¬',
  '’ï' => 'ë¯­',
  '’ð' => 'ë¯®',
  '’ñ' => 'ë¯¯',
  '’ò' => 'ë¯°',
  '’ó' => 'ë¯±',
  '’ô' => 'ë¯²',
  '’õ' => 'ë¯³',
  '’ö' => 'ë¯´',
  '’÷' => 'ë¯µ',
  '’ø' => 'ë¯¶',
  '’ù' => 'ë¯·',
  '’ú' => 'ë¯º',
  '’û' => 'ë¯»',
  '’ü' => 'ë¯½',
  '’ý' => 'ë¯¾',
  '’þ' => 'ë°�',
  '“A' => 'ë°ƒ',
  '“B' => 'ë°„',
  '“C' => 'ë°…',
  '“D' => 'ë°†',
  '“E' => 'ë°‡',
  '“F' => 'ë°Š',
  '“G' => 'ë°Ž',
  '“H' => 'ë°�',
  '“I' => 'ë°’',
  '“J' => 'ë°“',
  '“K' => 'ë°™',
  '“L' => 'ë°š',
  '“M' => 'ë° ',
  '“N' => 'ë°¡',
  '“O' => 'ë°¢',
  '“P' => 'ë°£',
  '“Q' => 'ë°¦',
  '“R' => 'ë°¨',
  '“S' => 'ë°ª',
  '“T' => 'ë°«',
  '“U' => 'ë°¬',
  '“V' => 'ë°®',
  '“W' => 'ë°¯',
  '“X' => 'ë°²',
  '“Y' => 'ë°³',
  '“Z' => 'ë°µ',
  '“a' => 'ë°¶',
  '“b' => 'ë°·',
  '“c' => 'ë°¹',
  '“d' => 'ë°º',
  '“e' => 'ë°»',
  '“f' => 'ë°¼',
  '“g' => 'ë°½',
  '“h' => 'ë°¾',
  '“i' => 'ë°¿',
  '“j' => 'ë±‚',
  '“k' => 'ë±†',
  '“l' => 'ë±‡',
  '“m' => 'ë±ˆ',
  '“n' => 'ë±Š',
  '“o' => 'ë±‹',
  '“p' => 'ë±Ž',
  '“q' => 'ë±�',
  '“r' => 'ë±‘',
  '“s' => 'ë±’',
  '“t' => 'ë±“',
  '“u' => 'ë±”',
  '“v' => 'ë±•',
  '“w' => 'ë±–',
  '“x' => 'ë±—',
  '“y' => 'ë±˜',
  '“z' => 'ë±™',
  '“�' => 'ë±š',
  '“‚' => 'ë±›',
  '“ƒ' => 'ë±œ',
  '“„' => 'ë±ž',
  '“…' => 'ë±Ÿ',
  '“†' => 'ë± ',
  '“‡' => 'ë±¡',
  '“ˆ' => 'ë±¢',
  '“‰' => 'ë±£',
  '“Š' => 'ë±¤',
  '“‹' => 'ë±¥',
  '“Œ' => 'ë±¦',
  '“�' => 'ë±§',
  '“Ž' => 'ë±¨',
  '“�' => 'ë±©',
  '“�' => 'ë±ª',
  '“‘' => 'ë±«',
  '“’' => 'ë±¬',
  '““' => 'ë±­',
  '“”' => 'ë±®',
  '“•' => 'ë±¯',
  '“–' => 'ë±°',
  '“—' => 'ë±±',
  '“˜' => 'ë±²',
  '“™' => 'ë±³',
  '“š' => 'ë±´',
  '“›' => 'ë±µ',
  '“œ' => 'ë±¶',
  '“�' => 'ë±·',
  '“ž' => 'ë±¸',
  '“Ÿ' => 'ë±¹',
  '“ ' => 'ë±º',
  '“¡' => 'ë±»',
  '“¢' => 'ë±¼',
  '“£' => 'ë±½',
  '“¤' => 'ë±¾',
  '“¥' => 'ë±¿',
  '“¦' => 'ë²€',
  '“§' => 'ë²�',
  '“¨' => 'ë²‚',
  '“©' => 'ë²ƒ',
  '“ª' => 'ë²†',
  '“«' => 'ë²‡',
  '“¬' => 'ë²‰',
  '“­' => 'ë²Š',
  '“®' => 'ë²�',
  '“¯' => 'ë²�',
  '“°' => 'ë²�',
  '“±' => 'ë²‘',
  '“²' => 'ë²’',
  '“³' => 'ë²“',
  '“´' => 'ë²–',
  '“µ' => 'ë²˜',
  '“¶' => 'ë²›',
  '“·' => 'ë²œ',
  '“¸' => 'ë²�',
  '“¹' => 'ë²ž',
  '“º' => 'ë²Ÿ',
  '“»' => 'ë²¢',
  '“¼' => 'ë²£',
  '“½' => 'ë²¥',
  '“¾' => 'ë²¦',
  '“¿' => 'ë²©',
  '“À' => 'ë²ª',
  '“Á' => 'ë²«',
  '“Â' => 'ë²¬',
  '“Ã' => 'ë²­',
  '“Ä' => 'ë²®',
  '“Å' => 'ë²¯',
  '“Æ' => 'ë²²',
  '“Ç' => 'ë²¶',
  '“È' => 'ë²·',
  '“É' => 'ë²¸',
  '“Ê' => 'ë²¹',
  '“Ë' => 'ë²º',
  '“Ì' => 'ë²»',
  '“Í' => 'ë²¾',
  '“Î' => 'ë²¿',
  '“Ï' => 'ë³�',
  '“Ð' => 'ë³‚',
  '“Ñ' => 'ë³ƒ',
  '“Ò' => 'ë³…',
  '“Ó' => 'ë³†',
  '“Ô' => 'ë³‡',
  '“Õ' => 'ë³ˆ',
  '“Ö' => 'ë³‰',
  '“×' => 'ë³Š',
  '“Ø' => 'ë³‹',
  '“Ù' => 'ë³Œ',
  '“Ú' => 'ë³Ž',
  '“Û' => 'ë³’',
  '“Ü' => 'ë³“',
  '“Ý' => 'ë³”',
  '“Þ' => 'ë³–',
  '“ß' => 'ë³—',
  '“à' => 'ë³™',
  '“á' => 'ë³š',
  '“â' => 'ë³›',
  '“ã' => 'ë³�',
  '“ä' => 'ë³ž',
  '“å' => 'ë³Ÿ',
  '“æ' => 'ë³ ',
  '“ç' => 'ë³¡',
  '“è' => 'ë³¢',
  '“é' => 'ë³£',
  '“ê' => 'ë³¤',
  '“ë' => 'ë³¥',
  '“ì' => 'ë³¦',
  '“í' => 'ë³§',
  '“î' => 'ë³¨',
  '“ï' => 'ë³©',
  '“ð' => 'ë³ª',
  '“ñ' => 'ë³«',
  '“ò' => 'ë³¬',
  '“ó' => 'ë³­',
  '“ô' => 'ë³®',
  '“õ' => 'ë³¯',
  '“ö' => 'ë³°',
  '“÷' => 'ë³±',
  '“ø' => 'ë³²',
  '“ù' => 'ë³³',
  '“ú' => 'ë³·',
  '“û' => 'ë³¹',
  '“ü' => 'ë³º',
  '“ý' => 'ë³»',
  '“þ' => 'ë³½',
  '”A' => 'ë³¾',
  '”B' => 'ë³¿',
  '”C' => 'ë´€',
  '”D' => 'ë´�',
  '”E' => 'ë´‚',
  '”F' => 'ë´ƒ',
  '”G' => 'ë´†',
  '”H' => 'ë´ˆ',
  '”I' => 'ë´Š',
  '”J' => 'ë´‹',
  '”K' => 'ë´Œ',
  '”L' => 'ë´�',
  '”M' => 'ë´Ž',
  '”N' => 'ë´�',
  '”O' => 'ë´‘',
  '”P' => 'ë´’',
  '”Q' => 'ë´“',
  '”R' => 'ë´•',
  '”S' => 'ë´–',
  '”T' => 'ë´—',
  '”U' => 'ë´˜',
  '”V' => 'ë´™',
  '”W' => 'ë´š',
  '”X' => 'ë´›',
  '”Y' => 'ë´œ',
  '”Z' => 'ë´�',
  '”a' => 'ë´ž',
  '”b' => 'ë´Ÿ',
  '”c' => 'ë´ ',
  '”d' => 'ë´¡',
  '”e' => 'ë´¢',
  '”f' => 'ë´£',
  '”g' => 'ë´¥',
  '”h' => 'ë´¦',
  '”i' => 'ë´§',
  '”j' => 'ë´¨',
  '”k' => 'ë´©',
  '”l' => 'ë´ª',
  '”m' => 'ë´«',
  '”n' => 'ë´­',
  '”o' => 'ë´®',
  '”p' => 'ë´¯',
  '”q' => 'ë´°',
  '”r' => 'ë´±',
  '”s' => 'ë´²',
  '”t' => 'ë´³',
  '”u' => 'ë´´',
  '”v' => 'ë´µ',
  '”w' => 'ë´¶',
  '”x' => 'ë´·',
  '”y' => 'ë´¸',
  '”z' => 'ë´¹',
  '”�' => 'ë´º',
  '”‚' => 'ë´»',
  '”ƒ' => 'ë´¼',
  '”„' => 'ë´½',
  '”…' => 'ë´¾',
  '”†' => 'ë´¿',
  '”‡' => 'ëµ�',
  '”ˆ' => 'ëµ‚',
  '”‰' => 'ëµƒ',
  '”Š' => 'ëµ„',
  '”‹' => 'ëµ…',
  '”Œ' => 'ëµ†',
  '”�' => 'ëµ‡',
  '”Ž' => 'ëµŠ',
  '”�' => 'ëµ‹',
  '”�' => 'ëµ�',
  '”‘' => 'ëµŽ',
  '”’' => 'ëµ�',
  '”“' => 'ëµ‘',
  '””' => 'ëµ’',
  '”•' => 'ëµ“',
  '”–' => 'ëµ”',
  '”—' => 'ëµ•',
  '”˜' => 'ëµ–',
  '”™' => 'ëµ—',
  '”š' => 'ëµš',
  '”›' => 'ëµ›',
  '”œ' => 'ëµœ',
  '”�' => 'ëµ�',
  '”ž' => 'ëµž',
  '”Ÿ' => 'ëµŸ',
  '” ' => 'ëµ ',
  '”¡' => 'ëµ¡',
  '”¢' => 'ëµ¢',
  '”£' => 'ëµ£',
  '”¤' => 'ëµ¥',
  '”¥' => 'ëµ¦',
  '”¦' => 'ëµ§',
  '”§' => 'ëµ©',
  '”¨' => 'ëµª',
  '”©' => 'ëµ«',
  '”ª' => 'ëµ¬',
  '”«' => 'ëµ­',
  '”¬' => 'ëµ®',
  '”­' => 'ëµ¯',
  '”®' => 'ëµ°',
  '”¯' => 'ëµ±',
  '”°' => 'ëµ²',
  '”±' => 'ëµ³',
  '”²' => 'ëµ´',
  '”³' => 'ëµµ',
  '”´' => 'ëµ¶',
  '”µ' => 'ëµ·',
  '”¶' => 'ëµ¸',
  '”·' => 'ëµ¹',
  '”¸' => 'ëµº',
  '”¹' => 'ëµ»',
  '”º' => 'ëµ¼',
  '”»' => 'ëµ½',
  '”¼' => 'ëµ¾',
  '”½' => 'ëµ¿',
  '”¾' => 'ë¶‚',
  '”¿' => 'ë¶ƒ',
  '”À' => 'ë¶…',
  '”Á' => 'ë¶†',
  '”Â' => 'ë¶‹',
  '”Ã' => 'ë¶Œ',
  '”Ä' => 'ë¶�',
  '”Å' => 'ë¶Ž',
  '”Æ' => 'ë¶�',
  '”Ç' => 'ë¶’',
  '”È' => 'ë¶”',
  '”É' => 'ë¶–',
  '”Ê' => 'ë¶—',
  '”Ë' => 'ë¶˜',
  '”Ì' => 'ë¶›',
  '”Í' => 'ë¶�',
  '”Î' => 'ë¶ž',
  '”Ï' => 'ë¶Ÿ',
  '”Ð' => 'ë¶ ',
  '”Ñ' => 'ë¶¡',
  '”Ò' => 'ë¶¢',
  '”Ó' => 'ë¶£',
  '”Ô' => 'ë¶¥',
  '”Õ' => 'ë¶¦',
  '”Ö' => 'ë¶§',
  '”×' => 'ë¶¨',
  '”Ø' => 'ë¶©',
  '”Ù' => 'ë¶ª',
  '”Ú' => 'ë¶«',
  '”Û' => 'ë¶¬',
  '”Ü' => 'ë¶­',
  '”Ý' => 'ë¶®',
  '”Þ' => 'ë¶¯',
  '”ß' => 'ë¶±',
  '”à' => 'ë¶²',
  '”á' => 'ë¶³',
  '”â' => 'ë¶´',
  '”ã' => 'ë¶µ',
  '”ä' => 'ë¶¶',
  '”å' => 'ë¶·',
  '”æ' => 'ë¶¹',
  '”ç' => 'ë¶º',
  '”è' => 'ë¶»',
  '”é' => 'ë¶¼',
  '”ê' => 'ë¶½',
  '”ë' => 'ë¶¾',
  '”ì' => 'ë¶¿',
  '”í' => 'ë·€',
  '”î' => 'ë·�',
  '”ï' => 'ë·‚',
  '”ð' => 'ë·ƒ',
  '”ñ' => 'ë·„',
  '”ò' => 'ë·…',
  '”ó' => 'ë·†',
  '”ô' => 'ë·‡',
  '”õ' => 'ë·ˆ',
  '”ö' => 'ë·‰',
  '”÷' => 'ë·Š',
  '”ø' => 'ë·‹',
  '”ù' => 'ë·Œ',
  '”ú' => 'ë·�',
  '”û' => 'ë·Ž',
  '”ü' => 'ë·�',
  '”ý' => 'ë·�',
  '”þ' => 'ë·‘',
  '•A' => 'ë·’',
  '•B' => 'ë·“',
  '•C' => 'ë·–',
  '•D' => 'ë·—',
  '•E' => 'ë·™',
  '•F' => 'ë·š',
  '•G' => 'ë·›',
  '•H' => 'ë·�',
  '•I' => 'ë·ž',
  '•J' => 'ë·Ÿ',
  '•K' => 'ë· ',
  '•L' => 'ë·¡',
  '•M' => 'ë·¢',
  '•N' => 'ë·£',
  '•O' => 'ë·¤',
  '•P' => 'ë·¥',
  '•Q' => 'ë·¦',
  '•R' => 'ë·§',
  '•S' => 'ë·¨',
  '•T' => 'ë·ª',
  '•U' => 'ë·«',
  '•V' => 'ë·¬',
  '•W' => 'ë·­',
  '•X' => 'ë·®',
  '•Y' => 'ë·¯',
  '•Z' => 'ë·±',
  '•a' => 'ë·²',
  '•b' => 'ë·³',
  '•c' => 'ë·µ',
  '•d' => 'ë·¶',
  '•e' => 'ë··',
  '•f' => 'ë·¹',
  '•g' => 'ë·º',
  '•h' => 'ë·»',
  '•i' => 'ë·¼',
  '•j' => 'ë·½',
  '•k' => 'ë·¾',
  '•l' => 'ë·¿',
  '•m' => 'ë¸�',
  '•n' => 'ë¸‚',
  '•o' => 'ë¸„',
  '•p' => 'ë¸†',
  '•q' => 'ë¸‡',
  '•r' => 'ë¸ˆ',
  '•s' => 'ë¸‰',
  '•t' => 'ë¸Š',
  '•u' => 'ë¸‹',
  '•v' => 'ë¸Ž',
  '•w' => 'ë¸�',
  '•x' => 'ë¸‘',
  '•y' => 'ë¸’',
  '•z' => 'ë¸“',
  '•�' => 'ë¸•',
  '•‚' => 'ë¸–',
  '•ƒ' => 'ë¸—',
  '•„' => 'ë¸˜',
  '•…' => 'ë¸™',
  '•†' => 'ë¸š',
  '•‡' => 'ë¸›',
  '•ˆ' => 'ë¸ž',
  '•‰' => 'ë¸ ',
  '•Š' => 'ë¸¡',
  '•‹' => 'ë¸¢',
  '•Œ' => 'ë¸£',
  '•�' => 'ë¸¤',
  '•Ž' => 'ë¸¥',
  '•�' => 'ë¸¦',
  '•�' => 'ë¸§',
  '•‘' => 'ë¸¨',
  '•’' => 'ë¸©',
  '•“' => 'ë¸ª',
  '•”' => 'ë¸«',
  '••' => 'ë¸¬',
  '•–' => 'ë¸­',
  '•—' => 'ë¸®',
  '•˜' => 'ë¸¯',
  '•™' => 'ë¸°',
  '•š' => 'ë¸±',
  '•›' => 'ë¸²',
  '•œ' => 'ë¸³',
  '•�' => 'ë¸´',
  '•ž' => 'ë¸µ',
  '•Ÿ' => 'ë¸¶',
  '• ' => 'ë¸·',
  '•¡' => 'ë¸¸',
  '•¢' => 'ë¸¹',
  '•£' => 'ë¸º',
  '•¤' => 'ë¸»',
  '•¥' => 'ë¸¼',
  '•¦' => 'ë¸½',
  '•§' => 'ë¸¾',
  '•¨' => 'ë¸¿',
  '•©' => 'ë¹€',
  '•ª' => 'ë¹�',
  '•«' => 'ë¹‚',
  '•¬' => 'ë¹ƒ',
  '•­' => 'ë¹†',
  '•®' => 'ë¹‡',
  '•¯' => 'ë¹‰',
  '•°' => 'ë¹Š',
  '•±' => 'ë¹‹',
  '•²' => 'ë¹�',
  '•³' => 'ë¹�',
  '•´' => 'ë¹�',
  '•µ' => 'ë¹‘',
  '•¶' => 'ë¹’',
  '•·' => 'ë¹“',
  '•¸' => 'ë¹–',
  '•¹' => 'ë¹˜',
  '•º' => 'ë¹œ',
  '•»' => 'ë¹�',
  '•¼' => 'ë¹ž',
  '•½' => 'ë¹Ÿ',
  '•¾' => 'ë¹¢',
  '•¿' => 'ë¹£',
  '•À' => 'ë¹¥',
  '•Á' => 'ë¹¦',
  '•Â' => 'ë¹§',
  '•Ã' => 'ë¹©',
  '•Ä' => 'ë¹«',
  '•Å' => 'ë¹¬',
  '•Æ' => 'ë¹­',
  '•Ç' => 'ë¹®',
  '•È' => 'ë¹¯',
  '•É' => 'ë¹²',
  '•Ê' => 'ë¹¶',
  '•Ë' => 'ë¹·',
  '•Ì' => 'ë¹¸',
  '•Í' => 'ë¹¹',
  '•Î' => 'ë¹º',
  '•Ï' => 'ë¹¾',
  '•Ð' => 'ë¹¿',
  '•Ñ' => 'ëº�',
  '•Ò' => 'ëº‚',
  '•Ó' => 'ëºƒ',
  '•Ô' => 'ëº…',
  '•Õ' => 'ëº†',
  '•Ö' => 'ëº‡',
  '•×' => 'ëºˆ',
  '•Ø' => 'ëº‰',
  '•Ù' => 'ëºŠ',
  '•Ú' => 'ëº‹',
  '•Û' => 'ëºŽ',
  '•Ü' => 'ëº’',
  '•Ý' => 'ëº“',
  '•Þ' => 'ëº”',
  '•ß' => 'ëº•',
  '•à' => 'ëº–',
  '•á' => 'ëº—',
  '•â' => 'ëºš',
  '•ã' => 'ëº›',
  '•ä' => 'ëºœ',
  '•å' => 'ëº�',
  '•æ' => 'ëºž',
  '•ç' => 'ëºŸ',
  '•è' => 'ëº ',
  '•é' => 'ëº¡',
  '•ê' => 'ëº¢',
  '•ë' => 'ëº£',
  '•ì' => 'ëº¤',
  '•í' => 'ëº¥',
  '•î' => 'ëº¦',
  '•ï' => 'ëº§',
  '•ð' => 'ëº©',
  '•ñ' => 'ëºª',
  '•ò' => 'ëº«',
  '•ó' => 'ëº¬',
  '•ô' => 'ëº­',
  '•õ' => 'ëº®',
  '•ö' => 'ëº¯',
  '•÷' => 'ëº°',
  '•ø' => 'ëº±',
  '•ù' => 'ëº²',
  '•ú' => 'ëº³',
  '•û' => 'ëº´',
  '•ü' => 'ëºµ',
  '•ý' => 'ëº¶',
  '•þ' => 'ëº·',
  '–A' => 'ëº¸',
  '–B' => 'ëº¹',
  '–C' => 'ëºº',
  '–D' => 'ëº»',
  '–E' => 'ëº¼',
  '–F' => 'ëº½',
  '–G' => 'ëº¾',
  '–H' => 'ëº¿',
  '–I' => 'ë»€',
  '–J' => 'ë»�',
  '–K' => 'ë»‚',
  '–L' => 'ë»ƒ',
  '–M' => 'ë»„',
  '–N' => 'ë»…',
  '–O' => 'ë»†',
  '–P' => 'ë»‡',
  '–Q' => 'ë»ˆ',
  '–R' => 'ë»‰',
  '–S' => 'ë»Š',
  '–T' => 'ë»‹',
  '–U' => 'ë»Œ',
  '–V' => 'ë»�',
  '–W' => 'ë»Ž',
  '–X' => 'ë»�',
  '–Y' => 'ë»’',
  '–Z' => 'ë»“',
  '–a' => 'ë»•',
  '–b' => 'ë»–',
  '–c' => 'ë»™',
  '–d' => 'ë»š',
  '–e' => 'ë»›',
  '–f' => 'ë»œ',
  '–g' => 'ë»�',
  '–h' => 'ë»ž',
  '–i' => 'ë»Ÿ',
  '–j' => 'ë»¡',
  '–k' => 'ë»¢',
  '–l' => 'ë»¦',
  '–m' => 'ë»§',
  '–n' => 'ë»¨',
  '–o' => 'ë»©',
  '–p' => 'ë»ª',
  '–q' => 'ë»«',
  '–r' => 'ë»­',
  '–s' => 'ë»®',
  '–t' => 'ë»¯',
  '–u' => 'ë»°',
  '–v' => 'ë»±',
  '–w' => 'ë»²',
  '–x' => 'ë»³',
  '–y' => 'ë»´',
  '–z' => 'ë»µ',
  '–�' => 'ë»¶',
  '–‚' => 'ë»·',
  '–ƒ' => 'ë»¸',
  '–„' => 'ë»¹',
  '–…' => 'ë»º',
  '–†' => 'ë»»',
  '–‡' => 'ë»¼',
  '–ˆ' => 'ë»½',
  '–‰' => 'ë»¾',
  '–Š' => 'ë»¿',
  '–‹' => 'ë¼€',
  '–Œ' => 'ë¼‚',
  '–�' => 'ë¼ƒ',
  '–Ž' => 'ë¼„',
  '–�' => 'ë¼…',
  '–�' => 'ë¼†',
  '–‘' => 'ë¼‡',
  '–’' => 'ë¼Š',
  '–“' => 'ë¼‹',
  '–”' => 'ë¼Œ',
  '–•' => 'ë¼�',
  '––' => 'ë¼Ž',
  '–—' => 'ë¼�',
  '–˜' => 'ë¼�',
  '–™' => 'ë¼‘',
  '–š' => 'ë¼’',
  '–›' => 'ë¼“',
  '–œ' => 'ë¼”',
  '–�' => 'ë¼•',
  '–ž' => 'ë¼–',
  '–Ÿ' => 'ë¼—',
  '– ' => 'ë¼š',
  '–¡' => 'ë¼ž',
  '–¢' => 'ë¼Ÿ',
  '–£' => 'ë¼ ',
  '–¤' => 'ë¼¡',
  '–¥' => 'ë¼¢',
  '–¦' => 'ë¼£',
  '–§' => 'ë¼¤',
  '–¨' => 'ë¼¥',
  '–©' => 'ë¼¦',
  '–ª' => 'ë¼§',
  '–«' => 'ë¼¨',
  '–¬' => 'ë¼©',
  '–­' => 'ë¼ª',
  '–®' => 'ë¼«',
  '–¯' => 'ë¼¬',
  '–°' => 'ë¼­',
  '–±' => 'ë¼®',
  '–²' => 'ë¼¯',
  '–³' => 'ë¼°',
  '–´' => 'ë¼±',
  '–µ' => 'ë¼²',
  '–¶' => 'ë¼³',
  '–·' => 'ë¼´',
  '–¸' => 'ë¼µ',
  '–¹' => 'ë¼¶',
  '–º' => 'ë¼·',
  '–»' => 'ë¼¸',
  '–¼' => 'ë¼¹',
  '–½' => 'ë¼º',
  '–¾' => 'ë¼»',
  '–¿' => 'ë¼¼',
  '–À' => 'ë¼½',
  '–Á' => 'ë¼¾',
  '–Â' => 'ë¼¿',
  '–Ã' => 'ë½‚',
  '–Ä' => 'ë½ƒ',
  '–Å' => 'ë½…',
  '–Æ' => 'ë½†',
  '–Ç' => 'ë½‡',
  '–È' => 'ë½‰',
  '–É' => 'ë½Š',
  '–Ê' => 'ë½‹',
  '–Ë' => 'ë½Œ',
  '–Ì' => 'ë½�',
  '–Í' => 'ë½Ž',
  '–Î' => 'ë½�',
  '–Ï' => 'ë½’',
  '–Ð' => 'ë½“',
  '–Ñ' => 'ë½”',
  '–Ò' => 'ë½–',
  '–Ó' => 'ë½—',
  '–Ô' => 'ë½˜',
  '–Õ' => 'ë½™',
  '–Ö' => 'ë½š',
  '–×' => 'ë½›',
  '–Ø' => 'ë½œ',
  '–Ù' => 'ë½�',
  '–Ú' => 'ë½ž',
  '–Û' => 'ë½Ÿ',
  '–Ü' => 'ë½ ',
  '–Ý' => 'ë½¡',
  '–Þ' => 'ë½¢',
  '–ß' => 'ë½£',
  '–à' => 'ë½¤',
  '–á' => 'ë½¥',
  '–â' => 'ë½¦',
  '–ã' => 'ë½§',
  '–ä' => 'ë½¨',
  '–å' => 'ë½©',
  '–æ' => 'ë½ª',
  '–ç' => 'ë½«',
  '–è' => 'ë½¬',
  '–é' => 'ë½­',
  '–ê' => 'ë½®',
  '–ë' => 'ë½¯',
  '–ì' => 'ë½°',
  '–í' => 'ë½±',
  '–î' => 'ë½²',
  '–ï' => 'ë½³',
  '–ð' => 'ë½´',
  '–ñ' => 'ë½µ',
  '–ò' => 'ë½¶',
  '–ó' => 'ë½·',
  '–ô' => 'ë½¸',
  '–õ' => 'ë½¹',
  '–ö' => 'ë½º',
  '–÷' => 'ë½»',
  '–ø' => 'ë½¼',
  '–ù' => 'ë½½',
  '–ú' => 'ë½¾',
  '–û' => 'ë½¿',
  '–ü' => 'ë¾€',
  '–ý' => 'ë¾�',
  '–þ' => 'ë¾‚',
  '—A' => 'ë¾ƒ',
  '—B' => 'ë¾„',
  '—C' => 'ë¾…',
  '—D' => 'ë¾†',
  '—E' => 'ë¾‡',
  '—F' => 'ë¾ˆ',
  '—G' => 'ë¾‰',
  '—H' => 'ë¾Š',
  '—I' => 'ë¾‹',
  '—J' => 'ë¾Œ',
  '—K' => 'ë¾�',
  '—L' => 'ë¾Ž',
  '—M' => 'ë¾�',
  '—N' => 'ë¾�',
  '—O' => 'ë¾‘',
  '—P' => 'ë¾’',
  '—Q' => 'ë¾“',
  '—R' => 'ë¾•',
  '—S' => 'ë¾–',
  '—T' => 'ë¾—',
  '—U' => 'ë¾˜',
  '—V' => 'ë¾™',
  '—W' => 'ë¾š',
  '—X' => 'ë¾›',
  '—Y' => 'ë¾œ',
  '—Z' => 'ë¾�',
  '—a' => 'ë¾ž',
  '—b' => 'ë¾Ÿ',
  '—c' => 'ë¾ ',
  '—d' => 'ë¾¡',
  '—e' => 'ë¾¢',
  '—f' => 'ë¾£',
  '—g' => 'ë¾¤',
  '—h' => 'ë¾¥',
  '—i' => 'ë¾¦',
  '—j' => 'ë¾§',
  '—k' => 'ë¾¨',
  '—l' => 'ë¾©',
  '—m' => 'ë¾ª',
  '—n' => 'ë¾«',
  '—o' => 'ë¾¬',
  '—p' => 'ë¾­',
  '—q' => 'ë¾®',
  '—r' => 'ë¾¯',
  '—s' => 'ë¾±',
  '—t' => 'ë¾²',
  '—u' => 'ë¾³',
  '—v' => 'ë¾´',
  '—w' => 'ë¾µ',
  '—x' => 'ë¾¶',
  '—y' => 'ë¾·',
  '—z' => 'ë¾¸',
  '—�' => 'ë¾¹',
  '—‚' => 'ë¾º',
  '—ƒ' => 'ë¾»',
  '—„' => 'ë¾¼',
  '—…' => 'ë¾½',
  '—†' => 'ë¾¾',
  '—‡' => 'ë¾¿',
  '—ˆ' => 'ë¿€',
  '—‰' => 'ë¿�',
  '—Š' => 'ë¿‚',
  '—‹' => 'ë¿ƒ',
  '—Œ' => 'ë¿„',
  '—�' => 'ë¿†',
  '—Ž' => 'ë¿‡',
  '—�' => 'ë¿ˆ',
  '—�' => 'ë¿‰',
  '—‘' => 'ë¿Š',
  '—’' => 'ë¿‹',
  '—“' => 'ë¿Ž',
  '—”' => 'ë¿�',
  '—•' => 'ë¿‘',
  '—–' => 'ë¿’',
  '——' => 'ë¿“',
  '—˜' => 'ë¿•',
  '—™' => 'ë¿–',
  '—š' => 'ë¿—',
  '—›' => 'ë¿˜',
  '—œ' => 'ë¿™',
  '—�' => 'ë¿š',
  '—ž' => 'ë¿›',
  '—Ÿ' => 'ë¿�',
  '— ' => 'ë¿ž',
  '—¡' => 'ë¿ ',
  '—¢' => 'ë¿¢',
  '—£' => 'ë¿£',
  '—¤' => 'ë¿¤',
  '—¥' => 'ë¿¥',
  '—¦' => 'ë¿¦',
  '—§' => 'ë¿§',
  '—¨' => 'ë¿¨',
  '—©' => 'ë¿©',
  '—ª' => 'ë¿ª',
  '—«' => 'ë¿«',
  '—¬' => 'ë¿¬',
  '—­' => 'ë¿­',
  '—®' => 'ë¿®',
  '—¯' => 'ë¿¯',
  '—°' => 'ë¿°',
  '—±' => 'ë¿±',
  '—²' => 'ë¿²',
  '—³' => 'ë¿³',
  '—´' => 'ë¿´',
  '—µ' => 'ë¿µ',
  '—¶' => 'ë¿¶',
  '—·' => 'ë¿·',
  '—¸' => 'ë¿¸',
  '—¹' => 'ë¿¹',
  '—º' => 'ë¿º',
  '—»' => 'ë¿»',
  '—¼' => 'ë¿¼',
  '—½' => 'ë¿½',
  '—¾' => 'ë¿¾',
  '—¿' => 'ë¿¿',
  '—À' => 'ì€€',
  '—Á' => 'ì€�',
  '—Â' => 'ì€‚',
  '—Ã' => 'ì€ƒ',
  '—Ä' => 'ì€„',
  '—Å' => 'ì€…',
  '—Æ' => 'ì€†',
  '—Ç' => 'ì€‡',
  '—È' => 'ì€ˆ',
  '—É' => 'ì€‰',
  '—Ê' => 'ì€Š',
  '—Ë' => 'ì€‹',
  '—Ì' => 'ì€Œ',
  '—Í' => 'ì€�',
  '—Î' => 'ì€Ž',
  '—Ï' => 'ì€�',
  '—Ð' => 'ì€�',
  '—Ñ' => 'ì€‘',
  '—Ò' => 'ì€’',
  '—Ó' => 'ì€“',
  '—Ô' => 'ì€”',
  '—Õ' => 'ì€•',
  '—Ö' => 'ì€–',
  '—×' => 'ì€—',
  '—Ø' => 'ì€˜',
  '—Ù' => 'ì€™',
  '—Ú' => 'ì€š',
  '—Û' => 'ì€›',
  '—Ü' => 'ì€œ',
  '—Ý' => 'ì€�',
  '—Þ' => 'ì€ž',
  '—ß' => 'ì€Ÿ',
  '—à' => 'ì€ ',
  '—á' => 'ì€¡',
  '—â' => 'ì€¢',
  '—ã' => 'ì€£',
  '—ä' => 'ì€¤',
  '—å' => 'ì€¥',
  '—æ' => 'ì€¦',
  '—ç' => 'ì€§',
  '—è' => 'ì€¨',
  '—é' => 'ì€©',
  '—ê' => 'ì€ª',
  '—ë' => 'ì€«',
  '—ì' => 'ì€¬',
  '—í' => 'ì€­',
  '—î' => 'ì€®',
  '—ï' => 'ì€¯',
  '—ð' => 'ì€°',
  '—ñ' => 'ì€±',
  '—ò' => 'ì€²',
  '—ó' => 'ì€³',
  '—ô' => 'ì€´',
  '—õ' => 'ì€µ',
  '—ö' => 'ì€¶',
  '—÷' => 'ì€·',
  '—ø' => 'ì€¸',
  '—ù' => 'ì€¹',
  '—ú' => 'ì€º',
  '—û' => 'ì€»',
  '—ü' => 'ì€½',
  '—ý' => 'ì€¾',
  '—þ' => 'ì€¿',
  '˜A' => 'ì�€',
  '˜B' => 'ì��',
  '˜C' => 'ì�‚',
  '˜D' => 'ì�ƒ',
  '˜E' => 'ì�„',
  '˜F' => 'ì�…',
  '˜G' => 'ì�†',
  '˜H' => 'ì�‡',
  '˜I' => 'ì�ˆ',
  '˜J' => 'ì�‰',
  '˜K' => 'ì�Š',
  '˜L' => 'ì�‹',
  '˜M' => 'ì�Œ',
  '˜N' => 'ì��',
  '˜O' => 'ì�Ž',
  '˜P' => 'ì��',
  '˜Q' => 'ì��',
  '˜R' => 'ì�’',
  '˜S' => 'ì�“',
  '˜T' => 'ì�”',
  '˜U' => 'ì�•',
  '˜V' => 'ì�–',
  '˜W' => 'ì�—',
  '˜X' => 'ì�™',
  '˜Y' => 'ì�š',
  '˜Z' => 'ì�›',
  '˜a' => 'ì��',
  '˜b' => 'ì�ž',
  '˜c' => 'ì�Ÿ',
  '˜d' => 'ì�¡',
  '˜e' => 'ì�¢',
  '˜f' => 'ì�£',
  '˜g' => 'ì�¤',
  '˜h' => 'ì�¥',
  '˜i' => 'ì�¦',
  '˜j' => 'ì�§',
  '˜k' => 'ì�ª',
  '˜l' => 'ì�«',
  '˜m' => 'ì�¬',
  '˜n' => 'ì�­',
  '˜o' => 'ì�®',
  '˜p' => 'ì�¯',
  '˜q' => 'ì�°',
  '˜r' => 'ì�±',
  '˜s' => 'ì�²',
  '˜t' => 'ì�³',
  '˜u' => 'ì�´',
  '˜v' => 'ì�µ',
  '˜w' => 'ì�¶',
  '˜x' => 'ì�·',
  '˜y' => 'ì�¸',
  '˜z' => 'ì�¹',
  '˜�' => 'ì�º',
  '˜‚' => 'ì�»',
  '˜ƒ' => 'ì�¼',
  '˜„' => 'ì�½',
  '˜…' => 'ì�¾',
  '˜†' => 'ì�¿',
  '˜‡' => 'ì‚€',
  '˜ˆ' => 'ì‚�',
  '˜‰' => 'ì‚‚',
  '˜Š' => 'ì‚ƒ',
  '˜‹' => 'ì‚„',
  '˜Œ' => 'ì‚…',
  '˜�' => 'ì‚†',
  '˜Ž' => 'ì‚‡',
  '˜�' => 'ì‚ˆ',
  '˜�' => 'ì‚‰',
  '˜‘' => 'ì‚Š',
  '˜’' => 'ì‚‹',
  '˜“' => 'ì‚Œ',
  '˜”' => 'ì‚�',
  '˜•' => 'ì‚Ž',
  '˜–' => 'ì‚�',
  '˜—' => 'ì‚’',
  '˜˜' => 'ì‚“',
  '˜™' => 'ì‚•',
  '˜š' => 'ì‚–',
  '˜›' => 'ì‚—',
  '˜œ' => 'ì‚™',
  '˜�' => 'ì‚š',
  '˜ž' => 'ì‚›',
  '˜Ÿ' => 'ì‚œ',
  '˜ ' => 'ì‚�',
  '˜¡' => 'ì‚ž',
  '˜¢' => 'ì‚Ÿ',
  '˜£' => 'ì‚¢',
  '˜¤' => 'ì‚¤',
  '˜¥' => 'ì‚¦',
  '˜¦' => 'ì‚§',
  '˜§' => 'ì‚¨',
  '˜¨' => 'ì‚©',
  '˜©' => 'ì‚ª',
  '˜ª' => 'ì‚«',
  '˜«' => 'ì‚®',
  '˜¬' => 'ì‚±',
  '˜­' => 'ì‚²',
  '˜®' => 'ì‚·',
  '˜¯' => 'ì‚¸',
  '˜°' => 'ì‚¹',
  '˜±' => 'ì‚º',
  '˜²' => 'ì‚»',
  '˜³' => 'ì‚¾',
  '˜´' => 'ìƒ‚',
  '˜µ' => 'ìƒƒ',
  '˜¶' => 'ìƒ„',
  '˜·' => 'ìƒ†',
  '˜¸' => 'ìƒ‡',
  '˜¹' => 'ìƒŠ',
  '˜º' => 'ìƒ‹',
  '˜»' => 'ìƒ�',
  '˜¼' => 'ìƒŽ',
  '˜½' => 'ìƒ�',
  '˜¾' => 'ìƒ‘',
  '˜¿' => 'ìƒ’',
  '˜À' => 'ìƒ“',
  '˜Á' => 'ìƒ”',
  '˜Â' => 'ìƒ•',
  '˜Ã' => 'ìƒ–',
  '˜Ä' => 'ìƒ—',
  '˜Å' => 'ìƒš',
  '˜Æ' => 'ìƒž',
  '˜Ç' => 'ìƒŸ',
  '˜È' => 'ìƒ ',
  '˜É' => 'ìƒ¡',
  '˜Ê' => 'ìƒ¢',
  '˜Ë' => 'ìƒ£',
  '˜Ì' => 'ìƒ¦',
  '˜Í' => 'ìƒ§',
  '˜Î' => 'ìƒ©',
  '˜Ï' => 'ìƒª',
  '˜Ð' => 'ìƒ«',
  '˜Ñ' => 'ìƒ­',
  '˜Ò' => 'ìƒ®',
  '˜Ó' => 'ìƒ¯',
  '˜Ô' => 'ìƒ°',
  '˜Õ' => 'ìƒ±',
  '˜Ö' => 'ìƒ²',
  '˜×' => 'ìƒ³',
  '˜Ø' => 'ìƒ¶',
  '˜Ù' => 'ìƒ¸',
  '˜Ú' => 'ìƒº',
  '˜Û' => 'ìƒ»',
  '˜Ü' => 'ìƒ¼',
  '˜Ý' => 'ìƒ½',
  '˜Þ' => 'ìƒ¾',
  '˜ß' => 'ìƒ¿',
  '˜à' => 'ì„�',
  '˜á' => 'ì„‚',
  '˜â' => 'ì„ƒ',
  '˜ã' => 'ì„…',
  '˜ä' => 'ì„†',
  '˜å' => 'ì„‡',
  '˜æ' => 'ì„‰',
  '˜ç' => 'ì„Š',
  '˜è' => 'ì„‹',
  '˜é' => 'ì„Œ',
  '˜ê' => 'ì„�',
  '˜ë' => 'ì„Ž',
  '˜ì' => 'ì„�',
  '˜í' => 'ì„‘',
  '˜î' => 'ì„’',
  '˜ï' => 'ì„“',
  '˜ð' => 'ì„”',
  '˜ñ' => 'ì„–',
  '˜ò' => 'ì„—',
  '˜ó' => 'ì„˜',
  '˜ô' => 'ì„™',
  '˜õ' => 'ì„š',
  '˜ö' => 'ì„›',
  '˜÷' => 'ì„¡',
  '˜ø' => 'ì„¢',
  '˜ù' => 'ì„¥',
  '˜ú' => 'ì„¨',
  '˜û' => 'ì„©',
  '˜ü' => 'ì„ª',
  '˜ý' => 'ì„«',
  '˜þ' => 'ì„®',
  '™A' => 'ì„²',
  '™B' => 'ì„³',
  '™C' => 'ì„´',
  '™D' => 'ì„µ',
  '™E' => 'ì„·',
  '™F' => 'ì„º',
  '™G' => 'ì„»',
  '™H' => 'ì„½',
  '™I' => 'ì„¾',
  '™J' => 'ì„¿',
  '™K' => 'ì…�',
  '™L' => 'ì…‚',
  '™M' => 'ì…ƒ',
  '™N' => 'ì…„',
  '™O' => 'ì……',
  '™P' => 'ì…†',
  '™Q' => 'ì…‡',
  '™R' => 'ì…Š',
  '™S' => 'ì…Ž',
  '™T' => 'ì…�',
  '™U' => 'ì…�',
  '™V' => 'ì…‘',
  '™W' => 'ì…’',
  '™X' => 'ì…“',
  '™Y' => 'ì…–',
  '™Z' => 'ì…—',
  '™a' => 'ì…™',
  '™b' => 'ì…š',
  '™c' => 'ì…›',
  '™d' => 'ì…�',
  '™e' => 'ì…ž',
  '™f' => 'ì…Ÿ',
  '™g' => 'ì… ',
  '™h' => 'ì…¡',
  '™i' => 'ì…¢',
  '™j' => 'ì…£',
  '™k' => 'ì…¦',
  '™l' => 'ì…ª',
  '™m' => 'ì…«',
  '™n' => 'ì…¬',
  '™o' => 'ì…­',
  '™p' => 'ì…®',
  '™q' => 'ì…¯',
  '™r' => 'ì…±',
  '™s' => 'ì…²',
  '™t' => 'ì…³',
  '™u' => 'ì…µ',
  '™v' => 'ì…¶',
  '™w' => 'ì…·',
  '™x' => 'ì…¹',
  '™y' => 'ì…º',
  '™z' => 'ì…»',
  '™�' => 'ì…¼',
  '™‚' => 'ì…½',
  '™ƒ' => 'ì…¾',
  '™„' => 'ì…¿',
  '™…' => 'ì†€',
  '™†' => 'ì†�',
  '™‡' => 'ì†‚',
  '™ˆ' => 'ì†ƒ',
  '™‰' => 'ì†„',
  '™Š' => 'ì††',
  '™‹' => 'ì†‡',
  '™Œ' => 'ì†ˆ',
  '™�' => 'ì†‰',
  '™Ž' => 'ì†Š',
  '™�' => 'ì†‹',
  '™�' => 'ì†�',
  '™‘' => 'ì†‘',
  '™’' => 'ì†’',
  '™“' => 'ì†“',
  '™”' => 'ì†•',
  '™•' => 'ì†—',
  '™–' => 'ì†˜',
  '™—' => 'ì†™',
  '™˜' => 'ì†š',
  '™™' => 'ì†›',
  '™š' => 'ì†ž',
  '™›' => 'ì† ',
  '™œ' => 'ì†¢',
  '™�' => 'ì†£',
  '™ž' => 'ì†¤',
  '™Ÿ' => 'ì†¦',
  '™ ' => 'ì†§',
  '™¡' => 'ì†ª',
  '™¢' => 'ì†«',
  '™£' => 'ì†­',
  '™¤' => 'ì†®',
  '™¥' => 'ì†¯',
  '™¦' => 'ì†±',
  '™§' => 'ì†²',
  '™¨' => 'ì†³',
  '™©' => 'ì†´',
  '™ª' => 'ì†µ',
  '™«' => 'ì†¶',
  '™¬' => 'ì†·',
  '™­' => 'ì†¸',
  '™®' => 'ì†¹',
  '™¯' => 'ì†º',
  '™°' => 'ì†»',
  '™±' => 'ì†¼',
  '™²' => 'ì†¾',
  '™³' => 'ì†¿',
  '™´' => 'ì‡€',
  '™µ' => 'ì‡�',
  '™¶' => 'ì‡‚',
  '™·' => 'ì‡ƒ',
  '™¸' => 'ì‡…',
  '™¹' => 'ì‡†',
  '™º' => 'ì‡‡',
  '™»' => 'ì‡‰',
  '™¼' => 'ì‡Š',
  '™½' => 'ì‡‹',
  '™¾' => 'ì‡�',
  '™¿' => 'ì‡Ž',
  '™À' => 'ì‡�',
  '™Á' => 'ì‡�',
  '™Â' => 'ì‡‘',
  '™Ã' => 'ì‡’',
  '™Ä' => 'ì‡“',
  '™Å' => 'ì‡•',
  '™Æ' => 'ì‡–',
  '™Ç' => 'ì‡™',
  '™È' => 'ì‡š',
  '™É' => 'ì‡›',
  '™Ê' => 'ì‡œ',
  '™Ë' => 'ì‡�',
  '™Ì' => 'ì‡ž',
  '™Í' => 'ì‡Ÿ',
  '™Î' => 'ì‡¡',
  '™Ï' => 'ì‡¢',
  '™Ð' => 'ì‡£',
  '™Ñ' => 'ì‡¥',
  '™Ò' => 'ì‡¦',
  '™Ó' => 'ì‡§',
  '™Ô' => 'ì‡©',
  '™Õ' => 'ì‡ª',
  '™Ö' => 'ì‡«',
  '™×' => 'ì‡¬',
  '™Ø' => 'ì‡­',
  '™Ù' => 'ì‡®',
  '™Ú' => 'ì‡¯',
  '™Û' => 'ì‡²',
  '™Ü' => 'ì‡´',
  '™Ý' => 'ì‡µ',
  '™Þ' => 'ì‡¶',
  '™ß' => 'ì‡·',
  '™à' => 'ì‡¸',
  '™á' => 'ì‡¹',
  '™â' => 'ì‡º',
  '™ã' => 'ì‡»',
  '™ä' => 'ì‡¾',
  '™å' => 'ì‡¿',
  '™æ' => 'ìˆ�',
  '™ç' => 'ìˆ‚',
  '™è' => 'ìˆƒ',
  '™é' => 'ìˆ…',
  '™ê' => 'ìˆ†',
  '™ë' => 'ìˆ‡',
  '™ì' => 'ìˆˆ',
  '™í' => 'ìˆ‰',
  '™î' => 'ìˆŠ',
  '™ï' => 'ìˆ‹',
  '™ð' => 'ìˆŽ',
  '™ñ' => 'ìˆ�',
  '™ò' => 'ìˆ’',
  '™ó' => 'ìˆ“',
  '™ô' => 'ìˆ”',
  '™õ' => 'ìˆ•',
  '™ö' => 'ìˆ–',
  '™÷' => 'ìˆ—',
  '™ø' => 'ìˆš',
  '™ù' => 'ìˆ›',
  '™ú' => 'ìˆ�',
  '™û' => 'ìˆž',
  '™ü' => 'ìˆ¡',
  '™ý' => 'ìˆ¢',
  '™þ' => 'ìˆ£',
  'šA' => 'ìˆ¤',
  'šB' => 'ìˆ¥',
  'šC' => 'ìˆ¦',
  'šD' => 'ìˆ§',
  'šE' => 'ìˆª',
  'šF' => 'ìˆ¬',
  'šG' => 'ìˆ®',
  'šH' => 'ìˆ°',
  'šI' => 'ìˆ³',
  'šJ' => 'ìˆµ',
  'šK' => 'ìˆ¶',
  'šL' => 'ìˆ·',
  'šM' => 'ìˆ¸',
  'šN' => 'ìˆ¹',
  'šO' => 'ìˆº',
  'šP' => 'ìˆ»',
  'šQ' => 'ìˆ¼',
  'šR' => 'ìˆ½',
  'šS' => 'ìˆ¾',
  'šT' => 'ìˆ¿',
  'šU' => 'ì‰€',
  'šV' => 'ì‰�',
  'šW' => 'ì‰‚',
  'šX' => 'ì‰ƒ',
  'šY' => 'ì‰„',
  'šZ' => 'ì‰…',
  'ša' => 'ì‰†',
  'šb' => 'ì‰‡',
  'šc' => 'ì‰‰',
  'šd' => 'ì‰Š',
  'še' => 'ì‰‹',
  'šf' => 'ì‰Œ',
  'šg' => 'ì‰�',
  'šh' => 'ì‰Ž',
  'ši' => 'ì‰�',
  'šj' => 'ì‰’',
  'šk' => 'ì‰“',
  'šl' => 'ì‰•',
  'šm' => 'ì‰–',
  'šn' => 'ì‰—',
  'šo' => 'ì‰™',
  'šp' => 'ì‰š',
  'šq' => 'ì‰›',
  'šr' => 'ì‰œ',
  'šs' => 'ì‰�',
  'št' => 'ì‰ž',
  'šu' => 'ì‰Ÿ',
  'šv' => 'ì‰¡',
  'šw' => 'ì‰¢',
  'šx' => 'ì‰£',
  'šy' => 'ì‰¤',
  'šz' => 'ì‰¦',
  'š�' => 'ì‰§',
  'š‚' => 'ì‰¨',
  'šƒ' => 'ì‰©',
  'š„' => 'ì‰ª',
  'š…' => 'ì‰«',
  'š†' => 'ì‰®',
  'š‡' => 'ì‰¯',
  'šˆ' => 'ì‰±',
  'š‰' => 'ì‰²',
  'šŠ' => 'ì‰³',
  'š‹' => 'ì‰µ',
  'šŒ' => 'ì‰¶',
  'š�' => 'ì‰·',
  'šŽ' => 'ì‰¸',
  'š�' => 'ì‰¹',
  'š�' => 'ì‰º',
  'š‘' => 'ì‰»',
  'š’' => 'ì‰¾',
  'š“' => 'ìŠ€',
  'š”' => 'ìŠ‚',
  'š•' => 'ìŠƒ',
  'š–' => 'ìŠ„',
  'š—' => 'ìŠ…',
  'š˜' => 'ìŠ†',
  'š™' => 'ìŠ‡',
  'šš' => 'ìŠŠ',
  'š›' => 'ìŠ‹',
  'šœ' => 'ìŠŒ',
  'š�' => 'ìŠ�',
  'šž' => 'ìŠŽ',
  'šŸ' => 'ìŠ�',
  'š ' => 'ìŠ‘',
  'š¡' => 'ìŠ’',
  'š¢' => 'ìŠ“',
  'š£' => 'ìŠ”',
  'š¤' => 'ìŠ•',
  'š¥' => 'ìŠ–',
  'š¦' => 'ìŠ—',
  'š§' => 'ìŠ™',
  'š¨' => 'ìŠš',
  'š©' => 'ìŠœ',
  'šª' => 'ìŠž',
  'š«' => 'ìŠŸ',
  'š¬' => 'ìŠ ',
  'š­' => 'ìŠ¡',
  'š®' => 'ìŠ¢',
  'š¯' => 'ìŠ£',
  'š°' => 'ìŠ¦',
  'š±' => 'ìŠ§',
  'š²' => 'ìŠ©',
  'š³' => 'ìŠª',
  'š´' => 'ìŠ«',
  'šµ' => 'ìŠ®',
  'š¶' => 'ìŠ¯',
  'š·' => 'ìŠ°',
  'š¸' => 'ìŠ±',
  'š¹' => 'ìŠ²',
  'šº' => 'ìŠ³',
  'š»' => 'ìŠ¶',
  'š¼' => 'ìŠ¸',
  'š½' => 'ìŠº',
  'š¾' => 'ìŠ»',
  'š¿' => 'ìŠ¼',
  'šÀ' => 'ìŠ½',
  'šÁ' => 'ìŠ¾',
  'šÂ' => 'ìŠ¿',
  'šÃ' => 'ì‹€',
  'šÄ' => 'ì‹�',
  'šÅ' => 'ì‹‚',
  'šÆ' => 'ì‹ƒ',
  'šÇ' => 'ì‹„',
  'šÈ' => 'ì‹…',
  'šÉ' => 'ì‹†',
  'šÊ' => 'ì‹‡',
  'šË' => 'ì‹ˆ',
  'šÌ' => 'ì‹‰',
  'šÍ' => 'ì‹Š',
  'šÎ' => 'ì‹‹',
  'šÏ' => 'ì‹Œ',
  'šÐ' => 'ì‹�',
  'šÑ' => 'ì‹Ž',
  'šÒ' => 'ì‹�',
  'šÓ' => 'ì‹�',
  'šÔ' => 'ì‹‘',
  'šÕ' => 'ì‹’',
  'šÖ' => 'ì‹“',
  'š×' => 'ì‹”',
  'šØ' => 'ì‹•',
  'šÙ' => 'ì‹–',
  'šÚ' => 'ì‹—',
  'šÛ' => 'ì‹˜',
  'šÜ' => 'ì‹™',
  'šÝ' => 'ì‹š',
  'šÞ' => 'ì‹›',
  'šß' => 'ì‹ž',
  'šà' => 'ì‹Ÿ',
  'šá' => 'ì‹¡',
  'šâ' => 'ì‹¢',
  'šã' => 'ì‹¥',
  'šä' => 'ì‹¦',
  'šå' => 'ì‹§',
  'šæ' => 'ì‹¨',
  'šç' => 'ì‹©',
  'šè' => 'ì‹ª',
  'šé' => 'ì‹®',
  'šê' => 'ì‹°',
  'šë' => 'ì‹²',
  'šì' => 'ì‹³',
  'ší' => 'ì‹´',
  'šî' => 'ì‹µ',
  'šï' => 'ì‹·',
  'šð' => 'ì‹º',
  'šñ' => 'ì‹½',
  'šò' => 'ì‹¾',
  'šó' => 'ì‹¿',
  'šô' => 'ìŒ�',
  'šõ' => 'ìŒ‚',
  'šö' => 'ìŒƒ',
  'š÷' => 'ìŒ„',
  'šø' => 'ìŒ…',
  'šù' => 'ìŒ†',
  'šú' => 'ìŒ‡',
  'šû' => 'ìŒŠ',
  'šü' => 'ìŒ‹',
  'šý' => 'ìŒŽ',
  'šþ' => 'ìŒ�',
  '›A' => 'ìŒ�',
  '›B' => 'ìŒ‘',
  '›C' => 'ìŒ’',
  '›D' => 'ìŒ–',
  '›E' => 'ìŒ—',
  '›F' => 'ìŒ™',
  '›G' => 'ìŒš',
  '›H' => 'ìŒ›',
  '›I' => 'ìŒ�',
  '›J' => 'ìŒž',
  '›K' => 'ìŒŸ',
  '›L' => 'ìŒ ',
  '›M' => 'ìŒ¡',
  '›N' => 'ìŒ¢',
  '›O' => 'ìŒ£',
  '›P' => 'ìŒ¦',
  '›Q' => 'ìŒ§',
  '›R' => 'ìŒª',
  '›S' => 'ìŒ«',
  '›T' => 'ìŒ¬',
  '›U' => 'ìŒ­',
  '›V' => 'ìŒ®',
  '›W' => 'ìŒ¯',
  '›X' => 'ìŒ°',
  '›Y' => 'ìŒ±',
  '›Z' => 'ìŒ²',
  '›a' => 'ìŒ³',
  '›b' => 'ìŒ´',
  '›c' => 'ìŒµ',
  '›d' => 'ìŒ¶',
  '›e' => 'ìŒ·',
  '›f' => 'ìŒ¸',
  '›g' => 'ìŒ¹',
  '›h' => 'ìŒº',
  '›i' => 'ìŒ»',
  '›j' => 'ìŒ¼',
  '›k' => 'ìŒ½',
  '›l' => 'ìŒ¾',
  '›m' => 'ìŒ¿',
  '›n' => 'ì�€',
  '›o' => 'ì��',
  '›p' => 'ì�‚',
  '›q' => 'ì�ƒ',
  '›r' => 'ì�„',
  '›s' => 'ì�†',
  '›t' => 'ì�‡',
  '›u' => 'ì�ˆ',
  '›v' => 'ì�‰',
  '›w' => 'ì�Š',
  '›x' => 'ì�‹',
  '›y' => 'ì�Œ',
  '›z' => 'ì��',
  '›�' => 'ì�Ž',
  '›‚' => 'ì��',
  '›ƒ' => 'ì��',
  '›„' => 'ì�‘',
  '›…' => 'ì�’',
  '›†' => 'ì�“',
  '›‡' => 'ì�”',
  '›ˆ' => 'ì�•',
  '›‰' => 'ì�–',
  '›Š' => 'ì�—',
  '›‹' => 'ì�˜',
  '›Œ' => 'ì�™',
  '›�' => 'ì�š',
  '›Ž' => 'ì�›',
  '›�' => 'ì�œ',
  '›�' => 'ì��',
  '›‘' => 'ì�ž',
  '›’' => 'ì�Ÿ',
  '›“' => 'ì� ',
  '›”' => 'ì�¡',
  '›•' => 'ì�¢',
  '›–' => 'ì�£',
  '›—' => 'ì�¤',
  '›˜' => 'ì�¥',
  '›™' => 'ì�¦',
  '›š' => 'ì�§',
  '››' => 'ì�ª',
  '›œ' => 'ì�«',
  '›�' => 'ì�­',
  '›ž' => 'ì�®',
  '›Ÿ' => 'ì�¯',
  '› ' => 'ì�±',
  '›¡' => 'ì�³',
  '›¢' => 'ì�´',
  '›£' => 'ì�µ',
  '›¤' => 'ì�¶',
  '›¥' => 'ì�·',
  '›¦' => 'ì�º',
  '›§' => 'ì�»',
  '›¨' => 'ì�¾',
  '›©' => 'ì�¿',
  '›ª' => 'ìŽ€',
  '›«' => 'ìŽ�',
  '›¬' => 'ìŽ‚',
  '›­' => 'ìŽƒ',
  '›®' => 'ìŽ…',
  '›¯' => 'ìŽ†',
  '›°' => 'ìŽ‡',
  '›±' => 'ìŽ‰',
  '›²' => 'ìŽŠ',
  '›³' => 'ìŽ‹',
  '›´' => 'ìŽ�',
  '›µ' => 'ìŽŽ',
  '›¶' => 'ìŽ�',
  '›·' => 'ìŽ�',
  '›¸' => 'ìŽ‘',
  '›¹' => 'ìŽ’',
  '›º' => 'ìŽ“',
  '›»' => 'ìŽ”',
  '›¼' => 'ìŽ•',
  '›½' => 'ìŽ–',
  '›¾' => 'ìŽ—',
  '›¿' => 'ìŽ˜',
  '›À' => 'ìŽ™',
  '›Á' => 'ìŽš',
  '›Â' => 'ìŽ›',
  '›Ã' => 'ìŽœ',
  '›Ä' => 'ìŽ�',
  '›Å' => 'ìŽž',
  '›Æ' => 'ìŽŸ',
  '›Ç' => 'ìŽ ',
  '›È' => 'ìŽ¡',
  '›É' => 'ìŽ¢',
  '›Ê' => 'ìŽ£',
  '›Ë' => 'ìŽ¤',
  '›Ì' => 'ìŽ¥',
  '›Í' => 'ìŽ¦',
  '›Î' => 'ìŽ§',
  '›Ï' => 'ìŽ¨',
  '›Ð' => 'ìŽ©',
  '›Ñ' => 'ìŽª',
  '›Ò' => 'ìŽ«',
  '›Ó' => 'ìŽ¬',
  '›Ô' => 'ìŽ­',
  '›Õ' => 'ìŽ®',
  '›Ö' => 'ìŽ¯',
  '›×' => 'ìŽ°',
  '›Ø' => 'ìŽ±',
  '›Ù' => 'ìŽ²',
  '›Ú' => 'ìŽ³',
  '›Û' => 'ìŽ´',
  '›Ü' => 'ìŽµ',
  '›Ý' => 'ìŽ¶',
  '›Þ' => 'ìŽ·',
  '›ß' => 'ìŽ¸',
  '›à' => 'ìŽ¹',
  '›á' => 'ìŽº',
  '›â' => 'ìŽ»',
  '›ã' => 'ìŽ¼',
  '›ä' => 'ìŽ½',
  '›å' => 'ìŽ¾',
  '›æ' => 'ìŽ¿',
  '›ç' => 'ì��',
  '›è' => 'ì�‚',
  '›é' => 'ì�ƒ',
  '›ê' => 'ì�„',
  '›ë' => 'ì�…',
  '›ì' => 'ì�†',
  '›í' => 'ì�‡',
  '›î' => 'ì�ˆ',
  '›ï' => 'ì�‰',
  '›ð' => 'ì�Š',
  '›ñ' => 'ì�‹',
  '›ò' => 'ì�Œ',
  '›ó' => 'ì��',
  '›ô' => 'ì�Ž',
  '›õ' => 'ì��',
  '›ö' => 'ì��',
  '›÷' => 'ì�‘',
  '›ø' => 'ì�’',
  '›ù' => 'ì�“',
  '›ú' => 'ì�”',
  '›û' => 'ì�•',
  '›ü' => 'ì�–',
  '›ý' => 'ì�—',
  '›þ' => 'ì�š',
  'œA' => 'ì�›',
  'œB' => 'ì��',
  'œC' => 'ì�ž',
  'œD' => 'ì�¡',
  'œE' => 'ì�£',
  'œF' => 'ì�¤',
  'œG' => 'ì�¥',
  'œH' => 'ì�¦',
  'œI' => 'ì�§',
  'œJ' => 'ì�ª',
  'œK' => 'ì�«',
  'œL' => 'ì�¬',
  'œM' => 'ì�®',
  'œN' => 'ì�¯',
  'œO' => 'ì�°',
  'œP' => 'ì�±',
  'œQ' => 'ì�²',
  'œR' => 'ì�³',
  'œS' => 'ì�¶',
  'œT' => 'ì�·',
  'œU' => 'ì�¹',
  'œV' => 'ì�º',
  'œW' => 'ì�»',
  'œX' => 'ì�¼',
  'œY' => 'ì�½',
  'œZ' => 'ì�¾',
  'œa' => 'ì�¿',
  'œb' => 'ì�€',
  'œc' => 'ì��',
  'œd' => 'ì�‚',
  'œe' => 'ì�ƒ',
  'œf' => 'ì�„',
  'œg' => 'ì�…',
  'œh' => 'ì�†',
  'œi' => 'ì�‡',
  'œj' => 'ì�‰',
  'œk' => 'ì�Š',
  'œl' => 'ì�‹',
  'œm' => 'ì�Œ',
  'œn' => 'ì��',
  'œo' => 'ì�Ž',
  'œp' => 'ì��',
  'œq' => 'ì�‘',
  'œr' => 'ì�’',
  'œs' => 'ì�“',
  'œt' => 'ì�”',
  'œu' => 'ì�•',
  'œv' => 'ì�–',
  'œw' => 'ì�—',
  'œx' => 'ì�˜',
  'œy' => 'ì�™',
  'œz' => 'ì�š',
  'œ�' => 'ì�›',
  'œ‚' => 'ì�œ',
  'œƒ' => 'ì��',
  'œ„' => 'ì�ž',
  'œ…' => 'ì�Ÿ',
  'œ†' => 'ì� ',
  'œ‡' => 'ì�¡',
  'œˆ' => 'ì�¢',
  'œ‰' => 'ì�£',
  'œŠ' => 'ì�¥',
  'œ‹' => 'ì�¦',
  'œŒ' => 'ì�§',
  'œ�' => 'ì�¨',
  'œŽ' => 'ì�©',
  'œ�' => 'ì�ª',
  'œ�' => 'ì�«',
  'œ‘' => 'ì�­',
  'œ’' => 'ì�®',
  'œ“' => 'ì�¯',
  'œ”' => 'ì�±',
  'œ•' => 'ì�²',
  'œ–' => 'ì�³',
  'œ—' => 'ì�µ',
  'œ˜' => 'ì�¶',
  'œ™' => 'ì�·',
  'œš' => 'ì�¸',
  'œ›' => 'ì�¹',
  'œœ' => 'ì�º',
  'œ�' => 'ì�»',
  'œž' => 'ì�¾',
  'œŸ' => 'ì�¿',
  'œ ' => 'ì‘€',
  'œ¡' => 'ì‘�',
  'œ¢' => 'ì‘‚',
  'œ£' => 'ì‘ƒ',
  'œ¤' => 'ì‘„',
  'œ¥' => 'ì‘…',
  'œ¦' => 'ì‘†',
  'œ§' => 'ì‘‡',
  'œ¨' => 'ì‘‰',
  'œ©' => 'ì‘Š',
  'œª' => 'ì‘‹',
  'œ«' => 'ì‘Œ',
  'œ¬' => 'ì‘�',
  'œ­' => 'ì‘Ž',
  'œ®' => 'ì‘�',
  'œ¯' => 'ì‘�',
  'œ°' => 'ì‘‘',
  'œ±' => 'ì‘’',
  'œ²' => 'ì‘“',
  'œ³' => 'ì‘”',
  'œ´' => 'ì‘•',
  'œµ' => 'ì‘–',
  'œ¶' => 'ì‘—',
  'œ·' => 'ì‘˜',
  'œ¸' => 'ì‘™',
  'œ¹' => 'ì‘š',
  'œº' => 'ì‘›',
  'œ»' => 'ì‘œ',
  'œ¼' => 'ì‘�',
  'œ½' => 'ì‘ž',
  'œ¾' => 'ì‘Ÿ',
  'œ¿' => 'ì‘ ',
  'œÀ' => 'ì‘¡',
  'œÁ' => 'ì‘¢',
  'œÂ' => 'ì‘£',
  'œÃ' => 'ì‘¦',
  'œÄ' => 'ì‘§',
  'œÅ' => 'ì‘©',
  'œÆ' => 'ì‘ª',
  'œÇ' => 'ì‘«',
  'œÈ' => 'ì‘­',
  'œÉ' => 'ì‘®',
  'œÊ' => 'ì‘¯',
  'œË' => 'ì‘°',
  'œÌ' => 'ì‘±',
  'œÍ' => 'ì‘²',
  'œÎ' => 'ì‘³',
  'œÏ' => 'ì‘¶',
  'œÐ' => 'ì‘·',
  'œÑ' => 'ì‘¸',
  'œÒ' => 'ì‘º',
  'œÓ' => 'ì‘»',
  'œÔ' => 'ì‘¼',
  'œÕ' => 'ì‘½',
  'œÖ' => 'ì‘¾',
  'œ×' => 'ì‘¿',
  'œØ' => 'ì’�',
  'œÙ' => 'ì’‚',
  'œÚ' => 'ì’ƒ',
  'œÛ' => 'ì’„',
  'œÜ' => 'ì’…',
  'œÝ' => 'ì’†',
  'œÞ' => 'ì’‡',
  'œß' => 'ì’ˆ',
  'œà' => 'ì’‰',
  'œá' => 'ì’Š',
  'œâ' => 'ì’‹',
  'œã' => 'ì’Œ',
  'œä' => 'ì’�',
  'œå' => 'ì’Ž',
  'œæ' => 'ì’�',
  'œç' => 'ì’�',
  'œè' => 'ì’‘',
  'œé' => 'ì’’',
  'œê' => 'ì’“',
  'œë' => 'ì’•',
  'œì' => 'ì’–',
  'œí' => 'ì’—',
  'œî' => 'ì’˜',
  'œï' => 'ì’™',
  'œð' => 'ì’š',
  'œñ' => 'ì’›',
  'œò' => 'ì’�',
  'œó' => 'ì’ž',
  'œô' => 'ì’Ÿ',
  'œõ' => 'ì’ ',
  'œö' => 'ì’¡',
  'œ÷' => 'ì’¢',
  'œø' => 'ì’£',
  'œù' => 'ì’¤',
  'œú' => 'ì’¥',
  'œû' => 'ì’¦',
  'œü' => 'ì’§',
  'œý' => 'ì’¨',
  'œþ' => 'ì’©',
  '�A' => 'ì’ª',
  '�B' => 'ì’«',
  '�C' => 'ì’¬',
  '�D' => 'ì’­',
  '�E' => 'ì’®',
  '�F' => 'ì’¯',
  '�G' => 'ì’°',
  '�H' => 'ì’±',
  '�I' => 'ì’²',
  '�J' => 'ì’³',
  '�K' => 'ì’´',
  '�L' => 'ì’µ',
  '�M' => 'ì’¶',
  '�N' => 'ì’·',
  '�O' => 'ì’¹',
  '�P' => 'ì’º',
  '�Q' => 'ì’»',
  '�R' => 'ì’½',
  '�S' => 'ì’¾',
  '�T' => 'ì’¿',
  '�U' => 'ì“€',
  '�V' => 'ì“�',
  '�W' => 'ì“‚',
  '�X' => 'ì“ƒ',
  '�Y' => 'ì“„',
  '�Z' => 'ì“…',
  '�a' => 'ì“†',
  '�b' => 'ì“‡',
  '�c' => 'ì“ˆ',
  '�d' => 'ì“‰',
  '�e' => 'ì“Š',
  '�f' => 'ì“‹',
  '�g' => 'ì“Œ',
  '�h' => 'ì“�',
  '�i' => 'ì“Ž',
  '�j' => 'ì“�',
  '�k' => 'ì“�',
  '�l' => 'ì“‘',
  '�m' => 'ì“’',
  '�n' => 'ì““',
  '�o' => 'ì“”',
  '�p' => 'ì“•',
  '�q' => 'ì“–',
  '�r' => 'ì“—',
  '�s' => 'ì“˜',
  '�t' => 'ì“™',
  '�u' => 'ì“š',
  '�v' => 'ì“›',
  '�w' => 'ì“œ',
  '�x' => 'ì“�',
  '�y' => 'ì“ž',
  '�z' => 'ì“Ÿ',
  '��' => 'ì“ ',
  '�‚' => 'ì“¡',
  '�ƒ' => 'ì“¢',
  '�„' => 'ì“£',
  '�…' => 'ì“¤',
  '�†' => 'ì“¥',
  '�‡' => 'ì“¦',
  '�ˆ' => 'ì“§',
  '�‰' => 'ì“¨',
  '�Š' => 'ì“ª',
  '�‹' => 'ì“«',
  '�Œ' => 'ì“¬',
  '��' => 'ì“­',
  '�Ž' => 'ì“®',
  '��' => 'ì“¯',
  '��' => 'ì“²',
  '�‘' => 'ì“³',
  '�’' => 'ì“µ',
  '�“' => 'ì“¶',
  '�”' => 'ì“·',
  '�•' => 'ì“¹',
  '�–' => 'ì“»',
  '�—' => 'ì“¼',
  '�˜' => 'ì“½',
  '�™' => 'ì“¾',
  '�š' => 'ì”‚',
  '�›' => 'ì”ƒ',
  '�œ' => 'ì”„',
  '��' => 'ì”…',
  '�ž' => 'ì”†',
  '�Ÿ' => 'ì”‡',
  '� ' => 'ì”ˆ',
  '�¡' => 'ì”‰',
  '�¢' => 'ì”Š',
  '�£' => 'ì”‹',
  '�¤' => 'ì”�',
  '�¥' => 'ì”Ž',
  '�¦' => 'ì”�',
  '�§' => 'ì”‘',
  '�¨' => 'ì”’',
  '�©' => 'ì”“',
  '�ª' => 'ì”•',
  '�«' => 'ì”–',
  '�¬' => 'ì”—',
  '�­' => 'ì”˜',
  '�®' => 'ì”™',
  '�¯' => 'ì”š',
  '�°' => 'ì”›',
  '�±' => 'ì”�',
  '�²' => 'ì”ž',
  '�³' => 'ì”Ÿ',
  '�´' => 'ì” ',
  '�µ' => 'ì”¡',
  '�¶' => 'ì”¢',
  '�·' => 'ì”£',
  '�¸' => 'ì”¤',
  '�¹' => 'ì”¥',
  '�º' => 'ì”¦',
  '�»' => 'ì”§',
  '�¼' => 'ì”ª',
  '�½' => 'ì”«',
  '�¾' => 'ì”­',
  '�¿' => 'ì”®',
  '�À' => 'ì”¯',
  '�Á' => 'ì”±',
  '�Â' => 'ì”²',
  '�Ã' => 'ì”³',
  '�Ä' => 'ì”´',
  '�Å' => 'ì”µ',
  '�Æ' => 'ì”¶',
  '�Ç' => 'ì”·',
  '�È' => 'ì”º',
  '�É' => 'ì”¼',
  '�Ê' => 'ì”¾',
  '�Ë' => 'ì”¿',
  '�Ì' => 'ì•€',
  '�Í' => 'ì•�',
  '�Î' => 'ì•‚',
  '�Ï' => 'ì•ƒ',
  '�Ð' => 'ì•†',
  '�Ñ' => 'ì•‡',
  '�Ò' => 'ì•‹',
  '�Ó' => 'ì•�',
  '�Ô' => 'ì•�',
  '�Õ' => 'ì•‘',
  '�Ö' => 'ì•’',
  '�×' => 'ì•–',
  '�Ø' => 'ì•š',
  '�Ù' => 'ì•›',
  '�Ú' => 'ì•œ',
  '�Û' => 'ì•Ÿ',
  '�Ü' => 'ì•¢',
  '�Ý' => 'ì•£',
  '�Þ' => 'ì•¥',
  '�ß' => 'ì•¦',
  '�à' => 'ì•§',
  '�á' => 'ì•©',
  '�â' => 'ì•ª',
  '�ã' => 'ì•«',
  '�ä' => 'ì•¬',
  '�å' => 'ì•­',
  '�æ' => 'ì•®',
  '�ç' => 'ì•¯',
  '�è' => 'ì•²',
  '�é' => 'ì•¶',
  '�ê' => 'ì•·',
  '�ë' => 'ì•¸',
  '�ì' => 'ì•¹',
  '�í' => 'ì•º',
  '�î' => 'ì•»',
  '�ï' => 'ì•¾',
  '�ð' => 'ì•¿',
  '�ñ' => 'ì–�',
  '�ò' => 'ì–‚',
  '�ó' => 'ì–ƒ',
  '�ô' => 'ì–…',
  '�õ' => 'ì–†',
  '�ö' => 'ì–ˆ',
  '�÷' => 'ì–‰',
  '�ø' => 'ì–Š',
  '�ù' => 'ì–‹',
  '�ú' => 'ì–Ž',
  '�û' => 'ì–�',
  '�ü' => 'ì–’',
  '�ý' => 'ì–“',
  '�þ' => 'ì–”',
  'žA' => 'ì––',
  'žB' => 'ì–™',
  'žC' => 'ì–š',
  'žD' => 'ì–›',
  'žE' => 'ì–�',
  'žF' => 'ì–ž',
  'žG' => 'ì–Ÿ',
  'žH' => 'ì–¡',
  'žI' => 'ì–¢',
  'žJ' => 'ì–£',
  'žK' => 'ì–¤',
  'žL' => 'ì–¥',
  'žM' => 'ì–¦',
  'žN' => 'ì–§',
  'žO' => 'ì–¨',
  'žP' => 'ì–ª',
  'žQ' => 'ì–«',
  'žR' => 'ì–¬',
  'žS' => 'ì–­',
  'žT' => 'ì–®',
  'žU' => 'ì–¯',
  'žV' => 'ì–°',
  'žW' => 'ì–±',
  'žX' => 'ì–²',
  'žY' => 'ì–³',
  'žZ' => 'ì–¶',
  'ža' => 'ì–·',
  'žb' => 'ì–º',
  'žc' => 'ì–¿',
  'žd' => 'ì—€',
  'že' => 'ì—�',
  'žf' => 'ì—‚',
  'žg' => 'ì—ƒ',
  'žh' => 'ì—‹',
  'ži' => 'ì—�',
  'žj' => 'ì—�',
  'žk' => 'ì—’',
  'žl' => 'ì—“',
  'žm' => 'ì—•',
  'žn' => 'ì—–',
  'žo' => 'ì——',
  'žp' => 'ì—™',
  'žq' => 'ì—š',
  'žr' => 'ì—›',
  'žs' => 'ì—œ',
  'žt' => 'ì—�',
  'žu' => 'ì—ž',
  'žv' => 'ì—Ÿ',
  'žw' => 'ì—¢',
  'žx' => 'ì—¤',
  'žy' => 'ì—¦',
  'žz' => 'ì—§',
  'ž�' => 'ì—¨',
  'ž‚' => 'ì—©',
  'žƒ' => 'ì—ª',
  'ž„' => 'ì—«',
  'ž…' => 'ì—¯',
  'ž†' => 'ì—±',
  'ž‡' => 'ì—²',
  'žˆ' => 'ì—³',
  'ž‰' => 'ì—µ',
  'žŠ' => 'ì—¸',
  'ž‹' => 'ì—¹',
  'žŒ' => 'ì—º',
  'ž�' => 'ì—»',
  'žŽ' => 'ì˜‚',
  'ž�' => 'ì˜ƒ',
  'ž�' => 'ì˜„',
  'ž‘' => 'ì˜‰',
  'ž’' => 'ì˜Š',
  'ž“' => 'ì˜‹',
  'ž”' => 'ì˜�',
  'ž•' => 'ì˜Ž',
  'ž–' => 'ì˜�',
  'ž—' => 'ì˜‘',
  'ž˜' => 'ì˜’',
  'ž™' => 'ì˜“',
  'žš' => 'ì˜”',
  'ž›' => 'ì˜•',
  'žœ' => 'ì˜–',
  'ž�' => 'ì˜—',
  'žž' => 'ì˜š',
  'žŸ' => 'ì˜�',
  'ž ' => 'ì˜ž',
  'ž¡' => 'ì˜Ÿ',
  'ž¢' => 'ì˜ ',
  'ž£' => 'ì˜¡',
  'ž¤' => 'ì˜¢',
  'ž¥' => 'ì˜£',
  'ž¦' => 'ì˜¦',
  'ž§' => 'ì˜§',
  'ž¨' => 'ì˜©',
  'ž©' => 'ì˜ª',
  'žª' => 'ì˜«',
  'ž«' => 'ì˜¯',
  'ž¬' => 'ì˜±',
  'ž­' => 'ì˜²',
  'ž®' => 'ì˜¶',
  'ž¯' => 'ì˜¸',
  'ž°' => 'ì˜º',
  'ž±' => 'ì˜¼',
  'ž²' => 'ì˜½',
  'ž³' => 'ì˜¾',
  'ž´' => 'ì˜¿',
  'žµ' => 'ì™‚',
  'ž¶' => 'ì™ƒ',
  'ž·' => 'ì™…',
  'ž¸' => 'ì™†',
  'ž¹' => 'ì™‡',
  'žº' => 'ì™‰',
  'ž»' => 'ì™Š',
  'ž¼' => 'ì™‹',
  'ž½' => 'ì™Œ',
  'ž¾' => 'ì™�',
  'ž¿' => 'ì™Ž',
  'žÀ' => 'ì™�',
  'žÁ' => 'ì™’',
  'žÂ' => 'ì™–',
  'žÃ' => 'ì™—',
  'žÄ' => 'ì™˜',
  'žÅ' => 'ì™™',
  'žÆ' => 'ì™š',
  'žÇ' => 'ì™›',
  'žÈ' => 'ì™ž',
  'žÉ' => 'ì™Ÿ',
  'žÊ' => 'ì™¡',
  'žË' => 'ì™¢',
  'žÌ' => 'ì™£',
  'žÍ' => 'ì™¤',
  'žÎ' => 'ì™¥',
  'žÏ' => 'ì™¦',
  'žÐ' => 'ì™§',
  'žÑ' => 'ì™¨',
  'žÒ' => 'ì™©',
  'žÓ' => 'ì™ª',
  'žÔ' => 'ì™«',
  'žÕ' => 'ì™­',
  'žÖ' => 'ì™®',
  'ž×' => 'ì™°',
  'žØ' => 'ì™²',
  'žÙ' => 'ì™³',
  'žÚ' => 'ì™´',
  'žÛ' => 'ì™µ',
  'žÜ' => 'ì™¶',
  'žÝ' => 'ì™·',
  'žÞ' => 'ì™º',
  'žß' => 'ì™»',
  'žà' => 'ì™½',
  'žá' => 'ì™¾',
  'žâ' => 'ì™¿',
  'žã' => 'ìš�',
  'žä' => 'ìš‚',
  'žå' => 'ìšƒ',
  'žæ' => 'ìš„',
  'žç' => 'ìš…',
  'žè' => 'ìš†',
  'žé' => 'ìš‡',
  'žê' => 'ìšŠ',
  'žë' => 'ìšŒ',
  'žì' => 'ìšŽ',
  'ží' => 'ìš�',
  'žî' => 'ìš�',
  'žï' => 'ìš‘',
  'žð' => 'ìš’',
  'žñ' => 'ìš“',
  'žò' => 'ìš–',
  'žó' => 'ìš—',
  'žô' => 'ìš™',
  'žõ' => 'ìšš',
  'žö' => 'ìš›',
  'ž÷' => 'ìš�',
  'žø' => 'ìšž',
  'žù' => 'ìšŸ',
  'žú' => 'ìš ',
  'žû' => 'ìš¡',
  'žü' => 'ìš¢',
  'žý' => 'ìš£',
  'žþ' => 'ìš¦',
  'ŸA' => 'ìš¨',
  'ŸB' => 'ìšª',
  'ŸC' => 'ìš«',
  'ŸD' => 'ìš¬',
  'ŸE' => 'ìš­',
  'ŸF' => 'ìš®',
  'ŸG' => 'ìš¯',
  'ŸH' => 'ìš²',
  'ŸI' => 'ìš³',
  'ŸJ' => 'ìšµ',
  'ŸK' => 'ìš¶',
  'ŸL' => 'ìš·',
  'ŸM' => 'ìš»',
  'ŸN' => 'ìš¼',
  'ŸO' => 'ìš½',
  'ŸP' => 'ìš¾',
  'ŸQ' => 'ìš¿',
  'ŸR' => 'ì›‚',
  'ŸS' => 'ì›„',
  'ŸT' => 'ì›†',
  'ŸU' => 'ì›‡',
  'ŸV' => 'ì›ˆ',
  'ŸW' => 'ì›‰',
  'ŸX' => 'ì›Š',
  'ŸY' => 'ì›‹',
  'ŸZ' => 'ì›Ž',
  'Ÿa' => 'ì›�',
  'Ÿb' => 'ì›‘',
  'Ÿc' => 'ì›’',
  'Ÿd' => 'ì›“',
  'Ÿe' => 'ì›•',
  'Ÿf' => 'ì›–',
  'Ÿg' => 'ì›—',
  'Ÿh' => 'ì›˜',
  'Ÿi' => 'ì›™',
  'Ÿj' => 'ì›š',
  'Ÿk' => 'ì››',
  'Ÿl' => 'ì›ž',
  'Ÿm' => 'ì›Ÿ',
  'Ÿn' => 'ì›¢',
  'Ÿo' => 'ì›£',
  'Ÿp' => 'ì›¤',
  'Ÿq' => 'ì›¥',
  'Ÿr' => 'ì›¦',
  'Ÿs' => 'ì›§',
  'Ÿt' => 'ì›ª',
  'Ÿu' => 'ì›«',
  'Ÿv' => 'ì›­',
  'Ÿw' => 'ì›®',
  'Ÿx' => 'ì›¯',
  'Ÿy' => 'ì›±',
  'Ÿz' => 'ì›²',
  'Ÿ�' => 'ì›³',
  'Ÿ‚' => 'ì›´',
  'Ÿƒ' => 'ì›µ',
  'Ÿ„' => 'ì›¶',
  'Ÿ…' => 'ì›·',
  'Ÿ†' => 'ì›º',
  'Ÿ‡' => 'ì›»',
  'Ÿˆ' => 'ì›¼',
  'Ÿ‰' => 'ì›¾',
  'ŸŠ' => 'ì›¿',
  'Ÿ‹' => 'ìœ€',
  'ŸŒ' => 'ìœ�',
  'Ÿ�' => 'ìœ‚',
  'ŸŽ' => 'ìœƒ',
  'Ÿ�' => 'ìœ†',
  'Ÿ�' => 'ìœ‡',
  'Ÿ‘' => 'ìœ‰',
  'Ÿ’' => 'ìœŠ',
  'Ÿ“' => 'ìœ‹',
  'Ÿ”' => 'ìœ�',
  'Ÿ•' => 'ìœŽ',
  'Ÿ–' => 'ìœ�',
  'Ÿ—' => 'ìœ�',
  'Ÿ˜' => 'ìœ‘',
  'Ÿ™' => 'ìœ’',
  'Ÿš' => 'ìœ“',
  'Ÿ›' => 'ìœ–',
  'Ÿœ' => 'ìœ˜',
  'Ÿ�' => 'ìœš',
  'Ÿž' => 'ìœ›',
  'ŸŸ' => 'ìœœ',
  'Ÿ ' => 'ìœ�',
  'Ÿ¡' => 'ìœž',
  'Ÿ¢' => 'ìœŸ',
  'Ÿ£' => 'ìœ¢',
  'Ÿ¤' => 'ìœ£',
  'Ÿ¥' => 'ìœ¥',
  'Ÿ¦' => 'ìœ¦',
  'Ÿ§' => 'ìœ§',
  'Ÿ¨' => 'ìœ©',
  'Ÿ©' => 'ìœª',
  'Ÿª' => 'ìœ«',
  'Ÿ«' => 'ìœ¬',
  'Ÿ¬' => 'ìœ­',
  'Ÿ­' => 'ìœ®',
  'Ÿ®' => 'ìœ¯',
  'Ÿ¯' => 'ìœ²',
  'Ÿ°' => 'ìœ´',
  'Ÿ±' => 'ìœ¶',
  'Ÿ²' => 'ìœ¸',
  'Ÿ³' => 'ìœ¹',
  'Ÿ´' => 'ìœº',
  'Ÿµ' => 'ìœ»',
  'Ÿ¶' => 'ìœ¾',
  'Ÿ·' => 'ìœ¿',
  'Ÿ¸' => 'ì��',
  'Ÿ¹' => 'ì�‚',
  'Ÿº' => 'ì�ƒ',
  'Ÿ»' => 'ì�…',
  'Ÿ¼' => 'ì�†',
  'Ÿ½' => 'ì�‡',
  'Ÿ¾' => 'ì�ˆ',
  'Ÿ¿' => 'ì�‰',
  'ŸÀ' => 'ì�‹',
  'ŸÁ' => 'ì�Ž',
  'ŸÂ' => 'ì��',
  'ŸÃ' => 'ì�™',
  'ŸÄ' => 'ì�š',
  'ŸÅ' => 'ì�›',
  'ŸÆ' => 'ì��',
  'ŸÇ' => 'ì�ž',
  'ŸÈ' => 'ì�Ÿ',
  'ŸÉ' => 'ì�¡',
  'ŸÊ' => 'ì�¢',
  'ŸË' => 'ì�£',
  'ŸÌ' => 'ì�¤',
  'ŸÍ' => 'ì�¥',
  'ŸÎ' => 'ì�¦',
  'ŸÏ' => 'ì�§',
  'ŸÐ' => 'ì�©',
  'ŸÑ' => 'ì�ª',
  'ŸÒ' => 'ì�¬',
  'ŸÓ' => 'ì�­',
  'ŸÔ' => 'ì�®',
  'ŸÕ' => 'ì�¯',
  'ŸÖ' => 'ì�°',
  'Ÿ×' => 'ì�±',
  'ŸØ' => 'ì�²',
  'ŸÙ' => 'ì�³',
  'ŸÚ' => 'ì�¶',
  'ŸÛ' => 'ì�·',
  'ŸÜ' => 'ì�¹',
  'ŸÝ' => 'ì�º',
  'ŸÞ' => 'ì�»',
  'Ÿß' => 'ì�¿',
  'Ÿà' => 'ìž€',
  'Ÿá' => 'ìž�',
  'Ÿâ' => 'ìž‚',
  'Ÿã' => 'ìž†',
  'Ÿä' => 'ìž‹',
  'Ÿå' => 'ìžŒ',
  'Ÿæ' => 'ìž�',
  'Ÿç' => 'ìž�',
  'Ÿè' => 'ìž’',
  'Ÿé' => 'ìž“',
  'Ÿê' => 'ìž•',
  'Ÿë' => 'ìž™',
  'Ÿì' => 'ìž›',
  'Ÿí' => 'ìžœ',
  'Ÿî' => 'ìž�',
  'Ÿï' => 'ìžž',
  'Ÿð' => 'ìžŸ',
  'Ÿñ' => 'ìž¢',
  'Ÿò' => 'ìž§',
  'Ÿó' => 'ìž¨',
  'Ÿô' => 'ìž©',
  'Ÿõ' => 'ìžª',
  'Ÿö' => 'ìž«',
  'Ÿ÷' => 'ìž®',
  'Ÿø' => 'ìž¯',
  'Ÿù' => 'ìž±',
  'Ÿú' => 'ìž²',
  'Ÿû' => 'ìž³',
  'Ÿü' => 'ìžµ',
  'Ÿý' => 'ìž¶',
  'Ÿþ' => 'ìž·',
  ' A' => 'ìž¸',
  ' B' => 'ìž¹',
  ' C' => 'ìžº',
  ' D' => 'ìž»',
  ' E' => 'ìž¾',
  ' F' => 'ìŸ‚',
  ' G' => 'ìŸƒ',
  ' H' => 'ìŸ„',
  ' I' => 'ìŸ…',
  ' J' => 'ìŸ†',
  ' K' => 'ìŸ‡',
  ' L' => 'ìŸŠ',
  ' M' => 'ìŸ‹',
  ' N' => 'ìŸ�',
  ' O' => 'ìŸ�',
  ' P' => 'ìŸ‘',
  ' Q' => 'ìŸ’',
  ' R' => 'ìŸ“',
  ' S' => 'ìŸ”',
  ' T' => 'ìŸ•',
  ' U' => 'ìŸ–',
  ' V' => 'ìŸ—',
  ' W' => 'ìŸ™',
  ' X' => 'ìŸš',
  ' Y' => 'ìŸ›',
  ' Z' => 'ìŸœ',
  ' a' => 'ìŸž',
  ' b' => 'ìŸŸ',
  ' c' => 'ìŸ ',
  ' d' => 'ìŸ¡',
  ' e' => 'ìŸ¢',
  ' f' => 'ìŸ£',
  ' g' => 'ìŸ¥',
  ' h' => 'ìŸ¦',
  ' i' => 'ìŸ§',
  ' j' => 'ìŸ©',
  ' k' => 'ìŸª',
  ' l' => 'ìŸ«',
  ' m' => 'ìŸ­',
  ' n' => 'ìŸ®',
  ' o' => 'ìŸ¯',
  ' p' => 'ìŸ°',
  ' q' => 'ìŸ±',
  ' r' => 'ìŸ²',
  ' s' => 'ìŸ³',
  ' t' => 'ìŸ´',
  ' u' => 'ìŸµ',
  ' v' => 'ìŸ¶',
  ' w' => 'ìŸ·',
  ' x' => 'ìŸ¸',
  ' y' => 'ìŸ¹',
  ' z' => 'ìŸº',
  ' �' => 'ìŸ»',
  ' ‚' => 'ìŸ¼',
  ' ƒ' => 'ìŸ½',
  ' „' => 'ìŸ¾',
  ' …' => 'ìŸ¿',
  ' †' => 'ì ‚',
  ' ‡' => 'ì ƒ',
  ' ˆ' => 'ì …',
  ' ‰' => 'ì †',
  ' Š' => 'ì ‡',
  ' ‹' => 'ì ‰',
  ' Œ' => 'ì ‹',
  ' �' => 'ì Œ',
  ' Ž' => 'ì �',
  ' �' => 'ì Ž',
  ' �' => 'ì �',
  ' ‘' => 'ì ’',
  ' ’' => 'ì ”',
  ' “' => 'ì —',
  ' ”' => 'ì ˜',
  ' •' => 'ì ™',
  ' –' => 'ì š',
  ' —' => 'ì ›',
  ' ˜' => 'ì ž',
  ' ™' => 'ì Ÿ',
  ' š' => 'ì ¡',
  ' ›' => 'ì ¢',
  ' œ' => 'ì £',
  ' �' => 'ì ¥',
  ' ž' => 'ì ¦',
  ' Ÿ' => 'ì §',
  '  ' => 'ì ¨',
  ' ¡' => 'ì ©',
  ' ¢' => 'ì ª',
  ' £' => 'ì «',
  ' ¤' => 'ì ®',
  ' ¥' => 'ì °',
  ' ¦' => 'ì ²',
  ' §' => 'ì ³',
  ' ¨' => 'ì ´',
  ' ©' => 'ì µ',
  ' ª' => 'ì ¶',
  ' «' => 'ì ·',
  ' ¬' => 'ì ¹',
  ' ­' => 'ì º',
  ' ®' => 'ì »',
  ' ¯' => 'ì ½',
  ' °' => 'ì ¾',
  ' ±' => 'ì ¿',
  ' ²' => 'ì¡�',
  ' ³' => 'ì¡‚',
  ' ´' => 'ì¡ƒ',
  ' µ' => 'ì¡„',
  ' ¶' => 'ì¡…',
  ' ·' => 'ì¡†',
  ' ¸' => 'ì¡‡',
  ' ¹' => 'ì¡Š',
  ' º' => 'ì¡‹',
  ' »' => 'ì¡Ž',
  ' ¼' => 'ì¡�',
  ' ½' => 'ì¡�',
  ' ¾' => 'ì¡‘',
  ' ¿' => 'ì¡’',
  ' À' => 'ì¡“',
  ' Á' => 'ì¡•',
  ' Â' => 'ì¡–',
  ' Ã' => 'ì¡—',
  ' Ä' => 'ì¡˜',
  ' Å' => 'ì¡™',
  ' Æ' => 'ì¡š',
  ' Ç' => 'ì¡›',
  ' È' => 'ì¡œ',
  ' É' => 'ì¡�',
  ' Ê' => 'ì¡ž',
  ' Ë' => 'ì¡Ÿ',
  ' Ì' => 'ì¡ ',
  ' Í' => 'ì¡¡',
  ' Î' => 'ì¡¢',
  ' Ï' => 'ì¡£',
  ' Ð' => 'ì¡¤',
  ' Ñ' => 'ì¡¥',
  ' Ò' => 'ì¡¦',
  ' Ó' => 'ì¡§',
  ' Ô' => 'ì¡¨',
  ' Õ' => 'ì¡©',
  ' Ö' => 'ì¡ª',
  ' ×' => 'ì¡«',
  ' Ø' => 'ì¡¬',
  ' Ù' => 'ì¡­',
  ' Ú' => 'ì¡®',
  ' Û' => 'ì¡¯',
  ' Ü' => 'ì¡²',
  ' Ý' => 'ì¡³',
  ' Þ' => 'ì¡µ',
  ' ß' => 'ì¡¶',
  ' à' => 'ì¡·',
  ' á' => 'ì¡¹',
  ' â' => 'ì¡»',
  ' ã' => 'ì¡¼',
  ' ä' => 'ì¡½',
  ' å' => 'ì¡¾',
  ' æ' => 'ì¡¿',
  ' ç' => 'ì¢‚',
  ' è' => 'ì¢„',
  ' é' => 'ì¢ˆ',
  ' ê' => 'ì¢‰',
  ' ë' => 'ì¢Š',
  ' ì' => 'ì¢Ž',
  ' í' => 'ì¢�',
  ' î' => 'ì¢�',
  ' ï' => 'ì¢‘',
  ' ð' => 'ì¢’',
  ' ñ' => 'ì¢“',
  ' ò' => 'ì¢•',
  ' ó' => 'ì¢–',
  ' ô' => 'ì¢—',
  ' õ' => 'ì¢˜',
  ' ö' => 'ì¢™',
  ' ÷' => 'ì¢š',
  ' ø' => 'ì¢›',
  ' ù' => 'ì¢œ',
  ' ú' => 'ì¢ž',
  ' û' => 'ì¢ ',
  ' ü' => 'ì¢¢',
  ' ý' => 'ì¢£',
  ' þ' => 'ì¢¤',
  '¡A' => 'ì¢¥',
  '¡B' => 'ì¢¦',
  '¡C' => 'ì¢§',
  '¡D' => 'ì¢©',
  '¡E' => 'ì¢ª',
  '¡F' => 'ì¢«',
  '¡G' => 'ì¢¬',
  '¡H' => 'ì¢­',
  '¡I' => 'ì¢®',
  '¡J' => 'ì¢¯',
  '¡K' => 'ì¢°',
  '¡L' => 'ì¢±',
  '¡M' => 'ì¢²',
  '¡N' => 'ì¢³',
  '¡O' => 'ì¢´',
  '¡P' => 'ì¢µ',
  '¡Q' => 'ì¢¶',
  '¡R' => 'ì¢·',
  '¡S' => 'ì¢¸',
  '¡T' => 'ì¢¹',
  '¡U' => 'ì¢º',
  '¡V' => 'ì¢»',
  '¡W' => 'ì¢¾',
  '¡X' => 'ì¢¿',
  '¡Y' => 'ì£€',
  '¡Z' => 'ì£�',
  '¡a' => 'ì£‚',
  '¡b' => 'ì£ƒ',
  '¡c' => 'ì£…',
  '¡d' => 'ì£†',
  '¡e' => 'ì£‡',
  '¡f' => 'ì£‰',
  '¡g' => 'ì£Š',
  '¡h' => 'ì£‹',
  '¡i' => 'ì£�',
  '¡j' => 'ì£Ž',
  '¡k' => 'ì£�',
  '¡l' => 'ì£�',
  '¡m' => 'ì£‘',
  '¡n' => 'ì£’',
  '¡o' => 'ì£“',
  '¡p' => 'ì£–',
  '¡q' => 'ì£˜',
  '¡r' => 'ì£š',
  '¡s' => 'ì£›',
  '¡t' => 'ì£œ',
  '¡u' => 'ì£�',
  '¡v' => 'ì£ž',
  '¡w' => 'ì£Ÿ',
  '¡x' => 'ì£¢',
  '¡y' => 'ì££',
  '¡z' => 'ì£¥',
  '¡�' => 'ì£¦',
  '¡‚' => 'ì£§',
  '¡ƒ' => 'ì£¨',
  '¡„' => 'ì£©',
  '¡…' => 'ì£ª',
  '¡†' => 'ì£«',
  '¡‡' => 'ì£¬',
  '¡ˆ' => 'ì£­',
  '¡‰' => 'ì£®',
  '¡Š' => 'ì£¯',
  '¡‹' => 'ì£°',
  '¡Œ' => 'ì£±',
  '¡�' => 'ì£²',
  '¡Ž' => 'ì£³',
  '¡�' => 'ì£´',
  '¡�' => 'ì£¶',
  '¡‘' => 'ì£·',
  '¡’' => 'ì£¸',
  '¡“' => 'ì£¹',
  '¡”' => 'ì£º',
  '¡•' => 'ì£»',
  '¡–' => 'ì£¾',
  '¡—' => 'ì£¿',
  '¡˜' => 'ì¤�',
  '¡™' => 'ì¤‚',
  '¡š' => 'ì¤ƒ',
  '¡›' => 'ì¤‡',
  '¡œ' => 'ì¤ˆ',
  '¡�' => 'ì¤‰',
  '¡ž' => 'ì¤Š',
  '¡Ÿ' => 'ì¤‹',
  '¡ ' => 'ì¤Ž',
  '¡¡' => 'ã€€',
  '¡¢' => 'ã€�',
  '¡£' => 'ã€‚',
  '¡¤' => 'Â·',
  '¡¥' => 'â€¥',
  '¡¦' => 'â€¦',
  '¡§' => 'Â¨',
  '¡¨' => 'ã€ƒ',
  '¡©' => 'Â­',
  '¡ª' => 'â€•',
  '¡«' => 'âˆ¥',
  '¡¬' => 'ï¼¼',
  '¡­' => 'âˆ¼',
  '¡®' => 'â€˜',
  '¡¯' => 'â€™',
  '¡°' => 'â€œ',
  '¡±' => 'â€�',
  '¡²' => 'ã€”',
  '¡³' => 'ã€•',
  '¡´' => 'ã€ˆ',
  '¡µ' => 'ã€‰',
  '¡¶' => 'ã€Š',
  '¡·' => 'ã€‹',
  '¡¸' => 'ã€Œ',
  '¡¹' => 'ã€�',
  '¡º' => 'ã€Ž',
  '¡»' => 'ã€�',
  '¡¼' => 'ã€�',
  '¡½' => 'ã€‘',
  '¡¾' => 'Â±',
  '¡¿' => 'Ã—',
  '¡À' => 'Ã·',
  '¡Á' => 'â‰ ',
  '¡Â' => 'â‰¤',
  '¡Ã' => 'â‰¥',
  '¡Ä' => 'âˆž',
  '¡Å' => 'âˆ´',
  '¡Æ' => 'Â°',
  '¡Ç' => 'â€²',
  '¡È' => 'â€³',
  '¡É' => 'â„ƒ',
  '¡Ê' => 'â„«',
  '¡Ë' => 'ï¿ ',
  '¡Ì' => 'ï¿¡',
  '¡Í' => 'ï¿¥',
  '¡Î' => 'â™‚',
  '¡Ï' => 'â™€',
  '¡Ð' => 'âˆ ',
  '¡Ñ' => 'âŠ¥',
  '¡Ò' => 'âŒ’',
  '¡Ó' => 'âˆ‚',
  '¡Ô' => 'âˆ‡',
  '¡Õ' => 'â‰¡',
  '¡Ö' => 'â‰’',
  '¡×' => 'Â§',
  '¡Ø' => 'â€»',
  '¡Ù' => 'â˜†',
  '¡Ú' => 'â˜…',
  '¡Û' => 'â—‹',
  '¡Ü' => 'â—�',
  '¡Ý' => 'â—Ž',
  '¡Þ' => 'â—‡',
  '¡ß' => 'â—†',
  '¡à' => 'â–¡',
  '¡á' => 'â– ',
  '¡â' => 'â–³',
  '¡ã' => 'â–²',
  '¡ä' => 'â–½',
  '¡å' => 'â–¼',
  '¡æ' => 'â†’',
  '¡ç' => 'â†�',
  '¡è' => 'â†‘',
  '¡é' => 'â†“',
  '¡ê' => 'â†”',
  '¡ë' => 'ã€“',
  '¡ì' => 'â‰ª',
  '¡í' => 'â‰«',
  '¡î' => 'âˆš',
  '¡ï' => 'âˆ½',
  '¡ð' => 'âˆ�',
  '¡ñ' => 'âˆµ',
  '¡ò' => 'âˆ«',
  '¡ó' => 'âˆ¬',
  '¡ô' => 'âˆˆ',
  '¡õ' => 'âˆ‹',
  '¡ö' => 'âŠ†',
  '¡÷' => 'âŠ‡',
  '¡ø' => 'âŠ‚',
  '¡ù' => 'âŠƒ',
  '¡ú' => 'âˆª',
  '¡û' => 'âˆ©',
  '¡ü' => 'âˆ§',
  '¡ý' => 'âˆ¨',
  '¡þ' => 'ï¿¢',
  '¢A' => 'ì¤�',
  '¢B' => 'ì¤’',
  '¢C' => 'ì¤“',
  '¢D' => 'ì¤”',
  '¢E' => 'ì¤•',
  '¢F' => 'ì¤–',
  '¢G' => 'ì¤—',
  '¢H' => 'ì¤™',
  '¢I' => 'ì¤š',
  '¢J' => 'ì¤›',
  '¢K' => 'ì¤œ',
  '¢L' => 'ì¤�',
  '¢M' => 'ì¤ž',
  '¢N' => 'ì¤Ÿ',
  '¢O' => 'ì¤ ',
  '¢P' => 'ì¤¡',
  '¢Q' => 'ì¤¢',
  '¢R' => 'ì¤£',
  '¢S' => 'ì¤¤',
  '¢T' => 'ì¤¥',
  '¢U' => 'ì¤¦',
  '¢V' => 'ì¤§',
  '¢W' => 'ì¤¨',
  '¢X' => 'ì¤©',
  '¢Y' => 'ì¤ª',
  '¢Z' => 'ì¤«',
  '¢a' => 'ì¤­',
  '¢b' => 'ì¤®',
  '¢c' => 'ì¤¯',
  '¢d' => 'ì¤°',
  '¢e' => 'ì¤±',
  '¢f' => 'ì¤²',
  '¢g' => 'ì¤³',
  '¢h' => 'ì¤µ',
  '¢i' => 'ì¤¶',
  '¢j' => 'ì¤·',
  '¢k' => 'ì¤¸',
  '¢l' => 'ì¤¹',
  '¢m' => 'ì¤º',
  '¢n' => 'ì¤»',
  '¢o' => 'ì¤¼',
  '¢p' => 'ì¤½',
  '¢q' => 'ì¤¾',
  '¢r' => 'ì¤¿',
  '¢s' => 'ì¥€',
  '¢t' => 'ì¥�',
  '¢u' => 'ì¥‚',
  '¢v' => 'ì¥ƒ',
  '¢w' => 'ì¥„',
  '¢x' => 'ì¥…',
  '¢y' => 'ì¥†',
  '¢z' => 'ì¥‡',
  '¢�' => 'ì¥ˆ',
  '¢‚' => 'ì¥‰',
  '¢ƒ' => 'ì¥Š',
  '¢„' => 'ì¥‹',
  '¢…' => 'ì¥Œ',
  '¢†' => 'ì¥�',
  '¢‡' => 'ì¥Ž',
  '¢ˆ' => 'ì¥�',
  '¢‰' => 'ì¥’',
  '¢Š' => 'ì¥“',
  '¢‹' => 'ì¥•',
  '¢Œ' => 'ì¥–',
  '¢�' => 'ì¥—',
  '¢Ž' => 'ì¥™',
  '¢�' => 'ì¥š',
  '¢�' => 'ì¥›',
  '¢‘' => 'ì¥œ',
  '¢’' => 'ì¥�',
  '¢“' => 'ì¥ž',
  '¢”' => 'ì¥Ÿ',
  '¢•' => 'ì¥¢',
  '¢–' => 'ì¥¤',
  '¢—' => 'ì¥¥',
  '¢˜' => 'ì¥¦',
  '¢™' => 'ì¥§',
  '¢š' => 'ì¥¨',
  '¢›' => 'ì¥©',
  '¢œ' => 'ì¥ª',
  '¢�' => 'ì¥«',
  '¢ž' => 'ì¥­',
  '¢Ÿ' => 'ì¥®',
  '¢ ' => 'ì¥¯',
  '¢¡' => 'â‡’',
  '¢¢' => 'â‡”',
  '¢£' => 'âˆ€',
  '¢¤' => 'âˆƒ',
  '¢¥' => 'Â´',
  '¢¦' => 'ï½ž',
  '¢§' => 'Ë‡',
  '¢¨' => 'Ë˜',
  '¢©' => 'Ë�',
  '¢ª' => 'Ëš',
  '¢«' => 'Ë™',
  '¢¬' => 'Â¸',
  '¢­' => 'Ë›',
  '¢®' => 'Â¡',
  '¢¯' => 'Â¿',
  '¢°' => 'Ë�',
  '¢±' => 'âˆ®',
  '¢²' => 'âˆ‘',
  '¢³' => 'âˆ�',
  '¢´' => 'Â¤',
  '¢µ' => 'â„‰',
  '¢¶' => 'â€°',
  '¢·' => 'â—�',
  '¢¸' => 'â—€',
  '¢¹' => 'â–·',
  '¢º' => 'â–¶',
  '¢»' => 'â™¤',
  '¢¼' => 'â™ ',
  '¢½' => 'â™¡',
  '¢¾' => 'â™¥',
  '¢¿' => 'â™§',
  '¢À' => 'â™£',
  '¢Á' => 'âŠ™',
  '¢Â' => 'â—ˆ',
  '¢Ã' => 'â–£',
  '¢Ä' => 'â—�',
  '¢Å' => 'â—‘',
  '¢Æ' => 'â–’',
  '¢Ç' => 'â–¤',
  '¢È' => 'â–¥',
  '¢É' => 'â–¨',
  '¢Ê' => 'â–§',
  '¢Ë' => 'â–¦',
  '¢Ì' => 'â–©',
  '¢Í' => 'â™¨',
  '¢Î' => 'â˜�',
  '¢Ï' => 'â˜Ž',
  '¢Ð' => 'â˜œ',
  '¢Ñ' => 'â˜ž',
  '¢Ò' => 'Â¶',
  '¢Ó' => 'â€ ',
  '¢Ô' => 'â€¡',
  '¢Õ' => 'â†•',
  '¢Ö' => 'â†—',
  '¢×' => 'â†™',
  '¢Ø' => 'â†–',
  '¢Ù' => 'â†˜',
  '¢Ú' => 'â™­',
  '¢Û' => 'â™©',
  '¢Ü' => 'â™ª',
  '¢Ý' => 'â™¬',
  '¢Þ' => 'ã‰¿',
  '¢ß' => 'ãˆœ',
  '¢à' => 'â„–',
  '¢á' => 'ã�‡',
  '¢â' => 'â„¢',
  '¢ã' => 'ã�‚',
  '¢ä' => 'ã�˜',
  '¢å' => 'â„¡',
  '¢æ' => 'â‚¬',
  '¢ç' => 'Â®',
  '£A' => 'ì¥±',
  '£B' => 'ì¥²',
  '£C' => 'ì¥³',
  '£D' => 'ì¥µ',
  '£E' => 'ì¥¶',
  '£F' => 'ì¥·',
  '£G' => 'ì¥¸',
  '£H' => 'ì¥¹',
  '£I' => 'ì¥º',
  '£J' => 'ì¥»',
  '£K' => 'ì¥½',
  '£L' => 'ì¥¾',
  '£M' => 'ì¥¿',
  '£N' => 'ì¦€',
  '£O' => 'ì¦�',
  '£P' => 'ì¦‚',
  '£Q' => 'ì¦ƒ',
  '£R' => 'ì¦„',
  '£S' => 'ì¦…',
  '£T' => 'ì¦†',
  '£U' => 'ì¦‡',
  '£V' => 'ì¦Š',
  '£W' => 'ì¦‹',
  '£X' => 'ì¦�',
  '£Y' => 'ì¦Ž',
  '£Z' => 'ì¦�',
  '£a' => 'ì¦‘',
  '£b' => 'ì¦’',
  '£c' => 'ì¦“',
  '£d' => 'ì¦”',
  '£e' => 'ì¦•',
  '£f' => 'ì¦–',
  '£g' => 'ì¦—',
  '£h' => 'ì¦š',
  '£i' => 'ì¦œ',
  '£j' => 'ì¦ž',
  '£k' => 'ì¦Ÿ',
  '£l' => 'ì¦ ',
  '£m' => 'ì¦¡',
  '£n' => 'ì¦¢',
  '£o' => 'ì¦£',
  '£p' => 'ì¦¤',
  '£q' => 'ì¦¥',
  '£r' => 'ì¦¦',
  '£s' => 'ì¦§',
  '£t' => 'ì¦¨',
  '£u' => 'ì¦©',
  '£v' => 'ì¦ª',
  '£w' => 'ì¦«',
  '£x' => 'ì¦¬',
  '£y' => 'ì¦­',
  '£z' => 'ì¦®',
  '£�' => 'ì¦¯',
  '£‚' => 'ì¦°',
  '£ƒ' => 'ì¦±',
  '£„' => 'ì¦²',
  '£…' => 'ì¦³',
  '£†' => 'ì¦´',
  '£‡' => 'ì¦µ',
  '£ˆ' => 'ì¦¶',
  '£‰' => 'ì¦·',
  '£Š' => 'ì¦¸',
  '£‹' => 'ì¦¹',
  '£Œ' => 'ì¦º',
  '£�' => 'ì¦»',
  '£Ž' => 'ì¦¼',
  '£�' => 'ì¦½',
  '£�' => 'ì¦¾',
  '£‘' => 'ì¦¿',
  '£’' => 'ì§‚',
  '£“' => 'ì§ƒ',
  '£”' => 'ì§…',
  '£•' => 'ì§†',
  '£–' => 'ì§‰',
  '£—' => 'ì§‹',
  '£˜' => 'ì§Œ',
  '£™' => 'ì§�',
  '£š' => 'ì§Ž',
  '£›' => 'ì§�',
  '£œ' => 'ì§’',
  '£�' => 'ì§”',
  '£ž' => 'ì§—',
  '£Ÿ' => 'ì§˜',
  '£ ' => 'ì§›',
  '£¡' => 'ï¼�',
  '£¢' => 'ï¼‚',
  '££' => 'ï¼ƒ',
  '£¤' => 'ï¼„',
  '£¥' => 'ï¼…',
  '£¦' => 'ï¼†',
  '£§' => 'ï¼‡',
  '£¨' => 'ï¼ˆ',
  '£©' => 'ï¼‰',
  '£ª' => 'ï¼Š',
  '£«' => 'ï¼‹',
  '£¬' => 'ï¼Œ',
  '£­' => 'ï¼�',
  '£®' => 'ï¼Ž',
  '£¯' => 'ï¼�',
  '£°' => 'ï¼�',
  '£±' => 'ï¼‘',
  '£²' => 'ï¼’',
  '£³' => 'ï¼“',
  '£´' => 'ï¼”',
  '£µ' => 'ï¼•',
  '£¶' => 'ï¼–',
  '£·' => 'ï¼—',
  '£¸' => 'ï¼˜',
  '£¹' => 'ï¼™',
  '£º' => 'ï¼š',
  '£»' => 'ï¼›',
  '£¼' => 'ï¼œ',
  '£½' => 'ï¼�',
  '£¾' => 'ï¼ž',
  '£¿' => 'ï¼Ÿ',
  '£À' => 'ï¼ ',
  '£Á' => 'ï¼¡',
  '£Â' => 'ï¼¢',
  '£Ã' => 'ï¼£',
  '£Ä' => 'ï¼¤',
  '£Å' => 'ï¼¥',
  '£Æ' => 'ï¼¦',
  '£Ç' => 'ï¼§',
  '£È' => 'ï¼¨',
  '£É' => 'ï¼©',
  '£Ê' => 'ï¼ª',
  '£Ë' => 'ï¼«',
  '£Ì' => 'ï¼¬',
  '£Í' => 'ï¼­',
  '£Î' => 'ï¼®',
  '£Ï' => 'ï¼¯',
  '£Ð' => 'ï¼°',
  '£Ñ' => 'ï¼±',
  '£Ò' => 'ï¼²',
  '£Ó' => 'ï¼³',
  '£Ô' => 'ï¼´',
  '£Õ' => 'ï¼µ',
  '£Ö' => 'ï¼¶',
  '£×' => 'ï¼·',
  '£Ø' => 'ï¼¸',
  '£Ù' => 'ï¼¹',
  '£Ú' => 'ï¼º',
  '£Û' => 'ï¼»',
  '£Ü' => 'ï¿¦',
  '£Ý' => 'ï¼½',
  '£Þ' => 'ï¼¾',
  '£ß' => 'ï¼¿',
  '£à' => 'ï½€',
  '£á' => 'ï½�',
  '£â' => 'ï½‚',
  '£ã' => 'ï½ƒ',
  '£ä' => 'ï½„',
  '£å' => 'ï½…',
  '£æ' => 'ï½†',
  '£ç' => 'ï½‡',
  '£è' => 'ï½ˆ',
  '£é' => 'ï½‰',
  '£ê' => 'ï½Š',
  '£ë' => 'ï½‹',
  '£ì' => 'ï½Œ',
  '£í' => 'ï½�',
  '£î' => 'ï½Ž',
  '£ï' => 'ï½�',
  '£ð' => 'ï½�',
  '£ñ' => 'ï½‘',
  '£ò' => 'ï½’',
  '£ó' => 'ï½“',
  '£ô' => 'ï½”',
  '£õ' => 'ï½•',
  '£ö' => 'ï½–',
  '£÷' => 'ï½—',
  '£ø' => 'ï½˜',
  '£ù' => 'ï½™',
  '£ú' => 'ï½š',
  '£û' => 'ï½›',
  '£ü' => 'ï½œ',
  '£ý' => 'ï½�',
  '£þ' => 'ï¿£',
  '¤A' => 'ì§ž',
  '¤B' => 'ì§Ÿ',
  '¤C' => 'ì§¡',
  '¤D' => 'ì§£',
  '¤E' => 'ì§¥',
  '¤F' => 'ì§¦',
  '¤G' => 'ì§¨',
  '¤H' => 'ì§©',
  '¤I' => 'ì§ª',
  '¤J' => 'ì§«',
  '¤K' => 'ì§®',
  '¤L' => 'ì§²',
  '¤M' => 'ì§³',
  '¤N' => 'ì§´',
  '¤O' => 'ì§µ',
  '¤P' => 'ì§¶',
  '¤Q' => 'ì§·',
  '¤R' => 'ì§º',
  '¤S' => 'ì§»',
  '¤T' => 'ì§½',
  '¤U' => 'ì§¾',
  '¤V' => 'ì§¿',
  '¤W' => 'ì¨�',
  '¤X' => 'ì¨‚',
  '¤Y' => 'ì¨ƒ',
  '¤Z' => 'ì¨„',
  '¤a' => 'ì¨…',
  '¤b' => 'ì¨†',
  '¤c' => 'ì¨‡',
  '¤d' => 'ì¨Š',
  '¤e' => 'ì¨Ž',
  '¤f' => 'ì¨�',
  '¤g' => 'ì¨�',
  '¤h' => 'ì¨‘',
  '¤i' => 'ì¨’',
  '¤j' => 'ì¨“',
  '¤k' => 'ì¨•',
  '¤l' => 'ì¨–',
  '¤m' => 'ì¨—',
  '¤n' => 'ì¨™',
  '¤o' => 'ì¨š',
  '¤p' => 'ì¨›',
  '¤q' => 'ì¨œ',
  '¤r' => 'ì¨�',
  '¤s' => 'ì¨ž',
  '¤t' => 'ì¨Ÿ',
  '¤u' => 'ì¨ ',
  '¤v' => 'ì¨¡',
  '¤w' => 'ì¨¢',
  '¤x' => 'ì¨£',
  '¤y' => 'ì¨¤',
  '¤z' => 'ì¨¥',
  '¤�' => 'ì¨¦',
  '¤‚' => 'ì¨§',
  '¤ƒ' => 'ì¨¨',
  '¤„' => 'ì¨ª',
  '¤…' => 'ì¨«',
  '¤†' => 'ì¨¬',
  '¤‡' => 'ì¨­',
  '¤ˆ' => 'ì¨®',
  '¤‰' => 'ì¨¯',
  '¤Š' => 'ì¨°',
  '¤‹' => 'ì¨±',
  '¤Œ' => 'ì¨²',
  '¤�' => 'ì¨³',
  '¤Ž' => 'ì¨´',
  '¤�' => 'ì¨µ',
  '¤�' => 'ì¨¶',
  '¤‘' => 'ì¨·',
  '¤’' => 'ì¨¸',
  '¤“' => 'ì¨¹',
  '¤”' => 'ì¨º',
  '¤•' => 'ì¨»',
  '¤–' => 'ì¨¼',
  '¤—' => 'ì¨½',
  '¤˜' => 'ì¨¾',
  '¤™' => 'ì¨¿',
  '¤š' => 'ì©€',
  '¤›' => 'ì©�',
  '¤œ' => 'ì©‚',
  '¤�' => 'ì©ƒ',
  '¤ž' => 'ì©„',
  '¤Ÿ' => 'ì©…',
  '¤ ' => 'ì©†',
  '¤¡' => 'ã„±',
  '¤¢' => 'ã„²',
  '¤£' => 'ã„³',
  '¤¤' => 'ã„´',
  '¤¥' => 'ã„µ',
  '¤¦' => 'ã„¶',
  '¤§' => 'ã„·',
  '¤¨' => 'ã„¸',
  '¤©' => 'ã„¹',
  '¤ª' => 'ã„º',
  '¤«' => 'ã„»',
  '¤¬' => 'ã„¼',
  '¤­' => 'ã„½',
  '¤®' => 'ã„¾',
  '¤¯' => 'ã„¿',
  '¤°' => 'ã…€',
  '¤±' => 'ã…�',
  '¤²' => 'ã…‚',
  '¤³' => 'ã…ƒ',
  '¤´' => 'ã…„',
  '¤µ' => 'ã……',
  '¤¶' => 'ã…†',
  '¤·' => 'ã…‡',
  '¤¸' => 'ã…ˆ',
  '¤¹' => 'ã…‰',
  '¤º' => 'ã…Š',
  '¤»' => 'ã…‹',
  '¤¼' => 'ã…Œ',
  '¤½' => 'ã…�',
  '¤¾' => 'ã…Ž',
  '¤¿' => 'ã…�',
  '¤À' => 'ã…�',
  '¤Á' => 'ã…‘',
  '¤Â' => 'ã…’',
  '¤Ã' => 'ã…“',
  '¤Ä' => 'ã…”',
  '¤Å' => 'ã…•',
  '¤Æ' => 'ã…–',
  '¤Ç' => 'ã…—',
  '¤È' => 'ã…˜',
  '¤É' => 'ã…™',
  '¤Ê' => 'ã…š',
  '¤Ë' => 'ã…›',
  '¤Ì' => 'ã…œ',
  '¤Í' => 'ã…�',
  '¤Î' => 'ã…ž',
  '¤Ï' => 'ã…Ÿ',
  '¤Ð' => 'ã… ',
  '¤Ñ' => 'ã…¡',
  '¤Ò' => 'ã…¢',
  '¤Ó' => 'ã…£',
  '¤Ô' => 'ã…¤',
  '¤Õ' => 'ã…¥',
  '¤Ö' => 'ã…¦',
  '¤×' => 'ã…§',
  '¤Ø' => 'ã…¨',
  '¤Ù' => 'ã…©',
  '¤Ú' => 'ã…ª',
  '¤Û' => 'ã…«',
  '¤Ü' => 'ã…¬',
  '¤Ý' => 'ã…­',
  '¤Þ' => 'ã…®',
  '¤ß' => 'ã…¯',
  '¤à' => 'ã…°',
  '¤á' => 'ã…±',
  '¤â' => 'ã…²',
  '¤ã' => 'ã…³',
  '¤ä' => 'ã…´',
  '¤å' => 'ã…µ',
  '¤æ' => 'ã…¶',
  '¤ç' => 'ã…·',
  '¤è' => 'ã…¸',
  '¤é' => 'ã…¹',
  '¤ê' => 'ã…º',
  '¤ë' => 'ã…»',
  '¤ì' => 'ã…¼',
  '¤í' => 'ã…½',
  '¤î' => 'ã…¾',
  '¤ï' => 'ã…¿',
  '¤ð' => 'ã†€',
  '¤ñ' => 'ã†�',
  '¤ò' => 'ã†‚',
  '¤ó' => 'ã†ƒ',
  '¤ô' => 'ã†„',
  '¤õ' => 'ã†…',
  '¤ö' => 'ã††',
  '¤÷' => 'ã†‡',
  '¤ø' => 'ã†ˆ',
  '¤ù' => 'ã†‰',
  '¤ú' => 'ã†Š',
  '¤û' => 'ã†‹',
  '¤ü' => 'ã†Œ',
  '¤ý' => 'ã†�',
  '¤þ' => 'ã†Ž',
  '¥A' => 'ì©‡',
  '¥B' => 'ì©ˆ',
  '¥C' => 'ì©‰',
  '¥D' => 'ì©Š',
  '¥E' => 'ì©‹',
  '¥F' => 'ì©Ž',
  '¥G' => 'ì©�',
  '¥H' => 'ì©‘',
  '¥I' => 'ì©’',
  '¥J' => 'ì©“',
  '¥K' => 'ì©•',
  '¥L' => 'ì©–',
  '¥M' => 'ì©—',
  '¥N' => 'ì©˜',
  '¥O' => 'ì©™',
  '¥P' => 'ì©š',
  '¥Q' => 'ì©›',
  '¥R' => 'ì©ž',
  '¥S' => 'ì©¢',
  '¥T' => 'ì©£',
  '¥U' => 'ì©¤',
  '¥V' => 'ì©¥',
  '¥W' => 'ì©¦',
  '¥X' => 'ì©§',
  '¥Y' => 'ì©©',
  '¥Z' => 'ì©ª',
  '¥a' => 'ì©«',
  '¥b' => 'ì©¬',
  '¥c' => 'ì©­',
  '¥d' => 'ì©®',
  '¥e' => 'ì©¯',
  '¥f' => 'ì©°',
  '¥g' => 'ì©±',
  '¥h' => 'ì©²',
  '¥i' => 'ì©³',
  '¥j' => 'ì©´',
  '¥k' => 'ì©µ',
  '¥l' => 'ì©¶',
  '¥m' => 'ì©·',
  '¥n' => 'ì©¸',
  '¥o' => 'ì©¹',
  '¥p' => 'ì©º',
  '¥q' => 'ì©»',
  '¥r' => 'ì©¼',
  '¥s' => 'ì©¾',
  '¥t' => 'ì©¿',
  '¥u' => 'ìª€',
  '¥v' => 'ìª�',
  '¥w' => 'ìª‚',
  '¥x' => 'ìªƒ',
  '¥y' => 'ìª…',
  '¥z' => 'ìª†',
  '¥�' => 'ìª‡',
  '¥‚' => 'ìªˆ',
  '¥ƒ' => 'ìª‰',
  '¥„' => 'ìªŠ',
  '¥…' => 'ìª‹',
  '¥†' => 'ìªŒ',
  '¥‡' => 'ìª�',
  '¥ˆ' => 'ìªŽ',
  '¥‰' => 'ìª�',
  '¥Š' => 'ìª�',
  '¥‹' => 'ìª‘',
  '¥Œ' => 'ìª’',
  '¥�' => 'ìª“',
  '¥Ž' => 'ìª”',
  '¥�' => 'ìª•',
  '¥�' => 'ìª–',
  '¥‘' => 'ìª—',
  '¥’' => 'ìª™',
  '¥“' => 'ìªš',
  '¥”' => 'ìª›',
  '¥•' => 'ìªœ',
  '¥–' => 'ìª�',
  '¥—' => 'ìªž',
  '¥˜' => 'ìªŸ',
  '¥™' => 'ìª ',
  '¥š' => 'ìª¡',
  '¥›' => 'ìª¢',
  '¥œ' => 'ìª£',
  '¥�' => 'ìª¤',
  '¥ž' => 'ìª¥',
  '¥Ÿ' => 'ìª¦',
  '¥ ' => 'ìª§',
  '¥¡' => 'â…°',
  '¥¢' => 'â…±',
  '¥£' => 'â…²',
  '¥¤' => 'â…³',
  '¥¥' => 'â…´',
  '¥¦' => 'â…µ',
  '¥§' => 'â…¶',
  '¥¨' => 'â…·',
  '¥©' => 'â…¸',
  '¥ª' => 'â…¹',
  '¥°' => 'â… ',
  '¥±' => 'â…¡',
  '¥²' => 'â…¢',
  '¥³' => 'â…£',
  '¥´' => 'â…¤',
  '¥µ' => 'â…¥',
  '¥¶' => 'â…¦',
  '¥·' => 'â…§',
  '¥¸' => 'â…¨',
  '¥¹' => 'â…©',
  '¥Á' => 'Î‘',
  '¥Â' => 'Î’',
  '¥Ã' => 'Î“',
  '¥Ä' => 'Î”',
  '¥Å' => 'Î•',
  '¥Æ' => 'Î–',
  '¥Ç' => 'Î—',
  '¥È' => 'Î˜',
  '¥É' => 'Î™',
  '¥Ê' => 'Îš',
  '¥Ë' => 'Î›',
  '¥Ì' => 'Îœ',
  '¥Í' => 'Î�',
  '¥Î' => 'Îž',
  '¥Ï' => 'ÎŸ',
  '¥Ð' => 'Î ',
  '¥Ñ' => 'Î¡',
  '¥Ò' => 'Î£',
  '¥Ó' => 'Î¤',
  '¥Ô' => 'Î¥',
  '¥Õ' => 'Î¦',
  '¥Ö' => 'Î§',
  '¥×' => 'Î¨',
  '¥Ø' => 'Î©',
  '¥á' => 'Î±',
  '¥â' => 'Î²',
  '¥ã' => 'Î³',
  '¥ä' => 'Î´',
  '¥å' => 'Îµ',
  '¥æ' => 'Î¶',
  '¥ç' => 'Î·',
  '¥è' => 'Î¸',
  '¥é' => 'Î¹',
  '¥ê' => 'Îº',
  '¥ë' => 'Î»',
  '¥ì' => 'Î¼',
  '¥í' => 'Î½',
  '¥î' => 'Î¾',
  '¥ï' => 'Î¿',
  '¥ð' => 'Ï€',
  '¥ñ' => 'Ï�',
  '¥ò' => 'Ïƒ',
  '¥ó' => 'Ï„',
  '¥ô' => 'Ï…',
  '¥õ' => 'Ï†',
  '¥ö' => 'Ï‡',
  '¥÷' => 'Ïˆ',
  '¥ø' => 'Ï‰',
  '¦A' => 'ìª¨',
  '¦B' => 'ìª©',
  '¦C' => 'ìªª',
  '¦D' => 'ìª«',
  '¦E' => 'ìª¬',
  '¦F' => 'ìª­',
  '¦G' => 'ìª®',
  '¦H' => 'ìª¯',
  '¦I' => 'ìª°',
  '¦J' => 'ìª±',
  '¦K' => 'ìª²',
  '¦L' => 'ìª³',
  '¦M' => 'ìª´',
  '¦N' => 'ìªµ',
  '¦O' => 'ìª¶',
  '¦P' => 'ìª·',
  '¦Q' => 'ìª¸',
  '¦R' => 'ìª¹',
  '¦S' => 'ìªº',
  '¦T' => 'ìª»',
  '¦U' => 'ìª¾',
  '¦V' => 'ìª¿',
  '¦W' => 'ì«�',
  '¦X' => 'ì«‚',
  '¦Y' => 'ì«ƒ',
  '¦Z' => 'ì«…',
  '¦a' => 'ì«†',
  '¦b' => 'ì«‡',
  '¦c' => 'ì«ˆ',
  '¦d' => 'ì«‰',
  '¦e' => 'ì«Š',
  '¦f' => 'ì«‹',
  '¦g' => 'ì«Ž',
  '¦h' => 'ì«�',
  '¦i' => 'ì«’',
  '¦j' => 'ì«”',
  '¦k' => 'ì«•',
  '¦l' => 'ì«–',
  '¦m' => 'ì«—',
  '¦n' => 'ì«š',
  '¦o' => 'ì«›',
  '¦p' => 'ì«œ',
  '¦q' => 'ì«�',
  '¦r' => 'ì«ž',
  '¦s' => 'ì«Ÿ',
  '¦t' => 'ì«¡',
  '¦u' => 'ì«¢',
  '¦v' => 'ì«£',
  '¦w' => 'ì«¤',
  '¦x' => 'ì«¥',
  '¦y' => 'ì«¦',
  '¦z' => 'ì«§',
  '¦�' => 'ì«¨',
  '¦‚' => 'ì«©',
  '¦ƒ' => 'ì«ª',
  '¦„' => 'ì««',
  '¦…' => 'ì«­',
  '¦†' => 'ì«®',
  '¦‡' => 'ì«¯',
  '¦ˆ' => 'ì«°',
  '¦‰' => 'ì«±',
  '¦Š' => 'ì«²',
  '¦‹' => 'ì«³',
  '¦Œ' => 'ì«µ',
  '¦�' => 'ì«¶',
  '¦Ž' => 'ì«·',
  '¦�' => 'ì«¸',
  '¦�' => 'ì«¹',
  '¦‘' => 'ì«º',
  '¦’' => 'ì«»',
  '¦“' => 'ì«¼',
  '¦”' => 'ì«½',
  '¦•' => 'ì«¾',
  '¦–' => 'ì«¿',
  '¦—' => 'ì¬€',
  '¦˜' => 'ì¬�',
  '¦™' => 'ì¬‚',
  '¦š' => 'ì¬ƒ',
  '¦›' => 'ì¬„',
  '¦œ' => 'ì¬…',
  '¦�' => 'ì¬†',
  '¦ž' => 'ì¬‡',
  '¦Ÿ' => 'ì¬‰',
  '¦ ' => 'ì¬Š',
  '¦¡' => 'â”€',
  '¦¢' => 'â”‚',
  '¦£' => 'â”Œ',
  '¦¤' => 'â”�',
  '¦¥' => 'â”˜',
  '¦¦' => 'â””',
  '¦§' => 'â”œ',
  '¦¨' => 'â”¬',
  '¦©' => 'â”¤',
  '¦ª' => 'â”´',
  '¦«' => 'â”¼',
  '¦¬' => 'â”�',
  '¦­' => 'â”ƒ',
  '¦®' => 'â”�',
  '¦¯' => 'â”“',
  '¦°' => 'â”›',
  '¦±' => 'â”—',
  '¦²' => 'â”£',
  '¦³' => 'â”³',
  '¦´' => 'â”«',
  '¦µ' => 'â”»',
  '¦¶' => 'â•‹',
  '¦·' => 'â” ',
  '¦¸' => 'â”¯',
  '¦¹' => 'â”¨',
  '¦º' => 'â”·',
  '¦»' => 'â”¿',
  '¦¼' => 'â”�',
  '¦½' => 'â”°',
  '¦¾' => 'â”¥',
  '¦¿' => 'â”¸',
  '¦À' => 'â•‚',
  '¦Á' => 'â”’',
  '¦Â' => 'â”‘',
  '¦Ã' => 'â”š',
  '¦Ä' => 'â”™',
  '¦Å' => 'â”–',
  '¦Æ' => 'â”•',
  '¦Ç' => 'â”Ž',
  '¦È' => 'â”�',
  '¦É' => 'â”ž',
  '¦Ê' => 'â”Ÿ',
  '¦Ë' => 'â”¡',
  '¦Ì' => 'â”¢',
  '¦Í' => 'â”¦',
  '¦Î' => 'â”§',
  '¦Ï' => 'â”©',
  '¦Ð' => 'â”ª',
  '¦Ñ' => 'â”­',
  '¦Ò' => 'â”®',
  '¦Ó' => 'â”±',
  '¦Ô' => 'â”²',
  '¦Õ' => 'â”µ',
  '¦Ö' => 'â”¶',
  '¦×' => 'â”¹',
  '¦Ø' => 'â”º',
  '¦Ù' => 'â”½',
  '¦Ú' => 'â”¾',
  '¦Û' => 'â•€',
  '¦Ü' => 'â•�',
  '¦Ý' => 'â•ƒ',
  '¦Þ' => 'â•„',
  '¦ß' => 'â•…',
  '¦à' => 'â•†',
  '¦á' => 'â•‡',
  '¦â' => 'â•ˆ',
  '¦ã' => 'â•‰',
  '¦ä' => 'â•Š',
  '§A' => 'ì¬‹',
  '§B' => 'ì¬Œ',
  '§C' => 'ì¬�',
  '§D' => 'ì¬Ž',
  '§E' => 'ì¬�',
  '§F' => 'ì¬‘',
  '§G' => 'ì¬’',
  '§H' => 'ì¬“',
  '§I' => 'ì¬•',
  '§J' => 'ì¬–',
  '§K' => 'ì¬—',
  '§L' => 'ì¬™',
  '§M' => 'ì¬š',
  '§N' => 'ì¬›',
  '§O' => 'ì¬œ',
  '§P' => 'ì¬�',
  '§Q' => 'ì¬ž',
  '§R' => 'ì¬Ÿ',
  '§S' => 'ì¬¢',
  '§T' => 'ì¬£',
  '§U' => 'ì¬¤',
  '§V' => 'ì¬¥',
  '§W' => 'ì¬¦',
  '§X' => 'ì¬§',
  '§Y' => 'ì¬¨',
  '§Z' => 'ì¬©',
  '§a' => 'ì¬ª',
  '§b' => 'ì¬«',
  '§c' => 'ì¬¬',
  '§d' => 'ì¬­',
  '§e' => 'ì¬®',
  '§f' => 'ì¬¯',
  '§g' => 'ì¬°',
  '§h' => 'ì¬±',
  '§i' => 'ì¬²',
  '§j' => 'ì¬³',
  '§k' => 'ì¬´',
  '§l' => 'ì¬µ',
  '§m' => 'ì¬¶',
  '§n' => 'ì¬·',
  '§o' => 'ì¬¸',
  '§p' => 'ì¬¹',
  '§q' => 'ì¬º',
  '§r' => 'ì¬»',
  '§s' => 'ì¬¼',
  '§t' => 'ì¬½',
  '§u' => 'ì¬¾',
  '§v' => 'ì¬¿',
  '§w' => 'ì­€',
  '§x' => 'ì­‚',
  '§y' => 'ì­ƒ',
  '§z' => 'ì­„',
  '§�' => 'ì­…',
  '§‚' => 'ì­†',
  '§ƒ' => 'ì­‡',
  '§„' => 'ì­Š',
  '§…' => 'ì­‹',
  '§†' => 'ì­�',
  '§‡' => 'ì­Ž',
  '§ˆ' => 'ì­�',
  '§‰' => 'ì­‘',
  '§Š' => 'ì­’',
  '§‹' => 'ì­“',
  '§Œ' => 'ì­”',
  '§�' => 'ì­•',
  '§Ž' => 'ì­–',
  '§�' => 'ì­—',
  '§�' => 'ì­š',
  '§‘' => 'ì­›',
  '§’' => 'ì­œ',
  '§“' => 'ì­ž',
  '§”' => 'ì­Ÿ',
  '§•' => 'ì­ ',
  '§–' => 'ì­¡',
  '§—' => 'ì­¢',
  '§˜' => 'ì­£',
  '§™' => 'ì­¥',
  '§š' => 'ì­¦',
  '§›' => 'ì­§',
  '§œ' => 'ì­¨',
  '§�' => 'ì­©',
  '§ž' => 'ì­ª',
  '§Ÿ' => 'ì­«',
  '§ ' => 'ì­¬',
  '§¡' => 'ãŽ•',
  '§¢' => 'ãŽ–',
  '§£' => 'ãŽ—',
  '§¤' => 'â„“',
  '§¥' => 'ãŽ˜',
  '§¦' => 'ã�„',
  '§§' => 'ãŽ£',
  '§¨' => 'ãŽ¤',
  '§©' => 'ãŽ¥',
  '§ª' => 'ãŽ¦',
  '§«' => 'ãŽ™',
  '§¬' => 'ãŽš',
  '§­' => 'ãŽ›',
  '§®' => 'ãŽœ',
  '§¯' => 'ãŽ�',
  '§°' => 'ãŽž',
  '§±' => 'ãŽŸ',
  '§²' => 'ãŽ ',
  '§³' => 'ãŽ¡',
  '§´' => 'ãŽ¢',
  '§µ' => 'ã�Š',
  '§¶' => 'ãŽ�',
  '§·' => 'ãŽŽ',
  '§¸' => 'ãŽ�',
  '§¹' => 'ã��',
  '§º' => 'ãŽˆ',
  '§»' => 'ãŽ‰',
  '§¼' => 'ã�ˆ',
  '§½' => 'ãŽ§',
  '§¾' => 'ãŽ¨',
  '§¿' => 'ãŽ°',
  '§À' => 'ãŽ±',
  '§Á' => 'ãŽ²',
  '§Â' => 'ãŽ³',
  '§Ã' => 'ãŽ´',
  '§Ä' => 'ãŽµ',
  '§Å' => 'ãŽ¶',
  '§Æ' => 'ãŽ·',
  '§Ç' => 'ãŽ¸',
  '§È' => 'ãŽ¹',
  '§É' => 'ãŽ€',
  '§Ê' => 'ãŽ�',
  '§Ë' => 'ãŽ‚',
  '§Ì' => 'ãŽƒ',
  '§Í' => 'ãŽ„',
  '§Î' => 'ãŽº',
  '§Ï' => 'ãŽ»',
  '§Ð' => 'ãŽ¼',
  '§Ñ' => 'ãŽ½',
  '§Ò' => 'ãŽ¾',
  '§Ó' => 'ãŽ¿',
  '§Ô' => 'ãŽ�',
  '§Õ' => 'ãŽ‘',
  '§Ö' => 'ãŽ’',
  '§×' => 'ãŽ“',
  '§Ø' => 'ãŽ”',
  '§Ù' => 'â„¦',
  '§Ú' => 'ã�€',
  '§Û' => 'ã��',
  '§Ü' => 'ãŽŠ',
  '§Ý' => 'ãŽ‹',
  '§Þ' => 'ãŽŒ',
  '§ß' => 'ã�–',
  '§à' => 'ã�…',
  '§á' => 'ãŽ­',
  '§â' => 'ãŽ®',
  '§ã' => 'ãŽ¯',
  '§ä' => 'ã�›',
  '§å' => 'ãŽ©',
  '§æ' => 'ãŽª',
  '§ç' => 'ãŽ«',
  '§è' => 'ãŽ¬',
  '§é' => 'ã��',
  '§ê' => 'ã��',
  '§ë' => 'ã�“',
  '§ì' => 'ã�ƒ',
  '§í' => 'ã�‰',
  '§î' => 'ã�œ',
  '§ï' => 'ã�†',
  '¨A' => 'ì­­',
  '¨B' => 'ì­®',
  '¨C' => 'ì­¯',
  '¨D' => 'ì­°',
  '¨E' => 'ì­±',
  '¨F' => 'ì­²',
  '¨G' => 'ì­³',
  '¨H' => 'ì­´',
  '¨I' => 'ì­µ',
  '¨J' => 'ì­¶',
  '¨K' => 'ì­·',
  '¨L' => 'ì­º',
  '¨M' => 'ì­»',
  '¨N' => 'ì­¼',
  '¨O' => 'ì­½',
  '¨P' => 'ì­¾',
  '¨Q' => 'ì­¿',
  '¨R' => 'ì®€',
  '¨S' => 'ì®�',
  '¨T' => 'ì®‚',
  '¨U' => 'ì®ƒ',
  '¨V' => 'ì®„',
  '¨W' => 'ì®…',
  '¨X' => 'ì®†',
  '¨Y' => 'ì®‡',
  '¨Z' => 'ì®ˆ',
  '¨a' => 'ì®‰',
  '¨b' => 'ì®Š',
  '¨c' => 'ì®‹',
  '¨d' => 'ì®Œ',
  '¨e' => 'ì®�',
  '¨f' => 'ì®Ž',
  '¨g' => 'ì®�',
  '¨h' => 'ì®�',
  '¨i' => 'ì®‘',
  '¨j' => 'ì®’',
  '¨k' => 'ì®“',
  '¨l' => 'ì®”',
  '¨m' => 'ì®•',
  '¨n' => 'ì®–',
  '¨o' => 'ì®—',
  '¨p' => 'ì®˜',
  '¨q' => 'ì®™',
  '¨r' => 'ì®š',
  '¨s' => 'ì®›',
  '¨t' => 'ì®�',
  '¨u' => 'ì®ž',
  '¨v' => 'ì®Ÿ',
  '¨w' => 'ì® ',
  '¨x' => 'ì®¡',
  '¨y' => 'ì®¢',
  '¨z' => 'ì®£',
  '¨�' => 'ì®¤',
  '¨‚' => 'ì®¥',
  '¨ƒ' => 'ì®¦',
  '¨„' => 'ì®§',
  '¨…' => 'ì®¨',
  '¨†' => 'ì®©',
  '¨‡' => 'ì®ª',
  '¨ˆ' => 'ì®«',
  '¨‰' => 'ì®¬',
  '¨Š' => 'ì®­',
  '¨‹' => 'ì®®',
  '¨Œ' => 'ì®¯',
  '¨�' => 'ì®°',
  '¨Ž' => 'ì®±',
  '¨�' => 'ì®²',
  '¨�' => 'ì®³',
  '¨‘' => 'ì®´',
  '¨’' => 'ì®µ',
  '¨“' => 'ì®¶',
  '¨”' => 'ì®·',
  '¨•' => 'ì®¹',
  '¨–' => 'ì®º',
  '¨—' => 'ì®»',
  '¨˜' => 'ì®¼',
  '¨™' => 'ì®½',
  '¨š' => 'ì®¾',
  '¨›' => 'ì®¿',
  '¨œ' => 'ì¯€',
  '¨�' => 'ì¯�',
  '¨ž' => 'ì¯‚',
  '¨Ÿ' => 'ì¯ƒ',
  '¨ ' => 'ì¯„',
  '¨¡' => 'Ã†',
  '¨¢' => 'Ã�',
  '¨£' => 'Âª',
  '¨¤' => 'Ä¦',
  '¨¦' => 'Ä²',
  '¨¨' => 'Ä¿',
  '¨©' => 'Å�',
  '¨ª' => 'Ã˜',
  '¨«' => 'Å’',
  '¨¬' => 'Âº',
  '¨­' => 'Ãž',
  '¨®' => 'Å¦',
  '¨¯' => 'ÅŠ',
  '¨±' => 'ã‰ ',
  '¨²' => 'ã‰¡',
  '¨³' => 'ã‰¢',
  '¨´' => 'ã‰£',
  '¨µ' => 'ã‰¤',
  '¨¶' => 'ã‰¥',
  '¨·' => 'ã‰¦',
  '¨¸' => 'ã‰§',
  '¨¹' => 'ã‰¨',
  '¨º' => 'ã‰©',
  '¨»' => 'ã‰ª',
  '¨¼' => 'ã‰«',
  '¨½' => 'ã‰¬',
  '¨¾' => 'ã‰­',
  '¨¿' => 'ã‰®',
  '¨À' => 'ã‰¯',
  '¨Á' => 'ã‰°',
  '¨Â' => 'ã‰±',
  '¨Ã' => 'ã‰²',
  '¨Ä' => 'ã‰³',
  '¨Å' => 'ã‰´',
  '¨Æ' => 'ã‰µ',
  '¨Ç' => 'ã‰¶',
  '¨È' => 'ã‰·',
  '¨É' => 'ã‰¸',
  '¨Ê' => 'ã‰¹',
  '¨Ë' => 'ã‰º',
  '¨Ì' => 'ã‰»',
  '¨Í' => 'â“�',
  '¨Î' => 'â“‘',
  '¨Ï' => 'â“’',
  '¨Ð' => 'â““',
  '¨Ñ' => 'â“”',
  '¨Ò' => 'â“•',
  '¨Ó' => 'â“–',
  '¨Ô' => 'â“—',
  '¨Õ' => 'â“˜',
  '¨Ö' => 'â“™',
  '¨×' => 'â“š',
  '¨Ø' => 'â“›',
  '¨Ù' => 'â“œ',
  '¨Ú' => 'â“�',
  '¨Û' => 'â“ž',
  '¨Ü' => 'â“Ÿ',
  '¨Ý' => 'â“ ',
  '¨Þ' => 'â“¡',
  '¨ß' => 'â“¢',
  '¨à' => 'â“£',
  '¨á' => 'â“¤',
  '¨â' => 'â“¥',
  '¨ã' => 'â“¦',
  '¨ä' => 'â“§',
  '¨å' => 'â“¨',
  '¨æ' => 'â“©',
  '¨ç' => 'â‘ ',
  '¨è' => 'â‘¡',
  '¨é' => 'â‘¢',
  '¨ê' => 'â‘£',
  '¨ë' => 'â‘¤',
  '¨ì' => 'â‘¥',
  '¨í' => 'â‘¦',
  '¨î' => 'â‘§',
  '¨ï' => 'â‘¨',
  '¨ð' => 'â‘©',
  '¨ñ' => 'â‘ª',
  '¨ò' => 'â‘«',
  '¨ó' => 'â‘¬',
  '¨ô' => 'â‘­',
  '¨õ' => 'â‘®',
  '¨ö' => 'Â½',
  '¨÷' => 'â…“',
  '¨ø' => 'â…”',
  '¨ù' => 'Â¼',
  '¨ú' => 'Â¾',
  '¨û' => 'â…›',
  '¨ü' => 'â…œ',
  '¨ý' => 'â…�',
  '¨þ' => 'â…ž',
  '©A' => 'ì¯…',
  '©B' => 'ì¯†',
  '©C' => 'ì¯‡',
  '©D' => 'ì¯ˆ',
  '©E' => 'ì¯‰',
  '©F' => 'ì¯Š',
  '©G' => 'ì¯‹',
  '©H' => 'ì¯Œ',
  '©I' => 'ì¯�',
  '©J' => 'ì¯Ž',
  '©K' => 'ì¯�',
  '©L' => 'ì¯�',
  '©M' => 'ì¯‘',
  '©N' => 'ì¯’',
  '©O' => 'ì¯“',
  '©P' => 'ì¯•',
  '©Q' => 'ì¯–',
  '©R' => 'ì¯—',
  '©S' => 'ì¯˜',
  '©T' => 'ì¯™',
  '©U' => 'ì¯š',
  '©V' => 'ì¯›',
  '©W' => 'ì¯œ',
  '©X' => 'ì¯�',
  '©Y' => 'ì¯ž',
  '©Z' => 'ì¯Ÿ',
  '©a' => 'ì¯ ',
  '©b' => 'ì¯¡',
  '©c' => 'ì¯¢',
  '©d' => 'ì¯£',
  '©e' => 'ì¯¥',
  '©f' => 'ì¯¦',
  '©g' => 'ì¯¨',
  '©h' => 'ì¯ª',
  '©i' => 'ì¯«',
  '©j' => 'ì¯¬',
  '©k' => 'ì¯­',
  '©l' => 'ì¯®',
  '©m' => 'ì¯¯',
  '©n' => 'ì¯°',
  '©o' => 'ì¯±',
  '©p' => 'ì¯²',
  '©q' => 'ì¯³',
  '©r' => 'ì¯´',
  '©s' => 'ì¯µ',
  '©t' => 'ì¯¶',
  '©u' => 'ì¯·',
  '©v' => 'ì¯¸',
  '©w' => 'ì¯¹',
  '©x' => 'ì¯º',
  '©y' => 'ì¯»',
  '©z' => 'ì¯¼',
  '©�' => 'ì¯½',
  '©‚' => 'ì¯¾',
  '©ƒ' => 'ì¯¿',
  '©„' => 'ì°€',
  '©…' => 'ì°�',
  '©†' => 'ì°‚',
  '©‡' => 'ì°ƒ',
  '©ˆ' => 'ì°„',
  '©‰' => 'ì°…',
  '©Š' => 'ì°†',
  '©‹' => 'ì°‡',
  '©Œ' => 'ì°ˆ',
  '©�' => 'ì°‰',
  '©Ž' => 'ì°Š',
  '©�' => 'ì°‹',
  '©�' => 'ì°Ž',
  '©‘' => 'ì°�',
  '©’' => 'ì°‘',
  '©“' => 'ì°’',
  '©”' => 'ì°“',
  '©•' => 'ì°•',
  '©–' => 'ì°–',
  '©—' => 'ì°—',
  '©˜' => 'ì°˜',
  '©™' => 'ì°™',
  '©š' => 'ì°š',
  '©›' => 'ì°›',
  '©œ' => 'ì°ž',
  '©�' => 'ì°Ÿ',
  '©ž' => 'ì° ',
  '©Ÿ' => 'ì°£',
  '© ' => 'ì°¤',
  '©¡' => 'Ã¦',
  '©¢' => 'Ä‘',
  '©£' => 'Ã°',
  '©¤' => 'Ä§',
  '©¥' => 'Ä±',
  '©¦' => 'Ä³',
  '©§' => 'Ä¸',
  '©¨' => 'Å€',
  '©©' => 'Å‚',
  '©ª' => 'Ã¸',
  '©«' => 'Å“',
  '©¬' => 'ÃŸ',
  '©­' => 'Ã¾',
  '©®' => 'Å§',
  '©¯' => 'Å‹',
  '©°' => 'Å‰',
  '©±' => 'ãˆ€',
  '©²' => 'ãˆ�',
  '©³' => 'ãˆ‚',
  '©´' => 'ãˆƒ',
  '©µ' => 'ãˆ„',
  '©¶' => 'ãˆ…',
  '©·' => 'ãˆ†',
  '©¸' => 'ãˆ‡',
  '©¹' => 'ãˆˆ',
  '©º' => 'ãˆ‰',
  '©»' => 'ãˆŠ',
  '©¼' => 'ãˆ‹',
  '©½' => 'ãˆŒ',
  '©¾' => 'ãˆ�',
  '©¿' => 'ãˆŽ',
  '©À' => 'ãˆ�',
  '©Á' => 'ãˆ�',
  '©Â' => 'ãˆ‘',
  '©Ã' => 'ãˆ’',
  '©Ä' => 'ãˆ“',
  '©Å' => 'ãˆ”',
  '©Æ' => 'ãˆ•',
  '©Ç' => 'ãˆ–',
  '©È' => 'ãˆ—',
  '©É' => 'ãˆ˜',
  '©Ê' => 'ãˆ™',
  '©Ë' => 'ãˆš',
  '©Ì' => 'ãˆ›',
  '©Í' => 'â’œ',
  '©Î' => 'â’�',
  '©Ï' => 'â’ž',
  '©Ð' => 'â’Ÿ',
  '©Ñ' => 'â’ ',
  '©Ò' => 'â’¡',
  '©Ó' => 'â’¢',
  '©Ô' => 'â’£',
  '©Õ' => 'â’¤',
  '©Ö' => 'â’¥',
  '©×' => 'â’¦',
  '©Ø' => 'â’§',
  '©Ù' => 'â’¨',
  '©Ú' => 'â’©',
  '©Û' => 'â’ª',
  '©Ü' => 'â’«',
  '©Ý' => 'â’¬',
  '©Þ' => 'â’­',
  '©ß' => 'â’®',
  '©à' => 'â’¯',
  '©á' => 'â’°',
  '©â' => 'â’±',
  '©ã' => 'â’²',
  '©ä' => 'â’³',
  '©å' => 'â’´',
  '©æ' => 'â’µ',
  '©ç' => 'â‘´',
  '©è' => 'â‘µ',
  '©é' => 'â‘¶',
  '©ê' => 'â‘·',
  '©ë' => 'â‘¸',
  '©ì' => 'â‘¹',
  '©í' => 'â‘º',
  '©î' => 'â‘»',
  '©ï' => 'â‘¼',
  '©ð' => 'â‘½',
  '©ñ' => 'â‘¾',
  '©ò' => 'â‘¿',
  '©ó' => 'â’€',
  '©ô' => 'â’�',
  '©õ' => 'â’‚',
  '©ö' => 'Â¹',
  '©÷' => 'Â²',
  '©ø' => 'Â³',
  '©ù' => 'â�´',
  '©ú' => 'â�¿',
  '©û' => 'â‚�',
  '©ü' => 'â‚‚',
  '©ý' => 'â‚ƒ',
  '©þ' => 'â‚„',
  'ªA' => 'ì°¥',
  'ªB' => 'ì°¦',
  'ªC' => 'ì°ª',
  'ªD' => 'ì°«',
  'ªE' => 'ì°­',
  'ªF' => 'ì°¯',
  'ªG' => 'ì°±',
  'ªH' => 'ì°²',
  'ªI' => 'ì°³',
  'ªJ' => 'ì°´',
  'ªK' => 'ì°µ',
  'ªL' => 'ì°¶',
  'ªM' => 'ì°·',
  'ªN' => 'ì°º',
  'ªO' => 'ì°¿',
  'ªP' => 'ì±€',
  'ªQ' => 'ì±�',
  'ªR' => 'ì±‚',
  'ªS' => 'ì±ƒ',
  'ªT' => 'ì±†',
  'ªU' => 'ì±‡',
  'ªV' => 'ì±‰',
  'ªW' => 'ì±Š',
  'ªX' => 'ì±‹',
  'ªY' => 'ì±�',
  'ªZ' => 'ì±Ž',
  'ªa' => 'ì±�',
  'ªb' => 'ì±�',
  'ªc' => 'ì±‘',
  'ªd' => 'ì±’',
  'ªe' => 'ì±“',
  'ªf' => 'ì±–',
  'ªg' => 'ì±š',
  'ªh' => 'ì±›',
  'ªi' => 'ì±œ',
  'ªj' => 'ì±�',
  'ªk' => 'ì±ž',
  'ªl' => 'ì±Ÿ',
  'ªm' => 'ì±¡',
  'ªn' => 'ì±¢',
  'ªo' => 'ì±£',
  'ªp' => 'ì±¥',
  'ªq' => 'ì±§',
  'ªr' => 'ì±©',
  'ªs' => 'ì±ª',
  'ªt' => 'ì±«',
  'ªu' => 'ì±¬',
  'ªv' => 'ì±­',
  'ªw' => 'ì±®',
  'ªx' => 'ì±¯',
  'ªy' => 'ì±±',
  'ªz' => 'ì±²',
  'ª�' => 'ì±³',
  'ª‚' => 'ì±´',
  'ªƒ' => 'ì±¶',
  'ª„' => 'ì±·',
  'ª…' => 'ì±¸',
  'ª†' => 'ì±¹',
  'ª‡' => 'ì±º',
  'ªˆ' => 'ì±»',
  'ª‰' => 'ì±¼',
  'ªŠ' => 'ì±½',
  'ª‹' => 'ì±¾',
  'ªŒ' => 'ì±¿',
  'ª�' => 'ì²€',
  'ªŽ' => 'ì²�',
  'ª�' => 'ì²‚',
  'ª�' => 'ì²ƒ',
  'ª‘' => 'ì²„',
  'ª’' => 'ì²…',
  'ª“' => 'ì²†',
  'ª”' => 'ì²‡',
  'ª•' => 'ì²ˆ',
  'ª–' => 'ì²‰',
  'ª—' => 'ì²Š',
  'ª˜' => 'ì²‹',
  'ª™' => 'ì²Œ',
  'ªš' => 'ì²�',
  'ª›' => 'ì²Ž',
  'ªœ' => 'ì²�',
  'ª�' => 'ì²�',
  'ªž' => 'ì²‘',
  'ªŸ' => 'ì²’',
  'ª ' => 'ì²“',
  'ª¡' => 'ã��',
  'ª¢' => 'ã�‚',
  'ª£' => 'ã�ƒ',
  'ª¤' => 'ã�„',
  'ª¥' => 'ã�…',
  'ª¦' => 'ã�†',
  'ª§' => 'ã�‡',
  'ª¨' => 'ã�ˆ',
  'ª©' => 'ã�‰',
  'ªª' => 'ã�Š',
  'ª«' => 'ã�‹',
  'ª¬' => 'ã�Œ',
  'ª­' => 'ã��',
  'ª®' => 'ã�Ž',
  'ª¯' => 'ã��',
  'ª°' => 'ã��',
  'ª±' => 'ã�‘',
  'ª²' => 'ã�’',
  'ª³' => 'ã�“',
  'ª´' => 'ã�”',
  'ªµ' => 'ã�•',
  'ª¶' => 'ã�–',
  'ª·' => 'ã�—',
  'ª¸' => 'ã�˜',
  'ª¹' => 'ã�™',
  'ªº' => 'ã�š',
  'ª»' => 'ã�›',
  'ª¼' => 'ã�œ',
  'ª½' => 'ã��',
  'ª¾' => 'ã�ž',
  'ª¿' => 'ã�Ÿ',
  'ªÀ' => 'ã� ',
  'ªÁ' => 'ã�¡',
  'ªÂ' => 'ã�¢',
  'ªÃ' => 'ã�£',
  'ªÄ' => 'ã�¤',
  'ªÅ' => 'ã�¥',
  'ªÆ' => 'ã�¦',
  'ªÇ' => 'ã�§',
  'ªÈ' => 'ã�¨',
  'ªÉ' => 'ã�©',
  'ªÊ' => 'ã�ª',
  'ªË' => 'ã�«',
  'ªÌ' => 'ã�¬',
  'ªÍ' => 'ã�­',
  'ªÎ' => 'ã�®',
  'ªÏ' => 'ã�¯',
  'ªÐ' => 'ã�°',
  'ªÑ' => 'ã�±',
  'ªÒ' => 'ã�²',
  'ªÓ' => 'ã�³',
  'ªÔ' => 'ã�´',
  'ªÕ' => 'ã�µ',
  'ªÖ' => 'ã�¶',
  'ª×' => 'ã�·',
  'ªØ' => 'ã�¸',
  'ªÙ' => 'ã�¹',
  'ªÚ' => 'ã�º',
  'ªÛ' => 'ã�»',
  'ªÜ' => 'ã�¼',
  'ªÝ' => 'ã�½',
  'ªÞ' => 'ã�¾',
  'ªß' => 'ã�¿',
  'ªà' => 'ã‚€',
  'ªá' => 'ã‚�',
  'ªâ' => 'ã‚‚',
  'ªã' => 'ã‚ƒ',
  'ªä' => 'ã‚„',
  'ªå' => 'ã‚…',
  'ªæ' => 'ã‚†',
  'ªç' => 'ã‚‡',
  'ªè' => 'ã‚ˆ',
  'ªé' => 'ã‚‰',
  'ªê' => 'ã‚Š',
  'ªë' => 'ã‚‹',
  'ªì' => 'ã‚Œ',
  'ªí' => 'ã‚�',
  'ªî' => 'ã‚Ž',
  'ªï' => 'ã‚�',
  'ªð' => 'ã‚�',
  'ªñ' => 'ã‚‘',
  'ªò' => 'ã‚’',
  'ªó' => 'ã‚“',
  '«A' => 'ì²”',
  '«B' => 'ì²•',
  '«C' => 'ì²–',
  '«D' => 'ì²—',
  '«E' => 'ì²š',
  '«F' => 'ì²›',
  '«G' => 'ì²�',
  '«H' => 'ì²ž',
  '«I' => 'ì²Ÿ',
  '«J' => 'ì²¡',
  '«K' => 'ì²¢',
  '«L' => 'ì²£',
  '«M' => 'ì²¤',
  '«N' => 'ì²¥',
  '«O' => 'ì²¦',
  '«P' => 'ì²§',
  '«Q' => 'ì²ª',
  '«R' => 'ì²®',
  '«S' => 'ì²¯',
  '«T' => 'ì²°',
  '«U' => 'ì²±',
  '«V' => 'ì²²',
  '«W' => 'ì²³',
  '«X' => 'ì²¶',
  '«Y' => 'ì²·',
  '«Z' => 'ì²¹',
  '«a' => 'ì²º',
  '«b' => 'ì²»',
  '«c' => 'ì²½',
  '«d' => 'ì²¾',
  '«e' => 'ì²¿',
  '«f' => 'ì³€',
  '«g' => 'ì³�',
  '«h' => 'ì³‚',
  '«i' => 'ì³ƒ',
  '«j' => 'ì³†',
  '«k' => 'ì³ˆ',
  '«l' => 'ì³Š',
  '«m' => 'ì³‹',
  '«n' => 'ì³Œ',
  '«o' => 'ì³�',
  '«p' => 'ì³Ž',
  '«q' => 'ì³�',
  '«r' => 'ì³‘',
  '«s' => 'ì³’',
  '«t' => 'ì³“',
  '«u' => 'ì³•',
  '«v' => 'ì³–',
  '«w' => 'ì³—',
  '«x' => 'ì³˜',
  '«y' => 'ì³™',
  '«z' => 'ì³š',
  '«�' => 'ì³›',
  '«‚' => 'ì³œ',
  '«ƒ' => 'ì³�',
  '«„' => 'ì³ž',
  '«…' => 'ì³Ÿ',
  '«†' => 'ì³ ',
  '«‡' => 'ì³¡',
  '«ˆ' => 'ì³¢',
  '«‰' => 'ì³£',
  '«Š' => 'ì³¥',
  '«‹' => 'ì³¦',
  '«Œ' => 'ì³§',
  '«�' => 'ì³¨',
  '«Ž' => 'ì³©',
  '«�' => 'ì³ª',
  '«�' => 'ì³«',
  '«‘' => 'ì³­',
  '«’' => 'ì³®',
  '«“' => 'ì³¯',
  '«”' => 'ì³±',
  '«•' => 'ì³²',
  '«–' => 'ì³³',
  '«—' => 'ì³´',
  '«˜' => 'ì³µ',
  '«™' => 'ì³¶',
  '«š' => 'ì³·',
  '«›' => 'ì³¸',
  '«œ' => 'ì³¹',
  '«�' => 'ì³º',
  '«ž' => 'ì³»',
  '«Ÿ' => 'ì³¼',
  '« ' => 'ì³½',
  '«¡' => 'ã‚¡',
  '«¢' => 'ã‚¢',
  '«£' => 'ã‚£',
  '«¤' => 'ã‚¤',
  '«¥' => 'ã‚¥',
  '«¦' => 'ã‚¦',
  '«§' => 'ã‚§',
  '«¨' => 'ã‚¨',
  '«©' => 'ã‚©',
  '«ª' => 'ã‚ª',
  '««' => 'ã‚«',
  '«¬' => 'ã‚¬',
  '«­' => 'ã‚­',
  '«®' => 'ã‚®',
  '«¯' => 'ã‚¯',
  '«°' => 'ã‚°',
  '«±' => 'ã‚±',
  '«²' => 'ã‚²',
  '«³' => 'ã‚³',
  '«´' => 'ã‚´',
  '«µ' => 'ã‚µ',
  '«¶' => 'ã‚¶',
  '«·' => 'ã‚·',
  '«¸' => 'ã‚¸',
  '«¹' => 'ã‚¹',
  '«º' => 'ã‚º',
  '«»' => 'ã‚»',
  '«¼' => 'ã‚¼',
  '«½' => 'ã‚½',
  '«¾' => 'ã‚¾',
  '«¿' => 'ã‚¿',
  '«À' => 'ãƒ€',
  '«Á' => 'ãƒ�',
  '«Â' => 'ãƒ‚',
  '«Ã' => 'ãƒƒ',
  '«Ä' => 'ãƒ„',
  '«Å' => 'ãƒ…',
  '«Æ' => 'ãƒ†',
  '«Ç' => 'ãƒ‡',
  '«È' => 'ãƒˆ',
  '«É' => 'ãƒ‰',
  '«Ê' => 'ãƒŠ',
  '«Ë' => 'ãƒ‹',
  '«Ì' => 'ãƒŒ',
  '«Í' => 'ãƒ�',
  '«Î' => 'ãƒŽ',
  '«Ï' => 'ãƒ�',
  '«Ð' => 'ãƒ�',
  '«Ñ' => 'ãƒ‘',
  '«Ò' => 'ãƒ’',
  '«Ó' => 'ãƒ“',
  '«Ô' => 'ãƒ”',
  '«Õ' => 'ãƒ•',
  '«Ö' => 'ãƒ–',
  '«×' => 'ãƒ—',
  '«Ø' => 'ãƒ˜',
  '«Ù' => 'ãƒ™',
  '«Ú' => 'ãƒš',
  '«Û' => 'ãƒ›',
  '«Ü' => 'ãƒœ',
  '«Ý' => 'ãƒ�',
  '«Þ' => 'ãƒž',
  '«ß' => 'ãƒŸ',
  '«à' => 'ãƒ ',
  '«á' => 'ãƒ¡',
  '«â' => 'ãƒ¢',
  '«ã' => 'ãƒ£',
  '«ä' => 'ãƒ¤',
  '«å' => 'ãƒ¥',
  '«æ' => 'ãƒ¦',
  '«ç' => 'ãƒ§',
  '«è' => 'ãƒ¨',
  '«é' => 'ãƒ©',
  '«ê' => 'ãƒª',
  '«ë' => 'ãƒ«',
  '«ì' => 'ãƒ¬',
  '«í' => 'ãƒ­',
  '«î' => 'ãƒ®',
  '«ï' => 'ãƒ¯',
  '«ð' => 'ãƒ°',
  '«ñ' => 'ãƒ±',
  '«ò' => 'ãƒ²',
  '«ó' => 'ãƒ³',
  '«ô' => 'ãƒ´',
  '«õ' => 'ãƒµ',
  '«ö' => 'ãƒ¶',
  '¬A' => 'ì³¾',
  '¬B' => 'ì³¿',
  '¬C' => 'ì´€',
  '¬D' => 'ì´‚',
  '¬E' => 'ì´ƒ',
  '¬F' => 'ì´„',
  '¬G' => 'ì´…',
  '¬H' => 'ì´†',
  '¬I' => 'ì´‡',
  '¬J' => 'ì´Š',
  '¬K' => 'ì´‹',
  '¬L' => 'ì´�',
  '¬M' => 'ì´Ž',
  '¬N' => 'ì´�',
  '¬O' => 'ì´‘',
  '¬P' => 'ì´’',
  '¬Q' => 'ì´“',
  '¬R' => 'ì´”',
  '¬S' => 'ì´•',
  '¬T' => 'ì´–',
  '¬U' => 'ì´—',
  '¬V' => 'ì´š',
  '¬W' => 'ì´œ',
  '¬X' => 'ì´ž',
  '¬Y' => 'ì´Ÿ',
  '¬Z' => 'ì´ ',
  '¬a' => 'ì´¡',
  '¬b' => 'ì´¢',
  '¬c' => 'ì´£',
  '¬d' => 'ì´¥',
  '¬e' => 'ì´¦',
  '¬f' => 'ì´§',
  '¬g' => 'ì´©',
  '¬h' => 'ì´ª',
  '¬i' => 'ì´«',
  '¬j' => 'ì´­',
  '¬k' => 'ì´®',
  '¬l' => 'ì´¯',
  '¬m' => 'ì´°',
  '¬n' => 'ì´±',
  '¬o' => 'ì´²',
  '¬p' => 'ì´³',
  '¬q' => 'ì´´',
  '¬r' => 'ì´µ',
  '¬s' => 'ì´¶',
  '¬t' => 'ì´·',
  '¬u' => 'ì´¸',
  '¬v' => 'ì´º',
  '¬w' => 'ì´»',
  '¬x' => 'ì´¼',
  '¬y' => 'ì´½',
  '¬z' => 'ì´¾',
  '¬�' => 'ì´¿',
  '¬‚' => 'ìµ€',
  '¬ƒ' => 'ìµ�',
  '¬„' => 'ìµ‚',
  '¬…' => 'ìµƒ',
  '¬†' => 'ìµ„',
  '¬‡' => 'ìµ…',
  '¬ˆ' => 'ìµ†',
  '¬‰' => 'ìµ‡',
  '¬Š' => 'ìµˆ',
  '¬‹' => 'ìµ‰',
  '¬Œ' => 'ìµŠ',
  '¬�' => 'ìµ‹',
  '¬Ž' => 'ìµŒ',
  '¬�' => 'ìµ�',
  '¬�' => 'ìµŽ',
  '¬‘' => 'ìµ�',
  '¬’' => 'ìµ�',
  '¬“' => 'ìµ‘',
  '¬”' => 'ìµ’',
  '¬•' => 'ìµ“',
  '¬–' => 'ìµ”',
  '¬—' => 'ìµ•',
  '¬˜' => 'ìµ–',
  '¬™' => 'ìµ—',
  '¬š' => 'ìµ˜',
  '¬›' => 'ìµ™',
  '¬œ' => 'ìµš',
  '¬�' => 'ìµ›',
  '¬ž' => 'ìµ�',
  '¬Ÿ' => 'ìµž',
  '¬ ' => 'ìµŸ',
  '¬¡' => 'Ð�',
  '¬¢' => 'Ð‘',
  '¬£' => 'Ð’',
  '¬¤' => 'Ð“',
  '¬¥' => 'Ð”',
  '¬¦' => 'Ð•',
  '¬§' => 'Ð�',
  '¬¨' => 'Ð–',
  '¬©' => 'Ð—',
  '¬ª' => 'Ð˜',
  '¬«' => 'Ð™',
  '¬¬' => 'Ðš',
  '¬­' => 'Ð›',
  '¬®' => 'Ðœ',
  '¬¯' => 'Ð�',
  '¬°' => 'Ðž',
  '¬±' => 'ÐŸ',
  '¬²' => 'Ð ',
  '¬³' => 'Ð¡',
  '¬´' => 'Ð¢',
  '¬µ' => 'Ð£',
  '¬¶' => 'Ð¤',
  '¬·' => 'Ð¥',
  '¬¸' => 'Ð¦',
  '¬¹' => 'Ð§',
  '¬º' => 'Ð¨',
  '¬»' => 'Ð©',
  '¬¼' => 'Ðª',
  '¬½' => 'Ð«',
  '¬¾' => 'Ð¬',
  '¬¿' => 'Ð­',
  '¬À' => 'Ð®',
  '¬Á' => 'Ð¯',
  '¬Ñ' => 'Ð°',
  '¬Ò' => 'Ð±',
  '¬Ó' => 'Ð²',
  '¬Ô' => 'Ð³',
  '¬Õ' => 'Ð´',
  '¬Ö' => 'Ðµ',
  '¬×' => 'Ñ‘',
  '¬Ø' => 'Ð¶',
  '¬Ù' => 'Ð·',
  '¬Ú' => 'Ð¸',
  '¬Û' => 'Ð¹',
  '¬Ü' => 'Ðº',
  '¬Ý' => 'Ð»',
  '¬Þ' => 'Ð¼',
  '¬ß' => 'Ð½',
  '¬à' => 'Ð¾',
  '¬á' => 'Ð¿',
  '¬â' => 'Ñ€',
  '¬ã' => 'Ñ�',
  '¬ä' => 'Ñ‚',
  '¬å' => 'Ñƒ',
  '¬æ' => 'Ñ„',
  '¬ç' => 'Ñ…',
  '¬è' => 'Ñ†',
  '¬é' => 'Ñ‡',
  '¬ê' => 'Ñˆ',
  '¬ë' => 'Ñ‰',
  '¬ì' => 'ÑŠ',
  '¬í' => 'Ñ‹',
  '¬î' => 'ÑŒ',
  '¬ï' => 'Ñ�',
  '¬ð' => 'ÑŽ',
  '¬ñ' => 'Ñ�',
  '­A' => 'ìµ¡',
  '­B' => 'ìµ¢',
  '­C' => 'ìµ£',
  '­D' => 'ìµ¥',
  '­E' => 'ìµ¦',
  '­F' => 'ìµ§',
  '­G' => 'ìµ¨',
  '­H' => 'ìµ©',
  '­I' => 'ìµª',
  '­J' => 'ìµ«',
  '­K' => 'ìµ®',
  '­L' => 'ìµ°',
  '­M' => 'ìµ²',
  '­N' => 'ìµ³',
  '­O' => 'ìµ´',
  '­P' => 'ìµµ',
  '­Q' => 'ìµ¶',
  '­R' => 'ìµ·',
  '­S' => 'ìµ¹',
  '­T' => 'ìµº',
  '­U' => 'ìµ»',
  '­V' => 'ìµ¼',
  '­W' => 'ìµ½',
  '­X' => 'ìµ¾',
  '­Y' => 'ìµ¿',
  '­Z' => 'ì¶€',
  '­a' => 'ì¶�',
  '­b' => 'ì¶‚',
  '­c' => 'ì¶ƒ',
  '­d' => 'ì¶„',
  '­e' => 'ì¶…',
  '­f' => 'ì¶†',
  '­g' => 'ì¶‡',
  '­h' => 'ì¶‰',
  '­i' => 'ì¶Š',
  '­j' => 'ì¶‹',
  '­k' => 'ì¶Œ',
  '­l' => 'ì¶�',
  '­m' => 'ì¶Ž',
  '­n' => 'ì¶�',
  '­o' => 'ì¶�',
  '­p' => 'ì¶‘',
  '­q' => 'ì¶’',
  '­r' => 'ì¶“',
  '­s' => 'ì¶–',
  '­t' => 'ì¶—',
  '­u' => 'ì¶™',
  '­v' => 'ì¶š',
  '­w' => 'ì¶›',
  '­x' => 'ì¶�',
  '­y' => 'ì¶ž',
  '­z' => 'ì¶Ÿ',
  '­�' => 'ì¶ ',
  '­‚' => 'ì¶¡',
  '­ƒ' => 'ì¶¢',
  '­„' => 'ì¶£',
  '­…' => 'ì¶¦',
  '­†' => 'ì¶¨',
  '­‡' => 'ì¶ª',
  '­ˆ' => 'ì¶«',
  '­‰' => 'ì¶¬',
  '­Š' => 'ì¶­',
  '­‹' => 'ì¶®',
  '­Œ' => 'ì¶¯',
  '­�' => 'ì¶±',
  '­Ž' => 'ì¶²',
  '­�' => 'ì¶³',
  '­�' => 'ì¶´',
  '­‘' => 'ì¶µ',
  '­’' => 'ì¶¶',
  '­“' => 'ì¶·',
  '­”' => 'ì¶¸',
  '­•' => 'ì¶¹',
  '­–' => 'ì¶º',
  '­—' => 'ì¶»',
  '­˜' => 'ì¶¼',
  '­™' => 'ì¶½',
  '­š' => 'ì¶¾',
  '­›' => 'ì¶¿',
  '­œ' => 'ì·€',
  '­�' => 'ì·�',
  '­ž' => 'ì·‚',
  '­Ÿ' => 'ì·ƒ',
  '­ ' => 'ì·…',
  '®A' => 'ì·†',
  '®B' => 'ì·‡',
  '®C' => 'ì·ˆ',
  '®D' => 'ì·‰',
  '®E' => 'ì·Š',
  '®F' => 'ì·‹',
  '®G' => 'ì·�',
  '®H' => 'ì·Ž',
  '®I' => 'ì·�',
  '®J' => 'ì·‘',
  '®K' => 'ì·’',
  '®L' => 'ì·“',
  '®M' => 'ì·”',
  '®N' => 'ì·•',
  '®O' => 'ì·–',
  '®P' => 'ì·—',
  '®Q' => 'ì·˜',
  '®R' => 'ì·™',
  '®S' => 'ì·š',
  '®T' => 'ì·›',
  '®U' => 'ì·œ',
  '®V' => 'ì·�',
  '®W' => 'ì·ž',
  '®X' => 'ì·Ÿ',
  '®Y' => 'ì· ',
  '®Z' => 'ì·¡',
  '®a' => 'ì·¢',
  '®b' => 'ì·£',
  '®c' => 'ì·¤',
  '®d' => 'ì·¥',
  '®e' => 'ì·¦',
  '®f' => 'ì·§',
  '®g' => 'ì·©',
  '®h' => 'ì·ª',
  '®i' => 'ì·«',
  '®j' => 'ì·­',
  '®k' => 'ì·®',
  '®l' => 'ì·¯',
  '®m' => 'ì·±',
  '®n' => 'ì·²',
  '®o' => 'ì·³',
  '®p' => 'ì·´',
  '®q' => 'ì·µ',
  '®r' => 'ì·¶',
  '®s' => 'ì··',
  '®t' => 'ì·º',
  '®u' => 'ì·¼',
  '®v' => 'ì·¾',
  '®w' => 'ì·¿',
  '®x' => 'ì¸€',
  '®y' => 'ì¸�',
  '®z' => 'ì¸‚',
  '®�' => 'ì¸ƒ',
  '®‚' => 'ì¸…',
  '®ƒ' => 'ì¸†',
  '®„' => 'ì¸‡',
  '®…' => 'ì¸‰',
  '®†' => 'ì¸Š',
  '®‡' => 'ì¸‹',
  '®ˆ' => 'ì¸�',
  '®‰' => 'ì¸Ž',
  '®Š' => 'ì¸�',
  '®‹' => 'ì¸�',
  '®Œ' => 'ì¸‘',
  '®�' => 'ì¸’',
  '®Ž' => 'ì¸“',
  '®�' => 'ì¸•',
  '®�' => 'ì¸–',
  '®‘' => 'ì¸—',
  '®’' => 'ì¸˜',
  '®“' => 'ì¸š',
  '®”' => 'ì¸›',
  '®•' => 'ì¸œ',
  '®–' => 'ì¸�',
  '®—' => 'ì¸ž',
  '®˜' => 'ì¸Ÿ',
  '®™' => 'ì¸¢',
  '®š' => 'ì¸£',
  '®›' => 'ì¸¥',
  '®œ' => 'ì¸¦',
  '®�' => 'ì¸§',
  '®ž' => 'ì¸©',
  '®Ÿ' => 'ì¸ª',
  '® ' => 'ì¸«',
  '¯A' => 'ì¸¬',
  '¯B' => 'ì¸­',
  '¯C' => 'ì¸®',
  '¯D' => 'ì¸¯',
  '¯E' => 'ì¸²',
  '¯F' => 'ì¸´',
  '¯G' => 'ì¸¶',
  '¯H' => 'ì¸·',
  '¯I' => 'ì¸¸',
  '¯J' => 'ì¸¹',
  '¯K' => 'ì¸º',
  '¯L' => 'ì¸»',
  '¯M' => 'ì¸¼',
  '¯N' => 'ì¸½',
  '¯O' => 'ì¸¾',
  '¯P' => 'ì¸¿',
  '¯Q' => 'ì¹€',
  '¯R' => 'ì¹�',
  '¯S' => 'ì¹‚',
  '¯T' => 'ì¹ƒ',
  '¯U' => 'ì¹„',
  '¯V' => 'ì¹…',
  '¯W' => 'ì¹†',
  '¯X' => 'ì¹‡',
  '¯Y' => 'ì¹ˆ',
  '¯Z' => 'ì¹‰',
  '¯a' => 'ì¹Š',
  '¯b' => 'ì¹‹',
  '¯c' => 'ì¹Œ',
  '¯d' => 'ì¹�',
  '¯e' => 'ì¹Ž',
  '¯f' => 'ì¹�',
  '¯g' => 'ì¹�',
  '¯h' => 'ì¹‘',
  '¯i' => 'ì¹’',
  '¯j' => 'ì¹“',
  '¯k' => 'ì¹”',
  '¯l' => 'ì¹•',
  '¯m' => 'ì¹–',
  '¯n' => 'ì¹—',
  '¯o' => 'ì¹š',
  '¯p' => 'ì¹›',
  '¯q' => 'ì¹�',
  '¯r' => 'ì¹ž',
  '¯s' => 'ì¹¢',
  '¯t' => 'ì¹£',
  '¯u' => 'ì¹¤',
  '¯v' => 'ì¹¥',
  '¯w' => 'ì¹¦',
  '¯x' => 'ì¹§',
  '¯y' => 'ì¹ª',
  '¯z' => 'ì¹¬',
  '¯�' => 'ì¹®',
  '¯‚' => 'ì¹¯',
  '¯ƒ' => 'ì¹°',
  '¯„' => 'ì¹±',
  '¯…' => 'ì¹²',
  '¯†' => 'ì¹³',
  '¯‡' => 'ì¹¶',
  '¯ˆ' => 'ì¹·',
  '¯‰' => 'ì¹¹',
  '¯Š' => 'ì¹º',
  '¯‹' => 'ì¹»',
  '¯Œ' => 'ì¹½',
  '¯�' => 'ì¹¾',
  '¯Ž' => 'ì¹¿',
  '¯�' => 'ìº€',
  '¯�' => 'ìº�',
  '¯‘' => 'ìº‚',
  '¯’' => 'ìºƒ',
  '¯“' => 'ìº†',
  '¯”' => 'ìºˆ',
  '¯•' => 'ìºŠ',
  '¯–' => 'ìº‹',
  '¯—' => 'ìºŒ',
  '¯˜' => 'ìº�',
  '¯™' => 'ìºŽ',
  '¯š' => 'ìº�',
  '¯›' => 'ìº’',
  '¯œ' => 'ìº“',
  '¯�' => 'ìº•',
  '¯ž' => 'ìº–',
  '¯Ÿ' => 'ìº—',
  '¯ ' => 'ìº™',
  '°A' => 'ìºš',
  '°B' => 'ìº›',
  '°C' => 'ìºœ',
  '°D' => 'ìº�',
  '°E' => 'ìºž',
  '°F' => 'ìºŸ',
  '°G' => 'ìº¢',
  '°H' => 'ìº¦',
  '°I' => 'ìº§',
  '°J' => 'ìº¨',
  '°K' => 'ìº©',
  '°L' => 'ìºª',
  '°M' => 'ìº«',
  '°N' => 'ìº®',
  '°O' => 'ìº¯',
  '°P' => 'ìº°',
  '°Q' => 'ìº±',
  '°R' => 'ìº²',
  '°S' => 'ìº³',
  '°T' => 'ìº´',
  '°U' => 'ìºµ',
  '°V' => 'ìº¶',
  '°W' => 'ìº·',
  '°X' => 'ìº¸',
  '°Y' => 'ìº¹',
  '°Z' => 'ìºº',
  '°a' => 'ìº»',
  '°b' => 'ìº¼',
  '°c' => 'ìº½',
  '°d' => 'ìº¾',
  '°e' => 'ìº¿',
  '°f' => 'ì»€',
  '°g' => 'ì»‚',
  '°h' => 'ì»ƒ',
  '°i' => 'ì»„',
  '°j' => 'ì»…',
  '°k' => 'ì»†',
  '°l' => 'ì»‡',
  '°m' => 'ì»ˆ',
  '°n' => 'ì»‰',
  '°o' => 'ì»Š',
  '°p' => 'ì»‹',
  '°q' => 'ì»Œ',
  '°r' => 'ì»�',
  '°s' => 'ì»Ž',
  '°t' => 'ì»�',
  '°u' => 'ì»�',
  '°v' => 'ì»‘',
  '°w' => 'ì»’',
  '°x' => 'ì»“',
  '°y' => 'ì»”',
  '°z' => 'ì»•',
  '°�' => 'ì»–',
  '°‚' => 'ì»—',
  '°ƒ' => 'ì»˜',
  '°„' => 'ì»™',
  '°…' => 'ì»š',
  '°†' => 'ì»›',
  '°‡' => 'ì»œ',
  '°ˆ' => 'ì»�',
  '°‰' => 'ì»ž',
  '°Š' => 'ì»Ÿ',
  '°‹' => 'ì» ',
  '°Œ' => 'ì»¡',
  '°�' => 'ì»¢',
  '°Ž' => 'ì»£',
  '°�' => 'ì»¦',
  '°�' => 'ì»§',
  '°‘' => 'ì»©',
  '°’' => 'ì»ª',
  '°“' => 'ì»­',
  '°”' => 'ì»®',
  '°•' => 'ì»¯',
  '°–' => 'ì»°',
  '°—' => 'ì»±',
  '°˜' => 'ì»²',
  '°™' => 'ì»³',
  '°š' => 'ì»¶',
  '°›' => 'ì»º',
  '°œ' => 'ì»»',
  '°�' => 'ì»¼',
  '°ž' => 'ì»½',
  '°Ÿ' => 'ì»¾',
  '° ' => 'ì»¿',
  '°¡' => 'ê°€',
  '°¢' => 'ê°�',
  '°£' => 'ê°„',
  '°¤' => 'ê°‡',
  '°¥' => 'ê°ˆ',
  '°¦' => 'ê°‰',
  '°§' => 'ê°Š',
  '°¨' => 'ê°�',
  '°©' => 'ê°‘',
  '°ª' => 'ê°’',
  '°«' => 'ê°“',
  '°¬' => 'ê°”',
  '°­' => 'ê°•',
  '°®' => 'ê°–',
  '°¯' => 'ê°—',
  '°°' => 'ê°™',
  '°±' => 'ê°š',
  '°²' => 'ê°›',
  '°³' => 'ê°œ',
  '°´' => 'ê°�',
  '°µ' => 'ê° ',
  '°¶' => 'ê°¤',
  '°·' => 'ê°¬',
  '°¸' => 'ê°­',
  '°¹' => 'ê°¯',
  '°º' => 'ê°°',
  '°»' => 'ê°±',
  '°¼' => 'ê°¸',
  '°½' => 'ê°¹',
  '°¾' => 'ê°¼',
  '°¿' => 'ê±€',
  '°À' => 'ê±‹',
  '°Á' => 'ê±�',
  '°Â' => 'ê±”',
  '°Ã' => 'ê±˜',
  '°Ä' => 'ê±œ',
  '°Å' => 'ê±°',
  '°Æ' => 'ê±±',
  '°Ç' => 'ê±´',
  '°È' => 'ê±·',
  '°É' => 'ê±¸',
  '°Ê' => 'ê±º',
  '°Ë' => 'ê²€',
  '°Ì' => 'ê²�',
  '°Í' => 'ê²ƒ',
  '°Î' => 'ê²„',
  '°Ï' => 'ê²…',
  '°Ð' => 'ê²†',
  '°Ñ' => 'ê²‰',
  '°Ò' => 'ê²Š',
  '°Ó' => 'ê²‹',
  '°Ô' => 'ê²Œ',
  '°Õ' => 'ê²�',
  '°Ö' => 'ê²”',
  '°×' => 'ê²œ',
  '°Ø' => 'ê²�',
  '°Ù' => 'ê²Ÿ',
  '°Ú' => 'ê² ',
  '°Û' => 'ê²¡',
  '°Ü' => 'ê²¨',
  '°Ý' => 'ê²©',
  '°Þ' => 'ê²ª',
  '°ß' => 'ê²¬',
  '°à' => 'ê²¯',
  '°á' => 'ê²°',
  '°â' => 'ê²¸',
  '°ã' => 'ê²¹',
  '°ä' => 'ê²»',
  '°å' => 'ê²¼',
  '°æ' => 'ê²½',
  '°ç' => 'ê³�',
  '°è' => 'ê³„',
  '°é' => 'ê³ˆ',
  '°ê' => 'ê³Œ',
  '°ë' => 'ê³•',
  '°ì' => 'ê³—',
  '°í' => 'ê³ ',
  '°î' => 'ê³¡',
  '°ï' => 'ê³¤',
  '°ð' => 'ê³§',
  '°ñ' => 'ê³¨',
  '°ò' => 'ê³ª',
  '°ó' => 'ê³¬',
  '°ô' => 'ê³¯',
  '°õ' => 'ê³°',
  '°ö' => 'ê³±',
  '°÷' => 'ê³³',
  '°ø' => 'ê³µ',
  '°ù' => 'ê³¶',
  '°ú' => 'ê³¼',
  '°û' => 'ê³½',
  '°ü' => 'ê´€',
  '°ý' => 'ê´„',
  '°þ' => 'ê´†',
  '±A' => 'ì¼‚',
  '±B' => 'ì¼ƒ',
  '±C' => 'ì¼…',
  '±D' => 'ì¼†',
  '±E' => 'ì¼‡',
  '±F' => 'ì¼‰',
  '±G' => 'ì¼Š',
  '±H' => 'ì¼‹',
  '±I' => 'ì¼Œ',
  '±J' => 'ì¼�',
  '±K' => 'ì¼Ž',
  '±L' => 'ì¼�',
  '±M' => 'ì¼’',
  '±N' => 'ì¼”',
  '±O' => 'ì¼–',
  '±P' => 'ì¼—',
  '±Q' => 'ì¼˜',
  '±R' => 'ì¼™',
  '±S' => 'ì¼š',
  '±T' => 'ì¼›',
  '±U' => 'ì¼�',
  '±V' => 'ì¼ž',
  '±W' => 'ì¼Ÿ',
  '±X' => 'ì¼¡',
  '±Y' => 'ì¼¢',
  '±Z' => 'ì¼£',
  '±a' => 'ì¼¥',
  '±b' => 'ì¼¦',
  '±c' => 'ì¼§',
  '±d' => 'ì¼¨',
  '±e' => 'ì¼©',
  '±f' => 'ì¼ª',
  '±g' => 'ì¼«',
  '±h' => 'ì¼®',
  '±i' => 'ì¼²',
  '±j' => 'ì¼³',
  '±k' => 'ì¼´',
  '±l' => 'ì¼µ',
  '±m' => 'ì¼¶',
  '±n' => 'ì¼·',
  '±o' => 'ì¼¹',
  '±p' => 'ì¼º',
  '±q' => 'ì¼»',
  '±r' => 'ì¼¼',
  '±s' => 'ì¼½',
  '±t' => 'ì¼¾',
  '±u' => 'ì¼¿',
  '±v' => 'ì½€',
  '±w' => 'ì½�',
  '±x' => 'ì½‚',
  '±y' => 'ì½ƒ',
  '±z' => 'ì½„',
  '±�' => 'ì½…',
  '±‚' => 'ì½†',
  '±ƒ' => 'ì½‡',
  '±„' => 'ì½ˆ',
  '±…' => 'ì½‰',
  '±†' => 'ì½Š',
  '±‡' => 'ì½‹',
  '±ˆ' => 'ì½Œ',
  '±‰' => 'ì½�',
  '±Š' => 'ì½Ž',
  '±‹' => 'ì½�',
  '±Œ' => 'ì½�',
  '±�' => 'ì½‘',
  '±Ž' => 'ì½’',
  '±�' => 'ì½“',
  '±�' => 'ì½–',
  '±‘' => 'ì½—',
  '±’' => 'ì½™',
  '±“' => 'ì½š',
  '±”' => 'ì½›',
  '±•' => 'ì½�',
  '±–' => 'ì½ž',
  '±—' => 'ì½Ÿ',
  '±˜' => 'ì½ ',
  '±™' => 'ì½¡',
  '±š' => 'ì½¢',
  '±›' => 'ì½£',
  '±œ' => 'ì½¦',
  '±�' => 'ì½¨',
  '±ž' => 'ì½ª',
  '±Ÿ' => 'ì½«',
  '± ' => 'ì½¬',
  '±¡' => 'ê´Œ',
  '±¢' => 'ê´�',
  '±£' => 'ê´�',
  '±¤' => 'ê´‘',
  '±¥' => 'ê´˜',
  '±¦' => 'ê´œ',
  '±§' => 'ê´ ',
  '±¨' => 'ê´©',
  '±©' => 'ê´¬',
  '±ª' => 'ê´­',
  '±«' => 'ê´´',
  '±¬' => 'ê´µ',
  '±­' => 'ê´¸',
  '±®' => 'ê´¼',
  '±¯' => 'êµ„',
  '±°' => 'êµ…',
  '±±' => 'êµ‡',
  '±²' => 'êµ‰',
  '±³' => 'êµ�',
  '±´' => 'êµ”',
  '±µ' => 'êµ˜',
  '±¶' => 'êµ¡',
  '±·' => 'êµ£',
  '±¸' => 'êµ¬',
  '±¹' => 'êµ­',
  '±º' => 'êµ°',
  '±»' => 'êµ³',
  '±¼' => 'êµ´',
  '±½' => 'êµµ',
  '±¾' => 'êµ¶',
  '±¿' => 'êµ»',
  '±À' => 'êµ¼',
  '±Á' => 'êµ½',
  '±Â' => 'êµ¿',
  '±Ã' => 'ê¶�',
  '±Ä' => 'ê¶‚',
  '±Å' => 'ê¶ˆ',
  '±Æ' => 'ê¶‰',
  '±Ç' => 'ê¶Œ',
  '±È' => 'ê¶�',
  '±É' => 'ê¶œ',
  '±Ê' => 'ê¶�',
  '±Ë' => 'ê¶¤',
  '±Ì' => 'ê¶·',
  '±Í' => 'ê·€',
  '±Î' => 'ê·�',
  '±Ï' => 'ê·„',
  '±Ð' => 'ê·ˆ',
  '±Ñ' => 'ê·�',
  '±Ò' => 'ê·‘',
  '±Ó' => 'ê·“',
  '±Ô' => 'ê·œ',
  '±Õ' => 'ê· ',
  '±Ö' => 'ê·¤',
  '±×' => 'ê·¸',
  '±Ø' => 'ê·¹',
  '±Ù' => 'ê·¼',
  '±Ú' => 'ê·¿',
  '±Û' => 'ê¸€',
  '±Ü' => 'ê¸�',
  '±Ý' => 'ê¸ˆ',
  '±Þ' => 'ê¸‰',
  '±ß' => 'ê¸‹',
  '±à' => 'ê¸�',
  '±á' => 'ê¸”',
  '±â' => 'ê¸°',
  '±ã' => 'ê¸±',
  '±ä' => 'ê¸´',
  '±å' => 'ê¸·',
  '±æ' => 'ê¸¸',
  '±ç' => 'ê¸º',
  '±è' => 'ê¹€',
  '±é' => 'ê¹�',
  '±ê' => 'ê¹ƒ',
  '±ë' => 'ê¹…',
  '±ì' => 'ê¹†',
  '±í' => 'ê¹Š',
  '±î' => 'ê¹Œ',
  '±ï' => 'ê¹�',
  '±ð' => 'ê¹Ž',
  '±ñ' => 'ê¹�',
  '±ò' => 'ê¹”',
  '±ó' => 'ê¹–',
  '±ô' => 'ê¹œ',
  '±õ' => 'ê¹�',
  '±ö' => 'ê¹Ÿ',
  '±÷' => 'ê¹ ',
  '±ø' => 'ê¹¡',
  '±ù' => 'ê¹¥',
  '±ú' => 'ê¹¨',
  '±û' => 'ê¹©',
  '±ü' => 'ê¹¬',
  '±ý' => 'ê¹°',
  '±þ' => 'ê¹¸',
  '²A' => 'ì½­',
  '²B' => 'ì½®',
  '²C' => 'ì½¯',
  '²D' => 'ì½²',
  '²E' => 'ì½³',
  '²F' => 'ì½µ',
  '²G' => 'ì½¶',
  '²H' => 'ì½·',
  '²I' => 'ì½¹',
  '²J' => 'ì½º',
  '²K' => 'ì½»',
  '²L' => 'ì½¼',
  '²M' => 'ì½½',
  '²N' => 'ì½¾',
  '²O' => 'ì½¿',
  '²P' => 'ì¾�',
  '²Q' => 'ì¾‚',
  '²R' => 'ì¾ƒ',
  '²S' => 'ì¾„',
  '²T' => 'ì¾†',
  '²U' => 'ì¾‡',
  '²V' => 'ì¾ˆ',
  '²W' => 'ì¾‰',
  '²X' => 'ì¾Š',
  '²Y' => 'ì¾‹',
  '²Z' => 'ì¾�',
  '²a' => 'ì¾Ž',
  '²b' => 'ì¾�',
  '²c' => 'ì¾�',
  '²d' => 'ì¾‘',
  '²e' => 'ì¾’',
  '²f' => 'ì¾“',
  '²g' => 'ì¾”',
  '²h' => 'ì¾•',
  '²i' => 'ì¾–',
  '²j' => 'ì¾—',
  '²k' => 'ì¾˜',
  '²l' => 'ì¾™',
  '²m' => 'ì¾š',
  '²n' => 'ì¾›',
  '²o' => 'ì¾œ',
  '²p' => 'ì¾�',
  '²q' => 'ì¾ž',
  '²r' => 'ì¾Ÿ',
  '²s' => 'ì¾ ',
  '²t' => 'ì¾¢',
  '²u' => 'ì¾£',
  '²v' => 'ì¾¤',
  '²w' => 'ì¾¥',
  '²x' => 'ì¾¦',
  '²y' => 'ì¾§',
  '²z' => 'ì¾©',
  '²�' => 'ì¾ª',
  '²‚' => 'ì¾«',
  '²ƒ' => 'ì¾¬',
  '²„' => 'ì¾­',
  '²…' => 'ì¾®',
  '²†' => 'ì¾¯',
  '²‡' => 'ì¾±',
  '²ˆ' => 'ì¾²',
  '²‰' => 'ì¾³',
  '²Š' => 'ì¾´',
  '²‹' => 'ì¾µ',
  '²Œ' => 'ì¾¶',
  '²�' => 'ì¾·',
  '²Ž' => 'ì¾¸',
  '²�' => 'ì¾¹',
  '²�' => 'ì¾º',
  '²‘' => 'ì¾»',
  '²’' => 'ì¾¼',
  '²“' => 'ì¾½',
  '²”' => 'ì¾¾',
  '²•' => 'ì¾¿',
  '²–' => 'ì¿€',
  '²—' => 'ì¿�',
  '²˜' => 'ì¿‚',
  '²™' => 'ì¿ƒ',
  '²š' => 'ì¿…',
  '²›' => 'ì¿†',
  '²œ' => 'ì¿‡',
  '²�' => 'ì¿ˆ',
  '²ž' => 'ì¿‰',
  '²Ÿ' => 'ì¿Š',
  '² ' => 'ì¿‹',
  '²¡' => 'ê¹¹',
  '²¢' => 'ê¹»',
  '²£' => 'ê¹¼',
  '²¤' => 'ê¹½',
  '²¥' => 'êº„',
  '²¦' => 'êº…',
  '²§' => 'êºŒ',
  '²¨' => 'êº¼',
  '²©' => 'êº½',
  '²ª' => 'êº¾',
  '²«' => 'ê»€',
  '²¬' => 'ê»„',
  '²­' => 'ê»Œ',
  '²®' => 'ê»�',
  '²¯' => 'ê»�',
  '²°' => 'ê»�',
  '²±' => 'ê»‘',
  '²²' => 'ê»˜',
  '²³' => 'ê»™',
  '²´' => 'ê»œ',
  '²µ' => 'ê»¨',
  '²¶' => 'ê»«',
  '²·' => 'ê»­',
  '²¸' => 'ê»´',
  '²¹' => 'ê»¸',
  '²º' => 'ê»¼',
  '²»' => 'ê¼‡',
  '²¼' => 'ê¼ˆ',
  '²½' => 'ê¼�',
  '²¾' => 'ê¼�',
  '²¿' => 'ê¼¬',
  '²À' => 'ê¼­',
  '²Á' => 'ê¼°',
  '²Â' => 'ê¼²',
  '²Ã' => 'ê¼´',
  '²Ä' => 'ê¼¼',
  '²Å' => 'ê¼½',
  '²Æ' => 'ê¼¿',
  '²Ç' => 'ê½�',
  '²È' => 'ê½‚',
  '²É' => 'ê½ƒ',
  '²Ê' => 'ê½ˆ',
  '²Ë' => 'ê½‰',
  '²Ì' => 'ê½�',
  '²Í' => 'ê½œ',
  '²Î' => 'ê½�',
  '²Ï' => 'ê½¤',
  '²Ð' => 'ê½¥',
  '²Ñ' => 'ê½¹',
  '²Ò' => 'ê¾€',
  '²Ó' => 'ê¾„',
  '²Ô' => 'ê¾ˆ',
  '²Õ' => 'ê¾�',
  '²Ö' => 'ê¾‘',
  '²×' => 'ê¾•',
  '²Ø' => 'ê¾œ',
  '²Ù' => 'ê¾¸',
  '²Ú' => 'ê¾¹',
  '²Û' => 'ê¾¼',
  '²Ü' => 'ê¿€',
  '²Ý' => 'ê¿‡',
  '²Þ' => 'ê¿ˆ',
  '²ß' => 'ê¿‰',
  '²à' => 'ê¿‹',
  '²á' => 'ê¿�',
  '²â' => 'ê¿Ž',
  '²ã' => 'ê¿”',
  '²ä' => 'ê¿œ',
  '²å' => 'ê¿¨',
  '²æ' => 'ê¿©',
  '²ç' => 'ê¿°',
  '²è' => 'ê¿±',
  '²é' => 'ê¿´',
  '²ê' => 'ê¿¸',
  '²ë' => 'ë€€',
  '²ì' => 'ë€�',
  '²í' => 'ë€„',
  '²î' => 'ë€Œ',
  '²ï' => 'ë€�',
  '²ð' => 'ë€”',
  '²ñ' => 'ë€œ',
  '²ò' => 'ë€�',
  '²ó' => 'ë€¨',
  '²ô' => 'ë�„',
  '²õ' => 'ë�…',
  '²ö' => 'ë�ˆ',
  '²÷' => 'ë�Š',
  '²ø' => 'ë�Œ',
  '²ù' => 'ë�Ž',
  '²ú' => 'ë�“',
  '²û' => 'ë�”',
  '²ü' => 'ë�•',
  '²ý' => 'ë�—',
  '²þ' => 'ë�™',
  '³A' => 'ì¿Œ',
  '³B' => 'ì¿�',
  '³C' => 'ì¿Ž',
  '³D' => 'ì¿�',
  '³E' => 'ì¿�',
  '³F' => 'ì¿‘',
  '³G' => 'ì¿’',
  '³H' => 'ì¿“',
  '³I' => 'ì¿”',
  '³J' => 'ì¿•',
  '³K' => 'ì¿–',
  '³L' => 'ì¿—',
  '³M' => 'ì¿˜',
  '³N' => 'ì¿™',
  '³O' => 'ì¿š',
  '³P' => 'ì¿›',
  '³Q' => 'ì¿œ',
  '³R' => 'ì¿�',
  '³S' => 'ì¿ž',
  '³T' => 'ì¿Ÿ',
  '³U' => 'ì¿¢',
  '³V' => 'ì¿£',
  '³W' => 'ì¿¥',
  '³X' => 'ì¿¦',
  '³Y' => 'ì¿§',
  '³Z' => 'ì¿©',
  '³a' => 'ì¿ª',
  '³b' => 'ì¿«',
  '³c' => 'ì¿¬',
  '³d' => 'ì¿­',
  '³e' => 'ì¿®',
  '³f' => 'ì¿¯',
  '³g' => 'ì¿²',
  '³h' => 'ì¿´',
  '³i' => 'ì¿¶',
  '³j' => 'ì¿·',
  '³k' => 'ì¿¸',
  '³l' => 'ì¿¹',
  '³m' => 'ì¿º',
  '³n' => 'ì¿»',
  '³o' => 'ì¿½',
  '³p' => 'ì¿¾',
  '³q' => 'ì¿¿',
  '³r' => 'í€�',
  '³s' => 'í€‚',
  '³t' => 'í€ƒ',
  '³u' => 'í€…',
  '³v' => 'í€†',
  '³w' => 'í€‡',
  '³x' => 'í€ˆ',
  '³y' => 'í€‰',
  '³z' => 'í€Š',
  '³�' => 'í€‹',
  '³‚' => 'í€Œ',
  '³ƒ' => 'í€�',
  '³„' => 'í€Ž',
  '³…' => 'í€�',
  '³†' => 'í€�',
  '³‡' => 'í€’',
  '³ˆ' => 'í€“',
  '³‰' => 'í€”',
  '³Š' => 'í€•',
  '³‹' => 'í€–',
  '³Œ' => 'í€—',
  '³�' => 'í€™',
  '³Ž' => 'í€š',
  '³�' => 'í€›',
  '³�' => 'í€œ',
  '³‘' => 'í€�',
  '³’' => 'í€ž',
  '³“' => 'í€Ÿ',
  '³”' => 'í€ ',
  '³•' => 'í€¡',
  '³–' => 'í€¢',
  '³—' => 'í€£',
  '³˜' => 'í€¤',
  '³™' => 'í€¥',
  '³š' => 'í€¦',
  '³›' => 'í€§',
  '³œ' => 'í€¨',
  '³�' => 'í€©',
  '³ž' => 'í€ª',
  '³Ÿ' => 'í€«',
  '³ ' => 'í€¬',
  '³¡' => 'ë��',
  '³¢' => 'ë�¼',
  '³£' => 'ë�½',
  '³¤' => 'ë‚€',
  '³¥' => 'ë‚„',
  '³¦' => 'ë‚Œ',
  '³§' => 'ë‚�',
  '³¨' => 'ë‚�',
  '³©' => 'ë‚‘',
  '³ª' => 'ë‚˜',
  '³«' => 'ë‚™',
  '³¬' => 'ë‚š',
  '³­' => 'ë‚œ',
  '³®' => 'ë‚Ÿ',
  '³¯' => 'ë‚ ',
  '³°' => 'ë‚¡',
  '³±' => 'ë‚¢',
  '³²' => 'ë‚¨',
  '³³' => 'ë‚©',
  '³´' => 'ë‚«',
  '³µ' => 'ë‚¬',
  '³¶' => 'ë‚­',
  '³·' => 'ë‚®',
  '³¸' => 'ë‚¯',
  '³¹' => 'ë‚±',
  '³º' => 'ë‚³',
  '³»' => 'ë‚´',
  '³¼' => 'ë‚µ',
  '³½' => 'ë‚¸',
  '³¾' => 'ë‚¼',
  '³¿' => 'ëƒ„',
  '³À' => 'ëƒ…',
  '³Á' => 'ëƒ‡',
  '³Â' => 'ëƒˆ',
  '³Ã' => 'ëƒ‰',
  '³Ä' => 'ëƒ�',
  '³Å' => 'ëƒ‘',
  '³Æ' => 'ëƒ”',
  '³Ç' => 'ëƒ˜',
  '³È' => 'ëƒ ',
  '³É' => 'ëƒ¥',
  '³Ê' => 'ë„ˆ',
  '³Ë' => 'ë„‰',
  '³Ì' => 'ë„‹',
  '³Í' => 'ë„Œ',
  '³Î' => 'ë„�',
  '³Ï' => 'ë„’',
  '³Ð' => 'ë„“',
  '³Ñ' => 'ë„˜',
  '³Ò' => 'ë„™',
  '³Ó' => 'ë„›',
  '³Ô' => 'ë„œ',
  '³Õ' => 'ë„�',
  '³Ö' => 'ë„£',
  '³×' => 'ë„¤',
  '³Ø' => 'ë„¥',
  '³Ù' => 'ë„¨',
  '³Ú' => 'ë„¬',
  '³Û' => 'ë„´',
  '³Ü' => 'ë„µ',
  '³Ý' => 'ë„·',
  '³Þ' => 'ë„¸',
  '³ß' => 'ë„¹',
  '³à' => 'ë…€',
  '³á' => 'ë…�',
  '³â' => 'ë…„',
  '³ã' => 'ë…ˆ',
  '³ä' => 'ë…�',
  '³å' => 'ë…‘',
  '³æ' => 'ë…”',
  '³ç' => 'ë…•',
  '³è' => 'ë…˜',
  '³é' => 'ë…œ',
  '³ê' => 'ë… ',
  '³ë' => 'ë…¸',
  '³ì' => 'ë…¹',
  '³í' => 'ë…¼',
  '³î' => 'ë†€',
  '³ï' => 'ë†‚',
  '³ð' => 'ë†ˆ',
  '³ñ' => 'ë†‰',
  '³ò' => 'ë†‹',
  '³ó' => 'ë†�',
  '³ô' => 'ë†’',
  '³õ' => 'ë†“',
  '³ö' => 'ë†”',
  '³÷' => 'ë†˜',
  '³ø' => 'ë†œ',
  '³ù' => 'ë†¨',
  '³ú' => 'ë‡Œ',
  '³û' => 'ë‡�',
  '³ü' => 'ë‡”',
  '³ý' => 'ë‡œ',
  '³þ' => 'ë‡�',
  '´A' => 'í€®',
  '´B' => 'í€¯',
  '´C' => 'í€°',
  '´D' => 'í€±',
  '´E' => 'í€²',
  '´F' => 'í€³',
  '´G' => 'í€¶',
  '´H' => 'í€·',
  '´I' => 'í€¹',
  '´J' => 'í€º',
  '´K' => 'í€»',
  '´L' => 'í€½',
  '´M' => 'í€¾',
  '´N' => 'í€¿',
  '´O' => 'í�€',
  '´P' => 'í��',
  '´Q' => 'í�‚',
  '´R' => 'í�ƒ',
  '´S' => 'í�†',
  '´T' => 'í�ˆ',
  '´U' => 'í�Š',
  '´V' => 'í�‹',
  '´W' => 'í�Œ',
  '´X' => 'í��',
  '´Y' => 'í�Ž',
  '´Z' => 'í��',
  '´a' => 'í�‘',
  '´b' => 'í�’',
  '´c' => 'í�“',
  '´d' => 'í�•',
  '´e' => 'í�–',
  '´f' => 'í�—',
  '´g' => 'í�™',
  '´h' => 'í�š',
  '´i' => 'í�›',
  '´j' => 'í�œ',
  '´k' => 'í��',
  '´l' => 'í�ž',
  '´m' => 'í�Ÿ',
  '´n' => 'í�¡',
  '´o' => 'í�¢',
  '´p' => 'í�£',
  '´q' => 'í�¤',
  '´r' => 'í�¥',
  '´s' => 'í�¦',
  '´t' => 'í�§',
  '´u' => 'í�¨',
  '´v' => 'í�©',
  '´w' => 'í�ª',
  '´x' => 'í�«',
  '´y' => 'í�®',
  '´z' => 'í�¯',
  '´�' => 'í�±',
  '´‚' => 'í�²',
  '´ƒ' => 'í�³',
  '´„' => 'í�µ',
  '´…' => 'í�¶',
  '´†' => 'í�·',
  '´‡' => 'í�¸',
  '´ˆ' => 'í�¹',
  '´‰' => 'í�º',
  '´Š' => 'í�»',
  '´‹' => 'í�¾',
  '´Œ' => 'í�¿',
  '´�' => 'í‚€',
  '´Ž' => 'í‚‚',
  '´�' => 'í‚ƒ',
  '´�' => 'í‚„',
  '´‘' => 'í‚…',
  '´’' => 'í‚†',
  '´“' => 'í‚‡',
  '´”' => 'í‚ˆ',
  '´•' => 'í‚‰',
  '´–' => 'í‚Š',
  '´—' => 'í‚‹',
  '´˜' => 'í‚Œ',
  '´™' => 'í‚�',
  '´š' => 'í‚Ž',
  '´›' => 'í‚�',
  '´œ' => 'í‚�',
  '´�' => 'í‚‘',
  '´ž' => 'í‚’',
  '´Ÿ' => 'í‚“',
  '´ ' => 'í‚”',
  '´¡' => 'ë‡Ÿ',
  '´¢' => 'ë‡¨',
  '´£' => 'ë‡©',
  '´¤' => 'ë‡¬',
  '´¥' => 'ë‡°',
  '´¦' => 'ë‡¹',
  '´§' => 'ë‡»',
  '´¨' => 'ë‡½',
  '´©' => 'ëˆ„',
  '´ª' => 'ëˆ…',
  '´«' => 'ëˆˆ',
  '´¬' => 'ëˆ‹',
  '´­' => 'ëˆŒ',
  '´®' => 'ëˆ”',
  '´¯' => 'ëˆ•',
  '´°' => 'ëˆ—',
  '´±' => 'ëˆ™',
  '´²' => 'ëˆ ',
  '´³' => 'ëˆ´',
  '´´' => 'ëˆ¼',
  '´µ' => 'ë‰˜',
  '´¶' => 'ë‰œ',
  '´·' => 'ë‰ ',
  '´¸' => 'ë‰¨',
  '´¹' => 'ë‰©',
  '´º' => 'ë‰´',
  '´»' => 'ë‰µ',
  '´¼' => 'ë‰¼',
  '´½' => 'ëŠ„',
  '´¾' => 'ëŠ…',
  '´¿' => 'ëŠ‰',
  '´À' => 'ëŠ�',
  '´Á' => 'ëŠ‘',
  '´Â' => 'ëŠ”',
  '´Ã' => 'ëŠ˜',
  '´Ä' => 'ëŠ™',
  '´Å' => 'ëŠš',
  '´Æ' => 'ëŠ ',
  '´Ç' => 'ëŠ¡',
  '´È' => 'ëŠ£',
  '´É' => 'ëŠ¥',
  '´Ê' => 'ëŠ¦',
  '´Ë' => 'ëŠª',
  '´Ì' => 'ëŠ¬',
  '´Í' => 'ëŠ°',
  '´Î' => 'ëŠ´',
  '´Ï' => 'ë‹ˆ',
  '´Ð' => 'ë‹‰',
  '´Ñ' => 'ë‹Œ',
  '´Ò' => 'ë‹�',
  '´Ó' => 'ë‹’',
  '´Ô' => 'ë‹˜',
  '´Õ' => 'ë‹™',
  '´Ö' => 'ë‹›',
  '´×' => 'ë‹�',
  '´Ø' => 'ë‹¢',
  '´Ù' => 'ë‹¤',
  '´Ú' => 'ë‹¥',
  '´Û' => 'ë‹¦',
  '´Ü' => 'ë‹¨',
  '´Ý' => 'ë‹«',
  '´Þ' => 'ë‹¬',
  '´ß' => 'ë‹­',
  '´à' => 'ë‹®',
  '´á' => 'ë‹¯',
  '´â' => 'ë‹³',
  '´ã' => 'ë‹´',
  '´ä' => 'ë‹µ',
  '´å' => 'ë‹·',
  '´æ' => 'ë‹¸',
  '´ç' => 'ë‹¹',
  '´è' => 'ë‹º',
  '´é' => 'ë‹»',
  '´ê' => 'ë‹¿',
  '´ë' => 'ëŒ€',
  '´ì' => 'ëŒ�',
  '´í' => 'ëŒ„',
  '´î' => 'ëŒˆ',
  '´ï' => 'ëŒ�',
  '´ð' => 'ëŒ‘',
  '´ñ' => 'ëŒ“',
  '´ò' => 'ëŒ”',
  '´ó' => 'ëŒ•',
  '´ô' => 'ëŒœ',
  '´õ' => 'ë�”',
  '´ö' => 'ë�•',
  '´÷' => 'ë�–',
  '´ø' => 'ë�˜',
  '´ù' => 'ë�›',
  '´ú' => 'ë�œ',
  '´û' => 'ë�ž',
  '´ü' => 'ë�Ÿ',
  '´ý' => 'ë�¤',
  '´þ' => 'ë�¥',
  'µA' => 'í‚•',
  'µB' => 'í‚–',
  'µC' => 'í‚—',
  'µD' => 'í‚˜',
  'µE' => 'í‚™',
  'µF' => 'í‚š',
  'µG' => 'í‚›',
  'µH' => 'í‚œ',
  'µI' => 'í‚�',
  'µJ' => 'í‚ž',
  'µK' => 'í‚Ÿ',
  'µL' => 'í‚ ',
  'µM' => 'í‚¡',
  'µN' => 'í‚¢',
  'µO' => 'í‚£',
  'µP' => 'í‚¦',
  'µQ' => 'í‚§',
  'µR' => 'í‚©',
  'µS' => 'í‚ª',
  'µT' => 'í‚«',
  'µU' => 'í‚­',
  'µV' => 'í‚®',
  'µW' => 'í‚¯',
  'µX' => 'í‚°',
  'µY' => 'í‚±',
  'µZ' => 'í‚²',
  'µa' => 'í‚³',
  'µb' => 'í‚¶',
  'µc' => 'í‚¸',
  'µd' => 'í‚º',
  'µe' => 'í‚»',
  'µf' => 'í‚¼',
  'µg' => 'í‚½',
  'µh' => 'í‚¾',
  'µi' => 'í‚¿',
  'µj' => 'íƒ‚',
  'µk' => 'íƒƒ',
  'µl' => 'íƒ…',
  'µm' => 'íƒ†',
  'µn' => 'íƒ‡',
  'µo' => 'íƒŠ',
  'µp' => 'íƒ‹',
  'µq' => 'íƒŒ',
  'µr' => 'íƒ�',
  'µs' => 'íƒŽ',
  'µt' => 'íƒ�',
  'µu' => 'íƒ’',
  'µv' => 'íƒ–',
  'µw' => 'íƒ—',
  'µx' => 'íƒ˜',
  'µy' => 'íƒ™',
  'µz' => 'íƒš',
  'µ�' => 'íƒ›',
  'µ‚' => 'íƒž',
  'µƒ' => 'íƒŸ',
  'µ„' => 'íƒ¡',
  'µ…' => 'íƒ¢',
  'µ†' => 'íƒ£',
  'µ‡' => 'íƒ¥',
  'µˆ' => 'íƒ¦',
  'µ‰' => 'íƒ§',
  'µŠ' => 'íƒ¨',
  'µ‹' => 'íƒ©',
  'µŒ' => 'íƒª',
  'µ�' => 'íƒ«',
  'µŽ' => 'íƒ®',
  'µ�' => 'íƒ²',
  'µ�' => 'íƒ³',
  'µ‘' => 'íƒ´',
  'µ’' => 'íƒµ',
  'µ“' => 'íƒ¶',
  'µ”' => 'íƒ·',
  'µ•' => 'íƒ¹',
  'µ–' => 'íƒº',
  'µ—' => 'íƒ»',
  'µ˜' => 'íƒ¼',
  'µ™' => 'íƒ½',
  'µš' => 'íƒ¾',
  'µ›' => 'íƒ¿',
  'µœ' => 'í„€',
  'µ�' => 'í„�',
  'µž' => 'í„‚',
  'µŸ' => 'í„ƒ',
  'µ ' => 'í„„',
  'µ¡' => 'ë�§',
  'µ¢' => 'ë�©',
  'µ£' => 'ë�«',
  'µ¤' => 'ë�®',
  'µ¥' => 'ë�°',
  'µ¦' => 'ë�±',
  'µ§' => 'ë�´',
  'µ¨' => 'ë�¸',
  'µ©' => 'ëŽ€',
  'µª' => 'ëŽ�',
  'µ«' => 'ëŽƒ',
  'µ¬' => 'ëŽ„',
  'µ­' => 'ëŽ…',
  'µ®' => 'ëŽŒ',
  'µ¯' => 'ëŽ�',
  'µ°' => 'ëŽ”',
  'µ±' => 'ëŽ ',
  'µ²' => 'ëŽ¡',
  'µ³' => 'ëŽ¨',
  'µ´' => 'ëŽ¬',
  'µµ' => 'ë�„',
  'µ¶' => 'ë�…',
  'µ·' => 'ë�ˆ',
  'µ¸' => 'ë�‹',
  'µ¹' => 'ë�Œ',
  'µº' => 'ë�Ž',
  'µ»' => 'ë��',
  'µ¼' => 'ë�”',
  'µ½' => 'ë�•',
  'µ¾' => 'ë�—',
  'µ¿' => 'ë�™',
  'µÀ' => 'ë�›',
  'µÁ' => 'ë��',
  'µÂ' => 'ë� ',
  'µÃ' => 'ë�¤',
  'µÄ' => 'ë�¨',
  'µÅ' => 'ë�¼',
  'µÆ' => 'ë��',
  'µÇ' => 'ë�˜',
  'µÈ' => 'ë�œ',
  'µÉ' => 'ë� ',
  'µÊ' => 'ë�¨',
  'µË' => 'ë�©',
  'µÌ' => 'ë�«',
  'µÍ' => 'ë�´',
  'µÎ' => 'ë‘�',
  'µÏ' => 'ë‘‘',
  'µÐ' => 'ë‘”',
  'µÑ' => 'ë‘˜',
  'µÒ' => 'ë‘ ',
  'µÓ' => 'ë‘¡',
  'µÔ' => 'ë‘£',
  'µÕ' => 'ë‘¥',
  'µÖ' => 'ë‘¬',
  'µ×' => 'ë’€',
  'µØ' => 'ë’ˆ',
  'µÙ' => 'ë’�',
  'µÚ' => 'ë’¤',
  'µÛ' => 'ë’¨',
  'µÜ' => 'ë’¬',
  'µÝ' => 'ë’µ',
  'µÞ' => 'ë’·',
  'µß' => 'ë’¹',
  'µà' => 'ë“€',
  'µá' => 'ë“„',
  'µâ' => 'ë“ˆ',
  'µã' => 'ë“�',
  'µä' => 'ë“•',
  'µå' => 'ë“œ',
  'µæ' => 'ë“�',
  'µç' => 'ë“ ',
  'µè' => 'ë“£',
  'µé' => 'ë“¤',
  'µê' => 'ë“¦',
  'µë' => 'ë“¬',
  'µì' => 'ë“­',
  'µí' => 'ë“¯',
  'µî' => 'ë“±',
  'µï' => 'ë“¸',
  'µð' => 'ë””',
  'µñ' => 'ë”•',
  'µò' => 'ë”˜',
  'µó' => 'ë”›',
  'µô' => 'ë”œ',
  'µõ' => 'ë”¤',
  'µö' => 'ë”¥',
  'µ÷' => 'ë”§',
  'µø' => 'ë”¨',
  'µù' => 'ë”©',
  'µú' => 'ë”ª',
  'µû' => 'ë”°',
  'µü' => 'ë”±',
  'µý' => 'ë”´',
  'µþ' => 'ë”¸',
  '¶A' => 'í„…',
  '¶B' => 'í„†',
  '¶C' => 'í„‡',
  '¶D' => 'í„ˆ',
  '¶E' => 'í„‰',
  '¶F' => 'í„Š',
  '¶G' => 'í„‹',
  '¶H' => 'í„Œ',
  '¶I' => 'í„Ž',
  '¶J' => 'í„�',
  '¶K' => 'í„�',
  '¶L' => 'í„‘',
  '¶M' => 'í„’',
  '¶N' => 'í„“',
  '¶O' => 'í„”',
  '¶P' => 'í„•',
  '¶Q' => 'í„–',
  '¶R' => 'í„—',
  '¶S' => 'í„˜',
  '¶T' => 'í„™',
  '¶U' => 'í„š',
  '¶V' => 'í„›',
  '¶W' => 'í„œ',
  '¶X' => 'í„�',
  '¶Y' => 'í„ž',
  '¶Z' => 'í„Ÿ',
  '¶a' => 'í„ ',
  '¶b' => 'í„¡',
  '¶c' => 'í„¢',
  '¶d' => 'í„£',
  '¶e' => 'í„¤',
  '¶f' => 'í„¥',
  '¶g' => 'í„¦',
  '¶h' => 'í„§',
  '¶i' => 'í„¨',
  '¶j' => 'í„©',
  '¶k' => 'í„ª',
  '¶l' => 'í„«',
  '¶m' => 'í„¬',
  '¶n' => 'í„­',
  '¶o' => 'í„®',
  '¶p' => 'í„¯',
  '¶q' => 'í„²',
  '¶r' => 'í„³',
  '¶s' => 'í„µ',
  '¶t' => 'í„¶',
  '¶u' => 'í„·',
  '¶v' => 'í„¹',
  '¶w' => 'í„»',
  '¶x' => 'í„¼',
  '¶y' => 'í„½',
  '¶z' => 'í„¾',
  '¶�' => 'í„¿',
  '¶‚' => 'í…‚',
  '¶ƒ' => 'í…†',
  '¶„' => 'í…‡',
  '¶…' => 'í…ˆ',
  '¶†' => 'í…‰',
  '¶‡' => 'í…Š',
  '¶ˆ' => 'í…‹',
  '¶‰' => 'í…Ž',
  '¶Š' => 'í…�',
  '¶‹' => 'í…‘',
  '¶Œ' => 'í…’',
  '¶�' => 'í…“',
  '¶Ž' => 'í…•',
  '¶�' => 'í…–',
  '¶�' => 'í…—',
  '¶‘' => 'í…˜',
  '¶’' => 'í…™',
  '¶“' => 'í…š',
  '¶”' => 'í…›',
  '¶•' => 'í…ž',
  '¶–' => 'í… ',
  '¶—' => 'í…¢',
  '¶˜' => 'í…£',
  '¶™' => 'í…¤',
  '¶š' => 'í…¥',
  '¶›' => 'í…¦',
  '¶œ' => 'í…§',
  '¶�' => 'í…©',
  '¶ž' => 'í…ª',
  '¶Ÿ' => 'í…«',
  '¶ ' => 'í…­',
  '¶¡' => 'ë•€',
  '¶¢' => 'ë•�',
  '¶£' => 'ë•ƒ',
  '¶¤' => 'ë•„',
  '¶¥' => 'ë•…',
  '¶¦' => 'ë•‹',
  '¶§' => 'ë•Œ',
  '¶¨' => 'ë•�',
  '¶©' => 'ë•�',
  '¶ª' => 'ë•”',
  '¶«' => 'ë•œ',
  '¶¬' => 'ë•�',
  '¶­' => 'ë•Ÿ',
  '¶®' => 'ë• ',
  '¶¯' => 'ë•¡',
  '¶°' => 'ë– ',
  '¶±' => 'ë–¡',
  '¶²' => 'ë–¤',
  '¶³' => 'ë–¨',
  '¶´' => 'ë–ª',
  '¶µ' => 'ë–«',
  '¶¶' => 'ë–°',
  '¶·' => 'ë–±',
  '¶¸' => 'ë–³',
  '¶¹' => 'ë–´',
  '¶º' => 'ë–µ',
  '¶»' => 'ë–»',
  '¶¼' => 'ë–¼',
  '¶½' => 'ë–½',
  '¶¾' => 'ë—€',
  '¶¿' => 'ë—„',
  '¶À' => 'ë—Œ',
  '¶Á' => 'ë—�',
  '¶Â' => 'ë—�',
  '¶Ã' => 'ë—�',
  '¶Ä' => 'ë—‘',
  '¶Å' => 'ë—˜',
  '¶Æ' => 'ë—¬',
  '¶Ç' => 'ë˜�',
  '¶È' => 'ë˜‘',
  '¶É' => 'ë˜”',
  '¶Ê' => 'ë˜˜',
  '¶Ë' => 'ë˜¥',
  '¶Ì' => 'ë˜¬',
  '¶Í' => 'ë˜´',
  '¶Î' => 'ë™ˆ',
  '¶Ï' => 'ë™¤',
  '¶Ð' => 'ë™¨',
  '¶Ñ' => 'ëšœ',
  '¶Ò' => 'ëš�',
  '¶Ó' => 'ëš ',
  '¶Ô' => 'ëš¤',
  '¶Õ' => 'ëš«',
  '¶Ö' => 'ëš¬',
  '¶×' => 'ëš±',
  '¶Ø' => 'ë›”',
  '¶Ù' => 'ë›°',
  '¶Ú' => 'ë›´',
  '¶Û' => 'ë›¸',
  '¶Ü' => 'ëœ€',
  '¶Ý' => 'ëœ�',
  '¶Þ' => 'ëœ…',
  '¶ß' => 'ëœ¨',
  '¶à' => 'ëœ©',
  '¶á' => 'ëœ¬',
  '¶â' => 'ëœ¯',
  '¶ã' => 'ëœ°',
  '¶ä' => 'ëœ¸',
  '¶å' => 'ëœ¹',
  '¶æ' => 'ëœ»',
  '¶ç' => 'ë�„',
  '¶è' => 'ë�ˆ',
  '¶é' => 'ë�Œ',
  '¶ê' => 'ë�”',
  '¶ë' => 'ë�•',
  '¶ì' => 'ë� ',
  '¶í' => 'ë�¤',
  '¶î' => 'ë�¨',
  '¶ï' => 'ë�°',
  '¶ð' => 'ë�±',
  '¶ñ' => 'ë�³',
  '¶ò' => 'ë�µ',
  '¶ó' => 'ë�¼',
  '¶ô' => 'ë�½',
  '¶õ' => 'ëž€',
  '¶ö' => 'ëž„',
  '¶÷' => 'ëžŒ',
  '¶ø' => 'ëž�',
  '¶ù' => 'ëž�',
  '¶ú' => 'ëž�',
  '¶û' => 'ëž‘',
  '¶ü' => 'ëž’',
  '¶ý' => 'ëž–',
  '¶þ' => 'ëž—',
  '·A' => 'í…®',
  '·B' => 'í…¯',
  '·C' => 'í…°',
  '·D' => 'í…±',
  '·E' => 'í…²',
  '·F' => 'í…³',
  '·G' => 'í…´',
  '·H' => 'í…µ',
  '·I' => 'í…¶',
  '·J' => 'í…·',
  '·K' => 'í…¸',
  '·L' => 'í…¹',
  '·M' => 'í…º',
  '·N' => 'í…»',
  '·O' => 'í…½',
  '·P' => 'í…¾',
  '·Q' => 'í…¿',
  '·R' => 'í†€',
  '·S' => 'í†�',
  '·T' => 'í†‚',
  '·U' => 'í†ƒ',
  '·V' => 'í†…',
  '·W' => 'í††',
  '·X' => 'í†‡',
  '·Y' => 'í†‰',
  '·Z' => 'í†Š',
  '·a' => 'í†‹',
  '·b' => 'í†Œ',
  '·c' => 'í†�',
  '·d' => 'í†Ž',
  '·e' => 'í†�',
  '·f' => 'í†�',
  '·g' => 'í†‘',
  '·h' => 'í†’',
  '·i' => 'í†“',
  '·j' => 'í†”',
  '·k' => 'í†•',
  '·l' => 'í†–',
  '·m' => 'í†—',
  '·n' => 'í†˜',
  '·o' => 'í†™',
  '·p' => 'í†š',
  '·q' => 'í†›',
  '·r' => 'í†œ',
  '·s' => 'í†�',
  '·t' => 'í†ž',
  '·u' => 'í†Ÿ',
  '·v' => 'í†¢',
  '·w' => 'í†£',
  '·x' => 'í†¥',
  '·y' => 'í†¦',
  '·z' => 'í†§',
  '·�' => 'í†©',
  '·‚' => 'í†ª',
  '·ƒ' => 'í†«',
  '·„' => 'í†¬',
  '·…' => 'í†­',
  '·†' => 'í†®',
  '·‡' => 'í†¯',
  '·ˆ' => 'í†²',
  '·‰' => 'í†´',
  '·Š' => 'í†¶',
  '·‹' => 'í†·',
  '·Œ' => 'í†¸',
  '·�' => 'í†¹',
  '·Ž' => 'í†»',
  '·�' => 'í†½',
  '·�' => 'í†¾',
  '·‘' => 'í†¿',
  '·’' => 'í‡�',
  '·“' => 'í‡‚',
  '·”' => 'í‡ƒ',
  '·•' => 'í‡„',
  '·–' => 'í‡…',
  '·—' => 'í‡†',
  '·˜' => 'í‡‡',
  '·™' => 'í‡ˆ',
  '·š' => 'í‡‰',
  '·›' => 'í‡Š',
  '·œ' => 'í‡‹',
  '·�' => 'í‡Œ',
  '·ž' => 'í‡�',
  '·Ÿ' => 'í‡Ž',
  '· ' => 'í‡�',
  '·¡' => 'ëž˜',
  '·¢' => 'ëž™',
  '·£' => 'ëžœ',
  '·¤' => 'ëž ',
  '·¥' => 'ëž¨',
  '·¦' => 'ëž©',
  '·§' => 'ëž«',
  '·¨' => 'ëž¬',
  '·©' => 'ëž­',
  '·ª' => 'ëž´',
  '·«' => 'ëžµ',
  '·¬' => 'ëž¸',
  '·­' => 'ëŸ‡',
  '·®' => 'ëŸ‰',
  '·¯' => 'ëŸ¬',
  '·°' => 'ëŸ­',
  '·±' => 'ëŸ°',
  '·²' => 'ëŸ´',
  '·³' => 'ëŸ¼',
  '·´' => 'ëŸ½',
  '·µ' => 'ëŸ¿',
  '·¶' => 'ë €',
  '··' => 'ë �',
  '·¸' => 'ë ‡',
  '·¹' => 'ë ˆ',
  '·º' => 'ë ‰',
  '·»' => 'ë Œ',
  '·¼' => 'ë �',
  '·½' => 'ë ˜',
  '·¾' => 'ë ™',
  '·¿' => 'ë ›',
  '·À' => 'ë �',
  '·Á' => 'ë ¤',
  '·Â' => 'ë ¥',
  '·Ã' => 'ë ¨',
  '·Ä' => 'ë ¬',
  '·Å' => 'ë ´',
  '·Æ' => 'ë µ',
  '·Ç' => 'ë ·',
  '·È' => 'ë ¸',
  '·É' => 'ë ¹',
  '·Ê' => 'ë¡€',
  '·Ë' => 'ë¡„',
  '·Ì' => 'ë¡‘',
  '·Í' => 'ë¡“',
  '·Î' => 'ë¡œ',
  '·Ï' => 'ë¡�',
  '·Ð' => 'ë¡ ',
  '·Ñ' => 'ë¡¤',
  '·Ò' => 'ë¡¬',
  '·Ó' => 'ë¡­',
  '·Ô' => 'ë¡¯',
  '·Õ' => 'ë¡±',
  '·Ö' => 'ë¡¸',
  '·×' => 'ë¡¼',
  '·Ø' => 'ë¢�',
  '·Ù' => 'ë¢¨',
  '·Ú' => 'ë¢°',
  '·Û' => 'ë¢´',
  '·Ü' => 'ë¢¸',
  '·Ý' => 'ë£€',
  '·Þ' => 'ë£�',
  '·ß' => 'ë£ƒ',
  '·à' => 'ë£…',
  '·á' => 'ë£Œ',
  '·â' => 'ë£�',
  '·ã' => 'ë£”',
  '·ä' => 'ë£�',
  '·å' => 'ë£Ÿ',
  '·æ' => 'ë£¡',
  '·ç' => 'ë£¨',
  '·è' => 'ë£©',
  '·é' => 'ë£¬',
  '·ê' => 'ë£°',
  '·ë' => 'ë£¸',
  '·ì' => 'ë£¹',
  '·í' => 'ë£»',
  '·î' => 'ë£½',
  '·ï' => 'ë¤„',
  '·ð' => 'ë¤˜',
  '·ñ' => 'ë¤ ',
  '·ò' => 'ë¤¼',
  '·ó' => 'ë¤½',
  '·ô' => 'ë¥€',
  '·õ' => 'ë¥„',
  '·ö' => 'ë¥Œ',
  '·÷' => 'ë¥�',
  '·ø' => 'ë¥‘',
  '·ù' => 'ë¥˜',
  '·ú' => 'ë¥™',
  '·û' => 'ë¥œ',
  '·ü' => 'ë¥ ',
  '·ý' => 'ë¥¨',
  '·þ' => 'ë¥©',
  '¸A' => 'í‡�',
  '¸B' => 'í‡‘',
  '¸C' => 'í‡’',
  '¸D' => 'í‡“',
  '¸E' => 'í‡”',
  '¸F' => 'í‡•',
  '¸G' => 'í‡–',
  '¸H' => 'í‡—',
  '¸I' => 'í‡™',
  '¸J' => 'í‡š',
  '¸K' => 'í‡›',
  '¸L' => 'í‡œ',
  '¸M' => 'í‡�',
  '¸N' => 'í‡ž',
  '¸O' => 'í‡Ÿ',
  '¸P' => 'í‡ ',
  '¸Q' => 'í‡¡',
  '¸R' => 'í‡¢',
  '¸S' => 'í‡£',
  '¸T' => 'í‡¤',
  '¸U' => 'í‡¥',
  '¸V' => 'í‡¦',
  '¸W' => 'í‡§',
  '¸X' => 'í‡¨',
  '¸Y' => 'í‡©',
  '¸Z' => 'í‡ª',
  '¸a' => 'í‡«',
  '¸b' => 'í‡¬',
  '¸c' => 'í‡­',
  '¸d' => 'í‡®',
  '¸e' => 'í‡¯',
  '¸f' => 'í‡°',
  '¸g' => 'í‡±',
  '¸h' => 'í‡²',
  '¸i' => 'í‡³',
  '¸j' => 'í‡µ',
  '¸k' => 'í‡¶',
  '¸l' => 'í‡·',
  '¸m' => 'í‡¹',
  '¸n' => 'í‡º',
  '¸o' => 'í‡»',
  '¸p' => 'í‡¼',
  '¸q' => 'í‡½',
  '¸r' => 'í‡¾',
  '¸s' => 'í‡¿',
  '¸t' => 'íˆ€',
  '¸u' => 'íˆ�',
  '¸v' => 'íˆ‚',
  '¸w' => 'íˆƒ',
  '¸x' => 'íˆ„',
  '¸y' => 'íˆ…',
  '¸z' => 'íˆ†',
  '¸�' => 'íˆˆ',
  '¸‚' => 'íˆŠ',
  '¸ƒ' => 'íˆ‹',
  '¸„' => 'íˆŒ',
  '¸…' => 'íˆ�',
  '¸†' => 'íˆŽ',
  '¸‡' => 'íˆ�',
  '¸ˆ' => 'íˆ‘',
  '¸‰' => 'íˆ’',
  '¸Š' => 'íˆ“',
  '¸‹' => 'íˆ”',
  '¸Œ' => 'íˆ•',
  '¸�' => 'íˆ–',
  '¸Ž' => 'íˆ—',
  '¸�' => 'íˆ˜',
  '¸�' => 'íˆ™',
  '¸‘' => 'íˆš',
  '¸’' => 'íˆ›',
  '¸“' => 'íˆœ',
  '¸”' => 'íˆ�',
  '¸•' => 'íˆž',
  '¸–' => 'íˆŸ',
  '¸—' => 'íˆ ',
  '¸˜' => 'íˆ¡',
  '¸™' => 'íˆ¢',
  '¸š' => 'íˆ£',
  '¸›' => 'íˆ¤',
  '¸œ' => 'íˆ¥',
  '¸�' => 'íˆ¦',
  '¸ž' => 'íˆ§',
  '¸Ÿ' => 'íˆ¨',
  '¸ ' => 'íˆ©',
  '¸¡' => 'ë¥«',
  '¸¢' => 'ë¥­',
  '¸£' => 'ë¥´',
  '¸¤' => 'ë¥µ',
  '¸¥' => 'ë¥¸',
  '¸¦' => 'ë¥¼',
  '¸§' => 'ë¦„',
  '¸¨' => 'ë¦…',
  '¸©' => 'ë¦‡',
  '¸ª' => 'ë¦‰',
  '¸«' => 'ë¦Š',
  '¸¬' => 'ë¦�',
  '¸­' => 'ë¦Ž',
  '¸®' => 'ë¦¬',
  '¸¯' => 'ë¦­',
  '¸°' => 'ë¦°',
  '¸±' => 'ë¦´',
  '¸²' => 'ë¦¼',
  '¸³' => 'ë¦½',
  '¸´' => 'ë¦¿',
  '¸µ' => 'ë§�',
  '¸¶' => 'ë§ˆ',
  '¸·' => 'ë§‰',
  '¸¸' => 'ë§Œ',
  '¸¹' => 'ë§Ž',
  '¸º' => 'ë§�',
  '¸»' => 'ë§�',
  '¸¼' => 'ë§‘',
  '¸½' => 'ë§’',
  '¸¾' => 'ë§˜',
  '¸¿' => 'ë§™',
  '¸À' => 'ë§›',
  '¸Á' => 'ë§�',
  '¸Â' => 'ë§ž',
  '¸Ã' => 'ë§¡',
  '¸Ä' => 'ë§£',
  '¸Å' => 'ë§¤',
  '¸Æ' => 'ë§¥',
  '¸Ç' => 'ë§¨',
  '¸È' => 'ë§¬',
  '¸É' => 'ë§´',
  '¸Ê' => 'ë§µ',
  '¸Ë' => 'ë§·',
  '¸Ì' => 'ë§¸',
  '¸Í' => 'ë§¹',
  '¸Î' => 'ë§º',
  '¸Ï' => 'ë¨€',
  '¸Ð' => 'ë¨�',
  '¸Ñ' => 'ë¨ˆ',
  '¸Ò' => 'ë¨•',
  '¸Ó' => 'ë¨¸',
  '¸Ô' => 'ë¨¹',
  '¸Õ' => 'ë¨¼',
  '¸Ö' => 'ë©€',
  '¸×' => 'ë©‚',
  '¸Ø' => 'ë©ˆ',
  '¸Ù' => 'ë©‰',
  '¸Ú' => 'ë©‹',
  '¸Û' => 'ë©�',
  '¸Ü' => 'ë©Ž',
  '¸Ý' => 'ë©“',
  '¸Þ' => 'ë©”',
  '¸ß' => 'ë©•',
  '¸à' => 'ë©˜',
  '¸á' => 'ë©œ',
  '¸â' => 'ë©¤',
  '¸ã' => 'ë©¥',
  '¸ä' => 'ë©§',
  '¸å' => 'ë©¨',
  '¸æ' => 'ë©©',
  '¸ç' => 'ë©°',
  '¸è' => 'ë©±',
  '¸é' => 'ë©´',
  '¸ê' => 'ë©¸',
  '¸ë' => 'ëªƒ',
  '¸ì' => 'ëª„',
  '¸í' => 'ëª…',
  '¸î' => 'ëª‡',
  '¸ï' => 'ëªŒ',
  '¸ð' => 'ëª¨',
  '¸ñ' => 'ëª©',
  '¸ò' => 'ëª«',
  '¸ó' => 'ëª¬',
  '¸ô' => 'ëª°',
  '¸õ' => 'ëª²',
  '¸ö' => 'ëª¸',
  '¸÷' => 'ëª¹',
  '¸ø' => 'ëª»',
  '¸ù' => 'ëª½',
  '¸ú' => 'ë«„',
  '¸û' => 'ë«ˆ',
  '¸ü' => 'ë«˜',
  '¸ý' => 'ë«™',
  '¸þ' => 'ë«¼',
  '¹A' => 'íˆª',
  '¹B' => 'íˆ«',
  '¹C' => 'íˆ®',
  '¹D' => 'íˆ¯',
  '¹E' => 'íˆ±',
  '¹F' => 'íˆ²',
  '¹G' => 'íˆ³',
  '¹H' => 'íˆµ',
  '¹I' => 'íˆ¶',
  '¹J' => 'íˆ·',
  '¹K' => 'íˆ¸',
  '¹L' => 'íˆ¹',
  '¹M' => 'íˆº',
  '¹N' => 'íˆ»',
  '¹O' => 'íˆ¾',
  '¹P' => 'í‰€',
  '¹Q' => 'í‰‚',
  '¹R' => 'í‰ƒ',
  '¹S' => 'í‰„',
  '¹T' => 'í‰…',
  '¹U' => 'í‰†',
  '¹V' => 'í‰‡',
  '¹W' => 'í‰‰',
  '¹X' => 'í‰Š',
  '¹Y' => 'í‰‹',
  '¹Z' => 'í‰Œ',
  '¹a' => 'í‰�',
  '¹b' => 'í‰Ž',
  '¹c' => 'í‰�',
  '¹d' => 'í‰�',
  '¹e' => 'í‰‘',
  '¹f' => 'í‰’',
  '¹g' => 'í‰“',
  '¹h' => 'í‰”',
  '¹i' => 'í‰•',
  '¹j' => 'í‰–',
  '¹k' => 'í‰—',
  '¹l' => 'í‰˜',
  '¹m' => 'í‰™',
  '¹n' => 'í‰š',
  '¹o' => 'í‰›',
  '¹p' => 'í‰�',
  '¹q' => 'í‰ž',
  '¹r' => 'í‰Ÿ',
  '¹s' => 'í‰ ',
  '¹t' => 'í‰¡',
  '¹u' => 'í‰¢',
  '¹v' => 'í‰£',
  '¹w' => 'í‰¥',
  '¹x' => 'í‰¦',
  '¹y' => 'í‰§',
  '¹z' => 'í‰¨',
  '¹�' => 'í‰©',
  '¹‚' => 'í‰ª',
  '¹ƒ' => 'í‰«',
  '¹„' => 'í‰¬',
  '¹…' => 'í‰­',
  '¹†' => 'í‰®',
  '¹‡' => 'í‰¯',
  '¹ˆ' => 'í‰°',
  '¹‰' => 'í‰±',
  '¹Š' => 'í‰²',
  '¹‹' => 'í‰³',
  '¹Œ' => 'í‰´',
  '¹�' => 'í‰µ',
  '¹Ž' => 'í‰¶',
  '¹�' => 'í‰·',
  '¹�' => 'í‰¸',
  '¹‘' => 'í‰¹',
  '¹’' => 'í‰º',
  '¹“' => 'í‰»',
  '¹”' => 'í‰¼',
  '¹•' => 'í‰½',
  '¹–' => 'í‰¾',
  '¹—' => 'í‰¿',
  '¹˜' => 'íŠ‚',
  '¹™' => 'íŠƒ',
  '¹š' => 'íŠ…',
  '¹›' => 'íŠ†',
  '¹œ' => 'íŠ‡',
  '¹�' => 'íŠ‰',
  '¹ž' => 'íŠŠ',
  '¹Ÿ' => 'íŠ‹',
  '¹ ' => 'íŠŒ',
  '¹¡' => 'ë¬€',
  '¹¢' => 'ë¬„',
  '¹£' => 'ë¬�',
  '¹¤' => 'ë¬�',
  '¹¥' => 'ë¬‘',
  '¹¦' => 'ë¬˜',
  '¹§' => 'ë¬œ',
  '¹¨' => 'ë¬ ',
  '¹©' => 'ë¬©',
  '¹ª' => 'ë¬«',
  '¹«' => 'ë¬´',
  '¹¬' => 'ë¬µ',
  '¹­' => 'ë¬¶',
  '¹®' => 'ë¬¸',
  '¹¯' => 'ë¬»',
  '¹°' => 'ë¬¼',
  '¹±' => 'ë¬½',
  '¹²' => 'ë¬¾',
  '¹³' => 'ë­„',
  '¹´' => 'ë­…',
  '¹µ' => 'ë­‡',
  '¹¶' => 'ë­‰',
  '¹·' => 'ë­�',
  '¹¸' => 'ë­�',
  '¹¹' => 'ë­�',
  '¹º' => 'ë­”',
  '¹»' => 'ë­˜',
  '¹¼' => 'ë­¡',
  '¹½' => 'ë­£',
  '¹¾' => 'ë­¬',
  '¹¿' => 'ë®ˆ',
  '¹À' => 'ë®Œ',
  '¹Á' => 'ë®�',
  '¹Â' => 'ë®¤',
  '¹Ã' => 'ë®¨',
  '¹Ä' => 'ë®¬',
  '¹Å' => 'ë®´',
  '¹Æ' => 'ë®·',
  '¹Ç' => 'ë¯€',
  '¹È' => 'ë¯„',
  '¹É' => 'ë¯ˆ',
  '¹Ê' => 'ë¯�',
  '¹Ë' => 'ë¯“',
  '¹Ì' => 'ë¯¸',
  '¹Í' => 'ë¯¹',
  '¹Î' => 'ë¯¼',
  '¹Ï' => 'ë¯¿',
  '¹Ð' => 'ë°€',
  '¹Ñ' => 'ë°‚',
  '¹Ò' => 'ë°ˆ',
  '¹Ó' => 'ë°‰',
  '¹Ô' => 'ë°‹',
  '¹Õ' => 'ë°Œ',
  '¹Ö' => 'ë°�',
  '¹×' => 'ë°�',
  '¹Ø' => 'ë°‘',
  '¹Ù' => 'ë°”',
  '¹Ú' => 'ë°•',
  '¹Û' => 'ë°–',
  '¹Ü' => 'ë°—',
  '¹Ý' => 'ë°˜',
  '¹Þ' => 'ë°›',
  '¹ß' => 'ë°œ',
  '¹à' => 'ë°�',
  '¹á' => 'ë°ž',
  '¹â' => 'ë°Ÿ',
  '¹ã' => 'ë°¤',
  '¹ä' => 'ë°¥',
  '¹å' => 'ë°§',
  '¹æ' => 'ë°©',
  '¹ç' => 'ë°­',
  '¹è' => 'ë°°',
  '¹é' => 'ë°±',
  '¹ê' => 'ë°´',
  '¹ë' => 'ë°¸',
  '¹ì' => 'ë±€',
  '¹í' => 'ë±�',
  '¹î' => 'ë±ƒ',
  '¹ï' => 'ë±„',
  '¹ð' => 'ë±…',
  '¹ñ' => 'ë±‰',
  '¹ò' => 'ë±Œ',
  '¹ó' => 'ë±�',
  '¹ô' => 'ë±�',
  '¹õ' => 'ë±�',
  '¹ö' => 'ë²„',
  '¹÷' => 'ë²…',
  '¹ø' => 'ë²ˆ',
  '¹ù' => 'ë²‹',
  '¹ú' => 'ë²Œ',
  '¹û' => 'ë²Ž',
  '¹ü' => 'ë²”',
  '¹ý' => 'ë²•',
  '¹þ' => 'ë²—',
  'ºA' => 'íŠ�',
  'ºB' => 'íŠŽ',
  'ºC' => 'íŠ�',
  'ºD' => 'íŠ’',
  'ºE' => 'íŠ“',
  'ºF' => 'íŠ”',
  'ºG' => 'íŠ–',
  'ºH' => 'íŠ—',
  'ºI' => 'íŠ˜',
  'ºJ' => 'íŠ™',
  'ºK' => 'íŠš',
  'ºL' => 'íŠ›',
  'ºM' => 'íŠ�',
  'ºN' => 'íŠž',
  'ºO' => 'íŠŸ',
  'ºP' => 'íŠ¡',
  'ºQ' => 'íŠ¢',
  'ºR' => 'íŠ£',
  'ºS' => 'íŠ¥',
  'ºT' => 'íŠ¦',
  'ºU' => 'íŠ§',
  'ºV' => 'íŠ¨',
  'ºW' => 'íŠ©',
  'ºX' => 'íŠª',
  'ºY' => 'íŠ«',
  'ºZ' => 'íŠ­',
  'ºa' => 'íŠ®',
  'ºb' => 'íŠ¯',
  'ºc' => 'íŠ°',
  'ºd' => 'íŠ²',
  'ºe' => 'íŠ³',
  'ºf' => 'íŠ´',
  'ºg' => 'íŠµ',
  'ºh' => 'íŠ¶',
  'ºi' => 'íŠ·',
  'ºj' => 'íŠº',
  'ºk' => 'íŠ»',
  'ºl' => 'íŠ½',
  'ºm' => 'íŠ¾',
  'ºn' => 'í‹�',
  'ºo' => 'í‹ƒ',
  'ºp' => 'í‹„',
  'ºq' => 'í‹…',
  'ºr' => 'í‹†',
  'ºs' => 'í‹‡',
  'ºt' => 'í‹Š',
  'ºu' => 'í‹Œ',
  'ºv' => 'í‹�',
  'ºw' => 'í‹Ž',
  'ºx' => 'í‹�',
  'ºy' => 'í‹�',
  'ºz' => 'í‹‘',
  'º�' => 'í‹’',
  'º‚' => 'í‹“',
  'ºƒ' => 'í‹•',
  'º„' => 'í‹–',
  'º…' => 'í‹—',
  'º†' => 'í‹™',
  'º‡' => 'í‹š',
  'ºˆ' => 'í‹›',
  'º‰' => 'í‹�',
  'ºŠ' => 'í‹ž',
  'º‹' => 'í‹Ÿ',
  'ºŒ' => 'í‹ ',
  'º�' => 'í‹¡',
  'ºŽ' => 'í‹¢',
  'º�' => 'í‹£',
  'º�' => 'í‹¦',
  'º‘' => 'í‹§',
  'º’' => 'í‹¨',
  'º“' => 'í‹©',
  'º”' => 'í‹ª',
  'º•' => 'í‹«',
  'º–' => 'í‹¬',
  'º—' => 'í‹­',
  'º˜' => 'í‹®',
  'º™' => 'í‹¯',
  'ºš' => 'í‹²',
  'º›' => 'í‹³',
  'ºœ' => 'í‹µ',
  'º�' => 'í‹¶',
  'ºž' => 'í‹·',
  'ºŸ' => 'í‹¹',
  'º ' => 'í‹º',
  'º¡' => 'ë²™',
  'º¢' => 'ë²š',
  'º£' => 'ë² ',
  'º¤' => 'ë²¡',
  'º¥' => 'ë²¤',
  'º¦' => 'ë²§',
  'º§' => 'ë²¨',
  'º¨' => 'ë²°',
  'º©' => 'ë²±',
  'ºª' => 'ë²³',
  'º«' => 'ë²´',
  'º¬' => 'ë²µ',
  'º­' => 'ë²¼',
  'º®' => 'ë²½',
  'º¯' => 'ë³€',
  'º°' => 'ë³„',
  'º±' => 'ë³�',
  'º²' => 'ë³�',
  'º³' => 'ë³�',
  'º´' => 'ë³‘',
  'ºµ' => 'ë³•',
  'º¶' => 'ë³˜',
  'º·' => 'ë³œ',
  'º¸' => 'ë³´',
  'º¹' => 'ë³µ',
  'ºº' => 'ë³¶',
  'º»' => 'ë³¸',
  'º¼' => 'ë³¼',
  'º½' => 'ë´„',
  'º¾' => 'ë´…',
  'º¿' => 'ë´‡',
  'ºÀ' => 'ë´‰',
  'ºÁ' => 'ë´�',
  'ºÂ' => 'ë´”',
  'ºÃ' => 'ë´¤',
  'ºÄ' => 'ë´¬',
  'ºÅ' => 'ëµ€',
  'ºÆ' => 'ëµˆ',
  'ºÇ' => 'ëµ‰',
  'ºÈ' => 'ëµŒ',
  'ºÉ' => 'ëµ�',
  'ºÊ' => 'ëµ˜',
  'ºË' => 'ëµ™',
  'ºÌ' => 'ëµ¤',
  'ºÍ' => 'ëµ¨',
  'ºÎ' => 'ë¶€',
  'ºÏ' => 'ë¶�',
  'ºÐ' => 'ë¶„',
  'ºÑ' => 'ë¶‡',
  'ºÒ' => 'ë¶ˆ',
  'ºÓ' => 'ë¶‰',
  'ºÔ' => 'ë¶Š',
  'ºÕ' => 'ë¶�',
  'ºÖ' => 'ë¶‘',
  'º×' => 'ë¶“',
  'ºØ' => 'ë¶•',
  'ºÙ' => 'ë¶™',
  'ºÚ' => 'ë¶š',
  'ºÛ' => 'ë¶œ',
  'ºÜ' => 'ë¶¤',
  'ºÝ' => 'ë¶°',
  'ºÞ' => 'ë¶¸',
  'ºß' => 'ë·”',
  'ºà' => 'ë·•',
  'ºá' => 'ë·˜',
  'ºâ' => 'ë·œ',
  'ºã' => 'ë·©',
  'ºä' => 'ë·°',
  'ºå' => 'ë·´',
  'ºæ' => 'ë·¸',
  'ºç' => 'ë¸€',
  'ºè' => 'ë¸ƒ',
  'ºé' => 'ë¸…',
  'ºê' => 'ë¸Œ',
  'ºë' => 'ë¸�',
  'ºì' => 'ë¸�',
  'ºí' => 'ë¸”',
  'ºî' => 'ë¸œ',
  'ºï' => 'ë¸�',
  'ºð' => 'ë¸Ÿ',
  'ºñ' => 'ë¹„',
  'ºò' => 'ë¹…',
  'ºó' => 'ë¹ˆ',
  'ºô' => 'ë¹Œ',
  'ºõ' => 'ë¹Ž',
  'ºö' => 'ë¹”',
  'º÷' => 'ë¹•',
  'ºø' => 'ë¹—',
  'ºù' => 'ë¹™',
  'ºú' => 'ë¹š',
  'ºû' => 'ë¹›',
  'ºü' => 'ë¹ ',
  'ºý' => 'ë¹¡',
  'ºþ' => 'ë¹¤',
  '»A' => 'í‹»',
  '»B' => 'í‹¼',
  '»C' => 'í‹½',
  '»D' => 'í‹¾',
  '»E' => 'í‹¿',
  '»F' => 'íŒ‚',
  '»G' => 'íŒ„',
  '»H' => 'íŒ†',
  '»I' => 'íŒ‡',
  '»J' => 'íŒˆ',
  '»K' => 'íŒ‰',
  '»L' => 'íŒŠ',
  '»M' => 'íŒ‹',
  '»N' => 'íŒ�',
  '»O' => 'íŒ‘',
  '»P' => 'íŒ’',
  '»Q' => 'íŒ“',
  '»R' => 'íŒ•',
  '»S' => 'íŒ—',
  '»T' => 'íŒ˜',
  '»U' => 'íŒ™',
  '»V' => 'íŒš',
  '»W' => 'íŒ›',
  '»X' => 'íŒž',
  '»Y' => 'íŒ¢',
  '»Z' => 'íŒ£',
  '»a' => 'íŒ¤',
  '»b' => 'íŒ¦',
  '»c' => 'íŒ§',
  '»d' => 'íŒª',
  '»e' => 'íŒ«',
  '»f' => 'íŒ­',
  '»g' => 'íŒ®',
  '»h' => 'íŒ¯',
  '»i' => 'íŒ±',
  '»j' => 'íŒ²',
  '»k' => 'íŒ³',
  '»l' => 'íŒ´',
  '»m' => 'íŒµ',
  '»n' => 'íŒ¶',
  '»o' => 'íŒ·',
  '»p' => 'íŒº',
  '»q' => 'íŒ¾',
  '»r' => 'íŒ¿',
  '»s' => 'í�€',
  '»t' => 'í��',
  '»u' => 'í�‚',
  '»v' => 'í�ƒ',
  '»w' => 'í�†',
  '»x' => 'í�‡',
  '»y' => 'í�ˆ',
  '»z' => 'í�‰',
  '»�' => 'í�Š',
  '»‚' => 'í�‹',
  '»ƒ' => 'í�Œ',
  '»„' => 'í��',
  '»…' => 'í�Ž',
  '»†' => 'í��',
  '»‡' => 'í��',
  '»ˆ' => 'í�‘',
  '»‰' => 'í�’',
  '»Š' => 'í�“',
  '»‹' => 'í�”',
  '»Œ' => 'í�•',
  '»�' => 'í�–',
  '»Ž' => 'í�—',
  '»�' => 'í�˜',
  '»�' => 'í�™',
  '»‘' => 'í�š',
  '»’' => 'í�›',
  '»“' => 'í�œ',
  '»”' => 'í��',
  '»•' => 'í�ž',
  '»–' => 'í�Ÿ',
  '»—' => 'í� ',
  '»˜' => 'í�¡',
  '»™' => 'í�¢',
  '»š' => 'í�£',
  '»›' => 'í�¤',
  '»œ' => 'í�¥',
  '»�' => 'í�¦',
  '»ž' => 'í�§',
  '»Ÿ' => 'í�¨',
  '» ' => 'í�©',
  '»¡' => 'ë¹¨',
  '»¢' => 'ë¹ª',
  '»£' => 'ë¹°',
  '»¤' => 'ë¹±',
  '»¥' => 'ë¹³',
  '»¦' => 'ë¹´',
  '»§' => 'ë¹µ',
  '»¨' => 'ë¹»',
  '»©' => 'ë¹¼',
  '»ª' => 'ë¹½',
  '»«' => 'ëº€',
  '»¬' => 'ëº„',
  '»­' => 'ëºŒ',
  '»®' => 'ëº�',
  '»¯' => 'ëº�',
  '»°' => 'ëº�',
  '»±' => 'ëº‘',
  '»²' => 'ëº˜',
  '»³' => 'ëº™',
  '»´' => 'ëº¨',
  '»µ' => 'ë»�',
  '»¶' => 'ë»‘',
  '»·' => 'ë»”',
  '»¸' => 'ë»—',
  '»¹' => 'ë»˜',
  '»º' => 'ë» ',
  '»»' => 'ë»£',
  '»¼' => 'ë»¤',
  '»½' => 'ë»¥',
  '»¾' => 'ë»¬',
  '»¿' => 'ë¼�',
  '»À' => 'ë¼ˆ',
  '»Á' => 'ë¼‰',
  '»Â' => 'ë¼˜',
  '»Ã' => 'ë¼™',
  '»Ä' => 'ë¼›',
  '»Å' => 'ë¼œ',
  '»Æ' => 'ë¼�',
  '»Ç' => 'ë½€',
  '»È' => 'ë½�',
  '»É' => 'ë½„',
  '»Ê' => 'ë½ˆ',
  '»Ë' => 'ë½�',
  '»Ì' => 'ë½‘',
  '»Í' => 'ë½•',
  '»Î' => 'ë¾”',
  '»Ï' => 'ë¾°',
  '»Ð' => 'ë¿…',
  '»Ñ' => 'ë¿Œ',
  '»Ò' => 'ë¿�',
  '»Ó' => 'ë¿�',
  '»Ô' => 'ë¿”',
  '»Õ' => 'ë¿œ',
  '»Ö' => 'ë¿Ÿ',
  '»×' => 'ë¿¡',
  '»Ø' => 'ì€¼',
  '»Ù' => 'ì�‘',
  '»Ú' => 'ì�˜',
  '»Û' => 'ì�œ',
  '»Ü' => 'ì� ',
  '»Ý' => 'ì�¨',
  '»Þ' => 'ì�©',
  '»ß' => 'ì‚�',
  '»à' => 'ì‚‘',
  '»á' => 'ì‚”',
  '»â' => 'ì‚˜',
  '»ã' => 'ì‚ ',
  '»ä' => 'ì‚¡',
  '»å' => 'ì‚£',
  '»æ' => 'ì‚¥',
  '»ç' => 'ì‚¬',
  '»è' => 'ì‚­',
  '»é' => 'ì‚¯',
  '»ê' => 'ì‚°',
  '»ë' => 'ì‚³',
  '»ì' => 'ì‚´',
  '»í' => 'ì‚µ',
  '»î' => 'ì‚¶',
  '»ï' => 'ì‚¼',
  '»ð' => 'ì‚½',
  '»ñ' => 'ì‚¿',
  '»ò' => 'ìƒ€',
  '»ó' => 'ìƒ�',
  '»ô' => 'ìƒ…',
  '»õ' => 'ìƒˆ',
  '»ö' => 'ìƒ‰',
  '»÷' => 'ìƒŒ',
  '»ø' => 'ìƒ�',
  '»ù' => 'ìƒ˜',
  '»ú' => 'ìƒ™',
  '»û' => 'ìƒ›',
  '»ü' => 'ìƒœ',
  '»ý' => 'ìƒ�',
  '»þ' => 'ìƒ¤',
  '¼A' => 'í�ª',
  '¼B' => 'í�«',
  '¼C' => 'í�¬',
  '¼D' => 'í�­',
  '¼E' => 'í�®',
  '¼F' => 'í�¯',
  '¼G' => 'í�°',
  '¼H' => 'í�±',
  '¼I' => 'í�²',
  '¼J' => 'í�³',
  '¼K' => 'í�´',
  '¼L' => 'í�µ',
  '¼M' => 'í�¶',
  '¼N' => 'í�·',
  '¼O' => 'í�¸',
  '¼P' => 'í�¹',
  '¼Q' => 'í�º',
  '¼R' => 'í�»',
  '¼S' => 'í�¾',
  '¼T' => 'í�¿',
  '¼U' => 'íŽ�',
  '¼V' => 'íŽ‚',
  '¼W' => 'íŽƒ',
  '¼X' => 'íŽ…',
  '¼Y' => 'íŽ†',
  '¼Z' => 'íŽ‡',
  '¼a' => 'íŽˆ',
  '¼b' => 'íŽ‰',
  '¼c' => 'íŽŠ',
  '¼d' => 'íŽ‹',
  '¼e' => 'íŽŽ',
  '¼f' => 'íŽ’',
  '¼g' => 'íŽ“',
  '¼h' => 'íŽ”',
  '¼i' => 'íŽ•',
  '¼j' => 'íŽ–',
  '¼k' => 'íŽ—',
  '¼l' => 'íŽš',
  '¼m' => 'íŽ›',
  '¼n' => 'íŽ�',
  '¼o' => 'íŽž',
  '¼p' => 'íŽŸ',
  '¼q' => 'íŽ¡',
  '¼r' => 'íŽ¢',
  '¼s' => 'íŽ£',
  '¼t' => 'íŽ¤',
  '¼u' => 'íŽ¥',
  '¼v' => 'íŽ¦',
  '¼w' => 'íŽ§',
  '¼x' => 'íŽª',
  '¼y' => 'íŽ¬',
  '¼z' => 'íŽ®',
  '¼�' => 'íŽ¯',
  '¼‚' => 'íŽ°',
  '¼ƒ' => 'íŽ±',
  '¼„' => 'íŽ²',
  '¼…' => 'íŽ³',
  '¼†' => 'íŽµ',
  '¼‡' => 'íŽ¶',
  '¼ˆ' => 'íŽ·',
  '¼‰' => 'íŽ¹',
  '¼Š' => 'íŽº',
  '¼‹' => 'íŽ»',
  '¼Œ' => 'íŽ½',
  '¼�' => 'íŽ¾',
  '¼Ž' => 'íŽ¿',
  '¼�' => 'í�€',
  '¼�' => 'í��',
  '¼‘' => 'í�‚',
  '¼’' => 'í�ƒ',
  '¼“' => 'í�†',
  '¼”' => 'í�‡',
  '¼•' => 'í�Š',
  '¼–' => 'í�‹',
  '¼—' => 'í�Œ',
  '¼˜' => 'í��',
  '¼™' => 'í�Ž',
  '¼š' => 'í��',
  '¼›' => 'í�‘',
  '¼œ' => 'í�’',
  '¼�' => 'í�“',
  '¼ž' => 'í�”',
  '¼Ÿ' => 'í�•',
  '¼ ' => 'í�–',
  '¼¡' => 'ìƒ¥',
  '¼¢' => 'ìƒ¨',
  '¼£' => 'ìƒ¬',
  '¼¤' => 'ìƒ´',
  '¼¥' => 'ìƒµ',
  '¼¦' => 'ìƒ·',
  '¼§' => 'ìƒ¹',
  '¼¨' => 'ì„€',
  '¼©' => 'ì„„',
  '¼ª' => 'ì„ˆ',
  '¼«' => 'ì„�',
  '¼¬' => 'ì„•',
  '¼­' => 'ì„œ',
  '¼®' => 'ì„�',
  '¼¯' => 'ì„ž',
  '¼°' => 'ì„Ÿ',
  '¼±' => 'ì„ ',
  '¼²' => 'ì„£',
  '¼³' => 'ì„¤',
  '¼´' => 'ì„¦',
  '¼µ' => 'ì„§',
  '¼¶' => 'ì„¬',
  '¼·' => 'ì„­',
  '¼¸' => 'ì„¯',
  '¼¹' => 'ì„°',
  '¼º' => 'ì„±',
  '¼»' => 'ì„¶',
  '¼¼' => 'ì„¸',
  '¼½' => 'ì„¹',
  '¼¾' => 'ì„¼',
  '¼¿' => 'ì…€',
  '¼À' => 'ì…ˆ',
  '¼Á' => 'ì…‰',
  '¼Â' => 'ì…‹',
  '¼Ã' => 'ì…Œ',
  '¼Ä' => 'ì…�',
  '¼Å' => 'ì…”',
  '¼Æ' => 'ì…•',
  '¼Ç' => 'ì…˜',
  '¼È' => 'ì…œ',
  '¼É' => 'ì…¤',
  '¼Ê' => 'ì…¥',
  '¼Ë' => 'ì…§',
  '¼Ì' => 'ì…¨',
  '¼Í' => 'ì…©',
  '¼Î' => 'ì…°',
  '¼Ï' => 'ì…´',
  '¼Ð' => 'ì…¸',
  '¼Ñ' => 'ì†…',
  '¼Ò' => 'ì†Œ',
  '¼Ó' => 'ì†�',
  '¼Ô' => 'ì†Ž',
  '¼Õ' => 'ì†�',
  '¼Ö' => 'ì†”',
  '¼×' => 'ì†–',
  '¼Ø' => 'ì†œ',
  '¼Ù' => 'ì†�',
  '¼Ú' => 'ì†Ÿ',
  '¼Û' => 'ì†¡',
  '¼Ü' => 'ì†¥',
  '¼Ý' => 'ì†¨',
  '¼Þ' => 'ì†©',
  '¼ß' => 'ì†¬',
  '¼à' => 'ì†°',
  '¼á' => 'ì†½',
  '¼â' => 'ì‡„',
  '¼ã' => 'ì‡ˆ',
  '¼ä' => 'ì‡Œ',
  '¼å' => 'ì‡”',
  '¼æ' => 'ì‡—',
  '¼ç' => 'ì‡˜',
  '¼è' => 'ì‡ ',
  '¼é' => 'ì‡¤',
  '¼ê' => 'ì‡¨',
  '¼ë' => 'ì‡°',
  '¼ì' => 'ì‡±',
  '¼í' => 'ì‡³',
  '¼î' => 'ì‡¼',
  '¼ï' => 'ì‡½',
  '¼ð' => 'ìˆ€',
  '¼ñ' => 'ìˆ„',
  '¼ò' => 'ìˆŒ',
  '¼ó' => 'ìˆ�',
  '¼ô' => 'ìˆ�',
  '¼õ' => 'ìˆ‘',
  '¼ö' => 'ìˆ˜',
  '¼÷' => 'ìˆ™',
  '¼ø' => 'ìˆœ',
  '¼ù' => 'ìˆŸ',
  '¼ú' => 'ìˆ ',
  '¼û' => 'ìˆ¨',
  '¼ü' => 'ìˆ©',
  '¼ý' => 'ìˆ«',
  '¼þ' => 'ìˆ­',
  '½A' => 'í�—',
  '½B' => 'í�™',
  '½C' => 'í�š',
  '½D' => 'í�›',
  '½E' => 'í�œ',
  '½F' => 'í��',
  '½G' => 'í�ž',
  '½H' => 'í�Ÿ',
  '½I' => 'í� ',
  '½J' => 'í�¢',
  '½K' => 'í�¤',
  '½L' => 'í�¥',
  '½M' => 'í�¦',
  '½N' => 'í�§',
  '½O' => 'í�¨',
  '½P' => 'í�©',
  '½Q' => 'í�ª',
  '½R' => 'í�«',
  '½S' => 'í�®',
  '½T' => 'í�¯',
  '½U' => 'í�±',
  '½V' => 'í�²',
  '½W' => 'í�³',
  '½X' => 'í�µ',
  '½Y' => 'í�¶',
  '½Z' => 'í�·',
  '½a' => 'í�¸',
  '½b' => 'í�¹',
  '½c' => 'í�º',
  '½d' => 'í�»',
  '½e' => 'í�¾',
  '½f' => 'í�€',
  '½g' => 'í�‚',
  '½h' => 'í�ƒ',
  '½i' => 'í�„',
  '½j' => 'í�…',
  '½k' => 'í�†',
  '½l' => 'í�‡',
  '½m' => 'í�‰',
  '½n' => 'í�Š',
  '½o' => 'í�‹',
  '½p' => 'í�Œ',
  '½q' => 'í��',
  '½r' => 'í�Ž',
  '½s' => 'í��',
  '½t' => 'í��',
  '½u' => 'í�‘',
  '½v' => 'í�’',
  '½w' => 'í�“',
  '½x' => 'í�”',
  '½y' => 'í�•',
  '½z' => 'í�–',
  '½�' => 'í�—',
  '½‚' => 'í�˜',
  '½ƒ' => 'í�™',
  '½„' => 'í�š',
  '½…' => 'í�›',
  '½†' => 'í�œ',
  '½‡' => 'í�ž',
  '½ˆ' => 'í�Ÿ',
  '½‰' => 'í� ',
  '½Š' => 'í�¡',
  '½‹' => 'í�¢',
  '½Œ' => 'í�£',
  '½�' => 'í�¤',
  '½Ž' => 'í�¥',
  '½�' => 'í�¦',
  '½�' => 'í�§',
  '½‘' => 'í�¨',
  '½’' => 'í�©',
  '½“' => 'í�ª',
  '½”' => 'í�«',
  '½•' => 'í�¬',
  '½–' => 'í�­',
  '½—' => 'í�®',
  '½˜' => 'í�¯',
  '½™' => 'í�°',
  '½š' => 'í�±',
  '½›' => 'í�²',
  '½œ' => 'í�³',
  '½�' => 'í�´',
  '½ž' => 'í�µ',
  '½Ÿ' => 'í�¶',
  '½ ' => 'í�·',
  '½¡' => 'ìˆ¯',
  '½¢' => 'ìˆ±',
  '½£' => 'ìˆ²',
  '½¤' => 'ìˆ´',
  '½¥' => 'ì‰ˆ',
  '½¦' => 'ì‰�',
  '½§' => 'ì‰‘',
  '½¨' => 'ì‰”',
  '½©' => 'ì‰˜',
  '½ª' => 'ì‰ ',
  '½«' => 'ì‰¥',
  '½¬' => 'ì‰¬',
  '½­' => 'ì‰­',
  '½®' => 'ì‰°',
  '½¯' => 'ì‰´',
  '½°' => 'ì‰¼',
  '½±' => 'ì‰½',
  '½²' => 'ì‰¿',
  '½³' => 'ìŠ�',
  '½´' => 'ìŠˆ',
  '½µ' => 'ìŠ‰',
  '½¶' => 'ìŠ�',
  '½·' => 'ìŠ˜',
  '½¸' => 'ìŠ›',
  '½¹' => 'ìŠ�',
  '½º' => 'ìŠ¤',
  '½»' => 'ìŠ¥',
  '½¼' => 'ìŠ¨',
  '½½' => 'ìŠ¬',
  '½¾' => 'ìŠ­',
  '½¿' => 'ìŠ´',
  '½À' => 'ìŠµ',
  '½Á' => 'ìŠ·',
  '½Â' => 'ìŠ¹',
  '½Ã' => 'ì‹œ',
  '½Ä' => 'ì‹�',
  '½Å' => 'ì‹ ',
  '½Æ' => 'ì‹£',
  '½Ç' => 'ì‹¤',
  '½È' => 'ì‹«',
  '½É' => 'ì‹¬',
  '½Ê' => 'ì‹­',
  '½Ë' => 'ì‹¯',
  '½Ì' => 'ì‹±',
  '½Í' => 'ì‹¶',
  '½Î' => 'ì‹¸',
  '½Ï' => 'ì‹¹',
  '½Ð' => 'ì‹»',
  '½Ñ' => 'ì‹¼',
  '½Ò' => 'ìŒ€',
  '½Ó' => 'ìŒˆ',
  '½Ô' => 'ìŒ‰',
  '½Õ' => 'ìŒŒ',
  '½Ö' => 'ìŒ�',
  '½×' => 'ìŒ“',
  '½Ø' => 'ìŒ”',
  '½Ù' => 'ìŒ•',
  '½Ú' => 'ìŒ˜',
  '½Û' => 'ìŒœ',
  '½Ü' => 'ìŒ¤',
  '½Ý' => 'ìŒ¥',
  '½Þ' => 'ìŒ¨',
  '½ß' => 'ìŒ©',
  '½à' => 'ì�…',
  '½á' => 'ì�¨',
  '½â' => 'ì�©',
  '½ã' => 'ì�¬',
  '½ä' => 'ì�°',
  '½å' => 'ì�²',
  '½æ' => 'ì�¸',
  '½ç' => 'ì�¹',
  '½è' => 'ì�¼',
  '½é' => 'ì�½',
  '½ê' => 'ìŽ„',
  '½ë' => 'ìŽˆ',
  '½ì' => 'ìŽŒ',
  '½í' => 'ì�€',
  '½î' => 'ì�˜',
  '½ï' => 'ì�™',
  '½ð' => 'ì�œ',
  '½ñ' => 'ì�Ÿ',
  '½ò' => 'ì� ',
  '½ó' => 'ì�¢',
  '½ô' => 'ì�¨',
  '½õ' => 'ì�©',
  '½ö' => 'ì�­',
  '½÷' => 'ì�´',
  '½ø' => 'ì�µ',
  '½ù' => 'ì�¸',
  '½ú' => 'ì�ˆ',
  '½û' => 'ì��',
  '½ü' => 'ì�¤',
  '½ý' => 'ì�¬',
  '½þ' => 'ì�°',
  '¾A' => 'í�¸',
  '¾B' => 'í�¹',
  '¾C' => 'í�º',
  '¾D' => 'í�»',
  '¾E' => 'í�¼',
  '¾F' => 'í�½',
  '¾G' => 'í�¾',
  '¾H' => 'í�¿',
  '¾I' => 'í‘�',
  '¾J' => 'í‘‚',
  '¾K' => 'í‘ƒ',
  '¾L' => 'í‘…',
  '¾M' => 'í‘†',
  '¾N' => 'í‘‡',
  '¾O' => 'í‘ˆ',
  '¾P' => 'í‘‰',
  '¾Q' => 'í‘Š',
  '¾R' => 'í‘‹',
  '¾S' => 'í‘Œ',
  '¾T' => 'í‘�',
  '¾U' => 'í‘Ž',
  '¾V' => 'í‘�',
  '¾W' => 'í‘�',
  '¾X' => 'í‘‘',
  '¾Y' => 'í‘’',
  '¾Z' => 'í‘“',
  '¾a' => 'í‘”',
  '¾b' => 'í‘•',
  '¾c' => 'í‘–',
  '¾d' => 'í‘—',
  '¾e' => 'í‘˜',
  '¾f' => 'í‘™',
  '¾g' => 'í‘š',
  '¾h' => 'í‘›',
  '¾i' => 'í‘�',
  '¾j' => 'í‘ž',
  '¾k' => 'í‘Ÿ',
  '¾l' => 'í‘¡',
  '¾m' => 'í‘¢',
  '¾n' => 'í‘£',
  '¾o' => 'í‘¥',
  '¾p' => 'í‘¦',
  '¾q' => 'í‘§',
  '¾r' => 'í‘¨',
  '¾s' => 'í‘©',
  '¾t' => 'í‘ª',
  '¾u' => 'í‘«',
  '¾v' => 'í‘¬',
  '¾w' => 'í‘®',
  '¾x' => 'í‘°',
  '¾y' => 'í‘±',
  '¾z' => 'í‘²',
  '¾�' => 'í‘³',
  '¾‚' => 'í‘´',
  '¾ƒ' => 'í‘µ',
  '¾„' => 'í‘¶',
  '¾…' => 'í‘·',
  '¾†' => 'í‘º',
  '¾‡' => 'í‘»',
  '¾ˆ' => 'í‘½',
  '¾‰' => 'í‘¾',
  '¾Š' => 'í’�',
  '¾‹' => 'í’ƒ',
  '¾Œ' => 'í’„',
  '¾�' => 'í’…',
  '¾Ž' => 'í’†',
  '¾�' => 'í’‡',
  '¾�' => 'í’Š',
  '¾‘' => 'í’Œ',
  '¾’' => 'í’Ž',
  '¾“' => 'í’�',
  '¾”' => 'í’�',
  '¾•' => 'í’‘',
  '¾–' => 'í’’',
  '¾—' => 'í’“',
  '¾˜' => 'í’•',
  '¾™' => 'í’–',
  '¾š' => 'í’—',
  '¾›' => 'í’˜',
  '¾œ' => 'í’™',
  '¾�' => 'í’š',
  '¾ž' => 'í’›',
  '¾Ÿ' => 'í’œ',
  '¾ ' => 'í’�',
  '¾¡' => 'ì�´',
  '¾¢' => 'ì�¼',
  '¾£' => 'ì�½',
  '¾¤' => 'ì‘ˆ',
  '¾¥' => 'ì‘¤',
  '¾¦' => 'ì‘¥',
  '¾§' => 'ì‘¨',
  '¾¨' => 'ì‘¬',
  '¾©' => 'ì‘´',
  '¾ª' => 'ì‘µ',
  '¾«' => 'ì‘¹',
  '¾¬' => 'ì’€',
  '¾­' => 'ì’”',
  '¾®' => 'ì’œ',
  '¾¯' => 'ì’¸',
  '¾°' => 'ì’¼',
  '¾±' => 'ì“©',
  '¾²' => 'ì“°',
  '¾³' => 'ì“±',
  '¾´' => 'ì“´',
  '¾µ' => 'ì“¸',
  '¾¶' => 'ì“º',
  '¾·' => 'ì“¿',
  '¾¸' => 'ì”€',
  '¾¹' => 'ì”�',
  '¾º' => 'ì”Œ',
  '¾»' => 'ì”�',
  '¾¼' => 'ì””',
  '¾½' => 'ì”œ',
  '¾¾' => 'ì”¨',
  '¾¿' => 'ì”©',
  '¾À' => 'ì”¬',
  '¾Á' => 'ì”°',
  '¾Â' => 'ì”¸',
  '¾Ã' => 'ì”¹',
  '¾Ä' => 'ì”»',
  '¾Å' => 'ì”½',
  '¾Æ' => 'ì•„',
  '¾Ç' => 'ì•…',
  '¾È' => 'ì•ˆ',
  '¾É' => 'ì•‰',
  '¾Ê' => 'ì•Š',
  '¾Ë' => 'ì•Œ',
  '¾Ì' => 'ì•�',
  '¾Í' => 'ì•Ž',
  '¾Î' => 'ì•“',
  '¾Ï' => 'ì•”',
  '¾Ð' => 'ì••',
  '¾Ñ' => 'ì•—',
  '¾Ò' => 'ì•˜',
  '¾Ó' => 'ì•™',
  '¾Ô' => 'ì•�',
  '¾Õ' => 'ì•ž',
  '¾Ö' => 'ì• ',
  '¾×' => 'ì•¡',
  '¾Ø' => 'ì•¤',
  '¾Ù' => 'ì•¨',
  '¾Ú' => 'ì•°',
  '¾Û' => 'ì•±',
  '¾Ü' => 'ì•³',
  '¾Ý' => 'ì•´',
  '¾Þ' => 'ì•µ',
  '¾ß' => 'ì•¼',
  '¾à' => 'ì•½',
  '¾á' => 'ì–€',
  '¾â' => 'ì–„',
  '¾ã' => 'ì–‡',
  '¾ä' => 'ì–Œ',
  '¾å' => 'ì–�',
  '¾æ' => 'ì–�',
  '¾ç' => 'ì–‘',
  '¾è' => 'ì–•',
  '¾é' => 'ì–—',
  '¾ê' => 'ì–˜',
  '¾ë' => 'ì–œ',
  '¾ì' => 'ì– ',
  '¾í' => 'ì–©',
  '¾î' => 'ì–´',
  '¾ï' => 'ì–µ',
  '¾ð' => 'ì–¸',
  '¾ñ' => 'ì–¹',
  '¾ò' => 'ì–»',
  '¾ó' => 'ì–¼',
  '¾ô' => 'ì–½',
  '¾õ' => 'ì–¾',
  '¾ö' => 'ì—„',
  '¾÷' => 'ì—…',
  '¾ø' => 'ì—†',
  '¾ù' => 'ì—‡',
  '¾ú' => 'ì—ˆ',
  '¾û' => 'ì—‰',
  '¾ü' => 'ì—Š',
  '¾ý' => 'ì—Œ',
  '¾þ' => 'ì—Ž',
  '¿A' => 'í’ž',
  '¿B' => 'í’Ÿ',
  '¿C' => 'í’ ',
  '¿D' => 'í’¡',
  '¿E' => 'í’¢',
  '¿F' => 'í’£',
  '¿G' => 'í’¤',
  '¿H' => 'í’¥',
  '¿I' => 'í’¦',
  '¿J' => 'í’§',
  '¿K' => 'í’¨',
  '¿L' => 'í’ª',
  '¿M' => 'í’«',
  '¿N' => 'í’¬',
  '¿O' => 'í’­',
  '¿P' => 'í’®',
  '¿Q' => 'í’¯',
  '¿R' => 'í’°',
  '¿S' => 'í’±',
  '¿T' => 'í’²',
  '¿U' => 'í’³',
  '¿V' => 'í’´',
  '¿W' => 'í’µ',
  '¿X' => 'í’¶',
  '¿Y' => 'í’·',
  '¿Z' => 'í’¸',
  '¿a' => 'í’¹',
  '¿b' => 'í’º',
  '¿c' => 'í’»',
  '¿d' => 'í’¼',
  '¿e' => 'í’½',
  '¿f' => 'í’¾',
  '¿g' => 'í’¿',
  '¿h' => 'í“€',
  '¿i' => 'í“�',
  '¿j' => 'í“‚',
  '¿k' => 'í“ƒ',
  '¿l' => 'í“„',
  '¿m' => 'í“…',
  '¿n' => 'í“†',
  '¿o' => 'í“‡',
  '¿p' => 'í“ˆ',
  '¿q' => 'í“‰',
  '¿r' => 'í“Š',
  '¿s' => 'í“‹',
  '¿t' => 'í“�',
  '¿u' => 'í“Ž',
  '¿v' => 'í“�',
  '¿w' => 'í“‘',
  '¿x' => 'í“’',
  '¿y' => 'í““',
  '¿z' => 'í“•',
  '¿�' => 'í“–',
  '¿‚' => 'í“—',
  '¿ƒ' => 'í“˜',
  '¿„' => 'í“™',
  '¿…' => 'í“š',
  '¿†' => 'í“›',
  '¿‡' => 'í“�',
  '¿ˆ' => 'í“ž',
  '¿‰' => 'í“ ',
  '¿Š' => 'í“¡',
  '¿‹' => 'í“¢',
  '¿Œ' => 'í“£',
  '¿�' => 'í“¤',
  '¿Ž' => 'í“¥',
  '¿�' => 'í“¦',
  '¿�' => 'í“§',
  '¿‘' => 'í“©',
  '¿’' => 'í“ª',
  '¿“' => 'í“«',
  '¿”' => 'í“­',
  '¿•' => 'í“®',
  '¿–' => 'í“¯',
  '¿—' => 'í“±',
  '¿˜' => 'í“²',
  '¿™' => 'í“³',
  '¿š' => 'í“´',
  '¿›' => 'í“µ',
  '¿œ' => 'í“¶',
  '¿�' => 'í“·',
  '¿ž' => 'í“¹',
  '¿Ÿ' => 'í“º',
  '¿ ' => 'í“¼',
  '¿¡' => 'ì—�',
  '¿¢' => 'ì—‘',
  '¿£' => 'ì—”',
  '¿¤' => 'ì—˜',
  '¿¥' => 'ì— ',
  '¿¦' => 'ì—¡',
  '¿§' => 'ì—£',
  '¿¨' => 'ì—¥',
  '¿©' => 'ì—¬',
  '¿ª' => 'ì—­',
  '¿«' => 'ì—®',
  '¿¬' => 'ì—°',
  '¿­' => 'ì—´',
  '¿®' => 'ì—¶',
  '¿¯' => 'ì—·',
  '¿°' => 'ì—¼',
  '¿±' => 'ì—½',
  '¿²' => 'ì—¾',
  '¿³' => 'ì—¿',
  '¿´' => 'ì˜€',
  '¿µ' => 'ì˜�',
  '¿¶' => 'ì˜…',
  '¿·' => 'ì˜†',
  '¿¸' => 'ì˜‡',
  '¿¹' => 'ì˜ˆ',
  '¿º' => 'ì˜Œ',
  '¿»' => 'ì˜�',
  '¿¼' => 'ì˜˜',
  '¿½' => 'ì˜™',
  '¿¾' => 'ì˜›',
  '¿¿' => 'ì˜œ',
  '¿À' => 'ì˜¤',
  '¿Á' => 'ì˜¥',
  '¿Â' => 'ì˜¨',
  '¿Ã' => 'ì˜¬',
  '¿Ä' => 'ì˜­',
  '¿Å' => 'ì˜®',
  '¿Æ' => 'ì˜°',
  '¿Ç' => 'ì˜³',
  '¿È' => 'ì˜´',
  '¿É' => 'ì˜µ',
  '¿Ê' => 'ì˜·',
  '¿Ë' => 'ì˜¹',
  '¿Ì' => 'ì˜»',
  '¿Í' => 'ì™€',
  '¿Î' => 'ì™�',
  '¿Ï' => 'ì™„',
  '¿Ð' => 'ì™ˆ',
  '¿Ñ' => 'ì™�',
  '¿Ò' => 'ì™‘',
  '¿Ó' => 'ì™“',
  '¿Ô' => 'ì™”',
  '¿Õ' => 'ì™•',
  '¿Ö' => 'ì™œ',
  '¿×' => 'ì™�',
  '¿Ø' => 'ì™ ',
  '¿Ù' => 'ì™¬',
  '¿Ú' => 'ì™¯',
  '¿Û' => 'ì™±',
  '¿Ü' => 'ì™¸',
  '¿Ý' => 'ì™¹',
  '¿Þ' => 'ì™¼',
  '¿ß' => 'ìš€',
  '¿à' => 'ìšˆ',
  '¿á' => 'ìš‰',
  '¿â' => 'ìš‹',
  '¿ã' => 'ìš�',
  '¿ä' => 'ìš”',
  '¿å' => 'ìš•',
  '¿æ' => 'ìš˜',
  '¿ç' => 'ìšœ',
  '¿è' => 'ìš¤',
  '¿é' => 'ìš¥',
  '¿ê' => 'ìš§',
  '¿ë' => 'ìš©',
  '¿ì' => 'ìš°',
  '¿í' => 'ìš±',
  '¿î' => 'ìš´',
  '¿ï' => 'ìš¸',
  '¿ð' => 'ìš¹',
  '¿ñ' => 'ìšº',
  '¿ò' => 'ì›€',
  '¿ó' => 'ì›�',
  '¿ô' => 'ì›ƒ',
  '¿õ' => 'ì›…',
  '¿ö' => 'ì›Œ',
  '¿÷' => 'ì›�',
  '¿ø' => 'ì›�',
  '¿ù' => 'ì›”',
  '¿ú' => 'ì›œ',
  '¿û' => 'ì›�',
  '¿ü' => 'ì› ',
  '¿ý' => 'ì›¡',
  '¿þ' => 'ì›¨',
  'ÀA' => 'í“¾',
  'ÀB' => 'í“¿',
  'ÀC' => 'í”€',
  'ÀD' => 'í”�',
  'ÀE' => 'í”‚',
  'ÀF' => 'í”ƒ',
  'ÀG' => 'í”…',
  'ÀH' => 'í”†',
  'ÀI' => 'í”‡',
  'ÀJ' => 'í”‰',
  'ÀK' => 'í”Š',
  'ÀL' => 'í”‹',
  'ÀM' => 'í”�',
  'ÀN' => 'í”Ž',
  'ÀO' => 'í”�',
  'ÀP' => 'í”�',
  'ÀQ' => 'í”‘',
  'ÀR' => 'í”’',
  'ÀS' => 'í”“',
  'ÀT' => 'í”–',
  'ÀU' => 'í”˜',
  'ÀV' => 'í”™',
  'ÀW' => 'í”š',
  'ÀX' => 'í”›',
  'ÀY' => 'í”œ',
  'ÀZ' => 'í”�',
  'Àa' => 'í”ž',
  'Àb' => 'í”Ÿ',
  'Àc' => 'í” ',
  'Àd' => 'í”¡',
  'Àe' => 'í”¢',
  'Àf' => 'í”£',
  'Àg' => 'í”¤',
  'Àh' => 'í”¥',
  'Ài' => 'í”¦',
  'Àj' => 'í”§',
  'Àk' => 'í”¨',
  'Àl' => 'í”©',
  'Àm' => 'í”ª',
  'Àn' => 'í”«',
  'Ào' => 'í”¬',
  'Àp' => 'í”­',
  'Àq' => 'í”®',
  'Àr' => 'í”¯',
  'Às' => 'í”°',
  'Àt' => 'í”±',
  'Àu' => 'í”²',
  'Àv' => 'í”³',
  'Àw' => 'í”´',
  'Àx' => 'í”µ',
  'Ày' => 'í”¶',
  'Àz' => 'í”·',
  'À�' => 'í”¸',
  'À‚' => 'í”¹',
  'Àƒ' => 'í”º',
  'À„' => 'í”»',
  'À…' => 'í”¾',
  'À†' => 'í”¿',
  'À‡' => 'í•�',
  'Àˆ' => 'í•‚',
  'À‰' => 'í•ƒ',
  'ÀŠ' => 'í•…',
  'À‹' => 'í•†',
  'ÀŒ' => 'í•‡',
  'À�' => 'í•ˆ',
  'ÀŽ' => 'í•‰',
  'À�' => 'í•Š',
  'À�' => 'í•‹',
  'À‘' => 'í•Ž',
  'À’' => 'í•�',
  'À“' => 'í•’',
  'À”' => 'í•“',
  'À•' => 'í•”',
  'À–' => 'í••',
  'À—' => 'í•–',
  'À˜' => 'í•—',
  'À™' => 'í•š',
  'Àš' => 'í•›',
  'À›' => 'í•�',
  'Àœ' => 'í•ž',
  'À�' => 'í•Ÿ',
  'Àž' => 'í•¡',
  'ÀŸ' => 'í•¢',
  'À ' => 'í•£',
  'À¡' => 'ì›©',
  'À¢' => 'ì›¬',
  'À£' => 'ì›°',
  'À¤' => 'ì›¸',
  'À¥' => 'ì›¹',
  'À¦' => 'ì›½',
  'À§' => 'ìœ„',
  'À¨' => 'ìœ…',
  'À©' => 'ìœˆ',
  'Àª' => 'ìœŒ',
  'À«' => 'ìœ”',
  'À¬' => 'ìœ•',
  'À­' => 'ìœ—',
  'À®' => 'ìœ™',
  'À¯' => 'ìœ ',
  'À°' => 'ìœ¡',
  'À±' => 'ìœ¤',
  'À²' => 'ìœ¨',
  'À³' => 'ìœ°',
  'À´' => 'ìœ±',
  'Àµ' => 'ìœ³',
  'À¶' => 'ìœµ',
  'À·' => 'ìœ·',
  'À¸' => 'ìœ¼',
  'À¹' => 'ìœ½',
  'Àº' => 'ì�€',
  'À»' => 'ì�„',
  'À¼' => 'ì�Š',
  'À½' => 'ì�Œ',
  'À¾' => 'ì��',
  'À¿' => 'ì��',
  'ÀÀ' => 'ì�‘',
  'ÀÁ' => 'ì�’',
  'ÀÂ' => 'ì�“',
  'ÀÃ' => 'ì�”',
  'ÀÄ' => 'ì�•',
  'ÀÅ' => 'ì�–',
  'ÀÆ' => 'ì�—',
  'ÀÇ' => 'ì�˜',
  'ÀÈ' => 'ì�œ',
  'ÀÉ' => 'ì� ',
  'ÀÊ' => 'ì�¨',
  'ÀË' => 'ì�«',
  'ÀÌ' => 'ì�´',
  'ÀÍ' => 'ì�µ',
  'ÀÎ' => 'ì�¸',
  'ÀÏ' => 'ì�¼',
  'ÀÐ' => 'ì�½',
  'ÀÑ' => 'ì�¾',
  'ÀÒ' => 'ìžƒ',
  'ÀÓ' => 'ìž„',
  'ÀÔ' => 'ìž…',
  'ÀÕ' => 'ìž‡',
  'ÀÖ' => 'ìžˆ',
  'À×' => 'ìž‰',
  'ÀØ' => 'ìžŠ',
  'ÀÙ' => 'ìžŽ',
  'ÀÚ' => 'ìž�',
  'ÀÛ' => 'ìž‘',
  'ÀÜ' => 'ìž”',
  'ÀÝ' => 'ìž–',
  'ÀÞ' => 'ìž—',
  'Àß' => 'ìž˜',
  'Àà' => 'ìžš',
  'Àá' => 'ìž ',
  'Àâ' => 'ìž¡',
  'Àã' => 'ìž£',
  'Àä' => 'ìž¤',
  'Àå' => 'ìž¥',
  'Àæ' => 'ìž¦',
  'Àç' => 'ìž¬',
  'Àè' => 'ìž­',
  'Àé' => 'ìž°',
  'Àê' => 'ìž´',
  'Àë' => 'ìž¼',
  'Àì' => 'ìž½',
  'Àí' => 'ìž¿',
  'Àî' => 'ìŸ€',
  'Àï' => 'ìŸ�',
  'Àð' => 'ìŸˆ',
  'Àñ' => 'ìŸ‰',
  'Àò' => 'ìŸŒ',
  'Àó' => 'ìŸŽ',
  'Àô' => 'ìŸ�',
  'Àõ' => 'ìŸ˜',
  'Àö' => 'ìŸ�',
  'À÷' => 'ìŸ¤',
  'Àø' => 'ìŸ¨',
  'Àù' => 'ìŸ¬',
  'Àú' => 'ì €',
  'Àû' => 'ì �',
  'Àü' => 'ì „',
  'Àý' => 'ì ˆ',
  'Àþ' => 'ì Š',
  'ÁA' => 'í•¤',
  'ÁB' => 'í•¦',
  'ÁC' => 'í•§',
  'ÁD' => 'í•ª',
  'ÁE' => 'í•¬',
  'ÁF' => 'í•®',
  'ÁG' => 'í•¯',
  'ÁH' => 'í•°',
  'ÁI' => 'í•±',
  'ÁJ' => 'í•²',
  'ÁK' => 'í•³',
  'ÁL' => 'í•¶',
  'ÁM' => 'í•·',
  'ÁN' => 'í•¹',
  'ÁO' => 'í•º',
  'ÁP' => 'í•»',
  'ÁQ' => 'í•½',
  'ÁR' => 'í•¾',
  'ÁS' => 'í•¿',
  'ÁT' => 'í–€',
  'ÁU' => 'í–�',
  'ÁV' => 'í–‚',
  'ÁW' => 'í–ƒ',
  'ÁX' => 'í–†',
  'ÁY' => 'í–Š',
  'ÁZ' => 'í–‹',
  'Áa' => 'í–Œ',
  'Áb' => 'í–�',
  'Ác' => 'í–Ž',
  'Ád' => 'í–�',
  'Áe' => 'í–‘',
  'Áf' => 'í–’',
  'Ág' => 'í–“',
  'Áh' => 'í–”',
  'Ái' => 'í–•',
  'Áj' => 'í––',
  'Ák' => 'í–—',
  'Ál' => 'í–˜',
  'Ám' => 'í–™',
  'Án' => 'í–š',
  'Áo' => 'í–›',
  'Áp' => 'í–œ',
  'Áq' => 'í–�',
  'Ár' => 'í–ž',
  'Ás' => 'í–Ÿ',
  'Át' => 'í– ',
  'Áu' => 'í–¡',
  'Áv' => 'í–¢',
  'Áw' => 'í–£',
  'Áx' => 'í–¤',
  'Áy' => 'í–¦',
  'Áz' => 'í–§',
  'Á�' => 'í–¨',
  'Á‚' => 'í–©',
  'Áƒ' => 'í–ª',
  'Á„' => 'í–«',
  'Á…' => 'í–¬',
  'Á†' => 'í–­',
  'Á‡' => 'í–®',
  'Áˆ' => 'í–¯',
  'Á‰' => 'í–°',
  'ÁŠ' => 'í–±',
  'Á‹' => 'í–²',
  'ÁŒ' => 'í–³',
  'Á�' => 'í–´',
  'ÁŽ' => 'í–µ',
  'Á�' => 'í–¶',
  'Á�' => 'í–·',
  'Á‘' => 'í–¸',
  'Á’' => 'í–¹',
  'Á“' => 'í–º',
  'Á”' => 'í–»',
  'Á•' => 'í–¼',
  'Á–' => 'í–½',
  'Á—' => 'í–¾',
  'Á˜' => 'í–¿',
  'Á™' => 'í—€',
  'Áš' => 'í—�',
  'Á›' => 'í—‚',
  'Áœ' => 'í—ƒ',
  'Á�' => 'í—„',
  'Áž' => 'í—…',
  'ÁŸ' => 'í—†',
  'Á ' => 'í—‡',
  'Á¡' => 'ì �',
  'Á¢' => 'ì ‘',
  'Á£' => 'ì “',
  'Á¤' => 'ì •',
  'Á¥' => 'ì –',
  'Á¦' => 'ì œ',
  'Á§' => 'ì �',
  'Á¨' => 'ì  ',
  'Á©' => 'ì ¤',
  'Áª' => 'ì ¬',
  'Á«' => 'ì ­',
  'Á¬' => 'ì ¯',
  'Á­' => 'ì ±',
  'Á®' => 'ì ¸',
  'Á¯' => 'ì ¼',
  'Á°' => 'ì¡€',
  'Á±' => 'ì¡ˆ',
  'Á²' => 'ì¡‰',
  'Á³' => 'ì¡Œ',
  'Á´' => 'ì¡�',
  'Áµ' => 'ì¡”',
  'Á¶' => 'ì¡°',
  'Á·' => 'ì¡±',
  'Á¸' => 'ì¡´',
  'Á¹' => 'ì¡¸',
  'Áº' => 'ì¡º',
  'Á»' => 'ì¢€',
  'Á¼' => 'ì¢�',
  'Á½' => 'ì¢ƒ',
  'Á¾' => 'ì¢…',
  'Á¿' => 'ì¢†',
  'ÁÀ' => 'ì¢‡',
  'ÁÁ' => 'ì¢‹',
  'ÁÂ' => 'ì¢Œ',
  'ÁÃ' => 'ì¢�',
  'ÁÄ' => 'ì¢”',
  'ÁÅ' => 'ì¢�',
  'ÁÆ' => 'ì¢Ÿ',
  'ÁÇ' => 'ì¢¡',
  'ÁÈ' => 'ì¢¨',
  'ÁÉ' => 'ì¢¼',
  'ÁÊ' => 'ì¢½',
  'ÁË' => 'ì£„',
  'ÁÌ' => 'ì£ˆ',
  'ÁÍ' => 'ì£Œ',
  'ÁÎ' => 'ì£”',
  'ÁÏ' => 'ì£•',
  'ÁÐ' => 'ì£—',
  'ÁÑ' => 'ì£™',
  'ÁÒ' => 'ì£ ',
  'ÁÓ' => 'ì£¡',
  'ÁÔ' => 'ì£¤',
  'ÁÕ' => 'ì£µ',
  'ÁÖ' => 'ì£¼',
  'Á×' => 'ì£½',
  'ÁØ' => 'ì¤€',
  'ÁÙ' => 'ì¤„',
  'ÁÚ' => 'ì¤…',
  'ÁÛ' => 'ì¤†',
  'ÁÜ' => 'ì¤Œ',
  'ÁÝ' => 'ì¤�',
  'ÁÞ' => 'ì¤�',
  'Áß' => 'ì¤‘',
  'Áà' => 'ì¤˜',
  'Áá' => 'ì¤¬',
  'Áâ' => 'ì¤´',
  'Áã' => 'ì¥�',
  'Áä' => 'ì¥‘',
  'Áå' => 'ì¥”',
  'Áæ' => 'ì¥˜',
  'Áç' => 'ì¥ ',
  'Áè' => 'ì¥¡',
  'Áé' => 'ì¥£',
  'Áê' => 'ì¥¬',
  'Áë' => 'ì¥°',
  'Áì' => 'ì¥´',
  'Áí' => 'ì¥¼',
  'Áî' => 'ì¦ˆ',
  'Áï' => 'ì¦‰',
  'Áð' => 'ì¦Œ',
  'Áñ' => 'ì¦�',
  'Áò' => 'ì¦˜',
  'Áó' => 'ì¦™',
  'Áô' => 'ì¦›',
  'Áõ' => 'ì¦�',
  'Áö' => 'ì§€',
  'Á÷' => 'ì§�',
  'Áø' => 'ì§„',
  'Áù' => 'ì§‡',
  'Áú' => 'ì§ˆ',
  'Áû' => 'ì§Š',
  'Áü' => 'ì§�',
  'Áý' => 'ì§‘',
  'Áþ' => 'ì§“',
  'ÂA' => 'í—Š',
  'ÂB' => 'í—‹',
  'ÂC' => 'í—�',
  'ÂD' => 'í—Ž',
  'ÂE' => 'í—�',
  'ÂF' => 'í—‘',
  'ÂG' => 'í—“',
  'ÂH' => 'í—”',
  'ÂI' => 'í—•',
  'ÂJ' => 'í—–',
  'ÂK' => 'í——',
  'ÂL' => 'í—š',
  'ÂM' => 'í—œ',
  'ÂN' => 'í—ž',
  'ÂO' => 'í—Ÿ',
  'ÂP' => 'í— ',
  'ÂQ' => 'í—¡',
  'ÂR' => 'í—¢',
  'ÂS' => 'í—£',
  'ÂT' => 'í—¦',
  'ÂU' => 'í—§',
  'ÂV' => 'í—©',
  'ÂW' => 'í—ª',
  'ÂX' => 'í—«',
  'ÂY' => 'í—­',
  'ÂZ' => 'í—®',
  'Âa' => 'í—¯',
  'Âb' => 'í—°',
  'Âc' => 'í—±',
  'Âd' => 'í—²',
  'Âe' => 'í—³',
  'Âf' => 'í—¶',
  'Âg' => 'í—¸',
  'Âh' => 'í—º',
  'Âi' => 'í—»',
  'Âj' => 'í—¼',
  'Âk' => 'í—½',
  'Âl' => 'í—¾',
  'Âm' => 'í—¿',
  'Ân' => 'í˜‚',
  'Âo' => 'í˜ƒ',
  'Âp' => 'í˜…',
  'Âq' => 'í˜†',
  'Âr' => 'í˜‡',
  'Âs' => 'í˜‰',
  'Ât' => 'í˜Š',
  'Âu' => 'í˜‹',
  'Âv' => 'í˜Œ',
  'Âw' => 'í˜�',
  'Âx' => 'í˜Ž',
  'Ây' => 'í˜�',
  'Âz' => 'í˜’',
  'Â�' => 'í˜–',
  'Â‚' => 'í˜—',
  'Âƒ' => 'í˜˜',
  'Â„' => 'í˜™',
  'Â…' => 'í˜š',
  'Â†' => 'í˜›',
  'Â‡' => 'í˜�',
  'Âˆ' => 'í˜ž',
  'Â‰' => 'í˜Ÿ',
  'ÂŠ' => 'í˜¡',
  'Â‹' => 'í˜¢',
  'ÂŒ' => 'í˜£',
  'Â�' => 'í˜¥',
  'ÂŽ' => 'í˜¦',
  'Â�' => 'í˜§',
  'Â�' => 'í˜¨',
  'Â‘' => 'í˜©',
  'Â’' => 'í˜ª',
  'Â“' => 'í˜«',
  'Â”' => 'í˜¬',
  'Â•' => 'í˜®',
  'Â–' => 'í˜¯',
  'Â—' => 'í˜°',
  'Â˜' => 'í˜±',
  'Â™' => 'í˜²',
  'Âš' => 'í˜³',
  'Â›' => 'í˜´',
  'Âœ' => 'í˜µ',
  'Â�' => 'í˜¶',
  'Âž' => 'í˜·',
  'ÂŸ' => 'í˜º',
  'Â ' => 'í˜»',
  'Â¡' => 'ì§•',
  'Â¢' => 'ì§–',
  'Â£' => 'ì§™',
  'Â¤' => 'ì§š',
  'Â¥' => 'ì§œ',
  'Â¦' => 'ì§�',
  'Â§' => 'ì§ ',
  'Â¨' => 'ì§¢',
  'Â©' => 'ì§¤',
  'Âª' => 'ì§§',
  'Â«' => 'ì§¬',
  'Â¬' => 'ì§­',
  'Â­' => 'ì§¯',
  'Â®' => 'ì§°',
  'Â¯' => 'ì§±',
  'Â°' => 'ì§¸',
  'Â±' => 'ì§¹',
  'Â²' => 'ì§¼',
  'Â³' => 'ì¨€',
  'Â´' => 'ì¨ˆ',
  'Âµ' => 'ì¨‰',
  'Â¶' => 'ì¨‹',
  'Â·' => 'ì¨Œ',
  'Â¸' => 'ì¨�',
  'Â¹' => 'ì¨”',
  'Âº' => 'ì¨˜',
  'Â»' => 'ì¨©',
  'Â¼' => 'ì©Œ',
  'Â½' => 'ì©�',
  'Â¾' => 'ì©�',
  'Â¿' => 'ì©”',
  'ÂÀ' => 'ì©œ',
  'ÂÁ' => 'ì©�',
  'ÂÂ' => 'ì©Ÿ',
  'ÂÃ' => 'ì© ',
  'ÂÄ' => 'ì©¡',
  'ÂÅ' => 'ì©¨',
  'ÂÆ' => 'ì©½',
  'ÂÇ' => 'ìª„',
  'ÂÈ' => 'ìª˜',
  'ÂÉ' => 'ìª¼',
  'ÂÊ' => 'ìª½',
  'ÂË' => 'ì«€',
  'ÂÌ' => 'ì«„',
  'ÂÍ' => 'ì«Œ',
  'ÂÎ' => 'ì«�',
  'ÂÏ' => 'ì«�',
  'ÂÐ' => 'ì«‘',
  'ÂÑ' => 'ì«“',
  'ÂÒ' => 'ì«˜',
  'ÂÓ' => 'ì«™',
  'ÂÔ' => 'ì« ',
  'ÂÕ' => 'ì«¬',
  'ÂÖ' => 'ì«´',
  'Â×' => 'ì¬ˆ',
  'ÂØ' => 'ì¬�',
  'ÂÙ' => 'ì¬”',
  'ÂÚ' => 'ì¬˜',
  'ÂÛ' => 'ì¬ ',
  'ÂÜ' => 'ì¬¡',
  'ÂÝ' => 'ì­�',
  'ÂÞ' => 'ì­ˆ',
  'Âß' => 'ì­‰',
  'Âà' => 'ì­Œ',
  'Âá' => 'ì­�',
  'Ââ' => 'ì­˜',
  'Âã' => 'ì­™',
  'Âä' => 'ì­�',
  'Âå' => 'ì­¤',
  'Âæ' => 'ì­¸',
  'Âç' => 'ì­¹',
  'Âè' => 'ì®œ',
  'Âé' => 'ì®¸',
  'Âê' => 'ì¯”',
  'Âë' => 'ì¯¤',
  'Âì' => 'ì¯§',
  'Âí' => 'ì¯©',
  'Âî' => 'ì°Œ',
  'Âï' => 'ì°�',
  'Âð' => 'ì°�',
  'Âñ' => 'ì°”',
  'Âò' => 'ì°œ',
  'Âó' => 'ì°�',
  'Âô' => 'ì°¡',
  'Âõ' => 'ì°¢',
  'Âö' => 'ì°§',
  'Â÷' => 'ì°¨',
  'Âø' => 'ì°©',
  'Âù' => 'ì°¬',
  'Âú' => 'ì°®',
  'Âû' => 'ì°°',
  'Âü' => 'ì°¸',
  'Âý' => 'ì°¹',
  'Âþ' => 'ì°»',
  'ÃA' => 'í˜½',
  'ÃB' => 'í˜¾',
  'ÃC' => 'í˜¿',
  'ÃD' => 'í™�',
  'ÃE' => 'í™‚',
  'ÃF' => 'í™ƒ',
  'ÃG' => 'í™„',
  'ÃH' => 'í™†',
  'ÃI' => 'í™‡',
  'ÃJ' => 'í™Š',
  'ÃK' => 'í™Œ',
  'ÃL' => 'í™Ž',
  'ÃM' => 'í™�',
  'ÃN' => 'í™�',
  'ÃO' => 'í™’',
  'ÃP' => 'í™“',
  'ÃQ' => 'í™–',
  'ÃR' => 'í™—',
  'ÃS' => 'í™™',
  'ÃT' => 'í™š',
  'ÃU' => 'í™›',
  'ÃV' => 'í™�',
  'ÃW' => 'í™ž',
  'ÃX' => 'í™Ÿ',
  'ÃY' => 'í™ ',
  'ÃZ' => 'í™¡',
  'Ãa' => 'í™¢',
  'Ãb' => 'í™£',
  'Ãc' => 'í™¤',
  'Ãd' => 'í™¥',
  'Ãe' => 'í™¦',
  'Ãf' => 'í™¨',
  'Ãg' => 'í™ª',
  'Ãh' => 'í™«',
  'Ãi' => 'í™¬',
  'Ãj' => 'í™­',
  'Ãk' => 'í™®',
  'Ãl' => 'í™¯',
  'Ãm' => 'í™²',
  'Ãn' => 'í™³',
  'Ão' => 'í™µ',
  'Ãp' => 'í™¶',
  'Ãq' => 'í™·',
  'Ãr' => 'í™¸',
  'Ãs' => 'í™¹',
  'Ãt' => 'í™º',
  'Ãu' => 'í™»',
  'Ãv' => 'í™¼',
  'Ãw' => 'í™½',
  'Ãx' => 'í™¾',
  'Ãy' => 'í™¿',
  'Ãz' => 'íš€',
  'Ã�' => 'íš�',
  'Ã‚' => 'íš‚',
  'Ãƒ' => 'íš„',
  'Ã„' => 'íš†',
  'Ã…' => 'íš‡',
  'Ã†' => 'íšˆ',
  'Ã‡' => 'íš‰',
  'Ãˆ' => 'íšŠ',
  'Ã‰' => 'íš‹',
  'ÃŠ' => 'íšŽ',
  'Ã‹' => 'íš�',
  'ÃŒ' => 'íš‘',
  'Ã�' => 'íš’',
  'ÃŽ' => 'íš“',
  'Ã�' => 'íš•',
  'Ã�' => 'íš–',
  'Ã‘' => 'íš—',
  'Ã’' => 'íš˜',
  'Ã“' => 'íš™',
  'Ã”' => 'íšš',
  'Ã•' => 'íš›',
  'Ã–' => 'íšœ',
  'Ã—' => 'íšž',
  'Ã˜' => 'íš ',
  'Ã™' => 'íš¢',
  'Ãš' => 'íš£',
  'Ã›' => 'íš¤',
  'Ãœ' => 'íš¥',
  'Ã�' => 'íš¦',
  'Ãž' => 'íš§',
  'ÃŸ' => 'íš©',
  'Ã ' => 'íšª',
  'Ã¡' => 'ì°¼',
  'Ã¢' => 'ì°½',
  'Ã£' => 'ì°¾',
  'Ã¤' => 'ì±„',
  'Ã¥' => 'ì±…',
  'Ã¦' => 'ì±ˆ',
  'Ã§' => 'ì±Œ',
  'Ã¨' => 'ì±”',
  'Ã©' => 'ì±•',
  'Ãª' => 'ì±—',
  'Ã«' => 'ì±˜',
  'Ã¬' => 'ì±™',
  'Ã­' => 'ì± ',
  'Ã®' => 'ì±¤',
  'Ã¯' => 'ì±¦',
  'Ã°' => 'ì±¨',
  'Ã±' => 'ì±°',
  'Ã²' => 'ì±µ',
  'Ã³' => 'ì²˜',
  'Ã´' => 'ì²™',
  'Ãµ' => 'ì²œ',
  'Ã¶' => 'ì² ',
  'Ã·' => 'ì²¨',
  'Ã¸' => 'ì²©',
  'Ã¹' => 'ì²«',
  'Ãº' => 'ì²¬',
  'Ã»' => 'ì²­',
  'Ã¼' => 'ì²´',
  'Ã½' => 'ì²µ',
  'Ã¾' => 'ì²¸',
  'Ã¿' => 'ì²¼',
  'ÃÀ' => 'ì³„',
  'ÃÁ' => 'ì³…',
  'ÃÂ' => 'ì³‡',
  'ÃÃ' => 'ì³‰',
  'ÃÄ' => 'ì³�',
  'ÃÅ' => 'ì³”',
  'ÃÆ' => 'ì³¤',
  'ÃÇ' => 'ì³¬',
  'ÃÈ' => 'ì³°',
  'ÃÉ' => 'ì´�',
  'ÃÊ' => 'ì´ˆ',
  'ÃË' => 'ì´‰',
  'ÃÌ' => 'ì´Œ',
  'ÃÍ' => 'ì´�',
  'ÃÎ' => 'ì´˜',
  'ÃÏ' => 'ì´™',
  'ÃÐ' => 'ì´›',
  'ÃÑ' => 'ì´�',
  'ÃÒ' => 'ì´¤',
  'ÃÓ' => 'ì´¨',
  'ÃÔ' => 'ì´¬',
  'ÃÕ' => 'ì´¹',
  'ÃÖ' => 'ìµœ',
  'Ã×' => 'ìµ ',
  'ÃØ' => 'ìµ¤',
  'ÃÙ' => 'ìµ¬',
  'ÃÚ' => 'ìµ­',
  'ÃÛ' => 'ìµ¯',
  'ÃÜ' => 'ìµ±',
  'ÃÝ' => 'ìµ¸',
  'ÃÞ' => 'ì¶ˆ',
  'Ãß' => 'ì¶”',
  'Ãà' => 'ì¶•',
  'Ãá' => 'ì¶˜',
  'Ãâ' => 'ì¶œ',
  'Ãã' => 'ì¶¤',
  'Ãä' => 'ì¶¥',
  'Ãå' => 'ì¶§',
  'Ãæ' => 'ì¶©',
  'Ãç' => 'ì¶°',
  'Ãè' => 'ì·„',
  'Ãé' => 'ì·Œ',
  'Ãê' => 'ì·�',
  'Ãë' => 'ì·¨',
  'Ãì' => 'ì·¬',
  'Ãí' => 'ì·°',
  'Ãî' => 'ì·¸',
  'Ãï' => 'ì·¹',
  'Ãð' => 'ì·»',
  'Ãñ' => 'ì·½',
  'Ãò' => 'ì¸„',
  'Ãó' => 'ì¸ˆ',
  'Ãô' => 'ì¸Œ',
  'Ãõ' => 'ì¸”',
  'Ãö' => 'ì¸™',
  'Ã÷' => 'ì¸ ',
  'Ãø' => 'ì¸¡',
  'Ãù' => 'ì¸¤',
  'Ãú' => 'ì¸¨',
  'Ãû' => 'ì¸°',
  'Ãü' => 'ì¸±',
  'Ãý' => 'ì¸³',
  'Ãþ' => 'ì¸µ',
  'ÄA' => 'íš«',
  'ÄB' => 'íš­',
  'ÄC' => 'íš®',
  'ÄD' => 'íš¯',
  'ÄE' => 'íš±',
  'ÄF' => 'íš²',
  'ÄG' => 'íš³',
  'ÄH' => 'íš´',
  'ÄI' => 'íšµ',
  'ÄJ' => 'íš¶',
  'ÄK' => 'íš·',
  'ÄL' => 'íš¸',
  'ÄM' => 'íšº',
  'ÄN' => 'íš¼',
  'ÄO' => 'íš½',
  'ÄP' => 'íš¾',
  'ÄQ' => 'íš¿',
  'ÄR' => 'í›€',
  'ÄS' => 'í›�',
  'ÄT' => 'í›‚',
  'ÄU' => 'í›ƒ',
  'ÄV' => 'í›†',
  'ÄW' => 'í›‡',
  'ÄX' => 'í›‰',
  'ÄY' => 'í›Š',
  'ÄZ' => 'í›‹',
  'Äa' => 'í›�',
  'Äb' => 'í›Ž',
  'Äc' => 'í›�',
  'Äd' => 'í›�',
  'Äe' => 'í›’',
  'Äf' => 'í›“',
  'Äg' => 'í›•',
  'Äh' => 'í›–',
  'Äi' => 'í›˜',
  'Äj' => 'í›š',
  'Äk' => 'í››',
  'Äl' => 'í›œ',
  'Äm' => 'í›�',
  'Än' => 'í›ž',
  'Äo' => 'í›Ÿ',
  'Äp' => 'í›¡',
  'Äq' => 'í›¢',
  'Är' => 'í›£',
  'Äs' => 'í›¥',
  'Ät' => 'í›¦',
  'Äu' => 'í›§',
  'Äv' => 'í›©',
  'Äw' => 'í›ª',
  'Äx' => 'í›«',
  'Äy' => 'í›¬',
  'Äz' => 'í›­',
  'Ä�' => 'í›®',
  'Ä‚' => 'í›¯',
  'Äƒ' => 'í›±',
  'Ä„' => 'í›²',
  'Ä…' => 'í›³',
  'Ä†' => 'í›´',
  'Ä‡' => 'í›¶',
  'Äˆ' => 'í›·',
  'Ä‰' => 'í›¸',
  'ÄŠ' => 'í›¹',
  'Ä‹' => 'í›º',
  'ÄŒ' => 'í›»',
  'Ä�' => 'í›¾',
  'ÄŽ' => 'í›¿',
  'Ä�' => 'íœ�',
  'Ä�' => 'íœ‚',
  'Ä‘' => 'íœƒ',
  'Ä’' => 'íœ…',
  'Ä“' => 'íœ†',
  'Ä”' => 'íœ‡',
  'Ä•' => 'íœˆ',
  'Ä–' => 'íœ‰',
  'Ä—' => 'íœŠ',
  'Ä˜' => 'íœ‹',
  'Ä™' => 'íœŒ',
  'Äš' => 'íœ�',
  'Ä›' => 'íœŽ',
  'Äœ' => 'íœ�',
  'Ä�' => 'íœ�',
  'Äž' => 'íœ’',
  'ÄŸ' => 'íœ“',
  'Ä ' => 'íœ”',
  'Ä¡' => 'ì¹˜',
  'Ä¢' => 'ì¹™',
  'Ä£' => 'ì¹œ',
  'Ä¤' => 'ì¹Ÿ',
  'Ä¥' => 'ì¹ ',
  'Ä¦' => 'ì¹¡',
  'Ä§' => 'ì¹¨',
  'Ä¨' => 'ì¹©',
  'Ä©' => 'ì¹«',
  'Äª' => 'ì¹­',
  'Ä«' => 'ì¹´',
  'Ä¬' => 'ì¹µ',
  'Ä­' => 'ì¹¸',
  'Ä®' => 'ì¹¼',
  'Ä¯' => 'ìº„',
  'Ä°' => 'ìº…',
  'Ä±' => 'ìº‡',
  'Ä²' => 'ìº‰',
  'Ä³' => 'ìº�',
  'Ä´' => 'ìº‘',
  'Äµ' => 'ìº”',
  'Ä¶' => 'ìº˜',
  'Ä·' => 'ìº ',
  'Ä¸' => 'ìº¡',
  'Ä¹' => 'ìº£',
  'Äº' => 'ìº¤',
  'Ä»' => 'ìº¥',
  'Ä¼' => 'ìº¬',
  'Ä½' => 'ìº­',
  'Ä¾' => 'ì»�',
  'Ä¿' => 'ì»¤',
  'ÄÀ' => 'ì»¥',
  'ÄÁ' => 'ì»¨',
  'ÄÂ' => 'ì»«',
  'ÄÃ' => 'ì»¬',
  'ÄÄ' => 'ì»´',
  'ÄÅ' => 'ì»µ',
  'ÄÆ' => 'ì»·',
  'ÄÇ' => 'ì»¸',
  'ÄÈ' => 'ì»¹',
  'ÄÉ' => 'ì¼€',
  'ÄÊ' => 'ì¼�',
  'ÄË' => 'ì¼„',
  'ÄÌ' => 'ì¼ˆ',
  'ÄÍ' => 'ì¼�',
  'ÄÎ' => 'ì¼‘',
  'ÄÏ' => 'ì¼“',
  'ÄÐ' => 'ì¼•',
  'ÄÑ' => 'ì¼œ',
  'ÄÒ' => 'ì¼ ',
  'ÄÓ' => 'ì¼¤',
  'ÄÔ' => 'ì¼¬',
  'ÄÕ' => 'ì¼­',
  'ÄÖ' => 'ì¼¯',
  'Ä×' => 'ì¼°',
  'ÄØ' => 'ì¼±',
  'ÄÙ' => 'ì¼¸',
  'ÄÚ' => 'ì½”',
  'ÄÛ' => 'ì½•',
  'ÄÜ' => 'ì½˜',
  'ÄÝ' => 'ì½œ',
  'ÄÞ' => 'ì½¤',
  'Äß' => 'ì½¥',
  'Äà' => 'ì½§',
  'Äá' => 'ì½©',
  'Äâ' => 'ì½°',
  'Äã' => 'ì½±',
  'Ää' => 'ì½´',
  'Äå' => 'ì½¸',
  'Äæ' => 'ì¾€',
  'Äç' => 'ì¾…',
  'Äè' => 'ì¾Œ',
  'Äé' => 'ì¾¡',
  'Äê' => 'ì¾¨',
  'Äë' => 'ì¾°',
  'Äì' => 'ì¿„',
  'Äí' => 'ì¿ ',
  'Äî' => 'ì¿¡',
  'Äï' => 'ì¿¤',
  'Äð' => 'ì¿¨',
  'Äñ' => 'ì¿°',
  'Äò' => 'ì¿±',
  'Äó' => 'ì¿³',
  'Äô' => 'ì¿µ',
  'Äõ' => 'ì¿¼',
  'Äö' => 'í€€',
  'Ä÷' => 'í€„',
  'Äø' => 'í€‘',
  'Äù' => 'í€˜',
  'Äú' => 'í€­',
  'Äû' => 'í€´',
  'Äü' => 'í€µ',
  'Äý' => 'í€¸',
  'Äþ' => 'í€¼',
  'ÅA' => 'íœ•',
  'ÅB' => 'íœ–',
  'ÅC' => 'íœ—',
  'ÅD' => 'íœš',
  'ÅE' => 'íœ›',
  'ÅF' => 'íœ�',
  'ÅG' => 'íœž',
  'ÅH' => 'íœŸ',
  'ÅI' => 'íœ¡',
  'ÅJ' => 'íœ¢',
  'ÅK' => 'íœ£',
  'ÅL' => 'íœ¤',
  'ÅM' => 'íœ¥',
  'ÅN' => 'íœ¦',
  'ÅO' => 'íœ§',
  'ÅP' => 'íœª',
  'ÅQ' => 'íœ¬',
  'ÅR' => 'íœ®',
  'ÅS' => 'íœ¯',
  'ÅT' => 'íœ°',
  'ÅU' => 'íœ±',
  'ÅV' => 'íœ²',
  'ÅW' => 'íœ³',
  'ÅX' => 'íœ¶',
  'ÅY' => 'íœ·',
  'ÅZ' => 'íœ¹',
  'Åa' => 'íœº',
  'Åb' => 'íœ»',
  'Åc' => 'íœ½',
  'Åd' => 'íœ¾',
  'Åe' => 'íœ¿',
  'Åf' => 'í�€',
  'Åg' => 'í��',
  'Åh' => 'í�‚',
  'Åi' => 'í�ƒ',
  'Åj' => 'í�…',
  'Åk' => 'í�†',
  'Ål' => 'í�ˆ',
  'Åm' => 'í�Š',
  'Ån' => 'í�‹',
  'Åo' => 'í�Œ',
  'Åp' => 'í��',
  'Åq' => 'í�Ž',
  'År' => 'í��',
  'Ås' => 'í�’',
  'Åt' => 'í�“',
  'Åu' => 'í�•',
  'Åv' => 'í�š',
  'Åw' => 'í�›',
  'Åx' => 'í�œ',
  'Åy' => 'í��',
  'Åz' => 'í�ž',
  'Å�' => 'í�Ÿ',
  'Å‚' => 'í�¢',
  'Åƒ' => 'í�¤',
  'Å„' => 'í�¦',
  'Å…' => 'í�§',
  'Å†' => 'í�¨',
  'Å‡' => 'í�ª',
  'Åˆ' => 'í�«',
  'Å‰' => 'í�­',
  'ÅŠ' => 'í�®',
  'Å‹' => 'í�¯',
  'ÅŒ' => 'í�±',
  'Å�' => 'í�²',
  'ÅŽ' => 'í�³',
  'Å�' => 'í�µ',
  'Å�' => 'í�¶',
  'Å‘' => 'í�·',
  'Å’' => 'í�¸',
  'Å“' => 'í�¹',
  'Å”' => 'í�º',
  'Å•' => 'í�»',
  'Å–' => 'í�¾',
  'Å—' => 'í�¿',
  'Å˜' => 'íž€',
  'Å™' => 'íž‚',
  'Åš' => 'ížƒ',
  'Å›' => 'íž„',
  'Åœ' => 'íž…',
  'Å�' => 'íž†',
  'Åž' => 'íž‡',
  'ÅŸ' => 'ížŠ',
  'Å ' => 'íž‹',
  'Å¡' => 'í�„',
  'Å¢' => 'í�…',
  'Å£' => 'í�‡',
  'Å¤' => 'í�‰',
  'Å¥' => 'í��',
  'Å¦' => 'í�”',
  'Å§' => 'í�˜',
  'Å¨' => 'í� ',
  'Å©' => 'í�¬',
  'Åª' => 'í�­',
  'Å«' => 'í�°',
  'Å¬' => 'í�´',
  'Å­' => 'í�¼',
  'Å®' => 'í�½',
  'Å¯' => 'í‚�',
  'Å°' => 'í‚¤',
  'Å±' => 'í‚¥',
  'Å²' => 'í‚¨',
  'Å³' => 'í‚¬',
  'Å´' => 'í‚´',
  'Åµ' => 'í‚µ',
  'Å¶' => 'í‚·',
  'Å·' => 'í‚¹',
  'Å¸' => 'íƒ€',
  'Å¹' => 'íƒ�',
  'Åº' => 'íƒ„',
  'Å»' => 'íƒˆ',
  'Å¼' => 'íƒ‰',
  'Å½' => 'íƒ�',
  'Å¾' => 'íƒ‘',
  'Å¿' => 'íƒ“',
  'ÅÀ' => 'íƒ”',
  'ÅÁ' => 'íƒ•',
  'ÅÂ' => 'íƒœ',
  'ÅÃ' => 'íƒ�',
  'ÅÄ' => 'íƒ ',
  'ÅÅ' => 'íƒ¤',
  'ÅÆ' => 'íƒ¬',
  'ÅÇ' => 'íƒ­',
  'ÅÈ' => 'íƒ¯',
  'ÅÉ' => 'íƒ°',
  'ÅÊ' => 'íƒ±',
  'ÅË' => 'íƒ¸',
  'ÅÌ' => 'í„�',
  'ÅÍ' => 'í„°',
  'ÅÎ' => 'í„±',
  'ÅÏ' => 'í„´',
  'ÅÐ' => 'í„¸',
  'ÅÑ' => 'í„º',
  'ÅÒ' => 'í…€',
  'ÅÓ' => 'í…�',
  'ÅÔ' => 'í…ƒ',
  'ÅÕ' => 'í…„',
  'ÅÖ' => 'í……',
  'Å×' => 'í…Œ',
  'ÅØ' => 'í…�',
  'ÅÙ' => 'í…�',
  'ÅÚ' => 'í…”',
  'ÅÛ' => 'í…œ',
  'ÅÜ' => 'í…�',
  'ÅÝ' => 'í…Ÿ',
  'ÅÞ' => 'í…¡',
  'Åß' => 'í…¨',
  'Åà' => 'í…¬',
  'Åá' => 'í…¼',
  'Åâ' => 'í†„',
  'Åã' => 'í†ˆ',
  'Åä' => 'í† ',
  'Åå' => 'í†¡',
  'Åæ' => 'í†¤',
  'Åç' => 'í†¨',
  'Åè' => 'í†°',
  'Åé' => 'í†±',
  'Åê' => 'í†³',
  'Åë' => 'í†µ',
  'Åì' => 'í†º',
  'Åí' => 'í†¼',
  'Åî' => 'í‡€',
  'Åï' => 'í‡˜',
  'Åð' => 'í‡´',
  'Åñ' => 'í‡¸',
  'Åò' => 'íˆ‡',
  'Åó' => 'íˆ‰',
  'Åô' => 'íˆ�',
  'Åõ' => 'íˆ¬',
  'Åö' => 'íˆ­',
  'Å÷' => 'íˆ°',
  'Åø' => 'íˆ´',
  'Åù' => 'íˆ¼',
  'Åú' => 'íˆ½',
  'Åû' => 'íˆ¿',
  'Åü' => 'í‰�',
  'Åý' => 'í‰ˆ',
  'Åþ' => 'í‰œ',
  'ÆA' => 'íž�',
  'ÆB' => 'ížŽ',
  'ÆC' => 'íž�',
  'ÆD' => 'íž‘',
  'ÆE' => 'íž’',
  'ÆF' => 'íž“',
  'ÆG' => 'íž”',
  'ÆH' => 'íž•',
  'ÆI' => 'íž–',
  'ÆJ' => 'íž—',
  'ÆK' => 'ížš',
  'ÆL' => 'ížœ',
  'ÆM' => 'ížž',
  'ÆN' => 'ížŸ',
  'ÆO' => 'íž ',
  'ÆP' => 'íž¡',
  'ÆQ' => 'íž¢',
  'ÆR' => 'íž£',
  'Æ¡' => 'í‰¤',
  'Æ¢' => 'íŠ€',
  'Æ£' => 'íŠ�',
  'Æ¤' => 'íŠ„',
  'Æ¥' => 'íŠˆ',
  'Æ¦' => 'íŠ�',
  'Æ§' => 'íŠ‘',
  'Æ¨' => 'íŠ•',
  'Æ©' => 'íŠœ',
  'Æª' => 'íŠ ',
  'Æ«' => 'íŠ¤',
  'Æ¬' => 'íŠ¬',
  'Æ­' => 'íŠ±',
  'Æ®' => 'íŠ¸',
  'Æ¯' => 'íŠ¹',
  'Æ°' => 'íŠ¼',
  'Æ±' => 'íŠ¿',
  'Æ²' => 'í‹€',
  'Æ³' => 'í‹‚',
  'Æ´' => 'í‹ˆ',
  'Æµ' => 'í‹‰',
  'Æ¶' => 'í‹‹',
  'Æ·' => 'í‹”',
  'Æ¸' => 'í‹˜',
  'Æ¹' => 'í‹œ',
  'Æº' => 'í‹¤',
  'Æ»' => 'í‹¥',
  'Æ¼' => 'í‹°',
  'Æ½' => 'í‹±',
  'Æ¾' => 'í‹´',
  'Æ¿' => 'í‹¸',
  'ÆÀ' => 'íŒ€',
  'ÆÁ' => 'íŒ�',
  'ÆÂ' => 'íŒƒ',
  'ÆÃ' => 'íŒ…',
  'ÆÄ' => 'íŒŒ',
  'ÆÅ' => 'íŒ�',
  'ÆÆ' => 'íŒŽ',
  'ÆÇ' => 'íŒ�',
  'ÆÈ' => 'íŒ”',
  'ÆÉ' => 'íŒ–',
  'ÆÊ' => 'íŒœ',
  'ÆË' => 'íŒ�',
  'ÆÌ' => 'íŒŸ',
  'ÆÍ' => 'íŒ ',
  'ÆÎ' => 'íŒ¡',
  'ÆÏ' => 'íŒ¥',
  'ÆÐ' => 'íŒ¨',
  'ÆÑ' => 'íŒ©',
  'ÆÒ' => 'íŒ¬',
  'ÆÓ' => 'íŒ°',
  'ÆÔ' => 'íŒ¸',
  'ÆÕ' => 'íŒ¹',
  'ÆÖ' => 'íŒ»',
  'Æ×' => 'íŒ¼',
  'ÆØ' => 'íŒ½',
  'ÆÙ' => 'í�„',
  'ÆÚ' => 'í�…',
  'ÆÛ' => 'í�¼',
  'ÆÜ' => 'í�½',
  'ÆÝ' => 'íŽ€',
  'ÆÞ' => 'íŽ„',
  'Æß' => 'íŽŒ',
  'Æà' => 'íŽ�',
  'Æá' => 'íŽ�',
  'Æâ' => 'íŽ�',
  'Æã' => 'íŽ‘',
  'Æä' => 'íŽ˜',
  'Æå' => 'íŽ™',
  'Ææ' => 'íŽœ',
  'Æç' => 'íŽ ',
  'Æè' => 'íŽ¨',
  'Æé' => 'íŽ©',
  'Æê' => 'íŽ«',
  'Æë' => 'íŽ­',
  'Æì' => 'íŽ´',
  'Æí' => 'íŽ¸',
  'Æî' => 'íŽ¼',
  'Æï' => 'í�„',
  'Æð' => 'í�…',
  'Æñ' => 'í�ˆ',
  'Æò' => 'í�‰',
  'Æó' => 'í��',
  'Æô' => 'í�˜',
  'Æõ' => 'í�¡',
  'Æö' => 'í�£',
  'Æ÷' => 'í�¬',
  'Æø' => 'í�­',
  'Æù' => 'í�°',
  'Æú' => 'í�´',
  'Æû' => 'í�¼',
  'Æü' => 'í�½',
  'Æý' => 'í�¿',
  'Æþ' => 'í��',
  'Ç¡' => 'í�ˆ',
  'Ç¢' => 'í��',
  'Ç£' => 'í‘€',
  'Ç¤' => 'í‘„',
  'Ç¥' => 'í‘œ',
  'Ç¦' => 'í‘ ',
  'Ç§' => 'í‘¤',
  'Ç¨' => 'í‘­',
  'Ç©' => 'í‘¯',
  'Çª' => 'í‘¸',
  'Ç«' => 'í‘¹',
  'Ç¬' => 'í‘¼',
  'Ç­' => 'í‘¿',
  'Ç®' => 'í’€',
  'Ç¯' => 'í’‚',
  'Ç°' => 'í’ˆ',
  'Ç±' => 'í’‰',
  'Ç²' => 'í’‹',
  'Ç³' => 'í’�',
  'Ç´' => 'í’”',
  'Çµ' => 'í’©',
  'Ç¶' => 'í“Œ',
  'Ç·' => 'í“�',
  'Ç¸' => 'í“”',
  'Ç¹' => 'í“œ',
  'Çº' => 'í“Ÿ',
  'Ç»' => 'í“¨',
  'Ç¼' => 'í“¬',
  'Ç½' => 'í“°',
  'Ç¾' => 'í“¸',
  'Ç¿' => 'í“»',
  'ÇÀ' => 'í“½',
  'ÇÁ' => 'í”„',
  'ÇÂ' => 'í”ˆ',
  'ÇÃ' => 'í”Œ',
  'ÇÄ' => 'í””',
  'ÇÅ' => 'í”•',
  'ÇÆ' => 'í”—',
  'ÇÇ' => 'í”¼',
  'ÇÈ' => 'í”½',
  'ÇÉ' => 'í•€',
  'ÇÊ' => 'í•„',
  'ÇË' => 'í•Œ',
  'ÇÌ' => 'í•�',
  'ÇÍ' => 'í•�',
  'ÇÎ' => 'í•‘',
  'ÇÏ' => 'í•˜',
  'ÇÐ' => 'í•™',
  'ÇÑ' => 'í•œ',
  'ÇÒ' => 'í• ',
  'ÇÓ' => 'í•¥',
  'ÇÔ' => 'í•¨',
  'ÇÕ' => 'í•©',
  'ÇÖ' => 'í•«',
  'Ç×' => 'í•­',
  'ÇØ' => 'í•´',
  'ÇÙ' => 'í•µ',
  'ÇÚ' => 'í•¸',
  'ÇÛ' => 'í•¼',
  'ÇÜ' => 'í–„',
  'ÇÝ' => 'í–…',
  'ÇÞ' => 'í–‡',
  'Çß' => 'í–ˆ',
  'Çà' => 'í–‰',
  'Çá' => 'í–�',
  'Çâ' => 'í–¥',
  'Çã' => 'í—ˆ',
  'Çä' => 'í—‰',
  'Çå' => 'í—Œ',
  'Çæ' => 'í—�',
  'Çç' => 'í—’',
  'Çè' => 'í—˜',
  'Çé' => 'í—™',
  'Çê' => 'í—›',
  'Çë' => 'í—�',
  'Çì' => 'í—¤',
  'Çí' => 'í—¥',
  'Çî' => 'í—¨',
  'Çï' => 'í—¬',
  'Çð' => 'í—´',
  'Çñ' => 'í—µ',
  'Çò' => 'í—·',
  'Çó' => 'í—¹',
  'Çô' => 'í˜€',
  'Çõ' => 'í˜�',
  'Çö' => 'í˜„',
  'Ç÷' => 'í˜ˆ',
  'Çø' => 'í˜�',
  'Çù' => 'í˜‘',
  'Çú' => 'í˜“',
  'Çû' => 'í˜”',
  'Çü' => 'í˜•',
  'Çý' => 'í˜œ',
  'Çþ' => 'í˜ ',
  'È¡' => 'í˜¤',
  'È¢' => 'í˜­',
  'È£' => 'í˜¸',
  'È¤' => 'í˜¹',
  'È¥' => 'í˜¼',
  'È¦' => 'í™€',
  'È§' => 'í™…',
  'È¨' => 'í™ˆ',
  'È©' => 'í™‰',
  'Èª' => 'í™‹',
  'È«' => 'í™�',
  'È¬' => 'í™‘',
  'È­' => 'í™”',
  'È®' => 'í™•',
  'È¯' => 'í™˜',
  'È°' => 'í™œ',
  'È±' => 'í™§',
  'È²' => 'í™©',
  'È³' => 'í™°',
  'È´' => 'í™±',
  'Èµ' => 'í™´',
  'È¶' => 'íšƒ',
  'È·' => 'íš…',
  'È¸' => 'íšŒ',
  'È¹' => 'íš�',
  'Èº' => 'íš�',
  'È»' => 'íš”',
  'È¼' => 'íš�',
  'È½' => 'íšŸ',
  'È¾' => 'íš¡',
  'È¿' => 'íš¨',
  'ÈÀ' => 'íš¬',
  'ÈÁ' => 'íš°',
  'ÈÂ' => 'íš¹',
  'ÈÃ' => 'íš»',
  'ÈÄ' => 'í›„',
  'ÈÅ' => 'í›…',
  'ÈÆ' => 'í›ˆ',
  'ÈÇ' => 'í›Œ',
  'ÈÈ' => 'í›‘',
  'ÈÉ' => 'í›”',
  'ÈÊ' => 'í›—',
  'ÈË' => 'í›™',
  'ÈÌ' => 'í› ',
  'ÈÍ' => 'í›¤',
  'ÈÎ' => 'í›¨',
  'ÈÏ' => 'í›°',
  'ÈÐ' => 'í›µ',
  'ÈÑ' => 'í›¼',
  'ÈÒ' => 'í›½',
  'ÈÓ' => 'íœ€',
  'ÈÔ' => 'íœ„',
  'ÈÕ' => 'íœ‘',
  'ÈÖ' => 'íœ˜',
  'È×' => 'íœ™',
  'ÈØ' => 'íœœ',
  'ÈÙ' => 'íœ ',
  'ÈÚ' => 'íœ¨',
  'ÈÛ' => 'íœ©',
  'ÈÜ' => 'íœ«',
  'ÈÝ' => 'íœ­',
  'ÈÞ' => 'íœ´',
  'Èß' => 'íœµ',
  'Èà' => 'íœ¸',
  'Èá' => 'íœ¼',
  'Èâ' => 'í�„',
  'Èã' => 'í�‡',
  'Èä' => 'í�‰',
  'Èå' => 'í��',
  'Èæ' => 'í�‘',
  'Èç' => 'í�”',
  'Èè' => 'í�–',
  'Èé' => 'í�—',
  'Èê' => 'í�˜',
  'Èë' => 'í�™',
  'Èì' => 'í� ',
  'Èí' => 'í�¡',
  'Èî' => 'í�£',
  'Èï' => 'í�¥',
  'Èð' => 'í�©',
  'Èñ' => 'í�¬',
  'Èò' => 'í�°',
  'Èó' => 'í�´',
  'Èô' => 'í�¼',
  'Èõ' => 'í�½',
  'Èö' => 'íž�',
  'È÷' => 'ížˆ',
  'Èø' => 'íž‰',
  'Èù' => 'ížŒ',
  'Èú' => 'íž�',
  'Èû' => 'íž˜',
  'Èü' => 'íž™',
  'Èý' => 'íž›',
  'Èþ' => 'íž�',
  'Ê¡' => 'ä¼½',
  'Ê¢' => 'ä½³',
  'Ê£' => 'å�‡',
  'Ê¤' => 'åƒ¹',
  'Ê¥' => 'åŠ ',
  'Ê¦' => 'å�¯',
  'Ê§' => 'å‘µ',
  'Ê¨' => 'å“¥',
  'Ê©' => 'å˜‰',
  'Êª' => 'å«�',
  'Ê«' => 'å®¶',
  'Ê¬' => 'æš‡',
  'Ê­' => 'æž¶',
  'Ê®' => 'æž·',
  'Ê¯' => 'æŸ¯',
  'Ê°' => 'æ­Œ',
  'Ê±' => 'ç�‚',
  'Ê²' => 'ç—‚',
  'Ê³' => 'ç¨¼',
  'Ê´' => 'è‹›',
  'Êµ' => 'èŒ„',
  'Ê¶' => 'è¡—',
  'Ê·' => 'è¢ˆ',
  'Ê¸' => 'è¨¶',
  'Ê¹' => 'è³ˆ',
  'Êº' => 'è·�',
  'Ê»' => 'è»»',
  'Ê¼' => 'è¿¦',
  'Ê½' => 'é§•',
  'Ê¾' => 'åˆ»',
  'Ê¿' => 'å�´',
  'ÊÀ' => 'å�„',
  'ÊÁ' => 'æ�ª',
  'ÊÂ' => 'æ…¤',
  'ÊÃ' => 'æ®¼',
  'ÊÄ' => 'ç��',
  'ÊÅ' => 'è„š',
  'ÊÆ' => 'è¦º',
  'ÊÇ' => 'è§’',
  'ÊÈ' => 'é–£',
  'ÊÉ' => 'ä¾ƒ',
  'ÊÊ' => 'åˆŠ',
  'ÊË' => 'å¢¾',
  'ÊÌ' => 'å¥¸',
  'ÊÍ' => 'å§¦',
  'ÊÎ' => 'å¹²',
  'ÊÏ' => 'å¹¹',
  'ÊÐ' => 'æ‡‡',
  'ÊÑ' => 'æ�€',
  'ÊÒ' => 'æ�†',
  'ÊÓ' => 'æŸ¬',
  'ÊÔ' => 'æ¡¿',
  'ÊÕ' => 'æ¾—',
  'ÊÖ' => 'ç™Ž',
  'Ê×' => 'çœ‹',
  'ÊØ' => 'ç£µ',
  'ÊÙ' => 'ç¨ˆ',
  'ÊÚ' => 'ç«¿',
  'ÊÛ' => 'ç°¡',
  'ÊÜ' => 'è‚�',
  'ÊÝ' => 'è‰®',
  'ÊÞ' => 'è‰±',
  'Êß' => 'è««',
  'Êà' => 'é–“',
  'Êá' => 'ä¹«',
  'Êâ' => 'å–�',
  'Êã' => 'æ›·',
  'Êä' => 'æ¸´',
  'Êå' => 'ç¢£',
  'Êæ' => 'ç«­',
  'Êç' => 'è‘›',
  'Êè' => 'è¤�',
  'Êé' => 'è�Ž',
  'Êê' => 'éž¨',
  'Êë' => 'å‹˜',
  'Êì' => 'å�Ž',
  'Êí' => 'å ª',
  'Êî' => 'åµŒ',
  'Êï' => 'æ„Ÿ',
  'Êð' => 'æ†¾',
  'Êñ' => 'æˆ¡',
  'Êò' => 'æ•¢',
  'Êó' => 'æŸ‘',
  'Êô' => 'æ©„',
  'Êõ' => 'æ¸›',
  'Êö' => 'ç”˜',
  'Ê÷' => 'ç–³',
  'Êø' => 'ç›£',
  'Êù' => 'çž°',
  'Êú' => 'ç´º',
  'Êû' => 'é‚¯',
  'Êü' => 'é‘‘',
  'Êý' => 'é‘’',
  'Êþ' => 'é¾•',
  'Ë¡' => 'åŒ£',
  'Ë¢' => 'å²¬',
  'Ë£' => 'ç”²',
  'Ë¤' => 'èƒ›',
  'Ë¥' => 'é‰€',
  'Ë¦' => 'é–˜',
  'Ë§' => 'å‰›',
  'Ë¨' => 'å ˆ',
  'Ë©' => 'å§œ',
  'Ëª' => 'å²¡',
  'Ë«' => 'å´—',
  'Ë¬' => 'åº·',
  'Ë­' => 'å¼º',
  'Ë®' => 'å½Š',
  'Ë¯' => 'æ…·',
  'Ë°' => 'æ±Ÿ',
  'Ë±' => 'ç•º',
  'Ë²' => 'ç–†',
  'Ë³' => 'ç³ ',
  'Ë´' => 'çµ³',
  'Ëµ' => 'ç¶±',
  'Ë¶' => 'ç¾Œ',
  'Ë·' => 'è…”',
  'Ë¸' => 'èˆ¡',
  'Ë¹' => 'è–‘',
  'Ëº' => 'è¥�',
  'Ë»' => 'è¬›',
  'Ë¼' => 'é‹¼',
  'Ë½' => 'é™�',
  'Ë¾' => 'é±‡',
  'Ë¿' => 'ä»‹',
  'ËÀ' => 'ä»·',
  'ËÁ' => 'å€‹',
  'ËÂ' => 'å‡±',
  'ËÃ' => 'å¡�',
  'ËÄ' => 'æ„·',
  'ËÅ' => 'æ„¾',
  'ËÆ' => 'æ…¨',
  'ËÇ' => 'æ”¹',
  'ËÈ' => 'æ§ª',
  'ËÉ' => 'æ¼‘',
  'ËÊ' => 'ç–¥',
  'ËË' => 'çš†',
  'ËÌ' => 'ç›–',
  'ËÍ' => 'ç®‡',
  'ËÎ' => 'èŠ¥',
  'ËÏ' => 'è“‹',
  'ËÐ' => 'ï¤€',
  'ËÑ' => 'éŽ§',
  'ËÒ' => 'é–‹',
  'ËÓ' => 'å–€',
  'ËÔ' => 'å®¢',
  'ËÕ' => 'å�‘',
  'ËÖ' => 'ï¤�',
  'Ë×' => 'ç²³',
  'ËØ' => 'ç¾¹',
  'ËÙ' => 'é†µ',
  'ËÚ' => 'å€¨',
  'ËÛ' => 'åŽ»',
  'ËÜ' => 'å±…',
  'ËÝ' => 'å·¨',
  'ËÞ' => 'æ‹’',
  'Ëß' => 'æ�®',
  'Ëà' => 'æ“š',
  'Ëá' => 'æ“§',
  'Ëâ' => 'æ¸ ',
  'Ëã' => 'ç‚¬',
  'Ëä' => 'ç¥›',
  'Ëå' => 'è·�',
  'Ëæ' => 'è¸ž',
  'Ëç' => 'ï¤‚',
  'Ëè' => 'é�½',
  'Ëé' => 'é‰…',
  'Ëê' => 'é‹¸',
  'Ëë' => 'ä¹¾',
  'Ëì' => 'ä»¶',
  'Ëí' => 'å�¥',
  'Ëî' => 'å·¾',
  'Ëï' => 'å»º',
  'Ëð' => 'æ„†',
  'Ëñ' => 'æ¥—',
  'Ëò' => 'è…±',
  'Ëó' => 'è™”',
  'Ëô' => 'è¹‡',
  'Ëõ' => 'é�µ',
  'Ëö' => 'é¨«',
  'Ë÷' => 'ä¹ž',
  'Ëø' => 'å‚‘',
  'Ëù' => 'æ�°',
  'Ëú' => 'æ¡€',
  'Ëû' => 'å„‰',
  'Ëü' => 'åŠ�',
  'Ëý' => 'åŠ’',
  'Ëþ' => 'æª¢',
  'Ì¡' => 'çž¼',
  'Ì¢' => 'éˆ�',
  'Ì£' => 'é»”',
  'Ì¤' => 'åŠ«',
  'Ì¥' => 'æ€¯',
  'Ì¦' => 'è¿²',
  'Ì§' => 'å�ˆ',
  'Ì¨' => 'æ†©',
  'Ì©' => 'æ�­',
  'Ìª' => 'æ“Š',
  'Ì«' => 'æ ¼',
  'Ì¬' => 'æª„',
  'Ì­' => 'æ¿€',
  'Ì®' => 'è†ˆ',
  'Ì¯' => 'è¦¡',
  'Ì°' => 'éš”',
  'Ì±' => 'å …',
  'Ì²' => 'ç‰½',
  'Ì³' => 'çŠ¬',
  'Ì´' => 'ç”„',
  'Ìµ' => 'çµ¹',
  'Ì¶' => 'ç¹­',
  'Ì·' => 'è‚©',
  'Ì¸' => 'è¦‹',
  'Ì¹' => 'è­´',
  'Ìº' => 'é�£',
  'Ì»' => 'éµ‘',
  'Ì¼' => 'æŠ‰',
  'Ì½' => 'æ±º',
  'Ì¾' => 'æ½”',
  'Ì¿' => 'çµ�',
  'ÌÀ' => 'ç¼º',
  'ÌÁ' => 'è¨£',
  'ÌÂ' => 'å…¼',
  'ÌÃ' => 'æ…Š',
  'ÌÄ' => 'ç®�',
  'ÌÅ' => 'è¬™',
  'ÌÆ' => 'é‰—',
  'ÌÇ' => 'éŽŒ',
  'ÌÈ' => 'äº¬',
  'ÌÉ' => 'ä¿“',
  'ÌÊ' => 'å€ž',
  'ÌË' => 'å‚¾',
  'ÌÌ' => 'å„†',
  'ÌÍ' => 'å‹�',
  'ÌÎ' => 'å‹�',
  'ÌÏ' => 'å�¿',
  'ÌÐ' => 'å�°',
  'ÌÑ' => 'å¢ƒ',
  'ÌÒ' => 'åºš',
  'ÌÓ' => 'å¾‘',
  'ÌÔ' => 'æ…¶',
  'ÌÕ' => 'æ†¬',
  'ÌÖ' => 'æ“Ž',
  'Ì×' => 'æ•¬',
  'ÌØ' => 'æ™¯',
  'ÌÙ' => 'æš»',
  'ÌÚ' => 'æ›´',
  'ÌÛ' => 'æ¢—',
  'ÌÜ' => 'æ¶‡',
  'ÌÝ' => 'ç‚…',
  'ÌÞ' => 'çƒ±',
  'Ìß' => 'ç’Ÿ',
  'Ìà' => 'ç’¥',
  'Ìá' => 'ç“Š',
  'Ìâ' => 'ç—™',
  'Ìã' => 'ç¡¬',
  'Ìä' => 'ç£¬',
  'Ìå' => 'ç«Ÿ',
  'Ìæ' => 'ç«¶',
  'Ìç' => 'çµ…',
  'Ìè' => 'ç¶“',
  'Ìé' => 'è€•',
  'Ìê' => 'è€¿',
  'Ìë' => 'è„›',
  'Ìì' => 'èŽ–',
  'Ìí' => 'è­¦',
  'Ìî' => 'è¼•',
  'Ìï' => 'é€•',
  'Ìð' => 'é�¡',
  'Ìñ' => 'é ƒ',
  'Ìò' => 'é ¸',
  'Ìó' => 'é©š',
  'Ìô' => 'é¯¨',
  'Ìõ' => 'ä¿‚',
  'Ìö' => 'å•“',
  'Ì÷' => 'å º',
  'Ìø' => 'å¥‘',
  'Ìù' => 'å­£',
  'Ìú' => 'å±†',
  'Ìû' => 'æ‚¸',
  'Ìü' => 'æˆ’',
  'Ìý' => 'æ¡‚',
  'Ìþ' => 'æ¢°',
  'Í¡' => 'æ£¨',
  'Í¢' => 'æºª',
  'Í£' => 'ç•Œ',
  'Í¤' => 'ç™¸',
  'Í¥' => 'ç£Ž',
  'Í¦' => 'ç¨½',
  'Í§' => 'ç³»',
  'Í¨' => 'ç¹«',
  'Í©' => 'ç¹¼',
  'Íª' => 'è¨ˆ',
  'Í«' => 'èª¡',
  'Í¬' => 'è°¿',
  'Í­' => 'éšŽ',
  'Í®' => 'é·„',
  'Í¯' => 'å�¤',
  'Í°' => 'å�©',
  'Í±' => 'å‘Š',
  'Í²' => 'å‘±',
  'Í³' => 'å›º',
  'Í´' => 'å§‘',
  'Íµ' => 'å­¤',
  'Í¶' => 'å°»',
  'Í·' => 'åº«',
  'Í¸' => 'æ‹·',
  'Í¹' => 'æ”·',
  'Íº' => 'æ•…',
  'Í»' => 'æ•²',
  'Í¼' => 'æš ',
  'Í½' => 'æž¯',
  'Í¾' => 'æ§�',
  'Í¿' => 'æ²½',
  'ÍÀ' => 'ç—¼',
  'ÍÁ' => 'çš�',
  'ÍÂ' => 'ç�¾',
  'ÍÃ' => 'ç¨¿',
  'ÍÄ' => 'ç¾”',
  'ÍÅ' => 'è€ƒ',
  'ÍÆ' => 'è‚¡',
  'ÍÇ' => 'è†�',
  'ÍÈ' => 'è‹¦',
  'ÍÉ' => 'è‹½',
  'ÍÊ' => 'è�°',
  'ÍË' => 'è—�',
  'ÍÌ' => 'è ±',
  'ÍÍ' => 'è¢´',
  'ÍÎ' => 'èª¥',
  'ÍÏ' => 'ï¤ƒ',
  'ÍÐ' => 'è¾œ',
  'ÍÑ' => 'éŒ®',
  'ÍÒ' => 'é›‡',
  'ÍÓ' => 'é¡§',
  'ÍÔ' => 'é«˜',
  'ÍÕ' => 'é¼“',
  'ÍÖ' => 'å“­',
  'Í×' => 'æ–›',
  'ÍØ' => 'æ›²',
  'ÍÙ' => 'æ¢�',
  'ÍÚ' => 'ç©€',
  'ÍÛ' => 'è°·',
  'ÍÜ' => 'éµ ',
  'ÍÝ' => 'å›°',
  'ÍÞ' => 'å�¤',
  'Íß' => 'å´‘',
  'Íà' => 'æ˜†',
  'Íá' => 'æ¢±',
  'Íâ' => 'æ£�',
  'Íã' => 'æ»¾',
  'Íä' => 'ç�¨',
  'Íå' => 'è¢ž',
  'Íæ' => 'é¯¤',
  'Íç' => 'æ±¨',
  'Íè' => 'ï¤„',
  'Íé' => 'éª¨',
  'Íê' => 'ä¾›',
  'Íë' => 'å…¬',
  'Íì' => 'å…±',
  'Íí' => 'åŠŸ',
  'Íî' => 'å­”',
  'Íï' => 'å·¥',
  'Íð' => 'æ��',
  'Íñ' => 'æ�­',
  'Íò' => 'æ‹±',
  'Íó' => 'æŽ§',
  'Íô' => 'æ”»',
  'Íõ' => 'ç�™',
  'Íö' => 'ç©º',
  'Í÷' => 'èš£',
  'Íø' => 'è²¢',
  'Íù' => 'éž�',
  'Íú' => 'ä¸²',
  'Íû' => 'å¯¡',
  'Íü' => 'æˆˆ',
  'Íý' => 'æžœ',
  'Íþ' => 'ç“œ',
  'Î¡' => 'ç§‘',
  'Î¢' => 'è�“',
  'Î£' => 'èª‡',
  'Î¤' => 'èª²',
  'Î¥' => 'è·¨',
  'Î¦' => 'é�Ž',
  'Î§' => 'é�‹',
  'Î¨' => 'é¡†',
  'Î©' => 'å»“',
  'Îª' => 'æ§¨',
  'Î«' => 'è—¿',
  'Î¬' => 'éƒ­',
  'Î­' => 'ï¤…',
  'Î®' => 'å† ',
  'Î¯' => 'å®˜',
  'Î°' => 'å¯¬',
  'Î±' => 'æ…£',
  'Î²' => 'æ£º',
  'Î³' => 'æ¬¾',
  'Î´' => 'ç�Œ',
  'Îµ' => 'ç�¯',
  'Î¶' => 'ç“˜',
  'Î·' => 'ç®¡',
  'Î¸' => 'ç½�',
  'Î¹' => 'è�…',
  'Îº' => 'è§€',
  'Î»' => 'è²«',
  'Î¼' => 'é—œ',
  'Î½' => 'é¤¨',
  'Î¾' => 'åˆ®',
  'Î¿' => 'æ��',
  'ÎÀ' => 'æ‹¬',
  'ÎÁ' => 'é€‚',
  'ÎÂ' => 'ä¾Š',
  'ÎÃ' => 'å…‰',
  'ÎÄ' => 'åŒ¡',
  'ÎÅ' => 'å£™',
  'ÎÆ' => 'å»£',
  'ÎÇ' => 'æ› ',
  'ÎÈ' => 'æ´¸',
  'ÎÉ' => 'ç‚š',
  'ÎÊ' => 'ç‹‚',
  'ÎË' => 'ç�–',
  'ÎÌ' => 'ç­�',
  'ÎÍ' => 'èƒ±',
  'ÎÎ' => 'é‘›',
  'ÎÏ' => 'å�¦',
  'ÎÐ' => 'æŽ›',
  'ÎÑ' => 'ç½«',
  'ÎÒ' => 'ä¹–',
  'ÎÓ' => 'å‚€',
  'ÎÔ' => 'å¡Š',
  'ÎÕ' => 'å£ž',
  'ÎÖ' => 'æ€ª',
  'Î×' => 'æ„§',
  'ÎØ' => 'æ‹�',
  'ÎÙ' => 'æ§�',
  'ÎÚ' => 'é­�',
  'ÎÛ' => 'å®�',
  'ÎÜ' => 'ç´˜',
  'ÎÝ' => 'è‚±',
  'ÎÞ' => 'è½Ÿ',
  'Îß' => 'äº¤',
  'Îà' => 'åƒ‘',
  'Îá' => 'å’¬',
  'Îâ' => 'å–¬',
  'Îã' => 'å¬Œ',
  'Îä' => 'å¶ ',
  'Îå' => 'å·§',
  'Îæ' => 'æ”ª',
  'Îç' => 'æ•Ž',
  'Îè' => 'æ ¡',
  'Îé' => 'æ©‹',
  'Îê' => 'ç‹¡',
  'Îë' => 'çšŽ',
  'Îì' => 'çŸ¯',
  'Îí' => 'çµž',
  'Îî' => 'ç¿¹',
  'Îï' => 'è† ',
  'Îð' => 'è•Ž',
  'Îñ' => 'è›Ÿ',
  'Îò' => 'è¼ƒ',
  'Îó' => 'è½Ž',
  'Îô' => 'éƒŠ',
  'Îõ' => 'é¤ƒ',
  'Îö' => 'é©•',
  'Î÷' => 'é®«',
  'Îø' => 'ä¸˜',
  'Îù' => 'ä¹…',
  'Îú' => 'ä¹�',
  'Îû' => 'ä»‡',
  'Îü' => 'ä¿±',
  'Îý' => 'å…·',
  'Îþ' => 'å‹¾',
  'Ï¡' => 'å�€',
  'Ï¢' => 'å�£',
  'Ï£' => 'å�¥',
  'Ï¤' => 'å’Ž',
  'Ï¥' => 'å˜”',
  'Ï¦' => 'å�µ',
  'Ï§' => 'åž¢',
  'Ï¨' => 'å¯‡',
  'Ï©' => 'å¶‡',
  'Ïª' => 'å»�',
  'Ï«' => 'æ‡¼',
  'Ï¬' => 'æ‹˜',
  'Ï­' => 'æ•‘',
  'Ï®' => 'æž¸',
  'Ï¯' => 'æŸ©',
  'Ï°' => 'æ§‹',
  'Ï±' => 'æ­�',
  'Ï²' => 'æ¯†',
  'Ï³' => 'æ¯¬',
  'Ï´' => 'æ±‚',
  'Ïµ' => 'æº�',
  'Ï¶' => 'ç�¸',
  'Ï·' => 'ç‹—',
  'Ï¸' => 'çŽ–',
  'Ï¹' => 'ç�ƒ',
  'Ïº' => 'çž¿',
  'Ï»' => 'çŸ©',
  'Ï¼' => 'ç©¶',
  'Ï½' => 'çµ¿',
  'Ï¾' => 'è€‰',
  'Ï¿' => 'è‡¼',
  'ÏÀ' => 'èˆ…',
  'ÏÁ' => 'èˆŠ',
  'ÏÂ' => 'è‹Ÿ',
  'ÏÃ' => 'è¡¢',
  'ÏÄ' => 'è¬³',
  'ÏÅ' => 'è³¼',
  'ÏÆ' => 'è»€',
  'ÏÇ' => 'é€‘',
  'ÏÈ' => 'é‚±',
  'ÏÉ' => 'é‰¤',
  'ÏÊ' => 'éŠ¶',
  'ÏË' => 'é§’',
  'ÏÌ' => 'é©…',
  'ÏÍ' => 'é³©',
  'ÏÎ' => 'é·—',
  'ÏÏ' => 'é¾œ',
  'ÏÐ' => 'åœ‹',
  'ÏÑ' => 'å±€',
  'ÏÒ' => 'è�Š',
  'ÏÓ' => 'éž ',
  'ÏÔ' => 'éž«',
  'ÏÕ' => 'éº´',
  'ÏÖ' => 'å�›',
  'Ï×' => 'çª˜',
  'ÏØ' => 'ç¾¤',
  'ÏÙ' => 'è£™',
  'ÏÚ' => 'è»�',
  'ÏÛ' => 'éƒ¡',
  'ÏÜ' => 'å €',
  'ÏÝ' => 'å±ˆ',
  'ÏÞ' => 'æŽ˜',
  'Ïß' => 'çªŸ',
  'Ïà' => 'å®®',
  'Ïá' => 'å¼“',
  'Ïâ' => 'ç©¹',
  'Ïã' => 'çª®',
  'Ïä' => 'èŠŽ',
  'Ïå' => 'èº¬',
  'Ïæ' => 'å€¦',
  'Ïç' => 'åˆ¸',
  'Ïè' => 'å‹¸',
  'Ïé' => 'å�·',
  'Ïê' => 'åœˆ',
  'Ïë' => 'æ‹³',
  'Ïì' => 'æ�²',
  'Ïí' => 'æ¬Š',
  'Ïî' => 'æ·ƒ',
  'Ïï' => 'çœ·',
  'Ïð' => 'åŽ¥',
  'Ïñ' => 'ç�—',
  'Ïò' => 'è•¨',
  'Ïó' => 'è¹¶',
  'Ïô' => 'é—•',
  'Ïõ' => 'æœº',
  'Ïö' => 'æ«ƒ',
  'Ï÷' => 'æ½°',
  'Ïø' => 'è©­',
  'Ïù' => 'è»Œ',
  'Ïú' => 'é¥‹',
  'Ïû' => 'ï¤†',
  'Ïü' => 'æ™·',
  'Ïý' => 'æ­¸',
  'Ïþ' => 'è²´',
  'Ð¡' => 'é¬¼',
  'Ð¢' => 'ï¤‡',
  'Ð£' => 'å�«',
  'Ð¤' => 'åœ­',
  'Ð¥' => 'å¥Ž',
  'Ð¦' => 'æ�†',
  'Ð§' => 'æ§»',
  'Ð¨' => 'ç�ª',
  'Ð©' => 'ç¡…',
  'Ðª' => 'çªº',
  'Ð«' => 'ç«…',
  'Ð¬' => 'ç³¾',
  'Ð­' => 'è‘µ',
  'Ð®' => 'è¦�',
  'Ð¯' => 'èµ³',
  'Ð°' => 'é€µ',
  'Ð±' => 'é–¨',
  'Ð²' => 'å‹»',
  'Ð³' => 'å�‡',
  'Ð´' => 'ç•‡',
  'Ðµ' => 'ç­ ',
  'Ð¶' => 'è�Œ',
  'Ð·' => 'éˆž',
  'Ð¸' => 'ï¤ˆ',
  'Ð¹' => 'æ©˜',
  'Ðº' => 'å…‹',
  'Ð»' => 'å‰‹',
  'Ð¼' => 'åŠ‡',
  'Ð½' => 'æˆŸ',
  'Ð¾' => 'æ£˜',
  'Ð¿' => 'æ¥µ',
  'ÐÀ' => 'éš™',
  'ÐÁ' => 'åƒ…',
  'ÐÂ' => 'åŠ¤',
  'ÐÃ' => 'å‹¤',
  'ÐÄ' => 'æ‡ƒ',
  'ÐÅ' => 'æ–¤',
  'ÐÆ' => 'æ ¹',
  'ÐÇ' => 'æ§¿',
  'ÐÈ' => 'ç‘¾',
  'ÐÉ' => 'ç­‹',
  'ÐÊ' => 'èŠ¹',
  'ÐË' => 'è�«',
  'ÐÌ' => 'è¦²',
  'ÐÍ' => 'è¬¹',
  'ÐÎ' => 'è¿‘',
  'ÐÏ' => 'é¥‰',
  'ÐÐ' => 'ï¤‰',
  'ÐÑ' => 'ä»Š',
  'ÐÒ' => 'å¦—',
  'ÐÓ' => 'æ“’',
  'ÐÔ' => 'æ˜‘',
  'ÐÕ' => 'æªŽ',
  'ÐÖ' => 'ç�´',
  'Ð×' => 'ç¦�',
  'ÐØ' => 'ç¦½',
  'ÐÙ' => 'èŠ©',
  'ÐÚ' => 'è¡¾',
  'ÐÛ' => 'è¡¿',
  'ÐÜ' => 'è¥Ÿ',
  'ÐÝ' => 'ï¤Š',
  'ÐÞ' => 'éŒ¦',
  'Ðß' => 'ä¼‹',
  'Ðà' => 'å�Š',
  'Ðá' => 'æ€¥',
  'Ðâ' => 'æ‰±',
  'Ðã' => 'æ±²',
  'Ðä' => 'ç´š',
  'Ðå' => 'çµ¦',
  'Ðæ' => 'äº˜',
  'Ðç' => 'å…¢',
  'Ðè' => 'çŸœ',
  'Ðé' => 'è‚¯',
  'Ðê' => 'ä¼�',
  'Ðë' => 'ä¼Ž',
  'Ðì' => 'å…¶',
  'Ðí' => 'å†€',
  'Ðî' => 'å—œ',
  'Ðï' => 'å™¨',
  'Ðð' => 'åœ»',
  'Ðñ' => 'åŸº',
  'Ðò' => 'åŸ¼',
  'Ðó' => 'å¤”',
  'Ðô' => 'å¥‡',
  'Ðõ' => 'å¦“',
  'Ðö' => 'å¯„',
  'Ð÷' => 'å²�',
  'Ðø' => 'å´Ž',
  'Ðù' => 'å·±',
  'Ðú' => 'å¹¾',
  'Ðû' => 'å¿Œ',
  'Ðü' => 'æŠ€',
  'Ðý' => 'æ——',
  'Ðþ' => 'æ—£',
  'Ñ¡' => 'æœž',
  'Ñ¢' => 'æœŸ',
  'Ñ£' => 'æ�ž',
  'Ñ¤' => 'æ£‹',
  'Ñ¥' => 'æ£„',
  'Ñ¦' => 'æ©Ÿ',
  'Ñ§' => 'æ¬º',
  'Ñ¨' => 'æ°£',
  'Ñ©' => 'æ±½',
  'Ñª' => 'æ²‚',
  'Ñ«' => 'æ·‡',
  'Ñ¬' => 'çŽ˜',
  'Ñ­' => 'ç�¦',
  'Ñ®' => 'ç�ª',
  'Ñ¯' => 'ç’‚',
  'Ñ°' => 'ç’£',
  'Ñ±' => 'ç•¸',
  'Ñ²' => 'ç•¿',
  'Ñ³' => 'ç¢�',
  'Ñ´' => 'ç£¯',
  'Ñµ' => 'ç¥�',
  'Ñ¶' => 'ç¥‡',
  'Ñ·' => 'ç¥ˆ',
  'Ñ¸' => 'ç¥º',
  'Ñ¹' => 'ç®•',
  'Ñº' => 'ç´€',
  'Ñ»' => 'ç¶º',
  'Ñ¼' => 'ç¾ˆ',
  'Ñ½' => 'è€†',
  'Ñ¾' => 'è€­',
  'Ñ¿' => 'è‚Œ',
  'ÑÀ' => 'è¨˜',
  'ÑÁ' => 'è­�',
  'ÑÂ' => 'è±ˆ',
  'ÑÃ' => 'èµ·',
  'ÑÄ' => 'éŒ¡',
  'ÑÅ' => 'éŒ¤',
  'ÑÆ' => 'é£¢',
  'ÑÇ' => 'é¥‘',
  'ÑÈ' => 'é¨Ž',
  'ÑÉ' => 'é¨�',
  'ÑÊ' => 'é©¥',
  'ÑË' => 'éº’',
  'ÑÌ' => 'ç·Š',
  'ÑÍ' => 'ä½¶',
  'ÑÎ' => 'å�‰',
  'ÑÏ' => 'æ‹®',
  'ÑÐ' => 'æ¡”',
  'ÑÑ' => 'é‡‘',
  'ÑÒ' => 'å–«',
  'ÑÓ' => 'å„º',
  'ÑÔ' => 'ï¤‹',
  'ÑÕ' => 'ï¤Œ',
  'ÑÖ' => 'å¨œ',
  'Ñ×' => 'æ‡¦',
  'ÑØ' => 'ï¤�',
  'ÑÙ' => 'æ‹�',
  'ÑÚ' => 'æ‹¿',
  'ÑÛ' => 'ï¤Ž',
  'ÑÜ' => 'ï¤�',
  'ÑÝ' => 'ï¤�',
  'ÑÞ' => 'ï¤‘',
  'Ñß' => 'ï¤’',
  'Ñà' => 'ï¤“',
  'Ñá' => 'é‚£',
  'Ñâ' => 'ï¤”',
  'Ñã' => 'ï¤•',
  'Ñä' => 'ï¤–',
  'Ñå' => 'ï¤—',
  'Ñæ' => 'ï¤˜',
  'Ñç' => 'è«¾',
  'Ñè' => 'ï¤™',
  'Ñé' => 'ï¤š',
  'Ñê' => 'ï¤›',
  'Ñë' => 'ï¤œ',
  'Ñì' => 'æš–',
  'Ñí' => 'ï¤�',
  'Ñî' => 'ç…–',
  'Ñï' => 'ï¤ž',
  'Ñð' => 'ï¤Ÿ',
  'Ññ' => 'é›£',
  'Ñò' => 'ï¤ ',
  'Ñó' => 'æ��',
  'Ñô' => 'æ�º',
  'Ñõ' => 'å�—',
  'Ñö' => 'ï¤¡',
  'Ñ÷' => 'æž�',
  'Ñø' => 'æ¥ ',
  'Ñù' => 'æ¹³',
  'Ñú' => 'ï¤¢',
  'Ñû' => 'ç”·',
  'Ñü' => 'ï¤£',
  'Ñý' => 'ï¤¤',
  'Ñþ' => 'ï¤¥',
  'Ò¡' => 'ç´�',
  'Ò¢' => 'ï¤¦',
  'Ò£' => 'ï¤§',
  'Ò¤' => 'è¡²',
  'Ò¥' => 'å›Š',
  'Ò¦' => 'å¨˜',
  'Ò§' => 'ï¤¨',
  'Ò¨' => 'ï¤©',
  'Ò©' => 'ï¤ª',
  'Òª' => 'ï¤«',
  'Ò«' => 'ï¤¬',
  'Ò¬' => 'ä¹ƒ',
  'Ò­' => 'ï¤­',
  'Ò®' => 'å…§',
  'Ò¯' => 'å¥ˆ',
  'Ò°' => 'æŸ°',
  'Ò±' => 'è€�',
  'Ò²' => 'ï¤®',
  'Ò³' => 'å¥³',
  'Ò´' => 'å¹´',
  'Òµ' => 'æ’š',
  'Ò¶' => 'ç§Š',
  'Ò·' => 'å¿µ',
  'Ò¸' => 'æ�¬',
  'Ò¹' => 'æ‹ˆ',
  'Òº' => 'æ�»',
  'Ò»' => 'å¯§',
  'Ò¼' => 'å¯—',
  'Ò½' => 'åŠª',
  'Ò¾' => 'ï¤¯',
  'Ò¿' => 'å¥´',
  'ÒÀ' => 'å¼©',
  'ÒÁ' => 'æ€’',
  'ÒÂ' => 'ï¤°',
  'ÒÃ' => 'ï¤±',
  'ÒÄ' => 'ï¤²',
  'ÒÅ' => 'ç‘™',
  'ÒÆ' => 'ï¤³',
  'ÒÇ' => 'ï¤´',
  'ÒÈ' => 'ï¤µ',
  'ÒÉ' => 'ï¤¶',
  'ÒÊ' => 'ï¤·',
  'ÒË' => 'ï¤¸',
  'ÒÌ' => 'é§‘',
  'ÒÍ' => 'ï¤¹',
  'ÒÎ' => 'ï¤º',
  'ÒÏ' => 'ï¤»',
  'ÒÐ' => 'ï¤¼',
  'ÒÑ' => 'ï¤½',
  'ÒÒ' => 'ï¤¾',
  'ÒÓ' => 'ï¤¿',
  'ÒÔ' => 'ï¥€',
  'ÒÕ' => 'ï¥�',
  'ÒÖ' => 'ï¥‚',
  'Ò×' => 'ï¥ƒ',
  'ÒØ' => 'æ¿ƒ',
  'ÒÙ' => 'ï¥„',
  'ÒÚ' => 'ï¥…',
  'ÒÛ' => 'è†¿',
  'ÒÜ' => 'è¾²',
  'ÒÝ' => 'æƒ±',
  'ÒÞ' => 'ï¥†',
  'Òß' => 'ï¥‡',
  'Òà' => 'è…¦',
  'Òá' => 'ï¥ˆ',
  'Òâ' => 'ï¥‰',
  'Òã' => 'å°¿',
  'Òä' => 'ï¥Š',
  'Òå' => 'ï¥‹',
  'Òæ' => 'ï¥Œ',
  'Òç' => 'ï¥�',
  'Òè' => 'ï¥Ž',
  'Òé' => 'ï¥�',
  'Òê' => 'ï¥�',
  'Òë' => 'ï¥‘',
  'Òì' => 'å«©',
  'Òí' => 'è¨¥',
  'Òî' => 'æ�»',
  'Òï' => 'ç´�',
  'Òð' => 'ï¥’',
  'Òñ' => 'ï¥“',
  'Òò' => 'ï¥”',
  'Òó' => 'ï¥•',
  'Òô' => 'ï¥–',
  'Òõ' => 'ï¥—',
  'Òö' => 'èƒ½',
  'Ò÷' => 'ï¥˜',
  'Òø' => 'ï¥™',
  'Òù' => 'å°¼',
  'Òú' => 'æ³¥',
  'Òû' => 'åŒ¿',
  'Òü' => 'æºº',
  'Òý' => 'å¤š',
  'Òþ' => 'èŒ¶',
  'Ó¡' => 'ä¸¹',
  'Ó¢' => 'äº¶',
  'Ó£' => 'ä½†',
  'Ó¤' => 'å–®',
  'Ó¥' => 'åœ˜',
  'Ó¦' => 'å£‡',
  'Ó§' => 'å½–',
  'Ó¨' => 'æ–·',
  'Ó©' => 'æ—¦',
  'Óª' => 'æª€',
  'Ó«' => 'æ®µ',
  'Ó¬' => 'æ¹�',
  'Ó­' => 'çŸ­',
  'Ó®' => 'ç«¯',
  'Ó¯' => 'ç°ž',
  'Ó°' => 'ç·ž',
  'Ó±' => 'è›‹',
  'Ó²' => 'è¢’',
  'Ó³' => 'é„²',
  'Ó´' => 'é�›',
  'Óµ' => 'æ’»',
  'Ó¶' => 'æ¾¾',
  'Ó·' => 'ç�º',
  'Ó¸' => 'ç–¸',
  'Ó¹' => 'é�”',
  'Óº' => 'å•–',
  'Ó»' => 'å��',
  'Ó¼' => 'æ†º',
  'Ó½' => 'æ“”',
  'Ó¾' => 'æ›‡',
  'Ó¿' => 'æ·¡',
  'ÓÀ' => 'æ¹›',
  'ÓÁ' => 'æ½­',
  'ÓÂ' => 'æ¾¹',
  'ÓÃ' => 'ç—°',
  'ÓÄ' => 'è�ƒ',
  'ÓÅ' => 'è†½',
  'ÓÆ' => 'è•�',
  'ÓÇ' => 'è¦ƒ',
  'ÓÈ' => 'è«‡',
  'ÓÉ' => 'è­š',
  'ÓÊ' => 'éŒŸ',
  'ÓË' => 'æ²“',
  'ÓÌ' => 'ç•“',
  'ÓÍ' => 'ç­”',
  'ÓÎ' => 'è¸�',
  'ÓÏ' => 'é��',
  'ÓÐ' => 'å”�',
  'ÓÑ' => 'å ‚',
  'ÓÒ' => 'å¡˜',
  'ÓÓ' => 'å¹¢',
  'ÓÔ' => 'æˆ‡',
  'ÓÕ' => 'æ’ž',
  'ÓÖ' => 'æ£ ',
  'Ó×' => 'ç•¶',
  'ÓØ' => 'ç³–',
  'ÓÙ' => 'èž³',
  'ÓÚ' => 'é»¨',
  'ÓÛ' => 'ä»£',
  'ÓÜ' => 'åžˆ',
  'ÓÝ' => 'å�®',
  'ÓÞ' => 'å¤§',
  'Óß' => 'å°�',
  'Óà' => 'å²±',
  'Óá' => 'å¸¶',
  'Óâ' => 'å¾…',
  'Óã' => 'æˆ´',
  'Óä' => 'æ“¡',
  'Óå' => 'çŽ³',
  'Óæ' => 'è‡º',
  'Óç' => 'è¢‹',
  'Óè' => 'è²¸',
  'Óé' => 'éšŠ',
  'Óê' => 'é»›',
  'Óë' => 'å®…',
  'Óì' => 'å¾·',
  'Óí' => 'æ‚³',
  'Óî' => 'å€’',
  'Óï' => 'åˆ€',
  'Óð' => 'åˆ°',
  'Óñ' => 'åœ–',
  'Óò' => 'å µ',
  'Óó' => 'å¡—',
  'Óô' => 'å°Ž',
  'Óõ' => 'å± ',
  'Óö' => 'å³¶',
  'Ó÷' => 'å¶‹',
  'Óø' => 'åº¦',
  'Óù' => 'å¾’',
  'Óú' => 'æ‚¼',
  'Óû' => 'æŒ‘',
  'Óü' => 'æŽ‰',
  'Óý' => 'æ�—',
  'Óþ' => 'æ¡ƒ',
  'Ô¡' => 'æ£¹',
  'Ô¢' => 'æ«‚',
  'Ô£' => 'æ·˜',
  'Ô¤' => 'æ¸¡',
  'Ô¥' => 'æ»”',
  'Ô¦' => 'æ¿¤',
  'Ô§' => 'ç‡¾',
  'Ô¨' => 'ç›œ',
  'Ô©' => 'ç�¹',
  'Ôª' => 'ç¦±',
  'Ô«' => 'ç¨»',
  'Ô¬' => 'è�„',
  'Ô­' => 'è¦©',
  'Ô®' => 'è³­',
  'Ô¯' => 'è·³',
  'Ô°' => 'è¹ˆ',
  'Ô±' => 'é€ƒ',
  'Ô²' => 'é€”',
  'Ô³' => 'é�“',
  'Ô´' => 'éƒ½',
  'Ôµ' => 'é��',
  'Ô¶' => 'é™¶',
  'Ô·' => 'éŸœ',
  'Ô¸' => 'æ¯’',
  'Ô¹' => 'ç€†',
  'Ôº' => 'ç‰˜',
  'Ô»' => 'çŠ¢',
  'Ô¼' => 'ç�¨',
  'Ô½' => 'ç�£',
  'Ô¾' => 'ç¦¿',
  'Ô¿' => 'ç¯¤',
  'ÔÀ' => 'çº›',
  'ÔÁ' => 'è®€',
  'ÔÂ' => 'å¢©',
  'ÔÃ' => 'æƒ‡',
  'ÔÄ' => 'æ•¦',
  'ÔÅ' => 'æ—½',
  'ÔÆ' => 'æš¾',
  'ÔÇ' => 'æ²Œ',
  'ÔÈ' => 'ç„ž',
  'ÔÉ' => 'ç‡‰',
  'ÔÊ' => 'è±š',
  'ÔË' => 'é “',
  'ÔÌ' => 'ä¹­',
  'ÔÍ' => 'çª�',
  'ÔÎ' => 'ä»�',
  'ÔÏ' => 'å†¬',
  'ÔÐ' => 'å‡�',
  'ÔÑ' => 'å‹•',
  'ÔÒ' => 'å�Œ',
  'ÔÓ' => 'æ†§',
  'ÔÔ' => 'æ�±',
  'ÔÕ' => 'æ¡�',
  'ÔÖ' => 'æ£Ÿ',
  'Ô×' => 'æ´ž',
  'ÔØ' => 'æ½¼',
  'ÔÙ' => 'ç–¼',
  'ÔÚ' => 'çž³',
  'ÔÛ' => 'ç«¥',
  'ÔÜ' => 'èƒ´',
  'ÔÝ' => 'è‘£',
  'ÔÞ' => 'éŠ…',
  'Ôß' => 'å…œ',
  'Ôà' => 'æ–—',
  'Ôá' => 'æ�œ',
  'Ôâ' => 'æž“',
  'Ôã' => 'ç—˜',
  'Ôä' => 'ç«‡',
  'Ôå' => 'è�³',
  'Ôæ' => 'ï¥š',
  'Ôç' => 'è±†',
  'Ôè' => 'é€—',
  'Ôé' => 'é ­',
  'Ôê' => 'å±¯',
  'Ôë' => 'è‡€',
  'Ôì' => 'èŠš',
  'Ôí' => 'é��',
  'Ôî' => 'é�¯',
  'Ôï' => 'éˆ�',
  'Ôð' => 'å¾—',
  'Ôñ' => 'å¶�',
  'Ôò' => 'æ©™',
  'Ôó' => 'ç‡ˆ',
  'Ôô' => 'ç™»',
  'Ôõ' => 'ç­‰',
  'Ôö' => 'è—¤',
  'Ô÷' => 'è¬„',
  'Ôø' => 'é„§',
  'Ôù' => 'é¨°',
  'Ôú' => 'å–‡',
  'Ôû' => 'æ‡¶',
  'Ôü' => 'ï¥›',
  'Ôý' => 'ç™©',
  'Ôþ' => 'ç¾…',
  'Õ¡' => 'è˜¿',
  'Õ¢' => 'èžº',
  'Õ£' => 'è£¸',
  'Õ¤' => 'é‚�',
  'Õ¥' => 'ï¥œ',
  'Õ¦' => 'æ´›',
  'Õ§' => 'çƒ™',
  'Õ¨' => 'ç�ž',
  'Õ©' => 'çµ¡',
  'Õª' => 'è�½',
  'Õ«' => 'ï¥�',
  'Õ¬' => 'é…ª',
  'Õ­' => 'é§±',
  'Õ®' => 'ï¥ž',
  'Õ¯' => 'äº‚',
  'Õ°' => 'å�µ',
  'Õ±' => 'æ¬„',
  'Õ²' => 'æ¬’',
  'Õ³' => 'ç€¾',
  'Õ´' => 'çˆ›',
  'Õµ' => 'è˜­',
  'Õ¶' => 'é¸ž',
  'Õ·' => 'å‰Œ',
  'Õ¸' => 'è¾£',
  'Õ¹' => 'åµ�',
  'Õº' => 'æ“¥',
  'Õ»' => 'æ”¬',
  'Õ¼' => 'æ¬–',
  'Õ½' => 'æ¿«',
  'Õ¾' => 'ç±ƒ',
  'Õ¿' => 'çºœ',
  'ÕÀ' => 'è—�',
  'ÕÁ' => 'è¥¤',
  'ÕÂ' => 'è¦½',
  'ÕÃ' => 'æ‹‰',
  'ÕÄ' => 'è‡˜',
  'ÕÅ' => 'è Ÿ',
  'ÕÆ' => 'å»Š',
  'ÕÇ' => 'æœ—',
  'ÕÈ' => 'æµª',
  'ÕÉ' => 'ç‹¼',
  'ÕÊ' => 'ç�…',
  'ÕË' => 'ç‘¯',
  'ÕÌ' => 'èž‚',
  'ÕÍ' => 'éƒž',
  'ÕÎ' => 'ä¾†',
  'ÕÏ' => 'å´�',
  'ÕÐ' => 'å¾ ',
  'ÕÑ' => 'è�Š',
  'ÕÒ' => 'å†·',
  'ÕÓ' => 'æŽ ',
  'ÕÔ' => 'ç•¥',
  'ÕÕ' => 'äº®',
  'ÕÖ' => 'å€†',
  'Õ×' => 'å…©',
  'ÕØ' => 'å‡‰',
  'ÕÙ' => 'æ¢�',
  'ÕÚ' => 'æ¨‘',
  'ÕÛ' => 'ç²®',
  'ÕÜ' => 'ç²±',
  'ÕÝ' => 'ç³§',
  'ÕÞ' => 'è‰¯',
  'Õß' => 'è«’',
  'Õà' => 'è¼›',
  'Õá' => 'é‡�',
  'Õâ' => 'ä¾¶',
  'Õã' => 'å„·',
  'Õä' => 'å‹µ',
  'Õå' => 'å‘‚',
  'Õæ' => 'å»¬',
  'Õç' => 'æ…®',
  'Õè' => 'æˆ¾',
  'Õé' => 'æ—…',
  'Õê' => 'æ«š',
  'Õë' => 'æ¿¾',
  'Õì' => 'ç¤ª',
  'Õí' => 'è—œ',
  'Õî' => 'è £',
  'Õï' => 'é–­',
  'Õð' => 'é©¢',
  'Õñ' => 'é©ª',
  'Õò' => 'éº—',
  'Õó' => 'é»Ž',
  'Õô' => 'åŠ›',
  'Õõ' => 'æ›†',
  'Õö' => 'æ­·',
  'Õ÷' => 'ç€�',
  'Õø' => 'ç¤«',
  'Õù' => 'è½¢',
  'Õú' => 'é�‚',
  'Õû' => 'æ†�',
  'Õü' => 'æˆ€',
  'Õý' => 'æ”£',
  'Õþ' => 'æ¼£',
  'Ö¡' => 'ç…‰',
  'Ö¢' => 'ç’‰',
  'Ö£' => 'ç·´',
  'Ö¤' => 'è�¯',
  'Ö¥' => 'è“®',
  'Ö¦' => 'è¼¦',
  'Ö§' => 'é€£',
  'Ö¨' => 'é�Š',
  'Ö©' => 'å†½',
  'Öª' => 'åˆ—',
  'Ö«' => 'åŠ£',
  'Ö¬' => 'æ´Œ',
  'Ö­' => 'çƒˆ',
  'Ö®' => 'è£‚',
  'Ö¯' => 'å»‰',
  'Ö°' => 'æ–‚',
  'Ö±' => 'æ®®',
  'Ö²' => 'æ¿‚',
  'Ö³' => 'ç°¾',
  'Ö´' => 'ç�µ',
  'Öµ' => 'ä»¤',
  'Ö¶' => 'ä¼¶',
  'Ö·' => 'å›¹',
  'Ö¸' => 'ï¥Ÿ',
  'Ö¹' => 'å²º',
  'Öº' => 'å¶º',
  'Ö»' => 'æ€œ',
  'Ö¼' => 'çŽ²',
  'Ö½' => 'ç¬­',
  'Ö¾' => 'ç¾š',
  'Ö¿' => 'ç¿Ž',
  'ÖÀ' => 'è�†',
  'ÖÁ' => 'é€ž',
  'ÖÂ' => 'éˆ´',
  'ÖÃ' => 'é›¶',
  'ÖÄ' => 'é�ˆ',
  'ÖÅ' => 'é ˜',
  'ÖÆ' => 'é½¡',
  'ÖÇ' => 'ä¾‹',
  'ÖÈ' => 'æ¾§',
  'ÖÉ' => 'ç¦®',
  'ÖÊ' => 'é†´',
  'ÖË' => 'éš·',
  'ÖÌ' => 'å‹ž',
  'ÖÍ' => 'ï¥ ',
  'ÖÎ' => 'æ’ˆ',
  'ÖÏ' => 'æ“„',
  'ÖÐ' => 'æ«“',
  'ÖÑ' => 'æ½ž',
  'ÖÒ' => 'ç€˜',
  'ÖÓ' => 'çˆ�',
  'ÖÔ' => 'ç›§',
  'ÖÕ' => 'è€�',
  'ÖÖ' => 'è˜†',
  'Ö×' => 'è™œ',
  'ÖØ' => 'è·¯',
  'ÖÙ' => 'è¼…',
  'ÖÚ' => 'éœ²',
  'ÖÛ' => 'é­¯',
  'ÖÜ' => 'é·º',
  'ÖÝ' => 'é¹µ',
  'ÖÞ' => 'ç¢Œ',
  'Öß' => 'ç¥¿',
  'Öà' => 'ç¶ ',
  'Öá' => 'è�‰',
  'Öâ' => 'éŒ„',
  'Öã' => 'é¹¿',
  'Öä' => 'éº“',
  'Öå' => 'è«–',
  'Öæ' => 'å£Ÿ',
  'Öç' => 'å¼„',
  'Öè' => 'æœ§',
  'Öé' => 'ç€§',
  'Öê' => 'ç“�',
  'Öë' => 'ç± ',
  'Öì' => 'è�¾',
  'Öí' => 'å„¡',
  'Öî' => 'ç€¨',
  'Öï' => 'ç‰¢',
  'Öð' => 'ç£Š',
  'Öñ' => 'è³‚',
  'Öò' => 'è³š',
  'Öó' => 'è³´',
  'Öô' => 'é›·',
  'Öõ' => 'äº†',
  'Öö' => 'åƒš',
  'Ö÷' => 'å¯®',
  'Öø' => 'å»–',
  'Öù' => 'æ–™',
  'Öú' => 'ç‡Ž',
  'Öû' => 'ç™‚',
  'Öü' => 'çž­',
  'Öý' => 'è�Š',
  'Öþ' => 'è“¼',
  '×¡' => 'é�¼',
  '×¢' => 'é¬§',
  '×£' => 'é¾�',
  '×¤' => 'å£˜',
  '×¥' => 'å©�',
  '×¦' => 'å±¢',
  '×§' => 'æ¨“',
  '×¨' => 'æ·š',
  '×©' => 'æ¼�',
  '×ª' => 'ç˜»',
  '×«' => 'ç´¯',
  '×¬' => 'ç¸·',
  '×­' => 'è”ž',
  '×®' => 'è¤¸',
  '×¯' => 'é�¤',
  '×°' => 'é™‹',
  '×±' => 'åŠ‰',
  '×²' => 'æ—’',
  '×³' => 'æŸ³',
  '×´' => 'æ¦´',
  '×µ' => 'æµ�',
  '×¶' => 'æºœ',
  '×·' => 'ç€�',
  '×¸' => 'ç�‰',
  '×¹' => 'ç‘ ',
  '×º' => 'ç•™',
  '×»' => 'ç˜¤',
  '×¼' => 'ç¡«',
  '×½' => 'è¬¬',
  '×¾' => 'é¡ž',
  '×¿' => 'å…­',
  '×À' => 'æˆ®',
  '×Á' => 'é™¸',
  '×Â' => 'ä¾–',
  '×Ã' => 'å€«',
  '×Ä' => 'å´™',
  '×Å' => 'æ·ª',
  '×Æ' => 'ç¶¸',
  '×Ç' => 'è¼ª',
  '×È' => 'å¾‹',
  '×É' => 'æ…„',
  '×Ê' => 'æ —',
  '×Ë' => 'ï¥¡',
  '×Ì' => 'éš†',
  '×Í' => 'å‹’',
  '×Î' => 'è‚‹',
  '×Ï' => 'å‡œ',
  '×Ð' => 'å‡Œ',
  '×Ñ' => 'æ¥ž',
  '×Ò' => 'ç¨œ',
  '×Ó' => 'ç¶¾',
  '×Ô' => 'è�±',
  '×Õ' => 'é™µ',
  '×Ö' => 'ä¿š',
  '××' => 'åˆ©',
  '×Ø' => 'åŽ˜',
  '×Ù' => 'å��',
  '×Ú' => 'å”Ž',
  '×Û' => 'å±¥',
  '×Ü' => 'æ‚§',
  '×Ý' => 'æ�Ž',
  '×Þ' => 'æ¢¨',
  '×ß' => 'æµ¬',
  '×à' => 'çŠ�',
  '×á' => 'ç‹¸',
  '×â' => 'ç�†',
  '×ã' => 'ç’ƒ',
  '×ä' => 'ï¥¢',
  '×å' => 'ç—¢',
  '×æ' => 'ç±¬',
  '×ç' => 'ç½¹',
  '×è' => 'ç¾¸',
  '×é' => 'èŽ‰',
  '×ê' => 'è£�',
  '×ë' => 'è£¡',
  '×ì' => 'é‡Œ',
  '×í' => 'é‡�',
  '×î' => 'é›¢',
  '×ï' => 'é¯‰',
  '×ð' => 'å��',
  '×ñ' => 'æ½¾',
  '×ò' => 'ç‡�',
  '×ó' => 'ç’˜',
  '×ô' => 'è—º',
  '×õ' => 'èºª',
  '×ö' => 'éš£',
  '×÷' => 'é±—',
  '×ø' => 'éºŸ',
  '×ù' => 'æž—',
  '×ú' => 'æ·‹',
  '×û' => 'ç�³',
  '×ü' => 'è‡¨',
  '×ý' => 'éœ–',
  '×þ' => 'ç ¬',
  'Ø¡' => 'ç«‹',
  'Ø¢' => 'ç¬ ',
  'Ø£' => 'ç²’',
  'Ø¤' => 'æ‘©',
  'Ø¥' => 'ç‘ª',
  'Ø¦' => 'ç—²',
  'Ø§' => 'ç¢¼',
  'Ø¨' => 'ç£¨',
  'Ø©' => 'é¦¬',
  'Øª' => 'é­”',
  'Ø«' => 'éº»',
  'Ø¬' => 'å¯ž',
  'Ø­' => 'å¹•',
  'Ø®' => 'æ¼ ',
  'Ø¯' => 'è†œ',
  'Ø°' => 'èŽ«',
  'Ø±' => 'é‚ˆ',
  'Ø²' => 'ä¸‡',
  'Ø³' => 'å��',
  'Ø´' => 'å¨©',
  'Øµ' => 'å·’',
  'Ø¶' => 'å½Ž',
  'Ø·' => 'æ…¢',
  'Ø¸' => 'æŒ½',
  'Ø¹' => 'æ™©',
  'Øº' => 'æ›¼',
  'Ø»' => 'æ»¿',
  'Ø¼' => 'æ¼«',
  'Ø½' => 'ç�£',
  'Ø¾' => 'çžž',
  'Ø¿' => 'è�¬',
  'ØÀ' => 'è”“',
  'ØÁ' => 'è »',
  'ØÂ' => 'è¼“',
  'ØÃ' => 'é¥…',
  'ØÄ' => 'é°»',
  'ØÅ' => 'å”œ',
  'ØÆ' => 'æŠ¹',
  'ØÇ' => 'æœ«',
  'ØÈ' => 'æ²«',
  'ØÉ' => 'èŒ‰',
  'ØÊ' => 'è¥ª',
  'ØË' => 'é�º',
  'ØÌ' => 'äº¡',
  'ØÍ' => 'å¦„',
  'ØÎ' => 'å¿˜',
  'ØÏ' => 'å¿™',
  'ØÐ' => 'æœ›',
  'ØÑ' => 'ç¶²',
  'ØÒ' => 'ç½”',
  'ØÓ' => 'èŠ’',
  'ØÔ' => 'èŒ«',
  'ØÕ' => 'èŽ½',
  'ØÖ' => 'è¼ž',
  'Ø×' => 'é‚™',
  'ØØ' => 'åŸ‹',
  'ØÙ' => 'å¦¹',
  'ØÚ' => 'åª’',
  'ØÛ' => 'å¯�',
  'ØÜ' => 'æ˜§',
  'ØÝ' => 'æžš',
  'ØÞ' => 'æ¢…',
  'Øß' => 'æ¯�',
  'Øà' => 'ç…¤',
  'Øá' => 'ç½µ',
  'Øâ' => 'è²·',
  'Øã' => 'è³£',
  'Øä' => 'é‚�',
  'Øå' => 'é­…',
  'Øæ' => 'è„ˆ',
  'Øç' => 'è²Š',
  'Øè' => 'é™Œ',
  'Øé' => 'é©€',
  'Øê' => 'éº¥',
  'Øë' => 'å­Ÿ',
  'Øì' => 'æ°“',
  'Øí' => 'çŒ›',
  'Øî' => 'ç›²',
  'Øï' => 'ç›Ÿ',
  'Øð' => 'è�Œ',
  'Øñ' => 'å†ª',
  'Øò' => 'è¦“',
  'Øó' => 'å…�',
  'Øô' => 'å†•',
  'Øõ' => 'å‹‰',
  'Øö' => 'æ£‰',
  'Ø÷' => 'æ²”',
  'Øø' => 'çœ„',
  'Øù' => 'çœ ',
  'Øú' => 'ç¶¿',
  'Øû' => 'ç·¬',
  'Øü' => 'é�¢',
  'Øý' => 'éºµ',
  'Øþ' => 'æ»…',
  'Ù¡' => 'è”‘',
  'Ù¢' => 'å†¥',
  'Ù£' => 'å��',
  'Ù¤' => 'å‘½',
  'Ù¥' => 'æ˜Ž',
  'Ù¦' => 'æš�',
  'Ù§' => 'æ¤§',
  'Ù¨' => 'æºŸ',
  'Ù©' => 'çš¿',
  'Ùª' => 'çž‘',
  'Ù«' => 'èŒ—',
  'Ù¬' => 'è“‚',
  'Ù­' => 'èžŸ',
  'Ù®' => 'é…©',
  'Ù¯' => 'éŠ˜',
  'Ù°' => 'é³´',
  'Ù±' => 'è¢‚',
  'Ù²' => 'ä¾®',
  'Ù³' => 'å†’',
  'Ù´' => 'å‹Ÿ',
  'Ùµ' => 'å§†',
  'Ù¶' => 'å¸½',
  'Ù·' => 'æ…•',
  'Ù¸' => 'æ‘¸',
  'Ù¹' => 'æ‘¹',
  'Ùº' => 'æš®',
  'Ù»' => 'æŸ�',
  'Ù¼' => 'æ¨¡',
  'Ù½' => 'æ¯�',
  'Ù¾' => 'æ¯›',
  'Ù¿' => 'ç‰Ÿ',
  'ÙÀ' => 'ç‰¡',
  'ÙÁ' => 'ç‘�',
  'ÙÂ' => 'çœ¸',
  'ÙÃ' => 'çŸ›',
  'ÙÄ' => 'è€—',
  'ÙÅ' => 'èŠ¼',
  'ÙÆ' => 'èŒ…',
  'ÙÇ' => 'è¬€',
  'ÙÈ' => 'è¬¨',
  'ÙÉ' => 'è²Œ',
  'ÙÊ' => 'æœ¨',
  'ÙË' => 'æ²�',
  'ÙÌ' => 'ç‰§',
  'ÙÍ' => 'ç›®',
  'ÙÎ' => 'ç�¦',
  'ÙÏ' => 'ç©†',
  'ÙÐ' => 'é¶©',
  'ÙÑ' => 'æ­¿',
  'ÙÒ' => 'æ²’',
  'ÙÓ' => 'å¤¢',
  'ÙÔ' => 'æœ¦',
  'ÙÕ' => 'è’™',
  'ÙÖ' => 'å�¯',
  'Ù×' => 'å¢“',
  'ÙØ' => 'å¦™',
  'ÙÙ' => 'å»Ÿ',
  'ÙÚ' => 'æ��',
  'ÙÛ' => 'æ˜´',
  'ÙÜ' => 'æ�³',
  'ÙÝ' => 'æ¸º',
  'ÙÞ' => 'çŒ«',
  'Ùß' => 'ç«—',
  'Ùà' => 'è‹—',
  'Ùá' => 'éŒ¨',
  'Ùâ' => 'å‹™',
  'Ùã' => 'å·«',
  'Ùä' => 'æ†®',
  'Ùå' => 'æ‡‹',
  'Ùæ' => 'æˆŠ',
  'Ùç' => 'æ‹‡',
  'Ùè' => 'æ’«',
  'Ùé' => 'æ— ',
  'Ùê' => 'æ¥™',
  'Ùë' => 'æ­¦',
  'Ùì' => 'æ¯‹',
  'Ùí' => 'ç„¡',
  'Ùî' => 'ç�·',
  'Ùï' => 'ç•�',
  'Ùð' => 'ç¹†',
  'Ùñ' => 'èˆž',
  'Ùò' => 'èŒ‚',
  'Ùó' => 'è•ª',
  'Ùô' => 'èª£',
  'Ùõ' => 'è²¿',
  'Ùö' => 'éœ§',
  'Ù÷' => 'éµ¡',
  'Ùø' => 'å¢¨',
  'Ùù' => 'é»˜',
  'Ùú' => 'å€‘',
  'Ùû' => 'åˆŽ',
  'Ùü' => 'å�»',
  'Ùý' => 'å•�',
  'Ùþ' => 'æ–‡',
  'Ú¡' => 'æ±¶',
  'Ú¢' => 'ç´Š',
  'Ú£' => 'ç´‹',
  'Ú¤' => 'è�ž',
  'Ú¥' => 'èšŠ',
  'Ú¦' => 'é–€',
  'Ú§' => 'é›¯',
  'Ú¨' => 'å‹¿',
  'Ú©' => 'æ²•',
  'Úª' => 'ç‰©',
  'Ú«' => 'å‘³',
  'Ú¬' => 'åªš',
  'Ú­' => 'å°¾',
  'Ú®' => 'åµ‹',
  'Ú¯' => 'å½Œ',
  'Ú°' => 'å¾®',
  'Ú±' => 'æœª',
  'Ú²' => 'æ¢¶',
  'Ú³' => 'æ¥£',
  'Ú´' => 'æ¸¼',
  'Úµ' => 'æ¹„',
  'Ú¶' => 'çœ‰',
  'Ú·' => 'ç±³',
  'Ú¸' => 'ç¾Ž',
  'Ú¹' => 'è–‡',
  'Úº' => 'è¬Ž',
  'Ú»' => 'è¿·',
  'Ú¼' => 'é�¡',
  'Ú½' => 'é»´',
  'Ú¾' => 'å²·',
  'Ú¿' => 'æ‚¶',
  'ÚÀ' => 'æ„�',
  'ÚÁ' => 'æ†«',
  'ÚÂ' => 'æ•�',
  'ÚÃ' => 'æ—»',
  'ÚÄ' => 'æ—¼',
  'ÚÅ' => 'æ°‘',
  'ÚÆ' => 'æ³¯',
  'ÚÇ' => 'çŽŸ',
  'ÚÈ' => 'ç�‰',
  'ÚÉ' => 'ç·¡',
  'ÚÊ' => 'é–”',
  'ÚË' => 'å¯†',
  'ÚÌ' => 'èœœ',
  'ÚÍ' => 'è¬�',
  'ÚÎ' => 'å‰�',
  'ÚÏ' => 'å�š',
  'ÚÐ' => 'æ‹�',
  'ÚÑ' => 'æ��',
  'ÚÒ' => 'æ’²',
  'ÚÓ' => 'æœ´',
  'ÚÔ' => 'æ¨¸',
  'ÚÕ' => 'æ³Š',
  'ÚÖ' => 'ç�€',
  'Ú×' => 'ç’ž',
  'ÚØ' => 'ç®”',
  'ÚÙ' => 'ç²•',
  'ÚÚ' => 'ç¸›',
  'ÚÛ' => 'è†Š',
  'ÚÜ' => 'èˆ¶',
  'ÚÝ' => 'è–„',
  'ÚÞ' => 'è¿«',
  'Úß' => 'é›¹',
  'Úà' => 'é§�',
  'Úá' => 'ä¼´',
  'Úâ' => 'å�Š',
  'Úã' => 'å��',
  'Úä' => 'å�›',
  'Úå' => 'æ‹Œ',
  'Úæ' => 'æ�¬',
  'Úç' => 'æ”€',
  'Úè' => 'æ–‘',
  'Úé' => 'æ§ƒ',
  'Úê' => 'æ³®',
  'Úë' => 'æ½˜',
  'Úì' => 'ç�­',
  'Úí' => 'ç•”',
  'Úî' => 'ç˜¢',
  'Úï' => 'ç›¤',
  'Úð' => 'ç›¼',
  'Úñ' => 'ç£�',
  'Úò' => 'ç£»',
  'Úó' => 'ç¤¬',
  'Úô' => 'çµ†',
  'Úõ' => 'èˆ¬',
  'Úö' => 'èŸ ',
  'Ú÷' => 'è¿”',
  'Úø' => 'é ’',
  'Úù' => 'é£¯',
  'Úú' => 'å‹ƒ',
  'Úû' => 'æ‹”',
  'Úü' => 'æ’¥',
  'Úý' => 'æ¸¤',
  'Úþ' => 'æ½‘',
  'Û¡' => 'ç™¼',
  'Û¢' => 'è·‹',
  'Û£' => 'é†±',
  'Û¤' => 'é‰¢',
  'Û¥' => 'é«®',
  'Û¦' => 'é­ƒ',
  'Û§' => 'å€£',
  'Û¨' => 'å‚�',
  'Û©' => 'å�Š',
  'Ûª' => 'å¦¨',
  'Û«' => 'å°¨',
  'Û¬' => 'å¹‡',
  'Û­' => 'å½·',
  'Û®' => 'æˆ¿',
  'Û¯' => 'æ”¾',
  'Û°' => 'æ–¹',
  'Û±' => 'æ—�',
  'Û²' => 'æ˜‰',
  'Û³' => 'æž‹',
  'Û´' => 'æ¦œ',
  'Ûµ' => 'æ»‚',
  'Û¶' => 'ç£…',
  'Û·' => 'ç´¡',
  'Û¸' => 'è‚ª',
  'Û¹' => 'è†€',
  'Ûº' => 'èˆ«',
  'Û»' => 'èŠ³',
  'Û¼' => 'è’¡',
  'Û½' => 'èšŒ',
  'Û¾' => 'è¨ª',
  'Û¿' => 'è¬—',
  'ÛÀ' => 'é‚¦',
  'ÛÁ' => 'é˜²',
  'ÛÂ' => 'é¾�',
  'ÛÃ' => 'å€�',
  'ÛÄ' => 'ä¿³',
  'ÛÅ' => 'ï¥£',
  'ÛÆ' => 'åŸ¹',
  'ÛÇ' => 'å¾˜',
  'ÛÈ' => 'æ‹œ',
  'ÛÉ' => 'æŽ’',
  'ÛÊ' => 'æ�¯',
  'ÛË' => 'æ¹ƒ',
  'ÛÌ' => 'ç„™',
  'ÛÍ' => 'ç›ƒ',
  'ÛÎ' => 'èƒŒ',
  'ÛÏ' => 'èƒš',
  'ÛÐ' => 'è£´',
  'ÛÑ' => 'è£µ',
  'ÛÒ' => 'è¤™',
  'ÛÓ' => 'è³ ',
  'ÛÔ' => 'è¼©',
  'ÛÕ' => 'é…�',
  'ÛÖ' => 'é™ª',
  'Û×' => 'ä¼¯',
  'ÛØ' => 'ä½°',
  'ÛÙ' => 'å¸›',
  'ÛÚ' => 'æŸ�',
  'ÛÛ' => 'æ ¢',
  'ÛÜ' => 'ç™½',
  'ÛÝ' => 'ç™¾',
  'ÛÞ' => 'é­„',
  'Ûß' => 'å¹¡',
  'Ûà' => 'æ¨Š',
  'Ûá' => 'ç…©',
  'Ûâ' => 'ç‡”',
  'Ûã' => 'ç•ª',
  'Ûä' => 'ï¥¤',
  'Ûå' => 'ç¹�',
  'Ûæ' => 'è•ƒ',
  'Ûç' => 'è—©',
  'Ûè' => 'é£œ',
  'Ûé' => 'ä¼�',
  'Ûê' => 'ç­�',
  'Ûë' => 'ç½°',
  'Ûì' => 'é–¥',
  'Ûí' => 'å‡¡',
  'Ûî' => 'å¸†',
  'Ûï' => 'æ¢µ',
  'Ûð' => 'æ°¾',
  'Ûñ' => 'æ±Ž',
  'Ûò' => 'æ³›',
  'Ûó' => 'çŠ¯',
  'Ûô' => 'ç¯„',
  'Ûõ' => 'èŒƒ',
  'Ûö' => 'æ³•',
  'Û÷' => 'ç�º',
  'Ûø' => 'åƒ»',
  'Ûù' => 'åŠˆ',
  'Ûú' => 'å£�',
  'Ûû' => 'æ“˜',
  'Ûü' => 'æª—',
  'Ûý' => 'ç’§',
  'Ûþ' => 'ç™–',
  'Ü¡' => 'ç¢§',
  'Ü¢' => 'è˜—',
  'Ü£' => 'é—¢',
  'Ü¤' => 'éœ¹',
  'Ü¥' => 'ï¥¥',
  'Ü¦' => 'å�ž',
  'Ü§' => 'å¼�',
  'Ü¨' => 'è®Š',
  'Ü©' => 'è¾¨',
  'Üª' => 'è¾¯',
  'Ü«' => 'é‚Š',
  'Ü¬' => 'åˆ¥',
  'Ü­' => 'çž¥',
  'Ü®' => 'é±‰',
  'Ü¯' => 'é¼ˆ',
  'Ü°' => 'ä¸™',
  'Ü±' => 'å€‚',
  'Ü²' => 'å…µ',
  'Ü³' => 'å±›',
  'Ü´' => 'å¹·',
  'Üµ' => 'æ˜ž',
  'Ü¶' => 'æ˜º',
  'Ü·' => 'æŸ„',
  'Ü¸' => 'æ£…',
  'Ü¹' => 'ç‚³',
  'Üº' => 'ç”�',
  'Ü»' => 'ç—…',
  'Ü¼' => 'ç§‰',
  'Ü½' => 'ç«�',
  'Ü¾' => 'è¼§',
  'Ü¿' => 'é¤ ',
  'ÜÀ' => 'é¨ˆ',
  'ÜÁ' => 'ä¿�',
  'ÜÂ' => 'å ¡',
  'ÜÃ' => 'å ±',
  'ÜÄ' => 'å¯¶',
  'ÜÅ' => 'æ™®',
  'ÜÆ' => 'æ­¥',
  'ÜÇ' => 'æ´‘',
  'ÜÈ' => 'æ¹º',
  'ÜÉ' => 'æ½½',
  'ÜÊ' => 'ç�¤',
  'ÜË' => 'ç”«',
  'ÜÌ' => 'è�©',
  'ÜÍ' => 'è£œ',
  'ÜÎ' => 'è¤“',
  'ÜÏ' => 'è­œ',
  'ÜÐ' => 'è¼”',
  'ÜÑ' => 'ä¼�',
  'ÜÒ' => 'åƒ•',
  'ÜÓ' => 'åŒ�',
  'ÜÔ' => 'å�œ',
  'ÜÕ' => 'å®“',
  'ÜÖ' => 'å¾©',
  'Ü×' => 'æœ�',
  'ÜØ' => 'ç¦�',
  'ÜÙ' => 'è…¹',
  'ÜÚ' => 'èŒ¯',
  'ÜÛ' => 'è””',
  'ÜÜ' => 'è¤‡',
  'ÜÝ' => 'è¦†',
  'ÜÞ' => 'è¼¹',
  'Üß' => 'è¼»',
  'Üà' => 'é¦¥',
  'Üá' => 'é°’',
  'Üâ' => 'æœ¬',
  'Üã' => 'ä¹¶',
  'Üä' => 'ä¿¸',
  'Üå' => 'å¥‰',
  'Üæ' => 'å°�',
  'Üç' => 'å³¯',
  'Üè' => 'å³°',
  'Üé' => 'æ�§',
  'Üê' => 'æ£’',
  'Üë' => 'çƒ½',
  'Üì' => 'ç†¢',
  'Üí' => 'ç�«',
  'Üî' => 'ç¸«',
  'Üï' => 'è“¬',
  'Üð' => 'èœ‚',
  'Üñ' => 'é€¢',
  'Üò' => 'é‹’',
  'Üó' => 'é³³',
  'Üô' => 'ä¸�',
  'Üõ' => 'ä»˜',
  'Üö' => 'ä¿¯',
  'Ü÷' => 'å‚…',
  'Üø' => 'å‰–',
  'Üù' => 'å‰¯',
  'Üú' => 'å�¦',
  'Üû' => 'å’�',
  'Üü' => 'åŸ ',
  'Üý' => 'å¤«',
  'Üþ' => 'å©¦',
  'Ý¡' => 'å­š',
  'Ý¢' => 'å­µ',
  'Ý£' => 'å¯Œ',
  'Ý¤' => 'åºœ',
  'Ý¥' => 'ï¥¦',
  'Ý¦' => 'æ‰¶',
  'Ý§' => 'æ•·',
  'Ý¨' => 'æ–§',
  'Ý©' => 'æµ®',
  'Ýª' => 'æº¥',
  'Ý«' => 'çˆ¶',
  'Ý¬' => 'ç¬¦',
  'Ý­' => 'ç°¿',
  'Ý®' => 'ç¼¶',
  'Ý¯' => 'è…�',
  'Ý°' => 'è…‘',
  'Ý±' => 'è†š',
  'Ý²' => 'è‰€',
  'Ý³' => 'èŠ™',
  'Ý´' => 'èŽ©',
  'Ýµ' => 'è¨ƒ',
  'Ý¶' => 'è² ',
  'Ý·' => 'è³¦',
  'Ý¸' => 'è³»',
  'Ý¹' => 'èµ´',
  'Ýº' => 'è¶º',
  'Ý»' => 'éƒ¨',
  'Ý¼' => 'é‡œ',
  'Ý½' => 'é˜œ',
  'Ý¾' => 'é™„',
  'Ý¿' => 'é§™',
  'ÝÀ' => 'é³§',
  'ÝÁ' => 'åŒ—',
  'ÝÂ' => 'åˆ†',
  'ÝÃ' => 'å�©',
  'ÝÄ' => 'å™´',
  'ÝÅ' => 'å¢³',
  'ÝÆ' => 'å¥”',
  'ÝÇ' => 'å¥®',
  'ÝÈ' => 'å¿¿',
  'ÝÉ' => 'æ†¤',
  'ÝÊ' => 'æ‰®',
  'ÝË' => 'æ˜�',
  'ÝÌ' => 'æ±¾',
  'ÝÍ' => 'ç„š',
  'ÝÎ' => 'ç›†',
  'ÝÏ' => 'ç²‰',
  'ÝÐ' => 'ç³ž',
  'ÝÑ' => 'ç´›',
  'ÝÒ' => 'èŠ¬',
  'ÝÓ' => 'è³�',
  'ÝÔ' => 'é›°',
  'ÝÕ' => 'ï¥§',
  'ÝÖ' => 'ä½›',
  'Ý×' => 'å¼—',
  'ÝØ' => 'å½¿',
  'ÝÙ' => 'æ‹‚',
  'ÝÚ' => 'å´©',
  'ÝÛ' => 'æœ‹',
  'ÝÜ' => 'æ£š',
  'ÝÝ' => 'ç¡¼',
  'ÝÞ' => 'ç¹ƒ',
  'Ýß' => 'éµ¬',
  'Ýà' => 'ä¸•',
  'Ýá' => 'å‚™',
  'Ýâ' => 'åŒ•',
  'Ýã' => 'åŒª',
  'Ýä' => 'å�‘',
  'Ýå' => 'å¦ƒ',
  'Ýæ' => 'å©¢',
  'Ýç' => 'åº‡',
  'Ýè' => 'æ‚²',
  'Ýé' => 'æ†Š',
  'Ýê' => 'æ‰‰',
  'Ýë' => 'æ‰¹',
  'Ýì' => 'æ–�',
  'Ýí' => 'æž‡',
  'Ýî' => 'æ¦§',
  'Ýï' => 'æ¯”',
  'Ýð' => 'æ¯–',
  'Ýñ' => 'æ¯—',
  'Ýò' => 'æ¯˜',
  'Ýó' => 'æ²¸',
  'Ýô' => 'ï¥¨',
  'Ýõ' => 'ç�µ',
  'Ýö' => 'ç—º',
  'Ý÷' => 'ç ’',
  'Ýø' => 'ç¢‘',
  'Ýù' => 'ç§•',
  'Ýú' => 'ç§˜',
  'Ýû' => 'ç²ƒ',
  'Ýü' => 'ç·‹',
  'Ýý' => 'ç¿¡',
  'Ýþ' => 'è‚¥',
  'Þ¡' => 'è„¾',
  'Þ¢' => 'è‡‚',
  'Þ£' => 'è�²',
  'Þ¤' => 'èœš',
  'Þ¥' => 'è£¨',
  'Þ¦' => 'èª¹',
  'Þ§' => 'è­¬',
  'Þ¨' => 'è²»',
  'Þ©' => 'é„™',
  'Þª' => 'é�ž',
  'Þ«' => 'é£›',
  'Þ¬' => 'é¼»',
  'Þ­' => 'åš¬',
  'Þ®' => 'å¬ª',
  'Þ¯' => 'å½¬',
  'Þ°' => 'æ–Œ',
  'Þ±' => 'æª³',
  'Þ²' => 'æ®¯',
  'Þ³' => 'æµœ',
  'Þ´' => 'æ¿±',
  'Þµ' => 'ç€•',
  'Þ¶' => 'ç‰�',
  'Þ·' => 'çŽ­',
  'Þ¸' => 'è²§',
  'Þ¹' => 'è³“',
  'Þº' => 'é »',
  'Þ»' => 'æ†‘',
  'Þ¼' => 'æ°·',
  'Þ½' => 'è�˜',
  'Þ¾' => 'é¨�',
  'Þ¿' => 'ä¹�',
  'ÞÀ' => 'äº‹',
  'ÞÁ' => 'äº›',
  'ÞÂ' => 'ä»•',
  'ÞÃ' => 'ä¼º',
  'ÞÄ' => 'ä¼¼',
  'ÞÅ' => 'ä½¿',
  'ÞÆ' => 'ä¿Ÿ',
  'ÞÇ' => 'åƒ¿',
  'ÞÈ' => 'å�²',
  'ÞÉ' => 'å�¸',
  'ÞÊ' => 'å”†',
  'ÞË' => 'å—£',
  'ÞÌ' => 'å››',
  'ÞÍ' => 'å£«',
  'ÞÎ' => 'å¥¢',
  'ÞÏ' => 'å¨‘',
  'ÞÐ' => 'å¯«',
  'ÞÑ' => 'å¯º',
  'ÞÒ' => 'å°„',
  'ÞÓ' => 'å·³',
  'ÞÔ' => 'å¸«',
  'ÞÕ' => 'å¾™',
  'ÞÖ' => 'æ€�',
  'Þ×' => 'æ�¨',
  'ÞØ' => 'æ–œ',
  'ÞÙ' => 'æ–¯',
  'ÞÚ' => 'æŸ¶',
  'ÞÛ' => 'æŸ»',
  'ÞÜ' => 'æ¢­',
  'ÞÝ' => 'æ­»',
  'ÞÞ' => 'æ²™',
  'Þß' => 'æ³—',
  'Þà' => 'æ¸£',
  'Þá' => 'ç€‰',
  'Þâ' => 'ç�…',
  'Þã' => 'ç ‚',
  'Þä' => 'ç¤¾',
  'Þå' => 'ç¥€',
  'Þæ' => 'ç¥ ',
  'Þç' => 'ç§�',
  'Þè' => 'ç¯©',
  'Þé' => 'ç´—',
  'Þê' => 'çµ²',
  'Þë' => 'è‚†',
  'Þì' => 'èˆ�',
  'Þí' => 'èŽŽ',
  'Þî' => 'è“‘',
  'Þï' => 'è›‡',
  'Þð' => 'è£Ÿ',
  'Þñ' => 'è©�',
  'Þò' => 'è©ž',
  'Þó' => 'è¬�',
  'Þô' => 'è³œ',
  'Þõ' => 'èµ¦',
  'Þö' => 'è¾­',
  'Þ÷' => 'é‚ª',
  'Þø' => 'é£¼',
  'Þù' => 'é§Ÿ',
  'Þú' => 'éº�',
  'Þû' => 'å‰Š',
  'Þü' => 'ï¥©',
  'Þý' => 'æœ”',
  'Þþ' => 'ï¥ª',
  'ß¡' => 'å‚˜',
  'ß¢' => 'åˆª',
  'ß£' => 'å±±',
  'ß¤' => 'æ•£',
  'ß¥' => 'æ±•',
  'ß¦' => 'ç�Š',
  'ß§' => 'ç”£',
  'ß¨' => 'ç–�',
  'ß©' => 'ç®—',
  'ßª' => 'è’œ',
  'ß«' => 'é…¸',
  'ß¬' => 'éœ°',
  'ß­' => 'ä¹·',
  'ß®' => 'æ’’',
  'ß¯' => 'æ®º',
  'ß°' => 'ç…ž',
  'ß±' => 'è–©',
  'ß²' => 'ä¸‰',
  'ß³' => 'ï¥«',
  'ß´' => 'æ�‰',
  'ßµ' => 'æ£®',
  'ß¶' => 'æ¸—',
  'ß·' => 'èŠŸ',
  'ß¸' => 'è”˜',
  'ß¹' => 'è¡«',
  'ßº' => 'æ�·',
  'ß»' => 'æ¾�',
  'ß¼' => 'éˆ’',
  'ß½' => 'é¢¯',
  'ß¾' => 'ä¸Š',
  'ß¿' => 'å‚·',
  'ßÀ' => 'åƒ�',
  'ßÁ' => 'å„Ÿ',
  'ßÂ' => 'å•†',
  'ßÃ' => 'å–ª',
  'ßÄ' => 'å˜—',
  'ßÅ' => 'å­€',
  'ßÆ' => 'å°™',
  'ßÇ' => 'å³ ',
  'ßÈ' => 'å¸¸',
  'ßÉ' => 'åºŠ',
  'ßÊ' => 'åº ',
  'ßË' => 'å»‚',
  'ßÌ' => 'æƒ³',
  'ßÍ' => 'æ¡‘',
  'ßÎ' => 'æ©¡',
  'ßÏ' => 'æ¹˜',
  'ßÐ' => 'çˆ½',
  'ßÑ' => 'ç‰€',
  'ßÒ' => 'ç‹€',
  'ßÓ' => 'ç›¸',
  'ßÔ' => 'ç¥¥',
  'ßÕ' => 'ç®±',
  'ßÖ' => 'ç¿”',
  'ß×' => 'è£³',
  'ßØ' => 'è§´',
  'ßÙ' => 'è©³',
  'ßÚ' => 'è±¡',
  'ßÛ' => 'è³ž',
  'ßÜ' => 'éœœ',
  'ßÝ' => 'å¡ž',
  'ßÞ' => 'ç’½',
  'ßß' => 'è³½',
  'ßà' => 'å—‡',
  'ßá' => 'ï¥¬',
  'ßâ' => 'ç©¡',
  'ßã' => 'ç´¢',
  'ßä' => 'è‰²',
  'ßå' => 'ç‰²',
  'ßæ' => 'ç”Ÿ',
  'ßç' => 'ç”¥',
  'ßè' => 'ï¥­',
  'ßé' => 'ç¬™',
  'ßê' => 'å¢…',
  'ßë' => 'å£»',
  'ßì' => 'å¶¼',
  'ßí' => 'åº�',
  'ßî' => 'åº¶',
  'ßï' => 'å¾�',
  'ßð' => 'æ�•',
  'ßñ' => 'æŠ’',
  'ßò' => 'æ�¿',
  'ßó' => 'æ•�',
  'ßô' => 'æš‘',
  'ßõ' => 'æ›™',
  'ßö' => 'æ›¸',
  'ß÷' => 'æ –',
  'ßø' => 'æ£²',
  'ßù' => 'çŠ€',
  'ßú' => 'ç‘ž',
  'ßû' => 'ç­®',
  'ßü' => 'çµ®',
  'ßý' => 'ç·–',
  'ßþ' => 'ç½²',
  'à¡' => 'èƒ¥',
  'à¢' => 'èˆ’',
  'à£' => 'è–¯',
  'à¤' => 'è¥¿',
  'à¥' => 'èª“',
  'à¦' => 'é€�',
  'à§' => 'é‹¤',
  'à¨' => 'é»�',
  'à©' => 'é¼ ',
  'àª' => 'å¤•',
  'à«' => 'å¥­',
  'à¬' => 'å¸­',
  'à­' => 'æƒœ',
  'à®' => 'æ˜”',
  'à¯' => 'æ™³',
  'à°' => 'æž�',
  'à±' => 'æ±�',
  'à²' => 'æ·…',
  'à³' => 'æ½Ÿ',
  'à´' => 'çŸ³',
  'àµ' => 'ç¢©',
  'à¶' => 'è“†',
  'à·' => 'é‡‹',
  'à¸' => 'éŒ«',
  'à¹' => 'ä»™',
  'àº' => 'åƒŠ',
  'à»' => 'å…ˆ',
  'à¼' => 'å–„',
  'à½' => 'å¬‹',
  'à¾' => 'å®£',
  'à¿' => 'æ‰‡',
  'àÀ' => 'æ•¾',
  'àÁ' => 'æ—‹',
  'àÂ' => 'æ¸²',
  'àÃ' => 'ç…½',
  'àÄ' => 'ç��',
  'àÅ' => 'ç‘„',
  'àÆ' => 'ç’‡',
  'àÇ' => 'ç’¿',
  'àÈ' => 'ç™¬',
  'àÉ' => 'ç¦ª',
  'àÊ' => 'ç·š',
  'àË' => 'ç¹•',
  'àÌ' => 'ç¾¨',
  'àÍ' => 'è…º',
  'àÎ' => 'è†³',
  'àÏ' => 'èˆ¹',
  'àÐ' => 'è˜š',
  'àÑ' => 'èŸ¬',
  'àÒ' => 'è©µ',
  'àÓ' => 'è·£',
  'àÔ' => 'é�¸',
  'àÕ' => 'éŠ‘',
  'àÖ' => 'é�¥',
  'à×' => 'é¥�',
  'àØ' => 'é®®',
  'àÙ' => 'å�¨',
  'àÚ' => 'å±‘',
  'àÛ' => 'æ¥”',
  'àÜ' => 'æ³„',
  'àÝ' => 'æ´©',
  'àÞ' => 'æ¸«',
  'àß' => 'èˆŒ',
  'àà' => 'è–›',
  'àá' => 'è¤»',
  'àâ' => 'è¨­',
  'àã' => 'èªª',
  'àä' => 'é›ª',
  'àå' => 'é½§',
  'àæ' => 'å‰¡',
  'àç' => 'æš¹',
  'àè' => 'æ®²',
  'àé' => 'çº–',
  'àê' => 'èŸ¾',
  'àë' => 'è´�',
  'àì' => 'é–ƒ',
  'àí' => 'é™�',
  'àî' => 'æ”�',
  'àï' => 'æ¶‰',
  'àð' => 'ç‡®',
  'àñ' => 'ï¥®',
  'àò' => 'åŸŽ',
  'àó' => 'å§“',
  'àô' => 'å®¬',
  'àõ' => 'æ€§',
  'àö' => 'æƒº',
  'à÷' => 'æˆ�',
  'àø' => 'æ˜Ÿ',
  'àù' => 'æ™Ÿ',
  'àú' => 'çŒ©',
  'àû' => 'ç�¹',
  'àü' => 'ç››',
  'àý' => 'çœ�',
  'àþ' => 'ç­¬',
  'á¡' => 'è�–',
  'á¢' => 'è�²',
  'á£' => 'è…¥',
  'á¤' => 'èª ',
  'á¥' => 'é†’',
  'á¦' => 'ä¸–',
  'á§' => 'å‹¢',
  'á¨' => 'æ­²',
  'á©' => 'æ´—',
  'áª' => 'ç¨…',
  'á«' => 'ç¬¹',
  'á¬' => 'ç´°',
  'á­' => 'ï¥¯',
  'á®' => 'è²°',
  'á¯' => 'å�¬',
  'á°' => 'å˜¯',
  'á±' => 'å¡‘',
  'á²' => 'å®µ',
  'á³' => 'å°�',
  'á´' => 'å°‘',
  'áµ' => 'å·¢',
  'á¶' => 'æ‰€',
  'á·' => 'æŽƒ',
  'á¸' => 'æ�”',
  'á¹' => 'æ˜­',
  'áº' => 'æ¢³',
  'á»' => 'æ²¼',
  'á¼' => 'æ¶ˆ',
  'á½' => 'æº¯',
  'á¾' => 'ç€Ÿ',
  'á¿' => 'ç‚¤',
  'áÀ' => 'ç‡’',
  'áÁ' => 'ç”¦',
  'áÂ' => 'ç–�',
  'áÃ' => 'ç–Ž',
  'áÄ' => 'ç˜™',
  'áÅ' => 'ç¬‘',
  'áÆ' => 'ç¯ ',
  'áÇ' => 'ç°«',
  'áÈ' => 'ç´ ',
  'áÉ' => 'ç´¹',
  'áÊ' => 'è”¬',
  'áË' => 'è•­',
  'áÌ' => 'è˜‡',
  'áÍ' => 'è¨´',
  'áÎ' => 'é€�',
  'áÏ' => 'é�¡',
  'áÐ' => 'é‚µ',
  'áÑ' => 'éŠ·',
  'áÒ' => 'éŸ¶',
  'áÓ' => 'é¨·',
  'áÔ' => 'ä¿—',
  'áÕ' => 'å±¬',
  'áÖ' => 'æ�Ÿ',
  'á×' => 'æ¶‘',
  'áØ' => 'ç²Ÿ',
  'áÙ' => 'çºŒ',
  'áÚ' => 'è¬–',
  'áÛ' => 'è´–',
  'áÜ' => 'é€Ÿ',
  'áÝ' => 'å­«',
  'áÞ' => 'å·½',
  'áß' => 'æ��',
  'áà' => 'è“€',
  'áá' => 'é�œ',
  'áâ' => 'é£¡',
  'áã' => 'çŽ‡',
  'áä' => 'å®‹',
  'áå' => 'æ‚š',
  'áæ' => 'æ�¾',
  'áç' => 'æ·ž',
  'áè' => 'è¨Ÿ',
  'áé' => 'èª¦',
  'áê' => 'é€�',
  'áë' => 'é Œ',
  'áì' => 'åˆ·',
  'áí' => 'ï¥°',
  'áî' => 'ç�‘',
  'áï' => 'ç¢Ž',
  'áð' => 'éŽ–',
  'áñ' => 'è¡°',
  'áò' => 'é‡—',
  'áó' => 'ä¿®',
  'áô' => 'å�—',
  'áõ' => 'å—½',
  'áö' => 'å›š',
  'á÷' => 'åž‚',
  'áø' => 'å£½',
  'áù' => 'å«‚',
  'áú' => 'å®ˆ',
  'áû' => 'å²«',
  'áü' => 'å³€',
  'áý' => 'å¸¥',
  'áþ' => 'æ„�',
  'â¡' => 'æˆ�',
  'â¢' => 'æ‰‹',
  'â£' => 'æŽˆ',
  'â¤' => 'æ�œ',
  'â¥' => 'æ”¶',
  'â¦' => 'æ•¸',
  'â§' => 'æ¨¹',
  'â¨' => 'æ®Š',
  'â©' => 'æ°´',
  'âª' => 'æ´™',
  'â«' => 'æ¼±',
  'â¬' => 'ç‡§',
  'â­' => 'ç‹©',
  'â®' => 'ç�¸',
  'â¯' => 'ç�‡',
  'â°' => 'ç’²',
  'â±' => 'ç˜¦',
  'â²' => 'ç�¡',
  'â³' => 'ç§€',
  'â´' => 'ç©—',
  'âµ' => 'ç«ª',
  'â¶' => 'ç²¹',
  'â·' => 'ç¶�',
  'â¸' => 'ç¶¬',
  'â¹' => 'ç¹¡',
  'âº' => 'ç¾ž',
  'â»' => 'è„©',
  'â¼' => 'èŒ±',
  'â½' => 'è’�',
  'â¾' => 'è“š',
  'â¿' => 'è—ª',
  'âÀ' => 'è¢–',
  'âÁ' => 'èª°',
  'âÂ' => 'è®�',
  'âÃ' => 'è¼¸',
  'âÄ' => 'é�‚',
  'âÅ' => 'é‚ƒ',
  'âÆ' => 'é…¬',
  'âÇ' => 'éŠ–',
  'âÈ' => 'éŠ¹',
  'âÉ' => 'éš‹',
  'âÊ' => 'éš§',
  'âË' => 'éš¨',
  'âÌ' => 'é›–',
  'âÍ' => 'éœ€',
  'âÎ' => 'é ˆ',
  'âÏ' => 'é¦–',
  'âÐ' => 'é«“',
  'âÑ' => 'é¬š',
  'âÒ' => 'å�”',
  'âÓ' => 'å¡¾',
  'âÔ' => 'å¤™',
  'âÕ' => 'å­°',
  'âÖ' => 'å®¿',
  'â×' => 'æ·‘',
  'âØ' => 'æ½š',
  'âÙ' => 'ç†Ÿ',
  'âÚ' => 'ç�¡',
  'âÛ' => 'ç’¹',
  'âÜ' => 'è‚…',
  'âÝ' => 'è�½',
  'âÞ' => 'å·¡',
  'âß' => 'å¾‡',
  'âà' => 'å¾ª',
  'âá' => 'æ�‚',
  'ââ' => 'æ—¬',
  'âã' => 'æ ’',
  'âä' => 'æ¥¯',
  'âå' => 'æ©“',
  'âæ' => 'æ®‰',
  'âç' => 'æ´µ',
  'âè' => 'æ·³',
  'âé' => 'ç�£',
  'âê' => 'ç›¾',
  'âë' => 'çž¬',
  'âì' => 'ç­�',
  'âí' => 'ç´”',
  'âî' => 'è„£',
  'âï' => 'èˆœ',
  'âð' => 'è�€',
  'âñ' => 'è“´',
  'âò' => 'è•£',
  'âó' => 'è©¢',
  'âô' => 'è«„',
  'âõ' => 'é†‡',
  'âö' => 'éŒž',
  'â÷' => 'é †',
  'âø' => 'é¦´',
  'âù' => 'æˆŒ',
  'âú' => 'è¡“',
  'âû' => 'è¿°',
  'âü' => 'é‰¥',
  'âý' => 'å´‡',
  'âþ' => 'å´§',
  'ã¡' => 'åµ©',
  'ã¢' => 'ç‘Ÿ',
  'ã£' => 'è†�',
  'ã¤' => 'è�¨',
  'ã¥' => 'æ¿•',
  'ã¦' => 'æ‹¾',
  'ã§' => 'ç¿’',
  'ã¨' => 'è¤¶',
  'ã©' => 'è¥²',
  'ãª' => 'ä¸ž',
  'ã«' => 'ä¹˜',
  'ã¬' => 'åƒ§',
  'ã­' => 'å‹�',
  'ã®' => 'å�‡',
  'ã¯' => 'æ‰¿',
  'ã°' => 'æ˜‡',
  'ã±' => 'ç¹©',
  'ã²' => 'è …',
  'ã³' => 'é™ž',
  'ã´' => 'ä¾�',
  'ãµ' => 'åŒ™',
  'ã¶' => 'å˜¶',
  'ã·' => 'å§‹',
  'ã¸' => 'åª¤',
  'ã¹' => 'å°¸',
  'ãº' => 'å±Ž',
  'ã»' => 'å±�',
  'ã¼' => 'å¸‚',
  'ã½' => 'å¼‘',
  'ã¾' => 'æ�ƒ',
  'ã¿' => 'æ–½',
  'ãÀ' => 'æ˜¯',
  'ãÁ' => 'æ™‚',
  'ãÂ' => 'æž¾',
  'ãÃ' => 'æŸ´',
  'ãÄ' => 'çŒœ',
  'ãÅ' => 'çŸ¢',
  'ãÆ' => 'ç¤º',
  'ãÇ' => 'ç¿…',
  'ãÈ' => 'è’”',
  'ãÉ' => 'è“�',
  'ãÊ' => 'è¦–',
  'ãË' => 'è©¦',
  'ãÌ' => 'è©©',
  'ãÍ' => 'è«¡',
  'ãÎ' => 'è±•',
  'ãÏ' => 'è±º',
  'ãÐ' => 'åŸ´',
  'ãÑ' => 'å¯”',
  'ãÒ' => 'å¼�',
  'ãÓ' => 'æ�¯',
  'ãÔ' => 'æ‹­',
  'ãÕ' => 'æ¤�',
  'ãÖ' => 'æ®–',
  'ã×' => 'æ¹œ',
  'ãØ' => 'ç†„',
  'ãÙ' => 'ç¯’',
  'ãÚ' => 'è�•',
  'ãÛ' => 'è­˜',
  'ãÜ' => 'è»¾',
  'ãÝ' => 'é£Ÿ',
  'ãÞ' => 'é£¾',
  'ãß' => 'ä¼¸',
  'ãà' => 'ä¾�',
  'ãá' => 'ä¿¡',
  'ãâ' => 'å‘»',
  'ãã' => 'å¨ ',
  'ãä' => 'å®¸',
  'ãå' => 'æ„¼',
  'ãæ' => 'æ–°',
  'ãç' => 'æ™¨',
  'ãè' => 'ç‡¼',
  'ãé' => 'ç”³',
  'ãê' => 'ç¥ž',
  'ãë' => 'ç´³',
  'ãì' => 'è…Ž',
  'ãí' => 'è‡£',
  'ãî' => 'èŽ˜',
  'ãï' => 'è–ª',
  'ãð' => 'è—Ž',
  'ãñ' => 'èœƒ',
  'ãò' => 'è¨Š',
  'ãó' => 'èº«',
  'ãô' => 'è¾›',
  'ãõ' => 'ï¥±',
  'ãö' => 'è¿…',
  'ã÷' => 'å¤±',
  'ãø' => 'å®¤',
  'ãù' => 'å¯¦',
  'ãú' => 'æ‚‰',
  'ãû' => 'å¯©',
  'ãü' => 'å°‹',
  'ãý' => 'å¿ƒ',
  'ãþ' => 'æ²�',
  'ä¡' => 'ï¥²',
  'ä¢' => 'æ·±',
  'ä£' => 'ç€‹',
  'ä¤' => 'ç”š',
  'ä¥' => 'èŠ¯',
  'ä¦' => 'è«¶',
  'ä§' => 'ä»€',
  'ä¨' => 'å��',
  'ä©' => 'ï¥³',
  'äª' => 'é›™',
  'ä«' => 'æ°�',
  'ä¬' => 'äºž',
  'ä­' => 'ä¿„',
  'ä®' => 'å…’',
  'ä¯' => 'å•ž',
  'ä°' => 'å¨¥',
  'ä±' => 'å³¨',
  'ä²' => 'æˆ‘',
  'ä³' => 'ç‰™',
  'ä´' => 'èŠ½',
  'äµ' => 'èŽª',
  'ä¶' => 'è›¾',
  'ä·' => 'è¡™',
  'ä¸' => 'è¨�',
  'ä¹' => 'é˜¿',
  'äº' => 'é›…',
  'ä»' => 'é¤“',
  'ä¼' => 'é´‰',
  'ä½' => 'éµ�',
  'ä¾' => 'å Š',
  'ä¿' => 'å²³',
  'äÀ' => 'å¶½',
  'äÁ' => 'å¹„',
  'äÂ' => 'æƒ¡',
  'äÃ' => 'æ„•',
  'äÄ' => 'æ�¡',
  'äÅ' => 'æ¨‚',
  'äÆ' => 'æ¸¥',
  'äÇ' => 'é„‚',
  'äÈ' => 'é�”',
  'äÉ' => 'é¡Ž',
  'äÊ' => 'é°�',
  'äË' => 'é½·',
  'äÌ' => 'å®‰',
  'äÍ' => 'å²¸',
  'äÎ' => 'æŒ‰',
  'äÏ' => 'æ™�',
  'äÐ' => 'æ¡ˆ',
  'äÑ' => 'çœ¼',
  'äÒ' => 'é›�',
  'äÓ' => 'éž�',
  'äÔ' => 'é¡”',
  'äÕ' => 'é®Ÿ',
  'äÖ' => 'æ–¡',
  'ä×' => 'è¬�',
  'äØ' => 'è»‹',
  'äÙ' => 'é–¼',
  'äÚ' => 'å”µ',
  'äÛ' => 'å²©',
  'äÜ' => 'å·–',
  'äÝ' => 'åºµ',
  'äÞ' => 'æš—',
  'äß' => 'ç™Œ',
  'äà' => 'è�´',
  'äá' => 'é—‡',
  'äâ' => 'å£“',
  'äã' => 'æŠ¼',
  'ää' => 'ç‹Ž',
  'äå' => 'é´¨',
  'äæ' => 'ä»°',
  'äç' => 'å¤®',
  'äè' => 'æ€�',
  'äé' => 'æ˜»',
  'äê' => 'æ®ƒ',
  'äë' => 'ç§§',
  'äì' => 'é´¦',
  'äí' => 'åŽ“',
  'äî' => 'å“€',
  'äï' => 'åŸƒ',
  'äð' => 'å´–',
  'äñ' => 'æ„›',
  'äò' => 'æ›–',
  'äó' => 'æ¶¯',
  'äô' => 'ç¢�',
  'äõ' => 'è‰¾',
  'äö' => 'éš˜',
  'ä÷' => 'é�„',
  'äø' => 'åŽ„',
  'äù' => 'æ‰¼',
  'äú' => 'æŽ–',
  'äû' => 'æ¶²',
  'äü' => 'ç¸Š',
  'äý' => 'è…‹',
  'äþ' => 'é¡�',
  'å¡' => 'æ«»',
  'å¢' => 'ç½Œ',
  'å£' => 'é¶¯',
  'å¤' => 'é¸š',
  'å¥' => 'ä¹Ÿ',
  'å¦' => 'å€»',
  'å§' => 'å†¶',
  'å¨' => 'å¤œ',
  'å©' => 'æƒ¹',
  'åª' => 'æ�¶',
  'å«' => 'æ¤°',
  'å¬' => 'çˆº',
  'å­' => 'è€¶',
  'å®' => 'ï¥´',
  'å¯' => 'é‡Ž',
  'å°' => 'å¼±',
  'å±' => 'ï¥µ',
  'å²' => 'ï¥¶',
  'å³' => 'ç´„',
  'å´' => 'è‹¥',
  'åµ' => 'è‘¯',
  'å¶' => 'è’»',
  'å·' => 'è—¥',
  'å¸' => 'èº�',
  'å¹' => 'ï¥·',
  'åº' => 'ä½¯',
  'å»' => 'ï¥¸',
  'å¼' => 'ï¥¹',
  'å½' => 'å£¤',
  'å¾' => 'å­ƒ',
  'å¿' => 'æ�™',
  'åÀ' => 'æ�š',
  'åÁ' => 'æ”˜',
  'åÂ' => 'æ•­',
  'åÃ' => 'æš˜',
  'åÄ' => 'ï¥º',
  'åÅ' => 'æ¥Š',
  'åÆ' => 'æ¨£',
  'åÇ' => 'æ´‹',
  'åÈ' => 'ç€�',
  'åÉ' => 'ç…¬',
  'åÊ' => 'ç—’',
  'åË' => 'ç˜�',
  'åÌ' => 'ç¦³',
  'åÍ' => 'ç©°',
  'åÎ' => 'ï¥»',
  'åÏ' => 'ç¾Š',
  'åÐ' => 'ï¥¼',
  'åÑ' => 'è¥„',
  'åÒ' => 'ï¥½',
  'åÓ' => 'è®“',
  'åÔ' => 'é‡€',
  'åÕ' => 'é™½',
  'åÖ' => 'ï¥¾',
  'å×' => 'é¤Š',
  'åØ' => 'åœ„',
  'åÙ' => 'å¾¡',
  'åÚ' => 'æ–¼',
  'åÛ' => 'æ¼�',
  'åÜ' => 'ç˜€',
  'åÝ' => 'ç¦¦',
  'åÞ' => 'èªž',
  'åß' => 'é¦­',
  'åà' => 'é­š',
  'åá' => 'é½¬',
  'åâ' => 'å„„',
  'åã' => 'æ†¶',
  'åä' => 'æŠ‘',
  'åå' => 'æª�',
  'åæ' => 'è‡†',
  'åç' => 'å�ƒ',
  'åè' => 'å °',
  'åé' => 'å½¦',
  'åê' => 'ç„‰',
  'åë' => 'è¨€',
  'åì' => 'è«º',
  'åí' => 'å­¼',
  'åî' => 'è˜–',
  'åï' => 'ä¿º',
  'åð' => 'å„¼',
  'åñ' => 'åš´',
  'åò' => 'å¥„',
  'åó' => 'æŽ©',
  'åô' => 'æ·¹',
  'åõ' => 'å¶ª',
  'åö' => 'æ¥­',
  'å÷' => 'å††',
  'åø' => 'äºˆ',
  'åù' => 'ä½™',
  'åú' => 'ï¥¿',
  'åû' => 'ï¦€',
  'åü' => 'ï¦�',
  'åý' => 'å¦‚',
  'åþ' => 'ï¦‚',
  'æ¡' => 'ï¦ƒ',
  'æ¢' => 'æ­Ÿ',
  'æ£' => 'æ±�',
  'æ¤' => 'ï¦„',
  'æ¥' => 'ç’µ',
  'æ¦' => 'ç¤–',
  'æ§' => 'ï¦…',
  'æ¨' => 'èˆ‡',
  'æ©' => 'è‰…',
  'æª' => 'èŒ¹',
  'æ«' => 'è¼¿',
  'æ¬' => 'è½�',
  'æ­' => 'ï¦†',
  'æ®' => 'é¤˜',
  'æ¯' => 'ï¦‡',
  'æ°' => 'ï¦ˆ',
  'æ±' => 'ï¦‰',
  'æ²' => 'äº¦',
  'æ³' => 'ï¦Š',
  'æ´' => 'åŸŸ',
  'æµ' => 'å½¹',
  'æ¶' => 'æ˜“',
  'æ·' => 'ï¦‹',
  'æ¸' => 'ï¦Œ',
  'æ¹' => 'ç–«',
  'æº' => 'ç¹¹',
  'æ»' => 'è­¯',
  'æ¼' => 'ï¦�',
  'æ½' => 'é€†',
  'æ¾' => 'é©›',
  'æ¿' => 'åš¥',
  'æÀ' => 'å §',
  'æÁ' => 'å§¸',
  'æÂ' => 'å¨Ÿ',
  'æÃ' => 'å®´',
  'æÄ' => 'ï¦Ž',
  'æÅ' => 'å»¶',
  'æÆ' => 'ï¦�',
  'æÇ' => 'ï¦�',
  'æÈ' => 'æ��',
  'æÉ' => 'æŒ»',
  'æÊ' => 'ï¦‘',
  'æË' => 'æ¤½',
  'æÌ' => 'æ²‡',
  'æÍ' => 'æ²¿',
  'æÎ' => 'æ¶Ž',
  'æÏ' => 'æ¶“',
  'æÐ' => 'æ·µ',
  'æÑ' => 'æ¼”',
  'æÒ' => 'ï¦’',
  'æÓ' => 'çƒŸ',
  'æÔ' => 'ç„¶',
  'æÕ' => 'ç…™',
  'æÖ' => 'ï¦“',
  'æ×' => 'ç‡ƒ',
  'æØ' => 'ç‡•',
  'æÙ' => 'ï¦”',
  'æÚ' => 'ç¡�',
  'æÛ' => 'ç¡¯',
  'æÜ' => 'ï¦•',
  'æÝ' => 'ç­µ',
  'æÞ' => 'ç·£',
  'æß' => 'ï¦–',
  'æà' => 'ç¸¯',
  'æá' => 'ï¦—',
  'æâ' => 'è¡�',
  'æã' => 'è»Ÿ',
  'æä' => 'ï¦˜',
  'æå' => 'ï¦™',
  'ææ' => 'ï¦š',
  'æç' => 'é‰›',
  'æè' => 'ï¦›',
  'æé' => 'é³¶',
  'æê' => 'ï¦œ',
  'æë' => 'ï¦�',
  'æì' => 'ï¦ž',
  'æí' => 'æ‚…',
  'æî' => 'æ¶…',
  'æï' => 'ï¦Ÿ',
  'æð' => 'ç†±',
  'æñ' => 'ï¦ ',
  'æò' => 'ï¦¡',
  'æó' => 'é–±',
  'æô' => 'åŽ­',
  'æõ' => 'ï¦¢',
  'æö' => 'ï¦£',
  'æ÷' => 'ï¦¤',
  'æø' => 'æŸ“',
  'æù' => 'ï¦¥',
  'æú' => 'ç‚Ž',
  'æû' => 'ç„°',
  'æü' => 'ç�°',
  'æý' => 'è‰¶',
  'æþ' => 'è‹’',
  'ç¡' => 'ï¦¦',
  'ç¢' => 'é–»',
  'ç£' => 'é«¥',
  'ç¤' => 'é¹½',
  'ç¥' => 'æ›„',
  'ç¦' => 'ï¦§',
  'ç§' => 'ç‡�',
  'ç¨' => 'è‘‰',
  'ç©' => 'ï¦¨',
  'çª' => 'ï¦©',
  'ç«' => 'å¡‹',
  'ç¬' => 'ï¦ª',
  'ç­' => 'ï¦«',
  'ç®' => 'å¶¸',
  'ç¯' => 'å½±',
  'ç°' => 'ï¦¬',
  'ç±' => 'æ˜ ',
  'ç²' => 'æšŽ',
  'ç³' => 'æ¥¹',
  'ç´' => 'æ¦®',
  'çµ' => 'æ°¸',
  'ç¶' => 'æ³³',
  'ç·' => 'æ¸¶',
  'ç¸' => 'æ½�',
  'ç¹' => 'æ¿š',
  'çº' => 'ç€›',
  'ç»' => 'ç€¯',
  'ç¼' => 'ç…�',
  'ç½' => 'ç‡Ÿ',
  'ç¾' => 'ç�°',
  'ç¿' => 'ï¦­',
  'çÀ' => 'ç‘›',
  'çÁ' => 'ï¦®',
  'çÂ' => 'ç“”',
  'çÃ' => 'ç›ˆ',
  'çÄ' => 'ç©Ž',
  'çÅ' => 'çº“',
  'çÆ' => 'ï¦¯',
  'çÇ' => 'ï¦°',
  'çÈ' => 'è‹±',
  'çÉ' => 'è© ',
  'çÊ' => 'è¿Ž',
  'çË' => 'ï¦±',
  'çÌ' => 'é�ˆ',
  'çÍ' => 'ï¦²',
  'çÎ' => 'éœ™',
  'çÏ' => 'ï¦³',
  'çÐ' => 'ï¦´',
  'çÑ' => 'ä¹‚',
  'çÒ' => 'å€ª',
  'çÓ' => 'ï¦µ',
  'çÔ' => 'åˆˆ',
  'çÕ' => 'å�¡',
  'çÖ' => 'æ›³',
  'ç×' => 'æ±­',
  'çØ' => 'æ¿Š',
  'çÙ' => 'çŒŠ',
  'çÚ' => 'ç�¿',
  'çÛ' => 'ç©¢',
  'çÜ' => 'èŠ®',
  'çÝ' => 'è—�',
  'çÞ' => 'è˜‚',
  'çß' => 'ï¦¶',
  'çà' => 'è£”',
  'çá' => 'è©£',
  'çâ' => 'è­½',
  'çã' => 'è±«',
  'çä' => 'ï¦·',
  'çå' => 'éŠ³',
  'çæ' => 'ï¦¸',
  'çç' => 'éœ“',
  'çè' => 'é �',
  'çé' => 'äº”',
  'çê' => 'ä¼�',
  'çë' => 'ä¿‰',
  'çì' => 'å‚²',
  'çí' => 'å�ˆ',
  'çî' => 'å�¾',
  'çï' => 'å�³',
  'çð' => 'å—š',
  'çñ' => 'å¡¢',
  'çò' => 'å¢º',
  'çó' => 'å¥§',
  'çô' => 'å¨›',
  'çõ' => 'å¯¤',
  'çö' => 'æ‚Ÿ',
  'ç÷' => 'ï¦¹',
  'çø' => 'æ‡Š',
  'çù' => 'æ•–',
  'çú' => 'æ—¿',
  'çû' => 'æ™¤',
  'çü' => 'æ¢§',
  'çý' => 'æ±š',
  'çþ' => 'æ¾³',
  'è¡' => 'çƒ�',
  'è¢' => 'ç†¬',
  'è£' => 'ç�’',
  'è¤' => 'ç­½',
  'è¥' => 'èœˆ',
  'è¦' => 'èª¤',
  'è§' => 'é°²',
  'è¨' => 'é¼‡',
  'è©' => 'å±‹',
  'èª' => 'æ²ƒ',
  'è«' => 'ç�„',
  'è¬' => 'çŽ‰',
  'è­' => 'éˆº',
  'è®' => 'æº«',
  'è¯' => 'ç‘¥',
  'è°' => 'ç˜Ÿ',
  'è±' => 'ç©©',
  'è²' => 'ç¸•',
  'è³' => 'è˜Š',
  'è´' => 'å…€',
  'èµ' => 'å£…',
  'è¶' => 'æ“�',
  'è·' => 'ç“®',
  'è¸' => 'ç”•',
  'è¹' => 'ç™°',
  'èº' => 'ç¿�',
  'è»' => 'é‚•',
  'è¼' => 'é›�',
  'è½' => 'é¥”',
  'è¾' => 'æ¸¦',
  'è¿' => 'ç“¦',
  'èÀ' => 'çª©',
  'èÁ' => 'çªª',
  'èÂ' => 'è‡¥',
  'èÃ' => 'è›™',
  'èÄ' => 'è�¸',
  'èÅ' => 'è¨›',
  'èÆ' => 'å©‰',
  'èÇ' => 'å®Œ',
  'èÈ' => 'å®›',
  'èÉ' => 'æ¢¡',
  'èÊ' => 'æ¤€',
  'èË' => 'æµ£',
  'èÌ' => 'çŽ©',
  'èÍ' => 'ç�“',
  'èÎ' => 'ç�¬',
  'èÏ' => 'ç¢—',
  'èÐ' => 'ç·©',
  'èÑ' => 'ç¿«',
  'èÒ' => 'è„˜',
  'èÓ' => 'è…•',
  'èÔ' => 'èŽž',
  'èÕ' => 'è±Œ',
  'èÖ' => 'é˜®',
  'è×' => 'é ‘',
  'èØ' => 'æ›°',
  'èÙ' => 'å¾€',
  'èÚ' => 'æ—º',
  'èÛ' => 'æž‰',
  'èÜ' => 'æ±ª',
  'èÝ' => 'çŽ‹',
  'èÞ' => 'å€­',
  'èß' => 'å¨ƒ',
  'èà' => 'æ­ª',
  'èá' => 'çŸ®',
  'èâ' => 'å¤–',
  'èã' => 'åµ¬',
  'èä' => 'å·�',
  'èå' => 'çŒ¥',
  'èæ' => 'ç•�',
  'èç' => 'ï¦º',
  'èè' => 'ï¦»',
  'èé' => 'åƒ¥',
  'èê' => 'å‡¹',
  'èë' => 'å ¯',
  'èì' => 'å¤­',
  'èí' => 'å¦–',
  'èî' => 'å§š',
  'èï' => 'å¯¥',
  'èð' => 'ï¦¼',
  'èñ' => 'ï¦½',
  'èò' => 'å¶¢',
  'èó' => 'æ‹—',
  'èô' => 'æ�–',
  'èõ' => 'æ’“',
  'èö' => 'æ“¾',
  'è÷' => 'ï¦¾',
  'èø' => 'æ›œ',
  'èù' => 'ï¦¿',
  'èú' => 'æ©ˆ',
  'èû' => 'ï§€',
  'èü' => 'ç‡¿',
  'èý' => 'ç‘¤',
  'èþ' => 'ï§�',
  'é¡' => 'çªˆ',
  'é¢' => 'çª¯',
  'é£' => 'ç¹‡',
  'é¤' => 'ç¹ž',
  'é¥' => 'è€€',
  'é¦' => 'è…°',
  'é§' => 'ï§‚',
  'é¨' => 'èŸ¯',
  'é©' => 'è¦�',
  'éª' => 'è¬ ',
  'é«' => 'é�™',
  'é¬' => 'ï§ƒ',
  'é­' => 'é‚€',
  'é®' => 'é¥’',
  'é¯' => 'æ…¾',
  'é°' => 'æ¬²',
  'é±' => 'æµ´',
  'é²' => 'ç¸Ÿ',
  'é³' => 'è¤¥',
  'é´' => 'è¾±',
  'éµ' => 'ä¿‘',
  'é¶' => 'å‚­',
  'é·' => 'å†—',
  'é¸' => 'å‹‡',
  'é¹' => 'åŸ‡',
  'éº' => 'å¢‰',
  'é»' => 'å®¹',
  'é¼' => 'åº¸',
  'é½' => 'æ…‚',
  'é¾' => 'æ¦•',
  'é¿' => 'æ¶Œ',
  'éÀ' => 'æ¹§',
  'éÁ' => 'æº¶',
  'éÂ' => 'ç†”',
  'éÃ' => 'ç‘¢',
  'éÄ' => 'ç”¨',
  'éÅ' => 'ç”¬',
  'éÆ' => 'è�³',
  'éÇ' => 'èŒ¸',
  'éÈ' => 'è“‰',
  'éÉ' => 'è¸Š',
  'éÊ' => 'éŽ”',
  'éË' => 'é�ž',
  'éÌ' => 'ï§„',
  'éÍ' => 'äºŽ',
  'éÎ' => 'ä½‘',
  'éÏ' => 'å�¶',
  'éÐ' => 'å„ª',
  'éÑ' => 'å�ˆ',
  'éÒ' => 'å�‹',
  'éÓ' => 'å�³',
  'éÔ' => 'å®‡',
  'éÕ' => 'å¯“',
  'éÖ' => 'å°¤',
  'é×' => 'æ„š',
  'éØ' => 'æ†‚',
  'éÙ' => 'æ—´',
  'éÚ' => 'ç‰›',
  'éÛ' => 'çŽ—',
  'éÜ' => 'ç‘€',
  'éÝ' => 'ç›‚',
  'éÞ' => 'ç¥�',
  'éß' => 'ç¦‘',
  'éà' => 'ç¦¹',
  'éá' => 'ç´†',
  'éâ' => 'ç¾½',
  'éã' => 'èŠ‹',
  'éä' => 'è—•',
  'éå' => 'è™ž',
  'éæ' => 'è¿‚',
  'éç' => 'é�‡',
  'éè' => 'éƒµ',
  'éé' => 'é‡ª',
  'éê' => 'éš…',
  'éë' => 'é›¨',
  'éì' => 'é›©',
  'éí' => 'å‹–',
  'éî' => 'å½§',
  'éï' => 'æ—­',
  'éð' => 'æ˜±',
  'éñ' => 'æ ¯',
  'éò' => 'ç…œ',
  'éó' => 'ç¨¶',
  'éô' => 'éƒ�',
  'éõ' => 'é Š',
  'éö' => 'äº‘',
  'é÷' => 'ï§…',
  'éø' => 'æ©’',
  'éù' => 'æ®ž',
  'éú' => 'æ¾�',
  'éû' => 'ç†‰',
  'éü' => 'è€˜',
  'éý' => 'èŠ¸',
  'éþ' => 'è•“',
  'ê¡' => 'é�‹',
  'ê¢' => 'éš•',
  'ê£' => 'é›²',
  'ê¤' => 'éŸ»',
  'ê¥' => 'è”š',
  'ê¦' => 'é¬±',
  'ê§' => 'äº�',
  'ê¨' => 'ç†Š',
  'ê©' => 'é›„',
  'êª' => 'å…ƒ',
  'ê«' => 'åŽŸ',
  'ê¬' => 'å“¡',
  'ê­' => 'åœ“',
  'ê®' => 'åœ’',
  'ê¯' => 'åž£',
  'ê°' => 'åª›',
  'ê±' => 'å«„',
  'ê²' => 'å¯ƒ',
  'ê³' => 'æ€¨',
  'ê´' => 'æ„¿',
  'êµ' => 'æ�´',
  'ê¶' => 'æ²…',
  'ê·' => 'æ´¹',
  'ê¸' => 'æ¹²',
  'ê¹' => 'æº�',
  'êº' => 'çˆ°',
  'ê»' => 'çŒ¿',
  'ê¼' => 'ç‘—',
  'ê½' => 'è‹‘',
  'ê¾' => 'è¢�',
  'ê¿' => 'è½…',
  'êÀ' => 'é� ',
  'êÁ' => 'ï§†',
  'êÂ' => 'é™¢',
  'êÃ' => 'é¡˜',
  'êÄ' => 'é´›',
  'êÅ' => 'æœˆ',
  'êÆ' => 'è¶Š',
  'êÇ' => 'é‰ž',
  'êÈ' => 'ä½�',
  'êÉ' => 'å�‰',
  'êÊ' => 'åƒž',
  'êË' => 'å�±',
  'êÌ' => 'åœ�',
  'êÍ' => 'å§”',
  'êÎ' => 'å¨�',
  'êÏ' => 'å°‰',
  'êÐ' => 'æ…°',
  'êÑ' => 'æš�',
  'êÒ' => 'æ¸­',
  'êÓ' => 'çˆ²',
  'êÔ' => 'ç‘‹',
  'êÕ' => 'ç·¯',
  'êÖ' => 'èƒƒ',
  'ê×' => 'è�Ž',
  'êØ' => 'è‘¦',
  'êÙ' => 'è”¿',
  'êÚ' => 'è�Ÿ',
  'êÛ' => 'è¡›',
  'êÜ' => 'è¤˜',
  'êÝ' => 'è¬‚',
  'êÞ' => 'é�•',
  'êß' => 'éŸ‹',
  'êà' => 'é­�',
  'êá' => 'ä¹³',
  'êâ' => 'ä¾‘',
  'êã' => 'å„’',
  'êä' => 'å…ª',
  'êå' => 'ï§‡',
  'êæ' => 'å”¯',
  'êç' => 'å–©',
  'êè' => 'å­º',
  'êé' => 'å®¥',
  'êê' => 'å¹¼',
  'êë' => 'å¹½',
  'êì' => 'åº¾',
  'êí' => 'æ‚ ',
  'êî' => 'æƒŸ',
  'êï' => 'æ„ˆ',
  'êð' => 'æ„‰',
  'êñ' => 'æ�„',
  'êò' => 'æ”¸',
  'êó' => 'æœ‰',
  'êô' => 'ï§ˆ',
  'êõ' => 'æŸ”',
  'êö' => 'æŸš',
  'ê÷' => 'ï§‰',
  'êø' => 'æ¥¡',
  'êù' => 'æ¥¢',
  'êú' => 'æ²¹',
  'êû' => 'æ´§',
  'êü' => 'ï§Š',
  'êý' => 'æ¸¸',
  'êþ' => 'ï§‹',
  'ë¡' => 'æ¿¡',
  'ë¢' => 'çŒ¶',
  'ë£' => 'çŒ·',
  'ë¤' => 'ï§Œ',
  'ë¥' => 'ç‘œ',
  'ë¦' => 'ç”±',
  'ë§' => 'ï§�',
  'ë¨' => 'ç™’',
  'ë©' => 'ï§Ž',
  'ëª' => 'ï§�',
  'ë«' => 'ç¶­',
  'ë¬' => 'è‡¾',
  'ë­' => 'è�¸',
  'ë®' => 'è£•',
  'ë¯' => 'èª˜',
  'ë°' => 'è«›',
  'ë±' => 'è«­',
  'ë²' => 'è¸°',
  'ë³' => 'è¹‚',
  'ë´' => 'é�Š',
  'ëµ' => 'é€¾',
  'ë¶' => 'é�º',
  'ë·' => 'é…‰',
  'ë¸' => 'é‡‰',
  'ë¹' => 'é�®',
  'ëº' => 'ï§�',
  'ë»' => 'ï§‘',
  'ë¼' => 'å ‰',
  'ë½' => 'ï§’',
  'ë¾' => 'æ¯“',
  'ë¿' => 'è‚‰',
  'ëÀ' => 'è‚²',
  'ëÁ' => 'ï§“',
  'ëÂ' => 'ï§”',
  'ëÃ' => 'å…�',
  'ëÄ' => 'å¥«',
  'ëÅ' => 'å°¹',
  'ëÆ' => 'ï§•',
  'ëÇ' => 'ï§–',
  'ëÈ' => 'æ½¤',
  'ëÉ' => 'çŽ§',
  'ëÊ' => 'èƒ¤',
  'ëË' => 'è´‡',
  'ëÌ' => 'ï§—',
  'ëÍ' => 'éˆ—',
  'ëÎ' => 'é–�',
  'ëÏ' => 'ï§˜',
  'ëÐ' => 'ï§™',
  'ëÑ' => 'ï§š',
  'ëÒ' => 'ï§›',
  'ëÓ' => 'è�¿',
  'ëÔ' => 'æˆŽ',
  'ëÕ' => 'ç€œ',
  'ëÖ' => 'çµ¨',
  'ë×' => 'èž�',
  'ëØ' => 'ï§œ',
  'ëÙ' => 'åž ',
  'ëÚ' => 'æ�©',
  'ëÛ' => 'æ…‡',
  'ëÜ' => 'æ®·',
  'ëÝ' => 'èª¾',
  'ëÞ' => 'éŠ€',
  'ëß' => 'éš±',
  'ëà' => 'ä¹™',
  'ëá' => 'å�Ÿ',
  'ëâ' => 'æ·«',
  'ëã' => 'è”­',
  'ëä' => 'é™°',
  'ëå' => 'éŸ³',
  'ëæ' => 'é£®',
  'ëç' => 'æ�–',
  'ëè' => 'æ³£',
  'ëé' => 'é‚‘',
  'ëê' => 'å‡�',
  'ëë' => 'æ‡‰',
  'ëì' => 'è†º',
  'ëí' => 'é·¹',
  'ëî' => 'ä¾�',
  'ëï' => 'å€š',
  'ëð' => 'å„€',
  'ëñ' => 'å®œ',
  'ëò' => 'æ„�',
  'ëó' => 'æ‡¿',
  'ëô' => 'æ“¬',
  'ëõ' => 'æ¤…',
  'ëö' => 'æ¯…',
  'ë÷' => 'ç–‘',
  'ëø' => 'çŸ£',
  'ëù' => 'ç¾©',
  'ëú' => 'è‰¤',
  'ëû' => 'è–�',
  'ëü' => 'èŸ»',
  'ëý' => 'è¡£',
  'ëþ' => 'èª¼',
  'ì¡' => 'è­°',
  'ì¢' => 'é†«',
  'ì£' => 'äºŒ',
  'ì¤' => 'ä»¥',
  'ì¥' => 'ä¼Š',
  'ì¦' => 'ï§�',
  'ì§' => 'ï§ž',
  'ì¨' => 'å¤·',
  'ì©' => 'å§¨',
  'ìª' => 'ï§Ÿ',
  'ì«' => 'å·²',
  'ì¬' => 'å¼›',
  'ì­' => 'å½›',
  'ì®' => 'æ€¡',
  'ì¯' => 'ï§ ',
  'ì°' => 'ï§¡',
  'ì±' => 'ï§¢',
  'ì²' => 'ï§£',
  'ì³' => 'çˆ¾',
  'ì´' => 'ç�¥',
  'ìµ' => 'ï§¤',
  'ì¶' => 'ç•°',
  'ì·' => 'ç—�',
  'ì¸' => 'ï§¥',
  'ì¹' => 'ç§»',
  'ìº' => 'ï§¦',
  'ì»' => 'è€Œ',
  'ì¼' => 'è€³',
  'ì½' => 'è‚„',
  'ì¾' => 'è‹¡',
  'ì¿' => 'è�‘',
  'ìÀ' => 'ï§§',
  'ìÁ' => 'ï§¨',
  'ìÂ' => 'è²½',
  'ìÃ' => 'è²³',
  'ìÄ' => 'é‚‡',
  'ìÅ' => 'ï§©',
  'ìÆ' => 'ï§ª',
  'ìÇ' => 'é£´',
  'ìÈ' => 'é¤Œ',
  'ìÉ' => 'ï§«',
  'ìÊ' => 'ï§¬',
  'ìË' => 'ç€·',
  'ìÌ' => 'ç›Š',
  'ìÍ' => 'ç¿Š',
  'ìÎ' => 'ç¿Œ',
  'ìÏ' => 'ç¿¼',
  'ìÐ' => 'è¬š',
  'ìÑ' => 'äºº',
  'ìÒ' => 'ä»�',
  'ìÓ' => 'åˆƒ',
  'ìÔ' => 'å�°',
  'ìÕ' => 'ï§­',
  'ìÖ' => 'å’½',
  'ì×' => 'å› ',
  'ìØ' => 'å§»',
  'ìÙ' => 'å¯…',
  'ìÚ' => 'å¼•',
  'ìÛ' => 'å¿�',
  'ìÜ' => 'æ¹®',
  'ìÝ' => 'ï§®',
  'ìÞ' => 'ï§¯',
  'ìß' => 'çµª',
  'ìà' => 'èŒµ',
  'ìá' => 'ï§°',
  'ìâ' => 'èš“',
  'ìã' => 'èª�',
  'ìä' => 'ï§±',
  'ìå' => 'é�­',
  'ìæ' => 'é�·',
  'ìç' => 'ï§²',
  'ìè' => 'ï§³',
  'ìé' => 'ä¸€',
  'ìê' => 'ä½š',
  'ìë' => 'ä½¾',
  'ìì' => 'å£¹',
  'ìí' => 'æ—¥',
  'ìî' => 'æº¢',
  'ìï' => 'é€¸',
  'ìð' => 'éŽ°',
  'ìñ' => 'é¦¹',
  'ìò' => 'ä»»',
  'ìó' => 'å£¬',
  'ìô' => 'å¦Š',
  'ìõ' => 'å§™',
  'ìö' => 'æ��',
  'ì÷' => 'ï§´',
  'ìø' => 'ï§µ',
  'ìù' => 'ç¨”',
  'ìú' => 'ï§¶',
  'ìû' => 'è��',
  'ìü' => 'è³ƒ',
  'ìý' => 'å…¥',
  'ìþ' => 'å�„',
  'í¡' => 'ï§·',
  'í¢' => 'ï§¸',
  'í£' => 'ï§¹',
  'í¤' => 'ä»�',
  'í¥' => 'å‰©',
  'í¦' => 'å­•',
  'í§' => 'èŠ¿',
  'í¨' => 'ä»”',
  'í©' => 'åˆº',
  'íª' => 'å’¨',
  'í«' => 'å§‰',
  'í¬' => 'å§¿',
  'í­' => 'å­�',
  'í®' => 'å­—',
  'í¯' => 'å­œ',
  'í°' => 'æ�£',
  'í±' => 'æ…ˆ',
  'í²' => 'æ»‹',
  'í³' => 'ç‚™',
  'í´' => 'ç…®',
  'íµ' => 'çŽ†',
  'í¶' => 'ç“·',
  'í·' => 'ç–µ',
  'í¸' => 'ç£�',
  'í¹' => 'ç´«',
  'íº' => 'è€…',
  'í»' => 'è‡ª',
  'í¼' => 'èŒ¨',
  'í½' => 'è”—',
  'í¾' => 'è—‰',
  'í¿' => 'è«®',
  'íÀ' => 'è³‡',
  'íÁ' => 'é›Œ',
  'íÂ' => 'ä½œ',
  'íÃ' => 'å‹º',
  'íÄ' => 'åš¼',
  'íÅ' => 'æ–«',
  'íÆ' => 'æ˜¨',
  'íÇ' => 'ç�¼',
  'íÈ' => 'ç‚¸',
  'íÉ' => 'çˆµ',
  'íÊ' => 'ç¶½',
  'íË' => 'èŠ�',
  'íÌ' => 'é…Œ',
  'íÍ' => 'é›€',
  'íÎ' => 'éµ²',
  'íÏ' => 'å­±',
  'íÐ' => 'æ£§',
  'íÑ' => 'æ®˜',
  'íÒ' => 'æ½º',
  'íÓ' => 'ç›ž',
  'íÔ' => 'å²‘',
  'íÕ' => 'æš«',
  'íÖ' => 'æ½›',
  'í×' => 'ç®´',
  'íØ' => 'ç°ª',
  'íÙ' => 'è ¶',
  'íÚ' => 'é›œ',
  'íÛ' => 'ä¸ˆ',
  'íÜ' => 'ä»—',
  'íÝ' => 'åŒ ',
  'íÞ' => 'å ´',
  'íß' => 'å¢»',
  'íà' => 'å£¯',
  'íá' => 'å¥¬',
  'íâ' => 'å°‡',
  'íã' => 'å¸³',
  'íä' => 'åº„',
  'íå' => 'å¼µ',
  'íæ' => 'æŽŒ',
  'íç' => 'æš²',
  'íè' => 'æ�–',
  'íé' => 'æ¨Ÿ',
  'íê' => 'æª£',
  'íë' => 'æ¬Œ',
  'íì' => 'æ¼¿',
  'íí' => 'ç‰†',
  'íî' => 'ï§º',
  'íï' => 'ç��',
  'íð' => 'ç’‹',
  'íñ' => 'ç« ',
  'íò' => 'ç²§',
  'íó' => 'è…¸',
  'íô' => 'è‡Ÿ',
  'íõ' => 'è‡§',
  'íö' => 'èŽŠ',
  'í÷' => 'è‘¬',
  'íø' => 'è”£',
  'íù' => 'è–”',
  'íú' => 'è—�',
  'íû' => 'è£�',
  'íü' => 'è´“',
  'íý' => 'é†¬',
  'íþ' => 'é•·',
  'î¡' => 'éšœ',
  'î¢' => 'å†�',
  'î£' => 'å“‰',
  'î¤' => 'åœ¨',
  'î¥' => 'å®°',
  'î¦' => 'æ‰�',
  'î§' => 'æ��',
  'î¨' => 'æ ½',
  'î©' => 'æ¢“',
  'îª' => 'æ¸½',
  'î«' => 'æ»“',
  'î¬' => 'ç�½',
  'î­' => 'ç¸¡',
  'î®' => 'è£�',
  'î¯' => 'è²¡',
  'î°' => 'è¼‰',
  'î±' => 'é½‹',
  'î²' => 'é½Ž',
  'î³' => 'çˆ­',
  'î´' => 'ç®�',
  'îµ' => 'è«�',
  'î¶' => 'éŒš',
  'î·' => 'ä½‡',
  'î¸' => 'ä½Ž',
  'î¹' => 'å„²',
  'îº' => 'å’€',
  'î»' => 'å§�',
  'î¼' => 'åº•',
  'î½' => 'æŠµ',
  'î¾' => 'æ�µ',
  'î¿' => 'æ¥®',
  'îÀ' => 'æ¨—',
  'îÁ' => 'æ²®',
  'îÂ' => 'æ¸š',
  'îÃ' => 'ç‹™',
  'îÄ' => 'çŒª',
  'îÅ' => 'ç–½',
  'îÆ' => 'ç®¸',
  'îÇ' => 'ç´µ',
  'îÈ' => 'è‹§',
  'îÉ' => 'è�¹',
  'îÊ' => 'è‘—',
  'îË' => 'è—·',
  'îÌ' => 'è©›',
  'îÍ' => 'è²¯',
  'îÎ' => 'èº‡',
  'îÏ' => 'é€™',
  'îÐ' => 'é‚¸',
  'îÑ' => 'é›Ž',
  'îÒ' => 'é½Ÿ',
  'îÓ' => 'å‹£',
  'îÔ' => 'å�Š',
  'îÕ' => 'å«¡',
  'îÖ' => 'å¯‚',
  'î×' => 'æ‘˜',
  'îØ' => 'æ•µ',
  'îÙ' => 'æ»´',
  'îÚ' => 'ç‹„',
  'îÛ' => 'ï§»',
  'îÜ' => 'çš„',
  'îÝ' => 'ç©�',
  'îÞ' => 'ç¬›',
  'îß' => 'ç±�',
  'îà' => 'ç¸¾',
  'îá' => 'ç¿Ÿ',
  'îâ' => 'è�»',
  'îã' => 'è¬«',
  'îä' => 'è³Š',
  'îå' => 'èµ¤',
  'îæ' => 'è·¡',
  'îç' => 'è¹Ÿ',
  'îè' => 'è¿ª',
  'îé' => 'è¿¹',
  'îê' => 'é�©',
  'îë' => 'é�‘',
  'îì' => 'ä½ƒ',
  'îí' => 'ä½º',
  'îî' => 'å‚³',
  'îï' => 'å…¨',
  'îð' => 'å…¸',
  'îñ' => 'å‰�',
  'îò' => 'å‰ª',
  'îó' => 'å¡¡',
  'îô' => 'å¡¼',
  'îõ' => 'å¥ ',
  'îö' => 'å°ˆ',
  'î÷' => 'å±•',
  'îø' => 'å»›',
  'îù' => 'æ‚›',
  'îú' => 'æˆ°',
  'îû' => 'æ “',
  'îü' => 'æ®¿',
  'îý' => 'æ°ˆ',
  'îþ' => 'æ¾±',
  'ï¡' => 'ç…Ž',
  'ï¢' => 'ç� ',
  'ï£' => 'ç”°',
  'ï¤' => 'ç”¸',
  'ï¥' => 'ç•‘',
  'ï¦' => 'ç™²',
  'ï§' => 'ç­Œ',
  'ï¨' => 'ç®‹',
  'ï©' => 'ç®­',
  'ïª' => 'ç¯†',
  'ï«' => 'çº�',
  'ï¬' => 'è©®',
  'ï­' => 'è¼¾',
  'ï®' => 'è½‰',
  'ï¯' => 'éˆ¿',
  'ï°' => 'éŠ“',
  'ï±' => 'éŒ¢',
  'ï²' => 'é�«',
  'ï³' => 'é›»',
  'ï´' => 'é¡š',
  'ïµ' => 'é¡«',
  'ï¶' => 'é¤ž',
  'ï·' => 'åˆ‡',
  'ï¸' => 'æˆª',
  'ï¹' => 'æŠ˜',
  'ïº' => 'æµ™',
  'ï»' => 'ç™¤',
  'ï¼' => 'ç«Š',
  'ï½' => 'ç¯€',
  'ï¾' => 'çµ¶',
  'ï¿' => 'å� ',
  'ïÀ' => 'å²¾',
  'ïÁ' => 'åº—',
  'ïÂ' => 'æ¼¸',
  'ïÃ' => 'ç‚¹',
  'ïÄ' => 'ç²˜',
  'ïÅ' => 'éœ‘',
  'ïÆ' => 'é®Ž',
  'ïÇ' => 'é»ž',
  'ïÈ' => 'æŽ¥',
  'ïÉ' => 'æ‘º',
  'ïÊ' => 'è�¶',
  'ïË' => 'ä¸�',
  'ïÌ' => 'äº•',
  'ïÍ' => 'äº­',
  'ïÎ' => 'å�œ',
  'ïÏ' => 'å�µ',
  'ïÐ' => 'å‘ˆ',
  'ïÑ' => 'å§ƒ',
  'ïÒ' => 'å®š',
  'ïÓ' => 'å¹€',
  'ïÔ' => 'åº­',
  'ïÕ' => 'å»·',
  'ïÖ' => 'å¾�',
  'ï×' => 'æƒ…',
  'ïØ' => 'æŒº',
  'ïÙ' => 'æ”¿',
  'ïÚ' => 'æ•´',
  'ïÛ' => 'æ—Œ',
  'ïÜ' => 'æ™¶',
  'ïÝ' => 'æ™¸',
  'ïÞ' => 'æŸ¾',
  'ïß' => 'æ¥¨',
  'ïà' => 'æª‰',
  'ïá' => 'æ­£',
  'ïâ' => 'æ±€',
  'ïã' => 'æ·€',
  'ïä' => 'æ·¨',
  'ïå' => 'æ¸Ÿ',
  'ïæ' => 'æ¹ž',
  'ïç' => 'ç€ž',
  'ïè' => 'ç‚¡',
  'ïé' => 'çŽŽ',
  'ïê' => 'ç�½',
  'ïë' => 'ç”º',
  'ïì' => 'ç�›',
  'ïí' => 'ç¢‡',
  'ïî' => 'ç¦Ž',
  'ïï' => 'ç¨‹',
  'ïð' => 'ç©½',
  'ïñ' => 'ç²¾',
  'ïò' => 'ç¶Ž',
  'ïó' => 'è‰‡',
  'ïô' => 'è¨‚',
  'ïõ' => 'è«ª',
  'ïö' => 'è²ž',
  'ï÷' => 'é„­',
  'ïø' => 'é…Š',
  'ïù' => 'é‡˜',
  'ïú' => 'é‰¦',
  'ïû' => 'é‹Œ',
  'ïü' => 'éŒ ',
  'ïý' => 'éœ†',
  'ïþ' => 'é�–',
  'ð¡' => 'é�œ',
  'ð¢' => 'é ‚',
  'ð£' => 'é¼Ž',
  'ð¤' => 'åˆ¶',
  'ð¥' => 'åŠ‘',
  'ð¦' => 'å•¼',
  'ð§' => 'å ¤',
  'ð¨' => 'å¸�',
  'ð©' => 'å¼Ÿ',
  'ðª' => 'æ‚Œ',
  'ð«' => 'æ��',
  'ð¬' => 'æ¢¯',
  'ð­' => 'æ¿Ÿ',
  'ð®' => 'ç¥­',
  'ð¯' => 'ç¬¬',
  'ð°' => 'è‡�',
  'ð±' => 'è–º',
  'ð²' => 'è£½',
  'ð³' => 'è«¸',
  'ð´' => 'è¹„',
  'ðµ' => 'é†�',
  'ð¶' => 'é™¤',
  'ð·' => 'éš›',
  'ð¸' => 'éœ½',
  'ð¹' => 'é¡Œ',
  'ðº' => 'é½Š',
  'ð»' => 'ä¿Ž',
  'ð¼' => 'å…†',
  'ð½' => 'å‡‹',
  'ð¾' => 'åŠ©',
  'ð¿' => 'å˜²',
  'ðÀ' => 'å¼”',
  'ðÁ' => 'å½«',
  'ðÂ' => 'æŽª',
  'ðÃ' => 'æ“�',
  'ðÄ' => 'æ—©',
  'ðÅ' => 'æ™�',
  'ðÆ' => 'æ›º',
  'ðÇ' => 'æ›¹',
  'ðÈ' => 'æœ�',
  'ðÉ' => 'æ¢�',
  'ðÊ' => 'æ£—',
  'ðË' => 'æ§½',
  'ðÌ' => 'æ¼•',
  'ðÍ' => 'æ½®',
  'ðÎ' => 'ç…§',
  'ðÏ' => 'ç‡¥',
  'ðÐ' => 'çˆª',
  'ðÑ' => 'ç’ª',
  'ðÒ' => 'çœº',
  'ðÓ' => 'ç¥–',
  'ðÔ' => 'ç¥š',
  'ðÕ' => 'ç§Ÿ',
  'ðÖ' => 'ç¨ ',
  'ð×' => 'çª•',
  'ðØ' => 'ç²—',
  'ðÙ' => 'ç³Ÿ',
  'ðÚ' => 'çµ„',
  'ðÛ' => 'ç¹°',
  'ðÜ' => 'è‚‡',
  'ðÝ' => 'è—»',
  'ðÞ' => 'èš¤',
  'ðß' => 'è©”',
  'ðà' => 'èª¿',
  'ðá' => 'è¶™',
  'ðâ' => 'èº�',
  'ðã' => 'é€ ',
  'ðä' => 'é�­',
  'ðå' => 'é‡£',
  'ðæ' => 'é˜»',
  'ðç' => 'é›•',
  'ðè' => 'é³¥',
  'ðé' => 'æ—�',
  'ðê' => 'ç°‡',
  'ðë' => 'è¶³',
  'ðì' => 'é�ƒ',
  'ðí' => 'å­˜',
  'ðî' => 'å°Š',
  'ðï' => 'å�’',
  'ðð' => 'æ‹™',
  'ðñ' => 'çŒ�',
  'ðò' => 'å€§',
  'ðó' => 'å®—',
  'ðô' => 'å¾ž',
  'ðõ' => 'æ‚°',
  'ðö' => 'æ…«',
  'ð÷' => 'æ£•',
  'ðø' => 'æ·™',
  'ðù' => 'ç�®',
  'ðú' => 'ç¨®',
  'ðû' => 'çµ‚',
  'ðü' => 'ç¶œ',
  'ðý' => 'ç¸±',
  'ðþ' => 'è…«',
  'ñ¡' => 'è¸ª',
  'ñ¢' => 'è¸µ',
  'ñ£' => 'é�¾',
  'ñ¤' => 'é�˜',
  'ñ¥' => 'ä½�',
  'ñ¦' => 'å��',
  'ñ§' => 'å·¦',
  'ñ¨' => 'åº§',
  'ñ©' => 'æŒ«',
  'ñª' => 'ç½ª',
  'ñ«' => 'ä¸»',
  'ñ¬' => 'ä½�',
  'ñ­' => 'ä¾�',
  'ñ®' => 'å�š',
  'ñ¯' => 'å§�',
  'ñ°' => 'èƒ„',
  'ñ±' => 'å‘ª',
  'ñ²' => 'å‘¨',
  'ñ³' => 'å—¾',
  'ñ´' => 'å¥�',
  'ñµ' => 'å®™',
  'ñ¶' => 'å·ž',
  'ñ·' => 'å»š',
  'ñ¸' => 'æ™�',
  'ñ¹' => 'æœ±',
  'ñº' => 'æŸ±',
  'ñ»' => 'æ ª',
  'ñ¼' => 'æ³¨',
  'ñ½' => 'æ´²',
  'ñ¾' => 'æ¹Š',
  'ñ¿' => 'æ¾�',
  'ñÀ' => 'ç‚·',
  'ñÁ' => 'ç� ',
  'ñÂ' => 'ç–‡',
  'ñÃ' => 'ç±Œ',
  'ñÄ' => 'ç´‚',
  'ñÅ' => 'ç´¬',
  'ñÆ' => 'ç¶¢',
  'ñÇ' => 'èˆŸ',
  'ñÈ' => 'è››',
  'ñÉ' => 'è¨»',
  'ñÊ' => 'èª…',
  'ñË' => 'èµ°',
  'ñÌ' => 'èºŠ',
  'ñÍ' => 'è¼³',
  'ñÎ' => 'é€±',
  'ñÏ' => 'é…Ž',
  'ñÐ' => 'é…’',
  'ñÑ' => 'é‘„',
  'ñÒ' => 'é§�',
  'ñÓ' => 'ç«¹',
  'ñÔ' => 'ç²¥',
  'ñÕ' => 'ä¿Š',
  'ñÖ' => 'å„�',
  'ñ×' => 'å‡†',
  'ñØ' => 'åŸˆ',
  'ñÙ' => 'å¯¯',
  'ñÚ' => 'å³»',
  'ñÛ' => 'æ™™',
  'ñÜ' => 'æ¨½',
  'ñÝ' => 'æµš',
  'ñÞ' => 'æº–',
  'ñß' => 'æ¿¬',
  'ñà' => 'ç„Œ',
  'ñá' => 'ç•¯',
  'ñâ' => 'ç«£',
  'ñã' => 'è ¢',
  'ñä' => 'é€¡',
  'ñå' => 'é�µ',
  'ñæ' => 'é›‹',
  'ñç' => 'é§¿',
  'ñè' => 'èŒ�',
  'ñé' => 'ä¸­',
  'ñê' => 'ä»²',
  'ñë' => 'è¡†',
  'ñì' => 'é‡�',
  'ñí' => 'å�½',
  'ñî' => 'æ«›',
  'ñï' => 'æ¥«',
  'ñð' => 'æ±�',
  'ññ' => 'è‘º',
  'ñò' => 'å¢ž',
  'ñó' => 'æ†Ž',
  'ñô' => 'æ›¾',
  'ñõ' => 'æ‹¯',
  'ñö' => 'çƒ�',
  'ñ÷' => 'ç”‘',
  'ñø' => 'ç—‡',
  'ñù' => 'ç¹’',
  'ñú' => 'è’¸',
  'ñû' => 'è­‰',
  'ñü' => 'è´ˆ',
  'ñý' => 'ä¹‹',
  'ñþ' => 'å�ª',
  'ò¡' => 'å’«',
  'ò¢' => 'åœ°',
  'ò£' => 'å�€',
  'ò¤' => 'å¿—',
  'ò¥' => 'æŒ�',
  'ò¦' => 'æŒ‡',
  'ò§' => 'æ‘¯',
  'ò¨' => 'æ”¯',
  'ò©' => 'æ—¨',
  'òª' => 'æ™º',
  'ò«' => 'æž�',
  'ò¬' => 'æž³',
  'ò­' => 'æ­¢',
  'ò®' => 'æ± ',
  'ò¯' => 'æ²š',
  'ò°' => 'æ¼¬',
  'ò±' => 'çŸ¥',
  'ò²' => 'ç ¥',
  'ò³' => 'ç¥‰',
  'ò´' => 'ç¥—',
  'òµ' => 'ç´™',
  'ò¶' => 'è‚¢',
  'ò·' => 'è„‚',
  'ò¸' => 'è‡³',
  'ò¹' => 'èŠ�',
  'òº' => 'èŠ·',
  'ò»' => 'èœ˜',
  'ò¼' => 'èªŒ',
  'ò½' => 'ï§¼',
  'ò¾' => 'è´„',
  'ò¿' => 'è¶¾',
  'òÀ' => 'é�²',
  'òÁ' => 'ç›´',
  'òÂ' => 'ç¨™',
  'òÃ' => 'ç¨·',
  'òÄ' => 'ç¹”',
  'òÅ' => 'è�·',
  'òÆ' => 'å”‡',
  'òÇ' => 'å—”',
  'òÈ' => 'å¡µ',
  'òÉ' => 'æŒ¯',
  'òÊ' => 'æ�¢',
  'òË' => 'æ™‰',
  'òÌ' => 'æ™‹',
  'òÍ' => 'æ¡­',
  'òÎ' => 'æ¦›',
  'òÏ' => 'æ®„',
  'òÐ' => 'æ´¥',
  'òÑ' => 'æº±',
  'òÒ' => 'ç��',
  'òÓ' => 'ç‘¨',
  'òÔ' => 'ç’¡',
  'òÕ' => 'ç•›',
  'òÖ' => 'ç–¹',
  'ò×' => 'ç›¡',
  'òØ' => 'çœž',
  'òÙ' => 'çž‹',
  'òÚ' => 'ç§¦',
  'òÛ' => 'ç¸‰',
  'òÜ' => 'ç¸�',
  'òÝ' => 'è‡»',
  'òÞ' => 'è”¯',
  'òß' => 'è¢—',
  'òà' => 'è¨º',
  'òá' => 'è³‘',
  'òâ' => 'è»«',
  'òã' => 'è¾°',
  'òä' => 'é€²',
  'òå' => 'éŽ­',
  'òæ' => 'é™£',
  'òç' => 'é™³',
  'òè' => 'éœ‡',
  'òé' => 'ä¾„',
  'òê' => 'å�±',
  'òë' => 'å§ª',
  'òì' => 'å«‰',
  'òí' => 'å¸™',
  'òî' => 'æ¡Ž',
  'òï' => 'ç“†',
  'òð' => 'ç–¾',
  'òñ' => 'ç§©',
  'òò' => 'çª’',
  'òó' => 'è†£',
  'òô' => 'è›­',
  'òõ' => 'è³ª',
  'òö' => 'è·Œ',
  'ò÷' => 'è¿­',
  'òø' => 'æ–Ÿ',
  'òù' => 'æœ•',
  'òú' => 'ï§½',
  'òû' => 'åŸ·',
  'òü' => 'æ½—',
  'òý' => 'ç·�',
  'òþ' => 'è¼¯',
  'ó¡' => 'é�¶',
  'ó¢' => 'é›†',
  'ó£' => 'å¾µ',
  'ó¤' => 'æ‡²',
  'ó¥' => 'æ¾„',
  'ó¦' => 'ä¸”',
  'ó§' => 'ä¾˜',
  'ó¨' => 'å€Ÿ',
  'ó©' => 'å�‰',
  'óª' => 'å—Ÿ',
  'ó«' => 'åµ¯',
  'ó¬' => 'å·®',
  'ó­' => 'æ¬¡',
  'ó®' => 'æ­¤',
  'ó¯' => 'ç£‹',
  'ó°' => 'ç®š',
  'ó±' => 'ï§¾',
  'ó²' => 'è¹‰',
  'ó³' => 'è»Š',
  'ó´' => 'é�®',
  'óµ' => 'æ�‰',
  'ó¶' => 'æ�¾',
  'ó·' => 'ç�€',
  'ó¸' => 'çª„',
  'ó¹' => 'éŒ¯',
  'óº' => 'é‘¿',
  'ó»' => 'é½ª',
  'ó¼' => 'æ’°',
  'ó½' => 'æ¾¯',
  'ó¾' => 'ç‡¦',
  'ó¿' => 'ç’¨',
  'óÀ' => 'ç“š',
  'óÁ' => 'ç«„',
  'óÂ' => 'ç°’',
  'óÃ' => 'çº‚',
  'óÄ' => 'ç²²',
  'óÅ' => 'çº˜',
  'óÆ' => 'è®š',
  'óÇ' => 'è´Š',
  'óÈ' => 'é‘½',
  'óÉ' => 'é¤�',
  'óÊ' => 'é¥Œ',
  'óË' => 'åˆ¹',
  'óÌ' => 'å¯Ÿ',
  'óÍ' => 'æ“¦',
  'óÎ' => 'æœ­',
  'óÏ' => 'ç´®',
  'óÐ' => 'åƒ­',
  'óÑ' => 'å�ƒ',
  'óÒ' => 'å¡¹',
  'óÓ' => 'æ…˜',
  'óÔ' => 'æ…™',
  'óÕ' => 'æ‡º',
  'óÖ' => 'æ–¬',
  'ó×' => 'ç«™',
  'óØ' => 'è®’',
  'óÙ' => 'è®–',
  'óÚ' => 'å€‰',
  'óÛ' => 'å€¡',
  'óÜ' => 'å‰µ',
  'óÝ' => 'å”±',
  'óÞ' => 'å¨¼',
  'óß' => 'å» ',
  'óà' => 'å½°',
  'óá' => 'æ„´',
  'óâ' => 'æ•ž',
  'óã' => 'æ˜Œ',
  'óä' => 'æ˜¶',
  'óå' => 'æš¢',
  'óæ' => 'æ§�',
  'óç' => 'æ»„',
  'óè' => 'æ¼²',
  'óé' => 'çŒ–',
  'óê' => 'ç˜¡',
  'óë' => 'çª“',
  'óì' => 'è„¹',
  'óí' => 'è‰™',
  'óî' => 'è�–',
  'óï' => 'è’¼',
  'óð' => 'å‚µ',
  'óñ' => 'åŸ°',
  'óò' => 'å¯€',
  'óó' => 'å¯¨',
  'óô' => 'å½©',
  'óõ' => 'æŽ¡',
  'óö' => 'ç ¦',
  'ó÷' => 'ç¶µ',
  'óø' => 'è�œ',
  'óù' => 'è”¡',
  'óú' => 'é‡‡',
  'óû' => 'é‡µ',
  'óü' => 'å†Š',
  'óý' => 'æŸµ',
  'óþ' => 'ç­–',
  'ô¡' => 'è²¬',
  'ô¢' => 'å‡„',
  'ô£' => 'å¦»',
  'ô¤' => 'æ‚½',
  'ô¥' => 'è™•',
  'ô¦' => 'å€œ',
  'ô§' => 'ï§¿',
  'ô¨' => 'å‰”',
  'ô©' => 'å°º',
  'ôª' => 'æ…½',
  'ô«' => 'æˆš',
  'ô¬' => 'æ‹“',
  'ô­' => 'æ“²',
  'ô®' => 'æ–¥',
  'ô¯' => 'æ»Œ',
  'ô°' => 'ç˜ ',
  'ô±' => 'è„Š',
  'ô²' => 'è¹ ',
  'ô³' => 'é™Ÿ',
  'ô´' => 'éš»',
  'ôµ' => 'ä»Ÿ',
  'ô¶' => 'å�ƒ',
  'ô·' => 'å–˜',
  'ô¸' => 'å¤©',
  'ô¹' => 'å·�',
  'ôº' => 'æ“…',
  'ô»' => 'æ³‰',
  'ô¼' => 'æ·º',
  'ô½' => 'çŽ”',
  'ô¾' => 'ç©¿',
  'ô¿' => 'èˆ›',
  'ôÀ' => 'è–¦',
  'ôÁ' => 'è³¤',
  'ôÂ' => 'è¸�',
  'ôÃ' => 'é�·',
  'ôÄ' => 'é‡§',
  'ôÅ' => 'é—¡',
  'ôÆ' => 'é˜¡',
  'ôÇ' => 'éŸ†',
  'ôÈ' => 'å‡¸',
  'ôÉ' => 'å“²',
  'ôÊ' => 'å–†',
  'ôË' => 'å¾¹',
  'ôÌ' => 'æ’¤',
  'ôÍ' => 'æ¾ˆ',
  'ôÎ' => 'ç¶´',
  'ôÏ' => 'è¼Ÿ',
  'ôÐ' => 'è½�',
  'ôÑ' => 'é�µ',
  'ôÒ' => 'åƒ‰',
  'ôÓ' => 'å°–',
  'ôÔ' => 'æ²¾',
  'ôÕ' => 'æ·»',
  'ôÖ' => 'ç”›',
  'ô×' => 'çž»',
  'ôØ' => 'ç°½',
  'ôÙ' => 'ç±¤',
  'ôÚ' => 'è©¹',
  'ôÛ' => 'è«‚',
  'ôÜ' => 'å ž',
  'ôÝ' => 'å¦¾',
  'ôÞ' => 'å¸–',
  'ôß' => 'æ�·',
  'ôà' => 'ç‰’',
  'ôá' => 'ç–Š',
  'ôâ' => 'ç�«',
  'ôã' => 'è«œ',
  'ôä' => 'è²¼',
  'ôå' => 'è¼’',
  'ôæ' => 'å»³',
  'ôç' => 'æ™´',
  'ôè' => 'æ·¸',
  'ôé' => 'è�½',
  'ôê' => 'è��',
  'ôë' => 'è«‹',
  'ôì' => 'é�‘',
  'ôí' => 'é¯–',
  'ôî' => 'ï¨€',
  'ôï' => 'å‰ƒ',
  'ôð' => 'æ›¿',
  'ôñ' => 'æ¶•',
  'ôò' => 'æ»¯',
  'ôó' => 'ç· ',
  'ôô' => 'è«¦',
  'ôõ' => 'é€®',
  'ôö' => 'é�ž',
  'ô÷' => 'é«”',
  'ôø' => 'åˆ�',
  'ôù' => 'å‰¿',
  'ôú' => 'å“¨',
  'ôû' => 'æ†”',
  'ôü' => 'æŠ„',
  'ôý' => 'æ‹›',
  'ôþ' => 'æ¢¢',
  'õ¡' => 'æ¤’',
  'õ¢' => 'æ¥š',
  'õ£' => 'æ¨µ',
  'õ¤' => 'ç‚’',
  'õ¥' => 'ç„¦',
  'õ¦' => 'ç¡�',
  'õ§' => 'ç¤�',
  'õ¨' => 'ç¤Ž',
  'õ©' => 'ç§’',
  'õª' => 'ç¨�',
  'õ«' => 'è‚–',
  'õ¬' => 'è‰¸',
  'õ­' => 'è‹•',
  'õ®' => 'è�‰',
  'õ¯' => 'è•‰',
  'õ°' => 'è²‚',
  'õ±' => 'è¶…',
  'õ²' => 'é…¢',
  'õ³' => 'é†‹',
  'õ´' => 'é†®',
  'õµ' => 'ä¿ƒ',
  'õ¶' => 'å›‘',
  'õ·' => 'ç‡­',
  'õ¸' => 'çŸ—',
  'õ¹' => 'èœ€',
  'õº' => 'è§¸',
  'õ»' => 'å¯¸',
  'õ¼' => 'å¿–',
  'õ½' => 'æ�‘',
  'õ¾' => 'é‚¨',
  'õ¿' => 'å�¢',
  'õÀ' => 'å¡š',
  'õÁ' => 'å¯µ',
  'õÂ' => 'æ‚¤',
  'õÃ' => 'æ†�',
  'õÄ' => 'æ‘ ',
  'õÅ' => 'ç¸½',
  'õÆ' => 'è�°',
  'õÇ' => 'è”¥',
  'õÈ' => 'éŠƒ',
  'õÉ' => 'æ’®',
  'õÊ' => 'å‚¬',
  'õË' => 'å´”',
  'õÌ' => 'æœ€',
  'õÍ' => 'å¢œ',
  'õÎ' => 'æŠ½',
  'õÏ' => 'æŽ¨',
  'õÐ' => 'æ¤Ž',
  'õÑ' => 'æ¥¸',
  'õÒ' => 'æ¨ž',
  'õÓ' => 'æ¹«',
  'õÔ' => 'çšº',
  'õÕ' => 'ç§‹',
  'õÖ' => 'èŠ»',
  'õ×' => 'è�©',
  'õØ' => 'è«�',
  'õÙ' => 'è¶¨',
  'õÚ' => 'è¿½',
  'õÛ' => 'é„’',
  'õÜ' => 'é…‹',
  'õÝ' => 'é†œ',
  'õÞ' => 'éŒ�',
  'õß' => 'éŒ˜',
  'õà' => 'éŽš',
  'õá' => 'é››',
  'õâ' => 'é¨¶',
  'õã' => 'é°�',
  'õä' => 'ä¸‘',
  'õå' => 'ç•œ',
  'õæ' => 'ç¥�',
  'õç' => 'ç«º',
  'õè' => 'ç­‘',
  'õé' => 'ç¯‰',
  'õê' => 'ç¸®',
  'õë' => 'è“„',
  'õì' => 'è¹™',
  'õí' => 'è¹´',
  'õî' => 'è»¸',
  'õï' => 'é€�',
  'õð' => 'æ˜¥',
  'õñ' => 'æ¤¿',
  'õò' => 'ç‘ƒ',
  'õó' => 'å‡º',
  'õô' => 'æœ®',
  'õõ' => 'é»œ',
  'õö' => 'å……',
  'õ÷' => 'å¿ ',
  'õø' => 'æ²–',
  'õù' => 'èŸ²',
  'õú' => 'è¡�',
  'õû' => 'è¡·',
  'õü' => 'æ‚´',
  'õý' => 'è†µ',
  'õþ' => 'è�ƒ',
  'ö¡' => 'è´…',
  'ö¢' => 'å�–',
  'ö£' => 'å�¹',
  'ö¤' => 'å˜´',
  'ö¥' => 'å¨¶',
  'ö¦' => 'å°±',
  'ö§' => 'ç‚Š',
  'ö¨' => 'ç¿ ',
  'ö©' => 'è�š',
  'öª' => 'è„†',
  'ö«' => 'è‡­',
  'ö¬' => 'è¶£',
  'ö­' => 'é†‰',
  'ö®' => 'é©Ÿ',
  'ö¯' => 'é·²',
  'ö°' => 'å�´',
  'ö±' => 'ä»„',
  'ö²' => 'åŽ ',
  'ö³' => 'æƒ»',
  'ö´' => 'æ¸¬',
  'öµ' => 'å±¤',
  'ö¶' => 'ä¾ˆ',
  'ö·' => 'å€¤',
  'ö¸' => 'å—¤',
  'ö¹' => 'å³™',
  'öº' => 'å¹Ÿ',
  'ö»' => 'æ�¥',
  'ö¼' => 'æ¢”',
  'ö½' => 'æ²»',
  'ö¾' => 'æ·„',
  'ö¿' => 'ç†¾',
  'öÀ' => 'ç—”',
  'öÁ' => 'ç—´',
  'öÂ' => 'ç™¡',
  'öÃ' => 'ç¨š',
  'öÄ' => 'ç©‰',
  'öÅ' => 'ç·‡',
  'öÆ' => 'ç·»',
  'öÇ' => 'ç½®',
  'öÈ' => 'è‡´',
  'öÉ' => 'èš©',
  'öÊ' => 'è¼œ',
  'öË' => 'é›‰',
  'öÌ' => 'é¦³',
  'öÍ' => 'é½’',
  'öÎ' => 'å‰‡',
  'öÏ' => 'å‹…',
  'öÐ' => 'é£­',
  'öÑ' => 'è¦ª',
  'öÒ' => 'ä¸ƒ',
  'öÓ' => 'æŸ’',
  'öÔ' => 'æ¼†',
  'öÕ' => 'ä¾µ',
  'öÖ' => 'å¯¢',
  'ö×' => 'æž•',
  'öØ' => 'æ²ˆ',
  'öÙ' => 'æµ¸',
  'öÚ' => 'ç�›',
  'öÛ' => 'ç §',
  'öÜ' => 'é‡�',
  'öÝ' => 'é�¼',
  'öÞ' => 'èŸ„',
  'öß' => 'ç§¤',
  'öà' => 'ç¨±',
  'öá' => 'å¿«',
  'öâ' => 'ä»–',
  'öã' => 'å’¤',
  'öä' => 'å”¾',
  'öå' => 'å¢®',
  'öæ' => 'å¦¥',
  'öç' => 'æƒ°',
  'öè' => 'æ‰“',
  'öé' => 'æ‹–',
  'öê' => 'æœ¶',
  'öë' => 'æ¥•',
  'öì' => 'èˆµ',
  'öí' => 'é™€',
  'öî' => 'é¦±',
  'öï' => 'é§�',
  'öð' => 'å€¬',
  'öñ' => 'å�“',
  'öò' => 'å•„',
  'öó' => 'å�¼',
  'öô' => 'ï¨�',
  'öõ' => 'æ‰˜',
  'öö' => 'ï¨‚',
  'ö÷' => 'æ“¢',
  'öø' => 'æ™«',
  'öù' => 'æŸ�',
  'öú' => 'æ¿�',
  'öû' => 'æ¿¯',
  'öü' => 'ç�¢',
  'öý' => 'ç�¸',
  'öþ' => 'è¨—',
  '÷¡' => 'é�¸',
  '÷¢' => 'å‘‘',
  '÷£' => 'å˜†',
  '÷¤' => 'å�¦',
  '÷¥' => 'å½ˆ',
  '÷¦' => 'æ†š',
  '÷§' => 'æ­Ž',
  '÷¨' => 'ç�˜',
  '÷©' => 'ç‚­',
  '÷ª' => 'ç¶»',
  '÷«' => 'èª•',
  '÷¬' => 'å¥ª',
  '÷­' => 'è„«',
  '÷®' => 'æŽ¢',
  '÷¯' => 'çœˆ',
  '÷°' => 'è€½',
  '÷±' => 'è²ª',
  '÷²' => 'å¡”',
  '÷³' => 'æ�­',
  '÷´' => 'æ¦»',
  '÷µ' => 'å®•',
  '÷¶' => 'å¸‘',
  '÷·' => 'æ¹¯',
  '÷¸' => 'ï¨ƒ',
  '÷¹' => 'è•©',
  '÷º' => 'å…Œ',
  '÷»' => 'å�°',
  '÷¼' => 'å¤ª',
  '÷½' => 'æ€ ',
  '÷¾' => 'æ…‹',
  '÷¿' => 'æ®†',
  '÷À' => 'æ±°',
  '÷Á' => 'æ³°',
  '÷Â' => 'ç¬ž',
  '÷Ã' => 'èƒŽ',
  '÷Ä' => 'è‹”',
  '÷Å' => 'è·†',
  '÷Æ' => 'é‚°',
  '÷Ç' => 'é¢±',
  '÷È' => 'ï¨„',
  '÷É' => 'æ“‡',
  '÷Ê' => 'æ¾¤',
  '÷Ë' => 'æ’‘',
  '÷Ì' => 'æ”„',
  '÷Í' => 'å…Ž',
  '÷Î' => 'å��',
  '÷Ï' => 'åœŸ',
  '÷Ð' => 'è¨Ž',
  '÷Ñ' => 'æ…Ÿ',
  '÷Ò' => 'æ¡¶',
  '÷Ó' => 'ï¨…',
  '÷Ô' => 'ç—›',
  '÷Õ' => 'ç­’',
  '÷Ö' => 'çµ±',
  '÷×' => 'é€š',
  '÷Ø' => 'å †',
  '÷Ù' => 'æ§Œ',
  '÷Ú' => 'è…¿',
  '÷Û' => 'è¤ª',
  '÷Ü' => 'é€€',
  '÷Ý' => 'é ¹',
  '÷Þ' => 'å�¸',
  '÷ß' => 'å¥—',
  '÷à' => 'å¦¬',
  '÷á' => 'æŠ•',
  '÷â' => 'é€�',
  '÷ã' => 'é¬ª',
  '÷ä' => 'æ…�',
  '÷å' => 'ç‰¹',
  '÷æ' => 'é—–',
  '÷ç' => 'å�¡',
  '÷è' => 'å©†',
  '÷é' => 'å·´',
  '÷ê' => 'æŠŠ',
  '÷ë' => 'æ’­',
  '÷ì' => 'æ“º',
  '÷í' => 'æ�·',
  '÷î' => 'æ³¢',
  '÷ï' => 'æ´¾',
  '÷ð' => 'çˆ¬',
  '÷ñ' => 'ç�¶',
  '÷ò' => 'ç ´',
  '÷ó' => 'ç½·',
  '÷ô' => 'èŠ­',
  '÷õ' => 'è·›',
  '÷ö' => 'é —',
  '÷÷' => 'åˆ¤',
  '÷ø' => 'å�‚',
  '÷ù' => 'æ�¿',
  '÷ú' => 'ç‰ˆ',
  '÷û' => 'ç“£',
  '÷ü' => 'è²©',
  '÷ý' => 'è¾¦',
  '÷þ' => 'éˆ‘',
  'ø¡' => 'é˜ª',
  'ø¢' => 'å…«',
  'ø£' => 'å�­',
  'ø¤' => 'æ�Œ',
  'ø¥' => 'ä½©',
  'ø¦' => 'å”„',
  'ø§' => 'æ‚–',
  'ø¨' => 'æ•—',
  'ø©' => 'æ²›',
  'øª' => 'æµ¿',
  'ø«' => 'ç‰Œ',
  'ø¬' => 'ç‹½',
  'ø­' => 'ç¨—',
  'ø®' => 'è¦‡',
  'ø¯' => 'è²�',
  'ø°' => 'å½­',
  'ø±' => 'æ¾Ž',
  'ø²' => 'çƒ¹',
  'ø³' => 'è†¨',
  'ø´' => 'æ„Ž',
  'øµ' => 'ä¾¿',
  'ø¶' => 'å��',
  'ø·' => 'æ‰�',
  'ø¸' => 'ç‰‡',
  'ø¹' => 'ç¯‡',
  'øº' => 'ç·¨',
  'ø»' => 'ç¿©',
  'ø¼' => 'é��',
  'ø½' => 'éž­',
  'ø¾' => 'é¨™',
  'ø¿' => 'è²¶',
  'øÀ' => 'å�ª',
  'øÁ' => 'å¹³',
  'øÂ' => 'æž°',
  'øÃ' => 'è��',
  'øÄ' => 'è©•',
  'øÅ' => 'å� ',
  'øÆ' => 'å¬–',
  'øÇ' => 'å¹£',
  'øÈ' => 'å»¢',
  'øÉ' => 'å¼Š',
  'øÊ' => 'æ–ƒ',
  'øË' => 'è‚º',
  'øÌ' => 'è”½',
  'øÍ' => 'é–‰',
  'øÎ' => 'é™›',
  'øÏ' => 'ä½ˆ',
  'øÐ' => 'åŒ…',
  'øÑ' => 'åŒ�',
  'øÒ' => 'åŒ�',
  'øÓ' => 'å’†',
  'øÔ' => 'å“º',
  'øÕ' => 'åœƒ',
  'øÖ' => 'å¸ƒ',
  'ø×' => 'æ€–',
  'øØ' => 'æŠ›',
  'øÙ' => 'æŠ±',
  'øÚ' => 'æ�•',
  'øÛ' => 'ï¨†',
  'øÜ' => 'æ³¡',
  'øÝ' => 'æµ¦',
  'øÞ' => 'ç–±',
  'øß' => 'ç ²',
  'øà' => 'èƒž',
  'øá' => 'è„¯',
  'øâ' => 'è‹ž',
  'øã' => 'è‘¡',
  'øä' => 'è’²',
  'øå' => 'è¢�',
  'øæ' => 'è¤’',
  'øç' => 'é€‹',
  'øè' => 'é‹ª',
  'øé' => 'é£½',
  'øê' => 'é®‘',
  'øë' => 'å¹…',
  'øì' => 'æš´',
  'øí' => 'æ›�',
  'øî' => 'ç€‘',
  'øï' => 'çˆ†',
  'øð' => 'ï¨‡',
  'øñ' => 'ä¿µ',
  'øò' => 'å‰½',
  'øó' => 'å½ª',
  'øô' => 'æ…“',
  'øõ' => 'æ�“',
  'øö' => 'æ¨™',
  'ø÷' => 'æ¼‚',
  'øø' => 'ç“¢',
  'øù' => 'ç¥¨',
  'øú' => 'è¡¨',
  'øû' => 'è±¹',
  'øü' => 'é£‡',
  'øý' => 'é£„',
  'øþ' => 'é©ƒ',
  'ù¡' => 'å“�',
  'ù¢' => 'ç¨Ÿ',
  'ù£' => 'æ¥“',
  'ù¤' => 'è«·',
  'ù¥' => 'è±Š',
  'ù¦' => 'é¢¨',
  'ù§' => 'é¦®',
  'ù¨' => 'å½¼',
  'ù©' => 'æŠ«',
  'ùª' => 'ç–²',
  'ù«' => 'çš®',
  'ù¬' => 'è¢«',
  'ù­' => 'é�¿',
  'ù®' => 'é™‚',
  'ù¯' => 'åŒ¹',
  'ù°' => 'å¼¼',
  'ù±' => 'å¿…',
  'ù²' => 'æ³Œ',
  'ù³' => 'ç�Œ',
  'ù´' => 'ç•¢',
  'ùµ' => 'ç–‹',
  'ù¶' => 'ç­†',
  'ù·' => 'è‹¾',
  'ù¸' => 'é¦�',
  'ù¹' => 'ä¹�',
  'ùº' => 'é€¼',
  'ù»' => 'ä¸‹',
  'ù¼' => 'ä½•',
  'ù½' => 'åŽ¦',
  'ù¾' => 'å¤�',
  'ù¿' => 'å»ˆ',
  'ùÀ' => 'æ˜°',
  'ùÁ' => 'æ²³',
  'ùÂ' => 'ç‘•',
  'ùÃ' => 'è�·',
  'ùÄ' => 'è�¦',
  'ùÅ' => 'è³€',
  'ùÆ' => 'é��',
  'ùÇ' => 'éœž',
  'ùÈ' => 'é°•',
  'ùÉ' => 'å£‘',
  'ùÊ' => 'å­¸',
  'ùË' => 'è™�',
  'ùÌ' => 'è¬”',
  'ùÍ' => 'é¶´',
  'ùÎ' => 'å¯’',
  'ùÏ' => 'æ�¨',
  'ùÐ' => 'æ‚�',
  'ùÑ' => 'æ—±',
  'ùÒ' => 'æ±—',
  'ùÓ' => 'æ¼¢',
  'ùÔ' => 'æ¾£',
  'ùÕ' => 'ç€š',
  'ùÖ' => 'ç½•',
  'ù×' => 'ç¿°',
  'ùØ' => 'é–‘',
  'ùÙ' => 'é–’',
  'ùÚ' => 'é™�',
  'ùÛ' => 'éŸ“',
  'ùÜ' => 'å‰²',
  'ùÝ' => 'è½„',
  'ùÞ' => 'å‡½',
  'ùß' => 'å�«',
  'ùà' => 'å’¸',
  'ùá' => 'å•£',
  'ùâ' => 'å–Š',
  'ùã' => 'æª»',
  'ùä' => 'æ¶µ',
  'ùå' => 'ç·˜',
  'ùæ' => 'è‰¦',
  'ùç' => 'éŠœ',
  'ùè' => 'é™·',
  'ùé' => 'é¹¹',
  'ùê' => 'å�ˆ',
  'ùë' => 'å“ˆ',
  'ùì' => 'ç›’',
  'ùí' => 'è›¤',
  'ùî' => 'é–¤',
  'ùï' => 'é—”',
  'ùð' => 'é™œ',
  'ùñ' => 'äº¢',
  'ùò' => 'ä¼‰',
  'ùó' => 'å§®',
  'ùô' => 'å«¦',
  'ùõ' => 'å··',
  'ùö' => 'æ�’',
  'ù÷' => 'æŠ—',
  'ùø' => 'æ�­',
  'ùù' => 'æ¡�',
  'ùú' => 'æ²†',
  'ùû' => 'æ¸¯',
  'ùü' => 'ç¼¸',
  'ùý' => 'è‚›',
  'ùþ' => 'èˆª',
  'ú¡' => 'ï¨ˆ',
  'ú¢' => 'ï¨‰',
  'ú£' => 'é …',
  'ú¤' => 'äº¥',
  'ú¥' => 'å�•',
  'ú¦' => 'å’³',
  'ú§' => 'åž“',
  'ú¨' => 'å¥š',
  'ú©' => 'å­©',
  'úª' => 'å®³',
  'ú«' => 'æ‡ˆ',
  'ú¬' => 'æ¥·',
  'ú­' => 'æµ·',
  'ú®' => 'ç€£',
  'ú¯' => 'èŸ¹',
  'ú°' => 'è§£',
  'ú±' => 'è©²',
  'ú²' => 'è«§',
  'ú³' => 'é‚‚',
  'ú´' => 'é§­',
  'úµ' => 'éª¸',
  'ú¶' => 'åŠ¾',
  'ú·' => 'æ ¸',
  'ú¸' => 'å€–',
  'ú¹' => 'å¹¸',
  'úº' => 'æ��',
  'ú»' => 'è�‡',
  'ú¼' => 'è¡Œ',
  'ú½' => 'äº«',
  'ú¾' => 'å�‘',
  'ú¿' => 'åš®',
  'úÀ' => 'ç�¦',
  'úÁ' => 'é„•',
  'úÂ' => 'éŸ¿',
  'úÃ' => 'é¤‰',
  'úÄ' => 'é¥—',
  'úÅ' => 'é¦™',
  'úÆ' => 'å™“',
  'úÇ' => 'å¢Ÿ',
  'úÈ' => 'è™›',
  'úÉ' => 'è¨±',
  'úÊ' => 'æ†²',
  'úË' => 'æ«¶',
  'úÌ' => 'ç�»',
  'úÍ' => 'è»’',
  'úÎ' => 'æ­‡',
  'úÏ' => 'éšª',
  'úÐ' => 'é©—',
  'úÑ' => 'å¥•',
  'úÒ' => 'çˆ€',
  'úÓ' => 'èµ«',
  'úÔ' => 'é�©',
  'úÕ' => 'ä¿”',
  'úÖ' => 'å³´',
  'ú×' => 'å¼¦',
  'úØ' => 'æ‡¸',
  'úÙ' => 'æ™›',
  'úÚ' => 'æ³«',
  'úÛ' => 'ç‚«',
  'úÜ' => 'çŽ„',
  'úÝ' => 'çŽ¹',
  'úÞ' => 'ç�¾',
  'úß' => 'çœ©',
  'úà' => 'ç��',
  'úá' => 'çµƒ',
  'úâ' => 'çµ¢',
  'úã' => 'ç¸£',
  'úä' => 'èˆ·',
  'úå' => 'è¡’',
  'úæ' => 'ï¨Š',
  'úç' => 'è³¢',
  'úè' => 'é‰‰',
  'úé' => 'é¡¯',
  'úê' => 'å­‘',
  'úë' => 'ç©´',
  'úì' => 'è¡€',
  'úí' => 'é �',
  'úî' => 'å«Œ',
  'úï' => 'ä¿ ',
  'úð' => 'å�”',
  'úñ' => 'å¤¾',
  'úò' => 'å³½',
  'úó' => 'æŒ¾',
  'úô' => 'æµ¹',
  'úõ' => 'ç‹¹',
  'úö' => 'è„…',
  'ú÷' => 'è„‡',
  'úø' => 'èŽ¢',
  'úù' => 'é‹�',
  'úú' => 'é °',
  'úû' => 'äº¨',
  'úü' => 'å…„',
  'úý' => 'åˆ‘',
  'úþ' => 'åž‹',
  'û¡' => 'å½¢',
  'û¢' => 'æ³‚',
  'û£' => 'æ»Ž',
  'û¤' => 'ç€…',
  'û¥' => 'ç��',
  'û¦' => 'ç‚¯',
  'û§' => 'ç†’',
  'û¨' => 'ç�©',
  'û©' => 'ç‘©',
  'ûª' => 'è�Š',
  'û«' => 'èž¢',
  'û¬' => 'è¡¡',
  'û­' => 'é€ˆ',
  'û®' => 'é‚¢',
  'û¯' => 'éŽ£',
  'û°' => 'é¦¨',
  'û±' => 'å…®',
  'û²' => 'å½—',
  'û³' => 'æƒ ',
  'û´' => 'æ…§',
  'ûµ' => 'æš³',
  'û¶' => 'è•™',
  'û·' => 'è¹Š',
  'û¸' => 'é†¯',
  'û¹' => 'éž‹',
  'ûº' => 'ä¹Ž',
  'û»' => 'äº’',
  'û¼' => 'å‘¼',
  'û½' => 'å£•',
  'û¾' => 'å£º',
  'û¿' => 'å¥½',
  'ûÀ' => 'å²µ',
  'ûÁ' => 'å¼§',
  'ûÂ' => 'æˆ¶',
  'ûÃ' => 'æ‰ˆ',
  'ûÄ' => 'æ˜Š',
  'ûÅ' => 'æ™§',
  'ûÆ' => 'æ¯«',
  'ûÇ' => 'æµ©',
  'ûÈ' => 'æ·�',
  'ûÉ' => 'æ¹–',
  'ûÊ' => 'æ»¸',
  'ûË' => 'æ¾”',
  'ûÌ' => 'æ¿ ',
  'ûÍ' => 'æ¿©',
  'ûÎ' => 'ç��',
  'ûÏ' => 'ç‹�',
  'ûÐ' => 'ç�¥',
  'ûÑ' => 'ç‘š',
  'ûÒ' => 'ç“ ',
  'ûÓ' => 'çš“',
  'ûÔ' => 'ç¥œ',
  'ûÕ' => 'ç³Š',
  'ûÖ' => 'ç¸ž',
  'û×' => 'èƒ¡',
  'ûØ' => 'èŠ¦',
  'ûÙ' => 'è‘«',
  'ûÚ' => 'è’¿',
  'ûÛ' => 'è™Ž',
  'ûÜ' => 'è™Ÿ',
  'ûÝ' => 'è�´',
  'ûÞ' => 'è­·',
  'ûß' => 'è±ª',
  'ûà' => 'éŽ¬',
  'ûá' => 'é €',
  'ûâ' => 'é¡¥',
  'ûã' => 'æƒ‘',
  'ûä' => 'æˆ–',
  'ûå' => 'é…·',
  'ûæ' => 'å©š',
  'ûç' => 'æ˜�',
  'ûè' => 'æ··',
  'ûé' => 'æ¸¾',
  'ûê' => 'ç�¿',
  'ûë' => 'é­‚',
  'ûì' => 'å¿½',
  'ûí' => 'æƒš',
  'ûî' => 'ç¬�',
  'ûï' => 'å“„',
  'ûð' => 'å¼˜',
  'ûñ' => 'æ±ž',
  'ûò' => 'æ³“',
  'ûó' => 'æ´ª',
  'ûô' => 'çƒ˜',
  'ûõ' => 'ç´…',
  'ûö' => 'è™¹',
  'û÷' => 'è¨Œ',
  'ûø' => 'é´»',
  'ûù' => 'åŒ–',
  'ûú' => 'å’Œ',
  'ûû' => 'å¬…',
  'ûü' => 'æ¨º',
  'ûý' => 'ç�«',
  'ûþ' => 'ç•µ',
  'ü¡' => 'ç¦�',
  'ü¢' => 'ç¦¾',
  'ü£' => 'èŠ±',
  'ü¤' => 'è�¯',
  'ü¥' => 'è©±',
  'ü¦' => 'è­�',
  'ü§' => 'è²¨',
  'ü¨' => 'é�´',
  'ü©' => 'ï¨‹',
  'üª' => 'æ“´',
  'ü«' => 'æ”«',
  'ü¬' => 'ç¢º',
  'ü­' => 'ç¢»',
  'ü®' => 'ç©«',
  'ü¯' => 'ä¸¸',
  'ü°' => 'å–š',
  'ü±' => 'å¥�',
  'ü²' => 'å®¦',
  'ü³' => 'å¹»',
  'ü´' => 'æ‚£',
  'üµ' => 'æ�›',
  'ü¶' => 'æ­¡',
  'ü·' => 'æ™¥',
  'ü¸' => 'æ¡“',
  'ü¹' => 'æ¸™',
  'üº' => 'ç…¥',
  'ü»' => 'ç’°',
  'ü¼' => 'ç´ˆ',
  'ü½' => 'é‚„',
  'ü¾' => 'é©©',
  'ü¿' => 'é°¥',
  'üÀ' => 'æ´»',
  'üÁ' => 'æ»‘',
  'üÂ' => 'çŒ¾',
  'üÃ' => 'è±�',
  'üÄ' => 'é—Š',
  'üÅ' => 'å‡°',
  'üÆ' => 'å¹Œ',
  'üÇ' => 'å¾¨',
  'üÈ' => 'æ��',
  'üÉ' => 'æƒ¶',
  'üÊ' => 'æ„°',
  'üË' => 'æ…Œ',
  'üÌ' => 'æ™ƒ',
  'üÍ' => 'æ™„',
  'üÎ' => 'æ¦¥',
  'üÏ' => 'æ³�',
  'üÐ' => 'æ¹Ÿ',
  'üÑ' => 'æ»‰',
  'üÒ' => 'æ½¢',
  'üÓ' => 'ç…Œ',
  'üÔ' => 'ç’œ',
  'üÕ' => 'çš‡',
  'üÖ' => 'ç¯�',
  'ü×' => 'ç°§',
  'üØ' => 'è�’',
  'üÙ' => 'è�—',
  'üÚ' => 'é�‘',
  'üÛ' => 'éš�',
  'üÜ' => 'é»ƒ',
  'üÝ' => 'åŒ¯',
  'üÞ' => 'å›ž',
  'üß' => 'å»»',
  'üà' => 'å¾Š',
  'üá' => 'æ�¢',
  'üâ' => 'æ‚”',
  'üã' => 'æ‡·',
  'üä' => 'æ™¦',
  'üå' => 'æœƒ',
  'üæ' => 'æªœ',
  'üç' => 'æ·®',
  'üè' => 'æ¾®',
  'üé' => 'ç�°',
  'üê' => 'ç�ª',
  'üë' => 'ç¹ª',
  'üì' => 'è†¾',
  'üí' => 'èŒ´',
  'üî' => 'è›”',
  'üï' => 'èª¨',
  'üð' => 'è³„',
  'üñ' => 'åŠƒ',
  'üò' => 'ç�²',
  'üó' => 'å®–',
  'üô' => 'æ©«',
  'üõ' => 'é�„',
  'üö' => 'å“®',
  'ü÷' => 'åš†',
  'üø' => 'å­�',
  'üù' => 'æ•ˆ',
  'üú' => 'æ–…',
  'üû' => 'æ›‰',
  'üü' => 'æ¢Ÿ',
  'üý' => 'æ¶�',
  'üþ' => 'æ·†',
  'ý¡' => 'çˆ»',
  'ý¢' => 'è‚´',
  'ý£' => 'é…µ',
  'ý¤' => 'é©�',
  'ý¥' => 'ä¾¯',
  'ý¦' => 'å€™',
  'ý§' => 'åŽš',
  'ý¨' => 'å�Ž',
  'ý©' => 'å�¼',
  'ýª' => 'å–‰',
  'ý«' => 'å—…',
  'ý¬' => 'å¸¿',
  'ý­' => 'å¾Œ',
  'ý®' => 'æœ½',
  'ý¯' => 'ç…¦',
  'ý°' => 'ç��',
  'ý±' => 'é€…',
  'ý²' => 'å‹›',
  'ý³' => 'å‹³',
  'ý´' => 'å¡¤',
  'ýµ' => 'å£Ž',
  'ý¶' => 'ç„„',
  'ý·' => 'ç†�',
  'ý¸' => 'ç‡»',
  'ý¹' => 'è–°',
  'ýº' => 'è¨“',
  'ý»' => 'æšˆ',
  'ý¼' => 'è–¨',
  'ý½' => 'å–§',
  'ý¾' => 'æš„',
  'ý¿' => 'ç…Š',
  'ýÀ' => 'è�±',
  'ýÁ' => 'å�‰',
  'ýÂ' => 'å–™',
  'ýÃ' => 'æ¯�',
  'ýÄ' => 'å½™',
  'ýÅ' => 'å¾½',
  'ýÆ' => 'æ�®',
  'ýÇ' => 'æš‰',
  'ýÈ' => 'ç…‡',
  'ýÉ' => 'è«±',
  'ýÊ' => 'è¼�',
  'ýË' => 'éº¾',
  'ýÌ' => 'ä¼‘',
  'ýÍ' => 'æ�º',
  'ýÎ' => 'çƒ‹',
  'ýÏ' => 'ç•¦',
  'ýÐ' => 'è™§',
  'ýÑ' => 'æ�¤',
  'ýÒ' => 'è­Ž',
  'ýÓ' => 'é·¸',
  'ýÔ' => 'å…‡',
  'ýÕ' => 'å‡¶',
  'ýÖ' => 'åŒˆ',
  'ý×' => 'æ´¶',
  'ýØ' => 'èƒ¸',
  'ýÙ' => 'é»‘',
  'ýÚ' => 'æ˜•',
  'ýÛ' => 'æ¬£',
  'ýÜ' => 'ç‚˜',
  'ýÝ' => 'ç—•',
  'ýÞ' => 'å�ƒ',
  'ýß' => 'å±¹',
  'ýà' => 'ç´‡',
  'ýá' => 'è¨–',
  'ýâ' => 'æ¬ ',
  'ýã' => 'æ¬½',
  'ýä' => 'æ­†',
  'ýå' => 'å�¸',
  'ýæ' => 'æ�°',
  'ýç' => 'æ´½',
  'ýè' => 'ç¿•',
  'ýé' => 'èˆˆ',
  'ýê' => 'åƒ–',
  'ýë' => 'å‡ž',
  'ýì' => 'å–œ',
  'ýí' => 'å™«',
  'ýî' => 'å›�',
  'ýï' => 'å§¬',
  'ýð' => 'å¬‰',
  'ýñ' => 'å¸Œ',
  'ýò' => 'æ†™',
  'ýó' => 'æ†˜',
  'ýô' => 'æˆ±',
  'ýõ' => 'æ™ž',
  'ýö' => 'æ›¦',
  'ý÷' => 'ç†™',
  'ýø' => 'ç†¹',
  'ýù' => 'ç†º',
  'ýú' => 'çŠ§',
  'ýû' => 'ç¦§',
  'ýü' => 'ç¨€',
  'ýý' => 'ç¾²',
  'ýþ' => 'è©°',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => 'Âœ',
  '' => '	',
  '' => 'Â†',
  '' => '',
  '' => 'Â—',
  '	' => 'Â�',
  '
' => 'ÂŽ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => 'Â�',
  '' => 'Â…',
  '' => '',
  '' => 'Â‡',
  '' => '',
  '' => '',
  '' => 'Â’',
  '' => 'Â�',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => 'Â€',
  '!' => 'Â�',
  '"' => 'Â‚',
  '#' => 'Âƒ',
  '$' => 'Â„',
  '%' => '
',
  '&' => '',
  '\'' => '',
  '(' => 'Âˆ',
  ')' => 'Â‰',
  '*' => 'ÂŠ',
  '+' => 'Â‹',
  ',' => 'ÂŒ',
  '-' => '',
  '.' => '',
  '/' => '',
  0 => 'Â�',
  1 => 'Â‘',
  2 => '',
  3 => 'Â“',
  4 => 'Â”',
  5 => 'Â•',
  6 => 'Â–',
  7 => '',
  8 => 'Â˜',
  9 => 'Â™',
  ':' => 'Âš',
  ';' => 'Â›',
  '<' => '',
  '=' => '',
  '>' => 'Âž',
  '?' => '',
  '@' => ' ',
  'A' => 'Â ',
  'B' => 'Ã¢',
  'C' => 'Ã¤',
  'D' => 'Ã ',
  'E' => 'Ã¡',
  'F' => 'Ã£',
  'G' => 'Ã¥',
  'H' => 'Ã§',
  'I' => 'Ã±',
  'J' => 'Â¢',
  'K' => '.',
  'L' => '<',
  'M' => '(',
  'N' => '+',
  'O' => '|',
  'P' => '&',
  'Q' => 'Ã©',
  'R' => 'Ãª',
  'S' => 'Ã«',
  'T' => 'Ã¨',
  'U' => 'Ã­',
  'V' => 'Ã®',
  'W' => 'Ã¯',
  'X' => 'Ã¬',
  'Y' => 'ÃŸ',
  'Z' => '!',
  '[' => '$',
  '\\' => '*',
  ']' => ')',
  '^' => ';',
  '_' => 'Â¬',
  '`' => '-',
  'a' => '/',
  'b' => 'Ã‚',
  'c' => 'Ã„',
  'd' => 'Ã€',
  'e' => 'Ã�',
  'f' => 'Ãƒ',
  'g' => 'Ã…',
  'h' => 'Ã‡',
  'i' => 'Ã‘',
  'j' => 'Â¦',
  'k' => ',',
  'l' => '%',
  'm' => '_',
  'n' => '>',
  'o' => '?',
  'p' => 'Ã¸',
  'q' => 'Ã‰',
  'r' => 'ÃŠ',
  's' => 'Ã‹',
  't' => 'Ãˆ',
  'u' => 'Ã�',
  'v' => 'ÃŽ',
  'w' => 'Ã�',
  'x' => 'ÃŒ',
  'y' => '`',
  'z' => ':',
  '{' => '#',
  '|' => '@',
  '}' => '\'',
  '~' => '=',
  '' => '"',
  '€' => 'Ã˜',
  '�' => 'a',
  '‚' => 'b',
  'ƒ' => 'c',
  '„' => 'd',
  '…' => 'e',
  '†' => 'f',
  '‡' => 'g',
  'ˆ' => 'h',
  '‰' => 'i',
  'Š' => 'Â«',
  '‹' => 'Â»',
  'Œ' => 'Ã°',
  '�' => 'Ã½',
  'Ž' => 'Ã¾',
  '�' => 'Â±',
  '�' => 'Â°',
  '‘' => 'j',
  '’' => 'k',
  '“' => 'l',
  '”' => 'm',
  '•' => 'n',
  '–' => 'o',
  '—' => 'p',
  '˜' => 'q',
  '™' => 'r',
  'š' => 'Âª',
  '›' => 'Âº',
  'œ' => 'Ã¦',
  '�' => 'Â¸',
  'ž' => 'Ã†',
  'Ÿ' => 'Â¤',
  ' ' => 'Âµ',
  '¡' => '~',
  '¢' => 's',
  '£' => 't',
  '¤' => 'u',
  '¥' => 'v',
  '¦' => 'w',
  '§' => 'x',
  '¨' => 'y',
  '©' => 'z',
  'ª' => 'Â¡',
  '«' => 'Â¿',
  '¬' => 'Ã�',
  '­' => 'Ã�',
  '®' => 'Ãž',
  '¯' => 'Â®',
  '°' => '^',
  '±' => 'Â£',
  '²' => 'Â¥',
  '³' => 'Â·',
  '´' => 'Â©',
  'µ' => 'Â§',
  '¶' => 'Â¶',
  '·' => 'Â¼',
  '¸' => 'Â½',
  '¹' => 'Â¾',
  'º' => '[',
  '»' => ']',
  '¼' => 'Â¯',
  '½' => 'Â¨',
  '¾' => 'Â´',
  '¿' => 'Ã—',
  'À' => '{',
  'Á' => 'A',
  'Â' => 'B',
  'Ã' => 'C',
  'Ä' => 'D',
  'Å' => 'E',
  'Æ' => 'F',
  'Ç' => 'G',
  'È' => 'H',
  'É' => 'I',
  'Ê' => 'Â­',
  'Ë' => 'Ã´',
  'Ì' => 'Ã¶',
  'Í' => 'Ã²',
  'Î' => 'Ã³',
  'Ï' => 'Ãµ',
  'Ð' => '}',
  'Ñ' => 'J',
  'Ò' => 'K',
  'Ó' => 'L',
  'Ô' => 'M',
  'Õ' => 'N',
  'Ö' => 'O',
  '×' => 'P',
  'Ø' => 'Q',
  'Ù' => 'R',
  'Ú' => 'Â¹',
  'Û' => 'Ã»',
  'Ü' => 'Ã¼',
  'Ý' => 'Ã¹',
  'Þ' => 'Ãº',
  'ß' => 'Ã¿',
  'à' => '\\',
  'á' => 'Ã·',
  'â' => 'S',
  'ã' => 'T',
  'ä' => 'U',
  'å' => 'V',
  'æ' => 'W',
  'ç' => 'X',
  'è' => 'Y',
  'é' => 'Z',
  'ê' => 'Â²',
  'ë' => 'Ã”',
  'ì' => 'Ã–',
  'í' => 'Ã’',
  'î' => 'Ã“',
  'ï' => 'Ã•',
  'ð' => '0',
  'ñ' => '1',
  'ò' => '2',
  'ó' => '3',
  'ô' => '4',
  'õ' => '5',
  'ö' => '6',
  '÷' => '7',
  'ø' => '8',
  'ù' => '9',
  'ú' => 'Â³',
  'û' => 'Ã›',
  'ü' => 'Ãœ',
  'ý' => 'Ã™',
  'þ' => 'Ãš',
  'ÿ' => 'ÂŸ',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => '%',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
  '€' => 'Â€',
  '�' => 'Â�',
  '‚' => 'Â‚',
  'ƒ' => 'Âƒ',
  '„' => 'Â„',
  '…' => 'Â…',
  '†' => 'Â†',
  '‡' => 'Â‡',
  'ˆ' => 'Âˆ',
  '‰' => 'Â‰',
  'Š' => 'ÂŠ',
  '‹' => 'Â‹',
  'Œ' => 'ÂŒ',
  '�' => 'Â�',
  'Ž' => 'ÂŽ',
  '�' => 'Â�',
  '�' => 'Â�',
  '‘' => 'Â‘',
  '’' => 'Â’',
  '“' => 'Â“',
  '”' => 'Â”',
  '•' => 'Â•',
  '–' => 'Â–',
  '—' => 'Â—',
  '˜' => 'Â˜',
  '™' => 'Â™',
  'š' => 'Âš',
  '›' => 'Â›',
  'œ' => 'Âœ',
  '�' => 'Â�',
  'ž' => 'Âž',
  'Ÿ' => 'ÂŸ',
  ' ' => 'Â ',
  '¡' => 'á¸‚',
  '¢' => 'á¸ƒ',
  '£' => 'Â£',
  '¤' => 'ÄŠ',
  '¥' => 'Ä‹',
  '¦' => 'á¸Š',
  '§' => 'Â§',
  '¨' => 'áº€',
  '©' => 'Â©',
  'ª' => 'áº‚',
  '«' => 'á¸‹',
  '¬' => 'á»²',
  '­' => 'Â­',
  '®' => 'Â®',
  '¯' => 'Å¸',
  '°' => 'á¸ž',
  '±' => 'á¸Ÿ',
  '²' => 'Ä ',
  '³' => 'Ä¡',
  '´' => 'á¹€',
  'µ' => 'á¹�',
  '¶' => 'Â¶',
  '·' => 'á¹–',
  '¸' => 'áº�',
  '¹' => 'á¹—',
  'º' => 'áºƒ',
  '»' => 'á¹ ',
  '¼' => 'á»³',
  '½' => 'áº„',
  '¾' => 'áº…',
  '¿' => 'á¹¡',
  'À' => 'Ã€',
  'Á' => 'Ã�',
  'Â' => 'Ã‚',
  'Ã' => 'Ãƒ',
  'Ä' => 'Ã„',
  'Å' => 'Ã…',
  'Æ' => 'Ã†',
  'Ç' => 'Ã‡',
  'È' => 'Ãˆ',
  'É' => 'Ã‰',
  'Ê' => 'ÃŠ',
  'Ë' => 'Ã‹',
  'Ì' => 'ÃŒ',
  'Í' => 'Ã�',
  'Î' => 'ÃŽ',
  'Ï' => 'Ã�',
  'Ð' => 'Å´',
  'Ñ' => 'Ã‘',
  'Ò' => 'Ã’',
  'Ó' => 'Ã“',
  'Ô' => 'Ã”',
  'Õ' => 'Ã•',
  'Ö' => 'Ã–',
  '×' => 'á¹ª',
  'Ø' => 'Ã˜',
  'Ù' => 'Ã™',
  'Ú' => 'Ãš',
  'Û' => 'Ã›',
  'Ü' => 'Ãœ',
  'Ý' => 'Ã�',
  'Þ' => 'Å¶',
  'ß' => 'ÃŸ',
  'à' => 'Ã ',
  'á' => 'Ã¡',
  'â' => 'Ã¢',
  'ã' => 'Ã£',
  'ä' => 'Ã¤',
  'å' => 'Ã¥',
  'æ' => 'Ã¦',
  'ç' => 'Ã§',
  'è' => 'Ã¨',
  'é' => 'Ã©',
  'ê' => 'Ãª',
  'ë' => 'Ã«',
  'ì' => 'Ã¬',
  'í' => 'Ã­',
  'î' => 'Ã®',
  'ï' => 'Ã¯',
  'ð' => 'Åµ',
  'ñ' => 'Ã±',
  'ò' => 'Ã²',
  'ó' => 'Ã³',
  'ô' => 'Ã´',
  'õ' => 'Ãµ',
  'ö' => 'Ã¶',
  '÷' => 'á¹«',
  'ø' => 'Ã¸',
  'ù' => 'Ã¹',
  'ú' => 'Ãº',
  'û' => 'Ã»',
  'ü' => 'Ã¼',
  'ý' => 'Ã½',
  'þ' => 'Å·',
  'ÿ' => 'Ã¿',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => '%',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
  '€' => 'â‚¬',
  '‚' => 'â€š',
  'ƒ' => 'Æ’',
  '„' => 'â€ž',
  '…' => 'â€¦',
  '†' => 'â€ ',
  '‡' => 'â€¡',
  'ˆ' => 'Ë†',
  '‰' => 'â€°',
  'Š' => 'Å ',
  '‹' => 'â€¹',
  'Œ' => 'Å’',
  '‘' => 'â€˜',
  '’' => 'â€™',
  '“' => 'â€œ',
  '”' => 'â€�',
  '•' => 'â€¢',
  '–' => 'â€“',
  '—' => 'â€”',
  '˜' => 'Ëœ',
  '™' => 'â„¢',
  'š' => 'Å¡',
  '›' => 'â€º',
  'œ' => 'Å“',
  'Ÿ' => 'Å¸',
  ' ' => 'Â ',
  '¡' => 'Â¡',
  '¢' => 'Â¢',
  '£' => 'Â£',
  '¤' => 'Â¤',
  '¥' => 'Â¥',
  '¦' => 'Â¦',
  '§' => 'Â§',
  '¨' => 'Â¨',
  '©' => 'Â©',
  'ª' => 'Âª',
  '«' => 'Â«',
  '¬' => 'Â¬',
  '­' => 'Â­',
  '®' => 'Â®',
  '¯' => 'Â¯',
  '°' => 'Â°',
  '±' => 'Â±',
  '²' => 'Â²',
  '³' => 'Â³',
  '´' => 'Â´',
  'µ' => 'Âµ',
  '¶' => 'Â¶',
  '·' => 'Â·',
  '¸' => 'Â¸',
  '¹' => 'Â¹',
  'º' => 'Âº',
  '»' => 'Â»',
  '¼' => 'Â¼',
  '½' => 'Â½',
  '¾' => 'Â¾',
  '¿' => 'Â¿',
  'À' => 'Ã€',
  'Á' => 'Ã�',
  'Â' => 'Ã‚',
  'Ã' => 'Ãƒ',
  'Ä' => 'Ã„',
  'Å' => 'Ã…',
  'Æ' => 'Ã†',
  'Ç' => 'Ã‡',
  'È' => 'Ãˆ',
  'É' => 'Ã‰',
  'Ê' => 'ÃŠ',
  'Ë' => 'Ã‹',
  'Ì' => 'ÃŒ',
  'Í' => 'Ã�',
  'Î' => 'ÃŽ',
  'Ï' => 'Ã�',
  'Ð' => 'Äž',
  'Ñ' => 'Ã‘',
  'Ò' => 'Ã’',
  'Ó' => 'Ã“',
  'Ô' => 'Ã”',
  'Õ' => 'Ã•',
  'Ö' => 'Ã–',
  '×' => 'Ã—',
  'Ø' => 'Ã˜',
  'Ù' => 'Ã™',
  'Ú' => 'Ãš',
  'Û' => 'Ã›',
  'Ü' => 'Ãœ',
  'Ý' => 'Ä°',
  'Þ' => 'Åž',
  'ß' => 'ÃŸ',
  'à' => 'Ã ',
  'á' => 'Ã¡',
  'â' => 'Ã¢',
  'ã' => 'Ã£',
  'ä' => 'Ã¤',
  'å' => 'Ã¥',
  'æ' => 'Ã¦',
  'ç' => 'Ã§',
  'è' => 'Ã¨',
  'é' => 'Ã©',
  'ê' => 'Ãª',
  'ë' => 'Ã«',
  'ì' => 'Ã¬',
  'í' => 'Ã­',
  'î' => 'Ã®',
  'ï' => 'Ã¯',
  'ð' => 'ÄŸ',
  'ñ' => 'Ã±',
  'ò' => 'Ã²',
  'ó' => 'Ã³',
  'ô' => 'Ã´',
  'õ' => 'Ãµ',
  'ö' => 'Ã¶',
  '÷' => 'Ã·',
  'ø' => 'Ã¸',
  'ù' => 'Ã¹',
  'ú' => 'Ãº',
  'û' => 'Ã»',
  'ü' => 'Ã¼',
  'ý' => 'Ä±',
  'þ' => 'ÅŸ',
  'ÿ' => 'Ã¿',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => '%',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
  '€' => 'Â€',
  '�' => 'Â�',
  '‚' => 'Â‚',
  'ƒ' => 'Âƒ',
  '„' => 'Â„',
  '…' => 'Â…',
  '†' => 'Â†',
  '‡' => 'Â‡',
  'ˆ' => 'Âˆ',
  '‰' => 'Â‰',
  'Š' => 'ÂŠ',
  '‹' => 'Â‹',
  'Œ' => 'ÂŒ',
  '�' => 'Â�',
  'Ž' => 'ÂŽ',
  '�' => 'Â�',
  '�' => 'Â�',
  '‘' => 'Â‘',
  '’' => 'Â’',
  '“' => 'Â“',
  '”' => 'Â”',
  '•' => 'Â•',
  '–' => 'Â–',
  '—' => 'Â—',
  '˜' => 'Â˜',
  '™' => 'Â™',
  'š' => 'Âš',
  '›' => 'Â›',
  'œ' => 'Âœ',
  '�' => 'Â�',
  'ž' => 'Âž',
  'Ÿ' => 'ÂŸ',
  ' ' => 'Â ',
  '¡' => 'Ä„',
  '¢' => 'Ä¸',
  '£' => 'Å–',
  '¤' => 'Â¤',
  '¥' => 'Ä¨',
  '¦' => 'Ä»',
  '§' => 'Â§',
  '¨' => 'Â¨',
  '©' => 'Å ',
  'ª' => 'Ä’',
  '«' => 'Ä¢',
  '¬' => 'Å¦',
  '­' => 'Â­',
  '®' => 'Å½',
  '¯' => 'Â¯',
  '°' => 'Â°',
  '±' => 'Ä…',
  '²' => 'Ë›',
  '³' => 'Å—',
  '´' => 'Â´',
  'µ' => 'Ä©',
  '¶' => 'Ä¼',
  '·' => 'Ë‡',
  '¸' => 'Â¸',
  '¹' => 'Å¡',
  'º' => 'Ä“',
  '»' => 'Ä£',
  '¼' => 'Å§',
  '½' => 'ÅŠ',
  '¾' => 'Å¾',
  '¿' => 'Å‹',
  'À' => 'Ä€',
  'Á' => 'Ã�',
  'Â' => 'Ã‚',
  'Ã' => 'Ãƒ',
  'Ä' => 'Ã„',
  'Å' => 'Ã…',
  'Æ' => 'Ã†',
  'Ç' => 'Ä®',
  'È' => 'ÄŒ',
  'É' => 'Ã‰',
  'Ê' => 'Ä˜',
  'Ë' => 'Ã‹',
  'Ì' => 'Ä–',
  'Í' => 'Ã�',
  'Î' => 'ÃŽ',
  'Ï' => 'Äª',
  'Ð' => 'Ä�',
  'Ñ' => 'Å…',
  'Ò' => 'ÅŒ',
  'Ó' => 'Ä¶',
  'Ô' => 'Ã”',
  'Õ' => 'Ã•',
  'Ö' => 'Ã–',
  '×' => 'Ã—',
  'Ø' => 'Ã˜',
  'Ù' => 'Å²',
  'Ú' => 'Ãš',
  'Û' => 'Ã›',
  'Ü' => 'Ãœ',
  'Ý' => 'Å¨',
  'Þ' => 'Åª',
  'ß' => 'ÃŸ',
  'à' => 'Ä�',
  'á' => 'Ã¡',
  'â' => 'Ã¢',
  'ã' => 'Ã£',
  'ä' => 'Ã¤',
  'å' => 'Ã¥',
  'æ' => 'Ã¦',
  'ç' => 'Ä¯',
  'è' => 'Ä�',
  'é' => 'Ã©',
  'ê' => 'Ä™',
  'ë' => 'Ã«',
  'ì' => 'Ä—',
  'í' => 'Ã­',
  'î' => 'Ã®',
  'ï' => 'Ä«',
  'ð' => 'Ä‘',
  'ñ' => 'Å†',
  'ò' => 'Å�',
  'ó' => 'Ä·',
  'ô' => 'Ã´',
  'õ' => 'Ãµ',
  'ö' => 'Ã¶',
  '÷' => 'Ã·',
  'ø' => 'Ã¸',
  'ù' => 'Å³',
  'ú' => 'Ãº',
  'û' => 'Ã»',
  'ü' => 'Ã¼',
  'ý' => 'Å©',
  'þ' => 'Å«',
  'ÿ' => 'Ë™',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => '%',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
  '€' => 'Ð‚',
  '�' => 'Ðƒ',
  '‚' => 'â€š',
  'ƒ' => 'Ñ“',
  '„' => 'â€ž',
  '…' => 'â€¦',
  '†' => 'â€ ',
  '‡' => 'â€¡',
  'ˆ' => 'â‚¬',
  '‰' => 'â€°',
  'Š' => 'Ð‰',
  '‹' => 'â€¹',
  'Œ' => 'ÐŠ',
  '�' => 'ÐŒ',
  'Ž' => 'Ð‹',
  '�' => 'Ð�',
  '�' => 'Ñ’',
  '‘' => 'â€˜',
  '’' => 'â€™',
  '“' => 'â€œ',
  '”' => 'â€�',
  '•' => 'â€¢',
  '–' => 'â€“',
  '—' => 'â€”',
  '™' => 'â„¢',
  'š' => 'Ñ™',
  '›' => 'â€º',
  'œ' => 'Ñš',
  '�' => 'Ñœ',
  'ž' => 'Ñ›',
  'Ÿ' => 'ÑŸ',
  ' ' => 'Â ',
  '¡' => 'ÐŽ',
  '¢' => 'Ñž',
  '£' => 'Ðˆ',
  '¤' => 'Â¤',
  '¥' => 'Ò�',
  '¦' => 'Â¦',
  '§' => 'Â§',
  '¨' => 'Ð�',
  '©' => 'Â©',
  'ª' => 'Ð„',
  '«' => 'Â«',
  '¬' => 'Â¬',
  '­' => 'Â­',
  '®' => 'Â®',
  '¯' => 'Ð‡',
  '°' => 'Â°',
  '±' => 'Â±',
  '²' => 'Ð†',
  '³' => 'Ñ–',
  '´' => 'Ò‘',
  'µ' => 'Âµ',
  '¶' => 'Â¶',
  '·' => 'Â·',
  '¸' => 'Ñ‘',
  '¹' => 'â„–',
  'º' => 'Ñ”',
  '»' => 'Â»',
  '¼' => 'Ñ˜',
  '½' => 'Ð…',
  '¾' => 'Ñ•',
  '¿' => 'Ñ—',
  'À' => 'Ð�',
  'Á' => 'Ð‘',
  'Â' => 'Ð’',
  'Ã' => 'Ð“',
  'Ä' => 'Ð”',
  'Å' => 'Ð•',
  'Æ' => 'Ð–',
  'Ç' => 'Ð—',
  'È' => 'Ð˜',
  'É' => 'Ð™',
  'Ê' => 'Ðš',
  'Ë' => 'Ð›',
  'Ì' => 'Ðœ',
  'Í' => 'Ð�',
  'Î' => 'Ðž',
  'Ï' => 'ÐŸ',
  'Ð' => 'Ð ',
  'Ñ' => 'Ð¡',
  'Ò' => 'Ð¢',
  'Ó' => 'Ð£',
  'Ô' => 'Ð¤',
  'Õ' => 'Ð¥',
  'Ö' => 'Ð¦',
  '×' => 'Ð§',
  'Ø' => 'Ð¨',
  'Ù' => 'Ð©',
  'Ú' => 'Ðª',
  'Û' => 'Ð«',
  'Ü' => 'Ð¬',
  'Ý' => 'Ð­',
  'Þ' => 'Ð®',
  'ß' => 'Ð¯',
  'à' => 'Ð°',
  'á' => 'Ð±',
  'â' => 'Ð²',
  'ã' => 'Ð³',
  'ä' => 'Ð´',
  'å' => 'Ðµ',
  'æ' => 'Ð¶',
  'ç' => 'Ð·',
  'è' => 'Ð¸',
  'é' => 'Ð¹',
  'ê' => 'Ðº',
  'ë' => 'Ð»',
  'ì' => 'Ð¼',
  'í' => 'Ð½',
  'î' => 'Ð¾',
  'ï' => 'Ð¿',
  'ð' => 'Ñ€',
  'ñ' => 'Ñ�',
  'ò' => 'Ñ‚',
  'ó' => 'Ñƒ',
  'ô' => 'Ñ„',
  'õ' => 'Ñ…',
  'ö' => 'Ñ†',
  '÷' => 'Ñ‡',
  'ø' => 'Ñˆ',
  'ù' => 'Ñ‰',
  'ú' => 'ÑŠ',
  'û' => 'Ñ‹',
  'ü' => 'ÑŒ',
  'ý' => 'Ñ�',
  'þ' => 'ÑŽ',
  'ÿ' => 'Ñ�',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => '%',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
  '€' => 'Ã‡',
  '�' => 'Ã¼',
  '‚' => 'Ã©',
  'ƒ' => 'Ã¢',
  '„' => 'Ã¤',
  '…' => 'Ã ',
  '†' => 'Ã¥',
  '‡' => 'Ã§',
  'ˆ' => 'Ãª',
  '‰' => 'Ã«',
  'Š' => 'Ã¨',
  '‹' => 'Ã¯',
  'Œ' => 'Ã®',
  '�' => 'Ä±',
  'Ž' => 'Ã„',
  '�' => 'Ã…',
  '�' => 'Ã‰',
  '‘' => 'Ã¦',
  '’' => 'Ã†',
  '“' => 'Ã´',
  '”' => 'Ã¶',
  '•' => 'Ã²',
  '–' => 'Ã»',
  '—' => 'Ã¹',
  '˜' => 'Ä°',
  '™' => 'Ã–',
  'š' => 'Ãœ',
  '›' => 'Ã¸',
  'œ' => 'Â£',
  '�' => 'Ã˜',
  'ž' => 'Åž',
  'Ÿ' => 'ÅŸ',
  ' ' => 'Ã¡',
  '¡' => 'Ã­',
  '¢' => 'Ã³',
  '£' => 'Ãº',
  '¤' => 'Ã±',
  '¥' => 'Ã‘',
  '¦' => 'Äž',
  '§' => 'ÄŸ',
  '¨' => 'Â¿',
  '©' => 'Â®',
  'ª' => 'Â¬',
  '«' => 'Â½',
  '¬' => 'Â¼',
  '­' => 'Â¡',
  '®' => 'Â«',
  '¯' => 'Â»',
  '°' => 'â–‘',
  '±' => 'â–’',
  '²' => 'â–“',
  '³' => 'â”‚',
  '´' => 'â”¤',
  'µ' => 'Ã�',
  '¶' => 'Ã‚',
  '·' => 'Ã€',
  '¸' => 'Â©',
  '¹' => 'â•£',
  'º' => 'â•‘',
  '»' => 'â•—',
  '¼' => 'â•�',
  '½' => 'Â¢',
  '¾' => 'Â¥',
  '¿' => 'â”�',
  'À' => 'â””',
  'Á' => 'â”´',
  'Â' => 'â”¬',
  'Ã' => 'â”œ',
  'Ä' => 'â”€',
  'Å' => 'â”¼',
  'Æ' => 'Ã£',
  'Ç' => 'Ãƒ',
  'È' => 'â•š',
  'É' => 'â•”',
  'Ê' => 'â•©',
  'Ë' => 'â•¦',
  'Ì' => 'â• ',
  'Í' => 'â•�',
  'Î' => 'â•¬',
  'Ï' => 'Â¤',
  'Ð' => 'Âº',
  'Ñ' => 'Âª',
  'Ò' => 'ÃŠ',
  'Ó' => 'Ã‹',
  'Ô' => 'Ãˆ',
  'Ö' => 'Ã�',
  '×' => 'ÃŽ',
  'Ø' => 'Ã�',
  'Ù' => 'â”˜',
  'Ú' => 'â”Œ',
  'Û' => 'â–ˆ',
  'Ü' => 'â–„',
  'Ý' => 'Â¦',
  'Þ' => 'ÃŒ',
  'ß' => 'â–€',
  'à' => 'Ã“',
  'á' => 'ÃŸ',
  'â' => 'Ã”',
  'ã' => 'Ã’',
  'ä' => 'Ãµ',
  'å' => 'Ã•',
  'æ' => 'Âµ',
  'è' => 'Ã—',
  'é' => 'Ãš',
  'ê' => 'Ã›',
  'ë' => 'Ã™',
  'ì' => 'Ã¬',
  'í' => 'Ã¿',
  'î' => 'Â¯',
  'ï' => 'Â´',
  'ð' => 'Â­',
  'ñ' => 'Â±',
  'ó' => 'Â¾',
  'ô' => 'Â¶',
  'õ' => 'Â§',
  'ö' => 'Ã·',
  '÷' => 'Â¸',
  'ø' => 'Â°',
  'ù' => 'Â¨',
  'ú' => 'Â·',
  'û' => 'Â¹',
  'ü' => 'Â³',
  'ý' => 'Â²',
  'þ' => 'â– ',
  'ÿ' => 'Â ',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => '%',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => '%',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
  '€' => 'Â€',
  '�' => 'Â�',
  '‚' => 'Â‚',
  'ƒ' => 'Âƒ',
  '„' => 'Â„',
  '…' => 'Â…',
  '†' => 'Â†',
  '‡' => 'Â‡',
  'ˆ' => 'Âˆ',
  '‰' => 'Â‰',
  'Š' => 'ÂŠ',
  '‹' => 'Â‹',
  'Œ' => 'ÂŒ',
  '�' => 'Â�',
  'Ž' => 'ÂŽ',
  '�' => 'Â�',
  '�' => 'Â�',
  '‘' => 'Â‘',
  '’' => 'Â’',
  '“' => 'Â“',
  '”' => 'Â”',
  '•' => 'Â•',
  '–' => 'Â–',
  '—' => 'Â—',
  '˜' => 'Â˜',
  '™' => 'Â™',
  'š' => 'Âš',
  '›' => 'Â›',
  'œ' => 'Âœ',
  '�' => 'Â�',
  'ž' => 'Âž',
  'Ÿ' => 'ÂŸ',
  ' ' => 'Â ',
  '¡' => 'Ä„',
  '¢' => 'Ä…',
  '£' => 'Å�',
  '¤' => 'â‚¬',
  '¥' => 'â€ž',
  '¦' => 'Å ',
  '§' => 'Â§',
  '¨' => 'Å¡',
  '©' => 'Â©',
  'ª' => 'È˜',
  '«' => 'Â«',
  '¬' => 'Å¹',
  '­' => 'Â­',
  '®' => 'Åº',
  '¯' => 'Å»',
  '°' => 'Â°',
  '±' => 'Â±',
  '²' => 'ÄŒ',
  '³' => 'Å‚',
  '´' => 'Å½',
  'µ' => 'â€�',
  '¶' => 'Â¶',
  '·' => 'Â·',
  '¸' => 'Å¾',
  '¹' => 'Ä�',
  'º' => 'È™',
  '»' => 'Â»',
  '¼' => 'Å’',
  '½' => 'Å“',
  '¾' => 'Å¸',
  '¿' => 'Å¼',
  'À' => 'Ã€',
  'Á' => 'Ã�',
  'Â' => 'Ã‚',
  'Ã' => 'Ä‚',
  'Ä' => 'Ã„',
  'Å' => 'Ä†',
  'Æ' => 'Ã†',
  'Ç' => 'Ã‡',
  'È' => 'Ãˆ',
  'É' => 'Ã‰',
  'Ê' => 'ÃŠ',
  'Ë' => 'Ã‹',
  'Ì' => 'ÃŒ',
  'Í' => 'Ã�',
  'Î' => 'ÃŽ',
  'Ï' => 'Ã�',
  'Ð' => 'Ä�',
  'Ñ' => 'Åƒ',
  'Ò' => 'Ã’',
  'Ó' => 'Ã“',
  'Ô' => 'Ã”',
  'Õ' => 'Å�',
  'Ö' => 'Ã–',
  '×' => 'Åš',
  'Ø' => 'Å°',
  'Ù' => 'Ã™',
  'Ú' => 'Ãš',
  'Û' => 'Ã›',
  'Ü' => 'Ãœ',
  'Ý' => 'Ä˜',
  'Þ' => 'Èš',
  'ß' => 'ÃŸ',
  'à' => 'Ã ',
  'á' => 'Ã¡',
  'â' => 'Ã¢',
  'ã' => 'Äƒ',
  'ä' => 'Ã¤',
  'å' => 'Ä‡',
  'æ' => 'Ã¦',
  'ç' => 'Ã§',
  'è' => 'Ã¨',
  'é' => 'Ã©',
  'ê' => 'Ãª',
  'ë' => 'Ã«',
  'ì' => 'Ã¬',
  'í' => 'Ã­',
  'î' => 'Ã®',
  'ï' => 'Ã¯',
  'ð' => 'Ä‘',
  'ñ' => 'Å„',
  'ò' => 'Ã²',
  'ó' => 'Ã³',
  'ô' => 'Ã´',
  'õ' => 'Å‘',
  'ö' => 'Ã¶',
  '÷' => 'Å›',
  'ø' => 'Å±',
  'ù' => 'Ã¹',
  'ú' => 'Ãº',
  'û' => 'Ã»',
  'ü' => 'Ã¼',
  'ý' => 'Ä™',
  'þ' => 'È›',
  'ÿ' => 'Ã¿',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => '%',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
  '€' => '×�',
  '�' => '×‘',
  '‚' => '×’',
  'ƒ' => '×“',
  '„' => '×”',
  '…' => '×•',
  '†' => '×–',
  '‡' => '×—',
  'ˆ' => '×˜',
  '‰' => '×™',
  'Š' => '×š',
  '‹' => '×›',
  'Œ' => '×œ',
  '�' => '×�',
  'Ž' => '×ž',
  '�' => '×Ÿ',
  '�' => '× ',
  '‘' => '×¡',
  '’' => '×¢',
  '“' => '×£',
  '”' => '×¤',
  '•' => '×¥',
  '–' => '×¦',
  '—' => '×§',
  '˜' => '×¨',
  '™' => '×©',
  'š' => '×ª',
  '›' => 'Â¢',
  'œ' => 'Â£',
  '�' => 'Â¥',
  'ž' => 'â‚§',
  'Ÿ' => 'Æ’',
  ' ' => 'Ã¡',
  '¡' => 'Ã­',
  '¢' => 'Ã³',
  '£' => 'Ãº',
  '¤' => 'Ã±',
  '¥' => 'Ã‘',
  '¦' => 'Âª',
  '§' => 'Âº',
  '¨' => 'Â¿',
  '©' => 'âŒ�',
  'ª' => 'Â¬',
  '«' => 'Â½',
  '¬' => 'Â¼',
  '­' => 'Â¡',
  '®' => 'Â«',
  '¯' => 'Â»',
  '°' => 'â–‘',
  '±' => 'â–’',
  '²' => 'â–“',
  '³' => 'â”‚',
  '´' => 'â”¤',
  'µ' => 'â•¡',
  '¶' => 'â•¢',
  '·' => 'â•–',
  '¸' => 'â••',
  '¹' => 'â•£',
  'º' => 'â•‘',
  '»' => 'â•—',
  '¼' => 'â•�',
  '½' => 'â•œ',
  '¾' => 'â•›',
  '¿' => 'â”�',
  'À' => 'â””',
  'Á' => 'â”´',
  'Â' => 'â”¬',
  'Ã' => 'â”œ',
  'Ä' => 'â”€',
  'Å' => 'â”¼',
  'Æ' => 'â•ž',
  'Ç' => 'â•Ÿ',
  'È' => 'â•š',
  'É' => 'â•”',
  'Ê' => 'â•©',
  'Ë' => 'â•¦',
  'Ì' => 'â• ',
  'Í' => 'â•�',
  'Î' => 'â•¬',
  'Ï' => 'â•§',
  'Ð' => 'â•¨',
  'Ñ' => 'â•¤',
  'Ò' => 'â•¥',
  'Ó' => 'â•™',
  'Ô' => 'â•˜',
  'Õ' => 'â•’',
  'Ö' => 'â•“',
  '×' => 'â•«',
  'Ø' => 'â•ª',
  'Ù' => 'â”˜',
  'Ú' => 'â”Œ',
  'Û' => 'â–ˆ',
  'Ü' => 'â–„',
  'Ý' => 'â–Œ',
  'Þ' => 'â–�',
  'ß' => 'â–€',
  'à' => 'Î±',
  'á' => 'ÃŸ',
  'â' => 'Î“',
  'ã' => 'Ï€',
  'ä' => 'Î£',
  'å' => 'Ïƒ',
  'æ' => 'Âµ',
  'ç' => 'Ï„',
  'è' => 'Î¦',
  'é' => 'Î˜',
  'ê' => 'Î©',
  'ë' => 'Î´',
  'ì' => 'âˆž',
  'í' => 'Ï†',
  'î' => 'Îµ',
  'ï' => 'âˆ©',
  'ð' => 'â‰¡',
  'ñ' => 'Â±',
  'ò' => 'â‰¥',
  'ó' => 'â‰¤',
  'ô' => 'âŒ ',
  'õ' => 'âŒ¡',
  'ö' => 'Ã·',
  '÷' => 'â‰ˆ',
  'ø' => 'Â°',
  'ù' => 'âˆ™',
  'ú' => 'Â·',
  'û' => 'âˆš',
  'ü' => 'â�¿',
  'ý' => 'Â²',
  'þ' => 'â– ',
  'ÿ' => 'Â ',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => '%',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
  '€' => 'Ã‡',
  '�' => 'Ã¼',
  '‚' => 'Ã©',
  'ƒ' => 'Ã¢',
  '„' => 'Ã¤',
  '…' => 'Ã ',
  '†' => 'Ã¥',
  '‡' => 'Ã§',
  'ˆ' => 'Ãª',
  '‰' => 'Ã«',
  'Š' => 'Ã¨',
  '‹' => 'Ã¯',
  'Œ' => 'Ã®',
  '�' => 'Ã¬',
  'Ž' => 'Ã„',
  '�' => 'Ã…',
  '�' => 'Ã‰',
  '‘' => 'Ã¦',
  '’' => 'Ã†',
  '“' => 'Ã´',
  '”' => 'Ã¶',
  '•' => 'Ã²',
  '–' => 'Ã»',
  '—' => 'Ã¹',
  '˜' => 'Ã¿',
  '™' => 'Ã–',
  'š' => 'Ãœ',
  '›' => 'Ã¸',
  'œ' => 'Â£',
  '�' => 'Ã˜',
  'ž' => 'Ã—',
  'Ÿ' => 'Æ’',
  ' ' => 'Ã¡',
  '¡' => 'Ã­',
  '¢' => 'Ã³',
  '£' => 'Ãº',
  '¤' => 'Ã±',
  '¥' => 'Ã‘',
  '¦' => 'Âª',
  '§' => 'Âº',
  '¨' => 'Â¿',
  '©' => 'Â®',
  'ª' => 'Â¬',
  '«' => 'Â½',
  '¬' => 'Â¼',
  '­' => 'Â¡',
  '®' => 'Â«',
  '¯' => 'Â»',
  '°' => 'â–‘',
  '±' => 'â–’',
  '²' => 'â–“',
  '³' => 'â”‚',
  '´' => 'â”¤',
  'µ' => 'Ã�',
  '¶' => 'Ã‚',
  '·' => 'Ã€',
  '¸' => 'Â©',
  '¹' => 'â•£',
  'º' => 'â•‘',
  '»' => 'â•—',
  '¼' => 'â•�',
  '½' => 'Â¢',
  '¾' => 'Â¥',
  '¿' => 'â”�',
  'À' => 'â””',
  'Á' => 'â”´',
  'Â' => 'â”¬',
  'Ã' => 'â”œ',
  'Ä' => 'â”€',
  'Å' => 'â”¼',
  'Æ' => 'Ã£',
  'Ç' => 'Ãƒ',
  'È' => 'â•š',
  'É' => 'â•”',
  'Ê' => 'â•©',
  'Ë' => 'â•¦',
  'Ì' => 'â• ',
  'Í' => 'â•�',
  'Î' => 'â•¬',
  'Ï' => 'Â¤',
  'Ð' => 'Ã°',
  'Ñ' => 'Ã�',
  'Ò' => 'ÃŠ',
  'Ó' => 'Ã‹',
  'Ô' => 'Ãˆ',
  'Õ' => 'Ä±',
  'Ö' => 'Ã�',
  '×' => 'ÃŽ',
  'Ø' => 'Ã�',
  'Ù' => 'â”˜',
  'Ú' => 'â”Œ',
  'Û' => 'â–ˆ',
  'Ü' => 'â–„',
  'Ý' => 'Â¦',
  'Þ' => 'ÃŒ',
  'ß' => 'â–€',
  'à' => 'Ã“',
  'á' => 'ÃŸ',
  'â' => 'Ã”',
  'ã' => 'Ã’',
  'ä' => 'Ãµ',
  'å' => 'Ã•',
  'æ' => 'Âµ',
  'ç' => 'Ã¾',
  'è' => 'Ãž',
  'é' => 'Ãš',
  'ê' => 'Ã›',
  'ë' => 'Ã™',
  'ì' => 'Ã½',
  'í' => 'Ã�',
  'î' => 'Â¯',
  'ï' => 'Â´',
  'ð' => 'Â­',
  'ñ' => 'Â±',
  'ò' => 'â€—',
  'ó' => 'Â¾',
  'ô' => 'Â¶',
  'õ' => 'Â§',
  'ö' => 'Ã·',
  '÷' => 'Â¸',
  'ø' => 'Â°',
  'ù' => 'Â¨',
  'ú' => 'Â·',
  'û' => 'Â¹',
  'ü' => 'Â³',
  'ý' => 'Â²',
  'þ' => 'â– ',
  'ÿ' => 'Â ',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => '%',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
  '€' => 'Â€',
  '�' => 'Â�',
  '‚' => 'Â‚',
  'ƒ' => 'Âƒ',
  '„' => 'Â„',
  '…' => 'Â…',
  '†' => 'Â†',
  '‡' => 'Â‡',
  'ˆ' => 'Âˆ',
  '‰' => 'Â‰',
  'Š' => 'ÂŠ',
  '‹' => 'Â‹',
  'Œ' => 'ÂŒ',
  '�' => 'Â�',
  'Ž' => 'ÂŽ',
  '�' => 'Â�',
  '�' => 'Â�',
  '‘' => 'Â‘',
  '’' => 'Â’',
  '“' => 'Â“',
  '”' => 'Â”',
  '•' => 'Â•',
  '–' => 'Â–',
  '—' => 'Â—',
  '˜' => 'Â˜',
  '™' => 'Â™',
  'š' => 'Âš',
  '›' => 'Â›',
  'œ' => 'Âœ',
  '�' => 'Â�',
  'ž' => 'Âž',
  'Ÿ' => 'ÂŸ',
  ' ' => 'Â ',
  '¡' => 'Â¡',
  '¢' => 'Â¢',
  '£' => 'Â£',
  '¤' => 'Â¤',
  '¥' => 'Â¥',
  '¦' => 'Â¦',
  '§' => 'Â§',
  '¨' => 'Â¨',
  '©' => 'Â©',
  'ª' => 'Âª',
  '«' => 'Â«',
  '¬' => 'Â¬',
  '­' => 'Â­',
  '®' => 'Â®',
  '¯' => 'Â¯',
  '°' => 'Â°',
  '±' => 'Â±',
  '²' => 'Â²',
  '³' => 'Â³',
  '´' => 'Â´',
  'µ' => 'Âµ',
  '¶' => 'Â¶',
  '·' => 'Â·',
  '¸' => 'Â¸',
  '¹' => 'Â¹',
  'º' => 'Âº',
  '»' => 'Â»',
  '¼' => 'Â¼',
  '½' => 'Â½',
  '¾' => 'Â¾',
  '¿' => 'Â¿',
  'À' => 'Ã€',
  'Á' => 'Ã�',
  'Â' => 'Ã‚',
  'Ã' => 'Ãƒ',
  'Ä' => 'Ã„',
  'Å' => 'Ã…',
  'Æ' => 'Ã†',
  'Ç' => 'Ã‡',
  'È' => 'Ãˆ',
  'É' => 'Ã‰',
  'Ê' => 'ÃŠ',
  'Ë' => 'Ã‹',
  'Ì' => 'ÃŒ',
  'Í' => 'Ã�',
  'Î' => 'ÃŽ',
  'Ï' => 'Ã�',
  'Ð' => 'Äž',
  'Ñ' => 'Ã‘',
  'Ò' => 'Ã’',
  'Ó' => 'Ã“',
  'Ô' => 'Ã”',
  'Õ' => 'Ã•',
  'Ö' => 'Ã–',
  '×' => 'Ã—',
  'Ø' => 'Ã˜',
  'Ù' => 'Ã™',
  'Ú' => 'Ãš',
  'Û' => 'Ã›',
  'Ü' => 'Ãœ',
  'Ý' => 'Ä°',
  'Þ' => 'Åž',
  'ß' => 'ÃŸ',
  'à' => 'Ã ',
  'á' => 'Ã¡',
  'â' => 'Ã¢',
  'ã' => 'Ã£',
  'ä' => 'Ã¤',
  'å' => 'Ã¥',
  'æ' => 'Ã¦',
  'ç' => 'Ã§',
  'è' => 'Ã¨',
  'é' => 'Ã©',
  'ê' => 'Ãª',
  'ë' => 'Ã«',
  'ì' => 'Ã¬',
  'í' => 'Ã­',
  'î' => 'Ã®',
  'ï' => 'Ã¯',
  'ð' => 'ÄŸ',
  'ñ' => 'Ã±',
  'ò' => 'Ã²',
  'ó' => 'Ã³',
  'ô' => 'Ã´',
  'õ' => 'Ãµ',
  'ö' => 'Ã¶',
  '÷' => 'Ã·',
  'ø' => 'Ã¸',
  'ù' => 'Ã¹',
  'ú' => 'Ãº',
  'û' => 'Ã»',
  'ü' => 'Ã¼',
  'ý' => 'Ä±',
  'þ' => 'ÅŸ',
  'ÿ' => 'Ã¿',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => '%',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
  '€' => 'â‚¬',
  '‚' => 'â€š',
  'ƒ' => 'Æ’',
  '„' => 'â€ž',
  '…' => 'â€¦',
  '†' => 'â€ ',
  '‡' => 'â€¡',
  '‰' => 'â€°',
  '‹' => 'â€¹',
  '‘' => 'â€˜',
  '’' => 'â€™',
  '“' => 'â€œ',
  '”' => 'â€�',
  '•' => 'â€¢',
  '–' => 'â€“',
  '—' => 'â€”',
  '™' => 'â„¢',
  '›' => 'â€º',
  ' ' => 'Â ',
  '¡' => 'Î…',
  '¢' => 'Î†',
  '£' => 'Â£',
  '¤' => 'Â¤',
  '¥' => 'Â¥',
  '¦' => 'Â¦',
  '§' => 'Â§',
  '¨' => 'Â¨',
  '©' => 'Â©',
  '«' => 'Â«',
  '¬' => 'Â¬',
  '­' => 'Â­',
  '®' => 'Â®',
  '¯' => 'â€•',
  '°' => 'Â°',
  '±' => 'Â±',
  '²' => 'Â²',
  '³' => 'Â³',
  '´' => 'Î„',
  'µ' => 'Âµ',
  '¶' => 'Â¶',
  '·' => 'Â·',
  '¸' => 'Îˆ',
  '¹' => 'Î‰',
  'º' => 'ÎŠ',
  '»' => 'Â»',
  '¼' => 'ÎŒ',
  '½' => 'Â½',
  '¾' => 'ÎŽ',
  '¿' => 'Î�',
  'À' => 'Î�',
  'Á' => 'Î‘',
  'Â' => 'Î’',
  'Ã' => 'Î“',
  'Ä' => 'Î”',
  'Å' => 'Î•',
  'Æ' => 'Î–',
  'Ç' => 'Î—',
  'È' => 'Î˜',
  'É' => 'Î™',
  'Ê' => 'Îš',
  'Ë' => 'Î›',
  'Ì' => 'Îœ',
  'Í' => 'Î�',
  'Î' => 'Îž',
  'Ï' => 'ÎŸ',
  'Ð' => 'Î ',
  'Ñ' => 'Î¡',
  'Ó' => 'Î£',
  'Ô' => 'Î¤',
  'Õ' => 'Î¥',
  'Ö' => 'Î¦',
  '×' => 'Î§',
  'Ø' => 'Î¨',
  'Ù' => 'Î©',
  'Ú' => 'Îª',
  'Û' => 'Î«',
  'Ü' => 'Î¬',
  'Ý' => 'Î­',
  'Þ' => 'Î®',
  'ß' => 'Î¯',
  'à' => 'Î°',
  'á' => 'Î±',
  'â' => 'Î²',
  'ã' => 'Î³',
  'ä' => 'Î´',
  'å' => 'Îµ',
  'æ' => 'Î¶',
  'ç' => 'Î·',
  'è' => 'Î¸',
  'é' => 'Î¹',
  'ê' => 'Îº',
  'ë' => 'Î»',
  'ì' => 'Î¼',
  'í' => 'Î½',
  'î' => 'Î¾',
  'ï' => 'Î¿',
  'ð' => 'Ï€',
  'ñ' => 'Ï�',
  'ò' => 'Ï‚',
  'ó' => 'Ïƒ',
  'ô' => 'Ï„',
  'õ' => 'Ï…',
  'ö' => 'Ï†',
  '÷' => 'Ï‡',
  'ø' => 'Ïˆ',
  'ù' => 'Ï‰',
  'ú' => 'ÏŠ',
  'û' => 'Ï‹',
  'ü' => 'ÏŒ',
  'ý' => 'Ï�',
  'þ' => 'ÏŽ',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => '%',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
  '€' => 'Â€',
  '�' => 'Â�',
  '‚' => 'Â‚',
  'ƒ' => 'Âƒ',
  '„' => 'Â„',
  '…' => 'Â…',
  '†' => 'Â†',
  '‡' => 'Â‡',
  'ˆ' => 'Âˆ',
  '‰' => 'Â‰',
  'Š' => 'ÂŠ',
  '‹' => 'Â‹',
  'Œ' => 'ÂŒ',
  '�' => 'Â�',
  'Ž' => 'ÂŽ',
  '�' => 'Â�',
  '�' => 'Â�',
  '‘' => 'Â‘',
  '’' => 'Â’',
  '“' => 'Â“',
  '”' => 'Â”',
  '•' => 'Â•',
  '–' => 'Â–',
  '—' => 'Â—',
  '˜' => 'Â˜',
  '™' => 'Â™',
  'š' => 'Âš',
  '›' => 'Â›',
  'œ' => 'Âœ',
  '�' => 'Â�',
  'ž' => 'Âž',
  'Ÿ' => 'ÂŸ',
  ' ' => 'Â ',
  '¡' => 'Ä„',
  '¢' => 'Ë˜',
  '£' => 'Å�',
  '¤' => 'Â¤',
  '¥' => 'Ä½',
  '¦' => 'Åš',
  '§' => 'Â§',
  '¨' => 'Â¨',
  '©' => 'Å ',
  'ª' => 'Åž',
  '«' => 'Å¤',
  '¬' => 'Å¹',
  '­' => 'Â­',
  '®' => 'Å½',
  '¯' => 'Å»',
  '°' => 'Â°',
  '±' => 'Ä…',
  '²' => 'Ë›',
  '³' => 'Å‚',
  '´' => 'Â´',
  'µ' => 'Ä¾',
  '¶' => 'Å›',
  '·' => 'Ë‡',
  '¸' => 'Â¸',
  '¹' => 'Å¡',
  'º' => 'ÅŸ',
  '»' => 'Å¥',
  '¼' => 'Åº',
  '½' => 'Ë�',
  '¾' => 'Å¾',
  '¿' => 'Å¼',
  'À' => 'Å”',
  'Á' => 'Ã�',
  'Â' => 'Ã‚',
  'Ã' => 'Ä‚',
  'Ä' => 'Ã„',
  'Å' => 'Ä¹',
  'Æ' => 'Ä†',
  'Ç' => 'Ã‡',
  'È' => 'ÄŒ',
  'É' => 'Ã‰',
  'Ê' => 'Ä˜',
  'Ë' => 'Ã‹',
  'Ì' => 'Äš',
  'Í' => 'Ã�',
  'Î' => 'ÃŽ',
  'Ï' => 'ÄŽ',
  'Ð' => 'Ä�',
  'Ñ' => 'Åƒ',
  'Ò' => 'Å‡',
  'Ó' => 'Ã“',
  'Ô' => 'Ã”',
  'Õ' => 'Å�',
  'Ö' => 'Ã–',
  '×' => 'Ã—',
  'Ø' => 'Å˜',
  'Ù' => 'Å®',
  'Ú' => 'Ãš',
  'Û' => 'Å°',
  'Ü' => 'Ãœ',
  'Ý' => 'Ã�',
  'Þ' => 'Å¢',
  'ß' => 'ÃŸ',
  'à' => 'Å•',
  'á' => 'Ã¡',
  'â' => 'Ã¢',
  'ã' => 'Äƒ',
  'ä' => 'Ã¤',
  'å' => 'Äº',
  'æ' => 'Ä‡',
  'ç' => 'Ã§',
  'è' => 'Ä�',
  'é' => 'Ã©',
  'ê' => 'Ä™',
  'ë' => 'Ã«',
  'ì' => 'Ä›',
  'í' => 'Ã­',
  'î' => 'Ã®',
  'ï' => 'Ä�',
  'ð' => 'Ä‘',
  'ñ' => 'Å„',
  'ò' => 'Åˆ',
  'ó' => 'Ã³',
  'ô' => 'Ã´',
  'õ' => 'Å‘',
  'ö' => 'Ã¶',
  '÷' => 'Ã·',
  'ø' => 'Å™',
  'ù' => 'Å¯',
  'ú' => 'Ãº',
  'û' => 'Å±',
  'ü' => 'Ã¼',
  'ý' => 'Ã½',
  'þ' => 'Å£',
  'ÿ' => 'Ë™',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => '%',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
  '€' => 'Ã‡',
  '�' => 'Ã¼',
  '‚' => 'Ã©',
  'ƒ' => 'Ã¢',
  '„' => 'Ã¤',
  '…' => 'Å¯',
  '†' => 'Ä‡',
  '‡' => 'Ã§',
  'ˆ' => 'Å‚',
  '‰' => 'Ã«',
  'Š' => 'Å�',
  '‹' => 'Å‘',
  'Œ' => 'Ã®',
  '�' => 'Å¹',
  'Ž' => 'Ã„',
  '�' => 'Ä†',
  '�' => 'Ã‰',
  '‘' => 'Ä¹',
  '’' => 'Äº',
  '“' => 'Ã´',
  '”' => 'Ã¶',
  '•' => 'Ä½',
  '–' => 'Ä¾',
  '—' => 'Åš',
  '˜' => 'Å›',
  '™' => 'Ã–',
  'š' => 'Ãœ',
  '›' => 'Å¤',
  'œ' => 'Å¥',
  '�' => 'Å�',
  'ž' => 'Ã—',
  'Ÿ' => 'Ä�',
  ' ' => 'Ã¡',
  '¡' => 'Ã­',
  '¢' => 'Ã³',
  '£' => 'Ãº',
  '¤' => 'Ä„',
  '¥' => 'Ä…',
  '¦' => 'Å½',
  '§' => 'Å¾',
  '¨' => 'Ä˜',
  '©' => 'Ä™',
  'ª' => 'Â¬',
  '«' => 'Åº',
  '¬' => 'ÄŒ',
  '­' => 'ÅŸ',
  '®' => 'Â«',
  '¯' => 'Â»',
  '°' => 'â–‘',
  '±' => 'â–’',
  '²' => 'â–“',
  '³' => 'â”‚',
  '´' => 'â”¤',
  'µ' => 'Ã�',
  '¶' => 'Ã‚',
  '·' => 'Äš',
  '¸' => 'Åž',
  '¹' => 'â•£',
  'º' => 'â•‘',
  '»' => 'â•—',
  '¼' => 'â•�',
  '½' => 'Å»',
  '¾' => 'Å¼',
  '¿' => 'â”�',
  'À' => 'â””',
  'Á' => 'â”´',
  'Â' => 'â”¬',
  'Ã' => 'â”œ',
  'Ä' => 'â”€',
  'Å' => 'â”¼',
  'Æ' => 'Ä‚',
  'Ç' => 'Äƒ',
  'È' => 'â•š',
  'É' => 'â•”',
  'Ê' => 'â•©',
  'Ë' => 'â•¦',
  'Ì' => 'â• ',
  'Í' => 'â•�',
  'Î' => 'â•¬',
  'Ï' => 'Â¤',
  'Ð' => 'Ä‘',
  'Ñ' => 'Ä�',
  'Ò' => 'ÄŽ',
  'Ó' => 'Ã‹',
  'Ô' => 'Ä�',
  'Õ' => 'Å‡',
  'Ö' => 'Ã�',
  '×' => 'ÃŽ',
  'Ø' => 'Ä›',
  'Ù' => 'â”˜',
  'Ú' => 'â”Œ',
  'Û' => 'â–ˆ',
  'Ü' => 'â–„',
  'Ý' => 'Å¢',
  'Þ' => 'Å®',
  'ß' => 'â–€',
  'à' => 'Ã“',
  'á' => 'ÃŸ',
  'â' => 'Ã”',
  'ã' => 'Åƒ',
  'ä' => 'Å„',
  'å' => 'Åˆ',
  'æ' => 'Å ',
  'ç' => 'Å¡',
  'è' => 'Å”',
  'é' => 'Ãš',
  'ê' => 'Å•',
  'ë' => 'Å°',
  'ì' => 'Ã½',
  'í' => 'Ã�',
  'î' => 'Å£',
  'ï' => 'Â´',
  'ð' => 'Â­',
  'ñ' => 'Ë�',
  'ò' => 'Ë›',
  'ó' => 'Ë‡',
  'ô' => 'Ë˜',
  'õ' => 'Â§',
  'ö' => 'Ã·',
  '÷' => 'Â¸',
  'ø' => 'Â°',
  'ù' => 'Â¨',
  'ú' => 'Ë™',
  'û' => 'Å±',
  'ü' => 'Å˜',
  'ý' => 'Å™',
  'þ' => 'â– ',
  'ÿ' => 'Â ',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => '%',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
  '€' => 'â”€',
  '�' => 'â”‚',
  '‚' => 'â”Œ',
  'ƒ' => 'â”�',
  '„' => 'â””',
  '…' => 'â”˜',
  '†' => 'â”œ',
  '‡' => 'â”¤',
  'ˆ' => 'â”¬',
  '‰' => 'â”´',
  'Š' => 'â”¼',
  '‹' => 'â–€',
  'Œ' => 'â–„',
  '�' => 'â–ˆ',
  'Ž' => 'â–Œ',
  '�' => 'â–�',
  '�' => 'â–‘',
  '‘' => 'â–’',
  '’' => 'â–“',
  '“' => 'âŒ ',
  '”' => 'â– ',
  '•' => 'âˆ™',
  '–' => 'âˆš',
  '—' => 'â‰ˆ',
  '˜' => 'â‰¤',
  '™' => 'â‰¥',
  'š' => 'Â ',
  '›' => 'âŒ¡',
  'œ' => 'Â°',
  '�' => 'Â²',
  'ž' => 'Â·',
  'Ÿ' => 'Ã·',
  ' ' => 'â•�',
  '¡' => 'â•‘',
  '¢' => 'â•’',
  '£' => 'Ñ‘',
  '¤' => 'â•“',
  '¥' => 'â•”',
  '¦' => 'â••',
  '§' => 'â•–',
  '¨' => 'â•—',
  '©' => 'â•˜',
  'ª' => 'â•™',
  '«' => 'â•š',
  '¬' => 'â•›',
  '­' => 'â•œ',
  '®' => 'â•�',
  '¯' => 'â•ž',
  '°' => 'â•Ÿ',
  '±' => 'â• ',
  '²' => 'â•¡',
  '³' => 'Ð�',
  '´' => 'â•¢',
  'µ' => 'â•£',
  '¶' => 'â•¤',
  '·' => 'â•¥',
  '¸' => 'â•¦',
  '¹' => 'â•§',
  'º' => 'â•¨',
  '»' => 'â•©',
  '¼' => 'â•ª',
  '½' => 'â•«',
  '¾' => 'â•¬',
  '¿' => 'Â©',
  'À' => 'ÑŽ',
  'Á' => 'Ð°',
  'Â' => 'Ð±',
  'Ã' => 'Ñ†',
  'Ä' => 'Ð´',
  'Å' => 'Ðµ',
  'Æ' => 'Ñ„',
  'Ç' => 'Ð³',
  'È' => 'Ñ…',
  'É' => 'Ð¸',
  'Ê' => 'Ð¹',
  'Ë' => 'Ðº',
  'Ì' => 'Ð»',
  'Í' => 'Ð¼',
  'Î' => 'Ð½',
  'Ï' => 'Ð¾',
  'Ð' => 'Ð¿',
  'Ñ' => 'Ñ�',
  'Ò' => 'Ñ€',
  'Ó' => 'Ñ�',
  'Ô' => 'Ñ‚',
  'Õ' => 'Ñƒ',
  'Ö' => 'Ð¶',
  '×' => 'Ð²',
  'Ø' => 'ÑŒ',
  'Ù' => 'Ñ‹',
  'Ú' => 'Ð·',
  'Û' => 'Ñˆ',
  'Ü' => 'Ñ�',
  'Ý' => 'Ñ‰',
  'Þ' => 'Ñ‡',
  'ß' => 'ÑŠ',
  'à' => 'Ð®',
  'á' => 'Ð�',
  'â' => 'Ð‘',
  'ã' => 'Ð¦',
  'ä' => 'Ð”',
  'å' => 'Ð•',
  'æ' => 'Ð¤',
  'ç' => 'Ð“',
  'è' => 'Ð¥',
  'é' => 'Ð˜',
  'ê' => 'Ð™',
  'ë' => 'Ðš',
  'ì' => 'Ð›',
  'í' => 'Ðœ',
  'î' => 'Ð�',
  'ï' => 'Ðž',
  'ð' => 'ÐŸ',
  'ñ' => 'Ð¯',
  'ò' => 'Ð ',
  'ó' => 'Ð¡',
  'ô' => 'Ð¢',
  'õ' => 'Ð£',
  'ö' => 'Ð–',
  '÷' => 'Ð’',
  'ø' => 'Ð¬',
  'ù' => 'Ð«',
  'ú' => 'Ð—',
  'û' => 'Ð¨',
  'ü' => 'Ð­',
  'ý' => 'Ð©',
  'þ' => 'Ð§',
  'ÿ' => 'Ðª',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => '%',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
  '€' => 'Â€',
  '�' => 'Â�',
  '‚' => 'Â‚',
  'ƒ' => 'Âƒ',
  '„' => 'Â„',
  '…' => 'Â…',
  '†' => 'Â†',
  '‡' => 'Â‡',
  'ˆ' => 'Âˆ',
  '‰' => 'Â‰',
  'Š' => 'ÂŠ',
  '‹' => 'Â‹',
  'Œ' => 'ÂŒ',
  '�' => 'Â�',
  'Ž' => 'ÂŽ',
  '�' => 'Â�',
  '�' => 'Â�',
  '‘' => 'Â‘',
  '’' => 'Â’',
  '“' => 'Â“',
  '”' => 'Â”',
  '•' => 'Â•',
  '–' => 'Â–',
  '—' => 'Â—',
  '˜' => 'Â˜',
  '™' => 'Â™',
  'š' => 'Âš',
  '›' => 'Â›',
  'œ' => 'Âœ',
  '�' => 'Â�',
  'ž' => 'Âž',
  'Ÿ' => 'ÂŸ',
  ' ' => 'Â ',
  '¡' => 'â€˜',
  '¢' => 'â€™',
  '£' => 'Â£',
  '¦' => 'Â¦',
  '§' => 'Â§',
  '¨' => 'Â¨',
  '©' => 'Â©',
  '«' => 'Â«',
  '¬' => 'Â¬',
  '­' => 'Â­',
  '¯' => 'â€•',
  '°' => 'Â°',
  '±' => 'Â±',
  '²' => 'Â²',
  '³' => 'Â³',
  '´' => 'Î„',
  'µ' => 'Î…',
  '¶' => 'Î†',
  '·' => 'Â·',
  '¸' => 'Îˆ',
  '¹' => 'Î‰',
  'º' => 'ÎŠ',
  '»' => 'Â»',
  '¼' => 'ÎŒ',
  '½' => 'Â½',
  '¾' => 'ÎŽ',
  '¿' => 'Î�',
  'À' => 'Î�',
  'Á' => 'Î‘',
  'Â' => 'Î’',
  'Ã' => 'Î“',
  'Ä' => 'Î”',
  'Å' => 'Î•',
  'Æ' => 'Î–',
  'Ç' => 'Î—',
  'È' => 'Î˜',
  'É' => 'Î™',
  'Ê' => 'Îš',
  'Ë' => 'Î›',
  'Ì' => 'Îœ',
  'Í' => 'Î�',
  'Î' => 'Îž',
  'Ï' => 'ÎŸ',
  'Ð' => 'Î ',
  'Ñ' => 'Î¡',
  'Ó' => 'Î£',
  'Ô' => 'Î¤',
  'Õ' => 'Î¥',
  'Ö' => 'Î¦',
  '×' => 'Î§',
  'Ø' => 'Î¨',
  'Ù' => 'Î©',
  'Ú' => 'Îª',
  'Û' => 'Î«',
  'Ü' => 'Î¬',
  'Ý' => 'Î­',
  'Þ' => 'Î®',
  'ß' => 'Î¯',
  'à' => 'Î°',
  'á' => 'Î±',
  'â' => 'Î²',
  'ã' => 'Î³',
  'ä' => 'Î´',
  'å' => 'Îµ',
  'æ' => 'Î¶',
  'ç' => 'Î·',
  'è' => 'Î¸',
  'é' => 'Î¹',
  'ê' => 'Îº',
  'ë' => 'Î»',
  'ì' => 'Î¼',
  'í' => 'Î½',
  'î' => 'Î¾',
  'ï' => 'Î¿',
  'ð' => 'Ï€',
  'ñ' => 'Ï�',
  'ò' => 'Ï‚',
  'ó' => 'Ïƒ',
  'ô' => 'Ï„',
  'õ' => 'Ï…',
  'ö' => 'Ï†',
  '÷' => 'Ï‡',
  'ø' => 'Ïˆ',
  'ù' => 'Ï‰',
  'ú' => 'ÏŠ',
  'û' => 'Ï‹',
  'ü' => 'ÏŒ',
  'ý' => 'Ï�',
  'þ' => 'ÏŽ',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => 'Âœ',
  '' => '	',
  '' => 'Â†',
  '' => '',
  '' => 'Â—',
  '	' => 'Â�',
  '
' => 'ÂŽ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => 'Â�',
  '' => 'Â…',
  '' => '',
  '' => 'Â‡',
  '' => '',
  '' => '',
  '' => 'Â’',
  '' => 'Â�',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => 'Â€',
  '!' => 'Â�',
  '"' => 'Â‚',
  '#' => 'Âƒ',
  '$' => 'Â„',
  '%' => '
',
  '&' => '',
  '\'' => '',
  '(' => 'Âˆ',
  ')' => 'Â‰',
  '*' => 'ÂŠ',
  '+' => 'Â‹',
  ',' => 'ÂŒ',
  '-' => '',
  '.' => '',
  '/' => '',
  0 => 'Â�',
  1 => 'Â‘',
  2 => '',
  3 => 'Â“',
  4 => 'Â”',
  5 => 'Â•',
  6 => 'Â–',
  7 => '',
  8 => 'Â˜',
  9 => 'Â™',
  ':' => 'Âš',
  ';' => 'Â›',
  '<' => '',
  '=' => '',
  '>' => 'Âž',
  '?' => '',
  '@' => ' ',
  'A' => 'Î‘',
  'B' => 'Î’',
  'C' => 'Î“',
  'D' => 'Î”',
  'E' => 'Î•',
  'F' => 'Î–',
  'G' => 'Î—',
  'H' => 'Î˜',
  'I' => 'Î™',
  'J' => '[',
  'K' => '.',
  'L' => '<',
  'M' => '(',
  'N' => '+',
  'O' => '!',
  'P' => '&',
  'Q' => 'Îš',
  'R' => 'Î›',
  'S' => 'Îœ',
  'T' => 'Î�',
  'U' => 'Îž',
  'V' => 'ÎŸ',
  'W' => 'Î ',
  'X' => 'Î¡',
  'Y' => 'Î£',
  'Z' => ']',
  '[' => '$',
  '\\' => '*',
  ']' => ')',
  '^' => ';',
  '_' => '^',
  '`' => '-',
  'a' => '/',
  'b' => 'Î¤',
  'c' => 'Î¥',
  'd' => 'Î¦',
  'e' => 'Î§',
  'f' => 'Î¨',
  'g' => 'Î©',
  'h' => 'Îª',
  'i' => 'Î«',
  'j' => '|',
  'k' => ',',
  'l' => '%',
  'm' => '_',
  'n' => '>',
  'o' => '?',
  'p' => 'Â¨',
  'q' => 'Î†',
  'r' => 'Îˆ',
  's' => 'Î‰',
  't' => 'Â ',
  'u' => 'ÎŠ',
  'v' => 'ÎŒ',
  'w' => 'ÎŽ',
  'x' => 'Î�',
  'y' => '`',
  'z' => ':',
  '{' => '#',
  '|' => '@',
  '}' => '\'',
  '~' => '=',
  '' => '"',
  '€' => 'Î…',
  '�' => 'a',
  '‚' => 'b',
  'ƒ' => 'c',
  '„' => 'd',
  '…' => 'e',
  '†' => 'f',
  '‡' => 'g',
  'ˆ' => 'h',
  '‰' => 'i',
  'Š' => 'Î±',
  '‹' => 'Î²',
  'Œ' => 'Î³',
  '�' => 'Î´',
  'Ž' => 'Îµ',
  '�' => 'Î¶',
  '�' => 'Â°',
  '‘' => 'j',
  '’' => 'k',
  '“' => 'l',
  '”' => 'm',
  '•' => 'n',
  '–' => 'o',
  '—' => 'p',
  '˜' => 'q',
  '™' => 'r',
  'š' => 'Î·',
  '›' => 'Î¸',
  'œ' => 'Î¹',
  '�' => 'Îº',
  'ž' => 'Î»',
  'Ÿ' => 'Î¼',
  ' ' => 'Â´',
  '¡' => '~',
  '¢' => 's',
  '£' => 't',
  '¤' => 'u',
  '¥' => 'v',
  '¦' => 'w',
  '§' => 'x',
  '¨' => 'y',
  '©' => 'z',
  'ª' => 'Î½',
  '«' => 'Î¾',
  '¬' => 'Î¿',
  '­' => 'Ï€',
  '®' => 'Ï�',
  '¯' => 'Ïƒ',
  '°' => 'Â£',
  '±' => 'Î¬',
  '²' => 'Î­',
  '³' => 'Î®',
  '´' => 'ÏŠ',
  'µ' => 'Î¯',
  '¶' => 'ÏŒ',
  '·' => 'Ï�',
  '¸' => 'Ï‹',
  '¹' => 'ÏŽ',
  'º' => 'Ï‚',
  '»' => 'Ï„',
  '¼' => 'Ï…',
  '½' => 'Ï†',
  '¾' => 'Ï‡',
  '¿' => 'Ïˆ',
  'À' => '{',
  'Á' => 'A',
  'Â' => 'B',
  'Ã' => 'C',
  'Ä' => 'D',
  'Å' => 'E',
  'Æ' => 'F',
  'Ç' => 'G',
  'È' => 'H',
  'É' => 'I',
  'Ê' => 'Â­',
  'Ë' => 'Ï‰',
  'Ì' => 'Î�',
  'Í' => 'Î°',
  'Î' => 'â€˜',
  'Ï' => 'â€•',
  'Ð' => '}',
  'Ñ' => 'J',
  'Ò' => 'K',
  'Ó' => 'L',
  'Ô' => 'M',
  'Õ' => 'N',
  'Ö' => 'O',
  '×' => 'P',
  'Ø' => 'Q',
  'Ù' => 'R',
  'Ú' => 'Â±',
  'Û' => 'Â½',
  'Ü' => '',
  'Ý' => 'Î‡',
  'Þ' => 'â€™',
  'ß' => 'Â¦',
  'à' => '\\',
  'á' => '',
  'â' => 'S',
  'ã' => 'T',
  'ä' => 'U',
  'å' => 'V',
  'æ' => 'W',
  'ç' => 'X',
  'è' => 'Y',
  'é' => 'Z',
  'ê' => 'Â²',
  'ë' => 'Â§',
  'ì' => '',
  'í' => '',
  'î' => 'Â«',
  'ï' => 'Â¬',
  'ð' => '0',
  'ñ' => '1',
  'ò' => '2',
  'ó' => '3',
  'ô' => '4',
  'õ' => '5',
  'ö' => '6',
  '÷' => '7',
  'ø' => '8',
  'ù' => '9',
  'ú' => 'Â³',
  'û' => 'Â©',
  'ü' => '',
  'ý' => '',
  'þ' => 'Â»',
  'ÿ' => 'ÂŸ',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => '%',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
  '€' => 'â‚¬',
  '‚' => 'â€š',
  '„' => 'â€ž',
  '…' => 'â€¦',
  '†' => 'â€ ',
  '‡' => 'â€¡',
  '‰' => 'â€°',
  'Š' => 'Å ',
  '‹' => 'â€¹',
  'Œ' => 'Åš',
  '�' => 'Å¤',
  'Ž' => 'Å½',
  '�' => 'Å¹',
  '‘' => 'â€˜',
  '’' => 'â€™',
  '“' => 'â€œ',
  '”' => 'â€�',
  '•' => 'â€¢',
  '–' => 'â€“',
  '—' => 'â€”',
  '™' => 'â„¢',
  'š' => 'Å¡',
  '›' => 'â€º',
  'œ' => 'Å›',
  '�' => 'Å¥',
  'ž' => 'Å¾',
  'Ÿ' => 'Åº',
  ' ' => 'Â ',
  '¡' => 'Ë‡',
  '¢' => 'Ë˜',
  '£' => 'Å�',
  '¤' => 'Â¤',
  '¥' => 'Ä„',
  '¦' => 'Â¦',
  '§' => 'Â§',
  '¨' => 'Â¨',
  '©' => 'Â©',
  'ª' => 'Åž',
  '«' => 'Â«',
  '¬' => 'Â¬',
  '­' => 'Â­',
  '®' => 'Â®',
  '¯' => 'Å»',
  '°' => 'Â°',
  '±' => 'Â±',
  '²' => 'Ë›',
  '³' => 'Å‚',
  '´' => 'Â´',
  'µ' => 'Âµ',
  '¶' => 'Â¶',
  '·' => 'Â·',
  '¸' => 'Â¸',
  '¹' => 'Ä…',
  'º' => 'ÅŸ',
  '»' => 'Â»',
  '¼' => 'Ä½',
  '½' => 'Ë�',
  '¾' => 'Ä¾',
  '¿' => 'Å¼',
  'À' => 'Å”',
  'Á' => 'Ã�',
  'Â' => 'Ã‚',
  'Ã' => 'Ä‚',
  'Ä' => 'Ã„',
  'Å' => 'Ä¹',
  'Æ' => 'Ä†',
  'Ç' => 'Ã‡',
  'È' => 'ÄŒ',
  'É' => 'Ã‰',
  'Ê' => 'Ä˜',
  'Ë' => 'Ã‹',
  'Ì' => 'Äš',
  'Í' => 'Ã�',
  'Î' => 'ÃŽ',
  'Ï' => 'ÄŽ',
  'Ð' => 'Ä�',
  'Ñ' => 'Åƒ',
  'Ò' => 'Å‡',
  'Ó' => 'Ã“',
  'Ô' => 'Ã”',
  'Õ' => 'Å�',
  'Ö' => 'Ã–',
  '×' => 'Ã—',
  'Ø' => 'Å˜',
  'Ù' => 'Å®',
  'Ú' => 'Ãš',
  'Û' => 'Å°',
  'Ü' => 'Ãœ',
  'Ý' => 'Ã�',
  'Þ' => 'Å¢',
  'ß' => 'ÃŸ',
  'à' => 'Å•',
  'á' => 'Ã¡',
  'â' => 'Ã¢',
  'ã' => 'Äƒ',
  'ä' => 'Ã¤',
  'å' => 'Äº',
  'æ' => 'Ä‡',
  'ç' => 'Ã§',
  'è' => 'Ä�',
  'é' => 'Ã©',
  'ê' => 'Ä™',
  'ë' => 'Ã«',
  'ì' => 'Ä›',
  'í' => 'Ã­',
  'î' => 'Ã®',
  'ï' => 'Ä�',
  'ð' => 'Ä‘',
  'ñ' => 'Å„',
  'ò' => 'Åˆ',
  'ó' => 'Ã³',
  'ô' => 'Ã´',
  'õ' => 'Å‘',
  'ö' => 'Ã¶',
  '÷' => 'Ã·',
  'ø' => 'Å™',
  'ù' => 'Å¯',
  'ú' => 'Ãº',
  'û' => 'Å±',
  'ü' => 'Ã¼',
  'ý' => 'Ã½',
  'þ' => 'Å£',
  'ÿ' => 'Ë™',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => '%',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
  '¡' => 'ï½¡',
  '¢' => 'ï½¢',
  '£' => 'ï½£',
  '¤' => 'ï½¤',
  '¥' => 'ï½¥',
  '¦' => 'ï½¦',
  '§' => 'ï½§',
  '¨' => 'ï½¨',
  '©' => 'ï½©',
  'ª' => 'ï½ª',
  '«' => 'ï½«',
  '¬' => 'ï½¬',
  '­' => 'ï½­',
  '®' => 'ï½®',
  '¯' => 'ï½¯',
  '°' => 'ï½°',
  '±' => 'ï½±',
  '²' => 'ï½²',
  '³' => 'ï½³',
  '´' => 'ï½´',
  'µ' => 'ï½µ',
  '¶' => 'ï½¶',
  '·' => 'ï½·',
  '¸' => 'ï½¸',
  '¹' => 'ï½¹',
  'º' => 'ï½º',
  '»' => 'ï½»',
  '¼' => 'ï½¼',
  '½' => 'ï½½',
  '¾' => 'ï½¾',
  '¿' => 'ï½¿',
  'À' => 'ï¾€',
  'Á' => 'ï¾�',
  'Â' => 'ï¾‚',
  'Ã' => 'ï¾ƒ',
  'Ä' => 'ï¾„',
  'Å' => 'ï¾…',
  'Æ' => 'ï¾†',
  'Ç' => 'ï¾‡',
  'È' => 'ï¾ˆ',
  'É' => 'ï¾‰',
  'Ê' => 'ï¾Š',
  'Ë' => 'ï¾‹',
  'Ì' => 'ï¾Œ',
  'Í' => 'ï¾�',
  'Î' => 'ï¾Ž',
  'Ï' => 'ï¾�',
  'Ð' => 'ï¾�',
  'Ñ' => 'ï¾‘',
  'Ò' => 'ï¾’',
  'Ó' => 'ï¾“',
  'Ô' => 'ï¾”',
  'Õ' => 'ï¾•',
  'Ö' => 'ï¾–',
  '×' => 'ï¾—',
  'Ø' => 'ï¾˜',
  'Ù' => 'ï¾™',
  'Ú' => 'ï¾š',
  'Û' => 'ï¾›',
  'Ü' => 'ï¾œ',
  'Ý' => 'ï¾�',
  'Þ' => 'ï¾ž',
  'ß' => 'ï¾Ÿ',
  '�@' => 'ã€€',
  '�A' => 'ã€�',
  '�B' => 'ã€‚',
  '�C' => 'ï¼Œ',
  '�D' => 'ï¼Ž',
  '�E' => 'ãƒ»',
  '�F' => 'ï¼š',
  '�G' => 'ï¼›',
  '�H' => 'ï¼Ÿ',
  '�I' => 'ï¼�',
  '�J' => 'ã‚›',
  '�K' => 'ã‚œ',
  '�L' => 'Â´',
  '�M' => 'ï½€',
  '�N' => 'Â¨',
  '�O' => 'ï¼¾',
  '�P' => 'ï¿£',
  '�Q' => 'ï¼¿',
  '�R' => 'ãƒ½',
  '�S' => 'ãƒ¾',
  '�T' => 'ã‚�',
  '�U' => 'ã‚ž',
  '�V' => 'ã€ƒ',
  '�W' => 'ä»�',
  '�X' => 'ã€…',
  '�Y' => 'ã€†',
  '�Z' => 'ã€‡',
  '�[' => 'ãƒ¼',
  '�\\' => 'â€•',
  '�]' => 'â€�',
  '�^' => 'ï¼�',
  '�_' => 'ï¼¼',
  '�`' => 'ï½ž',
  '�a' => 'âˆ¥',
  '�b' => 'ï½œ',
  '�c' => 'â€¦',
  '�d' => 'â€¥',
  '�e' => 'â€˜',
  '�f' => 'â€™',
  '�g' => 'â€œ',
  '�h' => 'â€�',
  '�i' => 'ï¼ˆ',
  '�j' => 'ï¼‰',
  '�k' => 'ã€”',
  '�l' => 'ã€•',
  '�m' => 'ï¼»',
  '�n' => 'ï¼½',
  '�o' => 'ï½›',
  '�p' => 'ï½�',
  '�q' => 'ã€ˆ',
  '�r' => 'ã€‰',
  '�s' => 'ã€Š',
  '�t' => 'ã€‹',
  '�u' => 'ã€Œ',
  '�v' => 'ã€�',
  '�w' => 'ã€Ž',
  '�x' => 'ã€�',
  '�y' => 'ã€�',
  '�z' => 'ã€‘',
  '�{' => 'ï¼‹',
  '�|' => 'ï¼�',
  '�}' => 'Â±',
  '�~' => 'Ã—',
  '�€' => 'Ã·',
  '��' => 'ï¼�',
  '�‚' => 'â‰ ',
  '�ƒ' => 'ï¼œ',
  '�„' => 'ï¼ž',
  '�…' => 'â‰¦',
  '�†' => 'â‰§',
  '�‡' => 'âˆž',
  '�ˆ' => 'âˆ´',
  '�‰' => 'â™‚',
  '�Š' => 'â™€',
  '�‹' => 'Â°',
  '�Œ' => 'â€²',
  '��' => 'â€³',
  '�Ž' => 'â„ƒ',
  '��' => 'ï¿¥',
  '��' => 'ï¼„',
  '�‘' => 'ï¿ ',
  '�’' => 'ï¿¡',
  '�“' => 'ï¼…',
  '�”' => 'ï¼ƒ',
  '�•' => 'ï¼†',
  '�–' => 'ï¼Š',
  '�—' => 'ï¼ ',
  '�˜' => 'Â§',
  '�™' => 'â˜†',
  '�š' => 'â˜…',
  '�›' => 'â—‹',
  '�œ' => 'â—�',
  '��' => 'â—Ž',
  '�ž' => 'â—‡',
  '�Ÿ' => 'â—†',
  '� ' => 'â–¡',
  '�¡' => 'â– ',
  '�¢' => 'â–³',
  '�£' => 'â–²',
  '�¤' => 'â–½',
  '�¥' => 'â–¼',
  '�¦' => 'â€»',
  '�§' => 'ã€’',
  '�¨' => 'â†’',
  '�©' => 'â†�',
  '�ª' => 'â†‘',
  '�«' => 'â†“',
  '�¬' => 'ã€“',
  '�¸' => 'âˆˆ',
  '�¹' => 'âˆ‹',
  '�º' => 'âŠ†',
  '�»' => 'âŠ‡',
  '�¼' => 'âŠ‚',
  '�½' => 'âŠƒ',
  '�¾' => 'âˆª',
  '�¿' => 'âˆ©',
  '�È' => 'âˆ§',
  '�É' => 'âˆ¨',
  '�Ê' => 'ï¿¢',
  '�Ë' => 'â‡’',
  '�Ì' => 'â‡”',
  '�Í' => 'âˆ€',
  '�Î' => 'âˆƒ',
  '�Ú' => 'âˆ ',
  '�Û' => 'âŠ¥',
  '�Ü' => 'âŒ’',
  '�Ý' => 'âˆ‚',
  '�Þ' => 'âˆ‡',
  '�ß' => 'â‰¡',
  '�à' => 'â‰’',
  '�á' => 'â‰ª',
  '�â' => 'â‰«',
  '�ã' => 'âˆš',
  '�ä' => 'âˆ½',
  '�å' => 'âˆ�',
  '�æ' => 'âˆµ',
  '�ç' => 'âˆ«',
  '�è' => 'âˆ¬',
  '�ð' => 'â„«',
  '�ñ' => 'â€°',
  '�ò' => 'â™¯',
  '�ó' => 'â™­',
  '�ô' => 'â™ª',
  '�õ' => 'â€ ',
  '�ö' => 'â€¡',
  '�÷' => 'Â¶',
  '�ü' => 'â—¯',
  '‚O' => 'ï¼�',
  '‚P' => 'ï¼‘',
  '‚Q' => 'ï¼’',
  '‚R' => 'ï¼“',
  '‚S' => 'ï¼”',
  '‚T' => 'ï¼•',
  '‚U' => 'ï¼–',
  '‚V' => 'ï¼—',
  '‚W' => 'ï¼˜',
  '‚X' => 'ï¼™',
  '‚`' => 'ï¼¡',
  '‚a' => 'ï¼¢',
  '‚b' => 'ï¼£',
  '‚c' => 'ï¼¤',
  '‚d' => 'ï¼¥',
  '‚e' => 'ï¼¦',
  '‚f' => 'ï¼§',
  '‚g' => 'ï¼¨',
  '‚h' => 'ï¼©',
  '‚i' => 'ï¼ª',
  '‚j' => 'ï¼«',
  '‚k' => 'ï¼¬',
  '‚l' => 'ï¼­',
  '‚m' => 'ï¼®',
  '‚n' => 'ï¼¯',
  '‚o' => 'ï¼°',
  '‚p' => 'ï¼±',
  '‚q' => 'ï¼²',
  '‚r' => 'ï¼³',
  '‚s' => 'ï¼´',
  '‚t' => 'ï¼µ',
  '‚u' => 'ï¼¶',
  '‚v' => 'ï¼·',
  '‚w' => 'ï¼¸',
  '‚x' => 'ï¼¹',
  '‚y' => 'ï¼º',
  '‚�' => 'ï½�',
  '‚‚' => 'ï½‚',
  '‚ƒ' => 'ï½ƒ',
  '‚„' => 'ï½„',
  '‚…' => 'ï½…',
  '‚†' => 'ï½†',
  '‚‡' => 'ï½‡',
  '‚ˆ' => 'ï½ˆ',
  '‚‰' => 'ï½‰',
  '‚Š' => 'ï½Š',
  '‚‹' => 'ï½‹',
  '‚Œ' => 'ï½Œ',
  '‚�' => 'ï½�',
  '‚Ž' => 'ï½Ž',
  '‚�' => 'ï½�',
  '‚�' => 'ï½�',
  '‚‘' => 'ï½‘',
  '‚’' => 'ï½’',
  '‚“' => 'ï½“',
  '‚”' => 'ï½”',
  '‚•' => 'ï½•',
  '‚–' => 'ï½–',
  '‚—' => 'ï½—',
  '‚˜' => 'ï½˜',
  '‚™' => 'ï½™',
  '‚š' => 'ï½š',
  '‚Ÿ' => 'ã��',
  '‚ ' => 'ã�‚',
  '‚¡' => 'ã�ƒ',
  '‚¢' => 'ã�„',
  '‚£' => 'ã�…',
  '‚¤' => 'ã�†',
  '‚¥' => 'ã�‡',
  '‚¦' => 'ã�ˆ',
  '‚§' => 'ã�‰',
  '‚¨' => 'ã�Š',
  '‚©' => 'ã�‹',
  '‚ª' => 'ã�Œ',
  '‚«' => 'ã��',
  '‚¬' => 'ã�Ž',
  '‚­' => 'ã��',
  '‚®' => 'ã��',
  '‚¯' => 'ã�‘',
  '‚°' => 'ã�’',
  '‚±' => 'ã�“',
  '‚²' => 'ã�”',
  '‚³' => 'ã�•',
  '‚´' => 'ã�–',
  '‚µ' => 'ã�—',
  '‚¶' => 'ã�˜',
  '‚·' => 'ã�™',
  '‚¸' => 'ã�š',
  '‚¹' => 'ã�›',
  '‚º' => 'ã�œ',
  '‚»' => 'ã��',
  '‚¼' => 'ã�ž',
  '‚½' => 'ã�Ÿ',
  '‚¾' => 'ã� ',
  '‚¿' => 'ã�¡',
  '‚À' => 'ã�¢',
  '‚Á' => 'ã�£',
  '‚Â' => 'ã�¤',
  '‚Ã' => 'ã�¥',
  '‚Ä' => 'ã�¦',
  '‚Å' => 'ã�§',
  '‚Æ' => 'ã�¨',
  '‚Ç' => 'ã�©',
  '‚È' => 'ã�ª',
  '‚É' => 'ã�«',
  '‚Ê' => 'ã�¬',
  '‚Ë' => 'ã�­',
  '‚Ì' => 'ã�®',
  '‚Í' => 'ã�¯',
  '‚Î' => 'ã�°',
  '‚Ï' => 'ã�±',
  '‚Ð' => 'ã�²',
  '‚Ñ' => 'ã�³',
  '‚Ò' => 'ã�´',
  '‚Ó' => 'ã�µ',
  '‚Ô' => 'ã�¶',
  '‚Õ' => 'ã�·',
  '‚Ö' => 'ã�¸',
  '‚×' => 'ã�¹',
  '‚Ø' => 'ã�º',
  '‚Ù' => 'ã�»',
  '‚Ú' => 'ã�¼',
  '‚Û' => 'ã�½',
  '‚Ü' => 'ã�¾',
  '‚Ý' => 'ã�¿',
  '‚Þ' => 'ã‚€',
  '‚ß' => 'ã‚�',
  '‚à' => 'ã‚‚',
  '‚á' => 'ã‚ƒ',
  '‚â' => 'ã‚„',
  '‚ã' => 'ã‚…',
  '‚ä' => 'ã‚†',
  '‚å' => 'ã‚‡',
  '‚æ' => 'ã‚ˆ',
  '‚ç' => 'ã‚‰',
  '‚è' => 'ã‚Š',
  '‚é' => 'ã‚‹',
  '‚ê' => 'ã‚Œ',
  '‚ë' => 'ã‚�',
  '‚ì' => 'ã‚Ž',
  '‚í' => 'ã‚�',
  '‚î' => 'ã‚�',
  '‚ï' => 'ã‚‘',
  '‚ð' => 'ã‚’',
  '‚ñ' => 'ã‚“',
  'ƒ@' => 'ã‚¡',
  'ƒA' => 'ã‚¢',
  'ƒB' => 'ã‚£',
  'ƒC' => 'ã‚¤',
  'ƒD' => 'ã‚¥',
  'ƒE' => 'ã‚¦',
  'ƒF' => 'ã‚§',
  'ƒG' => 'ã‚¨',
  'ƒH' => 'ã‚©',
  'ƒI' => 'ã‚ª',
  'ƒJ' => 'ã‚«',
  'ƒK' => 'ã‚¬',
  'ƒL' => 'ã‚­',
  'ƒM' => 'ã‚®',
  'ƒN' => 'ã‚¯',
  'ƒO' => 'ã‚°',
  'ƒP' => 'ã‚±',
  'ƒQ' => 'ã‚²',
  'ƒR' => 'ã‚³',
  'ƒS' => 'ã‚´',
  'ƒT' => 'ã‚µ',
  'ƒU' => 'ã‚¶',
  'ƒV' => 'ã‚·',
  'ƒW' => 'ã‚¸',
  'ƒX' => 'ã‚¹',
  'ƒY' => 'ã‚º',
  'ƒZ' => 'ã‚»',
  'ƒ[' => 'ã‚¼',
  'ƒ\\' => 'ã‚½',
  'ƒ]' => 'ã‚¾',
  'ƒ^' => 'ã‚¿',
  'ƒ_' => 'ãƒ€',
  'ƒ`' => 'ãƒ�',
  'ƒa' => 'ãƒ‚',
  'ƒb' => 'ãƒƒ',
  'ƒc' => 'ãƒ„',
  'ƒd' => 'ãƒ…',
  'ƒe' => 'ãƒ†',
  'ƒf' => 'ãƒ‡',
  'ƒg' => 'ãƒˆ',
  'ƒh' => 'ãƒ‰',
  'ƒi' => 'ãƒŠ',
  'ƒj' => 'ãƒ‹',
  'ƒk' => 'ãƒŒ',
  'ƒl' => 'ãƒ�',
  'ƒm' => 'ãƒŽ',
  'ƒn' => 'ãƒ�',
  'ƒo' => 'ãƒ�',
  'ƒp' => 'ãƒ‘',
  'ƒq' => 'ãƒ’',
  'ƒr' => 'ãƒ“',
  'ƒs' => 'ãƒ”',
  'ƒt' => 'ãƒ•',
  'ƒu' => 'ãƒ–',
  'ƒv' => 'ãƒ—',
  'ƒw' => 'ãƒ˜',
  'ƒx' => 'ãƒ™',
  'ƒy' => 'ãƒš',
  'ƒz' => 'ãƒ›',
  'ƒ{' => 'ãƒœ',
  'ƒ|' => 'ãƒ�',
  'ƒ}' => 'ãƒž',
  'ƒ~' => 'ãƒŸ',
  'ƒ€' => 'ãƒ ',
  'ƒ�' => 'ãƒ¡',
  'ƒ‚' => 'ãƒ¢',
  'ƒƒ' => 'ãƒ£',
  'ƒ„' => 'ãƒ¤',
  'ƒ…' => 'ãƒ¥',
  'ƒ†' => 'ãƒ¦',
  'ƒ‡' => 'ãƒ§',
  'ƒˆ' => 'ãƒ¨',
  'ƒ‰' => 'ãƒ©',
  'ƒŠ' => 'ãƒª',
  'ƒ‹' => 'ãƒ«',
  'ƒŒ' => 'ãƒ¬',
  'ƒ�' => 'ãƒ­',
  'ƒŽ' => 'ãƒ®',
  'ƒ�' => 'ãƒ¯',
  'ƒ�' => 'ãƒ°',
  'ƒ‘' => 'ãƒ±',
  'ƒ’' => 'ãƒ²',
  'ƒ“' => 'ãƒ³',
  'ƒ”' => 'ãƒ´',
  'ƒ•' => 'ãƒµ',
  'ƒ–' => 'ãƒ¶',
  'ƒŸ' => 'Î‘',
  'ƒ ' => 'Î’',
  'ƒ¡' => 'Î“',
  'ƒ¢' => 'Î”',
  'ƒ£' => 'Î•',
  'ƒ¤' => 'Î–',
  'ƒ¥' => 'Î—',
  'ƒ¦' => 'Î˜',
  'ƒ§' => 'Î™',
  'ƒ¨' => 'Îš',
  'ƒ©' => 'Î›',
  'ƒª' => 'Îœ',
  'ƒ«' => 'Î�',
  'ƒ¬' => 'Îž',
  'ƒ­' => 'ÎŸ',
  'ƒ®' => 'Î ',
  'ƒ¯' => 'Î¡',
  'ƒ°' => 'Î£',
  'ƒ±' => 'Î¤',
  'ƒ²' => 'Î¥',
  'ƒ³' => 'Î¦',
  'ƒ´' => 'Î§',
  'ƒµ' => 'Î¨',
  'ƒ¶' => 'Î©',
  'ƒ¿' => 'Î±',
  'ƒÀ' => 'Î²',
  'ƒÁ' => 'Î³',
  'ƒÂ' => 'Î´',
  'ƒÃ' => 'Îµ',
  'ƒÄ' => 'Î¶',
  'ƒÅ' => 'Î·',
  'ƒÆ' => 'Î¸',
  'ƒÇ' => 'Î¹',
  'ƒÈ' => 'Îº',
  'ƒÉ' => 'Î»',
  'ƒÊ' => 'Î¼',
  'ƒË' => 'Î½',
  'ƒÌ' => 'Î¾',
  'ƒÍ' => 'Î¿',
  'ƒÎ' => 'Ï€',
  'ƒÏ' => 'Ï�',
  'ƒÐ' => 'Ïƒ',
  'ƒÑ' => 'Ï„',
  'ƒÒ' => 'Ï…',
  'ƒÓ' => 'Ï†',
  'ƒÔ' => 'Ï‡',
  'ƒÕ' => 'Ïˆ',
  'ƒÖ' => 'Ï‰',
  '„@' => 'Ð�',
  '„A' => 'Ð‘',
  '„B' => 'Ð’',
  '„C' => 'Ð“',
  '„D' => 'Ð”',
  '„E' => 'Ð•',
  '„F' => 'Ð�',
  '„G' => 'Ð–',
  '„H' => 'Ð—',
  '„I' => 'Ð˜',
  '„J' => 'Ð™',
  '„K' => 'Ðš',
  '„L' => 'Ð›',
  '„M' => 'Ðœ',
  '„N' => 'Ð�',
  '„O' => 'Ðž',
  '„P' => 'ÐŸ',
  '„Q' => 'Ð ',
  '„R' => 'Ð¡',
  '„S' => 'Ð¢',
  '„T' => 'Ð£',
  '„U' => 'Ð¤',
  '„V' => 'Ð¥',
  '„W' => 'Ð¦',
  '„X' => 'Ð§',
  '„Y' => 'Ð¨',
  '„Z' => 'Ð©',
  '„[' => 'Ðª',
  '„\\' => 'Ð«',
  '„]' => 'Ð¬',
  '„^' => 'Ð­',
  '„_' => 'Ð®',
  '„`' => 'Ð¯',
  '„p' => 'Ð°',
  '„q' => 'Ð±',
  '„r' => 'Ð²',
  '„s' => 'Ð³',
  '„t' => 'Ð´',
  '„u' => 'Ðµ',
  '„v' => 'Ñ‘',
  '„w' => 'Ð¶',
  '„x' => 'Ð·',
  '„y' => 'Ð¸',
  '„z' => 'Ð¹',
  '„{' => 'Ðº',
  '„|' => 'Ð»',
  '„}' => 'Ð¼',
  '„~' => 'Ð½',
  '„€' => 'Ð¾',
  '„�' => 'Ð¿',
  '„‚' => 'Ñ€',
  '„ƒ' => 'Ñ�',
  '„„' => 'Ñ‚',
  '„…' => 'Ñƒ',
  '„†' => 'Ñ„',
  '„‡' => 'Ñ…',
  '„ˆ' => 'Ñ†',
  '„‰' => 'Ñ‡',
  '„Š' => 'Ñˆ',
  '„‹' => 'Ñ‰',
  '„Œ' => 'ÑŠ',
  '„�' => 'Ñ‹',
  '„Ž' => 'ÑŒ',
  '„�' => 'Ñ�',
  '„�' => 'ÑŽ',
  '„‘' => 'Ñ�',
  '„Ÿ' => 'â”€',
  '„ ' => 'â”‚',
  '„¡' => 'â”Œ',
  '„¢' => 'â”�',
  '„£' => 'â”˜',
  '„¤' => 'â””',
  '„¥' => 'â”œ',
  '„¦' => 'â”¬',
  '„§' => 'â”¤',
  '„¨' => 'â”´',
  '„©' => 'â”¼',
  '„ª' => 'â”�',
  '„«' => 'â”ƒ',
  '„¬' => 'â”�',
  '„­' => 'â”“',
  '„®' => 'â”›',
  '„¯' => 'â”—',
  '„°' => 'â”£',
  '„±' => 'â”³',
  '„²' => 'â”«',
  '„³' => 'â”»',
  '„´' => 'â•‹',
  '„µ' => 'â” ',
  '„¶' => 'â”¯',
  '„·' => 'â”¨',
  '„¸' => 'â”·',
  '„¹' => 'â”¿',
  '„º' => 'â”�',
  '„»' => 'â”°',
  '„¼' => 'â”¥',
  '„½' => 'â”¸',
  '„¾' => 'â•‚',
  '‡@' => 'â‘ ',
  '‡A' => 'â‘¡',
  '‡B' => 'â‘¢',
  '‡C' => 'â‘£',
  '‡D' => 'â‘¤',
  '‡E' => 'â‘¥',
  '‡F' => 'â‘¦',
  '‡G' => 'â‘§',
  '‡H' => 'â‘¨',
  '‡I' => 'â‘©',
  '‡J' => 'â‘ª',
  '‡K' => 'â‘«',
  '‡L' => 'â‘¬',
  '‡M' => 'â‘­',
  '‡N' => 'â‘®',
  '‡O' => 'â‘¯',
  '‡P' => 'â‘°',
  '‡Q' => 'â‘±',
  '‡R' => 'â‘²',
  '‡S' => 'â‘³',
  '‡T' => 'â… ',
  '‡U' => 'â…¡',
  '‡V' => 'â…¢',
  '‡W' => 'â…£',
  '‡X' => 'â…¤',
  '‡Y' => 'â…¥',
  '‡Z' => 'â…¦',
  '‡[' => 'â…§',
  '‡\\' => 'â…¨',
  '‡]' => 'â…©',
  '‡_' => 'ã�‰',
  '‡`' => 'ãŒ”',
  '‡a' => 'ãŒ¢',
  '‡b' => 'ã��',
  '‡c' => 'ãŒ˜',
  '‡d' => 'ãŒ§',
  '‡e' => 'ãŒƒ',
  '‡f' => 'ãŒ¶',
  '‡g' => 'ã�‘',
  '‡h' => 'ã�—',
  '‡i' => 'ãŒ�',
  '‡j' => 'ãŒ¦',
  '‡k' => 'ãŒ£',
  '‡l' => 'ãŒ«',
  '‡m' => 'ã�Š',
  '‡n' => 'ãŒ»',
  '‡o' => 'ãŽœ',
  '‡p' => 'ãŽ�',
  '‡q' => 'ãŽž',
  '‡r' => 'ãŽŽ',
  '‡s' => 'ãŽ�',
  '‡t' => 'ã�„',
  '‡u' => 'ãŽ¡',
  '‡~' => 'ã�»',
  '‡€' => 'ã€�',
  '‡�' => 'ã€Ÿ',
  '‡‚' => 'â„–',
  '‡ƒ' => 'ã��',
  '‡„' => 'â„¡',
  '‡…' => 'ãŠ¤',
  '‡†' => 'ãŠ¥',
  '‡‡' => 'ãŠ¦',
  '‡ˆ' => 'ãŠ§',
  '‡‰' => 'ãŠ¨',
  '‡Š' => 'ãˆ±',
  '‡‹' => 'ãˆ²',
  '‡Œ' => 'ãˆ¹',
  '‡�' => 'ã�¾',
  '‡Ž' => 'ã�½',
  '‡�' => 'ã�¼',
  '‡�' => 'â‰’',
  '‡‘' => 'â‰¡',
  '‡’' => 'âˆ«',
  '‡“' => 'âˆ®',
  '‡”' => 'âˆ‘',
  '‡•' => 'âˆš',
  '‡–' => 'âŠ¥',
  '‡—' => 'âˆ ',
  '‡˜' => 'âˆŸ',
  '‡™' => 'âŠ¿',
  '‡š' => 'âˆµ',
  '‡›' => 'âˆ©',
  '‡œ' => 'âˆª',
  'ˆŸ' => 'äºœ',
  'ˆ ' => 'å”–',
  'ˆ¡' => 'å¨ƒ',
  'ˆ¢' => 'é˜¿',
  'ˆ£' => 'å“€',
  'ˆ¤' => 'æ„›',
  'ˆ¥' => 'æŒ¨',
  'ˆ¦' => 'å§¶',
  'ˆ§' => 'é€¢',
  'ˆ¨' => 'è‘µ',
  'ˆ©' => 'èŒœ',
  'ˆª' => 'ç©�',
  'ˆ«' => 'æ‚ª',
  'ˆ¬' => 'æ�¡',
  'ˆ­' => 'æ¸¥',
  'ˆ®' => 'æ—­',
  'ˆ¯' => 'è‘¦',
  'ˆ°' => 'èŠ¦',
  'ˆ±' => 'é¯µ',
  'ˆ²' => 'æ¢“',
  'ˆ³' => 'åœ§',
  'ˆ´' => 'æ–¡',
  'ˆµ' => 'æ‰±',
  'ˆ¶' => 'å®›',
  'ˆ·' => 'å§�',
  'ˆ¸' => 'è™»',
  'ˆ¹' => 'é£´',
  'ˆº' => 'çµ¢',
  'ˆ»' => 'ç¶¾',
  'ˆ¼' => 'é®Ž',
  'ˆ½' => 'æˆ–',
  'ˆ¾' => 'ç²Ÿ',
  'ˆ¿' => 'è¢·',
  'ˆÀ' => 'å®‰',
  'ˆÁ' => 'åºµ',
  'ˆÂ' => 'æŒ‰',
  'ˆÃ' => 'æš—',
  'ˆÄ' => 'æ¡ˆ',
  'ˆÅ' => 'é—‡',
  'ˆÆ' => 'éž�',
  'ˆÇ' => 'æ��',
  'ˆÈ' => 'ä»¥',
  'ˆÉ' => 'ä¼Š',
  'ˆÊ' => 'ä½�',
  'ˆË' => 'ä¾�',
  'ˆÌ' => 'å�‰',
  'ˆÍ' => 'å›²',
  'ˆÎ' => 'å¤·',
  'ˆÏ' => 'å§”',
  'ˆÐ' => 'å¨�',
  'ˆÑ' => 'å°‰',
  'ˆÒ' => 'æƒŸ',
  'ˆÓ' => 'æ„�',
  'ˆÔ' => 'æ…°',
  'ˆÕ' => 'æ˜“',
  'ˆÖ' => 'æ¤…',
  'ˆ×' => 'ç‚º',
  'ˆØ' => 'ç•�',
  'ˆÙ' => 'ç•°',
  'ˆÚ' => 'ç§»',
  'ˆÛ' => 'ç¶­',
  'ˆÜ' => 'ç·¯',
  'ˆÝ' => 'èƒƒ',
  'ˆÞ' => 'è�Ž',
  'ˆß' => 'è¡£',
  'ˆà' => 'è¬‚',
  'ˆá' => 'é�•',
  'ˆâ' => 'é�º',
  'ˆã' => 'åŒ»',
  'ˆä' => 'äº•',
  'ˆå' => 'äº¥',
  'ˆæ' => 'åŸŸ',
  'ˆç' => 'è‚²',
  'ˆè' => 'éƒ�',
  'ˆé' => 'ç£¯',
  'ˆê' => 'ä¸€',
  'ˆë' => 'å£±',
  'ˆì' => 'æº¢',
  'ˆí' => 'é€¸',
  'ˆî' => 'ç¨²',
  'ˆï' => 'èŒ¨',
  'ˆð' => 'èŠ‹',
  'ˆñ' => 'é°¯',
  'ˆò' => 'å…�',
  'ˆó' => 'å�°',
  'ˆô' => 'å’½',
  'ˆõ' => 'å“¡',
  'ˆö' => 'å› ',
  'ˆ÷' => 'å§»',
  'ˆø' => 'å¼•',
  'ˆù' => 'é£²',
  'ˆú' => 'æ·«',
  'ˆû' => 'èƒ¤',
  'ˆü' => 'è”­',
  '‰@' => 'é™¢',
  '‰A' => 'é™°',
  '‰B' => 'éš ',
  '‰C' => 'éŸ»',
  '‰D' => 'å�‹',
  '‰E' => 'å�³',
  '‰F' => 'å®‡',
  '‰G' => 'çƒ�',
  '‰H' => 'ç¾½',
  '‰I' => 'è¿‚',
  '‰J' => 'é›¨',
  '‰K' => 'å�¯',
  '‰L' => 'éµœ',
  '‰M' => 'çªº',
  '‰N' => 'ä¸‘',
  '‰O' => 'ç¢“',
  '‰P' => 'è‡¼',
  '‰Q' => 'æ¸¦',
  '‰R' => 'å˜˜',
  '‰S' => 'å”„',
  '‰T' => 'æ¬�',
  '‰U' => 'è”š',
  '‰V' => 'é°»',
  '‰W' => 'å§¥',
  '‰X' => 'åŽ©',
  '‰Y' => 'æµ¦',
  '‰Z' => 'ç“œ',
  '‰[' => 'é–�',
  '‰\\' => 'å™‚',
  '‰]' => 'äº‘',
  '‰^' => 'é�‹',
  '‰_' => 'é›²',
  '‰`' => 'è��',
  '‰a' => 'é¤Œ',
  '‰b' => 'å�¡',
  '‰c' => 'å–¶',
  '‰d' => 'å¬°',
  '‰e' => 'å½±',
  '‰f' => 'æ˜ ',
  '‰g' => 'æ›³',
  '‰h' => 'æ „',
  '‰i' => 'æ°¸',
  '‰j' => 'æ³³',
  '‰k' => 'æ´©',
  '‰l' => 'ç‘›',
  '‰m' => 'ç›ˆ',
  '‰n' => 'ç©Ž',
  '‰o' => 'é ´',
  '‰p' => 'è‹±',
  '‰q' => 'è¡›',
  '‰r' => 'è© ',
  '‰s' => 'é‹­',
  '‰t' => 'æ¶²',
  '‰u' => 'ç–«',
  '‰v' => 'ç›Š',
  '‰w' => 'é§…',
  '‰x' => 'æ‚¦',
  '‰y' => 'è¬�',
  '‰z' => 'è¶Š',
  '‰{' => 'é–²',
  '‰|' => 'æ¦Ž',
  '‰}' => 'åŽ­',
  '‰~' => 'å††',
  '‰€' => 'åœ’',
  '‰�' => 'å °',
  '‰‚' => 'å¥„',
  '‰ƒ' => 'å®´',
  '‰„' => 'å»¶',
  '‰…' => 'æ€¨',
  '‰†' => 'æŽ©',
  '‰‡' => 'æ�´',
  '‰ˆ' => 'æ²¿',
  '‰‰' => 'æ¼”',
  '‰Š' => 'ç‚Ž',
  '‰‹' => 'ç„”',
  '‰Œ' => 'ç…™',
  '‰�' => 'ç‡•',
  '‰Ž' => 'çŒ¿',
  '‰�' => 'ç¸�',
  '‰�' => 'è‰¶',
  '‰‘' => 'è‹‘',
  '‰’' => 'è–—',
  '‰“' => 'é� ',
  '‰”' => 'é‰›',
  '‰•' => 'é´›',
  '‰–' => 'å¡©',
  '‰—' => 'æ–¼',
  '‰˜' => 'æ±š',
  '‰™' => 'ç”¥',
  '‰š' => 'å‡¹',
  '‰›' => 'å¤®',
  '‰œ' => 'å¥¥',
  '‰�' => 'å¾€',
  '‰ž' => 'å¿œ',
  '‰Ÿ' => 'æŠ¼',
  '‰ ' => 'æ—º',
  '‰¡' => 'æ¨ª',
  '‰¢' => 'æ¬§',
  '‰£' => 'æ®´',
  '‰¤' => 'çŽ‹',
  '‰¥' => 'ç¿�',
  '‰¦' => 'è¥–',
  '‰§' => 'é´¬',
  '‰¨' => 'é´Ž',
  '‰©' => 'é»„',
  '‰ª' => 'å²¡',
  '‰«' => 'æ²–',
  '‰¬' => 'è�»',
  '‰­' => 'å„„',
  '‰®' => 'å±‹',
  '‰¯' => 'æ†¶',
  '‰°' => 'è‡†',
  '‰±' => 'æ¡¶',
  '‰²' => 'ç‰¡',
  '‰³' => 'ä¹™',
  '‰´' => 'ä¿º',
  '‰µ' => 'å�¸',
  '‰¶' => 'æ�©',
  '‰·' => 'æ¸©',
  '‰¸' => 'ç©�',
  '‰¹' => 'éŸ³',
  '‰º' => 'ä¸‹',
  '‰»' => 'åŒ–',
  '‰¼' => 'ä»®',
  '‰½' => 'ä½•',
  '‰¾' => 'ä¼½',
  '‰¿' => 'ä¾¡',
  '‰À' => 'ä½³',
  '‰Á' => 'åŠ ',
  '‰Â' => 'å�¯',
  '‰Ã' => 'å˜‰',
  '‰Ä' => 'å¤�',
  '‰Å' => 'å«�',
  '‰Æ' => 'å®¶',
  '‰Ç' => 'å¯¡',
  '‰È' => 'ç§‘',
  '‰É' => 'æš‡',
  '‰Ê' => 'æžœ',
  '‰Ë' => 'æž¶',
  '‰Ì' => 'æ­Œ',
  '‰Í' => 'æ²³',
  '‰Î' => 'ç�«',
  '‰Ï' => 'ç�‚',
  '‰Ð' => 'ç¦�',
  '‰Ñ' => 'ç¦¾',
  '‰Ò' => 'ç¨¼',
  '‰Ó' => 'ç®‡',
  '‰Ô' => 'èŠ±',
  '‰Õ' => 'è‹›',
  '‰Ö' => 'èŒ„',
  '‰×' => 'è�·',
  '‰Ø' => 'è�¯',
  '‰Ù' => 'è�“',
  '‰Ú' => 'è�¦',
  '‰Û' => 'èª²',
  '‰Ü' => 'å˜©',
  '‰Ý' => 'è²¨',
  '‰Þ' => 'è¿¦',
  '‰ß' => 'é�Ž',
  '‰à' => 'éœž',
  '‰á' => 'èšŠ',
  '‰â' => 'ä¿„',
  '‰ã' => 'å³¨',
  '‰ä' => 'æˆ‘',
  '‰å' => 'ç‰™',
  '‰æ' => 'ç”»',
  '‰ç' => 'è‡¥',
  '‰è' => 'èŠ½',
  '‰é' => 'è›¾',
  '‰ê' => 'è³€',
  '‰ë' => 'é›…',
  '‰ì' => 'é¤“',
  '‰í' => 'é§•',
  '‰î' => 'ä»‹',
  '‰ï' => 'ä¼š',
  '‰ð' => 'è§£',
  '‰ñ' => 'å›ž',
  '‰ò' => 'å¡Š',
  '‰ó' => 'å£Š',
  '‰ô' => 'å»»',
  '‰õ' => 'å¿«',
  '‰ö' => 'æ€ª',
  '‰÷' => 'æ‚”',
  '‰ø' => 'æ�¢',
  '‰ù' => 'æ‡�',
  '‰ú' => 'æˆ’',
  '‰û' => 'æ‹�',
  '‰ü' => 'æ”¹',
  'Š@' => 'é­�',
  'ŠA' => 'æ™¦',
  'ŠB' => 'æ¢°',
  'ŠC' => 'æµ·',
  'ŠD' => 'ç�°',
  'ŠE' => 'ç•Œ',
  'ŠF' => 'çš†',
  'ŠG' => 'çµµ',
  'ŠH' => 'èŠ¥',
  'ŠI' => 'èŸ¹',
  'ŠJ' => 'é–‹',
  'ŠK' => 'éšŽ',
  'ŠL' => 'è²�',
  'ŠM' => 'å‡±',
  'ŠN' => 'åŠ¾',
  'ŠO' => 'å¤–',
  'ŠP' => 'å’³',
  'ŠQ' => 'å®³',
  'ŠR' => 'å´–',
  'ŠS' => 'æ…¨',
  'ŠT' => 'æ¦‚',
  'ŠU' => 'æ¶¯',
  'ŠV' => 'ç¢�',
  'ŠW' => 'è“‹',
  'ŠX' => 'è¡—',
  'ŠY' => 'è©²',
  'ŠZ' => 'éŽ§',
  'Š[' => 'éª¸',
  'Š\\' => 'æµ¬',
  'Š]' => 'é¦¨',
  'Š^' => 'è›™',
  'Š_' => 'åž£',
  'Š`' => 'æŸ¿',
  'Ša' => 'è›Ž',
  'Šb' => 'éˆŽ',
  'Šc' => 'åŠƒ',
  'Šd' => 'åš‡',
  'Še' => 'å�„',
  'Šf' => 'å»“',
  'Šg' => 'æ‹¡',
  'Šh' => 'æ’¹',
  'Ši' => 'æ ¼',
  'Šj' => 'æ ¸',
  'Šk' => 'æ®»',
  'Šl' => 'ç�²',
  'Šm' => 'ç¢º',
  'Šn' => 'ç©«',
  'Šo' => 'è¦š',
  'Šp' => 'è§’',
  'Šq' => 'èµ«',
  'Šr' => 'è¼ƒ',
  'Šs' => 'éƒ­',
  'Št' => 'é–£',
  'Šu' => 'éš”',
  'Šv' => 'é�©',
  'Šw' => 'å­¦',
  'Šx' => 'å²³',
  'Šy' => 'æ¥½',
  'Šz' => 'é¡�',
  'Š{' => 'é¡Ž',
  'Š|' => 'æŽ›',
  'Š}' => 'ç¬ ',
  'Š~' => 'æ¨«',
  'Š€' => 'æ©¿',
  'Š�' => 'æ¢¶',
  'Š‚' => 'é°�',
  'Šƒ' => 'æ½Ÿ',
  'Š„' => 'å‰²',
  'Š…' => 'å–�',
  'Š†' => 'æ�°',
  'Š‡' => 'æ‹¬',
  'Šˆ' => 'æ´»',
  'Š‰' => 'æ¸‡',
  'ŠŠ' => 'æ»‘',
  'Š‹' => 'è‘›',
  'ŠŒ' => 'è¤�',
  'Š�' => 'è½„',
  'ŠŽ' => 'ä¸”',
  'Š�' => 'é°¹',
  'Š�' => 'å�¶',
  'Š‘' => 'æ¤›',
  'Š’' => 'æ¨º',
  'Š“' => 'éž„',
  'Š”' => 'æ ª',
  'Š•' => 'å…œ',
  'Š–' => 'ç«ƒ',
  'Š—' => 'è’²',
  'Š˜' => 'é‡œ',
  'Š™' => 'éŽŒ',
  'Šš' => 'å™›',
  'Š›' => 'é´¨',
  'Šœ' => 'æ ¢',
  'Š�' => 'èŒ…',
  'Šž' => 'è�±',
  'ŠŸ' => 'ç²¥',
  'Š ' => 'åˆˆ',
  'Š¡' => 'è‹…',
  'Š¢' => 'ç“¦',
  'Š£' => 'ä¹¾',
  'Š¤' => 'ä¾ƒ',
  'Š¥' => 'å† ',
  'Š¦' => 'å¯’',
  'Š§' => 'åˆŠ',
  'Š¨' => 'å‹˜',
  'Š©' => 'å‹§',
  'Šª' => 'å·»',
  'Š«' => 'å–š',
  'Š¬' => 'å ª',
  'Š­' => 'å§¦',
  'Š®' => 'å®Œ',
  'Š¯' => 'å®˜',
  'Š°' => 'å¯›',
  'Š±' => 'å¹²',
  'Š²' => 'å¹¹',
  'Š³' => 'æ‚£',
  'Š´' => 'æ„Ÿ',
  'Šµ' => 'æ…£',
  'Š¶' => 'æ†¾',
  'Š·' => 'æ�›',
  'Š¸' => 'æ•¢',
  'Š¹' => 'æŸ‘',
  'Šº' => 'æ¡“',
  'Š»' => 'æ£º',
  'Š¼' => 'æ¬¾',
  'Š½' => 'æ­“',
  'Š¾' => 'æ±—',
  'Š¿' => 'æ¼¢',
  'ŠÀ' => 'æ¾—',
  'ŠÁ' => 'æ½…',
  'ŠÂ' => 'ç’°',
  'ŠÃ' => 'ç”˜',
  'ŠÄ' => 'ç›£',
  'ŠÅ' => 'çœ‹',
  'ŠÆ' => 'ç«¿',
  'ŠÇ' => 'ç®¡',
  'ŠÈ' => 'ç°¡',
  'ŠÉ' => 'ç·©',
  'ŠÊ' => 'ç¼¶',
  'ŠË' => 'ç¿°',
  'ŠÌ' => 'è‚�',
  'ŠÍ' => 'è‰¦',
  'ŠÎ' => 'èŽž',
  'ŠÏ' => 'è¦³',
  'ŠÐ' => 'è«Œ',
  'ŠÑ' => 'è²«',
  'ŠÒ' => 'é‚„',
  'ŠÓ' => 'é‘‘',
  'ŠÔ' => 'é–“',
  'ŠÕ' => 'é–‘',
  'ŠÖ' => 'é–¢',
  'Š×' => 'é™¥',
  'ŠØ' => 'éŸ“',
  'ŠÙ' => 'é¤¨',
  'ŠÚ' => 'èˆ˜',
  'ŠÛ' => 'ä¸¸',
  'ŠÜ' => 'å�«',
  'ŠÝ' => 'å²¸',
  'ŠÞ' => 'å·Œ',
  'Šß' => 'çŽ©',
  'Šà' => 'ç™Œ',
  'Šá' => 'çœ¼',
  'Šâ' => 'å²©',
  'Šã' => 'ç¿«',
  'Šä' => 'è´‹',
  'Šå' => 'é›�',
  'Šæ' => 'é ‘',
  'Šç' => 'é¡”',
  'Šè' => 'é¡˜',
  'Šé' => 'ä¼�',
  'Šê' => 'ä¼Ž',
  'Šë' => 'å�±',
  'Šì' => 'å–œ',
  'Ší' => 'å™¨',
  'Šî' => 'åŸº',
  'Šï' => 'å¥‡',
  'Šð' => 'å¬‰',
  'Šñ' => 'å¯„',
  'Šò' => 'å²�',
  'Šó' => 'å¸Œ',
  'Šô' => 'å¹¾',
  'Šõ' => 'å¿Œ',
  'Šö' => 'æ�®',
  'Š÷' => 'æœº',
  'Šø' => 'æ——',
  'Šù' => 'æ—¢',
  'Šú' => 'æœŸ',
  'Šû' => 'æ£‹',
  'Šü' => 'æ£„',
  '‹@' => 'æ©Ÿ',
  '‹A' => 'å¸°',
  '‹B' => 'æ¯…',
  '‹C' => 'æ°—',
  '‹D' => 'æ±½',
  '‹E' => 'ç•¿',
  '‹F' => 'ç¥ˆ',
  '‹G' => 'å­£',
  '‹H' => 'ç¨€',
  '‹I' => 'ç´€',
  '‹J' => 'å¾½',
  '‹K' => 'è¦�',
  '‹L' => 'è¨˜',
  '‹M' => 'è²´',
  '‹N' => 'èµ·',
  '‹O' => 'è»Œ',
  '‹P' => 'è¼�',
  '‹Q' => 'é£¢',
  '‹R' => 'é¨Ž',
  '‹S' => 'é¬¼',
  '‹T' => 'äº€',
  '‹U' => 'å�½',
  '‹V' => 'å„€',
  '‹W' => 'å¦“',
  '‹X' => 'å®œ',
  '‹Y' => 'æˆ¯',
  '‹Z' => 'æŠ€',
  '‹[' => 'æ“¬',
  '‹\\' => 'æ¬º',
  '‹]' => 'çŠ ',
  '‹^' => 'ç–‘',
  '‹_' => 'ç¥‡',
  '‹`' => 'ç¾©',
  '‹a' => 'èŸ»',
  '‹b' => 'èª¼',
  '‹c' => 'è­°',
  '‹d' => 'æŽ¬',
  '‹e' => 'è�Š',
  '‹f' => 'éž ',
  '‹g' => 'å�‰',
  '‹h' => 'å�ƒ',
  '‹i' => 'å–«',
  '‹j' => 'æ¡”',
  '‹k' => 'æ©˜',
  '‹l' => 'è©°',
  '‹m' => 'ç §',
  '‹n' => 'æ�µ',
  '‹o' => 'é»�',
  '‹p' => 'å�´',
  '‹q' => 'å®¢',
  '‹r' => 'è„š',
  '‹s' => 'è™�',
  '‹t' => 'é€†',
  '‹u' => 'ä¸˜',
  '‹v' => 'ä¹…',
  '‹w' => 'ä»‡',
  '‹x' => 'ä¼‘',
  '‹y' => 'å�Š',
  '‹z' => 'å�¸',
  '‹{' => 'å®®',
  '‹|' => 'å¼“',
  '‹}' => 'æ€¥',
  '‹~' => 'æ•‘',
  '‹€' => 'æœ½',
  '‹�' => 'æ±‚',
  '‹‚' => 'æ±²',
  '‹ƒ' => 'æ³£',
  '‹„' => 'ç�¸',
  '‹…' => 'ç�ƒ',
  '‹†' => 'ç©¶',
  '‹‡' => 'çª®',
  '‹ˆ' => 'ç¬ˆ',
  '‹‰' => 'ç´š',
  '‹Š' => 'ç³¾',
  '‹‹' => 'çµ¦',
  '‹Œ' => 'æ—§',
  '‹�' => 'ç‰›',
  '‹Ž' => 'åŽ»',
  '‹�' => 'å±…',
  '‹�' => 'å·¨',
  '‹‘' => 'æ‹’',
  '‹’' => 'æ‹ ',
  '‹“' => 'æŒ™',
  '‹”' => 'æ¸ ',
  '‹•' => 'è™š',
  '‹–' => 'è¨±',
  '‹—' => 'è·�',
  '‹˜' => 'é‹¸',
  '‹™' => 'æ¼�',
  '‹š' => 'ç¦¦',
  '‹›' => 'é­š',
  '‹œ' => 'äº¨',
  '‹�' => 'äº«',
  '‹ž' => 'äº¬',
  '‹Ÿ' => 'ä¾›',
  '‹ ' => 'ä¾ ',
  '‹¡' => 'åƒ‘',
  '‹¢' => 'å…‡',
  '‹£' => 'ç«¶',
  '‹¤' => 'å…±',
  '‹¥' => 'å‡¶',
  '‹¦' => 'å�”',
  '‹§' => 'åŒ¡',
  '‹¨' => 'å�¿',
  '‹©' => 'å�«',
  '‹ª' => 'å–¬',
  '‹«' => 'å¢ƒ',
  '‹¬' => 'å³¡',
  '‹­' => 'å¼·',
  '‹®' => 'å½Š',
  '‹¯' => 'æ€¯',
  '‹°' => 'æ��',
  '‹±' => 'æ�­',
  '‹²' => 'æŒŸ',
  '‹³' => 'æ•™',
  '‹´' => 'æ©‹',
  '‹µ' => 'æ³�',
  '‹¶' => 'ç‹‚',
  '‹·' => 'ç‹­',
  '‹¸' => 'çŸ¯',
  '‹¹' => 'èƒ¸',
  '‹º' => 'è„…',
  '‹»' => 'èˆˆ',
  '‹¼' => 'è•Ž',
  '‹½' => 'éƒ·',
  '‹¾' => 'é�¡',
  '‹¿' => 'éŸ¿',
  '‹À' => 'é¥—',
  '‹Á' => 'é©š',
  '‹Â' => 'ä»°',
  '‹Ã' => 'å‡�',
  '‹Ä' => 'å°­',
  '‹Å' => 'æš�',
  '‹Æ' => 'æ¥­',
  '‹Ç' => 'å±€',
  '‹È' => 'æ›²',
  '‹É' => 'æ¥µ',
  '‹Ê' => 'çŽ‰',
  '‹Ë' => 'æ¡�',
  '‹Ì' => 'ç²�',
  '‹Í' => 'åƒ…',
  '‹Î' => 'å‹¤',
  '‹Ï' => 'å�‡',
  '‹Ð' => 'å·¾',
  '‹Ñ' => 'éŒ¦',
  '‹Ò' => 'æ–¤',
  '‹Ó' => 'æ¬£',
  '‹Ô' => 'æ¬½',
  '‹Õ' => 'ç�´',
  '‹Ö' => 'ç¦�',
  '‹×' => 'ç¦½',
  '‹Ø' => 'ç­‹',
  '‹Ù' => 'ç·Š',
  '‹Ú' => 'èŠ¹',
  '‹Û' => 'è�Œ',
  '‹Ü' => 'è¡¿',
  '‹Ý' => 'è¥Ÿ',
  '‹Þ' => 'è¬¹',
  '‹ß' => 'è¿‘',
  '‹à' => 'é‡‘',
  '‹á' => 'å�Ÿ',
  '‹â' => 'éŠ€',
  '‹ã' => 'ä¹�',
  '‹ä' => 'å€¶',
  '‹å' => 'å�¥',
  '‹æ' => 'åŒº',
  '‹ç' => 'ç‹—',
  '‹è' => 'çŽ–',
  '‹é' => 'çŸ©',
  '‹ê' => 'è‹¦',
  '‹ë' => 'èº¯',
  '‹ì' => 'é§†',
  '‹í' => 'é§ˆ',
  '‹î' => 'é§’',
  '‹ï' => 'å…·',
  '‹ð' => 'æ„š',
  '‹ñ' => 'è™ž',
  '‹ò' => 'å–°',
  '‹ó' => 'ç©º',
  '‹ô' => 'å�¶',
  '‹õ' => 'å¯“',
  '‹ö' => 'é�‡',
  '‹÷' => 'éš…',
  '‹ø' => 'ä¸²',
  '‹ù' => 'æ«›',
  '‹ú' => 'é‡§',
  '‹û' => 'å±‘',
  '‹ü' => 'å±ˆ',
  'Œ@' => 'æŽ˜',
  'ŒA' => 'çªŸ',
  'ŒB' => 'æ²“',
  'ŒC' => 'é�´',
  'ŒD' => 'è½¡',
  'ŒE' => 'çªª',
  'ŒF' => 'ç†Š',
  'ŒG' => 'éšˆ',
  'ŒH' => 'ç²‚',
  'ŒI' => 'æ —',
  'ŒJ' => 'ç¹°',
  'ŒK' => 'æ¡‘',
  'ŒL' => 'é�¬',
  'ŒM' => 'å‹²',
  'ŒN' => 'å�›',
  'ŒO' => 'è–«',
  'ŒP' => 'è¨“',
  'ŒQ' => 'ç¾¤',
  'ŒR' => 'è»�',
  'ŒS' => 'éƒ¡',
  'ŒT' => 'å�¦',
  'ŒU' => 'è¢ˆ',
  'ŒV' => 'ç¥�',
  'ŒW' => 'ä¿‚',
  'ŒX' => 'å‚¾',
  'ŒY' => 'åˆ‘',
  'ŒZ' => 'å…„',
  'Œ[' => 'å•“',
  'Œ\\' => 'åœ­',
  'Œ]' => 'ç�ª',
  'Œ^' => 'åž‹',
  'Œ_' => 'å¥‘',
  'Œ`' => 'å½¢',
  'Œa' => 'å¾„',
  'Œb' => 'æ�µ',
  'Œc' => 'æ…¶',
  'Œd' => 'æ…§',
  'Œe' => 'æ†©',
  'Œf' => 'æŽ²',
  'Œg' => 'æ�º',
  'Œh' => 'æ•¬',
  'Œi' => 'æ™¯',
  'Œj' => 'æ¡‚',
  'Œk' => 'æ¸“',
  'Œl' => 'ç•¦',
  'Œm' => 'ç¨½',
  'Œn' => 'ç³»',
  'Œo' => 'çµŒ',
  'Œp' => 'ç¶™',
  'Œq' => 'ç¹‹',
  'Œr' => 'ç½«',
  'Œs' => 'èŒŽ',
  'Œt' => 'è�Š',
  'Œu' => 'è›�',
  'Œv' => 'è¨ˆ',
  'Œw' => 'è©£',
  'Œx' => 'è­¦',
  'Œy' => 'è»½',
  'Œz' => 'é š',
  'Œ{' => 'é¶�',
  'Œ|' => 'èŠ¸',
  'Œ}' => 'è¿Ž',
  'Œ~' => 'é¯¨',
  'Œ€' => 'åŠ‡',
  'Œ�' => 'æˆŸ',
  'Œ‚' => 'æ’ƒ',
  'Œƒ' => 'æ¿€',
  'Œ„' => 'éš™',
  'Œ…' => 'æ¡�',
  'Œ†' => 'å‚‘',
  'Œ‡' => 'æ¬ ',
  'Œˆ' => 'æ±º',
  'Œ‰' => 'æ½”',
  'ŒŠ' => 'ç©´',
  'Œ‹' => 'çµ�',
  'ŒŒ' => 'è¡€',
  'Œ�' => 'è¨£',
  'ŒŽ' => 'æœˆ',
  'Œ�' => 'ä»¶',
  'Œ�' => 'å€¹',
  'Œ‘' => 'å€¦',
  'Œ’' => 'å�¥',
  'Œ“' => 'å…¼',
  'Œ”' => 'åˆ¸',
  'Œ•' => 'å‰£',
  'Œ–' => 'å–§',
  'Œ—' => 'åœ�',
  'Œ˜' => 'å …',
  'Œ™' => 'å«Œ',
  'Œš' => 'å»º',
  'Œ›' => 'æ†²',
  'Œœ' => 'æ‡¸',
  'Œ�' => 'æ‹³',
  'Œž' => 'æ�²',
  'ŒŸ' => 'æ¤œ',
  'Œ ' => 'æ¨©',
  'Œ¡' => 'ç‰½',
  'Œ¢' => 'çŠ¬',
  'Œ£' => 'çŒ®',
  'Œ¤' => 'ç ”',
  'Œ¥' => 'ç¡¯',
  'Œ¦' => 'çµ¹',
  'Œ§' => 'çœŒ',
  'Œ¨' => 'è‚©',
  'Œ©' => 'è¦‹',
  'Œª' => 'è¬™',
  'Œ«' => 'è³¢',
  'Œ¬' => 'è»’',
  'Œ­' => 'é�£',
  'Œ®' => 'é�µ',
  'Œ¯' => 'é™º',
  'Œ°' => 'é¡•',
  'Œ±' => 'é¨“',
  'Œ²' => 'é¹¸',
  'Œ³' => 'å…ƒ',
  'Œ´' => 'åŽŸ',
  'Œµ' => 'åŽ³',
  'Œ¶' => 'å¹»',
  'Œ·' => 'å¼¦',
  'Œ¸' => 'æ¸›',
  'Œ¹' => 'æº�',
  'Œº' => 'çŽ„',
  'Œ»' => 'ç�¾',
  'Œ¼' => 'çµƒ',
  'Œ½' => 'èˆ·',
  'Œ¾' => 'è¨€',
  'Œ¿' => 'è«º',
  'ŒÀ' => 'é™�',
  'ŒÁ' => 'ä¹Ž',
  'ŒÂ' => 'å€‹',
  'ŒÃ' => 'å�¤',
  'ŒÄ' => 'å‘¼',
  'ŒÅ' => 'å›º',
  'ŒÆ' => 'å§‘',
  'ŒÇ' => 'å­¤',
  'ŒÈ' => 'å·±',
  'ŒÉ' => 'åº«',
  'ŒÊ' => 'å¼§',
  'ŒË' => 'æˆ¸',
  'ŒÌ' => 'æ•…',
  'ŒÍ' => 'æž¯',
  'ŒÎ' => 'æ¹–',
  'ŒÏ' => 'ç‹�',
  'ŒÐ' => 'ç³Š',
  'ŒÑ' => 'è¢´',
  'ŒÒ' => 'è‚¡',
  'ŒÓ' => 'èƒ¡',
  'ŒÔ' => 'è�°',
  'ŒÕ' => 'è™Ž',
  'ŒÖ' => 'èª‡',
  'Œ×' => 'è·¨',
  'ŒØ' => 'éˆ·',
  'ŒÙ' => 'é›‡',
  'ŒÚ' => 'é¡§',
  'ŒÛ' => 'é¼“',
  'ŒÜ' => 'äº”',
  'ŒÝ' => 'äº’',
  'ŒÞ' => 'ä¼�',
  'Œß' => 'å�ˆ',
  'Œà' => 'å‘‰',
  'Œá' => 'å�¾',
  'Œâ' => 'å¨¯',
  'Œã' => 'å¾Œ',
  'Œä' => 'å¾¡',
  'Œå' => 'æ‚Ÿ',
  'Œæ' => 'æ¢§',
  'Œç' => 'æªŽ',
  'Œè' => 'ç‘š',
  'Œé' => 'ç¢�',
  'Œê' => 'èªž',
  'Œë' => 'èª¤',
  'Œì' => 'è­·',
  'Œí' => 'é†�',
  'Œî' => 'ä¹ž',
  'Œï' => 'é¯‰',
  'Œð' => 'äº¤',
  'Œñ' => 'ä½¼',
  'Œò' => 'ä¾¯',
  'Œó' => 'å€™',
  'Œô' => 'å€–',
  'Œõ' => 'å…‰',
  'Œö' => 'å…¬',
  'Œ÷' => 'åŠŸ',
  'Œø' => 'åŠ¹',
  'Œù' => 'å‹¾',
  'Œú' => 'åŽš',
  'Œû' => 'å�£',
  'Œü' => 'å�‘',
  '�@' => 'å�Ž',
  '�A' => 'å–‰',
  '�B' => 'å�‘',
  '�C' => 'åž¢',
  '�D' => 'å¥½',
  '�E' => 'å­”',
  '�F' => 'å­�',
  '�G' => 'å®�',
  '�H' => 'å·¥',
  '�I' => 'å·§',
  '�J' => 'å··',
  '�K' => 'å¹¸',
  '�L' => 'åºƒ',
  '�M' => 'åºš',
  '�N' => 'åº·',
  '�O' => 'å¼˜',
  '�P' => 'æ�’',
  '�Q' => 'æ…Œ',
  '�R' => 'æŠ—',
  '�S' => 'æ‹˜',
  '�T' => 'æŽ§',
  '�U' => 'æ”»',
  '�V' => 'æ˜‚',
  '�W' => 'æ™ƒ',
  '�X' => 'æ›´',
  '�Y' => 'æ�­',
  '�Z' => 'æ ¡',
  '�[' => 'æ¢—',
  '�\\' => 'æ§‹',
  '�]' => 'æ±Ÿ',
  '�^' => 'æ´ª',
  '�_' => 'æµ©',
  '�`' => 'æ¸¯',
  '�a' => 'æº�',
  '�b' => 'ç”²',
  '�c' => 'çš‡',
  '�d' => 'ç¡¬',
  '�e' => 'ç¨¿',
  '�f' => 'ç³ ',
  '�g' => 'ç´…',
  '�h' => 'ç´˜',
  '�i' => 'çµž',
  '�j' => 'ç¶±',
  '�k' => 'è€•',
  '�l' => 'è€ƒ',
  '�m' => 'è‚¯',
  '�n' => 'è‚±',
  '�o' => 'è…”',
  '�p' => 'è†�',
  '�q' => 'èˆª',
  '�r' => 'è�’',
  '�s' => 'è¡Œ',
  '�t' => 'è¡¡',
  '�u' => 'è¬›',
  '�v' => 'è²¢',
  '�w' => 'è³¼',
  '�x' => 'éƒŠ',
  '�y' => 'é…µ',
  '�z' => 'é‰±',
  '�{' => 'ç ¿',
  '�|' => 'é‹¼',
  '�}' => 'é–¤',
  '�~' => 'é™�',
  '�€' => 'é …',
  '��' => 'é¦™',
  '�‚' => 'é«˜',
  '�ƒ' => 'é´»',
  '�„' => 'å‰›',
  '�…' => 'åŠ«',
  '�†' => 'å�·',
  '�‡' => 'å�ˆ',
  '�ˆ' => 'å£•',
  '�‰' => 'æ‹·',
  '�Š' => 'æ¿ ',
  '�‹' => 'è±ª',
  '�Œ' => 'è½Ÿ',
  '��' => 'éº¹',
  '�Ž' => 'å…‹',
  '��' => 'åˆ»',
  '��' => 'å‘Š',
  '�‘' => 'å›½',
  '�’' => 'ç©€',
  '�“' => 'é…·',
  '�”' => 'éµ ',
  '�•' => 'é»’',
  '�–' => 'ç�„',
  '�—' => 'æ¼‰',
  '�˜' => 'è…°',
  '�™' => 'ç”‘',
  '�š' => 'å¿½',
  '�›' => 'æƒš',
  '�œ' => 'éª¨',
  '��' => 'ç‹›',
  '�ž' => 'è¾¼',
  '�Ÿ' => 'æ­¤',
  '� ' => 'é ƒ',
  '�¡' => 'ä»Š',
  '�¢' => 'å›°',
  '�£' => 'å�¤',
  '�¤' => 'å¢¾',
  '�¥' => 'å©š',
  '�¦' => 'æ�¨',
  '�§' => 'æ‡‡',
  '�¨' => 'æ˜�',
  '�©' => 'æ˜†',
  '�ª' => 'æ ¹',
  '�«' => 'æ¢±',
  '�¬' => 'æ··',
  '�­' => 'ç—•',
  '�®' => 'ç´º',
  '�¯' => 'è‰®',
  '�°' => 'é­‚',
  '�±' => 'äº›',
  '�²' => 'ä½�',
  '�³' => 'å�‰',
  '�´' => 'å”†',
  '�µ' => 'åµ¯',
  '�¶' => 'å·¦',
  '�·' => 'å·®',
  '�¸' => 'æŸ»',
  '�¹' => 'æ²™',
  '�º' => 'ç‘³',
  '�»' => 'ç ‚',
  '�¼' => 'è©�',
  '�½' => 'éŽ–',
  '�¾' => 'è£Ÿ',
  '�¿' => 'å��',
  '�À' => 'åº§',
  '�Á' => 'æŒ«',
  '�Â' => 'å‚µ',
  '�Ã' => 'å‚¬',
  '�Ä' => 'å†�',
  '�Å' => 'æœ€',
  '�Æ' => 'å“‰',
  '�Ç' => 'å¡ž',
  '�È' => 'å¦»',
  '�É' => 'å®°',
  '�Ê' => 'å½©',
  '�Ë' => 'æ‰�',
  '�Ì' => 'æŽ¡',
  '�Í' => 'æ ½',
  '�Î' => 'æ­³',
  '�Ï' => 'æ¸ˆ',
  '�Ð' => 'ç�½',
  '�Ñ' => 'é‡‡',
  '�Ò' => 'çŠ€',
  '�Ó' => 'ç •',
  '�Ô' => 'ç ¦',
  '�Õ' => 'ç¥­',
  '�Ö' => 'æ–Ž',
  '�×' => 'ç´°',
  '�Ø' => 'è�œ',
  '�Ù' => 'è£�',
  '�Ú' => 'è¼‰',
  '�Û' => 'éš›',
  '�Ü' => 'å‰¤',
  '�Ý' => 'åœ¨',
  '�Þ' => 'æ��',
  '�ß' => 'ç½ª',
  '�à' => 'è²¡',
  '�á' => 'å†´',
  '�â' => 'å�‚',
  '�ã' => 'é˜ª',
  '�ä' => 'å º',
  '�å' => 'æ¦Š',
  '�æ' => 'è‚´',
  '�ç' => 'å’²',
  '�è' => 'å´Ž',
  '�é' => 'åŸ¼',
  '�ê' => 'ç¢•',
  '�ë' => 'é·º',
  '�ì' => 'ä½œ',
  '�í' => 'å‰Š',
  '�î' => 'å’‹',
  '�ï' => 'æ�¾',
  '�ð' => 'æ˜¨',
  '�ñ' => 'æœ”',
  '�ò' => 'æŸµ',
  '�ó' => 'çª„',
  '�ô' => 'ç­–',
  '�õ' => 'ç´¢',
  '�ö' => 'éŒ¯',
  '�÷' => 'æ¡œ',
  '�ø' => 'é®­',
  '�ù' => 'ç¬¹',
  '�ú' => 'åŒ™',
  '�û' => 'å†Š',
  '�ü' => 'åˆ·',
  'Ž@' => 'å¯Ÿ',
  'ŽA' => 'æ‹¶',
  'ŽB' => 'æ’®',
  'ŽC' => 'æ“¦',
  'ŽD' => 'æœ­',
  'ŽE' => 'æ®º',
  'ŽF' => 'è–©',
  'ŽG' => 'é›‘',
  'ŽH' => 'çš�',
  'ŽI' => 'é¯–',
  'ŽJ' => 'æ�Œ',
  'ŽK' => 'éŒ†',
  'ŽL' => 'é®«',
  'ŽM' => 'çš¿',
  'ŽN' => 'æ™’',
  'ŽO' => 'ä¸‰',
  'ŽP' => 'å‚˜',
  'ŽQ' => 'å�‚',
  'ŽR' => 'å±±',
  'ŽS' => 'æƒ¨',
  'ŽT' => 'æ’’',
  'ŽU' => 'æ•£',
  'ŽV' => 'æ¡Ÿ',
  'ŽW' => 'ç‡¦',
  'ŽX' => 'ç�Š',
  'ŽY' => 'ç”£',
  'ŽZ' => 'ç®—',
  'Ž[' => 'çº‚',
  'Ž\\' => 'èš•',
  'Ž]' => 'è®ƒ',
  'Ž^' => 'è³›',
  'Ž_' => 'é…¸',
  'Ž`' => 'é¤�',
  'Ža' => 'æ–¬',
  'Žb' => 'æš«',
  'Žc' => 'æ®‹',
  'Žd' => 'ä»•',
  'Že' => 'ä»”',
  'Žf' => 'ä¼º',
  'Žg' => 'ä½¿',
  'Žh' => 'åˆº',
  'Ži' => 'å�¸',
  'Žj' => 'å�²',
  'Žk' => 'å—£',
  'Žl' => 'å››',
  'Žm' => 'å£«',
  'Žn' => 'å§‹',
  'Žo' => 'å§‰',
  'Žp' => 'å§¿',
  'Žq' => 'å­�',
  'Žr' => 'å±�',
  'Žs' => 'å¸‚',
  'Žt' => 'å¸«',
  'Žu' => 'å¿—',
  'Žv' => 'æ€�',
  'Žw' => 'æŒ‡',
  'Žx' => 'æ”¯',
  'Žy' => 'å­œ',
  'Žz' => 'æ–¯',
  'Ž{' => 'æ–½',
  'Ž|' => 'æ—¨',
  'Ž}' => 'æž�',
  'Ž~' => 'æ­¢',
  'Ž€' => 'æ­»',
  'Ž�' => 'æ°�',
  'Ž‚' => 'ç�…',
  'Žƒ' => 'ç¥‰',
  'Ž„' => 'ç§�',
  'Ž…' => 'ç³¸',
  'Ž†' => 'ç´™',
  'Ž‡' => 'ç´«',
  'Žˆ' => 'è‚¢',
  'Ž‰' => 'è„‚',
  'ŽŠ' => 'è‡³',
  'Ž‹' => 'è¦–',
  'ŽŒ' => 'è©ž',
  'Ž�' => 'è©©',
  'ŽŽ' => 'è©¦',
  'Ž�' => 'èªŒ',
  'Ž�' => 'è«®',
  'Ž‘' => 'è³‡',
  'Ž’' => 'è³œ',
  'Ž“' => 'é›Œ',
  'Ž”' => 'é£¼',
  'Ž•' => 'æ­¯',
  'Ž–' => 'äº‹',
  'Ž—' => 'ä¼¼',
  'Ž˜' => 'ä¾�',
  'Ž™' => 'å…�',
  'Žš' => 'å­—',
  'Ž›' => 'å¯º',
  'Žœ' => 'æ…ˆ',
  'Ž�' => 'æŒ�',
  'Žž' => 'æ™‚',
  'ŽŸ' => 'æ¬¡',
  'Ž ' => 'æ»‹',
  'Ž¡' => 'æ²»',
  'Ž¢' => 'çˆ¾',
  'Ž£' => 'ç’½',
  'Ž¤' => 'ç—”',
  'Ž¥' => 'ç£�',
  'Ž¦' => 'ç¤º',
  'Ž§' => 'è€Œ',
  'Ž¨' => 'è€³',
  'Ž©' => 'è‡ª',
  'Žª' => 'è’”',
  'Ž«' => 'è¾ž',
  'Ž¬' => 'æ±�',
  'Ž­' => 'é¹¿',
  'Ž®' => 'å¼�',
  'Ž¯' => 'è­˜',
  'Ž°' => 'é´«',
  'Ž±' => 'ç«º',
  'Ž²' => 'è»¸',
  'Ž³' => 'å®�',
  'Ž´' => 'é›«',
  'Žµ' => 'ä¸ƒ',
  'Ž¶' => 'å�±',
  'Ž·' => 'åŸ·',
  'Ž¸' => 'å¤±',
  'Ž¹' => 'å«‰',
  'Žº' => 'å®¤',
  'Ž»' => 'æ‚‰',
  'Ž¼' => 'æ¹¿',
  'Ž½' => 'æ¼†',
  'Ž¾' => 'ç–¾',
  'Ž¿' => 'è³ª',
  'ŽÀ' => 'å®Ÿ',
  'ŽÁ' => 'è”€',
  'ŽÂ' => 'ç¯ ',
  'ŽÃ' => 'å�²',
  'ŽÄ' => 'æŸ´',
  'ŽÅ' => 'èŠ�',
  'ŽÆ' => 'å±¡',
  'ŽÇ' => 'è•Š',
  'ŽÈ' => 'ç¸ž',
  'ŽÉ' => 'èˆŽ',
  'ŽÊ' => 'å†™',
  'ŽË' => 'å°„',
  'ŽÌ' => 'æ�¨',
  'ŽÍ' => 'èµ¦',
  'ŽÎ' => 'æ–œ',
  'ŽÏ' => 'ç…®',
  'ŽÐ' => 'ç¤¾',
  'ŽÑ' => 'ç´—',
  'ŽÒ' => 'è€…',
  'ŽÓ' => 'è¬�',
  'ŽÔ' => 'è»Š',
  'ŽÕ' => 'é�®',
  'ŽÖ' => 'è›‡',
  'Ž×' => 'é‚ª',
  'ŽØ' => 'å€Ÿ',
  'ŽÙ' => 'å‹º',
  'ŽÚ' => 'å°º',
  'ŽÛ' => 'æ�“',
  'ŽÜ' => 'ç�¼',
  'ŽÝ' => 'çˆµ',
  'ŽÞ' => 'é…Œ',
  'Žß' => 'é‡ˆ',
  'Žà' => 'éŒ«',
  'Žá' => 'è‹¥',
  'Žâ' => 'å¯‚',
  'Žã' => 'å¼±',
  'Žä' => 'æƒ¹',
  'Žå' => 'ä¸»',
  'Žæ' => 'å�–',
  'Žç' => 'å®ˆ',
  'Žè' => 'æ‰‹',
  'Žé' => 'æœ±',
  'Žê' => 'æ®Š',
  'Žë' => 'ç‹©',
  'Žì' => 'ç� ',
  'Ží' => 'ç¨®',
  'Žî' => 'è…«',
  'Žï' => 'è¶£',
  'Žð' => 'é…’',
  'Žñ' => 'é¦–',
  'Žò' => 'å„’',
  'Žó' => 'å�—',
  'Žô' => 'å‘ª',
  'Žõ' => 'å¯¿',
  'Žö' => 'æŽˆ',
  'Ž÷' => 'æ¨¹',
  'Žø' => 'ç¶¬',
  'Žù' => 'éœ€',
  'Žú' => 'å›š',
  'Žû' => 'å�Ž',
  'Žü' => 'å‘¨',
  '�@' => 'å®—',
  '�A' => 'å°±',
  '�B' => 'å·ž',
  '�C' => 'ä¿®',
  '�D' => 'æ„�',
  '�E' => 'æ‹¾',
  '�F' => 'æ´²',
  '�G' => 'ç§€',
  '�H' => 'ç§‹',
  '�I' => 'çµ‚',
  '�J' => 'ç¹�',
  '�K' => 'ç¿’',
  '�L' => 'è‡­',
  '�M' => 'èˆŸ',
  '�N' => 'è’�',
  '�O' => 'è¡†',
  '�P' => 'è¥²',
  '�Q' => 'è®�',
  '�R' => 'è¹´',
  '�S' => 'è¼¯',
  '�T' => 'é€±',
  '�U' => 'é…‹',
  '�V' => 'é…¬',
  '�W' => 'é›†',
  '�X' => 'é†œ',
  '�Y' => 'ä»€',
  '�Z' => 'ä½�',
  '�[' => 'å……',
  '�\\' => 'å��',
  '�]' => 'å¾“',
  '�^' => 'æˆŽ',
  '�_' => 'æŸ”',
  '�`' => 'æ±�',
  '�a' => 'æ¸‹',
  '�b' => 'ç�£',
  '�c' => 'ç¸¦',
  '�d' => 'é‡�',
  '�e' => 'éŠƒ',
  '�f' => 'å�”',
  '�g' => 'å¤™',
  '�h' => 'å®¿',
  '�i' => 'æ·‘',
  '�j' => 'ç¥�',
  '�k' => 'ç¸®',
  '�l' => 'ç²›',
  '�m' => 'å¡¾',
  '�n' => 'ç†Ÿ',
  '�o' => 'å‡º',
  '�p' => 'è¡“',
  '�q' => 'è¿°',
  '�r' => 'ä¿Š',
  '�s' => 'å³»',
  '�t' => 'æ˜¥',
  '�u' => 'çž¬',
  '�v' => 'ç«£',
  '�w' => 'èˆœ',
  '�x' => 'é§¿',
  '�y' => 'å‡†',
  '�z' => 'å¾ª',
  '�{' => 'æ—¬',
  '�|' => 'æ¥¯',
  '�}' => 'æ®‰',
  '�~' => 'æ·³',
  '�€' => 'æº–',
  '��' => 'æ½¤',
  '�‚' => 'ç›¾',
  '�ƒ' => 'ç´”',
  '�„' => 'å·¡',
  '�…' => 'é�µ',
  '�†' => 'é†‡',
  '�‡' => 'é †',
  '�ˆ' => 'å‡¦',
  '�‰' => 'åˆ�',
  '�Š' => 'æ‰€',
  '�‹' => 'æš‘',
  '�Œ' => 'æ›™',
  '��' => 'æ¸š',
  '�Ž' => 'åº¶',
  '��' => 'ç·’',
  '��' => 'ç½²',
  '�‘' => 'æ›¸',
  '�’' => 'è–¯',
  '�“' => 'è—·',
  '�”' => 'è«¸',
  '�•' => 'åŠ©',
  '�–' => 'å�™',
  '�—' => 'å¥³',
  '�˜' => 'åº�',
  '�™' => 'å¾�',
  '�š' => 'æ�•',
  '�›' => 'é‹¤',
  '�œ' => 'é™¤',
  '��' => 'å‚·',
  '�ž' => 'å„Ÿ',
  '�Ÿ' => 'å‹�',
  '� ' => 'åŒ ',
  '�¡' => 'å�‡',
  '�¢' => 'å�¬',
  '�£' => 'å“¨',
  '�¤' => 'å•†',
  '�¥' => 'å”±',
  '�¦' => 'å˜—',
  '�§' => 'å¥¨',
  '�¨' => 'å¦¾',
  '�©' => 'å¨¼',
  '�ª' => 'å®µ',
  '�«' => 'å°†',
  '�¬' => 'å°�',
  '�­' => 'å°‘',
  '�®' => 'å°š',
  '�¯' => 'åº„',
  '�°' => 'åºŠ',
  '�±' => 'å» ',
  '�²' => 'å½°',
  '�³' => 'æ‰¿',
  '�´' => 'æŠ„',
  '�µ' => 'æ‹›',
  '�¶' => 'æŽŒ',
  '�·' => 'æ�·',
  '�¸' => 'æ˜‡',
  '�¹' => 'æ˜Œ',
  '�º' => 'æ˜­',
  '�»' => 'æ™¶',
  '�¼' => 'æ�¾',
  '�½' => 'æ¢¢',
  '�¾' => 'æ¨Ÿ',
  '�¿' => 'æ¨µ',
  '�À' => 'æ²¼',
  '�Á' => 'æ¶ˆ',
  '�Â' => 'æ¸‰',
  '�Ã' => 'æ¹˜',
  '�Ä' => 'ç„¼',
  '�Å' => 'ç„¦',
  '�Æ' => 'ç…§',
  '�Ç' => 'ç—‡',
  '�È' => 'çœ�',
  '�É' => 'ç¡�',
  '�Ê' => 'ç¤�',
  '�Ë' => 'ç¥¥',
  '�Ì' => 'ç§°',
  '�Í' => 'ç« ',
  '�Î' => 'ç¬‘',
  '�Ï' => 'ç²§',
  '�Ð' => 'ç´¹',
  '�Ñ' => 'è‚–',
  '�Ò' => 'è�–',
  '�Ó' => 'è’‹',
  '�Ô' => 'è•‰',
  '�Õ' => 'è¡�',
  '�Ö' => 'è£³',
  '�×' => 'è¨Ÿ',
  '�Ø' => 'è¨¼',
  '�Ù' => 'è©”',
  '�Ú' => 'è©³',
  '�Û' => 'è±¡',
  '�Ü' => 'è³ž',
  '�Ý' => 'é†¤',
  '�Þ' => 'é‰¦',
  '�ß' => 'é�¾',
  '�à' => 'é�˜',
  '�á' => 'éšœ',
  '�â' => 'éž˜',
  '�ã' => 'ä¸Š',
  '�ä' => 'ä¸ˆ',
  '�å' => 'ä¸ž',
  '�æ' => 'ä¹—',
  '�ç' => 'å†—',
  '�è' => 'å‰°',
  '�é' => 'åŸŽ',
  '�ê' => 'å ´',
  '�ë' => 'å£Œ',
  '�ì' => 'å¬¢',
  '�í' => 'å¸¸',
  '�î' => 'æƒ…',
  '�ï' => 'æ“¾',
  '�ð' => 'æ�¡',
  '�ñ' => 'æ�–',
  '�ò' => 'æµ„',
  '�ó' => 'çŠ¶',
  '�ô' => 'ç•³',
  '�õ' => 'ç©£',
  '�ö' => 'è’¸',
  '�÷' => 'è­²',
  '�ø' => 'é†¸',
  '�ù' => 'éŒ ',
  '�ú' => 'å˜±',
  '�û' => 'åŸ´',
  '�ü' => 'é£¾',
  '�@' => 'æ‹­',
  '�A' => 'æ¤�',
  '�B' => 'æ®–',
  '�C' => 'ç‡­',
  '�D' => 'ç¹”',
  '�E' => 'è�·',
  '�F' => 'è‰²',
  '�G' => 'è§¦',
  '�H' => 'é£Ÿ',
  '�I' => 'è�•',
  '�J' => 'è¾±',
  '�K' => 'å°»',
  '�L' => 'ä¼¸',
  '�M' => 'ä¿¡',
  '�N' => 'ä¾µ',
  '�O' => 'å”‡',
  '�P' => 'å¨ ',
  '�Q' => 'å¯�',
  '�R' => 'å¯©',
  '�S' => 'å¿ƒ',
  '�T' => 'æ…Ž',
  '�U' => 'æŒ¯',
  '�V' => 'æ–°',
  '�W' => 'æ™‹',
  '�X' => 'æ£®',
  '�Y' => 'æ¦›',
  '�Z' => 'æµ¸',
  '�[' => 'æ·±',
  '�\\' => 'ç”³',
  '�]' => 'ç–¹',
  '�^' => 'çœŸ',
  '�_' => 'ç¥ž',
  '�`' => 'ç§¦',
  '�a' => 'ç´³',
  '�b' => 'è‡£',
  '�c' => 'èŠ¯',
  '�d' => 'è–ª',
  '�e' => 'è¦ª',
  '�f' => 'è¨º',
  '�g' => 'èº«',
  '�h' => 'è¾›',
  '�i' => 'é€²',
  '�j' => 'é‡�',
  '�k' => 'éœ‡',
  '�l' => 'äºº',
  '�m' => 'ä»�',
  '�n' => 'åˆƒ',
  '�o' => 'å¡µ',
  '�p' => 'å£¬',
  '�q' => 'å°‹',
  '�r' => 'ç”š',
  '�s' => 'å°½',
  '�t' => 'è…Ž',
  '�u' => 'è¨Š',
  '�v' => 'è¿…',
  '�w' => 'é™£',
  '�x' => 'é�­',
  '�y' => 'ç¬¥',
  '�z' => 'è«�',
  '�{' => 'é ˆ',
  '�|' => 'é…¢',
  '�}' => 'å›³',
  '�~' => 'åŽ¨',
  '�€' => 'é€—',
  '��' => 'å�¹',
  '�‚' => 'åž‚',
  '�ƒ' => 'å¸¥',
  '�„' => 'æŽ¨',
  '�…' => 'æ°´',
  '�†' => 'ç‚Š',
  '�‡' => 'ç�¡',
  '�ˆ' => 'ç²‹',
  '�‰' => 'ç¿ ',
  '�Š' => 'è¡°',
  '�‹' => 'é�‚',
  '�Œ' => 'é…”',
  '��' => 'éŒ�',
  '�Ž' => 'éŒ˜',
  '��' => 'éš�',
  '��' => 'ç‘ž',
  '�‘' => 'é«„',
  '�’' => 'å´‡',
  '�“' => 'åµ©',
  '�”' => 'æ•°',
  '�•' => 'æž¢',
  '�–' => 'è¶¨',
  '�—' => 'é››',
  '�˜' => 'æ�®',
  '�™' => 'æ�‰',
  '�š' => 'æ¤™',
  '�›' => 'è�…',
  '�œ' => 'é —',
  '��' => 'é›€',
  '�ž' => 'è£¾',
  '�Ÿ' => 'æ¾„',
  '� ' => 'æ‘º',
  '�¡' => 'å¯¸',
  '�¢' => 'ä¸–',
  '�£' => 'ç€¬',
  '�¤' => 'ç•�',
  '�¥' => 'æ˜¯',
  '�¦' => 'å‡„',
  '�§' => 'åˆ¶',
  '�¨' => 'å‹¢',
  '�©' => 'å§“',
  '�ª' => 'å¾�',
  '�«' => 'æ€§',
  '�¬' => 'æˆ�',
  '�­' => 'æ”¿',
  '�®' => 'æ•´',
  '�¯' => 'æ˜Ÿ',
  '�°' => 'æ™´',
  '�±' => 'æ£²',
  '�²' => 'æ –',
  '�³' => 'æ­£',
  '�´' => 'æ¸…',
  '�µ' => 'ç‰²',
  '�¶' => 'ç”Ÿ',
  '�·' => 'ç››',
  '�¸' => 'ç²¾',
  '�¹' => 'è�–',
  '�º' => 'å£°',
  '�»' => 'è£½',
  '�¼' => 'è¥¿',
  '�½' => 'èª ',
  '�¾' => 'èª“',
  '�¿' => 'è«‹',
  '�À' => 'é€�',
  '�Á' => 'é†’',
  '�Â' => 'é�’',
  '�Ã' => 'é�™',
  '�Ä' => 'æ–‰',
  '�Å' => 'ç¨Ž',
  '�Æ' => 'è„†',
  '�Ç' => 'éš»',
  '�È' => 'å¸­',
  '�É' => 'æƒœ',
  '�Ê' => 'æˆš',
  '�Ë' => 'æ–¥',
  '�Ì' => 'æ˜”',
  '�Í' => 'æž�',
  '�Î' => 'çŸ³',
  '�Ï' => 'ç©�',
  '�Ð' => 'ç±�',
  '�Ñ' => 'ç¸¾',
  '�Ò' => 'è„Š',
  '�Ó' => 'è²¬',
  '�Ô' => 'èµ¤',
  '�Õ' => 'è·¡',
  '�Ö' => 'è¹Ÿ',
  '�×' => 'ç¢©',
  '�Ø' => 'åˆ‡',
  '�Ù' => 'æ‹™',
  '�Ú' => 'æŽ¥',
  '�Û' => 'æ‘‚',
  '�Ü' => 'æŠ˜',
  '�Ý' => 'è¨­',
  '�Þ' => 'çªƒ',
  '�ß' => 'ç¯€',
  '�à' => 'èª¬',
  '�á' => 'é›ª',
  '�â' => 'çµ¶',
  '�ã' => 'èˆŒ',
  '�ä' => 'è�‰',
  '�å' => 'ä»™',
  '�æ' => 'å…ˆ',
  '�ç' => 'å�ƒ',
  '�è' => 'å� ',
  '�é' => 'å®£',
  '�ê' => 'å°‚',
  '�ë' => 'å°–',
  '�ì' => 'å·�',
  '�í' => 'æˆ¦',
  '�î' => 'æ‰‡',
  '�ï' => 'æ’°',
  '�ð' => 'æ “',
  '�ñ' => 'æ ´',
  '�ò' => 'æ³‰',
  '�ó' => 'æµ…',
  '�ô' => 'æ´—',
  '�õ' => 'æŸ“',
  '�ö' => 'æ½œ',
  '�÷' => 'ç…Ž',
  '�ø' => 'ç…½',
  '�ù' => 'æ—‹',
  '�ú' => 'ç©¿',
  '�û' => 'ç®­',
  '�ü' => 'ç·š',
  '‘@' => 'ç¹Š',
  '‘A' => 'ç¾¨',
  '‘B' => 'è…º',
  '‘C' => 'èˆ›',
  '‘D' => 'èˆ¹',
  '‘E' => 'è–¦',
  '‘F' => 'è©®',
  '‘G' => 'è³Ž',
  '‘H' => 'è·µ',
  '‘I' => 'é�¸',
  '‘J' => 'é�·',
  '‘K' => 'éŠ­',
  '‘L' => 'éŠ‘',
  '‘M' => 'é–ƒ',
  '‘N' => 'é®®',
  '‘O' => 'å‰�',
  '‘P' => 'å–„',
  '‘Q' => 'æ¼¸',
  '‘R' => 'ç„¶',
  '‘S' => 'å…¨',
  '‘T' => 'ç¦…',
  '‘U' => 'ç¹•',
  '‘V' => 'è†³',
  '‘W' => 'ç³Ž',
  '‘X' => 'å™Œ',
  '‘Y' => 'å¡‘',
  '‘Z' => 'å²¨',
  '‘[' => 'æŽª',
  '‘\\' => 'æ›¾',
  '‘]' => 'æ›½',
  '‘^' => 'æ¥š',
  '‘_' => 'ç‹™',
  '‘`' => 'ç–�',
  '‘a' => 'ç–Ž',
  '‘b' => 'ç¤Ž',
  '‘c' => 'ç¥–',
  '‘d' => 'ç§Ÿ',
  '‘e' => 'ç²—',
  '‘f' => 'ç´ ',
  '‘g' => 'çµ„',
  '‘h' => 'è˜‡',
  '‘i' => 'è¨´',
  '‘j' => 'é˜»',
  '‘k' => 'é�¡',
  '‘l' => 'é¼ ',
  '‘m' => 'åƒ§',
  '‘n' => 'å‰µ',
  '‘o' => 'å�Œ',
  '‘p' => 'å�¢',
  '‘q' => 'å€‰',
  '‘r' => 'å–ª',
  '‘s' => 'å£®',
  '‘t' => 'å¥�',
  '‘u' => 'çˆ½',
  '‘v' => 'å®‹',
  '‘w' => 'å±¤',
  '‘x' => 'åŒ�',
  '‘y' => 'æƒ£',
  '‘z' => 'æƒ³',
  '‘{' => 'æ�œ',
  '‘|' => 'æŽƒ',
  '‘}' => 'æŒ¿',
  '‘~' => 'æŽ»',
  '‘€' => 'æ“�',
  '‘�' => 'æ—©',
  '‘‚' => 'æ›¹',
  '‘ƒ' => 'å·£',
  '‘„' => 'æ§�',
  '‘…' => 'æ§½',
  '‘†' => 'æ¼•',
  '‘‡' => 'ç‡¥',
  '‘ˆ' => 'äº‰',
  '‘‰' => 'ç—©',
  '‘Š' => 'ç›¸',
  '‘‹' => 'çª“',
  '‘Œ' => 'ç³Ÿ',
  '‘�' => 'ç·�',
  '‘Ž' => 'ç¶œ',
  '‘�' => 'è�¡',
  '‘�' => 'è�‰',
  '‘‘' => 'è�˜',
  '‘’' => 'è‘¬',
  '‘“' => 'è’¼',
  '‘”' => 'è—»',
  '‘•' => 'è£…',
  '‘–' => 'èµ°',
  '‘—' => 'é€�',
  '‘˜' => 'é�­',
  '‘™' => 'éŽ—',
  '‘š' => 'éœœ',
  '‘›' => 'é¨’',
  '‘œ' => 'åƒ�',
  '‘�' => 'å¢—',
  '‘ž' => 'æ†Ž',
  '‘Ÿ' => 'è‡“',
  '‘ ' => 'è”µ',
  '‘¡' => 'è´ˆ',
  '‘¢' => 'é€ ',
  '‘£' => 'ä¿ƒ',
  '‘¤' => 'å�´',
  '‘¥' => 'å‰‡',
  '‘¦' => 'å�³',
  '‘§' => 'æ�¯',
  '‘¨' => 'æ�‰',
  '‘©' => 'æ�Ÿ',
  '‘ª' => 'æ¸¬',
  '‘«' => 'è¶³',
  '‘¬' => 'é€Ÿ',
  '‘­' => 'ä¿—',
  '‘®' => 'å±ž',
  '‘¯' => 'è³Š',
  '‘°' => 'æ—�',
  '‘±' => 'ç¶š',
  '‘²' => 'å�’',
  '‘³' => 'è¢–',
  '‘´' => 'å…¶',
  '‘µ' => 'æ�ƒ',
  '‘¶' => 'å­˜',
  '‘·' => 'å­«',
  '‘¸' => 'å°Š',
  '‘¹' => 'æ��',
  '‘º' => 'æ�‘',
  '‘»' => 'é�œ',
  '‘¼' => 'ä»–',
  '‘½' => 'å¤š',
  '‘¾' => 'å¤ª',
  '‘¿' => 'æ±°',
  '‘À' => 'è©‘',
  '‘Á' => 'å”¾',
  '‘Â' => 'å •',
  '‘Ã' => 'å¦¥',
  '‘Ä' => 'æƒ°',
  '‘Å' => 'æ‰“',
  '‘Æ' => 'æŸ�',
  '‘Ç' => 'èˆµ',
  '‘È' => 'æ¥•',
  '‘É' => 'é™€',
  '‘Ê' => 'é§„',
  '‘Ë' => 'é¨¨',
  '‘Ì' => 'ä½“',
  '‘Í' => 'å †',
  '‘Î' => 'å¯¾',
  '‘Ï' => 'è€�',
  '‘Ð' => 'å²±',
  '‘Ñ' => 'å¸¯',
  '‘Ò' => 'å¾…',
  '‘Ó' => 'æ€ ',
  '‘Ô' => 'æ…‹',
  '‘Õ' => 'æˆ´',
  '‘Ö' => 'æ›¿',
  '‘×' => 'æ³°',
  '‘Ø' => 'æ»ž',
  '‘Ù' => 'èƒŽ',
  '‘Ú' => 'è…¿',
  '‘Û' => 'è‹”',
  '‘Ü' => 'è¢‹',
  '‘Ý' => 'è²¸',
  '‘Þ' => 'é€€',
  '‘ß' => 'é€®',
  '‘à' => 'éšŠ',
  '‘á' => 'é»›',
  '‘â' => 'é¯›',
  '‘ã' => 'ä»£',
  '‘ä' => 'å�°',
  '‘å' => 'å¤§',
  '‘æ' => 'ç¬¬',
  '‘ç' => 'é†�',
  '‘è' => 'é¡Œ',
  '‘é' => 'é·¹',
  '‘ê' => 'æ»�',
  '‘ë' => 'ç€§',
  '‘ì' => 'å�“',
  '‘í' => 'å•„',
  '‘î' => 'å®…',
  '‘ï' => 'æ‰˜',
  '‘ð' => 'æŠž',
  '‘ñ' => 'æ‹“',
  '‘ò' => 'æ²¢',
  '‘ó' => 'æ¿¯',
  '‘ô' => 'ç�¢',
  '‘õ' => 'è¨—',
  '‘ö' => 'é�¸',
  '‘÷' => 'æ¿�',
  '‘ø' => 'è«¾',
  '‘ù' => 'èŒ¸',
  '‘ú' => 'å‡§',
  '‘û' => 'è›¸',
  '‘ü' => 'å�ª',
  '’@' => 'å�©',
  '’A' => 'ä½†',
  '’B' => 'é�”',
  '’C' => 'è¾°',
  '’D' => 'å¥ª',
  '’E' => 'è„±',
  '’F' => 'å·½',
  '’G' => 'ç«ª',
  '’H' => 'è¾¿',
  '’I' => 'æ£š',
  '’J' => 'è°·',
  '’K' => 'ç‹¸',
  '’L' => 'é±ˆ',
  '’M' => 'æ¨½',
  '’N' => 'èª°',
  '’O' => 'ä¸¹',
  '’P' => 'å�˜',
  '’Q' => 'å˜†',
  '’R' => 'å�¦',
  '’S' => 'æ‹…',
  '’T' => 'æŽ¢',
  '’U' => 'æ—¦',
  '’V' => 'æ­Ž',
  '’W' => 'æ·¡',
  '’X' => 'æ¹›',
  '’Y' => 'ç‚­',
  '’Z' => 'çŸ­',
  '’[' => 'ç«¯',
  '’\\' => 'ç®ª',
  '’]' => 'ç¶»',
  '’^' => 'è€½',
  '’_' => 'èƒ†',
  '’`' => 'è›‹',
  '’a' => 'èª•',
  '’b' => 'é�›',
  '’c' => 'å›£',
  '’d' => 'å£‡',
  '’e' => 'å¼¾',
  '’f' => 'æ–­',
  '’g' => 'æš–',
  '’h' => 'æª€',
  '’i' => 'æ®µ',
  '’j' => 'ç”·',
  '’k' => 'è«‡',
  '’l' => 'å€¤',
  '’m' => 'çŸ¥',
  '’n' => 'åœ°',
  '’o' => 'å¼›',
  '’p' => 'æ�¥',
  '’q' => 'æ™º',
  '’r' => 'æ± ',
  '’s' => 'ç—´',
  '’t' => 'ç¨š',
  '’u' => 'ç½®',
  '’v' => 'è‡´',
  '’w' => 'èœ˜',
  '’x' => 'é�…',
  '’y' => 'é¦³',
  '’z' => 'ç¯‰',
  '’{' => 'ç•œ',
  '’|' => 'ç«¹',
  '’}' => 'ç­‘',
  '’~' => 'è“„',
  '’€' => 'é€�',
  '’�' => 'ç§©',
  '’‚' => 'çª’',
  '’ƒ' => 'èŒ¶',
  '’„' => 'å«¡',
  '’…' => 'ç�€',
  '’†' => 'ä¸­',
  '’‡' => 'ä»²',
  '’ˆ' => 'å®™',
  '’‰' => 'å¿ ',
  '’Š' => 'æŠ½',
  '’‹' => 'æ˜¼',
  '’Œ' => 'æŸ±',
  '’�' => 'æ³¨',
  '’Ž' => 'è™«',
  '’�' => 'è¡·',
  '’�' => 'è¨»',
  '’‘' => 'é…Ž',
  '’’' => 'é‹³',
  '’“' => 'é§�',
  '’”' => 'æ¨—',
  '’•' => 'ç€¦',
  '’–' => 'çŒª',
  '’—' => 'è‹§',
  '’˜' => 'è‘—',
  '’™' => 'è²¯',
  '’š' => 'ä¸�',
  '’›' => 'å…†',
  '’œ' => 'å‡‹',
  '’�' => 'å–‹',
  '’ž' => 'å¯µ',
  '’Ÿ' => 'å¸–',
  '’ ' => 'å¸³',
  '’¡' => 'åº�',
  '’¢' => 'å¼”',
  '’£' => 'å¼µ',
  '’¤' => 'å½«',
  '’¥' => 'å¾´',
  '’¦' => 'æ‡²',
  '’§' => 'æŒ‘',
  '’¨' => 'æš¢',
  '’©' => 'æœ�',
  '’ª' => 'æ½®',
  '’«' => 'ç‰’',
  '’¬' => 'ç”º',
  '’­' => 'çœº',
  '’®' => 'è�´',
  '’¯' => 'è„¹',
  '’°' => 'è…¸',
  '’±' => 'è�¶',
  '’²' => 'èª¿',
  '’³' => 'è«œ',
  '’´' => 'è¶…',
  '’µ' => 'è·³',
  '’¶' => 'éŠš',
  '’·' => 'é•·',
  '’¸' => 'é ‚',
  '’¹' => 'é³¥',
  '’º' => 'å‹…',
  '’»' => 'æ�—',
  '’¼' => 'ç›´',
  '’½' => 'æœ•',
  '’¾' => 'æ²ˆ',
  '’¿' => 'ç��',
  '’À' => 'è³ƒ',
  '’Á' => 'éŽ®',
  '’Â' => 'é™³',
  '’Ã' => 'æ´¥',
  '’Ä' => 'å¢œ',
  '’Å' => 'æ¤Ž',
  '’Æ' => 'æ§Œ',
  '’Ç' => 'è¿½',
  '’È' => 'éŽš',
  '’É' => 'ç—›',
  '’Ê' => 'é€š',
  '’Ë' => 'å¡š',
  '’Ì' => 'æ ‚',
  '’Í' => 'æŽ´',
  '’Î' => 'æ§»',
  '’Ï' => 'ä½ƒ',
  '’Ð' => 'æ¼¬',
  '’Ñ' => 'æŸ˜',
  '’Ò' => 'è¾»',
  '’Ó' => 'è”¦',
  '’Ô' => 'ç¶´',
  '’Õ' => 'é�”',
  '’Ö' => 'æ¤¿',
  '’×' => 'æ½°',
  '’Ø' => 'å�ª',
  '’Ù' => 'å£·',
  '’Ú' => 'å¬¬',
  '’Û' => 'ç´¬',
  '’Ü' => 'çˆª',
  '’Ý' => 'å�Š',
  '’Þ' => 'é‡£',
  '’ß' => 'é¶´',
  '’à' => 'äº­',
  '’á' => 'ä½Ž',
  '’â' => 'å�œ',
  '’ã' => 'å�µ',
  '’ä' => 'å‰ƒ',
  '’å' => 'è²ž',
  '’æ' => 'å‘ˆ',
  '’ç' => 'å ¤',
  '’è' => 'å®š',
  '’é' => 'å¸�',
  '’ê' => 'åº•',
  '’ë' => 'åº­',
  '’ì' => 'å»·',
  '’í' => 'å¼Ÿ',
  '’î' => 'æ‚Œ',
  '’ï' => 'æŠµ',
  '’ð' => 'æŒº',
  '’ñ' => 'æ��',
  '’ò' => 'æ¢¯',
  '’ó' => 'æ±€',
  '’ô' => 'ç¢‡',
  '’õ' => 'ç¦Ž',
  '’ö' => 'ç¨‹',
  '’÷' => 'ç· ',
  '’ø' => 'è‰‡',
  '’ù' => 'è¨‚',
  '’ú' => 'è«¦',
  '’û' => 'è¹„',
  '’ü' => 'é€“',
  '“@' => 'é‚¸',
  '“A' => 'é„­',
  '“B' => 'é‡˜',
  '“C' => 'é¼Ž',
  '“D' => 'æ³¥',
  '“E' => 'æ‘˜',
  '“F' => 'æ“¢',
  '“G' => 'æ•µ',
  '“H' => 'æ»´',
  '“I' => 'çš„',
  '“J' => 'ç¬›',
  '“K' => 'é�©',
  '“L' => 'é�‘',
  '“M' => 'æºº',
  '“N' => 'å“²',
  '“O' => 'å¾¹',
  '“P' => 'æ’¤',
  '“Q' => 'è½�',
  '“R' => 'è¿­',
  '“S' => 'é‰„',
  '“T' => 'å…¸',
  '“U' => 'å¡«',
  '“V' => 'å¤©',
  '“W' => 'å±•',
  '“X' => 'åº—',
  '“Y' => 'æ·»',
  '“Z' => 'çº�',
  '“[' => 'ç”œ',
  '“\\' => 'è²¼',
  '“]' => 'è»¢',
  '“^' => 'é¡›',
  '“_' => 'ç‚¹',
  '“`' => 'ä¼�',
  '“a' => 'æ®¿',
  '“b' => 'æ¾±',
  '“c' => 'ç”°',
  '“d' => 'é›»',
  '“e' => 'å…Ž',
  '“f' => 'å��',
  '“g' => 'å µ',
  '“h' => 'å¡—',
  '“i' => 'å¦¬',
  '“j' => 'å± ',
  '“k' => 'å¾’',
  '“l' => 'æ–—',
  '“m' => 'æ�œ',
  '“n' => 'æ¸¡',
  '“o' => 'ç™»',
  '“p' => 'è�Ÿ',
  '“q' => 'è³­',
  '“r' => 'é€”',
  '“s' => 'éƒ½',
  '“t' => 'é��',
  '“u' => 'ç ¥',
  '“v' => 'ç º',
  '“w' => 'åŠª',
  '“x' => 'åº¦',
  '“y' => 'åœŸ',
  '“z' => 'å¥´',
  '“{' => 'æ€’',
  '“|' => 'å€’',
  '“}' => 'å…š',
  '“~' => 'å†¬',
  '“€' => 'å‡�',
  '“�' => 'åˆ€',
  '“‚' => 'å”�',
  '“ƒ' => 'å¡”',
  '“„' => 'å¡˜',
  '“…' => 'å¥—',
  '“†' => 'å®•',
  '“‡' => 'å³¶',
  '“ˆ' => 'å¶‹',
  '“‰' => 'æ‚¼',
  '“Š' => 'æŠ•',
  '“‹' => 'æ�­',
  '“Œ' => 'æ�±',
  '“�' => 'æ¡ƒ',
  '“Ž' => 'æ¢¼',
  '“�' => 'æ£Ÿ',
  '“�' => 'ç›—',
  '“‘' => 'æ·˜',
  '“’' => 'æ¹¯',
  '““' => 'æ¶›',
  '“”' => 'ç�¯',
  '“•' => 'ç‡ˆ',
  '“–' => 'å½“',
  '“—' => 'ç—˜',
  '“˜' => 'ç¥·',
  '“™' => 'ç­‰',
  '“š' => 'ç­”',
  '“›' => 'ç­’',
  '“œ' => 'ç³–',
  '“�' => 'çµ±',
  '“ž' => 'åˆ°',
  '“Ÿ' => 'è‘£',
  '“ ' => 'è•©',
  '“¡' => 'è—¤',
  '“¢' => 'è¨Ž',
  '“£' => 'è¬„',
  '“¤' => 'è±†',
  '“¥' => 'è¸�',
  '“¦' => 'é€ƒ',
  '“§' => 'é€�',
  '“¨' => 'é�™',
  '“©' => 'é™¶',
  '“ª' => 'é ­',
  '“«' => 'é¨°',
  '“¬' => 'é—˜',
  '“­' => 'åƒ�',
  '“®' => 'å‹•',
  '“¯' => 'å�Œ',
  '“°' => 'å ‚',
  '“±' => 'å°Ž',
  '“²' => 'æ†§',
  '“³' => 'æ’ž',
  '“´' => 'æ´ž',
  '“µ' => 'çž³',
  '“¶' => 'ç«¥',
  '“·' => 'èƒ´',
  '“¸' => 'è�„',
  '“¹' => 'é�“',
  '“º' => 'éŠ…',
  '“»' => 'å³ ',
  '“¼' => 'é´‡',
  '“½' => 'åŒ¿',
  '“¾' => 'å¾—',
  '“¿' => 'å¾³',
  '“À' => 'æ¶œ',
  '“Á' => 'ç‰¹',
  '“Â' => 'ç�£',
  '“Ã' => 'ç¦¿',
  '“Ä' => 'ç¯¤',
  '“Å' => 'æ¯’',
  '“Æ' => 'ç‹¬',
  '“Ç' => 'èª­',
  '“È' => 'æ ƒ',
  '“É' => 'æ©¡',
  '“Ê' => 'å‡¸',
  '“Ë' => 'çª�',
  '“Ì' => 'æ¤´',
  '“Í' => 'å±Š',
  '“Î' => 'é³¶',
  '“Ï' => 'è‹«',
  '“Ð' => 'å¯…',
  '“Ñ' => 'é…‰',
  '“Ò' => 'ç€ž',
  '“Ó' => 'å™¸',
  '“Ô' => 'å±¯',
  '“Õ' => 'æƒ‡',
  '“Ö' => 'æ•¦',
  '“×' => 'æ²Œ',
  '“Ø' => 'è±š',
  '“Ù' => 'é��',
  '“Ú' => 'é “',
  '“Û' => 'å‘‘',
  '“Ü' => 'æ›‡',
  '“Ý' => 'éˆ�',
  '“Þ' => 'å¥ˆ',
  '“ß' => 'é‚£',
  '“à' => 'å†…',
  '“á' => 'ä¹�',
  '“â' => 'å‡ª',
  '“ã' => 'è–™',
  '“ä' => 'è¬Ž',
  '“å' => 'ç�˜',
  '“æ' => 'æ�º',
  '“ç' => 'é�‹',
  '“è' => 'æ¥¢',
  '“é' => 'é¦´',
  '“ê' => 'ç¸„',
  '“ë' => 'ç•·',
  '“ì' => 'å�—',
  '“í' => 'æ¥ ',
  '“î' => 'è»Ÿ',
  '“ï' => 'é›£',
  '“ð' => 'æ±�',
  '“ñ' => 'äºŒ',
  '“ò' => 'å°¼',
  '“ó' => 'å¼�',
  '“ô' => 'è¿©',
  '“õ' => 'åŒ‚',
  '“ö' => 'è³‘',
  '“÷' => 'è‚‰',
  '“ø' => 'è™¹',
  '“ù' => 'å»¿',
  '“ú' => 'æ—¥',
  '“û' => 'ä¹³',
  '“ü' => 'å…¥',
  '”@' => 'å¦‚',
  '”A' => 'å°¿',
  '”B' => 'éŸ®',
  '”C' => 'ä»»',
  '”D' => 'å¦Š',
  '”E' => 'å¿�',
  '”F' => 'èª�',
  '”G' => 'æ¿¡',
  '”H' => 'ç¦°',
  '”I' => 'ç¥¢',
  '”J' => 'å¯§',
  '”K' => 'è‘±',
  '”L' => 'çŒ«',
  '”M' => 'ç†±',
  '”N' => 'å¹´',
  '”O' => 'å¿µ',
  '”P' => 'æ�»',
  '”Q' => 'æ’š',
  '”R' => 'ç‡ƒ',
  '”S' => 'ç²˜',
  '”T' => 'ä¹ƒ',
  '”U' => 'å»¼',
  '”V' => 'ä¹‹',
  '”W' => 'åŸœ',
  '”X' => 'åš¢',
  '”Y' => 'æ‚©',
  '”Z' => 'æ¿ƒ',
  '”[' => 'ç´�',
  '”\\' => 'èƒ½',
  '”]' => 'è„³',
  '”^' => 'è†¿',
  '”_' => 'è¾²',
  '”`' => 'è¦—',
  '”a' => 'èš¤',
  '”b' => 'å·´',
  '”c' => 'æŠŠ',
  '”d' => 'æ’­',
  '”e' => 'è¦‡',
  '”f' => 'æ�·',
  '”g' => 'æ³¢',
  '”h' => 'æ´¾',
  '”i' => 'ç�¶',
  '”j' => 'ç ´',
  '”k' => 'å©†',
  '”l' => 'ç½µ',
  '”m' => 'èŠ­',
  '”n' => 'é¦¬',
  '”o' => 'ä¿³',
  '”p' => 'å»ƒ',
  '”q' => 'æ‹�',
  '”r' => 'æŽ’',
  '”s' => 'æ•—',
  '”t' => 'æ�¯',
  '”u' => 'ç›ƒ',
  '”v' => 'ç‰Œ',
  '”w' => 'èƒŒ',
  '”x' => 'è‚º',
  '”y' => 'è¼©',
  '”z' => 'é…�',
  '”{' => 'å€�',
  '”|' => 'åŸ¹',
  '”}' => 'åª’',
  '”~' => 'æ¢…',
  '”€' => 'æ¥³',
  '”�' => 'ç…¤',
  '”‚' => 'ç‹½',
  '”ƒ' => 'è²·',
  '”„' => 'å£²',
  '”…' => 'è³ ',
  '”†' => 'é™ª',
  '”‡' => 'é€™',
  '”ˆ' => 'è�¿',
  '”‰' => 'ç§¤',
  '”Š' => 'çŸ§',
  '”‹' => 'è�©',
  '”Œ' => 'ä¼¯',
  '”�' => 'å‰¥',
  '”Ž' => 'å�š',
  '”�' => 'æ‹�',
  '”�' => 'æŸ�',
  '”‘' => 'æ³Š',
  '”’' => 'ç™½',
  '”“' => 'ç®”',
  '””' => 'ç²•',
  '”•' => 'èˆ¶',
  '”–' => 'è–„',
  '”—' => 'è¿«',
  '”˜' => 'æ›�',
  '”™' => 'æ¼ ',
  '”š' => 'çˆ†',
  '”›' => 'ç¸›',
  '”œ' => 'èŽ«',
  '”�' => 'é§�',
  '”ž' => 'éº¦',
  '”Ÿ' => 'å‡½',
  '” ' => 'ç®±',
  '”¡' => 'ç¡²',
  '”¢' => 'ç®¸',
  '”£' => 'è‚‡',
  '”¤' => 'ç­ˆ',
  '”¥' => 'æ«¨',
  '”¦' => 'å¹¡',
  '”§' => 'è‚Œ',
  '”¨' => 'ç•‘',
  '”©' => 'ç• ',
  '”ª' => 'å…«',
  '”«' => 'é‰¢',
  '”¬' => 'æºŒ',
  '”­' => 'ç™º',
  '”®' => 'é†—',
  '”¯' => 'é«ª',
  '”°' => 'ä¼�',
  '”±' => 'ç½°',
  '”²' => 'æŠœ',
  '”³' => 'ç­�',
  '”´' => 'é–¥',
  '”µ' => 'é³©',
  '”¶' => 'å™º',
  '”·' => 'å¡™',
  '”¸' => 'è›¤',
  '”¹' => 'éš¼',
  '”º' => 'ä¼´',
  '”»' => 'åˆ¤',
  '”¼' => 'å�Š',
  '”½' => 'å��',
  '”¾' => 'å�›',
  '”¿' => 'å¸†',
  '”À' => 'æ�¬',
  '”Á' => 'æ–‘',
  '”Â' => 'æ�¿',
  '”Ã' => 'æ°¾',
  '”Ä' => 'æ±Ž',
  '”Å' => 'ç‰ˆ',
  '”Æ' => 'çŠ¯',
  '”Ç' => 'ç�­',
  '”È' => 'ç•”',
  '”É' => 'ç¹�',
  '”Ê' => 'èˆ¬',
  '”Ë' => 'è—©',
  '”Ì' => 'è²©',
  '”Í' => 'ç¯„',
  '”Î' => 'é‡†',
  '”Ï' => 'ç…©',
  '”Ð' => 'é ’',
  '”Ñ' => 'é£¯',
  '”Ò' => 'æŒ½',
  '”Ó' => 'æ™©',
  '”Ô' => 'ç•ª',
  '”Õ' => 'ç›¤',
  '”Ö' => 'ç£�',
  '”×' => 'è•ƒ',
  '”Ø' => 'è›®',
  '”Ù' => 'åŒª',
  '”Ú' => 'å�‘',
  '”Û' => 'å�¦',
  '”Ü' => 'å¦ƒ',
  '”Ý' => 'åº‡',
  '”Þ' => 'å½¼',
  '”ß' => 'æ‚²',
  '”à' => 'æ‰‰',
  '”á' => 'æ‰¹',
  '”â' => 'æŠ«',
  '”ã' => 'æ–�',
  '”ä' => 'æ¯”',
  '”å' => 'æ³Œ',
  '”æ' => 'ç–²',
  '”ç' => 'çš®',
  '”è' => 'ç¢‘',
  '”é' => 'ç§˜',
  '”ê' => 'ç·‹',
  '”ë' => 'ç½·',
  '”ì' => 'è‚¥',
  '”í' => 'è¢«',
  '”î' => 'èª¹',
  '”ï' => 'è²»',
  '”ð' => 'é�¿',
  '”ñ' => 'é�ž',
  '”ò' => 'é£›',
  '”ó' => 'æ¨‹',
  '”ô' => 'ç°¸',
  '”õ' => 'å‚™',
  '”ö' => 'å°¾',
  '”÷' => 'å¾®',
  '”ø' => 'æž‡',
  '”ù' => 'æ¯˜',
  '”ú' => 'ç�µ',
  '”û' => 'çœ‰',
  '”ü' => 'ç¾Ž',
  '•@' => 'é¼»',
  '•A' => 'æŸŠ',
  '•B' => 'ç¨—',
  '•C' => 'åŒ¹',
  '•D' => 'ç–‹',
  '•E' => 'é«­',
  '•F' => 'å½¦',
  '•G' => 'è†�',
  '•H' => 'è�±',
  '•I' => 'è‚˜',
  '•J' => 'å¼¼',
  '•K' => 'å¿…',
  '•L' => 'ç•¢',
  '•M' => 'ç­†',
  '•N' => 'é€¼',
  '•O' => 'æ¡§',
  '•P' => 'å§«',
  '•Q' => 'åª›',
  '•R' => 'ç´�',
  '•S' => 'ç™¾',
  '•T' => 'è¬¬',
  '•U' => 'ä¿µ',
  '•V' => 'å½ª',
  '•W' => 'æ¨™',
  '•X' => 'æ°·',
  '•Y' => 'æ¼‚',
  '•Z' => 'ç“¢',
  '•[' => 'ç¥¨',
  '•\\' => 'è¡¨',
  '•]' => 'è©•',
  '•^' => 'è±¹',
  '•_' => 'å»Ÿ',
  '•`' => 'æ��',
  '•a' => 'ç—…',
  '•b' => 'ç§’',
  '•c' => 'è‹—',
  '•d' => 'éŒ¨',
  '•e' => 'é‹²',
  '•f' => 'è’œ',
  '•g' => 'è›­',
  '•h' => 'é°­',
  '•i' => 'å“�',
  '•j' => 'å½¬',
  '•k' => 'æ–Œ',
  '•l' => 'æµœ',
  '•m' => 'ç€•',
  '•n' => 'è²§',
  '•o' => 'è³“',
  '•p' => 'é »',
  '•q' => 'æ•�',
  '•r' => 'ç“¶',
  '•s' => 'ä¸�',
  '•t' => 'ä»˜',
  '•u' => 'åŸ ',
  '•v' => 'å¤«',
  '•w' => 'å©¦',
  '•x' => 'å¯Œ',
  '•y' => 'å†¨',
  '•z' => 'å¸ƒ',
  '•{' => 'åºœ',
  '•|' => 'æ€–',
  '•}' => 'æ‰¶',
  '•~' => 'æ•·',
  '•€' => 'æ–§',
  '•�' => 'æ™®',
  '•‚' => 'æµ®',
  '•ƒ' => 'çˆ¶',
  '•„' => 'ç¬¦',
  '•…' => 'è…�',
  '•†' => 'è†š',
  '•‡' => 'èŠ™',
  '•ˆ' => 'è­œ',
  '•‰' => 'è² ',
  '•Š' => 'è³¦',
  '•‹' => 'èµ´',
  '•Œ' => 'é˜œ',
  '•�' => 'é™„',
  '•Ž' => 'ä¾®',
  '•�' => 'æ’«',
  '•�' => 'æ­¦',
  '•‘' => 'èˆž',
  '•’' => 'è‘¡',
  '•“' => 'è•ª',
  '•”' => 'éƒ¨',
  '••' => 'å°�',
  '•–' => 'æ¥“',
  '•—' => 'é¢¨',
  '•˜' => 'è‘º',
  '•™' => 'è•—',
  '•š' => 'ä¼�',
  '•›' => 'å‰¯',
  '•œ' => 'å¾©',
  '•�' => 'å¹…',
  '•ž' => 'æœ�',
  '•Ÿ' => 'ç¦�',
  '• ' => 'è…¹',
  '•¡' => 'è¤‡',
  '•¢' => 'è¦†',
  '•£' => 'æ·µ',
  '•¤' => 'å¼—',
  '•¥' => 'æ‰•',
  '•¦' => 'æ²¸',
  '•§' => 'ä»�',
  '•¨' => 'ç‰©',
  '•©' => 'é®’',
  '•ª' => 'åˆ†',
  '•«' => 'å�»',
  '•¬' => 'å™´',
  '•­' => 'å¢³',
  '•®' => 'æ†¤',
  '•¯' => 'æ‰®',
  '•°' => 'ç„š',
  '•±' => 'å¥®',
  '•²' => 'ç²‰',
  '•³' => 'ç³ž',
  '•´' => 'ç´›',
  '•µ' => 'é›°',
  '•¶' => 'æ–‡',
  '•·' => 'è�ž',
  '•¸' => 'ä¸™',
  '•¹' => 'ä½µ',
  '•º' => 'å…µ',
  '•»' => 'å¡€',
  '•¼' => 'å¹£',
  '•½' => 'å¹³',
  '•¾' => 'å¼Š',
  '•¿' => 'æŸ„',
  '•À' => 'ä¸¦',
  '•Á' => 'è”½',
  '•Â' => 'é–‰',
  '•Ã' => 'é™›',
  '•Ä' => 'ç±³',
  '•Å' => 'é �',
  '•Æ' => 'åƒ»',
  '•Ç' => 'å£�',
  '•È' => 'ç™–',
  '•É' => 'ç¢§',
  '•Ê' => 'åˆ¥',
  '•Ë' => 'çž¥',
  '•Ì' => 'è”‘',
  '•Í' => 'ç®†',
  '•Î' => 'å��',
  '•Ï' => 'å¤‰',
  '•Ð' => 'ç‰‡',
  '•Ñ' => 'ç¯‡',
  '•Ò' => 'ç·¨',
  '•Ó' => 'è¾º',
  '•Ô' => 'è¿”',
  '•Õ' => 'é��',
  '•Ö' => 'ä¾¿',
  '•×' => 'å‹‰',
  '•Ø' => 'å¨©',
  '•Ù' => 'å¼�',
  '•Ú' => 'éž­',
  '•Û' => 'ä¿�',
  '•Ü' => 'èˆ—',
  '•Ý' => 'é‹ª',
  '•Þ' => 'åœƒ',
  '•ß' => 'æ�•',
  '•à' => 'æ­©',
  '•á' => 'ç”«',
  '•â' => 'è£œ',
  '•ã' => 'è¼”',
  '•ä' => 'ç©‚',
  '•å' => 'å‹Ÿ',
  '•æ' => 'å¢“',
  '•ç' => 'æ…•',
  '•è' => 'æˆŠ',
  '•é' => 'æš®',
  '•ê' => 'æ¯�',
  '•ë' => 'ç°¿',
  '•ì' => 'è�©',
  '•í' => 'å€£',
  '•î' => 'ä¿¸',
  '•ï' => 'åŒ…',
  '•ð' => 'å‘†',
  '•ñ' => 'å ±',
  '•ò' => 'å¥‰',
  '•ó' => 'å®�',
  '•ô' => 'å³°',
  '•õ' => 'å³¯',
  '•ö' => 'å´©',
  '•÷' => 'åº–',
  '•ø' => 'æŠ±',
  '•ù' => 'æ�§',
  '•ú' => 'æ”¾',
  '•û' => 'æ–¹',
  '•ü' => 'æœ‹',
  '–@' => 'æ³•',
  '–A' => 'æ³¡',
  '–B' => 'çƒ¹',
  '–C' => 'ç ²',
  '–D' => 'ç¸«',
  '–E' => 'èƒž',
  '–F' => 'èŠ³',
  '–G' => 'è�Œ',
  '–H' => 'è“¬',
  '–I' => 'èœ‚',
  '–J' => 'è¤’',
  '–K' => 'è¨ª',
  '–L' => 'è±Š',
  '–M' => 'é‚¦',
  '–N' => 'é‹’',
  '–O' => 'é£½',
  '–P' => 'é³³',
  '–Q' => 'éµ¬',
  '–R' => 'ä¹�',
  '–S' => 'äº¡',
  '–T' => 'å‚�',
  '–U' => 'å‰–',
  '–V' => 'å�Š',
  '–W' => 'å¦¨',
  '–X' => 'å¸½',
  '–Y' => 'å¿˜',
  '–Z' => 'å¿™',
  '–[' => 'æˆ¿',
  '–\\' => 'æš´',
  '–]' => 'æœ›',
  '–^' => 'æŸ�',
  '–_' => 'æ£’',
  '–`' => 'å†’',
  '–a' => 'ç´¡',
  '–b' => 'è‚ª',
  '–c' => 'è†¨',
  '–d' => 'è¬€',
  '–e' => 'è²Œ',
  '–f' => 'è²¿',
  '–g' => 'é‰¾',
  '–h' => 'é˜²',
  '–i' => 'å� ',
  '–j' => 'é ¬',
  '–k' => 'åŒ—',
  '–l' => 'åƒ•',
  '–m' => 'å�œ',
  '–n' => 'å¢¨',
  '–o' => 'æ’²',
  '–p' => 'æœ´',
  '–q' => 'ç‰§',
  '–r' => 'ç�¦',
  '–s' => 'ç©†',
  '–t' => 'é‡¦',
  '–u' => 'å‹ƒ',
  '–v' => 'æ²¡',
  '–w' => 'æ®†',
  '–x' => 'å €',
  '–y' => 'å¹Œ',
  '–z' => 'å¥”',
  '–{' => 'æœ¬',
  '–|' => 'ç¿»',
  '–}' => 'å‡¡',
  '–~' => 'ç›†',
  '–€' => 'æ‘©',
  '–�' => 'ç£¨',
  '–‚' => 'é­”',
  '–ƒ' => 'éº»',
  '–„' => 'åŸ‹',
  '–…' => 'å¦¹',
  '–†' => 'æ˜§',
  '–‡' => 'æžš',
  '–ˆ' => 'æ¯Ž',
  '–‰' => 'å“©',
  '–Š' => 'æ§™',
  '–‹' => 'å¹•',
  '–Œ' => 'è†œ',
  '–�' => 'æž•',
  '–Ž' => 'é®ª',
  '–�' => 'æŸ¾',
  '–�' => 'é±’',
  '–‘' => 'æ¡�',
  '–’' => 'äº¦',
  '–“' => 'ä¿£',
  '–”' => 'å�ˆ',
  '–•' => 'æŠ¹',
  '––' => 'æœ«',
  '–—' => 'æ²«',
  '–˜' => 'è¿„',
  '–™' => 'ä¾­',
  '–š' => 'ç¹­',
  '–›' => 'éº¿',
  '–œ' => 'ä¸‡',
  '–�' => 'æ…¢',
  '–ž' => 'æº€',
  '–Ÿ' => 'æ¼«',
  '– ' => 'è”“',
  '–¡' => 'å‘³',
  '–¢' => 'æœª',
  '–£' => 'é­…',
  '–¤' => 'å·³',
  '–¥' => 'ç®•',
  '–¦' => 'å²¬',
  '–§' => 'å¯†',
  '–¨' => 'èœœ',
  '–©' => 'æ¹Š',
  '–ª' => 'è“‘',
  '–«' => 'ç¨”',
  '–¬' => 'è„ˆ',
  '–­' => 'å¦™',
  '–®' => 'ç²�',
  '–¯' => 'æ°‘',
  '–°' => 'çœ ',
  '–±' => 'å‹™',
  '–²' => 'å¤¢',
  '–³' => 'ç„¡',
  '–´' => 'ç‰Ÿ',
  '–µ' => 'çŸ›',
  '–¶' => 'éœ§',
  '–·' => 'éµ¡',
  '–¸' => 'æ¤‹',
  '–¹' => 'å©¿',
  '–º' => 'å¨˜',
  '–»' => 'å†¥',
  '–¼' => 'å��',
  '–½' => 'å‘½',
  '–¾' => 'æ˜Ž',
  '–¿' => 'ç›Ÿ',
  '–À' => 'è¿·',
  '–Á' => 'éŠ˜',
  '–Â' => 'é³´',
  '–Ã' => 'å§ª',
  '–Ä' => 'ç‰�',
  '–Å' => 'æ»…',
  '–Æ' => 'å…�',
  '–Ç' => 'æ£‰',
  '–È' => 'ç¶¿',
  '–É' => 'ç·¬',
  '–Ê' => 'é�¢',
  '–Ë' => 'éºº',
  '–Ì' => 'æ‘¸',
  '–Í' => 'æ¨¡',
  '–Î' => 'èŒ‚',
  '–Ï' => 'å¦„',
  '–Ð' => 'å­Ÿ',
  '–Ñ' => 'æ¯›',
  '–Ò' => 'çŒ›',
  '–Ó' => 'ç›²',
  '–Ô' => 'ç¶²',
  '–Õ' => 'è€—',
  '–Ö' => 'è’™',
  '–×' => 'å„²',
  '–Ø' => 'æœ¨',
  '–Ù' => 'é»™',
  '–Ú' => 'ç›®',
  '–Û' => 'æ�¢',
  '–Ü' => 'å‹¿',
  '–Ý' => 'é¤…',
  '–Þ' => 'å°¤',
  '–ß' => 'æˆ»',
  '–à' => 'ç±¾',
  '–á' => 'è²°',
  '–â' => 'å•�',
  '–ã' => 'æ‚¶',
  '–ä' => 'ç´‹',
  '–å' => 'é–€',
  '–æ' => 'åŒ�',
  '–ç' => 'ä¹Ÿ',
  '–è' => 'å†¶',
  '–é' => 'å¤œ',
  '–ê' => 'çˆº',
  '–ë' => 'è€¶',
  '–ì' => 'é‡Ž',
  '–í' => 'å¼¥',
  '–î' => 'çŸ¢',
  '–ï' => 'åŽ„',
  '–ð' => 'å½¹',
  '–ñ' => 'ç´„',
  '–ò' => 'è–¬',
  '–ó' => 'è¨³',
  '–ô' => 'èº�',
  '–õ' => 'é�–',
  '–ö' => 'æŸ³',
  '–÷' => 'è–®',
  '–ø' => 'é‘“',
  '–ù' => 'æ„‰',
  '–ú' => 'æ„ˆ',
  '–û' => 'æ²¹',
  '–ü' => 'ç™’',
  '—@' => 'è«­',
  '—A' => 'è¼¸',
  '—B' => 'å”¯',
  '—C' => 'ä½‘',
  '—D' => 'å„ª',
  '—E' => 'å‹‡',
  '—F' => 'å�‹',
  '—G' => 'å®¥',
  '—H' => 'å¹½',
  '—I' => 'æ‚ ',
  '—J' => 'æ†‚',
  '—K' => 'æ�–',
  '—L' => 'æœ‰',
  '—M' => 'æŸš',
  '—N' => 'æ¹§',
  '—O' => 'æ¶Œ',
  '—P' => 'çŒ¶',
  '—Q' => 'çŒ·',
  '—R' => 'ç”±',
  '—S' => 'ç¥�',
  '—T' => 'è£•',
  '—U' => 'èª˜',
  '—V' => 'é�Š',
  '—W' => 'é‚‘',
  '—X' => 'éƒµ',
  '—Y' => 'é›„',
  '—Z' => 'èž�',
  '—[' => 'å¤•',
  '—\\' => 'äºˆ',
  '—]' => 'ä½™',
  '—^' => 'ä¸Ž',
  '—_' => 'èª‰',
  '—`' => 'è¼¿',
  '—a' => 'é �',
  '—b' => 'å‚­',
  '—c' => 'å¹¼',
  '—d' => 'å¦–',
  '—e' => 'å®¹',
  '—f' => 'åº¸',
  '—g' => 'æ�š',
  '—h' => 'æ�º',
  '—i' => 'æ“�',
  '—j' => 'æ›œ',
  '—k' => 'æ¥Š',
  '—l' => 'æ§˜',
  '—m' => 'æ´‹',
  '—n' => 'æº¶',
  '—o' => 'ç†”',
  '—p' => 'ç”¨',
  '—q' => 'çª¯',
  '—r' => 'ç¾Š',
  '—s' => 'è€€',
  '—t' => 'è‘‰',
  '—u' => 'è“‰',
  '—v' => 'è¦�',
  '—w' => 'è¬¡',
  '—x' => 'è¸Š',
  '—y' => 'é�¥',
  '—z' => 'é™½',
  '—{' => 'é¤Š',
  '—|' => 'æ…¾',
  '—}' => 'æŠ‘',
  '—~' => 'æ¬²',
  '—€' => 'æ²ƒ',
  '—�' => 'æµ´',
  '—‚' => 'ç¿Œ',
  '—ƒ' => 'ç¿¼',
  '—„' => 'æ·€',
  '—…' => 'ç¾…',
  '—†' => 'èžº',
  '—‡' => 'è£¸',
  '—ˆ' => 'æ�¥',
  '—‰' => 'èŽ±',
  '—Š' => 'é ¼',
  '—‹' => 'é›·',
  '—Œ' => 'æ´›',
  '—�' => 'çµ¡',
  '—Ž' => 'è�½',
  '—�' => 'é…ª',
  '—�' => 'ä¹±',
  '—‘' => 'å�µ',
  '—’' => 'åµ�',
  '—“' => 'æ¬„',
  '—”' => 'æ¿«',
  '—•' => 'è—�',
  '—–' => 'è˜­',
  '——' => 'è¦§',
  '—˜' => 'åˆ©',
  '—™' => 'å��',
  '—š' => 'å±¥',
  '—›' => 'æ�Ž',
  '—œ' => 'æ¢¨',
  '—�' => 'ç�†',
  '—ž' => 'ç’ƒ',
  '—Ÿ' => 'ç—¢',
  '— ' => 'è£�',
  '—¡' => 'è£¡',
  '—¢' => 'é‡Œ',
  '—£' => 'é›¢',
  '—¤' => 'é™¸',
  '—¥' => 'å¾‹',
  '—¦' => 'çŽ‡',
  '—§' => 'ç«‹',
  '—¨' => 'è‘Ž',
  '—©' => 'æŽ ',
  '—ª' => 'ç•¥',
  '—«' => 'åŠ‰',
  '—¬' => 'æµ�',
  '—­' => 'æºœ',
  '—®' => 'ç�‰',
  '—¯' => 'ç•™',
  '—°' => 'ç¡«',
  '—±' => 'ç²’',
  '—²' => 'éš†',
  '—³' => 'ç«œ',
  '—´' => 'é¾�',
  '—µ' => 'ä¾¶',
  '—¶' => 'æ…®',
  '—·' => 'æ—…',
  '—¸' => 'è™œ',
  '—¹' => 'äº†',
  '—º' => 'äº®',
  '—»' => 'åƒš',
  '—¼' => 'ä¸¡',
  '—½' => 'å‡Œ',
  '—¾' => 'å¯®',
  '—¿' => 'æ–™',
  '—À' => 'æ¢�',
  '—Á' => 'æ¶¼',
  '—Â' => 'çŒŸ',
  '—Ã' => 'ç™‚',
  '—Ä' => 'çž­',
  '—Å' => 'ç¨œ',
  '—Æ' => 'ç³§',
  '—Ç' => 'è‰¯',
  '—È' => 'è«’',
  '—É' => 'é�¼',
  '—Ê' => 'é‡�',
  '—Ë' => 'é™µ',
  '—Ì' => 'é ˜',
  '—Í' => 'åŠ›',
  '—Î' => 'ç·‘',
  '—Ï' => 'å€«',
  '—Ð' => 'åŽ˜',
  '—Ñ' => 'æž—',
  '—Ò' => 'æ·‹',
  '—Ó' => 'ç‡�',
  '—Ô' => 'ç�³',
  '—Õ' => 'è‡¨',
  '—Ö' => 'è¼ª',
  '—×' => 'éš£',
  '—Ø' => 'é±—',
  '—Ù' => 'éºŸ',
  '—Ú' => 'ç‘ ',
  '—Û' => 'å¡�',
  '—Ü' => 'æ¶™',
  '—Ý' => 'ç´¯',
  '—Þ' => 'é¡ž',
  '—ß' => 'ä»¤',
  '—à' => 'ä¼¶',
  '—á' => 'ä¾‹',
  '—â' => 'å†·',
  '—ã' => 'åŠ±',
  '—ä' => 'å¶º',
  '—å' => 'æ€œ',
  '—æ' => 'çŽ²',
  '—ç' => 'ç¤¼',
  '—è' => 'è‹“',
  '—é' => 'éˆ´',
  '—ê' => 'éš·',
  '—ë' => 'é›¶',
  '—ì' => 'éœŠ',
  '—í' => 'éº—',
  '—î' => 'é½¢',
  '—ï' => 'æš¦',
  '—ð' => 'æ­´',
  '—ñ' => 'åˆ—',
  '—ò' => 'åŠ£',
  '—ó' => 'çƒˆ',
  '—ô' => 'è£‚',
  '—õ' => 'å»‰',
  '—ö' => 'æ�‹',
  '—÷' => 'æ†�',
  '—ø' => 'æ¼£',
  '—ù' => 'ç…‰',
  '—ú' => 'ç°¾',
  '—û' => 'ç·´',
  '—ü' => 'è�¯',
  '˜@' => 'è“®',
  '˜A' => 'é€£',
  '˜B' => 'éŒ¬',
  '˜C' => 'å‘‚',
  '˜D' => 'é­¯',
  '˜E' => 'æ«“',
  '˜F' => 'ç‚‰',
  '˜G' => 'è³‚',
  '˜H' => 'è·¯',
  '˜I' => 'éœ²',
  '˜J' => 'åŠ´',
  '˜K' => 'å©�',
  '˜L' => 'å»Š',
  '˜M' => 'å¼„',
  '˜N' => 'æœ—',
  '˜O' => 'æ¥¼',
  '˜P' => 'æ¦”',
  '˜Q' => 'æµª',
  '˜R' => 'æ¼�',
  '˜S' => 'ç‰¢',
  '˜T' => 'ç‹¼',
  '˜U' => 'ç¯­',
  '˜V' => 'è€�',
  '˜W' => 'è�¾',
  '˜X' => 'è�‹',
  '˜Y' => 'éƒŽ',
  '˜Z' => 'å…­',
  '˜[' => 'éº“',
  '˜\\' => 'ç¦„',
  '˜]' => 'è‚‹',
  '˜^' => 'éŒ²',
  '˜_' => 'è«–',
  '˜`' => 'å€­',
  '˜a' => 'å’Œ',
  '˜b' => 'è©±',
  '˜c' => 'æ­ª',
  '˜d' => 'è³„',
  '˜e' => 'è„‡',
  '˜f' => 'æƒ‘',
  '˜g' => 'æž ',
  '˜h' => 'é·²',
  '˜i' => 'äº™',
  '˜j' => 'äº˜',
  '˜k' => 'é°�',
  '˜l' => 'è©«',
  '˜m' => 'è—�',
  '˜n' => 'è•¨',
  '˜o' => 'æ¤€',
  '˜p' => 'æ¹¾',
  '˜q' => 'ç¢—',
  '˜r' => 'è…•',
  '˜Ÿ' => 'å¼Œ',
  '˜ ' => 'ä¸�',
  '˜¡' => 'ä¸•',
  '˜¢' => 'ä¸ª',
  '˜£' => 'ä¸±',
  '˜¤' => 'ä¸¶',
  '˜¥' => 'ä¸¼',
  '˜¦' => 'ä¸¿',
  '˜§' => 'ä¹‚',
  '˜¨' => 'ä¹–',
  '˜©' => 'ä¹˜',
  '˜ª' => 'äº‚',
  '˜«' => 'äº…',
  '˜¬' => 'è±«',
  '˜­' => 'äºŠ',
  '˜®' => 'èˆ’',
  '˜¯' => 'å¼�',
  '˜°' => 'äºŽ',
  '˜±' => 'äºž',
  '˜²' => 'äºŸ',
  '˜³' => 'äº ',
  '˜´' => 'äº¢',
  '˜µ' => 'äº°',
  '˜¶' => 'äº³',
  '˜·' => 'äº¶',
  '˜¸' => 'ä»Ž',
  '˜¹' => 'ä»�',
  '˜º' => 'ä»„',
  '˜»' => 'ä»†',
  '˜¼' => 'ä»‚',
  '˜½' => 'ä»—',
  '˜¾' => 'ä»ž',
  '˜¿' => 'ä»­',
  '˜À' => 'ä»Ÿ',
  '˜Á' => 'ä»·',
  '˜Â' => 'ä¼‰',
  '˜Ã' => 'ä½š',
  '˜Ä' => 'ä¼°',
  '˜Å' => 'ä½›',
  '˜Æ' => 'ä½�',
  '˜Ç' => 'ä½—',
  '˜È' => 'ä½‡',
  '˜É' => 'ä½¶',
  '˜Ê' => 'ä¾ˆ',
  '˜Ë' => 'ä¾�',
  '˜Ì' => 'ä¾˜',
  '˜Í' => 'ä½»',
  '˜Î' => 'ä½©',
  '˜Ï' => 'ä½°',
  '˜Ð' => 'ä¾‘',
  '˜Ñ' => 'ä½¯',
  '˜Ò' => 'ä¾†',
  '˜Ó' => 'ä¾–',
  '˜Ô' => 'å„˜',
  '˜Õ' => 'ä¿”',
  '˜Ö' => 'ä¿Ÿ',
  '˜×' => 'ä¿Ž',
  '˜Ø' => 'ä¿˜',
  '˜Ù' => 'ä¿›',
  '˜Ú' => 'ä¿‘',
  '˜Û' => 'ä¿š',
  '˜Ü' => 'ä¿�',
  '˜Ý' => 'ä¿¤',
  '˜Þ' => 'ä¿¥',
  '˜ß' => 'å€š',
  '˜à' => 'å€¨',
  '˜á' => 'å€”',
  '˜â' => 'å€ª',
  '˜ã' => 'å€¥',
  '˜ä' => 'å€…',
  '˜å' => 'ä¼œ',
  '˜æ' => 'ä¿¶',
  '˜ç' => 'å€¡',
  '˜è' => 'å€©',
  '˜é' => 'å€¬',
  '˜ê' => 'ä¿¾',
  '˜ë' => 'ä¿¯',
  '˜ì' => 'å€‘',
  '˜í' => 'å€†',
  '˜î' => 'å�ƒ',
  '˜ï' => 'å�‡',
  '˜ð' => 'æœƒ',
  '˜ñ' => 'å�•',
  '˜ò' => 'å��',
  '˜ó' => 'å�ˆ',
  '˜ô' => 'å�š',
  '˜õ' => 'å�–',
  '˜ö' => 'å�¬',
  '˜÷' => 'å�¸',
  '˜ø' => 'å‚€',
  '˜ù' => 'å‚š',
  '˜ú' => 'å‚…',
  '˜û' => 'å‚´',
  '˜ü' => 'å‚²',
  '™@' => 'åƒ‰',
  '™A' => 'åƒŠ',
  '™B' => 'å‚³',
  '™C' => 'åƒ‚',
  '™D' => 'åƒ–',
  '™E' => 'åƒž',
  '™F' => 'åƒ¥',
  '™G' => 'åƒ­',
  '™H' => 'åƒ£',
  '™I' => 'åƒ®',
  '™J' => 'åƒ¹',
  '™K' => 'åƒµ',
  '™L' => 'å„‰',
  '™M' => 'å„�',
  '™N' => 'å„‚',
  '™O' => 'å„–',
  '™P' => 'å„•',
  '™Q' => 'å„”',
  '™R' => 'å„š',
  '™S' => 'å„¡',
  '™T' => 'å„º',
  '™U' => 'å„·',
  '™V' => 'å„¼',
  '™W' => 'å„»',
  '™X' => 'å„¿',
  '™Y' => 'å…€',
  '™Z' => 'å…’',
  '™[' => 'å…Œ',
  '™\\' => 'å…”',
  '™]' => 'å…¢',
  '™^' => 'ç«¸',
  '™_' => 'å…©',
  '™`' => 'å…ª',
  '™a' => 'å…®',
  '™b' => 'å†€',
  '™c' => 'å†‚',
  '™d' => 'å›˜',
  '™e' => 'å†Œ',
  '™f' => 'å†‰',
  '™g' => 'å†�',
  '™h' => 'å†‘',
  '™i' => 'å†“',
  '™j' => 'å†•',
  '™k' => 'å†–',
  '™l' => 'å†¤',
  '™m' => 'å†¦',
  '™n' => 'å†¢',
  '™o' => 'å†©',
  '™p' => 'å†ª',
  '™q' => 'å†«',
  '™r' => 'å†³',
  '™s' => 'å†±',
  '™t' => 'å†²',
  '™u' => 'å†°',
  '™v' => 'å†µ',
  '™w' => 'å†½',
  '™x' => 'å‡…',
  '™y' => 'å‡‰',
  '™z' => 'å‡›',
  '™{' => 'å‡ ',
  '™|' => 'è™•',
  '™}' => 'å‡©',
  '™~' => 'å‡­',
  '™€' => 'å‡°',
  '™�' => 'å‡µ',
  '™‚' => 'å‡¾',
  '™ƒ' => 'åˆ„',
  '™„' => 'åˆ‹',
  '™…' => 'åˆ”',
  '™†' => 'åˆŽ',
  '™‡' => 'åˆ§',
  '™ˆ' => 'åˆª',
  '™‰' => 'åˆ®',
  '™Š' => 'åˆ³',
  '™‹' => 'åˆ¹',
  '™Œ' => 'å‰�',
  '™�' => 'å‰„',
  '™Ž' => 'å‰‹',
  '™�' => 'å‰Œ',
  '™�' => 'å‰ž',
  '™‘' => 'å‰”',
  '™’' => 'å‰ª',
  '™“' => 'å‰´',
  '™”' => 'å‰©',
  '™•' => 'å‰³',
  '™–' => 'å‰¿',
  '™—' => 'å‰½',
  '™˜' => 'åŠ�',
  '™™' => 'åŠ”',
  '™š' => 'åŠ’',
  '™›' => 'å‰±',
  '™œ' => 'åŠˆ',
  '™�' => 'åŠ‘',
  '™ž' => 'è¾¨',
  '™Ÿ' => 'è¾§',
  '™ ' => 'åŠ¬',
  '™¡' => 'åŠ­',
  '™¢' => 'åŠ¼',
  '™£' => 'åŠµ',
  '™¤' => 'å‹�',
  '™¥' => 'å‹�',
  '™¦' => 'å‹—',
  '™§' => 'å‹ž',
  '™¨' => 'å‹£',
  '™©' => 'å‹¦',
  '™ª' => 'é£­',
  '™«' => 'å‹ ',
  '™¬' => 'å‹³',
  '™­' => 'å‹µ',
  '™®' => 'å‹¸',
  '™¯' => 'å‹¹',
  '™°' => 'åŒ†',
  '™±' => 'åŒˆ',
  '™²' => 'ç”¸',
  '™³' => 'åŒ�',
  '™´' => 'åŒ�',
  '™µ' => 'åŒ�',
  '™¶' => 'åŒ•',
  '™·' => 'åŒš',
  '™¸' => 'åŒ£',
  '™¹' => 'åŒ¯',
  '™º' => 'åŒ±',
  '™»' => 'åŒ³',
  '™¼' => 'åŒ¸',
  '™½' => 'å�€',
  '™¾' => 'å�†',
  '™¿' => 'å�…',
  '™À' => 'ä¸—',
  '™Á' => 'å�‰',
  '™Â' => 'å��',
  '™Ã' => 'å‡–',
  '™Ä' => 'å�ž',
  '™Å' => 'å�©',
  '™Æ' => 'å�®',
  '™Ç' => 'å¤˜',
  '™È' => 'å�»',
  '™É' => 'å�·',
  '™Ê' => 'åŽ‚',
  '™Ë' => 'åŽ–',
  '™Ì' => 'åŽ ',
  '™Í' => 'åŽ¦',
  '™Î' => 'åŽ¥',
  '™Ï' => 'åŽ®',
  '™Ð' => 'åŽ°',
  '™Ñ' => 'åŽ¶',
  '™Ò' => 'å�ƒ',
  '™Ó' => 'ç°’',
  '™Ô' => 'é›™',
  '™Õ' => 'å�Ÿ',
  '™Ö' => 'æ›¼',
  '™×' => 'ç‡®',
  '™Ø' => 'å�®',
  '™Ù' => 'å�¨',
  '™Ú' => 'å�­',
  '™Û' => 'å�º',
  '™Ü' => 'å��',
  '™Ý' => 'å�½',
  '™Þ' => 'å‘€',
  '™ß' => 'å�¬',
  '™à' => 'å�­',
  '™á' => 'å�¼',
  '™â' => 'å�®',
  '™ã' => 'å�¶',
  '™ä' => 'å�©',
  '™å' => 'å��',
  '™æ' => 'å‘Ž',
  '™ç' => 'å’�',
  '™è' => 'å‘µ',
  '™é' => 'å’Ž',
  '™ê' => 'å‘Ÿ',
  '™ë' => 'å‘±',
  '™ì' => 'å‘·',
  '™í' => 'å‘°',
  '™î' => 'å’’',
  '™ï' => 'å‘»',
  '™ð' => 'å’€',
  '™ñ' => 'å‘¶',
  '™ò' => 'å’„',
  '™ó' => 'å’�',
  '™ô' => 'å’†',
  '™õ' => 'å“‡',
  '™ö' => 'å’¢',
  '™÷' => 'å’¸',
  '™ø' => 'å’¥',
  '™ù' => 'å’¬',
  '™ú' => 'å“„',
  '™û' => 'å“ˆ',
  '™ü' => 'å’¨',
  'š@' => 'å’«',
  'šA' => 'å“‚',
  'šB' => 'å’¤',
  'šC' => 'å’¾',
  'šD' => 'å’¼',
  'šE' => 'å“˜',
  'šF' => 'å“¥',
  'šG' => 'å“¦',
  'šH' => 'å”�',
  'šI' => 'å””',
  'šJ' => 'å“½',
  'šK' => 'å“®',
  'šL' => 'å“­',
  'šM' => 'å“º',
  'šN' => 'å“¢',
  'šO' => 'å”¹',
  'šP' => 'å•€',
  'šQ' => 'å•£',
  'šR' => 'å•Œ',
  'šS' => 'å”®',
  'šT' => 'å•œ',
  'šU' => 'å•…',
  'šV' => 'å•–',
  'šW' => 'å•—',
  'šX' => 'å”¸',
  'šY' => 'å”³',
  'šZ' => 'å•�',
  'š[' => 'å–™',
  'š\\' => 'å–€',
  'š]' => 'å’¯',
  'š^' => 'å–Š',
  'š_' => 'å–Ÿ',
  'š`' => 'å•»',
  'ša' => 'å•¾',
  'šb' => 'å–˜',
  'šc' => 'å–ž',
  'šd' => 'å–®',
  'še' => 'å•¼',
  'šf' => 'å–ƒ',
  'šg' => 'å–©',
  'šh' => 'å–‡',
  'ši' => 'å–¨',
  'šj' => 'å—š',
  'šk' => 'å—…',
  'šl' => 'å—Ÿ',
  'šm' => 'å—„',
  'šn' => 'å—œ',
  'šo' => 'å—¤',
  'šp' => 'å—”',
  'šq' => 'å˜”',
  'šr' => 'å—·',
  'šs' => 'å˜–',
  'št' => 'å—¾',
  'šu' => 'å—½',
  'šv' => 'å˜›',
  'šw' => 'å—¹',
  'šx' => 'å™Ž',
  'šy' => 'å™�',
  'šz' => 'ç‡Ÿ',
  'š{' => 'å˜´',
  'š|' => 'å˜¶',
  'š}' => 'å˜²',
  'š~' => 'å˜¸',
  'š€' => 'å™«',
  'š�' => 'å™¤',
  'š‚' => 'å˜¯',
  'šƒ' => 'å™¬',
  'š„' => 'å™ª',
  'š…' => 'åš†',
  'š†' => 'åš€',
  'š‡' => 'åšŠ',
  'šˆ' => 'åš ',
  'š‰' => 'åš”',
  'šŠ' => 'åš�',
  'š‹' => 'åš¥',
  'šŒ' => 'åš®',
  'š�' => 'åš¶',
  'šŽ' => 'åš´',
  'š�' => 'å›‚',
  'š�' => 'åš¼',
  'š‘' => 'å›�',
  'š’' => 'å›ƒ',
  'š“' => 'å›€',
  'š”' => 'å›ˆ',
  'š•' => 'å›Ž',
  'š–' => 'å›‘',
  'š—' => 'å›“',
  'š˜' => 'å›—',
  'š™' => 'å›®',
  'šš' => 'å›¹',
  'š›' => 'åœ€',
  'šœ' => 'å›¿',
  'š�' => 'åœ„',
  'šž' => 'åœ‰',
  'šŸ' => 'åœˆ',
  'š ' => 'åœ‹',
  'š¡' => 'åœ�',
  'š¢' => 'åœ“',
  'š£' => 'åœ˜',
  'š¤' => 'åœ–',
  'š¥' => 'å—‡',
  'š¦' => 'åœœ',
  'š§' => 'åœ¦',
  'š¨' => 'åœ·',
  'š©' => 'åœ¸',
  'šª' => 'å�Ž',
  'š«' => 'åœ»',
  'š¬' => 'å�€',
  'š­' => 'å��',
  'š®' => 'å�©',
  'š¯' => 'åŸ€',
  'š°' => 'åžˆ',
  'š±' => 'å�¡',
  'š²' => 'å�¿',
  'š³' => 'åž‰',
  'š´' => 'åž“',
  'šµ' => 'åž ',
  'š¶' => 'åž³',
  'š·' => 'åž¤',
  'š¸' => 'åžª',
  'š¹' => 'åž°',
  'šº' => 'åŸƒ',
  'š»' => 'åŸ†',
  'š¼' => 'åŸ”',
  'š½' => 'åŸ’',
  'š¾' => 'åŸ“',
  'š¿' => 'å Š',
  'šÀ' => 'åŸ–',
  'šÁ' => 'åŸ£',
  'šÂ' => 'å ‹',
  'šÃ' => 'å ™',
  'šÄ' => 'å �',
  'šÅ' => 'å¡²',
  'šÆ' => 'å ¡',
  'šÇ' => 'å¡¢',
  'šÈ' => 'å¡‹',
  'šÉ' => 'å¡°',
  'šÊ' => 'æ¯€',
  'šË' => 'å¡’',
  'šÌ' => 'å ½',
  'šÍ' => 'å¡¹',
  'šÎ' => 'å¢…',
  'šÏ' => 'å¢¹',
  'šÐ' => 'å¢Ÿ',
  'šÑ' => 'å¢«',
  'šÒ' => 'å¢º',
  'šÓ' => 'å£ž',
  'šÔ' => 'å¢»',
  'šÕ' => 'å¢¸',
  'šÖ' => 'å¢®',
  'š×' => 'å£…',
  'šØ' => 'å£“',
  'šÙ' => 'å£‘',
  'šÚ' => 'å£—',
  'šÛ' => 'å£™',
  'šÜ' => 'å£˜',
  'šÝ' => 'å£¥',
  'šÞ' => 'å£œ',
  'šß' => 'å£¤',
  'šà' => 'å£Ÿ',
  'šá' => 'å£¯',
  'šâ' => 'å£º',
  'šã' => 'å£¹',
  'šä' => 'å£»',
  'šå' => 'å£¼',
  'šæ' => 'å£½',
  'šç' => 'å¤‚',
  'šè' => 'å¤Š',
  'šé' => 'å¤�',
  'šê' => 'å¤›',
  'šë' => 'æ¢¦',
  'šì' => 'å¤¥',
  'ší' => 'å¤¬',
  'šî' => 'å¤­',
  'šï' => 'å¤²',
  'šð' => 'å¤¸',
  'šñ' => 'å¤¾',
  'šò' => 'ç«’',
  'šó' => 'å¥•',
  'šô' => 'å¥�',
  'šõ' => 'å¥Ž',
  'šö' => 'å¥š',
  'š÷' => 'å¥˜',
  'šø' => 'å¥¢',
  'šù' => 'å¥ ',
  'šú' => 'å¥§',
  'šû' => 'å¥¬',
  'šü' => 'å¥©',
  '›@' => 'å¥¸',
  '›A' => 'å¦�',
  '›B' => 'å¦�',
  '›C' => 'ä½ž',
  '›D' => 'ä¾«',
  '›E' => 'å¦£',
  '›F' => 'å¦²',
  '›G' => 'å§†',
  '›H' => 'å§¨',
  '›I' => 'å§œ',
  '›J' => 'å¦�',
  '›K' => 'å§™',
  '›L' => 'å§š',
  '›M' => 'å¨¥',
  '›N' => 'å¨Ÿ',
  '›O' => 'å¨‘',
  '›P' => 'å¨œ',
  '›Q' => 'å¨‰',
  '›R' => 'å¨š',
  '›S' => 'å©€',
  '›T' => 'å©¬',
  '›U' => 'å©‰',
  '›V' => 'å¨µ',
  '›W' => 'å¨¶',
  '›X' => 'å©¢',
  '›Y' => 'å©ª',
  '›Z' => 'åªš',
  '›[' => 'åª¼',
  '›\\' => 'åª¾',
  '›]' => 'å«‹',
  '›^' => 'å«‚',
  '›_' => 'åª½',
  '›`' => 'å«£',
  '›a' => 'å«—',
  '›b' => 'å«¦',
  '›c' => 'å«©',
  '›d' => 'å«–',
  '›e' => 'å«º',
  '›f' => 'å«»',
  '›g' => 'å¬Œ',
  '›h' => 'å¬‹',
  '›i' => 'å¬–',
  '›j' => 'å¬²',
  '›k' => 'å«�',
  '›l' => 'å¬ª',
  '›m' => 'å¬¶',
  '›n' => 'å¬¾',
  '›o' => 'å­ƒ',
  '›p' => 'å­…',
  '›q' => 'å­€',
  '›r' => 'å­‘',
  '›s' => 'å­•',
  '›t' => 'å­š',
  '›u' => 'å­›',
  '›v' => 'å­¥',
  '›w' => 'å­©',
  '›x' => 'å­°',
  '›y' => 'å­³',
  '›z' => 'å­µ',
  '›{' => 'å­¸',
  '›|' => 'æ–ˆ',
  '›}' => 'å­º',
  '›~' => 'å®€',
  '›€' => 'å®ƒ',
  '›�' => 'å®¦',
  '›‚' => 'å®¸',
  '›ƒ' => 'å¯ƒ',
  '›„' => 'å¯‡',
  '›…' => 'å¯‰',
  '›†' => 'å¯”',
  '›‡' => 'å¯�',
  '›ˆ' => 'å¯¤',
  '›‰' => 'å¯¦',
  '›Š' => 'å¯¢',
  '›‹' => 'å¯ž',
  '›Œ' => 'å¯¥',
  '›�' => 'å¯«',
  '›Ž' => 'å¯°',
  '›�' => 'å¯¶',
  '›�' => 'å¯³',
  '›‘' => 'å°…',
  '›’' => 'å°‡',
  '›“' => 'å°ˆ',
  '›”' => 'å°�',
  '›•' => 'å°“',
  '›–' => 'å° ',
  '›—' => 'å°¢',
  '›˜' => 'å°¨',
  '›™' => 'å°¸',
  '›š' => 'å°¹',
  '››' => 'å±�',
  '›œ' => 'å±†',
  '›�' => 'å±Ž',
  '›ž' => 'å±“',
  '›Ÿ' => 'å±�',
  '› ' => 'å±�',
  '›¡' => 'å­±',
  '›¢' => 'å±¬',
  '›£' => 'å±®',
  '›¤' => 'ä¹¢',
  '›¥' => 'å±¶',
  '›¦' => 'å±¹',
  '›§' => 'å²Œ',
  '›¨' => 'å²‘',
  '›©' => 'å²”',
  '›ª' => 'å¦›',
  '›«' => 'å²«',
  '›¬' => 'å²»',
  '›­' => 'å²¶',
  '›®' => 'å²¼',
  '›¯' => 'å²·',
  '›°' => 'å³…',
  '›±' => 'å²¾',
  '›²' => 'å³‡',
  '›³' => 'å³™',
  '›´' => 'å³©',
  '›µ' => 'å³½',
  '›¶' => 'å³º',
  '›·' => 'å³­',
  '›¸' => 'å¶Œ',
  '›¹' => 'å³ª',
  '›º' => 'å´‹',
  '›»' => 'å´•',
  '›¼' => 'å´—',
  '›½' => 'åµœ',
  '›¾' => 'å´Ÿ',
  '›¿' => 'å´›',
  '›À' => 'å´‘',
  '›Á' => 'å´”',
  '›Â' => 'å´¢',
  '›Ã' => 'å´š',
  '›Ä' => 'å´™',
  '›Å' => 'å´˜',
  '›Æ' => 'åµŒ',
  '›Ç' => 'åµ’',
  '›È' => 'åµŽ',
  '›É' => 'åµ‹',
  '›Ê' => 'åµ¬',
  '›Ë' => 'åµ³',
  '›Ì' => 'åµ¶',
  '›Í' => 'å¶‡',
  '›Î' => 'å¶„',
  '›Ï' => 'å¶‚',
  '›Ð' => 'å¶¢',
  '›Ñ' => 'å¶�',
  '›Ò' => 'å¶¬',
  '›Ó' => 'å¶®',
  '›Ô' => 'å¶½',
  '›Õ' => 'å¶�',
  '›Ö' => 'å¶·',
  '›×' => 'å¶¼',
  '›Ø' => 'å·‰',
  '›Ù' => 'å·�',
  '›Ú' => 'å·“',
  '›Û' => 'å·’',
  '›Ü' => 'å·–',
  '›Ý' => 'å·›',
  '›Þ' => 'å·«',
  '›ß' => 'å·²',
  '›à' => 'å·µ',
  '›á' => 'å¸‹',
  '›â' => 'å¸š',
  '›ã' => 'å¸™',
  '›ä' => 'å¸‘',
  '›å' => 'å¸›',
  '›æ' => 'å¸¶',
  '›ç' => 'å¸·',
  '›è' => 'å¹„',
  '›é' => 'å¹ƒ',
  '›ê' => 'å¹€',
  '›ë' => 'å¹Ž',
  '›ì' => 'å¹—',
  '›í' => 'å¹”',
  '›î' => 'å¹Ÿ',
  '›ï' => 'å¹¢',
  '›ð' => 'å¹¤',
  '›ñ' => 'å¹‡',
  '›ò' => 'å¹µ',
  '›ó' => 'å¹¶',
  '›ô' => 'å¹º',
  '›õ' => 'éº¼',
  '›ö' => 'å¹¿',
  '›÷' => 'åº ',
  '›ø' => 'å»�',
  '›ù' => 'å»‚',
  '›ú' => 'å»ˆ',
  '›û' => 'å»�',
  '›ü' => 'å»�',
  'œ@' => 'å»–',
  'œA' => 'å»£',
  'œB' => 'å»�',
  'œC' => 'å»š',
  'œD' => 'å»›',
  'œE' => 'å»¢',
  'œF' => 'å»¡',
  'œG' => 'å»¨',
  'œH' => 'å»©',
  'œI' => 'å»¬',
  'œJ' => 'å»±',
  'œK' => 'å»³',
  'œL' => 'å»°',
  'œM' => 'å»´',
  'œN' => 'å»¸',
  'œO' => 'å»¾',
  'œP' => 'å¼ƒ',
  'œQ' => 'å¼‰',
  'œR' => 'å½�',
  'œS' => 'å½œ',
  'œT' => 'å¼‹',
  'œU' => 'å¼‘',
  'œV' => 'å¼–',
  'œW' => 'å¼©',
  'œX' => 'å¼­',
  'œY' => 'å¼¸',
  'œZ' => 'å½�',
  'œ[' => 'å½ˆ',
  'œ\\' => 'å½Œ',
  'œ]' => 'å½Ž',
  'œ^' => 'å¼¯',
  'œ_' => 'å½‘',
  'œ`' => 'å½–',
  'œa' => 'å½—',
  'œb' => 'å½™',
  'œc' => 'å½¡',
  'œd' => 'å½­',
  'œe' => 'å½³',
  'œf' => 'å½·',
  'œg' => 'å¾ƒ',
  'œh' => 'å¾‚',
  'œi' => 'å½¿',
  'œj' => 'å¾Š',
  'œk' => 'å¾ˆ',
  'œl' => 'å¾‘',
  'œm' => 'å¾‡',
  'œn' => 'å¾ž',
  'œo' => 'å¾™',
  'œp' => 'å¾˜',
  'œq' => 'å¾ ',
  'œr' => 'å¾¨',
  'œs' => 'å¾­',
  'œt' => 'å¾¼',
  'œu' => 'å¿–',
  'œv' => 'å¿»',
  'œw' => 'å¿¤',
  'œx' => 'å¿¸',
  'œy' => 'å¿±',
  'œz' => 'å¿�',
  'œ{' => 'æ‚³',
  'œ|' => 'å¿¿',
  'œ}' => 'æ€¡',
  'œ~' => 'æ� ',
  'œ€' => 'æ€™',
  'œ�' => 'æ€�',
  'œ‚' => 'æ€©',
  'œƒ' => 'æ€Ž',
  'œ„' => 'æ€±',
  'œ…' => 'æ€›',
  'œ†' => 'æ€•',
  'œ‡' => 'æ€«',
  'œˆ' => 'æ€¦',
  'œ‰' => 'æ€�',
  'œŠ' => 'æ€º',
  'œ‹' => 'æ�š',
  'œŒ' => 'æ��',
  'œ�' => 'æ�ª',
  'œŽ' => 'æ�·',
  'œ�' => 'æ�Ÿ',
  'œ�' => 'æ�Š',
  'œ‘' => 'æ�†',
  'œ’' => 'æ��',
  'œ“' => 'æ�£',
  'œ”' => 'æ�ƒ',
  'œ•' => 'æ�¤',
  'œ–' => 'æ�‚',
  'œ—' => 'æ�¬',
  'œ˜' => 'æ�«',
  'œ™' => 'æ�™',
  'œš' => 'æ‚�',
  'œ›' => 'æ‚�',
  'œœ' => 'æƒ§',
  'œ�' => 'æ‚ƒ',
  'œž' => 'æ‚š',
  'œŸ' => 'æ‚„',
  'œ ' => 'æ‚›',
  'œ¡' => 'æ‚–',
  'œ¢' => 'æ‚—',
  'œ£' => 'æ‚’',
  'œ¤' => 'æ‚§',
  'œ¥' => 'æ‚‹',
  'œ¦' => 'æƒ¡',
  'œ§' => 'æ‚¸',
  'œ¨' => 'æƒ ',
  'œ©' => 'æƒ“',
  'œª' => 'æ‚´',
  'œ«' => 'å¿°',
  'œ¬' => 'æ‚½',
  'œ­' => 'æƒ†',
  'œ®' => 'æ‚µ',
  'œ¯' => 'æƒ˜',
  'œ°' => 'æ…�',
  'œ±' => 'æ„•',
  'œ²' => 'æ„†',
  'œ³' => 'æƒ¶',
  'œ´' => 'æƒ·',
  'œµ' => 'æ„€',
  'œ¶' => 'æƒ´',
  'œ·' => 'æƒº',
  'œ¸' => 'æ„ƒ',
  'œ¹' => 'æ„¡',
  'œº' => 'æƒ»',
  'œ»' => 'æƒ±',
  'œ¼' => 'æ„�',
  'œ½' => 'æ„Ž',
  'œ¾' => 'æ…‡',
  'œ¿' => 'æ„¾',
  'œÀ' => 'æ„¨',
  'œÁ' => 'æ„§',
  'œÂ' => 'æ…Š',
  'œÃ' => 'æ„¿',
  'œÄ' => 'æ„¼',
  'œÅ' => 'æ„¬',
  'œÆ' => 'æ„´',
  'œÇ' => 'æ„½',
  'œÈ' => 'æ…‚',
  'œÉ' => 'æ…„',
  'œÊ' => 'æ…³',
  'œË' => 'æ…·',
  'œÌ' => 'æ…˜',
  'œÍ' => 'æ…™',
  'œÎ' => 'æ…š',
  'œÏ' => 'æ…«',
  'œÐ' => 'æ…´',
  'œÑ' => 'æ…¯',
  'œÒ' => 'æ…¥',
  'œÓ' => 'æ…±',
  'œÔ' => 'æ…Ÿ',
  'œÕ' => 'æ…�',
  'œÖ' => 'æ…“',
  'œ×' => 'æ…µ',
  'œØ' => 'æ†™',
  'œÙ' => 'æ†–',
  'œÚ' => 'æ†‡',
  'œÛ' => 'æ†¬',
  'œÜ' => 'æ†”',
  'œÝ' => 'æ†š',
  'œÞ' => 'æ†Š',
  'œß' => 'æ†‘',
  'œà' => 'æ†«',
  'œá' => 'æ†®',
  'œâ' => 'æ‡Œ',
  'œã' => 'æ‡Š',
  'œä' => 'æ‡‰',
  'œå' => 'æ‡·',
  'œæ' => 'æ‡ˆ',
  'œç' => 'æ‡ƒ',
  'œè' => 'æ‡†',
  'œé' => 'æ†º',
  'œê' => 'æ‡‹',
  'œë' => 'ç½¹',
  'œì' => 'æ‡�',
  'œí' => 'æ‡¦',
  'œî' => 'æ‡£',
  'œï' => 'æ‡¶',
  'œð' => 'æ‡º',
  'œñ' => 'æ‡´',
  'œò' => 'æ‡¿',
  'œó' => 'æ‡½',
  'œô' => 'æ‡¼',
  'œõ' => 'æ‡¾',
  'œö' => 'æˆ€',
  'œ÷' => 'æˆˆ',
  'œø' => 'æˆ‰',
  'œù' => 'æˆ�',
  'œú' => 'æˆŒ',
  'œû' => 'æˆ”',
  'œü' => 'æˆ›',
  '�@' => 'æˆž',
  '�A' => 'æˆ¡',
  '�B' => 'æˆª',
  '�C' => 'æˆ®',
  '�D' => 'æˆ°',
  '�E' => 'æˆ²',
  '�F' => 'æˆ³',
  '�G' => 'æ‰�',
  '�H' => 'æ‰Ž',
  '�I' => 'æ‰ž',
  '�J' => 'æ‰£',
  '�K' => 'æ‰›',
  '�L' => 'æ‰ ',
  '�M' => 'æ‰¨',
  '�N' => 'æ‰¼',
  '�O' => 'æŠ‚',
  '�P' => 'æŠ‰',
  '�Q' => 'æ‰¾',
  '�R' => 'æŠ’',
  '�S' => 'æŠ“',
  '�T' => 'æŠ–',
  '�U' => 'æ‹”',
  '�V' => 'æŠƒ',
  '�W' => 'æŠ”',
  '�X' => 'æ‹—',
  '�Y' => 'æ‹‘',
  '�Z' => 'æŠ»',
  '�[' => 'æ‹�',
  '�\\' => 'æ‹¿',
  '�]' => 'æ‹†',
  '�^' => 'æ“”',
  '�_' => 'æ‹ˆ',
  '�`' => 'æ‹œ',
  '�a' => 'æ‹Œ',
  '�b' => 'æ‹Š',
  '�c' => 'æ‹‚',
  '�d' => 'æ‹‡',
  '�e' => 'æŠ›',
  '�f' => 'æ‹‰',
  '�g' => 'æŒŒ',
  '�h' => 'æ‹®',
  '�i' => 'æ‹±',
  '�j' => 'æŒ§',
  '�k' => 'æŒ‚',
  '�l' => 'æŒˆ',
  '�m' => 'æ‹¯',
  '�n' => 'æ‹µ',
  '�o' => 'æ��',
  '�p' => 'æŒ¾',
  '�q' => 'æ��',
  '�r' => 'æ�œ',
  '�s' => 'æ��',
  '�t' => 'æŽ–',
  '�u' => 'æŽŽ',
  '�v' => 'æŽ€',
  '�w' => 'æŽ«',
  '�x' => 'æ�¶',
  '�y' => 'æŽ£',
  '�z' => 'æŽ�',
  '�{' => 'æŽ‰',
  '�|' => 'æŽŸ',
  '�}' => 'æŽµ',
  '�~' => 'æ�«',
  '�€' => 'æ�©',
  '��' => 'æŽ¾',
  '�‚' => 'æ�©',
  '�ƒ' => 'æ�€',
  '�„' => 'æ�†',
  '�…' => 'æ�£',
  '�†' => 'æ�‰',
  '�‡' => 'æ�’',
  '�ˆ' => 'æ�¶',
  '�‰' => 'æ�„',
  '�Š' => 'æ�–',
  '�‹' => 'æ�´',
  '�Œ' => 'æ�†',
  '��' => 'æ�“',
  '�Ž' => 'æ�¦',
  '��' => 'æ�¶',
  '��' => 'æ”�',
  '�‘' => 'æ�—',
  '�’' => 'æ�¨',
  '�“' => 'æ��',
  '�”' => 'æ‘§',
  '�•' => 'æ‘¯',
  '�–' => 'æ‘¶',
  '�—' => 'æ‘Ž',
  '�˜' => 'æ”ª',
  '�™' => 'æ’•',
  '�š' => 'æ’“',
  '�›' => 'æ’¥',
  '�œ' => 'æ’©',
  '��' => 'æ’ˆ',
  '�ž' => 'æ’¼',
  '�Ÿ' => 'æ“š',
  '� ' => 'æ“’',
  '�¡' => 'æ“…',
  '�¢' => 'æ“‡',
  '�£' => 'æ’»',
  '�¤' => 'æ“˜',
  '�¥' => 'æ“‚',
  '�¦' => 'æ“±',
  '�§' => 'æ“§',
  '�¨' => 'èˆ‰',
  '�©' => 'æ“ ',
  '�ª' => 'æ“¡',
  '�«' => 'æŠ¬',
  '�¬' => 'æ“£',
  '�­' => 'æ“¯',
  '�®' => 'æ”¬',
  '�¯' => 'æ“¶',
  '�°' => 'æ“´',
  '�±' => 'æ“²',
  '�²' => 'æ“º',
  '�³' => 'æ”€',
  '�´' => 'æ“½',
  '�µ' => 'æ”˜',
  '�¶' => 'æ”œ',
  '�·' => 'æ”…',
  '�¸' => 'æ”¤',
  '�¹' => 'æ”£',
  '�º' => 'æ”«',
  '�»' => 'æ”´',
  '�¼' => 'æ”µ',
  '�½' => 'æ”·',
  '�¾' => 'æ”¶',
  '�¿' => 'æ”¸',
  '�À' => 'ç•‹',
  '�Á' => 'æ•ˆ',
  '�Â' => 'æ•–',
  '�Ã' => 'æ••',
  '�Ä' => 'æ•�',
  '�Å' => 'æ•˜',
  '�Æ' => 'æ•ž',
  '�Ç' => 'æ•�',
  '�È' => 'æ•²',
  '�É' => 'æ•¸',
  '�Ê' => 'æ–‚',
  '�Ë' => 'æ–ƒ',
  '�Ì' => 'è®Š',
  '�Í' => 'æ–›',
  '�Î' => 'æ–Ÿ',
  '�Ï' => 'æ–«',
  '�Ð' => 'æ–·',
  '�Ñ' => 'æ—ƒ',
  '�Ò' => 'æ—†',
  '�Ó' => 'æ—�',
  '�Ô' => 'æ—„',
  '�Õ' => 'æ—Œ',
  '�Ö' => 'æ—’',
  '�×' => 'æ—›',
  '�Ø' => 'æ—™',
  '�Ù' => 'æ— ',
  '�Ú' => 'æ—¡',
  '�Û' => 'æ—±',
  '�Ü' => 'æ�²',
  '�Ý' => 'æ˜Š',
  '�Þ' => 'æ˜ƒ',
  '�ß' => 'æ—»',
  '�à' => 'æ�³',
  '�á' => 'æ˜µ',
  '�â' => 'æ˜¶',
  '�ã' => 'æ˜´',
  '�ä' => 'æ˜œ',
  '�å' => 'æ™�',
  '�æ' => 'æ™„',
  '�ç' => 'æ™‰',
  '�è' => 'æ™�',
  '�é' => 'æ™ž',
  '�ê' => 'æ™�',
  '�ë' => 'æ™¤',
  '�ì' => 'æ™§',
  '�í' => 'æ™¨',
  '�î' => 'æ™Ÿ',
  '�ï' => 'æ™¢',
  '�ð' => 'æ™°',
  '�ñ' => 'æšƒ',
  '�ò' => 'æšˆ',
  '�ó' => 'æšŽ',
  '�ô' => 'æš‰',
  '�õ' => 'æš„',
  '�ö' => 'æš˜',
  '�÷' => 'æš�',
  '�ø' => 'æ›�',
  '�ù' => 'æš¹',
  '�ú' => 'æ›‰',
  '�û' => 'æš¾',
  '�ü' => 'æš¼',
  'ž@' => 'æ›„',
  'žA' => 'æš¸',
  'žB' => 'æ›–',
  'žC' => 'æ›š',
  'žD' => 'æ› ',
  'žE' => 'æ˜¿',
  'žF' => 'æ›¦',
  'žG' => 'æ›©',
  'žH' => 'æ›°',
  'žI' => 'æ›µ',
  'žJ' => 'æ›·',
  'žK' => 'æœ�',
  'žL' => 'æœ–',
  'žM' => 'æœž',
  'žN' => 'æœ¦',
  'žO' => 'æœ§',
  'žP' => 'éœ¸',
  'žQ' => 'æœ®',
  'žR' => 'æœ¿',
  'žS' => 'æœ¶',
  'žT' => 'æ��',
  'žU' => 'æœ¸',
  'žV' => 'æœ·',
  'žW' => 'æ�†',
  'žX' => 'æ�ž',
  'žY' => 'æ� ',
  'žZ' => 'æ�™',
  'ž[' => 'æ�£',
  'ž\\' => 'æ�¤',
  'ž]' => 'æž‰',
  'ž^' => 'æ�°',
  'ž_' => 'æž©',
  'ž`' => 'æ�¼',
  'ža' => 'æ�ª',
  'žb' => 'æžŒ',
  'žc' => 'æž‹',
  'žd' => 'æž¦',
  'že' => 'æž¡',
  'žf' => 'æž…',
  'žg' => 'æž·',
  'žh' => 'æŸ¯',
  'ži' => 'æž´',
  'žj' => 'æŸ¬',
  'žk' => 'æž³',
  'žl' => 'æŸ©',
  'žm' => 'æž¸',
  'žn' => 'æŸ¤',
  'žo' => 'æŸž',
  'žp' => 'æŸ�',
  'žq' => 'æŸ¢',
  'žr' => 'æŸ®',
  'žs' => 'æž¹',
  'žt' => 'æŸŽ',
  'žu' => 'æŸ†',
  'žv' => 'æŸ§',
  'žw' => 'æªœ',
  'žx' => 'æ ž',
  'žy' => 'æ¡†',
  'žz' => 'æ ©',
  'ž{' => 'æ¡€',
  'ž|' => 'æ¡�',
  'ž}' => 'æ ²',
  'ž~' => 'æ¡Ž',
  'ž€' => 'æ¢³',
  'ž�' => 'æ «',
  'ž‚' => 'æ¡™',
  'žƒ' => 'æ¡£',
  'ž„' => 'æ¡·',
  'ž…' => 'æ¡¿',
  'ž†' => 'æ¢Ÿ',
  'ž‡' => 'æ¢�',
  'žˆ' => 'æ¢­',
  'ž‰' => 'æ¢”',
  'žŠ' => 'æ¢�',
  'ž‹' => 'æ¢›',
  'žŒ' => 'æ¢ƒ',
  'ž�' => 'æª®',
  'žŽ' => 'æ¢¹',
  'ž�' => 'æ¡´',
  'ž�' => 'æ¢µ',
  'ž‘' => 'æ¢ ',
  'ž’' => 'æ¢º',
  'ž“' => 'æ¤�',
  'ž”' => 'æ¢�',
  'ž•' => 'æ¡¾',
  'ž–' => 'æ¤�',
  'ž—' => 'æ£Š',
  'ž˜' => 'æ¤ˆ',
  'ž™' => 'æ£˜',
  'žš' => 'æ¤¢',
  'ž›' => 'æ¤¦',
  'žœ' => 'æ£¡',
  'ž�' => 'æ¤Œ',
  'žž' => 'æ£�',
  'žŸ' => 'æ£”',
  'ž ' => 'æ£§',
  'ž¡' => 'æ£•',
  'ž¢' => 'æ¤¶',
  'ž£' => 'æ¤’',
  'ž¤' => 'æ¤„',
  'ž¥' => 'æ£—',
  'ž¦' => 'æ££',
  'ž§' => 'æ¤¥',
  'ž¨' => 'æ£¹',
  'ž©' => 'æ£ ',
  'žª' => 'æ£¯',
  'ž«' => 'æ¤¨',
  'ž¬' => 'æ¤ª',
  'ž­' => 'æ¤š',
  'ž®' => 'æ¤£',
  'ž¯' => 'æ¤¡',
  'ž°' => 'æ£†',
  'ž±' => 'æ¥¹',
  'ž²' => 'æ¥·',
  'ž³' => 'æ¥œ',
  'ž´' => 'æ¥¸',
  'žµ' => 'æ¥«',
  'ž¶' => 'æ¥”',
  'ž·' => 'æ¥¾',
  'ž¸' => 'æ¥®',
  'ž¹' => 'æ¤¹',
  'žº' => 'æ¥´',
  'ž»' => 'æ¤½',
  'ž¼' => 'æ¥™',
  'ž½' => 'æ¤°',
  'ž¾' => 'æ¥¡',
  'ž¿' => 'æ¥ž',
  'žÀ' => 'æ¥�',
  'žÁ' => 'æ¦�',
  'žÂ' => 'æ¥ª',
  'žÃ' => 'æ¦²',
  'žÄ' => 'æ¦®',
  'žÅ' => 'æ§�',
  'žÆ' => 'æ¦¿',
  'žÇ' => 'æ§�',
  'žÈ' => 'æ§“',
  'žÉ' => 'æ¦¾',
  'žÊ' => 'æ§Ž',
  'žË' => 'å¯¨',
  'žÌ' => 'æ§Š',
  'žÍ' => 'æ§�',
  'žÎ' => 'æ¦»',
  'žÏ' => 'æ§ƒ',
  'žÐ' => 'æ¦§',
  'žÑ' => 'æ¨®',
  'žÒ' => 'æ¦‘',
  'žÓ' => 'æ¦ ',
  'žÔ' => 'æ¦œ',
  'žÕ' => 'æ¦•',
  'žÖ' => 'æ¦´',
  'ž×' => 'æ§ž',
  'žØ' => 'æ§¨',
  'žÙ' => 'æ¨‚',
  'žÚ' => 'æ¨›',
  'žÛ' => 'æ§¿',
  'žÜ' => 'æ¬Š',
  'žÝ' => 'æ§¹',
  'žÞ' => 'æ§²',
  'žß' => 'æ§§',
  'žà' => 'æ¨…',
  'žá' => 'æ¦±',
  'žâ' => 'æ¨ž',
  'žã' => 'æ§­',
  'žä' => 'æ¨”',
  'žå' => 'æ§«',
  'žæ' => 'æ¨Š',
  'žç' => 'æ¨’',
  'žè' => 'æ«�',
  'žé' => 'æ¨£',
  'žê' => 'æ¨“',
  'žë' => 'æ©„',
  'žì' => 'æ¨Œ',
  'ží' => 'æ©²',
  'žî' => 'æ¨¶',
  'žï' => 'æ©¸',
  'žð' => 'æ©‡',
  'žñ' => 'æ©¢',
  'žò' => 'æ©™',
  'žó' => 'æ©¦',
  'žô' => 'æ©ˆ',
  'žõ' => 'æ¨¸',
  'žö' => 'æ¨¢',
  'ž÷' => 'æª�',
  'žø' => 'æª�',
  'žù' => 'æª ',
  'žú' => 'æª„',
  'žû' => 'æª¢',
  'žü' => 'æª£',
  'Ÿ@' => 'æª—',
  'ŸA' => 'è˜—',
  'ŸB' => 'æª»',
  'ŸC' => 'æ«ƒ',
  'ŸD' => 'æ«‚',
  'ŸE' => 'æª¸',
  'ŸF' => 'æª³',
  'ŸG' => 'æª¬',
  'ŸH' => 'æ«ž',
  'ŸI' => 'æ«‘',
  'ŸJ' => 'æ«Ÿ',
  'ŸK' => 'æªª',
  'ŸL' => 'æ«š',
  'ŸM' => 'æ«ª',
  'ŸN' => 'æ«»',
  'ŸO' => 'æ¬…',
  'ŸP' => 'è˜–',
  'ŸQ' => 'æ«º',
  'ŸR' => 'æ¬’',
  'ŸS' => 'æ¬–',
  'ŸT' => 'é¬±',
  'ŸU' => 'æ¬Ÿ',
  'ŸV' => 'æ¬¸',
  'ŸW' => 'æ¬·',
  'ŸX' => 'ç›œ',
  'ŸY' => 'æ¬¹',
  'ŸZ' => 'é£®',
  'Ÿ[' => 'æ­‡',
  'Ÿ\\' => 'æ­ƒ',
  'Ÿ]' => 'æ­‰',
  'Ÿ^' => 'æ­�',
  'Ÿ_' => 'æ­™',
  'Ÿ`' => 'æ­”',
  'Ÿa' => 'æ­›',
  'Ÿb' => 'æ­Ÿ',
  'Ÿc' => 'æ­¡',
  'Ÿd' => 'æ­¸',
  'Ÿe' => 'æ­¹',
  'Ÿf' => 'æ­¿',
  'Ÿg' => 'æ®€',
  'Ÿh' => 'æ®„',
  'Ÿi' => 'æ®ƒ',
  'Ÿj' => 'æ®�',
  'Ÿk' => 'æ®˜',
  'Ÿl' => 'æ®•',
  'Ÿm' => 'æ®ž',
  'Ÿn' => 'æ®¤',
  'Ÿo' => 'æ®ª',
  'Ÿp' => 'æ®«',
  'Ÿq' => 'æ®¯',
  'Ÿr' => 'æ®²',
  'Ÿs' => 'æ®±',
  'Ÿt' => 'æ®³',
  'Ÿu' => 'æ®·',
  'Ÿv' => 'æ®¼',
  'Ÿw' => 'æ¯†',
  'Ÿx' => 'æ¯‹',
  'Ÿy' => 'æ¯“',
  'Ÿz' => 'æ¯Ÿ',
  'Ÿ{' => 'æ¯¬',
  'Ÿ|' => 'æ¯«',
  'Ÿ}' => 'æ¯³',
  'Ÿ~' => 'æ¯¯',
  'Ÿ€' => 'éº¾',
  'Ÿ�' => 'æ°ˆ',
  'Ÿ‚' => 'æ°“',
  'Ÿƒ' => 'æ°”',
  'Ÿ„' => 'æ°›',
  'Ÿ…' => 'æ°¤',
  'Ÿ†' => 'æ°£',
  'Ÿ‡' => 'æ±ž',
  'Ÿˆ' => 'æ±•',
  'Ÿ‰' => 'æ±¢',
  'ŸŠ' => 'æ±ª',
  'Ÿ‹' => 'æ²‚',
  'ŸŒ' => 'æ²�',
  'Ÿ�' => 'æ²š',
  'ŸŽ' => 'æ²�',
  'Ÿ�' => 'æ²›',
  'Ÿ�' => 'æ±¾',
  'Ÿ‘' => 'æ±¨',
  'Ÿ’' => 'æ±³',
  'Ÿ“' => 'æ²’',
  'Ÿ”' => 'æ²�',
  'Ÿ•' => 'æ³„',
  'Ÿ–' => 'æ³±',
  'Ÿ—' => 'æ³“',
  'Ÿ˜' => 'æ²½',
  'Ÿ™' => 'æ³—',
  'Ÿš' => 'æ³…',
  'Ÿ›' => 'æ³�',
  'Ÿœ' => 'æ²®',
  'Ÿ�' => 'æ²±',
  'Ÿž' => 'æ²¾',
  'ŸŸ' => 'æ²º',
  'Ÿ ' => 'æ³›',
  'Ÿ¡' => 'æ³¯',
  'Ÿ¢' => 'æ³™',
  'Ÿ£' => 'æ³ª',
  'Ÿ¤' => 'æ´Ÿ',
  'Ÿ¥' => 'è¡�',
  'Ÿ¦' => 'æ´¶',
  'Ÿ§' => 'æ´«',
  'Ÿ¨' => 'æ´½',
  'Ÿ©' => 'æ´¸',
  'Ÿª' => 'æ´™',
  'Ÿ«' => 'æ´µ',
  'Ÿ¬' => 'æ´³',
  'Ÿ­' => 'æ´’',
  'Ÿ®' => 'æ´Œ',
  'Ÿ¯' => 'æµ£',
  'Ÿ°' => 'æ¶“',
  'Ÿ±' => 'æµ¤',
  'Ÿ²' => 'æµš',
  'Ÿ³' => 'æµ¹',
  'Ÿ´' => 'æµ™',
  'Ÿµ' => 'æ¶Ž',
  'Ÿ¶' => 'æ¶•',
  'Ÿ·' => 'æ¿¤',
  'Ÿ¸' => 'æ¶…',
  'Ÿ¹' => 'æ·¹',
  'Ÿº' => 'æ¸•',
  'Ÿ»' => 'æ¸Š',
  'Ÿ¼' => 'æ¶µ',
  'Ÿ½' => 'æ·‡',
  'Ÿ¾' => 'æ·¦',
  'Ÿ¿' => 'æ¶¸',
  'ŸÀ' => 'æ·†',
  'ŸÁ' => 'æ·¬',
  'ŸÂ' => 'æ·ž',
  'ŸÃ' => 'æ·Œ',
  'ŸÄ' => 'æ·¨',
  'ŸÅ' => 'æ·’',
  'ŸÆ' => 'æ·…',
  'ŸÇ' => 'æ·º',
  'ŸÈ' => 'æ·™',
  'ŸÉ' => 'æ·¤',
  'ŸÊ' => 'æ·•',
  'ŸË' => 'æ·ª',
  'ŸÌ' => 'æ·®',
  'ŸÍ' => 'æ¸­',
  'ŸÎ' => 'æ¹®',
  'ŸÏ' => 'æ¸®',
  'ŸÐ' => 'æ¸™',
  'ŸÑ' => 'æ¹²',
  'ŸÒ' => 'æ¹Ÿ',
  'ŸÓ' => 'æ¸¾',
  'ŸÔ' => 'æ¸£',
  'ŸÕ' => 'æ¹«',
  'ŸÖ' => 'æ¸«',
  'Ÿ×' => 'æ¹¶',
  'ŸØ' => 'æ¹�',
  'ŸÙ' => 'æ¸Ÿ',
  'ŸÚ' => 'æ¹ƒ',
  'ŸÛ' => 'æ¸º',
  'ŸÜ' => 'æ¹Ž',
  'ŸÝ' => 'æ¸¤',
  'ŸÞ' => 'æ»¿',
  'Ÿß' => 'æ¸�',
  'Ÿà' => 'æ¸¸',
  'Ÿá' => 'æº‚',
  'Ÿâ' => 'æºª',
  'Ÿã' => 'æº˜',
  'Ÿä' => 'æ»‰',
  'Ÿå' => 'æº·',
  'Ÿæ' => 'æ»“',
  'Ÿç' => 'æº½',
  'Ÿè' => 'æº¯',
  'Ÿé' => 'æ»„',
  'Ÿê' => 'æº²',
  'Ÿë' => 'æ»”',
  'Ÿì' => 'æ»•',
  'Ÿí' => 'æº�',
  'Ÿî' => 'æº¥',
  'Ÿï' => 'æ»‚',
  'Ÿð' => 'æºŸ',
  'Ÿñ' => 'æ½�',
  'Ÿò' => 'æ¼‘',
  'Ÿó' => 'ç�Œ',
  'Ÿô' => 'æ»¬',
  'Ÿõ' => 'æ»¸',
  'Ÿö' => 'æ»¾',
  'Ÿ÷' => 'æ¼¿',
  'Ÿø' => 'æ»²',
  'Ÿù' => 'æ¼±',
  'Ÿú' => 'æ»¯',
  'Ÿû' => 'æ¼²',
  'Ÿü' => 'æ»Œ',
  'à@' => 'æ¼¾',
  'àA' => 'æ¼“',
  'àB' => 'æ»·',
  'àC' => 'æ¾†',
  'àD' => 'æ½º',
  'àE' => 'æ½¸',
  'àF' => 'æ¾�',
  'àG' => 'æ¾€',
  'àH' => 'æ½¯',
  'àI' => 'æ½›',
  'àJ' => 'æ¿³',
  'àK' => 'æ½­',
  'àL' => 'æ¾‚',
  'àM' => 'æ½¼',
  'àN' => 'æ½˜',
  'àO' => 'æ¾Ž',
  'àP' => 'æ¾‘',
  'àQ' => 'æ¿‚',
  'àR' => 'æ½¦',
  'àS' => 'æ¾³',
  'àT' => 'æ¾£',
  'àU' => 'æ¾¡',
  'àV' => 'æ¾¤',
  'àW' => 'æ¾¹',
  'àX' => 'æ¿†',
  'àY' => 'æ¾ª',
  'àZ' => 'æ¿Ÿ',
  'à[' => 'æ¿•',
  'à\\' => 'æ¿¬',
  'à]' => 'æ¿”',
  'à^' => 'æ¿˜',
  'à_' => 'æ¿±',
  'à`' => 'æ¿®',
  'àa' => 'æ¿›',
  'àb' => 'ç€‰',
  'àc' => 'ç€‹',
  'àd' => 'æ¿º',
  'àe' => 'ç€‘',
  'àf' => 'ç€�',
  'àg' => 'ç€�',
  'àh' => 'æ¿¾',
  'ài' => 'ç€›',
  'àj' => 'ç€š',
  'àk' => 'æ½´',
  'àl' => 'ç€�',
  'àm' => 'ç€˜',
  'àn' => 'ç€Ÿ',
  'ào' => 'ç€°',
  'àp' => 'ç€¾',
  'àq' => 'ç€²',
  'àr' => 'ç�‘',
  'às' => 'ç�£',
  'àt' => 'ç‚™',
  'àu' => 'ç‚’',
  'àv' => 'ç‚¯',
  'àw' => 'çƒ±',
  'àx' => 'ç‚¬',
  'ày' => 'ç‚¸',
  'àz' => 'ç‚³',
  'à{' => 'ç‚®',
  'à|' => 'çƒŸ',
  'à}' => 'çƒ‹',
  'à~' => 'çƒ�',
  'à€' => 'çƒ™',
  'à�' => 'ç„‰',
  'à‚' => 'çƒ½',
  'àƒ' => 'ç„œ',
  'à„' => 'ç„™',
  'à…' => 'ç…¥',
  'à†' => 'ç…•',
  'à‡' => 'ç†ˆ',
  'àˆ' => 'ç…¦',
  'à‰' => 'ç…¢',
  'àŠ' => 'ç…Œ',
  'à‹' => 'ç…–',
  'àŒ' => 'ç…¬',
  'à�' => 'ç†�',
  'àŽ' => 'ç‡»',
  'à�' => 'ç†„',
  'à�' => 'ç†•',
  'à‘' => 'ç†¨',
  'à’' => 'ç†¬',
  'à“' => 'ç‡—',
  'à”' => 'ç†¹',
  'à•' => 'ç†¾',
  'à–' => 'ç‡’',
  'à—' => 'ç‡‰',
  'à˜' => 'ç‡”',
  'à™' => 'ç‡Ž',
  'àš' => 'ç‡ ',
  'à›' => 'ç‡¬',
  'àœ' => 'ç‡§',
  'à�' => 'ç‡µ',
  'àž' => 'ç‡¼',
  'àŸ' => 'ç‡¹',
  'à ' => 'ç‡¿',
  'à¡' => 'çˆ�',
  'à¢' => 'çˆ�',
  'à£' => 'çˆ›',
  'à¤' => 'çˆ¨',
  'à¥' => 'çˆ­',
  'à¦' => 'çˆ¬',
  'à§' => 'çˆ°',
  'à¨' => 'çˆ²',
  'à©' => 'çˆ»',
  'àª' => 'çˆ¼',
  'à«' => 'çˆ¿',
  'à¬' => 'ç‰€',
  'à­' => 'ç‰†',
  'à®' => 'ç‰‹',
  'à¯' => 'ç‰˜',
  'à°' => 'ç‰´',
  'à±' => 'ç‰¾',
  'à²' => 'çŠ‚',
  'à³' => 'çŠ�',
  'à´' => 'çŠ‡',
  'àµ' => 'çŠ’',
  'à¶' => 'çŠ–',
  'à·' => 'çŠ¢',
  'à¸' => 'çŠ§',
  'à¹' => 'çŠ¹',
  'àº' => 'çŠ²',
  'à»' => 'ç‹ƒ',
  'à¼' => 'ç‹†',
  'à½' => 'ç‹„',
  'à¾' => 'ç‹Ž',
  'à¿' => 'ç‹’',
  'àÀ' => 'ç‹¢',
  'àÁ' => 'ç‹ ',
  'àÂ' => 'ç‹¡',
  'àÃ' => 'ç‹¹',
  'àÄ' => 'ç‹·',
  'àÅ' => 'å€�',
  'àÆ' => 'çŒ—',
  'àÇ' => 'çŒŠ',
  'àÈ' => 'çŒœ',
  'àÉ' => 'çŒ–',
  'àÊ' => 'çŒ�',
  'àË' => 'çŒ´',
  'àÌ' => 'çŒ¯',
  'àÍ' => 'çŒ©',
  'àÎ' => 'çŒ¥',
  'àÏ' => 'çŒ¾',
  'àÐ' => 'ç�Ž',
  'àÑ' => 'ç��',
  'àÒ' => 'é»˜',
  'àÓ' => 'ç�—',
  'àÔ' => 'ç�ª',
  'àÕ' => 'ç�¨',
  'àÖ' => 'ç�°',
  'à×' => 'ç�¸',
  'àØ' => 'ç�µ',
  'àÙ' => 'ç�»',
  'àÚ' => 'ç�º',
  'àÛ' => 'ç�ˆ',
  'àÜ' => 'çŽ³',
  'àÝ' => 'ç�Ž',
  'àÞ' => 'çŽ»',
  'àß' => 'ç�€',
  'àà' => 'ç�¥',
  'àá' => 'ç�®',
  'àâ' => 'ç�ž',
  'àã' => 'ç’¢',
  'àä' => 'ç�…',
  'àå' => 'ç‘¯',
  'àæ' => 'ç�¥',
  'àç' => 'ç�¸',
  'àè' => 'ç�²',
  'àé' => 'ç�º',
  'àê' => 'ç‘•',
  'àë' => 'ç�¿',
  'àì' => 'ç‘Ÿ',
  'àí' => 'ç‘™',
  'àî' => 'ç‘�',
  'àï' => 'ç‘œ',
  'àð' => 'ç‘©',
  'àñ' => 'ç‘°',
  'àò' => 'ç‘£',
  'àó' => 'ç‘ª',
  'àô' => 'ç‘¶',
  'àõ' => 'ç‘¾',
  'àö' => 'ç’‹',
  'à÷' => 'ç’ž',
  'àø' => 'ç’§',
  'àù' => 'ç“Š',
  'àú' => 'ç“�',
  'àû' => 'ç“”',
  'àü' => 'ç�±',
  'á@' => 'ç“ ',
  'áA' => 'ç“£',
  'áB' => 'ç“§',
  'áC' => 'ç“©',
  'áD' => 'ç“®',
  'áE' => 'ç“²',
  'áF' => 'ç“°',
  'áG' => 'ç“±',
  'áH' => 'ç“¸',
  'áI' => 'ç“·',
  'áJ' => 'ç”„',
  'áK' => 'ç”ƒ',
  'áL' => 'ç”…',
  'áM' => 'ç”Œ',
  'áN' => 'ç”Ž',
  'áO' => 'ç”�',
  'áP' => 'ç”•',
  'áQ' => 'ç”“',
  'áR' => 'ç”ž',
  'áS' => 'ç”¦',
  'áT' => 'ç”¬',
  'áU' => 'ç”¼',
  'áV' => 'ç•„',
  'áW' => 'ç•�',
  'áX' => 'ç•Š',
  'áY' => 'ç•‰',
  'áZ' => 'ç•›',
  'á[' => 'ç•†',
  'á\\' => 'ç•š',
  'á]' => 'ç•©',
  'á^' => 'ç•¤',
  'á_' => 'ç•§',
  'á`' => 'ç•«',
  'áa' => 'ç•­',
  'áb' => 'ç•¸',
  'ác' => 'ç•¶',
  'ád' => 'ç–†',
  'áe' => 'ç–‡',
  'áf' => 'ç•´',
  'ág' => 'ç–Š',
  'áh' => 'ç–‰',
  'ái' => 'ç–‚',
  'áj' => 'ç–”',
  'ák' => 'ç–š',
  'ál' => 'ç–�',
  'ám' => 'ç–¥',
  'án' => 'ç–£',
  'áo' => 'ç—‚',
  'áp' => 'ç–³',
  'áq' => 'ç—ƒ',
  'ár' => 'ç–µ',
  'ás' => 'ç–½',
  'át' => 'ç–¸',
  'áu' => 'ç–¼',
  'áv' => 'ç–±',
  'áw' => 'ç—�',
  'áx' => 'ç—Š',
  'áy' => 'ç—’',
  'áz' => 'ç—™',
  'á{' => 'ç—£',
  'á|' => 'ç—ž',
  'á}' => 'ç—¾',
  'á~' => 'ç—¿',
  'á€' => 'ç—¼',
  'á�' => 'ç˜�',
  'á‚' => 'ç—°',
  'áƒ' => 'ç—º',
  'á„' => 'ç—²',
  'á…' => 'ç—³',
  'á†' => 'ç˜‹',
  'á‡' => 'ç˜�',
  'áˆ' => 'ç˜‰',
  'á‰' => 'ç˜Ÿ',
  'áŠ' => 'ç˜§',
  'á‹' => 'ç˜ ',
  'áŒ' => 'ç˜¡',
  'á�' => 'ç˜¢',
  'áŽ' => 'ç˜¤',
  'á�' => 'ç˜´',
  'á�' => 'ç˜°',
  'á‘' => 'ç˜»',
  'á’' => 'ç™‡',
  'á“' => 'ç™ˆ',
  'á”' => 'ç™†',
  'á•' => 'ç™œ',
  'á–' => 'ç™˜',
  'á—' => 'ç™¡',
  'á˜' => 'ç™¢',
  'á™' => 'ç™¨',
  'áš' => 'ç™©',
  'á›' => 'ç™ª',
  'áœ' => 'ç™§',
  'á�' => 'ç™¬',
  'áž' => 'ç™°',
  'áŸ' => 'ç™²',
  'á ' => 'ç™¶',
  'á¡' => 'ç™¸',
  'á¢' => 'ç™¼',
  'á£' => 'çš€',
  'á¤' => 'çšƒ',
  'á¥' => 'çšˆ',
  'á¦' => 'çš‹',
  'á§' => 'çšŽ',
  'á¨' => 'çš–',
  'á©' => 'çš“',
  'áª' => 'çš™',
  'á«' => 'çšš',
  'á¬' => 'çš°',
  'á­' => 'çš´',
  'á®' => 'çš¸',
  'á¯' => 'çš¹',
  'á°' => 'çšº',
  'á±' => 'ç›‚',
  'á²' => 'ç›�',
  'á³' => 'ç›–',
  'á´' => 'ç›’',
  'áµ' => 'ç›ž',
  'á¶' => 'ç›¡',
  'á·' => 'ç›¥',
  'á¸' => 'ç›§',
  'á¹' => 'ç›ª',
  'áº' => 'è˜¯',
  'á»' => 'ç›»',
  'á¼' => 'çœˆ',
  'á½' => 'çœ‡',
  'á¾' => 'çœ„',
  'á¿' => 'çœ©',
  'áÀ' => 'çœ¤',
  'áÁ' => 'çœž',
  'áÂ' => 'çœ¥',
  'áÃ' => 'çœ¦',
  'áÄ' => 'çœ›',
  'áÅ' => 'çœ·',
  'áÆ' => 'çœ¸',
  'áÇ' => 'ç�‡',
  'áÈ' => 'ç�š',
  'áÉ' => 'ç�¨',
  'áÊ' => 'ç�«',
  'áË' => 'ç�›',
  'áÌ' => 'ç�¥',
  'áÍ' => 'ç�¿',
  'áÎ' => 'ç�¾',
  'áÏ' => 'ç�¹',
  'áÐ' => 'çžŽ',
  'áÑ' => 'çž‹',
  'áÒ' => 'çž‘',
  'áÓ' => 'çž ',
  'áÔ' => 'çžž',
  'áÕ' => 'çž°',
  'áÖ' => 'çž¶',
  'á×' => 'çž¹',
  'áØ' => 'çž¿',
  'áÙ' => 'çž¼',
  'áÚ' => 'çž½',
  'áÛ' => 'çž»',
  'áÜ' => 'çŸ‡',
  'áÝ' => 'çŸ�',
  'áÞ' => 'çŸ—',
  'áß' => 'çŸš',
  'áà' => 'çŸœ',
  'áá' => 'çŸ£',
  'áâ' => 'çŸ®',
  'áã' => 'çŸ¼',
  'áä' => 'ç Œ',
  'áå' => 'ç ’',
  'áæ' => 'ç¤¦',
  'áç' => 'ç  ',
  'áè' => 'ç¤ª',
  'áé' => 'ç¡…',
  'áê' => 'ç¢Ž',
  'áë' => 'ç¡´',
  'áì' => 'ç¢†',
  'áí' => 'ç¡¼',
  'áî' => 'ç¢š',
  'áï' => 'ç¢Œ',
  'áð' => 'ç¢£',
  'áñ' => 'ç¢µ',
  'áò' => 'ç¢ª',
  'áó' => 'ç¢¯',
  'áô' => 'ç£‘',
  'áõ' => 'ç£†',
  'áö' => 'ç£‹',
  'á÷' => 'ç£”',
  'áø' => 'ç¢¾',
  'áù' => 'ç¢¼',
  'áú' => 'ç£…',
  'áû' => 'ç£Š',
  'áü' => 'ç£¬',
  'â@' => 'ç£§',
  'âA' => 'ç£š',
  'âB' => 'ç£½',
  'âC' => 'ç£´',
  'âD' => 'ç¤‡',
  'âE' => 'ç¤’',
  'âF' => 'ç¤‘',
  'âG' => 'ç¤™',
  'âH' => 'ç¤¬',
  'âI' => 'ç¤«',
  'âJ' => 'ç¥€',
  'âK' => 'ç¥ ',
  'âL' => 'ç¥—',
  'âM' => 'ç¥Ÿ',
  'âN' => 'ç¥š',
  'âO' => 'ç¥•',
  'âP' => 'ç¥“',
  'âQ' => 'ç¥º',
  'âR' => 'ç¥¿',
  'âS' => 'ç¦Š',
  'âT' => 'ç¦�',
  'âU' => 'ç¦§',
  'âV' => 'é½‹',
  'âW' => 'ç¦ª',
  'âX' => 'ç¦®',
  'âY' => 'ç¦³',
  'âZ' => 'ç¦¹',
  'â[' => 'ç¦º',
  'â\\' => 'ç§‰',
  'â]' => 'ç§•',
  'â^' => 'ç§§',
  'â_' => 'ç§¬',
  'â`' => 'ç§¡',
  'âa' => 'ç§£',
  'âb' => 'ç¨ˆ',
  'âc' => 'ç¨�',
  'âd' => 'ç¨˜',
  'âe' => 'ç¨™',
  'âf' => 'ç¨ ',
  'âg' => 'ç¨Ÿ',
  'âh' => 'ç¦€',
  'âi' => 'ç¨±',
  'âj' => 'ç¨»',
  'âk' => 'ç¨¾',
  'âl' => 'ç¨·',
  'âm' => 'ç©ƒ',
  'ân' => 'ç©—',
  'âo' => 'ç©‰',
  'âp' => 'ç©¡',
  'âq' => 'ç©¢',
  'âr' => 'ç©©',
  'âs' => 'é¾�',
  'ât' => 'ç©°',
  'âu' => 'ç©¹',
  'âv' => 'ç©½',
  'âw' => 'çªˆ',
  'âx' => 'çª—',
  'ây' => 'çª•',
  'âz' => 'çª˜',
  'â{' => 'çª–',
  'â|' => 'çª©',
  'â}' => 'ç«ˆ',
  'â~' => 'çª°',
  'â€' => 'çª¶',
  'â�' => 'ç«…',
  'â‚' => 'ç«„',
  'âƒ' => 'çª¿',
  'â„' => 'é‚ƒ',
  'â…' => 'ç«‡',
  'â†' => 'ç«Š',
  'â‡' => 'ç«�',
  'âˆ' => 'ç«�',
  'â‰' => 'ç«•',
  'âŠ' => 'ç«“',
  'â‹' => 'ç«™',
  'âŒ' => 'ç«š',
  'â�' => 'ç«�',
  'âŽ' => 'ç«¡',
  'â�' => 'ç«¢',
  'â�' => 'ç«¦',
  'â‘' => 'ç«­',
  'â’' => 'ç«°',
  'â“' => 'ç¬‚',
  'â”' => 'ç¬�',
  'â•' => 'ç¬Š',
  'â–' => 'ç¬†',
  'â—' => 'ç¬³',
  'â˜' => 'ç¬˜',
  'â™' => 'ç¬™',
  'âš' => 'ç¬ž',
  'â›' => 'ç¬µ',
  'âœ' => 'ç¬¨',
  'â�' => 'ç¬¶',
  'âž' => 'ç­�',
  'âŸ' => 'ç­º',
  'â ' => 'ç¬„',
  'â¡' => 'ç­�',
  'â¢' => 'ç¬‹',
  'â£' => 'ç­Œ',
  'â¤' => 'ç­…',
  'â¥' => 'ç­µ',
  'â¦' => 'ç­¥',
  'â§' => 'ç­´',
  'â¨' => 'ç­§',
  'â©' => 'ç­°',
  'âª' => 'ç­±',
  'â«' => 'ç­¬',
  'â¬' => 'ç­®',
  'â­' => 'ç®�',
  'â®' => 'ç®˜',
  'â¯' => 'ç®Ÿ',
  'â°' => 'ç®�',
  'â±' => 'ç®œ',
  'â²' => 'ç®š',
  'â³' => 'ç®‹',
  'â´' => 'ç®’',
  'âµ' => 'ç®�',
  'â¶' => 'ç­�',
  'â·' => 'ç®™',
  'â¸' => 'ç¯‹',
  'â¹' => 'ç¯�',
  'âº' => 'ç¯Œ',
  'â»' => 'ç¯�',
  'â¼' => 'ç®´',
  'â½' => 'ç¯†',
  'â¾' => 'ç¯�',
  'â¿' => 'ç¯©',
  'âÀ' => 'ç°‘',
  'âÁ' => 'ç°”',
  'âÂ' => 'ç¯¦',
  'âÃ' => 'ç¯¥',
  'âÄ' => 'ç± ',
  'âÅ' => 'ç°€',
  'âÆ' => 'ç°‡',
  'âÇ' => 'ç°“',
  'âÈ' => 'ç¯³',
  'âÉ' => 'ç¯·',
  'âÊ' => 'ç°—',
  'âË' => 'ç°�',
  'âÌ' => 'ç¯¶',
  'âÍ' => 'ç°£',
  'âÎ' => 'ç°§',
  'âÏ' => 'ç°ª',
  'âÐ' => 'ç°Ÿ',
  'âÑ' => 'ç°·',
  'âÒ' => 'ç°«',
  'âÓ' => 'ç°½',
  'âÔ' => 'ç±Œ',
  'âÕ' => 'ç±ƒ',
  'âÖ' => 'ç±”',
  'â×' => 'ç±�',
  'âØ' => 'ç±€',
  'âÙ' => 'ç±�',
  'âÚ' => 'ç±˜',
  'âÛ' => 'ç±Ÿ',
  'âÜ' => 'ç±¤',
  'âÝ' => 'ç±–',
  'âÞ' => 'ç±¥',
  'âß' => 'ç±¬',
  'âà' => 'ç±µ',
  'âá' => 'ç²ƒ',
  'ââ' => 'ç²�',
  'âã' => 'ç²¤',
  'âä' => 'ç²­',
  'âå' => 'ç²¢',
  'âæ' => 'ç²«',
  'âç' => 'ç²¡',
  'âè' => 'ç²¨',
  'âé' => 'ç²³',
  'âê' => 'ç²²',
  'âë' => 'ç²±',
  'âì' => 'ç²®',
  'âí' => 'ç²¹',
  'âî' => 'ç²½',
  'âï' => 'ç³€',
  'âð' => 'ç³…',
  'âñ' => 'ç³‚',
  'âò' => 'ç³˜',
  'âó' => 'ç³’',
  'âô' => 'ç³œ',
  'âõ' => 'ç³¢',
  'âö' => 'é¬»',
  'â÷' => 'ç³¯',
  'âø' => 'ç³²',
  'âù' => 'ç³´',
  'âú' => 'ç³¶',
  'âû' => 'ç³º',
  'âü' => 'ç´†',
  'ã@' => 'ç´‚',
  'ãA' => 'ç´œ',
  'ãB' => 'ç´•',
  'ãC' => 'ç´Š',
  'ãD' => 'çµ…',
  'ãE' => 'çµ‹',
  'ãF' => 'ç´®',
  'ãG' => 'ç´²',
  'ãH' => 'ç´¿',
  'ãI' => 'ç´µ',
  'ãJ' => 'çµ†',
  'ãK' => 'çµ³',
  'ãL' => 'çµ–',
  'ãM' => 'çµŽ',
  'ãN' => 'çµ²',
  'ãO' => 'çµ¨',
  'ãP' => 'çµ®',
  'ãQ' => 'çµ�',
  'ãR' => 'çµ£',
  'ãS' => 'ç¶“',
  'ãT' => 'ç¶‰',
  'ãU' => 'çµ›',
  'ãV' => 'ç¶�',
  'ãW' => 'çµ½',
  'ãX' => 'ç¶›',
  'ãY' => 'ç¶º',
  'ãZ' => 'ç¶®',
  'ã[' => 'ç¶£',
  'ã\\' => 'ç¶µ',
  'ã]' => 'ç·‡',
  'ã^' => 'ç¶½',
  'ã_' => 'ç¶«',
  'ã`' => 'ç¸½',
  'ãa' => 'ç¶¢',
  'ãb' => 'ç¶¯',
  'ãc' => 'ç·œ',
  'ãd' => 'ç¶¸',
  'ãe' => 'ç¶Ÿ',
  'ãf' => 'ç¶°',
  'ãg' => 'ç·˜',
  'ãh' => 'ç·�',
  'ãi' => 'ç·¤',
  'ãj' => 'ç·ž',
  'ãk' => 'ç·»',
  'ãl' => 'ç·²',
  'ãm' => 'ç·¡',
  'ãn' => 'ç¸…',
  'ão' => 'ç¸Š',
  'ãp' => 'ç¸£',
  'ãq' => 'ç¸¡',
  'ãr' => 'ç¸’',
  'ãs' => 'ç¸±',
  'ãt' => 'ç¸Ÿ',
  'ãu' => 'ç¸‰',
  'ãv' => 'ç¸‹',
  'ãw' => 'ç¸¢',
  'ãx' => 'ç¹†',
  'ãy' => 'ç¹¦',
  'ãz' => 'ç¸»',
  'ã{' => 'ç¸µ',
  'ã|' => 'ç¸¹',
  'ã}' => 'ç¹ƒ',
  'ã~' => 'ç¸·',
  'ã€' => 'ç¸²',
  'ã�' => 'ç¸º',
  'ã‚' => 'ç¹§',
  'ãƒ' => 'ç¹�',
  'ã„' => 'ç¹–',
  'ã…' => 'ç¹ž',
  'ã†' => 'ç¹™',
  'ã‡' => 'ç¹š',
  'ãˆ' => 'ç¹¹',
  'ã‰' => 'ç¹ª',
  'ãŠ' => 'ç¹©',
  'ã‹' => 'ç¹¼',
  'ãŒ' => 'ç¹»',
  'ã�' => 'çºƒ',
  'ãŽ' => 'ç·•',
  'ã�' => 'ç¹½',
  'ã�' => 'è¾®',
  'ã‘' => 'ç¹¿',
  'ã’' => 'çºˆ',
  'ã“' => 'çº‰',
  'ã”' => 'çºŒ',
  'ã•' => 'çº’',
  'ã–' => 'çº�',
  'ã—' => 'çº“',
  'ã˜' => 'çº”',
  'ã™' => 'çº–',
  'ãš' => 'çºŽ',
  'ã›' => 'çº›',
  'ãœ' => 'çºœ',
  'ã�' => 'ç¼¸',
  'ãž' => 'ç¼º',
  'ãŸ' => 'ç½…',
  'ã ' => 'ç½Œ',
  'ã¡' => 'ç½�',
  'ã¢' => 'ç½Ž',
  'ã£' => 'ç½�',
  'ã¤' => 'ç½‘',
  'ã¥' => 'ç½•',
  'ã¦' => 'ç½”',
  'ã§' => 'ç½˜',
  'ã¨' => 'ç½Ÿ',
  'ã©' => 'ç½ ',
  'ãª' => 'ç½¨',
  'ã«' => 'ç½©',
  'ã¬' => 'ç½§',
  'ã­' => 'ç½¸',
  'ã®' => 'ç¾‚',
  'ã¯' => 'ç¾†',
  'ã°' => 'ç¾ƒ',
  'ã±' => 'ç¾ˆ',
  'ã²' => 'ç¾‡',
  'ã³' => 'ç¾Œ',
  'ã´' => 'ç¾”',
  'ãµ' => 'ç¾ž',
  'ã¶' => 'ç¾�',
  'ã·' => 'ç¾š',
  'ã¸' => 'ç¾£',
  'ã¹' => 'ç¾¯',
  'ãº' => 'ç¾²',
  'ã»' => 'ç¾¹',
  'ã¼' => 'ç¾®',
  'ã½' => 'ç¾¶',
  'ã¾' => 'ç¾¸',
  'ã¿' => 'è­±',
  'ãÀ' => 'ç¿…',
  'ãÁ' => 'ç¿†',
  'ãÂ' => 'ç¿Š',
  'ãÃ' => 'ç¿•',
  'ãÄ' => 'ç¿”',
  'ãÅ' => 'ç¿¡',
  'ãÆ' => 'ç¿¦',
  'ãÇ' => 'ç¿©',
  'ãÈ' => 'ç¿³',
  'ãÉ' => 'ç¿¹',
  'ãÊ' => 'é£œ',
  'ãË' => 'è€†',
  'ãÌ' => 'è€„',
  'ãÍ' => 'è€‹',
  'ãÎ' => 'è€’',
  'ãÏ' => 'è€˜',
  'ãÐ' => 'è€™',
  'ãÑ' => 'è€œ',
  'ãÒ' => 'è€¡',
  'ãÓ' => 'è€¨',
  'ãÔ' => 'è€¿',
  'ãÕ' => 'è€»',
  'ãÖ' => 'è�Š',
  'ã×' => 'è�†',
  'ãØ' => 'è�’',
  'ãÙ' => 'è�˜',
  'ãÚ' => 'è�š',
  'ãÛ' => 'è�Ÿ',
  'ãÜ' => 'è�¢',
  'ãÝ' => 'è�¨',
  'ãÞ' => 'è�³',
  'ãß' => 'è�²',
  'ãà' => 'è�°',
  'ãá' => 'è�¶',
  'ãâ' => 'è�¹',
  'ãã' => 'è�½',
  'ãä' => 'è�¿',
  'ãå' => 'è‚„',
  'ãæ' => 'è‚†',
  'ãç' => 'è‚…',
  'ãè' => 'è‚›',
  'ãé' => 'è‚“',
  'ãê' => 'è‚š',
  'ãë' => 'è‚­',
  'ãì' => 'å†�',
  'ãí' => 'è‚¬',
  'ãî' => 'èƒ›',
  'ãï' => 'èƒ¥',
  'ãð' => 'èƒ™',
  'ãñ' => 'èƒ�',
  'ãò' => 'èƒ„',
  'ãó' => 'èƒš',
  'ãô' => 'èƒ–',
  'ãõ' => 'è„‰',
  'ãö' => 'èƒ¯',
  'ã÷' => 'èƒ±',
  'ãø' => 'è„›',
  'ãù' => 'è„©',
  'ãú' => 'è„£',
  'ãû' => 'è„¯',
  'ãü' => 'è…‹',
  'ä@' => 'éš‹',
  'äA' => 'è…†',
  'äB' => 'è„¾',
  'äC' => 'è…“',
  'äD' => 'è…‘',
  'äE' => 'èƒ¼',
  'äF' => 'è…±',
  'äG' => 'è…®',
  'äH' => 'è…¥',
  'äI' => 'è…¦',
  'äJ' => 'è…´',
  'äK' => 'è†ƒ',
  'äL' => 'è†ˆ',
  'äM' => 'è†Š',
  'äN' => 'è†€',
  'äO' => 'è†‚',
  'äP' => 'è† ',
  'äQ' => 'è†•',
  'äR' => 'è†¤',
  'äS' => 'è†£',
  'äT' => 'è…Ÿ',
  'äU' => 'è†“',
  'äV' => 'è†©',
  'äW' => 'è†°',
  'äX' => 'è†µ',
  'äY' => 'è†¾',
  'äZ' => 'è†¸',
  'ä[' => 'è†½',
  'ä\\' => 'è‡€',
  'ä]' => 'è‡‚',
  'ä^' => 'è†º',
  'ä_' => 'è‡‰',
  'ä`' => 'è‡�',
  'äa' => 'è‡‘',
  'äb' => 'è‡™',
  'äc' => 'è‡˜',
  'äd' => 'è‡ˆ',
  'äe' => 'è‡š',
  'äf' => 'è‡Ÿ',
  'äg' => 'è‡ ',
  'äh' => 'è‡§',
  'äi' => 'è‡º',
  'äj' => 'è‡»',
  'äk' => 'è‡¾',
  'äl' => 'èˆ�',
  'äm' => 'èˆ‚',
  'än' => 'èˆ…',
  'äo' => 'èˆ‡',
  'äp' => 'èˆŠ',
  'äq' => 'èˆ�',
  'är' => 'èˆ�',
  'äs' => 'èˆ–',
  'ät' => 'èˆ©',
  'äu' => 'èˆ«',
  'äv' => 'èˆ¸',
  'äw' => 'èˆ³',
  'äx' => 'è‰€',
  'äy' => 'è‰™',
  'äz' => 'è‰˜',
  'ä{' => 'è‰�',
  'ä|' => 'è‰š',
  'ä}' => 'è‰Ÿ',
  'ä~' => 'è‰¤',
  'ä€' => 'è‰¢',
  'ä�' => 'è‰¨',
  'ä‚' => 'è‰ª',
  'äƒ' => 'è‰«',
  'ä„' => 'èˆ®',
  'ä…' => 'è‰±',
  'ä†' => 'è‰·',
  'ä‡' => 'è‰¸',
  'äˆ' => 'è‰¾',
  'ä‰' => 'èŠ�',
  'äŠ' => 'èŠ’',
  'ä‹' => 'èŠ«',
  'äŒ' => 'èŠŸ',
  'ä�' => 'èŠ»',
  'äŽ' => 'èŠ¬',
  'ä�' => 'è‹¡',
  'ä�' => 'è‹£',
  'ä‘' => 'è‹Ÿ',
  'ä’' => 'è‹’',
  'ä“' => 'è‹´',
  'ä”' => 'è‹³',
  'ä•' => 'è‹º',
  'ä–' => 'èŽ“',
  'ä—' => 'èŒƒ',
  'ä˜' => 'è‹»',
  'ä™' => 'è‹¹',
  'äš' => 'è‹ž',
  'ä›' => 'èŒ†',
  'äœ' => 'è‹œ',
  'ä�' => 'èŒ‰',
  'äž' => 'è‹™',
  'äŸ' => 'èŒµ',
  'ä ' => 'èŒ´',
  'ä¡' => 'èŒ–',
  'ä¢' => 'èŒ²',
  'ä£' => 'èŒ±',
  'ä¤' => 'è�€',
  'ä¥' => 'èŒ¹',
  'ä¦' => 'è��',
  'ä§' => 'è�…',
  'ä¨' => 'èŒ¯',
  'ä©' => 'èŒ«',
  'äª' => 'èŒ—',
  'ä«' => 'èŒ˜',
  'ä¬' => 'èŽ…',
  'ä­' => 'èŽš',
  'ä®' => 'èŽª',
  'ä¯' => 'èŽŸ',
  'ä°' => 'èŽ¢',
  'ä±' => 'èŽ–',
  'ä²' => 'èŒ£',
  'ä³' => 'èŽŽ',
  'ä´' => 'èŽ‡',
  'äµ' => 'èŽŠ',
  'ä¶' => 'è�¼',
  'ä·' => 'èŽµ',
  'ä¸' => 'è�³',
  'ä¹' => 'è�µ',
  'äº' => 'èŽ ',
  'ä»' => 'èŽ‰',
  'ä¼' => 'èŽ¨',
  'ä½' => 'è�´',
  'ä¾' => 'è�“',
  'ä¿' => 'è�«',
  'äÀ' => 'è�Ž',
  'äÁ' => 'è�½',
  'äÂ' => 'è�ƒ',
  'äÃ' => 'è�˜',
  'äÄ' => 'è�‹',
  'äÅ' => 'è��',
  'äÆ' => 'è�·',
  'äÇ' => 'è�‡',
  'äÈ' => 'è� ',
  'äÉ' => 'è�²',
  'äÊ' => 'è��',
  'äË' => 'è�¢',
  'äÌ' => 'è� ',
  'äÍ' => 'èŽ½',
  'äÎ' => 'è�¸',
  'äÏ' => 'è”†',
  'äÐ' => 'è�»',
  'äÑ' => 'è‘­',
  'äÒ' => 'è�ª',
  'äÓ' => 'è�¼',
  'äÔ' => 'è•š',
  'äÕ' => 'è’„',
  'äÖ' => 'è‘·',
  'ä×' => 'è‘«',
  'äØ' => 'è’­',
  'äÙ' => 'è‘®',
  'äÚ' => 'è’‚',
  'äÛ' => 'è‘©',
  'äÜ' => 'è‘†',
  'äÝ' => 'è�¬',
  'äÞ' => 'è‘¯',
  'äß' => 'è‘¹',
  'äà' => 'è�µ',
  'äá' => 'è“Š',
  'äâ' => 'è‘¢',
  'äã' => 'è’¹',
  'ää' => 'è’¿',
  'äå' => 'è’Ÿ',
  'äæ' => 'è“™',
  'äç' => 'è“�',
  'äè' => 'è’»',
  'äé' => 'è“š',
  'äê' => 'è“�',
  'äë' => 'è“�',
  'äì' => 'è“†',
  'äí' => 'è“–',
  'äî' => 'è’¡',
  'äï' => 'è”¡',
  'äð' => 'è“¿',
  'äñ' => 'è“´',
  'äò' => 'è”—',
  'äó' => 'è”˜',
  'äô' => 'è”¬',
  'äõ' => 'è”Ÿ',
  'äö' => 'è”•',
  'ä÷' => 'è””',
  'äø' => 'è“¼',
  'äù' => 'è•€',
  'äú' => 'è•£',
  'äû' => 'è•˜',
  'äü' => 'è•ˆ',
  'å@' => 'è•�',
  'åA' => 'è˜‚',
  'åB' => 'è•‹',
  'åC' => 'è••',
  'åD' => 'è–€',
  'åE' => 'è–¤',
  'åF' => 'è–ˆ',
  'åG' => 'è–‘',
  'åH' => 'è–Š',
  'åI' => 'è–¨',
  'åJ' => 'è•­',
  'åK' => 'è–”',
  'åL' => 'è–›',
  'åM' => 'è—ª',
  'åN' => 'è–‡',
  'åO' => 'è–œ',
  'åP' => 'è•·',
  'åQ' => 'è•¾',
  'åR' => 'è–�',
  'åS' => 'è—‰',
  'åT' => 'è–º',
  'åU' => 'è—�',
  'åV' => 'è–¹',
  'åW' => 'è—�',
  'åX' => 'è—•',
  'åY' => 'è—�',
  'åZ' => 'è—¥',
  'å[' => 'è—œ',
  'å\\' => 'è—¹',
  'å]' => 'è˜Š',
  'å^' => 'è˜“',
  'å_' => 'è˜‹',
  'å`' => 'è—¾',
  'åa' => 'è—º',
  'åb' => 'è˜†',
  'åc' => 'è˜¢',
  'åd' => 'è˜š',
  'åe' => 'è˜°',
  'åf' => 'è˜¿',
  'åg' => 'è™�',
  'åh' => 'ä¹•',
  'åi' => 'è™”',
  'åj' => 'è™Ÿ',
  'åk' => 'è™§',
  'ål' => 'è™±',
  'åm' => 'èš“',
  'ån' => 'èš£',
  'åo' => 'èš©',
  'åp' => 'èšª',
  'åq' => 'èš‹',
  'år' => 'èšŒ',
  'ås' => 'èš¶',
  'åt' => 'èš¯',
  'åu' => 'è›„',
  'åv' => 'è›†',
  'åw' => 'èš°',
  'åx' => 'è›‰',
  'åy' => 'è £',
  'åz' => 'èš«',
  'å{' => 'è›”',
  'å|' => 'è›ž',
  'å}' => 'è›©',
  'å~' => 'è›¬',
  'å€' => 'è›Ÿ',
  'å�' => 'è››',
  'å‚' => 'è›¯',
  'åƒ' => 'èœ’',
  'å„' => 'èœ†',
  'å…' => 'èœˆ',
  'å†' => 'èœ€',
  'å‡' => 'èœƒ',
  'åˆ' => 'è›»',
  'å‰' => 'èœ‘',
  'åŠ' => 'èœ‰',
  'å‹' => 'èœ�',
  'åŒ' => 'è›¹',
  'å�' => 'èœŠ',
  'åŽ' => 'èœ´',
  'å�' => 'èœ¿',
  'å�' => 'èœ·',
  'å‘' => 'èœ»',
  'å’' => 'èœ¥',
  'å“' => 'èœ©',
  'å”' => 'èœš',
  'å•' => 'è� ',
  'å–' => 'è�Ÿ',
  'å—' => 'è�¸',
  'å˜' => 'è�Œ',
  'å™' => 'è�Ž',
  'åš' => 'è�´',
  'å›' => 'è�—',
  'åœ' => 'è�¨',
  'å�' => 'è�®',
  'åž' => 'è�™',
  'åŸ' => 'è�“',
  'å ' => 'è�£',
  'å¡' => 'è�ª',
  'å¢' => 'è …',
  'å£' => 'èž¢',
  'å¤' => 'èžŸ',
  'å¥' => 'èž‚',
  'å¦' => 'èž¯',
  'å§' => 'èŸ‹',
  'å¨' => 'èž½',
  'å©' => 'èŸ€',
  'åª' => 'èŸ�',
  'å«' => 'é›–',
  'å¬' => 'èž«',
  'å­' => 'èŸ„',
  'å®' => 'èž³',
  'å¯' => 'èŸ‡',
  'å°' => 'èŸ†',
  'å±' => 'èž»',
  'å²' => 'èŸ¯',
  'å³' => 'èŸ²',
  'å´' => 'èŸ ',
  'åµ' => 'è �',
  'å¶' => 'è �',
  'å·' => 'èŸ¾',
  'å¸' => 'èŸ¶',
  'å¹' => 'èŸ·',
  'åº' => 'è Ž',
  'å»' => 'èŸ’',
  'å¼' => 'è ‘',
  'å½' => 'è –',
  'å¾' => 'è •',
  'å¿' => 'è ¢',
  'åÀ' => 'è ¡',
  'åÁ' => 'è ±',
  'åÂ' => 'è ¶',
  'åÃ' => 'è ¹',
  'åÄ' => 'è §',
  'åÅ' => 'è »',
  'åÆ' => 'è¡„',
  'åÇ' => 'è¡‚',
  'åÈ' => 'è¡’',
  'åÉ' => 'è¡™',
  'åÊ' => 'è¡ž',
  'åË' => 'è¡¢',
  'åÌ' => 'è¡«',
  'åÍ' => 'è¢�',
  'åÎ' => 'è¡¾',
  'åÏ' => 'è¢ž',
  'åÐ' => 'è¡µ',
  'åÑ' => 'è¡½',
  'åÒ' => 'è¢µ',
  'åÓ' => 'è¡²',
  'åÔ' => 'è¢‚',
  'åÕ' => 'è¢—',
  'åÖ' => 'è¢’',
  'å×' => 'è¢®',
  'åØ' => 'è¢™',
  'åÙ' => 'è¢¢',
  'åÚ' => 'è¢�',
  'åÛ' => 'è¢¤',
  'åÜ' => 'è¢°',
  'åÝ' => 'è¢¿',
  'åÞ' => 'è¢±',
  'åß' => 'è£ƒ',
  'åà' => 'è£„',
  'åá' => 'è£”',
  'åâ' => 'è£˜',
  'åã' => 'è£™',
  'åä' => 'è£�',
  'åå' => 'è£¹',
  'åæ' => 'è¤‚',
  'åç' => 'è£¼',
  'åè' => 'è£´',
  'åé' => 'è£¨',
  'åê' => 'è£²',
  'åë' => 'è¤„',
  'åì' => 'è¤Œ',
  'åí' => 'è¤Š',
  'åî' => 'è¤“',
  'åï' => 'è¥ƒ',
  'åð' => 'è¤ž',
  'åñ' => 'è¤¥',
  'åò' => 'è¤ª',
  'åó' => 'è¤«',
  'åô' => 'è¥�',
  'åõ' => 'è¥„',
  'åö' => 'è¤»',
  'å÷' => 'è¤¶',
  'åø' => 'è¤¸',
  'åù' => 'è¥Œ',
  'åú' => 'è¤�',
  'åû' => 'è¥ ',
  'åü' => 'è¥ž',
  'æ@' => 'è¥¦',
  'æA' => 'è¥¤',
  'æB' => 'è¥­',
  'æC' => 'è¥ª',
  'æD' => 'è¥¯',
  'æE' => 'è¥´',
  'æF' => 'è¥·',
  'æG' => 'è¥¾',
  'æH' => 'è¦ƒ',
  'æI' => 'è¦ˆ',
  'æJ' => 'è¦Š',
  'æK' => 'è¦“',
  'æL' => 'è¦˜',
  'æM' => 'è¦¡',
  'æN' => 'è¦©',
  'æO' => 'è¦¦',
  'æP' => 'è¦¬',
  'æQ' => 'è¦¯',
  'æR' => 'è¦²',
  'æS' => 'è¦º',
  'æT' => 'è¦½',
  'æU' => 'è¦¿',
  'æV' => 'è§€',
  'æW' => 'è§š',
  'æX' => 'è§œ',
  'æY' => 'è§�',
  'æZ' => 'è§§',
  'æ[' => 'è§´',
  'æ\\' => 'è§¸',
  'æ]' => 'è¨ƒ',
  'æ^' => 'è¨–',
  'æ_' => 'è¨�',
  'æ`' => 'è¨Œ',
  'æa' => 'è¨›',
  'æb' => 'è¨�',
  'æc' => 'è¨¥',
  'æd' => 'è¨¶',
  'æe' => 'è©�',
  'æf' => 'è©›',
  'æg' => 'è©’',
  'æh' => 'è©†',
  'æi' => 'è©ˆ',
  'æj' => 'è©¼',
  'æk' => 'è©­',
  'æl' => 'è©¬',
  'æm' => 'è©¢',
  'æn' => 'èª…',
  'æo' => 'èª‚',
  'æp' => 'èª„',
  'æq' => 'èª¨',
  'ær' => 'èª¡',
  'æs' => 'èª‘',
  'æt' => 'èª¥',
  'æu' => 'èª¦',
  'æv' => 'èªš',
  'æw' => 'èª£',
  'æx' => 'è«„',
  'æy' => 'è«�',
  'æz' => 'è«‚',
  'æ{' => 'è«š',
  'æ|' => 'è««',
  'æ}' => 'è«³',
  'æ~' => 'è«§',
  'æ€' => 'è«¤',
  'æ�' => 'è«±',
  'æ‚' => 'è¬”',
  'æƒ' => 'è« ',
  'æ„' => 'è«¢',
  'æ…' => 'è«·',
  'æ†' => 'è«ž',
  'æ‡' => 'è«›',
  'æˆ' => 'è¬Œ',
  'æ‰' => 'è¬‡',
  'æŠ' => 'è¬š',
  'æ‹' => 'è«¡',
  'æŒ' => 'è¬–',
  'æ�' => 'è¬�',
  'æŽ' => 'è¬—',
  'æ�' => 'è¬ ',
  'æ�' => 'è¬³',
  'æ‘' => 'éž«',
  'æ’' => 'è¬¦',
  'æ“' => 'è¬«',
  'æ”' => 'è¬¾',
  'æ•' => 'è¬¨',
  'æ–' => 'è­�',
  'æ—' => 'è­Œ',
  'æ˜' => 'è­�',
  'æ™' => 'è­Ž',
  'æš' => 'è­‰',
  'æ›' => 'è­–',
  'æœ' => 'è­›',
  'æ�' => 'è­š',
  'æž' => 'è­«',
  'æŸ' => 'è­Ÿ',
  'æ ' => 'è­¬',
  'æ¡' => 'è­¯',
  'æ¢' => 'è­´',
  'æ£' => 'è­½',
  'æ¤' => 'è®€',
  'æ¥' => 'è®Œ',
  'æ¦' => 'è®Ž',
  'æ§' => 'è®’',
  'æ¨' => 'è®“',
  'æ©' => 'è®–',
  'æª' => 'è®™',
  'æ«' => 'è®š',
  'æ¬' => 'è°º',
  'æ­' => 'è±�',
  'æ®' => 'è°¿',
  'æ¯' => 'è±ˆ',
  'æ°' => 'è±Œ',
  'æ±' => 'è±Ž',
  'æ²' => 'è±�',
  'æ³' => 'è±•',
  'æ´' => 'è±¢',
  'æµ' => 'è±¬',
  'æ¶' => 'è±¸',
  'æ·' => 'è±º',
  'æ¸' => 'è²‚',
  'æ¹' => 'è²‰',
  'æº' => 'è²…',
  'æ»' => 'è²Š',
  'æ¼' => 'è²�',
  'æ½' => 'è²Ž',
  'æ¾' => 'è²”',
  'æ¿' => 'è±¼',
  'æÀ' => 'è²˜',
  'æÁ' => 'æˆ�',
  'æÂ' => 'è²­',
  'æÃ' => 'è²ª',
  'æÄ' => 'è²½',
  'æÅ' => 'è²²',
  'æÆ' => 'è²³',
  'æÇ' => 'è²®',
  'æÈ' => 'è²¶',
  'æÉ' => 'è³ˆ',
  'æÊ' => 'è³�',
  'æË' => 'è³¤',
  'æÌ' => 'è³£',
  'æÍ' => 'è³š',
  'æÎ' => 'è³½',
  'æÏ' => 'è³º',
  'æÐ' => 'è³»',
  'æÑ' => 'è´„',
  'æÒ' => 'è´…',
  'æÓ' => 'è´Š',
  'æÔ' => 'è´‡',
  'æÕ' => 'è´�',
  'æÖ' => 'è´�',
  'æ×' => 'è´�',
  'æØ' => 'é½Ž',
  'æÙ' => 'è´“',
  'æÚ' => 'è³�',
  'æÛ' => 'è´”',
  'æÜ' => 'è´–',
  'æÝ' => 'èµ§',
  'æÞ' => 'èµ­',
  'æß' => 'èµ±',
  'æà' => 'èµ³',
  'æá' => 'è¶�',
  'æâ' => 'è¶™',
  'æã' => 'è·‚',
  'æä' => 'è¶¾',
  'æå' => 'è¶º',
  'ææ' => 'è·�',
  'æç' => 'è·š',
  'æè' => 'è·–',
  'æé' => 'è·Œ',
  'æê' => 'è·›',
  'æë' => 'è·‹',
  'æì' => 'è·ª',
  'æí' => 'è·«',
  'æî' => 'è·Ÿ',
  'æï' => 'è·£',
  'æð' => 'è·¼',
  'æñ' => 'è¸ˆ',
  'æò' => 'è¸‰',
  'æó' => 'è·¿',
  'æô' => 'è¸�',
  'æõ' => 'è¸ž',
  'æö' => 'è¸�',
  'æ÷' => 'è¸Ÿ',
  'æø' => 'è¹‚',
  'æù' => 'è¸µ',
  'æú' => 'è¸°',
  'æû' => 'è¸´',
  'æü' => 'è¹Š',
  'ç@' => 'è¹‡',
  'çA' => 'è¹‰',
  'çB' => 'è¹Œ',
  'çC' => 'è¹�',
  'çD' => 'è¹ˆ',
  'çE' => 'è¹™',
  'çF' => 'è¹¤',
  'çG' => 'è¹ ',
  'çH' => 'è¸ª',
  'çI' => 'è¹£',
  'çJ' => 'è¹•',
  'çK' => 'è¹¶',
  'çL' => 'è¹²',
  'çM' => 'è¹¼',
  'çN' => 'èº�',
  'çO' => 'èº‡',
  'çP' => 'èº…',
  'çQ' => 'èº„',
  'çR' => 'èº‹',
  'çS' => 'èºŠ',
  'çT' => 'èº“',
  'çU' => 'èº‘',
  'çV' => 'èº”',
  'çW' => 'èº™',
  'çX' => 'èºª',
  'çY' => 'èº¡',
  'çZ' => 'èº¬',
  'ç[' => 'èº°',
  'ç\\' => 'è»†',
  'ç]' => 'èº±',
  'ç^' => 'èº¾',
  'ç_' => 'è»…',
  'ç`' => 'è»ˆ',
  'ça' => 'è»‹',
  'çb' => 'è»›',
  'çc' => 'è»£',
  'çd' => 'è»¼',
  'çe' => 'è»»',
  'çf' => 'è»«',
  'çg' => 'è»¾',
  'çh' => 'è¼Š',
  'çi' => 'è¼…',
  'çj' => 'è¼•',
  'çk' => 'è¼’',
  'çl' => 'è¼™',
  'çm' => 'è¼“',
  'çn' => 'è¼œ',
  'ço' => 'è¼Ÿ',
  'çp' => 'è¼›',
  'çq' => 'è¼Œ',
  'çr' => 'è¼¦',
  'çs' => 'è¼³',
  'çt' => 'è¼»',
  'çu' => 'è¼¹',
  'çv' => 'è½…',
  'çw' => 'è½‚',
  'çx' => 'è¼¾',
  'çy' => 'è½Œ',
  'çz' => 'è½‰',
  'ç{' => 'è½†',
  'ç|' => 'è½Ž',
  'ç}' => 'è½—',
  'ç~' => 'è½œ',
  'ç€' => 'è½¢',
  'ç�' => 'è½£',
  'ç‚' => 'è½¤',
  'çƒ' => 'è¾œ',
  'ç„' => 'è¾Ÿ',
  'ç…' => 'è¾£',
  'ç†' => 'è¾­',
  'ç‡' => 'è¾¯',
  'çˆ' => 'è¾·',
  'ç‰' => 'è¿š',
  'çŠ' => 'è¿¥',
  'ç‹' => 'è¿¢',
  'çŒ' => 'è¿ª',
  'ç�' => 'è¿¯',
  'çŽ' => 'é‚‡',
  'ç�' => 'è¿´',
  'ç�' => 'é€…',
  'ç‘' => 'è¿¹',
  'ç’' => 'è¿º',
  'ç“' => 'é€‘',
  'ç”' => 'é€•',
  'ç•' => 'é€¡',
  'ç–' => 'é€�',
  'ç—' => 'é€ž',
  'ç˜' => 'é€–',
  'ç™' => 'é€‹',
  'çš' => 'é€§',
  'ç›' => 'é€¶',
  'çœ' => 'é€µ',
  'ç�' => 'é€¹',
  'çž' => 'è¿¸',
  'çŸ' => 'é��',
  'ç ' => 'é��',
  'ç¡' => 'é�‘',
  'ç¢' => 'é�’',
  'ç£' => 'é€Ž',
  'ç¤' => 'é�‰',
  'ç¥' => 'é€¾',
  'ç¦' => 'é�–',
  'ç§' => 'é�˜',
  'ç¨' => 'é�ž',
  'ç©' => 'é�¨',
  'çª' => 'é�¯',
  'ç«' => 'é�¶',
  'ç¬' => 'éš¨',
  'ç­' => 'é�²',
  'ç®' => 'é‚‚',
  'ç¯' => 'é�½',
  'ç°' => 'é‚�',
  'ç±' => 'é‚€',
  'ç²' => 'é‚Š',
  'ç³' => 'é‚‰',
  'ç´' => 'é‚�',
  'çµ' => 'é‚¨',
  'ç¶' => 'é‚¯',
  'ç·' => 'é‚±',
  'ç¸' => 'é‚µ',
  'ç¹' => 'éƒ¢',
  'çº' => 'éƒ¤',
  'ç»' => 'æ‰ˆ',
  'ç¼' => 'éƒ›',
  'ç½' => 'é„‚',
  'ç¾' => 'é„’',
  'ç¿' => 'é„™',
  'çÀ' => 'é„²',
  'çÁ' => 'é„°',
  'çÂ' => 'é…Š',
  'çÃ' => 'é…–',
  'çÄ' => 'é…˜',
  'çÅ' => 'é…£',
  'çÆ' => 'é…¥',
  'çÇ' => 'é…©',
  'çÈ' => 'é…³',
  'çÉ' => 'é…²',
  'çÊ' => 'é†‹',
  'çË' => 'é†‰',
  'çÌ' => 'é†‚',
  'çÍ' => 'é†¢',
  'çÎ' => 'é†«',
  'çÏ' => 'é†¯',
  'çÐ' => 'é†ª',
  'çÑ' => 'é†µ',
  'çÒ' => 'é†´',
  'çÓ' => 'é†º',
  'çÔ' => 'é‡€',
  'çÕ' => 'é‡�',
  'çÖ' => 'é‡‰',
  'ç×' => 'é‡‹',
  'çØ' => 'é‡�',
  'çÙ' => 'é‡–',
  'çÚ' => 'é‡Ÿ',
  'çÛ' => 'é‡¡',
  'çÜ' => 'é‡›',
  'çÝ' => 'é‡¼',
  'çÞ' => 'é‡µ',
  'çß' => 'é‡¶',
  'çà' => 'éˆž',
  'çá' => 'é‡¿',
  'çâ' => 'éˆ”',
  'çã' => 'éˆ¬',
  'çä' => 'éˆ•',
  'çå' => 'éˆ‘',
  'çæ' => 'é‰ž',
  'çç' => 'é‰—',
  'çè' => 'é‰…',
  'çé' => 'é‰‰',
  'çê' => 'é‰¤',
  'çë' => 'é‰ˆ',
  'çì' => 'éŠ•',
  'çí' => 'éˆ¿',
  'çî' => 'é‰‹',
  'çï' => 'é‰�',
  'çð' => 'éŠœ',
  'çñ' => 'éŠ–',
  'çò' => 'éŠ“',
  'çó' => 'éŠ›',
  'çô' => 'é‰š',
  'çõ' => 'é‹�',
  'çö' => 'éŠ¹',
  'ç÷' => 'éŠ·',
  'çø' => 'é‹©',
  'çù' => 'éŒ�',
  'çú' => 'é‹º',
  'çû' => 'é�„',
  'çü' => 'éŒ®',
  'è@' => 'éŒ™',
  'èA' => 'éŒ¢',
  'èB' => 'éŒš',
  'èC' => 'éŒ£',
  'èD' => 'éŒº',
  'èE' => 'éŒµ',
  'èF' => 'éŒ»',
  'èG' => 'é�œ',
  'èH' => 'é� ',
  'èI' => 'é�¼',
  'èJ' => 'é�®',
  'èK' => 'é�–',
  'èL' => 'éŽ°',
  'èM' => 'éŽ¬',
  'èN' => 'éŽ­',
  'èO' => 'éŽ”',
  'èP' => 'éŽ¹',
  'èQ' => 'é�–',
  'èR' => 'é�—',
  'èS' => 'é�¨',
  'èT' => 'é�¥',
  'èU' => 'é�˜',
  'èV' => 'é�ƒ',
  'èW' => 'é��',
  'èX' => 'é��',
  'èY' => 'é�ˆ',
  'èZ' => 'é�¤',
  'è[' => 'é�š',
  'è\\' => 'é�”',
  'è]' => 'é�“',
  'è^' => 'é�ƒ',
  'è_' => 'é�‡',
  'è`' => 'é��',
  'èa' => 'é�¶',
  'èb' => 'é�«',
  'èc' => 'é�µ',
  'èd' => 'é�¡',
  'èe' => 'é�º',
  'èf' => 'é‘�',
  'èg' => 'é‘’',
  'èh' => 'é‘„',
  'èi' => 'é‘›',
  'èj' => 'é‘ ',
  'èk' => 'é‘¢',
  'èl' => 'é‘ž',
  'èm' => 'é‘ª',
  'èn' => 'éˆ©',
  'èo' => 'é‘°',
  'èp' => 'é‘µ',
  'èq' => 'é‘·',
  'èr' => 'é‘½',
  'ès' => 'é‘š',
  'èt' => 'é‘¼',
  'èu' => 'é‘¾',
  'èv' => 'é’�',
  'èw' => 'é‘¿',
  'èx' => 'é–‚',
  'èy' => 'é–‡',
  'èz' => 'é–Š',
  'è{' => 'é–”',
  'è|' => 'é––',
  'è}' => 'é–˜',
  'è~' => 'é–™',
  'è€' => 'é– ',
  'è�' => 'é–¨',
  'è‚' => 'é–§',
  'èƒ' => 'é–­',
  'è„' => 'é–¼',
  'è…' => 'é–»',
  'è†' => 'é–¹',
  'è‡' => 'é–¾',
  'èˆ' => 'é—Š',
  'è‰' => 'æ¿¶',
  'èŠ' => 'é—ƒ',
  'è‹' => 'é—�',
  'èŒ' => 'é—Œ',
  'è�' => 'é—•',
  'èŽ' => 'é—”',
  'è�' => 'é—–',
  'è�' => 'é—œ',
  'è‘' => 'é—¡',
  'è’' => 'é—¥',
  'è“' => 'é—¢',
  'è”' => 'é˜¡',
  'è•' => 'é˜¨',
  'è–' => 'é˜®',
  'è—' => 'é˜¯',
  'è˜' => 'é™‚',
  'è™' => 'é™Œ',
  'èš' => 'é™�',
  'è›' => 'é™‹',
  'èœ' => 'é™·',
  'è�' => 'é™œ',
  'èž' => 'é™ž',
  'èŸ' => 'é™�',
  'è ' => 'é™Ÿ',
  'è¡' => 'é™¦',
  'è¢' => 'é™²',
  'è£' => 'é™¬',
  'è¤' => 'éš�',
  'è¥' => 'éš˜',
  'è¦' => 'éš•',
  'è§' => 'éš—',
  'è¨' => 'éšª',
  'è©' => 'éš§',
  'èª' => 'éš±',
  'è«' => 'éš²',
  'è¬' => 'éš°',
  'è­' => 'éš´',
  'è®' => 'éš¶',
  'è¯' => 'éš¸',
  'è°' => 'éš¹',
  'è±' => 'é›Ž',
  'è²' => 'é›‹',
  'è³' => 'é›‰',
  'è´' => 'é›�',
  'èµ' => 'è¥�',
  'è¶' => 'é›œ',
  'è·' => 'éœ�',
  'è¸' => 'é›•',
  'è¹' => 'é›¹',
  'èº' => 'éœ„',
  'è»' => 'éœ†',
  'è¼' => 'éœˆ',
  'è½' => 'éœ“',
  'è¾' => 'éœŽ',
  'è¿' => 'éœ‘',
  'èÀ' => 'éœ�',
  'èÁ' => 'éœ–',
  'èÂ' => 'éœ™',
  'èÃ' => 'éœ¤',
  'èÄ' => 'éœª',
  'èÅ' => 'éœ°',
  'èÆ' => 'éœ¹',
  'èÇ' => 'éœ½',
  'èÈ' => 'éœ¾',
  'èÉ' => 'é�„',
  'èÊ' => 'é�†',
  'èË' => 'é�ˆ',
  'èÌ' => 'é�‚',
  'èÍ' => 'é�‰',
  'èÎ' => 'é�œ',
  'èÏ' => 'é� ',
  'èÐ' => 'é�¤',
  'èÑ' => 'é�¦',
  'èÒ' => 'é�¨',
  'èÓ' => 'å‹’',
  'èÔ' => 'é�«',
  'èÕ' => 'é�±',
  'èÖ' => 'é�¹',
  'è×' => 'éž…',
  'èØ' => 'é�¼',
  'èÙ' => 'éž�',
  'èÚ' => 'é�º',
  'èÛ' => 'éž†',
  'èÜ' => 'éž‹',
  'èÝ' => 'éž�',
  'èÞ' => 'éž�',
  'èß' => 'éžœ',
  'èà' => 'éž¨',
  'èá' => 'éž¦',
  'èâ' => 'éž£',
  'èã' => 'éž³',
  'èä' => 'éž´',
  'èå' => 'éŸƒ',
  'èæ' => 'éŸ†',
  'èç' => 'éŸˆ',
  'èè' => 'éŸ‹',
  'èé' => 'éŸœ',
  'èê' => 'éŸ­',
  'èë' => 'é½�',
  'èì' => 'éŸ²',
  'èí' => 'ç«Ÿ',
  'èî' => 'éŸ¶',
  'èï' => 'éŸµ',
  'èð' => 'é �',
  'èñ' => 'é Œ',
  'èò' => 'é ¸',
  'èó' => 'é ¤',
  'èô' => 'é ¡',
  'èõ' => 'é ·',
  'èö' => 'é ½',
  'è÷' => 'é¡†',
  'èø' => 'é¡�',
  'èù' => 'é¡‹',
  'èú' => 'é¡«',
  'èû' => 'é¡¯',
  'èü' => 'é¡°',
  'é@' => 'é¡±',
  'éA' => 'é¡´',
  'éB' => 'é¡³',
  'éC' => 'é¢ª',
  'éD' => 'é¢¯',
  'éE' => 'é¢±',
  'éF' => 'é¢¶',
  'éG' => 'é£„',
  'éH' => 'é£ƒ',
  'éI' => 'é£†',
  'éJ' => 'é£©',
  'éK' => 'é£«',
  'éL' => 'é¤ƒ',
  'éM' => 'é¤‰',
  'éN' => 'é¤’',
  'éO' => 'é¤”',
  'éP' => 'é¤˜',
  'éQ' => 'é¤¡',
  'éR' => 'é¤�',
  'éS' => 'é¤ž',
  'éT' => 'é¤¤',
  'éU' => 'é¤ ',
  'éV' => 'é¤¬',
  'éW' => 'é¤®',
  'éX' => 'é¤½',
  'éY' => 'é¤¾',
  'éZ' => 'é¥‚',
  'é[' => 'é¥‰',
  'é\\' => 'é¥…',
  'é]' => 'é¥�',
  'é^' => 'é¥‹',
  'é_' => 'é¥‘',
  'é`' => 'é¥’',
  'éa' => 'é¥Œ',
  'éb' => 'é¥•',
  'éc' => 'é¦—',
  'éd' => 'é¦˜',
  'ée' => 'é¦¥',
  'éf' => 'é¦­',
  'ég' => 'é¦®',
  'éh' => 'é¦¼',
  'éi' => 'é§Ÿ',
  'éj' => 'é§›',
  'ék' => 'é§�',
  'él' => 'é§˜',
  'ém' => 'é§‘',
  'én' => 'é§­',
  'éo' => 'é§®',
  'ép' => 'é§±',
  'éq' => 'é§²',
  'ér' => 'é§»',
  'és' => 'é§¸',
  'ét' => 'é¨�',
  'éu' => 'é¨�',
  'év' => 'é¨…',
  'éw' => 'é§¢',
  'éx' => 'é¨™',
  'éy' => 'é¨«',
  'éz' => 'é¨·',
  'é{' => 'é©…',
  'é|' => 'é©‚',
  'é}' => 'é©€',
  'é~' => 'é©ƒ',
  'é€' => 'é¨¾',
  'é�' => 'é©•',
  'é‚' => 'é©�',
  'éƒ' => 'é©›',
  'é„' => 'é©—',
  'é…' => 'é©Ÿ',
  'é†' => 'é©¢',
  'é‡' => 'é©¥',
  'éˆ' => 'é©¤',
  'é‰' => 'é©©',
  'éŠ' => 'é©«',
  'é‹' => 'é©ª',
  'éŒ' => 'éª­',
  'é�' => 'éª°',
  'éŽ' => 'éª¼',
  'é�' => 'é«€',
  'é�' => 'é«�',
  'é‘' => 'é«‘',
  'é’' => 'é«“',
  'é“' => 'é«”',
  'é”' => 'é«ž',
  'é•' => 'é«Ÿ',
  'é–' => 'é«¢',
  'é—' => 'é«£',
  'é˜' => 'é«¦',
  'é™' => 'é«¯',
  'éš' => 'é««',
  'é›' => 'é«®',
  'éœ' => 'é«´',
  'é�' => 'é«±',
  'éž' => 'é«·',
  'éŸ' => 'é«»',
  'é ' => 'é¬†',
  'é¡' => 'é¬˜',
  'é¢' => 'é¬š',
  'é£' => 'é¬Ÿ',
  'é¤' => 'é¬¢',
  'é¥' => 'é¬£',
  'é¦' => 'é¬¥',
  'é§' => 'é¬§',
  'é¨' => 'é¬¨',
  'é©' => 'é¬©',
  'éª' => 'é¬ª',
  'é«' => 'é¬®',
  'é¬' => 'é¬¯',
  'é­' => 'é¬²',
  'é®' => 'é­„',
  'é¯' => 'é­ƒ',
  'é°' => 'é­�',
  'é±' => 'é­�',
  'é²' => 'é­Ž',
  'é³' => 'é­‘',
  'é´' => 'é­˜',
  'éµ' => 'é­´',
  'é¶' => 'é®“',
  'é·' => 'é®ƒ',
  'é¸' => 'é®‘',
  'é¹' => 'é®–',
  'éº' => 'é®—',
  'é»' => 'é®Ÿ',
  'é¼' => 'é® ',
  'é½' => 'é®¨',
  'é¾' => 'é®´',
  'é¿' => 'é¯€',
  'éÀ' => 'é¯Š',
  'éÁ' => 'é®¹',
  'éÂ' => 'é¯†',
  'éÃ' => 'é¯�',
  'éÄ' => 'é¯‘',
  'éÅ' => 'é¯’',
  'éÆ' => 'é¯£',
  'éÇ' => 'é¯¢',
  'éÈ' => 'é¯¤',
  'éÉ' => 'é¯”',
  'éÊ' => 'é¯¡',
  'éË' => 'é°º',
  'éÌ' => 'é¯²',
  'éÍ' => 'é¯±',
  'éÎ' => 'é¯°',
  'éÏ' => 'é°•',
  'éÐ' => 'é°”',
  'éÑ' => 'é°‰',
  'éÒ' => 'é°“',
  'éÓ' => 'é°Œ',
  'éÔ' => 'é°†',
  'éÕ' => 'é°ˆ',
  'éÖ' => 'é°’',
  'é×' => 'é°Š',
  'éØ' => 'é°„',
  'éÙ' => 'é°®',
  'éÚ' => 'é°›',
  'éÛ' => 'é°¥',
  'éÜ' => 'é°¤',
  'éÝ' => 'é°¡',
  'éÞ' => 'é°°',
  'éß' => 'é±‡',
  'éà' => 'é°²',
  'éá' => 'é±†',
  'éâ' => 'é°¾',
  'éã' => 'é±š',
  'éä' => 'é± ',
  'éå' => 'é±§',
  'éæ' => 'é±¶',
  'éç' => 'é±¸',
  'éè' => 'é³§',
  'éé' => 'é³¬',
  'éê' => 'é³°',
  'éë' => 'é´‰',
  'éì' => 'é´ˆ',
  'éí' => 'é³«',
  'éî' => 'é´ƒ',
  'éï' => 'é´†',
  'éð' => 'é´ª',
  'éñ' => 'é´¦',
  'éò' => 'é¶¯',
  'éó' => 'é´£',
  'éô' => 'é´Ÿ',
  'éõ' => 'éµ„',
  'éö' => 'é´•',
  'é÷' => 'é´’',
  'éø' => 'éµ�',
  'éù' => 'é´¿',
  'éú' => 'é´¾',
  'éû' => 'éµ†',
  'éü' => 'éµˆ',
  'ê@' => 'éµ�',
  'êA' => 'éµž',
  'êB' => 'éµ¤',
  'êC' => 'éµ‘',
  'êD' => 'éµ�',
  'êE' => 'éµ™',
  'êF' => 'éµ²',
  'êG' => 'é¶‰',
  'êH' => 'é¶‡',
  'êI' => 'é¶«',
  'êJ' => 'éµ¯',
  'êK' => 'éµº',
  'êL' => 'é¶š',
  'êM' => 'é¶¤',
  'êN' => 'é¶©',
  'êO' => 'é¶²',
  'êP' => 'é·„',
  'êQ' => 'é·�',
  'êR' => 'é¶»',
  'êS' => 'é¶¸',
  'êT' => 'é¶º',
  'êU' => 'é·†',
  'êV' => 'é·�',
  'êW' => 'é·‚',
  'êX' => 'é·™',
  'êY' => 'é·“',
  'êZ' => 'é·¸',
  'ê[' => 'é·¦',
  'ê\\' => 'é·­',
  'ê]' => 'é·¯',
  'ê^' => 'é·½',
  'ê_' => 'é¸š',
  'ê`' => 'é¸›',
  'êa' => 'é¸ž',
  'êb' => 'é¹µ',
  'êc' => 'é¹¹',
  'êd' => 'é¹½',
  'êe' => 'éº�',
  'êf' => 'éºˆ',
  'êg' => 'éº‹',
  'êh' => 'éºŒ',
  'êi' => 'éº’',
  'êj' => 'éº•',
  'êk' => 'éº‘',
  'êl' => 'éº�',
  'êm' => 'éº¥',
  'ên' => 'éº©',
  'êo' => 'éº¸',
  'êp' => 'éºª',
  'êq' => 'éº­',
  'êr' => 'é�¡',
  'ês' => 'é»Œ',
  'êt' => 'é»Ž',
  'êu' => 'é»�',
  'êv' => 'é»�',
  'êw' => 'é»”',
  'êx' => 'é»œ',
  'êy' => 'é»ž',
  'êz' => 'é»�',
  'ê{' => 'é» ',
  'ê|' => 'é»¥',
  'ê}' => 'é»¨',
  'ê~' => 'é»¯',
  'ê€' => 'é»´',
  'ê�' => 'é»¶',
  'ê‚' => 'é»·',
  'êƒ' => 'é»¹',
  'ê„' => 'é»»',
  'ê…' => 'é»¼',
  'ê†' => 'é»½',
  'ê‡' => 'é¼‡',
  'êˆ' => 'é¼ˆ',
  'ê‰' => 'çš·',
  'êŠ' => 'é¼•',
  'ê‹' => 'é¼¡',
  'êŒ' => 'é¼¬',
  'ê�' => 'é¼¾',
  'êŽ' => 'é½Š',
  'ê�' => 'é½’',
  'ê�' => 'é½”',
  'ê‘' => 'é½£',
  'ê’' => 'é½Ÿ',
  'ê“' => 'é½ ',
  'ê”' => 'é½¡',
  'ê•' => 'é½¦',
  'ê–' => 'é½§',
  'ê—' => 'é½¬',
  'ê˜' => 'é½ª',
  'ê™' => 'é½·',
  'êš' => 'é½²',
  'ê›' => 'é½¶',
  'êœ' => 'é¾•',
  'ê�' => 'é¾œ',
  'êž' => 'é¾ ',
  'êŸ' => 'å ¯',
  'ê ' => 'æ§‡',
  'ê¡' => 'é�™',
  'ê¢' => 'ç‘¤',
  'ê£' => 'å‡œ',
  'ê¤' => 'ç†™',
  'í@' => 'çºŠ',
  'íA' => 'è¤œ',
  'íB' => 'é�ˆ',
  'íC' => 'éŠˆ',
  'íD' => 'è“œ',
  'íE' => 'ä¿‰',
  'íF' => 'ç‚»',
  'íG' => 'æ˜±',
  'íH' => 'æ£ˆ',
  'íI' => 'é‹¹',
  'íJ' => 'æ›»',
  'íK' => 'å½…',
  'íL' => 'ä¸¨',
  'íM' => 'ä»¡',
  'íN' => 'ä»¼',
  'íO' => 'ä¼€',
  'íP' => 'ä¼ƒ',
  'íQ' => 'ä¼¹',
  'íR' => 'ä½–',
  'íS' => 'ä¾’',
  'íT' => 'ä¾Š',
  'íU' => 'ä¾š',
  'íV' => 'ä¾”',
  'íW' => 'ä¿�',
  'íX' => 'å�€',
  'íY' => 'å€¢',
  'íZ' => 'ä¿¿',
  'í[' => 'å€ž',
  'í\\' => 'å�†',
  'í]' => 'å�°',
  'í^' => 'å�‚',
  'í_' => 'å‚”',
  'í`' => 'åƒ´',
  'ía' => 'åƒ˜',
  'íb' => 'å…Š',
  'íc' => 'å…¤',
  'íd' => 'å†�',
  'íe' => 'å†¾',
  'íf' => 'å‡¬',
  'íg' => 'åˆ•',
  'íh' => 'åŠœ',
  'íi' => 'åŠ¦',
  'íj' => 'å‹€',
  'ík' => 'å‹›',
  'íl' => 'åŒ€',
  'ím' => 'åŒ‡',
  'ín' => 'åŒ¤',
  'ío' => 'å�²',
  'íp' => 'åŽ“',
  'íq' => 'åŽ²',
  'ír' => 'å��',
  'ís' => 'ï¨Ž',
  'ít' => 'å’œ',
  'íu' => 'å’Š',
  'ív' => 'å’©',
  'íw' => 'å“¿',
  'íx' => 'å–†',
  'íy' => 'å�™',
  'íz' => 'å�¥',
  'í{' => 'åž¬',
  'í|' => 'åŸˆ',
  'í}' => 'åŸ‡',
  'í~' => 'ï¨�',
  'í€' => 'ï¨�',
  'í�' => 'å¢ž',
  'í‚' => 'å¢²',
  'íƒ' => 'å¤‹',
  'í„' => 'å¥“',
  'í…' => 'å¥›',
  'í†' => 'å¥�',
  'í‡' => 'å¥£',
  'íˆ' => 'å¦¤',
  'í‰' => 'å¦º',
  'íŠ' => 'å­–',
  'í‹' => 'å¯€',
  'íŒ' => 'ç”¯',
  'í�' => 'å¯˜',
  'íŽ' => 'å¯¬',
  'í�' => 'å°ž',
  'í�' => 'å²¦',
  'í‘' => 'å²º',
  'í’' => 'å³µ',
  'í“' => 'å´§',
  'í”' => 'åµ“',
  'í•' => 'ï¨‘',
  'í–' => 'åµ‚',
  'í—' => 'åµ­',
  'í˜' => 'å¶¸',
  'í™' => 'å¶¹',
  'íš' => 'å·�',
  'í›' => 'å¼¡',
  'íœ' => 'å¼´',
  'í�' => 'å½§',
  'íž' => 'å¾·',
  'íŸ' => 'å¿ž',
  'í ' => 'æ��',
  'í¡' => 'æ‚…',
  'í¢' => 'æ‚Š',
  'í£' => 'æƒž',
  'í¤' => 'æƒ•',
  'í¥' => 'æ„ ',
  'í¦' => 'æƒ²',
  'í§' => 'æ„‘',
  'í¨' => 'æ„·',
  'í©' => 'æ„°',
  'íª' => 'æ†˜',
  'í«' => 'æˆ“',
  'í¬' => 'æŠ¦',
  'í­' => 'æ�µ',
  'í®' => 'æ‘ ',
  'í¯' => 'æ’�',
  'í°' => 'æ“Ž',
  'í±' => 'æ•Ž',
  'í²' => 'æ˜€',
  'í³' => 'æ˜•',
  'í´' => 'æ˜»',
  'íµ' => 'æ˜‰',
  'í¶' => 'æ˜®',
  'í·' => 'æ˜ž',
  'í¸' => 'æ˜¤',
  'í¹' => 'æ™¥',
  'íº' => 'æ™—',
  'í»' => 'æ™™',
  'í¼' => 'ï¨’',
  'í½' => 'æ™³',
  'í¾' => 'æš™',
  'í¿' => 'æš ',
  'íÀ' => 'æš²',
  'íÁ' => 'æš¿',
  'íÂ' => 'æ›º',
  'íÃ' => 'æœŽ',
  'íÄ' => 'ï¤©',
  'íÅ' => 'æ�¦',
  'íÆ' => 'æž»',
  'íÇ' => 'æ¡’',
  'íÈ' => 'æŸ€',
  'íÉ' => 'æ �',
  'íÊ' => 'æ¡„',
  'íË' => 'æ£�',
  'íÌ' => 'ï¨“',
  'íÍ' => 'æ¥¨',
  'íÎ' => 'ï¨”',
  'íÏ' => 'æ¦˜',
  'íÐ' => 'æ§¢',
  'íÑ' => 'æ¨°',
  'íÒ' => 'æ©«',
  'íÓ' => 'æ©†',
  'íÔ' => 'æ©³',
  'íÕ' => 'æ©¾',
  'íÖ' => 'æ«¢',
  'í×' => 'æ«¤',
  'íØ' => 'æ¯–',
  'íÙ' => 'æ°¿',
  'íÚ' => 'æ±œ',
  'íÛ' => 'æ²†',
  'íÜ' => 'æ±¯',
  'íÝ' => 'æ³š',
  'íÞ' => 'æ´„',
  'íß' => 'æ¶‡',
  'íà' => 'æµ¯',
  'íá' => 'æ¶–',
  'íâ' => 'æ¶¬',
  'íã' => 'æ·�',
  'íä' => 'æ·¸',
  'íå' => 'æ·²',
  'íæ' => 'æ·¼',
  'íç' => 'æ¸¹',
  'íè' => 'æ¹œ',
  'íé' => 'æ¸§',
  'íê' => 'æ¸¼',
  'íë' => 'æº¿',
  'íì' => 'æ¾ˆ',
  'íí' => 'æ¾µ',
  'íî' => 'æ¿µ',
  'íï' => 'ç€…',
  'íð' => 'ç€‡',
  'íñ' => 'ç€¨',
  'íò' => 'ç‚…',
  'íó' => 'ç‚«',
  'íô' => 'ç„�',
  'íõ' => 'ç„„',
  'íö' => 'ç…œ',
  'í÷' => 'ç…†',
  'íø' => 'ç…‡',
  'íù' => 'ï¨•',
  'íú' => 'ç‡�',
  'íû' => 'ç‡¾',
  'íü' => 'çŠ±',
  'î@' => 'çŠ¾',
  'îA' => 'çŒ¤',
  'îB' => 'ï¨–',
  'îC' => 'ç�·',
  'îD' => 'çŽ½',
  'îE' => 'ç�‰',
  'îF' => 'ç�–',
  'îG' => 'ç�£',
  'îH' => 'ç�’',
  'îI' => 'ç�‡',
  'îJ' => 'ç�µ',
  'îK' => 'ç�¦',
  'îL' => 'ç�ª',
  'îM' => 'ç�©',
  'îN' => 'ç�®',
  'îO' => 'ç‘¢',
  'îP' => 'ç’‰',
  'îQ' => 'ç’Ÿ',
  'îR' => 'ç”�',
  'îS' => 'ç•¯',
  'îT' => 'çš‚',
  'îU' => 'çšœ',
  'îV' => 'çšž',
  'îW' => 'çš›',
  'îX' => 'çš¦',
  'îY' => 'ï¨—',
  'îZ' => 'ç�†',
  'î[' => 'åŠ¯',
  'î\\' => 'ç ¡',
  'î]' => 'ç¡Ž',
  'î^' => 'ç¡¤',
  'î_' => 'ç¡º',
  'î`' => 'ç¤°',
  'îa' => 'ï¨˜',
  'îb' => 'ï¨™',
  'îc' => 'ï¨š',
  'îd' => 'ç¦”',
  'îe' => 'ï¨›',
  'îf' => 'ç¦›',
  'îg' => 'ç«‘',
  'îh' => 'ç«§',
  'îi' => 'ï¨œ',
  'îj' => 'ç««',
  'îk' => 'ç®ž',
  'îl' => 'ï¨�',
  'îm' => 'çµˆ',
  'în' => 'çµœ',
  'îo' => 'ç¶·',
  'îp' => 'ç¶ ',
  'îq' => 'ç·–',
  'îr' => 'ç¹’',
  'îs' => 'ç½‡',
  'ît' => 'ç¾¡',
  'îu' => 'ï¨ž',
  'îv' => 'èŒ�',
  'îw' => 'è�¢',
  'îx' => 'è�¿',
  'îy' => 'è�‡',
  'îz' => 'è�¶',
  'î{' => 'è‘ˆ',
  'î|' => 'è’´',
  'î}' => 'è•“',
  'î~' => 'è•™',
  'î€' => 'è•«',
  'î�' => 'ï¨Ÿ',
  'î‚' => 'è–°',
  'îƒ' => 'ï¨ ',
  'î„' => 'ï¨¡',
  'î…' => 'è ‡',
  'î†' => 'è£µ',
  'î‡' => 'è¨’',
  'îˆ' => 'è¨·',
  'î‰' => 'è©¹',
  'îŠ' => 'èª§',
  'î‹' => 'èª¾',
  'îŒ' => 'è«Ÿ',
  'î�' => 'ï¨¢',
  'îŽ' => 'è«¶',
  'î�' => 'è­“',
  'î�' => 'è­¿',
  'î‘' => 'è³°',
  'î’' => 'è³´',
  'î“' => 'è´’',
  'î”' => 'èµ¶',
  'î•' => 'ï¨£',
  'î–' => 'è»�',
  'î—' => 'ï¨¤',
  'î˜' => 'ï¨¥',
  'î™' => 'é�§',
  'îš' => 'éƒž',
  'î›' => 'ï¨¦',
  'îœ' => 'é„•',
  'î�' => 'é„§',
  'îž' => 'é‡š',
  'îŸ' => 'é‡—',
  'î ' => 'é‡ž',
  'î¡' => 'é‡­',
  'î¢' => 'é‡®',
  'î£' => 'é‡¤',
  'î¤' => 'é‡¥',
  'î¥' => 'éˆ†',
  'î¦' => 'éˆ�',
  'î§' => 'éˆŠ',
  'î¨' => 'éˆº',
  'î©' => 'é‰€',
  'îª' => 'éˆ¼',
  'î«' => 'é‰Ž',
  'î¬' => 'é‰™',
  'î­' => 'é‰‘',
  'î®' => 'éˆ¹',
  'î¯' => 'é‰§',
  'î°' => 'éŠ§',
  'î±' => 'é‰·',
  'î²' => 'é‰¸',
  'î³' => 'é‹§',
  'î´' => 'é‹—',
  'îµ' => 'é‹™',
  'î¶' => 'é‹�',
  'î·' => 'ï¨§',
  'î¸' => 'é‹•',
  'î¹' => 'é‹ ',
  'îº' => 'é‹“',
  'î»' => 'éŒ¥',
  'î¼' => 'éŒ¡',
  'î½' => 'é‹»',
  'î¾' => 'ï¨¨',
  'î¿' => 'éŒž',
  'îÀ' => 'é‹¿',
  'îÁ' => 'éŒ�',
  'îÂ' => 'éŒ‚',
  'îÃ' => 'é�°',
  'îÄ' => 'é�—',
  'îÅ' => 'éŽ¤',
  'îÆ' => 'é�†',
  'îÇ' => 'é�ž',
  'îÈ' => 'é�¸',
  'îÉ' => 'é�±',
  'îÊ' => 'é‘…',
  'îË' => 'é‘ˆ',
  'îÌ' => 'é–’',
  'îÍ' => 'ï§œ',
  'îÎ' => 'ï¨©',
  'îÏ' => 'éš�',
  'îÐ' => 'éš¯',
  'îÑ' => 'éœ³',
  'îÒ' => 'éœ»',
  'îÓ' => 'é�ƒ',
  'îÔ' => 'é��',
  'îÕ' => 'é��',
  'îÖ' => 'é�‘',
  'î×' => 'é�•',
  'îØ' => 'é¡—',
  'îÙ' => 'é¡¥',
  'îÚ' => 'ï¨ª',
  'îÛ' => 'ï¨«',
  'îÜ' => 'é¤§',
  'îÝ' => 'ï¨¬',
  'îÞ' => 'é¦ž',
  'îß' => 'é©Ž',
  'îà' => 'é«™',
  'îá' => 'é«œ',
  'îâ' => 'é­µ',
  'îã' => 'é­²',
  'îä' => 'é®�',
  'îå' => 'é®±',
  'îæ' => 'é®»',
  'îç' => 'é°€',
  'îè' => 'éµ°',
  'îé' => 'éµ«',
  'îê' => 'ï¨­',
  'îë' => 'é¸™',
  'îì' => 'é»‘',
  'îï' => 'â…°',
  'îð' => 'â…±',
  'îñ' => 'â…²',
  'îò' => 'â…³',
  'îó' => 'â…´',
  'îô' => 'â…µ',
  'îõ' => 'â…¶',
  'îö' => 'â…·',
  'î÷' => 'â…¸',
  'îø' => 'â…¹',
  'îù' => 'ï¿¢',
  'îú' => 'ï¿¤',
  'îû' => 'ï¼‡',
  'îü' => 'ï¼‚',
  'ú@' => 'â…°',
  'úA' => 'â…±',
  'úB' => 'â…²',
  'úC' => 'â…³',
  'úD' => 'â…´',
  'úE' => 'â…µ',
  'úF' => 'â…¶',
  'úG' => 'â…·',
  'úH' => 'â…¸',
  'úI' => 'â…¹',
  'úJ' => 'â… ',
  'úK' => 'â…¡',
  'úL' => 'â…¢',
  'úM' => 'â…£',
  'úN' => 'â…¤',
  'úO' => 'â…¥',
  'úP' => 'â…¦',
  'úQ' => 'â…§',
  'úR' => 'â…¨',
  'úS' => 'â…©',
  'úT' => 'ï¿¢',
  'úU' => 'ï¿¤',
  'úV' => 'ï¼‡',
  'úW' => 'ï¼‚',
  'úX' => 'ãˆ±',
  'úY' => 'â„–',
  'úZ' => 'â„¡',
  'ú[' => 'âˆµ',
  'ú\\' => 'çºŠ',
  'ú]' => 'è¤œ',
  'ú^' => 'é�ˆ',
  'ú_' => 'éŠˆ',
  'ú`' => 'è“œ',
  'úa' => 'ä¿‰',
  'úb' => 'ç‚»',
  'úc' => 'æ˜±',
  'úd' => 'æ£ˆ',
  'úe' => 'é‹¹',
  'úf' => 'æ›»',
  'úg' => 'å½…',
  'úh' => 'ä¸¨',
  'úi' => 'ä»¡',
  'új' => 'ä»¼',
  'úk' => 'ä¼€',
  'úl' => 'ä¼ƒ',
  'úm' => 'ä¼¹',
  'ún' => 'ä½–',
  'úo' => 'ä¾’',
  'úp' => 'ä¾Š',
  'úq' => 'ä¾š',
  'úr' => 'ä¾”',
  'ús' => 'ä¿�',
  'út' => 'å�€',
  'úu' => 'å€¢',
  'úv' => 'ä¿¿',
  'úw' => 'å€ž',
  'úx' => 'å�†',
  'úy' => 'å�°',
  'úz' => 'å�‚',
  'ú{' => 'å‚”',
  'ú|' => 'åƒ´',
  'ú}' => 'åƒ˜',
  'ú~' => 'å…Š',
  'ú€' => 'å…¤',
  'ú�' => 'å†�',
  'ú‚' => 'å†¾',
  'úƒ' => 'å‡¬',
  'ú„' => 'åˆ•',
  'ú…' => 'åŠœ',
  'ú†' => 'åŠ¦',
  'ú‡' => 'å‹€',
  'úˆ' => 'å‹›',
  'ú‰' => 'åŒ€',
  'úŠ' => 'åŒ‡',
  'ú‹' => 'åŒ¤',
  'úŒ' => 'å�²',
  'ú�' => 'åŽ“',
  'úŽ' => 'åŽ²',
  'ú�' => 'å��',
  'ú�' => 'ï¨Ž',
  'ú‘' => 'å’œ',
  'ú’' => 'å’Š',
  'ú“' => 'å’©',
  'ú”' => 'å“¿',
  'ú•' => 'å–†',
  'ú–' => 'å�™',
  'ú—' => 'å�¥',
  'ú˜' => 'åž¬',
  'ú™' => 'åŸˆ',
  'úš' => 'åŸ‡',
  'ú›' => 'ï¨�',
  'úœ' => 'ï¨�',
  'ú�' => 'å¢ž',
  'úž' => 'å¢²',
  'úŸ' => 'å¤‹',
  'ú ' => 'å¥“',
  'ú¡' => 'å¥›',
  'ú¢' => 'å¥�',
  'ú£' => 'å¥£',
  'ú¤' => 'å¦¤',
  'ú¥' => 'å¦º',
  'ú¦' => 'å­–',
  'ú§' => 'å¯€',
  'ú¨' => 'ç”¯',
  'ú©' => 'å¯˜',
  'úª' => 'å¯¬',
  'ú«' => 'å°ž',
  'ú¬' => 'å²¦',
  'ú­' => 'å²º',
  'ú®' => 'å³µ',
  'ú¯' => 'å´§',
  'ú°' => 'åµ“',
  'ú±' => 'ï¨‘',
  'ú²' => 'åµ‚',
  'ú³' => 'åµ­',
  'ú´' => 'å¶¸',
  'úµ' => 'å¶¹',
  'ú¶' => 'å·�',
  'ú·' => 'å¼¡',
  'ú¸' => 'å¼´',
  'ú¹' => 'å½§',
  'úº' => 'å¾·',
  'ú»' => 'å¿ž',
  'ú¼' => 'æ��',
  'ú½' => 'æ‚…',
  'ú¾' => 'æ‚Š',
  'ú¿' => 'æƒž',
  'úÀ' => 'æƒ•',
  'úÁ' => 'æ„ ',
  'úÂ' => 'æƒ²',
  'úÃ' => 'æ„‘',
  'úÄ' => 'æ„·',
  'úÅ' => 'æ„°',
  'úÆ' => 'æ†˜',
  'úÇ' => 'æˆ“',
  'úÈ' => 'æŠ¦',
  'úÉ' => 'æ�µ',
  'úÊ' => 'æ‘ ',
  'úË' => 'æ’�',
  'úÌ' => 'æ“Ž',
  'úÍ' => 'æ•Ž',
  'úÎ' => 'æ˜€',
  'úÏ' => 'æ˜•',
  'úÐ' => 'æ˜»',
  'úÑ' => 'æ˜‰',
  'úÒ' => 'æ˜®',
  'úÓ' => 'æ˜ž',
  'úÔ' => 'æ˜¤',
  'úÕ' => 'æ™¥',
  'úÖ' => 'æ™—',
  'ú×' => 'æ™™',
  'úØ' => 'ï¨’',
  'úÙ' => 'æ™³',
  'úÚ' => 'æš™',
  'úÛ' => 'æš ',
  'úÜ' => 'æš²',
  'úÝ' => 'æš¿',
  'úÞ' => 'æ›º',
  'úß' => 'æœŽ',
  'úà' => 'ï¤©',
  'úá' => 'æ�¦',
  'úâ' => 'æž»',
  'úã' => 'æ¡’',
  'úä' => 'æŸ€',
  'úå' => 'æ �',
  'úæ' => 'æ¡„',
  'úç' => 'æ£�',
  'úè' => 'ï¨“',
  'úé' => 'æ¥¨',
  'úê' => 'ï¨”',
  'úë' => 'æ¦˜',
  'úì' => 'æ§¢',
  'úí' => 'æ¨°',
  'úî' => 'æ©«',
  'úï' => 'æ©†',
  'úð' => 'æ©³',
  'úñ' => 'æ©¾',
  'úò' => 'æ«¢',
  'úó' => 'æ«¤',
  'úô' => 'æ¯–',
  'úõ' => 'æ°¿',
  'úö' => 'æ±œ',
  'ú÷' => 'æ²†',
  'úø' => 'æ±¯',
  'úù' => 'æ³š',
  'úú' => 'æ´„',
  'úû' => 'æ¶‡',
  'úü' => 'æµ¯',
  'û@' => 'æ¶–',
  'ûA' => 'æ¶¬',
  'ûB' => 'æ·�',
  'ûC' => 'æ·¸',
  'ûD' => 'æ·²',
  'ûE' => 'æ·¼',
  'ûF' => 'æ¸¹',
  'ûG' => 'æ¹œ',
  'ûH' => 'æ¸§',
  'ûI' => 'æ¸¼',
  'ûJ' => 'æº¿',
  'ûK' => 'æ¾ˆ',
  'ûL' => 'æ¾µ',
  'ûM' => 'æ¿µ',
  'ûN' => 'ç€…',
  'ûO' => 'ç€‡',
  'ûP' => 'ç€¨',
  'ûQ' => 'ç‚…',
  'ûR' => 'ç‚«',
  'ûS' => 'ç„�',
  'ûT' => 'ç„„',
  'ûU' => 'ç…œ',
  'ûV' => 'ç…†',
  'ûW' => 'ç…‡',
  'ûX' => 'ï¨•',
  'ûY' => 'ç‡�',
  'ûZ' => 'ç‡¾',
  'û[' => 'çŠ±',
  'û\\' => 'çŠ¾',
  'û]' => 'çŒ¤',
  'û^' => 'ï¨–',
  'û_' => 'ç�·',
  'û`' => 'çŽ½',
  'ûa' => 'ç�‰',
  'ûb' => 'ç�–',
  'ûc' => 'ç�£',
  'ûd' => 'ç�’',
  'ûe' => 'ç�‡',
  'ûf' => 'ç�µ',
  'ûg' => 'ç�¦',
  'ûh' => 'ç�ª',
  'ûi' => 'ç�©',
  'ûj' => 'ç�®',
  'ûk' => 'ç‘¢',
  'ûl' => 'ç’‰',
  'ûm' => 'ç’Ÿ',
  'ûn' => 'ç”�',
  'ûo' => 'ç•¯',
  'ûp' => 'çš‚',
  'ûq' => 'çšœ',
  'ûr' => 'çšž',
  'ûs' => 'çš›',
  'ût' => 'çš¦',
  'ûu' => 'ï¨—',
  'ûv' => 'ç�†',
  'ûw' => 'åŠ¯',
  'ûx' => 'ç ¡',
  'ûy' => 'ç¡Ž',
  'ûz' => 'ç¡¤',
  'û{' => 'ç¡º',
  'û|' => 'ç¤°',
  'û}' => 'ï¨˜',
  'û~' => 'ï¨™',
  'û€' => 'ï¨š',
  'û�' => 'ç¦”',
  'û‚' => 'ï¨›',
  'ûƒ' => 'ç¦›',
  'û„' => 'ç«‘',
  'û…' => 'ç«§',
  'û†' => 'ï¨œ',
  'û‡' => 'ç««',
  'ûˆ' => 'ç®ž',
  'û‰' => 'ï¨�',
  'ûŠ' => 'çµˆ',
  'û‹' => 'çµœ',
  'ûŒ' => 'ç¶·',
  'û�' => 'ç¶ ',
  'ûŽ' => 'ç·–',
  'û�' => 'ç¹’',
  'û�' => 'ç½‡',
  'û‘' => 'ç¾¡',
  'û’' => 'ï¨ž',
  'û“' => 'èŒ�',
  'û”' => 'è�¢',
  'û•' => 'è�¿',
  'û–' => 'è�‡',
  'û—' => 'è�¶',
  'û˜' => 'è‘ˆ',
  'û™' => 'è’´',
  'ûš' => 'è•“',
  'û›' => 'è•™',
  'ûœ' => 'è•«',
  'û�' => 'ï¨Ÿ',
  'ûž' => 'è–°',
  'ûŸ' => 'ï¨ ',
  'û ' => 'ï¨¡',
  'û¡' => 'è ‡',
  'û¢' => 'è£µ',
  'û£' => 'è¨’',
  'û¤' => 'è¨·',
  'û¥' => 'è©¹',
  'û¦' => 'èª§',
  'û§' => 'èª¾',
  'û¨' => 'è«Ÿ',
  'û©' => 'ï¨¢',
  'ûª' => 'è«¶',
  'û«' => 'è­“',
  'û¬' => 'è­¿',
  'û­' => 'è³°',
  'û®' => 'è³´',
  'û¯' => 'è´’',
  'û°' => 'èµ¶',
  'û±' => 'ï¨£',
  'û²' => 'è»�',
  'û³' => 'ï¨¤',
  'û´' => 'ï¨¥',
  'ûµ' => 'é�§',
  'û¶' => 'éƒž',
  'û·' => 'ï¨¦',
  'û¸' => 'é„•',
  'û¹' => 'é„§',
  'ûº' => 'é‡š',
  'û»' => 'é‡—',
  'û¼' => 'é‡ž',
  'û½' => 'é‡­',
  'û¾' => 'é‡®',
  'û¿' => 'é‡¤',
  'ûÀ' => 'é‡¥',
  'ûÁ' => 'éˆ†',
  'ûÂ' => 'éˆ�',
  'ûÃ' => 'éˆŠ',
  'ûÄ' => 'éˆº',
  'ûÅ' => 'é‰€',
  'ûÆ' => 'éˆ¼',
  'ûÇ' => 'é‰Ž',
  'ûÈ' => 'é‰™',
  'ûÉ' => 'é‰‘',
  'ûÊ' => 'éˆ¹',
  'ûË' => 'é‰§',
  'ûÌ' => 'éŠ§',
  'ûÍ' => 'é‰·',
  'ûÎ' => 'é‰¸',
  'ûÏ' => 'é‹§',
  'ûÐ' => 'é‹—',
  'ûÑ' => 'é‹™',
  'ûÒ' => 'é‹�',
  'ûÓ' => 'ï¨§',
  'ûÔ' => 'é‹•',
  'ûÕ' => 'é‹ ',
  'ûÖ' => 'é‹“',
  'û×' => 'éŒ¥',
  'ûØ' => 'éŒ¡',
  'ûÙ' => 'é‹»',
  'ûÚ' => 'ï¨¨',
  'ûÛ' => 'éŒž',
  'ûÜ' => 'é‹¿',
  'ûÝ' => 'éŒ�',
  'ûÞ' => 'éŒ‚',
  'ûß' => 'é�°',
  'ûà' => 'é�—',
  'ûá' => 'éŽ¤',
  'ûâ' => 'é�†',
  'ûã' => 'é�ž',
  'ûä' => 'é�¸',
  'ûå' => 'é�±',
  'ûæ' => 'é‘…',
  'ûç' => 'é‘ˆ',
  'ûè' => 'é–’',
  'ûé' => 'ï§œ',
  'ûê' => 'ï¨©',
  'ûë' => 'éš�',
  'ûì' => 'éš¯',
  'ûí' => 'éœ³',
  'ûî' => 'éœ»',
  'ûï' => 'é�ƒ',
  'ûð' => 'é��',
  'ûñ' => 'é��',
  'ûò' => 'é�‘',
  'ûó' => 'é�•',
  'ûô' => 'é¡—',
  'ûõ' => 'é¡¥',
  'ûö' => 'ï¨ª',
  'û÷' => 'ï¨«',
  'ûø' => 'é¤§',
  'ûù' => 'ï¨¬',
  'ûú' => 'é¦ž',
  'ûû' => 'é©Ž',
  'ûü' => 'é«™',
  'ü@' => 'é«œ',
  'üA' => 'é­µ',
  'üB' => 'é­²',
  'üC' => 'é®�',
  'üD' => 'é®±',
  'üE' => 'é®»',
  'üF' => 'é°€',
  'üG' => 'éµ°',
  'üH' => 'éµ«',
  'üI' => 'ï¨­',
  'üJ' => 'é¸™',
  'üK' => 'é»‘',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => '%',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
  '€' => 'â‚¬',
  '‚' => 'â€š',
  'ƒ' => 'Æ’',
  '„' => 'â€ž',
  '…' => 'â€¦',
  '†' => 'â€ ',
  '‡' => 'â€¡',
  'ˆ' => 'Ë†',
  '‰' => 'â€°',
  'Š' => 'Å ',
  '‹' => 'â€¹',
  'Œ' => 'Å’',
  'Ž' => 'Å½',
  '‘' => 'â€˜',
  '’' => 'â€™',
  '“' => 'â€œ',
  '”' => 'â€�',
  '•' => 'â€¢',
  '–' => 'â€“',
  '—' => 'â€”',
  '˜' => 'Ëœ',
  '™' => 'â„¢',
  'š' => 'Å¡',
  '›' => 'â€º',
  'œ' => 'Å“',
  'ž' => 'Å¾',
  'Ÿ' => 'Å¸',
  ' ' => 'Â ',
  '¡' => 'Â¡',
  '¢' => 'Â¢',
  '£' => 'Â£',
  '¤' => 'Â¤',
  '¥' => 'Â¥',
  '¦' => 'Â¦',
  '§' => 'Â§',
  '¨' => 'Â¨',
  '©' => 'Â©',
  'ª' => 'Âª',
  '«' => 'Â«',
  '¬' => 'Â¬',
  '­' => 'Â­',
  '®' => 'Â®',
  '¯' => 'Â¯',
  '°' => 'Â°',
  '±' => 'Â±',
  '²' => 'Â²',
  '³' => 'Â³',
  '´' => 'Â´',
  'µ' => 'Âµ',
  '¶' => 'Â¶',
  '·' => 'Â·',
  '¸' => 'Â¸',
  '¹' => 'Â¹',
  'º' => 'Âº',
  '»' => 'Â»',
  '¼' => 'Â¼',
  '½' => 'Â½',
  '¾' => 'Â¾',
  '¿' => 'Â¿',
  'À' => 'Ã€',
  'Á' => 'Ã�',
  'Â' => 'Ã‚',
  'Ã' => 'Ãƒ',
  'Ä' => 'Ã„',
  'Å' => 'Ã…',
  'Æ' => 'Ã†',
  'Ç' => 'Ã‡',
  'È' => 'Ãˆ',
  'É' => 'Ã‰',
  'Ê' => 'ÃŠ',
  'Ë' => 'Ã‹',
  'Ì' => 'ÃŒ',
  'Í' => 'Ã�',
  'Î' => 'ÃŽ',
  'Ï' => 'Ã�',
  'Ð' => 'Ã�',
  'Ñ' => 'Ã‘',
  'Ò' => 'Ã’',
  'Ó' => 'Ã“',
  'Ô' => 'Ã”',
  'Õ' => 'Ã•',
  'Ö' => 'Ã–',
  '×' => 'Ã—',
  'Ø' => 'Ã˜',
  'Ù' => 'Ã™',
  'Ú' => 'Ãš',
  'Û' => 'Ã›',
  'Ü' => 'Ãœ',
  'Ý' => 'Ã�',
  'Þ' => 'Ãž',
  'ß' => 'ÃŸ',
  'à' => 'Ã ',
  'á' => 'Ã¡',
  'â' => 'Ã¢',
  'ã' => 'Ã£',
  'ä' => 'Ã¤',
  'å' => 'Ã¥',
  'æ' => 'Ã¦',
  'ç' => 'Ã§',
  'è' => 'Ã¨',
  'é' => 'Ã©',
  'ê' => 'Ãª',
  'ë' => 'Ã«',
  'ì' => 'Ã¬',
  'í' => 'Ã­',
  'î' => 'Ã®',
  'ï' => 'Ã¯',
  'ð' => 'Ã°',
  'ñ' => 'Ã±',
  'ò' => 'Ã²',
  'ó' => 'Ã³',
  'ô' => 'Ã´',
  'õ' => 'Ãµ',
  'ö' => 'Ã¶',
  '÷' => 'Ã·',
  'ø' => 'Ã¸',
  'ù' => 'Ã¹',
  'ú' => 'Ãº',
  'û' => 'Ã»',
  'ü' => 'Ã¼',
  'ý' => 'Ã½',
  'þ' => 'Ã¾',
  'ÿ' => 'Ã¿',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => '%',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
  '†' => 'Î†',
  'ˆ' => 'Â·',
  '‰' => 'Â¬',
  'Š' => 'Â¦',
  '‹' => 'â€˜',
  'Œ' => 'â€™',
  '�' => 'Îˆ',
  'Ž' => 'â€•',
  '�' => 'Î‰',
  '�' => 'ÎŠ',
  '‘' => 'Îª',
  '’' => 'ÎŒ',
  '•' => 'ÎŽ',
  '–' => 'Î«',
  '—' => 'Â©',
  '˜' => 'Î�',
  '™' => 'Â²',
  'š' => 'Â³',
  '›' => 'Î¬',
  'œ' => 'Â£',
  '�' => 'Î­',
  'ž' => 'Î®',
  'Ÿ' => 'Î¯',
  ' ' => 'ÏŠ',
  '¡' => 'Î�',
  '¢' => 'ÏŒ',
  '£' => 'Ï�',
  '¤' => 'Î‘',
  '¥' => 'Î’',
  '¦' => 'Î“',
  '§' => 'Î”',
  '¨' => 'Î•',
  '©' => 'Î–',
  'ª' => 'Î—',
  '«' => 'Â½',
  '¬' => 'Î˜',
  '­' => 'Î™',
  '®' => 'Â«',
  '¯' => 'Â»',
  '°' => 'â–‘',
  '±' => 'â–’',
  '²' => 'â–“',
  '³' => 'â”‚',
  '´' => 'â”¤',
  'µ' => 'Îš',
  '¶' => 'Î›',
  '·' => 'Îœ',
  '¸' => 'Î�',
  '¹' => 'â•£',
  'º' => 'â•‘',
  '»' => 'â•—',
  '¼' => 'â•�',
  '½' => 'Îž',
  '¾' => 'ÎŸ',
  '¿' => 'â”�',
  'À' => 'â””',
  'Á' => 'â”´',
  'Â' => 'â”¬',
  'Ã' => 'â”œ',
  'Ä' => 'â”€',
  'Å' => 'â”¼',
  'Æ' => 'Î ',
  'Ç' => 'Î¡',
  'È' => 'â•š',
  'É' => 'â•”',
  'Ê' => 'â•©',
  'Ë' => 'â•¦',
  'Ì' => 'â• ',
  'Í' => 'â•�',
  'Î' => 'â•¬',
  'Ï' => 'Î£',
  'Ð' => 'Î¤',
  'Ñ' => 'Î¥',
  'Ò' => 'Î¦',
  'Ó' => 'Î§',
  'Ô' => 'Î¨',
  'Õ' => 'Î©',
  'Ö' => 'Î±',
  '×' => 'Î²',
  'Ø' => 'Î³',
  'Ù' => 'â”˜',
  'Ú' => 'â”Œ',
  'Û' => 'â–ˆ',
  'Ü' => 'â–„',
  'Ý' => 'Î´',
  'Þ' => 'Îµ',
  'ß' => 'â–€',
  'à' => 'Î¶',
  'á' => 'Î·',
  'â' => 'Î¸',
  'ã' => 'Î¹',
  'ä' => 'Îº',
  'å' => 'Î»',
  'æ' => 'Î¼',
  'ç' => 'Î½',
  'è' => 'Î¾',
  'é' => 'Î¿',
  'ê' => 'Ï€',
  'ë' => 'Ï�',
  'ì' => 'Ïƒ',
  'í' => 'Ï‚',
  'î' => 'Ï„',
  'ï' => 'Î„',
  'ð' => 'Â­',
  'ñ' => 'Â±',
  'ò' => 'Ï…',
  'ó' => 'Ï†',
  'ô' => 'Ï‡',
  'õ' => 'Â§',
  'ö' => 'Ïˆ',
  '÷' => 'Î…',
  'ø' => 'Â°',
  'ù' => 'Â¨',
  'ú' => 'Ï‰',
  'û' => 'Ï‹',
  'ü' => 'Î°',
  'ý' => 'ÏŽ',
  'þ' => 'â– ',
  'ÿ' => 'Â ',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => '%',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
  '€' => '×�',
  '�' => '×‘',
  '‚' => '×’',
  'ƒ' => '×“',
  '„' => '×”',
  '…' => '×•',
  '†' => '×–',
  '‡' => '×—',
  'ˆ' => '×˜',
  '‰' => '×™',
  'Š' => '×š',
  '‹' => '×›',
  'Œ' => '×œ',
  '�' => '×�',
  'Ž' => '×ž',
  '�' => '×Ÿ',
  '�' => '× ',
  '‘' => '×¡',
  '’' => '×¢',
  '“' => '×£',
  '”' => '×¤',
  '•' => '×¥',
  '–' => '×¦',
  '—' => '×§',
  '˜' => '×¨',
  '™' => '×©',
  'š' => '×ª',
  'œ' => 'Â£',
  'ž' => 'Ã—',
  '©' => 'Â®',
  'ª' => 'Â¬',
  '«' => 'Â½',
  '¬' => 'Â¼',
  '®' => 'Â«',
  '¯' => 'Â»',
  '°' => 'â–‘',
  '±' => 'â–’',
  '²' => 'â–“',
  '³' => 'â”‚',
  '´' => 'â”¤',
  '¸' => 'Â©',
  '¹' => 'â•£',
  'º' => 'â•‘',
  '»' => 'â•—',
  '¼' => 'â•�',
  '½' => 'Â¢',
  '¾' => 'Â¥',
  '¿' => 'â”�',
  'À' => 'â””',
  'Á' => 'â”´',
  'Â' => 'â”¬',
  'Ã' => 'â”œ',
  'Ä' => 'â”€',
  'Å' => 'â”¼',
  'È' => 'â•š',
  'É' => 'â•”',
  'Ê' => 'â•©',
  'Ë' => 'â•¦',
  'Ì' => 'â• ',
  'Í' => 'â•�',
  'Î' => 'â•¬',
  'Ï' => 'Â¤',
  'Ù' => 'â”˜',
  'Ú' => 'â”Œ',
  'Û' => 'â–ˆ',
  'Ü' => 'â–„',
  'Ý' => 'Â¦',
  'ß' => 'â–€',
  'æ' => 'Âµ',
  'î' => 'Â¯',
  'ï' => 'Â´',
  'ð' => 'Â­',
  'ñ' => 'Â±',
  'ò' => 'â€—',
  'ó' => 'Â¾',
  'ô' => 'Â¶',
  'õ' => 'Â§',
  'ö' => 'Ã·',
  '÷' => 'Â¸',
  'ø' => 'Â°',
  'ù' => 'Â¨',
  'ú' => 'Â·',
  'û' => 'Â¹',
  'ü' => 'Â³',
  'ý' => 'Â²',
  'þ' => 'â– ',
  'ÿ' => 'Â ',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '	' => '	',
  '
' => '
',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => ' ',
  '!' => '!',
  '"' => '"',
  '#' => '#',
  '$' => '$',
  '%' => '%',
  '&' => '&',
  '\'' => '\'',
  '(' => '(',
  ')' => ')',
  '*' => '*',
  '+' => '+',
  ',' => ',',
  '-' => '-',
  '.' => '.',
  '/' => '/',
  0 => '0',
  1 => '1',
  2 => '2',
  3 => '3',
  4 => '4',
  5 => '5',
  6 => '6',
  7 => '7',
  8 => '8',
  9 => '9',
  ':' => ':',
  ';' => ';',
  '<' => '<',
  '=' => '=',
  '>' => '>',
  '?' => '?',
  '@' => '@',
  'A' => 'A',
  'B' => 'B',
  'C' => 'C',
  'D' => 'D',
  'E' => 'E',
  'F' => 'F',
  'G' => 'G',
  'H' => 'H',
  'I' => 'I',
  'J' => 'J',
  'K' => 'K',
  'L' => 'L',
  'M' => 'M',
  'N' => 'N',
  'O' => 'O',
  'P' => 'P',
  'Q' => 'Q',
  'R' => 'R',
  'S' => 'S',
  'T' => 'T',
  'U' => 'U',
  'V' => 'V',
  'W' => 'W',
  'X' => 'X',
  'Y' => 'Y',
  'Z' => 'Z',
  '[' => '[',
  '\\' => '\\',
  ']' => ']',
  '^' => '^',
  '_' => '_',
  '`' => '`',
  'a' => 'a',
  'b' => 'b',
  'c' => 'c',
  'd' => 'd',
  'e' => 'e',
  'f' => 'f',
  'g' => 'g',
  'h' => 'h',
  'i' => 'i',
  'j' => 'j',
  'k' => 'k',
  'l' => 'l',
  'm' => 'm',
  'n' => 'n',
  'o' => 'o',
  'p' => 'p',
  'q' => 'q',
  'r' => 'r',
  's' => 's',
  't' => 't',
  'u' => 'u',
  'v' => 'v',
  'w' => 'w',
  'x' => 'x',
  'y' => 'y',
  'z' => 'z',
  '{' => '{',
  '|' => '|',
  '}' => '}',
  '~' => '~',
  '' => '',
  '€' => 'â‚¬',
  '�@' => 'ä¸‚',
  '�A' => 'ä¸„',
  '�B' => 'ä¸…',
  '�C' => 'ä¸†',
  '�D' => 'ä¸�',
  '�E' => 'ä¸’',
  '�F' => 'ä¸—',
  '�G' => 'ä¸Ÿ',
  '�H' => 'ä¸ ',
  '�I' => 'ä¸¡',
  '�J' => 'ä¸£',
  '�K' => 'ä¸¦',
  '�L' => 'ä¸©',
  '�M' => 'ä¸®',
  '�N' => 'ä¸¯',
  '�O' => 'ä¸±',
  '�P' => 'ä¸³',
  '�Q' => 'ä¸µ',
  '�R' => 'ä¸·',
  '�S' => 'ä¸¼',
  '�T' => 'ä¹€',
  '�U' => 'ä¹�',
  '�V' => 'ä¹‚',
  '�W' => 'ä¹„',
  '�X' => 'ä¹†',
  '�Y' => 'ä¹Š',
  '�Z' => 'ä¹‘',
  '�[' => 'ä¹•',
  '�\\' => 'ä¹—',
  '�]' => 'ä¹š',
  '�^' => 'ä¹›',
  '�_' => 'ä¹¢',
  '�`' => 'ä¹£',
  '�a' => 'ä¹¤',
  '�b' => 'ä¹¥',
  '�c' => 'ä¹§',
  '�d' => 'ä¹¨',
  '�e' => 'ä¹ª',
  '�f' => 'ä¹«',
  '�g' => 'ä¹¬',
  '�h' => 'ä¹­',
  '�i' => 'ä¹®',
  '�j' => 'ä¹¯',
  '�k' => 'ä¹²',
  '�l' => 'ä¹´',
  '�m' => 'ä¹µ',
  '�n' => 'ä¹¶',
  '�o' => 'ä¹·',
  '�p' => 'ä¹¸',
  '�q' => 'ä¹¹',
  '�r' => 'ä¹º',
  '�s' => 'ä¹»',
  '�t' => 'ä¹¼',
  '�u' => 'ä¹½',
  '�v' => 'ä¹¿',
  '�w' => 'äº€',
  '�x' => 'äº�',
  '�y' => 'äº‚',
  '�z' => 'äºƒ',
  '�{' => 'äº„',
  '�|' => 'äº…',
  '�}' => 'äº‡',
  '�~' => 'äºŠ',
  '�€' => 'äº�',
  '��' => 'äº–',
  '�‚' => 'äº—',
  '�ƒ' => 'äº™',
  '�„' => 'äºœ',
  '�…' => 'äº�',
  '�†' => 'äºž',
  '�‡' => 'äº£',
  '�ˆ' => 'äºª',
  '�‰' => 'äº¯',
  '�Š' => 'äº°',
  '�‹' => 'äº±',
  '�Œ' => 'äº´',
  '��' => 'äº¶',
  '�Ž' => 'äº·',
  '��' => 'äº¸',
  '��' => 'äº¹',
  '�‘' => 'äº¼',
  '�’' => 'äº½',
  '�“' => 'äº¾',
  '�”' => 'ä»ˆ',
  '�•' => 'ä»Œ',
  '�–' => 'ä»�',
  '�—' => 'ä»�',
  '�˜' => 'ä»’',
  '�™' => 'ä»š',
  '�š' => 'ä»›',
  '�›' => 'ä»œ',
  '�œ' => 'ä» ',
  '��' => 'ä»¢',
  '�ž' => 'ä»¦',
  '�Ÿ' => 'ä»§',
  '� ' => 'ä»©',
  '�¡' => 'ä»­',
  '�¢' => 'ä»®',
  '�£' => 'ä»¯',
  '�¤' => 'ä»±',
  '�¥' => 'ä»´',
  '�¦' => 'ä»¸',
  '�§' => 'ä»¹',
  '�¨' => 'ä»º',
  '�©' => 'ä»¼',
  '�ª' => 'ä»¾',
  '�«' => 'ä¼€',
  '�¬' => 'ä¼‚',
  '�­' => 'ä¼ƒ',
  '�®' => 'ä¼„',
  '�¯' => 'ä¼…',
  '�°' => 'ä¼†',
  '�±' => 'ä¼‡',
  '�²' => 'ä¼ˆ',
  '�³' => 'ä¼‹',
  '�´' => 'ä¼Œ',
  '�µ' => 'ä¼’',
  '�¶' => 'ä¼“',
  '�·' => 'ä¼”',
  '�¸' => 'ä¼•',
  '�¹' => 'ä¼–',
  '�º' => 'ä¼œ',
  '�»' => 'ä¼�',
  '�¼' => 'ä¼¡',
  '�½' => 'ä¼£',
  '�¾' => 'ä¼¨',
  '�¿' => 'ä¼©',
  '�À' => 'ä¼¬',
  '�Á' => 'ä¼­',
  '�Â' => 'ä¼®',
  '�Ã' => 'ä¼±',
  '�Ä' => 'ä¼³',
  '�Å' => 'ä¼µ',
  '�Æ' => 'ä¼·',
  '�Ç' => 'ä¼¹',
  '�È' => 'ä¼»',
  '�É' => 'ä¼¾',
  '�Ê' => 'ä¼¿',
  '�Ë' => 'ä½€',
  '�Ì' => 'ä½�',
  '�Í' => 'ä½‚',
  '�Î' => 'ä½„',
  '�Ï' => 'ä½…',
  '�Ð' => 'ä½‡',
  '�Ñ' => 'ä½ˆ',
  '�Ò' => 'ä½‰',
  '�Ó' => 'ä½Š',
  '�Ô' => 'ä½‹',
  '�Õ' => 'ä½Œ',
  '�Ö' => 'ä½’',
  '�×' => 'ä½”',
  '�Ø' => 'ä½–',
  '�Ù' => 'ä½¡',
  '�Ú' => 'ä½¢',
  '�Û' => 'ä½¦',
  '�Ü' => 'ä½¨',
  '�Ý' => 'ä½ª',
  '�Þ' => 'ä½«',
  '�ß' => 'ä½­',
  '�à' => 'ä½®',
  '�á' => 'ä½±',
  '�â' => 'ä½²',
  '�ã' => 'ä½µ',
  '�ä' => 'ä½·',
  '�å' => 'ä½¸',
  '�æ' => 'ä½¹',
  '�ç' => 'ä½º',
  '�è' => 'ä½½',
  '�é' => 'ä¾€',
  '�ê' => 'ä¾�',
  '�ë' => 'ä¾‚',
  '�ì' => 'ä¾…',
  '�í' => 'ä¾†',
  '�î' => 'ä¾‡',
  '�ï' => 'ä¾Š',
  '�ð' => 'ä¾Œ',
  '�ñ' => 'ä¾Ž',
  '�ò' => 'ä¾�',
  '�ó' => 'ä¾’',
  '�ô' => 'ä¾“',
  '�õ' => 'ä¾•',
  '�ö' => 'ä¾–',
  '�÷' => 'ä¾˜',
  '�ø' => 'ä¾™',
  '�ù' => 'ä¾š',
  '�ú' => 'ä¾œ',
  '�û' => 'ä¾ž',
  '�ü' => 'ä¾Ÿ',
  '�ý' => 'ä¾¡',
  '�þ' => 'ä¾¢',
  '‚@' => 'ä¾¤',
  '‚A' => 'ä¾«',
  '‚B' => 'ä¾­',
  '‚C' => 'ä¾°',
  '‚D' => 'ä¾±',
  '‚E' => 'ä¾²',
  '‚F' => 'ä¾³',
  '‚G' => 'ä¾´',
  '‚H' => 'ä¾¶',
  '‚I' => 'ä¾·',
  '‚J' => 'ä¾¸',
  '‚K' => 'ä¾¹',
  '‚L' => 'ä¾º',
  '‚M' => 'ä¾»',
  '‚N' => 'ä¾¼',
  '‚O' => 'ä¾½',
  '‚P' => 'ä¾¾',
  '‚Q' => 'ä¿€',
  '‚R' => 'ä¿�',
  '‚S' => 'ä¿‚',
  '‚T' => 'ä¿†',
  '‚U' => 'ä¿‡',
  '‚V' => 'ä¿ˆ',
  '‚W' => 'ä¿‰',
  '‚X' => 'ä¿‹',
  '‚Y' => 'ä¿Œ',
  '‚Z' => 'ä¿�',
  '‚[' => 'ä¿’',
  '‚\\' => 'ä¿“',
  '‚]' => 'ä¿”',
  '‚^' => 'ä¿•',
  '‚_' => 'ä¿–',
  '‚`' => 'ä¿™',
  '‚a' => 'ä¿›',
  '‚b' => 'ä¿ ',
  '‚c' => 'ä¿¢',
  '‚d' => 'ä¿¤',
  '‚e' => 'ä¿¥',
  '‚f' => 'ä¿§',
  '‚g' => 'ä¿«',
  '‚h' => 'ä¿¬',
  '‚i' => 'ä¿°',
  '‚j' => 'ä¿²',
  '‚k' => 'ä¿´',
  '‚l' => 'ä¿µ',
  '‚m' => 'ä¿¶',
  '‚n' => 'ä¿·',
  '‚o' => 'ä¿¹',
  '‚p' => 'ä¿»',
  '‚q' => 'ä¿¼',
  '‚r' => 'ä¿½',
  '‚s' => 'ä¿¿',
  '‚t' => 'å€€',
  '‚u' => 'å€�',
  '‚v' => 'å€‚',
  '‚w' => 'å€ƒ',
  '‚x' => 'å€„',
  '‚y' => 'å€…',
  '‚z' => 'å€†',
  '‚{' => 'å€‡',
  '‚|' => 'å€ˆ',
  '‚}' => 'å€‰',
  '‚~' => 'å€Š',
  '‚€' => 'å€‹',
  '‚�' => 'å€Ž',
  '‚‚' => 'å€�',
  '‚ƒ' => 'å€‘',
  '‚„' => 'å€“',
  '‚…' => 'å€•',
  '‚†' => 'å€–',
  '‚‡' => 'å€—',
  '‚ˆ' => 'å€›',
  '‚‰' => 'å€�',
  '‚Š' => 'å€ž',
  '‚‹' => 'å€ ',
  '‚Œ' => 'å€¢',
  '‚�' => 'å€£',
  '‚Ž' => 'å€¤',
  '‚�' => 'å€§',
  '‚�' => 'å€«',
  '‚‘' => 'å€¯',
  '‚’' => 'å€°',
  '‚“' => 'å€±',
  '‚”' => 'å€²',
  '‚•' => 'å€³',
  '‚–' => 'å€´',
  '‚—' => 'å€µ',
  '‚˜' => 'å€¶',
  '‚™' => 'å€·',
  '‚š' => 'å€¸',
  '‚›' => 'å€¹',
  '‚œ' => 'å€»',
  '‚�' => 'å€½',
  '‚ž' => 'å€¿',
  '‚Ÿ' => 'å�€',
  '‚ ' => 'å��',
  '‚¡' => 'å�‚',
  '‚¢' => 'å�„',
  '‚£' => 'å�…',
  '‚¤' => 'å�†',
  '‚¥' => 'å�‰',
  '‚¦' => 'å�Š',
  '‚§' => 'å�‹',
  '‚¨' => 'å��',
  '‚©' => 'å��',
  '‚ª' => 'å�‘',
  '‚«' => 'å�’',
  '‚¬' => 'å�“',
  '‚­' => 'å�”',
  '‚®' => 'å�–',
  '‚¯' => 'å�—',
  '‚°' => 'å�˜',
  '‚±' => 'å�™',
  '‚²' => 'å�›',
  '‚³' => 'å��',
  '‚´' => 'å�ž',
  '‚µ' => 'å�Ÿ',
  '‚¶' => 'å� ',
  '‚·' => 'å�¡',
  '‚¸' => 'å�¢',
  '‚¹' => 'å�£',
  '‚º' => 'å�¤',
  '‚»' => 'å�¦',
  '‚¼' => 'å�§',
  '‚½' => 'å�¨',
  '‚¾' => 'å�©',
  '‚¿' => 'å�ª',
  '‚À' => 'å�«',
  '‚Á' => 'å�­',
  '‚Â' => 'å�®',
  '‚Ã' => 'å�¯',
  '‚Ä' => 'å�°',
  '‚Å' => 'å�±',
  '‚Æ' => 'å�²',
  '‚Ç' => 'å�³',
  '‚È' => 'å�´',
  '‚É' => 'å�µ',
  '‚Ê' => 'å�¸',
  '‚Ë' => 'å�¹',
  '‚Ì' => 'å�º',
  '‚Í' => 'å�¼',
  '‚Î' => 'å�½',
  '‚Ï' => 'å‚�',
  '‚Ð' => 'å‚‚',
  '‚Ñ' => 'å‚ƒ',
  '‚Ò' => 'å‚„',
  '‚Ó' => 'å‚†',
  '‚Ô' => 'å‚‡',
  '‚Õ' => 'å‚‰',
  '‚Ö' => 'å‚Š',
  '‚×' => 'å‚‹',
  '‚Ø' => 'å‚Œ',
  '‚Ù' => 'å‚Ž',
  '‚Ú' => 'å‚�',
  '‚Û' => 'å‚�',
  '‚Ü' => 'å‚‘',
  '‚Ý' => 'å‚’',
  '‚Þ' => 'å‚“',
  '‚ß' => 'å‚”',
  '‚à' => 'å‚•',
  '‚á' => 'å‚–',
  '‚â' => 'å‚—',
  '‚ã' => 'å‚˜',
  '‚ä' => 'å‚™',
  '‚å' => 'å‚š',
  '‚æ' => 'å‚›',
  '‚ç' => 'å‚œ',
  '‚è' => 'å‚�',
  '‚é' => 'å‚ž',
  '‚ê' => 'å‚Ÿ',
  '‚ë' => 'å‚ ',
  '‚ì' => 'å‚¡',
  '‚í' => 'å‚¢',
  '‚î' => 'å‚¤',
  '‚ï' => 'å‚¦',
  '‚ð' => 'å‚ª',
  '‚ñ' => 'å‚«',
  '‚ò' => 'å‚­',
  '‚ó' => 'å‚®',
  '‚ô' => 'å‚¯',
  '‚õ' => 'å‚°',
  '‚ö' => 'å‚±',
  '‚÷' => 'å‚³',
  '‚ø' => 'å‚´',
  '‚ù' => 'å‚µ',
  '‚ú' => 'å‚¶',
  '‚û' => 'å‚·',
  '‚ü' => 'å‚¸',
  '‚ý' => 'å‚¹',
  '‚þ' => 'å‚¼',
  'ƒ@' => 'å‚½',
  'ƒA' => 'å‚¾',
  'ƒB' => 'å‚¿',
  'ƒC' => 'åƒ€',
  'ƒD' => 'åƒ�',
  'ƒE' => 'åƒ‚',
  'ƒF' => 'åƒƒ',
  'ƒG' => 'åƒ„',
  'ƒH' => 'åƒ…',
  'ƒI' => 'åƒ†',
  'ƒJ' => 'åƒ‡',
  'ƒK' => 'åƒˆ',
  'ƒL' => 'åƒ‰',
  'ƒM' => 'åƒŠ',
  'ƒN' => 'åƒ‹',
  'ƒO' => 'åƒŒ',
  'ƒP' => 'åƒ�',
  'ƒQ' => 'åƒŽ',
  'ƒR' => 'åƒ�',
  'ƒS' => 'åƒ‘',
  'ƒT' => 'åƒ’',
  'ƒU' => 'åƒ“',
  'ƒV' => 'åƒ”',
  'ƒW' => 'åƒ•',
  'ƒX' => 'åƒ—',
  'ƒY' => 'åƒ˜',
  'ƒZ' => 'åƒ™',
  'ƒ[' => 'åƒ›',
  'ƒ\\' => 'åƒœ',
  'ƒ]' => 'åƒ�',
  'ƒ^' => 'åƒž',
  'ƒ_' => 'åƒŸ',
  'ƒ`' => 'åƒ ',
  'ƒa' => 'åƒ¡',
  'ƒb' => 'åƒ¢',
  'ƒc' => 'åƒ£',
  'ƒd' => 'åƒ¤',
  'ƒe' => 'åƒ¥',
  'ƒf' => 'åƒ¨',
  'ƒg' => 'åƒ©',
  'ƒh' => 'åƒª',
  'ƒi' => 'åƒ«',
  'ƒj' => 'åƒ¯',
  'ƒk' => 'åƒ°',
  'ƒl' => 'åƒ±',
  'ƒm' => 'åƒ²',
  'ƒn' => 'åƒ´',
  'ƒo' => 'åƒ¶',
  'ƒp' => 'åƒ·',
  'ƒq' => 'åƒ¸',
  'ƒr' => 'åƒ¹',
  'ƒs' => 'åƒº',
  'ƒt' => 'åƒ¼',
  'ƒu' => 'åƒ½',
  'ƒv' => 'åƒ¾',
  'ƒw' => 'åƒ¿',
  'ƒx' => 'å„€',
  'ƒy' => 'å„�',
  'ƒz' => 'å„‚',
  'ƒ{' => 'å„ƒ',
  'ƒ|' => 'å„„',
  'ƒ}' => 'å„…',
  'ƒ~' => 'å„ˆ',
  'ƒ€' => 'å„‰',
  'ƒ�' => 'å„Š',
  'ƒ‚' => 'å„Œ',
  'ƒƒ' => 'å„�',
  'ƒ„' => 'å„Ž',
  'ƒ…' => 'å„�',
  'ƒ†' => 'å„�',
  'ƒ‡' => 'å„‘',
  'ƒˆ' => 'å„“',
  'ƒ‰' => 'å„”',
  'ƒŠ' => 'å„•',
  'ƒ‹' => 'å„–',
  'ƒŒ' => 'å„—',
  'ƒ�' => 'å„˜',
  'ƒŽ' => 'å„™',
  'ƒ�' => 'å„š',
  'ƒ�' => 'å„›',
  'ƒ‘' => 'å„œ',
  'ƒ’' => 'å„�',
  'ƒ“' => 'å„ž',
  'ƒ”' => 'å„Ÿ',
  'ƒ•' => 'å„ ',
  'ƒ–' => 'å„¢',
  'ƒ—' => 'å„£',
  'ƒ˜' => 'å„¤',
  'ƒ™' => 'å„¥',
  'ƒš' => 'å„¦',
  'ƒ›' => 'å„§',
  'ƒœ' => 'å„¨',
  'ƒ�' => 'å„©',
  'ƒž' => 'å„ª',
  'ƒŸ' => 'å„«',
  'ƒ ' => 'å„¬',
  'ƒ¡' => 'å„­',
  'ƒ¢' => 'å„®',
  'ƒ£' => 'å„¯',
  'ƒ¤' => 'å„°',
  'ƒ¥' => 'å„±',
  'ƒ¦' => 'å„²',
  'ƒ§' => 'å„³',
  'ƒ¨' => 'å„´',
  'ƒ©' => 'å„µ',
  'ƒª' => 'å„¶',
  'ƒ«' => 'å„·',
  'ƒ¬' => 'å„¸',
  'ƒ­' => 'å„¹',
  'ƒ®' => 'å„º',
  'ƒ¯' => 'å„»',
  'ƒ°' => 'å„¼',
  'ƒ±' => 'å„½',
  'ƒ²' => 'å„¾',
  'ƒ³' => 'å…‚',
  'ƒ´' => 'å…‡',
  'ƒµ' => 'å…Š',
  'ƒ¶' => 'å…Œ',
  'ƒ·' => 'å…Ž',
  'ƒ¸' => 'å…�',
  'ƒ¹' => 'å…�',
  'ƒº' => 'å…’',
  'ƒ»' => 'å…“',
  'ƒ¼' => 'å…—',
  'ƒ½' => 'å…˜',
  'ƒ¾' => 'å…™',
  'ƒ¿' => 'å…›',
  'ƒÀ' => 'å…�',
  'ƒÁ' => 'å…ž',
  'ƒÂ' => 'å…Ÿ',
  'ƒÃ' => 'å… ',
  'ƒÄ' => 'å…¡',
  'ƒÅ' => 'å…£',
  'ƒÆ' => 'å…¤',
  'ƒÇ' => 'å…¦',
  'ƒÈ' => 'å…§',
  'ƒÉ' => 'å…©',
  'ƒÊ' => 'å…ª',
  'ƒË' => 'å…¯',
  'ƒÌ' => 'å…²',
  'ƒÍ' => 'å…º',
  'ƒÎ' => 'å…¾',
  'ƒÏ' => 'å…¿',
  'ƒÐ' => 'å†ƒ',
  'ƒÑ' => 'å†„',
  'ƒÒ' => 'å††',
  'ƒÓ' => 'å†‡',
  'ƒÔ' => 'å†Š',
  'ƒÕ' => 'å†‹',
  'ƒÖ' => 'å†Ž',
  'ƒ×' => 'å†�',
  'ƒØ' => 'å†�',
  'ƒÙ' => 'å†‘',
  'ƒÚ' => 'å†“',
  'ƒÛ' => 'å†”',
  'ƒÜ' => 'å†˜',
  'ƒÝ' => 'å†š',
  'ƒÞ' => 'å†�',
  'ƒß' => 'å†ž',
  'ƒà' => 'å†Ÿ',
  'ƒá' => 'å†¡',
  'ƒâ' => 'å†£',
  'ƒã' => 'å†¦',
  'ƒä' => 'å†§',
  'ƒå' => 'å†¨',
  'ƒæ' => 'å†©',
  'ƒç' => 'å†ª',
  'ƒè' => 'å†­',
  'ƒé' => 'å†®',
  'ƒê' => 'å†´',
  'ƒë' => 'å†¸',
  'ƒì' => 'å†¹',
  'ƒí' => 'å†º',
  'ƒî' => 'å†¾',
  'ƒï' => 'å†¿',
  'ƒð' => 'å‡�',
  'ƒñ' => 'å‡‚',
  'ƒò' => 'å‡ƒ',
  'ƒó' => 'å‡…',
  'ƒô' => 'å‡ˆ',
  'ƒõ' => 'å‡Š',
  'ƒö' => 'å‡�',
  'ƒ÷' => 'å‡Ž',
  'ƒø' => 'å‡�',
  'ƒù' => 'å‡’',
  'ƒú' => 'å‡“',
  'ƒû' => 'å‡”',
  'ƒü' => 'å‡•',
  'ƒý' => 'å‡–',
  'ƒþ' => 'å‡—',
  '„@' => 'å‡˜',
  '„A' => 'å‡™',
  '„B' => 'å‡š',
  '„C' => 'å‡œ',
  '„D' => 'å‡ž',
  '„E' => 'å‡Ÿ',
  '„F' => 'å‡¢',
  '„G' => 'å‡£',
  '„H' => 'å‡¥',
  '„I' => 'å‡¦',
  '„J' => 'å‡§',
  '„K' => 'å‡¨',
  '„L' => 'å‡©',
  '„M' => 'å‡ª',
  '„N' => 'å‡¬',
  '„O' => 'å‡®',
  '„P' => 'å‡±',
  '„Q' => 'å‡²',
  '„R' => 'å‡´',
  '„S' => 'å‡·',
  '„T' => 'å‡¾',
  '„U' => 'åˆ„',
  '„V' => 'åˆ…',
  '„W' => 'åˆ‰',
  '„X' => 'åˆ‹',
  '„Y' => 'åˆŒ',
  '„Z' => 'åˆ�',
  '„[' => 'åˆ�',
  '„\\' => 'åˆ“',
  '„]' => 'åˆ”',
  '„^' => 'åˆ•',
  '„_' => 'åˆœ',
  '„`' => 'åˆž',
  '„a' => 'åˆŸ',
  '„b' => 'åˆ¡',
  '„c' => 'åˆ¢',
  '„d' => 'åˆ£',
  '„e' => 'åˆ¥',
  '„f' => 'åˆ¦',
  '„g' => 'åˆ§',
  '„h' => 'åˆª',
  '„i' => 'åˆ¬',
  '„j' => 'åˆ¯',
  '„k' => 'åˆ±',
  '„l' => 'åˆ²',
  '„m' => 'åˆ´',
  '„n' => 'åˆµ',
  '„o' => 'åˆ¼',
  '„p' => 'åˆ¾',
  '„q' => 'å‰„',
  '„r' => 'å‰…',
  '„s' => 'å‰†',
  '„t' => 'å‰‡',
  '„u' => 'å‰ˆ',
  '„v' => 'å‰‰',
  '„w' => 'å‰‹',
  '„x' => 'å‰Ž',
  '„y' => 'å‰�',
  '„z' => 'å‰’',
  '„{' => 'å‰“',
  '„|' => 'å‰•',
  '„}' => 'å‰—',
  '„~' => 'å‰˜',
  '„€' => 'å‰™',
  '„�' => 'å‰š',
  '„‚' => 'å‰›',
  '„ƒ' => 'å‰�',
  '„„' => 'å‰Ÿ',
  '„…' => 'å‰ ',
  '„†' => 'å‰¢',
  '„‡' => 'å‰£',
  '„ˆ' => 'å‰¤',
  '„‰' => 'å‰¦',
  '„Š' => 'å‰¨',
  '„‹' => 'å‰«',
  '„Œ' => 'å‰¬',
  '„�' => 'å‰­',
  '„Ž' => 'å‰®',
  '„�' => 'å‰°',
  '„�' => 'å‰±',
  '„‘' => 'å‰³',
  '„’' => 'å‰´',
  '„“' => 'å‰µ',
  '„”' => 'å‰¶',
  '„•' => 'å‰·',
  '„–' => 'å‰¸',
  '„—' => 'å‰¹',
  '„˜' => 'å‰º',
  '„™' => 'å‰»',
  '„š' => 'å‰¼',
  '„›' => 'å‰¾',
  '„œ' => 'åŠ€',
  '„�' => 'åŠƒ',
  '„ž' => 'åŠ„',
  '„Ÿ' => 'åŠ…',
  '„ ' => 'åŠ†',
  '„¡' => 'åŠ‡',
  '„¢' => 'åŠ‰',
  '„£' => 'åŠŠ',
  '„¤' => 'åŠ‹',
  '„¥' => 'åŠŒ',
  '„¦' => 'åŠ�',
  '„§' => 'åŠŽ',
  '„¨' => 'åŠ�',
  '„©' => 'åŠ‘',
  '„ª' => 'åŠ’',
  '„«' => 'åŠ”',
  '„¬' => 'åŠ•',
  '„­' => 'åŠ–',
  '„®' => 'åŠ—',
  '„¯' => 'åŠ˜',
  '„°' => 'åŠ™',
  '„±' => 'åŠš',
  '„²' => 'åŠœ',
  '„³' => 'åŠ¤',
  '„´' => 'åŠ¥',
  '„µ' => 'åŠ¦',
  '„¶' => 'åŠ§',
  '„·' => 'åŠ®',
  '„¸' => 'åŠ¯',
  '„¹' => 'åŠ°',
  '„º' => 'åŠ´',
  '„»' => 'åŠµ',
  '„¼' => 'åŠ¶',
  '„½' => 'åŠ·',
  '„¾' => 'åŠ¸',
  '„¿' => 'åŠ¹',
  '„À' => 'åŠº',
  '„Á' => 'åŠ»',
  '„Â' => 'åŠ¼',
  '„Ã' => 'åŠ½',
  '„Ä' => 'å‹€',
  '„Å' => 'å‹�',
  '„Æ' => 'å‹‚',
  '„Ç' => 'å‹„',
  '„È' => 'å‹…',
  '„É' => 'å‹†',
  '„Ê' => 'å‹ˆ',
  '„Ë' => 'å‹Š',
  '„Ì' => 'å‹Œ',
  '„Í' => 'å‹�',
  '„Î' => 'å‹Ž',
  '„Ï' => 'å‹�',
  '„Ð' => 'å‹‘',
  '„Ñ' => 'å‹“',
  '„Ò' => 'å‹”',
  '„Ó' => 'å‹•',
  '„Ô' => 'å‹—',
  '„Õ' => 'å‹™',
  '„Ö' => 'å‹š',
  '„×' => 'å‹›',
  '„Ø' => 'å‹œ',
  '„Ù' => 'å‹�',
  '„Ú' => 'å‹ž',
  '„Û' => 'å‹ ',
  '„Ü' => 'å‹¡',
  '„Ý' => 'å‹¢',
  '„Þ' => 'å‹£',
  '„ß' => 'å‹¥',
  '„à' => 'å‹¦',
  '„á' => 'å‹§',
  '„â' => 'å‹¨',
  '„ã' => 'å‹©',
  '„ä' => 'å‹ª',
  '„å' => 'å‹«',
  '„æ' => 'å‹¬',
  '„ç' => 'å‹­',
  '„è' => 'å‹®',
  '„é' => 'å‹¯',
  '„ê' => 'å‹±',
  '„ë' => 'å‹²',
  '„ì' => 'å‹³',
  '„í' => 'å‹´',
  '„î' => 'å‹µ',
  '„ï' => 'å‹¶',
  '„ð' => 'å‹·',
  '„ñ' => 'å‹¸',
  '„ò' => 'å‹»',
  '„ó' => 'å‹¼',
  '„ô' => 'å‹½',
  '„õ' => 'åŒ�',
  '„ö' => 'åŒ‚',
  '„÷' => 'åŒƒ',
  '„ø' => 'åŒ„',
  '„ù' => 'åŒ‡',
  '„ú' => 'åŒ‰',
  '„û' => 'åŒŠ',
  '„ü' => 'åŒ‹',
  '„ý' => 'åŒŒ',
  '„þ' => 'åŒŽ',
  '…@' => 'åŒ‘',
  '…A' => 'åŒ’',
  '…B' => 'åŒ“',
  '…C' => 'åŒ”',
  '…D' => 'åŒ˜',
  '…E' => 'åŒ›',
  '…F' => 'åŒœ',
  '…G' => 'åŒž',
  '…H' => 'åŒŸ',
  '…I' => 'åŒ¢',
  '…J' => 'åŒ¤',
  '…K' => 'åŒ¥',
  '…L' => 'åŒ§',
  '…M' => 'åŒ¨',
  '…N' => 'åŒ©',
  '…O' => 'åŒ«',
  '…P' => 'åŒ¬',
  '…Q' => 'åŒ­',
  '…R' => 'åŒ¯',
  '…S' => 'åŒ°',
  '…T' => 'åŒ±',
  '…U' => 'åŒ²',
  '…V' => 'åŒ³',
  '…W' => 'åŒ´',
  '…X' => 'åŒµ',
  '…Y' => 'åŒ¶',
  '…Z' => 'åŒ·',
  '…[' => 'åŒ¸',
  '…\\' => 'åŒ¼',
  '…]' => 'åŒ½',
  '…^' => 'å�€',
  '…_' => 'å�‚',
  '…`' => 'å�„',
  '…a' => 'å�†',
  '…b' => 'å�‹',
  '…c' => 'å�Œ',
  '…d' => 'å��',
  '…e' => 'å��',
  '…f' => 'å�”',
  '…g' => 'å�˜',
  '…h' => 'å�™',
  '…i' => 'å�›',
  '…j' => 'å��',
  '…k' => 'å�¥',
  '…l' => 'å�¨',
  '…m' => 'å�ª',
  '…n' => 'å�¬',
  '…o' => 'å�­',
  '…p' => 'å�²',
  '…q' => 'å�¶',
  '…r' => 'å�¹',
  '…s' => 'å�»',
  '…t' => 'å�¼',
  '…u' => 'å�½',
  '…v' => 'å�¾',
  '…w' => 'åŽ€',
  '…x' => 'åŽ�',
  '…y' => 'åŽƒ',
  '…z' => 'åŽ‡',
  '…{' => 'åŽˆ',
  '…|' => 'åŽŠ',
  '…}' => 'åŽŽ',
  '…~' => 'åŽ�',
  '…€' => 'åŽ�',
  '…�' => 'åŽ‘',
  '…‚' => 'åŽ’',
  '…ƒ' => 'åŽ“',
  '…„' => 'åŽ”',
  '……' => 'åŽ–',
  '…†' => 'åŽ—',
  '…‡' => 'åŽ™',
  '…ˆ' => 'åŽ›',
  '…‰' => 'åŽœ',
  '…Š' => 'åŽž',
  '…‹' => 'åŽ ',
  '…Œ' => 'åŽ¡',
  '…�' => 'åŽ¤',
  '…Ž' => 'åŽ§',
  '…�' => 'åŽª',
  '…�' => 'åŽ«',
  '…‘' => 'åŽ¬',
  '…’' => 'åŽ­',
  '…“' => 'åŽ¯',
  '…”' => 'åŽ°',
  '…•' => 'åŽ±',
  '…–' => 'åŽ²',
  '…—' => 'åŽ³',
  '…˜' => 'åŽ´',
  '…™' => 'åŽµ',
  '…š' => 'åŽ·',
  '…›' => 'åŽ¸',
  '…œ' => 'åŽ¹',
  '…�' => 'åŽº',
  '…ž' => 'åŽ¼',
  '…Ÿ' => 'åŽ½',
  '… ' => 'åŽ¾',
  '…¡' => 'å�€',
  '…¢' => 'å�ƒ',
  '…£' => 'å�„',
  '…¤' => 'å�…',
  '…¥' => 'å�†',
  '…¦' => 'å�‡',
  '…§' => 'å�Ž',
  '…¨' => 'å��',
  '…©' => 'å��',
  '…ª' => 'å�’',
  '…«' => 'å�“',
  '…¬' => 'å�•',
  '…­' => 'å�š',
  '…®' => 'å�œ',
  '…¯' => 'å��',
  '…°' => 'å�ž',
  '…±' => 'å�¡',
  '…²' => 'å�¢',
  '…³' => 'å�§',
  '…´' => 'å�´',
  '…µ' => 'å�º',
  '…¶' => 'å�¾',
  '…·' => 'å�¿',
  '…¸' => 'å�€',
  '…¹' => 'å�‚',
  '…º' => 'å�…',
  '…»' => 'å�‡',
  '…¼' => 'å�‹',
  '…½' => 'å�”',
  '…¾' => 'å�˜',
  '…¿' => 'å�™',
  '…À' => 'å�š',
  '…Á' => 'å�œ',
  '…Â' => 'å�¢',
  '…Ã' => 'å�¤',
  '…Ä' => 'å�¥',
  '…Å' => 'å�ª',
  '…Æ' => 'å�°',
  '…Ç' => 'å�³',
  '…È' => 'å�¶',
  '…É' => 'å�·',
  '…Ê' => 'å�º',
  '…Ë' => 'å�½',
  '…Ì' => 'å�¿',
  '…Í' => 'å‘�',
  '…Î' => 'å‘‚',
  '…Ï' => 'å‘„',
  '…Ð' => 'å‘…',
  '…Ñ' => 'å‘‡',
  '…Ò' => 'å‘‰',
  '…Ó' => 'å‘Œ',
  '…Ô' => 'å‘�',
  '…Õ' => 'å‘Ž',
  '…Ö' => 'å‘�',
  '…×' => 'å‘‘',
  '…Ø' => 'å‘š',
  '…Ù' => 'å‘�',
  '…Ú' => 'å‘ž',
  '…Û' => 'å‘Ÿ',
  '…Ü' => 'å‘ ',
  '…Ý' => 'å‘¡',
  '…Þ' => 'å‘£',
  '…ß' => 'å‘¥',
  '…à' => 'å‘§',
  '…á' => 'å‘©',
  '…â' => 'å‘ª',
  '…ã' => 'å‘«',
  '…ä' => 'å‘¬',
  '…å' => 'å‘­',
  '…æ' => 'å‘®',
  '…ç' => 'å‘¯',
  '…è' => 'å‘°',
  '…é' => 'å‘´',
  '…ê' => 'å‘¹',
  '…ë' => 'å‘º',
  '…ì' => 'å‘¾',
  '…í' => 'å‘¿',
  '…î' => 'å’�',
  '…ï' => 'å’ƒ',
  '…ð' => 'å’…',
  '…ñ' => 'å’‡',
  '…ò' => 'å’ˆ',
  '…ó' => 'å’‰',
  '…ô' => 'å’Š',
  '…õ' => 'å’�',
  '…ö' => 'å’‘',
  '…÷' => 'å’“',
  '…ø' => 'å’—',
  '…ù' => 'å’˜',
  '…ú' => 'å’œ',
  '…û' => 'å’ž',
  '…ü' => 'å’Ÿ',
  '…ý' => 'å’ ',
  '…þ' => 'å’¡',
  '†@' => 'å’¢',
  '†A' => 'å’¥',
  '†B' => 'å’®',
  '†C' => 'å’°',
  '†D' => 'å’²',
  '†E' => 'å’µ',
  '†F' => 'å’¶',
  '†G' => 'å’·',
  '†H' => 'å’¹',
  '†I' => 'å’º',
  '†J' => 'å’¼',
  '†K' => 'å’¾',
  '†L' => 'å“ƒ',
  '†M' => 'å“…',
  '†N' => 'å“Š',
  '†O' => 'å“‹',
  '†P' => 'å“–',
  '†Q' => 'å“˜',
  '†R' => 'å“›',
  '†S' => 'å“ ',
  '†T' => 'å“¡',
  '†U' => 'å“¢',
  '†V' => 'å“£',
  '†W' => 'å“¤',
  '†X' => 'å“«',
  '†Y' => 'å“¬',
  '†Z' => 'å“¯',
  '†[' => 'å“°',
  '†\\' => 'å“±',
  '†]' => 'å“´',
  '†^' => 'å“µ',
  '†_' => 'å“¶',
  '†`' => 'å“·',
  '†a' => 'å“¸',
  '†b' => 'å“¹',
  '†c' => 'å“»',
  '†d' => 'å“¾',
  '†e' => 'å”€',
  '†f' => 'å”‚',
  '†g' => 'å”ƒ',
  '†h' => 'å”„',
  '†i' => 'å”…',
  '†j' => 'å”ˆ',
  '†k' => 'å”Š',
  '†l' => 'å”‹',
  '†m' => 'å”Œ',
  '†n' => 'å”�',
  '†o' => 'å”Ž',
  '†p' => 'å”’',
  '†q' => 'å”“',
  '†r' => 'å”•',
  '†s' => 'å”–',
  '†t' => 'å”—',
  '†u' => 'å”˜',
  '†v' => 'å”™',
  '†w' => 'å”š',
  '†x' => 'å”œ',
  '†y' => 'å”�',
  '†z' => 'å”ž',
  '†{' => 'å”Ÿ',
  '†|' => 'å”¡',
  '†}' => 'å”¥',
  '†~' => 'å”¦',
  '†€' => 'å”¨',
  '†�' => 'å”©',
  '†‚' => 'å”«',
  '†ƒ' => 'å”­',
  '†„' => 'å”²',
  '†…' => 'å”´',
  '††' => 'å”µ',
  '†‡' => 'å”¶',
  '†ˆ' => 'å”¸',
  '†‰' => 'å”¹',
  '†Š' => 'å”º',
  '†‹' => 'å”»',
  '†Œ' => 'å”½',
  '†�' => 'å•€',
  '†Ž' => 'å•‚',
  '†�' => 'å•…',
  '†�' => 'å•‡',
  '†‘' => 'å•ˆ',
  '†’' => 'å•‹',
  '†“' => 'å•Œ',
  '†”' => 'å•�',
  '†•' => 'å•Ž',
  '†–' => 'å•�',
  '†—' => 'å•‘',
  '†˜' => 'å•’',
  '†™' => 'å•“',
  '†š' => 'å•”',
  '†›' => 'å•—',
  '†œ' => 'å•˜',
  '†�' => 'å•™',
  '†ž' => 'å•š',
  '†Ÿ' => 'å•›',
  '† ' => 'å•�',
  '†¡' => 'å•ž',
  '†¢' => 'å•Ÿ',
  '†£' => 'å• ',
  '†¤' => 'å•¢',
  '†¥' => 'å•£',
  '†¦' => 'å•¨',
  '†§' => 'å•©',
  '†¨' => 'å•«',
  '†©' => 'å•¯',
  '†ª' => 'å•°',
  '†«' => 'å•±',
  '†¬' => 'å•²',
  '†­' => 'å•³',
  '†®' => 'å•´',
  '†¯' => 'å•¹',
  '†°' => 'å•º',
  '†±' => 'å•½',
  '†²' => 'å•¿',
  '†³' => 'å–…',
  '†´' => 'å–†',
  '†µ' => 'å–Œ',
  '†¶' => 'å–�',
  '†·' => 'å–Ž',
  '†¸' => 'å–�',
  '†¹' => 'å–’',
  '†º' => 'å–“',
  '†»' => 'å–•',
  '†¼' => 'å––',
  '†½' => 'å–—',
  '†¾' => 'å–š',
  '†¿' => 'å–›',
  '†À' => 'å–ž',
  '†Á' => 'å– ',
  '†Â' => 'å–¡',
  '†Ã' => 'å–¢',
  '†Ä' => 'å–£',
  '†Å' => 'å–¤',
  '†Æ' => 'å–¥',
  '†Ç' => 'å–¦',
  '†È' => 'å–¨',
  '†É' => 'å–©',
  '†Ê' => 'å–ª',
  '†Ë' => 'å–«',
  '†Ì' => 'å–¬',
  '†Í' => 'å–­',
  '†Î' => 'å–®',
  '†Ï' => 'å–¯',
  '†Ð' => 'å–°',
  '†Ñ' => 'å–²',
  '†Ò' => 'å–´',
  '†Ó' => 'å–¶',
  '†Ô' => 'å–¸',
  '†Õ' => 'å–º',
  '†Ö' => 'å–¼',
  '†×' => 'å–¿',
  '†Ø' => 'å—€',
  '†Ù' => 'å—�',
  '†Ú' => 'å—‚',
  '†Û' => 'å—ƒ',
  '†Ü' => 'å—†',
  '†Ý' => 'å—‡',
  '†Þ' => 'å—ˆ',
  '†ß' => 'å—Š',
  '†à' => 'å—‹',
  '†á' => 'å—Ž',
  '†â' => 'å—�',
  '†ã' => 'å—�',
  '†ä' => 'å—•',
  '†å' => 'å——',
  '†æ' => 'å—˜',
  '†ç' => 'å—™',
  '†è' => 'å—š',
  '†é' => 'å—›',
  '†ê' => 'å—ž',
  '†ë' => 'å— ',
  '†ì' => 'å—¢',
  '†í' => 'å—§',
  '†î' => 'å—©',
  '†ï' => 'å—­',
  '†ð' => 'å—®',
  '†ñ' => 'å—°',
  '†ò' => 'å—±',
  '†ó' => 'å—´',
  '†ô' => 'å—¶',
  '†õ' => 'å—¸',
  '†ö' => 'å—¹',
  '†÷' => 'å—º',
  '†ø' => 'å—»',
  '†ù' => 'å—¼',
  '†ú' => 'å—¿',
  '†û' => 'å˜‚',
  '†ü' => 'å˜ƒ',
  '†ý' => 'å˜„',
  '†þ' => 'å˜…',
  '‡@' => 'å˜†',
  '‡A' => 'å˜‡',
  '‡B' => 'å˜Š',
  '‡C' => 'å˜‹',
  '‡D' => 'å˜�',
  '‡E' => 'å˜�',
  '‡F' => 'å˜‘',
  '‡G' => 'å˜’',
  '‡H' => 'å˜“',
  '‡I' => 'å˜”',
  '‡J' => 'å˜•',
  '‡K' => 'å˜–',
  '‡L' => 'å˜—',
  '‡M' => 'å˜™',
  '‡N' => 'å˜š',
  '‡O' => 'å˜œ',
  '‡P' => 'å˜�',
  '‡Q' => 'å˜ ',
  '‡R' => 'å˜¡',
  '‡S' => 'å˜¢',
  '‡T' => 'å˜¥',
  '‡U' => 'å˜¦',
  '‡V' => 'å˜¨',
  '‡W' => 'å˜©',
  '‡X' => 'å˜ª',
  '‡Y' => 'å˜«',
  '‡Z' => 'å˜®',
  '‡[' => 'å˜¯',
  '‡\\' => 'å˜°',
  '‡]' => 'å˜³',
  '‡^' => 'å˜µ',
  '‡_' => 'å˜·',
  '‡`' => 'å˜¸',
  '‡a' => 'å˜º',
  '‡b' => 'å˜¼',
  '‡c' => 'å˜½',
  '‡d' => 'å˜¾',
  '‡e' => 'å™€',
  '‡f' => 'å™�',
  '‡g' => 'å™‚',
  '‡h' => 'å™ƒ',
  '‡i' => 'å™„',
  '‡j' => 'å™…',
  '‡k' => 'å™†',
  '‡l' => 'å™‡',
  '‡m' => 'å™ˆ',
  '‡n' => 'å™‰',
  '‡o' => 'å™Š',
  '‡p' => 'å™‹',
  '‡q' => 'å™�',
  '‡r' => 'å™�',
  '‡s' => 'å™‘',
  '‡t' => 'å™’',
  '‡u' => 'å™“',
  '‡v' => 'å™•',
  '‡w' => 'å™–',
  '‡x' => 'å™š',
  '‡y' => 'å™›',
  '‡z' => 'å™�',
  '‡{' => 'å™ž',
  '‡|' => 'å™Ÿ',
  '‡}' => 'å™ ',
  '‡~' => 'å™¡',
  '‡€' => 'å™£',
  '‡�' => 'å™¥',
  '‡‚' => 'å™¦',
  '‡ƒ' => 'å™§',
  '‡„' => 'å™­',
  '‡…' => 'å™®',
  '‡†' => 'å™¯',
  '‡‡' => 'å™°',
  '‡ˆ' => 'å™²',
  '‡‰' => 'å™³',
  '‡Š' => 'å™´',
  '‡‹' => 'å™µ',
  '‡Œ' => 'å™·',
  '‡�' => 'å™¸',
  '‡Ž' => 'å™¹',
  '‡�' => 'å™º',
  '‡�' => 'å™½',
  '‡‘' => 'å™¾',
  '‡’' => 'å™¿',
  '‡“' => 'åš€',
  '‡”' => 'åš�',
  '‡•' => 'åš‚',
  '‡–' => 'åšƒ',
  '‡—' => 'åš„',
  '‡˜' => 'åš‡',
  '‡™' => 'åšˆ',
  '‡š' => 'åš‰',
  '‡›' => 'åšŠ',
  '‡œ' => 'åš‹',
  '‡�' => 'åšŒ',
  '‡ž' => 'åš�',
  '‡Ÿ' => 'åš�',
  '‡ ' => 'åš‘',
  '‡¡' => 'åš’',
  '‡¢' => 'åš”',
  '‡£' => 'åš•',
  '‡¤' => 'åš–',
  '‡¥' => 'åš—',
  '‡¦' => 'åš˜',
  '‡§' => 'åš™',
  '‡¨' => 'åšš',
  '‡©' => 'åš›',
  '‡ª' => 'åšœ',
  '‡«' => 'åš�',
  '‡¬' => 'åšž',
  '‡­' => 'åšŸ',
  '‡®' => 'åš ',
  '‡¯' => 'åš¡',
  '‡°' => 'åš¢',
  '‡±' => 'åš¤',
  '‡²' => 'åš¥',
  '‡³' => 'åš¦',
  '‡´' => 'åš§',
  '‡µ' => 'åš¨',
  '‡¶' => 'åš©',
  '‡·' => 'åšª',
  '‡¸' => 'åš«',
  '‡¹' => 'åš¬',
  '‡º' => 'åš­',
  '‡»' => 'åš®',
  '‡¼' => 'åš°',
  '‡½' => 'åš±',
  '‡¾' => 'åš²',
  '‡¿' => 'åš³',
  '‡À' => 'åš´',
  '‡Á' => 'åšµ',
  '‡Â' => 'åš¶',
  '‡Ã' => 'åš¸',
  '‡Ä' => 'åš¹',
  '‡Å' => 'åšº',
  '‡Æ' => 'åš»',
  '‡Ç' => 'åš½',
  '‡È' => 'åš¾',
  '‡É' => 'åš¿',
  '‡Ê' => 'å›€',
  '‡Ë' => 'å›�',
  '‡Ì' => 'å›‚',
  '‡Í' => 'å›ƒ',
  '‡Î' => 'å›„',
  '‡Ï' => 'å›…',
  '‡Ð' => 'å›†',
  '‡Ñ' => 'å›‡',
  '‡Ò' => 'å›ˆ',
  '‡Ó' => 'å›‰',
  '‡Ô' => 'å›‹',
  '‡Õ' => 'å›Œ',
  '‡Ö' => 'å›�',
  '‡×' => 'å›Ž',
  '‡Ø' => 'å›�',
  '‡Ù' => 'å›�',
  '‡Ú' => 'å›‘',
  '‡Û' => 'å›’',
  '‡Ü' => 'å›“',
  '‡Ý' => 'å›•',
  '‡Þ' => 'å›–',
  '‡ß' => 'å›˜',
  '‡à' => 'å›™',
  '‡á' => 'å›œ',
  '‡â' => 'å›£',
  '‡ã' => 'å›¥',
  '‡ä' => 'å›¦',
  '‡å' => 'å›§',
  '‡æ' => 'å›¨',
  '‡ç' => 'å›©',
  '‡è' => 'å›ª',
  '‡é' => 'å›¬',
  '‡ê' => 'å›®',
  '‡ë' => 'å›¯',
  '‡ì' => 'å›²',
  '‡í' => 'å›³',
  '‡î' => 'å›¶',
  '‡ï' => 'å›·',
  '‡ð' => 'å›¸',
  '‡ñ' => 'å›»',
  '‡ò' => 'å›¼',
  '‡ó' => 'åœ€',
  '‡ô' => 'åœ�',
  '‡õ' => 'åœ‚',
  '‡ö' => 'åœ…',
  '‡÷' => 'åœ‡',
  '‡ø' => 'åœ‹',
  '‡ù' => 'åœŒ',
  '‡ú' => 'åœ�',
  '‡û' => 'åœŽ',
  '‡ü' => 'åœ�',
  '‡ý' => 'åœ�',
  '‡þ' => 'åœ‘',
  'ˆ@' => 'åœ’',
  'ˆA' => 'åœ“',
  'ˆB' => 'åœ”',
  'ˆC' => 'åœ•',
  'ˆD' => 'åœ–',
  'ˆE' => 'åœ—',
  'ˆF' => 'åœ˜',
  'ˆG' => 'åœ™',
  'ˆH' => 'åœš',
  'ˆI' => 'åœ›',
  'ˆJ' => 'åœ�',
  'ˆK' => 'åœž',
  'ˆL' => 'åœ ',
  'ˆM' => 'åœ¡',
  'ˆN' => 'åœ¢',
  'ˆO' => 'åœ¤',
  'ˆP' => 'åœ¥',
  'ˆQ' => 'åœ¦',
  'ˆR' => 'åœ§',
  'ˆS' => 'åœ«',
  'ˆT' => 'åœ±',
  'ˆU' => 'åœ²',
  'ˆV' => 'åœ´',
  'ˆW' => 'åœµ',
  'ˆX' => 'åœ¶',
  'ˆY' => 'åœ·',
  'ˆZ' => 'åœ¸',
  'ˆ[' => 'åœ¼',
  'ˆ\\' => 'åœ½',
  'ˆ]' => 'åœ¿',
  'ˆ^' => 'å��',
  'ˆ_' => 'å�ƒ',
  'ˆ`' => 'å�„',
  'ˆa' => 'å�…',
  'ˆb' => 'å�†',
  'ˆc' => 'å�ˆ',
  'ˆd' => 'å�‰',
  'ˆe' => 'å�‹',
  'ˆf' => 'å�’',
  'ˆg' => 'å�“',
  'ˆh' => 'å�”',
  'ˆi' => 'å�•',
  'ˆj' => 'å�–',
  'ˆk' => 'å�˜',
  'ˆl' => 'å�™',
  'ˆm' => 'å�¢',
  'ˆn' => 'å�£',
  'ˆo' => 'å�¥',
  'ˆp' => 'å�§',
  'ˆq' => 'å�¬',
  'ˆr' => 'å�®',
  'ˆs' => 'å�°',
  'ˆt' => 'å�±',
  'ˆu' => 'å�²',
  'ˆv' => 'å�´',
  'ˆw' => 'å�µ',
  'ˆx' => 'å�¸',
  'ˆy' => 'å�¹',
  'ˆz' => 'å�º',
  'ˆ{' => 'å�½',
  'ˆ|' => 'å�¾',
  'ˆ}' => 'å�¿',
  'ˆ~' => 'åž€',
  'ˆ€' => 'åž�',
  'ˆ�' => 'åž‡',
  'ˆ‚' => 'åžˆ',
  'ˆƒ' => 'åž‰',
  'ˆ„' => 'åžŠ',
  'ˆ…' => 'åž�',
  'ˆ†' => 'åžŽ',
  'ˆ‡' => 'åž�',
  'ˆˆ' => 'åž�',
  'ˆ‰' => 'åž‘',
  'ˆŠ' => 'åž”',
  'ˆ‹' => 'åž•',
  'ˆŒ' => 'åž–',
  'ˆ�' => 'åž—',
  'ˆŽ' => 'åž˜',
  'ˆ�' => 'åž™',
  'ˆ�' => 'åžš',
  'ˆ‘' => 'åžœ',
  'ˆ’' => 'åž�',
  'ˆ“' => 'åžž',
  'ˆ”' => 'åžŸ',
  'ˆ•' => 'åž¥',
  'ˆ–' => 'åž¨',
  'ˆ—' => 'åžª',
  'ˆ˜' => 'åž¬',
  'ˆ™' => 'åž¯',
  'ˆš' => 'åž°',
  'ˆ›' => 'åž±',
  'ˆœ' => 'åž³',
  'ˆ�' => 'åžµ',
  'ˆž' => 'åž¶',
  'ˆŸ' => 'åž·',
  'ˆ ' => 'åž¹',
  'ˆ¡' => 'åžº',
  'ˆ¢' => 'åž»',
  'ˆ£' => 'åž¼',
  'ˆ¤' => 'åž½',
  'ˆ¥' => 'åž¾',
  'ˆ¦' => 'åž¿',
  'ˆ§' => 'åŸ€',
  'ˆ¨' => 'åŸ�',
  'ˆ©' => 'åŸ„',
  'ˆª' => 'åŸ…',
  'ˆ«' => 'åŸ†',
  'ˆ¬' => 'åŸ‡',
  'ˆ­' => 'åŸˆ',
  'ˆ®' => 'åŸ‰',
  'ˆ¯' => 'åŸŠ',
  'ˆ°' => 'åŸŒ',
  'ˆ±' => 'åŸ�',
  'ˆ²' => 'åŸ�',
  'ˆ³' => 'åŸ‘',
  'ˆ´' => 'åŸ“',
  'ˆµ' => 'åŸ–',
  'ˆ¶' => 'åŸ—',
  'ˆ·' => 'åŸ›',
  'ˆ¸' => 'åŸœ',
  'ˆ¹' => 'åŸž',
  'ˆº' => 'åŸ¡',
  'ˆ»' => 'åŸ¢',
  'ˆ¼' => 'åŸ£',
  'ˆ½' => 'åŸ¥',
  'ˆ¾' => 'åŸ¦',
  'ˆ¿' => 'åŸ§',
  'ˆÀ' => 'åŸ¨',
  'ˆÁ' => 'åŸ©',
  'ˆÂ' => 'åŸª',
  'ˆÃ' => 'åŸ«',
  'ˆÄ' => 'åŸ¬',
  'ˆÅ' => 'åŸ®',
  'ˆÆ' => 'åŸ°',
  'ˆÇ' => 'åŸ±',
  'ˆÈ' => 'åŸ²',
  'ˆÉ' => 'åŸ³',
  'ˆÊ' => 'åŸµ',
  'ˆË' => 'åŸ¶',
  'ˆÌ' => 'åŸ·',
  'ˆÍ' => 'åŸ»',
  'ˆÎ' => 'åŸ¼',
  'ˆÏ' => 'åŸ¾',
  'ˆÐ' => 'åŸ¿',
  'ˆÑ' => 'å �',
  'ˆÒ' => 'å ƒ',
  'ˆÓ' => 'å „',
  'ˆÔ' => 'å …',
  'ˆÕ' => 'å ˆ',
  'ˆÖ' => 'å ‰',
  'ˆ×' => 'å Š',
  'ˆØ' => 'å Œ',
  'ˆÙ' => 'å Ž',
  'ˆÚ' => 'å �',
  'ˆÛ' => 'å �',
  'ˆÜ' => 'å ’',
  'ˆÝ' => 'å “',
  'ˆÞ' => 'å ”',
  'ˆß' => 'å –',
  'ˆà' => 'å —',
  'ˆá' => 'å ˜',
  'ˆâ' => 'å š',
  'ˆã' => 'å ›',
  'ˆä' => 'å œ',
  'ˆå' => 'å �',
  'ˆæ' => 'å Ÿ',
  'ˆç' => 'å ¢',
  'ˆè' => 'å £',
  'ˆé' => 'å ¥',
  'ˆê' => 'å ¦',
  'ˆë' => 'å §',
  'ˆì' => 'å ¨',
  'ˆí' => 'å ©',
  'ˆî' => 'å «',
  'ˆï' => 'å ¬',
  'ˆð' => 'å ­',
  'ˆñ' => 'å ®',
  'ˆò' => 'å ¯',
  'ˆó' => 'å ±',
  'ˆô' => 'å ²',
  'ˆõ' => 'å ³',
  'ˆö' => 'å ´',
  'ˆ÷' => 'å ¶',
  'ˆø' => 'å ·',
  'ˆù' => 'å ¸',
  'ˆú' => 'å ¹',
  'ˆû' => 'å º',
  'ˆü' => 'å »',
  'ˆý' => 'å ¼',
  'ˆþ' => 'å ½',
  '‰@' => 'å ¾',
  '‰A' => 'å ¿',
  '‰B' => 'å¡€',
  '‰C' => 'å¡�',
  '‰D' => 'å¡‚',
  '‰E' => 'å¡ƒ',
  '‰F' => 'å¡…',
  '‰G' => 'å¡†',
  '‰H' => 'å¡‡',
  '‰I' => 'å¡ˆ',
  '‰J' => 'å¡‰',
  '‰K' => 'å¡Š',
  '‰L' => 'å¡‹',
  '‰M' => 'å¡Ž',
  '‰N' => 'å¡�',
  '‰O' => 'å¡�',
  '‰P' => 'å¡’',
  '‰Q' => 'å¡“',
  '‰R' => 'å¡•',
  '‰S' => 'å¡–',
  '‰T' => 'å¡—',
  '‰U' => 'å¡™',
  '‰V' => 'å¡š',
  '‰W' => 'å¡›',
  '‰X' => 'å¡œ',
  '‰Y' => 'å¡�',
  '‰Z' => 'å¡Ÿ',
  '‰[' => 'å¡ ',
  '‰\\' => 'å¡¡',
  '‰]' => 'å¡¢',
  '‰^' => 'å¡£',
  '‰_' => 'å¡¤',
  '‰`' => 'å¡¦',
  '‰a' => 'å¡§',
  '‰b' => 'å¡¨',
  '‰c' => 'å¡©',
  '‰d' => 'å¡ª',
  '‰e' => 'å¡­',
  '‰f' => 'å¡®',
  '‰g' => 'å¡¯',
  '‰h' => 'å¡°',
  '‰i' => 'å¡±',
  '‰j' => 'å¡²',
  '‰k' => 'å¡³',
  '‰l' => 'å¡´',
  '‰m' => 'å¡µ',
  '‰n' => 'å¡¶',
  '‰o' => 'å¡·',
  '‰p' => 'å¡¸',
  '‰q' => 'å¡¹',
  '‰r' => 'å¡º',
  '‰s' => 'å¡»',
  '‰t' => 'å¡¼',
  '‰u' => 'å¡½',
  '‰v' => 'å¡¿',
  '‰w' => 'å¢‚',
  '‰x' => 'å¢„',
  '‰y' => 'å¢†',
  '‰z' => 'å¢‡',
  '‰{' => 'å¢ˆ',
  '‰|' => 'å¢Š',
  '‰}' => 'å¢‹',
  '‰~' => 'å¢Œ',
  '‰€' => 'å¢�',
  '‰�' => 'å¢Ž',
  '‰‚' => 'å¢�',
  '‰ƒ' => 'å¢�',
  '‰„' => 'å¢‘',
  '‰…' => 'å¢”',
  '‰†' => 'å¢•',
  '‰‡' => 'å¢–',
  '‰ˆ' => 'å¢—',
  '‰‰' => 'å¢˜',
  '‰Š' => 'å¢›',
  '‰‹' => 'å¢œ',
  '‰Œ' => 'å¢�',
  '‰�' => 'å¢ ',
  '‰Ž' => 'å¢¡',
  '‰�' => 'å¢¢',
  '‰�' => 'å¢£',
  '‰‘' => 'å¢¤',
  '‰’' => 'å¢¥',
  '‰“' => 'å¢¦',
  '‰”' => 'å¢§',
  '‰•' => 'å¢ª',
  '‰–' => 'å¢«',
  '‰—' => 'å¢¬',
  '‰˜' => 'å¢­',
  '‰™' => 'å¢®',
  '‰š' => 'å¢¯',
  '‰›' => 'å¢°',
  '‰œ' => 'å¢±',
  '‰�' => 'å¢²',
  '‰ž' => 'å¢³',
  '‰Ÿ' => 'å¢´',
  '‰ ' => 'å¢µ',
  '‰¡' => 'å¢¶',
  '‰¢' => 'å¢·',
  '‰£' => 'å¢¸',
  '‰¤' => 'å¢¹',
  '‰¥' => 'å¢º',
  '‰¦' => 'å¢»',
  '‰§' => 'å¢½',
  '‰¨' => 'å¢¾',
  '‰©' => 'å¢¿',
  '‰ª' => 'å£€',
  '‰«' => 'å£‚',
  '‰¬' => 'å£ƒ',
  '‰­' => 'å£„',
  '‰®' => 'å£†',
  '‰¯' => 'å£‡',
  '‰°' => 'å£ˆ',
  '‰±' => 'å£‰',
  '‰²' => 'å£Š',
  '‰³' => 'å£‹',
  '‰´' => 'å£Œ',
  '‰µ' => 'å£�',
  '‰¶' => 'å£Ž',
  '‰·' => 'å£�',
  '‰¸' => 'å£�',
  '‰¹' => 'å£’',
  '‰º' => 'å£“',
  '‰»' => 'å£”',
  '‰¼' => 'å£–',
  '‰½' => 'å£—',
  '‰¾' => 'å£˜',
  '‰¿' => 'å£™',
  '‰À' => 'å£š',
  '‰Á' => 'å£›',
  '‰Â' => 'å£œ',
  '‰Ã' => 'å£�',
  '‰Ä' => 'å£ž',
  '‰Å' => 'å£Ÿ',
  '‰Æ' => 'å£ ',
  '‰Ç' => 'å£¡',
  '‰È' => 'å£¢',
  '‰É' => 'å££',
  '‰Ê' => 'å£¥',
  '‰Ë' => 'å£¦',
  '‰Ì' => 'å£§',
  '‰Í' => 'å£¨',
  '‰Î' => 'å£©',
  '‰Ï' => 'å£ª',
  '‰Ð' => 'å£­',
  '‰Ñ' => 'å£¯',
  '‰Ò' => 'å£±',
  '‰Ó' => 'å£²',
  '‰Ô' => 'å£´',
  '‰Õ' => 'å£µ',
  '‰Ö' => 'å£·',
  '‰×' => 'å£¸',
  '‰Ø' => 'å£º',
  '‰Ù' => 'å£»',
  '‰Ú' => 'å£¼',
  '‰Û' => 'å£½',
  '‰Ü' => 'å£¾',
  '‰Ý' => 'å£¿',
  '‰Þ' => 'å¤€',
  '‰ß' => 'å¤�',
  '‰à' => 'å¤ƒ',
  '‰á' => 'å¤…',
  '‰â' => 'å¤†',
  '‰ã' => 'å¤ˆ',
  '‰ä' => 'å¤‰',
  '‰å' => 'å¤Š',
  '‰æ' => 'å¤‹',
  '‰ç' => 'å¤Œ',
  '‰è' => 'å¤Ž',
  '‰é' => 'å¤�',
  '‰ê' => 'å¤‘',
  '‰ë' => 'å¤’',
  '‰ì' => 'å¤“',
  '‰í' => 'å¤—',
  '‰î' => 'å¤˜',
  '‰ï' => 'å¤›',
  '‰ð' => 'å¤�',
  '‰ñ' => 'å¤ž',
  '‰ò' => 'å¤ ',
  '‰ó' => 'å¤¡',
  '‰ô' => 'å¤¢',
  '‰õ' => 'å¤£',
  '‰ö' => 'å¤¦',
  '‰÷' => 'å¤¨',
  '‰ø' => 'å¤¬',
  '‰ù' => 'å¤°',
  '‰ú' => 'å¤²',
  '‰û' => 'å¤³',
  '‰ü' => 'å¤µ',
  '‰ý' => 'å¤¶',
  '‰þ' => 'å¤»',
  'Š@' => 'å¤½',
  'ŠA' => 'å¤¾',
  'ŠB' => 'å¤¿',
  'ŠC' => 'å¥€',
  'ŠD' => 'å¥ƒ',
  'ŠE' => 'å¥…',
  'ŠF' => 'å¥†',
  'ŠG' => 'å¥Š',
  'ŠH' => 'å¥Œ',
  'ŠI' => 'å¥�',
  'ŠJ' => 'å¥�',
  'ŠK' => 'å¥’',
  'ŠL' => 'å¥“',
  'ŠM' => 'å¥™',
  'ŠN' => 'å¥›',
  'ŠO' => 'å¥œ',
  'ŠP' => 'å¥�',
  'ŠQ' => 'å¥ž',
  'ŠR' => 'å¥Ÿ',
  'ŠS' => 'å¥¡',
  'ŠT' => 'å¥£',
  'ŠU' => 'å¥¤',
  'ŠV' => 'å¥¦',
  'ŠW' => 'å¥§',
  'ŠX' => 'å¥¨',
  'ŠY' => 'å¥©',
  'ŠZ' => 'å¥ª',
  'Š[' => 'å¥«',
  'Š\\' => 'å¥¬',
  'Š]' => 'å¥­',
  'Š^' => 'å¥®',
  'Š_' => 'å¥¯',
  'Š`' => 'å¥°',
  'Ša' => 'å¥±',
  'Šb' => 'å¥²',
  'Šc' => 'å¥µ',
  'Šd' => 'å¥·',
  'Še' => 'å¥º',
  'Šf' => 'å¥»',
  'Šg' => 'å¥¼',
  'Šh' => 'å¥¾',
  'Ši' => 'å¥¿',
  'Šj' => 'å¦€',
  'Šk' => 'å¦…',
  'Šl' => 'å¦‰',
  'Šm' => 'å¦‹',
  'Šn' => 'å¦Œ',
  'Šo' => 'å¦Ž',
  'Šp' => 'å¦�',
  'Šq' => 'å¦�',
  'Šr' => 'å¦‘',
  'Šs' => 'å¦”',
  'Št' => 'å¦•',
  'Šu' => 'å¦˜',
  'Šv' => 'å¦š',
  'Šw' => 'å¦›',
  'Šx' => 'å¦œ',
  'Šy' => 'å¦�',
  'Šz' => 'å¦Ÿ',
  'Š{' => 'å¦ ',
  'Š|' => 'å¦¡',
  'Š}' => 'å¦¢',
  'Š~' => 'å¦¦',
  'Š€' => 'å¦§',
  'Š�' => 'å¦¬',
  'Š‚' => 'å¦­',
  'Šƒ' => 'å¦°',
  'Š„' => 'å¦±',
  'Š…' => 'å¦³',
  'Š†' => 'å¦´',
  'Š‡' => 'å¦µ',
  'Šˆ' => 'å¦¶',
  'Š‰' => 'å¦·',
  'ŠŠ' => 'å¦¸',
  'Š‹' => 'å¦º',
  'ŠŒ' => 'å¦¼',
  'Š�' => 'å¦½',
  'ŠŽ' => 'å¦¿',
  'Š�' => 'å§€',
  'Š�' => 'å§�',
  'Š‘' => 'å§‚',
  'Š’' => 'å§ƒ',
  'Š“' => 'å§„',
  'Š”' => 'å§…',
  'Š•' => 'å§‡',
  'Š–' => 'å§ˆ',
  'Š—' => 'å§‰',
  'Š˜' => 'å§Œ',
  'Š™' => 'å§�',
  'Šš' => 'å§Ž',
  'Š›' => 'å§�',
  'Šœ' => 'å§•',
  'Š�' => 'å§–',
  'Šž' => 'å§™',
  'ŠŸ' => 'å§›',
  'Š ' => 'å§ž',
  'Š¡' => 'å§Ÿ',
  'Š¢' => 'å§ ',
  'Š£' => 'å§¡',
  'Š¤' => 'å§¢',
  'Š¥' => 'å§¤',
  'Š¦' => 'å§¦',
  'Š§' => 'å§§',
  'Š¨' => 'å§©',
  'Š©' => 'å§ª',
  'Šª' => 'å§«',
  'Š«' => 'å§­',
  'Š¬' => 'å§®',
  'Š­' => 'å§¯',
  'Š®' => 'å§°',
  'Š¯' => 'å§±',
  'Š°' => 'å§²',
  'Š±' => 'å§³',
  'Š²' => 'å§´',
  'Š³' => 'å§µ',
  'Š´' => 'å§¶',
  'Šµ' => 'å§·',
  'Š¶' => 'å§¸',
  'Š·' => 'å§º',
  'Š¸' => 'å§¼',
  'Š¹' => 'å§½',
  'Šº' => 'å§¾',
  'Š»' => 'å¨€',
  'Š¼' => 'å¨‚',
  'Š½' => 'å¨Š',
  'Š¾' => 'å¨‹',
  'Š¿' => 'å¨�',
  'ŠÀ' => 'å¨Ž',
  'ŠÁ' => 'å¨�',
  'ŠÂ' => 'å¨�',
  'ŠÃ' => 'å¨’',
  'ŠÄ' => 'å¨”',
  'ŠÅ' => 'å¨•',
  'ŠÆ' => 'å¨–',
  'ŠÇ' => 'å¨—',
  'ŠÈ' => 'å¨™',
  'ŠÉ' => 'å¨š',
  'ŠÊ' => 'å¨›',
  'ŠË' => 'å¨�',
  'ŠÌ' => 'å¨ž',
  'ŠÍ' => 'å¨¡',
  'ŠÎ' => 'å¨¢',
  'ŠÏ' => 'å¨¤',
  'ŠÐ' => 'å¨¦',
  'ŠÑ' => 'å¨§',
  'ŠÒ' => 'å¨¨',
  'ŠÓ' => 'å¨ª',
  'ŠÔ' => 'å¨«',
  'ŠÕ' => 'å¨¬',
  'ŠÖ' => 'å¨­',
  'Š×' => 'å¨®',
  'ŠØ' => 'å¨¯',
  'ŠÙ' => 'å¨°',
  'ŠÚ' => 'å¨³',
  'ŠÛ' => 'å¨µ',
  'ŠÜ' => 'å¨·',
  'ŠÝ' => 'å¨¸',
  'ŠÞ' => 'å¨¹',
  'Šß' => 'å¨º',
  'Šà' => 'å¨»',
  'Šá' => 'å¨½',
  'Šâ' => 'å¨¾',
  'Šã' => 'å¨¿',
  'Šä' => 'å©�',
  'Šå' => 'å©‚',
  'Šæ' => 'å©ƒ',
  'Šç' => 'å©„',
  'Šè' => 'å©…',
  'Šé' => 'å©‡',
  'Šê' => 'å©ˆ',
  'Šë' => 'å©‹',
  'Šì' => 'å©Œ',
  'Ší' => 'å©�',
  'Šî' => 'å©Ž',
  'Šï' => 'å©�',
  'Šð' => 'å©�',
  'Šñ' => 'å©‘',
  'Šò' => 'å©’',
  'Šó' => 'å©“',
  'Šô' => 'å©”',
  'Šõ' => 'å©–',
  'Šö' => 'å©—',
  'Š÷' => 'å©˜',
  'Šø' => 'å©™',
  'Šù' => 'å©›',
  'Šú' => 'å©œ',
  'Šû' => 'å©�',
  'Šü' => 'å©ž',
  'Šý' => 'å©Ÿ',
  'Šþ' => 'å© ',
  '‹@' => 'å©¡',
  '‹A' => 'å©£',
  '‹B' => 'å©¤',
  '‹C' => 'å©¥',
  '‹D' => 'å©¦',
  '‹E' => 'å©¨',
  '‹F' => 'å©©',
  '‹G' => 'å©«',
  '‹H' => 'å©¬',
  '‹I' => 'å©­',
  '‹J' => 'å©®',
  '‹K' => 'å©¯',
  '‹L' => 'å©°',
  '‹M' => 'å©±',
  '‹N' => 'å©²',
  '‹O' => 'å©³',
  '‹P' => 'å©¸',
  '‹Q' => 'å©¹',
  '‹R' => 'å©»',
  '‹S' => 'å©¼',
  '‹T' => 'å©½',
  '‹U' => 'å©¾',
  '‹V' => 'åª€',
  '‹W' => 'åª�',
  '‹X' => 'åª‚',
  '‹Y' => 'åªƒ',
  '‹Z' => 'åª„',
  '‹[' => 'åª…',
  '‹\\' => 'åª†',
  '‹]' => 'åª‡',
  '‹^' => 'åªˆ',
  '‹_' => 'åª‰',
  '‹`' => 'åªŠ',
  '‹a' => 'åª‹',
  '‹b' => 'åªŒ',
  '‹c' => 'åª�',
  '‹d' => 'åªŽ',
  '‹e' => 'åª�',
  '‹f' => 'åª�',
  '‹g' => 'åª‘',
  '‹h' => 'åª“',
  '‹i' => 'åª”',
  '‹j' => 'åª•',
  '‹k' => 'åª–',
  '‹l' => 'åª—',
  '‹m' => 'åª˜',
  '‹n' => 'åª™',
  '‹o' => 'åªœ',
  '‹p' => 'åª�',
  '‹q' => 'åªž',
  '‹r' => 'åªŸ',
  '‹s' => 'åª ',
  '‹t' => 'åª¡',
  '‹u' => 'åª¢',
  '‹v' => 'åª£',
  '‹w' => 'åª¤',
  '‹x' => 'åª¥',
  '‹y' => 'åª¦',
  '‹z' => 'åª§',
  '‹{' => 'åª¨',
  '‹|' => 'åª©',
  '‹}' => 'åª«',
  '‹~' => 'åª¬',
  '‹€' => 'åª­',
  '‹�' => 'åª®',
  '‹‚' => 'åª¯',
  '‹ƒ' => 'åª°',
  '‹„' => 'åª±',
  '‹…' => 'åª´',
  '‹†' => 'åª¶',
  '‹‡' => 'åª·',
  '‹ˆ' => 'åª¹',
  '‹‰' => 'åªº',
  '‹Š' => 'åª»',
  '‹‹' => 'åª¼',
  '‹Œ' => 'åª½',
  '‹�' => 'åª¿',
  '‹Ž' => 'å«€',
  '‹�' => 'å«ƒ',
  '‹�' => 'å«„',
  '‹‘' => 'å«…',
  '‹’' => 'å«†',
  '‹“' => 'å«‡',
  '‹”' => 'å«ˆ',
  '‹•' => 'å«Š',
  '‹–' => 'å«‹',
  '‹—' => 'å«�',
  '‹˜' => 'å«Ž',
  '‹™' => 'å«�',
  '‹š' => 'å«�',
  '‹›' => 'å«‘',
  '‹œ' => 'å«“',
  '‹�' => 'å«•',
  '‹ž' => 'å«—',
  '‹Ÿ' => 'å«™',
  '‹ ' => 'å«š',
  '‹¡' => 'å«›',
  '‹¢' => 'å«�',
  '‹£' => 'å«ž',
  '‹¤' => 'å«Ÿ',
  '‹¥' => 'å«¢',
  '‹¦' => 'å«¤',
  '‹§' => 'å«¥',
  '‹¨' => 'å«§',
  '‹©' => 'å«¨',
  '‹ª' => 'å«ª',
  '‹«' => 'å«¬',
  '‹¬' => 'å«­',
  '‹­' => 'å«®',
  '‹®' => 'å«¯',
  '‹¯' => 'å«°',
  '‹°' => 'å«²',
  '‹±' => 'å«³',
  '‹²' => 'å«´',
  '‹³' => 'å«µ',
  '‹´' => 'å«¶',
  '‹µ' => 'å«·',
  '‹¶' => 'å«¸',
  '‹·' => 'å«¹',
  '‹¸' => 'å«º',
  '‹¹' => 'å«»',
  '‹º' => 'å«¼',
  '‹»' => 'å«½',
  '‹¼' => 'å«¾',
  '‹½' => 'å«¿',
  '‹¾' => 'å¬€',
  '‹¿' => 'å¬�',
  '‹À' => 'å¬‚',
  '‹Á' => 'å¬ƒ',
  '‹Â' => 'å¬„',
  '‹Ã' => 'å¬…',
  '‹Ä' => 'å¬†',
  '‹Å' => 'å¬‡',
  '‹Æ' => 'å¬ˆ',
  '‹Ç' => 'å¬Š',
  '‹È' => 'å¬‹',
  '‹É' => 'å¬Œ',
  '‹Ê' => 'å¬�',
  '‹Ë' => 'å¬Ž',
  '‹Ì' => 'å¬�',
  '‹Í' => 'å¬�',
  '‹Î' => 'å¬‘',
  '‹Ï' => 'å¬’',
  '‹Ð' => 'å¬“',
  '‹Ñ' => 'å¬”',
  '‹Ò' => 'å¬•',
  '‹Ó' => 'å¬˜',
  '‹Ô' => 'å¬™',
  '‹Õ' => 'å¬š',
  '‹Ö' => 'å¬›',
  '‹×' => 'å¬œ',
  '‹Ø' => 'å¬�',
  '‹Ù' => 'å¬ž',
  '‹Ú' => 'å¬Ÿ',
  '‹Û' => 'å¬ ',
  '‹Ü' => 'å¬¡',
  '‹Ý' => 'å¬¢',
  '‹Þ' => 'å¬£',
  '‹ß' => 'å¬¤',
  '‹à' => 'å¬¥',
  '‹á' => 'å¬¦',
  '‹â' => 'å¬§',
  '‹ã' => 'å¬¨',
  '‹ä' => 'å¬©',
  '‹å' => 'å¬ª',
  '‹æ' => 'å¬«',
  '‹ç' => 'å¬¬',
  '‹è' => 'å¬­',
  '‹é' => 'å¬®',
  '‹ê' => 'å¬¯',
  '‹ë' => 'å¬°',
  '‹ì' => 'å¬±',
  '‹í' => 'å¬³',
  '‹î' => 'å¬µ',
  '‹ï' => 'å¬¶',
  '‹ð' => 'å¬¸',
  '‹ñ' => 'å¬¹',
  '‹ò' => 'å¬º',
  '‹ó' => 'å¬»',
  '‹ô' => 'å¬¼',
  '‹õ' => 'å¬½',
  '‹ö' => 'å¬¾',
  '‹÷' => 'å¬¿',
  '‹ø' => 'å­�',
  '‹ù' => 'å­‚',
  '‹ú' => 'å­ƒ',
  '‹û' => 'å­„',
  '‹ü' => 'å­…',
  '‹ý' => 'å­†',
  '‹þ' => 'å­‡',
  'Œ@' => 'å­ˆ',
  'ŒA' => 'å­‰',
  'ŒB' => 'å­Š',
  'ŒC' => 'å­‹',
  'ŒD' => 'å­Œ',
  'ŒE' => 'å­�',
  'ŒF' => 'å­Ž',
  'ŒG' => 'å­�',
  'ŒH' => 'å­’',
  'ŒI' => 'å­–',
  'ŒJ' => 'å­ž',
  'ŒK' => 'å­ ',
  'ŒL' => 'å­¡',
  'ŒM' => 'å­§',
  'ŒN' => 'å­¨',
  'ŒO' => 'å­«',
  'ŒP' => 'å­­',
  'ŒQ' => 'å­®',
  'ŒR' => 'å­¯',
  'ŒS' => 'å­²',
  'ŒT' => 'å­´',
  'ŒU' => 'å­¶',
  'ŒV' => 'å­·',
  'ŒW' => 'å­¸',
  'ŒX' => 'å­¹',
  'ŒY' => 'å­»',
  'ŒZ' => 'å­¼',
  'Œ[' => 'å­¾',
  'Œ\\' => 'å­¿',
  'Œ]' => 'å®‚',
  'Œ^' => 'å®†',
  'Œ_' => 'å®Š',
  'Œ`' => 'å®�',
  'Œa' => 'å®Ž',
  'Œb' => 'å®�',
  'Œc' => 'å®‘',
  'Œd' => 'å®’',
  'Œe' => 'å®”',
  'Œf' => 'å®–',
  'Œg' => 'å®Ÿ',
  'Œh' => 'å®§',
  'Œi' => 'å®¨',
  'Œj' => 'å®©',
  'Œk' => 'å®¬',
  'Œl' => 'å®­',
  'Œm' => 'å®®',
  'Œn' => 'å®¯',
  'Œo' => 'å®±',
  'Œp' => 'å®²',
  'Œq' => 'å®·',
  'Œr' => 'å®º',
  'Œs' => 'å®»',
  'Œt' => 'å®¼',
  'Œu' => 'å¯€',
  'Œv' => 'å¯�',
  'Œw' => 'å¯ƒ',
  'Œx' => 'å¯ˆ',
  'Œy' => 'å¯‰',
  'Œz' => 'å¯Š',
  'Œ{' => 'å¯‹',
  'Œ|' => 'å¯�',
  'Œ}' => 'å¯Ž',
  'Œ~' => 'å¯�',
  'Œ€' => 'å¯‘',
  'Œ�' => 'å¯”',
  'Œ‚' => 'å¯•',
  'Œƒ' => 'å¯–',
  'Œ„' => 'å¯—',
  'Œ…' => 'å¯˜',
  'Œ†' => 'å¯™',
  'Œ‡' => 'å¯š',
  'Œˆ' => 'å¯›',
  'Œ‰' => 'å¯œ',
  'ŒŠ' => 'å¯ ',
  'Œ‹' => 'å¯¢',
  'ŒŒ' => 'å¯£',
  'Œ�' => 'å¯¦',
  'ŒŽ' => 'å¯§',
  'Œ�' => 'å¯©',
  'Œ�' => 'å¯ª',
  'Œ‘' => 'å¯«',
  'Œ’' => 'å¯¬',
  'Œ“' => 'å¯­',
  'Œ”' => 'å¯¯',
  'Œ•' => 'å¯±',
  'Œ–' => 'å¯²',
  'Œ—' => 'å¯³',
  'Œ˜' => 'å¯´',
  'Œ™' => 'å¯µ',
  'Œš' => 'å¯¶',
  'Œ›' => 'å¯·',
  'Œœ' => 'å¯½',
  'Œ�' => 'å¯¾',
  'Œž' => 'å°€',
  'ŒŸ' => 'å°‚',
  'Œ ' => 'å°ƒ',
  'Œ¡' => 'å°…',
  'Œ¢' => 'å°‡',
  'Œ£' => 'å°ˆ',
  'Œ¤' => 'å°‹',
  'Œ¥' => 'å°Œ',
  'Œ¦' => 'å°�',
  'Œ§' => 'å°Ž',
  'Œ¨' => 'å°�',
  'Œ©' => 'å°’',
  'Œª' => 'å°“',
  'Œ«' => 'å°—',
  'Œ¬' => 'å°™',
  'Œ­' => 'å°›',
  'Œ®' => 'å°ž',
  'Œ¯' => 'å°Ÿ',
  'Œ°' => 'å° ',
  'Œ±' => 'å°¡',
  'Œ²' => 'å°£',
  'Œ³' => 'å°¦',
  'Œ´' => 'å°¨',
  'Œµ' => 'å°©',
  'Œ¶' => 'å°ª',
  'Œ·' => 'å°«',
  'Œ¸' => 'å°­',
  'Œ¹' => 'å°®',
  'Œº' => 'å°¯',
  'Œ»' => 'å°°',
  'Œ¼' => 'å°²',
  'Œ½' => 'å°³',
  'Œ¾' => 'å°µ',
  'Œ¿' => 'å°¶',
  'ŒÀ' => 'å°·',
  'ŒÁ' => 'å±ƒ',
  'ŒÂ' => 'å±„',
  'ŒÃ' => 'å±†',
  'ŒÄ' => 'å±‡',
  'ŒÅ' => 'å±Œ',
  'ŒÆ' => 'å±�',
  'ŒÇ' => 'å±’',
  'ŒÈ' => 'å±“',
  'ŒÉ' => 'å±”',
  'ŒÊ' => 'å±–',
  'ŒË' => 'å±—',
  'ŒÌ' => 'å±˜',
  'ŒÍ' => 'å±š',
  'ŒÎ' => 'å±›',
  'ŒÏ' => 'å±œ',
  'ŒÐ' => 'å±�',
  'ŒÑ' => 'å±Ÿ',
  'ŒÒ' => 'å±¢',
  'ŒÓ' => 'å±¤',
  'ŒÔ' => 'å±§',
  'ŒÕ' => 'å±¨',
  'ŒÖ' => 'å±©',
  'Œ×' => 'å±ª',
  'ŒØ' => 'å±«',
  'ŒÙ' => 'å±¬',
  'ŒÚ' => 'å±­',
  'ŒÛ' => 'å±°',
  'ŒÜ' => 'å±²',
  'ŒÝ' => 'å±³',
  'ŒÞ' => 'å±´',
  'Œß' => 'å±µ',
  'Œà' => 'å±¶',
  'Œá' => 'å±·',
  'Œâ' => 'å±¸',
  'Œã' => 'å±»',
  'Œä' => 'å±¼',
  'Œå' => 'å±½',
  'Œæ' => 'å±¾',
  'Œç' => 'å²€',
  'Œè' => 'å²ƒ',
  'Œé' => 'å²„',
  'Œê' => 'å²…',
  'Œë' => 'å²†',
  'Œì' => 'å²‡',
  'Œí' => 'å²‰',
  'Œî' => 'å²Š',
  'Œï' => 'å²‹',
  'Œð' => 'å²Ž',
  'Œñ' => 'å²�',
  'Œò' => 'å²’',
  'Œó' => 'å²“',
  'Œô' => 'å²•',
  'Œõ' => 'å²�',
  'Œö' => 'å²ž',
  'Œ÷' => 'å²Ÿ',
  'Œø' => 'å² ',
  'Œù' => 'å²¡',
  'Œú' => 'å²¤',
  'Œû' => 'å²¥',
  'Œü' => 'å²¦',
  'Œý' => 'å²§',
  'Œþ' => 'å²¨',
  '�@' => 'å²ª',
  '�A' => 'å²®',
  '�B' => 'å²¯',
  '�C' => 'å²°',
  '�D' => 'å²²',
  '�E' => 'å²´',
  '�F' => 'å²¶',
  '�G' => 'å²¹',
  '�H' => 'å²º',
  '�I' => 'å²»',
  '�J' => 'å²¼',
  '�K' => 'å²¾',
  '�L' => 'å³€',
  '�M' => 'å³‚',
  '�N' => 'å³ƒ',
  '�O' => 'å³…',
  '�P' => 'å³†',
  '�Q' => 'å³‡',
  '�R' => 'å³ˆ',
  '�S' => 'å³‰',
  '�T' => 'å³Š',
  '�U' => 'å³Œ',
  '�V' => 'å³�',
  '�W' => 'å³Ž',
  '�X' => 'å³�',
  '�Y' => 'å³�',
  '�Z' => 'å³‘',
  '�[' => 'å³“',
  '�\\' => 'å³”',
  '�]' => 'å³•',
  '�^' => 'å³–',
  '�_' => 'å³—',
  '�`' => 'å³˜',
  '�a' => 'å³š',
  '�b' => 'å³›',
  '�c' => 'å³œ',
  '�d' => 'å³�',
  '�e' => 'å³ž',
  '�f' => 'å³Ÿ',
  '�g' => 'å³ ',
  '�h' => 'å³¢',
  '�i' => 'å³£',
  '�j' => 'å³§',
  '�k' => 'å³©',
  '�l' => 'å³«',
  '�m' => 'å³¬',
  '�n' => 'å³®',
  '�o' => 'å³¯',
  '�p' => 'å³±',
  '�q' => 'å³²',
  '�r' => 'å³³',
  '�s' => 'å³´',
  '�t' => 'å³µ',
  '�u' => 'å³¶',
  '�v' => 'å³·',
  '�w' => 'å³¸',
  '�x' => 'å³¹',
  '�y' => 'å³º',
  '�z' => 'å³¼',
  '�{' => 'å³½',
  '�|' => 'å³¾',
  '�}' => 'å³¿',
  '�~' => 'å´€',
  '�€' => 'å´�',
  '��' => 'å´„',
  '�‚' => 'å´…',
  '�ƒ' => 'å´ˆ',
  '�„' => 'å´‰',
  '�…' => 'å´Š',
  '�†' => 'å´‹',
  '�‡' => 'å´Œ',
  '�ˆ' => 'å´�',
  '�‰' => 'å´�',
  '�Š' => 'å´�',
  '�‹' => 'å´‘',
  '�Œ' => 'å´’',
  '��' => 'å´“',
  '�Ž' => 'å´•',
  '��' => 'å´—',
  '��' => 'å´˜',
  '�‘' => 'å´™',
  '�’' => 'å´š',
  '�“' => 'å´œ',
  '�”' => 'å´�',
  '�•' => 'å´Ÿ',
  '�–' => 'å´ ',
  '�—' => 'å´¡',
  '�˜' => 'å´¢',
  '�™' => 'å´£',
  '�š' => 'å´¥',
  '�›' => 'å´¨',
  '�œ' => 'å´ª',
  '��' => 'å´«',
  '�ž' => 'å´¬',
  '�Ÿ' => 'å´¯',
  '� ' => 'å´°',
  '�¡' => 'å´±',
  '�¢' => 'å´²',
  '�£' => 'å´³',
  '�¤' => 'å´µ',
  '�¥' => 'å´¶',
  '�¦' => 'å´·',
  '�§' => 'å´¸',
  '�¨' => 'å´¹',
  '�©' => 'å´º',
  '�ª' => 'å´»',
  '�«' => 'å´¼',
  '�¬' => 'å´¿',
  '�­' => 'åµ€',
  '�®' => 'åµ�',
  '�¯' => 'åµ‚',
  '�°' => 'åµƒ',
  '�±' => 'åµ„',
  '�²' => 'åµ…',
  '�³' => 'åµ†',
  '�´' => 'åµˆ',
  '�µ' => 'åµ‰',
  '�¶' => 'åµ�',
  '�·' => 'åµŽ',
  '�¸' => 'åµ�',
  '�¹' => 'åµ�',
  '�º' => 'åµ‘',
  '�»' => 'åµ’',
  '�¼' => 'åµ“',
  '�½' => 'åµ”',
  '�¾' => 'åµ•',
  '�¿' => 'åµ–',
  '�À' => 'åµ—',
  '�Á' => 'åµ™',
  '�Â' => 'åµš',
  '�Ã' => 'åµœ',
  '�Ä' => 'åµž',
  '�Å' => 'åµŸ',
  '�Æ' => 'åµ ',
  '�Ç' => 'åµ¡',
  '�È' => 'åµ¢',
  '�É' => 'åµ£',
  '�Ê' => 'åµ¤',
  '�Ë' => 'åµ¥',
  '�Ì' => 'åµ¦',
  '�Í' => 'åµ§',
  '�Î' => 'åµ¨',
  '�Ï' => 'åµª',
  '�Ð' => 'åµ­',
  '�Ñ' => 'åµ®',
  '�Ò' => 'åµ°',
  '�Ó' => 'åµ±',
  '�Ô' => 'åµ²',
  '�Õ' => 'åµ³',
  '�Ö' => 'åµµ',
  '�×' => 'åµ¶',
  '�Ø' => 'åµ·',
  '�Ù' => 'åµ¸',
  '�Ú' => 'åµ¹',
  '�Û' => 'åµº',
  '�Ü' => 'åµ»',
  '�Ý' => 'åµ¼',
  '�Þ' => 'åµ½',
  '�ß' => 'åµ¾',
  '�à' => 'åµ¿',
  '�á' => 'å¶€',
  '�â' => 'å¶�',
  '�ã' => 'å¶ƒ',
  '�ä' => 'å¶„',
  '�å' => 'å¶…',
  '�æ' => 'å¶†',
  '�ç' => 'å¶‡',
  '�è' => 'å¶ˆ',
  '�é' => 'å¶‰',
  '�ê' => 'å¶Š',
  '�ë' => 'å¶‹',
  '�ì' => 'å¶Œ',
  '�í' => 'å¶�',
  '�î' => 'å¶Ž',
  '�ï' => 'å¶�',
  '�ð' => 'å¶�',
  '�ñ' => 'å¶‘',
  '�ò' => 'å¶’',
  '�ó' => 'å¶“',
  '�ô' => 'å¶”',
  '�õ' => 'å¶•',
  '�ö' => 'å¶–',
  '�÷' => 'å¶—',
  '�ø' => 'å¶˜',
  '�ù' => 'å¶š',
  '�ú' => 'å¶›',
  '�û' => 'å¶œ',
  '�ü' => 'å¶ž',
  '�ý' => 'å¶Ÿ',
  '�þ' => 'å¶ ',
  'Ž@' => 'å¶¡',
  'ŽA' => 'å¶¢',
  'ŽB' => 'å¶£',
  'ŽC' => 'å¶¤',
  'ŽD' => 'å¶¥',
  'ŽE' => 'å¶¦',
  'ŽF' => 'å¶§',
  'ŽG' => 'å¶¨',
  'ŽH' => 'å¶©',
  'ŽI' => 'å¶ª',
  'ŽJ' => 'å¶«',
  'ŽK' => 'å¶¬',
  'ŽL' => 'å¶­',
  'ŽM' => 'å¶®',
  'ŽN' => 'å¶¯',
  'ŽO' => 'å¶°',
  'ŽP' => 'å¶±',
  'ŽQ' => 'å¶²',
  'ŽR' => 'å¶³',
  'ŽS' => 'å¶´',
  'ŽT' => 'å¶µ',
  'ŽU' => 'å¶¶',
  'ŽV' => 'å¶¸',
  'ŽW' => 'å¶¹',
  'ŽX' => 'å¶º',
  'ŽY' => 'å¶»',
  'ŽZ' => 'å¶¼',
  'Ž[' => 'å¶½',
  'Ž\\' => 'å¶¾',
  'Ž]' => 'å¶¿',
  'Ž^' => 'å·€',
  'Ž_' => 'å·�',
  'Ž`' => 'å·‚',
  'Ža' => 'å·ƒ',
  'Žb' => 'å·„',
  'Žc' => 'å·†',
  'Žd' => 'å·‡',
  'Že' => 'å·ˆ',
  'Žf' => 'å·‰',
  'Žg' => 'å·Š',
  'Žh' => 'å·‹',
  'Ži' => 'å·Œ',
  'Žj' => 'å·Ž',
  'Žk' => 'å·�',
  'Žl' => 'å·�',
  'Žm' => 'å·‘',
  'Žn' => 'å·’',
  'Žo' => 'å·“',
  'Žp' => 'å·”',
  'Žq' => 'å·•',
  'Žr' => 'å·–',
  'Žs' => 'å·—',
  'Žt' => 'å·˜',
  'Žu' => 'å·™',
  'Žv' => 'å·š',
  'Žw' => 'å·œ',
  'Žx' => 'å·Ÿ',
  'Žy' => 'å· ',
  'Žz' => 'å·£',
  'Ž{' => 'å·¤',
  'Ž|' => 'å·ª',
  'Ž}' => 'å·¬',
  'Ž~' => 'å·­',
  'Ž€' => 'å·°',
  'Ž�' => 'å·µ',
  'Ž‚' => 'å·¶',
  'Žƒ' => 'å·¸',
  'Ž„' => 'å·¹',
  'Ž…' => 'å·º',
  'Ž†' => 'å·»',
  'Ž‡' => 'å·¼',
  'Žˆ' => 'å·¿',
  'Ž‰' => 'å¸€',
  'ŽŠ' => 'å¸„',
  'Ž‹' => 'å¸‡',
  'ŽŒ' => 'å¸‰',
  'Ž�' => 'å¸Š',
  'ŽŽ' => 'å¸‹',
  'Ž�' => 'å¸�',
  'Ž�' => 'å¸Ž',
  'Ž‘' => 'å¸’',
  'Ž’' => 'å¸“',
  'Ž“' => 'å¸—',
  'Ž”' => 'å¸ž',
  'Ž•' => 'å¸Ÿ',
  'Ž–' => 'å¸ ',
  'Ž—' => 'å¸¡',
  'Ž˜' => 'å¸¢',
  'Ž™' => 'å¸£',
  'Žš' => 'å¸¤',
  'Ž›' => 'å¸¥',
  'Žœ' => 'å¸¨',
  'Ž�' => 'å¸©',
  'Žž' => 'å¸ª',
  'ŽŸ' => 'å¸«',
  'Ž ' => 'å¸¬',
  'Ž¡' => 'å¸¯',
  'Ž¢' => 'å¸°',
  'Ž£' => 'å¸²',
  'Ž¤' => 'å¸³',
  'Ž¥' => 'å¸´',
  'Ž¦' => 'å¸µ',
  'Ž§' => 'å¸¶',
  'Ž¨' => 'å¸¹',
  'Ž©' => 'å¸º',
  'Žª' => 'å¸¾',
  'Ž«' => 'å¸¿',
  'Ž¬' => 'å¹€',
  'Ž­' => 'å¹�',
  'Ž®' => 'å¹ƒ',
  'Ž¯' => 'å¹†',
  'Ž°' => 'å¹‡',
  'Ž±' => 'å¹ˆ',
  'Ž²' => 'å¹‰',
  'Ž³' => 'å¹Š',
  'Ž´' => 'å¹‹',
  'Žµ' => 'å¹�',
  'Ž¶' => 'å¹Ž',
  'Ž·' => 'å¹�',
  'Ž¸' => 'å¹�',
  'Ž¹' => 'å¹‘',
  'Žº' => 'å¹’',
  'Ž»' => 'å¹“',
  'Ž¼' => 'å¹–',
  'Ž½' => 'å¹—',
  'Ž¾' => 'å¹˜',
  'Ž¿' => 'å¹™',
  'ŽÀ' => 'å¹š',
  'ŽÁ' => 'å¹œ',
  'ŽÂ' => 'å¹�',
  'ŽÃ' => 'å¹Ÿ',
  'ŽÄ' => 'å¹ ',
  'ŽÅ' => 'å¹£',
  'ŽÆ' => 'å¹¤',
  'ŽÇ' => 'å¹¥',
  'ŽÈ' => 'å¹¦',
  'ŽÉ' => 'å¹§',
  'ŽÊ' => 'å¹¨',
  'ŽË' => 'å¹©',
  'ŽÌ' => 'å¹ª',
  'ŽÍ' => 'å¹«',
  'ŽÎ' => 'å¹¬',
  'ŽÏ' => 'å¹­',
  'ŽÐ' => 'å¹®',
  'ŽÑ' => 'å¹¯',
  'ŽÒ' => 'å¹°',
  'ŽÓ' => 'å¹±',
  'ŽÔ' => 'å¹µ',
  'ŽÕ' => 'å¹·',
  'ŽÖ' => 'å¹¹',
  'Ž×' => 'å¹¾',
  'ŽØ' => 'åº�',
  'ŽÙ' => 'åº‚',
  'ŽÚ' => 'åºƒ',
  'ŽÛ' => 'åº…',
  'ŽÜ' => 'åºˆ',
  'ŽÝ' => 'åº‰',
  'ŽÞ' => 'åºŒ',
  'Žß' => 'åº�',
  'Žà' => 'åºŽ',
  'Žá' => 'åº’',
  'Žâ' => 'åº˜',
  'Žã' => 'åº›',
  'Žä' => 'åº�',
  'Žå' => 'åº¡',
  'Žæ' => 'åº¢',
  'Žç' => 'åº£',
  'Žè' => 'åº¤',
  'Žé' => 'åº¨',
  'Žê' => 'åº©',
  'Žë' => 'åºª',
  'Žì' => 'åº«',
  'Ží' => 'åº¬',
  'Žî' => 'åº®',
  'Žï' => 'åº¯',
  'Žð' => 'åº°',
  'Žñ' => 'åº±',
  'Žò' => 'åº²',
  'Žó' => 'åº´',
  'Žô' => 'åºº',
  'Žõ' => 'åº»',
  'Žö' => 'åº¼',
  'Ž÷' => 'åº½',
  'Žø' => 'åº¿',
  'Žù' => 'å»€',
  'Žú' => 'å»�',
  'Žû' => 'å»‚',
  'Žü' => 'å»ƒ',
  'Žý' => 'å»„',
  'Žþ' => 'å»…',
  '�@' => 'å»†',
  '�A' => 'å»‡',
  '�B' => 'å»ˆ',
  '�C' => 'å»‹',
  '�D' => 'å»Œ',
  '�E' => 'å»�',
  '�F' => 'å»Ž',
  '�G' => 'å»�',
  '�H' => 'å»�',
  '�I' => 'å»”',
  '�J' => 'å»•',
  '�K' => 'å»—',
  '�L' => 'å»˜',
  '�M' => 'å»™',
  '�N' => 'å»š',
  '�O' => 'å»œ',
  '�P' => 'å»�',
  '�Q' => 'å»ž',
  '�R' => 'å»Ÿ',
  '�S' => 'å» ',
  '�T' => 'å»¡',
  '�U' => 'å»¢',
  '�V' => 'å»£',
  '�W' => 'å»¤',
  '�X' => 'å»¥',
  '�Y' => 'å»¦',
  '�Z' => 'å»§',
  '�[' => 'å»©',
  '�\\' => 'å»«',
  '�]' => 'å»¬',
  '�^' => 'å»­',
  '�_' => 'å»®',
  '�`' => 'å»¯',
  '�a' => 'å»°',
  '�b' => 'å»±',
  '�c' => 'å»²',
  '�d' => 'å»³',
  '�e' => 'å»µ',
  '�f' => 'å»¸',
  '�g' => 'å»¹',
  '�h' => 'å»»',
  '�i' => 'å»¼',
  '�j' => 'å»½',
  '�k' => 'å¼…',
  '�l' => 'å¼†',
  '�m' => 'å¼‡',
  '�n' => 'å¼‰',
  '�o' => 'å¼Œ',
  '�p' => 'å¼�',
  '�q' => 'å¼Ž',
  '�r' => 'å¼�',
  '�s' => 'å¼’',
  '�t' => 'å¼”',
  '�u' => 'å¼–',
  '�v' => 'å¼™',
  '�w' => 'å¼š',
  '�x' => 'å¼œ',
  '�y' => 'å¼�',
  '�z' => 'å¼ž',
  '�{' => 'å¼¡',
  '�|' => 'å¼¢',
  '�}' => 'å¼£',
  '�~' => 'å¼¤',
  '�€' => 'å¼¨',
  '��' => 'å¼«',
  '�‚' => 'å¼¬',
  '�ƒ' => 'å¼®',
  '�„' => 'å¼°',
  '�…' => 'å¼²',
  '�†' => 'å¼³',
  '�‡' => 'å¼´',
  '�ˆ' => 'å¼µ',
  '�‰' => 'å¼¶',
  '�Š' => 'å¼·',
  '�‹' => 'å¼¸',
  '�Œ' => 'å¼»',
  '��' => 'å¼½',
  '�Ž' => 'å¼¾',
  '��' => 'å¼¿',
  '��' => 'å½�',
  '�‘' => 'å½‚',
  '�’' => 'å½ƒ',
  '�“' => 'å½„',
  '�”' => 'å½…',
  '�•' => 'å½†',
  '�–' => 'å½‡',
  '�—' => 'å½ˆ',
  '�˜' => 'å½‰',
  '�™' => 'å½Š',
  '�š' => 'å½‹',
  '�›' => 'å½Œ',
  '�œ' => 'å½�',
  '��' => 'å½Ž',
  '�ž' => 'å½�',
  '�Ÿ' => 'å½‘',
  '� ' => 'å½”',
  '�¡' => 'å½™',
  '�¢' => 'å½š',
  '�£' => 'å½›',
  '�¤' => 'å½œ',
  '�¥' => 'å½ž',
  '�¦' => 'å½Ÿ',
  '�§' => 'å½ ',
  '�¨' => 'å½£',
  '�©' => 'å½¥',
  '�ª' => 'å½§',
  '�«' => 'å½¨',
  '�¬' => 'å½«',
  '�­' => 'å½®',
  '�®' => 'å½¯',
  '�¯' => 'å½²',
  '�°' => 'å½´',
  '�±' => 'å½µ',
  '�²' => 'å½¶',
  '�³' => 'å½¸',
  '�´' => 'å½º',
  '�µ' => 'å½½',
  '�¶' => 'å½¾',
  '�·' => 'å½¿',
  '�¸' => 'å¾ƒ',
  '�¹' => 'å¾†',
  '�º' => 'å¾�',
  '�»' => 'å¾Ž',
  '�¼' => 'å¾�',
  '�½' => 'å¾‘',
  '�¾' => 'å¾“',
  '�¿' => 'å¾”',
  '�À' => 'å¾–',
  '�Á' => 'å¾š',
  '�Â' => 'å¾›',
  '�Ã' => 'å¾�',
  '�Ä' => 'å¾ž',
  '�Å' => 'å¾Ÿ',
  '�Æ' => 'å¾ ',
  '�Ç' => 'å¾¢',
  '�È' => 'å¾£',
  '�É' => 'å¾¤',
  '�Ê' => 'å¾¥',
  '�Ë' => 'å¾¦',
  '�Ì' => 'å¾§',
  '�Í' => 'å¾©',
  '�Î' => 'å¾«',
  '�Ï' => 'å¾¬',
  '�Ð' => 'å¾¯',
  '�Ñ' => 'å¾°',
  '�Ò' => 'å¾±',
  '�Ó' => 'å¾²',
  '�Ô' => 'å¾³',
  '�Õ' => 'å¾´',
  '�Ö' => 'å¾¶',
  '�×' => 'å¾¸',
  '�Ø' => 'å¾¹',
  '�Ù' => 'å¾º',
  '�Ú' => 'å¾»',
  '�Û' => 'å¾¾',
  '�Ü' => 'å¾¿',
  '�Ý' => 'å¿€',
  '�Þ' => 'å¿�',
  '�ß' => 'å¿‚',
  '�à' => 'å¿‡',
  '�á' => 'å¿ˆ',
  '�â' => 'å¿Š',
  '�ã' => 'å¿‹',
  '�ä' => 'å¿Ž',
  '�å' => 'å¿“',
  '�æ' => 'å¿”',
  '�ç' => 'å¿•',
  '�è' => 'å¿š',
  '�é' => 'å¿›',
  '�ê' => 'å¿œ',
  '�ë' => 'å¿ž',
  '�ì' => 'å¿Ÿ',
  '�í' => 'å¿¢',
  '�î' => 'å¿£',
  '�ï' => 'å¿¥',
  '�ð' => 'å¿¦',
  '�ñ' => 'å¿¨',
  '�ò' => 'å¿©',
  '�ó' => 'å¿¬',
  '�ô' => 'å¿¯',
  '�õ' => 'å¿°',
  '�ö' => 'å¿²',
  '�÷' => 'å¿³',
  '�ø' => 'å¿´',
  '�ù' => 'å¿¶',
  '�ú' => 'å¿·',
  '�û' => 'å¿¹',
  '�ü' => 'å¿º',
  '�ý' => 'å¿¼',
  '�þ' => 'æ€‡',
  '�@' => 'æ€ˆ',
  '�A' => 'æ€‰',
  '�B' => 'æ€‹',
  '�C' => 'æ€Œ',
  '�D' => 'æ€�',
  '�E' => 'æ€‘',
  '�F' => 'æ€“',
  '�G' => 'æ€—',
  '�H' => 'æ€˜',
  '�I' => 'æ€š',
  '�J' => 'æ€ž',
  '�K' => 'æ€Ÿ',
  '�L' => 'æ€¢',
  '�M' => 'æ€£',
  '�N' => 'æ€¤',
  '�O' => 'æ€¬',
  '�P' => 'æ€­',
  '�Q' => 'æ€®',
  '�R' => 'æ€°',
  '�S' => 'æ€±',
  '�T' => 'æ€²',
  '�U' => 'æ€³',
  '�V' => 'æ€´',
  '�W' => 'æ€¶',
  '�X' => 'æ€·',
  '�Y' => 'æ€¸',
  '�Z' => 'æ€¹',
  '�[' => 'æ€º',
  '�\\' => 'æ€½',
  '�]' => 'æ€¾',
  '�^' => 'æ�€',
  '�_' => 'æ�„',
  '�`' => 'æ�…',
  '�a' => 'æ�†',
  '�b' => 'æ�‡',
  '�c' => 'æ�ˆ',
  '�d' => 'æ�‰',
  '�e' => 'æ�Š',
  '�f' => 'æ�Œ',
  '�g' => 'æ�Ž',
  '�h' => 'æ��',
  '�i' => 'æ�‘',
  '�j' => 'æ�“',
  '�k' => 'æ�”',
  '�l' => 'æ�–',
  '�m' => 'æ�—',
  '�n' => 'æ�˜',
  '�o' => 'æ�›',
  '�p' => 'æ�œ',
  '�q' => 'æ�ž',
  '�r' => 'æ�Ÿ',
  '�s' => 'æ� ',
  '�t' => 'æ�¡',
  '�u' => 'æ�¥',
  '�v' => 'æ�¦',
  '�w' => 'æ�®',
  '�x' => 'æ�±',
  '�y' => 'æ�²',
  '�z' => 'æ�´',
  '�{' => 'æ�µ',
  '�|' => 'æ�·',
  '�}' => 'æ�¾',
  '�~' => 'æ‚€',
  '�€' => 'æ‚�',
  '��' => 'æ‚‚',
  '�‚' => 'æ‚…',
  '�ƒ' => 'æ‚†',
  '�„' => 'æ‚‡',
  '�…' => 'æ‚ˆ',
  '�†' => 'æ‚Š',
  '�‡' => 'æ‚‹',
  '�ˆ' => 'æ‚Ž',
  '�‰' => 'æ‚�',
  '�Š' => 'æ‚�',
  '�‹' => 'æ‚‘',
  '�Œ' => 'æ‚“',
  '��' => 'æ‚•',
  '�Ž' => 'æ‚—',
  '��' => 'æ‚˜',
  '��' => 'æ‚™',
  '�‘' => 'æ‚œ',
  '�’' => 'æ‚ž',
  '�“' => 'æ‚¡',
  '�”' => 'æ‚¢',
  '�•' => 'æ‚¤',
  '�–' => 'æ‚¥',
  '�—' => 'æ‚§',
  '�˜' => 'æ‚©',
  '�™' => 'æ‚ª',
  '�š' => 'æ‚®',
  '�›' => 'æ‚°',
  '�œ' => 'æ‚³',
  '��' => 'æ‚µ',
  '�ž' => 'æ‚¶',
  '�Ÿ' => 'æ‚·',
  '� ' => 'æ‚¹',
  '�¡' => 'æ‚º',
  '�¢' => 'æ‚½',
  '�£' => 'æ‚¾',
  '�¤' => 'æ‚¿',
  '�¥' => 'æƒ€',
  '�¦' => 'æƒ�',
  '�§' => 'æƒ‚',
  '�¨' => 'æƒƒ',
  '�©' => 'æƒ„',
  '�ª' => 'æƒ‡',
  '�«' => 'æƒˆ',
  '�¬' => 'æƒ‰',
  '�­' => 'æƒŒ',
  '�®' => 'æƒ�',
  '�¯' => 'æƒŽ',
  '�°' => 'æƒ�',
  '�±' => 'æƒ�',
  '�²' => 'æƒ’',
  '�³' => 'æƒ“',
  '�´' => 'æƒ”',
  '�µ' => 'æƒ–',
  '�¶' => 'æƒ—',
  '�·' => 'æƒ™',
  '�¸' => 'æƒ›',
  '�¹' => 'æƒž',
  '�º' => 'æƒ¡',
  '�»' => 'æƒ¢',
  '�¼' => 'æƒ£',
  '�½' => 'æƒ¤',
  '�¾' => 'æƒ¥',
  '�¿' => 'æƒª',
  '�À' => 'æƒ±',
  '�Á' => 'æƒ²',
  '�Â' => 'æƒµ',
  '�Ã' => 'æƒ·',
  '�Ä' => 'æƒ¸',
  '�Å' => 'æƒ»',
  '�Æ' => 'æƒ¼',
  '�Ç' => 'æƒ½',
  '�È' => 'æƒ¾',
  '�É' => 'æƒ¿',
  '�Ê' => 'æ„‚',
  '�Ë' => 'æ„ƒ',
  '�Ì' => 'æ„„',
  '�Í' => 'æ„…',
  '�Î' => 'æ„‡',
  '�Ï' => 'æ„Š',
  '�Ð' => 'æ„‹',
  '�Ñ' => 'æ„Œ',
  '�Ò' => 'æ„�',
  '�Ó' => 'æ„‘',
  '�Ô' => 'æ„’',
  '�Õ' => 'æ„“',
  '�Ö' => 'æ„”',
  '�×' => 'æ„–',
  '�Ø' => 'æ„—',
  '�Ù' => 'æ„˜',
  '�Ú' => 'æ„™',
  '�Û' => 'æ„›',
  '�Ü' => 'æ„œ',
  '�Ý' => 'æ„�',
  '�Þ' => 'æ„ž',
  '�ß' => 'æ„¡',
  '�à' => 'æ„¢',
  '�á' => 'æ„¥',
  '�â' => 'æ„¨',
  '�ã' => 'æ„©',
  '�ä' => 'æ„ª',
  '�å' => 'æ„¬',
  '�æ' => 'æ„­',
  '�ç' => 'æ„®',
  '�è' => 'æ„¯',
  '�é' => 'æ„°',
  '�ê' => 'æ„±',
  '�ë' => 'æ„²',
  '�ì' => 'æ„³',
  '�í' => 'æ„´',
  '�î' => 'æ„µ',
  '�ï' => 'æ„¶',
  '�ð' => 'æ„·',
  '�ñ' => 'æ„¸',
  '�ò' => 'æ„¹',
  '�ó' => 'æ„º',
  '�ô' => 'æ„»',
  '�õ' => 'æ„¼',
  '�ö' => 'æ„½',
  '�÷' => 'æ„¾',
  '�ø' => 'æ…€',
  '�ù' => 'æ…�',
  '�ú' => 'æ…‚',
  '�û' => 'æ…ƒ',
  '�ü' => 'æ…„',
  '�ý' => 'æ……',
  '�þ' => 'æ…†',
  '‘@' => 'æ…‡',
  '‘A' => 'æ…‰',
  '‘B' => 'æ…‹',
  '‘C' => 'æ…�',
  '‘D' => 'æ…�',
  '‘E' => 'æ…�',
  '‘F' => 'æ…’',
  '‘G' => 'æ…“',
  '‘H' => 'æ…”',
  '‘I' => 'æ…–',
  '‘J' => 'æ…—',
  '‘K' => 'æ…˜',
  '‘L' => 'æ…™',
  '‘M' => 'æ…š',
  '‘N' => 'æ…›',
  '‘O' => 'æ…œ',
  '‘P' => 'æ…ž',
  '‘Q' => 'æ…Ÿ',
  '‘R' => 'æ… ',
  '‘S' => 'æ…¡',
  '‘T' => 'æ…£',
  '‘U' => 'æ…¤',
  '‘V' => 'æ…¥',
  '‘W' => 'æ…¦',
  '‘X' => 'æ…©',
  '‘Y' => 'æ…ª',
  '‘Z' => 'æ…«',
  '‘[' => 'æ…¬',
  '‘\\' => 'æ…­',
  '‘]' => 'æ…®',
  '‘^' => 'æ…¯',
  '‘_' => 'æ…±',
  '‘`' => 'æ…²',
  '‘a' => 'æ…³',
  '‘b' => 'æ…´',
  '‘c' => 'æ…¶',
  '‘d' => 'æ…¸',
  '‘e' => 'æ…¹',
  '‘f' => 'æ…º',
  '‘g' => 'æ…»',
  '‘h' => 'æ…¼',
  '‘i' => 'æ…½',
  '‘j' => 'æ…¾',
  '‘k' => 'æ…¿',
  '‘l' => 'æ†€',
  '‘m' => 'æ†�',
  '‘n' => 'æ†‚',
  '‘o' => 'æ†ƒ',
  '‘p' => 'æ†„',
  '‘q' => 'æ†…',
  '‘r' => 'æ††',
  '‘s' => 'æ†‡',
  '‘t' => 'æ†ˆ',
  '‘u' => 'æ†‰',
  '‘v' => 'æ†Š',
  '‘w' => 'æ†Œ',
  '‘x' => 'æ†�',
  '‘y' => 'æ†�',
  '‘z' => 'æ†�',
  '‘{' => 'æ†‘',
  '‘|' => 'æ†’',
  '‘}' => 'æ†“',
  '‘~' => 'æ†•',
  '‘€' => 'æ†–',
  '‘�' => 'æ†—',
  '‘‚' => 'æ†˜',
  '‘ƒ' => 'æ†™',
  '‘„' => 'æ†š',
  '‘…' => 'æ†›',
  '‘†' => 'æ†œ',
  '‘‡' => 'æ†ž',
  '‘ˆ' => 'æ†Ÿ',
  '‘‰' => 'æ† ',
  '‘Š' => 'æ†¡',
  '‘‹' => 'æ†¢',
  '‘Œ' => 'æ†£',
  '‘�' => 'æ†¤',
  '‘Ž' => 'æ†¥',
  '‘�' => 'æ†¦',
  '‘�' => 'æ†ª',
  '‘‘' => 'æ†«',
  '‘’' => 'æ†­',
  '‘“' => 'æ†®',
  '‘”' => 'æ†¯',
  '‘•' => 'æ†°',
  '‘–' => 'æ†±',
  '‘—' => 'æ†²',
  '‘˜' => 'æ†³',
  '‘™' => 'æ†´',
  '‘š' => 'æ†µ',
  '‘›' => 'æ†¶',
  '‘œ' => 'æ†¸',
  '‘�' => 'æ†¹',
  '‘ž' => 'æ†º',
  '‘Ÿ' => 'æ†»',
  '‘ ' => 'æ†¼',
  '‘¡' => 'æ†½',
  '‘¢' => 'æ†¿',
  '‘£' => 'æ‡€',
  '‘¤' => 'æ‡�',
  '‘¥' => 'æ‡ƒ',
  '‘¦' => 'æ‡„',
  '‘§' => 'æ‡…',
  '‘¨' => 'æ‡†',
  '‘©' => 'æ‡‡',
  '‘ª' => 'æ‡‰',
  '‘«' => 'æ‡Œ',
  '‘¬' => 'æ‡�',
  '‘­' => 'æ‡Ž',
  '‘®' => 'æ‡�',
  '‘¯' => 'æ‡�',
  '‘°' => 'æ‡“',
  '‘±' => 'æ‡•',
  '‘²' => 'æ‡–',
  '‘³' => 'æ‡—',
  '‘´' => 'æ‡˜',
  '‘µ' => 'æ‡™',
  '‘¶' => 'æ‡š',
  '‘·' => 'æ‡›',
  '‘¸' => 'æ‡œ',
  '‘¹' => 'æ‡�',
  '‘º' => 'æ‡ž',
  '‘»' => 'æ‡Ÿ',
  '‘¼' => 'æ‡ ',
  '‘½' => 'æ‡¡',
  '‘¾' => 'æ‡¢',
  '‘¿' => 'æ‡£',
  '‘À' => 'æ‡¤',
  '‘Á' => 'æ‡¥',
  '‘Â' => 'æ‡§',
  '‘Ã' => 'æ‡¨',
  '‘Ä' => 'æ‡©',
  '‘Å' => 'æ‡ª',
  '‘Æ' => 'æ‡«',
  '‘Ç' => 'æ‡¬',
  '‘È' => 'æ‡­',
  '‘É' => 'æ‡®',
  '‘Ê' => 'æ‡¯',
  '‘Ë' => 'æ‡°',
  '‘Ì' => 'æ‡±',
  '‘Í' => 'æ‡²',
  '‘Î' => 'æ‡³',
  '‘Ï' => 'æ‡´',
  '‘Ð' => 'æ‡¶',
  '‘Ñ' => 'æ‡·',
  '‘Ò' => 'æ‡¸',
  '‘Ó' => 'æ‡¹',
  '‘Ô' => 'æ‡º',
  '‘Õ' => 'æ‡»',
  '‘Ö' => 'æ‡¼',
  '‘×' => 'æ‡½',
  '‘Ø' => 'æ‡¾',
  '‘Ù' => 'æˆ€',
  '‘Ú' => 'æˆ�',
  '‘Û' => 'æˆ‚',
  '‘Ü' => 'æˆƒ',
  '‘Ý' => 'æˆ„',
  '‘Þ' => 'æˆ…',
  '‘ß' => 'æˆ‡',
  '‘à' => 'æˆ‰',
  '‘á' => 'æˆ“',
  '‘â' => 'æˆ”',
  '‘ã' => 'æˆ™',
  '‘ä' => 'æˆœ',
  '‘å' => 'æˆ�',
  '‘æ' => 'æˆž',
  '‘ç' => 'æˆ ',
  '‘è' => 'æˆ£',
  '‘é' => 'æˆ¦',
  '‘ê' => 'æˆ§',
  '‘ë' => 'æˆ¨',
  '‘ì' => 'æˆ©',
  '‘í' => 'æˆ«',
  '‘î' => 'æˆ­',
  '‘ï' => 'æˆ¯',
  '‘ð' => 'æˆ°',
  '‘ñ' => 'æˆ±',
  '‘ò' => 'æˆ²',
  '‘ó' => 'æˆµ',
  '‘ô' => 'æˆ¶',
  '‘õ' => 'æˆ¸',
  '‘ö' => 'æˆ¹',
  '‘÷' => 'æˆº',
  '‘ø' => 'æˆ»',
  '‘ù' => 'æˆ¼',
  '‘ú' => 'æ‰‚',
  '‘û' => 'æ‰„',
  '‘ü' => 'æ‰…',
  '‘ý' => 'æ‰†',
  '‘þ' => 'æ‰Š',
  '’@' => 'æ‰�',
  '’A' => 'æ‰�',
  '’B' => 'æ‰•',
  '’C' => 'æ‰–',
  '’D' => 'æ‰—',
  '’E' => 'æ‰™',
  '’F' => 'æ‰š',
  '’G' => 'æ‰œ',
  '’H' => 'æ‰�',
  '’I' => 'æ‰ž',
  '’J' => 'æ‰Ÿ',
  '’K' => 'æ‰ ',
  '’L' => 'æ‰¡',
  '’M' => 'æ‰¢',
  '’N' => 'æ‰¤',
  '’O' => 'æ‰¥',
  '’P' => 'æ‰¨',
  '’Q' => 'æ‰±',
  '’R' => 'æ‰²',
  '’S' => 'æ‰´',
  '’T' => 'æ‰µ',
  '’U' => 'æ‰·',
  '’V' => 'æ‰¸',
  '’W' => 'æ‰º',
  '’X' => 'æ‰»',
  '’Y' => 'æ‰½',
  '’Z' => 'æŠ�',
  '’[' => 'æŠ‚',
  '’\\' => 'æŠƒ',
  '’]' => 'æŠ…',
  '’^' => 'æŠ†',
  '’_' => 'æŠ‡',
  '’`' => 'æŠˆ',
  '’a' => 'æŠ‹',
  '’b' => 'æŠŒ',
  '’c' => 'æŠ�',
  '’d' => 'æŠŽ',
  '’e' => 'æŠ�',
  '’f' => 'æŠ�',
  '’g' => 'æŠ”',
  '’h' => 'æŠ™',
  '’i' => 'æŠœ',
  '’j' => 'æŠ�',
  '’k' => 'æŠž',
  '’l' => 'æŠ£',
  '’m' => 'æŠ¦',
  '’n' => 'æŠ§',
  '’o' => 'æŠ©',
  '’p' => 'æŠª',
  '’q' => 'æŠ­',
  '’r' => 'æŠ®',
  '’s' => 'æŠ¯',
  '’t' => 'æŠ°',
  '’u' => 'æŠ²',
  '’v' => 'æŠ³',
  '’w' => 'æŠ´',
  '’x' => 'æŠ¶',
  '’y' => 'æŠ·',
  '’z' => 'æŠ¸',
  '’{' => 'æŠº',
  '’|' => 'æŠ¾',
  '’}' => 'æ‹€',
  '’~' => 'æ‹�',
  '’€' => 'æ‹ƒ',
  '’�' => 'æ‹‹',
  '’‚' => 'æ‹�',
  '’ƒ' => 'æ‹‘',
  '’„' => 'æ‹•',
  '’…' => 'æ‹�',
  '’†' => 'æ‹ž',
  '’‡' => 'æ‹ ',
  '’ˆ' => 'æ‹¡',
  '’‰' => 'æ‹¤',
  '’Š' => 'æ‹ª',
  '’‹' => 'æ‹«',
  '’Œ' => 'æ‹°',
  '’�' => 'æ‹²',
  '’Ž' => 'æ‹µ',
  '’�' => 'æ‹¸',
  '’�' => 'æ‹¹',
  '’‘' => 'æ‹º',
  '’’' => 'æ‹»',
  '’“' => 'æŒ€',
  '’”' => 'æŒƒ',
  '’•' => 'æŒ„',
  '’–' => 'æŒ…',
  '’—' => 'æŒ†',
  '’˜' => 'æŒŠ',
  '’™' => 'æŒ‹',
  '’š' => 'æŒŒ',
  '’›' => 'æŒ�',
  '’œ' => 'æŒ�',
  '’�' => 'æŒ�',
  '’ž' => 'æŒ’',
  '’Ÿ' => 'æŒ“',
  '’ ' => 'æŒ”',
  '’¡' => 'æŒ•',
  '’¢' => 'æŒ—',
  '’£' => 'æŒ˜',
  '’¤' => 'æŒ™',
  '’¥' => 'æŒœ',
  '’¦' => 'æŒ¦',
  '’§' => 'æŒ§',
  '’¨' => 'æŒ©',
  '’©' => 'æŒ¬',
  '’ª' => 'æŒ­',
  '’«' => 'æŒ®',
  '’¬' => 'æŒ°',
  '’­' => 'æŒ±',
  '’®' => 'æŒ³',
  '’¯' => 'æŒ´',
  '’°' => 'æŒµ',
  '’±' => 'æŒ¶',
  '’²' => 'æŒ·',
  '’³' => 'æŒ¸',
  '’´' => 'æŒ»',
  '’µ' => 'æŒ¼',
  '’¶' => 'æŒ¾',
  '’·' => 'æŒ¿',
  '’¸' => 'æ�€',
  '’¹' => 'æ��',
  '’º' => 'æ�„',
  '’»' => 'æ�‡',
  '’¼' => 'æ�ˆ',
  '’½' => 'æ�Š',
  '’¾' => 'æ�‘',
  '’¿' => 'æ�’',
  '’À' => 'æ�“',
  '’Á' => 'æ�”',
  '’Â' => 'æ�–',
  '’Ã' => 'æ�—',
  '’Ä' => 'æ�˜',
  '’Å' => 'æ�™',
  '’Æ' => 'æ�š',
  '’Ç' => 'æ�›',
  '’È' => 'æ�œ',
  '’É' => 'æ��',
  '’Ê' => 'æ� ',
  '’Ë' => 'æ�¤',
  '’Ì' => 'æ�¥',
  '’Í' => 'æ�¦',
  '’Î' => 'æ�¨',
  '’Ï' => 'æ�ª',
  '’Ð' => 'æ�«',
  '’Ñ' => 'æ�¬',
  '’Ò' => 'æ�¯',
  '’Ó' => 'æ�°',
  '’Ô' => 'æ�²',
  '’Õ' => 'æ�³',
  '’Ö' => 'æ�´',
  '’×' => 'æ�µ',
  '’Ø' => 'æ�¸',
  '’Ù' => 'æ�¹',
  '’Ú' => 'æ�¼',
  '’Û' => 'æ�½',
  '’Ü' => 'æ�¾',
  '’Ý' => 'æ�¿',
  '’Þ' => 'æŽ�',
  '’ß' => 'æŽƒ',
  '’à' => 'æŽ„',
  '’á' => 'æŽ…',
  '’â' => 'æŽ†',
  '’ã' => 'æŽ‹',
  '’ä' => 'æŽ�',
  '’å' => 'æŽ‘',
  '’æ' => 'æŽ“',
  '’ç' => 'æŽ”',
  '’è' => 'æŽ•',
  '’é' => 'æŽ—',
  '’ê' => 'æŽ™',
  '’ë' => 'æŽš',
  '’ì' => 'æŽ›',
  '’í' => 'æŽœ',
  '’î' => 'æŽ�',
  '’ï' => 'æŽž',
  '’ð' => 'æŽŸ',
  '’ñ' => 'æŽ¡',
  '’ò' => 'æŽ¤',
  '’ó' => 'æŽ¦',
  '’ô' => 'æŽ«',
  '’õ' => 'æŽ¯',
  '’ö' => 'æŽ±',
  '’÷' => 'æŽ²',
  '’ø' => 'æŽµ',
  '’ù' => 'æŽ¶',
  '’ú' => 'æŽ¹',
  '’û' => 'æŽ»',
  '’ü' => 'æŽ½',
  '’ý' => 'æŽ¿',
  '’þ' => 'æ�€',
  '“@' => 'æ��',
  '“A' => 'æ�‚',
  '“B' => 'æ�ƒ',
  '“C' => 'æ�…',
  '“D' => 'æ�‡',
  '“E' => 'æ�ˆ',
  '“F' => 'æ�Š',
  '“G' => 'æ�‹',
  '“H' => 'æ�Œ',
  '“I' => 'æ�‘',
  '“J' => 'æ�“',
  '“K' => 'æ�”',
  '“L' => 'æ�•',
  '“M' => 'æ�—',
  '“N' => 'æ�˜',
  '“O' => 'æ�™',
  '“P' => 'æ�š',
  '“Q' => 'æ�›',
  '“R' => 'æ�œ',
  '“S' => 'æ��',
  '“T' => 'æ�Ÿ',
  '“U' => 'æ�¢',
  '“V' => 'æ�¤',
  '“W' => 'æ�¥',
  '“X' => 'æ�¦',
  '“Y' => 'æ�§',
  '“Z' => 'æ�¨',
  '“[' => 'æ�«',
  '“\\' => 'æ�¬',
  '“]' => 'æ�®',
  '“^' => 'æ�¯',
  '“_' => 'æ�°',
  '“`' => 'æ�±',
  '“a' => 'æ�³',
  '“b' => 'æ�µ',
  '“c' => 'æ�·',
  '“d' => 'æ�¹',
  '“e' => 'æ�º',
  '“f' => 'æ�»',
  '“g' => 'æ�¼',
  '“h' => 'æ�¾',
  '“i' => 'æ�ƒ',
  '“j' => 'æ�„',
  '“k' => 'æ�†',
  '“l' => 'æ�‡',
  '“m' => 'æ�ˆ',
  '“n' => 'æ�‰',
  '“o' => 'æ�Š',
  '“p' => 'æ��',
  '“q' => 'æ�Ž',
  '“r' => 'æ�‘',
  '“s' => 'æ�’',
  '“t' => 'æ�•',
  '“u' => 'æ�–',
  '“v' => 'æ�—',
  '“w' => 'æ�˜',
  '“x' => 'æ�™',
  '“y' => 'æ�š',
  '“z' => 'æ��',
  '“{' => 'æ�Ÿ',
  '“|' => 'æ�¢',
  '“}' => 'æ�£',
  '“~' => 'æ�¤',
  '“€' => 'æ�¥',
  '“�' => 'æ�§',
  '“‚' => 'æ�¨',
  '“ƒ' => 'æ�©',
  '“„' => 'æ�«',
  '“…' => 'æ�®',
  '“†' => 'æ�¯',
  '“‡' => 'æ�°',
  '“ˆ' => 'æ�±',
  '“‰' => 'æ�²',
  '“Š' => 'æ�³',
  '“‹' => 'æ�µ',
  '“Œ' => 'æ�¶',
  '“�' => 'æ�·',
  '“Ž' => 'æ�¸',
  '“�' => 'æ�¹',
  '“�' => 'æ�»',
  '“‘' => 'æ�¼',
  '“’' => 'æ�¾',
  '““' => 'æ‘€',
  '“”' => 'æ‘‚',
  '“•' => 'æ‘ƒ',
  '“–' => 'æ‘‰',
  '“—' => 'æ‘‹',
  '“˜' => 'æ‘Œ',
  '“™' => 'æ‘�',
  '“š' => 'æ‘Ž',
  '“›' => 'æ‘�',
  '“œ' => 'æ‘�',
  '“�' => 'æ‘‘',
  '“ž' => 'æ‘“',
  '“Ÿ' => 'æ‘•',
  '“ ' => 'æ‘–',
  '“¡' => 'æ‘—',
  '“¢' => 'æ‘™',
  '“£' => 'æ‘š',
  '“¤' => 'æ‘›',
  '“¥' => 'æ‘œ',
  '“¦' => 'æ‘�',
  '“§' => 'æ‘Ÿ',
  '“¨' => 'æ‘ ',
  '“©' => 'æ‘¡',
  '“ª' => 'æ‘¢',
  '“«' => 'æ‘£',
  '“¬' => 'æ‘¤',
  '“­' => 'æ‘¥',
  '“®' => 'æ‘¦',
  '“¯' => 'æ‘¨',
  '“°' => 'æ‘ª',
  '“±' => 'æ‘«',
  '“²' => 'æ‘¬',
  '“³' => 'æ‘®',
  '“´' => 'æ‘¯',
  '“µ' => 'æ‘°',
  '“¶' => 'æ‘±',
  '“·' => 'æ‘²',
  '“¸' => 'æ‘³',
  '“¹' => 'æ‘´',
  '“º' => 'æ‘µ',
  '“»' => 'æ‘¶',
  '“¼' => 'æ‘·',
  '“½' => 'æ‘»',
  '“¾' => 'æ‘¼',
  '“¿' => 'æ‘½',
  '“À' => 'æ‘¾',
  '“Á' => 'æ‘¿',
  '“Â' => 'æ’€',
  '“Ã' => 'æ’�',
  '“Ä' => 'æ’ƒ',
  '“Å' => 'æ’†',
  '“Æ' => 'æ’ˆ',
  '“Ç' => 'æ’‰',
  '“È' => 'æ’Š',
  '“É' => 'æ’‹',
  '“Ê' => 'æ’Œ',
  '“Ë' => 'æ’�',
  '“Ì' => 'æ’Ž',
  '“Í' => 'æ’�',
  '“Î' => 'æ’�',
  '“Ï' => 'æ’“',
  '“Ð' => 'æ’”',
  '“Ñ' => 'æ’—',
  '“Ò' => 'æ’˜',
  '“Ó' => 'æ’š',
  '“Ô' => 'æ’›',
  '“Õ' => 'æ’œ',
  '“Ö' => 'æ’�',
  '“×' => 'æ’Ÿ',
  '“Ø' => 'æ’ ',
  '“Ù' => 'æ’¡',
  '“Ú' => 'æ’¢',
  '“Û' => 'æ’£',
  '“Ü' => 'æ’¥',
  '“Ý' => 'æ’¦',
  '“Þ' => 'æ’§',
  '“ß' => 'æ’¨',
  '“à' => 'æ’ª',
  '“á' => 'æ’«',
  '“â' => 'æ’¯',
  '“ã' => 'æ’±',
  '“ä' => 'æ’²',
  '“å' => 'æ’³',
  '“æ' => 'æ’´',
  '“ç' => 'æ’¶',
  '“è' => 'æ’¹',
  '“é' => 'æ’»',
  '“ê' => 'æ’½',
  '“ë' => 'æ’¾',
  '“ì' => 'æ’¿',
  '“í' => 'æ“�',
  '“î' => 'æ“ƒ',
  '“ï' => 'æ“„',
  '“ð' => 'æ“†',
  '“ñ' => 'æ“‡',
  '“ò' => 'æ“ˆ',
  '“ó' => 'æ“‰',
  '“ô' => 'æ“Š',
  '“õ' => 'æ“‹',
  '“ö' => 'æ“Œ',
  '“÷' => 'æ“�',
  '“ø' => 'æ“‘',
  '“ù' => 'æ““',
  '“ú' => 'æ“”',
  '“û' => 'æ“•',
  '“ü' => 'æ“–',
  '“ý' => 'æ“™',
  '“þ' => 'æ“š',
  '”@' => 'æ“›',
  '”A' => 'æ“œ',
  '”B' => 'æ“�',
  '”C' => 'æ“Ÿ',
  '”D' => 'æ“ ',
  '”E' => 'æ“¡',
  '”F' => 'æ“£',
  '”G' => 'æ“¥',
  '”H' => 'æ“§',
  '”I' => 'æ“¨',
  '”J' => 'æ“©',
  '”K' => 'æ“ª',
  '”L' => 'æ“«',
  '”M' => 'æ“¬',
  '”N' => 'æ“­',
  '”O' => 'æ“®',
  '”P' => 'æ“¯',
  '”Q' => 'æ“°',
  '”R' => 'æ“±',
  '”S' => 'æ“²',
  '”T' => 'æ“³',
  '”U' => 'æ“´',
  '”V' => 'æ“µ',
  '”W' => 'æ“¶',
  '”X' => 'æ“·',
  '”Y' => 'æ“¸',
  '”Z' => 'æ“¹',
  '”[' => 'æ“º',
  '”\\' => 'æ“»',
  '”]' => 'æ“¼',
  '”^' => 'æ“½',
  '”_' => 'æ“¾',
  '”`' => 'æ“¿',
  '”a' => 'æ”�',
  '”b' => 'æ”‚',
  '”c' => 'æ”ƒ',
  '”d' => 'æ”„',
  '”e' => 'æ”…',
  '”f' => 'æ”†',
  '”g' => 'æ”‡',
  '”h' => 'æ”ˆ',
  '”i' => 'æ”Š',
  '”j' => 'æ”‹',
  '”k' => 'æ”Œ',
  '”l' => 'æ”�',
  '”m' => 'æ”Ž',
  '”n' => 'æ”�',
  '”o' => 'æ”�',
  '”p' => 'æ”‘',
  '”q' => 'æ”“',
  '”r' => 'æ””',
  '”s' => 'æ”•',
  '”t' => 'æ”–',
  '”u' => 'æ”—',
  '”v' => 'æ”™',
  '”w' => 'æ”š',
  '”x' => 'æ”›',
  '”y' => 'æ”œ',
  '”z' => 'æ”�',
  '”{' => 'æ”ž',
  '”|' => 'æ”Ÿ',
  '”}' => 'æ” ',
  '”~' => 'æ”¡',
  '”€' => 'æ”¢',
  '”�' => 'æ”£',
  '”‚' => 'æ”¤',
  '”ƒ' => 'æ”¦',
  '”„' => 'æ”§',
  '”…' => 'æ”¨',
  '”†' => 'æ”©',
  '”‡' => 'æ”ª',
  '”ˆ' => 'æ”¬',
  '”‰' => 'æ”­',
  '”Š' => 'æ”°',
  '”‹' => 'æ”±',
  '”Œ' => 'æ”²',
  '”�' => 'æ”³',
  '”Ž' => 'æ”·',
  '”�' => 'æ”º',
  '”�' => 'æ”¼',
  '”‘' => 'æ”½',
  '”’' => 'æ•€',
  '”“' => 'æ•�',
  '””' => 'æ•‚',
  '”•' => 'æ•ƒ',
  '”–' => 'æ•„',
  '”—' => 'æ•†',
  '”˜' => 'æ•‡',
  '”™' => 'æ•Š',
  '”š' => 'æ•‹',
  '”›' => 'æ•�',
  '”œ' => 'æ•Ž',
  '”�' => 'æ•�',
  '”ž' => 'æ•’',
  '”Ÿ' => 'æ•“',
  '” ' => 'æ•”',
  '”¡' => 'æ•—',
  '”¢' => 'æ•˜',
  '”£' => 'æ•š',
  '”¤' => 'æ•œ',
  '”¥' => 'æ•Ÿ',
  '”¦' => 'æ• ',
  '”§' => 'æ•¡',
  '”¨' => 'æ•¤',
  '”©' => 'æ•¥',
  '”ª' => 'æ•§',
  '”«' => 'æ•¨',
  '”¬' => 'æ•©',
  '”­' => 'æ•ª',
  '”®' => 'æ•­',
  '”¯' => 'æ•®',
  '”°' => 'æ•¯',
  '”±' => 'æ•±',
  '”²' => 'æ•³',
  '”³' => 'æ•µ',
  '”´' => 'æ•¶',
  '”µ' => 'æ•¸',
  '”¶' => 'æ•¹',
  '”·' => 'æ•º',
  '”¸' => 'æ•»',
  '”¹' => 'æ•¼',
  '”º' => 'æ•½',
  '”»' => 'æ•¾',
  '”¼' => 'æ•¿',
  '”½' => 'æ–€',
  '”¾' => 'æ–�',
  '”¿' => 'æ–‚',
  '”À' => 'æ–ƒ',
  '”Á' => 'æ–„',
  '”Â' => 'æ–…',
  '”Ã' => 'æ–†',
  '”Ä' => 'æ–ˆ',
  '”Å' => 'æ–‰',
  '”Æ' => 'æ–Š',
  '”Ç' => 'æ–�',
  '”È' => 'æ–Ž',
  '”É' => 'æ–�',
  '”Ê' => 'æ–’',
  '”Ë' => 'æ–”',
  '”Ì' => 'æ–•',
  '”Í' => 'æ––',
  '”Î' => 'æ–˜',
  '”Ï' => 'æ–š',
  '”Ð' => 'æ–�',
  '”Ñ' => 'æ–ž',
  '”Ò' => 'æ– ',
  '”Ó' => 'æ–¢',
  '”Ô' => 'æ–£',
  '”Õ' => 'æ–¦',
  '”Ö' => 'æ–¨',
  '”×' => 'æ–ª',
  '”Ø' => 'æ–¬',
  '”Ù' => 'æ–®',
  '”Ú' => 'æ–±',
  '”Û' => 'æ–²',
  '”Ü' => 'æ–³',
  '”Ý' => 'æ–´',
  '”Þ' => 'æ–µ',
  '”ß' => 'æ–¶',
  '”à' => 'æ–·',
  '”á' => 'æ–¸',
  '”â' => 'æ–º',
  '”ã' => 'æ–»',
  '”ä' => 'æ–¾',
  '”å' => 'æ–¿',
  '”æ' => 'æ—€',
  '”ç' => 'æ—‚',
  '”è' => 'æ—‡',
  '”é' => 'æ—ˆ',
  '”ê' => 'æ—‰',
  '”ë' => 'æ—Š',
  '”ì' => 'æ—�',
  '”í' => 'æ—�',
  '”î' => 'æ—‘',
  '”ï' => 'æ—“',
  '”ð' => 'æ—”',
  '”ñ' => 'æ—•',
  '”ò' => 'æ—˜',
  '”ó' => 'æ—™',
  '”ô' => 'æ—š',
  '”õ' => 'æ—›',
  '”ö' => 'æ—œ',
  '”÷' => 'æ—�',
  '”ø' => 'æ—ž',
  '”ù' => 'æ—Ÿ',
  '”ú' => 'æ—¡',
  '”û' => 'æ—£',
  '”ü' => 'æ—¤',
  '”ý' => 'æ—ª',
  '”þ' => 'æ—«',
  '•@' => 'æ—²',
  '•A' => 'æ—³',
  '•B' => 'æ—´',
  '•C' => 'æ—µ',
  '•D' => 'æ—¸',
  '•E' => 'æ—¹',
  '•F' => 'æ—»',
  '•G' => 'æ—¼',
  '•H' => 'æ—½',
  '•I' => 'æ—¾',
  '•J' => 'æ—¿',
  '•K' => 'æ˜�',
  '•L' => 'æ˜„',
  '•M' => 'æ˜…',
  '•N' => 'æ˜‡',
  '•O' => 'æ˜ˆ',
  '•P' => 'æ˜‰',
  '•Q' => 'æ˜‹',
  '•R' => 'æ˜�',
  '•S' => 'æ˜�',
  '•T' => 'æ˜‘',
  '•U' => 'æ˜’',
  '•V' => 'æ˜–',
  '•W' => 'æ˜—',
  '•X' => 'æ˜˜',
  '•Y' => 'æ˜š',
  '•Z' => 'æ˜›',
  '•[' => 'æ˜œ',
  '•\\' => 'æ˜ž',
  '•]' => 'æ˜¡',
  '•^' => 'æ˜¢',
  '•_' => 'æ˜£',
  '•`' => 'æ˜¤',
  '•a' => 'æ˜¦',
  '•b' => 'æ˜©',
  '•c' => 'æ˜ª',
  '•d' => 'æ˜«',
  '•e' => 'æ˜¬',
  '•f' => 'æ˜®',
  '•g' => 'æ˜°',
  '•h' => 'æ˜²',
  '•i' => 'æ˜³',
  '•j' => 'æ˜·',
  '•k' => 'æ˜¸',
  '•l' => 'æ˜¹',
  '•m' => 'æ˜º',
  '•n' => 'æ˜»',
  '•o' => 'æ˜½',
  '•p' => 'æ˜¿',
  '•q' => 'æ™€',
  '•r' => 'æ™‚',
  '•s' => 'æ™„',
  '•t' => 'æ™…',
  '•u' => 'æ™†',
  '•v' => 'æ™‡',
  '•w' => 'æ™ˆ',
  '•x' => 'æ™‰',
  '•y' => 'æ™Š',
  '•z' => 'æ™�',
  '•{' => 'æ™Ž',
  '•|' => 'æ™�',
  '•}' => 'æ™‘',
  '•~' => 'æ™˜',
  '•€' => 'æ™™',
  '•�' => 'æ™›',
  '•‚' => 'æ™œ',
  '•ƒ' => 'æ™�',
  '•„' => 'æ™ž',
  '•…' => 'æ™ ',
  '•†' => 'æ™¢',
  '•‡' => 'æ™£',
  '•ˆ' => 'æ™¥',
  '•‰' => 'æ™§',
  '•Š' => 'æ™©',
  '•‹' => 'æ™ª',
  '•Œ' => 'æ™«',
  '•�' => 'æ™¬',
  '•Ž' => 'æ™­',
  '•�' => 'æ™±',
  '•�' => 'æ™²',
  '•‘' => 'æ™³',
  '•’' => 'æ™µ',
  '•“' => 'æ™¸',
  '•”' => 'æ™¹',
  '••' => 'æ™»',
  '•–' => 'æ™¼',
  '•—' => 'æ™½',
  '•˜' => 'æ™¿',
  '•™' => 'æš€',
  '•š' => 'æš�',
  '•›' => 'æšƒ',
  '•œ' => 'æš…',
  '•�' => 'æš†',
  '•ž' => 'æšˆ',
  '•Ÿ' => 'æš‰',
  '• ' => 'æšŠ',
  '•¡' => 'æš‹',
  '•¢' => 'æš�',
  '•£' => 'æšŽ',
  '•¤' => 'æš�',
  '•¥' => 'æš�',
  '•¦' => 'æš’',
  '•§' => 'æš“',
  '•¨' => 'æš”',
  '•©' => 'æš•',
  '•ª' => 'æš˜',
  '•«' => 'æš™',
  '•¬' => 'æšš',
  '•­' => 'æš›',
  '•®' => 'æšœ',
  '•¯' => 'æšž',
  '•°' => 'æšŸ',
  '•±' => 'æš ',
  '•²' => 'æš¡',
  '•³' => 'æš¢',
  '•´' => 'æš£',
  '•µ' => 'æš¤',
  '•¶' => 'æš¥',
  '•·' => 'æš¦',
  '•¸' => 'æš©',
  '•¹' => 'æšª',
  '•º' => 'æš«',
  '•»' => 'æš¬',
  '•¼' => 'æš­',
  '•½' => 'æš¯',
  '•¾' => 'æš°',
  '•¿' => 'æš±',
  '•À' => 'æš²',
  '•Á' => 'æš³',
  '•Â' => 'æšµ',
  '•Ã' => 'æš¶',
  '•Ä' => 'æš·',
  '•Å' => 'æš¸',
  '•Æ' => 'æšº',
  '•Ç' => 'æš»',
  '•È' => 'æš¼',
  '•É' => 'æš½',
  '•Ê' => 'æš¿',
  '•Ë' => 'æ›€',
  '•Ì' => 'æ›�',
  '•Í' => 'æ›‚',
  '•Î' => 'æ›ƒ',
  '•Ï' => 'æ›„',
  '•Ð' => 'æ›…',
  '•Ñ' => 'æ›†',
  '•Ò' => 'æ›‡',
  '•Ó' => 'æ›ˆ',
  '•Ô' => 'æ›‰',
  '•Õ' => 'æ›Š',
  '•Ö' => 'æ›‹',
  '•×' => 'æ›Œ',
  '•Ø' => 'æ›�',
  '•Ù' => 'æ›Ž',
  '•Ú' => 'æ›�',
  '•Û' => 'æ›�',
  '•Ü' => 'æ›‘',
  '•Ý' => 'æ›’',
  '•Þ' => 'æ›“',
  '•ß' => 'æ›”',
  '•à' => 'æ›•',
  '•á' => 'æ›–',
  '•â' => 'æ›—',
  '•ã' => 'æ›˜',
  '•ä' => 'æ›š',
  '•å' => 'æ›ž',
  '•æ' => 'æ›Ÿ',
  '•ç' => 'æ› ',
  '•è' => 'æ›¡',
  '•é' => 'æ›¢',
  '•ê' => 'æ›£',
  '•ë' => 'æ›¤',
  '•ì' => 'æ›¥',
  '•í' => 'æ›§',
  '•î' => 'æ›¨',
  '•ï' => 'æ›ª',
  '•ð' => 'æ›«',
  '•ñ' => 'æ›¬',
  '•ò' => 'æ›­',
  '•ó' => 'æ›®',
  '•ô' => 'æ›¯',
  '•õ' => 'æ›±',
  '•ö' => 'æ›µ',
  '•÷' => 'æ›¶',
  '•ø' => 'æ›¸',
  '•ù' => 'æ›º',
  '•ú' => 'æ›»',
  '•û' => 'æ›½',
  '•ü' => 'æœ�',
  '•ý' => 'æœ‚',
  '•þ' => 'æœƒ',
  '–@' => 'æœ„',
  '–A' => 'æœ…',
  '–B' => 'æœ†',
  '–C' => 'æœ‡',
  '–D' => 'æœŒ',
  '–E' => 'æœŽ',
  '–F' => 'æœ�',
  '–G' => 'æœ‘',
  '–H' => 'æœ’',
  '–I' => 'æœ“',
  '–J' => 'æœ–',
  '–K' => 'æœ˜',
  '–L' => 'æœ™',
  '–M' => 'æœš',
  '–N' => 'æœœ',
  '–O' => 'æœž',
  '–P' => 'æœ ',
  '–Q' => 'æœ¡',
  '–R' => 'æœ¢',
  '–S' => 'æœ£',
  '–T' => 'æœ¤',
  '–U' => 'æœ¥',
  '–V' => 'æœ§',
  '–W' => 'æœ©',
  '–X' => 'æœ®',
  '–Y' => 'æœ°',
  '–Z' => 'æœ²',
  '–[' => 'æœ³',
  '–\\' => 'æœ¶',
  '–]' => 'æœ·',
  '–^' => 'æœ¸',
  '–_' => 'æœ¹',
  '–`' => 'æœ»',
  '–a' => 'æœ¼',
  '–b' => 'æœ¾',
  '–c' => 'æœ¿',
  '–d' => 'æ��',
  '–e' => 'æ�„',
  '–f' => 'æ�…',
  '–g' => 'æ�‡',
  '–h' => 'æ�Š',
  '–i' => 'æ�‹',
  '–j' => 'æ��',
  '–k' => 'æ�’',
  '–l' => 'æ�”',
  '–m' => 'æ�•',
  '–n' => 'æ�—',
  '–o' => 'æ�˜',
  '–p' => 'æ�™',
  '–q' => 'æ�š',
  '–r' => 'æ�›',
  '–s' => 'æ��',
  '–t' => 'æ�¢',
  '–u' => 'æ�£',
  '–v' => 'æ�¤',
  '–w' => 'æ�¦',
  '–x' => 'æ�§',
  '–y' => 'æ�«',
  '–z' => 'æ�¬',
  '–{' => 'æ�®',
  '–|' => 'æ�±',
  '–}' => 'æ�´',
  '–~' => 'æ�¶',
  '–€' => 'æ�¸',
  '–�' => 'æ�¹',
  '–‚' => 'æ�º',
  '–ƒ' => 'æ�»',
  '–„' => 'æ�½',
  '–…' => 'æž€',
  '–†' => 'æž‚',
  '–‡' => 'æžƒ',
  '–ˆ' => 'æž…',
  '–‰' => 'æž†',
  '–Š' => 'æžˆ',
  '–‹' => 'æžŠ',
  '–Œ' => 'æžŒ',
  '–�' => 'æž�',
  '–Ž' => 'æžŽ',
  '–�' => 'æž�',
  '–�' => 'æž‘',
  '–‘' => 'æž’',
  '–’' => 'æž“',
  '–“' => 'æž”',
  '–”' => 'æž–',
  '–•' => 'æž™',
  '––' => 'æž›',
  '–—' => 'æžŸ',
  '–˜' => 'æž ',
  '–™' => 'æž¡',
  '–š' => 'æž¤',
  '–›' => 'æž¦',
  '–œ' => 'æž©',
  '–�' => 'æž¬',
  '–ž' => 'æž®',
  '–Ÿ' => 'æž±',
  '– ' => 'æž²',
  '–¡' => 'æž´',
  '–¢' => 'æž¹',
  '–£' => 'æžº',
  '–¤' => 'æž»',
  '–¥' => 'æž¼',
  '–¦' => 'æž½',
  '–§' => 'æž¾',
  '–¨' => 'æž¿',
  '–©' => 'æŸ€',
  '–ª' => 'æŸ‚',
  '–«' => 'æŸ…',
  '–¬' => 'æŸ†',
  '–­' => 'æŸ‡',
  '–®' => 'æŸˆ',
  '–¯' => 'æŸ‰',
  '–°' => 'æŸŠ',
  '–±' => 'æŸ‹',
  '–²' => 'æŸŒ',
  '–³' => 'æŸ�',
  '–´' => 'æŸŽ',
  '–µ' => 'æŸ•',
  '–¶' => 'æŸ–',
  '–·' => 'æŸ—',
  '–¸' => 'æŸ›',
  '–¹' => 'æŸŸ',
  '–º' => 'æŸ¡',
  '–»' => 'æŸ£',
  '–¼' => 'æŸ¤',
  '–½' => 'æŸ¦',
  '–¾' => 'æŸ§',
  '–¿' => 'æŸ¨',
  '–À' => 'æŸª',
  '–Á' => 'æŸ«',
  '–Â' => 'æŸ­',
  '–Ã' => 'æŸ®',
  '–Ä' => 'æŸ²',
  '–Å' => 'æŸµ',
  '–Æ' => 'æŸ¶',
  '–Ç' => 'æŸ·',
  '–È' => 'æŸ¸',
  '–É' => 'æŸ¹',
  '–Ê' => 'æŸº',
  '–Ë' => 'æŸ»',
  '–Ì' => 'æŸ¼',
  '–Í' => 'æŸ¾',
  '–Î' => 'æ �',
  '–Ï' => 'æ ‚',
  '–Ð' => 'æ ƒ',
  '–Ñ' => 'æ „',
  '–Ò' => 'æ †',
  '–Ó' => 'æ �',
  '–Ô' => 'æ �',
  '–Õ' => 'æ ’',
  '–Ö' => 'æ ”',
  '–×' => 'æ •',
  '–Ø' => 'æ ˜',
  '–Ù' => 'æ ™',
  '–Ú' => 'æ š',
  '–Û' => 'æ ›',
  '–Ü' => 'æ œ',
  '–Ý' => 'æ ž',
  '–Þ' => 'æ Ÿ',
  '–ß' => 'æ  ',
  '–à' => 'æ ¢',
  '–á' => 'æ £',
  '–â' => 'æ ¤',
  '–ã' => 'æ ¥',
  '–ä' => 'æ ¦',
  '–å' => 'æ §',
  '–æ' => 'æ ¨',
  '–ç' => 'æ «',
  '–è' => 'æ ¬',
  '–é' => 'æ ­',
  '–ê' => 'æ ®',
  '–ë' => 'æ ¯',
  '–ì' => 'æ °',
  '–í' => 'æ ±',
  '–î' => 'æ ´',
  '–ï' => 'æ µ',
  '–ð' => 'æ ¶',
  '–ñ' => 'æ º',
  '–ò' => 'æ »',
  '–ó' => 'æ ¿',
  '–ô' => 'æ¡‡',
  '–õ' => 'æ¡‹',
  '–ö' => 'æ¡�',
  '–÷' => 'æ¡�',
  '–ø' => 'æ¡’',
  '–ù' => 'æ¡–',
  '–ú' => 'æ¡—',
  '–û' => 'æ¡˜',
  '–ü' => 'æ¡™',
  '–ý' => 'æ¡š',
  '–þ' => 'æ¡›',
  '—@' => 'æ¡œ',
  '—A' => 'æ¡�',
  '—B' => 'æ¡ž',
  '—C' => 'æ¡Ÿ',
  '—D' => 'æ¡ª',
  '—E' => 'æ¡¬',
  '—F' => 'æ¡­',
  '—G' => 'æ¡®',
  '—H' => 'æ¡¯',
  '—I' => 'æ¡°',
  '—J' => 'æ¡±',
  '—K' => 'æ¡²',
  '—L' => 'æ¡³',
  '—M' => 'æ¡µ',
  '—N' => 'æ¡¸',
  '—O' => 'æ¡¹',
  '—P' => 'æ¡º',
  '—Q' => 'æ¡»',
  '—R' => 'æ¡¼',
  '—S' => 'æ¡½',
  '—T' => 'æ¡¾',
  '—U' => 'æ¡¿',
  '—V' => 'æ¢€',
  '—W' => 'æ¢‚',
  '—X' => 'æ¢„',
  '—Y' => 'æ¢‡',
  '—Z' => 'æ¢ˆ',
  '—[' => 'æ¢‰',
  '—\\' => 'æ¢Š',
  '—]' => 'æ¢‹',
  '—^' => 'æ¢Œ',
  '—_' => 'æ¢�',
  '—`' => 'æ¢Ž',
  '—a' => 'æ¢�',
  '—b' => 'æ¢‘',
  '—c' => 'æ¢’',
  '—d' => 'æ¢”',
  '—e' => 'æ¢•',
  '—f' => 'æ¢–',
  '—g' => 'æ¢˜',
  '—h' => 'æ¢™',
  '—i' => 'æ¢š',
  '—j' => 'æ¢›',
  '—k' => 'æ¢œ',
  '—l' => 'æ¢�',
  '—m' => 'æ¢ž',
  '—n' => 'æ¢Ÿ',
  '—o' => 'æ¢ ',
  '—p' => 'æ¢¡',
  '—q' => 'æ¢£',
  '—r' => 'æ¢¤',
  '—s' => 'æ¢¥',
  '—t' => 'æ¢©',
  '—u' => 'æ¢ª',
  '—v' => 'æ¢«',
  '—w' => 'æ¢¬',
  '—x' => 'æ¢®',
  '—y' => 'æ¢±',
  '—z' => 'æ¢²',
  '—{' => 'æ¢´',
  '—|' => 'æ¢¶',
  '—}' => 'æ¢·',
  '—~' => 'æ¢¸',
  '—€' => 'æ¢¹',
  '—�' => 'æ¢º',
  '—‚' => 'æ¢»',
  '—ƒ' => 'æ¢¼',
  '—„' => 'æ¢½',
  '—…' => 'æ¢¾',
  '—†' => 'æ¢¿',
  '—‡' => 'æ£�',
  '—ˆ' => 'æ£ƒ',
  '—‰' => 'æ£„',
  '—Š' => 'æ£…',
  '—‹' => 'æ£†',
  '—Œ' => 'æ£‡',
  '—�' => 'æ£ˆ',
  '—Ž' => 'æ£Š',
  '—�' => 'æ£Œ',
  '—�' => 'æ£Ž',
  '—‘' => 'æ£�',
  '—’' => 'æ£�',
  '—“' => 'æ£‘',
  '—”' => 'æ£“',
  '—•' => 'æ£”',
  '—–' => 'æ£–',
  '——' => 'æ£—',
  '—˜' => 'æ£™',
  '—™' => 'æ£›',
  '—š' => 'æ£œ',
  '—›' => 'æ£�',
  '—œ' => 'æ£ž',
  '—�' => 'æ£Ÿ',
  '—ž' => 'æ£¡',
  '—Ÿ' => 'æ£¢',
  '— ' => 'æ£¤',
  '—¡' => 'æ£¥',
  '—¢' => 'æ£¦',
  '—£' => 'æ£§',
  '—¤' => 'æ£¨',
  '—¥' => 'æ£©',
  '—¦' => 'æ£ª',
  '—§' => 'æ£«',
  '—¨' => 'æ£¬',
  '—©' => 'æ£­',
  '—ª' => 'æ£¯',
  '—«' => 'æ£²',
  '—¬' => 'æ£³',
  '—­' => 'æ£´',
  '—®' => 'æ£¶',
  '—¯' => 'æ£·',
  '—°' => 'æ£¸',
  '—±' => 'æ£»',
  '—²' => 'æ£½',
  '—³' => 'æ£¾',
  '—´' => 'æ£¿',
  '—µ' => 'æ¤€',
  '—¶' => 'æ¤‚',
  '—·' => 'æ¤ƒ',
  '—¸' => 'æ¤„',
  '—¹' => 'æ¤†',
  '—º' => 'æ¤‡',
  '—»' => 'æ¤ˆ',
  '—¼' => 'æ¤‰',
  '—½' => 'æ¤Š',
  '—¾' => 'æ¤Œ',
  '—¿' => 'æ¤�',
  '—À' => 'æ¤‘',
  '—Á' => 'æ¤“',
  '—Â' => 'æ¤”',
  '—Ã' => 'æ¤•',
  '—Ä' => 'æ¤–',
  '—Å' => 'æ¤—',
  '—Æ' => 'æ¤˜',
  '—Ç' => 'æ¤™',
  '—È' => 'æ¤š',
  '—É' => 'æ¤›',
  '—Ê' => 'æ¤œ',
  '—Ë' => 'æ¤�',
  '—Ì' => 'æ¤ž',
  '—Í' => 'æ¤¡',
  '—Î' => 'æ¤¢',
  '—Ï' => 'æ¤£',
  '—Ð' => 'æ¤¥',
  '—Ñ' => 'æ¤¦',
  '—Ò' => 'æ¤§',
  '—Ó' => 'æ¤¨',
  '—Ô' => 'æ¤©',
  '—Õ' => 'æ¤ª',
  '—Ö' => 'æ¤«',
  '—×' => 'æ¤¬',
  '—Ø' => 'æ¤®',
  '—Ù' => 'æ¤¯',
  '—Ú' => 'æ¤±',
  '—Û' => 'æ¤²',
  '—Ü' => 'æ¤³',
  '—Ý' => 'æ¤µ',
  '—Þ' => 'æ¤¶',
  '—ß' => 'æ¤·',
  '—à' => 'æ¤¸',
  '—á' => 'æ¤º',
  '—â' => 'æ¤»',
  '—ã' => 'æ¤¼',
  '—ä' => 'æ¤¾',
  '—å' => 'æ¥€',
  '—æ' => 'æ¥�',
  '—ç' => 'æ¥ƒ',
  '—è' => 'æ¥„',
  '—é' => 'æ¥…',
  '—ê' => 'æ¥†',
  '—ë' => 'æ¥‡',
  '—ì' => 'æ¥ˆ',
  '—í' => 'æ¥‰',
  '—î' => 'æ¥Š',
  '—ï' => 'æ¥‹',
  '—ð' => 'æ¥Œ',
  '—ñ' => 'æ¥�',
  '—ò' => 'æ¥Ž',
  '—ó' => 'æ¥�',
  '—ô' => 'æ¥�',
  '—õ' => 'æ¥‘',
  '—ö' => 'æ¥’',
  '—÷' => 'æ¥“',
  '—ø' => 'æ¥•',
  '—ù' => 'æ¥–',
  '—ú' => 'æ¥˜',
  '—û' => 'æ¥™',
  '—ü' => 'æ¥›',
  '—ý' => 'æ¥œ',
  '—þ' => 'æ¥Ÿ',
  '˜@' => 'æ¥¡',
  '˜A' => 'æ¥¢',
  '˜B' => 'æ¥¤',
  '˜C' => 'æ¥¥',
  '˜D' => 'æ¥§',
  '˜E' => 'æ¥¨',
  '˜F' => 'æ¥©',
  '˜G' => 'æ¥ª',
  '˜H' => 'æ¥¬',
  '˜I' => 'æ¥­',
  '˜J' => 'æ¥¯',
  '˜K' => 'æ¥°',
  '˜L' => 'æ¥²',
  '˜M' => 'æ¥³',
  '˜N' => 'æ¥´',
  '˜O' => 'æ¥µ',
  '˜P' => 'æ¥¶',
  '˜Q' => 'æ¥º',
  '˜R' => 'æ¥»',
  '˜S' => 'æ¥½',
  '˜T' => 'æ¥¾',
  '˜U' => 'æ¥¿',
  '˜V' => 'æ¦�',
  '˜W' => 'æ¦ƒ',
  '˜X' => 'æ¦…',
  '˜Y' => 'æ¦Š',
  '˜Z' => 'æ¦‹',
  '˜[' => 'æ¦Œ',
  '˜\\' => 'æ¦Ž',
  '˜]' => 'æ¦�',
  '˜^' => 'æ¦�',
  '˜_' => 'æ¦‘',
  '˜`' => 'æ¦’',
  '˜a' => 'æ¦“',
  '˜b' => 'æ¦–',
  '˜c' => 'æ¦—',
  '˜d' => 'æ¦™',
  '˜e' => 'æ¦š',
  '˜f' => 'æ¦�',
  '˜g' => 'æ¦ž',
  '˜h' => 'æ¦Ÿ',
  '˜i' => 'æ¦ ',
  '˜j' => 'æ¦¡',
  '˜k' => 'æ¦¢',
  '˜l' => 'æ¦£',
  '˜m' => 'æ¦¤',
  '˜n' => 'æ¦¥',
  '˜o' => 'æ¦¦',
  '˜p' => 'æ¦©',
  '˜q' => 'æ¦ª',
  '˜r' => 'æ¦¬',
  '˜s' => 'æ¦®',
  '˜t' => 'æ¦¯',
  '˜u' => 'æ¦°',
  '˜v' => 'æ¦²',
  '˜w' => 'æ¦³',
  '˜x' => 'æ¦µ',
  '˜y' => 'æ¦¶',
  '˜z' => 'æ¦¸',
  '˜{' => 'æ¦¹',
  '˜|' => 'æ¦º',
  '˜}' => 'æ¦¼',
  '˜~' => 'æ¦½',
  '˜€' => 'æ¦¾',
  '˜�' => 'æ¦¿',
  '˜‚' => 'æ§€',
  '˜ƒ' => 'æ§‚',
  '˜„' => 'æ§ƒ',
  '˜…' => 'æ§„',
  '˜†' => 'æ§…',
  '˜‡' => 'æ§†',
  '˜ˆ' => 'æ§‡',
  '˜‰' => 'æ§ˆ',
  '˜Š' => 'æ§‰',
  '˜‹' => 'æ§‹',
  '˜Œ' => 'æ§�',
  '˜�' => 'æ§�',
  '˜Ž' => 'æ§‘',
  '˜�' => 'æ§’',
  '˜�' => 'æ§“',
  '˜‘' => 'æ§•',
  '˜’' => 'æ§–',
  '˜“' => 'æ§—',
  '˜”' => 'æ§˜',
  '˜•' => 'æ§™',
  '˜–' => 'æ§š',
  '˜—' => 'æ§œ',
  '˜˜' => 'æ§�',
  '˜™' => 'æ§ž',
  '˜š' => 'æ§¡',
  '˜›' => 'æ§¢',
  '˜œ' => 'æ§£',
  '˜�' => 'æ§¤',
  '˜ž' => 'æ§¥',
  '˜Ÿ' => 'æ§¦',
  '˜ ' => 'æ§§',
  '˜¡' => 'æ§¨',
  '˜¢' => 'æ§©',
  '˜£' => 'æ§ª',
  '˜¤' => 'æ§«',
  '˜¥' => 'æ§¬',
  '˜¦' => 'æ§®',
  '˜§' => 'æ§¯',
  '˜¨' => 'æ§°',
  '˜©' => 'æ§±',
  '˜ª' => 'æ§³',
  '˜«' => 'æ§´',
  '˜¬' => 'æ§µ',
  '˜­' => 'æ§¶',
  '˜®' => 'æ§·',
  '˜¯' => 'æ§¸',
  '˜°' => 'æ§¹',
  '˜±' => 'æ§º',
  '˜²' => 'æ§»',
  '˜³' => 'æ§¼',
  '˜´' => 'æ§¾',
  '˜µ' => 'æ¨€',
  '˜¶' => 'æ¨�',
  '˜·' => 'æ¨‚',
  '˜¸' => 'æ¨ƒ',
  '˜¹' => 'æ¨„',
  '˜º' => 'æ¨…',
  '˜»' => 'æ¨†',
  '˜¼' => 'æ¨‡',
  '˜½' => 'æ¨ˆ',
  '˜¾' => 'æ¨‰',
  '˜¿' => 'æ¨‹',
  '˜À' => 'æ¨Œ',
  '˜Á' => 'æ¨�',
  '˜Â' => 'æ¨Ž',
  '˜Ã' => 'æ¨�',
  '˜Ä' => 'æ¨�',
  '˜Å' => 'æ¨‘',
  '˜Æ' => 'æ¨’',
  '˜Ç' => 'æ¨“',
  '˜È' => 'æ¨”',
  '˜É' => 'æ¨•',
  '˜Ê' => 'æ¨–',
  '˜Ë' => 'æ¨™',
  '˜Ì' => 'æ¨š',
  '˜Í' => 'æ¨›',
  '˜Î' => 'æ¨œ',
  '˜Ï' => 'æ¨�',
  '˜Ð' => 'æ¨ž',
  '˜Ñ' => 'æ¨ ',
  '˜Ò' => 'æ¨¢',
  '˜Ó' => 'æ¨£',
  '˜Ô' => 'æ¨¤',
  '˜Õ' => 'æ¨¥',
  '˜Ö' => 'æ¨¦',
  '˜×' => 'æ¨§',
  '˜Ø' => 'æ¨©',
  '˜Ù' => 'æ¨«',
  '˜Ú' => 'æ¨¬',
  '˜Û' => 'æ¨­',
  '˜Ü' => 'æ¨®',
  '˜Ý' => 'æ¨°',
  '˜Þ' => 'æ¨²',
  '˜ß' => 'æ¨³',
  '˜à' => 'æ¨´',
  '˜á' => 'æ¨¶',
  '˜â' => 'æ¨·',
  '˜ã' => 'æ¨¸',
  '˜ä' => 'æ¨¹',
  '˜å' => 'æ¨º',
  '˜æ' => 'æ¨»',
  '˜ç' => 'æ¨¼',
  '˜è' => 'æ¨¿',
  '˜é' => 'æ©€',
  '˜ê' => 'æ©�',
  '˜ë' => 'æ©‚',
  '˜ì' => 'æ©ƒ',
  '˜í' => 'æ©…',
  '˜î' => 'æ©†',
  '˜ï' => 'æ©ˆ',
  '˜ð' => 'æ©‰',
  '˜ñ' => 'æ©Š',
  '˜ò' => 'æ©‹',
  '˜ó' => 'æ©Œ',
  '˜ô' => 'æ©�',
  '˜õ' => 'æ©Ž',
  '˜ö' => 'æ©�',
  '˜÷' => 'æ©‘',
  '˜ø' => 'æ©’',
  '˜ù' => 'æ©“',
  '˜ú' => 'æ©”',
  '˜û' => 'æ©•',
  '˜ü' => 'æ©–',
  '˜ý' => 'æ©—',
  '˜þ' => 'æ©š',
  '™@' => 'æ©œ',
  '™A' => 'æ©�',
  '™B' => 'æ©ž',
  '™C' => 'æ©Ÿ',
  '™D' => 'æ© ',
  '™E' => 'æ©¢',
  '™F' => 'æ©£',
  '™G' => 'æ©¤',
  '™H' => 'æ©¦',
  '™I' => 'æ©§',
  '™J' => 'æ©¨',
  '™K' => 'æ©©',
  '™L' => 'æ©ª',
  '™M' => 'æ©«',
  '™N' => 'æ©¬',
  '™O' => 'æ©­',
  '™P' => 'æ©®',
  '™Q' => 'æ©¯',
  '™R' => 'æ©°',
  '™S' => 'æ©²',
  '™T' => 'æ©³',
  '™U' => 'æ©´',
  '™V' => 'æ©µ',
  '™W' => 'æ©¶',
  '™X' => 'æ©·',
  '™Y' => 'æ©¸',
  '™Z' => 'æ©º',
  '™[' => 'æ©»',
  '™\\' => 'æ©½',
  '™]' => 'æ©¾',
  '™^' => 'æ©¿',
  '™_' => 'æª�',
  '™`' => 'æª‚',
  '™a' => 'æªƒ',
  '™b' => 'æª…',
  '™c' => 'æª†',
  '™d' => 'æª‡',
  '™e' => 'æªˆ',
  '™f' => 'æª‰',
  '™g' => 'æªŠ',
  '™h' => 'æª‹',
  '™i' => 'æªŒ',
  '™j' => 'æª�',
  '™k' => 'æª�',
  '™l' => 'æª’',
  '™m' => 'æª“',
  '™n' => 'æª”',
  '™o' => 'æª•',
  '™p' => 'æª–',
  '™q' => 'æª˜',
  '™r' => 'æª™',
  '™s' => 'æªš',
  '™t' => 'æª›',
  '™u' => 'æªœ',
  '™v' => 'æª�',
  '™w' => 'æªž',
  '™x' => 'æªŸ',
  '™y' => 'æª¡',
  '™z' => 'æª¢',
  '™{' => 'æª£',
  '™|' => 'æª¤',
  '™}' => 'æª¥',
  '™~' => 'æª¦',
  '™€' => 'æª§',
  '™�' => 'æª¨',
  '™‚' => 'æªª',
  '™ƒ' => 'æª­',
  '™„' => 'æª®',
  '™…' => 'æª¯',
  '™†' => 'æª°',
  '™‡' => 'æª±',
  '™ˆ' => 'æª²',
  '™‰' => 'æª³',
  '™Š' => 'æª´',
  '™‹' => 'æªµ',
  '™Œ' => 'æª¶',
  '™�' => 'æª·',
  '™Ž' => 'æª¸',
  '™�' => 'æª¹',
  '™�' => 'æªº',
  '™‘' => 'æª»',
  '™’' => 'æª¼',
  '™“' => 'æª½',
  '™”' => 'æª¾',
  '™•' => 'æª¿',
  '™–' => 'æ«€',
  '™—' => 'æ«�',
  '™˜' => 'æ«‚',
  '™™' => 'æ«ƒ',
  '™š' => 'æ«„',
  '™›' => 'æ«…',
  '™œ' => 'æ«†',
  '™�' => 'æ«‡',
  '™ž' => 'æ«ˆ',
  '™Ÿ' => 'æ«‰',
  '™ ' => 'æ«Š',
  '™¡' => 'æ«‹',
  '™¢' => 'æ«Œ',
  '™£' => 'æ«�',
  '™¤' => 'æ«Ž',
  '™¥' => 'æ«�',
  '™¦' => 'æ«�',
  '™§' => 'æ«‘',
  '™¨' => 'æ«’',
  '™©' => 'æ«“',
  '™ª' => 'æ«”',
  '™«' => 'æ«•',
  '™¬' => 'æ«–',
  '™­' => 'æ«—',
  '™®' => 'æ«˜',
  '™¯' => 'æ«™',
  '™°' => 'æ«š',
  '™±' => 'æ«›',
  '™²' => 'æ«œ',
  '™³' => 'æ«�',
  '™´' => 'æ«ž',
  '™µ' => 'æ«Ÿ',
  '™¶' => 'æ« ',
  '™·' => 'æ«¡',
  '™¸' => 'æ«¢',
  '™¹' => 'æ«£',
  '™º' => 'æ«¤',
  '™»' => 'æ«¥',
  '™¼' => 'æ«¦',
  '™½' => 'æ«§',
  '™¾' => 'æ«¨',
  '™¿' => 'æ«©',
  '™À' => 'æ«ª',
  '™Á' => 'æ««',
  '™Â' => 'æ«¬',
  '™Ã' => 'æ«­',
  '™Ä' => 'æ«®',
  '™Å' => 'æ«¯',
  '™Æ' => 'æ«°',
  '™Ç' => 'æ«±',
  '™È' => 'æ«²',
  '™É' => 'æ«³',
  '™Ê' => 'æ«´',
  '™Ë' => 'æ«µ',
  '™Ì' => 'æ«¶',
  '™Í' => 'æ«·',
  '™Î' => 'æ«¸',
  '™Ï' => 'æ«¹',
  '™Ð' => 'æ«º',
  '™Ñ' => 'æ«»',
  '™Ò' => 'æ«¼',
  '™Ó' => 'æ«½',
  '™Ô' => 'æ«¾',
  '™Õ' => 'æ«¿',
  '™Ö' => 'æ¬€',
  '™×' => 'æ¬�',
  '™Ø' => 'æ¬‚',
  '™Ù' => 'æ¬ƒ',
  '™Ú' => 'æ¬„',
  '™Û' => 'æ¬…',
  '™Ü' => 'æ¬†',
  '™Ý' => 'æ¬‡',
  '™Þ' => 'æ¬ˆ',
  '™ß' => 'æ¬‰',
  '™à' => 'æ¬Š',
  '™á' => 'æ¬‹',
  '™â' => 'æ¬Œ',
  '™ã' => 'æ¬�',
  '™ä' => 'æ¬Ž',
  '™å' => 'æ¬�',
  '™æ' => 'æ¬�',
  '™ç' => 'æ¬‘',
  '™è' => 'æ¬’',
  '™é' => 'æ¬“',
  '™ê' => 'æ¬”',
  '™ë' => 'æ¬•',
  '™ì' => 'æ¬–',
  '™í' => 'æ¬—',
  '™î' => 'æ¬˜',
  '™ï' => 'æ¬™',
  '™ð' => 'æ¬š',
  '™ñ' => 'æ¬›',
  '™ò' => 'æ¬œ',
  '™ó' => 'æ¬�',
  '™ô' => 'æ¬ž',
  '™õ' => 'æ¬Ÿ',
  '™ö' => 'æ¬¥',
  '™÷' => 'æ¬¦',
  '™ø' => 'æ¬¨',
  '™ù' => 'æ¬©',
  '™ú' => 'æ¬ª',
  '™û' => 'æ¬«',
  '™ü' => 'æ¬¬',
  '™ý' => 'æ¬­',
  '™þ' => 'æ¬®',
  'š@' => 'æ¬¯',
  'šA' => 'æ¬°',
  'šB' => 'æ¬±',
  'šC' => 'æ¬³',
  'šD' => 'æ¬´',
  'šE' => 'æ¬µ',
  'šF' => 'æ¬¶',
  'šG' => 'æ¬¸',
  'šH' => 'æ¬»',
  'šI' => 'æ¬¼',
  'šJ' => 'æ¬½',
  'šK' => 'æ¬¿',
  'šL' => 'æ­€',
  'šM' => 'æ­�',
  'šN' => 'æ­‚',
  'šO' => 'æ­„',
  'šP' => 'æ­…',
  'šQ' => 'æ­ˆ',
  'šR' => 'æ­Š',
  'šS' => 'æ­‹',
  'šT' => 'æ­�',
  'šU' => 'æ­Ž',
  'šV' => 'æ­�',
  'šW' => 'æ­�',
  'šX' => 'æ­‘',
  'šY' => 'æ­’',
  'šZ' => 'æ­“',
  'š[' => 'æ­”',
  'š\\' => 'æ­•',
  'š]' => 'æ­–',
  'š^' => 'æ­—',
  'š_' => 'æ­˜',
  'š`' => 'æ­š',
  'ša' => 'æ­›',
  'šb' => 'æ­œ',
  'šc' => 'æ­�',
  'šd' => 'æ­ž',
  'še' => 'æ­Ÿ',
  'šf' => 'æ­ ',
  'šg' => 'æ­¡',
  'šh' => 'æ­¨',
  'ši' => 'æ­©',
  'šj' => 'æ­«',
  'šk' => 'æ­¬',
  'šl' => 'æ­­',
  'šm' => 'æ­®',
  'šn' => 'æ­¯',
  'šo' => 'æ­°',
  'šp' => 'æ­±',
  'šq' => 'æ­²',
  'šr' => 'æ­³',
  'šs' => 'æ­´',
  'št' => 'æ­µ',
  'šu' => 'æ­¶',
  'šv' => 'æ­·',
  'šw' => 'æ­¸',
  'šx' => 'æ­º',
  'šy' => 'æ­½',
  'šz' => 'æ­¾',
  'š{' => 'æ­¿',
  'š|' => 'æ®€',
  'š}' => 'æ®…',
  'š~' => 'æ®ˆ',
  'š€' => 'æ®Œ',
  'š�' => 'æ®Ž',
  'š‚' => 'æ®�',
  'šƒ' => 'æ®�',
  'š„' => 'æ®‘',
  'š…' => 'æ®”',
  'š†' => 'æ®•',
  'š‡' => 'æ®—',
  'šˆ' => 'æ®˜',
  'š‰' => 'æ®™',
  'šŠ' => 'æ®œ',
  'š‹' => 'æ®�',
  'šŒ' => 'æ®ž',
  'š�' => 'æ®Ÿ',
  'šŽ' => 'æ® ',
  'š�' => 'æ®¢',
  'š�' => 'æ®£',
  'š‘' => 'æ®¤',
  'š’' => 'æ®¥',
  'š“' => 'æ®¦',
  'š”' => 'æ®§',
  'š•' => 'æ®¨',
  'š–' => 'æ®©',
  'š—' => 'æ®«',
  'š˜' => 'æ®¬',
  'š™' => 'æ®­',
  'šš' => 'æ®®',
  'š›' => 'æ®¯',
  'šœ' => 'æ®°',
  'š�' => 'æ®±',
  'šž' => 'æ®²',
  'šŸ' => 'æ®¶',
  'š ' => 'æ®¸',
  'š¡' => 'æ®¹',
  'š¢' => 'æ®º',
  'š£' => 'æ®»',
  'š¤' => 'æ®¼',
  'š¥' => 'æ®½',
  'š¦' => 'æ®¾',
  'š§' => 'æ¯€',
  'š¨' => 'æ¯ƒ',
  'š©' => 'æ¯„',
  'šª' => 'æ¯†',
  'š«' => 'æ¯‡',
  'š¬' => 'æ¯ˆ',
  'š­' => 'æ¯‰',
  'š®' => 'æ¯Š',
  'š¯' => 'æ¯Œ',
  'š°' => 'æ¯Ž',
  'š±' => 'æ¯�',
  'š²' => 'æ¯‘',
  'š³' => 'æ¯˜',
  'š´' => 'æ¯š',
  'šµ' => 'æ¯œ',
  'š¶' => 'æ¯�',
  'š·' => 'æ¯ž',
  'š¸' => 'æ¯Ÿ',
  'š¹' => 'æ¯ ',
  'šº' => 'æ¯¢',
  'š»' => 'æ¯£',
  'š¼' => 'æ¯¤',
  'š½' => 'æ¯¥',
  'š¾' => 'æ¯¦',
  'š¿' => 'æ¯§',
  'šÀ' => 'æ¯¨',
  'šÁ' => 'æ¯©',
  'šÂ' => 'æ¯¬',
  'šÃ' => 'æ¯­',
  'šÄ' => 'æ¯®',
  'šÅ' => 'æ¯°',
  'šÆ' => 'æ¯±',
  'šÇ' => 'æ¯²',
  'šÈ' => 'æ¯´',
  'šÉ' => 'æ¯¶',
  'šÊ' => 'æ¯·',
  'šË' => 'æ¯¸',
  'šÌ' => 'æ¯º',
  'šÍ' => 'æ¯»',
  'šÎ' => 'æ¯¼',
  'šÏ' => 'æ¯¾',
  'šÐ' => 'æ¯¿',
  'šÑ' => 'æ°€',
  'šÒ' => 'æ°�',
  'šÓ' => 'æ°‚',
  'šÔ' => 'æ°ƒ',
  'šÕ' => 'æ°„',
  'šÖ' => 'æ°ˆ',
  'š×' => 'æ°‰',
  'šØ' => 'æ°Š',
  'šÙ' => 'æ°‹',
  'šÚ' => 'æ°Œ',
  'šÛ' => 'æ°Ž',
  'šÜ' => 'æ°’',
  'šÝ' => 'æ°—',
  'šÞ' => 'æ°œ',
  'šß' => 'æ°�',
  'šà' => 'æ°ž',
  'šá' => 'æ° ',
  'šâ' => 'æ°£',
  'šã' => 'æ°¥',
  'šä' => 'æ°«',
  'šå' => 'æ°¬',
  'šæ' => 'æ°­',
  'šç' => 'æ°±',
  'šè' => 'æ°³',
  'šé' => 'æ°¶',
  'šê' => 'æ°·',
  'šë' => 'æ°¹',
  'šì' => 'æ°º',
  'ší' => 'æ°»',
  'šî' => 'æ°¼',
  'šï' => 'æ°¾',
  'šð' => 'æ°¿',
  'šñ' => 'æ±ƒ',
  'šò' => 'æ±„',
  'šó' => 'æ±…',
  'šô' => 'æ±ˆ',
  'šõ' => 'æ±‹',
  'šö' => 'æ±Œ',
  'š÷' => 'æ±�',
  'šø' => 'æ±Ž',
  'šù' => 'æ±�',
  'šú' => 'æ±‘',
  'šû' => 'æ±’',
  'šü' => 'æ±“',
  'šý' => 'æ±–',
  'šþ' => 'æ±˜',
  '›@' => 'æ±™',
  '›A' => 'æ±š',
  '›B' => 'æ±¢',
  '›C' => 'æ±£',
  '›D' => 'æ±¥',
  '›E' => 'æ±¦',
  '›F' => 'æ±§',
  '›G' => 'æ±«',
  '›H' => 'æ±¬',
  '›I' => 'æ±­',
  '›J' => 'æ±®',
  '›K' => 'æ±¯',
  '›L' => 'æ±±',
  '›M' => 'æ±³',
  '›N' => 'æ±µ',
  '›O' => 'æ±·',
  '›P' => 'æ±¸',
  '›Q' => 'æ±º',
  '›R' => 'æ±»',
  '›S' => 'æ±¼',
  '›T' => 'æ±¿',
  '›U' => 'æ²€',
  '›V' => 'æ²„',
  '›W' => 'æ²‡',
  '›X' => 'æ²Š',
  '›Y' => 'æ²‹',
  '›Z' => 'æ²�',
  '›[' => 'æ²Ž',
  '›\\' => 'æ²‘',
  '›]' => 'æ²’',
  '›^' => 'æ²•',
  '›_' => 'æ²–',
  '›`' => 'æ²—',
  '›a' => 'æ²˜',
  '›b' => 'æ²š',
  '›c' => 'æ²œ',
  '›d' => 'æ²�',
  '›e' => 'æ²ž',
  '›f' => 'æ² ',
  '›g' => 'æ²¢',
  '›h' => 'æ²¨',
  '›i' => 'æ²¬',
  '›j' => 'æ²¯',
  '›k' => 'æ²°',
  '›l' => 'æ²´',
  '›m' => 'æ²µ',
  '›n' => 'æ²¶',
  '›o' => 'æ²·',
  '›p' => 'æ²º',
  '›q' => 'æ³€',
  '›r' => 'æ³�',
  '›s' => 'æ³‚',
  '›t' => 'æ³ƒ',
  '›u' => 'æ³†',
  '›v' => 'æ³‡',
  '›w' => 'æ³ˆ',
  '›x' => 'æ³‹',
  '›y' => 'æ³�',
  '›z' => 'æ³Ž',
  '›{' => 'æ³�',
  '›|' => 'æ³‘',
  '›}' => 'æ³’',
  '›~' => 'æ³˜',
  '›€' => 'æ³™',
  '›�' => 'æ³š',
  '›‚' => 'æ³œ',
  '›ƒ' => 'æ³�',
  '›„' => 'æ³Ÿ',
  '›…' => 'æ³¤',
  '›†' => 'æ³¦',
  '›‡' => 'æ³§',
  '›ˆ' => 'æ³©',
  '›‰' => 'æ³¬',
  '›Š' => 'æ³­',
  '›‹' => 'æ³²',
  '›Œ' => 'æ³´',
  '›�' => 'æ³¹',
  '›Ž' => 'æ³¿',
  '›�' => 'æ´€',
  '›�' => 'æ´‚',
  '›‘' => 'æ´ƒ',
  '›’' => 'æ´…',
  '›“' => 'æ´†',
  '›”' => 'æ´ˆ',
  '›•' => 'æ´‰',
  '›–' => 'æ´Š',
  '›—' => 'æ´�',
  '›˜' => 'æ´�',
  '›™' => 'æ´�',
  '›š' => 'æ´‘',
  '››' => 'æ´“',
  '›œ' => 'æ´”',
  '›�' => 'æ´•',
  '›ž' => 'æ´–',
  '›Ÿ' => 'æ´˜',
  '› ' => 'æ´œ',
  '›¡' => 'æ´�',
  '›¢' => 'æ´Ÿ',
  '›£' => 'æ´ ',
  '›¤' => 'æ´¡',
  '›¥' => 'æ´¢',
  '›¦' => 'æ´£',
  '›§' => 'æ´¤',
  '›¨' => 'æ´¦',
  '›©' => 'æ´¨',
  '›ª' => 'æ´©',
  '›«' => 'æ´¬',
  '›¬' => 'æ´­',
  '›­' => 'æ´¯',
  '›®' => 'æ´°',
  '›¯' => 'æ´´',
  '›°' => 'æ´¶',
  '›±' => 'æ´·',
  '›²' => 'æ´¸',
  '›³' => 'æ´º',
  '›´' => 'æ´¿',
  '›µ' => 'æµ€',
  '›¶' => 'æµ‚',
  '›·' => 'æµ„',
  '›¸' => 'æµ‰',
  '›¹' => 'æµŒ',
  '›º' => 'æµ�',
  '›»' => 'æµ•',
  '›¼' => 'æµ–',
  '›½' => 'æµ—',
  '›¾' => 'æµ˜',
  '›¿' => 'æµ›',
  '›À' => 'æµ�',
  '›Á' => 'æµŸ',
  '›Â' => 'æµ¡',
  '›Ã' => 'æµ¢',
  '›Ä' => 'æµ¤',
  '›Å' => 'æµ¥',
  '›Æ' => 'æµ§',
  '›Ç' => 'æµ¨',
  '›È' => 'æµ«',
  '›É' => 'æµ¬',
  '›Ê' => 'æµ­',
  '›Ë' => 'æµ°',
  '›Ì' => 'æµ±',
  '›Í' => 'æµ²',
  '›Î' => 'æµ³',
  '›Ï' => 'æµµ',
  '›Ð' => 'æµ¶',
  '›Ñ' => 'æµ¹',
  '›Ò' => 'æµº',
  '›Ó' => 'æµ»',
  '›Ô' => 'æµ½',
  '›Õ' => 'æµ¾',
  '›Ö' => 'æµ¿',
  '›×' => 'æ¶€',
  '›Ø' => 'æ¶�',
  '›Ù' => 'æ¶ƒ',
  '›Ú' => 'æ¶„',
  '›Û' => 'æ¶†',
  '›Ü' => 'æ¶‡',
  '›Ý' => 'æ¶Š',
  '›Þ' => 'æ¶‹',
  '›ß' => 'æ¶�',
  '›à' => 'æ¶�',
  '›á' => 'æ¶�',
  '›â' => 'æ¶’',
  '›ã' => 'æ¶–',
  '›ä' => 'æ¶—',
  '›å' => 'æ¶˜',
  '›æ' => 'æ¶™',
  '›ç' => 'æ¶š',
  '›è' => 'æ¶œ',
  '›é' => 'æ¶¢',
  '›ê' => 'æ¶¥',
  '›ë' => 'æ¶¬',
  '›ì' => 'æ¶­',
  '›í' => 'æ¶°',
  '›î' => 'æ¶±',
  '›ï' => 'æ¶³',
  '›ð' => 'æ¶´',
  '›ñ' => 'æ¶¶',
  '›ò' => 'æ¶·',
  '›ó' => 'æ¶¹',
  '›ô' => 'æ¶º',
  '›õ' => 'æ¶»',
  '›ö' => 'æ¶¼',
  '›÷' => 'æ¶½',
  '›ø' => 'æ¶¾',
  '›ù' => 'æ·�',
  '›ú' => 'æ·‚',
  '›û' => 'æ·ƒ',
  '›ü' => 'æ·ˆ',
  '›ý' => 'æ·‰',
  '›þ' => 'æ·Š',
  'œ@' => 'æ·�',
  'œA' => 'æ·Ž',
  'œB' => 'æ·�',
  'œC' => 'æ·�',
  'œD' => 'æ·’',
  'œE' => 'æ·“',
  'œF' => 'æ·”',
  'œG' => 'æ·•',
  'œH' => 'æ·—',
  'œI' => 'æ·š',
  'œJ' => 'æ·›',
  'œK' => 'æ·œ',
  'œL' => 'æ·Ÿ',
  'œM' => 'æ·¢',
  'œN' => 'æ·£',
  'œO' => 'æ·¥',
  'œP' => 'æ·§',
  'œQ' => 'æ·¨',
  'œR' => 'æ·©',
  'œS' => 'æ·ª',
  'œT' => 'æ·­',
  'œU' => 'æ·¯',
  'œV' => 'æ·°',
  'œW' => 'æ·²',
  'œX' => 'æ·´',
  'œY' => 'æ·µ',
  'œZ' => 'æ·¶',
  'œ[' => 'æ·¸',
  'œ\\' => 'æ·º',
  'œ]' => 'æ·½',
  'œ^' => 'æ·¾',
  'œ_' => 'æ·¿',
  'œ`' => 'æ¸€',
  'œa' => 'æ¸�',
  'œb' => 'æ¸‚',
  'œc' => 'æ¸ƒ',
  'œd' => 'æ¸„',
  'œe' => 'æ¸†',
  'œf' => 'æ¸‡',
  'œg' => 'æ¸ˆ',
  'œh' => 'æ¸‰',
  'œi' => 'æ¸‹',
  'œj' => 'æ¸�',
  'œk' => 'æ¸’',
  'œl' => 'æ¸“',
  'œm' => 'æ¸•',
  'œn' => 'æ¸˜',
  'œo' => 'æ¸™',
  'œp' => 'æ¸›',
  'œq' => 'æ¸œ',
  'œr' => 'æ¸ž',
  'œs' => 'æ¸Ÿ',
  'œt' => 'æ¸¢',
  'œu' => 'æ¸¦',
  'œv' => 'æ¸§',
  'œw' => 'æ¸¨',
  'œx' => 'æ¸ª',
  'œy' => 'æ¸¬',
  'œz' => 'æ¸®',
  'œ{' => 'æ¸°',
  'œ|' => 'æ¸±',
  'œ}' => 'æ¸³',
  'œ~' => 'æ¸µ',
  'œ€' => 'æ¸¶',
  'œ�' => 'æ¸·',
  'œ‚' => 'æ¸¹',
  'œƒ' => 'æ¸»',
  'œ„' => 'æ¸¼',
  'œ…' => 'æ¸½',
  'œ†' => 'æ¸¾',
  'œ‡' => 'æ¸¿',
  'œˆ' => 'æ¹€',
  'œ‰' => 'æ¹�',
  'œŠ' => 'æ¹‚',
  'œ‹' => 'æ¹…',
  'œŒ' => 'æ¹†',
  'œ�' => 'æ¹‡',
  'œŽ' => 'æ¹ˆ',
  'œ�' => 'æ¹‰',
  'œ�' => 'æ¹Š',
  'œ‘' => 'æ¹‹',
  'œ’' => 'æ¹Œ',
  'œ“' => 'æ¹�',
  'œ”' => 'æ¹�',
  'œ•' => 'æ¹‘',
  'œ–' => 'æ¹’',
  'œ—' => 'æ¹•',
  'œ˜' => 'æ¹—',
  'œ™' => 'æ¹™',
  'œš' => 'æ¹š',
  'œ›' => 'æ¹œ',
  'œœ' => 'æ¹�',
  'œ�' => 'æ¹ž',
  'œž' => 'æ¹ ',
  'œŸ' => 'æ¹¡',
  'œ ' => 'æ¹¢',
  'œ¡' => 'æ¹£',
  'œ¢' => 'æ¹¤',
  'œ£' => 'æ¹¥',
  'œ¤' => 'æ¹¦',
  'œ¥' => 'æ¹§',
  'œ¦' => 'æ¹¨',
  'œ§' => 'æ¹©',
  'œ¨' => 'æ¹ª',
  'œ©' => 'æ¹¬',
  'œª' => 'æ¹­',
  'œ«' => 'æ¹¯',
  'œ¬' => 'æ¹°',
  'œ­' => 'æ¹±',
  'œ®' => 'æ¹²',
  'œ¯' => 'æ¹³',
  'œ°' => 'æ¹´',
  'œ±' => 'æ¹µ',
  'œ²' => 'æ¹¶',
  'œ³' => 'æ¹·',
  'œ´' => 'æ¹¸',
  'œµ' => 'æ¹¹',
  'œ¶' => 'æ¹º',
  'œ·' => 'æ¹»',
  'œ¸' => 'æ¹¼',
  'œ¹' => 'æ¹½',
  'œº' => 'æº€',
  'œ»' => 'æº�',
  'œ¼' => 'æº‚',
  'œ½' => 'æº„',
  'œ¾' => 'æº‡',
  'œ¿' => 'æºˆ',
  'œÀ' => 'æºŠ',
  'œÁ' => 'æº‹',
  'œÂ' => 'æºŒ',
  'œÃ' => 'æº�',
  'œÄ' => 'æºŽ',
  'œÅ' => 'æº‘',
  'œÆ' => 'æº’',
  'œÇ' => 'æº“',
  'œÈ' => 'æº”',
  'œÉ' => 'æº•',
  'œÊ' => 'æº–',
  'œË' => 'æº—',
  'œÌ' => 'æº™',
  'œÍ' => 'æºš',
  'œÎ' => 'æº›',
  'œÏ' => 'æº�',
  'œÐ' => 'æºž',
  'œÑ' => 'æº ',
  'œÒ' => 'æº¡',
  'œÓ' => 'æº£',
  'œÔ' => 'æº¤',
  'œÕ' => 'æº¦',
  'œÖ' => 'æº¨',
  'œ×' => 'æº©',
  'œØ' => 'æº«',
  'œÙ' => 'æº¬',
  'œÚ' => 'æº­',
  'œÛ' => 'æº®',
  'œÜ' => 'æº°',
  'œÝ' => 'æº³',
  'œÞ' => 'æºµ',
  'œß' => 'æº¸',
  'œà' => 'æº¹',
  'œá' => 'æº¼',
  'œâ' => 'æº¾',
  'œã' => 'æº¿',
  'œä' => 'æ»€',
  'œå' => 'æ»ƒ',
  'œæ' => 'æ»„',
  'œç' => 'æ»…',
  'œè' => 'æ»†',
  'œé' => 'æ»ˆ',
  'œê' => 'æ»‰',
  'œë' => 'æ»Š',
  'œì' => 'æ»Œ',
  'œí' => 'æ»�',
  'œî' => 'æ»Ž',
  'œï' => 'æ»�',
  'œð' => 'æ»’',
  'œñ' => 'æ»–',
  'œò' => 'æ»˜',
  'œó' => 'æ»™',
  'œô' => 'æ»›',
  'œõ' => 'æ»œ',
  'œö' => 'æ»�',
  'œ÷' => 'æ»£',
  'œø' => 'æ»§',
  'œù' => 'æ»ª',
  'œú' => 'æ»«',
  'œû' => 'æ»¬',
  'œü' => 'æ»­',
  'œý' => 'æ»®',
  'œþ' => 'æ»¯',
  '�@' => 'æ»°',
  '�A' => 'æ»±',
  '�B' => 'æ»²',
  '�C' => 'æ»³',
  '�D' => 'æ»µ',
  '�E' => 'æ»¶',
  '�F' => 'æ»·',
  '�G' => 'æ»¸',
  '�H' => 'æ»º',
  '�I' => 'æ»»',
  '�J' => 'æ»¼',
  '�K' => 'æ»½',
  '�L' => 'æ»¾',
  '�M' => 'æ»¿',
  '�N' => 'æ¼€',
  '�O' => 'æ¼�',
  '�P' => 'æ¼ƒ',
  '�Q' => 'æ¼„',
  '�R' => 'æ¼…',
  '�S' => 'æ¼‡',
  '�T' => 'æ¼ˆ',
  '�U' => 'æ¼Š',
  '�V' => 'æ¼‹',
  '�W' => 'æ¼Œ',
  '�X' => 'æ¼�',
  '�Y' => 'æ¼Ž',
  '�Z' => 'æ¼�',
  '�[' => 'æ¼‘',
  '�\\' => 'æ¼’',
  '�]' => 'æ¼–',
  '�^' => 'æ¼—',
  '�_' => 'æ¼˜',
  '�`' => 'æ¼™',
  '�a' => 'æ¼š',
  '�b' => 'æ¼›',
  '�c' => 'æ¼œ',
  '�d' => 'æ¼�',
  '�e' => 'æ¼ž',
  '�f' => 'æ¼Ÿ',
  '�g' => 'æ¼¡',
  '�h' => 'æ¼¢',
  '�i' => 'æ¼£',
  '�j' => 'æ¼¥',
  '�k' => 'æ¼¦',
  '�l' => 'æ¼§',
  '�m' => 'æ¼¨',
  '�n' => 'æ¼¬',
  '�o' => 'æ¼®',
  '�p' => 'æ¼°',
  '�q' => 'æ¼²',
  '�r' => 'æ¼´',
  '�s' => 'æ¼µ',
  '�t' => 'æ¼·',
  '�u' => 'æ¼¸',
  '�v' => 'æ¼¹',
  '�w' => 'æ¼º',
  '�x' => 'æ¼»',
  '�y' => 'æ¼¼',
  '�z' => 'æ¼½',
  '�{' => 'æ¼¿',
  '�|' => 'æ½€',
  '�}' => 'æ½�',
  '�~' => 'æ½‚',
  '�€' => 'æ½ƒ',
  '��' => 'æ½„',
  '�‚' => 'æ½…',
  '�ƒ' => 'æ½ˆ',
  '�„' => 'æ½‰',
  '�…' => 'æ½Š',
  '�†' => 'æ½Œ',
  '�‡' => 'æ½Ž',
  '�ˆ' => 'æ½�',
  '�‰' => 'æ½�',
  '�Š' => 'æ½‘',
  '�‹' => 'æ½’',
  '�Œ' => 'æ½“',
  '��' => 'æ½”',
  '�Ž' => 'æ½•',
  '��' => 'æ½–',
  '��' => 'æ½—',
  '�‘' => 'æ½™',
  '�’' => 'æ½š',
  '�“' => 'æ½›',
  '�”' => 'æ½�',
  '�•' => 'æ½Ÿ',
  '�–' => 'æ½ ',
  '�—' => 'æ½¡',
  '�˜' => 'æ½£',
  '�™' => 'æ½¤',
  '�š' => 'æ½¥',
  '�›' => 'æ½§',
  '�œ' => 'æ½¨',
  '��' => 'æ½©',
  '�ž' => 'æ½ª',
  '�Ÿ' => 'æ½«',
  '� ' => 'æ½¬',
  '�¡' => 'æ½¯',
  '�¢' => 'æ½°',
  '�£' => 'æ½±',
  '�¤' => 'æ½³',
  '�¥' => 'æ½µ',
  '�¦' => 'æ½¶',
  '�§' => 'æ½·',
  '�¨' => 'æ½¹',
  '�©' => 'æ½»',
  '�ª' => 'æ½½',
  '�«' => 'æ½¾',
  '�¬' => 'æ½¿',
  '�­' => 'æ¾€',
  '�®' => 'æ¾�',
  '�¯' => 'æ¾‚',
  '�°' => 'æ¾ƒ',
  '�±' => 'æ¾…',
  '�²' => 'æ¾†',
  '�³' => 'æ¾‡',
  '�´' => 'æ¾Š',
  '�µ' => 'æ¾‹',
  '�¶' => 'æ¾�',
  '�·' => 'æ¾�',
  '�¸' => 'æ¾‘',
  '�¹' => 'æ¾’',
  '�º' => 'æ¾“',
  '�»' => 'æ¾”',
  '�¼' => 'æ¾•',
  '�½' => 'æ¾–',
  '�¾' => 'æ¾—',
  '�¿' => 'æ¾˜',
  '�À' => 'æ¾™',
  '�Á' => 'æ¾š',
  '�Â' => 'æ¾›',
  '�Ã' => 'æ¾�',
  '�Ä' => 'æ¾ž',
  '�Å' => 'æ¾Ÿ',
  '�Æ' => 'æ¾ ',
  '�Ç' => 'æ¾¢',
  '�È' => 'æ¾£',
  '�É' => 'æ¾¤',
  '�Ê' => 'æ¾¥',
  '�Ë' => 'æ¾¦',
  '�Ì' => 'æ¾¨',
  '�Í' => 'æ¾©',
  '�Î' => 'æ¾ª',
  '�Ï' => 'æ¾«',
  '�Ð' => 'æ¾¬',
  '�Ñ' => 'æ¾­',
  '�Ò' => 'æ¾®',
  '�Ó' => 'æ¾¯',
  '�Ô' => 'æ¾°',
  '�Õ' => 'æ¾±',
  '�Ö' => 'æ¾²',
  '�×' => 'æ¾´',
  '�Ø' => 'æ¾µ',
  '�Ù' => 'æ¾·',
  '�Ú' => 'æ¾¸',
  '�Û' => 'æ¾º',
  '�Ü' => 'æ¾»',
  '�Ý' => 'æ¾¼',
  '�Þ' => 'æ¾½',
  '�ß' => 'æ¾¾',
  '�à' => 'æ¾¿',
  '�á' => 'æ¿�',
  '�â' => 'æ¿ƒ',
  '�ã' => 'æ¿„',
  '�ä' => 'æ¿…',
  '�å' => 'æ¿†',
  '�æ' => 'æ¿‡',
  '�ç' => 'æ¿ˆ',
  '�è' => 'æ¿Š',
  '�é' => 'æ¿‹',
  '�ê' => 'æ¿Œ',
  '�ë' => 'æ¿�',
  '�ì' => 'æ¿Ž',
  '�í' => 'æ¿�',
  '�î' => 'æ¿�',
  '�ï' => 'æ¿“',
  '�ð' => 'æ¿”',
  '�ñ' => 'æ¿•',
  '�ò' => 'æ¿–',
  '�ó' => 'æ¿—',
  '�ô' => 'æ¿˜',
  '�õ' => 'æ¿™',
  '�ö' => 'æ¿š',
  '�÷' => 'æ¿›',
  '�ø' => 'æ¿œ',
  '�ù' => 'æ¿�',
  '�ú' => 'æ¿Ÿ',
  '�û' => 'æ¿¢',
  '�ü' => 'æ¿£',
  '�ý' => 'æ¿¤',
  '�þ' => 'æ¿¥',
  'ž@' => 'æ¿¦',
  'žA' => 'æ¿§',
  'žB' => 'æ¿¨',
  'žC' => 'æ¿©',
  'žD' => 'æ¿ª',
  'žE' => 'æ¿«',
  'žF' => 'æ¿¬',
  'žG' => 'æ¿­',
  'žH' => 'æ¿°',
  'žI' => 'æ¿±',
  'žJ' => 'æ¿²',
  'žK' => 'æ¿³',
  'žL' => 'æ¿´',
  'žM' => 'æ¿µ',
  'žN' => 'æ¿¶',
  'žO' => 'æ¿·',
  'žP' => 'æ¿¸',
  'žQ' => 'æ¿¹',
  'žR' => 'æ¿º',
  'žS' => 'æ¿»',
  'žT' => 'æ¿¼',
  'žU' => 'æ¿½',
  'žV' => 'æ¿¾',
  'žW' => 'æ¿¿',
  'žX' => 'ç€€',
  'žY' => 'ç€�',
  'žZ' => 'ç€‚',
  'ž[' => 'ç€ƒ',
  'ž\\' => 'ç€„',
  'ž]' => 'ç€…',
  'ž^' => 'ç€†',
  'ž_' => 'ç€‡',
  'ž`' => 'ç€ˆ',
  'ža' => 'ç€‰',
  'žb' => 'ç€Š',
  'žc' => 'ç€‹',
  'žd' => 'ç€Œ',
  'že' => 'ç€�',
  'žf' => 'ç€Ž',
  'žg' => 'ç€�',
  'žh' => 'ç€�',
  'ži' => 'ç€’',
  'žj' => 'ç€“',
  'žk' => 'ç€”',
  'žl' => 'ç€•',
  'žm' => 'ç€–',
  'žn' => 'ç€—',
  'žo' => 'ç€˜',
  'žp' => 'ç€™',
  'žq' => 'ç€œ',
  'žr' => 'ç€�',
  'žs' => 'ç€ž',
  'žt' => 'ç€Ÿ',
  'žu' => 'ç€ ',
  'žv' => 'ç€¡',
  'žw' => 'ç€¢',
  'žx' => 'ç€¤',
  'žy' => 'ç€¥',
  'žz' => 'ç€¦',
  'ž{' => 'ç€§',
  'ž|' => 'ç€¨',
  'ž}' => 'ç€©',
  'ž~' => 'ç€ª',
  'ž€' => 'ç€«',
  'ž�' => 'ç€¬',
  'ž‚' => 'ç€­',
  'žƒ' => 'ç€®',
  'ž„' => 'ç€¯',
  'ž…' => 'ç€°',
  'ž†' => 'ç€±',
  'ž‡' => 'ç€²',
  'žˆ' => 'ç€³',
  'ž‰' => 'ç€´',
  'žŠ' => 'ç€¶',
  'ž‹' => 'ç€·',
  'žŒ' => 'ç€¸',
  'ž�' => 'ç€º',
  'žŽ' => 'ç€»',
  'ž�' => 'ç€¼',
  'ž�' => 'ç€½',
  'ž‘' => 'ç€¾',
  'ž’' => 'ç€¿',
  'ž“' => 'ç�€',
  'ž”' => 'ç��',
  'ž•' => 'ç�‚',
  'ž–' => 'ç�ƒ',
  'ž—' => 'ç�„',
  'ž˜' => 'ç�…',
  'ž™' => 'ç�†',
  'žš' => 'ç�‡',
  'ž›' => 'ç�ˆ',
  'žœ' => 'ç�‰',
  'ž�' => 'ç�Š',
  'žž' => 'ç�‹',
  'žŸ' => 'ç��',
  'ž ' => 'ç�Ž',
  'ž¡' => 'ç��',
  'ž¢' => 'ç�‘',
  'ž£' => 'ç�’',
  'ž¤' => 'ç�“',
  'ž¥' => 'ç�”',
  'ž¦' => 'ç�•',
  'ž§' => 'ç�–',
  'ž¨' => 'ç�—',
  'ž©' => 'ç�˜',
  'žª' => 'ç�™',
  'ž«' => 'ç�š',
  'ž¬' => 'ç�›',
  'ž­' => 'ç�œ',
  'ž®' => 'ç��',
  'ž¯' => 'ç�Ÿ',
  'ž°' => 'ç� ',
  'ž±' => 'ç�¡',
  'ž²' => 'ç�¢',
  'ž³' => 'ç�£',
  'ž´' => 'ç�¤',
  'žµ' => 'ç�¥',
  'ž¶' => 'ç�¦',
  'ž·' => 'ç�§',
  'ž¸' => 'ç�¨',
  'ž¹' => 'ç�©',
  'žº' => 'ç�ª',
  'ž»' => 'ç�®',
  'ž¼' => 'ç�±',
  'ž½' => 'ç�²',
  'ž¾' => 'ç�³',
  'ž¿' => 'ç�´',
  'žÀ' => 'ç�·',
  'žÁ' => 'ç�¹',
  'žÂ' => 'ç�º',
  'žÃ' => 'ç�»',
  'žÄ' => 'ç�½',
  'žÅ' => 'ç‚�',
  'žÆ' => 'ç‚‚',
  'žÇ' => 'ç‚ƒ',
  'žÈ' => 'ç‚„',
  'žÉ' => 'ç‚†',
  'žÊ' => 'ç‚‡',
  'žË' => 'ç‚ˆ',
  'žÌ' => 'ç‚‹',
  'žÍ' => 'ç‚Œ',
  'žÎ' => 'ç‚�',
  'žÏ' => 'ç‚�',
  'žÐ' => 'ç‚�',
  'žÑ' => 'ç‚‘',
  'žÒ' => 'ç‚“',
  'žÓ' => 'ç‚—',
  'žÔ' => 'ç‚˜',
  'žÕ' => 'ç‚š',
  'žÖ' => 'ç‚›',
  'ž×' => 'ç‚ž',
  'žØ' => 'ç‚Ÿ',
  'žÙ' => 'ç‚ ',
  'žÚ' => 'ç‚¡',
  'žÛ' => 'ç‚¢',
  'žÜ' => 'ç‚£',
  'žÝ' => 'ç‚¤',
  'žÞ' => 'ç‚¥',
  'žß' => 'ç‚¦',
  'žà' => 'ç‚§',
  'žá' => 'ç‚¨',
  'žâ' => 'ç‚©',
  'žã' => 'ç‚ª',
  'žä' => 'ç‚°',
  'žå' => 'ç‚²',
  'žæ' => 'ç‚´',
  'žç' => 'ç‚µ',
  'žè' => 'ç‚¶',
  'žé' => 'ç‚º',
  'žê' => 'ç‚¾',
  'žë' => 'ç‚¿',
  'žì' => 'çƒ„',
  'ží' => 'çƒ…',
  'žî' => 'çƒ†',
  'žï' => 'çƒ‡',
  'žð' => 'çƒ‰',
  'žñ' => 'çƒ‹',
  'žò' => 'çƒŒ',
  'žó' => 'çƒ�',
  'žô' => 'çƒŽ',
  'žõ' => 'çƒ�',
  'žö' => 'çƒ�',
  'ž÷' => 'çƒ‘',
  'žø' => 'çƒ’',
  'žù' => 'çƒ“',
  'žú' => 'çƒ”',
  'žû' => 'çƒ•',
  'žü' => 'çƒ–',
  'žý' => 'çƒ—',
  'žþ' => 'çƒš',
  'Ÿ@' => 'çƒœ',
  'ŸA' => 'çƒ�',
  'ŸB' => 'çƒž',
  'ŸC' => 'çƒ ',
  'ŸD' => 'çƒ¡',
  'ŸE' => 'çƒ¢',
  'ŸF' => 'çƒ£',
  'ŸG' => 'çƒ¥',
  'ŸH' => 'çƒª',
  'ŸI' => 'çƒ®',
  'ŸJ' => 'çƒ°',
  'ŸK' => 'çƒ±',
  'ŸL' => 'çƒ²',
  'ŸM' => 'çƒ³',
  'ŸN' => 'çƒ´',
  'ŸO' => 'çƒµ',
  'ŸP' => 'çƒ¶',
  'ŸQ' => 'çƒ¸',
  'ŸR' => 'çƒº',
  'ŸS' => 'çƒ»',
  'ŸT' => 'çƒ¼',
  'ŸU' => 'çƒ¾',
  'ŸV' => 'çƒ¿',
  'ŸW' => 'ç„€',
  'ŸX' => 'ç„�',
  'ŸY' => 'ç„‚',
  'ŸZ' => 'ç„ƒ',
  'Ÿ[' => 'ç„„',
  'Ÿ\\' => 'ç„…',
  'Ÿ]' => 'ç„†',
  'Ÿ^' => 'ç„‡',
  'Ÿ_' => 'ç„ˆ',
  'Ÿ`' => 'ç„‹',
  'Ÿa' => 'ç„Œ',
  'Ÿb' => 'ç„�',
  'Ÿc' => 'ç„Ž',
  'Ÿd' => 'ç„�',
  'Ÿe' => 'ç„‘',
  'Ÿf' => 'ç„’',
  'Ÿg' => 'ç„”',
  'Ÿh' => 'ç„—',
  'Ÿi' => 'ç„›',
  'Ÿj' => 'ç„œ',
  'Ÿk' => 'ç„�',
  'Ÿl' => 'ç„ž',
  'Ÿm' => 'ç„Ÿ',
  'Ÿn' => 'ç„ ',
  'Ÿo' => 'ç„¡',
  'Ÿp' => 'ç„¢',
  'Ÿq' => 'ç„£',
  'Ÿr' => 'ç„¤',
  'Ÿs' => 'ç„¥',
  'Ÿt' => 'ç„§',
  'Ÿu' => 'ç„¨',
  'Ÿv' => 'ç„©',
  'Ÿw' => 'ç„ª',
  'Ÿx' => 'ç„«',
  'Ÿy' => 'ç„¬',
  'Ÿz' => 'ç„­',
  'Ÿ{' => 'ç„®',
  'Ÿ|' => 'ç„²',
  'Ÿ}' => 'ç„³',
  'Ÿ~' => 'ç„´',
  'Ÿ€' => 'ç„µ',
  'Ÿ�' => 'ç„·',
  'Ÿ‚' => 'ç„¸',
  'Ÿƒ' => 'ç„¹',
  'Ÿ„' => 'ç„º',
  'Ÿ…' => 'ç„»',
  'Ÿ†' => 'ç„¼',
  'Ÿ‡' => 'ç„½',
  'Ÿˆ' => 'ç„¾',
  'Ÿ‰' => 'ç„¿',
  'ŸŠ' => 'ç…€',
  'Ÿ‹' => 'ç…�',
  'ŸŒ' => 'ç…‚',
  'Ÿ�' => 'ç…ƒ',
  'ŸŽ' => 'ç…„',
  'Ÿ�' => 'ç…†',
  'Ÿ�' => 'ç…‡',
  'Ÿ‘' => 'ç…ˆ',
  'Ÿ’' => 'ç…‰',
  'Ÿ“' => 'ç…‹',
  'Ÿ”' => 'ç…�',
  'Ÿ•' => 'ç…�',
  'Ÿ–' => 'ç…�',
  'Ÿ—' => 'ç…‘',
  'Ÿ˜' => 'ç…’',
  'Ÿ™' => 'ç…“',
  'Ÿš' => 'ç…”',
  'Ÿ›' => 'ç…•',
  'Ÿœ' => 'ç…–',
  'Ÿ�' => 'ç…—',
  'Ÿž' => 'ç…˜',
  'ŸŸ' => 'ç…™',
  'Ÿ ' => 'ç…š',
  'Ÿ¡' => 'ç…›',
  'Ÿ¢' => 'ç…�',
  'Ÿ£' => 'ç…Ÿ',
  'Ÿ¤' => 'ç… ',
  'Ÿ¥' => 'ç…¡',
  'Ÿ¦' => 'ç…¢',
  'Ÿ§' => 'ç…£',
  'Ÿ¨' => 'ç…¥',
  'Ÿ©' => 'ç…©',
  'Ÿª' => 'ç…ª',
  'Ÿ«' => 'ç…«',
  'Ÿ¬' => 'ç…¬',
  'Ÿ­' => 'ç…­',
  'Ÿ®' => 'ç…¯',
  'Ÿ¯' => 'ç…°',
  'Ÿ°' => 'ç…±',
  'Ÿ±' => 'ç…´',
  'Ÿ²' => 'ç…µ',
  'Ÿ³' => 'ç…¶',
  'Ÿ´' => 'ç…·',
  'Ÿµ' => 'ç…¹',
  'Ÿ¶' => 'ç…»',
  'Ÿ·' => 'ç…¼',
  'Ÿ¸' => 'ç…¾',
  'Ÿ¹' => 'ç…¿',
  'Ÿº' => 'ç†€',
  'Ÿ»' => 'ç†�',
  'Ÿ¼' => 'ç†‚',
  'Ÿ½' => 'ç†ƒ',
  'Ÿ¾' => 'ç†…',
  'Ÿ¿' => 'ç††',
  'ŸÀ' => 'ç†‡',
  'ŸÁ' => 'ç†ˆ',
  'ŸÂ' => 'ç†‰',
  'ŸÃ' => 'ç†‹',
  'ŸÄ' => 'ç†Œ',
  'ŸÅ' => 'ç†�',
  'ŸÆ' => 'ç†Ž',
  'ŸÇ' => 'ç†�',
  'ŸÈ' => 'ç†‘',
  'ŸÉ' => 'ç†’',
  'ŸÊ' => 'ç†“',
  'ŸË' => 'ç†•',
  'ŸÌ' => 'ç†–',
  'ŸÍ' => 'ç†—',
  'ŸÎ' => 'ç†š',
  'ŸÏ' => 'ç†›',
  'ŸÐ' => 'ç†œ',
  'ŸÑ' => 'ç†�',
  'ŸÒ' => 'ç†ž',
  'ŸÓ' => 'ç†¡',
  'ŸÔ' => 'ç†¢',
  'ŸÕ' => 'ç†£',
  'ŸÖ' => 'ç†¤',
  'Ÿ×' => 'ç†¥',
  'ŸØ' => 'ç†¦',
  'ŸÙ' => 'ç†§',
  'ŸÚ' => 'ç†©',
  'ŸÛ' => 'ç†ª',
  'ŸÜ' => 'ç†«',
  'ŸÝ' => 'ç†­',
  'ŸÞ' => 'ç†®',
  'Ÿß' => 'ç†¯',
  'Ÿà' => 'ç†°',
  'Ÿá' => 'ç†±',
  'Ÿâ' => 'ç†²',
  'Ÿã' => 'ç†´',
  'Ÿä' => 'ç†¶',
  'Ÿå' => 'ç†·',
  'Ÿæ' => 'ç†¸',
  'Ÿç' => 'ç†º',
  'Ÿè' => 'ç†»',
  'Ÿé' => 'ç†¼',
  'Ÿê' => 'ç†½',
  'Ÿë' => 'ç†¾',
  'Ÿì' => 'ç†¿',
  'Ÿí' => 'ç‡€',
  'Ÿî' => 'ç‡�',
  'Ÿï' => 'ç‡‚',
  'Ÿð' => 'ç‡„',
  'Ÿñ' => 'ç‡…',
  'Ÿò' => 'ç‡†',
  'Ÿó' => 'ç‡‡',
  'Ÿô' => 'ç‡ˆ',
  'Ÿõ' => 'ç‡‰',
  'Ÿö' => 'ç‡Š',
  'Ÿ÷' => 'ç‡‹',
  'Ÿø' => 'ç‡Œ',
  'Ÿù' => 'ç‡�',
  'Ÿú' => 'ç‡�',
  'Ÿû' => 'ç‡�',
  'Ÿü' => 'ç‡‘',
  'Ÿý' => 'ç‡’',
  'Ÿþ' => 'ç‡“',
  ' @' => 'ç‡–',
  ' A' => 'ç‡—',
  ' B' => 'ç‡˜',
  ' C' => 'ç‡™',
  ' D' => 'ç‡š',
  ' E' => 'ç‡›',
  ' F' => 'ç‡œ',
  ' G' => 'ç‡�',
  ' H' => 'ç‡ž',
  ' I' => 'ç‡Ÿ',
  ' J' => 'ç‡¡',
  ' K' => 'ç‡¢',
  ' L' => 'ç‡£',
  ' M' => 'ç‡¤',
  ' N' => 'ç‡¦',
  ' O' => 'ç‡¨',
  ' P' => 'ç‡©',
  ' Q' => 'ç‡ª',
  ' R' => 'ç‡«',
  ' S' => 'ç‡¬',
  ' T' => 'ç‡­',
  ' U' => 'ç‡¯',
  ' V' => 'ç‡°',
  ' W' => 'ç‡±',
  ' X' => 'ç‡²',
  ' Y' => 'ç‡³',
  ' Z' => 'ç‡´',
  ' [' => 'ç‡µ',
  ' \\' => 'ç‡¶',
  ' ]' => 'ç‡·',
  ' ^' => 'ç‡¸',
  ' _' => 'ç‡º',
  ' `' => 'ç‡»',
  ' a' => 'ç‡¼',
  ' b' => 'ç‡½',
  ' c' => 'ç‡¾',
  ' d' => 'ç‡¿',
  ' e' => 'çˆ€',
  ' f' => 'çˆ�',
  ' g' => 'çˆ‚',
  ' h' => 'çˆƒ',
  ' i' => 'çˆ„',
  ' j' => 'çˆ…',
  ' k' => 'çˆ‡',
  ' l' => 'çˆˆ',
  ' m' => 'çˆ‰',
  ' n' => 'çˆŠ',
  ' o' => 'çˆ‹',
  ' p' => 'çˆŒ',
  ' q' => 'çˆ�',
  ' r' => 'çˆŽ',
  ' s' => 'çˆ�',
  ' t' => 'çˆ�',
  ' u' => 'çˆ‘',
  ' v' => 'çˆ’',
  ' w' => 'çˆ“',
  ' x' => 'çˆ”',
  ' y' => 'çˆ•',
  ' z' => 'çˆ–',
  ' {' => 'çˆ—',
  ' |' => 'çˆ˜',
  ' }' => 'çˆ™',
  ' ~' => 'çˆš',
  ' €' => 'çˆ›',
  ' �' => 'çˆœ',
  ' ‚' => 'çˆž',
  ' ƒ' => 'çˆŸ',
  ' „' => 'çˆ ',
  ' …' => 'çˆ¡',
  ' †' => 'çˆ¢',
  ' ‡' => 'çˆ£',
  ' ˆ' => 'çˆ¤',
  ' ‰' => 'çˆ¥',
  ' Š' => 'çˆ¦',
  ' ‹' => 'çˆ§',
  ' Œ' => 'çˆ©',
  ' �' => 'çˆ«',
  ' Ž' => 'çˆ­',
  ' �' => 'çˆ®',
  ' �' => 'çˆ¯',
  ' ‘' => 'çˆ²',
  ' ’' => 'çˆ³',
  ' “' => 'çˆ´',
  ' ”' => 'çˆº',
  ' •' => 'çˆ¼',
  ' –' => 'çˆ¾',
  ' —' => 'ç‰€',
  ' ˜' => 'ç‰�',
  ' ™' => 'ç‰‚',
  ' š' => 'ç‰ƒ',
  ' ›' => 'ç‰„',
  ' œ' => 'ç‰…',
  ' �' => 'ç‰†',
  ' ž' => 'ç‰‰',
  ' Ÿ' => 'ç‰Š',
  '  ' => 'ç‰‹',
  ' ¡' => 'ç‰Ž',
  ' ¢' => 'ç‰�',
  ' £' => 'ç‰�',
  ' ¤' => 'ç‰‘',
  ' ¥' => 'ç‰“',
  ' ¦' => 'ç‰”',
  ' §' => 'ç‰•',
  ' ¨' => 'ç‰—',
  ' ©' => 'ç‰˜',
  ' ª' => 'ç‰š',
  ' «' => 'ç‰œ',
  ' ¬' => 'ç‰ž',
  ' ­' => 'ç‰ ',
  ' ®' => 'ç‰£',
  ' ¯' => 'ç‰¤',
  ' °' => 'ç‰¥',
  ' ±' => 'ç‰¨',
  ' ²' => 'ç‰ª',
  ' ³' => 'ç‰«',
  ' ´' => 'ç‰¬',
  ' µ' => 'ç‰­',
  ' ¶' => 'ç‰°',
  ' ·' => 'ç‰±',
  ' ¸' => 'ç‰³',
  ' ¹' => 'ç‰´',
  ' º' => 'ç‰¶',
  ' »' => 'ç‰·',
  ' ¼' => 'ç‰¸',
  ' ½' => 'ç‰»',
  ' ¾' => 'ç‰¼',
  ' ¿' => 'ç‰½',
  ' À' => 'çŠ‚',
  ' Á' => 'çŠƒ',
  ' Â' => 'çŠ…',
  ' Ã' => 'çŠ†',
  ' Ä' => 'çŠ‡',
  ' Å' => 'çŠˆ',
  ' Æ' => 'çŠ‰',
  ' Ç' => 'çŠŒ',
  ' È' => 'çŠŽ',
  ' É' => 'çŠ�',
  ' Ê' => 'çŠ‘',
  ' Ë' => 'çŠ“',
  ' Ì' => 'çŠ”',
  ' Í' => 'çŠ•',
  ' Î' => 'çŠ–',
  ' Ï' => 'çŠ—',
  ' Ð' => 'çŠ˜',
  ' Ñ' => 'çŠ™',
  ' Ò' => 'çŠš',
  ' Ó' => 'çŠ›',
  ' Ô' => 'çŠœ',
  ' Õ' => 'çŠ�',
  ' Ö' => 'çŠž',
  ' ×' => 'çŠ ',
  ' Ø' => 'çŠ¡',
  ' Ù' => 'çŠ¢',
  ' Ú' => 'çŠ£',
  ' Û' => 'çŠ¤',
  ' Ü' => 'çŠ¥',
  ' Ý' => 'çŠ¦',
  ' Þ' => 'çŠ§',
  ' ß' => 'çŠ¨',
  ' à' => 'çŠ©',
  ' á' => 'çŠª',
  ' â' => 'çŠ«',
  ' ã' => 'çŠ®',
  ' ä' => 'çŠ±',
  ' å' => 'çŠ²',
  ' æ' => 'çŠ³',
  ' ç' => 'çŠµ',
  ' è' => 'çŠº',
  ' é' => 'çŠ»',
  ' ê' => 'çŠ¼',
  ' ë' => 'çŠ½',
  ' ì' => 'çŠ¾',
  ' í' => 'çŠ¿',
  ' î' => 'ç‹€',
  ' ï' => 'ç‹…',
  ' ð' => 'ç‹†',
  ' ñ' => 'ç‹‡',
  ' ò' => 'ç‹‰',
  ' ó' => 'ç‹Š',
  ' ô' => 'ç‹‹',
  ' õ' => 'ç‹Œ',
  ' ö' => 'ç‹�',
  ' ÷' => 'ç‹‘',
  ' ø' => 'ç‹“',
  ' ù' => 'ç‹”',
  ' ú' => 'ç‹•',
  ' û' => 'ç‹–',
  ' ü' => 'ç‹˜',
  ' ý' => 'ç‹š',
  ' þ' => 'ç‹›',
  '¡¡' => 'ã€€',
  '¡¢' => 'ã€�',
  '¡£' => 'ã€‚',
  '¡¤' => 'Â·',
  '¡¥' => 'Ë‰',
  '¡¦' => 'Ë‡',
  '¡§' => 'Â¨',
  '¡¨' => 'ã€ƒ',
  '¡©' => 'ã€…',
  '¡ª' => 'â€”',
  '¡«' => 'ï½ž',
  '¡¬' => 'â€–',
  '¡­' => 'â€¦',
  '¡®' => 'â€˜',
  '¡¯' => 'â€™',
  '¡°' => 'â€œ',
  '¡±' => 'â€�',
  '¡²' => 'ã€”',
  '¡³' => 'ã€•',
  '¡´' => 'ã€ˆ',
  '¡µ' => 'ã€‰',
  '¡¶' => 'ã€Š',
  '¡·' => 'ã€‹',
  '¡¸' => 'ã€Œ',
  '¡¹' => 'ã€�',
  '¡º' => 'ã€Ž',
  '¡»' => 'ã€�',
  '¡¼' => 'ã€–',
  '¡½' => 'ã€—',
  '¡¾' => 'ã€�',
  '¡¿' => 'ã€‘',
  '¡À' => 'Â±',
  '¡Á' => 'Ã—',
  '¡Â' => 'Ã·',
  '¡Ã' => 'âˆ¶',
  '¡Ä' => 'âˆ§',
  '¡Å' => 'âˆ¨',
  '¡Æ' => 'âˆ‘',
  '¡Ç' => 'âˆ�',
  '¡È' => 'âˆª',
  '¡É' => 'âˆ©',
  '¡Ê' => 'âˆˆ',
  '¡Ë' => 'âˆ·',
  '¡Ì' => 'âˆš',
  '¡Í' => 'âŠ¥',
  '¡Î' => 'âˆ¥',
  '¡Ï' => 'âˆ ',
  '¡Ð' => 'âŒ’',
  '¡Ñ' => 'âŠ™',
  '¡Ò' => 'âˆ«',
  '¡Ó' => 'âˆ®',
  '¡Ô' => 'â‰¡',
  '¡Õ' => 'â‰Œ',
  '¡Ö' => 'â‰ˆ',
  '¡×' => 'âˆ½',
  '¡Ø' => 'âˆ�',
  '¡Ù' => 'â‰ ',
  '¡Ú' => 'â‰®',
  '¡Û' => 'â‰¯',
  '¡Ü' => 'â‰¤',
  '¡Ý' => 'â‰¥',
  '¡Þ' => 'âˆž',
  '¡ß' => 'âˆµ',
  '¡à' => 'âˆ´',
  '¡á' => 'â™‚',
  '¡â' => 'â™€',
  '¡ã' => 'Â°',
  '¡ä' => 'â€²',
  '¡å' => 'â€³',
  '¡æ' => 'â„ƒ',
  '¡ç' => 'ï¼„',
  '¡è' => 'Â¤',
  '¡é' => 'ï¿ ',
  '¡ê' => 'ï¿¡',
  '¡ë' => 'â€°',
  '¡ì' => 'Â§',
  '¡í' => 'â„–',
  '¡î' => 'â˜†',
  '¡ï' => 'â˜…',
  '¡ð' => 'â—‹',
  '¡ñ' => 'â—�',
  '¡ò' => 'â—Ž',
  '¡ó' => 'â—‡',
  '¡ô' => 'â—†',
  '¡õ' => 'â–¡',
  '¡ö' => 'â– ',
  '¡÷' => 'â–³',
  '¡ø' => 'â–²',
  '¡ù' => 'â€»',
  '¡ú' => 'â†’',
  '¡û' => 'â†�',
  '¡ü' => 'â†‘',
  '¡ý' => 'â†“',
  '¡þ' => 'ã€“',
  '¢¡' => 'â…°',
  '¢¢' => 'â…±',
  '¢£' => 'â…²',
  '¢¤' => 'â…³',
  '¢¥' => 'â…´',
  '¢¦' => 'â…µ',
  '¢§' => 'â…¶',
  '¢¨' => 'â…·',
  '¢©' => 'â…¸',
  '¢ª' => 'â…¹',
  '¢±' => 'â’ˆ',
  '¢²' => 'â’‰',
  '¢³' => 'â’Š',
  '¢´' => 'â’‹',
  '¢µ' => 'â’Œ',
  '¢¶' => 'â’�',
  '¢·' => 'â’Ž',
  '¢¸' => 'â’�',
  '¢¹' => 'â’�',
  '¢º' => 'â’‘',
  '¢»' => 'â’’',
  '¢¼' => 'â’“',
  '¢½' => 'â’”',
  '¢¾' => 'â’•',
  '¢¿' => 'â’–',
  '¢À' => 'â’—',
  '¢Á' => 'â’˜',
  '¢Â' => 'â’™',
  '¢Ã' => 'â’š',
  '¢Ä' => 'â’›',
  '¢Å' => 'â‘´',
  '¢Æ' => 'â‘µ',
  '¢Ç' => 'â‘¶',
  '¢È' => 'â‘·',
  '¢É' => 'â‘¸',
  '¢Ê' => 'â‘¹',
  '¢Ë' => 'â‘º',
  '¢Ì' => 'â‘»',
  '¢Í' => 'â‘¼',
  '¢Î' => 'â‘½',
  '¢Ï' => 'â‘¾',
  '¢Ð' => 'â‘¿',
  '¢Ñ' => 'â’€',
  '¢Ò' => 'â’�',
  '¢Ó' => 'â’‚',
  '¢Ô' => 'â’ƒ',
  '¢Õ' => 'â’„',
  '¢Ö' => 'â’…',
  '¢×' => 'â’†',
  '¢Ø' => 'â’‡',
  '¢Ù' => 'â‘ ',
  '¢Ú' => 'â‘¡',
  '¢Û' => 'â‘¢',
  '¢Ü' => 'â‘£',
  '¢Ý' => 'â‘¤',
  '¢Þ' => 'â‘¥',
  '¢ß' => 'â‘¦',
  '¢à' => 'â‘§',
  '¢á' => 'â‘¨',
  '¢â' => 'â‘©',
  '¢å' => 'ãˆ ',
  '¢æ' => 'ãˆ¡',
  '¢ç' => 'ãˆ¢',
  '¢è' => 'ãˆ£',
  '¢é' => 'ãˆ¤',
  '¢ê' => 'ãˆ¥',
  '¢ë' => 'ãˆ¦',
  '¢ì' => 'ãˆ§',
  '¢í' => 'ãˆ¨',
  '¢î' => 'ãˆ©',
  '¢ñ' => 'â… ',
  '¢ò' => 'â…¡',
  '¢ó' => 'â…¢',
  '¢ô' => 'â…£',
  '¢õ' => 'â…¤',
  '¢ö' => 'â…¥',
  '¢÷' => 'â…¦',
  '¢ø' => 'â…§',
  '¢ù' => 'â…¨',
  '¢ú' => 'â…©',
  '¢û' => 'â…ª',
  '¢ü' => 'â…«',
  '£¡' => 'ï¼�',
  '£¢' => 'ï¼‚',
  '££' => 'ï¼ƒ',
  '£¤' => 'ï¿¥',
  '£¥' => 'ï¼…',
  '£¦' => 'ï¼†',
  '£§' => 'ï¼‡',
  '£¨' => 'ï¼ˆ',
  '£©' => 'ï¼‰',
  '£ª' => 'ï¼Š',
  '£«' => 'ï¼‹',
  '£¬' => 'ï¼Œ',
  '£­' => 'ï¼�',
  '£®' => 'ï¼Ž',
  '£¯' => 'ï¼�',
  '£°' => 'ï¼�',
  '£±' => 'ï¼‘',
  '£²' => 'ï¼’',
  '£³' => 'ï¼“',
  '£´' => 'ï¼”',
  '£µ' => 'ï¼•',
  '£¶' => 'ï¼–',
  '£·' => 'ï¼—',
  '£¸' => 'ï¼˜',
  '£¹' => 'ï¼™',
  '£º' => 'ï¼š',
  '£»' => 'ï¼›',
  '£¼' => 'ï¼œ',
  '£½' => 'ï¼�',
  '£¾' => 'ï¼ž',
  '£¿' => 'ï¼Ÿ',
  '£À' => 'ï¼ ',
  '£Á' => 'ï¼¡',
  '£Â' => 'ï¼¢',
  '£Ã' => 'ï¼£',
  '£Ä' => 'ï¼¤',
  '£Å' => 'ï¼¥',
  '£Æ' => 'ï¼¦',
  '£Ç' => 'ï¼§',
  '£È' => 'ï¼¨',
  '£É' => 'ï¼©',
  '£Ê' => 'ï¼ª',
  '£Ë' => 'ï¼«',
  '£Ì' => 'ï¼¬',
  '£Í' => 'ï¼­',
  '£Î' => 'ï¼®',
  '£Ï' => 'ï¼¯',
  '£Ð' => 'ï¼°',
  '£Ñ' => 'ï¼±',
  '£Ò' => 'ï¼²',
  '£Ó' => 'ï¼³',
  '£Ô' => 'ï¼´',
  '£Õ' => 'ï¼µ',
  '£Ö' => 'ï¼¶',
  '£×' => 'ï¼·',
  '£Ø' => 'ï¼¸',
  '£Ù' => 'ï¼¹',
  '£Ú' => 'ï¼º',
  '£Û' => 'ï¼»',
  '£Ü' => 'ï¼¼',
  '£Ý' => 'ï¼½',
  '£Þ' => 'ï¼¾',
  '£ß' => 'ï¼¿',
  '£à' => 'ï½€',
  '£á' => 'ï½�',
  '£â' => 'ï½‚',
  '£ã' => 'ï½ƒ',
  '£ä' => 'ï½„',
  '£å' => 'ï½…',
  '£æ' => 'ï½†',
  '£ç' => 'ï½‡',
  '£è' => 'ï½ˆ',
  '£é' => 'ï½‰',
  '£ê' => 'ï½Š',
  '£ë' => 'ï½‹',
  '£ì' => 'ï½Œ',
  '£í' => 'ï½�',
  '£î' => 'ï½Ž',
  '£ï' => 'ï½�',
  '£ð' => 'ï½�',
  '£ñ' => 'ï½‘',
  '£ò' => 'ï½’',
  '£ó' => 'ï½“',
  '£ô' => 'ï½”',
  '£õ' => 'ï½•',
  '£ö' => 'ï½–',
  '£÷' => 'ï½—',
  '£ø' => 'ï½˜',
  '£ù' => 'ï½™',
  '£ú' => 'ï½š',
  '£û' => 'ï½›',
  '£ü' => 'ï½œ',
  '£ý' => 'ï½�',
  '£þ' => 'ï¿£',
  '¤¡' => 'ã��',
  '¤¢' => 'ã�‚',
  '¤£' => 'ã�ƒ',
  '¤¤' => 'ã�„',
  '¤¥' => 'ã�…',
  '¤¦' => 'ã�†',
  '¤§' => 'ã�‡',
  '¤¨' => 'ã�ˆ',
  '¤©' => 'ã�‰',
  '¤ª' => 'ã�Š',
  '¤«' => 'ã�‹',
  '¤¬' => 'ã�Œ',
  '¤­' => 'ã��',
  '¤®' => 'ã�Ž',
  '¤¯' => 'ã��',
  '¤°' => 'ã��',
  '¤±' => 'ã�‘',
  '¤²' => 'ã�’',
  '¤³' => 'ã�“',
  '¤´' => 'ã�”',
  '¤µ' => 'ã�•',
  '¤¶' => 'ã�–',
  '¤·' => 'ã�—',
  '¤¸' => 'ã�˜',
  '¤¹' => 'ã�™',
  '¤º' => 'ã�š',
  '¤»' => 'ã�›',
  '¤¼' => 'ã�œ',
  '¤½' => 'ã��',
  '¤¾' => 'ã�ž',
  '¤¿' => 'ã�Ÿ',
  '¤À' => 'ã� ',
  '¤Á' => 'ã�¡',
  '¤Â' => 'ã�¢',
  '¤Ã' => 'ã�£',
  '¤Ä' => 'ã�¤',
  '¤Å' => 'ã�¥',
  '¤Æ' => 'ã�¦',
  '¤Ç' => 'ã�§',
  '¤È' => 'ã�¨',
  '¤É' => 'ã�©',
  '¤Ê' => 'ã�ª',
  '¤Ë' => 'ã�«',
  '¤Ì' => 'ã�¬',
  '¤Í' => 'ã�­',
  '¤Î' => 'ã�®',
  '¤Ï' => 'ã�¯',
  '¤Ð' => 'ã�°',
  '¤Ñ' => 'ã�±',
  '¤Ò' => 'ã�²',
  '¤Ó' => 'ã�³',
  '¤Ô' => 'ã�´',
  '¤Õ' => 'ã�µ',
  '¤Ö' => 'ã�¶',
  '¤×' => 'ã�·',
  '¤Ø' => 'ã�¸',
  '¤Ù' => 'ã�¹',
  '¤Ú' => 'ã�º',
  '¤Û' => 'ã�»',
  '¤Ü' => 'ã�¼',
  '¤Ý' => 'ã�½',
  '¤Þ' => 'ã�¾',
  '¤ß' => 'ã�¿',
  '¤à' => 'ã‚€',
  '¤á' => 'ã‚�',
  '¤â' => 'ã‚‚',
  '¤ã' => 'ã‚ƒ',
  '¤ä' => 'ã‚„',
  '¤å' => 'ã‚…',
  '¤æ' => 'ã‚†',
  '¤ç' => 'ã‚‡',
  '¤è' => 'ã‚ˆ',
  '¤é' => 'ã‚‰',
  '¤ê' => 'ã‚Š',
  '¤ë' => 'ã‚‹',
  '¤ì' => 'ã‚Œ',
  '¤í' => 'ã‚�',
  '¤î' => 'ã‚Ž',
  '¤ï' => 'ã‚�',
  '¤ð' => 'ã‚�',
  '¤ñ' => 'ã‚‘',
  '¤ò' => 'ã‚’',
  '¤ó' => 'ã‚“',
  '¥¡' => 'ã‚¡',
  '¥¢' => 'ã‚¢',
  '¥£' => 'ã‚£',
  '¥¤' => 'ã‚¤',
  '¥¥' => 'ã‚¥',
  '¥¦' => 'ã‚¦',
  '¥§' => 'ã‚§',
  '¥¨' => 'ã‚¨',
  '¥©' => 'ã‚©',
  '¥ª' => 'ã‚ª',
  '¥«' => 'ã‚«',
  '¥¬' => 'ã‚¬',
  '¥­' => 'ã‚­',
  '¥®' => 'ã‚®',
  '¥¯' => 'ã‚¯',
  '¥°' => 'ã‚°',
  '¥±' => 'ã‚±',
  '¥²' => 'ã‚²',
  '¥³' => 'ã‚³',
  '¥´' => 'ã‚´',
  '¥µ' => 'ã‚µ',
  '¥¶' => 'ã‚¶',
  '¥·' => 'ã‚·',
  '¥¸' => 'ã‚¸',
  '¥¹' => 'ã‚¹',
  '¥º' => 'ã‚º',
  '¥»' => 'ã‚»',
  '¥¼' => 'ã‚¼',
  '¥½' => 'ã‚½',
  '¥¾' => 'ã‚¾',
  '¥¿' => 'ã‚¿',
  '¥À' => 'ãƒ€',
  '¥Á' => 'ãƒ�',
  '¥Â' => 'ãƒ‚',
  '¥Ã' => 'ãƒƒ',
  '¥Ä' => 'ãƒ„',
  '¥Å' => 'ãƒ…',
  '¥Æ' => 'ãƒ†',
  '¥Ç' => 'ãƒ‡',
  '¥È' => 'ãƒˆ',
  '¥É' => 'ãƒ‰',
  '¥Ê' => 'ãƒŠ',
  '¥Ë' => 'ãƒ‹',
  '¥Ì' => 'ãƒŒ',
  '¥Í' => 'ãƒ�',
  '¥Î' => 'ãƒŽ',
  '¥Ï' => 'ãƒ�',
  '¥Ð' => 'ãƒ�',
  '¥Ñ' => 'ãƒ‘',
  '¥Ò' => 'ãƒ’',
  '¥Ó' => 'ãƒ“',
  '¥Ô' => 'ãƒ”',
  '¥Õ' => 'ãƒ•',
  '¥Ö' => 'ãƒ–',
  '¥×' => 'ãƒ—',
  '¥Ø' => 'ãƒ˜',
  '¥Ù' => 'ãƒ™',
  '¥Ú' => 'ãƒš',
  '¥Û' => 'ãƒ›',
  '¥Ü' => 'ãƒœ',
  '¥Ý' => 'ãƒ�',
  '¥Þ' => 'ãƒž',
  '¥ß' => 'ãƒŸ',
  '¥à' => 'ãƒ ',
  '¥á' => 'ãƒ¡',
  '¥â' => 'ãƒ¢',
  '¥ã' => 'ãƒ£',
  '¥ä' => 'ãƒ¤',
  '¥å' => 'ãƒ¥',
  '¥æ' => 'ãƒ¦',
  '¥ç' => 'ãƒ§',
  '¥è' => 'ãƒ¨',
  '¥é' => 'ãƒ©',
  '¥ê' => 'ãƒª',
  '¥ë' => 'ãƒ«',
  '¥ì' => 'ãƒ¬',
  '¥í' => 'ãƒ­',
  '¥î' => 'ãƒ®',
  '¥ï' => 'ãƒ¯',
  '¥ð' => 'ãƒ°',
  '¥ñ' => 'ãƒ±',
  '¥ò' => 'ãƒ²',
  '¥ó' => 'ãƒ³',
  '¥ô' => 'ãƒ´',
  '¥õ' => 'ãƒµ',
  '¥ö' => 'ãƒ¶',
  '¦¡' => 'Î‘',
  '¦¢' => 'Î’',
  '¦£' => 'Î“',
  '¦¤' => 'Î”',
  '¦¥' => 'Î•',
  '¦¦' => 'Î–',
  '¦§' => 'Î—',
  '¦¨' => 'Î˜',
  '¦©' => 'Î™',
  '¦ª' => 'Îš',
  '¦«' => 'Î›',
  '¦¬' => 'Îœ',
  '¦­' => 'Î�',
  '¦®' => 'Îž',
  '¦¯' => 'ÎŸ',
  '¦°' => 'Î ',
  '¦±' => 'Î¡',
  '¦²' => 'Î£',
  '¦³' => 'Î¤',
  '¦´' => 'Î¥',
  '¦µ' => 'Î¦',
  '¦¶' => 'Î§',
  '¦·' => 'Î¨',
  '¦¸' => 'Î©',
  '¦Á' => 'Î±',
  '¦Â' => 'Î²',
  '¦Ã' => 'Î³',
  '¦Ä' => 'Î´',
  '¦Å' => 'Îµ',
  '¦Æ' => 'Î¶',
  '¦Ç' => 'Î·',
  '¦È' => 'Î¸',
  '¦É' => 'Î¹',
  '¦Ê' => 'Îº',
  '¦Ë' => 'Î»',
  '¦Ì' => 'Î¼',
  '¦Í' => 'Î½',
  '¦Î' => 'Î¾',
  '¦Ï' => 'Î¿',
  '¦Ð' => 'Ï€',
  '¦Ñ' => 'Ï�',
  '¦Ò' => 'Ïƒ',
  '¦Ó' => 'Ï„',
  '¦Ô' => 'Ï…',
  '¦Õ' => 'Ï†',
  '¦Ö' => 'Ï‡',
  '¦×' => 'Ïˆ',
  '¦Ø' => 'Ï‰',
  '¦à' => 'ï¸µ',
  '¦á' => 'ï¸¶',
  '¦â' => 'ï¸¹',
  '¦ã' => 'ï¸º',
  '¦ä' => 'ï¸¿',
  '¦å' => 'ï¹€',
  '¦æ' => 'ï¸½',
  '¦ç' => 'ï¸¾',
  '¦è' => 'ï¹�',
  '¦é' => 'ï¹‚',
  '¦ê' => 'ï¹ƒ',
  '¦ë' => 'ï¹„',
  '¦î' => 'ï¸»',
  '¦ï' => 'ï¸¼',
  '¦ð' => 'ï¸·',
  '¦ñ' => 'ï¸¸',
  '¦ò' => 'ï¸±',
  '¦ô' => 'ï¸³',
  '¦õ' => 'ï¸´',
  '§¡' => 'Ð�',
  '§¢' => 'Ð‘',
  '§£' => 'Ð’',
  '§¤' => 'Ð“',
  '§¥' => 'Ð”',
  '§¦' => 'Ð•',
  '§§' => 'Ð�',
  '§¨' => 'Ð–',
  '§©' => 'Ð—',
  '§ª' => 'Ð˜',
  '§«' => 'Ð™',
  '§¬' => 'Ðš',
  '§­' => 'Ð›',
  '§®' => 'Ðœ',
  '§¯' => 'Ð�',
  '§°' => 'Ðž',
  '§±' => 'ÐŸ',
  '§²' => 'Ð ',
  '§³' => 'Ð¡',
  '§´' => 'Ð¢',
  '§µ' => 'Ð£',
  '§¶' => 'Ð¤',
  '§·' => 'Ð¥',
  '§¸' => 'Ð¦',
  '§¹' => 'Ð§',
  '§º' => 'Ð¨',
  '§»' => 'Ð©',
  '§¼' => 'Ðª',
  '§½' => 'Ð«',
  '§¾' => 'Ð¬',
  '§¿' => 'Ð­',
  '§À' => 'Ð®',
  '§Á' => 'Ð¯',
  '§Ñ' => 'Ð°',
  '§Ò' => 'Ð±',
  '§Ó' => 'Ð²',
  '§Ô' => 'Ð³',
  '§Õ' => 'Ð´',
  '§Ö' => 'Ðµ',
  '§×' => 'Ñ‘',
  '§Ø' => 'Ð¶',
  '§Ù' => 'Ð·',
  '§Ú' => 'Ð¸',
  '§Û' => 'Ð¹',
  '§Ü' => 'Ðº',
  '§Ý' => 'Ð»',
  '§Þ' => 'Ð¼',
  '§ß' => 'Ð½',
  '§à' => 'Ð¾',
  '§á' => 'Ð¿',
  '§â' => 'Ñ€',
  '§ã' => 'Ñ�',
  '§ä' => 'Ñ‚',
  '§å' => 'Ñƒ',
  '§æ' => 'Ñ„',
  '§ç' => 'Ñ…',
  '§è' => 'Ñ†',
  '§é' => 'Ñ‡',
  '§ê' => 'Ñˆ',
  '§ë' => 'Ñ‰',
  '§ì' => 'ÑŠ',
  '§í' => 'Ñ‹',
  '§î' => 'ÑŒ',
  '§ï' => 'Ñ�',
  '§ð' => 'ÑŽ',
  '§ñ' => 'Ñ�',
  '¨@' => 'ËŠ',
  '¨A' => 'Ë‹',
  '¨B' => 'Ë™',
  '¨C' => 'â€“',
  '¨D' => 'â€•',
  '¨E' => 'â€¥',
  '¨F' => 'â€µ',
  '¨G' => 'â„…',
  '¨H' => 'â„‰',
  '¨I' => 'â†–',
  '¨J' => 'â†—',
  '¨K' => 'â†˜',
  '¨L' => 'â†™',
  '¨M' => 'âˆ•',
  '¨N' => 'âˆŸ',
  '¨O' => 'âˆ£',
  '¨P' => 'â‰’',
  '¨Q' => 'â‰¦',
  '¨R' => 'â‰§',
  '¨S' => 'âŠ¿',
  '¨T' => 'â•�',
  '¨U' => 'â•‘',
  '¨V' => 'â•’',
  '¨W' => 'â•“',
  '¨X' => 'â•”',
  '¨Y' => 'â••',
  '¨Z' => 'â•–',
  '¨[' => 'â•—',
  '¨\\' => 'â•˜',
  '¨]' => 'â•™',
  '¨^' => 'â•š',
  '¨_' => 'â•›',
  '¨`' => 'â•œ',
  '¨a' => 'â•�',
  '¨b' => 'â•ž',
  '¨c' => 'â•Ÿ',
  '¨d' => 'â• ',
  '¨e' => 'â•¡',
  '¨f' => 'â•¢',
  '¨g' => 'â•£',
  '¨h' => 'â•¤',
  '¨i' => 'â•¥',
  '¨j' => 'â•¦',
  '¨k' => 'â•§',
  '¨l' => 'â•¨',
  '¨m' => 'â•©',
  '¨n' => 'â•ª',
  '¨o' => 'â•«',
  '¨p' => 'â•¬',
  '¨q' => 'â•­',
  '¨r' => 'â•®',
  '¨s' => 'â•¯',
  '¨t' => 'â•°',
  '¨u' => 'â•±',
  '¨v' => 'â•²',
  '¨w' => 'â•³',
  '¨x' => 'â–�',
  '¨y' => 'â–‚',
  '¨z' => 'â–ƒ',
  '¨{' => 'â–„',
  '¨|' => 'â–…',
  '¨}' => 'â–†',
  '¨~' => 'â–‡',
  '¨€' => 'â–ˆ',
  '¨�' => 'â–‰',
  '¨‚' => 'â–Š',
  '¨ƒ' => 'â–‹',
  '¨„' => 'â–Œ',
  '¨…' => 'â–�',
  '¨†' => 'â–Ž',
  '¨‡' => 'â–�',
  '¨ˆ' => 'â–“',
  '¨‰' => 'â–”',
  '¨Š' => 'â–•',
  '¨‹' => 'â–¼',
  '¨Œ' => 'â–½',
  '¨�' => 'â—¢',
  '¨Ž' => 'â—£',
  '¨�' => 'â—¤',
  '¨�' => 'â—¥',
  '¨‘' => 'â˜‰',
  '¨’' => 'âŠ•',
  '¨“' => 'ã€’',
  '¨”' => 'ã€�',
  '¨•' => 'ã€ž',
  '¨¡' => 'Ä�',
  '¨¢' => 'Ã¡',
  '¨£' => 'ÇŽ',
  '¨¤' => 'Ã ',
  '¨¥' => 'Ä“',
  '¨¦' => 'Ã©',
  '¨§' => 'Ä›',
  '¨¨' => 'Ã¨',
  '¨©' => 'Ä«',
  '¨ª' => 'Ã­',
  '¨«' => 'Ç�',
  '¨¬' => 'Ã¬',
  '¨­' => 'Å�',
  '¨®' => 'Ã³',
  '¨¯' => 'Ç’',
  '¨°' => 'Ã²',
  '¨±' => 'Å«',
  '¨²' => 'Ãº',
  '¨³' => 'Ç”',
  '¨´' => 'Ã¹',
  '¨µ' => 'Ç–',
  '¨¶' => 'Ç˜',
  '¨·' => 'Çš',
  '¨¸' => 'Çœ',
  '¨¹' => 'Ã¼',
  '¨º' => 'Ãª',
  '¨»' => 'É‘',
  '¨½' => 'Å„',
  '¨¾' => 'Åˆ',
  '¨À' => 'É¡',
  '¨Å' => 'ã„…',
  '¨Æ' => 'ã„†',
  '¨Ç' => 'ã„‡',
  '¨È' => 'ã„ˆ',
  '¨É' => 'ã„‰',
  '¨Ê' => 'ã„Š',
  '¨Ë' => 'ã„‹',
  '¨Ì' => 'ã„Œ',
  '¨Í' => 'ã„�',
  '¨Î' => 'ã„Ž',
  '¨Ï' => 'ã„�',
  '¨Ð' => 'ã„�',
  '¨Ñ' => 'ã„‘',
  '¨Ò' => 'ã„’',
  '¨Ó' => 'ã„“',
  '¨Ô' => 'ã„”',
  '¨Õ' => 'ã„•',
  '¨Ö' => 'ã„–',
  '¨×' => 'ã„—',
  '¨Ø' => 'ã„˜',
  '¨Ù' => 'ã„™',
  '¨Ú' => 'ã„š',
  '¨Û' => 'ã„›',
  '¨Ü' => 'ã„œ',
  '¨Ý' => 'ã„�',
  '¨Þ' => 'ã„ž',
  '¨ß' => 'ã„Ÿ',
  '¨à' => 'ã„ ',
  '¨á' => 'ã„¡',
  '¨â' => 'ã„¢',
  '¨ã' => 'ã„£',
  '¨ä' => 'ã„¤',
  '¨å' => 'ã„¥',
  '¨æ' => 'ã„¦',
  '¨ç' => 'ã„§',
  '¨è' => 'ã„¨',
  '¨é' => 'ã„©',
  '©@' => 'ã€¡',
  '©A' => 'ã€¢',
  '©B' => 'ã€£',
  '©C' => 'ã€¤',
  '©D' => 'ã€¥',
  '©E' => 'ã€¦',
  '©F' => 'ã€§',
  '©G' => 'ã€¨',
  '©H' => 'ã€©',
  '©I' => 'ãŠ£',
  '©J' => 'ãŽŽ',
  '©K' => 'ãŽ�',
  '©L' => 'ãŽœ',
  '©M' => 'ãŽ�',
  '©N' => 'ãŽž',
  '©O' => 'ãŽ¡',
  '©P' => 'ã�„',
  '©Q' => 'ã�Ž',
  '©R' => 'ã�‘',
  '©S' => 'ã�’',
  '©T' => 'ã�•',
  '©U' => 'ï¸°',
  '©V' => 'ï¿¢',
  '©W' => 'ï¿¤',
  '©Y' => 'â„¡',
  '©Z' => 'ãˆ±',
  '©\\' => 'â€�',
  '©`' => 'ãƒ¼',
  '©a' => 'ã‚›',
  '©b' => 'ã‚œ',
  '©c' => 'ãƒ½',
  '©d' => 'ãƒ¾',
  '©e' => 'ã€†',
  '©f' => 'ã‚�',
  '©g' => 'ã‚ž',
  '©h' => 'ï¹‰',
  '©i' => 'ï¹Š',
  '©j' => 'ï¹‹',
  '©k' => 'ï¹Œ',
  '©l' => 'ï¹�',
  '©m' => 'ï¹Ž',
  '©n' => 'ï¹�',
  '©o' => 'ï¹�',
  '©p' => 'ï¹‘',
  '©q' => 'ï¹’',
  '©r' => 'ï¹”',
  '©s' => 'ï¹•',
  '©t' => 'ï¹–',
  '©u' => 'ï¹—',
  '©v' => 'ï¹™',
  '©w' => 'ï¹š',
  '©x' => 'ï¹›',
  '©y' => 'ï¹œ',
  '©z' => 'ï¹�',
  '©{' => 'ï¹ž',
  '©|' => 'ï¹Ÿ',
  '©}' => 'ï¹ ',
  '©~' => 'ï¹¡',
  '©€' => 'ï¹¢',
  '©�' => 'ï¹£',
  '©‚' => 'ï¹¤',
  '©ƒ' => 'ï¹¥',
  '©„' => 'ï¹¦',
  '©…' => 'ï¹¨',
  '©†' => 'ï¹©',
  '©‡' => 'ï¹ª',
  '©ˆ' => 'ï¹«',
  '©–' => 'ã€‡',
  '©¤' => 'â”€',
  '©¥' => 'â”�',
  '©¦' => 'â”‚',
  '©§' => 'â”ƒ',
  '©¨' => 'â”„',
  '©©' => 'â”…',
  '©ª' => 'â”†',
  '©«' => 'â”‡',
  '©¬' => 'â”ˆ',
  '©­' => 'â”‰',
  '©®' => 'â”Š',
  '©¯' => 'â”‹',
  '©°' => 'â”Œ',
  '©±' => 'â”�',
  '©²' => 'â”Ž',
  '©³' => 'â”�',
  '©´' => 'â”�',
  '©µ' => 'â”‘',
  '©¶' => 'â”’',
  '©·' => 'â”“',
  '©¸' => 'â””',
  '©¹' => 'â”•',
  '©º' => 'â”–',
  '©»' => 'â”—',
  '©¼' => 'â”˜',
  '©½' => 'â”™',
  '©¾' => 'â”š',
  '©¿' => 'â”›',
  '©À' => 'â”œ',
  '©Á' => 'â”�',
  '©Â' => 'â”ž',
  '©Ã' => 'â”Ÿ',
  '©Ä' => 'â” ',
  '©Å' => 'â”¡',
  '©Æ' => 'â”¢',
  '©Ç' => 'â”£',
  '©È' => 'â”¤',
  '©É' => 'â”¥',
  '©Ê' => 'â”¦',
  '©Ë' => 'â”§',
  '©Ì' => 'â”¨',
  '©Í' => 'â”©',
  '©Î' => 'â”ª',
  '©Ï' => 'â”«',
  '©Ð' => 'â”¬',
  '©Ñ' => 'â”­',
  '©Ò' => 'â”®',
  '©Ó' => 'â”¯',
  '©Ô' => 'â”°',
  '©Õ' => 'â”±',
  '©Ö' => 'â”²',
  '©×' => 'â”³',
  '©Ø' => 'â”´',
  '©Ù' => 'â”µ',
  '©Ú' => 'â”¶',
  '©Û' => 'â”·',
  '©Ü' => 'â”¸',
  '©Ý' => 'â”¹',
  '©Þ' => 'â”º',
  '©ß' => 'â”»',
  '©à' => 'â”¼',
  '©á' => 'â”½',
  '©â' => 'â”¾',
  '©ã' => 'â”¿',
  '©ä' => 'â•€',
  '©å' => 'â•�',
  '©æ' => 'â•‚',
  '©ç' => 'â•ƒ',
  '©è' => 'â•„',
  '©é' => 'â•…',
  '©ê' => 'â•†',
  '©ë' => 'â•‡',
  '©ì' => 'â•ˆ',
  '©í' => 'â•‰',
  '©î' => 'â•Š',
  '©ï' => 'â•‹',
  'ª@' => 'ç‹œ',
  'ªA' => 'ç‹�',
  'ªB' => 'ç‹Ÿ',
  'ªC' => 'ç‹¢',
  'ªD' => 'ç‹£',
  'ªE' => 'ç‹¤',
  'ªF' => 'ç‹¥',
  'ªG' => 'ç‹¦',
  'ªH' => 'ç‹§',
  'ªI' => 'ç‹ª',
  'ªJ' => 'ç‹«',
  'ªK' => 'ç‹µ',
  'ªL' => 'ç‹¶',
  'ªM' => 'ç‹¹',
  'ªN' => 'ç‹½',
  'ªO' => 'ç‹¾',
  'ªP' => 'ç‹¿',
  'ªQ' => 'çŒ€',
  'ªR' => 'çŒ‚',
  'ªS' => 'çŒ„',
  'ªT' => 'çŒ…',
  'ªU' => 'çŒ†',
  'ªV' => 'çŒ‡',
  'ªW' => 'çŒˆ',
  'ªX' => 'çŒ‰',
  'ªY' => 'çŒ‹',
  'ªZ' => 'çŒŒ',
  'ª[' => 'çŒ�',
  'ª\\' => 'çŒ�',
  'ª]' => 'çŒ�',
  'ª^' => 'çŒ‘',
  'ª_' => 'çŒ’',
  'ª`' => 'çŒ”',
  'ªa' => 'çŒ˜',
  'ªb' => 'çŒ™',
  'ªc' => 'çŒš',
  'ªd' => 'çŒŸ',
  'ªe' => 'çŒ ',
  'ªf' => 'çŒ£',
  'ªg' => 'çŒ¤',
  'ªh' => 'çŒ¦',
  'ªi' => 'çŒ§',
  'ªj' => 'çŒ¨',
  'ªk' => 'çŒ­',
  'ªl' => 'çŒ¯',
  'ªm' => 'çŒ°',
  'ªn' => 'çŒ²',
  'ªo' => 'çŒ³',
  'ªp' => 'çŒµ',
  'ªq' => 'çŒ¶',
  'ªr' => 'çŒº',
  'ªs' => 'çŒ»',
  'ªt' => 'çŒ¼',
  'ªu' => 'çŒ½',
  'ªv' => 'ç�€',
  'ªw' => 'ç��',
  'ªx' => 'ç�‚',
  'ªy' => 'ç�ƒ',
  'ªz' => 'ç�„',
  'ª{' => 'ç�…',
  'ª|' => 'ç�†',
  'ª}' => 'ç�‡',
  'ª~' => 'ç�ˆ',
  'ª€' => 'ç�‰',
  'ª�' => 'ç�Š',
  'ª‚' => 'ç�‹',
  'ªƒ' => 'ç�Œ',
  'ª„' => 'ç�Ž',
  'ª…' => 'ç��',
  'ª†' => 'ç�‘',
  'ª‡' => 'ç�“',
  'ªˆ' => 'ç�”',
  'ª‰' => 'ç�•',
  'ªŠ' => 'ç�–',
  'ª‹' => 'ç�˜',
  'ªŒ' => 'ç�™',
  'ª�' => 'ç�š',
  'ªŽ' => 'ç�›',
  'ª�' => 'ç�œ',
  'ª�' => 'ç��',
  'ª‘' => 'ç�ž',
  'ª’' => 'ç�Ÿ',
  'ª“' => 'ç�¡',
  'ª”' => 'ç�¢',
  'ª•' => 'ç�£',
  'ª–' => 'ç�¤',
  'ª—' => 'ç�¥',
  'ª˜' => 'ç�¦',
  'ª™' => 'ç�§',
  'ªš' => 'ç�¨',
  'ª›' => 'ç�©',
  'ªœ' => 'ç�ª',
  'ª�' => 'ç�«',
  'ªž' => 'ç�®',
  'ªŸ' => 'ç�°',
  'ª ' => 'ç�±',
  '«@' => 'ç�²',
  '«A' => 'ç�³',
  '«B' => 'ç�´',
  '«C' => 'ç�µ',
  '«D' => 'ç�¶',
  '«E' => 'ç�·',
  '«F' => 'ç�¸',
  '«G' => 'ç�¹',
  '«H' => 'ç�º',
  '«I' => 'ç�»',
  '«J' => 'ç�¼',
  '«K' => 'ç�½',
  '«L' => 'ç�¿',
  '«M' => 'çŽ€',
  '«N' => 'çŽ�',
  '«O' => 'çŽ‚',
  '«P' => 'çŽƒ',
  '«Q' => 'çŽ…',
  '«R' => 'çŽ†',
  '«S' => 'çŽˆ',
  '«T' => 'çŽŠ',
  '«U' => 'çŽŒ',
  '«V' => 'çŽ�',
  '«W' => 'çŽ�',
  '«X' => 'çŽ�',
  '«Y' => 'çŽ’',
  '«Z' => 'çŽ“',
  '«[' => 'çŽ”',
  '«\\' => 'çŽ•',
  '«]' => 'çŽ—',
  '«^' => 'çŽ˜',
  '«_' => 'çŽ™',
  '«`' => 'çŽš',
  '«a' => 'çŽœ',
  '«b' => 'çŽ�',
  '«c' => 'çŽž',
  '«d' => 'çŽ ',
  '«e' => 'çŽ¡',
  '«f' => 'çŽ£',
  '«g' => 'çŽ¤',
  '«h' => 'çŽ¥',
  '«i' => 'çŽ¦',
  '«j' => 'çŽ§',
  '«k' => 'çŽ¨',
  '«l' => 'çŽª',
  '«m' => 'çŽ¬',
  '«n' => 'çŽ­',
  '«o' => 'çŽ±',
  '«p' => 'çŽ´',
  '«q' => 'çŽµ',
  '«r' => 'çŽ¶',
  '«s' => 'çŽ¸',
  '«t' => 'çŽ¹',
  '«u' => 'çŽ¼',
  '«v' => 'çŽ½',
  '«w' => 'çŽ¾',
  '«x' => 'çŽ¿',
  '«y' => 'ç��',
  '«z' => 'ç�ƒ',
  '«{' => 'ç�„',
  '«|' => 'ç�…',
  '«}' => 'ç�†',
  '«~' => 'ç�‡',
  '«€' => 'ç�‹',
  '«�' => 'ç�Œ',
  '«‚' => 'ç�Ž',
  '«ƒ' => 'ç�’',
  '«„' => 'ç�“',
  '«…' => 'ç�”',
  '«†' => 'ç�•',
  '«‡' => 'ç�–',
  '«ˆ' => 'ç�—',
  '«‰' => 'ç�˜',
  '«Š' => 'ç�š',
  '«‹' => 'ç�›',
  '«Œ' => 'ç�œ',
  '«�' => 'ç��',
  '«Ž' => 'ç�Ÿ',
  '«�' => 'ç�¡',
  '«�' => 'ç�¢',
  '«‘' => 'ç�£',
  '«’' => 'ç�¤',
  '«“' => 'ç�¦',
  '«”' => 'ç�¨',
  '«•' => 'ç�ª',
  '«–' => 'ç�«',
  '«—' => 'ç�¬',
  '«˜' => 'ç�®',
  '«™' => 'ç�¯',
  '«š' => 'ç�°',
  '«›' => 'ç�±',
  '«œ' => 'ç�³',
  '«�' => 'ç�´',
  '«ž' => 'ç�µ',
  '«Ÿ' => 'ç�¶',
  '« ' => 'ç�·',
  '¬@' => 'ç�¸',
  '¬A' => 'ç�¹',
  '¬B' => 'ç�º',
  '¬C' => 'ç�»',
  '¬D' => 'ç�¼',
  '¬E' => 'ç�½',
  '¬F' => 'ç�¾',
  '¬G' => 'ç�¿',
  '¬H' => 'ç�€',
  '¬I' => 'ç��',
  '¬J' => 'ç�‚',
  '¬K' => 'ç�„',
  '¬L' => 'ç�‡',
  '¬M' => 'ç�ˆ',
  '¬N' => 'ç�‹',
  '¬O' => 'ç�Œ',
  '¬P' => 'ç��',
  '¬Q' => 'ç�Ž',
  '¬R' => 'ç�‘',
  '¬S' => 'ç�’',
  '¬T' => 'ç�“',
  '¬U' => 'ç�”',
  '¬V' => 'ç�•',
  '¬W' => 'ç�–',
  '¬X' => 'ç�—',
  '¬Y' => 'ç�˜',
  '¬Z' => 'ç�™',
  '¬[' => 'ç�œ',
  '¬\\' => 'ç��',
  '¬]' => 'ç�ž',
  '¬^' => 'ç�Ÿ',
  '¬_' => 'ç� ',
  '¬`' => 'ç�¡',
  '¬a' => 'ç�£',
  '¬b' => 'ç�¤',
  '¬c' => 'ç�§',
  '¬d' => 'ç�©',
  '¬e' => 'ç�«',
  '¬f' => 'ç�­',
  '¬g' => 'ç�¯',
  '¬h' => 'ç�±',
  '¬i' => 'ç�²',
  '¬j' => 'ç�·',
  '¬k' => 'ç�¸',
  '¬l' => 'ç�¹',
  '¬m' => 'ç�º',
  '¬n' => 'ç�»',
  '¬o' => 'ç�½',
  '¬p' => 'ç�¾',
  '¬q' => 'ç�¿',
  '¬r' => 'ç‘€',
  '¬s' => 'ç‘‚',
  '¬t' => 'ç‘ƒ',
  '¬u' => 'ç‘„',
  '¬v' => 'ç‘…',
  '¬w' => 'ç‘†',
  '¬x' => 'ç‘‡',
  '¬y' => 'ç‘ˆ',
  '¬z' => 'ç‘‰',
  '¬{' => 'ç‘Š',
  '¬|' => 'ç‘‹',
  '¬}' => 'ç‘Œ',
  '¬~' => 'ç‘�',
  '¬€' => 'ç‘Ž',
  '¬�' => 'ç‘�',
  '¬‚' => 'ç‘�',
  '¬ƒ' => 'ç‘‘',
  '¬„' => 'ç‘’',
  '¬…' => 'ç‘“',
  '¬†' => 'ç‘”',
  '¬‡' => 'ç‘–',
  '¬ˆ' => 'ç‘˜',
  '¬‰' => 'ç‘�',
  '¬Š' => 'ç‘ ',
  '¬‹' => 'ç‘¡',
  '¬Œ' => 'ç‘¢',
  '¬�' => 'ç‘£',
  '¬Ž' => 'ç‘¤',
  '¬�' => 'ç‘¥',
  '¬�' => 'ç‘¦',
  '¬‘' => 'ç‘§',
  '¬’' => 'ç‘¨',
  '¬“' => 'ç‘©',
  '¬”' => 'ç‘ª',
  '¬•' => 'ç‘«',
  '¬–' => 'ç‘¬',
  '¬—' => 'ç‘®',
  '¬˜' => 'ç‘¯',
  '¬™' => 'ç‘±',
  '¬š' => 'ç‘²',
  '¬›' => 'ç‘³',
  '¬œ' => 'ç‘´',
  '¬�' => 'ç‘µ',
  '¬ž' => 'ç‘¸',
  '¬Ÿ' => 'ç‘¹',
  '¬ ' => 'ç‘º',
  '­@' => 'ç‘»',
  '­A' => 'ç‘¼',
  '­B' => 'ç‘½',
  '­C' => 'ç‘¿',
  '­D' => 'ç’‚',
  '­E' => 'ç’„',
  '­F' => 'ç’…',
  '­G' => 'ç’†',
  '­H' => 'ç’ˆ',
  '­I' => 'ç’‰',
  '­J' => 'ç’Š',
  '­K' => 'ç’Œ',
  '­L' => 'ç’�',
  '­M' => 'ç’�',
  '­N' => 'ç’‘',
  '­O' => 'ç’’',
  '­P' => 'ç’“',
  '­Q' => 'ç’”',
  '­R' => 'ç’•',
  '­S' => 'ç’–',
  '­T' => 'ç’—',
  '­U' => 'ç’˜',
  '­V' => 'ç’™',
  '­W' => 'ç’š',
  '­X' => 'ç’›',
  '­Y' => 'ç’�',
  '­Z' => 'ç’Ÿ',
  '­[' => 'ç’ ',
  '­\\' => 'ç’¡',
  '­]' => 'ç’¢',
  '­^' => 'ç’£',
  '­_' => 'ç’¤',
  '­`' => 'ç’¥',
  '­a' => 'ç’¦',
  '­b' => 'ç’ª',
  '­c' => 'ç’«',
  '­d' => 'ç’¬',
  '­e' => 'ç’­',
  '­f' => 'ç’®',
  '­g' => 'ç’¯',
  '­h' => 'ç’°',
  '­i' => 'ç’±',
  '­j' => 'ç’²',
  '­k' => 'ç’³',
  '­l' => 'ç’´',
  '­m' => 'ç’µ',
  '­n' => 'ç’¶',
  '­o' => 'ç’·',
  '­p' => 'ç’¸',
  '­q' => 'ç’¹',
  '­r' => 'ç’»',
  '­s' => 'ç’¼',
  '­t' => 'ç’½',
  '­u' => 'ç’¾',
  '­v' => 'ç’¿',
  '­w' => 'ç“€',
  '­x' => 'ç“�',
  '­y' => 'ç“‚',
  '­z' => 'ç“ƒ',
  '­{' => 'ç“„',
  '­|' => 'ç“…',
  '­}' => 'ç“†',
  '­~' => 'ç“‡',
  '­€' => 'ç“ˆ',
  '­�' => 'ç“‰',
  '­‚' => 'ç“Š',
  '­ƒ' => 'ç“‹',
  '­„' => 'ç“Œ',
  '­…' => 'ç“�',
  '­†' => 'ç“Ž',
  '­‡' => 'ç“�',
  '­ˆ' => 'ç“�',
  '­‰' => 'ç“‘',
  '­Š' => 'ç““',
  '­‹' => 'ç“”',
  '­Œ' => 'ç“•',
  '­�' => 'ç“–',
  '­Ž' => 'ç“—',
  '­�' => 'ç“˜',
  '­�' => 'ç“™',
  '­‘' => 'ç“š',
  '­’' => 'ç“›',
  '­“' => 'ç“�',
  '­”' => 'ç“Ÿ',
  '­•' => 'ç“¡',
  '­–' => 'ç“¥',
  '­—' => 'ç“§',
  '­˜' => 'ç“¨',
  '­™' => 'ç“©',
  '­š' => 'ç“ª',
  '­›' => 'ç“«',
  '­œ' => 'ç“¬',
  '­�' => 'ç“­',
  '­ž' => 'ç“°',
  '­Ÿ' => 'ç“±',
  '­ ' => 'ç“²',
  '®@' => 'ç“³',
  '®A' => 'ç“µ',
  '®B' => 'ç“¸',
  '®C' => 'ç“¹',
  '®D' => 'ç“º',
  '®E' => 'ç“»',
  '®F' => 'ç“¼',
  '®G' => 'ç“½',
  '®H' => 'ç“¾',
  '®I' => 'ç”€',
  '®J' => 'ç”�',
  '®K' => 'ç”‚',
  '®L' => 'ç”ƒ',
  '®M' => 'ç”…',
  '®N' => 'ç”†',
  '®O' => 'ç”‡',
  '®P' => 'ç”ˆ',
  '®Q' => 'ç”‰',
  '®R' => 'ç”Š',
  '®S' => 'ç”‹',
  '®T' => 'ç”Œ',
  '®U' => 'ç”Ž',
  '®V' => 'ç”�',
  '®W' => 'ç”’',
  '®X' => 'ç””',
  '®Y' => 'ç”•',
  '®Z' => 'ç”–',
  '®[' => 'ç”—',
  '®\\' => 'ç”›',
  '®]' => 'ç”�',
  '®^' => 'ç”ž',
  '®_' => 'ç” ',
  '®`' => 'ç”¡',
  '®a' => 'ç”¢',
  '®b' => 'ç”£',
  '®c' => 'ç”¤',
  '®d' => 'ç”¦',
  '®e' => 'ç”§',
  '®f' => 'ç”ª',
  '®g' => 'ç”®',
  '®h' => 'ç”´',
  '®i' => 'ç”¶',
  '®j' => 'ç”¹',
  '®k' => 'ç”¼',
  '®l' => 'ç”½',
  '®m' => 'ç”¿',
  '®n' => 'ç•�',
  '®o' => 'ç•‚',
  '®p' => 'ç•ƒ',
  '®q' => 'ç•„',
  '®r' => 'ç•†',
  '®s' => 'ç•‡',
  '®t' => 'ç•‰',
  '®u' => 'ç•Š',
  '®v' => 'ç•�',
  '®w' => 'ç•�',
  '®x' => 'ç•‘',
  '®y' => 'ç•’',
  '®z' => 'ç•“',
  '®{' => 'ç••',
  '®|' => 'ç•–',
  '®}' => 'ç•—',
  '®~' => 'ç•˜',
  '®€' => 'ç•�',
  '®�' => 'ç•ž',
  '®‚' => 'ç•Ÿ',
  '®ƒ' => 'ç• ',
  '®„' => 'ç•¡',
  '®…' => 'ç•¢',
  '®†' => 'ç•£',
  '®‡' => 'ç•¤',
  '®ˆ' => 'ç•§',
  '®‰' => 'ç•¨',
  '®Š' => 'ç•©',
  '®‹' => 'ç•«',
  '®Œ' => 'ç•¬',
  '®�' => 'ç•­',
  '®Ž' => 'ç•®',
  '®�' => 'ç•¯',
  '®�' => 'ç•°',
  '®‘' => 'ç•±',
  '®’' => 'ç•³',
  '®“' => 'ç•µ',
  '®”' => 'ç•¶',
  '®•' => 'ç•·',
  '®–' => 'ç•º',
  '®—' => 'ç•»',
  '®˜' => 'ç•¼',
  '®™' => 'ç•½',
  '®š' => 'ç•¾',
  '®›' => 'ç–€',
  '®œ' => 'ç–�',
  '®�' => 'ç–‚',
  '®ž' => 'ç–„',
  '®Ÿ' => 'ç–…',
  '® ' => 'ç–‡',
  '¯@' => 'ç–ˆ',
  '¯A' => 'ç–‰',
  '¯B' => 'ç–Š',
  '¯C' => 'ç–Œ',
  '¯D' => 'ç–�',
  '¯E' => 'ç–Ž',
  '¯F' => 'ç–�',
  '¯G' => 'ç–“',
  '¯H' => 'ç–•',
  '¯I' => 'ç–˜',
  '¯J' => 'ç–›',
  '¯K' => 'ç–œ',
  '¯L' => 'ç–ž',
  '¯M' => 'ç–¢',
  '¯N' => 'ç–¦',
  '¯O' => 'ç–§',
  '¯P' => 'ç–¨',
  '¯Q' => 'ç–©',
  '¯R' => 'ç–ª',
  '¯S' => 'ç–­',
  '¯T' => 'ç–¶',
  '¯U' => 'ç–·',
  '¯V' => 'ç–º',
  '¯W' => 'ç–»',
  '¯X' => 'ç–¿',
  '¯Y' => 'ç—€',
  '¯Z' => 'ç—�',
  '¯[' => 'ç—†',
  '¯\\' => 'ç—‹',
  '¯]' => 'ç—Œ',
  '¯^' => 'ç—Ž',
  '¯_' => 'ç—�',
  '¯`' => 'ç—�',
  '¯a' => 'ç—‘',
  '¯b' => 'ç—“',
  '¯c' => 'ç——',
  '¯d' => 'ç—™',
  '¯e' => 'ç—š',
  '¯f' => 'ç—œ',
  '¯g' => 'ç—�',
  '¯h' => 'ç—Ÿ',
  '¯i' => 'ç— ',
  '¯j' => 'ç—¡',
  '¯k' => 'ç—¥',
  '¯l' => 'ç—©',
  '¯m' => 'ç—¬',
  '¯n' => 'ç—­',
  '¯o' => 'ç—®',
  '¯p' => 'ç—¯',
  '¯q' => 'ç—²',
  '¯r' => 'ç—³',
  '¯s' => 'ç—µ',
  '¯t' => 'ç—¶',
  '¯u' => 'ç—·',
  '¯v' => 'ç—¸',
  '¯w' => 'ç—º',
  '¯x' => 'ç—»',
  '¯y' => 'ç—½',
  '¯z' => 'ç—¾',
  '¯{' => 'ç˜‚',
  '¯|' => 'ç˜„',
  '¯}' => 'ç˜†',
  '¯~' => 'ç˜‡',
  '¯€' => 'ç˜ˆ',
  '¯�' => 'ç˜‰',
  '¯‚' => 'ç˜‹',
  '¯ƒ' => 'ç˜�',
  '¯„' => 'ç˜Ž',
  '¯…' => 'ç˜�',
  '¯†' => 'ç˜‘',
  '¯‡' => 'ç˜’',
  '¯ˆ' => 'ç˜“',
  '¯‰' => 'ç˜”',
  '¯Š' => 'ç˜–',
  '¯‹' => 'ç˜š',
  '¯Œ' => 'ç˜œ',
  '¯�' => 'ç˜�',
  '¯Ž' => 'ç˜ž',
  '¯�' => 'ç˜¡',
  '¯�' => 'ç˜£',
  '¯‘' => 'ç˜§',
  '¯’' => 'ç˜¨',
  '¯“' => 'ç˜¬',
  '¯”' => 'ç˜®',
  '¯•' => 'ç˜¯',
  '¯–' => 'ç˜±',
  '¯—' => 'ç˜²',
  '¯˜' => 'ç˜¶',
  '¯™' => 'ç˜·',
  '¯š' => 'ç˜¹',
  '¯›' => 'ç˜º',
  '¯œ' => 'ç˜»',
  '¯�' => 'ç˜½',
  '¯ž' => 'ç™�',
  '¯Ÿ' => 'ç™‚',
  '¯ ' => 'ç™„',
  '°@' => 'ç™…',
  '°A' => 'ç™†',
  '°B' => 'ç™‡',
  '°C' => 'ç™ˆ',
  '°D' => 'ç™‰',
  '°E' => 'ç™Š',
  '°F' => 'ç™‹',
  '°G' => 'ç™Ž',
  '°H' => 'ç™�',
  '°I' => 'ç™�',
  '°J' => 'ç™‘',
  '°K' => 'ç™’',
  '°L' => 'ç™“',
  '°M' => 'ç™•',
  '°N' => 'ç™—',
  '°O' => 'ç™˜',
  '°P' => 'ç™™',
  '°Q' => 'ç™š',
  '°R' => 'ç™›',
  '°S' => 'ç™�',
  '°T' => 'ç™Ÿ',
  '°U' => 'ç™ ',
  '°V' => 'ç™¡',
  '°W' => 'ç™¢',
  '°X' => 'ç™¤',
  '°Y' => 'ç™¥',
  '°Z' => 'ç™¦',
  '°[' => 'ç™§',
  '°\\' => 'ç™¨',
  '°]' => 'ç™©',
  '°^' => 'ç™ª',
  '°_' => 'ç™¬',
  '°`' => 'ç™­',
  '°a' => 'ç™®',
  '°b' => 'ç™°',
  '°c' => 'ç™±',
  '°d' => 'ç™²',
  '°e' => 'ç™³',
  '°f' => 'ç™´',
  '°g' => 'ç™µ',
  '°h' => 'ç™¶',
  '°i' => 'ç™·',
  '°j' => 'ç™¹',
  '°k' => 'ç™º',
  '°l' => 'ç™¼',
  '°m' => 'ç™¿',
  '°n' => 'çš€',
  '°o' => 'çš�',
  '°p' => 'çšƒ',
  '°q' => 'çš…',
  '°r' => 'çš‰',
  '°s' => 'çšŠ',
  '°t' => 'çšŒ',
  '°u' => 'çš�',
  '°v' => 'çš�',
  '°w' => 'çš�',
  '°x' => 'çš’',
  '°y' => 'çš”',
  '°z' => 'çš•',
  '°{' => 'çš—',
  '°|' => 'çš˜',
  '°}' => 'çšš',
  '°~' => 'çš›',
  '°€' => 'çšœ',
  '°�' => 'çš�',
  '°‚' => 'çšž',
  '°ƒ' => 'çšŸ',
  '°„' => 'çš ',
  '°…' => 'çš¡',
  '°†' => 'çš¢',
  '°‡' => 'çš£',
  '°ˆ' => 'çš¥',
  '°‰' => 'çš¦',
  '°Š' => 'çš§',
  '°‹' => 'çš¨',
  '°Œ' => 'çš©',
  '°�' => 'çšª',
  '°Ž' => 'çš«',
  '°�' => 'çš¬',
  '°�' => 'çš­',
  '°‘' => 'çš¯',
  '°’' => 'çš°',
  '°“' => 'çš³',
  '°”' => 'çšµ',
  '°•' => 'çš¶',
  '°–' => 'çš·',
  '°—' => 'çš¸',
  '°˜' => 'çš¹',
  '°™' => 'çšº',
  '°š' => 'çš»',
  '°›' => 'çš¼',
  '°œ' => 'çš½',
  '°�' => 'çš¾',
  '°ž' => 'ç›€',
  '°Ÿ' => 'ç›�',
  '° ' => 'ç›ƒ',
  '°¡' => 'å•Š',
  '°¢' => 'é˜¿',
  '°£' => 'åŸƒ',
  '°¤' => 'æŒ¨',
  '°¥' => 'å“Ž',
  '°¦' => 'å”‰',
  '°§' => 'å“€',
  '°¨' => 'çš‘',
  '°©' => 'ç™Œ',
  '°ª' => 'è”¼',
  '°«' => 'çŸ®',
  '°¬' => 'è‰¾',
  '°­' => 'ç¢�',
  '°®' => 'çˆ±',
  '°¯' => 'éš˜',
  '°°' => 'éž�',
  '°±' => 'æ°¨',
  '°²' => 'å®‰',
  '°³' => 'ä¿º',
  '°´' => 'æŒ‰',
  '°µ' => 'æš—',
  '°¶' => 'å²¸',
  '°·' => 'èƒº',
  '°¸' => 'æ¡ˆ',
  '°¹' => 'è‚®',
  '°º' => 'æ˜‚',
  '°»' => 'ç›Ž',
  '°¼' => 'å‡¹',
  '°½' => 'æ•–',
  '°¾' => 'ç†¬',
  '°¿' => 'ç¿±',
  '°À' => 'è¢„',
  '°Á' => 'å‚²',
  '°Â' => 'å¥¥',
  '°Ã' => 'æ‡Š',
  '°Ä' => 'æ¾³',
  '°Å' => 'èŠ­',
  '°Æ' => 'æ�Œ',
  '°Ç' => 'æ‰’',
  '°È' => 'å�­',
  '°É' => 'å�§',
  '°Ê' => 'ç¬†',
  '°Ë' => 'å…«',
  '°Ì' => 'ç–¤',
  '°Í' => 'å·´',
  '°Î' => 'æ‹”',
  '°Ï' => 'è·‹',
  '°Ð' => 'é�¶',
  '°Ñ' => 'æŠŠ',
  '°Ò' => 'è€™',
  '°Ó' => 'å��',
  '°Ô' => 'éœ¸',
  '°Õ' => 'ç½¢',
  '°Ö' => 'çˆ¸',
  '°×' => 'ç™½',
  '°Ø' => 'æŸ�',
  '°Ù' => 'ç™¾',
  '°Ú' => 'æ‘†',
  '°Û' => 'ä½°',
  '°Ü' => 'è´¥',
  '°Ý' => 'æ‹œ',
  '°Þ' => 'ç¨—',
  '°ß' => 'æ–‘',
  '°à' => 'ç�­',
  '°á' => 'æ�¬',
  '°â' => 'æ‰³',
  '°ã' => 'èˆ¬',
  '°ä' => 'é¢�',
  '°å' => 'æ�¿',
  '°æ' => 'ç‰ˆ',
  '°ç' => 'æ‰®',
  '°è' => 'æ‹Œ',
  '°é' => 'ä¼´',
  '°ê' => 'ç“£',
  '°ë' => 'å�Š',
  '°ì' => 'åŠž',
  '°í' => 'ç»Š',
  '°î' => 'é‚¦',
  '°ï' => 'å¸®',
  '°ð' => 'æ¢†',
  '°ñ' => 'æ¦œ',
  '°ò' => 'è†€',
  '°ó' => 'ç»‘',
  '°ô' => 'æ£’',
  '°õ' => 'ç£…',
  '°ö' => 'èšŒ',
  '°÷' => 'é•‘',
  '°ø' => 'å‚�',
  '°ù' => 'è°¤',
  '°ú' => 'è‹ž',
  '°û' => 'èƒž',
  '°ü' => 'åŒ…',
  '°ý' => 'è¤’',
  '°þ' => 'å‰¥',
  '±@' => 'ç›„',
  '±A' => 'ç›‡',
  '±B' => 'ç›‰',
  '±C' => 'ç›‹',
  '±D' => 'ç›Œ',
  '±E' => 'ç›“',
  '±F' => 'ç›•',
  '±G' => 'ç›™',
  '±H' => 'ç›š',
  '±I' => 'ç›œ',
  '±J' => 'ç›�',
  '±K' => 'ç›ž',
  '±L' => 'ç› ',
  '±M' => 'ç›¡',
  '±N' => 'ç›¢',
  '±O' => 'ç›£',
  '±P' => 'ç›¤',
  '±Q' => 'ç›¦',
  '±R' => 'ç›§',
  '±S' => 'ç›¨',
  '±T' => 'ç›©',
  '±U' => 'ç›ª',
  '±V' => 'ç›«',
  '±W' => 'ç›¬',
  '±X' => 'ç›­',
  '±Y' => 'ç›°',
  '±Z' => 'ç›³',
  '±[' => 'ç›µ',
  '±\\' => 'ç›¶',
  '±]' => 'ç›·',
  '±^' => 'ç›º',
  '±_' => 'ç›»',
  '±`' => 'ç›½',
  '±a' => 'ç›¿',
  '±b' => 'çœ€',
  '±c' => 'çœ‚',
  '±d' => 'çœƒ',
  '±e' => 'çœ…',
  '±f' => 'çœ†',
  '±g' => 'çœŠ',
  '±h' => 'çœŒ',
  '±i' => 'çœŽ',
  '±j' => 'çœ�',
  '±k' => 'çœ�',
  '±l' => 'çœ‘',
  '±m' => 'çœ’',
  '±n' => 'çœ“',
  '±o' => 'çœ”',
  '±p' => 'çœ•',
  '±q' => 'çœ–',
  '±r' => 'çœ—',
  '±s' => 'çœ˜',
  '±t' => 'çœ›',
  '±u' => 'çœœ',
  '±v' => 'çœ�',
  '±w' => 'çœž',
  '±x' => 'çœ¡',
  '±y' => 'çœ£',
  '±z' => 'çœ¤',
  '±{' => 'çœ¥',
  '±|' => 'çœ§',
  '±}' => 'çœª',
  '±~' => 'çœ«',
  '±€' => 'çœ¬',
  '±�' => 'çœ®',
  '±‚' => 'çœ°',
  '±ƒ' => 'çœ±',
  '±„' => 'çœ²',
  '±…' => 'çœ³',
  '±†' => 'çœ´',
  '±‡' => 'çœ¹',
  '±ˆ' => 'çœ»',
  '±‰' => 'çœ½',
  '±Š' => 'çœ¾',
  '±‹' => 'çœ¿',
  '±Œ' => 'ç�‚',
  '±�' => 'ç�„',
  '±Ž' => 'ç�…',
  '±�' => 'ç�†',
  '±�' => 'ç�ˆ',
  '±‘' => 'ç�‰',
  '±’' => 'ç�Š',
  '±“' => 'ç�‹',
  '±”' => 'ç�Œ',
  '±•' => 'ç��',
  '±–' => 'ç�Ž',
  '±—' => 'ç��',
  '±˜' => 'ç�’',
  '±™' => 'ç�“',
  '±š' => 'ç�”',
  '±›' => 'ç�•',
  '±œ' => 'ç�–',
  '±�' => 'ç�—',
  '±ž' => 'ç�˜',
  '±Ÿ' => 'ç�™',
  '± ' => 'ç�œ',
  '±¡' => 'è–„',
  '±¢' => 'é›¹',
  '±£' => 'ä¿�',
  '±¤' => 'å ¡',
  '±¥' => 'é¥±',
  '±¦' => 'å®�',
  '±§' => 'æŠ±',
  '±¨' => 'æŠ¥',
  '±©' => 'æš´',
  '±ª' => 'è±¹',
  '±«' => 'é²�',
  '±¬' => 'çˆ†',
  '±­' => 'æ�¯',
  '±®' => 'ç¢‘',
  '±¯' => 'æ‚²',
  '±°' => 'å�‘',
  '±±' => 'åŒ—',
  '±²' => 'è¾ˆ',
  '±³' => 'èƒŒ',
  '±´' => 'è´�',
  '±µ' => 'é’¡',
  '±¶' => 'å€�',
  '±·' => 'ç‹ˆ',
  '±¸' => 'å¤‡',
  '±¹' => 'æƒ«',
  '±º' => 'ç„™',
  '±»' => 'è¢«',
  '±¼' => 'å¥”',
  '±½' => 'è‹¯',
  '±¾' => 'æœ¬',
  '±¿' => 'ç¬¨',
  '±À' => 'å´©',
  '±Á' => 'ç»·',
  '±Â' => 'ç”­',
  '±Ã' => 'æ³µ',
  '±Ä' => 'è¹¦',
  '±Å' => 'è¿¸',
  '±Æ' => 'é€¼',
  '±Ç' => 'é¼»',
  '±È' => 'æ¯”',
  '±É' => 'é„™',
  '±Ê' => 'ç¬”',
  '±Ë' => 'å½¼',
  '±Ì' => 'ç¢§',
  '±Í' => 'è“–',
  '±Î' => 'è”½',
  '±Ï' => 'æ¯•',
  '±Ð' => 'æ¯™',
  '±Ñ' => 'æ¯–',
  '±Ò' => 'å¸�',
  '±Ó' => 'åº‡',
  '±Ô' => 'ç—¹',
  '±Õ' => 'é—­',
  '±Ö' => 'æ•�',
  '±×' => 'å¼Š',
  '±Ø' => 'å¿…',
  '±Ù' => 'è¾Ÿ',
  '±Ú' => 'å£�',
  '±Û' => 'è‡‚',
  '±Ü' => 'é�¿',
  '±Ý' => 'é™›',
  '±Þ' => 'éž­',
  '±ß' => 'è¾¹',
  '±à' => 'ç¼–',
  '±á' => 'è´¬',
  '±â' => 'æ‰�',
  '±ã' => 'ä¾¿',
  '±ä' => 'å�˜',
  '±å' => 'å�ž',
  '±æ' => 'è¾¨',
  '±ç' => 'è¾©',
  '±è' => 'è¾«',
  '±é' => 'é��',
  '±ê' => 'æ ‡',
  '±ë' => 'å½ª',
  '±ì' => 'è†˜',
  '±í' => 'è¡¨',
  '±î' => 'é³–',
  '±ï' => 'æ†‹',
  '±ð' => 'åˆ«',
  '±ñ' => 'ç˜ª',
  '±ò' => 'å½¬',
  '±ó' => 'æ–Œ',
  '±ô' => 'æ¿’',
  '±õ' => 'æ»¨',
  '±ö' => 'å®¾',
  '±÷' => 'æ‘ˆ',
  '±ø' => 'å…µ',
  '±ù' => 'å†°',
  '±ú' => 'æŸ„',
  '±û' => 'ä¸™',
  '±ü' => 'ç§‰',
  '±ý' => 'é¥¼',
  '±þ' => 'ç‚³',
  '²@' => 'ç��',
  '²A' => 'ç�ž',
  '²B' => 'ç�Ÿ',
  '²C' => 'ç� ',
  '²D' => 'ç�¤',
  '²E' => 'ç�§',
  '²F' => 'ç�©',
  '²G' => 'ç�ª',
  '²H' => 'ç�­',
  '²I' => 'ç�®',
  '²J' => 'ç�¯',
  '²K' => 'ç�°',
  '²L' => 'ç�±',
  '²M' => 'ç�²',
  '²N' => 'ç�³',
  '²O' => 'ç�´',
  '²P' => 'ç�µ',
  '²Q' => 'ç�¶',
  '²R' => 'ç�·',
  '²S' => 'ç�¸',
  '²T' => 'ç�º',
  '²U' => 'ç�»',
  '²V' => 'ç�¼',
  '²W' => 'çž�',
  '²X' => 'çž‚',
  '²Y' => 'çžƒ',
  '²Z' => 'çž†',
  '²[' => 'çž‡',
  '²\\' => 'çžˆ',
  '²]' => 'çž‰',
  '²^' => 'çžŠ',
  '²_' => 'çž‹',
  '²`' => 'çž�',
  '²a' => 'çž�',
  '²b' => 'çž“',
  '²c' => 'çž”',
  '²d' => 'çž•',
  '²e' => 'çž–',
  '²f' => 'çž—',
  '²g' => 'çž˜',
  '²h' => 'çž™',
  '²i' => 'çžš',
  '²j' => 'çž›',
  '²k' => 'çžœ',
  '²l' => 'çž�',
  '²m' => 'çžž',
  '²n' => 'çž¡',
  '²o' => 'çž£',
  '²p' => 'çž¤',
  '²q' => 'çž¦',
  '²r' => 'çž¨',
  '²s' => 'çž«',
  '²t' => 'çž­',
  '²u' => 'çž®',
  '²v' => 'çž¯',
  '²w' => 'çž±',
  '²x' => 'çž²',
  '²y' => 'çž´',
  '²z' => 'çž¶',
  '²{' => 'çž·',
  '²|' => 'çž¸',
  '²}' => 'çž¹',
  '²~' => 'çžº',
  '²€' => 'çž¼',
  '²�' => 'çž¾',
  '²‚' => 'çŸ€',
  '²ƒ' => 'çŸ�',
  '²„' => 'çŸ‚',
  '²…' => 'çŸƒ',
  '²†' => 'çŸ„',
  '²‡' => 'çŸ…',
  '²ˆ' => 'çŸ†',
  '²‰' => 'çŸ‡',
  '²Š' => 'çŸˆ',
  '²‹' => 'çŸ‰',
  '²Œ' => 'çŸŠ',
  '²�' => 'çŸ‹',
  '²Ž' => 'çŸŒ',
  '²�' => 'çŸŽ',
  '²�' => 'çŸ�',
  '²‘' => 'çŸ�',
  '²’' => 'çŸ‘',
  '²“' => 'çŸ’',
  '²”' => 'çŸ“',
  '²•' => 'çŸ”',
  '²–' => 'çŸ•',
  '²—' => 'çŸ–',
  '²˜' => 'çŸ˜',
  '²™' => 'çŸ™',
  '²š' => 'çŸš',
  '²›' => 'çŸ�',
  '²œ' => 'çŸž',
  '²�' => 'çŸŸ',
  '²ž' => 'çŸ ',
  '²Ÿ' => 'çŸ¡',
  '² ' => 'çŸ¤',
  '²¡' => 'ç—…',
  '²¢' => 'å¹¶',
  '²£' => 'çŽ»',
  '²¤' => 'è� ',
  '²¥' => 'æ’­',
  '²¦' => 'æ‹¨',
  '²§' => 'é’µ',
  '²¨' => 'æ³¢',
  '²©' => 'å�š',
  '²ª' => 'å‹ƒ',
  '²«' => 'æ��',
  '²¬' => 'é“‚',
  '²­' => 'ç®”',
  '²®' => 'ä¼¯',
  '²¯' => 'å¸›',
  '²°' => 'èˆ¶',
  '²±' => 'è„–',
  '²²' => 'è†Š',
  '²³' => 'æ¸¤',
  '²´' => 'æ³Š',
  '²µ' => 'é©³',
  '²¶' => 'æ�•',
  '²·' => 'å�œ',
  '²¸' => 'å“º',
  '²¹' => 'è¡¥',
  '²º' => 'åŸ ',
  '²»' => 'ä¸�',
  '²¼' => 'å¸ƒ',
  '²½' => 'æ­¥',
  '²¾' => 'ç°¿',
  '²¿' => 'éƒ¨',
  '²À' => 'æ€–',
  '²Á' => 'æ“¦',
  '²Â' => 'çŒœ',
  '²Ã' => 'è£�',
  '²Ä' => 'æ��',
  '²Å' => 'æ‰�',
  '²Æ' => 'è´¢',
  '²Ç' => 'ç�¬',
  '²È' => 'è¸©',
  '²É' => 'é‡‡',
  '²Ê' => 'å½©',
  '²Ë' => 'è�œ',
  '²Ì' => 'è”¡',
  '²Í' => 'é¤�',
  '²Î' => 'å�‚',
  '²Ï' => 'èš•',
  '²Ð' => 'æ®‹',
  '²Ñ' => 'æƒ­',
  '²Ò' => 'æƒ¨',
  '²Ó' => 'ç�¿',
  '²Ô' => 'è‹�',
  '²Õ' => 'èˆ±',
  '²Ö' => 'ä»“',
  '²×' => 'æ²§',
  '²Ø' => 'è—�',
  '²Ù' => 'æ“�',
  '²Ú' => 'ç³™',
  '²Û' => 'æ§½',
  '²Ü' => 'æ›¹',
  '²Ý' => 'è�‰',
  '²Þ' => 'åŽ•',
  '²ß' => 'ç­–',
  '²à' => 'ä¾§',
  '²á' => 'å†Œ',
  '²â' => 'æµ‹',
  '²ã' => 'å±‚',
  '²ä' => 'è¹­',
  '²å' => 'æ�’',
  '²æ' => 'å�‰',
  '²ç' => 'èŒ¬',
  '²è' => 'èŒ¶',
  '²é' => 'æŸ¥',
  '²ê' => 'ç¢´',
  '²ë' => 'æ�½',
  '²ì' => 'å¯Ÿ',
  '²í' => 'å²”',
  '²î' => 'å·®',
  '²ï' => 'è¯§',
  '²ð' => 'æ‹†',
  '²ñ' => 'æŸ´',
  '²ò' => 'è±º',
  '²ó' => 'æ�€',
  '²ô' => 'æŽº',
  '²õ' => 'è�‰',
  '²ö' => 'é¦‹',
  '²÷' => 'è°—',
  '²ø' => 'ç¼ ',
  '²ù' => 'é“²',
  '²ú' => 'äº§',
  '²û' => 'é˜�',
  '²ü' => 'é¢¤',
  '²ý' => 'æ˜Œ',
  '²þ' => 'çŒ–',
  '³@' => 'çŸ¦',
  '³A' => 'çŸ¨',
  '³B' => 'çŸª',
  '³C' => 'çŸ¯',
  '³D' => 'çŸ°',
  '³E' => 'çŸ±',
  '³F' => 'çŸ²',
  '³G' => 'çŸ´',
  '³H' => 'çŸµ',
  '³I' => 'çŸ·',
  '³J' => 'çŸ¹',
  '³K' => 'çŸº',
  '³L' => 'çŸ»',
  '³M' => 'çŸ¼',
  '³N' => 'ç ƒ',
  '³O' => 'ç „',
  '³P' => 'ç …',
  '³Q' => 'ç †',
  '³R' => 'ç ‡',
  '³S' => 'ç ˆ',
  '³T' => 'ç Š',
  '³U' => 'ç ‹',
  '³V' => 'ç Ž',
  '³W' => 'ç �',
  '³X' => 'ç �',
  '³Y' => 'ç “',
  '³Z' => 'ç •',
  '³[' => 'ç ™',
  '³\\' => 'ç ›',
  '³]' => 'ç ž',
  '³^' => 'ç  ',
  '³_' => 'ç ¡',
  '³`' => 'ç ¢',
  '³a' => 'ç ¤',
  '³b' => 'ç ¨',
  '³c' => 'ç ª',
  '³d' => 'ç «',
  '³e' => 'ç ®',
  '³f' => 'ç ¯',
  '³g' => 'ç ±',
  '³h' => 'ç ²',
  '³i' => 'ç ³',
  '³j' => 'ç µ',
  '³k' => 'ç ¶',
  '³l' => 'ç ½',
  '³m' => 'ç ¿',
  '³n' => 'ç¡�',
  '³o' => 'ç¡‚',
  '³p' => 'ç¡ƒ',
  '³q' => 'ç¡„',
  '³r' => 'ç¡†',
  '³s' => 'ç¡ˆ',
  '³t' => 'ç¡‰',
  '³u' => 'ç¡Š',
  '³v' => 'ç¡‹',
  '³w' => 'ç¡�',
  '³x' => 'ç¡�',
  '³y' => 'ç¡‘',
  '³z' => 'ç¡“',
  '³{' => 'ç¡”',
  '³|' => 'ç¡˜',
  '³}' => 'ç¡™',
  '³~' => 'ç¡š',
  '³€' => 'ç¡›',
  '³�' => 'ç¡œ',
  '³‚' => 'ç¡ž',
  '³ƒ' => 'ç¡Ÿ',
  '³„' => 'ç¡ ',
  '³…' => 'ç¡¡',
  '³†' => 'ç¡¢',
  '³‡' => 'ç¡£',
  '³ˆ' => 'ç¡¤',
  '³‰' => 'ç¡¥',
  '³Š' => 'ç¡¦',
  '³‹' => 'ç¡§',
  '³Œ' => 'ç¡¨',
  '³�' => 'ç¡©',
  '³Ž' => 'ç¡¯',
  '³�' => 'ç¡°',
  '³�' => 'ç¡±',
  '³‘' => 'ç¡²',
  '³’' => 'ç¡³',
  '³“' => 'ç¡´',
  '³”' => 'ç¡µ',
  '³•' => 'ç¡¶',
  '³–' => 'ç¡¸',
  '³—' => 'ç¡¹',
  '³˜' => 'ç¡º',
  '³™' => 'ç¡»',
  '³š' => 'ç¡½',
  '³›' => 'ç¡¾',
  '³œ' => 'ç¡¿',
  '³�' => 'ç¢€',
  '³ž' => 'ç¢�',
  '³Ÿ' => 'ç¢‚',
  '³ ' => 'ç¢ƒ',
  '³¡' => 'åœº',
  '³¢' => 'å°�',
  '³£' => 'å¸¸',
  '³¤' => 'é•¿',
  '³¥' => 'å�¿',
  '³¦' => 'è‚ ',
  '³§' => 'åŽ‚',
  '³¨' => 'æ•ž',
  '³©' => 'ç•…',
  '³ª' => 'å”±',
  '³«' => 'å€¡',
  '³¬' => 'è¶…',
  '³­' => 'æŠ„',
  '³®' => 'é’ž',
  '³¯' => 'æœ�',
  '³°' => 'å˜²',
  '³±' => 'æ½®',
  '³²' => 'å·¢',
  '³³' => 'å�µ',
  '³´' => 'ç‚’',
  '³µ' => 'è½¦',
  '³¶' => 'æ‰¯',
  '³·' => 'æ’¤',
  '³¸' => 'æŽ£',
  '³¹' => 'å½»',
  '³º' => 'æ¾ˆ',
  '³»' => 'éƒ´',
  '³¼' => 'è‡£',
  '³½' => 'è¾°',
  '³¾' => 'å°˜',
  '³¿' => 'æ™¨',
  '³À' => 'å¿±',
  '³Á' => 'æ²‰',
  '³Â' => 'é™ˆ',
  '³Ã' => 'è¶�',
  '³Ä' => 'è¡¬',
  '³Å' => 'æ’‘',
  '³Æ' => 'ç§°',
  '³Ç' => 'åŸŽ',
  '³È' => 'æ©™',
  '³É' => 'æˆ�',
  '³Ê' => 'å‘ˆ',
  '³Ë' => 'ä¹˜',
  '³Ì' => 'ç¨‹',
  '³Í' => 'æƒ©',
  '³Î' => 'æ¾„',
  '³Ï' => 'è¯š',
  '³Ð' => 'æ‰¿',
  '³Ñ' => 'é€ž',
  '³Ò' => 'éª‹',
  '³Ó' => 'ç§¤',
  '³Ô' => 'å�ƒ',
  '³Õ' => 'ç—´',
  '³Ö' => 'æŒ�',
  '³×' => 'åŒ™',
  '³Ø' => 'æ± ',
  '³Ù' => 'è¿Ÿ',
  '³Ú' => 'å¼›',
  '³Û' => 'é©°',
  '³Ü' => 'è€»',
  '³Ý' => 'é½¿',
  '³Þ' => 'ä¾ˆ',
  '³ß' => 'å°º',
  '³à' => 'èµ¤',
  '³á' => 'ç¿…',
  '³â' => 'æ–¥',
  '³ã' => 'ç‚½',
  '³ä' => 'å……',
  '³å' => 'å†²',
  '³æ' => 'è™«',
  '³ç' => 'å´‡',
  '³è' => 'å® ',
  '³é' => 'æŠ½',
  '³ê' => 'é…¬',
  '³ë' => 'ç•´',
  '³ì' => 'è¸Œ',
  '³í' => 'ç¨ ',
  '³î' => 'æ„�',
  '³ï' => 'ç­¹',
  '³ð' => 'ä»‡',
  '³ñ' => 'ç»¸',
  '³ò' => 'çž…',
  '³ó' => 'ä¸‘',
  '³ô' => 'è‡­',
  '³õ' => 'åˆ�',
  '³ö' => 'å‡º',
  '³÷' => 'æ©±',
  '³ø' => 'åŽ¨',
  '³ù' => 'èº‡',
  '³ú' => 'é”„',
  '³û' => 'é›�',
  '³ü' => 'æ»�',
  '³ý' => 'é™¤',
  '³þ' => 'æ¥š',
  '´@' => 'ç¢„',
  '´A' => 'ç¢…',
  '´B' => 'ç¢†',
  '´C' => 'ç¢ˆ',
  '´D' => 'ç¢Š',
  '´E' => 'ç¢‹',
  '´F' => 'ç¢�',
  '´G' => 'ç¢�',
  '´H' => 'ç¢’',
  '´I' => 'ç¢”',
  '´J' => 'ç¢•',
  '´K' => 'ç¢–',
  '´L' => 'ç¢™',
  '´M' => 'ç¢�',
  '´N' => 'ç¢ž',
  '´O' => 'ç¢ ',
  '´P' => 'ç¢¢',
  '´Q' => 'ç¢¤',
  '´R' => 'ç¢¦',
  '´S' => 'ç¢¨',
  '´T' => 'ç¢©',
  '´U' => 'ç¢ª',
  '´V' => 'ç¢«',
  '´W' => 'ç¢¬',
  '´X' => 'ç¢­',
  '´Y' => 'ç¢®',
  '´Z' => 'ç¢¯',
  '´[' => 'ç¢µ',
  '´\\' => 'ç¢¶',
  '´]' => 'ç¢·',
  '´^' => 'ç¢¸',
  '´_' => 'ç¢º',
  '´`' => 'ç¢»',
  '´a' => 'ç¢¼',
  '´b' => 'ç¢½',
  '´c' => 'ç¢¿',
  '´d' => 'ç£€',
  '´e' => 'ç£‚',
  '´f' => 'ç£ƒ',
  '´g' => 'ç£„',
  '´h' => 'ç£†',
  '´i' => 'ç£‡',
  '´j' => 'ç£ˆ',
  '´k' => 'ç£Œ',
  '´l' => 'ç£�',
  '´m' => 'ç£Ž',
  '´n' => 'ç£�',
  '´o' => 'ç£‘',
  '´p' => 'ç£’',
  '´q' => 'ç£“',
  '´r' => 'ç£–',
  '´s' => 'ç£—',
  '´t' => 'ç£˜',
  '´u' => 'ç£š',
  '´v' => 'ç£›',
  '´w' => 'ç£œ',
  '´x' => 'ç£�',
  '´y' => 'ç£ž',
  '´z' => 'ç£Ÿ',
  '´{' => 'ç£ ',
  '´|' => 'ç£¡',
  '´}' => 'ç£¢',
  '´~' => 'ç££',
  '´€' => 'ç£¤',
  '´�' => 'ç£¥',
  '´‚' => 'ç£¦',
  '´ƒ' => 'ç£§',
  '´„' => 'ç£©',
  '´…' => 'ç£ª',
  '´†' => 'ç£«',
  '´‡' => 'ç£­',
  '´ˆ' => 'ç£®',
  '´‰' => 'ç£¯',
  '´Š' => 'ç£°',
  '´‹' => 'ç£±',
  '´Œ' => 'ç£³',
  '´�' => 'ç£µ',
  '´Ž' => 'ç£¶',
  '´�' => 'ç£¸',
  '´�' => 'ç£¹',
  '´‘' => 'ç£»',
  '´’' => 'ç£¼',
  '´“' => 'ç£½',
  '´”' => 'ç£¾',
  '´•' => 'ç£¿',
  '´–' => 'ç¤€',
  '´—' => 'ç¤‚',
  '´˜' => 'ç¤ƒ',
  '´™' => 'ç¤„',
  '´š' => 'ç¤†',
  '´›' => 'ç¤‡',
  '´œ' => 'ç¤ˆ',
  '´�' => 'ç¤‰',
  '´ž' => 'ç¤Š',
  '´Ÿ' => 'ç¤‹',
  '´ ' => 'ç¤Œ',
  '´¡' => 'ç¡€',
  '´¢' => 'å‚¨',
  '´£' => 'çŸ—',
  '´¤' => 'æ��',
  '´¥' => 'è§¦',
  '´¦' => 'å¤„',
  '´§' => 'æ�£',
  '´¨' => 'å·�',
  '´©' => 'ç©¿',
  '´ª' => 'æ¤½',
  '´«' => 'ä¼ ',
  '´¬' => 'èˆ¹',
  '´­' => 'å–˜',
  '´®' => 'ä¸²',
  '´¯' => 'ç–®',
  '´°' => 'çª—',
  '´±' => 'å¹¢',
  '´²' => 'åºŠ',
  '´³' => 'é—¯',
  '´´' => 'åˆ›',
  '´µ' => 'å�¹',
  '´¶' => 'ç‚Š',
  '´·' => 'æ�¶',
  '´¸' => 'é”¤',
  '´¹' => 'åž‚',
  '´º' => 'æ˜¥',
  '´»' => 'æ¤¿',
  '´¼' => 'é†‡',
  '´½' => 'å”‡',
  '´¾' => 'æ·³',
  '´¿' => 'çº¯',
  '´À' => 'è ¢',
  '´Á' => 'æˆ³',
  '´Â' => 'ç»°',
  '´Ã' => 'ç–µ',
  '´Ä' => 'èŒ¨',
  '´Å' => 'ç£�',
  '´Æ' => 'é›Œ',
  '´Ç' => 'è¾ž',
  '´È' => 'æ…ˆ',
  '´É' => 'ç“·',
  '´Ê' => 'è¯�',
  '´Ë' => 'æ­¤',
  '´Ì' => 'åˆº',
  '´Í' => 'èµ�',
  '´Î' => 'æ¬¡',
  '´Ï' => 'è�ª',
  '´Ð' => 'è‘±',
  '´Ñ' => 'å›±',
  '´Ò' => 'åŒ†',
  '´Ó' => 'ä»Ž',
  '´Ô' => 'ä¸›',
  '´Õ' => 'å‡‘',
  '´Ö' => 'ç²—',
  '´×' => 'é†‹',
  '´Ø' => 'ç°‡',
  '´Ù' => 'ä¿ƒ',
  '´Ú' => 'è¹¿',
  '´Û' => 'ç¯¡',
  '´Ü' => 'çªœ',
  '´Ý' => 'æ‘§',
  '´Þ' => 'å´”',
  '´ß' => 'å‚¬',
  '´à' => 'è„†',
  '´á' => 'ç˜�',
  '´â' => 'ç²¹',
  '´ã' => 'æ·¬',
  '´ä' => 'ç¿ ',
  '´å' => 'æ�‘',
  '´æ' => 'å­˜',
  '´ç' => 'å¯¸',
  '´è' => 'ç£‹',
  '´é' => 'æ’®',
  '´ê' => 'æ�“',
  '´ë' => 'æŽª',
  '´ì' => 'æŒ«',
  '´í' => 'é”™',
  '´î' => 'æ�­',
  '´ï' => 'è¾¾',
  '´ð' => 'ç­”',
  '´ñ' => 'ç˜©',
  '´ò' => 'æ‰“',
  '´ó' => 'å¤§',
  '´ô' => 'å‘†',
  '´õ' => 'æ­¹',
  '´ö' => 'å‚£',
  '´÷' => 'æˆ´',
  '´ø' => 'å¸¦',
  '´ù' => 'æ®†',
  '´ú' => 'ä»£',
  '´û' => 'è´·',
  '´ü' => 'è¢‹',
  '´ý' => 'å¾…',
  '´þ' => 'é€®',
  'µ@' => 'ç¤�',
  'µA' => 'ç¤Ž',
  'µB' => 'ç¤�',
  'µC' => 'ç¤�',
  'µD' => 'ç¤‘',
  'µE' => 'ç¤’',
  'µF' => 'ç¤”',
  'µG' => 'ç¤•',
  'µH' => 'ç¤–',
  'µI' => 'ç¤—',
  'µJ' => 'ç¤˜',
  'µK' => 'ç¤™',
  'µL' => 'ç¤š',
  'µM' => 'ç¤›',
  'µN' => 'ç¤œ',
  'µO' => 'ç¤�',
  'µP' => 'ç¤Ÿ',
  'µQ' => 'ç¤ ',
  'µR' => 'ç¤¡',
  'µS' => 'ç¤¢',
  'µT' => 'ç¤£',
  'µU' => 'ç¤¥',
  'µV' => 'ç¤¦',
  'µW' => 'ç¤§',
  'µX' => 'ç¤¨',
  'µY' => 'ç¤©',
  'µZ' => 'ç¤ª',
  'µ[' => 'ç¤«',
  'µ\\' => 'ç¤¬',
  'µ]' => 'ç¤­',
  'µ^' => 'ç¤®',
  'µ_' => 'ç¤¯',
  'µ`' => 'ç¤°',
  'µa' => 'ç¤±',
  'µb' => 'ç¤²',
  'µc' => 'ç¤³',
  'µd' => 'ç¤µ',
  'µe' => 'ç¤¶',
  'µf' => 'ç¤·',
  'µg' => 'ç¤¸',
  'µh' => 'ç¤¹',
  'µi' => 'ç¤½',
  'µj' => 'ç¤¿',
  'µk' => 'ç¥‚',
  'µl' => 'ç¥ƒ',
  'µm' => 'ç¥„',
  'µn' => 'ç¥…',
  'µo' => 'ç¥‡',
  'µp' => 'ç¥Š',
  'µq' => 'ç¥‹',
  'µr' => 'ç¥Œ',
  'µs' => 'ç¥�',
  'µt' => 'ç¥Ž',
  'µu' => 'ç¥�',
  'µv' => 'ç¥�',
  'µw' => 'ç¥‘',
  'µx' => 'ç¥’',
  'µy' => 'ç¥”',
  'µz' => 'ç¥•',
  'µ{' => 'ç¥˜',
  'µ|' => 'ç¥™',
  'µ}' => 'ç¥¡',
  'µ~' => 'ç¥£',
  'µ€' => 'ç¥¤',
  'µ�' => 'ç¥¦',
  'µ‚' => 'ç¥©',
  'µƒ' => 'ç¥ª',
  'µ„' => 'ç¥«',
  'µ…' => 'ç¥¬',
  'µ†' => 'ç¥®',
  'µ‡' => 'ç¥°',
  'µˆ' => 'ç¥±',
  'µ‰' => 'ç¥²',
  'µŠ' => 'ç¥³',
  'µ‹' => 'ç¥´',
  'µŒ' => 'ç¥µ',
  'µ�' => 'ç¥¶',
  'µŽ' => 'ç¥¹',
  'µ�' => 'ç¥»',
  'µ�' => 'ç¥¼',
  'µ‘' => 'ç¥½',
  'µ’' => 'ç¥¾',
  'µ“' => 'ç¥¿',
  'µ”' => 'ç¦‚',
  'µ•' => 'ç¦ƒ',
  'µ–' => 'ç¦†',
  'µ—' => 'ç¦‡',
  'µ˜' => 'ç¦ˆ',
  'µ™' => 'ç¦‰',
  'µš' => 'ç¦‹',
  'µ›' => 'ç¦Œ',
  'µœ' => 'ç¦�',
  'µ�' => 'ç¦Ž',
  'µž' => 'ç¦�',
  'µŸ' => 'ç¦‘',
  'µ ' => 'ç¦’',
  'µ¡' => 'æ€ ',
  'µ¢' => 'è€½',
  'µ£' => 'æ‹…',
  'µ¤' => 'ä¸¹',
  'µ¥' => 'å�•',
  'µ¦' => 'éƒ¸',
  'µ§' => 'æŽ¸',
  'µ¨' => 'èƒ†',
  'µ©' => 'æ—¦',
  'µª' => 'æ°®',
  'µ«' => 'ä½†',
  'µ¬' => 'æƒ®',
  'µ­' => 'æ·¡',
  'µ®' => 'è¯ž',
  'µ¯' => 'å¼¹',
  'µ°' => 'è›‹',
  'µ±' => 'å½“',
  'µ²' => 'æŒ¡',
  'µ³' => 'å…š',
  'µ´' => 'è�¡',
  'µµ' => 'æ¡£',
  'µ¶' => 'åˆ€',
  'µ·' => 'æ�£',
  'µ¸' => 'è¹ˆ',
  'µ¹' => 'å€’',
  'µº' => 'å²›',
  'µ»' => 'ç¥·',
  'µ¼' => 'å¯¼',
  'µ½' => 'åˆ°',
  'µ¾' => 'ç¨»',
  'µ¿' => 'æ‚¼',
  'µÀ' => 'é�“',
  'µÁ' => 'ç›—',
  'µÂ' => 'å¾·',
  'µÃ' => 'å¾—',
  'µÄ' => 'çš„',
  'µÅ' => 'è¹¬',
  'µÆ' => 'ç�¯',
  'µÇ' => 'ç™»',
  'µÈ' => 'ç­‰',
  'µÉ' => 'çžª',
  'µÊ' => 'å‡³',
  'µË' => 'é‚“',
  'µÌ' => 'å ¤',
  'µÍ' => 'ä½Ž',
  'µÎ' => 'æ»´',
  'µÏ' => 'è¿ª',
  'µÐ' => 'æ•Œ',
  'µÑ' => 'ç¬›',
  'µÒ' => 'ç‹„',
  'µÓ' => 'æ¶¤',
  'µÔ' => 'ç¿Ÿ',
  'µÕ' => 'å«¡',
  'µÖ' => 'æŠµ',
  'µ×' => 'åº•',
  'µØ' => 'åœ°',
  'µÙ' => 'è’‚',
  'µÚ' => 'ç¬¬',
  'µÛ' => 'å¸�',
  'µÜ' => 'å¼Ÿ',
  'µÝ' => 'é€’',
  'µÞ' => 'ç¼”',
  'µß' => 'é¢ ',
  'µà' => 'æŽ‚',
  'µá' => 'æ»‡',
  'µâ' => 'ç¢˜',
  'µã' => 'ç‚¹',
  'µä' => 'å…¸',
  'µå' => 'é�›',
  'µæ' => 'åž«',
  'µç' => 'ç”µ',
  'µè' => 'ä½ƒ',
  'µé' => 'ç”¸',
  'µê' => 'åº—',
  'µë' => 'æƒ¦',
  'µì' => 'å¥ ',
  'µí' => 'æ·€',
  'µî' => 'æ®¿',
  'µï' => 'ç¢‰',
  'µð' => 'å�¼',
  'µñ' => 'é›•',
  'µò' => 'å‡‹',
  'µó' => 'åˆ�',
  'µô' => 'æŽ‰',
  'µõ' => 'å�Š',
  'µö' => 'é’“',
  'µ÷' => 'è°ƒ',
  'µø' => 'è·Œ',
  'µù' => 'çˆ¹',
  'µú' => 'ç¢Ÿ',
  'µû' => 'è�¶',
  'µü' => 'è¿­',
  'µý' => 'è°�',
  'µþ' => 'å� ',
  '¶@' => 'ç¦“',
  '¶A' => 'ç¦”',
  '¶B' => 'ç¦•',
  '¶C' => 'ç¦–',
  '¶D' => 'ç¦—',
  '¶E' => 'ç¦˜',
  '¶F' => 'ç¦™',
  '¶G' => 'ç¦›',
  '¶H' => 'ç¦œ',
  '¶I' => 'ç¦�',
  '¶J' => 'ç¦ž',
  '¶K' => 'ç¦Ÿ',
  '¶L' => 'ç¦ ',
  '¶M' => 'ç¦¡',
  '¶N' => 'ç¦¢',
  '¶O' => 'ç¦£',
  '¶P' => 'ç¦¤',
  '¶Q' => 'ç¦¥',
  '¶R' => 'ç¦¦',
  '¶S' => 'ç¦¨',
  '¶T' => 'ç¦©',
  '¶U' => 'ç¦ª',
  '¶V' => 'ç¦«',
  '¶W' => 'ç¦¬',
  '¶X' => 'ç¦­',
  '¶Y' => 'ç¦®',
  '¶Z' => 'ç¦¯',
  '¶[' => 'ç¦°',
  '¶\\' => 'ç¦±',
  '¶]' => 'ç¦²',
  '¶^' => 'ç¦´',
  '¶_' => 'ç¦µ',
  '¶`' => 'ç¦¶',
  '¶a' => 'ç¦·',
  '¶b' => 'ç¦¸',
  '¶c' => 'ç¦¼',
  '¶d' => 'ç¦¿',
  '¶e' => 'ç§‚',
  '¶f' => 'ç§„',
  '¶g' => 'ç§…',
  '¶h' => 'ç§‡',
  '¶i' => 'ç§ˆ',
  '¶j' => 'ç§Š',
  '¶k' => 'ç§Œ',
  '¶l' => 'ç§Ž',
  '¶m' => 'ç§�',
  '¶n' => 'ç§�',
  '¶o' => 'ç§“',
  '¶p' => 'ç§”',
  '¶q' => 'ç§–',
  '¶r' => 'ç§—',
  '¶s' => 'ç§™',
  '¶t' => 'ç§š',
  '¶u' => 'ç§›',
  '¶v' => 'ç§œ',
  '¶w' => 'ç§�',
  '¶x' => 'ç§ž',
  '¶y' => 'ç§ ',
  '¶z' => 'ç§¡',
  '¶{' => 'ç§¢',
  '¶|' => 'ç§¥',
  '¶}' => 'ç§¨',
  '¶~' => 'ç§ª',
  '¶€' => 'ç§¬',
  '¶�' => 'ç§®',
  '¶‚' => 'ç§±',
  '¶ƒ' => 'ç§²',
  '¶„' => 'ç§³',
  '¶…' => 'ç§´',
  '¶†' => 'ç§µ',
  '¶‡' => 'ç§¶',
  '¶ˆ' => 'ç§·',
  '¶‰' => 'ç§¹',
  '¶Š' => 'ç§º',
  '¶‹' => 'ç§¼',
  '¶Œ' => 'ç§¾',
  '¶�' => 'ç§¿',
  '¶Ž' => 'ç¨�',
  '¶�' => 'ç¨„',
  '¶�' => 'ç¨…',
  '¶‘' => 'ç¨‡',
  '¶’' => 'ç¨ˆ',
  '¶“' => 'ç¨‰',
  '¶”' => 'ç¨Š',
  '¶•' => 'ç¨Œ',
  '¶–' => 'ç¨�',
  '¶—' => 'ç¨�',
  '¶˜' => 'ç¨‘',
  '¶™' => 'ç¨’',
  '¶š' => 'ç¨“',
  '¶›' => 'ç¨•',
  '¶œ' => 'ç¨–',
  '¶�' => 'ç¨˜',
  '¶ž' => 'ç¨™',
  '¶Ÿ' => 'ç¨›',
  '¶ ' => 'ç¨œ',
  '¶¡' => 'ä¸�',
  '¶¢' => 'ç›¯',
  '¶£' => 'å�®',
  '¶¤' => 'é’‰',
  '¶¥' => 'é¡¶',
  '¶¦' => 'é¼Ž',
  '¶§' => 'é”­',
  '¶¨' => 'å®š',
  '¶©' => 'è®¢',
  '¶ª' => 'ä¸¢',
  '¶«' => 'ä¸œ',
  '¶¬' => 'å†¬',
  '¶­' => 'è‘£',
  '¶®' => 'æ‡‚',
  '¶¯' => 'åŠ¨',
  '¶°' => 'æ ‹',
  '¶±' => 'ä¾—',
  '¶²' => 'æ�«',
  '¶³' => 'å†»',
  '¶´' => 'æ´ž',
  '¶µ' => 'å…œ',
  '¶¶' => 'æŠ–',
  '¶·' => 'æ–—',
  '¶¸' => 'é™¡',
  '¶¹' => 'è±†',
  '¶º' => 'é€—',
  '¶»' => 'ç—˜',
  '¶¼' => 'éƒ½',
  '¶½' => 'ç�£',
  '¶¾' => 'æ¯’',
  '¶¿' => 'çŠŠ',
  '¶À' => 'ç‹¬',
  '¶Á' => 'è¯»',
  '¶Â' => 'å µ',
  '¶Ã' => 'ç�¹',
  '¶Ä' => 'èµŒ',
  '¶Å' => 'æ�œ',
  '¶Æ' => 'é•€',
  '¶Ç' => 'è‚š',
  '¶È' => 'åº¦',
  '¶É' => 'æ¸¡',
  '¶Ê' => 'å¦’',
  '¶Ë' => 'ç«¯',
  '¶Ì' => 'çŸ­',
  '¶Í' => 'é”»',
  '¶Î' => 'æ®µ',
  '¶Ï' => 'æ–­',
  '¶Ð' => 'ç¼Ž',
  '¶Ñ' => 'å †',
  '¶Ò' => 'å…‘',
  '¶Ó' => 'é˜Ÿ',
  '¶Ô' => 'å¯¹',
  '¶Õ' => 'å¢©',
  '¶Ö' => 'å�¨',
  '¶×' => 'è¹²',
  '¶Ø' => 'æ•¦',
  '¶Ù' => 'é¡¿',
  '¶Ú' => 'å›¤',
  '¶Û' => 'é’�',
  '¶Ü' => 'ç›¾',
  '¶Ý' => 'é��',
  '¶Þ' => 'æŽ‡',
  '¶ß' => 'å“†',
  '¶à' => 'å¤š',
  '¶á' => 'å¤º',
  '¶â' => 'åž›',
  '¶ã' => 'èº²',
  '¶ä' => 'æœµ',
  '¶å' => 'è·º',
  '¶æ' => 'èˆµ',
  '¶ç' => 'å‰�',
  '¶è' => 'æƒ°',
  '¶é' => 'å •',
  '¶ê' => 'è›¾',
  '¶ë' => 'å³¨',
  '¶ì' => 'é¹…',
  '¶í' => 'ä¿„',
  '¶î' => 'é¢�',
  '¶ï' => 'è®¹',
  '¶ð' => 'å¨¥',
  '¶ñ' => 'æ�¶',
  '¶ò' => 'åŽ„',
  '¶ó' => 'æ‰¼',
  '¶ô' => 'é��',
  '¶õ' => 'é„‚',
  '¶ö' => 'é¥¿',
  '¶÷' => 'æ�©',
  '¶ø' => 'è€Œ',
  '¶ù' => 'å„¿',
  '¶ú' => 'è€³',
  '¶û' => 'å°”',
  '¶ü' => 'é¥µ',
  '¶ý' => 'æ´±',
  '¶þ' => 'äºŒ',
  '·@' => 'ç¨�',
  '·A' => 'ç¨Ÿ',
  '·B' => 'ç¨¡',
  '·C' => 'ç¨¢',
  '·D' => 'ç¨¤',
  '·E' => 'ç¨¥',
  '·F' => 'ç¨¦',
  '·G' => 'ç¨§',
  '·H' => 'ç¨¨',
  '·I' => 'ç¨©',
  '·J' => 'ç¨ª',
  '·K' => 'ç¨«',
  '·L' => 'ç¨¬',
  '·M' => 'ç¨­',
  '·N' => 'ç¨®',
  '·O' => 'ç¨¯',
  '·P' => 'ç¨°',
  '·Q' => 'ç¨±',
  '·R' => 'ç¨²',
  '·S' => 'ç¨´',
  '·T' => 'ç¨µ',
  '·U' => 'ç¨¶',
  '·V' => 'ç¨¸',
  '·W' => 'ç¨º',
  '·X' => 'ç¨¾',
  '·Y' => 'ç©€',
  '·Z' => 'ç©�',
  '·[' => 'ç©‚',
  '·\\' => 'ç©ƒ',
  '·]' => 'ç©„',
  '·^' => 'ç©…',
  '·_' => 'ç©‡',
  '·`' => 'ç©ˆ',
  '·a' => 'ç©‰',
  '·b' => 'ç©Š',
  '·c' => 'ç©‹',
  '·d' => 'ç©Œ',
  '·e' => 'ç©�',
  '·f' => 'ç©Ž',
  '·g' => 'ç©�',
  '·h' => 'ç©�',
  '·i' => 'ç©’',
  '·j' => 'ç©“',
  '·k' => 'ç©”',
  '·l' => 'ç©•',
  '·m' => 'ç©–',
  '·n' => 'ç©˜',
  '·o' => 'ç©™',
  '·p' => 'ç©š',
  '·q' => 'ç©›',
  '·r' => 'ç©œ',
  '·s' => 'ç©�',
  '·t' => 'ç©ž',
  '·u' => 'ç©Ÿ',
  '·v' => 'ç© ',
  '·w' => 'ç©¡',
  '·x' => 'ç©¢',
  '·y' => 'ç©£',
  '·z' => 'ç©¤',
  '·{' => 'ç©¥',
  '·|' => 'ç©¦',
  '·}' => 'ç©§',
  '·~' => 'ç©¨',
  '·€' => 'ç©©',
  '·�' => 'ç©ª',
  '·‚' => 'ç©«',
  '·ƒ' => 'ç©¬',
  '·„' => 'ç©­',
  '·…' => 'ç©®',
  '·†' => 'ç©¯',
  '·‡' => 'ç©±',
  '·ˆ' => 'ç©²',
  '·‰' => 'ç©³',
  '·Š' => 'ç©µ',
  '·‹' => 'ç©»',
  '·Œ' => 'ç©¼',
  '·�' => 'ç©½',
  '·Ž' => 'ç©¾',
  '·�' => 'çª‚',
  '·�' => 'çª…',
  '·‘' => 'çª‡',
  '·’' => 'çª‰',
  '·“' => 'çªŠ',
  '·”' => 'çª‹',
  '·•' => 'çªŒ',
  '·–' => 'çªŽ',
  '·—' => 'çª�',
  '·˜' => 'çª�',
  '·™' => 'çª“',
  '·š' => 'çª”',
  '·›' => 'çª™',
  '·œ' => 'çªš',
  '·�' => 'çª›',
  '·ž' => 'çªž',
  '·Ÿ' => 'çª¡',
  '· ' => 'çª¢',
  '·¡' => 'è´°',
  '·¢' => 'å�‘',
  '·£' => 'ç½š',
  '·¤' => 'ç­�',
  '·¥' => 'ä¼�',
  '·¦' => 'ä¹�',
  '·§' => 'é˜€',
  '·¨' => 'æ³•',
  '·©' => 'ç��',
  '·ª' => 'è—©',
  '·«' => 'å¸†',
  '·¬' => 'ç•ª',
  '·­' => 'ç¿»',
  '·®' => 'æ¨Š',
  '·¯' => 'çŸ¾',
  '·°' => 'é’’',
  '·±' => 'ç¹�',
  '·²' => 'å‡¡',
  '·³' => 'çƒ¦',
  '·´' => 'å��',
  '·µ' => 'è¿”',
  '·¶' => 'èŒƒ',
  '··' => 'è´©',
  '·¸' => 'çŠ¯',
  '·¹' => 'é¥­',
  '·º' => 'æ³›',
  '·»' => 'å�Š',
  '·¼' => 'èŠ³',
  '·½' => 'æ–¹',
  '·¾' => 'è‚ª',
  '·¿' => 'æˆ¿',
  '·À' => 'é˜²',
  '·Á' => 'å¦¨',
  '·Â' => 'ä»¿',
  '·Ã' => 'è®¿',
  '·Ä' => 'çºº',
  '·Å' => 'æ”¾',
  '·Æ' => 'è�²',
  '·Ç' => 'é�ž',
  '·È' => 'å•¡',
  '·É' => 'é£ž',
  '·Ê' => 'è‚¥',
  '·Ë' => 'åŒª',
  '·Ì' => 'è¯½',
  '·Í' => 'å� ',
  '·Î' => 'è‚º',
  '·Ï' => 'åºŸ',
  '·Ð' => 'æ²¸',
  '·Ñ' => 'è´¹',
  '·Ò' => 'èŠ¬',
  '·Ó' => 'é…š',
  '·Ô' => 'å�©',
  '·Õ' => 'æ°›',
  '·Ö' => 'åˆ†',
  '·×' => 'çº·',
  '·Ø' => 'å�Ÿ',
  '·Ù' => 'ç„š',
  '·Ú' => 'æ±¾',
  '·Û' => 'ç²‰',
  '·Ü' => 'å¥‹',
  '·Ý' => 'ä»½',
  '·Þ' => 'å¿¿',
  '·ß' => 'æ„¤',
  '·à' => 'ç²ª',
  '·á' => 'ä¸°',
  '·â' => 'å°�',
  '·ã' => 'æž«',
  '·ä' => 'èœ‚',
  '·å' => 'å³°',
  '·æ' => 'é”‹',
  '·ç' => 'é£Ž',
  '·è' => 'ç–¯',
  '·é' => 'çƒ½',
  '·ê' => 'é€¢',
  '·ë' => 'å†¯',
  '·ì' => 'ç¼�',
  '·í' => 'è®½',
  '·î' => 'å¥‰',
  '·ï' => 'å‡¤',
  '·ð' => 'ä½›',
  '·ñ' => 'å�¦',
  '·ò' => 'å¤«',
  '·ó' => 'æ•·',
  '·ô' => 'è‚¤',
  '·õ' => 'å­µ',
  '·ö' => 'æ‰¶',
  '·÷' => 'æ‹‚',
  '·ø' => 'è¾�',
  '·ù' => 'å¹…',
  '·ú' => 'æ°Ÿ',
  '·û' => 'ç¬¦',
  '·ü' => 'ä¼�',
  '·ý' => 'ä¿˜',
  '·þ' => 'æœ�',
  '¸@' => 'çª£',
  '¸A' => 'çª¤',
  '¸B' => 'çª§',
  '¸C' => 'çª©',
  '¸D' => 'çªª',
  '¸E' => 'çª«',
  '¸F' => 'çª®',
  '¸G' => 'çª¯',
  '¸H' => 'çª°',
  '¸I' => 'çª±',
  '¸J' => 'çª²',
  '¸K' => 'çª´',
  '¸L' => 'çªµ',
  '¸M' => 'çª¶',
  '¸N' => 'çª·',
  '¸O' => 'çª¸',
  '¸P' => 'çª¹',
  '¸Q' => 'çªº',
  '¸R' => 'çª»',
  '¸S' => 'çª¼',
  '¸T' => 'çª½',
  '¸U' => 'çª¾',
  '¸V' => 'ç«€',
  '¸W' => 'ç«�',
  '¸X' => 'ç«‚',
  '¸Y' => 'ç«ƒ',
  '¸Z' => 'ç«„',
  '¸[' => 'ç«…',
  '¸\\' => 'ç«†',
  '¸]' => 'ç«‡',
  '¸^' => 'ç«ˆ',
  '¸_' => 'ç«‰',
  '¸`' => 'ç«Š',
  '¸a' => 'ç«Œ',
  '¸b' => 'ç«�',
  '¸c' => 'ç«Ž',
  '¸d' => 'ç«�',
  '¸e' => 'ç«�',
  '¸f' => 'ç«‘',
  '¸g' => 'ç«’',
  '¸h' => 'ç«“',
  '¸i' => 'ç«”',
  '¸j' => 'ç«•',
  '¸k' => 'ç«—',
  '¸l' => 'ç«˜',
  '¸m' => 'ç«š',
  '¸n' => 'ç«›',
  '¸o' => 'ç«œ',
  '¸p' => 'ç«�',
  '¸q' => 'ç«¡',
  '¸r' => 'ç«¢',
  '¸s' => 'ç«¤',
  '¸t' => 'ç«§',
  '¸u' => 'ç«¨',
  '¸v' => 'ç«©',
  '¸w' => 'ç«ª',
  '¸x' => 'ç««',
  '¸y' => 'ç«¬',
  '¸z' => 'ç«®',
  '¸{' => 'ç«°',
  '¸|' => 'ç«±',
  '¸}' => 'ç«²',
  '¸~' => 'ç«³',
  '¸€' => 'ç«´',
  '¸�' => 'ç«µ',
  '¸‚' => 'ç«¶',
  '¸ƒ' => 'ç«·',
  '¸„' => 'ç«¸',
  '¸…' => 'ç«»',
  '¸†' => 'ç«¼',
  '¸‡' => 'ç«¾',
  '¸ˆ' => 'ç¬€',
  '¸‰' => 'ç¬�',
  '¸Š' => 'ç¬‚',
  '¸‹' => 'ç¬…',
  '¸Œ' => 'ç¬‡',
  '¸�' => 'ç¬‰',
  '¸Ž' => 'ç¬Œ',
  '¸�' => 'ç¬�',
  '¸�' => 'ç¬Ž',
  '¸‘' => 'ç¬�',
  '¸’' => 'ç¬’',
  '¸“' => 'ç¬“',
  '¸”' => 'ç¬–',
  '¸•' => 'ç¬—',
  '¸–' => 'ç¬˜',
  '¸—' => 'ç¬š',
  '¸˜' => 'ç¬œ',
  '¸™' => 'ç¬�',
  '¸š' => 'ç¬Ÿ',
  '¸›' => 'ç¬¡',
  '¸œ' => 'ç¬¢',
  '¸�' => 'ç¬£',
  '¸ž' => 'ç¬§',
  '¸Ÿ' => 'ç¬©',
  '¸ ' => 'ç¬­',
  '¸¡' => 'æµ®',
  '¸¢' => 'æ¶ª',
  '¸£' => 'ç¦�',
  '¸¤' => 'è¢±',
  '¸¥' => 'å¼—',
  '¸¦' => 'ç”«',
  '¸§' => 'æŠš',
  '¸¨' => 'è¾…',
  '¸©' => 'ä¿¯',
  '¸ª' => 'é‡œ',
  '¸«' => 'æ–§',
  '¸¬' => 'è„¯',
  '¸­' => 'è…‘',
  '¸®' => 'åºœ',
  '¸¯' => 'è…�',
  '¸°' => 'èµ´',
  '¸±' => 'å‰¯',
  '¸²' => 'è¦†',
  '¸³' => 'èµ‹',
  '¸´' => 'å¤�',
  '¸µ' => 'å‚…',
  '¸¶' => 'ä»˜',
  '¸·' => 'é˜œ',
  '¸¸' => 'çˆ¶',
  '¸¹' => 'è…¹',
  '¸º' => 'è´Ÿ',
  '¸»' => 'å¯Œ',
  '¸¼' => 'è®£',
  '¸½' => 'é™„',
  '¸¾' => 'å¦‡',
  '¸¿' => 'ç¼š',
  '¸À' => 'å’�',
  '¸Á' => 'å™¶',
  '¸Â' => 'å˜Ž',
  '¸Ã' => 'è¯¥',
  '¸Ä' => 'æ”¹',
  '¸Å' => 'æ¦‚',
  '¸Æ' => 'é’™',
  '¸Ç' => 'ç›–',
  '¸È' => 'æº‰',
  '¸É' => 'å¹²',
  '¸Ê' => 'ç”˜',
  '¸Ë' => 'æ�†',
  '¸Ì' => 'æŸ‘',
  '¸Í' => 'ç«¿',
  '¸Î' => 'è‚�',
  '¸Ï' => 'èµ¶',
  '¸Ð' => 'æ„Ÿ',
  '¸Ñ' => 'ç§†',
  '¸Ò' => 'æ•¢',
  '¸Ó' => 'èµ£',
  '¸Ô' => 'å†ˆ',
  '¸Õ' => 'åˆš',
  '¸Ö' => 'é’¢',
  '¸×' => 'ç¼¸',
  '¸Ø' => 'è‚›',
  '¸Ù' => 'çº²',
  '¸Ú' => 'å²—',
  '¸Û' => 'æ¸¯',
  '¸Ü' => 'æ� ',
  '¸Ý' => 'ç¯™',
  '¸Þ' => 'çš‹',
  '¸ß' => 'é«˜',
  '¸à' => 'è†�',
  '¸á' => 'ç¾”',
  '¸â' => 'ç³•',
  '¸ã' => 'æ�ž',
  '¸ä' => 'é•�',
  '¸å' => 'ç¨¿',
  '¸æ' => 'å‘Š',
  '¸ç' => 'å“¥',
  '¸è' => 'æ­Œ',
  '¸é' => 'æ��',
  '¸ê' => 'æˆˆ',
  '¸ë' => 'é¸½',
  '¸ì' => 'èƒ³',
  '¸í' => 'ç–™',
  '¸î' => 'å‰²',
  '¸ï' => 'é�©',
  '¸ð' => 'è‘›',
  '¸ñ' => 'æ ¼',
  '¸ò' => 'è›¤',
  '¸ó' => 'é˜�',
  '¸ô' => 'éš”',
  '¸õ' => 'é“¬',
  '¸ö' => 'ä¸ª',
  '¸÷' => 'å�„',
  '¸ø' => 'ç»™',
  '¸ù' => 'æ ¹',
  '¸ú' => 'è·Ÿ',
  '¸û' => 'è€•',
  '¸ü' => 'æ›´',
  '¸ý' => 'åºš',
  '¸þ' => 'ç¾¹',
  '¹@' => 'ç¬¯',
  '¹A' => 'ç¬°',
  '¹B' => 'ç¬²',
  '¹C' => 'ç¬´',
  '¹D' => 'ç¬µ',
  '¹E' => 'ç¬¶',
  '¹F' => 'ç¬·',
  '¹G' => 'ç¬¹',
  '¹H' => 'ç¬»',
  '¹I' => 'ç¬½',
  '¹J' => 'ç¬¿',
  '¹K' => 'ç­€',
  '¹L' => 'ç­�',
  '¹M' => 'ç­‚',
  '¹N' => 'ç­ƒ',
  '¹O' => 'ç­„',
  '¹P' => 'ç­†',
  '¹Q' => 'ç­ˆ',
  '¹R' => 'ç­Š',
  '¹S' => 'ç­�',
  '¹T' => 'ç­Ž',
  '¹U' => 'ç­“',
  '¹V' => 'ç­•',
  '¹W' => 'ç­—',
  '¹X' => 'ç­™',
  '¹Y' => 'ç­œ',
  '¹Z' => 'ç­ž',
  '¹[' => 'ç­Ÿ',
  '¹\\' => 'ç­¡',
  '¹]' => 'ç­£',
  '¹^' => 'ç­¤',
  '¹_' => 'ç­¥',
  '¹`' => 'ç­¦',
  '¹a' => 'ç­§',
  '¹b' => 'ç­¨',
  '¹c' => 'ç­©',
  '¹d' => 'ç­ª',
  '¹e' => 'ç­«',
  '¹f' => 'ç­¬',
  '¹g' => 'ç­­',
  '¹h' => 'ç­¯',
  '¹i' => 'ç­°',
  '¹j' => 'ç­³',
  '¹k' => 'ç­´',
  '¹l' => 'ç­¶',
  '¹m' => 'ç­¸',
  '¹n' => 'ç­º',
  '¹o' => 'ç­¼',
  '¹p' => 'ç­½',
  '¹q' => 'ç­¿',
  '¹r' => 'ç®�',
  '¹s' => 'ç®‚',
  '¹t' => 'ç®ƒ',
  '¹u' => 'ç®„',
  '¹v' => 'ç®†',
  '¹w' => 'ç®‡',
  '¹x' => 'ç®ˆ',
  '¹y' => 'ç®‰',
  '¹z' => 'ç®Š',
  '¹{' => 'ç®‹',
  '¹|' => 'ç®Œ',
  '¹}' => 'ç®Ž',
  '¹~' => 'ç®�',
  '¹€' => 'ç®‘',
  '¹�' => 'ç®’',
  '¹‚' => 'ç®“',
  '¹ƒ' => 'ç®–',
  '¹„' => 'ç®˜',
  '¹…' => 'ç®™',
  '¹†' => 'ç®š',
  '¹‡' => 'ç®›',
  '¹ˆ' => 'ç®ž',
  '¹‰' => 'ç®Ÿ',
  '¹Š' => 'ç® ',
  '¹‹' => 'ç®£',
  '¹Œ' => 'ç®¤',
  '¹�' => 'ç®¥',
  '¹Ž' => 'ç®®',
  '¹�' => 'ç®¯',
  '¹�' => 'ç®°',
  '¹‘' => 'ç®²',
  '¹’' => 'ç®³',
  '¹“' => 'ç®µ',
  '¹”' => 'ç®¶',
  '¹•' => 'ç®·',
  '¹–' => 'ç®¹',
  '¹—' => 'ç®º',
  '¹˜' => 'ç®»',
  '¹™' => 'ç®¼',
  '¹š' => 'ç®½',
  '¹›' => 'ç®¾',
  '¹œ' => 'ç®¿',
  '¹�' => 'ç¯€',
  '¹ž' => 'ç¯‚',
  '¹Ÿ' => 'ç¯ƒ',
  '¹ ' => 'ç¯„',
  '¹¡' => 'åŸ‚',
  '¹¢' => 'è€¿',
  '¹£' => 'æ¢—',
  '¹¤' => 'å·¥',
  '¹¥' => 'æ”»',
  '¹¦' => 'åŠŸ',
  '¹§' => 'æ�­',
  '¹¨' => 'é¾š',
  '¹©' => 'ä¾›',
  '¹ª' => 'èº¬',
  '¹«' => 'å…¬',
  '¹¬' => 'å®«',
  '¹­' => 'å¼“',
  '¹®' => 'å·©',
  '¹¯' => 'æ±ž',
  '¹°' => 'æ‹±',
  '¹±' => 'è´¡',
  '¹²' => 'å…±',
  '¹³' => 'é’©',
  '¹´' => 'å‹¾',
  '¹µ' => 'æ²Ÿ',
  '¹¶' => 'è‹Ÿ',
  '¹·' => 'ç‹—',
  '¹¸' => 'åž¢',
  '¹¹' => 'æž„',
  '¹º' => 'è´­',
  '¹»' => 'å¤Ÿ',
  '¹¼' => 'è¾œ',
  '¹½' => 'è�‡',
  '¹¾' => 'å’•',
  '¹¿' => 'ç®�',
  '¹À' => 'ä¼°',
  '¹Á' => 'æ²½',
  '¹Â' => 'å­¤',
  '¹Ã' => 'å§‘',
  '¹Ä' => 'é¼“',
  '¹Å' => 'å�¤',
  '¹Æ' => 'è›Š',
  '¹Ç' => 'éª¨',
  '¹È' => 'è°·',
  '¹É' => 'è‚¡',
  '¹Ê' => 'æ•…',
  '¹Ë' => 'é¡¾',
  '¹Ì' => 'å›º',
  '¹Í' => 'é›‡',
  '¹Î' => 'åˆ®',
  '¹Ï' => 'ç“œ',
  '¹Ð' => 'å‰�',
  '¹Ñ' => 'å¯¡',
  '¹Ò' => 'æŒ‚',
  '¹Ó' => 'è¤‚',
  '¹Ô' => 'ä¹–',
  '¹Õ' => 'æ‹�',
  '¹Ö' => 'æ€ª',
  '¹×' => 'æ£º',
  '¹Ø' => 'å…³',
  '¹Ù' => 'å®˜',
  '¹Ú' => 'å† ',
  '¹Û' => 'è§‚',
  '¹Ü' => 'ç®¡',
  '¹Ý' => 'é¦†',
  '¹Þ' => 'ç½�',
  '¹ß' => 'æƒ¯',
  '¹à' => 'ç�Œ',
  '¹á' => 'è´¯',
  '¹â' => 'å…‰',
  '¹ã' => 'å¹¿',
  '¹ä' => 'é€›',
  '¹å' => 'ç‘°',
  '¹æ' => 'è§„',
  '¹ç' => 'åœ­',
  '¹è' => 'ç¡…',
  '¹é' => 'å½’',
  '¹ê' => 'é¾Ÿ',
  '¹ë' => 'é—º',
  '¹ì' => 'è½¨',
  '¹í' => 'é¬¼',
  '¹î' => 'è¯¡',
  '¹ï' => 'ç™¸',
  '¹ð' => 'æ¡‚',
  '¹ñ' => 'æŸœ',
  '¹ò' => 'è·ª',
  '¹ó' => 'è´µ',
  '¹ô' => 'åˆ½',
  '¹õ' => 'è¾Š',
  '¹ö' => 'æ»š',
  '¹÷' => 'æ£�',
  '¹ø' => 'é”…',
  '¹ù' => 'éƒ­',
  '¹ú' => 'å›½',
  '¹û' => 'æžœ',
  '¹ü' => 'è£¹',
  '¹ý' => 'è¿‡',
  '¹þ' => 'å“ˆ',
  'º@' => 'ç¯…',
  'ºA' => 'ç¯ˆ',
  'ºB' => 'ç¯‰',
  'ºC' => 'ç¯Š',
  'ºD' => 'ç¯‹',
  'ºE' => 'ç¯�',
  'ºF' => 'ç¯Ž',
  'ºG' => 'ç¯�',
  'ºH' => 'ç¯�',
  'ºI' => 'ç¯’',
  'ºJ' => 'ç¯”',
  'ºK' => 'ç¯•',
  'ºL' => 'ç¯–',
  'ºM' => 'ç¯—',
  'ºN' => 'ç¯˜',
  'ºO' => 'ç¯›',
  'ºP' => 'ç¯œ',
  'ºQ' => 'ç¯ž',
  'ºR' => 'ç¯Ÿ',
  'ºS' => 'ç¯ ',
  'ºT' => 'ç¯¢',
  'ºU' => 'ç¯£',
  'ºV' => 'ç¯¤',
  'ºW' => 'ç¯§',
  'ºX' => 'ç¯¨',
  'ºY' => 'ç¯©',
  'ºZ' => 'ç¯«',
  'º[' => 'ç¯¬',
  'º\\' => 'ç¯­',
  'º]' => 'ç¯¯',
  'º^' => 'ç¯°',
  'º_' => 'ç¯²',
  'º`' => 'ç¯³',
  'ºa' => 'ç¯´',
  'ºb' => 'ç¯µ',
  'ºc' => 'ç¯¶',
  'ºd' => 'ç¯¸',
  'ºe' => 'ç¯¹',
  'ºf' => 'ç¯º',
  'ºg' => 'ç¯»',
  'ºh' => 'ç¯½',
  'ºi' => 'ç¯¿',
  'ºj' => 'ç°€',
  'ºk' => 'ç°�',
  'ºl' => 'ç°‚',
  'ºm' => 'ç°ƒ',
  'ºn' => 'ç°„',
  'ºo' => 'ç°…',
  'ºp' => 'ç°†',
  'ºq' => 'ç°ˆ',
  'ºr' => 'ç°‰',
  'ºs' => 'ç°Š',
  'ºt' => 'ç°�',
  'ºu' => 'ç°Ž',
  'ºv' => 'ç°�',
  'ºw' => 'ç°‘',
  'ºx' => 'ç°’',
  'ºy' => 'ç°“',
  'ºz' => 'ç°”',
  'º{' => 'ç°•',
  'º|' => 'ç°—',
  'º}' => 'ç°˜',
  'º~' => 'ç°™',
  'º€' => 'ç°š',
  'º�' => 'ç°›',
  'º‚' => 'ç°œ',
  'ºƒ' => 'ç°�',
  'º„' => 'ç°ž',
  'º…' => 'ç° ',
  'º†' => 'ç°¡',
  'º‡' => 'ç°¢',
  'ºˆ' => 'ç°£',
  'º‰' => 'ç°¤',
  'ºŠ' => 'ç°¥',
  'º‹' => 'ç°¨',
  'ºŒ' => 'ç°©',
  'º�' => 'ç°«',
  'ºŽ' => 'ç°¬',
  'º�' => 'ç°­',
  'º�' => 'ç°®',
  'º‘' => 'ç°¯',
  'º’' => 'ç°°',
  'º“' => 'ç°±',
  'º”' => 'ç°²',
  'º•' => 'ç°³',
  'º–' => 'ç°´',
  'º—' => 'ç°µ',
  'º˜' => 'ç°¶',
  'º™' => 'ç°·',
  'ºš' => 'ç°¹',
  'º›' => 'ç°º',
  'ºœ' => 'ç°»',
  'º�' => 'ç°¼',
  'ºž' => 'ç°½',
  'ºŸ' => 'ç°¾',
  'º ' => 'ç±‚',
  'º¡' => 'éª¸',
  'º¢' => 'å­©',
  'º£' => 'æµ·',
  'º¤' => 'æ°¦',
  'º¥' => 'äº¥',
  'º¦' => 'å®³',
  'º§' => 'éª‡',
  'º¨' => 'é…£',
  'º©' => 'æ†¨',
  'ºª' => 'é‚¯',
  'º«' => 'éŸ©',
  'º¬' => 'å�«',
  'º­' => 'æ¶µ',
  'º®' => 'å¯’',
  'º¯' => 'å‡½',
  'º°' => 'å–Š',
  'º±' => 'ç½•',
  'º²' => 'ç¿°',
  'º³' => 'æ’¼',
  'º´' => 'æ��',
  'ºµ' => 'æ—±',
  'º¶' => 'æ†¾',
  'º·' => 'æ‚�',
  'º¸' => 'ç„Š',
  'º¹' => 'æ±—',
  'ºº' => 'æ±‰',
  'º»' => 'å¤¯',
  'º¼' => 'æ�­',
  'º½' => 'èˆª',
  'º¾' => 'å£•',
  'º¿' => 'åšŽ',
  'ºÀ' => 'è±ª',
  'ºÁ' => 'æ¯«',
  'ºÂ' => 'éƒ�',
  'ºÃ' => 'å¥½',
  'ºÄ' => 'è€—',
  'ºÅ' => 'å�·',
  'ºÆ' => 'æµ©',
  'ºÇ' => 'å‘µ',
  'ºÈ' => 'å–�',
  'ºÉ' => 'è�·',
  'ºÊ' => 'è��',
  'ºË' => 'æ ¸',
  'ºÌ' => 'ç¦¾',
  'ºÍ' => 'å’Œ',
  'ºÎ' => 'ä½•',
  'ºÏ' => 'å�ˆ',
  'ºÐ' => 'ç›’',
  'ºÑ' => 'è²‰',
  'ºÒ' => 'é˜‚',
  'ºÓ' => 'æ²³',
  'ºÔ' => 'æ¶¸',
  'ºÕ' => 'èµ«',
  'ºÖ' => 'è¤�',
  'º×' => 'é¹¤',
  'ºØ' => 'è´º',
  'ºÙ' => 'å˜¿',
  'ºÚ' => 'é»‘',
  'ºÛ' => 'ç—•',
  'ºÜ' => 'å¾ˆ',
  'ºÝ' => 'ç‹ ',
  'ºÞ' => 'æ�¨',
  'ºß' => 'å“¼',
  'ºà' => 'äº¨',
  'ºá' => 'æ¨ª',
  'ºâ' => 'è¡¡',
  'ºã' => 'æ�’',
  'ºä' => 'è½°',
  'ºå' => 'å“„',
  'ºæ' => 'çƒ˜',
  'ºç' => 'è™¹',
  'ºè' => 'é¸¿',
  'ºé' => 'æ´ª',
  'ºê' => 'å®�',
  'ºë' => 'å¼˜',
  'ºì' => 'çº¢',
  'ºí' => 'å–‰',
  'ºî' => 'ä¾¯',
  'ºï' => 'çŒ´',
  'ºð' => 'å�¼',
  'ºñ' => 'åŽš',
  'ºò' => 'å€™',
  'ºó' => 'å�Ž',
  'ºô' => 'å‘¼',
  'ºõ' => 'ä¹Ž',
  'ºö' => 'å¿½',
  'º÷' => 'ç‘š',
  'ºø' => 'å£¶',
  'ºù' => 'è‘«',
  'ºú' => 'èƒ¡',
  'ºû' => 'è�´',
  'ºü' => 'ç‹�',
  'ºý' => 'ç³Š',
  'ºþ' => 'æ¹–',
  '»@' => 'ç±ƒ',
  '»A' => 'ç±„',
  '»B' => 'ç±…',
  '»C' => 'ç±†',
  '»D' => 'ç±‡',
  '»E' => 'ç±ˆ',
  '»F' => 'ç±‰',
  '»G' => 'ç±Š',
  '»H' => 'ç±‹',
  '»I' => 'ç±Œ',
  '»J' => 'ç±Ž',
  '»K' => 'ç±�',
  '»L' => 'ç±�',
  '»M' => 'ç±‘',
  '»N' => 'ç±’',
  '»O' => 'ç±“',
  '»P' => 'ç±”',
  '»Q' => 'ç±•',
  '»R' => 'ç±–',
  '»S' => 'ç±—',
  '»T' => 'ç±˜',
  '»U' => 'ç±™',
  '»V' => 'ç±š',
  '»W' => 'ç±›',
  '»X' => 'ç±œ',
  '»Y' => 'ç±�',
  '»Z' => 'ç±ž',
  '»[' => 'ç±Ÿ',
  '»\\' => 'ç± ',
  '»]' => 'ç±¡',
  '»^' => 'ç±¢',
  '»_' => 'ç±£',
  '»`' => 'ç±¤',
  '»a' => 'ç±¥',
  '»b' => 'ç±¦',
  '»c' => 'ç±§',
  '»d' => 'ç±¨',
  '»e' => 'ç±©',
  '»f' => 'ç±ª',
  '»g' => 'ç±«',
  '»h' => 'ç±¬',
  '»i' => 'ç±­',
  '»j' => 'ç±®',
  '»k' => 'ç±¯',
  '»l' => 'ç±°',
  '»m' => 'ç±±',
  '»n' => 'ç±²',
  '»o' => 'ç±µ',
  '»p' => 'ç±¶',
  '»q' => 'ç±·',
  '»r' => 'ç±¸',
  '»s' => 'ç±¹',
  '»t' => 'ç±º',
  '»u' => 'ç±¾',
  '»v' => 'ç±¿',
  '»w' => 'ç²€',
  '»x' => 'ç²�',
  '»y' => 'ç²‚',
  '»z' => 'ç²ƒ',
  '»{' => 'ç²„',
  '»|' => 'ç²…',
  '»}' => 'ç²†',
  '»~' => 'ç²‡',
  '»€' => 'ç²ˆ',
  '»�' => 'ç²Š',
  '»‚' => 'ç²‹',
  '»ƒ' => 'ç²Œ',
  '»„' => 'ç²�',
  '»…' => 'ç²Ž',
  '»†' => 'ç²�',
  '»‡' => 'ç²�',
  '»ˆ' => 'ç²“',
  '»‰' => 'ç²”',
  '»Š' => 'ç²–',
  '»‹' => 'ç²™',
  '»Œ' => 'ç²š',
  '»�' => 'ç²›',
  '»Ž' => 'ç² ',
  '»�' => 'ç²¡',
  '»�' => 'ç²£',
  '»‘' => 'ç²¦',
  '»’' => 'ç²§',
  '»“' => 'ç²¨',
  '»”' => 'ç²©',
  '»•' => 'ç²«',
  '»–' => 'ç²¬',
  '»—' => 'ç²­',
  '»˜' => 'ç²¯',
  '»™' => 'ç²°',
  '»š' => 'ç²´',
  '»›' => 'ç²µ',
  '»œ' => 'ç²¶',
  '»�' => 'ç²·',
  '»ž' => 'ç²¸',
  '»Ÿ' => 'ç²º',
  '» ' => 'ç²»',
  '»¡' => 'å¼§',
  '»¢' => 'è™Ž',
  '»£' => 'å”¬',
  '»¤' => 'æŠ¤',
  '»¥' => 'äº’',
  '»¦' => 'æ²ª',
  '»§' => 'æˆ·',
  '»¨' => 'èŠ±',
  '»©' => 'å“—',
  '»ª' => 'å�Ž',
  '»«' => 'çŒ¾',
  '»¬' => 'æ»‘',
  '»­' => 'ç”»',
  '»®' => 'åˆ’',
  '»¯' => 'åŒ–',
  '»°' => 'è¯�',
  '»±' => 'æ§�',
  '»²' => 'å¾Š',
  '»³' => 'æ€€',
  '»´' => 'æ·®',
  '»µ' => 'å��',
  '»¶' => 'æ¬¢',
  '»·' => 'çŽ¯',
  '»¸' => 'æ¡“',
  '»¹' => 'è¿˜',
  '»º' => 'ç¼“',
  '»»' => 'æ�¢',
  '»¼' => 'æ‚£',
  '»½' => 'å”¤',
  '»¾' => 'ç—ª',
  '»¿' => 'è±¢',
  '»À' => 'ç„•',
  '»Á' => 'æ¶£',
  '»Â' => 'å®¦',
  '»Ã' => 'å¹»',
  '»Ä' => 'è�’',
  '»Å' => 'æ…Œ',
  '»Æ' => 'é»„',
  '»Ç' => 'ç£º',
  '»È' => 'è�—',
  '»É' => 'ç°§',
  '»Ê' => 'çš‡',
  '»Ë' => 'å‡°',
  '»Ì' => 'æƒ¶',
  '»Í' => 'ç…Œ',
  '»Î' => 'æ™ƒ',
  '»Ï' => 'å¹Œ',
  '»Ð' => 'æ��',
  '»Ñ' => 'è°Ž',
  '»Ò' => 'ç�°',
  '»Ó' => 'æŒ¥',
  '»Ô' => 'è¾‰',
  '»Õ' => 'å¾½',
  '»Ö' => 'æ�¢',
  '»×' => 'è›”',
  '»Ø' => 'å›ž',
  '»Ù' => 'æ¯�',
  '»Ú' => 'æ‚”',
  '»Û' => 'æ…§',
  '»Ü' => 'å�‰',
  '»Ý' => 'æƒ ',
  '»Þ' => 'æ™¦',
  '»ß' => 'è´¿',
  '»à' => 'ç§½',
  '»á' => 'ä¼š',
  '»â' => 'çƒ©',
  '»ã' => 'æ±‡',
  '»ä' => 'è®³',
  '»å' => 'è¯²',
  '»æ' => 'ç»˜',
  '»ç' => 'è�¤',
  '»è' => 'æ˜�',
  '»é' => 'å©š',
  '»ê' => 'é­‚',
  '»ë' => 'æµ‘',
  '»ì' => 'æ··',
  '»í' => 'è±�',
  '»î' => 'æ´»',
  '»ï' => 'ä¼™',
  '»ð' => 'ç�«',
  '»ñ' => 'èŽ·',
  '»ò' => 'æˆ–',
  '»ó' => 'æƒ‘',
  '»ô' => 'éœ�',
  '»õ' => 'è´§',
  '»ö' => 'ç¥¸',
  '»÷' => 'å‡»',
  '»ø' => 'åœ¾',
  '»ù' => 'åŸº',
  '»ú' => 'æœº',
  '»û' => 'ç•¸',
  '»ü' => 'ç¨½',
  '»ý' => 'ç§¯',
  '»þ' => 'ç®•',
  '¼@' => 'ç²¿',
  '¼A' => 'ç³€',
  '¼B' => 'ç³‚',
  '¼C' => 'ç³ƒ',
  '¼D' => 'ç³„',
  '¼E' => 'ç³†',
  '¼F' => 'ç³‰',
  '¼G' => 'ç³‹',
  '¼H' => 'ç³Ž',
  '¼I' => 'ç³�',
  '¼J' => 'ç³�',
  '¼K' => 'ç³‘',
  '¼L' => 'ç³’',
  '¼M' => 'ç³“',
  '¼N' => 'ç³”',
  '¼O' => 'ç³˜',
  '¼P' => 'ç³š',
  '¼Q' => 'ç³›',
  '¼R' => 'ç³�',
  '¼S' => 'ç³ž',
  '¼T' => 'ç³¡',
  '¼U' => 'ç³¢',
  '¼V' => 'ç³£',
  '¼W' => 'ç³¤',
  '¼X' => 'ç³¥',
  '¼Y' => 'ç³¦',
  '¼Z' => 'ç³§',
  '¼[' => 'ç³©',
  '¼\\' => 'ç³ª',
  '¼]' => 'ç³«',
  '¼^' => 'ç³¬',
  '¼_' => 'ç³­',
  '¼`' => 'ç³®',
  '¼a' => 'ç³°',
  '¼b' => 'ç³±',
  '¼c' => 'ç³²',
  '¼d' => 'ç³³',
  '¼e' => 'ç³´',
  '¼f' => 'ç³µ',
  '¼g' => 'ç³¶',
  '¼h' => 'ç³·',
  '¼i' => 'ç³¹',
  '¼j' => 'ç³º',
  '¼k' => 'ç³¼',
  '¼l' => 'ç³½',
  '¼m' => 'ç³¾',
  '¼n' => 'ç³¿',
  '¼o' => 'ç´€',
  '¼p' => 'ç´�',
  '¼q' => 'ç´‚',
  '¼r' => 'ç´ƒ',
  '¼s' => 'ç´„',
  '¼t' => 'ç´…',
  '¼u' => 'ç´†',
  '¼v' => 'ç´‡',
  '¼w' => 'ç´ˆ',
  '¼x' => 'ç´‰',
  '¼y' => 'ç´‹',
  '¼z' => 'ç´Œ',
  '¼{' => 'ç´�',
  '¼|' => 'ç´Ž',
  '¼}' => 'ç´�',
  '¼~' => 'ç´�',
  '¼€' => 'ç´‘',
  '¼�' => 'ç´’',
  '¼‚' => 'ç´“',
  '¼ƒ' => 'ç´”',
  '¼„' => 'ç´•',
  '¼…' => 'ç´–',
  '¼†' => 'ç´—',
  '¼‡' => 'ç´˜',
  '¼ˆ' => 'ç´™',
  '¼‰' => 'ç´š',
  '¼Š' => 'ç´›',
  '¼‹' => 'ç´œ',
  '¼Œ' => 'ç´�',
  '¼�' => 'ç´ž',
  '¼Ž' => 'ç´Ÿ',
  '¼�' => 'ç´¡',
  '¼�' => 'ç´£',
  '¼‘' => 'ç´¤',
  '¼’' => 'ç´¥',
  '¼“' => 'ç´¦',
  '¼”' => 'ç´¨',
  '¼•' => 'ç´©',
  '¼–' => 'ç´ª',
  '¼—' => 'ç´¬',
  '¼˜' => 'ç´­',
  '¼™' => 'ç´®',
  '¼š' => 'ç´°',
  '¼›' => 'ç´±',
  '¼œ' => 'ç´²',
  '¼�' => 'ç´³',
  '¼ž' => 'ç´´',
  '¼Ÿ' => 'ç´µ',
  '¼ ' => 'ç´¶',
  '¼¡' => 'è‚Œ',
  '¼¢' => 'é¥¥',
  '¼£' => 'è¿¹',
  '¼¤' => 'æ¿€',
  '¼¥' => 'è®¥',
  '¼¦' => 'é¸¡',
  '¼§' => 'å§¬',
  '¼¨' => 'ç»©',
  '¼©' => 'ç¼‰',
  '¼ª' => 'å�‰',
  '¼«' => 'æž�',
  '¼¬' => 'æ£˜',
  '¼­' => 'è¾‘',
  '¼®' => 'ç±�',
  '¼¯' => 'é›†',
  '¼°' => 'å�Š',
  '¼±' => 'æ€¥',
  '¼²' => 'ç–¾',
  '¼³' => 'æ±²',
  '¼´' => 'å�³',
  '¼µ' => 'å«‰',
  '¼¶' => 'çº§',
  '¼·' => 'æŒ¤',
  '¼¸' => 'å‡ ',
  '¼¹' => 'è„Š',
  '¼º' => 'å·±',
  '¼»' => 'è“Ÿ',
  '¼¼' => 'æŠ€',
  '¼½' => 'å†€',
  '¼¾' => 'å­£',
  '¼¿' => 'ä¼Ž',
  '¼À' => 'ç¥­',
  '¼Á' => 'å‰‚',
  '¼Â' => 'æ‚¸',
  '¼Ã' => 'æµŽ',
  '¼Ä' => 'å¯„',
  '¼Å' => 'å¯‚',
  '¼Æ' => 'è®¡',
  '¼Ç' => 'è®°',
  '¼È' => 'æ—¢',
  '¼É' => 'å¿Œ',
  '¼Ê' => 'é™…',
  '¼Ë' => 'å¦“',
  '¼Ì' => 'ç»§',
  '¼Í' => 'çºª',
  '¼Î' => 'å˜‰',
  '¼Ï' => 'æž·',
  '¼Ð' => 'å¤¹',
  '¼Ñ' => 'ä½³',
  '¼Ò' => 'å®¶',
  '¼Ó' => 'åŠ ',
  '¼Ô' => 'è�š',
  '¼Õ' => 'é¢Š',
  '¼Ö' => 'è´¾',
  '¼×' => 'ç”²',
  '¼Ø' => 'é’¾',
  '¼Ù' => 'å�‡',
  '¼Ú' => 'ç¨¼',
  '¼Û' => 'ä»·',
  '¼Ü' => 'æž¶',
  '¼Ý' => 'é©¾',
  '¼Þ' => 'å«�',
  '¼ß' => 'æ­¼',
  '¼à' => 'ç›‘',
  '¼á' => 'å�š',
  '¼â' => 'å°–',
  '¼ã' => 'ç¬º',
  '¼ä' => 'é—´',
  '¼å' => 'ç…Ž',
  '¼æ' => 'å…¼',
  '¼ç' => 'è‚©',
  '¼è' => 'è‰°',
  '¼é' => 'å¥¸',
  '¼ê' => 'ç¼„',
  '¼ë' => 'èŒ§',
  '¼ì' => 'æ£€',
  '¼í' => 'æŸ¬',
  '¼î' => 'ç¢±',
  '¼ï' => 'ç¡·',
  '¼ð' => 'æ‹£',
  '¼ñ' => 'æ�¡',
  '¼ò' => 'ç®€',
  '¼ó' => 'ä¿­',
  '¼ô' => 'å‰ª',
  '¼õ' => 'å‡�',
  '¼ö' => 'è��',
  '¼÷' => 'æ§›',
  '¼ø' => 'é‰´',
  '¼ù' => 'è·µ',
  '¼ú' => 'è´±',
  '¼û' => 'è§�',
  '¼ü' => 'é”®',
  '¼ý' => 'ç®­',
  '¼þ' => 'ä»¶',
  '½@' => 'ç´·',
  '½A' => 'ç´¸',
  '½B' => 'ç´¹',
  '½C' => 'ç´º',
  '½D' => 'ç´»',
  '½E' => 'ç´¼',
  '½F' => 'ç´½',
  '½G' => 'ç´¾',
  '½H' => 'ç´¿',
  '½I' => 'çµ€',
  '½J' => 'çµ�',
  '½K' => 'çµ‚',
  '½L' => 'çµƒ',
  '½M' => 'çµ„',
  '½N' => 'çµ…',
  '½O' => 'çµ†',
  '½P' => 'çµ‡',
  '½Q' => 'çµˆ',
  '½R' => 'çµ‰',
  '½S' => 'çµŠ',
  '½T' => 'çµ‹',
  '½U' => 'çµŒ',
  '½V' => 'çµ�',
  '½W' => 'çµŽ',
  '½X' => 'çµ�',
  '½Y' => 'çµ�',
  '½Z' => 'çµ‘',
  '½[' => 'çµ’',
  '½\\' => 'çµ“',
  '½]' => 'çµ”',
  '½^' => 'çµ•',
  '½_' => 'çµ–',
  '½`' => 'çµ—',
  '½a' => 'çµ˜',
  '½b' => 'çµ™',
  '½c' => 'çµš',
  '½d' => 'çµ›',
  '½e' => 'çµœ',
  '½f' => 'çµ�',
  '½g' => 'çµž',
  '½h' => 'çµŸ',
  '½i' => 'çµ ',
  '½j' => 'çµ¡',
  '½k' => 'çµ¢',
  '½l' => 'çµ£',
  '½m' => 'çµ¤',
  '½n' => 'çµ¥',
  '½o' => 'çµ¦',
  '½p' => 'çµ§',
  '½q' => 'çµ¨',
  '½r' => 'çµ©',
  '½s' => 'çµª',
  '½t' => 'çµ«',
  '½u' => 'çµ¬',
  '½v' => 'çµ­',
  '½w' => 'çµ¯',
  '½x' => 'çµ°',
  '½y' => 'çµ±',
  '½z' => 'çµ²',
  '½{' => 'çµ³',
  '½|' => 'çµ´',
  '½}' => 'çµµ',
  '½~' => 'çµ¶',
  '½€' => 'çµ¸',
  '½�' => 'çµ¹',
  '½‚' => 'çµº',
  '½ƒ' => 'çµ»',
  '½„' => 'çµ¼',
  '½…' => 'çµ½',
  '½†' => 'çµ¾',
  '½‡' => 'çµ¿',
  '½ˆ' => 'ç¶€',
  '½‰' => 'ç¶�',
  '½Š' => 'ç¶‚',
  '½‹' => 'ç¶ƒ',
  '½Œ' => 'ç¶„',
  '½�' => 'ç¶…',
  '½Ž' => 'ç¶†',
  '½�' => 'ç¶‡',
  '½�' => 'ç¶ˆ',
  '½‘' => 'ç¶‰',
  '½’' => 'ç¶Š',
  '½“' => 'ç¶‹',
  '½”' => 'ç¶Œ',
  '½•' => 'ç¶�',
  '½–' => 'ç¶Ž',
  '½—' => 'ç¶�',
  '½˜' => 'ç¶�',
  '½™' => 'ç¶‘',
  '½š' => 'ç¶’',
  '½›' => 'ç¶“',
  '½œ' => 'ç¶”',
  '½�' => 'ç¶•',
  '½ž' => 'ç¶–',
  '½Ÿ' => 'ç¶—',
  '½ ' => 'ç¶˜',
  '½¡' => 'å�¥',
  '½¢' => 'èˆ°',
  '½£' => 'å‰‘',
  '½¤' => 'é¥¯',
  '½¥' => 'æ¸�',
  '½¦' => 'æº…',
  '½§' => 'æ¶§',
  '½¨' => 'å»º',
  '½©' => 'åƒµ',
  '½ª' => 'å§œ',
  '½«' => 'å°†',
  '½¬' => 'æµ†',
  '½­' => 'æ±Ÿ',
  '½®' => 'ç–†',
  '½¯' => 'è’‹',
  '½°' => 'æ¡¨',
  '½±' => 'å¥–',
  '½²' => 'è®²',
  '½³' => 'åŒ ',
  '½´' => 'é…±',
  '½µ' => 'é™�',
  '½¶' => 'è•‰',
  '½·' => 'æ¤’',
  '½¸' => 'ç¤�',
  '½¹' => 'ç„¦',
  '½º' => 'èƒ¶',
  '½»' => 'äº¤',
  '½¼' => 'éƒŠ',
  '½½' => 'æµ‡',
  '½¾' => 'éª„',
  '½¿' => 'å¨‡',
  '½À' => 'åš¼',
  '½Á' => 'æ�…',
  '½Â' => 'é“°',
  '½Ã' => 'çŸ«',
  '½Ä' => 'ä¾¥',
  '½Å' => 'è„š',
  '½Æ' => 'ç‹¡',
  '½Ç' => 'è§’',
  '½È' => 'é¥º',
  '½É' => 'ç¼´',
  '½Ê' => 'ç»ž',
  '½Ë' => 'å‰¿',
  '½Ì' => 'æ•™',
  '½Í' => 'é…µ',
  '½Î' => 'è½¿',
  '½Ï' => 'è¾ƒ',
  '½Ð' => 'å�«',
  '½Ñ' => 'çª–',
  '½Ò' => 'æ�­',
  '½Ó' => 'æŽ¥',
  '½Ô' => 'çš†',
  '½Õ' => 'ç§¸',
  '½Ö' => 'è¡—',
  '½×' => 'é˜¶',
  '½Ø' => 'æˆª',
  '½Ù' => 'åŠ«',
  '½Ú' => 'èŠ‚',
  '½Û' => 'æ¡”',
  '½Ü' => 'æ�°',
  '½Ý' => 'æ�·',
  '½Þ' => 'ç�«',
  '½ß' => 'ç«­',
  '½à' => 'æ´�',
  '½á' => 'ç»“',
  '½â' => 'è§£',
  '½ã' => 'å§�',
  '½ä' => 'æˆ’',
  '½å' => 'è—‰',
  '½æ' => 'èŠ¥',
  '½ç' => 'ç•Œ',
  '½è' => 'å€Ÿ',
  '½é' => 'ä»‹',
  '½ê' => 'ç–¥',
  '½ë' => 'è¯«',
  '½ì' => 'å±Š',
  '½í' => 'å·¾',
  '½î' => 'ç­‹',
  '½ï' => 'æ–¤',
  '½ð' => 'é‡‘',
  '½ñ' => 'ä»Š',
  '½ò' => 'æ´¥',
  '½ó' => 'è¥Ÿ',
  '½ô' => 'ç´§',
  '½õ' => 'é”¦',
  '½ö' => 'ä»…',
  '½÷' => 'è°¨',
  '½ø' => 'è¿›',
  '½ù' => 'é�³',
  '½ú' => 'æ™‹',
  '½û' => 'ç¦�',
  '½ü' => 'è¿‘',
  '½ý' => 'çƒ¬',
  '½þ' => 'æµ¸',
  '¾@' => 'ç¶™',
  '¾A' => 'ç¶š',
  '¾B' => 'ç¶›',
  '¾C' => 'ç¶œ',
  '¾D' => 'ç¶�',
  '¾E' => 'ç¶ž',
  '¾F' => 'ç¶Ÿ',
  '¾G' => 'ç¶ ',
  '¾H' => 'ç¶¡',
  '¾I' => 'ç¶¢',
  '¾J' => 'ç¶£',
  '¾K' => 'ç¶¤',
  '¾L' => 'ç¶¥',
  '¾M' => 'ç¶§',
  '¾N' => 'ç¶¨',
  '¾O' => 'ç¶©',
  '¾P' => 'ç¶ª',
  '¾Q' => 'ç¶«',
  '¾R' => 'ç¶¬',
  '¾S' => 'ç¶­',
  '¾T' => 'ç¶¯',
  '¾U' => 'ç¶°',
  '¾V' => 'ç¶±',
  '¾W' => 'ç¶²',
  '¾X' => 'ç¶³',
  '¾Y' => 'ç¶´',
  '¾Z' => 'ç¶µ',
  '¾[' => 'ç¶¶',
  '¾\\' => 'ç¶·',
  '¾]' => 'ç¶¸',
  '¾^' => 'ç¶¹',
  '¾_' => 'ç¶º',
  '¾`' => 'ç¶»',
  '¾a' => 'ç¶¼',
  '¾b' => 'ç¶½',
  '¾c' => 'ç¶¾',
  '¾d' => 'ç¶¿',
  '¾e' => 'ç·€',
  '¾f' => 'ç·�',
  '¾g' => 'ç·‚',
  '¾h' => 'ç·ƒ',
  '¾i' => 'ç·„',
  '¾j' => 'ç·…',
  '¾k' => 'ç·†',
  '¾l' => 'ç·‡',
  '¾m' => 'ç·ˆ',
  '¾n' => 'ç·‰',
  '¾o' => 'ç·Š',
  '¾p' => 'ç·‹',
  '¾q' => 'ç·Œ',
  '¾r' => 'ç·�',
  '¾s' => 'ç·Ž',
  '¾t' => 'ç·�',
  '¾u' => 'ç·�',
  '¾v' => 'ç·‘',
  '¾w' => 'ç·’',
  '¾x' => 'ç·“',
  '¾y' => 'ç·”',
  '¾z' => 'ç·•',
  '¾{' => 'ç·–',
  '¾|' => 'ç·—',
  '¾}' => 'ç·˜',
  '¾~' => 'ç·™',
  '¾€' => 'ç·š',
  '¾�' => 'ç·›',
  '¾‚' => 'ç·œ',
  '¾ƒ' => 'ç·�',
  '¾„' => 'ç·ž',
  '¾…' => 'ç·Ÿ',
  '¾†' => 'ç· ',
  '¾‡' => 'ç·¡',
  '¾ˆ' => 'ç·¢',
  '¾‰' => 'ç·£',
  '¾Š' => 'ç·¤',
  '¾‹' => 'ç·¥',
  '¾Œ' => 'ç·¦',
  '¾�' => 'ç·§',
  '¾Ž' => 'ç·¨',
  '¾�' => 'ç·©',
  '¾�' => 'ç·ª',
  '¾‘' => 'ç·«',
  '¾’' => 'ç·¬',
  '¾“' => 'ç·­',
  '¾”' => 'ç·®',
  '¾•' => 'ç·¯',
  '¾–' => 'ç·°',
  '¾—' => 'ç·±',
  '¾˜' => 'ç·²',
  '¾™' => 'ç·³',
  '¾š' => 'ç·´',
  '¾›' => 'ç·µ',
  '¾œ' => 'ç·¶',
  '¾�' => 'ç··',
  '¾ž' => 'ç·¸',
  '¾Ÿ' => 'ç·¹',
  '¾ ' => 'ç·º',
  '¾¡' => 'å°½',
  '¾¢' => 'åŠ²',
  '¾£' => 'è�†',
  '¾¤' => 'å…¢',
  '¾¥' => 'èŒŽ',
  '¾¦' => 'ç�›',
  '¾§' => 'æ™¶',
  '¾¨' => 'é²¸',
  '¾©' => 'äº¬',
  '¾ª' => 'æƒŠ',
  '¾«' => 'ç²¾',
  '¾¬' => 'ç²³',
  '¾­' => 'ç»�',
  '¾®' => 'äº•',
  '¾¯' => 'è­¦',
  '¾°' => 'æ™¯',
  '¾±' => 'é¢ˆ',
  '¾²' => 'é�™',
  '¾³' => 'å¢ƒ',
  '¾´' => 'æ•¬',
  '¾µ' => 'é•œ',
  '¾¶' => 'å¾„',
  '¾·' => 'ç—‰',
  '¾¸' => 'é�–',
  '¾¹' => 'ç«Ÿ',
  '¾º' => 'ç«ž',
  '¾»' => 'å‡€',
  '¾¼' => 'ç‚¯',
  '¾½' => 'çª˜',
  '¾¾' => 'æ�ª',
  '¾¿' => 'ç©¶',
  '¾À' => 'çº ',
  '¾Á' => 'çŽ–',
  '¾Â' => 'éŸ­',
  '¾Ã' => 'ä¹…',
  '¾Ä' => 'ç�¸',
  '¾Å' => 'ä¹�',
  '¾Æ' => 'é…’',
  '¾Ç' => 'åŽ©',
  '¾È' => 'æ•‘',
  '¾É' => 'æ—§',
  '¾Ê' => 'è‡¼',
  '¾Ë' => 'èˆ…',
  '¾Ì' => 'å’Ž',
  '¾Í' => 'å°±',
  '¾Î' => 'ç–š',
  '¾Ï' => 'éž ',
  '¾Ð' => 'æ‹˜',
  '¾Ñ' => 'ç‹™',
  '¾Ò' => 'ç–½',
  '¾Ó' => 'å±…',
  '¾Ô' => 'é©¹',
  '¾Õ' => 'è�Š',
  '¾Ö' => 'å±€',
  '¾×' => 'å’€',
  '¾Ø' => 'çŸ©',
  '¾Ù' => 'ä¸¾',
  '¾Ú' => 'æ²®',
  '¾Û' => 'è�š',
  '¾Ü' => 'æ‹’',
  '¾Ý' => 'æ�®',
  '¾Þ' => 'å·¨',
  '¾ß' => 'å…·',
  '¾à' => 'è·�',
  '¾á' => 'è¸ž',
  '¾â' => 'é”¯',
  '¾ã' => 'ä¿±',
  '¾ä' => 'å�¥',
  '¾å' => 'æƒ§',
  '¾æ' => 'ç‚¬',
  '¾ç' => 'å‰§',
  '¾è' => 'æ��',
  '¾é' => 'é¹ƒ',
  '¾ê' => 'å¨Ÿ',
  '¾ë' => 'å€¦',
  '¾ì' => 'çœ·',
  '¾í' => 'å�·',
  '¾î' => 'ç»¢',
  '¾ï' => 'æ’…',
  '¾ð' => 'æ”«',
  '¾ñ' => 'æŠ‰',
  '¾ò' => 'æŽ˜',
  '¾ó' => 'å€”',
  '¾ô' => 'çˆµ',
  '¾õ' => 'è§‰',
  '¾ö' => 'å†³',
  '¾÷' => 'è¯€',
  '¾ø' => 'ç»�',
  '¾ù' => 'å�‡',
  '¾ú' => 'è�Œ',
  '¾û' => 'é’§',
  '¾ü' => 'å†›',
  '¾ý' => 'å�›',
  '¾þ' => 'å³»',
  '¿@' => 'ç·»',
  '¿A' => 'ç·¼',
  '¿B' => 'ç·½',
  '¿C' => 'ç·¾',
  '¿D' => 'ç·¿',
  '¿E' => 'ç¸€',
  '¿F' => 'ç¸�',
  '¿G' => 'ç¸‚',
  '¿H' => 'ç¸ƒ',
  '¿I' => 'ç¸„',
  '¿J' => 'ç¸…',
  '¿K' => 'ç¸†',
  '¿L' => 'ç¸‡',
  '¿M' => 'ç¸ˆ',
  '¿N' => 'ç¸‰',
  '¿O' => 'ç¸Š',
  '¿P' => 'ç¸‹',
  '¿Q' => 'ç¸Œ',
  '¿R' => 'ç¸�',
  '¿S' => 'ç¸Ž',
  '¿T' => 'ç¸�',
  '¿U' => 'ç¸�',
  '¿V' => 'ç¸‘',
  '¿W' => 'ç¸’',
  '¿X' => 'ç¸“',
  '¿Y' => 'ç¸”',
  '¿Z' => 'ç¸•',
  '¿[' => 'ç¸–',
  '¿\\' => 'ç¸—',
  '¿]' => 'ç¸˜',
  '¿^' => 'ç¸™',
  '¿_' => 'ç¸š',
  '¿`' => 'ç¸›',
  '¿a' => 'ç¸œ',
  '¿b' => 'ç¸�',
  '¿c' => 'ç¸ž',
  '¿d' => 'ç¸Ÿ',
  '¿e' => 'ç¸ ',
  '¿f' => 'ç¸¡',
  '¿g' => 'ç¸¢',
  '¿h' => 'ç¸£',
  '¿i' => 'ç¸¤',
  '¿j' => 'ç¸¥',
  '¿k' => 'ç¸¦',
  '¿l' => 'ç¸§',
  '¿m' => 'ç¸¨',
  '¿n' => 'ç¸©',
  '¿o' => 'ç¸ª',
  '¿p' => 'ç¸«',
  '¿q' => 'ç¸¬',
  '¿r' => 'ç¸­',
  '¿s' => 'ç¸®',
  '¿t' => 'ç¸¯',
  '¿u' => 'ç¸°',
  '¿v' => 'ç¸±',
  '¿w' => 'ç¸²',
  '¿x' => 'ç¸³',
  '¿y' => 'ç¸´',
  '¿z' => 'ç¸µ',
  '¿{' => 'ç¸¶',
  '¿|' => 'ç¸·',
  '¿}' => 'ç¸¸',
  '¿~' => 'ç¸¹',
  '¿€' => 'ç¸º',
  '¿�' => 'ç¸¼',
  '¿‚' => 'ç¸½',
  '¿ƒ' => 'ç¸¾',
  '¿„' => 'ç¸¿',
  '¿…' => 'ç¹€',
  '¿†' => 'ç¹‚',
  '¿‡' => 'ç¹ƒ',
  '¿ˆ' => 'ç¹„',
  '¿‰' => 'ç¹…',
  '¿Š' => 'ç¹†',
  '¿‹' => 'ç¹ˆ',
  '¿Œ' => 'ç¹‰',
  '¿�' => 'ç¹Š',
  '¿Ž' => 'ç¹‹',
  '¿�' => 'ç¹Œ',
  '¿�' => 'ç¹�',
  '¿‘' => 'ç¹Ž',
  '¿’' => 'ç¹�',
  '¿“' => 'ç¹�',
  '¿”' => 'ç¹‘',
  '¿•' => 'ç¹’',
  '¿–' => 'ç¹“',
  '¿—' => 'ç¹”',
  '¿˜' => 'ç¹•',
  '¿™' => 'ç¹–',
  '¿š' => 'ç¹—',
  '¿›' => 'ç¹˜',
  '¿œ' => 'ç¹™',
  '¿�' => 'ç¹š',
  '¿ž' => 'ç¹›',
  '¿Ÿ' => 'ç¹œ',
  '¿ ' => 'ç¹�',
  '¿¡' => 'ä¿Š',
  '¿¢' => 'ç«£',
  '¿£' => 'æµš',
  '¿¤' => 'éƒ¡',
  '¿¥' => 'éª�',
  '¿¦' => 'å–€',
  '¿§' => 'å’–',
  '¿¨' => 'å�¡',
  '¿©' => 'å’¯',
  '¿ª' => 'å¼€',
  '¿«' => 'æ�©',
  '¿¬' => 'æ¥·',
  '¿­' => 'å‡¯',
  '¿®' => 'æ…¨',
  '¿¯' => 'åˆŠ',
  '¿°' => 'å ª',
  '¿±' => 'å‹˜',
  '¿²' => 'å�Ž',
  '¿³' => 'ç �',
  '¿´' => 'çœ‹',
  '¿µ' => 'åº·',
  '¿¶' => 'æ…·',
  '¿·' => 'ç³ ',
  '¿¸' => 'æ‰›',
  '¿¹' => 'æŠ—',
  '¿º' => 'äº¢',
  '¿»' => 'ç‚•',
  '¿¼' => 'è€ƒ',
  '¿½' => 'æ‹·',
  '¿¾' => 'çƒ¤',
  '¿¿' => 'é� ',
  '¿À' => 'å�·',
  '¿Á' => 'è‹›',
  '¿Â' => 'æŸ¯',
  '¿Ã' => 'æ£µ',
  '¿Ä' => 'ç£•',
  '¿Å' => 'é¢—',
  '¿Æ' => 'ç§‘',
  '¿Ç' => 'å£³',
  '¿È' => 'å’³',
  '¿É' => 'å�¯',
  '¿Ê' => 'æ¸´',
  '¿Ë' => 'å…‹',
  '¿Ì' => 'åˆ»',
  '¿Í' => 'å®¢',
  '¿Î' => 'è¯¾',
  '¿Ï' => 'è‚¯',
  '¿Ð' => 'å•ƒ',
  '¿Ñ' => 'åž¦',
  '¿Ò' => 'æ�³',
  '¿Ó' => 'å�‘',
  '¿Ô' => 'å�­',
  '¿Õ' => 'ç©º',
  '¿Ö' => 'æ��',
  '¿×' => 'å­”',
  '¿Ø' => 'æŽ§',
  '¿Ù' => 'æŠ ',
  '¿Ú' => 'å�£',
  '¿Û' => 'æ‰£',
  '¿Ü' => 'å¯‡',
  '¿Ý' => 'æž¯',
  '¿Þ' => 'å“­',
  '¿ß' => 'çªŸ',
  '¿à' => 'è‹¦',
  '¿á' => 'é…·',
  '¿â' => 'åº“',
  '¿ã' => 'è£¤',
  '¿ä' => 'å¤¸',
  '¿å' => 'åž®',
  '¿æ' => 'æŒŽ',
  '¿ç' => 'è·¨',
  '¿è' => 'èƒ¯',
  '¿é' => 'å�—',
  '¿ê' => 'ç­·',
  '¿ë' => 'ä¾©',
  '¿ì' => 'å¿«',
  '¿í' => 'å®½',
  '¿î' => 'æ¬¾',
  '¿ï' => 'åŒ¡',
  '¿ð' => 'ç­�',
  '¿ñ' => 'ç‹‚',
  '¿ò' => 'æ¡†',
  '¿ó' => 'çŸ¿',
  '¿ô' => 'çœ¶',
  '¿õ' => 'æ—·',
  '¿ö' => 'å†µ',
  '¿÷' => 'äº�',
  '¿ø' => 'ç›”',
  '¿ù' => 'å²¿',
  '¿ú' => 'çª¥',
  '¿û' => 'è‘µ',
  '¿ü' => 'å¥Ž',
  '¿ý' => 'é­�',
  '¿þ' => 'å‚€',
  'À@' => 'ç¹ž',
  'ÀA' => 'ç¹Ÿ',
  'ÀB' => 'ç¹ ',
  'ÀC' => 'ç¹¡',
  'ÀD' => 'ç¹¢',
  'ÀE' => 'ç¹£',
  'ÀF' => 'ç¹¤',
  'ÀG' => 'ç¹¥',
  'ÀH' => 'ç¹¦',
  'ÀI' => 'ç¹§',
  'ÀJ' => 'ç¹¨',
  'ÀK' => 'ç¹©',
  'ÀL' => 'ç¹ª',
  'ÀM' => 'ç¹«',
  'ÀN' => 'ç¹¬',
  'ÀO' => 'ç¹­',
  'ÀP' => 'ç¹®',
  'ÀQ' => 'ç¹¯',
  'ÀR' => 'ç¹°',
  'ÀS' => 'ç¹±',
  'ÀT' => 'ç¹²',
  'ÀU' => 'ç¹³',
  'ÀV' => 'ç¹´',
  'ÀW' => 'ç¹µ',
  'ÀX' => 'ç¹¶',
  'ÀY' => 'ç¹·',
  'ÀZ' => 'ç¹¸',
  'À[' => 'ç¹¹',
  'À\\' => 'ç¹º',
  'À]' => 'ç¹»',
  'À^' => 'ç¹¼',
  'À_' => 'ç¹½',
  'À`' => 'ç¹¾',
  'Àa' => 'ç¹¿',
  'Àb' => 'çº€',
  'Àc' => 'çº�',
  'Àd' => 'çºƒ',
  'Àe' => 'çº„',
  'Àf' => 'çº…',
  'Àg' => 'çº†',
  'Àh' => 'çº‡',
  'Ài' => 'çºˆ',
  'Àj' => 'çº‰',
  'Àk' => 'çºŠ',
  'Àl' => 'çº‹',
  'Àm' => 'çºŒ',
  'Àn' => 'çº�',
  'Ào' => 'çºŽ',
  'Àp' => 'çº�',
  'Àq' => 'çº�',
  'Àr' => 'çº‘',
  'Às' => 'çº’',
  'Àt' => 'çº“',
  'Àu' => 'çº”',
  'Àv' => 'çº•',
  'Àw' => 'çº–',
  'Àx' => 'çº—',
  'Ày' => 'çº˜',
  'Àz' => 'çº™',
  'À{' => 'çºš',
  'À|' => 'çºœ',
  'À}' => 'çº�',
  'À~' => 'çºž',
  'À€' => 'çº®',
  'À�' => 'çº´',
  'À‚' => 'çº»',
  'Àƒ' => 'çº¼',
  'À„' => 'ç»–',
  'À…' => 'ç»¤',
  'À†' => 'ç»¬',
  'À‡' => 'ç»¹',
  'Àˆ' => 'ç¼Š',
  'À‰' => 'ç¼�',
  'ÀŠ' => 'ç¼ž',
  'À‹' => 'ç¼·',
  'ÀŒ' => 'ç¼¹',
  'À�' => 'ç¼»',
  'ÀŽ' => 'ç¼¼',
  'À�' => 'ç¼½',
  'À�' => 'ç¼¾',
  'À‘' => 'ç¼¿',
  'À’' => 'ç½€',
  'À“' => 'ç½�',
  'À”' => 'ç½ƒ',
  'À•' => 'ç½†',
  'À–' => 'ç½‡',
  'À—' => 'ç½ˆ',
  'À˜' => 'ç½‰',
  'À™' => 'ç½Š',
  'Àš' => 'ç½‹',
  'À›' => 'ç½Œ',
  'Àœ' => 'ç½�',
  'À�' => 'ç½Ž',
  'Àž' => 'ç½�',
  'ÀŸ' => 'ç½’',
  'À ' => 'ç½“',
  'À¡' => 'é¦ˆ',
  'À¢' => 'æ„§',
  'À£' => 'æºƒ',
  'À¤' => 'å�¤',
  'À¥' => 'æ˜†',
  'À¦' => 'æ�†',
  'À§' => 'å›°',
  'À¨' => 'æ‹¬',
  'À©' => 'æ‰©',
  'Àª' => 'å»“',
  'À«' => 'é˜”',
  'À¬' => 'åžƒ',
  'À­' => 'æ‹‰',
  'À®' => 'å–‡',
  'À¯' => 'èœ¡',
  'À°' => 'è…Š',
  'À±' => 'è¾£',
  'À²' => 'å•¦',
  'À³' => 'èŽ±',
  'À´' => 'æ�¥',
  'Àµ' => 'èµ–',
  'À¶' => 'è“�',
  'À·' => 'å©ª',
  'À¸' => 'æ �',
  'À¹' => 'æ‹¦',
  'Àº' => 'ç¯®',
  'À»' => 'é˜‘',
  'À¼' => 'å…°',
  'À½' => 'æ¾œ',
  'À¾' => 'è°°',
  'À¿' => 'æ�½',
  'ÀÀ' => 'è§ˆ',
  'ÀÁ' => 'æ‡’',
  'ÀÂ' => 'ç¼†',
  'ÀÃ' => 'çƒ‚',
  'ÀÄ' => 'æ»¥',
  'ÀÅ' => 'ç�…',
  'ÀÆ' => 'æ¦”',
  'ÀÇ' => 'ç‹¼',
  'ÀÈ' => 'å»Š',
  'ÀÉ' => 'éƒŽ',
  'ÀÊ' => 'æœ—',
  'ÀË' => 'æµª',
  'ÀÌ' => 'æ�ž',
  'ÀÍ' => 'åŠ³',
  'ÀÎ' => 'ç‰¢',
  'ÀÏ' => 'è€�',
  'ÀÐ' => 'ä½¬',
  'ÀÑ' => 'å§¥',
  'ÀÒ' => 'é…ª',
  'ÀÓ' => 'çƒ™',
  'ÀÔ' => 'æ¶�',
  'ÀÕ' => 'å‹’',
  'ÀÖ' => 'ä¹�',
  'À×' => 'é›·',
  'ÀØ' => 'é•­',
  'ÀÙ' => 'è•¾',
  'ÀÚ' => 'ç£Š',
  'ÀÛ' => 'ç´¯',
  'ÀÜ' => 'å„¡',
  'ÀÝ' => 'åž’',
  'ÀÞ' => 'æ“‚',
  'Àß' => 'è‚‹',
  'Àà' => 'ç±»',
  'Àá' => 'æ³ª',
  'Àâ' => 'æ£±',
  'Àã' => 'æ¥ž',
  'Àä' => 'å†·',
  'Àå' => 'åŽ˜',
  'Àæ' => 'æ¢¨',
  'Àç' => 'çŠ�',
  'Àè' => 'é»Ž',
  'Àé' => 'ç¯±',
  'Àê' => 'ç‹¸',
  'Àë' => 'ç¦»',
  'Àì' => 'æ¼“',
  'Àí' => 'ç�†',
  'Àî' => 'æ�Ž',
  'Àï' => 'é‡Œ',
  'Àð' => 'é²¤',
  'Àñ' => 'ç¤¼',
  'Àò' => 'èŽ‰',
  'Àó' => 'è�”',
  'Àô' => 'å��',
  'Àõ' => 'æ —',
  'Àö' => 'ä¸½',
  'À÷' => 'åŽ‰',
  'Àø' => 'åŠ±',
  'Àù' => 'ç ¾',
  'Àú' => 'åŽ†',
  'Àû' => 'åˆ©',
  'Àü' => 'å‚ˆ',
  'Àý' => 'ä¾‹',
  'Àþ' => 'ä¿�',
  'Á@' => 'ç½–',
  'ÁA' => 'ç½™',
  'ÁB' => 'ç½›',
  'ÁC' => 'ç½œ',
  'ÁD' => 'ç½�',
  'ÁE' => 'ç½ž',
  'ÁF' => 'ç½ ',
  'ÁG' => 'ç½£',
  'ÁH' => 'ç½¤',
  'ÁI' => 'ç½¥',
  'ÁJ' => 'ç½¦',
  'ÁK' => 'ç½§',
  'ÁL' => 'ç½«',
  'ÁM' => 'ç½¬',
  'ÁN' => 'ç½­',
  'ÁO' => 'ç½¯',
  'ÁP' => 'ç½°',
  'ÁQ' => 'ç½³',
  'ÁR' => 'ç½µ',
  'ÁS' => 'ç½¶',
  'ÁT' => 'ç½·',
  'ÁU' => 'ç½¸',
  'ÁV' => 'ç½º',
  'ÁW' => 'ç½»',
  'ÁX' => 'ç½¼',
  'ÁY' => 'ç½½',
  'ÁZ' => 'ç½¿',
  'Á[' => 'ç¾€',
  'Á\\' => 'ç¾‚',
  'Á]' => 'ç¾ƒ',
  'Á^' => 'ç¾„',
  'Á_' => 'ç¾…',
  'Á`' => 'ç¾†',
  'Áa' => 'ç¾‡',
  'Áb' => 'ç¾ˆ',
  'Ác' => 'ç¾‰',
  'Ád' => 'ç¾‹',
  'Áe' => 'ç¾�',
  'Áf' => 'ç¾�',
  'Ág' => 'ç¾�',
  'Áh' => 'ç¾‘',
  'Ái' => 'ç¾’',
  'Áj' => 'ç¾“',
  'Ák' => 'ç¾•',
  'Ál' => 'ç¾–',
  'Ám' => 'ç¾—',
  'Án' => 'ç¾˜',
  'Áo' => 'ç¾™',
  'Áp' => 'ç¾›',
  'Áq' => 'ç¾œ',
  'Ár' => 'ç¾ ',
  'Ás' => 'ç¾¢',
  'Át' => 'ç¾£',
  'Áu' => 'ç¾¥',
  'Áv' => 'ç¾¦',
  'Áw' => 'ç¾¨',
  'Áx' => 'ç¾©',
  'Áy' => 'ç¾ª',
  'Áz' => 'ç¾«',
  'Á{' => 'ç¾¬',
  'Á|' => 'ç¾­',
  'Á}' => 'ç¾®',
  'Á~' => 'ç¾±',
  'Á€' => 'ç¾³',
  'Á�' => 'ç¾´',
  'Á‚' => 'ç¾µ',
  'Áƒ' => 'ç¾¶',
  'Á„' => 'ç¾·',
  'Á…' => 'ç¾º',
  'Á†' => 'ç¾»',
  'Á‡' => 'ç¾¾',
  'Áˆ' => 'ç¿€',
  'Á‰' => 'ç¿‚',
  'ÁŠ' => 'ç¿ƒ',
  'Á‹' => 'ç¿„',
  'ÁŒ' => 'ç¿†',
  'Á�' => 'ç¿‡',
  'ÁŽ' => 'ç¿ˆ',
  'Á�' => 'ç¿‰',
  'Á�' => 'ç¿‹',
  'Á‘' => 'ç¿�',
  'Á’' => 'ç¿�',
  'Á“' => 'ç¿�',
  'Á”' => 'ç¿‘',
  'Á•' => 'ç¿’',
  'Á–' => 'ç¿“',
  'Á—' => 'ç¿–',
  'Á˜' => 'ç¿—',
  'Á™' => 'ç¿™',
  'Áš' => 'ç¿š',
  'Á›' => 'ç¿›',
  'Áœ' => 'ç¿œ',
  'Á�' => 'ç¿�',
  'Áž' => 'ç¿ž',
  'ÁŸ' => 'ç¿¢',
  'Á ' => 'ç¿£',
  'Á¡' => 'ç—¢',
  'Á¢' => 'ç«‹',
  'Á£' => 'ç²’',
  'Á¤' => 'æ²¥',
  'Á¥' => 'éš¶',
  'Á¦' => 'åŠ›',
  'Á§' => 'ç’ƒ',
  'Á¨' => 'å“©',
  'Á©' => 'ä¿©',
  'Áª' => 'è�”',
  'Á«' => 'èŽ²',
  'Á¬' => 'è¿ž',
  'Á­' => 'é•°',
  'Á®' => 'å»‰',
  'Á¯' => 'æ€œ',
  'Á°' => 'æ¶Ÿ',
  'Á±' => 'å¸˜',
  'Á²' => 'æ•›',
  'Á³' => 'è„¸',
  'Á´' => 'é“¾',
  'Áµ' => 'æ�‹',
  'Á¶' => 'ç‚¼',
  'Á·' => 'ç»ƒ',
  'Á¸' => 'ç²®',
  'Á¹' => 'å‡‰',
  'Áº' => 'æ¢�',
  'Á»' => 'ç²±',
  'Á¼' => 'è‰¯',
  'Á½' => 'ä¸¤',
  'Á¾' => 'è¾†',
  'Á¿' => 'é‡�',
  'ÁÀ' => 'æ™¾',
  'ÁÁ' => 'äº®',
  'ÁÂ' => 'è°…',
  'ÁÃ' => 'æ’©',
  'ÁÄ' => 'è�Š',
  'ÁÅ' => 'åƒš',
  'ÁÆ' => 'ç–—',
  'ÁÇ' => 'ç‡Ž',
  'ÁÈ' => 'å¯¥',
  'ÁÉ' => 'è¾½',
  'ÁÊ' => 'æ½¦',
  'ÁË' => 'äº†',
  'ÁÌ' => 'æ’‚',
  'ÁÍ' => 'é•£',
  'ÁÎ' => 'å»–',
  'ÁÏ' => 'æ–™',
  'ÁÐ' => 'åˆ—',
  'ÁÑ' => 'è£‚',
  'ÁÒ' => 'çƒˆ',
  'ÁÓ' => 'åŠ£',
  'ÁÔ' => 'çŒŽ',
  'ÁÕ' => 'ç�³',
  'ÁÖ' => 'æž—',
  'Á×' => 'ç£·',
  'ÁØ' => 'éœ–',
  'ÁÙ' => 'ä¸´',
  'ÁÚ' => 'é‚»',
  'ÁÛ' => 'é³ž',
  'ÁÜ' => 'æ·‹',
  'ÁÝ' => 'å‡›',
  'ÁÞ' => 'èµ�',
  'Áß' => 'å��',
  'Áà' => 'æ‹Ž',
  'Áá' => 'çŽ²',
  'Áâ' => 'è�±',
  'Áã' => 'é›¶',
  'Áä' => 'é¾„',
  'Áå' => 'é“ƒ',
  'Áæ' => 'ä¼¶',
  'Áç' => 'ç¾š',
  'Áè' => 'å‡Œ',
  'Áé' => 'ç�µ',
  'Áê' => 'é™µ',
  'Áë' => 'å²­',
  'Áì' => 'é¢†',
  'Áí' => 'å�¦',
  'Áî' => 'ä»¤',
  'Áï' => 'æºœ',
  'Áð' => 'ç�‰',
  'Áñ' => 'æ¦´',
  'Áò' => 'ç¡«',
  'Áó' => 'é¦�',
  'Áô' => 'ç•™',
  'Áõ' => 'åˆ˜',
  'Áö' => 'ç˜¤',
  'Á÷' => 'æµ�',
  'Áø' => 'æŸ³',
  'Áù' => 'å…­',
  'Áú' => 'é¾™',
  'Áû' => 'è�‹',
  'Áü' => 'å’™',
  'Áý' => 'ç¬¼',
  'Áþ' => 'çª¿',
  'Â@' => 'ç¿¤',
  'ÂA' => 'ç¿§',
  'ÂB' => 'ç¿¨',
  'ÂC' => 'ç¿ª',
  'ÂD' => 'ç¿«',
  'ÂE' => 'ç¿¬',
  'ÂF' => 'ç¿­',
  'ÂG' => 'ç¿¯',
  'ÂH' => 'ç¿²',
  'ÂI' => 'ç¿´',
  'ÂJ' => 'ç¿µ',
  'ÂK' => 'ç¿¶',
  'ÂL' => 'ç¿·',
  'ÂM' => 'ç¿¸',
  'ÂN' => 'ç¿¹',
  'ÂO' => 'ç¿º',
  'ÂP' => 'ç¿½',
  'ÂQ' => 'ç¿¾',
  'ÂR' => 'ç¿¿',
  'ÂS' => 'è€‚',
  'ÂT' => 'è€‡',
  'ÂU' => 'è€ˆ',
  'ÂV' => 'è€‰',
  'ÂW' => 'è€Š',
  'ÂX' => 'è€Ž',
  'ÂY' => 'è€�',
  'ÂZ' => 'è€‘',
  'Â[' => 'è€“',
  'Â\\' => 'è€š',
  'Â]' => 'è€›',
  'Â^' => 'è€�',
  'Â_' => 'è€ž',
  'Â`' => 'è€Ÿ',
  'Âa' => 'è€¡',
  'Âb' => 'è€£',
  'Âc' => 'è€¤',
  'Âd' => 'è€«',
  'Âe' => 'è€¬',
  'Âf' => 'è€­',
  'Âg' => 'è€®',
  'Âh' => 'è€¯',
  'Âi' => 'è€°',
  'Âj' => 'è€²',
  'Âk' => 'è€´',
  'Âl' => 'è€¹',
  'Âm' => 'è€º',
  'Ân' => 'è€¼',
  'Âo' => 'è€¾',
  'Âp' => 'è�€',
  'Âq' => 'è��',
  'Âr' => 'è�„',
  'Âs' => 'è�…',
  'Ât' => 'è�‡',
  'Âu' => 'è�ˆ',
  'Âv' => 'è�‰',
  'Âw' => 'è�Ž',
  'Âx' => 'è��',
  'Ây' => 'è��',
  'Âz' => 'è�‘',
  'Â{' => 'è�“',
  'Â|' => 'è�•',
  'Â}' => 'è�–',
  'Â~' => 'è�—',
  'Â€' => 'è�™',
  'Â�' => 'è�›',
  'Â‚' => 'è�œ',
  'Âƒ' => 'è��',
  'Â„' => 'è�ž',
  'Â…' => 'è�Ÿ',
  'Â†' => 'è� ',
  'Â‡' => 'è�¡',
  'Âˆ' => 'è�¢',
  'Â‰' => 'è�£',
  'ÂŠ' => 'è�¤',
  'Â‹' => 'è�¥',
  'ÂŒ' => 'è�¦',
  'Â�' => 'è�§',
  'ÂŽ' => 'è�¨',
  'Â�' => 'è�«',
  'Â�' => 'è�¬',
  'Â‘' => 'è�­',
  'Â’' => 'è�®',
  'Â“' => 'è�¯',
  'Â”' => 'è�°',
  'Â•' => 'è�²',
  'Â–' => 'è�³',
  'Â—' => 'è�´',
  'Â˜' => 'è�µ',
  'Â™' => 'è�¶',
  'Âš' => 'è�·',
  'Â›' => 'è�¸',
  'Âœ' => 'è�¹',
  'Â�' => 'è�º',
  'Âž' => 'è�»',
  'ÂŸ' => 'è�¼',
  'Â ' => 'è�½',
  'Â¡' => 'éš†',
  'Â¢' => 'åž„',
  'Â£' => 'æ‹¢',
  'Â¤' => 'é™‡',
  'Â¥' => 'æ¥¼',
  'Â¦' => 'å¨„',
  'Â§' => 'æ�‚',
  'Â¨' => 'ç¯“',
  'Â©' => 'æ¼�',
  'Âª' => 'é™‹',
  'Â«' => 'èŠ¦',
  'Â¬' => 'å�¢',
  'Â­' => 'é¢…',
  'Â®' => 'åº�',
  'Â¯' => 'ç‚‰',
  'Â°' => 'æŽ³',
  'Â±' => 'å�¤',
  'Â²' => 'è™�',
  'Â³' => 'é²�',
  'Â´' => 'éº“',
  'Âµ' => 'ç¢Œ',
  'Â¶' => 'éœ²',
  'Â·' => 'è·¯',
  'Â¸' => 'èµ‚',
  'Â¹' => 'é¹¿',
  'Âº' => 'æ½ž',
  'Â»' => 'ç¦„',
  'Â¼' => 'å½•',
  'Â½' => 'é™†',
  'Â¾' => 'æˆ®',
  'Â¿' => 'é©´',
  'ÂÀ' => 'å�•',
  'ÂÁ' => 'é“�',
  'ÂÂ' => 'ä¾£',
  'ÂÃ' => 'æ—…',
  'ÂÄ' => 'å±¥',
  'ÂÅ' => 'å±¡',
  'ÂÆ' => 'ç¼•',
  'ÂÇ' => 'è™‘',
  'ÂÈ' => 'æ°¯',
  'ÂÉ' => 'å¾‹',
  'ÂÊ' => 'çŽ‡',
  'ÂË' => 'æ»¤',
  'ÂÌ' => 'ç»¿',
  'ÂÍ' => 'å³¦',
  'ÂÎ' => 'æŒ›',
  'ÂÏ' => 'å­ª',
  'ÂÐ' => 'æ»¦',
  'ÂÑ' => 'å�µ',
  'ÂÒ' => 'ä¹±',
  'ÂÓ' => 'æŽ ',
  'ÂÔ' => 'ç•¥',
  'ÂÕ' => 'æŠ¡',
  'ÂÖ' => 'è½®',
  'Â×' => 'ä¼¦',
  'ÂØ' => 'ä»‘',
  'ÂÙ' => 'æ²¦',
  'ÂÚ' => 'çº¶',
  'ÂÛ' => 'è®º',
  'ÂÜ' => 'è��',
  'ÂÝ' => 'èžº',
  'ÂÞ' => 'ç½—',
  'Âß' => 'é€»',
  'Âà' => 'é”£',
  'Âá' => 'ç®©',
  'Ââ' => 'éª¡',
  'Âã' => 'è£¸',
  'Âä' => 'è�½',
  'Âå' => 'æ´›',
  'Âæ' => 'éª†',
  'Âç' => 'ç»œ',
  'Âè' => 'å¦ˆ',
  'Âé' => 'éº»',
  'Âê' => 'çŽ›',
  'Âë' => 'ç �',
  'Âì' => 'èš‚',
  'Âí' => 'é©¬',
  'Âî' => 'éª‚',
  'Âï' => 'å˜›',
  'Âð' => 'å�—',
  'Âñ' => 'åŸ‹',
  'Âò' => 'ä¹°',
  'Âó' => 'éº¦',
  'Âô' => 'å�–',
  'Âõ' => 'è¿ˆ',
  'Âö' => 'è„‰',
  'Â÷' => 'çž’',
  'Âø' => 'é¦’',
  'Âù' => 'è›®',
  'Âú' => 'æ»¡',
  'Âû' => 'è”“',
  'Âü' => 'æ›¼',
  'Âý' => 'æ…¢',
  'Âþ' => 'æ¼«',
  'Ã@' => 'è�¾',
  'ÃA' => 'è‚�',
  'ÃB' => 'è‚‚',
  'ÃC' => 'è‚…',
  'ÃD' => 'è‚ˆ',
  'ÃE' => 'è‚Š',
  'ÃF' => 'è‚�',
  'ÃG' => 'è‚Ž',
  'ÃH' => 'è‚�',
  'ÃI' => 'è‚�',
  'ÃJ' => 'è‚‘',
  'ÃK' => 'è‚’',
  'ÃL' => 'è‚”',
  'ÃM' => 'è‚•',
  'ÃN' => 'è‚—',
  'ÃO' => 'è‚™',
  'ÃP' => 'è‚ž',
  'ÃQ' => 'è‚£',
  'ÃR' => 'è‚¦',
  'ÃS' => 'è‚§',
  'ÃT' => 'è‚¨',
  'ÃU' => 'è‚¬',
  'ÃV' => 'è‚°',
  'ÃW' => 'è‚³',
  'ÃX' => 'è‚µ',
  'ÃY' => 'è‚¶',
  'ÃZ' => 'è‚¸',
  'Ã[' => 'è‚¹',
  'Ã\\' => 'è‚»',
  'Ã]' => 'èƒ…',
  'Ã^' => 'èƒ‡',
  'Ã_' => 'èƒˆ',
  'Ã`' => 'èƒ‰',
  'Ãa' => 'èƒŠ',
  'Ãb' => 'èƒ‹',
  'Ãc' => 'èƒ�',
  'Ãd' => 'èƒ�',
  'Ãe' => 'èƒ‘',
  'Ãf' => 'èƒ’',
  'Ãg' => 'èƒ“',
  'Ãh' => 'èƒ”',
  'Ãi' => 'èƒ•',
  'Ãj' => 'èƒ˜',
  'Ãk' => 'èƒŸ',
  'Ãl' => 'èƒ ',
  'Ãm' => 'èƒ¢',
  'Ãn' => 'èƒ£',
  'Ão' => 'èƒ¦',
  'Ãp' => 'èƒ®',
  'Ãq' => 'èƒµ',
  'Ãr' => 'èƒ·',
  'Ãs' => 'èƒ¹',
  'Ãt' => 'èƒ»',
  'Ãu' => 'èƒ¾',
  'Ãv' => 'èƒ¿',
  'Ãw' => 'è„€',
  'Ãx' => 'è„�',
  'Ãy' => 'è„ƒ',
  'Ãz' => 'è„„',
  'Ã{' => 'è„…',
  'Ã|' => 'è„‡',
  'Ã}' => 'è„ˆ',
  'Ã~' => 'è„‹',
  'Ã€' => 'è„Œ',
  'Ã�' => 'è„•',
  'Ã‚' => 'è„—',
  'Ãƒ' => 'è„™',
  'Ã„' => 'è„›',
  'Ã…' => 'è„œ',
  'Ã†' => 'è„�',
  'Ã‡' => 'è„Ÿ',
  'Ãˆ' => 'è„ ',
  'Ã‰' => 'è„¡',
  'ÃŠ' => 'è„¢',
  'Ã‹' => 'è„£',
  'ÃŒ' => 'è„¤',
  'Ã�' => 'è„¥',
  'ÃŽ' => 'è„¦',
  'Ã�' => 'è„§',
  'Ã�' => 'è„¨',
  'Ã‘' => 'è„©',
  'Ã’' => 'è„ª',
  'Ã“' => 'è„«',
  'Ã”' => 'è„­',
  'Ã•' => 'è„®',
  'Ã–' => 'è„°',
  'Ã—' => 'è„³',
  'Ã˜' => 'è„´',
  'Ã™' => 'è„µ',
  'Ãš' => 'è„·',
  'Ã›' => 'è„¹',
  'Ãœ' => 'è„º',
  'Ã�' => 'è„»',
  'Ãž' => 'è„¼',
  'ÃŸ' => 'è„½',
  'Ã ' => 'è„¿',
  'Ã¡' => 'è°©',
  'Ã¢' => 'èŠ’',
  'Ã£' => 'èŒ«',
  'Ã¤' => 'ç›²',
  'Ã¥' => 'æ°“',
  'Ã¦' => 'å¿™',
  'Ã§' => 'èŽ½',
  'Ã¨' => 'çŒ«',
  'Ã©' => 'èŒ…',
  'Ãª' => 'é”š',
  'Ã«' => 'æ¯›',
  'Ã¬' => 'çŸ›',
  'Ã­' => 'é“†',
  'Ã®' => 'å�¯',
  'Ã¯' => 'èŒ‚',
  'Ã°' => 'å†’',
  'Ã±' => 'å¸½',
  'Ã²' => 'è²Œ',
  'Ã³' => 'è´¸',
  'Ã´' => 'ä¹ˆ',
  'Ãµ' => 'çŽ«',
  'Ã¶' => 'æžš',
  'Ã·' => 'æ¢…',
  'Ã¸' => 'é…¶',
  'Ã¹' => 'éœ‰',
  'Ãº' => 'ç…¤',
  'Ã»' => 'æ²¡',
  'Ã¼' => 'çœ‰',
  'Ã½' => 'åª’',
  'Ã¾' => 'é•�',
  'Ã¿' => 'æ¯�',
  'ÃÀ' => 'ç¾Ž',
  'ÃÁ' => 'æ˜§',
  'ÃÂ' => 'å¯�',
  'ÃÃ' => 'å¦¹',
  'ÃÄ' => 'åªš',
  'ÃÅ' => 'é—¨',
  'ÃÆ' => 'é—·',
  'ÃÇ' => 'ä»¬',
  'ÃÈ' => 'è�Œ',
  'ÃÉ' => 'è’™',
  'ÃÊ' => 'æª¬',
  'ÃË' => 'ç›Ÿ',
  'ÃÌ' => 'é”°',
  'ÃÍ' => 'çŒ›',
  'ÃÎ' => 'æ¢¦',
  'ÃÏ' => 'å­Ÿ',
  'ÃÐ' => 'çœ¯',
  'ÃÑ' => 'é†š',
  'ÃÒ' => 'é�¡',
  'ÃÓ' => 'ç³œ',
  'ÃÔ' => 'è¿·',
  'ÃÕ' => 'è°œ',
  'ÃÖ' => 'å¼¥',
  'Ã×' => 'ç±³',
  'ÃØ' => 'ç§˜',
  'ÃÙ' => 'è§…',
  'ÃÚ' => 'æ³Œ',
  'ÃÛ' => 'èœœ',
  'ÃÜ' => 'å¯†',
  'ÃÝ' => 'å¹‚',
  'ÃÞ' => 'æ£‰',
  'Ãß' => 'çœ ',
  'Ãà' => 'ç»µ',
  'Ãá' => 'å†•',
  'Ãâ' => 'å…�',
  'Ãã' => 'å‹‰',
  'Ãä' => 'å¨©',
  'Ãå' => 'ç¼…',
  'Ãæ' => 'é�¢',
  'Ãç' => 'è‹—',
  'Ãè' => 'æ��',
  'Ãé' => 'çž„',
  'Ãê' => 'è—�',
  'Ãë' => 'ç§’',
  'Ãì' => 'æ¸º',
  'Ãí' => 'åº™',
  'Ãî' => 'å¦™',
  'Ãï' => 'è”‘',
  'Ãð' => 'ç�­',
  'Ãñ' => 'æ°‘',
  'Ãò' => 'æŠ¿',
  'Ãó' => 'çš¿',
  'Ãô' => 'æ•�',
  'Ãõ' => 'æ‚¯',
  'Ãö' => 'é—½',
  'Ã÷' => 'æ˜Ž',
  'Ãø' => 'èžŸ',
  'Ãù' => 'é¸£',
  'Ãú' => 'é“­',
  'Ãû' => 'å��',
  'Ãü' => 'å‘½',
  'Ãý' => 'è°¬',
  'Ãþ' => 'æ‘¸',
  'Ä@' => 'è…€',
  'ÄA' => 'è…�',
  'ÄB' => 'è…‚',
  'ÄC' => 'è…ƒ',
  'ÄD' => 'è…„',
  'ÄE' => 'è……',
  'ÄF' => 'è…‡',
  'ÄG' => 'è…‰',
  'ÄH' => 'è…�',
  'ÄI' => 'è…Ž',
  'ÄJ' => 'è…�',
  'ÄK' => 'è…’',
  'ÄL' => 'è…–',
  'ÄM' => 'è…—',
  'ÄN' => 'è…˜',
  'ÄO' => 'è…›',
  'ÄP' => 'è…œ',
  'ÄQ' => 'è…�',
  'ÄR' => 'è…ž',
  'ÄS' => 'è…Ÿ',
  'ÄT' => 'è…¡',
  'ÄU' => 'è…¢',
  'ÄV' => 'è…£',
  'ÄW' => 'è…¤',
  'ÄX' => 'è…¦',
  'ÄY' => 'è…¨',
  'ÄZ' => 'è…ª',
  'Ä[' => 'è…«',
  'Ä\\' => 'è…¬',
  'Ä]' => 'è…¯',
  'Ä^' => 'è…²',
  'Ä_' => 'è…³',
  'Ä`' => 'è…µ',
  'Äa' => 'è…¶',
  'Äb' => 'è…·',
  'Äc' => 'è…¸',
  'Äd' => 'è†�',
  'Äe' => 'è†ƒ',
  'Äf' => 'è†„',
  'Äg' => 'è†…',
  'Äh' => 'è††',
  'Äi' => 'è†‡',
  'Äj' => 'è†‰',
  'Äk' => 'è†‹',
  'Äl' => 'è†Œ',
  'Äm' => 'è†�',
  'Än' => 'è†Ž',
  'Äo' => 'è†�',
  'Äp' => 'è†’',
  'Äq' => 'è†“',
  'Är' => 'è†”',
  'Äs' => 'è†•',
  'Ät' => 'è†–',
  'Äu' => 'è†—',
  'Äv' => 'è†™',
  'Äw' => 'è†š',
  'Äx' => 'è†ž',
  'Äy' => 'è†Ÿ',
  'Äz' => 'è† ',
  'Ä{' => 'è†¡',
  'Ä|' => 'è†¢',
  'Ä}' => 'è†¤',
  'Ä~' => 'è†¥',
  'Ä€' => 'è†§',
  'Ä�' => 'è†©',
  'Ä‚' => 'è†«',
  'Äƒ' => 'è†¬',
  'Ä„' => 'è†­',
  'Ä…' => 'è†®',
  'Ä†' => 'è†¯',
  'Ä‡' => 'è†°',
  'Äˆ' => 'è†±',
  'Ä‰' => 'è†²',
  'ÄŠ' => 'è†´',
  'Ä‹' => 'è†µ',
  'ÄŒ' => 'è†¶',
  'Ä�' => 'è†·',
  'ÄŽ' => 'è†¸',
  'Ä�' => 'è†¹',
  'Ä�' => 'è†¼',
  'Ä‘' => 'è†½',
  'Ä’' => 'è†¾',
  'Ä“' => 'è†¿',
  'Ä”' => 'è‡„',
  'Ä•' => 'è‡…',
  'Ä–' => 'è‡‡',
  'Ä—' => 'è‡ˆ',
  'Ä˜' => 'è‡‰',
  'Ä™' => 'è‡‹',
  'Äš' => 'è‡�',
  'Ä›' => 'è‡Ž',
  'Äœ' => 'è‡�',
  'Ä�' => 'è‡�',
  'Äž' => 'è‡‘',
  'ÄŸ' => 'è‡’',
  'Ä ' => 'è‡“',
  'Ä¡' => 'æ‘¹',
  'Ä¢' => 'è˜‘',
  'Ä£' => 'æ¨¡',
  'Ä¤' => 'è†œ',
  'Ä¥' => 'ç£¨',
  'Ä¦' => 'æ‘©',
  'Ä§' => 'é­”',
  'Ä¨' => 'æŠ¹',
  'Ä©' => 'æœ«',
  'Äª' => 'èŽ«',
  'Ä«' => 'å¢¨',
  'Ä¬' => 'é»˜',
  'Ä­' => 'æ²«',
  'Ä®' => 'æ¼ ',
  'Ä¯' => 'å¯ž',
  'Ä°' => 'é™Œ',
  'Ä±' => 'è°‹',
  'Ä²' => 'ç‰Ÿ',
  'Ä³' => 'æŸ�',
  'Ä´' => 'æ‹‡',
  'Äµ' => 'ç‰¡',
  'Ä¶' => 'äº©',
  'Ä·' => 'å§†',
  'Ä¸' => 'æ¯�',
  'Ä¹' => 'å¢“',
  'Äº' => 'æš®',
  'Ä»' => 'å¹•',
  'Ä¼' => 'å‹Ÿ',
  'Ä½' => 'æ…•',
  'Ä¾' => 'æœ¨',
  'Ä¿' => 'ç›®',
  'ÄÀ' => 'ç�¦',
  'ÄÁ' => 'ç‰§',
  'ÄÂ' => 'ç©†',
  'ÄÃ' => 'æ‹¿',
  'ÄÄ' => 'å“ª',
  'ÄÅ' => 'å‘�',
  'ÄÆ' => 'é’ ',
  'ÄÇ' => 'é‚£',
  'ÄÈ' => 'å¨œ',
  'ÄÉ' => 'çº³',
  'ÄÊ' => 'æ°–',
  'ÄË' => 'ä¹ƒ',
  'ÄÌ' => 'å¥¶',
  'ÄÍ' => 'è€�',
  'ÄÎ' => 'å¥ˆ',
  'ÄÏ' => 'å�—',
  'ÄÐ' => 'ç”·',
  'ÄÑ' => 'éš¾',
  'ÄÒ' => 'å›Š',
  'ÄÓ' => 'æŒ ',
  'ÄÔ' => 'è„‘',
  'ÄÕ' => 'æ�¼',
  'ÄÖ' => 'é—¹',
  'Ä×' => 'æ·–',
  'ÄØ' => 'å‘¢',
  'ÄÙ' => 'é¦�',
  'ÄÚ' => 'å†…',
  'ÄÛ' => 'å«©',
  'ÄÜ' => 'èƒ½',
  'ÄÝ' => 'å¦®',
  'ÄÞ' => 'éœ“',
  'Äß' => 'å€ª',
  'Äà' => 'æ³¥',
  'Äá' => 'å°¼',
  'Äâ' => 'æ‹Ÿ',
  'Äã' => 'ä½ ',
  'Ää' => 'åŒ¿',
  'Äå' => 'è…»',
  'Äæ' => 'é€†',
  'Äç' => 'æºº',
  'Äè' => 'è”«',
  'Äé' => 'æ‹ˆ',
  'Äê' => 'å¹´',
  'Äë' => 'ç¢¾',
  'Äì' => 'æ’µ',
  'Äí' => 'æ�»',
  'Äî' => 'å¿µ',
  'Äï' => 'å¨˜',
  'Äð' => 'é…¿',
  'Äñ' => 'é¸Ÿ',
  'Äò' => 'å°¿',
  'Äó' => 'æ��',
  'Äô' => 'è�‚',
  'Äõ' => 'å­½',
  'Äö' => 'å•®',
  'Ä÷' => 'é•Š',
  'Äø' => 'é•�',
  'Äù' => 'æ¶…',
  'Äú' => 'æ‚¨',
  'Äû' => 'æŸ ',
  'Äü' => 'ç‹ž',
  'Äý' => 'å‡�',
  'Äþ' => 'å®�',
  'Å@' => 'è‡”',
  'ÅA' => 'è‡•',
  'ÅB' => 'è‡–',
  'ÅC' => 'è‡—',
  'ÅD' => 'è‡˜',
  'ÅE' => 'è‡™',
  'ÅF' => 'è‡š',
  'ÅG' => 'è‡›',
  'ÅH' => 'è‡œ',
  'ÅI' => 'è‡�',
  'ÅJ' => 'è‡ž',
  'ÅK' => 'è‡Ÿ',
  'ÅL' => 'è‡ ',
  'ÅM' => 'è‡¡',
  'ÅN' => 'è‡¢',
  'ÅO' => 'è‡¤',
  'ÅP' => 'è‡¥',
  'ÅQ' => 'è‡¦',
  'ÅR' => 'è‡¨',
  'ÅS' => 'è‡©',
  'ÅT' => 'è‡«',
  'ÅU' => 'è‡®',
  'ÅV' => 'è‡¯',
  'ÅW' => 'è‡°',
  'ÅX' => 'è‡±',
  'ÅY' => 'è‡²',
  'ÅZ' => 'è‡µ',
  'Å[' => 'è‡¶',
  'Å\\' => 'è‡·',
  'Å]' => 'è‡¸',
  'Å^' => 'è‡¹',
  'Å_' => 'è‡º',
  'Å`' => 'è‡½',
  'Åa' => 'è‡¿',
  'Åb' => 'èˆƒ',
  'Åc' => 'èˆ‡',
  'Åd' => 'èˆˆ',
  'Åe' => 'èˆ‰',
  'Åf' => 'èˆŠ',
  'Åg' => 'èˆ‹',
  'Åh' => 'èˆŽ',
  'Åi' => 'èˆ�',
  'Åj' => 'èˆ‘',
  'Åk' => 'èˆ“',
  'Ål' => 'èˆ•',
  'Åm' => 'èˆ–',
  'Ån' => 'èˆ—',
  'Åo' => 'èˆ˜',
  'Åp' => 'èˆ™',
  'Åq' => 'èˆš',
  'År' => 'èˆ�',
  'Ås' => 'èˆ ',
  'Åt' => 'èˆ¤',
  'Åu' => 'èˆ¥',
  'Åv' => 'èˆ¦',
  'Åw' => 'èˆ§',
  'Åx' => 'èˆ©',
  'Åy' => 'èˆ®',
  'Åz' => 'èˆ²',
  'Å{' => 'èˆº',
  'Å|' => 'èˆ¼',
  'Å}' => 'èˆ½',
  'Å~' => 'èˆ¿',
  'Å€' => 'è‰€',
  'Å�' => 'è‰�',
  'Å‚' => 'è‰‚',
  'Åƒ' => 'è‰ƒ',
  'Å„' => 'è‰…',
  'Å…' => 'è‰†',
  'Å†' => 'è‰ˆ',
  'Å‡' => 'è‰Š',
  'Åˆ' => 'è‰Œ',
  'Å‰' => 'è‰�',
  'ÅŠ' => 'è‰Ž',
  'Å‹' => 'è‰�',
  'ÅŒ' => 'è‰‘',
  'Å�' => 'è‰’',
  'ÅŽ' => 'è‰“',
  'Å�' => 'è‰”',
  'Å�' => 'è‰•',
  'Å‘' => 'è‰–',
  'Å’' => 'è‰—',
  'Å“' => 'è‰™',
  'Å”' => 'è‰›',
  'Å•' => 'è‰œ',
  'Å–' => 'è‰�',
  'Å—' => 'è‰ž',
  'Å˜' => 'è‰ ',
  'Å™' => 'è‰¡',
  'Åš' => 'è‰¢',
  'Å›' => 'è‰£',
  'Åœ' => 'è‰¤',
  'Å�' => 'è‰¥',
  'Åž' => 'è‰¦',
  'ÅŸ' => 'è‰§',
  'Å ' => 'è‰©',
  'Å¡' => 'æ‹§',
  'Å¢' => 'æ³ž',
  'Å£' => 'ç‰›',
  'Å¤' => 'æ‰­',
  'Å¥' => 'é’®',
  'Å¦' => 'çº½',
  'Å§' => 'è„“',
  'Å¨' => 'æµ“',
  'Å©' => 'å†œ',
  'Åª' => 'å¼„',
  'Å«' => 'å¥´',
  'Å¬' => 'åŠª',
  'Å­' => 'æ€’',
  'Å®' => 'å¥³',
  'Å¯' => 'æš–',
  'Å°' => 'è™�',
  'Å±' => 'ç–Ÿ',
  'Å²' => 'æŒª',
  'Å³' => 'æ‡¦',
  'Å´' => 'ç³¯',
  'Åµ' => 'è¯º',
  'Å¶' => 'å“¦',
  'Å·' => 'æ¬§',
  'Å¸' => 'é¸¥',
  'Å¹' => 'æ®´',
  'Åº' => 'è—•',
  'Å»' => 'å‘•',
  'Å¼' => 'å�¶',
  'Å½' => 'æ²¤',
  'Å¾' => 'å•ª',
  'Å¿' => 'è¶´',
  'ÅÀ' => 'çˆ¬',
  'ÅÁ' => 'å¸•',
  'ÅÂ' => 'æ€•',
  'ÅÃ' => 'ç�¶',
  'ÅÄ' => 'æ‹�',
  'ÅÅ' => 'æŽ’',
  'ÅÆ' => 'ç‰Œ',
  'ÅÇ' => 'å¾˜',
  'ÅÈ' => 'æ¹ƒ',
  'ÅÉ' => 'æ´¾',
  'ÅÊ' => 'æ”€',
  'ÅË' => 'æ½˜',
  'ÅÌ' => 'ç›˜',
  'ÅÍ' => 'ç£�',
  'ÅÎ' => 'ç›¼',
  'ÅÏ' => 'ç•”',
  'ÅÐ' => 'åˆ¤',
  'ÅÑ' => 'å�›',
  'ÅÒ' => 'ä¹“',
  'ÅÓ' => 'åºž',
  'ÅÔ' => 'æ—�',
  'ÅÕ' => 'è€ª',
  'ÅÖ' => 'èƒ–',
  'Å×' => 'æŠ›',
  'ÅØ' => 'å’†',
  'ÅÙ' => 'åˆ¨',
  'ÅÚ' => 'ç‚®',
  'ÅÛ' => 'è¢�',
  'ÅÜ' => 'è·‘',
  'ÅÝ' => 'æ³¡',
  'ÅÞ' => 'å‘¸',
  'Åß' => 'èƒš',
  'Åà' => 'åŸ¹',
  'Åá' => 'è£´',
  'Åâ' => 'èµ”',
  'Åã' => 'é™ª',
  'Åä' => 'é…�',
  'Åå' => 'ä½©',
  'Åæ' => 'æ²›',
  'Åç' => 'å–·',
  'Åè' => 'ç›†',
  'Åé' => 'ç °',
  'Åê' => 'æŠ¨',
  'Åë' => 'çƒ¹',
  'Åì' => 'æ¾Ž',
  'Åí' => 'å½­',
  'Åî' => 'è“¬',
  'Åï' => 'æ£š',
  'Åð' => 'ç¡¼',
  'Åñ' => 'ç¯·',
  'Åò' => 'è†¨',
  'Åó' => 'æœ‹',
  'Åô' => 'é¹�',
  'Åõ' => 'æ�§',
  'Åö' => 'ç¢°',
  'Å÷' => 'å�¯',
  'Åø' => 'ç ’',
  'Åù' => 'éœ¹',
  'Åú' => 'æ‰¹',
  'Åû' => 'æŠ«',
  'Åü' => 'åŠˆ',
  'Åý' => 'ç�µ',
  'Åþ' => 'æ¯—',
  'Æ@' => 'è‰ª',
  'ÆA' => 'è‰«',
  'ÆB' => 'è‰¬',
  'ÆC' => 'è‰­',
  'ÆD' => 'è‰±',
  'ÆE' => 'è‰µ',
  'ÆF' => 'è‰¶',
  'ÆG' => 'è‰·',
  'ÆH' => 'è‰¸',
  'ÆI' => 'è‰»',
  'ÆJ' => 'è‰¼',
  'ÆK' => 'èŠ€',
  'ÆL' => 'èŠ�',
  'ÆM' => 'èŠƒ',
  'ÆN' => 'èŠ…',
  'ÆO' => 'èŠ†',
  'ÆP' => 'èŠ‡',
  'ÆQ' => 'èŠ‰',
  'ÆR' => 'èŠŒ',
  'ÆS' => 'èŠ�',
  'ÆT' => 'èŠ“',
  'ÆU' => 'èŠ”',
  'ÆV' => 'èŠ•',
  'ÆW' => 'èŠ–',
  'ÆX' => 'èŠš',
  'ÆY' => 'èŠ›',
  'ÆZ' => 'èŠž',
  'Æ[' => 'èŠ ',
  'Æ\\' => 'èŠ¢',
  'Æ]' => 'èŠ£',
  'Æ^' => 'èŠ§',
  'Æ_' => 'èŠ²',
  'Æ`' => 'èŠµ',
  'Æa' => 'èŠ¶',
  'Æb' => 'èŠº',
  'Æc' => 'èŠ»',
  'Æd' => 'èŠ¼',
  'Æe' => 'èŠ¿',
  'Æf' => 'è‹€',
  'Æg' => 'è‹‚',
  'Æh' => 'è‹ƒ',
  'Æi' => 'è‹…',
  'Æj' => 'è‹†',
  'Æk' => 'è‹‰',
  'Æl' => 'è‹�',
  'Æm' => 'è‹–',
  'Æn' => 'è‹™',
  'Æo' => 'è‹š',
  'Æp' => 'è‹�',
  'Æq' => 'è‹¢',
  'Ær' => 'è‹§',
  'Æs' => 'è‹¨',
  'Æt' => 'è‹©',
  'Æu' => 'è‹ª',
  'Æv' => 'è‹¬',
  'Æw' => 'è‹­',
  'Æx' => 'è‹®',
  'Æy' => 'è‹°',
  'Æz' => 'è‹²',
  'Æ{' => 'è‹³',
  'Æ|' => 'è‹µ',
  'Æ}' => 'è‹¶',
  'Æ~' => 'è‹¸',
  'Æ€' => 'è‹º',
  'Æ�' => 'è‹¼',
  'Æ‚' => 'è‹½',
  'Æƒ' => 'è‹¾',
  'Æ„' => 'è‹¿',
  'Æ…' => 'èŒ€',
  'Æ†' => 'èŒŠ',
  'Æ‡' => 'èŒ‹',
  'Æˆ' => 'èŒ�',
  'Æ‰' => 'èŒ�',
  'ÆŠ' => 'èŒ’',
  'Æ‹' => 'èŒ“',
  'ÆŒ' => 'èŒ–',
  'Æ�' => 'èŒ˜',
  'ÆŽ' => 'èŒ™',
  'Æ�' => 'èŒ�',
  'Æ�' => 'èŒž',
  'Æ‘' => 'èŒŸ',
  'Æ’' => 'èŒ ',
  'Æ“' => 'èŒ¡',
  'Æ”' => 'èŒ¢',
  'Æ•' => 'èŒ£',
  'Æ–' => 'èŒ¤',
  'Æ—' => 'èŒ¥',
  'Æ˜' => 'èŒ¦',
  'Æ™' => 'èŒ©',
  'Æš' => 'èŒª',
  'Æ›' => 'èŒ®',
  'Æœ' => 'èŒ°',
  'Æ�' => 'èŒ²',
  'Æž' => 'èŒ·',
  'ÆŸ' => 'èŒ»',
  'Æ ' => 'èŒ½',
  'Æ¡' => 'å•¤',
  'Æ¢' => 'è„¾',
  'Æ£' => 'ç–²',
  'Æ¤' => 'çš®',
  'Æ¥' => 'åŒ¹',
  'Æ¦' => 'ç—ž',
  'Æ§' => 'åƒ»',
  'Æ¨' => 'å±�',
  'Æ©' => 'è­¬',
  'Æª' => 'ç¯‡',
  'Æ«' => 'å��',
  'Æ¬' => 'ç‰‡',
  'Æ­' => 'éª—',
  'Æ®' => 'é£˜',
  'Æ¯' => 'æ¼‚',
  'Æ°' => 'ç“¢',
  'Æ±' => 'ç¥¨',
  'Æ²' => 'æ’‡',
  'Æ³' => 'çž¥',
  'Æ´' => 'æ‹¼',
  'Æµ' => 'é¢‘',
  'Æ¶' => 'è´«',
  'Æ·' => 'å“�',
  'Æ¸' => 'è�˜',
  'Æ¹' => 'ä¹’',
  'Æº' => 'å�ª',
  'Æ»' => 'è‹¹',
  'Æ¼' => 'è��',
  'Æ½' => 'å¹³',
  'Æ¾' => 'å‡­',
  'Æ¿' => 'ç“¶',
  'ÆÀ' => 'è¯„',
  'ÆÁ' => 'å±�',
  'ÆÂ' => 'å�¡',
  'ÆÃ' => 'æ³¼',
  'ÆÄ' => 'é¢‡',
  'ÆÅ' => 'å©†',
  'ÆÆ' => 'ç ´',
  'ÆÇ' => 'é­„',
  'ÆÈ' => 'è¿«',
  'ÆÉ' => 'ç²•',
  'ÆÊ' => 'å‰–',
  'ÆË' => 'æ‰‘',
  'ÆÌ' => 'é“º',
  'ÆÍ' => 'ä»†',
  'ÆÎ' => 'èŽ†',
  'ÆÏ' => 'è‘¡',
  'ÆÐ' => 'è�©',
  'ÆÑ' => 'è’²',
  'ÆÒ' => 'åŸ”',
  'ÆÓ' => 'æœ´',
  'ÆÔ' => 'åœƒ',
  'ÆÕ' => 'æ™®',
  'ÆÖ' => 'æµ¦',
  'Æ×' => 'è°±',
  'ÆØ' => 'æ›�',
  'ÆÙ' => 'ç€‘',
  'ÆÚ' => 'æœŸ',
  'ÆÛ' => 'æ¬º',
  'ÆÜ' => 'æ –',
  'ÆÝ' => 'æˆš',
  'ÆÞ' => 'å¦»',
  'Æß' => 'ä¸ƒ',
  'Æà' => 'å‡„',
  'Æá' => 'æ¼†',
  'Æâ' => 'æŸ’',
  'Æã' => 'æ²�',
  'Æä' => 'å…¶',
  'Æå' => 'æ£‹',
  'Ææ' => 'å¥‡',
  'Æç' => 'æ­§',
  'Æè' => 'ç•¦',
  'Æé' => 'å´Ž',
  'Æê' => 'è„�',
  'Æë' => 'é½�',
  'Æì' => 'æ——',
  'Æí' => 'ç¥ˆ',
  'Æî' => 'ç¥�',
  'Æï' => 'éª‘',
  'Æð' => 'èµ·',
  'Æñ' => 'å²‚',
  'Æò' => 'ä¹ž',
  'Æó' => 'ä¼�',
  'Æô' => 'å�¯',
  'Æõ' => 'å¥‘',
  'Æö' => 'ç Œ',
  'Æ÷' => 'å™¨',
  'Æø' => 'æ°”',
  'Æù' => 'è¿„',
  'Æú' => 'å¼ƒ',
  'Æû' => 'æ±½',
  'Æü' => 'æ³£',
  'Æý' => 'è®«',
  'Æþ' => 'æŽ�',
  'Ç@' => 'èŒ¾',
  'ÇA' => 'èŒ¿',
  'ÇB' => 'è��',
  'ÇC' => 'è�‚',
  'ÇD' => 'è�„',
  'ÇE' => 'è�…',
  'ÇF' => 'è�ˆ',
  'ÇG' => 'è�Š',
  'ÇH' => 'è�‹',
  'ÇI' => 'è�Œ',
  'ÇJ' => 'è��',
  'ÇK' => 'è�Ž',
  'ÇL' => 'è�“',
  'ÇM' => 'è�•',
  'ÇN' => 'è�–',
  'ÇO' => 'è�—',
  'ÇP' => 'è�˜',
  'ÇQ' => 'è�™',
  'ÇR' => 'è��',
  'ÇS' => 'è�¢',
  'ÇT' => 'è�°',
  'ÇU' => 'è�±',
  'ÇV' => 'è�²',
  'ÇW' => 'è�³',
  'ÇX' => 'è�´',
  'ÇY' => 'è�µ',
  'ÇZ' => 'è�¶',
  'Ç[' => 'è�¹',
  'Ç\\' => 'è�º',
  'Ç]' => 'è�¾',
  'Ç^' => 'è�¿',
  'Ç_' => 'èŽ€',
  'Ç`' => 'èŽ�',
  'Ça' => 'èŽ‚',
  'Çb' => 'èŽƒ',
  'Çc' => 'èŽ„',
  'Çd' => 'èŽ‡',
  'Çe' => 'èŽˆ',
  'Çf' => 'èŽŠ',
  'Çg' => 'èŽ‹',
  'Çh' => 'èŽŒ',
  'Çi' => 'èŽ�',
  'Çj' => 'èŽ�',
  'Çk' => 'èŽ�',
  'Çl' => 'èŽ‘',
  'Çm' => 'èŽ”',
  'Çn' => 'èŽ•',
  'Ço' => 'èŽ–',
  'Çp' => 'èŽ—',
  'Çq' => 'èŽ™',
  'Çr' => 'èŽš',
  'Çs' => 'èŽ�',
  'Çt' => 'èŽŸ',
  'Çu' => 'èŽ¡',
  'Çv' => 'èŽ¢',
  'Çw' => 'èŽ£',
  'Çx' => 'èŽ¤',
  'Çy' => 'èŽ¥',
  'Çz' => 'èŽ¦',
  'Ç{' => 'èŽ§',
  'Ç|' => 'èŽ¬',
  'Ç}' => 'èŽ­',
  'Ç~' => 'èŽ®',
  'Ç€' => 'èŽ¯',
  'Ç�' => 'èŽµ',
  'Ç‚' => 'èŽ»',
  'Çƒ' => 'èŽ¾',
  'Ç„' => 'èŽ¿',
  'Ç…' => 'è�‚',
  'Ç†' => 'è�ƒ',
  'Ç‡' => 'è�„',
  'Çˆ' => 'è�†',
  'Ç‰' => 'è�ˆ',
  'ÇŠ' => 'è�‰',
  'Ç‹' => 'è�‹',
  'ÇŒ' => 'è��',
  'Ç�' => 'è�Ž',
  'ÇŽ' => 'è��',
  'Ç�' => 'è�‘',
  'Ç�' => 'è�’',
  'Ç‘' => 'è�“',
  'Ç’' => 'è�•',
  'Ç“' => 'è�—',
  'Ç”' => 'è�™',
  'Ç•' => 'è�š',
  'Ç–' => 'è�›',
  'Ç—' => 'è�ž',
  'Ç˜' => 'è�¢',
  'Ç™' => 'è�£',
  'Çš' => 'è�¤',
  'Ç›' => 'è�¦',
  'Çœ' => 'è�§',
  'Ç�' => 'è�¨',
  'Çž' => 'è�«',
  'ÇŸ' => 'è�¬',
  'Ç ' => 'è�­',
  'Ç¡' => 'æ�°',
  'Ç¢' => 'æ´½',
  'Ç£' => 'ç‰µ',
  'Ç¤' => 'æ‰¦',
  'Ç¥' => 'é’Ž',
  'Ç¦' => 'é“…',
  'Ç§' => 'å�ƒ',
  'Ç¨' => 'è¿�',
  'Ç©' => 'ç­¾',
  'Çª' => 'ä»Ÿ',
  'Ç«' => 'è°¦',
  'Ç¬' => 'ä¹¾',
  'Ç­' => 'é»”',
  'Ç®' => 'é’±',
  'Ç¯' => 'é’³',
  'Ç°' => 'å‰�',
  'Ç±' => 'æ½œ',
  'Ç²' => 'é�£',
  'Ç³' => 'æµ…',
  'Ç´' => 'è°´',
  'Çµ' => 'å ‘',
  'Ç¶' => 'åµŒ',
  'Ç·' => 'æ¬ ',
  'Ç¸' => 'æ­‰',
  'Ç¹' => 'æžª',
  'Çº' => 'å‘›',
  'Ç»' => 'è…”',
  'Ç¼' => 'ç¾Œ',
  'Ç½' => 'å¢™',
  'Ç¾' => 'è”·',
  'Ç¿' => 'å¼º',
  'ÇÀ' => 'æŠ¢',
  'ÇÁ' => 'æ©‡',
  'ÇÂ' => 'é”¹',
  'ÇÃ' => 'æ•²',
  'ÇÄ' => 'æ‚„',
  'ÇÅ' => 'æ¡¥',
  'ÇÆ' => 'çž§',
  'ÇÇ' => 'ä¹”',
  'ÇÈ' => 'ä¾¨',
  'ÇÉ' => 'å·§',
  'ÇÊ' => 'éž˜',
  'ÇË' => 'æ’¬',
  'ÇÌ' => 'ç¿˜',
  'ÇÍ' => 'å³­',
  'ÇÎ' => 'ä¿�',
  'ÇÏ' => 'çª�',
  'ÇÐ' => 'åˆ‡',
  'ÇÑ' => 'èŒ„',
  'ÇÒ' => 'ä¸”',
  'ÇÓ' => 'æ€¯',
  'ÇÔ' => 'çªƒ',
  'ÇÕ' => 'é’¦',
  'ÇÖ' => 'ä¾µ',
  'Ç×' => 'äº²',
  'ÇØ' => 'ç§¦',
  'ÇÙ' => 'ç�´',
  'ÇÚ' => 'å‹¤',
  'ÇÛ' => 'èŠ¹',
  'ÇÜ' => 'æ“’',
  'ÇÝ' => 'ç¦½',
  'ÇÞ' => 'å¯�',
  'Çß' => 'æ²�',
  'Çà' => 'é�’',
  'Çá' => 'è½»',
  'Çâ' => 'æ°¢',
  'Çã' => 'å€¾',
  'Çä' => 'å�¿',
  'Çå' => 'æ¸…',
  'Çæ' => 'æ“Ž',
  'Çç' => 'æ™´',
  'Çè' => 'æ°°',
  'Çé' => 'æƒ…',
  'Çê' => 'é¡·',
  'Çë' => 'è¯·',
  'Çì' => 'åº†',
  'Çí' => 'ç�¼',
  'Çî' => 'ç©·',
  'Çï' => 'ç§‹',
  'Çð' => 'ä¸˜',
  'Çñ' => 'é‚±',
  'Çò' => 'ç�ƒ',
  'Çó' => 'æ±‚',
  'Çô' => 'å›š',
  'Çõ' => 'é…‹',
  'Çö' => 'æ³…',
  'Ç÷' => 'è¶‹',
  'Çø' => 'åŒº',
  'Çù' => 'è›†',
  'Çú' => 'æ›²',
  'Çû' => 'èº¯',
  'Çü' => 'å±ˆ',
  'Çý' => 'é©±',
  'Çþ' => 'æ¸ ',
  'È@' => 'è�®',
  'ÈA' => 'è�¯',
  'ÈB' => 'è�³',
  'ÈC' => 'è�´',
  'ÈD' => 'è�µ',
  'ÈE' => 'è�¶',
  'ÈF' => 'è�·',
  'ÈG' => 'è�º',
  'ÈH' => 'è�»',
  'ÈI' => 'è�¼',
  'ÈJ' => 'è�¾',
  'ÈK' => 'è�¿',
  'ÈL' => 'è�€',
  'ÈM' => 'è�‚',
  'ÈN' => 'è�…',
  'ÈO' => 'è�‡',
  'ÈP' => 'è�ˆ',
  'ÈQ' => 'è�‰',
  'ÈR' => 'è�Š',
  'ÈS' => 'è��',
  'ÈT' => 'è�’',
  'ÈU' => 'è�“',
  'ÈV' => 'è�”',
  'ÈW' => 'è�•',
  'ÈX' => 'è�–',
  'ÈY' => 'è�—',
  'ÈZ' => 'è�™',
  'È[' => 'è�š',
  'È\\' => 'è�›',
  'È]' => 'è�ž',
  'È^' => 'è�Ÿ',
  'È_' => 'è� ',
  'È`' => 'è�¡',
  'Èa' => 'è�¢',
  'Èb' => 'è�£',
  'Èc' => 'è�©',
  'Èd' => 'è�ª',
  'Èe' => 'è�«',
  'Èf' => 'è�¬',
  'Èg' => 'è�­',
  'Èh' => 'è�®',
  'Èi' => 'è�¯',
  'Èj' => 'è�°',
  'Èk' => 'è�²',
  'Èl' => 'è�³',
  'Èm' => 'è�´',
  'Èn' => 'è�µ',
  'Èo' => 'è�¶',
  'Èp' => 'è�·',
  'Èq' => 'è�¹',
  'Èr' => 'è�º',
  'Ès' => 'è�»',
  'Èt' => 'è�¾',
  'Èu' => 'è�¿',
  'Èv' => 'è‘€',
  'Èw' => 'è‘�',
  'Èx' => 'è‘‚',
  'Èy' => 'è‘ƒ',
  'Èz' => 'è‘„',
  'È{' => 'è‘…',
  'È|' => 'è‘‡',
  'È}' => 'è‘ˆ',
  'È~' => 'è‘‰',
  'È€' => 'è‘Š',
  'È�' => 'è‘‹',
  'È‚' => 'è‘Œ',
  'Èƒ' => 'è‘�',
  'È„' => 'è‘Ž',
  'È…' => 'è‘�',
  'È†' => 'è‘�',
  'È‡' => 'è‘’',
  'Èˆ' => 'è‘“',
  'È‰' => 'è‘”',
  'ÈŠ' => 'è‘•',
  'È‹' => 'è‘–',
  'ÈŒ' => 'è‘˜',
  'È�' => 'è‘�',
  'ÈŽ' => 'è‘ž',
  'È�' => 'è‘Ÿ',
  'È�' => 'è‘ ',
  'È‘' => 'è‘¢',
  'È’' => 'è‘¤',
  'È“' => 'è‘¥',
  'È”' => 'è‘¦',
  'È•' => 'è‘§',
  'È–' => 'è‘¨',
  'È—' => 'è‘ª',
  'È˜' => 'è‘®',
  'È™' => 'è‘¯',
  'Èš' => 'è‘°',
  'È›' => 'è‘²',
  'Èœ' => 'è‘´',
  'È�' => 'è‘·',
  'Èž' => 'è‘¹',
  'ÈŸ' => 'è‘»',
  'È ' => 'è‘¼',
  'È¡' => 'å�–',
  'È¢' => 'å¨¶',
  'È£' => 'é¾‹',
  'È¤' => 'è¶£',
  'È¥' => 'åŽ»',
  'È¦' => 'åœˆ',
  'È§' => 'é¢§',
  'È¨' => 'æ�ƒ',
  'È©' => 'é†›',
  'Èª' => 'æ³‰',
  'È«' => 'å…¨',
  'È¬' => 'ç—Š',
  'È­' => 'æ‹³',
  'È®' => 'çŠ¬',
  'È¯' => 'åˆ¸',
  'È°' => 'åŠ�',
  'È±' => 'ç¼º',
  'È²' => 'ç‚”',
  'È³' => 'ç˜¸',
  'È´' => 'å�´',
  'Èµ' => 'é¹Š',
  'È¶' => 'æ¦·',
  'È·' => 'ç¡®',
  'È¸' => 'é›€',
  'È¹' => 'è£™',
  'Èº' => 'ç¾¤',
  'È»' => 'ç„¶',
  'È¼' => 'ç‡ƒ',
  'È½' => 'å†‰',
  'È¾' => 'æŸ“',
  'È¿' => 'ç“¤',
  'ÈÀ' => 'å£¤',
  'ÈÁ' => 'æ”˜',
  'ÈÂ' => 'åš·',
  'ÈÃ' => 'è®©',
  'ÈÄ' => 'é¥¶',
  'ÈÅ' => 'æ‰°',
  'ÈÆ' => 'ç»•',
  'ÈÇ' => 'æƒ¹',
  'ÈÈ' => 'çƒ­',
  'ÈÉ' => 'å£¬',
  'ÈÊ' => 'ä»�',
  'ÈË' => 'äºº',
  'ÈÌ' => 'å¿�',
  'ÈÍ' => 'éŸ§',
  'ÈÎ' => 'ä»»',
  'ÈÏ' => 'è®¤',
  'ÈÐ' => 'åˆƒ',
  'ÈÑ' => 'å¦Š',
  'ÈÒ' => 'çº«',
  'ÈÓ' => 'æ‰”',
  'ÈÔ' => 'ä»�',
  'ÈÕ' => 'æ—¥',
  'ÈÖ' => 'æˆŽ',
  'È×' => 'èŒ¸',
  'ÈØ' => 'è“‰',
  'ÈÙ' => 'è�£',
  'ÈÚ' => 'èž�',
  'ÈÛ' => 'ç†”',
  'ÈÜ' => 'æº¶',
  'ÈÝ' => 'å®¹',
  'ÈÞ' => 'ç»’',
  'Èß' => 'å†—',
  'Èà' => 'æ�‰',
  'Èá' => 'æŸ”',
  'Èâ' => 'è‚‰',
  'Èã' => 'èŒ¹',
  'Èä' => 'è •',
  'Èå' => 'å„’',
  'Èæ' => 'å­º',
  'Èç' => 'å¦‚',
  'Èè' => 'è¾±',
  'Èé' => 'ä¹³',
  'Èê' => 'æ±�',
  'Èë' => 'å…¥',
  'Èì' => 'è¤¥',
  'Èí' => 'è½¯',
  'Èî' => 'é˜®',
  'Èï' => 'è•Š',
  'Èð' => 'ç‘ž',
  'Èñ' => 'é”�',
  'Èò' => 'é—°',
  'Èó' => 'æ¶¦',
  'Èô' => 'è‹¥',
  'Èõ' => 'å¼±',
  'Èö' => 'æ’’',
  'È÷' => 'æ´’',
  'Èø' => 'è�¨',
  'Èù' => 'è…®',
  'Èú' => 'é³ƒ',
  'Èû' => 'å¡ž',
  'Èü' => 'èµ›',
  'Èý' => 'ä¸‰',
  'Èþ' => 'å��',
  'É@' => 'è‘½',
  'ÉA' => 'è‘¾',
  'ÉB' => 'è‘¿',
  'ÉC' => 'è’€',
  'ÉD' => 'è’�',
  'ÉE' => 'è’ƒ',
  'ÉF' => 'è’„',
  'ÉG' => 'è’…',
  'ÉH' => 'è’†',
  'ÉI' => 'è’Š',
  'ÉJ' => 'è’�',
  'ÉK' => 'è’�',
  'ÉL' => 'è’�',
  'ÉM' => 'è’‘',
  'ÉN' => 'è’’',
  'ÉO' => 'è’“',
  'ÉP' => 'è’”',
  'ÉQ' => 'è’•',
  'ÉR' => 'è’–',
  'ÉS' => 'è’˜',
  'ÉT' => 'è’š',
  'ÉU' => 'è’›',
  'ÉV' => 'è’�',
  'ÉW' => 'è’ž',
  'ÉX' => 'è’Ÿ',
  'ÉY' => 'è’ ',
  'ÉZ' => 'è’¢',
  'É[' => 'è’£',
  'É\\' => 'è’¤',
  'É]' => 'è’¥',
  'É^' => 'è’¦',
  'É_' => 'è’§',
  'É`' => 'è’¨',
  'Éa' => 'è’©',
  'Éb' => 'è’ª',
  'Éc' => 'è’«',
  'Éd' => 'è’¬',
  'Ée' => 'è’­',
  'Éf' => 'è’®',
  'Ég' => 'è’°',
  'Éh' => 'è’±',
  'Éi' => 'è’³',
  'Éj' => 'è’µ',
  'Ék' => 'è’¶',
  'Él' => 'è’·',
  'Ém' => 'è’»',
  'Én' => 'è’¼',
  'Éo' => 'è’¾',
  'Ép' => 'è“€',
  'Éq' => 'è“‚',
  'Ér' => 'è“ƒ',
  'És' => 'è“…',
  'Ét' => 'è“†',
  'Éu' => 'è“‡',
  'Év' => 'è“ˆ',
  'Éw' => 'è“‹',
  'Éx' => 'è“Œ',
  'Éy' => 'è“Ž',
  'Éz' => 'è“�',
  'É{' => 'è“’',
  'É|' => 'è“”',
  'É}' => 'è“•',
  'É~' => 'è“—',
  'É€' => 'è“˜',
  'É�' => 'è“™',
  'É‚' => 'è“š',
  'Éƒ' => 'è“›',
  'É„' => 'è“œ',
  'É…' => 'è“ž',
  'É†' => 'è“¡',
  'É‡' => 'è“¢',
  'Éˆ' => 'è“¤',
  'É‰' => 'è“§',
  'ÉŠ' => 'è“¨',
  'É‹' => 'è“©',
  'ÉŒ' => 'è“ª',
  'É�' => 'è“«',
  'ÉŽ' => 'è“­',
  'É�' => 'è“®',
  'É�' => 'è“¯',
  'É‘' => 'è“±',
  'É’' => 'è“²',
  'É“' => 'è“³',
  'É”' => 'è“´',
  'É•' => 'è“µ',
  'É–' => 'è“¶',
  'É—' => 'è“·',
  'É˜' => 'è“¸',
  'É™' => 'è“¹',
  'Éš' => 'è“º',
  'É›' => 'è“»',
  'Éœ' => 'è“½',
  'É�' => 'è“¾',
  'Éž' => 'è”€',
  'ÉŸ' => 'è”�',
  'É ' => 'è”‚',
  'É¡' => 'ä¼ž',
  'É¢' => 'æ•£',
  'É£' => 'æ¡‘',
  'É¤' => 'å—“',
  'É¥' => 'ä¸§',
  'É¦' => 'æ�”',
  'É§' => 'éªš',
  'É¨' => 'æ‰«',
  'É©' => 'å«‚',
  'Éª' => 'ç‘Ÿ',
  'É«' => 'è‰²',
  'É¬' => 'æ¶©',
  'É­' => 'æ£®',
  'É®' => 'åƒ§',
  'É¯' => 'èŽŽ',
  'É°' => 'ç ‚',
  'É±' => 'æ�€',
  'É²' => 'åˆ¹',
  'É³' => 'æ²™',
  'É´' => 'çº±',
  'Éµ' => 'å‚»',
  'É¶' => 'å•¥',
  'É·' => 'ç…ž',
  'É¸' => 'ç­›',
  'É¹' => 'æ™’',
  'Éº' => 'ç�Š',
  'É»' => 'è‹«',
  'É¼' => 'æ�‰',
  'É½' => 'å±±',
  'É¾' => 'åˆ ',
  'É¿' => 'ç…½',
  'ÉÀ' => 'è¡«',
  'ÉÁ' => 'é—ª',
  'ÉÂ' => 'é™•',
  'ÉÃ' => 'æ“…',
  'ÉÄ' => 'èµ¡',
  'ÉÅ' => 'è†³',
  'ÉÆ' => 'å–„',
  'ÉÇ' => 'æ±•',
  'ÉÈ' => 'æ‰‡',
  'ÉÉ' => 'ç¼®',
  'ÉÊ' => 'å¢’',
  'ÉË' => 'ä¼¤',
  'ÉÌ' => 'å•†',
  'ÉÍ' => 'èµ�',
  'ÉÎ' => 'æ™Œ',
  'ÉÏ' => 'ä¸Š',
  'ÉÐ' => 'å°š',
  'ÉÑ' => 'è£³',
  'ÉÒ' => 'æ¢¢',
  'ÉÓ' => 'æ�Ž',
  'ÉÔ' => 'ç¨�',
  'ÉÕ' => 'çƒ§',
  'ÉÖ' => 'èŠ�',
  'É×' => 'å‹º',
  'ÉØ' => 'éŸ¶',
  'ÉÙ' => 'å°‘',
  'ÉÚ' => 'å“¨',
  'ÉÛ' => 'é‚µ',
  'ÉÜ' => 'ç»�',
  'ÉÝ' => 'å¥¢',
  'ÉÞ' => 'èµŠ',
  'Éß' => 'è›‡',
  'Éà' => 'èˆŒ',
  'Éá' => 'èˆ�',
  'Éâ' => 'èµ¦',
  'Éã' => 'æ‘„',
  'Éä' => 'å°„',
  'Éå' => 'æ…‘',
  'Éæ' => 'æ¶‰',
  'Éç' => 'ç¤¾',
  'Éè' => 'è®¾',
  'Éé' => 'ç ·',
  'Éê' => 'ç”³',
  'Éë' => 'å‘»',
  'Éì' => 'ä¼¸',
  'Éí' => 'èº«',
  'Éî' => 'æ·±',
  'Éï' => 'å¨ ',
  'Éð' => 'ç»…',
  'Éñ' => 'ç¥ž',
  'Éò' => 'æ²ˆ',
  'Éó' => 'å®¡',
  'Éô' => 'å©¶',
  'Éõ' => 'ç”š',
  'Éö' => 'è‚¾',
  'É÷' => 'æ…Ž',
  'Éø' => 'æ¸—',
  'Éù' => 'å£°',
  'Éú' => 'ç”Ÿ',
  'Éû' => 'ç”¥',
  'Éü' => 'ç‰²',
  'Éý' => 'å�‡',
  'Éþ' => 'ç»³',
  'Ê@' => 'è”ƒ',
  'ÊA' => 'è”„',
  'ÊB' => 'è”…',
  'ÊC' => 'è”†',
  'ÊD' => 'è”‡',
  'ÊE' => 'è”ˆ',
  'ÊF' => 'è”‰',
  'ÊG' => 'è”Š',
  'ÊH' => 'è”‹',
  'ÊI' => 'è”�',
  'ÊJ' => 'è”Ž',
  'ÊK' => 'è”�',
  'ÊL' => 'è”�',
  'ÊM' => 'è”’',
  'ÊN' => 'è””',
  'ÊO' => 'è”•',
  'ÊP' => 'è”–',
  'ÊQ' => 'è”˜',
  'ÊR' => 'è”™',
  'ÊS' => 'è”›',
  'ÊT' => 'è”œ',
  'ÊU' => 'è”�',
  'ÊV' => 'è”ž',
  'ÊW' => 'è” ',
  'ÊX' => 'è”¢',
  'ÊY' => 'è”£',
  'ÊZ' => 'è”¤',
  'Ê[' => 'è”¥',
  'Ê\\' => 'è”¦',
  'Ê]' => 'è”§',
  'Ê^' => 'è”¨',
  'Ê_' => 'è”©',
  'Ê`' => 'è”ª',
  'Êa' => 'è”­',
  'Êb' => 'è”®',
  'Êc' => 'è”¯',
  'Êd' => 'è”°',
  'Êe' => 'è”±',
  'Êf' => 'è”²',
  'Êg' => 'è”³',
  'Êh' => 'è”´',
  'Êi' => 'è”µ',
  'Êj' => 'è”¶',
  'Êk' => 'è”¾',
  'Êl' => 'è”¿',
  'Êm' => 'è•€',
  'Ên' => 'è•�',
  'Êo' => 'è•‚',
  'Êp' => 'è•„',
  'Êq' => 'è•…',
  'Êr' => 'è•†',
  'Ês' => 'è•‡',
  'Êt' => 'è•‹',
  'Êu' => 'è•Œ',
  'Êv' => 'è•�',
  'Êw' => 'è•Ž',
  'Êx' => 'è•�',
  'Êy' => 'è•�',
  'Êz' => 'è•‘',
  'Ê{' => 'è•’',
  'Ê|' => 'è•“',
  'Ê}' => 'è•”',
  'Ê~' => 'è••',
  'Ê€' => 'è•—',
  'Ê�' => 'è•˜',
  'Ê‚' => 'è•š',
  'Êƒ' => 'è•›',
  'Ê„' => 'è•œ',
  'Ê…' => 'è•�',
  'Ê†' => 'è•Ÿ',
  'Ê‡' => 'è• ',
  'Êˆ' => 'è•¡',
  'Ê‰' => 'è•¢',
  'ÊŠ' => 'è•£',
  'Ê‹' => 'è•¥',
  'ÊŒ' => 'è•¦',
  'Ê�' => 'è•§',
  'ÊŽ' => 'è•©',
  'Ê�' => 'è•ª',
  'Ê�' => 'è•«',
  'Ê‘' => 'è•¬',
  'Ê’' => 'è•­',
  'Ê“' => 'è•®',
  'Ê”' => 'è•¯',
  'Ê•' => 'è•°',
  'Ê–' => 'è•±',
  'Ê—' => 'è•³',
  'Ê˜' => 'è•µ',
  'Ê™' => 'è•¶',
  'Êš' => 'è•·',
  'Ê›' => 'è•¸',
  'Êœ' => 'è•¼',
  'Ê�' => 'è•½',
  'Êž' => 'è•¿',
  'ÊŸ' => 'è–€',
  'Ê ' => 'è–�',
  'Ê¡' => 'çœ�',
  'Ê¢' => 'ç››',
  'Ê£' => 'å‰©',
  'Ê¤' => 'èƒœ',
  'Ê¥' => 'åœ£',
  'Ê¦' => 'å¸ˆ',
  'Ê§' => 'å¤±',
  'Ê¨' => 'ç‹®',
  'Ê©' => 'æ–½',
  'Êª' => 'æ¹¿',
  'Ê«' => 'è¯—',
  'Ê¬' => 'å°¸',
  'Ê­' => 'è™±',
  'Ê®' => 'å��',
  'Ê¯' => 'çŸ³',
  'Ê°' => 'æ‹¾',
  'Ê±' => 'æ—¶',
  'Ê²' => 'ä»€',
  'Ê³' => 'é£Ÿ',
  'Ê´' => 'èš€',
  'Êµ' => 'å®ž',
  'Ê¶' => 'è¯†',
  'Ê·' => 'å�²',
  'Ê¸' => 'çŸ¢',
  'Ê¹' => 'ä½¿',
  'Êº' => 'å±Ž',
  'Ê»' => 'é©¶',
  'Ê¼' => 'å§‹',
  'Ê½' => 'å¼�',
  'Ê¾' => 'ç¤º',
  'Ê¿' => 'å£«',
  'ÊÀ' => 'ä¸–',
  'ÊÁ' => 'æŸ¿',
  'ÊÂ' => 'äº‹',
  'ÊÃ' => 'æ‹­',
  'ÊÄ' => 'èª“',
  'ÊÅ' => 'é€�',
  'ÊÆ' => 'åŠ¿',
  'ÊÇ' => 'æ˜¯',
  'ÊÈ' => 'å—œ',
  'ÊÉ' => 'å™¬',
  'ÊÊ' => 'é€‚',
  'ÊË' => 'ä»•',
  'ÊÌ' => 'ä¾�',
  'ÊÍ' => 'é‡Š',
  'ÊÎ' => 'é¥°',
  'ÊÏ' => 'æ°�',
  'ÊÐ' => 'å¸‚',
  'ÊÑ' => 'æ�ƒ',
  'ÊÒ' => 'å®¤',
  'ÊÓ' => 'è§†',
  'ÊÔ' => 'è¯•',
  'ÊÕ' => 'æ”¶',
  'ÊÖ' => 'æ‰‹',
  'Ê×' => 'é¦–',
  'ÊØ' => 'å®ˆ',
  'ÊÙ' => 'å¯¿',
  'ÊÚ' => 'æŽˆ',
  'ÊÛ' => 'å”®',
  'ÊÜ' => 'å�—',
  'ÊÝ' => 'ç˜¦',
  'ÊÞ' => 'å…½',
  'Êß' => 'è”¬',
  'Êà' => 'æž¢',
  'Êá' => 'æ¢³',
  'Êâ' => 'æ®Š',
  'Êã' => 'æŠ’',
  'Êä' => 'è¾“',
  'Êå' => 'å�”',
  'Êæ' => 'èˆ’',
  'Êç' => 'æ·‘',
  'Êè' => 'ç–�',
  'Êé' => 'ä¹¦',
  'Êê' => 'èµŽ',
  'Êë' => 'å­°',
  'Êì' => 'ç†Ÿ',
  'Êí' => 'è–¯',
  'Êî' => 'æš‘',
  'Êï' => 'æ›™',
  'Êð' => 'ç½²',
  'Êñ' => 'èœ€',
  'Êò' => 'é»�',
  'Êó' => 'é¼ ',
  'Êô' => 'å±ž',
  'Êõ' => 'æœ¯',
  'Êö' => 'è¿°',
  'Ê÷' => 'æ ‘',
  'Êø' => 'æ�Ÿ',
  'Êù' => 'æˆ�',
  'Êú' => 'ç«–',
  'Êû' => 'å¢…',
  'Êü' => 'åº¶',
  'Êý' => 'æ•°',
  'Êþ' => 'æ¼±',
  'Ë@' => 'è–‚',
  'ËA' => 'è–ƒ',
  'ËB' => 'è–†',
  'ËC' => 'è–ˆ',
  'ËD' => 'è–‰',
  'ËE' => 'è–Š',
  'ËF' => 'è–‹',
  'ËG' => 'è–Œ',
  'ËH' => 'è–�',
  'ËI' => 'è–Ž',
  'ËJ' => 'è–�',
  'ËK' => 'è–‘',
  'ËL' => 'è–’',
  'ËM' => 'è–“',
  'ËN' => 'è–”',
  'ËO' => 'è–•',
  'ËP' => 'è––',
  'ËQ' => 'è–—',
  'ËR' => 'è–˜',
  'ËS' => 'è–™',
  'ËT' => 'è–š',
  'ËU' => 'è–�',
  'ËV' => 'è–ž',
  'ËW' => 'è–Ÿ',
  'ËX' => 'è– ',
  'ËY' => 'è–¡',
  'ËZ' => 'è–¢',
  'Ë[' => 'è–£',
  'Ë\\' => 'è–¥',
  'Ë]' => 'è–¦',
  'Ë^' => 'è–§',
  'Ë_' => 'è–©',
  'Ë`' => 'è–«',
  'Ëa' => 'è–¬',
  'Ëb' => 'è–­',
  'Ëc' => 'è–±',
  'Ëd' => 'è–²',
  'Ëe' => 'è–³',
  'Ëf' => 'è–´',
  'Ëg' => 'è–µ',
  'Ëh' => 'è–¶',
  'Ëi' => 'è–¸',
  'Ëj' => 'è–º',
  'Ëk' => 'è–»',
  'Ël' => 'è–¼',
  'Ëm' => 'è–½',
  'Ën' => 'è–¾',
  'Ëo' => 'è–¿',
  'Ëp' => 'è—€',
  'Ëq' => 'è—‚',
  'Ër' => 'è—ƒ',
  'Ës' => 'è—„',
  'Ët' => 'è—…',
  'Ëu' => 'è—†',
  'Ëv' => 'è—‡',
  'Ëw' => 'è—ˆ',
  'Ëx' => 'è—Š',
  'Ëy' => 'è—‹',
  'Ëz' => 'è—Œ',
  'Ë{' => 'è—�',
  'Ë|' => 'è—Ž',
  'Ë}' => 'è—‘',
  'Ë~' => 'è—’',
  'Ë€' => 'è—”',
  'Ë�' => 'è—–',
  'Ë‚' => 'è——',
  'Ëƒ' => 'è—˜',
  'Ë„' => 'è—™',
  'Ë…' => 'è—š',
  'Ë†' => 'è—›',
  'Ë‡' => 'è—�',
  'Ëˆ' => 'è—ž',
  'Ë‰' => 'è—Ÿ',
  'ËŠ' => 'è— ',
  'Ë‹' => 'è—¡',
  'ËŒ' => 'è—¢',
  'Ë�' => 'è—£',
  'ËŽ' => 'è—¥',
  'Ë�' => 'è—¦',
  'Ë�' => 'è—§',
  'Ë‘' => 'è—¨',
  'Ë’' => 'è—ª',
  'Ë“' => 'è—«',
  'Ë”' => 'è—¬',
  'Ë•' => 'è—­',
  'Ë–' => 'è—®',
  'Ë—' => 'è—¯',
  'Ë˜' => 'è—°',
  'Ë™' => 'è—±',
  'Ëš' => 'è—²',
  'Ë›' => 'è—³',
  'Ëœ' => 'è—´',
  'Ë�' => 'è—µ',
  'Ëž' => 'è—¶',
  'ËŸ' => 'è—·',
  'Ë ' => 'è—¸',
  'Ë¡' => 'æ�•',
  'Ë¢' => 'åˆ·',
  'Ë£' => 'è€�',
  'Ë¤' => 'æ‘”',
  'Ë¥' => 'è¡°',
  'Ë¦' => 'ç”©',
  'Ë§' => 'å¸…',
  'Ë¨' => 'æ “',
  'Ë©' => 'æ‹´',
  'Ëª' => 'éœœ',
  'Ë«' => 'å�Œ',
  'Ë¬' => 'çˆ½',
  'Ë­' => 'è°�',
  'Ë®' => 'æ°´',
  'Ë¯' => 'ç�¡',
  'Ë°' => 'ç¨Ž',
  'Ë±' => 'å�®',
  'Ë²' => 'çž¬',
  'Ë³' => 'é¡º',
  'Ë´' => 'èˆœ',
  'Ëµ' => 'è¯´',
  'Ë¶' => 'ç¡•',
  'Ë·' => 'æœ”',
  'Ë¸' => 'çƒ�',
  'Ë¹' => 'æ–¯',
  'Ëº' => 'æ’•',
  'Ë»' => 'å˜¶',
  'Ë¼' => 'æ€�',
  'Ë½' => 'ç§�',
  'Ë¾' => 'å�¸',
  'Ë¿' => 'ä¸�',
  'ËÀ' => 'æ­»',
  'ËÁ' => 'è‚†',
  'ËÂ' => 'å¯º',
  'ËÃ' => 'å—£',
  'ËÄ' => 'å››',
  'ËÅ' => 'ä¼º',
  'ËÆ' => 'ä¼¼',
  'ËÇ' => 'é¥²',
  'ËÈ' => 'å·³',
  'ËÉ' => 'æ�¾',
  'ËÊ' => 'è€¸',
  'ËË' => 'æ€‚',
  'ËÌ' => 'é¢‚',
  'ËÍ' => 'é€�',
  'ËÎ' => 'å®‹',
  'ËÏ' => 'è®¼',
  'ËÐ' => 'è¯µ',
  'ËÑ' => 'æ�œ',
  'ËÒ' => 'è‰˜',
  'ËÓ' => 'æ“ž',
  'ËÔ' => 'å—½',
  'ËÕ' => 'è‹�',
  'ËÖ' => 'é…¥',
  'Ë×' => 'ä¿—',
  'ËØ' => 'ç´ ',
  'ËÙ' => 'é€Ÿ',
  'ËÚ' => 'ç²Ÿ',
  'ËÛ' => 'åƒ³',
  'ËÜ' => 'å¡‘',
  'ËÝ' => 'æº¯',
  'ËÞ' => 'å®¿',
  'Ëß' => 'è¯‰',
  'Ëà' => 'è‚ƒ',
  'Ëá' => 'é…¸',
  'Ëâ' => 'è’œ',
  'Ëã' => 'ç®—',
  'Ëä' => 'è™½',
  'Ëå' => 'éš‹',
  'Ëæ' => 'éš�',
  'Ëç' => 'ç»¥',
  'Ëè' => 'é«“',
  'Ëé' => 'ç¢Ž',
  'Ëê' => 'å²�',
  'Ëë' => 'ç©—',
  'Ëì' => 'é�‚',
  'Ëí' => 'éš§',
  'Ëî' => 'ç¥Ÿ',
  'Ëï' => 'å­™',
  'Ëð' => 'æ�Ÿ',
  'Ëñ' => 'ç¬‹',
  'Ëò' => 'è“‘',
  'Ëó' => 'æ¢­',
  'Ëô' => 'å”†',
  'Ëõ' => 'ç¼©',
  'Ëö' => 'ç��',
  'Ë÷' => 'ç´¢',
  'Ëø' => 'é”�',
  'Ëù' => 'æ‰€',
  'Ëú' => 'å¡Œ',
  'Ëû' => 'ä»–',
  'Ëü' => 'å®ƒ',
  'Ëý' => 'å¥¹',
  'Ëþ' => 'å¡”',
  'Ì@' => 'è—¹',
  'ÌA' => 'è—º',
  'ÌB' => 'è—¼',
  'ÌC' => 'è—½',
  'ÌD' => 'è—¾',
  'ÌE' => 'è˜€',
  'ÌF' => 'è˜�',
  'ÌG' => 'è˜‚',
  'ÌH' => 'è˜ƒ',
  'ÌI' => 'è˜„',
  'ÌJ' => 'è˜†',
  'ÌK' => 'è˜‡',
  'ÌL' => 'è˜ˆ',
  'ÌM' => 'è˜‰',
  'ÌN' => 'è˜Š',
  'ÌO' => 'è˜‹',
  'ÌP' => 'è˜Œ',
  'ÌQ' => 'è˜�',
  'ÌR' => 'è˜Ž',
  'ÌS' => 'è˜�',
  'ÌT' => 'è˜�',
  'ÌU' => 'è˜’',
  'ÌV' => 'è˜“',
  'ÌW' => 'è˜”',
  'ÌX' => 'è˜•',
  'ÌY' => 'è˜—',
  'ÌZ' => 'è˜˜',
  'Ì[' => 'è˜™',
  'Ì\\' => 'è˜š',
  'Ì]' => 'è˜›',
  'Ì^' => 'è˜œ',
  'Ì_' => 'è˜�',
  'Ì`' => 'è˜ž',
  'Ìa' => 'è˜Ÿ',
  'Ìb' => 'è˜ ',
  'Ìc' => 'è˜¡',
  'Ìd' => 'è˜¢',
  'Ìe' => 'è˜£',
  'Ìf' => 'è˜¤',
  'Ìg' => 'è˜¥',
  'Ìh' => 'è˜¦',
  'Ìi' => 'è˜¨',
  'Ìj' => 'è˜ª',
  'Ìk' => 'è˜«',
  'Ìl' => 'è˜¬',
  'Ìm' => 'è˜­',
  'Ìn' => 'è˜®',
  'Ìo' => 'è˜¯',
  'Ìp' => 'è˜°',
  'Ìq' => 'è˜±',
  'Ìr' => 'è˜²',
  'Ìs' => 'è˜³',
  'Ìt' => 'è˜´',
  'Ìu' => 'è˜µ',
  'Ìv' => 'è˜¶',
  'Ìw' => 'è˜·',
  'Ìx' => 'è˜¹',
  'Ìy' => 'è˜º',
  'Ìz' => 'è˜»',
  'Ì{' => 'è˜½',
  'Ì|' => 'è˜¾',
  'Ì}' => 'è˜¿',
  'Ì~' => 'è™€',
  'Ì€' => 'è™�',
  'Ì�' => 'è™‚',
  'Ì‚' => 'è™ƒ',
  'Ìƒ' => 'è™„',
  'Ì„' => 'è™…',
  'Ì…' => 'è™†',
  'Ì†' => 'è™‡',
  'Ì‡' => 'è™ˆ',
  'Ìˆ' => 'è™‰',
  'Ì‰' => 'è™Š',
  'ÌŠ' => 'è™‹',
  'Ì‹' => 'è™Œ',
  'ÌŒ' => 'è™’',
  'Ì�' => 'è™“',
  'ÌŽ' => 'è™•',
  'Ì�' => 'è™–',
  'Ì�' => 'è™—',
  'Ì‘' => 'è™˜',
  'Ì’' => 'è™™',
  'Ì“' => 'è™›',
  'Ì”' => 'è™œ',
  'Ì•' => 'è™�',
  'Ì–' => 'è™Ÿ',
  'Ì—' => 'è™ ',
  'Ì˜' => 'è™¡',
  'Ì™' => 'è™£',
  'Ìš' => 'è™¤',
  'Ì›' => 'è™¥',
  'Ìœ' => 'è™¦',
  'Ì�' => 'è™§',
  'Ìž' => 'è™¨',
  'ÌŸ' => 'è™©',
  'Ì ' => 'è™ª',
  'Ì¡' => 'ç�­',
  'Ì¢' => 'æŒž',
  'Ì£' => 'è¹‹',
  'Ì¤' => 'è¸�',
  'Ì¥' => 'èƒŽ',
  'Ì¦' => 'è‹”',
  'Ì§' => 'æŠ¬',
  'Ì¨' => 'å�°',
  'Ì©' => 'æ³°',
  'Ìª' => 'é…ž',
  'Ì«' => 'å¤ª',
  'Ì¬' => 'æ€�',
  'Ì­' => 'æ±°',
  'Ì®' => 'å��',
  'Ì¯' => 'æ‘Š',
  'Ì°' => 'è´ª',
  'Ì±' => 'ç˜«',
  'Ì²' => 'æ»©',
  'Ì³' => 'å�›',
  'Ì´' => 'æª€',
  'Ìµ' => 'ç—°',
  'Ì¶' => 'æ½­',
  'Ì·' => 'è°­',
  'Ì¸' => 'è°ˆ',
  'Ì¹' => 'å�¦',
  'Ìº' => 'æ¯¯',
  'Ì»' => 'è¢’',
  'Ì¼' => 'ç¢³',
  'Ì½' => 'æŽ¢',
  'Ì¾' => 'å�¹',
  'Ì¿' => 'ç‚­',
  'ÌÀ' => 'æ±¤',
  'ÌÁ' => 'å¡˜',
  'ÌÂ' => 'æ�ª',
  'ÌÃ' => 'å ‚',
  'ÌÄ' => 'æ£ ',
  'ÌÅ' => 'è†›',
  'ÌÆ' => 'å”�',
  'ÌÇ' => 'ç³–',
  'ÌÈ' => 'å€˜',
  'ÌÉ' => 'èºº',
  'ÌÊ' => 'æ·Œ',
  'ÌË' => 'è¶Ÿ',
  'ÌÌ' => 'çƒ«',
  'ÌÍ' => 'æŽ�',
  'ÌÎ' => 'æ¶›',
  'ÌÏ' => 'æ»”',
  'ÌÐ' => 'ç»¦',
  'ÌÑ' => 'è�„',
  'ÌÒ' => 'æ¡ƒ',
  'ÌÓ' => 'é€ƒ',
  'ÌÔ' => 'æ·˜',
  'ÌÕ' => 'é™¶',
  'ÌÖ' => 'è®¨',
  'Ì×' => 'å¥—',
  'ÌØ' => 'ç‰¹',
  'ÌÙ' => 'è—¤',
  'ÌÚ' => 'è…¾',
  'ÌÛ' => 'ç–¼',
  'ÌÜ' => 'èªŠ',
  'ÌÝ' => 'æ¢¯',
  'ÌÞ' => 'å‰”',
  'Ìß' => 'è¸¢',
  'Ìà' => 'é”‘',
  'Ìá' => 'æ��',
  'Ìâ' => 'é¢˜',
  'Ìã' => 'è¹„',
  'Ìä' => 'å•¼',
  'Ìå' => 'ä½“',
  'Ìæ' => 'æ›¿',
  'Ìç' => 'åš�',
  'Ìè' => 'æƒ•',
  'Ìé' => 'æ¶•',
  'Ìê' => 'å‰ƒ',
  'Ìë' => 'å±‰',
  'Ìì' => 'å¤©',
  'Ìí' => 'æ·»',
  'Ìî' => 'å¡«',
  'Ìï' => 'ç”°',
  'Ìð' => 'ç”œ',
  'Ìñ' => 'æ�¬',
  'Ìò' => 'èˆ”',
  'Ìó' => 'è…†',
  'Ìô' => 'æŒ‘',
  'Ìõ' => 'æ�¡',
  'Ìö' => 'è¿¢',
  'Ì÷' => 'çœº',
  'Ìø' => 'è·³',
  'Ìù' => 'è´´',
  'Ìú' => 'é“�',
  'Ìû' => 'å¸–',
  'Ìü' => 'åŽ…',
  'Ìý' => 'å�¬',
  'Ìþ' => 'çƒƒ',
  'Í@' => 'è™­',
  'ÍA' => 'è™¯',
  'ÍB' => 'è™°',
  'ÍC' => 'è™²',
  'ÍD' => 'è™³',
  'ÍE' => 'è™´',
  'ÍF' => 'è™µ',
  'ÍG' => 'è™¶',
  'ÍH' => 'è™·',
  'ÍI' => 'è™¸',
  'ÍJ' => 'èšƒ',
  'ÍK' => 'èš„',
  'ÍL' => 'èš…',
  'ÍM' => 'èš†',
  'ÍN' => 'èš‡',
  'ÍO' => 'èšˆ',
  'ÍP' => 'èš‰',
  'ÍQ' => 'èšŽ',
  'ÍR' => 'èš�',
  'ÍS' => 'èš�',
  'ÍT' => 'èš‘',
  'ÍU' => 'èš’',
  'ÍV' => 'èš”',
  'ÍW' => 'èš–',
  'ÍX' => 'èš—',
  'ÍY' => 'èš˜',
  'ÍZ' => 'èš™',
  'Í[' => 'èšš',
  'Í\\' => 'èš›',
  'Í]' => 'èšž',
  'Í^' => 'èšŸ',
  'Í_' => 'èš ',
  'Í`' => 'èš¡',
  'Ía' => 'èš¢',
  'Íb' => 'èš¥',
  'Íc' => 'èš¦',
  'Íd' => 'èš«',
  'Íe' => 'èš­',
  'Íf' => 'èš®',
  'Íg' => 'èš²',
  'Íh' => 'èš³',
  'Íi' => 'èš·',
  'Íj' => 'èš¸',
  'Ík' => 'èš¹',
  'Íl' => 'èš»',
  'Ím' => 'èš¼',
  'Ín' => 'èš½',
  'Ío' => 'èš¾',
  'Íp' => 'èš¿',
  'Íq' => 'è›�',
  'Ír' => 'è›‚',
  'Ís' => 'è›ƒ',
  'Ít' => 'è›…',
  'Íu' => 'è›ˆ',
  'Ív' => 'è›Œ',
  'Íw' => 'è›�',
  'Íx' => 'è›’',
  'Íy' => 'è›“',
  'Íz' => 'è›•',
  'Í{' => 'è›–',
  'Í|' => 'è›—',
  'Í}' => 'è›š',
  'Í~' => 'è›œ',
  'Í€' => 'è›�',
  'Í�' => 'è› ',
  'Í‚' => 'è›¡',
  'Íƒ' => 'è›¢',
  'Í„' => 'è›£',
  'Í…' => 'è›¥',
  'Í†' => 'è›¦',
  'Í‡' => 'è›§',
  'Íˆ' => 'è›¨',
  'Í‰' => 'è›ª',
  'ÍŠ' => 'è›«',
  'Í‹' => 'è›¬',
  'ÍŒ' => 'è›¯',
  'Í�' => 'è›µ',
  'ÍŽ' => 'è›¶',
  'Í�' => 'è›·',
  'Í�' => 'è›º',
  'Í‘' => 'è›»',
  'Í’' => 'è›¼',
  'Í“' => 'è›½',
  'Í”' => 'è›¿',
  'Í•' => 'èœ�',
  'Í–' => 'èœ„',
  'Í—' => 'èœ…',
  'Í˜' => 'èœ†',
  'Í™' => 'èœ‹',
  'Íš' => 'èœŒ',
  'Í›' => 'èœŽ',
  'Íœ' => 'èœ�',
  'Í�' => 'èœ�',
  'Íž' => 'èœ‘',
  'ÍŸ' => 'èœ”',
  'Í ' => 'èœ–',
  'Í¡' => 'æ±€',
  'Í¢' => 'å»·',
  'Í£' => 'å�œ',
  'Í¤' => 'äº­',
  'Í¥' => 'åº­',
  'Í¦' => 'æŒº',
  'Í§' => 'è‰‡',
  'Í¨' => 'é€š',
  'Í©' => 'æ¡�',
  'Íª' => 'é…®',
  'Í«' => 'çž³',
  'Í¬' => 'å�Œ',
  'Í­' => 'é“œ',
  'Í®' => 'å½¤',
  'Í¯' => 'ç«¥',
  'Í°' => 'æ¡¶',
  'Í±' => 'æ�…',
  'Í²' => 'ç­’',
  'Í³' => 'ç»Ÿ',
  'Í´' => 'ç—›',
  'Íµ' => 'å�·',
  'Í¶' => 'æŠ•',
  'Í·' => 'å¤´',
  'Í¸' => 'é€�',
  'Í¹' => 'å‡¸',
  'Íº' => 'ç§ƒ',
  'Í»' => 'çª�',
  'Í¼' => 'å›¾',
  'Í½' => 'å¾’',
  'Í¾' => 'é€”',
  'Í¿' => 'æ¶‚',
  'ÍÀ' => 'å± ',
  'ÍÁ' => 'åœŸ',
  'ÍÂ' => 'å��',
  'ÍÃ' => 'å…”',
  'ÍÄ' => 'æ¹�',
  'ÍÅ' => 'å›¢',
  'ÍÆ' => 'æŽ¨',
  'ÍÇ' => 'é¢“',
  'ÍÈ' => 'è…¿',
  'ÍÉ' => 'èœ•',
  'ÍÊ' => 'è¤ª',
  'ÍË' => 'é€€',
  'ÍÌ' => 'å�ž',
  'ÍÍ' => 'å±¯',
  'ÍÎ' => 'è‡€',
  'ÍÏ' => 'æ‹–',
  'ÍÐ' => 'æ‰˜',
  'ÍÑ' => 'è„±',
  'ÍÒ' => 'é¸µ',
  'ÍÓ' => 'é™€',
  'ÍÔ' => 'é©®',
  'ÍÕ' => 'é©¼',
  'ÍÖ' => 'æ¤­',
  'Í×' => 'å¦¥',
  'ÍØ' => 'æ‹“',
  'ÍÙ' => 'å”¾',
  'ÍÚ' => 'æŒ–',
  'ÍÛ' => 'å“‡',
  'ÍÜ' => 'è›™',
  'ÍÝ' => 'æ´¼',
  'ÍÞ' => 'å¨ƒ',
  'Íß' => 'ç“¦',
  'Íà' => 'è¢œ',
  'Íá' => 'æ­ª',
  'Íâ' => 'å¤–',
  'Íã' => 'è±Œ',
  'Íä' => 'å¼¯',
  'Íå' => 'æ¹¾',
  'Íæ' => 'çŽ©',
  'Íç' => 'é¡½',
  'Íè' => 'ä¸¸',
  'Íé' => 'çƒ·',
  'Íê' => 'å®Œ',
  'Íë' => 'ç¢—',
  'Íì' => 'æŒ½',
  'Íí' => 'æ™š',
  'Íî' => 'çš–',
  'Íï' => 'æƒ‹',
  'Íð' => 'å®›',
  'Íñ' => 'å©‰',
  'Íò' => 'ä¸‡',
  'Íó' => 'è…•',
  'Íô' => 'æ±ª',
  'Íõ' => 'çŽ‹',
  'Íö' => 'äº¡',
  'Í÷' => 'æž‰',
  'Íø' => 'ç½‘',
  'Íù' => 'å¾€',
  'Íú' => 'æ—º',
  'Íû' => 'æœ›',
  'Íü' => 'å¿˜',
  'Íý' => 'å¦„',
  'Íþ' => 'å¨�',
  'Î@' => 'èœ™',
  'ÎA' => 'èœ›',
  'ÎB' => 'èœ�',
  'ÎC' => 'èœŸ',
  'ÎD' => 'èœ ',
  'ÎE' => 'èœ¤',
  'ÎF' => 'èœ¦',
  'ÎG' => 'èœ§',
  'ÎH' => 'èœ¨',
  'ÎI' => 'èœª',
  'ÎJ' => 'èœ«',
  'ÎK' => 'èœ¬',
  'ÎL' => 'èœ­',
  'ÎM' => 'èœ¯',
  'ÎN' => 'èœ°',
  'ÎO' => 'èœ²',
  'ÎP' => 'èœ³',
  'ÎQ' => 'èœµ',
  'ÎR' => 'èœ¶',
  'ÎS' => 'èœ¸',
  'ÎT' => 'èœ¹',
  'ÎU' => 'èœº',
  'ÎV' => 'èœ¼',
  'ÎW' => 'èœ½',
  'ÎX' => 'è�€',
  'ÎY' => 'è��',
  'ÎZ' => 'è�‚',
  'Î[' => 'è�ƒ',
  'Î\\' => 'è�„',
  'Î]' => 'è�…',
  'Î^' => 'è�†',
  'Î_' => 'è�Š',
  'Î`' => 'è�‹',
  'Îa' => 'è��',
  'Îb' => 'è��',
  'Îc' => 'è��',
  'Îd' => 'è�‘',
  'Îe' => 'è�’',
  'Îf' => 'è�”',
  'Îg' => 'è�•',
  'Îh' => 'è�–',
  'Îi' => 'è�˜',
  'Îj' => 'è�š',
  'Îk' => 'è�›',
  'Îl' => 'è�œ',
  'Îm' => 'è��',
  'În' => 'è�ž',
  'Îo' => 'è�Ÿ',
  'Îp' => 'è�¡',
  'Îq' => 'è�¢',
  'Îr' => 'è�¦',
  'Îs' => 'è�§',
  'Ît' => 'è�¨',
  'Îu' => 'è�©',
  'Îv' => 'è�ª',
  'Îw' => 'è�«',
  'Îx' => 'è�¬',
  'Îy' => 'è�­',
  'Îz' => 'è�¯',
  'Î{' => 'è�±',
  'Î|' => 'è�²',
  'Î}' => 'è�³',
  'Î~' => 'è�µ',
  'Î€' => 'è�·',
  'Î�' => 'è�¸',
  'Î‚' => 'è�¹',
  'Îƒ' => 'è�º',
  'Î„' => 'è�¿',
  'Î…' => 'èž€',
  'Î†' => 'èž�',
  'Î‡' => 'èž„',
  'Îˆ' => 'èž†',
  'Î‰' => 'èž‡',
  'ÎŠ' => 'èž‰',
  'Î‹' => 'èžŠ',
  'ÎŒ' => 'èžŒ',
  'Î�' => 'èžŽ',
  'ÎŽ' => 'èž�',
  'Î�' => 'èž�',
  'Î�' => 'èž‘',
  'Î‘' => 'èž’',
  'Î’' => 'èž”',
  'Î“' => 'èž•',
  'Î”' => 'èž–',
  'Î•' => 'èž˜',
  'Î–' => 'èž™',
  'Î—' => 'èžš',
  'Î˜' => 'èž›',
  'Î™' => 'èžœ',
  'Îš' => 'èž�',
  'Î›' => 'èžž',
  'Îœ' => 'èž ',
  'Î�' => 'èž¡',
  'Îž' => 'èž¢',
  'ÎŸ' => 'èž£',
  'Î ' => 'èž¤',
  'Î¡' => 'å·�',
  'Î¢' => 'å¾®',
  'Î£' => 'å�±',
  'Î¤' => 'éŸ¦',
  'Î¥' => 'è¿�',
  'Î¦' => 'æ¡…',
  'Î§' => 'å›´',
  'Î¨' => 'å”¯',
  'Î©' => 'æƒŸ',
  'Îª' => 'ä¸º',
  'Î«' => 'æ½�',
  'Î¬' => 'ç»´',
  'Î­' => 'è‹‡',
  'Î®' => 'è�Ž',
  'Î¯' => 'å§”',
  'Î°' => 'ä¼Ÿ',
  'Î±' => 'ä¼ª',
  'Î²' => 'å°¾',
  'Î³' => 'çº¬',
  'Î´' => 'æœª',
  'Îµ' => 'è”š',
  'Î¶' => 'å‘³',
  'Î·' => 'ç•�',
  'Î¸' => 'èƒƒ',
  'Î¹' => 'å–‚',
  'Îº' => 'é­�',
  'Î»' => 'ä½�',
  'Î¼' => 'æ¸­',
  'Î½' => 'è°“',
  'Î¾' => 'å°‰',
  'Î¿' => 'æ…°',
  'ÎÀ' => 'å�«',
  'ÎÁ' => 'ç˜Ÿ',
  'ÎÂ' => 'æ¸©',
  'ÎÃ' => 'èšŠ',
  'ÎÄ' => 'æ–‡',
  'ÎÅ' => 'é—»',
  'ÎÆ' => 'çº¹',
  'ÎÇ' => 'å�»',
  'ÎÈ' => 'ç¨³',
  'ÎÉ' => 'ç´Š',
  'ÎÊ' => 'é—®',
  'ÎË' => 'å—¡',
  'ÎÌ' => 'ç¿�',
  'ÎÍ' => 'ç“®',
  'ÎÎ' => 'æŒ�',
  'ÎÏ' => 'èœ—',
  'ÎÐ' => 'æ¶¡',
  'ÎÑ' => 'çª�',
  'ÎÒ' => 'æˆ‘',
  'ÎÓ' => 'æ–¡',
  'ÎÔ' => 'å�§',
  'ÎÕ' => 'æ�¡',
  'ÎÖ' => 'æ²ƒ',
  'Î×' => 'å·«',
  'ÎØ' => 'å‘œ',
  'ÎÙ' => 'é’¨',
  'ÎÚ' => 'ä¹Œ',
  'ÎÛ' => 'æ±¡',
  'ÎÜ' => 'è¯¬',
  'ÎÝ' => 'å±‹',
  'ÎÞ' => 'æ— ',
  'Îß' => 'èŠœ',
  'Îà' => 'æ¢§',
  'Îá' => 'å�¾',
  'Îâ' => 'å�´',
  'Îã' => 'æ¯‹',
  'Îä' => 'æ­¦',
  'Îå' => 'äº”',
  'Îæ' => 'æ�‚',
  'Îç' => 'å�ˆ',
  'Îè' => 'èˆž',
  'Îé' => 'ä¼�',
  'Îê' => 'ä¾®',
  'Îë' => 'å�ž',
  'Îì' => 'æˆŠ',
  'Îí' => 'é›¾',
  'Îî' => 'æ™¤',
  'Îï' => 'ç‰©',
  'Îð' => 'å‹¿',
  'Îñ' => 'åŠ¡',
  'Îò' => 'æ‚Ÿ',
  'Îó' => 'è¯¯',
  'Îô' => 'æ˜”',
  'Îõ' => 'ç†™',
  'Îö' => 'æž�',
  'Î÷' => 'è¥¿',
  'Îø' => 'ç¡’',
  'Îù' => 'çŸ½',
  'Îú' => 'æ™°',
  'Îû' => 'å˜»',
  'Îü' => 'å�¸',
  'Îý' => 'é”¡',
  'Îþ' => 'ç‰º',
  'Ï@' => 'èž¥',
  'ÏA' => 'èž¦',
  'ÏB' => 'èž§',
  'ÏC' => 'èž©',
  'ÏD' => 'èžª',
  'ÏE' => 'èž®',
  'ÏF' => 'èž°',
  'ÏG' => 'èž±',
  'ÏH' => 'èž²',
  'ÏI' => 'èž´',
  'ÏJ' => 'èž¶',
  'ÏK' => 'èž·',
  'ÏL' => 'èž¸',
  'ÏM' => 'èž¹',
  'ÏN' => 'èž»',
  'ÏO' => 'èž¼',
  'ÏP' => 'èž¾',
  'ÏQ' => 'èž¿',
  'ÏR' => 'èŸ�',
  'ÏS' => 'èŸ‚',
  'ÏT' => 'èŸƒ',
  'ÏU' => 'èŸ„',
  'ÏV' => 'èŸ…',
  'ÏW' => 'èŸ‡',
  'ÏX' => 'èŸˆ',
  'ÏY' => 'èŸ‰',
  'ÏZ' => 'èŸŒ',
  'Ï[' => 'èŸ�',
  'Ï\\' => 'èŸŽ',
  'Ï]' => 'èŸ�',
  'Ï^' => 'èŸ�',
  'Ï_' => 'èŸ”',
  'Ï`' => 'èŸ•',
  'Ïa' => 'èŸ–',
  'Ïb' => 'èŸ—',
  'Ïc' => 'èŸ˜',
  'Ïd' => 'èŸ™',
  'Ïe' => 'èŸš',
  'Ïf' => 'èŸœ',
  'Ïg' => 'èŸ�',
  'Ïh' => 'èŸž',
  'Ïi' => 'èŸŸ',
  'Ïj' => 'èŸ¡',
  'Ïk' => 'èŸ¢',
  'Ïl' => 'èŸ£',
  'Ïm' => 'èŸ¤',
  'Ïn' => 'èŸ¦',
  'Ïo' => 'èŸ§',
  'Ïp' => 'èŸ¨',
  'Ïq' => 'èŸ©',
  'Ïr' => 'èŸ«',
  'Ïs' => 'èŸ¬',
  'Ït' => 'èŸ­',
  'Ïu' => 'èŸ¯',
  'Ïv' => 'èŸ°',
  'Ïw' => 'èŸ±',
  'Ïx' => 'èŸ²',
  'Ïy' => 'èŸ³',
  'Ïz' => 'èŸ´',
  'Ï{' => 'èŸµ',
  'Ï|' => 'èŸ¶',
  'Ï}' => 'èŸ·',
  'Ï~' => 'èŸ¸',
  'Ï€' => 'èŸº',
  'Ï�' => 'èŸ»',
  'Ï‚' => 'èŸ¼',
  'Ïƒ' => 'èŸ½',
  'Ï„' => 'èŸ¿',
  'Ï…' => 'è €',
  'Ï†' => 'è �',
  'Ï‡' => 'è ‚',
  'Ïˆ' => 'è „',
  'Ï‰' => 'è …',
  'ÏŠ' => 'è †',
  'Ï‹' => 'è ‡',
  'ÏŒ' => 'è ˆ',
  'Ï�' => 'è ‰',
  'ÏŽ' => 'è ‹',
  'Ï�' => 'è Œ',
  'Ï�' => 'è �',
  'Ï‘' => 'è Ž',
  'Ï’' => 'è �',
  'Ï“' => 'è �',
  'Ï”' => 'è ‘',
  'Ï•' => 'è ’',
  'Ï–' => 'è ”',
  'Ï—' => 'è —',
  'Ï˜' => 'è ˜',
  'Ï™' => 'è ™',
  'Ïš' => 'è š',
  'Ï›' => 'è œ',
  'Ïœ' => 'è �',
  'Ï�' => 'è ž',
  'Ïž' => 'è Ÿ',
  'ÏŸ' => 'è  ',
  'Ï ' => 'è £',
  'Ï¡' => 'ç¨€',
  'Ï¢' => 'æ�¯',
  'Ï£' => 'å¸Œ',
  'Ï¤' => 'æ‚‰',
  'Ï¥' => 'è†�',
  'Ï¦' => 'å¤•',
  'Ï§' => 'æƒœ',
  'Ï¨' => 'ç†„',
  'Ï©' => 'çƒ¯',
  'Ïª' => 'æºª',
  'Ï«' => 'æ±�',
  'Ï¬' => 'çŠ€',
  'Ï­' => 'æª„',
  'Ï®' => 'è¢­',
  'Ï¯' => 'å¸­',
  'Ï°' => 'ä¹ ',
  'Ï±' => 'åª³',
  'Ï²' => 'å–œ',
  'Ï³' => 'é“£',
  'Ï´' => 'æ´—',
  'Ïµ' => 'ç³»',
  'Ï¶' => 'éš™',
  'Ï·' => 'æˆ�',
  'Ï¸' => 'ç»†',
  'Ï¹' => 'çžŽ',
  'Ïº' => 'è™¾',
  'Ï»' => 'åŒ£',
  'Ï¼' => 'éœž',
  'Ï½' => 'è¾–',
  'Ï¾' => 'æš‡',
  'Ï¿' => 'å³¡',
  'ÏÀ' => 'ä¾ ',
  'ÏÁ' => 'ç‹­',
  'ÏÂ' => 'ä¸‹',
  'ÏÃ' => 'åŽ¦',
  'ÏÄ' => 'å¤�',
  'ÏÅ' => 'å�“',
  'ÏÆ' => 'æŽ€',
  'ÏÇ' => 'é”¨',
  'ÏÈ' => 'å…ˆ',
  'ÏÉ' => 'ä»™',
  'ÏÊ' => 'é²œ',
  'ÏË' => 'çº¤',
  'ÏÌ' => 'å’¸',
  'ÏÍ' => 'è´¤',
  'ÏÎ' => 'è¡”',
  'ÏÏ' => 'èˆ·',
  'ÏÐ' => 'é—²',
  'ÏÑ' => 'æ¶Ž',
  'ÏÒ' => 'å¼¦',
  'ÏÓ' => 'å«Œ',
  'ÏÔ' => 'æ˜¾',
  'ÏÕ' => 'é™©',
  'ÏÖ' => 'çŽ°',
  'Ï×' => 'çŒ®',
  'ÏØ' => 'åŽ¿',
  'ÏÙ' => 'è…º',
  'ÏÚ' => 'é¦…',
  'ÏÛ' => 'ç¾¡',
  'ÏÜ' => 'å®ª',
  'ÏÝ' => 'é™·',
  'ÏÞ' => 'é™�',
  'Ïß' => 'çº¿',
  'Ïà' => 'ç›¸',
  'Ïá' => 'åŽ¢',
  'Ïâ' => 'é•¶',
  'Ïã' => 'é¦™',
  'Ïä' => 'ç®±',
  'Ïå' => 'è¥„',
  'Ïæ' => 'æ¹˜',
  'Ïç' => 'ä¹¡',
  'Ïè' => 'ç¿”',
  'Ïé' => 'ç¥¥',
  'Ïê' => 'è¯¦',
  'Ïë' => 'æƒ³',
  'Ïì' => 'å“�',
  'Ïí' => 'äº«',
  'Ïî' => 'é¡¹',
  'Ïï' => 'å··',
  'Ïð' => 'æ©¡',
  'Ïñ' => 'åƒ�',
  'Ïò' => 'å�‘',
  'Ïó' => 'è±¡',
  'Ïô' => 'è�§',
  'Ïõ' => 'ç¡�',
  'Ïö' => 'éœ„',
  'Ï÷' => 'å‰Š',
  'Ïø' => 'å“®',
  'Ïù' => 'åš£',
  'Ïú' => 'é”€',
  'Ïû' => 'æ¶ˆ',
  'Ïü' => 'å®µ',
  'Ïý' => 'æ·†',
  'Ïþ' => 'æ™“',
  'Ð@' => 'è ¤',
  'ÐA' => 'è ¥',
  'ÐB' => 'è ¦',
  'ÐC' => 'è §',
  'ÐD' => 'è ¨',
  'ÐE' => 'è ©',
  'ÐF' => 'è ª',
  'ÐG' => 'è «',
  'ÐH' => 'è ¬',
  'ÐI' => 'è ­',
  'ÐJ' => 'è ®',
  'ÐK' => 'è ¯',
  'ÐL' => 'è °',
  'ÐM' => 'è ±',
  'ÐN' => 'è ³',
  'ÐO' => 'è ´',
  'ÐP' => 'è µ',
  'ÐQ' => 'è ¶',
  'ÐR' => 'è ·',
  'ÐS' => 'è ¸',
  'ÐT' => 'è º',
  'ÐU' => 'è »',
  'ÐV' => 'è ½',
  'ÐW' => 'è ¾',
  'ÐX' => 'è ¿',
  'ÐY' => 'è¡�',
  'ÐZ' => 'è¡‚',
  'Ð[' => 'è¡ƒ',
  'Ð\\' => 'è¡†',
  'Ð]' => 'è¡‡',
  'Ð^' => 'è¡ˆ',
  'Ð_' => 'è¡‰',
  'Ð`' => 'è¡Š',
  'Ða' => 'è¡‹',
  'Ðb' => 'è¡Ž',
  'Ðc' => 'è¡�',
  'Ðd' => 'è¡�',
  'Ðe' => 'è¡‘',
  'Ðf' => 'è¡’',
  'Ðg' => 'è¡“',
  'Ðh' => 'è¡•',
  'Ði' => 'è¡–',
  'Ðj' => 'è¡˜',
  'Ðk' => 'è¡š',
  'Ðl' => 'è¡›',
  'Ðm' => 'è¡œ',
  'Ðn' => 'è¡�',
  'Ðo' => 'è¡ž',
  'Ðp' => 'è¡Ÿ',
  'Ðq' => 'è¡ ',
  'Ðr' => 'è¡¦',
  'Ðs' => 'è¡§',
  'Ðt' => 'è¡ª',
  'Ðu' => 'è¡­',
  'Ðv' => 'è¡¯',
  'Ðw' => 'è¡±',
  'Ðx' => 'è¡³',
  'Ðy' => 'è¡´',
  'Ðz' => 'è¡µ',
  'Ð{' => 'è¡¶',
  'Ð|' => 'è¡¸',
  'Ð}' => 'è¡¹',
  'Ð~' => 'è¡º',
  'Ð€' => 'è¡»',
  'Ð�' => 'è¡¼',
  'Ð‚' => 'è¢€',
  'Ðƒ' => 'è¢ƒ',
  'Ð„' => 'è¢†',
  'Ð…' => 'è¢‡',
  'Ð†' => 'è¢‰',
  'Ð‡' => 'è¢Š',
  'Ðˆ' => 'è¢Œ',
  'Ð‰' => 'è¢Ž',
  'ÐŠ' => 'è¢�',
  'Ð‹' => 'è¢�',
  'ÐŒ' => 'è¢‘',
  'Ð�' => 'è¢“',
  'ÐŽ' => 'è¢”',
  'Ð�' => 'è¢•',
  'Ð�' => 'è¢—',
  'Ð‘' => 'è¢˜',
  'Ð’' => 'è¢™',
  'Ð“' => 'è¢š',
  'Ð”' => 'è¢›',
  'Ð•' => 'è¢�',
  'Ð–' => 'è¢ž',
  'Ð—' => 'è¢Ÿ',
  'Ð˜' => 'è¢ ',
  'Ð™' => 'è¢¡',
  'Ðš' => 'è¢£',
  'Ð›' => 'è¢¥',
  'Ðœ' => 'è¢¦',
  'Ð�' => 'è¢§',
  'Ðž' => 'è¢¨',
  'ÐŸ' => 'è¢©',
  'Ð ' => 'è¢ª',
  'Ð¡' => 'å°�',
  'Ð¢' => 'å­�',
  'Ð£' => 'æ ¡',
  'Ð¤' => 'è‚–',
  'Ð¥' => 'å•¸',
  'Ð¦' => 'ç¬‘',
  'Ð§' => 'æ•ˆ',
  'Ð¨' => 'æ¥”',
  'Ð©' => 'äº›',
  'Ðª' => 'æ­‡',
  'Ð«' => 'è�Ž',
  'Ð¬' => 'éž‹',
  'Ð­' => 'å��',
  'Ð®' => 'æŒŸ',
  'Ð¯' => 'æ�º',
  'Ð°' => 'é‚ª',
  'Ð±' => 'æ–œ',
  'Ð²' => 'èƒ�',
  'Ð³' => 'è°�',
  'Ð´' => 'å†™',
  'Ðµ' => 'æ¢°',
  'Ð¶' => 'å�¸',
  'Ð·' => 'èŸ¹',
  'Ð¸' => 'æ‡ˆ',
  'Ð¹' => 'æ³„',
  'Ðº' => 'æ³»',
  'Ð»' => 'è°¢',
  'Ð¼' => 'å±‘',
  'Ð½' => 'è–ª',
  'Ð¾' => 'èŠ¯',
  'Ð¿' => 'é”Œ',
  'ÐÀ' => 'æ¬£',
  'ÐÁ' => 'è¾›',
  'ÐÂ' => 'æ–°',
  'ÐÃ' => 'å¿»',
  'ÐÄ' => 'å¿ƒ',
  'ÐÅ' => 'ä¿¡',
  'ÐÆ' => 'è¡…',
  'ÐÇ' => 'æ˜Ÿ',
  'ÐÈ' => 'è…¥',
  'ÐÉ' => 'çŒ©',
  'ÐÊ' => 'æƒº',
  'ÐË' => 'å…´',
  'ÐÌ' => 'åˆ‘',
  'ÐÍ' => 'åž‹',
  'ÐÎ' => 'å½¢',
  'ÐÏ' => 'é‚¢',
  'ÐÐ' => 'è¡Œ',
  'ÐÑ' => 'é†’',
  'ÐÒ' => 'å¹¸',
  'ÐÓ' => 'æ��',
  'ÐÔ' => 'æ€§',
  'ÐÕ' => 'å§“',
  'ÐÖ' => 'å…„',
  'Ð×' => 'å‡¶',
  'ÐØ' => 'èƒ¸',
  'ÐÙ' => 'åŒˆ',
  'ÐÚ' => 'æ±¹',
  'ÐÛ' => 'é›„',
  'ÐÜ' => 'ç†Š',
  'ÐÝ' => 'ä¼‘',
  'ÐÞ' => 'ä¿®',
  'Ðß' => 'ç¾ž',
  'Ðà' => 'æœ½',
  'Ðá' => 'å—…',
  'Ðâ' => 'é”ˆ',
  'Ðã' => 'ç§€',
  'Ðä' => 'è¢–',
  'Ðå' => 'ç»£',
  'Ðæ' => 'å¢Ÿ',
  'Ðç' => 'æˆŒ',
  'Ðè' => 'éœ€',
  'Ðé' => 'è™š',
  'Ðê' => 'å˜˜',
  'Ðë' => 'é¡»',
  'Ðì' => 'å¾�',
  'Ðí' => 'è®¸',
  'Ðî' => 'è“„',
  'Ðï' => 'é…—',
  'Ðð' => 'å�™',
  'Ðñ' => 'æ—­',
  'Ðò' => 'åº�',
  'Ðó' => 'ç•œ',
  'Ðô' => 'æ�¤',
  'Ðõ' => 'çµ®',
  'Ðö' => 'å©¿',
  'Ð÷' => 'ç»ª',
  'Ðø' => 'ç»­',
  'Ðù' => 'è½©',
  'Ðú' => 'å–§',
  'Ðû' => 'å®£',
  'Ðü' => 'æ‚¬',
  'Ðý' => 'æ—‹',
  'Ðþ' => 'çŽ„',
  'Ñ@' => 'è¢¬',
  'ÑA' => 'è¢®',
  'ÑB' => 'è¢¯',
  'ÑC' => 'è¢°',
  'ÑD' => 'è¢²',
  'ÑE' => 'è¢³',
  'ÑF' => 'è¢´',
  'ÑG' => 'è¢µ',
  'ÑH' => 'è¢¶',
  'ÑI' => 'è¢¸',
  'ÑJ' => 'è¢¹',
  'ÑK' => 'è¢º',
  'ÑL' => 'è¢»',
  'ÑM' => 'è¢½',
  'ÑN' => 'è¢¾',
  'ÑO' => 'è¢¿',
  'ÑP' => 'è£€',
  'ÑQ' => 'è£ƒ',
  'ÑR' => 'è£„',
  'ÑS' => 'è£‡',
  'ÑT' => 'è£ˆ',
  'ÑU' => 'è£Š',
  'ÑV' => 'è£‹',
  'ÑW' => 'è£Œ',
  'ÑX' => 'è£�',
  'ÑY' => 'è£�',
  'ÑZ' => 'è£�',
  'Ñ[' => 'è£‘',
  'Ñ\\' => 'è£“',
  'Ñ]' => 'è£–',
  'Ñ^' => 'è£—',
  'Ñ_' => 'è£š',
  'Ñ`' => 'è£›',
  'Ña' => 'è£œ',
  'Ñb' => 'è£�',
  'Ñc' => 'è£ž',
  'Ñd' => 'è£ ',
  'Ñe' => 'è£¡',
  'Ñf' => 'è£¦',
  'Ñg' => 'è£§',
  'Ñh' => 'è£©',
  'Ñi' => 'è£ª',
  'Ñj' => 'è£«',
  'Ñk' => 'è£¬',
  'Ñl' => 'è£­',
  'Ñm' => 'è£®',
  'Ñn' => 'è£¯',
  'Ño' => 'è£²',
  'Ñp' => 'è£µ',
  'Ñq' => 'è£¶',
  'Ñr' => 'è£·',
  'Ñs' => 'è£º',
  'Ñt' => 'è£»',
  'Ñu' => 'è£½',
  'Ñv' => 'è£¿',
  'Ñw' => 'è¤€',
  'Ñx' => 'è¤�',
  'Ñy' => 'è¤ƒ',
  'Ñz' => 'è¤„',
  'Ñ{' => 'è¤…',
  'Ñ|' => 'è¤†',
  'Ñ}' => 'è¤‡',
  'Ñ~' => 'è¤ˆ',
  'Ñ€' => 'è¤‰',
  'Ñ�' => 'è¤‹',
  'Ñ‚' => 'è¤Œ',
  'Ñƒ' => 'è¤�',
  'Ñ„' => 'è¤Ž',
  'Ñ…' => 'è¤�',
  'Ñ†' => 'è¤‘',
  'Ñ‡' => 'è¤”',
  'Ñˆ' => 'è¤•',
  'Ñ‰' => 'è¤–',
  'ÑŠ' => 'è¤—',
  'Ñ‹' => 'è¤˜',
  'ÑŒ' => 'è¤œ',
  'Ñ�' => 'è¤�',
  'ÑŽ' => 'è¤ž',
  'Ñ�' => 'è¤Ÿ',
  'Ñ�' => 'è¤ ',
  'Ñ‘' => 'è¤¢',
  'Ñ’' => 'è¤£',
  'Ñ“' => 'è¤¤',
  'Ñ”' => 'è¤¦',
  'Ñ•' => 'è¤§',
  'Ñ–' => 'è¤¨',
  'Ñ—' => 'è¤©',
  'Ñ˜' => 'è¤¬',
  'Ñ™' => 'è¤­',
  'Ñš' => 'è¤®',
  'Ñ›' => 'è¤¯',
  'Ñœ' => 'è¤±',
  'Ñ�' => 'è¤²',
  'Ñž' => 'è¤³',
  'ÑŸ' => 'è¤µ',
  'Ñ ' => 'è¤·',
  'Ñ¡' => 'é€‰',
  'Ñ¢' => 'ç™£',
  'Ñ£' => 'çœ©',
  'Ñ¤' => 'ç»š',
  'Ñ¥' => 'é�´',
  'Ñ¦' => 'è–›',
  'Ñ§' => 'å­¦',
  'Ñ¨' => 'ç©´',
  'Ñ©' => 'é›ª',
  'Ñª' => 'è¡€',
  'Ñ«' => 'å‹‹',
  'Ñ¬' => 'ç†�',
  'Ñ­' => 'å¾ª',
  'Ñ®' => 'æ—¬',
  'Ñ¯' => 'è¯¢',
  'Ñ°' => 'å¯»',
  'Ñ±' => 'é©¯',
  'Ñ²' => 'å·¡',
  'Ñ³' => 'æ®‰',
  'Ñ´' => 'æ±›',
  'Ñµ' => 'è®­',
  'Ñ¶' => 'è®¯',
  'Ñ·' => 'é€Š',
  'Ñ¸' => 'è¿…',
  'Ñ¹' => 'åŽ‹',
  'Ñº' => 'æŠ¼',
  'Ñ»' => 'é¸¦',
  'Ñ¼' => 'é¸­',
  'Ñ½' => 'å‘€',
  'Ñ¾' => 'ä¸«',
  'Ñ¿' => 'èŠ½',
  'ÑÀ' => 'ç‰™',
  'ÑÁ' => 'èšœ',
  'ÑÂ' => 'å´–',
  'ÑÃ' => 'è¡™',
  'ÑÄ' => 'æ¶¯',
  'ÑÅ' => 'é›…',
  'ÑÆ' => 'å“‘',
  'ÑÇ' => 'äºš',
  'ÑÈ' => 'è®¶',
  'ÑÉ' => 'ç„‰',
  'ÑÊ' => 'å’½',
  'ÑË' => 'é˜‰',
  'ÑÌ' => 'çƒŸ',
  'ÑÍ' => 'æ·¹',
  'ÑÎ' => 'ç›�',
  'ÑÏ' => 'ä¸¥',
  'ÑÐ' => 'ç ”',
  'ÑÑ' => 'èœ’',
  'ÑÒ' => 'å²©',
  'ÑÓ' => 'å»¶',
  'ÑÔ' => 'è¨€',
  'ÑÕ' => 'é¢œ',
  'ÑÖ' => 'é˜Ž',
  'Ñ×' => 'ç‚Ž',
  'ÑØ' => 'æ²¿',
  'ÑÙ' => 'å¥„',
  'ÑÚ' => 'æŽ©',
  'ÑÛ' => 'çœ¼',
  'ÑÜ' => 'è¡�',
  'ÑÝ' => 'æ¼”',
  'ÑÞ' => 'è‰³',
  'Ñß' => 'å °',
  'Ñà' => 'ç‡•',
  'Ñá' => 'åŽŒ',
  'Ñâ' => 'ç š',
  'Ñã' => 'é›�',
  'Ñä' => 'å”�',
  'Ñå' => 'å½¦',
  'Ñæ' => 'ç„°',
  'Ñç' => 'å®´',
  'Ñè' => 'è°š',
  'Ñé' => 'éªŒ',
  'Ñê' => 'æ®ƒ',
  'Ñë' => 'å¤®',
  'Ñì' => 'é¸¯',
  'Ñí' => 'ç§§',
  'Ñî' => 'æ�¨',
  'Ñï' => 'æ‰¬',
  'Ñð' => 'ä½¯',
  'Ññ' => 'ç–¡',
  'Ñò' => 'ç¾Š',
  'Ñó' => 'æ´‹',
  'Ñô' => 'é˜³',
  'Ñõ' => 'æ°§',
  'Ñö' => 'ä»°',
  'Ñ÷' => 'ç—’',
  'Ñø' => 'å…»',
  'Ñù' => 'æ ·',
  'Ñú' => 'æ¼¾',
  'Ñû' => 'é‚€',
  'Ñü' => 'è…°',
  'Ñý' => 'å¦–',
  'Ñþ' => 'ç‘¶',
  'Ò@' => 'è¤¸',
  'ÒA' => 'è¤¹',
  'ÒB' => 'è¤º',
  'ÒC' => 'è¤»',
  'ÒD' => 'è¤¼',
  'ÒE' => 'è¤½',
  'ÒF' => 'è¤¾',
  'ÒG' => 'è¤¿',
  'ÒH' => 'è¥€',
  'ÒI' => 'è¥‚',
  'ÒJ' => 'è¥ƒ',
  'ÒK' => 'è¥…',
  'ÒL' => 'è¥†',
  'ÒM' => 'è¥‡',
  'ÒN' => 'è¥ˆ',
  'ÒO' => 'è¥‰',
  'ÒP' => 'è¥Š',
  'ÒQ' => 'è¥‹',
  'ÒR' => 'è¥Œ',
  'ÒS' => 'è¥�',
  'ÒT' => 'è¥Ž',
  'ÒU' => 'è¥�',
  'ÒV' => 'è¥�',
  'ÒW' => 'è¥‘',
  'ÒX' => 'è¥’',
  'ÒY' => 'è¥“',
  'ÒZ' => 'è¥”',
  'Ò[' => 'è¥•',
  'Ò\\' => 'è¥–',
  'Ò]' => 'è¥—',
  'Ò^' => 'è¥˜',
  'Ò_' => 'è¥™',
  'Ò`' => 'è¥š',
  'Òa' => 'è¥›',
  'Òb' => 'è¥œ',
  'Òc' => 'è¥�',
  'Òd' => 'è¥ ',
  'Òe' => 'è¥¡',
  'Òf' => 'è¥¢',
  'Òg' => 'è¥£',
  'Òh' => 'è¥¤',
  'Òi' => 'è¥¥',
  'Òj' => 'è¥§',
  'Òk' => 'è¥¨',
  'Òl' => 'è¥©',
  'Òm' => 'è¥ª',
  'Òn' => 'è¥«',
  'Òo' => 'è¥¬',
  'Òp' => 'è¥­',
  'Òq' => 'è¥®',
  'Òr' => 'è¥¯',
  'Òs' => 'è¥°',
  'Òt' => 'è¥±',
  'Òu' => 'è¥²',
  'Òv' => 'è¥³',
  'Òw' => 'è¥´',
  'Òx' => 'è¥µ',
  'Òy' => 'è¥¶',
  'Òz' => 'è¥·',
  'Ò{' => 'è¥¸',
  'Ò|' => 'è¥¹',
  'Ò}' => 'è¥º',
  'Ò~' => 'è¥¼',
  'Ò€' => 'è¥½',
  'Ò�' => 'è¥¾',
  'Ò‚' => 'è¦€',
  'Òƒ' => 'è¦‚',
  'Ò„' => 'è¦„',
  'Ò…' => 'è¦…',
  'Ò†' => 'è¦‡',
  'Ò‡' => 'è¦ˆ',
  'Òˆ' => 'è¦‰',
  'Ò‰' => 'è¦Š',
  'ÒŠ' => 'è¦‹',
  'Ò‹' => 'è¦Œ',
  'ÒŒ' => 'è¦�',
  'Ò�' => 'è¦Ž',
  'ÒŽ' => 'è¦�',
  'Ò�' => 'è¦�',
  'Ò�' => 'è¦‘',
  'Ò‘' => 'è¦’',
  'Ò’' => 'è¦“',
  'Ò“' => 'è¦”',
  'Ò”' => 'è¦•',
  'Ò•' => 'è¦–',
  'Ò–' => 'è¦—',
  'Ò—' => 'è¦˜',
  'Ò˜' => 'è¦™',
  'Ò™' => 'è¦š',
  'Òš' => 'è¦›',
  'Ò›' => 'è¦œ',
  'Òœ' => 'è¦�',
  'Ò�' => 'è¦ž',
  'Òž' => 'è¦Ÿ',
  'ÒŸ' => 'è¦ ',
  'Ò ' => 'è¦¡',
  'Ò¡' => 'æ‘‡',
  'Ò¢' => 'å°§',
  'Ò£' => 'é�¥',
  'Ò¤' => 'çª‘',
  'Ò¥' => 'è°£',
  'Ò¦' => 'å§š',
  'Ò§' => 'å’¬',
  'Ò¨' => 'èˆ€',
  'Ò©' => 'è�¯',
  'Òª' => 'è¦�',
  'Ò«' => 'è€€',
  'Ò¬' => 'æ¤°',
  'Ò­' => 'å™Ž',
  'Ò®' => 'è€¶',
  'Ò¯' => 'çˆ·',
  'Ò°' => 'é‡Ž',
  'Ò±' => 'å†¶',
  'Ò²' => 'ä¹Ÿ',
  'Ò³' => 'é¡µ',
  'Ò´' => 'æŽ–',
  'Òµ' => 'ä¸š',
  'Ò¶' => 'å�¶',
  'Ò·' => 'æ›³',
  'Ò¸' => 'è…‹',
  'Ò¹' => 'å¤œ',
  'Òº' => 'æ¶²',
  'Ò»' => 'ä¸€',
  'Ò¼' => 'å£¹',
  'Ò½' => 'åŒ»',
  'Ò¾' => 'æ�–',
  'Ò¿' => 'é“±',
  'ÒÀ' => 'ä¾�',
  'ÒÁ' => 'ä¼Š',
  'ÒÂ' => 'è¡£',
  'ÒÃ' => 'é¢�',
  'ÒÄ' => 'å¤·',
  'ÒÅ' => 'é�—',
  'ÒÆ' => 'ç§»',
  'ÒÇ' => 'ä»ª',
  'ÒÈ' => 'èƒ°',
  'ÒÉ' => 'ç–‘',
  'ÒÊ' => 'æ²‚',
  'ÒË' => 'å®œ',
  'ÒÌ' => 'å§¨',
  'ÒÍ' => 'å½�',
  'ÒÎ' => 'æ¤…',
  'ÒÏ' => 'èš�',
  'ÒÐ' => 'å€š',
  'ÒÑ' => 'å·²',
  'ÒÒ' => 'ä¹™',
  'ÒÓ' => 'çŸ£',
  'ÒÔ' => 'ä»¥',
  'ÒÕ' => 'è‰º',
  'ÒÖ' => 'æŠ‘',
  'Ò×' => 'æ˜“',
  'ÒØ' => 'é‚‘',
  'ÒÙ' => 'å±¹',
  'ÒÚ' => 'äº¿',
  'ÒÛ' => 'å½¹',
  'ÒÜ' => 'è‡†',
  'ÒÝ' => 'é€¸',
  'ÒÞ' => 'è‚„',
  'Òß' => 'ç–«',
  'Òà' => 'äº¦',
  'Òá' => 'è£”',
  'Òâ' => 'æ„�',
  'Òã' => 'æ¯…',
  'Òä' => 'å¿†',
  'Òå' => 'ä¹‰',
  'Òæ' => 'ç›Š',
  'Òç' => 'æº¢',
  'Òè' => 'è¯£',
  'Òé' => 'è®®',
  'Òê' => 'è°Š',
  'Òë' => 'è¯‘',
  'Òì' => 'å¼‚',
  'Òí' => 'ç¿¼',
  'Òî' => 'ç¿Œ',
  'Òï' => 'ç»Ž',
  'Òð' => 'èŒµ',
  'Òñ' => 'è�«',
  'Òò' => 'å› ',
  'Òó' => 'æ®·',
  'Òô' => 'éŸ³',
  'Òõ' => 'é˜´',
  'Òö' => 'å§»',
  'Ò÷' => 'å�Ÿ',
  'Òø' => 'é“¶',
  'Òù' => 'æ·«',
  'Òú' => 'å¯…',
  'Òû' => 'é¥®',
  'Òü' => 'å°¹',
  'Òý' => 'å¼•',
  'Òþ' => 'éš�',
  'Ó@' => 'è¦¢',
  'ÓA' => 'è¦£',
  'ÓB' => 'è¦¤',
  'ÓC' => 'è¦¥',
  'ÓD' => 'è¦¦',
  'ÓE' => 'è¦§',
  'ÓF' => 'è¦¨',
  'ÓG' => 'è¦©',
  'ÓH' => 'è¦ª',
  'ÓI' => 'è¦«',
  'ÓJ' => 'è¦¬',
  'ÓK' => 'è¦­',
  'ÓL' => 'è¦®',
  'ÓM' => 'è¦¯',
  'ÓN' => 'è¦°',
  'ÓO' => 'è¦±',
  'ÓP' => 'è¦²',
  'ÓQ' => 'è¦³',
  'ÓR' => 'è¦´',
  'ÓS' => 'è¦µ',
  'ÓT' => 'è¦¶',
  'ÓU' => 'è¦·',
  'ÓV' => 'è¦¸',
  'ÓW' => 'è¦¹',
  'ÓX' => 'è¦º',
  'ÓY' => 'è¦»',
  'ÓZ' => 'è¦¼',
  'Ó[' => 'è¦½',
  'Ó\\' => 'è¦¾',
  'Ó]' => 'è¦¿',
  'Ó^' => 'è§€',
  'Ó_' => 'è§ƒ',
  'Ó`' => 'è§�',
  'Óa' => 'è§“',
  'Ób' => 'è§”',
  'Óc' => 'è§•',
  'Ód' => 'è§—',
  'Óe' => 'è§˜',
  'Óf' => 'è§™',
  'Óg' => 'è§›',
  'Óh' => 'è§�',
  'Ói' => 'è§Ÿ',
  'Ój' => 'è§ ',
  'Ók' => 'è§¡',
  'Ól' => 'è§¢',
  'Óm' => 'è§¤',
  'Ón' => 'è§§',
  'Óo' => 'è§¨',
  'Óp' => 'è§©',
  'Óq' => 'è§ª',
  'Ór' => 'è§¬',
  'Ós' => 'è§­',
  'Ót' => 'è§®',
  'Óu' => 'è§°',
  'Óv' => 'è§±',
  'Ów' => 'è§²',
  'Óx' => 'è§´',
  'Óy' => 'è§µ',
  'Óz' => 'è§¶',
  'Ó{' => 'è§·',
  'Ó|' => 'è§¸',
  'Ó}' => 'è§¹',
  'Ó~' => 'è§º',
  'Ó€' => 'è§»',
  'Ó�' => 'è§¼',
  'Ó‚' => 'è§½',
  'Óƒ' => 'è§¾',
  'Ó„' => 'è§¿',
  'Ó…' => 'è¨�',
  'Ó†' => 'è¨‚',
  'Ó‡' => 'è¨ƒ',
  'Óˆ' => 'è¨„',
  'Ó‰' => 'è¨…',
  'ÓŠ' => 'è¨†',
  'Ó‹' => 'è¨ˆ',
  'ÓŒ' => 'è¨‰',
  'Ó�' => 'è¨Š',
  'ÓŽ' => 'è¨‹',
  'Ó�' => 'è¨Œ',
  'Ó�' => 'è¨�',
  'Ó‘' => 'è¨Ž',
  'Ó’' => 'è¨�',
  'Ó“' => 'è¨�',
  'Ó”' => 'è¨‘',
  'Ó•' => 'è¨’',
  'Ó–' => 'è¨“',
  'Ó—' => 'è¨”',
  'Ó˜' => 'è¨•',
  'Ó™' => 'è¨–',
  'Óš' => 'è¨—',
  'Ó›' => 'è¨˜',
  'Óœ' => 'è¨™',
  'Ó�' => 'è¨š',
  'Óž' => 'è¨›',
  'ÓŸ' => 'è¨œ',
  'Ó ' => 'è¨�',
  'Ó¡' => 'å�°',
  'Ó¢' => 'è‹±',
  'Ó£' => 'æ¨±',
  'Ó¤' => 'å©´',
  'Ó¥' => 'é¹°',
  'Ó¦' => 'åº”',
  'Ó§' => 'ç¼¨',
  'Ó¨' => 'èŽ¹',
  'Ó©' => 'è�¤',
  'Óª' => 'è�¥',
  'Ó«' => 'è�§',
  'Ó¬' => 'è�‡',
  'Ó­' => 'è¿Ž',
  'Ó®' => 'èµ¢',
  'Ó¯' => 'ç›ˆ',
  'Ó°' => 'å½±',
  'Ó±' => 'é¢–',
  'Ó²' => 'ç¡¬',
  'Ó³' => 'æ˜ ',
  'Ó´' => 'å“Ÿ',
  'Óµ' => 'æ‹¥',
  'Ó¶' => 'ä½£',
  'Ó·' => 'è‡ƒ',
  'Ó¸' => 'ç—ˆ',
  'Ó¹' => 'åº¸',
  'Óº' => 'é›�',
  'Ó»' => 'è¸Š',
  'Ó¼' => 'è›¹',
  'Ó½' => 'å’�',
  'Ó¾' => 'æ³³',
  'Ó¿' => 'æ¶Œ',
  'ÓÀ' => 'æ°¸',
  'ÓÁ' => 'æ�¿',
  'ÓÂ' => 'å‹‡',
  'ÓÃ' => 'ç”¨',
  'ÓÄ' => 'å¹½',
  'ÓÅ' => 'ä¼˜',
  'ÓÆ' => 'æ‚ ',
  'ÓÇ' => 'å¿§',
  'ÓÈ' => 'å°¤',
  'ÓÉ' => 'ç”±',
  'ÓÊ' => 'é‚®',
  'ÓË' => 'é“€',
  'ÓÌ' => 'çŠ¹',
  'ÓÍ' => 'æ²¹',
  'ÓÎ' => 'æ¸¸',
  'ÓÏ' => 'é…‰',
  'ÓÐ' => 'æœ‰',
  'ÓÑ' => 'å�‹',
  'ÓÒ' => 'å�³',
  'ÓÓ' => 'ä½‘',
  'ÓÔ' => 'é‡‰',
  'ÓÕ' => 'è¯±',
  'ÓÖ' => 'å�ˆ',
  'Ó×' => 'å¹¼',
  'ÓØ' => 'è¿‚',
  'ÓÙ' => 'æ·¤',
  'ÓÚ' => 'äºŽ',
  'ÓÛ' => 'ç›‚',
  'ÓÜ' => 'æ¦†',
  'ÓÝ' => 'è™ž',
  'ÓÞ' => 'æ„š',
  'Óß' => 'èˆ†',
  'Óà' => 'ä½™',
  'Óá' => 'ä¿ž',
  'Óâ' => 'é€¾',
  'Óã' => 'é±¼',
  'Óä' => 'æ„‰',
  'Óå' => 'æ¸�',
  'Óæ' => 'æ¸”',
  'Óç' => 'éš…',
  'Óè' => 'äºˆ',
  'Óé' => 'å¨±',
  'Óê' => 'é›¨',
  'Óë' => 'ä¸Ž',
  'Óì' => 'å±¿',
  'Óí' => 'ç¦¹',
  'Óî' => 'å®‡',
  'Óï' => 'è¯­',
  'Óð' => 'ç¾½',
  'Óñ' => 'çŽ‰',
  'Óò' => 'åŸŸ',
  'Óó' => 'èŠ‹',
  'Óô' => 'éƒ�',
  'Óõ' => 'å��',
  'Óö' => 'é�‡',
  'Ó÷' => 'å–»',
  'Óø' => 'å³ª',
  'Óù' => 'å¾¡',
  'Óú' => 'æ„ˆ',
  'Óû' => 'æ¬²',
  'Óü' => 'ç‹±',
  'Óý' => 'è‚²',
  'Óþ' => 'èª‰',
  'Ô@' => 'è¨ž',
  'ÔA' => 'è¨Ÿ',
  'ÔB' => 'è¨ ',
  'ÔC' => 'è¨¡',
  'ÔD' => 'è¨¢',
  'ÔE' => 'è¨£',
  'ÔF' => 'è¨¤',
  'ÔG' => 'è¨¥',
  'ÔH' => 'è¨¦',
  'ÔI' => 'è¨§',
  'ÔJ' => 'è¨¨',
  'ÔK' => 'è¨©',
  'ÔL' => 'è¨ª',
  'ÔM' => 'è¨«',
  'ÔN' => 'è¨¬',
  'ÔO' => 'è¨­',
  'ÔP' => 'è¨®',
  'ÔQ' => 'è¨¯',
  'ÔR' => 'è¨°',
  'ÔS' => 'è¨±',
  'ÔT' => 'è¨²',
  'ÔU' => 'è¨³',
  'ÔV' => 'è¨´',
  'ÔW' => 'è¨µ',
  'ÔX' => 'è¨¶',
  'ÔY' => 'è¨·',
  'ÔZ' => 'è¨¸',
  'Ô[' => 'è¨¹',
  'Ô\\' => 'è¨º',
  'Ô]' => 'è¨»',
  'Ô^' => 'è¨¼',
  'Ô_' => 'è¨½',
  'Ô`' => 'è¨¿',
  'Ôa' => 'è©€',
  'Ôb' => 'è©�',
  'Ôc' => 'è©‚',
  'Ôd' => 'è©ƒ',
  'Ôe' => 'è©„',
  'Ôf' => 'è©…',
  'Ôg' => 'è©†',
  'Ôh' => 'è©‡',
  'Ôi' => 'è©‰',
  'Ôj' => 'è©Š',
  'Ôk' => 'è©‹',
  'Ôl' => 'è©Œ',
  'Ôm' => 'è©�',
  'Ôn' => 'è©Ž',
  'Ôo' => 'è©�',
  'Ôp' => 'è©�',
  'Ôq' => 'è©‘',
  'Ôr' => 'è©’',
  'Ôs' => 'è©“',
  'Ôt' => 'è©”',
  'Ôu' => 'è©•',
  'Ôv' => 'è©–',
  'Ôw' => 'è©—',
  'Ôx' => 'è©˜',
  'Ôy' => 'è©™',
  'Ôz' => 'è©š',
  'Ô{' => 'è©›',
  'Ô|' => 'è©œ',
  'Ô}' => 'è©�',
  'Ô~' => 'è©ž',
  'Ô€' => 'è©Ÿ',
  'Ô�' => 'è© ',
  'Ô‚' => 'è©¡',
  'Ôƒ' => 'è©¢',
  'Ô„' => 'è©£',
  'Ô…' => 'è©¤',
  'Ô†' => 'è©¥',
  'Ô‡' => 'è©¦',
  'Ôˆ' => 'è©§',
  'Ô‰' => 'è©¨',
  'ÔŠ' => 'è©©',
  'Ô‹' => 'è©ª',
  'ÔŒ' => 'è©«',
  'Ô�' => 'è©¬',
  'ÔŽ' => 'è©­',
  'Ô�' => 'è©®',
  'Ô�' => 'è©¯',
  'Ô‘' => 'è©°',
  'Ô’' => 'è©±',
  'Ô“' => 'è©²',
  'Ô”' => 'è©³',
  'Ô•' => 'è©´',
  'Ô–' => 'è©µ',
  'Ô—' => 'è©¶',
  'Ô˜' => 'è©·',
  'Ô™' => 'è©¸',
  'Ôš' => 'è©º',
  'Ô›' => 'è©»',
  'Ôœ' => 'è©¼',
  'Ô�' => 'è©½',
  'Ôž' => 'è©¾',
  'ÔŸ' => 'è©¿',
  'Ô ' => 'èª€',
  'Ô¡' => 'æµ´',
  'Ô¢' => 'å¯“',
  'Ô£' => 'è£•',
  'Ô¤' => 'é¢„',
  'Ô¥' => 'è±«',
  'Ô¦' => 'é©­',
  'Ô§' => 'é¸³',
  'Ô¨' => 'æ¸Š',
  'Ô©' => 'å†¤',
  'Ôª' => 'å…ƒ',
  'Ô«' => 'åž£',
  'Ô¬' => 'è¢�',
  'Ô­' => 'åŽŸ',
  'Ô®' => 'æ�´',
  'Ô¯' => 'è¾•',
  'Ô°' => 'å›­',
  'Ô±' => 'å‘˜',
  'Ô²' => 'åœ†',
  'Ô³' => 'çŒ¿',
  'Ô´' => 'æº�',
  'Ôµ' => 'ç¼˜',
  'Ô¶' => 'è¿œ',
  'Ô·' => 'è‹‘',
  'Ô¸' => 'æ„¿',
  'Ô¹' => 'æ€¨',
  'Ôº' => 'é™¢',
  'Ô»' => 'æ›°',
  'Ô¼' => 'çº¦',
  'Ô½' => 'è¶Š',
  'Ô¾' => 'è·ƒ',
  'Ô¿' => 'é’¥',
  'ÔÀ' => 'å²³',
  'ÔÁ' => 'ç²¤',
  'ÔÂ' => 'æœˆ',
  'ÔÃ' => 'æ‚¦',
  'ÔÄ' => 'é˜…',
  'ÔÅ' => 'è€˜',
  'ÔÆ' => 'äº‘',
  'ÔÇ' => 'éƒ§',
  'ÔÈ' => 'åŒ€',
  'ÔÉ' => 'é™¨',
  'ÔÊ' => 'å…�',
  'ÔË' => 'è¿�',
  'ÔÌ' => 'è•´',
  'ÔÍ' => 'é…�',
  'ÔÎ' => 'æ™•',
  'ÔÏ' => 'éŸµ',
  'ÔÐ' => 'å­•',
  'ÔÑ' => 'åŒ�',
  'ÔÒ' => 'ç ¸',
  'ÔÓ' => 'æ�‚',
  'ÔÔ' => 'æ ½',
  'ÔÕ' => 'å“‰',
  'ÔÖ' => 'ç�¾',
  'Ô×' => 'å®°',
  'ÔØ' => 'è½½',
  'ÔÙ' => 'å†�',
  'ÔÚ' => 'åœ¨',
  'ÔÛ' => 'å’±',
  'ÔÜ' => 'æ”’',
  'ÔÝ' => 'æš‚',
  'ÔÞ' => 'èµž',
  'Ôß' => 'èµƒ',
  'Ôà' => 'è„�',
  'Ôá' => 'è‘¬',
  'Ôâ' => 'é�­',
  'Ôã' => 'ç³Ÿ',
  'Ôä' => 'å‡¿',
  'Ôå' => 'è—»',
  'Ôæ' => 'æž£',
  'Ôç' => 'æ—©',
  'Ôè' => 'æ¾¡',
  'Ôé' => 'èš¤',
  'Ôê' => 'èº�',
  'Ôë' => 'å™ª',
  'Ôì' => 'é€ ',
  'Ôí' => 'çš‚',
  'Ôî' => 'ç�¶',
  'Ôï' => 'ç‡¥',
  'Ôð' => 'è´£',
  'Ôñ' => 'æ‹©',
  'Ôò' => 'åˆ™',
  'Ôó' => 'æ³½',
  'Ôô' => 'è´¼',
  'Ôõ' => 'æ€Ž',
  'Ôö' => 'å¢ž',
  'Ô÷' => 'æ†Ž',
  'Ôø' => 'æ›¾',
  'Ôù' => 'èµ ',
  'Ôú' => 'æ‰Ž',
  'Ôû' => 'å–³',
  'Ôü' => 'æ¸£',
  'Ôý' => 'æœ­',
  'Ôþ' => 'è½§',
  'Õ@' => 'èª�',
  'ÕA' => 'èª‚',
  'ÕB' => 'èªƒ',
  'ÕC' => 'èª„',
  'ÕD' => 'èª…',
  'ÕE' => 'èª†',
  'ÕF' => 'èª‡',
  'ÕG' => 'èªˆ',
  'ÕH' => 'èª‹',
  'ÕI' => 'èªŒ',
  'ÕJ' => 'èª�',
  'ÕK' => 'èªŽ',
  'ÕL' => 'èª�',
  'ÕM' => 'èª�',
  'ÕN' => 'èª‘',
  'ÕO' => 'èª’',
  'ÕP' => 'èª”',
  'ÕQ' => 'èª•',
  'ÕR' => 'èª–',
  'ÕS' => 'èª—',
  'ÕT' => 'èª˜',
  'ÕU' => 'èª™',
  'ÕV' => 'èªš',
  'ÕW' => 'èª›',
  'ÕX' => 'èªœ',
  'ÕY' => 'èª�',
  'ÕZ' => 'èªž',
  'Õ[' => 'èªŸ',
  'Õ\\' => 'èª ',
  'Õ]' => 'èª¡',
  'Õ^' => 'èª¢',
  'Õ_' => 'èª£',
  'Õ`' => 'èª¤',
  'Õa' => 'èª¥',
  'Õb' => 'èª¦',
  'Õc' => 'èª§',
  'Õd' => 'èª¨',
  'Õe' => 'èª©',
  'Õf' => 'èªª',
  'Õg' => 'èª«',
  'Õh' => 'èª¬',
  'Õi' => 'èª­',
  'Õj' => 'èª®',
  'Õk' => 'èª¯',
  'Õl' => 'èª°',
  'Õm' => 'èª±',
  'Õn' => 'èª²',
  'Õo' => 'èª³',
  'Õp' => 'èª´',
  'Õq' => 'èªµ',
  'Õr' => 'èª¶',
  'Õs' => 'èª·',
  'Õt' => 'èª¸',
  'Õu' => 'èª¹',
  'Õv' => 'èªº',
  'Õw' => 'èª»',
  'Õx' => 'èª¼',
  'Õy' => 'èª½',
  'Õz' => 'èª¾',
  'Õ{' => 'èª¿',
  'Õ|' => 'è«€',
  'Õ}' => 'è«�',
  'Õ~' => 'è«‚',
  'Õ€' => 'è«ƒ',
  'Õ�' => 'è«„',
  'Õ‚' => 'è«…',
  'Õƒ' => 'è«†',
  'Õ„' => 'è«‡',
  'Õ…' => 'è«ˆ',
  'Õ†' => 'è«‰',
  'Õ‡' => 'è«Š',
  'Õˆ' => 'è«‹',
  'Õ‰' => 'è«Œ',
  'ÕŠ' => 'è«�',
  'Õ‹' => 'è«Ž',
  'ÕŒ' => 'è«�',
  'Õ�' => 'è«�',
  'ÕŽ' => 'è«‘',
  'Õ�' => 'è«’',
  'Õ�' => 'è«“',
  'Õ‘' => 'è«”',
  'Õ’' => 'è«•',
  'Õ“' => 'è«–',
  'Õ”' => 'è«—',
  'Õ•' => 'è«˜',
  'Õ–' => 'è«™',
  'Õ—' => 'è«š',
  'Õ˜' => 'è«›',
  'Õ™' => 'è«œ',
  'Õš' => 'è«�',
  'Õ›' => 'è«ž',
  'Õœ' => 'è«Ÿ',
  'Õ�' => 'è« ',
  'Õž' => 'è«¡',
  'ÕŸ' => 'è«¢',
  'Õ ' => 'è«£',
  'Õ¡' => 'é“¡',
  'Õ¢' => 'é—¸',
  'Õ£' => 'çœ¨',
  'Õ¤' => 'æ …',
  'Õ¥' => 'æ¦¨',
  'Õ¦' => 'å’‹',
  'Õ§' => 'ä¹�',
  'Õ¨' => 'ç‚¸',
  'Õ©' => 'è¯ˆ',
  'Õª' => 'æ‘˜',
  'Õ«' => 'æ–‹',
  'Õ¬' => 'å®…',
  'Õ­' => 'çª„',
  'Õ®' => 'å€º',
  'Õ¯' => 'å¯¨',
  'Õ°' => 'çž»',
  'Õ±' => 'æ¯¡',
  'Õ²' => 'è©¹',
  'Õ³' => 'ç²˜',
  'Õ´' => 'æ²¾',
  'Õµ' => 'ç›�',
  'Õ¶' => 'æ–©',
  'Õ·' => 'è¾—',
  'Õ¸' => 'å´­',
  'Õ¹' => 'å±•',
  'Õº' => 'è˜¸',
  'Õ»' => 'æ ˆ',
  'Õ¼' => 'å� ',
  'Õ½' => 'æˆ˜',
  'Õ¾' => 'ç«™',
  'Õ¿' => 'æ¹›',
  'ÕÀ' => 'ç»½',
  'ÕÁ' => 'æ¨Ÿ',
  'ÕÂ' => 'ç« ',
  'ÕÃ' => 'å½°',
  'ÕÄ' => 'æ¼³',
  'ÕÅ' => 'å¼ ',
  'ÕÆ' => 'æŽŒ',
  'ÕÇ' => 'æ¶¨',
  'ÕÈ' => 'æ�–',
  'ÕÉ' => 'ä¸ˆ',
  'ÕÊ' => 'å¸�',
  'ÕË' => 'è´¦',
  'ÕÌ' => 'ä»—',
  'ÕÍ' => 'èƒ€',
  'ÕÎ' => 'ç˜´',
  'ÕÏ' => 'éšœ',
  'ÕÐ' => 'æ‹›',
  'ÕÑ' => 'æ˜­',
  'ÕÒ' => 'æ‰¾',
  'ÕÓ' => 'æ²¼',
  'ÕÔ' => 'èµµ',
  'ÕÕ' => 'ç…§',
  'ÕÖ' => 'ç½©',
  'Õ×' => 'å…†',
  'ÕØ' => 'è‚‡',
  'ÕÙ' => 'å�¬',
  'ÕÚ' => 'é�®',
  'ÕÛ' => 'æŠ˜',
  'ÕÜ' => 'å“²',
  'ÕÝ' => 'è›°',
  'ÕÞ' => 'è¾™',
  'Õß' => 'è€…',
  'Õà' => 'é”—',
  'Õá' => 'è”—',
  'Õâ' => 'è¿™',
  'Õã' => 'æµ™',
  'Õä' => 'ç��',
  'Õå' => 'æ–Ÿ',
  'Õæ' => 'çœŸ',
  'Õç' => 'ç”„',
  'Õè' => 'ç §',
  'Õé' => 'è‡»',
  'Õê' => 'è´ž',
  'Õë' => 'é’ˆ',
  'Õì' => 'ä¾¦',
  'Õí' => 'æž•',
  'Õî' => 'ç–¹',
  'Õï' => 'è¯Š',
  'Õð' => 'éœ‡',
  'Õñ' => 'æŒ¯',
  'Õò' => 'é•‡',
  'Õó' => 'é˜µ',
  'Õô' => 'è’¸',
  'Õõ' => 'æŒ£',
  'Õö' => 'ç��',
  'Õ÷' => 'å¾�',
  'Õø' => 'ç‹°',
  'Õù' => 'äº‰',
  'Õú' => 'æ€”',
  'Õû' => 'æ•´',
  'Õü' => 'æ‹¯',
  'Õý' => 'æ­£',
  'Õþ' => 'æ”¿',
  'Ö@' => 'è«¤',
  'ÖA' => 'è«¥',
  'ÖB' => 'è«¦',
  'ÖC' => 'è«§',
  'ÖD' => 'è«¨',
  'ÖE' => 'è«©',
  'ÖF' => 'è«ª',
  'ÖG' => 'è««',
  'ÖH' => 'è«¬',
  'ÖI' => 'è«­',
  'ÖJ' => 'è«®',
  'ÖK' => 'è«¯',
  'ÖL' => 'è«°',
  'ÖM' => 'è«±',
  'ÖN' => 'è«²',
  'ÖO' => 'è«³',
  'ÖP' => 'è«´',
  'ÖQ' => 'è«µ',
  'ÖR' => 'è«¶',
  'ÖS' => 'è«·',
  'ÖT' => 'è«¸',
  'ÖU' => 'è«¹',
  'ÖV' => 'è«º',
  'ÖW' => 'è«»',
  'ÖX' => 'è«¼',
  'ÖY' => 'è«½',
  'ÖZ' => 'è«¾',
  'Ö[' => 'è«¿',
  'Ö\\' => 'è¬€',
  'Ö]' => 'è¬�',
  'Ö^' => 'è¬‚',
  'Ö_' => 'è¬ƒ',
  'Ö`' => 'è¬„',
  'Öa' => 'è¬…',
  'Öb' => 'è¬†',
  'Öc' => 'è¬ˆ',
  'Öd' => 'è¬‰',
  'Öe' => 'è¬Š',
  'Öf' => 'è¬‹',
  'Ög' => 'è¬Œ',
  'Öh' => 'è¬�',
  'Öi' => 'è¬Ž',
  'Öj' => 'è¬�',
  'Ök' => 'è¬�',
  'Öl' => 'è¬‘',
  'Öm' => 'è¬’',
  'Ön' => 'è¬“',
  'Öo' => 'è¬”',
  'Öp' => 'è¬•',
  'Öq' => 'è¬–',
  'Ör' => 'è¬—',
  'Ös' => 'è¬˜',
  'Öt' => 'è¬™',
  'Öu' => 'è¬š',
  'Öv' => 'è¬›',
  'Öw' => 'è¬œ',
  'Öx' => 'è¬�',
  'Öy' => 'è¬ž',
  'Öz' => 'è¬Ÿ',
  'Ö{' => 'è¬ ',
  'Ö|' => 'è¬¡',
  'Ö}' => 'è¬¢',
  'Ö~' => 'è¬£',
  'Ö€' => 'è¬¤',
  'Ö�' => 'è¬¥',
  'Ö‚' => 'è¬§',
  'Öƒ' => 'è¬¨',
  'Ö„' => 'è¬©',
  'Ö…' => 'è¬ª',
  'Ö†' => 'è¬«',
  'Ö‡' => 'è¬¬',
  'Öˆ' => 'è¬­',
  'Ö‰' => 'è¬®',
  'ÖŠ' => 'è¬¯',
  'Ö‹' => 'è¬°',
  'ÖŒ' => 'è¬±',
  'Ö�' => 'è¬²',
  'ÖŽ' => 'è¬³',
  'Ö�' => 'è¬´',
  'Ö�' => 'è¬µ',
  'Ö‘' => 'è¬¶',
  'Ö’' => 'è¬·',
  'Ö“' => 'è¬¸',
  'Ö”' => 'è¬¹',
  'Ö•' => 'è¬º',
  'Ö–' => 'è¬»',
  'Ö—' => 'è¬¼',
  'Ö˜' => 'è¬½',
  'Ö™' => 'è¬¾',
  'Öš' => 'è¬¿',
  'Ö›' => 'è­€',
  'Öœ' => 'è­�',
  'Ö�' => 'è­‚',
  'Öž' => 'è­ƒ',
  'ÖŸ' => 'è­„',
  'Ö ' => 'è­…',
  'Ö¡' => 'å¸§',
  'Ö¢' => 'ç—‡',
  'Ö£' => 'éƒ‘',
  'Ö¤' => 'è¯�',
  'Ö¥' => 'èŠ�',
  'Ö¦' => 'æž�',
  'Ö§' => 'æ”¯',
  'Ö¨' => 'å�±',
  'Ö©' => 'èœ˜',
  'Öª' => 'çŸ¥',
  'Ö«' => 'è‚¢',
  'Ö¬' => 'è„‚',
  'Ö­' => 'æ±�',
  'Ö®' => 'ä¹‹',
  'Ö¯' => 'ç»‡',
  'Ö°' => 'è�Œ',
  'Ö±' => 'ç›´',
  'Ö²' => 'æ¤�',
  'Ö³' => 'æ®–',
  'Ö´' => 'æ‰§',
  'Öµ' => 'å€¼',
  'Ö¶' => 'ä¾„',
  'Ö·' => 'å�€',
  'Ö¸' => 'æŒ‡',
  'Ö¹' => 'æ­¢',
  'Öº' => 'è¶¾',
  'Ö»' => 'å�ª',
  'Ö¼' => 'æ—¨',
  'Ö½' => 'çº¸',
  'Ö¾' => 'å¿—',
  'Ö¿' => 'æŒš',
  'ÖÀ' => 'æŽ·',
  'ÖÁ' => 'è‡³',
  'ÖÂ' => 'è‡´',
  'ÖÃ' => 'ç½®',
  'ÖÄ' => 'å¸œ',
  'ÖÅ' => 'å³™',
  'ÖÆ' => 'åˆ¶',
  'ÖÇ' => 'æ™º',
  'ÖÈ' => 'ç§©',
  'ÖÉ' => 'ç¨š',
  'ÖÊ' => 'è´¨',
  'ÖË' => 'ç‚™',
  'ÖÌ' => 'ç—”',
  'ÖÍ' => 'æ»ž',
  'ÖÎ' => 'æ²»',
  'ÖÏ' => 'çª’',
  'ÖÐ' => 'ä¸­',
  'ÖÑ' => 'ç›…',
  'ÖÒ' => 'å¿ ',
  'ÖÓ' => 'é’Ÿ',
  'ÖÔ' => 'è¡·',
  'ÖÕ' => 'ç»ˆ',
  'ÖÖ' => 'ç§�',
  'Ö×' => 'è‚¿',
  'ÖØ' => 'é‡�',
  'ÖÙ' => 'ä»²',
  'ÖÚ' => 'ä¼—',
  'ÖÛ' => 'èˆŸ',
  'ÖÜ' => 'å‘¨',
  'ÖÝ' => 'å·ž',
  'ÖÞ' => 'æ´²',
  'Öß' => 'è¯Œ',
  'Öà' => 'ç²¥',
  'Öá' => 'è½´',
  'Öâ' => 'è‚˜',
  'Öã' => 'å¸š',
  'Öä' => 'å’’',
  'Öå' => 'çš±',
  'Öæ' => 'å®™',
  'Öç' => 'æ˜¼',
  'Öè' => 'éª¤',
  'Öé' => 'ç� ',
  'Öê' => 'æ ª',
  'Öë' => 'è››',
  'Öì' => 'æœ±',
  'Öí' => 'çŒª',
  'Öî' => 'è¯¸',
  'Öï' => 'è¯›',
  'Öð' => 'é€�',
  'Öñ' => 'ç«¹',
  'Öò' => 'çƒ›',
  'Öó' => 'ç…®',
  'Öô' => 'æ‹„',
  'Öõ' => 'çž©',
  'Öö' => 'å˜±',
  'Ö÷' => 'ä¸»',
  'Öø' => 'è‘—',
  'Öù' => 'æŸ±',
  'Öú' => 'åŠ©',
  'Öû' => 'è›€',
  'Öü' => 'è´®',
  'Öý' => 'é“¸',
  'Öþ' => 'ç­‘',
  '×@' => 'è­†',
  '×A' => 'è­‡',
  '×B' => 'è­ˆ',
  '×C' => 'è­‰',
  '×D' => 'è­Š',
  '×E' => 'è­‹',
  '×F' => 'è­Œ',
  '×G' => 'è­�',
  '×H' => 'è­Ž',
  '×I' => 'è­�',
  '×J' => 'è­�',
  '×K' => 'è­‘',
  '×L' => 'è­’',
  '×M' => 'è­“',
  '×N' => 'è­”',
  '×O' => 'è­•',
  '×P' => 'è­–',
  '×Q' => 'è­—',
  '×R' => 'è­˜',
  '×S' => 'è­™',
  '×T' => 'è­š',
  '×U' => 'è­›',
  '×V' => 'è­œ',
  '×W' => 'è­�',
  '×X' => 'è­ž',
  '×Y' => 'è­Ÿ',
  '×Z' => 'è­ ',
  '×[' => 'è­¡',
  '×\\' => 'è­¢',
  '×]' => 'è­£',
  '×^' => 'è­¤',
  '×_' => 'è­¥',
  '×`' => 'è­§',
  '×a' => 'è­¨',
  '×b' => 'è­©',
  '×c' => 'è­ª',
  '×d' => 'è­«',
  '×e' => 'è­­',
  '×f' => 'è­®',
  '×g' => 'è­¯',
  '×h' => 'è­°',
  '×i' => 'è­±',
  '×j' => 'è­²',
  '×k' => 'è­³',
  '×l' => 'è­´',
  '×m' => 'è­µ',
  '×n' => 'è­¶',
  '×o' => 'è­·',
  '×p' => 'è­¸',
  '×q' => 'è­¹',
  '×r' => 'è­º',
  '×s' => 'è­»',
  '×t' => 'è­¼',
  '×u' => 'è­½',
  '×v' => 'è­¾',
  '×w' => 'è­¿',
  '×x' => 'è®€',
  '×y' => 'è®�',
  '×z' => 'è®‚',
  '×{' => 'è®ƒ',
  '×|' => 'è®„',
  '×}' => 'è®…',
  '×~' => 'è®†',
  '×€' => 'è®‡',
  '×�' => 'è®ˆ',
  '×‚' => 'è®‰',
  '×ƒ' => 'è®Š',
  '×„' => 'è®‹',
  '×…' => 'è®Œ',
  '×†' => 'è®�',
  '×‡' => 'è®Ž',
  '×ˆ' => 'è®�',
  '×‰' => 'è®�',
  '×Š' => 'è®‘',
  '×‹' => 'è®’',
  '×Œ' => 'è®“',
  '×�' => 'è®”',
  '×Ž' => 'è®•',
  '×�' => 'è®–',
  '×�' => 'è®—',
  '×‘' => 'è®˜',
  '×’' => 'è®™',
  '×“' => 'è®š',
  '×”' => 'è®›',
  '×•' => 'è®œ',
  '×–' => 'è®�',
  '×—' => 'è®ž',
  '×˜' => 'è®Ÿ',
  '×™' => 'è®¬',
  '×š' => 'è®±',
  '×›' => 'è®»',
  '×œ' => 'è¯‡',
  '×�' => 'è¯�',
  '×ž' => 'è¯ª',
  '×Ÿ' => 'è°‰',
  '× ' => 'è°ž',
  '×¡' => 'ä½�',
  '×¢' => 'æ³¨',
  '×£' => 'ç¥�',
  '×¤' => 'é©»',
  '×¥' => 'æŠ“',
  '×¦' => 'çˆª',
  '×§' => 'æ‹½',
  '×¨' => 'ä¸“',
  '×©' => 'ç –',
  '×ª' => 'è½¬',
  '×«' => 'æ’°',
  '×¬' => 'èµš',
  '×­' => 'ç¯†',
  '×®' => 'æ¡©',
  '×¯' => 'åº„',
  '×°' => 'è£…',
  '×±' => 'å¦†',
  '×²' => 'æ’ž',
  '×³' => 'å£®',
  '×´' => 'çŠ¶',
  '×µ' => 'æ¤Ž',
  '×¶' => 'é”¥',
  '×·' => 'è¿½',
  '×¸' => 'èµ˜',
  '×¹' => 'å� ',
  '×º' => 'ç¼€',
  '×»' => 'è°†',
  '×¼' => 'å‡†',
  '×½' => 'æ�‰',
  '×¾' => 'æ‹™',
  '×¿' => 'å�“',
  '×À' => 'æ¡Œ',
  '×Á' => 'ç�¢',
  '×Â' => 'èŒ�',
  '×Ã' => 'é…Œ',
  '×Ä' => 'å•„',
  '×Å' => 'ç�€',
  '×Æ' => 'ç�¼',
  '×Ç' => 'æµŠ',
  '×È' => 'å…¹',
  '×É' => 'å’¨',
  '×Ê' => 'èµ„',
  '×Ë' => 'å§¿',
  '×Ì' => 'æ»‹',
  '×Í' => 'æ·„',
  '×Î' => 'å­œ',
  '×Ï' => 'ç´«',
  '×Ð' => 'ä»”',
  '×Ñ' => 'ç±½',
  '×Ò' => 'æ»“',
  '×Ó' => 'å­�',
  '×Ô' => 'è‡ª',
  '×Õ' => 'æ¸�',
  '×Ö' => 'å­—',
  '××' => 'é¬ƒ',
  '×Ø' => 'æ£•',
  '×Ù' => 'è¸ª',
  '×Ú' => 'å®—',
  '×Û' => 'ç»¼',
  '×Ü' => 'æ€»',
  '×Ý' => 'çºµ',
  '×Þ' => 'é‚¹',
  '×ß' => 'èµ°',
  '×à' => 'å¥�',
  '×á' => 'æ��',
  '×â' => 'ç§Ÿ',
  '×ã' => 'è¶³',
  '×ä' => 'å�’',
  '×å' => 'æ—�',
  '×æ' => 'ç¥–',
  '×ç' => 'è¯…',
  '×è' => 'é˜»',
  '×é' => 'ç»„',
  '×ê' => 'é’»',
  '×ë' => 'çº‚',
  '×ì' => 'å˜´',
  '×í' => 'é†‰',
  '×î' => 'æœ€',
  '×ï' => 'ç½ª',
  '×ð' => 'å°Š',
  '×ñ' => 'é�µ',
  '×ò' => 'æ˜¨',
  '×ó' => 'å·¦',
  '×ô' => 'ä½�',
  '×õ' => 'æŸž',
  '×ö' => 'å�š',
  '×÷' => 'ä½œ',
  '×ø' => 'å��',
  '×ù' => 'åº§',
  'Ø@' => 'è°¸',
  'ØA' => 'è°¹',
  'ØB' => 'è°º',
  'ØC' => 'è°»',
  'ØD' => 'è°¼',
  'ØE' => 'è°½',
  'ØF' => 'è°¾',
  'ØG' => 'è°¿',
  'ØH' => 'è±€',
  'ØI' => 'è±‚',
  'ØJ' => 'è±ƒ',
  'ØK' => 'è±„',
  'ØL' => 'è±…',
  'ØM' => 'è±ˆ',
  'ØN' => 'è±Š',
  'ØO' => 'è±‹',
  'ØP' => 'è±�',
  'ØQ' => 'è±Ž',
  'ØR' => 'è±�',
  'ØS' => 'è±�',
  'ØT' => 'è±‘',
  'ØU' => 'è±’',
  'ØV' => 'è±“',
  'ØW' => 'è±”',
  'ØX' => 'è±–',
  'ØY' => 'è±—',
  'ØZ' => 'è±˜',
  'Ø[' => 'è±™',
  'Ø\\' => 'è±›',
  'Ø]' => 'è±œ',
  'Ø^' => 'è±�',
  'Ø_' => 'è±ž',
  'Ø`' => 'è±Ÿ',
  'Øa' => 'è± ',
  'Øb' => 'è±£',
  'Øc' => 'è±¤',
  'Ød' => 'è±¥',
  'Øe' => 'è±¦',
  'Øf' => 'è±§',
  'Øg' => 'è±¨',
  'Øh' => 'è±©',
  'Øi' => 'è±¬',
  'Øj' => 'è±­',
  'Øk' => 'è±®',
  'Øl' => 'è±¯',
  'Øm' => 'è±°',
  'Øn' => 'è±±',
  'Øo' => 'è±²',
  'Øp' => 'è±´',
  'Øq' => 'è±µ',
  'Ør' => 'è±¶',
  'Øs' => 'è±·',
  'Øt' => 'è±»',
  'Øu' => 'è±¼',
  'Øv' => 'è±½',
  'Øw' => 'è±¾',
  'Øx' => 'è±¿',
  'Øy' => 'è²€',
  'Øz' => 'è²�',
  'Ø{' => 'è²ƒ',
  'Ø|' => 'è²„',
  'Ø}' => 'è²†',
  'Ø~' => 'è²‡',
  'Ø€' => 'è²ˆ',
  'Ø�' => 'è²‹',
  'Ø‚' => 'è²�',
  'Øƒ' => 'è²Ž',
  'Ø„' => 'è²�',
  'Ø…' => 'è²�',
  'Ø†' => 'è²‘',
  'Ø‡' => 'è²’',
  'Øˆ' => 'è²“',
  'Ø‰' => 'è²•',
  'ØŠ' => 'è²–',
  'Ø‹' => 'è²—',
  'ØŒ' => 'è²™',
  'Ø�' => 'è²š',
  'ØŽ' => 'è²›',
  'Ø�' => 'è²œ',
  'Ø�' => 'è²�',
  'Ø‘' => 'è²ž',
  'Ø’' => 'è²Ÿ',
  'Ø“' => 'è² ',
  'Ø”' => 'è²¡',
  'Ø•' => 'è²¢',
  'Ø–' => 'è²£',
  'Ø—' => 'è²¤',
  'Ø˜' => 'è²¥',
  'Ø™' => 'è²¦',
  'Øš' => 'è²§',
  'Ø›' => 'è²¨',
  'Øœ' => 'è²©',
  'Ø�' => 'è²ª',
  'Øž' => 'è²«',
  'ØŸ' => 'è²¬',
  'Ø ' => 'è²­',
  'Ø¡' => 'äº�',
  'Ø¢' => 'ä¸Œ',
  'Ø£' => 'å…€',
  'Ø¤' => 'ä¸�',
  'Ø¥' => 'å»¿',
  'Ø¦' => 'å�…',
  'Ø§' => 'ä¸•',
  'Ø¨' => 'äº˜',
  'Ø©' => 'ä¸ž',
  'Øª' => 'é¬²',
  'Ø«' => 'å­¬',
  'Ø¬' => 'å™©',
  'Ø­' => 'ä¸¨',
  'Ø®' => 'ç¦º',
  'Ø¯' => 'ä¸¿',
  'Ø°' => 'åŒ•',
  'Ø±' => 'ä¹‡',
  'Ø²' => 'å¤­',
  'Ø³' => 'çˆ»',
  'Ø´' => 'å�®',
  'Øµ' => 'æ°�',
  'Ø¶' => 'å›Ÿ',
  'Ø·' => 'èƒ¤',
  'Ø¸' => 'é¦—',
  'Ø¹' => 'æ¯“',
  'Øº' => 'ç�¾',
  'Ø»' => 'é¼—',
  'Ø¼' => 'ä¸¶',
  'Ø½' => 'äºŸ',
  'Ø¾' => 'é¼�',
  'Ø¿' => 'ä¹œ',
  'ØÀ' => 'ä¹©',
  'ØÁ' => 'äº“',
  'ØÂ' => 'èŠˆ',
  'ØÃ' => 'å­›',
  'ØÄ' => 'å•¬',
  'ØÅ' => 'å˜�',
  'ØÆ' => 'ä»„',
  'ØÇ' => 'åŽ�',
  'ØÈ' => 'åŽ�',
  'ØÉ' => 'åŽ£',
  'ØÊ' => 'åŽ¥',
  'ØË' => 'åŽ®',
  'ØÌ' => 'é�¥',
  'ØÍ' => 'èµ�',
  'ØÎ' => 'åŒš',
  'ØÏ' => 'å�µ',
  'ØÐ' => 'åŒ¦',
  'ØÑ' => 'åŒ®',
  'ØÒ' => 'åŒ¾',
  'ØÓ' => 'èµœ',
  'ØÔ' => 'å�¦',
  'ØÕ' => 'å�£',
  'ØÖ' => 'åˆ‚',
  'Ø×' => 'åˆˆ',
  'ØØ' => 'åˆŽ',
  'ØÙ' => 'åˆ­',
  'ØÚ' => 'åˆ³',
  'ØÛ' => 'åˆ¿',
  'ØÜ' => 'å‰€',
  'ØÝ' => 'å‰Œ',
  'ØÞ' => 'å‰ž',
  'Øß' => 'å‰¡',
  'Øà' => 'å‰œ',
  'Øá' => 'è’¯',
  'Øâ' => 'å‰½',
  'Øã' => 'åŠ‚',
  'Øä' => 'åŠ�',
  'Øå' => 'åŠ�',
  'Øæ' => 'åŠ“',
  'Øç' => 'å†‚',
  'Øè' => 'ç½”',
  'Øé' => 'äº»',
  'Øê' => 'ä»ƒ',
  'Øë' => 'ä»‰',
  'Øì' => 'ä»‚',
  'Øí' => 'ä»¨',
  'Øî' => 'ä»¡',
  'Øï' => 'ä»«',
  'Øð' => 'ä»ž',
  'Øñ' => 'ä¼›',
  'Øò' => 'ä»³',
  'Øó' => 'ä¼¢',
  'Øô' => 'ä½¤',
  'Øõ' => 'ä»µ',
  'Øö' => 'ä¼¥',
  'Ø÷' => 'ä¼§',
  'Øø' => 'ä¼‰',
  'Øù' => 'ä¼«',
  'Øú' => 'ä½ž',
  'Øû' => 'ä½§',
  'Øü' => 'æ”¸',
  'Øý' => 'ä½š',
  'Øþ' => 'ä½�',
  'Ù@' => 'è²®',
  'ÙA' => 'è²¯',
  'ÙB' => 'è²°',
  'ÙC' => 'è²±',
  'ÙD' => 'è²²',
  'ÙE' => 'è²³',
  'ÙF' => 'è²´',
  'ÙG' => 'è²µ',
  'ÙH' => 'è²¶',
  'ÙI' => 'è²·',
  'ÙJ' => 'è²¸',
  'ÙK' => 'è²¹',
  'ÙL' => 'è²º',
  'ÙM' => 'è²»',
  'ÙN' => 'è²¼',
  'ÙO' => 'è²½',
  'ÙP' => 'è²¾',
  'ÙQ' => 'è²¿',
  'ÙR' => 'è³€',
  'ÙS' => 'è³�',
  'ÙT' => 'è³‚',
  'ÙU' => 'è³ƒ',
  'ÙV' => 'è³„',
  'ÙW' => 'è³…',
  'ÙX' => 'è³†',
  'ÙY' => 'è³‡',
  'ÙZ' => 'è³ˆ',
  'Ù[' => 'è³‰',
  'Ù\\' => 'è³Š',
  'Ù]' => 'è³‹',
  'Ù^' => 'è³Œ',
  'Ù_' => 'è³�',
  'Ù`' => 'è³Ž',
  'Ùa' => 'è³�',
  'Ùb' => 'è³�',
  'Ùc' => 'è³‘',
  'Ùd' => 'è³’',
  'Ùe' => 'è³“',
  'Ùf' => 'è³”',
  'Ùg' => 'è³•',
  'Ùh' => 'è³–',
  'Ùi' => 'è³—',
  'Ùj' => 'è³˜',
  'Ùk' => 'è³™',
  'Ùl' => 'è³š',
  'Ùm' => 'è³›',
  'Ùn' => 'è³œ',
  'Ùo' => 'è³�',
  'Ùp' => 'è³ž',
  'Ùq' => 'è³Ÿ',
  'Ùr' => 'è³ ',
  'Ùs' => 'è³¡',
  'Ùt' => 'è³¢',
  'Ùu' => 'è³£',
  'Ùv' => 'è³¤',
  'Ùw' => 'è³¥',
  'Ùx' => 'è³¦',
  'Ùy' => 'è³§',
  'Ùz' => 'è³¨',
  'Ù{' => 'è³©',
  'Ù|' => 'è³ª',
  'Ù}' => 'è³«',
  'Ù~' => 'è³¬',
  'Ù€' => 'è³­',
  'Ù�' => 'è³®',
  'Ù‚' => 'è³¯',
  'Ùƒ' => 'è³°',
  'Ù„' => 'è³±',
  'Ù…' => 'è³²',
  'Ù†' => 'è³³',
  'Ù‡' => 'è³´',
  'Ùˆ' => 'è³µ',
  'Ù‰' => 'è³¶',
  'ÙŠ' => 'è³·',
  'Ù‹' => 'è³¸',
  'ÙŒ' => 'è³¹',
  'Ù�' => 'è³º',
  'ÙŽ' => 'è³»',
  'Ù�' => 'è³¼',
  'Ù�' => 'è³½',
  'Ù‘' => 'è³¾',
  'Ù’' => 'è³¿',
  'Ù“' => 'è´€',
  'Ù”' => 'è´�',
  'Ù•' => 'è´‚',
  'Ù–' => 'è´ƒ',
  'Ù—' => 'è´„',
  'Ù˜' => 'è´…',
  'Ù™' => 'è´†',
  'Ùš' => 'è´‡',
  'Ù›' => 'è´ˆ',
  'Ùœ' => 'è´‰',
  'Ù�' => 'è´Š',
  'Ùž' => 'è´‹',
  'ÙŸ' => 'è´Œ',
  'Ù ' => 'è´�',
  'Ù¡' => 'ä½Ÿ',
  'Ù¢' => 'ä½—',
  'Ù£' => 'ä¼²',
  'Ù¤' => 'ä¼½',
  'Ù¥' => 'ä½¶',
  'Ù¦' => 'ä½´',
  'Ù§' => 'ä¾‘',
  'Ù¨' => 'ä¾‰',
  'Ù©' => 'ä¾ƒ',
  'Ùª' => 'ä¾�',
  'Ù«' => 'ä½¾',
  'Ù¬' => 'ä½»',
  'Ù­' => 'ä¾ª',
  'Ù®' => 'ä½¼',
  'Ù¯' => 'ä¾¬',
  'Ù°' => 'ä¾”',
  'Ù±' => 'ä¿¦',
  'Ù²' => 'ä¿¨',
  'Ù³' => 'ä¿ª',
  'Ù´' => 'ä¿…',
  'Ùµ' => 'ä¿š',
  'Ù¶' => 'ä¿£',
  'Ù·' => 'ä¿œ',
  'Ù¸' => 'ä¿‘',
  'Ù¹' => 'ä¿Ÿ',
  'Ùº' => 'ä¿¸',
  'Ù»' => 'å€©',
  'Ù¼' => 'å�Œ',
  'Ù½' => 'ä¿³',
  'Ù¾' => 'å€¬',
  'Ù¿' => 'å€�',
  'ÙÀ' => 'å€®',
  'ÙÁ' => 'å€­',
  'ÙÂ' => 'ä¿¾',
  'ÙÃ' => 'å€œ',
  'ÙÄ' => 'å€Œ',
  'ÙÅ' => 'å€¥',
  'ÙÆ' => 'å€¨',
  'ÙÇ' => 'å�¾',
  'ÙÈ' => 'å�ƒ',
  'ÙÉ' => 'å�•',
  'ÙÊ' => 'å�ˆ',
  'ÙË' => 'å�Ž',
  'ÙÌ' => 'å�¬',
  'ÙÍ' => 'å�»',
  'ÙÎ' => 'å‚¥',
  'ÙÏ' => 'å‚§',
  'ÙÐ' => 'å‚©',
  'ÙÑ' => 'å‚º',
  'ÙÒ' => 'åƒ–',
  'ÙÓ' => 'å„†',
  'ÙÔ' => 'åƒ­',
  'ÙÕ' => 'åƒ¬',
  'ÙÖ' => 'åƒ¦',
  'Ù×' => 'åƒ®',
  'ÙØ' => 'å„‡',
  'ÙÙ' => 'å„‹',
  'ÙÚ' => 'ä»�',
  'ÙÛ' => 'æ°½',
  'ÙÜ' => 'ä½˜',
  'ÙÝ' => 'ä½¥',
  'ÙÞ' => 'ä¿Ž',
  'Ùß' => 'é¾ ',
  'Ùà' => 'æ±†',
  'Ùá' => 'ç±´',
  'Ùâ' => 'å…®',
  'Ùã' => 'å·½',
  'Ùä' => 'é»‰',
  'Ùå' => 'é¦˜',
  'Ùæ' => 'å†�',
  'Ùç' => 'å¤”',
  'Ùè' => 'å‹¹',
  'Ùé' => 'åŒ�',
  'Ùê' => 'è¨‡',
  'Ùë' => 'åŒ�',
  'Ùì' => 'å‡«',
  'Ùí' => 'å¤™',
  'Ùî' => 'å…•',
  'Ùï' => 'äº ',
  'Ùð' => 'å…–',
  'Ùñ' => 'äº³',
  'Ùò' => 'è¡®',
  'Ùó' => 'è¢¤',
  'Ùô' => 'äºµ',
  'Ùõ' => 'è„”',
  'Ùö' => 'è£’',
  'Ù÷' => 'ç¦€',
  'Ùø' => 'å¬´',
  'Ùù' => 'è ƒ',
  'Ùú' => 'ç¾¸',
  'Ùû' => 'å†«',
  'Ùü' => 'å†±',
  'Ùý' => 'å†½',
  'Ùþ' => 'å†¼',
  'Ú@' => 'è´Ž',
  'ÚA' => 'è´�',
  'ÚB' => 'è´�',
  'ÚC' => 'è´‘',
  'ÚD' => 'è´’',
  'ÚE' => 'è´“',
  'ÚF' => 'è´”',
  'ÚG' => 'è´•',
  'ÚH' => 'è´–',
  'ÚI' => 'è´—',
  'ÚJ' => 'è´˜',
  'ÚK' => 'è´™',
  'ÚL' => 'è´š',
  'ÚM' => 'è´›',
  'ÚN' => 'è´œ',
  'ÚO' => 'è´ ',
  'ÚP' => 'èµ‘',
  'ÚQ' => 'èµ’',
  'ÚR' => 'èµ—',
  'ÚS' => 'èµŸ',
  'ÚT' => 'èµ¥',
  'ÚU' => 'èµ¨',
  'ÚV' => 'èµ©',
  'ÚW' => 'èµª',
  'ÚX' => 'èµ¬',
  'ÚY' => 'èµ®',
  'ÚZ' => 'èµ¯',
  'Ú[' => 'èµ±',
  'Ú\\' => 'èµ²',
  'Ú]' => 'èµ¸',
  'Ú^' => 'èµ¹',
  'Ú_' => 'èµº',
  'Ú`' => 'èµ»',
  'Úa' => 'èµ¼',
  'Úb' => 'èµ½',
  'Úc' => 'èµ¾',
  'Úd' => 'èµ¿',
  'Úe' => 'è¶€',
  'Úf' => 'è¶‚',
  'Úg' => 'è¶ƒ',
  'Úh' => 'è¶†',
  'Úi' => 'è¶‡',
  'Új' => 'è¶ˆ',
  'Úk' => 'è¶‰',
  'Úl' => 'è¶Œ',
  'Úm' => 'è¶�',
  'Ún' => 'è¶Ž',
  'Úo' => 'è¶�',
  'Úp' => 'è¶�',
  'Úq' => 'è¶’',
  'Úr' => 'è¶“',
  'Ús' => 'è¶•',
  'Út' => 'è¶–',
  'Úu' => 'è¶—',
  'Úv' => 'è¶˜',
  'Úw' => 'è¶™',
  'Úx' => 'è¶š',
  'Úy' => 'è¶›',
  'Úz' => 'è¶œ',
  'Ú{' => 'è¶�',
  'Ú|' => 'è¶ž',
  'Ú}' => 'è¶ ',
  'Ú~' => 'è¶¡',
  'Ú€' => 'è¶¢',
  'Ú�' => 'è¶¤',
  'Ú‚' => 'è¶¥',
  'Úƒ' => 'è¶¦',
  'Ú„' => 'è¶§',
  'Ú…' => 'è¶¨',
  'Ú†' => 'è¶©',
  'Ú‡' => 'è¶ª',
  'Úˆ' => 'è¶«',
  'Ú‰' => 'è¶¬',
  'ÚŠ' => 'è¶­',
  'Ú‹' => 'è¶®',
  'ÚŒ' => 'è¶¯',
  'Ú�' => 'è¶°',
  'ÚŽ' => 'è¶²',
  'Ú�' => 'è¶¶',
  'Ú�' => 'è¶·',
  'Ú‘' => 'è¶¹',
  'Ú’' => 'è¶»',
  'Ú“' => 'è¶½',
  'Ú”' => 'è·€',
  'Ú•' => 'è·�',
  'Ú–' => 'è·‚',
  'Ú—' => 'è·…',
  'Ú˜' => 'è·‡',
  'Ú™' => 'è·ˆ',
  'Úš' => 'è·‰',
  'Ú›' => 'è·Š',
  'Úœ' => 'è·�',
  'Ú�' => 'è·�',
  'Úž' => 'è·’',
  'ÚŸ' => 'è·“',
  'Ú ' => 'è·”',
  'Ú¡' => 'å‡‡',
  'Ú¢' => 'å†–',
  'Ú£' => 'å†¢',
  'Ú¤' => 'å†¥',
  'Ú¥' => 'è® ',
  'Ú¦' => 'è®¦',
  'Ú§' => 'è®§',
  'Ú¨' => 'è®ª',
  'Ú©' => 'è®´',
  'Úª' => 'è®µ',
  'Ú«' => 'è®·',
  'Ú¬' => 'è¯‚',
  'Ú­' => 'è¯ƒ',
  'Ú®' => 'è¯‹',
  'Ú¯' => 'è¯�',
  'Ú°' => 'è¯Ž',
  'Ú±' => 'è¯’',
  'Ú²' => 'è¯“',
  'Ú³' => 'è¯”',
  'Ú´' => 'è¯–',
  'Úµ' => 'è¯˜',
  'Ú¶' => 'è¯™',
  'Ú·' => 'è¯œ',
  'Ú¸' => 'è¯Ÿ',
  'Ú¹' => 'è¯ ',
  'Úº' => 'è¯¤',
  'Ú»' => 'è¯¨',
  'Ú¼' => 'è¯©',
  'Ú½' => 'è¯®',
  'Ú¾' => 'è¯°',
  'Ú¿' => 'è¯³',
  'ÚÀ' => 'è¯¶',
  'ÚÁ' => 'è¯¹',
  'ÚÂ' => 'è¯¼',
  'ÚÃ' => 'è¯¿',
  'ÚÄ' => 'è°€',
  'ÚÅ' => 'è°‚',
  'ÚÆ' => 'è°„',
  'ÚÇ' => 'è°‡',
  'ÚÈ' => 'è°Œ',
  'ÚÉ' => 'è°�',
  'ÚÊ' => 'è°‘',
  'ÚË' => 'è°’',
  'ÚÌ' => 'è°”',
  'ÚÍ' => 'è°•',
  'ÚÎ' => 'è°–',
  'ÚÏ' => 'è°™',
  'ÚÐ' => 'è°›',
  'ÚÑ' => 'è°˜',
  'ÚÒ' => 'è°�',
  'ÚÓ' => 'è°Ÿ',
  'ÚÔ' => 'è° ',
  'ÚÕ' => 'è°¡',
  'ÚÖ' => 'è°¥',
  'Ú×' => 'è°§',
  'ÚØ' => 'è°ª',
  'ÚÙ' => 'è°«',
  'ÚÚ' => 'è°®',
  'ÚÛ' => 'è°¯',
  'ÚÜ' => 'è°²',
  'ÚÝ' => 'è°³',
  'ÚÞ' => 'è°µ',
  'Úß' => 'è°¶',
  'Úà' => 'å�©',
  'Úá' => 'å�º',
  'Úâ' => 'é˜�',
  'Úã' => 'é˜¢',
  'Úä' => 'é˜¡',
  'Úå' => 'é˜±',
  'Úæ' => 'é˜ª',
  'Úç' => 'é˜½',
  'Úè' => 'é˜¼',
  'Úé' => 'é™‚',
  'Úê' => 'é™‰',
  'Úë' => 'é™”',
  'Úì' => 'é™Ÿ',
  'Úí' => 'é™§',
  'Úî' => 'é™¬',
  'Úï' => 'é™²',
  'Úð' => 'é™´',
  'Úñ' => 'éšˆ',
  'Úò' => 'éš�',
  'Úó' => 'éš—',
  'Úô' => 'éš°',
  'Úõ' => 'é‚—',
  'Úö' => 'é‚›',
  'Ú÷' => 'é‚�',
  'Úø' => 'é‚™',
  'Úù' => 'é‚¬',
  'Úú' => 'é‚¡',
  'Úû' => 'é‚´',
  'Úü' => 'é‚³',
  'Úý' => 'é‚¶',
  'Úþ' => 'é‚º',
  'Û@' => 'è·•',
  'ÛA' => 'è·˜',
  'ÛB' => 'è·™',
  'ÛC' => 'è·œ',
  'ÛD' => 'è· ',
  'ÛE' => 'è·¡',
  'ÛF' => 'è·¢',
  'ÛG' => 'è·¥',
  'ÛH' => 'è·¦',
  'ÛI' => 'è·§',
  'ÛJ' => 'è·©',
  'ÛK' => 'è·­',
  'ÛL' => 'è·®',
  'ÛM' => 'è·°',
  'ÛN' => 'è·±',
  'ÛO' => 'è·²',
  'ÛP' => 'è·´',
  'ÛQ' => 'è·¶',
  'ÛR' => 'è·¼',
  'ÛS' => 'è·¾',
  'ÛT' => 'è·¿',
  'ÛU' => 'è¸€',
  'ÛV' => 'è¸�',
  'ÛW' => 'è¸‚',
  'ÛX' => 'è¸ƒ',
  'ÛY' => 'è¸„',
  'ÛZ' => 'è¸†',
  'Û[' => 'è¸‡',
  'Û\\' => 'è¸ˆ',
  'Û]' => 'è¸‹',
  'Û^' => 'è¸�',
  'Û_' => 'è¸Ž',
  'Û`' => 'è¸�',
  'Ûa' => 'è¸‘',
  'Ûb' => 'è¸’',
  'Ûc' => 'è¸“',
  'Ûd' => 'è¸•',
  'Ûe' => 'è¸–',
  'Ûf' => 'è¸—',
  'Ûg' => 'è¸˜',
  'Ûh' => 'è¸™',
  'Ûi' => 'è¸š',
  'Ûj' => 'è¸›',
  'Ûk' => 'è¸œ',
  'Ûl' => 'è¸ ',
  'Ûm' => 'è¸¡',
  'Ûn' => 'è¸¤',
  'Ûo' => 'è¸¥',
  'Ûp' => 'è¸¦',
  'Ûq' => 'è¸§',
  'Ûr' => 'è¸¨',
  'Ûs' => 'è¸«',
  'Ût' => 'è¸­',
  'Ûu' => 'è¸°',
  'Ûv' => 'è¸²',
  'Ûw' => 'è¸³',
  'Ûx' => 'è¸´',
  'Ûy' => 'è¸¶',
  'Ûz' => 'è¸·',
  'Û{' => 'è¸¸',
  'Û|' => 'è¸»',
  'Û}' => 'è¸¼',
  'Û~' => 'è¸¾',
  'Û€' => 'è¸¿',
  'Û�' => 'è¹ƒ',
  'Û‚' => 'è¹…',
  'Ûƒ' => 'è¹†',
  'Û„' => 'è¹Œ',
  'Û…' => 'è¹�',
  'Û†' => 'è¹Ž',
  'Û‡' => 'è¹�',
  'Ûˆ' => 'è¹�',
  'Û‰' => 'è¹“',
  'ÛŠ' => 'è¹”',
  'Û‹' => 'è¹•',
  'ÛŒ' => 'è¹–',
  'Û�' => 'è¹—',
  'ÛŽ' => 'è¹˜',
  'Û�' => 'è¹š',
  'Û�' => 'è¹›',
  'Û‘' => 'è¹œ',
  'Û’' => 'è¹�',
  'Û“' => 'è¹ž',
  'Û”' => 'è¹Ÿ',
  'Û•' => 'è¹ ',
  'Û–' => 'è¹¡',
  'Û—' => 'è¹¢',
  'Û˜' => 'è¹£',
  'Û™' => 'è¹¤',
  'Ûš' => 'è¹¥',
  'Û›' => 'è¹§',
  'Ûœ' => 'è¹¨',
  'Û�' => 'è¹ª',
  'Ûž' => 'è¹«',
  'ÛŸ' => 'è¹®',
  'Û ' => 'è¹±',
  'Û¡' => 'é‚¸',
  'Û¢' => 'é‚°',
  'Û£' => 'éƒ�',
  'Û¤' => 'éƒ…',
  'Û¥' => 'é‚¾',
  'Û¦' => 'éƒ�',
  'Û§' => 'éƒ„',
  'Û¨' => 'éƒ‡',
  'Û©' => 'éƒ“',
  'Ûª' => 'éƒ¦',
  'Û«' => 'éƒ¢',
  'Û¬' => 'éƒœ',
  'Û­' => 'éƒ—',
  'Û®' => 'éƒ›',
  'Û¯' => 'éƒ«',
  'Û°' => 'éƒ¯',
  'Û±' => 'éƒ¾',
  'Û²' => 'é„„',
  'Û³' => 'é„¢',
  'Û´' => 'é„ž',
  'Ûµ' => 'é„£',
  'Û¶' => 'é„±',
  'Û·' => 'é„¯',
  'Û¸' => 'é„¹',
  'Û¹' => 'é…ƒ',
  'Ûº' => 'é…†',
  'Û»' => 'åˆ�',
  'Û¼' => 'å¥‚',
  'Û½' => 'åŠ¢',
  'Û¾' => 'åŠ¬',
  'Û¿' => 'åŠ­',
  'ÛÀ' => 'åŠ¾',
  'ÛÁ' => 'å“¿',
  'ÛÂ' => 'å‹�',
  'ÛÃ' => 'å‹–',
  'ÛÄ' => 'å‹°',
  'ÛÅ' => 'å�Ÿ',
  'ÛÆ' => 'ç‡®',
  'ÛÇ' => 'çŸ�',
  'ÛÈ' => 'å»´',
  'ÛÉ' => 'å‡µ',
  'ÛÊ' => 'å‡¼',
  'ÛË' => 'é¬¯',
  'ÛÌ' => 'åŽ¶',
  'ÛÍ' => 'å¼�',
  'ÛÎ' => 'ç•š',
  'ÛÏ' => 'å·¯',
  'ÛÐ' => 'å�Œ',
  'ÛÑ' => 'åž©',
  'ÛÒ' => 'åž¡',
  'ÛÓ' => 'å¡¾',
  'ÛÔ' => 'å¢¼',
  'ÛÕ' => 'å£…',
  'ÛÖ' => 'å£‘',
  'Û×' => 'åœ©',
  'ÛØ' => 'åœ¬',
  'ÛÙ' => 'åœª',
  'ÛÚ' => 'åœ³',
  'ÛÛ' => 'åœ¹',
  'ÛÜ' => 'åœ®',
  'ÛÝ' => 'åœ¯',
  'ÛÞ' => 'å�œ',
  'Ûß' => 'åœ»',
  'Ûà' => 'å�‚',
  'Ûá' => 'å�©',
  'Ûâ' => 'åž…',
  'Ûã' => 'å�«',
  'Ûä' => 'åž†',
  'Ûå' => 'å�¼',
  'Ûæ' => 'å�»',
  'Ûç' => 'å�¨',
  'Ûè' => 'å�­',
  'Ûé' => 'å�¶',
  'Ûê' => 'å�³',
  'Ûë' => 'åž­',
  'Ûì' => 'åž¤',
  'Ûí' => 'åžŒ',
  'Ûî' => 'åž²',
  'Ûï' => 'åŸ�',
  'Ûð' => 'åž§',
  'Ûñ' => 'åž´',
  'Ûò' => 'åž“',
  'Ûó' => 'åž ',
  'Ûô' => 'åŸ•',
  'Ûõ' => 'åŸ˜',
  'Ûö' => 'åŸš',
  'Û÷' => 'åŸ™',
  'Ûø' => 'åŸ’',
  'Ûù' => 'åž¸',
  'Ûú' => 'åŸ´',
  'Ûû' => 'åŸ¯',
  'Ûü' => 'åŸ¸',
  'Ûý' => 'åŸ¤',
  'Ûþ' => 'åŸ�',
  'Ü@' => 'è¹³',
  'ÜA' => 'è¹µ',
  'ÜB' => 'è¹·',
  'ÜC' => 'è¹¸',
  'ÜD' => 'è¹¹',
  'ÜE' => 'è¹º',
  'ÜF' => 'è¹»',
  'ÜG' => 'è¹½',
  'ÜH' => 'è¹¾',
  'ÜI' => 'èº€',
  'ÜJ' => 'èº‚',
  'ÜK' => 'èºƒ',
  'ÜL' => 'èº„',
  'ÜM' => 'èº†',
  'ÜN' => 'èºˆ',
  'ÜO' => 'èº‰',
  'ÜP' => 'èºŠ',
  'ÜQ' => 'èº‹',
  'ÜR' => 'èºŒ',
  'ÜS' => 'èº�',
  'ÜT' => 'èºŽ',
  'ÜU' => 'èº‘',
  'ÜV' => 'èº’',
  'ÜW' => 'èº“',
  'ÜX' => 'èº•',
  'ÜY' => 'èº–',
  'ÜZ' => 'èº—',
  'Ü[' => 'èº˜',
  'Ü\\' => 'èº™',
  'Ü]' => 'èºš',
  'Ü^' => 'èº›',
  'Ü_' => 'èº�',
  'Ü`' => 'èºŸ',
  'Üa' => 'èº ',
  'Üb' => 'èº¡',
  'Üc' => 'èº¢',
  'Üd' => 'èº£',
  'Üe' => 'èº¤',
  'Üf' => 'èº¥',
  'Üg' => 'èº¦',
  'Üh' => 'èº§',
  'Üi' => 'èº¨',
  'Üj' => 'èº©',
  'Ük' => 'èºª',
  'Ül' => 'èº­',
  'Üm' => 'èº®',
  'Ün' => 'èº°',
  'Üo' => 'èº±',
  'Üp' => 'èº³',
  'Üq' => 'èº´',
  'Ür' => 'èºµ',
  'Üs' => 'èº¶',
  'Üt' => 'èº·',
  'Üu' => 'èº¸',
  'Üv' => 'èº¹',
  'Üw' => 'èº»',
  'Üx' => 'èº¼',
  'Üy' => 'èº½',
  'Üz' => 'èº¾',
  'Ü{' => 'èº¿',
  'Ü|' => 'è»€',
  'Ü}' => 'è»�',
  'Ü~' => 'è»‚',
  'Ü€' => 'è»ƒ',
  'Ü�' => 'è»„',
  'Ü‚' => 'è»…',
  'Üƒ' => 'è»†',
  'Ü„' => 'è»‡',
  'Ü…' => 'è»ˆ',
  'Ü†' => 'è»‰',
  'Ü‡' => 'è»Š',
  'Üˆ' => 'è»‹',
  'Ü‰' => 'è»Œ',
  'ÜŠ' => 'è»�',
  'Ü‹' => 'è»�',
  'ÜŒ' => 'è»�',
  'Ü�' => 'è»‘',
  'ÜŽ' => 'è»’',
  'Ü�' => 'è»“',
  'Ü�' => 'è»”',
  'Ü‘' => 'è»•',
  'Ü’' => 'è»–',
  'Ü“' => 'è»—',
  'Ü”' => 'è»˜',
  'Ü•' => 'è»™',
  'Ü–' => 'è»š',
  'Ü—' => 'è»›',
  'Ü˜' => 'è»œ',
  'Ü™' => 'è»�',
  'Üš' => 'è»ž',
  'Ü›' => 'è»Ÿ',
  'Üœ' => 'è» ',
  'Ü�' => 'è»¡',
  'Üž' => 'è»¢',
  'ÜŸ' => 'è»£',
  'Ü ' => 'è»¤',
  'Ü¡' => 'å ‹',
  'Ü¢' => 'å �',
  'Ü£' => 'åŸ½',
  'Ü¤' => 'åŸ­',
  'Ü¥' => 'å €',
  'Ü¦' => 'å ž',
  'Ü§' => 'å ™',
  'Ü¨' => 'å¡„',
  'Ü©' => 'å  ',
  'Üª' => 'å¡¥',
  'Ü«' => 'å¡¬',
  'Ü¬' => 'å¢�',
  'Ü­' => 'å¢‰',
  'Ü®' => 'å¢š',
  'Ü¯' => 'å¢€',
  'Ü°' => 'é¦¨',
  'Ü±' => 'é¼™',
  'Ü²' => 'æ‡¿',
  'Ü³' => 'è‰¹',
  'Ü´' => 'è‰½',
  'Üµ' => 'è‰¿',
  'Ü¶' => 'èŠ�',
  'Ü·' => 'èŠŠ',
  'Ü¸' => 'èŠ¨',
  'Ü¹' => 'èŠ„',
  'Üº' => 'èŠŽ',
  'Ü»' => 'èŠ‘',
  'Ü¼' => 'èŠ—',
  'Ü½' => 'èŠ™',
  'Ü¾' => 'èŠ«',
  'Ü¿' => 'èŠ¸',
  'ÜÀ' => 'èŠ¾',
  'ÜÁ' => 'èŠ°',
  'ÜÂ' => 'è‹ˆ',
  'ÜÃ' => 'è‹Š',
  'ÜÄ' => 'è‹£',
  'ÜÅ' => 'èŠ˜',
  'ÜÆ' => 'èŠ·',
  'ÜÇ' => 'èŠ®',
  'ÜÈ' => 'è‹‹',
  'ÜÉ' => 'è‹Œ',
  'ÜÊ' => 'è‹�',
  'ÜË' => 'èŠ©',
  'ÜÌ' => 'èŠ´',
  'ÜÍ' => 'èŠ¡',
  'ÜÎ' => 'èŠª',
  'ÜÏ' => 'èŠŸ',
  'ÜÐ' => 'è‹„',
  'ÜÑ' => 'è‹Ž',
  'ÜÒ' => 'èŠ¤',
  'ÜÓ' => 'è‹¡',
  'ÜÔ' => 'èŒ‰',
  'ÜÕ' => 'è‹·',
  'ÜÖ' => 'è‹¤',
  'Ü×' => 'èŒ�',
  'ÜØ' => 'èŒ‡',
  'ÜÙ' => 'è‹œ',
  'ÜÚ' => 'è‹´',
  'ÜÛ' => 'è‹’',
  'ÜÜ' => 'è‹˜',
  'ÜÝ' => 'èŒŒ',
  'ÜÞ' => 'è‹»',
  'Üß' => 'è‹“',
  'Üà' => 'èŒ‘',
  'Üá' => 'èŒš',
  'Üâ' => 'èŒ†',
  'Üã' => 'èŒ”',
  'Üä' => 'èŒ•',
  'Üå' => 'è‹ ',
  'Üæ' => 'è‹•',
  'Üç' => 'èŒœ',
  'Üè' => 'è�‘',
  'Üé' => 'è�›',
  'Üê' => 'è�œ',
  'Üë' => 'èŒˆ',
  'Üì' => 'èŽ’',
  'Üí' => 'èŒ¼',
  'Üî' => 'èŒ´',
  'Üï' => 'èŒ±',
  'Üð' => 'èŽ›',
  'Üñ' => 'è�ž',
  'Üò' => 'èŒ¯',
  'Üó' => 'è��',
  'Üô' => 'è�‡',
  'Üõ' => 'è�ƒ',
  'Üö' => 'è�Ÿ',
  'Ü÷' => 'è�€',
  'Üø' => 'èŒ—',
  'Üù' => 'è� ',
  'Üú' => 'èŒ­',
  'Üû' => 'èŒº',
  'Üü' => 'èŒ³',
  'Üý' => 'è�¦',
  'Üþ' => 'è�¥',
  'Ý@' => 'è»¥',
  'ÝA' => 'è»¦',
  'ÝB' => 'è»§',
  'ÝC' => 'è»¨',
  'ÝD' => 'è»©',
  'ÝE' => 'è»ª',
  'ÝF' => 'è»«',
  'ÝG' => 'è»¬',
  'ÝH' => 'è»­',
  'ÝI' => 'è»®',
  'ÝJ' => 'è»¯',
  'ÝK' => 'è»°',
  'ÝL' => 'è»±',
  'ÝM' => 'è»²',
  'ÝN' => 'è»³',
  'ÝO' => 'è»´',
  'ÝP' => 'è»µ',
  'ÝQ' => 'è»¶',
  'ÝR' => 'è»·',
  'ÝS' => 'è»¸',
  'ÝT' => 'è»¹',
  'ÝU' => 'è»º',
  'ÝV' => 'è»»',
  'ÝW' => 'è»¼',
  'ÝX' => 'è»½',
  'ÝY' => 'è»¾',
  'ÝZ' => 'è»¿',
  'Ý[' => 'è¼€',
  'Ý\\' => 'è¼�',
  'Ý]' => 'è¼‚',
  'Ý^' => 'è¼ƒ',
  'Ý_' => 'è¼„',
  'Ý`' => 'è¼…',
  'Ýa' => 'è¼†',
  'Ýb' => 'è¼‡',
  'Ýc' => 'è¼ˆ',
  'Ýd' => 'è¼‰',
  'Ýe' => 'è¼Š',
  'Ýf' => 'è¼‹',
  'Ýg' => 'è¼Œ',
  'Ýh' => 'è¼�',
  'Ýi' => 'è¼Ž',
  'Ýj' => 'è¼�',
  'Ýk' => 'è¼�',
  'Ýl' => 'è¼‘',
  'Ým' => 'è¼’',
  'Ýn' => 'è¼“',
  'Ýo' => 'è¼”',
  'Ýp' => 'è¼•',
  'Ýq' => 'è¼–',
  'Ýr' => 'è¼—',
  'Ýs' => 'è¼˜',
  'Ýt' => 'è¼™',
  'Ýu' => 'è¼š',
  'Ýv' => 'è¼›',
  'Ýw' => 'è¼œ',
  'Ýx' => 'è¼�',
  'Ýy' => 'è¼ž',
  'Ýz' => 'è¼Ÿ',
  'Ý{' => 'è¼ ',
  'Ý|' => 'è¼¡',
  'Ý}' => 'è¼¢',
  'Ý~' => 'è¼£',
  'Ý€' => 'è¼¤',
  'Ý�' => 'è¼¥',
  'Ý‚' => 'è¼¦',
  'Ýƒ' => 'è¼§',
  'Ý„' => 'è¼¨',
  'Ý…' => 'è¼©',
  'Ý†' => 'è¼ª',
  'Ý‡' => 'è¼«',
  'Ýˆ' => 'è¼¬',
  'Ý‰' => 'è¼­',
  'ÝŠ' => 'è¼®',
  'Ý‹' => 'è¼¯',
  'ÝŒ' => 'è¼°',
  'Ý�' => 'è¼±',
  'ÝŽ' => 'è¼²',
  'Ý�' => 'è¼³',
  'Ý�' => 'è¼´',
  'Ý‘' => 'è¼µ',
  'Ý’' => 'è¼¶',
  'Ý“' => 'è¼·',
  'Ý”' => 'è¼¸',
  'Ý•' => 'è¼¹',
  'Ý–' => 'è¼º',
  'Ý—' => 'è¼»',
  'Ý˜' => 'è¼¼',
  'Ý™' => 'è¼½',
  'Ýš' => 'è¼¾',
  'Ý›' => 'è¼¿',
  'Ýœ' => 'è½€',
  'Ý�' => 'è½�',
  'Ýž' => 'è½‚',
  'ÝŸ' => 'è½ƒ',
  'Ý ' => 'è½„',
  'Ý¡' => 'è�¨',
  'Ý¢' => 'èŒ›',
  'Ý£' => 'è�©',
  'Ý¤' => 'è�¬',
  'Ý¥' => 'è�ª',
  'Ý¦' => 'è�­',
  'Ý§' => 'è�®',
  'Ý¨' => 'èŽ°',
  'Ý©' => 'è�¸',
  'Ýª' => 'èŽ³',
  'Ý«' => 'èŽ´',
  'Ý¬' => 'èŽ ',
  'Ý­' => 'èŽª',
  'Ý®' => 'èŽ“',
  'Ý¯' => 'èŽœ',
  'Ý°' => 'èŽ…',
  'Ý±' => 'è�¼',
  'Ý²' => 'èŽ¶',
  'Ý³' => 'èŽ©',
  'Ý´' => 'è�½',
  'Ýµ' => 'èŽ¸',
  'Ý¶' => 'è�»',
  'Ý·' => 'èŽ˜',
  'Ý¸' => 'èŽž',
  'Ý¹' => 'èŽ¨',
  'Ýº' => 'èŽº',
  'Ý»' => 'èŽ¼',
  'Ý¼' => 'è��',
  'Ý½' => 'è��',
  'Ý¾' => 'è�¥',
  'Ý¿' => 'è�˜',
  'ÝÀ' => 'å ‡',
  'ÝÁ' => 'è�˜',
  'ÝÂ' => 'è�‹',
  'ÝÃ' => 'è��',
  'ÝÄ' => 'è�½',
  'ÝÅ' => 'è�–',
  'ÝÆ' => 'è�œ',
  'ÝÇ' => 'è�¸',
  'ÝÈ' => 'è�‘',
  'ÝÉ' => 'è�†',
  'ÝÊ' => 'è�”',
  'ÝË' => 'è�Ÿ',
  'ÝÌ' => 'è��',
  'ÝÍ' => 'è�ƒ',
  'ÝÎ' => 'è�¸',
  'ÝÏ' => 'è�¹',
  'ÝÐ' => 'è�ª',
  'ÝÑ' => 'è�…',
  'ÝÒ' => 'è�€',
  'ÝÓ' => 'è�¦',
  'ÝÔ' => 'è�°',
  'ÝÕ' => 'è�¡',
  'ÝÖ' => 'è‘œ',
  'Ý×' => 'è‘‘',
  'ÝØ' => 'è‘š',
  'ÝÙ' => 'è‘™',
  'ÝÚ' => 'è‘³',
  'ÝÛ' => 'è’‡',
  'ÝÜ' => 'è’ˆ',
  'ÝÝ' => 'è‘º',
  'ÝÞ' => 'è’‰',
  'Ýß' => 'è‘¸',
  'Ýà' => 'è�¼',
  'Ýá' => 'è‘†',
  'Ýâ' => 'è‘©',
  'Ýã' => 'è‘¶',
  'Ýä' => 'è’Œ',
  'Ýå' => 'è’Ž',
  'Ýæ' => 'è�±',
  'Ýç' => 'è‘­',
  'Ýè' => 'è“�',
  'Ýé' => 'è“�',
  'Ýê' => 'è“�',
  'Ýë' => 'è“¦',
  'Ýì' => 'è’½',
  'Ýí' => 'è““',
  'Ýî' => 'è“Š',
  'Ýï' => 'è’¿',
  'Ýð' => 'è’º',
  'Ýñ' => 'è“ ',
  'Ýò' => 'è’¡',
  'Ýó' => 'è’¹',
  'Ýô' => 'è’´',
  'Ýõ' => 'è’—',
  'Ýö' => 'è“¥',
  'Ý÷' => 'è“£',
  'Ýø' => 'è”Œ',
  'Ýù' => 'ç”�',
  'Ýú' => 'è”¸',
  'Ýû' => 'è“°',
  'Ýü' => 'è”¹',
  'Ýý' => 'è”Ÿ',
  'Ýþ' => 'è”º',
  'Þ@' => 'è½…',
  'ÞA' => 'è½†',
  'ÞB' => 'è½‡',
  'ÞC' => 'è½ˆ',
  'ÞD' => 'è½‰',
  'ÞE' => 'è½Š',
  'ÞF' => 'è½‹',
  'ÞG' => 'è½Œ',
  'ÞH' => 'è½�',
  'ÞI' => 'è½Ž',
  'ÞJ' => 'è½�',
  'ÞK' => 'è½�',
  'ÞL' => 'è½‘',
  'ÞM' => 'è½’',
  'ÞN' => 'è½“',
  'ÞO' => 'è½”',
  'ÞP' => 'è½•',
  'ÞQ' => 'è½–',
  'ÞR' => 'è½—',
  'ÞS' => 'è½˜',
  'ÞT' => 'è½™',
  'ÞU' => 'è½š',
  'ÞV' => 'è½›',
  'ÞW' => 'è½œ',
  'ÞX' => 'è½�',
  'ÞY' => 'è½ž',
  'ÞZ' => 'è½Ÿ',
  'Þ[' => 'è½ ',
  'Þ\\' => 'è½¡',
  'Þ]' => 'è½¢',
  'Þ^' => 'è½£',
  'Þ_' => 'è½¤',
  'Þ`' => 'è½¥',
  'Þa' => 'è½ª',
  'Þb' => 'è¾€',
  'Þc' => 'è¾Œ',
  'Þd' => 'è¾’',
  'Þe' => 'è¾�',
  'Þf' => 'è¾ ',
  'Þg' => 'è¾¡',
  'Þh' => 'è¾¢',
  'Þi' => 'è¾¤',
  'Þj' => 'è¾¥',
  'Þk' => 'è¾¦',
  'Þl' => 'è¾§',
  'Þm' => 'è¾ª',
  'Þn' => 'è¾¬',
  'Þo' => 'è¾­',
  'Þp' => 'è¾®',
  'Þq' => 'è¾¯',
  'Þr' => 'è¾²',
  'Þs' => 'è¾³',
  'Þt' => 'è¾´',
  'Þu' => 'è¾µ',
  'Þv' => 'è¾·',
  'Þw' => 'è¾¸',
  'Þx' => 'è¾º',
  'Þy' => 'è¾»',
  'Þz' => 'è¾¼',
  'Þ{' => 'è¾¿',
  'Þ|' => 'è¿€',
  'Þ}' => 'è¿ƒ',
  'Þ~' => 'è¿†',
  'Þ€' => 'è¿‰',
  'Þ�' => 'è¿Š',
  'Þ‚' => 'è¿‹',
  'Þƒ' => 'è¿Œ',
  'Þ„' => 'è¿�',
  'Þ…' => 'è¿�',
  'Þ†' => 'è¿’',
  'Þ‡' => 'è¿–',
  'Þˆ' => 'è¿—',
  'Þ‰' => 'è¿š',
  'ÞŠ' => 'è¿ ',
  'Þ‹' => 'è¿¡',
  'ÞŒ' => 'è¿£',
  'Þ�' => 'è¿§',
  'ÞŽ' => 'è¿¬',
  'Þ�' => 'è¿¯',
  'Þ�' => 'è¿±',
  'Þ‘' => 'è¿²',
  'Þ’' => 'è¿´',
  'Þ“' => 'è¿µ',
  'Þ”' => 'è¿¶',
  'Þ•' => 'è¿º',
  'Þ–' => 'è¿»',
  'Þ—' => 'è¿¼',
  'Þ˜' => 'è¿¾',
  'Þ™' => 'è¿¿',
  'Þš' => 'é€‡',
  'Þ›' => 'é€ˆ',
  'Þœ' => 'é€Œ',
  'Þ�' => 'é€Ž',
  'Þž' => 'é€“',
  'ÞŸ' => 'é€•',
  'Þ ' => 'é€˜',
  'Þ¡' => 'è•–',
  'Þ¢' => 'è”»',
  'Þ£' => 'è“¿',
  'Þ¤' => 'è“¼',
  'Þ¥' => 'è•™',
  'Þ¦' => 'è•ˆ',
  'Þ§' => 'è•¨',
  'Þ¨' => 'è•¤',
  'Þ©' => 'è•ž',
  'Þª' => 'è•º',
  'Þ«' => 'çž¢',
  'Þ¬' => 'è•ƒ',
  'Þ­' => 'è•²',
  'Þ®' => 'è•»',
  'Þ¯' => 'è–¤',
  'Þ°' => 'è–¨',
  'Þ±' => 'è–‡',
  'Þ²' => 'è–�',
  'Þ³' => 'è•¹',
  'Þ´' => 'è–®',
  'Þµ' => 'è–œ',
  'Þ¶' => 'è–…',
  'Þ·' => 'è–¹',
  'Þ¸' => 'è–·',
  'Þ¹' => 'è–°',
  'Þº' => 'è—“',
  'Þ»' => 'è—�',
  'Þ¼' => 'è—œ',
  'Þ½' => 'è—¿',
  'Þ¾' => 'è˜§',
  'Þ¿' => 'è˜…',
  'ÞÀ' => 'è˜©',
  'ÞÁ' => 'è˜–',
  'ÞÂ' => 'è˜¼',
  'ÞÃ' => 'å»¾',
  'ÞÄ' => 'å¼ˆ',
  'ÞÅ' => 'å¤¼',
  'ÞÆ' => 'å¥�',
  'ÞÇ' => 'è€·',
  'ÞÈ' => 'å¥•',
  'ÞÉ' => 'å¥š',
  'ÞÊ' => 'å¥˜',
  'ÞË' => 'åŒ�',
  'ÞÌ' => 'å°¢',
  'ÞÍ' => 'å°¥',
  'ÞÎ' => 'å°¬',
  'ÞÏ' => 'å°´',
  'ÞÐ' => 'æ‰Œ',
  'ÞÑ' => 'æ‰ª',
  'ÞÒ' => 'æŠŸ',
  'ÞÓ' => 'æŠ»',
  'ÞÔ' => 'æ‹Š',
  'ÞÕ' => 'æ‹š',
  'ÞÖ' => 'æ‹—',
  'Þ×' => 'æ‹®',
  'ÞØ' => 'æŒ¢',
  'ÞÙ' => 'æ‹¶',
  'ÞÚ' => 'æŒ¹',
  'ÞÛ' => 'æ�‹',
  'ÞÜ' => 'æ�ƒ',
  'ÞÝ' => 'æŽ­',
  'ÞÞ' => 'æ�¶',
  'Þß' => 'æ�±',
  'Þà' => 'æ�º',
  'Þá' => 'æŽŽ',
  'Þâ' => 'æŽ´',
  'Þã' => 'æ�­',
  'Þä' => 'æŽ¬',
  'Þå' => 'æŽŠ',
  'Þæ' => 'æ�©',
  'Þç' => 'æŽ®',
  'Þè' => 'æŽ¼',
  'Þé' => 'æ�²',
  'Þê' => 'æ�¸',
  'Þë' => 'æ� ',
  'Þì' => 'æ�¿',
  'Þí' => 'æ�„',
  'Þî' => 'æ�ž',
  'Þï' => 'æ�Ž',
  'Þð' => 'æ‘’',
  'Þñ' => 'æ�†',
  'Þò' => 'æŽ¾',
  'Þó' => 'æ‘…',
  'Þô' => 'æ‘�',
  'Þõ' => 'æ�‹',
  'Þö' => 'æ�›',
  'Þ÷' => 'æ� ',
  'Þø' => 'æ�Œ',
  'Þù' => 'æ�¦',
  'Þú' => 'æ�¡',
  'Þû' => 'æ‘ž',
  'Þü' => 'æ’„',
  'Þý' => 'æ‘­',
  'Þþ' => 'æ’–',
  'ß@' => 'é€™',
  'ßA' => 'é€œ',
  'ßB' => 'é€£',
  'ßC' => 'é€¤',
  'ßD' => 'é€¥',
  'ßE' => 'é€§',
  'ßF' => 'é€¨',
  'ßG' => 'é€©',
  'ßH' => 'é€ª',
  'ßI' => 'é€«',
  'ßJ' => 'é€¬',
  'ßK' => 'é€°',
  'ßL' => 'é€±',
  'ßM' => 'é€²',
  'ßN' => 'é€³',
  'ßO' => 'é€´',
  'ßP' => 'é€·',
  'ßQ' => 'é€¹',
  'ßR' => 'é€º',
  'ßS' => 'é€½',
  'ßT' => 'é€¿',
  'ßU' => 'é�€',
  'ßV' => 'é�ƒ',
  'ßW' => 'é�…',
  'ßX' => 'é�†',
  'ßY' => 'é�ˆ',
  'ßZ' => 'é�‰',
  'ß[' => 'é�Š',
  'ß\\' => 'é�‹',
  'ß]' => 'é�Œ',
  'ß^' => 'é�Ž',
  'ß_' => 'é�”',
  'ß`' => 'é�•',
  'ßa' => 'é�–',
  'ßb' => 'é�™',
  'ßc' => 'é�š',
  'ßd' => 'é�œ',
  'ße' => 'é��',
  'ßf' => 'é�ž',
  'ßg' => 'é�Ÿ',
  'ßh' => 'é� ',
  'ßi' => 'é�¡',
  'ßj' => 'é�¤',
  'ßk' => 'é�¦',
  'ßl' => 'é�§',
  'ßm' => 'é�©',
  'ßn' => 'é�ª',
  'ßo' => 'é�«',
  'ßp' => 'é�¬',
  'ßq' => 'é�¯',
  'ßr' => 'é�°',
  'ßs' => 'é�±',
  'ßt' => 'é�²',
  'ßu' => 'é�³',
  'ßv' => 'é�¶',
  'ßw' => 'é�·',
  'ßx' => 'é�¸',
  'ßy' => 'é�¹',
  'ßz' => 'é�º',
  'ß{' => 'é�»',
  'ß|' => 'é�¼',
  'ß}' => 'é�¾',
  'ß~' => 'é‚�',
  'ß€' => 'é‚„',
  'ß�' => 'é‚…',
  'ß‚' => 'é‚†',
  'ßƒ' => 'é‚‡',
  'ß„' => 'é‚‰',
  'ß…' => 'é‚Š',
  'ß†' => 'é‚Œ',
  'ß‡' => 'é‚�',
  'ßˆ' => 'é‚Ž',
  'ß‰' => 'é‚�',
  'ßŠ' => 'é‚�',
  'ß‹' => 'é‚’',
  'ßŒ' => 'é‚”',
  'ß�' => 'é‚–',
  'ßŽ' => 'é‚˜',
  'ß�' => 'é‚š',
  'ß�' => 'é‚œ',
  'ß‘' => 'é‚ž',
  'ß’' => 'é‚Ÿ',
  'ß“' => 'é‚ ',
  'ß”' => 'é‚¤',
  'ß•' => 'é‚¥',
  'ß–' => 'é‚§',
  'ß—' => 'é‚¨',
  'ß˜' => 'é‚©',
  'ß™' => 'é‚«',
  'ßš' => 'é‚­',
  'ß›' => 'é‚²',
  'ßœ' => 'é‚·',
  'ß�' => 'é‚¼',
  'ßž' => 'é‚½',
  'ßŸ' => 'é‚¿',
  'ß ' => 'éƒ€',
  'ß¡' => 'æ‘º',
  'ß¢' => 'æ’·',
  'ß£' => 'æ’¸',
  'ß¤' => 'æ’™',
  'ß¥' => 'æ’º',
  'ß¦' => 'æ“€',
  'ß§' => 'æ“�',
  'ß¨' => 'æ“—',
  'ß©' => 'æ“¤',
  'ßª' => 'æ“¢',
  'ß«' => 'æ”‰',
  'ß¬' => 'æ”¥',
  'ß­' => 'æ”®',
  'ß®' => 'å¼‹',
  'ß¯' => 'å¿’',
  'ß°' => 'ç”™',
  'ß±' => 'å¼‘',
  'ß²' => 'å�Ÿ',
  'ß³' => 'å�±',
  'ß´' => 'å�½',
  'ßµ' => 'å�©',
  'ß¶' => 'å�¨',
  'ß·' => 'å�»',
  'ß¸' => 'å�’',
  'ß¹' => 'å�–',
  'ßº' => 'å�†',
  'ß»' => 'å‘‹',
  'ß¼' => 'å‘’',
  'ß½' => 'å‘“',
  'ß¾' => 'å‘”',
  'ß¿' => 'å‘–',
  'ßÀ' => 'å‘ƒ',
  'ßÁ' => 'å�¡',
  'ßÂ' => 'å‘—',
  'ßÃ' => 'å‘™',
  'ßÄ' => 'å�£',
  'ßÅ' => 'å�²',
  'ßÆ' => 'å’‚',
  'ßÇ' => 'å’”',
  'ßÈ' => 'å‘·',
  'ßÉ' => 'å‘±',
  'ßÊ' => 'å‘¤',
  'ßË' => 'å’š',
  'ßÌ' => 'å’›',
  'ßÍ' => 'å’„',
  'ßÎ' => 'å‘¶',
  'ßÏ' => 'å‘¦',
  'ßÐ' => 'å’�',
  'ßÑ' => 'å“�',
  'ßÒ' => 'å’­',
  'ßÓ' => 'å“‚',
  'ßÔ' => 'å’´',
  'ßÕ' => 'å“’',
  'ßÖ' => 'å’§',
  'ß×' => 'å’¦',
  'ßØ' => 'å““',
  'ßÙ' => 'å“”',
  'ßÚ' => 'å‘²',
  'ßÛ' => 'å’£',
  'ßÜ' => 'å“•',
  'ßÝ' => 'å’»',
  'ßÞ' => 'å’¿',
  'ßß' => 'å“Œ',
  'ßà' => 'å“™',
  'ßá' => 'å“š',
  'ßâ' => 'å“œ',
  'ßã' => 'å’©',
  'ßä' => 'å’ª',
  'ßå' => 'å’¤',
  'ßæ' => 'å“�',
  'ßç' => 'å“�',
  'ßè' => 'å“ž',
  'ßé' => 'å”›',
  'ßê' => 'å“§',
  'ßë' => 'å” ',
  'ßì' => 'å“½',
  'ßí' => 'å””',
  'ßî' => 'å“³',
  'ßï' => 'å”¢',
  'ßð' => 'å”£',
  'ßñ' => 'å”�',
  'ßò' => 'å”‘',
  'ßó' => 'å”§',
  'ßô' => 'å”ª',
  'ßõ' => 'å•§',
  'ßö' => 'å–�',
  'ß÷' => 'å–µ',
  'ßø' => 'å•‰',
  'ßù' => 'å•­',
  'ßú' => 'å•�',
  'ßû' => 'å••',
  'ßü' => 'å”¿',
  'ßý' => 'å•�',
  'ßþ' => 'å”¼',
  'à@' => 'éƒ‚',
  'àA' => 'éƒƒ',
  'àB' => 'éƒ†',
  'àC' => 'éƒˆ',
  'àD' => 'éƒ‰',
  'àE' => 'éƒ‹',
  'àF' => 'éƒŒ',
  'àG' => 'éƒ�',
  'àH' => 'éƒ’',
  'àI' => 'éƒ”',
  'àJ' => 'éƒ•',
  'àK' => 'éƒ–',
  'àL' => 'éƒ˜',
  'àM' => 'éƒ™',
  'àN' => 'éƒš',
  'àO' => 'éƒž',
  'àP' => 'éƒŸ',
  'àQ' => 'éƒ ',
  'àR' => 'éƒ£',
  'àS' => 'éƒ¤',
  'àT' => 'éƒ¥',
  'àU' => 'éƒ©',
  'àV' => 'éƒª',
  'àW' => 'éƒ¬',
  'àX' => 'éƒ®',
  'àY' => 'éƒ°',
  'àZ' => 'éƒ±',
  'à[' => 'éƒ²',
  'à\\' => 'éƒ³',
  'à]' => 'éƒµ',
  'à^' => 'éƒ¶',
  'à_' => 'éƒ·',
  'à`' => 'éƒ¹',
  'àa' => 'éƒº',
  'àb' => 'éƒ»',
  'àc' => 'éƒ¼',
  'àd' => 'éƒ¿',
  'àe' => 'é„€',
  'àf' => 'é„�',
  'àg' => 'é„ƒ',
  'àh' => 'é„…',
  'ài' => 'é„†',
  'àj' => 'é„‡',
  'àk' => 'é„ˆ',
  'àl' => 'é„‰',
  'àm' => 'é„Š',
  'àn' => 'é„‹',
  'ào' => 'é„Œ',
  'àp' => 'é„�',
  'àq' => 'é„Ž',
  'àr' => 'é„�',
  'às' => 'é„�',
  'àt' => 'é„‘',
  'àu' => 'é„’',
  'àv' => 'é„“',
  'àw' => 'é„”',
  'àx' => 'é„•',
  'ày' => 'é„–',
  'àz' => 'é„—',
  'à{' => 'é„˜',
  'à|' => 'é„š',
  'à}' => 'é„›',
  'à~' => 'é„œ',
  'à€' => 'é„�',
  'à�' => 'é„Ÿ',
  'à‚' => 'é„ ',
  'àƒ' => 'é„¡',
  'à„' => 'é„¤',
  'à…' => 'é„¥',
  'à†' => 'é„¦',
  'à‡' => 'é„§',
  'àˆ' => 'é„¨',
  'à‰' => 'é„©',
  'àŠ' => 'é„ª',
  'à‹' => 'é„«',
  'àŒ' => 'é„¬',
  'à�' => 'é„­',
  'àŽ' => 'é„®',
  'à�' => 'é„°',
  'à�' => 'é„²',
  'à‘' => 'é„³',
  'à’' => 'é„´',
  'à“' => 'é„µ',
  'à”' => 'é„¶',
  'à•' => 'é„·',
  'à–' => 'é„¸',
  'à—' => 'é„º',
  'à˜' => 'é„»',
  'à™' => 'é„¼',
  'àš' => 'é„½',
  'à›' => 'é„¾',
  'àœ' => 'é„¿',
  'à�' => 'é…€',
  'àž' => 'é…�',
  'àŸ' => 'é…‚',
  'à ' => 'é…„',
  'à¡' => 'å”·',
  'à¢' => 'å•–',
  'à£' => 'å•µ',
  'à¤' => 'å•¶',
  'à¥' => 'å•·',
  'à¦' => 'å”³',
  'à§' => 'å”°',
  'à¨' => 'å•œ',
  'à©' => 'å–‹',
  'àª' => 'å—’',
  'à«' => 'å–ƒ',
  'à¬' => 'å–±',
  'à­' => 'å–¹',
  'à®' => 'å–ˆ',
  'à¯' => 'å–�',
  'à°' => 'å–Ÿ',
  'à±' => 'å•¾',
  'à²' => 'å—–',
  'à³' => 'å–‘',
  'à´' => 'å•»',
  'àµ' => 'å—Ÿ',
  'à¶' => 'å–½',
  'à·' => 'å–¾',
  'à¸' => 'å–”',
  'à¹' => 'å–™',
  'àº' => 'å—ª',
  'à»' => 'å—·',
  'à¼' => 'å—‰',
  'à½' => 'å˜Ÿ',
  'à¾' => 'å—‘',
  'à¿' => 'å—«',
  'àÀ' => 'å—¬',
  'àÁ' => 'å—”',
  'àÂ' => 'å—¦',
  'àÃ' => 'å—�',
  'àÄ' => 'å—„',
  'àÅ' => 'å—¯',
  'àÆ' => 'å—¥',
  'àÇ' => 'å—²',
  'àÈ' => 'å—³',
  'àÉ' => 'å—Œ',
  'àÊ' => 'å—�',
  'àË' => 'å—¨',
  'àÌ' => 'å—µ',
  'àÍ' => 'å—¤',
  'àÎ' => 'è¾”',
  'àÏ' => 'å˜ž',
  'àÐ' => 'å˜ˆ',
  'àÑ' => 'å˜Œ',
  'àÒ' => 'å˜�',
  'àÓ' => 'å˜¤',
  'àÔ' => 'å˜£',
  'àÕ' => 'å—¾',
  'àÖ' => 'å˜€',
  'à×' => 'å˜§',
  'àØ' => 'å˜­',
  'àÙ' => 'å™˜',
  'àÚ' => 'å˜¹',
  'àÛ' => 'å™—',
  'àÜ' => 'å˜¬',
  'àÝ' => 'å™�',
  'àÞ' => 'å™¢',
  'àß' => 'å™™',
  'àà' => 'å™œ',
  'àá' => 'å™Œ',
  'àâ' => 'å™”',
  'àã' => 'åš†',
  'àä' => 'å™¤',
  'àå' => 'å™±',
  'àæ' => 'å™«',
  'àç' => 'å™»',
  'àè' => 'å™¼',
  'àé' => 'åš…',
  'àê' => 'åš“',
  'àë' => 'åš¯',
  'àì' => 'å›”',
  'àí' => 'å›—',
  'àî' => 'å›�',
  'àï' => 'å›¡',
  'àð' => 'å›µ',
  'àñ' => 'å›«',
  'àò' => 'å›¹',
  'àó' => 'å›¿',
  'àô' => 'åœ„',
  'àõ' => 'åœŠ',
  'àö' => 'åœ‰',
  'à÷' => 'åœœ',
  'àø' => 'å¸�',
  'àù' => 'å¸™',
  'àú' => 'å¸”',
  'àû' => 'å¸‘',
  'àü' => 'å¸±',
  'àý' => 'å¸»',
  'àþ' => 'å¸¼',
  'á@' => 'é……',
  'áA' => 'é…‡',
  'áB' => 'é…ˆ',
  'áC' => 'é…‘',
  'áD' => 'é…“',
  'áE' => 'é…”',
  'áF' => 'é…•',
  'áG' => 'é…–',
  'áH' => 'é…˜',
  'áI' => 'é…™',
  'áJ' => 'é…›',
  'áK' => 'é…œ',
  'áL' => 'é…Ÿ',
  'áM' => 'é… ',
  'áN' => 'é…¦',
  'áO' => 'é…§',
  'áP' => 'é…¨',
  'áQ' => 'é…«',
  'áR' => 'é…­',
  'áS' => 'é…³',
  'áT' => 'é…º',
  'áU' => 'é…»',
  'áV' => 'é…¼',
  'áW' => 'é†€',
  'áX' => 'é†�',
  'áY' => 'é†‚',
  'áZ' => 'é†ƒ',
  'á[' => 'é†„',
  'á\\' => 'é††',
  'á]' => 'é†ˆ',
  'á^' => 'é†Š',
  'á_' => 'é†Ž',
  'á`' => 'é†�',
  'áa' => 'é†“',
  'áb' => 'é†”',
  'ác' => 'é†•',
  'ád' => 'é†–',
  'áe' => 'é†—',
  'áf' => 'é†˜',
  'ág' => 'é†™',
  'áh' => 'é†œ',
  'ái' => 'é†�',
  'áj' => 'é†ž',
  'ák' => 'é†Ÿ',
  'ál' => 'é† ',
  'ám' => 'é†¡',
  'án' => 'é†¤',
  'áo' => 'é†¥',
  'áp' => 'é†¦',
  'áq' => 'é†§',
  'ár' => 'é†¨',
  'ás' => 'é†©',
  'át' => 'é†«',
  'áu' => 'é†¬',
  'áv' => 'é†°',
  'áw' => 'é†±',
  'áx' => 'é†²',
  'áy' => 'é†³',
  'áz' => 'é†¶',
  'á{' => 'é†·',
  'á|' => 'é†¸',
  'á}' => 'é†¹',
  'á~' => 'é†»',
  'á€' => 'é†¼',
  'á�' => 'é†½',
  'á‚' => 'é†¾',
  'áƒ' => 'é†¿',
  'á„' => 'é‡€',
  'á…' => 'é‡�',
  'á†' => 'é‡‚',
  'á‡' => 'é‡ƒ',
  'áˆ' => 'é‡„',
  'á‰' => 'é‡…',
  'áŠ' => 'é‡†',
  'á‹' => 'é‡ˆ',
  'áŒ' => 'é‡‹',
  'á�' => 'é‡�',
  'áŽ' => 'é‡’',
  'á�' => 'é‡“',
  'á�' => 'é‡”',
  'á‘' => 'é‡•',
  'á’' => 'é‡–',
  'á“' => 'é‡—',
  'á”' => 'é‡˜',
  'á•' => 'é‡™',
  'á–' => 'é‡š',
  'á—' => 'é‡›',
  'á˜' => 'é‡�',
  'á™' => 'é‡ž',
  'áš' => 'é‡Ÿ',
  'á›' => 'é‡ ',
  'áœ' => 'é‡¡',
  'á�' => 'é‡¢',
  'áž' => 'é‡£',
  'áŸ' => 'é‡¤',
  'á ' => 'é‡¥',
  'á¡' => 'å¸·',
  'á¢' => 'å¹„',
  'á£' => 'å¹”',
  'á¤' => 'å¹›',
  'á¥' => 'å¹ž',
  'á¦' => 'å¹¡',
  'á§' => 'å²Œ',
  'á¨' => 'å±º',
  'á©' => 'å²�',
  'áª' => 'å²�',
  'á«' => 'å²–',
  'á¬' => 'å²ˆ',
  'á­' => 'å²˜',
  'á®' => 'å²™',
  'á¯' => 'å²‘',
  'á°' => 'å²š',
  'á±' => 'å²œ',
  'á²' => 'å²µ',
  'á³' => 'å²¢',
  'á´' => 'å²½',
  'áµ' => 'å²¬',
  'á¶' => 'å²«',
  'á·' => 'å²±',
  'á¸' => 'å²£',
  'á¹' => 'å³�',
  'áº' => 'å²·',
  'á»' => 'å³„',
  'á¼' => 'å³’',
  'á½' => 'å³¤',
  'á¾' => 'å³‹',
  'á¿' => 'å³¥',
  'áÀ' => 'å´‚',
  'áÁ' => 'å´ƒ',
  'áÂ' => 'å´§',
  'áÃ' => 'å´¦',
  'áÄ' => 'å´®',
  'áÅ' => 'å´¤',
  'áÆ' => 'å´ž',
  'áÇ' => 'å´†',
  'áÈ' => 'å´›',
  'áÉ' => 'åµ˜',
  'áÊ' => 'å´¾',
  'áË' => 'å´´',
  'áÌ' => 'å´½',
  'áÍ' => 'åµ¬',
  'áÎ' => 'åµ›',
  'áÏ' => 'åµ¯',
  'áÐ' => 'åµ�',
  'áÑ' => 'åµ«',
  'áÒ' => 'åµ‹',
  'áÓ' => 'åµŠ',
  'áÔ' => 'åµ©',
  'áÕ' => 'åµ´',
  'áÖ' => 'å¶‚',
  'á×' => 'å¶™',
  'áØ' => 'å¶�',
  'áÙ' => 'è±³',
  'áÚ' => 'å¶·',
  'áÛ' => 'å·…',
  'áÜ' => 'å½³',
  'áÝ' => 'å½·',
  'áÞ' => 'å¾‚',
  'áß' => 'å¾‡',
  'áà' => 'å¾‰',
  'áá' => 'å¾Œ',
  'áâ' => 'å¾•',
  'áã' => 'å¾™',
  'áä' => 'å¾œ',
  'áå' => 'å¾¨',
  'áæ' => 'å¾­',
  'áç' => 'å¾µ',
  'áè' => 'å¾¼',
  'áé' => 'è¡¢',
  'áê' => 'å½¡',
  'áë' => 'çŠ­',
  'áì' => 'çŠ°',
  'áí' => 'çŠ´',
  'áî' => 'çŠ·',
  'áï' => 'çŠ¸',
  'áð' => 'ç‹ƒ',
  'áñ' => 'ç‹�',
  'áò' => 'ç‹Ž',
  'áó' => 'ç‹�',
  'áô' => 'ç‹’',
  'áõ' => 'ç‹¨',
  'áö' => 'ç‹¯',
  'á÷' => 'ç‹©',
  'áø' => 'ç‹²',
  'áù' => 'ç‹´',
  'áú' => 'ç‹·',
  'áû' => 'çŒ�',
  'áü' => 'ç‹³',
  'áý' => 'çŒƒ',
  'áþ' => 'ç‹º',
  'â@' => 'é‡¦',
  'âA' => 'é‡§',
  'âB' => 'é‡¨',
  'âC' => 'é‡©',
  'âD' => 'é‡ª',
  'âE' => 'é‡«',
  'âF' => 'é‡¬',
  'âG' => 'é‡­',
  'âH' => 'é‡®',
  'âI' => 'é‡¯',
  'âJ' => 'é‡°',
  'âK' => 'é‡±',
  'âL' => 'é‡²',
  'âM' => 'é‡³',
  'âN' => 'é‡´',
  'âO' => 'é‡µ',
  'âP' => 'é‡¶',
  'âQ' => 'é‡·',
  'âR' => 'é‡¸',
  'âS' => 'é‡¹',
  'âT' => 'é‡º',
  'âU' => 'é‡»',
  'âV' => 'é‡¼',
  'âW' => 'é‡½',
  'âX' => 'é‡¾',
  'âY' => 'é‡¿',
  'âZ' => 'éˆ€',
  'â[' => 'éˆ�',
  'â\\' => 'éˆ‚',
  'â]' => 'éˆƒ',
  'â^' => 'éˆ„',
  'â_' => 'éˆ…',
  'â`' => 'éˆ†',
  'âa' => 'éˆ‡',
  'âb' => 'éˆˆ',
  'âc' => 'éˆ‰',
  'âd' => 'éˆŠ',
  'âe' => 'éˆ‹',
  'âf' => 'éˆŒ',
  'âg' => 'éˆ�',
  'âh' => 'éˆŽ',
  'âi' => 'éˆ�',
  'âj' => 'éˆ�',
  'âk' => 'éˆ‘',
  'âl' => 'éˆ’',
  'âm' => 'éˆ“',
  'ân' => 'éˆ”',
  'âo' => 'éˆ•',
  'âp' => 'éˆ–',
  'âq' => 'éˆ—',
  'âr' => 'éˆ˜',
  'âs' => 'éˆ™',
  'ât' => 'éˆš',
  'âu' => 'éˆ›',
  'âv' => 'éˆœ',
  'âw' => 'éˆ�',
  'âx' => 'éˆž',
  'ây' => 'éˆŸ',
  'âz' => 'éˆ ',
  'â{' => 'éˆ¡',
  'â|' => 'éˆ¢',
  'â}' => 'éˆ£',
  'â~' => 'éˆ¤',
  'â€' => 'éˆ¥',
  'â�' => 'éˆ¦',
  'â‚' => 'éˆ§',
  'âƒ' => 'éˆ¨',
  'â„' => 'éˆ©',
  'â…' => 'éˆª',
  'â†' => 'éˆ«',
  'â‡' => 'éˆ¬',
  'âˆ' => 'éˆ­',
  'â‰' => 'éˆ®',
  'âŠ' => 'éˆ¯',
  'â‹' => 'éˆ°',
  'âŒ' => 'éˆ±',
  'â�' => 'éˆ²',
  'âŽ' => 'éˆ³',
  'â�' => 'éˆ´',
  'â�' => 'éˆµ',
  'â‘' => 'éˆ¶',
  'â’' => 'éˆ·',
  'â“' => 'éˆ¸',
  'â”' => 'éˆ¹',
  'â•' => 'éˆº',
  'â–' => 'éˆ»',
  'â—' => 'éˆ¼',
  'â˜' => 'éˆ½',
  'â™' => 'éˆ¾',
  'âš' => 'éˆ¿',
  'â›' => 'é‰€',
  'âœ' => 'é‰�',
  'â�' => 'é‰‚',
  'âž' => 'é‰ƒ',
  'âŸ' => 'é‰„',
  'â ' => 'é‰…',
  'â¡' => 'ç‹»',
  'â¢' => 'çŒ—',
  'â£' => 'çŒ“',
  'â¤' => 'çŒ¡',
  'â¥' => 'çŒŠ',
  'â¦' => 'çŒž',
  'â§' => 'çŒ�',
  'â¨' => 'çŒ•',
  'â©' => 'çŒ¢',
  'âª' => 'çŒ¹',
  'â«' => 'çŒ¥',
  'â¬' => 'çŒ¬',
  'â­' => 'çŒ¸',
  'â®' => 'çŒ±',
  'â¯' => 'ç��',
  'â°' => 'ç��',
  'â±' => 'ç�—',
  'â²' => 'ç� ',
  'â³' => 'ç�¬',
  'â´' => 'ç�¯',
  'âµ' => 'ç�¾',
  'â¶' => 'èˆ›',
  'â·' => 'å¤¥',
  'â¸' => 'é£§',
  'â¹' => 'å¤¤',
  'âº' => 'å¤‚',
  'â»' => 'é¥£',
  'â¼' => 'é¥§',
  'â½' => 'é¥¨',
  'â¾' => 'é¥©',
  'â¿' => 'é¥ª',
  'âÀ' => 'é¥«',
  'âÁ' => 'é¥¬',
  'âÂ' => 'é¥´',
  'âÃ' => 'é¥·',
  'âÄ' => 'é¥½',
  'âÅ' => 'é¦€',
  'âÆ' => 'é¦„',
  'âÇ' => 'é¦‡',
  'âÈ' => 'é¦Š',
  'âÉ' => 'é¦�',
  'âÊ' => 'é¦�',
  'âË' => 'é¦‘',
  'âÌ' => 'é¦“',
  'âÍ' => 'é¦”',
  'âÎ' => 'é¦•',
  'âÏ' => 'åº€',
  'âÐ' => 'åº‘',
  'âÑ' => 'åº‹',
  'âÒ' => 'åº–',
  'âÓ' => 'åº¥',
  'âÔ' => 'åº ',
  'âÕ' => 'åº¹',
  'âÖ' => 'åºµ',
  'â×' => 'åº¾',
  'âØ' => 'åº³',
  'âÙ' => 'èµ“',
  'âÚ' => 'å»’',
  'âÛ' => 'å»‘',
  'âÜ' => 'å»›',
  'âÝ' => 'å»¨',
  'âÞ' => 'å»ª',
  'âß' => 'è†º',
  'âà' => 'å¿„',
  'âá' => 'å¿‰',
  'ââ' => 'å¿–',
  'âã' => 'å¿�',
  'âä' => 'æ€ƒ',
  'âå' => 'å¿®',
  'âæ' => 'æ€„',
  'âç' => 'å¿¡',
  'âè' => 'å¿¤',
  'âé' => 'å¿¾',
  'âê' => 'æ€…',
  'âë' => 'æ€†',
  'âì' => 'å¿ª',
  'âí' => 'å¿­',
  'âî' => 'å¿¸',
  'âï' => 'æ€™',
  'âð' => 'æ€µ',
  'âñ' => 'æ€¦',
  'âò' => 'æ€›',
  'âó' => 'æ€�',
  'âô' => 'æ€�',
  'âõ' => 'æ€©',
  'âö' => 'æ€«',
  'â÷' => 'æ€Š',
  'âø' => 'æ€¿',
  'âù' => 'æ€¡',
  'âú' => 'æ�¸',
  'âû' => 'æ�¹',
  'âü' => 'æ�»',
  'âý' => 'æ�º',
  'âþ' => 'æ�‚',
  'ã@' => 'é‰†',
  'ãA' => 'é‰‡',
  'ãB' => 'é‰ˆ',
  'ãC' => 'é‰‰',
  'ãD' => 'é‰Š',
  'ãE' => 'é‰‹',
  'ãF' => 'é‰Œ',
  'ãG' => 'é‰�',
  'ãH' => 'é‰Ž',
  'ãI' => 'é‰�',
  'ãJ' => 'é‰�',
  'ãK' => 'é‰‘',
  'ãL' => 'é‰’',
  'ãM' => 'é‰“',
  'ãN' => 'é‰”',
  'ãO' => 'é‰•',
  'ãP' => 'é‰–',
  'ãQ' => 'é‰—',
  'ãR' => 'é‰˜',
  'ãS' => 'é‰™',
  'ãT' => 'é‰š',
  'ãU' => 'é‰›',
  'ãV' => 'é‰œ',
  'ãW' => 'é‰�',
  'ãX' => 'é‰ž',
  'ãY' => 'é‰Ÿ',
  'ãZ' => 'é‰ ',
  'ã[' => 'é‰¡',
  'ã\\' => 'é‰¢',
  'ã]' => 'é‰£',
  'ã^' => 'é‰¤',
  'ã_' => 'é‰¥',
  'ã`' => 'é‰¦',
  'ãa' => 'é‰§',
  'ãb' => 'é‰¨',
  'ãc' => 'é‰©',
  'ãd' => 'é‰ª',
  'ãe' => 'é‰«',
  'ãf' => 'é‰¬',
  'ãg' => 'é‰­',
  'ãh' => 'é‰®',
  'ãi' => 'é‰¯',
  'ãj' => 'é‰°',
  'ãk' => 'é‰±',
  'ãl' => 'é‰²',
  'ãm' => 'é‰³',
  'ãn' => 'é‰µ',
  'ão' => 'é‰¶',
  'ãp' => 'é‰·',
  'ãq' => 'é‰¸',
  'ãr' => 'é‰¹',
  'ãs' => 'é‰º',
  'ãt' => 'é‰»',
  'ãu' => 'é‰¼',
  'ãv' => 'é‰½',
  'ãw' => 'é‰¾',
  'ãx' => 'é‰¿',
  'ãy' => 'éŠ€',
  'ãz' => 'éŠ�',
  'ã{' => 'éŠ‚',
  'ã|' => 'éŠƒ',
  'ã}' => 'éŠ„',
  'ã~' => 'éŠ…',
  'ã€' => 'éŠ†',
  'ã�' => 'éŠ‡',
  'ã‚' => 'éŠˆ',
  'ãƒ' => 'éŠ‰',
  'ã„' => 'éŠŠ',
  'ã…' => 'éŠ‹',
  'ã†' => 'éŠŒ',
  'ã‡' => 'éŠ�',
  'ãˆ' => 'éŠ�',
  'ã‰' => 'éŠ�',
  'ãŠ' => 'éŠ‘',
  'ã‹' => 'éŠ’',
  'ãŒ' => 'éŠ“',
  'ã�' => 'éŠ”',
  'ãŽ' => 'éŠ•',
  'ã�' => 'éŠ–',
  'ã�' => 'éŠ—',
  'ã‘' => 'éŠ˜',
  'ã’' => 'éŠ™',
  'ã“' => 'éŠš',
  'ã”' => 'éŠ›',
  'ã•' => 'éŠœ',
  'ã–' => 'éŠ�',
  'ã—' => 'éŠž',
  'ã˜' => 'éŠŸ',
  'ã™' => 'éŠ ',
  'ãš' => 'éŠ¡',
  'ã›' => 'éŠ¢',
  'ãœ' => 'éŠ£',
  'ã�' => 'éŠ¤',
  'ãž' => 'éŠ¥',
  'ãŸ' => 'éŠ¦',
  'ã ' => 'éŠ§',
  'ã¡' => 'æ�ª',
  'ã¢' => 'æ�½',
  'ã£' => 'æ‚–',
  'ã¤' => 'æ‚š',
  'ã¥' => 'æ‚­',
  'ã¦' => 'æ‚�',
  'ã§' => 'æ‚ƒ',
  'ã¨' => 'æ‚’',
  'ã©' => 'æ‚Œ',
  'ãª' => 'æ‚›',
  'ã«' => 'æƒ¬',
  'ã¬' => 'æ‚»',
  'ã­' => 'æ‚±',
  'ã®' => 'æƒ�',
  'ã¯' => 'æƒ˜',
  'ã°' => 'æƒ†',
  'ã±' => 'æƒš',
  'ã²' => 'æ‚´',
  'ã³' => 'æ„ ',
  'ã´' => 'æ„¦',
  'ãµ' => 'æ„•',
  'ã¶' => 'æ„£',
  'ã·' => 'æƒ´',
  'ã¸' => 'æ„€',
  'ã¹' => 'æ„Ž',
  'ãº' => 'æ„«',
  'ã»' => 'æ…Š',
  'ã¼' => 'æ…µ',
  'ã½' => 'æ†¬',
  'ã¾' => 'æ†”',
  'ã¿' => 'æ†§',
  'ãÀ' => 'æ†·',
  'ãÁ' => 'æ‡”',
  'ãÂ' => 'æ‡µ',
  'ãÃ' => 'å¿�',
  'ãÄ' => 'éš³',
  'ãÅ' => 'é—©',
  'ãÆ' => 'é—«',
  'ãÇ' => 'é—±',
  'ãÈ' => 'é—³',
  'ãÉ' => 'é—µ',
  'ãÊ' => 'é—¶',
  'ãË' => 'é—¼',
  'ãÌ' => 'é—¾',
  'ãÍ' => 'é˜ƒ',
  'ãÎ' => 'é˜„',
  'ãÏ' => 'é˜†',
  'ãÐ' => 'é˜ˆ',
  'ãÑ' => 'é˜Š',
  'ãÒ' => 'é˜‹',
  'ãÓ' => 'é˜Œ',
  'ãÔ' => 'é˜�',
  'ãÕ' => 'é˜�',
  'ãÖ' => 'é˜’',
  'ã×' => 'é˜•',
  'ãØ' => 'é˜–',
  'ãÙ' => 'é˜—',
  'ãÚ' => 'é˜™',
  'ãÛ' => 'é˜š',
  'ãÜ' => 'ä¸¬',
  'ãÝ' => 'çˆ¿',
  'ãÞ' => 'æˆ•',
  'ãß' => 'æ°µ',
  'ãà' => 'æ±”',
  'ãá' => 'æ±œ',
  'ãâ' => 'æ±Š',
  'ãã' => 'æ²£',
  'ãä' => 'æ²…',
  'ãå' => 'æ²�',
  'ãæ' => 'æ²”',
  'ãç' => 'æ²Œ',
  'ãè' => 'æ±¨',
  'ãé' => 'æ±©',
  'ãê' => 'æ±´',
  'ãë' => 'æ±¶',
  'ãì' => 'æ²†',
  'ãí' => 'æ²©',
  'ãî' => 'æ³�',
  'ãï' => 'æ³”',
  'ãð' => 'æ²­',
  'ãñ' => 'æ³·',
  'ãò' => 'æ³¸',
  'ãó' => 'æ³±',
  'ãô' => 'æ³—',
  'ãõ' => 'æ²²',
  'ãö' => 'æ³ ',
  'ã÷' => 'æ³–',
  'ãø' => 'æ³º',
  'ãù' => 'æ³«',
  'ãú' => 'æ³®',
  'ãû' => 'æ²±',
  'ãü' => 'æ³“',
  'ãý' => 'æ³¯',
  'ãþ' => 'æ³¾',
  'ä@' => 'éŠ¨',
  'äA' => 'éŠ©',
  'äB' => 'éŠª',
  'äC' => 'éŠ«',
  'äD' => 'éŠ¬',
  'äE' => 'éŠ­',
  'äF' => 'éŠ¯',
  'äG' => 'éŠ°',
  'äH' => 'éŠ±',
  'äI' => 'éŠ²',
  'äJ' => 'éŠ³',
  'äK' => 'éŠ´',
  'äL' => 'éŠµ',
  'äM' => 'éŠ¶',
  'äN' => 'éŠ·',
  'äO' => 'éŠ¸',
  'äP' => 'éŠ¹',
  'äQ' => 'éŠº',
  'äR' => 'éŠ»',
  'äS' => 'éŠ¼',
  'äT' => 'éŠ½',
  'äU' => 'éŠ¾',
  'äV' => 'éŠ¿',
  'äW' => 'é‹€',
  'äX' => 'é‹�',
  'äY' => 'é‹‚',
  'äZ' => 'é‹ƒ',
  'ä[' => 'é‹„',
  'ä\\' => 'é‹…',
  'ä]' => 'é‹†',
  'ä^' => 'é‹‡',
  'ä_' => 'é‹‰',
  'ä`' => 'é‹Š',
  'äa' => 'é‹‹',
  'äb' => 'é‹Œ',
  'äc' => 'é‹�',
  'äd' => 'é‹Ž',
  'äe' => 'é‹�',
  'äf' => 'é‹�',
  'äg' => 'é‹‘',
  'äh' => 'é‹’',
  'äi' => 'é‹“',
  'äj' => 'é‹”',
  'äk' => 'é‹•',
  'äl' => 'é‹–',
  'äm' => 'é‹—',
  'än' => 'é‹˜',
  'äo' => 'é‹™',
  'äp' => 'é‹š',
  'äq' => 'é‹›',
  'är' => 'é‹œ',
  'äs' => 'é‹�',
  'ät' => 'é‹ž',
  'äu' => 'é‹Ÿ',
  'äv' => 'é‹ ',
  'äw' => 'é‹¡',
  'äx' => 'é‹¢',
  'äy' => 'é‹£',
  'äz' => 'é‹¤',
  'ä{' => 'é‹¥',
  'ä|' => 'é‹¦',
  'ä}' => 'é‹§',
  'ä~' => 'é‹¨',
  'ä€' => 'é‹©',
  'ä�' => 'é‹ª',
  'ä‚' => 'é‹«',
  'äƒ' => 'é‹¬',
  'ä„' => 'é‹­',
  'ä…' => 'é‹®',
  'ä†' => 'é‹¯',
  'ä‡' => 'é‹°',
  'äˆ' => 'é‹±',
  'ä‰' => 'é‹²',
  'äŠ' => 'é‹³',
  'ä‹' => 'é‹´',
  'äŒ' => 'é‹µ',
  'ä�' => 'é‹¶',
  'äŽ' => 'é‹·',
  'ä�' => 'é‹¸',
  'ä�' => 'é‹¹',
  'ä‘' => 'é‹º',
  'ä’' => 'é‹»',
  'ä“' => 'é‹¼',
  'ä”' => 'é‹½',
  'ä•' => 'é‹¾',
  'ä–' => 'é‹¿',
  'ä—' => 'éŒ€',
  'ä˜' => 'éŒ�',
  'ä™' => 'éŒ‚',
  'äš' => 'éŒƒ',
  'ä›' => 'éŒ„',
  'äœ' => 'éŒ…',
  'ä�' => 'éŒ†',
  'äž' => 'éŒ‡',
  'äŸ' => 'éŒˆ',
  'ä ' => 'éŒ‰',
  'ä¡' => 'æ´¹',
  'ä¢' => 'æ´§',
  'ä£' => 'æ´Œ',
  'ä¤' => 'æµƒ',
  'ä¥' => 'æµˆ',
  'ä¦' => 'æ´‡',
  'ä§' => 'æ´„',
  'ä¨' => 'æ´™',
  'ä©' => 'æ´Ž',
  'äª' => 'æ´«',
  'ä«' => 'æµ�',
  'ä¬' => 'æ´®',
  'ä­' => 'æ´µ',
  'ä®' => 'æ´š',
  'ä¯' => 'æµ�',
  'ä°' => 'æµ’',
  'ä±' => 'æµ”',
  'ä²' => 'æ´³',
  'ä³' => 'æ¶‘',
  'ä´' => 'æµ¯',
  'äµ' => 'æ¶ž',
  'ä¶' => 'æ¶ ',
  'ä·' => 'æµž',
  'ä¸' => 'æ¶“',
  'ä¹' => 'æ¶”',
  'äº' => 'æµœ',
  'ä»' => 'æµ ',
  'ä¼' => 'æµ¼',
  'ä½' => 'æµ£',
  'ä¾' => 'æ¸š',
  'ä¿' => 'æ·‡',
  'äÀ' => 'æ·…',
  'äÁ' => 'æ·ž',
  'äÂ' => 'æ¸Ž',
  'äÃ' => 'æ¶¿',
  'äÄ' => 'æ· ',
  'äÅ' => 'æ¸‘',
  'äÆ' => 'æ·¦',
  'äÇ' => 'æ·�',
  'äÈ' => 'æ·™',
  'äÉ' => 'æ¸–',
  'äÊ' => 'æ¶«',
  'äË' => 'æ¸Œ',
  'äÌ' => 'æ¶®',
  'äÍ' => 'æ¸«',
  'äÎ' => 'æ¹®',
  'äÏ' => 'æ¹Ž',
  'äÐ' => 'æ¹«',
  'äÑ' => 'æº²',
  'äÒ' => 'æ¹Ÿ',
  'äÓ' => 'æº†',
  'äÔ' => 'æ¹“',
  'äÕ' => 'æ¹”',
  'äÖ' => 'æ¸²',
  'ä×' => 'æ¸¥',
  'äØ' => 'æ¹„',
  'äÙ' => 'æ»Ÿ',
  'äÚ' => 'æº±',
  'äÛ' => 'æº˜',
  'äÜ' => 'æ» ',
  'äÝ' => 'æ¼­',
  'äÞ' => 'æ»¢',
  'äß' => 'æº¥',
  'äà' => 'æº§',
  'äá' => 'æº½',
  'äâ' => 'æº»',
  'äã' => 'æº·',
  'ää' => 'æ»—',
  'äå' => 'æº´',
  'äæ' => 'æ»�',
  'äç' => 'æº�',
  'äè' => 'æ»‚',
  'äé' => 'æºŸ',
  'äê' => 'æ½¢',
  'äë' => 'æ½†',
  'äì' => 'æ½‡',
  'äí' => 'æ¼¤',
  'äî' => 'æ¼•',
  'äï' => 'æ»¹',
  'äð' => 'æ¼¯',
  'äñ' => 'æ¼¶',
  'äò' => 'æ½‹',
  'äó' => 'æ½´',
  'äô' => 'æ¼ª',
  'äõ' => 'æ¼‰',
  'äö' => 'æ¼©',
  'ä÷' => 'æ¾‰',
  'äø' => 'æ¾�',
  'äù' => 'æ¾Œ',
  'äú' => 'æ½¸',
  'äû' => 'æ½²',
  'äü' => 'æ½¼',
  'äý' => 'æ½º',
  'äþ' => 'æ¿‘',
  'å@' => 'éŒŠ',
  'åA' => 'éŒ‹',
  'åB' => 'éŒŒ',
  'åC' => 'éŒ�',
  'åD' => 'éŒŽ',
  'åE' => 'éŒ�',
  'åF' => 'éŒ�',
  'åG' => 'éŒ‘',
  'åH' => 'éŒ’',
  'åI' => 'éŒ“',
  'åJ' => 'éŒ”',
  'åK' => 'éŒ•',
  'åL' => 'éŒ–',
  'åM' => 'éŒ—',
  'åN' => 'éŒ˜',
  'åO' => 'éŒ™',
  'åP' => 'éŒš',
  'åQ' => 'éŒ›',
  'åR' => 'éŒœ',
  'åS' => 'éŒ�',
  'åT' => 'éŒž',
  'åU' => 'éŒŸ',
  'åV' => 'éŒ ',
  'åW' => 'éŒ¡',
  'åX' => 'éŒ¢',
  'åY' => 'éŒ£',
  'åZ' => 'éŒ¤',
  'å[' => 'éŒ¥',
  'å\\' => 'éŒ¦',
  'å]' => 'éŒ§',
  'å^' => 'éŒ¨',
  'å_' => 'éŒ©',
  'å`' => 'éŒª',
  'åa' => 'éŒ«',
  'åb' => 'éŒ¬',
  'åc' => 'éŒ­',
  'åd' => 'éŒ®',
  'åe' => 'éŒ¯',
  'åf' => 'éŒ°',
  'åg' => 'éŒ±',
  'åh' => 'éŒ²',
  'åi' => 'éŒ³',
  'åj' => 'éŒ´',
  'åk' => 'éŒµ',
  'ål' => 'éŒ¶',
  'åm' => 'éŒ·',
  'ån' => 'éŒ¸',
  'åo' => 'éŒ¹',
  'åp' => 'éŒº',
  'åq' => 'éŒ»',
  'år' => 'éŒ¼',
  'ås' => 'éŒ½',
  'åt' => 'éŒ¿',
  'åu' => 'é�€',
  'åv' => 'é��',
  'åw' => 'é�‚',
  'åx' => 'é�ƒ',
  'åy' => 'é�„',
  'åz' => 'é�…',
  'å{' => 'é�†',
  'å|' => 'é�‡',
  'å}' => 'é�ˆ',
  'å~' => 'é�‰',
  'å€' => 'é�Š',
  'å�' => 'é�‹',
  'å‚' => 'é�Œ',
  'åƒ' => 'é��',
  'å„' => 'é�Ž',
  'å…' => 'é��',
  'å†' => 'é��',
  'å‡' => 'é�‘',
  'åˆ' => 'é�’',
  'å‰' => 'é�“',
  'åŠ' => 'é�”',
  'å‹' => 'é�•',
  'åŒ' => 'é�–',
  'å�' => 'é�—',
  'åŽ' => 'é�˜',
  'å�' => 'é�™',
  'å�' => 'é�š',
  'å‘' => 'é�›',
  'å’' => 'é�œ',
  'å“' => 'é��',
  'å”' => 'é�ž',
  'å•' => 'é�Ÿ',
  'å–' => 'é� ',
  'å—' => 'é�¡',
  'å˜' => 'é�¢',
  'å™' => 'é�£',
  'åš' => 'é�¤',
  'å›' => 'é�¥',
  'åœ' => 'é�¦',
  'å�' => 'é�§',
  'åž' => 'é�¨',
  'åŸ' => 'é�©',
  'å ' => 'é�«',
  'å¡' => 'æ¿‰',
  'å¢' => 'æ¾§',
  'å£' => 'æ¾¹',
  'å¤' => 'æ¾¶',
  'å¥' => 'æ¿‚',
  'å¦' => 'æ¿¡',
  'å§' => 'æ¿®',
  'å¨' => 'æ¿ž',
  'å©' => 'æ¿ ',
  'åª' => 'æ¿¯',
  'å«' => 'ç€š',
  'å¬' => 'ç€£',
  'å­' => 'ç€›',
  'å®' => 'ç€¹',
  'å¯' => 'ç€µ',
  'å°' => 'ç��',
  'å±' => 'ç�ž',
  'å²' => 'å®€',
  'å³' => 'å®„',
  'å´' => 'å®•',
  'åµ' => 'å®“',
  'å¶' => 'å®¥',
  'å·' => 'å®¸',
  'å¸' => 'ç”¯',
  'å¹' => 'éªž',
  'åº' => 'æ�´',
  'å»' => 'å¯¤',
  'å¼' => 'å¯®',
  'å½' => 'è¤°',
  'å¾' => 'å¯°',
  'å¿' => 'è¹‡',
  'åÀ' => 'è¬‡',
  'åÁ' => 'è¾¶',
  'åÂ' => 'è¿“',
  'åÃ' => 'è¿•',
  'åÄ' => 'è¿¥',
  'åÅ' => 'è¿®',
  'åÆ' => 'è¿¤',
  'åÇ' => 'è¿©',
  'åÈ' => 'è¿¦',
  'åÉ' => 'è¿³',
  'åÊ' => 'è¿¨',
  'åË' => 'é€…',
  'åÌ' => 'é€„',
  'åÍ' => 'é€‹',
  'åÎ' => 'é€¦',
  'åÏ' => 'é€‘',
  'åÐ' => 'é€�',
  'åÑ' => 'é€–',
  'åÒ' => 'é€¡',
  'åÓ' => 'é€µ',
  'åÔ' => 'é€¶',
  'åÕ' => 'é€­',
  'åÖ' => 'é€¯',
  'å×' => 'é�„',
  'åØ' => 'é�‘',
  'åÙ' => 'é�’',
  'åÚ' => 'é��',
  'åÛ' => 'é�¨',
  'åÜ' => 'é�˜',
  'åÝ' => 'é�¢',
  'åÞ' => 'é�›',
  'åß' => 'æš¹',
  'åà' => 'é�´',
  'åá' => 'é�½',
  'åâ' => 'é‚‚',
  'åã' => 'é‚ˆ',
  'åä' => 'é‚ƒ',
  'åå' => 'é‚‹',
  'åæ' => 'å½�',
  'åç' => 'å½—',
  'åè' => 'å½–',
  'åé' => 'å½˜',
  'åê' => 'å°»',
  'åë' => 'å’«',
  'åì' => 'å±�',
  'åí' => 'å±™',
  'åî' => 'å­±',
  'åï' => 'å±£',
  'åð' => 'å±¦',
  'åñ' => 'ç¾¼',
  'åò' => 'å¼ª',
  'åó' => 'å¼©',
  'åô' => 'å¼­',
  'åõ' => 'è‰´',
  'åö' => 'å¼¼',
  'å÷' => 'é¬»',
  'åø' => 'å±®',
  'åù' => 'å¦�',
  'åú' => 'å¦ƒ',
  'åû' => 'å¦�',
  'åü' => 'å¦©',
  'åý' => 'å¦ª',
  'åþ' => 'å¦£',
  'æ@' => 'é�¬',
  'æA' => 'é�­',
  'æB' => 'é�®',
  'æC' => 'é�¯',
  'æD' => 'é�°',
  'æE' => 'é�±',
  'æF' => 'é�²',
  'æG' => 'é�³',
  'æH' => 'é�´',
  'æI' => 'é�µ',
  'æJ' => 'é�¶',
  'æK' => 'é�·',
  'æL' => 'é�¸',
  'æM' => 'é�¹',
  'æN' => 'é�º',
  'æO' => 'é�»',
  'æP' => 'é�¼',
  'æQ' => 'é�½',
  'æR' => 'é�¾',
  'æS' => 'é�¿',
  'æT' => 'éŽ€',
  'æU' => 'éŽ�',
  'æV' => 'éŽ‚',
  'æW' => 'éŽƒ',
  'æX' => 'éŽ„',
  'æY' => 'éŽ…',
  'æZ' => 'éŽ†',
  'æ[' => 'éŽ‡',
  'æ\\' => 'éŽˆ',
  'æ]' => 'éŽ‰',
  'æ^' => 'éŽŠ',
  'æ_' => 'éŽ‹',
  'æ`' => 'éŽŒ',
  'æa' => 'éŽ�',
  'æb' => 'éŽŽ',
  'æc' => 'éŽ�',
  'æd' => 'éŽ‘',
  'æe' => 'éŽ’',
  'æf' => 'éŽ“',
  'æg' => 'éŽ”',
  'æh' => 'éŽ•',
  'æi' => 'éŽ–',
  'æj' => 'éŽ—',
  'æk' => 'éŽ˜',
  'æl' => 'éŽ™',
  'æm' => 'éŽš',
  'æn' => 'éŽ›',
  'æo' => 'éŽœ',
  'æp' => 'éŽ�',
  'æq' => 'éŽž',
  'ær' => 'éŽŸ',
  'æs' => 'éŽ ',
  'æt' => 'éŽ¡',
  'æu' => 'éŽ¢',
  'æv' => 'éŽ£',
  'æw' => 'éŽ¤',
  'æx' => 'éŽ¥',
  'æy' => 'éŽ¦',
  'æz' => 'éŽ§',
  'æ{' => 'éŽ¨',
  'æ|' => 'éŽ©',
  'æ}' => 'éŽª',
  'æ~' => 'éŽ«',
  'æ€' => 'éŽ¬',
  'æ�' => 'éŽ­',
  'æ‚' => 'éŽ®',
  'æƒ' => 'éŽ¯',
  'æ„' => 'éŽ°',
  'æ…' => 'éŽ±',
  'æ†' => 'éŽ²',
  'æ‡' => 'éŽ³',
  'æˆ' => 'éŽ´',
  'æ‰' => 'éŽµ',
  'æŠ' => 'éŽ¶',
  'æ‹' => 'éŽ·',
  'æŒ' => 'éŽ¸',
  'æ�' => 'éŽ¹',
  'æŽ' => 'éŽº',
  'æ�' => 'éŽ»',
  'æ�' => 'éŽ¼',
  'æ‘' => 'éŽ½',
  'æ’' => 'éŽ¾',
  'æ“' => 'éŽ¿',
  'æ”' => 'é�€',
  'æ•' => 'é��',
  'æ–' => 'é�‚',
  'æ—' => 'é�ƒ',
  'æ˜' => 'é�„',
  'æ™' => 'é�…',
  'æš' => 'é�†',
  'æ›' => 'é�‡',
  'æœ' => 'é�ˆ',
  'æ�' => 'é�‰',
  'æž' => 'é�‹',
  'æŸ' => 'é�Œ',
  'æ ' => 'é��',
  'æ¡' => 'å¦—',
  'æ¢' => 'å§Š',
  'æ£' => 'å¦«',
  'æ¤' => 'å¦ž',
  'æ¥' => 'å¦¤',
  'æ¦' => 'å§’',
  'æ§' => 'å¦²',
  'æ¨' => 'å¦¯',
  'æ©' => 'å§—',
  'æª' => 'å¦¾',
  'æ«' => 'å¨…',
  'æ¬' => 'å¨†',
  'æ­' => 'å§�',
  'æ®' => 'å¨ˆ',
  'æ¯' => 'å§£',
  'æ°' => 'å§˜',
  'æ±' => 'å§¹',
  'æ²' => 'å¨Œ',
  'æ³' => 'å¨‰',
  'æ´' => 'å¨²',
  'æµ' => 'å¨´',
  'æ¶' => 'å¨‘',
  'æ·' => 'å¨£',
  'æ¸' => 'å¨“',
  'æ¹' => 'å©€',
  'æº' => 'å©§',
  'æ»' => 'å©Š',
  'æ¼' => 'å©•',
  'æ½' => 'å¨¼',
  'æ¾' => 'å©¢',
  'æ¿' => 'å©µ',
  'æÀ' => 'èƒ¬',
  'æÁ' => 'åªª',
  'æÂ' => 'åª›',
  'æÃ' => 'å©·',
  'æÄ' => 'å©º',
  'æÅ' => 'åª¾',
  'æÆ' => 'å««',
  'æÇ' => 'åª²',
  'æÈ' => 'å«’',
  'æÉ' => 'å«”',
  'æÊ' => 'åª¸',
  'æË' => 'å« ',
  'æÌ' => 'å«£',
  'æÍ' => 'å«±',
  'æÎ' => 'å«–',
  'æÏ' => 'å«¦',
  'æÐ' => 'å«˜',
  'æÑ' => 'å«œ',
  'æÒ' => 'å¬‰',
  'æÓ' => 'å¬—',
  'æÔ' => 'å¬–',
  'æÕ' => 'å¬²',
  'æÖ' => 'å¬·',
  'æ×' => 'å­€',
  'æØ' => 'å°•',
  'æÙ' => 'å°œ',
  'æÚ' => 'å­š',
  'æÛ' => 'å­¥',
  'æÜ' => 'å­³',
  'æÝ' => 'å­‘',
  'æÞ' => 'å­“',
  'æß' => 'å­¢',
  'æà' => 'é©µ',
  'æá' => 'é©·',
  'æâ' => 'é©¸',
  'æã' => 'é©º',
  'æä' => 'é©¿',
  'æå' => 'é©½',
  'ææ' => 'éª€',
  'æç' => 'éª�',
  'æè' => 'éª…',
  'æé' => 'éªˆ',
  'æê' => 'éªŠ',
  'æë' => 'éª�',
  'æì' => 'éª’',
  'æí' => 'éª“',
  'æî' => 'éª–',
  'æï' => 'éª˜',
  'æð' => 'éª›',
  'æñ' => 'éªœ',
  'æò' => 'éª�',
  'æó' => 'éªŸ',
  'æô' => 'éª ',
  'æõ' => 'éª¢',
  'æö' => 'éª£',
  'æ÷' => 'éª¥',
  'æø' => 'éª§',
  'æù' => 'çºŸ',
  'æú' => 'çº¡',
  'æû' => 'çº£',
  'æü' => 'çº¥',
  'æý' => 'çº¨',
  'æþ' => 'çº©',
  'ç@' => 'é�Ž',
  'çA' => 'é��',
  'çB' => 'é��',
  'çC' => 'é�‘',
  'çD' => 'é�’',
  'çE' => 'é�“',
  'çF' => 'é�”',
  'çG' => 'é�•',
  'çH' => 'é�—',
  'çI' => 'é�˜',
  'çJ' => 'é�™',
  'çK' => 'é�š',
  'çL' => 'é�›',
  'çM' => 'é�œ',
  'çN' => 'é��',
  'çO' => 'é�ž',
  'çP' => 'é�Ÿ',
  'çQ' => 'é� ',
  'çR' => 'é�¡',
  'çS' => 'é�¢',
  'çT' => 'é�£',
  'çU' => 'é�¤',
  'çV' => 'é�¥',
  'çW' => 'é�¦',
  'çX' => 'é�§',
  'çY' => 'é�¨',
  'çZ' => 'é�©',
  'ç[' => 'é�ª',
  'ç\\' => 'é�«',
  'ç]' => 'é�¬',
  'ç^' => 'é�­',
  'ç_' => 'é�®',
  'ç`' => 'é�¯',
  'ça' => 'é�°',
  'çb' => 'é�±',
  'çc' => 'é�²',
  'çd' => 'é�³',
  'çe' => 'é�´',
  'çf' => 'é�µ',
  'çg' => 'é�¶',
  'çh' => 'é�·',
  'çi' => 'é�¸',
  'çj' => 'é�¹',
  'çk' => 'é�º',
  'çl' => 'é�»',
  'çm' => 'é�¼',
  'çn' => 'é�½',
  'ço' => 'é�¾',
  'çp' => 'é�¿',
  'çq' => 'é�€',
  'çr' => 'é��',
  'çs' => 'é�‚',
  'çt' => 'é�ƒ',
  'çu' => 'é�„',
  'çv' => 'é�…',
  'çw' => 'é�†',
  'çx' => 'é�‡',
  'çy' => 'é�ˆ',
  'çz' => 'é�‰',
  'ç{' => 'é�Š',
  'ç|' => 'é�‹',
  'ç}' => 'é�Œ',
  'ç~' => 'é��',
  'ç€' => 'é�Ž',
  'ç�' => 'é��',
  'ç‚' => 'é��',
  'çƒ' => 'é�‘',
  'ç„' => 'é�’',
  'ç…' => 'é�“',
  'ç†' => 'é�”',
  'ç‡' => 'é�•',
  'çˆ' => 'é�–',
  'ç‰' => 'é�—',
  'çŠ' => 'é�˜',
  'ç‹' => 'é�™',
  'çŒ' => 'é�š',
  'ç�' => 'é�›',
  'çŽ' => 'é�œ',
  'ç�' => 'é��',
  'ç�' => 'é�ž',
  'ç‘' => 'é�Ÿ',
  'ç’' => 'é� ',
  'ç“' => 'é�¡',
  'ç”' => 'é�¢',
  'ç•' => 'é�£',
  'ç–' => 'é�¤',
  'ç—' => 'é�¥',
  'ç˜' => 'é�¦',
  'ç™' => 'é�§',
  'çš' => 'é�¨',
  'ç›' => 'é�©',
  'çœ' => 'é�ª',
  'ç�' => 'é�«',
  'çž' => 'é�¬',
  'çŸ' => 'é�­',
  'ç ' => 'é�®',
  'ç¡' => 'çº­',
  'ç¢' => 'çº°',
  'ç£' => 'çº¾',
  'ç¤' => 'ç»€',
  'ç¥' => 'ç»�',
  'ç¦' => 'ç»‚',
  'ç§' => 'ç»‰',
  'ç¨' => 'ç»‹',
  'ç©' => 'ç»Œ',
  'çª' => 'ç»�',
  'ç«' => 'ç»”',
  'ç¬' => 'ç»—',
  'ç­' => 'ç»›',
  'ç®' => 'ç» ',
  'ç¯' => 'ç»¡',
  'ç°' => 'ç»¨',
  'ç±' => 'ç»«',
  'ç²' => 'ç»®',
  'ç³' => 'ç»¯',
  'ç´' => 'ç»±',
  'çµ' => 'ç»²',
  'ç¶' => 'ç¼�',
  'ç·' => 'ç»¶',
  'ç¸' => 'ç»º',
  'ç¹' => 'ç»»',
  'çº' => 'ç»¾',
  'ç»' => 'ç¼�',
  'ç¼' => 'ç¼‚',
  'ç½' => 'ç¼ƒ',
  'ç¾' => 'ç¼‡',
  'ç¿' => 'ç¼ˆ',
  'çÀ' => 'ç¼‹',
  'çÁ' => 'ç¼Œ',
  'çÂ' => 'ç¼�',
  'çÃ' => 'ç¼‘',
  'çÄ' => 'ç¼’',
  'çÅ' => 'ç¼—',
  'çÆ' => 'ç¼™',
  'çÇ' => 'ç¼œ',
  'çÈ' => 'ç¼›',
  'çÉ' => 'ç¼Ÿ',
  'çÊ' => 'ç¼¡',
  'çË' => 'ç¼¢',
  'çÌ' => 'ç¼£',
  'çÍ' => 'ç¼¤',
  'çÎ' => 'ç¼¥',
  'çÏ' => 'ç¼¦',
  'çÐ' => 'ç¼§',
  'çÑ' => 'ç¼ª',
  'çÒ' => 'ç¼«',
  'çÓ' => 'ç¼¬',
  'çÔ' => 'ç¼­',
  'çÕ' => 'ç¼¯',
  'çÖ' => 'ç¼°',
  'ç×' => 'ç¼±',
  'çØ' => 'ç¼²',
  'çÙ' => 'ç¼³',
  'çÚ' => 'ç¼µ',
  'çÛ' => 'å¹º',
  'çÜ' => 'ç•¿',
  'çÝ' => 'å·›',
  'çÞ' => 'ç”¾',
  'çß' => 'é‚•',
  'çà' => 'çŽŽ',
  'çá' => 'çŽ‘',
  'çâ' => 'çŽ®',
  'çã' => 'çŽ¢',
  'çä' => 'çŽŸ',
  'çå' => 'ç��',
  'çæ' => 'ç�‚',
  'çç' => 'ç�‘',
  'çè' => 'çŽ·',
  'çé' => 'çŽ³',
  'çê' => 'ç�€',
  'çë' => 'ç�‰',
  'çì' => 'ç�ˆ',
  'çí' => 'ç�¥',
  'çî' => 'ç�™',
  'çï' => 'é¡¼',
  'çð' => 'ç�Š',
  'çñ' => 'ç�©',
  'çò' => 'ç�§',
  'çó' => 'ç�ž',
  'çô' => 'çŽº',
  'çõ' => 'ç�²',
  'çö' => 'ç��',
  'ç÷' => 'ç�ª',
  'çø' => 'ç‘›',
  'çù' => 'ç�¦',
  'çú' => 'ç�¥',
  'çû' => 'ç�¨',
  'çü' => 'ç�°',
  'çý' => 'ç�®',
  'çþ' => 'ç�¬',
  'è@' => 'é�¯',
  'èA' => 'é�°',
  'èB' => 'é�±',
  'èC' => 'é�²',
  'èD' => 'é�³',
  'èE' => 'é�´',
  'èF' => 'é�µ',
  'èG' => 'é�¶',
  'èH' => 'é�·',
  'èI' => 'é�¸',
  'èJ' => 'é�¹',
  'èK' => 'é�º',
  'èL' => 'é�»',
  'èM' => 'é�¼',
  'èN' => 'é�½',
  'èO' => 'é�¿',
  'èP' => 'é‘€',
  'èQ' => 'é‘�',
  'èR' => 'é‘‚',
  'èS' => 'é‘ƒ',
  'èT' => 'é‘„',
  'èU' => 'é‘…',
  'èV' => 'é‘†',
  'èW' => 'é‘‡',
  'èX' => 'é‘ˆ',
  'èY' => 'é‘‰',
  'èZ' => 'é‘Š',
  'è[' => 'é‘‹',
  'è\\' => 'é‘Œ',
  'è]' => 'é‘�',
  'è^' => 'é‘Ž',
  'è_' => 'é‘�',
  'è`' => 'é‘�',
  'èa' => 'é‘‘',
  'èb' => 'é‘’',
  'èc' => 'é‘“',
  'èd' => 'é‘”',
  'èe' => 'é‘•',
  'èf' => 'é‘–',
  'èg' => 'é‘—',
  'èh' => 'é‘˜',
  'èi' => 'é‘™',
  'èj' => 'é‘š',
  'èk' => 'é‘›',
  'èl' => 'é‘œ',
  'èm' => 'é‘�',
  'èn' => 'é‘ž',
  'èo' => 'é‘Ÿ',
  'èp' => 'é‘ ',
  'èq' => 'é‘¡',
  'èr' => 'é‘¢',
  'ès' => 'é‘£',
  'èt' => 'é‘¤',
  'èu' => 'é‘¥',
  'èv' => 'é‘¦',
  'èw' => 'é‘§',
  'èx' => 'é‘¨',
  'èy' => 'é‘©',
  'èz' => 'é‘ª',
  'è{' => 'é‘¬',
  'è|' => 'é‘­',
  'è}' => 'é‘®',
  'è~' => 'é‘¯',
  'è€' => 'é‘°',
  'è�' => 'é‘±',
  'è‚' => 'é‘²',
  'èƒ' => 'é‘³',
  'è„' => 'é‘´',
  'è…' => 'é‘µ',
  'è†' => 'é‘¶',
  'è‡' => 'é‘·',
  'èˆ' => 'é‘¸',
  'è‰' => 'é‘¹',
  'èŠ' => 'é‘º',
  'è‹' => 'é‘»',
  'èŒ' => 'é‘¼',
  'è�' => 'é‘½',
  'èŽ' => 'é‘¾',
  'è�' => 'é‘¿',
  'è�' => 'é’€',
  'è‘' => 'é’�',
  'è’' => 'é’‚',
  'è“' => 'é’ƒ',
  'è”' => 'é’„',
  'è•' => 'é’‘',
  'è–' => 'é’–',
  'è—' => 'é’˜',
  'è˜' => 'é“‡',
  'è™' => 'é“�',
  'èš' => 'é““',
  'è›' => 'é“”',
  'èœ' => 'é“š',
  'è�' => 'é“¦',
  'èž' => 'é“»',
  'èŸ' => 'é”œ',
  'è ' => 'é” ',
  'è¡' => 'ç�›',
  'è¢' => 'ç�š',
  'è£' => 'ç‘�',
  'è¤' => 'ç‘œ',
  'è¥' => 'ç‘—',
  'è¦' => 'ç‘•',
  'è§' => 'ç‘™',
  'è¨' => 'ç‘·',
  'è©' => 'ç‘­',
  'èª' => 'ç‘¾',
  'è«' => 'ç’œ',
  'è¬' => 'ç’Ž',
  'è­' => 'ç’€',
  'è®' => 'ç’�',
  'è¯' => 'ç’‡',
  'è°' => 'ç’‹',
  'è±' => 'ç’ž',
  'è²' => 'ç’¨',
  'è³' => 'ç’©',
  'è´' => 'ç’�',
  'èµ' => 'ç’§',
  'è¶' => 'ç“’',
  'è·' => 'ç’º',
  'è¸' => 'éŸª',
  'è¹' => 'éŸ«',
  'èº' => 'éŸ¬',
  'è»' => 'æ�Œ',
  'è¼' => 'æ�“',
  'è½' => 'æ�ž',
  'è¾' => 'æ�ˆ',
  'è¿' => 'æ�©',
  'èÀ' => 'æž¥',
  'èÁ' => 'æž‡',
  'èÂ' => 'æ�ª',
  'èÃ' => 'æ�³',
  'èÄ' => 'æž˜',
  'èÅ' => 'æž§',
  'èÆ' => 'æ�µ',
  'èÇ' => 'æž¨',
  'èÈ' => 'æžž',
  'èÉ' => 'æž­',
  'èÊ' => 'æž‹',
  'èË' => 'æ�·',
  'èÌ' => 'æ�¼',
  'èÍ' => 'æŸ°',
  'èÎ' => 'æ ‰',
  'èÏ' => 'æŸ˜',
  'èÐ' => 'æ Š',
  'èÑ' => 'æŸ©',
  'èÒ' => 'æž°',
  'èÓ' => 'æ Œ',
  'èÔ' => 'æŸ™',
  'èÕ' => 'æžµ',
  'èÖ' => 'æŸš',
  'è×' => 'æž³',
  'èØ' => 'æŸ�',
  'èÙ' => 'æ €',
  'èÚ' => 'æŸƒ',
  'èÛ' => 'æž¸',
  'èÜ' => 'æŸ¢',
  'èÝ' => 'æ Ž',
  'èÞ' => 'æŸ�',
  'èß' => 'æŸ½',
  'èà' => 'æ ²',
  'èá' => 'æ ³',
  'èâ' => 'æ¡ ',
  'èã' => 'æ¡¡',
  'èä' => 'æ¡Ž',
  'èå' => 'æ¡¢',
  'èæ' => 'æ¡„',
  'èç' => 'æ¡¤',
  'èè' => 'æ¢ƒ',
  'èé' => 'æ �',
  'èê' => 'æ¡•',
  'èë' => 'æ¡¦',
  'èì' => 'æ¡�',
  'èí' => 'æ¡§',
  'èî' => 'æ¡€',
  'èï' => 'æ ¾',
  'èð' => 'æ¡Š',
  'èñ' => 'æ¡‰',
  'èò' => 'æ ©',
  'èó' => 'æ¢µ',
  'èô' => 'æ¢�',
  'èõ' => 'æ¡´',
  'èö' => 'æ¡·',
  'è÷' => 'æ¢“',
  'èø' => 'æ¡«',
  'èù' => 'æ£‚',
  'èú' => 'æ¥®',
  'èû' => 'æ£¼',
  'èü' => 'æ¤Ÿ',
  'èý' => 'æ¤ ',
  'èþ' => 'æ£¹',
  'é@' => 'é”§',
  'éA' => 'é”³',
  'éB' => 'é”½',
  'éC' => 'é•ƒ',
  'éD' => 'é•ˆ',
  'éE' => 'é•‹',
  'éF' => 'é••',
  'éG' => 'é•š',
  'éH' => 'é• ',
  'éI' => 'é•®',
  'éJ' => 'é•´',
  'éK' => 'é•µ',
  'éL' => 'é•·',
  'éM' => 'é•¸',
  'éN' => 'é•¹',
  'éO' => 'é•º',
  'éP' => 'é•»',
  'éQ' => 'é•¼',
  'éR' => 'é•½',
  'éS' => 'é•¾',
  'éT' => 'é–€',
  'éU' => 'é–�',
  'éV' => 'é–‚',
  'éW' => 'é–ƒ',
  'éX' => 'é–„',
  'éY' => 'é–…',
  'éZ' => 'é–†',
  'é[' => 'é–‡',
  'é\\' => 'é–ˆ',
  'é]' => 'é–‰',
  'é^' => 'é–Š',
  'é_' => 'é–‹',
  'é`' => 'é–Œ',
  'éa' => 'é–�',
  'éb' => 'é–Ž',
  'éc' => 'é–�',
  'éd' => 'é–�',
  'ée' => 'é–‘',
  'éf' => 'é–’',
  'ég' => 'é–“',
  'éh' => 'é–”',
  'éi' => 'é–•',
  'éj' => 'é––',
  'ék' => 'é–—',
  'él' => 'é–˜',
  'ém' => 'é–™',
  'én' => 'é–š',
  'éo' => 'é–›',
  'ép' => 'é–œ',
  'éq' => 'é–�',
  'ér' => 'é–ž',
  'és' => 'é–Ÿ',
  'ét' => 'é– ',
  'éu' => 'é–¡',
  'év' => 'é–¢',
  'éw' => 'é–£',
  'éx' => 'é–¤',
  'éy' => 'é–¥',
  'éz' => 'é–¦',
  'é{' => 'é–§',
  'é|' => 'é–¨',
  'é}' => 'é–©',
  'é~' => 'é–ª',
  'é€' => 'é–«',
  'é�' => 'é–¬',
  'é‚' => 'é–­',
  'éƒ' => 'é–®',
  'é„' => 'é–¯',
  'é…' => 'é–°',
  'é†' => 'é–±',
  'é‡' => 'é–²',
  'éˆ' => 'é–³',
  'é‰' => 'é–´',
  'éŠ' => 'é–µ',
  'é‹' => 'é–¶',
  'éŒ' => 'é–·',
  'é�' => 'é–¸',
  'éŽ' => 'é–¹',
  'é�' => 'é–º',
  'é�' => 'é–»',
  'é‘' => 'é–¼',
  'é’' => 'é–½',
  'é“' => 'é–¾',
  'é”' => 'é–¿',
  'é•' => 'é—€',
  'é–' => 'é—�',
  'é—' => 'é—‚',
  'é˜' => 'é—ƒ',
  'é™' => 'é—„',
  'éš' => 'é—…',
  'é›' => 'é—†',
  'éœ' => 'é—‡',
  'é�' => 'é—ˆ',
  'éž' => 'é—‰',
  'éŸ' => 'é—Š',
  'é ' => 'é—‹',
  'é¡' => 'æ¤¤',
  'é¢' => 'æ£°',
  'é£' => 'æ¤‹',
  'é¤' => 'æ¤�',
  'é¥' => 'æ¥—',
  'é¦' => 'æ££',
  'é§' => 'æ¤�',
  'é¨' => 'æ¥±',
  'é©' => 'æ¤¹',
  'éª' => 'æ¥ ',
  'é«' => 'æ¥‚',
  'é¬' => 'æ¥�',
  'é­' => 'æ¦„',
  'é®' => 'æ¥«',
  'é¯' => 'æ¦€',
  'é°' => 'æ¦˜',
  'é±' => 'æ¥¸',
  'é²' => 'æ¤´',
  'é³' => 'æ§Œ',
  'é´' => 'æ¦‡',
  'éµ' => 'æ¦ˆ',
  'é¶' => 'æ§Ž',
  'é·' => 'æ¦‰',
  'é¸' => 'æ¥¦',
  'é¹' => 'æ¥£',
  'éº' => 'æ¥¹',
  'é»' => 'æ¦›',
  'é¼' => 'æ¦§',
  'é½' => 'æ¦»',
  'é¾' => 'æ¦«',
  'é¿' => 'æ¦­',
  'éÀ' => 'æ§”',
  'éÁ' => 'æ¦±',
  'éÂ' => 'æ§�',
  'éÃ' => 'æ§Š',
  'éÄ' => 'æ§Ÿ',
  'éÅ' => 'æ¦•',
  'éÆ' => 'æ§ ',
  'éÇ' => 'æ¦�',
  'éÈ' => 'æ§¿',
  'éÉ' => 'æ¨¯',
  'éÊ' => 'æ§­',
  'éË' => 'æ¨—',
  'éÌ' => 'æ¨˜',
  'éÍ' => 'æ©¥',
  'éÎ' => 'æ§²',
  'éÏ' => 'æ©„',
  'éÐ' => 'æ¨¾',
  'éÑ' => 'æª ',
  'éÒ' => 'æ©�',
  'éÓ' => 'æ©›',
  'éÔ' => 'æ¨µ',
  'éÕ' => 'æªŽ',
  'éÖ' => 'æ©¹',
  'é×' => 'æ¨½',
  'éØ' => 'æ¨¨',
  'éÙ' => 'æ©˜',
  'éÚ' => 'æ©¼',
  'éÛ' => 'æª‘',
  'éÜ' => 'æª�',
  'éÝ' => 'æª©',
  'éÞ' => 'æª—',
  'éß' => 'æª«',
  'éà' => 'çŒ·',
  'éá' => 'ç�’',
  'éâ' => 'æ®�',
  'éã' => 'æ®‚',
  'éä' => 'æ®‡',
  'éå' => 'æ®„',
  'éæ' => 'æ®’',
  'éç' => 'æ®“',
  'éè' => 'æ®�',
  'éé' => 'æ®š',
  'éê' => 'æ®›',
  'éë' => 'æ®¡',
  'éì' => 'æ®ª',
  'éí' => 'è½«',
  'éî' => 'è½­',
  'éï' => 'è½±',
  'éð' => 'è½²',
  'éñ' => 'è½³',
  'éò' => 'è½µ',
  'éó' => 'è½¶',
  'éô' => 'è½¸',
  'éõ' => 'è½·',
  'éö' => 'è½¹',
  'é÷' => 'è½º',
  'éø' => 'è½¼',
  'éù' => 'è½¾',
  'éú' => 'è¾�',
  'éû' => 'è¾‚',
  'éü' => 'è¾„',
  'éý' => 'è¾‡',
  'éþ' => 'è¾‹',
  'ê@' => 'é—Œ',
  'êA' => 'é—�',
  'êB' => 'é—Ž',
  'êC' => 'é—�',
  'êD' => 'é—�',
  'êE' => 'é—‘',
  'êF' => 'é—’',
  'êG' => 'é—“',
  'êH' => 'é—”',
  'êI' => 'é—•',
  'êJ' => 'é—–',
  'êK' => 'é——',
  'êL' => 'é—˜',
  'êM' => 'é—™',
  'êN' => 'é—š',
  'êO' => 'é—›',
  'êP' => 'é—œ',
  'êQ' => 'é—�',
  'êR' => 'é—ž',
  'êS' => 'é—Ÿ',
  'êT' => 'é— ',
  'êU' => 'é—¡',
  'êV' => 'é—¢',
  'êW' => 'é—£',
  'êX' => 'é—¤',
  'êY' => 'é—¥',
  'êZ' => 'é—¦',
  'ê[' => 'é—§',
  'ê\\' => 'é—¬',
  'ê]' => 'é—¿',
  'ê^' => 'é˜‡',
  'ê_' => 'é˜“',
  'ê`' => 'é˜˜',
  'êa' => 'é˜›',
  'êb' => 'é˜ž',
  'êc' => 'é˜ ',
  'êd' => 'é˜£',
  'êe' => 'é˜¤',
  'êf' => 'é˜¥',
  'êg' => 'é˜¦',
  'êh' => 'é˜§',
  'êi' => 'é˜¨',
  'êj' => 'é˜©',
  'êk' => 'é˜«',
  'êl' => 'é˜¬',
  'êm' => 'é˜­',
  'ên' => 'é˜¯',
  'êo' => 'é˜°',
  'êp' => 'é˜·',
  'êq' => 'é˜¸',
  'êr' => 'é˜¹',
  'ês' => 'é˜º',
  'êt' => 'é˜¾',
  'êu' => 'é™�',
  'êv' => 'é™ƒ',
  'êw' => 'é™Š',
  'êx' => 'é™Ž',
  'êy' => 'é™�',
  'êz' => 'é™‘',
  'ê{' => 'é™’',
  'ê|' => 'é™“',
  'ê}' => 'é™–',
  'ê~' => 'é™—',
  'ê€' => 'é™˜',
  'ê�' => 'é™™',
  'ê‚' => 'é™š',
  'êƒ' => 'é™œ',
  'ê„' => 'é™�',
  'ê…' => 'é™ž',
  'ê†' => 'é™ ',
  'ê‡' => 'é™£',
  'êˆ' => 'é™¥',
  'ê‰' => 'é™¦',
  'êŠ' => 'é™«',
  'ê‹' => 'é™­',
  'êŒ' => 'é™®',
  'ê�' => 'é™¯',
  'êŽ' => 'é™°',
  'ê�' => 'é™±',
  'ê�' => 'é™³',
  'ê‘' => 'é™¸',
  'ê’' => 'é™¹',
  'ê“' => 'é™º',
  'ê”' => 'é™»',
  'ê•' => 'é™¼',
  'ê–' => 'é™½',
  'ê—' => 'é™¾',
  'ê˜' => 'é™¿',
  'ê™' => 'éš€',
  'êš' => 'éš�',
  'ê›' => 'éš‚',
  'êœ' => 'éšƒ',
  'ê�' => 'éš„',
  'êž' => 'éš‡',
  'êŸ' => 'éš‰',
  'ê ' => 'éšŠ',
  'ê¡' => 'è¾�',
  'ê¢' => 'è¾Ž',
  'ê£' => 'è¾�',
  'ê¤' => 'è¾˜',
  'ê¥' => 'è¾š',
  'ê¦' => 'è»Ž',
  'ê§' => 'æˆ‹',
  'ê¨' => 'æˆ—',
  'ê©' => 'æˆ›',
  'êª' => 'æˆŸ',
  'ê«' => 'æˆ¢',
  'ê¬' => 'æˆ¡',
  'ê­' => 'æˆ¥',
  'ê®' => 'æˆ¤',
  'ê¯' => 'æˆ¬',
  'ê°' => 'è‡§',
  'ê±' => 'ç“¯',
  'ê²' => 'ç“´',
  'ê³' => 'ç“¿',
  'ê´' => 'ç”�',
  'êµ' => 'ç”‘',
  'ê¶' => 'ç”“',
  'ê·' => 'æ”´',
  'ê¸' => 'æ—®',
  'ê¹' => 'æ—¯',
  'êº' => 'æ—°',
  'ê»' => 'æ˜Š',
  'ê¼' => 'æ˜™',
  'ê½' => 'æ�²',
  'ê¾' => 'æ˜ƒ',
  'ê¿' => 'æ˜•',
  'êÀ' => 'æ˜€',
  'êÁ' => 'ç‚…',
  'êÂ' => 'æ›·',
  'êÃ' => 'æ˜�',
  'êÄ' => 'æ˜´',
  'êÅ' => 'æ˜±',
  'êÆ' => 'æ˜¶',
  'êÇ' => 'æ˜µ',
  'êÈ' => 'è€†',
  'êÉ' => 'æ™Ÿ',
  'êÊ' => 'æ™”',
  'êË' => 'æ™�',
  'êÌ' => 'æ™�',
  'êÍ' => 'æ™–',
  'êÎ' => 'æ™¡',
  'êÏ' => 'æ™—',
  'êÐ' => 'æ™·',
  'êÑ' => 'æš„',
  'êÒ' => 'æšŒ',
  'êÓ' => 'æš§',
  'êÔ' => 'æš�',
  'êÕ' => 'æš¾',
  'êÖ' => 'æ››',
  'ê×' => 'æ›œ',
  'êØ' => 'æ›¦',
  'êÙ' => 'æ›©',
  'êÚ' => 'è´²',
  'êÛ' => 'è´³',
  'êÜ' => 'è´¶',
  'êÝ' => 'è´»',
  'êÞ' => 'è´½',
  'êß' => 'èµ€',
  'êà' => 'èµ…',
  'êá' => 'èµ†',
  'êâ' => 'èµˆ',
  'êã' => 'èµ‰',
  'êä' => 'èµ‡',
  'êå' => 'èµ�',
  'êæ' => 'èµ•',
  'êç' => 'èµ™',
  'êè' => 'è§‡',
  'êé' => 'è§Š',
  'êê' => 'è§‹',
  'êë' => 'è§Œ',
  'êì' => 'è§Ž',
  'êí' => 'è§�',
  'êî' => 'è§�',
  'êï' => 'è§‘',
  'êð' => 'ç‰®',
  'êñ' => 'çŠŸ',
  'êò' => 'ç‰�',
  'êó' => 'ç‰¦',
  'êô' => 'ç‰¯',
  'êõ' => 'ç‰¾',
  'êö' => 'ç‰¿',
  'ê÷' => 'çŠ„',
  'êø' => 'çŠ‹',
  'êù' => 'çŠ�',
  'êú' => 'çŠ�',
  'êû' => 'çŠ’',
  'êü' => 'æŒˆ',
  'êý' => 'æŒ²',
  'êþ' => 'æŽ°',
  'ë@' => 'éšŒ',
  'ëA' => 'éšŽ',
  'ëB' => 'éš‘',
  'ëC' => 'éš’',
  'ëD' => 'éš“',
  'ëE' => 'éš•',
  'ëF' => 'éš–',
  'ëG' => 'éšš',
  'ëH' => 'éš›',
  'ëI' => 'éš�',
  'ëJ' => 'éšž',
  'ëK' => 'éšŸ',
  'ëL' => 'éš ',
  'ëM' => 'éš¡',
  'ëN' => 'éš¢',
  'ëO' => 'éš£',
  'ëP' => 'éš¤',
  'ëQ' => 'éš¥',
  'ëR' => 'éš¦',
  'ëS' => 'éš¨',
  'ëT' => 'éš©',
  'ëU' => 'éšª',
  'ëV' => 'éš«',
  'ëW' => 'éš¬',
  'ëX' => 'éš­',
  'ëY' => 'éš®',
  'ëZ' => 'éš¯',
  'ë[' => 'éš±',
  'ë\\' => 'éš²',
  'ë]' => 'éš´',
  'ë^' => 'éšµ',
  'ë_' => 'éš·',
  'ë`' => 'éš¸',
  'ëa' => 'éšº',
  'ëb' => 'éš»',
  'ëc' => 'éš¿',
  'ëd' => 'é›‚',
  'ëe' => 'é›ƒ',
  'ëf' => 'é›ˆ',
  'ëg' => 'é›Š',
  'ëh' => 'é›‹',
  'ëi' => 'é›�',
  'ëj' => 'é›‘',
  'ëk' => 'é›“',
  'ël' => 'é›”',
  'ëm' => 'é›–',
  'ën' => 'é›—',
  'ëo' => 'é›˜',
  'ëp' => 'é›™',
  'ëq' => 'é›š',
  'ër' => 'é››',
  'ës' => 'é›œ',
  'ët' => 'é›�',
  'ëu' => 'é›ž',
  'ëv' => 'é›Ÿ',
  'ëw' => 'é›¡',
  'ëx' => 'é›¢',
  'ëy' => 'é›£',
  'ëz' => 'é›¤',
  'ë{' => 'é›¥',
  'ë|' => 'é›¦',
  'ë}' => 'é›§',
  'ë~' => 'é›«',
  'ë€' => 'é›¬',
  'ë�' => 'é›­',
  'ë‚' => 'é›®',
  'ëƒ' => 'é›°',
  'ë„' => 'é›±',
  'ë…' => 'é›²',
  'ë†' => 'é›´',
  'ë‡' => 'é›µ',
  'ëˆ' => 'é›¸',
  'ë‰' => 'é›º',
  'ëŠ' => 'é›»',
  'ë‹' => 'é›¼',
  'ëŒ' => 'é›½',
  'ë�' => 'é›¿',
  'ëŽ' => 'éœ‚',
  'ë�' => 'éœƒ',
  'ë�' => 'éœ…',
  'ë‘' => 'éœŠ',
  'ë’' => 'éœ‹',
  'ë“' => 'éœŒ',
  'ë”' => 'éœ�',
  'ë•' => 'éœ‘',
  'ë–' => 'éœ’',
  'ë—' => 'éœ”',
  'ë˜' => 'éœ•',
  'ë™' => 'éœ—',
  'ëš' => 'éœ˜',
  'ë›' => 'éœ™',
  'ëœ' => 'éœš',
  'ë�' => 'éœ›',
  'ëž' => 'éœ�',
  'ëŸ' => 'éœŸ',
  'ë ' => 'éœ ',
  'ë¡' => 'æ�¿',
  'ë¢' => 'æ“˜',
  'ë£' => 'è€„',
  'ë¤' => 'æ¯ª',
  'ë¥' => 'æ¯³',
  'ë¦' => 'æ¯½',
  'ë§' => 'æ¯µ',
  'ë¨' => 'æ¯¹',
  'ë©' => 'æ°…',
  'ëª' => 'æ°‡',
  'ë«' => 'æ°†',
  'ë¬' => 'æ°�',
  'ë­' => 'æ°•',
  'ë®' => 'æ°˜',
  'ë¯' => 'æ°™',
  'ë°' => 'æ°š',
  'ë±' => 'æ°¡',
  'ë²' => 'æ°©',
  'ë³' => 'æ°¤',
  'ë´' => 'æ°ª',
  'ëµ' => 'æ°²',
  'ë¶' => 'æ”µ',
  'ë·' => 'æ••',
  'ë¸' => 'æ•«',
  'ë¹' => 'ç‰�',
  'ëº' => 'ç‰’',
  'ë»' => 'ç‰–',
  'ë¼' => 'çˆ°',
  'ë½' => 'è™¢',
  'ë¾' => 'åˆ–',
  'ë¿' => 'è‚Ÿ',
  'ëÀ' => 'è‚œ',
  'ëÁ' => 'è‚“',
  'ëÂ' => 'è‚¼',
  'ëÃ' => 'æœŠ',
  'ëÄ' => 'è‚½',
  'ëÅ' => 'è‚±',
  'ëÆ' => 'è‚«',
  'ëÇ' => 'è‚­',
  'ëÈ' => 'è‚´',
  'ëÉ' => 'è‚·',
  'ëÊ' => 'èƒ§',
  'ëË' => 'èƒ¨',
  'ëÌ' => 'èƒ©',
  'ëÍ' => 'èƒª',
  'ëÎ' => 'èƒ›',
  'ëÏ' => 'èƒ‚',
  'ëÐ' => 'èƒ„',
  'ëÑ' => 'èƒ™',
  'ëÒ' => 'èƒ�',
  'ëÓ' => 'èƒ—',
  'ëÔ' => 'æœ�',
  'ëÕ' => 'èƒ�',
  'ëÖ' => 'èƒ«',
  'ë×' => 'èƒ±',
  'ëØ' => 'èƒ´',
  'ëÙ' => 'èƒ­',
  'ëÚ' => 'è„�',
  'ëÛ' => 'è„Ž',
  'ëÜ' => 'èƒ²',
  'ëÝ' => 'èƒ¼',
  'ëÞ' => 'æœ•',
  'ëß' => 'è„’',
  'ëà' => 'è±š',
  'ëá' => 'è„¶',
  'ëâ' => 'è„ž',
  'ëã' => 'è„¬',
  'ëä' => 'è„˜',
  'ëå' => 'è„²',
  'ëæ' => 'è…ˆ',
  'ëç' => 'è…Œ',
  'ëè' => 'è…“',
  'ëé' => 'è…´',
  'ëê' => 'è…™',
  'ëë' => 'è…š',
  'ëì' => 'è…±',
  'ëí' => 'è… ',
  'ëî' => 'è…©',
  'ëï' => 'è…¼',
  'ëð' => 'è…½',
  'ëñ' => 'è…­',
  'ëò' => 'è…§',
  'ëó' => 'å¡�',
  'ëô' => 'åªµ',
  'ëõ' => 'è†ˆ',
  'ëö' => 'è†‚',
  'ë÷' => 'è†‘',
  'ëø' => 'æ»•',
  'ëù' => 'è†£',
  'ëú' => 'è†ª',
  'ëû' => 'è‡Œ',
  'ëü' => 'æœ¦',
  'ëý' => 'è‡Š',
  'ëþ' => 'è†»',
  'ì@' => 'éœ¡',
  'ìA' => 'éœ¢',
  'ìB' => 'éœ£',
  'ìC' => 'éœ¤',
  'ìD' => 'éœ¥',
  'ìE' => 'éœ¦',
  'ìF' => 'éœ§',
  'ìG' => 'éœ¨',
  'ìH' => 'éœ©',
  'ìI' => 'éœ«',
  'ìJ' => 'éœ¬',
  'ìK' => 'éœ®',
  'ìL' => 'éœ¯',
  'ìM' => 'éœ±',
  'ìN' => 'éœ³',
  'ìO' => 'éœ´',
  'ìP' => 'éœµ',
  'ìQ' => 'éœ¶',
  'ìR' => 'éœ·',
  'ìS' => 'éœº',
  'ìT' => 'éœ»',
  'ìU' => 'éœ¼',
  'ìV' => 'éœ½',
  'ìW' => 'éœ¿',
  'ìX' => 'é�€',
  'ìY' => 'é��',
  'ìZ' => 'é�‚',
  'ì[' => 'é�ƒ',
  'ì\\' => 'é�„',
  'ì]' => 'é�…',
  'ì^' => 'é�†',
  'ì_' => 'é�‡',
  'ì`' => 'é�ˆ',
  'ìa' => 'é�‰',
  'ìb' => 'é�Š',
  'ìc' => 'é�‹',
  'ìd' => 'é�Œ',
  'ìe' => 'é��',
  'ìf' => 'é�Ž',
  'ìg' => 'é��',
  'ìh' => 'é��',
  'ìi' => 'é�‘',
  'ìj' => 'é�”',
  'ìk' => 'é�•',
  'ìl' => 'é�—',
  'ìm' => 'é�˜',
  'ìn' => 'é�š',
  'ìo' => 'é�œ',
  'ìp' => 'é��',
  'ìq' => 'é�Ÿ',
  'ìr' => 'é�£',
  'ìs' => 'é�¤',
  'ìt' => 'é�¦',
  'ìu' => 'é�§',
  'ìv' => 'é�¨',
  'ìw' => 'é�ª',
  'ìx' => 'é�«',
  'ìy' => 'é�¬',
  'ìz' => 'é�­',
  'ì{' => 'é�®',
  'ì|' => 'é�¯',
  'ì}' => 'é�°',
  'ì~' => 'é�±',
  'ì€' => 'é�²',
  'ì�' => 'é�µ',
  'ì‚' => 'é�·',
  'ìƒ' => 'é�¸',
  'ì„' => 'é�¹',
  'ì…' => 'é�º',
  'ì†' => 'é�»',
  'ì‡' => 'é�½',
  'ìˆ' => 'é�¾',
  'ì‰' => 'é�¿',
  'ìŠ' => 'éž€',
  'ì‹' => 'éž�',
  'ìŒ' => 'éž‚',
  'ì�' => 'éžƒ',
  'ìŽ' => 'éž„',
  'ì�' => 'éž†',
  'ì�' => 'éž‡',
  'ì‘' => 'éžˆ',
  'ì’' => 'éž‰',
  'ì“' => 'éžŠ',
  'ì”' => 'éžŒ',
  'ì•' => 'éžŽ',
  'ì–' => 'éž�',
  'ì—' => 'éž�',
  'ì˜' => 'éž“',
  'ì™' => 'éž•',
  'ìš' => 'éž–',
  'ì›' => 'éž—',
  'ìœ' => 'éž™',
  'ì�' => 'éžš',
  'ìž' => 'éž›',
  'ìŸ' => 'éžœ',
  'ì ' => 'éž�',
  'ì¡' => 'è‡�',
  'ì¢' => 'è†¦',
  'ì£' => 'æ¬¤',
  'ì¤' => 'æ¬·',
  'ì¥' => 'æ¬¹',
  'ì¦' => 'æ­ƒ',
  'ì§' => 'æ­†',
  'ì¨' => 'æ­™',
  'ì©' => 'é£‘',
  'ìª' => 'é£’',
  'ì«' => 'é£“',
  'ì¬' => 'é£•',
  'ì­' => 'é£™',
  'ì®' => 'é£š',
  'ì¯' => 'æ®³',
  'ì°' => 'å½€',
  'ì±' => 'æ¯‚',
  'ì²' => 'è§³',
  'ì³' => 'æ–�',
  'ì´' => 'é½‘',
  'ìµ' => 'æ–“',
  'ì¶' => 'æ–¼',
  'ì·' => 'æ—†',
  'ì¸' => 'æ—„',
  'ì¹' => 'æ—ƒ',
  'ìº' => 'æ—Œ',
  'ì»' => 'æ—Ž',
  'ì¼' => 'æ—’',
  'ì½' => 'æ—–',
  'ì¾' => 'ç‚€',
  'ì¿' => 'ç‚œ',
  'ìÀ' => 'ç‚–',
  'ìÁ' => 'ç‚�',
  'ìÂ' => 'ç‚»',
  'ìÃ' => 'çƒ€',
  'ìÄ' => 'ç‚·',
  'ìÅ' => 'ç‚«',
  'ìÆ' => 'ç‚±',
  'ìÇ' => 'çƒ¨',
  'ìÈ' => 'çƒŠ',
  'ìÉ' => 'ç„�',
  'ìÊ' => 'ç„“',
  'ìË' => 'ç„–',
  'ìÌ' => 'ç„¯',
  'ìÍ' => 'ç„±',
  'ìÎ' => 'ç…³',
  'ìÏ' => 'ç…œ',
  'ìÐ' => 'ç…¨',
  'ìÑ' => 'ç……',
  'ìÒ' => 'ç…²',
  'ìÓ' => 'ç…Š',
  'ìÔ' => 'ç…¸',
  'ìÕ' => 'ç…º',
  'ìÖ' => 'ç†˜',
  'ì×' => 'ç†³',
  'ìØ' => 'ç†µ',
  'ìÙ' => 'ç†¨',
  'ìÚ' => 'ç† ',
  'ìÛ' => 'ç‡ ',
  'ìÜ' => 'ç‡”',
  'ìÝ' => 'ç‡§',
  'ìÞ' => 'ç‡¹',
  'ìß' => 'çˆ�',
  'ìà' => 'çˆ¨',
  'ìá' => 'ç�¬',
  'ìâ' => 'ç„˜',
  'ìã' => 'ç…¦',
  'ìä' => 'ç†¹',
  'ìå' => 'æˆ¾',
  'ìæ' => 'æˆ½',
  'ìç' => 'æ‰ƒ',
  'ìè' => 'æ‰ˆ',
  'ìé' => 'æ‰‰',
  'ìê' => 'ç¤»',
  'ìë' => 'ç¥€',
  'ìì' => 'ç¥†',
  'ìí' => 'ç¥‰',
  'ìî' => 'ç¥›',
  'ìï' => 'ç¥œ',
  'ìð' => 'ç¥“',
  'ìñ' => 'ç¥š',
  'ìò' => 'ç¥¢',
  'ìó' => 'ç¥—',
  'ìô' => 'ç¥ ',
  'ìõ' => 'ç¥¯',
  'ìö' => 'ç¥§',
  'ì÷' => 'ç¥º',
  'ìø' => 'ç¦…',
  'ìù' => 'ç¦Š',
  'ìú' => 'ç¦š',
  'ìû' => 'ç¦§',
  'ìü' => 'ç¦³',
  'ìý' => 'å¿‘',
  'ìþ' => 'å¿�',
  'í@' => 'éžž',
  'íA' => 'éžŸ',
  'íB' => 'éž¡',
  'íC' => 'éž¢',
  'íD' => 'éž¤',
  'íE' => 'éž¥',
  'íF' => 'éž¦',
  'íG' => 'éž§',
  'íH' => 'éž¨',
  'íI' => 'éž©',
  'íJ' => 'éžª',
  'íK' => 'éž¬',
  'íL' => 'éž®',
  'íM' => 'éž°',
  'íN' => 'éž±',
  'íO' => 'éž³',
  'íP' => 'éžµ',
  'íQ' => 'éž¶',
  'íR' => 'éž·',
  'íS' => 'éž¸',
  'íT' => 'éž¹',
  'íU' => 'éžº',
  'íV' => 'éž»',
  'íW' => 'éž¼',
  'íX' => 'éž½',
  'íY' => 'éž¾',
  'íZ' => 'éž¿',
  'í[' => 'éŸ€',
  'í\\' => 'éŸ�',
  'í]' => 'éŸ‚',
  'í^' => 'éŸƒ',
  'í_' => 'éŸ„',
  'í`' => 'éŸ…',
  'ía' => 'éŸ†',
  'íb' => 'éŸ‡',
  'íc' => 'éŸˆ',
  'íd' => 'éŸ‰',
  'íe' => 'éŸŠ',
  'íf' => 'éŸ‹',
  'íg' => 'éŸŒ',
  'íh' => 'éŸ�',
  'íi' => 'éŸŽ',
  'íj' => 'éŸ�',
  'ík' => 'éŸ�',
  'íl' => 'éŸ‘',
  'ím' => 'éŸ’',
  'ín' => 'éŸ“',
  'ío' => 'éŸ”',
  'íp' => 'éŸ•',
  'íq' => 'éŸ–',
  'ír' => 'éŸ—',
  'ís' => 'éŸ˜',
  'ít' => 'éŸ™',
  'íu' => 'éŸš',
  'ív' => 'éŸ›',
  'íw' => 'éŸœ',
  'íx' => 'éŸ�',
  'íy' => 'éŸž',
  'íz' => 'éŸŸ',
  'í{' => 'éŸ ',
  'í|' => 'éŸ¡',
  'í}' => 'éŸ¢',
  'í~' => 'éŸ£',
  'í€' => 'éŸ¤',
  'í�' => 'éŸ¥',
  'í‚' => 'éŸ¨',
  'íƒ' => 'éŸ®',
  'í„' => 'éŸ¯',
  'í…' => 'éŸ°',
  'í†' => 'éŸ±',
  'í‡' => 'éŸ²',
  'íˆ' => 'éŸ´',
  'í‰' => 'éŸ·',
  'íŠ' => 'éŸ¸',
  'í‹' => 'éŸ¹',
  'íŒ' => 'éŸº',
  'í�' => 'éŸ»',
  'íŽ' => 'éŸ¼',
  'í�' => 'éŸ½',
  'í�' => 'éŸ¾',
  'í‘' => 'éŸ¿',
  'í’' => 'é €',
  'í“' => 'é �',
  'í”' => 'é ‚',
  'í•' => 'é ƒ',
  'í–' => 'é „',
  'í—' => 'é …',
  'í˜' => 'é †',
  'í™' => 'é ‡',
  'íš' => 'é ˆ',
  'í›' => 'é ‰',
  'íœ' => 'é Š',
  'í�' => 'é ‹',
  'íž' => 'é Œ',
  'íŸ' => 'é �',
  'í ' => 'é Ž',
  'í¡' => 'æ€¼',
  'í¢' => 'æ��',
  'í£' => 'æ�š',
  'í¤' => 'æ�§',
  'í¥' => 'æ��',
  'í¦' => 'æ�™',
  'í§' => 'æ�£',
  'í¨' => 'æ‚«',
  'í©' => 'æ„†',
  'íª' => 'æ„�',
  'í«' => 'æ…�',
  'í¬' => 'æ†©',
  'í­' => 'æ†�',
  'í®' => 'æ‡‹',
  'í¯' => 'æ‡‘',
  'í°' => 'æˆ†',
  'í±' => 'è‚€',
  'í²' => 'è�¿',
  'í³' => 'æ²“',
  'í´' => 'æ³¶',
  'íµ' => 'æ·¼',
  'í¶' => 'çŸ¶',
  'í·' => 'çŸ¸',
  'í¸' => 'ç €',
  'í¹' => 'ç ‰',
  'íº' => 'ç —',
  'í»' => 'ç ˜',
  'í¼' => 'ç ‘',
  'í½' => 'æ–«',
  'í¾' => 'ç ­',
  'í¿' => 'ç œ',
  'íÀ' => 'ç �',
  'íÁ' => 'ç ¹',
  'íÂ' => 'ç º',
  'íÃ' => 'ç »',
  'íÄ' => 'ç Ÿ',
  'íÅ' => 'ç ¼',
  'íÆ' => 'ç ¥',
  'íÇ' => 'ç ¬',
  'íÈ' => 'ç £',
  'íÉ' => 'ç ©',
  'íÊ' => 'ç¡Ž',
  'íË' => 'ç¡­',
  'íÌ' => 'ç¡–',
  'íÍ' => 'ç¡—',
  'íÎ' => 'ç ¦',
  'íÏ' => 'ç¡�',
  'íÐ' => 'ç¡‡',
  'íÑ' => 'ç¡Œ',
  'íÒ' => 'ç¡ª',
  'íÓ' => 'ç¢›',
  'íÔ' => 'ç¢“',
  'íÕ' => 'ç¢š',
  'íÖ' => 'ç¢‡',
  'í×' => 'ç¢œ',
  'íØ' => 'ç¢¡',
  'íÙ' => 'ç¢£',
  'íÚ' => 'ç¢²',
  'íÛ' => 'ç¢¹',
  'íÜ' => 'ç¢¥',
  'íÝ' => 'ç£”',
  'íÞ' => 'ç£™',
  'íß' => 'ç£‰',
  'íà' => 'ç£¬',
  'íá' => 'ç£²',
  'íâ' => 'ç¤…',
  'íã' => 'ç£´',
  'íä' => 'ç¤“',
  'íå' => 'ç¤¤',
  'íæ' => 'ç¤ž',
  'íç' => 'ç¤´',
  'íè' => 'é¾›',
  'íé' => 'é»¹',
  'íê' => 'é»»',
  'íë' => 'é»¼',
  'íì' => 'ç›±',
  'íí' => 'çœ„',
  'íî' => 'çœ�',
  'íï' => 'ç›¹',
  'íð' => 'çœ‡',
  'íñ' => 'çœˆ',
  'íò' => 'çœš',
  'íó' => 'çœ¢',
  'íô' => 'çœ™',
  'íõ' => 'çœ­',
  'íö' => 'çœ¦',
  'í÷' => 'çœµ',
  'íø' => 'çœ¸',
  'íù' => 'ç��',
  'íú' => 'ç�‘',
  'íû' => 'ç�‡',
  'íü' => 'ç�ƒ',
  'íý' => 'ç�š',
  'íþ' => 'ç�¨',
  'î@' => 'é �',
  'îA' => 'é �',
  'îB' => 'é ‘',
  'îC' => 'é ’',
  'îD' => 'é “',
  'îE' => 'é ”',
  'îF' => 'é •',
  'îG' => 'é –',
  'îH' => 'é —',
  'îI' => 'é ˜',
  'îJ' => 'é ™',
  'îK' => 'é š',
  'îL' => 'é ›',
  'îM' => 'é œ',
  'îN' => 'é �',
  'îO' => 'é ž',
  'îP' => 'é Ÿ',
  'îQ' => 'é  ',
  'îR' => 'é ¡',
  'îS' => 'é ¢',
  'îT' => 'é £',
  'îU' => 'é ¤',
  'îV' => 'é ¥',
  'îW' => 'é ¦',
  'îX' => 'é §',
  'îY' => 'é ¨',
  'îZ' => 'é ©',
  'î[' => 'é ª',
  'î\\' => 'é «',
  'î]' => 'é ¬',
  'î^' => 'é ­',
  'î_' => 'é ®',
  'î`' => 'é ¯',
  'îa' => 'é °',
  'îb' => 'é ±',
  'îc' => 'é ²',
  'îd' => 'é ³',
  'îe' => 'é ´',
  'îf' => 'é µ',
  'îg' => 'é ¶',
  'îh' => 'é ·',
  'îi' => 'é ¸',
  'îj' => 'é ¹',
  'îk' => 'é º',
  'îl' => 'é »',
  'îm' => 'é ¼',
  'în' => 'é ½',
  'îo' => 'é ¾',
  'îp' => 'é ¿',
  'îq' => 'é¡€',
  'îr' => 'é¡�',
  'îs' => 'é¡‚',
  'ît' => 'é¡ƒ',
  'îu' => 'é¡„',
  'îv' => 'é¡…',
  'îw' => 'é¡†',
  'îx' => 'é¡‡',
  'îy' => 'é¡ˆ',
  'îz' => 'é¡‰',
  'î{' => 'é¡Š',
  'î|' => 'é¡‹',
  'î}' => 'é¡Œ',
  'î~' => 'é¡�',
  'î€' => 'é¡Ž',
  'î�' => 'é¡�',
  'î‚' => 'é¡�',
  'îƒ' => 'é¡‘',
  'î„' => 'é¡’',
  'î…' => 'é¡“',
  'î†' => 'é¡”',
  'î‡' => 'é¡•',
  'îˆ' => 'é¡–',
  'î‰' => 'é¡—',
  'îŠ' => 'é¡˜',
  'î‹' => 'é¡™',
  'îŒ' => 'é¡š',
  'î�' => 'é¡›',
  'îŽ' => 'é¡œ',
  'î�' => 'é¡�',
  'î�' => 'é¡ž',
  'î‘' => 'é¡Ÿ',
  'î’' => 'é¡ ',
  'î“' => 'é¡¡',
  'î”' => 'é¡¢',
  'î•' => 'é¡£',
  'î–' => 'é¡¤',
  'î—' => 'é¡¥',
  'î˜' => 'é¡¦',
  'î™' => 'é¡§',
  'îš' => 'é¡¨',
  'î›' => 'é¡©',
  'îœ' => 'é¡ª',
  'î�' => 'é¡«',
  'îž' => 'é¡¬',
  'îŸ' => 'é¡­',
  'î ' => 'é¡®',
  'î¡' => 'ç�¢',
  'î¢' => 'ç�¥',
  'î£' => 'ç�¿',
  'î¤' => 'çž�',
  'î¥' => 'ç�½',
  'î¦' => 'çž€',
  'î§' => 'çžŒ',
  'î¨' => 'çž‘',
  'î©' => 'çžŸ',
  'îª' => 'çž ',
  'î«' => 'çž°',
  'î¬' => 'çžµ',
  'î­' => 'çž½',
  'î®' => 'ç”º',
  'î¯' => 'ç•€',
  'î°' => 'ç•Ž',
  'î±' => 'ç•‹',
  'î²' => 'ç•ˆ',
  'î³' => 'ç•›',
  'î´' => 'ç•²',
  'îµ' => 'ç•¹',
  'î¶' => 'ç–ƒ',
  'î·' => 'ç½˜',
  'î¸' => 'ç½¡',
  'î¹' => 'ç½Ÿ',
  'îº' => 'è©ˆ',
  'î»' => 'ç½¨',
  'î¼' => 'ç½´',
  'î½' => 'ç½±',
  'î¾' => 'ç½¹',
  'î¿' => 'ç¾�',
  'îÀ' => 'ç½¾',
  'îÁ' => 'ç›�',
  'îÂ' => 'ç›¥',
  'îÃ' => 'è ²',
  'îÄ' => 'é’…',
  'îÅ' => 'é’†',
  'îÆ' => 'é’‡',
  'îÇ' => 'é’‹',
  'îÈ' => 'é’Š',
  'îÉ' => 'é’Œ',
  'îÊ' => 'é’�',
  'îË' => 'é’�',
  'îÌ' => 'é’�',
  'îÍ' => 'é’”',
  'îÎ' => 'é’—',
  'îÏ' => 'é’•',
  'îÐ' => 'é’š',
  'îÑ' => 'é’›',
  'îÒ' => 'é’œ',
  'îÓ' => 'é’£',
  'îÔ' => 'é’¤',
  'îÕ' => 'é’«',
  'îÖ' => 'é’ª',
  'î×' => 'é’­',
  'îØ' => 'é’¬',
  'îÙ' => 'é’¯',
  'îÚ' => 'é’°',
  'îÛ' => 'é’²',
  'îÜ' => 'é’´',
  'îÝ' => 'é’¶',
  'îÞ' => 'é’·',
  'îß' => 'é’¸',
  'îà' => 'é’¹',
  'îá' => 'é’º',
  'îâ' => 'é’¼',
  'îã' => 'é’½',
  'îä' => 'é’¿',
  'îå' => 'é“„',
  'îæ' => 'é“ˆ',
  'îç' => 'é“‰',
  'îè' => 'é“Š',
  'îé' => 'é“‹',
  'îê' => 'é“Œ',
  'îë' => 'é“�',
  'îì' => 'é“Ž',
  'îí' => 'é“�',
  'îî' => 'é“‘',
  'îï' => 'é“’',
  'îð' => 'é“•',
  'îñ' => 'é“–',
  'îò' => 'é“—',
  'îó' => 'é“™',
  'îô' => 'é“˜',
  'îõ' => 'é“›',
  'îö' => 'é“ž',
  'î÷' => 'é“Ÿ',
  'îø' => 'é“ ',
  'îù' => 'é“¢',
  'îú' => 'é“¤',
  'îû' => 'é“¥',
  'îü' => 'é“§',
  'îý' => 'é“¨',
  'îþ' => 'é“ª',
  'ï@' => 'é¡¯',
  'ïA' => 'é¡°',
  'ïB' => 'é¡±',
  'ïC' => 'é¡²',
  'ïD' => 'é¡³',
  'ïE' => 'é¡´',
  'ïF' => 'é¢‹',
  'ïG' => 'é¢Ž',
  'ïH' => 'é¢’',
  'ïI' => 'é¢•',
  'ïJ' => 'é¢™',
  'ïK' => 'é¢£',
  'ïL' => 'é¢¨',
  'ïM' => 'é¢©',
  'ïN' => 'é¢ª',
  'ïO' => 'é¢«',
  'ïP' => 'é¢¬',
  'ïQ' => 'é¢­',
  'ïR' => 'é¢®',
  'ïS' => 'é¢¯',
  'ïT' => 'é¢°',
  'ïU' => 'é¢±',
  'ïV' => 'é¢²',
  'ïW' => 'é¢³',
  'ïX' => 'é¢´',
  'ïY' => 'é¢µ',
  'ïZ' => 'é¢¶',
  'ï[' => 'é¢·',
  'ï\\' => 'é¢¸',
  'ï]' => 'é¢¹',
  'ï^' => 'é¢º',
  'ï_' => 'é¢»',
  'ï`' => 'é¢¼',
  'ïa' => 'é¢½',
  'ïb' => 'é¢¾',
  'ïc' => 'é¢¿',
  'ïd' => 'é£€',
  'ïe' => 'é£�',
  'ïf' => 'é£‚',
  'ïg' => 'é£ƒ',
  'ïh' => 'é£„',
  'ïi' => 'é£…',
  'ïj' => 'é£†',
  'ïk' => 'é£‡',
  'ïl' => 'é£ˆ',
  'ïm' => 'é£‰',
  'ïn' => 'é£Š',
  'ïo' => 'é£‹',
  'ïp' => 'é£Œ',
  'ïq' => 'é£�',
  'ïr' => 'é£�',
  'ïs' => 'é£�',
  'ït' => 'é£”',
  'ïu' => 'é£–',
  'ïv' => 'é£—',
  'ïw' => 'é£›',
  'ïx' => 'é£œ',
  'ïy' => 'é£�',
  'ïz' => 'é£ ',
  'ï{' => 'é£¡',
  'ï|' => 'é£¢',
  'ï}' => 'é££',
  'ï~' => 'é£¤',
  'ï€' => 'é£¥',
  'ï�' => 'é£¦',
  'ï‚' => 'é£©',
  'ïƒ' => 'é£ª',
  'ï„' => 'é£«',
  'ï…' => 'é£¬',
  'ï†' => 'é£­',
  'ï‡' => 'é£®',
  'ïˆ' => 'é£¯',
  'ï‰' => 'é£°',
  'ïŠ' => 'é£±',
  'ï‹' => 'é£²',
  'ïŒ' => 'é£³',
  'ï�' => 'é£´',
  'ïŽ' => 'é£µ',
  'ï�' => 'é£¶',
  'ï�' => 'é£·',
  'ï‘' => 'é£¸',
  'ï’' => 'é£¹',
  'ï“' => 'é£º',
  'ï”' => 'é£»',
  'ï•' => 'é£¼',
  'ï–' => 'é£½',
  'ï—' => 'é£¾',
  'ï˜' => 'é£¿',
  'ï™' => 'é¤€',
  'ïš' => 'é¤�',
  'ï›' => 'é¤‚',
  'ïœ' => 'é¤ƒ',
  'ï�' => 'é¤„',
  'ïž' => 'é¤…',
  'ïŸ' => 'é¤†',
  'ï ' => 'é¤‡',
  'ï¡' => 'é“©',
  'ï¢' => 'é“«',
  'ï£' => 'é“®',
  'ï¤' => 'é“¯',
  'ï¥' => 'é“³',
  'ï¦' => 'é“´',
  'ï§' => 'é“µ',
  'ï¨' => 'é“·',
  'ï©' => 'é“¹',
  'ïª' => 'é“¼',
  'ï«' => 'é“½',
  'ï¬' => 'é“¿',
  'ï­' => 'é”ƒ',
  'ï®' => 'é”‚',
  'ï¯' => 'é”†',
  'ï°' => 'é”‡',
  'ï±' => 'é”‰',
  'ï²' => 'é”Š',
  'ï³' => 'é”�',
  'ï´' => 'é”Ž',
  'ïµ' => 'é”�',
  'ï¶' => 'é”’',
  'ï·' => 'é”“',
  'ï¸' => 'é””',
  'ï¹' => 'é”•',
  'ïº' => 'é”–',
  'ï»' => 'é”˜',
  'ï¼' => 'é”›',
  'ï½' => 'é”�',
  'ï¾' => 'é”ž',
  'ï¿' => 'é”Ÿ',
  'ïÀ' => 'é”¢',
  'ïÁ' => 'é”ª',
  'ïÂ' => 'é”«',
  'ïÃ' => 'é”©',
  'ïÄ' => 'é”¬',
  'ïÅ' => 'é”±',
  'ïÆ' => 'é”²',
  'ïÇ' => 'é”´',
  'ïÈ' => 'é”¶',
  'ïÉ' => 'é”·',
  'ïÊ' => 'é”¸',
  'ïË' => 'é”¼',
  'ïÌ' => 'é”¾',
  'ïÍ' => 'é”¿',
  'ïÎ' => 'é•‚',
  'ïÏ' => 'é”µ',
  'ïÐ' => 'é•„',
  'ïÑ' => 'é•…',
  'ïÒ' => 'é•†',
  'ïÓ' => 'é•‰',
  'ïÔ' => 'é•Œ',
  'ïÕ' => 'é•Ž',
  'ïÖ' => 'é•�',
  'ï×' => 'é•’',
  'ïØ' => 'é•“',
  'ïÙ' => 'é•”',
  'ïÚ' => 'é•–',
  'ïÛ' => 'é•—',
  'ïÜ' => 'é•˜',
  'ïÝ' => 'é•™',
  'ïÞ' => 'é•›',
  'ïß' => 'é•ž',
  'ïà' => 'é•Ÿ',
  'ïá' => 'é•�',
  'ïâ' => 'é•¡',
  'ïã' => 'é•¢',
  'ïä' => 'é•¤',
  'ïå' => 'é•¥',
  'ïæ' => 'é•¦',
  'ïç' => 'é•§',
  'ïè' => 'é•¨',
  'ïé' => 'é•©',
  'ïê' => 'é•ª',
  'ïë' => 'é•«',
  'ïì' => 'é•¬',
  'ïí' => 'é•¯',
  'ïî' => 'é•±',
  'ïï' => 'é•²',
  'ïð' => 'é•³',
  'ïñ' => 'é”º',
  'ïò' => 'çŸ§',
  'ïó' => 'çŸ¬',
  'ïô' => 'é›‰',
  'ïõ' => 'ç§•',
  'ïö' => 'ç§­',
  'ï÷' => 'ç§£',
  'ïø' => 'ç§«',
  'ïù' => 'ç¨†',
  'ïú' => 'åµ‡',
  'ïû' => 'ç¨ƒ',
  'ïü' => 'ç¨‚',
  'ïý' => 'ç¨ž',
  'ïþ' => 'ç¨”',
  'ð@' => 'é¤ˆ',
  'ðA' => 'é¤‰',
  'ðB' => 'é¤Š',
  'ðC' => 'é¤‹',
  'ðD' => 'é¤Œ',
  'ðE' => 'é¤Ž',
  'ðF' => 'é¤�',
  'ðG' => 'é¤‘',
  'ðH' => 'é¤’',
  'ðI' => 'é¤“',
  'ðJ' => 'é¤”',
  'ðK' => 'é¤•',
  'ðL' => 'é¤–',
  'ðM' => 'é¤—',
  'ðN' => 'é¤˜',
  'ðO' => 'é¤™',
  'ðP' => 'é¤š',
  'ðQ' => 'é¤›',
  'ðR' => 'é¤œ',
  'ðS' => 'é¤�',
  'ðT' => 'é¤ž',
  'ðU' => 'é¤Ÿ',
  'ðV' => 'é¤ ',
  'ðW' => 'é¤¡',
  'ðX' => 'é¤¢',
  'ðY' => 'é¤£',
  'ðZ' => 'é¤¤',
  'ð[' => 'é¤¥',
  'ð\\' => 'é¤¦',
  'ð]' => 'é¤§',
  'ð^' => 'é¤¨',
  'ð_' => 'é¤©',
  'ð`' => 'é¤ª',
  'ða' => 'é¤«',
  'ðb' => 'é¤¬',
  'ðc' => 'é¤­',
  'ðd' => 'é¤¯',
  'ðe' => 'é¤°',
  'ðf' => 'é¤±',
  'ðg' => 'é¤²',
  'ðh' => 'é¤³',
  'ði' => 'é¤´',
  'ðj' => 'é¤µ',
  'ðk' => 'é¤¶',
  'ðl' => 'é¤·',
  'ðm' => 'é¤¸',
  'ðn' => 'é¤¹',
  'ðo' => 'é¤º',
  'ðp' => 'é¤»',
  'ðq' => 'é¤¼',
  'ðr' => 'é¤½',
  'ðs' => 'é¤¾',
  'ðt' => 'é¤¿',
  'ðu' => 'é¥€',
  'ðv' => 'é¥�',
  'ðw' => 'é¥‚',
  'ðx' => 'é¥ƒ',
  'ðy' => 'é¥„',
  'ðz' => 'é¥…',
  'ð{' => 'é¥†',
  'ð|' => 'é¥‡',
  'ð}' => 'é¥ˆ',
  'ð~' => 'é¥‰',
  'ð€' => 'é¥Š',
  'ð�' => 'é¥‹',
  'ð‚' => 'é¥Œ',
  'ðƒ' => 'é¥�',
  'ð„' => 'é¥Ž',
  'ð…' => 'é¥�',
  'ð†' => 'é¥�',
  'ð‡' => 'é¥‘',
  'ðˆ' => 'é¥’',
  'ð‰' => 'é¥“',
  'ðŠ' => 'é¥–',
  'ð‹' => 'é¥—',
  'ðŒ' => 'é¥˜',
  'ð�' => 'é¥™',
  'ðŽ' => 'é¥š',
  'ð�' => 'é¥›',
  'ð�' => 'é¥œ',
  'ð‘' => 'é¥�',
  'ð’' => 'é¥ž',
  'ð“' => 'é¥Ÿ',
  'ð”' => 'é¥ ',
  'ð•' => 'é¥¡',
  'ð–' => 'é¥¢',
  'ð—' => 'é¥¤',
  'ð˜' => 'é¥¦',
  'ð™' => 'é¥³',
  'ðš' => 'é¥¸',
  'ð›' => 'é¥¹',
  'ðœ' => 'é¥»',
  'ð�' => 'é¥¾',
  'ðž' => 'é¦‚',
  'ðŸ' => 'é¦ƒ',
  'ð ' => 'é¦‰',
  'ð¡' => 'ç¨¹',
  'ð¢' => 'ç¨·',
  'ð£' => 'ç©‘',
  'ð¤' => 'é»�',
  'ð¥' => 'é¦¥',
  'ð¦' => 'ç©°',
  'ð§' => 'çšˆ',
  'ð¨' => 'çšŽ',
  'ð©' => 'çš“',
  'ðª' => 'çš™',
  'ð«' => 'çš¤',
  'ð¬' => 'ç“ž',
  'ð­' => 'ç“ ',
  'ð®' => 'ç”¬',
  'ð¯' => 'é¸ ',
  'ð°' => 'é¸¢',
  'ð±' => 'é¸¨',
  'ð²' => 'é¸©',
  'ð³' => 'é¸ª',
  'ð´' => 'é¸«',
  'ðµ' => 'é¸¬',
  'ð¶' => 'é¸²',
  'ð·' => 'é¸±',
  'ð¸' => 'é¸¶',
  'ð¹' => 'é¸¸',
  'ðº' => 'é¸·',
  'ð»' => 'é¸¹',
  'ð¼' => 'é¸º',
  'ð½' => 'é¸¾',
  'ð¾' => 'é¹�',
  'ð¿' => 'é¹‚',
  'ðÀ' => 'é¹„',
  'ðÁ' => 'é¹†',
  'ðÂ' => 'é¹‡',
  'ðÃ' => 'é¹ˆ',
  'ðÄ' => 'é¹‰',
  'ðÅ' => 'é¹‹',
  'ðÆ' => 'é¹Œ',
  'ðÇ' => 'é¹Ž',
  'ðÈ' => 'é¹‘',
  'ðÉ' => 'é¹•',
  'ðÊ' => 'é¹—',
  'ðË' => 'é¹š',
  'ðÌ' => 'é¹›',
  'ðÍ' => 'é¹œ',
  'ðÎ' => 'é¹ž',
  'ðÏ' => 'é¹£',
  'ðÐ' => 'é¹¦',
  'ðÑ' => 'é¹§',
  'ðÒ' => 'é¹¨',
  'ðÓ' => 'é¹©',
  'ðÔ' => 'é¹ª',
  'ðÕ' => 'é¹«',
  'ðÖ' => 'é¹¬',
  'ð×' => 'é¹±',
  'ðØ' => 'é¹­',
  'ðÙ' => 'é¹³',
  'ðÚ' => 'ç–’',
  'ðÛ' => 'ç–”',
  'ðÜ' => 'ç––',
  'ðÝ' => 'ç– ',
  'ðÞ' => 'ç–�',
  'ðß' => 'ç–¬',
  'ðà' => 'ç–£',
  'ðá' => 'ç–³',
  'ðâ' => 'ç–´',
  'ðã' => 'ç–¸',
  'ðä' => 'ç—„',
  'ðå' => 'ç–±',
  'ðæ' => 'ç–°',
  'ðç' => 'ç—ƒ',
  'ðè' => 'ç—‚',
  'ðé' => 'ç—–',
  'ðê' => 'ç—�',
  'ðë' => 'ç—£',
  'ðì' => 'ç—¨',
  'ðí' => 'ç—¦',
  'ðî' => 'ç—¤',
  'ðï' => 'ç—«',
  'ðð' => 'ç—§',
  'ðñ' => 'ç˜ƒ',
  'ðò' => 'ç—±',
  'ðó' => 'ç—¼',
  'ðô' => 'ç—¿',
  'ðõ' => 'ç˜�',
  'ðö' => 'ç˜€',
  'ð÷' => 'ç˜…',
  'ðø' => 'ç˜Œ',
  'ðù' => 'ç˜—',
  'ðú' => 'ç˜Š',
  'ðû' => 'ç˜¥',
  'ðü' => 'ç˜˜',
  'ðý' => 'ç˜•',
  'ðþ' => 'ç˜™',
  'ñ@' => 'é¦Œ',
  'ñA' => 'é¦Ž',
  'ñB' => 'é¦š',
  'ñC' => 'é¦›',
  'ñD' => 'é¦œ',
  'ñE' => 'é¦�',
  'ñF' => 'é¦ž',
  'ñG' => 'é¦Ÿ',
  'ñH' => 'é¦ ',
  'ñI' => 'é¦¡',
  'ñJ' => 'é¦¢',
  'ñK' => 'é¦£',
  'ñL' => 'é¦¤',
  'ñM' => 'é¦¦',
  'ñN' => 'é¦§',
  'ñO' => 'é¦©',
  'ñP' => 'é¦ª',
  'ñQ' => 'é¦«',
  'ñR' => 'é¦¬',
  'ñS' => 'é¦­',
  'ñT' => 'é¦®',
  'ñU' => 'é¦¯',
  'ñV' => 'é¦°',
  'ñW' => 'é¦±',
  'ñX' => 'é¦²',
  'ñY' => 'é¦³',
  'ñZ' => 'é¦´',
  'ñ[' => 'é¦µ',
  'ñ\\' => 'é¦¶',
  'ñ]' => 'é¦·',
  'ñ^' => 'é¦¸',
  'ñ_' => 'é¦¹',
  'ñ`' => 'é¦º',
  'ña' => 'é¦»',
  'ñb' => 'é¦¼',
  'ñc' => 'é¦½',
  'ñd' => 'é¦¾',
  'ñe' => 'é¦¿',
  'ñf' => 'é§€',
  'ñg' => 'é§�',
  'ñh' => 'é§‚',
  'ñi' => 'é§ƒ',
  'ñj' => 'é§„',
  'ñk' => 'é§…',
  'ñl' => 'é§†',
  'ñm' => 'é§‡',
  'ñn' => 'é§ˆ',
  'ño' => 'é§‰',
  'ñp' => 'é§Š',
  'ñq' => 'é§‹',
  'ñr' => 'é§Œ',
  'ñs' => 'é§�',
  'ñt' => 'é§Ž',
  'ñu' => 'é§�',
  'ñv' => 'é§�',
  'ñw' => 'é§‘',
  'ñx' => 'é§’',
  'ñy' => 'é§“',
  'ñz' => 'é§”',
  'ñ{' => 'é§•',
  'ñ|' => 'é§–',
  'ñ}' => 'é§—',
  'ñ~' => 'é§˜',
  'ñ€' => 'é§™',
  'ñ�' => 'é§š',
  'ñ‚' => 'é§›',
  'ñƒ' => 'é§œ',
  'ñ„' => 'é§�',
  'ñ…' => 'é§ž',
  'ñ†' => 'é§Ÿ',
  'ñ‡' => 'é§ ',
  'ñˆ' => 'é§¡',
  'ñ‰' => 'é§¢',
  'ñŠ' => 'é§£',
  'ñ‹' => 'é§¤',
  'ñŒ' => 'é§¥',
  'ñ�' => 'é§¦',
  'ñŽ' => 'é§§',
  'ñ�' => 'é§¨',
  'ñ�' => 'é§©',
  'ñ‘' => 'é§ª',
  'ñ’' => 'é§«',
  'ñ“' => 'é§¬',
  'ñ”' => 'é§­',
  'ñ•' => 'é§®',
  'ñ–' => 'é§¯',
  'ñ—' => 'é§°',
  'ñ˜' => 'é§±',
  'ñ™' => 'é§²',
  'ñš' => 'é§³',
  'ñ›' => 'é§´',
  'ñœ' => 'é§µ',
  'ñ�' => 'é§¶',
  'ñž' => 'é§·',
  'ñŸ' => 'é§¸',
  'ñ ' => 'é§¹',
  'ñ¡' => 'ç˜›',
  'ñ¢' => 'ç˜¼',
  'ñ£' => 'ç˜¢',
  'ñ¤' => 'ç˜ ',
  'ñ¥' => 'ç™€',
  'ñ¦' => 'ç˜­',
  'ñ§' => 'ç˜°',
  'ñ¨' => 'ç˜¿',
  'ñ©' => 'ç˜µ',
  'ñª' => 'ç™ƒ',
  'ñ«' => 'ç˜¾',
  'ñ¬' => 'ç˜³',
  'ñ­' => 'ç™�',
  'ñ®' => 'ç™ž',
  'ñ¯' => 'ç™”',
  'ñ°' => 'ç™œ',
  'ñ±' => 'ç™–',
  'ñ²' => 'ç™«',
  'ñ³' => 'ç™¯',
  'ñ´' => 'ç¿Š',
  'ñµ' => 'ç«¦',
  'ñ¶' => 'ç©¸',
  'ñ·' => 'ç©¹',
  'ñ¸' => 'çª€',
  'ñ¹' => 'çª†',
  'ñº' => 'çªˆ',
  'ñ»' => 'çª•',
  'ñ¼' => 'çª¦',
  'ñ½' => 'çª ',
  'ñ¾' => 'çª¬',
  'ñ¿' => 'çª¨',
  'ñÀ' => 'çª­',
  'ñÁ' => 'çª³',
  'ñÂ' => 'è¡¤',
  'ñÃ' => 'è¡©',
  'ñÄ' => 'è¡²',
  'ñÅ' => 'è¡½',
  'ñÆ' => 'è¡¿',
  'ñÇ' => 'è¢‚',
  'ñÈ' => 'è¢¢',
  'ñÉ' => 'è£†',
  'ñÊ' => 'è¢·',
  'ñË' => 'è¢¼',
  'ñÌ' => 'è£‰',
  'ñÍ' => 'è£¢',
  'ñÎ' => 'è£Ž',
  'ñÏ' => 'è££',
  'ñÐ' => 'è£¥',
  'ñÑ' => 'è£±',
  'ñÒ' => 'è¤š',
  'ñÓ' => 'è£¼',
  'ñÔ' => 'è£¨',
  'ñÕ' => 'è£¾',
  'ñÖ' => 'è£°',
  'ñ×' => 'è¤¡',
  'ñØ' => 'è¤™',
  'ñÙ' => 'è¤“',
  'ñÚ' => 'è¤›',
  'ñÛ' => 'è¤Š',
  'ñÜ' => 'è¤´',
  'ñÝ' => 'è¤«',
  'ñÞ' => 'è¤¶',
  'ñß' => 'è¥�',
  'ñà' => 'è¥¦',
  'ñá' => 'è¥»',
  'ñâ' => 'ç–‹',
  'ñã' => 'èƒ¥',
  'ñä' => 'çš²',
  'ñå' => 'çš´',
  'ñæ' => 'çŸœ',
  'ñç' => 'è€’',
  'ñè' => 'è€”',
  'ñé' => 'è€–',
  'ñê' => 'è€œ',
  'ñë' => 'è€ ',
  'ñì' => 'è€¢',
  'ñí' => 'è€¥',
  'ñî' => 'è€¦',
  'ñï' => 'è€§',
  'ñð' => 'è€©',
  'ññ' => 'è€¨',
  'ñò' => 'è€±',
  'ñó' => 'è€‹',
  'ñô' => 'è€µ',
  'ñõ' => 'è�ƒ',
  'ñö' => 'è�†',
  'ñ÷' => 'è��',
  'ñø' => 'è�’',
  'ñù' => 'è�©',
  'ñú' => 'è�±',
  'ñû' => 'è¦ƒ',
  'ñü' => 'é¡¸',
  'ñý' => 'é¢€',
  'ñþ' => 'é¢ƒ',
  'ò@' => 'é§º',
  'òA' => 'é§»',
  'òB' => 'é§¼',
  'òC' => 'é§½',
  'òD' => 'é§¾',
  'òE' => 'é§¿',
  'òF' => 'é¨€',
  'òG' => 'é¨�',
  'òH' => 'é¨‚',
  'òI' => 'é¨ƒ',
  'òJ' => 'é¨„',
  'òK' => 'é¨…',
  'òL' => 'é¨†',
  'òM' => 'é¨‡',
  'òN' => 'é¨ˆ',
  'òO' => 'é¨‰',
  'òP' => 'é¨Š',
  'òQ' => 'é¨‹',
  'òR' => 'é¨Œ',
  'òS' => 'é¨�',
  'òT' => 'é¨Ž',
  'òU' => 'é¨�',
  'òV' => 'é¨�',
  'òW' => 'é¨‘',
  'òX' => 'é¨’',
  'òY' => 'é¨“',
  'òZ' => 'é¨”',
  'ò[' => 'é¨•',
  'ò\\' => 'é¨–',
  'ò]' => 'é¨—',
  'ò^' => 'é¨˜',
  'ò_' => 'é¨™',
  'ò`' => 'é¨š',
  'òa' => 'é¨›',
  'òb' => 'é¨œ',
  'òc' => 'é¨�',
  'òd' => 'é¨ž',
  'òe' => 'é¨Ÿ',
  'òf' => 'é¨ ',
  'òg' => 'é¨¡',
  'òh' => 'é¨¢',
  'òi' => 'é¨£',
  'òj' => 'é¨¤',
  'òk' => 'é¨¥',
  'òl' => 'é¨¦',
  'òm' => 'é¨§',
  'òn' => 'é¨¨',
  'òo' => 'é¨©',
  'òp' => 'é¨ª',
  'òq' => 'é¨«',
  'òr' => 'é¨¬',
  'òs' => 'é¨­',
  'òt' => 'é¨®',
  'òu' => 'é¨¯',
  'òv' => 'é¨°',
  'òw' => 'é¨±',
  'òx' => 'é¨²',
  'òy' => 'é¨³',
  'òz' => 'é¨´',
  'ò{' => 'é¨µ',
  'ò|' => 'é¨¶',
  'ò}' => 'é¨·',
  'ò~' => 'é¨¸',
  'ò€' => 'é¨¹',
  'ò�' => 'é¨º',
  'ò‚' => 'é¨»',
  'òƒ' => 'é¨¼',
  'ò„' => 'é¨½',
  'ò…' => 'é¨¾',
  'ò†' => 'é¨¿',
  'ò‡' => 'é©€',
  'òˆ' => 'é©�',
  'ò‰' => 'é©‚',
  'òŠ' => 'é©ƒ',
  'ò‹' => 'é©„',
  'òŒ' => 'é©…',
  'ò�' => 'é©†',
  'òŽ' => 'é©‡',
  'ò�' => 'é©ˆ',
  'ò�' => 'é©‰',
  'ò‘' => 'é©Š',
  'ò’' => 'é©‹',
  'ò“' => 'é©Œ',
  'ò”' => 'é©�',
  'ò•' => 'é©Ž',
  'ò–' => 'é©�',
  'ò—' => 'é©�',
  'ò˜' => 'é©‘',
  'ò™' => 'é©’',
  'òš' => 'é©“',
  'ò›' => 'é©”',
  'òœ' => 'é©•',
  'ò�' => 'é©–',
  'òž' => 'é©—',
  'òŸ' => 'é©˜',
  'ò ' => 'é©™',
  'ò¡' => 'é¢‰',
  'ò¢' => 'é¢Œ',
  'ò£' => 'é¢�',
  'ò¤' => 'é¢�',
  'ò¥' => 'é¢”',
  'ò¦' => 'é¢š',
  'ò§' => 'é¢›',
  'ò¨' => 'é¢ž',
  'ò©' => 'é¢Ÿ',
  'òª' => 'é¢¡',
  'ò«' => 'é¢¢',
  'ò¬' => 'é¢¥',
  'ò­' => 'é¢¦',
  'ò®' => 'è™�',
  'ò¯' => 'è™”',
  'ò°' => 'è™¬',
  'ò±' => 'è™®',
  'ò²' => 'è™¿',
  'ò³' => 'è™º',
  'ò´' => 'è™¼',
  'òµ' => 'è™»',
  'ò¶' => 'èš¨',
  'ò·' => 'èš�',
  'ò¸' => 'èš‹',
  'ò¹' => 'èš¬',
  'òº' => 'èš�',
  'ò»' => 'èš§',
  'ò¼' => 'èš£',
  'ò½' => 'èšª',
  'ò¾' => 'èš“',
  'ò¿' => 'èš©',
  'òÀ' => 'èš¶',
  'òÁ' => 'è›„',
  'òÂ' => 'èšµ',
  'òÃ' => 'è›Ž',
  'òÄ' => 'èš°',
  'òÅ' => 'èšº',
  'òÆ' => 'èš±',
  'òÇ' => 'èš¯',
  'òÈ' => 'è›‰',
  'òÉ' => 'è›�',
  'òÊ' => 'èš´',
  'òË' => 'è›©',
  'òÌ' => 'è›±',
  'òÍ' => 'è›²',
  'òÎ' => 'è›­',
  'òÏ' => 'è›³',
  'òÐ' => 'è›�',
  'òÑ' => 'èœ“',
  'òÒ' => 'è›ž',
  'òÓ' => 'è›´',
  'òÔ' => 'è›Ÿ',
  'òÕ' => 'è›˜',
  'òÖ' => 'è›‘',
  'ò×' => 'èœƒ',
  'òØ' => 'èœ‡',
  'òÙ' => 'è›¸',
  'òÚ' => 'èœˆ',
  'òÛ' => 'èœŠ',
  'òÜ' => 'èœ�',
  'òÝ' => 'èœ‰',
  'òÞ' => 'èœ£',
  'òß' => 'èœ»',
  'òà' => 'èœž',
  'òá' => 'èœ¥',
  'òâ' => 'èœ®',
  'òã' => 'èœš',
  'òä' => 'èœ¾',
  'òå' => 'è�ˆ',
  'òæ' => 'èœ´',
  'òç' => 'èœ±',
  'òè' => 'èœ©',
  'òé' => 'èœ·',
  'òê' => 'èœ¿',
  'òë' => 'èž‚',
  'òì' => 'èœ¢',
  'òí' => 'è�½',
  'òî' => 'è�¾',
  'òï' => 'è�»',
  'òð' => 'è� ',
  'òñ' => 'è�°',
  'òò' => 'è�Œ',
  'òó' => 'è�®',
  'òô' => 'èž‹',
  'òõ' => 'è�“',
  'òö' => 'è�£',
  'ò÷' => 'è�¼',
  'òø' => 'è�¤',
  'òù' => 'è�™',
  'òú' => 'è�¥',
  'òû' => 'èž“',
  'òü' => 'èž¯',
  'òý' => 'èž¨',
  'òþ' => 'èŸ’',
  'ó@' => 'é©š',
  'óA' => 'é©›',
  'óB' => 'é©œ',
  'óC' => 'é©�',
  'óD' => 'é©ž',
  'óE' => 'é©Ÿ',
  'óF' => 'é© ',
  'óG' => 'é©¡',
  'óH' => 'é©¢',
  'óI' => 'é©£',
  'óJ' => 'é©¤',
  'óK' => 'é©¥',
  'óL' => 'é©¦',
  'óM' => 'é©§',
  'óN' => 'é©¨',
  'óO' => 'é©©',
  'óP' => 'é©ª',
  'óQ' => 'é©«',
  'óR' => 'é©²',
  'óS' => 'éªƒ',
  'óT' => 'éª‰',
  'óU' => 'éª�',
  'óV' => 'éªŽ',
  'óW' => 'éª”',
  'óX' => 'éª•',
  'óY' => 'éª™',
  'óZ' => 'éª¦',
  'ó[' => 'éª©',
  'ó\\' => 'éªª',
  'ó]' => 'éª«',
  'ó^' => 'éª¬',
  'ó_' => 'éª­',
  'ó`' => 'éª®',
  'óa' => 'éª¯',
  'ób' => 'éª²',
  'óc' => 'éª³',
  'ód' => 'éª´',
  'óe' => 'éªµ',
  'óf' => 'éª¹',
  'óg' => 'éª»',
  'óh' => 'éª½',
  'ói' => 'éª¾',
  'ój' => 'éª¿',
  'ók' => 'é«ƒ',
  'ól' => 'é«„',
  'óm' => 'é«†',
  'ón' => 'é«‡',
  'óo' => 'é«ˆ',
  'óp' => 'é«‰',
  'óq' => 'é«Š',
  'ór' => 'é«�',
  'ós' => 'é«Ž',
  'ót' => 'é«�',
  'óu' => 'é«�',
  'óv' => 'é«’',
  'ów' => 'é«”',
  'óx' => 'é«•',
  'óy' => 'é«–',
  'óz' => 'é«—',
  'ó{' => 'é«™',
  'ó|' => 'é«š',
  'ó}' => 'é«›',
  'ó~' => 'é«œ',
  'ó€' => 'é«�',
  'ó�' => 'é«ž',
  'ó‚' => 'é« ',
  'óƒ' => 'é«¢',
  'ó„' => 'é«£',
  'ó…' => 'é«¤',
  'ó†' => 'é«¥',
  'ó‡' => 'é«§',
  'óˆ' => 'é«¨',
  'ó‰' => 'é«©',
  'óŠ' => 'é«ª',
  'ó‹' => 'é«¬',
  'óŒ' => 'é«®',
  'ó�' => 'é«°',
  'óŽ' => 'é«±',
  'ó�' => 'é«²',
  'ó�' => 'é«³',
  'ó‘' => 'é«´',
  'ó’' => 'é«µ',
  'ó“' => 'é«¶',
  'ó”' => 'é«·',
  'ó•' => 'é«¸',
  'ó–' => 'é«º',
  'ó—' => 'é«¼',
  'ó˜' => 'é«½',
  'ó™' => 'é«¾',
  'óš' => 'é«¿',
  'ó›' => 'é¬€',
  'óœ' => 'é¬�',
  'ó�' => 'é¬‚',
  'óž' => 'é¬„',
  'óŸ' => 'é¬…',
  'ó ' => 'é¬†',
  'ó¡' => 'èŸ†',
  'ó¢' => 'èžˆ',
  'ó£' => 'èž…',
  'ó¤' => 'èž­',
  'ó¥' => 'èž—',
  'ó¦' => 'èžƒ',
  'ó§' => 'èž«',
  'ó¨' => 'èŸ¥',
  'ó©' => 'èž¬',
  'óª' => 'èžµ',
  'ó«' => 'èž³',
  'ó¬' => 'èŸ‹',
  'ó­' => 'èŸ“',
  'ó®' => 'èž½',
  'ó¯' => 'èŸ‘',
  'ó°' => 'èŸ€',
  'ó±' => 'èŸŠ',
  'ó²' => 'èŸ›',
  'ó³' => 'èŸª',
  'ó´' => 'èŸ ',
  'óµ' => 'èŸ®',
  'ó¶' => 'è –',
  'ó·' => 'è “',
  'ó¸' => 'èŸ¾',
  'ó¹' => 'è Š',
  'óº' => 'è ›',
  'ó»' => 'è ¡',
  'ó¼' => 'è ¹',
  'ó½' => 'è ¼',
  'ó¾' => 'ç¼¶',
  'ó¿' => 'ç½‚',
  'óÀ' => 'ç½„',
  'óÁ' => 'ç½…',
  'óÂ' => 'èˆ�',
  'óÃ' => 'ç«º',
  'óÄ' => 'ç«½',
  'óÅ' => 'ç¬ˆ',
  'óÆ' => 'ç¬ƒ',
  'óÇ' => 'ç¬„',
  'óÈ' => 'ç¬•',
  'óÉ' => 'ç¬Š',
  'óÊ' => 'ç¬«',
  'óË' => 'ç¬�',
  'óÌ' => 'ç­‡',
  'óÍ' => 'ç¬¸',
  'óÎ' => 'ç¬ª',
  'óÏ' => 'ç¬™',
  'óÐ' => 'ç¬®',
  'óÑ' => 'ç¬±',
  'óÒ' => 'ç¬ ',
  'óÓ' => 'ç¬¥',
  'óÔ' => 'ç¬¤',
  'óÕ' => 'ç¬³',
  'óÖ' => 'ç¬¾',
  'ó×' => 'ç¬ž',
  'óØ' => 'ç­˜',
  'óÙ' => 'ç­š',
  'óÚ' => 'ç­…',
  'óÛ' => 'ç­µ',
  'óÜ' => 'ç­Œ',
  'óÝ' => 'ç­�',
  'óÞ' => 'ç­ ',
  'óß' => 'ç­®',
  'óà' => 'ç­»',
  'óá' => 'ç­¢',
  'óâ' => 'ç­²',
  'óã' => 'ç­±',
  'óä' => 'ç®�',
  'óå' => 'ç®¦',
  'óæ' => 'ç®§',
  'óç' => 'ç®¸',
  'óè' => 'ç®¬',
  'óé' => 'ç®�',
  'óê' => 'ç®¨',
  'óë' => 'ç®…',
  'óì' => 'ç®ª',
  'óí' => 'ç®œ',
  'óî' => 'ç®¢',
  'óï' => 'ç®«',
  'óð' => 'ç®´',
  'óñ' => 'ç¯‘',
  'óò' => 'ç¯�',
  'óó' => 'ç¯Œ',
  'óô' => 'ç¯�',
  'óõ' => 'ç¯š',
  'óö' => 'ç¯¥',
  'ó÷' => 'ç¯¦',
  'óø' => 'ç¯ª',
  'óù' => 'ç°Œ',
  'óú' => 'ç¯¾',
  'óû' => 'ç¯¼',
  'óü' => 'ç°�',
  'óý' => 'ç°–',
  'óþ' => 'ç°‹',
  'ô@' => 'é¬‡',
  'ôA' => 'é¬‰',
  'ôB' => 'é¬Š',
  'ôC' => 'é¬‹',
  'ôD' => 'é¬Œ',
  'ôE' => 'é¬�',
  'ôF' => 'é¬Ž',
  'ôG' => 'é¬�',
  'ôH' => 'é¬‘',
  'ôI' => 'é¬’',
  'ôJ' => 'é¬”',
  'ôK' => 'é¬•',
  'ôL' => 'é¬–',
  'ôM' => 'é¬—',
  'ôN' => 'é¬˜',
  'ôO' => 'é¬™',
  'ôP' => 'é¬š',
  'ôQ' => 'é¬›',
  'ôR' => 'é¬œ',
  'ôS' => 'é¬�',
  'ôT' => 'é¬ž',
  'ôU' => 'é¬ ',
  'ôV' => 'é¬¡',
  'ôW' => 'é¬¢',
  'ôX' => 'é¬¤',
  'ôY' => 'é¬¥',
  'ôZ' => 'é¬¦',
  'ô[' => 'é¬§',
  'ô\\' => 'é¬¨',
  'ô]' => 'é¬©',
  'ô^' => 'é¬ª',
  'ô_' => 'é¬«',
  'ô`' => 'é¬¬',
  'ôa' => 'é¬­',
  'ôb' => 'é¬®',
  'ôc' => 'é¬°',
  'ôd' => 'é¬±',
  'ôe' => 'é¬³',
  'ôf' => 'é¬´',
  'ôg' => 'é¬µ',
  'ôh' => 'é¬¶',
  'ôi' => 'é¬·',
  'ôj' => 'é¬¸',
  'ôk' => 'é¬¹',
  'ôl' => 'é¬º',
  'ôm' => 'é¬½',
  'ôn' => 'é¬¾',
  'ôo' => 'é¬¿',
  'ôp' => 'é­€',
  'ôq' => 'é­†',
  'ôr' => 'é­Š',
  'ôs' => 'é­‹',
  'ôt' => 'é­Œ',
  'ôu' => 'é­Ž',
  'ôv' => 'é­�',
  'ôw' => 'é­’',
  'ôx' => 'é­“',
  'ôy' => 'é­•',
  'ôz' => 'é­–',
  'ô{' => 'é­—',
  'ô|' => 'é­˜',
  'ô}' => 'é­™',
  'ô~' => 'é­š',
  'ô€' => 'é­›',
  'ô�' => 'é­œ',
  'ô‚' => 'é­�',
  'ôƒ' => 'é­ž',
  'ô„' => 'é­Ÿ',
  'ô…' => 'é­ ',
  'ô†' => 'é­¡',
  'ô‡' => 'é­¢',
  'ôˆ' => 'é­£',
  'ô‰' => 'é­¤',
  'ôŠ' => 'é­¥',
  'ô‹' => 'é­¦',
  'ôŒ' => 'é­§',
  'ô�' => 'é­¨',
  'ôŽ' => 'é­©',
  'ô�' => 'é­ª',
  'ô�' => 'é­«',
  'ô‘' => 'é­¬',
  'ô’' => 'é­­',
  'ô“' => 'é­®',
  'ô”' => 'é­¯',
  'ô•' => 'é­°',
  'ô–' => 'é­±',
  'ô—' => 'é­²',
  'ô˜' => 'é­³',
  'ô™' => 'é­´',
  'ôš' => 'é­µ',
  'ô›' => 'é­¶',
  'ôœ' => 'é­·',
  'ô�' => 'é­¸',
  'ôž' => 'é­¹',
  'ôŸ' => 'é­º',
  'ô ' => 'é­»',
  'ô¡' => 'ç°Ÿ',
  'ô¢' => 'ç°ª',
  'ô£' => 'ç°¦',
  'ô¤' => 'ç°¸',
  'ô¥' => 'ç±�',
  'ô¦' => 'ç±€',
  'ô§' => 'è‡¾',
  'ô¨' => 'èˆ�',
  'ô©' => 'èˆ‚',
  'ôª' => 'èˆ„',
  'ô«' => 'è‡¬',
  'ô¬' => 'è¡„',
  'ô­' => 'èˆ¡',
  'ô®' => 'èˆ¢',
  'ô¯' => 'èˆ£',
  'ô°' => 'èˆ­',
  'ô±' => 'èˆ¯',
  'ô²' => 'èˆ¨',
  'ô³' => 'èˆ«',
  'ô´' => 'èˆ¸',
  'ôµ' => 'èˆ»',
  'ô¶' => 'èˆ³',
  'ô·' => 'èˆ´',
  'ô¸' => 'èˆ¾',
  'ô¹' => 'è‰„',
  'ôº' => 'è‰‰',
  'ô»' => 'è‰‹',
  'ô¼' => 'è‰�',
  'ô½' => 'è‰š',
  'ô¾' => 'è‰Ÿ',
  'ô¿' => 'è‰¨',
  'ôÀ' => 'è¡¾',
  'ôÁ' => 'è¢…',
  'ôÂ' => 'è¢ˆ',
  'ôÃ' => 'è£˜',
  'ôÄ' => 'è£Ÿ',
  'ôÅ' => 'è¥ž',
  'ôÆ' => 'ç¾�',
  'ôÇ' => 'ç¾Ÿ',
  'ôÈ' => 'ç¾§',
  'ôÉ' => 'ç¾¯',
  'ôÊ' => 'ç¾°',
  'ôË' => 'ç¾²',
  'ôÌ' => 'ç±¼',
  'ôÍ' => 'æ•‰',
  'ôÎ' => 'ç²‘',
  'ôÏ' => 'ç²�',
  'ôÐ' => 'ç²œ',
  'ôÑ' => 'ç²ž',
  'ôÒ' => 'ç²¢',
  'ôÓ' => 'ç²²',
  'ôÔ' => 'ç²¼',
  'ôÕ' => 'ç²½',
  'ôÖ' => 'ç³�',
  'ô×' => 'ç³‡',
  'ôØ' => 'ç³Œ',
  'ôÙ' => 'ç³�',
  'ôÚ' => 'ç³ˆ',
  'ôÛ' => 'ç³…',
  'ôÜ' => 'ç³—',
  'ôÝ' => 'ç³¨',
  'ôÞ' => 'è‰®',
  'ôß' => 'æš¨',
  'ôà' => 'ç¾¿',
  'ôá' => 'ç¿Ž',
  'ôâ' => 'ç¿•',
  'ôã' => 'ç¿¥',
  'ôä' => 'ç¿¡',
  'ôå' => 'ç¿¦',
  'ôæ' => 'ç¿©',
  'ôç' => 'ç¿®',
  'ôè' => 'ç¿³',
  'ôé' => 'ç³¸',
  'ôê' => 'çµ·',
  'ôë' => 'ç¶¦',
  'ôì' => 'ç¶®',
  'ôí' => 'ç¹‡',
  'ôî' => 'çº›',
  'ôï' => 'éº¸',
  'ôð' => 'éº´',
  'ôñ' => 'èµ³',
  'ôò' => 'è¶„',
  'ôó' => 'è¶”',
  'ôô' => 'è¶‘',
  'ôõ' => 'è¶±',
  'ôö' => 'èµ§',
  'ô÷' => 'èµ­',
  'ôø' => 'è±‡',
  'ôù' => 'è±‰',
  'ôú' => 'é…Š',
  'ôû' => 'é…�',
  'ôü' => 'é…Ž',
  'ôý' => 'é…�',
  'ôþ' => 'é…¤',
  'õ@' => 'é­¼',
  'õA' => 'é­½',
  'õB' => 'é­¾',
  'õC' => 'é­¿',
  'õD' => 'é®€',
  'õE' => 'é®�',
  'õF' => 'é®‚',
  'õG' => 'é®ƒ',
  'õH' => 'é®„',
  'õI' => 'é®…',
  'õJ' => 'é®†',
  'õK' => 'é®‡',
  'õL' => 'é®ˆ',
  'õM' => 'é®‰',
  'õN' => 'é®Š',
  'õO' => 'é®‹',
  'õP' => 'é®Œ',
  'õQ' => 'é®�',
  'õR' => 'é®Ž',
  'õS' => 'é®�',
  'õT' => 'é®�',
  'õU' => 'é®‘',
  'õV' => 'é®’',
  'õW' => 'é®“',
  'õX' => 'é®”',
  'õY' => 'é®•',
  'õZ' => 'é®–',
  'õ[' => 'é®—',
  'õ\\' => 'é®˜',
  'õ]' => 'é®™',
  'õ^' => 'é®š',
  'õ_' => 'é®›',
  'õ`' => 'é®œ',
  'õa' => 'é®�',
  'õb' => 'é®ž',
  'õc' => 'é®Ÿ',
  'õd' => 'é® ',
  'õe' => 'é®¡',
  'õf' => 'é®¢',
  'õg' => 'é®£',
  'õh' => 'é®¤',
  'õi' => 'é®¥',
  'õj' => 'é®¦',
  'õk' => 'é®§',
  'õl' => 'é®¨',
  'õm' => 'é®©',
  'õn' => 'é®ª',
  'õo' => 'é®«',
  'õp' => 'é®¬',
  'õq' => 'é®­',
  'õr' => 'é®®',
  'õs' => 'é®¯',
  'õt' => 'é®°',
  'õu' => 'é®±',
  'õv' => 'é®²',
  'õw' => 'é®³',
  'õx' => 'é®´',
  'õy' => 'é®µ',
  'õz' => 'é®¶',
  'õ{' => 'é®·',
  'õ|' => 'é®¸',
  'õ}' => 'é®¹',
  'õ~' => 'é®º',
  'õ€' => 'é®»',
  'õ�' => 'é®¼',
  'õ‚' => 'é®½',
  'õƒ' => 'é®¾',
  'õ„' => 'é®¿',
  'õ…' => 'é¯€',
  'õ†' => 'é¯�',
  'õ‡' => 'é¯‚',
  'õˆ' => 'é¯ƒ',
  'õ‰' => 'é¯„',
  'õŠ' => 'é¯…',
  'õ‹' => 'é¯†',
  'õŒ' => 'é¯‡',
  'õ�' => 'é¯ˆ',
  'õŽ' => 'é¯‰',
  'õ�' => 'é¯Š',
  'õ�' => 'é¯‹',
  'õ‘' => 'é¯Œ',
  'õ’' => 'é¯�',
  'õ“' => 'é¯Ž',
  'õ”' => 'é¯�',
  'õ•' => 'é¯�',
  'õ–' => 'é¯‘',
  'õ—' => 'é¯’',
  'õ˜' => 'é¯“',
  'õ™' => 'é¯”',
  'õš' => 'é¯•',
  'õ›' => 'é¯–',
  'õœ' => 'é¯—',
  'õ�' => 'é¯˜',
  'õž' => 'é¯™',
  'õŸ' => 'é¯š',
  'õ ' => 'é¯›',
  'õ¡' => 'é…¢',
  'õ¢' => 'é…¡',
  'õ£' => 'é…°',
  'õ¤' => 'é…©',
  'õ¥' => 'é…¯',
  'õ¦' => 'é…½',
  'õ§' => 'é…¾',
  'õ¨' => 'é…²',
  'õ©' => 'é…´',
  'õª' => 'é…¹',
  'õ«' => 'é†Œ',
  'õ¬' => 'é†…',
  'õ­' => 'é†�',
  'õ®' => 'é†�',
  'õ¯' => 'é†‘',
  'õ°' => 'é†¢',
  'õ±' => 'é†£',
  'õ²' => 'é†ª',
  'õ³' => 'é†­',
  'õ´' => 'é†®',
  'õµ' => 'é†¯',
  'õ¶' => 'é†µ',
  'õ·' => 'é†´',
  'õ¸' => 'é†º',
  'õ¹' => 'è±•',
  'õº' => 'é¹¾',
  'õ»' => 'è¶¸',
  'õ¼' => 'è·«',
  'õ½' => 'è¸…',
  'õ¾' => 'è¹™',
  'õ¿' => 'è¹©',
  'õÀ' => 'è¶µ',
  'õÁ' => 'è¶¿',
  'õÂ' => 'è¶¼',
  'õÃ' => 'è¶º',
  'õÄ' => 'è·„',
  'õÅ' => 'è·–',
  'õÆ' => 'è·—',
  'õÇ' => 'è·š',
  'õÈ' => 'è·ž',
  'õÉ' => 'è·Ž',
  'õÊ' => 'è·�',
  'õË' => 'è·›',
  'õÌ' => 'è·†',
  'õÍ' => 'è·¬',
  'õÎ' => 'è··',
  'õÏ' => 'è·¸',
  'õÐ' => 'è·£',
  'õÑ' => 'è·¹',
  'õÒ' => 'è·»',
  'õÓ' => 'è·¤',
  'õÔ' => 'è¸‰',
  'õÕ' => 'è·½',
  'õÖ' => 'è¸”',
  'õ×' => 'è¸�',
  'õØ' => 'è¸Ÿ',
  'õÙ' => 'è¸¬',
  'õÚ' => 'è¸®',
  'õÛ' => 'è¸£',
  'õÜ' => 'è¸¯',
  'õÝ' => 'è¸º',
  'õÞ' => 'è¹€',
  'õß' => 'è¸¹',
  'õà' => 'è¸µ',
  'õá' => 'è¸½',
  'õâ' => 'è¸±',
  'õã' => 'è¹‰',
  'õä' => 'è¹�',
  'õå' => 'è¹‚',
  'õæ' => 'è¹‘',
  'õç' => 'è¹’',
  'õè' => 'è¹Š',
  'õé' => 'è¹°',
  'õê' => 'è¹¶',
  'õë' => 'è¹¼',
  'õì' => 'è¹¯',
  'õí' => 'è¹´',
  'õî' => 'èº…',
  'õï' => 'èº�',
  'õð' => 'èº”',
  'õñ' => 'èº�',
  'õò' => 'èºœ',
  'õó' => 'èºž',
  'õô' => 'è±¸',
  'õõ' => 'è²‚',
  'õö' => 'è²Š',
  'õ÷' => 'è²…',
  'õø' => 'è²˜',
  'õù' => 'è²”',
  'õú' => 'æ–›',
  'õû' => 'è§–',
  'õü' => 'è§ž',
  'õý' => 'è§š',
  'õþ' => 'è§œ',
  'ö@' => 'é¯œ',
  'öA' => 'é¯�',
  'öB' => 'é¯ž',
  'öC' => 'é¯Ÿ',
  'öD' => 'é¯ ',
  'öE' => 'é¯¡',
  'öF' => 'é¯¢',
  'öG' => 'é¯£',
  'öH' => 'é¯¤',
  'öI' => 'é¯¥',
  'öJ' => 'é¯¦',
  'öK' => 'é¯§',
  'öL' => 'é¯¨',
  'öM' => 'é¯©',
  'öN' => 'é¯ª',
  'öO' => 'é¯«',
  'öP' => 'é¯¬',
  'öQ' => 'é¯­',
  'öR' => 'é¯®',
  'öS' => 'é¯¯',
  'öT' => 'é¯°',
  'öU' => 'é¯±',
  'öV' => 'é¯²',
  'öW' => 'é¯³',
  'öX' => 'é¯´',
  'öY' => 'é¯µ',
  'öZ' => 'é¯¶',
  'ö[' => 'é¯·',
  'ö\\' => 'é¯¸',
  'ö]' => 'é¯¹',
  'ö^' => 'é¯º',
  'ö_' => 'é¯»',
  'ö`' => 'é¯¼',
  'öa' => 'é¯½',
  'öb' => 'é¯¾',
  'öc' => 'é¯¿',
  'öd' => 'é°€',
  'öe' => 'é°�',
  'öf' => 'é°‚',
  'ög' => 'é°ƒ',
  'öh' => 'é°„',
  'öi' => 'é°…',
  'öj' => 'é°†',
  'ök' => 'é°‡',
  'öl' => 'é°ˆ',
  'öm' => 'é°‰',
  'ön' => 'é°Š',
  'öo' => 'é°‹',
  'öp' => 'é°Œ',
  'öq' => 'é°�',
  'ör' => 'é°Ž',
  'ös' => 'é°�',
  'öt' => 'é°�',
  'öu' => 'é°‘',
  'öv' => 'é°’',
  'öw' => 'é°“',
  'öx' => 'é°”',
  'öy' => 'é°•',
  'öz' => 'é°–',
  'ö{' => 'é°—',
  'ö|' => 'é°˜',
  'ö}' => 'é°™',
  'ö~' => 'é°š',
  'ö€' => 'é°›',
  'ö�' => 'é°œ',
  'ö‚' => 'é°�',
  'öƒ' => 'é°ž',
  'ö„' => 'é°Ÿ',
  'ö…' => 'é° ',
  'ö†' => 'é°¡',
  'ö‡' => 'é°¢',
  'öˆ' => 'é°£',
  'ö‰' => 'é°¤',
  'öŠ' => 'é°¥',
  'ö‹' => 'é°¦',
  'öŒ' => 'é°§',
  'ö�' => 'é°¨',
  'öŽ' => 'é°©',
  'ö�' => 'é°ª',
  'ö�' => 'é°«',
  'ö‘' => 'é°¬',
  'ö’' => 'é°­',
  'ö“' => 'é°®',
  'ö”' => 'é°¯',
  'ö•' => 'é°°',
  'ö–' => 'é°±',
  'ö—' => 'é°²',
  'ö˜' => 'é°³',
  'ö™' => 'é°´',
  'öš' => 'é°µ',
  'ö›' => 'é°¶',
  'öœ' => 'é°·',
  'ö�' => 'é°¸',
  'öž' => 'é°¹',
  'öŸ' => 'é°º',
  'ö ' => 'é°»',
  'ö¡' => 'è§¥',
  'ö¢' => 'è§«',
  'ö£' => 'è§¯',
  'ö¤' => 'è¨¾',
  'ö¥' => 'è¬¦',
  'ö¦' => 'é�“',
  'ö§' => 'é›©',
  'ö¨' => 'é›³',
  'ö©' => 'é›¯',
  'öª' => 'éœ†',
  'ö«' => 'éœ�',
  'ö¬' => 'éœˆ',
  'ö­' => 'éœ�',
  'ö®' => 'éœŽ',
  'ö¯' => 'éœª',
  'ö°' => 'éœ­',
  'ö±' => 'éœ°',
  'ö²' => 'éœ¾',
  'ö³' => 'é¾€',
  'ö´' => 'é¾ƒ',
  'öµ' => 'é¾…',
  'ö¶' => 'é¾†',
  'ö·' => 'é¾‡',
  'ö¸' => 'é¾ˆ',
  'ö¹' => 'é¾‰',
  'öº' => 'é¾Š',
  'ö»' => 'é¾Œ',
  'ö¼' => 'é»¾',
  'ö½' => 'é¼‹',
  'ö¾' => 'é¼�',
  'ö¿' => 'éš¹',
  'öÀ' => 'éš¼',
  'öÁ' => 'éš½',
  'öÂ' => 'é›Ž',
  'öÃ' => 'é›’',
  'öÄ' => 'çž¿',
  'öÅ' => 'é› ',
  'öÆ' => 'éŠŽ',
  'öÇ' => 'éŠ®',
  'öÈ' => 'é‹ˆ',
  'öÉ' => 'éŒ¾',
  'öÊ' => 'é�ª',
  'öË' => 'é�Š',
  'öÌ' => 'éŽ�',
  'öÍ' => 'é�¾',
  'öÎ' => 'é‘«',
  'öÏ' => 'é±¿',
  'öÐ' => 'é²‚',
  'öÑ' => 'é²…',
  'öÒ' => 'é²†',
  'öÓ' => 'é²‡',
  'öÔ' => 'é²ˆ',
  'öÕ' => 'ç¨£',
  'öÖ' => 'é²‹',
  'ö×' => 'é²Ž',
  'öØ' => 'é²�',
  'öÙ' => 'é²‘',
  'öÚ' => 'é²’',
  'öÛ' => 'é²”',
  'öÜ' => 'é²•',
  'öÝ' => 'é²š',
  'öÞ' => 'é²›',
  'öß' => 'é²ž',
  'öà' => 'é²Ÿ',
  'öá' => 'é² ',
  'öâ' => 'é²¡',
  'öã' => 'é²¢',
  'öä' => 'é²£',
  'öå' => 'é²¥',
  'öæ' => 'é²¦',
  'öç' => 'é²§',
  'öè' => 'é²¨',
  'öé' => 'é²©',
  'öê' => 'é²«',
  'öë' => 'é²­',
  'öì' => 'é²®',
  'öí' => 'é²°',
  'öî' => 'é²±',
  'öï' => 'é²²',
  'öð' => 'é²³',
  'öñ' => 'é²´',
  'öò' => 'é²µ',
  'öó' => 'é²¶',
  'öô' => 'é²·',
  'öõ' => 'é²º',
  'öö' => 'é²»',
  'ö÷' => 'é²¼',
  'öø' => 'é²½',
  'öù' => 'é³„',
  'öú' => 'é³…',
  'öû' => 'é³†',
  'öü' => 'é³‡',
  'öý' => 'é³Š',
  'öþ' => 'é³‹',
  '÷@' => 'é°¼',
  '÷A' => 'é°½',
  '÷B' => 'é°¾',
  '÷C' => 'é°¿',
  '÷D' => 'é±€',
  '÷E' => 'é±�',
  '÷F' => 'é±‚',
  '÷G' => 'é±ƒ',
  '÷H' => 'é±„',
  '÷I' => 'é±…',
  '÷J' => 'é±†',
  '÷K' => 'é±‡',
  '÷L' => 'é±ˆ',
  '÷M' => 'é±‰',
  '÷N' => 'é±Š',
  '÷O' => 'é±‹',
  '÷P' => 'é±Œ',
  '÷Q' => 'é±�',
  '÷R' => 'é±Ž',
  '÷S' => 'é±�',
  '÷T' => 'é±�',
  '÷U' => 'é±‘',
  '÷V' => 'é±’',
  '÷W' => 'é±“',
  '÷X' => 'é±”',
  '÷Y' => 'é±•',
  '÷Z' => 'é±–',
  '÷[' => 'é±—',
  '÷\\' => 'é±˜',
  '÷]' => 'é±™',
  '÷^' => 'é±š',
  '÷_' => 'é±›',
  '÷`' => 'é±œ',
  '÷a' => 'é±�',
  '÷b' => 'é±ž',
  '÷c' => 'é±Ÿ',
  '÷d' => 'é± ',
  '÷e' => 'é±¡',
  '÷f' => 'é±¢',
  '÷g' => 'é±£',
  '÷h' => 'é±¤',
  '÷i' => 'é±¥',
  '÷j' => 'é±¦',
  '÷k' => 'é±§',
  '÷l' => 'é±¨',
  '÷m' => 'é±©',
  '÷n' => 'é±ª',
  '÷o' => 'é±«',
  '÷p' => 'é±¬',
  '÷q' => 'é±­',
  '÷r' => 'é±®',
  '÷s' => 'é±¯',
  '÷t' => 'é±°',
  '÷u' => 'é±±',
  '÷v' => 'é±²',
  '÷w' => 'é±³',
  '÷x' => 'é±´',
  '÷y' => 'é±µ',
  '÷z' => 'é±¶',
  '÷{' => 'é±·',
  '÷|' => 'é±¸',
  '÷}' => 'é±¹',
  '÷~' => 'é±º',
  '÷€' => 'é±»',
  '÷�' => 'é±½',
  '÷‚' => 'é±¾',
  '÷ƒ' => 'é²€',
  '÷„' => 'é²ƒ',
  '÷…' => 'é²„',
  '÷†' => 'é²‰',
  '÷‡' => 'é²Š',
  '÷ˆ' => 'é²Œ',
  '÷‰' => 'é²�',
  '÷Š' => 'é²“',
  '÷‹' => 'é²–',
  '÷Œ' => 'é²—',
  '÷�' => 'é²˜',
  '÷Ž' => 'é²™',
  '÷�' => 'é²�',
  '÷�' => 'é²ª',
  '÷‘' => 'é²¬',
  '÷’' => 'é²¯',
  '÷“' => 'é²¹',
  '÷”' => 'é²¾',
  '÷•' => 'é²¿',
  '÷–' => 'é³€',
  '÷—' => 'é³�',
  '÷˜' => 'é³‚',
  '÷™' => 'é³ˆ',
  '÷š' => 'é³‰',
  '÷›' => 'é³‘',
  '÷œ' => 'é³’',
  '÷�' => 'é³š',
  '÷ž' => 'é³›',
  '÷Ÿ' => 'é³ ',
  '÷ ' => 'é³¡',
  '÷¡' => 'é³Œ',
  '÷¢' => 'é³�',
  '÷£' => 'é³Ž',
  '÷¤' => 'é³�',
  '÷¥' => 'é³�',
  '÷¦' => 'é³“',
  '÷§' => 'é³”',
  '÷¨' => 'é³•',
  '÷©' => 'é³—',
  '÷ª' => 'é³˜',
  '÷«' => 'é³™',
  '÷¬' => 'é³œ',
  '÷­' => 'é³�',
  '÷®' => 'é³Ÿ',
  '÷¯' => 'é³¢',
  '÷°' => 'é�¼',
  '÷±' => 'éž…',
  '÷²' => 'éž‘',
  '÷³' => 'éž’',
  '÷´' => 'éž”',
  '÷µ' => 'éž¯',
  '÷¶' => 'éž«',
  '÷·' => 'éž£',
  '÷¸' => 'éž²',
  '÷¹' => 'éž´',
  '÷º' => 'éª±',
  '÷»' => 'éª°',
  '÷¼' => 'éª·',
  '÷½' => 'é¹˜',
  '÷¾' => 'éª¶',
  '÷¿' => 'éªº',
  '÷À' => 'éª¼',
  '÷Á' => 'é«�',
  '÷Â' => 'é«€',
  '÷Ã' => 'é«…',
  '÷Ä' => 'é«‚',
  '÷Å' => 'é«‹',
  '÷Æ' => 'é«Œ',
  '÷Ç' => 'é«‘',
  '÷È' => 'é­…',
  '÷É' => 'é­ƒ',
  '÷Ê' => 'é­‡',
  '÷Ë' => 'é­‰',
  '÷Ì' => 'é­ˆ',
  '÷Í' => 'é­�',
  '÷Î' => 'é­‘',
  '÷Ï' => 'é£¨',
  '÷Ð' => 'é¤�',
  '÷Ñ' => 'é¤®',
  '÷Ò' => 'é¥•',
  '÷Ó' => 'é¥”',
  '÷Ô' => 'é«Ÿ',
  '÷Õ' => 'é«¡',
  '÷Ö' => 'é«¦',
  '÷×' => 'é«¯',
  '÷Ø' => 'é««',
  '÷Ù' => 'é«»',
  '÷Ú' => 'é«­',
  '÷Û' => 'é«¹',
  '÷Ü' => 'é¬ˆ',
  '÷Ý' => 'é¬�',
  '÷Þ' => 'é¬“',
  '÷ß' => 'é¬Ÿ',
  '÷à' => 'é¬£',
  '÷á' => 'éº½',
  '÷â' => 'éº¾',
  '÷ã' => 'ç¸»',
  '÷ä' => 'éº‚',
  '÷å' => 'éº‡',
  '÷æ' => 'éºˆ',
  '÷ç' => 'éº‹',
  '÷è' => 'éº’',
  '÷é' => 'é�–',
  '÷ê' => 'éº�',
  '÷ë' => 'éºŸ',
  '÷ì' => 'é»›',
  '÷í' => 'é»œ',
  '÷î' => 'é»�',
  '÷ï' => 'é» ',
  '÷ð' => 'é»Ÿ',
  '÷ñ' => 'é»¢',
  '÷ò' => 'é»©',
  '÷ó' => 'é»§',
  '÷ô' => 'é»¥',
  '÷õ' => 'é»ª',
  '÷ö' => 'é»¯',
  '÷÷' => 'é¼¢',
  '÷ø' => 'é¼¬',
  '÷ù' => 'é¼¯',
  '÷ú' => 'é¼¹',
  '÷û' => 'é¼·',
  '÷ü' => 'é¼½',
  '÷ý' => 'é¼¾',
  '÷þ' => 'é½„',
  'ø@' => 'é³£',
  'øA' => 'é³¤',
  'øB' => 'é³¥',
  'øC' => 'é³¦',
  'øD' => 'é³§',
  'øE' => 'é³¨',
  'øF' => 'é³©',
  'øG' => 'é³ª',
  'øH' => 'é³«',
  'øI' => 'é³¬',
  'øJ' => 'é³­',
  'øK' => 'é³®',
  'øL' => 'é³¯',
  'øM' => 'é³°',
  'øN' => 'é³±',
  'øO' => 'é³²',
  'øP' => 'é³³',
  'øQ' => 'é³´',
  'øR' => 'é³µ',
  'øS' => 'é³¶',
  'øT' => 'é³·',
  'øU' => 'é³¸',
  'øV' => 'é³¹',
  'øW' => 'é³º',
  'øX' => 'é³»',
  'øY' => 'é³¼',
  'øZ' => 'é³½',
  'ø[' => 'é³¾',
  'ø\\' => 'é³¿',
  'ø]' => 'é´€',
  'ø^' => 'é´�',
  'ø_' => 'é´‚',
  'ø`' => 'é´ƒ',
  'øa' => 'é´„',
  'øb' => 'é´…',
  'øc' => 'é´†',
  'ød' => 'é´‡',
  'øe' => 'é´ˆ',
  'øf' => 'é´‰',
  'øg' => 'é´Š',
  'øh' => 'é´‹',
  'øi' => 'é´Œ',
  'øj' => 'é´�',
  'øk' => 'é´Ž',
  'øl' => 'é´�',
  'øm' => 'é´�',
  'øn' => 'é´‘',
  'øo' => 'é´’',
  'øp' => 'é´“',
  'øq' => 'é´”',
  'ør' => 'é´•',
  'øs' => 'é´–',
  'øt' => 'é´—',
  'øu' => 'é´˜',
  'øv' => 'é´™',
  'øw' => 'é´š',
  'øx' => 'é´›',
  'øy' => 'é´œ',
  'øz' => 'é´�',
  'ø{' => 'é´ž',
  'ø|' => 'é´Ÿ',
  'ø}' => 'é´ ',
  'ø~' => 'é´¡',
  'ø€' => 'é´¢',
  'ø�' => 'é´£',
  'ø‚' => 'é´¤',
  'øƒ' => 'é´¥',
  'ø„' => 'é´¦',
  'ø…' => 'é´§',
  'ø†' => 'é´¨',
  'ø‡' => 'é´©',
  'øˆ' => 'é´ª',
  'ø‰' => 'é´«',
  'øŠ' => 'é´¬',
  'ø‹' => 'é´­',
  'øŒ' => 'é´®',
  'ø�' => 'é´¯',
  'øŽ' => 'é´°',
  'ø�' => 'é´±',
  'ø�' => 'é´²',
  'ø‘' => 'é´³',
  'ø’' => 'é´´',
  'ø“' => 'é´µ',
  'ø”' => 'é´¶',
  'ø•' => 'é´·',
  'ø–' => 'é´¸',
  'ø—' => 'é´¹',
  'ø˜' => 'é´º',
  'ø™' => 'é´»',
  'øš' => 'é´¼',
  'ø›' => 'é´½',
  'øœ' => 'é´¾',
  'ø�' => 'é´¿',
  'øž' => 'éµ€',
  'øŸ' => 'éµ�',
  'ø ' => 'éµ‚',
  'ù@' => 'éµƒ',
  'ùA' => 'éµ„',
  'ùB' => 'éµ…',
  'ùC' => 'éµ†',
  'ùD' => 'éµ‡',
  'ùE' => 'éµˆ',
  'ùF' => 'éµ‰',
  'ùG' => 'éµŠ',
  'ùH' => 'éµ‹',
  'ùI' => 'éµŒ',
  'ùJ' => 'éµ�',
  'ùK' => 'éµŽ',
  'ùL' => 'éµ�',
  'ùM' => 'éµ�',
  'ùN' => 'éµ‘',
  'ùO' => 'éµ’',
  'ùP' => 'éµ“',
  'ùQ' => 'éµ”',
  'ùR' => 'éµ•',
  'ùS' => 'éµ–',
  'ùT' => 'éµ—',
  'ùU' => 'éµ˜',
  'ùV' => 'éµ™',
  'ùW' => 'éµš',
  'ùX' => 'éµ›',
  'ùY' => 'éµœ',
  'ùZ' => 'éµ�',
  'ù[' => 'éµž',
  'ù\\' => 'éµŸ',
  'ù]' => 'éµ ',
  'ù^' => 'éµ¡',
  'ù_' => 'éµ¢',
  'ù`' => 'éµ£',
  'ùa' => 'éµ¤',
  'ùb' => 'éµ¥',
  'ùc' => 'éµ¦',
  'ùd' => 'éµ§',
  'ùe' => 'éµ¨',
  'ùf' => 'éµ©',
  'ùg' => 'éµª',
  'ùh' => 'éµ«',
  'ùi' => 'éµ¬',
  'ùj' => 'éµ­',
  'ùk' => 'éµ®',
  'ùl' => 'éµ¯',
  'ùm' => 'éµ°',
  'ùn' => 'éµ±',
  'ùo' => 'éµ²',
  'ùp' => 'éµ³',
  'ùq' => 'éµ´',
  'ùr' => 'éµµ',
  'ùs' => 'éµ¶',
  'ùt' => 'éµ·',
  'ùu' => 'éµ¸',
  'ùv' => 'éµ¹',
  'ùw' => 'éµº',
  'ùx' => 'éµ»',
  'ùy' => 'éµ¼',
  'ùz' => 'éµ½',
  'ù{' => 'éµ¾',
  'ù|' => 'éµ¿',
  'ù}' => 'é¶€',
  'ù~' => 'é¶�',
  'ù€' => 'é¶‚',
  'ù�' => 'é¶ƒ',
  'ù‚' => 'é¶„',
  'ùƒ' => 'é¶…',
  'ù„' => 'é¶†',
  'ù…' => 'é¶‡',
  'ù†' => 'é¶ˆ',
  'ù‡' => 'é¶‰',
  'ùˆ' => 'é¶Š',
  'ù‰' => 'é¶‹',
  'ùŠ' => 'é¶Œ',
  'ù‹' => 'é¶�',
  'ùŒ' => 'é¶Ž',
  'ù�' => 'é¶�',
  'ùŽ' => 'é¶�',
  'ù�' => 'é¶‘',
  'ù�' => 'é¶’',
  'ù‘' => 'é¶“',
  'ù’' => 'é¶”',
  'ù“' => 'é¶•',
  'ù”' => 'é¶–',
  'ù•' => 'é¶—',
  'ù–' => 'é¶˜',
  'ù—' => 'é¶™',
  'ù˜' => 'é¶š',
  'ù™' => 'é¶›',
  'ùš' => 'é¶œ',
  'ù›' => 'é¶�',
  'ùœ' => 'é¶ž',
  'ù�' => 'é¶Ÿ',
  'ùž' => 'é¶ ',
  'ùŸ' => 'é¶¡',
  'ù ' => 'é¶¢',
  'ú@' => 'é¶£',
  'úA' => 'é¶¤',
  'úB' => 'é¶¥',
  'úC' => 'é¶¦',
  'úD' => 'é¶§',
  'úE' => 'é¶¨',
  'úF' => 'é¶©',
  'úG' => 'é¶ª',
  'úH' => 'é¶«',
  'úI' => 'é¶¬',
  'úJ' => 'é¶­',
  'úK' => 'é¶®',
  'úL' => 'é¶¯',
  'úM' => 'é¶°',
  'úN' => 'é¶±',
  'úO' => 'é¶²',
  'úP' => 'é¶³',
  'úQ' => 'é¶´',
  'úR' => 'é¶µ',
  'úS' => 'é¶¶',
  'úT' => 'é¶·',
  'úU' => 'é¶¸',
  'úV' => 'é¶¹',
  'úW' => 'é¶º',
  'úX' => 'é¶»',
  'úY' => 'é¶¼',
  'úZ' => 'é¶½',
  'ú[' => 'é¶¾',
  'ú\\' => 'é¶¿',
  'ú]' => 'é·€',
  'ú^' => 'é·�',
  'ú_' => 'é·‚',
  'ú`' => 'é·ƒ',
  'úa' => 'é·„',
  'úb' => 'é·…',
  'úc' => 'é·†',
  'úd' => 'é·‡',
  'úe' => 'é·ˆ',
  'úf' => 'é·‰',
  'úg' => 'é·Š',
  'úh' => 'é·‹',
  'úi' => 'é·Œ',
  'új' => 'é·�',
  'úk' => 'é·Ž',
  'úl' => 'é·�',
  'úm' => 'é·�',
  'ún' => 'é·‘',
  'úo' => 'é·’',
  'úp' => 'é·“',
  'úq' => 'é·”',
  'úr' => 'é·•',
  'ús' => 'é·–',
  'út' => 'é·—',
  'úu' => 'é·˜',
  'úv' => 'é·™',
  'úw' => 'é·š',
  'úx' => 'é·›',
  'úy' => 'é·œ',
  'úz' => 'é·�',
  'ú{' => 'é·ž',
  'ú|' => 'é·Ÿ',
  'ú}' => 'é· ',
  'ú~' => 'é·¡',
  'ú€' => 'é·¢',
  'ú�' => 'é·£',
  'ú‚' => 'é·¤',
  'úƒ' => 'é·¥',
  'ú„' => 'é·¦',
  'ú…' => 'é·§',
  'ú†' => 'é·¨',
  'ú‡' => 'é·©',
  'úˆ' => 'é·ª',
  'ú‰' => 'é·«',
  'úŠ' => 'é·¬',
  'ú‹' => 'é·­',
  'úŒ' => 'é·®',
  'ú�' => 'é·¯',
  'úŽ' => 'é·°',
  'ú�' => 'é·±',
  'ú�' => 'é·²',
  'ú‘' => 'é·³',
  'ú’' => 'é·´',
  'ú“' => 'é·µ',
  'ú”' => 'é·¶',
  'ú•' => 'é··',
  'ú–' => 'é·¸',
  'ú—' => 'é·¹',
  'ú˜' => 'é·º',
  'ú™' => 'é·»',
  'úš' => 'é·¼',
  'ú›' => 'é·½',
  'úœ' => 'é·¾',
  'ú�' => 'é·¿',
  'úž' => 'é¸€',
  'úŸ' => 'é¸�',
  'ú ' => 'é¸‚',
  'û@' => 'é¸ƒ',
  'ûA' => 'é¸„',
  'ûB' => 'é¸…',
  'ûC' => 'é¸†',
  'ûD' => 'é¸‡',
  'ûE' => 'é¸ˆ',
  'ûF' => 'é¸‰',
  'ûG' => 'é¸Š',
  'ûH' => 'é¸‹',
  'ûI' => 'é¸Œ',
  'ûJ' => 'é¸�',
  'ûK' => 'é¸Ž',
  'ûL' => 'é¸�',
  'ûM' => 'é¸�',
  'ûN' => 'é¸‘',
  'ûO' => 'é¸’',
  'ûP' => 'é¸“',
  'ûQ' => 'é¸”',
  'ûR' => 'é¸•',
  'ûS' => 'é¸–',
  'ûT' => 'é¸—',
  'ûU' => 'é¸˜',
  'ûV' => 'é¸™',
  'ûW' => 'é¸š',
  'ûX' => 'é¸›',
  'ûY' => 'é¸œ',
  'ûZ' => 'é¸�',
  'û[' => 'é¸ž',
  'û\\' => 'é¸¤',
  'û]' => 'é¸§',
  'û^' => 'é¸®',
  'û_' => 'é¸°',
  'û`' => 'é¸´',
  'ûa' => 'é¸»',
  'ûb' => 'é¸¼',
  'ûc' => 'é¹€',
  'ûd' => 'é¹�',
  'ûe' => 'é¹�',
  'ûf' => 'é¹’',
  'ûg' => 'é¹“',
  'ûh' => 'é¹”',
  'ûi' => 'é¹–',
  'ûj' => 'é¹™',
  'ûk' => 'é¹�',
  'ûl' => 'é¹Ÿ',
  'ûm' => 'é¹ ',
  'ûn' => 'é¹¡',
  'ûo' => 'é¹¢',
  'ûp' => 'é¹¥',
  'ûq' => 'é¹®',
  'ûr' => 'é¹¯',
  'ûs' => 'é¹²',
  'ût' => 'é¹´',
  'ûu' => 'é¹µ',
  'ûv' => 'é¹¶',
  'ûw' => 'é¹·',
  'ûx' => 'é¹¸',
  'ûy' => 'é¹¹',
  'ûz' => 'é¹º',
  'û{' => 'é¹»',
  'û|' => 'é¹¼',
  'û}' => 'é¹½',
  'û~' => 'éº€',
  'û€' => 'éº�',
  'û�' => 'éºƒ',
  'û‚' => 'éº„',
  'ûƒ' => 'éº…',
  'û„' => 'éº†',
  'û…' => 'éº‰',
  'û†' => 'éºŠ',
  'û‡' => 'éºŒ',
  'ûˆ' => 'éº�',
  'û‰' => 'éºŽ',
  'ûŠ' => 'éº�',
  'û‹' => 'éº�',
  'ûŒ' => 'éº‘',
  'û�' => 'éº”',
  'ûŽ' => 'éº•',
  'û�' => 'éº–',
  'û�' => 'éº—',
  'û‘' => 'éº˜',
  'û’' => 'éº™',
  'û“' => 'éºš',
  'û”' => 'éº›',
  'û•' => 'éºœ',
  'û–' => 'éºž',
  'û—' => 'éº ',
  'û˜' => 'éº¡',
  'û™' => 'éº¢',
  'ûš' => 'éº£',
  'û›' => 'éº¤',
  'ûœ' => 'éº¥',
  'û�' => 'éº§',
  'ûž' => 'éº¨',
  'ûŸ' => 'éº©',
  'û ' => 'éºª',
  'ü@' => 'éº«',
  'üA' => 'éº¬',
  'üB' => 'éº­',
  'üC' => 'éº®',
  'üD' => 'éº¯',
  'üE' => 'éº°',
  'üF' => 'éº±',
  'üG' => 'éº²',
  'üH' => 'éº³',
  'üI' => 'éºµ',
  'üJ' => 'éº¶',
  'üK' => 'éº·',
  'üL' => 'éº¹',
  'üM' => 'éºº',
  'üN' => 'éº¼',
  'üO' => 'éº¿',
  'üP' => 'é»€',
  'üQ' => 'é»�',
  'üR' => 'é»‚',
  'üS' => 'é»ƒ',
  'üT' => 'é»…',
  'üU' => 'é»†',
  'üV' => 'é»‡',
  'üW' => 'é»ˆ',
  'üX' => 'é»Š',
  'üY' => 'é»‹',
  'üZ' => 'é»Œ',
  'ü[' => 'é»�',
  'ü\\' => 'é»’',
  'ü]' => 'é»“',
  'ü^' => 'é»•',
  'ü_' => 'é»–',
  'ü`' => 'é»—',
  'üa' => 'é»™',
  'üb' => 'é»š',
  'üc' => 'é»ž',
  'üd' => 'é»¡',
  'üe' => 'é»£',
  'üf' => 'é»¤',
  'üg' => 'é»¦',
  'üh' => 'é»¨',
  'üi' => 'é»«',
  'üj' => 'é»¬',
  'ük' => 'é»­',
  'ül' => 'é»®',
  'üm' => 'é»°',
  'ün' => 'é»±',
  'üo' => 'é»²',
  'üp' => 'é»³',
  'üq' => 'é»´',
  'ür' => 'é»µ',
  'üs' => 'é»¶',
  'üt' => 'é»·',
  'üu' => 'é»¸',
  'üv' => 'é»º',
  'üw' => 'é»½',
  'üx' => 'é»¿',
  'üy' => 'é¼€',
  'üz' => 'é¼�',
  'ü{' => 'é¼‚',
  'ü|' => 'é¼ƒ',
  'ü}' => 'é¼„',
  'ü~' => 'é¼…',
  'ü€' => 'é¼†',
  'ü�' => 'é¼‡',
  'ü‚' => 'é¼ˆ',
  'üƒ' => 'é¼‰',
  'ü„' => 'é¼Š',
  'ü…' => 'é¼Œ',
  'ü†' => 'é¼�',
  'ü‡' => 'é¼‘',
  'üˆ' => 'é¼’',
  'ü‰' => 'é¼”',
  'üŠ' => 'é¼•',
  'ü‹' => 'é¼–',
  'üŒ' => 'é¼˜',
  'ü�' => 'é¼š',
  'üŽ' => 'é¼›',
  'ü�' => 'é¼œ',
  'ü�' => 'é¼�',
  'ü‘' => 'é¼ž',
  'ü’' => 'é¼Ÿ',
  'ü“' => 'é¼¡',
  'ü”' => 'é¼£',
  'ü•' => 'é¼¤',
  'ü–' => 'é¼¥',
  'ü—' => 'é¼¦',
  'ü˜' => 'é¼§',
  'ü™' => 'é¼¨',
  'üš' => 'é¼©',
  'ü›' => 'é¼ª',
  'üœ' => 'é¼«',
  'ü�' => 'é¼­',
  'üž' => 'é¼®',
  'üŸ' => 'é¼°',
  'ü ' => 'é¼±',
  'ý@' => 'é¼²',
  'ýA' => 'é¼³',
  'ýB' => 'é¼´',
  'ýC' => 'é¼µ',
  'ýD' => 'é¼¶',
  'ýE' => 'é¼¸',
  'ýF' => 'é¼º',
  'ýG' => 'é¼¼',
  'ýH' => 'é¼¿',
  'ýI' => 'é½€',
  'ýJ' => 'é½�',
  'ýK' => 'é½‚',
  'ýL' => 'é½ƒ',
  'ýM' => 'é½…',
  'ýN' => 'é½†',
  'ýO' => 'é½‡',
  'ýP' => 'é½ˆ',
  'ýQ' => 'é½‰',
  'ýR' => 'é½Š',
  'ýS' => 'é½‹',
  'ýT' => 'é½Œ',
  'ýU' => 'é½�',
  'ýV' => 'é½Ž',
  'ýW' => 'é½�',
  'ýX' => 'é½’',
  'ýY' => 'é½“',
  'ýZ' => 'é½”',
  'ý[' => 'é½•',
  'ý\\' => 'é½–',
  'ý]' => 'é½—',
  'ý^' => 'é½˜',
  'ý_' => 'é½™',
  'ý`' => 'é½š',
  'ýa' => 'é½›',
  'ýb' => 'é½œ',
  'ýc' => 'é½�',
  'ýd' => 'é½ž',
  'ýe' => 'é½Ÿ',
  'ýf' => 'é½ ',
  'ýg' => 'é½¡',
  'ýh' => 'é½¢',
  'ýi' => 'é½£',
  'ýj' => 'é½¤',
  'ýk' => 'é½¥',
  'ýl' => 'é½¦',
  'ým' => 'é½§',
  'ýn' => 'é½¨',
  'ýo' => 'é½©',
  'ýp' => 'é½ª',
  'ýq' => 'é½«',
  'ýr' => 'é½¬',
  'ýs' => 'é½­',
  'ýt' => 'é½®',
  'ýu' => 'é½¯',
  'ýv' => 'é½°',
  'ýw' => 'é½±',
  'ýx' => 'é½²',
  'ýy' => 'é½³',
  'ýz' => 'é½´',
  'ý{' => 'é½µ',
  'ý|' => 'é½¶',
  'ý}' => 'é½·',
  'ý~' => 'é½¸',
  'ý€' => 'é½¹',
  'ý�' => 'é½º',
  'ý‚' => 'é½»',
  'ýƒ' => 'é½¼',
  'ý„' => 'é½½',
  'ý…' => 'é½¾',
  'ý†' => 'é¾�',
  'ý‡' => 'é¾‚',
  'ýˆ' => 'é¾�',
  'ý‰' => 'é¾Ž',
  'ýŠ' => 'é¾�',
  'ý‹' => 'é¾�',
  'ýŒ' => 'é¾‘',
  'ý�' => 'é¾’',
  'ýŽ' => 'é¾“',
  'ý�' => 'é¾”',
  'ý�' => 'é¾•',
  'ý‘' => 'é¾–',
  'ý’' => 'é¾—',
  'ý“' => 'é¾˜',
  'ý”' => 'é¾œ',
  'ý•' => 'é¾�',
  'ý–' => 'é¾ž',
  'ý—' => 'é¾¡',
  'ý˜' => 'é¾¢',
  'ý™' => 'é¾£',
  'ýš' => 'é¾¤',
  'ý›' => 'é¾¥',
  'ýœ' => 'ï¤¬',
  'ý�' => 'ï¥¹',
  'ýž' => 'ï¦•',
  'ýŸ' => 'ï§§',
  'ý ' => 'ï§±',
  'þ@' => 'ï¨Œ',
  'þA' => 'ï¨�',
  'þB' => 'ï¨Ž',
  'þC' => 'ï¨�',
  'þD' => 'ï¨‘',
  'þE' => 'ï¨“',
  'þF' => 'ï¨”',
  'þG' => 'ï¨˜',
  'þH' => 'ï¨Ÿ',
  'þI' => 'ï¨ ',
  'þJ' => 'ï¨¡',
  'þK' => 'ï¨£',
  'þL' => 'ï¨¤',
  'þM' => 'ï¨§',
  'þN' => 'ï¨¨',
  'þO' => 'ï¨©',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  ' ' => ' ',
  '' => '',
  '' => '',
  '' => '',
  '' => 'Âœ',
  '' => '	',
  '' => 'Â†',
  '' => '',
  '' => 'Â—',
  '	' => 'Â�',
  '
' => 'ÂŽ',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  '' => 'Â�',
  '' => 'Â…',
  '' => '',
  '' => 'Â‡',
  '' => '',
  '' => '',
  '' => 'Â’',
  '' => 'Â�',
  '' => '',
  '' => '',
  '' => '',
  '' => '',
  ' ' => 'Â€',
  '!' => 'Â�',
  '"' => 'Â‚',
  '#' => 'Âƒ',
  '$' => 'Â„',
  '%' => '
',
  '&' => '',
  '\'' => '',
  '(' => 'Âˆ',
  ')' => 'Â‰',
  '*' => 'ÂŠ',
  '+' => 'Â‹',
  ',' => 'ÂŒ',
  '-' => '',
  '.' => '',
  '/' => '',
  0 => 'Â�',
  1 => 'Â‘',
  2 => '',
  3 => 'Â“',
  4 => 'Â”',
  5 => 'Â•',
  6 => 'Â–',
  7 => '',
  8 => 'Â˜',
  9 => 'Â™',
  ':' => 'Âš',
  ';' => 'Â›',
  '<' => '',
  '=' => '',
  '>' => 'Âž',
  '?' => '',
  '@' => ' ',
  'A' => 'Â ',
  'B' => 'Ã¢',
  'C' => 'Ã¤',
  'D' => 'Ã ',
  'E' => 'Ã¡',
  'F' => 'Ã£',
  'G' => 'Ã¥',
  'H' => 'Ã§',
  'I' => 'Ã±',
  'J' => '[',
  'K' => '.',
  'L' => '<',
  'M' => '(',
  'N' => '+',
  'O' => '!',
  'P' => '&',
  'Q' => 'Ã©',
  'R' => 'Ãª',
  'S' => 'Ã«',
  'T' => 'Ã¨',
  'U' => 'Ã­',
  'V' => 'Ã®',
  'W' => 'Ã¯',
  'X' => 'Ã¬',
  'Y' => 'ÃŸ',
  'Z' => ']',
  '[' => '$',
  '\\' => '*',
  ']' => ')',
  '^' => ';',
  '_' => '^',
  '`' => '-',
  'a' => '/',
  'b' => 'Ã‚',
  'c' => 'Ã„',
  'd' => 'Ã€',
  'e' => 'Ã�',
  'f' => 'Ãƒ',
  'g' => 'Ã…',
  'h' => 'Ã‡',
  'i' => 'Ã‘',
  'j' => 'Â¦',
  'k' => ',',
  'l' => '%',
  'm' => '_',
  'n' => '>',
  'o' => '?',
  'p' => 'Ã¸',
  'q' => 'Ã‰',
  'r' => 'ÃŠ',
  's' => 'Ã‹',
  't' => 'Ãˆ',
  'u' => 'Ã�',
  'v' => 'ÃŽ',
  'w' => 'Ã�',
  'x' => 'ÃŒ',
  'y' => '`',
  'z' => ':',
  '{' => '#',
  '|' => '@',
  '}' => '\'',
  '~' => '=',
  '' => '"',
  '€' => 'Ã˜',
  '�' => 'a',
  '‚' => 'b',
  'ƒ' => 'c',
  '„' => 'd',
  '…' => 'e',
  '†' => 'f',
  '‡' => 'g',
  'ˆ' => 'h',
  '‰' => 'i',
  'Š' => 'Â«',
  '‹' => 'Â»',
  'Œ' => 'Ã°',
  '�' => 'Ã½',
  'Ž' => 'Ã¾',
  '�' => 'Â±',
  '�' => 'Â°',
  '‘' => 'j',
  '’' => 'k',
  '“' => 'l',
  '”' => 'm',
  '•' => 'n',
  '–' => 'o',
  '—' => 'p',
  '˜' => 'q',
  '™' => 'r',
  'š' => 'Âª',
  '›' => 'Âº',
  'œ' => 'Ã¦',
  '�' => 'Â¸',
  'ž' => 'Ã†',
  'Ÿ' => 'Â¤',
  ' ' => 'Âµ',
  '¡' => '~',
  '¢' => 's',
  '£' => 't',
  '¤' => 'u',
  '¥' => 'v',
  '¦' => 'w',
  '§' => 'x',
  '¨' => 'y',
  '©' => 'z',
  'ª' => 'Â¡',
  '«' => 'Â¿',
  '¬' => 'Ã�',
  '­' => 'Ã�',
  '®' => 'Ãž',
  '¯' => 'Â®',
  '°' => 'Â¢',
  '±' => 'Â£',
  '²' => 'Â¥',
  '³' => 'Â·',
  '´' => 'Â©',
  'µ' => 'Â§',
  '¶' => 'Â¶',
  '·' => 'Â¼',
  '¸' => 'Â½',
  '¹' => 'Â¾',
  'º' => 'Â¬',
  '»' => '|',
  '¼' => 'Â¯',
  '½' => 'Â¨',
  '¾' => 'Â´',
  '¿' => 'Ã—',
  'À' => '{',
  'Á' => 'A',
  'Â' => 'B',
  'Ã' => 'C',
  'Ä' => 'D',
  'Å' => 'E',
  'Æ' => 'F',
  'Ç' => 'G',
  'È' => 'H',
  'É' => 'I',
  'Ê' => 'Â­',
  'Ë' => 'Ã´',
  'Ì' => 'Ã¶',
  'Í' => 'Ã²',
  'Î' => 'Ã³',
  'Ï' => 'Ãµ',
  'Ð' => '}',
  'Ñ' => 'J',
  'Ò' => 'K',
  'Ó' => 'L',
  'Ô' => 'M',
  'Õ' => 'N',
  'Ö' => 'O',
  '×' => 'P',
  'Ø' => 'Q',
  'Ù' => 'R',
  'Ú' => 'Â¹',
  'Û' => 'Ã»',
  'Ü' => 'Ã¼',
  'Ý' => 'Ã¹',
  'Þ' => 'Ãº',
  'ß' => 'Ã¿',
  'à' => '\\',
  'á' => 'Ã·',
  'â' => 'S',
  'ã' => 'T',
  'ä' => 'U',
  'å' => 'V',
  'æ' => 'W',
  'ç' => 'X',
  'è' => 'Y',
  'é' => 'Z',
  'ê' => 'Â²',
  'ë' => 'Ã”',
  'ì' => 'Ã–',
  'í' => 'Ã’',
  'î' => 'Ã“',
  'ï' => 'Ã•',
  'ð' => '0',
  'ñ' => '1',
  'ò' => '2',
  'ó' => '3',
  'ô' => '4',
  'õ' => '5',
  'ö' => '6',
  '÷' => '7',
  'ø' => '8',
  'ù' => '9',
  'ú' => 'Â³',
  'û' => 'Ã›',
  'ü' => 'Ãœ',
  'ý' => 'Ã™',
  'þ' => 'Ãš',
  'ÿ' => 'ÂŸ',
);

$result =& $data;
unset($data);

return $result;
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

if (\PHP_VERSION_ID >= 80000) {
    return require __DIR__.'/bootstrap80.php';
}

return require __DIR__.'/bootstrap72.php';
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use Symfony\Polyfill\Mbstring as p;

if (!function_exists('mb_convert_encoding')) {
    function mb_convert_encoding($string, $to_encoding, $from_encoding = null) { return p\Mbstring::mb_convert_encoding($string, $to_encoding, $from_encoding); }
}
if (!function_exists('mb_decode_mimeheader')) {
    function mb_decode_mimeheader($string) { return p\Mbstring::mb_decode_mimeheader($string); }
}
if (!function_exists('mb_encode_mimeheader')) {
    function mb_encode_mimeheader($string, $charset = null, $transfer_encoding = null, $newline = "\r\n", $indent = 0) { return p\Mbstring::mb_encode_mimeheader($string, $charset, $transfer_encoding, $newline, $indent); }
}
if (!function_exists('mb_decode_numericentity')) {
    function mb_decode_numericentity($string, $map, $encoding = null) { return p\Mbstring::mb_decode_numericentity($string, $map, $encoding); }
}
if (!function_exists('mb_encode_numericentity')) {
    function mb_encode_numericentity($string, $map, $encoding = null, $hex = false) { return p\Mbstring::mb_encode_numericentity($string, $map, $encoding, $hex); }
}
if (!function_exists('mb_convert_case')) {
    function mb_convert_case($string, $mode, $encoding = null) { return p\Mbstring::mb_convert_case($string, $mode, $encoding); }
}
if (!function_exists('mb_internal_encoding')) {
    function mb_internal_encoding($encoding = null) { return p\Mbstring::mb_internal_encoding($encoding); }
}
if (!function_exists('mb_language')) {
    function mb_language($language = null) { return p\Mbstring::mb_language($language); }
}
if (!function_exists('mb_list_encodings')) {
    function mb_list_encodings() { return p\Mbstring::mb_list_encodings(); }
}
if (!function_exists('mb_encoding_aliases')) {
    function mb_encoding_aliases($encoding) { return p\Mbstring::mb_encoding_aliases($encoding); }
}
if (!function_exists('mb_check_encoding')) {
    function mb_check_encoding($value = null, $encoding = null) { return p\Mbstring::mb_check_encoding($value, $encoding); }
}
if (!function_exists('mb_detect_encoding')) {
    function mb_detect_encoding($string, $encodings = null, $strict = false) { return p\Mbstring::mb_detect_encoding($string, $encodings, $strict); }
}
if (!function_exists('mb_detect_order')) {
    function mb_detect_order($encoding = null) { return p\Mbstring::mb_detect_order($encoding); }
}
if (!function_exists('mb_parse_str')) {
    function mb_parse_str($string, &$result = []) { parse_str($string, $result); return (bool) $result; }
}
if (!function_exists('mb_strlen')) {
    function mb_strlen($string, $encoding = null) { return p\Mbstring::mb_strlen($string, $encoding); }
}
if (!function_exists('mb_strpos')) {
    function mb_strpos($haystack, $needle, $offset = 0, $encoding = null) { return p\Mbstring::mb_strpos($haystack, $needle, $offset, $encoding); }
}
if (!function_exists('mb_strtolower')) {
    function mb_strtolower($string, $encoding = null) { return p\Mbstring::mb_strtolower($string, $encoding); }
}
if (!function_exists('mb_strtoupper')) {
    function mb_strtoupper($string, $encoding = null) { return p\Mbstring::mb_strtoupper($string, $encoding); }
}
if (!function_exists('mb_substitute_character')) {
    function mb_substitute_character($substitute_character = null) { return p\Mbstring::mb_substitute_character($substitute_character); }
}
if (!function_exists('mb_substr')) {
    function mb_substr($string, $start, $length = 2147483647, $encoding = null) { return p\Mbstring::mb_substr($string, $start, $length, $encoding); }
}
if (!function_exists('mb_stripos')) {
    function mb_stripos($haystack, $needle, $offset = 0, $encoding = null) { return p\Mbstring::mb_stripos($haystack, $needle, $offset, $encoding); }
}
if (!function_exists('mb_stristr')) {
    function mb_stristr($haystack, $needle, $before_needle = false, $encoding = null) { return p\Mbstring::mb_stristr($haystack, $needle, $before_needle, $encoding); }
}
if (!function_exists('mb_strrchr')) {
    function mb_strrchr($haystack, $needle, $before_needle = false, $encoding = null) { return p\Mbstring::mb_strrchr($haystack, $needle, $before_needle, $encoding); }
}
if (!function_exists('mb_strrichr')) {
    function mb_strrichr($haystack, $needle, $before_needle = false, $encoding = null) { return p\Mbstring::mb_strrichr($haystack, $needle, $before_needle, $encoding); }
}
if (!function_exists('mb_strripos')) {
    function mb_strripos($haystack, $needle, $offset = 0, $encoding = null) { return p\Mbstring::mb_strripos($haystack, $needle, $offset, $encoding); }
}
if (!function_exists('mb_strrpos')) {
    function mb_strrpos($haystack, $needle, $offset = 0, $encoding = null) { return p\Mbstring::mb_strrpos($haystack, $needle, $offset, $encoding); }
}
if (!function_exists('mb_strstr')) {
    function mb_strstr($haystack, $needle, $before_needle = false, $encoding = null) { return p\Mbstring::mb_strstr($haystack, $needle, $before_needle, $encoding); }
}
if (!function_exists('mb_get_info')) {
    function mb_get_info($type = 'all') { return p\Mbstring::mb_get_info($type); }
}
if (!function_exists('mb_http_output')) {
    function mb_http_output($encoding = null) { return p\Mbstring::mb_http_output($encoding); }
}
if (!function_exists('mb_strwidth')) {
    function mb_strwidth($string, $encoding = null) { return p\Mbstring::mb_strwidth($string, $encoding); }
}
if (!function_exists('mb_substr_count')) {
    function mb_substr_count($haystack, $needle, $encoding = null) { return p\Mbstring::mb_substr_count($haystack, $needle, $encoding); }
}
if (!function_exists('mb_output_handler')) {
    function mb_output_handler($string, $status) { return p\Mbstring::mb_output_handler($string, $status); }
}
if (!function_exists('mb_http_input')) {
    function mb_http_input($type = null) { return p\Mbstring::mb_http_input($type); }
}

if (!function_exists('mb_convert_variables')) {
    function mb_convert_variables($to_encoding, $from_encoding, &...$vars) { return p\Mbstring::mb_convert_variables($to_encoding, $from_encoding, ...$vars); }
}

if (!function_exists('mb_ord')) {
    function mb_ord($string, $encoding = null) { return p\Mbstring::mb_ord($string, $encoding); }
}
if (!function_exists('mb_chr')) {
    function mb_chr($codepoint, $encoding = null) { return p\Mbstring::mb_chr($codepoint, $encoding); }
}
if (!function_exists('mb_scrub')) {
    function mb_scrub($string, $encoding = null) { return p\Mbstring::mb_scrub($string, $encoding); }
}
if (!function_exists('mb_str_split')) {
    function mb_str_split($string, $length = 1, $encoding = null) { return p\Mbstring::mb_str_split($string, $length, $encoding); }
}

if (!function_exists('mb_str_pad')) {
    /** @return string|false */
    function mb_str_pad(?string $string, ?int $length, ?string $pad_string = ' ', ?int $pad_type = STR_PAD_RIGHT, ?string $encoding = null) { return p\Mbstring::mb_str_pad((string) $string, (int) $length, (string) $pad_string, (int) $pad_type, $encoding); }
}

if (!function_exists('mb_ucfirst')) {
    /** @return string|false */
    function mb_ucfirst(?string $string, ?string $encoding = null) { return p\Mbstring::mb_ucfirst((string) $string, $encoding); }
}

if (!function_exists('mb_lcfirst')) {
    /** @return string|false */
    function mb_lcfirst(?string $string, ?string $encoding = null) { return p\Mbstring::mb_lcfirst((string) $string, $encoding); }
}

if (!function_exists('mb_trim')) {
    /** @return string|false */
    function mb_trim(?string $string, ?string $characters = null, ?string $encoding = null) { return p\Mbstring::mb_trim((string) $string, $characters, $encoding); }
}

if (!function_exists('mb_ltrim')) {
    /** @return string|false */
    function mb_ltrim(?string $string, ?string $characters = null, ?string $encoding = null) { return p\Mbstring::mb_ltrim((string) $string, $characters, $encoding); }
}

if (!function_exists('mb_rtrim')) {
    /** @return string|false */
    function mb_rtrim(?string $string, ?string $characters = null, ?string $encoding = null) { return p\Mbstring::mb_rtrim((string) $string, $characters, $encoding); }
}

if (extension_loaded('mbstring')) {
    return;
}

if (!defined('MB_CASE_UPPER')) {
    define('MB_CASE_UPPER', 0);
}
if (!defined('MB_CASE_LOWER')) {
    define('MB_CASE_LOWER', 1);
}
if (!defined('MB_CASE_TITLE')) {
    define('MB_CASE_TITLE', 2);
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use Symfony\Polyfill\Mbstring as p;

if (!function_exists('mb_convert_encoding')) {
    function mb_convert_encoding(array|string|null $string, ?string $to_encoding, array|string|null $from_encoding = null): array|string|false { return p\Mbstring::mb_convert_encoding($string ?? '', (string) $to_encoding, $from_encoding); }
}
if (!function_exists('mb_decode_mimeheader')) {
    function mb_decode_mimeheader(?string $string): string { return p\Mbstring::mb_decode_mimeheader((string) $string); }
}
if (!function_exists('mb_encode_mimeheader')) {
    function mb_encode_mimeheader(?string $string, ?string $charset = null, ?string $transfer_encoding = null, ?string $newline = "\r\n", ?int $indent = 0): string { return p\Mbstring::mb_encode_mimeheader((string) $string, $charset, $transfer_encoding, (string) $newline, (int) $indent); }
}
if (!function_exists('mb_decode_numericentity')) {
    function mb_decode_numericentity(?string $string, array $map, ?string $encoding = null): string { return p\Mbstring::mb_decode_numericentity((string) $string, $map, $encoding); }
}
if (!function_exists('mb_encode_numericentity')) {
    function mb_encode_numericentity(?string $string, array $map, ?string $encoding = null, ?bool $hex = false): string { return p\Mbstring::mb_encode_numericentity((string) $string, $map, $encoding, (bool) $hex); }
}
if (!function_exists('mb_convert_case')) {
    function mb_convert_case(?string $string, ?int $mode, ?string $encoding = null): string { return p\Mbstring::mb_convert_case((string) $string, (int) $mode, $encoding); }
}
if (!function_exists('mb_internal_encoding')) {
    function mb_internal_encoding(?string $encoding = null): string|bool { return p\Mbstring::mb_internal_encoding($encoding); }
}
if (!function_exists('mb_language')) {
    function mb_language(?string $language = null): string|bool { return p\Mbstring::mb_language($language); }
}
if (!function_exists('mb_list_encodings')) {
    function mb_list_encodings(): array { return p\Mbstring::mb_list_encodings(); }
}
if (!function_exists('mb_encoding_aliases')) {
    function mb_encoding_aliases(?string $encoding): array { return p\Mbstring::mb_encoding_aliases((string) $encoding); }
}
if (!function_exists('mb_check_encoding')) {
    function mb_check_encoding(array|string|null $value = null, ?string $encoding = null): bool { return p\Mbstring::mb_check_encoding($value, $encoding); }
}
if (!function_exists('mb_detect_encoding')) {
    function mb_detect_encoding(?string $string, array|string|null $encodings = null, ?bool $strict = false): string|false { return p\Mbstring::mb_detect_encoding((string) $string, $encodings, (bool) $strict); }
}
if (!function_exists('mb_detect_order')) {
    function mb_detect_order(array|string|null $encoding = null): array|bool { return p\Mbstring::mb_detect_order($encoding); }
}
if (!function_exists('mb_parse_str')) {
    function mb_parse_str(?string $string, &$result = []): bool { parse_str((string) $string, $result); return (bool) $result; }
}
if (!function_exists('mb_strlen')) {
    function mb_strlen(?string $string, ?string $encoding = null): int { return p\Mbstring::mb_strlen((string) $string, $encoding); }
}
if (!function_exists('mb_strpos')) {
    function mb_strpos(?string $haystack, ?string $needle, ?int $offset = 0, ?string $encoding = null): int|false { return p\Mbstring::mb_strpos((string) $haystack, (string) $needle, (int) $offset, $encoding); }
}
if (!function_exists('mb_strtolower')) {
    function mb_strtolower(?string $string, ?string $encoding = null): string { return p\Mbstring::mb_strtolower((string) $string, $encoding); }
}
if (!function_exists('mb_strtoupper')) {
    function mb_strtoupper(?string $string, ?string $encoding = null): string { return p\Mbstring::mb_strtoupper((string) $string, $encoding); }
}
if (!function_exists('mb_substitute_character')) {
    function mb_substitute_character(string|int|null $substitute_character = null): string|int|bool { return p\Mbstring::mb_substitute_character($substitute_character); }
}
if (!function_exists('mb_substr')) {
    function mb_substr(?string $string, ?int $start, ?int $length = null, ?string $encoding = null): string { return p\Mbstring::mb_substr((string) $string, (int) $start, $length, $encoding); }
}
if (!function_exists('mb_stripos')) {
    function mb_stripos(?string $haystack, ?string $needle, ?int $offset = 0, ?string $encoding = null): int|false { return p\Mbstring::mb_stripos((string) $haystack, (string) $needle, (int) $offset, $encoding); }
}
if (!function_exists('mb_stristr')) {
    function mb_stristr(?string $haystack, ?string $needle, ?bool $before_needle = false, ?string $encoding = null): string|false { return p\Mbstring::mb_stristr((string) $haystack, (string) $needle, (bool) $before_needle, $encoding); }
}
if (!function_exists('mb_strrchr')) {
    function mb_strrchr(?string $haystack, ?string $needle, ?bool $before_needle = false, ?string $encoding = null): string|false { return p\Mbstring::mb_strrchr((string) $haystack, (string) $needle, (bool) $before_needle, $encoding); }
}
if (!function_exists('mb_strrichr')) {
    function mb_strrichr(?string $haystack, ?string $needle, ?bool $before_needle = false, ?string $encoding = null): string|false { return p\Mbstring::mb_strrichr((string) $haystack, (string) $needle, (bool) $before_needle, $encoding); }
}
if (!function_exists('mb_strripos')) {
    function mb_strripos(?string $haystack, ?string $needle, ?int $offset = 0, ?string $encoding = null): int|false { return p\Mbstring::mb_strripos((string) $haystack, (string) $needle, (int) $offset, $encoding); }
}
if (!function_exists('mb_strrpos')) {
    function mb_strrpos(?string $haystack, ?string $needle, ?int $offset = 0, ?string $encoding = null): int|false { return p\Mbstring::mb_strrpos((string) $haystack, (string) $needle, (int) $offset, $encoding); }
}
if (!function_exists('mb_strstr')) {
    function mb_strstr(?string $haystack, ?string $needle, ?bool $before_needle = false, ?string $encoding = null): string|false { return p\Mbstring::mb_strstr((string) $haystack, (string) $needle, (bool) $before_needle, $encoding); }
}
if (!function_exists('mb_get_info')) {
    function mb_get_info(?string $type = 'all'): array|string|int|false|null { return p\Mbstring::mb_get_info((string) $type); }
}
if (!function_exists('mb_http_output')) {
    function mb_http_output(?string $encoding = null): string|bool { return p\Mbstring::mb_http_output($encoding); }
}
if (!function_exists('mb_strwidth')) {
    function mb_strwidth(?string $string, ?string $encoding = null): int { return p\Mbstring::mb_strwidth((string) $string, $encoding); }
}
if (!function_exists('mb_substr_count')) {
    function mb_substr_count(?string $haystack, ?string $needle, ?string $encoding = null): int { return p\Mbstring::mb_substr_count((string) $haystack, (string) $needle, $encoding); }
}
if (!function_exists('mb_output_handler')) {
    function mb_output_handler(?string $string, ?int $status): string { return p\Mbstring::mb_output_handler((string) $string, (int) $status); }
}
if (!function_exists('mb_http_input')) {
    function mb_http_input(?string $type = null): array|string|false { return p\Mbstring::mb_http_input($type); }
}

if (!function_exists('mb_convert_variables')) {
    function mb_convert_variables(?string $to_encoding, array|string|null $from_encoding, mixed &$var, mixed &...$vars): string|false { return p\Mbstring::mb_convert_variables((string) $to_encoding, $from_encoding ?? '', $var, ...$vars); }
}

if (!function_exists('mb_ord')) {
    function mb_ord(?string $string, ?string $encoding = null): int|false { return p\Mbstring::mb_ord((string) $string, $encoding); }
}
if (!function_exists('mb_chr')) {
    function mb_chr(?int $codepoint, ?string $encoding = null): string|false { return p\Mbstring::mb_chr((int) $codepoint, $encoding); }
}
if (!function_exists('mb_scrub')) {
    function mb_scrub(?string $string, ?string $encoding = null): string { return p\Mbstring::mb_scrub($string, $encoding); }
}
if (!function_exists('mb_str_split')) {
    function mb_str_split(?string $string, ?int $length = 1, ?string $encoding = null): array { return p\Mbstring::mb_str_split((string) $string, (int) $length, $encoding); }
}

if (!function_exists('mb_str_pad')) {
    function mb_str_pad(?string $string, ?int $length, ?string $pad_string = ' ', ?int $pad_type = STR_PAD_RIGHT, ?string $encoding = null): string { return p\Mbstring::mb_str_pad((string) $string, (int) $length, (string) $pad_string, (int) $pad_type, $encoding); }
}

if (!function_exists('mb_ucfirst')) {
    function mb_ucfirst(?string $string, ?string $encoding = null): string { return p\Mbstring::mb_ucfirst((string) $string, $encoding); }
}

if (!function_exists('mb_lcfirst')) {
    function mb_lcfirst(?string $string, ?string $encoding = null): string { return p\Mbstring::mb_lcfirst((string) $string, $encoding); }
}

if (!function_exists('mb_trim')) {
    function mb_trim(?string $string, ?string $characters = null, ?string $encoding = null): string { return p\Mbstring::mb_trim((string) $string, $characters, $encoding); }
}

if (!function_exists('mb_ltrim')) {
    function mb_ltrim(?string $string, ?string $characters = null, ?string $encoding = null): string { return p\Mbstring::mb_ltrim((string) $string, $characters, $encoding); }
}

if (!function_exists('mb_rtrim')) {
    function mb_rtrim(?string $string, ?string $characters = null, ?string $encoding = null): string { return p\Mbstring::mb_rtrim((string) $string, $characters, $encoding); }
}

if (extension_loaded('mbstring')) {
    return;
}

if (!defined('MB_CASE_UPPER')) {
    define('MB_CASE_UPPER', 0);
}
if (!defined('MB_CASE_LOWER')) {
    define('MB_CASE_LOWER', 1);
}
if (!defined('MB_CASE_TITLE')) {
    define('MB_CASE_TITLE', 2);
}
{
    "name": "symfony/polyfill-mbstring",
    "type": "library",
    "description": "Symfony polyfill for the Mbstring extension",
    "keywords": ["polyfill", "shim", "compatibility", "portable", "mbstring"],
    "homepage": "https://symfony.com",
    "license": "MIT",
    "authors": [
        {
            "name": "Nicolas Grekas",
            "email": "p@tchwork.com"
        },
        {
            "name": "Symfony Community",
            "homepage": "https://symfony.com/contributors"
        }
    ],
    "require": {
        "php": ">=7.2",
        "ext-iconv": "*"
    },
    "provide": {
        "ext-mbstring": "*"
    },
    "autoload": {
        "psr-4": { "Symfony\\Polyfill\\Mbstring\\": "" },
        "files": [ "bootstrap.php" ]
    },
    "suggest": {
        "ext-mbstring": "For best performance"
    },
    "minimum-stability": "dev",
    "extra": {
        "thanks": {
            "name": "symfony/polyfill",
            "url": "https://github.com/symfony/polyfill"
        }
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Polyfill\Mbstring;

/**
 * Partial mbstring implementation in PHP, iconv based, UTF-8 centric.
 *
 * Implemented:
 * - mb_chr                  - Returns a specific character from its Unicode code point
 * - mb_convert_encoding     - Convert character encoding
 * - mb_convert_variables    - Convert character code in variable(s)
 * - mb_decode_mimeheader    - Decode string in MIME header field
 * - mb_encode_mimeheader    - Encode string for MIME header XXX NATIVE IMPLEMENTATION IS REALLY BUGGED
 * - mb_decode_numericentity - Decode HTML numeric string reference to character
 * - mb_encode_numericentity - Encode character to HTML numeric string reference
 * - mb_convert_case         - Perform case folding on a string
 * - mb_detect_encoding      - Detect character encoding
 * - mb_get_info             - Get internal settings of mbstring
 * - mb_http_input           - Detect HTTP input character encoding
 * - mb_http_output          - Set/Get HTTP output character encoding
 * - mb_internal_encoding    - Set/Get internal character encoding
 * - mb_list_encodings       - Returns an array of all supported encodings
 * - mb_ord                  - Returns the Unicode code point of a character
 * - mb_output_handler       - Callback function converts character encoding in output buffer
 * - mb_scrub                - Replaces ill-formed byte sequences with substitute characters
 * - mb_strlen               - Get string length
 * - mb_strpos               - Find position of first occurrence of string in a string
 * - mb_strrpos              - Find position of last occurrence of a string in a string
 * - mb_str_split            - Convert a string to an array
 * - mb_strtolower           - Make a string lowercase
 * - mb_strtoupper           - Make a string uppercase
 * - mb_substitute_character - Set/Get substitution character
 * - mb_substr               - Get part of string
 * - mb_stripos              - Finds position of first occurrence of a string within another, case insensitive
 * - mb_stristr              - Finds first occurrence of a string within another, case insensitive
 * - mb_strrchr              - Finds the last occurrence of a character in a string within another
 * - mb_strrichr             - Finds the last occurrence of a character in a string within another, case insensitive
 * - mb_strripos             - Finds position of last occurrence of a string within another, case insensitive
 * - mb_strstr               - Finds first occurrence of a string within another
 * - mb_strwidth             - Return width of string
 * - mb_substr_count         - Count the number of substring occurrences
 * - mb_ucfirst              - Make a string's first character uppercase
 * - mb_lcfirst              - Make a string's first character lowercase
 * - mb_trim                 - Strip whitespace (or other characters) from the beginning and end of a string
 * - mb_ltrim                - Strip whitespace (or other characters) from the beginning of a string
 * - mb_rtrim                - Strip whitespace (or other characters) from the end of a string
 *
 * Not implemented:
 * - mb_convert_kana         - Convert "kana" one from another ("zen-kaku", "han-kaku" and more)
 * - mb_ereg_*               - Regular expression with multibyte support
 * - mb_parse_str            - Parse GET/POST/COOKIE data and set global variable
 * - mb_preferred_mime_name  - Get MIME charset string
 * - mb_regex_encoding       - Returns current encoding for multibyte regex as string
 * - mb_regex_set_options    - Set/Get the default options for mbregex functions
 * - mb_send_mail            - Send encoded mail
 * - mb_split                - Split multibyte string using regular expression
 * - mb_strcut               - Get part of string
 * - mb_strimwidth           - Get truncated string with specified width
 *
 * @author Nicolas Grekas <p@tchwork.com>
 *
 * @internal
 */
final class Mbstring
{
    public const MB_CASE_FOLD = \PHP_INT_MAX;

    private const SIMPLE_CASE_FOLD = [
        ['Âµ', 'Å¿', "\xCD\x85", 'Ï‚', "\xCF\x90", "\xCF\x91", "\xCF\x95", "\xCF\x96", "\xCF\xB0", "\xCF\xB1", "\xCF\xB5", "\xE1\xBA\x9B", "\xE1\xBE\xBE"],
        ['Î¼', 's', 'Î¹',        'Ïƒ', 'Î²',        'Î¸',        'Ï†',        'Ï€',        'Îº',        'Ï�',        'Îµ',        "\xE1\xB9\xA1", 'Î¹'],
    ];

    // Encodings rejected by mb_ord() and mb_chr(), with their aliases
    private const UNSUPPORTED_CODEPOINT_ENCODINGS = [
        'BASE64' => 'BASE64',
        'UUENCODE' => 'UUENCODE',
        'X-UUENCODE' => 'UUENCODE',
        'HTML-ENTITIES' => 'HTML-ENTITIES',
        'HTML' => 'HTML-ENTITIES',
        'QUOTED-PRINTABLE' => 'Quoted-Printable',
        'QPRINT' => 'Quoted-Printable',
        'UTF-7' => 'UTF-7',
        'UTF7' => 'UTF-7',
        'UTF7-IMAP' => 'UTF7-IMAP',
        'MUTF-7' => 'UTF7-IMAP',
        'JIS' => 'JIS',
        'ISO-2022-JP' => 'ISO-2022-JP',
        'ISO-2022-JP-2004' => 'ISO-2022-JP-2004',
        'ISO-2022-JP-MOBILE#KDDI' => 'ISO-2022-JP-MOBILE#KDDI',
        'ISO-2022-JP-KDDI' => 'ISO-2022-JP-MOBILE#KDDI',
        'ISO-2022-JP-MS' => 'ISO-2022-JP-MS',
        'ISO2022JPMS' => 'ISO-2022-JP-MS',
        'CP50220' => 'CP50220',
        'CP50220RAW' => 'CP50220',
        'CP50220-RAW' => 'CP50220',
        'JIS-MS' => 'CP50220',
        'CP50221' => 'CP50221',
        'CP50222' => 'CP50222',
    ];

    // Well-formed characters in group 1, then the maximal subparts of ill-formed sequences
    private const UTF8_CHAR_REGEX = '/([\x00-\x7F]|[\xC2-\xDF][\x80-\xBF]|\xE0[\xA0-\xBF][\x80-\xBF]|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}|\xED[\x80-\x9F][\x80-\xBF]|\xF0[\x90-\xBF][\x80-\xBF]{2}|[\xF1-\xF3][\x80-\xBF]{3}|\xF4[\x80-\x8F][\x80-\xBF]{2})|\xE0[\xA0-\xBF]|[\xE1-\xEC\xEE\xEF][\x80-\xBF]|\xED[\x80-\x9F]|\xF0[\x90-\xBF][\x80-\xBF]?|[\xF1-\xF3][\x80-\xBF]{1,2}|\xF4[\x80-\x8F][\x80-\xBF]?|[\x80-\xFF]/';

    private static $encodingList = ['ASCII', 'UTF-8'];
    private static $language = 'neutral';
    private static $internalEncoding = 'UTF-8';
    private static $iconvSupportsIgnore;
    private static $convertibleEncodings = ['UTF-8' => true];

    public static function mb_convert_encoding($s, $toEncoding, $fromEncoding = null)
    {
        if (\is_string($fromEncoding) && 2 < \strlen($fromEncoding) && '"' === $fromEncoding[0] && '"' === $fromEncoding[-1]) {
            $fromEncoding = substr($fromEncoding, 1, -1);
        }

        if (80000 <= \PHP_VERSION_ID
            && (!isset(self::$convertibleEncodings[$toEncoding]) || (null !== $fromEncoding && !(\is_string($fromEncoding) && isset(self::$convertibleEncodings[$fromEncoding]))))
            && null !== $error = self::getConversionError('mb_convert_encoding', 2, $toEncoding, $fromEncoding)
        ) {
            throw new \ValueError($error);
        }

        if (\is_array($s)) {
            return self::convertArray($s, $toEncoding, $fromEncoding);
        }

        if (\is_array($fromEncoding) || (null !== $fromEncoding && false !== strpos($fromEncoding, ','))) {
            $fromEncoding = self::mb_detect_encoding($s, $fromEncoding);
        } else {
            $fromEncoding = self::getEncoding($fromEncoding);
        }

        $toEncoding = self::getEncoding($toEncoding);

        if ('BASE64' === $fromEncoding) {
            $s = base64_decode($s);
            $fromEncoding = $toEncoding;
        }

        if ('BASE64' === $toEncoding) {
            return base64_encode($s);
        }

        if ('HTML-ENTITIES' === $toEncoding || 'HTML' === $toEncoding) {
            if ('HTML-ENTITIES' === $fromEncoding || 'HTML' === $fromEncoding) {
                $fromEncoding = 'Windows-1252';
            }
            if ('UTF-8' !== $fromEncoding) {
                $s = self::iconv($fromEncoding, 'UTF-8', $s);
            } elseif (!preg_match('//u', $s)) {
                $s = self::scrubUtf8($s);
            }

            return preg_replace_callback('/[\x80-\xFF]+/', [__CLASS__, 'html_encoding_callback'], $s);
        }

        if ('HTML-ENTITIES' === $fromEncoding) {
            $decodeControlChars = static function ($m) {
                $code = '' !== ($m[2] ?? '') ? hexdec($m[2]) : (int) $m[1];

                if ($code < 32 || 127 === $code) {
                    return \chr($code);
                }
                if (128 <= $code && $code <= 159) {
                    return "\xC2".\chr(0x80 | ($code & 0x3F));
                }

                return $m[0];
            };

            if (\PHP_VERSION_ID >= 70400) {
                $s = html_entity_decode($s, \ENT_QUOTES, 'UTF-8');
                // html_entity_decode() leaves numeric entities for C0/C1 control
                // characters as-is (HTML spec), but mb_convert_encoding() decodes
                // them. Catch what html_entity_decode() missed.
                if (false !== strpos($s, '&#')) {
                    $s = preg_replace_callback('/&#(?:0*([0-9]++)|[xX]0*([0-9a-fA-F]++));/', $decodeControlChars, $s);
                }
            } else {
                // PHP < 7.4: html_entity_decode() truncates strings at NUL bytes,
                // so decode the control character entities first then call
                // html_entity_decode() on each NUL-delimited chunk independently.
                $s = preg_replace_callback('/&#(?:0*([0-9]++)|[xX]0*([0-9a-fA-F]++));/', $decodeControlChars, $s);
                $s = implode("\0", array_map(static function ($chunk) {
                    return html_entity_decode($chunk, \ENT_QUOTES, 'UTF-8');
                }, explode("\0", $s)));
            }
            $fromEncoding = 'UTF-8';
        }

        if ('UTF-8' === $fromEncoding && !preg_match('//u', $s)) {
            $s = self::scrubUtf8($s);
        }

        return self::iconv($fromEncoding, $toEncoding, $s);
    }

    public static function mb_convert_variables($toEncoding, $fromEncoding, &...$vars)
    {
        if (\is_string($fromEncoding) && 2 < \strlen($fromEncoding) && '"' === $fromEncoding[0] && '"' === $fromEncoding[-1]) {
            $fromEncoding = substr($fromEncoding, 1, -1);
        }

        if (80000 <= \PHP_VERSION_ID && null !== $error = self::getConversionError('mb_convert_variables', 1, $toEncoding, $fromEncoding)) {
            throw new \ValueError($error);
        }

        $ok = true;
        foreach ($vars as $i => &$var) {
            $convert = static function (&$v) use (&$ok, $toEncoding, $fromEncoding, $i) {
                if (\is_string($v)) {
                    if (false === $v = self::mb_convert_encoding($v, $toEncoding, $fromEncoding)) {
                        $ok = false;
                    }
                } elseif (80600 <= \PHP_VERSION_ID && !\is_object($v)) {
                    trigger_error(\sprintf('mb_convert_variables(): Argument #%d must be of type string|array|object or only contain entries of type string|array|object, %s given', 3 + $i, strtok(get_debug_type($v), ' ')), \E_USER_WARNING);
                }
            };

            if (\is_array($var)) {
                array_walk_recursive($var, $convert);
            } else {
                $convert($var);
            }
        }
        unset($var);

        return $ok ? $fromEncoding : false;
    }

    public static function mb_decode_mimeheader($s)
    {
        return iconv_mime_decode($s, 2, self::$internalEncoding);
    }

    public static function mb_encode_mimeheader($s, $charset = null, $transferEncoding = null, $linefeed = null, $indent = null)
    {
        trigger_error('mb_encode_mimeheader() is bugged. Please use iconv_mime_encode() instead', \E_USER_WARNING);
    }

    public static function mb_decode_numericentity($s, $convmap, $encoding = null)
    {
        if (null !== $s && !\is_scalar($s) && !(\is_object($s) && method_exists($s, '__toString'))) {
            trigger_error('mb_decode_numericentity() expects parameter 1 to be string, '.\gettype($s).' given', \E_USER_WARNING);

            return null;
        }

        if (!\is_array($convmap) || (80000 > \PHP_VERSION_ID && !$convmap)) {
            return false;
        }

        if (null !== $encoding && !\is_scalar($encoding)) {
            trigger_error('mb_decode_numericentity() expects parameter 3 to be string, '.\gettype($s).' given', \E_USER_WARNING);

            return '';  // Instead of null (cf. mb_encode_numericentity).
        }

        $s = (string) $s;
        if ('' === $s) {
            return '';
        }

        $encoding = self::getEncoding($encoding);

        if ('UTF-8' === $encoding) {
            $encoding = null;
            if (!preg_match('//u', $s)) {
                $s = self::scrubUtf8($s);
            }
        } else {
            $s = self::iconv($encoding, 'UTF-8', $s);
        }

        $cnt = floor(\count($convmap) / 4) * 4;

        for ($i = 0; $i < $cnt; $i += 4) {
            // collector_decode_htmlnumericentity ignores $convmap[$i + 3]
            $convmap[$i] += $convmap[$i + 2];
            $convmap[$i + 1] += $convmap[$i + 2];
        }

        $s = preg_replace_callback('/&#(?:0*([0-9]+)|x0*([0-9a-fA-F]+))'.(\PHP_VERSION_ID >= 80200 ? '' : '(?!&)').';?/', static function (array $m) use ($cnt, $convmap) {
            $c = isset($m[2]) ? (int) hexdec($m[2]) : $m[1];
            for ($i = 0; $i < $cnt; $i += 4) {
                if ($c >= $convmap[$i] && $c <= $convmap[$i + 1]) {
                    return self::mb_chr($c - $convmap[$i + 2]);
                }
            }

            return $m[0];
        }, $s);

        if (null === $encoding) {
            return $s;
        }

        return self::iconv('UTF-8', $encoding, $s);
    }

    public static function mb_encode_numericentity($s, $convmap, $encoding = null, $is_hex = false)
    {
        if (null !== $s && !\is_scalar($s) && !(\is_object($s) && method_exists($s, '__toString'))) {
            trigger_error('mb_encode_numericentity() expects parameter 1 to be string, '.\gettype($s).' given', \E_USER_WARNING);

            return null;
        }

        if (!\is_array($convmap) || (80000 > \PHP_VERSION_ID && !$convmap)) {
            return false;
        }

        if (null !== $encoding && !\is_scalar($encoding)) {
            trigger_error('mb_encode_numericentity() expects parameter 3 to be string, '.\gettype($s).' given', \E_USER_WARNING);

            return null;  // Instead of '' (cf. mb_decode_numericentity).
        }

        if (null !== $is_hex && !\is_scalar($is_hex)) {
            trigger_error('mb_encode_numericentity() expects parameter 4 to be boolean, '.\gettype($s).' given', \E_USER_WARNING);

            return null;
        }

        $s = (string) $s;
        if ('' === $s) {
            return '';
        }

        $encoding = self::getEncoding($encoding);

        if ('UTF-8' === $encoding) {
            $encoding = null;
            if (!preg_match('//u', $s)) {
                $s = self::scrubUtf8($s);
            }
        } else {
            $s = self::iconv($encoding, 'UTF-8', $s);
        }

        static $ulenMask = ["\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4];

        $cnt = floor(\count($convmap) / 4) * 4;
        $i = 0;
        $len = \strlen($s);
        $result = '';

        while ($i < $len) {
            $ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xF0"] ?? 1;
            $uchr = substr($s, $i, $ulen);
            $i += $ulen;

            // code points above U+10FFFF are outside any convmap, but mb_ord() rejects them
            if (false === $c = self::mb_ord($uchr)) {
                $result .= $uchr;
                continue;
            }

            for ($j = 0; $j < $cnt; $j += 4) {
                if ($c >= $convmap[$j] && $c <= $convmap[$j + 1]) {
                    $cOffset = ($c + $convmap[$j + 2]) & $convmap[$j + 3];
                    $result .= $is_hex ? \sprintf('&#x%X;', $cOffset) : '&#'.$cOffset.';';
                    continue 2;
                }
            }
            $result .= $uchr;
        }

        if (null === $encoding) {
            return $result;
        }

        return self::iconv('UTF-8', $encoding, $result);
    }

    public static function mb_convert_case($s, $mode, $encoding = null)
    {
        $s = (string) $s;
        if ('' === $s) {
            return '';
        }

        $encoding = self::getEncoding($encoding);

        if ('UTF-8' === $encoding) {
            $encoding = null;
            if (!preg_match('//u', $s)) {
                $s = self::scrubUtf8($s);
            }
        } else {
            $s = self::iconv($encoding, 'UTF-8', $s);
        }

        if (\MB_CASE_TITLE == $mode) {
            static $titleRegexp = null;
            if (null === $titleRegexp) {
                $titleRegexp = self::getData('titleCaseRegexp');
            }
            $s = preg_replace_callback($titleRegexp, [__CLASS__, 'title_case'], $s);
        } else {
            if (\MB_CASE_UPPER == $mode) {
                static $upper = null;
                if (null === $upper) {
                    $upper = self::getData('upperCase');
                }
                $map = $upper;
            } else {
                if (self::MB_CASE_FOLD === $mode) {
                    static $caseFolding = null;
                    if (null === $caseFolding) {
                        $caseFolding = self::getData('caseFolding');
                    }
                    $s = strtr($s, $caseFolding);
                }

                static $lower = null;
                if (null === $lower) {
                    $lower = self::getData('lowerCase');
                }
                $map = $lower;
            }

            static $ulenMask = ["\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4];

            $i = 0;
            $len = \strlen($s);

            while ($i < $len) {
                $ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xF0"] ?? 1;
                $uchr = substr($s, $i, $ulen);
                $i += $ulen;

                if (isset($map[$uchr])) {
                    $uchr = $map[$uchr];
                    $nlen = \strlen($uchr);

                    if ($nlen == $ulen) {
                        $nlen = $i;
                        do {
                            $s[--$nlen] = $uchr[--$ulen];
                        } while ($ulen);
                    } else {
                        $s = substr_replace($s, $uchr, $i - $ulen, $ulen);
                        $len += $nlen - $ulen;
                        $i += $nlen - $ulen;
                    }
                }
            }
        }

        if (null === $encoding) {
            return $s;
        }

        return self::iconv('UTF-8', $encoding, $s);
    }

    public static function mb_internal_encoding($encoding = null)
    {
        if (null === $encoding) {
            return self::$internalEncoding;
        }

        $normalizedEncoding = self::getEncoding($encoding);

        if ('UTF-8' === $normalizedEncoding || false !== @iconv($normalizedEncoding, $normalizedEncoding, ' ')) {
            self::$internalEncoding = $normalizedEncoding;

            return true;
        }

        if (80000 > \PHP_VERSION_ID) {
            return false;
        }

        throw new \ValueError(\sprintf('Argument #1 ($encoding) must be a valid encoding, "%s" given', $encoding));
    }

    public static function mb_language($lang = null)
    {
        if (null === $lang) {
            return self::$language;
        }

        switch ($normalizedLang = strtolower($lang)) {
            case 'uni':
            case 'neutral':
                self::$language = $normalizedLang;

                return true;
        }

        if (80000 > \PHP_VERSION_ID) {
            return false;
        }

        throw new \ValueError(\sprintf('Argument #1 ($language) must be a valid language, "%s" given', $lang));
    }

    public static function mb_list_encodings()
    {
        return ['UTF-8'];
    }

    public static function mb_encoding_aliases($encoding)
    {
        switch (strtoupper($encoding)) {
            case 'UTF8':
            case 'UTF-8':
                return ['utf8'];
        }

        return false;
    }

    public static function mb_check_encoding($var = null, $encoding = null)
    {
        if (null === $encoding) {
            if (null === $var) {
                return false;
            }
            $encoding = self::$internalEncoding;
        }

        if (!\is_array($var)) {
            if ('UTF-8' === self::getEncoding($encoding)) {
                return (bool) preg_match('//u', $var);
            }

            return self::mb_detect_encoding($var, [$encoding]) || false !== @iconv($encoding, $encoding, $var);
        }

        foreach ($var as $key => $value) {
            if (!self::mb_check_encoding($key, $encoding)) {
                return false;
            }
            if (!self::mb_check_encoding($value, $encoding)) {
                return false;
            }
        }

        return true;
    }

    public static function mb_detect_encoding($str, $encodingList = null, $strict = false)
    {
        if (null === $encodingList) {
            $encodingList = self::$encodingList;
        } else {
            if (!\is_array($encodingList)) {
                $encodingList = array_map('trim', explode(',', $encodingList));
            }
            $encodingList = array_map('strtoupper', $encodingList);
        }

        foreach ($encodingList as $enc) {
            switch ($enc) {
                case 'ASCII':
                    if (!preg_match('/[\x80-\xFF]/', $str)) {
                        return $enc;
                    }
                    break;

                case 'UTF8':
                case 'UTF-8':
                    if (preg_match('//u', $str)) {
                        return 'UTF-8';
                    }
                    break;

                default:
                    if (0 === strncmp($enc, 'ISO-8859-', 9)) {
                        return $enc;
                    }
            }
        }

        return false;
    }

    public static function mb_detect_order($encodingList = null)
    {
        if (null === $encodingList) {
            return self::$encodingList;
        }

        if (!\is_array($encodingList)) {
            $encodingList = array_map('trim', explode(',', $encodingList));
        }
        $encodingList = array_map('strtoupper', $encodingList);

        foreach ($encodingList as $enc) {
            switch ($enc) {
                default:
                    if (strncmp($enc, 'ISO-8859-', 9)) {
                        return false;
                    }
                    // no break
                case 'ASCII':
                case 'UTF8':
                case 'UTF-8':
            }
        }

        self::$encodingList = $encodingList;

        return true;
    }

    public static function mb_strlen($s, $encoding = null)
    {
        $encoding = self::getEncoding($encoding);
        if ('CP850' === $encoding || 'ASCII' === $encoding) {
            return \strlen($s);
        }

        if (false !== $len = @iconv_strlen($s, $encoding)) {
            return $len;
        }

        if ('UTF-8' !== $encoding) {
            return $len;
        }

        return preg_match_all('/[\x00-\x7F]|[\xC0-\xDF][\x80-\xBF]?|[\xE0-\xEF][\x80-\xBF]{0,2}|[\xF0-\xF7][\x80-\xBF]{0,3}|[\xF8-\xFB][\x80-\xBF]{0,4}|[\xFC-\xFD][\x80-\xBF]{0,5}|[\x80-\xBF\xFE\xFF]/s', $s);
    }

    public static function mb_strpos($haystack, $needle, $offset = 0, $encoding = null)
    {
        return self::find(__FUNCTION__, $haystack, $needle, $offset, $encoding, false, false);
    }

    public static function mb_strrpos($haystack, $needle, $offset = 0, $encoding = null)
    {
        if ($offset != (int) $offset) {
            $offset = 0;
        }

        return self::find(__FUNCTION__, $haystack, $needle, $offset, $encoding, true, false);
    }

    public static function mb_str_split($string, $split_length = 1, $encoding = null)
    {
        if (null !== $string && !\is_scalar($string) && !(\is_object($string) && method_exists($string, '__toString'))) {
            trigger_error('mb_str_split() expects parameter 1 to be string, '.\gettype($string).' given', \E_USER_WARNING);

            return null;
        }

        if (1 > $split_length = (int) $split_length) {
            if (80000 > \PHP_VERSION_ID) {
                trigger_error('The length of each segment must be greater than zero', \E_USER_WARNING);

                return false;
            }

            throw new \ValueError('Argument #2 ($length) must be greater than 0');
        }

        if (80300 <= \PHP_VERSION_ID && 0x3FFFFFFF < $split_length) {
            throw new \ValueError('Argument #2 ($length) is too large');
        }

        if (null === $encoding) {
            $encoding = mb_internal_encoding();
        }

        if ('UTF-8' === $encoding = self::getEncoding($encoding)) {
            $string = (string) $string;

            if (\strlen($string) <= $split_length) {
                return '' === $string ? [] : [$string];
            }

            $rx = '/(';
            while (65535 < $split_length) {
                $rx .= '.{65535}';
                $split_length -= 65535;
            }
            $rx .= '.{'.$split_length.'})/Aus';

            return preg_split($rx, $string, -1, \PREG_SPLIT_DELIM_CAPTURE | \PREG_SPLIT_NO_EMPTY);
        }

        $result = [];
        $length = mb_strlen($string, $encoding);

        for ($i = 0; $i < $length; $i += $split_length) {
            $result[] = mb_substr($string, $i, $split_length, $encoding);
        }

        return $result;
    }

    public static function mb_strtolower($s, $encoding = null)
    {
        return self::mb_convert_case($s, \MB_CASE_LOWER, $encoding);
    }

    public static function mb_strtoupper($s, $encoding = null)
    {
        return self::mb_convert_case($s, \MB_CASE_UPPER, $encoding);
    }

    public static function mb_substitute_character($c = null)
    {
        if (null === $c) {
            return 'none';
        }
        if (0 === strcasecmp($c, 'none')) {
            return true;
        }
        if (80000 > \PHP_VERSION_ID) {
            return false;
        }
        if (\is_int($c) || 'long' === $c || 'entity' === $c) {
            return false;
        }

        throw new \ValueError('Argument #1 ($substitute_character) must be "none", "long", "entity" or a valid codepoint');
    }

    public static function mb_substr($s, $start, $length = null, $encoding = null)
    {
        $encoding = self::getEncoding($encoding);
        if ('CP850' === $encoding || 'ASCII' === $encoding) {
            return (string) substr($s, $start, null === $length ? 2147483647 : $length);
        }

        if ($start < 0) {
            $start = iconv_strlen($s, $encoding) + $start;
            if ($start < 0) {
                $start = 0;
            }
        }

        if (null === $length) {
            $length = 2147483647;
        } elseif ($length < 0) {
            $length = iconv_strlen($s, $encoding) + $length - $start;
            if ($length < 0) {
                return '';
            }
        }

        return (string) iconv_substr($s, $start, $length, $encoding);
    }

    public static function mb_stripos($haystack, $needle, $offset = 0, $encoding = null)
    {
        return self::find(__FUNCTION__, $haystack, $needle, $offset, $encoding, false, true);
    }

    public static function mb_stristr($haystack, $needle, $part = false, $encoding = null)
    {
        return self::findPart(__FUNCTION__, $haystack, $needle, $part, $encoding, false, true);
    }

    public static function mb_strrchr($haystack, $needle, $part = false, $encoding = null)
    {
        return self::findPart(__FUNCTION__, $haystack, $needle, $part, $encoding, true, false);
    }

    public static function mb_strrichr($haystack, $needle, $part = false, $encoding = null)
    {
        return self::findPart(__FUNCTION__, $haystack, $needle, $part, $encoding, true, true);
    }

    public static function mb_strripos($haystack, $needle, $offset = 0, $encoding = null)
    {
        return self::find(__FUNCTION__, $haystack, $needle, $offset, $encoding, true, true);
    }

    public static function mb_strstr($haystack, $needle, $part = false, $encoding = null)
    {
        return self::findPart(__FUNCTION__, $haystack, $needle, $part, $encoding, false, false);
    }

    public static function mb_get_info($type = 'all')
    {
        $info = [
            'internal_encoding' => self::$internalEncoding,
            'http_output' => 'pass',
            'http_output_conv_mimetypes' => '^(text/|application/xhtml\+xml)',
            'func_overload' => 0,
            'func_overload_list' => 'no overload',
            'mail_charset' => 'UTF-8',
            'mail_header_encoding' => 'BASE64',
            'mail_body_encoding' => 'BASE64',
            'illegal_chars' => 0,
            'encoding_translation' => 'Off',
            'language' => self::$language,
            'detect_order' => self::$encodingList,
            'substitute_character' => 'none',
            'strict_detection' => 'Off',
        ];

        if ('all' === $type) {
            return $info;
        }
        if (isset($info[$type])) {
            return $info[$type];
        }

        return false;
    }

    public static function mb_http_input($type = '')
    {
        return false;
    }

    public static function mb_http_output($encoding = null)
    {
        return null !== $encoding ? 'pass' === $encoding : 'pass';
    }

    public static function mb_strwidth($s, $encoding = null)
    {
        $encoding = self::getEncoding($encoding);
        $wideChars = '/[\x{1100}-\x{115F}\x{2329}\x{232A}\x{2E80}-\x{303E}\x{3040}-\x{A4CF}\x{AC00}-\x{D7A3}\x{F900}-\x{FAFF}\x{FE10}-\x{FE19}\x{FE30}-\x{FE6F}\x{FF00}-\x{FF60}\x{FFE0}-\x{FFE6}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}]/u';

        if ('UTF-8' !== $encoding) {
            $s = self::iconv($encoding, 'UTF-8', $s);
        } elseif (!preg_match('//u', $s)) {
            // Each maximal subpart of an ill-formed sequence is one column wide
            return preg_match_all(self::UTF8_CHAR_REGEX, $s) + preg_match_all($wideChars, self::scrubUtf8($s));
        }

        $s = preg_replace($wideChars, '', $s, -1, $wide);

        return ($wide << 1) + iconv_strlen($s, 'UTF-8');
    }

    public static function mb_substr_count($haystack, $needle, $encoding = null)
    {
        if ('' === $needle = (string) $needle) {
            if (80000 > \PHP_VERSION_ID) {
                trigger_error('mb_substr_count(): Empty substring', \E_USER_WARNING);

                return false;
            }

            throw new \ValueError('mb_substr_count(): Argument #2 ($needle) must not be empty');
        }

        if (!$search = self::prepareSearch((string) $haystack, $needle, self::getEncoding($encoding), false, true)) {
            return 0;
        }

        return substr_count($search[0], $search[1]);
    }

    public static function mb_output_handler($contents, $status)
    {
        return $contents;
    }

    public static function mb_chr($code, $encoding = null)
    {
        if (null !== $encoding && !self::assertCodepointEncoding($encoding, 'mb_chr')) {
            return false;
        }

        if (0x80 > $code %= 0x200000) {
            $s = \chr($code);
        } elseif (0x800 > $code) {
            $s = \chr(0xC0 | $code >> 6).\chr(0x80 | $code & 0x3F);
        } elseif (0x10000 > $code) {
            $s = \chr(0xE0 | $code >> 12).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F);
        } else {
            $s = \chr(0xF0 | $code >> 18).\chr(0x80 | $code >> 12 & 0x3F).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F);
        }

        if ('UTF-8' !== $encoding = self::getEncoding($encoding)) {
            $s = mb_convert_encoding($s, $encoding, 'UTF-8');
        }

        return $s;
    }

    public static function mb_ord($s, $encoding = null)
    {
        if ('' === (string) $s) {
            if (80000 > \PHP_VERSION_ID) {
                trigger_error('mb_ord(): Empty string', \E_USER_WARNING);

                return false;
            }

            throw new \ValueError('mb_ord(): Argument #1 ($string) must not be empty');
        }

        if (null !== $encoding && !self::assertCodepointEncoding($encoding, 'mb_ord')) {
            return false;
        }

        if ('UTF-8' !== $encoding = self::getEncoding($encoding)) {
            $s = mb_convert_encoding($s, 'UTF-8', $encoding);
        }

        $s = unpack('C*', substr($s, 0, 4));
        $code = $s[1] ?? 0;
        if (0x80 > $code) {
            return $code;
        }

        if (0xC2 > $code || 0xF4 < $code) {
            return false;
        }

        $c2 = $s[2] ?? 0;
        if (0x80 !== ($c2 & 0xC0)) {
            return false;
        }

        if (0xE0 > $code) {
            return (($code & 0x1F) << 6) | ($c2 & 0x3F);
        }

        $c3 = $s[3] ?? 0;
        if (0x80 !== ($c3 & 0xC0)
            || 0xE0 === $code && 0xA0 > $c2
            || 0xED === $code && 0xA0 <= $c2
        ) {
            return false;
        }

        if (0xF0 > $code) {
            return (($code & 0x0F) << 12) | (($c2 & 0x3F) << 6) | ($c3 & 0x3F);
        }

        $c4 = $s[4] ?? 0;
        if (0x80 !== ($c4 & 0xC0)
            || 0xF0 === $code && 0x90 > $c2
            || 0xF4 === $code && 0x90 <= $c2
        ) {
            return false;
        }

        return (($code & 0x07) << 18) | (($c2 & 0x3F) << 12) | (($c3 & 0x3F) << 6) | ($c4 & 0x3F);
    }

    /** @return string|false */
    public static function mb_scrub(?string $string, ?string $encoding = null): string
    {
        if (null === $encoding) {
            $encoding = self::mb_internal_encoding();
        } elseif (!self::assertEncoding($encoding, 'mb_scrub(): Argument #2 ($encoding) must be a valid encoding, "%s" given')) {
            return false;
        }

        return self::mb_convert_encoding((string) $string, $encoding, $encoding);
    }

    /** @return string|false */
    public static function mb_str_pad(string $string, int $length, string $pad_string = ' ', int $pad_type = \STR_PAD_RIGHT, ?string $encoding = null)
    {
        if (null === $encoding) {
            $encoding = self::mb_internal_encoding();
        } elseif (!self::assertEncoding($encoding, 'mb_str_pad(): Argument #5 ($encoding) must be a valid encoding, "%s" given')) {
            return false;
        }

        if (0 >= $padStringLength = self::mb_strlen($pad_string, $encoding)) {
            if (\PHP_VERSION_ID < 80000) {
                trigger_error('mb_str_pad(): Argument #3 ($pad_string) must be a non-empty string', \E_USER_WARNING);

                return false;
            }

            throw new \ValueError('mb_str_pad(): Argument #3 ($pad_string) must be a non-empty string');
        }

        if (!\in_array($pad_type, [\STR_PAD_RIGHT, \STR_PAD_LEFT, \STR_PAD_BOTH], true)) {
            if (\PHP_VERSION_ID < 80000) {
                trigger_error('mb_str_pad(): Argument #4 ($pad_type) must be STR_PAD_LEFT, STR_PAD_RIGHT, or STR_PAD_BOTH', \E_USER_WARNING);

                return false;
            }

            throw new \ValueError('mb_str_pad(): Argument #4 ($pad_type) must be STR_PAD_LEFT, STR_PAD_RIGHT, or STR_PAD_BOTH');
        }

        $paddingRequired = $length - self::mb_strlen($string, $encoding);

        if ($paddingRequired < 1) {
            return $string;
        }

        switch ($pad_type) {
            case \STR_PAD_LEFT:
                $leftPaddingLength = $paddingRequired;
                break;
            case \STR_PAD_RIGHT:
                $leftPaddingLength = 0;
                break;
            default:
                $leftPaddingLength = intdiv($paddingRequired, 2);
        }

        $rightPaddingLength = $paddingRequired - $leftPaddingLength;

        return str_repeat($pad_string, intdiv($leftPaddingLength, $padStringLength)).self::mb_substr($pad_string, 0, $leftPaddingLength % $padStringLength, $encoding)
            .$string
            .str_repeat($pad_string, intdiv($rightPaddingLength, $padStringLength)).self::mb_substr($pad_string, 0, $rightPaddingLength % $padStringLength, $encoding);
    }

    /** @return string|false */
    public static function mb_ucfirst(string $string, ?string $encoding = null)
    {
        if (null === $encoding) {
            $encoding = self::mb_internal_encoding();
        } elseif (!self::assertEncoding($encoding, 'mb_ucfirst(): Argument #2 ($encoding) must be a valid encoding, "%s" given')) {
            return false;
        }

        $firstChar = mb_substr($string, 0, 1, $encoding);
        $firstChar = mb_convert_case($firstChar, \MB_CASE_TITLE, $encoding);

        return $firstChar.mb_substr($string, 1, null, $encoding);
    }

    /** @return string|false */
    public static function mb_lcfirst(string $string, ?string $encoding = null)
    {
        if (null === $encoding) {
            $encoding = self::mb_internal_encoding();
        } elseif (!self::assertEncoding($encoding, 'mb_lcfirst(): Argument #2 ($encoding) must be a valid encoding, "%s" given')) {
            return false;
        }

        $firstChar = mb_substr($string, 0, 1, $encoding);
        $firstChar = mb_convert_case($firstChar, \MB_CASE_LOWER, $encoding);

        return $firstChar.mb_substr($string, 1, null, $encoding);
    }

    /** @return string|false */
    public static function mb_trim(string $string, ?string $characters = null, ?string $encoding = null)
    {
        return self::mb_internal_trim('{^[%s]+|[%1$s]+$}Du', $string, $characters, $encoding, __FUNCTION__);
    }

    /** @return string|false */
    public static function mb_ltrim(string $string, ?string $characters = null, ?string $encoding = null)
    {
        return self::mb_internal_trim('{^[%s]+}Du', $string, $characters, $encoding, __FUNCTION__);
    }

    /** @return string|false */
    public static function mb_rtrim(string $string, ?string $characters = null, ?string $encoding = null)
    {
        return self::mb_internal_trim('{[%s]+$}Du', $string, $characters, $encoding, __FUNCTION__);
    }

    /**
     * Finds the position of $needle in $haystack like native mbstring.
     *
     * @return int|false
     */
    private static function find(string $function, $haystack, $needle, $offset, $encoding, bool $reverse, bool $fold)
    {
        $needle = (string) $needle;

        if (80000 > \PHP_VERSION_ID && '' === $needle && 'mb_stripos' === $function) {
            trigger_error($function.'(): Empty delimiter', \E_USER_WARNING);

            return false;
        }

        if (!$search = self::prepareSearch((string) $haystack, $needle, self::searchEncoding($encoding, $fold), $fold, $fold)) {
            return false;
        }

        [$haystack, $needle, $bytes, $dropped] = $search;

        if (80000 > \PHP_VERSION_ID && $fold && ('' === $haystack || '' === $needle)) {
            return false;
        }

        if (false === $start = self::utf8Offset($haystack, $offset = (int) $offset, $bytes)) {
            // Native mbstring counts the ill-formed sequences that iconv() dropped: the offset could be in range
            if ($dropped && abs($offset) <= self::utf8Length($haystack) + $dropped) {
                return false;
            }

            if (80000 > \PHP_VERSION_ID) {
                trigger_error($function.'(): '.($reverse ? 'Offset is greater than the length of haystack string' : 'Offset not contained in string'), \E_USER_WARNING);

                return false;
            }

            throw new \ValueError($function.'(): Argument #3 ($offset) must be contained in argument #1 ($haystack)');
        }

        if (80000 > \PHP_VERSION_ID && '' === $needle) {
            if ('mb_strpos' === $function) {
                trigger_error($function.'(): Empty delimiter', \E_USER_WARNING);
            }

            return false;
        }

        if (!$reverse) {
            $pos = strpos($haystack, $needle, $start);
        } else {
            $pos = strrpos($haystack, $needle, 0 > $offset ? $start - \strlen($haystack) : $start);
        }

        return false === $pos || $bytes ? $pos : self::utf8Length(substr($haystack, 0, $pos));
    }

    /**
     * Returns the part of $haystack before or from $needle like native mbstring.
     *
     * @return string|false
     */
    private static function findPart(string $function, $haystack, $needle, $part, $encoding, bool $reverse, bool $fold)
    {
        $needle = (string) $needle;

        if (80000 > \PHP_VERSION_ID && '' === $needle) {
            if (!$reverse) {
                trigger_error($function.'(): Empty delimiter', \E_USER_WARNING);
            }

            return false;
        }

        $haystack = (string) $haystack;

        if (!$search = self::prepareSearch($haystack, $needle, $encoding = self::searchEncoding($encoding, $fold), $fold, $fold)) {
            return false;
        }

        [$h, $n, $bytes] = $search;

        if (false === $pos = $reverse ? strrpos($h, $n) : strpos($h, $n)) {
            return false;
        }

        if ($bytes) {
            return (string) ($part ? substr($haystack, 0, $pos) : substr($haystack, $pos));
        }

        if ($fold || ('UTF-8' === $encoding && (!preg_match('//u', $haystack) || !preg_match('//u', $needle)))) {
            // Cut after as many characters as native mbstring counts before the match
            $pos = self::utf8Length(substr($h, 0, $pos));
            $h = 'UTF-8' === $encoding ? self::markIllFormedUtf8($haystack) : self::utf8($haystack, $encoding);
            $pos = self::utf8Offset($h, $pos, false);
        }

        $h = (string) ($part ? substr($h, 0, $pos) : substr($h, $pos));

        if (false !== strpos($h, "\xFF")) {
            $h = str_replace("\xFF", 'none' === mb_substitute_character() ? '' : '?', $h);
        }

        return 'UTF-8' === $encoding ? $h : (string) @self::iconv('UTF-8', $encoding, $h);
    }

    /**
     * @return array{string, string, bool, int}|null The haystack and the needle to search, whether offsets in them count bytes rather than UTF-8 characters, and how many bytes of ill-formed sequences iconv() dropped from the haystack
     */
    private static function prepareSearch(string $haystack, string $needle, string $encoding, bool $fold, bool $markIllFormed)
    {
        if ('ASCII' === $encoding || ('CP850' === $encoding && !$fold)) {
            if ('ASCII' === $encoding) {
                // Native mbstring turns every byte above 0x7F into the same error marker
                $haystack = preg_replace('/[\x80-\xFF]/', "\xFF", $haystack);
                $needle = preg_replace('/[\x80-\xFF]/', "\xFF", $needle);
            }

            if ($fold) {
                $haystack = strtr($haystack, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz');
                $needle = strtr($needle, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz');
            }

            return [$haystack, $needle, true, 0];
        }

        $dropped = 0;

        if ('UTF-8' !== $encoding) {
            if (false === ($haystack = self::utf8($haystack, $encoding, $dropped)) || false === $needle = self::utf8($needle, $encoding)) {
                return null;
            }
        } elseif ($markIllFormed) {
            $haystack = self::markIllFormedUtf8($haystack);
            $needle = self::markIllFormedUtf8($needle);
        }

        return $fold ? [self::foldCase($haystack), self::foldCase($needle), false, $dropped] : [$haystack, $needle, false, $dropped];
    }

    private static function searchEncoding($encoding, bool $fold): string
    {
        $normalizedEncoding = self::getEncoding($encoding);

        // Native mbstring folds the case of 8bit strings as Latin-1
        return $fold && 'CP850' === $normalizedEncoding && null !== $encoding && 'CP850' !== strtoupper($encoding) ? 'ISO-8859-1' : $normalizedEncoding;
    }

    /**
     * Converts $s to UTF-8 to search it, ending with "\xFF", a byte that is invalid in UTF-8, when its last sequence is truncated, like native mbstring.
     *
     * @param int $dropped Set to the number of bytes of the ill-formed sequences that iconv() dropped elsewhere
     *
     * @return string|false False when the encoding is unknown
     */
    private static function utf8(string $s, string $encoding, &$dropped = 0)
    {
        if ('UTF-8' === $encoding || '' === $s) {
            return $s;
        }

        if (false !== $u = @iconv($encoding, 'UTF-8', $s)) {
            return $u;
        }

        // Let iconv() warn about an unknown encoding
        if (false === iconv($encoding, 'UTF-8', '')) {
            return false;
        }

        // iconv() drops the ill-formed sequences, but fails on a truncated one at the end
        for ($i = 0; $i < 4; ++$i) {
            if (false !== $u = @self::iconv($encoding, 'UTF-8', $i ? substr($s, 0, -$i) : $s)) {
                $dropped = \strlen($s) - $i - \strlen((string) @iconv('UTF-8', $encoding, $u));

                return $i || '' === $u ? $u."\xFF" : $u;
            }
        }

        $dropped = \strlen($s);

        return "\xFF";
    }

    /**
     * Replaces each maximal subpart of ill-formed UTF-8 sequences with "\xFF", like native mbstring before searching case-insensitively.
     */
    private static function markIllFormedUtf8(string $s): string
    {
        if (preg_match('//u', $s)) {
            return $s;
        }

        return preg_replace('/\G(?:[\x00-\x7F]|[\xC2-\xDF][\x80-\xBF]|\xE0[\xA0-\xBF][\x80-\xBF]|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}|\xED[\x80-\x9F][\x80-\xBF]|\xF0[\x90-\xBF][\x80-\xBF]{2}|[\xF1-\xF3][\x80-\xBF]{3}|\xF4[\x80-\x8F][\x80-\xBF]{2})*+\K(?:\xE0[\xA0-\xBF]|[\xE1-\xEC\xEE\xEF][\x80-\xBF]|\xED[\x80-\x9F]|\xF0[\x90-\xBF][\x80-\xBF]?|[\xF1-\xF3][\x80-\xBF]{1,2}|\xF4[\x80-\x8F][\x80-\xBF]?|[\x80-\xFF])/', "\xFF", $s);
    }

    /**
     * Folds the case of UTF-8 $s like the simple case folding of native mbstring.
     */
    private static function foldCase(string $s): string
    {
        static $map = null;

        if (null === $map) {
            $map = array_combine(self::SIMPLE_CASE_FOLD[0], self::SIMPLE_CASE_FOLD[1]) + self::getData('lowerCase');
            unset($map["\xC4\xB0"]); // U+0130 has no simple case folding
        }

        return strtr($s, $map);
    }

    /**
     * Counts the characters of UTF-8 $s like native mbstring, even when ill-formed: one per byte that is not a continuation byte.
     */
    private static function utf8Length(string $s): int
    {
        return \strlen($s) - array_sum(\array_slice(count_chars($s), 0x80, 0x40));
    }

    /**
     * @return int|false The offset in bytes of the character at $offset, false when it is out of range, like native mbstring
     */
    private static function utf8Offset(string $s, int $offset, bool $bytes)
    {
        if (0 === $offset) {
            return 0;
        }

        if ($bytes || preg_match('//u', $u = str_replace("\xFF", '?', $s))) {
            $length = $bytes ? \strlen($s) : self::utf8Length($s);

            if (0 > $offset) {
                $offset += $length;
            }

            if (0 > $offset || $offset > $length) {
                return false;
            }

            if ($bytes) {
                return $offset;
            }

            for ($rx = ''; 65535 < $offset; $offset -= 65535) {
                $rx .= '.{65535}';
            }
            preg_match('/^'.$rx.'.{'.$offset.'}\K/su', $u, $m, \PREG_OFFSET_CAPTURE);

            return $m[0][1];
        }

        // Walk ill-formed UTF-8 like native mbstring: backward over the bytes that are not continuation bytes, forward by the lengths that lead bytes announce
        $length = \strlen($s);

        if (0 > $offset) {
            $i = $length;

            while (0 > $offset) {
                if (0 === $i) {
                    return false;
                }

                if (0x80 !== (\ord($s[--$i]) & 0xC0)) {
                    ++$offset;
                }
            }

            return $i;
        }

        $i = 0;

        while (0 < $offset--) {
            if ($i >= $length) {
                return false;
            }

            $c = \ord($s[$i]);
            $i += $c < 0xC2 ? 1 : ($c < 0xE0 ? 2 : ($c < 0xF0 ? 3 : ($c < 0xF5 ? 4 : 1)));
        }

        return min($i, $length);
    }

    private static function html_encoding_callback(array $m)
    {
        $i = 1;
        $entities = '';
        $m = unpack('C*', htmlentities($m[0], \ENT_COMPAT, 'UTF-8'));

        while (isset($m[$i])) {
            if (0x80 > $m[$i]) {
                $entities .= \chr($m[$i++]);
                continue;
            }
            if (0xF0 <= $m[$i]) {
                $c = (($m[$i++] - 0xF0) << 18) + (($m[$i++] - 0x80) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80;
            } elseif (0xE0 <= $m[$i]) {
                $c = (($m[$i++] - 0xE0) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80;
            } else {
                $c = (($m[$i++] - 0xC0) << 6) + $m[$i++] - 0x80;
            }

            $entities .= '&#'.$c.';';
        }

        return $entities;
    }

    private static function title_case(array $s)
    {
        return self::mb_convert_case($s[1], \MB_CASE_UPPER, 'UTF-8').self::mb_convert_case($s[2], \MB_CASE_LOWER, 'UTF-8');
    }

    private static function getData($file)
    {
        if (file_exists($file = __DIR__.'/Resources/unidata/'.$file.'.php')) {
            return require $file;
        }

        return false;
    }

    private static function getEncoding($encoding)
    {
        if (null === $encoding) {
            return self::$internalEncoding;
        }

        if ('UTF-8' === $encoding) {
            return 'UTF-8';
        }

        $encoding = strtoupper($encoding);

        if ('8BIT' === $encoding || 'BINARY' === $encoding) {
            return 'CP850';
        }

        if ('UTF8' === $encoding) {
            return 'UTF-8';
        }

        if ('UTF-32' === $encoding) {
            return 'UTF-32BE';
        }

        if ('UTF-16' === $encoding) {
            return 'UTF-16BE';
        }

        return $encoding;
    }

    private static function iconv($fromEncoding, $toEncoding, $s)
    {
        if (null === self::$iconvSupportsIgnore) {
            self::$iconvSupportsIgnore = false !== @iconv('UTF-8', 'UTF-8//IGNORE', '');
        }

        return self::$iconvSupportsIgnore
            ? iconv($fromEncoding, $toEncoding.'//IGNORE', $s)
            : iconv($fromEncoding, $toEncoding, $s);
    }

    /** @return string|false */
    private static function mb_internal_trim(string $regex, string $string, ?string $characters, ?string $encoding, string $function)
    {
        if (null === $encoding) {
            $encoding = self::mb_internal_encoding();
        } elseif (!self::assertEncoding($encoding, $function.'(): Argument #3 ($encoding) must be a valid encoding, "%s" given')) {
            return false;
        }

        if ('' === $characters) {
            return $string;
        }

        if ('UTF-8' === self::getEncoding($encoding)) {
            $encoding = null;
            if (!preg_match('//u', $string) || (null !== $characters && !preg_match('//u', $characters))) {
                return self::mb_trim_invalid_utf8($string, $characters, $function);
            }
        } else {
            $string = self::iconv($encoding, 'UTF-8', $string);

            if (null !== $characters) {
                $characters = self::iconv($encoding, 'UTF-8', $characters);
            }
        }

        if (null === $characters) {
            $characters = "\\0 \f\n\r\t\v\u{00A0}\u{1680}\u{2000}\u{2001}\u{2002}\u{2003}\u{2004}\u{2005}\u{2006}\u{2007}\u{2008}\u{2009}\u{200A}\u{2028}\u{2029}\u{202F}\u{205F}\u{3000}\u{0085}\u{180E}";
        } else {
            $characters = preg_quote($characters);
        }

        $string = preg_replace(\sprintf($regex, $characters), '', $string);

        if (null === $encoding) {
            return $string;
        }

        return self::iconv('UTF-8', $encoding, $string);
    }

    /**
     * Trims ill-formed UTF-8 like mbstring: each maximal subpart of an ill-formed
     * sequence counts as one character, and the string is returned as is when
     * nothing is trimmed, else re-encoded with the substitute character.
     */
    private static function mb_trim_invalid_utf8(string $string, ?string $characters, string $function): string
    {
        $regex = self::UTF8_CHAR_REGEX;

        // Ill-formed sequences leave group 1 empty: they are trimmed when $characters has one too
        preg_match_all($regex, $characters ?? "\0 \f\n\r\t\v\u{00A0}\u{1680}\u{2000}\u{2001}\u{2002}\u{2003}\u{2004}\u{2005}\u{2006}\u{2007}\u{2008}\u{2009}\u{200A}\u{2028}\u{2029}\u{202F}\u{205F}\u{3000}\u{0085}\u{180E}", $m);
        $trimmed = array_flip($m[1]);

        $start = 0;
        $end = \strlen($string);

        while ('mb_rtrim' !== $function && $start < $end && preg_match($regex, $string, $m, 0, $start) && isset($trimmed[$m[1] ?? ''])) {
            $start += \strlen($m[0]);
        }

        while ('mb_ltrim' !== $function && $start < $end) {
            // Characters are at most 4 bytes long, and only continuation bytes don't start one
            for ($i = $end - 1; $i > $start && $i > $end - 4 && 0x80 === (\ord($string[$i]) & 0xC0); --$i) {
            }

            preg_match_all($regex, substr($string, $i, $end - $i), $m);

            if (!isset($trimmed[end($m[1])])) {
                break;
            }

            $end -= \strlen(end($m[0]));
        }

        if (0 === $start && \strlen($string) === $end) {
            return $string;
        }

        $string = substr($string, $start, $end - $start);

        return 'none' === mb_substitute_character() ? self::scrubUtf8($string) : mb_convert_encoding($string, 'UTF-8', 'UTF-8');
    }

    private static function scrubUtf8(string $s): string
    {
        return preg_replace(self::UTF8_CHAR_REGEX, '$1', $s);
    }

    /**
     * @param true[] $referenceIds The ids of the references followed to reach $array
     */
    private static function convertArray(array $array, $toEncoding, $fromEncoding, array $referenceIds = []): array
    {
        $r = [];

        foreach ($array as $key => $v) {
            if (null !== $v && !\is_scalar($v) && !\is_array($v)) {
                trigger_error('mb_convert_encoding(): Object is not supported', \E_USER_WARNING);
                continue;
            }

            $k = \is_string($key) ? self::mb_convert_encoding($key, $toEncoding, $fromEncoding) : $key;

            if (\is_string($v)) {
                $v = self::mb_convert_encoding($v, $toEncoding, $fromEncoding);
            } elseif (\is_array($v)) {
                // Only a reference can close a cycle: following the same one twice means one was found
                $id = 70400 <= \PHP_VERSION_ID && ($reference = \ReflectionReference::fromArrayElement($array, $key)) ? $reference->getId() : null;

                if (null !== $id && isset($referenceIds[$id])) {
                    trigger_error('mb_convert_encoding(): Cannot convert recursively referenced values', \E_USER_WARNING);
                    $v = [];
                } else {
                    $v = self::convertArray($v, $toEncoding, $fromEncoding, null === $id ? $referenceIds : $referenceIds + [$id => true]);
                }
            }

            // Keys that collide once converted keep their first value
            $r += [$k => $v];
        }

        return $r;
    }

    /**
     * Returns the error native mbstring throws for these encoding arguments, if any.
     */
    private static function getConversionError(string $function, int $argument, $toEncoding, $fromEncoding): ?string
    {
        if (!self::isConvertibleEncoding((string) $toEncoding)) {
            return \sprintf('%s(): Argument #%d ($to_encoding) must be a valid encoding, "%s" given', $function, $argument, $toEncoding);
        }

        if (null === $fromEncoding) {
            return null;
        }

        if (!$list = \is_array($fromEncoding) ? $fromEncoding : ('' === $fromEncoding ? [] : explode(',', $fromEncoding))) {
            return \sprintf('%s(): Argument #%d ($from_encoding) must specify at least one encoding', $function, 1 + $argument);
        }

        foreach ($list as $encoding) {
            $encoding = \is_array($fromEncoding) ? (string) $encoding : trim($encoding, " \t");

            // PHP 8.4 takes an empty entry of a comma-separated list for "auto"
            if ('' === $encoding && !\is_array($fromEncoding) && 80400 <= \PHP_VERSION_ID) {
                continue;
            }

            if (0 !== strcasecmp($encoding, 'auto') && !self::isConvertibleEncoding($encoding)) {
                return \sprintf('%s(): Argument #%d ($from_encoding) contains invalid encoding "%s"', $function, 1 + $argument, $encoding);
            }
        }

        return null;
    }

    private static function isConvertibleEncoding(string $encoding): bool
    {
        if (isset(self::$convertibleEncodings[$encoding])) {
            return true;
        }

        $normalizedEncoding = self::getEncoding($encoding);

        // Native encoding names use no other characters, while iconv() also accepts suffixes like //TRANSLIT
        if ('' === $normalizedEncoding || preg_match('/[^\w#\-.:]/', $encoding) || (null === (self::UNSUPPORTED_CODEPOINT_ENCODINGS[$normalizedEncoding] ?? null) && false === @iconv($normalizedEncoding, $normalizedEncoding, ''))) {
            return false;
        }

        return self::$convertibleEncodings[$encoding] = true;
    }

    private static function assertEncoding(string $encoding, string $errorFormat): bool
    {
        try {
            $validEncoding = @self::mb_check_encoding('', $encoding);
        } catch (\ValueError $e) {
            throw new \ValueError(\sprintf($errorFormat, $encoding));
        }

        if (!$validEncoding) {
            if (80000 > \PHP_VERSION_ID) {
                trigger_error(\sprintf($errorFormat, $encoding), \E_USER_WARNING);
            } else {
                throw new \ValueError(\sprintf($errorFormat, $encoding));
            }
        }

        return $validEncoding;
    }

    private static function assertCodepointEncoding(string $encoding, string $function): bool
    {
        if (null !== $name = self::UNSUPPORTED_CODEPOINT_ENCODINGS[strtoupper($encoding)] ?? null) {
            if (80000 > \PHP_VERSION_ID) {
                trigger_error(\sprintf('%s(): Unsupported encoding "%s"', $function, $name), \E_USER_WARNING);

                return false;
            }

            throw new \ValueError(\sprintf('%s() does not support the "%s" encoding', $function, $name));
        }

        $normalizedEncoding = self::getEncoding($encoding);

        if ('UTF-8' === $normalizedEncoding || false !== @iconv($normalizedEncoding, $normalizedEncoding, '')) {
            return true;
        }

        if (80000 > \PHP_VERSION_ID) {
            trigger_error(\sprintf('%s(): Unknown encoding "%s"', $function, $encoding), \E_USER_WARNING);

            return false;
        }

        throw new \ValueError(\sprintf('%s(): Argument #2 ($encoding) must be a valid encoding, "%s" given', $function, $encoding));
    }
}
<?php

return array (
  'a' => 'A',
  'b' => 'B',
  'c' => 'C',
  'd' => 'D',
  'e' => 'E',
  'f' => 'F',
  'g' => 'G',
  'h' => 'H',
  'i' => 'I',
  'j' => 'J',
  'k' => 'K',
  'l' => 'L',
  'm' => 'M',
  'n' => 'N',
  'o' => 'O',
  'p' => 'P',
  'q' => 'Q',
  'r' => 'R',
  's' => 'S',
  't' => 'T',
  'u' => 'U',
  'v' => 'V',
  'w' => 'W',
  'x' => 'X',
  'y' => 'Y',
  'z' => 'Z',
  'Âµ' => 'Îœ',
  'Ã ' => 'Ã€',
  'Ã¡' => 'Ã�',
  'Ã¢' => 'Ã‚',
  'Ã£' => 'Ãƒ',
  'Ã¤' => 'Ã„',
  'Ã¥' => 'Ã…',
  'Ã¦' => 'Ã†',
  'Ã§' => 'Ã‡',
  'Ã¨' => 'Ãˆ',
  'Ã©' => 'Ã‰',
  'Ãª' => 'ÃŠ',
  'Ã«' => 'Ã‹',
  'Ã¬' => 'ÃŒ',
  'Ã­' => 'Ã�',
  'Ã®' => 'ÃŽ',
  'Ã¯' => 'Ã�',
  'Ã°' => 'Ã�',
  'Ã±' => 'Ã‘',
  'Ã²' => 'Ã’',
  'Ã³' => 'Ã“',
  'Ã´' => 'Ã”',
  'Ãµ' => 'Ã•',
  'Ã¶' => 'Ã–',
  'Ã¸' => 'Ã˜',
  'Ã¹' => 'Ã™',
  'Ãº' => 'Ãš',
  'Ã»' => 'Ã›',
  'Ã¼' => 'Ãœ',
  'Ã½' => 'Ã�',
  'Ã¾' => 'Ãž',
  'Ã¿' => 'Å¸',
  'Ä�' => 'Ä€',
  'Äƒ' => 'Ä‚',
  'Ä…' => 'Ä„',
  'Ä‡' => 'Ä†',
  'Ä‰' => 'Äˆ',
  'Ä‹' => 'ÄŠ',
  'Ä�' => 'ÄŒ',
  'Ä�' => 'ÄŽ',
  'Ä‘' => 'Ä�',
  'Ä“' => 'Ä’',
  'Ä•' => 'Ä”',
  'Ä—' => 'Ä–',
  'Ä™' => 'Ä˜',
  'Ä›' => 'Äš',
  'Ä�' => 'Äœ',
  'ÄŸ' => 'Äž',
  'Ä¡' => 'Ä ',
  'Ä£' => 'Ä¢',
  'Ä¥' => 'Ä¤',
  'Ä§' => 'Ä¦',
  'Ä©' => 'Ä¨',
  'Ä«' => 'Äª',
  'Ä­' => 'Ä¬',
  'Ä¯' => 'Ä®',
  'Ä±' => 'I',
  'Ä³' => 'Ä²',
  'Äµ' => 'Ä´',
  'Ä·' => 'Ä¶',
  'Äº' => 'Ä¹',
  'Ä¼' => 'Ä»',
  'Ä¾' => 'Ä½',
  'Å€' => 'Ä¿',
  'Å‚' => 'Å�',
  'Å„' => 'Åƒ',
  'Å†' => 'Å…',
  'Åˆ' => 'Å‡',
  'Å‹' => 'ÅŠ',
  'Å�' => 'ÅŒ',
  'Å�' => 'ÅŽ',
  'Å‘' => 'Å�',
  'Å“' => 'Å’',
  'Å•' => 'Å”',
  'Å—' => 'Å–',
  'Å™' => 'Å˜',
  'Å›' => 'Åš',
  'Å�' => 'Åœ',
  'ÅŸ' => 'Åž',
  'Å¡' => 'Å ',
  'Å£' => 'Å¢',
  'Å¥' => 'Å¤',
  'Å§' => 'Å¦',
  'Å©' => 'Å¨',
  'Å«' => 'Åª',
  'Å­' => 'Å¬',
  'Å¯' => 'Å®',
  'Å±' => 'Å°',
  'Å³' => 'Å²',
  'Åµ' => 'Å´',
  'Å·' => 'Å¶',
  'Åº' => 'Å¹',
  'Å¼' => 'Å»',
  'Å¾' => 'Å½',
  'Å¿' => 'S',
  'Æ€' => 'Éƒ',
  'Æƒ' => 'Æ‚',
  'Æ…' => 'Æ„',
  'Æˆ' => 'Æ‡',
  'ÆŒ' => 'Æ‹',
  'Æ’' => 'Æ‘',
  'Æ•' => 'Ç¶',
  'Æ™' => 'Æ˜',
  'Æš' => 'È½',
  'Æž' => 'È ',
  'Æ¡' => 'Æ ',
  'Æ£' => 'Æ¢',
  'Æ¥' => 'Æ¤',
  'Æ¨' => 'Æ§',
  'Æ­' => 'Æ¬',
  'Æ°' => 'Æ¯',
  'Æ´' => 'Æ³',
  'Æ¶' => 'Æµ',
  'Æ¹' => 'Æ¸',
  'Æ½' => 'Æ¼',
  'Æ¿' => 'Ç·',
  'Ç…' => 'Ç„',
  'Ç†' => 'Ç„',
  'Çˆ' => 'Ç‡',
  'Ç‰' => 'Ç‡',
  'Ç‹' => 'ÇŠ',
  'ÇŒ' => 'ÇŠ',
  'ÇŽ' => 'Ç�',
  'Ç�' => 'Ç�',
  'Ç’' => 'Ç‘',
  'Ç”' => 'Ç“',
  'Ç–' => 'Ç•',
  'Ç˜' => 'Ç—',
  'Çš' => 'Ç™',
  'Çœ' => 'Ç›',
  'Ç�' => 'ÆŽ',
  'ÇŸ' => 'Çž',
  'Ç¡' => 'Ç ',
  'Ç£' => 'Ç¢',
  'Ç¥' => 'Ç¤',
  'Ç§' => 'Ç¦',
  'Ç©' => 'Ç¨',
  'Ç«' => 'Çª',
  'Ç­' => 'Ç¬',
  'Ç¯' => 'Ç®',
  'Ç²' => 'Ç±',
  'Ç³' => 'Ç±',
  'Çµ' => 'Ç´',
  'Ç¹' => 'Ç¸',
  'Ç»' => 'Çº',
  'Ç½' => 'Ç¼',
  'Ç¿' => 'Ç¾',
  'È�' => 'È€',
  'Èƒ' => 'È‚',
  'È…' => 'È„',
  'È‡' => 'È†',
  'È‰' => 'Èˆ',
  'È‹' => 'ÈŠ',
  'È�' => 'ÈŒ',
  'È�' => 'ÈŽ',
  'È‘' => 'È�',
  'È“' => 'È’',
  'È•' => 'È”',
  'È—' => 'È–',
  'È™' => 'È˜',
  'È›' => 'Èš',
  'È�' => 'Èœ',
  'ÈŸ' => 'Èž',
  'È£' => 'È¢',
  'È¥' => 'È¤',
  'È§' => 'È¦',
  'È©' => 'È¨',
  'È«' => 'Èª',
  'È­' => 'È¬',
  'È¯' => 'È®',
  'È±' => 'È°',
  'È³' => 'È²',
  'È¼' => 'È»',
  'È¿' => 'â±¾',
  'É€' => 'â±¿',
  'É‚' => 'É�',
  'É‡' => 'É†',
  'É‰' => 'Éˆ',
  'É‹' => 'ÉŠ',
  'É�' => 'ÉŒ',
  'É�' => 'ÉŽ',
  'É�' => 'â±¯',
  'É‘' => 'â±­',
  'É’' => 'â±°',
  'É“' => 'Æ�',
  'É”' => 'Æ†',
  'É–' => 'Æ‰',
  'É—' => 'ÆŠ',
  'É™' => 'Æ�',
  'É›' => 'Æ�',
  'Éœ' => 'êž«',
  'É ' => 'Æ“',
  'É¡' => 'êž¬',
  'É£' => 'Æ”',
  'É¥' => 'êž�',
  'É¦' => 'êžª',
  'É¨' => 'Æ—',
  'É©' => 'Æ–',
  'Éª' => 'êž®',
  'É«' => 'â±¢',
  'É¬' => 'êž­',
  'É¯' => 'Æœ',
  'É±' => 'â±®',
  'É²' => 'Æ�',
  'Éµ' => 'ÆŸ',
  'É½' => 'â±¤',
  'Ê€' => 'Æ¦',
  'Ê‚' => 'êŸ…',
  'Êƒ' => 'Æ©',
  'Ê‡' => 'êž±',
  'Êˆ' => 'Æ®',
  'Ê‰' => 'É„',
  'ÊŠ' => 'Æ±',
  'Ê‹' => 'Æ²',
  'ÊŒ' => 'É…',
  'Ê’' => 'Æ·',
  'Ê�' => 'êž²',
  'Êž' => 'êž°',
  'Í…' => 'Î™',
  'Í±' => 'Í°',
  'Í³' => 'Í²',
  'Í·' => 'Í¶',
  'Í»' => 'Ï½',
  'Í¼' => 'Ï¾',
  'Í½' => 'Ï¿',
  'Î¬' => 'Î†',
  'Î­' => 'Îˆ',
  'Î®' => 'Î‰',
  'Î¯' => 'ÎŠ',
  'Î±' => 'Î‘',
  'Î²' => 'Î’',
  'Î³' => 'Î“',
  'Î´' => 'Î”',
  'Îµ' => 'Î•',
  'Î¶' => 'Î–',
  'Î·' => 'Î—',
  'Î¸' => 'Î˜',
  'Î¹' => 'Î™',
  'Îº' => 'Îš',
  'Î»' => 'Î›',
  'Î¼' => 'Îœ',
  'Î½' => 'Î�',
  'Î¾' => 'Îž',
  'Î¿' => 'ÎŸ',
  'Ï€' => 'Î ',
  'Ï�' => 'Î¡',
  'Ï‚' => 'Î£',
  'Ïƒ' => 'Î£',
  'Ï„' => 'Î¤',
  'Ï…' => 'Î¥',
  'Ï†' => 'Î¦',
  'Ï‡' => 'Î§',
  'Ïˆ' => 'Î¨',
  'Ï‰' => 'Î©',
  'ÏŠ' => 'Îª',
  'Ï‹' => 'Î«',
  'ÏŒ' => 'ÎŒ',
  'Ï�' => 'ÎŽ',
  'ÏŽ' => 'Î�',
  'Ï�' => 'Î’',
  'Ï‘' => 'Î˜',
  'Ï•' => 'Î¦',
  'Ï–' => 'Î ',
  'Ï—' => 'Ï�',
  'Ï™' => 'Ï˜',
  'Ï›' => 'Ïš',
  'Ï�' => 'Ïœ',
  'ÏŸ' => 'Ïž',
  'Ï¡' => 'Ï ',
  'Ï£' => 'Ï¢',
  'Ï¥' => 'Ï¤',
  'Ï§' => 'Ï¦',
  'Ï©' => 'Ï¨',
  'Ï«' => 'Ïª',
  'Ï­' => 'Ï¬',
  'Ï¯' => 'Ï®',
  'Ï°' => 'Îš',
  'Ï±' => 'Î¡',
  'Ï²' => 'Ï¹',
  'Ï³' => 'Í¿',
  'Ïµ' => 'Î•',
  'Ï¸' => 'Ï·',
  'Ï»' => 'Ïº',
  'Ð°' => 'Ð�',
  'Ð±' => 'Ð‘',
  'Ð²' => 'Ð’',
  'Ð³' => 'Ð“',
  'Ð´' => 'Ð”',
  'Ðµ' => 'Ð•',
  'Ð¶' => 'Ð–',
  'Ð·' => 'Ð—',
  'Ð¸' => 'Ð˜',
  'Ð¹' => 'Ð™',
  'Ðº' => 'Ðš',
  'Ð»' => 'Ð›',
  'Ð¼' => 'Ðœ',
  'Ð½' => 'Ð�',
  'Ð¾' => 'Ðž',
  'Ð¿' => 'ÐŸ',
  'Ñ€' => 'Ð ',
  'Ñ�' => 'Ð¡',
  'Ñ‚' => 'Ð¢',
  'Ñƒ' => 'Ð£',
  'Ñ„' => 'Ð¤',
  'Ñ…' => 'Ð¥',
  'Ñ†' => 'Ð¦',
  'Ñ‡' => 'Ð§',
  'Ñˆ' => 'Ð¨',
  'Ñ‰' => 'Ð©',
  'ÑŠ' => 'Ðª',
  'Ñ‹' => 'Ð«',
  'ÑŒ' => 'Ð¬',
  'Ñ�' => 'Ð­',
  'ÑŽ' => 'Ð®',
  'Ñ�' => 'Ð¯',
  'Ñ�' => 'Ð€',
  'Ñ‘' => 'Ð�',
  'Ñ’' => 'Ð‚',
  'Ñ“' => 'Ðƒ',
  'Ñ”' => 'Ð„',
  'Ñ•' => 'Ð…',
  'Ñ–' => 'Ð†',
  'Ñ—' => 'Ð‡',
  'Ñ˜' => 'Ðˆ',
  'Ñ™' => 'Ð‰',
  'Ñš' => 'ÐŠ',
  'Ñ›' => 'Ð‹',
  'Ñœ' => 'ÐŒ',
  'Ñ�' => 'Ð�',
  'Ñž' => 'ÐŽ',
  'ÑŸ' => 'Ð�',
  'Ñ¡' => 'Ñ ',
  'Ñ£' => 'Ñ¢',
  'Ñ¥' => 'Ñ¤',
  'Ñ§' => 'Ñ¦',
  'Ñ©' => 'Ñ¨',
  'Ñ«' => 'Ñª',
  'Ñ­' => 'Ñ¬',
  'Ñ¯' => 'Ñ®',
  'Ñ±' => 'Ñ°',
  'Ñ³' => 'Ñ²',
  'Ñµ' => 'Ñ´',
  'Ñ·' => 'Ñ¶',
  'Ñ¹' => 'Ñ¸',
  'Ñ»' => 'Ñº',
  'Ñ½' => 'Ñ¼',
  'Ñ¿' => 'Ñ¾',
  'Ò�' => 'Ò€',
  'Ò‹' => 'ÒŠ',
  'Ò�' => 'ÒŒ',
  'Ò�' => 'ÒŽ',
  'Ò‘' => 'Ò�',
  'Ò“' => 'Ò’',
  'Ò•' => 'Ò”',
  'Ò—' => 'Ò–',
  'Ò™' => 'Ò˜',
  'Ò›' => 'Òš',
  'Ò�' => 'Òœ',
  'ÒŸ' => 'Òž',
  'Ò¡' => 'Ò ',
  'Ò£' => 'Ò¢',
  'Ò¥' => 'Ò¤',
  'Ò§' => 'Ò¦',
  'Ò©' => 'Ò¨',
  'Ò«' => 'Òª',
  'Ò­' => 'Ò¬',
  'Ò¯' => 'Ò®',
  'Ò±' => 'Ò°',
  'Ò³' => 'Ò²',
  'Òµ' => 'Ò´',
  'Ò·' => 'Ò¶',
  'Ò¹' => 'Ò¸',
  'Ò»' => 'Òº',
  'Ò½' => 'Ò¼',
  'Ò¿' => 'Ò¾',
  'Ó‚' => 'Ó�',
  'Ó„' => 'Óƒ',
  'Ó†' => 'Ó…',
  'Óˆ' => 'Ó‡',
  'ÓŠ' => 'Ó‰',
  'ÓŒ' => 'Ó‹',
  'ÓŽ' => 'Ó�',
  'Ó�' => 'Ó€',
  'Ó‘' => 'Ó�',
  'Ó“' => 'Ó’',
  'Ó•' => 'Ó”',
  'Ó—' => 'Ó–',
  'Ó™' => 'Ó˜',
  'Ó›' => 'Óš',
  'Ó�' => 'Óœ',
  'ÓŸ' => 'Óž',
  'Ó¡' => 'Ó ',
  'Ó£' => 'Ó¢',
  'Ó¥' => 'Ó¤',
  'Ó§' => 'Ó¦',
  'Ó©' => 'Ó¨',
  'Ó«' => 'Óª',
  'Ó­' => 'Ó¬',
  'Ó¯' => 'Ó®',
  'Ó±' => 'Ó°',
  'Ó³' => 'Ó²',
  'Óµ' => 'Ó´',
  'Ó·' => 'Ó¶',
  'Ó¹' => 'Ó¸',
  'Ó»' => 'Óº',
  'Ó½' => 'Ó¼',
  'Ó¿' => 'Ó¾',
  'Ô�' => 'Ô€',
  'Ôƒ' => 'Ô‚',
  'Ô…' => 'Ô„',
  'Ô‡' => 'Ô†',
  'Ô‰' => 'Ôˆ',
  'Ô‹' => 'ÔŠ',
  'Ô�' => 'ÔŒ',
  'Ô�' => 'ÔŽ',
  'Ô‘' => 'Ô�',
  'Ô“' => 'Ô’',
  'Ô•' => 'Ô”',
  'Ô—' => 'Ô–',
  'Ô™' => 'Ô˜',
  'Ô›' => 'Ôš',
  'Ô�' => 'Ôœ',
  'ÔŸ' => 'Ôž',
  'Ô¡' => 'Ô ',
  'Ô£' => 'Ô¢',
  'Ô¥' => 'Ô¤',
  'Ô§' => 'Ô¦',
  'Ô©' => 'Ô¨',
  'Ô«' => 'Ôª',
  'Ô­' => 'Ô¬',
  'Ô¯' => 'Ô®',
  'Õ¡' => 'Ô±',
  'Õ¢' => 'Ô²',
  'Õ£' => 'Ô³',
  'Õ¤' => 'Ô´',
  'Õ¥' => 'Ôµ',
  'Õ¦' => 'Ô¶',
  'Õ§' => 'Ô·',
  'Õ¨' => 'Ô¸',
  'Õ©' => 'Ô¹',
  'Õª' => 'Ôº',
  'Õ«' => 'Ô»',
  'Õ¬' => 'Ô¼',
  'Õ­' => 'Ô½',
  'Õ®' => 'Ô¾',
  'Õ¯' => 'Ô¿',
  'Õ°' => 'Õ€',
  'Õ±' => 'Õ�',
  'Õ²' => 'Õ‚',
  'Õ³' => 'Õƒ',
  'Õ´' => 'Õ„',
  'Õµ' => 'Õ…',
  'Õ¶' => 'Õ†',
  'Õ·' => 'Õ‡',
  'Õ¸' => 'Õˆ',
  'Õ¹' => 'Õ‰',
  'Õº' => 'ÕŠ',
  'Õ»' => 'Õ‹',
  'Õ¼' => 'ÕŒ',
  'Õ½' => 'Õ�',
  'Õ¾' => 'ÕŽ',
  'Õ¿' => 'Õ�',
  'Ö€' => 'Õ�',
  'Ö�' => 'Õ‘',
  'Ö‚' => 'Õ’',
  'Öƒ' => 'Õ“',
  'Ö„' => 'Õ”',
  'Ö…' => 'Õ•',
  'Ö†' => 'Õ–',
  'áƒ�' => 'á²�',
  'áƒ‘' => 'á²‘',
  'áƒ’' => 'á²’',
  'áƒ“' => 'á²“',
  'áƒ”' => 'á²”',
  'áƒ•' => 'á²•',
  'áƒ–' => 'á²–',
  'áƒ—' => 'á²—',
  'áƒ˜' => 'á²˜',
  'áƒ™' => 'á²™',
  'áƒš' => 'á²š',
  'áƒ›' => 'á²›',
  'áƒœ' => 'á²œ',
  'áƒ�' => 'á²�',
  'áƒž' => 'á²ž',
  'áƒŸ' => 'á²Ÿ',
  'áƒ ' => 'á² ',
  'áƒ¡' => 'á²¡',
  'áƒ¢' => 'á²¢',
  'áƒ£' => 'á²£',
  'áƒ¤' => 'á²¤',
  'áƒ¥' => 'á²¥',
  'áƒ¦' => 'á²¦',
  'áƒ§' => 'á²§',
  'áƒ¨' => 'á²¨',
  'áƒ©' => 'á²©',
  'áƒª' => 'á²ª',
  'áƒ«' => 'á²«',
  'áƒ¬' => 'á²¬',
  'áƒ­' => 'á²­',
  'áƒ®' => 'á²®',
  'áƒ¯' => 'á²¯',
  'áƒ°' => 'á²°',
  'áƒ±' => 'á²±',
  'áƒ²' => 'á²²',
  'áƒ³' => 'á²³',
  'áƒ´' => 'á²´',
  'áƒµ' => 'á²µ',
  'áƒ¶' => 'á²¶',
  'áƒ·' => 'á²·',
  'áƒ¸' => 'á²¸',
  'áƒ¹' => 'á²¹',
  'áƒº' => 'á²º',
  'áƒ½' => 'á²½',
  'áƒ¾' => 'á²¾',
  'áƒ¿' => 'á²¿',
  'á�¸' => 'á�°',
  'á�¹' => 'á�±',
  'á�º' => 'á�²',
  'á�»' => 'á�³',
  'á�¼' => 'á�´',
  'á�½' => 'á�µ',
  'á²€' => 'Ð’',
  'á²�' => 'Ð”',
  'á²‚' => 'Ðž',
  'á²ƒ' => 'Ð¡',
  'á²„' => 'Ð¢',
  'á²…' => 'Ð¢',
  'á²†' => 'Ðª',
  'á²‡' => 'Ñ¢',
  'á²ˆ' => 'ê™Š',
  'áµ¹' => 'ê�½',
  'áµ½' => 'â±£',
  'á¶Ž' => 'êŸ†',
  'á¸�' => 'á¸€',
  'á¸ƒ' => 'á¸‚',
  'á¸…' => 'á¸„',
  'á¸‡' => 'á¸†',
  'á¸‰' => 'á¸ˆ',
  'á¸‹' => 'á¸Š',
  'á¸�' => 'á¸Œ',
  'á¸�' => 'á¸Ž',
  'á¸‘' => 'á¸�',
  'á¸“' => 'á¸’',
  'á¸•' => 'á¸”',
  'á¸—' => 'á¸–',
  'á¸™' => 'á¸˜',
  'á¸›' => 'á¸š',
  'á¸�' => 'á¸œ',
  'á¸Ÿ' => 'á¸ž',
  'á¸¡' => 'á¸ ',
  'á¸£' => 'á¸¢',
  'á¸¥' => 'á¸¤',
  'á¸§' => 'á¸¦',
  'á¸©' => 'á¸¨',
  'á¸«' => 'á¸ª',
  'á¸­' => 'á¸¬',
  'á¸¯' => 'á¸®',
  'á¸±' => 'á¸°',
  'á¸³' => 'á¸²',
  'á¸µ' => 'á¸´',
  'á¸·' => 'á¸¶',
  'á¸¹' => 'á¸¸',
  'á¸»' => 'á¸º',
  'á¸½' => 'á¸¼',
  'á¸¿' => 'á¸¾',
  'á¹�' => 'á¹€',
  'á¹ƒ' => 'á¹‚',
  'á¹…' => 'á¹„',
  'á¹‡' => 'á¹†',
  'á¹‰' => 'á¹ˆ',
  'á¹‹' => 'á¹Š',
  'á¹�' => 'á¹Œ',
  'á¹�' => 'á¹Ž',
  'á¹‘' => 'á¹�',
  'á¹“' => 'á¹’',
  'á¹•' => 'á¹”',
  'á¹—' => 'á¹–',
  'á¹™' => 'á¹˜',
  'á¹›' => 'á¹š',
  'á¹�' => 'á¹œ',
  'á¹Ÿ' => 'á¹ž',
  'á¹¡' => 'á¹ ',
  'á¹£' => 'á¹¢',
  'á¹¥' => 'á¹¤',
  'á¹§' => 'á¹¦',
  'á¹©' => 'á¹¨',
  'á¹«' => 'á¹ª',
  'á¹­' => 'á¹¬',
  'á¹¯' => 'á¹®',
  'á¹±' => 'á¹°',
  'á¹³' => 'á¹²',
  'á¹µ' => 'á¹´',
  'á¹·' => 'á¹¶',
  'á¹¹' => 'á¹¸',
  'á¹»' => 'á¹º',
  'á¹½' => 'á¹¼',
  'á¹¿' => 'á¹¾',
  'áº�' => 'áº€',
  'áºƒ' => 'áº‚',
  'áº…' => 'áº„',
  'áº‡' => 'áº†',
  'áº‰' => 'áºˆ',
  'áº‹' => 'áºŠ',
  'áº�' => 'áºŒ',
  'áº�' => 'áºŽ',
  'áº‘' => 'áº�',
  'áº“' => 'áº’',
  'áº•' => 'áº”',
  'áº›' => 'á¹ ',
  'áº¡' => 'áº ',
  'áº£' => 'áº¢',
  'áº¥' => 'áº¤',
  'áº§' => 'áº¦',
  'áº©' => 'áº¨',
  'áº«' => 'áºª',
  'áº­' => 'áº¬',
  'áº¯' => 'áº®',
  'áº±' => 'áº°',
  'áº³' => 'áº²',
  'áºµ' => 'áº´',
  'áº·' => 'áº¶',
  'áº¹' => 'áº¸',
  'áº»' => 'áºº',
  'áº½' => 'áº¼',
  'áº¿' => 'áº¾',
  'á»�' => 'á»€',
  'á»ƒ' => 'á»‚',
  'á»…' => 'á»„',
  'á»‡' => 'á»†',
  'á»‰' => 'á»ˆ',
  'á»‹' => 'á»Š',
  'á»�' => 'á»Œ',
  'á»�' => 'á»Ž',
  'á»‘' => 'á»�',
  'á»“' => 'á»’',
  'á»•' => 'á»”',
  'á»—' => 'á»–',
  'á»™' => 'á»˜',
  'á»›' => 'á»š',
  'á»�' => 'á»œ',
  'á»Ÿ' => 'á»ž',
  'á»¡' => 'á» ',
  'á»£' => 'á»¢',
  'á»¥' => 'á»¤',
  'á»§' => 'á»¦',
  'á»©' => 'á»¨',
  'á»«' => 'á»ª',
  'á»­' => 'á»¬',
  'á»¯' => 'á»®',
  'á»±' => 'á»°',
  'á»³' => 'á»²',
  'á»µ' => 'á»´',
  'á»·' => 'á»¶',
  'á»¹' => 'á»¸',
  'á»»' => 'á»º',
  'á»½' => 'á»¼',
  'á»¿' => 'á»¾',
  'á¼€' => 'á¼ˆ',
  'á¼�' => 'á¼‰',
  'á¼‚' => 'á¼Š',
  'á¼ƒ' => 'á¼‹',
  'á¼„' => 'á¼Œ',
  'á¼…' => 'á¼�',
  'á¼†' => 'á¼Ž',
  'á¼‡' => 'á¼�',
  'á¼�' => 'á¼˜',
  'á¼‘' => 'á¼™',
  'á¼’' => 'á¼š',
  'á¼“' => 'á¼›',
  'á¼”' => 'á¼œ',
  'á¼•' => 'á¼�',
  'á¼ ' => 'á¼¨',
  'á¼¡' => 'á¼©',
  'á¼¢' => 'á¼ª',
  'á¼£' => 'á¼«',
  'á¼¤' => 'á¼¬',
  'á¼¥' => 'á¼­',
  'á¼¦' => 'á¼®',
  'á¼§' => 'á¼¯',
  'á¼°' => 'á¼¸',
  'á¼±' => 'á¼¹',
  'á¼²' => 'á¼º',
  'á¼³' => 'á¼»',
  'á¼´' => 'á¼¼',
  'á¼µ' => 'á¼½',
  'á¼¶' => 'á¼¾',
  'á¼·' => 'á¼¿',
  'á½€' => 'á½ˆ',
  'á½�' => 'á½‰',
  'á½‚' => 'á½Š',
  'á½ƒ' => 'á½‹',
  'á½„' => 'á½Œ',
  'á½…' => 'á½�',
  'á½‘' => 'á½™',
  'á½“' => 'á½›',
  'á½•' => 'á½�',
  'á½—' => 'á½Ÿ',
  'á½ ' => 'á½¨',
  'á½¡' => 'á½©',
  'á½¢' => 'á½ª',
  'á½£' => 'á½«',
  'á½¤' => 'á½¬',
  'á½¥' => 'á½­',
  'á½¦' => 'á½®',
  'á½§' => 'á½¯',
  'á½°' => 'á¾º',
  'á½±' => 'á¾»',
  'á½²' => 'á¿ˆ',
  'á½³' => 'á¿‰',
  'á½´' => 'á¿Š',
  'á½µ' => 'á¿‹',
  'á½¶' => 'á¿š',
  'á½·' => 'á¿›',
  'á½¸' => 'á¿¸',
  'á½¹' => 'á¿¹',
  'á½º' => 'á¿ª',
  'á½»' => 'á¿«',
  'á½¼' => 'á¿º',
  'á½½' => 'á¿»',
  'á¾€' => 'á¼ˆÎ™',
  'á¾�' => 'á¼‰Î™',
  'á¾‚' => 'á¼ŠÎ™',
  'á¾ƒ' => 'á¼‹Î™',
  'á¾„' => 'á¼ŒÎ™',
  'á¾…' => 'á¼�Î™',
  'á¾†' => 'á¼ŽÎ™',
  'á¾‡' => 'á¼�Î™',
  'á¾�' => 'á¼¨Î™',
  'á¾‘' => 'á¼©Î™',
  'á¾’' => 'á¼ªÎ™',
  'á¾“' => 'á¼«Î™',
  'á¾”' => 'á¼¬Î™',
  'á¾•' => 'á¼­Î™',
  'á¾–' => 'á¼®Î™',
  'á¾—' => 'á¼¯Î™',
  'á¾ ' => 'á½¨Î™',
  'á¾¡' => 'á½©Î™',
  'á¾¢' => 'á½ªÎ™',
  'á¾£' => 'á½«Î™',
  'á¾¤' => 'á½¬Î™',
  'á¾¥' => 'á½­Î™',
  'á¾¦' => 'á½®Î™',
  'á¾§' => 'á½¯Î™',
  'á¾°' => 'á¾¸',
  'á¾±' => 'á¾¹',
  'á¾³' => 'Î‘Î™',
  'á¾¾' => 'Î™',
  'á¿ƒ' => 'Î—Î™',
  'á¿�' => 'á¿˜',
  'á¿‘' => 'á¿™',
  'á¿ ' => 'á¿¨',
  'á¿¡' => 'á¿©',
  'á¿¥' => 'á¿¬',
  'á¿³' => 'Î©Î™',
  'â…Ž' => 'â„²',
  'â…°' => 'â… ',
  'â…±' => 'â…¡',
  'â…²' => 'â…¢',
  'â…³' => 'â…£',
  'â…´' => 'â…¤',
  'â…µ' => 'â…¥',
  'â…¶' => 'â…¦',
  'â…·' => 'â…§',
  'â…¸' => 'â…¨',
  'â…¹' => 'â…©',
  'â…º' => 'â…ª',
  'â…»' => 'â…«',
  'â…¼' => 'â…¬',
  'â…½' => 'â…­',
  'â…¾' => 'â…®',
  'â…¿' => 'â…¯',
  'â†„' => 'â†ƒ',
  'â“�' => 'â’¶',
  'â“‘' => 'â’·',
  'â“’' => 'â’¸',
  'â““' => 'â’¹',
  'â“”' => 'â’º',
  'â“•' => 'â’»',
  'â“–' => 'â’¼',
  'â“—' => 'â’½',
  'â“˜' => 'â’¾',
  'â“™' => 'â’¿',
  'â“š' => 'â“€',
  'â“›' => 'â“�',
  'â“œ' => 'â“‚',
  'â“�' => 'â“ƒ',
  'â“ž' => 'â“„',
  'â“Ÿ' => 'â“…',
  'â“ ' => 'â“†',
  'â“¡' => 'â“‡',
  'â“¢' => 'â“ˆ',
  'â“£' => 'â“‰',
  'â“¤' => 'â“Š',
  'â“¥' => 'â“‹',
  'â“¦' => 'â“Œ',
  'â“§' => 'â“�',
  'â“¨' => 'â“Ž',
  'â“©' => 'â“�',
  'â°°' => 'â°€',
  'â°±' => 'â°�',
  'â°²' => 'â°‚',
  'â°³' => 'â°ƒ',
  'â°´' => 'â°„',
  'â°µ' => 'â°…',
  'â°¶' => 'â°†',
  'â°·' => 'â°‡',
  'â°¸' => 'â°ˆ',
  'â°¹' => 'â°‰',
  'â°º' => 'â°Š',
  'â°»' => 'â°‹',
  'â°¼' => 'â°Œ',
  'â°½' => 'â°�',
  'â°¾' => 'â°Ž',
  'â°¿' => 'â°�',
  'â±€' => 'â°�',
  'â±�' => 'â°‘',
  'â±‚' => 'â°’',
  'â±ƒ' => 'â°“',
  'â±„' => 'â°”',
  'â±…' => 'â°•',
  'â±†' => 'â°–',
  'â±‡' => 'â°—',
  'â±ˆ' => 'â°˜',
  'â±‰' => 'â°™',
  'â±Š' => 'â°š',
  'â±‹' => 'â°›',
  'â±Œ' => 'â°œ',
  'â±�' => 'â°�',
  'â±Ž' => 'â°ž',
  'â±�' => 'â°Ÿ',
  'â±�' => 'â° ',
  'â±‘' => 'â°¡',
  'â±’' => 'â°¢',
  'â±“' => 'â°£',
  'â±”' => 'â°¤',
  'â±•' => 'â°¥',
  'â±–' => 'â°¦',
  'â±—' => 'â°§',
  'â±˜' => 'â°¨',
  'â±™' => 'â°©',
  'â±š' => 'â°ª',
  'â±›' => 'â°«',
  'â±œ' => 'â°¬',
  'â±�' => 'â°­',
  'â±ž' => 'â°®',
  'â±¡' => 'â± ',
  'â±¥' => 'Èº',
  'â±¦' => 'È¾',
  'â±¨' => 'â±§',
  'â±ª' => 'â±©',
  'â±¬' => 'â±«',
  'â±³' => 'â±²',
  'â±¶' => 'â±µ',
  'â²�' => 'â²€',
  'â²ƒ' => 'â²‚',
  'â²…' => 'â²„',
  'â²‡' => 'â²†',
  'â²‰' => 'â²ˆ',
  'â²‹' => 'â²Š',
  'â²�' => 'â²Œ',
  'â²�' => 'â²Ž',
  'â²‘' => 'â²�',
  'â²“' => 'â²’',
  'â²•' => 'â²”',
  'â²—' => 'â²–',
  'â²™' => 'â²˜',
  'â²›' => 'â²š',
  'â²�' => 'â²œ',
  'â²Ÿ' => 'â²ž',
  'â²¡' => 'â² ',
  'â²£' => 'â²¢',
  'â²¥' => 'â²¤',
  'â²§' => 'â²¦',
  'â²©' => 'â²¨',
  'â²«' => 'â²ª',
  'â²­' => 'â²¬',
  'â²¯' => 'â²®',
  'â²±' => 'â²°',
  'â²³' => 'â²²',
  'â²µ' => 'â²´',
  'â²·' => 'â²¶',
  'â²¹' => 'â²¸',
  'â²»' => 'â²º',
  'â²½' => 'â²¼',
  'â²¿' => 'â²¾',
  'â³�' => 'â³€',
  'â³ƒ' => 'â³‚',
  'â³…' => 'â³„',
  'â³‡' => 'â³†',
  'â³‰' => 'â³ˆ',
  'â³‹' => 'â³Š',
  'â³�' => 'â³Œ',
  'â³�' => 'â³Ž',
  'â³‘' => 'â³�',
  'â³“' => 'â³’',
  'â³•' => 'â³”',
  'â³—' => 'â³–',
  'â³™' => 'â³˜',
  'â³›' => 'â³š',
  'â³�' => 'â³œ',
  'â³Ÿ' => 'â³ž',
  'â³¡' => 'â³ ',
  'â³£' => 'â³¢',
  'â³¬' => 'â³«',
  'â³®' => 'â³­',
  'â³³' => 'â³²',
  'â´€' => 'á‚ ',
  'â´�' => 'á‚¡',
  'â´‚' => 'á‚¢',
  'â´ƒ' => 'á‚£',
  'â´„' => 'á‚¤',
  'â´…' => 'á‚¥',
  'â´†' => 'á‚¦',
  'â´‡' => 'á‚§',
  'â´ˆ' => 'á‚¨',
  'â´‰' => 'á‚©',
  'â´Š' => 'á‚ª',
  'â´‹' => 'á‚«',
  'â´Œ' => 'á‚¬',
  'â´�' => 'á‚­',
  'â´Ž' => 'á‚®',
  'â´�' => 'á‚¯',
  'â´�' => 'á‚°',
  'â´‘' => 'á‚±',
  'â´’' => 'á‚²',
  'â´“' => 'á‚³',
  'â´”' => 'á‚´',
  'â´•' => 'á‚µ',
  'â´–' => 'á‚¶',
  'â´—' => 'á‚·',
  'â´˜' => 'á‚¸',
  'â´™' => 'á‚¹',
  'â´š' => 'á‚º',
  'â´›' => 'á‚»',
  'â´œ' => 'á‚¼',
  'â´�' => 'á‚½',
  'â´ž' => 'á‚¾',
  'â´Ÿ' => 'á‚¿',
  'â´ ' => 'áƒ€',
  'â´¡' => 'áƒ�',
  'â´¢' => 'áƒ‚',
  'â´£' => 'áƒƒ',
  'â´¤' => 'áƒ„',
  'â´¥' => 'áƒ…',
  'â´§' => 'áƒ‡',
  'â´­' => 'áƒ�',
  'ê™�' => 'ê™€',
  'ê™ƒ' => 'ê™‚',
  'ê™…' => 'ê™„',
  'ê™‡' => 'ê™†',
  'ê™‰' => 'ê™ˆ',
  'ê™‹' => 'ê™Š',
  'ê™�' => 'ê™Œ',
  'ê™�' => 'ê™Ž',
  'ê™‘' => 'ê™�',
  'ê™“' => 'ê™’',
  'ê™•' => 'ê™”',
  'ê™—' => 'ê™–',
  'ê™™' => 'ê™˜',
  'ê™›' => 'ê™š',
  'ê™�' => 'ê™œ',
  'ê™Ÿ' => 'ê™ž',
  'ê™¡' => 'ê™ ',
  'ê™£' => 'ê™¢',
  'ê™¥' => 'ê™¤',
  'ê™§' => 'ê™¦',
  'ê™©' => 'ê™¨',
  'ê™«' => 'ê™ª',
  'ê™­' => 'ê™¬',
  'êš�' => 'êš€',
  'êšƒ' => 'êš‚',
  'êš…' => 'êš„',
  'êš‡' => 'êš†',
  'êš‰' => 'êšˆ',
  'êš‹' => 'êšŠ',
  'êš�' => 'êšŒ',
  'êš�' => 'êšŽ',
  'êš‘' => 'êš�',
  'êš“' => 'êš’',
  'êš•' => 'êš”',
  'êš—' => 'êš–',
  'êš™' => 'êš˜',
  'êš›' => 'êšš',
  'êœ£' => 'êœ¢',
  'êœ¥' => 'êœ¤',
  'êœ§' => 'êœ¦',
  'êœ©' => 'êœ¨',
  'êœ«' => 'êœª',
  'êœ­' => 'êœ¬',
  'êœ¯' => 'êœ®',
  'êœ³' => 'êœ²',
  'êœµ' => 'êœ´',
  'êœ·' => 'êœ¶',
  'êœ¹' => 'êœ¸',
  'êœ»' => 'êœº',
  'êœ½' => 'êœ¼',
  'êœ¿' => 'êœ¾',
  'ê��' => 'ê�€',
  'ê�ƒ' => 'ê�‚',
  'ê�…' => 'ê�„',
  'ê�‡' => 'ê�†',
  'ê�‰' => 'ê�ˆ',
  'ê�‹' => 'ê�Š',
  'ê��' => 'ê�Œ',
  'ê��' => 'ê�Ž',
  'ê�‘' => 'ê��',
  'ê�“' => 'ê�’',
  'ê�•' => 'ê�”',
  'ê�—' => 'ê�–',
  'ê�™' => 'ê�˜',
  'ê�›' => 'ê�š',
  'ê��' => 'ê�œ',
  'ê�Ÿ' => 'ê�ž',
  'ê�¡' => 'ê� ',
  'ê�£' => 'ê�¢',
  'ê�¥' => 'ê�¤',
  'ê�§' => 'ê�¦',
  'ê�©' => 'ê�¨',
  'ê�«' => 'ê�ª',
  'ê�­' => 'ê�¬',
  'ê�¯' => 'ê�®',
  'ê�º' => 'ê�¹',
  'ê�¼' => 'ê�»',
  'ê�¿' => 'ê�¾',
  'êž�' => 'êž€',
  'êžƒ' => 'êž‚',
  'êž…' => 'êž„',
  'êž‡' => 'êž†',
  'êžŒ' => 'êž‹',
  'êž‘' => 'êž�',
  'êž“' => 'êž’',
  'êž”' => 'êŸ„',
  'êž—' => 'êž–',
  'êž™' => 'êž˜',
  'êž›' => 'êžš',
  'êž�' => 'êžœ',
  'êžŸ' => 'êžž',
  'êž¡' => 'êž ',
  'êž£' => 'êž¢',
  'êž¥' => 'êž¤',
  'êž§' => 'êž¦',
  'êž©' => 'êž¨',
  'êžµ' => 'êž´',
  'êž·' => 'êž¶',
  'êž¹' => 'êž¸',
  'êž»' => 'êžº',
  'êž½' => 'êž¼',
  'êž¿' => 'êž¾',
  'êŸƒ' => 'êŸ‚',
  'êŸˆ' => 'êŸ‡',
  'êŸŠ' => 'êŸ‰',
  'êŸ¶' => 'êŸµ',
  'ê­“' => 'êž³',
  'ê­°' => 'áŽ ',
  'ê­±' => 'áŽ¡',
  'ê­²' => 'áŽ¢',
  'ê­³' => 'áŽ£',
  'ê­´' => 'áŽ¤',
  'ê­µ' => 'áŽ¥',
  'ê­¶' => 'áŽ¦',
  'ê­·' => 'áŽ§',
  'ê­¸' => 'áŽ¨',
  'ê­¹' => 'áŽ©',
  'ê­º' => 'áŽª',
  'ê­»' => 'áŽ«',
  'ê­¼' => 'áŽ¬',
  'ê­½' => 'áŽ­',
  'ê­¾' => 'áŽ®',
  'ê­¿' => 'áŽ¯',
  'ê®€' => 'áŽ°',
  'ê®�' => 'áŽ±',
  'ê®‚' => 'áŽ²',
  'ê®ƒ' => 'áŽ³',
  'ê®„' => 'áŽ´',
  'ê®…' => 'áŽµ',
  'ê®†' => 'áŽ¶',
  'ê®‡' => 'áŽ·',
  'ê®ˆ' => 'áŽ¸',
  'ê®‰' => 'áŽ¹',
  'ê®Š' => 'áŽº',
  'ê®‹' => 'áŽ»',
  'ê®Œ' => 'áŽ¼',
  'ê®�' => 'áŽ½',
  'ê®Ž' => 'áŽ¾',
  'ê®�' => 'áŽ¿',
  'ê®�' => 'á�€',
  'ê®‘' => 'á��',
  'ê®’' => 'á�‚',
  'ê®“' => 'á�ƒ',
  'ê®”' => 'á�„',
  'ê®•' => 'á�…',
  'ê®–' => 'á�†',
  'ê®—' => 'á�‡',
  'ê®˜' => 'á�ˆ',
  'ê®™' => 'á�‰',
  'ê®š' => 'á�Š',
  'ê®›' => 'á�‹',
  'ê®œ' => 'á�Œ',
  'ê®�' => 'á��',
  'ê®ž' => 'á�Ž',
  'ê®Ÿ' => 'á��',
  'ê® ' => 'á��',
  'ê®¡' => 'á�‘',
  'ê®¢' => 'á�’',
  'ê®£' => 'á�“',
  'ê®¤' => 'á�”',
  'ê®¥' => 'á�•',
  'ê®¦' => 'á�–',
  'ê®§' => 'á�—',
  'ê®¨' => 'á�˜',
  'ê®©' => 'á�™',
  'ê®ª' => 'á�š',
  'ê®«' => 'á�›',
  'ê®¬' => 'á�œ',
  'ê®­' => 'á��',
  'ê®®' => 'á�ž',
  'ê®¯' => 'á�Ÿ',
  'ê®°' => 'á� ',
  'ê®±' => 'á�¡',
  'ê®²' => 'á�¢',
  'ê®³' => 'á�£',
  'ê®´' => 'á�¤',
  'ê®µ' => 'á�¥',
  'ê®¶' => 'á�¦',
  'ê®·' => 'á�§',
  'ê®¸' => 'á�¨',
  'ê®¹' => 'á�©',
  'ê®º' => 'á�ª',
  'ê®»' => 'á�«',
  'ê®¼' => 'á�¬',
  'ê®½' => 'á�­',
  'ê®¾' => 'á�®',
  'ê®¿' => 'á�¯',
  'ï½�' => 'ï¼¡',
  'ï½‚' => 'ï¼¢',
  'ï½ƒ' => 'ï¼£',
  'ï½„' => 'ï¼¤',
  'ï½…' => 'ï¼¥',
  'ï½†' => 'ï¼¦',
  'ï½‡' => 'ï¼§',
  'ï½ˆ' => 'ï¼¨',
  'ï½‰' => 'ï¼©',
  'ï½Š' => 'ï¼ª',
  'ï½‹' => 'ï¼«',
  'ï½Œ' => 'ï¼¬',
  'ï½�' => 'ï¼­',
  'ï½Ž' => 'ï¼®',
  'ï½�' => 'ï¼¯',
  'ï½�' => 'ï¼°',
  'ï½‘' => 'ï¼±',
  'ï½’' => 'ï¼²',
  'ï½“' => 'ï¼³',
  'ï½”' => 'ï¼´',
  'ï½•' => 'ï¼µ',
  'ï½–' => 'ï¼¶',
  'ï½—' => 'ï¼·',
  'ï½˜' => 'ï¼¸',
  'ï½™' => 'ï¼¹',
  'ï½š' => 'ï¼º',
  'ð��¨' => 'ð��€',
  'ð��©' => 'ð���',
  'ð��ª' => 'ð��‚',
  'ð��«' => 'ð��ƒ',
  'ð��¬' => 'ð��„',
  'ð��­' => 'ð��…',
  'ð��®' => 'ð��†',
  'ð��¯' => 'ð��‡',
  'ð��°' => 'ð��ˆ',
  'ð��±' => 'ð��‰',
  'ð��²' => 'ð��Š',
  'ð��³' => 'ð��‹',
  'ð��´' => 'ð��Œ',
  'ð��µ' => 'ð���',
  'ð��¶' => 'ð��Ž',
  'ð��·' => 'ð���',
  'ð��¸' => 'ð���',
  'ð��¹' => 'ð��‘',
  'ð��º' => 'ð��’',
  'ð��»' => 'ð��“',
  'ð��¼' => 'ð��”',
  'ð��½' => 'ð��•',
  'ð��¾' => 'ð��–',
  'ð��¿' => 'ð��—',
  'ð�‘€' => 'ð��˜',
  'ð�‘�' => 'ð��™',
  'ð�‘‚' => 'ð��š',
  'ð�‘ƒ' => 'ð��›',
  'ð�‘„' => 'ð��œ',
  'ð�‘…' => 'ð���',
  'ð�‘†' => 'ð��ž',
  'ð�‘‡' => 'ð��Ÿ',
  'ð�‘ˆ' => 'ð�� ',
  'ð�‘‰' => 'ð��¡',
  'ð�‘Š' => 'ð��¢',
  'ð�‘‹' => 'ð��£',
  'ð�‘Œ' => 'ð��¤',
  'ð�‘�' => 'ð��¥',
  'ð�‘Ž' => 'ð��¦',
  'ð�‘�' => 'ð��§',
  'ð�“˜' => 'ð�’°',
  'ð�“™' => 'ð�’±',
  'ð�“š' => 'ð�’²',
  'ð�“›' => 'ð�’³',
  'ð�“œ' => 'ð�’´',
  'ð�“�' => 'ð�’µ',
  'ð�“ž' => 'ð�’¶',
  'ð�“Ÿ' => 'ð�’·',
  'ð�“ ' => 'ð�’¸',
  'ð�“¡' => 'ð�’¹',
  'ð�“¢' => 'ð�’º',
  'ð�“£' => 'ð�’»',
  'ð�“¤' => 'ð�’¼',
  'ð�“¥' => 'ð�’½',
  'ð�“¦' => 'ð�’¾',
  'ð�“§' => 'ð�’¿',
  'ð�“¨' => 'ð�“€',
  'ð�“©' => 'ð�“�',
  'ð�“ª' => 'ð�“‚',
  'ð�“«' => 'ð�“ƒ',
  'ð�“¬' => 'ð�“„',
  'ð�“­' => 'ð�“…',
  'ð�“®' => 'ð�“†',
  'ð�“¯' => 'ð�“‡',
  'ð�“°' => 'ð�“ˆ',
  'ð�“±' => 'ð�“‰',
  'ð�“²' => 'ð�“Š',
  'ð�“³' => 'ð�“‹',
  'ð�“´' => 'ð�“Œ',
  'ð�“µ' => 'ð�“�',
  'ð�“¶' => 'ð�“Ž',
  'ð�“·' => 'ð�“�',
  'ð�“¸' => 'ð�“�',
  'ð�“¹' => 'ð�“‘',
  'ð�“º' => 'ð�“’',
  'ð�“»' => 'ð�““',
  'ð�³€' => 'ð�²€',
  'ð�³�' => 'ð�²�',
  'ð�³‚' => 'ð�²‚',
  'ð�³ƒ' => 'ð�²ƒ',
  'ð�³„' => 'ð�²„',
  'ð�³…' => 'ð�²…',
  'ð�³†' => 'ð�²†',
  'ð�³‡' => 'ð�²‡',
  'ð�³ˆ' => 'ð�²ˆ',
  'ð�³‰' => 'ð�²‰',
  'ð�³Š' => 'ð�²Š',
  'ð�³‹' => 'ð�²‹',
  'ð�³Œ' => 'ð�²Œ',
  'ð�³�' => 'ð�²�',
  'ð�³Ž' => 'ð�²Ž',
  'ð�³�' => 'ð�²�',
  'ð�³�' => 'ð�²�',
  'ð�³‘' => 'ð�²‘',
  'ð�³’' => 'ð�²’',
  'ð�³“' => 'ð�²“',
  'ð�³”' => 'ð�²”',
  'ð�³•' => 'ð�²•',
  'ð�³–' => 'ð�²–',
  'ð�³—' => 'ð�²—',
  'ð�³˜' => 'ð�²˜',
  'ð�³™' => 'ð�²™',
  'ð�³š' => 'ð�²š',
  'ð�³›' => 'ð�²›',
  'ð�³œ' => 'ð�²œ',
  'ð�³�' => 'ð�²�',
  'ð�³ž' => 'ð�²ž',
  'ð�³Ÿ' => 'ð�²Ÿ',
  'ð�³ ' => 'ð�² ',
  'ð�³¡' => 'ð�²¡',
  'ð�³¢' => 'ð�²¢',
  'ð�³£' => 'ð�²£',
  'ð�³¤' => 'ð�²¤',
  'ð�³¥' => 'ð�²¥',
  'ð�³¦' => 'ð�²¦',
  'ð�³§' => 'ð�²§',
  'ð�³¨' => 'ð�²¨',
  'ð�³©' => 'ð�²©',
  'ð�³ª' => 'ð�²ª',
  'ð�³«' => 'ð�²«',
  'ð�³¬' => 'ð�²¬',
  'ð�³­' => 'ð�²­',
  'ð�³®' => 'ð�²®',
  'ð�³¯' => 'ð�²¯',
  'ð�³°' => 'ð�²°',
  'ð�³±' => 'ð�²±',
  'ð�³²' => 'ð�²²',
  'ð‘£€' => 'ð‘¢ ',
  'ð‘£�' => 'ð‘¢¡',
  'ð‘£‚' => 'ð‘¢¢',
  'ð‘£ƒ' => 'ð‘¢£',
  'ð‘£„' => 'ð‘¢¤',
  'ð‘£…' => 'ð‘¢¥',
  'ð‘£†' => 'ð‘¢¦',
  'ð‘£‡' => 'ð‘¢§',
  'ð‘£ˆ' => 'ð‘¢¨',
  'ð‘£‰' => 'ð‘¢©',
  'ð‘£Š' => 'ð‘¢ª',
  'ð‘£‹' => 'ð‘¢«',
  'ð‘£Œ' => 'ð‘¢¬',
  'ð‘£�' => 'ð‘¢­',
  'ð‘£Ž' => 'ð‘¢®',
  'ð‘£�' => 'ð‘¢¯',
  'ð‘£�' => 'ð‘¢°',
  'ð‘£‘' => 'ð‘¢±',
  'ð‘£’' => 'ð‘¢²',
  'ð‘£“' => 'ð‘¢³',
  'ð‘£”' => 'ð‘¢´',
  'ð‘£•' => 'ð‘¢µ',
  'ð‘£–' => 'ð‘¢¶',
  'ð‘£—' => 'ð‘¢·',
  'ð‘£˜' => 'ð‘¢¸',
  'ð‘£™' => 'ð‘¢¹',
  'ð‘£š' => 'ð‘¢º',
  'ð‘£›' => 'ð‘¢»',
  'ð‘£œ' => 'ð‘¢¼',
  'ð‘£�' => 'ð‘¢½',
  'ð‘£ž' => 'ð‘¢¾',
  'ð‘£Ÿ' => 'ð‘¢¿',
  'ð–¹ ' => 'ð–¹€',
  'ð–¹¡' => 'ð–¹�',
  'ð–¹¢' => 'ð–¹‚',
  'ð–¹£' => 'ð–¹ƒ',
  'ð–¹¤' => 'ð–¹„',
  'ð–¹¥' => 'ð–¹…',
  'ð–¹¦' => 'ð–¹†',
  'ð–¹§' => 'ð–¹‡',
  'ð–¹¨' => 'ð–¹ˆ',
  'ð–¹©' => 'ð–¹‰',
  'ð–¹ª' => 'ð–¹Š',
  'ð–¹«' => 'ð–¹‹',
  'ð–¹¬' => 'ð–¹Œ',
  'ð–¹­' => 'ð–¹�',
  'ð–¹®' => 'ð–¹Ž',
  'ð–¹¯' => 'ð–¹�',
  'ð–¹°' => 'ð–¹�',
  'ð–¹±' => 'ð–¹‘',
  'ð–¹²' => 'ð–¹’',
  'ð–¹³' => 'ð–¹“',
  'ð–¹´' => 'ð–¹”',
  'ð–¹µ' => 'ð–¹•',
  'ð–¹¶' => 'ð–¹–',
  'ð–¹·' => 'ð–¹—',
  'ð–¹¸' => 'ð–¹˜',
  'ð–¹¹' => 'ð–¹™',
  'ð–¹º' => 'ð–¹š',
  'ð–¹»' => 'ð–¹›',
  'ð–¹¼' => 'ð–¹œ',
  'ð–¹½' => 'ð–¹�',
  'ð–¹¾' => 'ð–¹ž',
  'ð–¹¿' => 'ð–¹Ÿ',
  'ðž¤¢' => 'ðž¤€',
  'ðž¤£' => 'ðž¤�',
  'ðž¤¤' => 'ðž¤‚',
  'ðž¤¥' => 'ðž¤ƒ',
  'ðž¤¦' => 'ðž¤„',
  'ðž¤§' => 'ðž¤…',
  'ðž¤¨' => 'ðž¤†',
  'ðž¤©' => 'ðž¤‡',
  'ðž¤ª' => 'ðž¤ˆ',
  'ðž¤«' => 'ðž¤‰',
  'ðž¤¬' => 'ðž¤Š',
  'ðž¤­' => 'ðž¤‹',
  'ðž¤®' => 'ðž¤Œ',
  'ðž¤¯' => 'ðž¤�',
  'ðž¤°' => 'ðž¤Ž',
  'ðž¤±' => 'ðž¤�',
  'ðž¤²' => 'ðž¤�',
  'ðž¤³' => 'ðž¤‘',
  'ðž¤´' => 'ðž¤’',
  'ðž¤µ' => 'ðž¤“',
  'ðž¤¶' => 'ðž¤”',
  'ðž¤·' => 'ðž¤•',
  'ðž¤¸' => 'ðž¤–',
  'ðž¤¹' => 'ðž¤—',
  'ðž¤º' => 'ðž¤˜',
  'ðž¤»' => 'ðž¤™',
  'ðž¤¼' => 'ðž¤š',
  'ðž¤½' => 'ðž¤›',
  'ðž¤¾' => 'ðž¤œ',
  'ðž¤¿' => 'ðž¤�',
  'ðž¥€' => 'ðž¤ž',
  'ðž¥�' => 'ðž¤Ÿ',
  'ðž¥‚' => 'ðž¤ ',
  'ðž¥ƒ' => 'ðž¤¡',
  'ÃŸ' => 'SS',
  'ï¬€' => 'FF',
  'ï¬�' => 'FI',
  'ï¬‚' => 'FL',
  'ï¬ƒ' => 'FFI',
  'ï¬„' => 'FFL',
  'ï¬…' => 'ST',
  'ï¬†' => 'ST',
  'Ö‡' => 'ÔµÕ’',
  'ï¬“' => 'Õ„Õ†',
  'ï¬”' => 'Õ„Ôµ',
  'ï¬•' => 'Õ„Ô»',
  'ï¬–' => 'ÕŽÕ†',
  'ï¬—' => 'Õ„Ô½',
  'Å‰' => 'Ê¼N',
  'Î�' => 'Î™ÌˆÌ�',
  'Î°' => 'Î¥ÌˆÌ�',
  'Ç°' => 'JÌŒ',
  'áº–' => 'HÌ±',
  'áº—' => 'TÌˆ',
  'áº˜' => 'WÌŠ',
  'áº™' => 'YÌŠ',
  'áºš' => 'AÊ¾',
  'á½�' => 'Î¥Ì“',
  'á½’' => 'Î¥Ì“Ì€',
  'á½”' => 'Î¥Ì“Ì�',
  'á½–' => 'Î¥Ì“Í‚',
  'á¾¶' => 'Î‘Í‚',
  'á¿†' => 'Î—Í‚',
  'á¿’' => 'Î™ÌˆÌ€',
  'á¿“' => 'Î™ÌˆÌ�',
  'á¿–' => 'Î™Í‚',
  'á¿—' => 'Î™ÌˆÍ‚',
  'á¿¢' => 'Î¥ÌˆÌ€',
  'á¿£' => 'Î¥ÌˆÌ�',
  'á¿¤' => 'Î¡Ì“',
  'á¿¦' => 'Î¥Í‚',
  'á¿§' => 'Î¥ÌˆÍ‚',
  'á¿¶' => 'Î©Í‚',
  'á¾ˆ' => 'á¼ˆÎ™',
  'á¾‰' => 'á¼‰Î™',
  'á¾Š' => 'á¼ŠÎ™',
  'á¾‹' => 'á¼‹Î™',
  'á¾Œ' => 'á¼ŒÎ™',
  'á¾�' => 'á¼�Î™',
  'á¾Ž' => 'á¼ŽÎ™',
  'á¾�' => 'á¼�Î™',
  'á¾˜' => 'á¼¨Î™',
  'á¾™' => 'á¼©Î™',
  'á¾š' => 'á¼ªÎ™',
  'á¾›' => 'á¼«Î™',
  'á¾œ' => 'á¼¬Î™',
  'á¾�' => 'á¼­Î™',
  'á¾ž' => 'á¼®Î™',
  'á¾Ÿ' => 'á¼¯Î™',
  'á¾¨' => 'á½¨Î™',
  'á¾©' => 'á½©Î™',
  'á¾ª' => 'á½ªÎ™',
  'á¾«' => 'á½«Î™',
  'á¾¬' => 'á½¬Î™',
  'á¾­' => 'á½­Î™',
  'á¾®' => 'á½®Î™',
  'á¾¯' => 'á½¯Î™',
  'á¾¼' => 'Î‘Î™',
  'á¿Œ' => 'Î—Î™',
  'á¿¼' => 'Î©Î™',
  'á¾²' => 'á¾ºÎ™',
  'á¾´' => 'Î†Î™',
  'á¿‚' => 'á¿ŠÎ™',
  'á¿„' => 'Î‰Î™',
  'á¿²' => 'á¿ºÎ™',
  'á¿´' => 'Î�Î™',
  'á¾·' => 'Î‘Í‚Î™',
  'á¿‡' => 'Î—Í‚Î™',
  'á¿·' => 'Î©Í‚Î™',
);
<?php

return array (
  'A' => 'a',
  'B' => 'b',
  'C' => 'c',
  'D' => 'd',
  'E' => 'e',
  'F' => 'f',
  'G' => 'g',
  'H' => 'h',
  'I' => 'i',
  'J' => 'j',
  'K' => 'k',
  'L' => 'l',
  'M' => 'm',
  'N' => 'n',
  'O' => 'o',
  'P' => 'p',
  'Q' => 'q',
  'R' => 'r',
  'S' => 's',
  'T' => 't',
  'U' => 'u',
  'V' => 'v',
  'W' => 'w',
  'X' => 'x',
  'Y' => 'y',
  'Z' => 'z',
  'Ã€' => 'Ã ',
  'Ã�' => 'Ã¡',
  'Ã‚' => 'Ã¢',
  'Ãƒ' => 'Ã£',
  'Ã„' => 'Ã¤',
  'Ã…' => 'Ã¥',
  'Ã†' => 'Ã¦',
  'Ã‡' => 'Ã§',
  'Ãˆ' => 'Ã¨',
  'Ã‰' => 'Ã©',
  'ÃŠ' => 'Ãª',
  'Ã‹' => 'Ã«',
  'ÃŒ' => 'Ã¬',
  'Ã�' => 'Ã­',
  'ÃŽ' => 'Ã®',
  'Ã�' => 'Ã¯',
  'Ã�' => 'Ã°',
  'Ã‘' => 'Ã±',
  'Ã’' => 'Ã²',
  'Ã“' => 'Ã³',
  'Ã”' => 'Ã´',
  'Ã•' => 'Ãµ',
  'Ã–' => 'Ã¶',
  'Ã˜' => 'Ã¸',
  'Ã™' => 'Ã¹',
  'Ãš' => 'Ãº',
  'Ã›' => 'Ã»',
  'Ãœ' => 'Ã¼',
  'Ã�' => 'Ã½',
  'Ãž' => 'Ã¾',
  'Ä€' => 'Ä�',
  'Ä‚' => 'Äƒ',
  'Ä„' => 'Ä…',
  'Ä†' => 'Ä‡',
  'Äˆ' => 'Ä‰',
  'ÄŠ' => 'Ä‹',
  'ÄŒ' => 'Ä�',
  'ÄŽ' => 'Ä�',
  'Ä�' => 'Ä‘',
  'Ä’' => 'Ä“',
  'Ä”' => 'Ä•',
  'Ä–' => 'Ä—',
  'Ä˜' => 'Ä™',
  'Äš' => 'Ä›',
  'Äœ' => 'Ä�',
  'Äž' => 'ÄŸ',
  'Ä ' => 'Ä¡',
  'Ä¢' => 'Ä£',
  'Ä¤' => 'Ä¥',
  'Ä¦' => 'Ä§',
  'Ä¨' => 'Ä©',
  'Äª' => 'Ä«',
  'Ä¬' => 'Ä­',
  'Ä®' => 'Ä¯',
  'Ä°' => 'iÌ‡',
  'Ä²' => 'Ä³',
  'Ä´' => 'Äµ',
  'Ä¶' => 'Ä·',
  'Ä¹' => 'Äº',
  'Ä»' => 'Ä¼',
  'Ä½' => 'Ä¾',
  'Ä¿' => 'Å€',
  'Å�' => 'Å‚',
  'Åƒ' => 'Å„',
  'Å…' => 'Å†',
  'Å‡' => 'Åˆ',
  'ÅŠ' => 'Å‹',
  'ÅŒ' => 'Å�',
  'ÅŽ' => 'Å�',
  'Å�' => 'Å‘',
  'Å’' => 'Å“',
  'Å”' => 'Å•',
  'Å–' => 'Å—',
  'Å˜' => 'Å™',
  'Åš' => 'Å›',
  'Åœ' => 'Å�',
  'Åž' => 'ÅŸ',
  'Å ' => 'Å¡',
  'Å¢' => 'Å£',
  'Å¤' => 'Å¥',
  'Å¦' => 'Å§',
  'Å¨' => 'Å©',
  'Åª' => 'Å«',
  'Å¬' => 'Å­',
  'Å®' => 'Å¯',
  'Å°' => 'Å±',
  'Å²' => 'Å³',
  'Å´' => 'Åµ',
  'Å¶' => 'Å·',
  'Å¸' => 'Ã¿',
  'Å¹' => 'Åº',
  'Å»' => 'Å¼',
  'Å½' => 'Å¾',
  'Æ�' => 'É“',
  'Æ‚' => 'Æƒ',
  'Æ„' => 'Æ…',
  'Æ†' => 'É”',
  'Æ‡' => 'Æˆ',
  'Æ‰' => 'É–',
  'ÆŠ' => 'É—',
  'Æ‹' => 'ÆŒ',
  'ÆŽ' => 'Ç�',
  'Æ�' => 'É™',
  'Æ�' => 'É›',
  'Æ‘' => 'Æ’',
  'Æ“' => 'É ',
  'Æ”' => 'É£',
  'Æ–' => 'É©',
  'Æ—' => 'É¨',
  'Æ˜' => 'Æ™',
  'Æœ' => 'É¯',
  'Æ�' => 'É²',
  'ÆŸ' => 'Éµ',
  'Æ ' => 'Æ¡',
  'Æ¢' => 'Æ£',
  'Æ¤' => 'Æ¥',
  'Æ¦' => 'Ê€',
  'Æ§' => 'Æ¨',
  'Æ©' => 'Êƒ',
  'Æ¬' => 'Æ­',
  'Æ®' => 'Êˆ',
  'Æ¯' => 'Æ°',
  'Æ±' => 'ÊŠ',
  'Æ²' => 'Ê‹',
  'Æ³' => 'Æ´',
  'Æµ' => 'Æ¶',
  'Æ·' => 'Ê’',
  'Æ¸' => 'Æ¹',
  'Æ¼' => 'Æ½',
  'Ç„' => 'Ç†',
  'Ç…' => 'Ç†',
  'Ç‡' => 'Ç‰',
  'Çˆ' => 'Ç‰',
  'ÇŠ' => 'ÇŒ',
  'Ç‹' => 'ÇŒ',
  'Ç�' => 'ÇŽ',
  'Ç�' => 'Ç�',
  'Ç‘' => 'Ç’',
  'Ç“' => 'Ç”',
  'Ç•' => 'Ç–',
  'Ç—' => 'Ç˜',
  'Ç™' => 'Çš',
  'Ç›' => 'Çœ',
  'Çž' => 'ÇŸ',
  'Ç ' => 'Ç¡',
  'Ç¢' => 'Ç£',
  'Ç¤' => 'Ç¥',
  'Ç¦' => 'Ç§',
  'Ç¨' => 'Ç©',
  'Çª' => 'Ç«',
  'Ç¬' => 'Ç­',
  'Ç®' => 'Ç¯',
  'Ç±' => 'Ç³',
  'Ç²' => 'Ç³',
  'Ç´' => 'Çµ',
  'Ç¶' => 'Æ•',
  'Ç·' => 'Æ¿',
  'Ç¸' => 'Ç¹',
  'Çº' => 'Ç»',
  'Ç¼' => 'Ç½',
  'Ç¾' => 'Ç¿',
  'È€' => 'È�',
  'È‚' => 'Èƒ',
  'È„' => 'È…',
  'È†' => 'È‡',
  'Èˆ' => 'È‰',
  'ÈŠ' => 'È‹',
  'ÈŒ' => 'È�',
  'ÈŽ' => 'È�',
  'È�' => 'È‘',
  'È’' => 'È“',
  'È”' => 'È•',
  'È–' => 'È—',
  'È˜' => 'È™',
  'Èš' => 'È›',
  'Èœ' => 'È�',
  'Èž' => 'ÈŸ',
  'È ' => 'Æž',
  'È¢' => 'È£',
  'È¤' => 'È¥',
  'È¦' => 'È§',
  'È¨' => 'È©',
  'Èª' => 'È«',
  'È¬' => 'È­',
  'È®' => 'È¯',
  'È°' => 'È±',
  'È²' => 'È³',
  'Èº' => 'â±¥',
  'È»' => 'È¼',
  'È½' => 'Æš',
  'È¾' => 'â±¦',
  'É�' => 'É‚',
  'Éƒ' => 'Æ€',
  'É„' => 'Ê‰',
  'É…' => 'ÊŒ',
  'É†' => 'É‡',
  'Éˆ' => 'É‰',
  'ÉŠ' => 'É‹',
  'ÉŒ' => 'É�',
  'ÉŽ' => 'É�',
  'Í°' => 'Í±',
  'Í²' => 'Í³',
  'Í¶' => 'Í·',
  'Í¿' => 'Ï³',
  'Î†' => 'Î¬',
  'Îˆ' => 'Î­',
  'Î‰' => 'Î®',
  'ÎŠ' => 'Î¯',
  'ÎŒ' => 'ÏŒ',
  'ÎŽ' => 'Ï�',
  'Î�' => 'ÏŽ',
  'Î‘' => 'Î±',
  'Î’' => 'Î²',
  'Î“' => 'Î³',
  'Î”' => 'Î´',
  'Î•' => 'Îµ',
  'Î–' => 'Î¶',
  'Î—' => 'Î·',
  'Î˜' => 'Î¸',
  'Î™' => 'Î¹',
  'Îš' => 'Îº',
  'Î›' => 'Î»',
  'Îœ' => 'Î¼',
  'Î�' => 'Î½',
  'Îž' => 'Î¾',
  'ÎŸ' => 'Î¿',
  'Î ' => 'Ï€',
  'Î¡' => 'Ï�',
  'Î£' => 'Ïƒ',
  'Î¤' => 'Ï„',
  'Î¥' => 'Ï…',
  'Î¦' => 'Ï†',
  'Î§' => 'Ï‡',
  'Î¨' => 'Ïˆ',
  'Î©' => 'Ï‰',
  'Îª' => 'ÏŠ',
  'Î«' => 'Ï‹',
  'Ï�' => 'Ï—',
  'Ï˜' => 'Ï™',
  'Ïš' => 'Ï›',
  'Ïœ' => 'Ï�',
  'Ïž' => 'ÏŸ',
  'Ï ' => 'Ï¡',
  'Ï¢' => 'Ï£',
  'Ï¤' => 'Ï¥',
  'Ï¦' => 'Ï§',
  'Ï¨' => 'Ï©',
  'Ïª' => 'Ï«',
  'Ï¬' => 'Ï­',
  'Ï®' => 'Ï¯',
  'Ï´' => 'Î¸',
  'Ï·' => 'Ï¸',
  'Ï¹' => 'Ï²',
  'Ïº' => 'Ï»',
  'Ï½' => 'Í»',
  'Ï¾' => 'Í¼',
  'Ï¿' => 'Í½',
  'Ð€' => 'Ñ�',
  'Ð�' => 'Ñ‘',
  'Ð‚' => 'Ñ’',
  'Ðƒ' => 'Ñ“',
  'Ð„' => 'Ñ”',
  'Ð…' => 'Ñ•',
  'Ð†' => 'Ñ–',
  'Ð‡' => 'Ñ—',
  'Ðˆ' => 'Ñ˜',
  'Ð‰' => 'Ñ™',
  'ÐŠ' => 'Ñš',
  'Ð‹' => 'Ñ›',
  'ÐŒ' => 'Ñœ',
  'Ð�' => 'Ñ�',
  'ÐŽ' => 'Ñž',
  'Ð�' => 'ÑŸ',
  'Ð�' => 'Ð°',
  'Ð‘' => 'Ð±',
  'Ð’' => 'Ð²',
  'Ð“' => 'Ð³',
  'Ð”' => 'Ð´',
  'Ð•' => 'Ðµ',
  'Ð–' => 'Ð¶',
  'Ð—' => 'Ð·',
  'Ð˜' => 'Ð¸',
  'Ð™' => 'Ð¹',
  'Ðš' => 'Ðº',
  'Ð›' => 'Ð»',
  'Ðœ' => 'Ð¼',
  'Ð�' => 'Ð½',
  'Ðž' => 'Ð¾',
  'ÐŸ' => 'Ð¿',
  'Ð ' => 'Ñ€',
  'Ð¡' => 'Ñ�',
  'Ð¢' => 'Ñ‚',
  'Ð£' => 'Ñƒ',
  'Ð¤' => 'Ñ„',
  'Ð¥' => 'Ñ…',
  'Ð¦' => 'Ñ†',
  'Ð§' => 'Ñ‡',
  'Ð¨' => 'Ñˆ',
  'Ð©' => 'Ñ‰',
  'Ðª' => 'ÑŠ',
  'Ð«' => 'Ñ‹',
  'Ð¬' => 'ÑŒ',
  'Ð­' => 'Ñ�',
  'Ð®' => 'ÑŽ',
  'Ð¯' => 'Ñ�',
  'Ñ ' => 'Ñ¡',
  'Ñ¢' => 'Ñ£',
  'Ñ¤' => 'Ñ¥',
  'Ñ¦' => 'Ñ§',
  'Ñ¨' => 'Ñ©',
  'Ñª' => 'Ñ«',
  'Ñ¬' => 'Ñ­',
  'Ñ®' => 'Ñ¯',
  'Ñ°' => 'Ñ±',
  'Ñ²' => 'Ñ³',
  'Ñ´' => 'Ñµ',
  'Ñ¶' => 'Ñ·',
  'Ñ¸' => 'Ñ¹',
  'Ñº' => 'Ñ»',
  'Ñ¼' => 'Ñ½',
  'Ñ¾' => 'Ñ¿',
  'Ò€' => 'Ò�',
  'ÒŠ' => 'Ò‹',
  'ÒŒ' => 'Ò�',
  'ÒŽ' => 'Ò�',
  'Ò�' => 'Ò‘',
  'Ò’' => 'Ò“',
  'Ò”' => 'Ò•',
  'Ò–' => 'Ò—',
  'Ò˜' => 'Ò™',
  'Òš' => 'Ò›',
  'Òœ' => 'Ò�',
  'Òž' => 'ÒŸ',
  'Ò ' => 'Ò¡',
  'Ò¢' => 'Ò£',
  'Ò¤' => 'Ò¥',
  'Ò¦' => 'Ò§',
  'Ò¨' => 'Ò©',
  'Òª' => 'Ò«',
  'Ò¬' => 'Ò­',
  'Ò®' => 'Ò¯',
  'Ò°' => 'Ò±',
  'Ò²' => 'Ò³',
  'Ò´' => 'Òµ',
  'Ò¶' => 'Ò·',
  'Ò¸' => 'Ò¹',
  'Òº' => 'Ò»',
  'Ò¼' => 'Ò½',
  'Ò¾' => 'Ò¿',
  'Ó€' => 'Ó�',
  'Ó�' => 'Ó‚',
  'Óƒ' => 'Ó„',
  'Ó…' => 'Ó†',
  'Ó‡' => 'Óˆ',
  'Ó‰' => 'ÓŠ',
  'Ó‹' => 'ÓŒ',
  'Ó�' => 'ÓŽ',
  'Ó�' => 'Ó‘',
  'Ó’' => 'Ó“',
  'Ó”' => 'Ó•',
  'Ó–' => 'Ó—',
  'Ó˜' => 'Ó™',
  'Óš' => 'Ó›',
  'Óœ' => 'Ó�',
  'Óž' => 'ÓŸ',
  'Ó ' => 'Ó¡',
  'Ó¢' => 'Ó£',
  'Ó¤' => 'Ó¥',
  'Ó¦' => 'Ó§',
  'Ó¨' => 'Ó©',
  'Óª' => 'Ó«',
  'Ó¬' => 'Ó­',
  'Ó®' => 'Ó¯',
  'Ó°' => 'Ó±',
  'Ó²' => 'Ó³',
  'Ó´' => 'Óµ',
  'Ó¶' => 'Ó·',
  'Ó¸' => 'Ó¹',
  'Óº' => 'Ó»',
  'Ó¼' => 'Ó½',
  'Ó¾' => 'Ó¿',
  'Ô€' => 'Ô�',
  'Ô‚' => 'Ôƒ',
  'Ô„' => 'Ô…',
  'Ô†' => 'Ô‡',
  'Ôˆ' => 'Ô‰',
  'ÔŠ' => 'Ô‹',
  'ÔŒ' => 'Ô�',
  'ÔŽ' => 'Ô�',
  'Ô�' => 'Ô‘',
  'Ô’' => 'Ô“',
  'Ô”' => 'Ô•',
  'Ô–' => 'Ô—',
  'Ô˜' => 'Ô™',
  'Ôš' => 'Ô›',
  'Ôœ' => 'Ô�',
  'Ôž' => 'ÔŸ',
  'Ô ' => 'Ô¡',
  'Ô¢' => 'Ô£',
  'Ô¤' => 'Ô¥',
  'Ô¦' => 'Ô§',
  'Ô¨' => 'Ô©',
  'Ôª' => 'Ô«',
  'Ô¬' => 'Ô­',
  'Ô®' => 'Ô¯',
  'Ô±' => 'Õ¡',
  'Ô²' => 'Õ¢',
  'Ô³' => 'Õ£',
  'Ô´' => 'Õ¤',
  'Ôµ' => 'Õ¥',
  'Ô¶' => 'Õ¦',
  'Ô·' => 'Õ§',
  'Ô¸' => 'Õ¨',
  'Ô¹' => 'Õ©',
  'Ôº' => 'Õª',
  'Ô»' => 'Õ«',
  'Ô¼' => 'Õ¬',
  'Ô½' => 'Õ­',
  'Ô¾' => 'Õ®',
  'Ô¿' => 'Õ¯',
  'Õ€' => 'Õ°',
  'Õ�' => 'Õ±',
  'Õ‚' => 'Õ²',
  'Õƒ' => 'Õ³',
  'Õ„' => 'Õ´',
  'Õ…' => 'Õµ',
  'Õ†' => 'Õ¶',
  'Õ‡' => 'Õ·',
  'Õˆ' => 'Õ¸',
  'Õ‰' => 'Õ¹',
  'ÕŠ' => 'Õº',
  'Õ‹' => 'Õ»',
  'ÕŒ' => 'Õ¼',
  'Õ�' => 'Õ½',
  'ÕŽ' => 'Õ¾',
  'Õ�' => 'Õ¿',
  'Õ�' => 'Ö€',
  'Õ‘' => 'Ö�',
  'Õ’' => 'Ö‚',
  'Õ“' => 'Öƒ',
  'Õ”' => 'Ö„',
  'Õ•' => 'Ö…',
  'Õ–' => 'Ö†',
  'á‚ ' => 'â´€',
  'á‚¡' => 'â´�',
  'á‚¢' => 'â´‚',
  'á‚£' => 'â´ƒ',
  'á‚¤' => 'â´„',
  'á‚¥' => 'â´…',
  'á‚¦' => 'â´†',
  'á‚§' => 'â´‡',
  'á‚¨' => 'â´ˆ',
  'á‚©' => 'â´‰',
  'á‚ª' => 'â´Š',
  'á‚«' => 'â´‹',
  'á‚¬' => 'â´Œ',
  'á‚­' => 'â´�',
  'á‚®' => 'â´Ž',
  'á‚¯' => 'â´�',
  'á‚°' => 'â´�',
  'á‚±' => 'â´‘',
  'á‚²' => 'â´’',
  'á‚³' => 'â´“',
  'á‚´' => 'â´”',
  'á‚µ' => 'â´•',
  'á‚¶' => 'â´–',
  'á‚·' => 'â´—',
  'á‚¸' => 'â´˜',
  'á‚¹' => 'â´™',
  'á‚º' => 'â´š',
  'á‚»' => 'â´›',
  'á‚¼' => 'â´œ',
  'á‚½' => 'â´�',
  'á‚¾' => 'â´ž',
  'á‚¿' => 'â´Ÿ',
  'áƒ€' => 'â´ ',
  'áƒ�' => 'â´¡',
  'áƒ‚' => 'â´¢',
  'áƒƒ' => 'â´£',
  'áƒ„' => 'â´¤',
  'áƒ…' => 'â´¥',
  'áƒ‡' => 'â´§',
  'áƒ�' => 'â´­',
  'áŽ ' => 'ê­°',
  'áŽ¡' => 'ê­±',
  'áŽ¢' => 'ê­²',
  'áŽ£' => 'ê­³',
  'áŽ¤' => 'ê­´',
  'áŽ¥' => 'ê­µ',
  'áŽ¦' => 'ê­¶',
  'áŽ§' => 'ê­·',
  'áŽ¨' => 'ê­¸',
  'áŽ©' => 'ê­¹',
  'áŽª' => 'ê­º',
  'áŽ«' => 'ê­»',
  'áŽ¬' => 'ê­¼',
  'áŽ­' => 'ê­½',
  'áŽ®' => 'ê­¾',
  'áŽ¯' => 'ê­¿',
  'áŽ°' => 'ê®€',
  'áŽ±' => 'ê®�',
  'áŽ²' => 'ê®‚',
  'áŽ³' => 'ê®ƒ',
  'áŽ´' => 'ê®„',
  'áŽµ' => 'ê®…',
  'áŽ¶' => 'ê®†',
  'áŽ·' => 'ê®‡',
  'áŽ¸' => 'ê®ˆ',
  'áŽ¹' => 'ê®‰',
  'áŽº' => 'ê®Š',
  'áŽ»' => 'ê®‹',
  'áŽ¼' => 'ê®Œ',
  'áŽ½' => 'ê®�',
  'áŽ¾' => 'ê®Ž',
  'áŽ¿' => 'ê®�',
  'á�€' => 'ê®�',
  'á��' => 'ê®‘',
  'á�‚' => 'ê®’',
  'á�ƒ' => 'ê®“',
  'á�„' => 'ê®”',
  'á�…' => 'ê®•',
  'á�†' => 'ê®–',
  'á�‡' => 'ê®—',
  'á�ˆ' => 'ê®˜',
  'á�‰' => 'ê®™',
  'á�Š' => 'ê®š',
  'á�‹' => 'ê®›',
  'á�Œ' => 'ê®œ',
  'á��' => 'ê®�',
  'á�Ž' => 'ê®ž',
  'á��' => 'ê®Ÿ',
  'á��' => 'ê® ',
  'á�‘' => 'ê®¡',
  'á�’' => 'ê®¢',
  'á�“' => 'ê®£',
  'á�”' => 'ê®¤',
  'á�•' => 'ê®¥',
  'á�–' => 'ê®¦',
  'á�—' => 'ê®§',
  'á�˜' => 'ê®¨',
  'á�™' => 'ê®©',
  'á�š' => 'ê®ª',
  'á�›' => 'ê®«',
  'á�œ' => 'ê®¬',
  'á��' => 'ê®­',
  'á�ž' => 'ê®®',
  'á�Ÿ' => 'ê®¯',
  'á� ' => 'ê®°',
  'á�¡' => 'ê®±',
  'á�¢' => 'ê®²',
  'á�£' => 'ê®³',
  'á�¤' => 'ê®´',
  'á�¥' => 'ê®µ',
  'á�¦' => 'ê®¶',
  'á�§' => 'ê®·',
  'á�¨' => 'ê®¸',
  'á�©' => 'ê®¹',
  'á�ª' => 'ê®º',
  'á�«' => 'ê®»',
  'á�¬' => 'ê®¼',
  'á�­' => 'ê®½',
  'á�®' => 'ê®¾',
  'á�¯' => 'ê®¿',
  'á�°' => 'á�¸',
  'á�±' => 'á�¹',
  'á�²' => 'á�º',
  'á�³' => 'á�»',
  'á�´' => 'á�¼',
  'á�µ' => 'á�½',
  'á²�' => 'áƒ�',
  'á²‘' => 'áƒ‘',
  'á²’' => 'áƒ’',
  'á²“' => 'áƒ“',
  'á²”' => 'áƒ”',
  'á²•' => 'áƒ•',
  'á²–' => 'áƒ–',
  'á²—' => 'áƒ—',
  'á²˜' => 'áƒ˜',
  'á²™' => 'áƒ™',
  'á²š' => 'áƒš',
  'á²›' => 'áƒ›',
  'á²œ' => 'áƒœ',
  'á²�' => 'áƒ�',
  'á²ž' => 'áƒž',
  'á²Ÿ' => 'áƒŸ',
  'á² ' => 'áƒ ',
  'á²¡' => 'áƒ¡',
  'á²¢' => 'áƒ¢',
  'á²£' => 'áƒ£',
  'á²¤' => 'áƒ¤',
  'á²¥' => 'áƒ¥',
  'á²¦' => 'áƒ¦',
  'á²§' => 'áƒ§',
  'á²¨' => 'áƒ¨',
  'á²©' => 'áƒ©',
  'á²ª' => 'áƒª',
  'á²«' => 'áƒ«',
  'á²¬' => 'áƒ¬',
  'á²­' => 'áƒ­',
  'á²®' => 'áƒ®',
  'á²¯' => 'áƒ¯',
  'á²°' => 'áƒ°',
  'á²±' => 'áƒ±',
  'á²²' => 'áƒ²',
  'á²³' => 'áƒ³',
  'á²´' => 'áƒ´',
  'á²µ' => 'áƒµ',
  'á²¶' => 'áƒ¶',
  'á²·' => 'áƒ·',
  'á²¸' => 'áƒ¸',
  'á²¹' => 'áƒ¹',
  'á²º' => 'áƒº',
  'á²½' => 'áƒ½',
  'á²¾' => 'áƒ¾',
  'á²¿' => 'áƒ¿',
  'á¸€' => 'á¸�',
  'á¸‚' => 'á¸ƒ',
  'á¸„' => 'á¸…',
  'á¸†' => 'á¸‡',
  'á¸ˆ' => 'á¸‰',
  'á¸Š' => 'á¸‹',
  'á¸Œ' => 'á¸�',
  'á¸Ž' => 'á¸�',
  'á¸�' => 'á¸‘',
  'á¸’' => 'á¸“',
  'á¸”' => 'á¸•',
  'á¸–' => 'á¸—',
  'á¸˜' => 'á¸™',
  'á¸š' => 'á¸›',
  'á¸œ' => 'á¸�',
  'á¸ž' => 'á¸Ÿ',
  'á¸ ' => 'á¸¡',
  'á¸¢' => 'á¸£',
  'á¸¤' => 'á¸¥',
  'á¸¦' => 'á¸§',
  'á¸¨' => 'á¸©',
  'á¸ª' => 'á¸«',
  'á¸¬' => 'á¸­',
  'á¸®' => 'á¸¯',
  'á¸°' => 'á¸±',
  'á¸²' => 'á¸³',
  'á¸´' => 'á¸µ',
  'á¸¶' => 'á¸·',
  'á¸¸' => 'á¸¹',
  'á¸º' => 'á¸»',
  'á¸¼' => 'á¸½',
  'á¸¾' => 'á¸¿',
  'á¹€' => 'á¹�',
  'á¹‚' => 'á¹ƒ',
  'á¹„' => 'á¹…',
  'á¹†' => 'á¹‡',
  'á¹ˆ' => 'á¹‰',
  'á¹Š' => 'á¹‹',
  'á¹Œ' => 'á¹�',
  'á¹Ž' => 'á¹�',
  'á¹�' => 'á¹‘',
  'á¹’' => 'á¹“',
  'á¹”' => 'á¹•',
  'á¹–' => 'á¹—',
  'á¹˜' => 'á¹™',
  'á¹š' => 'á¹›',
  'á¹œ' => 'á¹�',
  'á¹ž' => 'á¹Ÿ',
  'á¹ ' => 'á¹¡',
  'á¹¢' => 'á¹£',
  'á¹¤' => 'á¹¥',
  'á¹¦' => 'á¹§',
  'á¹¨' => 'á¹©',
  'á¹ª' => 'á¹«',
  'á¹¬' => 'á¹­',
  'á¹®' => 'á¹¯',
  'á¹°' => 'á¹±',
  'á¹²' => 'á¹³',
  'á¹´' => 'á¹µ',
  'á¹¶' => 'á¹·',
  'á¹¸' => 'á¹¹',
  'á¹º' => 'á¹»',
  'á¹¼' => 'á¹½',
  'á¹¾' => 'á¹¿',
  'áº€' => 'áº�',
  'áº‚' => 'áºƒ',
  'áº„' => 'áº…',
  'áº†' => 'áº‡',
  'áºˆ' => 'áº‰',
  'áºŠ' => 'áº‹',
  'áºŒ' => 'áº�',
  'áºŽ' => 'áº�',
  'áº�' => 'áº‘',
  'áº’' => 'áº“',
  'áº”' => 'áº•',
  'áºž' => 'ÃŸ',
  'áº ' => 'áº¡',
  'áº¢' => 'áº£',
  'áº¤' => 'áº¥',
  'áº¦' => 'áº§',
  'áº¨' => 'áº©',
  'áºª' => 'áº«',
  'áº¬' => 'áº­',
  'áº®' => 'áº¯',
  'áº°' => 'áº±',
  'áº²' => 'áº³',
  'áº´' => 'áºµ',
  'áº¶' => 'áº·',
  'áº¸' => 'áº¹',
  'áºº' => 'áº»',
  'áº¼' => 'áº½',
  'áº¾' => 'áº¿',
  'á»€' => 'á»�',
  'á»‚' => 'á»ƒ',
  'á»„' => 'á»…',
  'á»†' => 'á»‡',
  'á»ˆ' => 'á»‰',
  'á»Š' => 'á»‹',
  'á»Œ' => 'á»�',
  'á»Ž' => 'á»�',
  'á»�' => 'á»‘',
  'á»’' => 'á»“',
  'á»”' => 'á»•',
  'á»–' => 'á»—',
  'á»˜' => 'á»™',
  'á»š' => 'á»›',
  'á»œ' => 'á»�',
  'á»ž' => 'á»Ÿ',
  'á» ' => 'á»¡',
  'á»¢' => 'á»£',
  'á»¤' => 'á»¥',
  'á»¦' => 'á»§',
  'á»¨' => 'á»©',
  'á»ª' => 'á»«',
  'á»¬' => 'á»­',
  'á»®' => 'á»¯',
  'á»°' => 'á»±',
  'á»²' => 'á»³',
  'á»´' => 'á»µ',
  'á»¶' => 'á»·',
  'á»¸' => 'á»¹',
  'á»º' => 'á»»',
  'á»¼' => 'á»½',
  'á»¾' => 'á»¿',
  'á¼ˆ' => 'á¼€',
  'á¼‰' => 'á¼�',
  'á¼Š' => 'á¼‚',
  'á¼‹' => 'á¼ƒ',
  'á¼Œ' => 'á¼„',
  'á¼�' => 'á¼…',
  'á¼Ž' => 'á¼†',
  'á¼�' => 'á¼‡',
  'á¼˜' => 'á¼�',
  'á¼™' => 'á¼‘',
  'á¼š' => 'á¼’',
  'á¼›' => 'á¼“',
  'á¼œ' => 'á¼”',
  'á¼�' => 'á¼•',
  'á¼¨' => 'á¼ ',
  'á¼©' => 'á¼¡',
  'á¼ª' => 'á¼¢',
  'á¼«' => 'á¼£',
  'á¼¬' => 'á¼¤',
  'á¼­' => 'á¼¥',
  'á¼®' => 'á¼¦',
  'á¼¯' => 'á¼§',
  'á¼¸' => 'á¼°',
  'á¼¹' => 'á¼±',
  'á¼º' => 'á¼²',
  'á¼»' => 'á¼³',
  'á¼¼' => 'á¼´',
  'á¼½' => 'á¼µ',
  'á¼¾' => 'á¼¶',
  'á¼¿' => 'á¼·',
  'á½ˆ' => 'á½€',
  'á½‰' => 'á½�',
  'á½Š' => 'á½‚',
  'á½‹' => 'á½ƒ',
  'á½Œ' => 'á½„',
  'á½�' => 'á½…',
  'á½™' => 'á½‘',
  'á½›' => 'á½“',
  'á½�' => 'á½•',
  'á½Ÿ' => 'á½—',
  'á½¨' => 'á½ ',
  'á½©' => 'á½¡',
  'á½ª' => 'á½¢',
  'á½«' => 'á½£',
  'á½¬' => 'á½¤',
  'á½­' => 'á½¥',
  'á½®' => 'á½¦',
  'á½¯' => 'á½§',
  'á¾ˆ' => 'á¾€',
  'á¾‰' => 'á¾�',
  'á¾Š' => 'á¾‚',
  'á¾‹' => 'á¾ƒ',
  'á¾Œ' => 'á¾„',
  'á¾�' => 'á¾…',
  'á¾Ž' => 'á¾†',
  'á¾�' => 'á¾‡',
  'á¾˜' => 'á¾�',
  'á¾™' => 'á¾‘',
  'á¾š' => 'á¾’',
  'á¾›' => 'á¾“',
  'á¾œ' => 'á¾”',
  'á¾�' => 'á¾•',
  'á¾ž' => 'á¾–',
  'á¾Ÿ' => 'á¾—',
  'á¾¨' => 'á¾ ',
  'á¾©' => 'á¾¡',
  'á¾ª' => 'á¾¢',
  'á¾«' => 'á¾£',
  'á¾¬' => 'á¾¤',
  'á¾­' => 'á¾¥',
  'á¾®' => 'á¾¦',
  'á¾¯' => 'á¾§',
  'á¾¸' => 'á¾°',
  'á¾¹' => 'á¾±',
  'á¾º' => 'á½°',
  'á¾»' => 'á½±',
  'á¾¼' => 'á¾³',
  'á¿ˆ' => 'á½²',
  'á¿‰' => 'á½³',
  'á¿Š' => 'á½´',
  'á¿‹' => 'á½µ',
  'á¿Œ' => 'á¿ƒ',
  'á¿˜' => 'á¿�',
  'á¿™' => 'á¿‘',
  'á¿š' => 'á½¶',
  'á¿›' => 'á½·',
  'á¿¨' => 'á¿ ',
  'á¿©' => 'á¿¡',
  'á¿ª' => 'á½º',
  'á¿«' => 'á½»',
  'á¿¬' => 'á¿¥',
  'á¿¸' => 'á½¸',
  'á¿¹' => 'á½¹',
  'á¿º' => 'á½¼',
  'á¿»' => 'á½½',
  'á¿¼' => 'á¿³',
  'â„¦' => 'Ï‰',
  'â„ª' => 'k',
  'â„«' => 'Ã¥',
  'â„²' => 'â…Ž',
  'â… ' => 'â…°',
  'â…¡' => 'â…±',
  'â…¢' => 'â…²',
  'â…£' => 'â…³',
  'â…¤' => 'â…´',
  'â…¥' => 'â…µ',
  'â…¦' => 'â…¶',
  'â…§' => 'â…·',
  'â…¨' => 'â…¸',
  'â…©' => 'â…¹',
  'â…ª' => 'â…º',
  'â…«' => 'â…»',
  'â…¬' => 'â…¼',
  'â…­' => 'â…½',
  'â…®' => 'â…¾',
  'â…¯' => 'â…¿',
  'â†ƒ' => 'â†„',
  'â’¶' => 'â“�',
  'â’·' => 'â“‘',
  'â’¸' => 'â“’',
  'â’¹' => 'â““',
  'â’º' => 'â“”',
  'â’»' => 'â“•',
  'â’¼' => 'â“–',
  'â’½' => 'â“—',
  'â’¾' => 'â“˜',
  'â’¿' => 'â“™',
  'â“€' => 'â“š',
  'â“�' => 'â“›',
  'â“‚' => 'â“œ',
  'â“ƒ' => 'â“�',
  'â“„' => 'â“ž',
  'â“…' => 'â“Ÿ',
  'â“†' => 'â“ ',
  'â“‡' => 'â“¡',
  'â“ˆ' => 'â“¢',
  'â“‰' => 'â“£',
  'â“Š' => 'â“¤',
  'â“‹' => 'â“¥',
  'â“Œ' => 'â“¦',
  'â“�' => 'â“§',
  'â“Ž' => 'â“¨',
  'â“�' => 'â“©',
  'â°€' => 'â°°',
  'â°�' => 'â°±',
  'â°‚' => 'â°²',
  'â°ƒ' => 'â°³',
  'â°„' => 'â°´',
  'â°…' => 'â°µ',
  'â°†' => 'â°¶',
  'â°‡' => 'â°·',
  'â°ˆ' => 'â°¸',
  'â°‰' => 'â°¹',
  'â°Š' => 'â°º',
  'â°‹' => 'â°»',
  'â°Œ' => 'â°¼',
  'â°�' => 'â°½',
  'â°Ž' => 'â°¾',
  'â°�' => 'â°¿',
  'â°�' => 'â±€',
  'â°‘' => 'â±�',
  'â°’' => 'â±‚',
  'â°“' => 'â±ƒ',
  'â°”' => 'â±„',
  'â°•' => 'â±…',
  'â°–' => 'â±†',
  'â°—' => 'â±‡',
  'â°˜' => 'â±ˆ',
  'â°™' => 'â±‰',
  'â°š' => 'â±Š',
  'â°›' => 'â±‹',
  'â°œ' => 'â±Œ',
  'â°�' => 'â±�',
  'â°ž' => 'â±Ž',
  'â°Ÿ' => 'â±�',
  'â° ' => 'â±�',
  'â°¡' => 'â±‘',
  'â°¢' => 'â±’',
  'â°£' => 'â±“',
  'â°¤' => 'â±”',
  'â°¥' => 'â±•',
  'â°¦' => 'â±–',
  'â°§' => 'â±—',
  'â°¨' => 'â±˜',
  'â°©' => 'â±™',
  'â°ª' => 'â±š',
  'â°«' => 'â±›',
  'â°¬' => 'â±œ',
  'â°­' => 'â±�',
  'â°®' => 'â±ž',
  'â± ' => 'â±¡',
  'â±¢' => 'É«',
  'â±£' => 'áµ½',
  'â±¤' => 'É½',
  'â±§' => 'â±¨',
  'â±©' => 'â±ª',
  'â±«' => 'â±¬',
  'â±­' => 'É‘',
  'â±®' => 'É±',
  'â±¯' => 'É�',
  'â±°' => 'É’',
  'â±²' => 'â±³',
  'â±µ' => 'â±¶',
  'â±¾' => 'È¿',
  'â±¿' => 'É€',
  'â²€' => 'â²�',
  'â²‚' => 'â²ƒ',
  'â²„' => 'â²…',
  'â²†' => 'â²‡',
  'â²ˆ' => 'â²‰',
  'â²Š' => 'â²‹',
  'â²Œ' => 'â²�',
  'â²Ž' => 'â²�',
  'â²�' => 'â²‘',
  'â²’' => 'â²“',
  'â²”' => 'â²•',
  'â²–' => 'â²—',
  'â²˜' => 'â²™',
  'â²š' => 'â²›',
  'â²œ' => 'â²�',
  'â²ž' => 'â²Ÿ',
  'â² ' => 'â²¡',
  'â²¢' => 'â²£',
  'â²¤' => 'â²¥',
  'â²¦' => 'â²§',
  'â²¨' => 'â²©',
  'â²ª' => 'â²«',
  'â²¬' => 'â²­',
  'â²®' => 'â²¯',
  'â²°' => 'â²±',
  'â²²' => 'â²³',
  'â²´' => 'â²µ',
  'â²¶' => 'â²·',
  'â²¸' => 'â²¹',
  'â²º' => 'â²»',
  'â²¼' => 'â²½',
  'â²¾' => 'â²¿',
  'â³€' => 'â³�',
  'â³‚' => 'â³ƒ',
  'â³„' => 'â³…',
  'â³†' => 'â³‡',
  'â³ˆ' => 'â³‰',
  'â³Š' => 'â³‹',
  'â³Œ' => 'â³�',
  'â³Ž' => 'â³�',
  'â³�' => 'â³‘',
  'â³’' => 'â³“',
  'â³”' => 'â³•',
  'â³–' => 'â³—',
  'â³˜' => 'â³™',
  'â³š' => 'â³›',
  'â³œ' => 'â³�',
  'â³ž' => 'â³Ÿ',
  'â³ ' => 'â³¡',
  'â³¢' => 'â³£',
  'â³«' => 'â³¬',
  'â³­' => 'â³®',
  'â³²' => 'â³³',
  'ê™€' => 'ê™�',
  'ê™‚' => 'ê™ƒ',
  'ê™„' => 'ê™…',
  'ê™†' => 'ê™‡',
  'ê™ˆ' => 'ê™‰',
  'ê™Š' => 'ê™‹',
  'ê™Œ' => 'ê™�',
  'ê™Ž' => 'ê™�',
  'ê™�' => 'ê™‘',
  'ê™’' => 'ê™“',
  'ê™”' => 'ê™•',
  'ê™–' => 'ê™—',
  'ê™˜' => 'ê™™',
  'ê™š' => 'ê™›',
  'ê™œ' => 'ê™�',
  'ê™ž' => 'ê™Ÿ',
  'ê™ ' => 'ê™¡',
  'ê™¢' => 'ê™£',
  'ê™¤' => 'ê™¥',
  'ê™¦' => 'ê™§',
  'ê™¨' => 'ê™©',
  'ê™ª' => 'ê™«',
  'ê™¬' => 'ê™­',
  'êš€' => 'êš�',
  'êš‚' => 'êšƒ',
  'êš„' => 'êš…',
  'êš†' => 'êš‡',
  'êšˆ' => 'êš‰',
  'êšŠ' => 'êš‹',
  'êšŒ' => 'êš�',
  'êšŽ' => 'êš�',
  'êš�' => 'êš‘',
  'êš’' => 'êš“',
  'êš”' => 'êš•',
  'êš–' => 'êš—',
  'êš˜' => 'êš™',
  'êšš' => 'êš›',
  'êœ¢' => 'êœ£',
  'êœ¤' => 'êœ¥',
  'êœ¦' => 'êœ§',
  'êœ¨' => 'êœ©',
  'êœª' => 'êœ«',
  'êœ¬' => 'êœ­',
  'êœ®' => 'êœ¯',
  'êœ²' => 'êœ³',
  'êœ´' => 'êœµ',
  'êœ¶' => 'êœ·',
  'êœ¸' => 'êœ¹',
  'êœº' => 'êœ»',
  'êœ¼' => 'êœ½',
  'êœ¾' => 'êœ¿',
  'ê�€' => 'ê��',
  'ê�‚' => 'ê�ƒ',
  'ê�„' => 'ê�…',
  'ê�†' => 'ê�‡',
  'ê�ˆ' => 'ê�‰',
  'ê�Š' => 'ê�‹',
  'ê�Œ' => 'ê��',
  'ê�Ž' => 'ê��',
  'ê��' => 'ê�‘',
  'ê�’' => 'ê�“',
  'ê�”' => 'ê�•',
  'ê�–' => 'ê�—',
  'ê�˜' => 'ê�™',
  'ê�š' => 'ê�›',
  'ê�œ' => 'ê��',
  'ê�ž' => 'ê�Ÿ',
  'ê� ' => 'ê�¡',
  'ê�¢' => 'ê�£',
  'ê�¤' => 'ê�¥',
  'ê�¦' => 'ê�§',
  'ê�¨' => 'ê�©',
  'ê�ª' => 'ê�«',
  'ê�¬' => 'ê�­',
  'ê�®' => 'ê�¯',
  'ê�¹' => 'ê�º',
  'ê�»' => 'ê�¼',
  'ê�½' => 'áµ¹',
  'ê�¾' => 'ê�¿',
  'êž€' => 'êž�',
  'êž‚' => 'êžƒ',
  'êž„' => 'êž…',
  'êž†' => 'êž‡',
  'êž‹' => 'êžŒ',
  'êž�' => 'É¥',
  'êž�' => 'êž‘',
  'êž’' => 'êž“',
  'êž–' => 'êž—',
  'êž˜' => 'êž™',
  'êžš' => 'êž›',
  'êžœ' => 'êž�',
  'êžž' => 'êžŸ',
  'êž ' => 'êž¡',
  'êž¢' => 'êž£',
  'êž¤' => 'êž¥',
  'êž¦' => 'êž§',
  'êž¨' => 'êž©',
  'êžª' => 'É¦',
  'êž«' => 'Éœ',
  'êž¬' => 'É¡',
  'êž­' => 'É¬',
  'êž®' => 'Éª',
  'êž°' => 'Êž',
  'êž±' => 'Ê‡',
  'êž²' => 'Ê�',
  'êž³' => 'ê­“',
  'êž´' => 'êžµ',
  'êž¶' => 'êž·',
  'êž¸' => 'êž¹',
  'êžº' => 'êž»',
  'êž¼' => 'êž½',
  'êž¾' => 'êž¿',
  'êŸ‚' => 'êŸƒ',
  'êŸ„' => 'êž”',
  'êŸ…' => 'Ê‚',
  'êŸ†' => 'á¶Ž',
  'êŸ‡' => 'êŸˆ',
  'êŸ‰' => 'êŸŠ',
  'êŸµ' => 'êŸ¶',
  'ï¼¡' => 'ï½�',
  'ï¼¢' => 'ï½‚',
  'ï¼£' => 'ï½ƒ',
  'ï¼¤' => 'ï½„',
  'ï¼¥' => 'ï½…',
  'ï¼¦' => 'ï½†',
  'ï¼§' => 'ï½‡',
  'ï¼¨' => 'ï½ˆ',
  'ï¼©' => 'ï½‰',
  'ï¼ª' => 'ï½Š',
  'ï¼«' => 'ï½‹',
  'ï¼¬' => 'ï½Œ',
  'ï¼­' => 'ï½�',
  'ï¼®' => 'ï½Ž',
  'ï¼¯' => 'ï½�',
  'ï¼°' => 'ï½�',
  'ï¼±' => 'ï½‘',
  'ï¼²' => 'ï½’',
  'ï¼³' => 'ï½“',
  'ï¼´' => 'ï½”',
  'ï¼µ' => 'ï½•',
  'ï¼¶' => 'ï½–',
  'ï¼·' => 'ï½—',
  'ï¼¸' => 'ï½˜',
  'ï¼¹' => 'ï½™',
  'ï¼º' => 'ï½š',
  'ð��€' => 'ð��¨',
  'ð���' => 'ð��©',
  'ð��‚' => 'ð��ª',
  'ð��ƒ' => 'ð��«',
  'ð��„' => 'ð��¬',
  'ð��…' => 'ð��­',
  'ð��†' => 'ð��®',
  'ð��‡' => 'ð��¯',
  'ð��ˆ' => 'ð��°',
  'ð��‰' => 'ð��±',
  'ð��Š' => 'ð��²',
  'ð��‹' => 'ð��³',
  'ð��Œ' => 'ð��´',
  'ð���' => 'ð��µ',
  'ð��Ž' => 'ð��¶',
  'ð���' => 'ð��·',
  'ð���' => 'ð��¸',
  'ð��‘' => 'ð��¹',
  'ð��’' => 'ð��º',
  'ð��“' => 'ð��»',
  'ð��”' => 'ð��¼',
  'ð��•' => 'ð��½',
  'ð��–' => 'ð��¾',
  'ð��—' => 'ð��¿',
  'ð��˜' => 'ð�‘€',
  'ð��™' => 'ð�‘�',
  'ð��š' => 'ð�‘‚',
  'ð��›' => 'ð�‘ƒ',
  'ð��œ' => 'ð�‘„',
  'ð���' => 'ð�‘…',
  'ð��ž' => 'ð�‘†',
  'ð��Ÿ' => 'ð�‘‡',
  'ð�� ' => 'ð�‘ˆ',
  'ð��¡' => 'ð�‘‰',
  'ð��¢' => 'ð�‘Š',
  'ð��£' => 'ð�‘‹',
  'ð��¤' => 'ð�‘Œ',
  'ð��¥' => 'ð�‘�',
  'ð��¦' => 'ð�‘Ž',
  'ð��§' => 'ð�‘�',
  'ð�’°' => 'ð�“˜',
  'ð�’±' => 'ð�“™',
  'ð�’²' => 'ð�“š',
  'ð�’³' => 'ð�“›',
  'ð�’´' => 'ð�“œ',
  'ð�’µ' => 'ð�“�',
  'ð�’¶' => 'ð�“ž',
  'ð�’·' => 'ð�“Ÿ',
  'ð�’¸' => 'ð�“ ',
  'ð�’¹' => 'ð�“¡',
  'ð�’º' => 'ð�“¢',
  'ð�’»' => 'ð�“£',
  'ð�’¼' => 'ð�“¤',
  'ð�’½' => 'ð�“¥',
  'ð�’¾' => 'ð�“¦',
  'ð�’¿' => 'ð�“§',
  'ð�“€' => 'ð�“¨',
  'ð�“�' => 'ð�“©',
  'ð�“‚' => 'ð�“ª',
  'ð�“ƒ' => 'ð�“«',
  'ð�“„' => 'ð�“¬',
  'ð�“…' => 'ð�“­',
  'ð�“†' => 'ð�“®',
  'ð�“‡' => 'ð�“¯',
  'ð�“ˆ' => 'ð�“°',
  'ð�“‰' => 'ð�“±',
  'ð�“Š' => 'ð�“²',
  'ð�“‹' => 'ð�“³',
  'ð�“Œ' => 'ð�“´',
  'ð�“�' => 'ð�“µ',
  'ð�“Ž' => 'ð�“¶',
  'ð�“�' => 'ð�“·',
  'ð�“�' => 'ð�“¸',
  'ð�“‘' => 'ð�“¹',
  'ð�“’' => 'ð�“º',
  'ð�““' => 'ð�“»',
  'ð�²€' => 'ð�³€',
  'ð�²�' => 'ð�³�',
  'ð�²‚' => 'ð�³‚',
  'ð�²ƒ' => 'ð�³ƒ',
  'ð�²„' => 'ð�³„',
  'ð�²…' => 'ð�³…',
  'ð�²†' => 'ð�³†',
  'ð�²‡' => 'ð�³‡',
  'ð�²ˆ' => 'ð�³ˆ',
  'ð�²‰' => 'ð�³‰',
  'ð�²Š' => 'ð�³Š',
  'ð�²‹' => 'ð�³‹',
  'ð�²Œ' => 'ð�³Œ',
  'ð�²�' => 'ð�³�',
  'ð�²Ž' => 'ð�³Ž',
  'ð�²�' => 'ð�³�',
  'ð�²�' => 'ð�³�',
  'ð�²‘' => 'ð�³‘',
  'ð�²’' => 'ð�³’',
  'ð�²“' => 'ð�³“',
  'ð�²”' => 'ð�³”',
  'ð�²•' => 'ð�³•',
  'ð�²–' => 'ð�³–',
  'ð�²—' => 'ð�³—',
  'ð�²˜' => 'ð�³˜',
  'ð�²™' => 'ð�³™',
  'ð�²š' => 'ð�³š',
  'ð�²›' => 'ð�³›',
  'ð�²œ' => 'ð�³œ',
  'ð�²�' => 'ð�³�',
  'ð�²ž' => 'ð�³ž',
  'ð�²Ÿ' => 'ð�³Ÿ',
  'ð�² ' => 'ð�³ ',
  'ð�²¡' => 'ð�³¡',
  'ð�²¢' => 'ð�³¢',
  'ð�²£' => 'ð�³£',
  'ð�²¤' => 'ð�³¤',
  'ð�²¥' => 'ð�³¥',
  'ð�²¦' => 'ð�³¦',
  'ð�²§' => 'ð�³§',
  'ð�²¨' => 'ð�³¨',
  'ð�²©' => 'ð�³©',
  'ð�²ª' => 'ð�³ª',
  'ð�²«' => 'ð�³«',
  'ð�²¬' => 'ð�³¬',
  'ð�²­' => 'ð�³­',
  'ð�²®' => 'ð�³®',
  'ð�²¯' => 'ð�³¯',
  'ð�²°' => 'ð�³°',
  'ð�²±' => 'ð�³±',
  'ð�²²' => 'ð�³²',
  'ð‘¢ ' => 'ð‘£€',
  'ð‘¢¡' => 'ð‘£�',
  'ð‘¢¢' => 'ð‘£‚',
  'ð‘¢£' => 'ð‘£ƒ',
  'ð‘¢¤' => 'ð‘£„',
  'ð‘¢¥' => 'ð‘£…',
  'ð‘¢¦' => 'ð‘£†',
  'ð‘¢§' => 'ð‘£‡',
  'ð‘¢¨' => 'ð‘£ˆ',
  'ð‘¢©' => 'ð‘£‰',
  'ð‘¢ª' => 'ð‘£Š',
  'ð‘¢«' => 'ð‘£‹',
  'ð‘¢¬' => 'ð‘£Œ',
  'ð‘¢­' => 'ð‘£�',
  'ð‘¢®' => 'ð‘£Ž',
  'ð‘¢¯' => 'ð‘£�',
  'ð‘¢°' => 'ð‘£�',
  'ð‘¢±' => 'ð‘£‘',
  'ð‘¢²' => 'ð‘£’',
  'ð‘¢³' => 'ð‘£“',
  'ð‘¢´' => 'ð‘£”',
  'ð‘¢µ' => 'ð‘£•',
  'ð‘¢¶' => 'ð‘£–',
  'ð‘¢·' => 'ð‘£—',
  'ð‘¢¸' => 'ð‘£˜',
  'ð‘¢¹' => 'ð‘£™',
  'ð‘¢º' => 'ð‘£š',
  'ð‘¢»' => 'ð‘£›',
  'ð‘¢¼' => 'ð‘£œ',
  'ð‘¢½' => 'ð‘£�',
  'ð‘¢¾' => 'ð‘£ž',
  'ð‘¢¿' => 'ð‘£Ÿ',
  'ð–¹€' => 'ð–¹ ',
  'ð–¹�' => 'ð–¹¡',
  'ð–¹‚' => 'ð–¹¢',
  'ð–¹ƒ' => 'ð–¹£',
  'ð–¹„' => 'ð–¹¤',
  'ð–¹…' => 'ð–¹¥',
  'ð–¹†' => 'ð–¹¦',
  'ð–¹‡' => 'ð–¹§',
  'ð–¹ˆ' => 'ð–¹¨',
  'ð–¹‰' => 'ð–¹©',
  'ð–¹Š' => 'ð–¹ª',
  'ð–¹‹' => 'ð–¹«',
  'ð–¹Œ' => 'ð–¹¬',
  'ð–¹�' => 'ð–¹­',
  'ð–¹Ž' => 'ð–¹®',
  'ð–¹�' => 'ð–¹¯',
  'ð–¹�' => 'ð–¹°',
  'ð–¹‘' => 'ð–¹±',
  'ð–¹’' => 'ð–¹²',
  'ð–¹“' => 'ð–¹³',
  'ð–¹”' => 'ð–¹´',
  'ð–¹•' => 'ð–¹µ',
  'ð–¹–' => 'ð–¹¶',
  'ð–¹—' => 'ð–¹·',
  'ð–¹˜' => 'ð–¹¸',
  'ð–¹™' => 'ð–¹¹',
  'ð–¹š' => 'ð–¹º',
  'ð–¹›' => 'ð–¹»',
  'ð–¹œ' => 'ð–¹¼',
  'ð–¹�' => 'ð–¹½',
  'ð–¹ž' => 'ð–¹¾',
  'ð–¹Ÿ' => 'ð–¹¿',
  'ðž¤€' => 'ðž¤¢',
  'ðž¤�' => 'ðž¤£',
  'ðž¤‚' => 'ðž¤¤',
  'ðž¤ƒ' => 'ðž¤¥',
  'ðž¤„' => 'ðž¤¦',
  'ðž¤…' => 'ðž¤§',
  'ðž¤†' => 'ðž¤¨',
  'ðž¤‡' => 'ðž¤©',
  'ðž¤ˆ' => 'ðž¤ª',
  'ðž¤‰' => 'ðž¤«',
  'ðž¤Š' => 'ðž¤¬',
  'ðž¤‹' => 'ðž¤­',
  'ðž¤Œ' => 'ðž¤®',
  'ðž¤�' => 'ðž¤¯',
  'ðž¤Ž' => 'ðž¤°',
  'ðž¤�' => 'ðž¤±',
  'ðž¤�' => 'ðž¤²',
  'ðž¤‘' => 'ðž¤³',
  'ðž¤’' => 'ðž¤´',
  'ðž¤“' => 'ðž¤µ',
  'ðž¤”' => 'ðž¤¶',
  'ðž¤•' => 'ðž¤·',
  'ðž¤–' => 'ðž¤¸',
  'ðž¤—' => 'ðž¤¹',
  'ðž¤˜' => 'ðž¤º',
  'ðž¤™' => 'ðž¤»',
  'ðž¤š' => 'ðž¤¼',
  'ðž¤›' => 'ðž¤½',
  'ðž¤œ' => 'ðž¤¾',
  'ðž¤�' => 'ðž¤¿',
  'ðž¤ž' => 'ðž¥€',
  'ðž¤Ÿ' => 'ðž¥�',
  'ðž¤ ' => 'ðž¥‚',
  'ðž¤¡' => 'ðž¥ƒ',
);
<?php

return [
    'Ä°' => 'iÌ‡',
    'Âµ' => 'Î¼',
    'Å¿' => 's',
    'Í…' => 'Î¹',
    'Ï‚' => 'Ïƒ',
    'Ï�' => 'Î²',
    'Ï‘' => 'Î¸',
    'Ï•' => 'Ï†',
    'Ï–' => 'Ï€',
    'Ï°' => 'Îº',
    'Ï±' => 'Ï�',
    'Ïµ' => 'Îµ',
    'áº›' => 'á¹¡',
    'á¾¾' => 'Î¹',
    'ÃŸ' => 'ss',
    'Å‰' => 'Ê¼n',
    'Ç°' => 'Ç°',
    'Î�' => 'Î�',
    'Î°' => 'Î°',
    'Ö‡' => 'Õ¥Ö‚',
    'áº–' => 'áº–',
    'áº—' => 'áº—',
    'áº˜' => 'áº˜',
    'áº™' => 'áº™',
    'áºš' => 'aÊ¾',
    'áºž' => 'ss',
    'á½�' => 'á½�',
    'á½’' => 'á½’',
    'á½”' => 'á½”',
    'á½–' => 'á½–',
    'á¾€' => 'á¼€Î¹',
    'á¾�' => 'á¼�Î¹',
    'á¾‚' => 'á¼‚Î¹',
    'á¾ƒ' => 'á¼ƒÎ¹',
    'á¾„' => 'á¼„Î¹',
    'á¾…' => 'á¼…Î¹',
    'á¾†' => 'á¼†Î¹',
    'á¾‡' => 'á¼‡Î¹',
    'á¾ˆ' => 'á¼€Î¹',
    'á¾‰' => 'á¼�Î¹',
    'á¾Š' => 'á¼‚Î¹',
    'á¾‹' => 'á¼ƒÎ¹',
    'á¾Œ' => 'á¼„Î¹',
    'á¾�' => 'á¼…Î¹',
    'á¾Ž' => 'á¼†Î¹',
    'á¾�' => 'á¼‡Î¹',
    'á¾�' => 'á¼ Î¹',
    'á¾‘' => 'á¼¡Î¹',
    'á¾’' => 'á¼¢Î¹',
    'á¾“' => 'á¼£Î¹',
    'á¾”' => 'á¼¤Î¹',
    'á¾•' => 'á¼¥Î¹',
    'á¾–' => 'á¼¦Î¹',
    'á¾—' => 'á¼§Î¹',
    'á¾˜' => 'á¼ Î¹',
    'á¾™' => 'á¼¡Î¹',
    'á¾š' => 'á¼¢Î¹',
    'á¾›' => 'á¼£Î¹',
    'á¾œ' => 'á¼¤Î¹',
    'á¾�' => 'á¼¥Î¹',
    'á¾ž' => 'á¼¦Î¹',
    'á¾Ÿ' => 'á¼§Î¹',
    'á¾ ' => 'á½ Î¹',
    'á¾¡' => 'á½¡Î¹',
    'á¾¢' => 'á½¢Î¹',
    'á¾£' => 'á½£Î¹',
    'á¾¤' => 'á½¤Î¹',
    'á¾¥' => 'á½¥Î¹',
    'á¾¦' => 'á½¦Î¹',
    'á¾§' => 'á½§Î¹',
    'á¾¨' => 'á½ Î¹',
    'á¾©' => 'á½¡Î¹',
    'á¾ª' => 'á½¢Î¹',
    'á¾«' => 'á½£Î¹',
    'á¾¬' => 'á½¤Î¹',
    'á¾­' => 'á½¥Î¹',
    'á¾®' => 'á½¦Î¹',
    'á¾¯' => 'á½§Î¹',
    'á¾²' => 'á½°Î¹',
    'á¾³' => 'Î±Î¹',
    'á¾´' => 'Î¬Î¹',
    'á¾¶' => 'á¾¶',
    'á¾·' => 'á¾¶Î¹',
    'á¾¼' => 'Î±Î¹',
    'á¿‚' => 'á½´Î¹',
    'á¿ƒ' => 'Î·Î¹',
    'á¿„' => 'Î®Î¹',
    'á¿†' => 'á¿†',
    'á¿‡' => 'á¿†Î¹',
    'á¿Œ' => 'Î·Î¹',
    'á¿’' => 'á¿’',
    'á¿–' => 'á¿–',
    'á¿—' => 'á¿—',
    'á¿¢' => 'á¿¢',
    'á¿¤' => 'á¿¤',
    'á¿¦' => 'á¿¦',
    'á¿§' => 'á¿§',
    'á¿²' => 'á½¼Î¹',
    'á¿³' => 'Ï‰Î¹',
    'á¿´' => 'ÏŽÎ¹',
    'á¿¶' => 'á¿¶',
    'á¿·' => 'á¿¶Î¹',
    'á¿¼' => 'Ï‰Î¹',
    'ï¬€' => 'ff',
    'ï¬�' => 'fi',
    'ï¬‚' => 'fl',
    'ï¬ƒ' => 'ffi',
    'ï¬„' => 'ffl',
    'ï¬…' => 'st',
    'ï¬†' => 'st',
    'ï¬“' => 'Õ´Õ¶',
    'ï¬”' => 'Õ´Õ¥',
    'ï¬•' => 'Õ´Õ«',
    'ï¬–' => 'Õ¾Õ¶',
    'ï¬—' => 'Õ´Õ­',
];
<?php

// from Case_Ignorable in https://unicode.org/Public/UNIDATA/DerivedCoreProperties.txt

return '/(?<![\x{0027}\x{002E}\x{003A}\x{005E}\x{0060}\x{00A8}\x{00AD}\x{00AF}\x{00B4}\x{00B7}\x{00B8}\x{02B0}-\x{02C1}\x{02C2}-\x{02C5}\x{02C6}-\x{02D1}\x{02D2}-\x{02DF}\x{02E0}-\x{02E4}\x{02E5}-\x{02EB}\x{02EC}\x{02ED}\x{02EE}\x{02EF}-\x{02FF}\x{0300}-\x{036F}\x{0374}\x{0375}\x{037A}\x{0384}-\x{0385}\x{0387}\x{0483}-\x{0487}\x{0488}-\x{0489}\x{0559}\x{0591}-\x{05BD}\x{05BF}\x{05C1}-\x{05C2}\x{05C4}-\x{05C5}\x{05C7}\x{05F4}\x{0600}-\x{0605}\x{0610}-\x{061A}\x{061C}\x{0640}\x{064B}-\x{065F}\x{0670}\x{06D6}-\x{06DC}\x{06DD}\x{06DF}-\x{06E4}\x{06E5}-\x{06E6}\x{06E7}-\x{06E8}\x{06EA}-\x{06ED}\x{070F}\x{0711}\x{0730}-\x{074A}\x{07A6}-\x{07B0}\x{07EB}-\x{07F3}\x{07F4}-\x{07F5}\x{07FA}\x{07FD}\x{0816}-\x{0819}\x{081A}\x{081B}-\x{0823}\x{0824}\x{0825}-\x{0827}\x{0828}\x{0829}-\x{082D}\x{0859}-\x{085B}\x{08D3}-\x{08E1}\x{08E2}\x{08E3}-\x{0902}\x{093A}\x{093C}\x{0941}-\x{0948}\x{094D}\x{0951}-\x{0957}\x{0962}-\x{0963}\x{0971}\x{0981}\x{09BC}\x{09C1}-\x{09C4}\x{09CD}\x{09E2}-\x{09E3}\x{09FE}\x{0A01}-\x{0A02}\x{0A3C}\x{0A41}-\x{0A42}\x{0A47}-\x{0A48}\x{0A4B}-\x{0A4D}\x{0A51}\x{0A70}-\x{0A71}\x{0A75}\x{0A81}-\x{0A82}\x{0ABC}\x{0AC1}-\x{0AC5}\x{0AC7}-\x{0AC8}\x{0ACD}\x{0AE2}-\x{0AE3}\x{0AFA}-\x{0AFF}\x{0B01}\x{0B3C}\x{0B3F}\x{0B41}-\x{0B44}\x{0B4D}\x{0B56}\x{0B62}-\x{0B63}\x{0B82}\x{0BC0}\x{0BCD}\x{0C00}\x{0C04}\x{0C3E}-\x{0C40}\x{0C46}-\x{0C48}\x{0C4A}-\x{0C4D}\x{0C55}-\x{0C56}\x{0C62}-\x{0C63}\x{0C81}\x{0CBC}\x{0CBF}\x{0CC6}\x{0CCC}-\x{0CCD}\x{0CE2}-\x{0CE3}\x{0D00}-\x{0D01}\x{0D3B}-\x{0D3C}\x{0D41}-\x{0D44}\x{0D4D}\x{0D62}-\x{0D63}\x{0DCA}\x{0DD2}-\x{0DD4}\x{0DD6}\x{0E31}\x{0E34}-\x{0E3A}\x{0E46}\x{0E47}-\x{0E4E}\x{0EB1}\x{0EB4}-\x{0EB9}\x{0EBB}-\x{0EBC}\x{0EC6}\x{0EC8}-\x{0ECD}\x{0F18}-\x{0F19}\x{0F35}\x{0F37}\x{0F39}\x{0F71}-\x{0F7E}\x{0F80}-\x{0F84}\x{0F86}-\x{0F87}\x{0F8D}-\x{0F97}\x{0F99}-\x{0FBC}\x{0FC6}\x{102D}-\x{1030}\x{1032}-\x{1037}\x{1039}-\x{103A}\x{103D}-\x{103E}\x{1058}-\x{1059}\x{105E}-\x{1060}\x{1071}-\x{1074}\x{1082}\x{1085}-\x{1086}\x{108D}\x{109D}\x{10FC}\x{135D}-\x{135F}\x{1712}-\x{1714}\x{1732}-\x{1734}\x{1752}-\x{1753}\x{1772}-\x{1773}\x{17B4}-\x{17B5}\x{17B7}-\x{17BD}\x{17C6}\x{17C9}-\x{17D3}\x{17D7}\x{17DD}\x{180B}-\x{180D}\x{180E}\x{1843}\x{1885}-\x{1886}\x{18A9}\x{1920}-\x{1922}\x{1927}-\x{1928}\x{1932}\x{1939}-\x{193B}\x{1A17}-\x{1A18}\x{1A1B}\x{1A56}\x{1A58}-\x{1A5E}\x{1A60}\x{1A62}\x{1A65}-\x{1A6C}\x{1A73}-\x{1A7C}\x{1A7F}\x{1AA7}\x{1AB0}-\x{1ABD}\x{1ABE}\x{1B00}-\x{1B03}\x{1B34}\x{1B36}-\x{1B3A}\x{1B3C}\x{1B42}\x{1B6B}-\x{1B73}\x{1B80}-\x{1B81}\x{1BA2}-\x{1BA5}\x{1BA8}-\x{1BA9}\x{1BAB}-\x{1BAD}\x{1BE6}\x{1BE8}-\x{1BE9}\x{1BED}\x{1BEF}-\x{1BF1}\x{1C2C}-\x{1C33}\x{1C36}-\x{1C37}\x{1C78}-\x{1C7D}\x{1CD0}-\x{1CD2}\x{1CD4}-\x{1CE0}\x{1CE2}-\x{1CE8}\x{1CED}\x{1CF4}\x{1CF8}-\x{1CF9}\x{1D2C}-\x{1D6A}\x{1D78}\x{1D9B}-\x{1DBF}\x{1DC0}-\x{1DF9}\x{1DFB}-\x{1DFF}\x{1FBD}\x{1FBF}-\x{1FC1}\x{1FCD}-\x{1FCF}\x{1FDD}-\x{1FDF}\x{1FED}-\x{1FEF}\x{1FFD}-\x{1FFE}\x{200B}-\x{200F}\x{2018}\x{2019}\x{2024}\x{2027}\x{202A}-\x{202E}\x{2060}-\x{2064}\x{2066}-\x{206F}\x{2071}\x{207F}\x{2090}-\x{209C}\x{20D0}-\x{20DC}\x{20DD}-\x{20E0}\x{20E1}\x{20E2}-\x{20E4}\x{20E5}-\x{20F0}\x{2C7C}-\x{2C7D}\x{2CEF}-\x{2CF1}\x{2D6F}\x{2D7F}\x{2DE0}-\x{2DFF}\x{2E2F}\x{3005}\x{302A}-\x{302D}\x{3031}-\x{3035}\x{303B}\x{3099}-\x{309A}\x{309B}-\x{309C}\x{309D}-\x{309E}\x{30FC}-\x{30FE}\x{A015}\x{A4F8}-\x{A4FD}\x{A60C}\x{A66F}\x{A670}-\x{A672}\x{A674}-\x{A67D}\x{A67F}\x{A69C}-\x{A69D}\x{A69E}-\x{A69F}\x{A6F0}-\x{A6F1}\x{A700}-\x{A716}\x{A717}-\x{A71F}\x{A720}-\x{A721}\x{A770}\x{A788}\x{A789}-\x{A78A}\x{A7F8}-\x{A7F9}\x{A802}\x{A806}\x{A80B}\x{A825}-\x{A826}\x{A8C4}-\x{A8C5}\x{A8E0}-\x{A8F1}\x{A8FF}\x{A926}-\x{A92D}\x{A947}-\x{A951}\x{A980}-\x{A982}\x{A9B3}\x{A9B6}-\x{A9B9}\x{A9BC}\x{A9CF}\x{A9E5}\x{A9E6}\x{AA29}-\x{AA2E}\x{AA31}-\x{AA32}\x{AA35}-\x{AA36}\x{AA43}\x{AA4C}\x{AA70}\x{AA7C}\x{AAB0}\x{AAB2}-\x{AAB4}\x{AAB7}-\x{AAB8}\x{AABE}-\x{AABF}\x{AAC1}\x{AADD}\x{AAEC}-\x{AAED}\x{AAF3}-\x{AAF4}\x{AAF6}\x{AB5B}\x{AB5C}-\x{AB5F}\x{ABE5}\x{ABE8}\x{ABED}\x{FB1E}\x{FBB2}-\x{FBC1}\x{FE00}-\x{FE0F}\x{FE13}\x{FE20}-\x{FE2F}\x{FE52}\x{FE55}\x{FEFF}\x{FF07}\x{FF0E}\x{FF1A}\x{FF3E}\x{FF40}\x{FF70}\x{FF9E}-\x{FF9F}\x{FFE3}\x{FFF9}-\x{FFFB}\x{101FD}\x{102E0}\x{10376}-\x{1037A}\x{10A01}-\x{10A03}\x{10A05}-\x{10A06}\x{10A0C}-\x{10A0F}\x{10A38}-\x{10A3A}\x{10A3F}\x{10AE5}-\x{10AE6}\x{10D24}-\x{10D27}\x{10F46}-\x{10F50}\x{11001}\x{11038}-\x{11046}\x{1107F}-\x{11081}\x{110B3}-\x{110B6}\x{110B9}-\x{110BA}\x{110BD}\x{110CD}\x{11100}-\x{11102}\x{11127}-\x{1112B}\x{1112D}-\x{11134}\x{11173}\x{11180}-\x{11181}\x{111B6}-\x{111BE}\x{111C9}-\x{111CC}\x{1122F}-\x{11231}\x{11234}\x{11236}-\x{11237}\x{1123E}\x{112DF}\x{112E3}-\x{112EA}\x{11300}-\x{11301}\x{1133B}-\x{1133C}\x{11340}\x{11366}-\x{1136C}\x{11370}-\x{11374}\x{11438}-\x{1143F}\x{11442}-\x{11444}\x{11446}\x{1145E}\x{114B3}-\x{114B8}\x{114BA}\x{114BF}-\x{114C0}\x{114C2}-\x{114C3}\x{115B2}-\x{115B5}\x{115BC}-\x{115BD}\x{115BF}-\x{115C0}\x{115DC}-\x{115DD}\x{11633}-\x{1163A}\x{1163D}\x{1163F}-\x{11640}\x{116AB}\x{116AD}\x{116B0}-\x{116B5}\x{116B7}\x{1171D}-\x{1171F}\x{11722}-\x{11725}\x{11727}-\x{1172B}\x{1182F}-\x{11837}\x{11839}-\x{1183A}\x{11A01}-\x{11A0A}\x{11A33}-\x{11A38}\x{11A3B}-\x{11A3E}\x{11A47}\x{11A51}-\x{11A56}\x{11A59}-\x{11A5B}\x{11A8A}-\x{11A96}\x{11A98}-\x{11A99}\x{11C30}-\x{11C36}\x{11C38}-\x{11C3D}\x{11C3F}\x{11C92}-\x{11CA7}\x{11CAA}-\x{11CB0}\x{11CB2}-\x{11CB3}\x{11CB5}-\x{11CB6}\x{11D31}-\x{11D36}\x{11D3A}\x{11D3C}-\x{11D3D}\x{11D3F}-\x{11D45}\x{11D47}\x{11D90}-\x{11D91}\x{11D95}\x{11D97}\x{11EF3}-\x{11EF4}\x{16AF0}-\x{16AF4}\x{16B30}-\x{16B36}\x{16B40}-\x{16B43}\x{16F8F}-\x{16F92}\x{16F93}-\x{16F9F}\x{16FE0}-\x{16FE1}\x{1BC9D}-\x{1BC9E}\x{1BCA0}-\x{1BCA3}\x{1D167}-\x{1D169}\x{1D173}-\x{1D17A}\x{1D17B}-\x{1D182}\x{1D185}-\x{1D18B}\x{1D1AA}-\x{1D1AD}\x{1D242}-\x{1D244}\x{1DA00}-\x{1DA36}\x{1DA3B}-\x{1DA6C}\x{1DA75}\x{1DA84}\x{1DA9B}-\x{1DA9F}\x{1DAA1}-\x{1DAAF}\x{1E000}-\x{1E006}\x{1E008}-\x{1E018}\x{1E01B}-\x{1E021}\x{1E023}-\x{1E024}\x{1E026}-\x{1E02A}\x{1E8D0}-\x{1E8D6}\x{1E944}-\x{1E94A}\x{1F3FB}-\x{1F3FF}\x{E0001}\x{E0020}-\x{E007F}\x{E0100}-\x{E01EF}])(\pL)(\pL*+)/u';
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use Symfony\Polyfill\Php85 as p;

if (\PHP_VERSION_ID >= 80500) {
    return;
}

if (!function_exists('get_error_handler')) {
    function get_error_handler(): ?callable { return p\Php85::get_error_handler(); }
}

if (!function_exists('get_exception_handler')) {
    function get_exception_handler(): ?callable { return p\Php85::get_exception_handler(); }
}

if (!function_exists('array_first')) {
    function array_first(array $array) { return p\Php85::array_first($array); }
}

if (!function_exists('array_last')) {
    function array_last(array $array) { return p\Php85::array_last($array); }
}

if (extension_loaded('intl') && !function_exists('locale_is_right_to_left')) {
    function locale_is_right_to_left(string $locale): bool { return p\Php85::locale_is_right_to_left($locale); }
}

if (\PHP_VERSION_ID >= 80000) {
    require __DIR__.'/bootstrap80.php';

    return;
}

if (!class_exists('ValueError', false)) {
    class ValueError extends Error
    {
    }
}

if (extension_loaded('intl') && !function_exists('grapheme_levenshtein')) {
    function grapheme_levenshtein(string $string1, string $string2, ?int $insertion_cost = 1, ?int $replacement_cost = 1, ?int $deletion_cost = 1, string $locale = '') { return p\Php85::grapheme_levenshtein($string1, $string2, (int) $insertion_cost, (int) $replacement_cost, (int) $deletion_cost, $locale); }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use Symfony\Polyfill\Php85 as p;

if (\PHP_VERSION_ID >= 80500) {
    return;
}

if (extension_loaded('intl') && !function_exists('grapheme_levenshtein')) {
    function grapheme_levenshtein(string $string1, string $string2, ?int $insertion_cost = 1, ?int $replacement_cost = 1, ?int $deletion_cost = 1, string $locale = ''): int|false { return p\Php85::grapheme_levenshtein($string1, $string2, (int) $insertion_cost, (int) $replacement_cost, (int) $deletion_cost, $locale); }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Polyfill\Php85;

/**
 * @author Pierre Ambroise <pierre27.ambroise@gmail.com>
 * @author Alexander Schranz <alexander@sulu.io>
 *
 * @internal
 */
final class Php85
{
    public static function get_error_handler(): ?callable
    {
        $handler = set_error_handler(null);
        restore_error_handler();

        return $handler;
    }

    public static function get_exception_handler(): ?callable
    {
        $handler = set_exception_handler(null);
        restore_exception_handler();

        return $handler;
    }

    public static function array_first(array $array)
    {
        foreach ($array as $value) {
            return $value;
        }

        return null;
    }

    public static function array_last(array $array)
    {
        return $array ? current(\array_slice($array, -1)) : null;
    }

    // Taken from CLDR 44 (ICU 74); the exact set is ICU-version dependent and won't match every version of the native locale_is_right_to_left().
    private const RTL_SCRIPTS = [
        'Adlm' => true, 'Arab' => true, 'Armi' => true, 'Avst' => true,
        'Chrs' => true, 'Cprt' => true, 'Elym' => true, 'Hatr' => true,
        'Hebr' => true, 'Hung' => true, 'Khar' => true, 'Lydi' => true,
        'Mand' => true, 'Mani' => true, 'Mend' => true, 'Merc' => true,
        'Mero' => true, 'Narb' => true, 'Nbat' => true, 'Nkoo' => true,
        'Orkh' => true, 'Ougr' => true, 'Palm' => true, 'Phli' => true,
        'Phlp' => true, 'Phnx' => true, 'Prti' => true, 'Rohg' => true,
        'Samr' => true, 'Sarb' => true, 'Sogd' => true, 'Sogo' => true,
        'Syrc' => true, 'Thaa' => true, 'Yezi' => true,
    ];

    private const LANG_TO_SCRIPT = [
        'aeb' => 'Arab',
        'ar' => 'Arab',
        'arc' => 'Armi',
        'arq' => 'Arab',
        'ary' => 'Arab',
        'arz' => 'Arab',
        'bal' => 'Arab',
        'bgn' => 'Arab',
        'ckb' => 'Arab',
        'dv' => 'Thaa',
        'fa' => 'Arab',
        'glk' => 'Arab',
        'haz' => 'Arab',
        'he' => 'Hebr',
        'iw' => 'Hebr',
        'kas' => 'Arab',
        'ks' => 'Arab',
        'lrc' => 'Arab',
        'mzn' => 'Arab',
        'nqo' => 'Nkoo',
        'pnb' => 'Arab',
        'ps' => 'Arab',
        'rhg' => 'Rohg',
        'sam' => 'Samr',
        'sd' => 'Arab',
        'sdh' => 'Arab',
        'skr' => 'Arab',
        'syr' => 'Syrc',
        'ug' => 'Arab',
        'ur' => 'Arab',
        'yi' => 'Hebr',
    ];

    public static function locale_is_right_to_left(string $locale): bool
    {
        if ('' === $locale) {
            $locale = \Locale::getDefault();
        }

        $parts = preg_split('/[_-]/', substr($locale, 0, strcspn($locale, '@.')));
        $language = strtolower($parts[0]);

        if (isset($parts[1]) && 4 === \strlen($parts[1]) && ctype_alpha($parts[1])) {
            return isset(self::RTL_SCRIPTS[ucfirst(strtolower($parts[1]))]);
        }

        return isset(self::LANG_TO_SCRIPT[$language]) && isset(self::RTL_SCRIPTS[self::LANG_TO_SCRIPT[$language]]);
    }

    public static function grapheme_levenshtein(string $s1, string $s2, int $insertion_cost = 1, int $replacement_cost = 1, int $deletion_cost = 1, string $locale = '')
    {
        if ($insertion_cost <= 0 || $insertion_cost > 1073741823) {
            throw new \ValueError('grapheme_levenshtein(): Argument #3 ($insertion_cost) must be greater than 0 and less than or equal to 1073741823');
        }
        if ($replacement_cost <= 0 || $replacement_cost > 1073741823) {
            throw new \ValueError('grapheme_levenshtein(): Argument #4 ($replacement_cost) must be greater than 0 and less than or equal to 1073741823');
        }
        if ($deletion_cost <= 0 || $deletion_cost > 1073741823) {
            throw new \ValueError('grapheme_levenshtein(): Argument #5 ($deletion_cost) must be greater than 0 and less than or equal to 1073741823');
        }

        if (!preg_match('//u', $s1) || !preg_match('//u', $s2)) {
            return false;
        }

        $regex = ((float) \PCRE_VERSION >= 10.44)
            ? '\X'
            : '(?:\r\n|[\x{1F1E6}-\x{1F1FF}][\x{1F1E6}-\x{1F1FF}]?|(?:[ -~\x{200C}\x{200D}]|[á†¨-á‡¹]+|[á„€-á…Ÿ]*(?:[ê°€ê°œê°¸ê±”ê±°ê²Œê²¨ê³„ê³ ê³¼ê´˜ê´´êµ�êµ¬ê¶ˆê¶¤ê·€ê·œê·¸ê¸”ê¸°ê¹Œê¹¨êº„êº êº¼ê»˜ê»´ê¼�ê¼¬ê½ˆê½¤ê¾€ê¾œê¾¸ê¿”ê¿°ë€Œë€¨ë�„ë� ë�¼ë‚˜ë‚´ëƒ�ëƒ¬ë„ˆë„¤ë…€ë…œë…¸ë†”ë†°ë‡Œë‡¨ëˆ„ëˆ ëˆ¼ë‰˜ë‰´ëŠ�ëŠ¬ë‹ˆë‹¤ëŒ€ëŒœëŒ¸ë�”ë�°ëŽŒëŽ¨ë�„ë� ë�¼ë�˜ë�´ë‘�ë‘¬ë’ˆë’¤ë“€ë“œë“¸ë””ë”°ë•Œë•¨ë–„ë– ë–¼ë—˜ë—´ë˜�ë˜¬ë™ˆë™¤ëš€ëšœëš¸ë›”ë›°ëœŒëœ¨ë�„ë� ë�¼ëž˜ëž´ëŸ�ëŸ¬ë ˆë ¤ë¡€ë¡œë¡¸ë¢”ë¢°ë£Œë£¨ë¤„ë¤ ë¤¼ë¥˜ë¥´ë¦�ë¦¬ë§ˆë§¤ë¨€ë¨œë¨¸ë©”ë©°ëªŒëª¨ë«„ë« ë«¼ë¬˜ë¬´ë­�ë­¬ë®ˆë®¤ë¯€ë¯œë¯¸ë°”ë°°ë±Œë±¨ë²„ë² ë²¼ë³˜ë³´ë´�ë´¬ëµˆëµ¤ë¶€ë¶œë¶¸ë·”ë·°ë¸Œë¸¨ë¹„ë¹ ë¹¼ëº˜ëº´ë»�ë»¬ë¼ˆë¼¤ë½€ë½œë½¸ë¾”ë¾°ë¿Œë¿¨ì€„ì€ ì€¼ì�˜ì�´ì‚�ì‚¬ìƒˆìƒ¤ì„€ì„œì„¸ì…”ì…°ì†Œì†¨ì‡„ì‡ ì‡¼ìˆ˜ìˆ´ì‰�ì‰¬ìŠˆìŠ¤ì‹€ì‹œì‹¸ìŒ”ìŒ°ì�Œì�¨ìŽ„ìŽ ìŽ¼ì�˜ì�´ì��ì�¬ì‘ˆì‘¤ì’€ì’œì’¸ì“”ì“°ì”Œì”¨ì•„ì• ì•¼ì–˜ì–´ì—�ì—¬ì˜ˆì˜¤ì™€ì™œì™¸ìš”ìš°ì›Œì›¨ìœ„ìœ ìœ¼ì�˜ì�´ìž�ìž¬ìŸˆìŸ¤ì €ì œì ¸ì¡”ì¡°ì¢Œì¢¨ì£„ì£ ì£¼ì¤˜ì¤´ì¥�ì¥¬ì¦ˆì¦¤ì§€ì§œì§¸ì¨”ì¨°ì©Œì©¨ìª„ìª ìª¼ì«˜ì«´ì¬�ì¬¬ì­ˆì­¤ì®€ì®œì®¸ì¯”ì¯°ì°Œì°¨ì±„ì± ì±¼ì²˜ì²´ì³�ì³¬ì´ˆì´¤ìµ€ìµœìµ¸ì¶”ì¶°ì·Œì·¨ì¸„ì¸ ì¸¼ì¹˜ì¹´ìº�ìº¬ì»ˆì»¤ì¼€ì¼œì¼¸ì½”ì½°ì¾Œì¾¨ì¿„ì¿ ì¿¼í€˜í€´í��í�¬í‚ˆí‚¤íƒ€íƒœíƒ¸í„”í„°í…Œí…¨í†„í† í†¼í‡˜í‡´íˆ�íˆ¬í‰ˆí‰¤íŠ€íŠœíŠ¸í‹”í‹°íŒŒíŒ¨í�„í� í�¼íŽ˜íŽ´í��í�¬í�ˆí�¤í‘€í‘œí‘¸í’”í’°í“Œí“¨í”„í” í”¼í•˜í•´í–�í–¬í—ˆí—¤í˜€í˜œí˜¸í™”í™°íšŒíš¨í›„í› í›¼íœ˜íœ´í��í�¬ížˆ]?[á… -á†¢]+|[ê°€-íž£])[á†¨-á‡¹]*|[á„€-á…Ÿ]+|[^\p{Cc}\p{Cf}\p{Zl}\p{Zp}])[\p{Mn}\p{Mc}\p{Me}\x{09BE}\x{09D7}\x{0B3E}\x{0B57}\x{0BBE}\x{0BD7}\x{0CC2}\x{0CD5}\x{0CD6}\x{0D3E}\x{0D57}\x{0DCF}\x{0DDF}\x{200C}\x{1D165}\x{1D16E}-\x{1D172}\x{1F3FB}-\x{1F3FF}\x{FE0E}-\x{FE0F}\x{E0020}-\x{E007F}]*(?:\x{200D}(?:[\x{1F1E6}-\x{1F1FF}]|[ -~\x{200C}\x{200D}]|[á†¨-á‡¹]+|[á„€-á…Ÿ]*(?:[ê°€-íž£]?[á… -á†¢]+|[ê°€-íž£])[á†¨-á‡¹]*|[á„€-á…Ÿ]+|[^\p{Cc}\p{Cf}\p{Zl}\p{Zp}])[\p{Mn}\p{Mc}\p{Me}\x{09BE}\x{09D7}\x{0B3E}\x{0B57}\x{0BBE}\x{0BD7}\x{0CC2}\x{0CD5}\x{0CD6}\x{0D3E}\x{0D57}\x{0DCF}\x{0DDF}\x{200C}\x{1D165}\x{1D16E}-\x{1D172}\x{1F3FB}-\x{1F3FF}\x{FE0E}-\x{FE0F}\x{E0020}-\x{E007F}]*)*|[\p{Cc}\p{Cf}\p{Zl}\p{Zp}])';

        preg_match_all('/'.$regex.'/u', $s1, $s1);
        preg_match_all('/'.$regex.'/u', $s2, $s2);

        $s1 = $s1[0];
        $s2 = $s2[0];
        $l1 = \count($s1);
        $l2 = \count($s2);

        // Keep the rows as short as possible. Reversing the transformation
        // swaps the meaning of insertion and deletion.
        if ($l1 < $l2) {
            [$s1, $s2] = [$s2, $s1];
            [$l1, $l2] = [$l2, $l1];
            [$insertion_cost, $deletion_cost] = [$deletion_cost, $insertion_cost];
        }

        if (0 === $l2) {
            return $l1 * $deletion_cost;
        }

        // Graphemes are equal when the collator says so, as with ucol_strcoll() in intl
        try {
            $collator = new \Collator('' === $locale ? 'root' : $locale);
        } catch (\IntlException $e) {
            return false;
        }
        $s1 = array_map([$collator, 'getSortKey'], $s1);
        $s2 = array_map([$collator, 'getSortKey'], $s2);

        $previousRow = $currentRow = array_fill(0, $l2 + 1, 0);
        for ($j = 1; $j <= $l2; ++$j) {
            $previousRow[$j] = $previousRow[$j - 1] + $insertion_cost;
        }

        for ($i = 1; $i <= $l1; ++$i) {
            $currentRow[0] = $previousRow[0] + $deletion_cost;

            for ($j = 1; $j <= $l2; ++$j) {
                $cost = ($s1[$i - 1] === $s2[$j - 1]) ? 0 : $replacement_cost;
                $currentRow[$j] = min($previousRow[$j] + $deletion_cost, $currentRow[$j - 1] + $insertion_cost, $previousRow[$j - 1] + $cost);
            }

            [$previousRow, $currentRow] = [$currentRow, $previousRow];
        }

        return $previousRow[$l2];
    }
}
{
    "name": "symfony/polyfill-php85",
    "type": "library",
    "description": "Symfony polyfill backporting some PHP 8.5+ features to lower PHP versions",
    "keywords": ["polyfill", "shim", "compatibility", "portable"],
    "homepage": "https://symfony.com",
    "license": "MIT",
    "authors": [
        {
            "name": "Nicolas Grekas",
            "email": "p@tchwork.com"
        },
        {
            "name": "Symfony Community",
            "homepage": "https://symfony.com/contributors"
        }
    ],
    "require": {
        "php": ">=7.2"
    },
    "autoload": {
        "psr-4": { "Symfony\\Polyfill\\Php85\\": "" },
        "files": [ "bootstrap.php" ],
        "classmap": [ "Resources/stubs" ]
    },
    "minimum-stability": "dev",
    "extra": {
        "thanks": {
            "name": "symfony/polyfill",
            "url": "https://github.com/symfony/polyfill"
        }
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

if (\PHP_VERSION_ID < 80500) {
    // @author Daniel Scherzer <daniel.e.scherzer@gmail.com>
    #[Attribute(Attribute::TARGET_ALL)]
    final class DelayedTargetValidation
    {
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Filter;

if (\PHP_VERSION_ID < 80500) {
    class FilterFailedException extends FilterException
    {
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Filter;

if (\PHP_VERSION_ID < 80500) {
    class FilterException extends \Exception
    {
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

if (\PHP_VERSION_ID < 80500) {
    #[Attribute(Attribute::TARGET_METHOD | Attribute::TARGET_FUNCTION)]
    final class NoDiscard
    {
        public ?string $message;

        public function __construct(?string $message = null)
        {
            $this->message = $message;
        }
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Polyfill\Php86;

/**
 * @author kylekatarnls <kylekatarnls@gmail.com>
 *
 * @internal
 */
final class Php86
{
    /**
     * @template Value
     * @template Minimum
     * @template Maximum
     *
     * @param Value   $value
     * @param Minimum $min
     * @param Maximum $max
     *
     * @return Value|Minimum|Maximum
     */
    public static function clamp($value, $min, $max)
    {
        if (\is_float($min) && is_nan($min)) {
            self::throwValueErrorIfAvailable('clamp(): Argument #2 ($min) must not be NAN');
        }

        if (\is_float($max) && is_nan($max)) {
            self::throwValueErrorIfAvailable('clamp(): Argument #3 ($max) must not be NAN');
        }

        if ($max < $min) {
            self::throwValueErrorIfAvailable('clamp(): Argument #2 ($min) must be smaller than or equal to argument #3 ($max)');
        }

        if ($value > $max) {
            return $max;
        }

        if ($value < $min) {
            return $min;
        }

        return $value;
    }

    private static function throwValueErrorIfAvailable(string $message): void
    {
        if (!class_exists(\ValueError::class)) {
            throw new \InvalidArgumentException($message);
        }

        throw new \ValueError($message);
    }

    public static function grapheme_strrev(string $string)
    {
        $iterator = \IntlBreakIterator::createCharacterInstance();
        $iterator->setText($string);
        $reversed = '';
        $end = $iterator->last();

        while (\IntlBreakIterator::DONE !== $start = $iterator->previous()) {
            $reversed .= substr($string, $start, $end - $start);
            $end = $start;
        }

        return $reversed;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use Symfony\Polyfill\Php86 as p;

if (\PHP_VERSION_ID >= 80600) {
    return;
}

if (!defined('ARRAY_FILTER_USE_VALUE')) {
    define('ARRAY_FILTER_USE_VALUE', 0);
}

if (\PHP_VERSION_ID >= 80000) {
    return require __DIR__.'/bootstrap80.php';
}

if (!function_exists('clamp')) {
    /**
     * @template V
     * @template L
     * @template H
     *
     * @param V $value
     * @param L $min
     * @param H $max
     *
     * @return V|L|H
     */
    function clamp($value, $min, $max) { return p\Php86::clamp($value, $min, $max); }
}

if (extension_loaded('intl') && !function_exists('grapheme_strrev')) {
    function grapheme_strrev(string $string) { return p\Php86::grapheme_strrev($string); }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use Symfony\Polyfill\Php86 as p;

if (\PHP_VERSION_ID >= 80600) {
    return;
}

if (!function_exists('clamp')) {
    /**
     * @template V
     * @template L
     * @template H
     *
     * @param V $value
     * @param L $min
     * @param H $max
     *
     * @return V|L|H
     */
    function clamp(mixed $value, mixed $min, mixed $max): mixed { return p\Php86::clamp($value, $min, $max); }
}

if (extension_loaded('intl') && !function_exists('grapheme_strrev')) {
    function grapheme_strrev(string $string): string|false { return p\Php86::grapheme_strrev($string); }
}
{
    "name": "symfony/polyfill-php86",
    "type": "library",
    "description": "Symfony polyfill backporting some PHP 8.6+ features to lower PHP versions",
    "keywords": ["polyfill", "shim", "compatibility", "portable"],
    "homepage": "https://symfony.com",
    "license": "MIT",
    "authors": [
        {
            "name": "Nicolas Grekas",
            "email": "p@tchwork.com"
        },
        {
            "name": "Symfony Community",
            "homepage": "https://symfony.com/contributors"
        }
    ],
    "require": {
        "php": ">=7.2"
    },
    "autoload": {
        "psr-4": { "Symfony\\Polyfill\\Php86\\": "" },
        "files": [ "bootstrap.php" ],
        "classmap": [ "Resources/stubs" ]
    },
    "minimum-stability": "dev",
    "extra": {
        "thanks": {
            "name": "symfony/polyfill",
            "url": "https://github.com/symfony/polyfill"
        }
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

if (\PHP_VERSION_ID < 80600 && \PHP_VERSION_ID >= 80100) {
    enum SortDirection
    {
        case Ascending;
        case Descending;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use Symfony\Polyfill\Php84 as p;

if (\PHP_VERSION_ID >= 80400) {
    return;
}

if (extension_loaded('intl') && !function_exists('grapheme_str_split')) {
    function grapheme_str_split(string $string, int $length = 1): array|false { return p\Php84::grapheme_str_split($string, $length); }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use Symfony\Polyfill\Php84 as p;

if (\PHP_VERSION_ID >= 80400) {
    return;
}

if (\extension_loaded('curl')) {
    // CURL_VERSION_HTTP3 is defined by PHP 8.2+ when libcurl >= 7.66.0
    if (defined('CURL_VERSION_HTTP3') || \PHP_VERSION_ID < 80200 && curl_version()['version_number'] >= 0x074200) {
        if (!defined('CURL_HTTP_VERSION_3')) {
            define('CURL_HTTP_VERSION_3', 30);
        }

        // CURL_HTTP_VERSION_3ONLY requires libcurl >= 7.88.0 and is not gated by any PHP-defined constant before 8.4
        if (!defined('CURL_HTTP_VERSION_3ONLY') && curl_version()['version_number'] >= 0x075800) {
            define('CURL_HTTP_VERSION_3ONLY', 31);
        }
    }
}

if (!function_exists('array_find')) {
    function array_find(array $array, callable $callback) { return p\Php84::array_find($array, $callback); }
}

if (!function_exists('array_find_key')) {
    function array_find_key(array $array, callable $callback) { return p\Php84::array_find_key($array, $callback); }
}

if (!function_exists('array_any')) {
    function array_any(array $array, callable $callback): bool { return p\Php84::array_any($array, $callback); }
}

if (!function_exists('array_all')) {
    function array_all(array $array, callable $callback): bool { return p\Php84::array_all($array, $callback); }
}

if (!function_exists('fpow')) {
    function fpow(float $num, float $exponent): float { return p\Php84::fpow($num, $exponent); }
}

if (\PHP_VERSION_ID < 80000) {
    require __DIR__.'/bootstrap72.php';
}

if (extension_loaded('mbstring')) {
    if (!function_exists('mb_ucfirst')) {
        function mb_ucfirst(?string $string, ?string $encoding = null): string { return p\Php84::mb_ucfirst((string) $string, $encoding); }
    }

    if (!function_exists('mb_lcfirst')) {
        function mb_lcfirst(?string $string, ?string $encoding = null): string { return p\Php84::mb_lcfirst((string) $string, $encoding); }
    }

    if (!function_exists('mb_trim')) {
        function mb_trim(?string $string, ?string $characters = null, ?string $encoding = null): string { return p\Php84::mb_trim((string) $string, $characters, $encoding); }
    }

    if (!function_exists('mb_ltrim')) {
        function mb_ltrim(?string $string, ?string $characters = null, ?string $encoding = null): string { return p\Php84::mb_ltrim((string) $string, $characters, $encoding); }
    }

    if (!function_exists('mb_rtrim')) {
        function mb_rtrim(?string $string, ?string $characters = null, ?string $encoding = null): string { return p\Php84::mb_rtrim((string) $string, $characters, $encoding); }
    }
}

if (extension_loaded('bcmath')) {
    if (!function_exists('bcceil')) {
        function bcceil(string $num): string { return p\Php84::bcceil($num); }
    }
    if (!function_exists('bcdivmod')) {
        function bcdivmod(string $num1, string $num2, ?int $scale = null): ?array { return p\Php84::bcdivmod($num1, $num2, $scale); }
    }
    if (!function_exists('bcfloor')) {
        function bcfloor(string $num): string { return p\Php84::bcfloor($num); }
    }
    if (!function_exists('bcround')) {
        function bcround(string $num, int $precision = 0, $mode = \RoundingMode::HalfAwayFromZero): string { return p\Php84::bcround($num, $precision, $mode); }
    }
}

if (\PHP_VERSION_ID >= 80200) {
    return require __DIR__.'/bootstrap82.php';
}

if (extension_loaded('intl') && !function_exists('grapheme_str_split')) {
    function grapheme_str_split(string $string, int $length = 1) { return p\Php84::grapheme_str_split($string, $length); }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use Symfony\Polyfill\Php84 as p;

if (extension_loaded('mbstring')) {
    if (!function_exists('mb_ucfirst')) {
        /** @return string|false */
        function mb_ucfirst(?string $string, ?string $encoding = null) { return p\Php84::mb_ucfirst((string) $string, $encoding); }
    }

    if (!function_exists('mb_lcfirst')) {
        /** @return string|false */
        function mb_lcfirst(?string $string, ?string $encoding = null) { return p\Php84::mb_lcfirst((string) $string, $encoding); }
    }

    if (!function_exists('mb_trim')) {
        /** @return string|false */
        function mb_trim(?string $string, ?string $characters = null, ?string $encoding = null) { return p\Php84::mb_trim((string) $string, $characters, $encoding); }
    }

    if (!function_exists('mb_ltrim')) {
        /** @return string|false */
        function mb_ltrim(?string $string, ?string $characters = null, ?string $encoding = null) { return p\Php84::mb_ltrim((string) $string, $characters, $encoding); }
    }

    if (!function_exists('mb_rtrim')) {
        /** @return string|false */
        function mb_rtrim(?string $string, ?string $characters = null, ?string $encoding = null) { return p\Php84::mb_rtrim((string) $string, $characters, $encoding); }
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Polyfill\Php84;

/**
 * @author Ayesh Karunaratne <ayesh@aye.sh>
 * @author Pierre Ambroise <pierre27.ambroise@gmail.com>
 *
 * @internal
 */
final class Php84
{
    private const BCMATH_WELL_FORMED = '/^[+-]?[0-9]*+(?:\.[0-9]*+)?$/D';

    /** @return string|false */
    public static function mb_ucfirst(string $string, ?string $encoding = null)
    {
        if (null === $encoding) {
            $encoding = mb_internal_encoding();
        }

        try {
            $validEncoding = @mb_check_encoding('', $encoding);
        } catch (\ValueError $e) {
            throw new \ValueError(\sprintf('mb_ucfirst(): Argument #2 ($encoding) must be a valid encoding, "%s" given', $encoding));
        }

        if (!$validEncoding) {
            if (80000 > \PHP_VERSION_ID) {
                trigger_error(\sprintf('mb_ucfirst(): Argument #2 ($encoding) must be a valid encoding, "%s" given', $encoding), \E_USER_WARNING);

                return false;
            }

            throw new \ValueError(\sprintf('mb_ucfirst(): Argument #2 ($encoding) must be a valid encoding, "%s" given', $encoding));
        }

        $firstChar = mb_substr($string, 0, 1, $encoding);
        $firstChar = mb_convert_case($firstChar, \MB_CASE_TITLE, $encoding);

        return $firstChar.mb_substr($string, 1, null, $encoding);
    }

    /** @return string|false */
    public static function mb_lcfirst(string $string, ?string $encoding = null)
    {
        if (null === $encoding) {
            $encoding = mb_internal_encoding();
        }

        try {
            $validEncoding = @mb_check_encoding('', $encoding);
        } catch (\ValueError $e) {
            throw new \ValueError(\sprintf('mb_lcfirst(): Argument #2 ($encoding) must be a valid encoding, "%s" given', $encoding));
        }

        if (!$validEncoding) {
            if (80000 > \PHP_VERSION_ID) {
                trigger_error(\sprintf('mb_lcfirst(): Argument #2 ($encoding) must be a valid encoding, "%s" given', $encoding), \E_USER_WARNING);

                return false;
            }

            throw new \ValueError(\sprintf('mb_lcfirst(): Argument #2 ($encoding) must be a valid encoding, "%s" given', $encoding));
        }

        $firstChar = mb_substr($string, 0, 1, $encoding);
        $firstChar = mb_convert_case($firstChar, \MB_CASE_LOWER, $encoding);

        return $firstChar.mb_substr($string, 1, null, $encoding);
    }

    public static function array_find(array $array, callable $callback)
    {
        foreach ($array as $key => $value) {
            if ($callback($value, $key)) {
                return $value;
            }
        }

        return null;
    }

    public static function array_find_key(array $array, callable $callback)
    {
        foreach ($array as $key => $value) {
            if ($callback($value, $key)) {
                return $key;
            }
        }

        return null;
    }

    public static function array_any(array $array, callable $callback): bool
    {
        foreach ($array as $key => $value) {
            if ($callback($value, $key)) {
                return true;
            }
        }

        return false;
    }

    public static function array_all(array $array, callable $callback): bool
    {
        foreach ($array as $key => $value) {
            if (!$callback($value, $key)) {
                return false;
            }
        }

        return true;
    }

    public static function fpow(float $num, float $exponent): float
    {
        if (0.0 === $num && $exponent < 0.0) {
            // IEEE 754 pow(Â±0, y < 0), which the ** operator deprecates since PHP 8.4
            return -1.0 === fmod($exponent, 2.0) && '-' === ((string) $num)[0] ? -\INF : \INF;
        }

        return $num ** $exponent;
    }

    /** @return string|false */
    public static function mb_trim(string $string, ?string $characters = null, ?string $encoding = null)
    {
        return self::mb_internal_trim('{^[%s]+|[%1$s]+$}Du', $string, $characters, $encoding, __FUNCTION__);
    }

    /** @return string|false */
    public static function mb_ltrim(string $string, ?string $characters = null, ?string $encoding = null)
    {
        return self::mb_internal_trim('{^[%s]+}Du', $string, $characters, $encoding, __FUNCTION__);
    }

    /** @return string|false */
    public static function mb_rtrim(string $string, ?string $characters = null, ?string $encoding = null)
    {
        return self::mb_internal_trim('{[%s]+$}Du', $string, $characters, $encoding, __FUNCTION__);
    }

    /** @return string|false */
    private static function mb_internal_trim(string $regex, string $string, ?string $characters, ?string $encoding, string $function)
    {
        if (null === $encoding) {
            $encoding = mb_internal_encoding();
        }

        try {
            $validEncoding = @mb_check_encoding('', $encoding);
        } catch (\ValueError $e) {
            throw new \ValueError(\sprintf('%s(): Argument #3 ($encoding) must be a valid encoding, "%s" given', $function, $encoding));
        }

        if (!$validEncoding) {
            if (80000 > \PHP_VERSION_ID) {
                trigger_error(\sprintf('%s(): Argument #3 ($encoding) must be a valid encoding, "%s" given', $function, $encoding), \E_USER_WARNING);

                return false;
            }

            throw new \ValueError(\sprintf('%s(): Argument #3 ($encoding) must be a valid encoding, "%s" given', $function, $encoding));
        }

        if ('' === $characters) {
            return $string;
        }

        if ('UTF-8' === $encoding || \in_array(strtolower($encoding), ['utf-8', 'utf8'], true)) {
            $encoding = 'UTF-8';
        }

        if ('UTF-8' === $encoding && (!preg_match('//u', $string) || (null !== $characters && !preg_match('//u', $characters)))) {
            return self::mb_trim_invalid_utf8($string, $characters, $function);
        }

        $string = mb_convert_encoding($string, 'UTF-8', $encoding);

        if (null !== $characters) {
            $characters = mb_convert_encoding($characters, 'UTF-8', $encoding);
        }

        if (null === $characters) {
            $characters = "\\0 \f\n\r\t\v\u{00A0}\u{1680}\u{2000}\u{2001}\u{2002}\u{2003}\u{2004}\u{2005}\u{2006}\u{2007}\u{2008}\u{2009}\u{200A}\u{2028}\u{2029}\u{202F}\u{205F}\u{3000}\u{0085}\u{180E}";
        } else {
            $characters = preg_quote($characters);
        }

        $string = preg_replace(\sprintf($regex, $characters), '', $string);

        if ('UTF-8' === $encoding) {
            return $string;
        }

        return mb_convert_encoding($string, $encoding, 'UTF-8');
    }

    /**
     * Trims ill-formed UTF-8 like mbstring: each maximal subpart of an ill-formed
     * sequence counts as one character, and the string is returned as is when
     * nothing is trimmed, else re-encoded with the substitute character.
     */
    private static function mb_trim_invalid_utf8(string $string, ?string $characters, string $function): string
    {
        // Well-formed characters in group 1, then the maximal subparts of ill-formed sequences
        $regex = '/([\x00-\x7F]|[\xC2-\xDF][\x80-\xBF]|\xE0[\xA0-\xBF][\x80-\xBF]|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}|\xED[\x80-\x9F][\x80-\xBF]|\xF0[\x90-\xBF][\x80-\xBF]{2}|[\xF1-\xF3][\x80-\xBF]{3}|\xF4[\x80-\x8F][\x80-\xBF]{2})|\xE0[\xA0-\xBF]|[\xE1-\xEC\xEE\xEF][\x80-\xBF]|\xED[\x80-\x9F]|\xF0[\x90-\xBF][\x80-\xBF]?|[\xF1-\xF3][\x80-\xBF]{1,2}|\xF4[\x80-\x8F][\x80-\xBF]?|[\x80-\xFF]/';

        // Ill-formed sequences leave group 1 empty: they are trimmed when $characters has one too
        preg_match_all($regex, $characters ?? "\0 \f\n\r\t\v\u{00A0}\u{1680}\u{2000}\u{2001}\u{2002}\u{2003}\u{2004}\u{2005}\u{2006}\u{2007}\u{2008}\u{2009}\u{200A}\u{2028}\u{2029}\u{202F}\u{205F}\u{3000}\u{0085}\u{180E}", $m);
        $trimmed = array_flip($m[1]);

        $start = 0;
        $end = \strlen($string);

        while ('mb_rtrim' !== $function && $start < $end && preg_match($regex, $string, $m, 0, $start) && isset($trimmed[$m[1] ?? ''])) {
            $start += \strlen($m[0]);
        }

        while ('mb_ltrim' !== $function && $start < $end) {
            // Characters are at most 4 bytes long, and only continuation bytes don't start one
            for ($i = $end - 1; $i > $start && $i > $end - 4 && 0x80 === (\ord($string[$i]) & 0xC0); --$i) {
            }

            preg_match_all($regex, substr($string, $i, $end - $i), $m);

            if (!isset($trimmed[end($m[1])])) {
                break;
            }

            $end -= \strlen(end($m[0]));
        }

        if (0 === $start && \strlen($string) === $end) {
            return $string;
        }

        $string = substr($string, $start, $end - $start);

        return 'none' === mb_substitute_character() ? preg_replace($regex, '$1', $string) : mb_convert_encoding($string, 'UTF-8', 'UTF-8');
    }

    public static function grapheme_str_split(string $string, int $length)
    {
        if (0 >= $length || 1073741823 < $length) {
            throw new \ValueError('grapheme_str_split(): Argument #2 ($length) must be greater than 0 and less than or equal to 1073741823');
        }

        if ('' === $string) {
            return [];
        }

        $regex = ((float) \PCRE_VERSION >= 10.44)
            ? '\X'
            : '(?:\r\n|[\x{1F1E6}-\x{1F1FF}][\x{1F1E6}-\x{1F1FF}]?|(?:[ -~\x{200C}\x{200D}]|[á†¨-á‡¹]+|[á„€-á…Ÿ]*(?:[ê°€ê°œê°¸ê±”ê±°ê²Œê²¨ê³„ê³ ê³¼ê´˜ê´´êµ�êµ¬ê¶ˆê¶¤ê·€ê·œê·¸ê¸”ê¸°ê¹Œê¹¨êº„êº êº¼ê»˜ê»´ê¼�ê¼¬ê½ˆê½¤ê¾€ê¾œê¾¸ê¿”ê¿°ë€Œë€¨ë�„ë� ë�¼ë‚˜ë‚´ëƒ�ëƒ¬ë„ˆë„¤ë…€ë…œë…¸ë†”ë†°ë‡Œë‡¨ëˆ„ëˆ ëˆ¼ë‰˜ë‰´ëŠ�ëŠ¬ë‹ˆë‹¤ëŒ€ëŒœëŒ¸ë�”ë�°ëŽŒëŽ¨ë�„ë� ë�¼ë�˜ë�´ë‘�ë‘¬ë’ˆë’¤ë“€ë“œë“¸ë””ë”°ë•Œë•¨ë–„ë– ë–¼ë—˜ë—´ë˜�ë˜¬ë™ˆë™¤ëš€ëšœëš¸ë›”ë›°ëœŒëœ¨ë�„ë� ë�¼ëž˜ëž´ëŸ�ëŸ¬ë ˆë ¤ë¡€ë¡œë¡¸ë¢”ë¢°ë£Œë£¨ë¤„ë¤ ë¤¼ë¥˜ë¥´ë¦�ë¦¬ë§ˆë§¤ë¨€ë¨œë¨¸ë©”ë©°ëªŒëª¨ë«„ë« ë«¼ë¬˜ë¬´ë­�ë­¬ë®ˆë®¤ë¯€ë¯œë¯¸ë°”ë°°ë±Œë±¨ë²„ë² ë²¼ë³˜ë³´ë´�ë´¬ëµˆëµ¤ë¶€ë¶œë¶¸ë·”ë·°ë¸Œë¸¨ë¹„ë¹ ë¹¼ëº˜ëº´ë»�ë»¬ë¼ˆë¼¤ë½€ë½œë½¸ë¾”ë¾°ë¿Œë¿¨ì€„ì€ ì€¼ì�˜ì�´ì‚�ì‚¬ìƒˆìƒ¤ì„€ì„œì„¸ì…”ì…°ì†Œì†¨ì‡„ì‡ ì‡¼ìˆ˜ìˆ´ì‰�ì‰¬ìŠˆìŠ¤ì‹€ì‹œì‹¸ìŒ”ìŒ°ì�Œì�¨ìŽ„ìŽ ìŽ¼ì�˜ì�´ì��ì�¬ì‘ˆì‘¤ì’€ì’œì’¸ì“”ì“°ì”Œì”¨ì•„ì• ì•¼ì–˜ì–´ì—�ì—¬ì˜ˆì˜¤ì™€ì™œì™¸ìš”ìš°ì›Œì›¨ìœ„ìœ ìœ¼ì�˜ì�´ìž�ìž¬ìŸˆìŸ¤ì €ì œì ¸ì¡”ì¡°ì¢Œì¢¨ì£„ì£ ì£¼ì¤˜ì¤´ì¥�ì¥¬ì¦ˆì¦¤ì§€ì§œì§¸ì¨”ì¨°ì©Œì©¨ìª„ìª ìª¼ì«˜ì«´ì¬�ì¬¬ì­ˆì­¤ì®€ì®œì®¸ì¯”ì¯°ì°Œì°¨ì±„ì± ì±¼ì²˜ì²´ì³�ì³¬ì´ˆì´¤ìµ€ìµœìµ¸ì¶”ì¶°ì·Œì·¨ì¸„ì¸ ì¸¼ì¹˜ì¹´ìº�ìº¬ì»ˆì»¤ì¼€ì¼œì¼¸ì½”ì½°ì¾Œì¾¨ì¿„ì¿ ì¿¼í€˜í€´í��í�¬í‚ˆí‚¤íƒ€íƒœíƒ¸í„”í„°í…Œí…¨í†„í† í†¼í‡˜í‡´íˆ�íˆ¬í‰ˆí‰¤íŠ€íŠœíŠ¸í‹”í‹°íŒŒíŒ¨í�„í� í�¼íŽ˜íŽ´í��í�¬í�ˆí�¤í‘€í‘œí‘¸í’”í’°í“Œí“¨í”„í” í”¼í•˜í•´í–�í–¬í—ˆí—¤í˜€í˜œí˜¸í™”í™°íšŒíš¨í›„í› í›¼íœ˜íœ´í��í�¬ížˆ]?[á… -á†¢]+|[ê°€-íž£])[á†¨-á‡¹]*|[á„€-á…Ÿ]+|[^\p{Cc}\p{Cf}\p{Zl}\p{Zp}])[\p{Mn}\p{Mc}\p{Me}\x{09BE}\x{09D7}\x{0B3E}\x{0B57}\x{0BBE}\x{0BD7}\x{0CC2}\x{0CD5}\x{0CD6}\x{0D3E}\x{0D57}\x{0DCF}\x{0DDF}\x{200C}\x{1D165}\x{1D16E}-\x{1D172}\x{1F3FB}-\x{1F3FF}\x{FE0E}-\x{FE0F}\x{E0020}-\x{E007F}]*(?:\x{200D}(?:[\x{1F1E6}-\x{1F1FF}]|[ -~\x{200C}\x{200D}]|[á†¨-á‡¹]+|[á„€-á…Ÿ]*(?:[ê°€-íž£]?[á… -á†¢]+|[ê°€-íž£])[á†¨-á‡¹]*|[á„€-á…Ÿ]+|[^\p{Cc}\p{Cf}\p{Zl}\p{Zp}])[\p{Mn}\p{Mc}\p{Me}\x{09BE}\x{09D7}\x{0B3E}\x{0B57}\x{0BBE}\x{0BD7}\x{0CC2}\x{0CD5}\x{0CD6}\x{0D3E}\x{0D57}\x{0DCF}\x{0DDF}\x{200C}\x{1D165}\x{1D16E}-\x{1D172}\x{1F3FB}-\x{1F3FF}\x{FE0E}-\x{FE0F}\x{E0020}-\x{E007F}]*)*|[\p{Cc}\p{Cf}\p{Zl}\p{Zp}])';

        if (preg_match_all('/'.$regex.'/u', $string, $matches)) {
            $graphemes = $matches[0];
        } else {
            $graphemes = self::splitIllFormed($string, $regex);
        }

        if (!$graphemes) {
            return false;
        }

        if (1 === $length) {
            return $graphemes;
        }

        $chunks = array_chunk($graphemes, $length);
        foreach ($chunks as &$chunk) {
            $chunk = implode('', $chunk);
        }

        return $chunks;
    }

    public static function bcceil(string $num): string
    {
        if (!preg_match(self::BCMATH_WELL_FORMED, $num)) {
            throw new \ValueError('bcceil(): Argument #1 ($num) is not well-formed');
        }

        return self::bcround($num, 0, \RoundingMode::PositiveInfinity);
    }

    public static function bcdivmod(string $num1, string $num2, ?int $scale = null): ?array
    {
        if (null === $quot = @bcdiv($num1, $num2, 0)) {
            throw new \DivisionByZeroError('Division by zero');
        }
        $scale = $scale ?? (\PHP_VERSION_ID >= 70300 ? bcscale() : (\ini_get('bcmath.scale') ?: 0));

        return [$quot, bcmod($num1, $num2, $scale)];
    }

    public static function bcfloor(string $num): string
    {
        if (!preg_match(self::BCMATH_WELL_FORMED, $num)) {
            throw new \ValueError('bcfloor(): Argument #1 ($num) is not well-formed');
        }

        return self::bcround($num, 0, \RoundingMode::NegativeInfinity);
    }

    /**
     * @param \RoundingMode|\RoundingMode::* $mode
     */
    public static function bcround(string $num, int $precision = 0, $mode = \RoundingMode::HalfAwayFromZero): string
    {
        if (!preg_match(self::BCMATH_WELL_FORMED, $num)) {
            throw new \ValueError('bcround(): Argument #1 ($num) is not well-formed');
        }

        $sign = 1;
        if ('' !== $num && ('-' === $num[0] || '+' === $num[0])) {
            if ('-' === $num[0]) {
                $sign = -1;
            }

            $num = substr($num, 1);
        }

        if (false !== strpos($num, '.')) {
            [$intPart, $fracPart] = array_pad(explode('.', $num, 2), 2, '');
        } else {
            $intPart = $num;
            $fracPart = '';
        }

        if ('' === $intPart) {
            $intPart = '0';
        }

        $intPart = self::trimLeadingZeros($intPart);
        $fracPart = (string) $fracPart;

        if ($precision >= 0) {
            $fracLength = \strlen($fracPart);

            if ($precision <= $fracLength) {
                $scaledInt = $intPart.(string) substr($fracPart, 0, $precision);
                $scaledFrac = (string) substr($fracPart, $precision);
            } else {
                $scaledInt = $intPart.$fracPart.str_repeat('0', $precision - $fracLength);
                $scaledFrac = '';
            }
        } else {
            $shift = -$precision;
            $intLength = \strlen($intPart);

            if ($shift <= $intLength) {
                $splitPos = $intLength - $shift;
                $scaledInt = substr($intPart, 0, $splitPos);
                $scaledInt = '' === $scaledInt ? '0' : $scaledInt;
                $scaledFrac = substr($intPart, $splitPos).$fracPart;
            } else {
                $scaledInt = '0';
                $scaledFrac = str_repeat('0', $shift - $intLength).$intPart.$fracPart;
            }
        }

        $roundedInt = self::roundIntegerPart($scaledInt, $scaledFrac, $sign, $mode);
        $isZero = '' === trim($roundedInt, '0');
        $absResult = self::formatRoundedDigits($roundedInt, $precision);

        if (-1 === $sign && !$isZero) {
            $absResult = '-'.$absResult;
        }

        return $absResult;
    }

    private static function roundIntegerPart(string $intPart, string $fracPart, int $sign, $mode): string
    {
        $intPart = self::trimLeadingZeros($intPart);

        if ('' === $fracPart || '' === trim($fracPart, '0')) {
            return $intPart;
        }

        $firstDigit = $fracPart[0];
        $tail = (string) substr($fracPart, 1);
        $tailNonZero = '' !== trim($tail, '0');
        $isGreaterThanHalf = $firstDigit > '5' || ('5' === $firstDigit && $tailNonZero);
        $isExactlyHalf = '5' === $firstDigit && !$tailNonZero;
        $shouldIncrease = false;

        switch ($mode) {
            case \RoundingMode::TowardsZero:
                break;

            case \RoundingMode::AwayFromZero:
                $shouldIncrease = true;
                break;

            case \RoundingMode::PositiveInfinity:
                $shouldIncrease = $sign > 0;
                break;

            case \RoundingMode::NegativeInfinity:
                $shouldIncrease = $sign < 0;
                break;

            case \RoundingMode::HalfAwayFromZero:
                $shouldIncrease = $isGreaterThanHalf || $isExactlyHalf;
                break;

            case \RoundingMode::HalfTowardsZero:
                $shouldIncrease = $isGreaterThanHalf;
                break;

            case \RoundingMode::HalfEven:
                if ($isGreaterThanHalf) {
                    $shouldIncrease = true;
                } elseif ($isExactlyHalf && 1 === self::lastDigit($intPart) % 2) {
                    $shouldIncrease = true;
                }
                break;

            case \RoundingMode::HalfOdd:
                if ($isGreaterThanHalf) {
                    $shouldIncrease = true;
                } elseif ($isExactlyHalf && 0 === self::lastDigit($intPart) % 2) {
                    $shouldIncrease = true;
                }
                break;
        }

        if ($shouldIncrease) {
            $intPart = self::incrementDigits($intPart);
        }

        return self::trimLeadingZeros($intPart);
    }

    private static function formatRoundedDigits(string $roundedInt, int $precision): string
    {
        if ($precision > 0) {
            if (\strlen($roundedInt) <= $precision) {
                $roundedInt = str_pad($roundedInt, $precision + 1, '0', \STR_PAD_LEFT);
            }

            $intDigits = substr($roundedInt, 0, -$precision);
            $fracDigits = substr($roundedInt, -$precision);

            $intDigits = self::trimLeadingZeros('' === $intDigits ? '0' : $intDigits);
            $fracDigits = str_pad($fracDigits, $precision, '0', \STR_PAD_LEFT);

            return $intDigits.'.'.$fracDigits;
        }

        if (0 === $precision) {
            return self::trimLeadingZeros($roundedInt);
        }

        $shift = -$precision;
        $digits = $roundedInt.str_repeat('0', $shift);

        return self::trimLeadingZeros($digits);
    }

    private static function incrementDigits(string $digits): string
    {
        $digits = '' === $digits ? '0' : $digits;
        $index = \strlen($digits) - 1;
        $result = $digits;
        $carry = 1;

        while ($index >= 0 && $carry) {
            $value = \ord($result[$index]) - 48 + $carry;
            $carry = $value >= 10 ? 1 : 0;
            $result[$index] = \chr(48 + ($value % 10));
            --$index;
        }

        return $carry ? '1'.$result : $result;
    }

    private static function trimLeadingZeros(string $digits): string
    {
        $digits = ltrim($digits, '0');

        return '' === $digits ? '0' : $digits;
    }

    private static function lastDigit(string $digits): int
    {
        $length = \strlen($digits);

        return $length ? \ord($digits[$length - 1]) - 48 : 0;
    }

    /**
     * Splits a string that is not valid UTF-8 into grapheme clusters.
     *
     * The intl extension keeps the ill-formed bytes and breaks around them the
     * way it would around the U+FFFD each maximal subpart stands for. Standing
     * one in for real makes the cluster regexp applicable, then each cluster is
     * mapped back to the bytes it was made of.
     */
    private static function splitIllFormed(string $string, string $regex): array
    {
        // Table 3-7 of the Unicode standard for the well-formed byte sequences (group 1),
        // then the maximal subparts an ill-formed sequence is made of (group 2).
        preg_match_all('/([\x00-\x7F]|[\xC2-\xDF][\x80-\xBF]|\xE0[\xA0-\xBF][\x80-\xBF]|[\xE1-\xEC][\x80-\xBF]{2}|\xED[\x80-\x9F][\x80-\xBF]|[\xEE-\xEF][\x80-\xBF]{2}|\xF0[\x90-\xBF][\x80-\xBF]{2}|[\xF1-\xF3][\x80-\xBF]{3}|\xF4[\x80-\x8F][\x80-\xBF]{2})|(\xF0[\x90-\xBF][\x80-\xBF]?|[\xF1-\xF3][\x80-\xBF]{1,2}|\xF4[\x80-\x8F][\x80-\xBF]?|\xE0[\xA0-\xBF]|[\xE1-\xEC][\x80-\xBF]|\xED[\x80-\x9F]|[\xEE-\xEF][\x80-\xBF]|[\x80-\xFF])/s', $string, $matches, \PREG_SET_ORDER);

        $segments = [];
        $segmentAt = [];
        $sanitized = '';

        foreach ($matches as $m) {
            $segmentAt[\strlen($sanitized)] = \count($segments);

            if (isset($m[2]) && '' !== $m[2]) {
                $segments[] = $m[2];
                $sanitized .= "\xEF\xBF\xBD";
            } else {
                $segments[] = $m[1];
                $sanitized .= $m[1];
            }
        }

        $segmentAt[\strlen($sanitized)] = \count($segments);

        preg_match_all('/'.$regex.'/u', $sanitized, $clusters, \PREG_OFFSET_CAPTURE);

        $graphemes = [];

        foreach ($clusters[0] as $cluster) {
            $start = $segmentAt[$cluster[1]];
            $graphemes[] = implode('', \array_slice($segments, $start, $segmentAt[$cluster[1] + \strlen($cluster[0])] - $start));
        }

        return $graphemes;
    }
}
{
    "name": "symfony/polyfill-php84",
    "type": "library",
    "description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions",
    "keywords": ["polyfill", "shim", "compatibility", "portable"],
    "homepage": "https://symfony.com",
    "license": "MIT",
    "authors": [
        {
            "name": "Nicolas Grekas",
            "email": "p@tchwork.com"
        },
        {
            "name": "Symfony Community",
            "homepage": "https://symfony.com/contributors"
        }
    ],
    "require": {
        "php": ">=7.2"
    },
    "autoload": {
        "psr-4": { "Symfony\\Polyfill\\Php84\\": "" },
        "files": [ "bootstrap.php" ],
        "classmap": [ "Resources/stubs" ]
    },
    "minimum-stability": "dev",
    "extra": {
        "thanks": {
            "name": "symfony/polyfill",
            "url": "https://github.com/symfony/polyfill"
        }
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

if (\PHP_VERSION_ID < 80400) {
    // @author Daniel Scherzer <daniel.e.scherzer@gmail.com>
    final class ReflectionConstant
    {
        /**
         * @var string
         *
         * @readonly
         */
        public $name;

        private $value;
        private $deprecated;
        private $persistent;

        private static $persistentConstants = [];

        public function __construct(string $name)
        {
            if (!defined($name) || false !== strpos($name, '::')) {
                throw new ReflectionException("Constant \"$name\" does not exist");
            }

            $this->name = ltrim($name, '\\');
            $deprecated = false;
            $eh = set_error_handler(static function ($type, $msg, $file, $line) use ($name, &$deprecated, &$eh) {
                if (\E_DEPRECATED === $type && "Constant $name is deprecated" === $msg) {
                    return $deprecated = true;
                }

                return $eh && $eh($type, $msg, $file, $line);
            });

            try {
                $this->value = constant($name);
                $this->deprecated = $deprecated;
            } finally {
                restore_error_handler();
            }
        }

        public function getName(): string
        {
            return $this->name;
        }

        public function getValue()
        {
            return $this->value;
        }

        public function getNamespaceName(): string
        {
            if (false === $slashPos = strrpos($this->name, '\\')) {
                return '';
            }

            return substr($this->name, 0, $slashPos);
        }

        public function getShortName(): string
        {
            if (false === $slashPos = strrpos($this->name, '\\')) {
                return $this->name;
            }

            return substr($this->name, $slashPos + 1);
        }

        public function isDeprecated(): bool
        {
            return $this->deprecated;
        }

        public function __toString(): string
        {
            // A constant is persistent if provided by PHP itself rather than
            // being defined by users. If we got here, we know that it *is*
            // defined, so we just need to figure out if it is defined by the
            // user or not
            if (!self::$persistentConstants) {
                $persistentConstants = get_defined_constants(true);
                unset($persistentConstants['user']);
                foreach ($persistentConstants as $constants) {
                    self::$persistentConstants += $constants;
                }
            }
            $persistent = array_key_exists($this->name, self::$persistentConstants);

            // Can't match the inclusion of `no_file_cache` but the rest is
            // possible to match
            $result = 'Constant [ ';
            if ($persistent || $this->deprecated) {
                $result .= '<';
                if ($persistent) {
                    $result .= 'persistent';
                    if ($this->deprecated) {
                        $result .= ', ';
                    }
                }
                if ($this->deprecated) {
                    $result .= 'deprecated';
                }
                $result .= '> ';
            }
            // Cannot just use gettype() to match zend_zval_type_name()
            if (is_object($this->value)) {
                $result .= \PHP_VERSION_ID >= 80000 ? get_debug_type($this->value) : gettype($this->value);
            } elseif (is_bool($this->value)) {
                $result .= 'bool';
            } elseif (is_int($this->value)) {
                $result .= 'int';
            } elseif (is_float($this->value)) {
                $result .= 'float';
            } elseif (null === $this->value) {
                $result .= 'null';
            } else {
                $result .= gettype($this->value);
            }
            $result .= ' ';
            $result .= $this->name;
            $result .= ' ] { ';
            if (is_array($this->value)) {
                $result .= 'Array';
            } else {
                // This will throw an exception if the value is an object that
                // cannot be converted to string; that is expected and matches
                // the behavior of zval_get_string_func()
                $result .= (string) $this->value;
            }
            $result .= " }\n";

            return $result;
        }

        public function __sleep(): array
        {
            throw new Exception("Serialization of 'ReflectionConstant' is not allowed");
        }

        public function __wakeup(): void
        {
            throw new Exception("Unserialization of 'ReflectionConstant' is not allowed");
        }
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

if (\PHP_VERSION_ID < 80100) {
    final class RoundingMode
    {
        const HalfAwayFromZero = 0;
        const HalfTowardsZero = 1;
        const HalfEven = 2;
        const HalfOdd = 3;
        const TowardsZero = 4;
        const AwayFromZero = 5;
        const NegativeInfinity = 6;
        const PositiveInfinity = 7;

        private function __construct()
        {
        }

        public static function cases(): array
        {
            return [
                self::HalfAwayFromZero,
                self::HalfTowardsZero,
                self::HalfEven,
                self::HalfOdd,
                self::TowardsZero,
                self::AwayFromZero,
                self::NegativeInfinity,
                self::PositiveInfinity,
            ];
        }
    }
} elseif (\PHP_VERSION_ID < 80400) {
    require dirname(__DIR__).'/RoundingMode.php';
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Pdo;

if (\PHP_VERSION_ID < 80400 && \extension_loaded('pdo_sqlite')) {
    class Sqlite extends \PDO
    {
        public const ATTR_EXTENDED_RESULT_CODES = \PHP_VERSION_ID >= 70400 ? \PDO::SQLITE_ATTR_EXTENDED_RESULT_CODES : 1002;
        public const ATTR_OPEN_FLAGS = \PHP_VERSION_ID >= 70300 ? \PDO::SQLITE_ATTR_OPEN_FLAGS : 1000;
        public const ATTR_READONLY_STATEMENT = \PHP_VERSION_ID >= 70400 ? \PDO::SQLITE_ATTR_READONLY_STATEMENT : 1001;
        public const DETERMINISTIC = \PDO::SQLITE_DETERMINISTIC;
        public const OPEN_READONLY = \PHP_VERSION_ID >= 70300 ? \PDO::SQLITE_OPEN_READONLY : 1;
        public const OPEN_READWRITE = \PHP_VERSION_ID >= 70300 ? \PDO::SQLITE_OPEN_READWRITE : 2;
        public const OPEN_CREATE = \PHP_VERSION_ID >= 70300 ? \PDO::SQLITE_OPEN_CREATE : 4;

        public function __construct(string $dsn, ?string $username = null, ?string $password = null, ?array $options = null)
        {
            parent::__construct($dsn, $username, $password, $options);

            if ('sqlite' !== $driver = $this->getAttribute(\PDO::ATTR_DRIVER_NAME)) {
                throw new \PDOException(\sprintf('Pdo\Sqlite::__construct() cannot be used for connecting to the "%s" driver', $driver));
            }
        }

        public static function connect(string $dsn, ?string $username = null, ?string $password = null, ?array $options = null): self
        {
            try {
                return new self($dsn, $username, $password, $options);
            } catch (\PDOException $e) {
                throw preg_match('/^Pdo\\\\Sqlite::__construct\(\) cannot be used for connecting to the "([a-z]+)" driver/', $e->getMessage(), $matches) ? new \PDOException(\sprintf('Pdo\Sqlite::connect() cannot be used for connecting to the "%s" driver', $matches[1])) : $e;
            }
        }

        public function createAggregate(string $name, callable $step, callable $finalize, int $numArgs = -1): bool
        {
            return $this->sqliteCreateAggregate($name, $step, $finalize, $numArgs);
        }

        public function createCollation(string $name, callable $callback): bool
        {
            return $this->sqliteCreateCollation($name, $callback);
        }

        public function createFunction(string $function_name, callable $callback, int $num_args = -1, int $flags = 0): bool
        {
            return $this->sqliteCreateFunction($function_name, $callback, $num_args, $flags);
        }
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Pdo;

if (\PHP_VERSION_ID < 80400 && \extension_loaded('pdo_odbc')) {
    class Odbc extends \PDO
    {
        public const ATTR_USE_CURSOR_LIBRARY = \PDO::ODBC_ATTR_USE_CURSOR_LIBRARY;
        public const ATTR_ASSUME_UTF8 = \PDO::ODBC_ATTR_ASSUME_UTF8;
        public const SQL_USE_IF_NEEDED = \PDO::ODBC_SQL_USE_IF_NEEDED;
        public const SQL_USE_DRIVER = \PDO::ODBC_SQL_USE_DRIVER;
        public const SQL_USE_ODBC = \PDO::ODBC_SQL_USE_ODBC;

        public function __construct(string $dsn, ?string $username = null, ?string $password = null, ?array $options = null)
        {
            parent::__construct($dsn, $username, $password, $options);

            if ('odbc' !== $driver = $this->getAttribute(\PDO::ATTR_DRIVER_NAME)) {
                throw new \PDOException(\sprintf('Pdo\Odbc::__construct() cannot be used for connecting to the "%s" driver', $driver));
            }
        }

        public static function connect(string $dsn, ?string $username = null, ?string $password = null, ?array $options = null): self
        {
            try {
                return new self($dsn, $username, $password, $options);
            } catch (\PDOException $e) {
                throw preg_match('/^Pdo\\\\Odbc::__construct\(\) cannot be used for connecting to the "([a-z]+)" driver/', $e->getMessage(), $matches) ? new \PDOException(\sprintf('Pdo\Odbc::connect() cannot be used for connecting to the "%s" driver', $matches[1])) : $e;
            }
        }
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Pdo;

if (\PHP_VERSION_ID < 80400 && \extension_loaded('pdo_firebird')) {
    class Firebird extends \PDO
    {
        public const ATTR_DATE_FORMAT = \PDO::FB_ATTR_DATE_FORMAT;
        public const ATTR_TIME_FORMAT = \PDO::FB_ATTR_TIME_FORMAT;
        public const ATTR_TIMESTAMP_FORMAT = \PDO::FB_ATTR_TIMESTAMP_FORMAT;

        public function __construct(string $dsn, ?string $username = null, ?string $password = null, ?array $options = null)
        {
            parent::__construct($dsn, $username, $password, $options);

            if ('firebird' !== $driver = $this->getAttribute(\PDO::ATTR_DRIVER_NAME)) {
                throw new \PDOException(\sprintf('Pdo\Firebird::__construct() cannot be used for connecting to the "%s" driver', $driver));
            }
        }

        public static function connect(string $dsn, ?string $username = null, ?string $password = null, ?array $options = null): self
        {
            try {
                return new self($dsn, $username, $password, $options);
            } catch (\PDOException $e) {
                throw preg_match('/^Pdo\\\\Firebird::__construct\(\) cannot be used for connecting to the "([a-z]+)" driver/', $e->getMessage(), $matches) ? new \PDOException(\sprintf('Pdo\Firebird::connect() cannot be used for connecting to the "%s" driver', $matches[1])) : $e;
            }
        }
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Pdo;

if (\PHP_VERSION_ID < 80400 && \extension_loaded('pdo_dblib')) {
    class Dblib extends \PDO
    {
        public const ATTR_CONNECTION_TIMEOUT = \PDO::DBLIB_ATTR_CONNECTION_TIMEOUT;
        public const ATTR_QUERY_TIMEOUT = \PDO::DBLIB_ATTR_QUERY_TIMEOUT;
        public const ATTR_STRINGIFY_UNIQUEIDENTIFIER = \PDO::DBLIB_ATTR_STRINGIFY_UNIQUEIDENTIFIER;
        public const ATTR_VERSION = \PDO::DBLIB_ATTR_VERSION;
        public const ATTR_TDS_VERSION = \PHP_VERSION_ID >= 70300 ? \PDO::DBLIB_ATTR_TDS_VERSION : 1004;
        public const ATTR_SKIP_EMPTY_ROWSETS = \PHP_VERSION_ID >= 70300 ? \PDO::DBLIB_ATTR_SKIP_EMPTY_ROWSETS : 1005;
        public const ATTR_DATETIME_CONVERT = \PHP_VERSION_ID >= 70300 ? \PDO::DBLIB_ATTR_DATETIME_CONVERT : 1006;

        public function __construct(string $dsn, ?string $username = null, ?string $password = null, ?array $options = null)
        {
            parent::__construct($dsn, $username, $password, $options);

            if ('dblib' !== $driver = $this->getAttribute(\PDO::ATTR_DRIVER_NAME)) {
                throw new \PDOException(\sprintf('Pdo\Dblib::__construct() cannot be used for connecting to the "%s" driver', $driver));
            }
        }

        public static function connect(string $dsn, ?string $username = null, ?string $password = null, ?array $options = null): self
        {
            try {
                return new self($dsn, $username, $password, $options);
            } catch (\PDOException $e) {
                throw preg_match('/^Pdo\\\\Dblib::__construct\(\) cannot be used for connecting to the "([a-z]+)" driver/', $e->getMessage(), $matches) ? new \PDOException(\sprintf('Pdo\Dblib::connect() cannot be used for connecting to the "%s" driver', $matches[1])) : $e;
            }
        }
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Pdo;

use PDO;

if (\PHP_VERSION_ID < 80400 && \extension_loaded('pdo_mysql')) {
    // Feature detection for non-mysqlnd; see also https://www.php.net/manual/en/class.pdo-mysql.php#pdo-mysql.constants.attr-max-buffer-size
    if (\defined('PDO::MYSQL_ATTR_MAX_BUFFER_SIZE') && \defined('PDO::MYSQL_ATTR_READ_DEFAULT_FILE') && \defined('PDO::MYSQL_ATTR_READ_DEFAULT_GROUP')) {
        class Mysql extends \PDO
        {
            public const ATTR_COMPRESS = \PDO::MYSQL_ATTR_COMPRESS;
            public const ATTR_DIRECT_QUERY = \PDO::MYSQL_ATTR_DIRECT_QUERY;
            public const ATTR_FOUND_ROWS = \PDO::MYSQL_ATTR_FOUND_ROWS;
            public const ATTR_IGNORE_SPACE = \PDO::MYSQL_ATTR_IGNORE_SPACE;
            public const ATTR_INIT_COMMAND = \PDO::MYSQL_ATTR_INIT_COMMAND;
            public const ATTR_LOCAL_INFILE = \PDO::MYSQL_ATTR_LOCAL_INFILE;
            public const ATTR_LOCAL_INFILE_DIRECTORY = \PHP_VERSION_ID >= 80100 ? \PDO::MYSQL_ATTR_LOCAL_INFILE_DIRECTORY : 1015;
            public const ATTR_MAX_BUFFER_SIZE = \PDO::MYSQL_ATTR_MAX_BUFFER_SIZE;
            public const ATTR_MULTI_STATEMENTS = \PDO::MYSQL_ATTR_MULTI_STATEMENTS;
            public const ATTR_READ_DEFAULT_FILE = \PDO::MYSQL_ATTR_READ_DEFAULT_FILE;
            public const ATTR_READ_DEFAULT_GROUP = \PDO::MYSQL_ATTR_READ_DEFAULT_GROUP;
            public const ATTR_SERVER_PUBLIC_KEY = \PDO::MYSQL_ATTR_SERVER_PUBLIC_KEY;
            public const ATTR_SSL_CA = \PDO::MYSQL_ATTR_SSL_CA;
            public const ATTR_SSL_CAPATH = \PDO::MYSQL_ATTR_SSL_CAPATH;
            public const ATTR_SSL_CERT = \PDO::MYSQL_ATTR_SSL_CERT;
            public const ATTR_SSL_CIPHER = \PDO::MYSQL_ATTR_SSL_CIPHER;
            public const ATTR_SSL_KEY = \PDO::MYSQL_ATTR_SSL_KEY;
            public const ATTR_USE_BUFFERED_QUERY = \PDO::MYSQL_ATTR_USE_BUFFERED_QUERY;

            public function __construct(string $dsn, ?string $username = null, ?string $password = null, ?array $options = null)
            {
                parent::__construct($dsn, $username, $password, $options);

                if ('mysql' !== $driver = $this->getAttribute(\PDO::ATTR_DRIVER_NAME)) {
                    throw new \PDOException(\sprintf('Pdo\Mysql::__construct() cannot be used for connecting to the "%s" driver', $driver));
                }
            }

            public static function connect(string $dsn, ?string $username = null, ?string $password = null, ?array $options = null): self
            {
                try {
                    return new self($dsn, $username, $password, $options);
                } catch (\PDOException $e) {
                    throw preg_match('/^Pdo\\\\Mysql::__construct\(\) cannot be used for connecting to the "([a-z]+)" driver/', $e->getMessage(), $matches) ? new \PDOException(\sprintf('Pdo\Mysql::connect() cannot be used for connecting to the "%s" driver', $matches[1])) : $e;
                }
            }
        }
    } elseif (\defined('PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT')) {
        class Mysql extends \PDO
        {
            public const ATTR_COMPRESS = \PDO::MYSQL_ATTR_COMPRESS;
            public const ATTR_DIRECT_QUERY = \PDO::MYSQL_ATTR_DIRECT_QUERY;
            public const ATTR_FOUND_ROWS = \PDO::MYSQL_ATTR_FOUND_ROWS;
            public const ATTR_IGNORE_SPACE = \PDO::MYSQL_ATTR_IGNORE_SPACE;
            public const ATTR_INIT_COMMAND = \PDO::MYSQL_ATTR_INIT_COMMAND;
            public const ATTR_LOCAL_INFILE = \PDO::MYSQL_ATTR_LOCAL_INFILE;
            public const ATTR_LOCAL_INFILE_DIRECTORY = \PHP_VERSION_ID >= 80100 ? \PDO::MYSQL_ATTR_LOCAL_INFILE_DIRECTORY : 1015;
            // public const ATTR_MAX_BUFFER_SIZE = PDO::MYSQL_ATTR_MAX_BUFFER_SIZE; // disabled for mysqlnd
            public const ATTR_MULTI_STATEMENTS = \PDO::MYSQL_ATTR_MULTI_STATEMENTS;
            // public const ATTR_READ_DEFAULT_FILE = PDO::MYSQL_ATTR_READ_DEFAULT_FILE; // disabled for mysqlnd
            // public const ATTR_READ_DEFAULT_GROUP = PDO::MYSQL_ATTR_READ_DEFAULT_GROUP; // disabled for mysqlnd
            public const ATTR_SERVER_PUBLIC_KEY = \PDO::MYSQL_ATTR_SERVER_PUBLIC_KEY;
            public const ATTR_SSL_CA = \PDO::MYSQL_ATTR_SSL_CA;
            public const ATTR_SSL_CAPATH = \PDO::MYSQL_ATTR_SSL_CAPATH;
            public const ATTR_SSL_CERT = \PDO::MYSQL_ATTR_SSL_CERT;
            public const ATTR_SSL_CIPHER = \PDO::MYSQL_ATTR_SSL_CIPHER;
            public const ATTR_SSL_KEY = \PDO::MYSQL_ATTR_SSL_KEY;
            public const ATTR_SSL_VERIFY_SERVER_CERT = \PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT;
            public const ATTR_USE_BUFFERED_QUERY = \PDO::MYSQL_ATTR_USE_BUFFERED_QUERY;

            public function __construct(string $dsn, ?string $username = null, ?string $password = null, ?array $options = null)
            {
                parent::__construct($dsn, $username, $password, $options);

                if ('mysql' !== $driver = $this->getAttribute(\PDO::ATTR_DRIVER_NAME)) {
                    throw new \PDOException(\sprintf('Pdo\Mysql::__construct() cannot be used for connecting to the "%s" driver', $driver));
                }
            }

            public static function connect(string $dsn, ?string $username = null, ?string $password = null, ?array $options = null): self
            {
                try {
                    return new self($dsn, $username, $password, $options);
                } catch (\PDOException $e) {
                    throw preg_match('/^Pdo\\\\Mysql::__construct\(\) cannot be used for connecting to the "([a-z]+)" driver/', $e->getMessage(), $matches) ? new \PDOException(\sprintf('Pdo\Mysql::connect() cannot be used for connecting to the "%s" driver', $matches[1])) : $e;
                }
            }
        }
    } else {
        class Mysql extends \PDO
        {
            public const ATTR_COMPRESS = \PDO::MYSQL_ATTR_COMPRESS;
            public const ATTR_DIRECT_QUERY = \PDO::MYSQL_ATTR_DIRECT_QUERY;
            public const ATTR_FOUND_ROWS = \PDO::MYSQL_ATTR_FOUND_ROWS;
            public const ATTR_IGNORE_SPACE = \PDO::MYSQL_ATTR_IGNORE_SPACE;
            public const ATTR_INIT_COMMAND = \PDO::MYSQL_ATTR_INIT_COMMAND;
            public const ATTR_LOCAL_INFILE = \PDO::MYSQL_ATTR_LOCAL_INFILE;
            public const ATTR_LOCAL_INFILE_DIRECTORY = \PHP_VERSION_ID >= 80100 ? \PDO::MYSQL_ATTR_LOCAL_INFILE_DIRECTORY : 1015;
            // public const ATTR_MAX_BUFFER_SIZE = PDO::MYSQL_ATTR_MAX_BUFFER_SIZE; // disabled for mysqlnd
            public const ATTR_MULTI_STATEMENTS = \PDO::MYSQL_ATTR_MULTI_STATEMENTS;
            // public const ATTR_READ_DEFAULT_FILE = PDO::MYSQL_ATTR_READ_DEFAULT_FILE; // disabled for mysqlnd
            // public const ATTR_READ_DEFAULT_GROUP = PDO::MYSQL_ATTR_READ_DEFAULT_GROUP; // disabled for mysqlnd
            public const ATTR_SERVER_PUBLIC_KEY = \PDO::MYSQL_ATTR_SERVER_PUBLIC_KEY;
            public const ATTR_SSL_CA = \PDO::MYSQL_ATTR_SSL_CA;
            public const ATTR_SSL_CAPATH = \PDO::MYSQL_ATTR_SSL_CAPATH;
            public const ATTR_SSL_CERT = \PDO::MYSQL_ATTR_SSL_CERT;
            public const ATTR_SSL_CIPHER = \PDO::MYSQL_ATTR_SSL_CIPHER;
            public const ATTR_SSL_KEY = \PDO::MYSQL_ATTR_SSL_KEY;
            public const ATTR_USE_BUFFERED_QUERY = \PDO::MYSQL_ATTR_USE_BUFFERED_QUERY;

            public function __construct(string $dsn, ?string $username = null, ?string $password = null, ?array $options = null)
            {
                parent::__construct($dsn, $username, $password, $options);

                if ('mysql' !== $driver = $this->getAttribute(\PDO::ATTR_DRIVER_NAME)) {
                    throw new \PDOException(\sprintf('Pdo\Mysql::__construct() cannot be used for connecting to the "%s" driver', $driver));
                }
            }

            public static function connect(string $dsn, ?string $username = null, ?string $password = null, ?array $options = null): self
            {
                try {
                    return new self($dsn, $username, $password, $options);
                } catch (\PDOException $e) {
                    throw preg_match('/^Pdo\\\\Mysql::__construct\(\) cannot be used for connecting to the "([a-z]+)" driver/', $e->getMessage(), $matches) ? new \PDOException(\sprintf('Pdo\Mysql::connect() cannot be used for connecting to the "%s" driver', $matches[1])) : $e;
                }
            }
        }
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Pdo;

if (\PHP_VERSION_ID < 80400 && \extension_loaded('pdo_pgsql')) {
    class Pgsql extends \PDO
    {
        public const ATTR_DISABLE_PREPARES = \PDO::PGSQL_ATTR_DISABLE_PREPARES;

        public function __construct(string $dsn, ?string $username = null, ?string $password = null, ?array $options = null)
        {
            parent::__construct($dsn, $username, $password, $options);

            if ('pgsql' !== $driver = $this->getAttribute(\PDO::ATTR_DRIVER_NAME)) {
                throw new \PDOException(\sprintf('Pdo\Pgsql::__construct() cannot be used for connecting to the "%s" driver', $driver));
            }
        }

        public static function connect(string $dsn, ?string $username = null, ?string $password = null, ?array $options = null): self
        {
            try {
                return new self($dsn, $username, $password, $options);
            } catch (\PDOException $e) {
                throw preg_match('/^Pdo\\\\Pgsql::__construct\(\) cannot be used for connecting to the "([a-z]+)" driver/', $e->getMessage(), $matches) ? new \PDOException(\sprintf('Pdo\Pgsql::connect() cannot be used for connecting to the "%s" driver', $matches[1])) : $e;
            }
        }

        public function copyFromArray(string $tableName, array $rows, string $separator = "\t", string $nullAs = '\\\\N', ?string $fields = null): bool
        {
            return $this->pgsqlCopyFromArray($tableName, $rows, $separator, $nullAs, $fields);
        }

        public function copyFromFile(string $tableName, string $filename, string $separator = "\t", string $nullAs = '\\\\N', ?string $fields = null): bool
        {
            return $this->pgsqlCopyFromFile($tableName, $filename, $separator, $nullAs, $fields);
        }

        /**
         * @return array|false
         */
        public function copyToArray(string $tableName, string $separator = "\t", string $nullAs = '\\\\N', ?string $fields = null)
        {
            return $this->pgsqlCopyToArray($tableName, $separator, $nullAs, $fields);
        }

        public function copyToFile(string $tableName, string $filename, string $separator = "\t", string $nullAs = '\\\\N', ?string $fields = null): bool
        {
            return $this->pgsqlCopyToFile($tableName, $filename, $separator, $nullAs, $fields);
        }

        /**
         * @return array|false
         */
        public function getNotify(int $fetchMode = \PDO::FETCH_DEFAULT, int $timeoutMilliseconds = 0)
        {
            return $this->pgsqlGetNotify($fetchMode, $timeoutMilliseconds);
        }

        public function getPid(): int
        {
            return $this->pgsqlGetPid();
        }

        /**
         * @return string|false
         */
        public function lobCreate()
        {
            return $this->pgsqlLOBCreate();
        }

        /**
         * @return resource|false
         */
        public function lobOpen(string $oid, string $mode = 'rb')
        {
            return $this->pgsqlLOBOpen($oid, $mode);
        }

        public function lobUnlink(string $oid): bool
        {
            return $this->pgsqlLOBUnlink($oid);
        }
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

if (\PHP_VERSION_ID < 80100) {
    #[Attribute(Attribute::TARGET_METHOD | Attribute::TARGET_FUNCTION | Attribute::TARGET_CLASS_CONSTANT)]
    final class Deprecated
    {
        /**
         * @readonly
         */
        public ?string $message;

        /**
         * @readonly
         */
        public ?string $since;

        public function __construct(?string $message = null, ?string $since = null)
        {
            $this->message = $message;
            $this->since = $since;
        }
    }
} elseif (\PHP_VERSION_ID < 80400) {
    require dirname(__DIR__).'/Deprecated.php';
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

if (\PHP_VERSION_ID < 80400) {
    enum RoundingMode
    {
        case HalfAwayFromZero;
        case HalfTowardsZero;
        case HalfEven;
        case HalfOdd;
        case TowardsZero;
        case AwayFromZero;
        case NegativeInfinity;
        case PositiveInfinity;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

if (\PHP_VERSION_ID < 80400) {
    #[Attribute(Attribute::TARGET_METHOD | Attribute::TARGET_FUNCTION | Attribute::TARGET_CLASS_CONSTANT)]
    final class Deprecated
    {
        public readonly ?string $message;
        public readonly ?string $since;

        public function __construct(?string $message = null, ?string $since = null)
        {
            $this->message = $message;
            $this->since = $since;
        }
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Polyfill\Php83;

/**
 * @author Ion Bazan <ion.bazan@gmail.com>
 * @author Pierre Ambroise <pierre27.ambroise@gmail.com>
 *
 * @internal
 */
final class Php83
{
    private const JSON_MAX_DEPTH = 0x7FFFFFFF; // see https://www.php.net/manual/en/function.json-decode.php

    public static function json_validate(string $json, int $depth = 512, int $flags = 0): bool
    {
        if (0 !== $flags && \defined('JSON_INVALID_UTF8_IGNORE') && \JSON_INVALID_UTF8_IGNORE !== $flags) {
            throw new \ValueError('json_validate(): Argument #3 ($flags) must be a valid flag (allowed flags: JSON_INVALID_UTF8_IGNORE)');
        }

        if ('' === $json) {
            json_decode('');

            return false;
        }

        if ($depth <= 0) {
            json_decode('0');
            throw new \ValueError('json_validate(): Argument #2 ($depth) must be greater than 0');
        }

        if ($depth > self::JSON_MAX_DEPTH) {
            json_decode('0');
            throw new \ValueError(\sprintf('json_validate(): Argument #2 ($depth) must be less than %d', self::JSON_MAX_DEPTH));
        }

        json_decode($json, true, $depth, $flags);

        return \JSON_ERROR_NONE === json_last_error();
    }

    /** @return string|false */
    public static function mb_str_pad(string $string, int $length, string $pad_string = ' ', int $pad_type = \STR_PAD_RIGHT, ?string $encoding = null)
    {
        if (null === $encoding) {
            $encoding = mb_internal_encoding();
        }

        $errorToTrigger = null;
        try {
            if (!@mb_check_encoding('', $encoding)) {
                $errorToTrigger = \sprintf('mb_str_pad(): Argument #5 ($encoding) must be a valid encoding, "%s" given', $encoding);
            }
        } catch (\ValueError $e) {
            $errorToTrigger = \sprintf('mb_str_pad(): Argument #5 ($encoding) must be a valid encoding, "%s" given', $encoding);
        }

        if (null === $errorToTrigger && 0 >= $padStringLength = mb_strlen($pad_string, $encoding)) {
            $errorToTrigger = 'mb_str_pad(): Argument #3 ($pad_string) must be a non-empty string';
        }

        if (null === $errorToTrigger && !\in_array($pad_type, [\STR_PAD_RIGHT, \STR_PAD_LEFT, \STR_PAD_BOTH], true)) {
            $errorToTrigger = 'mb_str_pad(): Argument #4 ($pad_type) must be STR_PAD_LEFT, STR_PAD_RIGHT, or STR_PAD_BOTH';
        }

        if (null !== $errorToTrigger) {
            if (80000 > \PHP_VERSION_ID) {
                trigger_error($errorToTrigger, \E_USER_WARNING);

                return false;
            }

            throw new \ValueError($errorToTrigger);
        }

        $paddingRequired = $length - mb_strlen($string, $encoding);

        if ($paddingRequired < 1) {
            return $string;
        }

        switch ($pad_type) {
            case \STR_PAD_LEFT:
                $leftPaddingLength = $paddingRequired;
                break;
            case \STR_PAD_RIGHT:
                $leftPaddingLength = 0;
                break;
            default:
                $leftPaddingLength = intdiv($paddingRequired, 2);
        }

        $rightPaddingLength = $paddingRequired - $leftPaddingLength;

        return str_repeat($pad_string, intdiv($leftPaddingLength, $padStringLength)).mb_substr($pad_string, 0, $leftPaddingLength % $padStringLength, $encoding)
            .$string
            .str_repeat($pad_string, intdiv($rightPaddingLength, $padStringLength)).mb_substr($pad_string, 0, $rightPaddingLength % $padStringLength, $encoding);
    }

    public static function str_increment(string $string): string
    {
        if ('' === $string) {
            throw new \ValueError('str_increment(): Argument #1 ($string) cannot be empty');
        }

        if (!preg_match('/^[a-zA-Z0-9]++$/D', $string)) {
            throw new \ValueError('str_increment(): Argument #1 ($string) must be composed only of alphanumeric ASCII characters');
        }

        for ($i = \strlen($string) - 1; $i >= 0; --$i) {
            $char = $string[$i];

            if ('z' === $char) {
                $string[$i] = 'a';
                continue;
            }
            if ('Z' === $char) {
                $string[$i] = 'A';
                continue;
            }
            if ('9' === $char) {
                $string[$i] = '0';
                continue;
            }

            $string[$i] = \chr(\ord($char) + 1);

            return $string;
        }

        switch ($string[0]) {
            case 'a': return 'a'.$string;
            case 'A': return 'A'.$string;
        }

        return '1'.$string;
    }

    public static function str_decrement(string $string): string
    {
        if ('' === $string) {
            throw new \ValueError('str_decrement(): Argument #1 ($string) cannot be empty');
        }

        if (!preg_match('/^[a-zA-Z0-9]++$/D', $string)) {
            throw new \ValueError('str_decrement(): Argument #1 ($string) must be composed only of alphanumeric ASCII characters');
        }

        if ('0' === $string[0] || 'a' === $string || 'A' === $string) {
            throw new \ValueError(\sprintf('str_decrement(): Argument #1 ($string) "%s" is out of decrement range', $string));
        }

        for ($i = \strlen($string) - 1; $i >= 0; --$i) {
            $char = $string[$i];

            if ('a' === $char) {
                $string[$i] = 'z';
                continue;
            }
            if ('A' === $char) {
                $string[$i] = 'Z';
                continue;
            }
            if ('0' === $char) {
                $string[$i] = '9';
                continue;
            }

            $string[$i] = \chr(\ord($char) - 1);

            return '0' === $string[0] && isset($string[1]) ? substr($string, 1) : $string;
        }

        return substr($string, 1);
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use Symfony\Polyfill\Php83 as p;

if (\PHP_VERSION_ID >= 80300) {
    return;
}

if (!function_exists('json_validate')) {
    function json_validate(string $json, int $depth = 512, int $flags = 0): bool { return p\Php83::json_validate($json, $depth, $flags); }
}

if (!function_exists('stream_context_set_options')) {
    function stream_context_set_options($context, array $options): bool { return stream_context_set_option($context, $options); }
}

if (!function_exists('str_increment')) {
    function str_increment(string $string): string { return p\Php83::str_increment($string); }
}

if (!function_exists('str_decrement')) {
    function str_decrement(string $string): string { return p\Php83::str_decrement($string); }
}

if (\PHP_VERSION_ID < 80000) {
    require __DIR__.'/bootstrap72.php';
}

if (extension_loaded('mbstring')) {
    if (!function_exists('mb_str_pad')) {
        function mb_str_pad(?string $string, ?int $length, ?string $pad_string = ' ', ?int $pad_type = STR_PAD_RIGHT, ?string $encoding = null): string { return p\Php83::mb_str_pad((string) $string, (int) $length, (string) $pad_string, (int) $pad_type, $encoding); }
    }
}

if (\PHP_VERSION_ID >= 80100) {
    return require __DIR__.'/bootstrap81.php';
}

if (!function_exists('ldap_exop_sync') && function_exists('ldap_exop')) {
    function ldap_exop_sync($ldap, string $request_oid, ?string $request_data = null, ?array $controls = null, &$response_data = null, &$response_oid = null): bool { return ldap_exop($ldap, $request_oid, $request_data, $response_data, $response_oid); }
}

if (!function_exists('ldap_connect_wallet') && function_exists('ldap_connect')) {
    function ldap_connect_wallet(?string $uri, string $wallet, string $password, int $auth_mode = \GSLC_SSL_NO_AUTH) { return ldap_connect($uri, $wallet, $password, $auth_mode); }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use Symfony\Polyfill\Php83 as p;

if (!class_exists('ValueError', false)) {
    class ValueError extends Error
    {
    }
}

if (extension_loaded('mbstring')) {
    if (!function_exists('mb_str_pad')) {
        /** @return string|false */
        function mb_str_pad(?string $string, ?int $length, ?string $pad_string = ' ', ?int $pad_type = STR_PAD_RIGHT, ?string $encoding = null) { return p\Php83::mb_str_pad((string) $string, (int) $length, (string) $pad_string, (int) $pad_type, $encoding); }
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use Symfony\Polyfill\Php83 as p;

if (\PHP_VERSION_ID >= 80300) {
    return;
}

if (!function_exists('json_validate')) {
    function json_validate(string $json, int $depth = 512, int $flags = 0): bool { return p\Php83::json_validate($json, $depth, $flags); }
}

if (!function_exists('str_increment')) {
    function str_increment(string $string): string { return p\Php83::str_increment($string); }
}

if (!function_exists('str_decrement')) {
    function str_decrement(string $string): string { return p\Php83::str_decrement($string); }
}

if (extension_loaded('mbstring') && !function_exists('mb_str_pad')) {
    function mb_str_pad(?string $string, ?int $length, ?string $pad_string = ' ', ?int $pad_type = STR_PAD_RIGHT, ?string $encoding = null): string { return p\Php83::mb_str_pad((string) $string, (int) $length, (string) $pad_string, (int) $pad_type, $encoding); }
}

if (!function_exists('ldap_exop_sync') && function_exists('ldap_exop')) {
    function ldap_exop_sync(\LDAP\Connection $ldap, string $request_oid, ?string $request_data = null, ?array $controls = null, &$response_data = null, &$response_oid = null): bool { return ldap_exop($ldap, $request_oid, $request_data, $response_data, $response_oid); }
}

if (!function_exists('ldap_connect_wallet') && function_exists('ldap_connect')) {
    function ldap_connect_wallet(?string $uri, string $wallet, #[\SensitiveParameter] string $password, int $auth_mode = \GSLC_SSL_NO_AUTH): \LDAP\Connection|false { return ldap_connect($uri, $wallet, $password, $auth_mode); }
}
{
    "name": "symfony/polyfill-php83",
    "type": "library",
    "description": "Symfony polyfill backporting some PHP 8.3+ features to lower PHP versions",
    "keywords": ["polyfill", "shim", "compatibility", "portable"],
    "homepage": "https://symfony.com",
    "license": "MIT",
    "authors": [
        {
            "name": "Nicolas Grekas",
            "email": "p@tchwork.com"
        },
        {
            "name": "Symfony Community",
            "homepage": "https://symfony.com/contributors"
        }
    ],
    "require": {
        "php": ">=7.2"
    },
    "autoload": {
        "psr-4": { "Symfony\\Polyfill\\Php83\\": "" },
        "files": [ "bootstrap.php" ],
        "classmap": [ "Resources/stubs" ]
    },
    "minimum-stability": "dev",
    "extra": {
        "thanks": {
            "name": "symfony/polyfill",
            "url": "https://github.com/symfony/polyfill"
        }
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

if (\PHP_VERSION_ID < 80300) {
    class DateMalformedIntervalStringException extends DateException
    {
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

if (\PHP_VERSION_ID < 80300) {
    class DateInvalidTimeZoneException extends DateException
    {
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

if (\PHP_VERSION_ID < 80300) {
    class DateInvalidOperationException extends DateException
    {
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

if (\PHP_VERSION_ID < 80300) {
    class SQLite3Exception extends Exception
    {
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

if (\PHP_VERSION_ID < 80300) {
    class DateObjectError extends DateError
    {
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

if (\PHP_VERSION_ID < 80300) {
    class DateException extends Exception
    {
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

if (\PHP_VERSION_ID < 80300) {
    class DateError extends Error
    {
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

if (\PHP_VERSION_ID < 80300) {
    class DateRangeError extends DateError
    {
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

if (\PHP_VERSION_ID < 80300) {
    class DateMalformedStringException extends DateException
    {
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

if (\PHP_VERSION_ID < 80300) {
    class DateMalformedPeriodStringException extends DateException
    {
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

if (\PHP_VERSION_ID < 80300) {
    #[Attribute(Attribute::TARGET_METHOD)]
    final class Override
    {
        public function __construct()
        {
        }
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Lazy;

use DateTimeInterface;
use Ramsey\Uuid\Converter\NumberConverterInterface;
use Ramsey\Uuid\Exception\UnsupportedOperationException;
use Ramsey\Uuid\Fields\FieldsInterface;
use Ramsey\Uuid\Rfc4122\UuidV1;
use Ramsey\Uuid\Rfc4122\UuidV6;
use Ramsey\Uuid\Type\Hexadecimal;
use Ramsey\Uuid\Type\Integer as IntegerObject;
use Ramsey\Uuid\UuidFactory;
use Ramsey\Uuid\UuidInterface;
use ValueError;

use function assert;
use function bin2hex;
use function hex2bin;
use function sprintf;
use function str_replace;
use function substr;

/**
 * Lazy version of a UUID: its format has not been determined yet, so it is mostly only usable for string/bytes
 * conversion. This object optimizes instantiation, serialization and string conversion time, at the cost of increased
 * overhead for more advanced UUID operations.
 *
 * > [!NOTE]
 * > The {@see FieldsInterface} does not declare methods that deprecated API relies upon: the API has been ported from
 * > the {@see \Ramsey\Uuid\Uuid} definition, and is deprecated anyway.
 *
 * > [!NOTE]
 * > The deprecated API from {@see \Ramsey\Uuid\Uuid} is in use here (on purpose): it will be removed once the
 * > deprecated API is gone from this class too.
 *
 * @internal this type is used internally for performance reasons and is not supposed to be directly referenced in consumer libraries.
 */
final class LazyUuidFromString implements UuidInterface
{
    public const VALID_REGEX = '/\A[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\z/ms';

    private ?UuidInterface $unwrapped = null;

    /**
     * @param non-empty-string $uuid
     */
    public function __construct(private string $uuid)
    {
    }

    public static function fromBytes(string $bytes): self
    {
        $base16Uuid = bin2hex($bytes);

        return new self(
            substr($base16Uuid, 0, 8)
            . '-'
            . substr($base16Uuid, 8, 4)
            . '-'
            . substr($base16Uuid, 12, 4)
            . '-'
            . substr($base16Uuid, 16, 4)
            . '-'
            . substr($base16Uuid, 20, 12)
        );
    }

    public function serialize(): string
    {
        return $this->uuid;
    }

    /**
     * @return array{string: non-empty-string}
     */
    public function __serialize(): array
    {
        return ['string' => $this->uuid];
    }

    /**
     * {@inheritDoc}
     *
     * @param non-empty-string $data
     */
    public function unserialize(string $data): void
    {
        $this->uuid = $data;
    }

    /**
     * @param array{string?: non-empty-string} $data
     */
    public function __unserialize(array $data): void
    {
        // @codeCoverageIgnoreStart
        if (!isset($data['string'])) {
            throw new ValueError(sprintf('%s(): Argument #1 ($data) is invalid', __METHOD__));
        }
        // @codeCoverageIgnoreEnd

        $this->unserialize($data['string']);
    }

    public function getNumberConverter(): NumberConverterInterface
    {
        return ($this->unwrapped ?? $this->unwrap())->getNumberConverter();
    }

    /**
     * @inheritDoc
     */
    public function getFieldsHex(): array
    {
        return ($this->unwrapped ?? $this->unwrap())->getFieldsHex();
    }

    public function getClockSeqHiAndReservedHex(): string
    {
        return ($this->unwrapped ?? $this->unwrap())->getClockSeqHiAndReservedHex();
    }

    public function getClockSeqLowHex(): string
    {
        return ($this->unwrapped ?? $this->unwrap())->getClockSeqLowHex();
    }

    public function getClockSequenceHex(): string
    {
        return ($this->unwrapped ?? $this->unwrap())->getClockSequenceHex();
    }

    public function getDateTime(): DateTimeInterface
    {
        return ($this->unwrapped ?? $this->unwrap())->getDateTime();
    }

    public function getLeastSignificantBitsHex(): string
    {
        return ($this->unwrapped ?? $this->unwrap())->getLeastSignificantBitsHex();
    }

    public function getMostSignificantBitsHex(): string
    {
        return ($this->unwrapped ?? $this->unwrap())->getMostSignificantBitsHex();
    }

    public function getNodeHex(): string
    {
        return ($this->unwrapped ?? $this->unwrap())->getNodeHex();
    }

    public function getTimeHiAndVersionHex(): string
    {
        return ($this->unwrapped ?? $this->unwrap())->getTimeHiAndVersionHex();
    }

    public function getTimeLowHex(): string
    {
        return ($this->unwrapped ?? $this->unwrap())->getTimeLowHex();
    }

    public function getTimeMidHex(): string
    {
        return ($this->unwrapped ?? $this->unwrap())->getTimeMidHex();
    }

    public function getTimestampHex(): string
    {
        return ($this->unwrapped ?? $this->unwrap())->getTimestampHex();
    }

    public function getUrn(): string
    {
        return ($this->unwrapped ?? $this->unwrap())->getUrn();
    }

    public function getVariant(): ?int
    {
        return ($this->unwrapped ?? $this->unwrap())->getVariant();
    }

    public function getVersion(): ?int
    {
        return ($this->unwrapped ?? $this->unwrap())->getVersion();
    }

    public function compareTo(UuidInterface $other): int
    {
        return ($this->unwrapped ?? $this->unwrap())->compareTo($other);
    }

    public function equals(?object $other): bool
    {
        if (!$other instanceof UuidInterface) {
            return false;
        }

        return $this->uuid === $other->toString();
    }

    public function getBytes(): string
    {
        /**
         * @var non-empty-string
         * @phpstan-ignore possiblyImpure.functionCall, possiblyImpure.functionCall
         */
        return (string) hex2bin(str_replace('-', '', $this->uuid));
    }

    public function getFields(): FieldsInterface
    {
        return ($this->unwrapped ?? $this->unwrap())->getFields();
    }

    public function getHex(): Hexadecimal
    {
        return ($this->unwrapped ?? $this->unwrap())->getHex();
    }

    public function getInteger(): IntegerObject
    {
        return ($this->unwrapped ?? $this->unwrap())->getInteger();
    }

    public function toString(): string
    {
        return $this->uuid;
    }

    public function __toString(): string
    {
        return $this->uuid;
    }

    public function jsonSerialize(): string
    {
        return $this->uuid;
    }

    /**
     * @deprecated Use {@see UuidInterface::getFields()} to get a {@see FieldsInterface} instance. If it is a
     *     {@see Rfc4122FieldsInterface} instance, you may call {@see Rfc4122FieldsInterface::getClockSeqHiAndReserved()}
     *     and use the arbitrary-precision math library of your choice to convert it to a string integer.
     */
    public function getClockSeqHiAndReserved(): string
    {
        $instance = ($this->unwrapped ?? $this->unwrap());

        $fields = $instance->getFields();
        assert($fields instanceof \Ramsey\Uuid\Rfc4122\FieldsInterface);

        return $instance->getNumberConverter()->fromHex($fields->getClockSeqHiAndReserved()->toString());
    }

    /**
     * @deprecated Use {@see UuidInterface::getFields()} to get a {@see FieldsInterface} instance. If it is a
     *     {@see Rfc4122FieldsInterface} instance, you may call {@see Rfc4122FieldsInterface::getClockSeqLow()} and use
     *     the arbitrary-precision math library of your choice to convert it to a string integer.
     */
    public function getClockSeqLow(): string
    {
        $instance = ($this->unwrapped ?? $this->unwrap());

        $fields = $instance->getFields();
        assert($fields instanceof \Ramsey\Uuid\Rfc4122\FieldsInterface);

        return $instance->getNumberConverter()->fromHex($fields->getClockSeqLow()->toString());
    }

    /**
     * @deprecated Use {@see UuidInterface::getFields()} to get a {@see FieldsInterface} instance. If it is a
     *     {@see Rfc4122FieldsInterface} instance, you may call {@see Rfc4122FieldsInterface::getClockSeq()} and use the
     *     arbitrary-precision math library of your choice to convert it to a string integer.
     */
    public function getClockSequence(): string
    {
        $instance = ($this->unwrapped ?? $this->unwrap());

        $fields = $instance->getFields();
        assert($fields instanceof \Ramsey\Uuid\Rfc4122\FieldsInterface);

        return $instance->getNumberConverter()->fromHex($fields->getClockSeq()->toString());
    }

    /**
     * @deprecated This method will be removed in 5.0.0. There is no direct alternative, but the same information may be
     *     obtained by splitting in half the value returned by {@see UuidInterface::getHex()}.
     */
    public function getLeastSignificantBits(): string
    {
        $instance = ($this->unwrapped ?? $this->unwrap());

        return $instance->getNumberConverter()->fromHex(substr($instance->getHex()->toString(), 16));
    }

    /**
     * @deprecated This method will be removed in 5.0.0. There is no direct alternative, but the same information may be
     *     obtained by splitting in half the value returned by {@see UuidInterface::getHex()}.
     */
    public function getMostSignificantBits(): string
    {
        $instance = ($this->unwrapped ?? $this->unwrap());

        return $instance->getNumberConverter()->fromHex(substr($instance->getHex()->toString(), 0, 16));
    }

    /**
     * @deprecated Use {@see UuidInterface::getFields()} to get a {@see FieldsInterface} instance. If it is a
     *     {@see Rfc4122FieldsInterface} instance, you may call {@see Rfc4122FieldsInterface::getNode()} and use the
     *     arbitrary-precision math library of your choice to convert it to a string integer.
     */
    public function getNode(): string
    {
        $instance = ($this->unwrapped ?? $this->unwrap());

        $fields = $instance->getFields();
        assert($fields instanceof \Ramsey\Uuid\Rfc4122\FieldsInterface);

        return $instance->getNumberConverter()->fromHex($fields->getNode()->toString());
    }

    /**
     * @deprecated Use {@see UuidInterface::getFields()} to get a {@see FieldsInterface} instance. If it is a
     *     {@see Rfc4122FieldsInterface} instance, you may call {@see Rfc4122FieldsInterface::getTimeHiAndVersion()} and
     *     use the arbitrary-precision math library of your choice to convert it to a string integer.
     */
    public function getTimeHiAndVersion(): string
    {
        $instance = ($this->unwrapped ?? $this->unwrap());

        $fields = $instance->getFields();
        assert($fields instanceof \Ramsey\Uuid\Rfc4122\FieldsInterface);

        return $instance->getNumberConverter()->fromHex($fields->getTimeHiAndVersion()->toString());
    }

    /**
     * @deprecated Use {@see UuidInterface::getFields()} to get a {@see FieldsInterface} instance. If it is a
     *     {@see Rfc4122FieldsInterface} instance, you may call {@see Rfc4122FieldsInterface::getTimeLow()} and use the
     *     arbitrary-precision math library of your choice to convert it to a string integer.
     */
    public function getTimeLow(): string
    {
        $instance = ($this->unwrapped ?? $this->unwrap());

        $fields = $instance->getFields();
        assert($fields instanceof \Ramsey\Uuid\Rfc4122\FieldsInterface);

        return $instance->getNumberConverter()->fromHex($fields->getTimeLow()->toString());
    }

    /**
     * @deprecated Use {@see UuidInterface::getFields()} to get a {@see FieldsInterface} instance. If it is a
     *     {@see Rfc4122FieldsInterface} instance, you may call {@see Rfc4122FieldsInterface::getTimeMid()} and use the
     *     arbitrary-precision math library of your choice to convert it to a string integer.
     */
    public function getTimeMid(): string
    {
        $instance = ($this->unwrapped ?? $this->unwrap());

        $fields = $instance->getFields();
        assert($fields instanceof \Ramsey\Uuid\Rfc4122\FieldsInterface);

        return $instance->getNumberConverter()->fromHex($fields->getTimeMid()->toString());
    }

    /**
     * @deprecated Use {@see UuidInterface::getFields()} to get a {@see FieldsInterface} instance. If it is a
     *     {@see Rfc4122FieldsInterface} instance, you may call {@see Rfc4122FieldsInterface::getTimestamp()} and use
     *     the arbitrary-precision math library of your choice to convert it to a string integer.
     */
    public function getTimestamp(): string
    {
        $instance = ($this->unwrapped ?? $this->unwrap());

        $fields = $instance->getFields();
        assert($fields instanceof \Ramsey\Uuid\Rfc4122\FieldsInterface);

        if ($fields->getVersion() !== 1) {
            throw new UnsupportedOperationException('Not a time-based UUID');
        }

        return $instance->getNumberConverter()->fromHex($fields->getTimestamp()->toString());
    }

    public function toUuidV1(): UuidV1
    {
        $instance = ($this->unwrapped ?? $this->unwrap());

        if ($instance instanceof UuidV1) {
            return $instance;
        }

        assert($instance instanceof UuidV6);

        return $instance->toUuidV1();
    }

    public function toUuidV6(): UuidV6
    {
        $instance = ($this->unwrapped ?? $this->unwrap());

        assert($instance instanceof UuidV6);

        return $instance;
    }

    private function unwrap(): UuidInterface
    {
        return $this->unwrapped = (new UuidFactory())->fromString($this->uuid);
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid;

use Ramsey\Uuid\Builder\FallbackBuilder;
use Ramsey\Uuid\Builder\UuidBuilderInterface;
use Ramsey\Uuid\Codec\CodecInterface;
use Ramsey\Uuid\Codec\GuidStringCodec;
use Ramsey\Uuid\Codec\StringCodec;
use Ramsey\Uuid\Converter\Number\GenericNumberConverter;
use Ramsey\Uuid\Converter\NumberConverterInterface;
use Ramsey\Uuid\Converter\Time\GenericTimeConverter;
use Ramsey\Uuid\Converter\Time\PhpTimeConverter;
use Ramsey\Uuid\Converter\TimeConverterInterface;
use Ramsey\Uuid\Generator\DceSecurityGenerator;
use Ramsey\Uuid\Generator\DceSecurityGeneratorInterface;
use Ramsey\Uuid\Generator\NameGeneratorFactory;
use Ramsey\Uuid\Generator\NameGeneratorInterface;
use Ramsey\Uuid\Generator\PeclUuidNameGenerator;
use Ramsey\Uuid\Generator\PeclUuidRandomGenerator;
use Ramsey\Uuid\Generator\PeclUuidTimeGenerator;
use Ramsey\Uuid\Generator\RandomGeneratorFactory;
use Ramsey\Uuid\Generator\RandomGeneratorInterface;
use Ramsey\Uuid\Generator\TimeGeneratorFactory;
use Ramsey\Uuid\Generator\TimeGeneratorInterface;
use Ramsey\Uuid\Generator\UnixTimeGenerator;
use Ramsey\Uuid\Guid\GuidBuilder;
use Ramsey\Uuid\Math\BrickMathCalculator;
use Ramsey\Uuid\Math\CalculatorInterface;
use Ramsey\Uuid\Nonstandard\UuidBuilder as NonstandardUuidBuilder;
use Ramsey\Uuid\Provider\Dce\SystemDceSecurityProvider;
use Ramsey\Uuid\Provider\DceSecurityProviderInterface;
use Ramsey\Uuid\Provider\Node\FallbackNodeProvider;
use Ramsey\Uuid\Provider\Node\RandomNodeProvider;
use Ramsey\Uuid\Provider\Node\SystemNodeProvider;
use Ramsey\Uuid\Provider\NodeProviderInterface;
use Ramsey\Uuid\Provider\Time\SystemTimeProvider;
use Ramsey\Uuid\Provider\TimeProviderInterface;
use Ramsey\Uuid\Rfc4122\UuidBuilder as Rfc4122UuidBuilder;
use Ramsey\Uuid\Validator\GenericValidator;
use Ramsey\Uuid\Validator\ValidatorInterface;

use const PHP_INT_SIZE;

/**
 * FeatureSet detects and exposes available features in the current environment
 *
 * A feature set is used by UuidFactory to determine the available features and capabilities of the environment.
 */
class FeatureSet
{
    private ?TimeProviderInterface $timeProvider = null;
    private CalculatorInterface $calculator;
    private CodecInterface $codec;
    private DceSecurityGeneratorInterface $dceSecurityGenerator;
    private NameGeneratorInterface $nameGenerator;
    private NodeProviderInterface $nodeProvider;
    private NumberConverterInterface $numberConverter;
    private RandomGeneratorInterface $randomGenerator;
    private TimeConverterInterface $timeConverter;
    private TimeGeneratorInterface $timeGenerator;
    private TimeGeneratorInterface $unixTimeGenerator;
    private UuidBuilderInterface $builder;
    private ValidatorInterface $validator;

    /**
     * @param bool $useGuids True build UUIDs using the GuidStringCodec
     * @param bool $force32Bit True to force the use of 32-bit functionality (primarily for testing purposes)
     * @param bool $forceNoBigNumber (obsolete)
     * @param bool $ignoreSystemNode True to disable attempts to check for the system node ID (primarily for testing purposes)
     * @param bool $enablePecl True to enable the use of the PeclUuidTimeGenerator to generate version 1 UUIDs
     *
     * @phpstan-ignore constructor.unusedParameter ($forceNoBigNumber is deprecated)
     */
    public function __construct(
        bool $useGuids = false,
        private bool $force32Bit = false,
        bool $forceNoBigNumber = false,
        private bool $ignoreSystemNode = false,
        private bool $enablePecl = false,
    ) {
        $this->randomGenerator = $this->buildRandomGenerator();
        $this->setCalculator(new BrickMathCalculator());
        $this->builder = $this->buildUuidBuilder($useGuids);
        $this->codec = $this->buildCodec($useGuids);
        $this->nodeProvider = $this->buildNodeProvider();
        $this->nameGenerator = $this->buildNameGenerator();
        $this->setTimeProvider(new SystemTimeProvider());
        $this->setDceSecurityProvider(new SystemDceSecurityProvider());
        $this->validator = new GenericValidator();

        assert($this->timeProvider !== null);
        $this->unixTimeGenerator = $this->buildUnixTimeGenerator();
    }

    /**
     * Returns the builder configured for this environment
     */
    public function getBuilder(): UuidBuilderInterface
    {
        return $this->builder;
    }

    /**
     * Returns the calculator configured for this environment
     */
    public function getCalculator(): CalculatorInterface
    {
        return $this->calculator;
    }

    /**
     * Returns the codec configured for this environment
     */
    public function getCodec(): CodecInterface
    {
        return $this->codec;
    }

    /**
     * Returns the DCE Security generator configured for this environment
     */
    public function getDceSecurityGenerator(): DceSecurityGeneratorInterface
    {
        return $this->dceSecurityGenerator;
    }

    /**
     * Returns the name generator configured for this environment
     */
    public function getNameGenerator(): NameGeneratorInterface
    {
        return $this->nameGenerator;
    }

    /**
     * Returns the node provider configured for this environment
     */
    public function getNodeProvider(): NodeProviderInterface
    {
        return $this->nodeProvider;
    }

    /**
     * Returns the number converter configured for this environment
     */
    public function getNumberConverter(): NumberConverterInterface
    {
        return $this->numberConverter;
    }

    /**
     * Returns the random generator configured for this environment
     */
    public function getRandomGenerator(): RandomGeneratorInterface
    {
        return $this->randomGenerator;
    }

    /**
     * Returns the time converter configured for this environment
     */
    public function getTimeConverter(): TimeConverterInterface
    {
        return $this->timeConverter;
    }

    /**
     * Returns the time generator configured for this environment
     */
    public function getTimeGenerator(): TimeGeneratorInterface
    {
        return $this->timeGenerator;
    }

    /**
     * Returns the Unix Epoch time generator configured for this environment
     */
    public function getUnixTimeGenerator(): TimeGeneratorInterface
    {
        return $this->unixTimeGenerator;
    }

    /**
     * Returns the validator configured for this environment
     */
    public function getValidator(): ValidatorInterface
    {
        return $this->validator;
    }

    /**
     * Sets the calculator to use in this environment
     */
    public function setCalculator(CalculatorInterface $calculator): void
    {
        $this->calculator = $calculator;
        $this->numberConverter = $this->buildNumberConverter($calculator);
        $this->timeConverter = $this->buildTimeConverter($calculator);

        if (isset($this->timeProvider)) {
            $this->timeGenerator = $this->buildTimeGenerator($this->timeProvider);
        }
    }

    /**
     * Sets the DCE Security provider to use in this environment
     */
    public function setDceSecurityProvider(DceSecurityProviderInterface $dceSecurityProvider): void
    {
        $this->dceSecurityGenerator = $this->buildDceSecurityGenerator($dceSecurityProvider);
    }

    /**
     * Sets the node provider to use in this environment
     */
    public function setNodeProvider(NodeProviderInterface $nodeProvider): void
    {
        $this->nodeProvider = $nodeProvider;

        if (isset($this->timeProvider)) {
            $this->timeGenerator = $this->buildTimeGenerator($this->timeProvider);
        }
    }

    /**
     * Sets the time provider to use in this environment
     */
    public function setTimeProvider(TimeProviderInterface $timeProvider): void
    {
        $this->timeProvider = $timeProvider;
        $this->timeGenerator = $this->buildTimeGenerator($timeProvider);
    }

    /**
     * Set the validator to use in this environment
     */
    public function setValidator(ValidatorInterface $validator): void
    {
        $this->validator = $validator;
    }

    /**
     * Returns a codec configured for this environment
     *
     * @param bool $useGuids Whether to build UUIDs using the GuidStringCodec
     */
    private function buildCodec(bool $useGuids = false): CodecInterface
    {
        if ($useGuids) {
            return new GuidStringCodec($this->builder);
        }

        return new StringCodec($this->builder);
    }

    /**
     * Returns a DCE Security generator configured for this environment
     */
    private function buildDceSecurityGenerator(
        DceSecurityProviderInterface $dceSecurityProvider,
    ): DceSecurityGeneratorInterface {
        return new DceSecurityGenerator($this->numberConverter, $this->timeGenerator, $dceSecurityProvider);
    }

    /**
     * Returns a node provider configured for this environment
     */
    private function buildNodeProvider(): NodeProviderInterface
    {
        if ($this->ignoreSystemNode) {
            return new RandomNodeProvider();
        }

        return new FallbackNodeProvider([new SystemNodeProvider(), new RandomNodeProvider()]);
    }

    /**
     * Returns a number converter configured for this environment
     */
    private function buildNumberConverter(CalculatorInterface $calculator): NumberConverterInterface
    {
        return new GenericNumberConverter($calculator);
    }

    /**
     * Returns a random generator configured for this environment
     */
    private function buildRandomGenerator(): RandomGeneratorInterface
    {
        if ($this->enablePecl) {
            return new PeclUuidRandomGenerator();
        }

        return (new RandomGeneratorFactory())->getGenerator();
    }

    /**
     * Returns a time generator configured for this environment
     *
     * @param TimeProviderInterface $timeProvider The time provider to use with
     *     the time generator
     */
    private function buildTimeGenerator(TimeProviderInterface $timeProvider): TimeGeneratorInterface
    {
        if ($this->enablePecl) {
            return new PeclUuidTimeGenerator();
        }

        return (new TimeGeneratorFactory($this->nodeProvider, $this->timeConverter, $timeProvider))->getGenerator();
    }

    /**
     * Returns a Unix Epoch time generator configured for this environment
     */
    private function buildUnixTimeGenerator(): TimeGeneratorInterface
    {
        return new UnixTimeGenerator($this->randomGenerator);
    }

    /**
     * Returns a name generator configured for this environment
     */
    private function buildNameGenerator(): NameGeneratorInterface
    {
        if ($this->enablePecl) {
            return new PeclUuidNameGenerator();
        }

        return (new NameGeneratorFactory())->getGenerator();
    }

    /**
     * Returns a time converter configured for this environment
     */
    private function buildTimeConverter(CalculatorInterface $calculator): TimeConverterInterface
    {
        $genericConverter = new GenericTimeConverter($calculator);

        if ($this->is64BitSystem()) {
            return new PhpTimeConverter($calculator, $genericConverter);
        }

        return $genericConverter;
    }

    /**
     * Returns a UUID builder configured for this environment
     *
     * @param bool $useGuids Whether to build UUIDs using the GuidStringCodec
     */
    private function buildUuidBuilder(bool $useGuids = false): UuidBuilderInterface
    {
        if ($useGuids) {
            return new GuidBuilder($this->numberConverter, $this->timeConverter);
        }

        return new FallbackBuilder([
            new Rfc4122UuidBuilder($this->numberConverter, $this->timeConverter),
            new NonstandardUuidBuilder($this->numberConverter, $this->timeConverter),
        ]);
    }

    /**
     * Returns true if the PHP build is 64-bit
     */
    private function is64BitSystem(): bool
    {
        return PHP_INT_SIZE === 8 && !$this->force32Bit;
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Type;

use Ramsey\Uuid\Exception\InvalidArgumentException;
use ValueError;

use function assert;
use function is_numeric;
use function preg_match;
use function sprintf;
use function substr;

/**
 * A value object representing an integer
 *
 * This class exists for type-safety purposes, to ensure that integers returned from ramsey/uuid methods as strings are
 * truly integers and not some other kind of string.
 *
 * To support large integers beyond PHP_INT_MAX and PHP_INT_MIN on both 64-bit and 32-bit systems, we store the integers
 * as strings.
 *
 * @immutable
 */
final class Integer implements NumberInterface
{
    /**
     * @var numeric-string
     */
    private string $value;

    /**
     * @phpstan-ignore property.readOnlyByPhpDocDefaultValue
     */
    private bool $isNegative = false;

    public function __construct(self | float | int | string $value)
    {
        $this->value = $value instanceof self ? (string) $value : $this->prepareValue($value);
    }

    public function isNegative(): bool
    {
        return $this->isNegative;
    }

    /**
     * @return numeric-string
     *
     * @pure
     */
    public function toString(): string
    {
        return $this->value;
    }

    /**
     * @return numeric-string
     */
    public function __toString(): string
    {
        return $this->toString();
    }

    public function jsonSerialize(): string
    {
        return $this->toString();
    }

    public function serialize(): string
    {
        return $this->toString();
    }

    /**
     * @return array{string: string}
     */
    public function __serialize(): array
    {
        return ['string' => $this->toString()];
    }

    /**
     * Constructs the object from a serialized string representation
     *
     * @param string $data The serialized string representation of the object
     */
    public function unserialize(string $data): void
    {
        $this->__construct($data);
    }

    /**
     * @param array{string?: string} $data
     */
    public function __unserialize(array $data): void
    {
        // @codeCoverageIgnoreStart
        if (!isset($data['string'])) {
            throw new ValueError(sprintf('%s(): Argument #1 ($data) is invalid', __METHOD__));
        }
        // @codeCoverageIgnoreEnd

        $this->unserialize($data['string']);
    }

    /**
     * @return numeric-string
     */
    private function prepareValue(float | int | string $value): string
    {
        $value = (string) $value;
        $sign = '+';

        // If the value contains a sign, remove it for the digit pattern check.
        if (str_starts_with($value, '-') || str_starts_with($value, '+')) {
            $sign = substr($value, 0, 1);
            $value = substr($value, 1);
        }

        if (!preg_match('/^\d+$/', $value)) {
            throw new InvalidArgumentException(
                'Value must be a signed integer or a string containing only '
                . 'digits 0-9 and, optionally, a sign (+ or -)'
            );
        }

        // Trim any leading zeros.
        $value = ltrim($value, '0');

        // Set to zero if the string is empty after trimming zeros.
        if ($value === '') {
            $value = '0';
        }

        // Add the negative sign back to the value.
        if ($sign === '-' && $value !== '0') {
            $value = $sign . $value;

            /** @phpstan-ignore property.readOnlyByPhpDocAssignNotInConstructor */
            $this->isNegative = true;
        }

        assert(is_numeric($value));

        return $value;
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Type;

/**
 * NumberInterface ensures consistency in numeric values returned by ramsey/uuid
 *
 * @immutable
 */
interface NumberInterface extends TypeInterface
{
    /**
     * Returns true if this number is less than zero
     */
    public function isNegative(): bool;
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Type;

use JsonSerializable;
use Serializable;

/**
 * TypeInterface ensures consistency in typed values returned by ramsey/uuid
 *
 * @immutable
 */
interface TypeInterface extends JsonSerializable, Serializable
{
    /**
     * @pure
     */
    public function toString(): string;

    /**
     * @pure
     */
    public function __toString(): string;
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Type;

use Ramsey\Uuid\Exception\InvalidArgumentException;
use ValueError;

use function preg_match;
use function sprintf;
use function substr;

/**
 * A value object representing a hexadecimal number
 *
 * This class exists for type-safety purposes, to ensure that hexadecimal numbers returned from ramsey/uuid methods as
 * strings are truly hexadecimal and not some other kind of string.
 *
 * @immutable
 */
final class Hexadecimal implements TypeInterface
{
    /**
     * @var non-empty-string
     */
    private string $value;

    /**
     * @param self | string $value The hexadecimal value to store
     */
    public function __construct(self | string $value)
    {
        $this->value = $value instanceof self ? (string) $value : $this->prepareValue($value);
    }

    /**
     * @return non-empty-string
     *
     * @pure
     */
    public function toString(): string
    {
        return $this->value;
    }

    /**
     * @return non-empty-string
     */
    public function __toString(): string
    {
        return $this->toString();
    }

    /**
     * @return non-empty-string
     */
    public function jsonSerialize(): string
    {
        return $this->toString();
    }

    /**
     * @return non-empty-string
     */
    public function serialize(): string
    {
        return $this->toString();
    }

    /**
     * @return array{string: string}
     */
    public function __serialize(): array
    {
        return ['string' => $this->toString()];
    }

    /**
     * Constructs the object from a serialized string representation
     *
     * @param string $data The serialized string representation of the object
     */
    public function unserialize(string $data): void
    {
        $this->__construct($data);
    }

    /**
     * @param array{string?: string} $data
     */
    public function __unserialize(array $data): void
    {
        // @codeCoverageIgnoreStart
        if (!isset($data['string'])) {
            throw new ValueError(sprintf('%s(): Argument #1 ($data) is invalid', __METHOD__));
        }
        // @codeCoverageIgnoreEnd

        $this->unserialize($data['string']);
    }

    /**
     * @return non-empty-string
     */
    private function prepareValue(string $value): string
    {
        $value = strtolower($value);

        if (str_starts_with($value, '0x')) {
            $value = substr($value, 2);
        }

        if (!preg_match('/^[A-Fa-f0-9]+$/', $value)) {
            throw new InvalidArgumentException('Value must be a hexadecimal number');
        }

        /** @var non-empty-string */
        return $value;
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Type;

use Ramsey\Uuid\Exception\InvalidArgumentException;
use ValueError;

use function is_numeric;
use function sprintf;
use function str_starts_with;

/**
 * A value object representing a decimal
 *
 * This class exists for type-safety purposes, to ensure that decimals returned from ramsey/uuid methods as strings are
 * truly decimals and not some other kind of string.
 *
 * To support values as true decimals and not as floats or doubles, we store the decimals as strings.
 *
 * @immutable
 */
final class Decimal implements NumberInterface
{
    private string $value;
    private bool $isNegative;

    public function __construct(float | int | string | self $value)
    {
        $value = (string) $value;

        if (!is_numeric($value)) {
            throw new InvalidArgumentException(
                'Value must be a signed decimal or a string containing only '
                . 'digits 0-9 and, optionally, a decimal point or sign (+ or -)'
            );
        }

        // Remove the leading +-symbol.
        if (str_starts_with($value, '+')) {
            $value = substr($value, 1);
        }

        // For cases like `-0` or `-0.0000`, convert the value to `0`.
        if (abs((float) $value) === 0.0) {
            $value = '0';
        }

        if (str_starts_with($value, '-')) {
            $this->isNegative = true;
        } else {
            $this->isNegative = false;
        }

        $this->value = $value;
    }

    public function isNegative(): bool
    {
        return $this->isNegative;
    }

    public function toString(): string
    {
        return $this->value;
    }

    public function __toString(): string
    {
        return $this->toString();
    }

    public function jsonSerialize(): string
    {
        return $this->toString();
    }

    public function serialize(): string
    {
        return $this->toString();
    }

    /**
     * @return array{string: string}
     */
    public function __serialize(): array
    {
        return ['string' => $this->toString()];
    }

    /**
     * Constructs the object from a serialized string representation
     *
     * @param string $data The serialized string representation of the object
     */
    public function unserialize(string $data): void
    {
        $this->__construct($data);
    }

    /**
     * @param array{string?: string} $data
     */
    public function __unserialize(array $data): void
    {
        // @codeCoverageIgnoreStart
        if (!isset($data['string'])) {
            throw new ValueError(sprintf('%s(): Argument #1 ($data) is invalid', __METHOD__));
        }
        // @codeCoverageIgnoreEnd

        $this->unserialize($data['string']);
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Type;

use Ramsey\Uuid\Exception\UnsupportedOperationException;
use Ramsey\Uuid\Type\Integer as IntegerObject;
use ValueError;

use function json_decode;
use function json_encode;
use function sprintf;

/**
 * A value object representing a timestamp
 *
 * This class exists for type-safety purposes, to ensure that timestamps used by ramsey/uuid are truly timestamp
 * integers and not some other kind of string or integer.
 *
 * @immutable
 */
final class Time implements TypeInterface
{
    private IntegerObject $seconds;
    private IntegerObject $microseconds;

    public function __construct(
        IntegerObject | float | int | string $seconds,
        IntegerObject | float | int | string $microseconds = 0,
    ) {
        $this->seconds = new IntegerObject($seconds);
        $this->microseconds = new IntegerObject($microseconds);
    }

    /**
     * @pure
     */
    public function getSeconds(): IntegerObject
    {
        return $this->seconds;
    }

    /**
     * @pure
     */
    public function getMicroseconds(): IntegerObject
    {
        return $this->microseconds;
    }

    public function toString(): string
    {
        return $this->seconds->toString() . '.' . sprintf('%06s', $this->microseconds->toString());
    }

    public function __toString(): string
    {
        return $this->toString();
    }

    /**
     * @return string[]
     */
    public function jsonSerialize(): array
    {
        return [
            'seconds' => $this->getSeconds()->toString(),
            'microseconds' => $this->getMicroseconds()->toString(),
        ];
    }

    public function serialize(): string
    {
        return (string) json_encode($this);
    }

    /**
     * @return array{seconds: string, microseconds: string}
     */
    public function __serialize(): array
    {
        return [
            'seconds' => $this->getSeconds()->toString(),
            'microseconds' => $this->getMicroseconds()->toString(),
        ];
    }

    /**
     * Constructs the object from a serialized string representation
     *
     * @param string $data The serialized string representation of the object
     */
    public function unserialize(string $data): void
    {
        /** @var array{seconds?: float | int | string, microseconds?: float | int | string} $time */
        $time = json_decode($data, true);

        if (!isset($time['seconds']) || !isset($time['microseconds'])) {
            throw new UnsupportedOperationException('Attempted to unserialize an invalid value');
        }

        $this->__construct($time['seconds'], $time['microseconds']);
    }

    /**
     * @param array{seconds?: string, microseconds?: string} $data
     */
    public function __unserialize(array $data): void
    {
        // @codeCoverageIgnoreStart
        if (!isset($data['seconds']) || !isset($data['microseconds'])) {
            throw new ValueError(sprintf('%s(): Argument #1 ($data) is invalid', __METHOD__));
        }
        // @codeCoverageIgnoreEnd

        $this->__construct($data['seconds'], $data['microseconds']);
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Provider\Dce;

use Ramsey\Uuid\Exception\DceSecurityException;
use Ramsey\Uuid\Provider\DceSecurityProviderInterface;
use Ramsey\Uuid\Type\Integer as IntegerObject;

use function escapeshellarg;
use function preg_split;
use function str_getcsv;
use function strrpos;
use function strtolower;
use function strtoupper;
use function substr;
use function trim;

use const PREG_SPLIT_NO_EMPTY;

/**
 * SystemDceSecurityProvider retrieves the user or group identifiers from the system
 */
class SystemDceSecurityProvider implements DceSecurityProviderInterface
{
    /**
     * @throws DceSecurityException if unable to get a user identifier
     *
     * @inheritDoc
     */
    public function getUid(): IntegerObject
    {
        /** @var IntegerObject | int | float | string | null $uid */
        static $uid = null;

        if ($uid instanceof IntegerObject) {
            return $uid;
        }

        if ($uid === null) {
            $uid = $this->getSystemUid();
        }

        if ($uid === '') {
            throw new DceSecurityException(
                'Unable to get a user identifier using the system DCE Security provider; please provide a custom '
                . 'identifier or use a different provider',
            );
        }

        $uid = new IntegerObject($uid);

        return $uid;
    }

    /**
     * @throws DceSecurityException if unable to get a group identifier
     *
     * @inheritDoc
     */
    public function getGid(): IntegerObject
    {
        /** @var IntegerObject | int | float | string | null $gid */
        static $gid = null;

        if ($gid instanceof IntegerObject) {
            return $gid;
        }

        if ($gid === null) {
            $gid = $this->getSystemGid();
        }

        if ($gid === '') {
            throw new DceSecurityException(
                'Unable to get a group identifier using the system DCE Security provider; please provide a custom '
                . 'identifier or use a different provider',
            );
        }

        $gid = new IntegerObject($gid);

        return $gid;
    }

    /**
     * Returns the UID from the system
     */
    private function getSystemUid(): string
    {
        if (!$this->hasShellExec()) {
            return '';
        }

        return match ($this->getOs()) {
            'WIN' => $this->getWindowsUid(),
            default => trim((string) shell_exec('id -u')),
        };
    }

    /**
     * Returns the GID from the system
     */
    private function getSystemGid(): string
    {
        if (!$this->hasShellExec()) {
            return '';
        }

        return match ($this->getOs()) {
            'WIN' => $this->getWindowsGid(),
            default => trim((string) shell_exec('id -g')),
        };
    }

    /**
     * Returns true if shell_exec() is available for use
     */
    private function hasShellExec(): bool
    {
        return !str_contains(strtolower((string) ini_get('disable_functions')), 'shell_exec');
    }

    /**
     * Returns the PHP_OS string
     */
    private function getOs(): string
    {
        /** @var string $phpOs */
        $phpOs = constant('PHP_OS');

        return strtoupper(substr($phpOs, 0, 3));
    }

    /**
     * Returns the user identifier for a user on a Windows system
     *
     * Windows does not have the same concept as an effective POSIX UID for the running script. Instead, each user is
     * uniquely identified by an SID (security identifier). The SID includes three 32-bit unsigned integers that make up
     * a unique domain identifier, followed by an RID (relative identifier) that we will use as the UID. The primary
     * caveat is that this UID may not be unique to the system, since it is, instead, unique to the domain.
     *
     * @link https://www.lifewire.com/what-is-an-sid-number-2626005 What Is an SID Number?
     * @link https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/81d92bba-d22b-4a8c-908a-554ab29148ab Well-known SID Structures
     * @link https://learn.microsoft.com/en-us/windows-server/identity/ad-ds/manage/understand-security-identifiers#well-known-sids Well-known SIDs
     * @link https://www.windows-commandline.com/get-sid-of-user/ Get SID of user
     */
    private function getWindowsUid(): string
    {
        $response = shell_exec('whoami /user /fo csv /nh');

        if ($response === null) {
            return '';
        }

        $sid = str_getcsv(trim((string) $response), escape: '\\')[1] ?? '';

        if (($lastHyphen = strrpos($sid, '-')) === false) {
            return '';
        }

        return trim(substr($sid, $lastHyphen + 1));
    }

    /**
     * Returns a group identifier for a user on a Windows system
     *
     * Since Windows does not have the same concept as an effective POSIX GID for the running script, we will get the
     * local group memberships for the user running the script. Then, we will get the SID (security identifier) for the
     * first group that appears in that list. Finally, we will return the RID (relative identifier) for the group and
     * use that as the GID.
     *
     * @link https://www.windows-commandline.com/list-of-user-groups-command-line/ List of user groups command line
     */
    private function getWindowsGid(): string
    {
        $response = shell_exec('net user %username% | findstr /b /i "Local Group Memberships"');

        if ($response === null) {
            return '';
        }

        $userGroups = preg_split('/\s{2,}/', (string) $response, -1, PREG_SPLIT_NO_EMPTY);
        $firstGroup = trim($userGroups[1] ?? '', "* \t\n\r\0\x0B");

        if ($firstGroup === '') {
            return '';
        }

        $response = shell_exec('wmic group get name,sid | findstr /b /i ' . escapeshellarg($firstGroup));

        if ($response === null) {
            return '';
        }

        $userGroup = preg_split('/\s{2,}/', (string) $response, -1, PREG_SPLIT_NO_EMPTY);
        $sid = $userGroup[1] ?? '';

        if (($lastHyphen = strrpos($sid, '-')) === false) {
            return '';
        }

        return trim(substr($sid, $lastHyphen + 1));
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Provider;

use Ramsey\Uuid\Rfc4122\UuidV2;
use Ramsey\Uuid\Type\Integer as IntegerObject;

/**
 * A DCE provider provides access to local domain identifiers for version 2, DCE Security, UUIDs
 *
 * @see UuidV2
 */
interface DceSecurityProviderInterface
{
    /**
     * Returns a user identifier for the system
     *
     * @link https://en.wikipedia.org/wiki/User_identifier User identifier
     */
    public function getUid(): IntegerObject;

    /**
     * Returns a group identifier for the system
     *
     * @link https://en.wikipedia.org/wiki/Group_identifier Group identifier
     */
    public function getGid(): IntegerObject;
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Provider\Node;

use Ramsey\Uuid\Exception\InvalidArgumentException;
use Ramsey\Uuid\Provider\NodeProviderInterface;
use Ramsey\Uuid\Type\Hexadecimal;

use function dechex;
use function hexdec;
use function str_pad;
use function substr;

use const STR_PAD_LEFT;

/**
 * StaticNodeProvider provides a static node value with the multicast bit set
 *
 * @link https://www.rfc-editor.org/rfc/rfc9562#section-6.10 RFC 9562, 6.10. UUIDs That Do Not Identify the Host
 */
class StaticNodeProvider implements NodeProviderInterface
{
    private Hexadecimal $node;

    /**
     * @param Hexadecimal $node The static node value to use
     */
    public function __construct(Hexadecimal $node)
    {
        if (strlen($node->toString()) > 12) {
            throw new InvalidArgumentException('Static node value cannot be greater than 12 hexadecimal characters');
        }

        $this->node = $this->setMulticastBit($node);
    }

    public function getNode(): Hexadecimal
    {
        return $this->node;
    }

    /**
     * Set the multicast bit for the static node value
     */
    private function setMulticastBit(Hexadecimal $node): Hexadecimal
    {
        $nodeHex = str_pad($node->toString(), 12, '0', STR_PAD_LEFT);
        $firstOctet = substr($nodeHex, 0, 2);
        $firstOctet = str_pad(dechex(hexdec($firstOctet) | 0x01), 2, '0', STR_PAD_LEFT);

        return new Hexadecimal($firstOctet . substr($nodeHex, 2));
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Provider\Node;

use Ramsey\Uuid\Exception\NodeException;
use Ramsey\Uuid\Provider\NodeProviderInterface;
use Ramsey\Uuid\Type\Hexadecimal;

use function array_filter;
use function array_map;
use function array_walk;
use function count;
use function ob_get_clean;
use function ob_start;
use function preg_match;
use function preg_match_all;
use function reset;
use function str_contains;
use function str_replace;
use function strtolower;
use function strtoupper;
use function substr;

use const GLOB_NOSORT;
use const PREG_PATTERN_ORDER;

/**
 * SystemNodeProvider retrieves the system node ID, if possible
 *
 * The system node ID, or host ID, is often the same as the MAC address for a network interface on the host.
 */
class SystemNodeProvider implements NodeProviderInterface
{
    /**
     * Pattern to match nodes in `ifconfig` and `ipconfig` output.
     */
    private const IFCONFIG_PATTERN = '/[^:]([0-9a-f]{2}([:-])[0-9a-f]{2}(\2[0-9a-f]{2}){4})[^:]/i';

    /**
     * Pattern to match nodes in sysfs stream output.
     */
    private const SYSFS_PATTERN = '/^([0-9a-f]{2}:){5}[0-9a-f]{2}$/i';

    public function getNode(): Hexadecimal
    {
        $node = $this->getNodeFromSystem();

        if ($node === '') {
            throw new NodeException('Unable to fetch a node for this system');
        }

        return new Hexadecimal($node);
    }

    /**
     * Returns the system node if found
     */
    protected function getNodeFromSystem(): string
    {
        /** @var string | null $node */
        static $node = null;

        if ($node !== null) {
            return $node;
        }

        // First, try a Linux-specific approach.
        $node = $this->getSysfs();

        if ($node === '') {
            // Search ifconfig output for MAC addresses & return the first one.
            $node = $this->getIfconfig();
        }

        $node = str_replace([':', '-'], '', $node);

        return $node;
    }

    /**
     * Returns the network interface configuration for the system
     *
     * @codeCoverageIgnore
     */
    protected function getIfconfig(): string
    {
        if (str_contains(strtolower((string) ini_get('disable_functions')), 'passthru')) {
            return '';
        }

        /** @var string $phpOs */
        $phpOs = constant('PHP_OS');

        ob_start();
        switch (strtoupper(substr($phpOs, 0, 3))) {
            case 'WIN':
                passthru('ipconfig /all 2>&1');

                break;
            case 'DAR':
                passthru('ifconfig 2>&1');

                break;
            case 'FRE':
                passthru('netstat -i -f link 2>&1');

                break;
            case 'LIN':
            default:
                passthru('netstat -ie 2>&1');

                break;
        }

        $ifconfig = (string) ob_get_clean();

        if (preg_match_all(self::IFCONFIG_PATTERN, $ifconfig, $matches, PREG_PATTERN_ORDER)) {
            foreach ($matches[1] as $iface) {
                if ($iface !== '00:00:00:00:00:00' && $iface !== '00-00-00-00-00-00') {
                    return $iface;
                }
            }
        }

        return '';
    }

    /**
     * Returns MAC address from the first system interface via the sysfs interface
     */
    protected function getSysfs(): string
    {
        /** @var string $phpOs */
        $phpOs = constant('PHP_OS');

        if (strtoupper($phpOs) !== 'LINUX') {
            return '';
        }

        $addressPaths = glob('/sys/class/net/*/address', GLOB_NOSORT);

        if ($addressPaths === false || count($addressPaths) === 0) {
            return '';
        }

        /** @var array<array-key, string> $macs */
        $macs = [];

        array_walk($addressPaths, function (string $addressPath) use (&$macs): void {
            if (is_readable($addressPath)) {
                $macs[] = file_get_contents($addressPath);
            }
        });

        /** @var callable $trim */
        $trim = 'trim';

        $macs = array_map($trim, $macs);

        // Remove invalid entries.
        $macs = array_filter($macs, function (mixed $address): bool {
            assert(is_string($address));

            return $address !== '00:00:00:00:00:00' && preg_match(self::SYSFS_PATTERN, $address);
        });

        /** @var bool | string $mac */
        $mac = reset($macs);

        return (string) $mac;
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Provider\Node;

use Ramsey\Uuid\Exception\NodeException;
use Ramsey\Uuid\Provider\NodeProviderInterface;
use Ramsey\Uuid\Type\Hexadecimal;

/**
 * FallbackNodeProvider retrieves the system node ID by stepping through a list of providers until a node ID can be obtained
 */
class FallbackNodeProvider implements NodeProviderInterface
{
    /**
     * @param iterable<NodeProviderInterface> $providers Array of node providers
     */
    public function __construct(private iterable $providers)
    {
    }

    public function getNode(): Hexadecimal
    {
        $lastProviderException = null;

        foreach ($this->providers as $provider) {
            try {
                return $provider->getNode();
            } catch (NodeException $exception) {
                $lastProviderException = $exception;

                continue;
            }
        }

        throw new NodeException(message: 'Unable to find a suitable node provider', previous: $lastProviderException);
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Provider\Node;

use Ramsey\Collection\AbstractCollection;
use Ramsey\Uuid\Provider\NodeProviderInterface;
use Ramsey\Uuid\Type\Hexadecimal;

/**
 * A collection of NodeProviderInterface objects
 *
 * @deprecated this class has been deprecated and will be removed in 5.0.0. The use-case for this class comes from a
 *     pre-`phpstan/phpstan` and pre-`vimeo/psalm` ecosystem, in which type safety had to be mostly enforced at runtime:
 *     that is no longer necessary, now that you can safely verify your code to be correct and use more generic types
 *     like `iterable<T>` instead.
 *
 * @extends AbstractCollection<NodeProviderInterface>
 */
class NodeProviderCollection extends AbstractCollection
{
    public function getType(): string
    {
        return NodeProviderInterface::class;
    }

    /**
     * Re-constructs the object from its serialized form
     *
     * @param string $serialized The serialized PHP string to unserialize into a UuidInterface instance
     */
    public function unserialize($serialized): void
    {
        /** @var array<array-key, NodeProviderInterface> $data */
        $data = unserialize($serialized, [
            'allowed_classes' => [
                Hexadecimal::class,
                RandomNodeProvider::class,
                StaticNodeProvider::class,
                SystemNodeProvider::class,
            ],
        ]);

        /** @phpstan-ignore-next-line */
        $this->data = array_filter($data, fn ($unserialized): bool => $unserialized instanceof NodeProviderInterface);
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Provider\Node;

use Ramsey\Uuid\Exception\RandomSourceException;
use Ramsey\Uuid\Provider\NodeProviderInterface;
use Ramsey\Uuid\Type\Hexadecimal;
use Throwable;

use function bin2hex;
use function dechex;
use function hex2bin;
use function hexdec;
use function str_pad;
use function substr;

use const STR_PAD_LEFT;

/**
 * RandomNodeProvider generates a random node ID
 *
 * @link https://www.rfc-editor.org/rfc/rfc9562#section-6.10 RFC 9562, 6.10. UUIDs That Do Not Identify the Host
 */
class RandomNodeProvider implements NodeProviderInterface
{
    public function getNode(): Hexadecimal
    {
        try {
            $nodeBytes = random_bytes(6);
        } catch (Throwable $exception) {
            throw new RandomSourceException($exception->getMessage(), (int) $exception->getCode(), $exception);
        }

        // Split the node bytes for math on 32-bit systems.
        $nodeMsb = substr($nodeBytes, 0, 3);
        $nodeLsb = substr($nodeBytes, 3);

        // Set the multicast bit; see RFC 9562, section 6.10.
        $nodeMsb = hex2bin(str_pad(dechex(hexdec(bin2hex($nodeMsb)) | 0x010000), 6, '0', STR_PAD_LEFT));

        return new Hexadecimal(str_pad(bin2hex($nodeMsb . $nodeLsb), 12, '0', STR_PAD_LEFT));
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Provider\Time;

use Ramsey\Uuid\Provider\TimeProviderInterface;
use Ramsey\Uuid\Type\Integer as IntegerObject;
use Ramsey\Uuid\Type\Time;

/**
 * FixedTimeProvider uses a known time to provide the time
 *
 * This provider allows the use of a previously generated, or known, time when generating time-based UUIDs.
 */
class FixedTimeProvider implements TimeProviderInterface
{
    public function __construct(private Time $time)
    {
    }

    /**
     * Sets the `usec` component of the time
     *
     * @param IntegerObject | int | string $value The `usec` value to set
     */
    public function setUsec($value): void
    {
        $this->time = new Time($this->time->getSeconds(), $value);
    }

    /**
     * Sets the `sec` component of the time
     *
     * @param IntegerObject | int | string $value The `sec` value to set
     */
    public function setSec($value): void
    {
        $this->time = new Time($value, $this->time->getMicroseconds());
    }

    public function getTime(): Time
    {
        return $this->time;
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Provider\Time;

use Ramsey\Uuid\Provider\TimeProviderInterface;
use Ramsey\Uuid\Type\Time;

use function gettimeofday;

/**
 * SystemTimeProvider retrieves the current time using built-in PHP functions
 */
class SystemTimeProvider implements TimeProviderInterface
{
    public function getTime(): Time
    {
        $time = gettimeofday();

        return new Time($time['sec'], $time['usec']);
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Provider;

use Ramsey\Uuid\Type\Hexadecimal;

/**
 * A node provider retrieves or generates a node ID
 */
interface NodeProviderInterface
{
    /**
     * Returns a node ID
     *
     * @return Hexadecimal The node ID as a hexadecimal string
     */
    public function getNode(): Hexadecimal;
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Provider;

use Ramsey\Uuid\Type\Time;

/**
 * A time provider retrieves the current time
 */
interface TimeProviderInterface
{
    /**
     * Returns a time object
     */
    public function getTime(): Time;
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid;

use BadMethodCallException;
use DateTimeInterface;
use Ramsey\Uuid\Codec\CodecInterface;
use Ramsey\Uuid\Converter\NumberConverterInterface;
use Ramsey\Uuid\Converter\TimeConverterInterface;
use Ramsey\Uuid\Exception\InvalidArgumentException;
use Ramsey\Uuid\Exception\UnsupportedOperationException;
use Ramsey\Uuid\Fields\FieldsInterface;
use Ramsey\Uuid\Lazy\LazyUuidFromString;
use Ramsey\Uuid\Rfc4122\FieldsInterface as Rfc4122FieldsInterface;
use Ramsey\Uuid\Type\Hexadecimal;
use Ramsey\Uuid\Type\Integer as IntegerObject;
use ValueError;

use function assert;
use function bin2hex;
use function method_exists;
use function preg_match;
use function sprintf;
use function str_replace;
use function strcmp;
use function strlen;
use function strtolower;
use function substr;

/**
 * Uuid provides constants and static methods for working with and generating UUIDs
 *
 * @immutable
 */
class Uuid implements UuidInterface
{
    use DeprecatedUuidMethodsTrait;

    /**
     * When this namespace is specified, the name string is a fully qualified domain name
     *
     * @link https://www.rfc-editor.org/rfc/rfc9562#section-6.6 RFC 9562, 6.6. Namespace ID Usage and Allocation
     */
    public const NAMESPACE_DNS = '6ba7b810-9dad-11d1-80b4-00c04fd430c8';

    /**
     * When this namespace is specified, the name string is a URL
     *
     * @link https://www.rfc-editor.org/rfc/rfc9562#section-6.6 RFC 9562, 6.6. Namespace ID Usage and Allocation
     */
    public const NAMESPACE_URL = '6ba7b811-9dad-11d1-80b4-00c04fd430c8';

    /**
     * When this namespace is specified, the name string is an ISO OID
     *
     * @link https://www.rfc-editor.org/rfc/rfc9562#section-6.6 RFC 9562, 6.6. Namespace ID Usage and Allocation
     */
    public const NAMESPACE_OID = '6ba7b812-9dad-11d1-80b4-00c04fd430c8';

    /**
     * When this namespace is specified, the name string is an X.500 DN (in DER or a text output format)
     *
     * @link https://www.rfc-editor.org/rfc/rfc9562#section-6.6 RFC 9562, 6.6. Namespace ID Usage and Allocation
     */
    public const NAMESPACE_X500 = '6ba7b814-9dad-11d1-80b4-00c04fd430c8';

    /**
     * The Nil UUID is a special form of UUID that is specified to have all 128 bits set to zero
     *
     * @link https://www.rfc-editor.org/rfc/rfc9562#section-5.9 RFC 9562, 5.9. Nil UUID
     */
    public const NIL = '00000000-0000-0000-0000-000000000000';

    /**
     * The Max UUID is a special form of UUID that is specified to have all 128 bits set to one
     *
     * @link https://www.rfc-editor.org/rfc/rfc9562#section-5.10 RFC 9562, 5.10. Max UUID
     */
    public const MAX = 'ffffffff-ffff-ffff-ffff-ffffffffffff';

    /**
     * Variant: reserved, NCS backward compatibility
     *
     * @link https://www.rfc-editor.org/rfc/rfc9562#section-4.1 RFC 9562, 4.1. Variant Field
     */
    public const RESERVED_NCS = 0;

    /**
     * Variant: the UUID layout specified in RFC 9562 (formerly RFC 4122)
     *
     * @link https://www.rfc-editor.org/rfc/rfc9562#section-4.1 RFC 9562, 4.1. Variant Field
     * @see Uuid::RFC_9562
     */
    public const RFC_4122 = 2;

    /**
     * Variant: the UUID layout specified in RFC 9562 (formerly RFC 4122)
     *
     * @link https://www.rfc-editor.org/rfc/rfc9562#section-4.1 RFC 9562, 4.1. Variant Field
     */
    public const RFC_9562 = 2;

    /**
     * Variant: reserved, Microsoft Corporation backward compatibility
     *
     * @link https://www.rfc-editor.org/rfc/rfc9562#section-4.1 RFC 9562, 4.1. Variant Field
     */
    public const RESERVED_MICROSOFT = 6;

    /**
     * Variant: reserved for future definition
     *
     * @link https://www.rfc-editor.org/rfc/rfc9562#section-4.1 RFC 9562, 4.1. Variant Field
     */
    public const RESERVED_FUTURE = 7;

    /**
     * @deprecated Use {@see ValidatorInterface::getPattern()} instead.
     */
    public const VALID_PATTERN = '^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}$';

    /**
     * Version 1 (Gregorian time) UUID
     *
     * @link https://www.rfc-editor.org/rfc/rfc9562#section-4.2 RFC 9562, 4.2. Version Field
     */
    public const UUID_TYPE_TIME = 1;

    /**
     * Version 2 (DCE Security) UUID
     *
     * @link https://www.rfc-editor.org/rfc/rfc9562#section-4.2 RFC 9562, 4.2. Version Field
     */
    public const UUID_TYPE_DCE_SECURITY = 2;

    /**
     * @deprecated Use {@see Uuid::UUID_TYPE_DCE_SECURITY} instead.
     */
    public const UUID_TYPE_IDENTIFIER = 2;

    /**
     * Version 3 (name-based and hashed with MD5) UUID
     *
     * @link https://www.rfc-editor.org/rfc/rfc9562#section-4.2 RFC 9562, 4.2. Version Field
     */
    public const UUID_TYPE_HASH_MD5 = 3;

    /**
     * Version 4 (random) UUID
     *
     * @link https://www.rfc-editor.org/rfc/rfc9562#section-4.2 RFC 9562, 4.2. Version Field
     */
    public const UUID_TYPE_RANDOM = 4;

    /**
     * Version 5 (name-based and hashed with SHA1) UUID
     *
     * @link https://www.rfc-editor.org/rfc/rfc9562#section-4.2 RFC 9562, 4.2. Version Field
     */
    public const UUID_TYPE_HASH_SHA1 = 5;

    /**
     * @deprecated Use {@see Uuid::UUID_TYPE_REORDERED_TIME} instead.
     */
    public const UUID_TYPE_PEABODY = 6;

    /**
     * Version 6 (reordered Gregorian time) UUID
     *
     * @link https://www.rfc-editor.org/rfc/rfc9562#section-4.2 RFC 9562, 4.2. Version Field
     */
    public const UUID_TYPE_REORDERED_TIME = 6;

    /**
     * Version 7 (Unix Epoch time) UUID
     *
     * @link https://www.rfc-editor.org/rfc/rfc9562#section-4.2 RFC 9562, 4.2. Version Field
     */
    public const UUID_TYPE_UNIX_TIME = 7;

    /**
     * Version 8 (custom format) UUID
     *
     * @link https://www.rfc-editor.org/rfc/rfc9562#section-4.2 RFC 9562, 4.2. Version Field
     */
    public const UUID_TYPE_CUSTOM = 8;

    /**
     * DCE Security principal domain
     *
     * @link https://pubs.opengroup.org/onlinepubs/9696989899/chap11.htm#tagcjh_14_05_01_01 DCE 1.1, Â§11.5.1.1
     */
    public const DCE_DOMAIN_PERSON = 0;

    /**
     * DCE Security group domain
     *
     * @link https://pubs.opengroup.org/onlinepubs/9696989899/chap11.htm#tagcjh_14_05_01_01 DCE 1.1, Â§11.5.1.1
     */
    public const DCE_DOMAIN_GROUP = 1;

    /**
     * DCE Security organization domain
     *
     * @link https://pubs.opengroup.org/onlinepubs/9696989899/chap11.htm#tagcjh_14_05_01_01 DCE 1.1, Â§11.5.1.1
     */
    public const DCE_DOMAIN_ORG = 2;

    /**
     * DCE Security domain string names
     *
     * @link https://pubs.opengroup.org/onlinepubs/9696989899/chap11.htm#tagcjh_14_05_01_01 DCE 1.1, Â§11.5.1.1
     */
    public const DCE_DOMAIN_NAMES = [
        self::DCE_DOMAIN_PERSON => 'person',
        self::DCE_DOMAIN_GROUP => 'group',
        self::DCE_DOMAIN_ORG => 'org',
    ];

    /**
     * @phpstan-ignore property.readOnlyByPhpDocDefaultValue
     */
    private static ?UuidFactoryInterface $factory = null;

    /**
     * @var bool flag to detect if the UUID factory was replaced internally, which disables all optimizations for the
     *     default/happy path internal scenarios
     * @phpstan-ignore property.readOnlyByPhpDocDefaultValue
     */
    private static bool $factoryReplaced = false;

    protected CodecInterface $codec;
    protected NumberConverterInterface $numberConverter;
    protected Rfc4122FieldsInterface $fields;
    protected TimeConverterInterface $timeConverter;

    /**
     * Creates a universally unique identifier (UUID) from an array of fields
     *
     * Unless you're making advanced use of this library to generate identifiers that deviate from RFC 9562 (formerly
     * RFC 4122), you probably do not want to instantiate a UUID directly. Use the static methods, instead:
     *
     * ```
     * use Ramsey\Uuid\Uuid;
     *
     * $timeBasedUuid = Uuid::uuid1();
     * $namespaceMd5Uuid = Uuid::uuid3(Uuid::NAMESPACE_URL, 'http://php.net/');
     * $randomUuid = Uuid::uuid4();
     * $namespaceSha1Uuid = Uuid::uuid5(Uuid::NAMESPACE_URL, 'http://php.net/');
     * ```
     *
     * @param Rfc4122FieldsInterface $fields The fields from which to construct a UUID
     * @param NumberConverterInterface $numberConverter The number converter to use for converting hex values to/from integers
     * @param CodecInterface $codec The codec to use when encoding or decoding UUID strings
     * @param TimeConverterInterface $timeConverter The time converter to use for converting timestamps extracted from a
     *     UUID to unix timestamps
     */
    public function __construct(
        Rfc4122FieldsInterface $fields,
        NumberConverterInterface $numberConverter,
        CodecInterface $codec,
        TimeConverterInterface $timeConverter,
    ) {
        $this->fields = $fields;
        $this->codec = $codec;
        $this->numberConverter = $numberConverter;
        $this->timeConverter = $timeConverter;
    }

    /**
     * @return non-empty-string
     */
    public function __toString(): string
    {
        return $this->toString();
    }

    /**
     * Converts the UUID to a string for JSON serialization
     */
    public function jsonSerialize(): string
    {
        return $this->toString();
    }

    /**
     * Converts the UUID to a string for PHP serialization
     */
    public function serialize(): string
    {
        return $this->codec->encode($this);
    }

    /**
     * @return array{bytes: string}
     */
    public function __serialize(): array
    {
        return ['bytes' => $this->serialize()];
    }

    /**
     * Re-constructs the object from its serialized form
     *
     * @param string $data The serialized PHP string to unserialize into a UuidInterface instance
     */
    public function unserialize(string $data): void
    {
        if (strlen($data) === 16) {
            /** @var Uuid $uuid */
            $uuid = self::getFactory()->fromBytes($data);
        } else {
            /** @var Uuid $uuid */
            $uuid = self::getFactory()->fromString($data);
        }

        /** @phpstan-ignore property.readOnlyByPhpDocAssignNotInConstructor */
        $this->codec = $uuid->codec;

        /** @phpstan-ignore property.readOnlyByPhpDocAssignNotInConstructor */
        $this->numberConverter = $uuid->numberConverter;

        /** @phpstan-ignore property.readOnlyByPhpDocAssignNotInConstructor */
        $this->fields = $uuid->fields;

        /** @phpstan-ignore property.readOnlyByPhpDocAssignNotInConstructor */
        $this->timeConverter = $uuid->timeConverter;
    }

    /**
     * @param array{bytes?: string} $data
     */
    public function __unserialize(array $data): void
    {
        // @codeCoverageIgnoreStart
        if (!isset($data['bytes'])) {
            throw new ValueError(sprintf('%s(): Argument #1 ($data) is invalid', __METHOD__));
        }
        // @codeCoverageIgnoreEnd

        $this->unserialize($data['bytes']);
    }

    public function compareTo(UuidInterface $other): int
    {
        $compare = strcmp($this->toString(), $other->toString());

        if ($compare < 0) {
            return -1;
        }

        if ($compare > 0) {
            return 1;
        }

        return 0;
    }

    public function equals(?object $other): bool
    {
        if (!$other instanceof UuidInterface) {
            return false;
        }

        return $this->compareTo($other) === 0;
    }

    /**
     * @return non-empty-string
     */
    public function getBytes(): string
    {
        return $this->codec->encodeBinary($this);
    }

    public function getFields(): FieldsInterface
    {
        return $this->fields;
    }

    public function getHex(): Hexadecimal
    {
        return new Hexadecimal(str_replace('-', '', $this->toString()));
    }

    public function getInteger(): IntegerObject
    {
        return new IntegerObject($this->numberConverter->fromHex($this->getHex()->toString()));
    }

    public function getUrn(): string
    {
        return 'urn:uuid:' . $this->toString();
    }

    /**
     * @return non-empty-string
     */
    public function toString(): string
    {
        return $this->codec->encode($this);
    }

    /**
     * Returns the factory used to create UUIDs
     */
    public static function getFactory(): UuidFactoryInterface
    {
        if (self::$factory === null) {
            self::$factory = new UuidFactory();
        }

        return self::$factory;
    }

    /**
     * Sets the factory used to create UUIDs
     *
     * @param UuidFactoryInterface $factory A factory that will be used by this class to create UUIDs
     */
    public static function setFactory(UuidFactoryInterface $factory): void
    {
        // Note: non-strict equality is intentional here. If the factory is configured differently, every assumption
        //       around purity is broken, and we have to internally decide everything differently.
        // phpcs:ignore SlevomatCodingStandard.Operators.DisallowEqualOperators.DisallowedNotEqualOperator
        self::$factoryReplaced = ($factory != new UuidFactory());

        self::$factory = $factory;
    }

    /**
     * Creates a UUID from a byte string
     *
     * @param string $bytes A binary string
     *
     * @return UuidInterface A UuidInterface instance created from a binary string representation
     *
     * @throws InvalidArgumentException
     *
     * @pure
     */
    public static function fromBytes(string $bytes): UuidInterface
    {
        /** @phpstan-ignore impure.staticPropertyAccess */
        if (!self::$factoryReplaced && strlen($bytes) === 16) {
            $base16Uuid = bin2hex($bytes);

            // Note: we are calling `fromString` internally because we don't know if the given `$bytes` is a valid UUID
            return self::fromString(
                substr($base16Uuid, 0, 8)
                    . '-'
                    . substr($base16Uuid, 8, 4)
                    . '-'
                    . substr($base16Uuid, 12, 4)
                    . '-'
                    . substr($base16Uuid, 16, 4)
                    . '-'
                    . substr($base16Uuid, 20, 12),
            );
        }

        /** @phpstan-ignore possiblyImpure.methodCall */
        return self::getFactory()->fromBytes($bytes);
    }

    /**
     * Creates a UUID from the string standard representation
     *
     * @param string $uuid A hexadecimal string
     *
     * @return UuidInterface A UuidInterface instance created from a hexadecimal string representation
     *
     * @throws InvalidArgumentException
     *
     * @pure
     */
    public static function fromString(string $uuid): UuidInterface
    {
        $uuid = strtolower($uuid);
        /** @phpstan-ignore impure.staticPropertyAccess, possiblyImpure.functionCall */
        if (!self::$factoryReplaced && preg_match(LazyUuidFromString::VALID_REGEX, $uuid) === 1) {
            /** @phpstan-ignore possiblyImpure.new */
            return new LazyUuidFromString($uuid);
        }

        /** @phpstan-ignore possiblyImpure.methodCall */
        return self::getFactory()->fromString($uuid);
    }

    /**
     * Creates a UUID from a DateTimeInterface instance
     *
     * @param DateTimeInterface $dateTime The date and time
     * @param Hexadecimal | null $node A 48-bit number representing the hardware address
     * @param int | null $clockSeq A 14-bit number used to help avoid duplicates that could arise when the clock is set
     *     backwards in time or if the node ID changes
     *
     * @return UuidInterface A UuidInterface instance that represents a version 1 UUID created from a DateTimeInterface instance
     */
    public static function fromDateTime(
        DateTimeInterface $dateTime,
        ?Hexadecimal $node = null,
        ?int $clockSeq = null
    ): UuidInterface {
        return self::getFactory()->fromDateTime($dateTime, $node, $clockSeq);
    }

    /**
     * Creates a UUID from the Hexadecimal object
     *
     * @param Hexadecimal $hex Hexadecimal object representing a hexadecimal number
     *
     * @return UuidInterface A UuidInterface instance created from the Hexadecimal object representing a hexadecimal number
     *
     * @throws InvalidArgumentException
     *
     * @pure
     */
    public static function fromHexadecimal(Hexadecimal $hex): UuidInterface
    {
        /** @phpstan-ignore possiblyImpure.methodCall */
        $factory = self::getFactory();

        if (method_exists($factory, 'fromHexadecimal')) {
            /** @phpstan-ignore possiblyImpure.methodCall */
            $uuid = $factory->fromHexadecimal($hex);
            /** @phpstan-ignore possiblyImpure.functionCall */
            assert($uuid instanceof UuidInterface);

            return $uuid;
        }

        throw new BadMethodCallException('The method fromHexadecimal() does not exist on the provided factory');
    }

    /**
     * Creates a UUID from a 128-bit integer string
     *
     * @param string $integer String representation of 128-bit integer
     *
     * @return UuidInterface A UuidInterface instance created from the string representation of a 128-bit integer
     *
     * @throws InvalidArgumentException
     *
     * @pure
     */
    public static function fromInteger(string $integer): UuidInterface
    {
        /** @phpstan-ignore possiblyImpure.methodCall */
        return self::getFactory()->fromInteger($integer);
    }

    /**
     * Returns true if the provided string is a valid UUID
     *
     * @param string $uuid A string to validate as a UUID
     *
     * @return bool True if the string is a valid UUID, false otherwise
     *
     * @phpstan-assert-if-true =non-empty-string $uuid
     *
     * @pure
     */
    public static function isValid(string $uuid): bool
    {
        /** @phpstan-ignore possiblyImpure.methodCall, possiblyImpure.methodCall */
        return self::getFactory()->getValidator()->validate($uuid);
    }

    /**
     * Returns a version 1 (Gregorian time) UUID from a host ID, sequence number, and the current time
     *
     * @param Hexadecimal | int | string | null $node A 48-bit number representing the hardware address; this number may
     *     be represented as an integer or a hexadecimal string
     * @param int | null $clockSeq A 14-bit number used to help avoid duplicates that could arise when the clock is set
     *     backwards in time or if the node ID changes
     *
     * @return UuidInterface A UuidInterface instance that represents a version 1 UUID
     */
    public static function uuid1($node = null, ?int $clockSeq = null): UuidInterface
    {
        return self::getFactory()->uuid1($node, $clockSeq);
    }

    /**
     * Returns a version 2 (DCE Security) UUID from a local domain, local identifier, host ID, clock sequence, and the current time
     *
     * @param int $localDomain The local domain to use when generating bytes, according to DCE Security
     * @param IntegerObject | null $localIdentifier The local identifier for the given domain; this may be a UID or GID
     *     on POSIX systems, if the local domain is "person" or "group," or it may be a site-defined identifier if the
     *     local domain is "org"
     * @param Hexadecimal | null $node A 48-bit number representing the hardware address
     * @param int | null $clockSeq A 14-bit number used to help avoid duplicates that could arise when the clock is set
     *     backwards in time or if the node ID changes (in a version 2 UUID, the lower 8 bits of this number are
     *     replaced with the domain).
     *
     * @return UuidInterface A UuidInterface instance that represents a version 2 UUID
     */
    public static function uuid2(
        int $localDomain,
        ?IntegerObject $localIdentifier = null,
        ?Hexadecimal $node = null,
        ?int $clockSeq = null
    ): UuidInterface {
        return self::getFactory()->uuid2($localDomain, $localIdentifier, $node, $clockSeq);
    }

    /**
     * Returns a version 3 (name-based) UUID based on the MD5 hash of a namespace ID and a name
     *
     * @param UuidInterface | string $ns The namespace (must be a valid UUID)
     * @param string $name The name to use for creating a UUID
     *
     * @return UuidInterface A UuidInterface instance that represents a version 3 UUID
     *
     * @pure
     */
    public static function uuid3($ns, string $name): UuidInterface
    {
        /** @phpstan-ignore possiblyImpure.methodCall */
        return self::getFactory()->uuid3($ns, $name);
    }

    /**
     * Returns a version 4 (random) UUID
     *
     * @return UuidInterface A UuidInterface instance that represents a version 4 UUID
     */
    public static function uuid4(): UuidInterface
    {
        return self::getFactory()->uuid4();
    }

    /**
     * Returns a version 5 (name-based) UUID based on the SHA-1 hash of a namespace ID and a name
     *
     * @param UuidInterface | string $ns The namespace (must be a valid UUID)
     * @param string $name The name to use for creating a UUID
     *
     * @return UuidInterface A UuidInterface instance that represents a version 5 UUID
     *
     * @pure
     */
    public static function uuid5($ns, string $name): UuidInterface
    {
        /** @phpstan-ignore possiblyImpure.methodCall */
        return self::getFactory()->uuid5($ns, $name);
    }

    /**
     * Returns a version 6 (reordered Gregorian time) UUID from a host ID, sequence number, and the current time
     *
     * @param Hexadecimal | null $node A 48-bit number representing the hardware address
     * @param int | null $clockSeq A 14-bit number used to help avoid duplicates that could arise when the clock is set
     *     backwards in time or if the node ID changes
     *
     * @return UuidInterface A UuidInterface instance that represents a version 6 UUID
     */
    public static function uuid6(
        ?Hexadecimal $node = null,
        ?int $clockSeq = null
    ): UuidInterface {
        return self::getFactory()->uuid6($node, $clockSeq);
    }

    /**
     * Returns a version 7 (Unix Epoch time) UUID
     *
     * @param DateTimeInterface | null $dateTime An optional date/time from which to create the version 7 UUID. If not
     *     provided, the UUID is generated using the current date/time.
     *
     * @return UuidInterface A UuidInterface instance that represents a version 7 UUID
     */
    public static function uuid7(?DateTimeInterface $dateTime = null): UuidInterface
    {
        $factory = self::getFactory();

        if (method_exists($factory, 'uuid7')) {
            /** @var UuidInterface */
            return $factory->uuid7($dateTime);
        }

        throw new UnsupportedOperationException('The provided factory does not support the uuid7() method');
    }

    /**
     * Returns a version 8 (custom format) UUID
     *
     * The bytes provided may contain any value according to your application's needs. Be aware, however, that other
     * applications may not understand the semantics of the value.
     *
     * @param string $bytes A 16-byte octet string. This is an open blob of data that you may fill with 128 bits of
     *     information. Be aware, however, bits 48 through 51 will be replaced with the UUID version field, and bits 64
     *     and 65 will be replaced with the UUID variant. You MUST NOT rely on these bits for your application needs.
     *
     * @return UuidInterface A UuidInterface instance that represents a version 8 UUID
     *
     * @pure
     */
    public static function uuid8(string $bytes): UuidInterface
    {
        /** @phpstan-ignore possiblyImpure.methodCall */
        $factory = self::getFactory();

        if (method_exists($factory, 'uuid8')) {
            /**
             * @var UuidInterface
             * @phpstan-ignore possiblyImpure.methodCall
             */
            return $factory->uuid8($bytes);
        }

        throw new UnsupportedOperationException('The provided factory does not support the uuid8() method');
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Exception;

use RuntimeException as PhpRuntimeException;

/**
 * Thrown to indicate that no suitable builder could be found
 */
class BuilderNotFoundException extends PhpRuntimeException implements UuidExceptionInterface
{
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Exception;

use InvalidArgumentException as PhpInvalidArgumentException;

/**
 * Thrown to indicate that the argument received is not valid
 */
class InvalidArgumentException extends PhpInvalidArgumentException implements UuidExceptionInterface
{
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Exception;

/**
 * Thrown to indicate that the string received is not a valid UUID
 *
 * The InvalidArgumentException that this extends is the ramsey/uuid version of this exception. It exists in the same
 * namespace as this class.
 */
class InvalidUuidStringException extends InvalidArgumentException implements UuidExceptionInterface
{
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Exception;

use RuntimeException as PhpRuntimeException;

/**
 * Thrown to indicate that the source of random data encountered an error
 *
 * This exception is used mostly to indicate that random_bytes() or random_int() threw an exception. However, it may be
 * used for other sources of random data.
 */
class RandomSourceException extends PhpRuntimeException implements UuidExceptionInterface
{
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Exception;

use RuntimeException as PhpRuntimeException;

/**
 * Thrown to indicate that the bytes being operated on are invalid in some way
 */
class InvalidBytesException extends PhpRuntimeException implements UuidExceptionInterface
{
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Exception;

use RuntimeException as PhpRuntimeException;

/**
 * Thrown to indicate that attempting to fetch or create a node ID encountered an error
 */
class NodeException extends PhpRuntimeException implements UuidExceptionInterface
{
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Exception;

use RuntimeException as PhpRuntimeException;

/**
 * Thrown to indicate that the PHP DateTime extension encountered an exception/error
 */
class DateTimeException extends PhpRuntimeException implements UuidExceptionInterface
{
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Exception;

use LogicException as PhpLogicException;

/**
 * Thrown to indicate that the requested operation is not supported
 */
class UnsupportedOperationException extends PhpLogicException implements UuidExceptionInterface
{
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Exception;

use Throwable;

interface UuidExceptionInterface extends Throwable
{
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Exception;

use RuntimeException as PhpRuntimeException;

/**
 * Thrown to indicate a builder is unable to build a UUID
 */
class UnableToBuildUuidException extends PhpRuntimeException implements UuidExceptionInterface
{
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Exception;

use RuntimeException as PhpRuntimeException;

/**
 * Thrown to indicate that an error occurred while attempting to hash a namespace and name
 */
class NameException extends PhpRuntimeException implements UuidExceptionInterface
{
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Exception;

use RuntimeException as PhpRuntimeException;

/**
 * Thrown to indicate an exception occurred while dealing with DCE Security (version 2) UUIDs
 */
class DceSecurityException extends PhpRuntimeException implements UuidExceptionInterface
{
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Exception;

use RuntimeException as PhpRuntimeException;

/**
 * Thrown to indicate that the source of time encountered an error
 */
class TimeSourceException extends PhpRuntimeException implements UuidExceptionInterface
{
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid;

use DateTimeImmutable;
use DateTimeInterface;
use Ramsey\Uuid\Converter\NumberConverterInterface;
use Ramsey\Uuid\Exception\DateTimeException;
use Ramsey\Uuid\Exception\UnsupportedOperationException;
use Throwable;

use function str_pad;
use function substr;

use const STR_PAD_LEFT;

/**
 * This trait encapsulates deprecated methods for ramsey/uuid; this trait and its methods will be removed in ramsey/uuid 5.0.0.
 *
 * @deprecated This trait and its methods will be removed in ramsey/uuid 5.0.0.
 *
 * @immutable
 */
trait DeprecatedUuidMethodsTrait
{
    /**
     * @deprecated Use {@see UuidInterface::getFields()} to get a {@see \Ramsey\Uuid\Fields\FieldsInterface} instance.
     *     If it is a {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call
     *     {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getClockSeqHiAndReserved()} and use the arbitrary-precision math
     *     library of your choice to convert it to a string integer.
     */
    public function getClockSeqHiAndReserved(): string
    {
        return $this->numberConverter->fromHex($this->fields->getClockSeqHiAndReserved()->toString());
    }

    /**
     * @deprecated Use {@see UuidInterface::getFields()} to get a {@see \Ramsey\Uuid\Fields\FieldsInterface} instance.
     *     If it is a {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call
     *     {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getClockSeqHiAndReserved()}.
     */
    public function getClockSeqHiAndReservedHex(): string
    {
        return $this->fields->getClockSeqHiAndReserved()->toString();
    }

    /**
     * @deprecated Use {@see UuidInterface::getFields()} to get a {@see \Ramsey\Uuid\Fields\FieldsInterface} instance.
     *     If it is a {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call
     *     {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getClockSeqLow()} and use the arbitrary-precision math library of
     *     your choice to convert it to a string integer.
     */
    public function getClockSeqLow(): string
    {
        return $this->numberConverter->fromHex($this->fields->getClockSeqLow()->toString());
    }

    /**
     * @deprecated Use {@see UuidInterface::getFields()} to get a {@see \Ramsey\Uuid\Fields\FieldsInterface} instance.
     *     If it is a {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call
     *     {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getClockSeqLow()}.
     */
    public function getClockSeqLowHex(): string
    {
        return $this->fields->getClockSeqLow()->toString();
    }

    /**
     * @deprecated Use {@see UuidInterface::getFields()} to get a {@see \Ramsey\Uuid\Fields\FieldsInterface} instance.
     *     If it is a {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call
     *     {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getClockSeq()} and use the arbitrary-precision math library of
     *     your choice to convert it to a string integer.
     */
    public function getClockSequence(): string
    {
        return $this->numberConverter->fromHex($this->fields->getClockSeq()->toString());
    }

    /**
     * @deprecated Use {@see UuidInterface::getFields()} to get a {@see \Ramsey\Uuid\Fields\FieldsInterface} instance.
     *     If it is a {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call
     *     {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getClockSeq()}.
     */
    public function getClockSequenceHex(): string
    {
        return $this->fields->getClockSeq()->toString();
    }

    /**
     * @deprecated This method will be removed in 5.0.0. There is no alternative recommendation, so plan accordingly.
     */
    public function getNumberConverter(): NumberConverterInterface
    {
        return $this->numberConverter;
    }

    /**
     * @deprecated In ramsey/uuid version 5.0.0, this will be removed. It is available at {@see UuidV1::getDateTime()}.
     *
     * @return DateTimeImmutable An immutable instance of DateTimeInterface
     *
     * @throws UnsupportedOperationException if UUID is not time-based
     * @throws DateTimeException if DateTime throws an exception/error
     */
    public function getDateTime(): DateTimeInterface
    {
        if ($this->fields->getVersion() !== 1) {
            throw new UnsupportedOperationException('Not a time-based UUID');
        }

        $time = $this->timeConverter->convertTime($this->fields->getTimestamp());

        try {
            return new DateTimeImmutable(
                '@'
                . $time->getSeconds()->toString()
                . '.'
                . str_pad($time->getMicroseconds()->toString(), 6, '0', STR_PAD_LEFT)
            );
        } catch (Throwable $e) {
            throw new DateTimeException($e->getMessage(), (int) $e->getCode(), $e);
        }
    }

    /**
     * @deprecated Use {@see UuidInterface::getFields()} to get a {@see \Ramsey\Uuid\Fields\FieldsInterface} instance.
     *
     * @return string[]
     */
    public function getFieldsHex(): array
    {
        return [
            'time_low' => $this->fields->getTimeLow()->toString(),
            'time_mid' => $this->fields->getTimeMid()->toString(),
            'time_hi_and_version' => $this->fields->getTimeHiAndVersion()->toString(),
            'clock_seq_hi_and_reserved' => $this->fields->getClockSeqHiAndReserved()->toString(),
            'clock_seq_low' => $this->fields->getClockSeqLow()->toString(),
            'node' => $this->fields->getNode()->toString(),
        ];
    }

    /**
     * @deprecated This method will be removed in 5.0.0. There is no direct alternative, but the same information may be
     *     obtained by splitting in half the value returned by {@see UuidInterface::getHex()}.
     */
    public function getLeastSignificantBits(): string
    {
        $leastSignificantHex = substr($this->getHex()->toString(), 16);

        return $this->numberConverter->fromHex($leastSignificantHex);
    }

    /**
     * @deprecated This method will be removed in 5.0.0. There is no direct alternative, but the same information may be
     *     obtained by splitting in half the value returned by {@see UuidInterface::getHex()}.
     */
    public function getLeastSignificantBitsHex(): string
    {
        return substr($this->getHex()->toString(), 16);
    }

    /**
     * @deprecated This method will be removed in 5.0.0. There is no direct alternative, but the same information may be
     *     obtained by splitting in half the value returned by {@see UuidInterface::getHex()}.
     */
    public function getMostSignificantBits(): string
    {
        $mostSignificantHex = substr($this->getHex()->toString(), 0, 16);

        return $this->numberConverter->fromHex($mostSignificantHex);
    }

    /**
     * @deprecated This method will be removed in 5.0.0. There is no direct alternative, but the same information may be
     *     obtained by splitting in half the value returned by {@see UuidInterface::getHex()}.
     */
    public function getMostSignificantBitsHex(): string
    {
        return substr($this->getHex()->toString(), 0, 16);
    }

    /**
     * @deprecated Use {@see UuidInterface::getFields()} to get a {@see \Ramsey\Uuid\Fields\FieldsInterface} instance.
     *     If it is a {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call
     *     {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getNode()} and use the arbitrary-precision math library of your
     *     choice to convert it to a string integer.
     */
    public function getNode(): string
    {
        return $this->numberConverter->fromHex($this->fields->getNode()->toString());
    }

    /**
     * @deprecated Use {@see UuidInterface::getFields()} to get a {@see \Ramsey\Uuid\Fields\FieldsInterface} instance.
     *     If it is a {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call
     *     {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getNode()}.
     */
    public function getNodeHex(): string
    {
        return $this->fields->getNode()->toString();
    }

    /**
     * @deprecated Use {@see UuidInterface::getFields()} to get a {@see \Ramsey\Uuid\Fields\FieldsInterface} instance.
     *     If it is a {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call
     *     {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getTimeHiAndVersion()} and use the arbitrary-precision math
     *     library of your choice to convert it to a string integer.
     */
    public function getTimeHiAndVersion(): string
    {
        return $this->numberConverter->fromHex($this->fields->getTimeHiAndVersion()->toString());
    }

    /**
     * @deprecated Use {@see UuidInterface::getFields()} to get a {@see \Ramsey\Uuid\Fields\FieldsInterface} instance.
     *     If it is a {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call
     *     {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getTimeHiAndVersion()}.
     */
    public function getTimeHiAndVersionHex(): string
    {
        return $this->fields->getTimeHiAndVersion()->toString();
    }

    /**
     * @deprecated Use {@see UuidInterface::getFields()} to get a {@see \Ramsey\Uuid\Fields\FieldsInterface} instance.
     *     If it is a {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call
     *     {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getTimeLow()} and use the arbitrary-precision math library of
     *     your choice to convert it to a string integer.
     */
    public function getTimeLow(): string
    {
        return $this->numberConverter->fromHex($this->fields->getTimeLow()->toString());
    }

    /**
     * @deprecated Use {@see UuidInterface::getFields()} to get a {@see \Ramsey\Uuid\Fields\FieldsInterface} instance.
     *     If it is a {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call
     *     {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getTimeLow()}.
     */
    public function getTimeLowHex(): string
    {
        return $this->fields->getTimeLow()->toString();
    }

    /**
     * @deprecated Use {@see UuidInterface::getFields()} to get a {@see \Ramsey\Uuid\Fields\FieldsInterface} instance.
     *     If it is a {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call
     *     {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getTimeMid()} and use the arbitrary-precision math library of
     *     your choice to convert it to a string integer.
     */
    public function getTimeMid(): string
    {
        return $this->numberConverter->fromHex($this->fields->getTimeMid()->toString());
    }

    /**
     * @deprecated Use {@see UuidInterface::getFields()} to get a {@see \Ramsey\Uuid\Fields\FieldsInterface} instance.
     *     If it is a {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call
     *     {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getTimeMid()}.
     */
    public function getTimeMidHex(): string
    {
        return $this->fields->getTimeMid()->toString();
    }

    /**
     * @deprecated Use {@see UuidInterface::getFields()} to get a {@see \Ramsey\Uuid\Fields\FieldsInterface} instance.
     *     If it is a {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call
     *     {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getTimestamp()} and use the arbitrary-precision math library of
     *     your choice to convert it to a string integer.
     */
    public function getTimestamp(): string
    {
        if ($this->fields->getVersion() !== 1) {
            throw new UnsupportedOperationException('Not a time-based UUID');
        }

        return $this->numberConverter->fromHex($this->fields->getTimestamp()->toString());
    }

    /**
     * @deprecated Use {@see UuidInterface::getFields()} to get a {@see \Ramsey\Uuid\Fields\FieldsInterface} instance.
     *     If it is a {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call
     *     {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getTimestamp()}.
     */
    public function getTimestampHex(): string
    {
        if ($this->fields->getVersion() !== 1) {
            throw new UnsupportedOperationException('Not a time-based UUID');
        }

        return $this->fields->getTimestamp()->toString();
    }

    /**
     * @deprecated Use {@see UuidInterface::getFields()} to get a {@see \Ramsey\Uuid\Fields\FieldsInterface} instance.
     *     If it is a {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call
     *     {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getVariant()}.
     */
    public function getVariant(): ?int
    {
        return $this->fields->getVariant();
    }

    /**
     * @deprecated Use {@see UuidInterface::getFields()} to get a {@see \Ramsey\Uuid\Fields\FieldsInterface} instance.
     *     If it is a {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call
     *     {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getVersion()}.
     */
    public function getVersion(): ?int
    {
        return $this->fields->getVersion();
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid;

use JsonSerializable;
use Ramsey\Uuid\Fields\FieldsInterface;
use Ramsey\Uuid\Type\Hexadecimal;
use Ramsey\Uuid\Type\Integer as IntegerObject;
use Serializable;
use Stringable;

/**
 * A UUID is a universally unique identifier adhering to an agreed-upon representation format and standard for generation
 *
 * @immutable
 */
interface UuidInterface extends
    DeprecatedUuidInterface,
    JsonSerializable,
    Serializable,
    Stringable
{
    /**
     * Returns -1, 0, or 1 if the UUID is less than, equal to, or greater than the other UUID
     *
     * The first of two UUIDs is greater than the second if the most significant field in which the UUIDs differ is
     * greater for the first UUID.
     *
     * @param UuidInterface $other The UUID to compare
     *
     * @return int<-1,1> -1, 0, or 1 if the UUID is less than, equal to, or greater than $other
     */
    public function compareTo(UuidInterface $other): int;

    /**
     * Returns true if the UUID is equal to the provided object
     *
     * The result is true if and only if the argument is not null, is a UUID object, has the same variant, and contains
     * the same value, bit-for-bit, as the UUID.
     *
     * @param object | null $other An object to test for equality with this UUID
     *
     * @return bool True if the other object is equal to this UUID
     */
    public function equals(?object $other): bool;

    /**
     * Returns the binary string representation of the UUID
     *
     * @return non-empty-string
     *
     * @pure
     */
    public function getBytes(): string;

    /**
     * Returns the fields that comprise this UUID
     */
    public function getFields(): FieldsInterface;

    /**
     * Returns the hexadecimal representation of the UUID
     */
    public function getHex(): Hexadecimal;

    /**
     * Returns the integer representation of the UUID
     */
    public function getInteger(): IntegerObject;

    /**
     * Returns the string standard representation of the UUID as a URN
     *
     * @link http://en.wikipedia.org/wiki/Uniform_Resource_Name Uniform Resource Name
     * @link https://www.rfc-editor.org/rfc/rfc9562.html#section-4 RFC 9562, 4. UUID Format
     * @link https://www.rfc-editor.org/rfc/rfc9562.html#section-7 RFC 9562, 7. IANA Considerations
     * @link https://www.rfc-editor.org/rfc/rfc4122.html#section-3 RFC 4122, 3. Namespace Registration Template
     */
    public function getUrn(): string;

    /**
     * Returns the string standard representation of the UUID
     *
     * @return non-empty-string
     *
     * @pure
     */
    public function toString(): string;

    /**
     * Casts the UUID to the string standard representation
     *
     * @return non-empty-string
     *
     * @pure
     */
    public function __toString(): string;
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Rfc4122;

use Ramsey\Uuid\Uuid;
use Ramsey\Uuid\Validator\ValidatorInterface;

use function preg_match;
use function str_replace;

/**
 * Rfc4122\Validator validates strings as UUIDs of the RFC 9562 (formerly RFC 4122) variant
 *
 * @immutable
 */
final class Validator implements ValidatorInterface
{
    private const VALID_PATTERN = '\A[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-'
        . '[1-8][0-9A-Fa-f]{3}-[ABab89][0-9A-Fa-f]{3}-[0-9A-Fa-f]{12}\z';

    /**
     * @return non-empty-string
     */
    public function getPattern(): string
    {
        return self::VALID_PATTERN;
    }

    public function validate(string $uuid): bool
    {
        /** @phpstan-ignore possiblyImpure.functionCall */
        $uuid = strtolower(str_replace(['urn:', 'uuid:', 'URN:', 'UUID:', '{', '}'], '', $uuid));

        /** @phpstan-ignore possiblyImpure.functionCall */
        return $uuid === Uuid::NIL || $uuid === Uuid::MAX || preg_match('/' . self::VALID_PATTERN . '/Dms', $uuid);
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Rfc4122;

use Ramsey\Uuid\Builder\UuidBuilderInterface;
use Ramsey\Uuid\Codec\CodecInterface;
use Ramsey\Uuid\Converter\NumberConverterInterface;
use Ramsey\Uuid\Converter\Time\UnixTimeConverter;
use Ramsey\Uuid\Converter\TimeConverterInterface;
use Ramsey\Uuid\Exception\UnableToBuildUuidException;
use Ramsey\Uuid\Exception\UnsupportedOperationException;
use Ramsey\Uuid\Math\BrickMathCalculator;
use Ramsey\Uuid\Rfc4122\UuidInterface as Rfc4122UuidInterface;
use Ramsey\Uuid\Uuid;
use Ramsey\Uuid\UuidInterface;
use Throwable;

/**
 * UuidBuilder builds instances of RFC 9562 (formerly 4122) UUIDs
 *
 * @immutable
 */
class UuidBuilder implements UuidBuilderInterface
{
    private TimeConverterInterface $unixTimeConverter;

    /**
     * Constructs the DefaultUuidBuilder
     *
     * @param NumberConverterInterface $numberConverter The number converter to use when constructing the Uuid
     * @param TimeConverterInterface $timeConverter The time converter to use for converting Gregorian time extracted
     *     from version 1, 2, and 6 UUIDs to Unix timestamps
     * @param TimeConverterInterface | null $unixTimeConverter The time converter to use for converter Unix Epoch time
     *     extracted from version 7 UUIDs to Unix timestamps
     */
    public function __construct(
        private NumberConverterInterface $numberConverter,
        private TimeConverterInterface $timeConverter,
        ?TimeConverterInterface $unixTimeConverter = null,
    ) {
        $this->unixTimeConverter = $unixTimeConverter ?? new UnixTimeConverter(new BrickMathCalculator());
    }

    /**
     * Builds and returns a Uuid
     *
     * @param CodecInterface $codec The codec to use for building this Uuid instance
     * @param string $bytes The byte string from which to construct a UUID
     *
     * @return Rfc4122UuidInterface UuidBuilder returns instances of Rfc4122UuidInterface
     *
     * @pure
     */
    public function build(CodecInterface $codec, string $bytes): UuidInterface
    {
        try {
            /** @var Fields $fields */
            $fields = $this->buildFields($bytes);

            if ($fields->isNil()) {
                /** @phpstan-ignore possiblyImpure.new */
                return new NilUuid($fields, $this->numberConverter, $codec, $this->timeConverter);
            }

            if ($fields->isMax()) {
                /** @phpstan-ignore possiblyImpure.new */
                return new MaxUuid($fields, $this->numberConverter, $codec, $this->timeConverter);
            }

            return match ($fields->getVersion()) {
                /** @phpstan-ignore possiblyImpure.new */
                Uuid::UUID_TYPE_TIME => new UuidV1($fields, $this->numberConverter, $codec, $this->timeConverter),
                Uuid::UUID_TYPE_DCE_SECURITY
                    /** @phpstan-ignore possiblyImpure.new */
                    => new UuidV2($fields, $this->numberConverter, $codec, $this->timeConverter),
                /** @phpstan-ignore possiblyImpure.new */
                Uuid::UUID_TYPE_HASH_MD5 => new UuidV3($fields, $this->numberConverter, $codec, $this->timeConverter),
                /** @phpstan-ignore possiblyImpure.new */
                Uuid::UUID_TYPE_RANDOM => new UuidV4($fields, $this->numberConverter, $codec, $this->timeConverter),
                /** @phpstan-ignore possiblyImpure.new */
                Uuid::UUID_TYPE_HASH_SHA1 => new UuidV5($fields, $this->numberConverter, $codec, $this->timeConverter),
                Uuid::UUID_TYPE_REORDERED_TIME
                    /** @phpstan-ignore possiblyImpure.new */
                    => new UuidV6($fields, $this->numberConverter, $codec, $this->timeConverter),
                Uuid::UUID_TYPE_UNIX_TIME
                    /** @phpstan-ignore possiblyImpure.new */
                    => new UuidV7($fields, $this->numberConverter, $codec, $this->unixTimeConverter),
                /** @phpstan-ignore possiblyImpure.new */
                Uuid::UUID_TYPE_CUSTOM => new UuidV8($fields, $this->numberConverter, $codec, $this->timeConverter),
                default => throw new UnsupportedOperationException(
                    'The UUID version in the given fields is not supported by this UUID builder',
                ),
            };
        } catch (Throwable $e) {
            /** @phpstan-ignore possiblyImpure.methodCall, possiblyImpure.methodCall */
            throw new UnableToBuildUuidException($e->getMessage(), (int) $e->getCode(), $e);
        }
    }

    /**
     * Proxy method to allow injecting a mock for testing
     *
     * @pure
     */
    protected function buildFields(string $bytes): FieldsInterface
    {
        /** @phpstan-ignore possiblyImpure.new */
        return new Fields($bytes);
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Rfc4122;

use Ramsey\Uuid\Codec\CodecInterface;
use Ramsey\Uuid\Converter\NumberConverterInterface;
use Ramsey\Uuid\Converter\TimeConverterInterface;
use Ramsey\Uuid\Exception\InvalidArgumentException;
use Ramsey\Uuid\Rfc4122\FieldsInterface as Rfc4122FieldsInterface;
use Ramsey\Uuid\Uuid;

/**
 * Gregorian time, or version 1, UUIDs include timestamp, clock sequence, and node values, combined into a 128-bit unsigned integer
 *
 * @link https://www.rfc-editor.org/rfc/rfc9562#section-5.1 RFC 9562, 5.1. UUID Version 1
 *
 * @immutable
 */
final class UuidV1 extends Uuid implements UuidInterface
{
    use TimeTrait;

    /**
     * Creates a version 1 (Gregorian time) UUID
     *
     * @param Rfc4122FieldsInterface $fields The fields from which to construct a UUID
     * @param NumberConverterInterface $numberConverter The number converter to use for converting hex values to/from integers
     * @param CodecInterface $codec The codec to use when encoding or decoding UUID strings
     * @param TimeConverterInterface $timeConverter The time converter to use for converting timestamps extracted from a
     *     UUID to unix timestamps
     */
    public function __construct(
        Rfc4122FieldsInterface $fields,
        NumberConverterInterface $numberConverter,
        CodecInterface $codec,
        TimeConverterInterface $timeConverter,
    ) {
        if ($fields->getVersion() !== Uuid::UUID_TYPE_TIME) {
            throw new InvalidArgumentException(
                'Fields used to create a UuidV1 must represent a version 1 (time-based) UUID',
            );
        }

        parent::__construct($fields, $numberConverter, $codec, $timeConverter);
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Rfc4122;

use DateTimeImmutable;
use DateTimeInterface;
use Ramsey\Uuid\Exception\DateTimeException;
use Throwable;

use function str_pad;

use const STR_PAD_LEFT;

/**
 * Provides common functionality for getting the time from a time-based UUID
 *
 * @immutable
 */
trait TimeTrait
{
    /**
     * Returns a DateTimeInterface object representing the timestamp associated with the UUID
     *
     * @return DateTimeImmutable A PHP DateTimeImmutable instance representing the timestamp of a time-based UUID
     */
    public function getDateTime(): DateTimeInterface
    {
        $time = $this->timeConverter->convertTime($this->fields->getTimestamp());

        try {
            return new DateTimeImmutable(
                '@'
                . $time->getSeconds()->toString()
                . '.'
                . str_pad($time->getMicroseconds()->toString(), 6, '0', STR_PAD_LEFT)
            );
        } catch (Throwable $e) {
            throw new DateTimeException($e->getMessage(), (int) $e->getCode(), $e);
        }
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Rfc4122;

use Ramsey\Uuid\Codec\CodecInterface;
use Ramsey\Uuid\Converter\NumberConverterInterface;
use Ramsey\Uuid\Converter\TimeConverterInterface;
use Ramsey\Uuid\Exception\InvalidArgumentException;
use Ramsey\Uuid\Rfc4122\FieldsInterface as Rfc4122FieldsInterface;
use Ramsey\Uuid\Type\Integer as IntegerObject;
use Ramsey\Uuid\Uuid;

use function hexdec;

/**
 * DCE Security version, or version 2, UUIDs include local domain identifier, local ID for the specified domain, and
 * node values that are combined into a 128-bit unsigned integer
 *
 * It is important to note that a version 2 UUID suffers from some loss of timestamp fidelity, due to replacing the
 * time_low field with the local identifier. When constructing the timestamp value for date purposes, we replace the
 * local identifier bits with zeros. As a result, the timestamp can be off by a range of 0 to 429.4967295 seconds (or 7
 * minutes, 9 seconds, and 496,730 microseconds).
 *
 * Astute observers might note this value directly corresponds to `2^32-1`, or `0xffffffff`. The local identifier is
 * 32-bits, and we have set each of these bits to `0`, so the maximum range of timestamp drift is `0x00000000` to
 * `0xffffffff` (counted in 100-nanosecond intervals).
 *
 * @link https://www.rfc-editor.org/rfc/rfc9562#section-5.2 RFC 9562, 5.2. UUID Version 2
 * @link https://publications.opengroup.org/c311 DCE 1.1: Authentication and Security Services
 * @link https://publications.opengroup.org/c706 DCE 1.1: Remote Procedure Call
 * @link https://pubs.opengroup.org/onlinepubs/9696989899/chap5.htm#tagcjh_08_02_01_01 DCE 1.1: Auth & Sec, Â§5.2.1.1
 * @link https://pubs.opengroup.org/onlinepubs/9696989899/chap11.htm#tagcjh_14_05_01_01 DCE 1.1: Auth & Sec, Â§11.5.1.1
 * @link https://pubs.opengroup.org/onlinepubs/9629399/apdxa.htm DCE 1.1: RPC, Appendix A
 * @link https://github.com/google/uuid Go package for UUIDs (includes DCE implementation)
 *
 * @immutable
 */
final class UuidV2 extends Uuid implements UuidInterface
{
    use TimeTrait;

    /**
     * Creates a version 2 (DCE Security) UUID
     *
     * @param Rfc4122FieldsInterface $fields The fields from which to construct a UUID
     * @param NumberConverterInterface $numberConverter The number converter to use for converting hex values to/from integers
     * @param CodecInterface $codec The codec to use when encoding or decoding UUID strings
     * @param TimeConverterInterface $timeConverter The time converter to use for converting timestamps extracted from a
     *     UUID to unix timestamps
     */
    public function __construct(
        Rfc4122FieldsInterface $fields,
        NumberConverterInterface $numberConverter,
        CodecInterface $codec,
        TimeConverterInterface $timeConverter,
    ) {
        if ($fields->getVersion() !== Uuid::UUID_TYPE_DCE_SECURITY) {
            throw new InvalidArgumentException(
                'Fields used to create a UuidV2 must represent a version 2 (DCE Security) UUID'
            );
        }

        parent::__construct($fields, $numberConverter, $codec, $timeConverter);
    }

    /**
     * Returns the local domain used to create this version 2 UUID
     */
    public function getLocalDomain(): int
    {
        /** @var Rfc4122FieldsInterface $fields */
        $fields = $this->getFields();

        return (int) hexdec($fields->getClockSeqLow()->toString());
    }

    /**
     * Returns the string name of the local domain
     */
    public function getLocalDomainName(): string
    {
        return Uuid::DCE_DOMAIN_NAMES[$this->getLocalDomain()];
    }

    /**
     * Returns the local identifier for the domain used to create this version 2 UUID
     */
    public function getLocalIdentifier(): IntegerObject
    {
        /** @var Rfc4122FieldsInterface $fields */
        $fields = $this->getFields();

        return new IntegerObject($this->numberConverter->fromHex($fields->getTimeLow()->toString()));
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Rfc4122;

use Ramsey\Uuid\Exception\InvalidArgumentException;
use Ramsey\Uuid\Fields\SerializableFieldsTrait;
use Ramsey\Uuid\Type\Hexadecimal;
use Ramsey\Uuid\Uuid;

use function bin2hex;
use function dechex;
use function hexdec;
use function sprintf;
use function str_pad;
use function strlen;
use function substr;
use function unpack;

use const STR_PAD_LEFT;

/**
 * RFC 9562 (formerly RFC 4122) variant UUIDs consist of a set of named fields
 *
 * Internally, this class represents the fields together as a 16-byte binary string.
 *
 * @immutable
 */
final class Fields implements FieldsInterface
{
    use MaxTrait;
    use NilTrait;
    use SerializableFieldsTrait;
    use VariantTrait;
    use VersionTrait;

    /**
     * @param string $bytes A 16-byte binary string representation of a UUID
     *
     * @throws InvalidArgumentException if the byte string is not exactly 16 bytes
     * @throws InvalidArgumentException if the byte string does not represent an RFC 9562 (formerly RFC 4122) UUID
     * @throws InvalidArgumentException if the byte string does not contain a valid version
     */
    public function __construct(private string $bytes)
    {
        if (strlen($this->bytes) !== 16) {
            throw new InvalidArgumentException(
                'The byte string must be 16 bytes long; ' . 'received ' . strlen($this->bytes) . ' bytes',
            );
        }

        if (!$this->isCorrectVariant()) {
            throw new InvalidArgumentException(
                'The byte string received does not conform to the RFC 9562 (formerly RFC 4122) variant',
            );
        }

        if (!$this->isCorrectVersion()) {
            throw new InvalidArgumentException(
                'The byte string received does not contain a valid RFC 9562 (formerly RFC 4122) version',
            );
        }
    }

    /**
     * @pure
     */
    public function getBytes(): string
    {
        return $this->bytes;
    }

    public function getClockSeq(): Hexadecimal
    {
        if ($this->isMax()) {
            $clockSeq = 0xffff;
        } elseif ($this->isNil()) {
            $clockSeq = 0x0000;
        } else {
            $clockSeq = hexdec(bin2hex(substr($this->bytes, 8, 2))) & 0x3fff;
        }

        return new Hexadecimal(str_pad(dechex($clockSeq), 4, '0', STR_PAD_LEFT));
    }

    public function getClockSeqHiAndReserved(): Hexadecimal
    {
        return new Hexadecimal(bin2hex(substr($this->bytes, 8, 1)));
    }

    public function getClockSeqLow(): Hexadecimal
    {
        return new Hexadecimal(bin2hex(substr($this->bytes, 9, 1)));
    }

    public function getNode(): Hexadecimal
    {
        return new Hexadecimal(bin2hex(substr($this->bytes, 10)));
    }

    public function getTimeHiAndVersion(): Hexadecimal
    {
        return new Hexadecimal(bin2hex(substr($this->bytes, 6, 2)));
    }

    public function getTimeLow(): Hexadecimal
    {
        return new Hexadecimal(bin2hex(substr($this->bytes, 0, 4)));
    }

    public function getTimeMid(): Hexadecimal
    {
        return new Hexadecimal(bin2hex(substr($this->bytes, 4, 2)));
    }

    /**
     * Returns the full 60-bit timestamp, without the version
     *
     * For version 2 UUIDs, the time_low field is the local identifier and should not be returned as part of the time.
     * For this reason, we set the bottom 32 bits of the timestamp to 0's. As a result, there is some loss of timestamp
     * fidelity, for version 2 UUIDs. The timestamp can be off by a range of 0 to 429.4967295 seconds (or 7 minutes, 9
     * seconds, and 496,730 microseconds).
     *
     * For version 6 UUIDs, the timestamp order is reversed from the typical RFC 9562 (formerly RFC 4122) order (the
     * time bits are in the correct bit order, so that it is monotonically increasing). In returning the timestamp
     * value, we put the bits in the order: time_low + time_mid + time_hi.
     */
    public function getTimestamp(): Hexadecimal
    {
        return new Hexadecimal(match ($this->getVersion()) {
            Uuid::UUID_TYPE_DCE_SECURITY => sprintf(
                '%03x%04s%08s',
                hexdec($this->getTimeHiAndVersion()->toString()) & 0x0fff,
                $this->getTimeMid()->toString(),
                ''
            ),
            Uuid::UUID_TYPE_REORDERED_TIME => sprintf(
                '%08s%04s%03x',
                $this->getTimeLow()->toString(),
                $this->getTimeMid()->toString(),
                hexdec($this->getTimeHiAndVersion()->toString()) & 0x0fff
            ),
            // The Unix timestamp in version 7 UUIDs is a 48-bit number, but for consistency, we will return a 60-bit
            // number, padded to the left with zeros.
            Uuid::UUID_TYPE_UNIX_TIME => sprintf(
                '%011s%04s',
                $this->getTimeLow()->toString(),
                $this->getTimeMid()->toString(),
            ),
            default => sprintf(
                '%03x%04s%08s',
                hexdec($this->getTimeHiAndVersion()->toString()) & 0x0fff,
                $this->getTimeMid()->toString(),
                $this->getTimeLow()->toString()
            ),
        });
    }

    public function getVersion(): ?int
    {
        if ($this->isNil() || $this->isMax()) {
            return null;
        }

        /** @var int[] $parts */
        $parts = unpack('n*', $this->bytes);

        return $parts[4] >> 12;
    }

    private function isCorrectVariant(): bool
    {
        if ($this->isNil() || $this->isMax()) {
            return true;
        }

        return $this->getVariant() === Uuid::RFC_4122;
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Rfc4122;

use Ramsey\Uuid\Codec\CodecInterface;
use Ramsey\Uuid\Converter\NumberConverterInterface;
use Ramsey\Uuid\Converter\TimeConverterInterface;
use Ramsey\Uuid\Exception\InvalidArgumentException;
use Ramsey\Uuid\Rfc4122\FieldsInterface as Rfc4122FieldsInterface;
use Ramsey\Uuid\Uuid;

/**
 * Version 3 UUIDs are named-based, using a combination of a namespace and name that are hashed into a 128-bit unsigned
 * integer using the MD5 hashing algorithm
 *
 * @link https://www.rfc-editor.org/rfc/rfc9562#section-5.3 RFC 9562, 5.3. UUID Version 3
 *
 * @immutable
 */
final class UuidV3 extends Uuid implements UuidInterface
{
    /**
     * Creates a version 3 (name-based, MD5-hashed) UUID
     *
     * @param Rfc4122FieldsInterface $fields The fields from which to construct a UUID
     * @param NumberConverterInterface $numberConverter The number converter to use for converting hex values to/from integers
     * @param CodecInterface $codec The codec to use when encoding or decoding UUID strings
     * @param TimeConverterInterface $timeConverter The time converter to use for converting timestamps extracted from a
     *     UUID to unix timestamps
     */
    public function __construct(
        Rfc4122FieldsInterface $fields,
        NumberConverterInterface $numberConverter,
        CodecInterface $codec,
        TimeConverterInterface $timeConverter,
    ) {
        if ($fields->getVersion() !== Uuid::UUID_TYPE_HASH_MD5) {
            throw new InvalidArgumentException(
                'Fields used to create a UuidV3 must represent a version 3 (name-based, MD5-hashed) UUID',
            );
        }

        parent::__construct($fields, $numberConverter, $codec, $timeConverter);
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Rfc4122;

use Ramsey\Uuid\Codec\CodecInterface;
use Ramsey\Uuid\Converter\NumberConverterInterface;
use Ramsey\Uuid\Converter\TimeConverterInterface;
use Ramsey\Uuid\Exception\InvalidArgumentException;
use Ramsey\Uuid\Rfc4122\FieldsInterface as Rfc4122FieldsInterface;
use Ramsey\Uuid\Uuid;

/**
 * Unix Epoch time, or version 7, UUIDs include a timestamp in milliseconds since the Unix Epoch, along with random bytes
 *
 * @link https://www.rfc-editor.org/rfc/rfc9562#section-5.7 RFC 9562, 5.7. UUID Version 7
 *
 * @immutable
 */
final class UuidV7 extends Uuid implements UuidInterface
{
    use TimeTrait;

    /**
     * Creates a version 7 (Unix Epoch time) UUID
     *
     * @param Rfc4122FieldsInterface $fields The fields from which to construct a UUID
     * @param NumberConverterInterface $numberConverter The number converter to use for converting hex values to/from integers
     * @param CodecInterface $codec The codec to use when encoding or decoding UUID strings
     * @param TimeConverterInterface $timeConverter The time converter to use for converting timestamps extracted from a
     *     UUID to unix timestamps
     */
    public function __construct(
        Rfc4122FieldsInterface $fields,
        NumberConverterInterface $numberConverter,
        CodecInterface $codec,
        TimeConverterInterface $timeConverter,
    ) {
        if ($fields->getVersion() !== Uuid::UUID_TYPE_UNIX_TIME) {
            throw new InvalidArgumentException(
                'Fields used to create a UuidV7 must represent a version 7 (Unix Epoch time) UUID',
            );
        }

        parent::__construct($fields, $numberConverter, $codec, $timeConverter);
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Rfc4122;

use Ramsey\Uuid\Uuid;

/**
 * Provides common functionality for handling the version, as defined by RFC 9562 (formerly RFC 4122)
 *
 * @immutable
 */
trait VersionTrait
{
    /**
     * Returns the UUID version
     *
     * The version number describes how the UUID was generated and has the following meaning:
     *
     * 1. Gregorian time UUID
     * 2. DCE security UUID
     * 3. Name-based UUID hashed with MD5
     * 4. Randomly generated UUID
     * 5. Name-based UUID hashed with SHA-1
     * 6. Reordered Gregorian time UUID
     * 7. Unix Epoch time UUID
     * 8. Custom format UUID
     *
     * This returns `null` if the UUID is not an RFC 9562 (formerly RFC 4122) variant, since the version is only
     * meaningful for this variant.
     *
     * @link https://www.rfc-editor.org/rfc/rfc9562#section-4.2 RFC 9562, 4.2. Version Field
     *
     * @pure
     */
    abstract public function getVersion(): ?int;

    /**
     * Returns true if these fields represent a max UUID
     */
    abstract public function isMax(): bool;

    /**
     * Returns true if these fields represent a nil UUID
     */
    abstract public function isNil(): bool;

    /**
     * Returns true if the version matches one of those defined by RFC 9562 (formerly RFC 4122)
     *
     * @return bool True if the UUID version is valid, false otherwise
     */
    private function isCorrectVersion(): bool
    {
        if ($this->isNil() || $this->isMax()) {
            return true;
        }

        return match ($this->getVersion()) {
            Uuid::UUID_TYPE_TIME, Uuid::UUID_TYPE_DCE_SECURITY,
                Uuid::UUID_TYPE_HASH_MD5, Uuid::UUID_TYPE_RANDOM,
                Uuid::UUID_TYPE_HASH_SHA1, Uuid::UUID_TYPE_REORDERED_TIME,
                Uuid::UUID_TYPE_UNIX_TIME, Uuid::UUID_TYPE_CUSTOM => true,
            default => false,
        };
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Rfc4122;

use Ramsey\Uuid\Exception\InvalidBytesException;
use Ramsey\Uuid\Uuid;

use function decbin;
use function str_pad;
use function str_starts_with;
use function strlen;
use function substr;
use function unpack;

use const STR_PAD_LEFT;

/**
 * Provides common functionality for handling the variant, as defined by RFC 9562 (formerly RFC 4122)
 *
 * @immutable
 */
trait VariantTrait
{
    /**
     * Returns the bytes that comprise the fields
     */
    abstract public function getBytes(): string;

    /**
     * Returns the variant
     *
     * The variant number describes the layout of the UUID. The variant number has the following meaning:
     *
     * - 0 - Reserved for NCS backward compatibility
     * - 2 - The RFC 9562 (formerly RFC 4122) variant
     * - 6 - Reserved, Microsoft Corporation backward compatibility
     * - 7 - Reserved for future definition
     *
     * For RFC 9562 (formerly RFC 4122) variant UUIDs, this value should always be the integer `2`.
     *
     * @link https://www.rfc-editor.org/rfc/rfc9562#section-4.1 RFC 9562, 4.1. Variant Field
     */
    public function getVariant(): int
    {
        if (strlen($this->getBytes()) !== 16) {
            throw new InvalidBytesException('Invalid number of bytes');
        }

        // According to RFC 9562, sections {@link https://www.rfc-editor.org/rfc/rfc9562#section-4.1 4.1} and
        // {@link https://www.rfc-editor.org/rfc/rfc9562#section-5.10 5.10}, the Max UUID falls within the range
        // of the future variant.
        if ($this->isMax()) {
            return Uuid::RESERVED_FUTURE;
        }

        // According to RFC 9562, sections {@link https://www.rfc-editor.org/rfc/rfc9562#section-4.1 4.1} and
        // {@link https://www.rfc-editor.org/rfc/rfc9562#section-5.9 5.9}, the Nil UUID falls within the range
        // of the Apollo NCS variant.
        if ($this->isNil()) {
            return Uuid::RESERVED_NCS;
        }

        /** @var int[] $parts */
        $parts = unpack('n*', $this->getBytes());

        // $parts[5] is a 16-bit, unsigned integer containing the variant bits of the UUID. We convert this integer into
        // a string containing a binary representation, padded to 16 characters. We analyze the first three characters
        // (three most-significant bits) to determine the variant.
        $msb = substr(str_pad(decbin($parts[5]), 16, '0', STR_PAD_LEFT), 0, 3);

        if ($msb === '111') {
            return Uuid::RESERVED_FUTURE;
        } elseif ($msb === '110') {
            return Uuid::RESERVED_MICROSOFT;
        } elseif (str_starts_with($msb, '10')) {
            return Uuid::RFC_4122;
        }

        return Uuid::RESERVED_NCS;
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Rfc4122;

/**
 * Provides common functionality for max UUIDs
 *
 * @immutable
 */
trait MaxTrait
{
    /**
     * Returns the bytes that comprise the fields
     *
     * @pure
     */
    abstract public function getBytes(): string;

    /**
     * Returns true if the byte string represents a max UUID
     *
     * @pure
     */
    public function isMax(): bool
    {
        return $this->getBytes() === "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff";
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Rfc4122;

use Ramsey\Uuid\UuidInterface as BaseUuidInterface;

/**
 * A universally unique identifier (UUID), as defined in RFC 9562 (formerly RFC 4122)
 *
 * @link https://www.rfc-editor.org/rfc/rfc9562 RFC 9562
 *
 * @immutable
 */
interface UuidInterface extends BaseUuidInterface
{
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Rfc4122;

use Ramsey\Uuid\Uuid;

/**
 * The nil UUID is a special form of UUID that has all 128 bits set to zero (`0`)
 *
 * @link https://www.rfc-editor.org/rfc/rfc9562#section-5.9 RFC 9562, 5.9. Nil UUID
 *
 * @immutable
 */
final class NilUuid extends Uuid implements UuidInterface
{
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Rfc4122;

/**
 * Provides common functionality for nil UUIDs
 *
 * @immutable
 */
trait NilTrait
{
    /**
     * Returns the bytes that comprise the fields
     *
     * @pure
     */
    abstract public function getBytes(): string;

    /**
     * Returns true if the byte string represents a nil UUID
     */
    public function isNil(): bool
    {
        return $this->getBytes() === "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Rfc4122;

use Ramsey\Uuid\Codec\CodecInterface;
use Ramsey\Uuid\Converter\NumberConverterInterface;
use Ramsey\Uuid\Converter\TimeConverterInterface;
use Ramsey\Uuid\Exception\InvalidArgumentException;
use Ramsey\Uuid\Rfc4122\FieldsInterface as Rfc4122FieldsInterface;
use Ramsey\Uuid\Uuid;

/**
 * Version 5 UUIDs are named-based, using a combination of a namespace and name that are hashed into a 128-bit unsigned
 * integer using the SHA1 hashing algorithm
 *
 * @link https://www.rfc-editor.org/rfc/rfc9562#section-5.5 RFC 9562, 5.5. UUID Version 5
 *
 * @immutable
 */
final class UuidV5 extends Uuid implements UuidInterface
{
    /**
     * Creates a version 5 (name-based, SHA1-hashed) UUID
     *
     * @param Rfc4122FieldsInterface $fields The fields from which to construct a UUID
     * @param NumberConverterInterface $numberConverter The number converter to use for converting hex values to/from integers
     * @param CodecInterface $codec The codec to use when encoding or decoding UUID strings
     * @param TimeConverterInterface $timeConverter The time converter to use for converting timestamps extracted from a
     *     UUID to unix timestamps
     */
    public function __construct(
        Rfc4122FieldsInterface $fields,
        NumberConverterInterface $numberConverter,
        CodecInterface $codec,
        TimeConverterInterface $timeConverter,
    ) {
        if ($fields->getVersion() !== Uuid::UUID_TYPE_HASH_SHA1) {
            throw new InvalidArgumentException(
                'Fields used to create a UuidV5 must represent a version 5 (named-based, SHA1-hashed) UUID',
            );
        }

        parent::__construct($fields, $numberConverter, $codec, $timeConverter);
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Rfc4122;

use Ramsey\Uuid\Codec\CodecInterface;
use Ramsey\Uuid\Converter\NumberConverterInterface;
use Ramsey\Uuid\Converter\TimeConverterInterface;
use Ramsey\Uuid\Exception\InvalidArgumentException;
use Ramsey\Uuid\Rfc4122\FieldsInterface as Rfc4122FieldsInterface;
use Ramsey\Uuid\Uuid;

/**
 * Random, or version 4, UUIDs are randomly or pseudo-randomly generated 128-bit integers
 *
 * @link https://www.rfc-editor.org/rfc/rfc9562#section-5.4 RFC 9562, 5.4. UUID Version 4
 *
 * @immutable
 */
final class UuidV4 extends Uuid implements UuidInterface
{
    /**
     * Creates a version 4 (random) UUID
     *
     * @param Rfc4122FieldsInterface $fields The fields from which to construct a UUID
     * @param NumberConverterInterface $numberConverter The number converter to use for converting hex values to/from integers
     * @param CodecInterface $codec The codec to use when encoding or decoding UUID strings
     * @param TimeConverterInterface $timeConverter The time converter to use for converting timestamps extracted from a
     *     UUID to unix timestamps
     */
    public function __construct(
        Rfc4122FieldsInterface $fields,
        NumberConverterInterface $numberConverter,
        CodecInterface $codec,
        TimeConverterInterface $timeConverter,
    ) {
        if ($fields->getVersion() !== Uuid::UUID_TYPE_RANDOM) {
            throw new InvalidArgumentException(
                'Fields used to create a UuidV4 must represent a version 4 (random) UUID',
            );
        }

        parent::__construct($fields, $numberConverter, $codec, $timeConverter);
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Rfc4122;

use Ramsey\Uuid\Codec\CodecInterface;
use Ramsey\Uuid\Converter\NumberConverterInterface;
use Ramsey\Uuid\Converter\TimeConverterInterface;
use Ramsey\Uuid\Exception\InvalidArgumentException;
use Ramsey\Uuid\Rfc4122\FieldsInterface as Rfc4122FieldsInterface;
use Ramsey\Uuid\Uuid;

/**
 * Custom format, or version 8, UUIDs provide an RFC-compatible format for experimental or vendor-specific uses
 *
 * The only requirement for version 8 UUIDs is that the version and variant bits must be set. Otherwise, implementations
 * are free to set the other bits according to their needs. As a result, the uniqueness of version 8 UUIDs is
 * implementation-specific and should not be assumed.
 *
 * @link https://www.rfc-editor.org/rfc/rfc9562#section-5.8 RFC 9562, 5.8. UUID Version 8
 *
 * @immutable
 */
final class UuidV8 extends Uuid implements UuidInterface
{
    /**
     * Creates a version 8 (custom format) UUID
     *
     * @param Rfc4122FieldsInterface $fields The fields from which to construct a UUID
     * @param NumberConverterInterface $numberConverter The number converter to use for converting hex values to/from integers
     * @param CodecInterface $codec The codec to use when encoding or decoding UUID strings
     * @param TimeConverterInterface $timeConverter The time converter to use for converting timestamps extracted from a
     *     UUID to unix timestamps
     */
    public function __construct(
        Rfc4122FieldsInterface $fields,
        NumberConverterInterface $numberConverter,
        CodecInterface $codec,
        TimeConverterInterface $timeConverter,
    ) {
        if ($fields->getVersion() !== Uuid::UUID_TYPE_CUSTOM) {
            throw new InvalidArgumentException(
                'Fields used to create a UuidV8 must represent a version 8 (custom format) UUID',
            );
        }

        parent::__construct($fields, $numberConverter, $codec, $timeConverter);
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Rfc4122;

use Ramsey\Uuid\Fields\FieldsInterface as BaseFieldsInterface;
use Ramsey\Uuid\Type\Hexadecimal;

/**
 * UUID fields, as defined by RFC 4122
 *
 * This interface defines the fields of an RFC 4122 variant UUID. Since RFC 9562 removed the concept of fields and
 * instead defined layouts that are specific to a given version, this interface is a legacy artifact of the earlier, and
 * now obsolete, RFC 4122.
 *
 * The fields of an RFC 4122 variant UUID are:
 *
 * * **time_low**: The low field of the timestamp, an unsigned 32-bit integer
 * * **time_mid**: The middle field of the timestamp, an unsigned 16-bit integer
 * * **time_hi_and_version**: The high field of the timestamp multiplexed with the version number, an unsigned 16-bit integer
 * * **clock_seq_hi_and_reserved**: The high field of the clock sequence multiplexed with the variant, an unsigned 8-bit integer
 * * **clock_seq_low**: The low field of the clock sequence, an unsigned 8-bit integer
 * * **node**: The spatially unique node identifier, an unsigned 48-bit integer
 *
 * @link https://www.rfc-editor.org/rfc/rfc4122#section-4.1 RFC 4122, 4.1. Format
 * @link https://www.rfc-editor.org/rfc/rfc9562#section-4 RFC 9562, 4. UUID Format
 *
 * @immutable
 */
interface FieldsInterface extends BaseFieldsInterface
{
    /**
     * Returns the full 16-bit clock sequence, with the variant bits (two most significant bits) masked out
     */
    public function getClockSeq(): Hexadecimal;

    /**
     * Returns the high field of the clock sequence multiplexed with the variant
     */
    public function getClockSeqHiAndReserved(): Hexadecimal;

    /**
     * Returns the low field of the clock sequence
     */
    public function getClockSeqLow(): Hexadecimal;

    /**
     * Returns the node field
     */
    public function getNode(): Hexadecimal;

    /**
     * Returns the high field of the timestamp multiplexed with the version
     */
    public function getTimeHiAndVersion(): Hexadecimal;

    /**
     * Returns the low field of the timestamp
     */
    public function getTimeLow(): Hexadecimal;

    /**
     * Returns the middle field of the timestamp
     */
    public function getTimeMid(): Hexadecimal;

    /**
     * Returns the full 60-bit timestamp, without the version
     */
    public function getTimestamp(): Hexadecimal;

    /**
     * Returns the variant
     *
     * The variant number describes the layout of the UUID. The variant number has the following meaning:
     *
     * - 0 - Reserved for NCS backward compatibility
     * - 2 - The RFC 9562 (formerly RFC 4122) variant
     * - 6 - Reserved, Microsoft Corporation backward compatibility
     * - 7 - Reserved for future definition
     *
     * For RFC 9562 (formerly RFC 4122) variant UUIDs, this value should always be the integer `2`.
     *
     * @link https://www.rfc-editor.org/rfc/rfc9562#section-4.1 RFC 9562, 4.1. Variant Field
     */
    public function getVariant(): int;

    /**
     * Returns the UUID version
     *
     * The version number describes how the UUID was generated and has the following meaning:
     *
     * 1. Gregorian time UUID
     * 2. DCE security UUID
     * 3. Name-based UUID hashed with MD5
     * 4. Randomly generated UUID
     * 5. Name-based UUID hashed with SHA-1
     * 6. Reordered Gregorian time UUID
     * 7. Unix Epoch time UUID
     * 8. Custom format UUID
     *
     * This returns `null` if the UUID is not an RFC 9562 (formerly RFC 4122) variant, since the version is only
     * meaningful for this variant.
     *
     * @link https://www.rfc-editor.org/rfc/rfc9562#section-4.2 RFC 9562, 4.2. Version Field
     *
     * @pure
     */
    public function getVersion(): ?int;

    /**
     * Returns true if these fields represent a nil UUID
     *
     * The nil UUID is a special form of UUID that is specified to have all 128 bits set to zero.
     *
     * @pure
     */
    public function isNil(): bool;
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Rfc4122;

use Ramsey\Uuid\Nonstandard\UuidV6 as NonstandardUuidV6;

/**
 * Reordered Gregorian time, or version 6, UUIDs include timestamp, clock sequence, and node values that are combined
 * into a 128-bit unsigned integer
 *
 * @link https://www.rfc-editor.org/rfc/rfc9562#section-5.6 RFC 9562, 5.6. UUID Version 6
 *
 * @immutable
 */
final class UuidV6 extends NonstandardUuidV6 implements UuidInterface
{
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Rfc4122;

use Ramsey\Uuid\Uuid;

/**
 * The max UUID is a special form of UUID that has all 128 bits set to one (`1`)
 *
 * @link https://www.rfc-editor.org/rfc/rfc9562#section-5.10 RFC 9562, 5.10. Max UUID
 *
 * @immutable
 */
final class MaxUuid extends Uuid implements UuidInterface
{
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Fields;

use ValueError;

use function base64_decode;
use function sprintf;
use function strlen;

/**
 * Provides common serialization functionality to fields
 *
 * @immutable
 */
trait SerializableFieldsTrait
{
    /**
     * @param string $bytes The bytes that comprise the fields
     */
    abstract public function __construct(string $bytes);

    /**
     * Returns the bytes that comprise the fields
     */
    abstract public function getBytes(): string;

    /**
     * Returns a string representation of the object
     */
    public function serialize(): string
    {
        return $this->getBytes();
    }

    /**
     * @return array{bytes: string}
     */
    public function __serialize(): array
    {
        return ['bytes' => $this->getBytes()];
    }

    /**
     * Constructs the object from a serialized string representation
     *
     * @param string $data The serialized string representation of the object
     */
    public function unserialize(string $data): void
    {
        if (strlen($data) === 16) {
            $this->__construct($data);
        } else {
            $this->__construct(base64_decode($data));
        }
    }

    /**
     * @param array{bytes?: string} $data
     */
    public function __unserialize(array $data): void
    {
        // @codeCoverageIgnoreStart
        if (!isset($data['bytes'])) {
            throw new ValueError(sprintf('%s(): Argument #1 ($data) is invalid', __METHOD__));
        }
        // @codeCoverageIgnoreEnd

        $this->unserialize($data['bytes']);
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Fields;

use Serializable;

/**
 * UUIDs consist of unsigned integers, the bytes of which are separated into fields and arranged in a particular layout
 * defined by the specification for the variant
 *
 * @immutable
 */
interface FieldsInterface extends Serializable
{
    /**
     * Returns the bytes that comprise the fields
     *
     * @pure
     */
    public function getBytes(): string;
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Validator;

use Ramsey\Uuid\Uuid;

use function preg_match;
use function str_replace;

/**
 * GenericValidator validates strings as UUIDs of any variant
 *
 * @immutable
 */
final class GenericValidator implements ValidatorInterface
{
    /**
     * Regular expression pattern for matching a UUID of any variant.
     */
    private const VALID_PATTERN = '\A[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}\z';

    /**
     * @return non-empty-string
     */
    public function getPattern(): string
    {
        return self::VALID_PATTERN;
    }

    public function validate(string $uuid): bool
    {
        /** @phpstan-ignore possiblyImpure.functionCall */
        $uuid = str_replace(['urn:', 'uuid:', 'URN:', 'UUID:', '{', '}'], '', $uuid);

        /** @phpstan-ignore possiblyImpure.functionCall */
        return $uuid === Uuid::NIL || preg_match('/' . self::VALID_PATTERN . '/Dms', $uuid);
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Validator;

/**
 * A validator validates a string as a proper UUID
 *
 * @immutable
 */
interface ValidatorInterface
{
    /**
     * Returns the regular expression pattern used by this validator
     *
     * @return non-empty-string The regular expression pattern this validator uses
     */
    public function getPattern(): string;

    /**
     * Returns true if the provided string represents a UUID
     *
     * @param string $uuid The string to validate as a UUID
     *
     * @return bool True if the string is a valid UUID, false otherwise
     *
     * @pure
     */
    public function validate(string $uuid): bool;
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid;

use DateTimeInterface;
use Ramsey\Uuid\Builder\UuidBuilderInterface;
use Ramsey\Uuid\Codec\CodecInterface;
use Ramsey\Uuid\Converter\NumberConverterInterface;
use Ramsey\Uuid\Converter\TimeConverterInterface;
use Ramsey\Uuid\Generator\DceSecurityGeneratorInterface;
use Ramsey\Uuid\Generator\DefaultTimeGenerator;
use Ramsey\Uuid\Generator\NameGeneratorInterface;
use Ramsey\Uuid\Generator\RandomGeneratorInterface;
use Ramsey\Uuid\Generator\TimeGeneratorInterface;
use Ramsey\Uuid\Generator\UnixTimeGenerator;
use Ramsey\Uuid\Lazy\LazyUuidFromString;
use Ramsey\Uuid\Provider\NodeProviderInterface;
use Ramsey\Uuid\Provider\Time\FixedTimeProvider;
use Ramsey\Uuid\Type\Hexadecimal;
use Ramsey\Uuid\Type\Integer as IntegerObject;
use Ramsey\Uuid\Type\Time;
use Ramsey\Uuid\Validator\ValidatorInterface;

use function bin2hex;
use function hex2bin;
use function pack;
use function str_pad;
use function strtolower;
use function substr;
use function substr_replace;
use function unpack;

use const STR_PAD_LEFT;

class UuidFactory implements UuidFactoryInterface
{
    private CodecInterface $codec;
    private DceSecurityGeneratorInterface $dceSecurityGenerator;
    private NameGeneratorInterface $nameGenerator;
    private NodeProviderInterface $nodeProvider;
    private NumberConverterInterface $numberConverter;
    private RandomGeneratorInterface $randomGenerator;
    private TimeConverterInterface $timeConverter;
    private TimeGeneratorInterface $timeGenerator;
    private TimeGeneratorInterface $unixTimeGenerator;
    private UuidBuilderInterface $uuidBuilder;
    private ValidatorInterface $validator;

    /**
     * @var bool whether the feature set was provided from outside, or we can operate under "default" assumptions
     */
    private bool $isDefaultFeatureSet;

    /**
     * @param FeatureSet | null $features A set of available features in the current environment
     */
    public function __construct(?FeatureSet $features = null)
    {
        $this->isDefaultFeatureSet = $features === null;

        $features = $features ?: new FeatureSet();

        $this->codec = $features->getCodec();
        $this->dceSecurityGenerator = $features->getDceSecurityGenerator();
        $this->nameGenerator = $features->getNameGenerator();
        $this->nodeProvider = $features->getNodeProvider();
        $this->numberConverter = $features->getNumberConverter();
        $this->randomGenerator = $features->getRandomGenerator();
        $this->timeConverter = $features->getTimeConverter();
        $this->timeGenerator = $features->getTimeGenerator();
        $this->uuidBuilder = $features->getBuilder();
        $this->validator = $features->getValidator();
        $this->unixTimeGenerator = $features->getUnixTimeGenerator();
    }

    /**
     * Returns the codec used by this factory
     */
    public function getCodec(): CodecInterface
    {
        return $this->codec;
    }

    /**
     * Sets the codec to use for this factory
     *
     * @param CodecInterface $codec A UUID encoder-decoder
     */
    public function setCodec(CodecInterface $codec): void
    {
        $this->isDefaultFeatureSet = false;

        $this->codec = $codec;
    }

    /**
     * Returns the name generator used by this factory
     */
    public function getNameGenerator(): NameGeneratorInterface
    {
        return $this->nameGenerator;
    }

    /**
     * Sets the name generator to use for this factory
     *
     * @param NameGeneratorInterface $nameGenerator A generator to generate binary data, based on a namespace and name
     */
    public function setNameGenerator(NameGeneratorInterface $nameGenerator): void
    {
        $this->isDefaultFeatureSet = false;

        $this->nameGenerator = $nameGenerator;
    }

    /**
     * Returns the node provider used by this factory
     */
    public function getNodeProvider(): NodeProviderInterface
    {
        return $this->nodeProvider;
    }

    /**
     * Returns the random generator used by this factory
     */
    public function getRandomGenerator(): RandomGeneratorInterface
    {
        return $this->randomGenerator;
    }

    /**
     * Returns the time generator used by this factory
     */
    public function getTimeGenerator(): TimeGeneratorInterface
    {
        return $this->timeGenerator;
    }

    /**
     * Sets the time generator to use for this factory
     *
     * @param TimeGeneratorInterface $generator A generator to generate binary data, based on the time
     */
    public function setTimeGenerator(TimeGeneratorInterface $generator): void
    {
        $this->isDefaultFeatureSet = false;

        $this->timeGenerator = $generator;
    }

    /**
     * Returns the DCE Security generator used by this factory
     */
    public function getDceSecurityGenerator(): DceSecurityGeneratorInterface
    {
        return $this->dceSecurityGenerator;
    }

    /**
     * Sets the DCE Security generator to use for this factory
     *
     * @param DceSecurityGeneratorInterface $generator A generator to generate binary data, based on a local domain and
     *     local identifier
     */
    public function setDceSecurityGenerator(DceSecurityGeneratorInterface $generator): void
    {
        $this->isDefaultFeatureSet = false;

        $this->dceSecurityGenerator = $generator;
    }

    /**
     * Returns the number converter used by this factory
     */
    public function getNumberConverter(): NumberConverterInterface
    {
        return $this->numberConverter;
    }

    /**
     * Sets the random generator to use for this factory
     *
     * @param RandomGeneratorInterface $generator A generator to generate binary data, based on some random input
     */
    public function setRandomGenerator(RandomGeneratorInterface $generator): void
    {
        $this->isDefaultFeatureSet = false;

        $this->randomGenerator = $generator;
    }

    /**
     * Sets the number converter to use for this factory
     *
     * @param NumberConverterInterface $converter A converter to use for working with large integers (i.e., integers
     *     greater than PHP_INT_MAX)
     */
    public function setNumberConverter(NumberConverterInterface $converter): void
    {
        $this->isDefaultFeatureSet = false;

        $this->numberConverter = $converter;
    }

    /**
     * Returns the UUID builder used by this factory
     */
    public function getUuidBuilder(): UuidBuilderInterface
    {
        return $this->uuidBuilder;
    }

    /**
     * Sets the UUID builder to use for this factory
     *
     * @param UuidBuilderInterface $builder A builder for constructing instances of UuidInterface
     */
    public function setUuidBuilder(UuidBuilderInterface $builder): void
    {
        $this->isDefaultFeatureSet = false;

        $this->uuidBuilder = $builder;
    }

    public function getValidator(): ValidatorInterface
    {
        return $this->validator;
    }

    /**
     * Sets the validator to use for this factory
     *
     * @param ValidatorInterface $validator A validator to use for validating whether a string is a valid UUID
     */
    public function setValidator(ValidatorInterface $validator): void
    {
        $this->isDefaultFeatureSet = false;

        $this->validator = $validator;
    }

    /**
     * @pure
     */
    public function fromBytes(string $bytes): UuidInterface
    {
        return $this->codec->decodeBytes($bytes);
    }

    /**
     * @pure
     */
    public function fromString(string $uuid): UuidInterface
    {
        $uuid = strtolower($uuid);

        return $this->codec->decode($uuid);
    }

    /**
     * @pure
     */
    public function fromInteger(string $integer): UuidInterface
    {
        $hex = $this->numberConverter->toHex($integer);
        $hex = str_pad($hex, 32, '0', STR_PAD_LEFT);

        return $this->fromString($hex);
    }

    public function fromDateTime(
        DateTimeInterface $dateTime,
        ?Hexadecimal $node = null,
        ?int $clockSeq = null,
    ): UuidInterface {
        $timeProvider = new FixedTimeProvider(new Time($dateTime->format('U'), $dateTime->format('u')));
        $timeGenerator = new DefaultTimeGenerator($this->nodeProvider, $this->timeConverter, $timeProvider);
        $bytes = $timeGenerator->generate($node?->toString(), $clockSeq);

        return $this->uuidFromBytesAndVersion($bytes, Uuid::UUID_TYPE_TIME);
    }

    /**
     * @pure
     */
    public function fromHexadecimal(Hexadecimal $hex): UuidInterface
    {
        return $this->codec->decode($hex->__toString());
    }

    /**
     * @inheritDoc
     */
    public function uuid1($node = null, ?int $clockSeq = null): UuidInterface
    {
        $bytes = $this->timeGenerator->generate($node, $clockSeq);

        return $this->uuidFromBytesAndVersion($bytes, Uuid::UUID_TYPE_TIME);
    }

    public function uuid2(
        int $localDomain,
        ?IntegerObject $localIdentifier = null,
        ?Hexadecimal $node = null,
        ?int $clockSeq = null,
    ): UuidInterface {
        $bytes = $this->dceSecurityGenerator->generate($localDomain, $localIdentifier, $node, $clockSeq);

        return $this->uuidFromBytesAndVersion($bytes, Uuid::UUID_TYPE_DCE_SECURITY);
    }

    /**
     * @inheritDoc
     * @pure
     */
    public function uuid3($ns, string $name): UuidInterface
    {
        return $this->uuidFromNsAndName($ns, $name, Uuid::UUID_TYPE_HASH_MD5, 'md5');
    }

    public function uuid4(): UuidInterface
    {
        $bytes = $this->randomGenerator->generate(16);

        return $this->uuidFromBytesAndVersion($bytes, Uuid::UUID_TYPE_RANDOM);
    }

    /**
     * @inheritDoc
     * @pure
     */
    public function uuid5($ns, string $name): UuidInterface
    {
        return $this->uuidFromNsAndName($ns, $name, Uuid::UUID_TYPE_HASH_SHA1, 'sha1');
    }

    public function uuid6(?Hexadecimal $node = null, ?int $clockSeq = null): UuidInterface
    {
        $bytes = $this->timeGenerator->generate($node?->toString(), $clockSeq);

        // Rearrange the bytes, according to the UUID version 6 specification.
        $v6 = $bytes[6] . $bytes[7] . $bytes[4] . $bytes[5]
            . $bytes[0] . $bytes[1] . $bytes[2] . $bytes[3];
        $v6 = bin2hex($v6);

        // Drop the first four bits, while adding an empty four bits for the version field. This allows us to
        // reconstruct the correct time from the bytes of this UUID.
        $v6Bytes = hex2bin(substr($v6, 1, 12) . '0' . substr($v6, -3));
        $v6Bytes .= substr($bytes, 8);

        return $this->uuidFromBytesAndVersion($v6Bytes, Uuid::UUID_TYPE_REORDERED_TIME);
    }

    /**
     * Returns a version 7 (Unix Epoch time) UUID
     *
     * @param DateTimeInterface | null $dateTime An optional date/time from which to create the version 7 UUID. If not
     *     provided, the UUID is generated using the current date/time.
     *
     * @return UuidInterface A UuidInterface instance that represents a version 7 UUID
     */
    public function uuid7(?DateTimeInterface $dateTime = null): UuidInterface
    {
        assert($this->unixTimeGenerator instanceof UnixTimeGenerator);
        $bytes = $this->unixTimeGenerator->generate(null, null, $dateTime);

        return $this->uuidFromBytesAndVersion($bytes, Uuid::UUID_TYPE_UNIX_TIME);
    }

    /**
     * Returns a version 8 (custom format) UUID
     *
     * The bytes provided may contain any value according to your application's needs. Be aware, however, that other
     * applications may not understand the semantics of the value.
     *
     * @param string $bytes A 16-byte octet string. This is an open blob of data that you may fill with 128 bits of
     *     information. Be aware, however, bits 48 through 51 will be replaced with the UUID version field, and bits 64
     *     and 65 will be replaced with the UUID variant. You MUST NOT rely on these bits for your application needs.
     *
     * @return UuidInterface A UuidInterface instance that represents a version 8 UUID
     *
     * @pure
     */
    public function uuid8(string $bytes): UuidInterface
    {
        /** @phpstan-ignore possiblyImpure.methodCall */
        return $this->uuidFromBytesAndVersion($bytes, Uuid::UUID_TYPE_CUSTOM);
    }

    /**
     * Returns a Uuid created from the provided byte string
     *
     * Uses the configured builder and codec and the provided byte string to construct a Uuid object.
     *
     * @param string $bytes The byte string from which to construct a UUID
     *
     * @return UuidInterface An instance of UuidInterface, created from the provided bytes
     *
     * @pure
     */
    public function uuid(string $bytes): UuidInterface
    {
        return $this->uuidBuilder->build($this->codec, $bytes);
    }

    /**
     * Returns a version 3 or 5 namespaced Uuid
     *
     * @param UuidInterface | string $ns The namespace (must be a valid UUID)
     * @param string $name The name to hash together with the namespace
     * @param int $version The version of UUID to create (3 or 5)
     * @param string $hashAlgorithm The hashing algorithm to use when hashing together the namespace and name
     *
     * @return UuidInterface An instance of UuidInterface, created by hashing together the provided namespace and name
     *
     * @pure
     */
    private function uuidFromNsAndName(
        UuidInterface | string $ns,
        string $name,
        int $version,
        string $hashAlgorithm,
    ): UuidInterface {
        if (!($ns instanceof UuidInterface)) {
            $ns = $this->fromString($ns);
        }

        $bytes = $this->nameGenerator->generate($ns, $name, $hashAlgorithm);

        /** @phpstan-ignore possiblyImpure.methodCall */
        return $this->uuidFromBytesAndVersion(substr($bytes, 0, 16), $version);
    }

    /**
     * Returns a Uuid created from the provided bytes and version
     *
     * @param string $bytes The byte string to convert to a UUID
     * @param int $version The version to apply to the UUID
     *
     * @return UuidInterface An instance of UuidInterface, created from the byte string and version
     */
    private function uuidFromBytesAndVersion(string $bytes, int $version): UuidInterface
    {
        /** @var int[] $unpackedTime */
        $unpackedTime = unpack('n*', substr($bytes, 6, 2));
        $timeHi = $unpackedTime[1];
        $timeHiAndVersion = pack('n*', BinaryUtils::applyVersion($timeHi, $version));

        /** @var int[] $unpackedClockSeq */
        $unpackedClockSeq = unpack('n*', substr($bytes, 8, 2));
        $clockSeqHi = $unpackedClockSeq[1];
        $clockSeqHiAndReserved = pack('n*', BinaryUtils::applyVariant($clockSeqHi));

        $bytes = substr_replace($bytes, $timeHiAndVersion, 6, 2);
        $bytes = substr_replace($bytes, $clockSeqHiAndReserved, 8, 2);

        if ($this->isDefaultFeatureSet) {
            return LazyUuidFromString::fromBytes($bytes);
        }

        return $this->uuid($bytes);
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Codec;

use Ramsey\Uuid\Builder\UuidBuilderInterface;
use Ramsey\Uuid\Exception\InvalidArgumentException;
use Ramsey\Uuid\Exception\InvalidUuidStringException;
use Ramsey\Uuid\Uuid;
use Ramsey\Uuid\UuidInterface;

use function bin2hex;
use function hex2bin;
use function implode;
use function sprintf;
use function str_replace;
use function strlen;
use function substr;

/**
 * StringCodec encodes and decodes RFC 9562 (formerly RFC 4122) UUIDs
 *
 * @immutable
 */
class StringCodec implements CodecInterface
{
    /**
     * Constructs a StringCodec
     *
     * @param UuidBuilderInterface $builder The builder to use when encoding UUIDs
     */
    public function __construct(private UuidBuilderInterface $builder)
    {
    }

    public function encode(UuidInterface $uuid): string
    {
        /** @phpstan-ignore possiblyImpure.methodCall */
        $hex = bin2hex($uuid->getFields()->getBytes());

        /** @var non-empty-string */
        return sprintf(
            '%08s-%04s-%04s-%04s-%012s',
            substr($hex, 0, 8),
            substr($hex, 8, 4),
            substr($hex, 12, 4),
            substr($hex, 16, 4),
            substr($hex, 20),
        );
    }

    /**
     * @return non-empty-string
     */
    public function encodeBinary(UuidInterface $uuid): string
    {
        /** @phpstan-ignore-next-line PHPStan complains that this is not a non-empty-string. */
        return $uuid->getFields()->getBytes();
    }

    /**
     * @throws InvalidUuidStringException
     *
     * @inheritDoc
     */
    public function decode(string $encodedUuid): UuidInterface
    {
        /** @phpstan-ignore possiblyImpure.methodCall */
        return $this->builder->build($this, $this->getBytes($encodedUuid));
    }

    public function decodeBytes(string $bytes): UuidInterface
    {
        if (strlen($bytes) !== 16) {
            throw new InvalidArgumentException('$bytes string should contain 16 characters.');
        }

        return $this->builder->build($this, $bytes);
    }

    /**
     * Returns the UUID builder
     */
    protected function getBuilder(): UuidBuilderInterface
    {
        return $this->builder;
    }

    /**
     * Returns a byte string of the UUID
     */
    protected function getBytes(string $encodedUuid): string
    {
        $parsedUuid = str_replace(['urn:', 'uuid:', 'URN:', 'UUID:', '{', '}', '-'], '', $encodedUuid);

        $components = [
            substr($parsedUuid, 0, 8),
            substr($parsedUuid, 8, 4),
            substr($parsedUuid, 12, 4),
            substr($parsedUuid, 16, 4),
            substr($parsedUuid, 20),
        ];

        if (!Uuid::isValid(implode('-', $components))) {
            throw new InvalidUuidStringException('Invalid UUID string: ' . $encodedUuid);
        }

        return (string) hex2bin($parsedUuid);
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Codec;

use Ramsey\Uuid\UuidInterface;

/**
 * A codec encodes and decodes a UUID according to defined rules
 *
 * @immutable
 */
interface CodecInterface
{
    /**
     * Returns a hexadecimal string representation of a UuidInterface
     *
     * @param UuidInterface $uuid The UUID for which to create a hexadecimal string representation
     *
     * @return non-empty-string Hexadecimal string representation of a UUID
     *
     * @pure
     */
    public function encode(UuidInterface $uuid): string;

    /**
     * Returns a binary string representation of a UuidInterface
     *
     * @param UuidInterface $uuid The UUID for which to create a binary string representation
     *
     * @return non-empty-string Binary string representation of a UUID
     *
     * @pure
     */
    public function encodeBinary(UuidInterface $uuid): string;

    /**
     * Returns a UuidInterface derived from a hexadecimal string representation
     *
     * @param string $encodedUuid The hexadecimal string representation to convert into a UuidInterface instance
     *
     * @return UuidInterface An instance of a UUID decoded from a hexadecimal string representation
     *
     * @pure
     */
    public function decode(string $encodedUuid): UuidInterface;

    /**
     * Returns a UuidInterface derived from a binary string representation
     *
     * @param string $bytes The binary string representation to convert into a UuidInterface instance
     *
     * @return UuidInterface An instance of a UUID decoded from a binary string representation
     *
     * @pure
     */
    public function decodeBytes(string $bytes): UuidInterface;
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Codec;

use Ramsey\Uuid\Exception\InvalidUuidStringException;
use Ramsey\Uuid\UuidInterface;

use function bin2hex;
use function sprintf;
use function substr;
use function substr_replace;

/**
 * TimestampFirstCombCodec encodes and decodes COMBs, with the timestamp as the first 48 bits
 *
 * In contrast with the TimestampLastCombCodec, the TimestampFirstCombCodec adds the timestamp to the first 48 bits of
 * the COMB. To generate a timestamp-first COMB, set the TimestampFirstCombCodec as the codec, along with the
 * CombGenerator as the random generator.
 *
 * ```
 * $factory = new UuidFactory();
 *
 * $factory->setCodec(new TimestampFirstCombCodec($factory->getUuidBuilder()));
 *
 * $factory->setRandomGenerator(new CombGenerator(
 *     $factory->getRandomGenerator(),
 *     $factory->getNumberConverter(),
 * ));
 *
 * $timestampFirstComb = $factory->uuid4();
 * ```
 *
 * @deprecated Please migrate to {@link https://uuid.ramsey.dev/en/stable/rfc4122/version7.html Version 7, Unix Epoch Time UUIDs}.
 *
 * @link https://web.archive.org/web/20240118030355/https://www.informit.com/articles/printerfriendly/25862 The Cost of GUIDs as Primary Keys
 *
 * @immutable
 */
class TimestampFirstCombCodec extends StringCodec
{
    /**
     * @return non-empty-string
     */
    public function encode(UuidInterface $uuid): string
    {
        /** @phpstan-ignore possiblyImpure.methodCall */
        $bytes = $this->swapBytes($uuid->getFields()->getBytes());

        return sprintf(
            '%08s-%04s-%04s-%04s-%012s',
            bin2hex(substr($bytes, 0, 4)),
            bin2hex(substr($bytes, 4, 2)),
            bin2hex(substr($bytes, 6, 2)),
            bin2hex(substr($bytes, 8, 2)),
            bin2hex(substr($bytes, 10))
        );
    }

    /**
     * @return non-empty-string
     */
    public function encodeBinary(UuidInterface $uuid): string
    {
        /** @phpstan-ignore-next-line PHPStan complains that this is not a non-empty-string. */
        return $this->swapBytes($uuid->getFields()->getBytes());
    }

    /**
     * @throws InvalidUuidStringException
     *
     * @inheritDoc
     */
    public function decode(string $encodedUuid): UuidInterface
    {
        /** @phpstan-ignore possiblyImpure.methodCall */
        $bytes = $this->getBytes($encodedUuid);

        /** @phpstan-ignore possiblyImpure.methodCall */
        return $this->getBuilder()->build($this, $this->swapBytes($bytes));
    }

    public function decodeBytes(string $bytes): UuidInterface
    {
        /** @phpstan-ignore possiblyImpure.methodCall */
        return $this->getBuilder()->build($this, $this->swapBytes($bytes));
    }

    /**
     * Swaps bytes according to the timestamp-first COMB rules
     *
     * @pure
     */
    private function swapBytes(string $bytes): string
    {
        $first48Bits = substr($bytes, 0, 6);
        $last48Bits = substr($bytes, -6);

        return substr_replace(substr_replace($bytes, $last48Bits, 0, 6), $first48Bits, -6);
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Codec;

use Ramsey\Uuid\Exception\InvalidArgumentException;
use Ramsey\Uuid\Exception\UnsupportedOperationException;
use Ramsey\Uuid\Rfc4122\FieldsInterface as Rfc4122FieldsInterface;
use Ramsey\Uuid\Uuid;
use Ramsey\Uuid\UuidInterface;

use function strlen;
use function substr;

/**
 * OrderedTimeCodec encodes and decodes a UUID, optimizing the byte order for more efficient storage
 *
 * For binary representations of version 1 UUID, this codec may be used to reorganize the time fields, making the UUID
 * closer to sequential when storing the bytes. According to Percona, this optimization can improve database INSERT and
 * SELECT statements using the UUID column as a key.
 *
 * The string representation of the UUID will remain unchanged. Only the binary representation is reordered.
 *
 * PLEASE NOTE: Binary representations of UUIDs encoded with this codec must be decoded with this codec. Decoding using
 * another codec can result in malformed UUIDs.
 *
 * @deprecated Please migrate to {@link https://uuid.ramsey.dev/en/stable/rfc4122/version6.html Version 6, reordered time-based UUIDs}.
 *
 * @link https://www.percona.com/blog/2014/12/19/store-uuid-optimized-way/ Storing UUID Values in MySQL
 *
 * @immutable
 */
class OrderedTimeCodec extends StringCodec
{
    /**
     * Returns a binary string representation of a UUID, with the timestamp fields rearranged for optimized storage
     *
     * @return non-empty-string
     */
    public function encodeBinary(UuidInterface $uuid): string
    {
        if (
            /** @phpstan-ignore possiblyImpure.methodCall */
            !($uuid->getFields() instanceof Rfc4122FieldsInterface)
            /** @phpstan-ignore possiblyImpure.methodCall */
            || $uuid->getFields()->getVersion() !== Uuid::UUID_TYPE_TIME
        ) {
            throw new InvalidArgumentException('Expected version 1 (time-based) UUID');
        }

        /** @phpstan-ignore possiblyImpure.methodCall */
        $bytes = $uuid->getFields()->getBytes();

        return $bytes[6] . $bytes[7] . $bytes[4] . $bytes[5]
            . $bytes[0] . $bytes[1] . $bytes[2] . $bytes[3]
            . substr($bytes, 8);
    }

    /**
     * Returns a UuidInterface derived from an ordered-time binary string representation
     *
     * @throws InvalidArgumentException if $bytes is an invalid length
     *
     * @inheritDoc
     */
    public function decodeBytes(string $bytes): UuidInterface
    {
        if (strlen($bytes) !== 16) {
            throw new InvalidArgumentException('$bytes string should contain 16 characters.');
        }

        // Rearrange the bytes to their original order.
        $rearrangedBytes = $bytes[4] . $bytes[5] . $bytes[6] . $bytes[7]
            . $bytes[2] . $bytes[3] . $bytes[0] . $bytes[1]
            . substr($bytes, 8);

        $uuid = parent::decodeBytes($rearrangedBytes);

        /** @phpstan-ignore possiblyImpure.methodCall */
        $fields = $uuid->getFields();

        if (!$fields instanceof Rfc4122FieldsInterface || $fields->getVersion() !== Uuid::UUID_TYPE_TIME) {
            throw new UnsupportedOperationException(
                'Attempting to decode a non-time-based UUID using OrderedTimeCodec',
            );
        }

        return $uuid;
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Codec;

/**
 * TimestampLastCombCodec encodes and decodes COMBs, with the timestamp as the last 48 bits
 *
 * The CombGenerator when used with the StringCodec (and, by proxy, the TimestampLastCombCodec) adds the timestamp to
 * the last 48 bits of the COMB. The TimestampLastCombCodec is provided for the sake of consistency. In practice, it is
 * identical to the standard StringCodec, but it may be used with the CombGenerator for additional context when reading
 * code.
 *
 * Consider the following code. By default, the codec used by UuidFactory is the StringCodec, but here, we explicitly
 * set the TimestampLastCombCodec. It is redundant, but it is clear that we intend this COMB to be generated with the
 * timestamp appearing at the end.
 *
 * ```
 * $factory = new UuidFactory();
 *
 * $factory->setCodec(new TimestampLastCombCodec($factory->getUuidBuilder()));
 *
 * $factory->setRandomGenerator(new CombGenerator(
 *     $factory->getRandomGenerator(),
 *     $factory->getNumberConverter(),
 * ));
 *
 * $timestampLastComb = $factory->uuid4();
 * ```
 *
 * @deprecated Please use {@see StringCodec} instead.
 *
 * @immutable
 */
class TimestampLastCombCodec extends StringCodec
{
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Codec;

use Ramsey\Uuid\Guid\Guid;
use Ramsey\Uuid\UuidInterface;

use function bin2hex;
use function sprintf;
use function substr;

/**
 * GuidStringCodec encodes and decodes globally unique identifiers (GUID)
 *
 * @see Guid
 *
 * @immutable
 */
class GuidStringCodec extends StringCodec
{
    public function encode(UuidInterface $uuid): string
    {
        /** @phpstan-ignore possiblyImpure.methodCall */
        $hex = bin2hex($uuid->getFields()->getBytes());

        /** @var non-empty-string */
        return sprintf(
            '%02s%02s%02s%02s-%02s%02s-%02s%02s-%04s-%012s',
            substr($hex, 6, 2),
            substr($hex, 4, 2),
            substr($hex, 2, 2),
            substr($hex, 0, 2),
            substr($hex, 10, 2),
            substr($hex, 8, 2),
            substr($hex, 14, 2),
            substr($hex, 12, 2),
            substr($hex, 16, 4),
            substr($hex, 20),
        );
    }

    public function decode(string $encodedUuid): UuidInterface
    {
        /** @phpstan-ignore possiblyImpure.methodCall */
        $bytes = $this->getBytes($encodedUuid);

        /** @phpstan-ignore possiblyImpure.methodCall, possiblyImpure.methodCall */
        return $this->getBuilder()->build($this, $this->swapBytes($bytes));
    }

    public function decodeBytes(string $bytes): UuidInterface
    {
        // Call parent::decode() to preserve the correct byte order.
        return parent::decode(bin2hex($bytes));
    }

    /**
     * Swaps bytes according to the GUID rules
     */
    private function swapBytes(string $bytes): string
    {
        return $bytes[3] . $bytes[2] . $bytes[1] . $bytes[0]
            . $bytes[5] . $bytes[4] . $bytes[7] . $bytes[6]
            . substr($bytes, 8);
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Math;

use Brick\Math\BigDecimal;
use Brick\Math\BigInteger;
use Brick\Math\Exception\MathException;
use Ramsey\Uuid\Exception\InvalidArgumentException;
use Ramsey\Uuid\Type\Decimal;
use Ramsey\Uuid\Type\Hexadecimal;
use Ramsey\Uuid\Type\Integer as IntegerObject;
use Ramsey\Uuid\Type\NumberInterface;

/**
 * A calculator using the brick/math library for arbitrary-precision arithmetic
 *
 * @immutable
 */
final class BrickMathCalculator implements CalculatorInterface
{
    public function add(NumberInterface $augend, NumberInterface ...$addends): NumberInterface
    {
        $sum = BigInteger::of($augend->toString());

        foreach ($addends as $addend) {
            $sum = $sum->plus($addend->toString());
        }

        /** @phpstan-ignore possiblyImpure.new */
        return new IntegerObject((string) $sum);
    }

    public function subtract(NumberInterface $minuend, NumberInterface ...$subtrahends): NumberInterface
    {
        $difference = BigInteger::of($minuend->toString());

        foreach ($subtrahends as $subtrahend) {
            $difference = $difference->minus($subtrahend->toString());
        }

        /** @phpstan-ignore possiblyImpure.new */
        return new IntegerObject((string) $difference);
    }

    public function multiply(NumberInterface $multiplicand, NumberInterface ...$multipliers): NumberInterface
    {
        $product = BigInteger::of($multiplicand->toString());

        foreach ($multipliers as $multiplier) {
            $product = $product->multipliedBy($multiplier->toString());
        }

        /** @phpstan-ignore possiblyImpure.new */
        return new IntegerObject((string) $product);
    }

    public function divide(
        int $roundingMode,
        int $scale,
        NumberInterface $dividend,
        NumberInterface ...$divisors,
    ): NumberInterface {
        /** @phpstan-ignore possiblyImpure.methodCall */
        $brickRounding = BrickMathRoundingMode::resolve($roundingMode);

        $quotient = BigDecimal::of($dividend->toString());

        foreach ($divisors as $divisor) {
            $quotient = $quotient->dividedBy($divisor->toString(), $scale, $brickRounding);
        }

        if ($scale === 0) {
            /** @phpstan-ignore possiblyImpure.new */
            return new IntegerObject((string) $quotient->toBigInteger());
        }

        /** @phpstan-ignore possiblyImpure.new */
        return new Decimal((string) $quotient);
    }

    public function fromBase(string $value, int $base): IntegerObject
    {
        try {
            /** @phpstan-ignore possiblyImpure.new */
            return new IntegerObject((string) BigInteger::fromBase($value, $base));
        } catch (MathException | \InvalidArgumentException $exception) {
            throw new InvalidArgumentException(
                $exception->getMessage(), /** @phpstan-ignore possiblyImpure.methodCall */
                (int) $exception->getCode(), /** @phpstan-ignore possiblyImpure.methodCall */
                $exception
            );
        }
    }

    public function toBase(IntegerObject $value, int $base): string
    {
        try {
            return BigInteger::of($value->toString())->toBase($base);
        } catch (MathException | \InvalidArgumentException $exception) {
            throw new InvalidArgumentException(
                $exception->getMessage(), /** @phpstan-ignore possiblyImpure.methodCall */
                (int) $exception->getCode(), /** @phpstan-ignore possiblyImpure.methodCall */
                $exception
            );
        }
    }

    public function toHexadecimal(IntegerObject $value): Hexadecimal
    {
        /** @phpstan-ignore possiblyImpure.new */
        return new Hexadecimal($this->toBase($value, 16));
    }

    public function toInteger(Hexadecimal $value): IntegerObject
    {
        return $this->fromBase($value->toString(), 16);
    }
}
<?php

/**
 * This file was originally part of brick/math
 *
 * Copyright (c) 2013-present Benjamin Morel
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 * @link https://github.com/brick/math brick/math at GitHub
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Math;

/**
 * Specifies a rounding behavior for numerical operations capable of discarding precision.
 *
 * Each rounding mode indicates how the least significant returned digit of a rounded result is to be calculated. If
 * fewer digits are returned than the digits needed to represent the exact numerical result, the discarded digits will
 * be referred to as the discarded fraction regardless of the digits' contribution to the value of the number. In other
 * words, considered as a numerical value, the discarded fraction could have an absolute value greater than one.
 */
final class RoundingMode
{
    /**
     * Asserts that the requested operation has an exact result; hence no rounding is necessary.
     */
    public const UNNECESSARY = 0;

    /**
     * Rounds away from zero.
     *
     * Always increments the digit prior to a nonzero discarded fraction. Note that this rounding mode never decreases
     * the magnitude of the calculated value.
     */
    public const UP = 1;

    /**
     * Rounds towards zero.
     *
     * Never increments the digit prior to a discarded fraction (i.e., truncates). Note that this rounding mode never
     * increases the magnitude of the calculated value.
     */
    public const DOWN = 2;

    /**
     * Rounds towards positive infinity.
     *
     * If the result is positive, behaves as for UP; if negative, behaves as for DOWN. Note that this rounding mode
     * never decreases the calculated value.
     */
    public const CEILING = 3;

    /**
     * Rounds towards negative infinity.
     *
     * If the result is positive, behave as for DOWN; if negative, behave as for UP. Note that this rounding mode never
     * increases the calculated value.
     */
    public const FLOOR = 4;

    /**
     * Rounds towards "nearest neighbor" unless both neighbors are equidistant, in which case round up.
     *
     * Behaves as for UP if the discarded fraction is >= 0.5; otherwise, behaves as for DOWN. Note that this is the
     * rounding mode commonly taught at school.
     */
    public const HALF_UP = 5;

    /**
     * Rounds towards "nearest neighbor" unless both neighbors are equidistant, in which case round down.
     *
     * Behaves as for UP if the discarded fraction is > 0.5; otherwise, behaves as for DOWN.
     */
    public const HALF_DOWN = 6;

    /**
     * Rounds towards "nearest neighbor" unless both neighbors are equidistant, in which case round towards positive infinity.
     *
     * If the result is positive, behaves as for HALF_UP; if negative, behaves as for HALF_DOWN.
     */
    public const HALF_CEILING = 7;

    /**
     * Rounds towards "nearest neighbor" unless both neighbors are equidistant, in which case round towards negative infinity.
     *
     * If the result is positive, behaves as for HALF_DOWN; if negative, behaves as for HALF_UP.
     */
    public const HALF_FLOOR = 8;

    /**
     * Rounds towards the "nearest neighbor" unless both neighbors are equidistant, in which case rounds towards the even neighbor.
     *
     * Behaves as for HALF_UP if the digit to the left of the discarded fraction is odd; behaves as for HALF_DOWN if it's even.
     *
     * Note that this is the rounding mode that statistically minimizes cumulative error when applied repeatedly over a
     * sequence of calculations. It is sometimes known as "Banker's rounding", and is chiefly used in the USA.
     */
    public const HALF_EVEN = 9;

    /**
     * Private constructor. This class is not instantiable.
     *
     * @codeCoverageIgnore
     */
    private function __construct()
    {
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Math;

use Ramsey\Uuid\Type\Hexadecimal;
use Ramsey\Uuid\Type\Integer as IntegerObject;
use Ramsey\Uuid\Type\NumberInterface;

/**
 * A calculator performs arithmetic operations on numbers
 *
 * @immutable
 */
interface CalculatorInterface
{
    /**
     * Returns the sum of all the provided parameters
     *
     * @param NumberInterface $augend The first addend (the integer being added to)
     * @param NumberInterface ...$addends The additional integers to a add to the augend
     *
     * @return NumberInterface The sum of all the parameters
     *
     * @pure
     */
    public function add(NumberInterface $augend, NumberInterface ...$addends): NumberInterface;

    /**
     * Returns the difference of all the provided parameters
     *
     * @param NumberInterface $minuend The integer being subtracted from
     * @param NumberInterface ...$subtrahends The integers to subtract from the minuend
     *
     * @return NumberInterface The difference after subtracting all parameters
     *
     * @pure
     */
    public function subtract(NumberInterface $minuend, NumberInterface ...$subtrahends): NumberInterface;

    /**
     * Returns the product of all the provided parameters
     *
     * @param NumberInterface $multiplicand The integer to be multiplied
     * @param NumberInterface ...$multipliers The factors by which to multiply the multiplicand
     *
     * @return NumberInterface The product of multiplying all the provided parameters
     *
     * @pure
     */
    public function multiply(NumberInterface $multiplicand, NumberInterface ...$multipliers): NumberInterface;

    /**
     * Returns the quotient of the provided parameters divided left-to-right
     *
     * @param int $roundingMode The RoundingMode constant to use for this operation
     * @param int<0, max> $scale The scale to use for this operation
     * @param NumberInterface $dividend The integer to be divided
     * @param NumberInterface ...$divisors The integers to divide $dividend by, in the order in which the division
     *     operations should take place (left-to-right)
     *
     * @return NumberInterface The quotient of dividing the provided parameters left-to-right
     *
     * @pure
     */
    public function divide(
        int $roundingMode,
        int $scale,
        NumberInterface $dividend,
        NumberInterface ...$divisors,
    ): NumberInterface;

    /**
     * Converts a value from an arbitrary base to a base-10 integer value
     *
     * @param non-empty-string $value The value to convert
     * @param int<2, 36> $base The base to convert from (i.e., 2, 16, 32, etc.)
     *
     * @return IntegerObject The base-10 integer value of the converted value
     *
     * @pure
     */
    public function fromBase(string $value, int $base): IntegerObject;

    /**
     * Converts a base-10 integer value to an arbitrary base
     *
     * @param IntegerObject $value The integer value to convert
     * @param int<2, 36> $base The base to convert to (i.e., 2, 16, 32, etc.)
     *
     * @return string The value represented in the specified base
     *
     * @pure
     */
    public function toBase(IntegerObject $value, int $base): string;

    /**
     * Converts an Integer instance to a Hexadecimal instance
     *
     * @pure
     */
    public function toHexadecimal(IntegerObject $value): Hexadecimal;

    /**
     * Converts a Hexadecimal instance to an Integer instance
     *
     * @pure
     */
    public function toInteger(Hexadecimal $value): IntegerObject;
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Math;

use Brick\Math\RoundingMode as BrickMathRounding;

/**
 * @internal Polyfill for Brick\Math\RoundingMode constant naming
 * changes introduced in brick/math 0.15 (UPPER_SNAKE_CASE â†’ PascalCase)
 */
final class BrickMathRoundingMode
{
    /**
     * Maps ramsey/uuid rounding mode constants to their PascalCase (>= 0.15)
     * and UPPER_SNAKE_CASE (< 0.15) equivalents in brick/math
     */
    private const ROUNDING_MODE_MAP = [
        RoundingMode::UNNECESSARY => ['Unnecessary', 'UNNECESSARY'],
        RoundingMode::UP => ['Up', 'UP'],
        RoundingMode::DOWN => ['Down', 'DOWN'],
        RoundingMode::CEILING => ['Ceiling', 'CEILING'],
        RoundingMode::FLOOR => ['Floor', 'FLOOR'],
        RoundingMode::HALF_UP => ['HalfUp', 'HALF_UP'],
        RoundingMode::HALF_DOWN => ['HalfDown', 'HALF_DOWN'],
        RoundingMode::HALF_CEILING => ['HalfCeiling', 'HALF_CEILING'],
        RoundingMode::HALF_FLOOR => ['HalfFloor', 'HALF_FLOOR'],
        RoundingMode::HALF_EVEN => ['HalfEven', 'HALF_EVEN'],
    ];

    /**
     * Resolves a ramsey/uuid rounding mode to the correct
     * Brick\Math\RoundingMode value for the installed version.
     *
     * @return BrickMathRounding::*
     */
    public static function resolve(int $roundingMode)
    {
        [$pascal, $snake] = self::ROUNDING_MODE_MAP[$roundingMode]
            ?? self::ROUNDING_MODE_MAP[RoundingMode::UNNECESSARY];

        $class = BrickMathRounding::class;

        /** @var BrickMathRounding::* */
        return defined("$class::$pascal")
            ? constant("$class::$pascal")
            : constant("$class::$snake");
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid;

use DateTimeInterface;
use Ramsey\Uuid\Type\Hexadecimal;
use Ramsey\Uuid\Type\Integer as IntegerObject;
use Ramsey\Uuid\Validator\ValidatorInterface;

/**
 * UuidFactoryInterface defines the common functionality all `UuidFactory` instances must implement
 */
interface UuidFactoryInterface
{
    /**
     * Creates a UUID from a byte string
     *
     * @param string $bytes A binary string
     *
     * @return UuidInterface A UuidInterface instance created from a binary string representation
     *
     * @pure
     */
    public function fromBytes(string $bytes): UuidInterface;

    /**
     * Creates a UUID from a DateTimeInterface instance
     *
     * @param DateTimeInterface $dateTime The date and time
     * @param Hexadecimal | null $node A 48-bit number representing the hardware address
     * @param int | null $clockSeq A 14-bit number used to help avoid duplicates that could arise when the clock is set
     *     backwards in time or if the node ID changes
     *
     * @return UuidInterface A UuidInterface instance that represents a version 1 UUID created from a DateTimeInterface instance
     */
    public function fromDateTime(
        DateTimeInterface $dateTime,
        ?Hexadecimal $node = null,
        ?int $clockSeq = null,
    ): UuidInterface;

    /**
     * Creates a UUID from a 128-bit integer string
     *
     * @param string $integer String representation of 128-bit integer
     *
     * @return UuidInterface A UuidInterface instance created from the string representation of a 128-bit integer
     *
     * @pure
     */
    public function fromInteger(string $integer): UuidInterface;

    /**
     * Creates a UUID from the string standard representation
     *
     * @param string $uuid A hexadecimal string
     *
     * @return UuidInterface A UuidInterface instance created from a hexadecimal string representation
     *
     * @pure
     */
    public function fromString(string $uuid): UuidInterface;

    /**
     * Returns the validator used by the factory
     */
    public function getValidator(): ValidatorInterface;

    /**
     * Returns a version 1 (Gregorian time) UUID from a host ID, sequence number, and the current time
     *
     * @param Hexadecimal | int | string | null $node A 48-bit number representing the hardware address; this number may
     *     be represented as an integer or a hexadecimal string
     * @param int | null $clockSeq A 14-bit number used to help avoid duplicates that could arise when the clock is set
     *     backwards in time or if the node ID changes
     *
     * @return UuidInterface A UuidInterface instance that represents a version 1 UUID
     */
    public function uuid1($node = null, ?int $clockSeq = null): UuidInterface;

    /**
     * Returns a version 2 (DCE Security) UUID from a local domain, local identifier, host ID, clock sequence, and the
     * current time
     *
     * @param int $localDomain The local domain to use when generating bytes, according to DCE Security
     * @param IntegerObject | null $localIdentifier The local identifier for the given domain; this may be a UID or GID
     *     on POSIX systems, if the local domain is a person or group, or it may be a site-defined identifier if the
     *     local domain is org
     * @param Hexadecimal | null $node A 48-bit number representing the hardware address
     * @param int | null $clockSeq A 14-bit number used to help avoid duplicates that could arise when the clock is set
     *     backwards in time or if the node ID changes
     *
     * @return UuidInterface A UuidInterface instance that represents a version 2 UUID
     */
    public function uuid2(
        int $localDomain,
        ?IntegerObject $localIdentifier = null,
        ?Hexadecimal $node = null,
        ?int $clockSeq = null,
    ): UuidInterface;

    /**
     * Returns a version 3 (name-based) UUID based on the MD5 hash of a namespace ID and a name
     *
     * @param UuidInterface | string $ns The namespace (must be a valid UUID)
     * @param string $name The name to use for creating a UUID
     *
     * @return UuidInterface A UuidInterface instance that represents a version 3 UUID
     *
     * @pure
     */
    public function uuid3($ns, string $name): UuidInterface;

    /**
     * Returns a version 4 (random) UUID
     *
     * @return UuidInterface A UuidInterface instance that represents a version 4 UUID
     */
    public function uuid4(): UuidInterface;

    /**
     * Returns a version 5 (name-based) UUID based on the SHA-1 hash of a namespace ID and a name
     *
     * @param UuidInterface | string $ns The namespace (must be a valid UUID)
     * @param string $name The name to use for creating a UUID
     *
     * @return UuidInterface A UuidInterface instance that represents a version 5 UUID
     *
     * @pure
     */
    public function uuid5($ns, string $name): UuidInterface;

    /**
     * Returns a version 6 (reordered Gregorian time) UUID from a host ID, sequence number, and the current time
     *
     * @param Hexadecimal | null $node A 48-bit number representing the hardware address
     * @param int | null $clockSeq A 14-bit number used to help avoid duplicates that could arise when the clock is set
     *     backwards in time or if the node ID changes
     *
     * @return UuidInterface A UuidInterface instance that represents a version 6 UUID
     */
    public function uuid6(?Hexadecimal $node = null, ?int $clockSeq = null): UuidInterface;
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Generator;

use Ramsey\Uuid\Converter\NumberConverterInterface;
use Ramsey\Uuid\Exception\DceSecurityException;
use Ramsey\Uuid\Provider\DceSecurityProviderInterface;
use Ramsey\Uuid\Type\Hexadecimal;
use Ramsey\Uuid\Type\Integer as IntegerObject;
use Ramsey\Uuid\Uuid;

use function hex2bin;
use function in_array;
use function pack;
use function str_pad;
use function strlen;
use function substr_replace;

use const STR_PAD_LEFT;

/**
 * DceSecurityGenerator generates strings of binary data based on a local domain, local identifier, node ID, clock
 * sequence, and the current time
 */
class DceSecurityGenerator implements DceSecurityGeneratorInterface
{
    private const DOMAINS = [
        Uuid::DCE_DOMAIN_PERSON,
        Uuid::DCE_DOMAIN_GROUP,
        Uuid::DCE_DOMAIN_ORG,
    ];

    /**
     * Upper bounds for the clock sequence in DCE Security UUIDs.
     */
    private const CLOCK_SEQ_HIGH = 63;

    /**
     * Lower bounds for the clock sequence in DCE Security UUIDs.
     */
    private const CLOCK_SEQ_LOW = 0;

    public function __construct(
        private NumberConverterInterface $numberConverter,
        private TimeGeneratorInterface $timeGenerator,
        private DceSecurityProviderInterface $dceSecurityProvider,
    ) {
    }

    public function generate(
        int $localDomain,
        ?IntegerObject $localIdentifier = null,
        ?Hexadecimal $node = null,
        ?int $clockSeq = null,
    ): string {
        if (!in_array($localDomain, self::DOMAINS)) {
            throw new DceSecurityException('Local domain must be a valid DCE Security domain');
        }

        if ($localIdentifier && $localIdentifier->isNegative()) {
            throw new DceSecurityException(
                'Local identifier out of bounds; it must be a value between 0 and 4294967295',
            );
        }

        if ($clockSeq > self::CLOCK_SEQ_HIGH || $clockSeq < self::CLOCK_SEQ_LOW) {
            throw new DceSecurityException('Clock sequence out of bounds; it must be a value between 0 and 63');
        }

        switch ($localDomain) {
            case Uuid::DCE_DOMAIN_ORG:
                if ($localIdentifier === null) {
                    throw new DceSecurityException('A local identifier must be provided for the org domain');
                }

                break;
            case Uuid::DCE_DOMAIN_PERSON:
                if ($localIdentifier === null) {
                    $localIdentifier = $this->dceSecurityProvider->getUid();
                }

                break;
            case Uuid::DCE_DOMAIN_GROUP:
            default:
                if ($localIdentifier === null) {
                    $localIdentifier = $this->dceSecurityProvider->getGid();
                }

                break;
        }

        $identifierHex = $this->numberConverter->toHex($localIdentifier->toString());

        // The maximum value for the local identifier is 0xffffffff, or 4,294,967,295. This is 8 hexadecimal digits, so
        // if the length of hexadecimal digits is greater than 8, we know the value is greater than 0xffffffff.
        if (strlen($identifierHex) > 8) {
            throw new DceSecurityException(
                'Local identifier out of bounds; it must be a value between 0 and 4294967295',
            );
        }

        $domainByte = pack('n', $localDomain)[1];
        $identifierBytes = (string) hex2bin(str_pad($identifierHex, 8, '0', STR_PAD_LEFT));

        if ($node instanceof Hexadecimal) {
            $node = $node->toString();
        }

        // Shift the clock sequence 8 bits to the left, so it matches 0x3f00.
        if ($clockSeq !== null) {
            $clockSeq = $clockSeq << 8;
        }

        $bytes = $this->timeGenerator->generate($node, $clockSeq);

        // Replace bytes in the time-based UUID with DCE Security values.
        $bytes = substr_replace($bytes, $identifierBytes, 0, 4);

        return substr_replace($bytes, $domainByte, 9, 1);
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Generator;

use function uuid_create;
use function uuid_parse;

use const UUID_TYPE_TIME;

/**
 * PeclUuidTimeGenerator generates strings of binary data for time-base UUIDs, using ext-uuid
 *
 * @link https://pecl.php.net/package/uuid ext-uuid
 */
class PeclUuidTimeGenerator implements TimeGeneratorInterface
{
    /**
     * @inheritDoc
     */
    public function generate($node = null, ?int $clockSeq = null): string
    {
        $uuid = uuid_create(UUID_TYPE_TIME);

        return (string) uuid_parse($uuid);
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Generator;

use Brick\Math\BigInteger;
use DateTimeInterface;
use Ramsey\Uuid\Type\Hexadecimal;

use function assert;
use function hash;
use function pack;
use function str_pad;
use function strlen;
use function substr;
use function substr_replace;
use function unpack;

use const PHP_INT_SIZE;
use const STR_PAD_LEFT;

/**
 * UnixTimeGenerator generates bytes, combining a 48-bit timestamp in milliseconds since the Unix Epoch with 80 random bits
 *
 * Code and concepts within this class are borrowed from the symfony/uid package and are used under the terms of the MIT
 * license distributed with symfony/uid.
 *
 * symfony/uid is copyright (c) Fabien Potencier.
 *
 * @link https://symfony.com/components/Uid Symfony Uid component
 * @link https://github.com/symfony/uid/blob/4f9f537e57261519808a7ce1d941490736522bbc/UuidV7.php Symfony UuidV7 class
 * @link https://github.com/symfony/uid/blob/6.2/LICENSE MIT License
 */
class UnixTimeGenerator implements TimeGeneratorInterface
{
    private static string $time = '';
    private static ?string $seed = null;
    private static int $seedIndex = 0;

    /** @var int[] */
    private static array $rand = [];

    /** @var int[] */
    private static array $seedParts;

    public function __construct(
        private RandomGeneratorInterface $randomGenerator,
        private int $intSize = PHP_INT_SIZE,
    ) {
    }

    /**
     * @param Hexadecimal | int | string | null $node Unused in this generator
     * @param int | null $clockSeq Unused in this generator
     * @param DateTimeInterface | null $dateTime A date-time instance to use when generating bytes
     */
    public function generate($node = null, ?int $clockSeq = null, ?DateTimeInterface $dateTime = null): string
    {
        if ($dateTime === null) {
            $time = microtime(false);
            $time = substr($time, 11) . substr($time, 2, 3);
        } else {
            $time = $dateTime->format('Uv');
        }

        if ($time > self::$time || ($dateTime !== null && $time !== self::$time)) {
            $this->randomize($time);
        } else {
            $time = $this->increment();
        }

        if ($this->intSize >= 8) {
            $time = substr(pack('J', (int) $time), -6);
        } else {
            $time = str_pad(BigInteger::of($time)->toBytes(false), 6, "\x00", STR_PAD_LEFT);
        }

        assert(strlen($time) === 6);

        return $time . pack('n*', self::$rand[1], self::$rand[2], self::$rand[3], self::$rand[4], self::$rand[5]);
    }

    private function randomize(string $time): void
    {
        if (self::$seed === null) {
            $seed = $this->randomGenerator->generate(16);
            self::$seed = $seed;
        } else {
            $seed = $this->randomGenerator->generate(10);
        }

        /** @var int[] $rand */
        $rand = unpack('n*', $seed);
        $rand[1] &= 0x03ff;

        self::$rand = $rand;
        self::$time = $time;
    }

    /**
     * Special thanks to Nicolas Grekas (<https://github.com/nicolas-grekas>) for sharing the following information:
     *
     * Within the same ms, we increment the rand part by a random 24-bit number.
     *
     * Instead of getting this number from random_bytes(), which is slow, we get it by sha512-hashing self::$seed. This
     * produces 64 bytes of entropy, which we need to split in a list of 24-bit numbers. `unpack()` first splits them
     * into 16 x 32-bit numbers; we take the first byte of each number to get 5 extra 24-bit numbers. Then, we consume
     * each number one-by-one and run this logic every 21 iterations.
     *
     * `self::$rand` holds the random part of the UUID, split into 5 x 16-bit numbers for x86 portability. We increment
     * this random part by the next 24-bit number in the `self::$seedParts` list and decrement `self::$seedIndex`.
     */
    private function increment(): string
    {
        if (self::$seedIndex === 0 && self::$seed !== null) {
            self::$seed = hash('sha512', self::$seed, true);

            /** @var int[] $s */
            $s = unpack('l*', self::$seed);
            $s[] = ($s[1] >> 8 & 0xff0000) | ($s[2] >> 16 & 0xff00) | ($s[3] >> 24 & 0xff);
            $s[] = ($s[4] >> 8 & 0xff0000) | ($s[5] >> 16 & 0xff00) | ($s[6] >> 24 & 0xff);
            $s[] = ($s[7] >> 8 & 0xff0000) | ($s[8] >> 16 & 0xff00) | ($s[9] >> 24 & 0xff);
            $s[] = ($s[10] >> 8 & 0xff0000) | ($s[11] >> 16 & 0xff00) | ($s[12] >> 24 & 0xff);
            $s[] = ($s[13] >> 8 & 0xff0000) | ($s[14] >> 16 & 0xff00) | ($s[15] >> 24 & 0xff);

            self::$seedParts = $s;
            self::$seedIndex = 21;
        }

        self::$rand[5] = 0xffff & $carry = self::$rand[5] + 1 + (self::$seedParts[self::$seedIndex--] & 0xffffff);
        self::$rand[4] = 0xffff & $carry = self::$rand[4] + ($carry >> 16);
        self::$rand[3] = 0xffff & $carry = self::$rand[3] + ($carry >> 16);
        self::$rand[2] = 0xffff & $carry = self::$rand[2] + ($carry >> 16);
        self::$rand[1] += $carry >> 16;

        if (0xfc00 & self::$rand[1]) {
            $time = self::$time;
            $mtime = (int) substr($time, -9);

            if ($this->intSize >= 8 || strlen($time) < 10) {
                $time = (string) ((int) $time + 1);
            } elseif ($mtime === 999999999) {
                $time = (1 + (int) substr($time, 0, -9)) . '000000000';
            } else {
                $mtime++;
                $time = substr_replace($time, str_pad((string) $mtime, 9, '0', STR_PAD_LEFT), -9);
            }

            $this->randomize($time);
        }

        return self::$time;
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Generator;

use Ramsey\Uuid\Type\Hexadecimal;

/**
 * A time generator generates strings of binary data based on a node ID, clock sequence, and the current time
 */
interface TimeGeneratorInterface
{
    /**
     * Generate a binary string from a node ID, clock sequence, and current time
     *
     * @param Hexadecimal | int | string | null $node A 48-bit number representing the hardware address; this number may
     *     be represented as an integer or a hexadecimal string
     * @param int | null $clockSeq A 14-bit number used to help avoid duplicates that could arise when the clock is set
     *     backwards in time or if the node ID changes
     *
     * @return string A binary string
     */
    public function generate($node = null, ?int $clockSeq = null): string;
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Generator;

use Ramsey\Uuid\Exception\NameException;
use Ramsey\Uuid\UuidInterface;

use function sprintf;
use function uuid_generate_md5;
use function uuid_generate_sha1;
use function uuid_parse;

/**
 * PeclUuidNameGenerator generates strings of binary data from a namespace and a name, using ext-uuid
 *
 * @link https://pecl.php.net/package/uuid ext-uuid
 */
class PeclUuidNameGenerator implements NameGeneratorInterface
{
    /**
     * @pure
     */
    public function generate(UuidInterface $ns, string $name, string $hashAlgorithm): string
    {
        $uuid = match ($hashAlgorithm) {
            'md5' => uuid_generate_md5($ns->toString(), $name), /** @phpstan-ignore possiblyImpure.functionCall */
            'sha1' => uuid_generate_sha1($ns->toString(), $name), /** @phpstan-ignore possiblyImpure.functionCall */
            default => throw new NameException(
                sprintf('Unable to hash namespace and name with algorithm \'%s\'', $hashAlgorithm),
            ),
        };

        /** @phpstan-ignore possiblyImpure.functionCall */
        return (string) uuid_parse($uuid);
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Generator;

use Ramsey\Uuid\Exception\NameException;
use Ramsey\Uuid\UuidInterface;
use ValueError;

use function hash;

/**
 * DefaultNameGenerator generates strings of binary data based on a namespace, name, and hashing algorithm
 */
class DefaultNameGenerator implements NameGeneratorInterface
{
    /**
     * @pure
     */
    public function generate(UuidInterface $ns, string $name, string $hashAlgorithm): string
    {
        try {
            return hash($hashAlgorithm, $ns->getBytes() . $name, true);
        } catch (ValueError $e) {
            throw new NameException(
                message: sprintf('Unable to hash namespace and name with algorithm \'%s\'', $hashAlgorithm),
                previous: $e,
            );
        }
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Generator;

/**
 * RandomGeneratorFactory retrieves a default random generator, based on the environment
 */
class RandomGeneratorFactory
{
    /**
     * Returns a default random generator, based on the current environment
     */
    public function getGenerator(): RandomGeneratorInterface
    {
        return new RandomBytesGenerator();
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Generator;

use Ramsey\Uuid\Converter\TimeConverterInterface;
use Ramsey\Uuid\Exception\InvalidArgumentException;
use Ramsey\Uuid\Exception\RandomSourceException;
use Ramsey\Uuid\Exception\TimeSourceException;
use Ramsey\Uuid\Provider\NodeProviderInterface;
use Ramsey\Uuid\Provider\TimeProviderInterface;
use Ramsey\Uuid\Type\Hexadecimal;
use Throwable;

use function dechex;
use function hex2bin;
use function is_int;
use function pack;
use function preg_match;
use function sprintf;
use function str_pad;
use function strlen;

use const STR_PAD_LEFT;

/**
 * DefaultTimeGenerator generates strings of binary data based on a node ID, clock sequence, and the current time
 */
class DefaultTimeGenerator implements TimeGeneratorInterface
{
    public function __construct(
        private NodeProviderInterface $nodeProvider,
        private TimeConverterInterface $timeConverter,
        private TimeProviderInterface $timeProvider,
    ) {
    }

    /**
     * @throws InvalidArgumentException if the parameters contain invalid values
     * @throws RandomSourceException if random_int() throws an exception/error
     *
     * @inheritDoc
     */
    public function generate($node = null, ?int $clockSeq = null): string
    {
        if ($node instanceof Hexadecimal) {
            $node = $node->toString();
        }

        $node = $this->getValidNode($node);

        if ($clockSeq === null) {
            try {
                // This does not use "stable storage"; see RFC 9562, section 6.3.
                $clockSeq = random_int(0, 0x3fff);
            } catch (Throwable $exception) {
                throw new RandomSourceException($exception->getMessage(), (int) $exception->getCode(), $exception);
            }
        }

        $time = $this->timeProvider->getTime();

        $uuidTime = $this->timeConverter->calculateTime(
            $time->getSeconds()->toString(),
            $time->getMicroseconds()->toString()
        );

        $timeHex = str_pad($uuidTime->toString(), 16, '0', STR_PAD_LEFT);

        if (strlen($timeHex) !== 16) {
            throw new TimeSourceException(sprintf('The generated time of \'%s\' is larger than expected', $timeHex));
        }

        $timeBytes = (string) hex2bin($timeHex);

        return $timeBytes[4] . $timeBytes[5] . $timeBytes[6] . $timeBytes[7]
            . $timeBytes[2] . $timeBytes[3] . $timeBytes[0] . $timeBytes[1]
            . pack('n*', $clockSeq) . $node;
    }

    /**
     * Uses the node provider given when constructing this instance to get the node ID (usually a MAC address)
     *
     * @param int | string | null $node A node value that may be used to override the node provider
     *
     * @return string 6-byte binary string representation of the node
     *
     * @throws InvalidArgumentException
     */
    private function getValidNode(int | string | null $node): string
    {
        if ($node === null) {
            $node = $this->nodeProvider->getNode();
        }

        // Convert the node to hex if it is still an integer.
        if (is_int($node)) {
            $node = dechex($node);
        }

        if (!preg_match('/^[A-Fa-f0-9]+$/', (string) $node) || strlen((string) $node) > 12) {
            throw new InvalidArgumentException('Invalid node value');
        }

        return (string) hex2bin(str_pad((string) $node, 12, '0', STR_PAD_LEFT));
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Generator;

use Ramsey\Uuid\Converter\TimeConverterInterface;
use Ramsey\Uuid\Provider\NodeProviderInterface;
use Ramsey\Uuid\Provider\TimeProviderInterface;

/**
 * TimeGeneratorFactory retrieves a default time generator, based on the environment
 */
class TimeGeneratorFactory
{
    public function __construct(
        private NodeProviderInterface $nodeProvider,
        private TimeConverterInterface $timeConverter,
        private TimeProviderInterface $timeProvider,
    ) {
    }

    /**
     * Returns a default time generator, based on the current environment
     */
    public function getGenerator(): TimeGeneratorInterface
    {
        return new DefaultTimeGenerator($this->nodeProvider, $this->timeConverter, $this->timeProvider);
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Generator;

/**
 * NameGeneratorFactory retrieves a default name generator, based on the environment
 */
class NameGeneratorFactory
{
    /**
     * Returns a default name generator, based on the current environment
     */
    public function getGenerator(): NameGeneratorInterface
    {
        return new DefaultNameGenerator();
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Generator;

use Ramsey\Uuid\Converter\NumberConverterInterface;
use Ramsey\Uuid\Exception\InvalidArgumentException;

use function bin2hex;
use function explode;
use function hex2bin;
use function microtime;
use function str_pad;
use function substr;

use const STR_PAD_LEFT;

/**
 * CombGenerator generates COMBs (combined UUID/timestamp)
 *
 * The CombGenerator, when used with the StringCodec (and, by proxy, the TimestampLastCombCodec) or the
 * TimestampFirstCombCodec, combines the current timestamp with a UUID (hence the name "COMB"). The timestamp either
 * appears as the first or last 48 bits of the COMB, depending on the codec used.
 *
 * By default, COMBs will have the timestamp set as the last 48 bits of the identifier.
 *
 * ```
 * $factory = new UuidFactory();
 *
 * $factory->setRandomGenerator(new CombGenerator(
 *     $factory->getRandomGenerator(),
 *     $factory->getNumberConverter(),
 * ));
 *
 * $comb = $factory->uuid4();
 * ```
 *
 * To generate a COMB with the timestamp as the first 48 bits, set the TimestampFirstCombCodec as the codec.
 *
 * ```
 * $factory->setCodec(new TimestampFirstCombCodec($factory->getUuidBuilder()));
 * ```
 *
 * @deprecated Please migrate to {@link https://uuid.ramsey.dev/en/stable/rfc4122/version7.html Version 7, Unix Epoch Time UUIDs}.
 *
 * @link https://web.archive.org/web/20240118030355/https://www.informit.com/articles/printerfriendly/25862 The Cost of GUIDs as Primary Keys
 */
class CombGenerator implements RandomGeneratorInterface
{
    public const TIMESTAMP_BYTES = 6;

    public function __construct(
        private RandomGeneratorInterface $generator,
        private NumberConverterInterface $numberConverter
    ) {
    }

    /**
     * @throws InvalidArgumentException if $length is not a positive integer greater than or equal to CombGenerator::TIMESTAMP_BYTES
     *
     * @inheritDoc
     */
    public function generate(int $length): string
    {
        if ($length < self::TIMESTAMP_BYTES) {
            throw new InvalidArgumentException(
                'Length must be a positive integer greater than or equal to ' . self::TIMESTAMP_BYTES
            );
        }

        if ($length % 2 !== 0) {
            throw new InvalidArgumentException('Length must be an even number');
        }

        $hash = '';

        /** @phpstan-ignore greater.alwaysTrue (TIMESTAMP_BYTES constant could change in child classes) */
        if (self::TIMESTAMP_BYTES > 0 && $length > self::TIMESTAMP_BYTES) {
            $hash = $this->generator->generate($length - self::TIMESTAMP_BYTES);
        }

        $lsbTime = str_pad(
            $this->numberConverter->toHex($this->timestamp()),
            self::TIMESTAMP_BYTES * 2,
            '0',
            STR_PAD_LEFT,
        );

        return (string) hex2bin(str_pad(bin2hex($hash), $length - self::TIMESTAMP_BYTES, '0') . $lsbTime);
    }

    /**
     * Returns the current timestamp as a string integer, precise to 0.00001 seconds
     */
    private function timestamp(): string
    {
        $time = explode(' ', microtime(false));

        return $time[1] . substr($time[0], 2, 5);
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Generator;

use function uuid_create;
use function uuid_parse;

use const UUID_TYPE_RANDOM;

/**
 * PeclUuidRandomGenerator generates strings of random binary data using ext-uuid
 *
 * @link https://pecl.php.net/package/uuid ext-uuid
 */
class PeclUuidRandomGenerator implements RandomGeneratorInterface
{
    public function generate(int $length): string
    {
        $uuid = uuid_create(UUID_TYPE_RANDOM);

        return (string) uuid_parse($uuid);
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Generator;

use Ramsey\Uuid\Rfc4122\UuidV2;
use Ramsey\Uuid\Type\Hexadecimal;
use Ramsey\Uuid\Type\Integer as IntegerObject;

/**
 * A DCE Security generator generates strings of binary data based on a local domain, local identifier, node ID, clock
 * sequence, and the current time
 *
 * @see UuidV2
 */
interface DceSecurityGeneratorInterface
{
    /**
     * Generate a binary string from a local domain, local identifier, node ID, clock sequence, and current time
     *
     * @param int $localDomain The local domain to use when generating bytes, according to DCE Security
     * @param IntegerObject | null $localIdentifier The local identifier for the given domain; this may be a UID or GID
     *     on POSIX systems if the local domain is "person" or "group," or it may be a site-defined identifier if the
     *     local domain is "org"
     * @param Hexadecimal | null $node A 48-bit number representing the hardware address
     * @param int | null $clockSeq A 14-bit number used to help avoid duplicates that could arise when the clock is set
     *     backwards in time or if the node ID changes
     *
     * @return string A binary string
     */
    public function generate(
        int $localDomain,
        ?IntegerObject $localIdentifier = null,
        ?Hexadecimal $node = null,
        ?int $clockSeq = null,
    ): string;
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Generator;

use Ramsey\Uuid\UuidInterface;

/**
 * A name generator generates strings of binary data created by hashing together a namespace with a name, according to a
 * hashing algorithm
 */
interface NameGeneratorInterface
{
    /**
     * Generate a binary string from a namespace and name hashed together with the specified hashing algorithm
     *
     * @param UuidInterface $ns The namespace
     * @param string $name The name to use for creating a UUID
     * @param string $hashAlgorithm The hashing algorithm to use
     *
     * @return string A binary string
     *
     * @pure
     */
    public function generate(UuidInterface $ns, string $name, string $hashAlgorithm): string;
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Generator;

/**
 * A random generator generates strings of random binary data
 */
interface RandomGeneratorInterface
{
    /**
     * Generates a string of randomized binary data
     *
     * @param int<1, max> $length The number of bytes to generate of random binary data
     *
     * @return string A binary string
     */
    public function generate(int $length): string;
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Generator;

use RandomLib\Factory;
use RandomLib\Generator;

/**
 * RandomLibAdapter generates strings of random binary data using the paragonie/random-lib library
 *
 * @deprecated This class will be removed in 5.0.0. Use the default RandomBytesGenerator or implement your own generator
 *     that implements RandomGeneratorInterface.
 *
 * @link https://packagist.org/packages/paragonie/random-lib paragonie/random-lib
 */
class RandomLibAdapter implements RandomGeneratorInterface
{
    private Generator $generator;

    /**
     * Constructs a RandomLibAdapter
     *
     * By default, if no Generator is passed in, this creates a high-strength generator to use when generating random
     * binary data.
     *
     * @param Generator | null $generator The generator to use when generating binary data
     */
    public function __construct(?Generator $generator = null)
    {
        if ($generator === null) {
            $factory = new Factory();
            $generator = $factory->getHighStrengthGenerator();
        }

        $this->generator = $generator;
    }

    public function generate(int $length): string
    {
        return $this->generator->generate($length);
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Generator;

use Ramsey\Uuid\Exception\RandomSourceException;
use Throwable;

/**
 * RandomBytesGenerator generates strings of random binary data using the built-in `random_bytes()` PHP function
 *
 * @link http://php.net/random_bytes random_bytes()
 */
class RandomBytesGenerator implements RandomGeneratorInterface
{
    /**
     * @throws RandomSourceException if random_bytes() throws an exception/error
     *
     * @inheritDoc
     */
    public function generate(int $length): string
    {
        try {
            return random_bytes($length);
        } catch (Throwable $exception) {
            throw new RandomSourceException($exception->getMessage(), (int) $exception->getCode(), $exception);
        }
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid;

/**
 * @deprecated DegradedUuid is no longer necessary to represent UUIDs on 32-bit systems.
 *     Transition any type declarations using this class to {@see UuidInterface}.
 *
 * @immutable
 */
class DegradedUuid extends Uuid
{
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid;

use DateTimeInterface;
use Ramsey\Uuid\Converter\NumberConverterInterface;

/**
 * This interface encapsulates deprecated methods for ramsey/uuid
 *
 * @immutable
 */
interface DeprecatedUuidInterface
{
    /**
     * @deprecated This method will be removed in 5.0.0. There is no alternative recommendation, so plan accordingly.
     */
    public function getNumberConverter(): NumberConverterInterface;

    /**
     * @deprecated Use {@see UuidInterface::getFields()} to get a {@see FieldsInterface} instance.
     *
     * @return string[]
     */
    public function getFieldsHex(): array;

    /**
     * @deprecated Use {@see UuidInterface::getFields()} to get a {@see FieldsInterface} instance. If it is a
     *     {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call
     *     {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getClockSeqHiAndReserved()}.
     */
    public function getClockSeqHiAndReservedHex(): string;

    /**
     * @deprecated Use {@see UuidInterface::getFields()} to get a {@see FieldsInterface} instance. If it is a
     *     {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call
     *     {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getClockSeqLow()}.
     */
    public function getClockSeqLowHex(): string;

    /**
     * @deprecated Use {@see UuidInterface::getFields()} to get a {@see FieldsInterface} instance. If it is a
     *     {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call
     *     {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getClockSeq()}.
     */
    public function getClockSequenceHex(): string;

    /**
     * @deprecated In ramsey/uuid version 5.0.0, this will be removed from the interface. It is available at
     *     {@see UuidV1::getDateTime()}.
     */
    public function getDateTime(): DateTimeInterface;

    /**
     * @deprecated This method will be removed in 5.0.0. There is no direct alternative, but the same information may be
     *     obtained by splitting in half the value returned by {@see UuidInterface::getHex()}.
     */
    public function getLeastSignificantBitsHex(): string;

    /**
     * @deprecated This method will be removed in 5.0.0. There is no direct alternative, but the same information may be
     *     obtained by splitting in half the value returned by {@see UuidInterface::getHex()}.
     */
    public function getMostSignificantBitsHex(): string;

    /**
     * @deprecated Use {@see UuidInterface::getFields()} to get a {@see FieldsInterface} instance. If it is a
     *     {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call
     *     {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getNode()}.
     */
    public function getNodeHex(): string;

    /**
     * @deprecated Use {@see UuidInterface::getFields()} to get a {@see FieldsInterface} instance. If it is a
     *     {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call
     *     {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getTimeHiAndVersion()}.
     */
    public function getTimeHiAndVersionHex(): string;

    /**
     * @deprecated Use {@see UuidInterface::getFields()} to get a {@see FieldsInterface} instance. If it is a
     *     {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call
     *     {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getTimeLow()}.
     */
    public function getTimeLowHex(): string;

    /**
     * @deprecated Use {@see UuidInterface::getFields()} to get a {@see FieldsInterface} instance. If it is a
     *     {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call
     *     {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getTimeMid()}.
     */
    public function getTimeMidHex(): string;

    /**
     * @deprecated Use {@see UuidInterface::getFields()} to get a {@see FieldsInterface} instance. If it is a
     *     {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call
     *     {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getTimestamp()}.
     */
    public function getTimestampHex(): string;

    /**
     * @deprecated Use {@see UuidInterface::getFields()} to get a {@see FieldsInterface} instance. If it is a
     *     {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call
     *     {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getVariant()}.
     */
    public function getVariant(): ?int;

    /**
     * @deprecated Use {@see UuidInterface::getFields()} to get a {@see FieldsInterface} instance. If it is a
     *     {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call
     *     {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getVersion()}.
     */
    public function getVersion(): ?int;
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid;

/**
 * Provides binary math utilities
 */
class BinaryUtils
{
    /**
     * Applies the variant field to the 16-bit clock sequence
     *
     * @link https://www.rfc-editor.org/rfc/rfc9562#section-4.1 RFC 9562, 4.1. Variant Field
     *
     * @param int $clockSeq The 16-bit clock sequence value before the variant is applied
     *
     * @return int The 16-bit clock sequence multiplexed with the UUID variant
     *
     * @pure
     */
    public static function applyVariant(int $clockSeq): int
    {
        return ($clockSeq & 0x3fff) | 0x8000;
    }

    /**
     * Applies the version field to the 16-bit `time_hi_and_version` field
     *
     * @link https://www.rfc-editor.org/rfc/rfc9562#section-4.2 RFC 9562, 4.2. Version Field
     *
     * @param int $timeHi The value of the 16-bit `time_hi_and_version` field before the version is applied
     * @param int $version The version to apply to the `time_hi` field
     *
     * @return int The 16-bit time_hi field of the timestamp multiplexed with the UUID version number
     *
     * @pure
     */
    public static function applyVersion(int $timeHi, int $version): int
    {
        return ($timeHi & 0x0fff) | ($version << 12);
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 * phpcs:disable Squiz.Functions.GlobalFunction
 */

declare(strict_types=1);

namespace Ramsey\Uuid;

use DateTimeInterface;
use Ramsey\Uuid\Type\Hexadecimal;
use Ramsey\Uuid\Type\Integer as IntegerObject;

/**
 * Returns a version 1 (Gregorian time) UUID from a host ID, sequence number, and the current time
 *
 * @param Hexadecimal | int | string | null $node A 48-bit number representing the hardware address; this number may be
 *     represented as an integer or a hexadecimal string
 * @param int | null $clockSeq A 14-bit number used to help avoid duplicates that could arise when the clock is set
 *     backwards in time or if the node ID changes
 *
 * @return non-empty-string Version 1 UUID as a string
 */
function v1($node = null, ?int $clockSeq = null): string
{
    return Uuid::uuid1($node, $clockSeq)->toString();
}

/**
 * Returns a version 2 (DCE Security) UUID from a local domain, local identifier, host ID, clock sequence, and the current time
 *
 * @param int $localDomain The local domain to use when generating bytes, according to DCE Security
 * @param IntegerObject | null $localIdentifier The local identifier for the given domain; this may be a UID or GID on
 *     POSIX systems, if the local domain is a person or group, or it may be a site-defined identifier if the local
 *     domain is org
 * @param Hexadecimal | null $node A 48-bit number representing the hardware address
 * @param int | null $clockSeq A 14-bit number used to help avoid duplicates that could arise when the clock is set
 *     backwards in time or if the node ID changes
 *
 * @return non-empty-string Version 2 UUID as a string
 */
function v2(
    int $localDomain,
    ?IntegerObject $localIdentifier = null,
    ?Hexadecimal $node = null,
    ?int $clockSeq = null,
): string {
    return Uuid::uuid2($localDomain, $localIdentifier, $node, $clockSeq)->toString();
}

/**
 * Returns a version 3 (name-based) UUID based on the MD5 hash of a namespace ID and a name
 *
 * @param UuidInterface | string $ns The namespace (must be a valid UUID)
 *
 * @return non-empty-string Version 3 UUID as a string
 *
 * @pure
 */
function v3($ns, string $name): string
{
    return Uuid::uuid3($ns, $name)->toString();
}

/**
 * Returns a version 4 (random) UUID
 *
 * @return non-empty-string Version 4 UUID as a string
 */
function v4(): string
{
    return Uuid::uuid4()->toString();
}

/**
 * Returns a version 5 (name-based) UUID based on the SHA-1 hash of a namespace ID and a name
 *
 * @param UuidInterface | string $ns The namespace (must be a valid UUID)
 *
 * @return non-empty-string Version 5 UUID as a string
 *
 * @pure
 */
function v5($ns, string $name): string
{
    return Uuid::uuid5($ns, $name)->toString();
}

/**
 * Returns a version 6 (reordered Gregorian time) UUID from a host ID, sequence number, and the current time
 *
 * @param Hexadecimal | null $node A 48-bit number representing the hardware address
 * @param int | null $clockSeq A 14-bit number used to help avoid duplicates that could arise when the clock is set
 *     backwards in time or if the node ID changes
 *
 * @return non-empty-string Version 6 UUID as a string
 */
function v6(?Hexadecimal $node = null, ?int $clockSeq = null): string
{
    return Uuid::uuid6($node, $clockSeq)->toString();
}

/**
 * Returns a version 7 (Unix Epoch time) UUID
 *
 * @param DateTimeInterface|null $dateTime An optional date/time from which to create the version 7 UUID. If not
 *     provided, the UUID is generated using the current date/time.
 *
 * @return non-empty-string Version 7 UUID as a string
 */
function v7(?DateTimeInterface $dateTime = null): string
{
    return Uuid::uuid7($dateTime)->toString();
}

/**
 * Returns a version 8 (custom format) UUID
 *
 * The bytes provided may contain any value according to your application's needs. Be aware, however, that other
 * applications may not understand the semantics of the value.
 *
 * @param string $bytes A 16-byte octet string. This is an open blob of data that you may fill with 128 bits of
 *     information. Be aware, however, bits 48 through 51 will be replaced with the UUID version field, and bits 64 and
 *     65 will be replaced with the UUID variant. You MUST NOT rely on these bits for your application needs.
 *
 * @return non-empty-string Version 8 UUID as a string
 *
 * @pure
 */
function v8(string $bytes): string
{
    return Uuid::uuid8($bytes)->toString();
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Guid;

use Ramsey\Uuid\Exception\InvalidArgumentException;
use Ramsey\Uuid\Fields\SerializableFieldsTrait;
use Ramsey\Uuid\Rfc4122\FieldsInterface;
use Ramsey\Uuid\Rfc4122\MaxTrait;
use Ramsey\Uuid\Rfc4122\NilTrait;
use Ramsey\Uuid\Rfc4122\VariantTrait;
use Ramsey\Uuid\Rfc4122\VersionTrait;
use Ramsey\Uuid\Type\Hexadecimal;
use Ramsey\Uuid\Uuid;

use function bin2hex;
use function dechex;
use function hexdec;
use function pack;
use function sprintf;
use function str_pad;
use function strlen;
use function substr;
use function unpack;

use const STR_PAD_LEFT;

/**
 * GUIDs consist of a set of named fields, according to RFC 9562 (formerly RFC 4122)
 *
 * @see Guid
 *
 * @immutable
 */
final class Fields implements FieldsInterface
{
    use MaxTrait;
    use NilTrait;
    use SerializableFieldsTrait;
    use VariantTrait;
    use VersionTrait;

    /**
     * @param string $bytes A 16-byte binary string representation of a UUID
     *
     * @throws InvalidArgumentException if the byte string is not exactly 16 bytes
     * @throws InvalidArgumentException if the byte string does not represent a GUID
     * @throws InvalidArgumentException if the byte string does not contain a valid version
     */
    public function __construct(private string $bytes)
    {
        if (strlen($this->bytes) !== 16) {
            throw new InvalidArgumentException(
                'The byte string must be 16 bytes long; received ' . strlen($this->bytes) . ' bytes',
            );
        }

        if (!$this->isCorrectVariant()) {
            throw new InvalidArgumentException(
                'The byte string received does not conform to the RFC 9562 (formerly RFC 4122) '
                . 'or Microsoft Corporation variants',
            );
        }

        if (!$this->isCorrectVersion()) {
            throw new InvalidArgumentException('The byte string received does not contain a valid version');
        }
    }

    public function getBytes(): string
    {
        return $this->bytes;
    }

    public function getTimeLow(): Hexadecimal
    {
        // Swap the bytes from little endian to network byte order.
        /** @var string[] $hex */
        $hex = unpack(
            'H*',
            pack(
                'v*',
                hexdec(bin2hex(substr($this->bytes, 2, 2))),
                hexdec(bin2hex(substr($this->bytes, 0, 2))),
            ),
        );

        return new Hexadecimal($hex[1] ?? '');
    }

    public function getTimeMid(): Hexadecimal
    {
        // Swap the bytes from little endian to network byte order.
        /** @var string[] $hex */
        $hex = unpack('H*', pack('v', hexdec(bin2hex(substr($this->bytes, 4, 2)))));

        return new Hexadecimal($hex[1] ?? '');
    }

    public function getTimeHiAndVersion(): Hexadecimal
    {
        // Swap the bytes from little endian to network byte order.
        /** @var string[] $hex */
        $hex = unpack('H*', pack('v', hexdec(bin2hex(substr($this->bytes, 6, 2)))));

        return new Hexadecimal($hex[1] ?? '');
    }

    public function getTimestamp(): Hexadecimal
    {
        return new Hexadecimal(sprintf(
            '%03x%04s%08s',
            hexdec($this->getTimeHiAndVersion()->toString()) & 0x0fff,
            $this->getTimeMid()->toString(),
            $this->getTimeLow()->toString()
        ));
    }

    public function getClockSeq(): Hexadecimal
    {
        if ($this->isMax()) {
            $clockSeq = 0xffff;
        } elseif ($this->isNil()) {
            $clockSeq = 0x0000;
        } else {
            $clockSeq = hexdec(bin2hex(substr($this->bytes, 8, 2))) & 0x3fff;
        }

        return new Hexadecimal(str_pad(dechex($clockSeq), 4, '0', STR_PAD_LEFT));
    }

    public function getClockSeqHiAndReserved(): Hexadecimal
    {
        return new Hexadecimal(bin2hex(substr($this->bytes, 8, 1)));
    }

    public function getClockSeqLow(): Hexadecimal
    {
        return new Hexadecimal(bin2hex(substr($this->bytes, 9, 1)));
    }

    public function getNode(): Hexadecimal
    {
        return new Hexadecimal(bin2hex(substr($this->bytes, 10)));
    }

    public function getVersion(): ?int
    {
        if ($this->isNil() || $this->isMax()) {
            return null;
        }

        /** @var int[] $parts */
        $parts = unpack('n*', $this->bytes);

        return ($parts[4] >> 4) & 0x00f;
    }

    private function isCorrectVariant(): bool
    {
        if ($this->isNil() || $this->isMax()) {
            return true;
        }

        $variant = $this->getVariant();

        return $variant === Uuid::RFC_4122 || $variant === Uuid::RESERVED_MICROSOFT;
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Guid;

use Ramsey\Uuid\Builder\UuidBuilderInterface;
use Ramsey\Uuid\Codec\CodecInterface;
use Ramsey\Uuid\Converter\NumberConverterInterface;
use Ramsey\Uuid\Converter\TimeConverterInterface;
use Ramsey\Uuid\Exception\UnableToBuildUuidException;
use Ramsey\Uuid\UuidInterface;
use Throwable;

/**
 * GuidBuilder builds instances of Guid
 *
 * @see Guid
 *
 * @immutable
 */
class GuidBuilder implements UuidBuilderInterface
{
    /**
     * @param NumberConverterInterface $numberConverter The number converter to use when constructing the Guid
     * @param TimeConverterInterface $timeConverter The time converter to use for converting timestamps extracted from a
     *     UUID to Unix timestamps
     */
    public function __construct(
        private NumberConverterInterface $numberConverter,
        private TimeConverterInterface $timeConverter,
    ) {
    }

    /**
     * Builds and returns a Guid
     *
     * @param CodecInterface $codec The codec to use for building this Guid instance
     * @param string $bytes The byte string from which to construct a UUID
     *
     * @return Guid The GuidBuilder returns an instance of Ramsey\Uuid\Guid\Guid
     *
     * @pure
     */
    public function build(CodecInterface $codec, string $bytes): UuidInterface
    {
        try {
            /** @phpstan-ignore possiblyImpure.new */
            return new Guid($this->buildFields($bytes), $this->numberConverter, $codec, $this->timeConverter);
        } catch (Throwable $e) {
            /** @phpstan-ignore possiblyImpure.methodCall, possiblyImpure.methodCall */
            throw new UnableToBuildUuidException($e->getMessage(), (int) $e->getCode(), $e);
        }
    }

    /**
     * Proxy method to allow injecting a mock for testing
     *
     * @pure
     */
    protected function buildFields(string $bytes): Fields
    {
        /** @phpstan-ignore possiblyImpure.new */
        return new Fields($bytes);
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Guid;

use Ramsey\Uuid\Codec\CodecInterface;
use Ramsey\Uuid\Converter\NumberConverterInterface;
use Ramsey\Uuid\Converter\TimeConverterInterface;
use Ramsey\Uuid\Uuid;

/**
 * Guid represents a UUID with "native" (little-endian) byte order
 *
 * From Wikipedia:
 *
 * > The first three fields are unsigned 32- and 16-bit integers and are subject to swapping, while the last two fields
 * > consist of uninterpreted bytes, not subject to swapping. This byte swapping applies even for versions 3, 4, and 5,
 * > where the canonical fields do not correspond to the content of the UUID.
 *
 * The first three fields of a GUID are encoded in little-endian byte order, while the last three fields are in network
 * (big-endian) byte order. This is according to the history of the Microsoft GUID definition.
 *
 * According to the .NET Guid.ToByteArray method documentation:
 *
 * > Note that the order of bytes in the returned byte array is different from the string representation of a Guid value.
 * > The order of the beginning four-byte group and the next two two-byte groups is reversed, whereas the order of the
 * > last two-byte group and the closing six-byte group is the same.
 *
 * @link https://en.wikipedia.org/wiki/Universally_unique_identifier#Variants UUID Variants on Wikipedia
 * @link https://docs.microsoft.com/en-us/windows/win32/api/guiddef/ns-guiddef-guid Windows GUID structure
 * @link https://docs.microsoft.com/en-us/dotnet/api/system.guid .NET Guid Struct
 * @link https://docs.microsoft.com/en-us/dotnet/api/system.guid.tobytearray .NET Guid.ToByteArray Method
 *
 * @immutable
 */
final class Guid extends Uuid
{
    public function __construct(
        Fields $fields,
        NumberConverterInterface $numberConverter,
        CodecInterface $codec,
        TimeConverterInterface $timeConverter,
    ) {
        parent::__construct($fields, $numberConverter, $codec, $timeConverter);
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Nonstandard;

use Ramsey\Uuid\Builder\UuidBuilderInterface;
use Ramsey\Uuid\Codec\CodecInterface;
use Ramsey\Uuid\Converter\NumberConverterInterface;
use Ramsey\Uuid\Converter\TimeConverterInterface;
use Ramsey\Uuid\Exception\UnableToBuildUuidException;
use Ramsey\Uuid\UuidInterface;
use Throwable;

/**
 * Nonstandard\UuidBuilder builds instances of Nonstandard\Uuid
 *
 * @immutable
 */
class UuidBuilder implements UuidBuilderInterface
{
    /**
     * @param NumberConverterInterface $numberConverter The number converter to use when constructing the Nonstandard\Uuid
     * @param TimeConverterInterface $timeConverter The time converter to use for converting timestamps extracted from a
     *     UUID to Unix timestamps
     */
    public function __construct(
        private NumberConverterInterface $numberConverter,
        private TimeConverterInterface $timeConverter,
    ) {
    }

    /**
     * Builds and returns a Nonstandard\Uuid
     *
     * @param CodecInterface $codec The codec to use for building this instance
     * @param string $bytes The byte string from which to construct a UUID
     *
     * @return Uuid The Nonstandard\UuidBuilder returns an instance of Nonstandard\Uuid
     *
     * @pure
     */
    public function build(CodecInterface $codec, string $bytes): UuidInterface
    {
        try {
            /** @phpstan-ignore possiblyImpure.new */
            return new Uuid($this->buildFields($bytes), $this->numberConverter, $codec, $this->timeConverter);
        } catch (Throwable $e) {
            /** @phpstan-ignore possiblyImpure.methodCall, possiblyImpure.methodCall */
            throw new UnableToBuildUuidException($e->getMessage(), (int) $e->getCode(), $e);
        }
    }

    /**
     * Proxy method to allow injecting a mock for testing
     *
     * @pure
     */
    protected function buildFields(string $bytes): Fields
    {
        /** @phpstan-ignore possiblyImpure.new */
        return new Fields($bytes);
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Nonstandard;

use Ramsey\Uuid\Exception\InvalidArgumentException;
use Ramsey\Uuid\Fields\SerializableFieldsTrait;
use Ramsey\Uuid\Rfc4122\FieldsInterface;
use Ramsey\Uuid\Rfc4122\VariantTrait;
use Ramsey\Uuid\Type\Hexadecimal;

use function bin2hex;
use function dechex;
use function hexdec;
use function sprintf;
use function str_pad;
use function strlen;
use function substr;

use const STR_PAD_LEFT;

/**
 * Nonstandard UUID fields do not conform to the RFC 9562 (formerly RFC 4122) standard
 *
 * Since some systems may create nonstandard UUIDs, this implements the {@see FieldsInterface}, so that functionality of
 * a nonstandard UUID is not degraded, in the event these UUIDs are expected to contain RFC 9562 (formerly RFC 4122) fields.
 *
 * Internally, this class represents the fields together as a 16-byte binary string.
 *
 * @immutable
 */
final class Fields implements FieldsInterface
{
    use SerializableFieldsTrait;
    use VariantTrait;

    /**
     * @param string $bytes A 16-byte binary string representation of a UUID
     *
     * @throws InvalidArgumentException if the byte string is not exactly 16 bytes
     */
    public function __construct(private string $bytes)
    {
        if (strlen($this->bytes) !== 16) {
            throw new InvalidArgumentException(
                'The byte string must be 16 bytes long; received ' . strlen($this->bytes) . ' bytes',
            );
        }
    }

    public function getBytes(): string
    {
        return $this->bytes;
    }

    public function getClockSeq(): Hexadecimal
    {
        $clockSeq = hexdec(bin2hex(substr($this->bytes, 8, 2))) & 0x3fff;

        return new Hexadecimal(str_pad(dechex($clockSeq), 4, '0', STR_PAD_LEFT));
    }

    public function getClockSeqHiAndReserved(): Hexadecimal
    {
        return new Hexadecimal(bin2hex(substr($this->bytes, 8, 1)));
    }

    public function getClockSeqLow(): Hexadecimal
    {
        return new Hexadecimal(bin2hex(substr($this->bytes, 9, 1)));
    }

    public function getNode(): Hexadecimal
    {
        return new Hexadecimal(bin2hex(substr($this->bytes, 10)));
    }

    public function getTimeHiAndVersion(): Hexadecimal
    {
        return new Hexadecimal(bin2hex(substr($this->bytes, 6, 2)));
    }

    public function getTimeLow(): Hexadecimal
    {
        return new Hexadecimal(bin2hex(substr($this->bytes, 0, 4)));
    }

    public function getTimeMid(): Hexadecimal
    {
        return new Hexadecimal(bin2hex(substr($this->bytes, 4, 2)));
    }

    public function getTimestamp(): Hexadecimal
    {
        return new Hexadecimal(sprintf(
            '%03x%04s%08s',
            hexdec($this->getTimeHiAndVersion()->toString()) & 0x0fff,
            $this->getTimeMid()->toString(),
            $this->getTimeLow()->toString()
        ));
    }

    public function getVersion(): ?int
    {
        return null;
    }

    public function isNil(): bool
    {
        return false;
    }

    public function isMax(): bool
    {
        return false;
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Nonstandard;

use Ramsey\Uuid\Codec\CodecInterface;
use Ramsey\Uuid\Converter\NumberConverterInterface;
use Ramsey\Uuid\Converter\TimeConverterInterface;
use Ramsey\Uuid\Uuid as BaseUuid;

/**
 * Nonstandard\Uuid is a UUID that doesn't conform to RFC 9562 (formerly RFC 4122)
 *
 * @immutable
 * @pure
 */
final class Uuid extends BaseUuid
{
    public function __construct(
        Fields $fields,
        NumberConverterInterface $numberConverter,
        CodecInterface $codec,
        TimeConverterInterface $timeConverter,
    ) {
        parent::__construct($fields, $numberConverter, $codec, $timeConverter);
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Nonstandard;

use Ramsey\Uuid\Codec\CodecInterface;
use Ramsey\Uuid\Converter\NumberConverterInterface;
use Ramsey\Uuid\Converter\TimeConverterInterface;
use Ramsey\Uuid\Exception\InvalidArgumentException;
use Ramsey\Uuid\Lazy\LazyUuidFromString;
use Ramsey\Uuid\Rfc4122\FieldsInterface as Rfc4122FieldsInterface;
use Ramsey\Uuid\Rfc4122\TimeTrait;
use Ramsey\Uuid\Rfc4122\UuidInterface;
use Ramsey\Uuid\Rfc4122\UuidV1;
use Ramsey\Uuid\Uuid as BaseUuid;

/**
 * Reordered time, or version 6, UUIDs include timestamp, clock sequence, and node values that are combined into a
 * 128-bit unsigned integer
 *
 * @deprecated Use {@see \Ramsey\Uuid\Rfc4122\UuidV6} instead.
 *
 * @link https://github.com/uuid6/uuid6-ietf-draft UUID version 6 IETF draft
 * @link http://gh.peabody.io/uuidv6/ "Version 6" UUIDs
 * @link https://www.rfc-editor.org/rfc/rfc9562#section-5.6 RFC 9562, 5.6. UUID Version 6
 *
 * @immutable
 */
class UuidV6 extends BaseUuid implements UuidInterface
{
    use TimeTrait;

    /**
     * Creates a version 6 (reordered Gregorian time) UUID
     *
     * @param Rfc4122FieldsInterface $fields The fields from which to construct a UUID
     * @param NumberConverterInterface $numberConverter The number converter to use for converting hex values to/from integers
     * @param CodecInterface $codec The codec to use when encoding or decoding UUID strings
     * @param TimeConverterInterface $timeConverter The time converter to use for converting timestamps extracted from a
     *     UUID to unix timestamps
     */
    public function __construct(
        Rfc4122FieldsInterface $fields,
        NumberConverterInterface $numberConverter,
        CodecInterface $codec,
        TimeConverterInterface $timeConverter,
    ) {
        if ($fields->getVersion() !== BaseUuid::UUID_TYPE_REORDERED_TIME) {
            throw new InvalidArgumentException(
                'Fields used to create a UuidV6 must represent a version 6 (reordered time) UUID',
            );
        }

        parent::__construct($fields, $numberConverter, $codec, $timeConverter);
    }

    /**
     * Converts this UUID into an instance of a version 1 UUID
     */
    public function toUuidV1(): UuidV1
    {
        $hex = $this->getHex()->toString();
        $hex = substr($hex, 7, 5)
            . substr($hex, 13, 3)
            . substr($hex, 3, 4)
            . '1' . substr($hex, 0, 3)
            . substr($hex, 16);

        /** @var LazyUuidFromString $uuid */
        $uuid = BaseUuid::fromBytes((string) hex2bin($hex));

        return $uuid->toUuidV1();
    }

    /**
     * Converts a version 1 UUID into an instance of a version 6 UUID
     */
    public static function fromUuidV1(UuidV1 $uuidV1): \Ramsey\Uuid\Rfc4122\UuidV6
    {
        $hex = $uuidV1->getHex()->toString();
        $hex = substr($hex, 13, 3)
            . substr($hex, 8, 4)
            . substr($hex, 0, 5)
            . '6' . substr($hex, 5, 3)
            . substr($hex, 16);

        /** @var LazyUuidFromString $uuid */
        $uuid = BaseUuid::fromBytes((string) hex2bin($hex));

        return $uuid->toUuidV6();
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Builder;

use Ramsey\Uuid\Codec\CodecInterface;
use Ramsey\Uuid\UuidInterface;

/**
 * A UUID builder builds instances of UuidInterface
 *
 * @immutable
 */
interface UuidBuilderInterface
{
    /**
     * Builds and returns a UuidInterface
     *
     * @param CodecInterface $codec The codec to use for building this UuidInterface instance
     * @param string $bytes The byte string from which to construct a UUID
     *
     * @return UuidInterface Implementations may choose to return more specific instances of UUIDs that implement UuidInterface
     *
     * @pure
     */
    public function build(CodecInterface $codec, string $bytes): UuidInterface;
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Builder;

use Ramsey\Uuid\Rfc4122\UuidBuilder as Rfc4122UuidBuilder;

/**
 * @deprecated Please transition to {@see Rfc4122UuidBuilder}.
 *
 * @immutable
 */
class DefaultUuidBuilder extends Rfc4122UuidBuilder
{
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Builder;

use Ramsey\Uuid\Codec\CodecInterface;
use Ramsey\Uuid\Exception\BuilderNotFoundException;
use Ramsey\Uuid\Exception\UnableToBuildUuidException;
use Ramsey\Uuid\UuidInterface;

/**
 * FallbackBuilder builds a UUID by stepping through a list of UUID builders until a UUID can be constructed without exceptions
 *
 * @immutable
 */
class FallbackBuilder implements UuidBuilderInterface
{
    /**
     * @param iterable<UuidBuilderInterface> $builders An array of UUID builders
     */
    public function __construct(private iterable $builders)
    {
    }

    /**
     * Builds and returns a UuidInterface instance using the first builder that succeeds
     *
     * @param CodecInterface $codec The codec to use for building this instance
     * @param string $bytes The byte string from which to construct a UUID
     *
     * @return UuidInterface an instance of a UUID object
     *
     * @pure
     */
    public function build(CodecInterface $codec, string $bytes): UuidInterface
    {
        $lastBuilderException = null;

        foreach ($this->builders as $builder) {
            try {
                return $builder->build($codec, $bytes);
            } catch (UnableToBuildUuidException $exception) {
                $lastBuilderException = $exception;

                continue;
            }
        }

        throw new BuilderNotFoundException(
            'Could not find a suitable builder for the provided codec and fields',
            0,
            $lastBuilderException,
        );
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Builder;

use Ramsey\Uuid\Codec\CodecInterface;
use Ramsey\Uuid\Converter\NumberConverterInterface;
use Ramsey\Uuid\Converter\Time\DegradedTimeConverter;
use Ramsey\Uuid\Converter\TimeConverterInterface;
use Ramsey\Uuid\DegradedUuid;
use Ramsey\Uuid\Rfc4122\Fields as Rfc4122Fields;
use Ramsey\Uuid\UuidInterface;

/**
 * @deprecated DegradedUuid instances are no longer necessary to support 32-bit systems. Please transition to {@see DefaultUuidBuilder}.
 *
 * @immutable
 */
class DegradedUuidBuilder implements UuidBuilderInterface
{
    private TimeConverterInterface $timeConverter;

    /**
     * @param NumberConverterInterface $numberConverter The number converter to use when constructing the DegradedUuid
     * @param TimeConverterInterface|null $timeConverter The time converter to use for converting timestamps extracted
     *     from a UUID to Unix timestamps
     */
    public function __construct(
        private NumberConverterInterface $numberConverter,
        ?TimeConverterInterface $timeConverter = null
    ) {
        $this->timeConverter = $timeConverter ?: new DegradedTimeConverter();
    }

    /**
     * Builds and returns a DegradedUuid
     *
     * @param CodecInterface $codec The codec to use for building this DegradedUuid instance
     * @param string $bytes The byte string from which to construct a UUID
     *
     * @return DegradedUuid The DegradedUuidBuild returns an instance of Ramsey\Uuid\DegradedUuid
     *
     * @phpstan-impure
     */
    public function build(CodecInterface $codec, string $bytes): UuidInterface
    {
        return new DegradedUuid(new Rfc4122Fields($bytes), $this->numberConverter, $codec, $this->timeConverter);
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Builder;

use Ramsey\Collection\AbstractCollection;
use Ramsey\Uuid\Converter\Number\GenericNumberConverter;
use Ramsey\Uuid\Converter\Time\GenericTimeConverter;
use Ramsey\Uuid\Converter\Time\PhpTimeConverter;
use Ramsey\Uuid\Guid\GuidBuilder;
use Ramsey\Uuid\Math\BrickMathCalculator;
use Ramsey\Uuid\Nonstandard\UuidBuilder as NonstandardUuidBuilder;
use Ramsey\Uuid\Rfc4122\UuidBuilder as Rfc4122UuidBuilder;
use Traversable;

/**
 * A collection of UuidBuilderInterface objects
 *
 * @deprecated this class has been deprecated and will be removed in 5.0.0. The use-case for this class comes from a
 *     pre-`phpstan/phpstan` and pre-`vimeo/psalm` ecosystem, in which type safety had to be mostly enforced at runtime:
 *     that is no longer necessary, now that you can safely verify your code to be correct, and use more generic types
 *     like `iterable<T>` instead.
 *
 * @extends AbstractCollection<UuidBuilderInterface>
 */
class BuilderCollection extends AbstractCollection
{
    public function getType(): string
    {
        return UuidBuilderInterface::class;
    }

    public function getIterator(): Traversable
    {
        return parent::getIterator();
    }

    /**
     * Re-constructs the object from its serialized form
     *
     * @param string $serialized The serialized PHP string to unserialize into a UuidInterface instance
     */
    public function unserialize($serialized): void
    {
        /** @var array<array-key, UuidBuilderInterface> $data */
        $data = unserialize($serialized, [
            'allowed_classes' => [
                BrickMathCalculator::class,
                GenericNumberConverter::class,
                GenericTimeConverter::class,
                GuidBuilder::class,
                NonstandardUuidBuilder::class,
                PhpTimeConverter::class,
                Rfc4122UuidBuilder::class,
            ],
        ]);

        $this->data = array_filter(
            $data,
            function ($unserialized): bool {
                /** @phpstan-ignore instanceof.alwaysTrue */
                return $unserialized instanceof UuidBuilderInterface;
            },
        );
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Converter\Number;

use Ramsey\Uuid\Converter\NumberConverterInterface;
use Ramsey\Uuid\Math\CalculatorInterface;
use Ramsey\Uuid\Type\Integer as IntegerObject;

/**
 * GenericNumberConverter uses the provided calculator to convert decimal numbers to and from hexadecimal values
 *
 * @immutable
 */
class GenericNumberConverter implements NumberConverterInterface
{
    public function __construct(private CalculatorInterface $calculator)
    {
    }

    /**
     * @pure
     */
    public function fromHex(string $hex): string
    {
        /** @var non-empty-string $hex */
        return $this->calculator->fromBase($hex, 16)->toString();
    }

    /**
     * @pure
     */
    public function toHex(string $number): string
    {
        /** @phpstan-ignore return.type, possiblyImpure.new */
        return $this->calculator->toBase(new IntegerObject($number), 16);
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Converter\Number;

use Ramsey\Uuid\Converter\NumberConverterInterface;
use Ramsey\Uuid\Math\BrickMathCalculator;

/**
 * Previously used to integrate moontoast/math as a bignum arithmetic library, BigNumberConverter is deprecated in favor
 * of GenericNumberConverter
 *
 * @deprecated Please transition to {@see GenericNumberConverter}.
 *
 * @immutable
 */
class BigNumberConverter implements NumberConverterInterface
{
    private NumberConverterInterface $converter;

    public function __construct()
    {
        $this->converter = new GenericNumberConverter(new BrickMathCalculator());
    }

    /**
     * @pure
     */
    public function fromHex(string $hex): string
    {
        return $this->converter->fromHex($hex);
    }

    /**
     * @pure
     */
    public function toHex(string $number): string
    {
        return $this->converter->toHex($number);
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Converter\Number;

/**
 * @deprecated DegradedNumberConverter is no longer necessary for converting numbers on 32-bit systems. Please
 *     transition to {@see GenericNumberConverter}.
 *
 * @immutable
 */
class DegradedNumberConverter extends BigNumberConverter
{
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Converter;

/**
 * A number converter converts UUIDs from hexadecimal characters into representations of integers and vice versa
 *
 * @immutable
 */
interface NumberConverterInterface
{
    /**
     * Converts a hexadecimal number into a string integer representation of the number
     *
     * The integer representation returned is a string representation of the integer to accommodate unsigned integers
     * that are greater than `PHP_INT_MAX`.
     *
     * @param string $hex The hexadecimal string representation to convert
     *
     * @return numeric-string String representation of an integer
     *
     * @pure
     */
    public function fromHex(string $hex): string;

    /**
     * Converts a string integer representation into a hexadecimal string representation of the number
     *
     * @param string $number A string integer representation to convert; this must be a numeric string to accommodate
     *     unsigned integers that are greater than `PHP_INT_MAX`.
     *
     * @return non-empty-string Hexadecimal string
     *
     * @pure
     */
    public function toHex(string $number): string;
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Converter\Time;

use Ramsey\Uuid\Converter\TimeConverterInterface;
use Ramsey\Uuid\Math\CalculatorInterface;
use Ramsey\Uuid\Math\RoundingMode;
use Ramsey\Uuid\Type\Hexadecimal;
use Ramsey\Uuid\Type\Integer as IntegerObject;
use Ramsey\Uuid\Type\Time;

use function explode;
use function str_pad;

use const STR_PAD_LEFT;

/**
 * GenericTimeConverter uses the provided calculator to calculate and convert time values
 *
 * @immutable
 */
class GenericTimeConverter implements TimeConverterInterface
{
    /**
     * The number of 100-nanosecond intervals from the Gregorian calendar epoch to the Unix epoch.
     */
    private const GREGORIAN_TO_UNIX_INTERVALS = '122192928000000000';

    /**
     * The number of 100-nanosecond intervals in one second.
     */
    private const SECOND_INTERVALS = '10000000';

    /**
     * The number of 100-nanosecond intervals in one microsecond.
     */
    private const MICROSECOND_INTERVALS = '10';

    public function __construct(private CalculatorInterface $calculator)
    {
    }

    public function calculateTime(string $seconds, string $microseconds): Hexadecimal
    {
        /** @phpstan-ignore possiblyImpure.new */
        $timestamp = new Time($seconds, $microseconds);

        // Convert the seconds into a count of 100-nanosecond intervals.
        $sec = $this->calculator->multiply(
            $timestamp->getSeconds(),
            new IntegerObject(self::SECOND_INTERVALS), /** @phpstan-ignore possiblyImpure.new */
        );

        // Convert the microseconds into a count of 100-nanosecond intervals.
        $usec = $this->calculator->multiply(
            $timestamp->getMicroseconds(),
            new IntegerObject(self::MICROSECOND_INTERVALS), /** @phpstan-ignore possiblyImpure.new */
        );

        /**
         * Combine the intervals of seconds and microseconds and add the count of 100-nanosecond intervals from the
         * Gregorian calendar epoch to the Unix epoch. This gives us the correct count of 100-nanosecond intervals since
         * the Gregorian calendar epoch for the given seconds and microseconds.
         *
         * @var IntegerObject $uuidTime
         * @phpstan-ignore possiblyImpure.new
         */
        $uuidTime = $this->calculator->add($sec, $usec, new IntegerObject(self::GREGORIAN_TO_UNIX_INTERVALS));

        /**
         * PHPStan considers CalculatorInterface::toHexadecimal, Hexadecimal:toString impure.
         *
         * @phpstan-ignore possiblyImpure.new
         */
        return new Hexadecimal(str_pad($this->calculator->toHexadecimal($uuidTime)->toString(), 16, '0', STR_PAD_LEFT));
    }

    public function convertTime(Hexadecimal $uuidTimestamp): Time
    {
        // From the total, subtract the number of 100-nanosecond intervals from the Gregorian calendar epoch to the Unix
        // epoch. This gives us the number of 100-nanosecond intervals from the Unix epoch, which also includes the microtime.
        $epochNanoseconds = $this->calculator->subtract(
            $this->calculator->toInteger($uuidTimestamp),
            new IntegerObject(self::GREGORIAN_TO_UNIX_INTERVALS), /** @phpstan-ignore possiblyImpure.new */
        );

        // Convert the 100-nanosecond intervals into seconds and microseconds.
        $unixTimestamp = $this->calculator->divide(
            RoundingMode::HALF_UP,
            6,
            $epochNanoseconds,
            new IntegerObject(self::SECOND_INTERVALS), /** @phpstan-ignore possiblyImpure.new */
        );

        $split = explode('.', (string) $unixTimestamp, 2);

        /** @phpstan-ignore possiblyImpure.new */
        return new Time($split[0], $split[1] ?? 0);
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Converter\Time;

use Ramsey\Uuid\Converter\TimeConverterInterface;
use Ramsey\Uuid\Math\CalculatorInterface;
use Ramsey\Uuid\Math\RoundingMode;
use Ramsey\Uuid\Type\Hexadecimal;
use Ramsey\Uuid\Type\Integer as IntegerObject;
use Ramsey\Uuid\Type\Time;

use function explode;
use function str_pad;

use const STR_PAD_LEFT;

/**
 * UnixTimeConverter converts Unix Epoch timestamps to/from hexadecimal values consisting of milliseconds elapsed since
 * the Unix Epoch
 *
 * @immutable
 */
class UnixTimeConverter implements TimeConverterInterface
{
    private const MILLISECONDS = 1000;

    public function __construct(private CalculatorInterface $calculator)
    {
    }

    public function calculateTime(string $seconds, string $microseconds): Hexadecimal
    {
        /** @phpstan-ignore possiblyImpure.new */
        $timestamp = new Time($seconds, $microseconds);

        // Convert the seconds into milliseconds.
        $sec = $this->calculator->multiply(
            $timestamp->getSeconds(),
            new IntegerObject(self::MILLISECONDS) /** @phpstan-ignore possiblyImpure.new */
        );

        // Convert the microseconds into milliseconds; the scale is zero because we need to discard the fractional part.
        $usec = $this->calculator->divide(
            RoundingMode::DOWN, // Always round down to stay in the previous millisecond.
            0,
            $timestamp->getMicroseconds(),
            new IntegerObject(self::MILLISECONDS), /** @phpstan-ignore possiblyImpure.new */
        );

        /** @var IntegerObject $unixTime */
        $unixTime = $this->calculator->add($sec, $usec);

        /** @phpstan-ignore possiblyImpure.new */
        return new Hexadecimal(
            str_pad(
                $this->calculator->toHexadecimal($unixTime)->toString(),
                12,
                '0',
                STR_PAD_LEFT
            ),
        );
    }

    public function convertTime(Hexadecimal $uuidTimestamp): Time
    {
        $milliseconds = $this->calculator->toInteger($uuidTimestamp);

        $unixTimestamp = $this->calculator->divide(
            RoundingMode::HALF_UP,
            6,
            $milliseconds,
            new IntegerObject(self::MILLISECONDS), /** @phpstan-ignore possiblyImpure.new */
        );

        $split = explode('.', (string) $unixTimestamp, 2);

        /** @phpstan-ignore possiblyImpure.new */
        return new Time($split[0], $split[1] ?? '0');
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Converter\Time;

/**
 * @deprecated DegradedTimeConverter is no longer necessary for converting time on 32-bit systems. Please transition to
 *     {@see GenericTimeConverter}.
 *
 * @immutable
 */
class DegradedTimeConverter extends BigNumberTimeConverter
{
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Converter\Time;

use Ramsey\Uuid\Converter\TimeConverterInterface;
use Ramsey\Uuid\Math\BrickMathCalculator;
use Ramsey\Uuid\Math\CalculatorInterface;
use Ramsey\Uuid\Type\Hexadecimal;
use Ramsey\Uuid\Type\Integer as IntegerObject;
use Ramsey\Uuid\Type\Time;

use function count;
use function dechex;
use function explode;
use function is_float;
use function is_int;
use function str_pad;
use function strlen;
use function substr;

use const STR_PAD_LEFT;
use const STR_PAD_RIGHT;

/**
 * PhpTimeConverter uses built-in PHP functions and standard math operations available to the PHP programming language
 * to provide facilities for converting parts of time into representations that may be used in UUIDs
 *
 * @immutable
 */
class PhpTimeConverter implements TimeConverterInterface
{
    /**
     * The number of 100-nanosecond intervals from the Gregorian calendar epoch to the Unix epoch.
     */
    private const GREGORIAN_TO_UNIX_INTERVALS = 0x01b21dd213814000;

    /**
     * The number of 100-nanosecond intervals in one second.
     */
    private const SECOND_INTERVALS = 10_000_000;

    /**
     * The number of 100-nanosecond intervals in one microsecond.
     */
    private const MICROSECOND_INTERVALS = 10;

    private int $phpPrecision;
    private CalculatorInterface $calculator;
    private TimeConverterInterface $fallbackConverter;

    public function __construct(
        ?CalculatorInterface $calculator = null,
        ?TimeConverterInterface $fallbackConverter = null,
    ) {
        if ($calculator === null) {
            $calculator = new BrickMathCalculator();
        }

        if ($fallbackConverter === null) {
            $fallbackConverter = new GenericTimeConverter($calculator);
        }

        $this->calculator = $calculator;
        $this->fallbackConverter = $fallbackConverter;
        $this->phpPrecision = (int) ini_get('precision');
    }

    public function calculateTime(string $seconds, string $microseconds): Hexadecimal
    {
        $seconds = new IntegerObject($seconds); /** @phpstan-ignore possiblyImpure.new */
        $microseconds = new IntegerObject($microseconds); /** @phpstan-ignore possiblyImpure.new */

        // Calculate the count of 100-nanosecond intervals since the Gregorian calendar epoch
        // for the given seconds and microseconds.
        $uuidTime = ((int) $seconds->toString() * self::SECOND_INTERVALS)
            + ((int) $microseconds->toString() * self::MICROSECOND_INTERVALS)
            + self::GREGORIAN_TO_UNIX_INTERVALS;

        // Check to see whether we've overflowed the max/min integer size.
        // If so, we will default to a different time converter.
        // @phpstan-ignore function.alreadyNarrowedType (the integer value might have overflowed)
        if (!is_int($uuidTime)) {
            return $this->fallbackConverter->calculateTime(
                $seconds->toString(),
                $microseconds->toString(),
            );
        }

        /** @phpstan-ignore possiblyImpure.new */
        return new Hexadecimal(
            str_pad(dechex($uuidTime), 16, '0', STR_PAD_LEFT)
        );
    }

    public function convertTime(Hexadecimal $uuidTimestamp): Time
    {
        $timestamp = $this->calculator->toInteger($uuidTimestamp);

        // Convert the 100-nanosecond intervals into seconds and microseconds.
        $splitTime = $this->splitTime(
            ($timestamp->toString() - self::GREGORIAN_TO_UNIX_INTERVALS) / self::SECOND_INTERVALS,
        );

        if (count($splitTime) === 0) {
            return $this->fallbackConverter->convertTime($uuidTimestamp);
        }

        /** @phpstan-ignore possiblyImpure.new */
        return new Time($splitTime['sec'], $splitTime['usec']);
    }

    /**
     * @param float | int $time The time to split into seconds and microseconds
     *
     * @return string[]
     *
     * @pure
     */
    private function splitTime(float | int $time): array
    {
        $split = explode('.', (string) $time, 2);

        // If the $time value is a float but $split only has 1 element, then the float math was rounded up to the next
        // second, so we want to return an empty array to allow use of the fallback converter.
        if (is_float($time) && count($split) === 1) {
            return [];
        }

        if (count($split) === 1) {
            return ['sec' => $split[0], 'usec' => '0'];
        }

        // If the microseconds are less than six characters AND the length of the number is greater than or equal to the
        // PHP precision, then it's possible that we lost some precision for the microseconds. Return an empty array so
        // that we can choose to use the fallback converter.
        if (strlen($split[1]) < 6 && strlen((string) $time) >= $this->phpPrecision) {
            return [];
        }

        $microseconds = $split[1];

        // Ensure the microseconds are no longer than 6 digits. If they are,
        // truncate the number to the first 6 digits and round up, if needed.
        if (strlen($microseconds) > 6) {
            $roundingDigit = (int) substr($microseconds, 6, 1);
            $microseconds = (int) substr($microseconds, 0, 6);

            if ($roundingDigit >= 5) {
                $microseconds++;
            }
        }

        return [
            'sec' => $split[0],
            'usec' => str_pad((string) $microseconds, 6, '0', STR_PAD_RIGHT),
        ];
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Converter\Time;

use Ramsey\Uuid\Converter\TimeConverterInterface;
use Ramsey\Uuid\Math\BrickMathCalculator;
use Ramsey\Uuid\Type\Hexadecimal;
use Ramsey\Uuid\Type\Time;

/**
 * Previously used to integrate moontoast/math as a bignum arithmetic library, BigNumberTimeConverter is deprecated in
 * favor of GenericTimeConverter
 *
 * @deprecated Please transition to {@see GenericTimeConverter}.
 *
 * @immutable
 */
class BigNumberTimeConverter implements TimeConverterInterface
{
    private TimeConverterInterface $converter;

    public function __construct()
    {
        $this->converter = new GenericTimeConverter(new BrickMathCalculator());
    }

    public function calculateTime(string $seconds, string $microseconds): Hexadecimal
    {
        return $this->converter->calculateTime($seconds, $microseconds);
    }

    public function convertTime(Hexadecimal $uuidTimestamp): Time
    {
        return $this->converter->convertTime($uuidTimestamp);
    }
}
<?php

/**
 * This file is part of the ramsey/uuid library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Uuid\Converter;

use Ramsey\Uuid\Type\Hexadecimal;
use Ramsey\Uuid\Type\Time;

/**
 * A time converter converts timestamps into representations that may be used in UUIDs
 *
 * @immutable
 */
interface TimeConverterInterface
{
    /**
     * Uses the provided seconds and micro-seconds to calculate the count of 100-nanosecond intervals since
     * UTC 00:00:00.00, 15 October 1582, for RFC 9562 (formerly RFC 4122) variant UUIDs
     *
     * @link https://www.rfc-editor.org/rfc/rfc9562#appendix-A RFC 9562, Appendix A. Test Vectors
     *
     * @param string $seconds A string representation of seconds since the Unix epoch for the time to calculate
     * @param string $microseconds A string representation of the micro-seconds associated with the time to calculate
     *
     * @return Hexadecimal The full UUID timestamp as a Hexadecimal value
     *
     * @pure
     */
    public function calculateTime(string $seconds, string $microseconds): Hexadecimal;

    /**
     * Converts a timestamp extracted from a UUID to a Unix timestamp
     *
     * @param Hexadecimal $uuidTimestamp A hexadecimal representation of a UUID timestamp; a UUID timestamp is a count
     *     of 100-nanosecond intervals since UTC 00:00:00.00, 15 October 1582.
     *
     * @return Time An instance of {@see Time}
     *
     * @pure
     */
    public function convertTime(Hexadecimal $uuidTimestamp): Time;
}
{
    "name": "ramsey/uuid",
    "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).",
    "license": "MIT",
    "type": "library",
    "keywords": [
        "uuid",
        "identifier",
        "guid"
    ],
    "require": {
        "php": "^8.0",
        "brick/math": "^0.8.16 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13 || ^0.14 || ^0.15 || ^0.16 || ^0.17 || ^0.18 || ^0.19 || ^0.20 || ^1.0",
        "ramsey/collection": "^1.2 || ^2.0"
    },
    "require-dev": {
        "captainhook/captainhook": "^5.25",
        "captainhook/plugin-composer": "^5.3",
        "dealerdirect/phpcodesniffer-composer-installer": "^1.0",
        "ergebnis/composer-normalize": "^2.47",
        "mockery/mockery": "^1.6",
        "paragonie/random-lib": "^2",
        "php-mock/php-mock": "^2.6",
        "php-mock/php-mock-mockery": "^1.5",
        "php-parallel-lint/php-parallel-lint": "^1.4.0",
        "phpbench/phpbench": "^1.2.14",
        "phpstan/extension-installer": "^1.4",
        "phpstan/phpstan": "^2.1",
        "phpstan/phpstan-mockery": "^2.0",
        "phpstan/phpstan-phpunit": "^2.0",
        "phpunit/phpunit": "^9.6",
        "slevomat/coding-standard": "^8.18",
        "squizlabs/php_codesniffer": "^3.13"
    },
    "replace": {
        "rhumsaa/uuid": "self.version"
    },
    "suggest": {
        "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.",
        "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.",
        "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.",
        "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter",
        "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type."
    },
    "minimum-stability": "dev",
    "prefer-stable": true,
    "autoload": {
        "psr-4": {
            "Ramsey\\Uuid\\": "src/"
        },
        "files": [
            "src/functions.php"
        ]
    },
    "autoload-dev": {
        "psr-4": {
            "Ramsey\\Uuid\\Benchmark\\": "tests/benchmark/",
            "Ramsey\\Uuid\\StaticAnalysis\\": "tests/static-analysis/",
            "Ramsey\\Uuid\\Test\\": "tests/"
        }
    },
    "config": {
        "allow-plugins": {
            "captainhook/plugin-composer": true,
            "dealerdirect/phpcodesniffer-composer-installer": true,
            "ergebnis/composer-normalize": true,
            "phpstan/extension-installer": true
        },
        "sort-packages": true
    },
    "extra": {
        "captainhook": {
            "force-install": true
        }
    },
    "scripts": {
        "dev:analyze": "@dev:analyze:phpstan",
        "dev:analyze:phpstan": "phpstan analyse --ansi --memory-limit 1G",
        "dev:bench": "@php -d 'error_reporting=24575' vendor/bin/phpbench run",
        "dev:build:clean": "git clean -fX build/",
        "dev:lint": [
            "@dev:lint:syntax",
            "@dev:lint:style"
        ],
        "dev:lint:fix": "phpcbf --cache=build/cache/phpcs.cache",
        "dev:lint:style": "phpcs --cache=build/cache/phpcs.cache --colors",
        "dev:lint:syntax": "parallel-lint --colors src/ tests/",
        "dev:test": [
            "@dev:lint",
            "@dev:bench",
            "@dev:analyze",
            "@dev:test:unit"
        ],
        "dev:test:coverage:ci": "@php -d 'xdebug.mode=coverage' vendor/bin/phpunit --colors=always --coverage-text --coverage-clover build/coverage/clover.xml --coverage-cobertura build/coverage/cobertura.xml --coverage-crap4j build/coverage/crap4j.xml --coverage-xml build/coverage/coverage-xml --log-junit build/junit.xml",
        "dev:test:coverage:html": "@php -d 'xdebug.mode=coverage' vendor/bin/phpunit --colors=always --coverage-html build/coverage/coverage-html/",
        "dev:test:unit": "phpunit --colors=always",
        "test": "@dev:test"
    },
    "scripts-descriptions": {
        "dev:analyze": "Runs all static analysis checks.",
        "dev:analyze:phpstan": "Runs the PHPStan static analyzer.",
        "dev:bench": "Runs PHPBench benchmark tests.",
        "dev:build:clean": "Cleans the build/ directory.",
        "dev:lint": "Runs all linting checks.",
        "dev:lint:fix": "Auto-fixes coding standards issues, if possible.",
        "dev:lint:style": "Checks for coding standards issues.",
        "dev:lint:syntax": "Checks for syntax errors.",
        "dev:test": "Runs linting, static analysis, and unit tests.",
        "dev:test:coverage:ci": "Runs unit tests and generates CI coverage reports.",
        "dev:test:coverage:html": "Runs unit tests and generates HTML coverage report.",
        "dev:test:unit": "Runs unit tests.",
        "test": "Runs linting, static analysis, and unit tests."
    }
}
<?php

/**
 * This file is part of the ramsey/collection library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Collection;

/**
 * A collection represents a group of objects.
 *
 * Each object in the collection is of a specific, defined type.
 *
 * This is a direct implementation of `CollectionInterface`, provided for
 * the sake of convenience.
 *
 * Example usage:
 *
 * ```
 * $collection = new \Ramsey\Collection\Collection('My\\Foo');
 * $collection->add(new \My\Foo());
 * $collection->add(new \My\Foo());
 *
 * foreach ($collection as $foo) {
 *     // Do something with $foo
 * }
 * ```
 *
 * It is preferable to subclass `AbstractCollection` to create your own typed
 * collections. For example:
 *
 * ```
 * namespace My\Foo;
 *
 * class FooCollection extends \Ramsey\Collection\AbstractCollection
 * {
 *     public function getType()
 *     {
 *         return 'My\\Foo';
 *     }
 * }
 * ```
 *
 * And then use it similarly to the earlier example:
 *
 * ```
 * $fooCollection = new \My\Foo\FooCollection();
 * $fooCollection->add(new \My\Foo());
 * $fooCollection->add(new \My\Foo());
 *
 * foreach ($fooCollection as $foo) {
 *     // Do something with $foo
 * }
 * ```
 *
 * The benefit with this approach is that you may do type-checking on the
 * collection object:
 *
 * ```
 * if ($collection instanceof \My\Foo\FooCollection) {
 *     // the collection is a collection of My\Foo objects
 * }
 * ```
 *
 * @template T
 * @extends AbstractCollection<T>
 */
class Collection extends AbstractCollection
{
    /**
     * Constructs a collection object of the specified type, optionally with the
     * specified data.
     *
     * @param string $collectionType The type or class name associated with this
     *     collection.
     * @param array<array-key, T> $data The initial items to store in the collection.
     */
    public function __construct(private readonly string $collectionType, array $data = [])
    {
        parent::__construct($data);
    }

    public function getType(): string
    {
        return $this->collectionType;
    }
}
<?php

/**
 * This file is part of the ramsey/collection library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Collection;

use Closure;
use Ramsey\Collection\Exception\CollectionMismatchException;
use Ramsey\Collection\Exception\InvalidArgumentException;
use Ramsey\Collection\Exception\InvalidPropertyOrMethod;
use Ramsey\Collection\Exception\NoSuchElementException;
use Ramsey\Collection\Exception\UnsupportedOperationException;
use Ramsey\Collection\Tool\TypeTrait;
use Ramsey\Collection\Tool\ValueExtractorTrait;
use Ramsey\Collection\Tool\ValueToStringTrait;

use function array_filter;
use function array_key_first;
use function array_key_last;
use function array_map;
use function array_merge;
use function array_reduce;
use function array_search;
use function array_udiff;
use function array_uintersect;
use function in_array;
use function is_int;
use function is_object;
use function spl_object_id;
use function sprintf;
use function usort;

/**
 * This class provides a basic implementation of `CollectionInterface`, to
 * minimize the effort required to implement this interface
 *
 * @template T
 * @extends AbstractArray<T>
 * @implements CollectionInterface<T>
 */
abstract class AbstractCollection extends AbstractArray implements CollectionInterface
{
    use TypeTrait;
    use ValueToStringTrait;
    use ValueExtractorTrait;

    /**
     * @throws InvalidArgumentException if $element is of the wrong type.
     */
    public function add(mixed $element): bool
    {
        $this[] = $element;

        return true;
    }

    public function contains(mixed $element, bool $strict = true): bool
    {
        return in_array($element, $this->data, $strict);
    }

    /**
     * @throws InvalidArgumentException if $element is of the wrong type.
     */
    public function offsetSet(mixed $offset, mixed $value): void
    {
        if ($this->checkType($this->getType(), $value) === false) {
            throw new InvalidArgumentException(
                'Value must be of type ' . $this->getType() . '; value is '
                . $this->toolValueToString($value),
            );
        }

        if ($offset === null) {
            $this->data[] = $value;
        } else {
            $this->data[$offset] = $value;
        }
    }

    public function remove(mixed $element): bool
    {
        if (($position = array_search($element, $this->data, true)) !== false) {
            unset($this[$position]);

            return true;
        }

        return false;
    }

    /**
     * @throws InvalidPropertyOrMethod if the $propertyOrMethod does not exist
     *     on the elements in this collection.
     * @throws UnsupportedOperationException if unable to call column() on this
     *     collection.
     *
     * @inheritDoc
     */
    public function column(string $propertyOrMethod): array
    {
        $temp = [];

        foreach ($this->data as $item) {
            $temp[] = $this->extractValue($item, $propertyOrMethod);
        }

        return $temp;
    }

    /**
     * @return T
     *
     * @throws NoSuchElementException if this collection is empty.
     */
    public function first(): mixed
    {
        $firstIndex = array_key_first($this->data);

        if ($firstIndex === null) {
            throw new NoSuchElementException('Can\'t determine first item. Collection is empty');
        }

        return $this->data[$firstIndex];
    }

    /**
     * @return T
     *
     * @throws NoSuchElementException if this collection is empty.
     */
    public function last(): mixed
    {
        $lastIndex = array_key_last($this->data);

        if ($lastIndex === null) {
            throw new NoSuchElementException('Can\'t determine last item. Collection is empty');
        }

        return $this->data[$lastIndex];
    }

    /**
     * @return CollectionInterface<T>
     *
     * @throws InvalidPropertyOrMethod if the $propertyOrMethod does not exist
     *     on the elements in this collection.
     * @throws UnsupportedOperationException if unable to call sort() on this
     *     collection.
     */
    public function sort(?string $propertyOrMethod = null, Sort $order = Sort::Ascending): CollectionInterface
    {
        $collection = clone $this;

        usort(
            $collection->data,
            function (mixed $a, mixed $b) use ($propertyOrMethod, $order): int {
                $aValue = $this->extractValue($a, $propertyOrMethod);
                $bValue = $this->extractValue($b, $propertyOrMethod);

                return ($aValue <=> $bValue) * ($order === Sort::Descending ? -1 : 1);
            },
        );

        return $collection;
    }

    /**
     * @param callable(T): bool $callback A callable to use for filtering elements.
     *
     * @return CollectionInterface<T>
     */
    public function filter(callable $callback): CollectionInterface
    {
        $collection = clone $this;
        $collection->data = array_merge([], array_filter($collection->data, $callback));

        return $collection;
    }

    /**
     * @return CollectionInterface<T>
     *
     * @throws InvalidPropertyOrMethod if the $propertyOrMethod does not exist
     *     on the elements in this collection.
     * @throws UnsupportedOperationException if unable to call where() on this
     *     collection.
     */
    public function where(?string $propertyOrMethod, mixed $value): CollectionInterface
    {
        return $this->filter(
            fn (mixed $item): bool => $this->extractValue($item, $propertyOrMethod) === $value,
        );
    }

    /**
     * @param callable(T): TCallbackReturn $callback A callable to apply to each
     *     item of the collection.
     *
     * @return CollectionInterface<TCallbackReturn>
     *
     * @template TCallbackReturn
     */
    public function map(callable $callback): CollectionInterface
    {
        return new Collection('mixed', array_map($callback, $this->data));
    }

    /**
     * @param callable(TCarry, T): TCarry $callback A callable to apply to each
     *     item of the collection to reduce it to a single value.
     * @param TCarry $initial This is the initial value provided to the callback.
     *
     * @return TCarry
     *
     * @template TCarry
     */
    public function reduce(callable $callback, mixed $initial): mixed
    {
        return array_reduce($this->data, $callback, $initial);
    }

    /**
     * @param CollectionInterface<T> $other The collection to check for divergent
     *     items.
     *
     * @return CollectionInterface<T>
     *
     * @throws CollectionMismatchException if the compared collections are of
     *     differing types.
     */
    public function diff(CollectionInterface $other): CollectionInterface
    {
        $this->compareCollectionTypes($other);

        $diffAtoB = array_udiff($this->data, $other->toArray(), $this->getComparator());
        $diffBtoA = array_udiff($other->toArray(), $this->data, $this->getComparator());

        $collection = clone $this;
        $collection->data = array_merge($diffAtoB, $diffBtoA);

        return $collection;
    }

    /**
     * @param CollectionInterface<T> $other The collection to check for
     *     intersecting items.
     *
     * @return CollectionInterface<T>
     *
     * @throws CollectionMismatchException if the compared collections are of
     *     differing types.
     */
    public function intersect(CollectionInterface $other): CollectionInterface
    {
        $this->compareCollectionTypes($other);

        $collection = clone $this;
        $collection->data = array_uintersect($this->data, $other->toArray(), $this->getComparator());

        return $collection;
    }

    /**
     * @param CollectionInterface<T> ...$collections The collections to merge.
     *
     * @return CollectionInterface<T>
     *
     * @throws CollectionMismatchException if unable to merge any of the given
     *     collections or items within the given collections due to type
     *     mismatch errors.
     */
    public function merge(CollectionInterface ...$collections): CollectionInterface
    {
        $mergedCollection = clone $this;

        foreach ($collections as $index => $collection) {
            if (!$collection instanceof static) {
                throw new CollectionMismatchException(
                    sprintf('Collection with index %d must be of type %s', $index, static::class),
                );
            }

            // When using generics (Collection.php, Set.php, etc),
            // we also need to make sure that the internal types match each other
            if ($this->getUniformType($collection) !== $this->getUniformType($this)) {
                throw new CollectionMismatchException(
                    sprintf(
                        'Collection items in collection with index %d must be of type %s',
                        $index,
                        $this->getType(),
                    ),
                );
            }

            foreach ($collection as $key => $value) {
                if (is_int($key)) {
                    $mergedCollection[] = $value;
                } else {
                    $mergedCollection[$key] = $value;
                }
            }
        }

        return $mergedCollection;
    }

    /**
     * @param CollectionInterface<T> $other
     *
     * @throws CollectionMismatchException
     */
    private function compareCollectionTypes(CollectionInterface $other): void
    {
        if (!$other instanceof static) {
            throw new CollectionMismatchException('Collection must be of type ' . static::class);
        }

        // When using generics (Collection.php, Set.php, etc),
        // we also need to make sure that the internal types match each other
        if ($this->getUniformType($other) !== $this->getUniformType($this)) {
            throw new CollectionMismatchException('Collection items must be of type ' . $this->getType());
        }
    }

    private function getComparator(): Closure
    {
        return function (mixed $a, mixed $b): int {
            // If the two values are object, we convert them to unique scalars.
            // If the collection contains mixed values (unlikely) where some are objects
            // and some are not, we leave them as they are.
            // The comparator should still work and the result of $a < $b should
            // be consistent but unpredictable since not documented.
            if (is_object($a) && is_object($b)) {
                $a = spl_object_id($a);
                $b = spl_object_id($b);
            }

            return $a === $b ? 0 : ($a < $b ? 1 : -1);
        };
    }

    /**
     * @param CollectionInterface<mixed> $collection
     */
    private function getUniformType(CollectionInterface $collection): string
    {
        return match ($collection->getType()) {
            'integer' => 'int',
            'boolean' => 'bool',
            'double' => 'float',
            default => $collection->getType(),
        };
    }
}
<?php

/**
 * This file is part of the ramsey/collection library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Collection;

use ArrayIterator;
use Traversable;

use function count;

/**
 * This class provides a basic implementation of `ArrayInterface`, to minimize
 * the effort required to implement this interface.
 *
 * @template T
 * @implements ArrayInterface<T>
 */
abstract class AbstractArray implements ArrayInterface
{
    /**
     * The items of this array.
     *
     * @var array<array-key, T>
     */
    protected array $data = [];

    /**
     * Constructs a new array object.
     *
     * @param array<array-key, T> $data The initial items to add to this array.
     */
    public function __construct(array $data = [])
    {
        // Invoke offsetSet() for each value added; in this way, subclasses
        // may provide additional logic about values added to the array object.
        foreach ($data as $key => $value) {
            $this[$key] = $value;
        }
    }

    /**
     * Returns an iterator for this array.
     *
     * @link http://php.net/manual/en/iteratoraggregate.getiterator.php IteratorAggregate::getIterator()
     *
     * @return Traversable<array-key, T>
     */
    public function getIterator(): Traversable
    {
        return new ArrayIterator($this->data);
    }

    /**
     * Returns `true` if the given offset exists in this array.
     *
     * @link http://php.net/manual/en/arrayaccess.offsetexists.php ArrayAccess::offsetExists()
     *
     * @param array-key $offset The offset to check.
     */
    public function offsetExists(mixed $offset): bool
    {
        return isset($this->data[$offset]);
    }

    /**
     * Returns the value at the specified offset.
     *
     * @link http://php.net/manual/en/arrayaccess.offsetget.php ArrayAccess::offsetGet()
     *
     * @param array-key $offset The offset for which a value should be returned.
     *
     * @return T the value stored at the offset, or null if the offset
     *     does not exist.
     */
    public function offsetGet(mixed $offset): mixed
    {
        return $this->data[$offset];
    }

    /**
     * Sets the given value to the given offset in the array.
     *
     * @link http://php.net/manual/en/arrayaccess.offsetset.php ArrayAccess::offsetSet()
     *
     * @param array-key | null $offset The offset to set. If `null`, the value
     *     may be set at a numerically-indexed offset.
     * @param T $value The value to set at the given offset.
     */
    public function offsetSet(mixed $offset, mixed $value): void
    {
        if ($offset === null) {
            $this->data[] = $value;
        } else {
            $this->data[$offset] = $value;
        }
    }

    /**
     * Removes the given offset and its value from the array.
     *
     * @link http://php.net/manual/en/arrayaccess.offsetunset.php ArrayAccess::offsetUnset()
     *
     * @param array-key $offset The offset to remove from the array.
     */
    public function offsetUnset(mixed $offset): void
    {
        unset($this->data[$offset]);
    }

    /**
     * Returns data suitable for PHP serialization.
     *
     * @link https://www.php.net/manual/en/language.oop5.magic.php#language.oop5.magic.serialize
     * @link https://www.php.net/serialize
     *
     * @return array<array-key, T>
     */
    public function __serialize(): array
    {
        return $this->data;
    }

    /**
     * Adds unserialized data to the object.
     *
     * @param array<array-key, T> $data
     */
    public function __unserialize(array $data): void
    {
        $this->data = $data;
    }

    /**
     * Returns the number of items in this array.
     *
     * @link http://php.net/manual/en/countable.count.php Countable::count()
     */
    public function count(): int
    {
        return count($this->data);
    }

    public function clear(): void
    {
        $this->data = [];
    }

    /**
     * @inheritDoc
     */
    public function toArray(): array
    {
        return $this->data;
    }

    public function isEmpty(): bool
    {
        return $this->data === [];
    }
}
<?php

/**
 * This file is part of the ramsey/collection library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Collection;

use Ramsey\Collection\Exception\NoSuchElementException;
use RuntimeException;

/**
 * A queue is a collection in which the entities in the collection are kept in
 * order.
 *
 * The principal operations on the queue are the addition of entities to the end
 * (tail), also known as *enqueue*, and removal of entities from the front
 * (head), also known as *dequeue*. This makes the queue a first-in-first-out
 * (FIFO) data structure.
 *
 * Besides basic array operations, queues provide additional insertion,
 * extraction, and inspection operations. Each of these methods exists in two
 * forms: one throws an exception if the operation fails, the other returns a
 * special value (either `null` or `false`, depending on the operation). The
 * latter form of the insert operation is designed specifically for use with
 * capacity-restricted `QueueInterface` implementations; in most
 * implementations, insert operations cannot fail.
 *
 * <table>
 * <caption>Summary of QueueInterface methods</caption>
 * <thead>
 * <tr>
 * <td></td>
 * <td><em>Throws exception</em></td>
 * <td><em>Returns special value</em></td>
 * </tr>
 * </thead>
 * <tbody>
 * <tr>
 * <th>Insert</th>
 * <td><code>add()</code></td>
 * <td><code>offer()</code></td>
 * </tr>
 * <tr>
 * <th>Remove</th>
 * <td><code>remove()</code></td>
 * <td><code>poll()</code></td>
 * </tr>
 * <tr>
 * <th>Examine</th>
 * <td><code>element()</code></td>
 * <td><code>peek()</code></td>
 * </tr>
 * </tbody>
 * </table>
 *
 * Queues typically, but do not necessarily, order elements in a FIFO
 * (first-in-first-out) manner. Among the exceptions are priority queues, which
 * order elements according to a supplied comparator, or the elements' natural
 * ordering, and LIFO queues (or stacks) which order the elements LIFO
 * (last-in-first-out). Whatever the ordering used, the head of the queue is
 * that element which would be removed by a call to remove() or poll(). In a
 * FIFO queue, all new elements are inserted at the tail of the queue. Other
 * kinds of queues may use different placement rules. Every `QueueInterface`
 * implementation must specify its ordering properties.
 *
 * The `offer()` method inserts an element if possible, otherwise returning
 * `false`. This differs from the `add()` method, which can fail to add an
 * element only by throwing an unchecked exception. The `offer()` method is
 * designed for use when failure is a normal, rather than exceptional
 * occurrence, for example, in fixed-capacity (or "bounded") queues.
 *
 * The `remove()` and `poll()` methods remove and return the head of the queue.
 * Exactly which element is removed from the queue is a function of the queue's
 * ordering policy, which differs from implementation to implementation. The
 * `remove()` and `poll()` methods differ only in their behavior when the queue
 * is empty: the `remove()` method throws an exception, while the `poll()`
 * method returns `null`.
 *
 * The `element()` and `peek()` methods return, but do not remove, the head of
 * the queue.
 *
 * `QueueInterface` implementations generally do not allow insertion of `null`
 * elements, although some implementations do not prohibit insertion of `null`.
 * Even in the implementations that permit it, `null` should not be inserted
 * into a queue, as `null` is also used as a special return value by the
 * `poll()` method to indicate that the queue contains no elements.
 *
 * @template T
 * @extends ArrayInterface<T>
 */
interface QueueInterface extends ArrayInterface
{
    /**
     * Ensures that this queue contains the specified element (optional
     * operation).
     *
     * Returns `true` if this queue changed as a result of the call. (Returns
     * `false` if this queue does not permit duplicates and already contains the
     * specified element.)
     *
     * Queues that support this operation may place limitations on what elements
     * may be added to this queue. In particular, some queues will refuse to add
     * `null` elements, and others will impose restrictions on the type of
     * elements that may be added. Queue classes should clearly specify in their
     * documentation any restrictions on what elements may be added.
     *
     * If a queue refuses to add a particular element for any reason other than
     * that it already contains the element, it must throw an exception (rather
     * than returning `false`). This preserves the invariant that a queue always
     * contains the specified element after this call returns.
     *
     * @see self::offer()
     *
     * @param T $element The element to add to this queue.
     *
     * @return bool `true` if this queue changed as a result of the call.
     *
     * @throws RuntimeException if a queue refuses to add a particular element
     *     for any reason other than that it already contains the element.
     *     Implementations should use a more-specific exception that extends
     *     `\RuntimeException`.
     */
    public function add(mixed $element): bool;

    /**
     * Retrieves, but does not remove, the head of this queue.
     *
     * This method differs from `peek()` only in that it throws an exception if
     * this queue is empty.
     *
     * @see self::peek()
     *
     * @return T the head of this queue.
     *
     * @throws NoSuchElementException if this queue is empty.
     */
    public function element(): mixed;

    /**
     * Inserts the specified element into this queue if it is possible to do so
     * immediately without violating capacity restrictions.
     *
     * When using a capacity-restricted queue, this method is generally
     * preferable to `add()`, which can fail to insert an element only by
     * throwing an exception.
     *
     * @see self::add()
     *
     * @param T $element The element to add to this queue.
     *
     * @return bool `true` if the element was added to this queue, else `false`.
     */
    public function offer(mixed $element): bool;

    /**
     * Retrieves, but does not remove, the head of this queue, or returns `null`
     * if this queue is empty.
     *
     * @see self::element()
     *
     * @return T | null the head of this queue, or `null` if this queue is empty.
     */
    public function peek(): mixed;

    /**
     * Retrieves and removes the head of this queue, or returns `null`
     * if this queue is empty.
     *
     * @see self::remove()
     *
     * @return T | null the head of this queue, or `null` if this queue is empty.
     */
    public function poll(): mixed;

    /**
     * Retrieves and removes the head of this queue.
     *
     * This method differs from `poll()` only in that it throws an exception if
     * this queue is empty.
     *
     * @see self::poll()
     *
     * @return T the head of this queue.
     *
     * @throws NoSuchElementException if this queue is empty.
     */
    public function remove(): mixed;

    /**
     * Returns the type associated with this queue.
     */
    public function getType(): string;
}
<?php

/**
 * This file is part of the ramsey/collection library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Collection;

/**
 * A set is a collection that contains no duplicate elements.
 *
 * Great care must be exercised if mutable objects are used as set elements.
 * The behavior of a set is not specified if the value of an object is changed
 * in a manner that affects equals comparisons while the object is an element in
 * the set.
 *
 * Example usage:
 *
 * ```
 * $foo = new \My\Foo();
 * $set = new Set(\My\Foo::class);
 *
 * $set->add($foo); // returns TRUE, the element doesn't exist
 * $set->add($foo); // returns FALSE, the element already exists
 *
 * $bar = new \My\Foo();
 * $set->add($bar); // returns TRUE, $bar !== $foo
 * ```
 *
 * @template T
 * @extends AbstractSet<T>
 */
class Set extends AbstractSet
{
    /**
     * Constructs a set object of the specified type, optionally with the
     * specified data.
     *
     * @param string $setType The type or class name associated with this set.
     * @param array<array-key, T> $data The initial items to store in the set.
     */
    public function __construct(private readonly string $setType, array $data = [])
    {
        parent::__construct($data);
    }

    public function getType(): string
    {
        return $this->setType;
    }
}
<?php

/**
 * This file is part of the ramsey/collection library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Collection;

use Ramsey\Collection\Exception\NoSuchElementException;
use RuntimeException;

/**
 * A linear collection that supports element insertion and removal at both ends.
 *
 * Most `DoubleEndedQueueInterface` implementations place no fixed limits on the
 * number of elements they may contain, but this interface supports
 * capacity-restricted double-ended queues as well as those with no fixed size
 * limit.
 *
 * This interface defines methods to access the elements at both ends of the
 * double-ended queue. Methods are provided to insert, remove, and examine the
 * element. Each of these methods exists in two forms: one throws an exception
 * if the operation fails, the other returns a special value (either `null` or
 * `false`, depending on the operation). The latter form of the insert operation
 * is designed specifically for use with capacity-restricted implementations; in
 * most implementations, insert operations cannot fail.
 *
 * The twelve methods described above are summarized in the following table:
 *
 * <table>
 * <caption>Summary of DoubleEndedQueueInterface methods</caption>
 * <thead>
 * <tr>
 * <th></th>
 * <th colspan=2>First Element (Head)</th>
 * <th colspan=2>Last Element (Tail)</th>
 * </tr>
 * <tr>
 * <td></td>
 * <td><em>Throws exception</em></td>
 * <td><em>Special value</em></td>
 * <td><em>Throws exception</em></td>
 * <td><em>Special value</em></td>
 * </tr>
 * </thead>
 * <tbody>
 * <tr>
 * <th>Insert</th>
 * <td><code>addFirst()</code></td>
 * <td><code>offerFirst()</code></td>
 * <td><code>addLast()</code></td>
 * <td><code>offerLast()</code></td>
 * </tr>
 * <tr>
 * <th>Remove</th>
 * <td><code>removeFirst()</code></td>
 * <td><code>pollFirst()</code></td>
 * <td><code>removeLast()</code></td>
 * <td><code>pollLast()</code></td>
 * </tr>
 * <tr>
 * <th>Examine</th>
 * <td><code>firstElement()</code></td>
 * <td><code>peekFirst()</code></td>
 * <td><code>lastElement()</code></td>
 * <td><code>peekLast()</code></td>
 * </tr>
 * </tbody>
 * </table>
 *
 * This interface extends the `QueueInterface`. When a double-ended queue is
 * used as a queue, FIFO (first-in-first-out) behavior results. Elements are
 * added at the end of the double-ended queue and removed from the beginning.
 * The methods inherited from the `QueueInterface` are precisely equivalent to
 * `DoubleEndedQueueInterface` methods as indicated in the following table:
 *
 * <table>
 * <caption>Comparison of QueueInterface and DoubleEndedQueueInterface methods</caption>
 * <thead>
 * <tr>
 * <th>QueueInterface Method</th>
 * <th>DoubleEndedQueueInterface Method</th>
 * </tr>
 * </thead>
 * <tbody>
 * <tr>
 * <td><code>add()</code></td>
 * <td><code>addLast()</code></td>
 * </tr>
 * <tr>
 * <td><code>offer()</code></td>
 * <td><code>offerLast()</code></td>
 * </tr>
 * <tr>
 * <td><code>remove()</code></td>
 * <td><code>removeFirst()</code></td>
 * </tr>
 * <tr>
 * <td><code>poll()</code></td>
 * <td><code>pollFirst()</code></td>
 * </tr>
 * <tr>
 * <td><code>element()</code></td>
 * <td><code>firstElement()</code></td>
 * </tr>
 * <tr>
 * <td><code>peek()</code></td>
 * <td><code>peekFirst()</code></td>
 * </tr>
 * </tbody>
 * </table>
 *
 * Double-ended queues can also be used as LIFO (last-in-first-out) stacks. When
 * a double-ended queue is used as a stack, elements are pushed and popped from
 * the beginning of the double-ended queue. Stack concepts are precisely
 * equivalent to `DoubleEndedQueueInterface` methods as indicated in the table
 * below:
 *
 * <table>
 * <caption>Comparison of stack concepts and DoubleEndedQueueInterface methods</caption>
 * <thead>
 * <tr>
 * <th>Stack concept</th>
 * <th>DoubleEndedQueueInterface Method</th>
 * </tr>
 * </thead>
 * <tbody>
 * <tr>
 * <td><em>push</em></td>
 * <td><code>addFirst()</code></td>
 * </tr>
 * <tr>
 * <td><em>pop</em></td>
 * <td><code>removeFirst()</code></td>
 * </tr>
 * <tr>
 * <td><em>peek</em></td>
 * <td><code>peekFirst()</code></td>
 * </tr>
 * </tbody>
 * </table>
 *
 * Note that the `peek()` method works equally well when a double-ended queue is
 * used as a queue or a stack; in either case, elements are drawn from the
 * beginning of the double-ended queue.
 *
 * While `DoubleEndedQueueInterface` implementations are not strictly required
 * to prohibit the insertion of `null` elements, they are strongly encouraged to
 * do so. Users of any `DoubleEndedQueueInterface` implementations that do allow
 * `null` elements are strongly encouraged *not* to take advantage of the
 * ability to insert nulls. This is so because `null` is used as a special
 * return value by various methods to indicated that the double-ended queue is
 * empty.
 *
 * @template T
 * @extends QueueInterface<T>
 */
interface DoubleEndedQueueInterface extends QueueInterface
{
    /**
     * Inserts the specified element at the front of this queue if it is
     * possible to do so immediately without violating capacity restrictions.
     *
     * When using a capacity-restricted double-ended queue, it is generally
     * preferable to use the `offerFirst()` method.
     *
     * @param T $element The element to add to the front of this queue.
     *
     * @return bool `true` if this queue changed as a result of the call.
     *
     * @throws RuntimeException if a queue refuses to add a particular element
     *     for any reason other than that it already contains the element.
     *     Implementations should use a more-specific exception that extends
     *     `\RuntimeException`.
     */
    public function addFirst(mixed $element): bool;

    /**
     * Inserts the specified element at the end of this queue if it is possible
     * to do so immediately without violating capacity restrictions.
     *
     * When using a capacity-restricted double-ended queue, it is generally
     * preferable to use the `offerLast()` method.
     *
     * This method is equivalent to `add()`.
     *
     * @param T $element The element to add to the end of this queue.
     *
     * @return bool `true` if this queue changed as a result of the call.
     *
     * @throws RuntimeException if a queue refuses to add a particular element
     *     for any reason other than that it already contains the element.
     *     Implementations should use a more-specific exception that extends
     *     `\RuntimeException`.
     */
    public function addLast(mixed $element): bool;

    /**
     * Inserts the specified element at the front of this queue if it is
     * possible to do so immediately without violating capacity restrictions.
     *
     * When using a capacity-restricted queue, this method is generally
     * preferable to `addFirst()`, which can fail to insert an element only by
     * throwing an exception.
     *
     * @param T $element The element to add to the front of this queue.
     *
     * @return bool `true` if the element was added to this queue, else `false`.
     */
    public function offerFirst(mixed $element): bool;

    /**
     * Inserts the specified element at the end of this queue if it is possible
     * to do so immediately without violating capacity restrictions.
     *
     * When using a capacity-restricted queue, this method is generally
     * preferable to `addLast()` which can fail to insert an element only by
     * throwing an exception.
     *
     * @param T $element The element to add to the end of this queue.
     *
     * @return bool `true` if the element was added to this queue, else `false`.
     */
    public function offerLast(mixed $element): bool;

    /**
     * Retrieves and removes the head of this queue.
     *
     * This method differs from `pollFirst()` only in that it throws an
     * exception if this queue is empty.
     *
     * @return T the first element in this queue.
     *
     * @throws NoSuchElementException if this queue is empty.
     */
    public function removeFirst(): mixed;

    /**
     * Retrieves and removes the tail of this queue.
     *
     * This method differs from `pollLast()` only in that it throws an exception
     * if this queue is empty.
     *
     * @return T the last element in this queue.
     *
     * @throws NoSuchElementException if this queue is empty.
     */
    public function removeLast(): mixed;

    /**
     * Retrieves and removes the head of this queue, or returns `null` if this
     * queue is empty.
     *
     * @return T | null the head of this queue, or `null` if this queue is empty.
     */
    public function pollFirst(): mixed;

    /**
     * Retrieves and removes the tail of this queue, or returns `null` if this
     * queue is empty.
     *
     * @return T | null the tail of this queue, or `null` if this queue is empty.
     */
    public function pollLast(): mixed;

    /**
     * Retrieves, but does not remove, the head of this queue.
     *
     * This method differs from `peekFirst()` only in that it throws an
     * exception if this queue is empty.
     *
     * @return T the head of this queue.
     *
     * @throws NoSuchElementException if this queue is empty.
     */
    public function firstElement(): mixed;

    /**
     * Retrieves, but does not remove, the tail of this queue.
     *
     * This method differs from `peekLast()` only in that it throws an exception
     * if this queue is empty.
     *
     * @return T the tail of this queue.
     *
     * @throws NoSuchElementException if this queue is empty.
     */
    public function lastElement(): mixed;

    /**
     * Retrieves, but does not remove, the head of this queue, or returns `null`
     * if this queue is empty.
     *
     * @return T | null the head of this queue, or `null` if this queue is empty.
     */
    public function peekFirst(): mixed;

    /**
     * Retrieves, but does not remove, the tail of this queue, or returns `null`
     * if this queue is empty.
     *
     * @return T | null the tail of this queue, or `null` if this queue is empty.
     */
    public function peekLast(): mixed;
}
<?php

/**
 * This file is part of the ramsey/collection library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Collection;

use Ramsey\Collection\Exception\InvalidArgumentException;
use Ramsey\Collection\Exception\NoSuchElementException;

use function array_key_last;
use function array_pop;
use function array_unshift;

/**
 * This class provides a basic implementation of `DoubleEndedQueueInterface`, to
 * minimize the effort required to implement this interface.
 *
 * @template T
 * @extends Queue<T>
 * @implements DoubleEndedQueueInterface<T>
 */
class DoubleEndedQueue extends Queue implements DoubleEndedQueueInterface
{
    /**
     * Constructs a double-ended queue (dequeue) object of the specified type,
     * optionally with the specified data.
     *
     * @param string $queueType The type or class name associated with this dequeue.
     * @param array<array-key, T> $data The initial items to store in the dequeue.
     */
    public function __construct(private readonly string $queueType, array $data = [])
    {
        parent::__construct($this->queueType, $data);
    }

    /**
     * @throws InvalidArgumentException if $element is of the wrong type
     */
    public function addFirst(mixed $element): bool
    {
        if ($this->checkType($this->getType(), $element) === false) {
            throw new InvalidArgumentException(
                'Value must be of type ' . $this->getType() . '; value is '
                . $this->toolValueToString($element),
            );
        }

        array_unshift($this->data, $element);

        return true;
    }

    /**
     * @throws InvalidArgumentException if $element is of the wrong type
     */
    public function addLast(mixed $element): bool
    {
        return $this->add($element);
    }

    public function offerFirst(mixed $element): bool
    {
        try {
            return $this->addFirst($element);
        } catch (InvalidArgumentException) {
            return false;
        }
    }

    public function offerLast(mixed $element): bool
    {
        return $this->offer($element);
    }

    /**
     * @return T the first element in this queue.
     *
     * @throws NoSuchElementException if the queue is empty
     */
    public function removeFirst(): mixed
    {
        return $this->remove();
    }

    /**
     * @return T the last element in this queue.
     *
     * @throws NoSuchElementException if this queue is empty.
     */
    public function removeLast(): mixed
    {
        return $this->pollLast() ?? throw new NoSuchElementException(
            'Can\'t return element from Queue. Queue is empty.',
        );
    }

    /**
     * @return T | null the head of this queue, or `null` if this queue is empty.
     */
    public function pollFirst(): mixed
    {
        return $this->poll();
    }

    /**
     * @return T | null the tail of this queue, or `null` if this queue is empty.
     */
    public function pollLast(): mixed
    {
        return array_pop($this->data);
    }

    /**
     * @return T the head of this queue.
     *
     * @throws NoSuchElementException if this queue is empty.
     */
    public function firstElement(): mixed
    {
        return $this->element();
    }

    /**
     * @return T the tail of this queue.
     *
     * @throws NoSuchElementException if this queue is empty.
     */
    public function lastElement(): mixed
    {
        return $this->peekLast() ?? throw new NoSuchElementException(
            'Can\'t return element from Queue. Queue is empty.',
        );
    }

    /**
     * @return T | null the head of this queue, or `null` if this queue is empty.
     */
    public function peekFirst(): mixed
    {
        return $this->peek();
    }

    /**
     * @return T | null the tail of this queue, or `null` if this queue is empty.
     */
    public function peekLast(): mixed
    {
        $lastIndex = array_key_last($this->data);

        if ($lastIndex === null) {
            return null;
        }

        return $this->data[$lastIndex];
    }
}
<?php

/**
 * This file is part of the ramsey/collection library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Collection\Exception;

use OutOfBoundsException as PhpOutOfBoundsException;

/**
 * Thrown when attempting to access an element out of the range of the collection.
 */
class OutOfBoundsException extends PhpOutOfBoundsException implements CollectionException
{
}
<?php

/**
 * This file is part of the ramsey/collection library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Collection\Exception;

use InvalidArgumentException as PhpInvalidArgumentException;

/**
 * Thrown to indicate an argument is not of the expected type.
 */
class InvalidArgumentException extends PhpInvalidArgumentException implements CollectionException
{
}
<?php

/**
 * This file is part of the ramsey/collection library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Collection\Exception;

use RuntimeException;

/**
 * Thrown when attempting to access an element that does not exist.
 */
class NoSuchElementException extends RuntimeException implements CollectionException
{
}
<?php

/**
 * This file is part of the ramsey/collection library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Collection\Exception;

use RuntimeException;

/**
 * Thrown to indicate that the requested operation is not supported.
 */
class UnsupportedOperationException extends RuntimeException implements CollectionException
{
}
<?php

/**
 * This file is part of the ramsey/collection library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Collection\Exception;

use RuntimeException;

/**
 * Thrown when attempting to operate on collections of differing types.
 */
class CollectionMismatchException extends RuntimeException implements CollectionException
{
}
<?php

/**
 * This file is part of the ramsey/collection library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Collection\Exception;

use RuntimeException;

/**
 * Thrown when attempting to evaluate a property, method, or array key
 * that doesn't exist on an element or cannot otherwise be evaluated in the
 * current context.
 */
class InvalidPropertyOrMethod extends RuntimeException implements CollectionException
{
}
<?php

/**
 * This file is part of the ramsey/collection library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Collection\Exception;

use Throwable;

interface CollectionException extends Throwable
{
}
<?php

/**
 * This file is part of the ramsey/collection library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Collection;

use Ramsey\Collection\Exception\InvalidArgumentException;
use Ramsey\Collection\Exception\NoSuchElementException;
use Ramsey\Collection\Tool\TypeTrait;
use Ramsey\Collection\Tool\ValueToStringTrait;

use function array_key_first;

/**
 * This class provides a basic implementation of `QueueInterface`, to minimize
 * the effort required to implement this interface.
 *
 * @template T
 * @extends AbstractArray<T>
 * @implements QueueInterface<T>
 */
class Queue extends AbstractArray implements QueueInterface
{
    use TypeTrait;
    use ValueToStringTrait;

    /**
     * Constructs a queue object of the specified type, optionally with the
     * specified data.
     *
     * @param string $queueType The type or class name associated with this queue.
     * @param array<array-key, T> $data The initial items to store in the queue.
     */
    public function __construct(private readonly string $queueType, array $data = [])
    {
        parent::__construct($data);
    }

    /**
     * {@inheritDoc}
     *
     * Since arbitrary offsets may not be manipulated in a queue, this method
     * serves only to fulfill the `ArrayAccess` interface requirements. It is
     * invoked by other operations when adding values to the queue.
     *
     * @throws InvalidArgumentException if $value is of the wrong type.
     */
    public function offsetSet(mixed $offset, mixed $value): void
    {
        if ($this->checkType($this->getType(), $value) === false) {
            throw new InvalidArgumentException(
                'Value must be of type ' . $this->getType() . '; value is '
                . $this->toolValueToString($value),
            );
        }

        $this->data[] = $value;
    }

    /**
     * @throws InvalidArgumentException if $value is of the wrong type.
     */
    public function add(mixed $element): bool
    {
        $this[] = $element;

        return true;
    }

    /**
     * @return T
     *
     * @throws NoSuchElementException if this queue is empty.
     */
    public function element(): mixed
    {
        return $this->peek() ?? throw new NoSuchElementException(
            'Can\'t return element from Queue. Queue is empty.',
        );
    }

    public function offer(mixed $element): bool
    {
        try {
            return $this->add($element);
        } catch (InvalidArgumentException) {
            return false;
        }
    }

    /**
     * @return T | null
     */
    public function peek(): mixed
    {
        $index = array_key_first($this->data);

        if ($index === null) {
            return null;
        }

        return $this[$index];
    }

    /**
     * @return T | null
     */
    public function poll(): mixed
    {
        $index = array_key_first($this->data);

        if ($index === null) {
            return null;
        }

        $head = $this[$index];
        unset($this[$index]);

        return $head;
    }

    /**
     * @return T
     *
     * @throws NoSuchElementException if this queue is empty.
     */
    public function remove(): mixed
    {
        return $this->poll() ?? throw new NoSuchElementException(
            'Can\'t return element from Queue. Queue is empty.',
        );
    }

    public function getType(): string
    {
        return $this->queueType;
    }
}
<?php

/**
 * This file is part of the ramsey/collection library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Collection\Tool;

use function is_array;
use function is_bool;
use function is_callable;
use function is_float;
use function is_int;
use function is_numeric;
use function is_object;
use function is_resource;
use function is_scalar;
use function is_string;

/**
 * Provides functionality to check values for specific types.
 */
trait TypeTrait
{
    /**
     * Returns `true` if value is of the specified type.
     *
     * @param string $type The type to check the value against.
     * @param mixed $value The value to check.
     */
    protected function checkType(string $type, mixed $value): bool
    {
        return match ($type) {
            'array' => is_array($value),
            'bool', 'boolean' => is_bool($value),
            'callable' => is_callable($value),
            'float', 'double' => is_float($value),
            'int', 'integer' => is_int($value),
            'null' => $value === null,
            'numeric' => is_numeric($value),
            'object' => is_object($value),
            'resource' => is_resource($value),
            'scalar' => is_scalar($value),
            'string' => is_string($value),
            'mixed' => true,
            default => $value instanceof $type,
        };
    }
}
<?php

/**
 * This file is part of the ramsey/collection library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Collection\Tool;

use DateTimeInterface;

use function assert;
use function get_resource_type;
use function is_array;
use function is_bool;
use function is_callable;
use function is_object;
use function is_resource;
use function is_scalar;

/**
 * Provides functionality to express a value as string
 */
trait ValueToStringTrait
{
    /**
     * Returns a string representation of the value.
     *
     * - null value: `'NULL'`
     * - boolean: `'TRUE'`, `'FALSE'`
     * - array: `'Array'`
     * - scalar: converted-value
     * - resource: `'(type resource #number)'`
     * - object with `__toString()`: result of `__toString()`
     * - object DateTime: ISO 8601 date
     * - object: `'(className Object)'`
     * - anonymous function: same as object
     *
     * @param mixed $value the value to return as a string.
     */
    protected function toolValueToString(mixed $value): string
    {
        // null
        if ($value === null) {
            return 'NULL';
        }

        // boolean constants
        if (is_bool($value)) {
            return $value ? 'TRUE' : 'FALSE';
        }

        // array
        if (is_array($value)) {
            return 'Array';
        }

        // scalar types (integer, float, string)
        if (is_scalar($value)) {
            return (string) $value;
        }

        // resource
        if (is_resource($value)) {
            return '(' . get_resource_type($value) . ' resource #' . (int) $value . ')';
        }

        // From here, $value should be an object.
        assert(is_object($value));

        // __toString() is implemented
        if (is_callable([$value, '__toString'])) {
            /** @var string */
            return $value->__toString();
        }

        // object of type \DateTime
        if ($value instanceof DateTimeInterface) {
            return $value->format('c');
        }

        // unknown type
        return '(' . $value::class . ' Object)';
    }
}
<?php

/**
 * This file is part of the ramsey/collection library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Collection\Tool;

use Ramsey\Collection\Exception\InvalidPropertyOrMethod;
use Ramsey\Collection\Exception\UnsupportedOperationException;
use ReflectionProperty;

use function is_array;
use function is_object;
use function method_exists;
use function property_exists;
use function sprintf;

/**
 * Provides functionality to extract the value of a property or method from an object.
 */
trait ValueExtractorTrait
{
    /**
     * Returns the type associated with this collection.
     */
    abstract public function getType(): string;

    /**
     * Extracts the value of the given property, method, or array key from the
     * element.
     *
     * If `$propertyOrMethod` is `null`, we return the element as-is.
     *
     * @param mixed $element The element to extract the value from.
     * @param string | null $propertyOrMethod The property or method for which the
     *     value should be extracted.
     *
     * @return mixed the value extracted from the specified property, method,
     *     or array key, or the element itself.
     *
     * @throws InvalidPropertyOrMethod
     * @throws UnsupportedOperationException
     */
    protected function extractValue(mixed $element, ?string $propertyOrMethod): mixed
    {
        if ($propertyOrMethod === null) {
            return $element;
        }

        if (!is_object($element) && !is_array($element)) {
            throw new UnsupportedOperationException(sprintf(
                'The collection type "%s" does not support the $propertyOrMethod parameter',
                $this->getType(),
            ));
        }

        if (is_array($element)) {
            return $element[$propertyOrMethod] ?? throw new InvalidPropertyOrMethod(sprintf(
                'Key or index "%s" not found in collection elements',
                $propertyOrMethod,
            ));
        }

        if (property_exists($element, $propertyOrMethod) && method_exists($element, $propertyOrMethod)) {
            $reflectionProperty = new ReflectionProperty($element, $propertyOrMethod);
            if ($reflectionProperty->isPublic()) {
                return $element->$propertyOrMethod;
            }

            return $element->{$propertyOrMethod}();
        }

        if (property_exists($element, $propertyOrMethod)) {
            return $element->$propertyOrMethod;
        }

        if (method_exists($element, $propertyOrMethod)) {
            return $element->{$propertyOrMethod}();
        }

        if (isset($element->$propertyOrMethod)) {
            return $element->$propertyOrMethod;
        }

        throw new InvalidPropertyOrMethod(sprintf(
            'Method or property "%s" not defined in %s',
            $propertyOrMethod,
            $element::class,
        ));
    }
}
<?php

/**
 * This file is part of the ramsey/collection library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Collection;

use ArrayAccess;
use Countable;
use IteratorAggregate;

/**
 * `ArrayInterface` provides traversable array functionality to data types.
 *
 * @template T
 * @extends ArrayAccess<array-key, T>
 * @extends IteratorAggregate<array-key, T>
 */
interface ArrayInterface extends
    ArrayAccess,
    Countable,
    IteratorAggregate
{
    /**
     * Removes all items from this array.
     */
    public function clear(): void;

    /**
     * Returns a native PHP array representation of this array object.
     *
     * @return array<array-key, T>
     */
    public function toArray(): array;

    /**
     * Returns `true` if this array is empty.
     */
    public function isEmpty(): bool;
}
<?php

/**
 * This file is part of the ramsey/collection library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Collection\Map;

use Ramsey\Collection\AbstractArray;
use Ramsey\Collection\Exception\InvalidArgumentException;
use Traversable;

use function array_key_exists;
use function array_keys;
use function in_array;
use function var_export;

/**
 * This class provides a basic implementation of `MapInterface`, to minimize the
 * effort required to implement this interface.
 *
 * @template K of array-key
 * @template T
 * @extends AbstractArray<T>
 * @implements MapInterface<K, T>
 */
abstract class AbstractMap extends AbstractArray implements MapInterface
{
    /**
     * @param array<K, T> $data The initial items to add to this map.
     */
    public function __construct(array $data = [])
    {
        parent::__construct($data);
    }

    /**
     * @return Traversable<K, T>
     */
    public function getIterator(): Traversable
    {
        return parent::getIterator();
    }

    /**
     * @param K $offset The offset to set
     * @param T $value The value to set at the given offset.
     *
     * @inheritDoc
     */
    public function offsetSet(mixed $offset, mixed $value): void
    {
        if ($offset === null) {
            throw new InvalidArgumentException(
                'Map elements are key/value pairs; a key must be provided for '
                . 'value ' . var_export($value, true),
            );
        }

        $this->data[$offset] = $value;
    }

    public function containsKey(int | string $key): bool
    {
        return array_key_exists($key, $this->data);
    }

    public function containsValue(mixed $value): bool
    {
        return in_array($value, $this->data, true);
    }

    /**
     * @inheritDoc
     */
    public function keys(): array
    {
        /** @var list<K> */
        return array_keys($this->data);
    }

    /**
     * @param K $key The key to return from the map.
     * @param T | null $defaultValue The default value to use if `$key` is not found.
     *
     * @return T | null the value or `null` if the key could not be found.
     */
    public function get(int | string $key, mixed $defaultValue = null): mixed
    {
        return $this[$key] ?? $defaultValue;
    }

    /**
     * @param K $key The key to put or replace in the map.
     * @param T $value The value to store at `$key`.
     *
     * @return T | null the previous value associated with key, or `null` if
     *     there was no mapping for `$key`.
     */
    public function put(int | string $key, mixed $value): mixed
    {
        $previousValue = $this->get($key);
        $this[$key] = $value;

        return $previousValue;
    }

    /**
     * @param K $key The key to put in the map.
     * @param T $value The value to store at `$key`.
     *
     * @return T | null the previous value associated with key, or `null` if
     *     there was no mapping for `$key`.
     */
    public function putIfAbsent(int | string $key, mixed $value): mixed
    {
        $currentValue = $this->get($key);

        if ($currentValue === null) {
            $this[$key] = $value;
        }

        return $currentValue;
    }

    /**
     * @param K $key The key to remove from the map.
     *
     * @return T | null the previous value associated with key, or `null` if
     *     there was no mapping for `$key`.
     */
    public function remove(int | string $key): mixed
    {
        $previousValue = $this->get($key);
        unset($this[$key]);

        return $previousValue;
    }

    public function removeIf(int | string $key, mixed $value): bool
    {
        if ($this->get($key) === $value) {
            unset($this[$key]);

            return true;
        }

        return false;
    }

    /**
     * @param K $key The key to replace.
     * @param T $value The value to set at `$key`.
     *
     * @return T | null the previous value associated with key, or `null` if
     *     there was no mapping for `$key`.
     */
    public function replace(int | string $key, mixed $value): mixed
    {
        $currentValue = $this->get($key);

        if ($this->containsKey($key)) {
            $this[$key] = $value;
        }

        return $currentValue;
    }

    public function replaceIf(int | string $key, mixed $oldValue, mixed $newValue): bool
    {
        if ($this->get($key) === $oldValue) {
            $this[$key] = $newValue;

            return true;
        }

        return false;
    }

    /**
     * @return array<K, T>
     */
    public function __serialize(): array
    {
        /** @var array<K, T> */
        return parent::__serialize();
    }

    /**
     * @return array<K, T>
     */
    public function toArray(): array
    {
        /** @var array<K, T> */
        return parent::toArray();
    }
}
<?php

/**
 * This file is part of the ramsey/collection library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Collection\Map;

/**
 * A `TypedMapInterface` represents a map of elements where key and value are
 * typed.
 *
 * @template K of array-key
 * @template T
 * @extends MapInterface<K, T>
 */
interface TypedMapInterface extends MapInterface
{
    /**
     * Return the type used on the key.
     */
    public function getKeyType(): string;

    /**
     * Return the type forced on the values.
     */
    public function getValueType(): string;
}
<?php

/**
 * This file is part of the ramsey/collection library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Collection\Map;

/**
 * A `TypedMap` represents a map of elements where key and value are typed.
 *
 * Each element is identified by a key with defined type and a value of defined
 * type. The keys of the map must be unique. The values on the map can be
 * repeated but each with its own different key.
 *
 * The most common case is to use a string type key, but it's not limited to
 * this type of keys.
 *
 * This is a direct implementation of `TypedMapInterface`, provided for the sake
 * of convenience.
 *
 * Example usage:
 *
 * ```
 * $map = new TypedMap('string', Foo::class);
 * $map['x'] = new Foo();
 * foreach ($map as $key => $value) {
 *     // do something with $key, it will be a Foo::class
 * }
 *
 * // this will throw an exception since key must be string
 * $map[10] = new Foo();
 *
 * // this will throw an exception since value must be a Foo
 * $map['bar'] = 'bar';
 *
 * // initialize map with contents
 * $map = new TypedMap('string', Foo::class, [
 *     new Foo(), new Foo(), new Foo()
 * ]);
 * ```
 *
 * It is preferable to subclass `AbstractTypedMap` to create your own typed map
 * implementation:
 *
 * ```
 * class FooTypedMap extends AbstractTypedMap
 * {
 *     public function getKeyType()
 *     {
 *         return 'int';
 *     }
 *
 *     public function getValueType()
 *     {
 *          return Foo::class;
 *     }
 * }
 * ```
 *
 * â€¦ but you also may use the `TypedMap` class:
 *
 * ```
 * class FooTypedMap extends TypedMap
 * {
 *     public function __constructor(array $data = [])
 *     {
 *         parent::__construct('int', Foo::class, $data);
 *     }
 * }
 * ```
 *
 * @template K of array-key
 * @template T
 * @extends AbstractTypedMap<K, T>
 */
class TypedMap extends AbstractTypedMap
{
    /**
     * Constructs a map object of the specified key and value types,
     * optionally with the specified data.
     *
     * @param string $keyType The data type of the map's keys.
     * @param string $valueType The data type of the map's values.
     * @param array<K, T> $data The initial data to set for this map.
     */
    public function __construct(
        private readonly string $keyType,
        private readonly string $valueType,
        array $data = [],
    ) {
        parent::__construct($data);
    }

    public function getKeyType(): string
    {
        return $this->keyType;
    }

    public function getValueType(): string
    {
        return $this->valueType;
    }
}
<?php

/**
 * This file is part of the ramsey/collection library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Collection\Map;

/**
 * `AssociativeArrayMap` represents a standard associative array object.
 *
 * @extends AbstractMap<string, mixed>
 */
class AssociativeArrayMap extends AbstractMap
{
}
<?php

/**
 * This file is part of the ramsey/collection library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Collection\Map;

use Ramsey\Collection\ArrayInterface;

/**
 * An object that maps keys to values.
 *
 * A map cannot contain duplicate keys; each key can map to at most one value.
 *
 * @template K of array-key
 * @template T
 * @extends ArrayInterface<T>
 */
interface MapInterface extends ArrayInterface
{
    /**
     * Returns `true` if this map contains a mapping for the specified key.
     *
     * @param K $key The key to check in the map.
     */
    public function containsKey(int | string $key): bool;

    /**
     * Returns `true` if this map maps one or more keys to the specified value.
     *
     * This performs a strict type check on the value.
     *
     * @param T $value The value to check in the map.
     */
    public function containsValue(mixed $value): bool;

    /**
     * Return an array of the keys contained in this map.
     *
     * @return list<K>
     */
    public function keys(): array;

    /**
     * Returns the value to which the specified key is mapped, `null` if this
     * map contains no mapping for the key, or (optionally) `$defaultValue` if
     * this map contains no mapping for the key.
     *
     * @param K $key The key to return from the map.
     * @param T | null $defaultValue The default value to use if `$key` is not found.
     *
     * @return T | null the value or `null` if the key could not be found.
     */
    public function get(int | string $key, mixed $defaultValue = null): mixed;

    /**
     * Associates the specified value with the specified key in this map.
     *
     * If the map previously contained a mapping for the key, the old value is
     * replaced by the specified value.
     *
     * @param K $key The key to put or replace in the map.
     * @param T $value The value to store at `$key`.
     *
     * @return T | null the previous value associated with key, or `null` if
     *     there was no mapping for `$key`.
     */
    public function put(int | string $key, mixed $value): mixed;

    /**
     * Associates the specified value with the specified key in this map only if
     * it is not already set.
     *
     * If there is already a value associated with `$key`, this returns that
     * value without replacing it.
     *
     * @param K $key The key to put in the map.
     * @param T $value The value to store at `$key`.
     *
     * @return T | null the previous value associated with key, or `null` if
     *     there was no mapping for `$key`.
     */
    public function putIfAbsent(int | string $key, mixed $value): mixed;

    /**
     * Removes the mapping for a key from this map if it is present.
     *
     * @param K $key The key to remove from the map.
     *
     * @return T | null the previous value associated with key, or `null` if
     *     there was no mapping for `$key`.
     */
    public function remove(int | string $key): mixed;

    /**
     * Removes the entry for the specified key only if it is currently mapped to
     * the specified value.
     *
     * This performs a strict type check on the value.
     *
     * @param K $key The key to remove from the map.
     * @param T $value The value to match.
     *
     * @return bool true if the value was removed.
     */
    public function removeIf(int | string $key, mixed $value): bool;

    /**
     * Replaces the entry for the specified key only if it is currently mapped
     * to some value.
     *
     * @param K $key The key to replace.
     * @param T $value The value to set at `$key`.
     *
     * @return T | null the previous value associated with key, or `null` if
     *     there was no mapping for `$key`.
     */
    public function replace(int | string $key, mixed $value): mixed;

    /**
     * Replaces the entry for the specified key only if currently mapped to the
     * specified value.
     *
     * This performs a strict type check on the value.
     *
     * @param K $key The key to remove from the map.
     * @param T $oldValue The value to match.
     * @param T $newValue The value to use as a replacement.
     *
     * @return bool true if the value was replaced.
     */
    public function replaceIf(int | string $key, mixed $oldValue, mixed $newValue): bool;
}
<?php

/**
 * This file is part of the ramsey/collection library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Collection\Map;

use Ramsey\Collection\Exception\InvalidArgumentException;
use Ramsey\Collection\Tool\TypeTrait;
use Ramsey\Collection\Tool\ValueToStringTrait;

use function array_combine;
use function array_key_exists;
use function is_int;

/**
 * `NamedParameterMap` represents a mapping of values to a set of named keys
 * that may optionally be typed
 *
 * @extends AbstractMap<string, mixed>
 */
class NamedParameterMap extends AbstractMap
{
    use TypeTrait;
    use ValueToStringTrait;

    /**
     * Named parameters defined for this map.
     *
     * @var array<string, string>
     */
    private readonly array $namedParameters;

    /**
     * Constructs a new `NamedParameterMap`.
     *
     * @param array<array-key, string> $namedParameters The named parameters defined for this map.
     * @param array<string, mixed> $data An initial set of data to set on this map.
     */
    public function __construct(array $namedParameters, array $data = [])
    {
        $this->namedParameters = $this->filterNamedParameters($namedParameters);
        parent::__construct($data);
    }

    /**
     * Returns named parameters set for this `NamedParameterMap`.
     *
     * @return array<string, string>
     */
    public function getNamedParameters(): array
    {
        return $this->namedParameters;
    }

    public function offsetSet(mixed $offset, mixed $value): void
    {
        if (!array_key_exists($offset, $this->namedParameters)) {
            throw new InvalidArgumentException(
                'Attempting to set value for unconfigured parameter \''
                . $this->toolValueToString($offset) . '\'',
            );
        }

        if ($this->checkType($this->namedParameters[$offset], $value) === false) {
            throw new InvalidArgumentException(
                'Value for \'' . $offset . '\' must be of type '
                . $this->namedParameters[$offset] . '; value is '
                . $this->toolValueToString($value),
            );
        }

        $this->data[$offset] = $value;
    }

    /**
     * Given an array of named parameters, constructs a proper mapping of
     * named parameters to types.
     *
     * @param array<array-key, string> $namedParameters The named parameters to filter.
     *
     * @return array<string, string>
     */
    protected function filterNamedParameters(array $namedParameters): array
    {
        $names = [];
        $types = [];

        foreach ($namedParameters as $key => $value) {
            if (is_int($key)) {
                $names[] = $value;
                $types[] = 'mixed';
            } else {
                $names[] = $key;
                $types[] = $value;
            }
        }

        return array_combine($names, $types) ?: [];
    }
}
<?php

/**
 * This file is part of the ramsey/collection library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Collection\Map;

use Ramsey\Collection\Exception\InvalidArgumentException;
use Ramsey\Collection\Tool\TypeTrait;
use Ramsey\Collection\Tool\ValueToStringTrait;

/**
 * This class provides a basic implementation of `TypedMapInterface`, to
 * minimize the effort required to implement this interface.
 *
 * @template K of array-key
 * @template T
 * @extends AbstractMap<K, T>
 * @implements TypedMapInterface<K, T>
 */
abstract class AbstractTypedMap extends AbstractMap implements TypedMapInterface
{
    use TypeTrait;
    use ValueToStringTrait;

    /**
     * @param K $offset
     * @param T $value
     *
     * @inheritDoc
     */
    public function offsetSet(mixed $offset, mixed $value): void
    {
        if ($this->checkType($this->getKeyType(), $offset) === false) {
            throw new InvalidArgumentException(
                'Key must be of type ' . $this->getKeyType() . '; key is '
                . $this->toolValueToString($offset),
            );
        }

        if ($this->checkType($this->getValueType(), $value) === false) {
            throw new InvalidArgumentException(
                'Value must be of type ' . $this->getValueType() . '; value is '
                . $this->toolValueToString($value),
            );
        }

        parent::offsetSet($offset, $value);
    }
}
<?php

/**
 * This file is part of the ramsey/collection library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Collection;

/**
 * Collection sorting
 */
enum Sort: string
{
    /**
     * Sort items in a collection in ascending order.
     */
    case Ascending = 'asc';

    /**
     * Sort items in a collection in descending order.
     */
    case Descending = 'desc';
}
<?php

/**
 * This file is part of the ramsey/collection library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Collection;

use Ramsey\Collection\Exception\CollectionMismatchException;
use Ramsey\Collection\Exception\InvalidArgumentException;
use Ramsey\Collection\Exception\InvalidPropertyOrMethod;
use Ramsey\Collection\Exception\NoSuchElementException;
use Ramsey\Collection\Exception\UnsupportedOperationException;

/**
 * A collection represents a group of values, known as its elements.
 *
 * Some collections allow duplicate elements and others do not. Some are ordered
 * and others unordered.
 *
 * @template T
 * @extends ArrayInterface<T>
 */
interface CollectionInterface extends ArrayInterface
{
    /**
     * Ensures that this collection contains the specified element (optional
     * operation).
     *
     * Returns `true` if this collection changed as a result of the call.
     * (Returns `false` if this collection does not permit duplicates and
     * already contains the specified element.)
     *
     * Collections that support this operation may place limitations on what
     * elements may be added to this collection. In particular, some
     * collections will refuse to add `null` elements, and others will impose
     * restrictions on the type of elements that may be added. Collection
     * classes should clearly specify in their documentation any restrictions
     * on what elements may be added.
     *
     * If a collection refuses to add a particular element for any reason other
     * than that it already contains the element, it must throw an exception
     * (rather than returning `false`). This preserves the invariant that a
     * collection always contains the specified element after this call returns.
     *
     * @param T $element The element to add to the collection.
     *
     * @return bool `true` if this collection changed as a result of the call.
     *
     * @throws InvalidArgumentException if the collection refuses to add the
     *     $element for any reason other than that it already contains the element.
     */
    public function add(mixed $element): bool;

    /**
     * Returns `true` if this collection contains the specified element.
     *
     * @param T $element The element to check whether the collection contains.
     * @param bool $strict Whether to perform a strict type check on the value.
     */
    public function contains(mixed $element, bool $strict = true): bool;

    /**
     * Returns the type associated with this collection.
     */
    public function getType(): string;

    /**
     * Removes a single instance of the specified element from this collection,
     * if it is present.
     *
     * @param T $element The element to remove from the collection.
     *
     * @return bool `true` if an element was removed as a result of this call.
     */
    public function remove(mixed $element): bool;

    /**
     * Returns the values from the given property, method, or array key.
     *
     * @param string $propertyOrMethod The name of the property, method, or
     *     array key to evaluate and return.
     *
     * @return list<mixed>
     *
     * @throws InvalidPropertyOrMethod if the $propertyOrMethod does not exist
     *     on the elements in this collection.
     * @throws UnsupportedOperationException if unable to call column() on this
     *     collection.
     */
    public function column(string $propertyOrMethod): array;

    /**
     * Returns the first item of the collection.
     *
     * @return T
     *
     * @throws NoSuchElementException if this collection is empty.
     */
    public function first(): mixed;

    /**
     * Returns the last item of the collection.
     *
     * @return T
     *
     * @throws NoSuchElementException if this collection is empty.
     */
    public function last(): mixed;

    /**
     * Sort the collection by a property, method, or array key with the given
     * sort order.
     *
     * If $propertyOrMethod is `null`, this will sort by comparing each element.
     *
     * This will always leave the original collection untouched and will return
     * a new one.
     *
     * @param string | null $propertyOrMethod The property, method, or array key
     *     to sort by.
     * @param Sort $order The sort order for the resulting collection.
     *
     * @return CollectionInterface<T>
     *
     * @throws InvalidPropertyOrMethod if the $propertyOrMethod does not exist
     *     on the elements in this collection.
     * @throws UnsupportedOperationException if unable to call sort() on this
     *     collection.
     */
    public function sort(?string $propertyOrMethod = null, Sort $order = Sort::Ascending): self;

    /**
     * Filter out items of the collection which don't match the criteria of
     * given callback.
     *
     * This will always leave the original collection untouched and will return
     * a new one.
     *
     * See the {@link http://php.net/manual/en/function.array-filter.php PHP array_filter() documentation}
     * for examples of how the `$callback` parameter works.
     *
     * @param callable(T): bool $callback A callable to use for filtering elements.
     *
     * @return CollectionInterface<T>
     */
    public function filter(callable $callback): self;

    /**
     * Create a new collection where the result of the given property, method,
     * or array key of each item in the collection equals the given value.
     *
     * This will always leave the original collection untouched and will return
     * a new one.
     *
     * @param string | null $propertyOrMethod The property, method, or array key
     *     to evaluate. If `null`, the element itself is compared to $value.
     * @param mixed $value The value to match.
     *
     * @return CollectionInterface<T>
     *
     * @throws InvalidPropertyOrMethod if the $propertyOrMethod does not exist
     *     on the elements in this collection.
     * @throws UnsupportedOperationException if unable to call where() on this
     *     collection.
     */
    public function where(?string $propertyOrMethod, mixed $value): self;

    /**
     * Apply a given callback method on each item of the collection.
     *
     * This will always leave the original collection untouched. The new
     * collection is created by mapping the callback to each item of the
     * original collection.
     *
     * See the {@link http://php.net/manual/en/function.array-map.php PHP array_map() documentation}
     * for examples of how the `$callback` parameter works.
     *
     * @param callable(T): TCallbackReturn $callback A callable to apply to each
     *     item of the collection.
     *
     * @return CollectionInterface<TCallbackReturn>
     *
     * @template TCallbackReturn
     */
    public function map(callable $callback): self;

    /**
     * Apply a given callback method on each item of the collection
     * to reduce it to a single value.
     *
     * See the {@link http://php.net/manual/en/function.array-reduce.php PHP array_reduce() documentation}
     * for examples of how the `$callback` and `$initial` parameters work.
     *
     * @param callable(TCarry, T): TCarry $callback A callable to apply to each
     *     item of the collection to reduce it to a single value.
     * @param TCarry $initial This is the initial value provided to the callback.
     *
     * @return TCarry
     *
     * @template TCarry
     */
    public function reduce(callable $callback, mixed $initial): mixed;

    /**
     * Create a new collection with divergent items between current and given
     * collection.
     *
     * @param CollectionInterface<T> $other The collection to check for divergent
     *     items.
     *
     * @return CollectionInterface<T>
     *
     * @throws CollectionMismatchException if the compared collections are of
     *     differing types.
     */
    public function diff(CollectionInterface $other): self;

    /**
     * Create a new collection with intersecting item between current and given
     * collection.
     *
     * @param CollectionInterface<T> $other The collection to check for
     *     intersecting items.
     *
     * @return CollectionInterface<T>
     *
     * @throws CollectionMismatchException if the compared collections are of
     *     differing types.
     */
    public function intersect(CollectionInterface $other): self;

    /**
     * Merge current items and items of given collections into a new one.
     *
     * @param CollectionInterface<T> ...$collections The collections to merge.
     *
     * @return CollectionInterface<T>
     *
     * @throws CollectionMismatchException if unable to merge any of the given
     *     collections or items within the given collections due to type
     *     mismatch errors.
     */
    public function merge(CollectionInterface ...$collections): self;
}
<?php

/**
 * This file is part of the ramsey/collection library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Collection;

/**
 * This class contains the basic implementation of a collection that does not
 * allow duplicated values (a set), to minimize the effort required to implement
 * this specific type of collection.
 *
 * @template T
 * @extends AbstractCollection<T>
 */
abstract class AbstractSet extends AbstractCollection
{
    public function add(mixed $element): bool
    {
        if ($this->contains($element)) {
            return false;
        }

        // Call offsetSet() on the parent instead of add(), since calling
        // parent::add() will invoke $this->offsetSet(), which will call
        // $this->contains() a second time. This can cause performance issues
        // with extremely large collections. For more information, see
        // https://github.com/ramsey/collection/issues/68.
        parent::offsetSet(null, $element);

        return true;
    }

    public function offsetSet(mixed $offset, mixed $value): void
    {
        if ($this->contains($value)) {
            return;
        }

        parent::offsetSet($offset, $value);
    }
}
<?php

/**
 * This file is part of the ramsey/collection library
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
 * @license http://opensource.org/licenses/MIT MIT
 */

declare(strict_types=1);

namespace Ramsey\Collection;

/**
 * `GenericArray` represents a standard array object.
 *
 * @extends AbstractArray<mixed>
 */
class GenericArray extends AbstractArray
{
}
{
    "name": "ramsey/collection",
    "description": "A PHP library for representing and manipulating collections.",
    "license": "MIT",
    "type": "library",
    "keywords": [
        "array",
        "collection",
        "hash",
        "map",
        "queue",
        "set"
    ],
    "authors": [
        {
            "name": "Ben Ramsey",
            "email": "ben@benramsey.com",
            "homepage": "https://benramsey.com"
        }
    ],
    "require": {
        "php": "^8.1"
    },
    "require-dev": {
        "captainhook/plugin-composer": "^5.3",
        "ergebnis/composer-normalize": "^2.45",
        "fakerphp/faker": "^1.24",
        "hamcrest/hamcrest-php": "^2.0",
        "jangregor/phpstan-prophecy": "^2.1",
        "mockery/mockery": "^1.6",
        "php-parallel-lint/php-console-highlighter": "^1.0",
        "php-parallel-lint/php-parallel-lint": "^1.4",
        "phpspec/prophecy-phpunit": "^2.3",
        "phpstan/extension-installer": "^1.4",
        "phpstan/phpstan": "^2.1",
        "phpstan/phpstan-mockery": "^2.0",
        "phpstan/phpstan-phpunit": "^2.0",
        "phpunit/phpunit": "^10.5",
        "ramsey/coding-standard": "^2.3",
        "ramsey/conventional-commits": "^1.6",
        "roave/security-advisories": "dev-latest"
    },
    "prefer-stable": true,
    "autoload": {
        "psr-4": {
            "Ramsey\\Collection\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Ramsey\\Collection\\Test\\": "tests/"
        }
    },
    "config": {
        "allow-plugins": {
            "captainhook/plugin-composer": true,
            "dealerdirect/phpcodesniffer-composer-installer": true,
            "ergebnis/composer-normalize": true,
            "phpstan/extension-installer": true
        },
        "sort-packages": true
    },
    "extra": {
        "captainhook": {
            "force-install": true
        },
        "ramsey/conventional-commits": {
            "configFile": "conventional-commits.json"
        }
    },
    "scripts": {
        "dev:analyze": [
            "@dev:analyze:phpstan"
        ],
        "dev:analyze:phpstan": "phpstan analyse --ansi --memory-limit=1G",
        "dev:build:clean": "git clean -fX build/",
        "dev:lint": [
            "@dev:lint:syntax",
            "@dev:lint:style"
        ],
        "dev:lint:fix": "phpcbf",
        "dev:lint:style": "phpcs --colors",
        "dev:lint:syntax": "parallel-lint --colors src/ tests/",
        "dev:test": [
            "@dev:lint",
            "@dev:analyze",
            "@dev:test:unit"
        ],
        "dev:test:coverage:ci": "phpunit --colors=always --coverage-text --coverage-clover build/coverage/clover.xml --coverage-cobertura build/coverage/cobertura.xml --coverage-crap4j build/coverage/crap4j.xml --coverage-xml build/coverage/coverage-xml --log-junit build/junit.xml",
        "dev:test:coverage:html": "phpunit --colors=always --coverage-html build/coverage/coverage-html/",
        "dev:test:unit": "phpunit --colors=always",
        "test": "@dev:test"
    },
    "scripts-descriptions": {
        "dev:analyze": "Runs all static analysis checks.",
        "dev:analyze:phpstan": "Runs the PHPStan static analyzer.",
        "dev:build:clean": "Cleans the build/ directory.",
        "dev:lint": "Runs all linting checks.",
        "dev:lint:fix": "Auto-fixes coding standards issues, if possible.",
        "dev:lint:style": "Checks for coding standards issues.",
        "dev:lint:syntax": "Checks for syntax errors.",
        "dev:test": "Runs linting, static analysis, and unit tests.",
        "dev:test:coverage:ci": "Runs unit tests and generates CI coverage reports.",
        "dev:test:coverage:html": "Runs unit tests and generates HTML coverage report.",
        "dev:test:unit": "Runs unit tests.",
        "test": "Runs linting, static analysis, and unit tests."
    }
}
<?php

$config = new Amp\CodeStyle\Config;
$config->getFinder()
    ->in(__DIR__ . '/src')
    ->in(__DIR__ . '/test');

$config->setCacheFile(__DIR__ . '/.php_cs.cache');

return $config;
<?php declare(strict_types=1);

namespace Kelunik\Certificate;

class Certificate
{
    public static function derToPem($der)
    {
        if (!\is_string($der)) {
            throw new \InvalidArgumentException("\$der must be a string, " . \gettype($der) . " given.");
        }

        return \sprintf(
            "-----BEGIN CERTIFICATE-----\n%s-----END CERTIFICATE-----\n",
            \chunk_split(\base64_encode($der), 64, "\n")
        );
    }

    public static function pemToDer($pem)
    {
        if (!\is_string($pem)) {
            throw new \InvalidArgumentException("\$pem must be a string, " . \gettype($pem) . " given.");
        }

        $pattern = "@-----BEGIN CERTIFICATE-----\n([a-zA-Z0-9+/=\n]+)-----END CERTIFICATE-----@";

        if (!\preg_match($pattern, $pem, $match)) {
            throw new InvalidCertificateException("Invalid PEM could not be converted to DER format.");
        }

        return \base64_decode(\str_replace(["\n", "\r"], "", \trim($match[1])));
    }

    private $pem;
    private $info;
    private $issuer;
    private $subject;

    public function __construct($pem)
    {
        if (\is_string($pem)) {
            if (!$cert = @\openssl_x509_read($pem)) {
                throw new InvalidCertificateException("Invalid PEM encoded certificate!");
            }
        } elseif ($pem instanceof \OpenSSLCertificate) {
            $cert = $pem;
        } elseif (\is_resource($pem)) {
            if (\get_resource_type($pem) !== "OpenSSL X.509") {
                throw new InvalidCertificateException("Invalid resource of type other than 'OpenSSL X.509'!");
            }

            $cert = $pem;
        } else {
            throw new \InvalidArgumentException("Invalid variable type, expected string|resource, got " . \gettype($pem));
        }

        if (\openssl_x509_export($pem, $this->pem) === false) {
            throw new InvalidCertificateException("Could not convert 'OpenSSL X.509' resource to PEM!");
        }

        if (!$this->info = \openssl_x509_parse($cert)) {
            throw new InvalidCertificateException("Invalid PEM encoded certificate!");
        }
    }

    public function getNames()
    {
        $san = isset($this->info["extensions"]["subjectAltName"]) ? $this->info["extensions"]["subjectAltName"] : "";
        $names = [];

        $parts = \array_map("trim", \explode(",", $san));

        foreach ($parts as $part) {
            if (\stripos($part, "dns:") === 0) {
                $names[] = \substr($part, 4);
            }
        }

        $names = \array_map("strtolower", $names);
        $names = \array_unique($names);

        \sort($names);

        return $names;
    }

    public function getSubject()
    {
        if ($this->subject === null) {
            $this->subject = new Profile(
                isset($this->info["subject"]["CN"]) ? $this->info["subject"]["CN"] : null,
                isset($this->info["subject"]["O"]) ? $this->info["subject"]["O"] : null,
                isset($this->info["subject"]["C"]) ? $this->info["subject"]["C"] : null
            );
        }

        return $this->subject;
    }

    public function getIssuer()
    {
        if ($this->issuer === null) {
            $this->issuer = new Profile(
                isset($this->info["issuer"]["CN"]) ? $this->info["issuer"]["CN"] : null,
                isset($this->info["issuer"]["O"]) ? $this->info["issuer"]["O"] : null,
                isset($this->info["issuer"]["C"]) ? $this->info["issuer"]["C"] : null
            );
        }

        return $this->issuer;
    }

    public function getSerialNumber()
    {
        return $this->info["serialNumber"];
    }

    public function getValidFrom()
    {
        return $this->info["validFrom_time_t"];
    }

    public function getValidTo()
    {
        return $this->info["validTo_time_t"];
    }

    public function getSignatureType()
    {
        // https://3v4l.org/Iu3T2
        if (!isset($this->info["signatureTypeSN"])) {
            throw new FieldNotSupportedException("Signature type is not supported in this version of PHP. Please update your version to a higher bugfix version. See: https://3v4l.org/Iu3T2");
        }

        return $this->info["signatureTypeSN"];
    }

    public function isSelfSigned()
    {
        return $this->info["subject"] === $this->info["issuer"];
    }

    public function toPem()
    {
        return $this->pem;
    }

    public function toDer()
    {
        return self::pemToDer($this->pem);
    }

    public function __toString()
    {
        return $this->pem;
    }

    public function __debugInfo()
    {
        return [
            "commonName" => $this->getSubject()->getCommonName(),
            "names" => $this->getNames(),
            "issuedBy" => $this->getIssuer()->getCommonName(),
            "validFrom" => \date("d.m.Y", $this->getValidFrom()),
            "validTo" => \date("d.m.Y", $this->getValidTo()),
        ];
    }
}
<?php declare(strict_types=1);

namespace Kelunik\Certificate;

class InvalidCertificateException extends \Exception
{
}
<?php declare(strict_types=1);

namespace Kelunik\Certificate;

class Profile
{
    private $commonName;
    private $organizationName;
    private $country;

    public function __construct($commonName, $organizationName, $country)
    {
        $this->commonName = $commonName;
        $this->organizationName = $organizationName;
        $this->country = $country;
    }

    public function getCommonName()
    {
        return $this->commonName;
    }

    public function getOrganizationName()
    {
        return $this->organizationName;
    }

    public function getCountry()
    {
        return $this->country;
    }
}
<?php declare(strict_types=1);

namespace Kelunik\Certificate;

class FieldNotSupportedException extends \Exception
{
}
{
    "name": "kelunik/certificate",
    "description": "Access certificate details and transform between different formats.",
    "type": "library",
    "license": "MIT",
    "authors": [
        {
            "name": "Niklas Keller",
            "email": "me@kelunik.com"
        }
    ],
    "keywords": [
        "certificate",
        "certificates",
        "openssl",
        "x509",
        "pem",
        "der"
    ],
    "support": {
        "issues": "https://github.com/kelunik/certificate/issues"
    },
    "require": {
        "php": ">=7.0",
        "ext-openssl": "*"
    },
    "require-dev": {
        "amphp/php-cs-fixer-config": "^2",
        "phpunit/phpunit": "^6 | 7 | ^8 | ^9"
    },
    "autoload": {
        "psr-4": {
            "Kelunik\\Certificate\\": "src"
        }
    },
    "extra": {
        "branch-alias": {
            "dev-master": "1.x-dev"
        }
    }
}
<?php

declare(strict_types=1);

namespace Revolt;

use Revolt\EventLoop\CallbackType;
use Revolt\EventLoop\Driver;
use Revolt\EventLoop\DriverFactory;
use Revolt\EventLoop\Internal\AbstractDriver;
use Revolt\EventLoop\Internal\DriverCallback;
use Revolt\EventLoop\InvalidCallbackError;
use Revolt\EventLoop\Suspension;
use Revolt\EventLoop\UnsupportedFeatureException;

/**
 * Accessor to allow global access to the event loop.
 *
 * @see Driver
 */
final class EventLoop
{
    private static Driver $driver;

    /**
     * Sets the driver to be used as the event loop.
     */
    public static function setDriver(Driver $driver): void
    {
        /** @psalm-suppress RedundantPropertyInitializationCheck, RedundantCondition */
        if (isset(self::$driver) && self::$driver->isRunning()) {
            throw new \Error("Can't swap the event loop driver while the driver is running");
        }

        try {
            /** @psalm-suppress InternalClass */
            self::$driver = new class () extends AbstractDriver {
                #[\Override]
                protected function activate(array $callbacks): void
                {
                    throw new \Error("Can't activate callback during garbage collection.");
                }

                #[\Override]
                protected function dispatch(bool $blocking): void
                {
                    throw new \Error("Can't dispatch during garbage collection.");
                }

                #[\Override]
                protected function deactivate(DriverCallback $callback): void
                {
                    // do nothing
                }

                #[\Override]
                public function getHandle(): mixed
                {
                    return null;
                }

                #[\Override]
                protected function now(): float
                {
                    return (float) \hrtime(true) / 1_000_000_000;
                }
            };

            \gc_collect_cycles();
        } finally {
            self::$driver = $driver;
        }
    }

    /**
     * Queue a microtask.
     *
     * The queued callback MUST be executed immediately once the event loop gains control. Order of queueing MUST be
     * preserved when executing the callbacks. Recursive scheduling can thus result in infinite loops, use with care.
     *
     * Does NOT create an event callback, thus CAN NOT be marked as disabled or unreferenced.
     * Use {@see EventLoop::defer()} if you need these features.
     *
     * @param \Closure(...):void $closure The callback to queue.
     * @param mixed ...$args The callback arguments.
     */
    public static function queue(\Closure $closure, mixed ...$args): void
    {
        self::getDriver()->queue($closure, ...$args);
    }

    /**
     * Defer the execution of a callback.
     *
     * The deferred callback MUST be executed before any other type of callback in a tick. Order of enabling MUST be
     * preserved when executing the callbacks.
     *
     * The created callback MUST immediately be marked as enabled, but only be activated (i.e. callback can be called)
     * right before the next tick. Deferred callbacks MUST NOT be called in the tick they were enabled.
     *
     * @param \Closure(string):void $closure The callback to defer. The `$callbackId` will be
     *     invalidated before the callback invocation.
     *
     * @return string A unique identifier that can be used to cancel, enable or disable the callback.
     */
    public static function defer(\Closure $closure): string
    {
        return self::getDriver()->defer($closure);
    }

    /**
     * Delay the execution of a callback.
     *
     * The delay is a minimum and approximate, accuracy is not guaranteed. Order of calls MUST be determined by which
     * timers expire first, but timers with the same expiration time MAY be executed in any order.
     *
     * The created callback MUST immediately be marked as enabled, but only be activated (i.e. callback can be called)
     * right before the next tick. Callbacks MUST NOT be called in the tick they were enabled.
     *
     * @param float $delay The amount of time, in seconds, to delay the execution for.
     * @param \Closure(string):void $closure The callback to delay. The `$callbackId` will be invalidated
     *     before the callback invocation.
     *
     * @return string A unique identifier that can be used to cancel, enable or disable the callback.
     */
    public static function delay(float $delay, \Closure $closure): string
    {
        return self::getDriver()->delay($delay, $closure);
    }

    /**
     * Repeatedly execute a callback.
     *
     * The interval between executions is a minimum and approximate, accuracy is not guaranteed. Order of calls MUST be
     * determined by which timers expire first, but timers with the same expiration time MAY be executed in any order.
     * The first execution is scheduled after the first interval period.
     *
     * The created callback MUST immediately be marked as enabled, but only be activated (i.e. callback can be called)
     * right before the next tick. Callbacks MUST NOT be called in the tick they were enabled.
     *
     * @param float $interval The time interval, in seconds, to wait between executions.
     * @param \Closure(string):void $closure The callback to repeat.
     *
     * @return string A unique identifier that can be used to cancel, enable or disable the callback.
     */
    public static function repeat(float $interval, \Closure $closure): string
    {
        return self::getDriver()->repeat($interval, $closure);
    }

    /**
     * Execute a callback when a stream resource becomes readable or is closed for reading.
     *
     * Warning: Closing resources locally, e.g. with `fclose`, might not invoke the callback. Be sure to `cancel` the
     * callback when closing the resource locally. Drivers MAY choose to notify the user if there are callbacks on
     * invalid resources, but are not required to, due to the high performance impact. Callbacks on closed resources are
     * therefore undefined behavior.
     *
     * Multiple callbacks on the same stream MAY be executed in any order.
     *
     * The created callback MUST immediately be marked as enabled, but only be activated (i.e. callback can be called)
     * right before the next tick. Callbacks MUST NOT be called in the tick they were enabled.
     *
     * @param resource $stream The stream to monitor.
     * @param \Closure(string, resource):void $closure The callback to execute.
     *
     * @return string A unique identifier that can be used to cancel, enable or disable the callback.
     */
    public static function onReadable(mixed $stream, \Closure $closure): string
    {
        return self::getDriver()->onReadable($stream, $closure);
    }

    /**
     * Execute a callback when a stream resource becomes writable or is closed for writing.
     *
     * Warning: Closing resources locally, e.g. with `fclose`, might not invoke the callback. Be sure to `cancel` the
     * callback when closing the resource locally. Drivers MAY choose to notify the user if there are callbacks on
     * invalid resources, but are not required to, due to the high performance impact. Callbacks on closed resources are
     * therefore undefined behavior.
     *
     * Multiple callbacks on the same stream MAY be executed in any order.
     *
     * The created callback MUST immediately be marked as enabled, but only be activated (i.e. callback can be called)
     * right before the next tick. Callbacks MUST NOT be called in the tick they were enabled.
     *
     * @param resource $stream The stream to monitor.
     * @param \Closure(string, resource):void $closure The callback to execute.
     *
     * @return string A unique identifier that can be used to cancel, enable or disable the callback.
     */
    public static function onWritable(mixed $stream, \Closure $closure): string
    {
        return self::getDriver()->onWritable($stream, $closure);
    }

    /**
     * Execute a callback when a signal is received.
     *
     * Warning: Installing the same signal on different instances of this interface is deemed undefined behavior.
     * Implementations MAY try to detect this, if possible, but are not required to. This is due to technical
     * limitations of the signals being registered globally per process.
     *
     * Multiple callbacks on the same signal MAY be executed in any order.
     *
     * The created callback MUST immediately be marked as enabled, but only be activated (i.e. callback can be called)
     * right before the next tick. Callbacks MUST NOT be called in the tick they were enabled.
     *
     * @param int $signal The signal number to monitor.
     * @param \Closure(string, int):void $closure The callback to execute.
     *
     * @return string A unique identifier that can be used to cancel, enable or disable the callback.
     *
     * @throws UnsupportedFeatureException If signal handling is not supported.
     */
    public static function onSignal(int $signal, \Closure $closure): string
    {
        return self::getDriver()->onSignal($signal, $closure);
    }

    /**
     * Enable a callback to be active starting in the next tick.
     *
     * Callbacks MUST immediately be marked as enabled, but only be activated (i.e. callbacks can be called) right
     * before the next tick. Callbacks MUST NOT be called in the tick they were enabled.
     *
     * @param string $callbackId The callback identifier.
     *
     * @return string The callback identifier.
     *
     * @throws InvalidCallbackError If the callback identifier is invalid.
     */
    public static function enable(string $callbackId): string
    {
        return self::getDriver()->enable($callbackId);
    }

    /**
     * Disable a callback immediately.
     *
     * A callback MUST be disabled immediately, e.g. if a deferred callback disables another deferred callback,
     * the second deferred callback isn't executed in this tick.
     *
     * Disabling a callback MUST NOT invalidate the callback. Calling this function MUST NOT fail, even if passed an
     * invalid callback identifier.
     *
     * @param string $callbackId The callback identifier.
     *
     * @return string The callback identifier.
     */
    public static function disable(string $callbackId): string
    {
        return self::getDriver()->disable($callbackId);
    }

    /**
     * Cancel a callback.
     *
     * This will detach the event loop from all resources that are associated to the callback. After this operation the
     * callback is permanently invalid. Calling this function MUST NOT fail, even if passed an invalid identifier.
     *
     * @param string $callbackId The callback identifier.
     */
    public static function cancel(string $callbackId): void
    {
        self::getDriver()->cancel($callbackId);
    }

    /**
     * Reference a callback.
     *
     * This will keep the event loop alive whilst the event is still being monitored. Callbacks have this state by
     * default.
     *
     * @param string $callbackId The callback identifier.
     *
     * @return string The callback identifier.
     *
     * @throws InvalidCallbackError If the callback identifier is invalid.
     */
    public static function reference(string $callbackId): string
    {
        return self::getDriver()->reference($callbackId);
    }

    /**
     * Unreference a callback.
     *
     * The event loop should exit the run method when only unreferenced callbacks are still being monitored. Callbacks
     * are all referenced by default.
     *
     * @param string $callbackId The callback identifier.
     *
     * @return string The callback identifier.
     */
    public static function unreference(string $callbackId): string
    {
        return self::getDriver()->unreference($callbackId);
    }

    /**
     * Set a callback to be executed when an error occurs.
     *
     * The callback receives the error as the first and only parameter. The return value of the callback gets ignored.
     * If it can't handle the error, it MUST throw the error. Errors thrown by the callback or during its invocation
     * MUST be thrown into the `run` loop and stop the driver.
     *
     * Subsequent calls to this method will overwrite the previous handler.
     *
     * @param null|\Closure(\Throwable):void $errorHandler The callback to execute. `null` will clear the current handler.
     */
    public static function setErrorHandler(?\Closure $errorHandler): void
    {
        self::getDriver()->setErrorHandler($errorHandler);
    }

    /**
     * Gets the error handler closure or {@code null} if none is set.
     *
     * @return null|\Closure(\Throwable):void The previous handler, `null` if there was none.
     */
    public static function getErrorHandler(): ?\Closure
    {
        return self::getDriver()->getErrorHandler();
    }

    /**
     * Returns all registered non-cancelled callback identifiers.
     *
     * @return string[] Callback identifiers.
     */
    public static function getIdentifiers(): array
    {
        return self::getDriver()->getIdentifiers();
    }

    /**
     * Returns the type of the callback identified by the given callback identifier.
     *
     * @param string $callbackId The callback identifier.
     *
     * @return CallbackType The callback type.
     */
    public static function getType(string $callbackId): CallbackType
    {
        return self::getDriver()->getType($callbackId);
    }

    /**
     * Returns whether the callback identified by the given callback identifier is currently enabled.
     *
     * @param string $callbackId The callback identifier.
     *
     * @return bool `true` if the callback is currently enabled, otherwise `false`.
     */
    public static function isEnabled(string $callbackId): bool
    {
        return self::getDriver()->isEnabled($callbackId);
    }

    /**
     * Returns whether the callback identified by the given callback identifier is currently referenced.
     *
     * @param string $callbackId The callback identifier.
     *
     * @return bool `true` if the callback is currently referenced, otherwise `false`.
     */
    public static function isReferenced(string $callbackId): bool
    {
        return self::getDriver()->isReferenced($callbackId);
    }

    /**
     * Retrieve the event loop driver that is in scope.
     *
     * @return Driver
     */
    public static function getDriver(): Driver
    {
        /** @psalm-suppress RedundantPropertyInitializationCheck, RedundantCondition */
        if (!isset(self::$driver)) {
            self::setDriver((new DriverFactory())->create());
        }

        return self::$driver;
    }

    /**
     * Returns an object used to suspend and resume execution of the current fiber or {main}.
     *
     * Calls from the same fiber will return the same suspension object.
     *
     * @return Suspension
     */
    public static function getSuspension(): Suspension
    {
        return self::getDriver()->getSuspension();
    }

    /**
     * Run the event loop.
     *
     * This function may only be called from {main}, that is, not within a fiber.
     *
     * Libraries should use the {@link Suspension} API instead of calling this method.
     *
     * This method will not return until the event loop does not contain any pending, referenced callbacks anymore.
     */
    public static function run(): void
    {
        self::getDriver()->run();
    }

    /**
     * Disable construction as this is a static class.
     *
     * @psalm-suppress UnusedConstructor
     */
    private function __construct()
    {
        // intentionally left blank
    }
}
<?php

declare(strict_types=1);

namespace Revolt\EventLoop;

// @codeCoverageIgnoreStart
use Revolt\EventLoop\Driver\EvDriver;
use Revolt\EventLoop\Driver\EventDriver;
use Revolt\EventLoop\Driver\StreamSelectDriver;
use Revolt\EventLoop\Driver\TracingDriver;
use Revolt\EventLoop\Driver\UvDriver;

final class DriverFactory
{
    /**
     * Creates a new loop instance and chooses the best available driver.
     *
     * @return Driver
     *
     * @throws \Error If an invalid class has been specified via REVOLT_LOOP_DRIVER
     */
    public function create(): Driver
    {
        $driver = (function () {
            if ($driver = $this->createDriverFromEnv()) {
                return $driver;
            }

            if (UvDriver::isSupported()) {
                return new UvDriver();
            }

            if (EvDriver::isSupported()) {
                return new EvDriver();
            }

            if (EventDriver::isSupported()) {
                return new EventDriver();
            }

            return new StreamSelectDriver();
        })();

        /** @psalm-suppress RiskyTruthyFalsyComparison */
        if (\getenv("REVOLT_DRIVER_DEBUG_TRACE")) {
            return new TracingDriver($driver);
        }

        return $driver;
    }

    /**
     * @return Driver|null
     */
    private function createDriverFromEnv(): ?Driver
    {
        $driver = \getenv("REVOLT_DRIVER");

        /** @psalm-suppress RiskyTruthyFalsyComparison */
        if (!$driver) {
            return null;
        }

        if (!\class_exists($driver)) {
            throw new \Error(\sprintf(
                "Driver '%s' does not exist.",
                $driver
            ));
        }

        if (!\is_subclass_of($driver, Driver::class)) {
            throw new \Error(\sprintf(
                "Driver '%s' is not a subclass of '%s'.",
                $driver,
                Driver::class
            ));
        }

        return new $driver();
    }
}
// @codeCoverageIgnoreEnd
<?php

declare(strict_types=1);

namespace Revolt\EventLoop;

enum CallbackType
{
    case Defer;
    case Delay;
    case Repeat;
    case Readable;
    case Writable;
    case Signal;
}
<?php

declare(strict_types=1);

namespace Revolt\EventLoop;

/**
 * The driver MUST run in its own fiber and execute callbacks in a separate fiber. If fibers are reused, the driver
 * needs to call {@see FiberLocal::clear()} after running the callback.
 */
interface Driver
{
    /**
     * Run the event loop.
     *
     * One iteration of the loop is called one "tick". A tick covers the following steps:
     *
     *  1. Activate callbacks created / enabled in the last tick / before `run()`.
     *  2. Execute all enabled deferred callbacks.
     *  3. Execute all due timer, pending signal and actionable stream callbacks, each only once per tick.
     *
     * The loop MUST continue to run until it is either stopped explicitly, no referenced callbacks exist anymore, or an
     * exception is thrown that cannot be handled. Exceptions that cannot be handled are exceptions thrown from an
     * error handler or exceptions that would be passed to an error handler but none exists to handle them.
     *
     * @throws \Error Thrown if the event loop is already running.
     */
    public function run(): void;

    /**
     * Stop the event loop.
     *
     * When an event loop is stopped, it continues with its current tick and exits the loop afterwards. Multiple calls
     * to stop MUST be ignored and MUST NOT raise an exception.
     */
    public function stop(): void;

    /**
     * Returns an object used to suspend and resume execution of the current fiber or {main}.
     *
     * Calls from the same fiber will return the same suspension object.
     *
     * @return Suspension
     */
    public function getSuspension(): Suspension;

    /**
     * @return bool True if the event loop is running, false if it is stopped.
     */
    public function isRunning(): bool;

    /**
     * Queue a microtask.
     *
     * The queued callback MUST be executed immediately once the event loop gains control. Order of queueing MUST be
     * preserved when executing the callbacks. Recursive scheduling can thus result in infinite loops, use with care.
     *
     * Does NOT create an event callback, thus CAN NOT be marked as disabled or unreferenced.
     * Use {@see EventLoop::defer()} if you need these features.
     *
     * @param \Closure(...):void $closure The callback to queue.
     * @param mixed ...$args The callback arguments.
     */
    public function queue(\Closure $closure, mixed ...$args): void;

    /**
     * Defer the execution of a callback.
     *
     * The deferred callback MUST be executed before any other type of callback in a tick. Order of enabling MUST be
     * preserved when executing the callbacks.
     *
     * The created callback MUST immediately be marked as enabled, but only be activated (i.e. callback can be called)
     * right before the next tick. Callbacks MUST NOT be called in the tick they were enabled.
     *
     * @param \Closure(string):void $closure The callback to defer. The `$callbackId` will be invalidated before the
     *     callback invocation.
     *
     * @return string A unique identifier that can be used to cancel, enable or disable the callback.
     */
    public function defer(\Closure $closure): string;

    /**
     * Delay the execution of a callback.
     *
     * The delay is a minimum and approximate, accuracy is not guaranteed. Order of calls MUST be determined by which
     * timers expire first, but timers with the same expiration time MAY be executed in any order.
     *
     * The created callback MUST immediately be marked as enabled, but only be activated (i.e. callback can be called)
     * right before the next tick. Callbacks MUST NOT be called in the tick they were enabled.
     *
     * @param float $delay The amount of time, in seconds, to delay the execution for.
     * @param \Closure(string):void $closure The callback to delay. The `$callbackId` will be invalidated before the
     *     callback invocation.
     *
     * @return string A unique identifier that can be used to cancel, enable or disable the callback.
     */
    public function delay(float $delay, \Closure $closure): string;

    /**
     * Repeatedly execute a callback.
     *
     * The interval between executions is a minimum and approximate, accuracy is not guaranteed. Order of calls MUST be
     * determined by which timers expire first, but timers with the same expiration time MAY be executed in any order.
     * The first execution is scheduled after the first interval period.
     *
     * The created callback MUST immediately be marked as enabled, but only be activated (i.e. callback can be called)
     * right before the next tick. Callbacks MUST NOT be called in the tick they were enabled.
     *
     * @param float $interval The time interval, in seconds, to wait between executions.
     * @param \Closure(string):void $closure The callback to repeat.
     *
     * @return string A unique identifier that can be used to cancel, enable or disable the callback.
     */
    public function repeat(float $interval, \Closure $closure): string;

    /**
     * Execute a callback when a stream resource becomes readable or is closed for reading.
     *
     * Warning: Closing resources locally, e.g. with `fclose`, might not invoke the callback. Be sure to `cancel` the
     * callback when closing the resource locally. Drivers MAY choose to notify the user if there are callbacks on
     * invalid resources, but are not required to, due to the high performance impact. Callbacks on closed resources are
     * therefore undefined behavior.
     *
     * Multiple callbacks on the same stream MAY be executed in any order.
     *
     * The created callback MUST immediately be marked as enabled, but only be activated (i.e. callback can be called)
     * right before the next tick. Callbacks MUST NOT be called in the tick they were enabled.
     *
     * @param resource $stream The stream to monitor.
     * @param \Closure(string, resource):void $closure The callback to execute.
     *
     * @return string A unique identifier that can be used to cancel, enable or disable the callback.
     */
    public function onReadable(mixed $stream, \Closure $closure): string;

    /**
     * Execute a callback when a stream resource becomes writable or is closed for writing.
     *
     * Warning: Closing resources locally, e.g. with `fclose`, might not invoke the callback. Be sure to `cancel` the
     * callback when closing the resource locally. Drivers MAY choose to notify the user if there are callbacks on
     * invalid resources, but are not required to, due to the high performance impact. Callbacks on closed resources are
     * therefore undefined behavior.
     *
     * Multiple callbacks on the same stream MAY be executed in any order.
     *
     * The created callback MUST immediately be marked as enabled, but only be activated (i.e. callback can be called)
     * right before the next tick. Callbacks MUST NOT be called in the tick they were enabled.
     *
     * @param resource $stream The stream to monitor.
     * @param \Closure(string, resource):void $closure The callback to execute.
     *
     * @return string A unique identifier that can be used to cancel, enable or disable the callback.
     */
    public function onWritable(mixed $stream, \Closure $closure): string;

    /**
     * Execute a callback when a signal is received.
     *
     * Warning: Installing the same signal on different instances of this interface is deemed undefined behavior.
     * Implementations MAY try to detect this, if possible, but are not required to. This is due to technical
     * limitations of the signals being registered globally per process.
     *
     * Multiple callbacks on the same signal MAY be executed in any order.
     *
     * The created callback MUST immediately be marked as enabled, but only be activated (i.e. callback can be called)
     * right before the next tick. Callbacks MUST NOT be called in the tick they were enabled.
     *
     * @param int $signal The signal number to monitor.
     * @param \Closure(string, int):void $closure The callback to execute.
     *
     * @return string A unique identifier that can be used to cancel, enable or disable the callback.
     *
     * @throws UnsupportedFeatureException If signal handling is not supported.
     */
    public function onSignal(int $signal, \Closure $closure): string;

    /**
     * Enable a callback to be active starting in the next tick.
     *
     * Callbacks MUST immediately be marked as enabled, but only be activated (i.e. callbacks can be called) right
     * before the next tick. Callbacks MUST NOT be called in the tick they were enabled.
     *
     * @param string $callbackId The callback identifier.
     *
     * @return string The callback identifier.
     *
     * @throws InvalidCallbackError If the callback identifier is invalid.
     */
    public function enable(string $callbackId): string;

    /**
     * Cancel a callback.
     *
     * This will detach the event loop from all resources that are associated to the callback. After this operation the
     * callback is permanently invalid. Calling this function MUST NOT fail, even if passed an invalid identifier.
     *
     * @param string $callbackId The callback identifier.
     */
    public function cancel(string $callbackId): void;

    /**
     * Disable a callback immediately.
     *
     * A callback MUST be disabled immediately, e.g. if a deferred callback disables a later deferred callback,
     * the second deferred callback isn't executed in this tick.
     *
     * Disabling a callback MUST NOT invalidate the callback. Calling this function MUST NOT fail, even if passed an
     * invalid callback identifier.
     *
     * @param string $callbackId The callback identifier.
     *
     * @return string The callback identifier.
     */
    public function disable(string $callbackId): string;

    /**
     * Reference a callback.
     *
     * This will keep the event loop alive whilst the callback is still being monitored. Callbacks have this state by
     * default.
     *
     * @param string $callbackId The callback identifier.
     *
     * @return string The callback identifier.
     *
     * @throws InvalidCallbackError If the callback identifier is invalid.
     */
    public function reference(string $callbackId): string;

    /**
     * Unreference a callback.
     *
     * The event loop should exit the run method when only unreferenced callbacks are still being monitored. Callbacks
     * are all referenced by default.
     *
     * @param string $callbackId The callback identifier.
     *
     * @return string The callback identifier.
     */
    public function unreference(string $callbackId): string;

    /**
     * Set a callback to be executed when an error occurs.
     *
     * The callback receives the error as the first and only parameter. The return value of the callback gets ignored.
     * If it can't handle the error, it MUST throw the error. Errors thrown by the callback or during its invocation
     * MUST be thrown into the `run` loop and stop the driver.
     *
     * Subsequent calls to this method will overwrite the previous handler.
     *
     * @param null|\Closure(\Throwable):void $errorHandler The callback to execute. `null` will clear the current
     *     handler.
     */
    public function setErrorHandler(?\Closure $errorHandler): void;

    /**
     * Gets the error handler closure or {@code null} if none is set.
     *
     * @return null|\Closure(\Throwable):void The previous handler, `null` if there was none.
     */
    public function getErrorHandler(): ?\Closure;

    /**
     * Get the underlying loop handle.
     *
     * Example: the `uv_loop` resource for `libuv` or the `EvLoop` object for `libev` or `null` for a stream_select
     * driver.
     *
     * Note: This function is *not* exposed in the `Loop` class. Users shall access it directly on the respective loop
     * instance.
     *
     * @return null|object|resource The loop handle the event loop operates on. `null` if there is none.
     */
    public function getHandle(): mixed;

    /**
     * Returns all registered non-cancelled callback identifiers.
     *
     * @return string[] Callback identifiers.
     */
    public function getIdentifiers(): array;

    /**
     * Returns the type of the callback identified by the given callback identifier.
     *
     * @param string $callbackId The callback identifier.
     *
     * @return CallbackType The callback type.
     */
    public function getType(string $callbackId): CallbackType;

    /**
     * Returns whether the callback identified by the given callback identifier is currently enabled.
     *
     * @param string $callbackId The callback identifier.
     *
     * @return bool `true` if the callback is currently enabled, otherwise `false`.
     */
    public function isEnabled(string $callbackId): bool;

    /**
     * Returns whether the callback identified by the given callback identifier is currently referenced.
     *
     * @param string $callbackId The callback identifier.
     *
     * @return bool `true` if the callback is currently referenced, otherwise `false`.
     */
    public function isReferenced(string $callbackId): bool;

    /**
     * Returns some useful information about the event loop.
     *
     * If this method isn't implemented, dumping the event loop in a busy application, even indirectly, is a pain.
     *
     * @return array
     */
    public function __debugInfo(): array;
}
<?php

declare(strict_types=1);

/** @noinspection PhpComposerExtensionStubsInspection */

namespace Revolt\EventLoop\Driver;

use Revolt\EventLoop\Internal\AbstractDriver;
use Revolt\EventLoop\Internal\DriverCallback;
use Revolt\EventLoop\Internal\SignalCallback;
use Revolt\EventLoop\Internal\StreamCallback;
use Revolt\EventLoop\Internal\StreamReadableCallback;
use Revolt\EventLoop\Internal\StreamWritableCallback;
use Revolt\EventLoop\Internal\TimerCallback;

final class EvDriver extends AbstractDriver
{
    /** @var array<string, \EvSignal>|null */
    private static ?array $activeSignals = null;

    public static function isSupported(): bool
    {
        return \extension_loaded("ev");
    }

    private \EvLoop $handle;

    /** @var array<string, \EvWatcher> */
    private array $events = [];

    private readonly \Closure $ioCallback;

    private readonly \Closure $timerCallback;

    private readonly \Closure $signalCallback;

    /** @var array<string, \EvSignal> */
    private array $signals = [];

    public function __construct()
    {
        parent::__construct();

        $this->handle = new \EvLoop();

        if (self::$activeSignals === null) {
            self::$activeSignals = &$this->signals;
        }

        $this->ioCallback = function (\EvIo $event): void {
            /** @var StreamCallback $callback */
            $callback = $event->data;

            $this->enqueueCallback($callback);
        };

        $this->timerCallback = function (\EvTimer $event): void {
            /** @var TimerCallback $callback */
            $callback = $event->data;

            $this->enqueueCallback($callback);
        };

        $this->signalCallback = function (\EvSignal $event): void {
            /** @var SignalCallback $callback */
            $callback = $event->data;

            $this->enqueueCallback($callback);
        };
    }

    #[\Override]
    public function cancel(string $callbackId): void
    {
        parent::cancel($callbackId);
        unset($this->events[$callbackId]);
    }

    public function __destruct()
    {
        foreach ($this->events as $event) {
            /** @psalm-suppress all */
            if ($event !== null) { // Events may have been nulled in extension depending on destruct order.
                $event->stop();
            }
        }

        // We need to clear all references to events manually, see
        // https://bitbucket.org/osmanov/pecl-ev/issues/31/segfault-in-ev_timer_stop
        $this->events = [];

        // Reinitialize the loop handle due to indeterminate destruct order.
        // See https://github.com/revoltphp/event-loop/issues/105
        $this->handle = new \EvLoop();
    }

    #[\Override]
    public function run(): void
    {
        $active = self::$activeSignals;

        \assert($active !== null);

        foreach ($active as $event) {
            $event->stop();
        }

        self::$activeSignals = &$this->signals;

        foreach ($this->signals as $event) {
            $event->start();
        }

        try {
            parent::run();
        } finally {
            foreach ($this->signals as $event) {
                $event->stop();
            }

            self::$activeSignals = &$active;

            foreach ($active as $event) {
                $event->start();
            }
        }
    }

    #[\Override]
    public function stop(): void
    {
        $this->handle->stop();
        parent::stop();
    }

    #[\Override]
    public function getHandle(): \EvLoop
    {
        return $this->handle;
    }

    #[\Override]
    protected function now(): float
    {
        return (float) \hrtime(true) / 1_000_000_000;
    }

    #[\Override]
    protected function dispatch(bool $blocking): void
    {
        $this->handle->run($blocking ? \Ev::RUN_ONCE : \Ev::RUN_ONCE | \Ev::RUN_NOWAIT);
    }

    #[\Override]
    protected function activate(array $callbacks): void
    {
        $this->handle->nowUpdate();
        $now = $this->now();

        foreach ($callbacks as $callback) {
            if (!isset($this->events[$id = $callback->id])) {
                if ($callback instanceof StreamReadableCallback) {
                    \assert(\is_resource($callback->stream));

                    $this->events[$id] = $this->handle->io($callback->stream, \Ev::READ, $this->ioCallback, $callback);
                } elseif ($callback instanceof StreamWritableCallback) {
                    \assert(\is_resource($callback->stream));

                    $this->events[$id] = $this->handle->io(
                        $callback->stream,
                        \Ev::WRITE,
                        $this->ioCallback,
                        $callback
                    );
                } elseif ($callback instanceof TimerCallback) {
                    $interval = $callback->interval;
                    $this->events[$id] = $this->handle->timer(
                        \max(0, ($callback->expiration - $now)),
                        $callback->repeat ? $interval : 0,
                        $this->timerCallback,
                        $callback
                    );
                } elseif ($callback instanceof SignalCallback) {
                    $this->events[$id] = $this->handle->signal($callback->signal, $this->signalCallback, $callback);
                } else {
                    // @codeCoverageIgnoreStart
                    throw new \Error("Unknown callback type: " . \get_class($callback));
                    // @codeCoverageIgnoreEnd
                }
            } else {
                $this->events[$id]->start();
            }

            if ($callback instanceof SignalCallback) {
                /** @psalm-suppress PropertyTypeCoercion */
                $this->signals[$id] = $this->events[$id];
            }
        }
    }

    #[\Override]
    protected function deactivate(DriverCallback $callback): void
    {
        if (isset($this->events[$id = $callback->id])) {
            $this->events[$id]->stop();

            if ($callback instanceof SignalCallback) {
                unset($this->signals[$id]);
            }
        }
    }
}
<?php

declare(strict_types=1);

/** @noinspection PhpComposerExtensionStubsInspection */

namespace Revolt\EventLoop\Driver;

use Revolt\EventLoop\Internal\AbstractDriver;
use Revolt\EventLoop\Internal\DriverCallback;
use Revolt\EventLoop\Internal\SignalCallback;
use Revolt\EventLoop\Internal\StreamReadableCallback;
use Revolt\EventLoop\Internal\StreamWritableCallback;
use Revolt\EventLoop\Internal\TimerCallback;
use Revolt\EventLoop\Internal\TimerQueue;
use Revolt\EventLoop\UnsupportedFeatureException;

final class StreamSelectDriver extends AbstractDriver
{
    /** @var array<int, resource> */
    private array $readStreams = [];

    /** @var array<int, array<string, StreamReadableCallback>> */
    private array $readCallbacks = [];

    /** @var array<int, resource> */
    private array $writeStreams = [];

    /** @var array<int, array<string, StreamWritableCallback>> */
    private array $writeCallbacks = [];

    private readonly TimerQueue $timerQueue;

    /** @var array<int, array<string, SignalCallback>> */
    private array $signalCallbacks = [];

    /** @var \SplQueue<int> */
    private readonly \SplQueue $signalQueue;

    private bool $signalHandling;

    private readonly \Closure $streamSelectErrorHandler;

    private bool $streamSelectIgnoreResult = false;

    public function __construct()
    {
        parent::__construct();

        $this->signalQueue = new \SplQueue();
        $this->timerQueue = new TimerQueue();
        $this->signalHandling = \extension_loaded("pcntl")
            && \function_exists('pcntl_signal_dispatch')
            && \function_exists('pcntl_signal');

        $this->streamSelectErrorHandler = function (int $errno, string $message): void {
            // Casing changed in PHP 8 from 'unable' to 'Unable'
            if (\stripos($message, "stream_select(): unable to select [4]: ") === 0) { // EINTR
                $this->streamSelectIgnoreResult = true;

                return;
            }

            if (\str_contains($message, 'FD_SETSIZE')) {
                $message = \str_replace(["\r\n", "\n", "\r"], " ", $message);
                $pattern = '(stream_select\(\): You MUST recompile PHP with a larger value of FD_SETSIZE. It is set to (\d+), but you have descriptors numbered at least as high as (\d+)\.)';

                if (\preg_match($pattern, $message, $match)) {
                    $helpLink = 'https://revolt.run/extensions';

                    $message = 'You have reached the limits of stream_select(). It has a FD_SETSIZE of ' . $match[1]
                        . ', but you have file descriptors numbered at least as high as ' . $match[2] . '. '
                        . "You can install one of the extensions listed on {$helpLink} to support a higher number of "
                        . "concurrent file descriptors. If a large number of open file descriptors is unexpected, you "
                        . "might be leaking file descriptors that aren't closed correctly.";
                }
            }

            throw new \Exception($message, $errno);
        };
    }

    public function __destruct()
    {
        foreach ($this->signalCallbacks as $signalCallbacks) {
            foreach ($signalCallbacks as $signalCallback) {
                $this->deactivate($signalCallback);
            }
        }
    }

    /**
     * @throws UnsupportedFeatureException If the pcntl extension is not available.
     */
    #[\Override]
    public function onSignal(int $signal, \Closure $closure): string
    {
        if (!$this->signalHandling) {
            throw new UnsupportedFeatureException("Signal handling requires the pcntl extension");
        }

        return parent::onSignal($signal, $closure);
    }

    #[\Override]
    public function getHandle(): mixed
    {
        return null;
    }

    #[\Override]
    protected function now(): float
    {
        return (float) \hrtime(true) / 1_000_000_000;
    }

    /**
     * @throws \Throwable
     */
    #[\Override]
    protected function dispatch(bool $blocking): void
    {
        if ($this->signalHandling) {
            \pcntl_signal_dispatch();

            while (!$this->signalQueue->isEmpty()) {
                $signal = $this->signalQueue->dequeue();

                foreach ($this->signalCallbacks[$signal] as $callback) {
                    $this->enqueueCallback($callback);
                }

                $blocking = false;
            }
        }

        $this->selectStreams(
            $this->readStreams,
            $this->writeStreams,
            $blocking ? $this->getTimeout() : 0.0
        );

        $now = $this->now();

        while ($callback = $this->timerQueue->extract($now)) {
            $this->enqueueCallback($callback);
        }
    }

    #[\Override]
    protected function activate(array $callbacks): void
    {
        foreach ($callbacks as $callback) {
            if ($callback instanceof StreamReadableCallback) {
                \assert(\is_resource($callback->stream));

                $streamId = (int) $callback->stream;
                $this->readCallbacks[$streamId][$callback->id] = $callback;
                $this->readStreams[$streamId] = $callback->stream;
            } elseif ($callback instanceof StreamWritableCallback) {
                \assert(\is_resource($callback->stream));

                $streamId = (int) $callback->stream;
                $this->writeCallbacks[$streamId][$callback->id] = $callback;
                $this->writeStreams[$streamId] = $callback->stream;
            } elseif ($callback instanceof TimerCallback) {
                $this->timerQueue->insert($callback);
            } elseif ($callback instanceof SignalCallback) {
                if (!isset($this->signalCallbacks[$callback->signal])) {
                    \set_error_handler(static function (int $errno, string $errstr): bool {
                        throw new UnsupportedFeatureException(
                            \sprintf("Failed to register signal handler; Errno: %d; %s", $errno, $errstr)
                        );
                    });

                    // Avoid bug in Psalm handling of first-class callables by assigning to a temp variable.
                    $handler = $this->handleSignal(...);

                    try {
                        \pcntl_signal($callback->signal, $handler);
                    } finally {
                        \restore_error_handler();
                    }
                }

                $this->signalCallbacks[$callback->signal][$callback->id] = $callback;
            } else {
                // @codeCoverageIgnoreStart
                throw new \Error("Unknown callback type");
                // @codeCoverageIgnoreEnd
            }
        }
    }

    #[\Override]
    protected function deactivate(DriverCallback $callback): void
    {
        if ($callback instanceof StreamReadableCallback) {
            $streamId = (int) $callback->stream;
            unset($this->readCallbacks[$streamId][$callback->id]);
            if (empty($this->readCallbacks[$streamId])) {
                unset($this->readCallbacks[$streamId], $this->readStreams[$streamId]);
            }
        } elseif ($callback instanceof StreamWritableCallback) {
            $streamId = (int) $callback->stream;
            unset($this->writeCallbacks[$streamId][$callback->id]);
            if (empty($this->writeCallbacks[$streamId])) {
                unset($this->writeCallbacks[$streamId], $this->writeStreams[$streamId]);
            }
        } elseif ($callback instanceof TimerCallback) {
            $this->timerQueue->remove($callback);
        } elseif ($callback instanceof SignalCallback) {
            if (isset($this->signalCallbacks[$callback->signal])) {
                unset($this->signalCallbacks[$callback->signal][$callback->id]);

                if (empty($this->signalCallbacks[$callback->signal])) {
                    unset($this->signalCallbacks[$callback->signal]);
                    \set_error_handler(static fn () => true);
                    try {
                        \pcntl_signal($callback->signal, \SIG_DFL);
                    } finally {
                        \restore_error_handler();
                    }
                }
            }
        } else {
            // @codeCoverageIgnoreStart
            throw new \Error("Unknown callback type");
            // @codeCoverageIgnoreEnd
        }
    }

    /**
     * @param array<int, resource> $read
     * @param array<int, resource> $write
     */
    private function selectStreams(array $read, array $write, float $timeout): void
    {
        if (!empty($read) || !empty($write)) { // Use stream_select() if there are any streams in the loop.
            if ($timeout >= 0) {
                $seconds = (int) $timeout;
                $microseconds = (int) (($timeout - $seconds) * 1_000_000);
            } else {
                $seconds = null;
                $microseconds = null;
            }

            // Failed connection attempts are indicated via except on Windows
            // @link https://github.com/reactphp/event-loop/blob/8bd064ce23c26c4decf186c2a5a818c9a8209eb0/src/StreamSelectLoop.php#L279-L287
            // @link https://docs.microsoft.com/de-de/windows/win32/api/winsock2/nf-winsock2-select
            $except = null;
            if (\DIRECTORY_SEPARATOR === '\\') {
                $except = $write;
            }

            \set_error_handler($this->streamSelectErrorHandler);

            try {
                /** @psalm-suppress InvalidArgument */
                $result = \stream_select($read, $write, $except, $seconds, $microseconds);
            } finally {
                \restore_error_handler();
            }

            if ($this->streamSelectIgnoreResult || $result === 0) {
                $this->streamSelectIgnoreResult = false;
                return;
            }

            if (!$result) {
                throw new \Exception('Unknown error during stream_select');
            }

            foreach ($read as $stream) {
                $streamId = (int) $stream;
                if (!isset($this->readCallbacks[$streamId])) {
                    continue; // All read callbacks disabled.
                }

                foreach ($this->readCallbacks[$streamId] as $callback) {
                    $this->enqueueCallback($callback);
                }
            }

            /** @var array<int, resource>|null $except */
            if ($except !== null) {
                foreach ($except as $key => $socket) {
                    $write[$key] = $socket;
                }
            }

            foreach ($write as $stream) {
                $streamId = (int) $stream;
                if (!isset($this->writeCallbacks[$streamId])) {
                    continue; // All write callbacks disabled.
                }

                foreach ($this->writeCallbacks[$streamId] as $callback) {
                    $this->enqueueCallback($callback);
                }
            }

            return;
        }

        if ($timeout < 0) { // Only signal callbacks are enabled, so sleep indefinitely.
            /** @psalm-suppress ArgumentTypeCoercion */
            \usleep(\PHP_INT_MAX);
            return;
        }

        if ($timeout > 0) { // Sleep until next timer expires.
            /** @psalm-suppress ArgumentTypeCoercion $timeout is positive here. */
            \usleep((int) ($timeout * 1_000_000));
        }
    }

    /**
     * @return float Seconds until next timer expires or -1 if there are no pending timers.
     */
    private function getTimeout(): float
    {
        $expiration = $this->timerQueue->peek();

        if ($expiration === null) {
            return -1;
        }

        $expiration -= $this->now();

        return $expiration > 0 ? $expiration : 0.0;
    }

    private function handleSignal(int $signal): void
    {
        // Queue signals, so we don't suspend inside pcntl_signal_dispatch, which disables signals while it runs
        $this->signalQueue->enqueue($signal);
    }
}
<?php

declare(strict_types=1);

namespace Revolt\EventLoop\Driver;

use Revolt\EventLoop\CallbackType;
use Revolt\EventLoop\Driver;
use Revolt\EventLoop\InvalidCallbackError;
use Revolt\EventLoop\Suspension;

final class TracingDriver implements Driver
{
    private readonly Driver $driver;

    /** @var array<string, true> */
    private array $enabledCallbacks = [];

    /** @var array<string, true> */
    private array $unreferencedCallbacks = [];

    /** @var array<string, string> */
    private array $creationTraces = [];

    /** @var array<string, string> */
    private array $cancelTraces = [];

    public function __construct(Driver $driver)
    {
        $this->driver = $driver;
    }

    #[\Override]
    public function run(): void
    {
        $this->driver->run();
    }

    #[\Override]
    public function stop(): void
    {
        $this->driver->stop();
    }

    #[\Override]
    public function getSuspension(): Suspension
    {
        return $this->driver->getSuspension();
    }

    #[\Override]
    public function isRunning(): bool
    {
        return $this->driver->isRunning();
    }

    #[\Override]
    public function defer(\Closure $closure): string
    {
        $id = $this->driver->defer(function (...$args) use ($closure) {
            $this->cancel($args[0]);
            return $closure(...$args);
        });

        $this->creationTraces[$id] = $this->formatStacktrace(\debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS));
        $this->enabledCallbacks[$id] = true;

        return $id;
    }

    #[\Override]
    public function delay(float $delay, \Closure $closure): string
    {
        $id = $this->driver->delay($delay, function (...$args) use ($closure) {
            $this->cancel($args[0]);
            return $closure(...$args);
        });

        $this->creationTraces[$id] = $this->formatStacktrace(\debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS));
        $this->enabledCallbacks[$id] = true;

        return $id;
    }

    #[\Override]
    public function repeat(float $interval, \Closure $closure): string
    {
        $id = $this->driver->repeat($interval, $closure);

        $this->creationTraces[$id] = $this->formatStacktrace(\debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS));
        $this->enabledCallbacks[$id] = true;

        return $id;
    }

    #[\Override]
    public function onReadable(mixed $stream, \Closure $closure): string
    {
        $id = $this->driver->onReadable($stream, $closure);

        $this->creationTraces[$id] = $this->formatStacktrace(\debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS));
        $this->enabledCallbacks[$id] = true;

        return $id;
    }

    #[\Override]
    public function onWritable(mixed $stream, \Closure $closure): string
    {
        $id = $this->driver->onWritable($stream, $closure);

        $this->creationTraces[$id] = $this->formatStacktrace(\debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS));
        $this->enabledCallbacks[$id] = true;

        return $id;
    }

    #[\Override]
    public function onSignal(int $signal, \Closure $closure): string
    {
        $id = $this->driver->onSignal($signal, $closure);

        $this->creationTraces[$id] = $this->formatStacktrace(\debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS));
        $this->enabledCallbacks[$id] = true;

        return $id;
    }

    #[\Override]
    public function enable(string $callbackId): string
    {
        try {
            $this->driver->enable($callbackId);
            $this->enabledCallbacks[$callbackId] = true;
        } catch (InvalidCallbackError $e) {
            $e->addInfo("Creation trace", $this->getCreationTrace($callbackId));
            $e->addInfo("Cancellation trace", $this->getCancelTrace($callbackId));

            throw $e;
        }

        return $callbackId;
    }

    #[\Override]
    public function cancel(string $callbackId): void
    {
        $this->driver->cancel($callbackId);

        if (!isset($this->cancelTraces[$callbackId])) {
            $this->cancelTraces[$callbackId] = $this->formatStacktrace(\debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS));
        }

        unset($this->enabledCallbacks[$callbackId], $this->unreferencedCallbacks[$callbackId]);
    }

    #[\Override]
    public function disable(string $callbackId): string
    {
        $this->driver->disable($callbackId);
        unset($this->enabledCallbacks[$callbackId]);

        return $callbackId;
    }

    #[\Override]
    public function reference(string $callbackId): string
    {
        try {
            $this->driver->reference($callbackId);
            unset($this->unreferencedCallbacks[$callbackId]);
        } catch (InvalidCallbackError $e) {
            $e->addInfo("Creation trace", $this->getCreationTrace($callbackId));
            $e->addInfo("Cancellation trace", $this->getCancelTrace($callbackId));

            throw $e;
        }

        return $callbackId;
    }

    #[\Override]
    public function unreference(string $callbackId): string
    {
        $this->driver->unreference($callbackId);
        $this->unreferencedCallbacks[$callbackId] = true;

        return $callbackId;
    }

    #[\Override]
    public function setErrorHandler(?\Closure $errorHandler): void
    {
        $this->driver->setErrorHandler($errorHandler);
    }

    #[\Override]
    public function getErrorHandler(): ?\Closure
    {
        return $this->driver->getErrorHandler();
    }

    /** @inheritdoc */
    #[\Override]
    public function getHandle(): mixed
    {
        return $this->driver->getHandle();
    }

    public function dump(): string
    {
        $dump = "Enabled, referenced callbacks keeping the loop running: ";

        foreach ($this->enabledCallbacks as $callbackId => $_) {
            if (isset($this->unreferencedCallbacks[$callbackId])) {
                continue;
            }

            $dump .= "Callback identifier: " . $callbackId . "\r\n";
            $dump .= $this->getCreationTrace($callbackId);
            $dump .= "\r\n\r\n";
        }

        return \rtrim($dump);
    }

    #[\Override]
    public function getIdentifiers(): array
    {
        return $this->driver->getIdentifiers();
    }

    #[\Override]
    public function getType(string $callbackId): CallbackType
    {
        return $this->driver->getType($callbackId);
    }

    #[\Override]
    public function isEnabled(string $callbackId): bool
    {
        return $this->driver->isEnabled($callbackId);
    }

    #[\Override]
    public function isReferenced(string $callbackId): bool
    {
        return $this->driver->isReferenced($callbackId);
    }

    #[\Override]
    public function __debugInfo(): array
    {
        return $this->driver->__debugInfo();
    }

    #[\Override]
    public function queue(\Closure $closure, mixed ...$args): void
    {
        $this->driver->queue($closure, ...$args);
    }

    private function getCreationTrace(string $callbackId): string
    {
        return $this->creationTraces[$callbackId] ?? 'No creation trace, yet.';
    }

    private function getCancelTrace(string $callbackId): string
    {
        return $this->cancelTraces[$callbackId] ?? 'No cancellation trace, yet.';
    }

    /**
     * Formats a stacktrace obtained via `debug_backtrace()`.
     *
     * @param list<array{
     *     args?: list<mixed>,
     *     class?: class-string,
     *     file?: string,
     *     function: string,
     *     line?: int,
     *     object?: object,
     *     type?: string
     * }> $trace Output of `debug_backtrace()`.
     *
     * @return string Formatted stacktrace.
     */
    private function formatStacktrace(array $trace): string
    {
        return \implode("\n", \array_map(static function ($e, $i) {
            $line = "#{$i} ";

            if (isset($e["file"], $e['line'])) {
                $line .= "{$e['file']}:{$e['line']} ";
            }

            if (isset($e["class"], $e["type"])) {
                $line .= $e["class"] . $e["type"];
            }

            return $line . $e["function"] . "()";
        }, $trace, \array_keys($trace)));
    }
}
<?php

declare(strict_types=1);

/** @noinspection PhpComposerExtensionStubsInspection */

namespace Revolt\EventLoop\Driver;

use Revolt\EventLoop\Internal\AbstractDriver;
use Revolt\EventLoop\Internal\DriverCallback;
use Revolt\EventLoop\Internal\SignalCallback;
use Revolt\EventLoop\Internal\StreamCallback;
use Revolt\EventLoop\Internal\StreamReadableCallback;
use Revolt\EventLoop\Internal\StreamWritableCallback;
use Revolt\EventLoop\Internal\TimerCallback;

final class EventDriver extends AbstractDriver
{
    /** @var array<string, \Event>|null */
    private static ?array $activeSignals = null;

    public static function isSupported(): bool
    {
        return \extension_loaded("event");
    }

    private \EventBase $handle;
    /** @var array<string, \Event> */
    private array $events = [];
    private readonly \Closure $ioCallback;
    private readonly \Closure $timerCallback;
    private readonly \Closure $signalCallback;

    /** @var array<string, \Event> */
    private array $signals = [];

    public function __construct()
    {
        parent::__construct();

        /** @psalm-suppress TooFewArguments https://github.com/JetBrains/phpstorm-stubs/pull/763 */
        $this->handle = new \EventBase();

        if (self::$activeSignals === null) {
            self::$activeSignals = &$this->signals;
        }

        $this->ioCallback = function ($resource, $what, StreamCallback $callback): void {
            $this->enqueueCallback($callback);
        };

        $this->timerCallback = function ($resource, $what, TimerCallback $callback): void {
            $this->enqueueCallback($callback);
        };

        $this->signalCallback = function ($signo, $what, SignalCallback $callback): void {
            $this->enqueueCallback($callback);
        };
    }

    #[\Override]
    public function cancel(string $callbackId): void
    {
        parent::cancel($callbackId);

        if (isset($this->events[$callbackId])) {
            $this->events[$callbackId]->free();
            unset($this->events[$callbackId]);
        }
    }

    /**
     * @codeCoverageIgnore
     */
    public function __destruct()
    {
        foreach ($this->events as $event) {
            if ($event !== null) { // Events may have been nulled in extension depending on destruct order.
                $event->free();
            }
        }

        // Unset here, otherwise $event->del() fails with a warning, because __destruct order isn't defined.
        // See https://github.com/amphp/amp/issues/159.
        $this->events = [];

        // Manually free the loop handle to fully release loop resources.
        // See https://github.com/amphp/amp/issues/177.
        /** @psalm-suppress RedundantPropertyInitializationCheck */
        if (isset($this->handle)) {
            $this->handle->free();

            // Reinitialize the loop handle due to indeterminate destruct order.
            // See https://github.com/revoltphp/event-loop/issues/105
            /** @psalm-suppress TooFewArguments https://github.com/JetBrains/phpstorm-stubs/pull/763 */
            $this->handle = new \EventBase();
        }
    }

    #[\Override]
    public function run(): void
    {
        $active = self::$activeSignals;

        \assert($active !== null);

        foreach ($active as $event) {
            $event->del();
        }

        self::$activeSignals = &$this->signals;

        foreach ($this->signals as $event) {
            /** @psalm-suppress TooFewArguments https://github.com/JetBrains/phpstorm-stubs/pull/763 */
            $event->add();
        }

        try {
            parent::run();
        } finally {
            foreach ($this->signals as $event) {
                $event->del();
            }

            self::$activeSignals = &$active;

            foreach ($active as $event) {
                /** @psalm-suppress TooFewArguments https://github.com/JetBrains/phpstorm-stubs/pull/763 */
                $event->add();
            }
        }
    }

    #[\Override]
    public function stop(): void
    {
        $this->handle->stop();
        parent::stop();
    }

    #[\Override]
    public function getHandle(): \EventBase
    {
        return $this->handle;
    }

    #[\Override]
    protected function now(): float
    {
        return (float) \hrtime(true) / 1_000_000_000;
    }

    #[\Override]
    protected function dispatch(bool $blocking): void
    {
        $this->handle->loop($blocking ? \EventBase::LOOP_ONCE : \EventBase::LOOP_ONCE | \EventBase::LOOP_NONBLOCK);
    }

    #[\Override]
    protected function activate(array $callbacks): void
    {
        $now = $this->now();

        foreach ($callbacks as $callback) {
            if (!isset($this->events[$id = $callback->id])) {
                if ($callback instanceof StreamReadableCallback) {
                    \assert(\is_resource($callback->stream));

                    $this->events[$id] = new \Event(
                        $this->handle,
                        $callback->stream,
                        \Event::READ | \Event::PERSIST,
                        $this->ioCallback,
                        $callback
                    );
                } elseif ($callback instanceof StreamWritableCallback) {
                    \assert(\is_resource($callback->stream));

                    $this->events[$id] = new \Event(
                        $this->handle,
                        $callback->stream,
                        \Event::WRITE | \Event::PERSIST,
                        $this->ioCallback,
                        $callback
                    );
                } elseif ($callback instanceof TimerCallback) {
                    $this->events[$id] = new \Event(
                        $this->handle,
                        -1,
                        \Event::TIMEOUT,
                        $this->timerCallback,
                        $callback
                    );
                } elseif ($callback instanceof SignalCallback) {
                    $this->events[$id] = new \Event(
                        $this->handle,
                        $callback->signal,
                        \Event::SIGNAL | \Event::PERSIST,
                        $this->signalCallback,
                        $callback
                    );
                } else {
                    // @codeCoverageIgnoreStart
                    throw new \Error("Unknown callback type");
                    // @codeCoverageIgnoreEnd
                }
            }

            if ($callback instanceof TimerCallback) {
                $interval = \min(\max(0, $callback->expiration - $now), \PHP_INT_MAX / 2);
                $this->events[$id]->add($interval > 0 ? $interval : 0);
            } elseif ($callback instanceof SignalCallback) {
                $this->signals[$id] = $this->events[$id];
                /** @psalm-suppress TooFewArguments https://github.com/JetBrains/phpstorm-stubs/pull/763 */
                $this->events[$id]->add();
            } else {
                /** @psalm-suppress TooFewArguments https://github.com/JetBrains/phpstorm-stubs/pull/763 */
                $this->events[$id]->add();
            }
        }
    }

    #[\Override]
    protected function deactivate(DriverCallback $callback): void
    {
        if (isset($this->events[$id = $callback->id])) {
            $this->events[$id]->del();

            if ($callback instanceof SignalCallback) {
                unset($this->signals[$id]);
            }
        }
    }
}
<?php

declare(strict_types=1);

namespace Revolt\EventLoop\Driver;

use Revolt\EventLoop\Internal\AbstractDriver;
use Revolt\EventLoop\Internal\DriverCallback;
use Revolt\EventLoop\Internal\SignalCallback;
use Revolt\EventLoop\Internal\StreamCallback;
use Revolt\EventLoop\Internal\StreamReadableCallback;
use Revolt\EventLoop\Internal\StreamWritableCallback;
use Revolt\EventLoop\Internal\TimerCallback;

final class UvDriver extends AbstractDriver
{
    public static function isSupported(): bool
    {
        return \extension_loaded("uv");
    }

    /** @var resource|\UVLoop A uv_loop resource created with uv_loop_new() */
    private $handle;
    /** @var array<string, resource> */
    private array $events = [];
    /** @var array<int, array<array-key, DriverCallback>> */
    private array $uvCallbacks = [];
    /** @var array<int, resource> */
    private array $streams = [];
    private readonly \Closure $ioCallback;
    private readonly \Closure $timerCallback;
    private readonly \Closure $signalCallback;

    public function __construct()
    {
        parent::__construct();

        $this->handle = \uv_loop_new();

        /** @psalm-suppress UnusedClosureParam */
        $this->ioCallback = function ($event, $status, $events, $resource): void {
            $callbacks = $this->uvCallbacks[(int) $event];

            // Invoke the callback on errors, as this matches behavior with other loop back-ends.
            // Re-enable callback as libuv disables the callback on non-zero status.
            if ($status !== 0) {
                $flags = 0;
                foreach ($callbacks as $callback) {
                    \assert($callback instanceof StreamCallback);

                    $flags |= $callback->invokable ? $this->getStreamCallbackFlags($callback) : 0;
                }
                \uv_poll_start($event, $flags, $this->ioCallback);
            }

            foreach ($callbacks as $callback) {
                \assert($callback instanceof StreamCallback);

                // $events is ORed with 4 to trigger callback if no events are indicated (0) or on UV_DISCONNECT (4).
                // http://docs.libuv.org/en/v1.x/poll.html
                if (!($this->getStreamCallbackFlags($callback) & $events || ($events | 4) === 4)) {
                    continue;
                }

                $this->enqueueCallback($callback);
            }
        };

        $this->timerCallback = function ($event): void {
            $callback = $this->uvCallbacks[(int) $event][0];

            \assert($callback instanceof TimerCallback);

            $this->enqueueCallback($callback);
        };

        $this->signalCallback = function ($event): void {
            $callback = $this->uvCallbacks[(int) $event][0];

            $this->enqueueCallback($callback);
        };
    }

    #[\Override]
    public function cancel(string $callbackId): void
    {
        parent::cancel($callbackId);

        if (!isset($this->events[$callbackId])) {
            return;
        }

        $event = $this->events[$callbackId];
        $eventId = (int) $event;

        if (isset($this->uvCallbacks[$eventId][0])) { // All except IO callbacks.
            unset($this->uvCallbacks[$eventId]);
        } elseif (isset($this->uvCallbacks[$eventId][$callbackId])) {
            $callback = $this->uvCallbacks[$eventId][$callbackId];
            unset($this->uvCallbacks[$eventId][$callbackId]);

            \assert($callback instanceof StreamCallback);

            if (empty($this->uvCallbacks[$eventId])) {
                unset($this->uvCallbacks[$eventId], $this->streams[(int) $callback->stream]);
            }
        }

        unset($this->events[$callbackId]);
    }

    public function __destruct()
    {
        $this->events = [];
        $this->uvCallbacks = [];
        $this->streams = [];

        // Reinitialize the loop handle due to indeterminate destruct order.
        // See https://github.com/revoltphp/event-loop/issues/105
        $this->handle = \uv_loop_new();
    }

    /**
     * @return \UVLoop|resource
     */
    #[\Override]
    public function getHandle(): mixed
    {
        return $this->handle;
    }

    #[\Override]
    protected function now(): float
    {
        \uv_update_time($this->handle);

        /** @psalm-suppress TooManyArguments */
        return \uv_now($this->handle) / 1000;
    }

    #[\Override]
    protected function dispatch(bool $blocking): void
    {
        /** @psalm-suppress TooManyArguments */
        \uv_run($this->handle, $blocking ? \UV::RUN_ONCE : \UV::RUN_NOWAIT);
    }

    #[\Override]
    protected function activate(array $callbacks): void
    {
        $now = $this->now();

        foreach ($callbacks as $callback) {
            $id = $callback->id;

            if ($callback instanceof StreamCallback) {
                \assert(\is_resource($callback->stream));

                $streamId = (int) $callback->stream;

                if (isset($this->streams[$streamId])) {
                    $event = $this->streams[$streamId];
                } elseif (isset($this->events[$id])) {
                    $event = $this->streams[$streamId] = $this->events[$id];
                } else {
                    /** @psalm-suppress TooManyArguments */
                    $event = $this->streams[$streamId] = \uv_poll_init_socket($this->handle, $callback->stream);
                }

                $eventId = (int) $event;
                $this->events[$id] = $event;
                $this->uvCallbacks[$eventId][$id] = $callback;

                $flags = 0;
                foreach ($this->uvCallbacks[$eventId] as $w) {
                    \assert($w instanceof StreamCallback);

                    $flags |= $w->enabled ? ($this->getStreamCallbackFlags($w)) : 0;
                }
                \uv_poll_start($event, $flags, $this->ioCallback);
            } elseif ($callback instanceof TimerCallback) {
                if (isset($this->events[$id])) {
                    $event = $this->events[$id];
                } else {
                    $event = $this->events[$id] = \uv_timer_init($this->handle);
                }

                $this->uvCallbacks[(int) $event] = [$callback];

                \uv_timer_start(
                    $event,
                    (int) \min(\max(0, \ceil(($callback->expiration - $now) * 1000)), \PHP_INT_MAX),
                    $callback->repeat ? (int) \min(\max(0, \ceil($callback->interval * 1000)), \PHP_INT_MAX) : 0,
                    $this->timerCallback
                );
            } elseif ($callback instanceof SignalCallback) {
                if (isset($this->events[$id])) {
                    $event = $this->events[$id];
                } else {
                    /** @psalm-suppress TooManyArguments */
                    $event = $this->events[$id] = \uv_signal_init($this->handle);
                }

                $this->uvCallbacks[(int) $event] = [$callback];

                /** @psalm-suppress TooManyArguments */
                \uv_signal_start($event, $this->signalCallback, $callback->signal);
            } else {
                // @codeCoverageIgnoreStart
                throw new \Error("Unknown callback type");
                // @codeCoverageIgnoreEnd
            }
        }
    }

    #[\Override]
    protected function deactivate(DriverCallback $callback): void
    {
        $id = $callback->id;

        if (!isset($this->events[$id])) {
            return;
        }

        $event = $this->events[$id];

        if (!\uv_is_active($event)) {
            return;
        }

        if ($callback instanceof StreamCallback) {
            $flags = 0;
            foreach ($this->uvCallbacks[(int) $event] as $w) {
                \assert($w instanceof StreamCallback);

                $flags |= $w->invokable ? ($this->getStreamCallbackFlags($w)) : 0;
            }

            if ($flags) {
                \uv_poll_start($event, $flags, $this->ioCallback);
            } else {
                \uv_poll_stop($event);
            }
        } elseif ($callback instanceof TimerCallback) {
            \uv_timer_stop($event);
        } elseif ($callback instanceof SignalCallback) {
            \uv_signal_stop($event);
        } else {
            // @codeCoverageIgnoreStart
            throw new \Error("Unknown callback type");
            // @codeCoverageIgnoreEnd
        }
    }

    private function getStreamCallbackFlags(StreamCallback $callback): int
    {
        if ($callback instanceof StreamWritableCallback) {
            return \UV::WRITABLE;
        }

        if ($callback instanceof StreamReadableCallback) {
            return \UV::READABLE;
        }

        throw new \Error('Invalid callback type');
    }
}
<?php

declare(strict_types=1);

namespace Revolt\EventLoop;

use Revolt\EventLoop\Internal\ClosureHelper;

final class InvalidCallbackError extends \Error
{
    public const E_NONNULL_RETURN = 1;
    public const E_INVALID_IDENTIFIER = 2;

    /**
     * MUST be thrown if any callback returns a non-null value.
     */
    public static function nonNullReturn(string $callbackId, \Closure $closure): self
    {
        return new self(
            $callbackId,
            self::E_NONNULL_RETURN,
            'Non-null return value received from callback ' . ClosureHelper::getDescription($closure)
        );
    }

    /**
     * MUST be thrown if any operation (except disable() and cancel()) is attempted with an invalid callback identifier.
     *
     * An invalid callback identifier is any identifier that is not yet emitted by the driver or cancelled by the user.
     */
    public static function invalidIdentifier(string $callbackId): self
    {
        return new self($callbackId, self::E_INVALID_IDENTIFIER, 'Invalid callback identifier ' . $callbackId);
    }

    /** @var string */
    private readonly string $rawMessage;

    /** @var string */
    private readonly string $callbackId;

    /** @var array<string, string> */
    private array $info = [];

    /**
     * @param string $callbackId The callback identifier.
     * @param string $message The exception message.
     */
    private function __construct(string $callbackId, int $code, string $message)
    {
        parent::__construct($message, $code);

        $this->callbackId = $callbackId;
        $this->rawMessage = $message;
    }

    /**
     * @return string The callback identifier.
     */
    public function getCallbackId(): string
    {
        return $this->callbackId;
    }

    public function addInfo(string $key, string $message): void
    {
        $this->info[$key] = $message;

        $info = '';

        foreach ($this->info as $infoKey => $infoMessage) {
            $info .= "\r\n\r\n" . $infoKey . ': ' . $infoMessage;
        }

        $this->message = $this->rawMessage . $info;
    }
}
<?php

declare(strict_types=1);

namespace Revolt\EventLoop\Internal;

/** @internal */
final class StreamReadableCallback extends StreamCallback
{
}
<?php

declare(strict_types=1);

namespace Revolt\EventLoop\Internal;

/**
 * @internal
 */
abstract class DriverCallback
{
    public bool $invokable = false;

    public bool $enabled = true;

    public bool $referenced = true;

    public function __construct(
        public readonly string $id,
        public readonly \Closure $closure
    ) {
    }

    /**
     * @param string $property
     */
    public function __get(string $property): never
    {
        throw new \Error("Unknown property '{$property}'");
    }

    /**
     * @param string $property
     * @param mixed  $value
     */
    public function __set(string $property, mixed $value): never
    {
        throw new \Error("Unknown property '{$property}'");
    }
}
<?php

declare(strict_types=1);

namespace Revolt\EventLoop\Internal;

/** @internal */
final class TimerCallback extends DriverCallback
{
    public function __construct(
        string $id,
        public readonly float $interval,
        \Closure $callback,
        public float $expiration,
        public readonly bool $repeat = false
    ) {
        parent::__construct($id, $callback);
    }
}
<?php

/** @noinspection PhpPropertyOnlyWrittenInspection */

declare(strict_types=1);

namespace Revolt\EventLoop\Internal;

use Revolt\EventLoop\Suspension;

/**
 * @internal
 *
 * @template T
 * @implements Suspension<T>
 */
final class DriverSuspension implements Suspension
{
    /** @psalm-suppress UnusedProperty False-positive. */
    private ?\Fiber $suspendedFiber = null;

    /** @var \WeakReference<\Fiber>|null */
    private readonly ?\WeakReference $fiberRef;

    private ?\Error $error = null;

    private bool $pending = false;

    private bool $deadMain = false;

    /**
     * @param \WeakMap<object, \WeakReference<DriverSuspension>> $suspensions
     */
    public function __construct(
        private readonly \Closure $run,
        private readonly \Closure $queue,
        private readonly \Closure $interrupt,
        private readonly \WeakMap $suspensions,
    ) {
        $fiber = \Fiber::getCurrent();

        $this->fiberRef = $fiber ? \WeakReference::create($fiber) : null;
    }

    #[\Override]
    public function resume(mixed $value = null): void
    {
        // Ignore spurious resumes to old dead {main} suspension
        if ($this->deadMain) {
            return;
        }

        if (!$this->pending) {
            throw $this->error ?? new \Error('Must call suspend() before calling resume()');
        }

        $this->pending = false;

        $fiber = $this->fiberRef?->get();

        if ($fiber) {
            ($this->queue)(static function () use ($fiber, $value): void {
                // The fiber may be destroyed with suspension as part of the GC cycle collector.
                if (!$fiber->isTerminated()) {
                    $fiber->resume($value);
                }
            });
        } else {
            // Suspend event loop fiber to {main}.
            ($this->interrupt)(static fn () => $value);
        }
    }

    #[\Override]
    public function suspend(): mixed
    {
        // Throw exception when trying to use old dead {main} suspension
        if ($this->deadMain) {
            throw new \Error(
                'Suspension cannot be suspended after an uncaught exception is thrown from the event loop',
            );
        }

        if ($this->pending) {
            throw new \Error('Must call resume() or throw() before calling suspend() again');
        }

        $fiber = $this->fiberRef?->get();

        if ($fiber !== \Fiber::getCurrent()) {
            throw new \Error('Must not call suspend() from another fiber');
        }

        $this->pending = true;
        $this->error = null;

        // Awaiting from within a fiber.
        if ($fiber) {
            $this->suspendedFiber = $fiber;

            try {
                $value = \Fiber::suspend();
                $this->suspendedFiber = null;
            } catch (\FiberError $error) {
                $this->pending = false;
                $this->suspendedFiber = null;
                $this->error = $error;

                throw $error;
            }

            // Setting $this->suspendedFiber = null in finally will set the fiber to null if a fiber is destroyed
            // as part of a cycle collection, causing an error if the suspension is subsequently resumed.

            return $value;
        }

        // Awaiting from {main}.
        $result = ($this->run)();

        /** @psalm-suppress RedundantCondition $this->pending should be changed when resumed. */
        if ($this->pending) {
            // This is now a dead {main} suspension.
            $this->deadMain = true;

            // Unset suspension for {main} using queue closure.
            unset($this->suspensions[$this->queue]);

            $result && $result(); // Unwrap any uncaught exceptions from the event loop

            \gc_collect_cycles(); // Collect any circular references before dumping pending suspensions.

            $info = '';
            foreach ($this->suspensions as $suspensionRef) {
                if ($suspension = $suspensionRef->get()) {
                    \assert($suspension instanceof self);
                    $fiber = $suspension->fiberRef?->get();
                    if ($fiber === null) {
                        continue;
                    }

                    $reflectionFiber = new \ReflectionFiber($fiber);
                    $info .= "\n\n" . $this->formatStacktrace($reflectionFiber->getTrace(\DEBUG_BACKTRACE_IGNORE_ARGS));
                }
            }

            throw new \Error('Event loop terminated without resuming the current suspension (the cause is either a fiber deadlock, or an incorrectly unreferenced/canceled watcher):' . $info);
        }

        return $result();
    }

    #[\Override]
    public function throw(\Throwable $throwable): void
    {
        // Ignore spurious resumes to old dead {main} suspension
        if ($this->deadMain) {
            return;
        }

        if (!$this->pending) {
            throw $this->error ?? new \Error('Must call suspend() before calling throw()');
        }

        $this->pending = false;

        $fiber = $this->fiberRef?->get();

        if ($fiber) {
            ($this->queue)(static function () use ($fiber, $throwable): void {
                // The fiber may be destroyed with suspension as part of the GC cycle collector.
                if (!$fiber->isTerminated()) {
                    $fiber->throw($throwable);
                }
            });
        } else {
            // Suspend event loop fiber to {main}.
            ($this->interrupt)(static fn () => throw $throwable);
        }
    }

    private function formatStacktrace(array $trace): string
    {
        return \implode("\n", \array_map(static function ($e, $i) {
            $line = "#{$i} ";

            if (isset($e["file"])) {
                $line .= "{$e['file']}:{$e['line']} ";
            }

            if (isset($e["class"], $e["type"])) {
                $line .= $e["class"] . $e["type"];
            }

            return $line . $e["function"] . "()";
        }, $trace, \array_keys($trace)));
    }
}
<?php

declare(strict_types=1);

namespace Revolt\EventLoop\Internal;

/** @internal */
final class StreamWritableCallback extends StreamCallback
{
}
<?php

declare(strict_types=1);

namespace Revolt\EventLoop\Internal;

/**
 * Uses a binary tree stored in an array to implement a heap.
 *
 * @internal
 */
final class TimerQueue
{
    /** @var array<int, TimerCallback> */
    private array $callbacks = [];

    /** @var array<string, int> */
    private array $pointers = [];

    /**
     * Inserts the callback into the queue.
     *
     * Time complexity: O(log(n)).
     */
    public function insert(TimerCallback $callback): void
    {
        \assert(!isset($this->pointers[$callback->id]));

        $node = \count($this->callbacks);
        $this->callbacks[$node] = $callback;
        $this->pointers[$callback->id] = $node;

        $this->heapifyUp($node);
    }

    /**
     * Removes the given callback from the queue.
     *
     * Time complexity: O(log(n)).
     */
    public function remove(TimerCallback $callback): void
    {
        $id = $callback->id;

        if (!isset($this->pointers[$id])) {
            return;
        }

        $this->removeAndRebuild($this->pointers[$id]);
    }

    /**
     * Deletes and returns the callback on top of the heap if it has expired, otherwise null is returned.
     *
     * Time complexity: O(log(n)).
     *
     * @param float $now Current event loop time.
     *
     * @return TimerCallback|null Expired callback at the top of the heap or null if the callback has not expired.
     */
    public function extract(float $now): ?TimerCallback
    {
        if (!$this->callbacks) {
            return null;
        }

        $callback = $this->callbacks[0];
        if ($callback->expiration > $now) {
            return null;
        }

        $this->removeAndRebuild(0);

        return $callback;
    }

    /**
     * Returns the expiration time value at the top of the heap.
     *
     * Time complexity: O(1).
     *
     * @return float|null Expiration time of the callback at the top of the heap or null if the heap is empty.
     */
    public function peek(): ?float
    {
        return isset($this->callbacks[0]) ? $this->callbacks[0]->expiration : null;
    }

    /**
     * @param int $node Rebuild the data array from the given node upward.
     */
    private function heapifyUp(int $node): void
    {
        $entry = $this->callbacks[$node];
        while ($node !== 0 && $entry->expiration < $this->callbacks[$parent = ($node - 1) >> 1]->expiration) {
            $this->swap($node, $parent);
            $node = $parent;
        }
    }

    /**
     * @param int $node Rebuild the data array from the given node downward.
     */
    private function heapifyDown(int $node): void
    {
        $length = \count($this->callbacks);
        while (($child = ($node << 1) + 1) < $length) {
            if ($this->callbacks[$child]->expiration < $this->callbacks[$node]->expiration
                && ($child + 1 >= $length || $this->callbacks[$child]->expiration < $this->callbacks[$child + 1]->expiration)
            ) {
                // Left child is less than parent and right child.
                $swap = $child;
            } elseif ($child + 1 < $length && $this->callbacks[$child + 1]->expiration < $this->callbacks[$node]->expiration) {
                // Right child is less than parent and left child.
                $swap = $child + 1;
            } else { // Left and right child are greater than parent.
                break;
            }

            $this->swap($node, $swap);
            $node = $swap;
        }
    }

    private function swap(int $left, int $right): void
    {
        $temp = $this->callbacks[$left];

        $this->callbacks[$left] = $this->callbacks[$right];
        $this->pointers[$this->callbacks[$right]->id] = $left;

        $this->callbacks[$right] = $temp;
        $this->pointers[$temp->id] = $right;
    }

    /**
     * @param int $node Remove the given node and then rebuild the data array.
     */
    private function removeAndRebuild(int $node): void
    {
        $length = \count($this->callbacks) - 1;
        $id = $this->callbacks[$node]->id;
        $left = $this->callbacks[$node] = $this->callbacks[$length];
        $this->pointers[$left->id] = $node;
        unset($this->callbacks[$length], $this->pointers[$id]);

        if ($node < $length) { // don't need to do anything if we removed the last element
            $parent = ($node - 1) >> 1;
            if ($parent >= 0 && $this->callbacks[$node]->expiration < $this->callbacks[$parent]->expiration) {
                $this->heapifyUp($node);
            } else {
                $this->heapifyDown($node);
            }
        }
    }
}
<?php

declare(strict_types=1);

namespace Revolt\EventLoop\Internal;

/** @internal */
final class SignalCallback extends DriverCallback
{
    public function __construct(
        string $id,
        \Closure $closure,
        public readonly int $signal
    ) {
        parent::__construct($id, $closure);
    }
}
<?php

declare(strict_types=1);

namespace Revolt\EventLoop\Internal;

/** @internal */
abstract class StreamCallback extends DriverCallback
{
    /**
     * @param resource $stream
     */
    public function __construct(
        string $id,
        \Closure $closure,
        public readonly mixed $stream
    ) {
        parent::__construct($id, $closure);
    }
}
<?php

declare(strict_types=1);

namespace Revolt\EventLoop\Internal;

/** @internal */
final class DeferCallback extends DriverCallback
{
}
<?php

declare(strict_types=1);

namespace Revolt\EventLoop\Internal;

/** @internal */
final class ClosureHelper
{
    public static function getDescription(\Closure $closure): string
    {
        try {
            $reflection = new \ReflectionFunction($closure);

            $description = $reflection->name;

            if ($scopeClass = $reflection->getClosureScopeClass()) {
                $description = $scopeClass->name . '::' . $description;
            }

            if ($reflection->getFileName() !== false && $reflection->getStartLine()) {
                $description .= " defined in " . $reflection->getFileName() . ':' . $reflection->getStartLine();
            }

            return $description;
        } catch (\ReflectionException) {
            return '???';
        }
    }
}
<?php

declare(strict_types=1);

namespace Revolt\EventLoop\Internal;

use Revolt\EventLoop\CallbackType;
use Revolt\EventLoop\Driver;
use Revolt\EventLoop\FiberLocal;
use Revolt\EventLoop\InvalidCallbackError;
use Revolt\EventLoop\Suspension;
use Revolt\EventLoop\UncaughtThrowable;

/**
 * Event loop driver which implements all basic operations to allow interoperability.
 *
 * Callbacks (enabled or new callbacks) MUST immediately be marked as enabled, but only be activated (i.e. callbacks can
 * be called) right before the next tick. Callbacks MUST NOT be called in the tick they were enabled.
 *
 * @internal
 */
abstract class AbstractDriver implements Driver
{
    /** @var string Next callback identifier. */
    private string $nextId = "a";

    private \Fiber $fiber;

    private \Fiber $callbackFiber;
    private \Closure $errorCallback;

    /** @var array<string, DriverCallback> */
    private array $callbacks = [];

    /** @var array<string, DriverCallback> */
    private array $enableQueue = [];

    /** @var array<string, DriverCallback> */
    private array $enableDeferQueue = [];

    /** @var null|\Closure(\Throwable):void */
    private ?\Closure $errorHandler = null;

    /** @var null|\Closure():mixed */
    private ?\Closure $interrupt = null;

    private readonly \Closure $interruptCallback;
    private readonly \Closure $queueCallback;

    /** @var \Closure():(null|\Closure(): mixed) */
    private readonly \Closure $runCallback;

    private readonly \stdClass $internalSuspensionMarker;

    /** @var \SplQueue<array{\Closure, array}> */
    private readonly \SplQueue $microtaskQueue;

    /** @var \SplQueue<DriverCallback> */
    private readonly \SplQueue $callbackQueue;

    private bool $idle = false;
    private bool $stopped = false;

    /** @var \WeakMap<object, \WeakReference<DriverSuspension>> */
    private \WeakMap $suspensions;

    public function __construct()
    {
        if (\PHP_VERSION_ID < 80117 || \PHP_VERSION_ID >= 80200 && \PHP_VERSION_ID < 80204) {
            // PHP GC is broken on early 8.1 and 8.2 versions, see https://github.com/php/php-src/issues/10496
            /** @psalm-suppress RiskyTruthyFalsyComparison */
            if (!\getenv('REVOLT_DRIVER_SUPPRESS_ISSUE_10496')) {
                throw new \Error('Your version of PHP is affected by serious garbage collector bugs related to fibers. Please upgrade to a newer version of PHP, i.e. >= 8.1.17 or => 8.2.4');
            }
        }

        $this->suspensions = new \WeakMap();

        $this->internalSuspensionMarker = new \stdClass();
        $this->microtaskQueue = new \SplQueue();
        $this->callbackQueue = new \SplQueue();

        $this->createLoopFiber();
        $this->createCallbackFiber();
        $this->createErrorCallback();

        /** @psalm-suppress InvalidArgument */
        $this->interruptCallback = $this->setInterrupt(...);
        $this->queueCallback = $this->queue(...);
        $this->runCallback = function (): ?\Closure {
            do {
                if ($this->fiber->isTerminated()) {
                    $this->createLoopFiber();
                }

                $result = $this->fiber->isStarted() ? $this->fiber->resume() : $this->fiber->start();
                if ($result) { // Null indicates the loop fiber terminated without suspending.
                    return $result;
                }
            } while (\gc_collect_cycles() && !$this->stopped);

            return null;
        };
    }

    #[\Override]
    public function run(): void
    {
        if ($this->fiber->isRunning()) {
            throw new \Error("The event loop is already running");
        }

        if (\Fiber::getCurrent()) {
            throw new \Error(\sprintf("Can't call %s() within a fiber (i.e., outside of {main})", __METHOD__));
        }

        $lambda = ($this->runCallback)();

        if ($lambda) {
            $lambda();

            throw new \Error(
                'Interrupt from event loop must throw an exception: ' . ClosureHelper::getDescription($lambda)
            );
        }
    }

    #[\Override]
    public function stop(): void
    {
        $this->stopped = true;
    }

    #[\Override]
    public function isRunning(): bool
    {
        return $this->fiber->isRunning() || $this->fiber->isSuspended();
    }

    #[\Override]
    public function queue(\Closure $closure, mixed ...$args): void
    {
        $this->microtaskQueue->enqueue([$closure, $args]);
    }

    #[\Override]
    public function defer(\Closure $closure): string
    {
        $deferCallback = new DeferCallback($this->callbackId(), $closure);

        $this->callbacks[$deferCallback->id] = $deferCallback;
        $this->enableDeferQueue[$deferCallback->id] = $deferCallback;

        return $deferCallback->id;
    }

    #[\Override]
    public function delay(float $delay, \Closure $closure): string
    {
        if ($delay < 0) {
            throw new \Error("Delay must be greater than or equal to zero");
        }

        $timerCallback = new TimerCallback($this->callbackId(), $delay, $closure, $this->now() + $delay);

        $this->callbacks[$timerCallback->id] = $timerCallback;
        $this->enableQueue[$timerCallback->id] = $timerCallback;

        return $timerCallback->id;
    }

    #[\Override]
    public function repeat(float $interval, \Closure $closure): string
    {
        if ($interval < 0) {
            throw new \Error("Interval must be greater than or equal to zero");
        }

        $timerCallback = new TimerCallback($this->callbackId(), $interval, $closure, $this->now() + $interval, true);

        $this->callbacks[$timerCallback->id] = $timerCallback;
        $this->enableQueue[$timerCallback->id] = $timerCallback;

        return $timerCallback->id;
    }

    #[\Override]
    public function onReadable(mixed $stream, \Closure $closure): string
    {
        $streamCallback = new StreamReadableCallback($this->callbackId(), $closure, $stream);

        $this->callbacks[$streamCallback->id] = $streamCallback;
        $this->enableQueue[$streamCallback->id] = $streamCallback;

        return $streamCallback->id;
    }

    #[\Override]
    public function onWritable($stream, \Closure $closure): string
    {
        $streamCallback = new StreamWritableCallback($this->callbackId(), $closure, $stream);

        $this->callbacks[$streamCallback->id] = $streamCallback;
        $this->enableQueue[$streamCallback->id] = $streamCallback;

        return $streamCallback->id;
    }

    #[\Override]
    public function onSignal(int $signal, \Closure $closure): string
    {
        $signalCallback = new SignalCallback($this->callbackId(), $closure, $signal);

        $this->callbacks[$signalCallback->id] = $signalCallback;
        $this->enableQueue[$signalCallback->id] = $signalCallback;

        return $signalCallback->id;
    }

    #[\Override]
    public function enable(string $callbackId): string
    {
        if (!isset($this->callbacks[$callbackId])) {
            throw InvalidCallbackError::invalidIdentifier($callbackId);
        }

        $callback = $this->callbacks[$callbackId];

        if ($callback->enabled) {
            return $callbackId; // Callback already enabled.
        }

        $callback->enabled = true;

        if ($callback instanceof DeferCallback) {
            $this->enableDeferQueue[$callback->id] = $callback;
        } elseif ($callback instanceof TimerCallback) {
            $callback->expiration = $this->now() + $callback->interval;
            $this->enableQueue[$callback->id] = $callback;
        } else {
            $this->enableQueue[$callback->id] = $callback;
        }

        return $callbackId;
    }

    #[\Override]
    public function cancel(string $callbackId): void
    {
        $this->disable($callbackId);
        unset($this->callbacks[$callbackId]);
    }

    #[\Override]
    public function disable(string $callbackId): string
    {
        if (!isset($this->callbacks[$callbackId])) {
            return $callbackId;
        }

        $callback = $this->callbacks[$callbackId];

        if (!$callback->enabled) {
            return $callbackId; // Callback already disabled.
        }

        $callback->enabled = false;
        $callback->invokable = false;
        $id = $callback->id;

        if ($callback instanceof DeferCallback) {
            // Callback was only queued to be enabled.
            unset($this->enableDeferQueue[$id]);
        } elseif (isset($this->enableQueue[$id])) {
            // Callback was only queued to be enabled.
            unset($this->enableQueue[$id]);
        } else {
            $this->deactivate($callback);
        }

        return $callbackId;
    }

    #[\Override]
    public function reference(string $callbackId): string
    {
        if (!isset($this->callbacks[$callbackId])) {
            throw InvalidCallbackError::invalidIdentifier($callbackId);
        }

        $this->callbacks[$callbackId]->referenced = true;

        return $callbackId;
    }

    #[\Override]
    public function unreference(string $callbackId): string
    {
        if (!isset($this->callbacks[$callbackId])) {
            return $callbackId;
        }

        $this->callbacks[$callbackId]->referenced = false;

        return $callbackId;
    }

    #[\Override]
    public function getSuspension(): Suspension
    {
        $fiber = \Fiber::getCurrent();

        // User callbacks are always executed outside the event loop fiber, so this should always be false.
        \assert($fiber !== $this->fiber);

        // Use queue closure in case of {main}, which can be unset by DriverSuspension after an uncaught exception.
        $key = $fiber ?? $this->queueCallback;

        $suspension = ($this->suspensions[$key] ?? null)?->get();
        if ($suspension) {
            return $suspension;
        }

        $suspension = new DriverSuspension(
            $this->runCallback,
            $this->queueCallback,
            $this->interruptCallback,
            $this->suspensions,
        );

        $this->suspensions[$key] = \WeakReference::create($suspension);

        return $suspension;
    }

    #[\Override]
    public function setErrorHandler(?\Closure $errorHandler): void
    {
        $this->errorHandler = $errorHandler;
    }

    #[\Override]
    public function getErrorHandler(): ?\Closure
    {
        return $this->errorHandler;
    }

    #[\Override]
    public function __debugInfo(): array
    {
        // @codeCoverageIgnoreStart
        return \array_map(fn (DriverCallback $callback) => [
            'type' => $this->getType($callback->id),
            'enabled' => $callback->enabled,
            'referenced' => $callback->referenced,
        ], $this->callbacks);
        // @codeCoverageIgnoreEnd
    }

    #[\Override]
    public function getIdentifiers(): array
    {
        return \array_keys($this->callbacks);
    }

    #[\Override]
    public function getType(string $callbackId): CallbackType
    {
        $callback = $this->callbacks[$callbackId] ?? throw InvalidCallbackError::invalidIdentifier($callbackId);

        return match ($callback::class) {
            DeferCallback::class => CallbackType::Defer,
            TimerCallback::class => $callback->repeat ? CallbackType::Repeat : CallbackType::Delay,
            StreamReadableCallback::class => CallbackType::Readable,
            StreamWritableCallback::class => CallbackType::Writable,
            SignalCallback::class => CallbackType::Signal,
        };
    }

    #[\Override]
    public function isEnabled(string $callbackId): bool
    {
        $callback = $this->callbacks[$callbackId] ?? throw InvalidCallbackError::invalidIdentifier($callbackId);

        return $callback->enabled;
    }

    #[\Override]
    public function isReferenced(string $callbackId): bool
    {
        $callback = $this->callbacks[$callbackId] ?? throw InvalidCallbackError::invalidIdentifier($callbackId);

        return $callback->referenced;
    }

    /**
     * Activates (enables) all the given callbacks.
     */
    abstract protected function activate(array $callbacks): void;

    /**
     * Dispatches any pending read/write, timer, and signal events.
     */
    abstract protected function dispatch(bool $blocking): void;

    /**
     * Deactivates (disables) the given callback.
     */
    abstract protected function deactivate(DriverCallback $callback): void;

    final protected function enqueueCallback(DriverCallback $callback): void
    {
        $this->callbackQueue->enqueue($callback);
        $this->idle = false;
    }

    /**
     * Invokes the error handler with the given exception.
     *
     * @param \Throwable $exception The exception thrown from an event callback.
     */
    final protected function error(\Closure $closure, \Throwable $exception): void
    {
        if ($this->errorHandler === null) {
            // Explicitly override the previous interrupt if it exists in this case, hiding the exception is worse
            $this->interrupt = static fn () => $exception instanceof UncaughtThrowable
                ? throw $exception
                : throw UncaughtThrowable::throwingCallback($closure, $exception);
            return;
        }

        $fiber = new \Fiber($this->errorCallback);

        /** @noinspection PhpUnhandledExceptionInspection */
        $fiber->start($this->errorHandler, $exception);
    }

    /**
     * Returns the current event loop time in second increments.
     *
     * Note this value does not necessarily correlate to wall-clock time, rather the value returned is meant to be used
     * in relative comparisons to prior values returned by this method (intervals, expiration calculations, etc.).
     */
    abstract protected function now(): float;

    private function invokeMicrotasks(): void
    {
        while (!$this->microtaskQueue->isEmpty()) {
            [$callback, $args] = $this->microtaskQueue->dequeue();

            try {
                // Clear $args to allow garbage collection
                $callback(...$args, ...($args = []));
            } catch (\Throwable $exception) {
                $this->error($callback, $exception);
            } finally {
                FiberLocal::clear();
            }

            unset($callback, $args);

            if ($this->interrupt) {
                /** @noinspection PhpUnhandledExceptionInspection */
                \Fiber::suspend($this->internalSuspensionMarker);
            }
        }
    }

    /**
     * @return bool True if no enabled and referenced callbacks remain in the loop.
     */
    private function isEmpty(): bool
    {
        foreach ($this->callbacks as $callback) {
            if ($callback->enabled && $callback->referenced) {
                return false;
            }
        }

        return true;
    }

    /**
     * Executes a single tick of the event loop.
     */
    private function tick(bool $previousIdle): void
    {
        $this->activate($this->enableQueue);

        foreach ($this->enableQueue as $callback) {
            $callback->invokable = true;
        }

        $this->enableQueue = [];

        foreach ($this->enableDeferQueue as $callback) {
            $callback->invokable = true;
            $this->enqueueCallback($callback);
        }

        $this->enableDeferQueue = [];

        $blocking = $previousIdle
            && !$this->stopped
            && !$this->isEmpty();

        if ($blocking) {
            $this->invokeCallbacks();

            /** @psalm-suppress TypeDoesNotContainType */
            if (!empty($this->enableDeferQueue) || !empty($this->enableQueue)) {
                $blocking = false;
            }
        }

        /** @psalm-suppress RedundantCondition */
        $this->dispatch($blocking);
    }

    private function invokeCallbacks(): void
    {
        while (!$this->microtaskQueue->isEmpty() || !$this->callbackQueue->isEmpty()) {
            /** @noinspection PhpUnhandledExceptionInspection */
            if ($this->callbackFiber->isSuspended()) {
                $yielded = $this->callbackFiber->resume();
            } else {
                if ($this->callbackFiber->isTerminated()) {
                    $this->createCallbackFiber();
                }
                $yielded = $this->callbackFiber->start();
            }

            if ($yielded !== $this->internalSuspensionMarker) {
                $this->createCallbackFiber();
            }

            if ($this->interrupt) {
                $this->invokeInterrupt();
            }
        }
    }

    /**
     * @param \Closure():mixed $interrupt
     */
    private function setInterrupt(\Closure $interrupt): void
    {
        \assert($this->interrupt === null);

        $this->interrupt = $interrupt;
    }

    private function invokeInterrupt(): void
    {
        \assert($this->interrupt !== null);

        $interrupt = $this->interrupt;
        $this->interrupt = null;

        /** @noinspection PhpUnhandledExceptionInspection */
        \Fiber::suspend($interrupt);
    }

    private function createLoopFiber(): void
    {
        $this->fiber = new \Fiber(function (): void {
            $this->stopped = false;

            // Invoke microtasks if we have some
            $this->invokeCallbacks();

            /** @psalm-suppress RedundantCondition $this->stopped may be changed by $this->invokeCallbacks(). */
            while (!$this->stopped) {
                if ($this->interrupt) {
                    $this->invokeInterrupt();
                }

                if ($this->isEmpty()) {
                    return;
                }

                $previousIdle = $this->idle;
                $this->idle = true;

                $this->tick($previousIdle);
                $this->invokeCallbacks();
            }
        });
    }

    private function createCallbackFiber(): void
    {
        $this->callbackFiber = new \Fiber(function (): void {
            do {
                $this->invokeMicrotasks();

                while (!$this->callbackQueue->isEmpty()) {
                    $callback = $this->callbackQueue->dequeue();

                    if (!isset($this->callbacks[$callback->id]) || !$callback->invokable) {
                        unset($callback);

                        continue;
                    }

                    if ($callback instanceof DeferCallback) {
                        $this->cancel($callback->id);
                    } elseif ($callback instanceof TimerCallback) {
                        if (!$callback->repeat) {
                            $this->cancel($callback->id);
                        } else {
                            // Disable and re-enable, so it's not executed repeatedly in the same tick
                            // See https://github.com/amphp/amp/issues/131
                            $this->disable($callback->id);
                            $this->enable($callback->id);
                        }
                    }

                    try {
                        $result = match (true) {
                            $callback instanceof StreamCallback => ($callback->closure)(
                                $callback->id,
                                $callback->stream
                            ),
                            $callback instanceof SignalCallback => ($callback->closure)(
                                $callback->id,
                                $callback->signal
                            ),
                            default => ($callback->closure)($callback->id),
                        };

                        if ($result !== null) {
                            throw InvalidCallbackError::nonNullReturn($callback->id, $callback->closure);
                        }
                    } catch (\Throwable $exception) {
                        $this->error($callback->closure, $exception);
                    } finally {
                        FiberLocal::clear();
                    }

                    unset($callback);

                    if ($this->interrupt) {
                        /** @noinspection PhpUnhandledExceptionInspection */
                        \Fiber::suspend($this->internalSuspensionMarker);
                    }

                    $this->invokeMicrotasks();
                }

                /** @noinspection PhpUnhandledExceptionInspection */
                \Fiber::suspend($this->internalSuspensionMarker);
            } while (true);
        });
    }

    private function createErrorCallback(): void
    {
        $this->errorCallback = function (\Closure $errorHandler, \Throwable $exception): void {
            try {
                $errorHandler($exception);
            } catch (\Throwable $exception) {
                $this->interrupt = static fn () => $exception instanceof UncaughtThrowable
                    ? throw $exception
                    : throw UncaughtThrowable::throwingErrorHandler($errorHandler, $exception);
            }
        };
    }

    private function callbackId(): string
    {
        $callbackId = $this->nextId;

        if (\PHP_VERSION_ID >= 80300) {
            /** @psalm-suppress UndefinedFunction */
            $this->nextId = \str_increment($this->nextId);
        } else {
            $this->nextId++;
        }

        return $callbackId;
    }

    final public function __serialize(): never
    {
        throw new \Error(__CLASS__ . ' does not support serialization');
    }

    /**
     * @psalm-suppress UnusedParam $data param required.
     */
    final public function __unserialize(array $data): never
    {
        throw new \Error(__CLASS__ . ' does not support deserialization');
    }
}
<?php

declare(strict_types=1);

namespace Revolt\EventLoop;

/**
 * Should be used to run and suspend the event loop instead of directly interacting with fibers.
 *
 * **Example**
 *
 * ```php
 * $suspension = EventLoop::getSuspension();
 *
 * $promise->then(
 *     fn (mixed $value) => $suspension->resume($value),
 *     fn (Throwable $error) => $suspension->throw($error)
 * );
 *
 * $suspension->suspend();
 * ```
 *
 * @template T
 */
interface Suspension
{
    /**
     * @param T $value The value to return from the call to {@see suspend()}.
     */
    public function resume(mixed $value = null): void;

    /**
     * Returns the value provided to {@see resume()} or throws the exception provided to {@see throw()}.
     *
     * @return T
     */
    public function suspend(): mixed;

    /**
     * Throws the given exception from the call to {@see suspend()}.
     */
    public function throw(\Throwable $throwable): void;
}
<?php

declare(strict_types=1);

namespace Revolt\EventLoop;

/**
 * MUST be thrown if a feature is not supported by the system.
 *
 * This might happen if ext-pcntl is missing and the loop driver doesn't support another way to dispatch signals.
 */
final class UnsupportedFeatureException extends \Exception
{
}
<?php

declare(strict_types=1);

namespace Revolt\EventLoop;

/**
 * Fiber local storage.
 *
 * Each instance stores data separately for each fiber. Usage examples include contextual logging data.
 *
 * @template T
 */
final class FiberLocal
{
    /** @var \Fiber|null Dummy fiber for {main} */
    private static ?\Fiber $mainFiber = null;
    private static ?\WeakMap $localStorage = null;

    public static function clear(): void
    {
        if (self::$localStorage === null) {
            return;
        }

        $fiber = \Fiber::getCurrent() ?? self::$mainFiber;

        if ($fiber === null) {
            return;
        }

        unset(self::$localStorage[$fiber]);
    }

    private static function getFiberStorage(): \WeakMap
    {
        $fiber = \Fiber::getCurrent();

        if ($fiber === null) {
            $fiber = self::$mainFiber ??= new \Fiber(static function (): void {
                // dummy fiber for main, as we need some object for the WeakMap
            });
        }

        $localStorage = self::$localStorage ??= new \WeakMap();
        return $localStorage[$fiber] ??= new \WeakMap();
    }

    /**
     * @param \Closure():T $initializer
     */
    public function __construct(private readonly \Closure $initializer)
    {
    }

    /**
     * @param T $value
     */
    public function set(mixed $value): void
    {
        self::getFiberStorage()[$this] = [$value];
    }

    public function unset(): void
    {
        unset(self::getFiberStorage()[$this]);
    }

    /**
     * @return T
     */
    public function get(): mixed
    {
        $fiberStorage = self::getFiberStorage();

        if (!isset($fiberStorage[$this])) {
            $fiberStorage[$this] = [($this->initializer)()];
        }

        return $fiberStorage[$this][0];
    }
}
<?php

declare(strict_types=1);

namespace Revolt\EventLoop;

use Revolt\EventLoop\Internal\ClosureHelper;

final class UncaughtThrowable extends \Error
{
    public static function throwingCallback(\Closure $closure, \Throwable $previous): self
    {
        return new self(
            "Uncaught %s thrown in event loop callback %s; use Revolt\EventLoop::setErrorHandler() to gracefully handle such exceptions%s",
            $closure,
            $previous
        );
    }

    public static function throwingErrorHandler(\Closure $closure, \Throwable $previous): self
    {
        return new self("Uncaught %s thrown in event loop error handler %s%s", $closure, $previous);
    }

    private function __construct(string $message, \Closure $closure, \Throwable $previous)
    {
        parent::__construct(\sprintf(
            $message,
            \str_replace("\0", '@', \get_class($previous)), // replace NUL-byte in anonymous class name
            ClosureHelper::getDescription($closure),
            $previous->getMessage() !== '' ? ': ' . $previous->getMessage() : ''
        ), 0, $previous);
    }
}
{
    "name": "revolt/event-loop",
    "description": "Rock-solid event loop for concurrent PHP applications.",
    "keywords": [
        "async",
        "asynchronous",
        "concurrency",
        "non-blocking",
        "event",
        "event-loop",
        "scheduler"
    ],
    "license": "MIT",
    "authors": [
        {
            "name": "Aaron Piotrowski",
            "email": "aaron@trowski.com"
        },
        {
            "name": "Cees-Jan Kiewiet",
            "email": "ceesjank@gmail.com"
        },
        {
            "name": "Christian LÃ¼ck",
            "email": "christian@clue.engineering"
        },
        {
            "name": "Niklas Keller",
            "email": "me@kelunik.com"
        }
    ],
    "require": {
        "php": ">=8.1"
    },
    "require-dev": {
        "ext-json": "*",
        "phpunit/phpunit": "^9",
        "jetbrains/phpstorm-stubs": "^2019.3",
        "psalm/phar": "6.16.*"
    },
    "autoload": {
        "psr-4": {
            "Revolt\\": "src"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Revolt\\EventLoop\\": "test"
        }
    },
    "support": {
        "issues": "https://github.com/revoltphp/event-loop/issues"
    },
    "extra": {
        "branch-alias": {
            "dev-main": "1.x-dev"
        }
    },
    "scripts": {
        "test": "@php -dzend.assertions=1 -dassert.exception=1 ./vendor/bin/phpunit",
        "code-style": "@php tools/php-cs-fixer/vendor/bin/php-cs-fixer fix"
    }
}
<?php

declare(strict_types=1);

namespace Prometheus;

use Prometheus\Exception\MetricNotFoundException;
use Prometheus\Exception\MetricsRegistrationException;
use Prometheus\Storage\Adapter;
use Prometheus\Storage\Redis;

class CollectorRegistry implements RegistryInterface
{
    /**
     * @var CollectorRegistry
     */
    private static $defaultRegistry;

    /**
     * @var Adapter
     */
    private $storageAdapter;

    /**
     * @var Gauge[]
     */
    private $gauges = [];

    /**
     * @var Counter[]
     */
    private $counters = [];

    /**
     * @var Histogram[]
     */
    private $histograms = [];

    /**
     * @var Summary[]
     */
    private $summaries = [];

    /**
     * @var Gauge[]
     */
    private $defaultGauges = [];

    /**
     * CollectorRegistry constructor.
     *
     * @param Adapter $storageAdapter
     * @param bool $registerDefaultMetrics
     */
    public function __construct(Adapter $storageAdapter, bool $registerDefaultMetrics = true)
    {
        $this->storageAdapter = $storageAdapter;
        if ($registerDefaultMetrics) {
            $this->registerDefaultMetrics();
        }
    }

    /**
     * @return CollectorRegistry
     */
    public static function getDefault(): CollectorRegistry
    {
        return self::$defaultRegistry ?? (self::$defaultRegistry = new self(new Redis()));  /** @phpstan-ignore-line */
    }

    /**
     * Removes all previously stored metrics from underlying storage adapter
     *
     * @return void
     */
    public function wipeStorage(): void
    {
        $this->storageAdapter->wipeStorage();
    }

    /**
     * @return MetricFamilySamples[]
     */
    public function getMetricFamilySamples(bool $sortMetrics = true): array
    {
        return $this->storageAdapter->collect($sortMetrics);  /** @phpstan-ignore-line */
    }

    /**
     * @param string $namespace e.g. cms
     * @param string $name e.g. duration_seconds
     * @param string $help e.g. The duration something took in seconds.
     * @param string[] $labels e.g. ['controller', 'action']
     *
     * @return Gauge
     * @throws MetricsRegistrationException
     */
    public function registerGauge(string $namespace, string $name, string $help, array $labels = []): Gauge
    {
        $metricIdentifier = self::metricIdentifier($namespace, $name);
        if (isset($this->gauges[$metricIdentifier])) {
            throw new MetricsRegistrationException("Metric ` . $metricIdentifier . ` already registered");
        }
        $this->gauges[$metricIdentifier] = new Gauge(
            $this->storageAdapter,
            $namespace,
            $name,
            $help,
            $labels
        );
        return $this->gauges[$metricIdentifier];
    }

    /**
     * @param string $namespace
     * @param string $name
     *
     * @return Gauge
     * @throws MetricNotFoundException
     */
    public function getGauge(string $namespace, string $name): Gauge
    {
        $metricIdentifier = self::metricIdentifier($namespace, $name);
        if (!isset($this->gauges[$metricIdentifier])) {
            throw new MetricNotFoundException("Metric not found:" . $metricIdentifier);
        }
        return $this->gauges[$metricIdentifier];
    }

    /**
     * @param string $namespace e.g. cms
     * @param string $name e.g. duration_seconds
     * @param string $help e.g. The duration something took in seconds.
     * @param string[] $labels e.g. ['controller', 'action']
     *
     * @return Gauge
     * @throws MetricsRegistrationException
     */
    public function getOrRegisterGauge(string $namespace, string $name, string $help, array $labels = []): Gauge
    {
        try {
            $gauge = $this->getGauge($namespace, $name);
        } catch (MetricNotFoundException $e) {
            $gauge = $this->registerGauge($namespace, $name, $help, $labels);
        }
        return $gauge;
    }

    /**
     * @param string $namespace e.g. cms
     * @param string $name e.g. requests
     * @param string $help e.g. The number of requests made.
     * @param string[] $labels e.g. ['controller', 'action']
     *
     * @return Counter
     * @throws MetricsRegistrationException
     */
    public function registerCounter(string $namespace, string $name, string $help, array $labels = []): Counter
    {
        $metricIdentifier = self::metricIdentifier($namespace, $name);
        if (isset($this->counters[$metricIdentifier])) {
            throw new MetricsRegistrationException("Metric ` . $metricIdentifier . ` already registered");
        }
        $this->counters[$metricIdentifier] = new Counter(
            $this->storageAdapter,
            $namespace,
            $name,
            $help,
            $labels
        );
        return $this->counters[self::metricIdentifier($namespace, $name)];
    }

    /**
     * @param string $namespace
     * @param string $name
     *
     * @return Counter
     * @throws MetricNotFoundException
     */
    public function getCounter(string $namespace, string $name): Counter
    {
        $metricIdentifier = self::metricIdentifier($namespace, $name);
        if (!isset($this->counters[$metricIdentifier])) {
            throw new MetricNotFoundException("Metric not found:" . $metricIdentifier);
        }
        return $this->counters[self::metricIdentifier($namespace, $name)];
    }

    /**
     * @param string $namespace e.g. cms
     * @param string $name e.g. requests
     * @param string $help e.g. The number of requests made.
     * @param string[] $labels e.g. ['controller', 'action']
     *
     * @return Counter
     * @throws MetricsRegistrationException
     */
    public function getOrRegisterCounter(string $namespace, string $name, string $help, array $labels = []): Counter
    {
        try {
            $counter = $this->getCounter($namespace, $name);
        } catch (MetricNotFoundException $e) {
            $counter = $this->registerCounter($namespace, $name, $help, $labels);
        }
        return $counter;
    }

    /**
     * @param string $namespace e.g. cms
     * @param string $name e.g. duration_seconds
     * @param string $help e.g. A histogram of the duration in seconds.
     * @param string[] $labels e.g. ['controller', 'action']
     * @param float[]|null $buckets e.g. [100.0, 200.0, 300.0]
     *
     * @return Histogram
     * @throws MetricsRegistrationException
     */
    public function registerHistogram(
        string $namespace,
        string $name,
        string $help,
        array $labels = [],
        ?array $buckets = null
    ): Histogram {
        $metricIdentifier = self::metricIdentifier($namespace, $name);
        if (isset($this->histograms[$metricIdentifier])) {
            throw new MetricsRegistrationException("Metric ` . $metricIdentifier . ` already registered");
        }
        $this->histograms[$metricIdentifier] = new Histogram(
            $this->storageAdapter,
            $namespace,
            $name,
            $help,
            $labels,
            $buckets
        );
        return $this->histograms[$metricIdentifier];
    }

    /**
     * @param string $namespace
     * @param string $name
     *
     * @return Histogram
     * @throws MetricNotFoundException
     */
    public function getHistogram(string $namespace, string $name): Histogram
    {
        $metricIdentifier = self::metricIdentifier($namespace, $name);
        if (!isset($this->histograms[$metricIdentifier])) {
            throw new MetricNotFoundException("Metric not found:" . $metricIdentifier);
        }
        return $this->histograms[self::metricIdentifier($namespace, $name)];
    }

    /**
     * @param string $namespace e.g. cms
     * @param string $name e.g. duration_seconds
     * @param string $help e.g. A histogram of the duration in seconds.
     * @param string[] $labels e.g. ['controller', 'action']
     * @param float[]|null $buckets e.g. [100.0, 200.0, 300.0]
     *
     * @return Histogram
     * @throws MetricsRegistrationException
     */
    public function getOrRegisterHistogram(
        string $namespace,
        string $name,
        string $help,
        array $labels = [],
        ?array $buckets = null
    ): Histogram {
        try {
            $histogram = $this->getHistogram($namespace, $name);
        } catch (MetricNotFoundException $e) {
            $histogram = $this->registerHistogram($namespace, $name, $help, $labels, $buckets);
        }
        return $histogram;
    }


    /**
     * @param string $namespace e.g. cms
     * @param string $name e.g. duration_seconds
     * @param string $help e.g. A summary of the duration in seconds.
     * @param string[] $labels e.g. ['controller', 'action']
     * @param int $maxAgeSeconds e.g. 604800
     * @param float[]|null $quantiles e.g. [0.01, 0.5, 0.99]
     *
     * @return Summary
     * @throws MetricsRegistrationException
     */
    public function registerSummary(
        string $namespace,
        string $name,
        string $help,
        array $labels = [],
        int $maxAgeSeconds = 600,
        ?array $quantiles = null
    ): Summary {
        $metricIdentifier = self::metricIdentifier($namespace, $name);
        if (isset($this->summaries[$metricIdentifier])) {
            throw new MetricsRegistrationException("Metric ` . $metricIdentifier . ` already registered");
        }
        $this->summaries[$metricIdentifier] = new Summary(
            $this->storageAdapter,
            $namespace,
            $name,
            $help,
            $labels,
            $maxAgeSeconds,
            $quantiles
        );
        return $this->summaries[$metricIdentifier];
    }

    /**
     * @param string $namespace
     * @param string $name
     *
     * @return Summary
     * @throws MetricNotFoundException
     */
    public function getSummary(string $namespace, string $name): Summary
    {
        $metricIdentifier = self::metricIdentifier($namespace, $name);
        if (!isset($this->summaries[$metricIdentifier])) {
            throw new MetricNotFoundException("Metric not found:" . $metricIdentifier);
        }
        return $this->summaries[self::metricIdentifier($namespace, $name)];
    }

    /**
     * @param string $namespace e.g. cms
     * @param string $name e.g. duration_seconds
     * @param string $help e.g. A summary of the duration in seconds.
     * @param string[] $labels e.g. ['controller', 'action']
     * @param int $maxAgeSeconds e.g. 604800
     * @param float[]|null $quantiles e.g. [0.01, 0.5, 0.99]
     *
     * @return Summary
     * @throws MetricsRegistrationException
     */
    public function getOrRegisterSummary(
        string $namespace,
        string $name,
        string $help,
        array $labels = [],
        int $maxAgeSeconds = 600,
        ?array $quantiles = null
    ): Summary {
        try {
            $summary = $this->getSummary($namespace, $name);
        } catch (MetricNotFoundException $e) {
            $summary = $this->registerSummary($namespace, $name, $help, $labels, $maxAgeSeconds, $quantiles);
        }
        return $summary;
    }

    /**
     * @param string $namespace
     * @param string $name
     *
     * @return string
     */
    private static function metricIdentifier(string $namespace, string $name): string
    {
        return $namespace . ":" . $name;
    }

    private function registerDefaultMetrics(): void
    {
        $this->defaultGauges['php_info_gauge'] = $this->getOrRegisterGauge(
            "",
            "php_info",
            "Information about the PHP environment.",
            ["version"]
        );
        $this->defaultGauges['php_info_gauge']->set(1, [PHP_VERSION]);
    }
}
<?php

declare(strict_types=1);

namespace Prometheus\Storage;

use Prometheus\Math;
use Prometheus\MetricFamilySamples;
use RuntimeException;

class InMemory implements Adapter
{
    /**
     * @var mixed[]
     */
    protected $counters = [];

    /**
     * @var mixed[]
     */
    protected $gauges = [];

    /**
     * @var mixed[]
     */
    protected $histograms = [];

    /**
     * @var mixed[]
     */
    protected $summaries = [];

    /**
     * @return MetricFamilySamples[]
     */
    public function collect(bool $sortMetrics = true): array
    {
        $metrics = $this->internalCollect($this->counters, $sortMetrics);
        $metrics = array_merge($metrics, $this->internalCollect($this->gauges, $sortMetrics));
        $metrics = array_merge($metrics, $this->collectHistograms());
        $metrics = array_merge($metrics, $this->collectSummaries());
        return $metrics;
    }

    /**
     * @deprecated use replacement method wipeStorage from Adapter interface
     */
    public function flushMemory(): void
    {
        $this->wipeStorage();
    }

    /**
     * @inheritDoc
     */
    public function wipeStorage(): void
    {
        $this->counters = [];
        $this->gauges = [];
        $this->histograms = [];
        $this->summaries = [];
    }

    /**
     * @return MetricFamilySamples[]
     */
    protected function collectHistograms(): array
    {
        $histograms = [];
        foreach ($this->histograms as $histogram) {
            $metaData = $histogram['meta'];
            $data = [
                'name' => $metaData['name'],
                'help' => $metaData['help'],
                'type' => $metaData['type'],
                'labelNames' => $metaData['labelNames'],
                'buckets' => $metaData['buckets'],
            ];

            // Add the Inf bucket so we can compute it later on
            $data['buckets'][] = '+Inf';

            $histogramBuckets = [];
            foreach ($histogram['samples'] as $key => $value) {
                $parts = explode(':', $key);
                $labelValues = $parts[2];
                $bucket = $parts[3];
                // Key by labelValues
                $histogramBuckets[$labelValues][$bucket] = $value;
            }

            // Compute all buckets
            $labels = array_keys($histogramBuckets);
            sort($labels);
            foreach ($labels as $labelValues) {
                $acc = 0;
                $decodedLabelValues = $this->decodeLabelValues($labelValues);
                foreach ($data['buckets'] as $bucket) {
                    $bucket = (string)$bucket;
                    if (!isset($histogramBuckets[$labelValues][$bucket])) {
                        $data['samples'][] = [
                            'name' => $metaData['name'] . '_bucket',
                            'labelNames' => ['le'],
                            'labelValues' => array_merge($decodedLabelValues, [$bucket]),
                            'value' => $acc,
                        ];
                    } else {
                        $acc += $histogramBuckets[$labelValues][$bucket];
                        $data['samples'][] = [
                            'name' => $metaData['name'] . '_' . 'bucket',
                            'labelNames' => ['le'],
                            'labelValues' => array_merge($decodedLabelValues, [$bucket]),
                            'value' => $acc,
                        ];
                    }
                }

                // Add the count
                $data['samples'][] = [
                    'name' => $metaData['name'] . '_count',
                    'labelNames' => [],
                    'labelValues' => $decodedLabelValues,
                    'value' => $acc,
                ];

                // Add the sum
                $data['samples'][] = [
                    'name' => $metaData['name'] . '_sum',
                    'labelNames' => [],
                    'labelValues' => $decodedLabelValues,
                    'value' => $histogramBuckets[$labelValues]['sum'],
                ];
            }
            $histograms[] = new MetricFamilySamples($data);
        }
        return $histograms;
    }

    /**
     * @return MetricFamilySamples[]
     */
    protected function collectSummaries(): array
    {
        $math = new Math();
        $summaries = [];
        foreach ($this->summaries as $metaKey => &$summary) {
            $metaData = $summary['meta'];
            $data = [
                'name' => $metaData['name'],
                'help' => $metaData['help'],
                'type' => $metaData['type'],
                'labelNames' => $metaData['labelNames'],
                'maxAgeSeconds' => $metaData['maxAgeSeconds'],
                'quantiles' => $metaData['quantiles'],
                'samples' => [],
            ];

            foreach ($summary['samples'] as $key => &$values) {
                $parts = explode(':', $key);
                $labelValues = $parts[2];
                $decodedLabelValues = $this->decodeLabelValues($labelValues);

                // Remove old data
                $values = array_filter($values, function (array $value) use ($data): bool {
                    return time() - $value['time'] <= $data['maxAgeSeconds'];
                });
                if (count($values) === 0) {
                    unset($summary['samples'][$key]);
                    continue;
                }

                // Compute quantiles
                usort($values, function (array $value1, array $value2) {
                    if ($value1['value'] === $value2['value']) {
                        return 0;
                    }
                    return ($value1['value'] < $value2['value']) ? -1 : 1;
                });

                foreach ($data['quantiles'] as $quantile) {
                    $data['samples'][] = [
                        'name' => $metaData['name'],
                        'labelNames' => ['quantile'],
                        'labelValues' => array_merge($decodedLabelValues, [$quantile]),
                        'value' => $math->quantile(array_column($values, 'value'), $quantile),
                    ];
                }

                // Add the count
                $data['samples'][] = [
                    'name' => $metaData['name'] . '_count',
                    'labelNames' => [],
                    'labelValues' => $decodedLabelValues,
                    'value' => count($values),
                ];

                // Add the sum
                $data['samples'][] = [
                    'name' => $metaData['name'] . '_sum',
                    'labelNames' => [],
                    'labelValues' => $decodedLabelValues,
                    'value' => array_sum(array_column($values, 'value')),
                ];
            }
            if (count($data['samples']) > 0) {
                $summaries[] = new MetricFamilySamples($data);
            } else {
                unset($this->summaries[$metaKey]);
            }
        }
        return $summaries;
    }

    /**
     * @param mixed[] $metrics
     * @return MetricFamilySamples[]
     */
    protected function internalCollect(array $metrics, bool $sortMetrics = true): array
    {
        $result = [];
        foreach ($metrics as $metric) {
            $metaData = $metric['meta'];
            $data = [
                'name' => $metaData['name'],
                'help' => $metaData['help'],
                'type' => $metaData['type'],
                'labelNames' => $metaData['labelNames'],
                'samples' => [],
            ];
            foreach ($metric['samples'] as $key => $value) {
                $parts = explode(':', $key);
                $labelValues = $parts[2];
                $data['samples'][] = [
                    'name' => $metaData['name'],
                    'labelNames' => [],
                    'labelValues' => $this->decodeLabelValues($labelValues),
                    'value' => $value,
                ];
            }

            if ($sortMetrics) {
                $this->sortSamples($data['samples']);
            }

            $result[] = new MetricFamilySamples($data);
        }
        return $result;
    }

    /**
     * @param mixed[] $data
     * @return void
     */
    public function updateHistogram(array $data): void
    {
        // Initialize the sum
        $metaKey = $this->metaKey($data);
        if (array_key_exists($metaKey, $this->histograms) === false) {
            $this->histograms[$metaKey] = [
                'meta' => $this->metaData($data),
                'samples' => [],
            ];
        }
        $sumKey = $this->histogramBucketValueKey($data, 'sum');
        if (array_key_exists($sumKey, $this->histograms[$metaKey]['samples']) === false) {
            $this->histograms[$metaKey]['samples'][$sumKey] = 0;
        }

        $this->histograms[$metaKey]['samples'][$sumKey] += $data['value'];


        $bucketToIncrease = '+Inf';
        foreach ($data['buckets'] as $bucket) {
            if ($data['value'] <= $bucket) {
                $bucketToIncrease = $bucket;
                break;
            }
        }

        $bucketKey = $this->histogramBucketValueKey($data, $bucketToIncrease);
        if (array_key_exists($bucketKey, $this->histograms[$metaKey]['samples']) === false) {
            $this->histograms[$metaKey]['samples'][$bucketKey] = 0;
        }
        $this->histograms[$metaKey]['samples'][$bucketKey] += 1;
    }

    /**
     * @param mixed[] $data
     * @return void
     */
    public function updateSummary(array $data): void
    {
        $metaKey = $this->metaKey($data);
        if (array_key_exists($metaKey, $this->summaries) === false) {
            $this->summaries[$metaKey] = [
                'meta' => $this->metaData($data),
                'samples' => [],
            ];
        }

        $valueKey = $this->valueKey($data);
        if (array_key_exists($valueKey, $this->summaries[$metaKey]['samples']) === false) {
            $this->summaries[$metaKey]['samples'][$valueKey] = [];
        }

        $this->summaries[$metaKey]['samples'][$valueKey][] = [
            'time' => time(),
            'value' => $data['value'],
        ];
    }

    /**
     * @param mixed[] $data
     */
    public function updateGauge(array $data): void
    {
        $metaKey = $this->metaKey($data);
        $valueKey = $this->valueKey($data);
        if (array_key_exists($metaKey, $this->gauges) === false) {
            $this->gauges[$metaKey] = [
                'meta' => $this->metaData($data),
                'samples' => [],
            ];
        }
        if (array_key_exists($valueKey, $this->gauges[$metaKey]['samples']) === false) {
            $this->gauges[$metaKey]['samples'][$valueKey] = 0;
        }
        if ($data['command'] === Adapter::COMMAND_SET) {
            $this->gauges[$metaKey]['samples'][$valueKey] = $data['value'];
        } else {
            $this->gauges[$metaKey]['samples'][$valueKey] += $data['value'];
        }
    }

    /**
     * @param mixed[] $data
     */
    public function updateCounter(array $data): void
    {
        $metaKey = $this->metaKey($data);
        $valueKey = $this->valueKey($data);
        if (array_key_exists($metaKey, $this->counters) === false) {
            $this->counters[$metaKey] = [
                'meta' => $this->metaData($data),
                'samples' => [],
            ];
        }
        if (array_key_exists($valueKey, $this->counters[$metaKey]['samples']) === false) {
            $this->counters[$metaKey]['samples'][$valueKey] = 0;
        }
        if ($data['command'] === Adapter::COMMAND_SET) {
            $this->counters[$metaKey]['samples'][$valueKey] = 0;
        } else {
            $this->counters[$metaKey]['samples'][$valueKey] += $data['value'];
        }
    }

    /**
     * @param mixed[]    $data
     * @param string|int $bucket
     *
     * @return string
     */
    protected function histogramBucketValueKey(array $data, $bucket): string
    {
        return implode(':', [
            $data['type'],
            $data['name'],
            $this->encodeLabelValues($data['labelValues']),
            $bucket,
        ]);
    }

    /**
     * @param mixed[] $data
     *
     * @return string
     */
    protected function metaKey(array $data): string
    {
        return implode(':', [
            $data['type'],
            $data['name'],
            'meta'
        ]);
    }

    /**
     * @param mixed[] $data
     *
     * @return string
     */
    protected function valueKey(array $data): string
    {
        return implode(':', [
            $data['type'],
            $data['name'],
            $this->encodeLabelValues($data['labelValues']),
            'value'
        ]);
    }

    /**
     * @param mixed[] $data
     *
     * @return mixed[]
     */
    protected function metaData(array $data): array
    {
        $metricsMetaData = $data;
        unset($metricsMetaData['value'], $metricsMetaData['command'], $metricsMetaData['labelValues']);
        return $metricsMetaData;
    }

    /**
     * @param mixed[] $samples
     */
    protected function sortSamples(array &$samples): void
    {
        usort($samples, function ($a, $b): int {
            return strcmp(implode("", $a['labelValues']), implode("", $b['labelValues']));
        });
    }

    /**
     * @param mixed[] $values
     * @return string
     * @throws RuntimeException
     */
    protected function encodeLabelValues(array $values): string
    {
        $json = json_encode($values);
        if (false === $json) {
            throw new RuntimeException(json_last_error_msg());
        }
        return base64_encode($json);
    }

    /**
     * @param string $values
     * @return mixed[]
     * @throws RuntimeException
     */
    protected function decodeLabelValues(string $values): array
    {
        $json = base64_decode($values, true);
        if (false === $json) {
            throw new RuntimeException('Cannot base64 decode label values');
        }
        $decodedValues = json_decode($json, true);
        if (false === $decodedValues) {
            throw new RuntimeException(json_last_error_msg());
        }
        return $decodedValues;
    }
}
<?php

declare(strict_types=1);

namespace Prometheus\Storage;

use Predis\Client;
use Prometheus\Storage\RedisClients\Predis as PredisClient;

class Predis extends AbstractRedis
{
    /**
     * @var mixed[]
     */
    private static $defaultParameters = [
        'scheme' => 'tcp',
        'host' => '127.0.0.1',
        'port' => 6379,
        'timeout' => 0.1,
        'read_write_timeout' => 10,
        'persistent' => false,
        'password' => null,
        'username' => null,
    ];

    /**
     * @var mixed[]
     */
    private static $defaultOptions = [
        'prefix' => '',
        'throw_errors' => true,
    ];

    /**
     * @var mixed[]
     */
    private $parameters = [];

    /**
     * @var mixed[]
     */
    private $options = [];

    /**
     * Predis constructor.
     *
     * @param  mixed[]  $parameters
     * @param  mixed[]  $options
     */
    public function __construct(array $parameters = [], array $options = [])
    {
        $this->parameters = array_merge(self::$defaultParameters, $parameters);
        $this->options = array_merge(self::$defaultOptions, $options);
        $this->redis = PredisClient::create($this->parameters, $this->options);
    }

    public static function fromExistingConnection(Client $client): self
    {
        $self = new self();
        $self->redis = PredisClient::fromExistingConnection($client);

        return $self;
    }

    /**
     * @param  mixed[]  $parameters
     */
    public static function setDefaultParameters(array $parameters): void
    {
        self::$defaultParameters = array_merge(self::$defaultParameters, $parameters);
    }

    /**
     * @param  mixed[]  $options
     */
    public static function setDefaultOptions(array $options): void
    {
        self::$defaultOptions = array_merge(self::$defaultOptions, $options);
    }
}
<?php

declare(strict_types=1);

namespace Prometheus\Storage;

use APCuIterator;
use Prometheus\Exception\StorageException;
use Prometheus\Math;
use Prometheus\MetricFamilySamples;
use RuntimeException;

class APC implements Adapter
{
    /** @var string Default prefix to use for APC keys. */
    const PROMETHEUS_PREFIX = 'prom';

    /** @var string Prefix to use for APC keys. */
    private $prometheusPrefix;

    /**
     * APC constructor.
     *
     * @param string $prometheusPrefix Prefix for APCu keys (defaults to {@see PROMETHEUS_PREFIX}).
     *
     * @throws StorageException
     */
    public function __construct(string $prometheusPrefix = self::PROMETHEUS_PREFIX)
    {
        if (!extension_loaded('apcu')) {
            throw new StorageException('APCu extension is not loaded');
        }
        if (!apcu_enabled()) {
            throw new StorageException('APCu is not enabled');
        }

        $this->prometheusPrefix = $prometheusPrefix;
    }

    /**
     * @return MetricFamilySamples[]
     */
    public function collect(bool $sortMetrics = true): array
    {
        $metrics = $this->collectHistograms();
        $metrics = array_merge($metrics, $this->collectGauges($sortMetrics));
        $metrics = array_merge($metrics, $this->collectCounters($sortMetrics));
        $metrics = array_merge($metrics, $this->collectSummaries());
        return $metrics;
    }

    /**
     * @param mixed[] $data
     */
    public function updateHistogram(array $data): void
    {
        // Initialize the sum
        $sumKey = $this->histogramBucketValueKey($data, 'sum');
        if (!apcu_exists($sumKey)) {
            $new = apcu_add($sumKey, $this->toBinaryRepresentationAsInteger(0));

            // If sum does not exist, assume a new histogram and store the metadata
            if ($new) {
                apcu_store($this->metaKey($data), json_encode($this->metaData($data)));
            }
        }

        // Atomically increment the sum
        // Taken from https://github.com/prometheus/client_golang/blob/66058aac3a83021948e5fb12f1f408ff556b9037/prometheus/value.go#L91
        $done = false;
        while (!$done) {
            $old = apcu_fetch($sumKey);
            if ($old !== false) {
                $done = apcu_cas($sumKey, $old, $this->toBinaryRepresentationAsInteger($this->fromBinaryRepresentationAsInteger($old) + $data['value']));
            } else {
                $new = apcu_add($sumKey, $this->toBinaryRepresentationAsInteger(0));

                // If sum does not exist, assume a new histogram and store the metadata
                if ($new) {
                    apcu_store($this->metaKey($data), json_encode($this->metaData($data)));
                }
            }
        }

        // Figure out in which bucket the observation belongs
        $bucketToIncrease = '+Inf';
        foreach ($data['buckets'] as $bucket) {
            if ($data['value'] <= $bucket) {
                $bucketToIncrease = $bucket;
                break;
            }
        }

        // Initialize and increment the bucket
        $bucketKey = $this->histogramBucketValueKey($data, $bucketToIncrease);
        if (!apcu_exists($bucketKey)) {
            apcu_add($bucketKey, 0);
        }
        apcu_inc($bucketKey);
    }

    /**
     * @param mixed[] $data
     */
    public function updateSummary(array $data): void
    {
        // store meta
        $metaKey = $this->metaKey($data);
        if (!apcu_exists($metaKey)) {
            apcu_add($metaKey, $this->metaData($data));
        }

        // store value key
        $valueKey = $this->valueKey($data);
        if (!apcu_exists($valueKey)) {
            apcu_add($valueKey, $this->encodeLabelValues($data['labelValues']));
        }

        // trick to handle uniqid collision
        $done = false;
        while (!$done) {
            $sampleKey = $valueKey . ':' . uniqid('', true);
            $done = apcu_add($sampleKey, $data['value'], $data['maxAgeSeconds']);
        }
    }

    /**
     * @param mixed[] $data
     */
    public function updateGauge(array $data): void
    {
        $valueKey = $this->valueKey($data);
        $old = apcu_fetch($valueKey);
        if ($data['command'] === Adapter::COMMAND_SET) {
            $new = $this->toBinaryRepresentationAsInteger($data['value']);
            if ($old === false) {
                apcu_store($valueKey, $new);
                apcu_store($this->metaKey($data), json_encode($this->metaData($data)));
                return;
            } else {
                // Taken from https://github.com/prometheus/client_golang/blob/66058aac3a83021948e5fb12f1f408ff556b9037/prometheus/value.go#L91
                while (true) {
                    if ($old !== false) {
                        if (apcu_cas($valueKey, $old, $new)) {
                            return;
                        } else {
                            $old = apcu_fetch($valueKey);
                        }
                    } else {
                        // Cache got evicted under our feet? Just consider it a fresh/new insert and move on.
                        apcu_store($valueKey, $new);
                        apcu_store($this->metaKey($data), json_encode($this->metaData($data)));
                        return;
                    }
                }
            }
        } else {
            if ($old === false) {
                $new = apcu_add($valueKey, $this->toBinaryRepresentationAsInteger(0));
                if ($new) {
                    apcu_store($this->metaKey($data), json_encode($this->metaData($data)));
                }
            }
            // Taken from https://github.com/prometheus/client_golang/blob/66058aac3a83021948e5fb12f1f408ff556b9037/prometheus/value.go#L91
            $done = false;
            while (!$done) {
                $old = apcu_fetch($valueKey);
                if ($old !== false) {
                    $done = apcu_cas($valueKey, $old, $this->toBinaryRepresentationAsInteger($this->fromBinaryRepresentationAsInteger($old) + $data['value']));
                } else {
                    $new = apcu_add($valueKey, $this->toBinaryRepresentationAsInteger(0));
                    if ($new) {
                        apcu_store($this->metaKey($data), json_encode($this->metaData($data)));
                    }
                }
            }
        }
    }

    /**
     * @param mixed[] $data
     */
    public function updateCounter(array $data): void
    {
        $valueKey = $this->valueKey($data);
        // Check if value key already exists
        if (apcu_exists($this->valueKey($data)) === false) {
            apcu_add($this->valueKey($data), 0);
            apcu_store($this->metaKey($data), json_encode($this->metaData($data)));
        }

        // Taken from https://github.com/prometheus/client_golang/blob/66058aac3a83021948e5fb12f1f408ff556b9037/prometheus/value.go#L91
        $done = false;
        while (!$done) {
            $old = apcu_fetch($valueKey);
            if ($old !== false) {
                $done = apcu_cas($valueKey, $old, $this->toBinaryRepresentationAsInteger($this->fromBinaryRepresentationAsInteger($old) + $data['value']));
            } else {
                apcu_add($this->valueKey($data), 0);
                apcu_store($this->metaKey($data), json_encode($this->metaData($data)));
            }
        }
    }

    /**
     * @deprecated use replacement method wipeStorage from Adapter interface
     *
     * @return void
     */
    public function flushAPC(): void
    {
        $this->wipeStorage();
    }

    /**
     * Removes all previously stored data from apcu
     *
     * @return void
     */
    public function wipeStorage(): void
    {
        //                   /      / | PCRE expresion boundary
        //                    ^       | match from first character only
        //                     %s:    | common prefix substitute with colon suffix
        //                        .+  | at least one additional character
        $matchAll = sprintf('/^%s:.+/', $this->prometheusPrefix);

        foreach (new APCuIterator($matchAll) as $key => $value) {
            apcu_delete($key);
        }
    }

    /**
     * @param mixed[] $data
     * @return string
     */
    private function metaKey(array $data): string
    {
        return implode(':', [$this->prometheusPrefix, $data['type'], $data['name'], 'meta']);
    }

    /**
     * @param mixed[] $data
     * @return string
     */
    private function valueKey(array $data): string
    {
        return implode(':', [
            $this->prometheusPrefix,
            $data['type'],
            $data['name'],
            $this->encodeLabelValues($data['labelValues']),
            'value',
        ]);
    }

    /**
     * @param mixed[] $data
     * @param string|int $bucket
     * @return string
     */
    private function histogramBucketValueKey(array $data, $bucket): string
    {
        return implode(':', [
            $this->prometheusPrefix,
            $data['type'],
            $data['name'],
            $this->encodeLabelValues($data['labelValues']),
            $bucket,
            'value',
        ]);
    }

    /**
     * @param mixed[] $data
     * @return mixed[]
     */
    private function metaData(array $data): array
    {
        $metricsMetaData = $data;
        unset($metricsMetaData['value'], $metricsMetaData['command'], $metricsMetaData['labelValues']);
        return $metricsMetaData;
    }

    /**
     * @return MetricFamilySamples[]
     */
    private function collectCounters(bool $sortMetrics = true): array
    {
        $counters = [];
        foreach (new APCuIterator('/^' . $this->prometheusPrefix . ':counter:.*:meta/') as $counter) {
            $metaData = json_decode($counter['value'], true);
            $data = [
                'name' => $metaData['name'],
                'help' => $metaData['help'],
                'type' => $metaData['type'],
                'labelNames' => $metaData['labelNames'],
                'samples' => [],
            ];
            foreach (new APCuIterator('/^' . $this->prometheusPrefix . ':counter:' . $metaData['name'] . ':.*:value/') as $value) {
                $parts = explode(':', $value['key']);
                $labelValues = $parts[3];
                $data['samples'][] = [
                    'name' => $metaData['name'],
                    'labelNames' => [],
                    'labelValues' => $this->decodeLabelValues($labelValues),
                    'value' => $this->fromBinaryRepresentationAsInteger($value['value']),
                ];
            }

            if ($sortMetrics) {
                $this->sortSamples($data['samples']);
            }

            $counters[] = new MetricFamilySamples($data);
        }
        return $counters;
    }

    /**
     * @return MetricFamilySamples[]
     */
    private function collectGauges(bool $sortMetrics = true): array
    {
        $gauges = [];
        foreach (new APCuIterator('/^' . $this->prometheusPrefix . ':gauge:.*:meta/') as $gauge) {
            $metaData = json_decode($gauge['value'], true);
            $data = [
                'name' => $metaData['name'],
                'help' => $metaData['help'],
                'type' => $metaData['type'],
                'labelNames' => $metaData['labelNames'],
                'samples' => [],
            ];
            foreach (new APCuIterator('/^' . $this->prometheusPrefix . ':gauge:' . $metaData['name'] . ':.*:value/') as $value) {
                $parts = explode(':', $value['key']);
                $labelValues = $parts[3];
                $data['samples'][] = [
                    'name' => $metaData['name'],
                    'labelNames' => [],
                    'labelValues' => $this->decodeLabelValues($labelValues),
                    'value' => $this->fromBinaryRepresentationAsInteger($value['value']),
                ];
            }

            if ($sortMetrics) {
                $this->sortSamples($data['samples']);
            }

            $gauges[] = new MetricFamilySamples($data);
        }
        return $gauges;
    }

    /**
     * @return MetricFamilySamples[]
     */
    private function collectHistograms(): array
    {
        $histograms = [];
        foreach (new APCuIterator('/^' . $this->prometheusPrefix . ':histogram:.*:meta/') as $histogram) {
            $metaData = json_decode($histogram['value'], true);
            $data = [
                'name' => $metaData['name'],
                'help' => $metaData['help'],
                'type' => $metaData['type'],
                'labelNames' => $metaData['labelNames'],
                'buckets' => $metaData['buckets'],
            ];

            // Add the Inf bucket so we can compute it later on
            $data['buckets'][] = '+Inf';

            $histogramBuckets = [];
            foreach (new APCuIterator('/^' . $this->prometheusPrefix . ':histogram:' . $metaData['name'] . ':.*:value/') as $value) {
                $parts = explode(':', $value['key']);
                $labelValues = $parts[3];
                $bucket = $parts[4];
                // Key by labelValues
                $histogramBuckets[$labelValues][$bucket] = $value['value'];
            }

            // Compute all buckets
            $labels = array_keys($histogramBuckets);
            sort($labels);
            foreach ($labels as $labelValues) {
                $acc = 0;
                $decodedLabelValues = $this->decodeLabelValues($labelValues);
                foreach ($data['buckets'] as $bucket) {
                    $bucket = (string)$bucket;
                    if (!isset($histogramBuckets[$labelValues][$bucket])) {
                        $data['samples'][] = [
                            'name' => $metaData['name'] . '_bucket',
                            'labelNames' => ['le'],
                            'labelValues' => array_merge($decodedLabelValues, [$bucket]),
                            'value' => $acc,
                        ];
                    } else {
                        $acc += $histogramBuckets[$labelValues][$bucket];
                        $data['samples'][] = [
                            'name' => $metaData['name'] . '_' . 'bucket',
                            'labelNames' => ['le'],
                            'labelValues' => array_merge($decodedLabelValues, [$bucket]),
                            'value' => $acc,
                        ];
                    }
                }

                // Add the count
                $data['samples'][] = [
                    'name' => $metaData['name'] . '_count',
                    'labelNames' => [],
                    'labelValues' => $decodedLabelValues,
                    'value' => $acc,
                ];

                // Add the sum
                $data['samples'][] = [
                    'name' => $metaData['name'] . '_sum',
                    'labelNames' => [],
                    'labelValues' => $decodedLabelValues,
                    'value' => $this->fromBinaryRepresentationAsInteger($histogramBuckets[$labelValues]['sum'] ?? 0),
                ];
            }
            $histograms[] = new MetricFamilySamples($data);
        }
        return $histograms;
    }

    /**
     * @return MetricFamilySamples[]
     */
    private function collectSummaries(): array
    {
        $math = new Math();
        $summaries = [];
        foreach (new APCuIterator('/^' . $this->prometheusPrefix . ':summary:.*:meta/') as $summary) {
            $metaData = $summary['value'];
            $data = [
                'name' => $metaData['name'],
                'help' => $metaData['help'],
                'type' => $metaData['type'],
                'labelNames' => $metaData['labelNames'],
                'maxAgeSeconds' => $metaData['maxAgeSeconds'],
                'quantiles' => $metaData['quantiles'],
                'samples' => [],
            ];

            foreach (new APCuIterator('/^' . $this->prometheusPrefix . ':summary:' . $metaData['name'] . ':.*:value$/') as $value) {
                $encodedLabelValues = $value['value'];
                $decodedLabelValues = $this->decodeLabelValues($encodedLabelValues);
                $samples = [];
                foreach (new APCuIterator('/^' . $this->prometheusPrefix . ':summary:' . $metaData['name'] . ':' . str_replace('/', '\\/', preg_quote($encodedLabelValues)) . ':value:.*/') as $sample) {
                    $samples[] = $sample['value'];
                }

                if (count($samples) === 0) {
                    apcu_delete($value['key']);
                    continue;
                }

                // Compute quantiles
                sort($samples);
                foreach ($data['quantiles'] as $quantile) {
                    $data['samples'][] = [
                        'name' => $metaData['name'],
                        'labelNames' => ['quantile'],
                        'labelValues' => array_merge($decodedLabelValues, [$quantile]),
                        'value' => $math->quantile($samples, $quantile),
                    ];
                }

                // Add the count
                $data['samples'][] = [
                    'name' => $metaData['name'] . '_count',
                    'labelNames' => [],
                    'labelValues' => $decodedLabelValues,
                    'value' => count($samples),
                ];

                // Add the sum
                $data['samples'][] = [
                    'name' => $metaData['name'] . '_sum',
                    'labelNames' => [],
                    'labelValues' => $decodedLabelValues,
                    'value' => array_sum($samples),
                ];
            }

            if (count($data['samples']) > 0) {
                $summaries[] = new MetricFamilySamples($data);
            } else {
                apcu_delete($summary['key']);
            }
        }
        return $summaries;
    }

    /**
     * @param mixed $val
     * @return int
     * @throws RuntimeException
     */
    private function toBinaryRepresentationAsInteger($val): int
    {
        $packedDouble = pack('d', $val);
        if ((bool)$packedDouble !== false) {
            $unpackedData = unpack("Q", $packedDouble);
            if (is_array($unpackedData)) {
                return $unpackedData[1];
            }
        }
        throw new RuntimeException("Formatting from binary representation to integer did not work");
    }

    /**
     * @param mixed $val
     * @return float
     * @throws RuntimeException
     */
    private function fromBinaryRepresentationAsInteger($val): float
    {
        $packedBinary = pack('Q', $val);
        if ((bool)$packedBinary !== false) {
            $unpackedData = unpack("d", $packedBinary);
            if (is_array($unpackedData)) {
                return $unpackedData[1];
            }
        }
        throw new RuntimeException("Formatting from integer to binary representation did not work");
    }

    /**
     * @param mixed[] $samples
     */
    private function sortSamples(array &$samples): void
    {
        usort($samples, function ($a, $b): int {
            return strcmp(implode("", $a['labelValues']), implode("", $b['labelValues']));
        });
    }

    /**
     * @param mixed[] $values
     * @return string
     * @throws RuntimeException
     */
    private function encodeLabelValues(array $values): string
    {
        $json = json_encode($values);
        if (false === $json) {
            throw new RuntimeException(json_last_error_msg());
        }
        return base64_encode($json);
    }

    /**
     * @param string $values
     * @return mixed[]
     * @throws RuntimeException
     */
    private function decodeLabelValues(string $values): array
    {
        $json = base64_decode($values, true);
        if (false === $json) {
            throw new RuntimeException('Cannot base64 decode label values');
        }
        $decodedValues = json_decode($json, true);
        if (false === $decodedValues) {
            throw new RuntimeException(json_last_error_msg());
        }
        return $decodedValues;
    }
}
<?php

declare(strict_types=1);

namespace Prometheus\Storage\RedisClients;

class RedisClientException extends \Exception
{
}
<?php

declare(strict_types=1);

namespace Prometheus\Storage\RedisClients;

interface RedisClient
{
    public function getPrefix(): ?string;

    /**
     * @param  mixed[]  $args
     */
    public function eval(string $script, array $args = [], int $num_keys = 0): void;

    public function set(string $key, mixed $value, mixed $options = null): bool;

    public function setNx(string $key, mixed $value): void;

    /**
     * @return string[]
     */
    public function sMembers(string $key): array;

    /**
     * @return array<string, string>|false
     */
    public function hGetAll(string $key): array|false;

    /**
     * @return string[]
     */
    public function keys(string $pattern): array;

    public function get(string $key): string|false;

    /**
     * @param  string|string[]  $key
     */
    public function del(array|string $key, string ...$other_keys): void;

    public function ensureOpenConnection(): void;
}
<?php

declare(strict_types=1);

namespace Prometheus\Storage\RedisClients;

use InvalidArgumentException;
use Predis\Client;
use Prometheus\Exception\StorageException;

class Predis implements RedisClient
{
    private Client $client;

    public function __construct(Client $client)
    {
        $this->client = $client;
    }

    /**
     * @param  mixed[]  $parameters
     * @param  mixed[]  $options
     * @throws StorageException
     */
    public static function create(array $parameters, array $options): self
    {
        try {
            return new self(new Client($parameters, $options));
        } catch (InvalidArgumentException $e) {
            throw new StorageException('Invalid Redis client configuration: ' . $e->getMessage(), 0, $e);
        }
    }

    public static function fromExistingConnection(Client $client): self
    {
        return new self($client);
    }

    public function getPrefix(): ?string
    {
        $value = $this->client->getOptions()->prefix;

        return $value instanceof \Predis\Command\Processor\KeyPrefixProcessor
            ? $value->getPrefix()
            : null;
    }

    public function eval(string $script, array $args = [], int $num_keys = 0): void
    {
        $this->client->eval($script, $num_keys, ...$args);
    }

    public function set(string $key, mixed $value, mixed $options = null): bool
    {
        $result = $this->client->set($key, $value, ...$this->flattenFlags($options));

        return (string) $result === 'OK';
    }

    /**
     * @param  array<int|string, mixed>  $flags
     * @return mixed[]
     */
    private function flattenFlags(array $flags): array
    {
        $result = [];
        foreach ($flags as $key => $value) {
            if (is_int($key)) {
                $result[] = $value;
            } else {
                $result[] = $key;
                $result[] = $value;
            }
        }

        return $result;
    }

    public function setNx(string $key, mixed $value): void
    {
        $this->client->setnx($key, $value);
    }

    public function sMembers(string $key): array
    {
        /** @phpstan-ignore-next-line Predis can return null at runtime despite its array return type. */
        return $this->client->smembers($key) ?? [];
    }

    public function hGetAll(string $key): array|false
    {
        return $this->client->hgetall($key);
    }

    public function keys(string $pattern): array
    {
        return $this->client->keys($pattern);
    }

    public function get(string $key): string|false
    {
        return $this->client->get($key) ?? false;
    }

    public function del(array|string $key, string ...$other_keys): void
    {
        $this->client->del($key, ...$other_keys);
    }

    /**
     * @throws StorageException
     */
    public function ensureOpenConnection(): void
    {
        if (!$this->client->isConnected()) {
            try {
                $this->client->connect();
            } catch (\Predis\Connection\ConnectionException $e) {
                throw new StorageException('Cannot establish Redis Connection:' . $e->getMessage(), 0, $e);
            }
        }
    }
}
<?php

declare(strict_types=1);

namespace Prometheus\Storage\RedisClients;

use Prometheus\Exception\StorageException;

class PHPRedis implements RedisClient
{
    /**
     * @var \Redis
     */
    private $redis;

    /**
     * @var mixed[]
     */
    private $options = [];

    /**
     * @var bool
     */
    private $connectionInitialized = false;

    /**
     * @param  mixed[]  $options
     */
    public function __construct(\Redis $redis, array $options)
    {
        $this->redis = $redis;
        $this->options = $options;
    }

    /**
     * @param  mixed[]  $options
     */
    public static function create(array $options): self
    {
        $redis = new \Redis();

        return new self($redis, $options);
    }

    /**
     * @param  mixed[]  $options
     */
    public static function fromExistingConnection(\Redis $redis, array $options): self
    {
        $self = new self($redis, $options);
        $self->connectionInitialized = true;

        return $self;
    }

    public function getPrefix(): ?string
    {
        /** @var mixed $prefix */
        $prefix = $this->redis->getOption(\Redis::OPT_PREFIX);

        return is_string($prefix) && $prefix !== '' ? $prefix : null;
    }

    public function eval(string $script, array $args = [], int $num_keys = 0): void
    {
        $this->redis->eval($script, $args, $num_keys);
    }

    public function set(string $key, mixed $value, mixed $options = null): bool
    {
        return $this->redis->set($key, $value, $options);
    }

    public function setNx(string $key, mixed $value): void
    {
        $this->redis->setNx($key, $value); /** @phpstan-ignore-line */
    }

    public function sMembers(string $key): array
    {
        return $this->redis->sMembers($key);
    }

    public function hGetAll(string $key): array|false
    {
        return $this->redis->hGetAll($key);
    }

    public function keys(string $pattern): array
    {
        return $this->redis->keys($pattern);
    }

    public function get(string $key): string|false
    {
        return $this->redis->get($key);
    }

    /**
     * @throws RedisClientException
     */
    public function del(array|string $key, string ...$other_keys): void
    {
        try {
            $this->redis->del($key, ...$other_keys);
        } catch (\RedisException $e) {
            throw new RedisClientException($e->getMessage(), $e->getCode(), $e);
        }
    }

    /**
     * @throws StorageException
     */
    public function ensureOpenConnection(): void
    {
        if ($this->connectionInitialized === true) {
            return;
        }

        $this->connectToServer();
        $authParams = [];

        if (isset($this->options['user']) && $this->options['user'] !== '') {
            $authParams[] = $this->options['user'];
        }

        if (isset($this->options['password'])) {
            $authParams[] = $this->options['password'];
        }

        if ($authParams !== []) {
            $this->redis->auth($authParams);
        }

        if (isset($this->options['database'])) {
            $this->redis->select($this->options['database']);
        }

        $this->redis->setOption(\Redis::OPT_READ_TIMEOUT, $this->options['read_timeout']);

        $this->connectionInitialized = true;
    }

    /**
     * @throws StorageException
     */
    private function connectToServer(): void
    {
        try {
            $connection_successful = false;
            if ($this->options['persistent_connections'] !== false) {
                $connection_successful = $this->redis->pconnect(
                    $this->options['host'],
                    (int) $this->options['port'],
                    (float) $this->options['timeout']
                );
            } else {
                $connection_successful = $this->redis->connect($this->options['host'], (int) $this->options['port'], (float) $this->options['timeout']);
            }
            if (! $connection_successful) {
                throw new StorageException(
                    sprintf("Can't connect to Redis server. %s", $this->redis->getLastError()),
                    0
                );
            }
        } catch (\RedisException $e) {
            throw new StorageException(
                sprintf("Can't connect to Redis server. %s", $e->getMessage()),
                $e->getCode(),
                $e,
            );
        }
    }
}
<?php

declare(strict_types=1);

namespace Prometheus\Storage;

use Prometheus\Exception\StorageException;
use Prometheus\Storage\RedisClients\PHPRedis;

class Redis extends AbstractRedis
{
    /**
     * @var mixed[]
     */
    private static $defaultOptions = [
        'host' => '127.0.0.1',
        'port' => 6379,
        'timeout' => 0.1,
        'read_timeout' => '10',
        'persistent_connections' => false,
        'password' => null,
        'user' => null,
    ];

    /**
     * @var mixed[]
     */
    private $options = [];

    /**
     * Redis constructor.
     *
     * @param  mixed[]  $options
     */
    public function __construct(array $options = [])
    {
        $this->options = array_merge(self::$defaultOptions, $options);
        $this->redis = PHPRedis::create($this->options);
    }

    /**
     * @throws StorageException
     */
    public static function fromExistingConnection(\Redis $redis): self
    {
        if ($redis->isConnected() === false) {
            throw new StorageException('Connection to Redis server not established');
        }

        $self = new self();
        $self->redis = PHPRedis::fromExistingConnection($redis, self::$defaultOptions);

        return $self;
    }

    /**
     * @param  mixed[]  $options
     */
    public static function setDefaultOptions(array $options): void
    {
        self::$defaultOptions = array_merge(self::$defaultOptions, $options);
    }
}
<?php

declare(strict_types=1);

namespace Prometheus\Storage;

use InvalidArgumentException;
use Prometheus\Counter;
use Prometheus\Exception\MetricJsonException;
use Prometheus\Exception\StorageException;
use Prometheus\Gauge;
use Prometheus\Histogram;
use Prometheus\Math;
use Prometheus\MetricFamilySamples;
use Prometheus\Storage\RedisClients\RedisClient;
use Prometheus\Storage\RedisClients\RedisClientException;
use Prometheus\Summary;
use RuntimeException;

abstract class AbstractRedis implements Adapter
{
    const PROMETHEUS_METRIC_KEYS_SUFFIX = '_METRIC_KEYS';

    /**
     * Intentionally shared across all subclasses via self::$prefix.
     * Prefix is a global concern and mixing adapters,
     * with different prefixes in one application is not supported.
     *
     * @var string
     */
    protected static $prefix = 'PROMETHEUS_';

    /**
     * @var RedisClient
     */
    protected $redis;

    public static function setPrefix(string $prefix): void
    {
        self::$prefix = $prefix;
    }

    /**
     * @throws StorageException
     *
     * @deprecated use replacement method wipeStorage from Adapter interface
     */
    public function flushRedis(): void
    {
        $this->wipeStorage();
    }

    /**
     * {@inheritDoc}
     */
    public function wipeStorage(): void
    {
        $this->redis->ensureOpenConnection();

        $searchPattern = '';

        $globalPrefix = $this->redis->getPrefix();
        if ($globalPrefix !== null) {
            $searchPattern .= $globalPrefix;
        }

        $searchPattern .= self::$prefix;
        $searchPattern .= '*';

        $this->redis->eval(
            <<<'LUA'
redis.replicate_commands()
local cursor = "0"
repeat
    local results = redis.call('SCAN', cursor, 'MATCH', ARGV[1])
    cursor = results[1]
    for _, key in ipairs(results[2]) do
        redis.call('DEL', key)
    end
until cursor == "0"
LUA
            ,
            [$searchPattern],
            0
        );
    }

    /**
     * @param  mixed[]  $data
     */
    protected function metaKey(array $data): string
    {
        return implode(':', [
            $data['name'],
            'meta',
        ]);
    }

    /**
     * @param  mixed[]  $data
     */
    protected function valueKey(array $data): string
    {
        return implode(':', [
            $data['name'],
            $this->encodeLabelValues($data['labelValues']),
            'value',
        ]);
    }

    /**
     * @return MetricFamilySamples[]
     *
     * @throws StorageException
     */
    public function collect(bool $sortMetrics = true): array
    {
        $this->redis->ensureOpenConnection();
        $metrics = $this->collectHistograms();
        $metrics = array_merge($metrics, $this->collectGauges($sortMetrics));
        $metrics = array_merge($metrics, $this->collectCounters($sortMetrics));
        $metrics = array_merge($metrics, $this->collectSummaries());

        return array_map(
            function (array $metric): MetricFamilySamples {
                return new MetricFamilySamples($metric);
            },
            $metrics
        );
    }

    /**
     * @param  mixed[]  $data
     *
     * @throws StorageException
     */
    public function updateHistogram(array $data): void
    {
        $this->redis->ensureOpenConnection();
        $bucketToIncrease = '+Inf';
        foreach ($data['buckets'] as $bucket) {
            if ($data['value'] <= $bucket) {
                $bucketToIncrease = $bucket;
                break;
            }
        }
        $metaData = $data;
        unset($metaData['value'], $metaData['labelValues']);

        $this->redis->eval(
            <<<'LUA'
local result = redis.call('hIncrByFloat', KEYS[1], ARGV[1], ARGV[3])
redis.call('hIncrBy', KEYS[1], ARGV[2], 1)
if tonumber(result) >= tonumber(ARGV[3]) then
    redis.call('hSet', KEYS[1], '__meta', ARGV[4])
    redis.call('sAdd', KEYS[2], KEYS[1])
end
return result
LUA
            ,
            [
                $this->toMetricKey($data),
                self::$prefix . Histogram::TYPE . self::PROMETHEUS_METRIC_KEYS_SUFFIX,
                json_encode(['b' => 'sum', 'labelValues' => $data['labelValues']]),
                json_encode(['b' => $bucketToIncrease, 'labelValues' => $data['labelValues']]),
                $data['value'],
                json_encode($metaData),
            ],
            2
        );
    }

    /**
     * @param  mixed[]  $data
     *
     * @throws StorageException
     */
    public function updateSummary(array $data): void
    {
        $this->redis->ensureOpenConnection();

        // store meta
        $summaryKey = self::$prefix . Summary::TYPE . self::PROMETHEUS_METRIC_KEYS_SUFFIX;
        $metaKey = $summaryKey . ':' . $this->metaKey($data);
        $json = json_encode($this->metaData($data));
        if ($json === false) {
            throw new RuntimeException(json_last_error_msg());
        }
        $this->redis->setNx($metaKey, $json);

        // store value key
        $valueKey = $summaryKey . ':' . $this->valueKey($data);
        $json = json_encode($this->encodeLabelValues($data['labelValues']));
        if ($json === false) {
            throw new RuntimeException(json_last_error_msg());
        }
        $this->redis->setNx($valueKey, $json);

        // trick to handle uniqid collision
        $done = false;
        while (! $done) {
            $sampleKey = $valueKey . ':' . uniqid('', true);
            $done = $this->redis->set($sampleKey, $data['value'], ['NX', 'EX' => $data['maxAgeSeconds']]);
        }
    }

    /**
     * @param  mixed[]  $data
     *
     * @throws StorageException
     */
    public function updateGauge(array $data): void
    {
        $this->redis->ensureOpenConnection();
        $metaData = $data;
        unset($metaData['value'], $metaData['labelValues'], $metaData['command']);
        $this->redis->eval(
            <<<'LUA'
local result = redis.call(ARGV[1], KEYS[1], ARGV[2], ARGV[3])

if ARGV[1] == 'hSet' then
    if result == 1 then
        redis.call('hSet', KEYS[1], '__meta', ARGV[4])
        redis.call('sAdd', KEYS[2], KEYS[1])
    end
else
    if result == ARGV[3] then
        redis.call('hSet', KEYS[1], '__meta', ARGV[4])
        redis.call('sAdd', KEYS[2], KEYS[1])
    end
end
LUA
            ,
            [
                $this->toMetricKey($data),
                self::$prefix . Gauge::TYPE . self::PROMETHEUS_METRIC_KEYS_SUFFIX,
                $this->getRedisCommand($data['command']),
                json_encode($data['labelValues']),
                $data['value'],
                json_encode($metaData),
            ],
            2
        );
    }

    /**
     * @param  mixed[]  $data
     *
     * @throws StorageException
     */
    public function updateCounter(array $data): void
    {
        $this->redis->ensureOpenConnection();
        $metaData = $data;
        unset($metaData['value'], $metaData['labelValues'], $metaData['command']);
        $this->redis->eval(
            <<<'LUA'
local result = redis.call(ARGV[1], KEYS[1], ARGV[3], ARGV[2])
local added = redis.call('sAdd', KEYS[2], KEYS[1])
if added == 1 then
    redis.call('hMSet', KEYS[1], '__meta', ARGV[4])
end
return result
LUA
            ,
            [
                $this->toMetricKey($data),
                self::$prefix . Counter::TYPE . self::PROMETHEUS_METRIC_KEYS_SUFFIX,
                $this->getRedisCommand($data['command']),
                $data['value'],
                json_encode($data['labelValues']),
                json_encode($metaData),
            ],
            2
        );
    }

    /**
     * @param  mixed[]  $data
     * @return mixed[]
     */
    protected function metaData(array $data): array
    {
        $metricsMetaData = $data;
        unset($metricsMetaData['value'], $metricsMetaData['command'], $metricsMetaData['labelValues']);

        return $metricsMetaData;
    }

    /**
     * @return mixed[]
     */
    protected function collectHistograms(): array
    {
        $keys = $this->redis->sMembers(self::$prefix . Histogram::TYPE . self::PROMETHEUS_METRIC_KEYS_SUFFIX);
        sort($keys);
        $histograms = [];
        foreach ($keys as $key) {
            $raw = $this->redis->hGetAll($this->removePrefixFromKey($key));
            if (! isset($raw['__meta'])) {
                continue;
            }
            $histogram = json_decode($raw['__meta'], true);
            unset($raw['__meta']);
            $histogram['samples'] = [];

            // Add the Inf bucket so we can compute it later on
            $histogram['buckets'][] = '+Inf';

            $allLabelValues = [];
            foreach (array_keys($raw) as $k) {
                $d = json_decode($k, true);
                if ($d['b'] == 'sum') {
                    continue;
                }
                $allLabelValues[] = $d['labelValues'];
            }
            if (json_last_error() !== JSON_ERROR_NONE) {
                $this->throwMetricJsonException($key);
            }

            // We need set semantics.
            // This is the equivalent of array_unique but for arrays of arrays.
            $allLabelValues = array_map('unserialize', array_unique(array_map('serialize', $allLabelValues)));
            sort($allLabelValues);

            foreach ($allLabelValues as $labelValues) {
                // Fill up all buckets.
                // If the bucket doesn't exist fill in values from
                // the previous one.
                $acc = 0;
                foreach ($histogram['buckets'] as $bucket) {
                    $bucketKey = json_encode(['b' => $bucket, 'labelValues' => $labelValues]);
                    if (! isset($raw[$bucketKey])) {
                        $histogram['samples'][] = [
                            'name' => $histogram['name'] . '_bucket',
                            'labelNames' => ['le'],
                            'labelValues' => array_merge($labelValues, [$bucket]),
                            'value' => $acc,
                        ];
                    } else {
                        $acc += $raw[$bucketKey];
                        $histogram['samples'][] = [
                            'name' => $histogram['name'] . '_bucket',
                            'labelNames' => ['le'],
                            'labelValues' => array_merge($labelValues, [$bucket]),
                            'value' => $acc,
                        ];
                    }
                }

                // Add the count
                $histogram['samples'][] = [
                    'name' => $histogram['name'] . '_count',
                    'labelNames' => [],
                    'labelValues' => $labelValues,
                    'value' => $acc,
                ];

                // Add the sum
                $histogram['samples'][] = [
                    'name' => $histogram['name'] . '_sum',
                    'labelNames' => [],
                    'labelValues' => $labelValues,
                    'value' => $raw[json_encode(['b' => 'sum', 'labelValues' => $labelValues])],
                ];
            }
            $histograms[] = $histogram;
        }

        return $histograms;
    }

    protected function removePrefixFromKey(string $key): string
    {
        $prefix = $this->redis->getPrefix();

        if ($prefix === null) {
            return $key;
        }

        return substr($key, strlen($prefix));
    }

    /**
     * @return mixed[]
     */
    protected function collectSummaries(): array
    {
        $math = new Math();
        $summaryKey = self::$prefix . Summary::TYPE . self::PROMETHEUS_METRIC_KEYS_SUFFIX;
        $keys = $this->redis->keys($summaryKey . ':*:meta');

        $summaries = [];
        foreach ($keys as $metaKeyWithPrefix) {
            $metaKey = $this->removePrefixFromKey($metaKeyWithPrefix);
            $rawSummary = $this->redis->get($metaKey);
            if ($rawSummary === false) {
                continue;
            }
            $summary = json_decode($rawSummary, true);
            $metaData = $summary;
            $data = [
                'name' => $metaData['name'],
                'help' => $metaData['help'],
                'type' => $metaData['type'],
                'labelNames' => $metaData['labelNames'],
                'maxAgeSeconds' => $metaData['maxAgeSeconds'],
                'quantiles' => $metaData['quantiles'],
                'samples' => [],
            ];

            $values = $this->redis->keys($summaryKey . ':' . $metaData['name'] . ':*:value');
            foreach ($values as $valueKeyWithPrefix) {
                $valueKey = $this->removePrefixFromKey($valueKeyWithPrefix);
                $rawValue = $this->redis->get($valueKey);
                if ($rawValue === false) {
                    continue;
                }
                $value = json_decode($rawValue, true);
                $encodedLabelValues = $value;
                $decodedLabelValues = $this->decodeLabelValues($encodedLabelValues);

                $samples = [];
                $sampleValues = $this->redis->keys($summaryKey . ':' . $metaData['name'] . ':' . $encodedLabelValues . ':value:*');
                foreach ($sampleValues as $sampleValueWithPrefix) {
                    $sampleValue = $this->removePrefixFromKey($sampleValueWithPrefix);
                    $samples[] = (float) $this->redis->get($sampleValue);
                }

                if (count($samples) === 0) {
                    try {
                        $this->redis->del($valueKey);
                    } catch (RedisClientException $e) {
                        // ignore if we can't delete the key
                    }

                    continue;
                }

                // Compute quantiles
                sort($samples);
                foreach ($data['quantiles'] as $quantile) {
                    $data['samples'][] = [
                        'name' => $metaData['name'],
                        'labelNames' => ['quantile'],
                        'labelValues' => array_merge($decodedLabelValues, [$quantile]),
                        'value' => $math->quantile($samples, $quantile),
                    ];
                }

                // Add the count
                $data['samples'][] = [
                    'name' => $metaData['name'] . '_count',
                    'labelNames' => [],
                    'labelValues' => $decodedLabelValues,
                    'value' => count($samples),
                ];

                // Add the sum
                $data['samples'][] = [
                    'name' => $metaData['name'] . '_sum',
                    'labelNames' => [],
                    'labelValues' => $decodedLabelValues,
                    'value' => array_sum($samples),
                ];
            }

            if (count($data['samples']) > 0) {
                $summaries[] = $data;
            } else {
                try {
                    $this->redis->del($metaKey);
                } catch (RedisClientException $e) {
                    // ignore if we can't delete the key
                }
            }
        }

        return $summaries;
    }

    /**
     * @return mixed[]
     */
    protected function collectGauges(bool $sortMetrics = true): array
    {
        $keys = $this->redis->sMembers(self::$prefix . Gauge::TYPE . self::PROMETHEUS_METRIC_KEYS_SUFFIX);
        sort($keys);
        $gauges = [];
        foreach ($keys as $key) {
            $raw = $this->redis->hGetAll($this->removePrefixFromKey($key));
            if (! isset($raw['__meta'])) {
                continue;
            }
            $gauge = json_decode($raw['__meta'], true);
            unset($raw['__meta']);
            $gauge['samples'] = [];
            foreach ($raw as $k => $value) {
                $gauge['samples'][] = [
                    'name' => $gauge['name'],
                    'labelNames' => [],
                    'labelValues' => json_decode($k, true),
                    'value' => $value,
                ];
                if (json_last_error() !== JSON_ERROR_NONE) {
                    $this->throwMetricJsonException($key, $gauge['name']);
                }
            }

            if ($sortMetrics) {
                usort($gauge['samples'], function ($a, $b): int {
                    return strcmp(implode('', $a['labelValues']), implode('', $b['labelValues']));
                });
            }

            $gauges[] = $gauge;
        }

        return $gauges;
    }

    /**
     * @return mixed[]
     *
     * @throws MetricJsonException
     */
    protected function collectCounters(bool $sortMetrics = true): array
    {
        $keys = $this->redis->sMembers(self::$prefix . Counter::TYPE . self::PROMETHEUS_METRIC_KEYS_SUFFIX);
        sort($keys);
        $counters = [];
        foreach ($keys as $key) {
            $raw = $this->redis->hGetAll($this->removePrefixFromKey($key));
            if (! isset($raw['__meta'])) {
                continue;
            }
            $counter = json_decode($raw['__meta'], true);

            unset($raw['__meta']);
            $counter['samples'] = [];
            foreach ($raw as $k => $value) {
                $counter['samples'][] = [
                    'name' => $counter['name'],
                    'labelNames' => [],
                    'labelValues' => json_decode($k, true),
                    'value' => $value,
                ];

                if (json_last_error() !== JSON_ERROR_NONE) {
                    $this->throwMetricJsonException($key, $counter['name']);
                }
            }

            if ($sortMetrics) {
                usort($counter['samples'], function ($a, $b): int {
                    return strcmp(implode('', $a['labelValues']), implode('', $b['labelValues']));
                });
            }

            $counters[] = $counter;
        }

        return $counters;
    }

    protected function getRedisCommand(int $cmd): string
    {
        switch ($cmd) {
            case Adapter::COMMAND_INCREMENT_INTEGER:
                return 'hIncrBy';
            case Adapter::COMMAND_INCREMENT_FLOAT:
                return 'hIncrByFloat';
            case Adapter::COMMAND_SET:
                return 'hSet';
            default:
                throw new InvalidArgumentException('Unknown command');
        }
    }

    /**
     * @param  mixed[]  $data
     */
    protected function toMetricKey(array $data): string
    {
        return implode(':', [self::$prefix, $data['type'], $data['name']]);
    }

    /**
     * @param  mixed[]  $values
     *
     * @throws RuntimeException
     */
    protected function encodeLabelValues(array $values): string
    {
        $json = json_encode($values);
        if ($json === false) {
            throw new RuntimeException(json_last_error_msg());
        }

        return base64_encode($json);
    }

    /**
     * @return mixed[]
     *
     * @throws RuntimeException
     */
    protected function decodeLabelValues(string $values): array
    {
        $json = base64_decode($values, true);
        if ($json === false) {
            throw new RuntimeException('Cannot base64 decode label values');
        }
        $decodedValues = json_decode($json, true);
        if ($decodedValues === false) {
            throw new RuntimeException(json_last_error_msg());
        }

        return $decodedValues;
    }

    /**
     * @throws MetricJsonException
     */
    protected function throwMetricJsonException(string $redisKey, ?string $metricName = null): void
    {
        $metricName = $metricName ?? 'unknown';
        $message = 'Json error: ' . json_last_error_msg() . ' redis key : ' . $redisKey . ' metric name: ' . $metricName;
        throw new MetricJsonException($message, 0, null, $metricName);
    }
}
<?php

declare(strict_types=1);

namespace Prometheus\Storage;

use Prometheus\Counter;
use Prometheus\Gauge;
use Prometheus\Histogram;
use Prometheus\Math;
use Prometheus\MetricFamilySamples;
use Prometheus\Summary;

class PDO implements Adapter
{
    /**
     * @var \PDO
     */
    protected $database;

    /**
     * @var string
     */
    protected $prefix;

    /**
     * @var array{0: int, 1: int}
     */
    protected $precision;

    /**
     * @param \PDO $database
     *  PDO database connection.
     * @param string $prefix
     *  Database table prefix (default: "prometheus_").
     */
    public function __construct(\PDO $database, string $prefix = 'prometheus_')
    {
        if (!in_array($database->getAttribute(\PDO::ATTR_DRIVER_NAME), ['mysql', 'sqlite', 'pgsql'], true)) {
            throw new \RuntimeException('Only MySQL and SQLite are supported.');
        }

        $this->database = $database;
        $this->prefix = $prefix;

        $this->createTables();
    }

    /**
     * @return MetricFamilySamples[]
     */
    public function collect(): array
    {
        $metrics = $this->collectHistograms();
        $metrics = array_merge($metrics, $this->collectGauges());
        $metrics = array_merge($metrics, $this->collectCounters());
        return array_merge($metrics, $this->collectSummaries());
    }

    /**
     * @inheritDoc
     */
    public function wipeStorage(): void
    {
        switch ($this->database->getAttribute(\PDO::ATTR_DRIVER_NAME)) {
            case 'pgsql':
                $this->database->query("DELETE FROM \"{$this->prefix}_metadata\"");
                $this->database->query("DELETE FROM \"{$this->prefix}_values\"");
                $this->database->query("DELETE FROM \"{$this->prefix}_summaries\"");
                $this->database->query("DELETE FROM \"{$this->prefix}_histograms\"");
                break;
            default:
                $this->database->query("DELETE FROM `{$this->prefix}_metadata`");
                $this->database->query("DELETE FROM `{$this->prefix}_values`");
                $this->database->query("DELETE FROM `{$this->prefix}_summaries`");
                $this->database->query("DELETE FROM `{$this->prefix}_histograms`");
        }
    }

    public function deleteTables(): void
    {
        switch ($this->database->getAttribute(\PDO::ATTR_DRIVER_NAME)) {
            case 'pgsql':
                $this->database->query("DROP TABLE \"{$this->prefix}_metadata\"");
                $this->database->query("DROP TABLE \"{$this->prefix}_values\"");
                $this->database->query("DROP TABLE \"{$this->prefix}_summaries\"");
                $this->database->query("DROP TABLE \"{$this->prefix}_histograms\"");
                break;
            default:
                $this->database->query("DROP TABLE `{$this->prefix}_metadata`");
                $this->database->query("DROP TABLE `{$this->prefix}_values`");
                $this->database->query("DROP TABLE `{$this->prefix}_summaries`");
                $this->database->query("DROP TABLE `{$this->prefix}_histograms`");
        }
    }

    /**
     * @return MetricFamilySamples[]
     */
    protected function collectHistograms(): array
    {
        $result = [];

        $meta_query = $this->getMetaQuery();
        $meta_query->execute([':type' => Histogram::TYPE]);

        while ($row = $meta_query->fetch(\PDO::FETCH_ASSOC)) {
            $data = json_decode($row['metadata'], true);
            $data['samples'] = [];

            // Add the Inf bucket, so we can compute it later on.
            $data['buckets'][] = '+Inf';

            switch ($this->database->getAttribute(\PDO::ATTR_DRIVER_NAME)) {
                case 'pgsql':
                    $values_query = $this->database->prepare("SELECT name, labels_hash, labels, value, bucket FROM \"{$this->prefix}_histograms\" WHERE name = :name");
                    break;
                default:
                    $values_query = $this->database->prepare("SELECT name, labels_hash, labels, value, bucket FROM `{$this->prefix}_histograms` WHERE name = :name");
            }

            $values_query->execute([':name' => $data['name']]);

            $values = [];
            while ($value_row = $values_query->fetch(\PDO::FETCH_ASSOC)) {
                $values[$value_row['labels_hash']][] = $value_row;
            }

            $histogram_buckets = [];
            foreach ($values as $_hash => $value) {
                foreach ($value as $bucket_value) {
                    $histogram_buckets[$bucket_value['labels']][$bucket_value['bucket']] = $bucket_value['value'];
                }
            }

            // Compute all buckets
            $labels = array_keys($histogram_buckets);
            sort($labels);
            foreach ($labels as $label_values) {
                $acc = 0;
                $decoded_values = json_decode($label_values, true);
                foreach ($data['buckets'] as $bucket) {
                    $bucket = (string)$bucket;
                    if (!isset($histogram_buckets[$label_values][$bucket])) {
                        $data['samples'][] = [
                            'name' => $data['name'] . '_bucket',
                            'labelNames' => ['le'],
                            'labelValues' => array_merge($decoded_values, [$bucket]),
                            'value' => $acc,
                        ];
                    } else {
                        $acc += $histogram_buckets[$label_values][$bucket];
                        $data['samples'][] = [
                            'name' => $data['name'] . '_' . 'bucket',
                            'labelNames' => ['le'],
                            'labelValues' => array_merge($decoded_values, [$bucket]),
                            'value' => $acc,
                        ];
                    }
                }

                // Add the count
                $data['samples'][] = [
                    'name' => $data['name'] . '_count',
                    'labelNames' => [],
                    'labelValues' => $decoded_values,
                    'value' => $acc,
                ];

                // Add the sum
                $data['samples'][] = [
                    'name' => $data['name'] . '_sum',
                    'labelNames' => [],
                    'labelValues' => $decoded_values,
                    'value' => $histogram_buckets[$label_values]['sum'],
                ];
            }
            $result[] = new MetricFamilySamples($data);
        }

        return $result;
    }

    /**
     * @return MetricFamilySamples[]
     */
    protected function collectSummaries(): array
    {
        $math = new Math();
        $result = [];

        $meta_query = $this->getMetaQuery();
        $meta_query->execute([':type' => Summary::TYPE]);

        while ($row = $meta_query->fetch(\PDO::FETCH_ASSOC)) {
            $data = json_decode($row['metadata'], true);
            $data['samples'] = [];

            switch ($this->database->getAttribute(\PDO::ATTR_DRIVER_NAME)) {
                case 'pgsql':
                    $values_query = $this->database->prepare("SELECT name, labels_hash, labels, value, time FROM \"{$this->prefix}_summaries\" WHERE name = :name");
                    break;
                default:
                    $values_query = $this->database->prepare("SELECT name, labels_hash, labels, value, time FROM `{$this->prefix}_summaries` WHERE name = :name");
            }

            $values_query->execute([':name' => $data['name']]);

            $values = [];
            while ($value_row = $values_query->fetch(\PDO::FETCH_ASSOC)) {
                $values[$value_row['labels_hash']][] = $value_row;
            }

            foreach ($values as $_hash => $samples) {
                $samples = array_map(function ($sample) {
                    $sample['value'] = (float) $sample['value'];
                    return $sample;
                }, $samples);

                $decoded_labels = json_decode(reset($samples)['labels'], true);

                // Remove old data
                $samples = array_filter($samples, function (array $value) use ($data): bool {
                    return time() - $value['time'] <= $data['maxAgeSeconds'];
                });
                if (count($samples) === 0) {
                    continue;
                }

                // Compute quantiles
                usort($samples, function (array $value1, array $value2) {
                    if ($value1['value'] === $value2['value']) {
                        return 0;
                    }
                    return ($value1['value'] < $value2['value']) ? -1 : 1;
                });

                foreach ($data['quantiles'] as $quantile) {
                    $data['samples'][] = [
                        'name' => $data['name'],
                        'labelNames' => ['quantile'],
                        'labelValues' => array_merge($decoded_labels, [$quantile]),
                        'value' => $math->quantile(array_column($samples, 'value'), $quantile),
                    ];
                }

                // Add the count
                $data['samples'][] = [
                    'name' => $data['name'] . '_count',
                    'labelNames' => [],
                    'labelValues' => $decoded_labels,
                    'value' => count($samples),
                ];

                // Add the sum
                $data['samples'][] = [
                    'name' => $data['name'] . '_sum',
                    'labelNames' => [],
                    'labelValues' => $decoded_labels,
                    'value' => array_sum(array_column($samples, 'value')),
                ];
            }

            if (count($data['samples']) > 0) {
                $result[] = new MetricFamilySamples($data);
            }
        }

        return $result;
    }

    /**
     * @return MetricFamilySamples[]
     */
    protected function collectCounters(): array
    {
        return $this->collectStandard(Counter::TYPE);
    }

    /**
     * @return MetricFamilySamples[]
     */
    protected function collectStandard(string $type): array
    {
        $result = [];

        $meta_query = $this->getMetaQuery();
        $meta_query->execute([':type' => $type]);

        while ($row = $meta_query->fetch(\PDO::FETCH_ASSOC)) {
            $data = json_decode($row['metadata'], true);
            $data['samples'] = [];

            switch ($this->database->getAttribute(\PDO::ATTR_DRIVER_NAME)) {
                case 'pgsql':
                    $values_query = $this->database->prepare("SELECT name, labels, value FROM \"{$this->prefix}_values\" WHERE name = :name AND type = :type");
                    break;
                default:
                    $values_query = $this->database->prepare("SELECT name, labels, value FROM `{$this->prefix}_values` WHERE name = :name AND type = :type");
            }

            $values_query->execute([
                ':name' => $data['name'],
                ':type' => $type,
            ]);
            while ($value_row = $values_query->fetch(\PDO::FETCH_ASSOC)) {
                $data['samples'][] = [
                    'name' => $value_row['name'],
                    'labelNames' => [],
                    'labelValues' => json_decode($value_row['labels'], true),
                    'value' => $value_row['value'],
                ];
            }

            usort($data['samples'], function ($a, $b): int {
                return strcmp(implode("", $a['labelValues']), implode("", $b['labelValues']));
            });

            $result[] = new MetricFamilySamples($data);
        }

        return $result;
    }

    /**
     * @return MetricFamilySamples[]
     */
    protected function collectGauges(): array
    {
        return $this->collectStandard(Gauge::TYPE);
    }

    /**
     * @param mixed[] $data
     * @return void
     */
    public function updateHistogram(array $data): void
    {
        $this->updateMetadata($data, Histogram::TYPE);

        switch ($this->database->getAttribute(\PDO::ATTR_DRIVER_NAME)) {
            case 'sqlite':
                $values_sql = <<<SQL
INSERT INTO  `{$this->prefix}_histograms`(`name`, `labels_hash`, `labels`, `value`, `bucket`)
  VALUES(:name,:hash,:labels,:value,:bucket)
  ON CONFLICT(name, labels_hash, bucket) DO UPDATE SET
    `value` = `value` + excluded.value;
SQL;
                break;

            case 'mysql':
                $values_sql = <<<SQL
INSERT INTO  `{$this->prefix}_histograms`(`name`, `labels_hash`, `labels`, `value`, `bucket`)
  VALUES(:name,:hash,:labels,:value,:bucket)
  ON DUPLICATE KEY UPDATE
    `value` = `value` + VALUES(`value`);
SQL;
                break;

            case 'pgsql':
                $values_sql = <<<SQL
INSERT INTO "{$this->prefix}_histograms"("name", "labels_hash", "labels", "value", "bucket")
  VALUES(:name,:hash,:labels,:value,:bucket)
  ON CONFLICT("name", "labels_hash", "bucket") DO UPDATE SET
    "value" = "{$this->prefix}_histograms"."value" + "excluded"."value";
SQL;
                break;

            default:
                throw new \RuntimeException('Unsupported database type');
        }


        $statement = $this->database->prepare($values_sql);
        $label_values = $this->encodeLabelValues($data);
        $statement->execute([
            ':name' => $data['name'],
            ':hash' => hash('sha256', $label_values),
            ':labels' => $label_values,
            ':value' => $data['value'],
            ':bucket' => 'sum',
        ]);

        $bucket_to_increase = '+Inf';
        foreach ($data['buckets'] as $bucket) {
            if ($data['value'] <= $bucket) {
                $bucket_to_increase = $bucket;
                break;
            }
        }

        $statement->execute([
            ':name' => $data['name'],
            ':hash' => hash('sha256', $label_values),
            ':labels' => $label_values,
            ':value' => 1,
            ':bucket' => $bucket_to_increase,
        ]);
    }

    /**
     * @param mixed[] $data
     * @return void
     */
    public function updateSummary(array $data): void
    {
        $this->updateMetadata($data, Summary::TYPE);

        switch ($this->database->getAttribute(\PDO::ATTR_DRIVER_NAME)) {
            case 'pgsql':
                $values_sql = <<<SQL
INSERT INTO "{$this->prefix}_summaries"("name", "labels_hash", "labels", "value", "time")
  VALUES(:name,:hash,:labels,:value,:time)
SQL;
                break;
            default:
                $values_sql = <<<SQL
INSERT INTO  `{$this->prefix}_summaries`(`name`, `labels_hash`, `labels`, `value`, `time`)
  VALUES(:name,:hash,:labels,:value,:time)
SQL;
        }

        $statement = $this->database->prepare($values_sql);
        $label_values = $this->encodeLabelValues($data);
        $statement->execute([
            ':name' => $data['name'],
            ':hash' => hash('sha256', $label_values),
            ':labels' => $label_values,
            ':value' => $data['value'],
            ':time' => time(),
        ]);
    }

    /**
     * @param mixed[] $data
     */
    public function updateGauge(array $data): void
    {
        $this->updateStandard($data, Gauge::TYPE);
    }

    /**
     * @param mixed[] $data
     */
    public function updateCounter(array $data): void
    {
        $this->updateStandard($data, Counter::TYPE);
    }

    /**
     * @param mixed[] $data
     */
    protected function updateMetadata(array $data, string $type): void
    {
        // TODO do we update metadata at all? If metadata changes then the old labels might not be correct any more?
        switch ($this->database->getAttribute(\PDO::ATTR_DRIVER_NAME)) {
            case 'sqlite':
                $metadata_sql = <<<SQL
INSERT INTO  `{$this->prefix}_metadata`
  VALUES(:name, :type, :metadata)
  ON CONFLICT(name, type) DO UPDATE SET
    `metadata` = excluded.metadata;
SQL;
                break;

            case 'mysql':
                $metadata_sql = <<<SQL
INSERT INTO  `{$this->prefix}_metadata`
  VALUES(:name, :type, :metadata)
  ON DUPLICATE KEY UPDATE
    `metadata` = VALUES(`metadata`);
SQL;
                break;

            case 'pgsql':
                $metadata_sql = <<<SQL
INSERT INTO "{$this->prefix}_metadata"
  VALUES(:name, :type, :metadata)
  ON CONFLICT("name", "type") DO UPDATE SET
    "metadata" = "excluded"."metadata";
SQL;
                break;

            default:
                throw new \RuntimeException('Unsupported database type');
        }
        $statement = $this->database->prepare($metadata_sql);
        $statement->execute([
            ':name' => $data['name'],
            ':type' => $type,
            ':metadata' => $this->encodeMetadata($data),
        ]);
    }

    /**
     * @param mixed[] $data
     */
    protected function updateStandard(array $data, string $type): void
    {
        $this->updateMetadata($data, $type);

        switch ($this->database->getAttribute(\PDO::ATTR_DRIVER_NAME)) {
            case 'sqlite':
                if ($data['command'] === Adapter::COMMAND_SET) {
                    $values_sql = <<<SQL
INSERT INTO  `{$this->prefix}_values`(`name`, `type`, `labels_hash`, `labels`, `value`)
  VALUES(:name,:type,:hash,:labels,:value)
  ON CONFLICT(name, type, labels_hash) DO UPDATE SET
    `value` = excluded.value;
SQL;
                } else {
                    $values_sql = <<<SQL
INSERT INTO  `{$this->prefix}_values`(`name`, `type`, `labels_hash`, `labels`, `value`)
  VALUES(:name,:type,:hash,:labels,:value)
  ON CONFLICT(name, type, labels_hash) DO UPDATE SET
    `value` = `value` + excluded.value;
SQL;
                }
                break;

            case 'mysql':
                if ($data['command'] === Adapter::COMMAND_SET) {
                    $values_sql = <<<SQL
INSERT INTO  `{$this->prefix}_values`(`name`, `type`, `labels_hash`, `labels`, `value`)
  VALUES(:name,:type,:hash,:labels,:value)
  ON DUPLICATE KEY UPDATE
    `value` = VALUES(`value`);
SQL;
                } else {
                    $values_sql = <<<SQL
INSERT INTO  `{$this->prefix}_values`(`name`, `type`, `labels_hash`, `labels`, `value`)
  VALUES(:name,:type,:hash,:labels,:value)
  ON DUPLICATE KEY UPDATE
    `value` = `value` + VALUES(`value`);
SQL;
                }
                break;

            case 'pgsql':
                if ($data['command'] === Adapter::COMMAND_SET) {
                    $values_sql = <<<SQL
INSERT INTO "{$this->prefix}_values"("name", "type", "labels_hash", "labels", "value")
  VALUES(:name,:type,:hash,:labels,:value)
  ON CONFLICT("name", "type", "labels_hash") DO UPDATE SET
    "value" = "excluded"."value";
SQL;
                } else {
                    $values_sql = <<<SQL
INSERT INTO "{$this->prefix}_values"("name", "type", "labels_hash", "labels", "value")
  VALUES(:name,:type,:hash,:labels,:value)
  ON CONFLICT("name", "type", "labels_hash") DO UPDATE SET
    "value" = "{$this->prefix}_values"."value" + "excluded"."value";
SQL;
                }
                break;

            default:
                throw new \RuntimeException('Unsupported database type');
        }

        $statement = $this->database->prepare($values_sql);
        $label_values = $this->encodeLabelValues($data);
        $statement->execute([
            ':name' => $data['name'],
            ':type' => $type,
            ':hash' => hash('sha256', $label_values),
            ':labels' => $label_values,
            ':value' => $data['value'],
        ]);
    }

    protected function createTables(): void
    {
        $driver = $this->database->getAttribute(\PDO::ATTR_DRIVER_NAME);

        switch ($driver) {
            case 'pgsql':
                $sql = <<<SQL
CREATE TABLE IF NOT EXISTS "{$this->prefix}_metadata" (
    "name" varchar(255) NOT NULL,
    "type" varchar(9) NOT NULL,
    "metadata" text NOT NULL,
    PRIMARY KEY ("name", "type")
)
SQL;
                break;
            default:
                $sql = <<<SQL
CREATE TABLE IF NOT EXISTS `{$this->prefix}_metadata` (
    `name` varchar(255) NOT NULL,
    `type` varchar(9) NOT NULL,
    `metadata` text NOT NULL,
    PRIMARY KEY (`name`, `type`)
)
SQL;
        }

        $this->database->query($sql);

        $hash_size = $driver == 'sqlite' ? 32 : 64;

        switch ($driver) {
            case 'pgsql':
                $sql = <<<SQL
CREATE TABLE IF NOT EXISTS "{$this->prefix}_values" (
    "name" varchar(255) NOT NULL,
    "type" varchar(9) NOT NULL,
    "labels_hash" varchar({$hash_size}) NOT NULL,
    "labels" TEXT NOT NULL,
    "value" DOUBLE PRECISION DEFAULT 0.0,
    PRIMARY KEY ("name", "type", "labels_hash")
)
SQL;
                break;
            default:
                $sql = <<<SQL
CREATE TABLE IF NOT EXISTS `{$this->prefix}_values` (
    `name` varchar(255) NOT NULL,
    `type` varchar(9) NOT NULL,
    `labels_hash` varchar({$hash_size}) NOT NULL,
    `labels` TEXT NOT NULL,
    `value` double DEFAULT 0.0,
    PRIMARY KEY (`name`, `type`, `labels_hash`)
)
SQL;
        }

        $this->database->query($sql);

        $timestamp_type = $driver == 'sqlite' ? 'timestamp' : 'int';
        $sqlIndex = null;

        switch ($driver) {
            case 'sqlite':
                $sql = <<<SQL
CREATE TABLE IF NOT EXISTS `{$this->prefix}_summaries` (
    `name` varchar(255) NOT NULL,
    `labels_hash` varchar({$hash_size}) NOT NULL,
    `labels` TEXT NOT NULL,
    `value` double DEFAULT 0.0,
    `time` {$timestamp_type} NOT NULL
);
SQL;
                $sqlIndex = "CREATE INDEX IF NOT EXISTS `name` ON `{$this->prefix}_summaries`(`name`);";
                break;

            case 'mysql':
                $sql = <<<SQL
CREATE TABLE IF NOT EXISTS `{$this->prefix}_summaries` (
    `name` varchar(255) NOT NULL,
    `labels_hash` varchar({$hash_size}) NOT NULL,
    `labels` TEXT NOT NULL,
    `value` double DEFAULT 0.0,
    `time` {$timestamp_type} NOT NULL,
    KEY `name` (`name`)
);
SQL;
                break;

            case 'pgsql':
                $sql = <<<SQL
CREATE TABLE IF NOT EXISTS "{$this->prefix}_summaries" (
    "name" varchar(255) NOT NULL,
    "labels_hash" varchar({$hash_size}) NOT NULL,
    "labels" TEXT NOT NULL,
    "value" DOUBLE PRECISION DEFAULT 0.0,
    "time" {$timestamp_type} NOT NULL
);
SQL;
                $sqlIndex = "CREATE INDEX IF NOT EXISTS \"name\" ON \"{$this->prefix}_summaries\" (\"name\");";
                break;
        }

        $this->database->query($sql);
        if ($sqlIndex !== null) {
            $this->database->query($sqlIndex);
        }

        switch ($driver) {
            case 'pgsql':
                $sql = <<<SQL
CREATE TABLE IF NOT EXISTS "{$this->prefix}_histograms" (
    "name" varchar(255) NOT NULL,
    "labels_hash" varchar({$hash_size}) NOT NULL,
    "labels" TEXT NOT NULL,
    "value" DOUBLE PRECISION DEFAULT 0.0,
    "bucket" varchar(255) NOT NULL,
    PRIMARY KEY ("name", "labels_hash", "bucket")
); 
SQL;
                break;
            default:
                $sql = <<<SQL
CREATE TABLE IF NOT EXISTS `{$this->prefix}_histograms` (
    `name` varchar(255) NOT NULL,
    `labels_hash` varchar({$hash_size}) NOT NULL,
    `labels` TEXT NOT NULL,
    `value` double DEFAULT 0.0,
    `bucket` varchar(255) NOT NULL,
    PRIMARY KEY (`name`, `labels_hash`, `bucket`)
); 
SQL;
        }

        $this->database->query($sql);
    }

    /**
     * @param mixed[] $data
     * @return string
     */
    protected function encodeMetadata(array $data): string
    {
        unset($data['value'], $data['command'], $data['labelValues']);
        $json = json_encode($data);
        if (false === $json) {
            throw new \RuntimeException(json_last_error_msg());
        }
        return $json;
    }

    /**
     * @param mixed[] $data
     * @return string
     */
    protected function encodeLabelValues(array $data): string
    {
        $json = json_encode($data['labelValues']);
        if (false === $json) {
            throw new \RuntimeException(json_last_error_msg());
        }
        return $json;
    }

    /**
     * @return \PDOStatement
     */
    private function getMetaQuery()
    {
        switch ($this->database->getAttribute(\PDO::ATTR_DRIVER_NAME)) {
            case 'pgsql':
                return $this->database->prepare("SELECT name, metadata FROM \"{$this->prefix}_metadata\" WHERE type = :type");
            default:
                return $this->database->prepare("SELECT name, metadata FROM `{$this->prefix}_metadata` WHERE type = :type");
        }
    }
}
<?php

declare(strict_types=1);

namespace Prometheus\Storage;

use InvalidArgumentException;
use Prometheus\Counter;
use Prometheus\Exception\MetricJsonException;
use Prometheus\Exception\StorageException;
use Prometheus\Gauge;
use Prometheus\Histogram;
use Prometheus\Math;
use Prometheus\MetricFamilySamples;
use Prometheus\Summary;
use RuntimeException;

class RedisNg implements Adapter
{
    const PROMETHEUS_METRIC_KEYS_SUFFIX = '_METRIC_KEYS';

    /**
     * @var mixed[]
     */
    private static $defaultOptions = [
        'host' => '127.0.0.1',
        'port' => 6379,
        'timeout' => 0.1,
        'read_timeout' => '10',
        'persistent_connections' => false,
        'password' => null,
        'user' => null,
    ];

    /**
     * @var string
     */
    private static $prefix = 'PROMETHEUS_';

    /**
     * @var mixed[]
     */
    private $options = [];

    /**
     * @var \Redis
     */
    private $redis;

    /**
     * @var boolean
     */
    private $connectionInitialized = false;

    /**
     * Redis constructor.
     * @param mixed[] $options
     */
    public function __construct(array $options = [])
    {
        $this->options = array_merge(self::$defaultOptions, $options);
        $this->redis = new \Redis();
    }

    /**
     * @param \Redis $redis
     * @return self
     * @throws StorageException
     */
    public static function fromExistingConnection(\Redis $redis): self
    {
        if ($redis->isConnected() === false) {
            throw new StorageException('Connection to Redis server not established');
        }

        $self = new self();
        $self->connectionInitialized = true;
        $self->redis = $redis;

        return $self;
    }

    /**
     * @param mixed[] $options
     */
    public static function setDefaultOptions(array $options): void
    {
        self::$defaultOptions = array_merge(self::$defaultOptions, $options);
    }

    /**
     * @param string $prefix
     */
    public static function setPrefix(string $prefix): void
    {
        self::$prefix = $prefix;
    }

    /**
     * @throws StorageException
     * @deprecated use replacement method wipeStorage from Adapter interface
     */
    public function flushRedis(): void
    {
        $this->wipeStorage();
    }

    /**
     * @inheritDoc
     */
    public function wipeStorage(): void
    {
        $this->ensureOpenConnection();

        $searchPattern = "";

        $globalPrefix = $this->redis->getOption(\Redis::OPT_PREFIX);
        // @phpstan-ignore-next-line false positive, phpstan thinks getOptions returns int
        if (is_string($globalPrefix)) {
            $searchPattern .= $globalPrefix;
        }

        $searchPattern .= self::$prefix;
        $searchPattern .= '*';

        $this->redis->eval(
            <<<LUA
redis.replicate_commands()
local cursor = "0"
repeat 
    local results = redis.call('SCAN', cursor, 'MATCH', ARGV[1])
    cursor = results[1]
    for _, key in ipairs(results[2]) do
        redis.call('DEL', key)
    end
until cursor == "0"
LUA
            ,
            [$searchPattern],
            0
        );
    }

    /**
     * @param mixed[] $data
     *
     * @return string
     */
    private function metaKey(array $data): string
    {
        return implode(':', [
            $data['name'],
            'meta'
        ]);
    }

    /**
     * @param mixed[] $data
     *
     * @return string
     */
    private function valueKey(array $data): string
    {
        return implode(':', [
            $data['name'],
            $this->encodeLabelValues($data['labelValues']),
            'value'
        ]);
    }

    /**
     * @return MetricFamilySamples[]
     * @throws StorageException
     */
    public function collect(bool $sortMetrics = true): array
    {
        $this->ensureOpenConnection();
        $metrics = $this->collectHistograms();
        $metrics = array_merge($metrics, $this->collectGauges($sortMetrics));
        $metrics = array_merge($metrics, $this->collectCounters($sortMetrics));
        $metrics = array_merge($metrics, $this->collectSummaries());
        return array_map(
            function (array $metric): MetricFamilySamples {
                return new MetricFamilySamples($metric);
            },
            $metrics
        );
    }

    /**
     * @throws StorageException
     */
    private function ensureOpenConnection(): void
    {
        if ($this->connectionInitialized === true) {
            return;
        }

        $this->connectToServer();
        $authParams = [];

        if (isset($this->options['user']) && $this->options['user'] !== '') {
            $authParams[] = $this->options['user'];
        }

        if (isset($this->options['password'])) {
            $authParams[] = $this->options['password'];
        }

        if ($authParams !== []) {
            $this->redis->auth($authParams);
        }

        if (isset($this->options['database'])) {
            $this->redis->select($this->options['database']);
        }

        $this->redis->setOption(\Redis::OPT_READ_TIMEOUT, $this->options['read_timeout']);

        $this->connectionInitialized = true;
    }

    /**
     * @throws StorageException
     */
    private function connectToServer(): void
    {
        try {
            $connection_successful = false;
            if ($this->options['persistent_connections'] !== false) {
                $connection_successful = $this->redis->pconnect(
                    $this->options['host'],
                    (int)$this->options['port'],
                    (float)$this->options['timeout']
                );
            } else {
                $connection_successful = $this->redis->connect($this->options['host'], (int)$this->options['port'], (float)$this->options['timeout']);
            }
            if (!$connection_successful) {
                throw new StorageException("Can't connect to Redis server", 0);
            }
        } catch (\RedisException $e) {
            throw new StorageException("Can't connect to Redis server", 0, $e);
        }
    }

    /**
     * @param mixed[] $data
     * @throws StorageException
     */
    public function updateHistogram(array $data): void
    {
        $this->ensureOpenConnection();
        $bucketToIncrease = '+Inf';
        foreach ($data['buckets'] as $bucket) {
            if ($data['value'] <= $bucket) {
                $bucketToIncrease = $bucket;
                break;
            }
        }
        $metaData = $data;
        unset($metaData['value'], $metaData['labelValues']);

        $this->redis->eval(
            <<<LUA
local result = redis.call('hIncrByFloat', KEYS[1], ARGV[1], ARGV[3])
redis.call('hIncrBy', KEYS[1], ARGV[2], 1)
if tonumber(result) >= tonumber(ARGV[3]) then
    redis.call('hSet', KEYS[1], '__meta', ARGV[4])
    redis.call('sAdd', KEYS[2], KEYS[1])
end
return result
LUA
            ,
            [
                $this->toMetricKey($data),
                self::$prefix . Histogram::TYPE . self::PROMETHEUS_METRIC_KEYS_SUFFIX,
                json_encode(['b' => 'sum', 'labelValues' => $data['labelValues']]),
                json_encode(['b' => $bucketToIncrease, 'labelValues' => $data['labelValues']]),
                $data['value'],
                json_encode($metaData),
            ],
            2
        );
    }

    /**
     * @param mixed[] $data
     * @throws StorageException
     */
    public function updateSummary(array $data): void
    {
        $this->ensureOpenConnection();
// store meta
        $summaryKey = self::$prefix . Summary::TYPE . self::PROMETHEUS_METRIC_KEYS_SUFFIX;
        $summaryKeyIndexKey = self::$prefix . Summary::TYPE . self::PROMETHEUS_METRIC_KEYS_SUFFIX . ":keys";
        if (!$this->redis->sIsMember($summaryKeyIndexKey, $summaryKey . ':' . $data["name"])) {
            $this->redis->sAdd($summaryKeyIndexKey, $summaryKey . ':' . $data["name"]);
        }

        $metaKey = $summaryKey . ':' . $this->metaKey($data);
        $json = json_encode($this->metaData($data));
        if (false === $json) {
            throw new RuntimeException(json_last_error_msg());
        }
        $this->redis->setnx($metaKey, $json);

        // store value key
        $valueKey = $summaryKey . ':' . $this->valueKey($data);

        $json = json_encode($this->encodeLabelValues($data['labelValues']));
        if (false === $json) {
            throw new RuntimeException(json_last_error_msg());
        }
        $this->redis->setnx($valueKey, $json);

        // trick to handle uniqid collision
        $done = false;
        while (!$done) {
            $sampleKey = $valueKey . ':' . uniqid('', true);
            $done = $this->redis->set($sampleKey, $data['value'], ['NX', 'EX' => $data['maxAgeSeconds']]);
            $this->redis->sAdd($summaryKey . ':' . $data["name"] . ":value:keys", $sampleKey);
        }
    }

    /**
     * @param mixed[] $data
     * @throws StorageException
     */
    public function updateGauge(array $data): void
    {
        $this->ensureOpenConnection();
        $metaData = $data;
        unset($metaData['value'], $metaData['labelValues'], $metaData['command']);
        $this->redis->eval(
            <<<LUA
local result = redis.call(ARGV[1], KEYS[1], ARGV[2], ARGV[3])

if ARGV[1] == 'hSet' then
    if result == 1 then
        redis.call('hSet', KEYS[1], '__meta', ARGV[4])
        redis.call('sAdd', KEYS[2], KEYS[1])
    end
else
    if result == ARGV[3] then
        redis.call('hSet', KEYS[1], '__meta', ARGV[4])
        redis.call('sAdd', KEYS[2], KEYS[1])
    end
end
LUA
            ,
            [
                $this->toMetricKey($data),
                self::$prefix . Gauge::TYPE . self::PROMETHEUS_METRIC_KEYS_SUFFIX,
                $this->getRedisCommand($data['command']),
                json_encode($data['labelValues']),
                $data['value'],
                json_encode($metaData),
            ],
            2
        );
    }

    /**
     * @param mixed[] $data
     * @throws StorageException
     */
    public function updateCounter(array $data): void
    {
        $this->ensureOpenConnection();
        $metaData = $data;
        unset($metaData['value'], $metaData['labelValues'], $metaData['command']);
        $this->redis->eval(
            <<<LUA
local result = redis.call(ARGV[1], KEYS[1], ARGV[3], ARGV[2])
local added = redis.call('sAdd', KEYS[2], KEYS[1])
if added == 1 then
    redis.call('hMSet', KEYS[1], '__meta', ARGV[4])
end
return result
LUA
            ,
            [
                $this->toMetricKey($data),
                self::$prefix . Counter::TYPE . self::PROMETHEUS_METRIC_KEYS_SUFFIX,
                $this->getRedisCommand($data['command']),
                $data['value'],
                json_encode($data['labelValues']),
                json_encode($metaData),
            ],
            2
        );
    }


    /**
     * @param mixed[] $data
     * @return mixed[]
     */
    private function metaData(array $data): array
    {
        $metricsMetaData = $data;
        unset($metricsMetaData['value'], $metricsMetaData['command'], $metricsMetaData['labelValues']);
        return $metricsMetaData;
    }

    /**
     * @return mixed[]
     * @throws MetricJsonException
     */
    private function collectHistograms(): array
    {
        $keys = $this->redis->sMembers(self::$prefix . Histogram::TYPE . self::PROMETHEUS_METRIC_KEYS_SUFFIX);
        sort($keys);
        $histograms = [];
        foreach ($keys as $key) {
            $raw = $this->redis->hGetAll(ltrim($key, $this->redis->_prefix('')));
            if (!isset($raw['__meta'])) {
                continue;
            }
            $histogram = json_decode($raw['__meta'], true);
            unset($raw['__meta']);
            $histogram['samples'] = [];

            // Add the Inf bucket so we can compute it later on
            $histogram['buckets'][] = '+Inf';

            $allLabelValues = [];
            foreach (array_keys($raw) as $k) {
                $d = json_decode($k, true);
                if ($d['b'] == 'sum') {
                    continue;
                }
                $allLabelValues[] = $d['labelValues'];
            }

            if (json_last_error() !== JSON_ERROR_NONE) {
                $this->throwMetricJsonException($key);
            }

            // We need set semantics.
            // This is the equivalent of array_unique but for arrays of arrays.
            $allLabelValues = array_map("unserialize", array_unique(array_map("serialize", $allLabelValues)));
            sort($allLabelValues);

            foreach ($allLabelValues as $labelValues) {
                // Fill up all buckets.
                // If the bucket doesn't exist fill in values from
                // the previous one.
                $acc = 0;
                foreach ($histogram['buckets'] as $bucket) {
                    $bucketKey = json_encode(['b' => $bucket, 'labelValues' => $labelValues]);
                    if (!isset($raw[$bucketKey])) {
                        $histogram['samples'][] = [
                            'name' => $histogram['name'] . '_bucket',
                            'labelNames' => ['le'],
                            'labelValues' => array_merge($labelValues, [$bucket]),
                            'value' => $acc,
                        ];
                    } else {
                        $acc += $raw[$bucketKey];
                        $histogram['samples'][] = [
                            'name' => $histogram['name'] . '_bucket',
                            'labelNames' => ['le'],
                            'labelValues' => array_merge($labelValues, [$bucket]),
                            'value' => $acc,
                        ];
                    }
                }

                // Add the count
                $histogram['samples'][] = [
                    'name' => $histogram['name'] . '_count',
                    'labelNames' => [],
                    'labelValues' => $labelValues,
                    'value' => $acc,
                ];

                // Add the sum
                $histogram['samples'][] = [
                    'name' => $histogram['name'] . '_sum',
                    'labelNames' => [],
                    'labelValues' => $labelValues,
                    'value' => $raw[json_encode(['b' => 'sum', 'labelValues' => $labelValues])],
                ];
            }
            $histograms[] = $histogram;
        }
        return $histograms;
    }

    /**
     * @param string $key
     *
     * @return string
     */
    private function removePrefixFromKey(string $key): string  /** @phpstan-ignore-line */
    {
        // @phpstan-ignore-next-line false positive, phpstan thinks getOptions returns int
        if ($this->redis->getOption(\Redis::OPT_PREFIX) === null) {
            return $key;
        }
        // @phpstan-ignore-next-line false positive, phpstan thinks getOptions returns int
        return substr($key, strlen($this->redis->getOption(\Redis::OPT_PREFIX)));
    }

    /**
     * @return mixed[]
     */
    private function collectSummaries(): array
    {
        $math = new Math();
        $summaryKeyIndexKey = self::$prefix . Summary::TYPE . self::PROMETHEUS_METRIC_KEYS_SUFFIX . ":keys";

        $keys = $this->redis->sMembers($summaryKeyIndexKey);
        $summaries = [];
        foreach ($keys as $metaKey) {
            $rawSummary = $this->redis->get($metaKey . ':meta');
            if ($rawSummary === false) {
                continue;
            }
            $summary = json_decode($rawSummary, true);
            $metaData = $summary;
            $data = [
                'name' => $metaData['name'],
                'help' => $metaData['help'],
                'type' => $metaData['type'],
                'labelNames' => $metaData['labelNames'],
                'maxAgeSeconds' => $metaData['maxAgeSeconds'],
                'quantiles' => $metaData['quantiles'],
                'samples' => [],
            ];
            $values = $this->redis->sMembers($metaKey . ':value:keys');
            $samples = [];
            foreach ($values as $valueKey) {
                $rawValue = explode(":", $valueKey);
                if ($rawValue === false) {
                    continue;
                }
                $encodedLabelValues = $rawValue[2];
                $decodedLabelValues = $this->decodeLabelValues($encodedLabelValues);

                $return = $this->redis->get($valueKey);
                if ($return !== false) {
                    $samples[] = (float)$return;
                }
            }
            if (count($samples) === 0) {
                if (isset($valueKey)) {
                    $this->redis->del($valueKey);
                }

                continue;
            }

            assert(isset($decodedLabelValues));

            // Compute quantiles
            sort($samples);
            foreach ($data['quantiles'] as $quantile) {
                $data['samples'][] = [
                    'name' => $metaData['name'],
                    'labelNames' => ['quantile'],
                    'labelValues' => array_merge($decodedLabelValues, [$quantile]),
                    'value' => $math->quantile($samples, $quantile),
                ];
            }

            // Add the count
            $data['samples'][] = [
                'name' => $metaData['name'] . '_count',
                'labelNames' => [],
                'labelValues' => $decodedLabelValues,
                'value' => count($samples),
            ];

            // Add the sum
            $data['samples'][] = [
                'name' => $metaData['name'] . '_sum',
                'labelNames' => [],
                'labelValues' => $decodedLabelValues,
                'value' => array_sum($samples),
            ];


            $summaries[] = $data;
        }
        return $summaries;
    }

    /**
     * @return mixed[]
     */
    private function collectGauges(bool $sortMetrics = true): array
    {
        $keys = $this->redis->sMembers(self::$prefix . Gauge::TYPE . self::PROMETHEUS_METRIC_KEYS_SUFFIX);
        sort($keys);
        $gauges = [];
        foreach ($keys as $key) {
            $raw = $this->redis->hGetAll(ltrim($key, $this->redis->_prefix('')));
            if (!isset($raw['__meta'])) {
                continue;
            }
            $gauge = json_decode($raw['__meta'], true);
            unset($raw['__meta']);
            $gauge['samples'] = [];
            foreach ($raw as $k => $value) {
                $gauge['samples'][] = [
                    'name' => $gauge['name'],
                    'labelNames' => [],
                    'labelValues' => json_decode($k, true),
                    'value' => $value,
                ];
                if (json_last_error() !== JSON_ERROR_NONE) {
                    $this->throwMetricJsonException($key, $gauge['name']);
                }
            }

            if ($sortMetrics) {
                usort($gauge['samples'], function ($a, $b): int {
                    return strcmp(implode("", $a['labelValues']), implode("", $b['labelValues']));
                });
            }

            $gauges[] = $gauge;
        }
        return $gauges;
    }

    /**
     * @return mixed[]
     * @throws MetricJsonException
     */
    private function collectCounters(bool $sortMetrics = true): array
    {
        $keys = $this->redis->sMembers(self::$prefix . Counter::TYPE . self::PROMETHEUS_METRIC_KEYS_SUFFIX);
        sort($keys);
        $counters = [];
        foreach ($keys as $key) {
            $raw = $this->redis->hGetAll(ltrim($key, $this->redis->_prefix('')));
            if (!isset($raw['__meta'])) {
                continue;
            }
            $counter = json_decode($raw['__meta'], true);
            unset($raw['__meta']);
            $counter['samples'] = [];
            foreach ($raw as $k => $value) {
                $counter['samples'][] = [
                    'name' => $counter['name'],
                    'labelNames' => [],
                    'labelValues' => json_decode($k, true),
                    'value' => $value,
                ];
                if (json_last_error() !== JSON_ERROR_NONE) {
                    $this->throwMetricJsonException($key, $counter['name']);
                }
            }

            if ($sortMetrics) {
                usort($counter['samples'], function ($a, $b): int {
                    return strcmp(implode("", $a['labelValues']), implode("", $b['labelValues']));
                });
            }

            $counters[] = $counter;
        }
        return $counters;
    }

    /**
     * @param int $cmd
     * @return string
     */
    private function getRedisCommand(int $cmd): string
    {
        switch ($cmd) {
            case Adapter::COMMAND_INCREMENT_INTEGER:
                return 'hIncrBy';
            case Adapter::COMMAND_INCREMENT_FLOAT:
                return 'hIncrByFloat';
            case Adapter::COMMAND_SET:
                return 'hSet';
            default:
                throw new InvalidArgumentException("Unknown command");
        }
    }

    /**
     * @param mixed[] $data
     * @return string
     */
    private function toMetricKey(array $data): string
    {
        return implode(':', [self::$prefix, $data['type'], $data['name']]);
    }

    /**
     * @param mixed[] $values
     * @return string
     * @throws RuntimeException
     */
    private function encodeLabelValues(array $values): string
    {
        $json = json_encode($values);
        if (false === $json) {
            throw new RuntimeException(json_last_error_msg());
        }
        return base64_encode($json);
    }

    /**
     * @param string $values
     * @return mixed[]
     * @throws RuntimeException
     */
    private function decodeLabelValues(string $values): array
    {
        $json = base64_decode($values, true);
        if (false === $json) {
            throw new RuntimeException('Cannot base64 decode label values');
        }
        $decodedValues = json_decode($json, true);
        if (false === $decodedValues) {
            throw new RuntimeException(json_last_error_msg());
        }
        return $decodedValues;
    }

    /**
     * @param string $redisKey
     * @param string|null $metricName
     * @return void
     * @throws MetricJsonException
     */
    private function throwMetricJsonException(string $redisKey, ?string $metricName = null): void
    {
        $metricName = $metricName ?? 'unknown';
        $message = 'Json error: ' . json_last_error_msg() . ' redis key : ' . $redisKey . ' metric name: ' . $metricName;
        throw new MetricJsonException($message, 0, null, $metricName);
    }
}
<?php

declare(strict_types=1);

namespace Prometheus\Storage;

use Prometheus\Exception\StorageException;
use Prometheus\MetricFamilySamples;

interface Adapter
{
    const COMMAND_INCREMENT_INTEGER = 1;
    const COMMAND_INCREMENT_FLOAT = 2;
    const COMMAND_SET = 3;

    /**
     * @return MetricFamilySamples[]
     */
    public function collect(): array;

    /**
     * @param mixed[] $data
     * @return void
     */
    public function updateSummary(array $data): void;

    /**
     * @param mixed[] $data
     * @return void
     */
    public function updateHistogram(array $data): void;

    /**
     * @param mixed[] $data
     * @return void
     */
    public function updateGauge(array $data): void;

    /**
     * @param mixed[] $data
     * @return void
     */
    public function updateCounter(array $data): void;

    /**
     * Removes all previously stored metrics from underlying storage
     *
     * @throws StorageException
     * @return void
     */
    public function wipeStorage(): void;
}
<?php

declare(strict_types=1);

namespace Prometheus\Storage;

use APCuIterator;
use Prometheus\Exception\StorageException;
use Prometheus\Math;
use Prometheus\MetricFamilySamples;
use RuntimeException;
use UnexpectedValueException;

class APCng implements Adapter
{
    /** @var string Default prefix to use for APCu keys. */
    const PROMETHEUS_PREFIX = 'prom';

    private const MAX_LOOPS = 10;

    /**
     * @var int
     */
    private $precisionMultiplier;

    /** @var string APCu key where array of all discovered+created metainfo keys is stored */
    private $metainfoCacheKey;

    /** @var string APCu key where count of all added metainfo keys is stored */
    private $metaInfoCounterKey;

    /** @var string APCu key pattern where array of all added metainfo keys is stored */
    private $metaInfoCountedMetricKeyPattern;

    /** @var string Prefix to use for APCu keys. */
    private $prometheusPrefix;

    /**
     * @var array<string, array<mixed>>
     */
    private $metaCache = [];

    /**
     * APCng constructor.
     *
     * @param string $prometheusPrefix Prefix for APCu keys (defaults to {@see PROMETHEUS_PREFIX}).
     *
     * @throws StorageException
     */
    public function __construct(string $prometheusPrefix = self::PROMETHEUS_PREFIX, int $decimalPrecision = 3)
    {
        if (!extension_loaded('apcu')) {
            throw new StorageException('APCu extension is not loaded');
        }
        if (!apcu_enabled()) {
            throw new StorageException('APCu is not enabled');
        }

        $this->prometheusPrefix = $prometheusPrefix;
        $this->metainfoCacheKey = implode(':', [ $this->prometheusPrefix, 'metainfocache' ]);
        $this->metaInfoCounterKey = implode(':', [ $this->prometheusPrefix, 'metainfocounter' ]);
        $this->metaInfoCountedMetricKeyPattern = implode(':', [ $this->prometheusPrefix, 'metainfocountedmetric_#COUNTER#' ]);

        if ($decimalPrecision < 0 || $decimalPrecision > 6) {
            throw new UnexpectedValueException(
                sprintf('Decimal precision %d is not from interval <0;6>.', $decimalPrecision)
            );
        }

        $this->precisionMultiplier = 10 ** $decimalPrecision;
    }

    /**
     * @return MetricFamilySamples[]
     */
    public function collect(bool $sortMetrics = true): array
    {
        $metrics = $this->collectHistograms();
        $metrics = array_merge($metrics, $this->collectGauges($sortMetrics));
        $metrics = array_merge($metrics, $this->collectCounters($sortMetrics));
        $metrics = array_merge($metrics, $this->collectSummaries());
        return $metrics;
    }

    /**
     * @param mixed[] $data
     * @throws RuntimeException
     */
    public function updateHistogram(array $data): void
    {
        // Initialize or atomically increment the sum
        // Taken from https://github.com/prometheus/client_golang/blob/66058aac3a83021948e5fb12f1f408ff556b9037/prometheus/value.go#L91
        $sumKey = $this->histogramBucketValueKey($data, 'sum');

        $old = apcu_fetch($sumKey);

        if ($old === false) {
            // If sum does not exist, initialize it, store the metadata for the new histogram
            apcu_add($sumKey, 0, 0);
            $this->storeMetadata($data);
            $this->storeLabelKeys($data);
        }

        $this->incrementKeyWithValue($sumKey, $data['value']);

        // Figure out in which bucket the observation belongs
        $bucketToIncrease = '+Inf';
        foreach ($data['buckets'] as $bucket) {
            if ($data['value'] <= $bucket) {
                $bucketToIncrease = $bucket;
                break;
            }
        }

        // Initialize and increment the bucket
        $bucketKey = $this->histogramBucketValueKey($data, $bucketToIncrease);
        if (!apcu_exists($bucketKey)) {
            apcu_add($bucketKey, 0);
        }
        apcu_inc($bucketKey);
    }

    /**
     * For each second, store an incrementing counter which points to each individual observation, like this:
     *     prom:bla..blabla:value:16781560:observations = 199
     * Then we know that for the 1-second period at unix timestamp 16781560, 199 observations are stored, and they can
     * be retrieved using APC keynames "prom:...:16781560.0" thorough "prom:...:16781560.198"
     * We can deterministically calculate the intervening timestamps by subtracting maxAge, to get a range of seconds
     * when generating a summary, e.g. 16781560 back to 16780960 for a 600sec maxAge. Then collect observation counts
     * for each second, programmatically generate the APC keys for each individual observation, and we're able to avoid
     * performing a full APC key scan, which can block for several seconds if APCu contains a few million keys.
     *
     * @param mixed[] $data
     * @throws RuntimeException
     */
    public function updateSummary(array $data): void
    {
        // store value key; store metadata & labels if new
        $valueKey = $this->valueKey($data);
        $new = apcu_add($valueKey, $this->encodeLabelValues($data['labelValues']), 0);
        if ($new) {
            $this->storeMetadata($data, false);
            $this->storeLabelKeys($data);
        }
        $sampleKeyPrefix = $valueKey . ':' . time();
        $sampleCountKey = $sampleKeyPrefix . ':observations';

        // Check if sample counter for this timestamp already exists, so we can deterministically
        // store observations+counts, one key per second
        // Atomic increment of the observation counter, or initialize if new
        if (apcu_fetch($sampleCountKey) === false) {
            apcu_add($sampleCountKey, 0, $data['maxAgeSeconds']);
        }

        $sampleCount = apcu_inc($sampleCountKey);
        if ($sampleCount === false) { /** @phpstan-ignore-line */
            throw new RuntimeException('Failed to increment summary sample counter');
        }

        // We now have a deterministic keyname for this observation; let's save the observed value
        $sampleKey = $sampleKeyPrefix . '.' . ($sampleCount - 1);
        apcu_add($sampleKey, $data['value'], $data['maxAgeSeconds']);
    }

    /**
     * @param mixed[] $data
     * @throws RuntimeException
     */
    public function updateGauge(array $data): void
    {
        $valueKey = $this->valueKey($data);
        $old = apcu_fetch($valueKey);
        if ($data['command'] === Adapter::COMMAND_SET) {
            $new = $this->convertToIncrementalInteger($data['value']);
            if ($old === false) {
                apcu_store($valueKey, $new, 0);
                $this->storeMetadata($data);
                $this->storeLabelKeys($data);

                return;
            }

            for ($loops = 0; $loops < self::MAX_LOOPS; $loops++) {
                if (apcu_cas($valueKey, $old, $new)) {
                    break;
                }
                $old = apcu_fetch($valueKey);
                if ($old === false) {
                    apcu_store($valueKey, $new, 0);
                    $this->storeMetadata($data);
                    $this->storeLabelKeys($data);

                    return;
                }
            }

            return;
        }

        if ($old === false) {
            apcu_add($valueKey, 0, 0);
            $this->storeMetadata($data);
            $this->storeLabelKeys($data);
        }

        if ($data['value'] > 0) {
            $this->incrementKeyWithValue($valueKey, $data['value']);
        } elseif ($data['value'] < 0) {
            $this->decrementKeyWithValue($valueKey, -$data['value']);
        }
    }

    /**
     * @param mixed[] $data
     * @throws RuntimeException
     */
    public function updateCounter(array $data): void
    {
        $valueKey = $this->valueKey($data);
        $old = apcu_fetch($valueKey);

        if ($old === false) {
            apcu_add($valueKey, 0, 0);
            $this->storeMetadata($data);
            $this->storeLabelKeys($data);
        }

        $this->incrementKeyWithValue($valueKey, $data['value']);
    }

    /**
     * @param array<string> $metaData
     * @param string $labels
     * @return string
     */
    private function assembleLabelKey(array $metaData, string $labels): string
    {
        return implode(':', [ $this->prometheusPrefix, $metaData['type'], $metaData['name'], $labels, 'label' ]);
    }

    /**
     * Store ':label' keys for each metric's labelName in APCu.
     *
     * @param array<mixed> $data
     * @return void
     */
    private function storeLabelKeys(array $data): void
    {
        // Store labelValues in each labelName key
        foreach ($data['labelNames'] as $seq => $label) {
            $this->addItemToKey(implode(':', [
                $this->prometheusPrefix,
                $data['type'],
                $data['name'],
                $label,
                'label'
            ]), isset($data['labelValues']) ? (string)$data['labelValues'][$seq] : ''); // may not need the isset check
        }
    }

    /**
     * Ensures an array serialized into APCu contains exactly one copy of a given string
     *
     * @return void
     * @throws RuntimeException
     */
    private function addItemToKey(string $key, string $item): void
    {
        // Modify serialized array stored in $key
        $arr = apcu_fetch($key);
        if (false === $arr) {
            $arr = [];
        }
        $_item = $this->encodeLabelKey($item);
        if (!array_key_exists($_item, $arr)) {
            $arr[$_item] = 1;
            apcu_store($key, $arr, 0);
        }
    }

    /**
     * Removes all previously stored data from apcu
     *
     * NOTE: This is non-atomic: while it's iterating APCu, another thread could write a new Prometheus key that doesn't get erased.
     * In case this happens, getMetas() calls scanAndBuildMetainfoCache before reading metainfo back: this will ensure "orphaned"
     * metainfo gets enumerated.
     *
     * @return void
     */
    public function wipeStorage(): void
    {
        //                   /      / | PCRE expresion boundary
        //                    ^       | match from first character only
        //                     %s:    | common prefix substitute with colon suffix
        //                        .+  | at least one additional character
        $matchAll = sprintf('/^%s:.+/', $this->prometheusPrefix);

        foreach (new APCuIterator($matchAll, APC_ITER_KEY) as $key) {
            apcu_delete($key);
        }

        apcu_delete($this->metaInfoCounterKey);
        apcu_delete($this->metainfoCacheKey);
    }

    /**
     * Scans the APCu keyspace for all metainfo keys. A new metainfo cache array is built,
     * which references all metadata keys in APCu at that moment. This prevents a corner-case
     * where an orphaned key, while remaining writable, is rendered permanently invisible when reading
     * or enumerating metrics.
     *
     * Writing the cache to APCu allows it to be shared by other threads and by subsequent calls to getMetas(). This
     * reduces contention on APCu from repeated scans, and provides about a 2.5x speed-up when calling $this->collect().
     * The cache TTL is very short (default: 1sec), so if new metrics are tracked after the cache is built, they will
     * be readable at most 1 second after being written.
     *
     * @return array<string, array<array{key: string, value: array<mixed>}>>
     */
    private function scanAndBuildMetainfoCache(): array
    {
        $arr = [];

        $counter = (int) apcu_fetch($this->metaInfoCounterKey);

        for ($i = 1; $i <= $counter; $i++) {
            $metaCounterKey = $this->metaCounterKey($i);
            $metaKey = apcu_fetch($metaCounterKey);

            if (!is_string($metaKey)) {
                throw new UnexpectedValueException(
                    sprintf('Invalid meta counter key: %s', $metaCounterKey)
                );
            }

            if (preg_match('/' . $this->prometheusPrefix . ':([^:]+):.*:meta/', $metaKey, $matches) !== 1) {
                throw new UnexpectedValueException(
                    sprintf('Invalid meta key: %s', $metaKey)
                );
            }

            $type = $matches[1];

            if (!isset($arr[$type])) {
                $arr[$type] = [];
            }

            /** @var array<mixed>|false $metaInfo */
            $metaInfo = apcu_fetch($metaKey);

            if ($metaInfo === false) {
                throw new UnexpectedValueException(
                    sprintf('Meta info missing for meta key: %s', $metaKey)
                );
            }

            $arr[$type][] = ['key' => $metaKey, 'value' => $metaInfo];
        }

        apcu_store($this->metainfoCacheKey, $arr, 0);

        return $arr;
    }

    /**
     * @param mixed[] $data
     * @return string
     */
    private function metaKey(array $data): string
    {
        return implode(':', [$this->prometheusPrefix, $data['type'], $data['name'], 'meta']);
    }

    /**
     * @param mixed[] $data
     * @return string
     */
    private function valueKey(array $data): string
    {
        return implode(':', [
            $this->prometheusPrefix,
            $data['type'],
            $data['name'],
            $this->encodeLabelValues($data['labelValues']),
            'value',
        ]);
    }

    /**
     * @param mixed[] $data
     * @param string|int $bucket
     * @return string
     */
    private function histogramBucketValueKey(array $data, $bucket): string
    {
        return implode(':', [
            $this->prometheusPrefix,
            $data['type'],
            $data['name'],
            $this->encodeLabelValues($data['labelValues']),
            $bucket,
            'value',
        ]);
    }

    /**
     * @param mixed[] $data
     * @return mixed[]
     */
    private function metaData(array $data): array
    {
        $metricsMetaData = $data;
        unset($metricsMetaData['value'], $metricsMetaData['command'], $metricsMetaData['labelValues']);
        return $metricsMetaData;
    }

    /**
     * When given a ragged 2D array $labelValues of arbitrary size, and a 1D array $labelNames containing one
     * string labeling each row of $labelValues, return an array-of-arrays containing all possible permutations
     * of labelValues, with the sub-array elements in order of labelName.
     *
     * Example input:
     *  $labelNames:  ['endpoint', 'method', 'result']
     *  $labelValues: [0] => ['/', '/private', '/metrics'], // "endpoint"
     *                [1] => ['put', 'get', 'post'],        // "method"
     *                [2] => ['success', 'fail']            // "result"
     * Returned array:
     *  [0] => ['/', 'put', 'success'],          [1] => ['/', 'put', 'fail'],             [2] => ['/', 'get', 'success'],
     *  [3] => ['/', 'get', 'fail'],             [4] => ['/', 'post', 'success'],         [5] => ['/', 'post', 'fail'],
     *  [6] => ['/private', 'put', 'success'],   [7] => ['/private', 'put', 'fail'],      [8] => ['/private', 'get', 'success'],
     *  [9] => ['/private', 'get', 'fail'],     [10] => ['/private', 'post', 'success'], [11] => ['/private', 'post', 'fail'],
     *  [12] => ['/metrics', 'put', 'success'], [13] => ['/metrics', 'put', 'fail'],     [14] => ['/metrics', 'get', 'success'],
     *  [15] => ['/metrics', 'get', 'fail'],    [16] => ['/metrics', 'post', 'success'], [17] => ['/metrics', 'post', 'fail']
     * @param array<array> $labelValues
     * @return \Generator<array>
     */
    private function buildPermutationTree(array $labelValues): \Generator /** @phpstan-ignore-line */
    {
        if (count($labelValues) > 0) {
            $lastIndex = array_key_last($labelValues);
            $currentValue = array_pop($labelValues);
            if ($currentValue != null) {
                foreach ($this->buildPermutationTree($labelValues) as $prefix) {
                    foreach ($currentValue as $value) {
                        yield $prefix + [$lastIndex => $value];
                    }
                }
            }
        } else {
            yield [];
        }
    }

    /**
     * @return MetricFamilySamples[]
     */
    private function collectCounters(bool $sortMetrics = true): array
    {
        $counters = [];
        foreach ($this->getMetas('counter') as $counter) {
            $metaData = json_decode($counter['value'], true);
            $data = [
                'name' => $metaData['name'],
                'help' => $metaData['help'],
                'type' => $metaData['type'],
                'labelNames' => $metaData['labelNames'],
                'samples' => [],
            ];
            foreach ($this->getValues('counter', $metaData) as $value) {
                $parts = explode(':', $value['key']);
                $labelValues = $parts[3];
                $data['samples'][] = [
                    'name' => $metaData['name'],
                    'labelNames' => [],
                    'labelValues' => $this->decodeLabelValues($labelValues),
                    'value' => $this->convertIncrementalIntegerToFloat($value['value']),
                ];
            }

            if ($sortMetrics) {
                $this->sortSamples($data['samples']);
            }

            $counters[] = new MetricFamilySamples($data);
        }
        return $counters;
    }

    /**
     * When given a type ('histogram', 'gauge', or 'counter'), return an iterable array of matching records retrieved from APCu
     *
     * @param string $type
     * @return array<array>
     */
    private function getMetas(string $type): array /** @phpstan-ignore-line */
    {
        $arr = [];
        $counterModified = 0;
        $counterModifiedInfo = apcu_key_info($this->metaInfoCounterKey);

        if ($counterModifiedInfo !== null) {
            $counterModified = (int) $counterModifiedInfo['mtime'];
        }

        $cacheModified = 0;
        $cacheModifiedInfo = apcu_key_info($this->metainfoCacheKey);

        if ($cacheModifiedInfo !== null) {
            $cacheModified = (int) $cacheModifiedInfo['mtime'];
        }

        $cacheNeedsRebuild = $counterModified >= $cacheModified || $cacheModified === 0;
        $metaCache = null;

        if (isset($this->metaCache[$type]) && !$cacheNeedsRebuild) {
            return $this->metaCache[$type];
        }

        if ($cacheNeedsRebuild) {
            $metaCache = $this->scanAndBuildMetainfoCache();
        }

        if ($metaCache === null) {
            $metaCache = apcu_fetch($this->metainfoCacheKey);
        }

        $this->metaCache = $metaCache;

        return $this->metaCache[$type] ?? [];
    }

    /**
     * When given a type ('histogram', 'gauge', or 'counter') and metaData array, return an iterable array of matching records retrieved from APCu
     *
     * @param string $type
     * @param array<mixed> $metaData
     * @return array<array>
     */
    private function getValues(string $type, array $metaData): array /** @phpstan-ignore-line */
    {
        $labels = $arr = [];
        foreach (array_values($metaData['labelNames']) as $label) {
            $labelKey = $this->assembleLabelKey($metaData, $label);
            if (is_array($tmp = apcu_fetch($labelKey))) {
                $labels[] = array_map([$this, 'decodeLabelKey'], array_keys($tmp));
            }
        }
        // Append the histogram bucket-list and the histogram-specific label 'sum' to labels[] then generate the permutations
        if (isset($metaData['buckets'])) {
            $metaData['buckets'][] = 'sum';
            $labels[] = $metaData['buckets'];
        }

        $labelValuesList = $this->buildPermutationTree($labels);
        unset($labels);
        $histogramBucket = '';
        foreach ($labelValuesList as $labelValues) {
            // Extract bucket value from permuted element, if present, then construct the key and retrieve
            if (isset($metaData['buckets'])) {
                $histogramBucket = ':' . array_pop($labelValues);
            }
            $key = $this->prometheusPrefix . ":{$type}:{$metaData['name']}:" . $this->encodeLabelValues($labelValues) . $histogramBucket . ':value';
            if (false !== ($value = apcu_fetch($key))) {
                $arr[] = [ 'key' => $key, 'value' => $value ];
            }
        }
        return $arr;
    }

    /**
     * @return MetricFamilySamples[]
     */
    private function collectGauges(bool $sortMetrics = true): array
    {
        $gauges = [];
        foreach ($this->getMetas('gauge') as $gauge) {
            $metaData = json_decode($gauge['value'], true);
            $data = [
                'name' => $metaData['name'],
                'help' => $metaData['help'],
                'type' => $metaData['type'],
                'labelNames' => $metaData['labelNames'],
                'samples' => [],
            ];
            foreach ($this->getValues('gauge', $metaData) as $value) {
                $parts = explode(':', $value['key']);
                $labelValues = $parts[3];
                $data['samples'][] = [
                    'name' => $metaData['name'],
                    'labelNames' => [],
                    'labelValues' => $this->decodeLabelValues($labelValues),
                    'value' => $this->convertIncrementalIntegerToFloat($value['value']),
                ];
            }

            if ($sortMetrics) {
                $this->sortSamples($data['samples']);
            }

            $gauges[] = new MetricFamilySamples($data);
        }
        return $gauges;
    }

    /**
     * @return MetricFamilySamples[]
     */
    private function collectHistograms(): array
    {
        $histograms = [];
        foreach ($this->getMetas('histogram') as $histogram) {
            $metaData = json_decode($histogram['value'], true);

            // Add the Inf bucket so we can compute it later on
            $metaData['buckets'][] = '+Inf';

            $data = [
                'name' => $metaData['name'],
                'help' => $metaData['help'],
                'type' => $metaData['type'],
                'labelNames' => $metaData['labelNames'],
                'buckets' => $metaData['buckets'],
            ];

            $histogramBuckets = [];
            foreach ($this->getValues('histogram', $metaData) as $value) {
                $parts = explode(':', $value['key']);
                $labelValues = $parts[3];
                $bucket = $parts[4];
                // Key by labelValues
                $histogramBuckets[$labelValues][$bucket] = $value['value'];
            }

            // Compute all buckets
            $labels = array_keys($histogramBuckets);
            sort($labels);
            foreach ($labels as $labelValues) {
                $acc = 0;
                $decodedLabelValues = $this->decodeLabelValues($labelValues);
                foreach ($data['buckets'] as $bucket) {
                    $bucket = (string)$bucket;
                    if (!isset($histogramBuckets[$labelValues][$bucket])) {
                        $data['samples'][] = [
                            'name' => $metaData['name'] . '_bucket',
                            'labelNames' => ['le'],
                            'labelValues' => array_merge($decodedLabelValues, [$bucket]),
                            'value' => $acc,
                        ];
                    } else {
                        $acc += $histogramBuckets[$labelValues][$bucket];
                        $data['samples'][] = [
                            'name' => $metaData['name'] . '_' . 'bucket',
                            'labelNames' => ['le'],
                            'labelValues' => array_merge($decodedLabelValues, [$bucket]),
                            'value' => $acc,
                        ];
                    }
                }

                // Add the count
                $data['samples'][] = [
                    'name' => $metaData['name'] . '_count',
                    'labelNames' => [],
                    'labelValues' => $decodedLabelValues,
                    'value' => $acc,
                ];

                // Add the sum
                $data['samples'][] = [
                    'name' => $metaData['name'] . '_sum',
                    'labelNames' => [],
                    'labelValues' => $decodedLabelValues,
                    'value' => $this->convertIncrementalIntegerToFloat($histogramBuckets[$labelValues]['sum'] ?? 0),
                ];
            }
            $histograms[] = new MetricFamilySamples($data);
        }
        return $histograms;
    }

    /**
     * @return MetricFamilySamples[]
     */
    private function collectSummaries(): array
    {
        $math = new Math();
        $summaries = [];
        foreach ($this->getMetas('summary') as $summary) {
            $metaData = $summary['value'];
            $data = [
                'name' => $metaData['name'],
                'help' => $metaData['help'],
                'type' => $metaData['type'],
                'labelNames' => $metaData['labelNames'],
                'maxAgeSeconds' => $metaData['maxAgeSeconds'],
                'quantiles' => $metaData['quantiles'],
                'samples' => [],
            ];

            foreach ($this->getValues('summary', $metaData) as $value) {
                $encodedLabelValues = (string) $value['value'];
                $decodedLabelValues = $this->decodeLabelValues($encodedLabelValues);
                $samples = [];

                // Deterministically generate keys for all the sample observations, and retrieve them. Pass arrays to apcu_fetch to reduce calls to APCu.
                $end = time();
                $begin = $end - $metaData['maxAgeSeconds'];
                $valueKeyPrefix = $this->valueKey(array_merge($metaData, ['labelValues' => $decodedLabelValues]));

                $sampleCountKeysToRetrieve = [];
                for ($ts = $begin; $ts <= $end; $ts++) {
                    $sampleCountKeysToRetrieve[] = $valueKeyPrefix . ':' . $ts . ':observations';
                }
                $sampleCounts = apcu_fetch($sampleCountKeysToRetrieve);
                unset($sampleCountKeysToRetrieve);
                if (is_array($sampleCounts)) {
                    foreach ($sampleCounts as $k => $sampleCountThisSecond) {
                        $tstamp = explode(':', $k)[5];
                        $sampleKeysToRetrieve = [];
                        for ($i = 0; $i < $sampleCountThisSecond; $i++) {
                            $sampleKeysToRetrieve[] = $valueKeyPrefix . ':' . $tstamp . '.' . $i;
                        }
                        $newSamples = apcu_fetch($sampleKeysToRetrieve);
                        unset($sampleKeysToRetrieve);
                        if (is_array($newSamples)) {
                            $samples = array_merge($samples, $newSamples);
                        }
                    }
                }
                unset($sampleCounts);

                if (count($samples) === 0) {
                    apcu_delete($value['key']);
                    continue;
                }

                // Compute quantiles
                sort($samples);
                foreach ($data['quantiles'] as $quantile) {
                    $data['samples'][] = [
                        'name' => $metaData['name'],
                        'labelNames' => ['quantile'],
                        'labelValues' => array_merge($decodedLabelValues, [$quantile]),
                        'value' => $math->quantile($samples, $quantile),
                    ];
                }

                // Add the count
                $data['samples'][] = [
                    'name' => $metaData['name'] . '_count',
                    'labelNames' => [],
                    'labelValues' => $decodedLabelValues,
                    'value' => count($samples),
                ];

                // Add the sum
                $data['samples'][] = [
                    'name' => $metaData['name'] . '_sum',
                    'labelNames' => [],
                    'labelValues' => $decodedLabelValues,
                    'value' => array_sum($samples),
                ];
            }

            if (count($data['samples']) > 0) {
                $summaries[] = new MetricFamilySamples($data);
            } else {
                apcu_delete($summary['key']);
            }
        }
        return $summaries;
    }

    /**
     * @param int|float $val
     */
    private function incrementKeyWithValue(string $key, $val): void
    {
        $converted = $this->convertToIncrementalInteger($val);

        $this->doIncrementKeyWithValue($key, $converted);
    }

    private function doIncrementKeyWithValue(string $key, int $val): void
    {
        if ($val === 0) {
            return;
        }

        $loops = 0;

        do {
            $loops++;
            $success = apcu_inc($key, $val);
        } while ($success === false && $loops <= self::MAX_LOOPS); /** @phpstan-ignore-line */

        if ($success === false) { /** @phpstan-ignore-line */
            throw new RuntimeException('Caught possible infinite loop in ' . __METHOD__ . '()');
        }
    }

    /**
     * @param int|float $val
     */
    private function decrementKeyWithValue(string $key, $val): void
    {
        if ($val === 0 || $val === 0.0) {
            return;
        }

        $converted = $this->convertToIncrementalInteger($val);
        $loops = 0;

        do {
            $loops++;
            $success = apcu_dec($key, $converted);
        } while ($success === false && $loops <= self::MAX_LOOPS); /** @phpstan-ignore-line */

        if ($success === false) { /** @phpstan-ignore-line */
            throw new RuntimeException('Caught possible infinite loop in ' . __METHOD__ . '()');
        }
    }

    /**
     * @param int|float $val
     */
    private function convertToIncrementalInteger($val): int
    {
        return @intval($val * $this->precisionMultiplier);
    }

    private function convertIncrementalIntegerToFloat(int $val): float
    {
        return floatval((float) $val / (float) $this->precisionMultiplier);
    }

    /**
     * @param mixed[] $samples
     */
    private function sortSamples(array &$samples): void
    {
        usort($samples, function ($a, $b): int {
            return strcmp(implode("", $a['labelValues']), implode("", $b['labelValues']));
        });
    }

    /**
     * @param mixed[] $values
     * @return string
     * @throws RuntimeException
     */
    private function encodeLabelValues(array $values): string
    {
        $json = json_encode(array_map("strval", $values));
        if (false === $json) {
            throw new RuntimeException(json_last_error_msg());
        }
        return base64_encode($json);
    }

    /**
     * @param string $values
     * @return mixed[]
     * @throws RuntimeException
     */
    private function decodeLabelValues(string $values): array
    {
        $json = base64_decode($values, true);
        if (false === $json) {
            throw new RuntimeException('Cannot base64 decode label values');
        }
        $decodedValues = json_decode($json, true);
        if (false === $decodedValues) {
            throw new RuntimeException(json_last_error_msg());
        }
        return $decodedValues;
    }

    /**
     * @param string $keyString
     * @return string
     */
    private function encodeLabelKey(string $keyString): string
    {
        return base64_encode($keyString);
    }

    /**
     * @param string $str
     * @return string
     * @throws RuntimeException
     */
    private function decodeLabelKey(string $str): string
    {
        $decodedKey = base64_decode($str, true);
        if (false === $decodedKey) {
            throw new RuntimeException('Cannot base64 decode label key');
        }
        return $decodedKey;
    }

    /**
     * @param mixed[] $data
     */
    private function storeMetadata(array $data, bool $encoded = true): void
    {
        $metaKey = $this->metaKey($data);
        if (apcu_exists($metaKey)) {
            return;
        }

        $metaData = $this->metaData($data);
        $toStore = $metaData;

        if ($encoded) {
            $toStore = json_encode($metaData);
        }

        $stored = apcu_add($metaKey, $toStore, 0);

        if (!$stored) {
            return;
        }

        apcu_add($this->metaInfoCounterKey, 0, 0);
        $counter = apcu_inc($this->metaInfoCounterKey);

        $newCountedMetricKey = $this->metaCounterKey($counter);
        apcu_store($newCountedMetricKey, $metaKey, 0);
    }

    private function metaCounterKey(int $counter): string
    {
        return str_replace('#COUNTER#', (string) $counter, $this->metaInfoCountedMetricKeyPattern);
    }
}
<?php

namespace Prometheus;

use Prometheus\Exception\MetricNotFoundException;
use Prometheus\Exception\MetricsRegistrationException;

interface RegistryInterface
{
    /**
     * Removes all previously stored metrics from underlying storage adapter
     *
     * @return void
     */
    public function wipeStorage(): void;

    /**
     * @return MetricFamilySamples[]
     */
    public function getMetricFamilySamples(): array;

    /**
     * @param string   $namespace e.g. cms
     * @param string   $name e.g. duration_seconds
     * @param string   $help e.g. The duration something took in seconds.
     * @param string[] $labels e.g. ['controller', 'action']
     *
     * @return Gauge
     * @throws MetricsRegistrationException
     */
    public function registerGauge(string $namespace, string $name, string $help, array $labels = []): Gauge;

    /**
     * @param string $namespace
     * @param string $name
     *
     * @return Gauge
     * @throws MetricNotFoundException
     */
    public function getGauge(string $namespace, string $name): Gauge;

    /**
     * @param string   $namespace e.g. cms
     * @param string   $name e.g. duration_seconds
     * @param string   $help e.g. The duration something took in seconds.
     * @param string[] $labels e.g. ['controller', 'action']
     *
     * @return Gauge
     * @throws MetricsRegistrationException
     */
    public function getOrRegisterGauge(string $namespace, string $name, string $help, array $labels = []): Gauge;

    /**
     * @param string   $namespace e.g. cms
     * @param string   $name e.g. requests
     * @param string   $help e.g. The number of requests made.
     * @param string[] $labels e.g. ['controller', 'action']
     *
     * @return Counter
     * @throws MetricsRegistrationException
     */
    public function registerCounter(string $namespace, string $name, string $help, array $labels = []): Counter;

    /**
     * @param string $namespace
     * @param string $name
     *
     * @return Counter
     * @throws MetricNotFoundException
     */
    public function getCounter(string $namespace, string $name): Counter;

    /**
     * @param string   $namespace e.g. cms
     * @param string   $name e.g. requests
     * @param string   $help e.g. The number of requests made.
     * @param string[] $labels e.g. ['controller', 'action']
     *
     * @return Counter
     * @throws MetricsRegistrationException
     */
    public function getOrRegisterCounter(string $namespace, string $name, string $help, array $labels = []): Counter;

    /**
     * @param string   $namespace e.g. cms
     * @param string   $name e.g. duration_seconds
     * @param string   $help e.g. A histogram of the duration in seconds.
     * @param string[] $labels e.g. ['controller', 'action']
     * @param float[]|null $buckets e.g. [100, 200, 300]
     *
     * @return Histogram
     * @throws MetricsRegistrationException
     */
    public function registerHistogram(
        string $namespace,
        string $name,
        string $help,
        array $labels = [],
        ?array $buckets = null
    ): Histogram;

    /**
     * @param string $namespace
     * @param string $name
     *
     * @return Histogram
     * @throws MetricNotFoundException
     */
    public function getHistogram(string $namespace, string $name): Histogram;

    /**
     * @param string $namespace e.g. cms
     * @param string $name e.g. duration_seconds
     * @param string $help e.g. A histogram of the duration in seconds.
     * @param string[]  $labels e.g. ['controller', 'action']
     * @param float[]|null $buckets e.g. [100, 200, 300]
     *
     * @return Histogram
     * @throws MetricsRegistrationException
     */
    public function getOrRegisterHistogram(
        string $namespace,
        string $name,
        string $help,
        array $labels = [],
        ?array $buckets = null
    ): Histogram;

    /**
     * @param string   $namespace e.g. cms
     * @param string   $name e.g. duration_seconds
     * @param string   $help e.g. A histogram of the duration in seconds.
     * @param string[] $labels e.g. ['controller', 'action']
     * @param int $maxAgeSeconds e.g. 604800
     * @param float[]|null $quantiles e.g. [0.01, 0.5, 0.99]
     *
     * @return Summary
     * @throws MetricsRegistrationException
     */
    public function registerSummary(
        string $namespace,
        string $name,
        string $help,
        array $labels = [],
        int $maxAgeSeconds = 86400,
        ?array $quantiles = null
    ): Summary;

    /**
     * @param string $namespace
     * @param string $name
     *
     * @return Summary
     * @throws MetricNotFoundException
     */
    public function getSummary(string $namespace, string $name): Summary;

    /**
     * @param string $namespace e.g. cms
     * @param string $name e.g. duration_seconds
     * @param string $help e.g. A histogram of the duration in seconds.
     * @param string[]  $labels e.g. ['controller', 'action']
     * @param int $maxAgeSeconds e.g. 604800
     * @param float[]|null $quantiles e.g. [0.01, 0.5, 0.99]
     *
     * @return Summary
     * @throws MetricsRegistrationException
     */
    public function getOrRegisterSummary(
        string $namespace,
        string $name,
        string $help,
        array $labels = [],
        int $maxAgeSeconds = 86400,
        ?array $quantiles = null
    ): Summary;
}
<?php

declare(strict_types=1);

namespace Prometheus;

use Prometheus\Storage\Adapter;

class Gauge extends Collector
{
    const TYPE = 'gauge';

    /**
     * @param float $value e.g. 123.0
     * @param string[] $labels e.g. ['status', 'opcode']
     */
    public function set(float $value, array $labels = []): void
    {
        $this->assertLabelsAreDefinedCorrectly($labels);

        $this->storageAdapter->updateGauge(
            [
                'name' => $this->getName(),
                'help' => $this->getHelp(),
                'type' => $this->getType(),
                'labelNames' => $this->getLabelNames(),
                'labelValues' => $labels,
                'value' => $value,
                'command' => Adapter::COMMAND_SET,
            ]
        );
    }

    /**
     * @return string
     */
    public function getType(): string
    {
        return self::TYPE;
    }

    /**
     * @param string[] $labels
     */
    public function inc(array $labels = []): void
    {
        $this->incBy(1, $labels);
    }

    /**
     * @param int|float $value
     * @param string[] $labels
     */
    public function incBy($value, array $labels = []): void
    {
        $this->assertLabelsAreDefinedCorrectly($labels);

        $this->storageAdapter->updateGauge(
            [
                'name' => $this->getName(),
                'help' => $this->getHelp(),
                'type' => $this->getType(),
                'labelNames' => $this->getLabelNames(),
                'labelValues' => $labels,
                'value' => $value,
                'command' => Adapter::COMMAND_INCREMENT_FLOAT,
            ]
        );
    }

    /**
     * @param string[] $labels
     */
    public function dec(array $labels = []): void
    {
        $this->decBy(1, $labels);
    }

    /**
     * @param int|float $value
     * @param string[] $labels
     */
    public function decBy($value, array $labels = []): void
    {
        $this->incBy(-$value, $labels);
    }
}
<?php

namespace Prometheus;

interface RendererInterface
{
    /**
     * @param MetricFamilySamples[] $metrics
     *
     * @return string
     */
    public function render(array $metrics): string;
}
<?php

declare(strict_types=1);

namespace Prometheus;

use function is_infinite;

class Sample
{
    /**
     * @var string
     */
    private $name;

    /**
     * @var string[]
     */
    private $labelNames;

    /**
     * @var mixed[]
     */
    private $labelValues;

    /**
     * @var int|double
     */
    private $value;

    /**
     * Sample constructor.
     * @param mixed[] $data
     */
    public function __construct(array $data)
    {
        $this->name = $data['name'];
        $this->labelNames = (array) $data['labelNames'];
        $this->labelValues = (array) $data['labelValues'];
        $this->value = $data['value'];
    }

    /**
     * @return string
     */
    public function getName(): string
    {
        return $this->name;
    }

    /**
     * @return string[]
     */
    public function getLabelNames(): array
    {
        return $this->labelNames;
    }

    /**
     * @return mixed[]
     */
    public function getLabelValues(): array
    {
        return $this->labelValues;
    }

    /**
     * @return string
     */
    public function getValue(): string
    {
        if (is_float($this->value) && is_infinite($this->value)) {
            return $this->value > 0 ? '+Inf' : '-Inf';
        }
        return (string) $this->value;
    }

    /**
     * @return bool
     */
    public function hasLabelNames(): bool
    {
        return $this->labelNames !== [];
    }
}
<?php

declare(strict_types=1);

namespace Prometheus;

class Math
{
    /**
     * taken from https://www.php.net/manual/fr/function.stats-stat-percentile.php#79752
     * @param float[] $arr must be sorted
     * @param float $q
     *
     * @return float
     */
    public function quantile(array $arr, float $q): float
    {
        $count = count($arr);
        if ($count === 0) {
            return 0;
        }

        $j = floor($count * $q);
        $r = $count * $q - $j;
        if (0.0 === $r) {
            return $arr[$j - 1];
        }
        return $arr[$j];
    }
}
<?php

declare(strict_types=1);

namespace Prometheus;

use RuntimeException;
use Throwable;

class RenderTextFormat implements RendererInterface
{
    const MIME_TYPE = 'text/plain; version=0.0.4';

    /**
     * @param MetricFamilySamples[] $metrics
     * @param bool $silent If true, render value errors as comments instead of throwing them.
     * @return string
     */
    public function render(array $metrics, bool $silent = false): string
    {
        usort($metrics, function (MetricFamilySamples $a, MetricFamilySamples $b): int {
            return strcmp($a->getName(), $b->getName());
        });

        $lines = [];
        foreach ($metrics as $metric) {
            $lines[] = "# HELP " . $metric->getName() . " {$metric->getHelp()}";
            $lines[] = "# TYPE " . $metric->getName() . " {$metric->getType()}";
            foreach ($metric->getSamples() as $sample) {
                try {
                    $lines[] = $this->renderSample($metric, $sample);
                } catch (Throwable $e) {
                    // Redis and RedisNg allow samples with mismatching labels to be stored, which could cause ValueError
                    // to be thrown when rendering. If this happens, users can decide whether to ignore the error or not.
                    // These errors will normally disappear after the storage is flushed.
                    if (!$silent) {
                        throw $e;
                    }

                    $lines[] = "# Error: {$e->getMessage()}";
                    $lines[] = "#   Labels: " . json_encode(array_merge($metric->getLabelNames(), $sample->getLabelNames()));
                    $lines[] = "#   Values: " . json_encode(array_merge($sample->getLabelValues()));
                }
            }
        }
        return implode("\n", $lines) . "\n";
    }

    /**
     * @param MetricFamilySamples $metric
     * @param Sample $sample
     * @return string
     */
    private function renderSample(MetricFamilySamples $metric, Sample $sample): string
    {
        $labelNames = $metric->getLabelNames();
        if ($metric->hasLabelNames() || $sample->hasLabelNames()) {
            $escapedLabels = $this->escapeAllLabels($metric, $labelNames, $sample);
            return $sample->getName() . '{' . implode(',', $escapedLabels) . '} ' . $sample->getValue();
        }
        return $sample->getName() . ' ' . $sample->getValue();
    }

    /**
     * @param string $v
     * @return string
     */
    private function escapeLabelValue(string $v): string
    {
        return str_replace(["\\", "\n", "\""], ["\\\\", "\\n", "\\\""], $v);
    }

    /**
     * @param MetricFamilySamples $metric
     * @param string[] $labelNames
     * @param Sample $sample
     *
     * @return string[]
     */
    private function escapeAllLabels(MetricFamilySamples $metric, array $labelNames, Sample $sample): array
    {
        $escapedLabels = [];

        $labels = array_combine(array_merge($labelNames, $sample->getLabelNames()), $sample->getLabelValues());

        if ($labels === false) {
            throw new RuntimeException('Unable to combine labels for metric named ' . $metric->getName());
        }

        foreach ($labels as $labelName => $labelValue) {
            $escapedLabels[] = $labelName . '="' . $this->escapeLabelValue((string)$labelValue) . '"';
        }

        return $escapedLabels;
    }
}
<?php

declare(strict_types=1);

namespace Prometheus;

use InvalidArgumentException;
use Prometheus\Storage\Adapter;

abstract class Collector
{
    const RE_METRIC_NAME = '/^[a-zA-Z_:][a-zA-Z0-9_:]*$/';
    const RE_LABEL_NAME = '/^[a-zA-Z_][a-zA-Z0-9_]*$/';

    /**
     * @var Adapter
     */
    protected $storageAdapter;

    /**
     * @var string
     */
    protected $name;

    /**
     * @var string
     */
    protected $help;

    /**
     * @var string[]
     */
    protected $labels;

    /**
     * @param Adapter $storageAdapter
     * @param string $namespace
     * @param string $name
     * @param string $help
     * @param string[] $labels
     */
    public function __construct(Adapter $storageAdapter, string $namespace, string $name, string $help, array $labels = [])
    {
        $this->storageAdapter = $storageAdapter;
        $metricName = ($namespace !== '' ? $namespace . '_' : '') . $name;
        self::assertValidMetricName($metricName);
        $this->name = $metricName;
        $this->help = $help;
        foreach ($labels as $label) {
            self::assertValidLabel($label);
        }
        $this->labels = $labels;
    }

    /**
     * @return string
     */
    abstract public function getType(): string;

    /**
     * @return string
     */
    public function getName(): string
    {
        return $this->name;
    }

    /**
     * @return string[]
     */
    public function getLabelNames(): array
    {
        return $this->labels;
    }

    /**
     * @return string
     */
    public function getHelp(): string
    {
        return $this->help;
    }

    /**
     * @return string
     */
    public function getKey(): string
    {
        return sha1($this->getName() . serialize($this->getLabelNames()));
    }

    /**
     * @param string[] $labels
     */
    protected function assertLabelsAreDefinedCorrectly(array $labels): void
    {
        if (count($labels) !== count($this->labels)) {
            throw new InvalidArgumentException(sprintf('Labels are not defined correctly: %s', print_r($labels, true)));
        }
    }

    /**
     * @param string $metricName
     */
    public static function assertValidMetricName(string $metricName): void
    {
        if (preg_match(self::RE_METRIC_NAME, $metricName) !== 1) {
            throw new InvalidArgumentException("Invalid metric name: '" . $metricName . "'");
        }
    }

    /**
     * @param string $label
     */
    public static function assertValidLabel(string $label): void
    {
        if (preg_match(self::RE_LABEL_NAME, $label) !== 1) {
            throw new InvalidArgumentException("Invalid label name: '" . $label . "'");
        } else if (strpos($label, "__") === 0) {
            throw new InvalidArgumentException("Can't used a reserved label name: '" . $label . "'");
        }
    }
}
<?php

namespace Prometheus\Exception;

use RuntimeException;

/**
 * Exception thrown if a metric can't be found in the CollectorRegistry.
 */
class MetricNotFoundException extends RuntimeException
{
}
<?php

namespace Prometheus\Exception;

use Exception;

/**
 * Exception thrown if a metric can't be found in the CollectorRegistry.
 */
class MetricJsonException extends Exception
{
    private ?string $metricName;

    public function __construct(
        string $message = "",
        int $code = 0,
        ?Exception $previous = null,
        ?string $metricName = null
    ) {
        parent::__construct($message, $code, $previous);
        $this->metricName = $metricName;
    }

    public function getMetricName(): ?string
    {
        return $this->metricName;
    }
}
<?php

namespace Prometheus\Exception;

use Exception;

/**
 * Exception thrown if an error occurs during metrics storage.
 */
class StorageException extends Exception
{
}
<?php

namespace Prometheus\Exception;

use Exception;

/**
 * Exception thrown if an error occurs during metrics registration.
 */
class MetricsRegistrationException extends Exception
{
}
<?php

declare(strict_types=1);

namespace Prometheus;

use Prometheus\Storage\Adapter;

class Counter extends Collector
{
    const TYPE = 'counter';

    /**
     * @return string
     */
    public function getType(): string
    {
        return self::TYPE;
    }

    /**
     * @param string[] $labels e.g. ['status', 'opcode']
     */
    public function inc(array $labels = []): void
    {
        $this->incBy(1, $labels);
    }

    /**
     * @param int|float $count e.g. 2
     * @param string[] $labels e.g. ['status', 'opcode']
     */
    public function incBy($count, array $labels = []): void
    {
        $this->assertLabelsAreDefinedCorrectly($labels);

        $this->storageAdapter->updateCounter(
            [
                'name' => $this->getName(),
                'help' => $this->getHelp(),
                'type' => $this->getType(),
                'labelNames' => $this->getLabelNames(),
                'labelValues' => $labels,
                'value' => $count,
                'command' => is_float($count) ? Adapter::COMMAND_INCREMENT_FLOAT : Adapter::COMMAND_INCREMENT_INTEGER,
            ]
        );
    }
}
<?php

declare(strict_types=1);

namespace Prometheus;

use InvalidArgumentException;
use Prometheus\Storage\Adapter;

class Summary extends Collector
{
    const RESERVED_LABELS = ['quantile'];
    const TYPE = 'summary';

    /**
     * @var float[]|null
     */
    private $quantiles;

    /**
     * @var int
     */
    private $maxAgeSeconds;

    /**
     * @param Adapter       $adapter
     * @param string        $namespace
     * @param string        $name
     * @param string        $help
     * @param string[]      $labels
     * @param int           $maxAgeSeconds
     * @param float[]|null  $quantiles
     */
    public function __construct(
        Adapter $adapter,
        string $namespace,
        string $name,
        string $help,
        array $labels = [],
        int $maxAgeSeconds = 600,
        ?array $quantiles = null
    ) {
        parent::__construct($adapter, $namespace, $name, $help, $labels);

        if (null === $quantiles) {
            $quantiles = self::getDefaultQuantiles();
        }

        if (0 === count($quantiles)) {
            throw new InvalidArgumentException("Summary must have at least one quantile.");
        }

        for ($i = 0; $i < count($quantiles) - 1; $i++) {
            if ($quantiles[$i] >= $quantiles[$i + 1]) {
                throw new InvalidArgumentException(
                    "Summary quantiles must be in increasing order: " .
                    $quantiles[$i] . " >= " . $quantiles[$i + 1]
                );
            }
        }

        foreach ($quantiles as $quantile) {
            if ($quantile <= 0 || $quantile >= 1) {
                throw new InvalidArgumentException("Quantile $quantile invalid: Expected number between 0 and 1.");
            }
        }

        if ($maxAgeSeconds <= 0) {
            throw new InvalidArgumentException("maxAgeSeconds $maxAgeSeconds invalid: Expected number greater than 0.");
        }

        if (count(array_intersect(self::RESERVED_LABELS, $labels)) > 0) {
            throw new InvalidArgumentException("Summary cannot have a label named " . implode(', ', self::RESERVED_LABELS) . ".");
        }
        $this->quantiles = $quantiles;
        $this->maxAgeSeconds = $maxAgeSeconds;
    }

    /**
     * List of default quantiles suitable for typical web application latency metrics
     *
     * @return float[]
     */
    public static function getDefaultQuantiles(): array
    {
        return [
            0.01,
            0.05,
            0.5,
            0.95,
            0.99,
        ];
    }

    /**
     * @param float $value e.g. 123.0
     * @param string[]  $labels e.g. ['status', 'opcode']
     */
    public function observe(float $value, array $labels = []): void
    {
        $this->assertLabelsAreDefinedCorrectly($labels);

        $this->storageAdapter->updateSummary(
            [
                'value'         => $value,
                'name'          => $this->getName(),
                'help'          => $this->getHelp(),
                'type'          => $this->getType(),
                'labelNames'    => $this->getLabelNames(),
                'labelValues'   => $labels,
                'maxAgeSeconds' => $this->maxAgeSeconds,
                'quantiles'     => $this->quantiles,
            ]
        );
    }

    /**
     * @return string
     */
    public function getType(): string
    {
        return self::TYPE;
    }
}
<?php

declare(strict_types=1);

namespace Prometheus;

class MetricFamilySamples
{
    /**
     * @var mixed
     */
    private $name;

    /**
     * @var string
     */
    private $type;

    /**
     * @var string
     */
    private $help;

    /**
     * @var string[]
     */
    private $labelNames;

    /**
     * @var Sample[]
     */
    private $samples = [];

    /**
     * @param mixed[] $data
     */
    public function __construct(array $data)
    {
        $this->name = $data['name'];
        $this->type = $data['type'];
        $this->help = $data['help'];
        $this->labelNames = $data['labelNames'];
        if (isset($data['samples'])) {
            foreach ($data['samples'] as $sampleData) {
                $this->samples[] = new Sample($sampleData);
            }
        }
    }

    /**
     * @return string
     */
    public function getName(): string
    {
        return $this->name;
    }

    /**
     * @return string
     */
    public function getType(): string
    {
        return $this->type;
    }

    /**
     * @return string
     */
    public function getHelp(): string
    {
        return $this->help;
    }

    /**
     * @return Sample[]
     */
    public function getSamples(): array
    {
        return $this->samples;
    }

    /**
     * @return string[]
     */
    public function getLabelNames(): array
    {
        return $this->labelNames;
    }

    /**
     * @return bool
     */
    public function hasLabelNames(): bool
    {
        return $this->labelNames !== [];
    }
}
<?php

declare(strict_types=1);

namespace Prometheus;

use InvalidArgumentException;
use Prometheus\Storage\Adapter;

class Histogram extends Collector
{
    const TYPE = 'histogram';

    /**
     * @var float[]|null
     */
    private $buckets;

    /**
     * @param Adapter    $adapter
     * @param string     $namespace
     * @param string     $name
     * @param string     $help
     * @param string[]   $labels
     * @param float[]|null $buckets
     */
    public function __construct(
        Adapter $adapter,
        string $namespace,
        string $name,
        string $help,
        array $labels = [],
        ?array $buckets = null
    ) {
        parent::__construct($adapter, $namespace, $name, $help, $labels);

        if (null === $buckets) {
            $buckets = self::getDefaultBuckets();
        }

        if (0 === count($buckets)) {
            throw new InvalidArgumentException("Histogram must have at least one bucket.");
        }

        for ($i = 0; $i < count($buckets) - 1; $i++) {
            if ($buckets[$i] >= $buckets[$i + 1]) {
                throw new InvalidArgumentException(
                    "Histogram buckets must be in increasing order: " .
                    $buckets[$i] . " >= " . $buckets[$i + 1]
                );
            }
        }
        if (in_array('le', $labels, true)) {
            throw new \InvalidArgumentException("Histogram cannot have a label named 'le'.");
        }
        $this->buckets = $buckets;
    }

    /**
     * List of default buckets suitable for typical web application latency metrics
     *
     * @return float[]
     */
    public static function getDefaultBuckets(): array
    {
        return [
            0.005,
            0.01,
            0.025,
            0.05,
            0.075,
            0.1,
            0.25,
            0.5,
            0.75,
            1.0,
            2.5,
            5.0,
            7.5,
            10.0,
        ];
    }

    /**
     * @param float $start
     * @param float $growthFactor
     * @param int   $numberOfBuckets
     *
     * @return float[]
     */
    public static function exponentialBuckets(float $start, float $growthFactor, int $numberOfBuckets): array
    {
        if ($numberOfBuckets < 1) {
            throw new InvalidArgumentException('Number of buckets must be a positive integer');
        }

        if ($start <= 0) {
            throw new InvalidArgumentException('The starting position of a set of buckets must be a positive integer');
        }

        if ($growthFactor <= 1) {
            throw new InvalidArgumentException('The growth factor must greater than 1');
        }

        $buckets = [];

        for ($i = 0; $i < $numberOfBuckets; $i++) {
            $buckets[$i] = $start;
            $start       *= $growthFactor;
        }

        return $buckets;
    }

    /**
     * @param float $value e.g. 123.0
     * @param string[]  $labels e.g. ['status', 'opcode']
     */
    public function observe(float $value, array $labels = []): void
    {
        $this->assertLabelsAreDefinedCorrectly($labels);

        $this->storageAdapter->updateHistogram(
            [
                'value'       => $value,
                'name'        => $this->getName(),
                'help'        => $this->getHelp(),
                'type'        => $this->getType(),
                'labelNames'  => $this->getLabelNames(),
                'labelValues' => $labels,
                'buckets'     => $this->buckets,
            ]
        );
    }

    /**
     * @return string
     */
    public function getType(): string
    {
        return self::TYPE;
    }
}
{
    "name": "promphp/prometheus_client_php",
    "description": "Prometheus instrumentation library for PHP applications.",
    "type": "library",
    "license": "Apache-2.0",
    "authors": [
        {
            "name": "Lukas KÃ¤mmerling",
            "email": "kontakt@lukas-kaemmerling.de"
        }
    ],
    "replace": {
        "jimdo/prometheus_client_php": "*",
        "endclothing/prometheus_client_php": "*",
        "lkaemmerling/prometheus_client_php": "*"
    },
    "require": {
        "php": "^8.2",
        "ext-json": "*"
    },
    "require-dev": {
        "guzzlehttp/guzzle": "^6.3|^7.0",
        "phpstan/extension-installer": "^1.0",
        "phpstan/phpstan": "^1.5.4",
        "phpstan/phpstan-phpunit": "^1.1.0",
        "phpstan/phpstan-strict-rules": "^1.1.0",
        "phpunit/phpunit": "^9.4",
        "predis/predis": "^2.3",
        "squizlabs/php_codesniffer": "^3.6 || ^4.0",
        "symfony/polyfill-apcu": "^1.6"
    },
    "suggest": {
        "ext-redis": "Required if using Redis.",
        "predis/predis": "Required if using Predis.",
        "ext-apc": "Required if using APCu.",
        "ext-pdo": "Required if using PDO.",
        "promphp/prometheus_push_gateway_php": "An easy client for using Prometheus PushGateway.",
        "symfony/polyfill-apcu": "Required if you use APCu."
    },
    "autoload": {
        "psr-4": {
            "Prometheus\\": "src/Prometheus/"
        }
    },
    "autoload-dev": {
        "psr-0": {
            "Test\\Prometheus\\": "tests/"
        }
    },
    "extra": {
        "branch-alias": {
            "dev-master": "1.0-dev"
        }
    },
    "config": {
        "sort-packages": true,
        "allow-plugins": {
            "phpstan/extension-installer": true
        }
    },
    "prefer-stable": true
}
{
    "symbol-whitelist": [
        "null",
        "true",
        "false",
        "static",
        "self",
        "parent",
        "array",
        "string",
        "int",
        "float",
        "bool",
        "iterable",
        "callable",
        "mixed",
        "void",
        "object",
        "random_bytes",
        "escapeArgument",
        "IS_WINDOWS",
        "posix_kill",
        "pcntl_waitpid",
        "WNOHANG"
    ],
    "php-core-extensions": [
        "Core",
        "date",
        "pcre",
        "Phar",
        "Reflection",
        "SPL",
        "standard",
        "hash"
    ]
}
<?php declare(strict_types=1);

namespace Amp\Process;

class ProcessException extends \Exception
{
}
<?php declare(strict_types=1);

namespace Amp\Process\Internal;

use Amp\Cancellation;
use Amp\Process\ProcessException;

/**
 * @internal
 * @template THandle extends ProcessHandle
 */
interface ProcessRunner
{
    /**
     * Start a process using the supplied parameters.
     *
     * @param string $command The command to execute.
     * @param string|null $workingDirectory The working directory for the child process.
     * @param array $environment Environment variables to pass to the child process.
     * @param array $options `proc_open()` options.
     *
     * @throws ProcessException If starting the process fails.
     */
    public function start(
        string $command,
        Cancellation $cancellation,
        ?string $workingDirectory = null,
        array $environment = [],
        array $options = [],
    ): ProcessContext;

    /**
     * Wait for the child process to end.
     *
     * @param THandle $handle The process descriptor.
     *
     * @return int Exit code.
     */
    public function join(ProcessHandle $handle, ?Cancellation $cancellation = null): int;

    /**
     * Forcibly end the child process.
     *
     * @param THandle $handle The process descriptor.
     *
     * @throws ProcessException If terminating the process fails.
     */
    public function kill(ProcessHandle $handle): void;

    /**
     * Send a signal to the child process.
     *
     * @param THandle $handle The process descriptor.
     * @param int $signal Signal number to send to process.
     *
     * @throws ProcessException If sending the signal fails.
     */
    public function signal(ProcessHandle $handle, int $signal): void;

    /**
     * Release all resources held by the process handle.
     *
     * @param THandle $handle The process descriptor.
     */
    public function destroy(ProcessHandle $handle): void;
}
<?php declare(strict_types=1);

namespace Amp\Process\Internal;

use Amp\ByteStream\ReadableResourceStream;
use Amp\ByteStream\WritableResourceStream;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;

/** @internal */
final class ProcessStreams
{
    use ForbidCloning;
    use ForbidSerialization;

    public function __construct(
        public readonly WritableResourceStream $stdin,
        public readonly ReadableResourceStream $stdout,
        public readonly ReadableResourceStream $stderr,
    ) {
    }
}
<?php declare(strict_types=1);

namespace Amp\Process\Internal\Windows;

/**
 * @internal
 * @codeCoverageIgnore Windows only.
 */
final class HandshakeStatus
{
    public const SUCCESS = 0;
    public const SIGNAL_UNEXPECTED = 0x01;
    public const INVALID_STREAM_ID = 0x02;
    public const INVALID_PROCESS_ID = 0x03;
    public const DUPLICATE_STREAM_ID = 0x04;
    public const INVALID_CLIENT_TOKEN = 0x05;
    public const ACK_WRITE_ERROR = 0x06;
    public const ACK_STATUS_ERROR = 0x07;
    public const NO_LONGER_PENDING = 0x08;

    private function __construct()
    {
        // empty to prevent instances of this class
    }
}
<?php declare(strict_types=1);

namespace Amp\Process\Internal\Windows;

/**
 * @internal
 * @codeCoverageIgnore Windows only.
 */
final class SignalCode
{
    public const HANDSHAKE = 0x01;
    public const HANDSHAKE_ACK = 0x02;
    public const CHILD_PID = 0x03;
    public const EXIT_CODE = 0x04;

    private function __construct()
    {
        // empty to prevent instances of this class
    }
}
<?php declare(strict_types=1);

namespace Amp\Process\Internal\Windows;

use Amp\ByteStream\ReadableResourceStream;
use Amp\ByteStream\WritableResourceStream;
use Amp\Cancellation;
use Amp\CancelledException;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Process\Internal\ProcessStatus;
use Amp\Process\Internal\ProcessStreams;
use Amp\Process\ProcessException;
use Amp\TimeoutCancellation;
use Revolt\EventLoop;
use function Amp\async;

/**
 * @internal
 * @codeCoverageIgnore Windows only.
 */
final class SocketConnector
{
    use ForbidCloning;
    use ForbidSerialization;

    public const SECURITY_TOKEN_SIZE = 16;

    private const SERVER_SOCKET_URI = 'tcp://127.0.0.1:0';

    public string $address;
    public int $port;

    /** @var resource */
    private $server;

    /** @var WindowsHandle[] */
    private array $pendingProcesses = [];

    private string $acceptCallbackId;

    public function __construct()
    {
        $flags = \STREAM_SERVER_LISTEN | \STREAM_SERVER_BIND;
        $socket = \stream_socket_server(self::SERVER_SOCKET_URI, $errNo, $errStr, $flags);
        if (!$socket) {
            throw new \Error("Failed to create TCP server socket for process wrapper: {$errNo}: {$errStr}");
        }

        $this->server = $socket;

        if (!\stream_set_blocking($this->server, false)) {
            throw new \Error("Failed to set server socket to non-blocking mode");
        }

        $socketName = \stream_socket_get_name($this->server, false);
        if ($socketName === false) {
            throw new \Error("Failed to get server socket name");
        }

        [$this->address, $port] = \explode(':', $socketName);
        $this->port = (int) $port;

        $this->acceptCallbackId = EventLoop::unreference(EventLoop::onReadable(
            $this->server,
            fn () => $this->acceptClient()
        ));
    }

    public function connectPipes(WindowsHandle $handle, Cancellation $cancellation): ProcessStreams
    {
        EventLoop::reference($this->acceptCallbackId);

        $this->pendingProcesses[$handle->wrapperPid] = $handle;

        try {
            $handle->startBarrier->arrive();
            $handle->startBarrier->await($cancellation);

            $controlPipe = new ReadableResourceStream($handle->sockets[0]);
            $handle->pid = $this->readChildPid($controlPipe, $cancellation);
        } catch (\Throwable $exception) {
            foreach ($handle->sockets as $socket) {
                \fclose($socket);
            }

            throw $exception;
        } finally {
            unset($this->pendingProcesses[$handle->wrapperPid]);

            if (!$this->pendingProcesses) {
                EventLoop::unreference($this->acceptCallbackId);
            }
        }

        /** @psalm-suppress PossiblyUndefinedArrayOffset */
        $streams = new ProcessStreams(
            new WritableResourceStream($handle->sockets[0]),
            new ReadableResourceStream($handle->sockets[1]),
            new ReadableResourceStream($handle->sockets[2]),
        );

        $handle->status = ProcessStatus::Running;

        $handle->exitCodeStream = $controlPipe;

        $stdin = \WeakReference::create($streams->stdin);
        async(function () use ($handle, $stdin, $cancellation): void {
            try {
                $exitCode = $this->readExitCode($handle->exitCodeStream, $cancellation);

                $handle->joinDeferred->complete($exitCode);
            } catch (\Throwable) {
                $handle->joinDeferred->error(new ProcessException("Failed to read exit code from process wrapper"));
            } finally {
                $handle->status = ProcessStatus::Ended;
                $stdin->get()?->close();

                if (\is_resource($handle->sockets[0])) {
                    @\fclose($handle->sockets[0]);
                }
            }
        });

        return $streams;
    }

    private function acceptClient(): void
    {
        $socket = @\stream_socket_accept($this->server);
        if (!$socket) {
            return;
        }

        if (!\stream_set_blocking($socket, false)) {
            throw new \Error("Failed to set client socket to non-blocking mode");
        }

        async(function () use ($socket): void {
            try {
                $handle = $this->performClientHandshake($socket, new TimeoutCancellation(5));
                $handle->startBarrier->arrive();
            } catch (HandshakeException|CancelledException $e) {
                /** @psalm-suppress InvalidScalarArgument */
                \fwrite($socket, \chr(SignalCode::HANDSHAKE_ACK) . \chr($e->getCode()));
                \fclose($socket);
            }
        });
    }

    /**
     * @param resource $socket
     *
     * @throws HandshakeException
     */
    public function performClientHandshake($socket, Cancellation $cancellation): WindowsHandle
    {
        $stream = new ReadableResourceStream($socket);

        $packet = \unpack(
            'Csignal/Npid/Cstream_id/a*client_token',
            $this->read($stream, $cancellation, length: self::SECURITY_TOKEN_SIZE + 6)
        );

        // validate the client's handshake
        if ($packet['signal'] !== SignalCode::HANDSHAKE) {
            throw new HandshakeException(HandshakeStatus::SIGNAL_UNEXPECTED);
        }

        if ($packet['stream_id'] > 2) {
            throw new HandshakeException(HandshakeStatus::INVALID_STREAM_ID);
        }

        if (!isset($this->pendingProcesses[$packet['pid']])) {
            throw new HandshakeException(HandshakeStatus::INVALID_PROCESS_ID);
        }

        $handle = $this->pendingProcesses[$packet['pid']];

        if (isset($handle->sockets[$packet['stream_id']])) {
            throw new HandshakeException(HandshakeStatus::DUPLICATE_STREAM_ID);
        }

        if (!\hash_equals($packet['client_token'], $handle->securityTokens[$packet['stream_id']])) {
            throw new HandshakeException(HandshakeStatus::INVALID_CLIENT_TOKEN);
        }

        $ackData = \chr(SignalCode::HANDSHAKE_ACK) . \chr(HandshakeStatus::SUCCESS) . $handle->securityTokens[$packet['stream_id'] + 3];

        // Unless we set the security token size so high that it won't fit in the
        // buffer, this probably shouldn't ever happen unless something has gone wrong
        if (\fwrite($socket, $ackData) !== self::SECURITY_TOKEN_SIZE + 2) {
            throw new HandshakeException(HandshakeStatus::ACK_WRITE_ERROR);
        }

        $clientPid = (int) $packet['pid'];
        $clientStreamId = (int) $packet['stream_id'];

        // can happen if the start promise was failed
        if (!isset($this->pendingProcesses[$clientPid]) || $this->pendingProcesses[$clientPid]->status === ProcessStatus::Ended) {
            throw new HandshakeException(HandshakeStatus::NO_LONGER_PENDING);
        }

        $packet = \unpack('Csignal/Cstatus', $this->read($stream, $cancellation, length: 2));

        if ($packet['signal'] !== SignalCode::HANDSHAKE_ACK || $packet['status'] !== HandshakeStatus::SUCCESS) {
            throw new HandshakeException(HandshakeStatus::ACK_STATUS_ERROR);
        }

        $handle->sockets[$clientStreamId] = $socket;

        return $handle;
    }

    /**
     * @return positive-int
     */
    private function readChildPid(ReadableResourceStream $stream, Cancellation $cancellation): int
    {
        $packet = \unpack('Csignal/Npid', $this->read($stream, $cancellation, length: 5));
        if ($packet['signal'] !== SignalCode::CHILD_PID) {
            throw new HandshakeException(HandshakeStatus::SIGNAL_UNEXPECTED);
        }

        $pid = (int) $packet['pid'];
        \assert($pid > 0, 'Expected positive integer for PID');
        return $pid;
    }

    private function readExitCode(ReadableResourceStream $stream, Cancellation $cancellation): int
    {
        $packet = \unpack('Csignal/Ncode', $this->read($stream, $cancellation, length: 5));

        if ($packet['signal'] !== SignalCode::EXIT_CODE) {
            throw new HandshakeException(HandshakeStatus::SIGNAL_UNEXPECTED);
        }

        return (int) $packet['code'];
    }

    private function read(ReadableResourceStream $stream, Cancellation $cancellation, int $length): string
    {
        $buffer = '';

        do {
            $remaining = $length - \strlen($buffer);
            \assert($remaining > 0);

            $chunk = $stream->read($cancellation, limit: $remaining);
            if ($chunk === null) {
                break;
            }

            $buffer .= $chunk;
        } while (\strlen($buffer) < $length);

        if (\strlen($buffer) !== $length) {
            throw new ProcessException('Received ' . \strlen($buffer) . ' of ' . $length . ' expected bytes');
        }

        return $buffer;
    }
}
<?php declare(strict_types=1);

namespace Amp\Process\Internal\Windows;

use Amp\ByteStream\ReadableResourceStream;
use Amp\Process\Internal\ProcessHandle;
use Amp\Sync\Barrier;

/**
 * @internal
 * @codeCoverageIgnore Windows only.
 */
final class WindowsHandle extends ProcessHandle
{
    public readonly Barrier $startBarrier;

    /** @psalm-suppress PropertyNotSetInConstructor */
    public ReadableResourceStream $exitCodeStream;

    /** @psalm-suppress PropertyNotSetInConstructor */
    public int $wrapperPid;

    /** @var resource[] */
    public array $sockets = [];

    /** @var string[] */
    public array $securityTokens = [];

    /**
     * @param resource $proc
     */
    public function __construct($proc)
    {
        parent::__construct($proc);

        $this->startBarrier = new Barrier(4);
    }

    #[\Override]
    public function wait(): void
    {
        // Nothing to do.
    }
}
<?php declare(strict_types=1);

namespace Amp\Process\Internal\Windows;

use Amp\Cancellation;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Process\Internal\ProcessContext;
use Amp\Process\Internal\ProcessHandle;
use Amp\Process\Internal\ProcessRunner;
use Amp\Process\Internal\ProcessStatus;
use Amp\Process\ProcessException;
use const Amp\Process\BIN_DIR;
use const DIRECTORY_SEPARATOR;

/**
 * @internal
 * @implements ProcessRunner<WindowsHandle>
 * @codeCoverageIgnore Windows only.
 * @psalm-suppress UndefinedConstant Psalm 5.4 may have a bug with conditionally defined constants.
 */
final class WindowsRunner implements ProcessRunner
{
    use ForbidCloning;
    use ForbidSerialization;

    private const FD_SPEC = [
        ["pipe", "r"], // stdin
        ["pipe", "w"], // stdout
        ["pipe", "w"], // stderr
        ["pipe", "w"], // exit code pipe
    ];

    private const WRAPPER_EXE_PATH = PHP_INT_SIZE === 8
        ? (BIN_DIR . '\\windows\\ProcessWrapper64.exe') : (BIN_DIR . '\\windows\\ProcessWrapper.exe');

    private static ?string $pharWrapperPath = null;

    private SocketConnector $socketConnector;

    public function __construct()
    {
        $this->socketConnector = new SocketConnector;
    }

    #[\Override]
    public function start(
        string $command,
        Cancellation $cancellation,
        ?string $workingDirectory = null,
        array $environment = [],
        array $options = []
    ): ProcessContext {
        if (\str_contains($command, "\0")) {
            throw new ProcessException("Can't execute commands that contain NUL bytes.");
        }

        $options['bypass_shell'] = true;

        \set_error_handler(static function (int $code, string $message): never {
            throw new ProcessException("Process could not be started: Errno: {$code}; {$message}");
        });

        try {
            /** @psalm-suppress RiskyTruthyFalsyComparison */
            $proc = \proc_open(
                $this->makeCommand($workingDirectory ?? ''),
                self::FD_SPEC,
                $pipes,
                $workingDirectory ?: null,
                $environment ?: null,
                $options
            );
        } finally {
            \restore_error_handler();
        }

        if (!\is_resource($proc)) {
            throw new ProcessException("Process could not be started: unknown error");
        }

        $status = \proc_get_status($proc);
        $handle = new WindowsHandle($proc);

        $securityTokens = \random_bytes(SocketConnector::SECURITY_TOKEN_SIZE * 6);
        $written = \fwrite($pipes[0], $securityTokens . "\0" . $command . "\0");

        \fclose($pipes[0]);
        \fclose($pipes[1]);

        if ($written !== SocketConnector::SECURITY_TOKEN_SIZE * 6 + \strlen($command) + 2) {
            \fclose($pipes[2]);
            \proc_terminate($proc);
            \proc_close($proc);

            throw new ProcessException("Could not send security tokens / command to process wrapper");
        }

        $handle->securityTokens = \str_split($securityTokens, SocketConnector::SECURITY_TOKEN_SIZE);
        $handle->wrapperPid = $status['pid'];

        try {
            $streams = $this->socketConnector->connectPipes($handle, $cancellation);
        } catch (\Exception) {
            $running = \is_resource($proc) && \proc_get_status($proc)['running'];

            $message = null;
            if (!$running) {
                $message = \stream_get_contents($pipes[2]);
            }

            \fclose($pipes[2]);
            \proc_terminate($proc);
            \proc_close($proc);

            $cancellation->throwIfRequested();

            /** @psalm-suppress RiskyTruthyFalsyComparison */
            throw new ProcessException(\trim($message ?: 'Process did not connect to server before timeout elapsed'));
        }

        return new ProcessContext($handle, $streams);
    }

    #[\Override]
    public function join(ProcessHandle $handle, ?Cancellation $cancellation = null): int
    {
        $handle->exitCodeStream->reference();

        try {
            return $handle->joinDeferred->getFuture()->await($cancellation);
        } finally {
            $handle->exitCodeStream->unreference();
        }
    }

    #[\Override]
    public function kill(ProcessHandle $handle): void
    {
        \exec('taskkill /F /T /PID ' . $handle->pid . ' 2>&1');
    }

    #[\Override]
    public function signal(ProcessHandle $handle, int $signal): void
    {
        throw new ProcessException('Signals are not supported on Windows');
    }

    #[\Override]
    public function destroy(ProcessHandle $handle): void
    {
        if ($handle->status !== ProcessStatus::Ended && \getmypid() === $handle->originalParentPid) {
            try {
                $this->kill($handle);
            } catch (ProcessException) {
                // ignore
            }
        }
    }

    private function makeCommand(string $workingDirectory): string
    {
        $wrapperPath = self::WRAPPER_EXE_PATH;

        // We can't execute the exe from within the PHAR, so copy it out...
        if (\strncmp($wrapperPath, "phar://", 7) === 0) {
            if (self::$pharWrapperPath === null) {
                $fileHash = self::hashFile(self::WRAPPER_EXE_PATH);

                $tempDir = \sys_get_temp_dir();
                $tempPath = $tempDir . DIRECTORY_SEPARATOR . "amphp-process-wrapper-" . \bin2hex(\random_bytes(16));
                $wrapperPath = $tempDir . DIRECTORY_SEPARATOR . "amphp-process-wrapper-" . $fileHash;

                if (!self::verifyHash($wrapperPath, $fileHash)) {
                    try {
                        if (!\copy(self::WRAPPER_EXE_PATH, $tempPath)) {
                            throw new ProcessException("Failed to copy wrapper executable to " . $tempPath);
                        }

                        if (!\rename($tempPath, $wrapperPath) && !self::verifyHash($wrapperPath, $fileHash)) {
                            throw new ProcessException(
                                "Failed to rename temporary wrapper executable to " . $wrapperPath,
                            );
                        }
                    } finally {
                        @\unlink($tempPath);
                    }
                }

                self::$pharWrapperPath = $wrapperPath;
            }

            $wrapperPath = self::$pharWrapperPath;
        }

        $result = \sprintf(
            '%s --address=%s --port=%d --token-size=%d',
            \escapeshellarg($wrapperPath),
            $this->socketConnector->address,
            $this->socketConnector->port,
            SocketConnector::SECURITY_TOKEN_SIZE
        );

        if ($workingDirectory !== '') {
            $result .= ' ' . \escapeshellarg('--cwd=' . \rtrim($workingDirectory, '\\'));
        }

        return $result;
    }

    private static function hashFile(string $path): string
    {
        $hash = \hash_file('sha256', $path);
        if ($hash === false) {
            throw new ProcessException("Failed to calculate hash of file at " . $path);
        }

        return $hash;
    }

    private static function verifyHash(string $path, string $hash): bool
    {
        return \file_exists($path) && \hash_equals($hash, self::hashFile($path));
    }
}
<?php declare(strict_types=1);

namespace Amp\Process\Internal\Windows;

/** @internal */
final class HandshakeException extends \Exception
{
    public function __construct(int $code = 0)
    {
        parent::__construct('Handshake failed', $code);
    }
}
<?php declare(strict_types=1);

namespace Amp\Process\Internal;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;

/** @internal  */
final class ProcessContext
{
    use ForbidCloning;
    use ForbidSerialization;

    public function __construct(
        public readonly ProcessHandle $handle,
        public readonly ProcessStreams $streams,
    ) {
    }
}
<?php declare(strict_types=1);

namespace Amp\Process\Internal;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;

/** @internal */
final class ProcHolder
{
    use ForbidCloning;
    use ForbidSerialization;

    public function __construct(
        public readonly ProcessRunner $runner,
        public readonly ProcessHandle $handle,
    ) {
    }

    public function __destruct()
    {
        $this->runner->destroy($this->handle);
    }
}
<?php declare(strict_types=1);

namespace Amp\Process\Internal;

use Amp\DeferredFuture;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;

/** @internal */
abstract class ProcessHandle
{
    use ForbidCloning;
    use ForbidSerialization;

    /**
     * @var resource
     * @psalm-suppress UnusedProperty
     */
    private $proc;

    /** @var DeferredFuture<int> */
    public readonly DeferredFuture $joinDeferred;

    public readonly int $originalParentPid;

    /**
     * @psalm-suppress PropertyNotSetInConstructor
     * @var positive-int
     */
    public int $pid;

    public ProcessStatus $status = ProcessStatus::Starting;

    /**
     * @param resource $proc
     */
    public function __construct($proc)
    {
        $this->proc = $proc;
        $this->joinDeferred = new DeferredFuture;

        /** @psalm-suppress PossiblyFalsePropertyAssignmentValue */
        $this->originalParentPid = \getmypid();
    }

    abstract public function wait(): void;
}
<?php declare(strict_types=1);

namespace Amp\Process\Internal\Posix;

use Amp\ByteStream\WritableResourceStream;
use Amp\Process\Internal\ProcessHandle;
use Amp\Process\Internal\ProcessStatus;
use Amp\Process\ProcessException;
use Revolt\EventLoop;

/** @internal */
final class PosixHandle extends ProcessHandle
{
    private ?string $extraDataPipeCallbackId;

    private readonly int $shellPid;

    /**
     * @param resource $proc Resource from proc_open()
     * @param resource $extraDataPipe Stream resource for exit code
     * @param positive-int $pid
     */
    public function __construct(
        $proc,
        int $pid,
        WritableResourceStream $stdin,
        $extraDataPipe,
    ) {
        parent::__construct($proc);

        $this->status = ProcessStatus::Running;
        $this->pid = $pid;
        $this->shellPid = $shellPid = \proc_get_status($proc)['pid'];

        $status = &$this->status;
        $deferred = $this->joinDeferred;
        $stdin = \WeakReference::create($stdin);
        $this->extraDataPipeCallbackId = EventLoop::unreference(EventLoop::onReadable(
            $extraDataPipe,
            static function (string $callbackId, $stream) use (&$status, $deferred, $stdin, $shellPid): void {
                EventLoop::disable($callbackId);

                $status = ProcessStatus::Ended;

                if (!\is_resource($stream) || \feof($stream)) {
                    $deferred->error(new ProcessException("Process ended unexpectedly"));
                } else {
                    /** @psalm-suppress PossiblyFalseArgument */
                    $deferred->complete((int) \rtrim(\stream_get_contents($stream)));
                }

                // Don't call proc_close here or close output streams, as there might still be stream reads
                $stdin->get()?->close();

                if (\is_resource($stream)) {
                    \fclose($stream);
                }

                self::asyncWaitPid($shellPid);
            },
        ));
    }

    public function reference(): void
    {
        if ($this->extraDataPipeCallbackId !== null) {
            EventLoop::reference($this->extraDataPipeCallbackId);
        }
    }

    public function unreference(): void
    {
        if ($this->extraDataPipeCallbackId !== null) {
            EventLoop::unreference($this->extraDataPipeCallbackId);
        }
    }

    private static function asyncWaitPid(int $pid): void
    {
        if (self::hasChildExited($pid)) {
            return;
        }

        EventLoop::unreference(EventLoop::defer(static fn () => self::asyncWaitPid($pid)));
    }

    private static function hasChildExited(int $pid): bool
    {
        return !\extension_loaded('pcntl') || \pcntl_waitpid($pid, $status, \WNOHANG) !== 0;
    }

    public function __destruct()
    {
        if ($this->extraDataPipeCallbackId !== null) {
            EventLoop::cancel($this->extraDataPipeCallbackId);
            $this->extraDataPipeCallbackId = null;
        }

        if ($this->joinDeferred->isComplete()) {
            return;
        }

        self::asyncWaitPid($this->shellPid);
    }

    #[\Override]
    public function wait(): void
    {
        if (\extension_loaded('pcntl')) {
            \pcntl_waitpid($this->pid, $status);
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\Process\Internal\Posix;

use Amp\ByteStream\ReadableResourceStream;
use Amp\ByteStream\WritableResourceStream;
use Amp\Cancellation;
use Amp\CancelledException;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Process\Internal\ProcessContext;
use Amp\Process\Internal\ProcessHandle;
use Amp\Process\Internal\ProcessRunner;
use Amp\Process\Internal\ProcessStatus;
use Amp\Process\Internal\ProcessStreams;
use Amp\Process\ProcessException;
use Revolt\EventLoop;

/**
 * @internal
 * @implements ProcessRunner<PosixHandle>
 */
final class PosixRunner implements ProcessRunner
{
    use ForbidCloning;
    use ForbidSerialization;

    private const FD_SPEC = [
        ["pipe", "r"], // stdin
        ["pipe", "w"], // stdout
        ["pipe", "w"], // stderr
        ["pipe", "w"], // exit code pipe
    ];
    private const NULL_DESCRIPTOR = ["file", "/dev/null", "r"];

    private static ?string $fdPath = null;

    #[\Override]
    public function start(
        string $command,
        Cancellation $cancellation,
        ?string $workingDirectory = null,
        array $environment = [],
        array $options = [],
    ): ProcessContext {
        if (!\extension_loaded('posix')) {
            throw new ProcessException('Missing ext-posix to run processes with PosixRunner');
        }

        $command = \sprintf(
            '{ (%s) <&3 3<&- 3>/dev/null & } 3<&0; trap "" INT TERM QUIT HUP;' .
            'pid=$!; echo $pid >&3; wait $pid; RC=$?; echo $RC >&3; exit $RC',
            $command
        );

        \set_error_handler(static function (int $code, string $message): never {
            throw new ProcessException("Process could not be started: Errno: {$code}; {$message}");
        });

        try {
            $proc = \proc_open(
                $command,
                $this->generateFds(),
                $pipes,
                $workingDirectory,
                $environment ?: null,
                $options,
            );
        } finally {
            \restore_error_handler();
        }

        if (!\is_resource($proc)) {
            throw new ProcessException("Process could not be started: unknown error");
        }

        $extraDataPipe = $pipes[3];

        /** @psalm-suppress TypeDoesNotContainType */
        if (!\is_resource($extraDataPipe)) {
            throw new ProcessException("Process could not be started: the data pipe closed unexpectedly");
        }

        \stream_set_blocking($extraDataPipe, false);

        $suspension = EventLoop::getSuspension();

        $callbackId = EventLoop::onReadable(
            $extraDataPipe,
            static function (string $callbackId) use ($suspension): void {
                EventLoop::cancel($callbackId);
                $suspension->resume();
            },
        );

        $cancellationId = $cancellation->subscribe(
            static function (CancelledException $e) use ($suspension, $callbackId): void {
                EventLoop::cancel($callbackId);
                $suspension->throw($e);
            },
        );

        try {
            $suspension->suspend();
        } catch (\Throwable $exception) {
            \proc_terminate($proc);
            \proc_close($proc);
            throw $exception;
        } finally {
            $cancellation->unsubscribe($cancellationId);
        }

        /** @psalm-suppress RiskyTruthyFalsyComparison */
        $pid = \rtrim(\fgets($extraDataPipe) ?: '');
        if (!$pid || !\is_numeric($pid)) {
            \proc_terminate($proc);
            \proc_close($proc);
            throw new ProcessException("Process could not be started: could not determine PID");
        }

        $stdin = new WritableResourceStream($pipes[0]);
        $stdout = new ReadableResourceStream($pipes[1]);
        $stderr = new ReadableResourceStream($pipes[2]);

        $pid = (int) $pid;
        \assert($pid > 0, 'Expected positive integer for PID');

        return new ProcessContext(
            new PosixHandle($proc, $pid, $stdin, $extraDataPipe),
            new ProcessStreams($stdin, $stdout, $stderr),
        );
    }

    private function generateFds(): array
    {
        if (self::$fdPath === null) {
            self::$fdPath = \file_exists("/dev/fd") ? "/dev/fd" : "/proc/self/fd";
        }

        $fdList = \scandir(self::$fdPath, \SCANDIR_SORT_NONE);

        if ($fdList === false) {
            throw new ProcessException("Unable to list open file descriptors");
        }

        $fdList = \array_filter($fdList, static function (string $path): bool {
            return $path !== "." && $path !== "..";
        });

        $fds = [];
        foreach ($fdList as $id) {
            $fds[(int) $id] = self::NULL_DESCRIPTOR;
        }

        return self::FD_SPEC + $fds;
    }

    #[\Override]
    public function join(ProcessHandle $handle, ?Cancellation $cancellation = null): int
    {
        $handle->reference();

        try {
            return $handle->joinDeferred->getFuture()->await($cancellation);
        } finally {
            $handle->unreference();
        }
    }

    #[\Override]
    public function kill(ProcessHandle $handle): void
    {
        $handle->reference();

        $this->signal($handle, 9);
    }

    #[\Override]
    public function signal(ProcessHandle $handle, int $signal): void
    {
        /** @noinspection PhpComposerExtensionStubsInspection */
        \posix_kill($handle->pid, $signal);
    }

    #[\Override]
    public function destroy(ProcessHandle $handle): void
    {
        if ($handle->status !== ProcessStatus::Ended && \getmypid() === $handle->originalParentPid) {
            try {
                $this->kill($handle);
            } catch (ProcessException) {
                // ignore
            }
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\Process\Internal;

/** @internal */
enum ProcessStatus
{
    case Starting;
    case Running;
    case Ended;
}
<?php declare(strict_types=1);

namespace Amp\Process;

use Amp\ByteStream\ReadableResourceStream;
use Amp\ByteStream\WritableResourceStream;
use Amp\Cancellation;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\NullCancellation;
use Amp\Process\Internal\Posix\PosixRunner as PosixProcessRunner;
use Amp\Process\Internal\ProcessHandle;
use Amp\Process\Internal\ProcessRunner;
use Amp\Process\Internal\ProcessStatus;
use Amp\Process\Internal\ProcessStreams;
use Amp\Process\Internal\ProcHolder;
use Amp\Process\Internal\Windows\WindowsRunner as WindowsProcessRunner;
use Revolt\EventLoop;

final class Process
{
    use ForbidCloning;
    use ForbidSerialization;

    private static \WeakMap $driverRunner;

    private static \WeakMap $procHolder;

    private static \WeakMap $streamHolder;

    /**
     * Starts a new process.
     *
     * @param string|list<string> $command Command to run.
     * @param string|null $workingDirectory Working directory, or an empty string to use the working directory of the
     *     parent.
     * @param array<string, string> $environment Environment variables, or use an empty array to inherit from the
     *     parent.
     * @param array<string, bool> $options Options for {@see proc_open()}.
     *
     * @throws ProcessException If starting the process fails.
     * @throws \Error If the arguments are invalid.
     */
    public static function start(
        string|array $command,
        ?string $workingDirectory = null,
        array $environment = [],
        array $options = [],
        ?Cancellation $cancellation = null,
    ): self {
        $envVars = [];
        foreach ($environment as $key => $value) {
            if (\is_array($value)) {
                throw new \Error('Argument #3 ($environment) cannot accept nested array values');
            }

            /** @psalm-suppress RedundantCastGivenDocblockType */
            $envVars[(string) $key] = (string) $value;
        }

        $command = \is_array($command)
            ? \implode(" ", \array_map(escapeArgument(...), $command))
            : $command;

        if ($workingDirectory === null) {
            $cwd = \getcwd();
            if ($cwd === false) {
                throw new ProcessException('Failed to determine current working directory');
            }

            $workingDirectory = $cwd;
        }

        $runner = self::getRunner();

        $context = $runner->start(
            $command,
            $cancellation ?? new NullCancellation(),
            $workingDirectory,
            $envVars,
            $options,
        );

        $handle = $context->handle;
        $streams = $context->streams;

        $procHolder = new ProcHolder($runner, $handle);
        self::$procHolder[$procHolder] = $handle->pid;

        self::$streamHolder[$streams->stdin] = $procHolder;
        self::$streamHolder[$streams->stdout] = $procHolder;
        self::$streamHolder[$streams->stderr] = $procHolder;

        return new self($runner, $handle, $streams, $command, $workingDirectory, $envVars, $options);
    }

    private static function getRunner(): ProcessRunner
    {
        /** @psalm-suppress RedundantPropertyInitializationCheck */
        self::$driverRunner ??= new \WeakMap();

        /** @psalm-suppress RedundantPropertyInitializationCheck */
        if (!isset(self::$procHolder)) {
            self::$procHolder = new \WeakMap();

            \register_shutdown_function(static function (): void {
                /** @var ProcHolder $procHolder */
                foreach (self::$procHolder as $procHolder => $pid) {
                    $procHolder->handle->wait();
                }
            });
        }

        /** @psalm-suppress RedundantPropertyInitializationCheck */
        self::$streamHolder ??= new \WeakMap();

        $driver = EventLoop::getDriver();
        return self::$driverRunner[$driver] ??= \PHP_OS_FAMILY === 'Windows'
            ? new WindowsProcessRunner()
            : new PosixProcessRunner();
    }

    /**
     * @param array<string, string> $environment
     */
    private function __construct(
        private readonly ProcessRunner $runner,
        private readonly ProcessHandle $handle,
        private readonly ProcessStreams $streams,
        private readonly string $command,
        private readonly string $workingDirectory,
        private readonly array $environment = [],
        private readonly array $options = []
    ) {
    }

    /**
     * Wait for the process to end.
     *
     * @return int The process exit code.
     */
    public function join(?Cancellation $cancellation = null): int
    {
        return $this->runner->join($this->handle, $cancellation);
    }

    /**
     * Forcibly end the process.
     */
    public function kill(): void
    {
        if (!$this->isRunning()) {
            return;
        }

        $this->runner->kill($this->handle);
    }

    /**
     * Send a signal to the process.
     *
     * @param int $signo Signal number to send to process.
     *
     * @throws ProcessException If signal sending is not supported.
     */
    public function signal(int $signo): void
    {
        if (!$this->isRunning()) {
            return;
        }

        $this->runner->signal($this->handle, $signo);
    }

    /**
     * Returns the PID of the child process.
     */
    public function getPid(): int
    {
        return $this->handle->pid;
    }

    /**
     * Returns the command to execute.
     *
     * @return string The command to execute.
     */
    public function getCommand(): string
    {
        return $this->command;
    }

    /**
     * Gets the current working directory.
     *
     * @return string The working directory.
     */
    public function getWorkingDirectory(): string
    {
        return $this->workingDirectory;
    }

    /**
     * Gets the environment variables array.
     *
     * @return array<string, string> Array of environment variables.
     */
    public function getEnvironment(): array
    {
        return $this->environment;
    }

    /**
     * Gets the options to pass to {@see proc_open()}.
     *
     * @return array<string, bool> Array of options.
     */
    public function getOptions(): array
    {
        return $this->options;
    }

    /**
     * Determines if the process is still running.
     */
    public function isRunning(): bool
    {
        return $this->handle->status !== ProcessStatus::Ended;
    }

    /**
     * Gets the process input stream (STDIN).
     */
    public function getStdin(): WritableResourceStream
    {
        return $this->streams->stdin;
    }

    /**
     * Gets the process output stream (STDOUT).
     */
    public function getStdout(): ReadableResourceStream
    {
        return $this->streams->stdout;
    }

    /**
     * Gets the process error stream (STDERR).
     */
    public function getStderr(): ReadableResourceStream
    {
        return $this->streams->stderr;
    }

    /**
     * @return array{
     *     command: string,
     *     workingDirectory: string,
     *     environment: array<string, string>,
     *     options: array<string, bool>,
     *     pid: int,
     *     status: string,
     * }
     */
    public function __debugInfo(): array
    {
        return [
            'command' => $this->getCommand(),
            'workingDirectory' => $this->getWorkingDirectory(),
            'environment' => $this->getEnvironment(),
            'options' => $this->getOptions(),
            'pid' => $this->handle->pid,
            'status' => $this->isRunning() ? 'running' : 'terminated',
        ];
    }
}
<?php declare(strict_types=1);

namespace Amp\Process;

const BIN_DIR = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'bin';
const IS_WINDOWS = \PHP_OS_FAMILY === 'Windows';

if (!\function_exists(__NAMESPACE__ . '\\escapeArgument')) {
    if (IS_WINDOWS) {
        /**
         * Escapes the command argument for safe inclusion into a Windows command string used with proc_open()
         * and the option bypass_shell=true.
         */
        function escapeArgument(string $arg): string
        {
            return '"' . \preg_replace_callback('(\\\\*("|$))', function (array $m): string {
                return \str_repeat('\\', \strlen($m[0])) . $m[0];
            }, $arg) . '"';
        }
    } else {
        /**
         * Escapes the command argument for safe inclusion into a Posix shell command string used with proc_open().
         */
        function escapeArgument(string $arg): string
        {
            return \escapeshellarg($arg);
        }
    }

    /**
     * Returns the name for the signal number if defined.
     * ext-pcntl is required, otherwise null is always returned.
     *
     * @return non-empty-string|null
     */
    function getSignalName(int $signal): ?string
    {
        /** @var array<int, non-empty-string>|null $signalNameMap */
        static $signalNameMap = null;

        if (!\extension_loaded('pcntl')) {
            return null;
        }

        if ($signalNameMap === null) {
            $skippedNames = [
                'SIGBABY', // PHP inside-joke
                'SIGIOT', // Alias for SIGABRT
            ];

            /** @var array<non-empty-string, int> $constantMap */
            $constantMap = \array_filter(
                (new \ReflectionExtension('pcntl'))->getConstants(),
                fn (string $name) => \preg_match('[^SIG[A-Z0-9]+$]', $name) && !\in_array($name, $skippedNames, true),
                \ARRAY_FILTER_USE_KEY,
            );

            $signalNameMap = \array_flip($constantMap);
            \ksort($signalNameMap);
        }

        return $signalNameMap[$signal] ?? null;
    }
}
{
    "name": "amphp/process",
    "homepage": "https://amphp.org/process",
    "description": "A fiber-aware process manager based on Amp and Revolt.",
    "require": {
        "php": ">=8.1",
        "amphp/amp": "^3",
        "amphp/byte-stream": "^2",
        "amphp/sync": "^2",
        "revolt/event-loop": "^1 || ^0.2"
    },
    "require-dev": {
        "phpunit/phpunit": "^9",
        "amphp/phpunit-util": "^3",
        "amphp/php-cs-fixer-config": "^2",
        "psalm/phar": "6.16.1"
    },
    "license": "MIT",
    "authors": [
        {
            "name": "Bob Weinand",
            "email": "bobwei9@hotmail.com"
        },
        {
            "name": "Aaron Piotrowski",
            "email": "aaron@trowski.com"
        },
        {
            "name": "Niklas Keller",
            "email": "me@kelunik.com"
        }
    ],
    "autoload": {
        "psr-4": {
            "Amp\\Process\\": "src"
        },
        "files": ["src/functions.php"]
    },
    "autoload-dev": {
        "psr-4": {
            "Amp\\Process\\Test\\": "test"
        }
    },
    "scripts": {
        "check": [
            "@cs",
            "@test"
        ],
        "cs": "PHP_CS_FIXER_IGNORE_ENV=1 php-cs-fixer fix -v --diff --dry-run",
        "cs-fix": "PHP_CS_FIXER_IGNORE_ENV=1 php-cs-fixer fix -v --diff",
        "test": "@php -dzend.assertions=1 -dassert.exception=1 ./vendor/bin/phpunit --coverage-text"
    }
}
MZ�       ÿÿ  ¸       @                                   ø   º ´	Í!¸LÍ!This program cannot be run in DOS mode.
$       ‹ìä Ï�ŠóÏ�ŠóÏ�ŠóÆõóÃ�ŠóôÓ‹òÍ�ŠóôÓ‰òÍ�ŠóôÓ�òÝ�ŠóôÓŽòÂ�ŠórAóÊ�ŠóÏ�‹ó”�ŠóeÓ‚òÌ�ŠóeÓuóÎ�ŠóÏ�óÎ�ŠóeÓˆòÎ�ŠóRichÏ�Šó        PE  L h%s\        à   (        1-      @    @                      €         @�                           TY  È    �  ¨Ü                   p @  PU  p                           ÀU  @            @  p                          .text   ô&      (                    `.rdata  ê!   @   "   ,              @  @.data   ˜   p      N              @  À.gfids       €      P              @  @.rsrc   ¨Ü   �   Þ   R              @  @.reloc  @   p     0             @  B                                                                                                                                                                                                                                                                                                U‹ìƒì8¡p@ 3Å‰EüVW�EøÇEø   ‹ñPh~f€ÿvÿ¬@@ …Àt"Pÿ6hC@ è‚  ƒÄ3À_^‹Mü3Íè¤  ‹å]Ãƒ=Äs@  t8¸   ¿   f‰EÌ¡Às@  ·¼s@ PEÔÇEÐ    ÿ°@@ f‰EÎ�EÌë-¸   ¿   f‰Eè¡Às@ ‹ ‰Eì·¼s@ Pÿ°@@ f‰Eê�Eèj j j j WPÿvÿ´@@ ƒøÿt!ÿ6h˜C@ èÛ  ƒÄ3À_^‹Mü3Íèý  ‹å]Ãÿ¤@@ =3'  t"ÿ6hôC@ Pè,  ƒÄ3À_^‹Mü3ÍèÎ  ‹å]Ã‹Mü¸   _3Í^è¹  ‹å]ÃÌÌU‹ìƒì¡p@ 3Å‰Eü‹ES‹]V‹uWj j j ‰Eð‹ú�Eø‰}ìPSVQÇEô    ÿœ@@ ƒøÿu+Wÿuðh0D@ ÿ¤@@ Pè¨  ƒÄ3À_^[‹Mü3ÍèI  ‹å]Ã3À…Û„�   ƒû‚†   ‹Ó�NWíƒâø(å�fnQ�I@fnAÀƒÀfnI¸fnY°fbØfnAàfbÊfnQèfbÙfnIØfþëfnYÐfbØfbÊfbÙfþã;Âr®fþå(ÄfsØfþà(ÄfsØfþàf~á‰Môë3É3Ò3ÿ;Ãs,‹Ë+Èƒùr�Kÿ Æ|ÆƒÀ;Árò‹Mô;ÃsÆ�È‹Eø;Át(QPÿuìÿuðhpD@ è*  ƒÄ3À_^[‹Mü3ÍèK  ‹å]Ã‹Mü¸   _^3Í[è5  ‹å]ÃÌÌÌÌÌÌÌÌÌÌÌÌÌÌU‹ìƒì$¡p@ 3Å‰EüSVW�EàÇEà    ‹ÙPh~f€ÿsÿ¬@@ …Àt#Pÿ3hàD@ è±  ƒÄ3À_^[‹Mü3ÍèÒ  ‹å]Ã‹;‹5Ès@ ¯þÆEô=Ôs@ ÿ0@@ ‹‹ÈÁéˆMõ‹ÈÁéˆMö‹ÈˆEøŠˆEù�EôÁéhTE@ ‰Eè�EäˆM÷‹KjPÇEä   ‰uì‰}ðè¸ýÿÿƒÄ…Àtƒ‹Mü¸   _^ÇC   3Í[èK  ‹å]ÃÌÌÌÌU‹ìƒì$¡p@ 3Å‰Eü¡Ès@ SVƒÀÇEô    WP‹ù‰EèÿÔ@@ ƒÄ‰Eì�Eôj j P�EðPj�EèPÿwÿÀ@@ ƒøÿu5ÿ7hhE@ ÿ¤@@ Pè)  ƒÄÿuìÿØ@@ ƒÄ3À_^[‹Mü3Íè¾  ‹å]Ã‹Eðƒøs/ÿuèPÿ7hÈE@ èo  ÿuìÿØ@@ ƒÄ3À_^[‹Mü3Íè‡  ‹å]Ã‹]ìŠ€ùt¾ÁPÿ7hhF@ è6  ë‹ŠK„ÉtF€ù}¶Á‹…S@ ë¸(G@ P¾ÁPÿ7hHG@ è  ÿuìÿØ@@ ƒÄ3À_^[‹Mü3Íè  ‹å]Ã�MøfÇEø‰Mä‹MèÇEà   ÇEÜ    ;ÁtQPÿ7hÈE@ è¸  ‹]ìƒÄé†   ‹�S‹5Ès@ ¯ÎÜs@ ƒîr@ ‹;uƒÂƒÁƒîsïƒþütAŠ:u'ƒþýt6ŠB:Auƒþþt)ŠB:AuƒþÿtŠB:Atÿ7hÐG@ èA  ‹]ìƒÄëÆEù ÇG   ÇEÜ   SÿØ@@ ‹�Eà‹OƒÄh<H@ jPèˆûÿÿ‹MüƒÄ÷ØÀ3Í#EÜ_^[è$  ‹å]ÃÌÌÌÌÌÌÌÌÌÌÌÌÌ3À‹�ƒz|	@ƒø|ñ3ÀÃ¸   ÃÌÌÌÌÌÌU‹ìQƒ=Äs@  ¸   S‹Ù¹   VEÁ3öW‰EüjÿÔ@@ ƒÄ‹øjjÿuü‰7ÇG    ÿÄ@@ ‰Gƒøÿt‰<³Fƒþ|Ì¸   _^[‹å]Ãÿ7hìH@ èR  ƒÄ3À_^[‹å]ÃÌÌÌÌÌÌU‹ìƒäø�ì,  ¡p@ 3Ä‰„$(  S‹]VW‰\$3ö‹³èRùÿÿ…À„  Fƒþ|ê‹Ëèÿÿÿ…À„¯  D  3Ò3ö3ÿ‰T$3À‰´$   ‰¼$(  ‰D$f�‹ƒ‹C…Àux3É…öt‹C9„Œ$  tA;Îrò;Îuƒþ@s‹C‰„Œ$  ‹´$   F‰´$   3É…ÿt‹C@ 9„Œ,  tA;Ïrò;ÏuRƒÿ@sM‹C‰„Œ,  ‹¼$(  G‰¼$(  ë2ƒøu-3À…Òt‹K9L„t@;Ârõ;Âuƒú@s‹K‰L„‹T$B‰T$‹D$‹\$@‰D$ƒøŒ6ÿÿÿj �„$,  P�„$(  P�D$$Pj ÿ @@ ‹ðƒþÿ„'  …ö„  3ÿ…ö~uƒÿ}p�D$P‹»ÿpÿ¸@@ …Àt‹»èœûÿÿ…À„³   NëC�„$   P‹»ÿpÿ¸@@ …Àt‹»è‘úÿÿ…À„ˆ   Në�„$(  P‹»ÿpÿ¸@@ …Àu1G…ö‹‹\$‹Ëènýÿÿ…À…Vþÿÿ3À_^[‹Œ$(  3Ìèf  ‹å]Â ‹4»�D$P�D$ÇD$   Ph  hÿÿ  ÿvÿ¼@@ ÿ6ƒøÿu'hXH@ èô  ƒÄ‹Œ$4  ƒÈÿ_^[3Ìè  ‹å]Â h°H@ ÿt$èI  ƒÄëÓhXI@ èº  ƒÄëÄh$I@ ÿ¤@@ Pè$  ë®ÌÌSVW‹ù3ö�G(Ç    ÇG0   ÇG,    f�j P�÷�CPSÿ@@ …Àt_…öu‹Gë‹j jPÿ@@ …Àt1F�G(ƒþ|ÈÇG4D   ‹‰Gl‹G‰Gp‹G�O`   ‰Gt¸   _^[ÃVhJ@ è  ƒÄ3À_^[ÃVh°I@ è  ƒÄ3À_^[ÃÌÌÌÌÌÌÌÌQVW‹ù�GP�G4Pÿ5Ìs@ �W(j h   jRRÿ5às@ j ÿ4@@ …Àuh�J@ ÿ$@@ Pè,  ƒÄ3À_^YÃÿw‹5(@@ ÿÖÿ7ÿÖÿwÿÖÿwÿÖ_¸   ^YÃÌU‹ìƒäøƒì¡p@ 3Ä‰D$SV‹uƒËÿWh   ÿÔ@@ ‹= @@ �L$ƒÄ‰D$ÇD$   j Qh   Pÿvÿ×…ÀtB‹D$…Àt[‹‹Nh(K@ ‰D$�D$jPè‹öÿÿƒÄ…Àt<j �D$Ph   ÿt$ ÿvÿ×…Àu¾‹=$@@ ÿ×ƒømtÿ6hÐJ@ ÿ×PèO  ƒÄë3Ûÿvÿ(@@ ÿvÿ˜@@ ‹L$‹Ã_^[3ÌèÙ  ‹å]Â U‹ìƒäøƒì¡p@ 3Ä‰D$SV‹uWh   ÇD$     ÇD$ÿÿÿÿÿÔ@@ ‹À@@ ƒÄ‰D$�D$ÇD$   j j P�D$$Pj�D$$PÿvÿÓƒøÿtS‹=@@ �‹D$…À„“   j �L$$QPÿt$ ÿvÿ×…Àtu‹D$ ‹L$;ÁuVj j �D$$P�D$$Pj�D$$PÿvÿÓƒøÿu´ÿ6h4K@ ÿ$@@ PèQ  ƒÄÿvÿ(@@ ‹L$$‹D$_^[3Ìèæ  ‹å]Â QPÿ6hØK@ èŸ  ƒÄëÌÿ6hxK@ ë´ÇD$    ë¹ÌÌÌÌÌÌÌU‹ì¸  ès  ¡p@ 3Å‰Eü¡Ès@ SVW�4@jöö�<@ÿ@@ ‹Ø�…ôÿþÿj PV�…øÿþÿPSÿ @@ …Àu'h`L@ ÿ$@@ Pè¢  ƒÄ3À_^[‹Mü3ÍèC  ‹å]Ã‹…ôÿþÿ;Æt"VPh¨L@ èö  ƒÄ3À_^[‹Mü3Íè  ‹å]Ã‹5Ô@@ W‰=Ðs@ ÿÖƒÄ£Ôs@ ��øÿþÿWQPèé  ƒÄ‰=Øs@ WÿÖƒÄ£Üs@ ��øÿþÿÏWQPèÅ  ‹5 @@ �…ôÿþÿƒÄj Pj�…øÿþÿPSÿÖ…Àu
h0M@ é=ÿÿÿ‹…ôÿþÿƒøtPh˜M@ èU  é.ÿÿÿŠ…øÿþÿ„Àt¾ÀPh@N@ è8  éÿÿÿj �…ôÿþÿPh   �…øÿþÿPSÿÖ…Àu
hÐN@ éßþÿÿ‹…ôÿþÿ=   u hO@ èö
  ƒÄ3À_^[‹Mü3Íè  ‹å]Ã€¼÷ÿþÿ t h€O@ èÌ
  ƒÄ3À_^[‹Mü3Íèí  ‹å]Ã‹=@@ �…øÿþÿj j jÿPj héý  ÿ×‹ð…öu høO@ èŠ
  ƒÄ3À_^[‹Mü3Íè«  ‹å]Ã3Éº   ÷â�Á÷ÙÈQÿÔ@@ ƒÄ£às@ VPjÿ�…øÿþÿPj héý  ÿ×…Àu hhP@ è3
  ƒÄ3À_^[‹Mü3ÍèT  ‹å]Ã‹Mü¸   _^3Í[è>  ‹å]ÃÌÌÌÌÌÌÌU‹ìƒì¡p@ 3Å‰EüˆUô�Eô‹U‰Eð‹ÂÁèÇEõ    ˆEõ‹ÂÁèˆEö‹ÂV‹uÁèVˆE÷�EìˆUø‹‹IjPÇEì   è&òÿÿ‹MüƒÄ3Í^èË  ‹å]ÃÌÌÌÌU‹ìƒì¡p@ 3Å‰EüSVW3ÿ‹Ù3ö€    �EìPÿ4³ÿ @@ …À„�   ‹Eì=  u
‹³‰D½ðGë…Àu~Fƒþ|Í…ÿtIjÿj�EðPWÿ@@ ƒøÿuhHQ@ ÿ$@@ Pè”	  ƒÄ‹= @@ 3ö�EìPÿ4³ÿ×…Àt"ƒ}ì u1Fƒþ|ç¸   _^[‹Mü3Íè  ‹å]ÃVhðP@ ÿ$@@ PèH	  ƒÄ‹Mü3À_^3Í[èé
  ‹å]ÃÌÌU‹ìƒäø�ìÀ   ¡p@ 3Ä‰„$¼   ƒ=äs@  V‹uWujjÿè@@ ƒÄ£äs@ …ÀtPÿL@@ h t@ h  ÿ¨@@ …ÀtPh”Q@ èL  ƒÄ‹UVè@  ƒÄ…Àtxè´ûÿÿ…Àto�Œ$¬   èdõÿÿ…Àt_‹=,@@ �„$¬   j j Ph€@ j j ÿ×jx‹ð�D$4j PèG  ƒÄ�L$0èGøÿÿ…Àt"jÿVÿ@@ ƒøÿu0hÀQ@ ÿ$@@ PèD  ƒÄèÌ  ƒÈÿ_^‹Œ$¼   3ÌèÜ	  ‹å]Ã�„$¨   PVÿ @@ …Àu0hR@ ÿ$@@ Pè  ƒÄè‰  ƒÈÿ_^‹Œ$¼   3Ìè™	  ‹å]Ãƒ¼$¨    u—�L$0è_øÿÿ…ÀtŠ‹Œ$¬   º   hhR@ ÿt$Tè1ýÿÿƒÄ…À„eÿÿÿ‹„$¬   j ÇD$    j ‹@‰D$‹D$<‰D$�D$Ph @ j j ÿ×‰„$¸   ‹„$°   j ÇD$   j ‹@‰D$(‹D$@‰D$$�D$ Ph@@ j j ÿ×‰„$¼   ‹„$´   j ÇD$(   j ‹@‰D$4‹D$H‰D$0�D$,Ph@@ j j ÿ×jÿÿt$L‰„$È   ÿ@@ ƒøÿu0hpR@ ÿ$@@ PèÙ  ƒÄèa  ƒÈÿ_^‹Œ$¼   3Ìèq  ‹å]Ã�„$¨   Pÿt$Lÿ8@@ …Àu0hÀR@ ÿ$@@ Pè“  ƒÄè  ƒÈÿ_^‹Œ$¼   3Ìè+  ‹å]Ã‹Œ$¬   º   hS@ ÿ´$¬   è×ûÿÿƒÄ…À„þÿÿ�Œ$¸   è0üÿÿ…À„÷ýÿÿ‹„$¬   ÿpÿ˜@@ ‹Œ$Ä   ‹„$¨   _^3ÌèÄ  ‹å]ÃÌÌÌÌÌÌÌÌÌÌÌÌÌSVW‹Ù3ÿ¾p@ @ ‹‹Óf‹
f;uf…Étf‹Jf;HuƒÂƒÀf…ÉuÞ3ÀëÀƒÈ…ÀtƒÆG�þHp@ |Á_^3À[Ã�_^�…p@ [ÃÌÌÌÌÌÌÌÌÌÌÌÌÌU‹ìQVWjÿÔ@@ ƒÄ‹ðVÿujÿ”@@ ƒè t:ƒèthtS@ èÝ  ƒÄVÿØ@@ ƒÄ3À_^Y]Ã‹E‰pÇ@    ¸   _^Y]Ã‹=Ø@@ Vÿ×ƒÄjÿÔ@@ ƒÄ‹ðVÿujÿ”@@ ƒè t#ƒèthtS@ ë‹E‰pÇ@   ¸   _^Y]Ãh°S@ è[  ƒÄVÿ×ƒÄ3À_^Y]ÃÌÌÌÌÌÌÌÌÌÌÌU‹ìƒì¡p@ 3Å‰EüS‹]V‹uWÇEø    ÿ<A@ j
Ç     �EøPVÿÌ@@ ƒÄ‹ø9uøt/ÿ<A@ ƒ8 u$‰;…ÿ~�ÿ   }_^¸   [‹Mü3Íè  ‹å]Ãÿ3hàS@ èÂ  ‹MüƒÄ3Í3À_^[èã  ‹å]ÃÌÌÌÌÌÌÌÌÌÌÌÌU‹ìƒì¡p@ 3Å‰EüS‹]V‹uWÇEø    ÿ<A@ j
Ç     �EøPVÿÌ@@ ƒÄ‹ø9uøtÿ<A@ ƒ8 u‰{…ÿÿshT@ è>  ƒÄ‹Mü¸   _^3Í[è\  ‹å]ÃÌÌÌÌÌU‹ì‹E‹M‰H¸   ]ÃÌÌÌÌÌÌÌÌÌÌÌÌÌU‹ìVW‹ú·ƒø-uf9Gufƒ u	¸   _^]Ãƒ} t‹E‹0‹ÎèIýÿÿWVh¼s@ ‹@ÿÐƒÄ_^]Ãƒø-…£   f9G…™   ‹u�Gh@T@ P‰ÿˆ@@ ‹6‹øƒÄ‹Î…ÿu<èýüÿÿ…ÀuVhDT@ èn  ƒÄ3À_^]Ã‹@…Àu	¸   _^]ÃVh¼s@ ÿÐƒÄ_^]Ã3Àf‰è¼üÿÿ…Àt¿‹H…ÉuVhlT@ è&  ƒÄ3À_^]Ã�GPVh¼s@ ÿÑƒÄ_^]Ã_¸   ^]ÃÌU‹ìƒìS3ÛÇÄs@ ÿÿÿÿVW‹ú‰]ü�s‰¼s@ ‰Às@ ‰Ès@ ‰Ìs@ ;þ~=€    �EüSP‹E‹°è°þÿÿƒÄƒøwÿ$…˜'@ »   ë3ÛF;÷|Óƒ=Äs@ ÿuhU@ h8S@ h¼s@ èfüÿÿƒÄƒ=Ès@  uh¤T@ è`  ƒÄ3À_^[‹å]Ãƒ=¼s@  uhÜT@ èA  ƒÄ3À_^[‹å]Ã_^¸   [‹å]Ã�c'@ -'@ -'@ '@ &'@ ÌÌÌÌ¸�u@ ÃÌÌÌÌÌÌÌÌÌÌU‹ìƒäø‹E�MQj ÿuPèØÿÿÿÿpÿ0ÿXA@ ƒÄ‹å]ÃÌÌÌÌÌÌU‹ìƒäøƒìSV‹Ú‰L$W‰\$è¥ÿÿÿÿu‹øj Sj ‹j ÿwƒÉQÿTA@ ƒÉÿƒÄ…ÀHÁ;Áu	‹Á_^[‹å]Ã�X3É‹Ãº   ÷â�Á÷ÙÈQÿÔ@@ ‹ðƒÄ…öuƒÏÿ‹Ç_^[‹å]Ãÿuj ÿt$SVÿwÿ7ÿ`A@ ƒÄ¿ÿÿÿÿ…ÀHÇ;ÇuVÿØ@@ ƒÄ‹Ç_^[‹å]Ã‹L$_‰1^[‹å]ÃÌÌÌÌÌÌÌÌÌÌU‹ìƒìƒ=äs@  Vu3À^‹å]Ãjjÿè@@ ‹ðƒÄ…ötæ‹U�EP�Müè÷þÿÿ‹UüƒÄH�B·ƒø tƒø	t
ƒøtƒø
u
3Àf‰ƒéëßVÿ5äs@ ‰VÿH@@ ¸   ^‹å]ÃÌÌU‹ìƒì¡p@ 3Å‰EüV‹u�Eøj j Ph   Vj h   ÇEø    ÿ<@@ ‹U…Òu ÿuøVh$U@ è5ÿÿÿƒÄ^‹Mü3ÍèZ  ‹å]Ã�EP�MôèGþÿÿƒÄÿuøVÿuôh4U@ èÿÿÿ‹MüƒÄ3Í^è(  ‹å]ÃÌU‹ìQSVWh€   ÇEü€   ÿÔ@@ ƒÄ‹Ø‹=D@@ ¡äs@ …À„á   PÿP@@ …À„©   ‹pPÿÜ@@ ƒÄ…ö„”   j j ÿuüSjÿVj héý  ÿ×…Àu]ÿ$@@ ƒøzu¬j j j j jÿVj héý  ÿ×‹ø…ÿtŒWSÿà@@ ƒÄ…À„yÿÿÿj j WPjÿVj ‰}ü‹Ø‹=D@@ héý  ÿ×…À„ZÿÿÿShLU@ jÿ\A@ ƒÄPèýÿÿƒÄé;ÿÿÿ¡äs@ …Àt Pÿ@@@ ÿ5äs@ ÿÜ@@ ƒÄÇäs@     SÿØ@@ ƒÄ_^[‹å]Ã;p@ òuòÃòé_  Vjè  èX  PèE  èp  ‹ðè?  j‰èä  ƒÄ^„ÀtlÛâèL  h²3@ èl  è  Pèâ
  YY…ÀuJè  èc  …Àthb1@ è¾
  Yè+  è&  è   èß  Pè÷
  Yèì  „Àtè 
  èÅ  3ÀÃjè4  Ìèù  3ÀÃè…  è¨  PèÆ
  YÃjhY@ è"  jè  Y„Àujèú  2Ûˆ]çƒeü è³  ˆEÜ¡|s@ 3ÉA;ÁtÜ…ÀuI‰|s@ hŒA@ h€A@ è3
  YY…ÀtÇEüþÿÿÿ¸ÿ   éý   h|A@ htA@ è
  YYÇ|s@    ëŠÙˆ]çÿuÜèÙ  Yèq  ‹ð3ÿ9>tVè;  Y„ÀtWjW‹6‹Îè_  ÿÖèP  ‹ð9>tVè  Y„Àtÿ6èÝ	  YèÅ	  ‹øè¸	  ‹ðè�	  Pÿ7ÿ6èUóÿÿƒÄ‹ðè1  „ÀuVè�	  „Ûuè–	  j jèm  YYÇEüþÿÿÿ‹Æë=‹Mì‹‹ ‰EàQPè!	  YYÃ‹eèèì  „Àuÿuàè@	  €}ç uèS	  ÇEüþÿÿÿ‹Eàè  Ãè�  é‡þÿÿU‹ìj ÿt@@ ÿuÿx@@ h	 Àÿp@@ Pÿl@@ ]ÃU‹ì�ì$  jè=	  …ÀtjYÍ)£`q@ ‰\q@ ‰Xq@ ‰Tq@ ‰5Pq@ ‰=Lq@ fŒxq@ fŒlq@ fŒHq@ fŒDq@ fŒ%@q@ fŒ-<q@ œ�pq@ ‹E £dq@ ‹E£hq@ �E£tq@ ‹…ÜüÿÿÇ°p@   ¡hq@ £lp@ Ç`p@ 	 ÀÇdp@    Çpp@    jXkÀ Ç€tp@    jXkÀ ‹p@ ‰LøjXÁà ‹ p@ ‰Løh A@ èáþÿÿ‹å]ÃU‹ì‹EV‹H<È·A�QÐ·Akð(ò;Öt‹M;Jr
‹BB;ÈrƒÂ(;Öuê3À^]Ã‹Âëùèa  …Àu2ÀÃd¡   V¾€s@ ‹Pë;Ðt3À‹Êð±…Àuð2À^Ã°^ÃU‹ìƒ} uÆœs@ è‚  èŠ  „Àu2À]Ãè}  „Àu
j èr  Yëé°]ÃU‹ìƒìV‹u…ötƒþu|èà  …Àt*…öu&h„s@ èX  Y…Àt2ÀëWh�s@ èE  ÷ØYÀþÀëD¡p@ �uôWƒà¿„s@ j Y+ÈƒÈÿÓÈ3p@ ‰Eô‰Eø‰Eü¥¥¥¿�s@ ‰Eô‰Eø�uô‰Eü°¥¥¥_^‹å]Ãjè5  Ìjh8Y@ è=  ƒeü ¸MZ  f9  @ u]¡< @ �¸  @ PE  uL¹  f9ˆ @ u>‹E¹  @ +ÁPQèiþÿÿYY…Àt'ƒx$ |!ÇEüþÿÿÿ°ë‹Eì‹ 3É�8  À”Á‹ÁÃ‹eèÇEüþÿÿÿ2Àè  ÃU‹ìèÏ  …Àt€} u	3À¹€s@ ‡]ÃU‹ì€=œs@  t€} uÿuè  ÿuè  YY°]ÃU‹ì¡p@ ‹È3„s@ ƒáÿuÓÈƒøÿuè  ëh„s@ èô  Y÷ØYÀ÷Ð#E]ÃU‹ìÿuèºÿÿÿ÷ØYÀ÷ØH]ÃU‹ìƒìƒeô ƒeø ¡p@ VW¿Næ@»¾  ÿÿ;Çt…Æt	÷Ð£ p@ ëf�EôPÿ\@@ ‹Eø3Eô‰Eüÿ`@@ 1Eüÿ0@@ 1Eü�EìPÿd@@ ‹Mð�Eü3Mì3Mü3È;Ïu¹Oæ@»ë…Îu‹ÁG  ÁàÈ‰p@ ÷Ñ‰ p@ _^‹å]Ã3ÀÃ3À@Ã¸ @  Ãh s@ ÿL@@ Ã°Ãh   h   j è  ƒÄ…ÀuÃjè=   ÌÃ¸¨s@ Ãèöÿÿ‹Hƒ‰Hèçÿÿÿ‹Hƒ‰HÃ3À9p@ ”ÀÃ¸ìs@ Ã¸ès@ ÃU‹ì�ì$  SVjèÃ  …Àt‹MÍ)3ö�…ÜüÿÿhÌ  VP‰5°s@ è  ƒÄ‰…Œýÿÿ‰�ˆýÿÿ‰•„ýÿÿ‰�€ýÿÿ‰µ|ýÿÿ‰½xýÿÿfŒ•¤ýÿÿfŒ�˜ýÿÿfŒ�týÿÿfŒ…pýÿÿfŒ¥lýÿÿfŒ­hýÿÿœ�…œýÿÿ‹E‰…”ýÿÿ�E‰… ýÿÿÇ…Üüÿÿ  ‹@üjP‰…�ýÿÿ�E¨VPè{  ‹EƒÄÇE¨  @ÇE¬   ‰E´ÿX@@ V�Xÿ÷Û�E¨‰Eø�…ÜüÿÿÛ‰EüþÃÿt@@ �EøPÿx@@ …Àu¶Ã÷ØÀ!°s@ ^[‹å]Ãj ÿT@@ ‹È…Éu2ÀÃ¸MZ  f9uó‹A<Á�8PE  uæ¹  f9HuÛƒxtvÕƒ¸è    •ÀÃhF3@ ÿt@@ ÃU‹ì‹E‹ �8csmàu%ƒxu‹@= “t=!“t="“t= @™t3À]Â è$  ÌSV¾Y@ »Y@ ;ósW‹>…ÿt	‹Ïè8   ÿ×ƒÆ;órê_^[ÃSV¾Y@ »Y@ ;ósW‹>…ÿt	‹Ïè   ÿ×ƒÆ;órê_^[Ãÿ%pA@ ÌÌÌÌÌÌÌÌÌÌÌÌÌhK4@ dÿ5    ‹D$‰l$�l$+àSVW¡p@ 1Eü3ÅP‰eèÿuø‹EüÇEüþÿÿÿ‰Eø�Eðd£    òÃ‹Mðd‰    Y__^[‹å]QòÃU‹ìÿuÿuÿuÿuhó*@ hp@ è±  ƒÄ]ÃU‹ìƒ%´s@  ƒì(S3ÛC	p@ j
è$  …À„m  ƒeð 3Àƒp@ 3ÉVW‰´s@ �}ØS¢‹ó[‰‰w‰O‰W‹EØ‹Mä‰Eø�ñineI‹Eà5ntelÈ‹EÜj5GenuÈXj YS¢‹ó[‰‰w‰O‰WuC‹EØ%ð?ÿ=À t#=` t=p t=P t=` t=p u‹=¸s@ ƒÏ‰=¸s@ ë‹=¸s@ ƒ}ø‹Eä‰Eè‹Eà‰Eü‰Eì|2jX3ÉS¢‹ó[�]Ø‰‰s‰K‰S‹EÜ©   ‰Eð‹Eüt	ƒÏ‰=¸s@ _^©   tmƒp@ Ç´s@    ©   tU©   tN3ÉÐ‰Eô‰Uø‹Eô‹Møƒà3Éƒøu3…Éu/¡p@ ƒÈÇ´s@    öEð £p@ tƒÈ Ç´s@    £p@ 3À[‹å]Ã3À9p@ •ÀÃÿ%Œ@@ ÿ%„@@ ÿ%A@ ÿ%4A@ ÿ%ø@@ ÿ%HA@ ÿ%DA@ ÿ%@A@ ÿ%0A@ ÿ%,A@ ÿ%A@ ÿ%LA@ ÿ%dA@ ÿ%8A@ ÿ%A@ ÿ%A@ ÿ% A@ ÿ%A@ ÿ%ð@@ ÿ%ä@@ ÿ%hA@ ÿ%A@ ÿ%A@ ÿ% A@ ÿ%$A@ ÿ%(A@ ÿ%h@@ ÌÌÌÌÌÌÌÌÌÌQ�L$+ÈÀ÷Ð#È‹Ä% ðÿÿ;Èòr‹ÁY”‹ ‰$òÃ-   … ëçÌÿ%€@@                                                                                                                                                                                                                                                                             Œ[  ˜[  °[  À[  Ì[  æ[  ô[  
\   \  4\  D\  R\  b\  x\  Š\   \  ²\  Ê\  à\  ü\  ]  Ìa  ¸a  ža  ˆa  na  Ra  >a  *a  a  ð`      àa  Š]  v]  €]      ^]    €T]    €o  €s  €
  €	  €F]  —  €  €<]    €    È]      ¾]  ¶]  Ü]  ^  ¨_  &^      ’_      �^      Z_  d_  P_  _  n^  È_  ä_  B_   `  `  `  _  ü^  €^  4_  Ò]  Ü^  ¼^  ¤^  _      R^  8^  
^  ì]  &_  ¸_      Ÿ1@     °+@         +@ ¨+@                     `p@ °p@ S u c c e s s   S i g n a l   n o t   e x p e c t e d   a t   t h i s   t i m e     I n v a l i d   s t r e a m   i d e n t i f i e r   I n v a l i d   p r o c e s s   i d e n t i f i e r     D u p l i c a t e   s t r e a m   i d e n t i f i e r   I n v a l i d   c l i e n t   s e c u r i t y   t o k e n   I n v a l i d   s e r v e r   s e c u r i t y   t o k e n   F a i l e d   t o   s e t   s o c k e t   # % d   t o   n o n - b l o c k i n g   m o d e ,   f a i l e d   w i t h   % d       C o n n e c t i n g   s o c k e t   # % d   u n e x p e c t e d l y   s u c c e e d e d     C o n n e c t i n g   s o c k e t   # % d   f a i l e d     F a i l e d   t o   s e n d   % s   t o   s o c k e t   # % d   F a i l e d   t o   s e n d   % s   t o   s o c k e t   # % d :   s e n t   % d   o f   % d   b y t e s         F a i l e d   t o   s e t   s o c k e t   # % d   t o   b l o c k i n g   m o d e ,   f a i l e d   w i t h   % d   h a n d s h a k e   F a i l e d   t o   r e a d   h a n d s h a k e   d a t a   f r o m   s o c k e t   # % d       F a i l e d   t o   r e a d   h a n d s h a k e   d a t a   f r o m   s o c k e t   # % d :   r e c e i v e d   % d   o f   e x p e c t e d   % d   b y t e s   H a n d s h a k e   f a i l e d   f o r   s o c k e t   # % d :   U n e x p e c t e d   s i g n a l   c o d e   % d   f r o m   s e r v e r ,   e x p e c t i n g   H A N D S H A K E _ A C K   U n k n o w n   e r r o r       H a n d s h a k e   f a i l e d   f o r   s o c k e t   # % d :   S e r v e r   r e j e c t e d   c o n n e c t i o n :   % d :   % s   H a n d s h a k e   f a i l e d   f o r   s o c k e t   # % d :   I n v a l i d   s e r v e r   t o k e n   h a n d s h a k e   a c k   F a i l e d   t o   c o n n e c t   s o c k e t   # % d :   U n k n o w n   e r r o r   F a i l e d   t o   c o n n e c t   s o c k e t   # % d     F a i l e d   t o   c r e a t e   s o c k e t   # % d   s e l e c t ( )   o p e r a t i o n   f a i l e d   s e l e c t ( )   u n e x p e c t e d l y   r e t u r n e d   z e r o   s o c k e t s   F a i l e d   t o   c r e a t e   p i p e   f o r   p r o c e s s   I / O   s t r e a m   # % d         F a i l e d   t o   s e t   h a n d l e   i n f o r m a t i o n   f o r   p r o c e s s   I / O   s t r e a m   # % d   F a i l e d   t o   c r e a t e   c h i l d   p r o c e s s     F a i l e d   t o   r e a d   f r o m   c h i l d   p r o c e s s   p i p e   # % d     d a t a     F a i l e d   t o   r e a d   f r o m   s o c k e t   # % d         F a i l e d   t o   s e n d   d a t a   t o   c h i l d   p r o c e s s   p i p e   # % d       F a i l e d   t o   s e n d   d a t a   t o   c h i l d   p r o c e s s   p i p e   # % d :   s e n t   % d   o f   % d   b y t e s     F a i l e d   t o   r e a d   t o k e n s   f r o m   s t d i n         F a i l e d   t o   r e a d   t o k e n s   f r o m   s t d i n :   r e c e i v e d   % d   o f   e x p e c t e d   % d   b y t e s     F a i l e d   t o   r e a d   t o k e n - c o m m a n d   s e p a r a t o r   f r o m   s t d i n       F a i l e d   t o   r e a d   t o k e n - c o m m a n d   s e p a r a t o r   f r o m   s t d i n :   r e c e i v e d   % d   o f   e x p e c t e d   1   b y t e       F a i l e d   t o   r e a d   t o k e n - c o m m a n d   s e p a r a t o r   f r o m   s t d i n :   e x p e c t e d   0 ,   g o t   % d       F a i l e d   t o   r e a d   c o m m a n d   f r o m   s t d i n       F a i l e d   t o   r e a d   c o m m a n d   f r o m   s t d i n :   c o m m a n d   t o o   l o n g   F a i l e d   t o   r e a d   c o m m a n d   f r o m   s t d i n :   m i s s i n g   n u l l   t e r m i n a t o r     F a i l e d   t o   r e a d   c o m m a n d   f r o m   s t d i n :   i n v a l i d   U T F - 8   s t r i n g   F a i l e d   t o   r e a d   c o m m a n d   f r o m   s t d i n :   f a i l e d   t o   d e c o d e   U T F - 8   s t r i n g         R e t r i e v i n g   c o p y   t h r e a d   # % d   e x i t   c o d e   f a i l e d   W a i t   o p e r a t i o n   o n   c o p y   t h r e a d s   f a i l e d   W S A S t a r t u p   f a i l e d :   % d   W a i t   o p e r a t i o n   o n   c o n n e c t   t h r e a d   f a i l e d   R e t r i e v i n g   c o n n e c t   t h r e a d   e x i t   c o d e   f a i l e d     P I D   W a i t   o p e r a t i o n   o n   c h i l d   p r o c e s s   f a i l e d     R e t r i e v i n g   p r o c e s s   e x i t   c o d e   f a i l e d   e x i t   c o d e   ¨A@ ¸A@ üA@ 0B@ hB@  B@ ÜB@ a d d r e s s   p o r t     t o k e n - s i z e     c w d   E r r o r   p a r s i n g   s e r v e r   a d d r e s s     I n v a l i d   s e r v e r   a d d r e s s     I n v a l i d   s e r v e r   p o r t :   % d   I n v a l i d   t o k e n   s i z e :   % d     =   U n k n o w n   o p t i o n :   % s     O p t i o n   % s   r e q u i r e s   a   v a l u e     P r o c e s s   l a b e l   n o t   s u p p l i e d     S e r v e r   p o r t   n o t   s u p p l i e d     1 2 7 . 0 . 0 . 1   % d :   % s     % s :   % d :   % s     %s
     h%s\       f   $V  $B      h%s\          ŒV  ŒB      h%s\       h   V   B      h%s\                   \                                                           p@  V@    pA@                    K4  RSDS0=|«@ÕèE�¾¿’ó œØ   C:\Users\Dave\source\repos\windows-process-wrapper\Release\ProcessWrapper.pdb       "   "         GCTL   ô&  .text$mn     @  p  .idata$5    pA     .00cfg  tA     .CRT$XCA    xA     .CRT$XCAA   |A     .CRT$XCZ    €A     .CRT$XIA    „A     .CRT$XIAA   ˆA     .CRT$XIAC   ŒA     .CRT$XIZ    �A     .CRT$XPA    ”A     .CRT$XPZ    ˜A     .CRT$XTA    œA     .CRT$XTZ     A  €  .rdata   V     .rdata$sxdata   $V  ä  .rdata$zzzdbg   Y     .rtc$IAA    Y     .rtc$IZZ    Y     .rtc$TAA    Y     .rtc$TZZ    Y  <   .xdata$x    TY  ´   .idata$2    Z     .idata$3    Z  p  .idata$4    Œ[  ^  .idata$6     p  `   .data   `p  8  .bss     €      .gfids$y     �  À  .rsrc$01    À‘  èÚ  .rsrc$02                    þÿÿÿ    Ìÿÿÿ    þÿÿÿî,@ -@     þÿÿÿ    Øÿÿÿ    þÿÿÿ0@ 0@ Z          .]   @  °Z          j]  ”@  œZ          ¤]  €@  ðZ          *`  Ô@  èZ          J`  Ì@  [          l`   A  p[          Ž`  TA  [          ®`  ø@  [          Î`  ð@                      Œ[  ˜[  °[  À[  Ì[  æ[  ô[  
\   \  4\  D\  R\  b\  x\  Š\   \  ²\  Ê\  à\  ü\  ]  Ìa  ¸a  ža  ˆa  na  Ra  >a  *a  a  ð`      àa  Š]  v]  €]      ^]    €T]    €o  €s  €
  €	  €F]  —  €  €<]    €    È]      ¾]  ¶]  Ü]  ^  ¨_  &^      ’_      �^      Z_  d_  P_  _  n^  È_  ä_  B_   `  `  `  _  ü^  €^  4_  Ò]  Ü^  ¼^  ¤^  _      R^  8^  
^  ì]  &_  ¸_      PReadFile  SetHandleInformation  ÀGetStdHandle  áWriteFile ©WaitForMultipleObjects  Ô CreatePipe  «WaitForSingleObject ÑMultiByteToWideChar -GetExitCodeThread PGetLastError   CloseHandle è CreateThread  
GetCurrentProcessId Û CreateProcessW  ,GetExitCodeProcess  šFormatMessageW  TInterlockedFlushSList ÍWideCharToMultiByte WInterlockedPushEntrySList KInitializeSListHead VInterlockedPopEntrySList  KERNEL32.dll  H WSARecv   WSAConnect  M WSASend  InetPtonW WS2_32.dll  P wcsstr  H memset  5 _except_handler4_common VCRUNTIME140.dll   free   malloc  n wcstol  # _errno    _aligned_free  __stdio_common_vswprintf_s    __acrt_iob_func  realloc  _aligned_malloc  __stdio_common_vfprintf  __stdio_common_vswprintf  B _seh_filter_exe D _set_app_type . __setusermatherr   _configure_wide_argv  7 _initialize_wide_environment  + _get_initial_wide_environment 8 _initterm 9 _initterm_e X exit  % _exit T _set_fmode   __p___argc   __p___wargv  _cexit   _c_exit ? _register_thread_local_exe_atexit_callback   _configthreadlocale  _set_new_mode  __p__commode  6 _initialize_onexit_table  > _register_onexit_function  _crt_atexit  _controlfp_s  j terminate api-ms-win-crt-heap-l1-1-0.dll  api-ms-win-crt-convert-l1-1-0.dll api-ms-win-crt-runtime-l1-1-0.dll api-ms-win-crt-stdio-l1-1-0.dll api-ms-win-crt-math-l1-1-0.dll  api-ms-win-crt-locale-l1-1-0.dll  ‚UnhandledExceptionFilter  CSetUnhandledExceptionFilter 	GetCurrentProcess aTerminateProcess  mIsProcessorFeaturePresent -QueryPerformanceCounter GetCurrentThreadId  ÖGetSystemTimeAsFileTime gIsDebuggerPresent gGetModuleHandleW  F memcpy                        ±¿DNæ@»ÿÿÿÿ         8S@     °#@ HS@     €$@ TS@      %@ lS@      %@                                                                                                                                                                                                                                                                                                                                                                                                                                                         
         E   s   4      
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     0  €   `  €   x  €   �  €                  ¨  €   À  €   Ø  €   ð  €               e    €                    €                  8 €               	  P                 	  `                 	  p                 	  €                 	  �                 	                    	  °  À‘  h          (–  ¨          Ð¦  ¨%          xÌ  ›          �g >           Ðg X          (k }          (                                                ­�: ©|6	º�EcÈ™OÌÍ�Q÷Ò¥^ùÕ©cäË£c¡Ò¦_GÛ�5×žC         k7Ë *N H%ŒK/G»‹=xÃ�?õ³‚4ÿ«{/ÿ»‰9ÿÏšHÿ×©aÿÓ¬oî³‘[’²…>²�Q ˆ<  n:Î s<ÖV-§‰Y9Gb˜l3ì�\!ó_C—R:dgI�›o(æ¾�Dÿ¬˜xÿ¸�sÿÛµx±ÓžKÕ¤W e/Ên9Î’o?ÇøsI»ïoH¨ÿV4‰â;wCqV'_u_<–\F%l™‰oíÄ´›ÿÕªeÿà»‚ÿÔ®r�] yIÑLŠ`Øï�yßú¢€ÒÞ¢{¡ü†a¥ÿ_:¡Ù�`>ÅÂ£qÙxoa9ÄÅÇÜÊ¯ƒÿ×¨]ÿÝ´tÿÛ¼ŒèÕ¬lF‘jÞ`¢äÀ˜wÕÈaK¾�D¹©}>ÿ’jHÿ¹�VùØ·ƒ‚YPAMÁ¿»õ·›pÿÖ©bÿÝ³rÿäÃ�ÿá½„§‘xRlš~d\ˆmyL]Idšv;%Ã˜U}È ] ÂŸg x9UhK‘­†Iÿ»�KÿÓ¦_ÿÚ°nÿâ¿‰ÿÕºŽØÇ©{ûÜ¼ŠúÒ³�è’{W�´”b¯¤†V—´ŒM«³…<Ñ†`%°ZÆÅ‘?ÿØ¨[ÿ‰mAÿ€g@ÿÞ¾ŒÿçÍ¥ùÎ±ƒûæÅ’ÿäÂŽÿ¾žkþÕ«hÿÍžUÿº‡7ÿ�e$÷[AÛ›n'üÐ›Iÿ×§[ÿ‰mAÿ€g@ÿÞ¿ŒÿçÍ¥ùÆ§vãÝ³rÿÚ°lÿÙ¬cÿÎ›Jÿ½‰9ÿ•j&ÿ[AýhJþ®}/ÿÐ™DÿÓŸMÿÙ¬eÿÚ°mÿâÁŒÿÖ»�Ø½¡t§Ú°nÿ×ªbÿÍ�Oÿ¸…3ÿ¢r(ÿZ ÿS;ÿ�\!ÿ½‡1ÿÎ•<ÿÓ OÿÚ®iÿÜ³qÿåÆ•ÿá¾†¥ª“pIÓ¯víÆ’@ÿ¿ˆ0ÿµ€-ÿ�f%ÿnNÿV=ÿ“i%ÿÂ‹2ÿÎ˜AÿÖ§\ÿØªbÿÝ¶wÿäÅ’éÚ°lC?2Æ©{’Ï£]ÿ»…1ÿšn'ÿ‹c$ÿlMÿfIÿ¬|/ÿÎ˜CÿÓ¡PÿÕ¤WÿÛ±nÿá¾‡úÜ³r|µc ¸œn §ŒbÙ¹‡±Í¥gÿŸw6ÿŠc$ÿhJÿjKÿ±„=ÿÕ¢PÿÖ§[ÿÙ®iÿÝ·zëÛ³r|Ì’9
ÑœI , êÅˆ Ô­qâ¿ˆ“Ü¹�ïÇ¢gÿ³ŽTÿ½˜]ÿ×±uòÕªg½Öªg¶×¬g‹Ñ S9ÁÈŒ1             Ù¬e Ó£Vàº~IãÀˆ äÂŠËåÃŒÀá¼‚hÂ�@®o¡M ¢Q             ð  À  €                                          €  À  à  (       @                                                                 ‚Q �\ …]«:eÃ•K¿ÉœTë¾•TðÅ�_ñÓ¨cóÐ£]ÙËœR­Â�Bi—i ®f·t                                                                 �n# •e¯�8a»ŽHÌÂ—SóÕ¦[ÿÔ£TÿÒ Qÿ×©`ÿÙ­gÿÚ¯lÿÛ²qÿÐ©løÀ�eÍÓ£W–É‘77·o¿~                                            ‚[ [9¥{9zÉšO÷ÐœIÿÊ“=ÿÇ�7ÿÃ‹3ÿÄŒ3ÿÍ–?ÿÐšDÿÒ�HÿÔ Oÿ×ªbÿÝ·zÿà»ƒÿÛ²rêÔ¡QŠÙš6Æ�A ý¿%                             m9Ì     H&†29'  ÿ ©|5t»ŒBñË•@ÿ»…1ÿ«y+ÿ¤t)ÿšm'ÿšm'ÿ­{,ÿ¼‡2ÿÇ�9ÿÏ™CÿÔ¢QÿÖ¥XÿÛ¯iÿÖ²yÿ¸›lþ™u<Ã³�26\aj Ã‚                     l8Ì p<Ð p;ÑDN)“°%—l%0»Š<åÅŽ7ÿ°},ÿ—k&ÿoOüT<ÜG3·G2³Z@ÑxUö¡r(ÿÃŒ5ÿÐ›GÿÔ¢Qÿ²ŠIÿ”…nÿ�xjÿ°˜rÿÙ°oÙÏ—>Bá¹z Æˆ(             p=Í n:Î n:Î6k9ÉÜ@"w¯  $U<�‰b'ÿ€[ÿnOÿQ:ë(
w         "OV=Ç˜l&ÿÅŒ3ÿ©=ÿœ”ˆÿ¶±«ÿº™eÿã½„ÿåÆ–ÿÛ±nÙÌ“86ÑœG ÄŠ-         n:Í n:Ín:ÎÂm9Ìÿ\0®òX-§åX.�õY0‘ÿM+{ÿ6Oÿ(¢  " :+dK#JlQ&hnZ<>'S;¹�d#ÿ�‚nÿÛÝßÿ®˜vÿÕ§^ÿÛ¯lÿá½†ÿåÆ•ÿØªaÂÍ�/Í”8     n;Ì n<Én:Î‰m9Îÿn9ÎÿuCÒÿQÕÿ†YØÿ…YÙÿQ×ÿsBÏÿ`3´ôB#}£ (jQ)_yZ(÷P:ÿ‘}]ðspkI -RF4áÊÊÉÿ×ÔÑÿ·“\ÿÚ«bÿØªbÿÜ²qÿãÁ�ÿß¾Šÿ¹ŒDvÿÓ… W8 m9Í m8Í7n:Îè}OÓÿ™tÝÿ©Šãÿ¬�äÿª‹äÿ¦†àÿ¡�ÛÿœyÜÿ�dÝÿo?ÇÿD#‚ª?3!M¡IíÄŸeÿÀ¨ƒàlcV*   †††ÈëìíÿÇ¿³ÿÀ“LÿÙ«cÿØªbÿØ«dÿà¹}ÿØ¼’ÿºžqÏÎ›J0Ì›M P»sAÏ—ŽeÙÿ§‡âÿ§‡ãÿ¡ßø¡~âÆ˜u§Èª�sÿšqXÿxY[ÿz_˜ÿ…^ÐÿT,¡ø/'­ u/ñâ½ƒÿ×¼’¼©_ (()'¬¬¬çôõöÿÀ´¢ÿÂ�=ÿØ©`ÿÙ¬fÿÚ¯jÿÞ¶xÿæÇ–ÿéÍ¡ÿØ«b—·j }OÒ(’kÛÝ£�áÿ¢áÿ£âÿ…h¹ÝK:s%¤x+3¼�FÜÉ”?ÿ¦v&ÿvT$ÿ`CEÿ[==ÿ…^(ÿÊ™LÿÞ¿�ýÁ¥zr†‰� 655C···õøøùÿ¹®�ÿ¾Œ=ÿÚ¬cÿÜ²pÿÜ²pÿÜ³rÿâÀ‰ÿéÏ¦ÿßºÞÏ˜A*•mÞ¡~â^¢á‰¢á¯£€âÐ‘rÊåJ9gdñÄù «‚@bÉœVïÌšKÿ¿Œ9ÿ³‚0ÿ¼Š7ÿÑ¡TÿÛ·þË¯�²®…E   <0®©§¤ÿêëìÿ¤œ�ÿ”n2ÿØ©`ÿÜ²qÿÜ²pÿÜ²oÿàº~ÿçË�ÿåÇ–ûÕ£Sl	 	    —sÿ­‡þ¦‚ë)bLŠ+   �g£|@KÅ�\ÂÍ¤cïÔªhüÖ¯rùÐ®yÞÈ§s�›wA¤v-"!+fIæ’q>ÿ©Œ]ÿ±‹Oÿ—o1ÿÐ¢YÿÜ²pÿÜ²pÿÜ²oÿÞ¶wÿåÆ”ÿãÉŸÿÉŸ\�zb=»Ÿ‚UÆŠpF¡t\7yZF%R6(/       Ð³o �q,®‰M5»–[O°ŽXE�o;%`H"¤z9{™n+œ 4rQë°}+ÿÌ–@ÿÔŸLÿÄ“EÿÔ£WÿÝ±lÿÜ±mÿÝ´rÿÞ¶wÿæÇ•ÿÒ¿ þ¬‘f­§ŒbòæÅ’ÿà¿ŒÿØ·„ÿÌ«yû¼�lð�xR†E7!)e<�x_7€lU1`VC%,sY1`‡j=y¢I¢¸‹EÙ©z1þƒ]!€2#^€[ ûµ�/ÿÏšGÿÔ¢RÿÖ£SÿÓ¤[ÿœw<ÿ†d.ÿÁœbÿá¼�ÿçÊœÿïÛ½ÿâÁŒò®”jýçÇ”ÿåÅ“ÿæÆ”ÿçÇ”ÿéÉ–ÿ¤�iéPC-ÃÒ®uþ×²vÿÆ¤mù†mFÛË¢aýÎ¡ZÿË—Fÿ´�0ÿˆ`"ßI4`_DÒ q(ÿÆŽ7ÿÕ¤Vÿ×¨]ÿÛ®eÿ£Šaÿ
ÿ0-(ÿw`;ÿÙµ{ÿçÉšÿïÛ¼ÿäÄ‘ý°—oýçÇ•ÿåÅ“ÿåÅ“ÿåÅ“ÿçÇ•ÿÁ¦|ÿ„oMÿà»€ÿß¹|ÿÛ³sÿ¿”QÿÊ”AÿÃ‹4ÿ¯|,ÿŠc#ü\B­L6Ì€[ ÿ®|,ÿÌ•=ÿÖ¦Yÿ×§\ÿÛ®dÿ£Šaÿ
ÿ0-(ÿw`;ÿÙµ{ÿçÉšÿïÛ¼ÿäÄ‘ý®–pòçÇ•ÿåÅ“ÿåÄ‘ÿäÂŽÿãÀŠÿÛ·ÿÊ¢bÿØ¨^ÿÕ¤VÿÔ¡PÿÎ˜Cÿ¼…/ÿ§w*ÿŒd#ÿjKìC0àaEÿŽe$ÿ·‚.ÿÍ—BÿÔ OÿÔ¡PÿÕ¢SÿÓ¥\ÿœw<ÿ†d.ÿÁœbÿá¼�ÿçÊœÿïÛ½ÿâÁŒò¤ŽjÙá¾†ÿÞ¶xÿÜ³rÿÛ°mÿÚ®iÿÙ®hÿÖ¦YÿÓžLÿÓŸMÿÐœKÿÀ�<ÿ¢s)ÿ�e$ÿuSÿK5ùQ9þrQÿšm'ÿ»†1ÿÏšEÿÓŸLÿÓŸMÿÓŸLÿ×¨]ÿÝ²mÿÜ±mÿÝ´rÿÞ¶wÿçÉ™ÿÓÀ¢þ­’g­”^¬Û¶|ÿÙ¬eÿÙ­fÿÙ­gÿÙ­gÿÙ­gÿØªaÿÒŸNÿÇ“Bÿµ‚3ÿ¬z,ÿ r(ÿ~Z ÿaEÿ@.ÿ^Cÿƒ]!ÿ£t)ÿÃ�7ÿÏ—?ÿÐ™BÿÒœHÿÓ OÿÚ®hÿÜ²qÿÜ²pÿÜ²oÿÞ¶xÿæÉšÿäË¤ÿÉŸ]�zgJlÕ¶†ûÚ¯iÿÙ­gÿÙ­gÿÙ­gÿÙ­gÿÔ¥[ÿ¿Œ<ÿ­z,ÿ«y+ÿ¨w*ÿŽe$ÿZ ÿR:ÿJ5ÿjKÿ�f%ÿ®|,ÿÈ�5ÿÏ–>ÿÏ—?ÿÏ—?ÿÔ¡QÿÛ°lÿÜ²pÿÜ²pÿÜ²oÿà¼‚ÿéÎ¤ÿæÈ™ûÕ£SkI9*ÃªƒÞß·yÿÙ­fÿÙ­hÿÖ©aÿË™KÿÁ‹5ÿ»„/ÿ®|,ÿ©x+ÿ’h%ÿ�\!ÿxVÿF1ÿV>ÿvTÿ–k&ÿ»…/ÿÉŽ2ÿË’7ÿÏ–>ÿÑšEÿ×§\ÿØ«dÿÚ®iÿÛ±nÿÝ´sÿäÄ�ÿëÓ¬ÿß¹~ÛÏ—@'   £�i–áÂ‘ÿØ¬fÿÎžQÿÃŽ:ÿ¾‡0ÿ¾‡0ÿ¾‡0ÿ¹„/ÿšm'ÿ‚\!ÿ‚]!ÿiKÿB/ÿ_Dÿ€\ ÿ�p(ÿÀ‰0ÿÈŽ2ÿÈŽ2ÿÍ–?ÿÖ¦ZÿØªbÿØªbÿØªbÿÙ«dÿÞ·zÿçËŸÿèÍ£ÿ×©_� = „kC nX67Ï¶ŽéØ¯nÿ¿‰2ÿ¾†/ÿ¾‡0ÿ¾‡0ÿ¾‡0ÿ¬{,ÿ�e$ÿ…_"ÿ�\!ÿZ@ÿC/ÿdHÿˆa"ÿ¦v*ÿ¾‡0ÿÈ�4ÿÌ•>ÿÒŸNÿ×§]ÿØªbÿØªbÿØªaÿÚ­hÿá½…ÿèÌ¡ÿÞ·yÝÐšE+ÒžL '	    £ŠbŠßÂ“ÿÎ Vÿ¾‡1ÿ¾‡0ÿ¿‡0ÿ²~-ÿ‘h%ÿŒd$ÿŒd$ÿ‚]!ÿY@ÿL6ÿmNÿ“i%ÿµ‚1ÿÆ�9ÿÐœHÿÔ¢QÿÔ¢RÿÔ£Tÿ×¨^ÿØªbÿÙ¬fÿß¹~ÿæÈ™ÿà¼ƒ÷Ñ›HhòÐ› Ì—C     pW. [CÁ¦{ÂäÅ•ÿÏ VÿÀ‰3ÿµ�-ÿ•j&ÿ‹d$ÿŒd$ÿ�d$ÿyVÿbEÿ]Bÿ€[ ÿ­|,ÿË”>ÿÑšCÿÓžKÿÔ¢RÿÔ¢RÿÔ¢RÿÕ£TÿÙ¬eÿßºÿåÇ—ÿâÀŠüÓ¡QŽÆ‡#	Ì’7           Ç¢f Šl>6Ò¶‰ÙæÈ˜ÿÓ§bÿ§{4ÿ‹c$ÿ‹c#ÿŒd$ÿŠb#ÿmMÿcFÿ_CÿkLÿ˜m(ÿÉ”?ÿÑšCÿÒœFÿÔ¡QÿÔ¢RÿÕ£Tÿ×©_ÿÞ¶yÿåÇ—ÿà½„÷Ô¡RŽÊ�-Í“9                 + ÿÿê ª‡NBÝ¾�ÙæÉšÿÔ­pÿµ‹Hÿšq/ÿŽf&ÿ€[ ÿcFÿaDÿaDÿ`Dÿ‰f.ÿÓ¤XÿÕ£SÿÓŸMÿÖ¦ZÿÙ¬fÿÜ´tÿà»�ÿÞ¹ÿØ­hÝÑœJgÇˆ&	Ë�3                         mI ÿÿÿ Î V6à¼ƒÃçÉ›þãÁ‹ÿÕ­mÿÂ˜Uÿ¦~>ÿ�i1ÿŠg0ÿ•p6ÿ°‡FÿÏ¡Xÿß¹}ÿÛ´vÿØ®lÿÛ´vÿÞ¸}ÿÛ´uùÖ©bÙÎšG‹ÆŠ,)­V À}                                 ±p Ý²m ÐœIÛ²q‹äÄ‘ëçËžÿåÆ”ÿàº}ÿÜ³rÿÛ±oÿÝ´sÿá»~ÿãÁŒÿãÃ�ðÍŸWŠÂ‘D‰Æ•H‘Í™F{É‘9OÂ…%¬e ¶r                                                 Ì’6 Å„"Ô¤V9Ü´t—âÀŠÛäÅ”÷æÈ™ÿæÊœÿçÊ�ÿæÉ›úá¿ˆ×Ù®ifÉ�.ÐšF                                                                                 Ê‹( À~Ð›F$Ö§[WÙ®h‚Û±n”Ú°m‰Øªb`Ñ�K"ºn Å…#                                                     ÿÀÿÿ€ ÿ  ?ù  ð  à  À €  €                 À                                      €  €  À  à  ð  ø  ü  þ ÿÿ€?ÿ(   0   `           $                                                                                  ?, Ê�+ ¦t$¯|,e¶…7¨¼�A×Â“Hó§�Dß¯‰LàÍžT÷ÊšNæÇ•FÌÃ�=£¿ˆ2nº‚(6¼ƒ"   õàX                                                                                                                 ”d uE ¡q%'šo,t™u<³ËŸYûÔ©dÿ×¬gÿÙ­fÿÏ¥bÿÓ­pÿÞ·zÿÝ·zÿÜ¶zÿÚ³uÿÕ¬küÏ¢Zê¨~<¢šo*LÆŠ*(¾}Â„                                                                                                      �n# ˜i¥v,d¶‰AÛÇšTþÂ˜Uþ×¨]ÿÕ£TÿÒŸMÿÐœHÿÑ�IÿÖ¥XÿÖ§\ÿ×©_ÿØ«dÿÚ¯kÿÜ³sÿÞ·zÿ¼žnúÏ«t÷Ô¤ZâË”<�ÅŠ+.¿Â…$                                                                                         Šb! }T¨z1†Á”L÷Ô¤XÿÓžKÿÎ˜AÿÍ–?ÿÊ’8ÿÈ�4ÿÇ�2ÿÈ�4ÿÐ˜AÿÑ›DÿÒ›EÿÒœGÿÓŸLÿÕ£TÿØªbÿÝµwÿá½…ÿãÂŽÿÞ¹~ÿÒ¢WãÉ�7|Ã‡(ÇŒ/ ¼                                                             H*t <$`$    šm& ¤s%
ˆb&n©‚DæÕ£TÿÏ˜BÿÅŽ7ÿ¼†1ÿ»…/ÿ¹„.ÿ¶�-ÿ´€-ÿ¶‚.ÿÂŒ5ÿÇ�9ÿË”=ÿÏ˜AÿÑšCÿÑšCÿÔ¡Pÿ×¨]ÿÚ¯kÿÝµwÿá½…ÿãÃ�ÿÜ´týÒœGÀÐ“3;íšÙ—/                                                     o<Î žUÿK(‹^-S8_ «‚> žq)h¸ŠA÷Æ“CÿË”<ÿÀ‰3ÿ²-ÿ¦v*ÿ r)ÿ�p(ÿ–j&ÿ’h%ÿ•j&ÿ¥u)ÿ°},ÿº„/ÿÂ‹4ÿÉ’:ÿÑœGÿÔ¢RÿÔ¢QÿÔ£Sÿ×¨^ÿÝ³pÿÕ¯uÿ¹Ÿvÿ”{Tÿ‰e,ã±0_ë§;Ê‘6                                             m9Í l8Ìo:ÐtF%ƒÓ)R9 ˜k$'°‚:ÚÎšGÿÅŒ2ÿ·‚.ÿ¬z,ÿœo(ÿˆ`"ÿoNÿ\BýM7òG2éK5î_CúvTÿ’h%ÿªx+ÿ¼…/ÿÊ’9ÿÑ�JÿÔ¢RÿÕ¢RÿÓ¡Qÿ¥€Dÿ{mWÿxtpÿ`VGÿ¶Ÿ{ÿÖ«eñÎ•;rÆŠ,É�4                                     n;Í o<Ìn:Ïrh7Ãø2\Ÿ   ÄŠ0 |X³ƒ5ÿ¾ˆ4ÿ±~,ÿ¡s)ÿ‘g%ÿvSÿI3õ&
´f9   *1Q&
“F2ßrQÿ¡r)ÿ¿ˆ0ÿÉ�5ÿÐ›GÿÐŸOÿŠl;ÿ€{sÿ¿ÀÁÿ�‚oÿËªwÿçÆ”ÿèÌŸÿÙ­gôÍ”:rÈŽ2Ë“:                             m9Ì {Eç n:Í^o;Ðñ]1®ÿ,   %5L6Î€[ÿwUÿjLÿcGÿX?ÿ5&ä`   
                     )*žeHø�p(ÿ¿‡0ÿÈ�4ÿ†f4ÿˆ…‚ÿäåæÿ ˜Œÿ¸�SÿÞ´rÿà¼„ÿäÄ’ÿçËŸÿ×ªañÌ’7_ÿÿ� È�7                     q?Î m:Í n:Í<n:Îâo:Ïÿe5½ÿM)‘ïN)’çM)�êN*†üP,~ÿH)lÿ:"Pÿ&,ÿøc#         	  4(I81?1&   
!cF÷¤t)ÿ’h&ÿ{ulÿáâãÿÔÓÓÿ—}SÿÙªaÿØªbÿÜ³sÿá¼…ÿåÆ•ÿæÉ›ÿÔ£TâÊ�3<Ì’7 Ê‘8                 n;Í n;Ìn:Î¿n:Îÿn:Îÿn:Ïÿo:Ðÿn9Ðÿn8Ðÿn8Ñÿm8Ñÿm8Ðÿk7Ìÿd4½ÿO*–û6e¹%G    fM$ƒvX'ÜmP#îx[.æ…u[­AAA)   )“iJÿdWBÿÉÉÊÿ÷÷øÿ¨¢˜ÿº’SÿÚ«cÿØªbÿØ«cÿÝ¶xÿá½‡ÿçÊœÿãÁŒÿÑ›E¾Ï“6Ð—< ®{*         n<É _�  n:Í{n:Îþn:Îÿm8Îÿm9ÎÿsAÐÿQÔÿ‰_×ÿ�fÙÿ�gÙÿŠ`×ÿ�TÕÿwEÒÿp;Ñÿi7ÆÿO)“î"@x   $=“o6ódHÿ-
ÿM9ÿº§‰ÿa`]„´±¬  &Ï”“�ÿèèéÿôôöÿ”…lÿÐ£\ÿØªbÿØªbÿØªbÿÙ¬fÿß¹ÿâ¿ŠÿéÎ£ÿÐ©kù¦v*T°2 _=         n;Í n;Í.n:Îàm8Îÿq>Ïÿ…ZÖÿ¢€ßÿ³—æÿ·œçÿ´˜æÿ°“æÿ®�æÿ­Žæÿ«‹æÿ¦…ãÿ—pÝÿ}OÔÿo;ÐÿV-¡÷!?s   hBºÀ™[ÿÁ—VÿÌ§kÿº¨‹çFDAAlha    III«ÈÈÈÿñññÿççèÿ“xMÿÖ¦YÿØªbÿØªbÿØªbÿØªaÿÛ°nÿá¼„ÿáÂ‘ÿ¦”xù™zI¤Ð›G$È–H     m9Ì m:Êm9Í�n;Îÿ„XÕÿ§‡áÿ´™æÿ­Žäÿ¥ƒâÿ¢áÿ¡~áÿ�{Úÿ›yÊÿ–t¹ÿ�m°ÿŠl¸ÿ™yÒÿ£�ãÿ‰^Øÿp<ÏÿG%‡ê>:*Š©€@ÿÞµuÿäÌ¦ÿÂ§}¼g. %
    zzy×ÜÜÜÿøøøÿÚÚÚÿ˜u=ÿÕ MÿØªbÿØªbÿØªbÿØªcÿÙ­gÿà¼‚ÿäÄ“ÿçÑ­ÿáÀ‹ÿÓŸN�Ë”;ÑžL m9Ì l7Ë(r@Ïá’kÚÿ¬�äÿ§†ãÿ¢~áÿ¡~áÿ¡~ßø¢áËž{ÜŠŠikÉ²ˆWÿ²‚>ÿ˜m/ÿvU+ÿW@?ÿnU“ÿ–sØÿwIÍÿO'œÿ/ÉaEß¼ˆ6ÿÜ²pÿêÒ¬ÿÈ¨uŸ    4˜——ïãããÿüüüÿÒÑÐÿžw9ÿÒšAÿ×¨_ÿÙ«dÿÚ­hÿÛ°mÿÜ±oÿÞ·zÿäÄ‘ÿèÌ ÿëÔ®ÿØªbáÐšE(ÒžM ‡[Ø xHÐs“lÛÿ¥„âÿ¢áÿ¢áÿ¢áÿ¡~ßÿpWœÉ^I�“j ¤v)[À“KõÎ›JÿÃŒ5ÿªy*ÿ„^ÿX?ÿL5JÿH-\ÿ@(:ÿ`C!ÿ¥u*ÿÎ›Jÿá¾‡ÿÕÀ�ô²�TXÀ¢r **) $##G¥¥¥øåååÿþþþÿÏÎÌÿ y:ÿÒš@ÿØ©`ÿÜ±oÿÜ²pÿÜ²pÿÜ²pÿÜ³qÿâ¿‰ÿæÆ•ÿìÖ²ÿà¼‚ÿÒ�JrÕ¡N l;Ç“lÛš¢áî¢áú¢áÿ¢áÿ¢áÿ£€ãÿw\¥ï!A:+0 ¡n�l2ƒÆœZýÔ NÿÃŒ5ÿ±~-ÿ�o'ÿŠb ÿ†_ÿ—l$ÿ´�/ÿË–CÿÜ³rÿåÊ ÿ½Ÿq¯c: E2    �¥¥¥ÿæææÿþþþÿÌÌÌÿsY1ÿË˜HÿØ©^ÿÜ²pÿÜ²pÿÜ²pÿÜ²pÿÜ²oÿß¹~ÿåÅ’ÿêÐ§ÿèÍ¡ÿÕ£U»Ï™DˆbÌ¡~ß¡~à.¡}àN¡~àt¡~à›¢~á¿¢âÜ™xÕôL;k®   oP  Ÿp&¸ŒE¹Ê ]þÒ£XÿÎ™EÿÅ�9ÿÂŒ7ÿÃ�7ÿË•?ÿÔ¢Sÿß·yÿÜ¾�þ½£xÆ±…@4·‘X %	 VA1ö›š™ÿÜÝßÿôõöÿÁÀ¾ÿeM'ÿšs6ÿØ¦XÿÜ²oÿÜ²pÿÜ²pÿÜ²pÿÜ²pÿÝµuÿäÃ�ÿçÊœÿìÖ²ÿÙ­gèÐšF6                žyß štßžzß
 }ß¤�å9kS–O §x.  p$•r;|ÆžaìÕ«jÿÕ«hÿÚ®gÿÙ®jÿß¸{ÿÙ·‚þÑ´…ôÅ¢k°�h.&i žq*    +�uTÿy`7ÿ‰uTÿ–‚cÿ™~Rÿ§|7ÿ‚a+ÿÔ¢SÿÛ°mÿÜ²pÿÜ²pÿÜ²pÿÜ²pÿÜ²qÿâ¿‰ÿæÆ–ÿí×´ÿáº~üÐœHn#Q8*d? "                                  ¤q" šg­5@ª„H„¸•^»Î¨mÜ»›iÏÉ¦nËº–^™—t=D¬u"  œt4M—m)€   - —…_"ÿ¨w)ÿ¼†0ÿÌ˜EÿÔŸLÿÆ”Dÿ•o2ÿÒ PÿÛ¯kÿÜ²qÿÜ²pÿÜ²pÿÜ²pÿÜ²oÿà»�ÿåÆ”ÿãÌ§ÿ°˜tï•o3YbN.Ëº˜dÿ«ŒZô”wJâ|b:ÆdN+¤K:}1$W5                    e9 5  œh¯{)�n#¢g­J    SA%(›w=†©|3î‡`"—    0" †_"ÿ©x*ÿÁŠ4ÿÑœIÿÓŸMÿÒžLÿÍšJÿÓ OÿÚ®iÿÜ°mÿÚ°lÿÝ³rÿÝ´sÿÝ´tÿá¼ƒÿèËŸÿàË«ÿ´¥�õ¶�T›{eAçåÄ�ÿèÇ”ÿæÅ‘ÿâÀ‹ÿÙ·�ÿË©tÿ¸˜eü£…Tò�h@¡	7*C3g5(Z%D5		''5D3KhQ,r‘rA©´‹Jã¾‹;þ•j&þwUb   C/Ì�e$ÿ«y+ÿÂ‹5ÿÑ�KÿÓŸNÿÓ NÿÔ OÿÕ£TÿÖ§]ÿ»‹?ÿ¨|6ÿº’SÿÛ¶zÿà»ÿâÀ‰ÿéÏ¥ÿîÙ¸ÿòßÁÿÚ¯iæ†oKõåÅ’ÿåÅ“ÿåÅ“ÿåÅ“ÿæÆ”ÿæÆ•ÿçÇ•ÿêÈ•ÿ¹žtõ!WUE-z¸–`ý¹–]ý­‹V÷¥„Pò{c<¢aL+¢€Iê®ŠQð¼–YùÊ ^ÿÎ�QÿÅŽ9ÿ¢s(ÿ{XÙhJ"&
ojKû¡r(ÿ¹„/ÿË”=ÿÕ¤WÿÖ¦YÿÖ¦Zÿ×¨\ÿÛ±pÿ†lBÿ#
ÿÿT<ÿ¬ŠTÿà»€ÿá¾†ÿéÎ¤ÿíÖ´ÿñàÄÿÛ±oö�vRþæÆ”ÿåÅ“ÿåÅ“ÿåÅ“ÿåÅ“ÿåÅ“ÿåÅ“ÿæÆ”ÿÓ¶‡ÿ0)Þ]N5éà»€ÿâ½‚ÿã½�ÿå¿‚ÿ¢‡\ú~fAöàµqÿÛ¯iÿÓ¢VÿÉ“>ÿÀˆ1ÿ¦v*ÿ‚]!ýqQx!	TU=êŒc#ÿ¬z,ÿÅ�3ÿÑœGÿ×¨]ÿ×¨]ÿ×¨]ÿÚ­eÿË¯„ÿ#ÿ   ÿ-..ÿZULÿoV-ÿÚµ{ÿâ¾…ÿéÍ£ÿìÖ²ÿñáÆÿÜ³qþ�yVþçÇ•ÿåÅ“ÿåÅ“ÿåÅ“ÿåÅ“ÿåÅ“ÿåÅ“ÿåÅ“ÿäÄ“ÿ]O;ÿˆrNÿä¾‚ÿà»�ÿà»�ÿá¼€ÿÌ¨oÿ·�PÿÎ›KÿÆ�9ÿÄŠ1ÿ¾†/ÿ¥v*ÿ‡`"ÿsRÆ1#mK5åvSÿ™m'ÿ´.ÿÊ‘6ÿÓ¡Pÿ×¨^ÿ×¨]ÿ×¨]ÿÚ­eÿË¯„ÿ#ÿ   ÿ-..ÿZULÿoV-ÿÚµ{ÿâ¾…ÿéÍ£ÿìÖ²ÿñáÆÿÜ³qþ�xWõæÇ—ÿåÅ“ÿåÅ“ÿåÅ“ÿåÅ“ÿåÅ“ÿåÅ“ÿåÅ”ÿèÇ•ÿ¤Œgÿºšhÿá¹{ÿÝ´sÿÚ®iÿ×¨^ÿÕ¢SÿÈ‘:ÿÃ‰0ÿÃŠ1ÿ¸‚.ÿ r)ÿŠb#ÿuTóH4¦>,äbEÿ�\ ÿžp(ÿ¹ƒ.ÿÈ�7ÿÓ OÿÕ£UÿÕ¥WÿÖ¦Yÿ×§\ÿÛ±pÿ†lBÿ#
ÿÿT<ÿ¬ŠTÿà»€ÿá¾†ÿéÎ¤ÿíÖ´ÿñàÄÿÛ±oö…qRçåÇ˜ÿåÅ“ÿåÅ”ÿåÅ“ÿåÄ’ÿäÃ�ÿãÁ‹ÿá¾†ÿàºÿÖ¯pÿÓ¤YÿÕ£SÿÓ OÿÓŸMÿÓŸLÿÓŸMÿÌ–@ÿ¿ˆ0ÿ®{,ÿ›n'ÿ�e$ÿxVÿ]Cß<+çP9ÿkLÿ‰a#ÿ¥u*ÿÀˆ0ÿË•?ÿÓŸMÿÓŸMÿÓŸMÿÓŸNÿÓ OÿÕ£Tÿ×¨^ÿ»‹?ÿ¨|6ÿ»’SÿÛ¶{ÿà»ÿâ¿‰ÿéÎ¥ÿîÙ¸ÿòßÁÿÚ¯iæxeIËáÄ–ÿâ¾‡ÿà»�ÿÞ·zÿÜ´tÿÛ±nÿÚ¯jÿÙ­hÿÙ­gÿÙ¬eÿÔ¢RÿÓŸLÿÓŸMÿÓŸMÿÓŸMÿÒžLÿÄ‘?ÿ¤u*ÿ–k&ÿ‘g%ÿ{WÿkLýB/õJ5ÿ_CÿtSÿ‘g%ÿ¬z+ÿ¼†1ÿÏšEÿÓŸMÿÓŸMÿÓŸMÿÓŸMÿÓŸLÿÕ£TÿÛ°mÿÜ°lÿÚ°lÿÝ³rÿÝ´sÿÝ´tÿá½…ÿéÍ¢ÿàÌ­ÿµ¦�õ¶�T›dS8£Ø»�ÿÛ°lÿÙ­gÿÙ­fÿÙ­fÿÙ­gÿÙ­gÿÙ­gÿÙ­gÿÙ­gÿÖ¥YÿÓŸMÿÓŸMÿÓŸMÿË—Fÿ¹‡7ÿ­{-ÿ¤t)ÿ•j&ÿ}Y ÿsRÿT<ÿ9(ÿS;ÿlLÿZ ÿ˜l'ÿ³-ÿÃ�7ÿÏ—@ÿÐšDÿÒ�JÿÓŸMÿÓŸMÿÓŸMÿ×¨]ÿÜ²pÿÜ²pÿÜ²pÿÜ²pÿÜ²pÿÜ²oÿá½…ÿçÊ›ÿäÎ¬ÿ°™vï”n3YI:#nÊ±‰üÞ¶vÿÙ­fÿÙ­gÿÙ­gÿÙ­gÿÙ­gÿÙ­gÿÙ­gÿÙ­gÿØªaÿÔ OÿÎ›Iÿ¾‹;ÿ¯}.ÿªy+ÿ«y+ÿ©x+ÿ�d$ÿuTÿlMÿ@-ÿC0ÿX?ÿvTÿŠb#ÿ¡s)ÿ±.ÿÌ•=ÿÏ—?ÿÏ—?ÿÏ˜@ÿÑ›FÿÒžKÿÔ PÿÚ®hÿÜ²pÿÜ²pÿÜ²pÿÜ²pÿÜ²pÿÜ³qÿãÂŽÿçÊ�ÿîÚ¹ÿá»üÐ›Hn!6²›wèäÁŠÿÙ¬fÿÙ­gÿÙ­gÿÙ­gÿÙ­gÿÙ­gÿÙ­gÿÙ®hÿØ«cÿÈ•Fÿ²€1ÿ«y+ÿ«y+ÿ«y+ÿ«y+ÿ—k&ÿ‚]!ÿ€[!ÿ`Dÿ:)ÿO8ÿbFÿxUÿ˜l'ÿ¥v*ÿ»…/ÿÌ“9ÿÏ—?ÿÏ—?ÿÏ—?ÿÏ—?ÿÏ˜AÿÕ£UÿÜ±oÿÜ²pÿÜ²pÿÜ²pÿÜ²pÿÜ²pÿÞ¶wÿåÇ–ÿèÎ£ÿíØ¶ÿÙ­gèÑ›F5   �yW»åÉ�ÿÚ¯lÿÙ­gÿÙ­gÿÙ­gÿÙ­gÿÙ­hÿÙ¬fÿÑ¢XÿÆ‘?ÿ»…0ÿ­{+ÿ«y+ÿ«y+ÿ«y+ÿœo'ÿƒ^"ÿ�\!ÿ[!ÿN7ÿ@.ÿV>ÿmNÿ~Y ÿ�p(ÿ®|,ÿÆ�2ÿÈ�3ÿÌ”:ÿÏ—?ÿÏ—?ÿÏ—?ÿÐšDÿ×§\ÿÙ¬fÿÛ°lÿÜ²pÿÜ²pÿÜ²pÿÜ²oÿà¼ƒÿæÉšÿëÔ®ÿèÍ¢ÿÔ£T¹ÎšGâ¶q cO1r×¿™ÿà»�ÿÙ­fÿÙ­gÿÙ­gÿÙ­gÿÔ¦]ÿÉ–FÿÀ‰3ÿ¾†/ÿ¾‡0ÿ¸‚.ÿ¬z+ÿ«y+ÿ r(ÿ†_"ÿ�\!ÿ‚]!ÿuTÿ?-ÿH3ÿ^CÿsRÿ‰b#ÿšn'ÿº„/ÿÉŽ2ÿÈŽ2ÿÉ�3ÿÍ•<ÿÏ—?ÿÐ˜AÿÕ¤VÿØªbÿØªbÿØªcÿÙ­gÿÛ±mÿÜ²pÿÝ´sÿäÄ‘ÿçË�ÿîÙ¹ÿßºþÑ�IlÔ N 3' '(²šuàéÍ¢ÿÚ°lÿÙ­gÿÖ©aÿÌšLÿÁ‹6ÿ¾‡/ÿ¾‡0ÿ¾‡0ÿ¾‡0ÿ¾‡0ÿ·‚.ÿ¥u*ÿ‰a#ÿ€\!ÿ�\!ÿ‚]!ÿeHÿ:)ÿN8ÿeHÿxUÿ‘g%ÿ�p(ÿ¿ˆ0ÿÈŽ2ÿÈŽ2ÿÈŽ2ÿÉ�4ÿÎ—?ÿÔ¢TÿØªbÿØªbÿØªbÿØªbÿØªbÿØ«dÿÚ®iÿàºÿæÈ™ÿêÑ©ÿëÓ­ÿ×¨]ØÐ›F!ÒžM     }eA�áÊ¥ÿá¾‡ÿÑ¡VÿÃŽ;ÿ¾‡0ÿ¾‡0ÿ¾‡0ÿ¾‡0ÿ¾‡0ÿ¾‡0ÿ¾‡0ÿ¹„/ÿ•j&ÿ‚]!ÿ�\!ÿ�\!ÿ[ ÿV=ÿ;*ÿQ:ÿiKÿ~Z ÿ•j&ÿ¥v*ÿ½‡0ÿÇŽ2ÿÈŽ2ÿÈŽ2ÿÊ‘6ÿÑ�KÿØ©`ÿØªbÿØªbÿØªbÿØªbÿØªbÿØªaÿÙ­hÿá¾†ÿåÈ˜ÿíÙ¸ÿà¼‚ÿÒžK|á«R ÏœM     K8 9(.·�uáêÏ¥ÿÑ£[ÿ¾ˆ1ÿ¾‡0ÿ¾‡0ÿ¾‡0ÿ¾‡0ÿ¾‡0ÿ¾‡0ÿ¼†/ÿŸr)ÿŒd$ÿŠc#ÿ…_"ÿ‚]!ÿvTÿL7ÿ=+ÿT<ÿlMÿ„^!ÿ—l&ÿ®|,ÿº…/ÿÄ‹1ÿÉ�4ÿÌ–>ÿÒŸMÿÔ¢RÿÕ¥XÿØªaÿØªbÿØªbÿØªbÿØªbÿØ«dÿÞ¸{ÿâÀŠÿçÌ ÿæÈ™ÿÔ£UËÐ›GÑžL              x_8}ÙÀ—þåÅ“ÿÍœPÿ¾ˆ1ÿ¾‡0ÿ¾‡0ÿ¾‡0ÿ¾‡0ÿ½‡0ÿ¥v*ÿ�d$ÿŒd$ÿŒd$ÿŒd$ÿŠb#ÿoOÿO8ÿ?-ÿU<ÿmNÿˆa"ÿ›o'ÿ¶‚/ÿ¾‰3ÿÈ’<ÿÑžKÿÔ¢QÿÔ¢RÿÔ¢RÿÔ¢RÿÖ¦ZÿØªbÿØªbÿØªbÿØªbÿÝµuÿâ¿‰ÿåÇ—ÿèÎ¤ÿÖ§]ðÌ“:QÐ–: Í™H             :* ! £‡Z¾èÐ©ÿá¾‡ÿÌ›Oÿ¿ˆ1ÿ¾‡0ÿ¾‡0ÿ¾‡0ÿ«z+ÿŽf%ÿŒd$ÿŒd$ÿŒd$ÿŒd$ÿˆa#ÿjKÿ_CÿT;ÿgIÿ‚]!ÿ r(ÿµ‚0ÿË•>ÿÏ™BÿÑ›FÿÔ¡QÿÔ¢RÿÔ¢RÿÔ¢RÿÔ¢RÿÔ¢SÿÖ§\ÿØªbÿØªcÿÜ´tÿá¿ˆÿäÅ”ÿéÐ§ÿÚ°mûÎ–=‚Ë‘7Í•=                     Žl7 ]E;À£tâìÓ­ÿà¼ƒÿÏŸTÿÀŠ4ÿ¾‡/ÿ°}-ÿ‘g%ÿŒd$ÿŒd$ÿŒd$ÿŒd$ÿ�d$ÿZ ÿdGÿcFÿZ@ÿhJÿ‰b#ÿªy+ÿÀ‹5ÿÐ™CÿÑšCÿÑšCÿÓŸMÿÔ¢RÿÔ¢RÿÔ¢RÿÔ¢RÿÔ¢RÿÔ¢Sÿ×©`ÿÝ¶wÿâ¿ˆÿåÆ”ÿéÏ¦ÿÜ³sýÏ˜AšÊ‘6Í”;                          ÿÿÿ €b3_Ñ³„ñìÔ®ÿá¾†ÿÔ§_ÿ½‹;ÿ”j&ÿ‹c#ÿŒd$ÿŒd$ÿŒd$ÿŒd$ÿŒd$ÿrQÿbFÿcFÿ_Dÿ]BÿxVÿ�p(ÿ»†2ÿÏ˜AÿÑšCÿÑšCÿÒ�IÿÔ¢RÿÔ¢RÿÔ¢RÿÔ¢QÿÔ¢RÿÖ¦[ÿÛ±oÿá¾‡ÿæÉšÿèÎ£ÿÛ±nûÏ˜AšÎ•;Î–>                                 7(    šxArÚ»‹ôìÕ°ÿãÁŒÿÔ©fÿ±‡Dÿ”l+ÿ‹c#ÿ‹c#ÿŒd$ÿŒd$ÿ†`"ÿhJÿcFÿcFÿcFÿ_Cÿ_DÿjKÿ¬€9ÿÓŸMÿÑšDÿÑšBÿÑ›DÿÓ OÿÔ¢RÿÔ£TÿÖ§[ÿÙ­hÿÜ³tÿß¹~ÿäÅ“ÿäÅ”ÿ×©`ðÎ–?�Ê‘7Ì“9 _'                                      _F ( ²‰IqÞ½ˆñëÒ¬ÿäÃ�ÿÜ³sÿË [ÿ¯…Bÿ—o.ÿ�e%ÿ‹c#ÿzWÿbEÿbEÿbEÿbEÿaEÿ_CÿtV'ÿÈ™NÿÚ­fÿÖ¦ZÿÔ OÿÔ OÿÖ¦ZÿÙ¬fÿÛ°nÿÝµwÿßº�ÿâÁŒÿáÀ‹ÿ×¬hýÎ™EÊÌ”:PÊ�6Ë’8                                                 œs1 wQÏžP_Þ¸zãéÏ¦ÿçËŸÿá½„ÿÛ²pÿÐ¥`ÿ½“Oÿ«�>ÿˆd,ÿsS"ÿoP ÿpQ!ÿwW%ÿ‰f/ÿ«‚BÿÌœPÿØ©_ÿàºÿà»‚ÿÜ´tÿÜ³rÿÞ¶xÿá¾†ÿâÀ‹ÿâÀ‹ÿÞº€ÿ×ªeüÍ™FÕÇ�2xÄ‰*ÌŽ- ¿‡/                                                         Î˜B Á~Ñ�K<Ù®iÁåÆ–ýêÒ«ÿæÉ›ÿá¾†ÿÞ¶wÿÚ¯lÿÒ¥]ÿËžVÿÇšTÿÇ›TÿÎ XÿÖ¨^ÿÚ­dÿÚ­gÿß¹}ÿçÊœÿÔ«jóËžVöÏ¤`þÐ¦dÿÕªfýÔ¥\ôÏœKÜÊ“;«Å‹/bÃ‡)½�%¿„'                                                                     µz# Ö£R Î™CÕ¤W€Þ·yåçËžÿëÓ­ÿéÎ¤ÿäÃ�ÿà»€ÿß·yÿÞ¶vÿÞ¶uÿÞ·xÿßº~ÿâ¿ˆÿäÄ‘ÿéÎ£ÿá¾†ïÎœL`¶'C¸‚,`¹ƒ-eÁˆ-YÅ‰+=Ã‡'Á„#ÈŒ) ¦g                                                                                     Í•= È�2ÐœH0Ö¦Z’ÝµváäÅ“ýèÍ¢ÿéÏ§ÿéÏ¦ÿèÎ£ÿèÍ£ÿéÏ¦ÿêÑ«ÿëÓ­ÿæÉ›ÿÞ·yåÔ¤VhÊ‘6Ð›H                                                                                                                             Î–< Ê�5Ð›F&Ó¡Qj×©`¬Û²p×ß¹~íá¿‡öâÁŒùâÀŠöà»�ëÛ²qÎÖ§[�Ñ�K2Ê�5Î—A                                                                                                                                         œ= â¶S ÉŽ4Ï˜AÑœG;ÒžLQÒžLXÑžKPÐ›G7Î–=Ä„$Ç‹,                                                                                         ÿÿÀ ÿÿ  ÿþ  ÿ  ÿü  ÿ  ÿø  ÿ  ÿ0   ÿ  þ0     ü    ?  ø      ø €   ð <    à     à     À     €     €     €              0    ø     ü       ?€                                                                                            €      €      €      À      à      à      ð      ø      ø    ?  ü      þ   ÿ  ÿ   ÿ  ÿÀ  ÿ  ÿà ÿÿ  ÿø ÿÿ  ÿÿ ÿÿ  ‰PNG

   IHDR         \r¨f  € IDATxÚì}wœå™ÿ÷�ª.moöº¬;®‚ÁØ„ÐB	Ž$€Ó+I(ù]r	wÀÝåîRH%…�5	�\$@(¡0Ýlã¾öö¦®éïï�ÑhUFÒhW[Lüý|ÖÞ�F3£yž÷y¾OŽáŽáŽáŽáŽáŽádªOàÊcÃõ÷‡˜Æ–€ÝkFOTîO©çŸ¼Cžêó<†£ÇÀcÃõ÷‡hMÝ¬P °ŒaÈ
Ý �„�v AÏ¶k­srCSƒ°t
,Öºß0èÖp4ºÝxðW»Ž)ˆc°Ã10‰Øpýý!ÒÔ¶šg¹UA7·VÑéJŽeæNÆgkºñ¥t;�¥¤gŸúâÚÎ©¾Ç0õ8¦ &–À‡<âûŠÓ8–Y5ÕçdAÓ�7‚¿‡#w?{ÕÉ[§ú|ŽajpLTº¥�ã„KÝ{CõSŽgªy|.ïÓèø�©éÆþ”¢ß¢iÊ=Ç,ƒ,S U@–Ð_:ÖU>äâpq¹9E!77ÏÂ#0à¯À‚gí¿.U§H(:Tƒ"©ˆÊÂ)IÇ`BÁHRu¤(M5t0wG¢Ñï³
þ1pLŒ®¿?œ5ç ä“ËœZÉ{¼<Z"š¼ý"¼|Qá®T�b8©¢'*ãHTÅ�á$Í(ùô�ýÃá?¦ÞÝ8¦ *Ä)?|nE0¸Æ-pç8eè›}<f×ºÑr¡5 L¨°;E_LÁ;ƒ)ìêO",iE÷S5ýÎè¡_yæÆÂS}ÎÇP}Lý“x”à¼_o½ÌéjïâÌ«saN­í5.xÒ ªN¡Šf@5L›Ý0(ô"æ;K †!àók8Ó]¨DÁ$U=Q;ûSØ3�°u8†IŠvÕƒŸXq÷$ßöc˜`S %°~Óf±ö²k.!7–×¹8K=XÚìÅŒ ˜`;$UŠf@ÖH…fPõ*°yYpó&gàáST	%ÕQW@Ñ¼ÑÇŽ¾„­‹ jú�#wÿ3Çr
Þ=8¦ l°~ÓfÑýOW}Å-°Ÿ-%øæ7x±¬Ù‹yu.pì¨�¥T=ó{R5“4HÍÙ>Ùpó,‚.6‡PÌV ,Eðfw¬À"Ðtã�X2uÁ±hÁ»Ç@,Á¯ñ
ÿ\Ê¿¹8¬žáÃÊ6?Ü<[ðzJÕ3,üT
|)dGÎÞ:ˆIžÞAgXÊÙ®éÆþX2µñ˜8úqL¤‘öñKšúóëÜXÓ@G�»à5Kè“ê¨9_‰ð«…®›«±F	´ôï¥\	 ÿŸcp„‚e™Ì6'ˆ+|ƒ:/¯ÀÚîóv_ÏíÉ±Ž)�wþáÀ©?{åô€[üv±ø=G€%Í>œ<'ˆ:Ÿóš¦ˆÈ¦àÛùðÅ€¤ê�4“ðÓt
Å¨®ÿo�·E
º8o/ÜqeÔ9‚F/¿‹+Øo ®â¡]CH(£×¤éÆ‘ƒû7‹½ø‡U º¥½Îïù�r¡ÝëV¶ú±~N�<�ˆJÂ’†¤R:–®’ª9íû+šQ•Ì½±B`Ü<¯Èf¬„¸Í5øÍ~¡À5H(:Ú9„�„šÙÆ€þñW,ûÐÔ]Õ1Œÿp
Àòó½<þÛ.M·˜à—[í³‘RõÌ*‘§'ÀdLþlò25n-1g›¢¸ÿ­Á% iúåÇB„G'þ¡À)?|nE(¼­˜¹¿´Éƒ59¦¾¦H¨ˆH¥9¥êˆ+F�ÙïtÕ7zÚ÷×©ù+§Ù¸œåû3 K8–K(Æyé�aˆÊ¼‚°#4EŽ`fPÌ±ÍÀ}Û2	Dšnì¾ë¦%váÁõ›6‹ÌyŸZÔwh_jœ+xõº®µ €¡H�ùû3‚« X–ëQ’ÑA—7Ðçóˆ½„»�ñÕÇ?„Øpýý¡š9s¿É0ÌWí^o¹pÆ‚šœÕÎ‰à[+}¬„+ é’¦gÓ0L—@Ö)4ƒBÕ�LP5`%ñ,G rÅk,`¡”"h¹G-¢pJÃïßìË(·XJýÚãŸ]õ��?ÝÒK©TM{Ç«´TlÙX¯…< Ü¨2¦”FÐCšª¼¡ËÉíõ[i_×+Ç8ˆ±ã]¯ Ò$ß/ìØý�‹Ã¦y!,möe¶•|Í�é;ZM§N©�4Šdº`g²a��…‡gr”Q¶°Ô¸ù× ÁË£Á7*�ïô'ðøÞ°ußöïÛ€åD×¬j�7ë	:Ú�Pc»¦*oð÷„ßÍ?sÌRpŽw­X¿i³XÅµÿi·ês8af ›:B™‡ÜòñâªíñŠ™øvˆËd�"&kÐ)ªºÂW~��_ä rÄV ¦kQë.Œäó~{�a	á„„¡¡¡ª�cþê_	5¶SÃx8àÿ¦ýáæg�e.Ç»R”òõ["ÎYT›óG%	µh(/œÒJ
°eæGåâ
Â.ãnªaE6a?‘Aƒ/—Ì¶Â)¿y£=C$ñ²ŸéxE^vô~
<%}ß:§KHr�ªÜ3JiÔPåûkÞ»�)ƒB¼ëÀy¿ÞzãŽ|†Ÿ#ÀÆŽNœÊlK©:úb
$–.*kHÈ¥Mö¸¬•úlLG ˜¡=Ž >‘+ªD– Ù/ä¸3ƒBÆ:xl÷0ž~»’¬d^o¯ñ èá9]Ü‹¢8–�O0÷�+")1YÇ@BEXÒNH§4GÇÊ†¥”pÿ­;ÿç’ç§úÞO¼kÀ†ëïfÍùÏ±Wä¿ÖráÜ%uv¿”Ÿ_nÅW41YÏ˜÷N1ÝÜ Ù‰=ê½<Î&`£æÕ¹ pz¢2~ýü^´´5Ô¡ÎËW”�˜�R‰K$UG_BCOTF_$itÇuFÓœ+B�ír<|g�¡üê™D|W(€´ÉÿÇ|¢ÏnÕ/fî—üJVûb(fÈª=…šˆ"ž’AÔÉÌ¿W¥DÎ¾¼Ë \.x!PÁw b�)†l`ÁêN”�|%à´×¸ hvõ'Ç|_
>ßÍUüÕ è�)8‘qd ‚ž¤3‹Ë²
‚.ö²ÒQ¯ ÎûõÖË\÷Ãüâ�/�Ž«ÏøúÅV}Í00’*^´‘tŒ$•ŠVûbHÄ"ˆŽc¤¿R¸ƒ±”èŒ<!/B¬iBSC=¼¡:„ZÚá	Ô”?'Åþº]ƒZŸÉ7°àX4û…Ìß3ƒt
tŽH(‡låªeÝP.¦ä’cþ�qÅÀ¾¡ÆŒÎ°ìè€,!OÄûÝø�äÕ
à‚Û·}×Žå_ÚäÁyKê3+UJÕÑUrV}+œgÃ×tŠˆ¤UlæçÃøD4ŒÃýÃP#�ú'/BÅ¸¼hm›ÿÌ…¨ŸÙak%S €iA5úÅ%Pïát›î”È™=¬è‰ÕÐ$¥QÈšÍ Mw|ýâh¨R`8–”­g(‡¸b`÷@o÷Dq,!OxEæ;OaõãòÅL#•
`Ãõ÷‡::þ7?�Ÿ#ÀY‹ê°ªÍŸÙÖ•VýRßPB³à«ŠŒ�á’ƒ]8Ü?C.4‹“]{@õÊ	¬ñ‚°Úfw eÁr[FCõ¥€uOí”À¬�˜£`‡*ªUÙr•6ÿEŽÀ'rf£Ô¬š§è‰*ØÑ—ÀÞÁ$ÊqÿÁQ§ :>wóÜãÖžò‡ü_ÈÅáâå“ßŽá/eî�ÕÔWƒ½ÝéïEÏÀ`Ùý•È ÔÈÀ”ÞCÎÂ‚«Ñ4yÙæ €é4ú„œmÙ®Àî�ä¸ x–�·Â.ê"GàâXø]\E®ƒ¤êx«?åÈ*`	yÂM”¯¾9‚£J´žÿ•uKO9û/¾†¶n­ZÖ�Ia�Jº£JÎ{­VÙùˆË†SzE°ªS |dŸ#¡Ï†¡iHuï™ê[	 àƒ˜¿t%‚s––Ý×Ž´¬€½åãÿNàY°d|�¤_4;UB$îêObkOýÑ2<†®}ÛüŸwSÔà¨Q /žô¾ÿyð�|á_×ÀûÔfþÎ7ù5ÃÀ@\-0÷ÍÀ`R«ˆÕOÄ"èîîÁáCAuÕñûò‘ìÙªN�|”Ä¡˜µj=:Þ™QnˆI³/7DøDû†Rã>– ~±rö¿ü"‹:ç˜;80œÂË‡£%¥4ÊRýÊW¿ºþ®ªžìáèP „|xÓ·îÿmhFGfG€óŽ«Çª6?T�BÓ‰È9&±U(�PâÝ½ýcZí‹A�‡¡÷LáEâÐ €«yÖ^òEðþâ‘ƒ|W€cLËàp¤P™Y„«jPèEB«lVGc�ÀVlþ;Ç2ºL«À	gp`8…ç÷—tÞ-nÁ´W „å>µéßî¾5ÛLuq.]ÙˆÅ @DÒp(,gºbR5Ð“ùùªN1Øu»öî·%óÆƒéäX
  Ä`=V}øjøêš‹îŸop0œÔ i$Í¬lKÖcÐÅ‚#€È³9ÅKãu	
?‡C�›ud¼Õ—ÀK†m¯‡ã8øx€eØ¯¥~ÿÃ­)ÆÓ[ríi7üî{ÙÂrqøØ	ÍhIPICwLÉtç)fò÷ÇÄ4çP·™_©þÎªÄÿÇ}½¦‚cD9	¡¶k7¢Çc»>Ð–ÊöJ(ž¡ðöæ¿¥<<7ÏB¬ÒP§î�jP¼t(Š7�„GÏ‰ãrJ¦ãÉÔv/«_~4ZÓV–¿aý?ßz}Ãâ2Û¼<>vB3êÓ)½ƒI5Ó™Æ0(FRZN§Àùª?Y‚Ÿù¼iâd[ Z®Äq}ÉvŽ ­A ³QÉÎ*d zxÆñ0«n¡Z.ƒ[`Ñä-¯Â)Ïì4:Ã2ãw‹¯³ÐÁ2ì×ÿìªïŒû¤&ÓR–¿aí—x}ëª™m­ŸZÓ’ùÒ{b
†“*†d„?¿\×‰¯o™ú;wížÁÏF¢sç¤~^6¨aZLRÿÁLÙ-ÕÔŒ»³âÂÏ#[ùf£5`æôÇôÅÇÏ‚®±%ød1�×2pjôD<{`)Õ~EIJÊžpÏÅGK¤`Ú) Âò7œð™o]?óÄ³3ÛÚC.|ì„fxyŠAÑS2	,†AÑŸP!§É?M7�PtôÆÕ²¡½žÎâã;…<Ü-žÔÏ¤†CMMF¢º]N‚=Xÿéëm#Í>:ö•Oÿ-‡Ræ%pqB.vÜ‘„ ‹C½Ï¾˜)àâÀ³IÅÀcï�~hæ‰~éÑ�I86Õ;A ,ÃÒ‹¿|ýœM—d¶Í­uá“kZàæ
…_Õ)úb2²9š„¢£§ŒðaÏëÇ¡#Ý“¾êç\/a %"“òYÔ0`(IªÐÑ{CX¬èçöƒ=`xÁÈ	xƒuÄÛÐZx,tG%tüë‡‹cÁŽ£rÐ‚fPÄQIƒf .ž3QÖŒ$5p�+K÷¹y6ó7Ï,¨w£ÎËãHDB~.Cˆ„¹|ÁyŸKíÿË-/Œûâ&ÓGríâÓÿé¿~èË™MóëÜøØê�bPtŽH™Ìµ¤b /®ÂÈ2bâ2†S:B@)E¾‘¦*2ìÜŽÝ;ßBR™ütÜ|0œ`Z tb{šCN>¨ç�uyÀð‚ù#¸Àº¼`<A†„ÐœãFïŸnæ÷G$£*Â ž*þä\+ I30’Ò`ÐôÀ”1(˜¸b–|»9~_pž„Ô{y,lð /®ÚÌ:ÅéË?ô…¹�±Ô#�¶NËöÐÓÃ äÃ­+Nþíš/ÿ(crÎ­uáãyÂo1ûQIÃH*g@zc
d=?Ùg´¥§óÀ”øùå0‘©ÁÅÌ}Îë	€duæ<SðEOÎ6c[Î{}ÁZTÕ2ÿË!äâPëáÆR9‚9µnÔ¸90E‰aP¼x(Š—:£¶¯Ç“©íû�éØ«pê aÎt¬|ää¯þ"zj¹ðéµÎ„?¥êè�+(–Ò>8<Œ½[_Á`lüÙj�‰Ê	(&ü|°¬8:ÚŒ°<„`}Žà–‡PÛ&¯'_¨®±è�± ö¿¨TX	D€é´„’ç»o0…Gß²%5ƒ³jbÓtNLê•C–_&Ö¶ü~Ý¿“þfŸêË~Ã J¨9ÂKçüÛ	¿fPŒÛ�Œ`(9õæ~10Â‹ã?Pœ
? ˆµ-9ÂÀVø=^_U…Ÿgè¤
? „%�#’ã,Ðì0cJÕ±o(…¨¤Á(’ÙØQïÆeÇ7£Î[ØÌ”cH­Î{Ÿ<õg¯œ>©]SÆN˜Iuõ¹S¿~k] ÕläÓàåñ©5-ð‹l�ð[a>‘”Š�"‚•4lëÄ`$NÃ‚uû¡'"îk�ýfè©êÔ €¡$…ß_6ÝMÈ‚XÛFpåîh ë*Lry¼à¸ê™ëÕ"ÿ*¾70sC’ªŽe QBA—iT’�˜lŽxó	,ˆ�%!rŽkòb0¡b$/•˜!ÄÍzyÝÆ+öw?öëm“~ñ6˜€ñk©¦þyõç¿Ódeùy;¡AWVøâ2‹ÿÁÁ˜ñú¾n¤¤ÑÌLVô@l¬Z»úªƒuùÆ�4M)~Fp¬òœ'P°�°<8oÀö¸¢X]+e²Wÿ|Hš�î¨Œ�„š™ÄdÁ/²¶Âo!¦80,�É³ç-©Ãê™þ‚×t°ðz½wž~Ëÿ<¥7 �)Q F2rÇâ3>¼ÂŠõ»8&“ág…úŠ	oL±íe¯h:¶ìÅÁ¾Ûkâ<ˆÓS	0&ouÌP_!×Áês? à¼¡Â{d³0C5Á3Ó§9ªå$Ò±d·ÀEµƒjPt†%D‹¸CpÊÜ6Î³/®R|{:(�IW „åo,\û�…ÿôÿ2R|éÊF´ÍÆŠóÛ	ÿ‘°dÛÁ&šHâµ}ÝN(%?›Ô�ó×a:‚óÆ}Cµ~]¡ð{¶C7˜<~À‚ÈUwµæÙé} �ÝQ1Yƒ§Âôâî¨‚¡„Z”8¾Í‡Ž«·}m:(�ÉU „|XÔ\¿áËßƒî;wq]¦ª¯§ˆðkº�#a© Ì¤Mþƒƒ™LÀrp5Îr<rj2ÁûB ìØ}lj¦?a¹3@ ¤Ý„"“xN@51Õæ¿8HÅÁá]’²1�PÑ•K’ƒ-·� Lµ˜4@X~(½eõç¾“©9_×ÀúÙ¦0&Í¼};á/ã/eò—‚«±ÝV0¦¬{ì\€ÝêÏy‚«?ayÛkgÆ8†«RL'ó?¾t
±¤Qì–—+‹Å‡#JQ%0«FÄEKë¦�˜°~Óf‘êÚ]Ïû¬ß*0™[ëÂ9‹Ms<"�VñÅ½¬ð§To,kòay¸[:ªîÛŽcuìV ö«3ŸpÕ%ùŠaº™ÿ€I@çãHÄ4í+AJÕq`X²1 m!WI%pÂ÷ž¿|²¯}RÀóOÞyK`áš‹/øœ˜	—®h„À$TÝ1S�³“|Š	4‘Ä›ûsYþ±€°ü´‹°.ï˜Ü »ÕŸÜ«?P\ÐÇ:ˆ³RL7óß+ï,<�PÑ–sf”ƒjE°Æ ¼^ï�“�'0ñ
€�óÞÀæuŸûŽg8|ôø¦L¸¯'j
rR1r’|ì„¿R¿¦cd€ó•æ‘�b«?[ÄÅ)jŒƒpŠéfþ»8¦l‹°¤¢ãHDªˆp¢Šƒ*eï9å‡Ï­˜¬{0¡
€pÂLPzËÊ+¾™i3uÖ¢ºãoÅúU�æ4ò°#üöt�Éß/>P¡¦yüªŠ…âŠ�EÂPE )Jô· ml®V>¦“ùÏS8�¤QŽÈUU³jD[%`enüé–öÉ¸¦ Ò~ÿoÖœã·âýK›<Ò¯'+Öß5çóW~Í ØÓ=Œ®‘êeÊåC¨m�6‘�JSƒíŠ›ËÙšÿŒàvrÈ	ÁT¤þƒÕH¤¨ºIVÒï°œè¨wãôù…Ç�Ú¨¤ÿeÃõ÷‡&ú^L˜xá™ßþ‹+TòºÍ_`úý.k`’~ÃI5ÓÌCK—–Äåœ8¿fP¼ÝÙ7¡Âoa:Ex¿³Š»¢äo/èÅVùrdh5\®é´úW*üÙè©,B`)�bÑ�å­>¬k/$}÷²d¨å¾‰¾¢ Ë/cþmåÇ®Ï„ü.^Þ /ÏdH?†!Ii™‡+’Rs2ü,á+Ó?†sž6‘�ñ¦3|é¸=ÕÔÜ]…¡g¼«QB=]V;Æ¿R‰(+�R!Âg°¨±pññ¸„÷Mtx°êjyý¦Íbçþ7®?áÌæ¥ç
 °±£kfú¡]5Kàc’–SØ3ÙÂoÁ*Ò¢Õéÿ?öó``¨JÙá!TW, ªk ¬ ªÊÐ¥$ôTZ"->ªëÐ’1è©(´d$ó£§â Ðâ#™=‡.%@U	†œëò‚c	Sù#cšÿSZx
 4ã_)¢²G¥æ3]¬ˆˆ³çÀÁáyõ:ÅésÎùÔß>tëþ‰¸'UWË„åoà\žëÏüÎ_ÀûkÐìãñÅ“f@`zb
Â’U§™Ñ]ÖäÞì5ÂŸ5:yàÐ”}>P¾k05èRZ2šÓÌ0ËyíX}Þ_g›lDX®ÆÒœ“PÛš‰ –‡È0œ ·K,k5MvÝ¿\ã˜ô«3‚BE.EÈÍåŒWÏFTÒp÷ë½ý4ƒPWMDC‘ªÞËô_vÉUàý5àpéÊ&ADÒ2™~é§éúãÓKø�é°K¦†CS Ë	èRòÀaè‰HASÓJCzNL|šå"P]…$+H&âÂ`/â‘a$qh6½ß­Â˜î@%Ä`8¥- 
¸8œ¿Ä>2Wð¿qþU½+ÔÐêí8žÌÞx1 `cG-~Ša†ùòI¿ÞØh3�é"ü¦Cd€uû2B¯%#Ð¥%5öñâEÌw«Ex)P­´;b)„ðP?†††�LÄAuuÊcÿ•„ûÆŠÎ‘Êòº£JÑRâ¶�ëç„
¶OPµ;CXîS ÆÉ'lþ ³³Ï)&h•÷Æ=CúÄåœpßt~î–Ž)‹šÂò¡ÏG1%0–ó%“³ÂÛA—�·T£ºš±"±"’^46‘K¸o¬8©,c°'*½'«gøÐ^ã*Ø®øvµ“„ª¢ O°–êÚ.>ã#°x~hycÆô�¥ý~+Ó/–E f’Ït~“04†¦™ä›&ƒÝEÍyRåæ¦åÜ€±FdÊb$žB×Há„4©Š@t8¸PuŠ#çsTÃäÁì"CpÎ¢:¸ùB×)¡³w­ß´¹j…UQ F2úMÞlšûÁ« ˜U~íA1cúÀPÒü_ÓôgeýŒ“ç+ËÃÕÒ1þ•@¶Ðç›Úœ¯z]xË�C9Tb …ùá”†®‘ú#É	oË^MÆß)$�æÚå�Rõ‚¶a<ƒ3ö®ðyÜË’g}úÆj�ó¸ aùe,Ç]½ì’« z<ð
,Þ¿À|híLÿÞØèêŽH’Þ[m°¢®–yU?n¶àC±¤ MÃŠlŒ¯5½!U¦¨‹YNIÕ@Tš0Eà$Ç¢•´ŠªjQ> £Þ�e-…Q›�Ïõµj¹ã>ª«ÿåjšMÚO¾À €3ÔÀË3ˆHŠžcúgûýQIÃ;Gú«q“Î¨ZdÀ‰à[`8~RRx�œ‹Q�Ò!,_¶Âp"ÁD2þN1�P+Š”j&²±#”3�ÙÍx]"BÁàmÕ8×ñÝ)Âœ	àË.¹
Ç3í!N�[“¹	Ù!¿”ªgü~EÓñÖ¡©ŸŒ[)„ÚÖq·s*øÙ¨´B°JEœ·Y•ð&ÙŠ`<Ád0þNÑuN
ªéè˜x–àýjÁ±Lz6¡ÉkP�UÕˆ
ŒïnQú?�yÇÃšâ{áR3†™Pƒ¸¢C£$'Þ¯»ŽT­¤w²ájœ5&¦ÝZõÇ»F!åXûŠÏÏ¡p;åÆÒ_ ©²°â{4‰Œ¿¨:Íô¹p‚pJ+pƒ‚g–4ypB[¡+àåñßã­» äÃ ]±âbs–ßÒ&æÖš¦ª•è ê"G0’R3ñþ½#Ó–ñwŠJ"•˜ûÅÀp<8‡9	¥&—Š÷SÃp¤Tœð ã�š„SŽ„+‹L'á·�TôŠø€|WÀïâ2dæ©!¸óš³ê„c|n×ŒçÇ® (ý�ÀÂµhX|8œ—Î`êM›üé¹ñ²F!kôÇ•I©ì›h8�š6.ÁÏçåžÃVØb½€t.€ƒPŸ aSMÓ[Õ(ð™($TÇIB–+À3!—Cdz'Ïµq	sÅxº�Mòa +.ü< à„™4ûE¤TIÕ@BÑ‘Ru¨éÿ\ƒ¶šêÁ˜J”ŠTcÕÏç	Œ»kO9wâ¢”«tBþU‚pJ+ÉLE¸¯RtG�=å®cE‹6cÇnñÛc=·±)€¼Õßjî9˜Ì�ùÆÕLnxRÑÁ³3‚X2«µÞê¶šž
Øµ«æª_øyÎÜ€bdÕµ’n€S–_O„‹¾6IS7�)˜Êp_%�4ZÒà¿ÀÀ+š–Œ%?vØ4/T°�‚¬:ï×[/Ë¹U® òVÿg9sÅ×)†“*T�BRuDÒ\@B5�­Àý"‹ùmX0£±êC'&ÙÃF&Rø�Üh 7ÆU––È¨8‘ÍEû£RÆ%˜NŒ¿sÜ<¯È‚Í*—NªFÑ¦í!:êlÂÂ„Œ)9¨ò;Hñ5ß¬ã2«ÿûš‰*ƒIjº§?`¶ü �R¨ºýªSãáqÜ¬–£Þ-p5Îaù	~ %Sƒ³Q*M¸\��R$bæEÜ€ÉH™§4„Ò¤¦ùV½ñÑ{ææY„Ü\Ñ°”ÅpêÜBK�‚Ì‹P™ Ì™ ]±øìÍ Lß?(rˆÈf®8-üá¬N?I¥tö™åßÑzÔºjt| nRzë[©Á´„°i%»û”N¸qª´s&«pJ‹�¸Õ8ºBÉIÅäÈüSÖòUÓš¶C­‡Çr›Arc¥u•Z ßpÕ4¢mõé€LÊoTÒ 4ó¿Eüå›þ¥À³óÛ°dV3Ü®ÉR1^P]…`öÛw5Ì˜ðÏtÒ/°T8¯\‡_Ç<@ž0YS.Ñ\$Rª™w4)�À".k9æ~)ô'Ô¢×·¶½pò0™[{Ù5WrNŽ€9ÚË8yþ—ƒáxfy‹õÞvõJ›þ¥àY,o¯Çì¦c:óTW¡%¢¹÷ˆ!6Ll7g+5x¬ª«¥‰@%å(€êjŽ˜àásI?M7Ð9"Aªp–ßdÃÅ\8†@3€þ
„ìrqE­€JÎÍ± ºv-ËñtöÉ  6u„ ä®þ’:šøPÎô/‡¦ ‡™®ü€�ð[`]>µ­úùXÌä.™¤k%‰@Ày¶Ÿ•TíÐŸí5sØ"ŸÑ‘§¥àYSð.—¯LjÐ.ŽCÉÊ­€J¸ G
€ñkzaûúóï¯A{È…¹µîóo%ý¥{Ê:ulú—»�3‚X6§eÚð¥„ßçU<à£8™!XÊ×/Gê«þ´¤y&zõç8î2¤ßtR<KàØ’çÜ—p^üTÊ
XbÓM„\ãôØŽ€‘Š~€nºÕ—ÅBZÌJÕ!©:b²�šWžÉ„§’p"ü„ÚVÛ1ÜÕ€•\jÕÕKX†Z†¨ èGKD'|õ/'üº"SOZ‚Ï•ÉOÈ&ÊË¡”°Æf¦ Yå´\Ø™@éç½móœ³^�Å	3™ÕßÒNÖê/M`‘O�‡ŸR~À©ð[fMXd€ó…Jæ”$ÕTÙ>€N¯u¢CŸ~we÷oªˆÁl?ß)âÎï]1+ ÁËc–Mû°` àÈ
(« ^<	@ÇÜ“Ï ¼7íwDò|kõñW)¦‚°ÌÝJ1Q‘Î Jô	7PAó'¹c�ÅøWM7*¬ñ¢˜ŸïQÙplë$ ï±©da\æ¤R°¬0TåÓ `‘'Î0³•bé“²ü™ñ• “?0»~Âù«÷þX0‘‘¾Lo‚’õÿeâýN¢V£’±*ÇRÈgü+�"¬¼¤¸âs,ãç;A©´ßl¨-šØQçFÈ�Ò	Çpœpi¹ã–T ù×ºòðþÌ¯s£ÞÃgrþGRæêŸRÌÕ
¿‚Ä	M+Öåä¸kï'*2ÀùjJ&ß”äLü-°fR¼ªý	<<S”ñwŠ”Z\`Æ‹±˜ûÅNU(‘x|k¡àØÏ–;fI@åÄ… üm'~ €9Ã0ÍëæZ¾ÿd®þv¨ñðX9·³›jªæƒP]…Qa#Ìbà¼!ðÁ†ª^3+ºÁy‹•ä”TÙ¤­DÑO~›2-©Î}*î«C	µ¢~å`	þXÌý’ç™tvŽªAs†çfÃn¶ ™[®T¸´0Œ+XŽ§�+N…‹cp\“‘ôÍ÷ý§bõ·CSÐÃßÑ:n~ Æß)ø@CÕ#B¨©èkå|órn€© í�‘?i¸T… S8	÷UŠ¾¸:nRÐ
ëU[ð-Tb©D${àX,l(ä„ü.¡dN@Q@8a&Ë²ëk�?=,mòÂÍ³H)zA³Ï©^ýóQ�²ãJ[`;Eµ#e­Š!=]N”�¨ñpÁ¶bMJõqZÕ~ã!�†õÆÍ(Îòç#*kEÚqM…‹ã²RõE ÕÕKtM%³ßcöû[•f“ªaNz1("’6­Vÿ|XeÇ•†«á÷—‚«aFÕ” Ãñj[Š¾®Jã‹Ø‘�L]�ŒýžŒ�ñwŠ±ðÕôó� ’ÖaÅÜ€Ž:·mÛ°Rõ%\ r~¶ù¿¬Ù—“øci¬é¶úÛ¡’°a5ýþb œ¡¦iüJ£œP* 'ce�ŸÍ”kQ>+`<Œ¿S%TG™‚åç—ƒ¬Óª„í¸ �c>Xl[Àx‚µ,Ë®oZzbÆüÌÖÞ–ö‰HÓË÷/§eÇeúç£š‘�rn@©h€.ÅÊ'e…A™2]¥V@5§N�|L´ŸïNC‚IÕ(ê,i*\ä4M»`Ãõ÷‡ìö·U TN\¨k*i^µ€iþ[™QIC\1 éÆ„fýMJ•O´éŸ�jEÊ¹e:ˆãk‰°ã%N­€j2þNPÌ˜?ß	*‰XÄŠX­~1W‰é„c‚³æ|Àn{ kç@ýò“Á`^�‰´ÖQuŠHJ�´¬¿‰B~Ùñd˜þvàU)*eM”s´d¤¬P‰e¤FÊ*�‰`ü�`(«Æ~²ýür¨„)±ß¼ºÂÔ`
Ø†@š1Üàm›_]3æ7¤ÙU7§ü1Y?*W;Xü@]`b
wœ@¨m7)(Ö4•L
Ò¥WårV +º¡Å†ŸO¹¼€©~	EŸ?ß	œZªA‹º
Ÿ·À�c·o�xþ©»6ð·/] XÜ8ZöWt$d“0Œ£wõ·ƒ×ÄÌŽùà¼åKm'ã�šZ2)h<V€Uî«K‰Š:³&’ñ/Žeàæ	T½ú«ÕBT6gsZbA4 ¥uvIA…. ¥§@pÉI €¥Í¾ó$¥V­ÞºÀòy–E{[+šÚçÀãšÜ‡t¬-Åt9-…¡¤JFÌ}K'³XqÔ÷WFúŸ›jc1¸DaJZy»ybFÒ³õFnS�˜âLÄ‹Dàx† Ý¦B0äßŸ¿ÍŽ8 æ-GÈÅ¡ÞÃgÌI5Ç|+ÚôÔžcAR5
†Nx]"šÛç¢©¥eÂkÝ³QIá�%øÙÃ>ÑSº6 Œ %#9+<5Œâ�jrEƒ²­€É÷åÃÍ“œ¡šRÚôµœº¥¢µ…
À ¦lg#G0ž`-ËqË½móÁûk21EËü·4Ó»iõO”Ècðúƒ˜1k6|µõ“v>¬ËW22`hj�à[ S6¿ \åžÉüÎð¢mØÏj„êÊH/€É÷(˜¦k‡éjXS´� Øó;»¶0bCAVå‡s€‘Š­Ñ5•Xþÿ‚z·Ùô3mþÇeò»HúíVÿ|ð,‹ÆúzÌž5kÒø»È€%øåºõðÁÆ’¯ë‰Hé2a%•Yá³MÿlPMv¬¨&ƒ×S“*ü–¹_ÓÙ
°¸¶rHq¼[P" ¤©muöß¹w)íÿûæ®`j+÷ÿÝhþ'*ÈbdD·ÉL’[�ÝRÌÐTÇmºÊå å¹ ->\6æ¯%ÂŽ	AIžœiÐÅÌýR˜®V@Ä¡(¥ÀæÔ’Êù<@¾š< jŸñÿUƒ"©ˆ)ÆQ•ùWªN+?mÁëbÎÜ'¶ó/ 5Í04Õñ°ŽÌûj[�Ó�ÊY€³!NÂ‚ŒËCS!ÙUc|©iº¨¥.N¥Â�3Båy€ŒX¿i³ÈrÜrÞ„¯®í5®Lø/¥êˆË¦IÓÕª`¼uâõ¡ fvÌŸP~@—S‚ Leó[XÑ6Pú¼Jå0¼z2Zv…×¥DIN�qù2ó’‰ô*gY:ñó� ,M? ëÔq8°˜%;3Xh0T_‘]˜y²þþÜ='èšJæ›ÍDçÕ¹ ¤¨}×™ÿ©*$2YüÀD„µDTWA8b“Þî¦9%­ CNÚ–
3‚;£pœäõ«‘[E‘-ü™}+´dJ^ŸC?ß	b²mNr,ÅØ¥3ç}j‘õwæš² üí‹˜SHSŠY3�˜fM?Æ‹˜CÿÊ)ª6´„ß#zÀÊ¦ëfƒuûÀ”'.Gs\Âr`¸QEFuÍ¶@>òó'ÚN.ª†0s¿¦cUkRuö}—âZ…SR#Ë­ßGU(Å2 ´u  ZüTƒBÒL+àÝdþOÔ 	‹�[�/ü8o ¼/TÑ±<m2Ó{ŠÞ¨+Àð…ÄŸžŒ–�ûk‰pfÂ‰`Äâb<î¼Ûp6&Jð3çUÅÖaÕB5x€¶À¨0°íY¼ô“«ÿàzøÖ{­mÙjz% øg.Dƒ—‡›g¡êŠ™$ëïó¬ä_%h¬¯GMMzzû*j+¦Kq[á·À*Š°n¸`#Œd(2½Çt<`�E¹->R4$˜¹¯Ñ!°3J
?`F I„àr6I˜c™ª™ú¥`‘�Ó©>ÀâœU4¼Pxî!7‡Þ—ÁÛÞŠD×ÞïôÚlÇŽF	@ 4·M>>Sû/¥Í‹w‹ù?Yã£*M+¦šìH°…`CE£¸¼³—AO•núQªk`ºJ™¸?ÕdÇaÁdÂ™PM?ß	œúÜ“	§EwrÞs�EñûßÞ…½p-^¿õ›4Ñ½÷« ½6ÿ} ¼ðÌoP]%ÞÆ™`8ž™2	@Õ ™ðß»“=?ÎëámŸ‹D,‚þ�AÛªÉŽ{ë[¤ 4Ðéh†ãái[€TßA0®ÂfŒèáxèÑþ’V€žŒAãø¢s	Y—z2
Öå+kš
EJµÜ<™0S¿ŠŽ:Ïä%,9='¯Xþ^XŠ"‹â¡_ý ÿ{÷o �ô§_%? èMvïc €ÚR ¶ÎP>ð‰fE4ùx„\x‡óÍ§&Ãü/†RiÅ•v!ª42 ¶.€Gí¢i©i*t©¸¥@ZÜ>ù‡uy3V‰uÖ()ØV­°ÞX¡êtÚE‡¡À”ªãÅîÂ•ÿt6~ñó›é¨ð ½À^r­9ëc Ëß@uõúÅg|‹?úüÛûf ö¥°µ;Ÿhß¼!¥êP4)�BÖt$}Z»
1Y¯z`,Pu=ÃèR¼¢aœÙÐQÇghZOø¥?¶°5£	L¬Mù0ï¯+ï�@XbÝè1'ð| ¬ƒ&'¯._Ë€gè”	~6¼<.nüª8¦°¾ß*¨âY’ÞÇ\�—µŒ~oC	ÑîèéïÇ›o¿ƒ÷BMÅ¡�ô á®ÿåìS/þÙ³W�¼• ªk³ÀÝ: Pïáq8"!¥êÐ))ZÁåæÍVJÙ�‘¦H©ƒšaDÝ$W¦ƒbÐ¦I#‹HÄ¼è>°gÌÇá¼Gî5†iZ¼¤t®c}µ¶Â jl„åsÂ�9ÇJ‡y_ÈVø“´ËÈ‡$+¨©!ÓÊ¢ŒËÚ´Q °Ä”¿|a·CJÑáN�u^uó`ÎüpÏ^�è;9×gRŠVàCÖ•® !Ôoú ªN“4°„‚«àâX~kÿ,JÓ$ŠnDL¨Æ¤·«FòO5¡î–yPcCf-½ò¹“È€å×{Ú—ÎÙúÔs�µ'—:¦)ÉDÁº¼J¬òjd dM+z@Xñ”‚Ÿ³^ƒ“�©zF,açX,1Ï�ÉºÿœƒlPÕ °»“v¼†‹cZ�L � ž†hòñ™ƒI…P%íÌ±‚n>3B.,lð`i³unÌŠ¨÷šÍÙ	*Ÿ*ß¿øùèH¤Ì�¼¿î–Ž¢[9ˆu­Ž"Çµ´ŸåÎèî-}¥zöiš
-1R2ñH—%–!'m§1ÎÌœo"%CÕ§Þ-ËÆD†¼9Y¯ÀÂ+°ˆBnÎt³<KÀ0LŽðK+‹%ÙU*æ›çã	ÖÉˆ ¼ÁZÔºùQß^5 pkžM–1Ùì9ÄS¹r„å!6Ì¯$!uWÌˆµ-�‡{ÊZºAÎ%,w]bßë·z;Ž/š¹hUrÞšK€ð"¨¡C‰–$#µd4ã
0Âò¶ŠjºY’F!ØxŠ¦#,é�4³ŒÜ�SPSQˆ‚�gôûðšþºµ¢È¬êŒí*^~ÅÓ¨³UQ)òŒ\x†ä$‰S·áúûC•“m Àr<åý5¤ÆÍB1(dÍ€¤é¨á'?,RÊ�H©æyUêFLÿÈ]ýóÁ¸[æAéuLð&›/ë!÷”Ü�á¸–™ç~auçý?ù¾tø­kÜsVÝW—“`W)HøÑ4_]NBK„‹v5¦š
->Wã¬’J"%Ãç¦	˜‹…e‹iERÑÑ•0œ7ÄS•d¨±8 +¯¡nŽ % bÁüy<}çÆ|>fªoy9,51àâ
æÐšºY5ô6 pÕ6 ¨ñ™@:%ùÿŽeàwqcr#¦“û¯¨åÓN…šfxf,rÜ‹­(ÊðŸi=ÿË÷IáÁç”#;Kî«Æ†33³…?s-Ñ¡¢VG%uùÑTÂ²£’†þ˜Œ¸¬9VN‰x;wíÆƒ�<†×wìªŠë©9<F©Ï
ˆ…rÌ®F†0Ì `ÓNÐÅBÓ$U#Ã<Nw¸yA7�f¿€Y5n,nòaQƒ³B.´DøEŒœp¤$gÃ*	ËÃÝ2¯¢	Bœ7àˆK`ö;ŒÛA¢ÿð>¥ï@É}ÕØ0Ãeõó­Âñ`=�ŒòrÒ=h:qªNÑWrêx–@ å­H+"C5;wíÆÿ=ú4’69•@q˜›PJøÄÂï.àš€Ì  ŸÏÌó
,’ªÉÔO'¡©–µPçá1´ûUxâ]XÕêÅ’F:êÜh¹¹IWrª®CrXåe�÷×UdðÁ†²ûBNn9õÒK	Ë0Ñµ'VJ	po¶/ÒDÄÐTh‰0Ç›�Ió>ÛÌu(�nL�À³nÎüñ‹,¼<:êÜ.#GP–£šZ�é©'£¸ïþqpïî1Ÿ£aÐV‚S~¡ðÜSªÑÊQ]ó€2- Ï ©H©ú„€“…®¯ fÏ[· ¸‘ËŒªºYñ˜Ru$U#3þ|"0Ö‡Ü²œrb]ky2‘s]wÊu÷-yæß/¼Dê;ø0 "4Í)Ø�²< kP#ýE­]NA`ù¢V‚€Ø0«ä9'Rò„‘�™8:I“rEr[j=XBÐt¡;"å¼æu‰èÝ¿Ñ=¯Aê;�©¤jšà�¹ØöÞX•’O>ù>àõ¡±¥­âsgªNáD5Ý°uW\6‰|^‘mà Ô€K4ÓÝ‹ˆlF¼Âô eÆ‹½/>ÉU‹÷~ä‹¶¯ó,AÈÍe”‚aÐ)ABÑÍYˆ²Vµøðxëá…šfpÞ ¤þÎ²Œ¿l(`8®åÀÖ'¿j|V×éG¥¾ƒ¿Až`³ÚŒÓ´Èn<Êp|Ž©_¬+±ÙÔ4®L�‚„$Ãëß”$ž%eÝu>ÃÜ‹,A½—Ç`B…F�Ã/>œ®¨+ž¸Å{ƒ·ÁåAXºœÊIó~èáGðO}ž`Í¸®¯ŠåØeòr™Ë ´ Ñ E3 èÆ´" Ç
%6Œý»v oÇ‹Ôi}5Ã0¥Ð^ãÂ’fVµz±°Ám;|±TjþÛžŸàq”7à¤fÀ$¿²”þN×µÿ—èÚËàlH<C‘ ÅGÀp<Ø<s¿\Z³]^@>œò#@¡ùn)q¯ÀBäÙŠ„ß/0óÜA¿ÈAÐ“xéû_À+?ÿç’Â j"‚�—Â¡ßý’=ûrÆª¦}êo��iª–:Î�`/Ë Á3Z)&O³˜ùxðêßŸ èšJúw½2æã0ŸÈ™!žÏ˜B5I.+o AH8¾ld€aØï  (½‰°ü�– EBwTWÍ¨€�¹_ª�˜¡©¶Ó‚²!©FÁ}âY’v;A¯TØóÁ33‚…4cC½øÓ7?Šî7Ÿ­èxj"‚®©ÿ`Á}ëê<ˆÃ;^®ø�ö,æºÚYó.Žie@i  x·®´“!iÅ’Ž>~ëÕÌï{þþhÕŽ[L!4xù‚¹lœ„ÿ*…•EX*ÎÎy%• !ää¶³?û  ºzƒ¥R�oå^³àëöƒ°<Ôè íjohjÉnÁå:Ž‡¢j‚>–UÝ)fÔ¸Y4ûG•¹N)þú_Ÿ-»ê—ÂÐÿG-…¨ËI(á~hñaüý™§*¶œ&³ìëyˆ‡hfé·@JÕ�ê@6Þxî‰ÌïÝo�Ý(K!X.ÃòfÕ¸rÈFe‚,+'.AÙð çºÎšCuõ€|?Ñµ©}¯�°\Fð³¡F‡m#¥úZ\ `
;áxð.wæ‡ã8ÄULˆ C�Û¼.GPçáÐì°íÿnÁþ];Æu\]SÉÈ+†2Ò5ÒŸábF"tî»bì¢]ŠAÛ >À¬éæYÅ �T9ú5@Oç~$úgþÞ¿kÇ¸c²NÁ§I¤Ž:7VµzÑQç†G‹Â5An2©Ä%VúRáÁ!˜½–°ü�‰þÃH¾³Åö=ÔÐŠšü–Àp|æ‡uùÀº| º–vŽ³�„S“Ó£¯ÎÃÃ•·ÚÉ0žº÷—U9¾4Ò_à
 À[Û¶Vtœñf²«èe dh^KIHw”$•Â®—Ÿ)ØvxÛß'ý<†A@d�|þ7XÛÑ„U³0«ÞgL„2jšK-ÕR,CZ[î@ÿaÄw<mM04jlØ¼Î,a74¬ËgvNÿdŽ«©PÊôJtÚw¼°Ë�{áÑ –¤Z	¤Ãolë:¸oLd`9³2‹úËñÀŠ7˜’ÎÕÆÛ/=U°íÍ-/LÉ¹Þñ2Þxéy f^öìz?3QÊ€ó‹òå"B0�l%Ýö¤9«€1Û‡³¢''L˜/ìZ	Ö¿\ƒ˜¤BŸ„î<-~.žÍi¼i÷Ü8ÅG/»O?ý4^yå|ñKW0­ ªk™C‘ �«ž’DŽ©³µ¿Tƒõ
@ÓìÛöjÁöÎ7žƒa“Nrvíx‰®=PÂ}B£±ò€‹CÀågf×û1”T1Ž¡;ê<V/ õ* ë,%`W8d‚]ßò3kÕÕ@HLéÿ¶=‰Ð{ÎáscõjlØ¬øËZéÕèPéB!M+ê f'»RÖj¢Æ#ä¸ :îÚ>¦c}ô²Ëp÷]weþ>á„  ?ýÉ Îô_´Ð?Ð�Ù˜�;@7(]Ss^Ñtš^Y�éˆðî-¶f\¢ÿ0ûzÆpÄñaï‹�  zß(¾ºÔyxÌo­ÅIš1¿¥^÷øbËÃÕ8ËÖï/Y8Ä¹®ËïJo!_•Fú1ôâaØô1´[ÕKYz²ô
8n€�`äöÓsŽOêSÛ>ûé�§/V×Ð˜ñæØ�eHÁpP¨…ñ.èüâß·}­”N’‘‘£üÆo”ÝŸcÌTÔfÕbMG#ZcWV
±] Xd€á¸–ïßüß/¤•€šˆØ*]NAÏó%&—›™0]Gw¿›À°O³ÃËD�S…oW ãññÆ‚lâ±gës½×Í³9VÁX¹±a–­°‹ä‚£/”V!hA—S Zq—F•Šgjºy‚•€]âŒ«¦qGnýÕ¯
¶Ýrëmæ/,:Ž4ð‰pÉuƒæZ Ó¬kÖ˜“4ôíx1ûjbé À¾m¯Nª²ËNF’FúîÚWñ1,«`mG–¶×£Æ_yÑL1%P,2�Of�¥Â¯=ªæ
x~±R©¶çZ7`¢{ô�$Œ¤4ô'TôDeôÇdÌ^´lLÇúÍÝwã²Ë/Ç3Ï<ƒW_}W~ùüô'?  ˆ3ÁêÀy`E8ŽGcÃØÍXP,C�Ñ55˜Àtë›7VìyóEäp„ÜBnÌÜŒDdLé˜c…Åþ[è~ù‰1ÉD�‡Çò¶VÍn¨XØ)«›P>²3�N–Fú1òÊŸct¥Î_õKùúF™™Qibózb
$UÏIµ]²nã˜�÷›»ïÆ©§žŠÕ«Wg„ßUÓÂr™Fp�¯›—?8æÏ©4ƒ1 2NÚDS�¼”ß}ë7^qóú�WÜ�Lö…U"<ÑèïéÊN'Ý”vO*AÀÅeA%<��(J
r®ë²çÉg#�1x‡4Ò�È¶'s”@v6 ¡icv&ú™ŒÊ…–àIgœÞ[=átÍ<®`[ÛìŽ	‰D+á/–HÄ ÝÐLMÅ!«füÑž˜“òKÈ¿>ÿäòóOÞ!–»Üâ<ª%„å�E8þ…pÂF ±¾/Òjvž¸8Ìo­­È"°Sœ7PÀ0×rÀåúd±ã¬ßtÅgA˜ç]{ßÿff»!'s­€+}©f!šnLh>ÀPR-˜¿ÇxBØxÉgªr|WM#\�³¶·|EEÇoT®˜"e  %ŸV}óÆ
«ü @˜ç@éï¬×¨®n×5í **Ò¬ŒpÂ¨¦!7êšJvo/¨‹`f5®òJ\l(
ÁÊ¬€çŸ¼CfÜþ ì‹îÞuà�ùÃæZ%¢åF£Mô4§‘”IÎIÅPRCoLÁò}s-×qYŽ§5«Ï…PÓ>Ø˜áYj‚A´Ï�?¡×”;÷^`HB¢ ÀÈ&G–˜¡˜HXå¿,ÇSÂ°@Öoºâ öéšJ:·üÕÑ1Ã@JÑ—5„SÓ„QOTFçˆ„}C)¼3�Ä;I¼ÝÇÛ½qlë1Ž¼òË]C5Å,J ô&æ¹#¯=…Î°„îˆùÓWÐW0”T1”T•´t{v½âyu¯Ëg;‹äwìµË,gÉÈ0Ã‹›YŽ§Ã¯?F-¿ÞPåŒPJÈË±ãÝ+ðHDÁHJGoLÉ(–œù�[àm» Ö�ô!Â§*+z „ÁùjñÞ+6ÿ9âì(-°· h’5"  Éæ%kÆQ]`¥úêšv'ÕÕ‚”®çŸ¼Ca®FÙyKÀÃ)³lOTÎõÛ½qìèKbÏP
û‡%Sh£2**Â’fŽQ×Hš�Žv ŽØ5{ŽêZN|ˆ0ì{Þ6Ýkëý	ÅË–4$TôÆUtGåŒ²ÈVåCkÐ…÷ÌiÄ¬z_Ñ�‡™'�›6ÌˆžÂhçº®Ôgªü‚Añïº¦’ØÎç3|@vrÐØy€‰}UƒâH^û/ ð×5ãƒßúZWžRÑñxomç}¹Àô',�¶öÙ˜¹tMÅçÈ:tŠõ¸´[Ø%Íèæ 2PÈa3t?Ê-€Î7ž€ã	\ccŸ©ñW€ÜñÆKÏon½À™ëchª‘Œ1zt‰ð”pfØ¢$Ë�Ãˆ§dRTS '#�Fúc„å¿Hóª«Ûc¶ßøçÏŸt=+ˆ`yÄ ãòÁç!† êá­k6Çµ…êà©oü5Ðò¾Ž "ÏÂÃ3àY\Þ
À1³ëýL­Ï�ƒ½ƒ‘
/ÖÌlGªg4<ÉyC9ÂËp\ËÂ«o¿p÷>öÇb÷è¤ùïçŸ¼ó‚D×žž¶àëf@OÅ3ÊD��õlKˆ	ÇƒêöåÊq™ Ì�F)²òìB>ËOK)qL1 p4§+PLÖ °¬»ægcn	¦F2Ê•°6žvú˜È¿ñæ$•"EB †  73¸ŽæP`¦ü—��dd¸Ô¾Œ'pM¢kÏ™áÁÞ&_]3M5¢½�Lrà=û¡�ô ÒwÑ‘!hÑH#ýk„å¿og… –°Üq™š@GööR¹q¼×,ò©›1¾æÙÍY†À¬%Ð<žŒ†wqLú‡@àF»À\–ÏnÆÁÁ˜qh0^p-Œà�PÛ
e¸Û<÷t'¡ì˜~âà¶« U iëêë ô‘ð[Ï£víy Ò‚7t9FôØ¾×�â@‘~rž†Î~Fsœb5»(PºOàPRE“Ïì(ëƒ	5óÞ9ï=ÍkÎÂ¾·K7Íæ:XÑê	dÜŸsÎ>kBûÅ…]V¥fÐýa¹ÕUè)“p:‡lº &i�4QÙÀ³�=¤Ã~Ï?yGÉ÷ÉÈ0¹ú…ï}þ·P%$ú—p–ã©®©ý éèaÓzÂº	ÃF¨¡'Nè¤†€“6|ä�bçñü“wÈ æYäÚß_ºßK•””†ÃMõ†™A=ŠV€.PÑ™êÞ×›£{_Ïé¼ém›�ÆùËQ¿x-j_]3 €#*|"�Àf,Ëx§/Z0�ˆ÷×Á�â™•ŸórúüBNN�’+®\©ñWæ9599ÕõÜmLÁÜ%] Ã0–ÉA–²È=KIØ)‡á¤9¸ÛÆ%HH2|3Á7s‘yL)•ÓâŒj*”<²Ó*†ÚpÒº1uLÕiûúbÑ;¹NÈú !,÷)ªk·òÞ ÎýùX×nja».¢S	«ew\Ö�TtÄ£À¯yéû_@÷Öç>’Íü;ÀŸ| Èø~ ;òºt3œÐI)í<é”÷§wJÁx‚µP¥ÅÔÐS]_BVƒ+˜3ÛæcÆ{6aÆ{6"8Çd²]ƒ�‹ÍXšAq wÄ¶ò0Õ³7#ô†œÌ©4¤ø³+mA˜3A�GxoµkÏa8ˆõm`8¾dCwKGÑ×jC�qw®~‘Í‰�kè�$¡ÐQËW   wç�š¬'€óNZ9î•QƒÇÑ~ËZ¼¶ÛÚ9„í½‰œm‘¤r9Gã`fÇšj$UƒñLP×'°ÆˆÇd	Õ@2Í¾óÓ-Ó=vx7º·>ÿ\…ÂÂ	_ ºöSÂro�˜#àH¦‘Nu-gULÒ+ðé ]¼EX~@×&ºö®ßÝµçÌÝÞÒämœ‰ëÎFÛº³!Íè GTÔxøL�A0 `ç‘Ü‡ØÕ8É#»  3è#cˆÞK”V Ôø+€}j"ÒaÄ‡ÁøjÍ:ø2×EuÕÑ”ãÉ@­‡Ï<{€iId1pž ÔèÇcÑ¼¹X¶xá¸‡ÏýÿRŸ“=åÈBTRúaùeTW·ÀûðV-˜ƒf¿0)åÀ1IC"=ˆ#¥šB_jŠo|¨ÑÎÝHôìGïþ]Hv½©ï Õ5•°OŠÅ|îD˜
¢ºv@Wæ�…§_Š¶Õ§Ï3–"H©:¶uæ´,WcC>€j*¤�ÎÌks—�\óÌ�„Ë|úM ½Æ7ë8x;Ž¨ëò–e.Ô¶ä4ÉÆd[ uf~Déá …@öpP»q\c�‡gÐr•ßO`2ç��[·ô=Ô;¸’#¢§‹¦ÙòTÿÄfÍD³_¨êÍtb¾gCN&=ô6½0r`†Ž@ªg_±6MÄ4Ýµ ô˜ðg!­·¸�pÂÌØ�íŸ|åç¯_¶ýžÆŽùg\ŽŽ÷_fx\"Sëá±jV=vÈD	²ù Âñà<�7ÐoB	2 @Èã ôyð¼ÇÃPe°./Æ
E30öw�CÒM[|…ãÁ*ï³ V¢YéñàåáÔ/)°«© #C‡¸´9à—ÂýKc'Çc¾G»ö!Ö¹‘î9�<mÐà•´�þ*a¸'žò‘w¦ƒo>�‘NDºÀRxðÌí÷ÜôÅ=�Þõ�%|í'_`ÔúÜÌ’ö¦^@¨m5{t5',˜:ðú(£ Öo¼ü©Ÿý-Ub(©ÌÊNµâf~±ÙƒÓÇ¢Ñ—-Œn µ¶ûj ¥}H9“Èd€c�™>äT18M*V•´‚D Y3†ž¹ñ‚pÚF!ûºBé+i‚g£Jæ;)1*+Â¼	J÷–}ž0ìNð®�£´é£S]�6¾ùQÓGÿ«Yöúmÿ~íîÇ~»ùø+¾‰™ËÖ`Ns S	–l€2ÜmþLsá—ûˆ´BÞ C�•	”Î4Ÿ“‰™8™(§,¥  £\-€bÜ�]‡e�ÁÀÌ €7¬ˆ÷`fÝ£3&Á|ßàMlÈ[„åÞ¢šrÔT(V3E¨Çùj"í"|,Ù{ð¦çþûcÿÕ°æœ¬ÛüuÌhnAwTï¯ƒ�¡¤ÀûB�‡S º>ÏáGìÐaÈIPÃú.Ç7q20Ù£�QÅ g¥=sŒ–³Ggúþq%¬¾D~C>$ÍèÒ
€°ÜAª«™lÀýÃ ©bó}øÐ®rýÔöa4ÄVh¾SóªMþ˜èd¤Á¹Co<qÒŸ_~ø§ë®¾yÅ’OEwT†X×ŠTÏ>³©%ËƒZÖoÚ,–w»Ì,SCŠƒÓÛ¼ŸŽÐ …¬QÈ™ü¡`Y<cŽL³”‚;=þKÕ)º†aÄ†ÐÓß�7ß~oíDb¨ŒCÂ]ÿËÙ§^ü3 ­ ¨¡í€H·92œGØ™ï‰®=¥Ë|…°ìÎcæûÑC•_X¿ióÚ~|Õ¿ŒìûÄõk>r  ;ŠLV +º¡%Uô,XÝ†'ïØ_æ�C Ž:…>�¦b‰\¡ù¯QM£�­85tˆÁ_~ûküäûßÅ‘Ã�6G"[ApãÜf$#Ã�÷ÿØ¼V  ·ƒê*ý‡1°óU§æ;Ž™ïcÇ†ëïõËÊRCN6ò¡Æ¹Ztp €0™t±èžWnê~àG/u|îæ¹‚Ëó~ƒ0ýZ*þfË;¯tMé™>îï<ôëÿëïÜûËs®úïu)…`8Pg* —Z2ŠTÏžF˜>þQU×1ÔuÑ×Æ‚^9Õ§ãNÝÏ ãÜËðËMçã¡_ý ÿ{÷o²¬qò}€^
ä×Ç H›sHg’Á> ;	Ë¿Fm7a¸éT×c’í­çe]`þêkA��ÃÔ•Û?²û•ž9ñðœ·†�<ÝrúÇstš¶,·�hÊß)ùØ¾_\Yuad<ÁÚºE«oY÷Éë/TDû»¡F�êÙ]×Nì~àG/•>‚™àm›ßÂup5Îï�-BrÞø€ýmq»DÔ‡J�C/…„$£¿ûú½ÉƒÛ°éÛÝ×/²hôU7>V4zyø]åó	¼<|£Ñ9ÃC~ Üò]ôôôP]×þ(½)ÿ}�)â¸Ô¸ @˜wŠ™ï‰sÌ|/�õ›6‹+6žCXîÃïüè“?4Tùõð[×2V_bzOÇ¡Öï†Ï-¢ÖE ¹ÍtÑ “"éŽ´üîß¯Ü²úóß^ÑÒZ�ž¨ŒDÜ¬Ù`8n!€…à…‹D^À¢«oß†ù+á]Øù?—<?¦“Îƒ‘Œ¼þÄÅÙúÌç~ïÏ×K5ÍP£faO*6þ˜$¨ºŽápjd ±=¯€åK'Mæ`Òrp;ÌÊõ¹û‰^?.¼ô2œpÆEøÙ¯ï";¾ã{ñCo·ôÚìýFU‹jNÿnþwÌ|¯§üð¹ý‡||¸ŒM¯ò³þéøÍ_5XqãâùsÑQëF¨¦õ¾Òi¯ú¼YøÃMßXñÅ:®�ÙAV3ÄUôô¢ �#zGb£
AW¾Ò~Á—Ùyÿ�?[­k¢ºzÃk�ÿ©ÿ¸Ó/þ©h€–ˆB=Š@$–€.'AuÒH?¼�3Kî?]¦bqŒóV`ÅÎy ¡¢yÍYh^s¶={Í¾gþÔ¾0Ôv™e¹OìÜ¥ ,¼úö	ÁW»�’�ØÑ\ãGëqg¡-uð{Ýo½‚3VÌv|L–e°ñ’Ïd„0Ã@--�6`žI¤T;õb×á>tö‡;øÖòõ›6‹��à�=óÖÔNøÑx­« hÆÆKj¥-04†œ*:ÉxºÂŽ ´ß�Mê��°ËOAÃòS.êŠ<ˆ'ï¸8¦ Æ�Å_¿w=U’ßbX.Ó6Fà8tÌiÇq³š3ùà‹>=îþ×Ü®Z²FÍÎ>:…bÐÌ¬�šÚœ%ë.ý<jNè'?5ÔÍ³8~^ŽŸ×†”ªãÛOßºîù§îz|Án>™! tå¢E×Üþlc{Ç—Ÿ½êäÊæRg¡ëá[~X°Öàu?GïÑÃÿÉÕä²½-¸¦IÀíð<JïuG#0Žn³~?¦ ÆˆŽÏÝ<—÷…¾Mtå"Âš·Qà8,]8Kf6´U8›>ÿïè�)HiÔqïE‘%¶‰ž�›g!rž�ó,”‘>\ÿË{On>nÙ²{»Á°Ü)ƒ]‡Þ\tí�?jòn(_Ìc��?üÄ/]{ç¢©¾ÿN‘ÝOÐiÒdŽ-¯àÌ(V+`¥ågƒ¥ša<ø«]ÖßÇÀ¡ö¾ó5qþš‹€Ò‚¯hÂ’†˜¬CuÕ¡;*çtê+’ª‘3<ÓÃ3¹9xx+ÏºëN6‡[|þ¬Õ88œUó•¾xô²ÅWýúº�?üÄ/ÆòÙ»nºâª×ßê~àGU¿¯Z2
e¸òP7ÔØ09C5Wî°‹�!ú!x|àÝ>x›çÀÝ8b¨ÉÑ±©ÃiPÜ4à *ñÿ‹Y ½±ÂÕß ìÖìÈÝ»N8ËP:>wó\+øÏ?ïYøåUXÐÞ‚•K”~ÆeÃ)É¼•¾\Ç5-Ë  Ža�37‚Ž §_ü1Èš1íÎ®uãóg­ÆÛ»ñÐëûÑ;«#ü|ÑWï>­ÉëûôX¬'ï±ºMA5»ëP]ƒ¡iÈVTW?¸ñ½¯Aé?P2“´Ø+¼7ˆ†ù+P·h5ZÖžUT!X}À/­�Š4`;8íÉÁZÔÿ?.ìjÄü-çýS}¡cÅúM›ÅŸ5Î_å›×¶�²ˆ¬¥+/»ù†Âò7šSkª‡…Wß~!a˜_njçò'ªkCíá·±þüÓrö‹ËfÛp©HµªSð¬ùZ2ÝMXÓ�¢3ðx–dLAž!™^"K rÄV1HÅ�a	^‘E£—Ï(‚%³[Ñ1£�½þžÞ~ ÁE}Ñð²Å_¿÷SÕ
æ‚ =-xù&xlÏ«xöž‚D³3ÛÑÖÚŠÚ:³Ò®¶Öü?TÓ€ðˆIäc÷þNÚ»›ª‰é~óYt¿ù,ÞþÃ�é‚Ó.&s?xÑE5³ZM…1UH°¡¥Ø	ÃÅMóßåÐJ,•#p$Rh„“òcÙ
àÍ‹¯ŸX8û”´ Ï¥ Ç1„Ì[úéóâ…‘ƒ pá„ÿÍôâ']{ç†ù
 xg,Äi7þns×_oÅÝƒS/»K hzãjÁŠŸI3LW *�­¿�A¡*ÈêðÊ3�…—gàØœÕ !ë8 ëðŠ,šýx†@äœ»fæÏnÇoŸz‰xla|×ó—˜ @¶ æ@Ô€®ÂÐÔL_ÀDçÛè~ä ¦ÀoØp
ÞwÚiX»v-<žÂ¦ n·��…Ã4UI&{ìÇ–-[pï}÷á‘‡&;ý-$YÆÒÍ7@ÑP]Õ5ÈiâÒß^¼˜qºä 8�ÿóÿÍÀ@¢�ó }]¯dÿ=­À›_?‡aù&ß¼¶…CÎ¦ Ç�ÒV†arªÙ}Eotã»OþÁ|={Ç8°~ÓfqðøÓïf.²¶uÌž…å3€µk±óÑß`û3AûÚ3mo¶…°¤!"é™Î¬fGá~a¥$³Z
¼ùYí¿ÞcPD$É<îŒ  ŸÈåÄ…²Ž}r
>u3÷`Q£×ž"n}àIüí�Ÿ¼³~Ófq¿?´ª|vŸs¤3Eû 4)#}`E/O TW¡Œ˜}gÌlÇÝwÝ	 ðz½¶Â_
¼KÄâÅ‹±xñb|ìcÃu×Ý€o}ëF1ëZT)‘éi�8¸� 5Ë‹o( ‘%ãöÿF<èù®Û”(€bæ»­ ;˜S¨©*þß]?´þüKþ Ž±`Ãõ÷‡úbá‚Lxïøã`Vóèè¬ùïû0¢‰¢Â–4%Ô‚fªA3ÂO)Ð55§WÎ=àxpÜè×ÄeB��+`Š‡S:†S:‚.A›ó@ÄUÄ$m!x† èbÁ| 9glð_VœZ¸öÎíºéŠ«Æ{ÿ «®€ü ›¥ÃoA¬ŸªkÐ£ÍÏÛZ[Çñ	…˜7ovæ÷„$CÖ(´ø";Í‰Ñ®šF„Ž;©èû§CÐí�ý/ÿ?8RHƒåûÿÀ$( §æûxæ‘þâ¥G°#ÖŒÎà×9g„?+¶Ÿ/ü²¦£sD‚Fpš–CJš�¾¸jÛ‹�*)š‚Þ(SLM7 é@JE¦G�›gpqhŒæ¯›V�V $�bß`
í5.¸�ö¾øný¿GN> {pÿë‡ à+Ko|øÈŽëÏþÞ¸nb„ånb®�Fú‰;ÜVô€°Ü¤tÿIÄcÐ’QJ
‘7Ì‰Ñ³Nþ`I÷«šñÂ)(Ñsðp¤Päûÿ@ÀxÌ÷ñ ‹Ñï?û€yhBn¯é¿~Óf1[øŽÃñË— ¥f´N*¡èèÌbX5j’>‘Ã@Bµ�ÛS%…”$çaÕê€›RM÷BÕuD5|Ú„Œ¤G—Õ{r­…Î	F2‚›ÿ0jBAXc0ïýŒøðw_õëøXÃ„9×­«Ûu�;lŽïÞ!ÔFJäLšªIV F0òÊC�FúÁ{ƒXpÆeEßã4ñf"Á1Î{ æçÿ[ˆ«¶ñí7?[ðy•ž`µÍ÷ñâ¦?Üžn+F¶:RiŸtåÏþˆ¤Ù÷Ò×(ºÃQLî-µüÌkYm°M¢JÍd«Yí±ÇCÍVX„åÍŸ<· 0Õ JéJé¨s³E ÝQ~‘ËiøZ[W�UBç¾w9Â*ƒ§· á…Ÿ/þú½;ª ÿ¯TS.T|÷Kð/^_v h5 F?¸/? XvÉU€'TtqÌÂpÚI˜#´h¢Ðþ¡BÿŸã¸ûíÂã%?m2Ì÷ñàí�nÜÖùŠu_oüÑµwþ0›ð;þ¸Ž„?©hè�šÇ'•HÀPŠ�Å¶VACŠ—<¯Q]>úÅ*0GO–7‡yºr­‰¡”Ž¨¬¡Ö# äbÁ0ª�Ca­!·ùÕ¥ù‹¯ùþË7,E2ÇËû@UéþŽÏÝ¼f¼¥Æé±èŸ¥¿Mtíá0.ßxéä3?¸½ÿœ kÎAÛ)•|Ït  �šÿ¥Â{m€¢²Û×ö(ë7m±rMÊï÷çÜ‘©¿=¹øÉw§OŒy.]Í8ft|îæ¹T•¿‚ôj»xþÜŸ?¡èe… 4M«ëH&ì…ÚÐTr
Z"Àâ±Â´R@2Š¸ïòBGK]Uƒ /m¶„Œ5‘tø<c® Jàô5è¼÷YôŽÄêx�ï6 ÆýeQú;Ò
J¿?ôxoù&¡ã�îG×Ÿn¢º¦’À¼ã±æ×—}�Ç!ù6Q¨ÄüQ ±ôTé|Dø‹Ýþ¶êæù'ï�å—v½¬©Ó·�ãÛÝx¸k‡yÿ/ã=Þ¾_\¹øùßÿA—Sh®ñcQûh²ˆ¬åúüò…0GiÅ†í«Î´DÊpOFø�Ñ™xºœ2Ó`Gz!�4p©ž}HõìƒÜÊp7”án³A§Ï1¡	ËA’Ä"#‚,ç*ª¤jàÐH
IE‡7½Â‰ª9óâ†j¦{¬‡gð‰3×š÷–åNY|Õ¯?W•/�Ò›Ëß h—©jAMD k*	,\‹“¿ú0e­Ù[oj—¸j˜ÿ†ŸSút±ÌÍ¢ö†aÐ{£/ïÂtEfõþb¨òÕ8æà¶g¯9ø›¢‹fŒŽ¢Öt�#Î…ß2ë³+Ït9y¨;Gð¤ãá}Hõìƒ2Ü52 ]J€jrÎ Mª«0ä¤yüØ”‘^È‡ ÷‚)8¦¥²yÕ H¨Hd…ó•@oLÉÓì¨sãkMo�æªj}oTWo !�9‹bB1çÔáä«RVø�é‘ÇŸý·k ÐåL)ú#Åö/zÕüÌº?		±·Lõ})@,£™ÕŸÿg<ÇZzãÃ_�ñÁkž­YyÚ‹T×~#u¥îü×Ï`¤ß¬yïŽ)�‘Ë	¿=]ƒ®FrÂ^†œ„2Ü©¿³@)ä£ÔD]ª«Ð“QÈ��‡ºs&ìR]ÅÐÐPÆá
ž%èŽÊIŽ†I{bJNžBvùèŽŸƒ€Áîª*!ÁÑ“£¿!7Võ˜yðÍ:íÿttÎYk¯©V •$ÿ”2ÿíÊ5M¹§Ø±Šª’•÷Ýxàµ÷~i‹Ò5´6é÷À¿j>ôÁ‰Ý8Åo>•¶ÕÈÖñ¬þ§üð¹ƒ]‡¾˜w<óŽÇêÍÿ‚V—Ž·¶mÅsÿ«ÏßŒ„šû¥¨:u$üTS¡FMW ›é×Ñ¡743UÕPe³`ÅÐ3
ƒ°/‚0,Ã€°¼Iþq"˜¬H€¡¤ t‚óÀzC`8ó3“‰8MAcý¨UÓWÁ0‚.:%èŽ*˜2¹ƒ”ªc ¡¢ÁËcçlÿMZ¿æÌŽÏþø�jõ$™È\ †Í„+™‚g(Ü<)ÿŸjÿßïpõ9RÔügÐ6ûïé§¾¸¶³ØñJ:ºnÜÄ±ì½©]fh}:(MUqÛó�XW÷m8›šd‹þÎ}?f²jùç´ÏÏ2XÛ2IEÇ¡°žÑàFoÓH<÷&—þl?�j*”<K€ê*ôTºœÃ	04J¸z"’öóMmÎºý ËCÖƒ4‚õ˜‘	†ãÁÀq£ -…–ŒšÛ½fM…rGQ
dªÝº£2\�È±PušzÀä†û{ñûïü3®úô'°Ÿm¼uk*ƒâ .žÒ`°`ÀÍ³ikhTˆ¦ƒÿt;Ë	)•ü³«ß&âDéÿ–:^Ip�7þà�R�!Äm)�º3V#õv'¦
�Ü‰A% }ë7^ñÇ±Æý^}û…Ù™~sÚ3É3†1ZH¡I‡‡g��äœ|ª©Ž„_�t£Ñâ#Ð’³HeàpÙRØlðÞ ÄúpÏ\ÂòÐapÞPFà¤ÏA†Pk¦ÚJª�Áp-u£ì{çˆ„9µnp,“‰¸x<KP×Ü‚ïÝv àPï¶Þý,‚‹:>wóÜjXÔÐã=ÆX�Ru¤€«Àiã�‰B@tî~]öçÚ•Ø–jÆÈ¡C)u¼’
àù'ï�S'é!�nÆÆS»c@ÓeïCì™©Äûçg¬hùÍXãþë7m�ÿ²þöúühªmGSr›m f^ùp8šIÄ -YÈdç¿–C‰š#¤YÑC•¡FÍ•]ï qp;Õ55³üÔ^,ô6 èñ¡‰5É«>=…Îè zå•ÔDj"+œæ[¸¾ÙË Kqð¾˜ô N]NA�A¨1{½Jª�þH�Aóu�šM#[¦ù?”Ô0¯^ Ï$U)E‡[`1«¹óZë±·{¼7ðe W�ÿ›$# Å@$>þC�ÙVAƒ—‡fÐ)³j®þ>�)šû¿³¿Ðü×ÁÜ]®oCÙ¸C ­é&­³?“A‘ÚuÝ?­Ÿ?É·MºKpî†3�g€¿öïù£1ÆnÅCË7|<ÝE °pVK¦®Þ0Äeäg=Xí¸­Œ=«Ä4j¤¸ð[¯ër4Cø­ç­P©¼8·y	6úf¡Ã[Ø_bƒÓ¼‹è2:µ(^9Œ�©!ª&"däõÇß½5'œÚ8¬è†PÛ
ÂrÐ¥8”‘ÞŒH¤d„9!¯Ù#,i¨õp¹8ƒ”ªƒO»EYÏ¦\¸j¾Ó= —­ß´ùkãMºb8>f¨2”¬Â D"�††ÛýS)gýü*…•ü“Ttð,K&· ò¯Æm/®Šf`g¡A“”»Ë³¬X~Ïu/½vâ—Bæd>°k‡þë·˜õ��˜4‰ÖÀÙó—ãìùËaÆŸÜÙuà—+ï»±¢P%ÌU–x{}~£fqXÒ¡¹Â/ËfUYæýº
52 Â‰kÀ�â0äQL—S9Â¯'cÐ£ý�#ƒˆîÞÀœ¬tEëJòå§‚¯±ÏŒSGâ@bô8AVÄ2¶ËšÑeòŠÜ‡GûÌ¦ýÏÜƒÀÂµ-ß˜^ùÍÎ8ùJ`$–‚Àsð\ƒ¤¢ga„SZ†e§4Ôyxð,ÁÊ³!<ºŠ¦Õ¬Øxž¼£äxð²ß¥� Ð7<1ù N7?ú]«:…
@Ò4¸ÒÍV&Ú*:ú˜±ÿbá¿½CR&|k�€îú«/w\GªÇ ôæüm4)ãàu·!ùÖ!4|ìt°õcŸÚ20SÃ0Ì×k–tì?øo·?¹ÿºÛ6o¸þþP¹÷-¼úö³Wÿö–†œÕ8YFQ¥\íj•³RM6_ŠCÍvª«PÂ}™¿õdòp’·e„©¿�~ä›äºË>‡Ú“–Á¿düKæÀ=Üóg€õ›fzg‚œîZÔó¹µòAVÄû<í¸®ýR/x ÑÝ[0ô÷ÿK}Ö9Hñ—%žRàX¸8’F—MkF5(¢Y³!­ßy–à½‹Í~ú„aNÁ8qÒ)îg9žª‰"‘ÑóÒ´É"ZŒT“4Š¤bYiÆ8˜æà8šú 5žâáÌ7º
S*RŠ~‹“ã:R _=ã}¿¦”ÚÚ`ý¿{ûòGº|#ü–MÈ�*B˜�,ËÞ~'¢#ÿíößï¿î¶Íë7m¶ÿ’8¸í*iàt9�ãr|§«~l^�ešN €5ÌCJ yp+â»· Ño©o_�¾ô-ÌŸ3Ÿëÿq<Žçá™ÙÖïA½èÍ¼VL Àˆ¡PÀ´*]{0øÜïM¡�åº  À»Ü0rVÐpjô÷xVÇ¡ìîÅkÚ3–Rñ’:‡xþÉ;d]Sû³½—¨RùUÝjæ¤$œgHÙì;U7ATÒ hÎ:9;EÈ¡ð•“J÷¡_:9®#ðÌ�„S*öºòÒnrðÓ?_ëGÝÕçÃ½¤½ª7ªÂ\Â²ìíw�´±ÇR–e@8af|ß'Ç¼‰è®—�ŒãpO?Âá¨ºýêŸHå>tÙÍ, 3†o1ü†¦A‰@KFAUJï>Dß|,3`µ^ðâçù*nøø—
ßRg?Ü¼˜£ Sd+�Wä>¤‰Ä}º®Ÿ –èÚƒ‘7‡.%`(æù1CIf|Î¡,¡O*æ| éÒâtßÂ,2táÓ`¦®ãs7Ïÿ—Å¼ {÷ÌlR”*MV¥²»d›ÿN i¦uT«€cœ‡þjÜlQòïÍîBÕ	ù—9§'hkºI=Ôw)Rú§taÏ~ŒÆoBã§Î„oÍBÿeË”å¤Ë“/aY\r'¢ ÿvûSGžzmèŠ�§Ð·s/À»àn��={÷‚óÕ€j‡ Kq¸\nPo\¢ †rÒiíV=9ê¨ñ0�oAêÕèÉ¤‘~°Oÿß¦‹Èæ•i~�•Øú boìMšŸåæEÔ”G]‘ g’xû¤<Ú·› „åþ‡êÚ_­Ê»èî-p·-€—á˜äš–ˆBÔ�cMÂOÒŒL\B5LÇÇ³ÉÀ¨dò‡æ?zGbàÜ¾•ït`J÷8ùà¡} Ì6æ¥\ E–Á»JÏõM+EI”îj=c ªê£�¬&­•¢’Õßjã–�˜¤a÷@¡a‰F¿ïôØŽÏbù=×½ôÚ{¿´ÀÚRûõÿîI„ŸÝŠYßü(Ú®<ñ­ûynÇ”'Âlœ¹i5žÂjÄb1ú\ïr¤-…WºßÆ®#lFÐeO „‘Ý¦Y¯É™¬;=Ý_Hççk*”Þ}P%“üÓ"ýÐ¢Ðj"‚¥þfœµé"l^¹~¿pPPÉÖ ÷ÁÊ»°PL	d¯þ'møè]Ï?y‡™jr@7GÞxÞöã2¹€)\Àü=&ë£
@Ñ3¦¦¤ê20›`Z4£½#1‹‚í ÀÞ={3›ò™²‘�%xQšk²\ CNÒR÷Ûì°<þô_I£�4<KÀ³ÎIC§¹¥Vÿ×m|út%ÓŸ*jbe–ÛÏ²êN?uWŸ�¶ÓG €ßï'gûÍÆ�Ÿi7³ß	à`x;jÂØçp¨'ŠÁtcI«�•<�+”z|z2]1Ú¦Ú –{qÜªEØ´è,iÈô»sôT°õèƒQDž¶ÿþ\œ d)€ˆ&å¬þÙ¡9Æ¸ÆHF.”FúýÉ#»áïX	¤€–Œi•´L`v³ì¶æ	EG]zŸÕ´“0mãÿ&È[ ÅÖwF‰R¡@'L¡k*¡J
D°/
T°;A%VAÈÍ9ý[ýÍÀŽ¾ÂÌ¿hJþ¯rÇÌFEwaÍ–ŸÞ÷Ú{¿´…€¬u²ÿÐã¯bø…íhûô9h¸d|+:¦•"ÈÜžÇ’†V,ihÅÙ P Ð–®AJ’èa)F à`xvÁ{çs>¨>„ùõÅà^ÒŽÔÛ�züÕ¢ûBP/z3VÀ-=¯¦W²5¿ª9Î�Ü
Ðkâ{^†oöqk†j*4Ýl\¢ªAÁ3ªN¡éK
™Zhò¥CkÄ8Á¸ý/i7éÀ�˜3ÇŒ4kšVÐñp–PmL¥EÁ×ÛO¹«« ²QÊ*àçU¥ÞèŠÛ…þÞpúËFÅwÁ©`�&eùÑÑÿ‡gÐúùóP³q%|+:�:Ø‹øË»§4­¸ìÍáyøyž,1Í÷ì½ªðoX†‘'^/ºòÛáõx^9–ã)%ÌíâÞ/üÑPåkR]ïPCÓˆ.§Àq¦Mäà1ýeYÕÁ§ÙpƒZÎ²6JÖÖ¥‹Š(œÍã*SIa€ŽW_ß–Q Š¢Ø* 'ð`9žêšJñBõ…ûDfRºÿØY!—óÕ?»}[6ÍÀ›Ý6ÕÔ”:öý-Tì­ÙòÓû(¥×+]C8xÝmØõÉïaä©7ážÝŒ†K6 åë—À¿aÙ¤çL°õø7,CßÝO8~' ¢Iøé³}¿®iw«ˆ|ïÉ—¾j	ƒÈ!4³C›ŠjRý<K QóF}ÿì…¦IHƒaêQ�çàÕ—_Ìl)E:	¶´´ PFzm_+ù7HéIÐÇ@ÓËÏ(,µúoïML’" ûüÄŠ²™ù“šÕãk•XÙHí:Œƒ×Ý†¾E3ÑtÙûP³qeæçh°
ªÿ†eÐ"8ô_¿Í°ýNqKÏ«™‚¨´¯o»_šØ C‹‡˜µ
òP7æ-^Šµµ<êjB¨­«g²gâ©:ÅðÐ ¤p±á.ŠA\Ü�9óTý>–}žêÚæ×wìÌl+E:‰´µ¶âÈáN(‘Á‚×Ü<;eµÿ!gŽ‚×œév³J­þ¯¶qŸ)-ßóÌcR k¶üô¾üôàJa)‚î¶:4^´¡óÖÁ=»îÙÍP%òî#Hí>ò®SþËÀ×úqäÇ*`ú�àÁ¾·3¦¿®ëK›Ñ%@â EbÿÆâ´s.À	ûbÉ!˜<KÐÔØ Ža°pÎL,ð×ÇÇï~qR‹ ëM |i!tÂ<
˜-¼¶¼±kW-C"‘@MM�­à$0oþ<lÙòä¡®‚×êÜSSùçâH¦æŸ!Zšo�j€cIN/ÀRÌÿ]ñª­þÀ8æŒÇ
È†Ò5„#?ú#ºn}µ'-CíßßŠð+:à[Ñ‘£”þð´"+�Ã2¸æ4càöÇK}¥°#Ü�ÿì|– º¦ý  ¡RËñô}ç^HÎýÈ'?©æôœ,³”cˆ�‡7«[ýÎ=óýàÎ9o<z¾ækðÎ[]•IÌæ² +ž~ü¬]ef“&“É£Ÿ'DàìY  yðHÎv'™…z/¦H�¦SD4gr�"NBÑñj—­ï?¦Õg£ß×Þû¥—œF*�{ÑLÔ�±ž3VÁÌ}R{!è…6™öÖ�{I;Üg€zÇ%ø p8>ŒOîú“iúæ¹õ/?½”ð1ž`­‘ŠÝÏ{ü'ã‡ÿ‹ùïY Iéè�)I*Kz&P~‘Å{fø13(¢ÁË££Î…žƒ{ñþ3ÎÄ‘Ã‡·Ž?w¼ÃXËß@uõzÞÄã= ÀœX,ØÚÔ\Ò¸ýöÛññ�ÞÆ™Xqý™íÍ~¡¢œjÁ#°hõ;ãjÜ,¿ïv¼ü‡_¢­µüŒÅXÑšæ"én@SÆ_ŸihB@ßxðãË�ó½Ï…m»ô?×©‡ú^$8ÀR¾5ážÝ\ðz¶B˜‚%ôdv”×÷£û/§ÊK»ÇuƒRªŒËvÜ‹© ØÇx‚kJ™þ„fRMyŠåø¹_»ëI2Þ<
Ëè‘ i¹”µò[D  ¼Ù:ØRsjÝàY�!XÕæÃ™k±oß>,]ý^H#ý}„åO§º:æ’PÂòË¨®n€o~ózœ~º™8cÆ[7 >TS°0dã™gžÁ©§ž
 8ñ/€nðAGÝØ[°�í5.¢;zŽÛ^zßûæÕ™ú‘,Ä@˜7Vmêš;žêxÿe/T’ø“�qKîó'é>�N.ïqœ@h«Cè”ð7¾Û®ª$Cë�I µÛ4'J1°õ�¡ŒÀ¢ˆø‹;{f;†_Ø^1¹g‡”*ã³»°f–6Ó,¿s@W|øß~Š5ï;Û{Á×t‰Dƒ±$Y)èT”s}¢.QÀq³ZÐÁ³³B"Ö´Ðêçñìã�àüóÏ(%Ø cíÚuøö·Í>„µµµ¶n  Ìš5«è�úûûÑÔdF)WþË=ðÌX0e«ƒ—wTòkPŠ¶ ˜“ êÃÿüËW°ëÅ¿ [O`Ó8ïqÆ­ Þ¼øú9ú‘á·!“®^Ý‹fÂ¿j><ÇÍ‚kN³­…��D$
f$	=b&Ò¨Ã¦?¥¯IçÌX9_kæ°A/¸æð.1‡ŸèßòU·$ÕzyÂcxñ¬²MP	¹”~oî¢¥øÄÍÁÛé>q’f «o ‰heõ÷Þ@l:_ Ù/à’å�XÔhfÖ{x|ú#Ä#?€| ×Žãr÷è €Ûn»sæÌ—0³}ŽîÄœËnDûIçMÉêïâZbQß?nžA{�«`»ªSüúGßÅc·} ä#fšwõ0n•¸ò¾¼ºîÊ›!×UóÄœ µëp“N<"\í�f)m} žãf�¯õƒÌn€70ÍÆ`åù–â�ôf"ÉÃ}ù,~Uý Ãña\µç!Ëìw&ü @éç`åY—f„?�£³·ßv"¯�2«5Çƒp"ø@„Ú–Œð GÌ¹s�A{È¯@ð¹Ï~6­ è§×oÚü�1‘‚„|”vX>ñ·¿áÓŸú‰¼^/<žÂòçráÀeË–âÈáNÄnGÓªæWã¥ˆ¿|XmÙòÁ³â)W`¡þæ†_(‘�çÇË¹d£*6ÑUžØþ(øèxÂ‚Õ MÊJ!Ä#‚¯ñ�õ{ ‡<Ôír|;)I¢l8I @�%¡ta²±#Ü�kö>2ëçÅ9~Æ¬5’‘ ÐÚO `
ÿ¡®îÌ>Z|áíÏ"¾çe¤ºÞÉéG˜–ã©wö2â[¸'¼ï<,oY ØÖGÈÅ!äöbÝºuÖîþžùí qéHÂdo»÷OÆ%_Œ`0ˆD"a« Ã#%y€ãW­Æ#?ŒÔá�SÂü×z8¸V	Ö¸Ù¢aÙ—;£Jª¨[ø¸®þu óß¹wäÍ¿�X­ó¬ÚªõòÚ/^\�°à?2(¥x°ïmügç³–`î#,ÿA§[ZÀ‡~ùwˆ¾öuõeÌþðögÐó—›K
}1°OO»ü*rî§®B�›E�‡Ç™ÍÊ;Á‚šˆ€°üòŠÉÀ´ËÂr<õ/YOÂÛž |ô²ËðéO}
@q2°”�M^vûË<“—iÊ³3ƒÎLÿlâ/QIÃí¯öæÔe0††¡®ƒ'Wcj30†TàbX³å§÷QÐ-Õ:Þ?RªŒí|
7îÿ›YàC˜çOpM%•&ˆú @M×ÃKòhƒ�¾Ç�±?`V×=vÛwá˜ô(ró¡T%†œ¤,ÇS"zº*9&á„™VÛÛq<q7ÏÍ½÷OÎ´
±}ÿH	>ã„•«Àr<€C»ßÏWS1š|ÎLƒR´Š‡ŸÜ.(Ê’5úDµ„¨¢  ¶­öÃÅZ‡Cq<=°ço½wíH?´äûë7^~úØ_ò 8²ÍÌ­¯Œvª;ñÂq�çú}’jöÉ³âÚ�<ö(tM%º¦>TéùRMù ?ïÂ3s	XO ÁåfPMDpï}æ\‚D"a[P*)Èà„÷¼‡ Ààë��ëº+A%¦�‡+:xwûòÆ|3†7Q¾ZÍó­j^ä/Þ~&ü™k\„�q7�üGÀáø0¾Ûó"n>ø’f¡N¥¸ ?ë<°uLèOðUªÊÜó¦oÖÚ÷#P×ˆ¤ªC•exf.Bpñ‰`ˆ’ÈX	¥àªiDÍ	gáÄOþN¿ð2ÈC€%M^ˆj
gŸ{.¢ÑHŒ°üGAg“M “øþ êV½œ¯B¨| É[a¨2ví;„ó?p6\.t]‡×ë-8OEÜ€ƒ�à¹çžA*ÆÒ³¯˜ðïÓÅ4x8É‹áX‚™!—ík)UÇß*h;æáÙ¯=uåÚñ5aÉCÕ3xÖoÚ,þH
<3‚ï¤T¿ê{wv¿™å�“;Ò…=ãŽó2¼x’¡Ê�xgúOû—[áªŸ�Á‘0†Ã¹CJÓíÂ•È t9	ªk ,Vô€ó†àné@pF†œÇŒ€€—5b^�ýÃaÜø™Kðêk¯Q]×>ZIx*�¨ô
€&ß¬ãà_¸|°lzŠolß|ñ~ ÀYgŸ�¯ýó? m	Áb9¯¾ú*V¯^ 8ïE}Ûì	üV�'ü”bv­«èêÿÐÎ¡‚)¿²$mw?tËêñ¦^çcBRøÒ‚ONEnÀt†%øî}Ûbø�­/|±Z#Î-¤•ÀÿñÞ`Ó²K®Â¼MæH¿Á‘0²ê8À¢±¾^-«gø°ûñ{ðïÿý$úÇÒýÿúM›ÅçŸºëqPãdÞDÝ‰‚õø!F»3‹í8xû×2£ÒÊå”Ê´ò–]z-V_øéjÞâ´Dxygu½�/ÚégwÙ•ybŒ.¯OÆ_1LHiÔÏw<yä˜+0ŠÃñaÜÙÿ&®Úó}}ä0±Ì}ÂrW¯ßxù—í{­ê3Ø©¡&œð{]NÎïyó™_ø3tMEãìEhnl@]m‚¿á÷z‰èáõzr~êkBh¨¯5ê‚~²¬ÙA�ã�§þ·~ó³øã½÷@MÆŸ#,÷!úß*9¯ÎÛ¾ÐK ö„³ÀyƒkF{‹˜–€¬¿Ñ]fÏƒí»÷áüœU5§‹b®ÉŸ”$„B!ÛÏÛà0^yù%$F±ôÌ�LÈ÷[ëátjtóLÑ˜TÒðÀÛ“cú[˜°$þtW ¥ÊØ>Œÿ4Ëw³°„ü|ýÆ+n®¶9Wikàë,ÇŸ£k*ñÍ:3­€»u>¼ÍsPÓØ*x!ð,UQHDÃˆöv"¾ÿMÈ‡¶aÿ®V§�‡@˜Ÿ‚*ó`&üü ×Â;k)„Ú–Ì(sÖåÍL/€Î{ÿñCo ¾ø¥«qñ‡Î`,fd»§Ýxf-^YÕ{[I¶ Ì­s�ùÿþÍ>‰æ¶EWõ	ñÏ?ûÀD=+Úi*Ó„§–Ð?‘ìÄ£}»óãíû@È¿¦'OŠàçƒñk©œ¸�êúz€®G:õ¶ú ¼BXîæÑ±f¡Y¼ ¿·m>ü‹×ƒÔ‚ËŠÏ‹íe ˜3öüä3T×TÂr<ýÕ­¿$¥\�b\@ÇâeØ¿kæœú!lüÂ·ªv?+‰÷—óû_îŒâ¹ƒ¹n†¨«žúâÚ	+{�ðÆh/¯ýâÅ,ÃÜ;‘ƒSJ)ÞŠô`Ÿ4‚'Föá¥XW‰$²uý¦+ÖN•ðÛÁTÉ6jèm ­!ë5Çv“†ãcà];«AJ¦«ýÐäªiDè=çøýBmk† ÆåƒPÓŒÞÇoC÷#æ”«¹‹–â×¿0§ÔÙ
¹Ýn46ö*ýÎoÆ×®þXŽ§þÕ¤ZIA3‚‚ã�_)¿¿;ªàwoölWRÉ+^ýêú»ªr²E0)R9™ƒJ)Ž$F°/5Œíò Þ‰”øB–¿‘êêS}-“‰láç½AÔ®=¬ÛŸã÷sÞø,e@8BM3Çƒj*ÞùÑ'�èÚ 7CÐ.*`—˜ˆDQÓÖ5©X	éW¬Ð0C~w¿Þ‹¨œ7Ž>½së×OÝ<A_Ëè½žè Ò|@*°sªkŠ�R
IS0('Ñ$ªÉÌîwbè–"èTãE…�åxÊˆžÌké±ß™î´ÙûQÂœ\mÆº"_økN8œ'>ØPÔïLk€G[êÙ‹Ý?øxæ^~û?ÉtÊçŠYW~ùüô'?€«¦ùÙ ;öÆ NK|�Ò©¾€½ß?Q!?;Lš]>>à�Þ·ðoUí\éñ^1]Áˆ¡T´Š[Â.x ïáÐTzV[n]‘Á
bæw5ÉV}„VW³¢k:Â
C¢„ðN„«aFÎûø`CN¢…ÁïGç}fŸ WM#~û­ƒ¶|€!xèÐ!tÌ›OuM%Çüz¬<ëÃcº®Z‡Z‡3ý€Ò¤ßSûÂÓ}0Ìª‰Mò³Ã¤:æcåžØ‹kö<<)çÈr<åý5„åÅŒ€3.ÑÞå�*%‘ß…×PePMÉì]…2t,/‚¸aÅµ'ª¹Ã´�Éöß‚tšo1ájšrH?Ëï/†wÿFÒi½s-Å¾ý­¢JÀÎÈ¶.úÁ_*.
¸8Ô{8ÇŒ{�X”ôÛÙ—ÀÃ»¿þÉðû³1éÌÜ«ë®ü†a*êp8>Œó·¥›žòU Ý•¼ Ã-ôÔ5gš'zf/‡Øº /‚ÝP¢C ÂŒúuz*%œKÎ]…<Ü°|F)P–‡–ˆÀ�“ö®«ÐS1NÈ„µÞ­J€°üÁ¿éšJ\5�®:¬è)+ü„!6Ì,ylCNbÏÍŸÍð¥ºÙ¹ÙVÀÂó>‹“.»ÚñuyÍùäw÷ÉFLÆ=[

}¤dòÛ¯ÿ¿õ_Ÿð/)“Þ#ù—G^~êòÙk–ò”,qúž ïÂûß2óå	óPz?€•ü0nÿ6WýŒ¯1¢Œèë«E`ápÞ X—´`¦<a9ªd¦ÇºüàüµàCM¦EÀ`À˜·�eYP |°|°œ¯†ª€ê*Äš&ðõ3`(x_Jd  š©ªœI8á!úÑÙê8Œ'XKUù^Pã3Ô0ˆ·m>‚ËßÖå¨/+üBM3Súq$�Àâ÷bäµG`¨2ººŽ ¯¿ëO:	©T*'IHÓ4(Š’S?
… (:yî¹gÞ·�ÎXw6ñ¤g$–‚‹#hñŽ…¿Ñ/ ¦ˆ›•4Ü³qÝ2ª  ÍIDATmrÞpUQŸxíÚ÷N8é—�)i’þÎ¼Åž©‰§�Nö'„àåÈ–Â ,·ÔxºÒÏ¤ªœò´/9“*Ò Ðå$¼íÇ™«>Ë›Âlè`Ý°¢¼7Þ_Þ_Fp�uûÀð"Ë�áEh‰0 €\àýuêZÁyC™é;„å!Öµ�uû–a0‚†ª@¬i‚¤ÔÐ›aèaxñEjèG/'@˜3©*=àxÀLòñu¼¬Ç1Ô�ì’ÂÏ9ó«ëjC˜½ò$t>ÿ ¥†AöîÙUÓðžã�G*•‚ àÓ35M+(Zûžãqço~‹Hx„Äú:1ïäsK~^¥‰>µ>{‚1¥êøÃöADåÜÊFY’¶{#}gzæi2¿6`Š@ç�­úÖ�õ7#š:�Rãä=ª®á™‘és¦·�åsëŽ¿H(=‡õÕ‚uûánš±~&X—¬ÛÂ°`8Œ ‚p¼ù· BKäúüœÇÎ—VŒà6÷åE€R°ž „PXÑÖåƒ.% j€°œièø@=Ñ"ý”†�a˜�SJã ^ËuMÌU_ù9@¿ÀÇ{ƒ¨[õ>¸šç‚÷×€÷�~µvÂ bÝÇÂrsy]pÕ4"Ð¾˜ô¼ú8¥†A¶oÛ–Q‰D"G	$%	^—l:R ¸DÌëèÀ=÷ÜƒXï!_šç/³ý¼J…ßïâÐ\$ÍWÕ)îß1€¾¸š³� ÃŽnxúŸß×çä3ª�©“³tøÊõçmÑÃñ�BÊ>^p¸§oXŽk'.ÿO¨*WÜw ÑµwŸ»¥ãkÖß†ªÀÝ2šG@a7d“=`8oZ°Ý~ðþÚŒ`›û¸!àjœ:¨an'†M€åÁ
n°./¨¡�óÕCŠC““ÀûA˜ÓË½ZQYí`ý¦ÍbçÁmWQUºÀ 0³ûÞÆ€PÓÎí½./ÄºÖ_l˜U±ð[ð·ÌF }19ò’Ioß¶-ãä+�X"žS+°páBìÝ·Û·mCÿŽ¿Ûº•
¿›g0#T¼Gá_wcÿHîo1þÏ|ù½ïLÂ×f‹)S €Y4ô©¶ÕïB>PN	X<@B•Õ”­0}ûŠ@U9åmœ±œ° ôTžÖy œ`šö‚Ô$Ó®€¹Ò‡@½àae]^0o6Ïtû3¯3‚zj4¼CXœ' šV.„aÁºü`¨ËñPcÃ è,–e?GH×õO«æ*ë7mw¾õñÎýoüÀ¥ DÞD`ñ{án›Îí¨3]¥´°sÞ„P.G8qL+>ü-³œ},K`ïž=Ø¹sN?ý}H$9œ@>°~ýzÜóû{	��#;¶`Á) ›wæXÇ>?P:Ñ þ¶g;úr	bÆÐÀ3ôüg¾tâß'÷[ÌÅ”*  ¸µë•·ÓJàâRáABv«aì‰õÃœw‡ûÇòy5«No`=Ás8_x-8_Ü-æ*/z@X L:"0z{(¨®eÌzÖãç«ÃrÝvM÷f” Ãñ`]>pî,w€!ýy,!Ô„,kàDªÊ_ H=á„�SM2ž`-Õ”/txó6Pc3€Z–ã©îJâ_´œ¯| œ7dÞ3j¤c+8on¨­RŸ¿˜ð[°,K	tuÁ³/½ŠSO>	”ÒŒÈ'½^/Þsüñ¸ëîßP92@”¾ý˜yâ9±ý@yáî@¯uÅ¶Ë’tÅß¿râŸ&û»ÌÇ”+ ÀTŸž±&F€3J)FÕñèð hY¿ióOÆÒ5gÅÌ¥ÛÂ.ï'YÑã',=ƒ»iNFØÞ]2¿0Âr`Ñl�á«ëò˜ÖÇgÂ…Œà‚¡HWÀ‚É' º
Â‰émX—º"†BÞ•q	øP8—z"L©aˆ Ndæ*jÐ9/L6QÈðâIÔ0þƒªÒ- ÎEZð½3“Àqë	l ç	B5æøö¬è.ÈíÌ8¿X×V–í·PNø-ø[f£eùzÒ³õYhR#ƒý¸ÿÑ'1á"Ô…PU^¯·@	Ìž=®@yâÑG0xä ¼F+OÚT5áñP/uên/G¾öü—×þ|¢¿?'˜
  n=òò‹å”@“àÅýÛ(5ß‘#»þ:�è<°Ut¬L1‚ûÀôÑ9_êjCx„<"·*gZïÊ‚„°¶Ã‹¶9 „ãÁºý0²,yÂpà¼A€¦âÀ¨K`YbC;á\^PM±,‚•ÔÐ?àó ia˜õ›®8<Ö¶aÅ`úöÛO7?wRCÿ2€• ÄÑÿD"Ô¶€sû!„ÁºrÛt	Ázð�Ñ¤KØYW .øÝÎÓu]5�hYszw¼56MJà‰G�ªiX¶lTU…(Š0#G	œ´nM†èÜù&8–ÇìåkÊ~^9á¹3ŠÙÿãŸ]õ�j~oãÁ´Q €©>3cà;%À³:µÙë5ctŽÿÈ¿î‘óõŒÙ
Šy³Ûá9¸x^‘Ç`¼PÐ	Ã‚ ™Pßèv„1sr¶§-Ftƒjr†`òy.Ã»Ày‚E Ô¶@ð×‚Pš”  €zYç�­_aÎ°„Ô–'ë7^qªO°– Çƒ�3@éGA˜ëº¿õKjè—™Ÿ xgÂÛñø¬&\ |ÚÜ·¢&£×ã†X—›ÃŸë
�œ Sø=Bå½ü�3ß{ÂýÝH¦“…¶oÛ†g_z‹ÌË²`,Ëæ(�sÏ>¯¾ñ:öîÙƒ}oþl%àwq%	?»Ò^ÀLôyáËko¬øÂ&Ó²F÷ÕuWþ!ä:;%�•[¿isÃX&]{ç©*Åú{Öœ9hjiË¼NièIØ¾WOF`hjÁvCNeòË„¼Ôè�í,>5: -ÎÙFºƒ–Œ‚j*9irÑ¨±‘Rõ} é!QP#¯Àœ	‚­ øíÞÌr<uÕ6¶¦bÃL–3£n?XÑÂæ¡¢œ7”+øY¯åö”‚‡gPã+ž;_	>u¶Þõ_™ûÄr<½ôÒ"—\|1Z[[QSSŽã2)Ã‰H§�q¶l1»½ÿãÿ§|ô‹9î€A)ê<ýÅ…ÿÅCüÝfåŸŠ,?'˜–
 (®(¥8ãµ_§Çd�}VZÇçnžË	â¾Ì�`9¿z-Ø¬¦ŽbHªFÁ{©¦BKFl�kÈ)èR¼@ø-hÉ(4›n¼T“¡ÆÃÐ“…¯šb†³,92CŠƒ¦¢PÑLb¥à½¦Ïºý`õàýµéûÁ›.�è1óòPJð­|«Ùaù²¤ŸS¿Ä‡zñÒ/¾‰èîÑq®šF|ò²�ââ�ŸI¶Š‡úûûqÞyçg”Àâ3>‚‹¯ùO¸86“áW¬¦°/î¦¯ðÓX À+ë®¼†!ä¦|%ð“#/â¶ÎW Â<jŒ¹ï`¾ÐÖÜˆ¶¹2¯ËªŽ½qÛ÷–R  §-;š
-6Ã¦×}1E@Œè†ª@—bfß,¥ JI“‹ÐU[®0ùŠì:â6Âò`8$]‘¿Ò[(%ø ÒaÓ@ÁûKYc5ù�âàS÷aû½?ÌQ’3f¶ãü>„‹?t>QWW‡ÆÆÆK`î¢¥¸ôúŸcþ¼yEsûûN¾Àôóùó1­ `¯²‹ƒÆ4Ž*�×ßê‹G÷RUÎt£X¼òÃïqe*‚â2ú£öš†œ„žGþ±é]—“%• PÜÈ¼žCKFaÈ)3¡ÈUØŸê¨*ÃÐÌº+$i7Ô¼_\Æ¬'Î29¶øÊfÖBøl{ôºÝàƒE_·³ªiò—ƒÁÞŽwþv_ŽûÄ{ƒ¸äƒçâ}§�†µk×¢½Õt/þÈ‡ÓCOM«ákßù–¯ÛPx\�âÁ·qp¤ð™îÂL3Ð·yùÅOµ­~;;Y((¸±#Þ‡ÃR Ôƒ1’�‡ž¹GjX{n”Rúk["#MÍ£%©^�CBR*· “åÏ&Ù¬UÎL)v¯„¹3¢„-]Hgr¿~$È!�4É	`Ò†¬ÛÖí7ßgóÃºýf“à6žÒiÌ×Ærà<3¥7Ý©×n?Kðyméðµ2#Í}Bnu~wŽË5‘ðz½xÏ)ïÃŠ÷_LâI	#‡vQjÄPelß¶<ð xðÏØþö[ðx½¸òÊ+‘’u¼òòKÐ¤žùó½èîÄÒeË!zÌ,G3· G"¹ßch�%éŠéê+…ioXH÷¸Ãj(’M2žàìñ”Õ.ºúö]TWZç‚ºA±w0i;ž
0-ª«¶«ÕUh‰(¨Vš«44†’*jX1uª«™QÞ†’²%#+�¥´XÑÂ‰`8Œè)iXû³ÞPŽÒsŸÏ7i«¾…/�PžùêÅÞ§îÇÝƒDa4™÷qüÒÅèêîÆ‘Ã£=9]5�øäµÿŠ¥›>ˆÞ,håE€ažè—>ý…Õ�OÚŽG� 2G~K™C)ÅEoÞ�© äû ½v¬Ç]üõ{×ÉÈs™›ÂrX´leŽ+�Pt´ç\¢ ª¤�’Š¹–ŒÂ�âpÓìOæpùI5Ù º
CÓ@59£J¹ Ù¥¹¥Ìz»÷–sJÁçóÁç&ÔßÏ†‹cÐè –P6:¥Úý*^zìAt¾ñœ­2°½–YÇ¡õ_Dý’ÌxôIïæSU
 H·ëþYû@ï[¸qÿß€jXy„ ÛëÇ’eËsLT;>ÀÃ�æ›'ñ’JÀ©5`A—S%]ˆjÀIˆÎ$ýÜ¯ö™{éáñŽyxfÂ- »U¿DŽ@îÂ��Ûpø­W1ØÝ‰ÃûÞ�4Ü[4ìê›uZÎüVo¸ØLdï‰ÀQ§  3cíÔÀÝnUË
X¿i³8¸bãÖlW ?* ä*Žãàwå®„²,#/½ÒWb f
-ÕTèR†’§ÙŸsì"ñ{Â‰ãzÀ|ÁåÇævÏå:aV€“Uß~‘E£_ _„“§4<¿ë0ö’}ºªüµV—>üÌ�„'ä¢&G¥°ðêº+ÿãÏý;¯³¬ Â	Ç�§Ùf~n  ,X¸¡ºúœýºFˆ«(~N”€Sk€p¢m�j*¨&C—S²RÅ@8³JÑúk0èxQLð³Qm%À ÞËÃïpDW6šýBîâïH¨øëÎ~„S6n•®MÛ¿Õ
 0ÉÁ5[~ú€ü†ñ¾5Þ{‹¯úõçCÏao—.=žÀhs‹ƒ[· nþ
#¦1EŸpM7�ÅÒ†óQNS E�—¥ò�i%™¿›Çäª4$0}|–ãK
~6ªå
„\j=Ø
›Íº­�â«> ¼Õ—À“ïl§”FYª_9™<'G½ Òg«ØC=Ÿ°HA£2/Üþm�zœö±kÐW‘J§Þ—ã,P]5}þ<×€qùÊ¾w<¯p»D°_0°Ó	ÆkxM>¾bÁÊ¯ú’ªã©}aÛD0B�í^V¿üh"ûŠá]¡ ª�õ›6‹ƒ+7>F55“e¨w£ÿ±_Aî¥þÕ$ª�aŽ”€— ºœÌTV²ú�cQ „åáu‹­öÅ0+À+°¨óðûù@y_ º"2þº³ß6\W¤;ý�Ýö™é4âm<8¦ Š`Ãõ÷‡ú¢á—%¹pèå‡††^yø/ 0çÔ7ŸsÕÿdö3ƒ	C©ÒJ@Ó(RÒ‘5`�°|Ú2Hë�N€µÒWCèót9ËG�às,ƒf¿ ŸPüÜUƒâ¥ýƒx³§0òòn1ùîËTŸÀtÅ37^îøÜÍgK�ÛÏyëù{ :�ÛùãNCRÑáÒD�~"¯¡;oÄS68–÷ÿÛ;ûØªÎ:ŽŸç9çžû~o_i+XÖÂºN ¸¦BjL†fâÌÔÄlþ�C—8ÙœgÒD3’EˆD·l¨™Fb”2§t&3å5aH#8y]K¡o—{{_ÎÛóøÇí)íí¹¯½·´ô|’þçÐÒÛþ>Ïï÷{~Ï¹¾t�Oªy{ÀØ¤¡$�wê­�#nè�Þ£’«bŸIB3r–Ó	| ¨ñÉ¨óåÎ¤zo©xû¿ƒ¶�>"xOÈÍ6üëÉÕsj‹¯œ ªV®?ÚøÙo>èó°¼½m\	ÍD_4�çYó•…œ †~{`Lã×r‚°t°QI†wLJ f$Ø³‘Y
Hð+¥5÷,
I÷Sº‰“W†mW} s¾ËŸG Ò±î1¥·©%e=EØ%Ix`Åýðz&Oèé&G_T³­íÈ&‚BÏÐO—*ÑoÕX1Bn·Dv3x]¬äÀ÷¸ø$¸åÜ¥Åù›	t_²ýYÝM�¾\8(êUßûí�(¥ã']’„em-‡'¿µç‘”9åð¹ÈÁ|€µPÊôÞD‰ Î¯ä¬ó�ô¾þ‘‹CüjD�r£"J¸¹ÛûæË�wK£/Ž Š uó®Ù<1Q Ð¾´�õuSî/¦$°PU¦¡C3sPóN
 äfð¹¼òí8(Å¯ü…~¾tŸrØC´ïßí«þDIëæ]-²ÇˆJRÛÄ¿_ØÔ€%Í‹@3fƒJÉBnÝˆ§TŒêÈz
±Ì¤ ¬•>3è'Â
žæóŒ5ó¾ÎÎ\�ãôµû'<Ý¥þBpPk;÷…oÄ"oP&Mz‘ÏÀò{ï™Ò 
ïØÇ$4šnTD•€W¦ðH~E*x¿_f>9{P†oþHOò»4œýu7�í�ÑÁæâ9pP"ëS?¾~;¥ô©ÌkíK[° ¶fJ6 ¤Ë‚�¸žõ2ß`Œn
$tUUn4æ¢œ�©€ÌXÑoGf) 1Š�›!ì‘rvõ-ÎßLàäÕûù}ÌÏtßG Ó¤mËž�„ÒßdöªX|�}6À9GLãˆ$�)A\èPŒ…n
è¦	M7 ¼h)”" +ÐeFà–(%�))ë_«(é>A®±Ýñ×‚\HàÔµHÖÀ'‚÷<ò3så�•Æ@ÈÖ €ÖÅÍXÔXo›dŠ œ'ä,1èœŒÏ
hÆd9h‚¢Ú+AfS¥#3iì·Cb•	òlÈŒ¢ÚÃpí½XÝ±&ïŠŸÒMôôÇq¦÷VVÁ{ˆà?Ÿ�u~.”‰\%�Ï@[sã”íÂ‰$’Ihj
	:3Ûù&äf
™Q„ÜBn6ð'ïÇéãÝønç‹¶õ~$i §wïhY{#NàçÆ@™ißº·C¨‰Wì²�\eA×îN|aÓ³ ž bª�HÊDÊ˜~�Ÿ�;) �ÌThº_`³ÊóD?Øð€Xºjybû„=t.peDÅÙ¾ˆí>¾…ø…á äÊ€©":õìÝö=±õà…I?Õ0SÓ•’ÁL
Àêî—Djæýú‡�ãüÑ.´Ü·ŸÚú*.Ç�³ÏÁ9ìSè‹N�_Ž *Hëæ]-²?¼�<bw½¡* _¨ÿüñ— ÏüñxÖÏe˜1Õ@ÊUe¡R`pI
#EüDâš‰¿¼þN½ö“ÿ&ís…ë·,ùÖKA¥nÑ¤û„Q®«ûBn¶c¾wõ‹ÅÀÐ¾uo‡ÐÏgÎ À‡Ý�è§ú›?¶áÉ_í/ø0Žj˜Hh&R†@Êà%gå€Ì(Ü#ž±!ŸbƒÝ"®™¸8Åå�(†ãôèÎ¾ðè¿!øêUóDô¹ûŸýýÓîÆ% ‚÷¨£‘ßÕpí•ùº�?]Ì m[öl$À6«?9w}‡v�z‚où›Z‡Ú¾Ñ‰†Új,j¬G•·ð·Æ¶P�´t“§w¸€É“gÍr	ÀjøSJ!Q2~W‘\Œ–´ªÛÑÓ0��}&Wþôü±‘Ó]«­?Éµhí/»ïsÒüéãàÐ¶eÏFBðÔ…�Oüˆëj7 Ô¬zh°ñó›Æg	˜âÁ’ú ÔÖ”$;3�%˜"mƒÄðø½^„«Òo*MØâ+G`gc$©ãÃáQÜ¸•Â`,5O=“¸úÎù�¯-ðÓ;�#€YÂÂ/=ýNxÙšOÛ]óHµÕ¨«¡.è+Û™ý÷ßzJ 
ŸøÌ+ö}é\`(®c /8à-L]û»¼«®¶fï\{Þþ\Áy"Ð,Á·xÅKä£¢9óZÒ¸Ô?„KýCé{ƒ!,)üú¼ð¹J;9øþ¹s P6Ä5‘”�áX·b£IqÛ”>\ð˜0ŒnÅëÿƒ/6xÀ©ë+�“Ì2Ú·îízêæ=l';<AUÐÅ¥ äSàRÜðz<ðÉ4g¶ð‹¯}LVð³?Éû5t. 1Õ„j
Dã	hj
#) ©iˆ«zÁ+ûD˜ËÝ#8?”ê;Xsâo'çÃüÙ„#€YL)2ÈÄ#hÌ�¾ÛY‚âR�ìEWçWo Àºmo,hjjÄ(—93’4ÅÓ‰aRÓ�4	¸¡•ÜvXïûàDC0ü¶³ÊßYÌZ7ïjq¹½Ÿ’k=óV•*‹¡ãÐßõÛM ÐôÐw^®[ýpÙÿÏÌåî1tí]SMô˜ÉÑcÎ
?ûp0GiÝ¼«EòøWJ×È¡ú6®«íÅH¡÷ÀÎñ­µª•ë�6?úÜƒ…þÛ‰pÁc²â½lèÚ»ŒI×)ÁY¯$Îðý¯œw‚}öãà.bmç¾°¨ªi¾qå­r¸¾Åˆ.¡‘ƒµ~®«íÖ}¢ùì¶¯¬¦Þ „ÉË—ÿôÍnëº™Œ»ÃuQ ÐâÑ^êrßdLº®%¢ƒn_ð†ß«ôs-u“Œ]qRø¹ÍÿÒ‡+¦Å1È®    IEND®B`‚               h          ¨   00     ¨%          ›     X4   V S _ V E R S I O N _ I N F O     ½ïþ                  ?                        ¸   S t r i n g F i l e I n f o   ”   0 8 0 9 0 4 b 0   ,   C o m p a n y N a m e     a m p h p   Ô V  F i l e D e s c r i p t i o n     E x e c u t e s   a   c o m m a n d   a n d   p a s s e s   t h e   s t d i o   s t r e a m s   o f   t h e   c r e a t e d   p r o c e s s   t o   T C P   s o c k e t s   0   F i l e V e r s i o n     1 . 0 . 0 . 0   :   I n t e r n a l N a m e   P r o c e s s W . e x e     V   L e g a l C o p y r i g h t   C o p y r i g h t   ( C )   a m p h p   2 0 1 7     B   O r i g i n a l F i l e n a m e   P r o c e s s W . e x e     @   P r o d u c t N a m e     P r o c e s s   W r a p p e r   4   P r o d u c t V e r s i o n   0 . 0 . 0 . 0   D    V a r F i l e I n f o     $    T r a n s l a t i o n     	°<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
<assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
    <security>
      <requestedPrivileges>
        <requestedExecutionLevel level='asInvoker' uiAccess='false' />
      </requestedPrivileges>
    </security>
  </trustInfo>
</assembly>
                                                                                                                                                                                                                                                                                                                                                              ü   0)050U0k0u0‡0£0¯0¶0Ð0Ü0ü0
1G1z1ˆ1Ž1�2×2ú23)363<3c3·3Á3Ú3û3444H4V4�4ž4¥4±4¿4ÿ45#5v5œ5¬56&6A6e6�6Õ78/8Z8·8Ã8ê8ý899M9g9¢9¶9ß9ó9û9:
:":J:d:j:¡:×:å:ý:;*;Q;W;‹;ß;å;÷;<)<N<X<k<…<Ž<”<Á<â<é<ó<
==+=L=b==ª=Á=ë=>->_>g>„>Ç>7?Y?’?œ?¢?±?è?î?      L  0,0>0F0Q0V0a0k0¢0³0â0ì0ò0&1/151‚1Â1û142M2W2]2”2�2£2Ù23H3‰3�3º3Ë3Ú3é344/4>4\4‡4¢4µ4Å4õ4'5B5U5e5y5ú5$6-6I6m6‘6©6Ë6ß6å6ë6ñ67/777<7A7O7W7n7v7˜7œ7 7¤7¨7±7ß7"8V8�8™8È8Ý8'909G9r9‚9´9å9ð9õ9::?:f:‡:œ:¤:º:Å:Ë:Ñ:Ú:å:õ:<;e;Å;ó;<	<</<4<A<B=K=V=]=}=ƒ=‰=�=•=›=¢=©=°=·=¾=Å=Ì=Ô=Ü=ä=ð=ù=þ=>>>(>8>H>Q>¶>â>1?D?W?c?s?„?ª?¿?Æ?Ì?Þ?è?   0  ¨   F0S0z0‚0›0Õ0ð0ü011!1P1X1p1v1¡1Ç1Ð1Ö12²2Ñ2Û2ì2ú2;3A3Š3�3µ3º3ß3ñ34[4`4s4�4œ4§4/585@5‡5–5�5Ó5Ü5é5ô5ý5666"6(6.646:6@6F6L6R6X6^6d6j6p6v6|6‚6ˆ6Ž6”6š6 6¦6¬6²6ð6 @     p1x1„1ˆ1 1¤1 P  $   3 3$3(3,30343ü5 66,909L9P9 p     0 0$0,00080<0D0                                                                                                                                                                                                MZ�       ÿÿ  ¸       @                                     º ´	Í!¸LÍ!This program cannot be run in DOS mode.
$       äíP¦ Œ>õ Œ>õ Œ>õ©ô­õ¬Œ>õ›Ò?ô¢Œ>õ›Ò=ô£Œ>õ›Ò;ô±Œ>õ›Ò:ô¬Œ>õ}sõõ¥Œ>õ Œ?õþŒ>õ
Ò6ô£Œ>õ
ÒÁõ¡Œ>õ Œ©õ¡Œ>õ
Ò<ô¡Œ>õRich Œ>õ                        PE  d† U%s\        ð "   0        „2        @                        �         `�                                           ð_  È       ¨Ü   €  H           € L   X  p                           €X  ”            @  ø                          .text   .      0                    `.rdata  H*   @   ,   4              @  @.data   €   p      `              @  À.pdata  H   €      b              @  @.gfids      �      f              @  @.rsrc   ¨Ü       Þ   h              @  @.reloc  L    €     F             @  B                                                                                                                                                                                                                        @SH�ì€   H‹ð_  H3ÄH‰D$xH‹ÙÇD$@   H‹IL�D$@º~f€ÿ11  …Àt‹H�¬4  D‹Àè”  3Àéó   H‰´$˜   3ö95-f  H‰¼$    t9·f  �~¸   f‰D$XH‹ f   ‰t$\D$`ÿî0  f‰D$ZH�T$Xë3¸   ¿   f‰D$HH‹Ìe  ‹‰L$L··e  ÿ¹0  f‰D$JH�T$HH‹KE3ÉH‰t$0D‹ÇH‰t$(H‰t$ ÿP0  H‹¼$    H‹´$˜   ƒøÿt‹H�Z4  èÅ  3Àë'ÿ;0  =3'  tD‹H�š4  ‹Èèc  3Àë¸   H‹L$xH3Ìè­  H�Ä€   [ÃÌÌÌÌ@SUVWAVAWHƒìXH‹Œ^  H3ÄH‰D$HL‹¼$°   3ÛA‹ñH‰\$0I‹èH‰\$(D‹ò‰\$ L�L$@D‹ÆH‹Õ‹ûÿÀ/  ƒøÿu!ÿ¥/  E‹ÎH�K4  ‹ÈM‹ÇèÑ  3Àév  D‹Óƒþ‚ã   ‹þWíƒçøfoåA»   @ „     E‹ÊE�CMÀA‹ÓHÒA�KÿHÉMÉAƒÂfBnLÅ E�CfnDÕ MÀfnTÍ A�SfBn\Í A�KfbØE�KfBnDÅ MÉfbÑHÒfbÚHÉfþÝAƒÃfBnLÍ foëfnTÕ fn\Í fbØfbÑfbÚfþÜfoãD;×‚]ÿÿÿfþåfoÄfsØfþàfoÄfsØfþàf~ç‹ÓD;ÖsW‹ÆA+Âƒør9A‹Â‹ÎA+ÊHÁàƒéHÅÑéÿÁD‹ÁE�J@ f„     PH�@ IƒèuñD;Ös
A‹ÂHÀ|Å �øD‹L$@D;ÏtE‹Æ‰|$ I‹×H�3  è�  3Àë¸   H‹L$HH3Ìè§  HƒÄXA_A^_^][ÃÌÌÌÌÌÌÌÌÌÌ@SH�ì�   H‹€\  H3ÄH‰„$ˆ   H‹ÙÇD$P    H‹IL�D$Pº~f€ÿ¾-  …Àt‹H�	3  D‹Àè!  3Àé«   ‹Èb  H�D$xH‰D$8‹¯Á‰L$@ÇD$0   ÆD$xHcÈH²b  H‰L$Hÿ_,  D$0‹L�D$X‹ÈˆD$|ÁéA¹   ˆL$y‹ÈÁéˆL$z‹È¶ÁéD$XˆD$}H�÷2  D$@ˆL${H‹KH‰D$ D$hèýÿÿ…À„ZÿÿÿÇC   ¸   H‹Œ$ˆ   H3Ìè  H�Ä�   [ÃÌÌÌÌÌÌH‰\$ AVH�ì€   H‹[[  H3ÄH‰D$p‹áa  H‹ÙƒÀE3ö‹ÈD‰t$d‰D$@ÿ-  H‹KL�L$hH‰D$HE�FH�D$dL‰t$0L‰t$(H�T$@H‰D$ ÿ¢,  ƒøÿu)ÿW,  D‹H�=2  ‹Èè†  H‹L$HÿÃ,  3Àé‚  D‹D$hAƒøs%D‹L$@H�m2  ‹è–  H‹L$Hÿ“,  3ÀéR  H‰¼$    H‹|$H¶<t$‹H�×2  D¾Àè^  H‹L$Hÿ[,  3Àé  ¶O„Ét@€ù}¶ÁL�õ?  M‹ÁëL�X3  ‹D¾ÁH�k3  è  H‹L$Hÿ,  3ÀéÊ   D‹L$@H�D$`H‰´$˜   A‹öH‰D$XfÇD$`ÇD$P   E;Át‹H��1  èÈ  H‹|$HëLHcn`  ‹L‹Á¯ÁH�OHcÐH|`  èq'  …Àt‹H�r3  è�  H‹|$Hë@ˆt$a¾   ÇC   H‹Ïÿt+  ‹H�³3  H‹KL�D$PA¹   H‰D$ èÊúÿÿ…ÀADö‹ÆH‹´$˜   H‹¼$    H‹L$pH3ÌèE  H‹œ$¨   H�Ä€   A^ÃÌÌÌ3ÀH‹Áƒz|HÿÀHƒø|í3ÀÃ¸   ÃÌÌH‰\$ UAVAWH�¬$€úÿÿH�ì€  H‹ðX  H3ÄH‰…p  E3ÿL‹ñA‹ßI‹ÞèÔøÿÿ…À„Ì  HÿÃHƒû|æH‰´$¨  I‹ÎH‰¼$°  è{ÿÿÿ…À„é   E‹ÇD‰|$@E‹ÏD‰½P  E‹×D‰½`  I‹Þ¿   @ „     L‹A‹C…À…‘   A‹ÏE…ÉtI‹SD  H9”ÍX  tÿÁA;ÉrïA;Éu#Aƒù@sI‹CH‰„ÍX  D‹�P  AÿÁD‰�P  A‹ÏE…ÒtI‹SH9”Íh  tÿÁA;ÊrïA;ÊuiAƒú@scI‹CH‰„Íh  D‹•`  AÿÂD‰•`  ëDƒøu?A‹ÇE…ÀtI‹S‹ÈH9TÌHtÿÀA;ÀrðA;ÀuAƒø@s‹ÈI‹CH‰DÌHD‹D$@AÿÀD‰D$@HƒÃHƒï…ÿÿÿL��`  L‰|$ L�…P  3ÉH�T$@ÿ¶(  ‹Øƒøÿ„=  …À„'  A‹÷I‹ÿ~y Hƒÿ}pI‹þH�T$@H‹Iÿ§(  I‹þ…Àtèªûÿÿ…À„¢   ÿËë;H‹IH�•P  ÿ}(  I‹þ…Àtè`úÿÿ…Àt|ÿËëH‹IH�•`  ÿW(  …ÀuÿÆHÿÇ…ÛŠI‹Îè’ýÿÿ…À…þÿÿ3ÀëIHcÆL�L$4ºÿÿ  ÇD$0   A¸  I‹ÆH�D$0H‰D$ H‹Kÿ+(  ƒøÿuE‹H�Å0  èP  ƒÈÿH‹´$¨  H‹¼$°  H‹�p  H3ÌèN  H‹œ$¸  H�Ä€  A_A^]ÃD‹H�×0  ‹L$4èÆ  ë´H�}1  èø  ë¦ÿp'  ‹ÈH�'1  è¢  ë�ƒÈÿëžÌÌÌÌÌÌÌÌÌÌÌH‰\$H‰t$H‰|$AVHƒì 3ÛÇAH   H‰YPH‹ñÇAX   H‹ù�HcËL�FHHÁáE3ÉHÎH�QÿÍ%  …Àtj…ÛuH‹OëH‹E3ÀA�Pÿ�%  …ÀtCÿÃHƒÇƒû|¸ÇF`h   ¸   H‹H‰Ž°   H‹NH‰Ž¸   H‹N(�Žœ      H‰ŽÀ   ëH�i1  ëH�ð0  ‹Óè	  3ÀH‹\$0H‹t$8H‹|$@HƒÄ A^ÃÌH‰\$H‰l$H‰t$ WHƒìPH‹õT  H3ÄH‰D$HH‹ÙƒÏÿ¹   ÿ¼&  H‹KL�L$@3öH‰D$8H‹ÐÇD$0   A¸   H‰t$ ÿ°$  …ÀtZH�-í1  D  ‹D$@…Àtj‹L�D$0H‹KA¹   ‰D$0H‰l$ èÙõÿÿ…ÀtIH‹T$8L�L$@H‹KA¸   H‰t$ ÿV$  …Àu²ÿ”$  ƒømtÿ‰$  D‹H�'1  ‹ÈèÐ  ë‹þH‹Kÿr$  H‹Kÿ�%  ‹ÇH‹L$HH3Ìè	  H‹\$hH‹l$pH‹t$xHƒÄP_ÃÌÌÌÌH‰\$H‰t$WHƒìpH‹ÚS  H3ÄH‰D$`H‹Ù3ö¹   ‰t$TƒÏÿÿ›%  H‹KL�L$PH‰D$HD�FH�D$TH‰t$0H‰t$(H�T$@H‰D$ ÇD$@   ÿ"%  ;ÇtqD‹D$PE…À„Ä   H‹T$HL�L$XH‹KH‰t$ ÿo#  …À„”   D‹D$XD‹L$PE;ÁuuH‹KH�D$TH‰t$0L�L$PH‰t$(H�T$@A¸   H‰D$ ÿ²$  ƒøÿu�ÿO#  H�X0  D‹‹Èè–  H‹Kÿ<#  ‹ÇH‹L$`H3ÌèÝ  L�\$pI‹[I‹s I‹ã_Ã‹H�Â0  è�  ëÅÿý"  H�N0  ë¬‹þë²ÌÌÌÌÌÌÌÌH‰\$H‰l$H‰t$W¸P  èæ  H+àH‹|R  H3ÄH‰„$@  ‹ÿX  ¹öÿÿÿ�@Û�4@ÿh"  3íL�L$0H‹ÈH‰l$ D‹ÃH�T$@H‹øÿ8"  …Àuÿv"  ‹ÈH�µ0  èÀ  3Àéè  ‹T$0;ÓtD‹ÃH�ç0  èâ  3ÀéÊ  ‹Î‰5“X  ‹ÞÿË#  D‹ÆH�T$@H‹ÈH‰�X  èŒ  ‹Î‰5|X  ÿ¦#  H�T$@D‹ÆHÓH‰lX  H‹Èèd  L�L$0H‰l$ A¸   H�T$@H‹ÏÿŽ!  …ÀuÿÌ!  ‹ÈH�ë0  è  3Àé>  ‹T$0ƒútH�?1  è:  3Àé"  ¶D$@„Àt¾ÐH�Ð1  è  3Àé  L�L$0H‰l$ A¸   H�T$@H‹Ïÿ!  …ÀuÿT!  ‹ÈH�#2  èž  3ÀéÆ   ‹D$0=   uH�U2  èÀ  3Àé¨   ÿÈ@8l@tH�©2  è¤  3ÀéŒ   HƒÏÿ‰l$(D‹ÏH‰l$ L�D$@3Ò¹éý  ÿÓ   HcØ…ÀuH�í2  èh  3ÀëS¸   H÷ãH@ÇH‹ÈÿO"  D‹Ï‰\$(L�D$@H‰W  3ÒH‰D$ ¹éý  ÿ‚   …ÀuH�3  è  3Àë¸   H‹Œ$@  H3Ìè!  L�œ$P  I‹[I‹kI‹s I‹ã_ÃÌÌÌÌÌÌÌÌHƒìXH‹õO  H3ÄH‰D$H3ÀˆT$@‹H‹I‰D$AH�D$@H‰D$8A‹ÀÁèˆD$AA‹ÀÁèˆD$BA‹ÀÁèDˆD$DL�D$0L‰L$ A¹   ˆD$CÇD$0   èóðÿÿH‹L$HH3Ìè†  HƒÄXÃÌH‰\$H‰l$H‰t$ WAVAWHƒìPH‹aO  H3ÄH‰D$@3ÿH�t$(D‹÷‹ï‹ßL‹ù„     I‹ßH�T$ ÿq  …À„    ‹D$ =  uI‹ßAÿÆH‰HƒÆë…À…”   ÿÅHÿÃHƒû|»E…öt[AƒÉÿH�T$(A¸   A‹Îÿþ  ƒøÿuÿ  ‹ÈH�Š2  èe  H‹ßf�I‹ßH�T$ ÿñ  …Àtƒ|$  u4ÿÇHÿÃHƒû|Û¸   ë$ÿÖ  D‹Çë	ÿË  D‹ÅH�Ù1  ‹Èè  3ÀH‹L$@H3Ìèc  L�\$PI‹[(I‹k0I‹s8I‹ãA_A^_ÃÌÌÌÌÌÌÌÌÌ@USWH�¬$PÿÿÿH�ì°  H‹&N  H3ÄH‰…¨   Hƒ=ÜT   H‹ú‹Ùu"º   ‹ÊÿØ  H‰ÁT  H…Àt	H‹Èÿƒ  ¹  H�¿T  ÿ)  …Àt‹ÐH�Ü1  è§
  L‹Ç‹Óè­  …Àuè  ƒÈÿéa  èûÿÿ…Àtêƒ=,T   ¸   H‰´$Ð  H�uxL‰´$à  A¾   DEðL‰¼$è  E3ÿA‹ÿ¹   ÿ@  º   A‹ÎH‹Ø‰8D�BD‰xÿÝ  H‰CHƒøÿ„¾  H‰ÿÇHƒÆƒÿ|¾L‰|$(L�MxL�	ôÿÿD‰|$ 3Ò3Éÿb  3ÒH�M A¸È   H‹Øè,  H�M èýöÿÿ…À„z  ƒÊÿH‹Ëÿ	  ƒøÿuÿ  ‹ÈH�1  è`
  éP  H�UpH‹Ëÿî  …Àuÿì  ‹ÈH�+1  è6
  é&  D9}p…  H‹@S  H�EÐH‰D$HL�MèH�E 3ÉH‰D$@L�EèH‹õR  H‰D$8L‰|$0ÇD$(   ÇD$    ÿ¥  …Àuÿ{  ‹ÈH�Ò(  èÅ	  éµ  H‹MØÿf  H‹M ÿ\  H‹M¸ÿR  H‹MÈÿH  D‹EàL�Õ0  H‹Mxº   èßûÿÿ…À„l  H‹ExL�L$PD‰|$PL�Ò÷ÿÿL‰|$(3ÒD‰|$ H‹HH‹E¨H‰L$`3ÉH‰D$Xÿô  ÇD$h   L�L$hH‰…�   L�qöÿÿH‹…€   3ÒL‰|$(D‰|$ H‹HH‹E°H‰L$x3ÉH‰D$pÿ¬  ÇE€   L�M€H‰…˜   L�+öÿÿH‹…ˆ   3ÒL‰|$(D‰|$ H‹HH‹EÀH‰M�3ÉH‰Eˆÿh  H‹MÐƒÊÿH‰…    ÿ,  ƒøÿuÿ9  ‹ÈH�Ø/  èƒ  ëvH‹MÐH�UpÿC  …Àuÿ  ‹ÈH� 0  è[  ëND‹EpL�60  H‹Mxº   è úÿÿ…Àt1H���   èûÿÿ…Àt!H‹MxH‹Iÿö  ‹Epë‹H�X%  èK  èÆ  ƒÈÿL‹´$à  H‹´$Ð  L‹¼$è  H‹�¨   H3Ìè<
  H�Ä°  _[]ÃÌH‰\$H‰t$ WHƒì0H‹J  H3ÄH‰D$(3öH‹úH‰t$ H‹Ùÿ§  D�F
H‹ËH�T$ ‰0ÿ£  ‹ðH9\$ tÿ„  ƒ8 u	‰7¸   ë3ÀH‹L$(H3Ìè¿	  H‹\$PH‹t$XHƒÄ0_ÃÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌH‰\$H‰t$WHƒì H‹ÙI‹ð¹   ÿ`  H‹Ö¹   L‹ÀH‹øÿü  …Àt.ƒøt	H�t/  ëtH‰{¸   ÇC    H‹\$0H‹t$8HƒÄ _ÃH‹Ïÿ  ¹   ÿ  H‹Ö¹   L‹ÀH‹øÿ¢  …Àt!ƒøu¦‰C¸   H‰{H‹\$0H‹t$8HƒÄ _ÃH�>/  èÁ  H‹ÏÿÀ  H‹\$03ÀH‹t$8HƒÄ _ÃÌÌÌÌÌÌ@SHƒì H‹ÙH‹ÑI‹Èè|þÿÿ…Àt‹ÿÈ=þÿ  w¸   HƒÄ [Ã‹H�/  èd  3ÀHƒÄ [ÃÌÌÌÌÌÌÌÌÌÌÌÌ@SHƒì H�YI‹ÈH‹Óè+þÿÿ…Àtƒ; ‹H�/  è$  ¸   HƒÄ [ÃÌÌÌÌÌÌÌÌÌL‰A¸   ÃÌÌÌÌÌÌH‰\$WHƒì ·I‹ØH‹úfƒø-uf9Bufƒz u¸   H‹\$@HƒÄ _ÃH‰t$0L‰t$8E…ÉtrI‹L�5H  E3ÒH�5gH  E‹ÚM‹Î�M‹H‹ÃL+Ã€    ·B· +ÑuHƒÀ…Éuì…ÒtAÿÃIƒÁL;Î|ÌëIcÃH�@M�ÎL‹ÇH�õM  H‹ÓAÿRéB  fƒø-…3  f9B…)  H�JH�2.  I‰ÿi  L‹L�5wG  E3ÒH�5ÍG  H‹øM‹ÎH…Àuof�M‹I‹ÃM+Ã€    ·B· +ÑuHƒÀ…Éuì…ÒtAÿÂIƒÁL;Î|Ìé}   IcÂH�@I�ÎH…ÀtmH‹@H…Àu
¸   é�   I‹ÓH�:M  ÿÐéŒ   fD‰M‹I‹ÃM+ÃfD  ·B· +ÑuHƒÀ…Éuì…ÒtAÿÂIƒÁL;Î|ÍëIcÂH�@I�ÎH…ÀuI‹ÓH�P-  è;  3Àë0H‹@I‹ÓH…ÀuH�\-  è  3ÀëL�GH�°L  ÿÐë¸   H‹t$0L‹t$8H‹\$@HƒÄ _ÃÌÌH‰\$H‰l$H‰L$VWAVHƒì 3ÿHcê3ÀH‰|$@»   ‰=bL  H‰cL  M‹ðÇ^L  ÿÿÿÿ‹÷‰=ZL  H‰=WL  H;Ý}Nff„     I‹ÞL�D$@D‹Îèýÿÿ…Àtd~ƒø~ƒøt	ƒøu	‹÷ë¾   HÿÃH;Ý|Éƒ=L  ÿ…±   ¹   ÿA  H�-  ¹   L‹ÀH‹ØÿÙ  …Àt$ƒøtH�Q+  ën3Àé¨   H‰±K  ‰=³K  ëfH‹Ëÿ   ¹   ÿí  H�Æ,  ¹   L‹ÀH‹Øÿ…  …Àt!ƒøt	H�ý*  ëH‰dK  ÇbK     ëH�!+  è¤  H‹Ëÿ£  9=IK  uH�ü+  è‡  ë9=K  uH�,  èq  ë¿   ‹ÇH‹\$HH‹l$PHƒÄ A^_^ÃÌÌÌÌÌH�áL  ÃÌÌÌÌÌÌÌÌH‰T$L‰D$L‰L$ SVWHƒì0H‹úH�t$`H‹ÙèÊÿÿÿE3ÉH‰t$ L‹ÇH‹ÓH‹ÿ  HƒÄ0_^[ÃÌÌÌÌÌÌÌÌÌÌÌH‰l$H‰t$H‰|$ ATAVAWHƒì0I‹èL‹òH‹ñèyÿÿÿH‰l$(M‹ÎE3ÀHÇD$     3ÒL‹øH‹HƒÉÿ“  IƒÌÿ‹ø…ÀAHüA;üuA‹ÄëlHcÏ¸   HÿÁH‰\$PH÷áI@ÄH‹Èÿm  H‹ØH…Àt7I‹�GLcÀM‹ÎH‰l$(H‹ÓHÇD$     ÿB  …ÀAHÄƒøÿuH‹Ëÿ6  A‹ÄëH‰H‹\$PH‹l$XH‹t$`H‹|$hHƒÄ0A_A^A\ÃÌÌÌÌÌÌÌH‰L$H‰T$L‰D$L‰L$ Hƒì8Hƒ=ÈI   u3ÀHƒÄ8Ãº   H‰\$0�Jÿ¼  H‹ØH…Àu
H‹\$0HƒÄ8ÃH‹T$@L�D$HH�L$ è®þÿÿH‹T$ ÿÈHcÈI¹ &     E3ÀH�J ·fƒù w·ÉI£És
fD‰ HƒèëäH‹EI  H‰SH‹Óÿ   H‹\$0¸   HƒÄ8ÃÌL‹ÜI‰SM‰CM‰K SHƒì`H‹EB  H3ÄH‰D$P3À‹ÙI‰CÈD‹Á‰D$(A¹   I‰Cà3ÒI�Cà¹   I‰C¸ÿ‡  H‹T$xH…Òu&L‹D$HH�Ñ)  ‹ÓèÒþÿÿH‹L$PH3Ìèå  HƒÄ`[ÃL�„$€   H�L$@è½ýÿÿL‹L$HH�©)  H‹T$@D‹Ãè”þÿÿH‹L$PH3Ìè§  HƒÄ`[ÃÌH‰l$H‰t$ AVHƒì@½€   ‹Íÿc  H‰\$PE3öH‹ðH‰|$X H‹1H  H…É„#  ÿú  H…À„î   H‹XH‹Èÿ4  H…Û„Ø   L‰t$8AƒÉÿL‰t$0L‹Ã‰l$(3Ò¹éý  H‰t$ ÿœ  …À…‡   ÿN  ƒøzu‘L‰t$8AƒÉÿL‰t$0L‹ÃD‰t$(3Ò¹éý  L‰t$ ÿa  Hcø…À„^ÿÿÿH‹×H‹Îÿ’  H…À„IÿÿÿL‰t$8AƒÉÿL‰t$0L‹Ã‰|$(3Ò¹éý  H‰D$ H‹ð‹ïÿ  …À„ÿÿÿ¹   ÿR  L‹ÆH�p(  H‹ÈèüÿÿéóþÿÿH‹$G  H…ÉtÿÑ  H‹G  ÿ4  L‰5G  H‹ÎH‹|$XH‹\$PH‹l$`H‹t$hHƒÄ@A^Hÿ%  ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌff„     H;ù?  òuHÁÁf÷ÁÿÿòuòÃHÁÉé«  ÌÌÌ@SHƒì ¹   è  è;  ‹Èè8  èc  H‹Øè  ¹   ‰èC  „ÀtlèR	  H�—	  è6  èù  ‹ÈèÔ  …ÀuVèù  è,  …ÀtH�Õ  è°  è‹  è†  èÁ  ‹Èèî  èÙ  „Àtè˜  è§  3ÀHƒÄ [Ã¹   èý  Ì¹   èò  ÌÌHƒì(è¯  3ÀHƒÄ(ÃHƒì(ès  èj  ‹ÈHƒÄ(é™  ÌÌÌH‰\$H‰t$WHƒì0¹   è+  „Àu¹   è�  Ì@2ö@ˆt$ èÓ  ŠØ‹ÇD  ƒùu
¹   èx  …ÉuJÇªD     H�Ë  H�¬  èí
  …Àt
¸ÿ   éì   H�Š  H�s  èÆ
  ÇlD     ë@¶@ˆt$ ŠËèO  è  H‹ØHƒ8 t"H‹Èè�  „ÀtH‹H‹Ëèr  E3ÀA�P3ÉÿÓèÞ  H‹ØHƒ8 tH‹Èèm  „ÀtH‹è�
  èx
  H‹øèj
  H‹Øè>
  L‹ÀH‹‹è—ïÿÿ‹Øèè  „Àu‹Ëè1
  @„öuèE
  3Ò±èÚ  ‹Ãë!‹Øè¿  „Àu‹Ëè
  Ì€|$  uè
  ‹ÃH‹\$@H‹t$HHƒÄ0_ÃÌHƒì(è/  HƒÄ(évþÿÿÌÌ@SHƒì H‹Ù3ÉÿG  H‹ËÿF  ÿ0  H‹Èº	 ÀHƒÄ [Hÿ%  H‰L$Hƒì8¹   èç	  …Àt¹   Í)H�O>  èª   H‹D$8H‰6?  H�D$8HƒÀH‰Æ>  H‹?  H‰�=  H‹D$@H‰”>  Çj=  	 ÀÇd=     Çn=     ¸   HkÀ H�f=  HÇ   ¸   HkÀ H‹Ž<  H‰L ¸   HkÀH‹�<  H‰L H�Í  è ÿÿÿHƒÄ8ÃÌÌÌ@SVWHƒì@H‹ÙÿW  H‹³ø   3ÿE3ÀH�T$`H‹ÎÿÝ  H…Àt9Hƒd$8 H�L$hH‹T$`L‹ÈH‰L$0L‹ÆH�L$pH‰L$(3ÉH‰\$ ÿþ  ÿÇƒÿ|±HƒÄ@_^[ÃÌÌÌHƒì(èÿ  …Àt!eH‹%0   H‹HëH;Èt3ÀðH±ØA  uî2ÀHƒÄ(Ã°ë÷ÌÌÌ@SHƒì ¶óA  …É»   DÃˆãA  èâ  è  „Àu2Àëè  „Àu	3Éèý  ëêŠÃHƒÄ [ÃÌÌÌH‰\$UH‹ìHƒì@‹Ùƒù‡¦   èc  …Àt+…Ûu'H�XA  èá  …Àt2ÀëzH�\A  èÍ  …À”ÀëgH‹;  IƒÈÿ‹Â¹@   ƒà?+È°IÓÈL3ÂL‰EàL‰EèEàL‰EðòMðý@  L‰EàL‰EèEàL‰Eðòõ@  òMðñ@  òù@  H‹\$PHƒÄ@]Ã¹   èl  ÌÌÌÌHƒìL‹Á¸MZ  f9…ÊÿÿuyHc¸ÊÿÿH�uÊÿÿH��9PE  u_¸  f9AuTL+Â·AH�QHÐ·AH�€L�ÊH‰$I;Ñt‹JL;Ár
‹BÁL;ÀrHƒÂ(ëß3ÒH…Òu2Àëƒz$ }2Àë
°ë2Àë2ÀHƒÄÃÌÌÌ@SHƒì ŠÙè  3Ò…Àt„ÛuH‡ö?  HƒÄ [Ã@SHƒì €=@   ŠÙt„ÒuŠËèH  ŠËèA  °HƒÄ [ÃÌ@SHƒì H‹Ÿ9  H‹Ù‹ÊH3³?  ƒá?HÓÊHƒúÿu
H‹Ëè9  ëH‹ÓH�“?  è"  3É…ÀHDËH‹ÁHƒÄ [ÃÌHƒì(è§ÿÿÿH÷ØÀ÷ØÿÈHƒÄ(ÃÌH‰\$ UH‹ìHƒì Hƒe H»2¢ß-™+  H‹!9  H;ÃuoH�MÿÒ	  H‹EH‰EÿÌ	  ‹ÀH1EÿX	  ‹ÀH�M H1Eÿ¸	  ‹E H�MHÁà H3E H3EH3ÁH¹ÿÿÿÿÿÿ  H#ÁH¹3¢ß-™+  H;ÃHDÁH‰­8  H‹\$HH÷ÐH‰¦8  HƒÄ ]Ã3ÀÃÌ¸   ÃÌÌ¸ @  ÃÌÌH�Ý>  Hÿ%	  ÌÌ°ÃÌH�Ù>  ÃHƒì(èïóÿÿHƒèæÿÿÿHƒHƒÄ(ÃÌ3À9X8  ”ÀÃH�?  ÃH�?  ÃH‰\$UH�¬$@ûÿÿH�ìÀ  ‹Ù¹   èÕ  …Àt‹ËÍ)ƒ%x>   H�Mð3ÒA¸Ð  è%  H�Mðÿí  H‹�è   H�•Ø  H‹ËE3Àÿs  H…Àt<Hƒd$8 H��à  H‹•Ø  L‹ÈH‰L$0L‹ÃH��è  H‰L$(H�MðH‰L$ 3ÉÿŠ  H‹…È  H�L$PH‰…è   3ÒH�…È  A¸˜   HƒÀH‰…ˆ   èŽ  H‹…È  H‰D$`ÇD$P  @ÇD$T   ÿî  ƒøH�D$PH‰D$@H�Eð”ÃH‰D$H3Éÿ  H�L$@ÿ  …Àu
öÛÀ!t=  H‹œ$Ð  H�ÄÀ  ]ÃÌÌÌHƒì(3ÉÿŒ  H‹ÈH…Àu2Àë7¸MZ  f9uòHcA<HÁ�8PE  uã¹  f9HuØƒ¸„   vÏƒ¸ø    •ÀHƒÄ(ÃH�	   Hÿ%v  ÌÌHƒì(H‹�8csmàuƒxu‹H ��àúlæƒøv�ù @™t3ÀHƒÄ(Ãè  ÌH‰\$H‰t$WHƒì H�N"  H�5G"  ëH‹;H…ÿt
H‹Ïèi   ÿ×HƒÃH;ÞråH‹\$0H‹t$8HƒÄ _ÃÌÌH‰\$H‰t$WHƒì H�"  H�5"  ëH‹;H…ÿt
H‹Ïè   ÿ×HƒÃH;ÞråH‹\$0H‹t$8HƒÄ _ÃÌÌHÿ%¥  ÌH‰\$H‰|$UH‹ìHƒì ƒeè 3É3ÀÇ¤5     ¢D‹ÁÇ‘5     �ñcAMDD‹ÊD‹ÒA�ñentiA�òineIA�ðntelEÐD‹ÛD‹Ç;  A�óAuthEÙ‹ÓDÙ�òGenu3É‹øDÒ¸   ¢‰EðD‹ÉD‰Mø‹È‰]ô‰UüE…ÒuRHƒ)5  ÿAƒÈ%ð?ÿD‰u;  =À t(=` t!=p t°ùüÿƒø wH»     H£ÃsAƒÈD‰;;  E…Ûu�á ð�ù ` rAƒÈD‰;  ¸   ‰UàD‰Mä;ø|$3É¢‰Eð‰]ô‰Mø‰Uü‰]èºã	sAƒÈD‰é:  AºásnÇt4     Çn4     AºásSAºásL3ÉÐHÁâ HÐH‰UH‹E$<u2‹@4  ƒÈÇ/4     öEè ‰)4  tƒÈ Ç4     ‰4  H‹\$83ÀH‹|$@HƒÄ ]ÃÌÌ3À94  •ÀÃÂ  ÌÌÌÌÌÿ%â  ÿ%ä  ÿ%F  ÿ%8  ÿ%Â  ÿ%  ÿ%Æ  ÿ%Ð  ÿ%*  ÿ%D  ÿ%.  ÿ%   ÿ%J  ÿ%ô  ÿ%Æ  ÿ%¨  ÿ%Ú  ÿ%Ä  ÿ%^  ÿ%   ÿ%  ÿ%t  ÿ%®  ÿ%€  ÿ%‚  ÿ%  Hƒì(M‹A8H‹ÊI‹Ñè   ¸   HƒÄ(ÃÌÌÌ@SE‹H‹ÚAƒãøL‹ÉAö L‹ÑtA‹@McP÷ØLÑHcÈL#ÑIcÃJ‹H‹C‹HHKöAt
¶AƒàðLÈL3ÊI‹É[é»òÿÿÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌff„     HƒìL‰$L‰\$M3ÛL�T$L+ÐMBÓeL‹%   M;ÓòsfA�â ðM�› ðÿÿAÆ M;ÓòuïL‹$L‹\$HƒÄòÃÌÿ%x  ÿ%z  ÌÌÌÌÌÌÌÌff„     ÿà@UHƒì H‹êH‹H‹Ñ‹èTþÿÿ�HƒÄ ]ÃÌ@UH‹êH‹3É�8  À”Á‹Á]ÃÌ                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        °c      ¼c      Ôc      äc      ðc      
d      d      .d      Dd      Xd      hd      vd      †d      œd      ®d      Äd      Öd      îd      e       e      6e      i       j      j      òi      Üi      Âi      ¦i      ’i      ~i      `i      Di      0i      i              ¤e      ¼e      še      4j      >j              je      s      €      €o      €
      €xe            €—      €	      €‚e            €`e            €        êe              Êg      >f      Hf      àe      Øe      þe              ´g              ²f              Þf      êg      þf      rg      "h      0h      dg      Æf      †g      h      |g      Vg      ¢f      �f      g      @g      8g      ôe      *g              Úg      Hg      tf      ,f      f      Zf              (< @   Ð= @           ð0 @                   $0 @   à0 @                                            p @   @q @   S u c c e s s   S i g n a l   n o t   e x p e c t e d   a t   t h i s   t i m e         I n v a l i d   s t r e a m   i d e n t i f i e r       I n v a l i d   p r o c e s s   i d e n t i f i e r     D u p l i c a t e   s t r e a m   i d e n t i f i e r   I n v a l i d   c l i e n t   s e c u r i t y   t o k e n       I n v a l i d   s e r v e r   s e c u r i t y   t o k e n       F a i l e d   t o   s e t   s o c k e t   # % d   t o   n o n - b l o c k i n g   m o d e ,   f a i l e d   w i t h   % d       C o n n e c t i n g   s o c k e t   # % d   u n e x p e c t e d l y   s u c c e e d e d         C o n n e c t i n g   s o c k e t   # % d   f a i l e d         F a i l e d   t o   s e n d   % s   t o   s o c k e t   # % d   F a i l e d   t o   s e n d   % s   t o   s o c k e t   # % d :   s e n t   % d   o f   % d   b y t e s         F a i l e d   t o   s e t   s o c k e t   # % d   t o   b l o c k i n g   m o d e ,   f a i l e d   w i t h   % d       h a n d s h a k e       F a i l e d   t o   r e a d   h a n d s h a k e   d a t a   f r o m   s o c k e t   # % d       F a i l e d   t o   r e a d   h a n d s h a k e   d a t a   f r o m   s o c k e t   # % d :   r e c e i v e d   % d   o f   e x p e c t e d   % d   b y t e s   H a n d s h a k e   f a i l e d   f o r   s o c k e t   # % d :   U n e x p e c t e d   s i g n a l   c o d e   % d   f r o m   s e r v e r ,   e x p e c t i n g   H A N D S H A K E _ A C K   U n k n o w n   e r r o r       H a n d s h a k e   f a i l e d   f o r   s o c k e t   # % d :   S e r v e r   r e j e c t e d   c o n n e c t i o n :   % d :   % s           H a n d s h a k e   f a i l e d   f o r   s o c k e t   # % d :   I n v a l i d   s e r v e r   t o k e n       h a n d s h a k e   a c k       F a i l e d   t o   c o n n e c t   s o c k e t   # % d :   U n k n o w n   e r r o r   F a i l e d   t o   c o n n e c t   s o c k e t   # % d         F a i l e d   t o   c r e a t e   s o c k e t   # % d   s e l e c t ( )   o p e r a t i o n   f a i l e d               s e l e c t ( )   u n e x p e c t e d l y   r e t u r n e d   z e r o   s o c k e t s           F a i l e d   t o   c r e a t e   p i p e   f o r   p r o c e s s   I / O   s t r e a m   # % d                 F a i l e d   t o   s e t   h a n d l e   i n f o r m a t i o n   f o r   p r o c e s s   I / O   s t r e a m   # % d   F a i l e d   t o   c r e a t e   c h i l d   p r o c e s s             F a i l e d   t o   r e a d   f r o m   c h i l d   p r o c e s s   p i p e   # % d     d a t a         F a i l e d   t o   r e a d   f r o m   s o c k e t   # % d             F a i l e d   t o   s e n d   d a t a   t o   c h i l d   p r o c e s s   p i p e   # % d       F a i l e d   t o   s e n d   d a t a   t o   c h i l d   p r o c e s s   p i p e   # % d :   s e n t   % d   o f   % d   b y t e s             F a i l e d   t o   r e a d   t o k e n s   f r o m   s t d i n                 F a i l e d   t o   r e a d   t o k e n s   f r o m   s t d i n :   r e c e i v e d   % d   o f   e x p e c t e d   % d   b y t e s             F a i l e d   t o   r e a d   t o k e n - c o m m a n d   s e p a r a t o r   f r o m   s t d i n               F a i l e d   t o   r e a d   t o k e n - c o m m a n d   s e p a r a t o r   f r o m   s t d i n :   r e c e i v e d   % d   o f   e x p e c t e d   1   b y t e               F a i l e d   t o   r e a d   t o k e n - c o m m a n d   s e p a r a t o r   f r o m   s t d i n :   e x p e c t e d   0 ,   g o t   % d       F a i l e d   t o   r e a d   c o m m a n d   f r o m   s t d i n               F a i l e d   t o   r e a d   c o m m a n d   f r o m   s t d i n :   c o m m a n d   t o o   l o n g           F a i l e d   t o   r e a d   c o m m a n d   f r o m   s t d i n :   m i s s i n g   n u l l   t e r m i n a t o r             F a i l e d   t o   r e a d   c o m m a n d   f r o m   s t d i n :   i n v a l i d   U T F - 8   s t r i n g   F a i l e d   t o   r e a d   c o m m a n d   f r o m   s t d i n :   f a i l e d   t o   d e c o d e   U T F - 8   s t r i n g                 R e t r i e v i n g   c o p y   t h r e a d   # % d   e x i t   c o d e   f a i l e d           W a i t   o p e r a t i o n   o n   c o p y   t h r e a d s   f a i l e d       W S A S t a r t u p   f a i l e d :   % d       W a i t   o p e r a t i o n   o n   c o n n e c t   t h r e a d   f a i l e d   R e t r i e v i n g   c o n n e c t   t h r e a d   e x i t   c o d e   f a i l e d     P I D   W a i t   o p e r a t i o n   o n   c h i l d   p r o c e s s   f a i l e d     R e t r i e v i n g   p r o c e s s   e x i t   c o d e   f a i l e d   e x i t   c o d e       pC @   €C @   ÈC @    D @   8D @   pD @   °D @   a d d r e s s   p o r t         t o k e n - s i z e     c w d   E r r o r   p a r s i n g   s e r v e r   a d d r e s s         I n v a l i d   s e r v e r   a d d r e s s     I n v a l i d   s e r v e r   p o r t :   % d   I n v a l i d   t o k e n   s i z e :   % d     =       U n k n o w n   o p t i o n :   % s     O p t i o n   % s   r e q u i r e s   a   v a l u e     P r o c e s s   l a b e l   n o t   s u p p l i e d     S e r v e r   p o r t   n o t   s u p p l i e d         1 2 7 . 0 . 0 . 1       % d :   % s     % s :   % d :   % s     %s
                 U%s\       h   Y  M      U%s\          |Y  |M      U%s\       €  �Y  �M      U%s\                   ”                                                                                        p @                   øB @    C @                      RSDS3Þ¬«öÛmN¢¼H^„‘gØ   C:\Users\Dave\source\repos\windows-process-wrapper\Release\ProcessWrapper64.pdb                   GCTL   À-  .text$mn    À=     .text$mn$00 Ò=  6   .text$x  @  ø  .idata$5    øB     .00cfg  C     .CRT$XCA    C     .CRT$XCAA   C     .CRT$XCZ     C     .CRT$XIA    (C     .CRT$XIAA   0C     .CRT$XIAC   8C     .CRT$XIZ    @C     .CRT$XPA    HC     .CRT$XPZ    PC     .CRT$XTA    XC     .CRT$XTZ    `C  ´  .rdata  Y  ü  .rdata$zzzdbg   \     .rtc$IAA    \     .rtc$IZZ     \     .rtc$TAA    (\     .rtc$TZZ    0\  À  .xdata  ð_  ´   .idata$2    ¤`     .idata$3    ¸`  ø  .idata$4    °c  ˜  .idata$6     p      .data    p  à  .bss     €  H  .pdata   �     .gfids$y        À  .rsrc$01    À¡  èÚ  .rsrc$02                                     	ò0Ì<  x   ! t d    S  0\  !      S  0\   ¢	ðàp`P0  Ì<  H    	 0  Ì<  ˆ    4 òàÌ<  p   ! t �  \  ˜\  ! d \  æ  ¬\  !   \  æ  ¬\  !   �  \  ˜\  * 4× Ð 
ðàP  Ì<  p  ! tÖ dÕ ð  =  ô\  !   ð  =  ô\  !   tÖ  dÕ ð  =  ô\   t d 4 2à# d T 4 ’pÌ<  H    d 4 ÒpÌ<  `   /	 d T 4 
 p  Ì<  @   ¢  Ì<  H   '
 d T 4 ’ðàpÌ<  @   $ 6 p0P  Ì<  ¨  !& &ô= ä< d: À!  i"  è]  !   À!  i"  è]   d 4
 RpÌ<  (    d 4 2p 20@ @ä ;d 
4 
2p T
 4	 2àp` Rp`0
 t d T RðàÀ! 4
 ð+  ^,  �^  !   ð+  ^,  �^   b  ! 4 à,  -  Ì^  !   4 à,  -  Ì^  # ²0Ì<  P   -
 -t "4
 d T rà       	 d	 4 Rp0<     <1  R2  Ò=  R2   2P	 	b   rp`0	 "  0<     o5  ú5  ð=  ú5   P   4
 rP 4	 2P 4º ¸ P   t 4 2P    0   B         ¸`          Re   @   b          Že  HA  Ða          Æe  A  €b          <h  ÈA  pb          \h  ¸A  Øb          ~h   B  xc           h  ÀB  Èb          Àh  B  ¸b          àh   B                      °c      ¼c      Ôc      äc      ðc      
d      d      .d      Dd      Xd      hd      vd      †d      œd      ®d      Äd      Öd      îd      e       e      6e      i       j      j      òi      Üi      Âi      ¦i      ’i      ~i      `i      Di      0i      i              ¤e      ¼e      še      4j      >j              je      s      €      €o      €
      €xe            €—      €	      €‚e            €`e            €        êe              Êg      >f      Hf      àe      Øe      þe              ´g              ²f              Þf      êg      þf      rg      "h      0h      dg      Æf      †g      h      |g      Vg      ¢f      �f      g      @g      8g      ôe      *g              Úg      Hg      tf      ,f      f      Zf              TReadFile  SetHandleInformation  ÇGetStdHandle  ñWriteFile ¹WaitForMultipleObjects  Ô CreatePipe  »WaitForSingleObject ÔMultiByteToWideChar 4GetExitCodeThread VGetLastError   CloseHandle ç CreateThread  GetCurrentProcessId Û CreateProcessW  3GetExitCodeProcess   FormatMessageW  XInterlockedFlushSList ÝWideCharToMultiByte ZInterlockedPushEntrySList TInitializeSListHead YInterlockedPopEntrySList  KERNEL32.dll  H WSARecv   WSAConnect  M WSASend  InetPtonW WS2_32.dll  F wcsstr   __C_specific_handler  > memset  VCRUNTIME140.dll   free   malloc  n wcstol  ! _errno    _aligned_free  __stdio_common_vswprintf_s    __acrt_iob_func  realloc  _aligned_malloc  __stdio_common_vfprintf  __stdio_common_vswprintf  @ _seh_filter_exe B _set_app_type 	 __setusermatherr   _configure_wide_argv  5 _initialize_wide_environment  ) _get_initial_wide_environment 6 _initterm 7 _initterm_e U exit  # _exit T _set_fmode   __p___argc   __p___wargv  _cexit   _c_exit = _register_thread_local_exe_atexit_callback   _configthreadlocale  _set_new_mode  __p__commode  4 _initialize_onexit_table  < _register_onexit_function  _crt_atexit g terminate api-ms-win-crt-heap-l1-1-0.dll  api-ms-win-crt-convert-l1-1-0.dll api-ms-win-crt-runtime-l1-1-0.dll api-ms-win-crt-stdio-l1-1-0.dll api-ms-win-crt-math-l1-1-0.dll  api-ms-win-crt-locale-l1-1-0.dll  ®RtlCaptureContext µRtlLookupFunctionEntry  ¼RtlVirtualUnwind  ’UnhandledExceptionFilter  RSetUnhandledExceptionFilter GetCurrentProcess pTerminateProcess  pIsProcessorFeaturePresent 0QueryPerformanceCounter GetCurrentThreadId  ÝGetSystemTimeAsFileTime jIsDebuggerPresent mGetModuleHandleW  ; memcmp  < memcpy                                                                                                                                                                                                                                                                                                                                                                                                                                                          2¢ß-™+  Í] ÒfÔÿÿÿÿÿÿ         /                              ØU @           `& @   èU @           @' @   øU @           �' @   V @           Ð' @                                                                                                                                                                                                                                                                                                                                                                      S  0\  S    @\    \  X\  `  f  h\  p  Š  „\  �  \  ˜\  \  æ  ¬\  æ  ¦  À\  ¦  ®  Ô\  ®  Í  ä\  ð  =  ô\  =  £  ]  £  Ç  (]  Ç     8]       (]    ï  P]  ð    d]    X  €]  `  ø  ˜]         ¸]  €   ·!  È]  À!  i"  è]  i"  µ%   ^  µ%  Ï%  ^  Ð%  Q&  ,^  `&  :'  D^  @'  „'  T^  �'  Ç'  T^  à'  î)  \^  ð)  ‹+  p^   +  å+  „^  ð+  ^,  �^  ^,  ¿,  ¨^  ¿,  Ù,  ¼^  à,  -  Ì^  -  .-  Ô^  .-  Ÿ-  è^   -  _.  ü^  `.  ç/  _   0  !0  (_  $0  ß0  T^  à0  ð0  Ü_  ð0  	1  Ü_  1  ƒ2  ,_  „2  –2  Ü_  ˜2  Ì2  T^  Ì2  �3  \_   3  4  d_  4  M4  Ü_  P4  ™4  T^  œ4  e5  ˜_  h5  6  p_  6  (6  T^  (6  S6  T^  T6  £6  T^  ¤6  »6  Ü_  ¼6  h7  ¤_  ˜7  ³7  Ü_  Ð7  9  °_  9  l9  Ü_  |9  ´9  Ü_  ´9  þ9  D^   :  J:  D^  T:  <  À_  Ì<  é<  Ü_  ì<  E=  Ô_  `=  ±=  è_  Ð=  Ò=  Ð_  Ò=  ð=  T_  ð=  >  �_                                                                                                                                                                                          
         E   r   7                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            0  €   `  €   x  €   �  €                  ¨  €   À  €   Ø  €   ð  €               e    €                    €                  8 €               	  P                 	  `                 	  p                 	  €                 	  �                 	                    	  °  À¡  h          (¦  ¨          Ð¶  ¨%          xÜ  ›          �w >           Ðw X          ({ }          (                                                ­�: ©|6	º�EcÈ™OÌÍ�Q÷Ò¥^ùÕ©cäË£c¡Ò¦_GÛ�5×žC         k7Ë *N H%ŒK/G»‹=xÃ�?õ³‚4ÿ«{/ÿ»‰9ÿÏšHÿ×©aÿÓ¬oî³‘[’²…>²�Q ˆ<  n:Î s<ÖV-§‰Y9Gb˜l3ì�\!ó_C—R:dgI�›o(æ¾�Dÿ¬˜xÿ¸�sÿÛµx±ÓžKÕ¤W e/Ên9Î’o?ÇøsI»ïoH¨ÿV4‰â;wCqV'_u_<–\F%l™‰oíÄ´›ÿÕªeÿà»‚ÿÔ®r�] yIÑLŠ`Øï�yßú¢€ÒÞ¢{¡ü†a¥ÿ_:¡Ù�`>ÅÂ£qÙxoa9ÄÅÇÜÊ¯ƒÿ×¨]ÿÝ´tÿÛ¼ŒèÕ¬lF‘jÞ`¢äÀ˜wÕÈaK¾�D¹©}>ÿ’jHÿ¹�VùØ·ƒ‚YPAMÁ¿»õ·›pÿÖ©bÿÝ³rÿäÃ�ÿá½„§‘xRlš~d\ˆmyL]Idšv;%Ã˜U}È ] ÂŸg x9UhK‘­†Iÿ»�KÿÓ¦_ÿÚ°nÿâ¿‰ÿÕºŽØÇ©{ûÜ¼ŠúÒ³�è’{W�´”b¯¤†V—´ŒM«³…<Ñ†`%°ZÆÅ‘?ÿØ¨[ÿ‰mAÿ€g@ÿÞ¾ŒÿçÍ¥ùÎ±ƒûæÅ’ÿäÂŽÿ¾žkþÕ«hÿÍžUÿº‡7ÿ�e$÷[AÛ›n'üÐ›Iÿ×§[ÿ‰mAÿ€g@ÿÞ¿ŒÿçÍ¥ùÆ§vãÝ³rÿÚ°lÿÙ¬cÿÎ›Jÿ½‰9ÿ•j&ÿ[AýhJþ®}/ÿÐ™DÿÓŸMÿÙ¬eÿÚ°mÿâÁŒÿÖ»�Ø½¡t§Ú°nÿ×ªbÿÍ�Oÿ¸…3ÿ¢r(ÿZ ÿS;ÿ�\!ÿ½‡1ÿÎ•<ÿÓ OÿÚ®iÿÜ³qÿåÆ•ÿá¾†¥ª“pIÓ¯víÆ’@ÿ¿ˆ0ÿµ€-ÿ�f%ÿnNÿV=ÿ“i%ÿÂ‹2ÿÎ˜AÿÖ§\ÿØªbÿÝ¶wÿäÅ’éÚ°lC?2Æ©{’Ï£]ÿ»…1ÿšn'ÿ‹c$ÿlMÿfIÿ¬|/ÿÎ˜CÿÓ¡PÿÕ¤WÿÛ±nÿá¾‡úÜ³r|µc ¸œn §ŒbÙ¹‡±Í¥gÿŸw6ÿŠc$ÿhJÿjKÿ±„=ÿÕ¢PÿÖ§[ÿÙ®iÿÝ·zëÛ³r|Ì’9
ÑœI , êÅˆ Ô­qâ¿ˆ“Ü¹�ïÇ¢gÿ³ŽTÿ½˜]ÿ×±uòÕªg½Öªg¶×¬g‹Ñ S9ÁÈŒ1             Ù¬e Ó£Vàº~IãÀˆ äÂŠËåÃŒÀá¼‚hÂ�@®o¡M ¢Q             ð  À  €                                          €  À  à  (       @                                                                 ‚Q �\ …]«:eÃ•K¿ÉœTë¾•TðÅ�_ñÓ¨cóÐ£]ÙËœR­Â�Bi—i ®f·t                                                                 �n# •e¯�8a»ŽHÌÂ—SóÕ¦[ÿÔ£TÿÒ Qÿ×©`ÿÙ­gÿÚ¯lÿÛ²qÿÐ©løÀ�eÍÓ£W–É‘77·o¿~                                            ‚[ [9¥{9zÉšO÷ÐœIÿÊ“=ÿÇ�7ÿÃ‹3ÿÄŒ3ÿÍ–?ÿÐšDÿÒ�HÿÔ Oÿ×ªbÿÝ·zÿà»ƒÿÛ²rêÔ¡QŠÙš6Æ�A ý¿%                             m9Ì     H&†29'  ÿ ©|5t»ŒBñË•@ÿ»…1ÿ«y+ÿ¤t)ÿšm'ÿšm'ÿ­{,ÿ¼‡2ÿÇ�9ÿÏ™CÿÔ¢QÿÖ¥XÿÛ¯iÿÖ²yÿ¸›lþ™u<Ã³�26\aj Ã‚                     l8Ì p<Ð p;ÑDN)“°%—l%0»Š<åÅŽ7ÿ°},ÿ—k&ÿoOüT<ÜG3·G2³Z@ÑxUö¡r(ÿÃŒ5ÿÐ›GÿÔ¢Qÿ²ŠIÿ”…nÿ�xjÿ°˜rÿÙ°oÙÏ—>Bá¹z Æˆ(             p=Í n:Î n:Î6k9ÉÜ@"w¯  $U<�‰b'ÿ€[ÿnOÿQ:ë(
w         "OV=Ç˜l&ÿÅŒ3ÿ©=ÿœ”ˆÿ¶±«ÿº™eÿã½„ÿåÆ–ÿÛ±nÙÌ“86ÑœG ÄŠ-         n:Í n:Ín:ÎÂm9Ìÿ\0®òX-§åX.�õY0‘ÿM+{ÿ6Oÿ(¢  " :+dK#JlQ&hnZ<>'S;¹�d#ÿ�‚nÿÛÝßÿ®˜vÿÕ§^ÿÛ¯lÿá½†ÿåÆ•ÿØªaÂÍ�/Í”8     n;Ì n<Én:Î‰m9Îÿn9ÎÿuCÒÿQÕÿ†YØÿ…YÙÿQ×ÿsBÏÿ`3´ôB#}£ (jQ)_yZ(÷P:ÿ‘}]ðspkI -RF4áÊÊÉÿ×ÔÑÿ·“\ÿÚ«bÿØªbÿÜ²qÿãÁ�ÿß¾Šÿ¹ŒDvÿÓ… W8 m9Í m8Í7n:Îè}OÓÿ™tÝÿ©Šãÿ¬�äÿª‹äÿ¦†àÿ¡�ÛÿœyÜÿ�dÝÿo?ÇÿD#‚ª?3!M¡IíÄŸeÿÀ¨ƒàlcV*   †††ÈëìíÿÇ¿³ÿÀ“LÿÙ«cÿØªbÿØ«dÿà¹}ÿØ¼’ÿºžqÏÎ›J0Ì›M P»sAÏ—ŽeÙÿ§‡âÿ§‡ãÿ¡ßø¡~âÆ˜u§Èª�sÿšqXÿxY[ÿz_˜ÿ…^ÐÿT,¡ø/'­ u/ñâ½ƒÿ×¼’¼©_ (()'¬¬¬çôõöÿÀ´¢ÿÂ�=ÿØ©`ÿÙ¬fÿÚ¯jÿÞ¶xÿæÇ–ÿéÍ¡ÿØ«b—·j }OÒ(’kÛÝ£�áÿ¢áÿ£âÿ…h¹ÝK:s%¤x+3¼�FÜÉ”?ÿ¦v&ÿvT$ÿ`CEÿ[==ÿ…^(ÿÊ™LÿÞ¿�ýÁ¥zr†‰� 655C···õøøùÿ¹®�ÿ¾Œ=ÿÚ¬cÿÜ²pÿÜ²pÿÜ³rÿâÀ‰ÿéÏ¦ÿßºÞÏ˜A*•mÞ¡~â^¢á‰¢á¯£€âÐ‘rÊåJ9gdñÄù «‚@bÉœVïÌšKÿ¿Œ9ÿ³‚0ÿ¼Š7ÿÑ¡TÿÛ·þË¯�²®…E   <0®©§¤ÿêëìÿ¤œ�ÿ”n2ÿØ©`ÿÜ²qÿÜ²pÿÜ²oÿàº~ÿçË�ÿåÇ–ûÕ£Sl	 	    —sÿ­‡þ¦‚ë)bLŠ+   �g£|@KÅ�\ÂÍ¤cïÔªhüÖ¯rùÐ®yÞÈ§s�›wA¤v-"!+fIæ’q>ÿ©Œ]ÿ±‹Oÿ—o1ÿÐ¢YÿÜ²pÿÜ²pÿÜ²oÿÞ¶wÿåÆ”ÿãÉŸÿÉŸ\�zb=»Ÿ‚UÆŠpF¡t\7yZF%R6(/       Ð³o �q,®‰M5»–[O°ŽXE�o;%`H"¤z9{™n+œ 4rQë°}+ÿÌ–@ÿÔŸLÿÄ“EÿÔ£WÿÝ±lÿÜ±mÿÝ´rÿÞ¶wÿæÇ•ÿÒ¿ þ¬‘f­§ŒbòæÅ’ÿà¿ŒÿØ·„ÿÌ«yû¼�lð�xR†E7!)e<�x_7€lU1`VC%,sY1`‡j=y¢I¢¸‹EÙ©z1þƒ]!€2#^€[ ûµ�/ÿÏšGÿÔ¢RÿÖ£SÿÓ¤[ÿœw<ÿ†d.ÿÁœbÿá¼�ÿçÊœÿïÛ½ÿâÁŒò®”jýçÇ”ÿåÅ“ÿæÆ”ÿçÇ”ÿéÉ–ÿ¤�iéPC-ÃÒ®uþ×²vÿÆ¤mù†mFÛË¢aýÎ¡ZÿË—Fÿ´�0ÿˆ`"ßI4`_DÒ q(ÿÆŽ7ÿÕ¤Vÿ×¨]ÿÛ®eÿ£Šaÿ
ÿ0-(ÿw`;ÿÙµ{ÿçÉšÿïÛ¼ÿäÄ‘ý°—oýçÇ•ÿåÅ“ÿåÅ“ÿåÅ“ÿçÇ•ÿÁ¦|ÿ„oMÿà»€ÿß¹|ÿÛ³sÿ¿”QÿÊ”AÿÃ‹4ÿ¯|,ÿŠc#ü\B­L6Ì€[ ÿ®|,ÿÌ•=ÿÖ¦Yÿ×§\ÿÛ®dÿ£Šaÿ
ÿ0-(ÿw`;ÿÙµ{ÿçÉšÿïÛ¼ÿäÄ‘ý®–pòçÇ•ÿåÅ“ÿåÄ‘ÿäÂŽÿãÀŠÿÛ·ÿÊ¢bÿØ¨^ÿÕ¤VÿÔ¡PÿÎ˜Cÿ¼…/ÿ§w*ÿŒd#ÿjKìC0àaEÿŽe$ÿ·‚.ÿÍ—BÿÔ OÿÔ¡PÿÕ¢SÿÓ¥\ÿœw<ÿ†d.ÿÁœbÿá¼�ÿçÊœÿïÛ½ÿâÁŒò¤ŽjÙá¾†ÿÞ¶xÿÜ³rÿÛ°mÿÚ®iÿÙ®hÿÖ¦YÿÓžLÿÓŸMÿÐœKÿÀ�<ÿ¢s)ÿ�e$ÿuSÿK5ùQ9þrQÿšm'ÿ»†1ÿÏšEÿÓŸLÿÓŸMÿÓŸLÿ×¨]ÿÝ²mÿÜ±mÿÝ´rÿÞ¶wÿçÉ™ÿÓÀ¢þ­’g­”^¬Û¶|ÿÙ¬eÿÙ­fÿÙ­gÿÙ­gÿÙ­gÿØªaÿÒŸNÿÇ“Bÿµ‚3ÿ¬z,ÿ r(ÿ~Z ÿaEÿ@.ÿ^Cÿƒ]!ÿ£t)ÿÃ�7ÿÏ—?ÿÐ™BÿÒœHÿÓ OÿÚ®hÿÜ²qÿÜ²pÿÜ²oÿÞ¶xÿæÉšÿäË¤ÿÉŸ]�zgJlÕ¶†ûÚ¯iÿÙ­gÿÙ­gÿÙ­gÿÙ­gÿÔ¥[ÿ¿Œ<ÿ­z,ÿ«y+ÿ¨w*ÿŽe$ÿZ ÿR:ÿJ5ÿjKÿ�f%ÿ®|,ÿÈ�5ÿÏ–>ÿÏ—?ÿÏ—?ÿÔ¡QÿÛ°lÿÜ²pÿÜ²pÿÜ²oÿà¼‚ÿéÎ¤ÿæÈ™ûÕ£SkI9*ÃªƒÞß·yÿÙ­fÿÙ­hÿÖ©aÿË™KÿÁ‹5ÿ»„/ÿ®|,ÿ©x+ÿ’h%ÿ�\!ÿxVÿF1ÿV>ÿvTÿ–k&ÿ»…/ÿÉŽ2ÿË’7ÿÏ–>ÿÑšEÿ×§\ÿØ«dÿÚ®iÿÛ±nÿÝ´sÿäÄ�ÿëÓ¬ÿß¹~ÛÏ—@'   £�i–áÂ‘ÿØ¬fÿÎžQÿÃŽ:ÿ¾‡0ÿ¾‡0ÿ¾‡0ÿ¹„/ÿšm'ÿ‚\!ÿ‚]!ÿiKÿB/ÿ_Dÿ€\ ÿ�p(ÿÀ‰0ÿÈŽ2ÿÈŽ2ÿÍ–?ÿÖ¦ZÿØªbÿØªbÿØªbÿÙ«dÿÞ·zÿçËŸÿèÍ£ÿ×©_� = „kC nX67Ï¶ŽéØ¯nÿ¿‰2ÿ¾†/ÿ¾‡0ÿ¾‡0ÿ¾‡0ÿ¬{,ÿ�e$ÿ…_"ÿ�\!ÿZ@ÿC/ÿdHÿˆa"ÿ¦v*ÿ¾‡0ÿÈ�4ÿÌ•>ÿÒŸNÿ×§]ÿØªbÿØªbÿØªaÿÚ­hÿá½…ÿèÌ¡ÿÞ·yÝÐšE+ÒžL '	    £ŠbŠßÂ“ÿÎ Vÿ¾‡1ÿ¾‡0ÿ¿‡0ÿ²~-ÿ‘h%ÿŒd$ÿŒd$ÿ‚]!ÿY@ÿL6ÿmNÿ“i%ÿµ‚1ÿÆ�9ÿÐœHÿÔ¢QÿÔ¢RÿÔ£Tÿ×¨^ÿØªbÿÙ¬fÿß¹~ÿæÈ™ÿà¼ƒ÷Ñ›HhòÐ› Ì—C     pW. [CÁ¦{ÂäÅ•ÿÏ VÿÀ‰3ÿµ�-ÿ•j&ÿ‹d$ÿŒd$ÿ�d$ÿyVÿbEÿ]Bÿ€[ ÿ­|,ÿË”>ÿÑšCÿÓžKÿÔ¢RÿÔ¢RÿÔ¢RÿÕ£TÿÙ¬eÿßºÿåÇ—ÿâÀŠüÓ¡QŽÆ‡#	Ì’7           Ç¢f Šl>6Ò¶‰ÙæÈ˜ÿÓ§bÿ§{4ÿ‹c$ÿ‹c#ÿŒd$ÿŠb#ÿmMÿcFÿ_CÿkLÿ˜m(ÿÉ”?ÿÑšCÿÒœFÿÔ¡QÿÔ¢RÿÕ£Tÿ×©_ÿÞ¶yÿåÇ—ÿà½„÷Ô¡RŽÊ�-Í“9                 + ÿÿê ª‡NBÝ¾�ÙæÉšÿÔ­pÿµ‹Hÿšq/ÿŽf&ÿ€[ ÿcFÿaDÿaDÿ`Dÿ‰f.ÿÓ¤XÿÕ£SÿÓŸMÿÖ¦ZÿÙ¬fÿÜ´tÿà»�ÿÞ¹ÿØ­hÝÑœJgÇˆ&	Ë�3                         mI ÿÿÿ Î V6à¼ƒÃçÉ›þãÁ‹ÿÕ­mÿÂ˜Uÿ¦~>ÿ�i1ÿŠg0ÿ•p6ÿ°‡FÿÏ¡Xÿß¹}ÿÛ´vÿØ®lÿÛ´vÿÞ¸}ÿÛ´uùÖ©bÙÎšG‹ÆŠ,)­V À}                                 ±p Ý²m ÐœIÛ²q‹äÄ‘ëçËžÿåÆ”ÿàº}ÿÜ³rÿÛ±oÿÝ´sÿá»~ÿãÁŒÿãÃ�ðÍŸWŠÂ‘D‰Æ•H‘Í™F{É‘9OÂ…%¬e ¶r                                                 Ì’6 Å„"Ô¤V9Ü´t—âÀŠÛäÅ”÷æÈ™ÿæÊœÿçÊ�ÿæÉ›úá¿ˆ×Ù®ifÉ�.ÐšF                                                                                 Ê‹( À~Ð›F$Ö§[WÙ®h‚Û±n”Ú°m‰Øªb`Ñ�K"ºn Å…#                                                     ÿÀÿÿ€ ÿ  ?ù  ð  à  À €  €                 À                                      €  €  À  à  ð  ø  ü  þ ÿÿ€?ÿ(   0   `           $                                                                                  ?, Ê�+ ¦t$¯|,e¶…7¨¼�A×Â“Hó§�Dß¯‰LàÍžT÷ÊšNæÇ•FÌÃ�=£¿ˆ2nº‚(6¼ƒ"   õàX                                                                                                                 ”d uE ¡q%'šo,t™u<³ËŸYûÔ©dÿ×¬gÿÙ­fÿÏ¥bÿÓ­pÿÞ·zÿÝ·zÿÜ¶zÿÚ³uÿÕ¬küÏ¢Zê¨~<¢šo*LÆŠ*(¾}Â„                                                                                                      �n# ˜i¥v,d¶‰AÛÇšTþÂ˜Uþ×¨]ÿÕ£TÿÒŸMÿÐœHÿÑ�IÿÖ¥XÿÖ§\ÿ×©_ÿØ«dÿÚ¯kÿÜ³sÿÞ·zÿ¼žnúÏ«t÷Ô¤ZâË”<�ÅŠ+.¿Â…$                                                                                         Šb! }T¨z1†Á”L÷Ô¤XÿÓžKÿÎ˜AÿÍ–?ÿÊ’8ÿÈ�4ÿÇ�2ÿÈ�4ÿÐ˜AÿÑ›DÿÒ›EÿÒœGÿÓŸLÿÕ£TÿØªbÿÝµwÿá½…ÿãÂŽÿÞ¹~ÿÒ¢WãÉ�7|Ã‡(ÇŒ/ ¼                                                             H*t <$`$    šm& ¤s%
ˆb&n©‚DæÕ£TÿÏ˜BÿÅŽ7ÿ¼†1ÿ»…/ÿ¹„.ÿ¶�-ÿ´€-ÿ¶‚.ÿÂŒ5ÿÇ�9ÿË”=ÿÏ˜AÿÑšCÿÑšCÿÔ¡Pÿ×¨]ÿÚ¯kÿÝµwÿá½…ÿãÃ�ÿÜ´týÒœGÀÐ“3;íšÙ—/                                                     o<Î žUÿK(‹^-S8_ «‚> žq)h¸ŠA÷Æ“CÿË”<ÿÀ‰3ÿ²-ÿ¦v*ÿ r)ÿ�p(ÿ–j&ÿ’h%ÿ•j&ÿ¥u)ÿ°},ÿº„/ÿÂ‹4ÿÉ’:ÿÑœGÿÔ¢RÿÔ¢QÿÔ£Sÿ×¨^ÿÝ³pÿÕ¯uÿ¹Ÿvÿ”{Tÿ‰e,ã±0_ë§;Ê‘6                                             m9Í l8Ìo:ÐtF%ƒÓ)R9 ˜k$'°‚:ÚÎšGÿÅŒ2ÿ·‚.ÿ¬z,ÿœo(ÿˆ`"ÿoNÿ\BýM7òG2éK5î_CúvTÿ’h%ÿªx+ÿ¼…/ÿÊ’9ÿÑ�JÿÔ¢RÿÕ¢RÿÓ¡Qÿ¥€Dÿ{mWÿxtpÿ`VGÿ¶Ÿ{ÿÖ«eñÎ•;rÆŠ,É�4                                     n;Í o<Ìn:Ïrh7Ãø2\Ÿ   ÄŠ0 |X³ƒ5ÿ¾ˆ4ÿ±~,ÿ¡s)ÿ‘g%ÿvSÿI3õ&
´f9   *1Q&
“F2ßrQÿ¡r)ÿ¿ˆ0ÿÉ�5ÿÐ›GÿÐŸOÿŠl;ÿ€{sÿ¿ÀÁÿ�‚oÿËªwÿçÆ”ÿèÌŸÿÙ­gôÍ”:rÈŽ2Ë“:                             m9Ì {Eç n:Í^o;Ðñ]1®ÿ,   %5L6Î€[ÿwUÿjLÿcGÿX?ÿ5&ä`   
                     )*žeHø�p(ÿ¿‡0ÿÈ�4ÿ†f4ÿˆ…‚ÿäåæÿ ˜Œÿ¸�SÿÞ´rÿà¼„ÿäÄ’ÿçËŸÿ×ªañÌ’7_ÿÿ� È�7                     q?Î m:Í n:Í<n:Îâo:Ïÿe5½ÿM)‘ïN)’çM)�êN*†üP,~ÿH)lÿ:"Pÿ&,ÿøc#         	  4(I81?1&   
!cF÷¤t)ÿ’h&ÿ{ulÿáâãÿÔÓÓÿ—}SÿÙªaÿØªbÿÜ³sÿá¼…ÿåÆ•ÿæÉ›ÿÔ£TâÊ�3<Ì’7 Ê‘8                 n;Í n;Ìn:Î¿n:Îÿn:Îÿn:Ïÿo:Ðÿn9Ðÿn8Ðÿn8Ñÿm8Ñÿm8Ðÿk7Ìÿd4½ÿO*–û6e¹%G    fM$ƒvX'ÜmP#îx[.æ…u[­AAA)   )“iJÿdWBÿÉÉÊÿ÷÷øÿ¨¢˜ÿº’SÿÚ«cÿØªbÿØ«cÿÝ¶xÿá½‡ÿçÊœÿãÁŒÿÑ›E¾Ï“6Ð—< ®{*         n<É _�  n:Í{n:Îþn:Îÿm8Îÿm9ÎÿsAÐÿQÔÿ‰_×ÿ�fÙÿ�gÙÿŠ`×ÿ�TÕÿwEÒÿp;Ñÿi7ÆÿO)“î"@x   $=“o6ódHÿ-
ÿM9ÿº§‰ÿa`]„´±¬  &Ï”“�ÿèèéÿôôöÿ”…lÿÐ£\ÿØªbÿØªbÿØªbÿÙ¬fÿß¹ÿâ¿ŠÿéÎ£ÿÐ©kù¦v*T°2 _=         n;Í n;Í.n:Îàm8Îÿq>Ïÿ…ZÖÿ¢€ßÿ³—æÿ·œçÿ´˜æÿ°“æÿ®�æÿ­Žæÿ«‹æÿ¦…ãÿ—pÝÿ}OÔÿo;ÐÿV-¡÷!?s   hBºÀ™[ÿÁ—VÿÌ§kÿº¨‹çFDAAlha    III«ÈÈÈÿñññÿççèÿ“xMÿÖ¦YÿØªbÿØªbÿØªbÿØªaÿÛ°nÿá¼„ÿáÂ‘ÿ¦”xù™zI¤Ð›G$È–H     m9Ì m:Êm9Í�n;Îÿ„XÕÿ§‡áÿ´™æÿ­Žäÿ¥ƒâÿ¢áÿ¡~áÿ�{Úÿ›yÊÿ–t¹ÿ�m°ÿŠl¸ÿ™yÒÿ£�ãÿ‰^Øÿp<ÏÿG%‡ê>:*Š©€@ÿÞµuÿäÌ¦ÿÂ§}¼g. %
    zzy×ÜÜÜÿøøøÿÚÚÚÿ˜u=ÿÕ MÿØªbÿØªbÿØªbÿØªcÿÙ­gÿà¼‚ÿäÄ“ÿçÑ­ÿáÀ‹ÿÓŸN�Ë”;ÑžL m9Ì l7Ë(r@Ïá’kÚÿ¬�äÿ§†ãÿ¢~áÿ¡~áÿ¡~ßø¢áËž{ÜŠŠikÉ²ˆWÿ²‚>ÿ˜m/ÿvU+ÿW@?ÿnU“ÿ–sØÿwIÍÿO'œÿ/ÉaEß¼ˆ6ÿÜ²pÿêÒ¬ÿÈ¨uŸ    4˜——ïãããÿüüüÿÒÑÐÿžw9ÿÒšAÿ×¨_ÿÙ«dÿÚ­hÿÛ°mÿÜ±oÿÞ·zÿäÄ‘ÿèÌ ÿëÔ®ÿØªbáÐšE(ÒžM ‡[Ø xHÐs“lÛÿ¥„âÿ¢áÿ¢áÿ¢áÿ¡~ßÿpWœÉ^I�“j ¤v)[À“KõÎ›JÿÃŒ5ÿªy*ÿ„^ÿX?ÿL5JÿH-\ÿ@(:ÿ`C!ÿ¥u*ÿÎ›Jÿá¾‡ÿÕÀ�ô²�TXÀ¢r **) $##G¥¥¥øåååÿþþþÿÏÎÌÿ y:ÿÒš@ÿØ©`ÿÜ±oÿÜ²pÿÜ²pÿÜ²pÿÜ³qÿâ¿‰ÿæÆ•ÿìÖ²ÿà¼‚ÿÒ�JrÕ¡N l;Ç“lÛš¢áî¢áú¢áÿ¢áÿ¢áÿ£€ãÿw\¥ï!A:+0 ¡n�l2ƒÆœZýÔ NÿÃŒ5ÿ±~-ÿ�o'ÿŠb ÿ†_ÿ—l$ÿ´�/ÿË–CÿÜ³rÿåÊ ÿ½Ÿq¯c: E2    �¥¥¥ÿæææÿþþþÿÌÌÌÿsY1ÿË˜HÿØ©^ÿÜ²pÿÜ²pÿÜ²pÿÜ²pÿÜ²oÿß¹~ÿåÅ’ÿêÐ§ÿèÍ¡ÿÕ£U»Ï™DˆbÌ¡~ß¡~à.¡}àN¡~àt¡~à›¢~á¿¢âÜ™xÕôL;k®   oP  Ÿp&¸ŒE¹Ê ]þÒ£XÿÎ™EÿÅ�9ÿÂŒ7ÿÃ�7ÿË•?ÿÔ¢Sÿß·yÿÜ¾�þ½£xÆ±…@4·‘X %	 VA1ö›š™ÿÜÝßÿôõöÿÁÀ¾ÿeM'ÿšs6ÿØ¦XÿÜ²oÿÜ²pÿÜ²pÿÜ²pÿÜ²pÿÝµuÿäÃ�ÿçÊœÿìÖ²ÿÙ­gèÐšF6                žyß štßžzß
 }ß¤�å9kS–O §x.  p$•r;|ÆžaìÕ«jÿÕ«hÿÚ®gÿÙ®jÿß¸{ÿÙ·‚þÑ´…ôÅ¢k°�h.&i žq*    +�uTÿy`7ÿ‰uTÿ–‚cÿ™~Rÿ§|7ÿ‚a+ÿÔ¢SÿÛ°mÿÜ²pÿÜ²pÿÜ²pÿÜ²pÿÜ²qÿâ¿‰ÿæÆ–ÿí×´ÿáº~üÐœHn#Q8*d? "                                  ¤q" šg­5@ª„H„¸•^»Î¨mÜ»›iÏÉ¦nËº–^™—t=D¬u"  œt4M—m)€   - —…_"ÿ¨w)ÿ¼†0ÿÌ˜EÿÔŸLÿÆ”Dÿ•o2ÿÒ PÿÛ¯kÿÜ²qÿÜ²pÿÜ²pÿÜ²pÿÜ²oÿà»�ÿåÆ”ÿãÌ§ÿ°˜tï•o3YbN.Ëº˜dÿ«ŒZô”wJâ|b:ÆdN+¤K:}1$W5                    e9 5  œh¯{)�n#¢g­J    SA%(›w=†©|3î‡`"—    0" †_"ÿ©x*ÿÁŠ4ÿÑœIÿÓŸMÿÒžLÿÍšJÿÓ OÿÚ®iÿÜ°mÿÚ°lÿÝ³rÿÝ´sÿÝ´tÿá¼ƒÿèËŸÿàË«ÿ´¥�õ¶�T›{eAçåÄ�ÿèÇ”ÿæÅ‘ÿâÀ‹ÿÙ·�ÿË©tÿ¸˜eü£…Tò�h@¡	7*C3g5(Z%D5		''5D3KhQ,r‘rA©´‹Jã¾‹;þ•j&þwUb   C/Ì�e$ÿ«y+ÿÂ‹5ÿÑ�KÿÓŸNÿÓ NÿÔ OÿÕ£TÿÖ§]ÿ»‹?ÿ¨|6ÿº’SÿÛ¶zÿà»ÿâÀ‰ÿéÏ¥ÿîÙ¸ÿòßÁÿÚ¯iæ†oKõåÅ’ÿåÅ“ÿåÅ“ÿåÅ“ÿæÆ”ÿæÆ•ÿçÇ•ÿêÈ•ÿ¹žtõ!WUE-z¸–`ý¹–]ý­‹V÷¥„Pò{c<¢aL+¢€Iê®ŠQð¼–YùÊ ^ÿÎ�QÿÅŽ9ÿ¢s(ÿ{XÙhJ"&
ojKû¡r(ÿ¹„/ÿË”=ÿÕ¤WÿÖ¦YÿÖ¦Zÿ×¨\ÿÛ±pÿ†lBÿ#
ÿÿT<ÿ¬ŠTÿà»€ÿá¾†ÿéÎ¤ÿíÖ´ÿñàÄÿÛ±oö�vRþæÆ”ÿåÅ“ÿåÅ“ÿåÅ“ÿåÅ“ÿåÅ“ÿåÅ“ÿæÆ”ÿÓ¶‡ÿ0)Þ]N5éà»€ÿâ½‚ÿã½�ÿå¿‚ÿ¢‡\ú~fAöàµqÿÛ¯iÿÓ¢VÿÉ“>ÿÀˆ1ÿ¦v*ÿ‚]!ýqQx!	TU=êŒc#ÿ¬z,ÿÅ�3ÿÑœGÿ×¨]ÿ×¨]ÿ×¨]ÿÚ­eÿË¯„ÿ#ÿ   ÿ-..ÿZULÿoV-ÿÚµ{ÿâ¾…ÿéÍ£ÿìÖ²ÿñáÆÿÜ³qþ�yVþçÇ•ÿåÅ“ÿåÅ“ÿåÅ“ÿåÅ“ÿåÅ“ÿåÅ“ÿåÅ“ÿäÄ“ÿ]O;ÿˆrNÿä¾‚ÿà»�ÿà»�ÿá¼€ÿÌ¨oÿ·�PÿÎ›KÿÆ�9ÿÄŠ1ÿ¾†/ÿ¥v*ÿ‡`"ÿsRÆ1#mK5åvSÿ™m'ÿ´.ÿÊ‘6ÿÓ¡Pÿ×¨^ÿ×¨]ÿ×¨]ÿÚ­eÿË¯„ÿ#ÿ   ÿ-..ÿZULÿoV-ÿÚµ{ÿâ¾…ÿéÍ£ÿìÖ²ÿñáÆÿÜ³qþ�xWõæÇ—ÿåÅ“ÿåÅ“ÿåÅ“ÿåÅ“ÿåÅ“ÿåÅ“ÿåÅ”ÿèÇ•ÿ¤Œgÿºšhÿá¹{ÿÝ´sÿÚ®iÿ×¨^ÿÕ¢SÿÈ‘:ÿÃ‰0ÿÃŠ1ÿ¸‚.ÿ r)ÿŠb#ÿuTóH4¦>,äbEÿ�\ ÿžp(ÿ¹ƒ.ÿÈ�7ÿÓ OÿÕ£UÿÕ¥WÿÖ¦Yÿ×§\ÿÛ±pÿ†lBÿ#
ÿÿT<ÿ¬ŠTÿà»€ÿá¾†ÿéÎ¤ÿíÖ´ÿñàÄÿÛ±oö…qRçåÇ˜ÿåÅ“ÿåÅ”ÿåÅ“ÿåÄ’ÿäÃ�ÿãÁ‹ÿá¾†ÿàºÿÖ¯pÿÓ¤YÿÕ£SÿÓ OÿÓŸMÿÓŸLÿÓŸMÿÌ–@ÿ¿ˆ0ÿ®{,ÿ›n'ÿ�e$ÿxVÿ]Cß<+çP9ÿkLÿ‰a#ÿ¥u*ÿÀˆ0ÿË•?ÿÓŸMÿÓŸMÿÓŸMÿÓŸNÿÓ OÿÕ£Tÿ×¨^ÿ»‹?ÿ¨|6ÿ»’SÿÛ¶{ÿà»ÿâ¿‰ÿéÎ¥ÿîÙ¸ÿòßÁÿÚ¯iæxeIËáÄ–ÿâ¾‡ÿà»�ÿÞ·zÿÜ´tÿÛ±nÿÚ¯jÿÙ­hÿÙ­gÿÙ¬eÿÔ¢RÿÓŸLÿÓŸMÿÓŸMÿÓŸMÿÒžLÿÄ‘?ÿ¤u*ÿ–k&ÿ‘g%ÿ{WÿkLýB/õJ5ÿ_CÿtSÿ‘g%ÿ¬z+ÿ¼†1ÿÏšEÿÓŸMÿÓŸMÿÓŸMÿÓŸMÿÓŸLÿÕ£TÿÛ°mÿÜ°lÿÚ°lÿÝ³rÿÝ´sÿÝ´tÿá½…ÿéÍ¢ÿàÌ­ÿµ¦�õ¶�T›dS8£Ø»�ÿÛ°lÿÙ­gÿÙ­fÿÙ­fÿÙ­gÿÙ­gÿÙ­gÿÙ­gÿÙ­gÿÖ¥YÿÓŸMÿÓŸMÿÓŸMÿË—Fÿ¹‡7ÿ­{-ÿ¤t)ÿ•j&ÿ}Y ÿsRÿT<ÿ9(ÿS;ÿlLÿZ ÿ˜l'ÿ³-ÿÃ�7ÿÏ—@ÿÐšDÿÒ�JÿÓŸMÿÓŸMÿÓŸMÿ×¨]ÿÜ²pÿÜ²pÿÜ²pÿÜ²pÿÜ²pÿÜ²oÿá½…ÿçÊ›ÿäÎ¬ÿ°™vï”n3YI:#nÊ±‰üÞ¶vÿÙ­fÿÙ­gÿÙ­gÿÙ­gÿÙ­gÿÙ­gÿÙ­gÿÙ­gÿØªaÿÔ OÿÎ›Iÿ¾‹;ÿ¯}.ÿªy+ÿ«y+ÿ©x+ÿ�d$ÿuTÿlMÿ@-ÿC0ÿX?ÿvTÿŠb#ÿ¡s)ÿ±.ÿÌ•=ÿÏ—?ÿÏ—?ÿÏ˜@ÿÑ›FÿÒžKÿÔ PÿÚ®hÿÜ²pÿÜ²pÿÜ²pÿÜ²pÿÜ²pÿÜ³qÿãÂŽÿçÊ�ÿîÚ¹ÿá»üÐ›Hn!6²›wèäÁŠÿÙ¬fÿÙ­gÿÙ­gÿÙ­gÿÙ­gÿÙ­gÿÙ­gÿÙ®hÿØ«cÿÈ•Fÿ²€1ÿ«y+ÿ«y+ÿ«y+ÿ«y+ÿ—k&ÿ‚]!ÿ€[!ÿ`Dÿ:)ÿO8ÿbFÿxUÿ˜l'ÿ¥v*ÿ»…/ÿÌ“9ÿÏ—?ÿÏ—?ÿÏ—?ÿÏ—?ÿÏ˜AÿÕ£UÿÜ±oÿÜ²pÿÜ²pÿÜ²pÿÜ²pÿÜ²pÿÞ¶wÿåÇ–ÿèÎ£ÿíØ¶ÿÙ­gèÑ›F5   �yW»åÉ�ÿÚ¯lÿÙ­gÿÙ­gÿÙ­gÿÙ­gÿÙ­hÿÙ¬fÿÑ¢XÿÆ‘?ÿ»…0ÿ­{+ÿ«y+ÿ«y+ÿ«y+ÿœo'ÿƒ^"ÿ�\!ÿ[!ÿN7ÿ@.ÿV>ÿmNÿ~Y ÿ�p(ÿ®|,ÿÆ�2ÿÈ�3ÿÌ”:ÿÏ—?ÿÏ—?ÿÏ—?ÿÐšDÿ×§\ÿÙ¬fÿÛ°lÿÜ²pÿÜ²pÿÜ²pÿÜ²oÿà¼ƒÿæÉšÿëÔ®ÿèÍ¢ÿÔ£T¹ÎšGâ¶q cO1r×¿™ÿà»�ÿÙ­fÿÙ­gÿÙ­gÿÙ­gÿÔ¦]ÿÉ–FÿÀ‰3ÿ¾†/ÿ¾‡0ÿ¸‚.ÿ¬z+ÿ«y+ÿ r(ÿ†_"ÿ�\!ÿ‚]!ÿuTÿ?-ÿH3ÿ^CÿsRÿ‰b#ÿšn'ÿº„/ÿÉŽ2ÿÈŽ2ÿÉ�3ÿÍ•<ÿÏ—?ÿÐ˜AÿÕ¤VÿØªbÿØªbÿØªcÿÙ­gÿÛ±mÿÜ²pÿÝ´sÿäÄ‘ÿçË�ÿîÙ¹ÿßºþÑ�IlÔ N 3' '(²šuàéÍ¢ÿÚ°lÿÙ­gÿÖ©aÿÌšLÿÁ‹6ÿ¾‡/ÿ¾‡0ÿ¾‡0ÿ¾‡0ÿ¾‡0ÿ·‚.ÿ¥u*ÿ‰a#ÿ€\!ÿ�\!ÿ‚]!ÿeHÿ:)ÿN8ÿeHÿxUÿ‘g%ÿ�p(ÿ¿ˆ0ÿÈŽ2ÿÈŽ2ÿÈŽ2ÿÉ�4ÿÎ—?ÿÔ¢TÿØªbÿØªbÿØªbÿØªbÿØªbÿØ«dÿÚ®iÿàºÿæÈ™ÿêÑ©ÿëÓ­ÿ×¨]ØÐ›F!ÒžM     }eA�áÊ¥ÿá¾‡ÿÑ¡VÿÃŽ;ÿ¾‡0ÿ¾‡0ÿ¾‡0ÿ¾‡0ÿ¾‡0ÿ¾‡0ÿ¾‡0ÿ¹„/ÿ•j&ÿ‚]!ÿ�\!ÿ�\!ÿ[ ÿV=ÿ;*ÿQ:ÿiKÿ~Z ÿ•j&ÿ¥v*ÿ½‡0ÿÇŽ2ÿÈŽ2ÿÈŽ2ÿÊ‘6ÿÑ�KÿØ©`ÿØªbÿØªbÿØªbÿØªbÿØªbÿØªaÿÙ­hÿá¾†ÿåÈ˜ÿíÙ¸ÿà¼‚ÿÒžK|á«R ÏœM     K8 9(.·�uáêÏ¥ÿÑ£[ÿ¾ˆ1ÿ¾‡0ÿ¾‡0ÿ¾‡0ÿ¾‡0ÿ¾‡0ÿ¾‡0ÿ¼†/ÿŸr)ÿŒd$ÿŠc#ÿ…_"ÿ‚]!ÿvTÿL7ÿ=+ÿT<ÿlMÿ„^!ÿ—l&ÿ®|,ÿº…/ÿÄ‹1ÿÉ�4ÿÌ–>ÿÒŸMÿÔ¢RÿÕ¥XÿØªaÿØªbÿØªbÿØªbÿØªbÿØ«dÿÞ¸{ÿâÀŠÿçÌ ÿæÈ™ÿÔ£UËÐ›GÑžL              x_8}ÙÀ—þåÅ“ÿÍœPÿ¾ˆ1ÿ¾‡0ÿ¾‡0ÿ¾‡0ÿ¾‡0ÿ½‡0ÿ¥v*ÿ�d$ÿŒd$ÿŒd$ÿŒd$ÿŠb#ÿoOÿO8ÿ?-ÿU<ÿmNÿˆa"ÿ›o'ÿ¶‚/ÿ¾‰3ÿÈ’<ÿÑžKÿÔ¢QÿÔ¢RÿÔ¢RÿÔ¢RÿÖ¦ZÿØªbÿØªbÿØªbÿØªbÿÝµuÿâ¿‰ÿåÇ—ÿèÎ¤ÿÖ§]ðÌ“:QÐ–: Í™H             :* ! £‡Z¾èÐ©ÿá¾‡ÿÌ›Oÿ¿ˆ1ÿ¾‡0ÿ¾‡0ÿ¾‡0ÿ«z+ÿŽf%ÿŒd$ÿŒd$ÿŒd$ÿŒd$ÿˆa#ÿjKÿ_CÿT;ÿgIÿ‚]!ÿ r(ÿµ‚0ÿË•>ÿÏ™BÿÑ›FÿÔ¡QÿÔ¢RÿÔ¢RÿÔ¢RÿÔ¢RÿÔ¢SÿÖ§\ÿØªbÿØªcÿÜ´tÿá¿ˆÿäÅ”ÿéÐ§ÿÚ°mûÎ–=‚Ë‘7Í•=                     Žl7 ]E;À£tâìÓ­ÿà¼ƒÿÏŸTÿÀŠ4ÿ¾‡/ÿ°}-ÿ‘g%ÿŒd$ÿŒd$ÿŒd$ÿŒd$ÿ�d$ÿZ ÿdGÿcFÿZ@ÿhJÿ‰b#ÿªy+ÿÀ‹5ÿÐ™CÿÑšCÿÑšCÿÓŸMÿÔ¢RÿÔ¢RÿÔ¢RÿÔ¢RÿÔ¢RÿÔ¢Sÿ×©`ÿÝ¶wÿâ¿ˆÿåÆ”ÿéÏ¦ÿÜ³sýÏ˜AšÊ‘6Í”;                          ÿÿÿ €b3_Ñ³„ñìÔ®ÿá¾†ÿÔ§_ÿ½‹;ÿ”j&ÿ‹c#ÿŒd$ÿŒd$ÿŒd$ÿŒd$ÿŒd$ÿrQÿbFÿcFÿ_Dÿ]BÿxVÿ�p(ÿ»†2ÿÏ˜AÿÑšCÿÑšCÿÒ�IÿÔ¢RÿÔ¢RÿÔ¢RÿÔ¢QÿÔ¢RÿÖ¦[ÿÛ±oÿá¾‡ÿæÉšÿèÎ£ÿÛ±nûÏ˜AšÎ•;Î–>                                 7(    šxArÚ»‹ôìÕ°ÿãÁŒÿÔ©fÿ±‡Dÿ”l+ÿ‹c#ÿ‹c#ÿŒd$ÿŒd$ÿ†`"ÿhJÿcFÿcFÿcFÿ_Cÿ_DÿjKÿ¬€9ÿÓŸMÿÑšDÿÑšBÿÑ›DÿÓ OÿÔ¢RÿÔ£TÿÖ§[ÿÙ­hÿÜ³tÿß¹~ÿäÅ“ÿäÅ”ÿ×©`ðÎ–?�Ê‘7Ì“9 _'                                      _F ( ²‰IqÞ½ˆñëÒ¬ÿäÃ�ÿÜ³sÿË [ÿ¯…Bÿ—o.ÿ�e%ÿ‹c#ÿzWÿbEÿbEÿbEÿbEÿaEÿ_CÿtV'ÿÈ™NÿÚ­fÿÖ¦ZÿÔ OÿÔ OÿÖ¦ZÿÙ¬fÿÛ°nÿÝµwÿßº�ÿâÁŒÿáÀ‹ÿ×¬hýÎ™EÊÌ”:PÊ�6Ë’8                                                 œs1 wQÏžP_Þ¸zãéÏ¦ÿçËŸÿá½„ÿÛ²pÿÐ¥`ÿ½“Oÿ«�>ÿˆd,ÿsS"ÿoP ÿpQ!ÿwW%ÿ‰f/ÿ«‚BÿÌœPÿØ©_ÿàºÿà»‚ÿÜ´tÿÜ³rÿÞ¶xÿá¾†ÿâÀ‹ÿâÀ‹ÿÞº€ÿ×ªeüÍ™FÕÇ�2xÄ‰*ÌŽ- ¿‡/                                                         Î˜B Á~Ñ�K<Ù®iÁåÆ–ýêÒ«ÿæÉ›ÿá¾†ÿÞ¶wÿÚ¯lÿÒ¥]ÿËžVÿÇšTÿÇ›TÿÎ XÿÖ¨^ÿÚ­dÿÚ­gÿß¹}ÿçÊœÿÔ«jóËžVöÏ¤`þÐ¦dÿÕªfýÔ¥\ôÏœKÜÊ“;«Å‹/bÃ‡)½�%¿„'                                                                     µz# Ö£R Î™CÕ¤W€Þ·yåçËžÿëÓ­ÿéÎ¤ÿäÃ�ÿà»€ÿß·yÿÞ¶vÿÞ¶uÿÞ·xÿßº~ÿâ¿ˆÿäÄ‘ÿéÎ£ÿá¾†ïÎœL`¶'C¸‚,`¹ƒ-eÁˆ-YÅ‰+=Ã‡'Á„#ÈŒ) ¦g                                                                                     Í•= È�2ÐœH0Ö¦Z’ÝµváäÅ“ýèÍ¢ÿéÏ§ÿéÏ¦ÿèÎ£ÿèÍ£ÿéÏ¦ÿêÑ«ÿëÓ­ÿæÉ›ÿÞ·yåÔ¤VhÊ‘6Ð›H                                                                                                                             Î–< Ê�5Ð›F&Ó¡Qj×©`¬Û²p×ß¹~íá¿‡öâÁŒùâÀŠöà»�ëÛ²qÎÖ§[�Ñ�K2Ê�5Î—A                                                                                                                                         œ= â¶S ÉŽ4Ï˜AÑœG;ÒžLQÒžLXÑžKPÐ›G7Î–=Ä„$Ç‹,                                                                                         ÿÿÀ ÿÿ  ÿþ  ÿ  ÿü  ÿ  ÿø  ÿ  ÿ0   ÿ  þ0     ü    ?  ø      ø €   ð <    à     à     À     €     €     €              0    ø     ü       ?€                                                                                            €      €      €      À      à      à      ð      ø      ø    ?  ü      þ   ÿ  ÿ   ÿ  ÿÀ  ÿ  ÿà ÿÿ  ÿø ÿÿ  ÿÿ ÿÿ  ‰PNG

   IHDR         \r¨f  € IDATxÚì}wœå™ÿ÷�ª.moöº¬;®‚ÁØ„ÐB	Ž$€Ó+I(ù]r	wÀÝåîRH%…�5	�\$@(¡0Ýlã¾öö¦®éïï�ÑhUFÒhW[Lüý|ÖÞ�F3£yž÷y¾OŽáŽáŽáŽáŽáŽádªOàÊcÃõ÷‡˜Æ–€ÝkFOTîO©çŸ¼Cžêó<†£ÇÀcÃõ÷‡hMÝ¬P °ŒaÈ
Ý �„�v AÏ¶k­srCSƒ°t
,Öºß0èÖp4ºÝxðW»Ž)ˆc°Ã10‰Øpýý!ÒÔ¶šg¹UA7·VÑéJŽeæNÆgkºñ¥t;�¥¤gŸúâÚÎ©¾Ç0õ8¦ &–À‡<âûŠÓ8–Y5ÕçdAÓ�7‚¿‡#w?{ÕÉ[§ú|ŽajpLTº¥�ã„KÝ{CõSŽgªy|.ïÓèø�©éÆþ”¢ß¢iÊ=Ç,ƒ,S U@–Ð_:ÖU>äâpq¹9E!77ÏÂ#0à¯À‚gí¿.U§H(:Tƒ"©ˆÊÂ)IÇ`BÁHRu¤(M5t0wG¢Ñï³
þ1pLŒ®¿?œ5ç ä“ËœZÉ{¼<Z"š¼ý"¼|Qá®T�b8©¢'*ãHTÅ�á$Í(ùô�ýÃá?¦ÞÝ8¦ *Ä)?|nE0¸Æ-pç8eè›}<f×ºÑr¡5 L¨°;E_LÁ;ƒ)ìêO",iE÷S5ýÎè¡_yæÆÂS}ÎÇP}Lý“x”à¼_o½ÌéjïâÌ«saN­í5.xÒ ªN¡Šf@5L›Ý0(ô"æ;K †!àók8Ó]¨DÁ$U=Q;ûSØ3�°u8†IŠvÕƒŸXq÷$ßöc˜`S %°~Óf±ö²k.!7–×¹8K=XÚìÅŒ ˜`;$UŠf@ÖH…fPõ*°yYpó&gàáST	%ÕQW@Ñ¼ÑÇŽ¾„­‹ jú�#wÿ3Çr
Þ=8¦ l°~ÓfÑýOW}Å-°Ÿ-%øæ7x±¬Ù‹yu.pì¨�¥T=ó{R5“4HÍÙ>Ùpó,‚.6‡PÌV ,Eðfw¬À"Ðtã�X2uÁ±hÁ»Ç@,Á¯ñ
ÿ\Ê¿¹8¬žáÃÊ6?Ü<[ðzJÕ3,üT
|)dGÎÞ:ˆIžÞAgXÊÙ®éÆþX2µñ˜8úqL¤‘öñKšúóëÜXÓ@G�»à5Kè“ê¨9_‰ð«…®›«±F	´ôï¥\	 ÿŸcp„‚e™Ì6'ˆ+|ƒ:/¯ÀÚîóv_ÏíÉ±Ž)�wþáÀ©?{åô€[üv±ø=G€%Í>œ<'ˆ:Ÿóš¦ˆÈ¦àÛùðÅ€¤ê�4“ðÓt
Å¨®ÿo�·E
º8o/ÜqeÔ9‚F/¿‹+Øo ®â¡]CH(£×¤éÆ‘ƒû7‹½ø‡U º¥½Îïù�r¡ÝëV¶ú±~N�<�ˆJÂ’†¤R:–®’ª9íû+šQ•Ì½±B`Ü<¯Èf¬„¸Í5øÍ~¡À5H(:Ú9„�„šÙÆ€þñW,ûÐÔ]Õ1Œÿp
Àòó½<þÛ.M·˜à—[í³‘RõÌ*‘§'ÀdLþlò25n-1g›¢¸ÿ­Á% iúåÇB„G'þ¡À)?|nE(¼­˜¹¿´Éƒ59¦¾¦H¨ˆH¥9¥êˆ+F�ÙïtÕ7zÚ÷×©ù+§Ù¸œåû3 K8–K(Æyé�aˆÊ¼‚°#4EŽ`fPÌ±ÍÀ}Û2	Dšnì¾ë¦%váÁõ›6‹ÌyŸZÔwh_jœ+xõº®µ €¡H�ùû3‚« X–ëQ’ÑA—7Ðçóˆ½„»�ñÕÇ?„Øpýý¡š9s¿É0ÌWí^o¹pÆ‚šœÕÎ‰à[+}¬„+ é’¦gÓ0L—@Ö)4ƒBÕ�LP5`%ñ,G rÅk,`¡”"h¹G-¢pJÃïßìË(·XJýÚãŸ]õ��?ÝÒK©TM{Ç«´TlÙX¯…< Ü¨2¦”FÐCšª¼¡ËÉíõ[i_×+Ç8ˆ±ã]¯ Ò$ß/ìØý�‹Ã¦y!,möe¶•|Í�é;ZM§N©�4Šdº`g²a��…‡gr”Q¶°Ô¸ù× ÁË£Á7*�ïô'ðøÞ°ußöïÛ€åD×¬j�7ë	:Ú�Pc»¦*oð÷„ßÍ?sÌRpŽw­X¿i³XÅµÿi·ês8af ›:B™‡ÜòñâªíñŠ™øvˆËd�"&kÐ)ªºÂW~��_ä rÄV ¦kQë.Œäó~{�a	á„„¡¡¡ª�cþê_	5¶SÃx8àÿ¦ýáæg�e.Ç»R”òõ["ÎYT›óG%	µh(/œÒJ
°eæGåâ
Â.ãnªaE6a?‘Aƒ/—Ì¶Â)¿y£=C$ñ²ŸéxE^vô~
<%}ß:§KHr�ªÜ3JiÔPåûkÞ»�)ƒB¼ëÀy¿ÞzãŽ|†Ÿ#ÀÆŽNœÊlK©:úb
$–.*kHÈ¥Mö¸¬•úlLG ˜¡=Ž >‘+ªD– Ù/ä¸3ƒBÆ:xl÷0ž~»’¬d^o¯ñ èá9]Ü‹¢8–�O0÷�+")1YÇ@BEXÒNH§4GÇÊ†¥”pÿ­;ÿç’ç§úÞO¼kÀ†ëïfÍùÏ±Wä¿ÖráÜ%uv¿”Ÿ_nÅW41YÏ˜÷N1ÝÜ Ù‰=ê½<Î&`£æÕ¹ pz¢2~ýü^´´5Ô¡ÎËW”�˜�R‰K$UG_BCOTF_$itÇuFÓœ+B�ír<|g�¡üê™D|W(€´ÉÿÇ|¢ÏnÕ/fî—üJVûb(fÈª=…šˆ"ž’AÔÉÌ¿W¥DÎ¾¼Ë \.x!PÁw b�)†l`ÁêN”�|%à´×¸ hvõ'Ç|_
>ßÍUüÕ è�)8‘qd ‚ž¤3‹Ë²
‚.ö²ÒQ¯ ÎûõÖË\÷Ãüâ�/�Ž«ÏøúÅV}Í00’*^´‘tŒ$•ŠVûbHÄ"ˆŽc¤¿R¸ƒ±”èŒ<!/B¬iBSC=¼¡:„ZÚá	Ô”?'Åþº]ƒZŸÉ7°àX4û…Ìß3ƒt
tŽH(‡låªeÝP.¦ä’cþ�qÅÀ¾¡ÆŒÎ°ìè€,!OÄûÝø�äÕ
à‚Û·}×Žå_ÚäÁyKê3+UJÕÑUrV}+œgÃ×tŠˆ¤UlæçÃøD4ŒÃýÃP#�ú'/BÅ¸¼hm›ÿÌ…¨ŸÙak%S €iA5úÅ%Pïát›î”È™=¬è‰ÕÐ$¥QÈšÍ Mw|ýâh¨R`8–”­g(‡¸b`÷@o÷Dq,!OxEæ;OaõãòÅL#•
`Ãõ÷‡::þ7?�Ÿ#ÀY‹ê°ªÍŸÙÖ•VýRßPB³à«ŠŒ�á’ƒ]8Ü?C.4‹“]{@õÊ	¬ñ‚°Úfw eÁr[FCõ¥€uOí”À¬�˜£`‡*ªUÙr•6ÿEŽÀ'rf£Ô¬š§è‰*ØÑ—ÀÞÁ$ÊqÿÁQ§ :>wóÜãÖžò‡ü_ÈÅáâå“ßŽá/eî�ÕÔWƒ½ÝéïEÏÀ`Ùý•È ÔÈÀ”ÞCÎÂ‚«Ñ4yÙæ €é4ú„œmÙ®Àî�ä¸ x–�·Â.ê"GàâXø]\E®ƒ¤êx«?åÈ*`	yÂM”¯¾9‚£J´žÿ•uKO9û/¾†¶n­ZÖ�Ia�Jº£JÎ{­VÙùˆË†SzE°ªS |dŸ#¡Ï†¡iHuï™ê[	 àƒ˜¿t%‚s––Ý×Ž´¬€½åãÿNàY°d|�¤_4;UB$îêObkOýÑ2<†®}ÛüŸwSÔà¨Q /žô¾ÿyð�|á_×ÀûÔfþÎ7ù5ÃÀ@\-0÷ÍÀ`R«ˆÕOÄ"èîîÁáCAuÕñûò‘ìÙªN�|”Ä¡˜µj=:Þ™QnˆI³/7DøDû†Rã>– ~±rö¿ü"‹:ç˜;80œÂË‡£%¥4ÊRýÊW¿ºþ®ªžìáèP „|xÓ·îÿmhFGfG€óŽ«Çª6?T�BÓ‰È9&±U(�PâÝ½ýcZí‹A�‡¡÷LáEâÐ €«yÖ^òEðþâ‘ƒ|W€cLËàp¤P™Y„«jPèEB«lVGc�ÀVlþ;Ç2ºL«À	gp`8…ç÷—tÞ-nÁ´W „å>µéßî¾5ÛLuq.]ÙˆÅ @DÒp(,gºbR5Ð“ùùªN1Øu»öî·%óÆƒéäX
  Ä`=V}øjøêš‹îŸop0œÔ i$Í¬lKÖcÐÅ‚#€È³9ÅKãu	
?‡C�›ud¼Õ—ÀK†m¯‡ã8øx€eØ¯¥~ÿÃ­)ÆÓ[ríi7üî{ÙÂrqøØ	ÍhIPICwLÉtç)fò÷ÇÄ4çP·™_©þÎªÄÿÇ}½¦‚cD9	¡¶k7¢Çc»>Ð–ÊöJ(ž¡ðöæ¿¥<<7ÏB¬ÒP§î�jP¼t(Š7�„GÏ‰ãrJ¦ãÉÔv/«_~4ZÓV–¿aý?ßz}Ãâ2Û¼<>vB3êÓ)½ƒI5Ó™Æ0(FRZN§Àùª?Y‚Ÿù¼iâd[ Z®Äq}ÉvŽ ­A ³QÉÎ*d zxÆñ0«n¡Z.ƒ[`Ñä-¯Â)Ïì4:Ã2ãw‹¯³ÐÁ2ì×ÿìªïŒû¤&ÓR–¿aí—x}ëª™m­ŸZÓ’ùÒ{b
†“*†d„?¿\×‰¯o™ú;wížÁÏF¢sç¤~^6¨aZLRÿÁLÙ-ÕÔŒ»³âÂÏ#[ùf£5`æôÇôÅÇÏ‚®±%ød1�×2pjôD<{`)Õ~EIJÊžpÏÅGK¤`Ú) Âò7œð™o]?óÄ³3ÛÚC.|ì„fxyŠAÑS2	,†AÑŸP!§É?M7�PtôÆÕ²¡½žÎâã;…<Ü-žÔÏ¤†CMMF¢º]N‚=Xÿéëm#Í>:ö•Oÿ-‡Ræ%pqB.vÜ‘„ ‹C½Ï¾˜)àâÀ³IÅÀcï�~hæ‰~éÑ�I86Õ;A ,ÃÒ‹¿|ýœM—d¶Í­uá“kZàæ
…_Õ)úb2²9š„¢£§ŒðaÏëÇ¡#Ý“¾êç\/a %"“òYÔ0`(IªÐÑ{CX¬èçöƒ=`xÁÈ	xƒuÄÛÐZx,tG%tüë‡‹cÁŽ£rÐ‚fPÄQIƒf .ž3QÖŒ$5p�+K÷¹y6ó7Ï,¨w£ÎËãHDB~.Cˆ„¹|ÁyŸKíÿË-/Œûâ&ÓGríâÓÿé¿~èË™MóëÜøØê�bPtŽH™Ìµ¤b /®ÂÈ2bâ2†S:B@)E¾‘¦*2ìÜŽÝ;ßBR™ütÜ|0œ`Z tb{šCN>¨ç�uyÀð‚ù#¸Àº¼`<A†„ÐœãFïŸnæ÷G$£*Â ž*þä\+ I30’Ò`ÐôÀ”1(˜¸b–|»9~_pž„Ô{y,lð /®ÚÌ:ÅéË?ô…¹�±Ô#�¶NËöÐÓÃ äÃ­+Nþíš/ÿ(crÎ­uáãyÂo1ûQIÃH*g@zc
d=?Ùg´¥§óÀ”øùå0‘©ÁÅÌ}Îë	€duæ<SðEOÎ6c[Î{}ÁZTÕ2ÿË!äâPëáÆR9‚9µnÔ¸90E‰aP¼x(Š—:£¶¯Ç“©íû�éØ«pê aÎt¬|ää¯þ"zj¹ðéµÎ„?¥êè�+(–Ò>8<Œ½[_Á`lüÙj�‰Ê	(&ü|°¬8:ÚŒ°<„`}Žà–‡PÛ&¯'_¨®±è�± ö¿¨TX	D€é´„’ç»o0…Gß²%5ƒ³jbÓtNLê•C–_&Ö¶ü~Ý¿“þfŸêË~Ã J¨9ÂKçüÛ	¿fPŒÛ�Œ`(9õæ~10Â‹ã?Pœ
? ˆµ-9ÂÀVø=^_U…Ÿgè¤
? „%�#’ã,Ðì0cJÕ±o(…¨¤Á(’ÙØQïÆeÇ7£Î[ØÌ”cH­Î{Ÿ<õg¯œ>©]SÆN˜Iuõ¹S¿~k] ÕläÓàåñ©5-ð‹l�ð[a>‘”Š�"‚•4lëÄ`$NÃ‚uû¡'"îk�ýfè©êÔ €¡$…ß_6ÝMÈ‚XÛFpåîh ë*Lry¼à¸ê™ëÕ"ÿ*¾70sC’ªŽe QBA—iT’�˜lŽxó	,ˆ�%!rŽkòb0¡b$/•˜!ÄÍzyÝÆ+öw?öëm“~ñ6˜€ñk©¦þyõç¿Ódeùy;¡AWVøâ2‹ÿÁÁ˜ñú¾n¤¤ÑÌLVô@l¬Z»úªƒuùÆ�4M)~Fp¬òœ'P°�°<8oÀö¸¢X]+e²Wÿ|Hš�î¨Œ�„š™ÄdÁ/²¶Âo!¦80,�É³ç-©Ãê™þ‚×t°ðz½wž~Ëÿ<¥7 �)Q F2rÇâ3>¼ÂŠõ»8&“ág…úŠ	oL±íe¯h:¶ìÅÁ¾Ûkâ<ˆÓS	0&ouÌP_!×Áês? à¼¡Â{d³0C5Á3Ó§9ªå$Ò±d·ÀEµƒjPt†%D‹¸CpÊÜ6Î³/®R|{:(�IW „åo,\û�…ÿôÿ2R|éÊF´ÍÆŠóÛ	ÿ‘°dÛÁ&šHâµ}ÝN(%?›Ô�ó×a:‚óÆ}Cµ~]¡ð{¶C7˜<~À‚ÈUwµæÙé} �ÝQ1Yƒ§Âôâî¨‚¡„Z”8¾Í‡Ž«·}m:(�ÉU „|XÔ\¿áËßƒî;wq]¦ª¯§ˆðkº�#a© Ì¤Mþƒƒ™LÀrp5Îr<rj2ÁûB ìØ}lj¦?a¹3@ ¤Ý„"“xN@51Õæ¿8HÅÁá]’²1�PÑ•K’ƒ-·� Lµ˜4@X~(½eõç¾“©9_×ÀúÙ¦0&Í¼};á/ã/eò—‚«±ÝV0¦¬{ì\€ÝêÏy‚«?ayÛkgÆ8†«RL'ó?¾t
±¤Qì–—+‹Å‡#JQ%0«FÄEKë¦�˜°~Óf‘êÚ]Ïû¬ß*0™[ëÂ9‹Ms<"�VñÅ½¬ð§To,kòay¸[:ªîÛŽcuìV ö«3ŸpÕ%ùŠaº™ÿ€I@çãHÄ4í+AJÕq`X²1 m!WI%pÂ÷ž¿|²¯}RÀóOÞyK`áš‹/øœ˜	—®h„À$TÝ1S�³“|Š	4‘Ä›ûsYþ±€°ü´‹°.ï˜Ü »ÕŸÜ«?P\ÐÇ:ˆ³RL7óß+ï,<�PÑ–sf”ƒjE°Æ ¼^ï�“�'0ñ
€�óÞÀæuŸûŽg8|ôø¦L¸¯'j
rR1r’|ì„¿R¿¦cd€ó•æ‘�b«?[ÄÅ)jŒƒpŠéfþ»8¦l‹°¤¢ãHDªˆp¢Šƒ*eï9å‡Ï­˜¬{0¡
€pÂLPzËÊ+¾™i3uÖ¢ºãoÅúU�æ4ò°#üöt�Éß/>P¡¦yüªŠ…âŠ�EÂPE )Jô· ml®V>¦“ùÏS8�¤QŽÈUU³jD[%`enüé–öÉ¸¦ Ò~ÿoÖœã·âýK›<Ò¯'+Öß5çóW~Í ØÓ=Œ®‘êeÊåC¨m�6‘�JSƒíŠ›ËÙšÿŒàvrÈ	ÁT¤þƒÕH¤¨ºIVÒï°œè¨wãôù…Ç�Ú¨¤ÿeÃõ÷‡&ú^L˜xá™ßþ‹+TòºÍ_`úý.k`’~ÃI5ÓÌCK—–Äåœ8¿fP¼ÝÙ7¡Âoa:Ex¿³Š»¢äo/èÅVùrdh5\®é´úW*üÙè©,B`)�bÑ�å­>¬k/$}÷²d¨å¾‰¾¢ Ë/cþmåÇ®Ï„ü.^Þ /ÏdH?†!Ii™‡+’Rs2ü,á+Ó?†sž6‘�ñ¦3|é¸=ÕÔÜ]…¡g¼«QB=]V;Æ¿R‰(+�R!Âg°¨±pññ¸„÷Mtx°êjyý¦Íbçþ7®?áÌæ¥ç
 °±£kfú¡]5Kàc’–SØ3ÙÂoÁ*Ò¢Õéÿ?öó``¨JÙá!TW, ªk ¬ ªÊÐ¥$ôTZ"->ªëÐ’1è©(´d$ó£§â Ðâ#™=‡.%@U	†œëò‚c	Sù#cšÿSZx
 4ã_)¢²G¥æ3]¬ˆˆ³çÀÁáyõ:ÅésÎùÔß>tëþ‰¸'UWË„åoà\žëÏüÎ_ÀûkÐìãñÅ“f@`zb
Â’U§™Ñ]ÖäÞì5ÂŸ5:yàÐ”}>P¾k05èRZ2šÓÌ0ËyíX}Þ_g›lDX®ÆÒœ“PÛš‰ –‡È0œ ·K,k5MvÝ¿\ã˜ô«3‚BE.EÈÍåŒWÏFTÒp÷ë½ý4ƒPWMDC‘ªÞËô_vÉUàý5àpéÊ&ADÒ2™~é§éúãÓKø�é°K¦†CS Ë	èRòÀaè‰HASÓJCzNL|šå"P]…$+H&âÂ`/â‘a$qh6½ß­Â˜î@%Ä`8¥- 
¸8œ¿Ä>2Wð¿qþU½+ÔÐêí8žÌÞx1 `cG-~Ša†ùòI¿ÞØh3�é"ü¦Cd€uû2B¯%#Ð¥%5öñâEÌw«Ex)P­´;b)„ðP?†††�LÄAuuÊcÿ•„ûÆŠÎ‘Êòº£JÑRâ¶�ëç„
¶OPµ;CXîS ÆÉ'lþ ³³Ï)&h•÷Æ=CúÄåœpßt~î–Ž)‹šÂò¡ÏG1%0–ó%“³ÂÛA—�·T£ºš±"±"’^46‘K¸o¬8©,c°'*½'«gøÐ^ã*Ø®øvµ“„ª¢ O°–êÚ.>ã#°x~hycÆô�¥ý~+Ó/–E f’Ït~“04†¦™ä›&ƒÝEÍyRåæ¦åÜ€±FdÊb$žB×Há„4©Š@t8¸PuŠ#çsTÃäÁì"CpÎ¢:¸ùB×)¡³w­ß´¹j…UQ F2úMÞlšûÁ« ˜U~íA1cúÀPÒü_ÓôgeýŒ“ç+ËÃÕÒ1þ•@¶Ðç›Úœ¯z]xË�C9Tb …ùá”†®‘ú#É	oË^MÆß)$�æÚå�Rõ‚¶a<ƒ3ö®ðyÜË’g}úÆj�ó¸ aùe,Ç]½ì’« z<ð
,Þ¿À|híLÿÞØèêŽH’Þ[m°¢®–yU?n¶àC±¤ MÃŠlŒ¯5½!U¦¨‹YNIÕ@Tš0Eà$Ç¢•´ŠªjQ> £Þ�e-…Q›�Ïõµj¹ã>ª«ÿåjšMÚO¾À €3ÔÀË3ˆHŠžcúgûýQIÃ;Gú«q“Î¨ZdÀ‰à[`8~RRx�œ‹Q�Ò!,_¶Âp"ÁD2þN1�P+Š”j&²±#”3�ÙÍx]"BÁàmÕ8×ñÝ)Âœ	àË.¹
Ç3í!N�[“¹	Ù!¿”ªgü~EÓñÖ¡©ŸŒ[)„ÚÖq·s*øÙ¨´B°JEœ·Y•ð&ÙŠ`<Ád0þNÑuN
ªéè˜x–àýjÁ±Lz6¡ÉkP�UÕˆ
ŒïnQú?�yÇÃšâ{áR3†™Pƒ¸¢C£$'Þ¯»ŽT­¤w²ájœ5&¦ÝZõÇ»F!åXûŠÏÏ¡p;åÆÒ_ ©²°â{4‰Œ¿¨:Íô¹p‚pJ+pƒ‚g–4ypB[¡+àåñßã­» äÃ ]±âbs–ßÒ&æÖš¦ª•è ê"G0’R3ñþ½#Ó–ñwŠJ"•˜ûÅÀp<8‡9	¥&—Š÷SÃp¤Tœð ã�š„SŽ„+‹L'á·�TôŠø€|WÀïâ2dæ©!¸óš³ê„c|n×ŒçÇ® (ý�ÀÂµhX|8œ—Î`êM›üé¹ñ²F!kôÇ•I©ì›h8�š6.ÁÏçåžÃVØb½€t.€ƒPŸ aSMÓ[Õ(ð™($TÇIB–+À3!—Cdz'Ïµq	sÅxº�Mòa +.ü< à„™4ûE¤TIÕ@BÑ‘Ru¨éÿ\ƒ¶šêÁ˜J”ŠTcÕÏç	Œ»kO9wâ¢”«tBþU‚pJ+ÉLE¸¯RtG�=å®cE‹6cÇnñÛc=·±)€¼Õßjî9˜Ì�ùÆÕLnxRÑÁ³3‚X2«µÞê¶šž
Øµ«æª_øyÎÜ€bdÕµ’n€S–_O„‹¾6IS7�)˜Êp_%�4ZÒà¿ÀÀ+š–Œ%?vØ4/T°�‚¬:ï×[/Ë¹U® òVÿg9sÅ×)†“*T�BRuDÒ\@B5�­Àý"‹ùmX0£±êC'&ÙÃF&Rø�Üh 7ÆU––È¨8‘ÍEû£RÆ%˜NŒ¿sÜ<¯È‚Í*—NªFÑ¦í!:êlÂÂ„Œ)9¨ò;Hñ5ß¬ã2«ÿûš‰*ƒIjº§?`¶ü �R¨ºýªSãáqÜ¬–£Þ-p5Îaù	~ %Sƒ³Q*M¸\��R$bæEÜ€ÉH™§4„Ò¤¦ùV½ñÑ{ææY„Ü\Ñ°”ÅpêÜBK�‚Ì‹P™ Ì™ ]±øìÍ Lß?(rˆÈf®8-üá¬N?I¥tö™åßÑzÔºjt| nRzë[©Á´„°i%»û”N¸qª´s&«pJ‹�¸Õ8ºBÉIÅäÈüSÖòUÓš¶C­‡Çr›Arc¥u•Z ßpÕ4¢mõé€LÊoTÒ 4ó¿Eüå›þ¥À³óÛ°dV3Ü®ÉR1^P]…`öÛw5Ì˜ðÏtÒ/°T8¯\‡_Ç<@ž0YS.Ñ\$Rª™w4)�À".k9æ~)ô'Ô¢×·¶½pò0™[{Ù5WrNŽ€9ÚË8yþ—ƒáxfy‹õÞvõJ›þ¥àY,o¯Çì¦c:óTW¡%¢¹÷ˆ!6Ll7g+5x¬ª«¥‰@%å(€êjŽ˜àásI?M7Ð9"Aªp–ßdÃÅ\8†@3€þ
„ìrqE­€JÎÍ± ºv-ËñtöÉ  6u„ ä®þ’:šøPÎô/‡¦ ‡™®ü€�ð[`]>µ­úùXÌä.™¤k%‰@Ày¶Ÿ•TíÐŸí5sØ"ŸÑ‘§¥àYSð.—¯LjÐ.ŽCÉÊ­€J¸ G
€ñkzaûúóï¯A{È…¹µîóo%ý¥{Ê:ulú—»�3‚X6§eÚð¥„ßçU<à£8™!XÊ×/Gê«þ´¤y&zõç8î2¤ßtR<KàØ’çÜ—p^üTÊ
XbÓM„\ãôØŽ€‘Š~€nºÕ—ÅBZÌJÕ!©:b²�šWžÉ„§’p"ü„ÚVÛ1ÜÕ€•\jÕÕKX†Z†¨ èGKD'|õ/'üº"SOZ‚Ï•ÉOÈ&ÊË¡”°Æf¦ Yå´\Ø™@éç½móœ³^�Å	3™ÕßÒNÖê/M`‘O�‡ŸR~À©ð[fMXd€ó…Jæ”$ÕTÙ>€N¯u¢CŸ~we÷oªˆÁl?ß)âÎï]1+ ÁËc–Mû°` àÈ
(« ^<	@ÇÜ“Ï ¼7íwDò|kõñW)¦‚°ÌÝJ1Q‘Î Jô	7PAó'¹c�ÅøWM7*¬ñ¢˜ŸïQÙplë$ ï±©da\æ¤R°¬0TåÓ `‘'Î0³•bé“²ü™ñ• “?0»~Âù«÷þX0‘‘¾Lo‚’õÿeâýN¢V£’±*ÇRÈgü+�"¬¼¤¸âs,ãç;A©´ßl¨-šØQçFÈ�Ò	Çpœpi¹ã–T ù×ºòðþÌ¯s£ÞÃgrþGRæêŸRÌÕ
¿‚Ä	M+Öåä¸kï'*2ÀùjJ&ß”äLü-°fR¼ªý	<<S”ñwŠ”Z\`Æ‹±˜ûÅNU(‘x|k¡àØÏ–;fI@åÄ… üm'~ €9Ã0ÍëæZ¾ÿd®þv¨ñðX9·³›jªæƒP]…Qa#Ìbà¼!ðÁ†ª^3+ºÁy‹•ä”TÙ¤­DÑO~›2-©Î}*î«C	µ¢~å`	þXÌý’ç™tvŽªAs†çfÃn¶ ™[®T¸´0Œ+XŽ§�+N…‹cp\“‘ôÍ÷ý§bõ·CSÐÃßÑ:n~ Æß)ø@CÕ#B¨©èkå|órn€© í�‘?i¸T… S8	÷UŠ¾¸:nRÐ
ëU[ð-Tb©D${àX,l(ä„ü.¡dN@Q@8a&Ë²ëk�?=,mòÂÍ³H)zA³Ï©^ýóQ�²ãJ[`;Eµ#e­Š!=]N”�¨ñpÁ¶bMJõqZÕ~ã!�†õÆÍ(Îòç#*kEÚqM…‹ã²RõE ÕÕKtM%³ßcöû[•f“ªaNz1("’6­Vÿ|XeÇ•†«á÷—‚«aFÕ” Ãñj[Š¾®Jã‹Ø‘�L]�ŒýžŒ�ñwŠ±ðÕôó� ’ÖaÅÜ€Ž:·mÛ°Rõ%\ r~¶ù¿¬Ù—“øci¬é¶úÛ¡’°a5ýþb œ¡¦iüJ£œP* 'ce�ŸÍ”kQ>+`<Œ¿S%TG™‚åç—ƒ¬Óª„í¸ �c>Xl[Àx‚µ,Ë®oZzbÆüÌÖÞ–ö‰HÓË÷/§eÇeúç£š‘�rn@©h€.ÅÊ'e…A™2]¥V@5§N�|L´ŸïNC‚IÕ(ê,i*\ä4M»`Ãõ÷‡ìö·U TN\¨k*i^µ€iþ[™QIC\1 éÆ„fýMJ•O´éŸ�jEÊ¹e:ˆãk‰°ã%N­€j2þNPÌ˜?ß	*‰XÄŠX­~1W‰é„c‚³æ|Àn{ kç@ýò“Á`^�‰´ÖQuŠHJ�´¬¿‰B~Ùñd˜þvàU)*eM”s´d¤¬P‰e¤FÊ*�‰`ü�`(«Æ~²ýür¨„)±ß¼ºÂÔ`
Ø†@š1Üàm›_]3æ7¤ÙU7§ü1Y?*W;Xü@]`b
wœ@¨m7)(Ö4•L
Ò¥WårV +º¡Å†ŸO¹¼€©~	EŸ?ß	œZªA‹º
Ÿ·À�c·o�xþ©»6ð·/] XÜ8ZöWt$d“0Œ£wõ·ƒ×ÄÌŽùà¼åKm'ã�šZ2)h<V€Uî«K‰Š:³&’ñ/Žeàæ	T½ú«ÕBT6gsZbA4 ¥uvIA…. ¥§@pÉI €¥Í¾ó$¥V­ÞºÀòy–E{[+šÚçÀãšÜ‡t¬-Åt9-…¡¤JFÌ}K'³XqÔ÷WFúŸ›jc1¸DaJZy»ybFÒ³õFnS�˜âLÄ‹Dàx† Ý¦B0äßŸ¿ÍŽ8 æ-GÈÅ¡ÞÃgÌI5Ç|+ÚôÔžcAR5
†Nx]"šÛç¢©¥eÂkÝ³QIá�%øÙÃ>ÑSº6 Œ %#9+<5Œâ�jrEƒ²­€É÷åÃÍ“œ¡šRÚôµœº¥¢µ…
À ¦lg#G0ž`-ËqË½móÁûk21EËü·4Ó»iõO”Ècðúƒ˜1k6|µõ“v>¬ËW22`hj�à[ S6¿ \åžÉüÎð¢mØÏj„êÊH/€É÷(˜¦k‡éjXS´� Øó;»¶0bCAVå‡s€‘Š­Ñ5•Xþÿ‚z·Ùô3mþÇeò»HúíVÿ|ð,‹ÆúzÌž5kÒø»È€%øåºõðÁÆ’¯ë‰Hé2a%•Yá³MÿlPMv¬¨&ƒ×S“*ü–¹_ÓÙ
°¸¶rHq¼[P" ¤©muöß¹w)íÿûæ®`j+÷ÿÝhþ'*ÈbdD·ÉL’[�ÝRÌÐTÇmºÊå å¹ ->\6æ¯%ÂŽ	AIžœiÐÅÌýR˜®V@Ä¡(¥ÀæÔ’Êù<@¾š< jŸñÿUƒ"©ˆ)ÆQ•ùWªN+?mÁëbÎÜ'¶ó/ 5Í04Õñ°ŽÌûj[�Ó�ÊY€³!NÂ‚ŒËCS!ÙUc|©iº¨¥.N¥Â�3Båy€ŒX¿i³ÈrÜrÞ„¯®í5®Lø/¥êˆË¦IÓÕª`¼uâõ¡ fvÌŸP~@—S‚ Leó[XÑ6Pú¼Jå0¼z2Zv…×¥DIN�qù2ó’‰ô*gY:ñó� ,M? ëÔq8°˜%;3Xh0T_‘]˜y²þþÜ='èšJæ›ÍDçÕ¹ ¤¨}×™ÿ©*$2YüÀD„µDTWA8b“Þî¦9%­ CNÚ–
3‚;£pœäõ«‘[E‘-ü™}+´dJ^ŸC?ß	b²mNr,ÅØ¥3ç}j‘õwæš² üí‹˜SHSŠY3�˜fM?Æ‹˜CÿÊ)ª6´„ß#zÀÊ¦ëfƒuûÀ”'.Gs\Âr`¸QEFuÍ¶@>òó'ÚN.ª†0s¿¦cUkRuö}—âZ…SR#Ë­ßGU(Å2 ´u  ZüTƒBÒL+àÝdþOÔ 	‹�[�/ü8o ¼/TÑ±<m2Ó{ŠÞ¨+Àð…ÄŸžŒ–�ûk‰pfÂ‰`Äâb<î¼Ûp6&Jð3çUÅÖaÕB5x€¶À¨0°íY¼ô“«ÿàzøÖ{­mÙjz% øg.Dƒ—‡›g¡êŠ™$ëïó¬ä_%h¬¯GMMzzû*j+¦Kq[á·À*Š°n¸`#Œd(2½Çt<`�E¹->R4$˜¹¯Ñ!°3J
?`F I„àr6I˜c™ª™ú¥`‘�Ó©>ÀâœU4¼Pxî!7‡Þ—ÁÛÞŠD×ÞïôÚlÇŽF	@ 4·M>>Sû/¥Í‹w‹ù?Yã£*M+¦šìH°…`CE£¸¼³—AO•núQªk`ºJ™¸?ÕdÇaÁdÂ™PM?ß	œúÜ“	§EwrÞs�EñûßÞ…½p-^¿õ›4Ñ½÷« ½6ÿ} ¼ðÌoP]%ÞÆ™`8ž™2	@Õ ™ðß»“=?ÎëámŸ‹D,‚þ�AÛªÉŽ{ë[¤ 4Ðéh†ãái[€TßA0®ÂfŒèáxèÑþ’V€žŒAãø¢s	Y—z2
Öå+kš
EJµÜ<™0S¿ŠŽ:Ïä%,9='¯Xþ^XŠ"‹â¡_ý ÿ{÷o �ô§_%? èMvïc €ÚR ¶ÎP>ð‰fE4ùx„\x‡óÍ§&Ãü/†RiÅ•v!ª42 ¶.€Gí¢i©i*t©¸¥@ZÜ>ù‡uy3V‰uÖ()ØV­°ÞX¡êtÚE‡¡À”ªãÅîÂ•ÿt6~ñó›é¨ð ½À^r­9ëc Ëß@uõúÅg|‹?úüÛûf ö¥°µ;Ÿhß¼!¥êP4)�BÖt$}Z»
1Y¯z`,Pu=ÃèR¼¢aœÙÐQÇghZOø¥?¶°5£	L¬Mù0ï¯+ï�@XbÝè1'ð| ¬ƒ&'¯._Ë€gè”	~6¼<.nüª8¦°¾ß*¨âY’ÞÇ\�—µŒ~oC	ÑîèéïÇ›o¿ƒ÷BMÅ¡�ô á®ÿåìS/þÙ³W�¼• ªk³ÀÝ: Pïáq8"!¥êÐ))ZÁåæÍVJÙ�‘¦H©ƒšaDÝ$W¦ƒbÐ¦I#‹HÄ¼è>°gÌÇá¼Gî5†iZ¼¤t®c}µ¶Â jl„åsÂ�9ÇJ‡y_ÈVø“´ËÈ‡$+¨©!ÓÊ¢ŒËÚ´Q °Ä”¿|a·CJÑáN�u^uó`ÎüpÏ^�è;9×gRŠVàCÖ•® !Ôoú ªN“4°„‚«àâX~kÿ,JÓ$ŠnDL¨Æ¤·«FòO5¡î–yPcCf-½ò¹“È€å×{Ú—ÎÙúÔs�µ'—:¦)ÉDÁº¼J¬òjd dM+z@Xñ”‚Ÿ³^ƒ“�©zF,açX,1Ï�ÉºÿœƒlPÕ °»“v¼†‹cZ�L � ž†hòñ™ƒI…P%íÌ±‚n>3B.,lð`i³unÌŠ¨÷šÍÙ	*Ÿ*ß¿øùèH¤Ì�¼¿î–Ž¢[9ˆu­Ž"Çµ´ŸåÎèî-}¥zöiš
-1R2ñH—%–!'m§1ÎÌœo"%CÕ§Þ-ËÆD†¼9Y¯ÀÂ+°ˆBnÎt³<KÀ0LŽðK+‹%ÙU*æ›çã	ÖÉˆ ¼ÁZÔºùQß^5 pkžM–1Ùì9ÄS¹r„å!6Ì¯$!uWÌˆµ-�‡{ÊZºAÎ%,w]bßë·z;Ž/š¹hUrÞšK€ð"¨¡C‰–$#µd4ã
0Âò¶ŠjºY’F!ØxŠ¦#,é�4³ŒÜ�SPSQˆ‚�gôûðšþºµ¢È¬êŒí*^~ÅÓ¨³UQ)òŒ\x†ä$‰S·áúûC•“m Àr<åý5¤ÆÍB1(dÍ€¤é¨á'?,RÊ�H©æyUêFLÿÈ]ýóÁ¸[æAéuLð&›/ë!÷”Ü�á¸–™ç~auçý?ù¾tø­kÜsVÝW—“`W)HøÑ4_]NBK„‹v5¦š
->Wã¬’J"%Ãç¦	˜‹…e‹iERÑÑ•0œ7ÄS•d¨±8 +¯¡nŽ % bÁüy<}çÆ|>fªoy9,51àâ
æÐšºY5ô6 pÕ6 ¨ñ™@:%ùÿŽeàwqcr#¦“û¯¨åÓN…šfxf,rÜ‹­(ÊðŸi=ÿË÷IáÁç”#;Kî«Æ†33³…?s-Ñ¡¢VG%uùÑTÂ²£’†þ˜Œ¸¬9VN‰x;wíÆƒ�<†×wìªŠë©9<F©Ï
ˆ…rÌ®F†0Ì `ÓNÐÅBÓ$U#Ã<Nw¸yA7�f¿€Y5n,nòaQƒ³B.´DøEŒœp¤$gÃ*	ËÃÝ2¯¢	Bœ7àˆK`ö;ŒÛA¢ÿð>¥ï@É}ÕØ0Ãeõó­Âñ`=�ŒòrÒ=h:qªNÑWrêx–@ å­H+"C5;wíÆÿ=ú4’69•@q˜›PJøÄÂï.àš€Ì  ŸÏÌó
,’ªÉÔO'¡©–µPçá1´ûUxâ]XÕêÅ’F:êÜh¹¹IWrª®CrXåe�÷×UdðÁ†²ûBNn9õÒK	Ë0Ñµ'VJ	po¶/ÒDÄÐTh‰0Ç›�Ió>ÛÌu(�nL�À³nÎüñ‹,¼<:êÜ.#GP–£šZ�é©'£¸ïþqpïî1Ÿ£aÐV‚S~¡ðÜSªÑÊQ]ó€2- Ï ©H©ú„€“…®¯ fÏ[· ¸‘ËŒªºYñ˜Ru$U#3þ|"0Ö‡Ü²œrb]ky2‘s]wÊu÷-yæß/¼Dê;ø0 "4Í)Ø�²< kP#ýE­]NA`ù¢V‚€Ø0«ä9'Rò„‘�™8:I“rEr[j=XBÐt¡;"å¼æu‰èÝ¿Ñ=¯Aê;�©¤jšà�¹ØöÞX•’O>ù>àõ¡±¥­âsgªNáD5Ý°uW\6‰|^‘mà Ô€K4ÓÝ‹ˆlF¼Âô eÆ‹½/>ÉU‹÷~ä‹¶¯ó,AÈÍe”‚aÐ)ABÑÍYˆ²Vµøðxëá…šfpÞ ¤þÎ²Œ¿l(`8®åÀÖ'¿j|V×éG¥¾ƒ¿Až`³ÚŒÓ´Èn<Êp|Ž©_¬+±ÙÔ4®L�‚„$Ãëß”$ž%eÝu>ÃÜ‹,A½—Ç`B…F�Ã/>œ®¨+ž¸Å{ƒ·ÁåAXºœÊIó~èáGðO}ž`Í¸®¯ŠåØeòr™Ë ´ Ñ E3 èÆ´" Ç
%6Œý»v oÇ‹Ôi}5Ã0¥Ð^ãÂ’fVµz±°Ám;|±TjþÛžŸàq”7à¤fÀ$¿²”þN×µÿ—èÚËàlH<C‘ ÅGÀp<Ø<s¿\Z³]^@>œò#@¡ùn)q¯ÀBäÙŠ„ß/0óÜA¿ÈAÐ“xéû_À+?ÿç’Â j"‚�—Â¡ßý’=ûrÆª¦}êo��iª–:Î�`/Ë Á3Z)&O³˜ùxðêßŸ èšJúw½2æã0ŸÈ™!žÏ˜B5I.+o AH8¾ld€aØï  (½‰°ü�– EBwTWÍ¨€�¹_ª�˜¡©¶Ó‚²!©FÁ}âY’v;A¯TØóÁ33‚…4cC½øÓ7?Šî7Ÿ­èxj"‚®©ÿ`Á}ëê<ˆÃ;^®ø�ö,æºÚYó.Žie@i  x·®´“!iÅ’Ž>~ëÕÌï{þþhÕŽ[L!4xù‚¹lœ„ÿ*…•EX*ÎÎy%• !ää¶³?û  ºzƒ¥R�oå^³àëöƒ°<Ôè íjohjÉnÁå:Ž‡¢j‚>–UÝ)fÔ¸Y4ûG•¹N)þú_Ÿ-»ê—ÂÐÿG-…¨ËI(á~hñaüý™§*¶œ&³ìëyˆ‡hfé·@JÕ�ê@6Þxî‰ÌïÝo�Ý(K!X.ÃòfÕ¸rÈFe‚,+'.AÙð çºÎšCuõ€|?Ñµ©}¯�°\Fð³¡F‡m#¥úZ\ `
;áxð.wæ‡ã8ÄULˆ C�Û¼.GPçáÐì°íÿnÁþ];Æu\]SÉÈ+†2Ò5ÒŸábF"tî»bì¢]ŠAÛ >À¬éæYÅ �T9ú5@Oç~$úgþÞ¿kÇ¸c²NÁ§I¤Ž:7VµzÑQç†G‹Â5An2©Ä%VúRáÁ!˜½–°ü�‰þÃH¾³Åö=ÔÐŠšü–Àp|æ‡uùÀº| º–vŽ³�„S“Ó£¯ÎÃÃ•·ÚÉ0žº÷—U9¾4Ò_à
 À[Û¶Vtœñf²«èe dh^KIHw”$•Â®—Ÿ)ØvxÛß'ý<†A@d�|þ7XÛÑ„U³0«ÞgL„2jšK-ÕR,CZ[î@ÿaÄw<mM04jlØ¼Î,a74¬ËgvNÿdŽ«©PÊôJtÚw¼°Ë�{áÑ –¤Z	¤Ãolë:¸oLd`9³2‹úËñÀŠ7˜’ÎÕÆÛ/=U°íÍ-/LÉ¹Þñ2Þxéy f^öìz?3QÊ€ó‹òå"B0�l%Ýö¤9«€1Û‡³¢''L˜/ìZ	Ö¿\ƒ˜¤BŸ„î<-~.žÍi¼i÷Ü8ÅG/»O?ý4^yå|ñKW0­ ªk™C‘ �«ž’DŽ©³µ¿Tƒõ
@ÓìÛöjÁöÎ7žƒa“Nrvíx‰®=PÂ}B£±ò€‹CÀågf×û1”T1Ž¡;ê<V/ õ* ë,%`W8d‚]ßò3kÕÕ@HLéÿ¶=‰Ð{ÎáscõjlØ¬øËZéÕèPéB!M+ê f'»RÖj¢Æ#ä¸ :îÚ>¦c}ô²Ëp÷]weþ>á„  ?ýÉ Îô_´Ð?Ð�Ù˜�;@7(]Ss^Ñtš^Y�éˆðî-¶f\¢ÿ0ûzÆpÄñaï‹�  zß(¾ºÔyxÌo­ÅIš1¿¥^÷øbËÃÕ8ËÖï/Y8Ä¹®ËïJo!_•Fú1ôâaØô1´[ÕKYz²ô
8n€�`äöÓsŽOêSÛ>ûé�§/V×Ð˜ñæØ�eHÁpP¨…ñ.èüâß·}­”N’‘‘£üÆo”ÝŸcÌTÔfÕbMG#ZcWV
±] Xd€á¸–ïßüß/¤•€šˆØ*]NAÏó%&—›™0]Gw¿›À°O³ÃËD�S…oW ãññÆ‚lâ±gës½×Í³9VÁX¹±a–­°‹ä‚£/”V!hA—S Zq—F•Šgjºy‚•€]âŒ«¦qGnýÕ¯
¶Ýrëmæ/,:Ž4ð‰pÉuƒæZ Ó¬kÖ˜“4ôíx1ûjbé À¾m¯Nª²ËNF’FúîÚWñ1,«`mG–¶×£Æ_yÑL1%P,2�Of�¥Â¯=ªæ
x~±R©¶çZ7`¢{ô�$Œ¤4ô'TôDeôÇdÌ^´lLÇúÍÝwã²Ë/Ç3Ï<ƒW_}W~ùüô'?  ˆ3ÁêÀy`E8ŽGcÃØÍXP,C�Ñ55˜Àtë›7VìyóEäp„ÜBnÌÜŒDdLé˜c…Åþ[è~ù‰1ÉD�‡Çò¶VÍn¨XØ)«›P>²3�N–Fú1òÊŸct¥Î_õKùúF™™Qibózb
$UÏIµ]²nã˜�÷›»ïÆ©§žŠÕ«Wg„ßUÓÂr™Fp�¯›—?8æÏ©4ƒ1 2NÚDS�¼”ß}ë7^qóú�WÜ�Lö…U"<ÑèïéÊN'Ý”vO*AÀÅeA%<��(J
r®ë²çÉg#�1x‡4Ò�È¶'s”@v6 ¡icv&ú™ŒÊ…–àIgœÞ[=átÍ<®`[ÛìŽ	‰D+á/–HÄ ÝÐLMÅ!«füÑž˜“òKÈ¿>ÿäòóOÞ!–»Üâ<ª%„å�E8þ…pÂF ±¾/Òjvž¸8Ìo­­È"°Sœ7PÀ0×rÀåúd±ã¬ßtÅgA˜ç]{ßÿff»!'s­€+}©f!šnLh>ÀPR-˜¿ÇxBØxÉgªr|WM#\�³¶·|EEÇoT®˜"e  %ŸV}óÆ
«ü @˜ç@éï¬×¨®n×5í **Ò¬ŒpÂ¨¦!7êšJvo/¨‹`f5®òJ\l(
ÁÊ¬€çŸ¼CfÜþ ì‹îÞuà�ùÃæZ%¢åF£Mô4§‘”IÎIÅPRCoLÁò}s-×qYŽ§5«Ï…PÓ>Ø˜áYj‚A´Ï�?¡×”;÷^`HB¢ ÀÈ&G–˜¡˜HXå¿,ÇSÂ°@Öoºâ öéšJ:·üÕÑ1Ã@JÑ—5„SÓ„QOTFçˆ„}C)¼3�Ä;I¼ÝÇÛ½qlë1Ž¼òË]C5Å,J ô&æ¹#¯=…Î°„îˆùÓWÐW0”T1”T•´t{v½âyu¯Ëg;‹äwìµË,gÉÈ0Ã‹›YŽ§Ã¯?F-¿ÞPåŒPJÈË±ãÝ+ðHDÁHJGoLÉ(–œù�[àm» Ö�ô!Â§*+z „ÁùjñÞ+6ÿ9âì(-°· h’5"  Éæ%kÆQ]`¥úêšv'ÕÕ‚”®çŸ¼Ca®FÙyKÀÃ)³lOTÎõÛ½qìèKbÏP
û‡%Sh£2**Â’fŽQ×Hš�Žv ŽØ5{ŽêZN|ˆ0ì{Þ6Ýkëý	ÅË–4$TôÆUtGåŒ²ÈVåCkÐ…÷ÌiÄ¬z_Ñ�‡™'�›6ÌˆžÂhçº®Ôgªü‚Añïº¦’ØÎç3|@vrÐØy€‰}UƒâH^û/ ð×5ãƒßúZWžRÑñxomç}¹Àô',�¶öÙ˜¹tMÅçÈ:tŠõ¸´[Ø%Íèæ 2PÈa3t?Ê-€Î7ž€ã	\ccŸ©ñW€ÜñÆKÏon½À™ëchª‘Œ1zt‰ð”pfØ¢$Ë�Ãˆ§dRTS '#�Fúc„å¿Hóª«Ûc¶ßøçÏŸt=+ˆ`yÄ ãòÁç!† êá­k6Çµ…êà©oü5Ðò¾Ž "ÏÂÃ3àY\Þ
À1³ëýL­Ï�ƒ½ƒ‘
/ÖÌlGªg4<ÉyC9ÂËp\ËÂ«o¿p÷>öÇb÷è¤ùïçŸ¼ó‚D×žž¶àëf@OÅ3ÊD��õlKˆ	ÇƒêöåÊq™ Ì�F)²òìB>ËOK)qL1 p4§+PLÖ °¬»ægcn	¦F2Ê•°6žvú˜È¿ñæ$•"EB †  73¸ŽæP`¦ü—��dd¸Ô¾Œ'pM¢kÏ™áÁÞ&_]3M5¢½�Lrà=û¡�ô ÒwÑ‘!hÑH#ýk„å¿og… –°Üq™š@GööR¹q¼×,ò©›1¾æÙÍY†À¬%Ð<žŒ†wqLú‡@àF»À\–ÏnÆÁÁ˜qh0^p-Œà�PÛ
e¸Û<÷t'¡ì˜~âà¶« U iëêë ô‘ð[Ï£víy Ò‚7t9FôØ¾×�â@‘~rž†Î~Fsœb5»(PºOàPRE“Ïì(ëƒ	5óÞ9ï=ÍkÎÂ¾·K7Íæ:XÑê	dÜŸsÎ>kBûÅ…]V¥fÐýa¹ÕUè)“p:‡lº &i�4QÙÀ³�=¤Ã~Ï?yGÉ÷ÉÈ0¹ú…ï}þ·P%$ú—p–ã©®©ý éèaÓzÂº	ÃF¨¡'Nè¤†€“6|ä�bçñü“wÈ æYäÚß_ºßK•””†ÃMõ†™A=ŠV€.PÑ™êÞ×›£{_Ïé¼ém›�ÆùËQ¿x-j_]3 €#*|"�Àf,Ëx§/Z0�ˆ÷×Á�â™•ŸórúüBNN�’+®\©ñWæ9599ÕõÜmLÁÜ%] Ã0–ÉA–²È=KIØ)‡á¤9¸ÛÆ%HH2|3Á7s‘yL)•ÓâŒj*”<²Ó*†ÚpÒº1uLÕiûúbÑ;¹NÈú !,÷)ªk·òÞ ÎýùX×nja».¢S	«ew\Ö�TtÄ£À¯yéû_@÷Öç>’Íü;ÀŸ| Èø~ ;òºt3œÐI)í<é”÷§wJÁx‚µP¥ÅÔÐS]_BVƒ+˜3ÛæcÆ{6aÆ{6"8Çd²]ƒ�‹ÍXšAq wÄ¶ò0Õ³7#ô†œÌ©4¤ø³+mA˜3A�GxoµkÏa8ˆõm`8¾dCwKGÑ×jC�qw®~‘Í‰�kè�$¡ÐQËW   wç�š¬'€óNZ9î•QƒÇÑ~ËZ¼¶ÛÚ9„í½‰œm‘¤r9Gã`fÇšj$UƒñLP×'°ÆˆÇd	Õ@2Í¾óÓ-Ó=vx7º·>ÿ\…ÂÂ	_ ºöSÂro�˜#àH¦‘Nu-gULÒ+ðé ]¼EX~@×&ºö®ßÝµçÌÝÞÒämœ‰ëÎFÛº³!Íè GTÔxøL�A0 `ç‘Ü‡ØÕ8É#»  3è#cˆÞK”V Ôø+€}j"ÒaÄ‡ÁøjÍ:ø2×EuÕÑ”ãÉ@­‡Ï<{€iId1pž ÔèÇcÑ¼¹X¶xá¸‡ÏýÿRŸ“=åÈBTRúaùeTW·ÀûðV-˜ƒf¿0)åÀ1IC"=ˆ#¥šB_jŠo|¨ÑÎÝHôìGïþ]Hv½©ï Õ5•°OŠÅ|îD˜
¢ºv@Wæ�…§_Š¶Õ§Ï3–"H©:¶uæ´,WcC>€j*¤�ÎÌks—�\óÌ�„Ë|úM ½Æ7ë8x;Ž¨ëò–e.Ô¶ä4ÉÆd[ uf~Déá …@öpP»q\c�‡gÐr•ßO`2ç��[·ô=Ô;¸’#¢§‹¦ÙòTÿÄfÍD³_¨êÍtb¾gCN&=ô6½0r`†Ž@ªg_±6MÄ4Ýµ ô˜ðg!­·¸�pÂÌØ�íŸ|åç¯_¶ýžÆŽùg\ŽŽ÷_fx\"Sëá±jV=vÈD	²ù Âñà<�7ÐoB	2 @Èã ôyð¼ÇÃPe°./Æ
E30öw�CÒM[|…ãÁ*ï³ V¢YéñàåáÔ/)°«© #C‡¸´9à—ÂýKc'Çc¾G»ö!Ö¹‘î9�<mÐà•´�þ*a¸'žò‘w¦ƒo>�‘NDºÀRxðÌí÷ÜôÅ=�Þõ�%|í'_`ÔúÜÌ’ö¦^@¨m5{t5',˜:ðú(£ Öo¼ü©Ÿý-Ub(©ÌÊNµâf~±ÙƒÓÇ¢Ñ—-Œn µ¶ûj ¥}H9“Èd€c�™>äT18M*V•´‚D Y3†ž¹ñ‚pÚF!ûºBé+i‚g£Jæ;)1*+Â¼	J÷–}ž0ìNð®�£´é£S]�6¾ùQÓGÿ«Yöúmÿ~íîÇ~»ùø+¾‰™ËÖ`Ns S	–l€2ÜmþLsá—ûˆ´BÞ C�•	”Î4Ÿ“‰™8™(§,¥  £\-€bÜ�]‡e�ÁÀÌ €7¬ˆ÷`fÝ£3&Á|ßàMlÈ[„åÞ¢šrÔT(V3E¨Çùj"í"|,Ù{ð¦çþûcÿÕ°æœ¬ÛüuÌhnAwTï¯ƒ�¡¤ÀûB�‡S º>ÏáGìÐaÈIPÃú.Ç7q20Ù£�QÅ g¥=sŒ–³Ggúþq%¬¾D~C>$ÍèÒ
€°ÜAª«™lÀýÃ ©bó}øÐ®rýÔöa4ÄVh¾SóªMþ˜èd¤Á¹Co<qÒŸ_~ø§ë®¾yÅ’OEwT†X×ŠTÏ>³©%ËƒZÖoÚ,–w»Ì,SCŠƒÓÛ¼ŸŽÐ …¬QÈ™ü¡`Y<cŽL³”‚;=þKÕ)º†aÄ†ÐÓß�7ß~oíDb¨ŒCÂ]ÿËÙ§^ü3 ­ ¨¡í€H·92œGØ™ï‰®=¥Ë|…°ìÎcæûÑC•_X¿ióÚ~|Õ¿ŒìûÄõk>r  ;ŠLV +º¡%Uô,XÝ†'ïØ_æ�C Ž:…>�¦b‰\¡ù¯QM£�­85tˆÁ_~ûküäûßÅ‘Ã�6G"[ApãÜf$#Ã�÷ÿØ¼V  ·ƒê*ý‡1°óU§æ;Ž™ïcÇ†ëïõËÊRCN6ò¡Æ¹Ztp €0™t±èžWnê~àG/u|îæ¹‚Ëó~ƒ0ýZ*þfË;¯tMé™>îï<ôëÿëïÜûËs®úïu)…`8Pg* —Z2ŠTÏžF˜>þQU×1ÔuÑ×Æ‚^9Õ§ãNÝÏ ãÜËðËMçã¡_ý ÿ{÷o²¬qò}€^
ä×Ç H›sHg’Á> ;	Ë¿Fm7a¸éT×c’í­çe]`þêkA��ÃÔ•Û?²û•ž9ñðœ·†�<ÝrúÇstš¶,·�hÊß)ùØ¾_\Yuad<ÁÚºE«oY÷Éë/TDû»¡F�êÙ]×Nì~àG/•>‚™àm›ßÂup5Îï�-BrÞø€ýmq»DÔ‡J�C/…„$£¿ûú½ÉƒÛ°éÛÝ×/²hôU7>V4zyø]åó	¼<|£Ñ9ÃC~ Üò]ôôôP]×þ(½)ÿ}�)â¸Ô¸ @˜wŠ™ï‰sÌ|/�õ›6‹+6žCXîÃïüè“?4Tùõð[×2V_bzOÇ¡Öï†Ï-¢ÖE ¹ÍtÑ “"éŽ´üîß¯Ü²úóß^ÑÒZ�ž¨ŒDÜ¬Ù`8n!€…à…‹D^À¢«oß†ù+á]Øù?—<?¦“Îƒ‘Œ¼þÄÅÙúÌç~ïÏ×K5ÍP£faO*6þ˜$¨ºŽápjd ±=¯€åK'Mæ`Òrp;ÌÊõ¹û‰^?.¼ô2œpÆEøÙ¯ï";¾ã{ñCo·ôÚìýFU‹jNÿnþwÌ|¯§üð¹ý‡||¸ŒM¯ò³þéøÍ_5XqãâùsÑQëF¨¦õ¾Òi¯ú¼YøÃMßXñÅ:®�ÙAV3ÄUôô¢ �#zGb£
AW¾Ò~Á—Ùyÿ�?[­k¢ºzÃk�ÿ©ÿ¸Ó/þ©h€–ˆB=Š@$–€.'AuÒH?¼�3Kî?]¦bqŒóV`ÅÎy ¡¢yÍYh^s¶={Í¾gþÔ¾0Ôv™e¹OìÜ¥ ,¼úö	ÁW»�’�ØÑ\ãGëqg¡-uð{Ýo½‚3VÌv|L–e°ñ’Ïd„0Ã@--�6`žI¤T;õb×á>tö‡;øÖòõ›6‹��à�=óÖÔNøÑx­« hÆÆKj¥-04†œ*:ÉxºÂŽ ´ß�Mê��°ËOAÃòS.êŠ<ˆ'ï¸8¦ Æ�Å_¿w=U’ßbX.Ó6Fà8tÌiÇq³š3ùà‹>=îþ×Ü®Z²FÍÎ>:…bÐÌ¬�šÚœ%ë.ý<jNè'?5ÔÍ³8~^ŽŸ×†”ªãÛOßºîù§îz|Án>™! tå¢E×Üþlc{Ç—Ÿ½êäÊæRg¡ëá[~X°Öàu?GïÑÃÿÉÕä²½-¸¦IÀíð<JïuG#0Žn³~?¦ ÆˆŽÏÝ<—÷…¾Mtå"Âš·Qà8,]8Kf6´U8›>ÿïè�)HiÔqïE‘%¶‰ž�›g!rž�ó,”‘>\ÿË{On>nÙ²{»Á°Ü)ƒ]‡Þ\tí�?jòn(_Ìc��?üÄ/]{ç¢©¾ÿN‘ÝOÐiÒdŽ-¯àÌ(V+`¥ågƒ¥ša<ø«]ÖßÇÀ¡ö¾ó5qþš‹€Ò‚¯hÂ’†˜¬CuÕ¡;*çtê+’ª‘3<ÓÃ3¹9xx+ÏºëN6‡[|þ¬Õ88œUó•¾xô²ÅWýúº�?üÄ/ÆòÙ»nºâª×ßê~àGU¿¯Z2
e¸òP7ÔØ09C5Wî°‹�!ú!x|àÝ>x›çÀÝ8b¨ÉÑ±©ÃiPÜ4à *ñÿ‹Y ½±ÂÕß ìÖìÈÝ»N8ËP:>wó\+øÏ?ïYøåUXÐÞ‚•K”~ÆeÃ)É¼•¾\Ç5-Ë  Ža�37‚Ž §_ü1Èš1íÎ®uãóg­ÆÛ»ñÐëûÑ;«#ü|ÑWï>­ÉëûôX¬'ï±ºMA5»ëP]ƒ¡iÈVTW?¸ñ½¯Aé?P2“´Ø+¼7ˆ†ù+P·h5ZÖžUT!X}À/­�Š4`;8íÉÁZÔÿ?.ìjÄü-çýS}¡cÅúM›ÅŸ5Î_å›×¶�²ˆ¬¥+/»ù†Âò7šSkª‡…Wß~!a˜_njçò'ªkCíá·±þüÓrö‹ËfÛp©HµªSð¬ùZ2ÝMXÓ�¢3ðx–dLAž!™^"K rÄV1HÅ�a	^‘E£—Ï(‚%³[Ñ1£�½þžÞ~ ÁE}Ñð²Å_¿÷SÕ
æ‚ =-xù&xlÏ«xöž‚D³3ÛÑÖÚŠÚ:³Ò®¶Öü?TÓ€ðˆIäc÷þNÚ»›ª‰é~óYt¿ù,ÞþÃ�é‚Ó.&s?xÑE5³ZM…1UH°¡¥Ø	ÃÅMóßåÐJ,•#p$Rh„“òcÙ
àÍ‹¯ŸX8û”´ Ï¥ Ç1„Ì[úéóâ…‘ƒ pá„ÿÍôâ']{ç†ù
 xg,Äi7þns×_oÅÝƒS/»K hzãjÁŠŸI3LW *�­¿�A¡*ÈêðÊ3�…—gàØœÕ !ë8 ëðŠ,šýx†@äœ»fæÏnÇoŸz‰xla|×ó—˜ @¶ æ@Ô€®ÂÐÔL_ÀDçÛè~ä ¦ÀoØp
ÞwÚiX»v-<žÂ¦ n·��…Ã4UI&{ìÇ–-[pï}÷á‘‡&;ý-$YÆÒÍ7@ÑP]Õ5ÈiâÒß^¼˜qºä 8�ÿóÿÍÀ@¢�ó }]¯dÿ=­À›_?‡aù&ß¼¶…CÎ¦ Ç�ÒV†arªÙ}Eotã»OþÁ|={Ç8°~ÓfqðøÓïf.²¶uÌž…å3€µk±óÑß`û3AûÚ3mo¶…°¤!"é™Î¬fGá~a¥$³Z
¼ùYí¿ÞcPD$É<îŒ  ŸÈåÄ…²Ž}r
>u3÷`Q£×ž"n}àIüí�Ÿ¼³~Ófq¿?´ª|vŸs¤3Eû 4)#}`E/O TW¡Œ˜}gÌlÇÝwÝ	 ðz½¶Â_
¼KÄâÅ‹±xñb|ìcÃu×Ý€o}ëF1ëZT)‘éi�8¸� 5Ë‹o( ‘%ãöÿF<èù®Û”(€bæ»­ ;˜S¨©*þß]?´þüKþ Ž±`Ãõ÷‡úbá‚Lxïøã`Vóèè¬ùïû0¢‰¢Â–4%Ô‚fªA3ÂO)Ð55§WÎ=àxpÜè×ÄeB��+`Š‡S:†S:‚.A›ó@ÄUÄ$m!x† èbÁ| 9glð_VœZ¸öÎíºéŠ«Æ{ÿ «®€ü ›¥ÃoA¬ŸªkÐ£ÍÏÛZ[Çñ	…˜7ovæ÷„$CÖ(´ø";Í‰Ñ®šF„Ž;©èû§CÐí�ý/ÿ?8RHƒåûÿÀ$( §æûxæ‘þâ¥G°#ÖŒÎà×9g„?+¶Ÿ/ü²¦£sD‚Fpš–CJš�¾¸jÛ‹�*)š‚Þ(SLM7 é@JE¦G�›gpqhŒæ¯›V�V $�bß`
í5.¸�ö¾øný¿GN> {pÿë‡ à+Ko|øÈŽëÏþÞ¸nb„ånb®�Fú‰;ÜVô€°Ü¤tÿIÄcÐ’QJ
‘7Ì‰Ñ³Nþ`I÷«šñÂ)(Ñsðp¤Päûÿ@ÀxÌ÷ñ ‹Ñï?û€yhBn¯é¿~Óf1[øŽÃñË— ¥f´N*¡èèÌbX5j’>‘Ã@Bµ�ÛS%…”$çaÕê€›RM÷BÕuD5|Ú„Œ¤G—Õ{r­…Î	F2‚›ÿ0jBAXc0ïýŒøðw_õëøXÃ„9×­«Ûu�;lŽïÞ!ÔFJäLšªIV F0òÊC�FúÁ{ƒXpÆeEßã4ñf"Á1Î{ æçÿ[ˆ«¶ñí7?[ðy•ž`µÍ÷ñâ¦?Üžn+F¶:RiŸtåÏþˆ¤Ù÷Ò×(ºÃQLî-µüÌkYm°M¢JÍd«Yí±ÇCÍVX„åÍŸ<· 0Õ JéJé¨s³E ÝQ~‘ËiøZ[W�UBç¾w9Â*ƒ§· á…Ÿ/þú½;ª ÿ¯TS.T|÷Kð/^_v h5 F?¸/? XvÉU€'TtqÌÂpÚI˜#´h¢Ðþ¡BÿŸã¸ûíÂã%?m2Ì÷ñàí�nÜÖùŠu_oüÑµwþ0›ð;þ¸Ž„?©hè�šÇ'•HÀPŠ�Å¶VACŠ—<¯Q]>úÅ*0GO–7‡yºr­‰¡”Ž¨¬¡Ö# äbÁ0ª�Ca­!·ùÕ¥ù‹¯ùþË7,E2ÇËû@UéþŽÏÝ¼f¼¥Æé±èŸ¥¿Mtíá0.ßxéä3?¸½ÿœ kÎAÛ)•|Ït  �šÿ¥Â{m€¢²Û×ö(ë7m±rMÊï÷çÜ‘©¿=¹øÉw§OŒy.]Í8ft|îæ¹T•¿‚ôj»xþÜŸ?¡èe… 4M«ëH&ì…ÚÐTr
Z"Àâ±Â´R@2Š¸ïòBGK]Uƒ /m¶„Œ5‘tø<c® Jàô5è¼÷YôŽÄêx�ï6 ÆýeQú;Ò
J¿?ôxoù&¡ã�îG×Ÿn¢º¦’À¼ã±æ×—}�Ç!ù6Q¨ÄüQ ±ôTé|Dø‹Ýþ¶êæù'ï�å—v½¬©Ó·�ãÛÝx¸k‡yÿ/ã=Þ¾_\¹øùßÿA—Sh®ñcQûh²ˆ¬åúüò…0GiÅ†í«Î´DÊpOFø�Ñ™xºœ2Ó`Gz!�4p©ž}HõìƒÜÊp7”án³A§Ï1¡	ËA’Ä"#‚,ç*ª¤jàÐH
IE‡7½Â‰ª9óâ†j¦{¬‡gð‰3×š÷–åNY|Õ¯?W•/�Ò›Ëß h—©jAMD k*	,\‹“¿ú0e­Ù[oj—¸j˜ÿ†ŸSút±ÌÍ¢ö†aÐ{£/ïÂtEfõþb¨òÕ8æà¶g¯9ø›¢‹fŒŽ¢Öt�#Î…ß2ë³+Ït9y¨;Gð¤ãá}Hõìƒ2Ü52 ]J€jrÎ Mª«0ä¤yüØ”‘^È‡ ÷‚)8¦¥²yÕ H¨Hd…ó•@oLÉÓì¨sãkMo�æªj}oTWo !�9‹bB1çÔáä«RVø�é‘ÇŸý·k ÐåL)ú#Åö/zÕüÌº?		±·Lõ})@,£™ÕŸÿg<ÇZzãÃ_�ñÁkž­YyÚ‹T×~#u¥îü×Ï`¤ß¬yïŽ)�‘Ë	¿=]ƒ®FrÂ^†œ„2Ü©¿³@)ä£ÔD]ª«Ð“QÈ��‡ºs&ìR]ÅÐÐPÆá
ž%èŽÊIŽ†I{bJNžBvùèŽŸƒ€Áîª*!ÁÑ“£¿!7Võ˜yðÍ:íÿttÎYk¯©V •$ÿ”2ÿíÊ5M¹§Ø±Šª’•÷Ýxàµ÷~i‹Ò5´6é÷À¿j>ôÁ‰Ý8Åo>•¶ÕÈÖñ¬þ§üð¹ƒ]‡¾˜w<óŽÇêÍÿ‚V—Ž·¶mÅsÿ«ÏßŒ„šû¥¨:u$üTS¡FMW ›é×Ñ¡743UÕPe³`ÅÐ3
ƒ°/‚0,Ã€°¼Iþq"˜¬H€¡¤ t‚óÀzC`8ó3“‰8MAcý¨UÓWÁ0‚.:%èŽ*˜2¹ƒ”ªc ¡¢ÁËcçlÿMZ¿æÌŽÏþø�jõ$™È\ †Í„+™‚g(Ü<)ÿŸjÿßïpõ9RÔügÐ6ûïé§¾¸¶³ØñJ:ºnÜÄ±ì½©]fh}:(MUqÛó�XW÷m8›šd‹þÎ}?f²jùç´ÏÏ2XÛ2IEÇ¡°žÑàFoÓH<÷&—þl?�j*”<K€ê*ôTºœÃ	04J¸z"’öóMmÎºý ËCÖƒ4‚õ˜‘	†ãÁÀq£ -…–ŒšÛ½fM…rGQ
dªÝº£2\�È±PušzÀä†û{ñûïü3®úô'°Ÿm¼uk*ƒâ .žÒ`°`ÀÍ³ikhTˆ¦ƒÿt;Ë	)•ü³«ß&âDéÿ–:^Ip�7þà�R�!Äm)�º3V#õv'¦
�Ü‰A% }ë7^ñÇ±Æý^}û…Ù™~sÚ3É3†1ZH¡I‡‡g��äœ|ª©Ž„_�t£Ñâ#Ð’³HeàpÙRØlðÞ ÄúpÏ\ÂòÐapÞPFà¤ÏA†Pk¦ÚJª�Áp-u£ì{çˆ„9µnp,“‰¸x<KP×Ü‚ïÝv àPï¶Þý,‚‹:>wóÜjXÔÐã=ÆX�Ru¤€«Àiã�‰B@tî~]öçÚ•Ø–jÆÈ¡C)u¼’
àù'ï�S'é!�nÆÆS»c@ÓeïCì™©Äûçg¬hùÍXãþë7m�ÿ²þöúühªmGSr›m f^ùp8šIÄ -YÈdç¿–C‰š#¤YÑC•¡FÍ•]ï qp;Õ55³üÔ^,ô6 èñ¡‰5É«>=…Îè zå•ÔDj"+œæ[¸¾ÙË Kqð¾˜ô N]NA�A¨1{½Jª�þH�Aóu�šM#[¦ù?”Ô0¯^ Ï$U)E‡[`1«¹óZë±·{¼7ðe W�ÿ›$# Å@$>þC�ÙVAƒ—‡fÐ)³j®þ>�)šû¿³¿Ðü×ÁÜ]®oCÙ¸C ­é&­³?“A‘ÚuÝ?­Ÿ?É·MºKpî†3�g€¿öïù£1ÆnÅCË7|<ÝE °pVK¦®Þ0Äeäg=Xí¸­Œ=«Ä4j¤¸ð[¯ër4Cø­ç­P©¼8·y	6úf¡Ã[Ø_bƒÓ¼‹è2:µ(^9Œ�©!ª&"däõÇß½5'œÚ8¬è†PÛ
ÂrÐ¥8”‘ÞŒH¤d„9!¯Ù#,i¨õp¹8ƒ”ªƒO»EYÏ¦\¸j¾Ó= —­ß´ùkãMºb8>f¨2”¬Â D"�††ÛýS)gýü*…•ü“Ttð,K&· ò¯Æm/®Šf`g¡A“”»Ë³¬X~Ïu/½vâ—Bæd>°k‡þë·˜õ��˜4‰ÖÀÙó—ãìùËaÆŸÜÙuà—+ï»±¢P%ÌU–x{}~£fqXÒ¡¹Â/ËfUYæýº
52 Â‰kÀ�â0äQL—S9Â¯'cÐ£ý�#ƒˆîÞÀœ¬tEëJòå§‚¯±ÏŒSGâ@bô8AVÄ2¶ËšÑeòŠÜ‡GûÌ¦ýÏÜƒÀÂµ-ß˜^ùÍÎ8ùJ`$–‚Àsð\ƒ¤¢ga„SZ†e§4Ôyxð,ÁÊ³!<ºŠ¦Õ¬Øxž¼£äxð²ß¥� Ð7<1ù N7?ú]«:…
@Ò4¸ÒÍV&Ú*:ú˜±ÿbá¿½CR&|k�€îú«/w\GªÇ ôæüm4)ãàu·!ùÖ!4|ìt°õcŸÚ20SÃ0Ì×k–tì?øo·?¹ÿºÛ6o¸þþP¹÷-¼úö³Wÿö–†œÕ8YFQ¥\íj•³RM6_ŠCÍvª«PÂ}™¿õdòp’·e„©¿�~ä›äºË>‡Ú“–Á¿düKæÀ=Üóg€õ›fzg‚œîZÔó¹µòAVÄû<í¸®ýR/x ÑÝ[0ô÷ÿK}Ö9Hñ—%žRàX¸8’F—MkF5(¢Y³!­ßy–à½‹Í~ú„aNÁ8qÒ)îg9žª‰"‘ÑóÒ´É"ZŒT“4Š¤bYiÆ8˜æà8šú 5žâáÌ7º
S*RŠ~‹“ã:R _=ã}¿¦”ÚÚ`ý¿{ûòGº|#ü–MÈ�*B˜�,ËÞ~'¢#ÿíößï¿î¶Íë7m¶ÿ’8¸í*iàt9�ãr|§«~l^�ešN €5ÌCJ yp+â»· Ño©o_�¾ô-ÌŸ3Ÿëÿq<Žçá™ÙÖïA½èÍ¼VL Àˆ¡PÀ´*]{0øÜïM¡�åº  À»Ü0rVÐpjô÷xVÇ¡ìîÅkÚ3–Rñ’:‡xþÉ;d]Sû³½—¨RùUÝjæ¤$œgHÙì;U7ATÒ hÎ:9;EÈ¡ð•“J÷¡_:9®#ðÌ�„S*öºòÒnrðÓ?_ëGÝÕçÃ½¤½ª7ªÂ\Â²ìíw�´±ÇR–e@8af|ß'Ç¼‰è®—�ŒãpO?Âá¨ºýêŸHå>tÙÍ, 3†o1ü†¦A‰@KFAUJï>Dß|,3`µ^ðâçù*nøø—
ßRg?Ü¼˜£ Sd+�Wä>¤‰Ä}º®Ÿ –èÚƒ‘7‡.%`(æù1CIf|Î¡,¡O*æ| éÒâtßÂ,2táÓ`¦®ãs7Ïÿ—Å¼ {÷ÌlR”*MV¥²»d›ÿN i¦uT«€cœ‡þjÜlQòïÍîBÕ	ù—9§'hkºI=Ôw)Rú§taÏ~ŒÆoBã§Î„oÍBÿeË”å¤Ë“/aY\r'¢ ÿvûSGžzmèŠ�§Ð·s/À»àn��={÷‚óÕ€j‡ Kq¸\nPo\¢ †rÒiíV=9ê¨ñ0�oAêÕèÉ¤‘~°Oÿß¦‹Èæ•i~�•Øú boìMšŸåæEÔ”G]‘ g’xû¤<Ú·› „åþ‡êÚ_­Ê»èî-p·-€—á˜äš–ˆBÔ�cMÂOÒŒL\B5LÇÇ³ÉÀ¨dò‡æ?zGbàÜ¾•ït`J÷8ùà¡} Ì6æ¥\ E–Á»JÏõM+EI”îj=c ªê£�¬&­•¢’Õßjã–�˜¤a÷@¡a‰F¿ïôØŽÏbù=×½ôÚ{¿´ÀÚRûõÿîI„ŸÝŠYßü(Ú®<ñ­ûynÇ”'Âlœ¹i5žÂjÄb1ú\ïr¤-…WºßÆ®#lFÐeO „‘Ý¦Y¯É™¬;=Ý_Hççk*”Þ}P%“üÓ"ýÐ¢Ðj"‚¥þfœµé"l^¹~¿pPPÉÖ ÷ÁÊ»°PL	d¯þ'møè]Ï?y‡™jr@7GÞxÞöã2¹€)\Àü=&ë£
@Ñ3¦¦¤ê20›`Z4£½#1‹‚í ÀÞ={3›ò™²‘�%xQšk²\ CNÒR÷Ûì°<þô_I£�4<KÀ³ÎIC§¹¥Vÿ×m|út%ÓŸ*jbe–ÛÏ²êN?uWŸ�¶ÓG €ßï'gûÍÆ�Ÿi7³ß	à`x;jÂØçp¨'ŠÁtcI«�•<�+”z|z2]1Ú¦Ú –{qÜªEØ´è,iÈô»sôT°õèƒQDž¶ÿþ\œ d)€ˆ&å¬þÙ¡9Æ¸ÆHF.”FúýÉ#»áïX	¤€–Œi•´L`v³ì¶æ	EG]zŸÕ´“0mãÿ&È[ ÅÖwF‰R¡@'L¡k*¡J
D°/
T°;A%VAÈÍ9ý[ýÍÀŽ¾ÂÌ¿hJþ¯rÇÌFEwaÍ–ŸÞ÷Ú{¿´…€¬u²ÿÐã¯bø…íhûô9h¸d|+:¦•"ÈÜžÇ’†V,ihÅÙ P Ð–®AJ’èa)F à`xvÁ{çs>¨>„ùõÅà^ÒŽÔÛ�züÕ¢ûBP/z3VÀ-=¯¦W²5¿ª9Î�Ü
Ðkâ{^†oöqk†j*4Ýl\¢ªAÁ3ªN¡éK
™Zhò¥CkÄ8Á¸ý/i7éÀ�˜3ÇŒ4kšVÐñp–PmL¥EÁ×ÛO¹«« ²QÊ*àçU¥ÞèŠÛ…þÞpúËFÅwÁ©`�&eùÑÑÿ‡gÐúùóP³q%|+:�:Ø‹øË»§4­¸ìÍáyøyž,1Í÷ì½ªðoX†‘'^/ºòÛáõx^9–ã)%ÌíâÞ/üÑPåkR]ïPCÓˆ.§Àq¦Mäà1ýeYÕÁ§ÙpƒZÎ²6JÖÖ¥‹Š(œÍã*SIa€ŽW_ß–Q Š¢Ø* 'ð`9žêšJñBõ…ûDfRºÿØY!—óÕ?»}[6ÍÀ›Ý6ÕÔ”:öý-Tì­ÙòÓû(¥×+]C8xÝmØõÉïaä©7ážÝŒ†K6 åë—À¿aÙ¤çL°õø7,CßÝO8~' ¢Iøé³}¿®iw«ˆ|ïÉ—¾j	ƒÈ!4³C›ŠjRý<K QóF}ÿì…¦IHƒaêQ�çàÕ—_Ìl)E:	¶´´ PFzm_+ù7HéIÐÇ@ÓËÏ(,µúoïML’" ûüÄŠ²™ù“šÕãk•XÙHí:Œƒ×Ý†¾E3ÑtÙûP³qeæçh°
ªÿ†eÐ"8ô_¿Í°ýNqKÏ«™‚¨´¯o»_šØ C‹‡˜µ
òP7æ-^Šµµ<êjB¨­«g²gâ©:ÅðÐ ¤p±á.ŠA\Ü�9óTý>–}žêÚæ×wìÌl+E:‰´µ¶âÈáN(‘Á‚×Ü<;eµÿ!gŽ‚×œév³J­þ¯¶qŸ)-ßóÌcR k¶üô¾üôàJa)‚î¶:4^´¡óÖÁ=»îÙÍP%òî#Hí>ò®SþËÀ×úqäÇ*`ú�àÁ¾·3¦¿®ëK›Ñ%@â EbÿÆâ´s.À	ûbÉ!˜<KÐÔØ Ža°pÎL,ð×ÇÇï~qR‹ ëM |i!tÂ<
˜-¼¶¼±kW-C"‘@MM�­à$0oþ<lÙòä¡®‚×êÜSSùçâH¦æŸ!Zšo�j€cIN/ÀRÌÿ]ñª­þÀ8æŒÇ
È†Ò5„#?ú#ºn}µ'-CíßßŠð+:à[Ñ‘£”þð´"+�Ã2¸æ4càöÇK}¥°#Ü�ÿì|– º¦ý  ¡RËñô}ç^HÎýÈ'?©æôœ,³”cˆ�‡7«[ýÎ=óýàÎ9o<z¾ækðÎ[]•IÌæ² +ž~ü¬]ef“&“É£Ÿ'DàìY  yðHÎv'™…z/¦H�¦SD4gr�"NBÑñj—­ï?¦Õg£ß×Þû¥—œF*�{ÑLÔ�±ž3VÁÌ}R{!è…6™öÖ�{I;Üg€zÇ%ø p8>ŒOîú“iúæ¹õ/?½”ð1ž`­‘ŠÝÏ{ü'ã‡ÿ‹ùïY Iéè�)I*Kz&P~‘Å{fø13(¢ÁË££Î…žƒ{ñþ3ÎÄ‘Ã‡·Ž?w¼ÃXËß@uõzÞÄã= ÀœX,ØÚÔ\Ò¸ýöÛññ�ÞÆ™Xqý™íÍ~¡¢œjÁ#°hõ;ãjÜ,¿ïv¼ü‡_¢­µüŒÅXÑšæ"én@SÆ_ŸihB@ßxðãË�ó½Ï…m»ô?×©‡ú^$8ÀR¾5ážÝ\ðz¶B˜‚%ôdv”×÷£û/§ÊK»ÇuƒRªŒËvÜ‹© ØÇx‚kJ™þ„fRMyŠåø¹_»ëI2Þ<
Ëè‘ i¹”µò[D  ¼Ù:ØRsjÝàY�!XÕæÃ™k±oß>,]ý^H#ý}„åO§º:æ’PÂòË¨®n€o~ózœ~º™8cÆ[7 >TS°0dã™gžÁ©§ž
 8ñ/€nðAGÝØ[°�í5.¢;zŽÛ^zßûæÕ™ú‘,Ä@˜7Vmêš;žêxÿe/T’ø“�qKîó'é>�N.ïqœ@h«Cè”ð7¾Û®ª$Cë�I µÛ4'J1°õ�¡ŒÀ¢ˆø‹;{f;†_Ø^1¹g‡”*ã³»°f–6Ó,¿s@W|øß~Š5ï;Û{Á×t‰Dƒ±$Y)èT”s}¢.QÀq³ZÐÁ³³B"Ö´Ðêçñìã�àüóÏ(%Ø cíÚuøö·Í>„µµµ¶n  Ìš5«è�úûûÑÔdF)WþË=ðÌX0e«ƒ—wTòkPŠ¶ ˜“ êÃÿüËW°ëÅ¿ [O`Ó8ïqÆ­ Þ¼øú9ú‘á·!“®^Ý‹fÂ¿j><ÇÍ‚kN³­…��D$
f$	=b&Ò¨Ã¦?¥¯IçÌX9_kæ°A/¸æð.1‡ŸèßòU·$ÕzyÂcxñ¬²MP	¹”~oî¢¥øÄÍÁÛé>q’f «o ‰heõ÷Þ@l:_ Ù/à’å�XÔhfÖ{x|ú#Ä#?€| ×Žãr÷è €Ûn»sæÌ—0³}ŽîÄœËnDûIçMÉêïâZbQß?nžA{�«`»ªSüúGßÅc·} ä#fšwõ0n•¸ò¾¼ºîÊ›!×UóÄœ µëp“N<"\í�f)m} žãf�¯õƒÌn€70ÍÆ`åù–â�ôf"ÉÃ}ù,~Uý Ãña\µç!Ëìw&ü @éç`åY—f„?�£³·ßv"¯�2«5Çƒp"ø@„Ú–Œð GÌ¹s�A{È¯@ð¹Ï~6­ è§×oÚü�1‘‚„|”vX>ñ·¿áÓŸú‰¼^/<žÂòçráÀeË–âÈáNÄnGÓªæWã¥ˆ¿|XmÙòÁ³â)W`¡þæ†_(‘�çÇË¹d£*6ÑUžØþ(øèxÂ‚Õ MÊJ!Ä#‚¯ñ�õ{ ‡<Ôír|;)I¢l8I @�%¡ta²±#Ü�kö>2ëçÅ9~Æ¬5’‘ ÐÚO `
ÿ¡®îÌ>Z|áíÏ"¾çe¤ºÞÉéG˜–ã©wö2â[¸'¼ï<,oY ØÖGÈÅ!äöbÝºuÖîþžùí qéHÂdo»÷OÆ%_Œ`0ˆD"a« Ã#%y€ãW­Æ#?ŒÔá�SÂü×z8¸V	Ö¸Ù¢aÙ—;£Jª¨[ø¸®þu óß¹wäÍ¿�X­ó¬ÚªõòÚ/^\�°à?2(¥x°ïmügç³–`î#,ÿA§[ZÀ‡~ùwˆ¾öuõeÌþðögÐó—›K
}1°OO»ü*rî§®B�›E�‡Ç™ÍÊ;Á‚šˆ€°üòŠÉÀ´ËÂr<õ/YOÂÛž |ô²ËðéO}
@q2°”�M^vûË<“—iÊ³3ƒÎLÿlâ/QIÃí¯öæÔe0††¡®ƒ'Wcj30†TàbX³å§÷QÐ-Õ:Þ?RªŒí|
7îÿ›YàC˜çOpM%•&ˆú @M×ÃKòhƒ�¾Ç�±?`V×=vÛwá˜ô(ró¡T%†œ¤,ÇS"zº*9&á„™VÛÛq<q7ÏÍ½÷OÎ´
±}ÿH	>ã„•«Àr<€C»ßÏWS1š|ÎLƒR´Š‡ŸÜ.(Ê’5úDµ„¨¢  ¶­öÃÅZ‡Cq<=°ço½wíH?´äûë7^~úØ_ò 8²ÍÌ­¯Œvª;ñÂq�çú}’jöÉ³âÚ�<ö(tM%º¦>TéùRMù ?ïÂ3s	XO ÁåfPMDpï}æ\‚D"a[P*)Èà„÷¼‡ Ààë��ëº+A%¦�‡+:xwûòÆ|3†7Q¾ZÍó­j^ä/Þ~&ü™k\„�q7�üGÀáø0¾Ûó"n>ø’f¡N¥¸ ?ë<°uLèOðUªÊÜó¦oÖÚ÷#P×ˆ¤ªC•exf.Bpñ‰`ˆ’ÈX	¥àªiDÍ	gáÄOþN¿ð2ÈC€%M^ˆj
gŸ{.¢ÑHŒ°üGAg“M “øþ êV½œ¯B¨| É[a¨2ví;„ó?p6\.t]‡×ë-8OEÜ€ƒ�à¹çžA*ÆÒ³¯˜ðïÓÅ4x8É‹áX‚™!—ík)UÇß*h;æáÙ¯=uåÚñ5aÉCÕ3xÖoÚ,þH
<3‚ï¤T¿ê{wv¿™å�“;Ò…=ãŽó2¼x’¡Ê�xgúOû—[áªŸ�Á‘0†Ã¹CJÓíÂ•È t9	ªk ,Vô€ó†àné@pF†œÇŒ€€—5b^�ýÃaÜø™Kðêk¯Q]×>ZIx*�¨ô
€&ß¬ãà_¸|°lzŠolß|ñ~ ÀYgŸ�¯ýó? m	Áb9¯¾ú*V¯^ 8ïE}Ûì	üV�'ü”bv­«èêÿÐÎ¡‚)¿²$mw?tËêñ¦^çcBRøÒ‚ONEnÀt†%øî}Ûbø�­/|±Z#Î-¤•ÀÿñÞ`Ó²K®Â¼MæH¿Á‘0²ê8À¢±¾^-«gø°ûñ{ðïÿý$úÇÒýÿúM›ÅçŸºëqPãdÞDÝ‰‚õø!F»3‹í8xû×2£ÒÊå”Ê´ò–]z-V_øéjÞâ´Dxygu½�/ÚégwÙ•ybŒ.¯OÆ_1LHiÔÏw<yä˜+0ŠÃñaÜÙÿ&®Úó}}ä0±Ì}ÂrW¯ßxù—í{­ê3Ø©¡&œð{]NÎïyó™_ø3tMEãìEhnl@]m‚¿á÷z‰èáõzr~êkBh¨¯5ê‚~²¬ÙA�ã�§þ·~ó³øã½÷@MÆŸ#,÷!úß*9¯ÎÛ¾ÐK ö„³ÀyƒkF{‹˜–€¬¿Ñ]fÏƒí»÷áüœU5§‹b®ÉŸ”$„B!ÛÏÛà0^yù%$F±ôÌ�LÈ÷[ëátjtóLÑ˜TÒðÀÛ“cú[˜°$þtW ¥ÊØ>Œÿ4Ëw³°„ü|ýÆ+n®¶9Wikàë,ÇŸ£k*ñÍ:3­€»u>¼ÍsPÓØ*x!ð,UQHDÃˆöv"¾ÿMÈ‡¶aÿ®V§�‡@˜Ÿ‚*ó`&üü ×Â;k)„Ú–Ì(sÖåÍL/€Î{ÿñCo ¾ø¥«qñ‡Î`,fd»§Ýxf-^YÕ{[I¶ Ì­s�ùÿþÍ>‰æ¶EWõ	ñÏ?ûÀD=+Úi*Ó„§–Ð?‘ìÄ£}»óãíû@È¿¦'OŠàçƒñk©œ¸�êúz€®G:õ¶ú ¼BXîæÑ±f¡Y¼ ¿·m>ü‹×ƒÔ‚ËŠÏ‹íe ˜3öüä3T×TÂr<ýÕ­¿$¥\�b\@ÇâeØ¿kæœú!lüÂ·ªv?+‰÷—óû_îŒâ¹ƒ¹n†¨«žúâÚ	+{�ðÆh/¯ýâÅ,ÃÜ;‘ƒSJ)ÞŠô`Ÿ4‚'Föá¥XW‰$²uý¦+ÖN•ðÛÁTÉ6jèm ­!ë5Çv“†ãcà];«AJ¦«ýÐäªiDè=çøýBmk† ÆåƒPÓŒÞÇoC÷#æ”«¹‹–â×¿0§ÔÙ
¹Ýn46ö*ýÎoÆ×®þXŽ§þÕ¤ZIA3‚‚ã�_)¿¿;ªàwoölWRÉ+^ýêú»ªr²E0)R9™ƒJ)Ž$F°/5Œíò Þ‰”øB–¿‘êêS}-“‰láç½AÔ®=¬ÛŸã÷sÞø,e@8BM3Çƒj*ÞùÑ'�èÚ 7CÐ.*`—˜ˆDQÓÖ5©X	éW¬Ð0C~w¿Þ‹¨œ7Ž>½së×OÝ<A_Ëè½žè Ò|@*°sªkŠ�R
IS0('Ñ$ªÉÌîwbè–"èTãE…�åxÊˆžÌké±ß™î´ÙûQÂœ\mÆº"_økN8œ'>ØPÔïLk€G[êÙ‹Ý?øxæ^~û?ÉtÊçŠYW~ùüô'?€«¦ùÙ ;öÆ NK|�Ò©¾€½ß?Q!?;Lš]>>à�Þ·ðoUí\éñ^1]Áˆ¡T´Š[Â.x ïáÐTzV[n]‘Á
bæw5ÉV}„VW³¢k:Â
C¢„ðN„«aFÎûø`CN¢…ÁïGç}fŸ WM#~û­ƒ¶|€!xèÐ!tÌ›OuM%Çüz¬<ëÃcº®Z‡Z‡3ý€Ò¤ßSûÂÓ}0Ìª‰Mò³Ã¤:æcåžØ‹kö<<)çÈr<åý5„åÅŒ€3.ÑÞå�*%‘ß…×PePMÉì]…2t,/‚¸aÅµ'ª¹Ã´�Éöß‚tšo1ájšrH?Ëï/†wÿFÒi½s-Å¾ý­¢JÀÎÈ¶.úÁ_*.
¸8Ô{8ÇŒ{�X”ôÛÙ—ÀÃ»¿þÉðû³1éÌÜ«ë®ü†a*êp8>Œó·¥›žòU Ý•¼ Ã-ôÔ5gš'zf/‡Øº /‚ÝP¢C ÂŒúuz*%œKÎ]…<Ü°|F)P–‡–ˆÀ�“ö®«ÐS1NÈ„µÞ­J€°üÁ¿éšJ\5�®:¬è)+ü„!6Ì,ylCNbÏÍŸÍð¥ºÙ¹ÙVÀÂó>‹“.»ÚñuyÍùäw÷ÉFLÆ=[

}¤dòÛ¯ÿ¿õ_Ÿð/)“Þ#ù—G^~êòÙk–ò”,qúž ïÂûß2óå	óPz?€•ü0nÿ6WýŒ¯1¢Œèë«E`ápÞ X—´`¦<a9ªd¦ÇºüàüµàCM¦EÀ`À˜·�eYP |°|°œ¯†ª€ê*Äš&ðõ3`(x_Jd  š©ªœI8á!úÑÙê8Œ'XKUù^Pã3Ô0ˆ·m>‚ËßÖå¨/+üBM3Súq$�Àâ÷bäµG`¨2ººŽ ¯¿ëO:	©T*'IHÓ4(Š’S?
… (:yî¹gÞ·�ÎXw6ñ¤g$–‚‹#hñŽ…¿Ñ/ ¦ˆ›•4Ü³qÝ2ª  ÍIDATmrÞpUQŸxíÚ÷N8é—�)i’þÎ¼Åž©‰§�Nö'„àåÈ–Â ,·ÔxºÒÏ¤ªœò´/9“*Ò Ðå$¼íÇ™«>Ë›Âlè`Ý°¢¼7Þ_Þ_Fp�uûÀð"Ë�áEh‰0 €\àýuêZÁyC™é;„å!Öµ�uû–a0‚†ª@¬i‚¤ÔÐ›aèaxñEjèG/'@˜3©*=àxÀLòñu¼¬Ç1Ô�ì’ÂÏ9ó«ëjC˜½ò$t>ÿ ¥†AöîÙUÓðžã�G*•‚ àÓ35M+(Zûžãqço~‹Hx„Äú:1ïäsK~^¥‰>µ>{‚1¥êøÃöADåÜÊFY’¶{#}gzæi2¿6`Š@ç�­úÖ�õ7#š:�Rãä=ª®á™‘és¦·�åsëŽ¿H(=‡õÕ‚uûánš±~&X—¬ÛÂ°`8Œ ‚p¼ù· BKäúüœÇÎ—VŒà6÷åE€R°ž „PXÑÖåƒ.% j€°œièø@=Ñ"ý”†�a˜�SJã ^ËuMÌU_ù9@¿ÀÇ{ƒ¨[õ>¸šç‚÷×€÷�~µvÂ bÝÇÂrsy]pÕ4"Ð¾˜ô¼ú8¥†A¶oÛ–Q‰D"G	$%	^—l:R ¸DÌëèÀ=÷ÜƒXï!_šç/³ý¼J…ßïâÐ\$ÍWÕ)îß1€¾¸š³� ÃŽnxúŸß×çä3ª�©“³tøÊõçmÑÃñ�BÊ>^p¸§oXŽk'.ÿO¨*WÜw ÑµwŸ»¥ãkÖß†ªÀÝ2šG@a7d“=`8oZ°Ý~ðþÚŒ`›û¸!àjœ:¨an'†M€åÁ
n°./¨¡�óÕCŠC““ÀûA˜ÓË½ZQYí`ý¦ÍbçÁmWQUºÀ 0³ûÞÆ€PÓÎí½./ÄºÖ_l˜U±ð[ð·ÌF }19ò’Ioß¶-ãä+�X"žS+°páBìÝ·Û·mCÿŽ¿Ûº•
¿›g0#T¼Gá_wcÿHîo1þÏ|ù½ïLÂ×f‹)S €Y4ô©¶ÕïB>PN	X<@B•Õ”­0}ûŠ@U9åmœ±œ° ôTžÖy œ`šö‚Ô$Ó®€¹Ò‡@½àae]^0o6Ïtû3¯3‚zj4¼CXœ' šV.„aÁºü`¨ËñPcÃ è,–e?GH×õO«æ*ë7mw¾õñÎýoüÀ¥ DÞD`ñ{án›Îí¨3]¥´°sÞ„P.G8qL+>ü-³œ},K`ïž=Ø¹sN?ý}H$9œ@>°~ýzÜóû{	��#;¶`Á) ›wæXÇ>?P:Ñ þ¶g;úr	bÆÐÀ3ôüg¾tâß'÷[ÌÅ”*  ¸µë•·ÓJàâRáABv«aì‰õÃœw‡ûÇòy5«No`=Ás8_x-8_Ü-æ*/z@X L:"0z{(¨®eÌzÖãç«ÃrÝvM÷f” Ãñ`]>pî,w€!ýy,!Ô„,kàDªÊ_ H=á„�SM2ž`-Õ”/txó6Pc3€Z–ã©îJâ_´œ¯| œ7dÞ3j¤c+8on¨­RŸ¿˜ð[°,K	tuÁ³/½ŠSO>	”ÒŒÈ'½^/Þsüñ¸ëîßP92@”¾ý˜yâ9±ý@yáî@¯uÅ¶Ë’tÅß¿râŸ&û»ÌÇ”+ ÀTŸž±&F€3J)FÕñèð hY¿ióOÆÒ5gÅÌ¥ÛÂ.ï'YÑã',=ƒ»iNFØÞ]2¿0Âr`Ñl�á«ëò˜ÖÇgÂ…Œà‚¡HWÀ‚É' º
Â‰émX—º"†BÞ•q	øP8—z"L©aˆ Ndæ*jÐ9/L6QÈðâIÔ0þƒªÒ- ÎEZð½3“Àqë	l ç	B5æøö¬è.ÈíÌ8¿X×V–í·PNø-ø[f£eùzÒ³õYhR#ƒý¸ÿÑ'1á"Ô…PU^¯·@	Ìž=®@yâÑG0xä ¼F+OÚT5áñP/uên/G¾öü—×þ|¢¿?'˜
  n=òò‹å”@“àÅýÛ(5ß‘#»þ:�è<°Ut¬L1‚ûÀôÑ9_êjCx„<"·*gZïÊ‚„°¶Ã‹¶9 „ãÁºý0²,yÂpà¼A€¦âÀ¨K`YbC;á\^PM±,‚•ÔÐ?àó ia˜õ›®8<Ö¶aÅ`úöÛO7?wRCÿ2€• ÄÑÿD"Ô¶€sû!„ÁºrÛt	Ázð�Ñ¤KØYW .øÝÎÓu]5�hYszw¼56MJà‰G�ªiX¶lTU…(Š0#G	œ´nM†èÜù&8–ÇìåkÊ~^9á¹3ŠÙÿãŸ]õ�j~oãÁ´Q €©>3cà;%À³:µÙë5ctŽÿÈ¿î‘óõŒÙ
Šy³Ûá9¸x^‘Ç`¼PÐ	Ã‚ ™Pßèv„1sr¶§-Ftƒjr†`òy.Ã»Ày‚E Ô¶@ð×‚Pš”  €zYç�­_aÎ°„Ô–'ë7^qªO°– Çƒ�3@éGA˜ëº¿õKjè—™Ÿ xgÂÛñø¬&\ |ÚÜ·¢&£×ã†X—›ÃŸë
�œ Sø=Bå½ü�3ß{ÂýÝH¦“…¶oÛ†g_z‹ÌË²`,Ëæ(�sÏ>¯¾ñ:öîÙƒ}oþl%àwq%	?»Ò^ÀLôyáËko¬øÂ&Ó²F÷ÕuWþ!ä:;%�•[¿isÃX&]{ç©*Åú{Öœ9hjiË¼NièIØ¾WOF`hjÁvCNeòË„¼Ôè�í,>5: -ÎÙFºƒ–Œ‚j*9irÑ¨±‘Rõ} é!QP#¯Àœ	‚­ øíÞÌr<uÕ6¶¦bÃL–3£n?XÑÂæ¡¢œ7”+øY¯åö”‚‡gPã+ž;_	>u¶Þõ_™ûÄr<½ôÒ"—\|1Z[[QSSŽã2)Ã‰H§�q¶l1»½ÿãÿ§|ô‹9î€A)ê<ýÅ…ÿÅCüÝfåŸŠ,?'˜–
 (®(¥8ãµ_§Çd�}VZÇçnžË	â¾Ì�`9¿z-Ø¬¦ŽbHªFÁ{©¦BKFl�kÈ)èR¼@ø-hÉ(4›n¼T“¡ÆÃÐ“…¯šb†³,92CŠƒ¦¢PÑLb¥à½¦Ïºý`õàýµéûÁ›.�è1óòPJð­|«Ùaù²¤ŸS¿Ä‡zñÒ/¾‰èîÑq®šF|ò²�ââ�ŸI¶Š‡úûûqÞyçg”Àâ3>‚‹¯ùO¸86“áW¬¦°/î¦¯ðÓX À+ë®¼†!ä¦|%ð“#/â¶ÎW Â<jŒ¹ï`¾ÐÖÜˆ¶¹2¯ËªŽ½qÛ÷–R  §-;š
-6Ã¦×}1E@Œè†ª@—bfß,¥ JI“‹ÐU[®0ùŠì:â6Âò`8$]‘¿Ò[(%ø ÒaÓ@ÁûKYc5ù�âàS÷aû½?ÌQ’3f¶ãü>„‹?t>QWW‡ÆÆÆK`î¢¥¸ôúŸcþ¼yEsûûN¾Àôóùó1­ `¯²‹ƒÆ4Ž*�×ßê‹G÷RUÎt£X¼òÃïqe*‚â2ú£öš†œ„žGþ±é]—“%• PÜÈ¼žCKFaÈ)3¡ÈUØŸê¨*ÃÐÌº+$i7Ô¼_\Æ¬'Î29¶øÊfÖBøl{ôºÝàƒE_·³ªiò—ƒÁÞŽwþv_ŽûÄ{ƒ¸äƒçâ}§�†µk×¢½Õt/þÈ‡ÓCOM«ákßù–¯ÛPx\�âÁ·qp¤ð™îÂL3Ð·yùÅOµ­~;;Y((¸±#Þ‡ÃR Ôƒ1’�‡ž¹GjX{n”Rúk["#MÍ£%©^�CBR*· “åÏ&Ù¬UÎL)v¯„¹3¢„-]Hgr¿~$È!�4É	`Ò†¬ÛÖí7ßgóÃºýf“à6žÒiÌ×Ærà<3¥7Ý©×n?Kðyméðµ2#Í}Bnu~wŽË5‘ðz½xÏ)ïÃŠ÷_LâI	#‡vQjÄPelß¶<ð xðÏØþö[ðx½¸òÊ+‘’u¼òòKÐ¤žùó½èîÄÒeË!zÌ,G3· G"¹ßch�%éŠéê+…ioXH÷¸Ãj(’M2žàìñ”Õ.ºúö]TWZç‚ºA±w0i;ž
0-ª«¶«ÕUh‰(¨Vš«44†’*jX1uª«™QÞ†’²%#+�¥´XÑÂ‰`8Œè)iXû³ÞPŽÒsŸÏ7i«¾…/�PžùêÅÞ§îÇÝƒDa4™÷qüÒÅèêîÆ‘Ã£=9]5�øäµÿŠ¥›>ˆÞ,håE€ažè—>ý…Õ�OÚŽG� 2G~K™C)ÅEoÞ�© äû ½v¬Ç]üõ{×ÉÈs™›ÂrX´leŽ+�Pt´ç\¢ ª¤�’Š¹–ŒÂ�âpÓìOæpùI5Ù º
CÓ@59£J¹ Ù¥¹¥Ìz»÷–sJÁçóÁç&ÔßÏ†‹cÐè –P6:¥Úý*^zìAt¾ñœ­2°½–YÇ¡õ_Dý’ÌxôIïæSU
 H·ëþYû@ï[¸qÿß€jXy„ ÛëÇ’eËsLT;>ÀÃ�æ›'ñ’JÀ©5`A—S%]ˆjÀIˆÎ$ýÜ¯ö™{éáñŽyxfÂ- »U¿DŽ@îÂ��Ûpø­W1ØÝ‰ÃûÞ�4Ü[4ìê›uZÎüVo¸ØLdï‰ÀQ§  3cíÔÀÝnUË
X¿i³8¸bãÖlW ?* ä*Žãàwå®„²,#/½ÒWb f
-ÕTèR†’§ÙŸsì"ñ{Â‰ãzÀ|ÁåÇævÏå:aV€“Uß~‘E£_ _„“§4<¿ë0ö’}ºªüµV—>üÌ�„'ä¢&G¥°ðêº+ÿãÏý;¯³¬ Â	Ç�§Ùf~n  ,X¸¡ºúœýºFˆ«(~N”€Sk€p¢m�j*¨&C—S²RÅ@8³JÑúk0èxQLð³Qm%À ÞËÃïpDW6šýBîâïH¨øëÎ~„S6n•®MÛ¿Õ
 0ÉÁ5[~ú€ü†ñ¾5Þ{‹¯úõçCÏao—.=žÀhs‹ƒ[· nþ
#¦1EŸpM7�ÅÒ†óQNS E�—¥ò�i%™¿›Çäª4$0}|–ãK
~6ªå
„\j=Ø
›Íº­�â«> ¼Õ—À“ïl§”FYª_9™<'G½ Òg«ØC=Ÿ°HA£2/Üþm�zœö±kÐW‘J§Þ—ã,P]5}þ<×€qùÊ¾w<¯p»D°_0°Ó	ÆkxM>¾bÁÊ¯ú’ªã©}aÛD0B�í^V¿üh"ûŠá]¡ ª�õ›6‹ƒ+7>F55“e¨w£ÿ±_Aî¥þÕ$ª�aŽ”€— ºœÌTV²ú�cQ „åáu‹­öÅ0+À+°¨óðûù@y_ º"2þº³ß6\W¤;ý�Ýö™é4âm<8¦ Š`Ãõ÷‡ú¢á—%¹pèå‡††^yø/ 0çÔ7ŸsÕÿdö3ƒ	C©ÒJ@Ó(RÒ‘5`�°|Ú2Hë�N€µÒWCèót9ËG�às,ƒf¿ ŸPüÜUƒâ¥ýƒx³§0òòn1ùîËTŸÀtÅ37^îøÜÍgK�ÛÏyëù{ :�ÛùãNCRÑáÒD�~"¯¡;oÄS68–÷ÿÛ;ûØªÎ:ŽŸç9çžû~o_i+XÖÂºN ¸¦BjL†fâÌÔÄlþ�C—8ÙœgÒD3’EˆD·l¨™Fb”2§t&3å5aH#8y]K¡o—{{_ÎÛóøÇí)íí¹¯½·´ô|’þçÐÒÛþ>Ïï÷{~Ï¹¾t�Oªy{ÀØ¤¡$�wê­�#nè�Þ£’«bŸIB3r–Ó	| ¨ñÉ¨óåÎ¤zo©xû¿ƒ¶�>"xOÈÍ6üëÉÕsj‹¯œ ªV®?ÚøÙo>èó°¼½m\	ÍD_4�çYó•…œ †~{`Lã×r‚°t°QI†wLJ f$Ø³‘Y
Hð+¥5÷,
I÷Sº‰“W†mW} s¾ËŸG Ò±î1¥·©%e=EØ%Ix`Åýðz&Oèé&G_T³­íÈ&‚BÏÐO—*ÑoÕX1Bn·Dv3x]¬äÀ÷¸ø$¸åÜ¥Åù›	t_²ýYÝM�¾\8(êUßûí�(¥ã']’„em-‡'¿µç‘”9åð¹ÈÁ|€µPÊôÞD‰ Î¯ä¬ó�ô¾þ‘‹CüjD�r£"J¸¹ÛûæË�wK£/Ž Š uó®Ù<1Q Ð¾´�õuSî/¦$°PU¦¡C3sPóN
 äfð¹¼òí8(Å¯ü…~¾tŸrØC´ïßí«þDIëæ]-²ÇˆJRÛÄ¿_ØÔ€%Í‹@3fƒJÉBnÝˆ§TŒêÈz
±Ì¤ ¬•>3è'Â
žæóŒ5ó¾ÎÎ\�ãôµû'<Ý¥þBpPk;÷…oÄ"oP&Mz‘ÏÀò{ï™Ò 
ïØÇ$4šnTD•€W¦ðH~E*x¿_f>9{P†oþHOò»4œýu7�í�ÑÁæâ9pP"ëS?¾~;¥ô©ÌkíK[° ¶fJ6 ¤Ë‚�¸žõ2ß`Œn
$tUUn4æ¢œ�©€ÌXÑoGf) 1Š�›!ì‘rvõ-ÎßLàäÕûù}ÌÏtßG Ó¤mËž�„ÒßdöªX|�}6À9GLãˆ$�)A\èPŒ…n
è¦	M7 ¼h)”" +ÐeFà–(%�))ë_«(é>A®±Ýñ×‚\HàÔµHÖÀ'‚÷<ò3så�•Æ@ÈÖ €ÖÅÍXÔXo›dŠ œ'ä,1èœŒÏ
hÆd9h‚¢Ú+AfS¥#3iì·Cb•	òlÈŒ¢ÚÃpí½XÝ±&ïŠŸÒMôôÇq¦÷VVÁ{ˆà?Ÿ�u~.”‰\%�Ï@[sã”íÂ‰$’Ihj
	:3Ûù&äf
™Q„ÜBn6ð'ïÇéãÝønç‹¶õ~$i §wïhY{#NàçÆ@™ißº·C¨‰Wì²�\eA×îN|aÓ³ ž bª�HÊDÊ˜~�Ÿ�;) �ÌThº_`³ÊóD?Øð€Xºjybû„=t.peDÅÙ¾ˆí>¾…ø…á äÊ€©":õìÝö=±õà…I?Õ0SÓ•’ÁL
Àêî—Djæýú‡�ãüÑ.´Ü·ŸÚú*.Ç�³ÏÁ9ìSè‹N�_Ž *Hëæ]-²?¼�<bw½¡* _¨ÿüñ— ÏüñxÖÏe˜1Õ@ÊUe¡R`pI
#EüDâš‰¿¼þN½ö“ÿ&ís…ë·,ùÖKA¥nÑ¤û„Q®«ûBn¶c¾wõ‹ÅÀÐ¾uo‡ÐÏgÎ À‡Ý�è§ú›?¶áÉ_í/ø0Žj˜Hh&R†@Êà%gå€Ì(Ü#ž±!ŸbƒÝ"®™¸8Åå�(†ãôèÎ¾ðè¿!øêUóDô¹ûŸýýÓîÆ% ‚÷¨£‘ßÕpí•ùº�?]Ì m[öl$À6«?9w}‡v�z‚où›Z‡Ú¾Ñ‰†Új,j¬G•·ð·Æ¶P�´t“§w¸€É“gÍr	ÀjøSJ!Q2~W‘\Œ–´ªÛÑÓ0��}&Wþôü±‘Ó]«­?Éµhí/»ïsÒüéãàÐ¶eÏFBðÔ…�Oüˆëj7 Ô¬zh°ñó›Æg	˜âÁ’ú ÔÖ”$;3�%˜"mƒÄðø½^„«Òo*MØâ+G`gc$©ãÃáQÜ¸•Â`,5O=“¸úÎù�¯-ðÓ;�#€YÂÂ/=ýNxÙšOÛ]óHµÕ¨«¡.è+Û™ý÷ßzJ 
ŸøÌ+ö}é\`(®c /8à-L]û»¼«®¶fï\{Þþ\Áy"Ð,Á·xÅKä£¢9óZÒ¸Ô?„KýCé{ƒ!,)üú¼ð¹J;9øþ¹s P6Ä5‘”�áX·b£IqÛ”>\ð˜0ŒnÅëÿƒ/6xÀ©ë+�“Ì2Ú·îízêæ=l';<AUÐÅ¥ äSàRÜðz<ðÉ4g¶ð‹¯}LVð³?Éû5t. 1Õ„j
Dã	hj
#) ©iˆ«zÁ+ûD˜ËÝ#8?”ê;Xsâo'çÃüÙ„#€YL)2ÈÄ#hÌ�¾ÛY‚âR�ìEWçWo Àºmo,hjjÄ(—93’4ÅÓ‰aRÓ�4	¸¡•ÜvXïûàDC0ü¶³ÊßYÌZ7ïjq¹½Ÿ’k=óV•*‹¡ãÐßõÛM ÐôÐw^®[ýpÙÿÏÌåî1tí]SMô˜ÉÑcÎ
?ûp0GiÝ¼«EòøWJ×È¡ú6®«íÅH¡÷ÀÎñ­µª•ë�6?úÜƒ…þÛ‰pÁc²â½lèÚ»ŒI×)ÁY¯$Îðý¯œw‚}öãà.bmç¾°¨ªi¾qå­r¸¾Åˆ.¡‘ƒµ~®«íÖ}¢ùì¶¯¬¦Þ „ÉË—ÿôÍnëº™Œ»ÃuQ ÐâÑ^êrßdLº®%¢ƒn_ð†ß«ôs-u“Œ]qRø¹ÍÿÒ‡+¦Å1È®    IEND®B`‚               h          ¨   00     ¨%          ›     X4   V S _ V E R S I O N _ I N F O     ½ïþ                  ?                        ¸   S t r i n g F i l e I n f o   ”   0 8 0 9 0 4 b 0   ,   C o m p a n y N a m e     a m p h p   Ô V  F i l e D e s c r i p t i o n     E x e c u t e s   a   c o m m a n d   a n d   p a s s e s   t h e   s t d i o   s t r e a m s   o f   t h e   c r e a t e d   p r o c e s s   t o   T C P   s o c k e t s   0   F i l e V e r s i o n     1 . 0 . 0 . 0   :   I n t e r n a l N a m e   P r o c e s s W . e x e     V   L e g a l C o p y r i g h t   C o p y r i g h t   ( C )   a m p h p   2 0 1 7     B   O r i g i n a l F i l e n a m e   P r o c e s s W . e x e     @   P r o d u c t N a m e     P r o c e s s   W r a p p e r   4   P r o d u c t V e r s i o n   0 . 0 . 0 . 0   D    V a r F i l e I n f o     $    T r a n s l a t i o n     	°<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
<assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
    <security>
      <requestedPrivileges>
        <requestedExecutionLevel level='asInvoker' uiAccess='false' />
      </requestedPrivileges>
    </security>
  </trustInfo>
</assembly>
                                                                                                                                                                                                                                                                                                                                                            @     ø¢ ££(£0£`£h£   P      ¥¨¥°¥¸¥À¥È¥Ð¥Ø¨ð¨ø¨ p     @ P X h p € ˆ ˜                                                                                                                                                                                                                                                                                                                                                                                                                                                     <?php declare(strict_types=1);

namespace Amp\Http\Http2;

interface Http2Processor
{
    public function handlePong(string $data): void;

    public function handlePing(string $data): void;

    public function handleShutdown(int $lastId, int $error, string $message): void;

    public function handleStreamWindowIncrement(int $streamId, int $windowSize): void;

    public function handleConnectionWindowIncrement(int $windowSize): void;

    public function handleHeaders(int $streamId, array $pseudo, array $headers, bool $streamEnded): void;

    public function handlePushPromise(int $streamId, int $pushId, array $pseudo, array $headers): void;

    public function handlePriority(int $streamId, int $parentId, int $weight): void;

    public function handleStreamReset(int $streamId, int $errorCode): void;

    public function handleStreamException(Http2StreamException $exception): void;

    public function handleConnectionException(Http2ConnectionException $exception): void;

    public function handleData(int $streamId, string $data): void;

    public function handleSettings(array $settings): void;

    public function handleStreamEnd(int $streamId): void;
}
<?php declare(strict_types=1);

namespace Amp\Http\Http2;

final class Http2StreamException extends \Exception
{
    public function __construct(
        string $message,
        private readonly int $streamId,
        int $code,
        ?\Throwable $previous = null,
    ) {
        parent::__construct($message, $code, $previous);
    }

    public function getStreamId(): int
    {
        return $this->streamId;
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Http2;

final class Http2ConnectionException extends \Exception
{
    public function __construct(string $message, int $code, ?\Throwable $previous = null)
    {
        parent::__construct($message, $code, $previous);
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Http2;

use Amp\Http\HPack;
use Amp\Parser\Parser;

final class Http2Parser
{
    public const PREFACE = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";

    private const DEFAULT_MAX_FRAME_SIZE = 1 << 14;

    private const HEADER_NAME_REGEX = '/^[\x21-\x40\x5b-\x7e]+$/';

    public const KNOWN_RESPONSE_PSEUDO_HEADERS = [
        ":status" => true,
    ];

    public const KNOWN_REQUEST_PSEUDO_HEADERS = [
        ":method" => true,
        ":authority" => true,
        ":path" => true,
        ":scheme" => true,
    ];

    // SETTINGS Flags - https://http2.github.io/http2-spec/#rfc.section.6.5
    public const ACK = 0x01;

    // HEADERS Flags - https://http2.github.io/http2-spec/#rfc.section.6.2
    public const NO_FLAG = 0x00;
    public const END_STREAM = 0x01;
    public const END_HEADERS = 0x04;
    public const PADDED = 0x08;
    public const PRIORITY_FLAG = 0x20;

    // Frame Types - https://http2.github.io/http2-spec/#rfc.section.11.2
    public const DATA = 0x00;
    public const HEADERS = 0x01;
    public const PRIORITY = 0x02;
    public const RST_STREAM = 0x03;
    public const SETTINGS = 0x04;
    public const PUSH_PROMISE = 0x05;
    public const PING = 0x06;
    public const GOAWAY = 0x07;
    public const WINDOW_UPDATE = 0x08;
    public const CONTINUATION = 0x09;

    // Settings
    public const HEADER_TABLE_SIZE = 0x1; // 1 << 12
    public const ENABLE_PUSH = 0x2; // 1
    public const MAX_CONCURRENT_STREAMS = 0x3; // INF
    public const INITIAL_WINDOW_SIZE = 0x4; // 1 << 16 - 1
    public const MAX_FRAME_SIZE = 0x5; // 1 << 14
    public const MAX_HEADER_LIST_SIZE = 0x6; // INF

    // Error codes
    public const GRACEFUL_SHUTDOWN = 0x0;
    public const PROTOCOL_ERROR = 0x1;
    public const INTERNAL_ERROR = 0x2;
    public const FLOW_CONTROL_ERROR = 0x3;
    public const SETTINGS_TIMEOUT = 0x4;
    public const STREAM_CLOSED = 0x5;
    public const FRAME_SIZE_ERROR = 0x6;
    public const REFUSED_STREAM = 0x7;
    public const CANCEL = 0x8;
    public const COMPRESSION_ERROR = 0x9;
    public const CONNECT_ERROR = 0xa;
    public const ENHANCE_YOUR_CALM = 0xb;
    public const INADEQUATE_SECURITY = 0xc;
    public const HTTP_1_1_REQUIRED = 0xd;

    public static function compileFrame(string $data, int $type, int $flags, int $stream = 0): string
    {
        \assert(Http2Parser::logDebugFrame('send', $type, $flags, $stream, \strlen($data)));
        return \pack("NcN", (\strlen($data) << 8) | ($type & 0xff), $flags, $stream) . $data;
    }

    public static function getFrameName(int $type): string
    {
        $names = [
            self::DATA => 'DATA',
            self::HEADERS => 'HEADERS',
            self::PRIORITY => 'PRIORITY',
            self::RST_STREAM => 'RST_STREAM',
            self::SETTINGS => 'SETTINGS',
            self::PUSH_PROMISE => 'PUSH_PROMISE',
            self::PING => 'PING',
            self::GOAWAY => 'GOAWAY',
            self::WINDOW_UPDATE => 'WINDOW_UPDATE',
            self::CONTINUATION => 'CONTINUATION',
        ];

        return $names[$type] ?? ('0x' . \bin2hex(\chr($type)));
    }

    private static function logDebugFrame(
        string $action,
        int $frameType,
        int $frameFlags,
        int $streamId,
        int $frameLength
    ): bool {
        /** @psalm-suppress RiskyTruthyFalsyComparison */
        $env = \getenv("AMP_DEBUG_HTTP2_FRAMES") ?: "0";
        if (match ($env) {
            "0", "false", "off" => false,
            default => $env || \defined("AMP_DEBUG_HTTP2_FRAMES") && \AMP_DEBUG_HTTP2_FRAMES,
        }) {
            \fwrite(\STDERR, \sprintf(
                "%s %s <flags = %s, stream = %d, length = %d>\r\n",
                $action,
                self::getFrameName($frameType),
                \bin2hex(\chr($frameFlags)),
                $streamId,
                $frameLength,
            ));
        }

        return true;
    }

    private bool $continuationExpected = false;

    private int $headerFrameType = 0;

    /** @var list<string> */
    private array $headerBuffer = [];

    private int $headerLength = 0;

    private int $headerStream = 0;

    private int $receivedFrameCount = 0;

    private int $receivedByteCount = 0;

    private readonly Parser $parser;

    /**
     * @param positive-int $headerSizeLimit
     * @param positive-int $frameSizeLimit
     */
    public function __construct(
        private readonly Http2Processor $handler,
        private readonly HPack $hpack,
        ?string $peerSettings = null,
        private readonly int $headerSizeLimit = self::DEFAULT_MAX_FRAME_SIZE,
        private readonly int $frameSizeLimit = self::DEFAULT_MAX_FRAME_SIZE,
    ) {
        $this->parser = new Parser($this->parse($peerSettings));
    }

    public function getReceivedByteCount(): int
    {
        return $this->receivedByteCount;
    }

    public function getReceivedFrameCount(): int
    {
        return $this->receivedFrameCount;
    }

    /**
     * @throws Http2ConnectionException
     */
    public function push(string $data): void
    {
        $this->parser->push($data);
    }

    public function cancel(): void
    {
        $this->parser->cancel();
    }

    private function parse(?string $settings = null): \Generator
    {
        if ($settings !== null) {
            $this->parseSettings($settings, \strlen($settings), self::NO_FLAG, 0);
        }

        while (true) {
            /** @var string $frameHeader */
            $frameHeader = yield 9;
            $this->receivedByteCount += 9;

            [
                'length' => $frameLength,
                'flags' => $frameFlags,
                'id' => $streamId,
            ] = \unpack('Nlength/cflags/Nid', $frameHeader);

            $frameType = $frameLength & 0xff;
            $frameLength >>= 8;

            $streamId &= 0x7fffffff;

            $frameBuffer = $frameLength === 0 ? '' : yield $frameLength;
            $this->receivedByteCount += $frameLength;

            $this->receivedFrameCount++;

            \assert(self::logDebugFrame('recv', $frameType, $frameFlags, $streamId, $frameLength));

            try {
                // Do we want to allow increasing the maximum frame size?
                if ($frameLength > $this->frameSizeLimit) {
                    throw new Http2ConnectionException("Frame size limit exceeded", self::FRAME_SIZE_ERROR);
                }

                if ($this->continuationExpected && $frameType !== self::CONTINUATION) {
                    throw new Http2ConnectionException("Expected continuation frame", self::PROTOCOL_ERROR);
                }

                match ($frameType) {
                    self::DATA => $this->parseDataFrame($frameBuffer, $frameLength, $frameFlags, $streamId),
                    self::PUSH_PROMISE => $this->parsePushPromise($frameBuffer, $frameLength, $frameFlags, $streamId),
                    self::HEADERS => $this->parseHeaders($frameBuffer, $frameLength, $frameFlags, $streamId),
                    self::PRIORITY => $this->parsePriorityFrame($frameBuffer, $frameLength, $streamId),
                    self::RST_STREAM => $this->parseStreamReset($frameBuffer, $frameLength, $streamId),
                    self::SETTINGS => $this->parseSettings($frameBuffer, $frameLength, $frameFlags, $streamId),
                    self::PING => $this->parsePing($frameBuffer, $frameLength, $frameFlags, $streamId),
                    self::GOAWAY => $this->parseGoAway($frameBuffer, $frameLength, $streamId),
                    self::WINDOW_UPDATE => $this->parseWindowUpdate($frameBuffer, $frameLength, $streamId),
                    self::CONTINUATION => $this->parseContinuation($frameBuffer, $frameFlags, $streamId),
                    default => null, // Ignore and discard unknown frame per spec
                };
            } catch (Http2StreamException $exception) {
                $this->handler->handleStreamException($exception);
            } catch (Http2ConnectionException $exception) {
                $this->handler->handleConnectionException($exception);

                throw $exception;
            }
        }
    }

    private function parseDataFrame(string $frameBuffer, int $frameLength, int $frameFlags, int $streamId): void
    {
        $isPadded = $frameFlags & self::PADDED;

        $headerLength = $isPadded ? 1 : 0;

        if ($frameLength < $headerLength) {
            $this->throwInvalidFrameSizeError();
        }

        $header = $headerLength === 0 ? '' : \substr($frameBuffer, 0, $headerLength);

        $padding = $isPadded ? \ord($header[0]) : 0;

        if ($streamId === 0) {
            $this->throwInvalidZeroStreamIdError();
        }

        if ($frameLength - $headerLength - $padding < 0) {
            $this->throwInvalidPaddingError();
        }

        $data = \substr($frameBuffer, $headerLength, $frameLength - $headerLength - $padding);

        $this->handler->handleData($streamId, $data);

        if ($frameFlags & self::END_STREAM) {
            $this->handler->handleStreamEnd($streamId);
        }
    }

    /** @see https://http2.github.io/http2-spec/#rfc.section.6.6 */
    private function parsePushPromise(string $frameBuffer, int $frameLength, int $frameFlags, int $streamId): void
    {
        $isPadded = $frameFlags & self::PADDED;

        $headerLength = $isPadded ? 5 : 4;

        if ($frameLength < $headerLength) {
            $this->throwInvalidFrameSizeError();
        }

        $header = \substr($frameBuffer, 0, $headerLength);

        $padding = $isPadded ? \ord($header[0]) : 0;

        $pushId = \unpack("N", $header)[1] & 0x7fffffff;

        if ($frameLength - $headerLength - $padding < 0) {
            $this->throwInvalidPaddingError();
        }

        $this->headerFrameType = self::PUSH_PROMISE;

        $this->pushHeaderBlockFragment(
            $pushId,
            \substr($frameBuffer, $headerLength, $frameLength - $headerLength - $padding)
        );

        if ($frameFlags & self::END_HEADERS) {
            $this->continuationExpected = false;

            [$pseudo, $headers] = $this->parseHeaderBuffer();

            $this->handler->handlePushPromise($streamId, $pushId, $pseudo, $headers);
        } else {
            $this->continuationExpected = true;
        }

        if ($frameFlags & self::END_STREAM) {
            $this->handler->handleStreamEnd($streamId);
        }
    }

    /**
     * @return array{array<string, string>, array<string, list<string>>}
     */
    private function parseHeaderBuffer(): array
    {
        if ($this->headerStream === 0) {
            throw new Http2ConnectionException('Invalid stream ID 0 for header block', self::PROTOCOL_ERROR);
        }

        if (!$this->headerBuffer) {
            throw new Http2StreamException('Invalid empty header section', $this->headerStream, self::PROTOCOL_ERROR);
        }

        /** @var list<array{string, string}>|null $decoded */
        $decoded = $this->hpack->decode(\implode($this->headerBuffer), $this->headerSizeLimit);

        if ($decoded === null) {
            throw new Http2ConnectionException("Compression error in headers", self::COMPRESSION_ERROR);
        }

        $headers = [];
        $pseudo = [];

        foreach ($decoded as [$name, $value]) {
            if (!\preg_match(self::HEADER_NAME_REGEX, $name)) {
                throw new Http2StreamException("Invalid header field name", $this->headerStream, self::PROTOCOL_ERROR);
            }

            if ($name[0] === ':') {
                if (!empty($headers)) {
                    throw new Http2ConnectionException(
                        "Pseudo header after other headers",
                        self::PROTOCOL_ERROR
                    );
                }

                if (isset($pseudo[$name])) {
                    throw new Http2ConnectionException(
                        "Repeat pseudo header",
                        self::PROTOCOL_ERROR
                    );
                }

                $pseudo[$name] = $value;
                continue;
            }

            $headers[$name][] = $value;
        }

        $this->headerBuffer = [];
        $this->headerLength = 0;
        $this->headerStream = 0;

        return [$pseudo, $headers];
    }

    private function pushHeaderBlockFragment(int $streamId, string $buffer): void
    {
        if ($this->headerStream !== 0 && $this->headerStream !== $streamId) {
            throw new Http2ConnectionException(
                "Expected CONTINUATION frame for stream ID " . $this->headerStream,
                self::PROTOCOL_ERROR
            );
        }

        $this->headerStream = $streamId;
        $this->headerBuffer[] = $buffer;
        $this->headerLength += \strlen($buffer);

        $headersTooLarge = $this->headerLength > $this->headerSizeLimit;

        if ($headersTooLarge) {
            throw new Http2ConnectionException(
                "Headers exceed the maximum configured size of {$this->headerSizeLimit} bytes",
                self::COMPRESSION_ERROR
            );
        }
    }

    /** @see https://http2.github.io/http2-spec/#HEADERS */
    private function parseHeaders(string $frameBuffer, int $frameLength, int $frameFlags, int $streamId): void
    {
        if ($streamId === 0) {
            $this->throwInvalidZeroStreamIdError();
        }

        $headerLength = 0;
        $isPadded = $frameFlags & self::PADDED;
        $isPriority = $frameFlags & self::PRIORITY_FLAG;

        if ($isPadded) {
            $headerLength++;
        }

        if ($isPriority) {
            $headerLength += 5;
        }

        if ($frameLength < $headerLength) {
            $this->throwInvalidFrameSizeError();
        }

        $header = \substr($frameBuffer, 0, $headerLength);

        $padding = $isPadded ? \ord($header[0]) : 0;

        if ($isPriority) {
            ['parent' => $parent, 'weight' => $weight] = \unpack("Nparent/cweight", $header, $isPadded ? 1 : 0);

            $parent &= 0x7fffffff;

            if ($parent === $streamId) {
                $this->throwInvalidRecursiveDependency($streamId);
            }

            $this->handler->handlePriority($streamId, $parent, $weight + 1);
        }

        if ($frameLength - $headerLength - $padding < 0) {
            $this->throwInvalidPaddingError();
        }

        $this->headerFrameType = self::HEADERS;

        $this->pushHeaderBlockFragment(
            $streamId,
            \substr($frameBuffer, $headerLength, $frameLength - $headerLength - $padding)
        );

        $ended = $frameFlags & self::END_STREAM;

        if ($frameFlags & self::END_HEADERS) {
            $this->continuationExpected = false;

            $headersTooLarge = $this->headerLength > $this->headerSizeLimit;

            [$pseudo, $headers] = $this->parseHeaderBuffer();

            // This must happen after the parsing, otherwise we loose the connection state and must close the whole
            // connection, which is not what we want hereâ€¦
            if ($headersTooLarge) {
                throw new Http2StreamException(
                    "Headers exceed maximum configured size of {$this->headerSizeLimit} bytes",
                    $streamId,
                    self::ENHANCE_YOUR_CALM
                );
            }

            $this->handler->handleHeaders($streamId, $pseudo, $headers, (bool) $ended);
        } else {
            $this->continuationExpected = true;
        }

        if ($ended) {
            $this->handler->handleStreamEnd($streamId);
        }
    }

    private function parsePriorityFrame(string $frameBuffer, int $frameLength, int $streamId): void
    {
        if ($frameLength !== 5) {
            $this->throwInvalidFrameSizeError();
        }

        ['parent' => $parent, 'weight' => $weight] = \unpack("Nparent/cweight", $frameBuffer);

        if ($exclusive = ($parent & 0x80000000)) {
            $parent &= 0x7fffffff;
        }

        if ($streamId === 0) {
            $this->throwInvalidZeroStreamIdError();
        }

        if ($parent === $streamId) {
            $this->throwInvalidRecursiveDependency($streamId);
        }

        $this->handler->handlePriority($streamId, $parent, $weight + 1);
    }

    private function parseStreamReset(string $frameBuffer, int $frameLength, int $streamId): void
    {
        if ($frameLength !== 4) {
            $this->throwInvalidFrameSizeError();
        }

        if ($streamId === 0) {
            $this->throwInvalidZeroStreamIdError();
        }

        $errorCode = \unpack('N', $frameBuffer)[1];

        $this->handler->handleStreamReset($streamId, $errorCode);
    }

    private function parseSettings(string $frameBuffer, int $frameLength, int $frameFlags, int $streamId): void
    {
        if ($streamId !== 0) {
            $this->throwInvalidNonZeroStreamIdError();
        }

        if ($frameFlags & self::ACK) {
            if ($frameLength) {
                $this->throwInvalidFrameSizeError();
            }

            return; // Got ACK, nothing to do
        }

        if ($frameLength % 6 !== 0) {
            $this->throwInvalidFrameSizeError();
        }

        if ($frameLength > 60) {
            // Even with room for a few future options, sending that a big SETTINGS frame is just about
            // wasting our processing time. We declare this a protocol error.
            throw new Http2ConnectionException("Excessive SETTINGS frame", self::PROTOCOL_ERROR);
        }

        $settings = [];

        while ($frameLength > 0) {
            ['key' => $key, 'value' => $value] = \unpack("nkey/Nvalue", $frameBuffer);

            if ($value < 0) {
                throw new Http2ConnectionException(
                    "Invalid setting: {$value}",
                    self::PROTOCOL_ERROR
                );
            }

            $settings[$key] = $value;

            $frameBuffer = \substr($frameBuffer, 6);
            $frameLength -= 6;
        }

        $this->handler->handleSettings($settings);
    }

    /** @see https://http2.github.io/http2-spec/#rfc.section.6.7 */
    private function parsePing(string $frameBuffer, int $frameLength, int $frameFlags, int $streamId): void
    {
        if ($frameLength !== 8) {
            $this->throwInvalidFrameSizeError();
        }

        if ($streamId !== 0) {
            $this->throwInvalidNonZeroStreamIdError();
        }

        if ($frameFlags & self::ACK) {
            $this->handler->handlePong($frameBuffer);
        } else {
            $this->handler->handlePing($frameBuffer);
        }
    }

    /** @see https://http2.github.io/http2-spec/#rfc.section.6.8 */
    private function parseGoAway(string $frameBuffer, int $frameLength, int $streamId): void
    {
        if ($frameLength < 8) {
            $this->throwInvalidFrameSizeError();
        }

        if ($streamId !== 0) {
            $this->throwInvalidNonZeroStreamIdError();
        }

        ['last' => $lastId, 'error' => $error] = \unpack("Nlast/Nerror", $frameBuffer);

        $this->handler->handleShutdown($lastId & 0x7fffffff, $error, \substr($frameBuffer, 8));
    }

    /** @see https://http2.github.io/http2-spec/#rfc.section.6.9 */
    private function parseWindowUpdate(string $frameBuffer, int $frameLength, int $streamId): void
    {
        if ($frameLength !== 4) {
            $this->throwInvalidFrameSizeError();
        }

        $windowSize = \unpack('N', $frameBuffer)[1];

        if ($windowSize === 0) {
            if ($streamId) {
                throw new Http2StreamException(
                    "Invalid zero window update value",
                    $streamId,
                    self::PROTOCOL_ERROR
                );
            }

            throw new Http2ConnectionException("Invalid zero window update value", self::PROTOCOL_ERROR);
        }

        if ($streamId) {
            $this->handler->handleStreamWindowIncrement($streamId, $windowSize);
        } else {
            $this->handler->handleConnectionWindowIncrement($windowSize);
        }
    }

    /** @see https://http2.github.io/http2-spec/#rfc.section.6.10 */
    private function parseContinuation(string $frameBuffer, int $frameFlags, int $streamId): void
    {
        if ($streamId !== $this->headerStream) {
            throw new Http2ConnectionException(
                "Invalid CONTINUATION frame stream ID",
                self::PROTOCOL_ERROR
            );
        }

        if (!$this->headerBuffer) {
            throw new Http2ConnectionException(
                "Unexpected CONTINUATION frame for stream ID " . $this->headerStream,
                self::PROTOCOL_ERROR
            );
        }

        $this->pushHeaderBlockFragment($streamId, $frameBuffer);

        $ended = $frameFlags & self::END_STREAM;

        if ($frameFlags & self::END_HEADERS) {
            $this->continuationExpected = false;

            $isPush = $this->headerFrameType === self::PUSH_PROMISE;
            $pushId = $this->headerStream;

            [$pseudo, $headers] = $this->parseHeaderBuffer();

            if ($isPush) {
                $this->handler->handlePushPromise($streamId, $pushId, $pseudo, $headers);
            } else {
                $this->handler->handleHeaders($streamId, $pseudo, $headers, (bool) $ended);
            }
        }

        if ($ended) {
            $this->handler->handleStreamEnd($streamId);
        }
    }

    private function throwInvalidFrameSizeError(): never
    {
        throw new Http2ConnectionException("Invalid frame length", self::PROTOCOL_ERROR);
    }

    private function throwInvalidRecursiveDependency(int $streamId): never
    {
        throw new Http2ConnectionException(
            "Invalid recursive dependency for stream {$streamId}",
            self::PROTOCOL_ERROR
        );
    }

    private function throwInvalidPaddingError(): never
    {
        throw new Http2ConnectionException("Padding greater than length", self::PROTOCOL_ERROR);
    }

    private function throwInvalidZeroStreamIdError(): never
    {
        throw new Http2ConnectionException("Invalid zero stream ID", self::PROTOCOL_ERROR);
    }

    private function throwInvalidNonZeroStreamIdError(): never
    {
        throw new Http2ConnectionException("Invalid non-zero stream ID", self::PROTOCOL_ERROR);
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Cookie;

/**
 * A cookie as sent in a request's 'cookie' header, so without any attributes.
 *
 * This class does not deal with encoding of arbitrary names and values. If you want to use arbitrary values, please use
 * an encoding mechanism like Base64 or URL encoding.
 *
 * @link https://tools.ietf.org/html/rfc6265#section-5.4
 */
final class RequestCookie implements \Stringable
{
    private const NAME_REGEX = /** @lang RegExp */ '(^[^()<>@,;:\\\"/[\]?={}\x01-\x20\x7F]*+$)';
    private const VALUE_REGEX = /** @lang RegExp */ '(^[\x21\x23-\x2B\x2D-\x3A\x3C-\x5B\x5D-\x7E]*+$)';

    /**
     * Parses the cookies from a 'cookie' header.
     *
     * Note: Parsing is aborted if there's an invalid value and no cookies are returned.
     *
     * @param string $string Valid 'cookie' header line.
     *
     * @return list<RequestCookie>
     */
    public static function fromHeader(string $string): array
    {
        $cookies = \explode(";", $string);
        $result = [];

        try {
            foreach ($cookies as $cookie) {
                // Ignore zero-length cookie.
                if (\trim($cookie) === '') {
                    continue;
                }

                $parts = \explode('=', $cookie, 2);

                if (\count($parts) !== 2) {
                    return [];
                }

                [$name, $value] = $parts;

                $name = \trim($name);
                if ($name === '') {
                    return [];
                }

                // We can safely trim quotes, as they're not allowed within cookie values
                $result[] = new self($name, \trim($value, " \t\""));
            }
        } catch (InvalidCookieException) {
            return [];
        }

        return $result;
    }

    /**
     * @param non-empty-string $name Cookie name in its decoded form.
     * @param string $value Cookie value in its decoded form.
     *
     * @throws InvalidCookieException If name or value is invalid.
     */
    public function __construct(private string $name, private string $value = '')
    {
        if (!\preg_match(self::NAME_REGEX, $name)) {
            throw new InvalidCookieException("Invalid cookie name: '{$name}'");
        }

        if (!\preg_match(self::VALUE_REGEX, $value)) {
            throw new InvalidCookieException("Invalid cookie value: '{$value}'");
        }
    }

    /**
     * @return non-empty-string Name of the cookie.
     */
    public function getName(): string
    {
        return $this->name;
    }

    /**
     * @param non-empty-string $name
     */
    public function withName(string $name): self
    {
        if (!\preg_match(self::NAME_REGEX, $name)) {
            throw new InvalidCookieException("Invalid cookie name: '{$name}'");
        }

        $clone = clone $this;
        $clone->name = $name;

        return $clone;
    }

    /**
     * @return string Value of the cookie.
     */
    public function getValue(): string
    {
        return $this->value;
    }

    public function withValue(string $value): self
    {
        if (!\preg_match(self::VALUE_REGEX, $value)) {
            throw new InvalidCookieException("Invalid cookie value: '{$value}'");
        }

        $clone = clone $this;
        $clone->value = $value;

        return $clone;
    }

    /**
     * @return string Representation of the cookie as in a 'cookie' header.
     */
    public function toString(): string
    {
        return $this->name . '=' . $this->value;
    }

    /**
     * @see toString()
     */
    public function __toString(): string
    {
        return $this->toString();
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Cookie;

final class InvalidCookieException extends \Exception
{
    public function __construct(string $message)
    {
        parent::__construct($message);
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Cookie;

/**
 * A cookie as sent in a response's 'set-cookie' header, so with attributes.
 *
 * This class does not deal with encoding of arbitrary names and values. If you want to use arbitrary values, please use
 * an encoding mechanism like Base64 or URL encoding.
 *
 * @link https://tools.ietf.org/html/rfc6265#section-5.2
 */
final class ResponseCookie implements \Stringable
{
    private const NAME_REGEX = /** @lang RegExp */ '(^[^()<>@,;:\\\"/[\]?={}\x01-\x20\x7F]*+$)';
    private const VALUE_REGEX = /** @lang RegExp */ '(^[\x21\x23-\x2B\x2D-\x3A\x3C-\x5B\x5D-\x7E]*+$)';

    private static array $dateFormats = [
        'D, d M Y H:i:s T',
        'D, d-M-y H:i:s T',
        'D, d-M-Y H:i:s T',
        'D, d-m-y H:i:s T',
        'D, d-m-Y H:i:s T',
        'D M j G:i:s Y',
        'D M d H:i:s Y T',
    ];

    /**
     * Parses a cookie from a 'set-cookie' header.
     *
     * @param string $string Valid 'set-cookie' header line.
     *
     * @return self|null Returns a `ResponseCookie` instance on success and `null` on failure.
     */
    public static function fromHeader(string $string): ?self
    {
        $parts = \array_map("trim", \explode(";", $string));
        $nameValue = \explode("=", \array_shift($parts), 2);

        if (\count($nameValue) !== 2) {
            return null;
        }

        list($name, $value) = $nameValue;

        $name = \trim($name);
        $value = \trim($value, " \t\n\r\0\x0B\"");

        if ($name === "") {
            return null;
        }

        // httpOnly must default to false for parsing
        $meta = CookieAttributes::empty();
        $unknownAttributes = [];

        foreach ($parts as $part) {
            $pieces = \array_map('trim', \explode('=', $part, 2));
            $key = \strtolower($pieces[0]);

            if (\count($pieces) < 2) {
                switch ($key) {
                    case 'secure':
                        $meta = $meta->withSecure();
                        break;

                    case 'httponly':
                        $meta = $meta->withHttpOnly();
                        break;

                    default:
                        $unknownAttributes[] = $part;
                        break;
                }
            } else {
                switch ($key) {
                    case 'expires':
                        $time = self::parseDate($pieces[1]);

                        if ($time === null) {
                            break; // break is correct, see https://tools.ietf.org/html/rfc6265#section-5.2.1
                        }

                        $meta = $meta->withExpiry($time);
                        break;

                    case 'max-age':
                        $maxAge = \trim($pieces[1]);

                        // This also allows +1.42, but avoids a more complicated manual check
                        if (!\is_numeric($maxAge)) {
                            break; // break is correct, see https://tools.ietf.org/html/rfc6265#section-5.2.2
                        }

                        $meta = $meta->withMaxAge((int) $maxAge);
                        break;

                    case 'path':
                        $meta = $meta->withPath($pieces[1]);
                        break;

                    case 'domain':
                        $meta = $meta->withDomain($pieces[1]);
                        break;

                    case 'samesite':
                        $normalizedValue = \ucfirst(\strtolower($pieces[1]));
                        if (!\in_array($normalizedValue, [
                            CookieAttributes::SAMESITE_NONE,
                            CookieAttributes::SAMESITE_LAX,
                            CookieAttributes::SAMESITE_STRICT,
                        ], true)) {
                            $unknownAttributes[] = $part;
                        } else {
                            $meta = $meta->withSameSite($normalizedValue);
                        }

                        break;

                    default:
                        $unknownAttributes[] = $part;
                        break;
                }
            }
        }

        try {
            $cookie = new self($name, $value, $meta);
            $cookie->unknownAttributes = $unknownAttributes;

            return $cookie;
        } catch (InvalidCookieException) {
            return null;
        }
    }

    /**
     * @param string $date Formatted cookie date
     *
     * @return \DateTimeImmutable|null Parsed date.
     */
    private static function parseDate(string $date): ?\DateTimeImmutable
    {
        foreach (self::$dateFormats as $dateFormat) {
            if ($parsedDate = \DateTimeImmutable::createFromFormat($dateFormat, $date, new \DateTimeZone('GMT'))) {
                return $parsedDate;
            }
        }

        return null;
    }

    /** @var list<string> */
    private array $unknownAttributes = [];

    private CookieAttributes $attributes;

    /**
     * @param non-empty-string $name Name of the cookie.
     * @param string $value Value of the cookie.
     * @param CookieAttributes|null $attributes Attributes of the cookie.
     *
     * @throws InvalidCookieException If name or value is invalid.
     */
    public function __construct(
        private string $name,
        private string $value = '',
        ?CookieAttributes $attributes = null,
    ) {
        if (!\preg_match(self::NAME_REGEX, $name)) {
            throw new InvalidCookieException("Invalid cookie name: '{$name}'");
        }

        if (!\preg_match(self::VALUE_REGEX, $value)) {
            throw new InvalidCookieException("Invalid cookie value: '{$value}'");
        }

        $this->attributes = $attributes ?? CookieAttributes::default();
    }

    /**
     * @return non-empty-string Name of the cookie.
     */
    public function getName(): string
    {
        return $this->name;
    }

    /**
     * @param non-empty-string $name
     */
    public function withName(string $name): self
    {
        if (!\preg_match(self::NAME_REGEX, $name)) {
            throw new InvalidCookieException("Invalid cookie name: '{$name}'");
        }

        $clone = clone $this;
        $clone->name = $name;

        return $clone;
    }

    /**
     * @return string Value of the cookie.
     */
    public function getValue(): string
    {
        return $this->value;
    }

    public function withValue(string $value): self
    {
        if (!\preg_match(self::VALUE_REGEX, $value)) {
            throw new InvalidCookieException("Invalid cookie value: '{$value}'");
        }

        $clone = clone $this;
        $clone->value = $value;

        return $clone;
    }

    /**
     * @return \DateTimeImmutable|null Expiry if set, otherwise `null`.
     *
     * @link https://tools.ietf.org/html/rfc6265#section-5.2.1
     */
    public function getExpiry(): ?\DateTimeImmutable
    {
        return $this->attributes->getExpiry();
    }

    public function withExpiry(\DateTimeInterface $expiry): self
    {
        return $this->withAttributes($this->attributes->withExpiry($expiry));
    }

    public function withoutExpiry(): self
    {
        return $this->withAttributes($this->attributes->withoutExpiry());
    }

    /**
     * @return int|null Max-Age if set, otherwise `null`.
     *
     * @link https://tools.ietf.org/html/rfc6265#section-5.2.2
     */
    public function getMaxAge(): ?int
    {
        return $this->attributes->getMaxAge();
    }

    public function withMaxAge(int $maxAge): self
    {
        return $this->withAttributes($this->attributes->withMaxAge($maxAge));
    }

    public function withoutMaxAge(): self
    {
        return $this->withAttributes($this->attributes->withoutMaxAge());
    }

    /**
     * @return string Cookie path.
     *
     * @link https://tools.ietf.org/html/rfc6265#section-5.2.4
     */
    public function getPath(): string
    {
        return $this->attributes->getPath();
    }

    public function withPath(string $path): self
    {
        return $this->withAttributes($this->attributes->withPath($path));
    }

    /**
     * @return string Cookie domain.
     *
     * @link https://tools.ietf.org/html/rfc6265#section-5.2.3
     */
    public function getDomain(): string
    {
        return $this->attributes->getDomain();
    }

    public function withDomain(string $domain): self
    {
        return $this->withAttributes($this->attributes->withDomain($domain));
    }

    /**
     * @return bool Whether the secure flag is enabled or not.
     *
     * @link https://tools.ietf.org/html/rfc6265#section-5.2.5
     */
    public function isSecure(): bool
    {
        return $this->attributes->isSecure();
    }

    public function withSecure(): self
    {
        return $this->withAttributes($this->attributes->withSecure());
    }

    public function withoutSecure(): self
    {
        return $this->withAttributes($this->attributes->withoutSecure());
    }

    /**
     * @return bool Whether the httpOnly flag is enabled or not.
     *
     * @link https://tools.ietf.org/html/rfc6265#section-5.2.6
     */
    public function isHttpOnly(): bool
    {
        return $this->attributes->isHttpOnly();
    }

    public function withHttpOnly(): self
    {
        return $this->withAttributes($this->attributes->withHttpOnly());
    }

    public function withoutHttpOnly(): self
    {
        return $this->withAttributes($this->attributes->withoutHttpOnly());
    }

    public function withSameSite(string $sameSite): self
    {
        return $this->withAttributes($this->attributes->withSameSite($sameSite));
    }

    public function withoutSameSite(): self
    {
        return $this->withAttributes($this->attributes->withoutSameSite());
    }

    public function getSameSite(): ?string
    {
        return $this->attributes->getSameSite();
    }

    /**
     * @return CookieAttributes All cookie attributes.
     */
    public function getAttributes(): CookieAttributes
    {
        return $this->attributes;
    }

    public function withAttributes(CookieAttributes $attributes): self
    {
        $clone = clone $this;
        $clone->attributes = $attributes;

        return $clone;
    }

    /**
     * @return string Representation of the cookie as in a 'set-cookie' header.
     */
    public function toString(): string
    {
        $line = $this->name . '=' . $this->value;
        $line .= $this->attributes;

        $unknownAttributes = \implode('; ', $this->unknownAttributes);
        if ($unknownAttributes !== '') {
            $line .= '; ' . $unknownAttributes;
        }

        return $line;
    }

    /**
     * @see toString()
     */
    public function __toString(): string
    {
        return $this->toString();
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Cookie;

/**
 * Cookie attributes as defined in https://tools.ietf.org/html/rfc6265.
 *
 * @link https://tools.ietf.org/html/rfc6265
 */
final class CookieAttributes implements \Stringable
{
    public const SAMESITE_NONE = 'None';
    public const SAMESITE_LAX = 'Lax';
    public const SAMESITE_STRICT = 'Strict';

    /**
     * @return CookieAttributes No cookie attributes.
     *
     * @see self::default()
     */
    public static function empty(): self
    {
        $new = new self;
        $new->httpOnly = false;

        return $new;
    }

    /**
     * @return CookieAttributes Default cookie attributes, which means httpOnly is enabled by default.
     *
     * @see self::empty()
     */
    public static function default(): self
    {
        return new self;
    }

    private string $path = '';

    private string $domain = '';

    private ?int $maxAge = null;

    private ?\DateTimeImmutable $expiry = null;

    private bool $secure = false;

    private bool $httpOnly = true;

    private ?string $sameSite = null;

    private function __construct()
    {
        // only allow creation via named constructors
    }

    /**
     * @param string $path Cookie path.
     *
     * @return self Cloned instance with the specified operation applied. Cloned instance with the specified operation
     *     applied.
     *
     * @link https://tools.ietf.org/html/rfc6265#section-5.2.4
     */
    public function withPath(string $path): self
    {
        $new = clone $this;
        $new->path = $path;

        return $new;
    }

    /**
     * @param string $domain Cookie domain.
     *
     * @return self Cloned instance with the specified operation applied.
     *
     * @link https://tools.ietf.org/html/rfc6265#section-5.2.3
     */
    public function withDomain(string $domain): self
    {
        $new = clone $this;
        $new->domain = $domain;

        return $new;
    }

    /**
     * @param string $sameSite Cookie SameSite attribute value.
     *
     * @return self Cloned instance with the specified operation applied.
     *
     * @link https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-03#section-5.3.7
     */
    public function withSameSite(string $sameSite): self
    {
        $normalizedValue = \ucfirst(\strtolower($sameSite));
        if (!\in_array($normalizedValue, [self::SAMESITE_NONE, self::SAMESITE_LAX, self::SAMESITE_STRICT], true)) {
            throw new \Error("Invalid SameSite attribute: " . $sameSite);
        }

        $new = clone $this;
        $new->sameSite = $normalizedValue;

        return $new;
    }

    /**
     * @return self Cloned instance with the specified operation applied.
     *
     * @link https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-03#section-5.3.7
     */
    public function withoutSameSite(): self
    {
        $new = clone $this;
        $new->sameSite = null;

        return $new;
    }

    /**
     * Applies the given maximum age to the cookie.
     *
     * @param int $maxAge Cookie maximum age.
     *
     * @return self Cloned instance with the specified operation applied.
     *
     * @see self::withoutMaxAge()
     * @see self::withExpiry()
     *
     * @link https://tools.ietf.org/html/rfc6265#section-5.2.2
     */
    public function withMaxAge(int $maxAge): self
    {
        $new = clone $this;
        $new->maxAge = $maxAge;

        return $new;
    }

    /**
     * Removes any max-age information.
     *
     * @return self Cloned instance with the specified operation applied.
     *
     * @see self::withMaxAge()
     *
     * @link https://tools.ietf.org/html/rfc6265#section-5.2.2
     */
    public function withoutMaxAge(): self
    {
        $new = clone $this;
        $new->maxAge = null;

        return $new;
    }

    /**
     * Applies the given expiry to the cookie.
     *
     * @return self Cloned instance with the specified operation applied.
     *
     * @see self::withMaxAge()
     * @see self::withoutExpiry()
     *
     * @link https://tools.ietf.org/html/rfc6265#section-5.2.1
     */
    public function withExpiry(\DateTimeInterface $date): self
    {
        $new = clone $this;
        $new->expiry = \DateTimeImmutable::createFromInterface($date);

        return $new;
    }

    /**
     * Removes any expiry information.
     *
     * @return self Cloned instance with the specified operation applied.
     *
     * @see self::withExpiry()
     *
     * @link https://tools.ietf.org/html/rfc6265#section-5.2.1
     */
    public function withoutExpiry(): self
    {
        $new = clone $this;
        $new->expiry = null;

        return $new;
    }

    /**
     * @return self Cloned instance with the specified operation applied.
     *
     * @see self::withoutSecure()
     *
     * @link https://tools.ietf.org/html/rfc6265#section-5.2.5
     */
    public function withSecure(): self
    {
        $new = clone $this;
        $new->secure = true;

        return $new;
    }

    /**
     * @return self Cloned instance with the specified operation applied.
     *
     * @see self::withSecure()
     *
     * @link https://tools.ietf.org/html/rfc6265#section-5.2.5
     */
    public function withoutSecure(): self
    {
        $new = clone $this;
        $new->secure = false;

        return $new;
    }

    /**
     * @return self Cloned instance with the specified operation applied.
     *
     * @see self::withoutHttpOnly()
     *
     * @link https://tools.ietf.org/html/rfc6265#section-5.2.6
     */
    public function withHttpOnly(): self
    {
        $new = clone $this;
        $new->httpOnly = true;

        return $new;
    }

    /**
     * @return self Cloned instance with the specified operation applied.
     *
     * @see self::withHttpOnly()
     *
     * @link https://tools.ietf.org/html/rfc6265#section-5.2.6
     */
    public function withoutHttpOnly(): self
    {
        $new = clone $this;
        $new->httpOnly = false;

        return $new;
    }

    /**
     * @return string Cookie path.
     *
     * @link https://tools.ietf.org/html/rfc6265#section-5.2.4
     */
    public function getPath(): string
    {
        return $this->path;
    }

    /**
     * @return string Cookie domain.
     *
     * @link https://tools.ietf.org/html/rfc6265#section-5.2.3
     */
    public function getDomain(): string
    {
        return $this->domain;
    }

    /**
     * @return string Cookie domain.
     *
     * @link https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-03#section-5.3.7
     */
    public function getSameSite(): ?string
    {
        return $this->sameSite;
    }

    /**
     * @return int|null Cookie maximum age in seconds or `null` if no value is set.
     *
     * @link https://tools.ietf.org/html/rfc6265#section-5.2.2
     */
    public function getMaxAge(): ?int
    {
        return $this->maxAge;
    }

    /**
     * @return \DateTimeImmutable|null Cookie expiry or `null` if no value is set.
     *
     * @link https://tools.ietf.org/html/rfc6265#section-5.2.2
     */
    public function getExpiry(): ?\DateTimeImmutable
    {
        return $this->expiry;
    }

    /**
     * @return bool Whether the secure flag is enabled or not.
     *
     * @link https://tools.ietf.org/html/rfc6265#section-5.2.5
     */
    public function isSecure(): bool
    {
        return $this->secure;
    }

    /**
     * @return bool Whether the httpOnly flag is enabled or not.
     *
     * @link https://tools.ietf.org/html/rfc6265#section-5.2.6
     */
    public function isHttpOnly(): bool
    {
        return $this->httpOnly;
    }

    /**
     * @return string Representation of the cookie attributes appended to key=value in a 'set-cookie' header.
     */
    public function toString(): string
    {
        $string = '';

        if ($this->expiry) {
            $string .= '; Expires=' . \gmdate('D, j M Y G:i:s T', $this->expiry->getTimestamp());
        }

        /** @psalm-suppress RiskyTruthyFalsyComparison */
        if ($this->maxAge) {
            $string .= '; Max-Age=' . $this->maxAge;
        }

        if ('' !== $this->path) {
            $string .= '; Path=' . $this->path;
        }

        if ('' !== $this->domain) {
            $string .= '; Domain=' . $this->domain;
        }

        if ($this->secure) {
            $string .= '; Secure';
        }

        if ($this->httpOnly) {
            $string .= '; HttpOnly';
        }

        if ($this->sameSite !== null) {
            $string .= '; SameSite=' . $this->sameSite;
        }

        return $string;
    }

    /**
     * @see toString()
     */
    public function __toString(): string
    {
        return $this->toString();
    }
}
<?php declare(strict_types=1);

namespace Amp\Http;

use const Amp\Http\Internal\HEADER_LOWERCASE_MAP;

/**
 * Base class for HTTP request and response messages.
 *
 * @psalm-type HeaderPairsType = list<array{non-empty-string, string}>
 * @psalm-type HeaderParamValueType = string|array<string>
 * @psalm-type HeaderParamArrayType = array<non-empty-string, HeaderParamValueType>
 * @psalm-type HeaderMapType = array<non-empty-string, list<string>>
 */
abstract class HttpMessage
{
    /** @var HeaderMapType */
    private array $headers = [];

    /** @var array<non-empty-string, list<non-empty-string>> */
    private array $headerCase = [];

    /**
     * Returns the headers as a string-indexed array of arrays of strings or an empty array if no headers
     * have been set.
     *
     * @return HeaderMapType
     */
    public function getHeaders(): array
    {
        return $this->headers;
    }

    /**
     * Returns the headers as list of [field, name] pairs in the original casing provided by the application or server.
     *
     * @return HeaderPairsType
     */
    final public function getHeaderPairs(): array
    {
        $headers = [];

        foreach ($this->headers as $lcName => $values) {
            $size = \count($values);

            for ($i = 0; $i < $size; $i++) {
                $headers[] = [$this->headerCase[$lcName][$i], $values[$i]];
            }
        }

        return $headers;
    }

    /**
     * Returns the array of values for the given header or an empty array if the header does not exist.
     *
     * @return list<string>
     *
     * @psalm-suppress InvalidArrayOffset Using an empty string will not cause an error.
     */
    public function getHeaderArray(string $name): array
    {
        return $this->headers[HEADER_LOWERCASE_MAP[$name] ?? \strtolower($name)] ?? [];
    }

    /**
     * Returns the value of the given header. If multiple headers are present for the named header, only the first
     * header value will be returned. Use getHeaderArray() to return an array of all values for the particular header.
     * Returns null if the header does not exist.
     *
     * @psalm-suppress InvalidArrayOffset Using an empty string will not cause an error.
     */
    public function getHeader(string $name): ?string
    {
        return $this->headers[HEADER_LOWERCASE_MAP[$name] ?? \strtolower($name)][0] ?? null;
    }

    /**
     * Removes all current headers and sets new headers from the given array.
     *
     * @param HeaderParamArrayType $headers
     */
    protected function setHeaders(array $headers): void
    {
        // Ensure this is an atomic operation, either all headers are set or none.
        $before = $this->headers;
        $beforeCase = $this->headerCase;

        $this->headers = [];
        $this->headerCase = [];

        try {
            $this->setHeadersFromArray($headers);
        } catch (\Throwable $e) {
            $this->headers = $before;
            $this->headerCase = $beforeCase;

            throw $e;
        }
    }

    /**
     * Replaces headers from the given array. Header names not contained in the array are not changed.
     *
     * @param HeaderParamArrayType $headers
     */
    protected function replaceHeaders(array $headers): void
    {
        // Ensure this is an atomic operation, either all given headers are replaced or none.
        $before = $this->headers;
        $beforeCase = $this->headerCase;

        try {
            $this->setHeadersFromArray($headers);
        } catch (\Throwable $e) {
            $this->headers = $before;
            $this->headerCase = $beforeCase;

            throw $e;
        }
    }

    /**
     * @param HeaderParamArrayType $headers
     */
    private function setHeadersFromArray(array $headers): void
    {
        foreach ($headers as $name => $value) {
            if (!\is_string($value) && !\is_array($value)) {
                $value = self::castHeaderValue($value);
            }

            $this->setHeader($name, $value);
        }
    }

    /**
     * Sets the named header to the given value.
     *
     * @param non-empty-string $name
     * @param HeaderParamValueType $value
     *
     * @throws \Error If the header name or value is invalid.
     */
    protected function setHeader(string $name, array|string $value): void
    {
        \assert($this->isNameValid($name), "Invalid header name");

        $lcName = HEADER_LOWERCASE_MAP[$name] ?? \strtolower($name);

        if (!\is_array($value)) {
            \assert(self::isValueValid([$value]), "Invalid header value");
            $this->headers[$lcName] = [$value];
            $this->headerCase[$lcName] = [$name];
            return;
        }

        if (!$value) {
            $this->removeHeader($name);
            return;
        }

        $value = self::castHeaderArrayValues($value);

        \assert(self::isValueValid($value), "Invalid header value");

        $this->headers[$lcName] = $value;
        $this->headerCase[$lcName] = \array_fill(0, \count($value), $name);
    }

    /**
     * Adds the value to the named header, or creates the header with the given value if it did not exist.
     *
     * @param non-empty-string $name
     * @param HeaderParamValueType $value
     *
     * @throws \Error If the header name or value is invalid.
     */
    protected function addHeader(string $name, array|string $value): void
    {
        \assert($this->isNameValid($name), "Invalid header name");

        $lcName = HEADER_LOWERCASE_MAP[$name] ?? \strtolower($name);

        if (!\is_array($value)) {
            \assert(self::isValueValid([$value]), "Invalid header value");
            $this->headers[$lcName][] = $value;
            $this->headerCase[$lcName][] = $name;
            return;
        }

        $value = self::castHeaderArrayValues($value);

        \assert(self::isValueValid($value), "Invalid header value");

        foreach ($value as $header) {
            $this->headers[$lcName][] = $header;
            $this->headerCase[$lcName][] = $name;
        }
    }

    /**
     * Removes the given header if it exists.
     *
     * @psalm-suppress InvalidArrayOffset Using an empty string will not cause an error.
     */
    protected function removeHeader(string $name): void
    {
        $lcName = HEADER_LOWERCASE_MAP[$name] ?? \strtolower($name);

        unset($this->headers[$lcName], $this->headerCase[$lcName]);
    }

    /**
     * Checks if given header exists.
     *
     * @psalm-suppress InvalidArrayOffset Using an empty string will not cause an error.
     */
    public function hasHeader(string $name): bool
    {
        return isset($this->headers[HEADER_LOWERCASE_MAP[$name] ?? \strtolower($name)]);
    }

    private static function castHeaderValue(mixed $value): string
    {
        return match (true) {
            \is_string($value) => $value,
            \is_int($value), \is_float($value), $value instanceof \Stringable => (string) $value,
            default => throw new \TypeError(\sprintf(
                'Header array may contain only types which may be cast to a string; got "%s"',
                \get_debug_type($value),
            )),
        };
    }

    /**
     * @param array<int|float|string> $values
     * @return list<string>
     */
    private static function castHeaderArrayValues(array $values): array
    {
        static $mapper;

        return \array_map($mapper ??= self::castHeaderValue(...), \array_values($values));
    }

    private function isNameValid(string $name): bool
    {
        return (bool) \preg_match('/^[A-Za-z0-9`~!#$%^&_|\'\-*+.]+$/', $name);
    }

    /**
     * Determines if the given value is a valid header value.
     *
     * @param list<string> $values
     *
     * @throws \Error If the given value cannot be converted to a string and is not an array of values that can be
     *     converted to strings.
     */
    private static function isValueValid(array $values): bool
    {
        foreach ($values as $value) {
            if (\preg_match("/[^\t\r\n\x20-\x7e\x80-\xfe]|\r\n/", $value)) {
                return false;
            }
        }

        return true;
    }
}
<?php declare(strict_types=1);

namespace Amp\Http;

final class InvalidHeaderException extends \Exception
{
    /**
     * Thrown on header injection attempts.
     *
     * @param string $reason Reason that can be used as HTTP response reason.
     */
    public function __construct(string $reason)
    {
        parent::__construct($reason);
    }
}
<?php declare(strict_types=1);

namespace Amp\Http;

use League\Uri\QueryString;
use Psr\Http\Message\UriInterface as PsrUri;

/**
 * @psalm-type QueryPairsType = list<array{string, string|null}>
 * @psalm-type QueryValueType = string|array<string|null>|null
 * @psalm-type QueryArrayType = array<string, QueryValueType>
 * @psalm-type QueryMapType = array<string, list<string|null>>
 */
abstract class HttpRequest extends HttpMessage
{
    /** @var QueryMapType|null  */
    private ?array $queryMap = null;

    /** @var QueryPairsType|null */
    private ?array $queryPairs = null;

    /**
     * @param non-empty-string $method
     */
    public function __construct(
        private string $method,
        private PsrUri $uri,
    ) {
    }

    /**
     * @return non-empty-string
     */
    public function getMethod(): string
    {
        return $this->method;
    }

    /**
     * @param non-empty-string $method
     */
    protected function setMethod(string $method): void
    {
        $this->method = $method;
    }

    public function getUri(): PsrUri
    {
        return $this->uri;
    }

    protected function setUri(PsrUri $uri): void
    {
        if ($this->uri->getQuery() !== $uri->getQuery()) {
            $this->queryMap = null;
            $this->queryPairs = null;
        }

        $this->uri = $uri;
    }

    /**
     * @link https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
     */
    public function isIdempotent(): bool
    {
        return \in_array($this->getMethod(), ['GET', 'HEAD', 'PUT', 'DELETE'], true);
    }

    public function hasQueryParameter(string $key): bool
    {
        return isset($this->getQueryParameters()[$key]);
    }

    public function getQueryParameter(string $key): ?string
    {
        return $this->getQueryParameterArray($key)[0] ?? null;
    }

    /**
     * @return list<string>
     */
    public function getQueryParameterArray(string $key): array
    {
        return $this->getQueryParameters()[$key] ?? [];
    }

    /**
     * @return array<string, list<string>>
     */
    public function getQueryParameters(): array
    {
        static $mapper;

        $mapper ??= static function (array $values): array {
            \assert(\array_is_list($values));
            return \array_map('strval', $values);
        };

        return \array_map($mapper, $this->getInternalQueryParameters());
    }

    /**
     * @return QueryPairsType
     * @psalm-suppress PropertyTypeCoercion
     */
    public function getQueryParameterPairs(): array
    {
        /** @var QueryPairsType */
        return $this->queryPairs ??= match ($queryString = $this->uri->getQuery()) {
            '' => [],
            default => QueryString::parse($queryString, '&', \PHP_QUERY_RFC1738),
        };
    }

    /**
     * @param QueryValueType $value
     */
    protected function setQueryParameter(string $key, array|string|null $value): void
    {
        $query = $this->getInternalQueryParameters();
        $query[$key] = self::castQueryArrayValues(\is_array($value) ? $value : [$value]);
        $this->updateUriWithQuery($query);
    }

    /**
     * @param QueryValueType $value
     */
    protected function addQueryParameter(string $key, array|string|null $value): void
    {
        $query = $this->getInternalQueryParameters();
        $query[$key] = [
            ...($query[$key] ?? []),
            ...self::castQueryArrayValues(\is_array($value) ? $value : [$value]),
        ];
        $this->updateUriWithQuery($query);
    }

    /**
     * @param QueryArrayType $parameters
     */
    protected function setQueryParameters(array $parameters): void
    {
        $query = self::buildQueryFromParameters($parameters);
        $this->updateUriWithQuery($query);
    }

    /**
     * @param QueryArrayType $parameters
     */
    protected function replaceQueryParameters(array $parameters): void
    {
        $this->updateUriWithQuery([
            ...$this->getInternalQueryParameters(),
            ...self::buildQueryFromParameters($parameters),
        ]);
    }

    protected function removeQueryParameter(string $key): void
    {
        $query = $this->getInternalQueryParameters();
        unset($query[$key]);
        $this->updateUriWithQuery($query);
    }

    protected function removeQuery(): void
    {
        $this->uri = $this->uri->withQuery('');
        $this->queryMap = [];
        $this->queryPairs = [];
    }

    /**
     * @return QueryMapType
     */
    private function getInternalQueryParameters(): array
    {
        return $this->queryMap ??= $this->buildQueryFromUri();
    }

    /**
     * @return QueryMapType
     */
    private function buildQueryFromUri(): array
    {
        $query = [];
        foreach ($this->getQueryParameterPairs() as [$key, $value]) {
            $query[$key][] = $value;
        }

        return $query;
    }

    /**
     * @param QueryMapType $query
     */
    private function updateUriWithQuery(array $query): void
    {
        $pairs = [];
        foreach ($query as $key => $values) {
            \array_push($pairs, ...\array_map(static fn ($value) => [$key, $value], $values));
        }

        $this->uri = $this->uri->withQuery(QueryString::build($pairs, '&', \PHP_QUERY_RFC3986) ?? '');
        $this->queryMap = $query;
        $this->queryPairs = $pairs;
    }

    /**
     * @param array<string|int|float|null> $values
     * @return list<string|null>
     */
    private static function castQueryArrayValues(array $values): array
    {
        static $mapper;

        $mapper ??= static fn (mixed $value) => match (true) {
            \is_string($value) => $value,
            \is_null($value) => $value, // string and null check on separate lines for Psalm.
            \is_int($value), \is_float($value), $value instanceof \Stringable => (string) $value,
            default => throw new \TypeError(\sprintf(
                'Query array may contain only types which may be cast to a string; got "%s"',
                \get_debug_type($value),
            )),
        };

        return \array_map($mapper, \array_values($values));
    }

    private static function buildQueryFromParameters(array $parameters): array
    {
        $query = [];
        foreach ($parameters as $key => $values) {
            $query[$key] = self::castQueryArrayValues(\is_array($values) ? $values : [$values]);
        }

        return $query;
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Internal;

/** @internal */
const HEADER_LOWERCASE_MAP = [
    'Accept' => 'accept',
    'accept' => 'accept',
    'Accept-Encoding' => 'accept-encoding',
    'accept-encoding' => 'accept-encoding',
    'Accept-Language' => 'accept-language',
    'accept-language' => 'accept-language',
    'Authorization' => 'authorization',
    'authorization' => 'authorization',
    'Cache-Control' => 'cache-control',
    'cache-control' => 'cache-control',
    'Connection' => 'connection',
    'connection' => 'connection',
    'Content-Encoding' => 'content-encoding',
    'content-encoding' => 'content-encoding',
    'Content-Length' => 'content-length',
    'content-length' => 'content-length',
    'Content-Type' => 'content-type',
    'content-type' => 'content-type',
    'Content-Security-Policy' => 'content-security-policy',
    'content-security-policy' => 'content-security-policy',
    'Cookie' => 'cookie',
    'cookie' => 'cookie',
    'Date' => 'date',
    'date' => 'date',
    'Forwarded' => 'forwarded',
    'forwarded' => 'forwarded',
    'Host' => 'host',
    'host' => 'host',
    'Referrer-Policy' => 'referrer-policy',
    'referrer-policy' => 'referrer-policy',
    'Sec-Fetch-Dest' => 'sec-fetch-dest',
    'sec-fetch-dest' => 'sec-fetch-dest',
    'Sec-Fetch-Mode' => 'sec-fetch-mode',
    'sec-fetch-mode' => 'sec-fetch-mode',
    'Sec-Fetch-Site' => 'sec-fetch-site',
    'sec-fetch-site' => 'sec-fetch-site',
    'Sec-Fetch-User' => 'sec-fetch-user',
    'sec-fetch-user' => 'sec-fetch-user',
    'Set-Cookie' => 'set-cookie',
    'set-cookie' => 'set-cookie',
    'Strict-Transport-Security' => 'strict-transport-security',
    'strict-transport-security' => 'strict-transport-security',
    'Transfer-Encoding' => 'transfer-encoding',
    'transfer-encoding' => 'transfer-encoding',
    'Upgrade-Insecure-Requests' => 'upgrade-insecure-requests',
    'upgrade-insecure-requests' => 'upgrade-insecure-requests',
    'User-Agent' => 'user-agent',
    'user-agent' => 'user-agent',
    'Vary' => 'vary',
    'vary' => 'vary',
    'X-Content-Type-Options' => 'x-content-type-options',
    'x-content-type-options' => 'x-content-type-options',
    'X-Forwarded-For' => 'x-forwarded-for',
    'x-forwarded-for' => 'x-forwarded-for',
    'X-Forwarded-Host' => 'x-forwarded-host',
    'x-forwarded-host' => 'x-forwarded-host',
    'X-Forwarded-Proto' => 'x-forwarded-proto',
    'x-forwarded-proto' => 'x-forwarded-proto',
    'X-Frame-Options' => 'x-frame-options',
    'x-frame-options' => 'x-frame-options',
    'X-Xss-Protection' => 'x-xss-protection',
    'x-xss-protection' => 'x-xss-protection',
];
<?php declare(strict_types=1);

namespace Amp\Http;

abstract class HttpResponse extends HttpMessage
{
    private int $status;

    private string $reason;

    public function __construct(
        int $status,
        ?string $reason = null,
    ) {
        $this->validateStatusCode($status, $reason);
    }

    /**
     * Retrieve the response's three-digit HTTP status code.
     */
    public function getStatus(): int
    {
        return $this->status;
    }

    /**
     * Retrieve the response's (possibly empty) reason phrase.
     */
    public function getReason(): string
    {
        return $this->reason;
    }

    protected function setStatus(int $status, ?string $reason = null): void
    {
        $this->validateStatusCode($status, $reason);
    }

    private function validateStatusCode(int $status, ?string $reason): void
    {
        if ($status < 100 || $status > 599) {
            throw new \ValueError(
                'Invalid status code. Must be an integer between 100 and 599, inclusive.'
            );
        }

        $this->status = $status;
        $this->reason = $reason ?? HttpStatus::getReason($status);
    }

    /**
     * Response has a status code between 100 and 199.
     * @see HttpStatus::isInformational()
     */
    public function isInformational(): bool
    {
        return HttpStatus::isInformational($this->status);
    }

    /**
     * Response has a status code between 200 and 299.
     * @see HttpStatus::isSuccessful()
     */
    public function isSuccessful(): bool
    {
        return HttpStatus::isSuccessful($this->status);
    }

    /**
     * Response has a status code between 300 and 399.
     * @see HttpStatus::isRedirect()
     */
    public function isRedirect(): bool
    {
        return HttpStatus::isRedirect($this->status);
    }

    /**
     * Response has a status code between 400 and 499.
     * @see HttpStatus::isClientError()
     */
    public function isClientError(): bool
    {
        return HttpStatus::isClientError($this->status);
    }

    /**
     * Response has a status code between 500 and 599.
     * @see HttpStatus::isServerError()
     */
    public function isServerError(): bool
    {
        return HttpStatus::isServerError($this->status);
    }
}
<?php declare(strict_types=1);

namespace Amp\Http;

/**
 * @link https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
 */
final class HttpStatus
{
    public const CONTINUE = 100;
    public const SWITCHING_PROTOCOLS = 101;
    public const PROCESSING = 102;
    public const EARLY_HINTS = 103;

    public const OK = 200;
    public const CREATED = 201;
    public const ACCEPTED = 202;
    public const NON_AUTHORITATIVE_INFORMATION = 203;
    public const NO_CONTENT = 204;
    public const RESET_CONTENT = 205;
    public const PARTIAL_CONTENT = 206;
    public const MULTI_STATUS = 207;
    public const ALREADY_REPORTED = 208;
    public const IM_USED = 226;

    public const MULTIPLE_CHOICES = 300;
    public const MOVED_PERMANENTLY = 301;
    public const FOUND = 302;
    public const SEE_OTHER = 303;
    public const NOT_MODIFIED = 304;
    public const USE_PROXY = 305;
    public const TEMPORARY_REDIRECT = 307;
    public const PERMANENT_REDIRECT = 308;

    public const BAD_REQUEST = 400;
    public const UNAUTHORIZED = 401;
    public const PAYMENT_REQUIRED = 402;
    public const FORBIDDEN = 403;
    public const NOT_FOUND = 404;
    public const METHOD_NOT_ALLOWED = 405;
    public const NOT_ACCEPTABLE = 406;
    public const PROXY_AUTHENTICATION_REQUIRED = 407;
    public const REQUEST_TIMEOUT = 408;
    public const CONFLICT = 409;
    public const GONE = 410;
    public const LENGTH_REQUIRED = 411;
    public const PRECONDITION_FAILED = 412;
    public const PAYLOAD_TOO_LARGE = 413;
    public const URI_TOO_LONG = 414;
    public const UNSUPPORTED_MEDIA_TYPE  = 415;
    public const RANGE_NOT_SATISFIABLE = 416;
    public const EXPECTATION_FAILED = 417;
    public const MISDIRECTED_REQUEST = 421;
    public const UNPROCESSABLE_ENTITY = 422;
    public const LOCKED = 423;
    public const FAILED_DEPENDENCY = 424;
    public const UPGRADE_REQUIRED = 426;
    public const PRECONDITION_REQUIRED = 428;
    public const TOO_MANY_REQUESTS = 429;
    public const REQUEST_HEADER_FIELDS_TOO_LARGE = 431;
    public const UNAVAILABLE_FOR_LEGAL_REASONS = 451;

    public const INTERNAL_SERVER_ERROR = 500;
    public const NOT_IMPLEMENTED = 501;
    public const BAD_GATEWAY = 502;
    public const SERVICE_UNAVAILABLE = 503;
    public const GATEWAY_TIMEOUT = 504;
    public const HTTP_VERSION_NOT_SUPPORTED = 505;
    public const VARIANT_ALSO_NEGOTIATES = 506;
    public const INSUFFICIENT_STORAGE = 507;
    public const LOOP_DETECTED = 508;
    public const NOT_EXTENDED = 510;
    public const NETWORK_AUTHENTICATION_REQUIRED = 511;

    public static function getReason(int $code): string
    {
        return [
            100 => 'Continue',
            101 => 'Switching Protocols',
            102 => 'Processing',
            103 => 'Early Hints',
            200 => 'OK',
            201 => 'Created',
            202 => 'Accepted',
            203 => 'Non-Authoritative Information',
            204 => 'No Content',
            205 => 'Reset Content',
            206 => 'Partial Content',
            207 => 'Multi-Status',
            208 => 'Already Reported',
            226 => 'IM Used',
            300 => 'Multiple Choices',
            301 => 'Moved Permanently',
            302 => 'Found',
            303 => 'See Other',
            304 => 'Not Modified',
            305 => 'Use Proxy',
            307 => 'Temporary Redirect',
            308 => 'Permanent Redirect',
            400 => 'Bad Request',
            401 => 'Unauthorized',
            402 => 'Payment Required',
            403 => 'Forbidden',
            404 => 'Not Found',
            405 => 'Method Not Allowed',
            406 => 'Not Acceptable',
            407 => 'Proxy Authentication Required',
            408 => 'Request Timeout',
            409 => 'Conflict',
            410 => 'Gone',
            411 => 'Length Required',
            412 => 'Precondition Failed',
            413 => 'Payload Too Large',
            414 => 'URI Too Long',
            415 => 'Unsupported Media Type',
            416 => 'Range Not Satisfiable',
            417 => 'Expectation Failed',
            421 => 'Misdirected Request',
            422 => 'Unprocessable Entity',
            423 => 'Locked',
            424 => 'Failed Dependency',
            426 => 'Upgrade Required',
            428 => 'Precondition Required',
            429 => 'Too Many Requests',
            431 => 'Request Header Fields Too Large',
            451 => 'Unavailable For Legal Reasons',
            500 => 'Internal Server Error',
            501 => 'Not Implemented',
            502 => 'Bad Gateway',
            503 => 'Service Unavailable',
            504 => 'Gateway Timeout',
            505 => 'HTTP Version Not Supported',
            506 => 'Variant Also Negotiates',
            507 => 'Insufficient Storage',
            508 => 'Loop Detected',
            510 => 'Not Extended',
            511 => 'Network Authentication Required',
        ][$code] ?? '';
    }

    /**
     * Status code is between 100 and 199, representing an informational response.
     */
    public static function isInformational(int $code): bool
    {
        return $code >= 100 && $code < 200;
    }

    /**
     * Status code is between 200 and 299, representing a successful response.
     */
    public static function isSuccessful(int $code): bool
    {
        return $code >= 200 && $code < 300;
    }

    /**
     * Status code is between 300 and 399, representing a redirect response.
     */
    public static function isRedirect(int $code): bool
    {
        return $code >= 300 && $code < 400;
    }

    /**
     * Status code is between 400 and 499, representing a client error response.
     */
    public static function isClientError(int $code): bool
    {
        return $code >= 400 && $code < 500;
    }

    /**
     * Status code is between 500 and 599, representing a server error response.
     */
    public static function isServerError(int $code): bool
    {
        return $code >= 500 && $code < 600;
    }

    // @codeCoverageIgnoreStart
    private function __construct()
    {
        // forbid instances
    }
    // @codeCoverageIgnoreEnd
}
<?php declare(strict_types=1);

namespace Amp\Http\Http1;

use Amp\Http\HttpMessage;
use Amp\Http\InvalidHeaderException;
use const Amp\Http\Internal\HEADER_LOWERCASE_MAP;

/**
 * @link https://tools.ietf.org/html/rfc7230
 * @link https://tools.ietf.org/html/rfc2616
 * @link https://tools.ietf.org/html/rfc5234
 *
 * @psalm-import-type HeaderPairsType from HttpMessage
 * @psalm-import-type HeaderMapType from HttpMessage
 */
final class Rfc7230
{
    // We make use of possessive modifiers, which gives a slight performance boost
    private const HEADER_REGEX = "(^([^()<>@,;:\\\"/[\]?={}\x01-\x20\x7F]++):[ \t]*+((?:[ \t]*+[\x21-\x7E\x80-\xFF]++)*+)[ \t]*+\r\n)m";
    private const HEADER_FOLD_REGEX = "(\r\n[ \t]++)";

    private const HEADER_SPRINTF = [
        0 => "",
        1 => "%s: %s\r\n",
        2 => "%s: %s\r\n%s: %s\r\n",
        3 => "%s: %s\r\n%s: %s\r\n%s: %s\r\n",
        4 => "%s: %s\r\n%s: %s\r\n%s: %s\r\n%s: %s\r\n",
        5 => "%s: %s\r\n%s: %s\r\n%s: %s\r\n%s: %s\r\n%s: %s\r\n",
        6 => "%s: %s\r\n%s: %s\r\n%s: %s\r\n%s: %s\r\n%s: %s\r\n%s: %s\r\n",
        7 => "%s: %s\r\n%s: %s\r\n%s: %s\r\n%s: %s\r\n%s: %s\r\n%s: %s\r\n%s: %s\r\n",
        8 => "%s: %s\r\n%s: %s\r\n%s: %s\r\n%s: %s\r\n%s: %s\r\n%s: %s\r\n%s: %s\r\n%s: %s\r\n",
        9 => "%s: %s\r\n%s: %s\r\n%s: %s\r\n%s: %s\r\n%s: %s\r\n%s: %s\r\n%s: %s\r\n%s: %s\r\n%s: %s\r\n",
        10 => "%s: %s\r\n%s: %s\r\n%s: %s\r\n%s: %s\r\n%s: %s\r\n%s: %s\r\n%s: %s\r\n%s: %s\r\n%s: %s\r\n%s: %s\r\n",
    ];

    /**
     * Parses headers according to RFC 7230 and 2616.
     *
     * Allows empty header values, as HTTP/1.0 allows that.
     *
     * @return HeaderMapType Associative array mapping header names to arrays of values.
     *
     * @throws InvalidHeaderException If invalid headers have been passed.
     */
    public static function parseHeaders(string $rawHeaders): array
    {
        $matches = self::matchHeaders($rawHeaders);

        $headers = [];

        foreach ($matches as $header) {
            // Unfortunately, we can't avoid the \strtolower() calls due to \array_change_key_case() behavior
            // when equal headers are present with different casing, e.g. 'set-cookie' and 'Set-Cookie'.
            // Accessing matches directly is slightly faster vs. using foreach (... as [...]).
            $headers[HEADER_LOWERCASE_MAP[$header[1]] ?? \strtolower($header[1])][] = $header[2];
        }

        return $headers;
    }

    /**
     * Parses headers according to RFC 7230 and 2616.
     *
     * Allows empty header values, as HTTP/1.0 allows that.
     *
     * @return HeaderPairsType List of [field, value] header pairs.
     *
     * @throws InvalidHeaderException If invalid headers have been passed.
     */
    public static function parseHeaderPairs(string $rawHeaders): array
    {
        $matches = self::matchHeaders($rawHeaders);

        $headers = [];

        foreach ($matches as $match) {
            // We avoid a call to \trim() here due to the regex.
            // Accessing matches directly is slightly faster vs. using foreach (... as [...]).
            $headers[] = [$match[1], $match[2]];
        }

        return $headers;
    }

    /**
     * @psalm-type MatchListType = list<array{non-empty-string, non-empty-string, string}>
     *
     * @return MatchListType
     */
    private static function matchHeaders(string $rawHeaders): array
    {
        // Ensure that the last line also ends with a newline, this is important.
        \assert(\str_ends_with($rawHeaders, "\r\n"), "Argument 1 must end with CRLF: " . \bin2hex($rawHeaders));

        $count = \preg_match_all(self::HEADER_REGEX, $rawHeaders, $matches, \PREG_SET_ORDER);

        // If these aren't the same, then one line didn't match and there's an invalid header.
        if ($count !== \substr_count($rawHeaders, "\n")) {
            // Folding is deprecated, see https://tools.ietf.org/html/rfc7230#section-3.2.4
            if (self::containsFoldedHeaders($rawHeaders)) {
                throw new InvalidHeaderException("Invalid header syntax: Obsolete line folding");
            }

            throw new InvalidHeaderException("Invalid header syntax");
        }

        /** @var MatchListType $matches */
        return $matches;
    }

    /**
     * Folding is deprecated, see https://tools.ietf.org/html/rfc7230#section-3.2.4.
     */
    public static function containsFoldedHeaders(string $rawHeaders): bool
    {
        if (\preg_match(self::HEADER_FOLD_REGEX, $rawHeaders)) {
            return true;
        }

        return false;
    }

    /**
     * Format headers in to their on-the-wire format.
     *
     * Headers are always validated syntactically. This protects against response splitting and header injection
     * attacks.
     *
     * @param HeaderMapType $headers Headers in a format as returned by {@see parseHeaders()}.
     *
     * @return string Formatted headers.
     *
     * @throws InvalidHeaderException If header names or values are invalid.
     */
    public static function formatHeaders(array $headers): string
    {
        $headerList = [];

        foreach ($headers as $name => $values) {
            foreach ($values as $value) {
                // PHP casts integer-like keys to integers
                $headerList[] = [(string) $name, (string) $value];
            }
        }

        return self::formatHeaderPairs($headerList);
    }

    /**
     * Format headers in to their on-the-wire HTTP/1 format.
     *
     * Headers are always validated syntactically. This protects against response splitting and header injection
     * attacks.
     *
     * @param HeaderPairsType $headers List of headers in [field, value] format as returned by
     * {@see HttpMessage::getHeaderPairs()}.
     *
     * @return string Formatted headers.
     *
     * @throws InvalidHeaderException If header names or values are invalid.
     */
    public static function formatHeaderPairs(array $headers): string
    {
        $lines = \count($headers);
        $bytes = \sprintf(
            self::HEADER_SPRINTF[$lines] ?? \str_repeat(self::HEADER_SPRINTF[1], $lines),
            ...\array_merge(...$headers),
        );
        $count = \preg_match_all(self::HEADER_REGEX, $bytes);

        if ($lines !== $count || $lines !== \substr_count($bytes, "\n")) {
            throw new InvalidHeaderException("Invalid headers");
        }

        return $bytes;
    }

    // @codeCoverageIgnoreStart
    private function __construct()
    {
        // forbid instances
    }
    // @codeCoverageIgnoreEnd
}
<?php declare(strict_types=1);

namespace Amp\Http;

use Amp\Http\Http1\Rfc7230;

/**
 * Splits comma-separated fields into individual components. Returns null if a syntax error is encountered.
 *
 * For example, the following header
 * `Cache-Control: public, max-age=604800, must-revalidate`
 * would be parsed to the array
 * ['public', 'max-age=604800', 'must-revalidate']
 *
 * @param non-empty-string $headerName
 *
 * @return list<string>|null
 */
function splitHeader(HttpMessage $message, string $headerName): ?array
{
    $header = \implode(',', $message->getHeaderArray($headerName));

    if ($header === '') {
        return [];
    }

    $positions = [];
    $withinQuotes = false;
    $headerLength = \strlen($header);
    for ($i = 0; $i < $headerLength; ++$i) {
        match ($header[$i]) {
            '\\' => ++$i, // Skip next character
            '"' => $withinQuotes = !$withinQuotes,
            ',' => $withinQuotes ? null : $positions[] = $i,
            default => null,
        };
    }

    if ($withinQuotes) {
        return null;
    }

    if (!$positions) {
        return [\trim($header)];
    }

    $offset = 0;
    $headers = [];
    foreach ($positions as $position) {
        $headers[] = \substr($header, $offset, $position - $offset);
        $offset = $position + 1;
    }
    $headers[] = \substr($header, $offset);

    return \array_map(\trim(...), $headers);
}

/**
 * Parses a list of key-value pairs from each comma-separated header value. Returns null if a syntax error is
 * encountered.
 *
 * For example, the following header
 * `Forwarded: for="172.18.0.1";proto=https, for="172.25.0.1";proto=http`
 * would be parsed to the array
 * `[['for' => '172.18.0.1', 'proto' => 'https'], ['for' => '172.25.0.1', 'proto' => 'http']]`
 *
 * @param non-empty-string $headerName
 *
 * @return list<array<non-empty-string, string>>|null
 */
function parseMultipleHeaderFields(HttpMessage $message, string $headerName): ?array
{
    $headers = splitHeader($message, $headerName);
    if ($headers === null) {
        return null;
    }

    $maps = [];
    foreach ($headers as $header) {
        $map = parseSingleHeaderFields($header);
        if ($map === null) {
            return null;
        }

        $maps[] = $map;
    }

    return $maps;
}

/**
 * Parse a single header into key-value pairs.
 *
 * @see https://tools.ietf.org/html/rfc7230#section-3.2.6
 *
 * @return array<non-empty-string, string>|null
 */
function parseSingleHeaderFields(string $header): ?array
{
    \preg_match_all(
        '((?:^|;\s*)([^=]+)(?:=(?:"((?:[^\\\\"]|\\\\\\\\|\\\\")*)"|([^";]+)))?\s*)',
        $header,
        $matches,
        \PREG_SET_ORDER,
    );

    $totalMatchedLength = 0;
    $map = [];

    foreach ($matches as $match) {
        $totalMatchedLength += \strlen($match[0]);

        $key = \trim(\strtolower($match[1]));
        $value = $match[3] ?? $match[2] ?? '';

        if (($match[2] ?? '') !== '') {
            // decode escaped characters
            $value = (string) \preg_replace('/\\\\(.)/', '\1', $match[2]);
        }

        \assert($key !== '');
        $map[$key] = $value;
    }

    if ($totalMatchedLength !== \strlen($header)) {
        return null; // parse error
    }

    return $map;
}

/**
 * Parses a list of tokens from each comma-separated header value. Returns null if a syntax error is
 * encountered. Use {@see parseMultipleHeaderFields()} for headers with key-value pairs.
 *
 * @link https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.6
 *
 * @return list<non-empty-string>|null
 */
function parseHeaderTokens(HttpMessage $message, string $headerName): ?array
{
    $combinedHeader = \implode(",", $message->getHeaderArray($headerName));
    if (\preg_match('([^!#$%&\'*+\-.^_`|~0-9A-Za-z, ])', $combinedHeader)) {
        return null;
    }

    $elements = \explode(",", $combinedHeader);
    $normalizedElements = \array_map(fn ($element) => \strtolower(\trim($element)), $elements);

    return \array_values(\array_filter($normalizedElements, fn ($element) => $element !== ''));
}

/**
 * Format timestamp in seconds as an HTTP date header.
 *
 * @param int|null $timestamp Timestamp to format, current time if `null`.
 *
 * @return string Formatted date header value.
 */
function formatDateHeader(?int $timestamp = null): string
{
    static $cachedTimestamp, $cachedFormattedDate;

    $timestamp = $timestamp ?? \time();
    if ($cachedTimestamp === $timestamp) {
        return $cachedFormattedDate;
    }

    return $cachedFormattedDate = \gmdate("D, d M Y H:i:s", $cachedTimestamp = $timestamp) . " GMT";
}

/**
 * Convert the output of {@see Rfc7230::parseHeaderPairs()} or {@see HttpMessage::getHeaderPairs()} into the structure
 * returned by {@see Rfc7230::parseHeaders()} or {@see HttpMessage::getHeaders()}.
 *
 * @param list<array{non-empty-string, string}> $pairs
 *
 * @return array<non-empty-string, list<string>>
 */
function mapHeaderPairs(array $pairs): array
{
    $headers = [];

    foreach ($pairs as $header) {
        /** @psalm-suppress RedundantCondition */
        \assert(
            \count($header) === 2
            && \array_is_list($header)
            && \is_string($header[0])
            && \is_string($header[1])
        );

        $headers[Internal\HEADER_LOWERCASE_MAP[$header[0]] ?? \strtolower($header[0])][] = $header[1];
    }

    return $headers;
}
{
    "name": "amphp/http",
    "description": "Basic HTTP primitives which can be shared by servers and clients.",
    "type": "library",
    "license": "MIT",
    "authors": [
        {
            "name": "Niklas Keller",
            "email": "me@kelunik.com"
        },
        {
            "name": "Aaron Piotrowski",
            "email": "aaron@trowski.com"
        }
    ],
    "autoload": {
        "psr-4": {
            "Amp\\Http\\": "src"
        },
        "files": [
            "src/functions.php",
            "src/Internal/constants.php"
        ]
    },
    "autoload-dev": {
        "psr-4": {
          "Amp\\Http\\": "test"
        }
    },
    "require": {
        "php": ">=8.1",
        "amphp/hpack": "^3",
        "amphp/parser": "^1.1",
        "league/uri-components": "^2.4.2 | ^7.1",
        "psr/http-message": "^1 | ^2"
    },
    "require-dev": {
        "phpunit/phpunit": "^9",
        "amphp/php-cs-fixer-config": "^2",
        "league/uri": "^6.8 | ^7.1",
        "psalm/phar": "^5.26.1"
    },
    "scripts": {
        "test": "php -dzend.assertions=1 -dassert.exception=1 vendor/bin/phpunit",
        "code-style": "php vendor/bin/php-cs-fixer fix"
    }
}
<?php

$config = new Amp\CodeStyle\Config();
$config->getFinder()
    ->in(__DIR__ . '/examples')
    ->in(__DIR__ . '/src')
    ->in(__DIR__ . '/test');

$config->setCacheFile(__DIR__ . '/.php_cs.cache');

return $config;
{
    "symbol-whitelist": [
      "createClientId",
      "DeflateContext",
      "deflate_add",
      "deflate_init",
      "ZLIB_ENCODING_GZIP",
      "ZLIB_ENCODING_RAW",
      "ZLIB_FINISH",
      "ZLIB_SYNC_FLUSH"
    ],
    "php-core-extensions": [
        "Core",
        "date",
        "pcre",
        "Phar",
        "Reflection",
        "SPL",
        "standard"
    ]
}
<?php declare(strict_types=1);

namespace Amp\Http\Server;

final class MissingAttributeError extends \Error
{
    public function __construct(string $message)
    {
        parent::__construct($message);
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Server;

interface RequestHandler
{
    public function handleRequest(Request $request): Response;
}
<?php declare(strict_types=1);

namespace Amp\Http\Server;

final class HttpErrorException extends \Exception
{
    public function __construct(
        private readonly int $status,
        private readonly ?string $reason = null,
        ?\Throwable $previous = null,
    ) {
        parent::__construct(
            message: 'Error ' . $status . ($this->reason !== null && $this->reason !== '' ? ': ' . $reason : ''),
            previous: $previous,
        );
    }

    public function getReason(): ?string
    {
        return $this->reason;
    }

    public function getStatus(): int
    {
        return $this->status;
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Server\RequestHandler;

use Amp\Http\Server\Request;
use Amp\Http\Server\RequestHandler;
use Amp\Http\Server\Response;

final class ClosureRequestHandler implements RequestHandler
{
    /**
     * @param \Closure(Request):Response $closure Closure accepting an {@see Request} object as the first
     * argument and returning an instance of {@see Response}.
     */
    public function __construct(private readonly \Closure $closure)
    {
    }

    #[\Override]
    public function handleRequest(Request $request): Response
    {
        return ($this->closure)($request);
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Server;

use Amp\Http\HttpMessage;
use Amp\Http\InvalidHeaderException;
use Psr\Http\Message\UriInterface as PsrUri;

final class Push extends HttpMessage
{
    /**
     * @param array<non-empty-string, string|array<string>> $headers
     *
     * @throws InvalidHeaderException If given headers contain and invalid header name or value.
     * @throws \Error If the given headers have a colon-prefixed header or a Host header.
     */
    public function __construct(private readonly PsrUri $uri, array $headers = [])
    {
        $this->setHeaders($headers);
    }

    #[\Override]
    protected function setHeader(string $name, array|string $value): void
    {
        if (($name[0] ?? ":") === ":" || !\strncasecmp("host", $name, 4)) {
            throw new \Error("Pushed headers must not contain colon-prefixed headers or a Host header");
        }

        parent::setHeader($name, $value);
    }

    public function getUri(): PsrUri
    {
        return $this->uri;
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Server;

use Amp\ByteStream\ReadableStream;
use Amp\Http\Cookie\RequestCookie;
use Amp\Http\HttpMessage;
use Amp\Http\HttpRequest;
use Amp\Http\Server\Driver\Client;
use Psr\Http\Message\UriInterface as PsrUri;

/**
 * @psalm-import-type HeaderParamValueType from HttpMessage
 * @psalm-import-type HeaderParamArrayType from HttpMessage
 */
final class Request extends HttpRequest
{
    private ?RequestBody $body = null;

    /** @var array<non-empty-string, RequestCookie> */
    private array $cookies = [];

    /** @var array<non-empty-string, mixed> */
    private array $attributes = [];

    private ?Trailers $trailers = null;

    /**
     * @param Client $client The client sending the request.
     * @param non-empty-string $method HTTP request method.
     * @param PsrUri $uri The full URI being requested, including host, port, and protocol.
     * @param array<non-empty-string, string|string[]> $headers An array of strings or an array of string arrays.
     * @param string $protocol HTTP protocol version (e.g. 1.0, 1.1, or 2.0).
     * @param Trailers|null $trailers Trailers if request has trailers, or null otherwise.
     */
    public function __construct(
        private readonly Client $client,
        string $method,
        PsrUri $uri,
        array $headers = [],
        ReadableStream|string $body = '',
        private string $protocol = "1.1",
        ?Trailers $trailers = null
    ) {
        parent::__construct($method, $uri);

        if ($body !== '') {
            $this->setBody($body);
        }

        if (!empty($headers)) {
            $this->setHeaders($headers);
        }

        if ($trailers !== null) {
            $this->setTrailers($trailers);
        }
    }

    /**
     * @return Client The client sending the request.
     */
    public function getClient(): Client
    {
        return $this->client;
    }

    /**
     * Sets the request HTTP method.
     */
    #[\Override]
    public function setMethod(string $method): void
    {
        parent::setMethod($method);
    }

    /**
     * Sets a new URI for the request.
     */
    #[\Override]
    public function setUri(PsrUri $uri): void
    {
        parent::setUri($uri);
    }

    /**
     * This method returns the HTTP protocol version (e.g. "1.0", "1.1", "2") in use;
     * it has nothing to do with URI schemes like http:// or https:// ...
     */
    public function getProtocolVersion(): string
    {
        return $this->protocol;
    }

    /**
     * Sets a new protocol version number for the request.
     */
    public function setProtocolVersion(string $protocol): void
    {
        $this->protocol = $protocol;
    }

    /**
     * Sets the headers from the given array. Any cookie headers will automatically populate the contained array of
     * RequestCookie objects.
     *
     * @param HeaderParamArrayType $headers
     */
    #[\Override]
    public function setHeaders(array $headers): void
    {
        $cookies = $this->cookies;

        try {
            parent::setHeaders($headers);
        } catch (\Throwable $e) {
            $this->cookies = $cookies;

            throw $e;
        }
    }

    /**
     * Replace headers from the given array. Any cookie headers will automatically populate the contained array of
     * RequestCookie objects.
     *
     * @param HeaderParamArrayType $headers
     */
    #[\Override]
    public function replaceHeaders(array $headers): void
    {
        $cookies = $this->cookies;

        try {
            parent::replaceHeaders($headers);
        } catch (\Throwable $e) {
            $this->cookies = $cookies;

            throw $e;
        }
    }

    /**
     * Sets the named header to the given value.
     *
     * @param non-empty-string $name
     * @param HeaderParamValueType $value
     *
     * @throws \Error If the header name or value is invalid.
     */
    #[\Override]
    public function setHeader(string $name, array|string $value): void
    {
        if (($name[0] ?? ":") === ":") {
            throw new \Error("Header name cannot be empty or start with a colon (:)");
        }

        parent::setHeader($name, $value);

        if (\stripos($name, "cookie") === 0) {
            $this->setCookiesFromHeaders();
        }
    }

    /**
     * Adds the value to the named header, or creates the header with the given value if it did not exist.
     *
     * @param non-empty-string $name
     * @param HeaderParamValueType $value
     *
     * @throws \Error If the header name or value is invalid.
     */
    #[\Override]
    public function addHeader(string $name, array|string $value): void
    {
        if (($name[0] ?? ":") === ":") {
            throw new \Error("Header name cannot be empty or start with a colon (:)");
        }

        parent::addHeader($name, $value);

        if (\stripos($name, "cookie") === 0) {
            $this->setCookiesFromHeaders();
        }
    }

    /**
     * Removes the given header if it exists.
     */
    #[\Override]
    public function removeHeader(string $name): void
    {
        parent::removeHeader($name);

        if (\stripos($name, "cookie") === 0) {
            $this->cookies = [];
        }
    }

    #[\Override]
    public function setQueryParameter(string $key, array|string|null $value): void
    {
        parent::setQueryParameter($key, $value);
    }

    #[\Override]
    public function addQueryParameter(string $key, array|string|null $value): void
    {
        parent::addQueryParameter($key, $value);
    }

    #[\Override]
    public function setQueryParameters(array $parameters): void
    {
        parent::setQueryParameters($parameters);
    }

    #[\Override]
    public function replaceQueryParameters(array $parameters): void
    {
        parent::replaceQueryParameters($parameters);
    }

    #[\Override]
    public function removeQueryParameter(string $key): void
    {
        parent::removeQueryParameter($key);
    }

    #[\Override]
    public function removeQuery(): void
    {
        parent::removeQuery();
    }

    /**
     * Retrieve the request body.
     */
    public function getBody(): RequestBody
    {
        return $this->body ??= new RequestBody('');
    }

    /**
     * Sets the stream for the message body. Note that using a string will automatically set the Content-Length header
     * to the length of the given string. Using an ReadableStream or Body instance will remove the Content-Length header.
     *
     * @throws \Error
     * @throws \TypeError
     */
    public function setBody(ReadableStream|string $body): void
    {
        if ($body instanceof ReadableStream) {
            $this->body = $body instanceof RequestBody ? $body : new RequestBody($body);
            $this->removeHeader("content-length");
            return;
        }

        $this->body = new RequestBody($body);

        if ($length = \strlen($body)) {
            $this->setHeader("content-length", (string) $length);
        } elseif (!\in_array($this->getMethod(), ["GET", "HEAD", "OPTIONS", "TRACE"])) {
            $this->setHeader("content-length", "0");
        } else {
            $this->removeHeader("content-length");
        }
    }

    /**
     * @return array<non-empty-string, RequestCookie>
     */
    public function getCookies(): array
    {
        return $this->cookies;
    }

    /**
     * @param string $name Name of the cookie.
     */
    public function getCookie(string $name): ?RequestCookie
    {
        return $this->cookies[$name] ?? null;
    }

    /**
     * Adds a cookie to the request.
     */
    public function setCookie(RequestCookie $cookie): void
    {
        $this->cookies[$cookie->getName()] = $cookie;
        $this->setHeadersFromCookies();
    }

    /**
     * Removes a cookie from the request.
     */
    public function removeCookie(string $name): void
    {
        if (isset($this->cookies[$name])) {
            unset($this->cookies[$name]);
            $this->setHeadersFromCookies();
        }
    }

    /**
     * Sets cookies based on headers.
     *
     * @throws \Error
     */
    private function setCookiesFromHeaders(): void
    {
        $this->cookies = [];

        $headers = $this->getHeaderArray("cookie");

        foreach ($headers as $line) {
            $cookies = RequestCookie::fromHeader($line);
            foreach ($cookies as $cookie) {
                $this->cookies[$cookie->getName()] = $cookie;
            }
        }
    }

    /**
     * Sets headers based on cookie values.
     */
    private function setHeadersFromCookies(): void
    {
        $values = [];

        foreach ($this->cookies as $cookie) {
            $values[] = $cookie->toString();
        }

        $this->setHeader("cookie", $values);
    }

    /**
     * @return array<non-empty-string, mixed> An array of all request attributes in the request's mutable local storage,
     * indexed by name.
     */
    public function getAttributes(): array
    {
        return $this->attributes;
    }

    /**
     * Check whether a variable with the given name exists in the request's mutable local storage.
     *
     * Each request has its own mutable local storage to which request handlers and middleware may read and write data.
     * Other request handlers or middleware which are aware of this data can then access it without the server being
     * tightly coupled to specific implementations.
     *
     * @param non-empty-string $name Name of the attribute, should be namespaced with a vendor and package namespace
     *      like classes.
     */
    public function hasAttribute(string $name): bool
    {
        return \array_key_exists($name, $this->attributes);
    }

    /**
     * Retrieve a variable from the request's mutable local storage.
     *
     * Each request has its own mutable local storage to which request handlers and middleware may read and write data.
     * Other request handlers or middleware which are aware of this data can then access it without the server being
     * tightly coupled to specific implementations.
     *
     * @param non-empty-string $name Name of the attribute, should be namespaced with a vendor and package namespace
     *      like classes.
     *
     * @throws MissingAttributeError If an attribute with the given name does not exist.
     */
    public function getAttribute(string $name): mixed
    {
        if (!$this->hasAttribute($name)) {
            throw new MissingAttributeError("The requested attribute '{$name}' does not exist");
        }

        return $this->attributes[$name];
    }

    /**
     * Assign a variable to the request's mutable local storage.
     *
     * Each request has its own mutable local storage to which request handlers and middleware may read and write data.
     * Other request handlers or middleware which are aware of this data can then access it without the server being
     * tightly coupled to specific implementations.
     *
     * **Example**
     *
     * ```php
     * $request->setAttribute(Router::class, $routeArguments);
     * ```
     *
     * @param non-empty-string $name Name of the attribute, should be namespaced with a vendor and package namespace
     *      like classes.
     * @param mixed $value Value of the attribute, might be any value.
     */
    public function setAttribute(string $name, mixed $value): void
    {
        $this->attributes[$name] = $value;
    }

    /**
     * Remove an attribute from the request's mutable local storage.
     *
     * @param non-empty-string $name Name of the attribute, should be namespaced with a vendor and package namespace
     *      like classes.
     *
     * @throws MissingAttributeError If an attribute with the given name does not exist.
     */
    public function removeAttribute(string $name): void
    {
        if (!$this->hasAttribute($name)) {
            throw new MissingAttributeError("The requested attribute '{$name}' does not exist");
        }

        unset($this->attributes[$name]);
    }

    /**
     * Remove all attributes from the request's mutable local storage.
     */
    public function removeAttributes(): void
    {
        $this->attributes = [];
    }

    public function getTrailers(): ?Trailers
    {
        return $this->trailers;
    }

    public function setTrailers(Trailers $trailers): void
    {
        $this->trailers = $trailers;
    }

    /**
     * Removes any trailer headers from the request.
     */
    public function removeTrailers(): void
    {
        $this->trailers = null;
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Server;

use Amp\ByteStream\ReadableBuffer;
use Amp\ByteStream\ReadableStream;
use Amp\Http\Cookie\ResponseCookie;
use Amp\Http\HttpMessage;
use Amp\Http\HttpResponse;
use Amp\Http\HttpStatus;
use League\Uri;
use Revolt\EventLoop;

/**
 * @psalm-import-type HeaderParamValueType from HttpMessage
 * @psalm-import-type HeaderParamArrayType from HttpMessage
 */
final class Response extends HttpResponse
{
    private ReadableStream $body;

    /** @var array<non-empty-string, ResponseCookie> */
    private array $cookies = [];

    /** @var array<string, Push> */
    private array $push = [];

    private ?\Closure $upgrade = null;

    /** @var list<\Closure():void> */
    private array $onDispose = [];

    private ?Trailers $trailers = null;

    /**
     * @param int $status HttpStatus code.
     * @param HeaderParamArrayType $headers
     */
    public function __construct(
        int $status = HttpStatus::OK,
        array $headers = [],
        ReadableStream|string $body = '',
        ?Trailers $trailers = null
    ) {
        parent::__construct($this->validateStatusCode($status));

        $this->setBody($body);

        if ($headers) {
            $this->setHeaders($headers);
        }

        if ($trailers !== null) {
            $this->setTrailers($trailers);
        }
    }

    public function __destruct()
    {
        foreach ($this->onDispose as $onDispose) {
            EventLoop::queue($onDispose);
        }
    }

    /**
     * Returns the stream for the message body.
     */
    public function getBody(): ReadableStream
    {
        return $this->body;
    }

    /**
     * Sets the stream for the message body. Note that using a string will automatically set the Content-Length header
     * to the length of the given string. Setting a stream will remove the Content-Length header.
     */
    public function setBody(ReadableStream|string $body): void
    {
        if ($body instanceof ReadableStream) {
            $this->body = $body;
            $this->removeHeader("content-length");
            return;
        }

        $this->body = new ReadableBuffer($body);
        $this->setHeader("content-length", (string) \strlen($body));
    }

    /**
     * Sets the headers from the given array. Any cookie headers will automatically populate the contained array of
     * ResponseCookie objects.
     *
     * @param HeaderParamArrayType $headers
     */
    #[\Override]
    public function setHeaders(array $headers): void
    {
        $cookies = $this->cookies;

        try {
            parent::setHeaders($headers);
        } catch (\Throwable $e) {
            $this->cookies = $cookies;

            throw $e;
        }
    }

    /**
     * Replaces headers from the given array. Any cookie headers will automatically populate the contained array of
     * ResponseCookie objects.
     *
     * @param HeaderParamArrayType $headers
     */
    #[\Override]
    public function replaceHeaders(array $headers): void
    {
        $cookies = $this->cookies;

        try {
            parent::replaceHeaders($headers);
        } catch (\Throwable $e) {
            $this->cookies = $cookies;

            throw $e;
        }
    }

    /**
     * Sets the named header to the given value.
     *
     * @param non-empty-string $name
     * @param HeaderParamValueType $value
     *
     * @throws \Error If the header name or value is invalid.
     */
    #[\Override]
    public function setHeader(string $name, array|string $value): void
    {
        if (($name[0] ?? ":") === ":") {
            throw new \Error("Header name cannot be empty or start with a colon (:)");
        }

        parent::setHeader($name, $value);

        if (\stripos($name, "set-cookie") === 0) {
            $this->setCookiesFromHeaders();
        }
    }

    /**
     * Adds the value to the named header, or creates the header with the given value if it did not exist.
     *
     * @param non-empty-string $name
     * @param HeaderParamValueType $value
     *
     * @throws \Error If the header name or value is invalid.
     */
    #[\Override]
    public function addHeader(string $name, array|string $value): void
    {
        if (($name[0] ?? ":") === ":") {
            throw new \Error("Header name cannot be empty or start with a colon (:)");
        }

        parent::addHeader($name, $value);

        if (\stripos($name, "set-cookie") === 0) {
            $this->setCookiesFromHeaders();
        }
    }

    /**
     * Removes the given header if it exists.
     */
    #[\Override]
    public function removeHeader(string $name): void
    {
        parent::removeHeader($name);

        if (\stripos($name, "set-cookie") === 0) {
            $this->cookies = [];
        }
    }

    /**
     * Sets the response status code and reason phrase. Use null for the reason phrase to use the default phrase
     * associated with the status code.
     *
     * @param int $status 100 - 599
     */
    #[\Override]
    public function setStatus(int $status, ?string $reason = null): void
    {
        parent::setStatus($this->validateStatusCode($status), $reason);

        if ($this->upgrade && $status !== HttpStatus::SWITCHING_PROTOCOLS) {
            $this->upgrade = null;
        }
    }

    /**
     * @return array<non-empty-string, ResponseCookie>
     */
    public function getCookies(): array
    {
        return $this->cookies;
    }

    /**
     * @param string $name Name of the cookie.
     */
    public function getCookie(string $name): ?ResponseCookie
    {
        return $this->cookies[$name] ?? null;
    }

    /**
     * Adds a cookie to the response.
     */
    public function setCookie(ResponseCookie $cookie): void
    {
        $this->cookies[$cookie->getName()] = $cookie;
        $this->setHeadersFromCookies();
    }

    /**
     * Removes a cookie from the response.
     */
    public function removeCookie(string $name): void
    {
        if (isset($this->cookies[$name])) {
            unset($this->cookies[$name]);
            $this->setHeadersFromCookies();
        }
    }

    private function validateStatusCode(int $status): int
    {
        if ($status < 100 || $status > 599) {
            throw new \ValueError(
                'Invalid status code. Must be an integer between 100 and 599, inclusive.'
            );
        }

        return $status;
    }

    /**
     * Sets cookies based on headers.
     *
     * @throws \Error
     */
    private function setCookiesFromHeaders(): void
    {
        $this->cookies = [];

        $headers = $this->getHeaderArray("set-cookie");

        foreach ($headers as $line) {
            $cookie = ResponseCookie::fromHeader($line);
            if ($cookie) {
                $this->cookies[$cookie->getName()] = $cookie;
            }
        }
    }

    /**
     * Sets headers based on cookie values.
     */
    private function setHeadersFromCookies(): void
    {
        $values = [];

        foreach ($this->cookies as $cookie) {
            $values[] = $cookie->toString();
        }

        $this->setHeader("set-cookie", $values);
    }

    /**
     * @return Trailers|null Trailers to be written with the response.
     */
    public function getTrailers(): ?Trailers
    {
        return $this->trailers;
    }

    public function setTrailers(Trailers $trailers): void
    {
        $this->trailers = $trailers;
    }

    /**
     * Removes any trailer headers from the response.
     */
    public function removeTrailers(): void
    {
        $this->trailers = null;
    }

    /**
     * @return array<string, Push>
     */
    public function getPushes(): array
    {
        return $this->push;
    }

    /**
     * Indicate resources which a client likely needs to fetch (e.g. Link: preload or HTTP/2 Server Push).
     *
     * @param string $url URL of resource to push to the client.
     * @param HeaderParamArrayType $headers Additional headers to attach to the request.
     *
     * @throws \Error If the given url is invalid.
     */
    public function push(string $url, array $headers = []): void
    {
        try {
            $uri = Uri\Http::new($url);
        } catch (\Exception $exception) {
            throw new \Error("Invalid push URI: " . $exception->getMessage(), 0, $exception);
        }

        $this->push[$url] = new Push($uri, $headers);
    }

    /**
     * @return bool True if an upgrade callback has been set, false if none.
     */
    public function isUpgraded(): bool
    {
        return $this->upgrade !== null;
    }

    /**
     * Sets a callback to be invoked once the response has been written to the client and changes the status of the
     * response to 101 (Switching Protocols) and removes any trailers. The callback may be removed by changing the
     * response status to any value other than 101.
     *
     * @param \Closure(Driver\UpgradedSocket, Request, Response):void $upgrade Callback invoked once the response has
     * been written to the client. The callback is given three parameters: an instance of {@see Driver\UpgradedSocket},
     * the original {@see Request} object, and this {@see Response} object.
     */
    public function upgrade(\Closure $upgrade): void
    {
        $this->upgrade = $upgrade;
        $this->setStatus(HttpStatus::SWITCHING_PROTOCOLS);

        $this->removeTrailers();
    }

    /**
     * Returns the upgrade function if present.
     *
     * @return \Closure|null Upgrade function.
     */
    public function getUpgradeHandler(): ?\Closure
    {
        return $this->upgrade;
    }

    /**
     * Registers a function that is invoked when the Response is discarded. A response is discarded either once it has
     * been written to the client or if it gets replaced in a middleware chain.
     *
     * @param \Closure():void $onDispose
     */
    public function onDispose(\Closure $onDispose): void
    {
        $this->onDispose[] = $onDispose;
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Server;

use Amp\Http\Server\Middleware\ExceptionHandlerMiddleware;

interface ExceptionHandler
{
    /**
     * Handles an uncaught exception from the {@see RequestHandler} wrapped with {@see ExceptionHandlerMiddleware}.
     */
    public function handleException(Request $request, \Throwable $exception): Response;
}
<?php declare(strict_types=1);

namespace Amp\Http\Server;

use Amp\Cancellation;
use Amp\Future;
use Amp\Http\HttpMessage;
use Amp\Http\InvalidHeaderException;
use function Amp\async;

final class Trailers
{
    /** @see https://tools.ietf.org/html/rfc7230#section-4.1.2 */
    public const DISALLOWED_TRAILERS = [
        "authorization" => true,
        "content-encoding" => true,
        "content-length" => true,
        "content-range" => true,
        "content-type" => true,
        "cookie" => true,
        "expect" => true,
        "host" => true,
        "pragma" => true,
        "proxy-authenticate" => true,
        "proxy-authorization" => true,
        "range" => true,
        "te" => true,
        "trailer" => true,
        "transfer-encoding" => true,
        "www-authenticate" => true,
    ];

    /** @var list<string> */
    private readonly array $fields;

    /** @var Future<HttpMessage> */
    private readonly Future $messageFuture;

    /**
     * @param Future<array<non-empty-string, string|array<string>>> $future Resolved with the trailer values.
     * @param string[] $fields Expected header fields. May be empty, but if provided, the array of
     *     headers used to complete the given future must contain exactly the fields given in this array.
     *
     * @throws InvalidHeaderException If the fields list contains a disallowed field.
     */
    public function __construct(Future $future, array $fields = [])
    {
        $this->fields = $fields = \array_map('strtolower', \array_values($fields));

        foreach ($this->fields as $field) {
            if (isset(self::DISALLOWED_TRAILERS[$field])) {
                throw new InvalidHeaderException(\sprintf("Field '%s' is not allowed in trailers", $field));
            }
        }

        $this->messageFuture = async(static function () use ($future, $fields): HttpMessage {
            return new class($future->await(), $fields) extends HttpMessage {
                public function __construct(array $headers, array $fields)
                {
                    $this->setHeaders($headers);

                    $keys = \array_keys($this->getHeaders());

                    if (!empty($fields)) {
                        // Note that the Trailer header does not need to be set for the message to include trailers.
                        // @see https://tools.ietf.org/html/rfc7230#section-4.4

                        if (\array_diff($fields, $keys)) {
                            throw new InvalidHeaderException("Trailers do not contain the expected fields");
                        }

                        return; // Check below unnecessary if fields list is set.
                    }

                    foreach ($keys as $field) {
                        if (isset(Trailers::DISALLOWED_TRAILERS[$field])) {
                            throw new InvalidHeaderException(\sprintf("Field '%s' is not allowed in trailers", $field));
                        }
                    }
                }
            };
        });

        // Future may fail due to client disconnect or error, but we don't want to force awaiting.
        $this->messageFuture->ignore();
    }

    /**
     * @return list<string> List of expected trailer fields. May be empty, but still receive trailers.
     */
    public function getFields(): array
    {
        return $this->fields;
    }

    public function await(?Cancellation $cancellation = null): HttpMessage
    {
        return $this->messageFuture->await($cancellation);
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Server\Driver;

use Amp\CancelledException;
use Amp\Socket\Socket;
use Amp\Socket\SocketException;
use Amp\TimeoutCancellation;
use Psr\Log\LoggerInterface as PsrLogger;

final class SocketClientFactory implements ClientFactory
{
    public function __construct(
        private readonly PsrLogger $logger,
        private readonly float $tlsHandshakeTimeout = 5,
    ) {
    }

    #[\Override]
    public function createClient(Socket $socket): ?Client
    {
        $local = $socket->getLocalAddress()->toString();
        $remote = $socket->getRemoteAddress()->toString();

        /** @psalm-suppress RedundantCondition */
        \assert($this->logger->debug(\sprintf("Accepted %s on %s", $remote, $local)) || true);

        if ($socket->isTlsConfigurationAvailable()) {
            try {
                $socket->setupTls(new TimeoutCancellation($this->tlsHandshakeTimeout));

                /** @psalm-suppress RedundantCondition, PossiblyNullReference */
                \assert($this->logger->debug(\sprintf(
                    "TLS negotiated with %s (%s with %s, application protocol: %s)",
                    $remote,
                    $socket->getTlsInfo()->getVersion(),
                    $socket->getTlsInfo()->getCipherName(),
                    $socket->getTlsInfo()->getApplicationLayerProtocol() ?? "none",
                )) || true);
            } catch (SocketException $exception) {
                $message = $exception->getMessage();
                $this->logger->warning(
                    \sprintf("TLS negotiation failed for %s on %s: %s", $remote, $local, $message),
                    ['local' => $local, 'remote' => $remote, 'message' => $message],
                );

                return null;
            } catch (CancelledException) {
                $this->logger->warning(
                    \sprintf("TLS negotiation timed out for %s on %s", $remote, $local),
                    ['local' => $local, 'remote' => $remote],
                );

                return null;
            }
        }

        return new SocketClient($socket);
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Server\Driver;

use Amp\ByteStream\ReadableStream;
use Amp\ByteStream\ReadableStreamIteratorAggregate;
use Amp\ByteStream\ResourceStream;
use Amp\ByteStream\WritableStream;
use Amp\Cancellation;
use Amp\Socket\Socket;
use Amp\Socket\SocketAddress;
use Amp\Socket\TlsException;
use Amp\Socket\TlsInfo;
use Amp\Socket\TlsState;

/**
 * @implements \IteratorAggregate<int, string>
 */
final class UpgradedSocket implements Socket, ResourceStream, \IteratorAggregate
{
    use ReadableStreamIteratorAggregate;

    private string $readBuffer = '';

    public function __construct(
        private readonly Client $client,
        private readonly ReadableStream $readableStream,
        private readonly WritableStream $writableStream,
    ) {
    }

    public function getClient(): Client
    {
        return $this->client;
    }

    #[\Override]
    public function read(?Cancellation $cancellation = null, ?int $limit = null): ?string
    {
        if ($this->readBuffer !== '') {
            $buffer = $this->readBuffer;
            $this->readBuffer = '';

            if ($limit !== null && \strlen($buffer) > $limit) {
                $this->readBuffer = \substr($buffer, $limit);
                return \substr($buffer, 0, $limit);
            }

            return $buffer;
        }

        $buffer = $this->readableStream->read($cancellation);
        if ($buffer === null) {
            return null;
        }

        if ($limit !== null && \strlen($buffer) > $limit) {
            $this->readBuffer = \substr($buffer, $limit);
            return \substr($buffer, 0, $limit);
        }

        return $buffer;
    }

    #[\Override]
    public function close(): void
    {
        $this->readableStream->close();
        $this->writableStream->close();
        $this->client->close();
    }

    public function __destruct()
    {
        $this->close();
    }

    #[\Override]
    public function write(string $bytes): void
    {
        $this->writableStream->write($bytes);
    }

    #[\Override]
    public function end(): void
    {
        $this->writableStream->end();
    }

    #[\Override]
    public function reference(): void
    {
        if ($this->writableStream instanceof ResourceStream) {
            $this->writableStream->reference();
        }
    }

    #[\Override]
    public function unreference(): void
    {
        if ($this->writableStream instanceof ResourceStream) {
            $this->writableStream->unreference();
        }
    }

    #[\Override]
    public function getLocalAddress(): SocketAddress
    {
        return $this->client->getLocalAddress();
    }

    #[\Override]
    public function getRemoteAddress(): SocketAddress
    {
        return $this->client->getRemoteAddress();
    }

    #[\Override]
    public function isClosed(): bool
    {
        return !$this->isReadable() && !$this->isWritable();
    }

    #[\Override]
    public function onClose(\Closure $onClose): void
    {
        $this->readableStream->onClose($onClose);
    }

    #[\Override]
    public function isReadable(): bool
    {
        return $this->readBuffer !== '' || $this->readableStream->isReadable();
    }

    #[\Override]
    public function isWritable(): bool
    {
        return $this->writableStream->isWritable();
    }

    #[\Override]
    public function getResource()
    {
        return $this->writableStream instanceof ResourceStream
            ? $this->writableStream->getResource()
            : null;
    }

    #[\Override]
    public function setupTls(?Cancellation $cancellation = null): never
    {
        throw new TlsException('Not implemented on upgraded sockets; TLS should already be enabled if available');
    }

    #[\Override]
    public function shutdownTls(?Cancellation $cancellation = null): never
    {
        throw new TlsException('Not implemented on upgraded sockets');
    }

    #[\Override]
    public function isTlsConfigurationAvailable(): bool
    {
        return $this->writableStream instanceof Socket
            ? $this->writableStream->isTlsConfigurationAvailable()
            : false;
    }

    #[\Override]
    public function getTlsState(): TlsState
    {
        return $this->writableStream instanceof Socket
            ? $this->writableStream->getTlsState()
            : TlsState::Disabled;
    }

    #[\Override]
    public function getTlsInfo(): ?TlsInfo
    {
        return $this->writableStream instanceof Socket
            ? $this->writableStream->getTlsInfo()
            : null;
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Server\Driver;

use Amp\ByteStream\ReadableIterableStream;
use Amp\ByteStream\ReadableStream;
use Amp\ByteStream\StreamException;
use Amp\ByteStream\WritableStream;
use Amp\Cancellation;
use Amp\CancelledException;
use Amp\DeferredFuture;
use Amp\Future;
use Amp\Http\HPack;
use Amp\Http\Http2\Http2ConnectionException;
use Amp\Http\Http2\Http2Parser;
use Amp\Http\Http2\Http2Processor;
use Amp\Http\Http2\Http2StreamException;
use Amp\Http\HttpStatus;
use Amp\Http\InvalidHeaderException;
use Amp\Http\Server\ClientException;
use Amp\Http\Server\Driver\Internal\AbstractHttpDriver;
use Amp\Http\Server\Driver\Internal\Http2Stream;
use Amp\Http\Server\Driver\Internal\StreamTimeoutTracker;
use Amp\Http\Server\ErrorHandler;
use Amp\Http\Server\Push;
use Amp\Http\Server\Request;
use Amp\Http\Server\RequestBody;
use Amp\Http\Server\RequestHandler;
use Amp\Http\Server\Response;
use Amp\Http\Server\Trailers;
use Amp\Pipeline\Queue;
use Amp\Socket\InternetAddress;
use League\Uri;
use Psr\Log\LoggerInterface as PsrLogger;
use Revolt\EventLoop;
use function Amp\async;
use function Amp\Http\formatDateHeader;

final class Http2Driver extends AbstractHttpDriver implements Http2Processor
{
    public const DEFAULT_CONCURRENT_STREAM_LIMIT = 100;

    /** Stream behavior window interval in seconds. */
    private const STREAM_BEHAVIOR_WINDOW = 10;
    /** Ratio of normally released streams to exceptionally released which must be exceeded to automatically
     * disconnect the client.*/
    private const RESET_STREAM_RATIO = 0.25;
    /** Number of streams released within the behavior window for the reset ratio to be applicable. */
    private const STREAM_BEHAVIOR_THRESHOLD = 10;

    public const DEFAULT_MAX_FRAME_SIZE = 1 << 14;
    public const DEFAULT_WINDOW_SIZE = (1 << 16) - 1;

    private const MINIMUM_WINDOW = (1 << 15) - 1;
    private const MAX_INCREMENT = (1 << 16) - 1;

    // Headers to take over from original request if present
    private const PUSH_PROMISE_INTERSECT = [
        "accept" => true,
        "accept-charset" => true,
        "accept-encoding" => true,
        "accept-language" => true,
        "authorization" => true,
        "cache-control" => true,
        "cookie" => true,
        "date" => true,
        "host" => true,
        "user-agent" => true,
        "via" => true,
    ];

    private Client $client;
    private ReadableStream $readableStream;
    private WritableStream $writableStream;

    private StreamTimeoutTracker $timeoutTracker;

    private int $serverWindow = self::DEFAULT_WINDOW_SIZE;

    private int $clientWindow = self::DEFAULT_WINDOW_SIZE;

    private int $initialWindowSize = self::DEFAULT_WINDOW_SIZE;

    /** @var positive-int */
    private int $maxFrameSize = self::DEFAULT_MAX_FRAME_SIZE;

    private bool $allowsPush;

    /** @var non-negative-int Last used local stream ID. */
    private int $localStreamId = 0;

    /** @var non-negative-int Last used remote stream ID. */
    private int $remoteStreamId = 0;

    /** @var array<int, Http2Stream> */
    private array $streams = [];

    /** @var \WeakMap<Request, int> Map of Request objects to stream IDs. */
    private \WeakMap $streamIdMap;

    /** @var array<int, int> Release timestamps of the last {@see STREAM_BEHAVIOR_WINDOW} released streams. */
    private array $releasedStreams = [];

    /** @var array<int, int> Release timestamps of the last {@see STREAM_BEHAVIOR_WINDOW} streams released due to
     *      an exception (such as the client resetting the stream or sending an invalid frame).
     */
    private array $exceptionalStreams = [];

    /** @var array<string, int> Map of URLs pushed on this connection. */
    private array $pushCache = [];

    /** @var array<int, DeferredFuture> */
    private array $trailerDeferreds = [];

    /** @var array<int, Queue> */
    private array $bodyQueues = [];

    /** @var int Number of streams that may be opened. */
    private int $remainingStreams;

    private bool $stopping = false;

    private int $pinged = 0;

    private readonly HPack $hpack;

    public function __construct(
        RequestHandler $requestHandler,
        ErrorHandler $errorHandler,
        PsrLogger $logger,
        private readonly int $streamTimeout = self::DEFAULT_STREAM_TIMEOUT,
        private readonly int $connectionTimeout = self::DEFAULT_CONNECTION_TIMEOUT,
        private readonly int $headerSizeLimit = self::DEFAULT_HEADER_SIZE_LIMIT,
        private readonly int $bodySizeLimit = self::DEFAULT_BODY_SIZE_LIMIT,
        private readonly int $concurrentStreamLimit = self::DEFAULT_CONCURRENT_STREAM_LIMIT,
        private readonly bool $pushEnabled = true,
        private readonly ?string $settings = null,
    ) {
        parent::__construct($requestHandler, $errorHandler, $logger);

        $this->remainingStreams = $concurrentStreamLimit;
        $this->allowsPush = $pushEnabled;

        $this->hpack = new HPack();

        /** @var \WeakMap<Request, int> */
        $this->streamIdMap = new \WeakMap();
    }

    #[\Override]
    public function handleClient(
        Client $client,
        ReadableStream $readableStream,
        WritableStream $writableStream,
    ): void {
        /** @psalm-suppress RedundantPropertyInitializationCheck */
        \assert(!isset($this->client), "The driver has already been setup");

        $this->client = $client;
        $this->readableStream = $readableStream;
        $this->writableStream = $writableStream;

        $this->timeoutTracker = new StreamTimeoutTracker(
            $this->client,
            self::getTimeoutQueue(),
            $this->connectionTimeout,
            $this->streamTimeout,
            fn () => $this->shutdown(new ClientException($this->client, 'Shutting down connection due to inactivity')),
        );

        $this->processClientInput();
    }

    /**
     * Provide separate functions for Http2Driver initialization:
     * The Http1Driver may still be in process of reading a possible request body.
     * As we want to be able to already start sending HTTP/2 frames before the whole request body has been read,
     * we need to initialize writing early. Hence, we need a separate function for starting reading on the stream.
     */
    public function initializeWriting(
        Client $client,
        WritableStream $writableStream,
    ): void {
        /** @psalm-suppress RedundantPropertyInitializationCheck */
        \assert(!isset($this->client), "The driver has already been setup");

        $this->client = $client;
        $this->writableStream = $writableStream;

        $this->timeoutTracker = new StreamTimeoutTracker(
            $this->client,
            self::getTimeoutQueue(),
            $this->connectionTimeout,
            $this->streamTimeout,
            fn () => $this->shutdown(new ClientException($this->client, 'Shutting down connection due to inactivity')),
        );

        if ($this->settings !== null) {
            // Upgraded connections automatically assume an initial stream with ID 1.
            // No data will be incoming on this stream, so body size of 0.
            $this->createStream(1, 0, Http2Stream::RESERVED | Http2Stream::REMOTE_CLOSED);
            $this->remoteStreamId = \max(1, $this->remoteStreamId);
            $this->remainingStreams--;

            // Initial settings frame, sent immediately for upgraded connections.
            $this->writeFrame(
                \pack(
                    "nNnNnNnN",
                    Http2Parser::INITIAL_WINDOW_SIZE,
                    self::DEFAULT_WINDOW_SIZE,
                    Http2Parser::MAX_CONCURRENT_STREAMS,
                    $this->concurrentStreamLimit,
                    Http2Parser::MAX_HEADER_LIST_SIZE,
                    $this->headerSizeLimit,
                    Http2Parser::MAX_FRAME_SIZE,
                    self::DEFAULT_MAX_FRAME_SIZE
                ),
                Http2Parser::SETTINGS,
                Http2Parser::NO_FLAG
            );
        }
    }

    public function handleClientWithBuffer(string $buffer, ReadableStream $readableStream): void
    {
        /** @psalm-suppress RedundantPropertyInitializationCheck */
        \assert(isset($this->client), "The driver has not been setup");

        $this->readableStream = $readableStream;

        $this->processClientInput($buffer);
    }

    private function processClientInput(?string $chunk = null): void
    {
        /** @psalm-suppress RedundantCondition */
        \assert($this->logger->debug(\sprintf(
            "Handling requests from %s #%d using HTTP/2 driver",
            $this->client->getRemoteAddress()->toString(),
            $this->client->getId(),
        )) || true);

        $parser = new Http2Parser($this, $this->hpack, $this->settings);

        try {
            $parser->push($chunk ?? $this->readPreface());

            while (null !== $chunk = $this->readableStream->read()) {
                $parser->push($chunk);
            }

            $this->shutdown();
        } catch (StreamException|Http2ConnectionException $exception) {
            $this->shutdown(new ClientException(
                $this->client,
                "Exception thrown when reading client input: " . $exception->getMessage(),
                $exception->getCode(),
                $exception,
            ));
        } finally {
            $parser->cancel();
        }
    }

    #[\Override]
    protected function write(Request $request, Response $response): void
    {
        /** @psalm-suppress RedundantPropertyInitializationCheck */
        \assert(isset($this->client), "The driver has not been setup");

        $streamId = $this->streamIdMap[$request] ?? 1; // Default ID of 1 for upgrade requests.

        /** @psalm-suppress RedundantCondition */
        \assert((bool) $streamId); // For Psalm.

        if (!isset($this->streams[$streamId])) {
            return; // Client closed the stream or connection.
        }

        $stream = $this->streams[$streamId];

        if ($streamId & 1) {
            $this->timeoutTracker->update($streamId);
        }

        $deferred = new DeferredFuture;
        $stream->pendingWrite = $deferred->getFuture();
        $cancellation = $stream->deferredCancellation->getCancellation();

        try {
            $this->send($streamId, $response, $request, $cancellation);
        } finally {
            $deferred->complete();
        }
    }

    #[\Override]
    public function stop(): void
    {
        $this->shutdown();
    }

    #[\Override]
    public function getPendingRequestCount(): int
    {
        return \count($this->bodyQueues);
    }

    private function send(int $id, Response $response, Request $request, Cancellation $cancellation): void
    {
        $chunk = ""; // Required for the finally, not directly overwritten, even if your IDE says otherwise.

        $need = $response->getHeader("content-length");
        $wrote = 0;

        try {
            $status = $response->getStatus();

            if ($status < HttpStatus::OK) {
                $response->setStatus(HttpStatus::HTTP_VERSION_NOT_SUPPORTED);
                throw new ClientException(
                    $this->client,
                    "1xx response codes are not supported in HTTP/2",
                    Http2Parser::HTTP_1_1_REQUIRED
                );
            }

            if ($status === HttpStatus::HTTP_VERSION_NOT_SUPPORTED && $response->getHeader("upgrade")) {
                throw new ClientException(
                    $this->client,
                    "Upgrade requests require HTTP/1.1",
                    Http2Parser::HTTP_1_1_REQUIRED
                );
            }

            $headers = [
                ':status' => [$status],
                ...$response->getHeaders(),
                'date' => [formatDateHeader()],
            ];

            // Remove headers that are obsolete in HTTP/2.
            unset($headers["connection"], $headers["keep-alive"], $headers["transfer-encoding"]);

            $trailers = $response->getTrailers();

            if ($trailers !== null && !isset($headers["trailer"]) && ($fields = $trailers->getFields())) {
                $headers["trailer"] = [\implode(", ", $fields)];
            }

            foreach ($response->getPushes() as $push) {
                $headers["link"][] = "<{$push->getUri()}>; rel=preload";
                if ($this->allowsPush) {
                    $this->sendPushPromise($request, $id, $push);
                }
            }

            $this->writeHeaders($this->encodeHeaders($headers), Http2Parser::HEADERS, 0, $id);

            if ($request->getMethod() === "HEAD") {
                $this->streams[$id]->state |= Http2Stream::LOCAL_CLOSED;
                $this->writeData("", $id);

                return;
            }

            $body = $response->getBody();
            $chunk = $body->read($cancellation);

            while ($chunk !== null) {
                // Stream may have been closed while waiting for body data.
                if (!isset($this->streams[$id])) {
                    return;
                }
                $wrote += \strlen($chunk);

                $this->writeData($chunk, $id);

                $chunk = $body->read($cancellation);
            }

            // Stream may have been closed while waiting for body data.
            if (!isset($this->streams[$id])) {
                return;
            }

            $this->streams[$id]->state |= Http2Stream::LOCAL_CLOSED;

            if ($trailers === null) {
                $this->writeData("", $id);
            } else {
                $trailers = $trailers->await($cancellation);

                // Stream may have been closed while writing final body chunk or headers.
                if (!isset($this->streams[$id])) {
                    return;
                }

                $this->writeHeaders(
                    $this->encodeHeaders($trailers->getHeaders()),
                    Http2Parser::HEADERS,
                    Http2Parser::END_STREAM,
                    $id,
                );
            }
        } catch (ClientException $exception) {
            $error = $exception->getCode() ?? Http2Parser::CANCEL; // Set error code to be used below.
        } catch (StreamException|CancelledException) {
            // Body stream threw or client disconnected, ignore and proceed to clean up below.
            $chunk = null;
        } catch (\Throwable $throwable) {
            // Will be rethrown after cleanup below.
        }

        // Cleanup outside finally block since the fiber may suspend to write RST_STREAM frame.
        try {
            /** @psalm-suppress ParadoxicalCondition Stream may be unset while awaiting above */
            if (!isset($this->streams[$id])) {
                return;
            }

            if ($chunk !== null || ($need !== null && $wrote !== (int) $need)) {
                $error ??= Http2Parser::INTERNAL_ERROR;
                $this->writeFrame(\pack("N", $error), Http2Parser::RST_STREAM, Http2Parser::NO_FLAG, $id);
                $this->releaseStream($id, $exception ?? new ClientException($this->client, "Stream error", $error));
                return;
            }

            if ($this->streams[$id]->state & Http2Stream::REMOTE_CLOSED) {
                $this->releaseStream($id);
            }
        } finally {
            if (isset($throwable)) {
                throw $throwable;
            }
        }
    }

    private function shutdown(?ClientException $reason = null): void
    {
        if ($this->stopping) {
            return;
        }

        $this->stopping = true;

        $previous = $reason?->getPrevious();
        $previous = $previous instanceof Http2ConnectionException ? $previous : null;

        $code = $previous?->getCode() ?? Http2Parser::GRACEFUL_SHUTDOWN;

        try {
            $futures = [];
            foreach ($this->streams as $id => $stream) {
                if ($id > $this->remoteStreamId) {
                    break;
                }

                if ($stream->pendingResponse) {
                    $futures[] = $stream->pendingResponse;
                }
            }

            $message = match ($code) {
                Http2Parser::PROTOCOL_ERROR,
                Http2Parser::FLOW_CONTROL_ERROR,
                Http2Parser::FRAME_SIZE_ERROR,
                Http2Parser::COMPRESSION_ERROR,
                Http2Parser::SETTINGS_TIMEOUT,
                Http2Parser::ENHANCE_YOUR_CALM => $previous?->getMessage(),
                default => null,
            };

            $this->writeFrame(
                \pack("NN", $this->remoteStreamId, $code) . $message,
                Http2Parser::GOAWAY,
                Http2Parser::NO_FLAG,
            );

            /** @psalm-suppress RedundantCondition */
            \assert($this->logger->debug(\sprintf(
                "Shutting down HTTP/2 client @ %s #%d; last-id: %d; reason: %s",
                $this->client->getRemoteAddress()->toString(),
                $this->client->getId(),
                $this->remoteStreamId,
                $reason?->getMessage() ?? "undefined",
            )) || true);

            Future\await($futures);

            $futures = [];
            foreach ($this->streams as $id => $stream) {
                if ($id > $this->remoteStreamId) {
                    break;
                }

                if ($stream->pendingWrite) {
                    $futures[] = $stream->pendingWrite;
                }
            }

            Future\await($futures);
        } catch (StreamException) {
            // ignore if no longer writable
        } finally {
            if (!empty($this->streams)) {
                $reason ??= new ClientException($this->client, "Connection closed unexpectedly", Http2Parser::CANCEL);
                foreach ($this->streams as $id => $_stream) {
                    $this->releaseStream($id, $reason);
                }
            }

            $this->client->close();
            $this->readableStream->close();
            $this->writableStream->close();
        }
    }

    private function sendPushPromise(Request $request, int $streamId, Push $push): void
    {
        $requestUri = $request->getUri();
        $pushUri = $push->getUri();
        $path = $pushUri->getPath();

        if (($path[0] ?? "/") !== "/") { // Relative Path
            $pushUri = $requestUri // Base push URI from original request URI.
                ->withPath($requestUri->getPath() . "/" . $path)
                ->withQuery($pushUri->getQuery());
        }

        if ($pushUri->getAuthority() === '') {
            $pushUri = $pushUri // If push URI did not provide a host, use original request URI.
                ->withHost($requestUri->getHost())
                ->withPort($requestUri->getPort());
        }

        $url = (string) $pushUri;

        if (isset($this->pushCache[$url])) {
            return; // Resource already pushed to this client.
        }

        $this->pushCache[$url] = $streamId;

        $path = $pushUri->getPath();
        if ($query = $pushUri->getQuery()) {
            $path .= "?" . $query;
        }

        $headers = [
            ...\array_intersect_key($request->getHeaders(), self::PUSH_PROMISE_INTERSECT), // Uses only select headers
            ...$push->getHeaders() // Overwrites request headers with those defined in push.
        ];

        // $id is the new stream ID for the pushed response, $streamId is the original request stream ID.
        $id = $this->localStreamId += 2; // Server initiated stream IDs must be even.

        $request = new Request($this->client, "GET", $pushUri, $headers, "", "2");

        // No data will be incoming on this stream.
        $stream = $this->createStream($id, 0, Http2Stream::RESERVED | Http2Stream::REMOTE_CLOSED);

        $this->streamIdMap[$request] = $id;

        $headers = [
            ":authority" => [$pushUri->getAuthority()],
            ":scheme" => [$pushUri->getScheme()],
            ":path" => [$path],
            ":method" => ["GET"],
            ...$headers,
        ];

        $this->writeHeaders(
            \pack("N", $id) . $this->encodeHeaders($headers),
            Http2Parser::PUSH_PROMISE,
            0,
            $streamId
        );

        $stream->pendingResponse = async($this->handleRequest(...), $request);
    }

    private function writeFrame(string $data, int $type, int $flags, int $stream = 0): void
    {
        $this->writableStream->write(Http2Parser::compileFrame($data, $type, $flags, $stream));
    }

    private function writeData(string $data, int $id): void
    {
        \assert(isset($this->streams[$id]), "The stream was closed");

        $this->streams[$id]->buffer .= $data;

        $this->writeBufferedData($id);
    }

    private function writeBufferedData(int $streamId): void
    {
        \assert(isset($this->streams[$streamId]), "The stream was closed");

        $stream = $this->streams[$streamId];
        $delta = \min($this->clientWindow, $stream->clientWindow);
        $length = \strlen($stream->buffer);

        if ($streamId & 1) {
            $this->timeoutTracker->update($streamId);
        }

        // Stream state must be updated before writing, since a write may suspend and let another
        // fiber send the same buffered data.
        if ($delta >= $length) {
            $data = $stream->buffer;
            $stream->buffer = "";
            $stream->clientWindow -= $length;
            $this->clientWindow -= $length;

            $this->writeDataFrames(
                $data,
                $stream->state & Http2Stream::LOCAL_CLOSED ? Http2Parser::END_STREAM : Http2Parser::NO_FLAG,
                $streamId,
            );

            if ($stream->deferredFuture) {
                $stream->deferredFuture->complete();
                $stream->deferredFuture = null;
            }

            return;
        }

        if ($delta > 0) {
            $data = \substr($stream->buffer, 0, $delta);
            $stream->buffer = \substr($stream->buffer, $delta);
            $stream->clientWindow -= $delta;
            $this->clientWindow -= $delta;

            $this->writeDataFrames($data, Http2Parser::NO_FLAG, $streamId);
        }

        $stream->deferredFuture ??= new DeferredFuture;
        $stream->deferredFuture->getFuture()->await();
    }

    /**
     * Writes all frames at once, so frames of concurrent writers cannot interleave.
     */
    private function writeDataFrames(string $data, int $flags, int $streamId): void
    {
        $frames = "";
        $length = \strlen($data);

        for ($off = 0; $length - $off > $this->maxFrameSize; $off += $this->maxFrameSize) {
            $frames .= Http2Parser::compileFrame(
                \substr($data, $off, $this->maxFrameSize),
                Http2Parser::DATA,
                Http2Parser::NO_FLAG,
                $streamId
            );
        }

        $frames .= Http2Parser::compileFrame(\substr($data, $off), Http2Parser::DATA, $flags, $streamId);

        $this->writableStream->write($frames);
    }

    private function writeHeaders(string $headers, int $type, int $flags, int $id): void
    {
        $flags |= Http2Parser::END_HEADERS;

        if (\strlen($headers) > $this->maxFrameSize) {
            // Header frames must be sent as one contiguous block without frames from any other stream being
            // interleaved between due to HPack. See https://datatracker.ietf.org/doc/html/rfc7540#section-4.3
            $split = \str_split($headers, $this->maxFrameSize);
            $headers = \array_pop($split);

            $writeFrame = $this->writeFrame(...);
            foreach ($split as $part) {
                async($writeFrame, $part, $type, Http2Parser::NO_FLAG, $id)->ignore();
                $type = Http2Parser::CONTINUATION;
            }
            async($writeFrame, $headers, $type, $flags, $id)->await();

            return;
        }

        $this->writeFrame($headers, $type, $flags, $id);
    }

    private function createStream(int $id, int $bodySizeLimit, int $flags = Http2Stream::OPEN): Http2Stream
    {
        \assert(!isset($this->streams[$id]));

        if ($id & 1) {
            $this->timeoutTracker->insert(
                $id,
                fn (int $id) => $this->releaseStream(
                    $id,
                    new ClientException($this->client, "Closing stream due to inactivity"),
                ),
            );
        }

        // The receive window starts at what this server advertised in its SETTINGS frame;
        // the client's SETTINGS_INITIAL_WINDOW_SIZE only governs how much the server may send.
        return $this->streams[$id] = new Http2Stream(
            $bodySizeLimit,
            self::DEFAULT_WINDOW_SIZE,
            $this->initialWindowSize,
            $flags,
        );
    }

    private function releaseStream(int $id, ?ClientException $exception = null): void
    {
        \assert(isset($this->streams[$id]), "Tried to release a non-existent stream");

        $exceptional = $exception !== null;

        $this->streams[$id]->deferredCancellation->cancel();

        $clientInitiated = $id & 1;

        if ($clientInitiated) {
            $this->timeoutTracker->remove($id);
        }

        ($this->bodyQueues[$id] ?? null)?->error(
            $exception ??= new ClientException($this->client, "Client disconnected", Http2Parser::CANCEL)
        );

        ($this->trailerDeferreds[$id] ?? null)?->error(
            $exception ?? new ClientException($this->client, "Client disconnected", Http2Parser::CANCEL)
        );

        unset($this->streams[$id], $this->bodyQueues[$id], $this->trailerDeferreds[$id]);

        if (!$clientInitiated) {
            return; // Additional checks unnecessary for server-initiated streams.
        }

        $this->remainingStreams++;

        $now = \time();
        $filterCallback = fn (int $releasedAt) => $releasedAt > $now - self::STREAM_BEHAVIOR_WINDOW;
        $this->releasedStreams = \array_filter($this->releasedStreams, $filterCallback);
        $this->releasedStreams[$id] = $now;

        if ($exceptional) {
            $this->exceptionalStreams = \array_filter($this->exceptionalStreams, $filterCallback);
            $this->exceptionalStreams[$id] = $now;

            $releasedStreamCount = \count($this->releasedStreams);
            $exceptionalStreamCount = \count($this->exceptionalStreams);

            if ($releasedStreamCount >= self::STREAM_BEHAVIOR_THRESHOLD
                && $exceptionalStreamCount / $releasedStreamCount > self::RESET_STREAM_RATIO
            ) {
                $this->handleConnectionException(new Http2ConnectionException(
                    'Too many reset streams',
                    Http2Parser::ENHANCE_YOUR_CALM,
                ));
            }
        }
    }

    private function readPreface(): string
    {
        $buffer = $this->readableStream->read();
        if ($buffer === null) {
            throw new Http2ConnectionException("Invalid preface", Http2Parser::PROTOCOL_ERROR);
        }

        while (\strlen($buffer) < \strlen(Http2Parser::PREFACE)) {
            $chunk = $this->readableStream->read();
            if ($chunk === null) {
                throw new Http2ConnectionException("Invalid preface", Http2Parser::PROTOCOL_ERROR);
            }

            $buffer .= $chunk;
        }

        if (!\str_starts_with($buffer, Http2Parser::PREFACE)) {
            throw new Http2ConnectionException("Invalid preface", Http2Parser::PROTOCOL_ERROR);
        }

        $buffer = \substr($buffer, \strlen(Http2Parser::PREFACE));

        if ($this->settings === null) {
            // Initial settings frame, delayed until after the preface is read for non-upgraded connections.
            $this->writeFrame(
                \pack(
                    "nNnNnNnN",
                    Http2Parser::INITIAL_WINDOW_SIZE,
                    self::DEFAULT_WINDOW_SIZE,
                    Http2Parser::MAX_CONCURRENT_STREAMS,
                    $this->concurrentStreamLimit,
                    Http2Parser::MAX_HEADER_LIST_SIZE,
                    $this->headerSizeLimit,
                    Http2Parser::MAX_FRAME_SIZE,
                    self::DEFAULT_MAX_FRAME_SIZE
                ),
                Http2Parser::SETTINGS,
                Http2Parser::NO_FLAG
            );
        }

        return $buffer;
    }

    private function sendBufferedData(): void
    {
        foreach ($this->streams as $id => $stream) {
            if ($this->clientWindow <= 0) {
                return;
            }

            if ($stream->buffer === '' || $stream->clientWindow <= 0) {
                continue;
            }

            try {
                $this->writeBufferedData($id);
            } catch (StreamException) {
                return; // Socket closed while writing buffered data.
            }
        }
    }

    private function encodeHeaders(array $headers): string
    {
        $input = [];

        foreach ($headers as $field => $values) {
            $values = (array) $values;

            foreach ($values as $value) {
                $input[] = [(string) $field, (string) $value];
            }
        }

        return $this->hpack->encode($input);
    }

    #[\Override]
    public function handlePong(string $data): void
    {
        // Ignored
    }

    #[\Override]
    public function handlePing(string $data): void
    {
        if (!$this->pinged) {
            // Ensure there are a few extra seconds for request after first ping.
            $this->timeoutTracker->ping(5);
        }

        $this->pinged++;

        if ($this->pinged > 5) {
            $this->handleConnectionException(
                new Http2ConnectionException('Too many pings', Http2Parser::ENHANCE_YOUR_CALM)
            );
        } else {
            $this->writeFrame($data, Http2Parser::PING, Http2Parser::ACK);
        }
    }

    #[\Override]
    public function handleShutdown(int $lastId, int $error, string $message): void
    {
        $message = \sprintf(
            "Received GOAWAY frame from %s with error code %d and message '%s'",
            $this->client->getRemoteAddress()->toString(),
            $error,
            $message,
        );

        if ($error !== Http2Parser::GRACEFUL_SHUTDOWN) {
            $this->logger->notice($message);
        }

        $this->shutdown(new ClientException(
            $this->client,
            "Client closed HTTP/2 connection",
            $error,
            new Http2ConnectionException($message, $error),
        ));
    }

    #[\Override]
    public function handleStreamWindowIncrement(int $streamId, int $windowSize): void
    {
        if ($streamId > $this->remoteStreamId) {
            throw new Http2ConnectionException(
                "Stream ID does not exist",
                Http2Parser::PROTOCOL_ERROR
            );
        }

        if (!isset($this->streams[$streamId])) {
            return;
        }

        $stream = $this->streams[$streamId];

        if ($stream->clientWindow + $windowSize > 2147483647) {
            throw new Http2StreamException(
                "Current window size plus new window exceeds maximum size",
                $streamId,
                Http2Parser::FLOW_CONTROL_ERROR
            );
        }

        $stream->clientWindow += $windowSize;

        EventLoop::defer($this->sendBufferedData(...));
    }

    #[\Override]
    public function handleConnectionWindowIncrement(int $windowSize): void
    {
        if ($this->clientWindow + $windowSize > 2147483647) {
            throw new Http2ConnectionException(
                "Current window size plus new window exceeds maximum size",
                Http2Parser::FLOW_CONTROL_ERROR
            );
        }

        $this->clientWindow += $windowSize;

        EventLoop::defer($this->sendBufferedData(...));
    }

    #[\Override]
    public function handleHeaders(int $streamId, array $pseudo, array $headers, bool $streamEnded): void
    {
        foreach ($pseudo as $name => $_value) {
            if (!isset(Http2Parser::KNOWN_REQUEST_PSEUDO_HEADERS[$name])) {
                throw new Http2StreamException(
                    "Invalid pseudo header",
                    $streamId,
                    Http2Parser::PROTOCOL_ERROR
                );
            }
        }

        if (isset($this->streams[$streamId])) {
            $stream = $this->streams[$streamId];

            if ($stream->state & Http2Stream::REMOTE_CLOSED) {
                throw new Http2StreamException(
                    "Stream remote closed",
                    $streamId,
                    Http2Parser::STREAM_CLOSED
                );
            }
        } else {
            if (!($streamId & 1) || $streamId <= $this->remoteStreamId) {
                throw new Http2ConnectionException(
                    "Invalid stream ID",
                    Http2Parser::PROTOCOL_ERROR
                );
            }

            if ($this->remainingStreams-- <= 0) {
                throw new Http2ConnectionException(
                    "Concurrent stream limit exceeded",
                    Http2Parser::PROTOCOL_ERROR
                );
            }

            $stream = $this->createStream($streamId, $this->bodySizeLimit);
        }

        // Header frames can be received on previously opened streams (trailer headers).
        $this->remoteStreamId = \max($streamId, $this->remoteStreamId);

        if (isset($this->trailerDeferreds[$streamId]) && $stream->state & Http2Stream::RESERVED) {
            if (!$streamEnded) {
                throw new Http2ConnectionException(
                    "Trailers must end the stream",
                    Http2Parser::PROTOCOL_ERROR
                );
            }

            // Trailers must not contain pseudo-headers.
            if (!empty($pseudo)) {
                throw new Http2StreamException(
                    "Trailers must not contain pseudo headers",
                    $streamId,
                    Http2Parser::PROTOCOL_ERROR
                );
            }

            // Trailers must not contain any disallowed fields.
            if (\array_intersect_key($headers, Trailers::DISALLOWED_TRAILERS)) {
                throw new Http2StreamException(
                    "Disallowed trailer field name",
                    $streamId,
                    Http2Parser::PROTOCOL_ERROR
                );
            }

            $deferred = $this->trailerDeferreds[$streamId];
            $queue = $this->bodyQueues[$streamId];

            unset($this->bodyQueues[$streamId], $this->trailerDeferreds[$streamId]);

            $this->timeoutTracker->suspend($streamId);

            $queue->complete();
            $deferred->complete($headers);

            return;
        }

        $this->timeoutTracker->update($streamId);

        if ($stream->state & Http2Stream::RESERVED) {
            throw new Http2StreamException(
                "Stream already reserved",
                $streamId,
                Http2Parser::PROTOCOL_ERROR
            );
        }

        $stream->state |= Http2Stream::RESERVED;

        if ($this->stopping) {
            throw new Http2StreamException("Shutting down", $streamId, Http2Parser::REFUSED_STREAM);
        }

        if (!isset($pseudo[":method"], $pseudo[":path"], $pseudo[":scheme"], $pseudo[":authority"])
            || isset($headers["connection"])
            || $pseudo[":path"] === ''
            || (isset($headers["te"]) && \implode($headers["te"]) !== "trailers")
        ) {
            throw new Http2StreamException(
                "Invalid header values",
                $streamId,
                Http2Parser::PROTOCOL_ERROR
            );
        }

        [':method' => $method, ':path' => $target, ':scheme' => $scheme, ':authority' => $host] = $pseudo;
        $query = null;

        if (!\preg_match(self::HOST_HEADER_REGEX, $host, $matches)) {
            throw new Http2StreamException(
                "Invalid authority (host) name",
                $streamId,
                Http2Parser::PROTOCOL_ERROR
            );
        }

        $address = $this->client->getLocalAddress();

        $host = $matches[1];
        $port = isset($matches[2])
            ? (int) $matches[2]
            : ($address instanceof InternetAddress ? $address->getPort() : null);

        if ($position = \strpos($target, "#")) {
            $target = \substr($target, 0, $position);
        }

        if ($position = \strpos($target, "?")) {
            $query = \substr($target, $position + 1);
            $target = \substr($target, 0, $position);
        }

        try {
            if ($target === "*") {
                $uri = Uri\Http::fromComponents([
                    "scheme" => $scheme,
                    "host" => $host,
                    "port" => $port,
                ]);
            } else {
                $uri = Uri\Http::fromComponents([
                    "scheme" => $scheme,
                    "host" => $host,
                    "port" => $port,
                    "path" => $target,
                    "query" => $query,
                ]);
            }
        } catch (Uri\Contracts\UriException $exception) {
            throw new Http2ConnectionException(
                "Invalid request URI",
                Http2Parser::PROTOCOL_ERROR
            );
        }

        $this->pinged = 0; // Reset ping count when a request is received.

        if ($streamEnded) {
            $request = new Request(
                $this->client,
                $method,
                $uri,
                $headers,
                "",
                "2"
            );

            $this->timeoutTracker->suspend($streamId);

            $this->streamIdMap[$request] = $streamId;
            $stream->pendingResponse = async($this->handleRequest(...), $request);

            return;
        }

        $this->trailerDeferreds[$streamId] = new DeferredFuture;
        $this->bodyQueues[$streamId] = new Queue();

        $body = new RequestBody(
            new ReadableIterableStream($this->bodyQueues[$streamId]->pipe()),
            function (int $bodySize) use ($streamId) {
                if (!isset($this->streams[$streamId], $this->bodyQueues[$streamId])) {
                    return;
                }

                if ($this->streams[$streamId]->bodySizeLimit >= $bodySize) {
                    return;
                }

                $this->streams[$streamId]->bodySizeLimit = $bodySize;
            }
        );

        if ($this->serverWindow <= self::MINIMUM_WINDOW) {
            $this->serverWindow += self::MAX_INCREMENT;
            $this->writeFrame(\pack("N", self::MAX_INCREMENT), Http2Parser::WINDOW_UPDATE, Http2Parser::NO_FLAG);
        }

        if (isset($headers["content-length"])) {
            if (isset($headers["content-length"][1])) {
                throw new Http2StreamException(
                    "Received multiple content-length headers",
                    $streamId,
                    Http2Parser::PROTOCOL_ERROR
                );
            }

            $contentLength = $headers["content-length"][0];
            if (!\preg_match('/^0|[1-9]\d*$/', $contentLength)) {
                throw new Http2StreamException(
                    "Invalid content-length header value",
                    $streamId,
                    Http2Parser::PROTOCOL_ERROR
                );
            }

            $stream->expectedLength = (int) $contentLength;
        }

        try {
            $trailers = new Trailers(
                $this->trailerDeferreds[$streamId]->getFuture(),
                isset($headers['trailers'])
                    ? \array_map('trim', \explode(',', \implode(',', $headers['trailers'])))
                    : []
            );
        } catch (InvalidHeaderException $exception) {
            throw new Http2StreamException(
                "Invalid headers field in trailers",
                $streamId,
                Http2Parser::PROTOCOL_ERROR,
                $exception
            );
        }

        $request = new Request(
            $this->client,
            $method,
            $uri,
            $headers,
            $body,
            "2",
            $trailers
        );

        $this->streamIdMap[$request] = $streamId;
        $stream->pendingResponse = async($this->handleRequest(...), $request);
    }

    #[\Override]
    public function handleData(int $streamId, string $data): void
    {
        $this->pinged = 0;

        $length = \strlen($data);

        if ($streamId & 1) {
            $this->timeoutTracker->update($streamId);
        }

        if (!isset($this->streams[$streamId], $this->bodyQueues[$streamId], $this->trailerDeferreds[$streamId])) {
            if ($streamId > 0 && $streamId <= $this->remoteStreamId) {
                throw new Http2StreamException(
                    "Stream closed",
                    $streamId,
                    Http2Parser::STREAM_CLOSED
                );
            }

            throw new Http2ConnectionException(
                "Invalid stream ID",
                Http2Parser::PROTOCOL_ERROR
            );
        }

        $stream = $this->streams[$streamId];

        if ($stream->state & Http2Stream::REMOTE_CLOSED) {
            throw new Http2StreamException(
                "Stream remote closed",
                $streamId,
                Http2Parser::PROTOCOL_ERROR
            );
        }

        if (!$length) {
            return;
        }

        $this->serverWindow -= $length;
        $stream->serverWindow -= $length;
        $stream->receivedByteCount += $length;

        if ($stream->receivedByteCount > $stream->bodySizeLimit) {
            throw new Http2StreamException(
                "Max body size exceeded",
                $streamId,
                Http2Parser::CANCEL
            );
        }

        if ($this->serverWindow <= self::MINIMUM_WINDOW) {
            $this->serverWindow += self::MAX_INCREMENT;
            $this->writeFrame(\pack("N", self::MAX_INCREMENT), Http2Parser::WINDOW_UPDATE, Http2Parser::NO_FLAG);
        }

        if ($stream->expectedLength !== null) {
            $stream->expectedLength -= $length;
        }

        $future = $this->bodyQueues[$streamId]->pushAsync($data);
        $future->ignore();

        if ($stream->serverWindow <= self::MINIMUM_WINDOW) {
            EventLoop::queue(function () use ($future, $streamId): void {
                try {
                    $future->await();
                } catch (\Throwable) {
                    return;
                }

                if (!isset($this->streams[$streamId])) {
                    return;
                }

                $stream = $this->streams[$streamId];

                if ($stream->state & Http2Stream::REMOTE_CLOSED
                    || $stream->serverWindow > self::MINIMUM_WINDOW
                ) {
                    return;
                }

                $increment = \min(
                    $stream->bodySizeLimit + 1 - $stream->receivedByteCount - $stream->serverWindow,
                    self::MAX_INCREMENT
                );

                if ($increment <= 0) {
                    return;
                }

                $stream->serverWindow += $increment;

                $this->writeFrame(
                    \pack("N", $increment),
                    Http2Parser::WINDOW_UPDATE,
                    Http2Parser::NO_FLAG,
                    $streamId
                );
            });
        }
    }

    #[\Override]
    public function handleStreamEnd(int $streamId): void
    {
        if (!isset($this->streams[$streamId])) {
            return; // Stream already closed locally.
        }

        $stream = $this->streams[$streamId];

        $stream->state |= Http2Stream::REMOTE_CLOSED;

        if ($stream->expectedLength) {
            throw new Http2StreamException(
                "Body length does not match content-length header",
                $streamId,
                Http2Parser::PROTOCOL_ERROR
            );
        }

        try {
            if (!isset($this->bodyQueues[$streamId], $this->trailerDeferreds[$streamId])) {
                return; // Stream closed after emitting body fragment.
            }

            $deferred = $this->trailerDeferreds[$streamId];
            $queue = $this->bodyQueues[$streamId];

            unset($this->bodyQueues[$streamId], $this->trailerDeferreds[$streamId]);

            $queue->complete();
            $deferred->complete([]);
        } finally {
            if (!isset($this->streams[$streamId])) {
                return; // Stream may have closed after resolving body emitter or trailers deferred.
            }

            // Close stream only if also locally closed and there is no buffer remaining to write.
            if ($stream->state & Http2Stream::LOCAL_CLOSED && $stream->buffer === "") {
                $this->releaseStream($streamId);
            }
        }
    }

    #[\Override]
    public function handlePushPromise(int $streamId, int $pushId, array $pseudo, array $headers): void
    {
        throw new Http2ConnectionException(
            "Client should not send push promise frames",
            Http2Parser::PROTOCOL_ERROR
        );
    }

    #[\Override]
    public function handlePriority(int $streamId, int $parentId, int $weight): void
    {
        if (!isset($this->streams[$streamId])) {
            if (!($streamId & 1) || $this->remainingStreams-- <= 0) {
                throw new Http2ConnectionException(
                    "Invalid stream ID",
                    Http2Parser::PROTOCOL_ERROR
                );
            }

            if ($streamId <= $this->remoteStreamId) {
                return; // Ignore priority frames on closed streams.
            }

            // Open a new stream if the ID has not been seen before, but do not set
            // $this->remoteStreamId. That will be set once the headers are received.
            $this->createStream($streamId, $this->bodySizeLimit);
        }

        $stream = $this->streams[$streamId];

        $stream->dependency = $parentId;
        $stream->weight = $weight;
    }

    #[\Override]
    public function handleStreamReset(int $streamId, int $errorCode): void
    {
        if ($streamId > $this->remoteStreamId) {
            throw new Http2ConnectionException(
                "Invalid stream ID",
                Http2Parser::PROTOCOL_ERROR
            );
        }

        if (isset($this->streams[$streamId])) {
            $exception = new Http2StreamException("Stream reset", $streamId, $errorCode);

            $this->releaseStream(
                $streamId,
                new ClientException($this->client, "Client closed stream", $errorCode, $exception)
            );
        }
    }

    #[\Override]
    public function handleSettings(array $settings): void
    {
        foreach ($settings as $key => $value) {
            switch ($key) {
                case Http2Parser::INITIAL_WINDOW_SIZE:
                    if ($value > 2147483647) { // (1 << 31) - 1
                        throw new Http2ConnectionException("Invalid window size", Http2Parser::FLOW_CONTROL_ERROR);
                    }

                    $priorWindowSize = $this->initialWindowSize;
                    $this->initialWindowSize = $value;
                    $difference = $this->initialWindowSize - $priorWindowSize;

                    foreach ($this->streams as $stream) {
                        $stream->clientWindow += $difference;
                    }

                    // Settings ACK should be sent before HEADER or DATA frames.
                    EventLoop::defer($this->sendBufferedData(...));
                    break;

                case Http2Parser::ENABLE_PUSH:
                    if ($value & ~1) {
                        throw new Http2ConnectionException(
                            "Invalid push promise toggle value",
                            Http2Parser::PROTOCOL_ERROR
                        );
                    }

                    $this->allowsPush = ((bool) $value) && $this->pushEnabled;
                    break;

                case Http2Parser::MAX_FRAME_SIZE:
                    if ($value < 1 << 14 || $value >= 1 << 24) {
                        throw new Http2ConnectionException("Invalid max frame size", Http2Parser::PROTOCOL_ERROR);
                    }

                    $this->maxFrameSize = $value;
                    break;

                case Http2Parser::HEADER_TABLE_SIZE:
                case Http2Parser::MAX_HEADER_LIST_SIZE:
                case Http2Parser::MAX_CONCURRENT_STREAMS:
                    break; // @TODO Respect these settings from the client.

                default:
                    break; // Unknown setting, ignore (6.5.2).
            }
        }

        $this->writeFrame("", Http2Parser::SETTINGS, Http2Parser::ACK);
    }

    #[\Override]
    public function handleStreamException(Http2StreamException $exception): void
    {
        $streamId = $exception->getStreamId();
        $errorCode = $exception->getCode();

        $this->writeFrame(\pack("N", $errorCode), Http2Parser::RST_STREAM, Http2Parser::NO_FLAG, $streamId);

        if (isset($this->streams[$streamId])) {
            $this->releaseStream($streamId, new ClientException($this->client, "HTTP/2 stream error", 0, $exception));
        }
    }

    #[\Override]
    public function handleConnectionException(Http2ConnectionException $exception): void
    {
        $this->logger->notice("HTTP/2 connection error for client {address}: {message}", [
            'address' => $this->client->getRemoteAddress()->toString(),
            'message' => $exception->getMessage(),
        ]);

        $this->shutdown(
            new ClientException($this->client, "HTTP/2 connection error", $exception->getCode(), $exception)
        );
    }

    #[\Override]
    public function getApplicationLayerProtocols(): array
    {
        return ['h2'];
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Server\Driver;

use Amp\Http\Server\ErrorHandler;
use Amp\Http\Server\RequestHandler;
use Psr\Log\LoggerInterface as PsrLogger;

final class DefaultHttpDriverFactory implements HttpDriverFactory
{
    /**
     * @param bool $allowHttp2Upgrade Requires HTTP/2 support to be enabled.
     * @param bool $pushEnabled Requires HTTP/2 support to be enabled.
     */
    public function __construct(
        private readonly PsrLogger $logger,
        private readonly int $streamTimeout = HttpDriver::DEFAULT_STREAM_TIMEOUT,
        private readonly int $connectionTimeout = HttpDriver::DEFAULT_CONNECTION_TIMEOUT,
        private readonly int $headerSizeLimit = HttpDriver::DEFAULT_HEADER_SIZE_LIMIT,
        private readonly int $bodySizeLimit = HttpDriver::DEFAULT_BODY_SIZE_LIMIT,
        private readonly bool $http2Enabled = true,
        private readonly bool $allowHttp2Upgrade = false,
        private readonly bool $pushEnabled = true,
    ) {
    }

    #[\Override]
    public function createHttpDriver(
        RequestHandler $requestHandler,
        ErrorHandler $errorHandler,
        Client $client,
    ): HttpDriver {
        if ($client->getTlsInfo()?->getApplicationLayerProtocol() === "h2") {
            return new Http2Driver(
                requestHandler: $requestHandler,
                errorHandler: $errorHandler,
                logger: $this->logger,
                streamTimeout: $this->streamTimeout,
                connectionTimeout: $this->connectionTimeout,
                headerSizeLimit: $this->headerSizeLimit,
                bodySizeLimit: $this->bodySizeLimit,
                pushEnabled: $this->pushEnabled,
            );
        }

        return new Http1Driver(
            requestHandler: $requestHandler,
            errorHandler: $errorHandler,
            logger: $this->logger,
            connectionTimeout: $this->streamTimeout, // Intentional use of stream instead of connection timeout
            headerSizeLimit: $this->headerSizeLimit,
            bodySizeLimit: $this->bodySizeLimit,
            allowHttp2Upgrade: $this->http2Enabled && $this->allowHttp2Upgrade,
        );
    }

    #[\Override]
    public function getApplicationLayerProtocols(): array
    {
        if ($this->http2Enabled) {
            return ["h2", "http/1.1"];
        }

        return ["http/1.1"];
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Server\Driver;

use Amp\Closable;
use Amp\Socket\SocketAddress;
use Amp\Socket\TlsInfo;

interface Client extends Closable
{
    /**
     * Integer ID of this client.
     */
    public function getId(): int;

    /**
     * @return SocketAddress Remote client address.
     */
    public function getRemoteAddress(): SocketAddress;

    /**
     * @return SocketAddress Local server address.
     */
    public function getLocalAddress(): SocketAddress;

    /**
     * If the client is encrypted a TlsInfo object is returned, otherwise null.
     */
    public function getTlsInfo(): ?TlsInfo;
}
<?php declare(strict_types=1);

namespace Amp\Http\Server\Driver;

use Amp\Http\Server\ErrorHandler;
use Amp\Http\Server\RequestHandler;

interface HttpDriverFactory
{
    public function createHttpDriver(
        RequestHandler $requestHandler,
        ErrorHandler $errorHandler,
        Client $client,
    ): HttpDriver;

    /**
     * @return list<string>
     */
    public function getApplicationLayerProtocols(): array;
}
<?php declare(strict_types=1);

namespace Amp\Http\Server\Driver;

use Amp\Socket\Socket;
use Psr\Log\LoggerInterface as PsrLogger;

final class LoggingSocketClientFactory implements ClientFactory
{
    public function __construct(
        private readonly ClientFactory $clientFactory,
        private readonly PsrLogger $logger,
    ) {
    }

    #[\Override]
    public function createClient(Socket $socket): ?Client
    {
        $local = $socket->getLocalAddress()->toString();
        $remote = $socket->getRemoteAddress()->toString();
        $context = [
            'local' => $local,
            'remote' => $remote,
        ];

        $this->logger->info(\sprintf("Accepted %s on %s", $local, $remote), $context);

        $client = $this->clientFactory->createClient($socket);

        if ($client === null) {
            $this->logger->notice(
                \sprintf("Creation of client for %s on %s failed", $local, $remote),
                $context,
            );
        }

        return $client;
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Server\Driver;

use Amp\Socket\InternetAddress;
use Amp\Socket\InternetAddressVersion;
use Amp\Socket\Socket;
use Psr\Log\LoggerInterface as PsrLogger;

final class ConnectionLimitingClientFactory implements ClientFactory
{
    /** @var array<string, int> */
    private array $clientsPerIp = [];

    public function __construct(
        private readonly ClientFactory $clientFactory,
        private readonly PsrLogger $logger,
        private readonly int $connectionsPerIpLimit,
    ) {
    }

    #[\Override]
    public function createClient(Socket $socket): ?Client
    {
        $address = $socket->getRemoteAddress();

        if (!$address instanceof InternetAddress) {
            return $this->clientFactory->createClient($socket);
        }

        $ip = $address->getAddress();
        $bytes = $address->getAddressBytes();

        // Connections on localhost are excluded from the connections per IP setting.
        // Checks IPv4 loopback (127.x), IPv6 loopback (::1) and IPv4-to-IPv6 mapped loopback.
        if ($ip === "::1" || \str_starts_with($ip, "127.")
            || \str_starts_with($bytes, "\0\0\0\0\0\0\0\0\0\0\xff\xff\x7f")
        ) {
            return $this->clientFactory->createClient($socket);
        }

        if ($address->getVersion() === InternetAddressVersion::IPv6) {
            $bytes = \substr($bytes, 0, 7 /* /56 block for IPv6 */);
        }

        $this->clientsPerIp[$bytes] ??= 0;

        if ($this->clientsPerIp[$bytes] >= $this->connectionsPerIpLimit) {
            if (isset($bytes[4])) {
                $ip .= "/56";
            }

            $this->logger->warning(
                "Client denied: too many existing connections from {$ip}",
                ['local' => $socket->getLocalAddress()->toString(), 'remote' => $address->toString()],
            );

            return null;
        }

        ++$this->clientsPerIp[$bytes];

        $client = $this->clientFactory->createClient($socket);
        if ($client === null) {
            $this->onClose($bytes);

            return null;
        }

        $socket->onClose(fn () => $this->onClose($bytes));

        return $client;
    }

    private function onClose(string $bytes): void
    {
        if (--$this->clientsPerIp[$bytes] === 0) {
            unset($this->clientsPerIp[$bytes]);
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Server\Driver;

use Amp\ByteStream\ReadableBuffer;
use Amp\ByteStream\ReadableIterableStream;
use Amp\ByteStream\ReadableStream;
use Amp\ByteStream\ReadableStreamChain;
use Amp\ByteStream\StreamException;
use Amp\ByteStream\WritableStream;
use Amp\CancelledException;
use Amp\DeferredCancellation;
use Amp\DeferredFuture;
use Amp\Future;
use Amp\Http\Http1\Rfc7230;
use Amp\Http\HttpStatus;
use Amp\Http\InvalidHeaderException;
use Amp\Http\Server\ClientException;
use Amp\Http\Server\Driver\Internal\AbstractHttpDriver;
use Amp\Http\Server\ErrorHandler;
use Amp\Http\Server\Request;
use Amp\Http\Server\RequestBody;
use Amp\Http\Server\RequestHandler;
use Amp\Http\Server\Response;
use Amp\Http\Server\Trailers;
use Amp\Pipeline\DisposedException;
use Amp\Pipeline\Queue;
use Amp\Socket\InternetAddress;
use League\Uri;
use Psr\Log\LoggerInterface as PsrLogger;
use function Amp\async;
use function Amp\Http\formatDateHeader;
use function Amp\Http\mapHeaderPairs;

final class Http1Driver extends AbstractHttpDriver
{
    private static function makeHeaderReduceClosure(string $search): \Closure
    {
        return static fn (bool $carry, string $header) => $carry || \strcasecmp($search, $header) === 0;
    }

    private ?Http2Driver $http2driver = null;

    private Client $client;

    private ReadableStream $readableStream;

    private WritableStream $writableStream;

    private int $pendingResponseCount = 0;

    private ?Queue $bodyQueue = null;

    private Future $pendingResponse;

    private ?Future $lastWrite = null;

    private bool $stopping = false;

    private string $currentBuffer = "";

    private bool $continue = true;

    private readonly DeferredCancellation $deferredCancellation;

    public function __construct(
        RequestHandler $requestHandler,
        ErrorHandler $errorHandler,
        PsrLogger $logger,
        private readonly int $connectionTimeout = self::DEFAULT_STREAM_TIMEOUT,
        private readonly int $headerSizeLimit = self::DEFAULT_HEADER_SIZE_LIMIT,
        private readonly int $bodySizeLimit = self::DEFAULT_BODY_SIZE_LIMIT,
        private readonly bool $allowHttp2Upgrade = false,
    ) {
        parent::__construct($requestHandler, $errorHandler, $logger);

        $this->pendingResponse = Future::complete();
        $this->deferredCancellation = new DeferredCancellation();
    }

    /**
     * @psalm-suppress UnusedVariable
     */
    #[\Override]
    public function handleClient(
        Client $client,
        ReadableStream $readableStream,
        WritableStream $writableStream,
    ): void {
        /** @psalm-suppress RedundantPropertyInitializationCheck */
        \assert(!isset($this->client));

        /** @psalm-suppress RedundantCondition */
        \assert($this->logger->debug(\sprintf(
            "Handling requests from %s #%d using HTTP/1.x driver",
            $client->getRemoteAddress()->toString(),
            $client->getId(),
        )) || true);

        $this->client = $client;
        $this->readableStream = $readableStream;
        $this->writableStream = $writableStream;

        $this->insertTimeout();

        $headerSizeLimit = $this->headerSizeLimit;
        $cancellation = $this->deferredCancellation->getCancellation();

        try {
            $buffer = $readableStream->read($cancellation);
            if ($buffer === null) {
                $this->removeTimeout();
                return;
            }

            do {
                if ($this->http2driver) {
                    $this->suspendTimeout();
                    $this->http2driver->handleClientWithBuffer($buffer, $this->readableStream);
                    return;
                }

                $contentLength = null;
                $isChunked = false;
                $searchPos = 0;
                $rawHeaders = ""; // For Psalm

                $buffer = \ltrim($buffer, "\r\n");

                do {
                    if ($this->stopping) {
                        return;
                    }

                    if ($headerPos = \strpos($buffer, "\r\n\r\n", $searchPos)) {
                        $rawHeaders = \substr($buffer, 0, $headerPos + 2);
                        $buffer = \substr($buffer, $headerPos + 4);
                        break;
                    }

                    if (\strlen($buffer) > $headerSizeLimit) {
                        throw new ClientException(
                            $this->client,
                            "Bad Request: header size violation",
                            HttpStatus::REQUEST_HEADER_FIELDS_TOO_LARGE
                        );
                    }

                    $chunk = $readableStream->read($cancellation);
                    if ($chunk === null) {
                        return;
                    }

                    $searchPos = \max(0, \strlen($buffer) - 3);
                    $buffer .= $chunk;
                } while (true);

                if (!\preg_match("#^([^ ]+) (\S+) HTTP/(\d+(?:\.\d+)?)\r\n#", $rawHeaders, $matches)) {
                    throw new ClientException($this->client, "Bad Request: invalid request line", HttpStatus::BAD_REQUEST);
                }

                /**
                 * @psalm-suppress UnnecessaryVarAnnotation
                 * @var non-empty-list<non-empty-string> $matches
                 */
                [$startLine, $method, $target, $protocol] = $matches;
                $rawHeaders = \substr($rawHeaders, \strlen($startLine));

                if ($protocol !== "1.1" && $protocol !== "1.0") {
                    if ($protocol === "2.0" && $this->allowHttp2Upgrade) {
                        $this->removeTimeout();

                        // Internal upgrade to HTTP/2.
                        $this->http2driver = new Http2Driver(
                            requestHandler: $this->requestHandler,
                            errorHandler: $this->errorHandler,
                            logger: $this->logger,
                            streamTimeout: $this->connectionTimeout,
                            connectionTimeout: $this->connectionTimeout,
                            headerSizeLimit: $this->headerSizeLimit,
                            bodySizeLimit: $this->bodySizeLimit,
                            pushEnabled: false,
                        );

                        $this->http2driver->handleClient(
                            $this->client,
                            new ReadableStreamChain(
                                new ReadableBuffer("$startLine$rawHeaders\r\n$buffer"),
                                $readableStream
                            ),
                            $writableStream
                        );

                        return;
                    }

                    throw new ClientException(
                        $this->client,
                        "Unsupported version $protocol",
                        HttpStatus::HTTP_VERSION_NOT_SUPPORTED
                    );
                }

                if (!$rawHeaders) {
                    throw new ClientException($this->client, "Bad Request: missing host header", HttpStatus::BAD_REQUEST);
                }

                try {
                    $parsedHeaders = Rfc7230::parseHeaderPairs($rawHeaders);
                    $headers = mapHeaderPairs($parsedHeaders);
                } catch (InvalidHeaderException $e) {
                    throw new ClientException(
                        $this->client,
                        "Bad Request: " . $e->getMessage(),
                        HttpStatus::BAD_REQUEST
                    );
                }

                if (isset($headers["content-length"][1])) {
                    throw new ClientException(
                        $this->client,
                        "Bad Request: multiple content-length headers",
                        HttpStatus::BAD_REQUEST
                    );
                }

                $contentLength = $headers["content-length"][0] ?? null;
                if ($contentLength !== null) {
                    if (!\preg_match("/^(?:0|[1-9]\\d*)$/", $contentLength)) {
                        throw new ClientException(
                            $this->client,
                            "Bad Request: invalid content length",
                            HttpStatus::BAD_REQUEST
                        );
                    }

                    $contentLength = (int) $contentLength;
                }

                if (isset($headers["transfer-encoding"])) {
                    $transferEncoding = \strtolower(\implode(', ', $headers["transfer-encoding"]));
                    $isChunked = match ($transferEncoding) {
                        'chunked' => true,
                        'identity' => false,
                        default => throw new ClientException(
                            $this->client,
                            "Unsupported transfer-encoding",
                            HttpStatus::NOT_IMPLEMENTED
                        ),
                    };
                }

                if (!isset($headers["host"][0])) {
                    throw new ClientException($this->client, "Bad Request: missing host header", HttpStatus::BAD_REQUEST);
                }

                if (isset($headers["host"][1])) {
                    throw new ClientException($this->client, "Bad Request: multiple host headers", HttpStatus::BAD_REQUEST);
                }

                if (!\preg_match(self::HOST_HEADER_REGEX, $headers["host"][0], $matches)) {
                    throw new ClientException($this->client, "Bad Request: invalid host header", HttpStatus::BAD_REQUEST);
                }

                $address = $this->client->getLocalAddress();

                $host = $matches[1];
                $port = isset($matches[2])
                    ? (int) $matches[2]
                    : ($address instanceof InternetAddress ? $address->getPort() : null);
                $scheme = $this->client->getTlsInfo() !== null ? "https" : "http";
                $query = null;

                try {
                    if ($target[0] === "/") { // origin-form
                        if ($position = \strpos($target, "#")) {
                            $target = \substr($target, 0, $position);
                        }

                        if ($position = \strpos($target, "?")) {
                            $query = \substr($target, $position + 1);
                            $target = \substr($target, 0, $position);
                        }

                        $uri = Uri\Http::fromComponents([
                            "scheme" => $scheme,
                            "host" => $host,
                            "port" => $port,
                            "path" => $target,
                            "query" => $query,
                        ]);
                    } elseif ($target === "*") { // asterisk-form
                        $uri = Uri\Http::fromComponents([
                            "scheme" => $scheme,
                            "host" => $host,
                            "port" => $port,
                        ]);
                    } elseif (\preg_match("#^https?://#i", $target)) { // absolute-form
                        $uri = Uri\Http::new($target);

                        if ($uri->getHost() !== $host || $uri->getPort() !== $port) {
                            throw new ClientException(
                                $this->client,
                                "Bad Request: target host mis-matched to host header",
                                HttpStatus::BAD_REQUEST
                            );
                        }

                        if ($uri->getPath() === "") {
                            throw new ClientException(
                                $this->client,
                                "Bad Request: no request path provided in target",
                                HttpStatus::BAD_REQUEST
                            );
                        }
                    } else { // authority-form
                        if ($method !== "CONNECT") {
                            throw new ClientException(
                                $this->client,
                                "Bad Request: authority-form only valid for CONNECT requests",
                                HttpStatus::BAD_REQUEST
                            );
                        }

                        if (!\preg_match(self::HOST_HEADER_REGEX, $target, $matches)) {
                            throw new ClientException(
                                $this->client,
                                "Bad Request: invalid connect target",
                                HttpStatus::BAD_REQUEST
                            );
                        }

                        $uri = Uri\Http::fromComponents([
                            "host" => $matches[1],
                            "port" => (int) $matches[2],
                        ]);
                    }
                } catch (Uri\Contracts\UriException $exception) {
                    throw new ClientException(
                        $this->client,
                        "Bad Request: invalid target",
                        HttpStatus::BAD_REQUEST,
                        $exception
                    );
                }

                if (isset($headers["expect"][0]) && \strtolower($headers["expect"][0]) === "100-continue") {
                    $this->write(
                        new Request($this->client, $method, $uri, $headers, '', $protocol),
                        new Response(HttpStatus::CONTINUE, [])
                    );
                }

                // Handle HTTP/2 upgrade request.
                if ($protocol === "1.1"
                    && isset($headers["upgrade"][0], $headers["http2-settings"][0], $headers["connection"][0])
                    && $this->client->getTlsInfo() === null
                    && $this->allowHttp2Upgrade
                    && false !== \stripos($headers["connection"][0], "upgrade")
                    && \strtolower($headers["upgrade"][0]) === "h2c"
                    && false !== $h2cSettings = \base64_decode(
                        \strtr($headers["http2-settings"][0], "-_", "+/"),
                        true
                    )
                ) {
                    // Request instance will be overwritten below. This is for sending the switching protocols response.
                    $this->write(
                        new Request($this->client, $method, $uri, $headers, '', $protocol),
                        new Response(HttpStatus::SWITCHING_PROTOCOLS, [
                            "connection" => "upgrade",
                            "upgrade" => "h2c",
                        ])
                    );

                    $this->removeTimeout();

                    // Internal upgrade
                    $this->http2driver = new Http2Driver(
                        requestHandler: $this->requestHandler,
                        errorHandler: $this->errorHandler,
                        logger: $this->logger,
                        streamTimeout: $this->connectionTimeout,
                        connectionTimeout: $this->connectionTimeout,
                        headerSizeLimit: $this->headerSizeLimit,
                        bodySizeLimit: $this->bodySizeLimit,
                        pushEnabled: false,
                        settings: $h2cSettings,
                    );

                    $this->http2driver->initializeWriting($this->client, $this->writableStream);

                    // Remove headers that are not related to the HTTP/2 request.
                    $parsedHeaders = \array_filter(
                        $parsedHeaders,
                        static fn (array $pair) => match (\strtolower($pair[0])) {
                            'upgrade', 'connection', 'http2-settings' => false,
                            default => true,
                        },
                    );

                    $protocol = "2";
                }

                $this->updateTimeout();

                if (!($isChunked || $contentLength)) {
                    $request = new Request($this->client, $method, $uri, [], '', $protocol);
                    foreach ($parsedHeaders as $pair) {
                        $request->addHeader(...$pair);
                    }

                    $this->pendingResponseCount++;

                    try {
                        if ($this->http2driver) {
                            $this->pendingResponse = async($this->http2driver->handleRequest(...), $request);

                            continue;
                        }

                        $this->suspendTimeout();

                        $this->currentBuffer = $buffer;
                        $this->pendingResponse = async($this->handleRequest(...), $request);
                        $this->pendingResponse->await();
                        $this->pendingResponseCount--;

                        continue;
                    } finally {
                        $request = null; // DO NOT leave a reference to the Request object in the parser!
                    }
                }

                // HTTP/1.x clients only ever have a single body emitter.
                $this->bodyQueue = $queue = new Queue();
                $trailerDeferred = new DeferredFuture;
                $bodySizeLimit = $this->bodySizeLimit;

                $body = new RequestBody(
                    new ReadableIterableStream($queue->pipe()),
                    static function (int $bodySize) use (&$bodySizeLimit): void {
                        if ($bodySize > $bodySizeLimit) {
                            $bodySizeLimit = $bodySize;
                        }
                    }
                );

                try {
                    $trailers = new Trailers(
                        $trailerDeferred->getFuture(),
                        isset($headers['trailers'])
                            ? \array_map('trim', \explode(',', \implode(',', $headers['trailers'])))
                            : []
                    );
                } catch (InvalidHeaderException $exception) {
                    throw new ClientException(
                        $this->client,
                        "Invalid header field in trailers",
                        HttpStatus::BAD_REQUEST,
                        $exception
                    );
                }

                $request = new Request($this->client, $method, $uri, [], $body, $protocol, $trailers);
                foreach ($parsedHeaders as $pair) {
                    $request->addHeader(...$pair);
                }

                // Do not await future until body is completely read.
                $this->pendingResponseCount++;
                $this->currentBuffer = $buffer;
                $this->pendingResponse = async(
                    $this->http2driver
                        ? $this->http2driver->handleRequest(...)
                        : $this->handleRequest(...),
                    $request,
                );

                // DO NOT leave a reference to the Request, Trailers, or Body objects within the parser!
                $request = null;
                $trailers = null;
                $body = "";

                $bodySize = 0;

                if ($isChunked) {
                    while (true) {
                        $lineEndPos = 0; // For Psalm
                        while (false === ($lineEndPos = \strpos($buffer, "\r\n"))) {
                            if (\strlen($buffer) > 10) {
                                throw new ClientException(
                                    $this->client,
                                    "Bad Request: hex chunk size expected",
                                    HttpStatus::BAD_REQUEST
                                );
                            }

                            $chunk = $this->readableStream->read($cancellation);
                            if ($chunk === null) {
                                return;
                            }

                            $buffer .= $chunk;
                        }

                        /** @psalm-suppress NoValue $lineEndPos is set above, maybe a bug in Psalm? */
                        $line = \substr($buffer, 0, $lineEndPos);
                        $buffer = \substr($buffer, $lineEndPos + 2);
                        $hex = \trim($line);
                        if ($hex !== "0") {
                            $hex = \ltrim($line, "0");

                            if (!\preg_match("/^[1-9A-F][0-9A-F]*$/i", $hex)) {
                                throw new ClientException(
                                    $this->client,
                                    "Bad Request: invalid hex chunk size",
                                    HttpStatus::BAD_REQUEST
                                );
                            }
                        }

                        $chunkLengthRemaining = \hexdec($hex);

                        if ($chunkLengthRemaining === 0) {
                            while (!isset($buffer[1])) {
                                $chunk = $readableStream->read($cancellation);
                                if ($chunk === null) {
                                    return;
                                }

                                $buffer .= $chunk;
                            }

                            $firstTwoBytes = \substr($buffer, 0, 2);
                            if ($firstTwoBytes === "\r\n") {
                                $buffer = \substr($buffer, 2);
                                break; // finished, no trailers (chunked loop)
                            }

                            // Note that the Trailer header does not need to be set for the message to include trailers.
                            // @see https://tools.ietf.org/html/rfc7230#section-4.4

                            do {
                                if ($trailerPos = \strpos($buffer, "\r\n\r\n")) {
                                    $rawTrailers = \substr($buffer, 0, $trailerPos + 2);
                                    $buffer = \substr($buffer, $trailerPos + 4);
                                    break;
                                }

                                if (\strlen($buffer) > $headerSizeLimit) {
                                    throw new ClientException(
                                        $this->client,
                                        "Bad Request: trailer headers too large",
                                        HttpStatus::BAD_REQUEST
                                    );
                                }

                                $chunk = $this->readableStream->read($cancellation);
                                if ($chunk === null) {
                                    return;
                                }

                                $buffer .= $chunk;
                            } while (true);

                            \assert(isset($rawTrailers)); // For Psalm

                            try {
                                $trailers = Rfc7230::parseHeaders($rawTrailers);
                            } catch (InvalidHeaderException $e) {
                                throw new ClientException(
                                    $this->client,
                                    "Bad Request: " . $e->getMessage(),
                                    HttpStatus::BAD_REQUEST
                                );
                            }

                            if (\array_intersect_key($trailers, Trailers::DISALLOWED_TRAILERS)) {
                                throw new ClientException(
                                    $this->client,
                                    "Trailer section contains disallowed headers",
                                    HttpStatus::BAD_REQUEST
                                );
                            }

                            $trailerDeferred->complete($trailers);
                            $trailerDeferred = null;

                            break; // finished (chunked loop)
                        }

                        if ($bodySize + $chunkLengthRemaining > $bodySizeLimit) {
                            do {
                                $remaining = $bodySizeLimit - $bodySize;
                                $chunkLengthRemaining -= $remaining - \strlen($body);
                                $body .= $buffer;
                                $bodyBufferSize = \strlen($body);

                                while ($bodyBufferSize < $remaining) {
                                    if ($bodyBufferSize) {
                                        $this->updateTimeout();
                                        try {
                                            $queue->push($body);
                                        } catch (DisposedException) {
                                            // Ignore and continue consuming body.
                                        }
                                        $bodySize += $bodyBufferSize;
                                        $remaining -= $bodyBufferSize;
                                    }

                                    $body = $readableStream->read($cancellation);
                                    if ($body === null) {
                                        return;
                                    }

                                    $bodyBufferSize = \strlen($body);
                                }

                                if ($remaining) {
                                    $this->updateTimeout();
                                    try {
                                        $queue->push(\substr($body, 0, $remaining));
                                    } catch (DisposedException) {
                                        // Ignore and continue consuming body.
                                    }
                                    $buffer = \substr($body, $remaining);
                                    $body = "";
                                    $bodySize += $remaining;
                                }

                                if ($bodySize !== $bodySizeLimit) {
                                    continue;
                                }

                                throw new ClientException(
                                    $this->client,
                                    "Payload too large",
                                    HttpStatus::PAYLOAD_TOO_LARGE
                                );
                            } while ($bodySizeLimit < $bodySize + $chunkLengthRemaining);
                        }

                        while (true) {
                            $bufferLength = \strlen($buffer);

                            if (!$bufferLength) {
                                $chunk = $readableStream->read($cancellation);
                                if ($chunk === null) {
                                    return;
                                }

                                $buffer = $chunk;
                                $bufferLength = \strlen($buffer);
                            }

                            // These first two (extreme) edge cases prevent errors where the packet boundary ends after
                            // the \r and before the \n at the end of a chunk.
                            if ($bufferLength === $chunkLengthRemaining || $bufferLength === $chunkLengthRemaining + 1) {
                                $chunk = $readableStream->read($cancellation);
                                if ($chunk === null) {
                                    return;
                                }

                                $buffer .= $chunk;
                                continue;
                            }

                            $this->updateTimeout();

                            if ($bufferLength >= $chunkLengthRemaining + 2) {
                                try {
                                    $queue->push(\substr($buffer, 0, $chunkLengthRemaining));
                                } catch (DisposedException) {
                                    // Ignore and continue consuming body.
                                }
                                $buffer = \substr($buffer, $chunkLengthRemaining + 2);

                                $chunkLengthRemaining = null;
                                continue 2; // next chunk (chunked loop)
                            }

                            try {
                                $queue->push($buffer);
                            } catch (DisposedException) {
                                // Ignore and continue consuming body.
                            }
                            $buffer = "";
                            $chunkLengthRemaining -= $bufferLength;
                        }
                    }

                    if ($body !== "") {
                        try {
                            $queue->push($body);
                        } catch (DisposedException) {
                            // Ignore and continue consuming body.
                        }
                    }
                } else {
                    do {
                        $bodyBufferSize = \strlen($buffer);

                        // Note that $bodySizeLimit may change while looping.
                        while ($bodySize + $bodyBufferSize < \min($bodySizeLimit, $contentLength)) {
                            if ($bodyBufferSize) {
                                $this->updateTimeout();
                                try {
                                    $queue->push($buffer);
                                } catch (DisposedException) {
                                    // Ignore and continue consuming body.
                                }
                                $buffer = '';
                                $bodySize += $bodyBufferSize;
                            }

                            $chunk = $readableStream->read($cancellation);
                            if ($chunk === null) {
                                return;
                            }

                            $buffer .= $chunk;
                            $bodyBufferSize = \strlen($buffer);
                        }

                        $remaining = \min($bodySizeLimit, $contentLength) - $bodySize;

                        if ($remaining) {
                            $this->updateTimeout();
                            try {
                                $queue->push(\substr($buffer, 0, $remaining));
                            } catch (DisposedException) {
                                // Ignore and continue consuming body.
                            }
                            $buffer = \substr($buffer, $remaining);
                            $bodySize += $remaining;
                        }
                        // handle the case where $bodySizeLimit was increased during the $remaining sequence
                    } while ($bodySize < \min($bodySizeLimit, $contentLength));

                    if ($contentLength > $bodySizeLimit) {
                        throw new ClientException($this->client, "Payload too large", HttpStatus::PAYLOAD_TOO_LARGE);
                    }
                }

                /** @psalm-suppress TypeDoesNotContainNull, RedundantCondition $trailerDeferred could be null */
                $trailerDeferred?->complete([]);
                $trailerDeferred = null;

                $this->bodyQueue = null;
                $queue->complete();

                $this->suspendTimeout();

                if ($this->http2driver) {
                    continue;
                }

                $this->pendingResponse->await(); // Wait for response to be generated.
                $this->pendingResponseCount--;
            } while ($this->continue);
        } catch (ClientException $exception) {
            if ($this->bodyQueue === null || !$this->pendingResponseCount) {
                // Send an error response only if another response has not already been sent to the request.
                $this->sendErrorResponse($exception, $request ?? null)->await();
            }
        } catch (StreamException) {
            // Client disconnected, finally block will clean up.
        } catch (CancelledException) {
            // Server shutting down.
            if ($this->bodyQueue === null || !$this->pendingResponseCount) {
                // Send a service unavailable response only if another response has not already been sent.
                $this->sendServiceUnavailableResponse($request ?? null)->await();
            }
        } finally {
            $this->pendingResponse->finally(function (): void {
                $this->removeTimeout();

                /** @psalm-suppress RedundantCondition */
                \assert($this->logger->debug(\sprintf(
                    "Stopping HTTP/1.x parser @ %s #%d",
                    $this->client->getRemoteAddress()->toString(),
                    $this->client->getId(),
                )) || true);
            })->ignore();

            $this->bodyQueue?->error($exception ??= new ClientException(
                $this->client,
                "Client disconnected",
                HttpStatus::REQUEST_TIMEOUT
            ));
            $this->bodyQueue = null;

            /** @psalm-suppress TypeDoesNotContainNull, RedundantCondition */
            ($trailerDeferred ?? null)?->error($exception ?? new ClientException(
                $this->client,
                "Client disconnected",
                HttpStatus::REQUEST_TIMEOUT
            ));

            $this->deferredCancellation->cancel();
        }
    }

    private function insertTimeout(): void
    {
        self::getTimeoutQueue()->insert(
            $this->client,
            0,
            static fn (Client $client) => $client->close(),
            $this->connectionTimeout,
        );
    }

    private function updateTimeout(): void
    {
        self::getTimeoutQueue()->update($this->client, 0, $this->connectionTimeout);
    }

    private function suspendTimeout(): void
    {
        self::getTimeoutQueue()->suspend($this->client, 0);
    }

    private function removeTimeout(): void
    {
        self::getTimeoutQueue()->remove($this->client, 0);
    }

    /**
     * Selects HTTP/2 or HTTP/1.x writer depending on connection status.
     */
    #[\Override]
    protected function write(Request $request, Response $response): void
    {
        if ($this->http2driver) {
            $this->http2driver->write($request, $response);
            return;
        }

        $deferred = new DeferredFuture;
        $lastWrite = $this->lastWrite;
        $this->lastWrite = $future = $deferred->getFuture();

        try {
            $this->send($lastWrite, $response, $request);

            if ($response->isUpgraded()) {
                $this->upgrade($request, $response);
            }
        } finally {
            if ($this->lastWrite === $future) {
                $this->lastWrite = null;
            }
            $deferred->complete();
        }
    }

    /**
     * HTTP/1.x response writer.
     *
     * @psalm-suppress UnusedVariable $chunk is used, and line-level suppression was not sufficient.
     */
    private function send(?Future $lastWrite, Response $response, ?Request $request = null): void
    {
        /** @psalm-suppress RedundantPropertyInitializationCheck */
        \assert(isset($this->client), "The driver has not been setup; call setup first");

        $lastWrite?->await(); // Prevent sending multiple responses at once.

        $protocol = $request?->getProtocolVersion() ?? "1.0";

        $status = $response->getStatus();
        $reason = $response->getReason();

        [$headers, $need, $shouldClose] = $this->filter($response, $request, $protocol);

        $trailers = $response->getTrailers();

        if (($fields = $trailers?->getFields()) && !isset($headers["trailer"])) {
            $headers["trailer"] = [\implode(", ", $fields)];
        }

        // 1xx, 204, and 304 responses are terminated by the end of the header section and have no
        // body, so they must not use chunked encoding (RFC 9112 sections 6.1 and 6.3).
        $skipBody =  $status < HttpStatus::OK
            || $status === HttpStatus::NO_CONTENT
            || $status === HttpStatus::NOT_MODIFIED;

        $chunked = !$shouldClose
            && !$skipBody
            && (!isset($headers["content-length"]) || $trailers !== null)
            && $protocol === "1.1";

        if ($chunked) {
            $headers["transfer-encoding"] = ["chunked"];
        }

        $chunk = "HTTP/$protocol $status $reason\r\n" . Rfc7230::formatHeaders($headers) . "\r\n";

        $wrote = 0;

        try {
            $this->writableStream->write($chunk);

            $chunk = null; // Required for the finally, not directly overwritten, even if your IDE says otherwise.

            if ($skipBody || $request?->getMethod() === "HEAD") {
                $need = null; // No body is written, so nothing is owed to the client.

                if ($shouldClose) {
                    $this->writableStream->end();
                }

                return;
            }

            $body = $response->getBody();

            $this->updateTimeout();

            $cancellation = $this->deferredCancellation->getCancellation();

            while (null !== $chunk = $body->read($cancellation)) {
                if ($chunk === "") {
                    continue;
                }
                $wrote += \strlen($chunk);

                if ($chunked) {
                    $chunk = \sprintf("%x\r\n%s\r\n", \strlen($chunk), $chunk);
                }

                $this->writableStream->write($chunk);
                $this->updateTimeout();
            }

            if ($chunked) {
                $chunk = "0\r\n";

                if ($trailers !== null) {
                    $trailers = $trailers->await($cancellation);
                    $chunk .= Rfc7230::formatHeaders($trailers->getHeaders());
                }

                $chunk .= "\r\n";

                $this->writableStream->write($chunk);
                $chunk = null;
            }

            if ($shouldClose) {
                $this->writableStream->end();
            }
        } catch (StreamException|ClientException|CancelledException) {
            return; // Client will be closed in finally.
        } finally {
            /** @psalm-suppress TypeDoesNotContainType */
            if ($chunk !== null || ($need !== null && $wrote !== $need)) {
                $this->client->close();
            }
        }
    }

    /**
     * Filters and updates response headers based on protocol and connection header from the request.
     *
     * @param string $protocol Request protocol.
     *
     * @return array{array<non-empty-string, list<string>>, int|null,  bool} Response headers to be written,
     *      content length, and if the connection should be closed.
     */
    private function filter(Response $response, ?Request $request, string $protocol = "1.0"): array
    {
        static $closeReduce, $keepAliveReduce;
        $closeReduce ??= self::makeHeaderReduceClosure("close");
        $keepAliveReduce ??= self::makeHeaderReduceClosure("keep-alive");

        $headers = $response->getHeaders();

        if ($response->getStatus() < HttpStatus::OK) {
            unset($headers['content-length']); // 1xx responses do not have a body.
            return [$headers, null, false];
        }

        foreach ($response->getPushes() as $push) {
            $headers["link"][] = "<{$push->getUri()}>; rel=preload";
        }

        $requestConnectionHeaders = $request?->getHeaderArray("connection") ?? [];
        $responseConnectionHeaders = $headers["connection"] ?? [];

        $contentLength = $headers["content-length"][0] ?? null;
        if ($contentLength !== null) {
            $contentLength = (int) $contentLength;
        }

        $shouldClose = $request === null
            || \array_reduce($requestConnectionHeaders, $closeReduce, false)
            || \array_reduce($responseConnectionHeaders, $closeReduce, false)
            || $protocol === "1.0" && !\array_reduce($requestConnectionHeaders, $keepAliveReduce, false);

        if ($contentLength !== null) {
            unset($headers["transfer-encoding"]);
        } elseif ($protocol === "1.1") {
            unset($headers["content-length"]);
        } else {
            $shouldClose = true;
        }

        if ($shouldClose) {
            $headers["connection"] = ["close"];
        } else {
            $headers["connection"] = ["keep-alive"];
            $headers["keep-alive"] = ["timeout=" . $this->connectionTimeout];
        }

        $headers["date"] = [formatDateHeader()];

        return [$headers, $contentLength, $shouldClose];
    }

    /**
     * Invokes the upgrade handler of the Response with the socket upgraded from the HTTP server.
     */
    private function upgrade(Request $request, Response $response): void
    {
        $upgradeHandler = $response->getUpgradeHandler();
        if (!$upgradeHandler) {
            throw new \Error('Response was not upgraded');
        }

        $this->continue = false;

        $client = $request->getClient();

        $this->removeTimeout();

        $stream = $this->currentBuffer === ''
            ? $this->readableStream
            : new ReadableStreamChain(new ReadableBuffer($this->currentBuffer), $this->readableStream);

        $socket = new UpgradedSocket($client, $stream, $this->writableStream);

        try {
            $upgradeHandler($socket, $request, $response);
        } catch (\Throwable $exception) {
            $exceptionClass = $exception::class;

            $this->logger->error(
                "Unexpected {$exceptionClass} thrown during socket upgrade, closing connection.",
                ['exception' => $exception]
            );

            $client->close();
        }
    }

    /**
     * Creates a service unavailable response from the error handler and sends that response to the client.
     *
     * @return Future<void>
     */
    private function sendServiceUnavailableResponse(?Request $request): Future
    {
        $response = $this->errorHandler->handleError(HttpStatus::SERVICE_UNAVAILABLE, request: $request);
        $response->setHeader("connection", "close");

        return $this->lastWrite = async($this->send(...), $this->lastWrite, $response);
    }

    /**
     * Creates an error response from the error handler and sends that response to the client.
     *
     * @return Future<void>
     */
    private function sendErrorResponse(ClientException $exception, ?Request $request): Future
    {
        $message = $exception->getMessage();
        $status = $exception->getCode();

        if (!(HttpStatus::isClientError($status) || HttpStatus::isServerError($status))) {
            $status = HttpStatus::BAD_REQUEST;
        }

        $response = $this->errorHandler->handleError($status, $message, $request);
        $response->setHeader("connection", "close");

        return $this->lastWrite = async($this->send(...), $this->lastWrite, $response);
    }

    #[\Override]
    public function getPendingRequestCount(): int
    {
        if ($this->bodyQueue) {
            return 1;
        }

        if ($this->http2driver) {
            return $this->http2driver->getPendingRequestCount();
        }

        return 0;
    }

    #[\Override]
    public function stop(): void
    {
        $this->stopping = true;

        $this->pendingResponse->await();
        $this->lastWrite?->await();
        $this->deferredCancellation->cancel();
    }

    #[\Override]
    public function getApplicationLayerProtocols(): array
    {
        return ['http/1.1'];
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Server\Driver;

use Amp\Socket\BindContext;
use Amp\Socket\ResourceServerSocketFactory;
use Amp\Socket\ServerSocket;
use Amp\Socket\ServerSocketFactory;
use Amp\Socket\SocketAddress;
use Amp\Sync\Semaphore;

final class ConnectionLimitingServerSocketFactory implements ServerSocketFactory
{
    public function __construct(
        private readonly Semaphore $semaphore,
        private readonly ServerSocketFactory $socketServerFactory = new ResourceServerSocketFactory(),
    ) {
    }

    #[\Override]
    public function listen(SocketAddress|string $address, ?BindContext $bindContext = null): ServerSocket
    {
        return new ConnectionLimitingServerSocket(
            $this->socketServerFactory->listen($address, $bindContext),
            $this->semaphore,
        );
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Server\Driver\Internal;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Http\Server\Driver\Client;
use function Amp\async;
use function Amp\weakClosure;

/** @internal */
final class StreamTimeoutTracker
{
    use ForbidCloning;
    use ForbidSerialization;

    private readonly \Closure $onStreamTimeout;

    /** @var array<int, \Closure(int): void> */
    private array $callbacks = [];

    /** @psalm-suppress UnusedProperty False-positive. */
    private int $pingTimeout = 0;

    /**
     * @param \Closure():void $onConnectionTimeout
     */
    public function __construct(
        private readonly Client $client,
        private readonly TimeoutQueue $timeoutQueue,
        private readonly int $connectionTimeout,
        private readonly int $streamTimeout,
        \Closure $onConnectionTimeout,
    ) {
        $this->onStreamTimeout = weakClosure(function (Client $client, int $streamId): void {
            \assert(isset($this->callbacks[$streamId]), "Callback missing for stream ID " . $streamId);

            $callback = $this->callbacks[$streamId];
            unset($this->callbacks[$streamId]);

            async($callback, $streamId)->ignore();

            if (!$this->callbacks) {
                $this->timeoutQueue->update($this->client, 0, \min(0, $this->pingTimeout - \time()));
            }
        });

        $timeoutQueue->insert($this->client, 0, $onConnectionTimeout, $this->connectionTimeout);
    }

    public function __destruct()
    {
        $this->timeoutQueue->remove($this->client, 0);
    }

    public function ping(int $timeout): void
    {
        $this->pingTimeout = \time() + $timeout;

        if (!$this->callbacks) {
            $this->timeoutQueue->update($this->client, 0, $timeout);
        }
    }

    public function insert(int $streamId, \Closure $onTimeout): void
    {
        \assert($streamId > 0 && $streamId & 1);

        $this->timeoutQueue->insert($this->client, $streamId, $this->onStreamTimeout, $this->streamTimeout);
        $this->callbacks[$streamId] = $onTimeout;
        $this->timeoutQueue->suspend($this->client, 0);
    }

    public function update(int $streamId): void
    {
        \assert($streamId > 0 && $streamId & 1);

        $this->timeoutQueue->update($this->client, $streamId, $this->streamTimeout);
    }

    public function suspend(int $streamId): void
    {
        \assert($streamId > 0 && $streamId & 1);

        $this->timeoutQueue->suspend($this->client, $streamId);
    }

    public function remove(int $streamId): void
    {
        \assert($streamId > 0 && $streamId & 1);

        $this->timeoutQueue->remove($this->client, $streamId);
        unset($this->callbacks[$streamId]);

        if (!$this->callbacks) {
            $this->timeoutQueue->update($this->client, 0, $this->connectionTimeout);
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Server\Driver\Internal;

use Amp\Http\Server\ClientException;
use Amp\Http\Server\DefaultExceptionHandler;
use Amp\Http\Server\Driver\HttpDriver;
use Amp\Http\Server\ErrorHandler;
use Amp\Http\Server\ExceptionHandler;
use Amp\Http\Server\HttpErrorException;
use Amp\Http\Server\Middleware\ExceptionHandlerMiddleware;
use Amp\Http\Server\Request;
use Amp\Http\Server\RequestHandler;
use Amp\Http\Server\Response;
use Psr\Log\LoggerInterface as PsrLogger;

/** @internal */
abstract class AbstractHttpDriver implements HttpDriver
{
    protected const HOST_HEADER_REGEX = /** @lang RegExp */ '#^([A-Z\d._\-]+|\[[\dA-F:]+])(?::([1-9]\d*))?$#i';

    private static ?TimeoutQueue $timeoutQueue = null;

    final protected static function getTimeoutQueue(): TimeoutQueue
    {
        return self::$timeoutQueue ??= new TimeoutQueue();
    }

    private readonly DefaultExceptionHandler $exceptionHandler;

    private int $pendingRequestHandlerCount = 0;
    private int $pendingResponseCount = 0;

    protected readonly ErrorHandler $errorHandler;

    protected function __construct(
        protected readonly RequestHandler $requestHandler,
        ErrorHandler $errorHandler,
        protected readonly PsrLogger $logger,
    ) {
        $this->errorHandler = new HttpDriverErrorHandler($errorHandler, $this->logger);
        $this->exceptionHandler = new DefaultExceptionHandler($this->errorHandler, $this->logger);
    }

    /**
     * Respond to a parsed request.
     */
    final protected function handleRequest(Request $request): void
    {
        $clientRequest = $request;
        $request = clone $request;

        $this->pendingRequestHandlerCount++;
        $this->pendingResponseCount++;

        try {
            $response = $this->requestHandler->handleRequest($request);
        } catch (ClientException $exception) {
            throw $exception;
        } catch (HttpErrorException $exception) {
            $response = $this->errorHandler->handleError($exception->getStatus(), $exception->getReason(), $request);
        } catch (\Throwable $exception) {
            /**
             * This catch is not designed to be a general-purpose exception handler, rather a last-resort to write to
             * the logger if the application has failed to handle an exception thrown from a {@see RequestHandler}.
             * Instead of relying on this handler, use {@see ExceptionHandler} and {@see ExceptionHandlerMiddleware}.
             */
            $response = $this->exceptionHandler->handleException($request, $exception);
        } finally {
            $this->pendingRequestHandlerCount--;
        }

        /** @psalm-suppress RedundantCondition */
        \assert($this->logger->debug(\sprintf(
            '"%s %s" %d "%s" HTTP/%s @ %s #%d',
            $clientRequest->getMethod(),
            (string) $clientRequest->getUri(),
            $response->getStatus(),
            $response->getReason(),
            $clientRequest->getProtocolVersion(),
            $clientRequest->getClient()->getRemoteAddress()->toString(),
            $clientRequest->getClient()->getId(),
        )) || true);

        $this->write($clientRequest, $response);

        $this->pendingResponseCount--;
    }

    /**
     * Write the given response to the client.
     */
    abstract protected function write(Request $request, Response $response): void;
}
<?php declare(strict_types=1);

namespace Amp\Http\Server\Driver\Internal;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Http\Server\Driver\Client;
use Amp\Sync\PriorityQueue;
use Revolt\EventLoop;
use function Amp\async;
use function Amp\weakClosure;

/** @internal */
final class TimeoutQueue
{
    use ForbidCloning;
    use ForbidSerialization;

    private readonly PriorityQueue $priorityQueue;

    private readonly \WeakMap $streamNames;

    private readonly string $callbackId;

    private int $now;

    /** @var array<string, array{Client, int, \Closure(Client, int):void}> */
    private array $callbacks = [];

    public function __construct()
    {
        $this->priorityQueue = new PriorityQueue();
        $this->streamNames = new \WeakMap();
        $this->now = \time();

        $this->callbackId = EventLoop::unreference(
            EventLoop::repeat(1, weakClosure(function (): void {
                $this->now = \time();

                while ($id = $this->priorityQueue->extract($this->now)) {
                    \assert(isset($this->callbacks[$id]), "Timeout cache contains an invalid client ID");

                    // Client is either idle or taking too long to send request, so simply close the connection.
                    [$client, $streamId, $onTimeout] = $this->callbacks[$id];

                    async($onTimeout, $client, $streamId)->ignore();
                }
            }))
        );
    }

    public function __destruct()
    {
        EventLoop::cancel($this->callbackId);
    }

    /**
     * Insert a client and stream pair. The given closure is invoked when the timeout elapses.
     *
     * @param \Closure(Client, int):void $onTimeout
     */
    public function insert(Client $client, int $streamId, \Closure $onTimeout, int $timeout): void
    {
        $cacheId = $this->makeId($client, $streamId);
        \assert(!isset($this->callbacks[$cacheId]));

        $this->callbacks[$cacheId] = [$client, $streamId, $onTimeout];
        $this->priorityQueue->insert($cacheId, $this->now + $timeout);
    }

    private function makeId(Client $client, int $streamId): string
    {
        /** @psalm-suppress InaccessibleProperty $streamNames is a WeakMap */
        $streamMap = $this->streamNames[$client] ??= new \ArrayObject();
        return $streamMap[$streamId] ??= $client->getId() . ':' . $streamId;
    }

    /**
     * Update the timeout for the associated client and stream ID.
     */
    public function update(Client $client, int $streamId, int $timeout): void
    {
        $cacheId = $this->makeId($client, $streamId);
        \assert(isset($this->callbacks[$cacheId], $this->streamNames[$client][$streamId]));

        $this->priorityQueue->insert($cacheId, $this->now + $timeout);
    }

    public function suspend(Client $client, int $streamId): void
    {
        $cacheId = $this->makeId($client, $streamId);
        \assert(isset($this->callbacks[$cacheId], $this->streamNames[$client][$streamId]));

        $this->priorityQueue->remove($cacheId);
    }

    /**
     * Remove the given stream ID.
     */
    public function remove(Client $client, int $streamId): void
    {
        $cacheId = $this->makeId($client, $streamId);

        $this->priorityQueue->remove($cacheId);
        unset($this->callbacks[$cacheId], $this->streamNames[$client][$streamId]);
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Server\Driver\Internal;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Http\Server\DefaultErrorHandler;
use Amp\Http\Server\ErrorHandler;
use Amp\Http\Server\Request;
use Amp\Http\Server\Response;
use Psr\Log\LoggerInterface as PsrLogger;

/** @internal */
final class HttpDriverErrorHandler implements ErrorHandler
{
    use ForbidCloning;
    use ForbidSerialization;

    private static ?DefaultErrorHandler $defaultErrorHandler = null;

    private static function getDefaultErrorHandler(): ErrorHandler
    {
        return self::$defaultErrorHandler ??= new DefaultErrorHandler();
    }

    public function __construct(
        private readonly ErrorHandler $errorHandler,
        private readonly PsrLogger $logger,
    ) {
    }

    #[\Override]
    public function handleError(int $status, ?string $reason = null, ?Request $request = null): Response
    {
        try {
            return $this->errorHandler->handleError($status, $reason, $request);
        } catch (\Throwable $exception) {
            // If the error handler throws, fallback to returning the default error page.
            $this->logger->error(
                \sprintf(
                    "Unexpected %s thrown from %s::handleError(), falling back to default error handler.",
                    $exception::class,
                    $this->errorHandler::class,
                ),
                ['exception' => $exception],
            );

            // The default error handler will never throw, otherwise there's a bug
            return self::getDefaultErrorHandler()->handleError($status, null, $request);
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Server\Driver\Internal;

use Amp\DeferredCancellation;
use Amp\DeferredFuture;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Future;

/**
 * Used in Http2Driver.
 *
 * @internal
 * @psalm-suppress PossiblyUnusedProperty False-positive.
 */
final class Http2Stream
{
    use ForbidCloning;
    use ForbidSerialization;

    public const OPEN = 0;
    public const RESERVED = 0b0001;
    public const REMOTE_CLOSED = 0b0010;
    public const LOCAL_CLOSED = 0b0100;

    /** @var int Bytes received on the stream. */
    public int $receivedByteCount = 0;

    public ?Future $pendingResponse = null;

    public ?Future $pendingWrite = null;

    public string $buffer = "";

    public ?DeferredFuture $deferredFuture = null;

    /** @var int Integer between 1 and 256 */
    public int $weight = 0;

    public int $dependency = 0;

    public ?int $expectedLength = null;

    public readonly DeferredCancellation $deferredCancellation;

    public function __construct(
        public int $bodySizeLimit,
        public int $serverWindow,
        public int $clientWindow,
        public int $state = self::OPEN,
    ) {
        $this->deferredCancellation = new DeferredCancellation();
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Server\Driver;

use Amp\ByteStream\ReadableStream;
use Amp\ByteStream\WritableStream;

interface HttpDriver
{
    public const DEFAULT_CONNECTION_TIMEOUT = 60;
    public const DEFAULT_STREAM_TIMEOUT = 15;
    public const DEFAULT_HEADER_SIZE_LIMIT = 32768;
    public const DEFAULT_BODY_SIZE_LIMIT = 131072;

    /**
     * @return string[] A list of supported application-layer protocols (ALPNs).
     */
    public function getApplicationLayerProtocols(): array;

    /**
     * Set up the driver.
     */
    public function handleClient(
        Client $client,
        ReadableStream $readableStream,
        WritableStream $writableStream,
    ): void;

    /**
     * @return int Number of requests that are being read by the parser.
     */
    public function getPendingRequestCount(): int;

    /**
     * Stops processing further requests, returning once all currently pending requests have been fulfilled and any
     * remaining data is sent to the client (such as GOAWAY frames for HTTP/2).
     */
    public function stop(): void;
}
<?php declare(strict_types=1);

namespace Amp\Http\Server\Driver;

use Amp\Socket\Socket;
use Amp\Socket\SocketException;

interface ClientFactory
{
    /**
     * Create a client object for the given Socket, enabling TLS if necessary or configuring other socket options.
     *
     * @throws SocketException
     */
    public function createClient(Socket $socket): ?Client;
}
<?php declare(strict_types=1);

namespace Amp\Http\Server\Driver;

use Amp\Cancellation;
use Amp\Socket\BindContext;
use Amp\Socket\ServerSocket;
use Amp\Socket\Socket;
use Amp\Socket\SocketAddress;
use Amp\Sync\Semaphore;

final class ConnectionLimitingServerSocket implements ServerSocket
{
    public function __construct(
        private readonly ServerSocket $socketServer,
        private readonly Semaphore $semaphore,
    ) {
    }

    #[\Override]
    public function accept(?Cancellation $cancellation = null): ?Socket
    {
        $lock = $this->semaphore->acquire();

        $socket = $this->socketServer->accept($cancellation);
        if (!$socket) {
            $lock->release();
            return null;
        }

        $socket->onClose($lock->release(...));

        return $socket;
    }

    #[\Override]
    public function close(): void
    {
        $this->socketServer->close();
    }

    #[\Override]
    public function isClosed(): bool
    {
        return $this->socketServer->isClosed();
    }

    #[\Override]
    public function onClose(\Closure $onClose): void
    {
        $this->socketServer->onClose($onClose);
    }

    #[\Override]
    public function getAddress(): SocketAddress
    {
        return $this->socketServer->getAddress();
    }

    #[\Override]
    public function getBindContext(): BindContext
    {
        return $this->socketServer->getBindContext();
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Server\Driver;

use Amp\Socket\Socket;
use Amp\Socket\SocketAddress;
use Amp\Socket\TlsInfo;

final class SocketClient implements Client
{
    private readonly int $id;

    public function __construct(
        private readonly Socket $socket,
    ) {
        $this->id = createClientId();
    }

    #[\Override]
    public function getId(): int
    {
        return $this->id;
    }

    #[\Override]
    public function getRemoteAddress(): SocketAddress
    {
        return $this->socket->getRemoteAddress();
    }

    #[\Override]
    public function getLocalAddress(): SocketAddress
    {
        return $this->socket->getLocalAddress();
    }

    #[\Override]
    public function getTlsInfo(): ?TlsInfo
    {
        return $this->socket->getTlsInfo();
    }

    #[\Override]
    public function close(): void
    {
        $this->socket->close();
    }

    #[\Override]
    public function onClose(\Closure $onClose): void
    {
        $this->socket->onClose($onClose);
    }

    #[\Override]
    public function isClosed(): bool
    {
        return $this->socket->isClosed();
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Server\Driver;

function createClientId(): int
{
    static $nextId = 0;

    return $nextId++;
}
<?php declare(strict_types=1);

namespace Amp\Http\Server;

use Amp\ByteStream\BufferException;
use Amp\ByteStream\Payload;
use Amp\ByteStream\ReadableStream;
use Amp\ByteStream\ReadableStreamIteratorAggregate;
use Amp\ByteStream\StreamException;
use Amp\Cancellation;

/**
 * This class allows streamed and buffered access to a request body with an API similar to {@see Payload}.
 *
 * The {@see read()} and {@see buffer()} methods can throw {@see ClientException}, which extends
 * {@see StreamException}, though generally there is no need to catch this exception.
 *
 * Additionally, this class allows increasing the body size limit dynamically.
 *
 * @implements \IteratorAggregate<int, string>
 */
final class RequestBody implements ReadableStream, \IteratorAggregate, \Stringable
{
    use ReadableStreamIteratorAggregate;

    private readonly Payload $stream;

    /**
     * @param null|\Closure(int):void $upgradeSize Closure used to increase the maximum size of the body.
     */
    public function __construct(
        ReadableStream|string $stream,
        private readonly ?\Closure $upgradeSize = null,
    ) {
        $this->stream = new Payload($stream);
    }

    /**
     * @throws ClientException
     */
    #[\Override]
    public function read(?Cancellation $cancellation = null): ?string
    {
        return $this->stream->read($cancellation);
    }

    /**
     * @see Payload::buffer()
     * @throws ClientException|BufferException
     */
    public function buffer(?Cancellation $cancellation = null, int $limit = \PHP_INT_MAX): string
    {
        return $this->stream->buffer($cancellation, $limit);
    }

    #[\Override]
    public function isReadable(): bool
    {
        return $this->stream->isReadable();
    }

    /**
     * Indicates the remainder of the request body is no longer needed and will be discarded.
     */
    #[\Override]
    public function close(): void
    {
        $this->stream->close();
    }

    #[\Override]
    public function isClosed(): bool
    {
        return $this->stream->isClosed();
    }

    #[\Override]
    public function onClose(\Closure $onClose): void
    {
        $this->stream->onClose($onClose);
    }

    /**
     * Set a new maximum length of the body in bytes.
     */
    public function increaseSizeLimit(int $size): void
    {
        if ($this->upgradeSize === null) {
            return;
        }

        ($this->upgradeSize)($size);
    }

    /**
     * Buffers entire stream before returning. Use {@see self::buffer()} to optionally provide a {@see Cancellation}
     * and/or length limit.
     *
     * @throws ClientException|BufferException
     */
    #[\Override]
    public function __toString(): string
    {
        return $this->buffer();
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Server;

use Amp\Http\HttpStatus;

/**
 * Error handler that sends a simple HTML error page.
 */
final class DefaultErrorHandler implements ErrorHandler
{
    private static ?string $errorHtml = null;

    /** @var array<int, string> */
    private static array $cache = [];

    #[\Override]
    public function handleError(int $status, ?string $reason = null, ?Request $request = null): Response
    {
        self::$errorHtml ??= \file_get_contents(\dirname(__DIR__) . "/resources/error.html");

        /** @psalm-suppress PossiblyFalseArgument */
        $body = self::$cache[$status] ??= \str_replace(
            ["{code}", "{reason}"],
            // Using standard reason in HTML for caching purposes.
            [$status, HttpStatus::getReason($status)],
            self::$errorHtml,
        );

        $response = new Response(
            headers: [
                "content-type" => "text/html; charset=utf-8",
            ],
            body: $body,
        );

        $response->setStatus($status, $reason);

        return $response;
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Server;

interface HttpServer
{
    public function start(RequestHandler $requestHandler, ErrorHandler $errorHandler): void;

    public function stop(): void;

    /**
     * @param \Closure(HttpServer):void $onStart
     */
    public function onStart(\Closure $onStart): void;

    /**
     * @param \Closure(HttpServer):void $onStop
     */
    public function onStop(\Closure $onStop): void;

    public function getStatus(): HttpServerStatus;
}
<?php declare(strict_types=1);

namespace Amp\Http\Server\Middleware;

use Amp\Http\HttpStatus;
use Amp\Http\Server\ErrorHandler;
use Amp\Http\Server\Middleware;
use Amp\Http\Server\Request;
use Amp\Http\Server\RequestHandler;
use Amp\Http\Server\Response;
use Psr\Log\LoggerInterface as PsrLogger;

final class AllowedMethodsMiddleware implements Middleware
{
    /**
     * HTTP methods that are *known*.
     *
     * Requests for methods not defined here or within Options should result in a 501 (not implemented) response.
     */
    private const KNOWN_METHODS = [
        "GET" => true,
        "HEAD" => true,
        "POST" => true,
        "PUT" => true,
        "PATCH" => true,
        "DELETE" => true,
        "OPTIONS" => true,
        "TRACE" => true,
        "CONNECT" => true,
    ];

    public const DEFAULT_ALLOWED_METHODS = ["GET", "POST", "PUT", "PATCH", "HEAD", "OPTIONS", "DELETE"];

    /**
     * @param array<non-empty-string> $allowedMethods
     */
    public function __construct(
        private readonly ErrorHandler $errorHandler,
        private readonly PsrLogger $logger,
        private readonly array $allowedMethods = self::DEFAULT_ALLOWED_METHODS,
    ) {
    }

    #[\Override]
    public function handleRequest(Request $request, RequestHandler $requestHandler): Response
    {
        $method = $request->getMethod();

        if (!\in_array($method, $this->allowedMethods, true)) {
            return $this->handleInvalidMethod(
                $request,
                isset(self::KNOWN_METHODS[$method]) ? HttpStatus::METHOD_NOT_ALLOWED : HttpStatus::NOT_IMPLEMENTED,
            );
        }

        if ($method === "OPTIONS" && $request->getUri()->getPath() === "") {
            return $this->handleAsteriskOptionsRequest();
        }

        return $requestHandler->handleRequest($request);
    }

    /**
     * @return list<non-empty-string>
     */
    public function getAllowedMethods(): array
    {
        return \array_values($this->allowedMethods);
    }

    private function handleInvalidMethod(Request $request, int $status): Response
    {
        $client = $request->getClient();

        $local = $client->getLocalAddress()->toString();
        $remote = $client->getRemoteAddress()->toString();
        $method = $request->getMethod();
        $uri = (string) $request->getUri();
        $protocolVersion = $request->getProtocolVersion();

        $context = [
            'method' => $method,
            'uri' => $uri,
            'protocolVersion' => $protocolVersion,
            'local' => $local,
            'remote' => $remote,
        ];

        $this->logger->warning(\sprintf(
            "Invalid request method: %s %s HTTP/%s %s on %s",
            $method,
            $uri,
            $protocolVersion,
            $local,
            $remote,
        ), $context);

        $response = $this->errorHandler->handleError($status);
        $response->setHeader("allow", \implode(", ", $this->allowedMethods));
        return $response;
    }

    private function handleAsteriskOptionsRequest(): Response
    {
        return new Response(HttpStatus::NO_CONTENT, [
            "allow" => \implode(", ", $this->allowedMethods),
        ]);
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Server\Middleware;

use Amp\Http\Server\ClientException;
use Amp\Http\Server\Middleware;
use Amp\Http\Server\Request;
use Amp\Http\Server\RequestHandler;
use Amp\Http\Server\Response;
use Psr\Log\LoggerInterface as PsrLogger;
use Psr\Log\LogLevel;

final class AccessLoggerMiddleware implements Middleware
{
    public function __construct(
        private readonly PsrLogger $logger,
    ) {
    }

    #[\Override]
    public function handleRequest(Request $request, RequestHandler $requestHandler): Response
    {
        $client = $request->getClient();

        $local = $client->getLocalAddress()->toString();
        $remote = $client->getRemoteAddress()->toString();
        $method = $request->getMethod();
        $uri = (string) $request->getUri();
        $protocolVersion = $request->getProtocolVersion();

        $context = [
            'method' => $method,
            'uri' => $uri,
            'protocolVersion' => $protocolVersion,
            'local' => $local,
            'remote' => $remote,
        ];

        try {
            $response = $requestHandler->handleRequest($request);
        } catch (ClientException $exception) {
            $this->logger->warning(\sprintf(
                'Client exception for "%s %s" HTTP/%s %s',
                $method,
                $uri,
                $protocolVersion,
                $remote,
            ), $context);

            throw $exception;
        }

        $status = $response->getStatus();
        $reason = $response->getReason();

        $context = [
            'request' => $context,
            'response' => [
                'status' => $status,
                'reason' => $reason,
            ]
        ];

        $level = $status < 400 ? LogLevel::INFO : LogLevel::NOTICE;

        $this->logger->log($level, \sprintf(
            '"%s %s" %d "%s" HTTP/%s %s on %s',
            $method,
            $uri,
            $status,
            $reason,
            $protocolVersion,
            $remote,
            $local,
        ), $context);

        return $response;
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Server\Middleware;

use Amp\Http\Server\ClientException;
use Amp\Http\Server\ExceptionHandler;
use Amp\Http\Server\HttpErrorException;
use Amp\Http\Server\Middleware;
use Amp\Http\Server\Request;
use Amp\Http\Server\RequestHandler;
use Amp\Http\Server\Response;

/**
 * This middleware catches exceptions from the wrapped {@see RequestHandler}, delegating handling of the exception to
 * the provided instance of {@see ExceptionHandler}. Generally it is recommended that this middleware be first in the
 * middleware stack so it is able to catch any exception from another middleware or request handler.
 */
final class ExceptionHandlerMiddleware implements Middleware
{
    public function __construct(private readonly ExceptionHandler $exceptionHandler)
    {
    }

    #[\Override]
    public function handleRequest(Request $request, RequestHandler $requestHandler): Response
    {
        try {
            return $requestHandler->handleRequest($request);
        } catch (ClientException|HttpErrorException $exception) {
            // Rethrow our special client exception or HTTP error exception. These exceptions have special meaning
            // to the HTTP driver, so will be handled differently from other uncaught exceptions from the request
            // handler.
            throw $exception;
        } catch (\Throwable $exception) {
            return $this->exceptionHandler->handleException($request, $exception);
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Server\Middleware;

enum ForwardedHeaderType
{
    case Forwarded;
    case XForwardedFor;

    /**
     * @return non-empty-string
     */
    public function getHeaderName(): string
    {
        return match ($this) {
            self::Forwarded => 'forwarded',
            self::XForwardedFor => 'x-forwarded-for',
        };
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Server\Middleware;

use Amp\Http\Server\Middleware;
use Amp\Http\Server\Request;
use Amp\Http\Server\RequestHandler;
use Amp\Http\Server\Response;

final class ClosureMiddleware implements Middleware
{
    /**
     * @param \Closure(Request, RequestHandler):Response $closure Closure accepting an {@see Request} object
     * as the first argument, {@see RequestHandler} as second argument and returning an instance of {@see Response}.
     */
    public function __construct(private readonly \Closure $closure)
    {
    }

    #[\Override]
    public function handleRequest(Request $request, RequestHandler $requestHandler): Response
    {
        return ($this->closure)($request, $requestHandler);
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Server\Middleware\Internal;

use Amp\Http\Server\Middleware;
use Amp\Http\Server\Request;
use Amp\Http\Server\RequestHandler;
use Amp\Http\Server\Response;

/**
 * Wraps a request handler with a single middleware.
 *
 * @see stackMiddleware()
 * @internal
 */
final class MiddlewareRequestHandler implements RequestHandler
{
    public function __construct(
        private readonly Middleware $middleware,
        private readonly RequestHandler $requestHandler,
    ) {
    }

    #[\Override]
    public function handleRequest(Request $request): Response
    {
        return $this->middleware->handleRequest($request, $this->requestHandler);
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Server\Middleware;

use Amp\ByteStream\ReadableIterableStream;
use Amp\ByteStream\ReadableStream;
use Amp\Cache\LocalCache;
use Amp\CancelledException;
use Amp\Http\Server\Middleware;
use Amp\Http\Server\Request;
use Amp\Http\Server\RequestHandler;
use Amp\Http\Server\Response;
use Amp\TimeoutCancellation;

final class CompressionMiddleware implements Middleware
{
    private static ?\Closure $errorHandler;

    public const MAX_CACHE_SIZE = 1024;

    /** @link http://webmasters.stackexchange.com/questions/31750/what-is-recommended-minimum-object-size-for-deflate-performance-benefits */
    public const DEFAULT_MINIMUM_LENGTH = 860;
    public const DEFAULT_BUFFER_TIMEOUT = 0.1;
    public const DEFAULT_CONTENT_TYPE_REGEX = '#^(?:text/.*+|[^/]*+/xml|[^+]*\+xml|application/(?:json|(?:x-)?javascript))$#i';

    /** @var LocalCache<bool> */
    private readonly LocalCache $contentTypeCache;

    /**
     * @param positive-int $minimumLength Minimum body length before body is compressed.
     * @param non-empty-string $contentRegex Content-Type regex.
     */
    public function __construct(
        private readonly int $minimumLength = self::DEFAULT_MINIMUM_LENGTH,
        private readonly string $contentRegex = self::DEFAULT_CONTENT_TYPE_REGEX,
        private readonly float $bufferTimeout = self::DEFAULT_BUFFER_TIMEOUT,
    ) {
        if (!\extension_loaded('zlib')) {
            throw new \Error(__CLASS__ . ' requires ext-zlib');
        }

        /** @psalm-suppress DocblockTypeContradiction */
        if ($minimumLength < 1) {
            throw new \Error("The minimum length must be positive");
        }

        if ($bufferTimeout <= 0) {
            throw new \Error("The buffer timeout must be positive");
        }

        $this->contentTypeCache = new LocalCache(self::MAX_CACHE_SIZE);
    }

    #[\Override]
    public function handleRequest(Request $request, RequestHandler $requestHandler): Response
    {
        $response = $requestHandler->handleRequest($request);

        $headers = $response->getHeaders();

        if (isset($headers["content-encoding"])) {
            return $response; // Another request handler or middleware has already encoded the response.
        }

        $contentLength = $headers["content-length"][0] ?? null;

        if ($contentLength !== null) {
            if ($contentLength < $this->minimumLength) {
                return $response; // Content-Length too small, no need to compress.
            }
        }

        // We can't deflate if we don't know the content-type
        if (empty($headers["content-type"])) {
            return $response;
        }

        $contentType = $headers["content-type"][0];

        $weight = 0;
        foreach ($request->getHeaderArray("accept-encoding") as $values) {
            foreach (\array_map("trim", \explode(",", $values)) as $value) {
                if (\preg_match('/^(gzip|deflate)(?:;q=(1(?:\.0{1,3})?|0(?:\.\d{1,3})?))?$/i', $value, $matches)) {
                    $qValue = (float) ($matches[2] ?? 1);
                    if ($qValue <= $weight) {
                        continue;
                    }

                    $weight = $qValue;
                    $encoding = \strtolower($matches[1]);
                }
            }
        }

        if (!isset($encoding)) {
            return $response;
        }

        $doDeflate = $this->contentTypeCache->get($contentType);

        if ($doDeflate === null) {
            $doDeflate = \preg_match($this->contentRegex, \trim(\strstr($contentType, ";", true) ?: $contentType));
            $this->contentTypeCache->set($contentType, (bool) $doDeflate);
        }

        if (!$doDeflate) {
            return $response;
        }

        $body = $response->getBody();
        $bodyBuffer = '';

        if ($contentLength === null && !$this->shouldCompress($body, $bodyBuffer)) {
            $response->setBody($bodyBuffer);
            return $response;
        }

        $mode = match ($encoding) {
            "deflate" => \ZLIB_ENCODING_RAW,
            "gzip" => \ZLIB_ENCODING_GZIP,
            default => throw new \RuntimeException("Invalid encoding type"),
        };

        if (($context = \deflate_init($mode)) === false) {
            throw new \RuntimeException(
                "Failed initializing deflate context"
            );
        }

        // Once we decide to compress output we no longer know what the
        // final Content-Length will be. We need to update our headers
        // according to the HTTP protocol in use to reflect this.
        $response->removeHeader("content-length");
        if ($request->getProtocolVersion() === "1.0") { // Cannot chunk 1.0 responses.
            $response->setHeader("connection", "close");
        }
        $response->setHeader("content-encoding", $encoding);
        $response->addHeader("vary", "accept-encoding");

        /** @psalm-suppress InvalidArgument Psalm stubs are out of date, deflate_init returns a \DeflateContext */
        $response->setBody(
            new ReadableIterableStream(self::readBody($context, $body, $bodyBuffer))
        );

        return $response;
    }

    private function shouldCompress(ReadableStream $body, string &$bodyBuffer): bool
    {
        try {
            $cancellation = new TimeoutCancellation($this->bufferTimeout);

            do {
                $bodyBuffer .= $chunk = $body->read($cancellation);

                if (isset($bodyBuffer[$this->minimumLength])) {
                    return true;
                }

                if ($chunk === null) {
                    return false;
                }
            } while (true);
        } catch (CancelledException) {
            // Not enough bytes buffered within timeout to determine body size, so use compression by default.
        }

        return true;
    }

    /**
     * @psalm-suppress InvalidArgument
     */
    private static function readBody(
        \DeflateContext $context,
        ReadableStream $body,
        string $chunk,
    ): \Generator {
        self::$errorHandler ??= static function (int $code, string $message): never {
            throw new \RuntimeException('Compression error: ' . $message, $code);
        };

        do {
            if ($chunk !== '') {
                \set_error_handler(self::$errorHandler);

                try {
                    $chunk = \deflate_add($context, $chunk, \ZLIB_SYNC_FLUSH);
                } finally {
                    \restore_error_handler();
                }

                yield $chunk;
            }

            $chunk = $body->read();
        } while ($chunk !== null);

        \set_error_handler(self::$errorHandler);

        try {
            $chunk = \deflate_add($context, '', \ZLIB_FINISH);
        } finally {
            \restore_error_handler();
        }

        yield $chunk;
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Server\Middleware;

use Amp\Cache\LocalCache;
use Amp\Http;
use Amp\Http\Server\Middleware;
use Amp\Http\Server\Request;
use Amp\Http\Server\RequestHandler;
use Amp\Http\Server\Response;
use Amp\Socket\CidrMatcher;
use Amp\Socket\InternetAddress;

final class ForwardedMiddleware implements Middleware
{
    /** @var list<CidrMatcher> */
    private readonly array $trustedProxies;

    /** @var LocalCache<bool> */
    private readonly LocalCache $trustedIps;

    /**
     * @param array<non-empty-string> $trustedProxies Array of IPv4 or IPv6 addresses with an optional subnet mask.
     *      e.g., '172.18.0.0/24'
     * @param positive-int $cacheSize
     */
    public function __construct(
        private readonly ForwardedHeaderType $headerType,
        array $trustedProxies,
        int $cacheSize = 1000,
    ) {
        $this->trustedProxies = \array_map(
            static fn (string $ip) => new CidrMatcher($ip),
            \array_values($trustedProxies),
        );

        $this->trustedIps = new LocalCache($cacheSize);
    }

    #[\Override]
    public function handleRequest(Request $request, RequestHandler $requestHandler): Response
    {
        $clientAddress = $request->getClient()->getRemoteAddress();

        if ($clientAddress instanceof InternetAddress && $this->isTrustedProxy($clientAddress)) {
            $request->setAttribute(Forwarded::class, match ($this->headerType) {
                ForwardedHeaderType::Forwarded => $this->getForwarded($request),
                ForwardedHeaderType::XForwardedFor => $this->getForwardedFor($request),
            });
        }

        return $requestHandler->handleRequest($request);
    }

    private function isTrustedProxy(InternetAddress $address): bool
    {
        $ip = $address->getAddress();
        $trusted = $this->trustedIps->get($ip);
        if ($trusted !== null) {
            return $trusted;
        }

        $trusted = false;
        foreach ($this->trustedProxies as $matcher) {
            if ($matcher->match($ip)) {
                $trusted = true;
                break;
            }
        }

        $this->trustedIps->set($ip, $trusted);

        return $trusted;
    }

    private function getForwarded(Request $request): ?Forwarded
    {
        $headers = Http\parseMultipleHeaderFields($request, 'forwarded');
        if (!$headers) {
            return null;
        }

        foreach (\array_reverse($headers) as $header) {
            $for = $header['for'] ?? null;
            if ($for === null) {
                continue;
            }

            $address = InternetAddress::tryFromString($this->addPortIfMissing($for));
            if (!$address || $this->isTrustedProxy($address)) {
                continue;
            }

            return new Forwarded($address, $header);
        }

        return null;
    }

    private function addPortIfMissing(string $address): string
    {
        if (!\str_contains($address, ':') || \str_ends_with($address, ']')) {
            $address .= ':0';
        }

        return $address;
    }

    private function getForwardedFor(Request $request): ?Forwarded
    {
        $forwardedFor = Http\splitHeader($request, 'x-forwarded-for');
        if (!$forwardedFor) {
            return null;
        }

        $forwardedFor = \array_map(static function (string $ip): string {
            if (\str_contains($ip, ':')) {
                return '[' . \trim($ip, '[]') . ']:0';
            }

            return $ip . ':0';
        }, $forwardedFor);

        /** @var InternetAddress[] $forwardedFor */
        $forwardedFor = \array_filter(\array_map(InternetAddress::tryFromString(...), $forwardedFor));
        foreach (\array_reverse($forwardedFor) as $for) {
            if (!$this->isTrustedProxy($for)) {
                return new Forwarded($for, [
                    'for' => $for->getAddress(),
                    'host' => $request->getHeader('x-forwarded-host'),
                    'proto' => $request->getHeader('x-forwarded-proto'),
                ]);
            }
        }

        return null;
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Server\Middleware;

use Amp\Socket\InternetAddress;

final class Forwarded
{
    /**
     * @param array<non-empty-string, string|null> $fields
     */
    public function __construct(
        private readonly InternetAddress $for,
        private readonly array $fields,
    ) {
    }

    public function getFor(): InternetAddress
    {
        return $this->for;
    }

    public function getField(string $name): ?string
    {
        return $this->fields[$name] ?? null;
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Server\Middleware;

use Amp\Http\Server\Middleware;
use Amp\Http\Server\Middleware\Internal\MiddlewareRequestHandler;
use Amp\Http\Server\RequestHandler;

/**
 * Wraps a request handler with the given set of middlewares.
 *
 * @param RequestHandler $requestHandler Request handler to wrap.
 * @param Middleware[]   $middlewares Middlewares to apply; order determines the order of application.
 *
 * @return RequestHandler Wrapped request handler.
 */
function stackMiddleware(RequestHandler $requestHandler, Middleware ...$middlewares): RequestHandler
{
    foreach (\array_reverse($middlewares) as $middleware) {
        $requestHandler = new MiddlewareRequestHandler($middleware, $requestHandler);
    }

    return $requestHandler;
}
<?php declare(strict_types=1);

namespace Amp\Http\Server\Middleware;

use Amp\DeferredFuture;
use Amp\Http\Server\Middleware;
use Amp\Http\Server\Request;
use Amp\Http\Server\RequestHandler;
use Amp\Http\Server\Response;

final class ConcurrencyLimitingMiddleware implements Middleware
{
    private int $pendingRequests = 0;

    /** @var \SplQueue<DeferredFuture> */
    private readonly \SplQueue $queue;

    /**
     * @param positive-int $concurrencyLimit
     */
    public function __construct(private readonly int $concurrencyLimit)
    {
        /** @psalm-suppress DocblockTypeContradiction */
        if ($this->concurrencyLimit <= 0) {
            throw new \ValueError('The concurrency limit must be a positive integer');
        }

        $this->queue = new \SplQueue();
    }

    #[\Override]
    public function handleRequest(Request $request, RequestHandler $requestHandler): Response
    {
        if (++$this->pendingRequests > $this->concurrencyLimit) {
            $deferred = new DeferredFuture();
            $this->queue->push($deferred);
            $deferred->getFuture()->await();
        }

        try {
            return $requestHandler->handleRequest($request);
        } finally {
            --$this->pendingRequests;
            if (!$this->queue->isEmpty()) {
                $this->queue->shift()->complete();
            }
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Server;

enum HttpServerStatus
{
    case Starting;
    case Started;
    case Stopping;
    case Stopped;

    public function getLabel(): string
    {
        return match ($this) {
            self::Starting => 'Starting',
            self::Started => 'Started',
            self::Stopping => 'Stopping',
            self::Stopped => 'Stopped',
        };
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Server;

use Amp\Http\HttpStatus;
use Psr\Log\LoggerInterface as PsrLogger;

/**
 * Simple exception handler that writes a message to the logger and returns an error page generated by the provided
 * {@see ErrorHandler}.
 */
final class DefaultExceptionHandler implements ExceptionHandler
{
    public function __construct(
        private readonly ErrorHandler $errorHandler,
        private readonly PsrLogger $logger,
    ) {
    }

    #[\Override]
    public function handleException(Request $request, \Throwable $exception): Response
    {
        $client = $request->getClient();
        $method = $request->getMethod();
        $uri = (string) $request->getUri();
        $protocolVersion = $request->getProtocolVersion();
        $local = $client->getLocalAddress()->toString();
        $remote = $client->getRemoteAddress()->toString();

        $this->logger->error(
            \sprintf(
                "Unexpected %s with message '%s' thrown from %s:%d when handling request: %s %s HTTP/%s %s on %s",
                $exception::class,
                $exception->getMessage(),
                $exception->getFile(),
                $exception->getLine(),
                $method,
                $uri,
                $protocolVersion,
                $remote,
                $local,
            ),
            [
                'exception' => $exception,
                'method' => $method,
                'uri' => $uri,
                'protocolVersion' => $protocolVersion,
                'local' => $local,
                'remote' => $remote,
            ],
        );

        return $this->errorHandler->handleError(HttpStatus::INTERNAL_SERVER_ERROR, request: $request);
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Server;

/**
 * Middlewares allow pre-processing of requests and post-processing of responses.
 *
 * @see stackMiddleware() for how to apply a middleware to a request handler.
 */
interface Middleware
{
    public function handleRequest(Request $request, RequestHandler $requestHandler): Response;
}
<?php declare(strict_types=1);

namespace Amp\Http\Server;

use Amp\ByteStream\StreamException;

/**
 * A ClientException thrown from {@see RequestBody::read()} or {@see RequestBody::buffer()} indicates that the
 * requesting client stream has been closed due to an error or exceeding a server limit such as the body size limit.
 *
 * Applications may optionally catch this exception in request handlers to continue other processing. Users are NOT
 * required to catch it and if left uncaught it will simply end request handler execution. For streaming response bodies
 * in which the handler is also reading the request body, this exception should be caught and used to fail the streaming
 * response body.
 *
 * Throwing a ClientException from a request handler or failing streaming response body will abruptly
 * disconnect a client. It is not recommended to create ClientException instances in a request handler.
 *
 * Responses returned by request handlers after a ClientException has been thrown will be ignored, as a response has
 * already been generated by the error handler.
 *
 * @psalm-suppress ClassMustBeFinal
 */
class ClientException extends StreamException
{
    /** @internal Do not instantiate instances of this exception in your request handlers or middleware! */
    public function __construct(
        private readonly Driver\Client $client,
        string $message,
        int $code = 0,
        ?\Throwable $previous = null,
    ) {
        parent::__construct($message, $code, $previous);
    }

    final public function getClient(): Driver\Client
    {
        return $this->client;
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Server;

interface ErrorHandler
{
    /**
     * @param int $status Error status code, 4xx or 5xx.
     * @param string|null $reason Reason message. Will use the status code's default reason if not provided.
     * @param Request|null $request Null if the error occurred before parsing the request completed.
     */
    public function handleError(int $status, ?string $reason = null, ?Request $request = null): Response;
}
<?php declare(strict_types=1);

namespace Amp\Http\Server;

use Amp\CompositeException;
use Amp\Future;
use Amp\Http\Server\Driver\ClientFactory;
use Amp\Http\Server\Driver\ConnectionLimitingClientFactory;
use Amp\Http\Server\Driver\ConnectionLimitingServerSocketFactory;
use Amp\Http\Server\Driver\DefaultHttpDriverFactory;
use Amp\Http\Server\Driver\HttpDriver;
use Amp\Http\Server\Driver\HttpDriverFactory;
use Amp\Http\Server\Driver\SocketClientFactory;
use Amp\Http\Server\Middleware\AllowedMethodsMiddleware;
use Amp\Http\Server\Middleware\CompressionMiddleware;
use Amp\Http\Server\Middleware\ConcurrencyLimitingMiddleware;
use Amp\Http\Server\Middleware\ExceptionHandlerMiddleware;
use Amp\Http\Server\Middleware\ForwardedHeaderType;
use Amp\Http\Server\Middleware\ForwardedMiddleware;
use Amp\Socket\BindContext;
use Amp\Socket\ResourceServerSocketFactory;
use Amp\Socket\ServerSocket;
use Amp\Socket\ServerSocketFactory;
use Amp\Socket\Socket;
use Amp\Socket\SocketAddress;
use Amp\Socket\SocketException;
use Amp\Sync\LocalSemaphore;
use Psr\Log\LoggerInterface as PsrLogger;
use Revolt\EventLoop;
use function Amp\async;

final class SocketHttpServer implements HttpServer
{
    private const DEFAULT_CONCURRENCY_LIMIT = 1000;
    private const DEFAULT_CONNECTION_LIMIT = 1000;
    private const DEFAULT_CONNECTIONS_PER_IP_LIMIT = 10;

    private HttpServerStatus $status = HttpServerStatus::Stopped;

    private readonly HttpDriverFactory $httpDriverFactory;

    /** @var array<string, array{SocketAddress, BindContext|null}> */
    private array $addresses = [];

    /** @var list<ServerSocket> */
    private array $servers = [];

    /** @var array<int, HttpDriver> */
    private array $drivers = [];

    /** @var list<\Closure(HttpServer):void> */
    private array $onStart = [];

    /** @var list<\Closure(HttpServer):void> */
    private array $onStop = [];

    /**
     * Creates an instance appropriate for direct access by the public.
     *
     * @param positive-int|null $concurrencyLimit Default is {@see self::DEFAULT_CONCURRENCY_LIMIT}.
     *      Use null for no limit.
     * @param positive-int $connectionLimit Default is {@see self::DEFAULT_CONNECTION_LIMIT}.
     * @param positive-int $connectionLimitPerIp Default is {@see self::DEFAULT_CONNECTIONS_PER_IP_LIMIT}.
     * @param array<non-empty-string>|null $allowedMethods Use null to disable request method filtering.
     */
    public static function createForDirectAccess(
        PsrLogger $logger,
        bool $enableCompression = true,
        int $connectionLimit = self::DEFAULT_CONNECTION_LIMIT,
        int $connectionLimitPerIp = self::DEFAULT_CONNECTIONS_PER_IP_LIMIT,
        ?int $concurrencyLimit = self::DEFAULT_CONCURRENCY_LIMIT,
        ?array $allowedMethods = AllowedMethodsMiddleware::DEFAULT_ALLOWED_METHODS,
        ?HttpDriverFactory $httpDriverFactory = null,
        ?ExceptionHandler $exceptionHandler = null,
    ): self {
        $serverSocketFactory = new ConnectionLimitingServerSocketFactory(new LocalSemaphore($connectionLimit));

        $logger->notice(\sprintf("Total client connections are limited to %d.", $connectionLimit));

        $clientFactory = new ConnectionLimitingClientFactory(
            new SocketClientFactory($logger),
            $logger,
            $connectionLimitPerIp,
        );

        $logger->notice(\sprintf(
            "Client connections are limited to %s per IP address (excluding localhost).",
            $connectionLimitPerIp,
        ));

        $middleware = [];

        if ($exceptionHandler) {
            $middleware[] = new ExceptionHandlerMiddleware($exceptionHandler);
        }

        if ($concurrencyLimit !== null) {
            $logger->notice(\sprintf("Request concurrency limited to %s simultaneous requests", $concurrencyLimit));
            $middleware[] = new ConcurrencyLimitingMiddleware($concurrencyLimit);
        }

        if ($enableCompression && $compressionMiddleware = self::createCompressionMiddleware($logger)) {
            $middleware[] = $compressionMiddleware;
        }

        return new self(
            $logger,
            $serverSocketFactory,
            $clientFactory,
            $middleware,
            $allowedMethods,
            $httpDriverFactory,
        );
    }

    /**
     * Creates an instance appropriate for use when behind a proxy service such as nginx. It is not recommended
     * to allow public traffic to access the created server directly. There are no limits on the total number of
     * connections or connections per IP.
     *
     * @param positive-int|null $concurrencyLimit Default is {@see self::DEFAULT_CONCURRENCY_LIMIT}.
     *      Use null for no limit.
     * @param array<non-empty-string> $trustedProxies Array of IPv4 or IPv6 addresses with an optional subnet mask.
     *      e.g., '172.18.0.0/24'
     * @param array<non-empty-string>|null $allowedMethods Use null to disable request method filtering.
     */
    public static function createForBehindProxy(
        PsrLogger $logger,
        ForwardedHeaderType $headerType,
        array $trustedProxies,
        bool $enableCompression = true,
        ?int $concurrencyLimit = self::DEFAULT_CONCURRENCY_LIMIT,
        ?array $allowedMethods = AllowedMethodsMiddleware::DEFAULT_ALLOWED_METHODS,
        ?HttpDriverFactory $httpDriverFactory = null,
        ?ExceptionHandler $exceptionHandler = null,
    ): self {
        $middleware = [];

        if ($exceptionHandler) {
            $middleware[] = new ExceptionHandlerMiddleware($exceptionHandler);
        }

        if ($concurrencyLimit !== null) {
            $middleware[] = new ConcurrencyLimitingMiddleware($concurrencyLimit);
        }

        $middleware[] = new ForwardedMiddleware($headerType, $trustedProxies);

        if ($enableCompression && $compressionMiddleware = self::createCompressionMiddleware($logger)) {
            $middleware[] = $compressionMiddleware;
        }

        return new self(
            $logger,
            new ResourceServerSocketFactory(),
            new SocketClientFactory($logger),
            $middleware,
            $allowedMethods,
            $httpDriverFactory,
        );
    }

    private static function createCompressionMiddleware(PsrLogger $logger): ?CompressionMiddleware
    {
        if (!\extension_loaded('zlib')) {
            $logger->warning(
                'The zlib extension is not loaded which prevents using compression. ' .
                'Either activate the zlib extension or set $enableCompression to false'
            );

            return null;
        }

        return new CompressionMiddleware();
    }

    /**
     * @param array<Middleware> $middleware Default middlewares. You may also use {@see Middleware\stackMiddleware()}
     *      before passing the {@see RequestHandler} to {@see self::start()}.
     * @param array<non-empty-string>|null $allowedMethods Use null to disable request method filtering.
     */
    public function __construct(
        private readonly PsrLogger $logger,
        private readonly ServerSocketFactory $serverSocketFactory,
        private readonly ClientFactory $clientFactory,
        private readonly array $middleware = [],
        private readonly ?array $allowedMethods = AllowedMethodsMiddleware::DEFAULT_ALLOWED_METHODS,
        ?HttpDriverFactory $httpDriverFactory = null,
    ) {
        $this->httpDriverFactory = $httpDriverFactory ?? new DefaultHttpDriverFactory($logger);
    }

    /**
     * Listen for client connections on the given address. The socket server will be created using the
     * {@see ServerSocketFactory} provided to the constructor and the given {@see BindContext}.
     *
     * @throws SocketException
     */
    public function expose(SocketAddress|string $socketAddress, ?BindContext $bindContext = null): void
    {
        if (\is_string($socketAddress)) {
            $socketAddress = SocketAddress\fromString($socketAddress);
        }

        $name = $socketAddress->toString();
        if (isset($this->addresses[$name])) {
            throw new \Error(\sprintf('Already exposing %s on HTTP server', $name));
        }

        $this->addresses[$name] = [$socketAddress, $bindContext];
    }

    /**
     * May only be called when the server is running.
     *
     * @return list<ServerSocket>
     */
    public function getServers(): array
    {
        if ($this->status !== HttpServerStatus::Started) {
            throw new \Error('Cannot get the list of socket servers when the HTTP server is not running');
        }

        return $this->servers;
    }

    #[\Override]
    public function getStatus(): HttpServerStatus
    {
        return $this->status;
    }

    #[\Override]
    public function onStart(\Closure $onStart): void
    {
        $this->onStart[] = $onStart;
    }

    #[\Override]
    public function onStop(\Closure $onStop): void
    {
        $this->onStop[] = $onStop;
    }

    #[\Override]
    public function start(RequestHandler $requestHandler, ErrorHandler $errorHandler): void
    {
        if (empty($this->addresses)) {
            throw new \Error(\sprintf('No bind addresses specified; Call %s::expose() to add some', self::class));
        }

        if ($this->status !== HttpServerStatus::Stopped) {
            throw new \Error("Cannot start server: " . $this->status->getLabel());
        }

        if (\ini_get("zend.assertions") === "1") {
            $this->logger->warning(
                "Running in production with assertions enabled is not recommended; it has a negative impact " .
                "on performance. Disable assertions in php.ini (zend.assertions = -1) for best performance."
            );
        }

        if (\extension_loaded("xdebug")) {
            $this->logger->warning("The 'xdebug' extension is loaded, which has a major impact on performance.");
        }

        $requestHandler = Middleware\stackMiddleware($requestHandler, ...$this->middleware);

        if ($this->allowedMethods !== null) {
            $this->logger->notice(\sprintf(
                'Request methods restricted to %s.',
                \implode(', ', $this->allowedMethods),
            ));

            $requestHandler = Middleware\stackMiddleware(
                $requestHandler,
                new AllowedMethodsMiddleware($errorHandler, $this->logger, $this->allowedMethods),
            );
        }

        $this->logger->debug("Starting server");
        $this->status = HttpServerStatus::Starting;

        try {
            $futures = [];
            foreach ($this->onStart as $onStart) {
                $futures[] = async($onStart, $this);
            }

            [$exceptions] = Future\awaitAll($futures);

            if (!empty($exceptions)) {
                throw new CompositeException($exceptions);
            }

            foreach ($this->addresses as [$address, $bindContext]) {
                $tlsContext = $bindContext?->getTlsContext()?->withApplicationLayerProtocols(
                    $this->httpDriverFactory->getApplicationLayerProtocols(),
                );

                /** @psalm-suppress PropertyTypeCoercion */
                $this->servers[] = $this->serverSocketFactory->listen(
                    $address,
                    $bindContext?->withTlsContext($tlsContext),
                );
            }

            $this->logger->info("Started server");
            $this->status = HttpServerStatus::Started;

            foreach ($this->servers as $server) {
                $scheme = $server->getBindContext()->getTlsContext() !== null ? 'https' : 'http';
                $serverName = $server->getAddress()->toString();

                $this->logger->info("Listening on {$scheme}://{$serverName}/");

                EventLoop::queue($this->accept(...), $server, $requestHandler, $errorHandler);
            }
        } catch (\Throwable $exception) {
            try {
                $this->status = HttpServerStatus::Started;
                $this->stop();
            } finally {
                throw $exception;
            }
        }
    }

    private function accept(
        ServerSocket $server,
        RequestHandler $requestHandler,
        ErrorHandler $errorHandler,
    ): void {
        while ($socket = $server->accept()) {
            EventLoop::queue($this->handleClient(...), $socket, $requestHandler, $errorHandler);
        }
    }

    private function handleClient(
        Socket $socket,
        RequestHandler $requestHandler,
        ErrorHandler $errorHandler,
    ): void {
        try {
            $client = $this->clientFactory->createClient($socket);
            if (!$client) {
                $socket->close();
                return;
            }

            $id = $client->getId();

            if ($this->status !== HttpServerStatus::Started) {
                $client->close();
                return;
            }

            $this->drivers[$id] = $driver = $this->httpDriverFactory->createHttpDriver(
                $requestHandler,
                $errorHandler,
                $client,
            );

            try {
                $driver->handleClient($client, $socket, $socket);
            } finally {
                unset($this->drivers[$id]);
            }
        } catch (\Throwable $exception) {
            $this->logger->error("Exception while handling client {$socket->getRemoteAddress()->toString()}", [
                'address' => $socket->getRemoteAddress(),
                'exception' => $exception,
            ]);

            $socket->close();
        }
    }

    #[\Override]
    public function stop(): void
    {
        if ($this->status === HttpServerStatus::Stopped) {
            return;
        }

        if ($this->status !== HttpServerStatus::Started) {
            throw new \Error("Cannot stop server: " . $this->status->getLabel());
        }

        $this->logger->info("Stopping server");
        $this->status = HttpServerStatus::Stopping;

        foreach ($this->servers as $server) {
            $server->close();
        }

        $futures = [];
        foreach ($this->onStop as $onStop) {
            $futures[] = async($onStop, $this);
        }

        [$onStopExceptions] = Future\awaitAll($futures);

        $futures = [];
        foreach ($this->drivers as $driver) {
            $futures[] = async($driver->stop(...));
        }

        [$driverExceptions] = Future\awaitAll($futures);

        $exceptions = \array_merge($onStopExceptions, $driverExceptions);

        $this->logger->debug("Stopped server");
        $this->status = HttpServerStatus::Stopped;

        $this->servers = [];

        if (!empty($exceptions)) {
            throw new CompositeException($exceptions);
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Server;

use Amp\Http\HttpStatus;

function redirectTo(string $uri, int $statusCode = HttpStatus::SEE_OTHER): Response
{
    return new Response($statusCode, ['location' => $uri]);
}
{
    "name": "amphp/http-server",
    "homepage": "https://github.com/amphp/http-server",
    "description": "A non-blocking HTTP application server for PHP based on Amp.",
    "keywords": [
        "http",
        "server",
        "async",
        "non-blocking",
        "amp",
        "amphp"
    ],
    "license": "MIT",
    "authors": [
        {
            "name": "Daniel Lowrey",
            "email": "rdlowrey@php.net"
        },
        {
            "name": "Bob Weinand"
        },
        {
            "name": "Niklas Keller",
            "email": "me@kelunik.com"
        },
        {
            "name": "Aaron Piotrowski",
            "email": "aaron@trowski.com"
        }
    ],
    "require": {
        "php": ">=8.1",
        "amphp/amp": "^3",
        "amphp/byte-stream": "^2",
        "amphp/cache": "^2",
        "amphp/hpack": "^3",
        "amphp/http": "^2",
        "amphp/pipeline": "^1",
        "amphp/socket": "^2.1",
        "amphp/sync": "^2.2",
        "league/uri": "^7.1",
        "league/uri-interfaces": "^7.1",
        "psr/http-message": "^1 | ^2",
        "psr/log": "^1 | ^2 | ^3",
        "revolt/event-loop": "^1"
    },
    "require-dev": {
        "amphp/phpunit-util": "^3",
        "amphp/http-client": "^5",
        "amphp/log": "^2",
        "amphp/php-cs-fixer-config": "^2",
        "league/uri-components": "^7.1",
        "monolog/monolog": "^3",
        "phpunit/phpunit": "^9",
        "psalm/phar": "6.16.1"
    },
    "minimum-stability": "dev",
    "prefer-stable": true,
    "suggest": {
        "ext-zlib": "Allows GZip compression of response bodies"
    },
    "autoload": {
        "psr-4": {
            "Amp\\Http\\Server\\": "src"
        },
        "files": [
            "src/Driver/functions.php",
            "src/Middleware/functions.php",
            "src/functions.php"
        ]
    },
    "autoload-dev": {
        "psr-4": {
            "Amp\\Http\\Server\\Test\\": "test"
        },
        "files": [
            "test/functions.php"
        ]
    },
    "scripts": {
        "test": "@php -dzend.assertions=1 -dassert.exception=1 ./vendor/bin/phpunit",
        "code-style": "@php ./vendor/bin/php-cs-fixer fix"
    },
    "config": {
        "allow-plugins": {
            "ocramius/package-versions": false
        }
    }
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Error {code}</title>
    <style>
        body, html {
            margin: 0;
            padding: 0;
            background: #eee;
            font-family: monospace;
            display: flex;
            align-items: center;
            justify-content: center;
            height: 100vh;
        }

        .box {
            background: #fff;
            border-radius: 3px;
            padding: 20px 30px;
            max-width: 600px;
            flex-grow: 1;
            font-size: 14px;
            color: #333;
            margin: 0 auto; /* if there's no flex */
            box-shadow: 0 2px 6px rgba(0,0,0,.1);
        }

        h1 {
            color: #111;
            font-size: 20px;
            /* border-bottom: 2px solid #08e; */
            padding: 0 0 0 0;
            margin: 10px 0 8px 0;
        }

        ul {
            padding: 0 0 0 10px;
        }

        li {
            list-style-type: none;
        }

        li::before {
            content: '- ';
        }

        button {
            box-sizing: content-box;
            width: 100%;
            display: block;
            background: #48e;
            color: #fff;
            border-radius: 0 0 2px 2px;
            border: 0;
            padding: 15px 30px;
            margin: 20px -30px -20px -30px;
            font-weight: bold;
            font-family: monospace;
            box-shadow: 0 2px 4px rgba(0,0,0,.2) inset;
        }

        button:hover {
            background: #37d;
        }

        button:focus {
            outline: 0;
        }

        button:focus:not(:hover) {
            outline: 0;
            border: 2px solid #25d;
            padding: 13px 28px;
        }

        button:active {
            outline: 0;
            box-shadow: 0 0 6px rgba(0,0,0,.3) inset;
            border: 0;
            padding: 15px 30px;
        }
    </style>
</head>
<body>
    <div class="box">
        <header>
            <h1>Error {code}</h1>
        </header>
        <main>
            <p>{reason}</p>
            <ul>
                <li>Try again later.</li>
                <li>Check if you visited the correct URL.</li>
                <li>Report this issue if you think this is a mistake.</li>
            </ul>
            <button type="button" onclick="window.location.reload()">Retry</button>
        </main>
    </div>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Internal Server Error</title>
    <style>
        body, html {
            margin: 0;
            padding: 0;
            background: #eee;
            font-family: monospace;
            align-items: center;
            justify-content: center;
            height: 100vh;
        }

        .box {
            overflow-x: scroll;
            margin: 5%;
            background: #fff;
            border-radius: 3px;
            padding: 20px 30px;
            max-width: 90%;
            font-size: 14px;
            color: #333;
            box-shadow: 0 2px 6px rgba(0,0,0,.1);
        }

        h1, h2 {
            color: #111;
            /* border-bottom: 2px solid #08e; */
            padding: 0 0 0 0;
            margin: 10px 0 8px 0;
        }

        h1 {
            font-size: 20px;
        }

        h2 {
            font-size: 14px;
        }

        button {
            box-sizing: content-box;
            width: 100%;
            display: block;
            background: #e84;
            color: #fff;
            border-radius: 0 0 2px 2px;
            border: 0;
            padding: 15px 30px;
            margin: 20px -30px -20px -30px;
            font-weight: bold;
            font-family: monospace;
            box-shadow: 0 2px 4px rgba(0,0,0,.2) inset;
        }

        button:hover {
            background: #d73;
        }

        button:focus {
            outline: 0;
        }

        button:focus:not(:hover) {
            outline: 0;
            border: 2px solid #d52;
            padding: 13px 28px;
        }

        button:active {
            outline: 0;
            box-shadow: 0 0 6px rgba(0,0,0,.3) inset;
            border: 0;
            padding: 15px 30px;
        }
    </style>
</head>
<body>
    <div class="box">
        <header>
            <h1>Internal Server Error</h1>
        </header>
        <main>
            <h2>Requested: {uri}</h2>
            <h2>Uncaught {class}: {message} in {file} on line {line}.</h2>
            <pre>{trace}</pre>
            <button type="button" onclick="window.location.reload()">Retry</button>
        </main>
    </div>
</body>
</html>
{
    "symbol-whitelist": [
        "null",
        "true",
        "false",
        "static",
        "self",
        "parent",
        "array",
        "string",
        "int",
        "float",
        "bool",
        "iterable",
        "callable",
        "mixed",
        "void",
        "object",
        "never",
        "toMap",
        "createRedisConnector",
        "random_bytes"
    ],
    "php-core-extensions": [
        "Core",
        "date",
        "pcre",
        "Phar",
        "Reflection",
        "SPL",
        "standard",
        "hash"
    ]
}
<?php declare(strict_types=1);

namespace Amp\Redis;

use League\Uri\Uri;

final class RedisConfig
{
    public const DEFAULT_HOST = 'localhost';
    public const DEFAULT_PORT = 6379;
    public const DEFAULT_TIMEOUT = 5;

    /**
     * @throws RedisException
     */
    public static function fromUri(string $uri, float $timeout = self::DEFAULT_TIMEOUT): self
    {
        return new self($uri, $timeout);
    }

    private string $uri;
    private string $password;
    private int $database;
    private float $timeout;

    /**
     * @throws RedisException
     */
    private function __construct(string $uri, float $timeout)
    {
        $this->applyUri($uri);
        $this->timeout = $timeout;
    }

    public function getConnectUri(): string
    {
        return $this->uri;
    }

    public function getTimeout(): float
    {
        return $this->timeout;
    }

    public function getPassword(): string
    {
        return $this->password;
    }

    public function hasPassword(): bool
    {
        return $this->password !== '';
    }

    public function getDatabase(): int
    {
        return $this->database;
    }

    public function withTimeout(float $timeout): self
    {
        $clone = clone $this;
        $clone->timeout = $timeout;

        return $clone;
    }

    public function withPassword(string $password): self
    {
        $clone = clone $this;
        $clone->password = $password;

        return $clone;
    }

    public function withDatabase(int $database): self
    {
        $clone = clone $this;
        $clone->database = $database;

        return $clone;
    }

    /**
     * When using the "redis" schemes the URI is parsed according to the rules defined by the provisional registration
     * documents approved by IANA. If the URI has a password in its "user-information" part or a database number in the
     * "path" part these values override the values of "password" / "database" if they are present in the "query" part.
     *
     * @link http://www.iana.org/assignments/uri-schemes/prov/redis
     *
     * @param string $uri URI string.
     *
     * @throws RedisException
     */
    private function applyUri(string $uri): void
    {
        try {
            $uri = Uri::new($uri);
        } catch (\Exception) {
            throw new RedisException('Invalid redis configuration URI: ' . $uri);
        }

        $scheme = match (\strtolower($uri->getScheme() ?? '')) {
            'tcp', 'redis' => 'tcp',
            'unix' => 'unix',
            default => throw new RedisException(
                'Invalid scheme for redis URI, must be tcp, unix, or redis, got ' . $uri->getScheme()
            ),
        };

        \parse_str($uri->getQuery() ?? '', $query);

        [, $password] = \explode(':', $uri->getUserInfo() ?? '', 2) + [null, null];
        $this->password = $password ?? $query['password'] ?? $query['pass'] ?? '';

        $this->database = (int) ($query['database'] ?? $query['db'] ?? 0);

        if ($scheme === 'unix') {
            $this->uri = 'unix://' . $uri->getPath();
            return;
        }

        $path = \ltrim($uri->getPath(), '/');
        if ($path !== '') {
            $this->database = (int) $path;
        }

        $this->uri = \sprintf(
            'tcp://%s:%d',
            $uri->getHost() ?: self::DEFAULT_HOST,
            $uri->getPort() ?: self::DEFAULT_PORT,
        );
    }
}
<?php declare(strict_types=1);

namespace Amp\Redis\Sync;

use Amp\Redis\RedisClient;
use Amp\Serialization\NativeSerializer;
use Amp\Serialization\Serializer;
use Amp\Sync\Parcel;
use Amp\Sync\ParcelException;

/**
 * @template T
 * @implements Parcel<T>
 */
final class RedisParcel implements Parcel
{
    public static function create(
        RedisMutex $mutex,
        string $key,
        mixed $value,
        ?Serializer $serializer = null,
    ): self {
        return (new self($mutex, $key, $serializer))->init($value);
    }

    public static function use(
        RedisMutex $mutex,
        string $key,
        ?Serializer $serializer = null,
    ): self {
        return (new self($mutex, $key, $serializer))->open();
    }

    private readonly RedisClient $redis;

    private readonly Serializer $serializer;

    private function __construct(
        private readonly RedisMutex $mutex,
        private readonly string $key,
        ?Serializer $serializer = null,
    ) {
        $this->redis = $mutex->getClient();
        $this->serializer = $serializer ?? new NativeSerializer();
    }

    private function init(mixed $value): self
    {
        $value = $this->serializer->serialize($value);

        $lock = $this->mutex->acquire($this->key);

        try {
            $this->redis->set($this->key, $value);
        } finally {
            $lock->release();
        }

        return $this;
    }

    private function open(): self
    {
        if (!$this->redis->get($this->key)) {
            throw new ParcelException('Could not open parcel: key not found');
        }

        return $this;
    }

    public function getClient(): RedisClient
    {
        return $this->mutex->getClient();
    }

    public function unwrap(): mixed
    {
        $value = $this->redis->get($this->key)
            ?? throw new ParcelException('Could not unwrap parcel: key not found');

        return $this->serializer->unserialize($value);
    }

    public function synchronized(\Closure $closure): mixed
    {
        $lock = $this->mutex->acquire($this->key);

        try {
            $result = $closure($this->unwrap());
            $this->redis->set($this->key, $this->serializer->serialize($result));
        } finally {
            $lock->release();
        }

        return $result;
    }
}
<?php declare(strict_types=1);

namespace Amp\Redis\Sync;

use Amp\Redis\RedisClient;
use Amp\Redis\RedisException;
use Amp\Sync\KeyedMutex;
use Amp\Sync\Lock;
use Psr\Log\LoggerInterface as PsrLogger;
use Psr\Log\NullLogger;
use Revolt\EventLoop;
use function Amp\delay;

/**
 * Mutex can be used to create locks for mutual exclusion in distributed clients.
 *
 * @author Niklas Keller <me@kelunik.com>
 */
final class RedisMutex implements KeyedMutex
{
    private const LOCK = <<<LOCK
local lock = KEYS[1]
local queue = KEYS[2]

local token = ARGV[1]
local ttl = ARGV[2]
local queueTtl = ARGV[3]

if redis.call("exists", lock) == 0 then
    if redis.call("llen", queue) == 0 then
        redis.call("set", lock, token, "px", ttl)
        return 1
    else
        local queued_tokens = redis.call("lrange", queue, 0, -1)
        local push = 1
        local position = 0

        for i=1,#queued_tokens do
            if queued_tokens[i] == token then
                push = 0
                position = i
                break
            end
        end

        if push == 1 then
            redis.call("rpush", queue, token)
        end

        local queued = redis.call("lpop", queue)
        redis.call("set", lock, queued, "px", ttl)
        if queued == token then
            return 2
        else
            redis.call("pexpire", queue, queueTtl)

            return -1 - position
        end
    end
else
    if redis.call("get", lock) == token then
        redis.call("set", lock, token, "px", ttl)
        return 1
    end

    local queued_tokens = redis.call("lrange", queue, 0, -1)
    for i=1,#queued_tokens do
        if queued_tokens[i] == token then
            redis.call("pexpire", queue, queueTtl)

            return -1 - i
        end
    end

    redis.call("rpush", queue, token)
    redis.call("pexpire", queue, queueTtl)

    return -1 - redis.call("llen", queue)
end
LOCK;

    private const UNLOCK = <<<UNLOCK
local lock = KEYS[1]
local token = ARGV[1]

if redis.call("get", lock) == token then
    redis.call("del", lock)
    return 1
else
    return 2
end
UNLOCK;

    private const RENEW = <<<RENEW
for i=1,#KEYS do
    if redis.call("get", KEYS[i]) == ARGV[i + 1] then
        redis.call("pexpire", KEYS[i], ARGV[1])
    end
end
RENEW;

    private readonly RedisMutexOptions $options;

    private readonly RedisClient $redis;

    /** @var array<string, array{string, string}> */
    private array $locks = [];

    private ?string $watcher = null;

    private readonly PsrLogger $logger;

    private int $numberOfLocks = 0;

    private int $numberOfAttempts = 0;

    /**
     * Constructs a new Mutex instance. A single instance can be used to create as many locks as you need.
     */
    public function __construct(
        private readonly RedisClient $client,
        ?RedisMutexOptions $options = null,
        ?PsrLogger $logger = null,
    ) {
        $this->options = $options ?? new RedisMutexOptions;
        $this->redis = $client;
        $this->logger = $logger ?? new NullLogger;
    }

    public function __destruct()
    {
        if ($this->watcher !== null) {
            EventLoop::cancel($this->watcher);
        }
    }

    public function getClient(): RedisClient
    {
        return $this->client;
    }

    /**
     * Acquires a lock.
     *
     * If directly acquiring a lock fails, the client is placed in a queue and reattempts to lock the key. If a client
     * crashes or doesn't free the lock while not renewing it, the lock will expire and the next client in the queue
     * will be able to acquire it.
     *
     * @param string $key Lock key.
     */
    public function acquire(string $key): Lock
    {
        $this->numberOfLocks++;

        $token = \base64_encode(\random_bytes(16));
        $prefix = $this->options->getKeyPrefix();
        $timeLimit = \microtime(true) + $this->options->getLockTimeout();
        $attempts = 0;

        do {
            $attempts++;
            $this->numberOfAttempts++;

            $result = $this->redis->eval(
                self::LOCK,
                ["{$prefix}lock:{$key}", "{$prefix}lock-queue:{$key}"],
                [$token, $this->options->getLockExpiration() * 1000, ($this->options->getLockExpiration() + $this->options->getLockTimeout()) * 1000]
            );

            if ($result < 1) {
                if ($attempts > 2 && \microtime(true) > $timeLimit) {
                    // In very rare cases we might not get the lock, but are at the head of the queue and another
                    // client moves us into the lock position. Deleting the token from the queue and afterwards
                    // unlocking solves this. No yield required, because we use the same connection.
                    $this->redis->getList("{$prefix}lock-queue:{$key}")->remove($token);
                    $this->unlock($key, $token);

                    throw new RedisMutexException('Failed to acquire lock for ' . $key . ' within ' . $this->options->getLockTimeout() * 1000 . ' ms');
                }

                // A negative integer as reply means we're still in the queue and indicates the queue position.
                // Making the timing dependent on the queue position greatly reduces CPU usage and locking attempts.
                delay(0.005 + \min((-$result - 1) / 100, 0.3));
            }
        } while ($result < 1);

        if (empty($this->locks)) {
            $this->createRenewWatcher();
        }

        $this->locks[$key . ' @ ' . $token] = [$key, $token];

        return new Lock(fn () => $this->unlock($key, $token));
    }

    public function getNumberOfAttempts(): int
    {
        return $this->numberOfAttempts;
    }

    public function getNumberOfLocks(): int
    {
        return $this->numberOfLocks;
    }

    public function resetStatistics(): void
    {
        $this->numberOfAttempts = 0;
        $this->numberOfLocks = 0;
    }

    /**
     * Unlocks a previously acquired lock.
     *
     * @param string $key Lock key.
     * @param string $token Unique token generated during {@link lock()}.
     */
    private function unlock(string $key, string $token): void
    {
        // Unset before unlocking, as we don't want to renew the lock anymore
        // If something goes wrong, the lock will simply expire
        unset($this->locks[$key . ' @ ' . $token]);

        if (empty($this->locks) && $this->watcher !== null) {
            EventLoop::cancel($this->watcher);
            $this->watcher = null;
        }

        $prefix = $this->options->getKeyPrefix();

        for ($attempt = 0; $attempt < 2; $attempt++) {
            try {
                $result = $this->redis->eval(
                    self::UNLOCK,
                    ["{$prefix}lock:{$key}"],
                    [$token]
                );

                if ($result === 2) {
                    $this->logger->warning('Lock was already expired when unlocked', [
                        'key' => $key,
                    ]);
                }

                break;
            } catch (RedisException $e) {
                $this->logger->error('Unlock operation failed on attempt ' . ($attempt + 1), [
                    'exception' => $e,
                ]);
            }
        }
    }

    private function createRenewWatcher(): void
    {
        $locks = &$this->locks;
        $options = $this->options;
        $redis = $this->redis;
        $logger = $this->logger;

        $this->watcher = EventLoop::repeat(
            $options->getLockRenewInterval(),
            static function () use (&$locks, $options, $redis, $logger): void {
                \assert(!empty($locks));

                $keys = [];
                $arguments = [$options->getLockExpiration() * 1000];

                $prefix = $options->getKeyPrefix();

                foreach ($locks as [$key, $token]) {
                    $keys[] = "{$prefix}lock:{$key}";
                    $arguments[] = $token;
                }

                try {
                    $redis->eval(self::RENEW, $keys, $arguments);
                } catch (RedisException $e) {
                    $logger->error('Renew operation failed, locks might expire', [
                        'exception' => $e,
                    ]);
                }
            }
        );
    }
}
<?php declare(strict_types=1);

namespace Amp\Redis\Sync;

use Amp\Redis\RedisException;

class RedisMutexException extends RedisException
{
}
<?php declare(strict_types=1);

namespace Amp\Redis\Sync;

final class RedisMutexOptions
{
    private string $keyPrefix = '';
    private float $lockRenewInterval = 1;
    private float $lockExpiration = 3;
    private float $lockTimeout = 10;

    public function getKeyPrefix(): string
    {
        return $this->keyPrefix;
    }

    public function getLockExpiration(): float
    {
        return $this->lockExpiration;
    }

    public function getLockRenewInterval(): float
    {
        return $this->lockRenewInterval;
    }

    public function getLockTimeout(): float
    {
        return $this->lockTimeout;
    }

    public function withKeyPrefix(string $keyPrefix): self
    {
        $clone = clone $this;
        $clone->keyPrefix = $keyPrefix;

        return $clone;
    }

    public function withLockExpiration(float $lockExpiration): self
    {
        $clone = clone $this;
        $clone->lockExpiration = $lockExpiration;

        return $clone;
    }

    public function withLockRenewInterval(float $lockRenewInterval): self
    {
        $clone = clone $this;
        $clone->lockRenewInterval = $lockRenewInterval;

        return $clone;
    }

    public function withLockTimeout(float $lockTimeout): self
    {
        $clone = clone $this;
        $clone->lockTimeout = $lockTimeout;

        return $clone;
    }
}
<?php declare(strict_types=1);
/** @noinspection DuplicatedCode */

namespace Amp\Redis\Command;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Redis\Command\Option\SortOptions;
use Amp\Redis\RedisClient;

final class RedisList
{
    use ForbidCloning;
    use ForbidSerialization;

    public function __construct(
        private readonly RedisClient $client,
        private readonly string $key,
    ) {
    }

    /**
     * @link https://redis.io/commands/lindex
     *
     * TODO: Change type declaration to int only in next major.
     */
    public function get(string|int $index): ?string
    {
        return $this->client->execute('lindex', $this->key, $index);
    }

    /**
     * @link https://redis.io/commands/linsert
     */
    public function insertBefore(string $pivot, string $value): int
    {
        return $this->client->execute('linsert', $this->key, 'BEFORE', $pivot, $value);
    }

    /**
     * @link https://redis.io/commands/linsert
     */
    public function insertAfter(string $pivot, string $value): int
    {
        return $this->client->execute('linsert', $this->key, 'AFTER', $pivot, $value);
    }

    /**
     *
     * @link https://redis.io/commands/llen
     */
    public function getSize(): int
    {
        return $this->client->execute('llen', $this->key);
    }

    /**
     * @link https://redis.io/commands/lpush
     */
    public function pushHead(string $value, string ...$values): int
    {
        return $this->client->execute('lpush', $this->key, $value, ...$values);
    }

    /**
     * @link https://redis.io/commands/lpushx
     */
    public function pushHeadIfExists(string $value, string ...$values): int
    {
        return $this->client->execute('lpushx', $this->key, $value, ...$values);
    }

    /**
     * @link https://redis.io/commands/rpush
     */
    public function pushTail(string $value, string ...$values): int
    {
        return $this->client->execute('rpush', $this->key, $value, ...$values);
    }

    /**
     * @link https://redis.io/commands/rpushx
     */
    public function pushTailIfExists(string $value, string ...$values): int
    {
        return $this->client->execute('rpushx', $this->key, $value, ...$values);
    }

    /**
     *
     * @link https://redis.io/commands/lpop
     */
    public function popHead(): ?string
    {
        return $this->client->execute('lpop', $this->key);
    }

    /**
     * @link https://redis.io/commands/blpop
     */
    public function popHeadBlocking(int $timeout = 0): ?string
    {
        $response = $this->client->execute('blpop', $this->key, $timeout);
        return $response[1] ?? null;
    }

    /**
     * @link https://redis.io/commands/rpop
     */
    public function popTail(): ?string
    {
        return $this->client->execute('rpop', $this->key);
    }

    /**
     * @link https://redis.io/commands/brpop
     */
    public function popTailBlocking(int $timeout = 0): ?string
    {
        $response = $this->client->execute('brpop', $this->key, $timeout);
        return $response[1] ?? null;
    }

    /**
     * @link https://redis.io/commands/rpoplpush
     */
    public function popTailPushHead(string $destination): string
    {
        return $this->client->execute('rpoplpush', $this->key, $destination);
    }

    /**
     * @link https://redis.io/commands/brpoplpush
     */
    public function popTailPushHeadBlocking(string $destination, int $timeout = 0): ?string
    {
        return $this->client->execute('brpoplpush', $this->key, $destination, $timeout);
    }

    /**
     * @link https://redis.io/commands/lrange
     */
    public function getRange(int $start = 0, int $end = -1): array
    {
        return $this->client->execute('lrange', $this->key, $start, $end);
    }

    /**
     * @link https://redis.io/commands/lrem
     */
    public function remove(string $value, int $count = 0): int
    {
        return $this->client->execute('lrem', $this->key, $count, $value);
    }

    /**
     * @link https://redis.io/commands/lset
     */
    public function set(int $index, string $value): void
    {
        $this->client->execute('lset', $this->key, $index, $value);
    }

    /**
     * @link https://redis.io/commands/ltrim
     */
    public function trim(int $start = 0, int $stop = -1): void
    {
        $this->client->execute('ltrim', $this->key, $start, $stop);
    }

    /**
     * @link https://redis.io/commands/sort
     */
    public function sort(?SortOptions $options = null): array
    {
        return $this->client->execute('SORT', $this->key, ...($options ?? new SortOptions)->toQuery());
    }
}
<?php declare(strict_types=1);

namespace Amp\Redis\Command\Boundary;

final class ScoreBoundary
{
    public static function exclusive(float $value): self
    {
        return new self('(' . $value);
    }

    public static function inclusive(float $value): self
    {
        return new self((string) $value);
    }

    public static function negativeInfinity(): self
    {
        return new self('-inf');
    }

    public static function positiveInfinity(): self
    {
        return new self('+inf');
    }

    private function __construct(private readonly string $value)
    {
    }

    public function toQuery(): string
    {
        return $this->value;
    }
}
<?php declare(strict_types=1);

namespace Amp\Redis\Command\Boundary;

final class LexBoundary
{
    /**
     * @param non-empty-string $value
     */
    public static function exclusive(string $value): self
    {
        return new self('(' . $value);
    }

    /**
     * @param non-empty-string $value
     */
    public static function inclusive(string $value): self
    {
        return new self('[' . $value);
    }

    public static function negativeInfinity(): self
    {
        return new self('-');
    }

    public static function positiveInfinity(): self
    {
        return new self('+');
    }

    private function __construct(private readonly string $value)
    {
    }

    public function toQuery(): string
    {
        return $this->value;
    }
}
<?php declare(strict_types=1);

namespace Amp\Redis\Command\Option;

final class SetOptions
{
    private ?int $ttl = null;
    private string $ttlUnit = 'EX';
    private ?string $existenceFlag = null;

    public function withTtl(int $seconds): self
    {
        $clone = clone $this;
        $clone->ttl = $seconds;
        $clone->ttlUnit = 'EX';

        return $clone;
    }

    public function withTtlInMillis(int $millis): self
    {
        $clone = clone $this;
        $clone->ttl = $millis;
        $clone->ttlUnit = 'PX';

        return $clone;
    }

    public function withoutOverwrite(): self
    {
        $clone = clone $this;
        $clone->existenceFlag = 'NX';

        return $clone;
    }

    public function withoutCreation(): self
    {
        $clone = clone $this;
        $clone->existenceFlag = 'XX';

        return $clone;
    }

    /**
     * @return list<int|string>
     */
    public function toQuery(): array
    {
        $query = [];

        if ($this->ttl !== null) {
            $query[] = $this->ttlUnit;
            $query[] = $this->ttl;
        }

        if ($this->existenceFlag !== null) {
            $query[] = $this->existenceFlag;
        }

        return $query;
    }
}
<?php declare(strict_types=1);

namespace Amp\Redis\Command\Option;

final class RangeOptions
{
    private ?int $offset = null;
    private ?int $count = null;
    private bool $reverse = false;

    public function withReverseOrder(): self
    {
        $clone = clone $this;
        $clone->reverse = true;

        return $clone;
    }

    public function withLimit(int $offset, int $count): self
    {
        $clone = clone $this;
        $clone->offset = $offset;
        $clone->count = $count;

        return $clone;
    }

    /**
     * @return list<string|int>
     */
    public function toQuery(): array
    {
        $query = [];

        if ($this->reverse) {
            $query[] = "REV";
        }

        if ($this->offset !== null) {
            \assert($this->count !== null);

            $query[] = "LIMIT";
            $query[] = $this->offset;
            $query[] = $this->count;
        }

        return $query;
    }
}
<?php declare(strict_types=1);

namespace Amp\Redis\Command\Option;

final class SortOptions
{
    private ?string $pattern = null;
    private ?int $offset = null;
    private ?int $count = null;
    private bool $ascending = true;
    private bool $lexicographically = false;

    public function hasPattern(): bool
    {
        return $this->pattern !== null;
    }

    public function getPattern(): ?string
    {
        return $this->pattern;
    }

    public function withPattern(string $pattern): self
    {
        $clone = clone $this;
        $clone->pattern = $pattern;

        return $clone;
    }

    public function withoutPattern(): self
    {
        $clone = clone $this;
        $clone->pattern = null;

        return $clone;
    }

    public function getOffset(): ?int
    {
        return $this->offset;
    }

    public function getCount(): ?int
    {
        return $this->count;
    }

    public function hasLimit(): bool
    {
        return $this->offset !== null;
    }

    public function withLimit(int $offset, int $count): self
    {
        $clone = clone $this;
        $clone->offset = $offset;
        $clone->count = $count;

        return $clone;
    }

    public function withoutLimit(): self
    {
        $clone = clone $this;
        $clone->offset = null;
        $clone->count = null;

        return $clone;
    }

    public function isAscending(): bool
    {
        return $this->ascending;
    }

    public function isDescending(): bool
    {
        return !$this->ascending;
    }

    public function withAscendingOrder(): self
    {
        $clone = clone $this;
        $clone->ascending = true;

        return $clone;
    }

    public function withDescendingOrder(): self
    {
        $clone = clone $this;
        $clone->ascending = false;

        return $clone;
    }

    public function isLexicographicSorting(): bool
    {
        return $this->lexicographically;
    }

    public function withLexicographicSorting(): self
    {
        $clone = clone $this;
        $clone->lexicographically = true;

        return $clone;
    }

    public function withNumericSorting(): self
    {
        $clone = clone $this;
        $clone->lexicographically = false;

        return $clone;
    }

    /**
     * @return list<int|string>
     */
    public function toQuery(): array
    {
        $payload = [];

        $pattern = $this->getPattern();
        if ($pattern !== null) {
            $payload[] = 'BY';
            $payload[] = $pattern;
        }

        if ($this->hasLimit()) {
            $offset = $this->getOffset();
            $count = $this->getCount();

            \assert($offset !== null);
            \assert($count !== null);

            $payload[] = 'LIMIT';
            $payload[] = $offset;
            $payload[] = $count;
        }

        if ($this->isDescending()) {
            $payload[] = 'DESC';
        }

        if ($this->isLexicographicSorting()) {
            $payload[] = 'ALPHA';
        }

        return $payload;
    }
}
<?php declare(strict_types=1);
/** @noinspection DuplicatedCode */

namespace Amp\Redis\Command;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Redis\RedisClient;

final class RedisHyperLogLog
{
    use ForbidCloning;
    use ForbidSerialization;

    public function __construct(
        private readonly RedisClient $client,
        private readonly string $key,
    ) {
    }

    /**
     * @link https://redis.io/commands/pfadd
     */
    public function add(string $element, string ...$elements): bool
    {
        return (bool) $this->client->execute(
            'pfadd',
            $this->key,
            $element,
            ...$elements,
        );
    }

    /**
     * @link https://redis.io/commands/pfcount
     */
    public function count(): int
    {
        return $this->client->execute('pfcount', $this->key);
    }

    /**
     * @link https://redis.io/commands/pfmerge
     */
    public function storeUnion(string $sourceKey, string ...$sourceKeys): void
    {
        $this->client->execute(
            'pfmerge',
            $this->key,
            $sourceKey,
            ...$sourceKeys,
        );
    }
}
<?php declare(strict_types=1);

namespace Amp\Redis\Command;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Redis\Command\Boundary\LexBoundary;
use Amp\Redis\Command\Boundary\ScoreBoundary;
use Amp\Redis\Command\Option\RangeOptions;
use Amp\Redis\Command\Option\SortOptions;
use Amp\Redis\RedisClient;
use function Amp\Redis\Internal\toMap;

final class RedisSortedSet
{
    use ForbidCloning;
    use ForbidSerialization;

    private readonly RedisClient $client;
    private readonly string $key;

    public function __construct(RedisClient $client, string $key)
    {
        $this->client = $client;
        $this->key = $key;
    }

    /**
     * @param array<string, int|float> $data
     *
     * @return int Number of items added.
     */
    public function add(array $data): int
    {
        $payload = [$this->key];

        foreach ($data as $member => $score) {
            $payload[] = $score;
            $payload[] = $member;
        }

        return $this->client->execute('zadd', ...$payload);
    }

    /**
     * @return list<string>
     */
    public function getRange(int $start, int $end, ?RangeOptions $options = null): array
    {
        $options ??= new RangeOptions();
        return $this->client->execute('zrange', $this->key, $start, $end, ...$options->toQuery());
    }

    /**
     * @return array<string, float>
     */
    public function getRangeWithScores(int $start, int $end, ?RangeOptions $options = null): array
    {
        $options ??= new RangeOptions();
        return \array_map(\floatval(...), toMap($this->client->execute(
            'zrange',
            $this->key,
            $start,
            $end,
            'WITHSCORES',
            ...$options->toQuery(),
        )));
    }

    /**
     * @return list<string>
     */
    public function getRangeByScore(ScoreBoundary $min, ScoreBoundary $max, ?RangeOptions $options = null): array
    {
        $options ??= new RangeOptions();
        return $this->client->execute(
            'zrange',
            $this->key,
            $min->toQuery(),
            $max->toQuery(),
            'BYSCORE',
            ...$options->toQuery(),
        );
    }

    /**
     * @return array<string, float>
     */
    public function getRangeByScoreWithScores(ScoreBoundary $min, ScoreBoundary $max, ?RangeOptions $options = null): array
    {
        $options ??= new RangeOptions();
        return \array_map(\floatval(...), toMap($this->client->execute(
            'zrange',
            $this->key,
            $min->toQuery(),
            $max->toQuery(),
            'BYSCORE',
            'WITHSCORES',
            ...$options->toQuery(),
        )));
    }

    public function getLexicographicRange(LexBoundary $start, LexBoundary $end, ?RangeOptions $options = null): array
    {
        $options ??= new RangeOptions();
        return $this->client->execute(
            'zrange',
            $this->key,
            $start->toQuery(),
            $end->toQuery(),
            'BYLEX',
            ...$options->toQuery(),
        );
    }

    public function getSize(): int
    {
        return $this->client->execute('zcard', $this->key);
    }

    public function count(int $min, int $max): int
    {
        return $this->client->execute('zcount', $this->key, $min, $max);
    }

    public function increment(string $member, float $increment = 1): float
    {
        return (float) $this->client->execute('zincrby', $this->key, $increment, $member);
    }

    /**
     * @param string[] $keys
     */
    public function storeIntersection(array $keys, string $aggregate = 'sum'): int
    {
        $payload = [$this->key, \count($keys)];
        $weights = [];

        if (\count(\array_filter(\array_keys($keys), 'is_string'))) {
            foreach ($keys as $key => $weight) {
                $payload[] = $key;
                $weights[] = $weight;
            }
        } else {
            foreach ($keys as $key) {
                $payload[] = $key;
            }
        }

        if (\count($weights) > 0) {
            $payload[] = 'WEIGHTS';

            foreach ($weights as $weight) {
                $payload[] = $weight;
            }
        }

        if (\strtolower($aggregate) !== 'sum') {
            $payload[] = 'AGGREGATE';
            $payload[] = $aggregate;
        }

        return $this->client->execute('zinterstore', ...$payload);
    }

    public function countLexicographicRange(LexBoundary $min, LexBoundary $max): int
    {
        return $this->client->execute('zlexcount', $this->key, $min->toQuery(), $max->toQuery());
    }

    public function getRank(string $member): ?int
    {
        return $this->client->execute('zrank', $this->key, $member);
    }

    public function remove(string $member, string ...$members): int
    {
        return $this->client->execute('zrem', $this->key, $member, ...$members);
    }

    public function removeLexicographicRange(LexBoundary $min, LexBoundary $max): int
    {
        return $this->client->execute('zremrangebylex', $this->key, $min->toQuery(), $max->toQuery());
    }

    public function removeRankRange(int $start, int $stop): int
    {
        return $this->client->execute('zremrangebyrank', $this->key, $start, $stop);
    }

    public function removeRangeByScore(ScoreBoundary $min, ScoreBoundary $max): int
    {
        return $this->client->execute('zremrangebyscore', $this->key, $min->toQuery(), $max->toQuery());
    }

    public function getReversedRank(string $member): ?int
    {
        return $this->client->execute('zrevrank', $this->key, $member);
    }

    /**
     * @return \Traversable<array{string, float}>
     */
    public function scan(?string $pattern = null, ?int $count = null): \Traversable
    {
        $cursor = 0;

        do {
            $query = [$this->key, $cursor];

            if ($pattern !== null) {
                $query[] = 'MATCH';
                $query[] = $pattern;
            }

            if ($count !== null) {
                $query[] = 'COUNT';
                $query[] = $count;
            }

            /** @var list<string> $keys */
            [$cursor, $keys] = $this->client->execute('ZSCAN', ...$query);

            $count = \count($keys);
            \assert($count % 2 === 0);

            for ($i = 0; $i < $count; $i += 2) {
                yield [$keys[$i], (float) $keys[$i + 1]];
            }
        } while ($cursor !== '0');
    }

    public function getScore(string $member): ?float
    {
        return (float) $this->client->execute('zscore', $this->key, $member);
    }

    /**
     * @param string[] $keys
     */
    public function storeUnion(array $keys, string $aggregate = 'sum'): int
    {
        $payload = [$this->key, \count($keys)];
        $weights = [];

        if (\count(\array_filter(\array_keys($keys), 'is_string'))) {
            foreach ($keys as $key => $weight) {
                $payload[] = $key;
                $weights[] = $weight;
            }
        } else {
            foreach ($keys as $key) {
                $payload[] = $key;
            }
        }

        if (\count($weights) > 0) {
            $payload[] = 'WEIGHTS';

            foreach ($weights as $weight) {
                $payload[] = $weight;
            }
        }

        if (\strtolower($aggregate) !== 'sum') {
            $payload[] = 'AGGREGATE';
            $payload[] = $aggregate;
        }

        return $this->client->execute('zunionstore', ...$payload);
    }

    /**
     * @link https://redis.io/commands/sort
     */
    public function sort(?SortOptions $options = null): array
    {
        return $this->client->execute('SORT', $this->key, ...($options ?? new SortOptions)->toQuery());
    }
}
<?php declare(strict_types=1);
/** @noinspection DuplicatedCode */

namespace Amp\Redis\Command;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Redis\RedisClient;
use function Amp\Redis\Internal\toMap;

final class RedisMap
{
    use ForbidCloning;
    use ForbidSerialization;

    public function __construct(
        private readonly RedisClient $client,
        private readonly string $key,
    ) {
    }

    /**
     * @link https://redis.io/commands/hdel
     */
    public function remove(string $field, string ...$fields): int
    {
        return $this->client->execute('hdel', $this->key, $field, ...$fields);
    }

    /**
     * @link https://redis.io/commands/hexists
     */
    public function hasKey(string $field): bool
    {
        return (bool) $this->client->execute('hexists', $this->key, $field);
    }

    /**
     * @link https://redis.io/commands/hincrby
     */
    public function increment(string $field, int $increment = 1): int
    {
        return $this->client->execute('hincrby', $this->key, $field, $increment);
    }

    /**
     * @link https://redis.io/commands/hincrbyfloat
     */
    public function incrementByFloat(string $field, float $increment): float
    {
        return (float) $this->client->execute('hincrbyfloat', $this->key, $field, $increment);
    }

    /**
     * @link https://redis.io/commands/hkeys
     */
    public function getKeys(): array
    {
        return $this->client->execute('hkeys', $this->key);
    }

    /**
     * @link https://redis.io/commands/hlen
     */
    public function getSize(): int
    {
        return $this->client->execute('hlen', $this->key);
    }

    /**
     * @link https://redis.io/commands/hvals
     * @link https://redis.io/commands/hgetall
     */
    public function getAll(): array
    {
        return toMap($this->client->execute('hgetall', $this->key));
    }

    /**
     * @link https://redis.io/commands/hmset
     *
     * @param array<string, int|float|string> $data
     */
    public function setValues(array $data): void
    {
        $array = [$this->key];

        foreach ($data as $dataKey => $value) {
            $array[] = $dataKey;
            $array[] = $value;
        }

        $this->client->execute('hmset', ...$array);
    }

    /**
     * @link https://redis.io/commands/hget
     */
    public function getValue(string $field): ?string
    {
        return $this->client->execute('hget', $this->key, $field);
    }

    /**
     * @link https://redis.io/commands/hmget
     */
    public function getValues(string $field, string ...$fields): array
    {
        return $this->client->execute('hmget', $this->key, $field, ...$fields);
    }

    /**
     * @link https://redis.io/commands/hset
     */
    public function setValue(string $field, string $value): bool
    {
        return (bool) $this->client->execute('hset', $this->key, $field, $value);
    }

    /**

     * @link https://redis.io/commands/hsetnx
     */
    public function setValueWithoutOverwrite(string $field, string $value): bool
    {
        return (bool) $this->client->execute('hsetnx', $this->key, $field, $value);
    }

    /**

     * @link https://redis.io/commands/hstrlen
     */
    public function getLength(string $field): int
    {
        return $this->client->execute('hstrlen', $this->key, $field);
    }

    /**
     * @return \Traversable<array{string, string}>
     *
     * @link https://redis.io/commands/hscan
     */
    public function scan(?string $pattern = null, ?int $count = null): \Traversable
    {
        $cursor = 0;

        do {
            $query = [$this->key, $cursor];

            if ($pattern !== null) {
                $query[] = 'MATCH';
                $query[] = $pattern;
            }

            if ($count !== null) {
                $query[] = 'COUNT';
                $query[] = $count;
            }

            /** @var list<string> $keys */
            [$cursor, $keys] = $this->client->execute('HSCAN', ...$query);

            $count = \count($keys);
            \assert($count % 2 === 0);

            for ($i = 0; $i < $count; $i += 2) {
                yield [$keys[$i], $keys[$i + 1]];
            }
        } while ($cursor !== '0');
    }
}
<?php declare(strict_types=1);
/** @noinspection DuplicatedCode */

namespace Amp\Redis\Command;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Redis\Command\Option\SortOptions;
use Amp\Redis\RedisClient;

final class RedisSet
{
    use ForbidCloning;
    use ForbidSerialization;

    public function __construct(
        private readonly RedisClient $client,
        private readonly string $key,
    ) {
    }

    public function add(string $member, string ...$members): int
    {
        return $this->client->execute('sadd', $this->key, $member, ...$members);
    }

    public function getSize(): int
    {
        return $this->client->execute('scard', $this->key);
    }

    public function diff(string ...$keys): array
    {
        return $this->client->execute('sdiff', $this->key, ...$keys);
    }

    public function storeDiff(string $key, string ...$keys): int
    {
        return $this->client->execute('sdiffstore', $this->key, $key, ...$keys);
    }

    public function intersect(string ...$keys): array
    {
        return $this->client->execute('sinter', $this->key, ...$keys);
    }

    public function storeIntersection(string $key, string ...$keys): int
    {
        return $this->client->execute('sinterstore', $this->key, $key, ...$keys);
    }

    public function contains(string $member): bool
    {
        return (bool) $this->client->execute('sismember', $this->key, $member);
    }

    public function getAll(): array
    {
        return $this->client->execute('smembers', $this->key);
    }

    public function move(string $member, string $destination): bool
    {
        return (bool) $this->client->execute('smove', $this->key, $destination, $member);
    }

    public function popRandomMember(): string
    {
        return $this->client->execute('spop', $this->key);
    }

    public function getRandomMember(): ?string
    {
        return $this->client->execute('srandmember', $this->key);
    }

    /**
     *
     * @return string[]
     */
    public function getRandomMembers(int $count): array
    {
        return $this->client->execute('srandmember', $this->key, $count);
    }

    public function remove(string $member, string ...$members): int
    {
        return $this->client->execute('srem', $this->key, $member, ...$members);
    }

    public function union(string ...$keys): array
    {
        return $this->client->execute('sunion', $this->key, ...$keys);
    }

    public function storeUnion(string $key, string ...$keys): int
    {
        return $this->client->execute('sunionstore', $this->key, $key, ...$keys);
    }

    /**
     * @return \Traversable<string>
     */
    public function scan(?string $pattern = null, ?int $count = null): \Traversable
    {
        $cursor = 0;

        do {
            $query = [$this->key, $cursor];

            if ($pattern !== null) {
                $query[] = 'MATCH';
                $query[] = $pattern;
            }

            if ($count !== null) {
                $query[] = 'COUNT';
                $query[] = $count;
            }

            [$cursor, $keys] = $this->client->execute('SSCAN', ...$query);

            foreach ($keys as $key) {
                yield $key;
            }
        } while ($cursor !== '0');
    }

    /**
     * @link https://redis.io/commands/sort
     */
    public function sort(?SortOptions $options = null): array
    {
        return $this->client->execute('SORT', $this->key, ...($options ?? new SortOptions)->toQuery());
    }
}
<?php declare(strict_types=1);
/** @noinspection DuplicatedCode */

namespace Amp\Redis;

use Amp\Cache\AtomicCache;
use Amp\Cache\LocalCache;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Redis\Command\Option\SetOptions;
use Amp\Redis\Command\RedisHyperLogLog;
use Amp\Redis\Command\RedisList;
use Amp\Redis\Command\RedisMap;
use Amp\Redis\Command\RedisSet;
use Amp\Redis\Command\RedisSortedSet;
use Amp\Redis\Connection\RedisLink;
use Amp\Redis\Protocol\RedisError;
use Amp\Sync\LocalKeyedMutex;
use function Amp\Redis\Internal\toMap;

final class RedisClient
{
    use ForbidCloning;
    use ForbidSerialization;

    private AtomicCache $evalCache;

    public function __construct(
        private readonly RedisLink $link
    ) {
        $this->evalCache = $this->createCache();
    }

    public function getHyperLogLog(string $key): RedisHyperLogLog
    {
        return new RedisHyperLogLog($this, $key);
    }

    public function getList(string $key): RedisList
    {
        return new RedisList($this, $key);
    }

    public function getMap(string $key): RedisMap
    {
        return new RedisMap($this, $key);
    }

    public function getSet(string $key): RedisSet
    {
        return new RedisSet($this, $key);
    }

    public function getSortedSet(string $key): RedisSortedSet
    {
        return new RedisSortedSet($this, $key);
    }

    public function execute(string $command, string|int|float ...$args): mixed
    {
        return $this->link->execute($command, $args)->unwrap();
    }

    /**
     * @link https://redis.io/commands/del
     */
    public function delete(string $key, string ...$keys): int
    {
        return $this->execute('del', $key, ...$keys);
    }

    /**
     * @link https://redis.io/commands/dump
     */
    public function dump(string $key): string
    {
        return $this->execute('dump', $key);
    }

    /**
     * @link https://redis.io/commands/exists
     */
    public function has(string $key): bool
    {
        return (bool) $this->execute('exists', $key);
    }

    /**
     * @link https://redis.io/commands/expire
     */
    public function expireIn(string $key, int $seconds): bool
    {
        return (bool) $this->execute('expire', $key, $seconds);
    }

    /**
     * @link https://redis.io/commands/pexpire
     */
    public function expireInMillis(string $key, int $millis): bool
    {
        return (bool) $this->execute('pexpire', $key, $millis);
    }

    /**
     * @link https://redis.io/commands/expireat
     */
    public function expireAt(string $key, int $timestamp): bool
    {
        return (bool) $this->execute('expireat', $key, $timestamp);
    }

    /**
     * @link https://redis.io/commands/pexpireat
     */
    public function expireAtMillis(string $key, int $timestamp): bool
    {
        return (bool) $this->execute('pexpireat', $key, $timestamp);
    }

    /**
     * @link https://redis.io/commands/keys
     *
     * @see RedisClient::scan()
     */
    public function getKeys(string $pattern = '*'): array
    {
        return $this->execute('keys', $pattern);
    }

    /**
     * @link https://redis.io/commands/move
     */
    public function move(string $key, int $db): bool
    {
        return (bool) $this->execute('move', $key, $db);
    }

    /**
     * @link https://redis.io/commands/object
     */
    public function getObjectRefcount(string $key): int
    {
        return $this->execute('object', 'refcount', $key);
    }

    /**
     * @link https://redis.io/commands/object
     */
    public function getObjectEncoding(string $key): string
    {
        return $this->execute('object', 'encoding', $key);
    }

    /**
     * @link https://redis.io/commands/object
     */
    public function getObjectIdletime(string $key): int
    {
        return $this->execute('object', 'idletime', $key);
    }

    /**
     * @link https://redis.io/commands/persist
     */
    public function persist(string $key): bool
    {
        return (bool) $this->execute('persist', $key);
    }

    /**
     * @link https://redis.io/commands/randomkey
     */
    public function getRandomKey(): ?string
    {
        return $this->execute('randomkey');
    }

    /**
     * @link https://redis.io/commands/rename
     */
    public function rename(string $key, string $newKey): void
    {
        $this->execute('rename', $key, $newKey);
    }

    /**
     * @link https://redis.io/commands/renamenx
     */
    public function renameWithoutOverwrite(string $key, string $newKey): void
    {
        $this->execute('renamenx', $key, $newKey);
    }

    /**
     * @link https://redis.io/commands/restore
     */
    public function restore(string $key, string $serializedValue, int $ttl = 0): void
    {
        $this->execute('restore', $key, $ttl, $serializedValue);
    }

    /**
     * @return \Traversable<string>
     *
     * @link https://redis.io/commands/scan
     */
    public function scan(?string $pattern = null, ?int $count = null): \Traversable
    {
        $cursor = 0;

        do {
            $query = [$cursor];

            if ($pattern !== null) {
                $query[] = 'MATCH';
                $query[] = $pattern;
            }

            if ($count !== null) {
                $query[] = 'COUNT';
                $query[] = $count;
            }

            [$cursor, $keys] = $this->execute('SCAN', ...$query);

            foreach ($keys as $key) {
                yield $key;
            }
        } while ($cursor !== '0');
    }

    /**
     * @link https://redis.io/commands/ttl
     */
    public function getTtl(string $key): int
    {
        return $this->execute('ttl', $key);
    }

    /**
     * @link https://redis.io/commands/pttl
     */
    public function getTtlInMillis(string $key): int
    {
        return $this->execute('pttl', $key);
    }

    /**
     * @link https://redis.io/commands/type
     */
    public function getType(string $key): string
    {
        return $this->execute('type', $key);
    }

    /**
     * @link https://redis.io/commands/append
     */
    public function append(string $key, string $value): int
    {
        return $this->execute('append', $key, $value);
    }

    /**
     * @link https://redis.io/commands/bitcount
     */
    public function countBits(string $key, ?int $start = null, ?int $end = null): int
    {
        $cmd = [$key];

        if (isset($start, $end)) {
            $cmd[] = $start;
            $cmd[] = $end;
        } elseif (isset($start) || isset($end)) {
            throw new \Error('Start and end must both be set or unset in countBits(), got start = ' . $start . ' and end = ' . $end);
        }

        return $this->execute('bitcount', ...$cmd);
    }

    /**
     * @link https://redis.io/commands/bitop
     */
    public function storeBitwiseAnd(string $destination, string $key, string ...$keys): int
    {
        return $this->execute('bitop', 'and', $destination, $key, ...$keys);
    }

    /**
     * @link https://redis.io/commands/bitop
     */
    public function storeBitwiseOr(string $destination, string $key, string ...$keys): int
    {
        return $this->execute('bitop', 'or', $destination, $key, ...$keys);
    }

    /**
     * @link https://redis.io/commands/bitop
     */
    public function storeBitwiseXor(string $destination, string $key, string ...$keys): int
    {
        return $this->execute('bitop', 'xor', $destination, $key, ...$keys);
    }

    /**
     * @link https://redis.io/commands/bitop
     */
    public function storeBitwiseNot(string $destination, string $key): int
    {
        return $this->execute('bitop', 'not', $destination, $key);
    }

    /**
     * @link https://redis.io/commands/bitpos
     */
    public function getBitPosition(string $key, bool $bit, ?int $start = null, ?int $end = null): int
    {
        $payload = [$key, $bit ? 1 : 0];

        if ($start !== null) {
            $payload[] = $start;

            if ($end !== null) {
                $payload[] = $end;
            }
        }

        return $this->execute('bitpos', ...$payload);
    }

    /**
     * @link https://redis.io/commands/decrby
     */
    public function decrement(string $key, int $decrement = 1): int
    {
        if ($decrement === 1) {
            return $this->execute('decr', $key);
        }

        return $this->execute('decrby', $key, $decrement);
    }

    /**
     * @link https://redis.io/commands/get
     */
    public function get(string $key): ?string
    {
        return $this->execute('get', $key);
    }

    /**
     * @link https://redis.io/commands/getbit
     */
    public function getBit(string $key, int $offset): bool
    {
        return (bool) $this->execute('getbit', $key, $offset);
    }

    /**
     * @link https://redis.io/commands/getrange
     */
    public function getRange(string $key, int $start = 0, int $end = -1): string
    {
        return $this->execute('getrange', $key, $start, $end);
    }

    /**
     * @link https://redis.io/commands/getset
     */
    public function getAndSet(string $key, string $value): string
    {
        return $this->execute('getset', $key, $value);
    }

    /**
     * @link https://redis.io/commands/incrby
     */
    public function increment(string $key, int $increment = 1): int
    {
        if ($increment === 1) {
            return $this->execute('incr', $key);
        }

        return $this->execute('incrby', $key, $increment);
    }

    /**
     * @link https://redis.io/commands/incrbyfloat
     */
    public function incrementByFloat(string $key, float $increment): float
    {
        return (float) $this->execute('incrbyfloat', $key, $increment);
    }

    /**
     * @return array<string|null>
     *
     * @link https://redis.io/commands/mget
     */
    public function getMultiple(string $key, string ...$keys): array
    {
        \array_unshift($keys, $key);

        return \array_combine($keys, $this->execute('mget', ...$keys));
    }

    /**
     * @link https://redis.io/commands/mset
     *
     * @param array<string, int|float|string> $data
     */
    public function setMultiple(array $data): void
    {
        $payload = [];

        foreach ($data as $key => $value) {
            $payload[] = $key;
            $payload[] = $value;
        }

        $this->execute('mset', ...$payload);
    }

    /**
     * @link https://redis.io/commands/msetnx
     *
     * @param array<string, int|float|string> $data
     */
    public function setMultipleWithoutOverwrite(array $data): void
    {
        $payload = [];

        foreach ($data as $key => $value) {
            $payload[] = $key;
            $payload[] = $value;
        }

        $this->execute('msetnx', ...$payload);
    }

    /**
     * @link https://redis.io/commands/setnx
     */
    public function setWithoutOverwrite(string $key, string $value): bool
    {
        return (bool) $this->execute('setnx', $key, $value);
    }

    /**
     * @link https://redis.io/commands/set/
     */
    public function set(string $key, string $value, ?SetOptions $options = null): bool
    {
        $options ??= new SetOptions();
        return (bool) $this->execute('set', $key, $value, ...$options->toQuery());
    }

    /**
     * @link https://redis.io/commands/setbit
     */
    public function setBit(string $key, int $offset, bool $value): int
    {
        return $this->execute('setbit', $key, $offset, (int) $value);
    }

    /**
     * @param mixed  $value
     *
     * @link https://redis.io/commands/setrange
     */
    public function setRange(string $key, int $offset, string $value): int
    {
        return $this->execute('setrange', $key, $offset, $value);
    }

    /**
     * @link https://redis.io/commands/strlen
     */
    public function getLength(string $key): int
    {
        return $this->execute('strlen', $key);
    }

    /**
     * @link https://redis.io/commands/publish
     */
    public function publish(string $channel, string $message): int
    {
        return $this->execute('publish', $channel, $message);
    }

    /**
     * @link https://redis.io/commands/pubsub
     */
    public function getActiveChannels(?string $pattern = null): array
    {
        $payload = ['channels'];

        if ($pattern !== null) {
            $payload[] = $pattern;
        }

        return $this->execute('pubsub', ...$payload);
    }

    /**
     * @return int[]
     *
     * @link https://redis.io/commands/pubsub
     */
    public function getNumberOfSubscriptions(string ...$channels): array
    {
        return toMap($this->execute('pubsub', 'numsub', ...$channels));
    }

    /**
     * @link https://redis.io/commands/pubsub
     */
    public function getNumberOfPatternSubscriptions(): int
    {
        return $this->execute('pubsub', 'numpat');
    }

    /**
     * @link https://redis.io/commands/ping
     */
    public function ping(): void
    {
        $this->execute('ping');
    }

    /**
     * @link https://redis.io/commands/quit
     */
    public function quit(): void
    {
        $this->execute('quit');
    }

    /**
     * @link https://redis.io/commands/bgrewriteaof
     */
    public function rewriteAofAsync(): void
    {
        $this->execute('bgrewriteaof');
    }

    /**
     * @link https://redis.io/commands/bgsave
     */
    public function saveAsync(): void
    {
        $this->execute('bgsave');
    }

    /**
     * @link https://redis.io/commands/client-getname
     */
    public function getName(): ?string
    {
        return $this->execute('client', 'getname');
    }

    /**
     * @link https://redis.io/commands/client-pause
     */
    public function pauseMillis(int $timeInMillis): void
    {
        $this->execute('client', 'pause', $timeInMillis);
    }

    /**
     * @link https://redis.io/commands/client-setname
     */
    public function setName(string $name): void
    {
        $this->execute('client', 'setname', $name);
    }

    /**
     * @link https://redis.io/commands/config-get
     */
    public function getConfig(string $parameter): array
    {
        return $this->execute('config', 'get', $parameter);
    }

    /**
     * @link https://redis.io/commands/config-resetstat
     */
    public function resetStatistics(): void
    {
        $this->execute('config', 'resetstat');
    }

    /**
     * @link https://redis.io/commands/config-rewrite
     */
    public function rewriteConfig(): void
    {
        $this->execute('config', 'rewrite');
    }

    /**
     * @link https://redis.io/commands/config-set
     */
    public function setConfig(string $parameter, string $value): void
    {
        $this->execute('config', 'set', $parameter, $value);
    }

    /**
     * @link https://redis.io/commands/dbsize
     */
    public function getDatabaseSize(): int
    {
        return $this->execute('dbsize');
    }

    /**
     * @link https://redis.io/commands/flushall
     */
    public function flushAll(): void
    {
        $this->execute('flushall');
    }

    /**
     * @link https://redis.io/commands/flushdb
     */
    public function flushDatabase(): void
    {
        $this->execute('flushdb');
    }

    /**
     * @link https://redis.io/commands/lastsave
     */
    public function getLastSave(): int
    {
        return $this->execute('lastsave');
    }

    /**
     * @link https://redis.io/commands/role
     */
    public function getRole(): array
    {
        return $this->execute('role');
    }

    /**
     * @link https://redis.io/commands/save
     */
    public function save(): void
    {
        $this->execute('save');
    }

    /**
     * @link https://redis.io/commands/shutdown
     */
    public function shutdownWithSave(): string
    {
        return $this->execute('shutdown', 'save');
    }

    /**
     * @link https://redis.io/commands/shutdown
     */
    public function shutdownWithoutSave(): string
    {
        return $this->execute('shutdown', 'nosave');
    }

    /**
     * @link https://redis.io/commands/shutdown
     */
    public function shutdown(): string
    {
        return $this->execute('shutdown');
    }

    /**
     * @link https://redis.io/commands/slaveof
     */
    public function enableReplication(string $host, int $port): void
    {
        $this->execute('slaveof', $host, $port);
    }

    /**
     * @link https://redis.io/commands/slaveof
     */
    public function disableReplication(): void
    {
        $this->execute('slaveof', 'no', 'one');
    }

    /**
     * @link https://redis.io/commands/slowlog
     */
    public function getSlowlog(?int $count = null): array
    {
        $payload = ['get'];

        if ($count !== null) {
            $payload[] = $count;
        }

        return $this->execute('slowlog', ...$payload);
    }

    /**
     * @link https://redis.io/commands/slowlog
     */
    public function getSlowlogLength(): int
    {
        return $this->execute('slowlog', 'len');
    }

    /**
     * @link https://redis.io/commands/slowlog
     */
    public function resetSlowlog(): void
    {
        $this->execute('slowlog', 'reset');
    }

    /**
     * @link https://redis.io/commands/time
     */
    public function getTime(): array
    {
        return $this->execute('time');
    }

    /**
     * @link https://redis.io/commands/script-exists
     */
    public function hasScript(string $sha1): bool
    {
        $array = $this->execute('script', 'exists', $sha1);
        return (bool) ($array[0] ?? false);
    }

    /**
     * @link https://redis.io/commands/script-flush
     */
    public function flushScripts(): void
    {
        $this->evalCache = $this->createCache(); // same as internal redis behavior

        $this->execute('script', 'flush');
    }

    /**
     * @link https://redis.io/commands/script-kill
     */
    public function killScript(): void
    {
        $this->execute('script', 'kill');
    }

    /**
     * @link https://redis.io/commands/script-load
     */
    public function loadScript(string $script): string
    {
        return $this->execute('script', 'load', $script);
    }

    /**
     * @link https://redis.io/commands/echo
     */
    public function echo(string $text): string
    {
        return $this->execute('echo', $text);
    }

    /**
     * @param array<array-key, string> $keys
     * @param array<array-key, int|float|string> $args
     *
     * @link https://redis.io/commands/eval
     */
    public function eval(string $script, array $keys = [], array $args = []): mixed
    {
        $sha1 = $this->evalCache->computeIfAbsent($script, fn (string $value) => \sha1($value));

        $response = $this->link->execute('evalsha', [$sha1, \count($keys), ...$keys, ...$args]);

        if ($response instanceof RedisError && $response->getKind() === 'NOSCRIPT') {
            return $this->execute('eval', $script, \count($keys), ...$keys, ...$args);
        }

        return $response->unwrap();
    }

    public function select(int $database): void
    {
        $this->execute('select', $database);
    }

    private function createCache(): AtomicCache
    {
        return new AtomicCache(new LocalCache(1000, 60), new LocalKeyedMutex());
    }
}
<?php declare(strict_types=1);

namespace Amp\Redis\Protocol;

use Amp\Redis\RedisException;

final class ProtocolException extends RedisException
{
}
<?php declare(strict_types=1);

namespace Amp\Redis\Protocol;

use Amp\Redis\RedisException;

final class QueryException extends RedisException
{
}
<?php declare(strict_types=1);

namespace Amp\Redis\Protocol;

final class RedisError implements RedisResponse
{
    public function __construct(
        private readonly string $message,
    ) {
    }

    /**
     * @throws QueryException
     */
    public function unwrap(): never
    {
        throw new QueryException($this->message);
    }

    public function getKind(): ?string
    {
        $prefix = \strtok($this->message, ' ');

        // This is just a convention of Redis server, not part of the protocol
        if ($prefix === \strtoupper($prefix)) {
            return $prefix;
        }

        return null;
    }

    public function getMessage(): string
    {
        return $this->message;
    }
}
<?php declare(strict_types=1);

namespace Amp\Redis\Protocol;

/**
 * @psalm-import-type RedisValueType from RedisResponse
 */
final class RedisValue implements RedisResponse
{
    /**
     * @param RedisValueType $value
     */
    public function __construct(
        private readonly int|string|array|null $value,
    ) {
    }

    public function unwrap(): int|string|array|null
    {
        return $this->value;
    }
}
<?php declare(strict_types=1);

namespace Amp\Redis\Protocol;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Parser\Parser;

/**
 * @psalm-type ParserGeneratorType = \Generator<int, int|string, string, RedisResponse>
 */
final class RespParser
{
    use ForbidCloning;
    use ForbidSerialization;

    private const CRLF = "\r\n";

    private const TYPE_SIMPLE_STRING = '+';
    private const TYPE_ERROR = '-';
    private const TYPE_ARRAY = '*';
    private const TYPE_BULK_STRING = '$';
    private const TYPE_INTEGER = ':';

    private readonly Parser $parser;

    /**
     * @psalm-param \Closure(RedisResponse):void $push
     */
    public function __construct(\Closure $push)
    {
        $this->parser = new Parser(self::parser($push));
    }

    public function push(string $data): void
    {
        $this->parser->push($data);
    }

    public function cancel(): void
    {
        $this->parser->cancel();
    }

    /**
     * @param \Closure(RedisResponse):void $push
     *
     * @return \Generator<int, int|string, string, void>
     */
    private static function parser(\Closure $push): \Generator
    {
        while (true) {
            $push(yield from self::parseValue(yield 1, yield self::CRLF));
        }
    }

    /**
     * @psalm-return ParserGeneratorType
     */
    private static function parseValue(string $type, string $payload): \Generator
    {
        return match ($type) {
            self::TYPE_SIMPLE_STRING => new RedisValue($payload),
            self::TYPE_INTEGER => new RedisValue((int) $payload),
            self::TYPE_BULK_STRING => yield from self::parseString((int) $payload),
            self::TYPE_ARRAY => yield from self::parseArray((int) $payload),
            self::TYPE_ERROR => new RedisError($payload),
            default => throw new ProtocolException('Unknown resp data type: ' . $type),
        };
    }

    /**
     * @psalm-return ParserGeneratorType
     */
    private static function parseString(int $length): \Generator
    {
        if ($length < -1) {
            throw new ProtocolException('Invalid string length: ' . $length);
        }

        if ($length === -1) {
            return new RedisValue(null);
        }

        $payload = match ($length) {
            0 => '',
            default => yield $length,
        };

        yield 2; // Remove trailing CRLF

        return new RedisValue($payload);
    }

    /**
     * @psalm-return ParserGeneratorType
     */
    private static function parseArray(int $count): \Generator
    {
        if ($count < -1) {
            throw new ProtocolException('Invalid array length: ' . $count);
        }

        if ($count === -1) {
            return new RedisValue(null);
        }

        $payload = [];
        for ($i = 0; $i < $count; $i++) {
            $payload[] = (yield from self::parseValue(yield 1, yield self::CRLF))->unwrap();
        }

        return new RedisValue($payload);
    }
}
<?php declare(strict_types=1);

namespace Amp\Redis\Protocol;

/**
 * @psalm-type RedisValueType = int|string|list<mixed>|null
 */
interface RedisResponse
{
    /**
     * @return RedisValueType
     */
    public function unwrap(): int|string|array|null;
}
<?php declare(strict_types=1);

namespace Amp\Redis\Connection;

use Amp\Redis\Protocol\RedisResponse;

interface RedisLink
{
    /**
     * @param array<int|float|string> $parameters
     */
    public function execute(string $command, array $parameters): RedisResponse;
}
<?php declare(strict_types=1);

namespace Amp\Redis\Connection;

use Amp\Redis\RedisException;

final class RedisConnectionException extends RedisException
{
}
<?php declare(strict_types=1);

namespace Amp\Redis\Connection;

use Amp\Cancellation;
use Amp\CancelledException;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Redis\RedisException;
use Amp\Socket;
use Amp\Socket\ConnectContext;
use Amp\Socket\SocketConnector;

final class SocketRedisConnector implements RedisConnector
{
    use ForbidCloning;
    use ForbidSerialization;

    private readonly ConnectContext $connectContext;

    public function __construct(
        private readonly string $uri,
        ConnectContext $connectContext,
        private readonly ?SocketConnector $socketConnector = null,
    ) {
        $this->connectContext = $connectContext;
    }

    /**
     * @throws CancelledException
     * @throws RedisException
     * @throws RedisConnectionException
     */
    public function connect(?Cancellation $cancellation = null): RedisConnection
    {
        try {
            $socketConnector = $this->socketConnector ?? Socket\socketConnector();
            $socket = $socketConnector->connect($this->uri, $this->connectContext, $cancellation);
        } catch (Socket\SocketException $e) {
            throw new RedisConnectionException(
                'Failed to connect to redis instance (' . $this->uri . ')',
                0,
                $e
            );
        }

        return new SocketRedisConnection($socket);
    }
}
<?php declare(strict_types=1);

namespace Amp\Redis\Connection;

use Amp\Cancellation;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Redis\RedisException;

final class DatabaseSelector implements RedisConnector
{
    use ForbidCloning;
    use ForbidSerialization;

    public function __construct(
        private readonly int $database,
        private readonly RedisConnector $connector
    ) {
    }

    public function connect(?Cancellation $cancellation = null): RedisConnection
    {
        $connection = $this->connector->connect($cancellation);

        $connection->send('SELECT', (string) $this->database);

        if (!($connection->receive()?->unwrap())) {
            throw new RedisException('Failed to select database: ' . $connection->getName());
        }

        return $connection;
    }
}
<?php declare(strict_types=1);

namespace Amp\Redis\Connection;

use Amp\Cancellation;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Redis\RedisException;

final class Authenticator implements RedisConnector
{
    use ForbidCloning;
    use ForbidSerialization;

    public function __construct(
        #[\SensitiveParameter] private readonly string $password,
        private readonly RedisConnector $connector
    ) {
    }

    public function connect(?Cancellation $cancellation = null): RedisConnection
    {
        $connection = $this->connector->connect($cancellation);

        $connection->send('AUTH', $this->password);

        if (!($connection->receive()?->unwrap())) {
            throw new RedisException('Failed to authenticate to redis instance: ' . $connection->getName());
        }

        return $connection;
    }
}
<?php declare(strict_types=1);

namespace Amp\Redis\Connection;

use Amp\ByteStream\ResourceStream;
use Amp\ByteStream\StreamException;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Pipeline\ConcurrentIterator;
use Amp\Pipeline\Queue;
use Amp\Redis\Protocol\RedisResponse;
use Amp\Redis\Protocol\RespParser;
use Amp\Redis\RedisException;
use Amp\Socket\Socket;
use Revolt\EventLoop;

final class SocketRedisConnection implements RedisConnection
{
    use ForbidCloning;
    use ForbidSerialization;

    private readonly Socket $socket;

    private readonly string $name;

    private readonly ConcurrentIterator $iterator;

    public function __construct(Socket $socket)
    {
        $this->socket = $socket;
        $this->name = $socket->getRemoteAddress()->toString();

        $queue = new Queue();
        $this->iterator = $queue->iterate();

        EventLoop::queue(static function () use ($socket, $queue): void {
            /** @psalm-suppress InvalidArgument */
            $parser = new RespParser($queue->push(...));

            try {
                while (null !== $chunk = $socket->read()) {
                    $parser->push($chunk);
                }

                $parser->cancel();
                $queue->complete();
            } catch (RedisException $e) {
                $queue->error($e);
            }

            $socket->close();
        });
    }

    public function receive(): ?RedisResponse
    {
        if (!$this->iterator->continue()) {
            return null;
        }

        return $this->iterator->getValue();
    }

    public function send(string ...$args): void
    {
        if ($this->socket->isClosed()) {
            throw new RedisConnectionException('Redis connection already closed');
        }

        $payload = \implode("\r\n", \array_map(fn (string $arg) => '$' . \strlen($arg) . "\r\n" . $arg, $args));
        $payload = '*' . \count($args) . "\r\n{$payload}\r\n";

        try {
            $this->socket->write($payload);
        } catch (StreamException $e) {
            throw new RedisConnectionException($e->getMessage(), 0, $e);
        }
    }

    public function getName(): string
    {
        return $this->name;
    }

    public function reference(): void
    {
        if ($this->socket instanceof ResourceStream) {
            $this->socket->reference();
        }
    }

    public function unreference(): void
    {
        if ($this->socket instanceof ResourceStream) {
            $this->socket->unreference();
        }
    }

    public function close(): void
    {
        $this->socket->close();
    }

    public function isClosed(): bool
    {
        return $this->socket->isClosed();
    }

    public function onClose(\Closure $onClose): void
    {
        $this->socket->onClose($onClose);
    }

    public function __destruct()
    {
        $this->close();
    }
}
<?php declare(strict_types=1);

namespace Amp\Redis\Connection;

use Amp\Closable;
use Amp\Redis\Protocol\RedisResponse;
use Amp\Redis\RedisException;

/**
 * A RedisConnection allows sending and receiving values, but does not contain any reconnect logic or linking responses
 * to requests.
 */
interface RedisConnection extends Closable
{
    /**
     * @throws RedisException If reading from the connection fails.
     */
    public function receive(): ?RedisResponse;

    /**
     * @throws RedisException If writing to the connection fails.
     */
    public function send(string ...$args): void;

    /**
     * @return string A name for debugging purposes, e.g. the connect URI.
     */
    public function getName(): string;

    public function reference(): void;

    public function unreference(): void;
}
<?php declare(strict_types=1);

namespace Amp\Redis\Connection;

use Amp\Cancellation;
use Amp\Redis\RedisException;

interface RedisConnector
{
    /**
     * @throws RedisException
     */
    public function connect(?Cancellation $cancellation = null): RedisConnection;
}
<?php declare(strict_types=1);

namespace Amp\Redis\Connection;

use Amp\DeferredFuture;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Future;
use Amp\Redis\Protocol\RedisResponse;
use Amp\Redis\RedisException;
use Revolt\EventLoop;

final class ReconnectingRedisLink implements RedisLink
{
    use ForbidCloning;
    use ForbidSerialization;

    /** @var \SplQueue<array{DeferredFuture, string, list<string>}> */
    private readonly \SplQueue $queue;

    private ?int $database = null;

    private bool $running = false;

    private ?RedisConnection $connection = null;

    public function __construct(private readonly RedisConnector $connector)
    {
        $this->queue = new \SplQueue();
    }

    public function __destruct()
    {
        $this->running = false;
        $this->connection?->close();
    }

    public function execute(string $command, array $parameters): RedisResponse
    {
        if (!$this->running) {
            $this->run();
        }

        $parameters = \array_values(\array_map(\strval(...), $parameters));

        try {
            $response = $this->enqueue($command, $parameters)->await();
        } finally {
            if (\strcasecmp($command, 'quit') === 0) {
                $this->connection?->close();
            }
        }

        if (\strcasecmp($command, 'select') === 0) {
            $this->database = (int) ($parameters[0] ?? 0);
        }

        return $response;
    }

    /**
     * @param list<string> $parameters
     *
     * @return Future<RedisResponse>
     */
    private function enqueue(string $command, array $parameters): Future
    {
        $deferred = new DeferredFuture();
        $this->queue->push([$deferred, $command, $parameters]);

        $this->connection?->reference();

        try {
            $this->connection?->send($command, ...$parameters);
        } catch (RedisException) {
            $this->connection = null;
        }

        return $deferred->getFuture();
    }

    private function run(): void
    {
        $connector = $this->connector;
        $queue = $this->queue;
        $running = &$this->running;
        $connection = &$this->connection;
        $database = &$this->database;

        EventLoop::queue(static function () use (&$connection, &$running, &$database, $queue, $connector): void {
            try {
                while ($running) {
                    if ($database !== null) {
                        $connection = (new DatabaseSelector($database, $connector))->connect();
                    } else {
                        $connection = $connector->connect();
                    }

                    $connection->unreference();

                    try {
                        foreach ($queue as [$deferred, $command, $parameters]) {
                            $connection->reference();
                            $connection->send($command, ...$parameters);
                        }

                        while ($response = $connection->receive()) {
                            /** @var DeferredFuture $deferred */
                            [$deferred] = $queue->shift();
                            if ($queue->isEmpty()) {
                                $connection->unreference();
                            }

                            $deferred->complete($response);
                        }
                    } catch (RedisException) {
                        // Attempt to reconnect after failure.
                    } finally {
                        $connection = null;
                    }
                }
            } catch (\Throwable $exception) {
                $exception = new RedisConnectionException($exception->getMessage(), 0, $exception);

                while (!$queue->isEmpty()) {
                    /** @var DeferredFuture $deferred */
                    [$deferred] = $queue->shift();
                    $deferred->error($exception);
                }

                $running = false;
            }
        });

        $this->running = true;
    }
}
<?php declare(strict_types=1);

namespace Amp\Redis\Internal;

/** @internal */
function toMap(?array $values): array
{
    if ($values === null) {
        return [];
    }

    $size = \count($values);
    $result = [];

    for ($i = 0; $i < $size; $i += 2) {
        $result[$values[$i]] = $values[$i + 1];
    }

    return $result;
}
<?php declare(strict_types=1);

namespace Amp\Redis;

use Exception;

class RedisException extends Exception
{
}
<?php declare(strict_types=1);

namespace Amp\Redis;

use Amp\Cache\Cache;
use Amp\Cache\CacheException;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Redis\Command\Option\SetOptions;
use Amp\Serialization\NativeSerializer;
use Amp\Serialization\Serializer;

/**
 * @template T
 * @implements Cache<T>
 */
final class RedisCache implements Cache
{
    use ForbidCloning;
    use ForbidSerialization;

    public function __construct(
        private readonly RedisClient $redis,
        private readonly Serializer $serializer = new NativeSerializer(),
    ) {
    }

    public function get(string $key): mixed
    {
        try {
            $data = $this->redis->get($key);
            if ($data === null) {
                return null;
            }

            return $this->serializer->unserialize($data);
        } catch (RedisException $e) {
            throw new CacheException("Fetching '$key' from cache failed", 0, $e);
        }
    }

    public function set(string $key, mixed $value, ?int $ttl = null): void
    {
        if ($ttl !== null && $ttl < 0) {
            throw new \Error('Invalid TTL: ' . $ttl);
        }

        if ($ttl === 0) {
            return; // expires immediately
        }

        try {
            $options = new SetOptions;

            if ($ttl !== null) {
                $options = $options->withTtl($ttl);
            }

            $this->redis->set($key, $this->serializer->serialize($value), $options);
        } catch (RedisException $e) {
            throw new CacheException("Storing '{$key}' to cache failed", 0, $e);
        }
    }

    public function delete(string $key): bool
    {
        try {
            return (bool) $this->redis->delete($key);
        } catch (RedisException $e) {
            throw new CacheException("Deleting '{$key}' from cache failed", 0, $e);
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\Redis;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Future;
use Amp\Pipeline\Queue;
use Amp\Redis\Connection\RedisConnection;
use Amp\Redis\Connection\RedisConnectionException;
use Amp\Redis\Connection\RedisConnector;
use Revolt\EventLoop;
use function Amp\async;

final class RedisSubscriber
{
    use ForbidCloning;
    use ForbidSerialization;

    private ?RedisConnection $connection = null;

    private bool $running = false;

    /** @var array<string, array<int, Queue>> */
    private array $queues = [];

    /** @var array<string, array<int, Queue>> */
    private array $patternQueues = [];

    public function __construct(
        private readonly RedisConnector $connector,
    ) {
    }

    public function __destruct()
    {
        $this->running = false;
        $this->connection?->close();
    }

    public function subscribe(string $channel): RedisSubscription
    {
        if (!$this->running) {
            $this->run();
        }

        $subscribe = !isset($this->queues[$channel]);

        $queue = new Queue();
        $this->queues[$channel][\spl_object_id($queue)] = $queue;

        if ($subscribe) {
            try {
                $this->connection?->send('subscribe', $channel);
            } catch (\Throwable $e) {
                $this->unloadEmitter($queue, $channel);

                throw $e;
            }
        }

        return new RedisSubscription($queue->iterate(), fn () => $this->unloadEmitter($queue, $channel));
    }

    public function subscribeToPattern(string $pattern): RedisSubscription
    {
        if (!$this->running) {
            $this->run();
        }

        $subscribe = !isset($this->patternQueues[$pattern]);

        $queue = new Queue();
        $this->patternQueues[$pattern][\spl_object_id($queue)] = $queue;

        if ($subscribe) {
            try {
                $this->connection?->send('psubscribe', $pattern);
            } catch (\Throwable $e) {
                $this->unloadPatternEmitter($queue, $pattern);

                throw $e;
            }
        }

        return new RedisSubscription($queue->iterate(), fn () => $this->unloadPatternEmitter($queue, $pattern));
    }

    private function run(): void
    {
        $connector = $this->connector;
        $running = &$this->running;
        $connection = &$this->connection;
        $queues = &$this->queues;
        $patternQueues = &$this->patternQueues;

        EventLoop::queue(static function () use (
            &$running,
            &$connection,
            &$queues,
            &$patternQueues,
            $connector
        ): void {
            try {
                while ($running) {
                    $connection = $connector->connect();

                    try {
                        foreach (\array_keys($queues) as $queue) {
                            $connection->send('subscribe', $queue);
                        }

                        foreach (\array_keys($patternQueues) as $pattern) {
                            $connection->send('psubscribe', $pattern);
                        }

                        while ($response = $connection->receive()?->unwrap()) {
                            /** @psalm-suppress RedundantCondition */
                            \assert(
                                \is_array($response) && \array_is_list($response),
                                'Expected a list from RespSocket on subscription',
                            );

                            switch ($response[0]) {
                                case 'message':
                                    self::mapToQueues($queues[$response[1]] ?? [], $response[2]);
                                    break;

                                case 'pmessage':
                                    self::mapToQueues($patternQueues[$response[1]] ?? [], [$response[3], $response[2]]);
                                    break;
                            }
                        }
                    } catch (RedisException) {
                        // Attempt to reconnect after failure.
                    } finally {
                        $connection = null;
                    }
                }
            } catch (\Throwable $exception) {
                $exception = new RedisConnectionException($exception->getMessage(), 0, $exception);

                $queueGroups = \array_merge($queues, $patternQueues);

                $queues = [];
                $patternQueues = [];

                foreach ($queueGroups as $queueGroup) {
                    foreach ($queueGroup as $queue) {
                        $queue->error($exception);
                    }
                }

                $running = false;
            }
        });

        $this->running = true;
    }

    private function isIdle(): bool
    {
        return !$this->queues && !$this->patternQueues;
    }

    private function unloadEmitter(Queue $queue, string $channel): void
    {
        $hash = \spl_object_id($queue);

        if (isset($this->queues[$channel][$hash])) {
            unset($this->queues[$channel][$hash]);

            $queue->complete();

            if (empty($this->queues[$channel])) {
                unset($this->queues[$channel]);

                async(function () use ($channel): void {
                    try {
                        if (empty($this->queues[$channel])) {
                            $this->connection?->send('unsubscribe', $channel);
                        }

                        if ($this->isIdle()) {
                            $this->connection?->close();
                        }
                    } catch (RedisException) {
                        // if there's an exception, the unsubscribe is implicitly successful, because the connection broke
                    }
                })->ignore();
            }
        }
    }

    private function unloadPatternEmitter(Queue $queue, string $pattern): void
    {
        $hash = \spl_object_id($queue);

        if (isset($this->patternQueues[$pattern][$hash])) {
            unset($this->patternQueues[$pattern][$hash]);

            $queue->complete();

            if (empty($this->patternQueues[$pattern])) {
                unset($this->patternQueues[$pattern]);

                async(function () use ($pattern): void {
                    try {
                        if (empty($this->patternQueues[$pattern])) {
                            $this->connection?->send('punsubscribe', $pattern);
                        }

                        if ($this->isIdle()) {
                            $this->connection?->close();
                        }
                    } catch (RedisException) {
                        // if there's an exception, the unsubscribe is implicitly successful, because the connection broke
                    }
                })->ignore();
            }
        }
    }

    /**
     * @param array<int, Queue> $queues
     */
    private static function mapToQueues(array $queues, mixed $value): void
    {
        $backpressure = [];
        foreach ($queues as $queue) {
            $backpressure[] = $queue->pushAsync($value);
        }
        Future\awaitAll($backpressure);
    }
}
<?php declare(strict_types=1);

namespace Amp\Redis;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Pipeline\ConcurrentIterator;
use Revolt\EventLoop;

/**
 * @implements \IteratorAggregate<int, string>
 */
final class RedisSubscription implements \IteratorAggregate
{
    use ForbidCloning;
    use ForbidSerialization;

    /** @var null|\Closure():void */
    private ?\Closure $unsubscribe;

    /**
     * @param \Closure():void $unsubscribe
     */
    public function __construct(
        private readonly ConcurrentIterator $iterator,
        \Closure $unsubscribe,
    ) {
        $this->unsubscribe = $unsubscribe;
    }

    public function __destruct()
    {
        $this->unsubscribe();
    }

    /**
     * Using a Generator to maintain a reference to $this.
     */
    public function getIterator(): \Traversable
    {
        yield from $this->iterator;
    }

    public function unsubscribe(): void
    {
        if ($this->unsubscribe) {
            EventLoop::queue($this->unsubscribe);
            $this->unsubscribe = null;
        }

        $this->iterator->dispose();
    }
}
<?php declare(strict_types=1);

namespace Amp\Redis;

use Amp\Redis\Connection\Authenticator;
use Amp\Redis\Connection\DatabaseSelector;
use Amp\Redis\Connection\ReconnectingRedisLink;
use Amp\Redis\Connection\RedisConnector;
use Amp\Redis\Connection\SocketRedisConnector;
use Amp\Socket\ConnectContext;

function createRedisConnector(RedisConfig|string $config, ?RedisConnector $connector = null): RedisConnector
{
    if (\is_string($config)) {
        $config = RedisConfig::fromUri($config);
    }

    $connector ??= new SocketRedisConnector(
        $config->getConnectUri(),
        (new ConnectContext())->withConnectTimeout($config->getTimeout())
    );

    if ($config->hasPassword()) {
        $connector = new Authenticator($config->getPassword(), $connector);
    }

    if ($config->getDatabase() !== 0) {
        $connector = new DatabaseSelector($config->getDatabase(), $connector);
    }

    return $connector;
}

function createRedisClient(RedisConfig|string $config, ?RedisConnector $connector = null): RedisClient
{
    return new RedisClient(new ReconnectingRedisLink(createRedisConnector($config, $connector)));
}
{
    "name": "amphp/redis",
    "description": "Efficient asynchronous communication with Redis servers, enabling scalable and responsive data storage and retrieval.",
    "keywords": [
        "amp",
        "amphp",
        "async",
        "client",
        "redis",
        "revolt"
    ],
    "license": "MIT",
    "homepage": "https://amphp.org/redis",
    "authors": [
        {
            "name": "Niklas Keller",
            "email": "me@kelunik.com"
        },
        {
            "name": "Aaron Piotrowski",
            "email": "aaron@trowski.com"
        }
    ],
    "support": {
        "issues": "https://github.com/amphp/redis/issues"
    },
    "require": {
        "php": ">=8.1",
        "amphp/amp": "^3",
        "amphp/byte-stream": "^2",
        "amphp/cache": "^2",
        "amphp/parser": "^1",
        "amphp/pipeline": "^1",
        "amphp/serialization": "^1",
        "amphp/socket": "^2",
        "amphp/sync": "^2",
        "revolt/event-loop": "^1",
        "psr/log": "^1|^2|^3",
        "league/uri": "^7"
    },
    "require-dev": {
        "amphp/phpunit-util": "^3",
        "amphp/process": "^2",
        "phpunit/phpunit": "^9",
        "amphp/php-cs-fixer-config": "^2",
        "psalm/phar": "5.22"
    },
    "autoload": {
        "psr-4": {
            "Amp\\Redis\\": "src"
        },
        "files": [
            "src/functions.php",
            "src/Internal/functions.php"
        ]
    },
    "autoload-dev": {
        "psr-4": {
            "Amp\\Redis\\": "test"
        }
    }
}
<?php

$config = new Amp\CodeStyle\Config;
$config->getFinder()
    ->in(__DIR__ . '/src')
    ->in(__DIR__ . '/test');

$config->setCacheFile(__DIR__ . '/.php_cs.cache');

return $config;
{
    "symbol-whitelist": [
        "null",
        "true",
        "false",
        "static",
        "self",
        "parent",
        "array",
        "string",
        "int",
        "float",
        "bool",
        "iterable",
        "callable",
        "mixed",
        "void",
        "object",
        "random_bytes",
        "deflate_add",
        "deflate_init",
        "generateAcceptFromKey",
        "GUID",
        "inflate_add",
        "inflate_init",
        "ZLIB_ENCODING_RAW",
        "ZLIB_FULL_FLUSH",
        "ZLIB_SYNC_FLUSH"
    ],
    "php-core-extensions": [
        "Core",
        "date",
        "pcre",
        "Phar",
        "Reflection",
        "SPL",
        "standard",
        "hash",
        "zlib"
    ]
}
<?php declare(strict_types=1);

namespace Amp\Websocket;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Revolt\EventLoop;
use Revolt\EventLoop\Suspension;
use function Amp\weakClosure;

final class ConstantRateLimit implements WebsocketRateLimit
{
    use ForbidCloning;
    use ForbidSerialization;

    /** @var array<int, int> */
    private array $bytesReadInLastSecond = [];

    /** @var array<int, int> */
    private array $framesReadInLastSecond = [];

    /** @var Suspension[] */
    private array $rateSuspensions = [];

    private readonly string $watcher;

    /**
     * @param positive-int $bytesPerSecondLimit
     * @param positive-int $framesPerSecondLimit
     */
    public function __construct(
        private readonly int $bytesPerSecondLimit = 1048576, // 1MB
        private readonly int $framesPerSecondLimit = 100,
    ) {
        /** @psalm-suppress TypeDoesNotContainType */
        if ($this->bytesPerSecondLimit <= 0) {
            throw new \ValueError('Bytes-per-second limit must be greater than 0');
        }

        /** @psalm-suppress TypeDoesNotContainType */
        if ($this->framesPerSecondLimit <= 0) {
            throw new \ValueError('Frames-per-second limit must be greater than 0');
        }

        $this->watcher = EventLoop::repeat(1, weakClosure(function (string $watcher): void {
            $this->bytesReadInLastSecond = [];
            $this->framesReadInLastSecond = [];

            if (!empty($this->rateSuspensions)) {
                EventLoop::unreference($watcher);

                foreach ($this->rateSuspensions as $suspension) {
                    $suspension->resume();
                }

                $this->rateSuspensions = [];
            }
        }));

        EventLoop::unreference($this->watcher);
    }

    public function __destruct()
    {
        EventLoop::cancel($this->watcher);
    }

    public function notifyBytesReceived(int $clientId, int $byteCount): void
    {
        $count = $this->bytesReadInLastSecond[$clientId] = ($this->bytesReadInLastSecond[$clientId] ?? 0) + $byteCount;

        if ($count >= $this->bytesPerSecondLimit) {
            $suspension = $this->rateSuspensions[$clientId] ??= EventLoop::getSuspension();
            EventLoop::reference($this->watcher);
            $suspension->suspend();
        }
    }

    public function notifyFramesReceived(int $clientId, int $frameCount): void
    {
        $count = $this->framesReadInLastSecond[$clientId] = ($this->framesReadInLastSecond[$clientId] ?? 0) + $frameCount;

        if ($count >= $this->framesPerSecondLimit) {
            $suspension = $this->rateSuspensions[$clientId] ??= EventLoop::getSuspension();
            EventLoop::reference($this->watcher);
            $suspension->suspend();
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\Websocket;

enum WebsocketCount
{
    case BytesReceived;
    case BytesSent;
    case FramesReceived;
    case FramesSent;
    case MessagesReceived;
    case MessagesSent;
    case PingsReceived;
    case PingsSent;
    case PongsReceived;
    case PongsSent;
    case UnansweredPings;
}
<?php declare(strict_types=1);

namespace Amp\Websocket;

use Amp\ByteStream\BufferException;
use Amp\ByteStream\Payload;
use Amp\ByteStream\ReadableStream;
use Amp\Cancellation;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;

/**
 * This class allows streamed and buffered access to the websocket message.
 *
 * @implements \IteratorAggregate<int, string>
 */
final class WebsocketMessage implements ReadableStream, \IteratorAggregate, \Stringable
{
    use ForbidCloning;
    use ForbidSerialization;

    private readonly Payload $stream;

    /**
     * Create a WebsocketMessage from a UTF-8 text stream.
     *
     * @param ReadableStream|string $stream UTF-8 text stream or string.
     */
    public static function fromText(ReadableStream|string $stream): self
    {
        return new self($stream, false);
    }

    /**
     * Create a WebsocketMessage from a binary stream.
     *
     * @param ReadableStream|string $stream Binary stream or string.
     */
    public static function fromBinary(ReadableStream|string $stream): self
    {
        return new self($stream, true);
    }

    private function __construct(ReadableStream|string $stream, private readonly bool $binary)
    {
        $this->stream = new Payload($stream);
    }

    /**
     * @return bool True if the message is UTF-8 text, false if it is binary.
     *
     * @see WebsocketMessage::isBinary()
     */
    public function isText(): bool
    {
        return !$this->binary;
    }

    /**
     * @return bool True if the message is binary, false if it is UTF-8 text.
     *
     * @see WebsocketMessage::isText()
     */
    public function isBinary(): bool
    {
        return $this->binary;
    }

    /**
     * @throws WebsocketClosedException
     */
    public function read(?Cancellation $cancellation = null): ?string
    {
        return $this->stream->read($cancellation);
    }

    /**
     * Buffer the entire message contents. Note that the given size limit may not be reached if a smaller message size
     * limit has been imposed by {@see Rfc6455Client::$messageSizeLimit}.
     *
     * @throws WebsocketClosedException|BufferException
     * @see Payload::buffer()
     */
    public function buffer(?Cancellation $cancellation = null, int $limit = \PHP_INT_MAX): string
    {
        return $this->stream->buffer($cancellation, $limit);
    }

    public function isReadable(): bool
    {
        return $this->stream->isReadable();
    }

    /**
     * Indicates the remainder of the message is no longer needed and will be discarded.
     */
    public function close(): void
    {
        $this->stream->close();
    }

    public function isClosed(): bool
    {
        return $this->stream->isClosed();
    }

    public function onClose(\Closure $onClose): void
    {
        $this->stream->onClose($onClose);
    }

    public function getIterator(): \Traversable
    {
        return $this->stream->getIterator();
    }

    /**
     * Buffers entire stream before returning. Use {@see self::buffer()} to optionally provide a {@see Cancellation}
     * and/or length limit.
     *
     * @throws WebsocketClosedException|BufferException
     */
    public function __toString(): string
    {
        return $this->buffer();
    }
}
<?php declare(strict_types=1);

namespace Amp\Websocket;

final class WebsocketCloseInfo
{
    public function __construct(
        private readonly int $code,
        private readonly string $reason,
        private readonly float $timestamp,
        private readonly bool $byPeer,
    ) {
    }

    /**
     * @return float Unix timestamp (including fractions of a second) at which the connection was closed.
     */
    public function getTimestamp(): float
    {
        return $this->timestamp;
    }

    /**
     * See {@see WebsocketCloseCode} for protocol-defined close codes. Note that close codes are not limited to those
     * defined by the protocol.
     */
    public function getCode(): int
    {
        return $this->code;
    }

    public function getReason(): string
    {
        return $this->reason;
    }

    /**
     * @return bool `true` if the connection was closed by the peer.
     */
    public function isByPeer(): bool
    {
        return $this->byPeer;
    }
}
<?php declare(strict_types=1);

namespace Amp\Websocket;

final class WebsocketCloseCode
{
    public const NORMAL_CLOSE = 1000;
    public const GOING_AWAY = 1001;
    public const PROTOCOL_ERROR = 1002;
    public const UNACCEPTABLE_TYPE = 1003;
    // 1004 reserved and unused.
    public const NONE = 1005;
    public const ABNORMAL_CLOSE = 1006;
    public const INCONSISTENT_FRAME_DATA_TYPE = 1007;
    public const POLICY_VIOLATION = 1008;
    public const MESSAGE_TOO_LARGE = 1009;
    public const EXPECTED_EXTENSION_MISSING = 1010;
    public const UNEXPECTED_SERVER_ERROR = 1011;
    public const SERVICE_RESTARTING = 1012;
    public const TRY_AGAIN_LATER = 1013;
    public const BAD_GATEWAY = 1014;
    public const TLS_HANDSHAKE_FAILURE = 1015;

    /**
     * @param int $code Close code.
     *
     * @return string|null Constant name corresponding to the given code or null if the code is undefined.
     */
    public static function getName(int $code): ?string
    {
        return match ($code) {
            self::NORMAL_CLOSE => 'NORMAL_CLOSE',
            self::GOING_AWAY => 'GOING_AWAY',
            self::PROTOCOL_ERROR => 'PROTOCOL_ERROR',
            self::UNACCEPTABLE_TYPE => 'UNACCEPTABLE_TYPE',
            self::NONE => 'NONE',
            self::ABNORMAL_CLOSE => 'ABNORMAL_CLOSE',
            self::INCONSISTENT_FRAME_DATA_TYPE => 'INCONSISTENT_FRAME_DATA_TYPE',
            self::POLICY_VIOLATION => 'POLICY_VIOLATION',
            self::MESSAGE_TOO_LARGE => 'MESSAGE_TOO_LARGE',
            self::EXPECTED_EXTENSION_MISSING => 'EXPECTED_EXTENSION_MISSING',
            self::UNEXPECTED_SERVER_ERROR => 'UNEXPECTED_SERVER_ERROR',
            self::SERVICE_RESTARTING => 'SERVICE_RESTARTING',
            self::TRY_AGAIN_LATER => 'TRY_AGAIN_LATER',
            self::BAD_GATEWAY => 'BAD_GATEWAY',
            self::TLS_HANDSHAKE_FAILURE => 'TLS_HANDSHAKE_FAILURE',
            default => null,
        };
    }

    /**
     * Returns true if the given code is expected to be sent by browsers when closing a connection.
     * Some applications may define other expected close codes, in which case this function may not apply.
     */
    public static function isExpected(int $code): bool
    {
        return match ($code) {
            WebsocketCloseCode::NORMAL_CLOSE, WebsocketCloseCode::GOING_AWAY, WebsocketCloseCode::NONE => true,
            default => false,
        };
    }

    /**
     * @codeCoverageIgnore Class cannot be instantiated.
     */
    private function __construct()
    {
        // no instances allowed
    }
}
<?php declare(strict_types=1);

namespace Amp\Websocket;

use Amp\ByteStream\ReadableBuffer;
use Amp\ByteStream\ReadableStream;
use Amp\ByteStream\StreamException;
use Amp\Cancellation;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Future;
use Amp\Pipeline\ConcurrentIterator;
use Amp\Socket\Socket;
use Amp\Socket\SocketAddress;
use Amp\Socket\TlsInfo;
use Amp\Websocket\Compression\WebsocketCompressionContext;
use Amp\Websocket\Parser\Rfc6455FrameCompilerFactory;
use Amp\Websocket\Parser\Rfc6455ParserFactory;
use Amp\Websocket\Parser\WebsocketFrameCompilerFactory;
use Amp\Websocket\Parser\WebsocketFrameType;
use Amp\Websocket\Parser\WebsocketParserFactory;
use Revolt\EventLoop;

/**
 * @implements \IteratorAggregate<int, WebsocketMessage>
 */
final class Rfc6455Client implements WebsocketClient, \IteratorAggregate
{
    use ForbidCloning;
    use ForbidSerialization;

    public const DEFAULT_FRAME_SPLIT_THRESHOLD = 32768; // 32KB
    public const DEFAULT_CLOSE_PERIOD = 3;

    /** @var ConcurrentIterator<WebsocketMessage> */
    private readonly ConcurrentIterator $messageIterator;

    private readonly Internal\Rfc6455FrameHandler $frameHandler;

    private readonly Internal\WebsocketClientMetadata $metadata;

    private ?Future $lastWrite = null;

    /**
     * @param bool $masked True for client, false for server.
     */
    public function __construct(
        private readonly Socket $socket,
        bool $masked,
        WebsocketParserFactory $parserFactory = new Rfc6455ParserFactory(),
        WebsocketFrameCompilerFactory $compilerFactory = new Rfc6455FrameCompilerFactory(),
        ?WebsocketCompressionContext $compressionContext = null,
        ?WebsocketHeartbeatQueue $heartbeatQueue = null,
        ?WebsocketRateLimit $rateLimit = null,
        private readonly int $frameSplitThreshold = self::DEFAULT_FRAME_SPLIT_THRESHOLD,
        float $closePeriod = self::DEFAULT_CLOSE_PERIOD,
    ) {
        $this->metadata = new Internal\WebsocketClientMetadata($compressionContext !== null);

        $this->frameHandler = new Internal\Rfc6455FrameHandler(
            socket: $this->socket,
            frameCompiler: $compilerFactory->createFrameCompiler($masked, $compressionContext),
            heartbeatQueue: $heartbeatQueue,
            rateLimit: $rateLimit,
            metadata: $this->metadata,
            closePeriod: $closePeriod,
        );

        $this->messageIterator = $this->frameHandler->iterate();

        $heartbeatQueue?->insert($this);

        EventLoop::queue(
            $this->frameHandler->read(...),
            $parserFactory->createParser($this->frameHandler, $masked, $compressionContext),
        );
    }

    public function __destruct()
    {
        if ($this->metadata->isClosed()) {
            return;
        }

        $frameHandler = $this->frameHandler;
        $lastWrite = $this->lastWrite ?? Future::complete();
        $lastWrite->finally(static fn () => $frameHandler->close(WebsocketCloseCode::GOING_AWAY))->ignore();
    }

    public function receive(?Cancellation $cancellation = null): ?WebsocketMessage
    {
        return $this->messageIterator->continue($cancellation)
            ? $this->messageIterator->getValue()
            : null;
    }

    public function getIterator(): \Traversable
    {
        while ($message = $this->receive()) {
            yield $message;
        }
    }

    public function getId(): int
    {
        return $this->metadata->id;
    }

    public function getLocalAddress(): SocketAddress
    {
        return $this->socket->getLocalAddress();
    }

    public function getRemoteAddress(): SocketAddress
    {
        return $this->socket->getRemoteAddress();
    }

    public function getTlsInfo(): ?TlsInfo
    {
        return $this->socket->getTlsInfo();
    }

    public function getCloseInfo(): WebsocketCloseInfo
    {
        if (!$this->metadata->closeInfo) {
            throw new \Error('The client has not closed; check WebsocketClient::isClosed() before calling this method');
        }

        return $this->metadata->closeInfo;
    }

    public function isCompressionEnabled(): bool
    {
        return $this->metadata->compressionEnabled;
    }

    public function getCount(WebsocketCount $type): int
    {
        return match ($type) {
            WebsocketCount::BytesReceived => $this->metadata->bytesReceived,
            WebsocketCount::BytesSent => $this->metadata->bytesSent,
            WebsocketCount::FramesReceived => $this->metadata->framesReceived,
            WebsocketCount::FramesSent => $this->metadata->framesSent,
            WebsocketCount::MessagesReceived => $this->metadata->messagesReceived,
            WebsocketCount::MessagesSent => $this->metadata->messagesSent,
            WebsocketCount::PingsReceived => $this->metadata->pingsReceived,
            WebsocketCount::PingsSent => $this->metadata->pingsSent,
            WebsocketCount::PongsReceived => $this->metadata->pongsReceived,
            WebsocketCount::PongsSent => $this->metadata->pongsSent,
            WebsocketCount::UnansweredPings => \max(0, $this->metadata->pingsSent - $this->metadata->pongsReceived),
        };
    }

    public function getTimestamp(WebsocketTimestamp $type): float
    {
        return match ($type) {
            WebsocketTimestamp::Connected => $this->metadata->connectedAt,
            WebsocketTimestamp::Closed => $this->metadata->closeInfo?->getTimestamp() ?? \NAN,
            WebsocketTimestamp::LastRead => $this->metadata->lastReadAt,
            WebsocketTimestamp::LastSend => $this->metadata->lastSentAt,
            WebsocketTimestamp::LastDataRead => $this->metadata->lastDataReadAt,
            WebsocketTimestamp::LastDataSend => $this->metadata->lastDataSentAt,
            WebsocketTimestamp::LastHeartbeat => $this->metadata->lastHeartbeatAt,
        };
    }

    public function sendText(string $data): void
    {
        \assert((bool) \preg_match('//u', $data), 'Text data must be UTF-8');
        $this->pushData(WebsocketFrameType::Text, $data);
    }

    public function sendBinary(string $data): void
    {
        $this->pushData(WebsocketFrameType::Binary, $data);
    }

    public function streamText(ReadableStream $stream): void
    {
        $this->pushStream(WebsocketFrameType::Text, $stream);
    }

    public function streamBinary(ReadableStream $stream): void
    {
        $this->pushStream(WebsocketFrameType::Binary, $stream);
    }

    public function ping(): void
    {
        ++$this->metadata->pingsSent;
        $this->frameHandler->write(WebsocketFrameType::Ping, (string) $this->metadata->pingsSent);
    }

    private function pushData(WebsocketFrameType $frameType, string $data): void
    {
        if ($this->lastWrite || \strlen($data) > $this->frameSplitThreshold) {
            // Treat as a stream if another stream is pending or if splitting the data into multiple frames.
            $this->pushStream($frameType, new ReadableBuffer($data));
            return;
        }

        // The majority of messages can be sent with a single frame.
        $this->sendData($frameType, $data);
    }

    private function sendData(WebsocketFrameType $frameType, string $data): void
    {
        ++$this->metadata->messagesSent;
        $this->metadata->lastDataSentAt = \microtime(true);

        try {
            $this->frameHandler->write($frameType, $data);
        } catch (\Throwable $exception) {
            $code = WebsocketCloseCode::ABNORMAL_CLOSE;
            $reason = 'Writing to the client failed';
            $this->close($code, $reason);
            throw new WebsocketClosedException('Client unexpectedly closed', $code, $reason, $exception);
        }
    }

    private function pushStream(WebsocketFrameType $frameType, ReadableStream $stream): void
    {
        $this->lastWrite ??= Future::complete();

        // Setting $this->lastWrite will force subsequent sends to queue until this stream has ended.
        /** @psalm-suppress UndefinedVariable $thisWrite is defined below. */
        $this->lastWrite = $thisWrite = $this->lastWrite->map(
            function () use (&$thisWrite, $stream, $frameType): void {
                try {
                    $this->sendStream($stream, $frameType);
                } finally {
                    // Null the reference to this coroutine if no other writes have been made so subsequent
                    // writes do not have to await a future.
                    if ($this->lastWrite === $thisWrite) {
                        $this->lastWrite = null;
                    }
                }
            }
        );

        $this->lastWrite->await();
    }

    private function sendStream(ReadableStream $stream, WebsocketFrameType $frameType): void
    {
        ++$this->metadata->messagesSent;
        $this->metadata->lastDataSentAt = \microtime(true);

        try {
            $chunk = $stream->read();

            if ($chunk === null) {
                $this->frameHandler->write($frameType, '');
                return;
            }

            do {
                $buffer = $chunk;

                // Perform another read to avoid sending an empty frame on stream end.
                $chunk = $stream->read();

                $bufferedLength = \strlen($buffer);
                if ($bufferedLength === 0) {
                    continue;
                }

                if ($bufferedLength > $this->frameSplitThreshold) {
                    $splitLength = $bufferedLength;
                    $slices = (int) \ceil($splitLength / $this->frameSplitThreshold);
                    $splitLength = (int) \ceil($splitLength / $slices);

                    for ($i = 0; $i < $slices - 1; ++$i) {
                        $split = \substr($buffer, $splitLength * $i, $splitLength);

                        $this->frameHandler->write($frameType, $split, false);
                        $frameType = WebsocketFrameType::Continuation;
                    }

                    $buffer = \substr($buffer, $splitLength * $i, $splitLength);
                }

                $this->frameHandler->write($frameType, $buffer, $chunk === null);
                $frameType = WebsocketFrameType::Continuation;
            } while ($chunk !== null);
        } catch (StreamException $exception) {
            $code = WebsocketCloseCode::ABNORMAL_CLOSE;
            $reason = 'Writing to the client failed';
            $this->close($code, $reason);
            throw new WebsocketClosedException('Client unexpectedly closed', $code, $reason, $exception);
        } catch (\Throwable $exception) {
            $this->close(WebsocketCloseCode::UNEXPECTED_SERVER_ERROR, 'Error while reading message data');
            throw $exception;
        }
    }

    public function isClosed(): bool
    {
        return $this->metadata->isClosed();
    }

    public function close(int $code = WebsocketCloseCode::NORMAL_CLOSE, string $reason = ''): void
    {
        $this->frameHandler->close($code, $reason);
        $this->lastWrite = null;
    }

    public function onClose(\Closure $onClose): void
    {
        $this->frameHandler->onClose($onClose);
    }
}
<?php declare(strict_types=1);

namespace Amp\Websocket\Parser;

/**
 * Frame type determined by frame opcode.
 * @see https://www.rfc-editor.org/rfc/rfc6455#section-11.8
 */
enum WebsocketFrameType: int
{
    case Continuation = 0x00;
    case Text = 0x01;
    case Binary = 0x02;
    case Close = 0x08;
    case Ping = 0x09;
    case Pong = 0x0A;

    public function isControlFrame(): bool
    {
        return $this->value >= 0x08;
    }
}
<?php declare(strict_types=1);

namespace Amp\Websocket\Parser;

use Amp\Websocket\Compression\WebsocketCompressionContext;

interface WebsocketFrameCompilerFactory
{
    public function createFrameCompiler(
        bool $masked,
        ?WebsocketCompressionContext $compressionContext = null,
    ): WebsocketFrameCompiler;
}
<?php declare(strict_types=1);

namespace Amp\Websocket\Parser;

interface WebsocketParser
{
    /**
     * Parse websocket frames from peer data, invoking {@see WebsocketFrameHandler::handleFrame()} for each frame.
     *
     * @throws WebsocketParserException
     */
    public function push(string $data): void;

    /**
     * Cancel parsing and free any associated resources.
     */
    public function cancel(): void;
}
<?php declare(strict_types=1);

namespace Amp\Websocket\Parser;

use Amp\Websocket\Compression\WebsocketCompressionContext;

interface WebsocketParserFactory
{
    public function createParser(
        WebsocketFrameHandler $frameHandler,
        bool $masked,
        ?WebsocketCompressionContext $compressionContext = null,
    ): WebsocketParser;
}
<?php declare(strict_types=1);

namespace Amp\Websocket\Parser;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Websocket\Compression\WebsocketCompressionContext;

final class Rfc6455FrameCompiler implements WebsocketFrameCompiler
{
    use ForbidCloning;
    use ForbidSerialization;

    private ?WebsocketFrameType $currentFrameType = null;

    private bool $compressPayload = false;

    public function __construct(
        private readonly bool $masked,
        private readonly ?WebsocketCompressionContext $compressionContext = null,
    ) {
    }

    public function compileFrame(WebsocketFrameType $frameType, string $data, bool $isFinal): string
    {
        \assert($this->assertState($frameType));

        if ($this->compressionContext && $frameType === WebsocketFrameType::Text) {
            $this->compressPayload = !$isFinal || \strlen($data) > $this->compressionContext->getCompressionThreshold();
        }

        $isDataFrame = match ($frameType) {
            WebsocketFrameType::Text, WebsocketFrameType::Binary => true,
            default => false,
        };
        if ($isDataFrame) {
            $this->currentFrameType = $frameType;
        }

        $rsv = 0;

        try {
            if ($this->compressionContext && $this->compressPayload && match ($frameType) {
                WebsocketFrameType::Text, WebsocketFrameType::Continuation => true,
                default => false,
            }) {
                if ($frameType !== WebsocketFrameType::Continuation) {
                    $rsv |= $this->compressionContext->getRsv();
                }

                $data = $this->compressionContext->compress($data, $isFinal);
            }
        } catch (\Throwable $exception) {
            $isFinal = true; // Reset state in finally.
            throw $exception;
        } finally {
            if (($isDataFrame || $frameType === WebsocketFrameType::Continuation) && $isFinal) {
                $this->currentFrameType = null;
                $this->compressPayload = false;
            }
        }

        $length = \strlen($data);
        $w = \chr(((int) $isFinal << 7) | ($rsv << 4) | $frameType->value);

        $maskFlag = $this->masked ? 0x80 : 0;

        if ($length > 0xFFFF) {
            $w .= \chr(0x7F | $maskFlag) . \pack('J', $length);
        } elseif ($length > 0x7D) {
            $w .= \chr(0x7E | $maskFlag) . \pack('n', $length);
        } else {
            $w .= \chr($length | $maskFlag);
        }

        if ($this->masked) {
            $mask = \random_bytes(4);
            return $w . $mask . ($data ^ \str_repeat($mask, ($length + 3) >> 2));
        }

        return $w . $data;
    }

    private function assertState(WebsocketFrameType $frameType): bool
    {
        if ($this->currentFrameType && match ($frameType) {
            WebsocketFrameType::Text, WebsocketFrameType::Binary => true,
            default => false,
        }) {
            throw new \ValueError(\sprintf(
                'Next frame must be a continuation or control frame; got %s (0x%d) while sending %s (0x%d)',
                $frameType->name,
                \dechex($frameType->value),
                $this->currentFrameType->name,
                \dechex($frameType->value),
            ));
        }

        if (!$this->currentFrameType && $frameType === WebsocketFrameType::Continuation) {
            throw new \ValueError('Cannot send a continuation frame without sending a non-final text or binary frame');
        }

        return true;
    }
}
<?php declare(strict_types=1);

namespace Amp\Websocket\Parser;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Parser\Parser;
use Amp\Websocket\Compression\WebsocketCompressionContext;
use Amp\Websocket\WebsocketCloseCode;

final class Rfc6455Parser implements WebsocketParser
{
    use ForbidCloning;
    use ForbidSerialization;

    public const DEFAULT_TEXT_ONLY = false;
    public const DEFAULT_VALIDATE_UTF8 = true;
    public const DEFAULT_MESSAGE_SIZE_LIMIT = (2 ** 20) * 10; // 10MB
    public const DEFAULT_FRAME_SIZE_LIMIT = 2 ** 20; // 1MB

    private readonly Parser $parser;

    public function __construct(
        WebsocketFrameHandler $frameHandler,
        private readonly bool $masked,
        private readonly ?WebsocketCompressionContext $compressionContext = null,
        bool $textOnly = self::DEFAULT_TEXT_ONLY,
        bool $validateUtf8 = self::DEFAULT_VALIDATE_UTF8,
        int $messageSizeLimit = self::DEFAULT_MESSAGE_SIZE_LIMIT,
        int $frameSizeLimit = self::DEFAULT_FRAME_SIZE_LIMIT,
    ) {
        $this->parser = new Parser(self::parse(
            $frameHandler,
            $masked,
            $compressionContext,
            $textOnly,
            $validateUtf8,
            $messageSizeLimit,
            $frameSizeLimit,
        ));
    }

    public function push(string $data): void
    {
        $this->parser->push($data);
    }

    public function cancel(): void
    {
        $this->parser->cancel();
    }

    /**
     * A stateful generator websocket frame parser.
     *
     * @return \Generator<int, int, string, void>
     *
     * @psalm-suppress InvalidReturnType Psalm infers "never" as the return type.
     */
    private static function parse(
        WebsocketFrameHandler $frameHandler,
        bool $masked,
        ?WebsocketCompressionContext $compressionContext,
        bool $textOnly,
        bool $validateUtf8,
        int $messageSizeLimit,
        int $frameSizeLimit,
    ): \Generator {
        $doUtf8Validation = $validateUtf8;
        $compressedFlag = $compressionContext?->getRsv() ?? 0;

        $dataMsgBytesRecd = 0;
        $savedBuffer = '';
        $compressed = false;

        while (true) {
            $payload = ''; // Free memory from last frame payload.

            $buffer = yield 2;

            $firstByte = \ord($buffer[0]);
            $secondByte = \ord($buffer[1]);

            $final = (bool) ($firstByte & 0b10000000);
            $rsv = ($firstByte & 0b01110000) >> 4;
            $opcode = $firstByte & 0b00001111;
            $isMasked = (bool) ($secondByte & 0b10000000);
            $maskingKey = '';
            $frameLength = $secondByte & 0b01111111;

            if ($opcode >= 3 && $opcode <= 7) {
                throw new WebsocketParserException(WebsocketCloseCode::PROTOCOL_ERROR, 'Use of reserved non-control frame opcode');
            }

            if ($opcode >= 11 && $opcode <= 15) {
                throw new WebsocketParserException(WebsocketCloseCode::PROTOCOL_ERROR, 'Use of reserved control frame opcode');
            }

            $frameType = WebsocketFrameType::tryFrom($opcode);
            if (!$frameType) {
                throw new WebsocketParserException(WebsocketCloseCode::PROTOCOL_ERROR, 'Invalid opcode');
            }

            $isControlFrame = $frameType->isControlFrame();

            if ($isControlFrame || $frameType === WebsocketFrameType::Continuation) { // Control and continuation frames
                if ($rsv !== 0) {
                    throw new WebsocketParserException(
                        WebsocketCloseCode::PROTOCOL_ERROR,
                        'RSV must be 0 for control or continuation frames',
                    );
                }
            } else { // Text and binary frames
                if ($rsv !== 0 && (!$compressionContext || $rsv & ~$compressedFlag)) {
                    throw new WebsocketParserException(
                        WebsocketCloseCode::PROTOCOL_ERROR,
                        'Invalid RSV value for negotiated extensions',
                    );
                }

                $doUtf8Validation = $validateUtf8 && $frameType === WebsocketFrameType::Text;
                $compressed = (bool) ($rsv & $compressedFlag);
            }

            if ($frameLength === 0x7E) {
                [, $frameLength] = \unpack('n', yield 2);
            } elseif ($frameLength === 0x7F) {
                [, $highBytes, $lowBytes] = \unpack('N2', yield 8);

                if (\PHP_INT_MAX === 0x7fffffff) {
                    if ($highBytes !== 0 || $lowBytes < 0) {
                        throw new WebsocketParserException(
                            WebsocketCloseCode::MESSAGE_TOO_LARGE,
                            'Received payload exceeds maximum allowable size'
                        );
                    }
                    $frameLength = $lowBytes;
                } else {
                    $frameLength = ($highBytes << 32) | $lowBytes;
                    if ($frameLength < 0) {
                        throw new WebsocketParserException(
                            WebsocketCloseCode::PROTOCOL_ERROR,
                            'Most significant bit of 64-bit length field set'
                        );
                    }
                }
            }

            if ($frameLength > 0 && $isMasked === $masked) {
                throw new WebsocketParserException(
                    WebsocketCloseCode::PROTOCOL_ERROR,
                    'Payload mask error'
                );
            }

            if ($isControlFrame) {
                if (!$final) {
                    throw new WebsocketParserException(
                        WebsocketCloseCode::PROTOCOL_ERROR,
                        'Illegal control frame fragmentation'
                    );
                }

                if ($frameLength > 125) {
                    throw new WebsocketParserException(
                        WebsocketCloseCode::PROTOCOL_ERROR,
                        'Control frame payload must be of maximum 125 bytes or less'
                    );
                }
            }

            if ($frameSizeLimit && $frameLength > $frameSizeLimit) {
                throw new WebsocketParserException(
                    WebsocketCloseCode::MESSAGE_TOO_LARGE,
                    'Received payload exceeds maximum allowable size'
                );
            }

            if ($messageSizeLimit && ($frameLength + $dataMsgBytesRecd) > $messageSizeLimit) {
                throw new WebsocketParserException(
                    WebsocketCloseCode::MESSAGE_TOO_LARGE,
                    'Received payload exceeds maximum allowable size'
                );
            }

            if ($isMasked) {
                $maskingKey = yield 4;
            }

            if ($frameLength) {
                $payload = yield $frameLength;
            }

            if ($isMasked) {
                // This is memory hungry, but it's ~70x faster than iterating byte-by-byte
                // over the masked string. Deal with it; manual iteration is untenable.
                /** @psalm-suppress InvalidOperand String operands expected. */
                $payload ^= \str_repeat($maskingKey, ($frameLength + 3) >> 2);
            }

            if ($isControlFrame) {
                $frameHandler->handleFrame($frameType, $payload, true);
                continue;
            }

            if ($textOnly && $frameType === WebsocketFrameType::Binary) {
                throw new WebsocketParserException(
                    WebsocketCloseCode::UNACCEPTABLE_TYPE,
                    'BINARY opcodes (0x02) not accepted'
                );
            }

            $dataMsgBytesRecd += $frameLength;

            if ($savedBuffer !== '') {
                $payload = $savedBuffer . $payload;
                $savedBuffer = '';
            }

            if ($compressed) {
                /** @psalm-suppress PossiblyNullReference */
                $payload = $compressionContext->decompress($payload, $final);

                if ($payload === null) { // Decompression failed.
                    throw new WebsocketParserException(
                        WebsocketCloseCode::PROTOCOL_ERROR,
                        'Invalid compressed data'
                    );
                }
            }

            if ($doUtf8Validation) {
                if ($final) {
                    $valid = \preg_match('//u', $payload);
                } else {
                    for ($i = 0; !($valid = \preg_match('//u', $payload)); $i++) {
                        $savedBuffer = \substr($payload, -1) . $savedBuffer;
                        $payload = \substr($payload, 0, -1);

                        if ($i === 3) { // Remove a maximum of three bytes
                            break;
                        }
                    }
                }

                /** @psalm-suppress PossiblyUndefinedVariable Defined in either condition above. */
                if (!$valid) {
                    throw new WebsocketParserException(
                        WebsocketCloseCode::INCONSISTENT_FRAME_DATA_TYPE,
                        'Invalid TEXT data; UTF-8 required'
                    );
                }
            }

            if ($final) {
                $dataMsgBytesRecd = 0;
            }

            $frameHandler->handleFrame($frameType, $payload, $final);
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\Websocket\Parser;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Websocket\Compression\WebsocketCompressionContext;

final class Rfc6455FrameCompilerFactory implements WebsocketFrameCompilerFactory
{
    use ForbidCloning;
    use ForbidSerialization;

    public function createFrameCompiler(
        bool $masked,
        ?WebsocketCompressionContext $compressionContext = null,
    ): Rfc6455FrameCompiler {
        return new Rfc6455FrameCompiler($masked, $compressionContext);
    }
}
<?php declare(strict_types=1);

namespace Amp\Websocket\Parser;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Websocket\Compression\WebsocketCompressionContext;

final class Rfc6455ParserFactory implements WebsocketParserFactory
{
    use ForbidCloning;
    use ForbidSerialization;

    public function __construct(
        private readonly bool $textOnly = Rfc6455Parser::DEFAULT_TEXT_ONLY,
        private readonly bool $validateUtf8 = Rfc6455Parser::DEFAULT_VALIDATE_UTF8,
        private readonly int $messageSizeLimit = Rfc6455Parser::DEFAULT_MESSAGE_SIZE_LIMIT,
        private readonly int $frameSizeLimit = Rfc6455Parser::DEFAULT_FRAME_SIZE_LIMIT,
    ) {
    }

    public function createParser(
        WebsocketFrameHandler $frameHandler,
        bool $masked,
        ?WebsocketCompressionContext $compressionContext = null,
    ): Rfc6455Parser {
        return new Rfc6455Parser(
            $frameHandler,
            $masked,
            $compressionContext,
            $this->textOnly,
            $this->validateUtf8,
            $this->messageSizeLimit,
            $this->frameSizeLimit,
        );
    }
}
<?php declare(strict_types=1);

namespace Amp\Websocket\Parser;

interface WebsocketFrameHandler
{
    /**
     * Invoked each time a frame is received by the parser.
     */
    public function handleFrame(WebsocketFrameType $frameType, string $data, bool $isFinal): void;
}
<?php declare(strict_types=1);

namespace Amp\Websocket\Parser;

use Amp\Websocket\WebsocketException;

final class WebsocketParserException extends WebsocketException
{
    public function __construct(int $code, string $message)
    {
        parent::__construct($message, $code);
    }
}
<?php declare(strict_types=1);

namespace Amp\Websocket\Parser;

interface WebsocketFrameCompiler
{
    /**
     * Provides stateful compilation of websocket frames. Continuation frames must be proceeded by an initial text
     * or binary frame. Another text or binary frame cannot be sent until a final continuation frame is sent.
     * Control frames may be interleaved.
     */
    public function compileFrame(WebsocketFrameType $frameType, string $data, bool $isFinal): string;
}
<?php declare(strict_types=1);

namespace Amp\Websocket;

use Amp\ByteStream\ReadableStream;
use Amp\Cancellation;
use Amp\Closable;
use Amp\Socket\SocketAddress;
use Amp\Socket\TlsInfo;

/**
 * @extends \Traversable<int, WebsocketMessage>
 *
 * @psalm-type Timestamp = int<0, max>
 * @psalm-type Counter = int<0, max>
 */
interface WebsocketClient extends Closable, \Traversable
{
    /**
     * Receive a message from the remote Websocket endpoint.
     *
     * @param Cancellation|null $cancellation Cancel awaiting the next message. Note this does not close the
     * connection or discard the next message. A subsequent call to this method will still return the next message
     * received from the client.
     *
     * @return WebsocketMessage|null Returns the message sent by the remote or `null` if the connection closes.
     */
    public function receive(?Cancellation $cancellation = null): ?WebsocketMessage;

    /**
     * @return int Unique identifier for the client.
     */
    public function getId(): int;

    /**
     * @return SocketAddress Local socket address.
     */
    public function getLocalAddress(): SocketAddress;

    /**
     * @return SocketAddress Remote socket address.
     */
    public function getRemoteAddress(): SocketAddress;

    /**
     * @return TlsInfo|null TlsInfo object if connection is secure.
     */
    public function getTlsInfo(): ?TlsInfo;

    /**
     * @return WebsocketCloseInfo Throws an instance of Error if the client has not closed.
     */
    public function getCloseInfo(): WebsocketCloseInfo;

    /**
     * @return bool Determines if a compression context has been negotiated.
     */
    public function isCompressionEnabled(): bool;

    /**
     * Sends a text message to the endpoint. All data sent with this method must be valid UTF-8. Use `sendBinary()` if
     * you want to send binary data.
     *
     * @param string $data Payload to send.
     *
     * @throws WebsocketClosedException Thrown if sending to the client fails.
     */
    public function sendText(string $data): void;

    /**
     * Sends a binary message to the endpoint.
     *
     * @param string $data Payload to send.
     *
     * @throws WebsocketClosedException Thrown if sending to the client fails.
     */
    public function sendBinary(string $data): void;

    /**
     * Streams the given UTF-8 text stream to the endpoint. This method should be used only for large payloads such as
     * files. Use send() for smaller payloads.
     *
     * @throws WebsocketClosedException Thrown if sending to the client fails.
     */
    public function streamText(ReadableStream $stream): void;

    /**
     * Streams the given binary to the endpoint. This method should be used only for large payloads such as
     * files. Use sendBinary() for smaller payloads.
     *
     * @throws WebsocketClosedException Thrown if sending to the client fails.
     */
    public function streamBinary(ReadableStream $stream): void;

    /**
     * Sends a ping to the endpoint.
     */
    public function ping(): void;

    /**
     * Returns the corresponding count of events for the passed enum case.
     *
     * @return int<0, max>
     */
    public function getCount(WebsocketCount $type): int;

    /**
     * Returns the most recent unix timestamp (including fractions of a second) the given event enum case was observed,
     * or {@see \NAN} if the event has not occurred.
     *
     */
    public function getTimestamp(WebsocketTimestamp $type): float;

    /**
     * @return bool `false` if the client is still connected, `true` if the client has disconnected.
     *      Returns `true` as soon as the closing handshake is initiated by the server or client.
     */
    public function isClosed(): bool;

    /**
     * Closes the client connection.
     */
    public function close(int $code = WebsocketCloseCode::NORMAL_CLOSE, string $reason = ''): void;

    /**
     * Attaches a callback invoked when the client closes. The callback is passed this client as the only parameter.
     *
     * @param \Closure(int, WebsocketCloseInfo):void $onClose Function is passed the client ID and
     *      {@see WebsocketCloseInfo} object.
     */
    public function onClose(\Closure $onClose): void;
}
<?php declare(strict_types=1);

namespace Amp\Websocket;

enum WebsocketTimestamp
{
    case Connected;
    case Closed;
    case LastRead;
    case LastSend;
    case LastDataRead;
    case LastDataSend;
    case LastHeartbeat;
}
<?php declare(strict_types=1);

namespace Amp\Websocket;

final class WebsocketClosedException extends WebsocketException
{
    private readonly string $reason;

    public function __construct(string $message, int $code, string $reason, ?\Throwable $previous = null)
    {
        parent::__construct(\sprintf(
            '%s; Code %s (%s); Reason: "%s"',
            $message,
            $code,
            WebsocketCloseCode::getName($code) ?? 'Unknown code',
            $reason,
        ), $code, $previous);

        $this->reason = $reason;
    }

    public function getReason(): string
    {
        return $this->reason;
    }
}
<?php declare(strict_types=1);

namespace Amp\Websocket;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Revolt\EventLoop;
use function Amp\async;
use function Amp\weakClosure;

final class PeriodicHeartbeatQueue implements WebsocketHeartbeatQueue
{
    use ForbidCloning;
    use ForbidSerialization;

    /** @var array<int, \WeakReference<WebsocketClient>> */
    private array $clients = [];

    private readonly string $watcher;

    /** @var array<int, float> Least-recently-used cache of next ping (heartbeat) times. */
    private array $heartbeatTimeouts = [];

    /** @var float Cached current time to avoid syscall on each update. */
    private float $now;

    /**
     * @param positive-int $queuedPingLimit
     * @param positive-int $heartbeatPeriod
     */
    public function __construct(
        int $queuedPingLimit = 3,
        private readonly int $heartbeatPeriod = 10,
    ) {
        /** @psalm-suppress TypeDoesNotContainType */
        if ($queuedPingLimit <= 0) {
            throw new \ValueError('Queued ping limit must be greater than 0');
        }

        /** @psalm-suppress TypeDoesNotContainType */
        if ($this->heartbeatPeriod <= 0) {
            throw new \ValueError('Heartbeat period must be greater than 0');
        }

        $this->now = \microtime(true);

        $this->watcher = EventLoop::repeat(1, weakClosure(function () use ($queuedPingLimit): void {
            $this->now = \microtime(true);

            foreach ($this->heartbeatTimeouts as $clientId => $expiryTime) {
                if ($expiryTime >= $this->now) {
                    break;
                }

                /** @var WebsocketClient|null $client */
                $client = ($this->clients[$clientId] ?? null)?->get();
                if (!$client) {
                    unset($this->heartbeatTimeouts[$clientId]);
                    continue;
                }

                if ($client->getCount(WebsocketCount::UnansweredPings) > $queuedPingLimit) {
                    $this->remove($clientId);
                    async($client->close(...), WebsocketCloseCode::POLICY_VIOLATION, 'Exceeded unanswered PING limit')->ignore();
                    continue;
                }

                $this->update($clientId);

                async($client->ping(...))->ignore();
            }
        }));

        EventLoop::unreference($this->watcher);
    }

    public function __destruct()
    {
        EventLoop::cancel($this->watcher);
    }

    public function insert(WebsocketClient $client): void
    {
        $clientId = $client->getId();
        $this->clients[$clientId] = \WeakReference::create($client);
        $this->update($clientId);
    }

    public function update(int $clientId): void
    {
        \assert(isset($this->clients[$clientId]));
        unset($this->heartbeatTimeouts[$clientId]); // Unset to force ordering to end of list.
        $this->heartbeatTimeouts[$clientId] = $this->now + $this->heartbeatPeriod;
    }

    public function remove(int $clientId): void
    {
        unset($this->clients[$clientId], $this->heartbeatTimeouts[$clientId]);
    }
}
<?php declare(strict_types=1);

namespace Amp\Websocket\Compression;

interface WebsocketCompressionContextFactory
{
    /**
     * Create a compression context from a header received from a websocket client request.
     *
     * @param string $headerIn Header from request.
     * @param-out string|null $headerOut Sec-Websocket-Extension response header if an instance
     * of {@see WebsocketCompressionContext} is returned, otherwise {@code null}.
     */
    public function fromClientHeader(string $headerIn, ?string &$headerOut): ?WebsocketCompressionContext;

    /**
     * Create a compression context from a header received from a websocket server response.
     *
     * @param string $header Header from response.
     */
    public function fromServerHeader(string $header): ?WebsocketCompressionContext;

    /**
     * @return string Header value for Sec-Websocket-Extension header.
     */
    public function createRequestHeader(): string;
}
<?php declare(strict_types=1);

namespace Amp\Websocket\Compression;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;

final class Rfc7692Compression implements WebsocketCompressionContext
{
    use ForbidCloning;
    use ForbidSerialization;

    public const DEFAULT_WINDOW_SIZE = 15;

    private const RSV = 0b100;
    private const MINIMUM_LENGTH = 860;
    private const EMPTY_BLOCK = "\x0\x0\xff\xff";

    private static ?\Closure $initErrorHandler = null;

    private static ?\Closure $inflateErrorHandler = null;

    private static ?\Closure $deflateErrorHandler = null;

    /**
     * Create a compression context from a header received from a websocket client request.
     *
     * @param string $headerIn Header from request.
     * @param-out string|null $headerOut Sec-Websocket-Extension response header.
     */
    public static function fromClientHeader(string $headerIn, ?string &$headerOut): ?self
    {
        return self::fromHeader(true, $headerIn, $headerOut);
    }

    /**
     * Create a compression context from a header received from a websocket server response.
     *
     * @param string $header Header from response.
     */
    public static function fromServerHeader(string $header): ?self
    {
        return self::fromHeader(false, $header);
    }

    /**
     * @return string Header value for Sec-Websocket-Extension header.
     */
    public static function createRequestHeader(): string
    {
        return 'permessage-deflate; server_no_context_takeover; client_no_context_takeover';
    }

    /**
     * Note that 8 is no longer a valid window size, {@see https://github.com/madler/zlib/issues/171}.
     *
     * @param bool   $isServer True if creating a server context, false if creating a client context.
     * @param string $headerIn Header from request.
     * @param-out string|null $headerOut Sec-Websocket-Extension response header.
     */
    private static function fromHeader(bool $isServer, string $headerIn, ?string &$headerOut = null): ?self
    {
        $headerIn = \explode(';', \strtolower($headerIn));
        $headerIn = \array_map('trim', $headerIn);

        if (\array_shift($headerIn) !== 'permessage-deflate') {
            return null;
        }

        $serverWindowSize = self::DEFAULT_WINDOW_SIZE;
        $clientWindowSize = self::DEFAULT_WINDOW_SIZE;
        $serverContextTakeover = true;
        $clientContextTakeover = true;

        $headers = [];
        $headerOut = 'permessage-deflate';

        foreach ($headerIn as $param) {
            $parts = \explode('=', $param, 2);

            if (\in_array($parts[0], $headers, true)) {
                return null; // Repeat params in header.
            }

            $headers[] = $parts[0];

            switch ($parts[0]) {
                case 'client_max_window_bits':
                    if (isset($parts[1])) {
                        $value = (int) $parts[1];

                        if ($value < 9 || $value > 15) {
                            return null; // Invalid option value.
                        }

                        $clientWindowSize = $value;
                    } elseif (!$isServer) {
                        return null;
                    }

                    $headerOut .= '; client_max_window_bits=' . $clientWindowSize;
                    break;

                case 'client_no_context_takeover':
                    $clientContextTakeover = false;
                    $headerOut .= '; client_no_context_takeover';
                    break;

                case 'server_max_window_bits':
                    if (!isset($parts[1])) {
                        return null;
                    }

                    $value = (int) $parts[1];

                    if ($value < 9 || $value > 15) {
                        return null; // Invalid option value.
                    }

                    $serverWindowSize = $value;

                    $headerOut .= '; server_max_window_bits=' . $serverWindowSize;
                    break;

                case 'server_no_context_takeover':
                    $serverContextTakeover = false;
                    $headerOut .= '; server_no_context_takeover';
                    break;

                default:
                    return null; // Unrecognized option; do not accept extension request.
            }
        }

        if ($isServer) {
            return new self($clientWindowSize, $serverWindowSize, $clientContextTakeover, $serverContextTakeover);
        }

        return new self($serverWindowSize, $clientWindowSize, $serverContextTakeover, $clientContextTakeover);
    }

    private readonly \DeflateContext $deflate;

    private readonly \InflateContext $inflate;

    private readonly int $sendingFlushMode;

    private readonly int $receivingFlushMode;

    private function __construct(
        int $receivingWindowSize,
        int $sendingWindowSize,
        bool $receivingContextTakeover,
        bool $sendingContextTakeover
    ) {
        $this->receivingFlushMode = $receivingContextTakeover ? \ZLIB_SYNC_FLUSH : \ZLIB_FULL_FLUSH;
        $this->sendingFlushMode = $sendingContextTakeover ? \ZLIB_SYNC_FLUSH : \ZLIB_FULL_FLUSH;

        \set_error_handler(self::$initErrorHandler ??= static function (int $code, string $message): never {
            throw new \RuntimeException('Failed to initialized compression context: ' . $message);
        });

        try {
            /** @psalm-suppress InvalidPropertyAssignmentValue Psalm stubs are outdated */
            $this->inflate = \inflate_init(\ZLIB_ENCODING_RAW, ['window' => $receivingWindowSize]);
            /** @psalm-suppress InvalidPropertyAssignmentValue Psalm stubs are outdated */
            $this->deflate = \deflate_init(\ZLIB_ENCODING_RAW, ['window' => $sendingWindowSize]);
        } finally {
            \restore_error_handler();
        }
    }

    public function getRsv(): int
    {
        return self::RSV;
    }

    public function getCompressionThreshold(): int
    {
        return self::MINIMUM_LENGTH;
    }

    public function decompress(string $data, bool $isFinal): ?string
    {
        if ($isFinal) {
            $data .= self::EMPTY_BLOCK;
        }

        \set_error_handler(self::$inflateErrorHandler ??= static fn () => true);

        try {
            /** @psalm-suppress InvalidArgument Psalm stubs are outdated */
            $data = \inflate_add($this->inflate, $data, $this->receivingFlushMode);
        } finally {
            \restore_error_handler();
        }

        if ($data === false) {
            return null;
        }

        return $data;
    }

    public function compress(string $data, bool $isFinal): string
    {
        \set_error_handler(self::$deflateErrorHandler ??= static function (int $code, string $message): never {
            throw new \RuntimeException('Error when compressing data: ' . $message, $code);
        });

        try {
            /** @psalm-suppress InvalidArgument Psalm stubs are outdated */
            $data = \deflate_add($this->deflate, $data, $this->sendingFlushMode);
        } finally {
            \restore_error_handler();
        }

        if ($isFinal && \substr($data, -4) === self::EMPTY_BLOCK) {
            $data = \substr($data, 0, -4);
        }

        return $data;
    }
}
<?php declare(strict_types=1);

namespace Amp\Websocket\Compression;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;

final class Rfc7692CompressionFactory implements WebsocketCompressionContextFactory
{
    use ForbidCloning;
    use ForbidSerialization;

    public function fromClientHeader(string $headerIn, ?string &$headerOut): ?WebsocketCompressionContext
    {
        return Rfc7692Compression::fromClientHeader($headerIn, $headerOut);
    }

    public function fromServerHeader(string $header): ?WebsocketCompressionContext
    {
        return Rfc7692Compression::fromServerHeader($header);
    }

    public function createRequestHeader(): string
    {
        return Rfc7692Compression::createRequestHeader();
    }
}
<?php declare(strict_types=1);

namespace Amp\Websocket\Compression;

interface WebsocketCompressionContext
{
    /**
     * @return int The RSV value for this compression extension.
     */
    public function getRsv(): int;

    /**
     * @return int Minimum number of bytes a message must be before compressing.
     */
    public function getCompressionThreshold(): int;

    /**
     * Compress the given payload data.
     */
    public function compress(string $data, bool $isFinal): string;

    /**
     * Decompress the given payload data. Null should be returned if decompression fails.
     */
    public function decompress(string $data, bool $isFinal): ?string;
}
<?php declare(strict_types=1);

namespace Amp\Websocket;

use Amp\ByteStream\StreamException;

class WebsocketException extends StreamException
{
}
<?php declare(strict_types=1);

namespace Amp\Websocket\Internal;

use Amp\Websocket\WebsocketCloseInfo;

/** @internal */
final class WebsocketClientMetadata
{
    /** @var int<0, max> Next sequential client ID. */
    private static int $nextId = 0;

    /** @var int<0, max> */
    public readonly int $id;

    public ?WebsocketCloseInfo $closeInfo = null;

    public readonly float $connectedAt;

    public float $lastReadAt = \NAN;

    public float $lastSentAt = \NAN;

    public float $lastDataReadAt = \NAN;

    public float $lastDataSentAt = \NAN;

    public float $lastHeartbeatAt = \NAN;

    /** @var int<0, max> */
    public int $bytesReceived = 0;

    /** @var int<0, max> */
    public int $bytesSent = 0;

    /** @var int<0, max> */
    public int $framesReceived = 0;

    /** @var int<0, max> */
    public int $framesSent = 0;

    /** @var int<0, max> */
    public int $messagesReceived = 0;

    /** @var int<0, max> */
    public int $messagesSent = 0;

    /** @var int<0, max> */
    public int $pingsReceived = 0;

    /** @var int<0, max> */
    public int $pingsSent = 0;

    /** @var int<0, max> */
    public int $pongsReceived = 0;

    /** @var int<0, max> */
    public int $pongsSent = 0;

    public function __construct(
        public readonly bool $compressionEnabled,
    ) {
        $this->id = self::$nextId++;

        $this->connectedAt = \microtime(true);
    }

    public function isClosed(): bool
    {
        return $this->closeInfo !== null;
    }
}
<?php declare(strict_types=1);

namespace Amp\Websocket\Internal;

use Amp\ByteStream\ReadableIterableStream;
use Amp\ByteStream\ReadableStream;
use Amp\DeferredFuture;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Future;
use Amp\Pipeline\ConcurrentIterator;
use Amp\Pipeline\DisposedException;
use Amp\Pipeline\Queue;
use Amp\Socket\Socket;
use Amp\TimeoutCancellation;
use Amp\Websocket\Parser\WebsocketFrameCompiler;
use Amp\Websocket\Parser\WebsocketFrameHandler;
use Amp\Websocket\Parser\WebsocketFrameType;
use Amp\Websocket\Parser\WebsocketParser;
use Amp\Websocket\Parser\WebsocketParserException;
use Amp\Websocket\WebsocketCloseCode;
use Amp\Websocket\WebsocketClosedException;
use Amp\Websocket\WebsocketCloseInfo;
use Amp\Websocket\WebsocketHeartbeatQueue;
use Amp\Websocket\WebsocketMessage;
use Amp\Websocket\WebsocketRateLimit;
use function Amp\async;

/** @internal */
final class Rfc6455FrameHandler implements WebsocketFrameHandler
{
    use ForbidCloning;
    use ForbidSerialization;

    private ?DeferredFuture $closeDeferred;

    /** @var Queue<WebsocketMessage> */
    private readonly Queue $messageQueue;

    /** @var Queue<string>|null */
    private ?Queue $currentMessageQueue = null;

    public function __construct(
        private readonly Socket $socket,
        private readonly WebsocketFrameCompiler $frameCompiler,
        private readonly ?WebsocketHeartbeatQueue $heartbeatQueue,
        private readonly ?WebsocketRateLimit $rateLimit,
        private readonly WebsocketClientMetadata $metadata,
        private readonly float $closePeriod,
    ) {
        $this->closeDeferred = new DeferredFuture();
        $this->messageQueue = new Queue();
    }

    /**
     * @return ConcurrentIterator<WebsocketMessage>
     */
    public function iterate(): ConcurrentIterator
    {
        return $this->messageQueue->iterate();
    }

    public function read(WebsocketParser $parser): void
    {
        try {
            while (($chunk = $this->socket->read()) !== null) {
                if ($chunk === '') {
                    continue;
                }

                $this->metadata->lastReadAt = \microtime(true);
                $this->metadata->bytesReceived += \strlen($chunk);

                $this->heartbeatQueue?->update($this->metadata->id);
                $this->rateLimit?->notifyBytesReceived($this->metadata->id, \strlen($chunk));

                $parser->push($chunk);

                $chunk = ''; // Free memory from last chunk read.
            }
        } catch (WebsocketParserException $exception) {
            $message = $exception->getMessage();
            $code = $exception->getCode();
        } catch (\Throwable $exception) {
            $message = 'TCP connection closed with exception: ' . $exception->getMessage();
        } finally {
            $parser->cancel();
            $this->heartbeatQueue?->remove($this->metadata->id);
        }

        $this->closeDeferred?->complete();
        $this->closeDeferred = null;

        if (!$this->metadata->isClosed()) {
            $this->close(
                $code ?? WebsocketCloseCode::ABNORMAL_CLOSE,
                $message ?? 'TCP connection closed unexpectedly',
                byPeer: true,
            );
        }
    }

    public function handleFrame(WebsocketFrameType $frameType, string $data, bool $isFinal): void
    {
        ++$this->metadata->framesReceived;
        $this->rateLimit?->notifyFramesReceived($this->metadata->id, 1);

        if ($frameType->isControlFrame()) {
            $this->onControlFrame($frameType, $data);
        } else {
            $this->onData($frameType, $data, $isFinal);
        }
    }

    private function onData(WebsocketFrameType $frameType, string $data, bool $terminated): void
    {
        \assert(!$frameType->isControlFrame());

        $this->metadata->lastDataReadAt = \microtime(true);

        // Ignore further data received after initiating close.
        if ($this->metadata->isClosed()) {
            return;
        }

        if (!$this->currentMessageQueue) {
            if ($frameType === WebsocketFrameType::Continuation) {
                $this->close(
                    WebsocketCloseCode::PROTOCOL_ERROR,
                    'Illegal CONTINUATION opcode; initial message payload frame must be TEXT or BINARY',
                );
                return;
            }

            ++$this->metadata->messagesReceived;

            if (!$terminated) {
                $this->currentMessageQueue = new Queue();
            }

            // Avoid holding a reference to the ReadableStream or Message object here so destructors will be invoked
            // if the message is not consumed by the user.
            $this->messageQueue->push(self::createMessage(
                $frameType,
                $this->currentMessageQueue
                    ? new ReadableIterableStream($this->currentMessageQueue->iterate())
                    : $data,
            ));

            if (!$this->currentMessageQueue) {
                return;
            }
        } elseif ($frameType !== WebsocketFrameType::Continuation) {
            $this->close(
                WebsocketCloseCode::PROTOCOL_ERROR,
                'Illegal data type opcode after unfinished previous data type frame; opcode MUST be CONTINUATION',
            );
            return;
        }

        try {
            $this->currentMessageQueue->push($data);
        } catch (DisposedException) {
            // Message disposed, ignore exception.
        }

        if ($terminated) {
            $this->currentMessageQueue->complete();
            $this->currentMessageQueue = null;
        }
    }

    private function onControlFrame(WebsocketFrameType $frameType, string $data): void
    {
        \assert($frameType->isControlFrame());

        // Close already completed, so ignore any further data from the parser.
        if ($this->metadata->isClosed() && $this->closeDeferred === null) {
            return;
        }

        switch ($frameType) {
            case WebsocketFrameType::Close:
                $this->closeDeferred?->complete();
                $this->closeDeferred = null;

                if ($this->metadata->isClosed()) {
                    break;
                }

                $length = \strlen($data);
                if ($length === 0) {
                    $code = WebsocketCloseCode::NONE;
                    $reason = '';
                } elseif ($length < 2) {
                    $code = WebsocketCloseCode::PROTOCOL_ERROR;
                    $reason = 'Close code must be two bytes';
                } else {
                    $code = \unpack('n', $data)[1];
                    $reason = \substr($data, 2);

                    if ($code < 1000 // Reserved and unused.
                        || ($code >= 1004 && $code <= 1006) // Should not be sent over wire
                        || ($code >= 1014 && $code <= 1015) // Should not be sent over wire
                        || ($code >= 1016 && $code <= 1999) // Disallowed, reserved for future use
                        || ($code >= 2000 && $code <= 2999) // Disallowed, reserved for Websocket extensions
                        // 3000-3999 allowed, reserved for libraries
                        // 4000-4999 allowed, reserved for applications
                        || $code >= 5000 // >= 5000 invalid
                    ) {
                        $code = WebsocketCloseCode::PROTOCOL_ERROR;
                        $reason = 'Invalid close code';
                    } elseif (!\preg_match('//u', $reason)) {
                        $code = WebsocketCloseCode::INCONSISTENT_FRAME_DATA_TYPE;
                        $reason = 'Close reason must be valid UTF-8';
                    }
                }

                $this->close($code, $reason, byPeer: true);
                break;

            case WebsocketFrameType::Ping:
                ++$this->metadata->pingsReceived;
                $this->write(WebsocketFrameType::Pong, $data);
                ++$this->metadata->pongsSent;
                break;

            case WebsocketFrameType::Pong:
                if (!\preg_match('/^[1-9][0-9]*$/', $data)) {
                    // Ignore pong payload that is not an integer.
                    break;
                }

                // We need a min() here, else someone might just send a pong frame with a very high pong count and
                // leave TCP connection in open state... Then we'd accumulate connections which never are cleaned up...
                $this->metadata->pongsReceived = \min($this->metadata->pingsSent, \max(0, (int) $data));
                $this->metadata->lastHeartbeatAt = \microtime(true);
                break;

            default:
                // This should be unreachable
                throw new \Error('Non-control frame opcode: ' . $frameType->name);
        }
    }

    public function write(WebsocketFrameType $frameType, string $data, bool $isFinal = true): void
    {
        $frame = $this->frameCompiler->compileFrame($frameType, $data, $isFinal);

        ++$this->metadata->framesSent;
        $this->metadata->bytesSent += \strlen($frame);
        $this->metadata->lastSentAt = \microtime(true);

        $this->socket->write($frame);
    }

    public function close(int $code, string $reason = '', bool $byPeer = false): void
    {
        if ($this->metadata->isClosed()) {
            return;
        }

        \assert($code !== WebsocketCloseCode::NONE || $reason === '');

        $this->metadata->closeInfo = new WebsocketCloseInfo(
            $code,
            $reason,
            \microtime(true),
            $byPeer,
        );

        $this->messageQueue->complete();

        $this->currentMessageQueue?->error(new WebsocketClosedException(
            'Connection closed while streaming message body',
            $code,
            $reason,
        ));
        $this->currentMessageQueue = null;

        if ($this->socket->isClosed()) {
            return;
        }

        try {
            $cancellation = new TimeoutCancellation($this->closePeriod);

            async(
                $this->write(...),
                WebsocketFrameType::Close,
                $code !== WebsocketCloseCode::NONE ? \pack('n', $code) . $reason : '',
            )->await($cancellation);

            // Wait for peer close frame for configured number of seconds.
            $this->closeDeferred?->getFuture()->await($cancellation);
        } catch (\Throwable) {
            // Failed to write close frame or to receive response frame, but we were disconnecting anyway.
        }

        $this->socket->close();
    }

    /**
     * @param \Closure(int, WebsocketCloseInfo):void $onClose
     */
    public function onClose(\Closure $onClose): void
    {
        $future = $this->closeDeferred?->getFuture() ?? Future::complete();

        $metadata = $this->metadata;
        $future->finally(static function () use ($onClose, $metadata): void {
            \assert($metadata->closeInfo !== null, 'Client was not closed when onClose invoked');
            $onClose($metadata->id, $metadata->closeInfo);
        });
    }

    private static function createMessage(WebsocketFrameType $frameType, ReadableStream|string $stream): WebsocketMessage
    {
        if ($frameType === WebsocketFrameType::Binary) {
            return WebsocketMessage::fromBinary($stream);
        }

        return WebsocketMessage::fromText($stream);
    }
}
<?php declare(strict_types=1);

namespace Amp\Websocket;

// defined in https://tools.ietf.org/html/rfc6455#section-4
const GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';

/**
 * @param positive-int $length Random bytes to use to generate the key.
 *
 * @throws \Exception If generating a random key fails.
 */
function generateKey(int $length = 16): string
{
    return \base64_encode(\random_bytes($length));
}

/**
 * Generates the value for the Sec-Websocket-Accept header based on the given Sec-Websocket-Key header value.
 */
function generateAcceptFromKey(string $key): string
{
    return \base64_encode(\sha1($key . GUID, true));
}

/**
 * Determines if the Sec-Websocket-Accept value given matches the expected value for the Sec-Websocket-Key header.
 */
function validateAcceptForKey(string $accept, string $key): bool
{
    return $accept === generateAcceptFromKey($key);
}
<?php declare(strict_types=1);

namespace Amp\Websocket;

interface WebsocketRateLimit
{
    /**
     * Notify the rate limiter of bytes received by the client. The rate limiter may suspend if it wishes to
     * prevent processing data and receiving further data.
     *
     * @param positive-int $byteCount
     */
    public function notifyBytesReceived(int $clientId, int $byteCount): void;

    /**
     * Notify the rate limiter of frames received by the client. The rate limiter may suspend if it wishes to
     * prevent processing data and receiving further data.
     *
     * @param positive-int $frameCount
     */
    public function notifyFramesReceived(int $clientId, int $frameCount): void;
}
<?php declare(strict_types=1);

namespace Amp\Websocket;

interface WebsocketHeartbeatQueue
{
    /**
     * Insert the given client into the heartbeat queue. If a reference is retained to the {@see WebsocketClient},
     * it is recommended to use {@see \WeakReference}.
     */
    public function insert(WebsocketClient $client): void;

    /**
     * Update the heartbeat interval for the given client.
     */
    public function update(int $clientId): void;

    /**
     * Remove the given client from the heartbeat queue.
     */
    public function remove(int $clientId): void;
}
{
    "name": "amphp/websocket",
    "homepage": "https://github.com/amphp/websocket",
    "description": "Shared code for websocket servers and clients.",
    "support": {
        "issues": "https://github.com/amphp/websocket/issues"
    },
    "keywords": [
        "async",
        "non-blocking",
        "websocket",
        "http",
        "amp",
        "amphp"
    ],
    "license": "MIT",
    "authors": [
        {
            "name": "Aaron Piotrowski",
            "email": "aaron@trowski.com"
        },
        {
            "name": "Niklas Keller",
            "email": "me@kelunik.com"
        },
        {
            "name": "Bob Weinand",
            "email": "bobwei9@hotmail.com"
        }
    ],
    "require": {
        "php": ">=8.1",
        "amphp/amp": "^3",
        "amphp/byte-stream": "^2",
        "amphp/parser": "^1",
        "amphp/pipeline": "^1",
        "amphp/socket": "^2",
        "revolt/event-loop": "^1"
    },
    "require-dev": {
        "phpunit/phpunit": "^9",
        "amphp/phpunit-util": "^3",
        "amphp/php-cs-fixer-config": "^2",
        "psalm/phar": "^5.18"
    },
    "suggest": {
        "ext-zlib": "Required for compression"
    },
    "autoload": {
        "psr-4": {
            "Amp\\Websocket\\": "src"
        },
        "files": [
            "src/functions.php"
        ]
    },
    "autoload-dev": {
        "psr-4": {
            "Amp\\Websocket\\Test\\": "test"
        },
        "files": [
            "test/functions.php"
        ]
    }
}
<?php

$config = new Amp\CodeStyle\Config;

$config->getFinder()
    ->in(__DIR__ . '/examples')
    ->in(__DIR__ . '/src')
    ->in(__DIR__ . '/test');

$config->setCacheFile(__DIR__ . '/.php_cs.cache');

return $config;
{
    "symbol-whitelist": [
        "null",
        "true",
        "false",
        "static",
        "self",
        "parent",
        "array",
        "string",
        "int",
        "float",
        "bool",
        "iterable",
        "callable",
        "mixed",
        "void",
        "never",
        "object",
        "random_bytes",
        "str_increment",
        "readKey",
        "flattenArgument",
        "formatFlattenedBacktrace",
        "contextFactory",
        "workerFactory",
        "workerPool",
        "runContext",
        "parallel\\Events",
        "parallel\\Future",
        "parallel\\Runtime",
        "parallel\\Runtime\\Error\\Closed"
    ],
    "php-core-extensions": [
        "Core",
        "date",
        "pcre",
        "Phar",
        "Reflection",
        "SPL",
        "standard",
        "hash",
        "pcntl",
        "posix"
    ]
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Ipc;

use Amp\Cancellation;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Socket;
use Amp\Socket\ResourceSocket;
use Revolt\EventLoop;
use const Amp\Process\IS_WINDOWS;

final class LocalIpcHub implements IpcHub
{
    use ForbidCloning;
    use ForbidSerialization;

    private readonly SocketIpcHub $delegate;

    private ?string $toUnlink = null;

    /**
     * @param float $keyReceiveTimeout Timeout to receive the key on accepted connections.
     * @param positive-int $keyLength Length of the random key exchanged on the IPC channel when connecting.
     *
     * @throws Socket\SocketException
     */
    public function __construct(
        float $keyReceiveTimeout = SocketIpcHub::DEFAULT_KEY_RECEIVE_TIMEOUT,
        int $keyLength = SocketIpcHub::DEFAULT_KEY_LENGTH,
    ) {
        if (IS_WINDOWS) {
            $address = new Socket\InternetAddress('127.0.0.1', 0);
        } else {
            $suffix = \bin2hex(\random_bytes(10));
            $path = \sys_get_temp_dir() . "/amp-parallel-ipc-" . $suffix . ".sock";
            $address = new Socket\UnixAddress($path);
            $this->toUnlink = $path;
        }

        $this->delegate = new SocketIpcHub(Socket\listen($address), $keyReceiveTimeout, $keyLength);
    }

    public function __destruct()
    {
        EventLoop::queue($this->delegate->close(...));
        $this->unlink();
    }

    #[\Override]
    public function accept(string $key, ?Cancellation $cancellation = null): ResourceSocket
    {
        return $this->delegate->accept($key, $cancellation);
    }

    #[\Override]
    public function isClosed(): bool
    {
        return $this->delegate->isClosed();
    }

    #[\Override]
    public function close(): void
    {
        $this->delegate->close();
        $this->unlink();
    }

    #[\Override]
    public function onClose(\Closure $onClose): void
    {
        $this->delegate->onClose($onClose);
    }

    private function unlink(): void
    {
        if ($this->toUnlink === null) {
            return;
        }

        // Ignore errors when unlinking temp socket.
        \set_error_handler(static fn () => true);
        try {
            \unlink($this->toUnlink);
        } finally {
            \restore_error_handler();
            $this->toUnlink = null;
        }
    }

    #[\Override]
    public function getUri(): string
    {
        return $this->delegate->getUri();
    }

    #[\Override]
    public function generateKey(): string
    {
        return $this->delegate->generateKey();
    }
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Ipc;

use Amp\Cancellation;
use Amp\Closable;
use Amp\Socket\Socket;

interface IpcHub extends Closable
{
    /**
     * @param non-empty-string $key A key generated by {@see generateKey()}.
     */
    public function accept(string $key, ?Cancellation $cancellation = null): Socket;

    /**
     * @return non-empty-string URI to use with {@see connect()}.
     */
    public function getUri(): string;

    /**
     * @return non-empty-string Pass the key returned from this method to the connecting context and {@see accept()}.
     */
    public function generateKey(): string;
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Ipc;

use Amp\Cache\LocalCache;
use Amp\Cancellation;
use Amp\DeferredFuture;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\NullCancellation;
use Amp\Socket;
use Amp\Socket\ResourceSocket;
use Amp\Socket\SocketAddressType;
use Amp\TimeoutCancellation;
use Revolt\EventLoop;

final class SocketIpcHub implements IpcHub
{
    use ForbidCloning;
    use ForbidSerialization;

    public const DEFAULT_KEY_RECEIVE_TIMEOUT = 5;
    public const DEFAULT_KEY_LENGTH = 64;

    /** @var non-empty-string */
    private readonly string $uri;

    /** @var array<string, DeferredFuture> */
    private array $waitingByKey = [];

    /** @var \Closure(): void */
    private readonly \Closure $accept;

    private bool $queued = false;

    /** @var LocalCache<ResourceSocket> */
    private LocalCache $clientsByKey;

    /**
     * @param float $keyReceiveTimeout Timeout to receive the key on accepted connections.
     * @param positive-int $keyLength Length of the random key exchanged on the IPC channel when connecting.
     */
    public function __construct(
        private readonly Socket\ServerSocket $server,
        float $keyReceiveTimeout = self::DEFAULT_KEY_RECEIVE_TIMEOUT,
        private readonly int $keyLength = self::DEFAULT_KEY_LENGTH,
    ) {
        $address = $this->server->getAddress();
        $this->uri = match ($address->getType()) {
            SocketAddressType::Unix => 'unix://' . $address->toString(),
            SocketAddressType::Internet => 'tcp://' . $address->toString(),
        };

        $this->clientsByKey = new LocalCache(1024, $keyReceiveTimeout);

        $queued = &$this->queued;
        $waitingByKey = &$this->waitingByKey;
        $clientsByKey = &$this->clientsByKey;
        $this->accept = static function () use (
            &$queued,
            &$waitingByKey,
            &$clientsByKey,
            $server,
            $keyReceiveTimeout,
            $keyLength,
        ): void {
            while ($waitingByKey) {
                $client = $server->accept();
                if (!$client) {
                    $queued = false;
                    $exception = new Socket\SocketException('IPC socket closed before the client connected');
                    foreach ($waitingByKey as $deferred) {
                        $deferred->error($exception);
                    }
                    return;
                }

                try {
                    $received = readKey($client, new TimeoutCancellation($keyReceiveTimeout), $keyLength);
                } catch (\Throwable) {
                    $client->close();
                    continue; // Ignore possible foreign connection attempt.
                }

                if (isset($waitingByKey[$received])) {
                    $waitingByKey[$received]->complete($client);
                    unset($waitingByKey[$received]);
                } else {
                    $clientsByKey->set($received, $client);
                }
            }

            $queued = false;
        };
    }

    public function __destruct()
    {
        $this->close();
    }

    #[\Override]
    public function isClosed(): bool
    {
        return $this->server->isClosed();
    }

    #[\Override]
    public function close(): void
    {
        $this->server->close();

        if (!$this->waitingByKey) {
            return;
        }

        $exception = new Socket\SocketException('IPC socket closed before the client connected');
        foreach ($this->waitingByKey as $deferred) {
            $deferred->error($exception);
        }
    }

    #[\Override]
    public function onClose(\Closure $onClose): void
    {
        $this->server->onClose($onClose);
    }

    #[\Override]
    public function getUri(): string
    {
        return $this->uri;
    }

    #[\Override]
    public function generateKey(): string
    {
        return \random_bytes($this->keyLength);
    }

    /**
     * @param string $key A key generated by {@see generateKey()}.
     */
    #[\Override]
    public function accept(string $key, ?Cancellation $cancellation = null): ResourceSocket
    {
        if ($this->server->isClosed()) {
            throw new Socket\SocketException('The IPC server has been closed');
        }

        if (\strlen($key) !== $this->keyLength) {
            throw new \ValueError(\sprintf(
                "Key provided is of length %d, expected %d",
                \strlen($key),
                $this->keyLength,
            ));
        }

        if (isset($this->waitingByKey[$key])) {
            throw new \Error("An accept is already pending for the given key");
        }

        $client = $this->clientsByKey->get($key);
        if ($client !== null) {
            $this->clientsByKey->delete($key);

            return $client;
        }

        if (!$this->queued) {
            EventLoop::queue($this->accept);
            $this->queued = true;
        }

        $cancellation ??= new NullCancellation();

        $this->waitingByKey[$key] = $deferred = new DeferredFuture();

        $waitingByKey = &$this->waitingByKey;
        $cancellationId = $cancellation->subscribe(static function () use (&$waitingByKey, $key): void {
            unset($waitingByKey[$key]);
        });

        try {
            $client = $deferred->getFuture()->await($cancellation);
        } finally {
            $cancellation->unsubscribe($cancellationId);
        }

        return $client;
    }
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Ipc;

use Amp\ByteStream\ReadableResourceStream;
use Amp\Cancellation;
use Amp\Socket\Socket;
use Amp\Socket\SocketConnector;
use function Amp\Socket\socketConnector;

/**
 * @param positive-int $keyLength
 */
function readKey(
    ReadableResourceStream|Socket $stream,
    ?Cancellation $cancellation = null,
    int $keyLength = SocketIpcHub::DEFAULT_KEY_LENGTH,
): string {
    $key = "";

    // Read random key from $stream and send back to parent over IPC socket to authenticate.
    do {
        /** @psalm-suppress InvalidArgument */
        if (($chunk = $stream->read($cancellation, $keyLength - \strlen($key))) === null) {
            throw new \RuntimeException("Could not read key from parent", E_USER_ERROR);
        }
        $key .= $chunk;
    } while (\strlen($key) < $keyLength);

    return $key;
}

/**
 * Note that this is designed to be used in the child process/thread to connect to an IPC socket.
 */
function connect(
    string $uri,
    string $key,
    ?Cancellation $cancellation = null,
    ?SocketConnector $connector = null,
): Socket {
    $connector ??= socketConnector();

    $client = $connector->connect($uri, cancellation: $cancellation);
    $client->write($key);

    return $client;
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Context;

use Amp\ByteStream\StreamChannel;
use Amp\Cancellation;
use Amp\Parallel\Context\Internal\AbstractContext;
use Amp\Parallel\Context\Internal\ParallelHub;
use Amp\Parallel\Ipc\IpcHub;
use Amp\TimeoutCancellation;
use parallel\Future as ParallelFuture;
use parallel\Runtime;
use parallel\Runtime\Error\Closed;
use Revolt\EventLoop;

/**
 * @template-covariant TResult
 * @template-covariant TReceive
 * @template TSend
 * @extends AbstractContext<TResult, TReceive, TSend>
 */
final class ThreadContext extends AbstractContext
{
    private const EXIT_CHECK_FREQUENCY = 0.25;
    private const DEFAULT_START_TIMEOUT = 5;

    private static ?\WeakMap $hubs = null;

    /** @var int Next thread ID. */
    private static int $nextId = 1;

    private static ?string $autoloadPath = null;

    /**
     * Checks if threading is enabled.
     *
     * @return bool True if threading is enabled, otherwise false.
     */
    public static function isSupported(): bool
    {
        return \extension_loaded('parallel');
    }

    /**
     * @param string|non-empty-list<string> $script Path to PHP script or array with first element as path and
     *     following elements options to the PHP script (e.g.: ['bin/worker.php', 'Option1Value', 'Option2Value']).
     * @param positive-int $childConnectTimeout Number of seconds the thread will attempt to connect to the parent
     *      before failing.
     *
     * @throws ContextException If starting the process fails.
     */
    public static function start(
        IpcHub $ipcHub,
        string|array $script,
        ?Cancellation $cancellation = null,
        int $childConnectTimeout = self::DEFAULT_START_TIMEOUT
    ): self {
        /** @psalm-suppress RedundantFunctionCall */
        $script = \is_array($script) ? \array_values($script) : [$script];
        if (!$script) {
            throw new \ValueError('Empty script array provided to process context');
        }

        self::$hubs ??= new \WeakMap();
        $hub = (self::$hubs[EventLoop::getDriver()] ??= new Internal\ParallelHub());

        $key = $ipcHub->generateKey();

        if (self::$autoloadPath === null) {
            $paths = [
                \dirname(__DIR__, 2) . \DIRECTORY_SEPARATOR . "vendor" . \DIRECTORY_SEPARATOR . "autoload.php",
                \dirname(__DIR__, 4) . \DIRECTORY_SEPARATOR . "autoload.php",
            ];

            foreach ($paths as $path) {
                if (\file_exists($path)) {
                    self::$autoloadPath = $path;
                    break;
                }
            }

            if (self::$autoloadPath === null) {
                throw new \Error("Could not locate autoload.php");
            }
        }

        $id = self::$nextId++;

        $runtime = new Runtime(self::$autoloadPath);
        $future = $runtime->run(function (
            int $id,
            string $uri,
            string $key,
            float $connectTimeout,
            array $argv,
        ) {
            // @codeCoverageIgnoreStart
            // Only executed in thread.
            \define("AMP_CONTEXT", "parallel");
            \define("AMP_CONTEXT_ID", $id);

            EventLoop::unreference(EventLoop::repeat(self::EXIT_CHECK_FREQUENCY, function (): void {
                // Timer to give the chance for the PHP VM to be interrupted by Runtime::kill(), since system calls
                // such as select() will not be interrupted.
            }));

            EventLoop::queue(Internal\runContext(...), $uri, $key, new TimeoutCancellation($connectTimeout), $argv);

            EventLoop::run();

            return 0;
            // @codeCoverageIgnoreEnd
        }, [$id, $ipcHub->getUri(), $key, $childConnectTimeout, $script]);

        if (!$future) {
            $runtime->kill();
            throw new ContextException('Starting the thread did not return a future');
        }

        try {
            $socket = $ipcHub->accept($key, $cancellation);
            $ipcChannel = new StreamChannel($socket, $socket);

            $socket = $ipcHub->accept($key, $cancellation);
            $resultChannel = new StreamChannel($socket, $socket);
        } catch (\Throwable $exception) {
            $runtime->kill();

            $cancellation?->throwIfRequested();

            throw new ContextException("Starting the runtime failed", 0, $exception);
        }

        return new self($id, $runtime, $future, $hub, $ipcChannel, $resultChannel);
    }

    private readonly int $oid;

    private bool $exited = false;

    /**
     * @throws ContextException
     */
    private function __construct(
        private readonly int $id,
        private readonly Runtime $runtime,
        ParallelFuture $future,
        private readonly ParallelHub $hub,
        StreamChannel $ipcChannel,
        StreamChannel $resultChannel,
    ) {
        parent::__construct($ipcChannel, $resultChannel);

        $exited = &$this->exited;
        $this->hub->add($this->id, $future)->finally(static function () use (&$exited): void {
            $exited = true;
        });

        $pid = \getmypid();
        if ($pid === false) {
            throw new ContextException("Failed to determine PID");
        }

        $this->oid = $pid;
    }

    #[\Override]
    public function receive(?Cancellation $cancellation = null): mixed
    {
        if ($this->exited) {
            throw new ContextException('The thread has exited');
        }

        return parent::receive($cancellation);
    }

    #[\Override]
    public function send(mixed $data): void
    {
        if ($this->exited) {
            throw new ContextException('The thread has exited');
        }

        parent::send($data);
    }

    /**
     * Kills the thread if it is still running.
     */
    public function __destruct()
    {
        if (\getmypid() === $this->oid) {
            $this->close();
        }
    }

    #[\Override]
    public function close(): void
    {
        if (!$this->exited) {
            try {
                $this->runtime->kill();
            } catch (Closed) {
                // ignore
            }
        }

        $this->hub->remove($this->id);

        parent::close();
    }

    #[\Override]
    public function join(?Cancellation $cancellation = null): mixed
    {
        $result = $this->receiveExitResult($cancellation);

        return $result->getResult();
    }
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Context;

use Amp\ByteStream;
use Amp\Cancellation;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Parallel\Ipc\IpcHub;
use Amp\Parallel\Ipc\LocalIpcHub;
use function Amp\async;

final class DefaultContextFactory implements ContextFactory
{
    use ForbidCloning;
    use ForbidSerialization;

    private readonly ContextFactory $contextFactory;

    /**
     * @param IpcHub $ipcHub Optional IpcHub instance.
     */
    public function __construct(IpcHub $ipcHub = new LocalIpcHub())
    {
        if (ThreadContext::isSupported()) {
            $this->contextFactory = new ThreadContextFactory(ipcHub: $ipcHub);
        } else {
            $this->contextFactory = new ProcessContextFactory(ipcHub: $ipcHub);
        }
    }

    /**
     * @param string|non-empty-list<string> $script
     *
     * @throws ContextException
     */
    #[\Override]
    public function start(string|array $script, ?Cancellation $cancellation = null): Context
    {
        $context = $this->contextFactory->start($script, $cancellation);

        if ($context instanceof ProcessContext) {
            $stdout = $context->getStdout();
            $stdout->unreference();

            $stderr = $context->getStderr();
            $stderr->unreference();

            async(ByteStream\pipe(...), $stdout, ByteStream\getStdout())->ignore();
            async(ByteStream\pipe(...), $stderr, ByteStream\getStderr())->ignore();
        }

        return $context;
    }
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Context;

use Amp\Cancellation;

interface ContextFactory
{
    /**
     * Creates a new execution context.
     *
     * @param string|non-empty-list<string> $script Path to PHP script or array with first element as path and following
     *     elements as options to the PHP script (e.g.: ['bin/worker', 'ArgumentValue', '--option', 'OptionValue'].
     */
    public function start(string|array $script, ?Cancellation $cancellation = null): Context;
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Context;

use Amp\ByteStream\StreamChannel;
use Amp\Cancellation;
use Amp\Parallel\Context\Internal\AbstractContext;
use Amp\Parallel\Ipc\IpcHub;
use Amp\Serialization\NativeSerializer;
use Amp\Serialization\Serializer;
use Amp\TimeoutCancellation;
use Revolt\EventLoop;

/**
 * USE AT YOUR OWN RISK! This context is not used by default in {@see DefaultContextFactory} because the timing of its
 * creation must be purposeful and situational.
 *
 * Forking is not recommended at arbitrary points in an application since the entire state of the parent process is
 * inherited into the child process, including the event-loop!
 *
 * Forking very early in an application, may be beneficial to copy state from the parent that was expensive to set up.
 *
 * This context is NOT compatible with event-loop extensions such as ext-uv or ext-ev.
 *
 * @template-covariant TResult
 * @template-covariant TReceive
 * @template TSend
 * @extends AbstractContext<TResult, TReceive, TSend>
 */
final class ForkContext extends AbstractContext
{
    private const DEFAULT_START_TIMEOUT = 5;

    public static function isSupported(): bool
    {
        return \extension_loaded('pcntl')
            && \extension_loaded('posix')
            && \function_exists('pcntl_fork') // pcntl_fork may be disabled.
            && EventLoop::getDriver()->getHandle() === null;
    }

    /**
     * @param string|non-empty-list<string> $script Path to PHP script or array with first element as path and
     *     following elements options to the PHP script (e.g.: ['bin/worker.php', 'Option1Value', 'Option2Value']).
     * @param positive-int $childConnectTimeout Number of seconds the child will attempt to connect to the parent
     *      before failing.
     *
     * @throws ContextException If starting the process fails.
     */
    public static function start(
        IpcHub $ipcHub,
        string|array $script,
        ?Cancellation $cancellation = null,
        int $childConnectTimeout = self::DEFAULT_START_TIMEOUT,
        Serializer $serializer = new NativeSerializer(),
    ): self {
        $key = $ipcHub->generateKey();

        // Fork
        if (($pid = \pcntl_fork()) < 0) {
            throw new ContextException("Forking failed: " . \posix_strerror(\posix_get_last_error()));
        }

        // Parent
        if ($pid > 0) {
            try {
                $socket = $ipcHub->accept($key, $cancellation);
                $ipcChannel = new StreamChannel($socket, $socket, $serializer);

                $socket = $ipcHub->accept($key, $cancellation);
                $resultChannel = new StreamChannel($socket, $socket, $serializer);
            } catch (\Throwable $exception) {
                $cancellation?->throwIfRequested();

                throw new ContextException("Connecting failed after forking", previous: $exception);
            }

            return new self($pid, $ipcChannel, $resultChannel);
        }

        // Child
        \define("AMP_CONTEXT", "fork");
        \define("AMP_CONTEXT_ID", \getmypid());

        if (\is_string($script)) {
            $script = [$script];
        }

        $connectCancellation = new TimeoutCancellation((float) $childConnectTimeout);
        Internal\runContext($ipcHub->getUri(), $key, $connectCancellation, $script, $serializer);

        exit(0);
    }

    private ?int $exited = null;

    private bool $weKilled = false;

    /**
     * @param StreamChannel<TReceive, TSend> $ipcChannel
     */
    private function __construct(
        private readonly int $pid,
        StreamChannel $ipcChannel,
        StreamChannel $resultChannel,
    ) {
        parent::__construct($ipcChannel, $resultChannel);
    }

    #[\Override]
    public function receive(?Cancellation $cancellation = null): mixed
    {
        $this->checkExit(false); // Will throw if the process exited unexpectedly.

        return parent::receive($cancellation);
    }

    #[\Override]
    public function send(mixed $data): void
    {
        $this->checkExit(false); // Will throw if the process exited unexpectedly.

        parent::send($data);
    }

    private function checkExit(bool $wait): ?int
    {
        if ($this->exited === null) {
            if (\pcntl_waitpid($this->pid, $status, $wait ? 0 : \WNOHANG) === 0) {
                return null;
            }

            $this->exited = match (true) {
                \pcntl_wifsignaled($status) => \pcntl_wtermsig($status),
                \pcntl_wifexited($status) => \pcntl_wexitstatus($status) - 128,
                \pcntl_wifstopped($status) => \pcntl_wstopsig($status),
                default => -1,
            };
        }

        if (!$this->weKilled && $this->exited > 0) {
            throw new ContextException("Worker exited due to signal {$this->exited}", $this->exited);
        }

        return $this->exited;
    }

    #[\Override]
    public function close(): void
    {
        if ($this->checkExit(false) === null) {
            $this->weKilled = true;
            \posix_kill($this->pid, \SIGKILL);

            $this->checkExit(true);
        }

        parent::close();
    }

    #[\Override]
    public function join(?Cancellation $cancellation = null): mixed
    {
        $result = $this->receiveExitResult($cancellation);

        return $result->getResult();
    }
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Context;

use Amp\ByteStream\ReadableResourceStream;
use Amp\ByteStream\StreamChannel;
use Amp\ByteStream\WritableResourceStream;
use Amp\Cancellation;
use Amp\Parallel\Context\Internal\AbstractContext;
use Amp\Parallel\Ipc\IpcHub;
use Amp\Process\Process;
use Amp\Process\ProcessException;

/**
 * @template-covariant TResult
 * @template-covariant TReceive
 * @template TSend
 * @extends AbstractContext<TResult, TReceive, TSend>
 */
final class ProcessContext extends AbstractContext
{
    private const SCRIPT_PATH = __DIR__ . "/Internal/process-runner.php";
    private const DEFAULT_START_TIMEOUT = 5;

    private const DEFAULT_OPTIONS = [
        "html_errors" => "0",
        "display_errors" => "0",
        "log_errors" => "1",
    ];

    private const XDEBUG_OPTIONS = [
        "xdebug.mode",
        "xdebug.start_with_request",
        "xdebug.client_port",
        "xdebug.client_host",
    ];

    /** @var non-empty-string|null External version of SCRIPT_PATH if inside a PHAR. */
    private static ?string $pharScriptPath = null;

    /** @var non-empty-string|null PHAR path with a '.phar' extension. */
    private static ?string $pharCopy = null;

    /** @var non-empty-list<string>|null Cached path to located PHP binary. */
    private static ?array $binary = null;

    /** @var list<string>|null */
    private static ?array $options = null;

    /** @var list<int> */
    private static ?array $ignoredSignals = null;

    /**
     * @param string|non-empty-list<string> $script Path to PHP script or array with first element as path and
     *     following elements options to the PHP script (e.g.: ['bin/worker.php', 'Option1Value', 'Option2Value']).
     * @param string|null $workingDirectory Working directory.
     * @param array<string, string> $environment Array of environment variables, or use an empty array to inherit from
     *     the parent.
     * @param string|non-empty-list<string>|null $binary Path to PHP binary or array of binary path and options.
     *      Null will attempt to automatically locate the binary.
     * @param positive-int $childConnectTimeout Number of seconds the child will attempt to connect to the parent
     *      before failing.
     *
     * @throws ContextException If starting the process fails.
     */
    public static function start(
        IpcHub $ipcHub,
        string|array $script,
        ?string $workingDirectory = null,
        array $environment = [],
        ?Cancellation $cancellation = null,
        string|array|null $binary = null,
        int $childConnectTimeout = self::DEFAULT_START_TIMEOUT
    ): self {
        /** @psalm-suppress RedundantFunctionCall */
        $script = \is_array($script) ? \array_values($script) : [$script];
        if (!$script) {
            throw new \ValueError('Empty script array provided to process context');
        }

        if ($binary === null) {
            $binary = self::$binary ??= self::locateBinary();
        } else {
            /** @psalm-suppress RedundantFunctionCall */
            $binary = \is_array($binary) ? \array_values($binary) : [$binary];
            if (!$binary) {
                throw new \ValueError('Empty binary array provided to process context');
            }

            if (!\is_executable($binary[0])) {
                throw new \ValueError(
                    \sprintf("The PHP binary path '%s' was not found or is not executable", $binary[0])
                );
            }
        }

        // Write process runner to external file if inside a PHAR,
        // because PHP can't open files inside a PHAR directly except for the stub.
        if (\str_starts_with(self::SCRIPT_PATH, "phar://")) {
            if (self::$pharScriptPath !== null) {
                $scriptPath = self::$pharScriptPath;
            } else {
                $path = \dirname(self::SCRIPT_PATH);

                if (!\str_ends_with(\Phar::running(false), ".phar")) {
                    self::$pharCopy = \sys_get_temp_dir() . "/phar-" . \bin2hex(\random_bytes(10)) . ".phar";
                    \copy(\Phar::running(false), self::$pharCopy);

                    \register_shutdown_function(static fn () => self::unlinkExternalCopy(self::$pharCopy));

                    $path = "phar://" . self::$pharCopy . "/" . \substr($path, \strlen(\Phar::running(true)));
                }

                $contents = \file_get_contents(self::SCRIPT_PATH);
                if ($contents === false) {
                    throw new ContextException("Unable to read script '" . self::SCRIPT_PATH . "'");
                }

                $contents = \str_replace("__DIR__", \var_export($path, true), $contents);
                $suffix = \bin2hex(\random_bytes(10));
                self::$pharScriptPath = $scriptPath = \sys_get_temp_dir() . "/amp-process-runner-" . $suffix . ".php";
                \file_put_contents($scriptPath, $contents);

                \register_shutdown_function(static fn () => self::unlinkExternalCopy(self::$pharScriptPath));
            }

            // Monkey-patch the script path in the same way, only supported if the command is given as array.
            if (isset(self::$pharCopy)) {
                $script[0] = "phar://" . self::$pharCopy . \substr($script[0], \strlen(\Phar::running(true)));
            }
        } else {
            $scriptPath = self::SCRIPT_PATH;
        }

        $key = $ipcHub->generateKey();

        /** @var list<string> $command */
        $command = [
            ...$binary,
            ...(self::$options ??= self::buildOptions()),
            $scriptPath,
            $ipcHub->getUri(),
            (string) \strlen($key),
            (string) $childConnectTimeout,
            ...$script,
        ];

        try {
            $process = Process::start($command, $workingDirectory, $environment);
        } catch (\Throwable $exception) {
            throw new ContextException("Starting the process failed: " . $exception->getMessage(), 0, $exception);
        }

        try {
            $process->getStdin()->write($key);

            $socket = $ipcHub->accept($key, $cancellation);
            $ipcChannel = new StreamChannel($socket, $socket);

            $socket = $ipcHub->accept($key, $cancellation);
            $resultChannel = new StreamChannel($socket, $socket);
        } catch (\Throwable $exception) {
            if ($process->isRunning()) {
                $process->kill();
            }

            $cancellation?->throwIfRequested();

            throw new ContextException("Starting the process failed", 0, $exception);
        }

        return new self($process, $ipcChannel, $resultChannel);
    }

    private static function unlinkExternalCopy(?string $filepath): void
    {
        if ($filepath === null) {
            return;
        }

        \set_error_handler(static fn () => true);
        try {
            \unlink($filepath);
        } finally {
            \restore_error_handler();
        }
    }

    /**
     * @return non-empty-list<string>
     */
    private static function locateBinary(): array
    {
        if (\PHP_SAPI === "cli") {
            return [\PHP_BINARY];
        } elseif (\PHP_SAPI === "phpdbg") {
            return [\PHP_BINARY, '-qrr'];
        }

        $executable = \PHP_OS_FAMILY === 'Windows' ? "php.exe" : "php";

        /** @psalm-suppress RiskyTruthyFalsyComparison */
        $paths = \array_filter(\explode(
            \PATH_SEPARATOR,
            \getenv('PATH') ?: '/usr/bin' . \PATH_SEPARATOR . '/usr/local/bin',
        ));
        $paths[] = \PHP_BINDIR;
        $paths = \array_unique($paths);

        foreach ($paths as $path) {
            $path .= \DIRECTORY_SEPARATOR . $executable;
            if (\is_executable($path)) {
                return [$path];
            }
        }

        throw new \Error("Could not locate PHP executable binary");
    }

    /**
     * @return list<string>
     */
    private static function buildOptions(): array
    {
        $options = self::DEFAULT_OPTIONS;

        // This copies any ini values set via the command line (e.g., a debug run in PhpStorm)
        // to the child process, instead of relying only on those set in an ini file.
        if (\extension_loaded('xdebug') && \ini_get("xdebug.mode") !== false) {
            foreach (self::XDEBUG_OPTIONS as $option) {
                $iniValue = \ini_get($option);
                if ($iniValue !== false) {
                    $options[$option] = $iniValue;
                }
            }
        }

        $result = [];

        foreach ($options as $option => $value) {
            $result[] = \sprintf("-d%s=%s", $option, $value);
        }

        return $result;
    }

    /**
     * @return list<int>
     */
    public static function getIgnoredSignals(): array
    {
        return self::$ignoredSignals ??= [
            \defined('SIGHUP') ? \SIGHUP : 1,
            \defined('SIGINT') ? \SIGINT : 2,
            \defined('SIGQUIT') ? \SIGQUIT : 3,
            \defined('SIGTERM') ? \SIGTERM : 15,
            \defined('SIGALRM') ? \SIGALRM : 14,
            \defined('SIGUSR1') ? \SIGUSR1 : 10,
            \defined('SIGUSR2') ? \SIGUSR2 : 12,
        ];
    }

    /**
     * @param StreamChannel<TReceive, TSend> $ipcChannel
     */
    private function __construct(
        private readonly Process $process,
        StreamChannel $ipcChannel,
        StreamChannel $resultChannel,
    ) {
        parent::__construct($ipcChannel, $resultChannel);
    }

    public function __destruct()
    {
        $this->close();
    }

    /**
     * @return TResult
     * @throws ContextException
     */
    #[\Override]
    public function join(?Cancellation $cancellation = null): mixed
    {
        $result = $this->receiveExitResult($cancellation);

        $code = $this->process->join();

        try {
            return $result->getResult();
        } finally {
            if ($code !== 0) {
                // If an ExitFailure throws above, the exception will be automatically attached as the previous
                // exception on the instance thrown below.
                throw new ContextException(\sprintf("Context exited with code %d", $code));
            }
        }
    }

    /**
     * Send a signal to the process.
     *
     * @throws StatusError|ProcessException
     * @see Process::signal()
     */
    public function signal(int $signo): void
    {
        $this->process->signal($signo);
    }

    /**
     * Returns the PID of the process.
     *
     * @throws StatusError
     * @see Process::getPid()
     */
    public function getPid(): int
    {
        return $this->process->getPid();
    }

    /**
     * Returns the STDIN stream of the process.
     *
     * @throws StatusError
     * @see Process::getStdin()
     */
    public function getStdin(): WritableResourceStream
    {
        return $this->process->getStdin();
    }

    /**
     * Returns the STDOUT stream of the process.
     *
     * @throws StatusError
     * @see Process::getStdout()
     */
    public function getStdout(): ReadableResourceStream
    {
        return $this->process->getStdout();
    }

    /**
     * Returns the STDOUT stream of the process.
     *
     * @throws StatusError
     * @see Process::getStderr()
     */
    public function getStderr(): ReadableResourceStream
    {
        return $this->process->getStderr();
    }

    #[\Override]
    public function close(): void
    {
        $this->process->kill();

        parent::close();
    }
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Context;

use Amp\Cancellation;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Parallel\Ipc\IpcHub;
use Amp\Parallel\Ipc\LocalIpcHub;

final class ThreadContextFactory implements ContextFactory
{
    use ForbidCloning;
    use ForbidSerialization;

    /**
     * @param positive-int $childConnectTimeout Number of seconds the child will attempt to connect to the parent
     *      before failing.
     * @param IpcHub $ipcHub Optional IpcHub instance.
     */
    public function __construct(
        private readonly int $childConnectTimeout = 5,
        private readonly IpcHub $ipcHub = new LocalIpcHub(),
    ) {
    }

    #[\Override]
    public function start(array|string $script, ?Cancellation $cancellation = null): ThreadContext
    {
        return ThreadContext::start($this->ipcHub, $script, $cancellation, $this->childConnectTimeout);
    }
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Context;

use Amp\Cancellation;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Parallel\Ipc\IpcHub;
use Amp\Parallel\Ipc\LocalIpcHub;

/**
 * USE AT YOUR OWN RISK!
 *
 * Forking is not recommended at arbitrary points in an application since the entire state of the parent process is
 * inherited into the child process, including the event-loop!
 *
 * We recommend using {@see DefaultContextFactory} or {@see ProcessContextFactory} for general-purpose applications.
 */
final class ForkContextFactory implements ContextFactory
{
    use ForbidCloning;
    use ForbidSerialization;

    /**
     * @param positive-int $childConnectTimeout Number of seconds the child will attempt to connect to the parent
     *      before failing.
     * @param IpcHub $ipcHub Optional IpcHub instance.
     */
    public function __construct(
        private readonly int $childConnectTimeout = 5,
        private readonly IpcHub $ipcHub = new LocalIpcHub(),
    ) {
    }

    /**
     * @param string|non-empty-list<string> $script
     *
     * @throws ContextException
     */
    #[\Override]
    public function start(string|array $script, ?Cancellation $cancellation = null): ForkContext
    {
        return ForkContext::start(
            ipcHub: $this->ipcHub,
            script: $script,
            cancellation: $cancellation,
            childConnectTimeout: $this->childConnectTimeout,
        );
    }
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Context;

use Amp\Sync\ChannelException;

class ContextException extends ChannelException
{
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Context;

/**
 * @psalm-type FlattenedTrace = list<array<non-empty-string, scalar|list<scalar>>>
 */
final class ContextPanicError extends \Error
{
    use Internal\ContextException;
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Context\Internal;

use Amp\Cancellation;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Sync\Channel;

/**
 * @template-covariant TReceive
 * @template TSend
 * @implements Channel<TReceive, TSend>
 *
 * @internal
 */
final class ContextChannel implements Channel
{
    use ForbidCloning;
    use ForbidSerialization;

    public function __construct(
        private readonly Channel $channel,
    ) {
    }

    #[\Override]
    public function send(mixed $data): void
    {
        $this->channel->send(new ContextMessage($data));
    }

    #[\Override]
    public function receive(?Cancellation $cancellation = null): mixed
    {
        return $this->channel->receive($cancellation);
    }

    #[\Override]
    public function close(): void
    {
        $this->channel->close();
    }

    #[\Override]
    public function isClosed(): bool
    {
        return $this->channel->isClosed();
    }

    #[\Override]
    public function onClose(\Closure $onClose): void
    {
        $this->channel->onClose($onClose);
    }
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Context\Internal;

/**
 * @internal
 * @template-covariant TValue
 * @implements ExitResult<TValue>
 */
final class ExitSuccess implements ExitResult
{
    /**
     * @param TValue $result
     */
    public function __construct(
        private readonly mixed $result
    ) {
    }

    /**
     * @return TValue
     */
    #[\Override]
    public function getResult(): mixed
    {
        return $this->result;
    }
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Context\Internal;

use Amp\Parallel\Context\ContextException;
use Amp\Parallel\Context\ContextPanicError;
use function Amp\Parallel\Context\flattenThrowableBacktrace;

/**
 * @internal
 * @psalm-import-type FlattenedTrace from ContextPanicError
 * @template-implements ExitResult<never>
 */
final class ExitFailure implements ExitResult
{
    /** @var class-string<\Throwable> */
    private readonly string $className;

    private readonly string $message;

    private readonly int|string $code;

    private readonly string $file;

    private readonly int $line;

    /** @var FlattenedTrace */
    private readonly array $trace;

    private readonly ?self $previous;

    public function __construct(\Throwable $exception)
    {
        $this->className = \get_class($exception);
        $this->message = $exception->getMessage();
        $this->code = $exception->getCode();
        $this->file = $exception->getFile();
        $this->line = $exception->getLine();
        $this->trace = flattenThrowableBacktrace($exception);

        $previous = $exception->getPrevious();
        $this->previous = $previous ? new self($previous) : null;
    }

    /**
     * @throws ContextException
     */
    #[\Override]
    public function getResult(): never
    {
        $exception = $this->createException();

        throw new ContextException(
            'Process exited with an uncaught exception: ' . $exception->getMessage(),
            previous: $exception,
        );
    }

    private function createException(): ContextPanicError
    {
        $previous = $this->previous?->createException();

        return new ContextPanicError(
            $this->className,
            $this->message,
            $this->code,
            $this->file,
            $this->line,
            $this->trace,
            $previous,
        );
    }
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Context\Internal;

use Amp\DeferredFuture;
use Amp\Future as AmpFuture;
use parallel\Events;
use parallel\Future as ParallelFuture;
use Revolt\EventLoop;

/** @internal */
final class ParallelHub
{
    private const EXIT_CHECK_FREQUENCY = 0.25;

    /** @var array<int, DeferredFuture> */
    private array $deferredFutures = [];

    private readonly string $watcher;

    private readonly Events $events;

    public function __construct()
    {
        $events = $this->events = new Events();
        $this->events->setBlocking(false);

        $deferredFutures = &$this->deferredFutures;
        $this->watcher = EventLoop::repeat(self::EXIT_CHECK_FREQUENCY, static function () use (
            &$deferredFutures,
            $events,
        ): void {
            while ($event = $events->poll()) {
                $id = (int) $event->source;
                \assert(isset($deferredFutures[$id]), 'Deferred future for context ID not found');
                $deferredFuture = $deferredFutures[$id];
                unset($deferredFutures[$id]);
                $deferredFuture->complete();
            }
        });
        EventLoop::disable($this->watcher);
        EventLoop::unreference($this->watcher);
    }

    public function add(int $id, ParallelFuture $future): AmpFuture
    {
        $this->deferredFutures[$id] = $deferred = new DeferredFuture();
        $this->events->addFuture((string) $id, $future);

        EventLoop::enable($this->watcher);

        return $deferred->getFuture();
    }

    public function remove(int $id): void
    {
        $deferred = $this->deferredFutures[$id] ?? null;
        if (!$deferred) {
            return;
        }

        if (!$deferred->isComplete()) {
            $deferred->complete();
        }

        unset($this->deferredFutures[$id]);
        $this->events->remove((string) $id);

        if (empty($this->deferredFutures)) {
            EventLoop::disable($this->watcher);
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Context\Internal;

use Amp\ByteStream;
use Amp\Parallel\Context\ProcessContext;
use Amp\Parallel\Ipc;
use Amp\TimeoutCancellation;
use Revolt\EventLoop;

\define("AMP_CONTEXT", "process");
\define("AMP_CONTEXT_ID", \getmypid());

// Doesn't exist in phpdbg...
if (\function_exists("cli_set_process_title")) {
    \set_error_handler(static fn () => true);
    try {
        \cli_set_process_title("amp-process");
    } finally {
        \restore_error_handler();
    }
}

(function (): void {
    $paths = [
        \dirname(__DIR__, 5) . "/autoload.php",
        \dirname(__DIR__, 3) . "/vendor/autoload.php",
    ];

    foreach ($paths as $path) {
        if (\file_exists($path)) {
            $autoloadPath = $path;
            break;
        }
    }

    if (!isset($autoloadPath)) {
        \file_put_contents('php://stderr', "Could not locate autoload.php in any of the following files: " . \implode(", ", $paths), \FILE_APPEND);
        exit(255);
    }

    /** @psalm-suppress UnresolvableInclude */
    require $autoloadPath;
})();

// Wrap in a closure to keep local variables out of global scope
(function () use ($argv, $argc): void {
    try {
        foreach (ProcessContext::getIgnoredSignals() as $signal) {
            EventLoop::unreference(EventLoop::onSignal($signal, static fn () => null));
        }
    } catch (EventLoop\UnsupportedFeatureException) {
        // Signal handling not supported on current event loop driver.
    }

    /** @var list<string> $argv */

    if (!isset($argv[1])) {
        \file_put_contents('php://stderr', "No socket path provided", \FILE_APPEND);
        exit(255);
    }

    if (!isset($argv[2]) || !\is_numeric($argv[2])) {
        \file_put_contents('php://stderr', "No key length provided", \FILE_APPEND);
        exit(255);
    }

    if (!isset($argv[3]) || !\is_numeric($argv[3])) {
        \file_put_contents('php://stderr', "No timeout provided", \FILE_APPEND);
        exit(255);
    }

    [, $uri, $length, $timeout] = $argv;
    $length = (int) $length;
    $timeout = (int) $timeout;

    \assert($length > 0 && $timeout > 0);

    // Remove script path, socket path, key length, and timeout from process arguments.
    $argv = \array_slice($argv, 4);

    try {
        $cancellation = new TimeoutCancellation($timeout);
        $key = Ipc\readKey(ByteStream\getStdin(), $cancellation, $length);
    } catch (\Throwable $exception) {
        \file_put_contents('php://stderr', $exception->getMessage(), \FILE_APPEND);
        exit(255);
    }

    EventLoop::queue(runContext(...), $uri, $key, $cancellation, $argv);

    EventLoop::run();
})();
<?php declare(strict_types=1);

namespace Amp\Parallel\Context\Internal;

/** @internal */
final class ContextMessage
{
    public function __construct(
        private readonly mixed $message,
    ) {
    }

    public function getMessage(): mixed
    {
        return $this->message;
    }
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Context\Internal;

use Amp\Cancellation;
use Amp\CancelledException;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Future;
use Amp\Parallel\Context\Context;
use Amp\Parallel\Context\ContextException;
use Amp\Sync\Channel;
use Amp\Sync\ChannelException;
use function Amp\async;
use function Amp\Parallel\Context\flattenArgument;

/**
 * @template-covariant TResult
 * @template-covariant TReceive
 * @template TSend
 * @implements Context<TResult, TReceive, TSend>
 */
abstract class AbstractContext implements Context
{
    use ForbidCloning;
    use ForbidSerialization;

    /** @var Future<ExitResult<TResult>>|null */
    private ?Future $result = null;

    protected function __construct(
        private readonly Channel $ipcChannel,
        private readonly Channel $resultChannel,
    ) {
    }

    #[\Override]
    public function receive(?Cancellation $cancellation = null): mixed
    {
        try {
            $data = $this->ipcChannel->receive($cancellation);
        } catch (ChannelException $exception) {
            $this->ipcChannel->close();

            throw new ContextException(
                "The context stopped responding during receive, potentially due to a fatal error or calling exit",
                previous: $exception,
            );
        }

        if (!$data instanceof ContextMessage) {
            if ($data instanceof ExitResult) {
                $data = $data->getResult();

                throw new ContextException(\sprintf(
                    'Context unexpectedly exited when waiting to receive data with result: %s',
                    flattenArgument($data),
                ));
            }

            throw new ContextException(\sprintf(
                'Unexpected data type from context: %s',
                flattenArgument($data),
            ));
        }

        return $data->getMessage();
    }

    #[\Override]
    public function send(mixed $data): void
    {
        try {
            $this->ipcChannel->send($data);
        } catch (ChannelException $exception) {
            $this->ipcChannel->close();

            throw new ContextException(
                "The context stopped responding during send, potentially due to a fatal error or calling exit",
                previous: $exception,
            );
        }
    }

    #[\Override]
    public function close(): void
    {
        $this->ipcChannel->close();
    }

    #[\Override]
    public function isClosed(): bool
    {
        return $this->ipcChannel->isClosed();
    }

    #[\Override]
    public function onClose(\Closure $onClose): void
    {
        $this->ipcChannel->onClose($onClose);
    }

    protected function receiveExitResult(?Cancellation $cancellation = null): ExitResult
    {
        while ($this->result) {
            try {
                $this->result->await($cancellation);
            } catch (CancelledException) {
                // Ignore cancellation from a prior join request, throw only if this request was cancelled.
                $cancellation?->throwIfRequested();
            }
        }

        $this->result = async(function () use ($cancellation): ExitResult {
            try {
                $data = $this->resultChannel->receive($cancellation);
                $this->resultChannel->close();
            } catch (CancelledException $exception) {
                throw $exception;
            } catch (\Throwable $exception) {
                $this->resultChannel->close();
                throw new ContextException("Failed to receive result from context", previous: $exception);
            }

            if (!$data instanceof ExitResult) {
                throw new ContextException(\sprintf(
                    "The context sent data instead of exiting: %s",
                    flattenArgument($data),
                ));
            }

            return $data;
        });

        try {
            return $this->result->await();
        } catch (CancelledException $exception) {
            $this->result = null;
            throw $exception;
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Context\Internal;

use Amp\Parallel\Context\ContextException;

/**
 * @internal
 * @template-covariant TValue
 */
interface ExitResult
{
    /**
     * @return TValue Return value of the callable given to the execution context.
     *
     * @throws ContextException If the context exited with an uncaught exception.
     */
    public function getResult(): mixed;
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Context\Internal;

use Amp\Parallel\Context\ContextPanicError;
use function Amp\Parallel\Context\formatFlattenedBacktrace;

/**
 * @psalm-import-type FlattenedTrace from ContextPanicError
 *
 * @internal
 */
trait ContextException
{
    /**
     * @param class-string<\Throwable> $className Original exception class name.
     * @param FlattenedTrace $originalTrace Backtrace generated by {@see flattenThrowableBacktrace()}.
     */
    public function __construct(
        private readonly string $className,
        private readonly string $originalMessage,
        private readonly int|string $originalCode,
        private readonly string $originalFile,
        private readonly int $originalLine,
        private readonly array $originalTrace,
        ?\Throwable $previous = null,
    ) {
        $format = '%s thrown in context with message "%s" and code "%s" in %s:%d'
            . "\nStack trace in context:\n%s" ;

        parent::__construct(\sprintf(
            $format,
            $className,
            $originalMessage,
            $originalCode,
            $originalFile,
            $originalLine,
            $this->getOriginalTraceAsString(),
        ), previous: $previous);
    }

    /**
     * @return class-string<\Throwable> Original exception class name.
     */
    public function getOriginalClassName(): string
    {
        return $this->className;
    }

    /**
     * @return string Original exception message.
     */
    public function getOriginalMessage(): string
    {
        return $this->originalMessage;
    }

    /**
     * @return int|string Original exception code.
     */
    public function getOriginalCode(): int|string
    {
        return $this->originalCode;
    }

    public function getOriginalFile(): string
    {
        return $this->originalFile;
    }

    public function getOriginalLine(): int
    {
        return $this->originalLine;
    }

    /**
     * Returns the original exception stack trace.
     *
     * @return FlattenedTrace Same as {@see Throwable::getTrace()}, except all function arguments are formatted
     *      as strings. See {@see formatFlattenedBacktrace()}.
     */
    public function getOriginalTrace(): array
    {
        return $this->originalTrace;
    }

    /**
     * Original backtrace flattened to a human-readable string.
     */
    public function getOriginalTraceAsString(): string
    {
        return formatFlattenedBacktrace($this->originalTrace);
    }
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Context\Internal;

use Amp\ByteStream\StreamChannel;
use Amp\Cancellation;
use Amp\Future;
use Amp\Parallel\Ipc;
use Amp\Serialization\NativeSerializer;
use Amp\Serialization\SerializationException;
use Amp\Serialization\Serializer;
use Amp\Sync\ChannelException;

/** @internal */
function runContext(
    string $uri,
    string $key,
    Cancellation $connectCancellation,
    array $argv,
    Serializer $serializer = new NativeSerializer(),
): void {
    /** @noinspection PhpUnusedLocalVariableInspection */
    $argc = \count($argv);

    try {
        $socket = Ipc\connect($uri, $key, $connectCancellation);
        $ipcChannel = new StreamChannel($socket, $socket, $serializer);

        $socket = Ipc\connect($uri, $key, $connectCancellation);
        $resultChannel = new StreamChannel($socket, $socket, $serializer);
    } catch (\Throwable $exception) {
        \file_put_contents('php://stderr', $exception->getMessage(), \FILE_APPEND);
        exit(255);
    }

    try {
        if (!isset($argv[0])) {
            throw new \Error("No script path given");
        }

        if (!\is_file($argv[0])) {
            throw new \Error(\sprintf(
                "No script found at '%s' (be sure to provide the full path to the script)",
                $argv[0],
            ));
        }

        try {
            // Protect current scope by requiring script within another function.
            // Using $argc, so it is available to the required script.
            $callable = (function () use ($argc, $argv): callable {
                /** @psalm-suppress UnresolvableInclude */
                return require $argv[0];
            })();
        } catch (\TypeError $exception) {
            throw new \Error(\sprintf(
                "Script '%s' did not return a callable function: %s",
                $argv[0],
                $exception->getMessage(),
            ), 0, $exception);
        } catch (\ParseError $exception) {
            throw new \Error(\sprintf(
                "Script '%s' contains a parse error: %s",
                $argv[0],
                $exception->getMessage(),
            ), 0, $exception);
        }

        $returnValue = $callable(new ContextChannel($ipcChannel));
        $result = new ExitSuccess($returnValue instanceof Future ? $returnValue->await() : $returnValue);
    } catch (\Throwable $exception) {
        $result = new ExitFailure($exception);
    }

    $ipcChannel->close();

    try {
        try {
            $resultChannel->send($result);
        } catch (SerializationException $exception) {
            // Serializing the result failed. Send the reason why.
            $resultChannel->send(new ExitFailure($exception));
        } finally {
            $resultChannel->close();
        }
    } catch (ChannelException) {
        // The parent may have already closed the channel after reading
        // the result (e.g. during shutdown). Nothing left to do.
    } catch (\Throwable $exception) {
        \file_put_contents('php://stderr', $exception->getMessage(), \FILE_APPEND);
        exit(255);
    }
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Context;

use Amp\Cancellation;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Parallel\Ipc\IpcHub;
use Amp\Parallel\Ipc\LocalIpcHub;

final class ProcessContextFactory implements ContextFactory
{
    use ForbidCloning;
    use ForbidSerialization;

    /**
     * @param string|null $workingDirectory Working directory.
     * @param array<string, string> $environment Array of environment variables, or use an empty array to inherit from
     *     the parent.
     * @param string|non-empty-list<string>|null $binary Path to PHP binary or array of binary path and options.
     *      Null will attempt to automatically locate the binary.
     * @param positive-int $childConnectTimeout Number of seconds the child will attempt to connect to the parent
     *      before failing.
     * @param IpcHub $ipcHub Optional IpcHub instance.
     */
    public function __construct(
        private readonly ?string $workingDirectory = null,
        private readonly array $environment = [],
        private readonly string|array|null $binary = null,
        private readonly int $childConnectTimeout = 5,
        private readonly IpcHub $ipcHub = new LocalIpcHub(),
    ) {
    }

    /**
     * @param string|non-empty-list<string> $script
     *
     * @throws ContextException
     */
    #[\Override]
    public function start(string|array $script, ?Cancellation $cancellation = null): ProcessContext
    {
        return ProcessContext::start(
            ipcHub: $this->ipcHub,
            script: $script,
            workingDirectory: $this->workingDirectory,
            environment: $this->environment,
            cancellation: $cancellation,
            binary: $this->binary,
            childConnectTimeout: $this->childConnectTimeout,
        );
    }
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Context;

use Amp\Cancellation;
use Amp\Sync\Channel;

/**
 * @template-covariant TResult
 * @template-covariant TReceive
 * @template TSend
 * @extends Channel<TReceive, TSend>
 */
interface Context extends Channel
{
    /**
     * @return TResult The data returned from the context. This method may be called at any time to await the result or
     *      an exception will be thrown if the context is closed or throws an exception or exits with a non-zero code.
     *
     * @throws ContextException If the context exited with an uncaught exception or non-zero code.
     */
    public function join(?Cancellation $cancellation = null): mixed;
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Context;

class StatusError extends \Error
{
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Context;

use Amp\Cancellation;
use Revolt\EventLoop;
use function Amp\Serialization\encodeUnprintableChars;

/**
 * @template TResult
 * @template TReceive
 * @template TSend
 *
 * @param string|non-empty-list<string> $script Path to PHP script or array with first element as path and following
 *     elements as options to the PHP script (e.g.: ['bin/worker', 'Option1Value', 'Option2Value'].
 *
 * @return Context<TResult, TReceive, TSend>
 */
function startContext(string|array $script, ?Cancellation $cancellation = null): Context
{
    return contextFactory()->start($script, $cancellation);
}

/**
 * Gets or sets the global context factory.
 */
function contextFactory(?ContextFactory $factory = null): ContextFactory
{
    static $map;
    $map ??= new \WeakMap();
    $driver = EventLoop::getDriver();

    if ($factory) {
        return $map[$driver] = $factory;
    }

    return $map[$driver] ??= new DefaultContextFactory();
}

/**
 * @psalm-type FlattenedTraceEntry = array<non-empty-string, scalar|list<scalar>>
 *
 * @return list<FlattenedTraceEntry> Serializable exception backtrace, with all function
 *      arguments flattened to strings.
 */
function flattenThrowableBacktrace(\Throwable $exception): array
{
    return \array_map(function (array $call): array {
        /** @psalm-suppress InvalidArrayOffset */
        unset($call['object']);
        $call['args'] = \array_map(flattenArgument(...), $call['args'] ?? []);

        /** @var FlattenedTraceEntry $call */
        return $call;
    }, $exception->getTrace());
}

/**
 * @param array $trace Backtrace produced by {@see flattenThrowableBacktrace()}.
 */
function formatFlattenedBacktrace(array $trace): string
{
    $output = [];

    foreach ($trace as $index => $call) {
        if (isset($call['class'])) {
            $name = $call['class'] . $call['type'] . $call['function'];
        } else {
            $name = $call['function'];
        }

        $output[] = \sprintf(
            '#%d %s(%d): %s(%s)',
            $index,
            $call['file'] ?? '[internal function]',
            $call['line'] ?? 0,
            $name,
            \implode(', ', $call['args'] ?? [])
        );
    }

    return \implode("\n", $output);
}

/**
 * @return string Serializable string representation of $value for backtraces.
 */
function flattenArgument(mixed $value): string
{
    if ($value instanceof \Closure) {
        $closureReflection = new \ReflectionFunction($value);
        return \sprintf(
            'Closure(%s:%s)',
            $closureReflection->getFileName(),
            $closureReflection->getStartLine()
        );
    }

    if (\is_object($value)) {
        return \sprintf('Object(%s)', \get_class($value));
    }

    if (\is_array($value)) {
        $length = \count($value);
        if ($length > 5) {
            $value = \array_slice($value, 0, 5);
        }
        $value = \implode(', ', \array_map(flattenArgument(...), $value));
        return 'Array([' . $value . ($length > 5 ? ', ...' : '') . '])';
    }

    if (\is_resource($value)) {
        return \sprintf('Resource(%s)', \get_resource_type($value));
    }

    if (\is_string($value)) {
        if (\strlen($value) > 30) {
            $value = \substr($value, 0, 27) . '...';
        }
        return '"' . encodeUnprintableChars($value) . '"';
    }

    if (\is_null($value)) {
        return 'null';
    }

    if (\is_bool($value)) {
        return $value ? 'true' : 'false';
    }

    if (\is_nan($value)) {
        return 'NAN';
    }

    return (string) $value;
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Worker;

class WorkerException extends \Exception
{
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Worker;

use Amp\Cancellation;
use Amp\Sync\Channel;

/**
 * A runnable unit of execution.
 *
 * @template-covariant TResult
 * @template TReceive
 * @template TSend
 */
interface Task
{
    /**
     * Executed when running the Task in a worker.
     *
     * @param Channel<TReceive, TSend> $channel Communication channel to parent process.
     * @param Cancellation $cancellation Tasks may safely ignore this parameter if they are not cancellable.
     *
     * @return TResult A specific type can (and should) be declared in implementing classes.
     */
    public function run(Channel $channel, Cancellation $cancellation): mixed;
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Worker;

/**
 * Common interface for exceptions thrown when Task::run() throws an exception when being executed in a worker.
 *
 * @psalm-type FlattenedTrace = list<array<non-empty-string, scalar|list<scalar>>>
 */
interface TaskFailureThrowable extends \Throwable
{
    /**
     * @return string Original exception class name.
     */
    public function getOriginalClassName(): string;

    /**
     * @return string Original exception message.
     */
    public function getOriginalMessage(): string;

    /**
     * @return int|string Original exception code.
     */
    public function getOriginalCode(): string|int;

    /**
     * @return string Original exception file from which it was thrown.
     */
    public function getOriginalFile(): string;

    /**
     * @return int Original exception line from which it was thrown.
     */
    public function getOriginalLine(): int;

    /**
     * Returns the original exception stack trace.
     *
     * @return FlattenedTrace Same as {@see Throwable::getTrace()}, except all function arguments are formatted
     *      as strings. See {@see formatFlattenedBacktrace()}.
     */
    public function getOriginalTrace(): array;

    /**
     * Original backtrace flattened to a human-readable string.
     */
    public function getOriginalTraceAsString(): string;
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Worker;

use Amp\Cancellation;
use Amp\DeferredFuture;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Parallel\Worker\Internal\PooledWorker;

final class DelegatingWorkerPool implements LimitedWorkerPool
{
    use ForbidCloning;
    use ForbidSerialization;

    /** @var array<int, Worker> */
    private array $workerStorage = [];

    private int $pendingWorkerCount = 0;

    /** @var \SplQueue<DeferredFuture<Worker|null>> */
    private readonly \SplQueue $waiting;

    /**
     * @param int $limit Maximum number of workers to use from the delegate pool.
     */
    public function __construct(private readonly WorkerPool $pool, private readonly int $limit)
    {
        $this->waiting = new \SplQueue();
    }

    #[\Override]
    public function isRunning(): bool
    {
        return $this->pool->isRunning();
    }

    #[\Override]
    public function isIdle(): bool
    {
        return $this->pool->isIdle();
    }

    #[\Override]
    public function submit(Task $task, ?Cancellation $cancellation = null): Execution
    {
        $worker = $this->selectWorker();

        $execution = $worker->submit($task, $cancellation);

        $execution->getFuture()->finally(fn () => $this->push($worker))->ignore();

        return $execution;
    }

    private function selectWorker(): Worker
    {
        do {
            if (\count($this->workerStorage) + $this->pendingWorkerCount < $this->limit) {
                $this->pendingWorkerCount++;

                try {
                    $worker = $this->pool->getWorker();
                } finally {
                    $this->pendingWorkerCount--;
                }
            } else {
                /** @var DeferredFuture<Worker|null> $waiting */
                $waiting = new DeferredFuture();
                $this->waiting->push($waiting);

                $worker = $waiting->getFuture()->await();
                if (!$worker?->isRunning()) {
                    continue;
                }
            }

            $this->workerStorage[\spl_object_id($worker)] = $worker;

            return $worker;
        } while (true);
    }

    private function push(Worker $worker): void
    {
        unset($this->workerStorage[\spl_object_id($worker)]);

        if (!$this->waiting->isEmpty()) {
            $deferredFuture = $this->waiting->dequeue();
            $deferredFuture->complete($worker->isRunning() ? $worker : null);
        }
    }

    #[\Override]
    public function shutdown(): void
    {
        if (!$this->waiting->isEmpty()) {
            $exception = new WorkerException('The pool was shutdown before a worker could be obtained');
            $this->clearWaiting($exception);
        }

        $this->pool->shutdown();
    }

    #[\Override]
    public function kill(): void
    {
        if (!$this->waiting->isEmpty()) {
            $exception = new WorkerException('The pool was killed before a worker could be obtained');
            $this->clearWaiting($exception);
        }

        $this->pool->kill();
    }

    private function clearWaiting(\Throwable $exception): void
    {
        while (!$this->waiting->isEmpty()) {
            $deferredFuture = $this->waiting->dequeue();
            $deferredFuture->error($exception);
        }
    }

    #[\Override]
    public function getWorker(): Worker
    {
        $worker = $this->selectWorker();
        return new PooledWorker($worker, $this->push(...));
    }

    #[\Override]
    public function getWorkerLimit(): int
    {
        return $this->limit;
    }

    #[\Override]
    public function getWorkerCount(): int
    {
        return \min($this->limit, $this->pool->getWorkerCount());
    }

    #[\Override]
    public function getIdleWorkerCount(): int
    {
        return \min($this->limit, $this->pool->getIdleWorkerCount());
    }
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Worker;

use Amp\Parallel\Context\Internal;

final class TaskFailureException extends \Exception implements TaskFailureThrowable
{
    use Internal\ContextException;
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Worker;

use Amp\Cancellation;

/**
 * An interface for a parallel worker thread that runs a queue of tasks.
 */
interface Worker
{
    /**
     * Checks if the worker is running.
     *
     * @return bool True if the worker is running, otherwise false.
     */
    public function isRunning(): bool;

    /**
     * Checks if the worker is currently idle.
     */
    public function isIdle(): bool;

    /**
     * @template TResult
     * @template TReceive
     * @template TSend
     *
     * Executes a {@see Task} on the worker.
     *
     * @param Task<TResult, TReceive, TSend> $task The task to execute.
     * @param Cancellation|null $cancellation Token to request cancellation. The task must support cancellation for
     * this to have any effect.
     *
     * @return Execution<TResult, TReceive, TSend>
     */
    public function submit(Task $task, ?Cancellation $cancellation = null): Execution;

    /**
     * Gracefully shutdown the worker once all outstanding tasks have completed executing. Returns once the
     * worker has been shutdown.
     */
    public function shutdown(): void;

    /**
     * Immediately kills the context.
     */
    public function kill(): void;
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Worker;

use Amp\Cancellation;
use Amp\CancelledException;
use Amp\DeferredCancellation;
use Amp\DeferredFuture;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Future;
use Amp\Parallel\Context\StatusError;
use Revolt\EventLoop;
use function Amp\async;

/**
 * Provides a pool of workers that can be used to execute multiple tasks asynchronously.
 *
 * A worker pool is a collection of worker threads that can perform multiple
 * tasks simultaneously. The load on each worker is balanced such that tasks
 * are completed as soon as possible and workers are used efficiently.
 */
final class ContextWorkerPool implements LimitedWorkerPool
{
    use ForbidCloning;
    use ForbidSerialization;

    private int $pendingWorkerCount = 0;

    /** @var \SplObjectStorage<Worker, int> A collection of all workers in the pool. */
    private readonly \SplObjectStorage $workers;

    /** @var \SplQueue<Worker> A collection of idle workers. */
    private readonly \SplQueue $idleWorkers;

    /** @var \SplQueue<DeferredFuture<Worker|null>> Task submissions awaiting an available worker. */
    private readonly \SplQueue $waiting;

    /** @var \Closure(Worker):void */
    private readonly \Closure $push;

    private ?Future $exitStatus = null;

    private readonly DeferredCancellation $deferredCancellation;

    /**
     * Creates a new worker pool.
     *
     * @param int $limit The maximum number of workers the pool should spawn.
     *     Defaults to `Pool::DEFAULT_MAX_SIZE`.
     * @param WorkerFactory|null $factory A worker factory to be used to create
     *     new workers.
     *
     * @throws \Error
     */
    public function __construct(
        private readonly int $limit = self::DEFAULT_WORKER_LIMIT,
        private readonly ?WorkerFactory $factory = null,
    ) {
        if ($limit <= 0) {
            throw new \ValueError("Maximum size must be a positive integer");
        }

        $this->workers = new \SplObjectStorage();
        $this->idleWorkers = $idleWorkers = new \SplQueue();
        $this->waiting = $waiting = new \SplQueue();

        $this->deferredCancellation = new DeferredCancellation();

        $this->push = static function (Worker $worker) use ($waiting, $idleWorkers): void {
            if ($waiting->isEmpty()) {
                $idleWorkers->push($worker);
            } else {
                $waiting->dequeue()->complete($worker);
            }
        };
    }

    public function __destruct()
    {
        if ($this->isRunning()) {
            $this->deferredCancellation->cancel();
            self::killWorkers($this->workers, $this->waiting);
        }
    }

    /**
     * Checks if the pool is running.
     *
     * @return bool True if the pool is running, otherwise false.
     */
    #[\Override]
    public function isRunning(): bool
    {
        return !$this->deferredCancellation->isCancelled();
    }

    /**
     * Checks if the pool has any idle workers.
     *
     * @return bool True if the pool has at least one idle worker, otherwise false.
     */
    #[\Override]
    public function isIdle(): bool
    {
        return $this->idleWorkers->count() > 0 || $this->workers->count() < $this->limit;
    }

    #[\Override]
    public function getWorkerLimit(): int
    {
        return $this->limit;
    }

    /**
     * Gets the maximum number of workers the pool may spawn to handle concurrent tasks.
     *
     * @return int The maximum number of workers.
     *
     * @deprecated Use {@see getWorkerLimit()} instead.
     */
    public function getLimit(): int
    {
        return $this->getWorkerLimit();
    }

    #[\Override]
    public function getWorkerCount(): int
    {
        return $this->workers->count() + $this->pendingWorkerCount;
    }

    #[\Override]
    public function getIdleWorkerCount(): int
    {
        return $this->idleWorkers->count();
    }

    /**
     * Submits a {@see Task} to be executed by the worker pool.
     */
    #[\Override]
    public function submit(Task $task, ?Cancellation $cancellation = null): Execution
    {
        $worker = $this->pull();
        $push = $this->push;

        try {
            $execution = $worker->submit($task, $cancellation);
        } catch (\Throwable $exception) {
            $push($worker);
            throw $exception;
        }

        $execution->getFuture()->finally(static fn () => $push($worker))->ignore();

        return $execution;
    }

    /**
     * Shuts down the pool and all workers in it.
     *
     * @throws StatusError If the pool has not been started.
     */
    #[\Override]
    public function shutdown(): void
    {
        if ($this->exitStatus) {
            $this->exitStatus->await();
            return;
        }

        $this->deferredCancellation->cancel();

        while (!$this->waiting->isEmpty()) {
            $this->waiting->dequeue()->error(
                $exception ??= new WorkerException('The pool shut down before the task could be executed'),
            );
        }

        $futures = \array_map(
            static fn (Worker $worker) => async($worker->shutdown(...)),
            \iterator_to_array($this->workers),
        );

        ($this->exitStatus = async(Future\awaitAll(...), $futures)->map(static fn () => null))->await();
    }

    /**
     * Kills all workers in the pool and halts the worker pool.
     */
    #[\Override]
    public function kill(): void
    {
        $this->deferredCancellation->cancel();
        self::killWorkers($this->workers, $this->waiting);
    }

    /**
     * @param \SplObjectStorage<Worker, int> $workers
     * @param \SplQueue<DeferredFuture<Worker|null>> $waiting
     */
    private static function killWorkers(
        \SplObjectStorage $workers,
        \SplQueue $waiting,
        ?\Throwable $exception = null,
    ): void {
        foreach ($workers as $worker) {
            \assert($worker instanceof Worker);
            if ($worker->isRunning()) {
                $worker->kill();
            }
        }

        while (!$waiting->isEmpty()) {
            $waiting->dequeue()->error(
                $exception ??= new WorkerException('The pool was killed before the task could be executed'),
            );
        }
    }

    #[\Override]
    public function getWorker(): Worker
    {
        $worker = $this->pull();
        return new Internal\PooledWorker($worker, $this->push);
    }

    /**
     * Pulls a worker from the pool.
     *
     * @throws StatusError
     * @throws WorkerException
     */
    private function pull(): Worker
    {
        if (!$this->isRunning()) {
            throw new StatusError("The pool was shut down");
        }

        do {
            if ($this->idleWorkers->isEmpty()) {
                /** @var DeferredFuture<Worker|null> $deferredFuture */
                $deferredFuture = new DeferredFuture;
                $this->waiting->enqueue($deferredFuture);

                if ($this->getWorkerCount() < $this->limit) {
                    // Max worker count has not been reached, so create another worker.
                    $this->pendingWorkerCount++;

                    $factory = $this->factory ?? workerFactory();
                    $pending = &$this->pendingWorkerCount;
                    $cancellation = $this->deferredCancellation->getCancellation();
                    $workers = $this->workers;
                    $future = async(static function () use (&$pending, $factory, $workers, $cancellation): Worker {
                        try {
                            $worker = $factory->create($cancellation);
                        } catch (CancelledException) {
                            throw new WorkerException('The pool shut down before the task could be executed');
                        } finally {
                            $pending--;
                        }

                        if (!$worker->isRunning()) {
                            throw new WorkerException('Worker factory did not create a viable worker');
                        }

                        $workers->offsetSet($worker, 0);
                        return $worker;
                    });

                    $waiting = $this->waiting;
                    $deferredCancellation = $this->deferredCancellation;
                    $future
                        ->map($this->push)
                        ->catch(static function (\Throwable $e) use ($deferredCancellation, $workers, $waiting): void {
                            $deferredCancellation->cancel();
                            self::killWorkers($workers, $waiting, $e);
                        })
                        ->ignore();
                }

                $worker = $deferredFuture->getFuture()->await();
            } else {
                // Shift a worker off the idle queue.
                $worker = $this->idleWorkers->shift();
            }

            if ($worker === null) {
                // Worker crashed when executing a Task, which should have failed.
                continue;
            }

            \assert($worker instanceof Worker);

            if ($worker->isRunning()) {
                return $worker;
            }

            // Worker crashed while idle; trigger error and remove it from the pool.
            EventLoop::queue(static function () use ($worker): void {
                try {
                    $worker->shutdown();
                    \trigger_error('Worker in pool exited unexpectedly', \E_USER_WARNING);
                } catch (\Throwable $exception) {
                    \trigger_error(
                        'Worker in pool crashed with exception on shutdown: ' . $exception->getMessage(),
                        \E_USER_WARNING,
                    );
                }
            });

            $this->workers->detach($worker);
        } while (true);
    }
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Worker;

use Amp\Cancellation;

/**
 * Interface for factories used to create new workers.
 */
interface WorkerFactory
{
    /**
     * Creates a new worker instance.
     *
     * @return Worker The newly created worker.
     */
    public function create(?Cancellation $cancellation = null): Worker;
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Worker\Internal;

use Amp\Parallel\Worker\TaskFailureThrowable;
use function Amp\Parallel\Context\flattenThrowableBacktrace;

/**
 * @internal
 * @psalm-import-type FlattenedTrace from TaskFailureThrowable
 * @template-extends TaskResult<never>
 */
class TaskFailure extends TaskResult
{
    /** @var class-string<\Throwable> */
    private readonly string $className;

    private readonly TaskExceptionType $type;

    private readonly string $message;

    private readonly string|int $code;

    private readonly string $file;

    private readonly int $line;

    /** @var FlattenedTrace */
    private readonly array $trace;

    private readonly ?self $previous;

    public function __construct(string $id, \Throwable $exception)
    {
        parent::__construct($id);
        $this->className = \get_class($exception);
        $this->type = TaskExceptionType::fromException($exception);
        $this->message = $exception->getMessage();
        $this->code = $exception->getCode();
        $this->file = $exception->getFile();
        $this->line = $exception->getLine();
        $this->trace = flattenThrowableBacktrace($exception);

        $previous = $exception->getPrevious();
        $this->previous = $previous ? new self($id, $previous) : null;
    }

    /**
     * @throws TaskFailureThrowable
     */
    #[\Override]
    public function getResult(): never
    {
        throw $this->createException();
    }

    final protected function createException(): TaskFailureThrowable
    {
        return $this->type->createException(
            $this->className,
            $this->message,
            $this->code,
            $this->file,
            $this->line,
            $this->trace,
            $this->previous?->createException(),
        );
    }
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Worker\Internal;

use Amp\Cancellation;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Parallel\Worker\Execution;
use Amp\Parallel\Worker\Task;
use Amp\Parallel\Worker\Worker;

/** @internal */
final class PooledWorker implements Worker
{
    use ForbidCloning;
    use ForbidSerialization;

    /**
     * @param Closure(Worker):void $push Callable to push the worker back into the queue.
     */
    public function __construct(
        private readonly Worker $worker,
        private readonly \Closure $push,
    ) {
    }

    /**
     * Automatically pushes the worker back into the queue.
     */
    public function __destruct()
    {
        ($this->push)($this->worker);
    }

    #[\Override]
    public function isRunning(): bool
    {
        return $this->worker->isRunning();
    }

    #[\Override]
    public function isIdle(): bool
    {
        return $this->worker->isIdle();
    }

    #[\Override]
    public function submit(Task $task, ?Cancellation $cancellation = null): Execution
    {
        $job = $this->worker->submit($task, $cancellation);

        // Retain a reference to $this to prevent premature release of worker.
        $job->getFuture()->finally(fn () => $this)->ignore();

        return $job;
    }

    #[\Override]
    public function shutdown(): void
    {
        $this->worker->shutdown();
    }

    #[\Override]
    public function kill(): void
    {
        $this->worker->kill();
    }
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Worker\Internal;

/**
 * @internal
 *
 * @template-covariant T
 */
abstract class TaskResult extends JobPacket
{
    /**
     * @return T Resolved with the task result or failure reason.
     */
    abstract public function getResult(): mixed;
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Worker\Internal;

use Amp\CancelledException;
use Amp\Parallel\Worker\TaskCancelledException;

/** @internal */
final class TaskCancelled extends TaskFailure
{
    public function __construct(string $id, CancelledException $exception)
    {
        parent::__construct($id, $exception);
    }

    /**
     * @throws TaskCancelledException
     */
    #[\Override]
    public function getResult(): never
    {
        throw new TaskCancelledException($this->createException());
    }
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Worker\Internal;

use Amp\ByteStream\ReadableBuffer;
use Amp\ByteStream\StreamChannel;
use Amp\ByteStream\WritableBuffer;
use Amp\Cancellation;
use Amp\CancelledException;
use Amp\DeferredFuture;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Future;
use Amp\Parallel\Context\Context;
use Amp\Parallel\Context\StatusError;
use Amp\Parallel\Worker\Execution;
use Amp\Parallel\Worker\Internal;
use Amp\Parallel\Worker\Task;
use Amp\Parallel\Worker\Worker;
use Amp\Parallel\Worker\WorkerException;
use Amp\Pipeline\Queue;
use Amp\Sync\ChannelException;
use Amp\TimeoutCancellation;
use Revolt\EventLoop;
use function Amp\async;

/**
 * Context based worker implementation executing {@see Task}s.
 *
 * @internal
 */
final class ContextWorker implements Worker
{
    use ForbidCloning;
    use ForbidSerialization;

    private const SHUTDOWN_TIMEOUT = 1;
    private const ERROR_TIMEOUT = 0.25;

    /** @var array<string, DeferredFuture> */
    private array $jobQueue = [];

    /** @var array<string, Queue> */
    private array $queues = [];

    private readonly \Closure $onReceive;

    private ?Future $exitStatus = null;

    /**
     * @param Context<int, Internal\JobPacket, Internal\JobPacket|null> $context A context running tasks.
     */
    public function __construct(private readonly Context $context)
    {
        $jobQueue = &$this->jobQueue;
        $queues = &$this->queues;
        /** @psalm-suppress UndefinedVariable $onReceive is defined here. */
        $this->onReceive = $onReceive = static function (
            ?\Throwable $exception,
            ?Internal\JobPacket $data
        ) use (
            $context,
            &$jobQueue,
            &$queues,
            &$onReceive,
        ): void {
            if (!$data) {
                $exception ??= new WorkerException("Unexpected error in worker");
                foreach ($queues as $queue) {
                    $queue->error($exception);
                }
                foreach ($jobQueue as $deferred) {
                    $deferred->error($exception);
                }
                $context->close();
                return;
            }

            $id = $data->getId();

            try {
                if (!isset($jobQueue[$id], $queues[$id])) {
                    return;
                }

                if ($data instanceof Internal\JobMessage) {
                    $queues[$id]->pushAsync($data->getMessage())->ignore();
                    return;
                }

                if (!$data instanceof Internal\TaskResult) {
                    return;
                }

                $deferred = $jobQueue[$id];
                $queue = $queues[$id];
                unset($jobQueue[$id], $queues[$id]);

                $queue->complete();

                try {
                    $deferred->complete($data->getResult());
                } catch (\Throwable $exception) {
                    $deferred->error($exception);
                }
            } finally {
                if (!empty($jobQueue)) {
                    self::receive($context, $onReceive);
                }
            }
        };
    }

    private static function receive(Context $context, callable $onReceive): void
    {
        EventLoop::queue(static function () use ($context, $onReceive): void {
            try {
                $received = $context->receive();
            } catch (\Throwable $exception) {
                $onReceive($exception, null);
                return;
            }

            $onReceive(null, $received);
        });
    }

    #[\Override]
    public function isRunning(): bool
    {
        // Report as running unless shutdown or killed.
        return $this->exitStatus === null;
    }

    #[\Override]
    public function isIdle(): bool
    {
        return empty($this->jobQueue);
    }

    #[\Override]
    public function submit(Task $task, ?Cancellation $cancellation = null): Execution
    {
        if ($this->exitStatus) {
            throw new StatusError("The worker has been shut down");
        }

        try {
            $cancellation?->throwIfRequested();
        } catch (CancelledException $exception) {
            return self::createCancelledExecution($task, $exception);
        }

        $receive = empty($this->jobQueue);
        $submission = new Internal\TaskSubmission($task);
        $jobId = $submission->getId();
        $this->jobQueue[$jobId] = $deferred = new DeferredFuture;
        $future = $deferred->getFuture();

        try {
            $this->context->send($submission);
        } catch (ChannelException $exception) {
            try {
                $exception = new WorkerException("The worker exited unexpectedly", 0, $exception);
                $this->context->join(new TimeoutCancellation(self::ERROR_TIMEOUT));
            } catch (CancelledException) {
                $this->kill();
            } catch (\Throwable $exception) {
                $exception = new WorkerException("The worker crashed", 0, $exception);
            }

            $this->exitStatus ??= Future::error($exception);

            unset($this->jobQueue[$jobId]);
            throw $exception;
        } catch (\Throwable $exception) {
            unset($this->jobQueue[$jobId]);
            throw $exception;
        }

        /** @psalm-suppress TypeDoesNotContainType https://github.com/vimeo/psalm/issues/10608 */
        if ($cancellation) {
            $context = $this->context;
            $cancellationId = $cancellation->subscribe(static fn () => async(
                $context->send(...),
                new Internal\JobCancellation($jobId),
            )->ignore());
            $future = $future->finally(static fn () => $cancellation->unsubscribe($cancellationId));
        }

        $this->queues[$jobId] = $queue = new Queue();
        $channel = new Internal\JobChannel($jobId, $this->context, $queue->iterate());

        if ($receive) {
            self::receive($this->context, $this->onReceive);
        }

        /** @psalm-suppress InvalidArgument */
        return new Execution($task, $channel, $future);
    }

    #[\Override]
    public function shutdown(): void
    {
        if ($this->exitStatus) {
            $this->exitStatus->await();
            return;
        }

        ($this->exitStatus = async(function (): void {
            if ($this->context->isClosed()) {
                throw new WorkerException("The worker had crashed prior to being shutdown");
            }

            // Wait for pending tasks to finish.
            Future\awaitAll(\array_map(static fn (DeferredFuture $deferred) => $deferred->getFuture(), $this->jobQueue));

            try {
                $this->context->send(null); // End loop in task runner.
                $this->context->join(new TimeoutCancellation(self::SHUTDOWN_TIMEOUT));
            } catch (\Throwable $exception) {
                $this->context->close();
                throw new WorkerException("Failed to gracefully shutdown worker", 0, $exception);
            }
        }))->await();
    }

    #[\Override]
    public function kill(): void
    {
        if (!$this->context->isClosed()) {
            $this->context->close();
        }

        $this->exitStatus ??= Future::error(new WorkerException("The worker was killed"));
        $this->exitStatus->ignore();
    }

    private static function createCancelledExecution(Task $task, CancelledException $exception): Execution
    {
        $channel = new StreamChannel(new ReadableBuffer(), new WritableBuffer());
        $channel->close();

        return new Execution($task, $channel, Future::error($exception));
    }
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Worker\Internal;

use Amp\Parallel\Worker\Task;

/** @internal */
final class TaskSubmission extends JobPacket
{
    private static string $nextId = 'a';

    private Task|\__PHP_Incomplete_Class $task;

    public function __construct(Task $task)
    {
        $this->task = $task;
        $id = self::$nextId;
        \PHP_VERSION_ID >= 80300 ? self::$nextId = \str_increment(self::$nextId) : ++self::$nextId;
        parent::__construct($id);
    }

    public function getTask(): Task
    {
        // Classes that cannot be autoloaded will be unserialized as an instance of __PHP_Incomplete_Class.
        if ($this->task instanceof \__PHP_Incomplete_Class) {
            throw new \Error(\sprintf(
                "Classes implementing %s must be autoloadable by the Composer autoloader",
                Task::class
            ));
        }

        return $this->task;
    }
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Worker\Internal;

use Amp\Parallel\Worker\TaskFailureError;
use Amp\Parallel\Worker\TaskFailureException;
use Amp\Parallel\Worker\TaskFailureThrowable;

/**
 * @psalm-import-type FlattenedTrace from TaskFailureThrowable
 * @internal
 */
enum TaskExceptionType
{
    case Exception;
    case Error;

    public static function fromException(\Throwable $exception): self
    {
        return $exception instanceof \Error
            ? self::Error
            : self::Exception;
    }

    /**
     * @param class-string<\Throwable> $className
     * @param FlattenedTrace $trace
     */
    public function createException(
        string $className,
        string $message,
        int|string $code,
        string $file,
        int $line,
        array $trace,
        ?\Throwable $previous = null,
    ): TaskFailureThrowable {
        return match ($this) {
            self::Exception => new TaskFailureException(
                $className,
                $message,
                $code,
                $file,
                $line,
                $trace,
                $previous,
            ),
            self::Error => new TaskFailureError(
                $className,
                $message,
                $code,
                $file,
                $line,
                $trace,
                $previous,
            ),
        };
    }
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Worker\Internal;

/** @internal */
final class JobMessage extends JobPacket
{
    public function __construct(
        string $id,
        private readonly mixed $message,
    ) {
        parent::__construct($id);
    }

    public function getMessage(): mixed
    {
        return $this->message;
    }
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Worker\Internal;

use Amp\CancelledException;
use Amp\DeferredCancellation;
use Amp\Future;
use Amp\Parallel\Worker\Internal;
use Amp\Pipeline\Queue;
use Amp\Serialization\SerializationException;
use Amp\Sync\Channel;
use Revolt\EventLoop;

return static function (Channel $channel) use ($argc, $argv): int {
    if (!\defined("AMP_WORKER")) {
        \define("AMP_WORKER", \AMP_CONTEXT);
    }

    if (isset($argv[1])) {
        if (!\is_file($argv[1])) {
            throw new \Error(\sprintf("No file found at bootstrap path given '%s'", $argv[1]));
        }

        // Include file within closure to protect scope.
        (function () use ($argc, $argv): void {
            /** @psalm-suppress UnresolvableInclude */
            require $argv[1];
        })();
    }

    /** @var array<string, DeferredCancellation> $cancellationSources */
    $cancellationSources = [];

    /** @var array<string, Queue> $queues */
    $queues = [];

    while ($data = $channel->receive()) {
        // New Task execution request.
        if ($data instanceof Internal\TaskSubmission) {
            $id = $data->getId();

            $cancellationSources[$id] = $source = new DeferredCancellation;
            $queues[$id] = $queue = new Queue();

            $jobChannel = new JobChannel($id, $channel, $queue->iterate());

            EventLoop::queue(static function () use (
                &$cancellationSources,
                &$queues,
                $data,
                $id,
                $source,
                $queue,
                $jobChannel,
                $channel,
            ): void {
                try {
                    $result = $data->getTask()->run($jobChannel, $source->getCancellation());

                    if ($result instanceof Future) {
                        $result = $result->await($source->getCancellation());
                    }

                    $result = new Internal\TaskSuccess($data->getId(), $result);
                } catch (\Throwable $exception) {
                    if ($exception instanceof CancelledException && $source->isCancelled()) {
                        $result = new Internal\TaskCancelled($id, $exception);
                    } else {
                        $result = new Internal\TaskFailure($id, $exception);
                    }
                } finally {
                    $queue->complete();
                    unset($cancellationSources[$id], $queues[$id]);
                }

                try {
                    $channel->send($result);
                } catch (SerializationException $exception) {
                    // Could not serialize task result.
                    $channel->send(new Internal\TaskFailure($id, $exception));
                }
            });
            continue;
        }

        // Channel message.
        if ($data instanceof Internal\JobMessage) {
            ($queues[$data->getId()] ?? null)?->pushAsync($data->getMessage())->ignore();
            continue;
        }

        // Cancellation signal.
        if ($data instanceof Internal\JobCancellation) {
            ($cancellationSources[$data->getId()] ?? null)?->cancel();
            continue;
        }

        // Should not happen, but just in case...
        throw new \Error('Invalid value ' . \get_debug_type($data) . ' received in ' . __FUNCTION__);
    }

    return 0;
};
<?php declare(strict_types=1);

namespace Amp\Parallel\Worker\Internal;

/** @internal */
final class JobCancellation extends JobPacket
{
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Worker\Internal;

/** @internal */
abstract class JobPacket
{
    public function __construct(
        private readonly string $id,
    ) {
    }

    /**
     * @return string Task identifier.
     */
    final public function getId(): string
    {
        return $this->id;
    }
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Worker\Internal;

use Amp\Parallel\Worker\Task;

/**
 * @internal
 *
 * @template-covariant T
 * @template-extends TaskResult<T>
 */
final class TaskSuccess extends TaskResult
{
    /**
     * @param T $result
     */
    public function __construct(
        string $id,
        private readonly mixed $result
    ) {
        parent::__construct($id);
    }

    /**
     * @return T
     */
    #[\Override]
    public function getResult(): mixed
    {
        if ($this->result instanceof \__PHP_Incomplete_Class) {
            throw new \Error(\sprintf(
                "Class instances returned from %s::run() must be autoloadable by the Composer autoloader",
                Task::class
            ));
        }

        return $this->result;
    }
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Worker\Internal;

use Amp\Cancellation;
use Amp\DeferredFuture;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Pipeline\ConcurrentIterator;
use Amp\Sync\Channel;
use Amp\Sync\ChannelException;

/**
 * @template-covariant TReceive
 * @template TSend
 * @implements Channel<TReceive, TSend>
 *
 * @internal
 */
final class JobChannel implements Channel
{
    use ForbidCloning;
    use ForbidSerialization;

    private readonly DeferredFuture $onClose;

    public function __construct(
        private readonly string $id,
        private readonly Channel $channel,
        private readonly ConcurrentIterator $iterator,
    ) {
        $this->onClose = new DeferredFuture();
    }

    public function __destruct()
    {
        $this->close();
    }

    #[\Override]
    public function send(mixed $data): void
    {
        if ($this->onClose->isComplete()) {
            throw new ChannelException('Channel has already been closed.');
        }

        $this->channel->send(new JobMessage($this->id, $data));
    }

    #[\Override]
    public function receive(?Cancellation $cancellation = null): mixed
    {
        if (!$this->iterator->continue($cancellation)) {
            $this->close();
            throw new ChannelException('Channel source closed unexpectedly');
        }

        return $this->iterator->getValue();
    }

    #[\Override]
    public function close(): void
    {
        $this->iterator->dispose();

        if (!$this->onClose->isComplete()) {
            $this->onClose->complete();
        }
    }

    #[\Override]
    public function isClosed(): bool
    {
        return $this->channel->isClosed() || $this->onClose->isComplete();
    }

    #[\Override]
    public function onClose(\Closure $onClose): void
    {
        $this->onClose->getFuture()->finally($onClose);
    }
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Worker;

use Amp\Parallel\Context\Internal;

final class TaskFailureError extends \Error implements TaskFailureThrowable
{
    use Internal\ContextException;
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Worker;

use Amp\CancelledException;

final class TaskCancelledException extends CancelledException implements TaskFailureThrowable
{
    private readonly TaskFailureThrowable $failure;

    public function __construct(TaskFailureThrowable $exception)
    {
        parent::__construct($exception);
        $this->failure = $exception;
    }

    #[\Override]
    public function getOriginalClassName(): string
    {
        return $this->failure->getOriginalClassName();
    }

    #[\Override]
    public function getOriginalMessage(): string
    {
        return $this->failure->getOriginalMessage();
    }

    #[\Override]
    public function getOriginalCode(): string|int
    {
        return $this->failure->getOriginalCode();
    }

    #[\Override]
    public function getOriginalFile(): string
    {
        return $this->failure->getOriginalFile();
    }

    #[\Override]
    public function getOriginalLine(): int
    {
        return $this->failure->getOriginalLine();
    }

    #[\Override]
    public function getOriginalTrace(): array
    {
        return $this->failure->getOriginalTrace();
    }

    #[\Override]
    public function getOriginalTraceAsString(): string
    {
        return $this->failure->getOriginalTraceAsString();
    }
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Worker;

use Amp\Parallel\Context\StatusError;

/**
 * An interface for worker pools.
 */
interface WorkerPool extends Worker
{
    /** @var int The default maximum pool size. */
    public const DEFAULT_WORKER_LIMIT = 32;

    /**
     * Gets a worker from the pool. The worker is marked as busy and will only be reused if the pool runs out of
     * idle workers. The worker will be automatically marked as idle once no references to the returned worker remain.
     *
     * This method does not guarantee the worker will be dedicated to a particular task, rather is designed if you
     * wish to send a series of tasks to a single worker. For a dedicated worker, create a new worker using a
     * {@see WorkerFactory} or {@see createWorker()}.
     *
     * @throws StatusError If the pool is not running.
     */
    public function getWorker(): Worker;

    /**
     * Gets the number of workers currently running in the pool.
     *
     * @return int The number of workers.
     */
    public function getWorkerCount(): int;

    /**
     * Gets the number of workers that are currently idle.
     *
     * @return int The number of idle workers.
     */
    public function getIdleWorkerCount(): int;
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Worker;

interface LimitedWorkerPool extends WorkerPool
{
    /**
     * Gets the maximum number of workers the pool may spawn to handle concurrent tasks.
     *
     * @return int The maximum number of workers.
     */
    public function getWorkerLimit(): int;
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Worker;

use Amp\Cancellation;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Future;
use Amp\Sync\Channel;

/**
 * @template-covariant TResult
 * @template TReceive
 * @template TSend
 */
final class Execution
{
    use ForbidCloning;
    use ForbidSerialization;

    /**
     * @param Task<TResult, TReceive, TSend> $task
     * @param Channel<TSend, TReceive> $channel
     * @param Future<TResult> $future
     */
    public function __construct(
        private readonly Task $task,
        private readonly Channel $channel,
        private readonly Future $future,
    ) {
    }

    /**
     * @return Task<TResult, TReceive, TSend>
     */
    public function getTask(): Task
    {
        return $this->task;
    }

    /**
     * Communication channel to the task. The other end of this channel is provided to {@see Task::run()}.
     *
     * @return Channel<TSend, TReceive>
     */
    public function getChannel(): Channel
    {
        return $this->channel;
    }

    /**
     * Return value from {@see Task::run()}.
     *
     * @return Future<TResult>
     */
    public function getFuture(): Future
    {
        return $this->future;
    }

    /**
     * Shortcut to calling {@see self::getFuture()::await()}. Cancellation only cancels awaiting the result, it does
     * not cancel the task. Use the cancellation passed to {@see Worker::submit()} to cancel the task.
     *
     * @return TResult
     */
    public function await(?Cancellation $cancellation = null): mixed
    {
        return $this->future->await($cancellation);
    }
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Worker;

use Amp\Cancellation;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Parallel\Context\ContextFactory;
use Amp\Parallel\Worker\Internal\ContextWorker;
use function Amp\Parallel\Context\contextFactory;

/**
 * The built-in worker factory type.
 */
final class ContextWorkerFactory implements WorkerFactory
{
    use ForbidCloning;
    use ForbidSerialization;

    public const SCRIPT_PATH = __DIR__ . "/Internal/task-runner.php";

    public function __construct(
        private readonly ?string $bootstrapPath = null,
        private readonly ?ContextFactory $contextFactory = null,
    ) {
        if ($this->bootstrapPath !== null && !\file_exists($this->bootstrapPath)) {
            throw new \Error(\sprintf("No file found at bootstrap path given '%s'", $this->bootstrapPath));
        }
    }

    /**
     * The type of worker created depends on the extensions available. If multi-threading is enabled, a WorkerThread
     * will be created. If threads are not available a WorkerProcess will be created.
     */
    #[\Override]
    public function create(?Cancellation $cancellation = null): Worker
    {
        $script = [self::SCRIPT_PATH];

        if ($this->bootstrapPath !== null) {
            $script[] = $this->bootstrapPath;
        }

        return new ContextWorker(($this->contextFactory ?? contextFactory())->start($script, $cancellation));
    }
}
<?php declare(strict_types=1);

namespace Amp\Parallel\Worker;

use Amp\Cancellation;
use Revolt\EventLoop;

/**
 * Gets or sets the global worker pool.
 *
 * @param WorkerPool|null $pool A worker pool instance.
 *
 * @return WorkerPool The global worker pool instance.
 */
function workerPool(?WorkerPool $pool = null): WorkerPool
{
    static $map;
    $map ??= new \WeakMap();
    $driver = EventLoop::getDriver();

    if ($pool) {
        return $map[$driver] = $pool;
    }

    return $map[$driver] ??= new ContextWorkerPool();
}

/**
 * @template TResult
 * @template TReceive
 * @template TSend
 *
 * Executes a {@see Task} on the global worker pool.
 *
 * @param Task<TResult, TReceive, TSend> $task The task to execute.
 * @param Cancellation|null $cancellation Token to request cancellation. The task must support cancellation for
 * this to have any effect.
 *
 * @return Execution<TResult, TReceive, TSend>
 */
function submit(Task $task, ?Cancellation $cancellation = null): Execution
{
    return workerPool()->submit($task, $cancellation);
}

/**
 * Gets an available worker from the global worker pool.
 */
function getWorker(): Worker
{
    return workerPool()->getWorker();
}

/**
 * Creates a worker using the global worker factory.
 */
function createWorker(): Worker
{
    return workerFactory()->create();
}

/**
 * Gets or sets the global worker factory.
 */
function workerFactory(?WorkerFactory $factory = null): WorkerFactory
{
    static $map;
    $map ??= new \WeakMap();
    $driver = EventLoop::getDriver();

    if ($factory) {
        return $map[$driver] = $factory;
    }

    return $map[$driver] ??= new ContextWorkerFactory();
}
{
    "name": "amphp/parallel",
    "description": "Parallel processing component for Amp.",
    "keywords": [
        "asynchronous",
        "async",
        "concurrent",
        "multi-threading",
        "multi-processing"
    ],
    "homepage": "https://github.com/amphp/parallel",
    "license": "MIT",
    "authors": [
        {
            "name": "Aaron Piotrowski",
            "email": "aaron@trowski.com"
        },
        {
            "name": "Niklas Keller",
            "email": "me@kelunik.com"
        },
        {
            "name": "Stephen Coakley",
            "email": "me@stephencoakley.com"
        }
    ],
    "require": {
        "php": ">=8.1",
        "amphp/amp": "^3",
        "amphp/byte-stream": "^2",
        "amphp/cache": "^2",
        "amphp/parser": "^1",
        "amphp/pipeline": "^1",
        "amphp/process": "^2",
        "amphp/serialization": "^1",
        "amphp/socket": "^2",
        "amphp/sync": "^2",
        "revolt/event-loop": "^1"
    },
    "require-dev": {
        "phpunit/phpunit": "^9",
        "amphp/phpunit-util": "^3",
        "amphp/php-cs-fixer-config": "^2",
        "psalm/phar": "6.16.1"
    },
    "autoload": {
        "psr-4": {
            "Amp\\Parallel\\": "src"
        },
        "files": [
            "src/Context/functions.php",
            "src/Context/Internal/functions.php",
            "src/Ipc/functions.php",
            "src/Worker/functions.php"
        ]
    },
    "autoload-dev": {
        "psr-4": {
            "App\\Worker\\": "examples/worker",
            "Amp\\Parallel\\Test\\": "test"
        }
    },
    "scripts": {
        "check": [
            "@cs",
            "@test"
        ],
        "cs": "php-cs-fixer fix -v --diff --dry-run",
        "cs-fix": "php-cs-fixer fix -v --diff",
        "test": "@php -dzend.assertions=1 -dassert.exception=1 ./vendor/bin/phpunit --coverage-text"
    }
}
{
    "symbol-whitelist": [
        "null",
        "true",
        "false",
        "static",
        "self",
        "parent",
        "array",
        "string",
        "int",
        "float",
        "bool",
        "iterable",
        "callable",
        "mixed",
        "void",
        "object",
        "websocketConnector"
    ],
    "php-core-extensions": [
        "Core",
        "date",
        "pcre",
        "Phar",
        "Reflection",
        "SPL",
        "standard",
        "hash"
    ]
}
<?php

use Amp\ByteStream\StreamException;
use Amp\Websocket\Client\Rfc6455ConnectionFactory;
use Amp\Websocket\Client\Rfc6455Connector;
use Amp\Websocket\Client\WebsocketHandshake;
use Amp\Websocket\Parser\Rfc6455ParserFactory;
use Amp\Websocket\WebsocketClosedException;

require __DIR__ . '/../vendor/autoload.php';

const AGENT = 'amphp/websocket';

$errors = 0;

$connector = new Rfc6455Connector(new Rfc6455ConnectionFactory(
    parserFactory: new Rfc6455ParserFactory(messageSizeLimit: \PHP_INT_MAX, frameSizeLimit: \PHP_INT_MAX),
));

$connection = $connector->connect(new WebsocketHandshake('ws://127.0.0.1:9001/getCaseCount'));
$message = $connection->receive();
$cases = (int) $message->buffer();

echo "Going to run {$cases} test cases." . PHP_EOL;

for ($i = 1; $i < $cases; $i++) {
    $handshake = new WebsocketHandshake('ws://127.0.0.1:9001/getCaseInfo?case=' . $i . '&agent=' . AGENT);
    $connection =  $connector->connect($handshake);
    $message = $connection->receive();
    $info = \json_decode($message->buffer(), true);

    print $info['id'] . ' ' . \str_repeat('-', 80 - \strlen($info['id']) - 1) . PHP_EOL;
    print \wordwrap($info['description'], 80, PHP_EOL) . ' ';

    $handshake = new WebsocketHandshake('ws://127.0.0.1:9001/runCase?case=' . $i . '&agent=' . AGENT);
    $connection = $connector->connect($handshake);

    try {
        while ($message = $connection->receive()) {
            $content = $message->buffer();

            if ($message->isBinary()) {
                $connection->sendBinary($content);
            } else {
                $connection->sendText($content);
            }
        }
    } catch (WebsocketClosedException $e) {
        // ignore
    } catch (AssertionError $e) {
        print 'Assertion error: ' . $e->getMessage() . PHP_EOL;
        $connection->close();
    } catch (Error $e) {
        print 'Error: ' . $e->getMessage() . PHP_EOL;
        $connection->close();
    } catch (StreamException $e) {
        print 'Stream exception: ' . $e->getMessage() . PHP_EOL;
        $connection->close();
    }

    $handshake = new WebsocketHandshake('ws://127.0.0.1:9001/getCaseStatus?case=' . $i . '&agent=' . AGENT);
    $connection =  $connector->connect($handshake);
    $message = $connection->receive();
    print($result = \json_decode($message->buffer(), true)['behavior']);

    if ($result === 'FAILED') {
        $errors++;
    }

    print PHP_EOL . PHP_EOL;
}

$connection = $connector->connect(new WebsocketHandshake('ws://127.0.0.1:9001/updateReports?agent=' . AGENT));
$connection->close();

if ($errors) {
    exit(1);
}
{
    "url": "ws://127.0.0.1:9001",
    "outdir": "./reports/clients",
    "cases": [
        "*"
    ],
    "exclude-cases": [
        "12.2.*",
        "12.3.*",
        "12.4.*",
        "12.5.*",
        "13.2.*",
        "13.3.*",
        "13.4.*",
        "13.5.*",
        "13.6.*",
        "13.7.*"
    ]
}
<?php declare(strict_types=1);

namespace Amp\Websocket\Client;

use Amp\Http\Client\HttpException;
use Amp\Http\Client\Response;

final class WebsocketConnectException extends HttpException
{
    public function __construct(
        string $message,
        private readonly Response $response,
        ?\Throwable $previous = null,
    ) {
        parent::__construct($message, 0, $previous);
    }

    public function getResponse(): Response
    {
        return $this->response;
    }
}
<?php declare(strict_types=1);

namespace Amp\Websocket\Client;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Http\Client\Response;
use Amp\Socket\Socket;
use Amp\Websocket\Compression\WebsocketCompressionContext;
use Amp\Websocket\Parser\Rfc6455ParserFactory;
use Amp\Websocket\Parser\WebsocketParserFactory;
use Amp\Websocket\Rfc6455Client;
use Amp\Websocket\WebsocketHeartbeatQueue;
use Amp\Websocket\WebsocketRateLimit;

final class Rfc6455ConnectionFactory implements WebsocketConnectionFactory
{
    use ForbidCloning;
    use ForbidSerialization;

    public function __construct(
        private readonly ?WebsocketHeartbeatQueue $heartbeatQueue = null,
        private readonly ?WebsocketRateLimit $rateLimit = null,
        private readonly WebsocketParserFactory $parserFactory = new Rfc6455ParserFactory(
            messageSizeLimit: Rfc6455Connection::DEFAULT_MESSAGE_SIZE_LIMIT,
            frameSizeLimit: Rfc6455Connection::DEFAULT_FRAME_SIZE_LIMIT,
        ),
        private readonly int $frameSplitThreshold = Rfc6455Client::DEFAULT_FRAME_SPLIT_THRESHOLD,
        private readonly float $closePeriod = Rfc6455Client::DEFAULT_CLOSE_PERIOD,
    ) {
    }

    public function createConnection(
        Response $handshakeResponse,
        Socket $socket,
        ?WebsocketCompressionContext $compressionContext = null,
    ): WebsocketConnection {
        $client = new Rfc6455Client(
            socket: $socket,
            masked: true,
            parserFactory: $this->parserFactory,
            compressionContext: $compressionContext,
            heartbeatQueue: $this->heartbeatQueue,
            rateLimit: $this->rateLimit,
            frameSplitThreshold: $this->frameSplitThreshold,
            closePeriod: $this->closePeriod,
        );

        return new Rfc6455Connection($client, $handshakeResponse);
    }
}
<?php declare(strict_types=1);

namespace Amp\Websocket\Client;

use Amp\Http\Client\Response;
use Amp\Websocket\WebsocketClient;

interface WebsocketConnection extends WebsocketClient
{
    /**
     * @return Response Server response originating the client connection.
     */
    public function getHandshakeResponse(): Response;
}
<?php declare(strict_types=1);

namespace Amp\Websocket\Client;

use Amp\Cancellation;
use Amp\Http\Client\HttpException;

interface WebsocketConnector
{
    /**
     * @throws HttpException Thrown if the request fails.
     * @throws WebsocketConnectException If the response received is invalid or is not a switching protocols (101) response.
     */
    public function connect(WebsocketHandshake $handshake, ?Cancellation $cancellation = null): WebsocketConnection;
}
<?php declare(strict_types=1);

namespace Amp\Websocket\Client;

use Amp\ByteStream\ReadableStream;
use Amp\Cancellation;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Http\Client\Response;
use Amp\Socket\SocketAddress;
use Amp\Socket\TlsInfo;
use Amp\Websocket\Rfc6455Client;
use Amp\Websocket\WebsocketCloseCode;
use Amp\Websocket\WebsocketCloseInfo;
use Amp\Websocket\WebsocketCount;
use Amp\Websocket\WebsocketMessage;
use Amp\Websocket\WebsocketTimestamp;
use Traversable;

/**
 * @implements  \IteratorAggregate<int, WebsocketMessage>
 */
final class Rfc6455Connection implements WebsocketConnection, \IteratorAggregate
{
    use ForbidCloning;
    use ForbidSerialization;

    public const DEFAULT_MESSAGE_SIZE_LIMIT = (2 ** 20) * 10; // 10MB
    public const DEFAULT_FRAME_SIZE_LIMIT = (2 ** 20) * 10; // 10MB

    public function __construct(
        private readonly Rfc6455Client $client,
        private readonly Response $handshakeResponse,
    ) {
    }

    public function getHandshakeResponse(): Response
    {
        return $this->handshakeResponse;
    }

    public function receive(?Cancellation $cancellation = null): ?WebsocketMessage
    {
        return $this->client->receive($cancellation);
    }

    public function getId(): int
    {
        return $this->client->getId();
    }

    public function getLocalAddress(): SocketAddress
    {
        return $this->client->getLocalAddress();
    }

    public function getRemoteAddress(): SocketAddress
    {
        return $this->client->getRemoteAddress();
    }

    public function getTlsInfo(): ?TlsInfo
    {
        return $this->client->getTlsInfo();
    }

    public function getCloseInfo(): WebsocketCloseInfo
    {
        return $this->client->getCloseInfo();
    }

    public function sendText(string $data): void
    {
        $this->client->sendText($data);
    }

    public function sendBinary(string $data): void
    {
        $this->client->sendBinary($data);
    }

    public function streamText(ReadableStream $stream): void
    {
        $this->client->streamText($stream);
    }

    public function streamBinary(ReadableStream $stream): void
    {
        $this->client->streamBinary($stream);
    }

    public function ping(): void
    {
        $this->client->ping();
    }

    public function getCount(WebsocketCount $type): int
    {
        return $this->client->getCount($type);
    }

    public function getTimestamp(WebsocketTimestamp $type): float
    {
        return $this->client->getTimestamp($type);
    }

    public function isClosed(): bool
    {
        return $this->client->isClosed();
    }

    public function close(int $code = WebsocketCloseCode::NORMAL_CLOSE, string $reason = ''): void
    {
        $this->client->close($code, $reason);
    }

    public function onClose(\Closure $onClose): void
    {
        $this->client->onClose($onClose);
    }

    public function isCompressionEnabled(): bool
    {
        return $this->client->isCompressionEnabled();
    }

    public function getIterator(): Traversable
    {
        yield from $this->client;
    }
}
<?php declare(strict_types=1);

namespace Amp\Websocket\Client;

use Amp\ForbidSerialization;
use Amp\Http\Client\Request;
use Amp\Http\HttpMessage;
use Amp\Http\HttpRequest;
use League\Uri;
use Psr\Http\Message\UriInterface as PsrUri;

/**
 * @psalm-import-type HeaderParamArrayType from HttpMessage
 * @psalm-import-type HeaderParamValueType from HttpMessage
 * @psalm-import-type QueryArrayType from HttpRequest
 * @psalm-import-type QueryValueType from HttpRequest
 */
final class WebsocketHandshake extends HttpRequest
{
    use ForbidSerialization;

    private float $tcpConnectTimeout = 10;

    private float $tlsHandshakeTimeout = 10;

    private int $headerSizeLimit = Request::DEFAULT_HEADER_SIZE_LIMIT;

    /**
     * @param PsrUri|string $uri Target address of websocket (e.g. ws://foo.bar/bar or
     * wss://crypto.example/?secureConnection) or a PsrUri instance.
     * @param HeaderParamArrayType $headers
     */
    public function __construct(PsrUri|string $uri, array $headers = [])
    {
        parent::__construct('GET', self::makeUri($uri));

        $this->setHeaders($headers);
    }

    /**
     * @return self Cloned object
     */
    public function withUri(PsrUri|string $uri): self
    {
        $clone = clone $this;
        $clone->setUri(self::makeUri($uri));

        return $clone;
    }

    /**
     * @return float Timeout in seconds for the TCP connection.
     */
    public function getTcpConnectTimeout(): float
    {
        return $this->tcpConnectTimeout;
    }

    public function withTcpConnectTimeout(float $tcpConnectTimeout): self
    {
        $clone = clone $this;
        $clone->tcpConnectTimeout = $tcpConnectTimeout;

        return $clone;
    }

    /**
     * @return float Timeout in seconds for the TLS handshake.
     */
    public function getTlsHandshakeTimeout(): float
    {
        return $this->tlsHandshakeTimeout;
    }

    public function withTlsHandshakeTimeout(float $tlsHandshakeTimeout): self
    {
        $clone = clone $this;
        $clone->tlsHandshakeTimeout = $tlsHandshakeTimeout;

        return $clone;
    }

    public function getHeaderSizeLimit(): int
    {
        return $this->headerSizeLimit;
    }

    public function withHeaderSizeLimit(int $headerSizeLimit): self
    {
        $clone = clone $this;
        $clone->headerSizeLimit = $headerSizeLimit;

        return $clone;
    }

    /**
     * Replaces all headers in the returned instance.
     *
     * @param HeaderParamArrayType $headers
     *
     * @return self Cloned object.
     */
    public function withHeaders(array $headers): self
    {
        $clone = clone $this;
        $clone->setHeaders($headers);

        return $clone;
    }

    /**
     * Replaces the given header in the returned instance.
     *
     * @param non-empty-string $name
     * @param HeaderParamValueType $value
     *
     * @return self Cloned object.
     */
    public function withHeader(string $name, string|array $value): self
    {
        $clone = clone $this;
        $clone->setHeader($name, $value);

        return $clone;
    }

    /**
     * Adds the given header in the returned instance.
     *
     * @param non-empty-string $name
     * @param HeaderParamValueType $value
     *
     * @return self Cloned object.
     */
    public function withAddedHeader(string $name, string|array $value): self
    {
        $clone = clone $this;
        $clone->addHeader($name, $value);

        return $clone;
    }

    /**
     * Removes the given header in the returned instance.
     *
     * @return self Cloned object.
     */
    public function withoutHeader(string $name): self
    {
        $clone = clone $this;
        $clone->removeHeader($name);

        return $clone;
    }

    protected function setHeader(string $name, array|string $value): void
    {
        if (($name[0] ?? ':') === ':') {
            throw new \Error("Header name cannot be empty or start with a colon (:)");
        }

        parent::setHeader($name, $value);
    }

    protected function addHeader(string $name, array|string $value): void
    {
        if (($name[0] ?? ':') === ':') {
            throw new \Error("Header name cannot be empty or start with a colon (:)");
        }

        parent::addHeader($name, $value);
    }

    /**
     * @param QueryArrayType $parameters
     *
     * @return self Cloned object.
     */
    public function withQueryParameters(array $parameters): self
    {
        $clone = clone $this;
        $clone->setQueryParameters($parameters);

        return $clone;
    }

    /**
     * @param QueryValueType $value
     *
     * @return self Cloned object.
     */
    public function withQueryParameter(string $key, array|string|null $value): self
    {
        $clone = clone $this;
        $clone->setQueryParameter($key, $value);

        return $clone;
    }

    /**
     * @param QueryValueType $value
     *
     * @return self Cloned object.
     */
    public function withAddedQueryParameter(string $key, array|string|null $value): self
    {
        $clone = clone $this;
        $clone->addQueryParameter($key, $value);

        return $clone;
    }

    /**
     * @return self Cloned object.
     */
    public function withoutQueryParameter(string $key): self
    {
        $clone = clone $this;
        $clone->removeQueryParameter($key);

        return $clone;
    }

    /**
     * @return self Cloned object.
     */
    public function withoutQuery(): self
    {
        $clone = clone $this;
        $clone->removeQuery();

        return $clone;
    }

    private static function makeUri(PsrUri|string $uri): PsrUri
    {
        if (\is_string($uri)) {
            try {
                /** @psalm-suppress DeprecatedMethod Using deprecated method to support 6.x and 7.x of league/uri */
                $uri = Uri\Http::new($uri);
            } catch (\Exception $exception) {
                throw new \ValueError('Invalid Websocket URI provided', 0, $exception);
            }
        }

        return match ($uri->getScheme()) {
            'ws', 'wss' => $uri,
            default => throw new \ValueError('The URI scheme must be ws or wss, got "' . $uri->getScheme() . '"'),
        };
    }
}
<?php declare(strict_types=1);

namespace Amp\Websocket\Client;

use Amp\Http\Client\Response;
use Amp\Socket\Socket;
use Amp\Websocket\Compression\WebsocketCompressionContext;

interface WebsocketConnectionFactory
{
    /**
     * @param Response $handshakeResponse Response that initiated the websocket connection.
     * @param Socket $socket Underlying socket to be used for network communication.
     * @param WebsocketCompressionContext|null $compressionContext CompressionContext generated from the response headers.
     */
    public function createConnection(
        Response $handshakeResponse,
        Socket $socket,
        ?WebsocketCompressionContext $compressionContext = null,
    ): WebsocketConnection;
}
<?php declare(strict_types=1);

namespace Amp\Websocket\Client;

use Amp\Cancellation;
use Amp\DeferredFuture;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Http;
use Amp\Http\Client\Connection\DefaultConnectionFactory;
use Amp\Http\Client\Connection\UnlimitedConnectionPool;
use Amp\Http\Client\HttpClient;
use Amp\Http\Client\HttpClientBuilder;
use Amp\Http\Client\Request;
use Amp\Http\Client\Response;
use Amp\Socket\ConnectContext;
use Amp\Socket\Socket;
use Amp\Websocket;
use Amp\Websocket\Compression\Rfc7692CompressionFactory;
use Amp\Websocket\Compression\WebsocketCompressionContextFactory;

final class Rfc6455Connector implements WebsocketConnector
{
    use ForbidCloning;
    use ForbidSerialization;

    private readonly HttpClient $httpClient;

    /**
     * @param WebsocketCompressionContextFactory|null $compressionContextFactory Use null to disable compression.
     */
    public function __construct(
        private readonly WebsocketConnectionFactory $connectionFactory = new Rfc6455ConnectionFactory(),
        ?HttpClient $httpClient = null,
        private readonly ?WebsocketCompressionContextFactory $compressionContextFactory = new Rfc7692CompressionFactory(),
    ) {
        $this->httpClient = $httpClient
            ?? (new HttpClientBuilder)->usingPool(
                new UnlimitedConnectionPool(
                    new DefaultConnectionFactory(connectContext: (new ConnectContext)->withTcpNoDelay())
                )
            )->build();
    }

    public function connect(WebsocketHandshake $handshake, ?Cancellation $cancellation = null): WebsocketConnection
    {
        $key = Websocket\generateKey();
        $request = $this->generateRequest($handshake, $key);

        $deferred = new DeferredFuture();
        $connectionFactory = $this->connectionFactory;
        $compressionContextFactory = $this->compressionContextFactory;

        $request->setUpgradeHandler(static function (
            Socket $socket,
            Request $request,
            Response $response,
        ) use (
            $connectionFactory,
            $compressionContextFactory,
            $deferred,
            $key,
        ): void {
            if (\strtolower($response->getHeader('upgrade') ?? '') !== 'websocket') {
                $deferred->error(new WebsocketConnectException('Upgrade header does not equal "websocket"', $response));
                return;
            }

            if (!Websocket\validateAcceptForKey($response->getHeader('sec-websocket-accept') ?? '', $key)) {
                $deferred->error(new WebsocketConnectException('Invalid Sec-WebSocket-Accept header', $response));
                return;
            }

            $extensions = Http\splitHeader($response, 'sec-websocket-extensions') ?? [];

            foreach ($extensions as $extension) {
                if ($compressionContext = $compressionContextFactory?->fromServerHeader($extension)) {
                    break;
                }
            }

            $deferred->complete(
                $connectionFactory->createConnection($response, $socket, $compressionContext ?? null)
            );
        });

        $response = $this->httpClient->request($request, $cancellation);

        if ($response->getStatus() !== Http\HttpStatus::SWITCHING_PROTOCOLS) {
            throw new WebsocketConnectException(\sprintf(
                'A %s (%d) response was not received; instead received response status: %s (%d)',
                Http\HttpStatus::getReason(Http\HttpStatus::SWITCHING_PROTOCOLS),
                Http\HttpStatus::SWITCHING_PROTOCOLS,
                $response->getReason(),
                $response->getStatus()
            ), $response);
        }

        return $deferred->getFuture()->await();
    }

    private function generateRequest(WebsocketHandshake $handshake, string $key): Request
    {
        $uri = $handshake->getUri();
        $uri = $uri->withScheme($uri->getScheme() === 'wss' ? 'https' : 'http');

        $request = new Request($uri, 'GET');
        $request->setHeaders($handshake->getHeaders());

        $request->setTcpConnectTimeout($handshake->getTcpConnectTimeout());
        $request->setTlsHandshakeTimeout($handshake->getTlsHandshakeTimeout());
        $request->setHeaderSizeLimit($handshake->getHeaderSizeLimit());

        $extensions = Http\splitHeader($request, 'sec-websocket-extensions') ?? [];

        if ($this->compressionContextFactory && \extension_loaded('zlib')) {
            $extensions[] = $this->compressionContextFactory->createRequestHeader();
        }

        if ($extensions) {
            $request->setHeader('sec-websocket-extensions', \implode(', ', $extensions));
        }

        $request->setProtocolVersions(['1.1']);
        $request->setHeader('connection', 'Upgrade');
        $request->setHeader('upgrade', 'websocket');
        $request->setHeader('sec-websocket-version', '13');
        $request->setHeader('sec-websocket-key', $key);

        return $request;
    }
}
<?php declare(strict_types=1);

namespace Amp\Websocket\Client;

use Amp\Cancellation;
use Amp\Http\Client\HttpException;
use Psr\Http\Message\UriInterface as PsrUri;
use Revolt\EventLoop;

/**
 * Set or access the global websocket Connector instance.
 */
function websocketConnector(?WebsocketConnector $connector = null): WebsocketConnector
{
    static $map;
    $map ??= new \WeakMap();
    $driver = EventLoop::getDriver();

    if ($connector) {
        return $map[$driver] = $connector;
    }

    return $map[$driver] ??= new Rfc6455Connector();
}

/**
 * @throws WebsocketConnectException If the response received is invalid or is not a switching protocols (101) response.
 * @throws HttpException Thrown if the request fails.
 */
function connect(
    WebsocketHandshake|PsrUri|string $handshake,
    ?Cancellation $cancellation = null,
): WebsocketConnection {
    if (!$handshake instanceof WebsocketHandshake) {
        $handshake = new WebsocketHandshake($handshake);
    }

    return websocketConnector()->connect($handshake, $cancellation);
}
{
    "name": "amphp/websocket-client",
    "description": "Async WebSocket client for PHP based on Amp.",
    "license": "MIT",
    "authors": [
        {
            "name": "Bob Weinand",
            "email": "bobwei9@hotmail.com"
        },
        {
            "name": "Aaron Piotrowski",
            "email": "aaron@trowski.com"
        },
        {
            "name": "Niklas Keller",
            "email": "me@kelunik.com"
        }
    ],
    "support": {
        "issues": "https://github.com/amphp/websocket-client/issues"
    },
    "keywords": [
        "async",
        "non-blocking",
        "websocket",
        "client",
        "http",
        "amp",
        "amphp"
    ],
    "require": {
        "php": ">=8.1",
        "amphp/amp": "^3",
        "amphp/byte-stream": "^2.1",
        "amphp/http": "^2.1",
        "amphp/http-client": "^5",
        "amphp/socket": "^2.2",
        "amphp/websocket": "^2",
        "league/uri": "^7.1",
        "psr/http-message": "^1|^2",
        "revolt/event-loop": "^1"
    },
    "require-dev": {
        "amphp/http-server": "^3",
        "amphp/websocket-server": "^3|^4",
        "amphp/phpunit-util": "^3",
        "amphp/php-cs-fixer-config": "^2",
        "phpunit/phpunit": "^9",
        "psr/log": "^1",
        "psalm/phar": "~5.26.1"
    },
    "autoload": {
        "psr-4": {
            "Amp\\Websocket\\Client\\": "src"
        },
        "files": [
            "src/functions.php"
        ]
    },
    "autoload-dev": {
        "psr-4": {
            "Amp\\Websocket\\Client\\": "test"
        }
    }
}
<?php

$config = new Amp\CodeStyle\Config;

$config->getFinder()
    ->in(__DIR__ . '/examples')
    ->in(__DIR__ . '/src')
    ->in(__DIR__ . '/test');

$config->setCacheFile(__DIR__ . '/.php_cs.cache');

return $config;
{
    "symbol-whitelist": [
        "null",
        "true",
        "false",
        "static",
        "self",
        "parent",
        "array",
        "string",
        "int",
        "float",
        "bool",
        "iterable",
        "callable",
        "mixed",
        "void",
        "object",
        "socketConnector",
        "hasTlsAlpnSupport",
        "hasTlsSecurityLevelSupport",
        "fromResourceLocal",
        "fromResourcePeer",
        "fromString",
        "SOCKET_EAGAIN",
        "SOCKET_ECONNREFUSED",
        "SOCKET_ETIMEDOUT",
        "Amp\\Socket\\Internal\\CONNECTION_BUSY",
        "Amp\\Socket\\Internal\\CONNECTION_REFUSED",
        "Amp\\Socket\\Internal\\CONNECTION_TIMEOUT"
    ],
    "php-core-extensions": [
        "Core",
        "date",
        "pcre",
        "Phar",
        "Reflection",
        "SPL",
        "standard",
        "hash"
    ]
}
<?php declare(strict_types=1);

namespace Amp\Socket;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;

final class ResourceServerSocketFactory implements ServerSocketFactory
{
    use ForbidCloning;
    use ForbidSerialization;

    /**
     * @param positive-int|null $chunkSize
     */
    public function __construct(private readonly ?int $chunkSize = null)
    {
    }

    /**
     * @throws SocketException
     */
    #[\Override]
    public function listen(SocketAddress|string $address, ?BindContext $bindContext = null): ResourceServerSocket
    {
        $bindContext ??= new BindContext;

        if (\is_string($address)) {
            [$scheme, $host, $port] = Internal\parseUri($address);

            $address = match ($scheme) {
                'tcp' => new InternetAddress($host, $port),
                'unix' => new UnixAddress('/' . $host),
                default => throw new \ValueError('Invalid address: only tcp and unix schemes accepted; got ' . $address),
            };
        }

        $uri = match ($address->getType()) {
            SocketAddressType::Internet => 'tcp://' . $address->toString(),
            SocketAddressType::Unix => 'unix://' . $address->toString(),
        };

        $streamContext = \stream_context_create($bindContext->toStreamContextArray());

        \set_error_handler(static fn () => true); // Error checked after call to stream_socket_server().

        try {
            $server = \stream_socket_server($uri, $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $streamContext);
        } finally {
            \restore_error_handler();
        }

        if (!$server || $errno) {
            throw new SocketException(\sprintf(
                'Could not create server %s: [Error: #%d] %s',
                $uri,
                $errno,
                $errstr
            ), $errno);
        }

        return new ResourceServerSocket($server, $bindContext, $this->chunkSize ?? ResourceSocket::DEFAULT_CHUNK_SIZE);
    }
}
<?php declare(strict_types=1);

namespace Amp\Socket;

use Amp\ByteStream\StreamException;

class SocketException extends StreamException
{
}
<?php declare(strict_types=1);

namespace Amp\Socket;

use Amp\Cancellation;
use Amp\Closable;

interface ServerSocket extends Closable
{
    /**
     * @throws PendingAcceptError If another accept request is pending.
     */
    public function accept(?Cancellation $cancellation = null): ?Socket;

    public function getAddress(): SocketAddress;

    public function getBindContext(): BindContext;
}
<?php declare(strict_types=1);

namespace Amp\Socket;

/**
 * Thrown if TLS can't be properly negotiated or is not supported on the given socket.
 *
 * @psalm-suppress ClassMustBeFinal
 */
class TlsException extends SocketException
{
}
<?php declare(strict_types=1);

namespace Amp\Socket;

/**
 * @see ServerTlsContext::withDefaultCertificate()
 * @see ServerTlsContext::withCertificates()
 */
final class Certificate
{
    private readonly string $certFile;
    private readonly string $keyFile;
    private readonly ?string $passphase;

    /**
     * @param string      $certFile Certificate file with the certificate + intermediaries.
     * @param string|null $keyFile Key file with the corresponding private key or `null` if the key is in $certFile.
     */
    public function __construct(string $certFile, ?string $keyFile = null, ?string $passphrase = null)
    {
        $this->certFile = $certFile;
        $this->keyFile = $keyFile ?? $certFile;
        $this->passphase = $passphrase;
    }

    public function getCertFile(): string
    {
        return $this->certFile;
    }

    public function getKeyFile(): string
    {
        return $this->keyFile;
    }

    public function getPassphrase(): ?string
    {
        return $this->passphase;
    }
}
<?php declare(strict_types=1);

namespace Amp\Socket;

use Amp\Cancellation;
use Amp\CancelledException;

interface SocketConnector
{
    /**
     * Establish a socket connection to the specified URI.
     *
     * @param SocketAddress|string $uri URI in scheme://host:port format. TCP is assumed if no scheme is present.
     * @param ConnectContext|null $context Socket connect context to use when connecting.
     *
     * @throws ConnectException
     * @throws CancelledException
     */
    public function connect(
        SocketAddress|string $uri,
        ?ConnectContext $context = null,
        ?Cancellation $cancellation = null
    ): Socket;
}
<?php declare(strict_types=1);

namespace Amp\Socket;

use Amp\ByteStream\ClosedException;
use Amp\ByteStream\ReadableResourceStream;
use Amp\ByteStream\ReadableStreamIteratorAggregate;
use Amp\ByteStream\ResourceStream;
use Amp\ByteStream\WritableResourceStream;
use Amp\Cancellation;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;

/**
 * @implements \IteratorAggregate<int, string>
 */
final class ResourceSocket implements Socket, ResourceStream, \IteratorAggregate
{
    use ForbidCloning;
    use ForbidSerialization;
    use ReadableStreamIteratorAggregate;

    public const DEFAULT_CHUNK_SIZE = ReadableResourceStream::DEFAULT_CHUNK_SIZE;

    /**
     * @param resource $resource Stream resource.
     * @param positive-int $chunkSize Read and write chunk size.
     */
    public static function fromServerSocket($resource, int $chunkSize = self::DEFAULT_CHUNK_SIZE): self
    {
        return new self($resource, null, $chunkSize);
    }

    /**
     * @param resource $resource Stream resource.
     * @param positive-int $chunkSize Read and write chunk size.
     */
    public static function fromClientSocket(
        $resource,
        ?ClientTlsContext $tlsContext = null,
        int $chunkSize = self::DEFAULT_CHUNK_SIZE
    ): self {
        return new self($resource, $tlsContext, $chunkSize);
    }

    private TlsState $tlsState = TlsState::Disabled;

    private ?array $streamContext = null;

    private readonly ReadableResourceStream $reader;

    private readonly WritableResourceStream $writer;

    private readonly SocketAddress $localAddress;

    private readonly SocketAddress $remoteAddress;

    private ?TlsInfo $tlsInfo = null;

    /**
     * @param resource $resource Stream resource.
     * @param positive-int $chunkSize Read and write chunk size.
     */
    private function __construct(
        $resource,
        private readonly ?ClientTlsContext $tlsContext = null,
        int $chunkSize = self::DEFAULT_CHUNK_SIZE,
    ) {
        $this->reader = new ReadableResourceStream($resource, $chunkSize);
        $this->writer = new WritableResourceStream($resource, $chunkSize);
        $this->remoteAddress = SocketAddress\fromResourcePeer($resource);
        $this->localAddress = SocketAddress\fromResourceLocal($resource);
    }

    #[\Override]
    public function setupTls(?Cancellation $cancellation = null): void
    {
        $resource = $this->getResource();
        if ($resource === null) {
            throw new ClosedException("Can't setup TLS, because the socket has already been closed");
        }

        $context = $this->getStreamContext();

        if (empty($context['ssl'])) {
            throw new TlsException(
                "Can't enable TLS without configuration. If you used Amp\\Socket\\listen(), " .
                "be sure to pass a ServerTlsContext within the BindContext in the second argument, " .
                "otherwise set the 'ssl' context option to the PHP stream resource."
            );
        }

        $this->tlsState = TlsState::SetupPending;

        try {
            /** @psalm-suppress PossiblyInvalidArgument */
            Internal\setupTls($resource, $context, $cancellation);

            $this->tlsState = TlsState::Enabled;
        } catch (\Throwable $exception) {
            $this->close();
            $this->tlsState = TlsState::Disabled;
            throw $exception;
        }
    }

    #[\Override]
    public function shutdownTls(?Cancellation $cancellation = null): void
    {
        if (($resource = $this->reader->getResource()) === null) {
            throw new ClosedException("Can't shutdown TLS, because the socket has already been closed");
        }

        $this->tlsState = TlsState::ShutdownPending;

        try {
            /** @psalm-suppress PossiblyInvalidArgument */
            Internal\shutdownTls($resource);
        } finally {
            $this->tlsState = TlsState::Disabled;
        }
    }

    #[\Override]
    public function read(?Cancellation $cancellation = null, ?int $limit = null): ?string
    {
        return $this->reader->read($cancellation, $limit);
    }

    #[\Override]
    public function write(string $bytes): void
    {
        $this->writer->write($bytes);
    }

    #[\Override]
    public function end(): void
    {
        $this->writer->end();
    }

    #[\Override]
    public function close(): void
    {
        $this->reader->close();
        $this->writer->close();
    }

    #[\Override]
    public function reference(): void
    {
        $this->reader->reference();
        $this->writer->reference();
    }

    #[\Override]
    public function unreference(): void
    {
        $this->reader->unreference();
        $this->writer->unreference();
    }

    #[\Override]
    public function getLocalAddress(): SocketAddress
    {
        return $this->localAddress;
    }

    /**
     * @return resource|object|null
     */
    #[\Override]
    public function getResource()
    {
        return $this->reader->getResource();
    }

    #[\Override]
    public function getRemoteAddress(): SocketAddress
    {
        return $this->remoteAddress;
    }

    #[\Override]
    public function isTlsConfigurationAvailable(): bool
    {
        return $this->tlsContext || !empty($this->getStreamContext()['ssl']);
    }

    private function getStreamContext(): ?array
    {
        if ($this->streamContext !== null) {
            return $this->streamContext;
        }

        if ($this->tlsContext) {
            return $this->streamContext = $this->tlsContext->toStreamContextArray();
        }

        $resource = $this->getResource();
        if (!\is_resource($resource)) {
            return null;
        }

        return $this->streamContext = \stream_context_get_options($resource);
    }

    #[\Override]
    public function getTlsState(): TlsState
    {
        return $this->tlsState;
    }

    #[\Override]
    public function getTlsInfo(): ?TlsInfo
    {
        if ($this->tlsInfo !== null) {
            return $this->tlsInfo;
        }

        $resource = $this->getResource();
        if (!\is_resource($resource)) {
            return null;
        }

        return $this->tlsInfo = TlsInfo::fromStreamResource($resource);
    }

    #[\Override]
    public function isClosed(): bool
    {
        return $this->reader->isClosed() && $this->writer->isClosed();
    }

    #[\Override]
    public function onClose(\Closure $onClose): void
    {
        $this->reader->onClose($onClose);
    }

    /**
     * @param positive-int $chunkSize New default chunk size for reading and writing.
     */
    public function setChunkSize(int $chunkSize): void
    {
        $this->reader->setChunkSize($chunkSize);
        $this->writer->setChunkSize($chunkSize);
    }

    #[\Override]
    public function isReadable(): bool
    {
        return $this->reader->isReadable();
    }

    #[\Override]
    public function isWritable(): bool
    {
        return $this->writer->isWritable();
    }
}
<?php declare(strict_types=1);

namespace Amp\Socket;

/**
 * Thrown in case a second read operation is attempted while another read operation is still pending.
 */
final class PendingAcceptError extends \Error
{
    public function __construct(
        string $message = 'The previous accept operation must complete before accept can be called again',
        int $code = 0,
        ?\Throwable $previous = null
    ) {
        parent::__construct($message, $code, $previous);
    }
}
<?php declare(strict_types=1);

namespace Amp\Socket;

final class ClientTlsContext
{
    public const TLSv1_0 = \STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT;
    public const TLSv1_1 = \STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT;
    public const TLSv1_2 = \STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT;
    public const TLSv1_3 = \STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT;

    private const TLS_VERSIONS = [
        'TLSv1.0' => self::TLSv1_0,
        'TLSv1.1' => self::TLSv1_1,
        'TLSv1.2' => self::TLSv1_2,
        'TLSv1.3' => self::TLSv1_3,
    ];

    private int $minVersion = self::TLSv1_2;

    private string $peerName;

    private bool $verifyPeer = true;

    private bool $verifyPeerName = true;

    private int $verifyDepth = 10;

    private ?array $peerFingerprint = null;

    private ?string $ciphers = null;

    private ?string $caFile = null;

    private ?string $caPath = null;

    private bool $capturePeer = false;

    private bool $sniEnabled = true;

    private int $securityLevel = 2;

    private ?Certificate $certificate = null;

    /** @var string[] */
    private array $alpnProtocols = [];

    public function __construct(string $peerName = '')
    {
        $this->peerName = $peerName;
    }

    /**
     * Minimum TLS version to negotiate.
     *
     * Defaults to TLS 1.2.
     *
     * @param int $version One of the `ClientTlsContext::TLSv*` constants.
     *
     * @return self Cloned, modified instance.
     * @throws \Error If an invalid minimum version is given.
     */
    public function withMinimumVersion(int $version): self
    {
        if (!\in_array($version, self::TLS_VERSIONS, true)) {
            throw new \Error(\sprintf(
                'Invalid minimum version, only %s allowed',
                \implode(', ', \array_keys(self::TLS_VERSIONS))
            ));
        }

        $clone = clone $this;
        $clone->minVersion = $version;

        return $clone;
    }

    /**
     * Returns the minimum TLS version to negotiate.
     */
    public function getMinimumVersion(): int
    {
        return $this->minVersion;
    }

    /**
     * Expected name of the peer.
     *
     * @return self Cloned, modified instance.
     */
    public function withPeerName(string $peerName): self
    {
        $clone = clone $this;
        $clone->peerName = $peerName;

        return $clone;
    }

    /**
     * @return null|string Expected name of the peer or `null` if such an expectation doesn't exist.
     */
    public function getPeerName(): ?string
    {
        return $this->peerName;
    }

    /**
     * Enable peer verification.
     *
     * @return self Cloned, modified instance.
     */
    public function withPeerVerification(): self
    {
        $clone = clone $this;
        $clone->verifyPeer = true;
        $clone->verifyPeerName = true;

        return $clone;
    }

    /**
     * Disable peer verification, this is the default for servers.
     *
     * Warning: You usually shouldn't disable this setting for clients, because it allows active MitM attackers to
     * intercept the communication and change it without anyone noticing.
     *
     * @return self Cloned, modified instance.
     */
    public function withoutPeerVerification(): self
    {
        $clone = clone $this;
        $clone->verifyPeer = false;
        $clone->verifyPeerName = false;

        return $clone;
    }

    /**
     * @return bool Whether peer verification is enabled.
     */
    public function hasPeerVerification(): bool
    {
        return $this->verifyPeer;
    }

    /**
     * Enable peer name verification, this is the default with verifyPeer enabled.
     *
     * @return self Cloned, modified instance.
     */
    public function withPeerNameVerification(): self
    {
        $clone = clone $this;
        $clone->verifyPeerName = true;

        return $clone;
    }

    /**
     * Disable peer name verification.
     *
     * @return self Cloned, modified instance.
     */
    public function withoutPeerNameVerification(): self
    {
        $clone = clone $this;
        $clone->verifyPeerName = false;

        return $clone;
    }

    /**
     * @return bool Whether peer verification is enabled.
     */
    public function hasPeerNameVerification(): bool
    {
        return $this->verifyPeerName;
    }

    /**
     * Maximum chain length the peer might present including the certificates in the local trust store.
     *
     * @param int $verifyDepth Maximum length of the certificate chain.
     *
     * @return self Cloned, modified instance.
     */
    public function withVerificationDepth(int $verifyDepth): self
    {
        if ($verifyDepth < 0) {
            throw new \Error("Invalid verification depth ({$verifyDepth}), must be greater than or equal to 0");
        }

        $clone = clone $this;
        $clone->verifyDepth = $verifyDepth;

        return $clone;
    }

    /**
     * @return int Maximum length of the certificate chain.
     */
    public function getVerificationDepth(): int
    {
        return $this->verifyDepth;
    }

    /**
     * List of ciphers to negotiate, the server's order is always preferred.
     *
     * @param string|null $ciphers List of ciphers in OpenSSL's format (colon separated).
     *
     * @return self Cloned, modified instance.
     */
    public function withCiphers(?string $ciphers = null): self
    {
        $clone = clone $this;
        $clone->ciphers = $ciphers;

        return $clone;
    }

    /**
     * @return string List of ciphers in OpenSSL's format (colon separated).
     */
    public function getCiphers(): string
    {
        return $this->ciphers ?? \OPENSSL_DEFAULT_STREAM_CIPHERS;
    }

    /**
     * CAFile to check for trusted certificates.
     *
     * @param string|null $cafile Path to the file or `null` to unset.
     *
     * @return self Cloned, modified instance.
     */
    public function withCaFile(?string $cafile = null): self
    {
        $clone = clone $this;
        $clone->caFile = $cafile;

        return $clone;
    }

    /**
     * @return null|string Path to the trusted certificates file if one is set, otherwise `null`.
     */
    public function getCaFile(): ?string
    {
        return $this->caFile;
    }

    /**
     * CAPath to check for trusted certificates.
     *
     * @param string|null $capath Path to the directory or `null` to unset.
     *
     * @return self Cloned, modified instance.
     */
    public function withCaPath(?string $capath = null): self
    {
        $clone = clone $this;
        $clone->caPath = $capath;

        return $clone;
    }

    /**
     * @return null|string Path to the trusted certificate directory if one is set, otherwise `null`.
     */
    public function getCaPath(): ?string
    {
        return $this->caPath;
    }

    /**
     * Capture the certificates sent by the peer.
     *
     * Note: This is the chain as sent by the peer, NOT the verified chain.
     *
     * @return self Cloned, modified instance.
     */
    public function withPeerCapturing(): self
    {
        $clone = clone $this;
        $clone->capturePeer = true;

        return $clone;
    }

    /**
     * Don't capture the certificates sent by the peer.
     *
     * @return self Cloned, modified instance.
     */
    public function withoutPeerCapturing(): self
    {
        $clone = clone $this;
        $clone->capturePeer = false;

        return $clone;
    }

    /**
     * @return bool Whether to capture the certificates sent by the peer.
     */
    public function hasPeerCapturing(): bool
    {
        return $this->capturePeer;
    }

    /**
     * Enable SNI.
     *
     * @return self Cloned, modified instance.
     */
    public function withSni(): self
    {
        $clone = clone $this;
        $clone->sniEnabled = true;

        return $clone;
    }

    /**
     * Disable SNI.
     *
     * @return self Cloned, modified instance.
     */
    public function withoutSni(): self
    {
        $clone = clone $this;
        $clone->sniEnabled = false;

        return $clone;
    }

    /**
     * @return bool Whether SNI is enabled or not.
     */
    public function hasSni(): bool
    {
        return $this->sniEnabled;
    }

    /**
     * Security level to use.
     *
     * Requires OpenSSL 1.1.0 or higher.
     *
     * @param int $level Must be between 0 and 5.
     *
     * @return self Cloned, modified instance.
     */
    public function withSecurityLevel(int $level): self
    {
        // See https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_security_level.html
        // Level 2 is not recommended, because of SHA-1 by that document,
        // but SHA-1 should be phased out now on general internet use.
        // We therefore default to level 2.

        if ($level < 0 || $level > 5) {
            throw new \Error("Invalid security level ({$level}), must be between 0 and 5.");
        }

        if (!hasTlsSecurityLevelSupport()) {
            throw new \Error("Can't set a security level, as PHP is compiled with OpenSSL < 1.1.0.");
        }

        $clone = clone $this;
        $clone->securityLevel = $level;

        return $clone;
    }

    /**
     * @return int Security level between 0 and 5. Always 0 for OpenSSL < 1.1.0.
     */
    public function getSecurityLevel(): int
    {
        // 0 is equivalent to previous versions of OpenSSL and just does nothing
        if (!hasTlsSecurityLevelSupport()) {
            return 0;
        }

        return $this->securityLevel;
    }

    /**
     * Client certificate to use, if key is no present it assumes it is present in the same file as the certificate.
     *
     * @param Certificate $certificate Certificate and private key info
     *
     * @return self Cloned, modified instance.
     */
    public function withCertificate(?Certificate $certificate = null): self
    {
        $clone = clone $this;
        $clone->certificate = $certificate;

        return $clone;
    }

    public function getCertificate(): ?Certificate
    {
        return $this->certificate;
    }

    public function withPeerFingerprint(string $fingerprint): self
    {
        $hash = match (\strlen($fingerprint)) {
            40 => 'sha1',
            64 => 'sha256',
            default => throw new \ValueError('String must be an SHA256 or SHA1 hash'),
        };

        return $this->withPeerFingerprints([$hash => $fingerprint]);
    }

    public function withPeerFingerprints(array $fingerprints): self
    {
        foreach ($fingerprints as $hash => $fingerprint) {
            if (!\is_string($fingerprint) || !match ($hash) {
                'sha1' => \strlen($fingerprint) === 40,
                'sha256' => \strlen($fingerprint) === 64,
                default => false,
            }) {
                throw new \ValueError("Invalid fingerprint array; {$hash} is invalid");
            }
        }

        $clone = clone $this;
        $clone->peerFingerprint = $fingerprints;

        return $clone;
    }

    public function withoutPeerFingerprints(): self
    {
        $clone = clone $this;
        $clone->peerFingerprint = null;

        return $clone;
    }

    public function getPeerFingerprints(): ?array
    {
        return $this->peerFingerprint;
    }

    /**
     * @param string[] $protocols
     *
     * @return self Cloned, modified instance.
     */
    public function withApplicationLayerProtocols(array $protocols): self
    {
        if (!hasTlsAlpnSupport()) {
            throw new \Error("Can't set an application layer protocol list, as PHP is compiled with OpenSSL < 1.0.2.");
        }

        foreach ($protocols as $protocol) {
            if (!\is_string($protocol)) {
                throw new \TypeError("Protocol names must be strings");
            }
        }

        $clone = clone $this;
        $clone->alpnProtocols = $protocols;

        return $clone;
    }

    /**
     * @return string[]
     */
    public function getApplicationLayerProtocols(): array
    {
        return $this->alpnProtocols;
    }

    /**
     * Converts this TLS context into PHP's equivalent stream context array.
     *
     * @return array Stream context array compatible with PHP's streams.
     */
    public function toStreamContextArray(): array
    {
        $options = [
            'crypto_method' => $this->toStreamCryptoMethod(),
            'peer_name' => $this->peerName,
            'verify_peer' => $this->verifyPeer,
            'verify_peer_name' => $this->verifyPeerName,
            'verify_depth' => $this->verifyDepth,
            'ciphers' => $this->ciphers ?? \OPENSSL_DEFAULT_STREAM_CIPHERS,
            'capture_peer_cert' => $this->capturePeer,
            'capture_peer_cert_chain' => $this->capturePeer,
            'SNI_enabled' => $this->sniEnabled,
        ];

        if ($this->certificate !== null) {
            $options['local_cert'] = $this->certificate->getCertFile();

            if ($this->certificate->getCertFile() !== $this->certificate->getKeyFile()) {
                $options['local_pk'] = $this->certificate->getKeyFile();
            }

            if ($this->certificate->getPassphrase() !== null) {
                $options['passphrase'] = $this->certificate->getPassphrase();
            }
        }

        if ($this->caFile !== null) {
            $options['cafile'] = $this->caFile;
        }

        if ($this->caPath !== null) {
            $options['capath'] = $this->caPath;
        }

        if (hasTlsSecurityLevelSupport()) {
            $options['security_level'] = $this->securityLevel;
        }

        if (!empty($this->alpnProtocols)) {
            $options['alpn_protocols'] = \implode(',', $this->alpnProtocols);
        }

        if ($this->peerFingerprint !== null) {
            $options['peer_fingerprint'] = $this->peerFingerprint;
        }

        return ['ssl' => $options];
    }

    /**
     * @return int Crypto method compatible with PHP's streams.
     */
    public function toStreamCryptoMethod(): int
    {
        return match ($this->minVersion) {
            self::TLSv1_0 => self::TLSv1_0 | self::TLSv1_1 | self::TLSv1_2 | self::TLSv1_3,
            self::TLSv1_1 => self::TLSv1_1 | self::TLSv1_2 | self::TLSv1_3,
            self::TLSv1_2 => self::TLSv1_2 | self::TLSv1_3,
            self::TLSv1_3 => self::TLSv1_3,
            default => throw new \Error('Unknown minimum TLS version: ' . $this->minVersion),
        };
    }
}
<?php declare(strict_types=1);

namespace Amp\Socket;

use Amp\Cancellation;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use League\Uri\UriString;
use Revolt\EventLoop;

/**
 * SocketPool implementation that doesn't impose any limits on concurrent open connections.
 *
 * @psalm-type SocketEntry = object{
 *     uri: string,
 *     object: Socket,
 *     isAvailable: bool,
 *     idleWatcher: string|null,
 * }
 */
final class UnlimitedSocketPool implements SocketPool
{
    use ForbidCloning;
    use ForbidSerialization;

    private const ALLOWED_SCHEMES = [
        'tcp' => null,
        'unix' => null,
    ];

    /** @var array<string, array<int, SocketEntry>> */
    private array $sockets = [];

    /** @var array<int, string> */
    private array $objectIdCacheKeyMap = [];

    /** @var int[] */
    private array $pendingCount = [];

    public function __construct(
        private readonly float $idleTimeout = 10,
        private readonly ?SocketConnector $connector = null,
    ) {
    }

    #[\Override]
    public function checkout(
        string $uri,
        ?ConnectContext $context = null,
        ?Cancellation $cancellation = null,
    ): Socket {
        // A request might already be cancelled before we reach the checkout, so do not even attempt to checkout in that
        // case. The weird logic is required to throw the token's exception instead of creating a new one.
        if ($cancellation && $cancellation->isRequested()) {
            $cancellation->throwIfRequested();
        }

        [$uri, $fragment] = $this->normalizeUri($uri);

        $cacheKey = $uri;

        if ($context && ($tlsContext = $context->getTlsContext())) {
            $cacheKey .= ' + ' . \serialize($tlsContext->toStreamContextArray());
        }

        if ($fragment !== null) {
            $cacheKey .= ' # ' . $fragment;
        }

        if (empty($this->sockets[$cacheKey])) {
            return $this->checkoutNewSocket($uri, $cacheKey, $context, $cancellation);
        }

        foreach ($this->sockets[$cacheKey] as $socket) {
            if (!$socket->isAvailable) {
                continue;
            }

            if ($socket->object instanceof ResourceSocket) {
                $resource = $socket->object->getResource();

                if (!$resource || !\is_resource($resource) || \feof($resource)) {
                    $this->clearFromId($socket->object);
                    continue;
                }
            } elseif ($socket->object->isClosed()) {
                $this->clearFromId($socket->object);
                continue;
            }

            $socket->isAvailable = false;

            if ($socket->idleWatcher !== null) {
                EventLoop::disable($socket->idleWatcher);
            }

            return $socket->object;
        }

        return $this->checkoutNewSocket($uri, $cacheKey, $context, $cancellation);
    }

    #[\Override]
    public function clear(Socket $socket): void
    {
        $this->clearFromId($socket);
    }

    #[\Override]
    public function checkin(Socket $socket): void
    {
        $objectId = \spl_object_id($socket);

        if (!isset($this->objectIdCacheKeyMap[$objectId])) {
            throw new \Error(
                \sprintf('Unknown socket: %d', $objectId)
            );
        }

        $cacheKey = $this->objectIdCacheKeyMap[$objectId];

        if ($socket instanceof ResourceSocket) {
            $resource = $socket->getResource();

            if (!$resource || !\is_resource($resource) || \feof($resource)) {
                $this->clearFromId($socket);
                return;
            }
        } elseif ($socket->isClosed()) {
            $this->clearFromId($socket);
            return;
        }

        $socket = $this->sockets[$cacheKey][$objectId];
        $socket->isAvailable = true;

        $socket->idleWatcher ??= EventLoop::unreference(EventLoop::delay(
            $this->idleTimeout,
            fn () => $this->clearFromId($socket->object),
        ));

        EventLoop::enable($socket->idleWatcher);
    }

    /**
     * @throws SocketException
     */
    private function normalizeUri(string $uri): array
    {
        if (\stripos($uri, 'unix://') === 0) {
            return \explode('#', $uri) + [null, null];
        }

        try {
            $parts = UriString::parse($uri);
        } catch (\Exception $exception) {
            throw new SocketException('Could not parse URI', 0, $exception);
        }

        if ($parts['scheme'] === null) {
            throw new SocketException('Invalid URI for socket pool; no scheme given');
        }

        $port = $parts['port'] ?? 0;

        if ($port === 0 || $parts['host'] === null) {
            throw new SocketException('Invalid URI for socket pool; missing host or port');
        }

        $scheme = \strtolower($parts['scheme']);
        $host = \strtolower($parts['host']);

        if (!\array_key_exists($scheme, self::ALLOWED_SCHEMES)) {
            throw new SocketException(\sprintf(
                "Invalid URI for socket pool; '%s' scheme not allowed - scheme must be one of %s",
                $scheme,
                \implode(', ', \array_keys(self::ALLOWED_SCHEMES))
            ));
        }

        if ($parts['query'] !== null) {
            throw new SocketException('Invalid URI for socket pool; query component not allowed');
        }

        if ($parts['path'] !== '') {
            throw new SocketException('Invalid URI for socket pool; path component must be empty');
        }

        if ($parts['user'] !== null) {
            throw new SocketException('Invalid URI for socket pool; user component not allowed');
        }

        return [$scheme . '://' . $host . ':' . $port, $parts['fragment']];
    }

    private function checkoutNewSocket(
        string $uri,
        string $cacheKey,
        ?ConnectContext $connectContext = null,
        ?Cancellation $cancellation = null,
    ): Socket {
        $this->pendingCount[$uri] = ($this->pendingCount[$uri] ?? 0) + 1;

        try {
            $socket = ($this->connector ?? socketConnector())->connect($uri, $connectContext, $cancellation);
        } finally {
            if (--$this->pendingCount[$uri] === 0) {
                unset($this->pendingCount[$uri]);
            }
        }

        /** @psalm-suppress MissingConstructor */
        $socketEntry = new class($uri, $socket) {
            public bool $isAvailable = false;
            public ?string $idleWatcher = null;

            public function __construct(
                public readonly string $uri,
                public readonly Socket $object,
            ) {
            }
        };

        $objectId = \spl_object_id($socket);
        $this->sockets[$cacheKey][$objectId] = $socketEntry;
        $this->objectIdCacheKeyMap[$objectId] = $cacheKey;

        return $socket;
    }

    private function clearFromId(Socket $socket): void
    {
        $objectId = \spl_object_id($socket);

        if (!isset($this->objectIdCacheKeyMap[$objectId])) {
            throw new \Error(
                \sprintf('Unknown socket: %d', $objectId)
            );
        }

        $cacheKey = $this->objectIdCacheKeyMap[$objectId];
        $socket = $this->sockets[$cacheKey][$objectId];

        if ($socket->idleWatcher) {
            EventLoop::cancel($socket->idleWatcher);
        }

        unset(
            $this->sockets[$cacheKey][$objectId],
            $this->objectIdCacheKeyMap[$objectId],
        );

        if (empty($this->sockets[$cacheKey])) {
            unset($this->sockets[$cacheKey]);
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\Socket;

final class InternetAddress implements SocketAddress
{
    /**
     * @throws SocketException Thrown if the address or port is invalid.
     */
    public static function fromString(string $address): self
    {
        if (!\str_contains($address, ':')) {
            throw new SocketException('Missing port in address: ' . $address);
        }

        return self::tryFromString($address)
            ?? throw new SocketException('Invalid address: ' . $address);
    }

    /**
     * @return self|null Returns null if the address is invalid.
     */
    public static function tryFromString(string $address): ?self
    {
        $colon = \strrpos($address, ':');
        if ($colon === false) {
            return null;
        }

        $ip = \substr($address, 0, $colon);
        $port = \substr($address, $colon + 1);
        if (!\preg_match('[^\d+$]', $port)) {
            return null;
        }

        $port = (int) $port;
        if ($port < 0 || $port > 65535) {
            return null;
        }

        if (\strrpos($ip, ':')) {
            $ip = \trim($ip, '[]');
        }

        if (!\inet_pton($ip)) {
            return null;
        }

        return new self($ip, $port);
    }

    private readonly string $binaryAddress;

    private readonly string $textualAddress;

    /** @var int<0, 65535> */
    private readonly int $port;

    /**
     * @param int<0, 65535> $port
     *
     * @throws SocketException If an invalid address or port is given.
     */
    public function __construct(string $address, int $port)
    {
        /** @psalm-suppress TypeDoesNotContainType */
        if ($port < 0 || $port > 65535) {
            throw new SocketException('Port number must be an integer between 0 and 65535; got ' . $port);
        }

        if (\strrpos($address, ':')) {
            $address = \trim($address, '[]');
        }

        $binaryAddress = \inet_pton($address);
        if ($binaryAddress === false) {
            throw new SocketException('Invalid address: ' . $address);
        }
        $this->binaryAddress = $binaryAddress;

        $textualAddress = \inet_ntop($binaryAddress);
        if (!$textualAddress) {
            throw new SocketException('Invalid address: ' . $address);
        }
        $this->textualAddress = $textualAddress;

        $this->port = $port;
    }

    #[\Override]
    public function getType(): SocketAddressType
    {
        return SocketAddressType::Internet;
    }

    public function getAddress(): string
    {
        return $this->textualAddress;
    }

    public function getAddressBytes(): string
    {
        return $this->binaryAddress;
    }

    public function getVersion(): InternetAddressVersion
    {
        if (\strlen($this->binaryAddress) === 4) {
            return InternetAddressVersion::IPv4;
        }

        return InternetAddressVersion::IPv6;
    }

    /**
     * @return int<0, 65535>
     */
    public function getPort(): int
    {
        return $this->port;
    }

    /**
     * @return non-empty-string <address>:<port> formatted string.
     */
    #[\Override]
    public function toString(): string
    {
        if ($this->getVersion() === InternetAddressVersion::IPv6) {
            return '[' . $this->textualAddress . ']' . ':' . $this->port;
        }

        return $this->textualAddress . ':' . $this->port;
    }

    /**
     * @see toString
     */
    public function __toString(): string
    {
        return $this->toString();
    }
}
<?php declare(strict_types=1);

namespace Amp\Socket;

enum InternetAddressVersion
{
    case IPv4;
    case IPv6;
}
<?php declare(strict_types=1);

namespace Amp\Socket;

use Amp\Cancellation;
use Amp\CancelledException;

/**
 * Allows pooling of connections for stateless protocols.
 */
interface SocketPool
{
    /**
     * Checkout a socket from the specified URI authority.
     *
     * The resulting socket resource should be checked back in via `SocketPool::checkin()` once the calling code is
     * finished with the stream (even if the socket has been closed). Failure to checkin sockets will result in memory
     * leaks and socket queue blockage. Instead of checking the socket in again, it can also be cleared to prevent
     * re-use.
     *
     * @param string $uri URI in scheme://host:port format. TCP is assumed if no scheme is present. An
     *     optional fragment component can be used to differentiate different socket groups connected to the same URI.
     *     Connections to the same host with a different ConnectContext must use separate socket groups internally to
     *     prevent TLS negotiation with the wrong peer name or other TLS settings.
     * @param ConnectContext|null $context Socket connect context to use when connecting.
     * @param Cancellation|null $cancellation Optional cancellation token to cancel the checkout request.
     *
     * @return Socket Resolves to an Socket instance once a connection is available.
     *
     * @throws SocketException
     * @throws CancelledException
     */
    public function checkout(
        string $uri,
        ?ConnectContext $context = null,
        ?Cancellation $cancellation = null
    ): Socket;

    /**
     * Return a previously checked-out socket to the pool, so it can be reused.
     *
     * @param Socket $socket Socket instance.
     *
     * @throws \Error If the provided resource is unknown to the pool.
     */
    public function checkin(Socket $socket): void;

    /**
     * Remove the specified socket from the pool.
     *
     * @param Socket $socket Socket instance.
     *
     * @throws \Error If the provided resource is unknown to the pool.
     */
    public function clear(Socket $socket): void;
}
<?php declare(strict_types=1);

namespace Amp\Socket;

use Amp\Cancellation;
use Amp\CancelledException;
use Amp\DeferredFuture;
use Amp\Dns\DnsException;
use Amp\Dns\DnsRecord;
use Amp\Dns\DnsResolver;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\NullCancellation;
use Amp\TimeoutCancellation;
use Revolt\EventLoop;
use function Amp\Dns\dnsResolver;

final class DnsSocketConnector implements SocketConnector
{
    use ForbidCloning;
    use ForbidSerialization;

    public function __construct(private readonly ?DnsResolver $dnsResolver = null)
    {
    }

    #[\Override]
    public function connect(
        SocketAddress|string $uri,
        ?ConnectContext $context = null,
        ?Cancellation $cancellation = null
    ): Socket {
        $context ??= new ConnectContext();
        $cancellation ??= new NullCancellation();

        if ($uri instanceof SocketAddress) {
            $uri = match ($uri->getType()) {
                SocketAddressType::Internet => 'tcp://' . $uri->toString(),
                SocketAddressType::Unix => 'unix://' . $uri->toString(),
            };
        }

        $uris = $this->resolve($uri, $context);

        $flags = \STREAM_CLIENT_CONNECT | \STREAM_CLIENT_ASYNC_CONNECT;
        $timeout = $context->getConnectTimeout();

        $failures = [];
        foreach ($uris as $builtUri) {
            try {
                $streamContext = \stream_context_create($context->withoutTlsContext()->toStreamContextArray());

                \set_error_handler(static function (int $errno, string $errstr) use (
                    &$failures,
                    $uri,
                    $builtUri,
                ): never {
                    $authority = $uri === $builtUri ? $builtUri : $uri . ' @ ' . $builtUri;

                    throw new ConnectException(\sprintf(
                        'Connection to %s failed: (Error #%d) %s%s',
                        $authority,
                        $errno,
                        $errstr,
                        $failures ? '; previous attempts: ' . \implode($failures) : ''
                    ), $errno);
                });

                try {
                    $socket = \stream_socket_client($builtUri, flags: $flags, context: $streamContext);
                } finally {
                    \restore_error_handler();
                }

                \assert($socket !== false); // For Psalm, the error handler will throw for a false return case.

                \stream_set_blocking($socket, false);

                $deferred = new DeferredFuture();
                $id = $cancellation->subscribe($deferred->error(...));
                $watcher = EventLoop::onWritable(
                    $socket,
                    static function (string $watcher) use ($deferred, $id, $cancellation): void {
                        EventLoop::cancel($watcher);
                        $cancellation->unsubscribe($id);
                        $deferred->complete();
                    }
                );

                try {
                    $deferred->getFuture()->await(new TimeoutCancellation($timeout));
                } catch (CancelledException) {
                    $cancellation->throwIfRequested(); // Rethrow if cancelled from user-provided token.

                    throw new ConnectException(\sprintf(
                        'Connecting to %s @ %s failed: timeout exceeded (%0.3f s)%s',
                        $uri,
                        $builtUri,
                        $timeout,
                        $failures ? '; previous attempts: ' . \implode($failures) : ''
                    ), Internal\CONNECTION_TIMEOUT); // See ETIMEDOUT in http://www.virtsync.com/c-error-codes-include-errno
                } finally {
                    EventLoop::cancel($watcher);
                    $cancellation->unsubscribe($id);
                }

                // The following hack looks like the only way to detect connection refused errors with PHP's stream sockets.
                /** @psalm-suppress TypeDoesNotContainType */
                if (\stream_socket_get_name($socket, true) === false) {
                    \fclose($socket);
                    throw new ConnectException(\sprintf(
                        'Connection to %s @ %s refused%s',
                        $uri,
                        $builtUri,
                        $failures ? '; previous attempts: ' . \implode($failures) : ''
                    ), Internal\CONNECTION_REFUSED); // See ECONNREFUSED in http://www.virtsync.com/c-error-codes-include-errno
                }
            } catch (ConnectException $e) {
                // Includes only error codes used in this file, as error codes on other OS families might be different.
                // In fact, this might show a confusing error message on OS families that return 110 or 111 by itself.
                $knownReasons = [
                    Internal\CONNECTION_BUSY => 'connection busy',
                    Internal\CONNECTION_TIMEOUT => 'connection timeout',
                    Internal\CONNECTION_REFUSED => 'connection refused',
                ];

                $code = $e->getCode();
                $reason = $knownReasons[$code] ?? ('Error #' . $code);

                $failures[] = "$uri @ $builtUri ($reason)";

                continue; // Could not connect to host, try next host in the list.
            }

            /** @psalm-suppress PossiblyUndefinedVariable */
            return ResourceSocket::fromClientSocket($socket, $context->getTlsContext());
        }

        /**
         * This is reached if either all URIs failed or the maximum number of attempts is reached.
         *
         * @noinspection PhpUndefinedVariableInspection
         * @psalm-suppress UndefinedVariable
         */
        throw $e;
    }

    /**
     * @return non-empty-list<string>
     */
    private function resolve(string $uri, ConnectContext $context): array
    {
        [$scheme, $host, $port] = Internal\parseUri($uri);

        if ($host[0] === '[') {
            $host = \substr($host, 1, -1);
        }

        if ($port === 0 || \inet_pton($host)) {
            // Host is already an IP address or file path.
            return [$uri];
        }

        $resolver = $this->dnsResolver ?? dnsResolver();

        try {
            // Host is not an IP address, so resolve the domain name.
            $records = $resolver->resolve(
                $host,
                $context->getDnsTypeRestriction() ?? $this->getDnsTypeRestrictionFromBindTo($context)
            );
        } catch (DnsException $exception) {
            throw new ConnectException(
                message: \sprintf('DNS resolution for %s failed: %s', $host, $exception->getMessage()),
                previous: $exception,
            );
        }

        // Usually the faster response should be preferred, but we don't have a reliable way of determining IPv6
        // support, so we always prefer IPv4 here.
        \usort($records, static fn (DnsRecord $a, DnsRecord $b) => $a->getType() - $b->getType());

        $uris = [];
        foreach ($records as $record) {
            if ($record->getType() === DnsRecord::AAAA) {
                $uris[] = \sprintf('%s://[%s]:%d', $scheme, $record->getValue(), $port);
            } else {
                $uris[] = \sprintf('%s://%s:%d', $scheme, $record->getValue(), $port);
            }
        }

        return $uris;
    }

    private function getDnsTypeRestrictionFromBindTo(ConnectContext $context): ?int
    {
        $bindTo = $context->getBindTo();
        if ($bindTo === null) {
            return null;
        }

        if (\str_starts_with($bindTo, '[')) {
            return DnsRecord::AAAA;
        }

        return DnsRecord::A;
    }
}
<?php declare(strict_types=1);

namespace Amp\Socket;

use Amp\Cancellation;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;

/**
 * Connector that connects to a statically defined URI instead of the URI passed to the {@code connect()} call.
 */
final class StaticSocketConnector implements SocketConnector
{
    use ForbidCloning;
    use ForbidSerialization;

    public function __construct(
        private readonly SocketAddress|string $uri,
        private readonly SocketConnector $connector,
    ) {
    }

    #[\Override]
    public function connect(
        SocketAddress|string $uri,
        ?ConnectContext $context = null,
        ?Cancellation $cancellation = null
    ): Socket {
        return $this->connector->connect($this->uri, $context, $cancellation);
    }
}
<?php declare(strict_types=1);

namespace Amp\Socket;

/**
 * Thrown if connecting fails.
 *
 * @psalm-suppress ClassMustBeFinal
 */
class ConnectException extends SocketException
{
}
<?php declare(strict_types=1);

namespace Amp\Socket;

use function Amp\Socket\Internal\normalizeBindToOption;

final class BindContext
{
    private ?string $bindTo = null;

    /** @var positive-int */
    private int $backlog = 128;

    private bool $reusePort = false;
    private bool $broadcast = false;
    private bool $tcpNoDelay = false;

    private ?ServerTlsContext $tlsContext = null;

    public function withoutBindTo(): self
    {
        return $this->withBindTo(null);
    }

    public function withBindTo(?string $bindTo): self
    {
        $bindTo = normalizeBindToOption($bindTo);

        $clone = clone $this;
        $clone->bindTo = $bindTo;

        return $clone;
    }

    public function getBindTo(): ?string
    {
        return $this->bindTo;
    }

    public function getBacklog(): int
    {
        return $this->backlog;
    }

    /**
     * @param positive-int $backlog
     */
    public function withBacklog(int $backlog): self
    {
        $clone = clone $this;
        $clone->backlog = $backlog;

        return $clone;
    }

    public function hasReusePort(): bool
    {
        return $this->reusePort;
    }

    public function withReusePort(): self
    {
        $clone = clone $this;
        $clone->reusePort = true;

        return $clone;
    }

    public function withoutReusePort(): self
    {
        $clone = clone $this;
        $clone->reusePort = false;

        return $clone;
    }

    public function hasBroadcast(): bool
    {
        return $this->broadcast;
    }

    public function withBroadcast(): self
    {
        $clone = clone $this;
        $clone->broadcast = true;

        return $clone;
    }

    public function withoutBroadcast(): self
    {
        $clone = clone $this;
        $clone->broadcast = false;

        return $clone;
    }

    public function hasTcpNoDelay(): bool
    {
        return $this->tcpNoDelay;
    }

    public function withTcpNoDelay(): self
    {
        $clone = clone $this;
        $clone->tcpNoDelay = true;

        return $clone;
    }

    public function withoutTcpNoDelay(): self
    {
        $clone = clone $this;
        $clone->tcpNoDelay = false;

        return $clone;
    }

    public function getTlsContext(): ?ServerTlsContext
    {
        return $this->tlsContext;
    }

    public function withoutTlsContext(): self
    {
        return $this->withTlsContext(null);
    }

    public function withTlsContext(?ServerTlsContext $tlsContext): self
    {
        $clone = clone $this;
        $clone->tlsContext = $tlsContext;

        return $clone;
    }

    public function toStreamContextArray(): array
    {
        $array = [
            'socket' => [
                'bindto' => $this->bindTo,
                'backlog' => $this->backlog,
                'ipv6_v6only' => true,
                // SO_REUSEADDR has SO_REUSEPORT semantics on Windows
                'so_reuseaddr' => $this->reusePort && \PHP_OS_FAMILY === 'Windows',
                'so_reuseport' => $this->reusePort,
                'so_broadcast' => $this->broadcast,
                'tcp_nodelay' => $this->tcpNoDelay,
            ],
        ];

        if ($this->tlsContext) {
            $array = \array_merge($array, $this->tlsContext->toStreamContextArray());
        }

        return $array;
    }
}
<?php declare(strict_types=1);

namespace Amp\Socket;

use Amp\Cancellation;
use Amp\Closable;

interface UdpSocket extends Closable
{
    /**
     * @param positive-int|null $limit Read at most $limit bytes from the datagram socket. {@code null} uses an
     *     implementation defined limit.
     *
     * @return array{InternetAddress, string}|null Returns {@code null} if the socket is closed.
     *
     * @throws PendingReceiveError If a reception request is already pending.
     */
    public function receive(?Cancellation $cancellation = null, ?int $limit = null): ?array;

    /**
     * @throws SocketException If the UDP socket closes before the data can be sent or the payload is too large.
     */
    public function send(InternetAddress $address, string $data): void;

    public function getAddress(): InternetAddress;
}
<?php declare(strict_types=1);

namespace Amp\Socket;

use Amp\ByteStream\ResourceStream;
use Amp\Cancellation;
use Amp\CancelledException;
use Amp\DeferredFuture;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Revolt\EventLoop;
use Revolt\EventLoop\Suspension;

final class ResourceServerSocket implements ServerSocket, ResourceStream
{
    use ForbidCloning;
    use ForbidSerialization;

    /** @var resource|null Stream socket server resource. */
    private $socket;

    private readonly string $callbackId;

    private readonly SocketAddress $address;

    private ?Suspension $acceptor = null;

    /** @var \Closure(CancelledException):void */
    private readonly \Closure $cancel;

    private readonly \Closure $errorHandler;

    private readonly DeferredFuture $onClose;

    /**
     * @param resource $socket A bound socket server resource
     * @param positive-int $chunkSize Chunk size for the input and output stream.
     *
     * @throws \Error If a stream resource is not given for $socket.
     */
    public function __construct(
        $socket,
        private readonly BindContext $bindContext,
        private readonly int $chunkSize = ResourceSocket::DEFAULT_CHUNK_SIZE,
    ) {
        if (!\is_resource($socket) || \get_resource_type($socket) !== 'stream') {
            throw new \Error('Invalid resource given to constructor!');
        }

        $this->socket = $socket;
        $this->address = SocketAddress\fromResourceLocal($socket);

        // Ignore any errors raised while this handler is set. Errors will be checked through return values.
        $this->errorHandler = static fn () => true;

        $this->onClose = new DeferredFuture;

        \stream_set_blocking($this->socket, false);

        $acceptor = &$this->acceptor;
        $this->callbackId = EventLoop::onReadable($this->socket, static function () use (&$acceptor): void {
            $acceptor?->resume(true);
            $acceptor = null;
        });

        $callbackId = $this->callbackId;
        $this->cancel = static function (CancelledException $exception) use (&$acceptor, $callbackId): void {
            EventLoop::disable($callbackId);

            $acceptor?->throw($exception);
            $acceptor = null;
        };

        EventLoop::disable($this->callbackId);
    }

    /**
     * Automatically cancels the loop watcher.
     */
    public function __destruct()
    {
        if (!$this->socket) {
            return;
        }

        $this->free();
    }

    private function free(): void
    {
        EventLoop::cancel($this->callbackId);

        $this->socket = null;

        $this->acceptor?->resume(false);
        $this->acceptor = null;

        if (!$this->onClose->isComplete()) {
            $this->onClose->complete();
        }
    }

    /**
     * @throws PendingAcceptError If another accept request is pending.
     */
    #[\Override]
    public function accept(?Cancellation $cancellation = null): ?ResourceSocket
    {
        if ($this->acceptor) {
            throw new PendingAcceptError;
        }

        if (!$this->socket) {
            return null; // Resolve with null when server is closed.
        }

        if ($client = $this->acceptSocketClient()) {
            return ResourceSocket::fromServerSocket($client, $this->chunkSize);
        }

        EventLoop::enable($this->callbackId);

        $id = $cancellation?->subscribe($this->cancel);

        try {
            // Error reporting suppressed since stream_socket_accept() emits E_WARNING on client accept failure.
            do {
                $this->acceptor = EventLoop::getSuspension();
                if (!$this->acceptor->suspend()) {
                    return null;
                }
            } while (!$client = $this->acceptSocketClient());

            /** @var resource $client Psalm 5.x seems to think $client is of type 'never' */
            return ResourceSocket::fromServerSocket($client, $this->chunkSize);
        } finally {
            EventLoop::disable($this->callbackId);

            /** @psalm-suppress PossiblyNullArgument $id is always defined if $cancellation is non-null */
            $cancellation?->unsubscribe($id);
        }
    }

    /**
     * @return resource|false
     */
    private function acceptSocketClient()
    {
        \assert($this->socket !== null, "Unexpected server state");

        \set_error_handler($this->errorHandler);

        try {
            return \stream_socket_accept($this->socket, 0); // Timeout of 0 to be non-blocking.
        } finally {
            \restore_error_handler();
        }
    }

    /**
     * Closes the server and stops accepting connections. Any socket clients accepted will not be closed.
     */
    #[\Override]
    public function close(): void
    {
        if ($this->socket) {
            /** @psalm-suppress InvalidPropertyAssignmentValue */
            \fclose($this->socket);
        }

        $this->free();
    }

    #[\Override]
    public function isClosed(): bool
    {
        return $this->socket === null;
    }

    #[\Override]
    public function onClose(\Closure $onClose): void
    {
        $this->onClose->getFuture()->finally($onClose);
    }

    /**
     * References the readability callback used for detecting new connection attempts in {@code accept()}.
     *
     * @see EventLoop::reference()
     */
    #[\Override]
    public function reference(): void
    {
        if ($this->socket === null) {
            return;
        }

        EventLoop::reference($this->callbackId);
    }

    /**
     * Unreferences the readability callback used for detecting new connection attempts in {@code accept()}.
     *
     * @see EventLoop::unreference()
     */
    #[\Override]
    public function unreference(): void
    {
        if ($this->socket === null) {
            return;
        }

        EventLoop::unreference($this->callbackId);
    }

    #[\Override]
    public function getAddress(): SocketAddress
    {
        return $this->address;
    }

    #[\Override]
    public function getBindContext(): BindContext
    {
        return $this->bindContext;
    }

    /**
     * Raw stream socket resource.
     *
     * @return resource|null
     */
    #[\Override]
    public function getResource()
    {
        return $this->socket;
    }
}
<?php declare(strict_types=1);

namespace Amp\Socket;

use Amp\Cancellation;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use function Amp\delay;

final class RetrySocketConnector implements SocketConnector
{
    use ForbidCloning;
    use ForbidSerialization;

    /**
     * @param positive-int $maxAttempts
     * @param positive-int $exponentialBackoffBase
     */
    public function __construct(
        private readonly SocketConnector $delegate,
        private readonly int $maxAttempts = 3,
        private readonly int $exponentialBackoffBase = 2,
    ) {
        if ($this->maxAttempts < 1) {
            throw new \ValueError('The maximum attempts must be a positive integer');
        }

        if ($this->exponentialBackoffBase < 1) {
            throw new \ValueError('The exponential backoff base must be a positive integer');
        }
    }

    /**
     * @psalm-suppress InvalidReturnType
     */
    #[\Override]
    public function connect(
        SocketAddress|string $uri,
        ?ConnectContext $context = null,
        ?Cancellation $cancellation = null
    ): Socket {
        $attempts = 0;
        $failures = [];
        $context ??= new ConnectContext;

        do {
            try {
                return $this->delegate->connect($uri, $context, $cancellation);
            } catch (ConnectException $e) {
                if (++$attempts === $this->maxAttempts) {
                    throw new ConnectException(\sprintf(
                        'Connection to %s failed after %d attempts; previous attempts: %s',
                        (string) $uri,
                        $attempts,
                        \implode(', ', $failures),
                    ));
                }

                $failures[] = $e->getMessage();

                delay($this->exponentialBackoffBase ** $attempts);
            }
        } while (true);
    }
}
<?php declare(strict_types=1);

namespace Amp\Socket;

use Kelunik\Certificate\Certificate;

/**
 * Exposes a connection's negotiated TLS parameters.
 */
final class TlsInfo
{
    /** @var Certificate[]|null */
    private ?array $parsedCertificates = null;

    /**
     * Constructs a new instance from a stream socket resource.
     *
     * @param resource $resource Stream socket resource.
     *
     * @return self|null Returns null if TLS is not enabled on the stream socket.
     */
    public static function fromStreamResource($resource): ?self
    {
        if (!\is_resource($resource) || \get_resource_type($resource) !== 'stream') {
            throw new \Error("Expected a valid stream resource");
        }

        $metadata = \stream_get_meta_data($resource)['crypto'] ?? [];
        $tlsContext = \stream_context_get_options($resource)['ssl'] ?? [];

        return empty($metadata) ? null : self::fromMetaData($metadata, $tlsContext);
    }

    /**
     * Constructs a new instance from PHP's internal info.
     *
     * Always pass the info as obtained from PHP as this method might extract additional fields in the future.
     *
     * @param array $cryptoInfo Crypto info obtained via `stream_get_meta_data($socket->getResource())["crypto"]`.
     * @param array $tlsContext Context obtained via `stream_context_get_options($socket->getResource())["ssl"])`.
     */
    public static function fromMetaData(array $cryptoInfo, array $tlsContext): self
    {
        if (isset($tlsContext["peer_certificate"])) {
            $certificates = \array_merge([$tlsContext["peer_certificate"]], $tlsContext["peer_certificate_chain"] ?? []);
        } else {
            $certificates = $tlsContext["peer_certificate_chain"] ?? [];
        }

        return new self(
            $cryptoInfo["protocol"],
            $cryptoInfo["cipher_name"],
            $cryptoInfo["cipher_bits"],
            $cryptoInfo["cipher_version"],
            $cryptoInfo["alpn_protocol"] ?? null,
            empty($certificates) ? null : $certificates
        );
    }

    /**
     * @param array<resource>|null $certificates
     */
    private function __construct(
        private readonly string $version,
        private readonly string $cipherName,
        private readonly int $cipherBits,
        private readonly string $cipherVersion,
        private readonly ?string $alpnProtocol,
        private readonly ?array $certificates,
    ) {
    }

    public function getVersion(): string
    {
        return $this->version;
    }

    public function getCipherName(): string
    {
        return $this->cipherName;
    }

    public function getCipherBits(): int
    {
        return $this->cipherBits;
    }

    public function getCipherVersion(): string
    {
        return $this->cipherVersion;
    }

    public function getApplicationLayerProtocol(): ?string
    {
        return $this->alpnProtocol;
    }

    /**
     * @return Certificate[]
     *
     * @throws SocketException If peer certificates were not captured.
     */
    public function getPeerCertificates(): array
    {
        if ($this->certificates === null) {
            throw new SocketException("Peer certificates not captured; use ClientTlsContext::withPeerCapturing() to capture peer certificates");
        }

        return $this->parsedCertificates ??= \array_map(
            static fn ($resource) => new Certificate($resource),
            $this->certificates,
        );
    }
}
<?php declare(strict_types=1);

namespace Amp\Socket;

use Amp\Dns\DnsRecord;
use function Amp\Socket\Internal\normalizeBindToOption;

final class ConnectContext
{
    private ?string $bindTo = null;

    private float $connectTimeout = 10;

    private ?int $typeRestriction = null;

    private bool $tcpNoDelay = false;

    private ?ClientTlsContext $tlsContext = null;

    public function withoutBindTo(): self
    {
        return $this->withBindTo(null);
    }

    public function withBindTo(?string $bindTo): self
    {
        $bindTo = normalizeBindToOption($bindTo);

        $clone = clone $this;
        $clone->bindTo = $bindTo;

        return $clone;
    }

    public function getBindTo(): ?string
    {
        return $this->bindTo;
    }

    public function withConnectTimeout(float $timeout): self
    {
        if ($timeout <= 0) {
            throw new \ValueError("Invalid connect timeout ({$timeout}), must be greater than 0");
        }

        $clone = clone $this;
        $clone->connectTimeout = $timeout;

        return $clone;
    }

    public function getConnectTimeout(): float
    {
        return $this->connectTimeout;
    }

    public function withoutDnsTypeRestriction(): self
    {
        return $this->withDnsTypeRestriction(null);
    }

    public function withDnsTypeRestriction(?int $type): self
    {
        if ($type !== null && $type !== DnsRecord::AAAA && $type !== DnsRecord::A) {
            throw new \ValueError('Invalid resolver type restriction');
        }

        $clone = clone $this;
        $clone->typeRestriction = $type;

        return $clone;
    }

    public function getDnsTypeRestriction(): ?int
    {
        return $this->typeRestriction;
    }

    public function hasTcpNoDelay(): bool
    {
        return $this->tcpNoDelay;
    }

    public function withTcpNoDelay(): self
    {
        $clone = clone $this;
        $clone->tcpNoDelay = true;

        return $clone;
    }

    public function withoutTcpNoDelay(): self
    {
        $clone = clone $this;
        $clone->tcpNoDelay = false;

        return $clone;
    }

    public function withoutTlsContext(): self
    {
        return $this->withTlsContext(null);
    }

    public function withTlsContext(?ClientTlsContext $tlsContext): self
    {
        $clone = clone $this;
        $clone->tlsContext = $tlsContext;

        return $clone;
    }

    public function getTlsContext(): ?ClientTlsContext
    {
        return $this->tlsContext;
    }

    public function toStreamContextArray(): array
    {
        $options = [
            'tcp_nodelay' => $this->tcpNoDelay,
        ];

        if ($this->bindTo !== null) {
            $options['bindto'] = $this->bindTo;
        }

        $array = ['socket' => $options];

        if ($this->tlsContext) {
            $array = \array_merge($array, $this->tlsContext->toStreamContextArray());
        }

        return $array;
    }
}
<?php declare(strict_types=1);

namespace Amp\Socket;

use Amp\ByteStream\ResourceStream;
use Amp\Cancellation;
use Amp\CancelledException;
use Amp\DeferredFuture;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Revolt\EventLoop;
use Revolt\EventLoop\Suspension;

final class ResourceUdpSocket implements UdpSocket, ResourceStream
{
    use ForbidCloning;
    use ForbidSerialization;

    public const DEFAULT_LIMIT = 65507; // Max UDP payload size.

    /** @var resource|null UDP socket resource. */
    private $socket;

    private readonly string $callbackId;

    private readonly InternetAddress $address;

    private ?Suspension $reader = null;

    /** @var \Closure(CancelledException):void */
    private readonly \Closure $cancel;

    /** @psalm-suppress UnusedProperty */
    private int $limit;

    private int $defaultLimit;

    private readonly DeferredFuture $onClose;

    /**
     * @param resource $socket A bound udp socket resource.
     * @param positive-int $limit Maximum size for received messages.
     *
     * @throws \Error If a stream resource is not given for {@code $socket}.
     */
    public function __construct($socket, int $limit = self::DEFAULT_LIMIT)
    {
        if (!\is_resource($socket) || \get_resource_type($socket) !== 'stream') {
            throw new \Error('Invalid resource given to constructor!');
        }

        /** @psalm-suppress TypeDoesNotContainType */
        if ($limit < 1) {
            throw new \ValueError('Invalid length limit of ' . $limit . ', must be greater than 0');
        }

        $socketAddress = SocketAddress\fromResourceLocal($socket);

        $this->socket = $socket;
        $this->defaultLimit = $this->limit = &$limit;
        $this->address = match ($socketAddress::class) {
            InternetAddress::class => $socketAddress,
            default => throw new \ValueError('Invalid socket address type: ' . $socketAddress::class)
        };

        $this->onClose = new DeferredFuture;

        \stream_set_blocking($this->socket, false);
        /** @psalm-suppress UnusedFunctionCall */
        \stream_set_read_buffer($this->socket, 0);

        $reader = &$this->reader;
        $this->callbackId = EventLoop::onReadable($this->socket, static function (string $callbackId, $socket) use (
            &$reader,
            &$limit,
        ): void {
            static $errorHandler;

            \assert($reader !== null);

            \set_error_handler($errorHandler ??= static fn () => true);

            try {
                $data = \stream_socket_recvfrom($socket, $limit, 0, $address);
            } finally {
                \restore_error_handler();
            }

            /** @psalm-suppress TypeDoesNotContainType */
            if ($data === false) {
                EventLoop::cancel($callbackId);

                $reader->resume();
            } else {
                EventLoop::disable($callbackId);

                $reader->resume([SocketAddress\fromString($address), $data]);
            }

            $reader = null;
        });

        $callbackId = $this->callbackId;
        $this->cancel = static function (CancelledException $exception) use (&$reader, $callbackId): void {
            EventLoop::disable($callbackId);

            $reader?->throw($exception);
            $reader = null;
        };

        EventLoop::disable($this->callbackId);
    }

    /**
     * Automatically cancels the loop watcher.
     */
    public function __destruct()
    {
        if (!$this->socket) {
            return;
        }

        $this->free();
    }

    /**
     * @param positive-int|null $limit If null, the default chunk size is used.
     *
     * @return null|array{InternetAddress, string}
     */
    #[\Override]
    public function receive(?Cancellation $cancellation = null, ?int $limit = null): ?array
    {
        if ($this->reader) {
            throw new PendingReceiveError;
        }

        $limit ??= $this->defaultLimit;

        if ($limit <= 0) {
            throw new \ValueError('The length limit must be a positive integer, got ' . $limit);
        }

        if (!$this->socket) {
            return null; // Resolve with null when endpoint is closed.
        }

        $this->limit = $limit;
        $this->reader = EventLoop::getSuspension();

        EventLoop::enable($this->callbackId);

        $id = $cancellation?->subscribe($this->cancel);

        try {
            return $this->reader->suspend();
        } finally {
            /** @psalm-suppress PossiblyNullArgument $id is always defined if $cancellation is present */
            $cancellation?->unsubscribe($id);
        }
    }

    #[\Override]
    public function send(InternetAddress $address, string $data): void
    {
        static $errorHandler;
        $errorHandler ??= static function (int $errno, string $errstr): never {
            throw new SocketException(\sprintf('Could not send datagram packet: %s', $errstr));
        };

        if (!$this->socket) {
            throw new SocketException('The datagram socket is not writable');
        }

        \set_error_handler($errorHandler);

        try {
            $result = \stream_socket_sendto($this->socket, $data, 0, $address->toString());
            /** @psalm-suppress TypeDoesNotContainType */
            if ($result < 0 || $result === false) {
                throw new SocketException('Could not send datagram packet: Unknown error');
            }
        } finally {
            \restore_error_handler();
        }
    }

    /**
     * Raw stream socket resource.
     *
     * @return resource|null
     */
    #[\Override]
    public function getResource()
    {
        return $this->socket;
    }

    /**
     * References the event loop callback used for being notified about available packets.
     *
     * @see EventLoop::reference()
     */
    #[\Override]
    public function reference(): void
    {
        if ($this->socket === null) {
            return;
        }

        EventLoop::reference($this->callbackId);
    }

    /**
     * Unreferences the event loop callback used for being notified about available packets.
     *
     * @see EventLoop::unreference()
     */
    #[\Override]
    public function unreference(): void
    {
        if ($this->socket === null) {
            return;
        }

        EventLoop::unreference($this->callbackId);
    }

    /**
     * Closes the datagram socket and stops receiving data. A pending {@code receive()} will return {@code null}.
     */
    #[\Override]
    public function close(): void
    {
        if ($this->socket) {
            /** @psalm-suppress InvalidPropertyAssignmentValue */
            \fclose($this->socket);
        }

        $this->free();
    }

    #[\Override]
    public function isClosed(): bool
    {
        return $this->socket === null;
    }

    #[\Override]
    public function onClose(\Closure $onClose): void
    {
        $this->onClose->getFuture()->finally($onClose);
    }

    #[\Override]
    public function getAddress(): InternetAddress
    {
        return $this->address;
    }

    /**
     * @param positive-int $limit The new default maximum packet size to receive.
     */
    public function setLimit(int $limit): void
    {
        /** @psalm-suppress TypeDoesNotContainType */
        if ($limit <= 0) {
            throw new \ValueError('The chunk length must be a positive integer, got ' . $limit);
        }

        $this->defaultLimit = $limit;
    }

    private function free(): void
    {
        EventLoop::cancel($this->callbackId);

        $this->socket = null;

        $this->reader?->resume();
        $this->reader = null;

        if (!$this->onClose->isComplete()) {
            $this->onClose->complete();
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\Socket;

final class ServerTlsContext
{
    public const TLSv1_0 = \STREAM_CRYPTO_METHOD_TLSv1_0_SERVER;
    public const TLSv1_1 = \STREAM_CRYPTO_METHOD_TLSv1_1_SERVER;
    public const TLSv1_2 = \STREAM_CRYPTO_METHOD_TLSv1_2_SERVER;
    public const TLSv1_3 = \STREAM_CRYPTO_METHOD_TLSv1_3_SERVER;

    private const TLS_VERSIONS = [
        'TLSv1.0' => self::TLSv1_0,
        'TLSv1.1' => self::TLSv1_1,
        'TLSv1.2' => self::TLSv1_2,
        'TLSv1.3' => self::TLSv1_3,
    ];

    /**
     * @param resource $socket
     */
    public static function fromServerResource($socket): ?self
    {
        $tls = \stream_context_get_options($socket)['ssl'] ?? [];
        if (!$tls) {
            return null;
        }

        $context = new self();

        if (isset($tls['peer_name'])) {
            $context = $context->withPeerName($tls['peer_name']);
        }

        if (isset($tls['verify_depth'])) {
            $context = $context->withVerificationDepth($tls['verify_depth']);
        }

        if (isset($tls['ciphers'])) {
            $context = $context->withCiphers($tls['ciphers']);
        }

        if (isset($tls['security_level']) && hasTlsSecurityLevelSupport()) {
            $context = $context->withSecurityLevel($tls['security_level']);
        }

        if (isset($tls['local_cert'])) {
            $context = $context->withDefaultCertificate(
                new Certificate($tls['local_cert'], $tls['local_pk'] ?? null)
            );
        }

        if (isset($tls['SNI_server_certs'])) {
            $context = $context->withCertificates(\array_map(
                static fn (array $certificate) => new Certificate(
                    $certificate['local_cert'],
                    $certificate['local_pk'] ?? null,
                    $certificate['passphrase'] ?? null,
                ),
                $tls['SNI_server_certs'],
            ));
        }

        if (isset($tls['cafile'])) {
            $context = $context->withCaFile($tls['cafile']);
        }

        if (isset($tls['capath'])) {
            $context = $context->withCaPath($tls['capath']);
        }

        if (!empty($tls['alpn_protocols'])) {
            $context = $context->withApplicationLayerProtocols(\explode(',', $tls['alpn_protocols']));
        }

        if (($tls['verify_peer'] ?? false) || ($tls['verify_peer_name'] ?? false)) {
            $context = $context->withPeerVerification();
        }

        if (($tls['capture_peer_cert'] ?? false) || ($tls['capture_peer_chain'] ?? false)) {
            $context = $context->withPeerCapturing();
        }

        if (isset($tls['crypto_method'])) {
            $minVersion = self::TLSv1_3;
            foreach ([self::TLSv1_2, self::TLSv1_1, self::TLSv1_0] as $tlsVersion) {
                if ($tls['crypto_method'] & $tlsVersion) {
                    $minVersion = $tlsVersion;
                }
            }

            $context = $context->withMinimumVersion($minVersion);
        }

        return $context;
    }

    private int $minVersion = self::TLSv1_2;

    private ?string $peerName = null;

    private bool $verifyPeer = false;

    private bool $verifyPeerName = false;

    private int $verifyDepth = 10;

    private ?string $ciphers = null;

    private ?string $caFile = null;

    private ?string $caPath = null;

    private bool $capturePeer = false;

    private ?Certificate $defaultCertificate = null;

    /** @var Certificate[] */
    private array $certificates = [];

    private int $securityLevel = 2;

    /** @var string[] */
    private array $alpnProtocols = [];

    /**
     * Minimum TLS version to negotiate.
     *
     * Defaults to TLS 1.2.
     *
     * @param int $version One of the `ServerTlsContext::TLSv*` constants.
     *
     * @return self Cloned, modified instance.
     * @throws \Error If an invalid minimum version is given.
     */
    public function withMinimumVersion(int $version): self
    {
        if (!\in_array($version, self::TLS_VERSIONS, true)) {
            throw new \Error(\sprintf(
                'Invalid minimum version, only %s allowed',
                \implode(', ', \array_keys(self::TLS_VERSIONS))
            ));
        }

        $clone = clone $this;
        $clone->minVersion = $version;

        return $clone;
    }

    /**
     * Returns the minimum TLS version to negotiate.
     */
    public function getMinimumVersion(): int
    {
        return $this->minVersion;
    }

    /**
     * Expected name of the peer.
     *
     * @return self Cloned, modified instance.
     */
    public function withPeerName(?string $peerName = null): self
    {
        $clone = clone $this;
        $clone->peerName = $peerName;

        return $clone;
    }

    /**
     * @return null|string Expected name of the peer or `null` if such an expectation doesn't exist.
     */
    public function getPeerName(): ?string
    {
        return $this->peerName;
    }

    /**
     * Enable peer verification.
     *
     * @return self Cloned, modified instance.
     */
    public function withPeerVerification(): self
    {
        $clone = clone $this;
        $clone->verifyPeer = true;
        $clone->verifyPeerName = true;

        return $clone;
    }

    /**
     * Disable peer verification, this is the default for servers.
     *
     * @return self Cloned, modified instance.
     */
    public function withoutPeerVerification(): self
    {
        $clone = clone $this;
        $clone->verifyPeer = false;
        $clone->verifyPeerName = false;

        return $clone;
    }

    /**
     * @return bool Whether peer verification is enabled.
     */
    public function hasPeerVerification(): bool
    {
        return $this->verifyPeer;
    }

    /**
     * Enable peer name verification, this is the default with verifyPeer enabled.
     *
     * @return self Cloned, modified instance.
     */
    public function withPeerNameVerification(): self
    {
        $clone = clone $this;
        $clone->verifyPeerName = true;

        return $clone;
    }

    /**
     * Disable peer name verification.
     *
     * @return self Cloned, modified instance.
     */
    public function withoutPeerNameVerification(): self
    {
        $clone = clone $this;
        $clone->verifyPeerName = false;

        return $clone;
    }

    /**
     * @return bool Whether peer verification is enabled.
     */
    public function hasPeerNameVerification(): bool
    {
        return $this->verifyPeerName;
    }

    /**
     * Maximum chain length the peer might present including the certificates in the local trust store.
     *
     * @param int $verifyDepth Maximum length of the certificate chain.
     *
     * @return self Cloned, modified instance.
     */
    public function withVerificationDepth(int $verifyDepth): self
    {
        if ($verifyDepth < 0) {
            throw new \Error("Invalid verification depth ({$verifyDepth}), must be greater than or equal to 0");
        }

        $clone = clone $this;
        $clone->verifyDepth = $verifyDepth;

        return $clone;
    }

    /**
     * @return int Maximum length of the certificate chain.
     */
    public function getVerificationDepth(): int
    {
        return $this->verifyDepth;
    }

    /**
     * List of ciphers to negotiate, the server's order is always preferred.
     *
     * @param string|null $ciphers List of ciphers in OpenSSL's format (colon separated).
     *
     * @return self Cloned, modified instance.
     */
    public function withCiphers(?string $ciphers = null): self
    {
        $clone = clone $this;
        $clone->ciphers = $ciphers;

        return $clone;
    }

    /**
     * @return string List of ciphers in OpenSSL's format (colon separated).
     */
    public function getCiphers(): string
    {
        return $this->ciphers ?? \OPENSSL_DEFAULT_STREAM_CIPHERS;
    }

    /**
     * CAFile to check for trusted certificates.
     *
     * @param string|null $cafile Path to the file or `null` to unset.
     *
     * @return self Cloned, modified instance.
     */
    public function withCaFile(?string $cafile = null): self
    {
        $clone = clone $this;
        $clone->caFile = $cafile;

        return $clone;
    }

    /**
     * @return null|string Path to the trusted certificates file if one is set, otherwise `null`.
     */
    public function getCaFile(): ?string
    {
        return $this->caFile;
    }

    /**
     * CAPath to check for trusted certificates.
     *
     * @param string|null $capath Path to the directory or `null` to unset.
     *
     * @return self Cloned, modified instance.
     */
    public function withCaPath(?string $capath = null): self
    {
        $clone = clone $this;
        $clone->caPath = $capath;

        return $clone;
    }

    /**
     * @return null|string Path to the trusted certificate directory if one is set, otherwise `null`.
     */
    public function getCaPath(): ?string
    {
        return $this->caPath;
    }

    /**
     * Capture the certificates sent by the peer.
     *
     * Note: This is the chain as sent by the peer, NOT the verified chain.
     *
     * @return self Cloned, modified instance.
     */
    public function withPeerCapturing(): self
    {
        $clone = clone $this;
        $clone->capturePeer = true;

        return $clone;
    }

    /**
     * Don't capture the certificates sent by the peer.
     *
     * @return self Cloned, modified instance.
     */
    public function withoutPeerCapturing(): self
    {
        $clone = clone $this;
        $clone->capturePeer = false;

        return $clone;
    }

    /**
     * @return bool Whether to capture the certificates sent by the peer.
     */
    public function hasPeerCapturing(): bool
    {
        return $this->capturePeer;
    }

    /**
     * Default certificate to use in case no SNI certificate matches.
     *
     * @return self Cloned, modified instance.
     */
    public function withDefaultCertificate(?Certificate $defaultCertificate = null): self
    {
        $clone = clone $this;
        $clone->defaultCertificate = $defaultCertificate;

        return $clone;
    }

    /**
     * @return Certificate|null Default certificate to use in case no SNI certificate matches, or `null` if unset.
     */
    public function getDefaultCertificate(): ?Certificate
    {
        return $this->defaultCertificate;
    }

    /**
     * Certificates to use for the given host names.
     *
     * @param array $certificates Must be a associative array mapping hostnames to certificate instances.
     *
     * @return self Cloned, modified instance.
     */
    public function withCertificates(array $certificates): self
    {
        foreach ($certificates as $key => $certificate) {
            if (!\is_string($key)) {
                throw new \TypeError('Expected an array mapping domain names to Certificate instances');
            }

            if (!$certificate instanceof Certificate) {
                throw new \TypeError('Expected an array of Certificate instances');
            }
        }

        $clone = clone $this;
        $clone->certificates = $certificates;

        return $clone;
    }

    /**
     * @return array Associative array mapping hostnames to certificate instances.
     */
    public function getCertificates(): array
    {
        return $this->certificates;
    }

    /**
     * Security level to use.
     *
     * Requires OpenSSL 1.1.0 or higher.
     *
     * @param int $level Must be between 0 and 5.
     *
     * @return self Cloned, modified instance.
     */
    public function withSecurityLevel(int $level): self
    {
        // See https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_security_level.html
        // Level 2 is not recommended, because of SHA-1 by that document,
        // but SHA-1 should be phased out now on general internet use.
        // We therefore default to level 2.

        if ($level < 0 || $level > 5) {
            throw new \Error("Invalid security level ({$level}), must be between 0 and 5.");
        }

        if (!hasTlsSecurityLevelSupport()) {
            throw new \Error("Can't set a security level, as PHP is compiled with OpenSSL < 1.1.0.");
        }

        $clone = clone $this;
        $clone->securityLevel = $level;

        return $clone;
    }

    /**
     * @return int Security level between 0 and 5. Always 0 for OpenSSL < 1.1.0.
     */
    public function getSecurityLevel(): int
    {
        // 0 is equivalent to previous versions of OpenSSL and just does nothing
        if (!hasTlsSecurityLevelSupport()) {
            return 0;
        }

        return $this->securityLevel;
    }

    /**
     * @param string[] $protocols
     *
     * @return self Cloned, modified instance.
     */
    public function withApplicationLayerProtocols(array $protocols): self
    {
        if (!hasTlsAlpnSupport()) {
            throw new \Error("Can't set an application layer protocol list, as PHP is compiled with OpenSSL < 1.0.2.");
        }

        foreach ($protocols as $protocol) {
            if (!\is_string($protocol)) {
                throw new \TypeError("Protocol names must be strings");
            }
        }

        $clone = clone $this;
        $clone->alpnProtocols = $protocols;

        return $clone;
    }

    /**
     * @return string[]
     */
    public function getApplicationLayerProtocols(): array
    {
        return $this->alpnProtocols;
    }

    /**
     * Converts this TLS context into PHP's equivalent stream context array.
     *
     * @return array Stream context array compatible with PHP's streams.
     */
    public function toStreamContextArray(): array
    {
        $options = [
            'crypto_method' => $this->toStreamCryptoMethod(),
            'peer_name' => $this->peerName,
            'verify_peer' => $this->verifyPeer,
            'verify_peer_name' => $this->verifyPeerName,
            'verify_depth' => $this->verifyDepth,
            'ciphers' => $this->ciphers ?? \OPENSSL_DEFAULT_STREAM_CIPHERS,
            'honor_cipher_order' => true,
            'single_dh_use' => true,
            'no_ticket' => true,
            'capture_peer_cert' => $this->capturePeer,
            'capture_peer_chain' => $this->capturePeer,
        ];

        if (!empty($this->alpnProtocols)) {
            $options['alpn_protocols'] = \implode(',', $this->alpnProtocols);
        }

        if ($this->defaultCertificate !== null) {
            $options['local_cert'] = $this->defaultCertificate->getCertFile();

            if ($this->defaultCertificate->getCertFile() !== $this->defaultCertificate->getKeyFile()) {
                $options['local_pk'] = $this->defaultCertificate->getKeyFile();
            }
        }

        if ($this->certificates) {
            $options['SNI_server_certs'] = \array_map(static function (Certificate $certificate) {
                $options = [
                    'local_cert' => $certificate->getCertFile(),
                    'local_pk' => $certificate->getKeyFile(),
                ];

                if ($certificate->getPassphrase() !== null) {
                    $options['passphrase'] = $certificate->getPassphrase();
                }

                return $options;
            }, $this->certificates);
        }

        if ($this->caFile !== null) {
            $options['cafile'] = $this->caFile;
        }

        if ($this->caPath !== null) {
            $options['capath'] = $this->caPath;
        }

        if (hasTlsSecurityLevelSupport()) {
            $options['security_level'] = $this->securityLevel;
        }

        return ['ssl' => $options];
    }

    /**
     * @return int Crypto method compatible with PHP's streams.
     */
    public function toStreamCryptoMethod(): int
    {
        return match ($this->minVersion) {
            self::TLSv1_0 => self::TLSv1_0 | self::TLSv1_1 | self::TLSv1_2 | self::TLSv1_3,
            self::TLSv1_1 => self::TLSv1_1 | self::TLSv1_2 | self::TLSv1_3,
            self::TLSv1_2 => self::TLSv1_2 | self::TLSv1_3,
            self::TLSv1_3 => self::TLSv1_3,
            default => throw new \Error('Unknown minimum TLS version: ' . $this->minVersion),
        };
    }
}
<?php declare(strict_types=1);

namespace Amp\Socket\Internal;

use Amp\Cancellation;
use Amp\CancelledException;
use Amp\NullCancellation;
use Amp\Socket\TlsException;
use League\Uri\UriString;
use Revolt\EventLoop;

// Use Linux error codes if the socket extension is not available
\define(__NAMESPACE__ . '\CONNECTION_BUSY', \defined('SOCKET_EAGAIN') ? \SOCKET_EAGAIN : 11);
\define(__NAMESPACE__ . '\CONNECTION_TIMEOUT', \defined('SOCKET_ETIMEDOUT') ? \SOCKET_ETIMEDOUT : 110);
\define(__NAMESPACE__ . '\CONNECTION_REFUSED', \defined('SOCKET_ECONNREFUSED') ? \SOCKET_ECONNREFUSED : 111);

/**
 * Parse an URI into [scheme, host, port].
 *
 * @throws \Error If an invalid URI has been passed.
 *
 * @internal
 */
function parseUri(string $uri): array
{
    if (\stripos($uri, 'unix://') === 0) {
        /** @psalm-suppress PossiblyUndefinedArrayOffset */
        [$scheme, $path] = \explode('://', $uri, 2);
        return [$scheme, \ltrim($path, '/'), 0];
    }

    if (!\str_contains($uri, '://')) {
        // Set a default scheme of tcp if none was given.
        $uri = 'tcp://' . $uri;
    }

    try {
        $uriParts = UriString::parse($uri);
    } catch (\Exception $exception) {
        throw new \Error("Invalid URI: $uri", 0, $exception);
    }

    $scheme = $uriParts['scheme'];
    $host = $uriParts['host'] ?? '';
    $port = $uriParts['port'] ?? 0;

    if (!\in_array($scheme, ['tcp', 'udp', 'unix'], true)) {
        throw new \Error(
            "Invalid URI scheme ($scheme); tcp, udp, or unix scheme expected"
        );
    }

    if ($host === '') {
        throw new \Error(
            "Invalid URI: $uri; host component required"
        );
    }

    if (\str_contains($host, ':')) { // IPv6 address
        $host = \sprintf('[%s]', \trim($host, '[]'));
    }

    return [$scheme, $host, $port];
}

/**
 * Enable encryption on an existing socket stream.
 *
 * @param resource $socket
 *
 * @throws TlsException
 * @throws CancelledException
 *
 * @internal
 */
function setupTls($socket, array $options, ?Cancellation $cancellation): void
{
    $cancellation ??= new NullCancellation;

    if (isset(\stream_get_meta_data($socket)['crypto'])) {
        throw new TlsException("Can't setup TLS, because it has already been set up");
    }

    \error_clear_last();

    if (PHP_VERSION_ID >= 80300) {
        /** @psalm-suppress UnusedFunctionCall */
        \stream_context_set_options($socket, $options);
    } else {
        \stream_context_set_option($socket, $options);
    }

    $errorHandler = static function (int $errno, string $errstr) use ($socket): never {
        if (\feof($socket)) {
            $errstr = 'Connection reset by peer';
        }

        throw new TlsException('TLS negotiation failed: ' . $errstr);
    };

    try {
        \set_error_handler($errorHandler);
        $result = \stream_socket_enable_crypto($socket, enable: true);
        if ($result === false) {
            throw new TlsException('TLS negotiation failed: Unknown error');
        }
    } finally {
        \restore_error_handler();
    }

    // Yes, that function can return true / false / 0, don't use weak comparisons.
    if ($result === true) {
        /** @psalm-suppress InvalidReturnStatement */
        return;
    }

    while (true) {
        $cancellation->throwIfRequested();

        $suspension = EventLoop::getSuspension();

        // Watcher is guaranteed to be created, because we throw above if cancellation has already been requested
        /** @psalm-suppress PossiblyUndefinedVariable $callbackId is defined below. */
        $cancellationId = $cancellation->subscribe(static function ($e) use ($suspension, &$callbackId): void {
            EventLoop::cancel($callbackId);

            $suspension->throw($e);
        });

        $callbackId = EventLoop::onReadable($socket, static function () use (
            $suspension,
            $cancellation,
            $cancellationId,
        ): void {
            $cancellation->unsubscribe($cancellationId);

            $suspension->resume();
        });

        try {
            $suspension->suspend();
        } finally {
            EventLoop::cancel($callbackId);
        }

        try {
            \set_error_handler($errorHandler);
            $result = \stream_socket_enable_crypto($socket, enable: true);
            if ($result === false) {
                $message = \feof($socket) ? 'Connection reset by peer' : 'Unknown error';
                throw new TlsException('TLS negotiation failed: ' . $message);
            }
        } finally {
            \restore_error_handler();
        }

        // If $result is 0, just wait for the next invocation
        if ($result === true) {
            break;
        }
    }
}

/**
 * Disable encryption on an existing socket stream.
 *
 * @param resource $socket
 *
 * @internal
 * @psalm-suppress InvalidReturnType
 */
function shutdownTls($socket): void
{
    \set_error_handler(static function (int $errno, string $errstr) use ($socket): never {
        if (\feof($socket)) {
            $errstr = 'Connection reset by peer';
        }

        throw new TlsException('TLS negotiation failed: ' . $errstr);
    });

    try {
        // note that disabling crypto *ALWAYS* returns false, immediately
        // don't set _enabled to false, TLS can be setup only once
        \stream_socket_enable_crypto($socket, enable: false);
    } finally {
        \restore_error_handler();
    }
}

/**
 * Normalizes "bindto" options to add a ":0" in case no port is present, otherwise PHP will silently ignore those.
 *
 * @throws \Error If an invalid option has been passed.
 *
 * @internal
 */
function normalizeBindToOption(?string $bindTo = null): ?string
{
    if ($bindTo === null) {
        return null;
    }

    if (\preg_match("/\\[(?P<ip>[0-9a-f:]+)](:(?P<port>\\d+))?$/", $bindTo, $match)) {
        $ip = $match['ip'];
        $port = (int) ($match['port'] ?? 0);

        if (\inet_pton($ip) === false) {
            throw new \Error("Invalid IPv6 address: $ip");
        }

        if ($port < 0 || $port > 65535) {
            throw new \Error("Invalid port: $port");
        }

        return "[$ip]:$port";
    }

    if (\preg_match("/(?P<ip>\\d+\\.\\d+\\.\\d+\\.\\d+)(:(?P<port>\\d+))?$/", $bindTo, $match)) {
        $ip = $match['ip'];
        $port = (int) ($match['port'] ?? 0);

        if (\inet_pton($ip) === false) {
            throw new \Error("Invalid IPv4 address: $ip");
        }

        if ($port < 0 || $port > 65535) {
            throw new \Error("Invalid port: $port");
        }

        return "$ip:$port";
    }

    throw new \Error("Invalid bindTo value: $bindTo");
}

/**
 * Alias of {@see stream_socket_get_name()} with errors suppressed.
 *
 * @param resource $resource
 *
 * @internal
 */
function getStreamSocketName($resource, bool $wantPeer): string|false
{
    static $errorHandler;

    \set_error_handler($errorHandler ??= static fn () => true);

    try {
        return \stream_socket_get_name($resource, $wantPeer);
    } finally {
        \restore_error_handler();
    }
}
<?php declare(strict_types=1);

namespace Amp\Socket\SocketAddress;

use Amp\Socket\Internal;
use Amp\Socket\InternetAddress;
use Amp\Socket\SocketAddress;
use Amp\Socket\SocketException;
use Amp\Socket\UnixAddress;

/**
 * @param resource $resource
 *
 * @throws SocketException
 */
function fromResourcePeer($resource): SocketAddress
{
    $name = Internal\getStreamSocketName($resource, true);

    /** @psalm-suppress TypeDoesNotContainType */
    if ($name === false || $name === "\0") {
        return fromResourceLocal($resource);
    }

    return fromString($name);
}

/**
 * @param resource $resource
 *
 * @throws SocketException
 */
function fromResourceLocal($resource): SocketAddress
{
    $wantPeer = false;

    do {
        $name = Internal\getStreamSocketName($resource, $wantPeer);

        /** @psalm-suppress RedundantCondition */
        if ($name !== false && $name !== "\0") {
            return fromString($name);
        }
    } while ($wantPeer = !$wantPeer);

    return new UnixAddress('');
}

/**
 * @throws SocketException
 */
function fromString(string $name): SocketAddress
{
    if (\preg_match("/\\[(?P<ip>[0-9a-f:]+)](:(?P<port>\\d+))$/", $name, $match)) {
        /** @psalm-suppress ArgumentTypeCoercion */
        return new InternetAddress($match['ip'], (int) $match['port']);
    }

    if (\preg_match("/(?P<ip>\\d+\\.\\d+\\.\\d+\\.\\d+)(:(?P<port>\\d+))$/", $name, $match)) {
        /** @psalm-suppress ArgumentTypeCoercion */
        return new InternetAddress($match['ip'], (int) $match['port']);
    }

    return new UnixAddress($name);
}
<?php declare(strict_types=1);

namespace Amp\Socket;

use Amp\ByteStream\ReadableStream;
use Amp\ByteStream\WritableStream;
use Amp\Cancellation;

interface Socket extends ReadableStream, WritableStream
{
    /**
     * @param positive-int|null $limit Read at most $limit bytes from the socket. {@code null} uses an implementation
     *     defined limit.
     */
    #[\Override]
    public function read(?Cancellation $cancellation = null, ?int $limit = null): ?string;

    public function getLocalAddress(): SocketAddress;

    public function getRemoteAddress(): SocketAddress;

    /**
     * @return void Returns when TLS is successfully set up on the socket.
     *
     * @throws SocketException Socket is closed if setting up TLS fails.
     */
    public function setupTls(?Cancellation $cancellation = null): void;

    /**
     * @return void Returns when TLS is successfully shutdown.
     *
     * @throws SocketException Socket is closed if shutting down TLS fails.
     */
    public function shutdownTls(?Cancellation $cancellation = null): void;

    public function isTlsConfigurationAvailable(): bool;

    public function getTlsState(): TlsState;

    /**
     * @return TlsInfo|null The TLS (crypto) context info if TLS is enabled on the socket or null otherwise.
     */
    public function getTlsInfo(): ?TlsInfo;
}
<?php declare(strict_types=1);

namespace Amp\Socket;

interface SocketAddress extends \Stringable
{
    public function toString(): string;

    public function getType(): SocketAddressType;
}
<?php declare(strict_types=1);

namespace Amp\Socket;

final class UnixAddress implements SocketAddress
{
    public function __construct(private readonly string $path)
    {
    }

    #[\Override]
    public function getType(): SocketAddressType
    {
        return SocketAddressType::Unix;
    }

    public function isUnnamed(): bool
    {
        return $this->path === '';
    }

    public function isAbstract(): bool
    {
        return $this->path !== '' && $this->path[0] === "\0";
    }

    #[\Override]
    public function toString(): string
    {
        return $this->path;
    }

    /**
     * @see toString
     */
    public function __toString(): string
    {
        return $this->toString();
    }
}
<?php declare(strict_types=1);

namespace Amp\Socket;

enum TlsState
{
    case Disabled;
    case SetupPending;
    case Enabled;
    case ShutdownPending;
}
<?php declare(strict_types=1);

namespace Amp\Socket;

use ValueError;

final class CidrMatcher
{
    private static function toIPv6(string $networkAddress): string
    {
        if (\strlen($networkAddress) === 4) {
            // IPv4-mapped IPv6 address: https://www.rfc-editor.org/rfc/rfc4038#section-4.2
            $networkAddress = "\0\0\0\0\0\0\0\0\0\0\xFF\xFF" . $networkAddress;
        }

        \assert(\strlen($networkAddress) * 8 === 128);

        return $networkAddress;
    }

    private readonly string $address;
    private readonly string $mask;

    public function __construct(string $cidr)
    {
        [$networkAddress, $bits] = $this->parse($cidr);

        $binMask = \str_split(\str_repeat('1', $bits) . \str_repeat('0', 128 - $bits), 8);

        /** @psalm-suppress InvalidScalarArgument */
        $mask = \implode("", \array_map(fn ($byte) => \chr(\bindec($byte)), $binMask));

        $this->address = $networkAddress & $mask;
        $this->mask = $mask;
    }

    private function parse(string $cidr): array
    {
        [$address, $bits] = \explode("/", $cidr, 2) + [null, null];

        $networkAddress = \inet_pton($address);
        if (!$networkAddress) {
            throw new ValueError('Invalid IP address: ' . $address);
        }
        $ipv4 = \strlen($networkAddress) === 4;

        $maxBits = $ipv4 ? 32 : 128;

        if ($bits === null) {
            $bits = $maxBits;
        } else {
            if (!\preg_match('/^(0|[1-9]\d*)$/', $bits)) {
                throw new ValueError('Invalid CIDR prefix length: ' . $bits);
            }

            $bits = (int) $bits;
            if ($bits > $maxBits) {
                throw new ValueError(\sprintf('Invalid CIDR prefix length: %d, must be between 0 and %d', $bits, $maxBits));
            }
        }

        return [self::toIPv6($networkAddress), $bits + ($ipv4 ? 96 : 0)];
    }

    public function match(string $ip): bool
    {
        $inAddr = \inet_pton($ip);
        if (!$inAddr) {
            throw new ValueError('Invalid IP address: ' . $ip);
        }

        $networkAddress = self::toIPv6($inAddr);

        return ($networkAddress & $this->mask) === $this->address;
    }
}
<?php declare(strict_types=1);

namespace Amp\Socket;

/**
 * Thrown in case a second read operation is attempted while another receive operation is still pending.
 */
final class PendingReceiveError extends \Error
{
    public function __construct(
        string $message = 'The previous receive operation must complete before receive can be called again',
        int $code = 0,
        ?\Throwable $previous = null
    ) {
        parent::__construct($message, $code, $previous);
    }
}
<?php declare(strict_types=1);

namespace Amp\Socket;

use Amp\ByteStream\StreamException;
use Amp\Cancellation;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use League\Uri\Uri;

/** @api */
final class Socks5SocketConnector implements SocketConnector
{
    private const REPLIES = [
        0 => 'succeeded',
        1 => 'general SOCKS server failure',
        2 => 'connection not allowed by ruleset',
        3 => 'Network unreachable',
        4 => 'Host unreachable',
        5 => 'Connection refused',
        6 => 'TTL expired',
        7 => 'Command not supported',
        8 => 'Address type not supported'
    ];

    /**
     * @throws StreamException
     * @see https://datatracker.ietf.org/doc/html/rfc1928#section-3
     */
    private static function writeHello(?string $username, ?string $password, Socket $socket): void
    {
        $methods = \chr(0);
        if (isset($username) && isset($password)) {
            $methods .= \chr(2);
        }

        $socket->write(\chr(5) . \chr(\strlen($methods)) . $methods);
    }

    /**
     * @throws SocketException
     * @throws StreamException
     * @see https://datatracker.ietf.org/doc/html/rfc1928#section-4
     */
    private static function writeConnectRequest(Uri $uri, Socket $socket): void
    {
        $host = $uri->getHost();
        if ($host === null) {
            throw new SocketException("Host is null!");
        }

        $port = $uri->getPort();
        if ($port === null) {
            throw new SocketException("Port is null!");
        }

        $payload = \pack('C3', 0x5, 0x1, 0x0);

        // League returns IPv6 hosts in the bracketed IP-literal form (e.g. "[::1]"); strip the brackets so the
        // address is recognized and encoded as an IPv6 literal rather than a domain name.
        $ip = \inet_pton(\str_starts_with($host, '[') ? \substr($host, 1, -1) : $host);
        if ($ip !== false) {
            $payload .= \chr(\strlen($ip) === 4 ? 0x1 : 0x4) . $ip;
        } else {
            if (\strlen($host) > 255) {
                throw new SocketException("Host exceeds maximum length of 255 bytes: $host");
            }

            $payload .= \chr(0x3) . \chr(\strlen($host)) . $host;
        }

        $payload .= \pack('n', $port);

        $socket->write($payload);
    }

    use ForbidCloning;
    use ForbidSerialization;

    public static function tunnel(
        Socket $socket,
        string $target,
        ?string $username,
        ?string $password,
        ?Cancellation $cancellation
    ): void {
        if (($username === null) !== ($password === null)) {
            throw new \Error("Both or neither username and password must be provided!");
        }

        $scheme = \strstr($target, '://', true);
        $target = match ($scheme) {
            'tcp' => $target,
            false => 'tcp://' . $target,
            default => throw new \ValueError('Only tcp allowed for target scheme; got ' . $scheme),
        };

        try {
            $uri = Uri::new($target);
        } catch (\Exception $exception) {
            throw new SocketException('Invalid SOCKS5 target: ' . $target, previous: $exception);
        }

        $read = function (int $length) use ($socket, $cancellation): string {
            \assert($length > 0);

            $buffer = '';

            do {
                $limit = $length - \strlen($buffer);
                \assert($limit > 0);

                $chunk = $socket->read($cancellation, $limit);
                if ($chunk === null) {
                    throw new SocketException("The socket was closed before the tunnel could be established");
                }

                $buffer .= $chunk;
            } while (\strlen($buffer) !== $length);

            return $buffer;
        };

        self::writeHello($username, $password, $socket);

        $version = \ord($read(1));
        if ($version !== 5) {
            throw new SocketException("Wrong SOCKS5 version: $version");
        }

        $method = \ord($read(1));
        if ($method === 2) {
            if ($username === null || $password === null) {
                throw new SocketException("Unexpected method: $method");
            }

            $socket->write(
                \chr(1) .
                \chr(\strlen($username)) .
                $username .
                \chr(\strlen($password)) .
                $password
            );

            $version = \ord($read(1));
            if ($version !== 1) {
                throw new SocketException("Wrong authorized SOCKS version: $version");
            }

            $result = \ord($read(1));
            if ($result !== 0) {
                throw new SocketException("Wrong authorization status: $result");
            }
        } elseif ($method !== 0) {
            throw new SocketException("Unexpected method: $method");
        }

        self::writeConnectRequest($uri, $socket);

        $version = \ord($read(1));
        if ($version !== 5) {
            throw new SocketException("Wrong SOCKS5 version: $version");
        }

        $reply = \ord($read(1));
        if ($reply !== 0) {
            /** @psalm-suppress InvalidArrayOffset */
            $reply = self::REPLIES[$reply] ?? $reply;
            throw new SocketException("Wrong SOCKS5 reply: $reply");
        }

        $rsv = \ord($read(1));
        if ($rsv !== 0) {
            throw new SocketException("Wrong SOCKS5 RSV: $rsv");
        }

        $addressType = \ord($read(1));
        $read(match ($addressType) {
            0x1 => 6,
            0x4 => 18,
            0x3 => \ord($read(1)) + 2,
            default => throw new SocketException("Unsupported SOCKS5 address type: $addressType"),
        });
    }

    public function __construct(
        private readonly SocketAddress|string $proxyAddress,
        private readonly ?string $username = null,
        private readonly ?string $password = null,
        private readonly ?SocketConnector $socketConnector = null
    ) {
        if (($username === null) !== ($password === null)) {
            throw new \Error("Both or neither username and password must be provided!");
        }
    }

    #[\Override]
    public function connect(SocketAddress|string $uri, ?ConnectContext $context = null, ?Cancellation $cancellation = null): Socket
    {
        $connector = $this->socketConnector ?? socketConnector();

        $socket = $connector->connect($this->proxyAddress, $context, $cancellation);
        self::tunnel($socket, (string) $uri, $this->username, $this->password, $cancellation);

        return $socket;
    }
}
<?php declare(strict_types=1);

namespace Amp\Socket;

use Amp\Cancellation;
use Amp\CancelledException;
use League\Uri\UriString;
use Revolt\EventLoop;

/**
 * Listen for client connections on the specified server address.
 *
 * If you want to accept TLS connections, you have to use `yield $socket->setupTls()` after accepting new clients.
 *
 * @param SocketAddress|string $address URI in scheme://host:port format. TCP is assumed if no scheme is present.
 * @param BindContext|null $bindContext Context options for listening.
 * @param positive-int $chunkSize Chunk size for the accepted sockets.
 *
 * @throws SocketException If binding to the specified URI failed.
 */
function listen(
    SocketAddress|string $address,
    ?BindContext $bindContext = null,
    int $chunkSize = ResourceSocket::DEFAULT_CHUNK_SIZE
): ResourceServerSocket {
    return (new ResourceServerSocketFactory($chunkSize))->listen($address, $bindContext);
}

/**
 * Create a new Datagram (UDP server) on the specified server address.
 *
 * @param InternetAddress|string $address URI in scheme://host:port format. UDP is assumed if no scheme is present.
 * @param BindContext|null $bindContext Context options for listening.
 * @param positive-int $limit Maximum size for received messages.
 *
 * @throws SocketException If binding to the specified URI failed.
 */
function bindUdpSocket(
    InternetAddress|string $address,
    ?BindContext $bindContext = null,
    int $limit = ResourceUdpSocket::DEFAULT_LIMIT
): ResourceUdpSocket {
    $bindContext = $bindContext ?? new BindContext;

    $uri = (string) $address;
    $uri = match (\strstr($uri, '://', true)) {
        'udp' => $uri,
        false => 'udp://' . $uri,
        default => throw new \ValueError('Only udp scheme allowed for datagram creation; got ' . $uri),
    };

    $streamContext = \stream_context_create($bindContext->toStreamContextArray());

    \set_error_handler(static fn () => true); // Error checked after call to stream_socket_server().

    try {
        $server = \stream_socket_server($uri, $errno, $errstr, STREAM_SERVER_BIND, $streamContext);
    } finally {
        \restore_error_handler();
    }

    if (!$server || $errno) {
        throw new SocketException(
            \sprintf('Could not create datagram %s: [Error: #%d] %s', $uri, $errno, $errstr),
            $errno
        );
    }

    return new ResourceUdpSocket($server, $limit);
}

/**
 * Set or access the global SocketConnector instance.
 */
function socketConnector(?SocketConnector $connector = null): SocketConnector
{
    static $map;
    $map ??= new \WeakMap();
    $driver = EventLoop::getDriver();

    if ($connector) {
        return $map[$driver] = $connector;
    }

    return $map[$driver] ??= new RetrySocketConnector(new DnsSocketConnector());
}

/**
 * Establish a socket connection to the specified URI.
 *
 * @param SocketAddress|string $uri URI in scheme://host:port format. TCP is assumed if no scheme is present.
 * @param ConnectContext|null $context Socket connect context to use when connecting.
 *
 * @throws ConnectException
 * @throws CancelledException
 */
function connect(SocketAddress|string $uri, ?ConnectContext $context = null, ?Cancellation $cancellation = null): Socket
{
    return socketConnector()->connect($uri, $context, $cancellation);
}

/**
 * Establish a socket connection to the specified URI and enable TLS.
 *
 * @param SocketAddress|string $uri URI in scheme://host:port format. TCP is assumed if no scheme is present.
 * @param ConnectContext|null $context Socket connect context to use when connecting.
 *
 * @throws ConnectException
 * @throws TlsException
 * @throws CancelledException
 */
function connectTls(SocketAddress|string $uri, ?ConnectContext $context = null, ?Cancellation $cancellation = null): Socket
{
    $context ??= new ConnectContext();
    $tlsContext = $context->getTlsContext() ?? new ClientTlsContext('');

    if ($tlsContext->getPeerName() === '') {
        $hostname = '';
        $uriString = (string) $uri;
        if (\str_contains($uriString, 'tcp://')) {
            $hostname = UriString::parse($uriString)['host'] ?? '';
        } elseif (!\str_contains($uriString, '://')) {
            $hostname = UriString::parse('tcp://' . $uriString)['host'] ?? '';
        }

        $tlsContext = $tlsContext->withPeerName($hostname);
    }

    $socket = socketConnector()->connect($uri, $context->withTlsContext($tlsContext), $cancellation);
    $socket->setupTls($cancellation);

    return $socket;
}

/**
 * Returns a pair of connected stream socket resources.
 *
 * @param positive-int $chunkSize
 *
 * @return array{ResourceSocket, ResourceSocket} Pair of socket resources.
 *
 * @throws SocketException If creating the sockets fails.
 */
function createSocketPair(int $chunkSize = ResourceSocket::DEFAULT_CHUNK_SIZE): array
{
    try {
        \set_error_handler(static function (int $errno, string $errstr): void {
            throw new SocketException(\sprintf('Failed to create socket pair.  Errno: %d; %s', $errno, $errstr));
        });

        $sockets = \stream_socket_pair(
            \PHP_OS_FAMILY === 'Windows' ? \STREAM_PF_INET : \STREAM_PF_UNIX,
            \STREAM_SOCK_STREAM,
            \STREAM_IPPROTO_IP,
        );
        if ($sockets === false) {
            throw new SocketException('Failed to create socket pair.');
        }
    } finally {
        \restore_error_handler();
    }

    return [
        ResourceSocket::fromClientSocket($sockets[0], chunkSize: $chunkSize),
        ResourceSocket::fromClientSocket($sockets[1], chunkSize: $chunkSize),
    ];
}

/**
 * @see https://wiki.openssl.org/index.php/Manual:OPENSSL_VERSION_NUMBER(3)
 */
function hasTlsAlpnSupport(): bool
{
    return \defined('OPENSSL_VERSION_NUMBER') && \OPENSSL_VERSION_NUMBER >= 0x10002000;
}

function hasTlsSecurityLevelSupport(): bool
{
    return \defined('OPENSSL_VERSION_NUMBER') && \OPENSSL_VERSION_NUMBER >= 0x10100000;
}
<?php declare(strict_types=1);

namespace Amp\Socket;

interface ServerSocketFactory
{
    /**
     * @throws SocketException
     */
    public function listen(SocketAddress|string $address, ?BindContext $bindContext = null): ServerSocket;
}
<?php declare(strict_types=1);

namespace Amp\Socket;

enum SocketAddressType
{
    case Internet;
    case Unix;
}
{
    "name": "amphp/socket",
    "homepage": "https://github.com/amphp/socket",
    "description": "Non-blocking socket connection / server implementations based on Amp and Revolt.",
    "support": {
        "issues": "https://github.com/amphp/socket/issues"
    },
    "keywords": [
        "tcp",
        "sockets",
        "tls",
        "encryption",
        "async",
        "non-blocking",
        "amp"
    ],
    "license": "MIT",
    "authors": [
        {
            "name": "Daniel Lowrey",
            "email": "rdlowrey@gmail.com"
        },
        {
            "name": "Aaron Piotrowski",
            "email": "aaron@trowski.com"
        },
        {
            "name": "Niklas Keller",
            "email": "me@kelunik.com"
        }
    ],
    "require": {
        "php": ">=8.1",
        "ext-openssl": "*",
        "amphp/amp": "^3",
        "amphp/dns": "^2",
        "amphp/byte-stream": "^2",
        "kelunik/certificate": "^1.1",
        "league/uri": "^7",
        "league/uri-interfaces": "^7",
        "revolt/event-loop": "^1"
    },
    "require-dev": {
        "phpunit/phpunit": "^9",
        "amphp/phpunit-util": "^3",
        "amphp/php-cs-fixer-config": "^2",
        "amphp/process": "^2",
        "psalm/phar": "6.16.1"
    },
    "autoload": {
        "psr-4": {
            "Amp\\Socket\\": "src"
        },
        "files": [
            "src/functions.php",
            "src/Internal/functions.php",
            "src/SocketAddress/functions.php"
        ]
    },
    "autoload-dev": {
        "psr-4": {
            "Amp\\Socket\\": "test"
        }
    },
    "scripts": {
        "check": [
            "@cs",
            "@test"
        ],
        "cs": "PHP_CS_FIXER_IGNORE_ENV=1 php-cs-fixer fix -v --diff --dry-run",
        "cs-fix": "PHP_CS_FIXER_IGNORE_ENV=1 php-cs-fixer fix -v --diff",
        "test": "@php -dzend.assertions=1 -dassert.exception=1 ./vendor/bin/phpunit --coverage-text"
    }
}
<?php

$config = new Amp\CodeStyle\Config();
$config->getFinder()
    ->in(__DIR__ . '/examples')
    ->in(__DIR__ . '/src')
    ->in(__DIR__ . '/test');

$config->setCacheFile(__DIR__ . '/.php_cs.cache');

return $config;
{
    "symbol-whitelist": [
        "null",
        "true",
        "false",
        "static",
        "self",
        "parent",
        "array",
        "string",
        "int",
        "float",
        "bool",
        "iterable",
        "callable",
        "mixed",
        "void",
        "object",
        "fromIterable",
        "array_is_list"
    ],
    "php-core-extensions": [
        "Core",
        "date",
        "pcre",
        "Phar",
        "Reflection",
        "SPL",
        "standard"
    ]
}
<?php declare(strict_types=1);

namespace Amp\Pipeline;

use Amp\Cancellation;
use Amp\Pipeline\Internal\ConcurrentArrayIterator;
use Amp\Pipeline\Internal\ConcurrentChainedIterator;
use Amp\Pipeline\Internal\ConcurrentClosureIterator;
use Amp\Pipeline\Internal\ConcurrentIterableIterator;
use Amp\Pipeline\Internal\ConcurrentMergedIterator;
use Amp\Pipeline\Internal\FlatMapOperation;
use Amp\Pipeline\Internal\IntermediateOperation;
use Amp\Pipeline\Internal\Sequence;
use Amp\Pipeline\Internal\SortOperation;
use function Amp\delay;

/**
 * A pipeline represents an asynchronous set and provides operations which can be applied over the set.
 *
 * @template T
 * @template-implements \IteratorAggregate<int, T>
 */
final class Pipeline implements \IteratorAggregate
{
    /**
     * Creates a pipeline from the given iterable or closure returning an iterable.
     *
     * @template Ts
     *
     * @param (\Closure():iterable<Ts>)|iterable<Ts> $iterable
     *
     * @return self<Ts>
     */
    public static function fromIterable(\Closure|iterable $iterable): self
    {
        if ($iterable instanceof \Closure) {
            $iterable = $iterable();

            if (!\is_iterable($iterable)) {
                throw new \TypeError('Return value of argument #1 ($iterable) must be of type iterable, ' . \get_debug_type($iterable) . ' returned');
            }
        }

        if ($iterable instanceof self) {
            return $iterable;
        }

        if ($iterable instanceof ConcurrentIterator) {
            return new self($iterable);
        }

        if (\is_array($iterable)) {
            return new self(new ConcurrentArrayIterator($iterable));
        }

        return new self(new ConcurrentIterableIterator($iterable));
    }

    /**
     * Creates an infinite pipeline from the given closure invoking it repeatedly for each value.
     *
     * @template Ts
     *
     * @param \Closure(Cancellation): Ts $supplier Elements to emit.
     *
     * @return self<Ts>
     */
    public static function generate(\Closure $supplier): self
    {
        return new self(new ConcurrentClosureIterator($supplier));
    }

    /**
     * Merges the given iterables into a single pipeline. The returned pipeline emits a value anytime one of the
     * merged iterables produces a value.
     *
     * @template Ts
     *
     * @param array<iterable<Ts>> $pipelines
     *f
     * @return self<Ts>
     */
    public static function merge(array $pipelines): self
    {
        return new self(new ConcurrentMergedIterator(self::mapToConcurrentIterators($pipelines)));
    }

    /**
     * Concatenates the given iterables into a single pipeline in sequential order.
     *
     * The prior pipeline must complete before values are taken from any subsequent pipelines.
     *
     * @template Ts
     *
     * @param array<iterable<Ts>> $pipelines
     *
     * @return self<Ts>
     */
    public static function concat(array $pipelines): self
    {
        return new self(new ConcurrentChainedIterator(self::mapToConcurrentIterators($pipelines)));
    }

    /**
     * @template Tk of array-key
     * @template Ts
     *
     * @param array<Tk, iterable<Ts>> $iterables
     *
     * @return array<Tk, ConcurrentIterator<Ts>>
     */
    private static function mapToConcurrentIterators(array $iterables): array
    {
        foreach ($iterables as $key => $iterable) {
            if (!\is_iterable($iterable)) {
                throw new \TypeError(\sprintf(
                    'Argument #1 ($pipelines) must be of type array<iterable>, %s given at key %s',
                    \get_debug_type($iterable),
                    $key,
                ));
            }
        }

        return \array_map(static fn (iterable $pipeline) => self::fromIterable($pipeline)->getIterator(), $iterables);
    }

    /** @var non-negative-int */
    private int $bufferSize = 0;

    /** @var positive-int */
    private int $concurrency = 1;

    private bool $ordered = true;

    /** @var list<IntermediateOperation> */
    private array $intermediateOperations = [];

    private bool $used = false;

    /**
     * @param ConcurrentIterator<T> $source
     */
    public function __construct(
        private readonly ConcurrentIterator $source,
    ) {
    }

    public function __destruct()
    {
        if (!$this->used) {
            $this->source->dispose();
        }
    }

    public function buffer(int $bufferSize): static
    {
        if ($bufferSize < 0) {
            throw new \ValueError('Argument #1 ($bufferSize) must be non-negative, got ' . $bufferSize);
        }

        $this->bufferSize = $bufferSize;

        return $this;
    }

    public function concurrent(int $concurrency): static
    {
        if ($concurrency < 1) {
            throw new \ValueError('Argument #1 ($concurrency) must be positive, got ' . $concurrency);
        }

        $this->concurrency = $concurrency;

        return $this;
    }

    public function sequential(): static
    {
        return $this->concurrent(1);
    }

    public function ordered(): static
    {
        $this->ordered = true;

        return $this;
    }

    public function unordered(): static
    {
        $this->ordered = false;

        return $this;
    }

    /**
     * @psalm-suppress PossiblyUnusedReturnValue
     */
    public function count(): int
    {
        $count = 0;

        foreach ($this as $_) {
            $count++;
        }

        return $count;
    }

    /**
     * @template R
     *
     * @param null|\Closure(T, T): int $compare
     * @param R $default
     *
     * @return T|R
     */
    public function min(?\Closure $compare = null, mixed $default = null): mixed
    {
        $compare ??= static fn (mixed $a, mixed $b): int => $a <=> $b;
        $min = $default;
        $first = true;

        foreach ($this as $value) {
            if ($first) {
                $first = false;
                $min = $value;
            } else {
                /** @var T $min */
                $comparison = $compare($min, $value);
                if ($comparison > 0) {
                    $min = $value;
                }
            }
        }

        return $min;
    }

    /**
     * @template R
     *
     * @param null|\Closure(T, T): int $compare
     * @param R $default
     *
     * @return T|R
     */
    public function max(?\Closure $compare = null, mixed $default = null): mixed
    {
        $compare ??= static fn (mixed $a, mixed $b): int => $a <=> $b;
        $max = $default;
        $first = true;

        foreach ($this as $value) {
            if ($first) {
                $first = false;
                $max = $value;
            } else {
                /** @var T $max */
                $comparison = $compare($max, $value);
                if ($comparison < 0) {
                    $max = $value;
                }
            }
        }

        return $max;
    }

    /**
     * @param \Closure(T): bool $predicate
     */
    public function allMatch(\Closure $predicate): bool
    {
        foreach ($this->map($predicate) as $value) {
            if (!$value) {
                return false;
            }
        }

        return true;
    }

    /**
     * @param \Closure(T): bool $predicate
     */
    public function anyMatch(\Closure $predicate): bool
    {
        foreach ($this->map($predicate) as $value) {
            if ($value) {
                return true;
            }
        }

        return false;
    }

    /**
     * @param \Closure(T): bool $predicate
     */
    public function noneMatch(\Closure $predicate): bool
    {
        foreach ($this->map($predicate) as $value) {
            if ($value) {
                return false;
            }
        }

        return true;
    }

    /**
     * Invokes the given callback for each value emitted on the pipeline.
     *
     * @param \Closure(T):void $forEach
     */
    public function forEach(\Closure $forEach): void
    {
        $this->tap($forEach)->count();
    }

    /**
     * Collects all items into an array.
     *
     * @return list<T>
     */
    public function toArray(): array
    {
        return \iterator_to_array($this, false);
    }

    /**
     * Sorts values, requires buffering all values.
     *
     * @param null|\Closure(T, T):int $compare
     */
    public function sorted(?\Closure $compare = null): static
    {
        if ($this->used) {
            throw new \Error('Pipeline consumption has already been started');
        }

        $compare ??= static fn (mixed $a, mixed $b): int => $a <=> $b;
        $this->intermediateOperations[] = new SortOperation($compare);

        return $this->ordered();
    }

    /**
     * Maps values, flattening one level.
     *
     * @template R
     *
     * @param \Closure(T, int):iterable<R> $flatMap
     *
     * @return self<R>
     */
    public function flatMap(\Closure $flatMap): self
    {
        if ($this->used) {
            throw new \Error('Pipeline consumption has already been started');
        }

        $this->intermediateOperations[] = new FlatMapOperation(
            $this->bufferSize,
            $this->concurrency,
            $this->ordered,
            $flatMap,
        );

        /** @var self<R> */
        return $this;
    }

    /**
     * Maps values.
     *
     * @template R
     *
     * @param \Closure(T):R $map
     *
     * @return self<R>
     */
    public function map(\Closure $map): self
    {
        return $this->flatMap(static fn (mixed $value) => [$map($value)]);
    }

    /**
     * Filters values.
     *
     * @param \Closure(T):bool $filter Keep value if {@code $filter} returns {@code true}.
     */
    public function filter(\Closure $filter): static
    {
        return $this->flatMap(static fn (mixed $value) => $filter($value) ? [$value] : []);
    }

    /**
     * Invokes the given function each time a value is streamed through the pipeline to perform side effects.
     *
     * @param \Closure(T):void $tap
     */
    public function tap(\Closure $tap): static
    {
        return $this->flatMap(static function (mixed $value) use ($tap): array {
            $tap($value);

            return [$value];
        });
    }

    /**
     * @template R
     *
     * @param \Closure(R, T): R $accumulator
     * @param R $initial
     *
     * @return R
     */
    public function reduce(\Closure $accumulator, mixed $initial = null): mixed
    {
        $result = $initial;

        foreach ($this as $value) {
            $result = $accumulator($result, $value);
        }

        return $result;
    }

    /**
     * Delays each item by $delay seconds.
     */
    public function delay(float $delay): static
    {
        return $this->tap(static fn () => delay($delay));
    }

    /**
     * Skip the first N items of the pipeline.
     */
    public function skip(int $count): static
    {
        return $this->flatMap(static function (mixed $value) use ($count): array {
            static $i = 0;

            if ($i++ < $count) {
                return [];
            }

            return [$value];
        });
    }

    /**
     * Skips values on the pipeline until {@code $predicate} returns {@code false}.
     *
     * All values are emitted afterwards without invoking {@code $predicate}.
     *
     * @param \Closure(T):bool $predicate
     */
    public function skipWhile(\Closure $predicate): static
    {
        return $this->flatMap(
            static function (mixed $value, int $position) use ($predicate): array {
                static $sequence = new Sequence();
                static $skipping = true;

                if (!$skipping) {
                    return [$value];
                }

                $predicateResult = $predicate($value);

                $sequence->barrier($position);

                /** @psalm-suppress RedundantCondition $skipping may be modified by a concurrent call */
                if ($skipping && $predicateResult) {
                    return [];
                }

                $skipping = false;

                return [$value];
            }
        );
    }

    /**
     * Take only the first N items of the pipeline.
     */
    public function take(int $count): static
    {
        return $this->flatMap(static function (mixed $value) use ($count): array {
            static $i = 0;

            if (++$i < $count) {
                return [$value];
            }

            /** @var T $stopMarker Fake stop marker as type T. */
            $stopMarker = FlatMapOperation::getStopMarker();

            if ($i === $count) {
                return [$value, $stopMarker];
            }

            return [$stopMarker];
        });
    }

    /**
     * Takes values on the pipeline until {@code $predicate} returns {@code false}.
     *
     * @param \Closure(T):bool $predicate
     */
    public function takeWhile(\Closure $predicate): static
    {
        return $this->flatMap(
            static function (mixed $value, int $position) use ($predicate): array {
                static $sequence = new Sequence();
                static $taking = true;

                if (!$taking) {
                    return [FlatMapOperation::getStopMarker()];
                }

                $predicateResult = $predicate($value);

                $sequence->barrier($position);

                /** @psalm-suppress RedundantCondition $taking may be modified by a concurrent call */
                if ($taking && $predicateResult) {
                    return [$value];
                }

                $taking = false;

                /** @var T[] */
                return [FlatMapOperation::getStopMarker()];
            }
        );
    }

    /**
     * @return ConcurrentIterator<T>
     */
    #[\Override]
    public function getIterator(): ConcurrentIterator
    {
        if ($this->used) {
            throw new \Error('Pipelines can\'t be reused after a terminal operation');
        }

        $this->used = true;

        $source = $this->source;

        foreach ($this->intermediateOperations as $intermediateOperation) {
            $source = $intermediateOperation($source);
        }

        return $source;
    }

    /**
     * Disposes the source of the pipeline, indicating the consumer is no longer interested in the pipeline output.
     * Producers pushing to the source will receive a {@see DisposedException}.
     */
    public function dispose(): void
    {
        $this->source->dispose();
    }
}
<?php declare(strict_types=1);

namespace Amp\Pipeline;

/**
 * Will be thrown from {@see Queue::push()} or used to fail the future returned from {@see Queue::pushAsync()}
 * if the associated iterator is disposed.
 */
final class DisposedException extends \Exception
{
    public function __construct(?\Throwable $previous = null)
    {
        parent::__construct("The iterator has been disposed", 0, $previous);
    }
}
<?php declare(strict_types=1);

namespace Amp\Pipeline;

use Amp\Cancellation;

/**
 * @template-covariant T
 * @template-extends \IteratorAggregate<int, T>
 */
interface ConcurrentIterator extends \IteratorAggregate
{
    /**
     * Advances the iterator to the next position and value for the current fiber.
     *
     * The position and value must be available via {@see getPosition()} and {@see getValue()} to the fiber calling
     * {@see continue()} only.
     *
     * A fiber calling {@see continue()} must not affect the position or value of other fibers.
     *
     * If the iterator errors, the exception will be thrown from this method.
     *
     * @param Cancellation|null $cancellation Cancels waiting for the next value. If cancelled, the next value is not
     *     lost, but will be available to the next call to this method.
     *
     * @return bool `true` if a value is available, `false` if the iterator has completed.
     */
    public function continue(?Cancellation $cancellation = null): bool;

    /**
     * Returns the current value of the iterator for the current fiber.
     *
     * Advance the iterator to the next value using {@see continue()}, which must be called before this method may be
     * called for each value.
     *
     * @return T The current value of the iterator. If the iterator has completed or {@see continue()} has
     * not been called, an {@see \Error} will be thrown.
     */
    public function getValue(): mixed;

    /**
     * Returns the current position of the iterator for the current fiber.
     *
     * Advance the iterator to the next position using {@see continue()}, which must be called before this method may be
     * called for each position.
     *
     * @return int The current position of the iterator. If the iterator has completed or {@see continue()} has
     * not been called, an {@see \Error} will be thrown.
     */
    public function getPosition(): int;

    /**
     * @return bool `true` if the iterator has completed (either successfully or with an error) or `false`
     * if the iterator may still emit more values.
     */
    public function isComplete(): bool;

    /**
     * Disposes the iterator, indicating the consumer is no longer interested in the iterator output.
     *
     * Disposal applies only to this iterator. An iterator derived from another iterator does not dispose of its
     * source iterator, as the source may be shared with other consumers. Producers pushing to an abandoned source
     * remain suspended until the source itself is disposed, either explicitly (e.g. {@see Pipeline::dispose()})
     * or by garbage collection.
     */
    public function dispose(): void;

    /**
     * @return \Traversable<int, T> Returns an iterator with {@see getPosition()} as key and {@see getValue()} as
     *     value. Multiple calls must be allowed to allow for concurrent iteration.
     */
    #[\Override]
    public function getIterator(): \Traversable;
}
<?php declare(strict_types=1);

namespace Amp\Pipeline;

use Amp\Future;
use Amp\Pipeline\Internal\ConcurrentQueueIterator;

/**
 * Queue is an ordered sequence of values with support for concurrent consumption.
 *
 * {@see complete()} can be used to signal completeness of the queue, no new items will be queued.
 *
 * {@see error()} can be used to signal errors in the queue, no new items can be queued.
 *
 * @template T
 */
final class Queue
{
    /** @var Internal\QueueState<T> Has public emit, complete, and fail methods. */
    private readonly Internal\QueueState $state;

    /**
     * @param int $bufferSize Allowed number of items to internally buffer before awaiting backpressure from the
     * consumer of the queue.
     */
    public function __construct(int $bufferSize = 0)
    {
        $this->state = new Internal\QueueState($bufferSize);
    }

    /**
     * Returns a {@see Pipeline} to consume the queue.
     *
     * @return Pipeline<T>
     */
    public function pipe(): Pipeline
    {
        return new Pipeline($this->iterate());
    }

    /**
     * Returns a {@see ConcurrentIterator} to consume the queue.
     *
     * @return ConcurrentIterator<T>
     */
    public function iterate(): ConcurrentIterator
    {
        return new ConcurrentQueueIterator($this->state);
    }

    /**
     * Enqueues a value to the queue, returning a future that is completed once the value is inserted into the buffer
     * or consumed in case of an unbuffered queue.
     *
     * {@see await()} the {@see Future} returned at a later time, or use {@see push()} to await the value being
     * inserted into the buffer or consumed immediately.
     *
     * @param T $value
     *
     * @return Future<null> Completes with null when the emitted value has been consumed or errors with
     *                       {@see DisposedException} if the queue has been disposed.
     */
    public function pushAsync(mixed $value): Future
    {
        return $this->state->pushAsync($value);
    }

    /**
     * Pushes a value to the buffer or waits until the value is consumed if the buffer is full or the queue is
     * unbuffered.
     *
     * Use {@see pushAsync()} to push a value without waiting for consumption or free buffer space.
     *
     * @param T $value
     *
     * @throws DisposedException Thrown if the queue is disposed.
     */
    public function push(mixed $value): void
    {
        $this->state->push($value);
    }

    /**
     * @return bool True if the queue has been completed or errored.
     */
    public function isComplete(): bool
    {
        return $this->state->isComplete();
    }

    /**
     * @return bool True if the queue has been disposed.
     */
    public function isDisposed(): bool
    {
        return $this->state->isDisposed();
    }

    /**
     * Completes the queue.
     *
     * Values remaining in the buffer will still be delivered to consumers. Producers awaiting backpressure
     * continue to wait until their value is consumed or the queue is disposed.
     */
    public function complete(): void
    {
        $this->state->complete();
    }

    /**
     * Errors the queue with the given reason.
     *
     * Values remaining in the buffer will still be delivered to consumers before the given reason is thrown
     * from the iterator. Producers awaiting backpressure continue to wait until their value is consumed or
     * the queue is disposed.
     */
    public function error(\Throwable $reason): void
    {
        $this->state->error($reason);
    }
}
<?php declare(strict_types=1);

namespace Amp\Pipeline\Internal;

use Amp\Cancellation;
use Amp\Pipeline\ConcurrentIterator;
use Revolt\EventLoop\FiberLocal;

/**
 * Concatenates the given iterators into a single iterator in sequential order.
 *
 * The prior iterator must complete before values are taken from any subsequent iterators.
 *
 * @internal
 *
 * @template-covariant T
 * @template-implements ConcurrentIterator<T>
 */
final class ConcurrentChainedIterator implements ConcurrentIterator
{
    /** @var ConcurrentIterator<T>[] */
    private readonly array $iterators;

    /** @var FiberLocal<int|null> */
    private readonly FiberLocal $position;

    /**
     * @param ConcurrentIterator<T>[] $iterators
     */
    public function __construct(array $iterators)
    {
        foreach ($iterators as $key => $iterator) {
            if (!$iterator instanceof ConcurrentIterator) {
                throw new \TypeError(\sprintf(
                    'Argument #1 ($iterators) must be of type array<%s>, %s given at key %s',
                    ConcurrentIterator::class,
                    \get_debug_type($iterator),
                    $key
                ));
            }
        }

        $this->iterators = \array_values($iterators);
        $this->position = new FiberLocal(static fn () => 0);
    }

    #[\Override]
    public function continue(?Cancellation $cancellation = null): bool
    {
        $position = $this->position->get();
        if ($position === null) {
            return false;
        }

        while (isset($this->iterators[$position])) {
            if ($this->iterators[$position]->continue($cancellation)) {
                return true;
            }

            $this->position->set(++$position);
        }

        $this->position->set(null);

        return false;
    }

    #[\Override]
    public function getValue(): mixed
    {
        $position = $this->position->get();
        if ($position === null) {
            throw new \Error('No value available anymore, check continue() return value');
        }

        return $this->iterators[$position]->getValue();
    }

    #[\Override]
    public function getPosition(): int
    {
        $position = $this->position->get();
        if ($position === null) {
            throw new \Error('No value available anymore, check continue() return value');
        }

        return $this->iterators[$position]->getPosition();
    }

    #[\Override]
    public function isComplete(): bool
    {
        return $this->position->get() === null;
    }

    #[\Override]
    public function dispose(): void
    {
        foreach ($this->iterators as $iterator) {
            $iterator->dispose();
        }
    }

    #[\Override]
    public function getIterator(): \Traversable
    {
        while ($this->continue()) {
            yield $this->getPosition() => $this->getValue();
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\Pipeline\Internal;

use Amp\Cancellation;
use Amp\DeferredCancellation;
use Amp\Future;
use Amp\Pipeline\ConcurrentIterator;
use Amp\Pipeline\Queue;
use Revolt\EventLoop;
use function Amp\async;

/**
 * @internal
 *
 * @template-covariant T
 * @template-implements ConcurrentIterator<T>
 */
final class ConcurrentMergedIterator implements ConcurrentIterator
{
    /** @var ConcurrentIterator<T> */
    private readonly ConcurrentIterator $iterator;

    private readonly DeferredCancellation $deferredCancellation;

    /**
     * @param ConcurrentIterator<T>[] $iterators
     */
    public function __construct(array $iterators)
    {
        foreach ($iterators as $key => $iterator) {
            if (!$iterator instanceof ConcurrentIterator) {
                throw new \TypeError(\sprintf(
                    'Argument #1 ($iterators) must be of type array<%s>, %s given at key %s',
                    ConcurrentIterator::class,
                    \get_debug_type($iterator),
                    $key
                ));
            }
        }

        $queue = new Queue(\count($iterators));
        $this->iterator = $queue->iterate();

        $this->deferredCancellation = $deferredCancellation = new DeferredCancellation();
        $cancellation = $this->deferredCancellation->getCancellation();

        $futures = [];
        foreach ($iterators as $iterator) {
            $futures[] = async(static function () use ($iterator, $queue, $cancellation): void {
                try {
                    while ($iterator->continue($cancellation)) {
                        if ($queue->isComplete()) {
                            return;
                        }

                        $queue->push($iterator->getValue());
                    }
                } finally {
                    $iterator->dispose();
                }
            });
        }

        EventLoop::queue(static function () use ($futures, $queue, $deferredCancellation): void {
            try {
                Future\await($futures);
                $queue->complete();
            } catch (\Throwable $exception) {
                $queue->error($exception);
            } finally {
                $deferredCancellation->cancel();
            }
        });
    }

    #[\Override]
    public function continue(?Cancellation $cancellation = null): bool
    {
        return $this->iterator->continue($cancellation);
    }

    #[\Override]
    public function getValue(): mixed
    {
        return $this->iterator->getValue();
    }

    #[\Override]
    public function getPosition(): int
    {
        return $this->iterator->getPosition();
    }

    #[\Override]
    public function isComplete(): bool
    {
        return $this->iterator->isComplete();
    }

    #[\Override]
    public function dispose(): void
    {
        $this->iterator->dispose();
        $this->deferredCancellation->cancel();
    }

    #[\Override]
    public function getIterator(): \Traversable
    {
        return $this->iterator;
    }
}
<?php declare(strict_types=1);

namespace Amp\Pipeline\Internal;

use Amp\Pipeline\ConcurrentIterator;

/** @internal */
interface IntermediateOperation
{
    public function __invoke(ConcurrentIterator $source): ConcurrentIterator;
}
<?php declare(strict_types=1);

namespace Amp\Pipeline\Internal;

use Amp\Cancellation;
use Amp\CancelledException;
use Amp\DeferredCancellation;
use Amp\Pipeline\ConcurrentIterator;
use Amp\Pipeline\DisposedException;
use Revolt\EventLoop;
use Revolt\EventLoop\Suspension;

/**
 * @internal
 *
 * @template-covariant T
 * @template-implements ConcurrentIterator<T>
 */
final class ConcurrentClosureIterator implements ConcurrentIterator
{
    /** @var \SplQueue<Suspension<int>> */
    private readonly \SplQueue $sources;

    /** @var QueueState<T> */
    private readonly QueueState $queue;

    private readonly Sequence $sequence;

    private readonly DeferredCancellation $deferredCancellation;

    private int $cancellations = 0;

    private int $position = 0;

    /**
     * @param \Closure(Cancellation):T $supplier
     */
    public function __construct(private readonly \Closure $supplier)
    {
        $this->sequence = $sequence = new Sequence();
        $this->queue = new QueueState();
        $this->sources = $sources = new \SplQueue();
        $this->deferredCancellation = new DeferredCancellation();

        $this->deferredCancellation->getCancellation()->subscribe(static function () use ($sources, $sequence): void {
            while (!$sources->isEmpty()) {
                $sources->dequeue();
            }

            $sequence->dispose();
        });
    }

    #[\Override]
    public function continue(?Cancellation $cancellation = null): bool
    {
        if ($this->queue->isComplete()) {
            return $this->queue->continue($cancellation);
        }

        if ($this->cancellations) {
            // A previous cancellation left a supplier callback awaiting a value, so skip enqueuing another callback.
            --$this->cancellations;
        } elseif ($this->sources->isEmpty()) {
            $queue = $this->queue;
            $sources = $this->sources;
            $sequence = $this->sequence;
            $supplier = $this->supplier;
            $deferredCancellation = $this->deferredCancellation;
            EventLoop::queue(static function (int $position) use (
                $queue,
                $sources,
                $sequence,
                $supplier,
                $deferredCancellation
            ): void {
                $suspension = EventLoop::getSuspension();

                do {
                    try {
                        $value = $supplier($deferredCancellation->getCancellation());
                    } catch (\Throwable $exception) {
                        $sequence->await($position);
                        if (!$queue->isComplete()) {
                            $queue->error($exception);
                            $deferredCancellation->cancel($exception);
                        }

                        return;
                    }

                    $sequence->await($position);

                    try {
                        if (!$queue->isComplete()) {
                            $queue->push($value);
                        }
                    } catch (DisposedException $exception) {
                        $deferredCancellation->cancel($exception);
                        return;
                    } finally {
                        $sequence->resume($position);
                    }

                    // Make this fiber available for reuse only once it is suspended below, otherwise a concurrent
                    // continue() may resume the suspension while suspended in Sequence::await() or QueueState::push().
                    $sources->enqueue($suspension);
                } while ($position = $suspension->suspend());
            }, $this->position++);
        } else {
            $suspension = $this->sources->dequeue();
            $suspension->resume($this->position++);
        }

        try {
            return $this->queue->continue($cancellation);
        } catch (CancelledException $exception) {
            // The next call to continue() will consume the pending value.
            ++$this->cancellations;
            throw $exception;
        }
    }

    #[\Override]
    public function getValue(): mixed
    {
        return $this->queue->getValue();
    }

    #[\Override]
    public function getPosition(): int
    {
        return $this->queue->getPosition();
    }

    #[\Override]
    public function isComplete(): bool
    {
        return $this->queue->isConsumed() || $this->queue->isDisposed();
    }

    #[\Override]
    public function dispose(): void
    {
        $this->queue->dispose();
        $this->deferredCancellation->cancel();
    }

    #[\Override]
    public function getIterator(): \Traversable
    {
        while ($this->continue()) {
            yield $this->getPosition() => $this->getValue();
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\Pipeline\Internal;

use Amp\Cancellation;
use Amp\Pipeline\ConcurrentIterator;
use Amp\Pipeline\DisposedException;
use Revolt\EventLoop\FiberLocal;

/**
 * @internal
 *
 * @template-covariant T
 * @template-implements ConcurrentIterator<T>
 */
final class ConcurrentArrayIterator implements ConcurrentIterator
{
    private int $position = 0;
    private readonly int $size;

    private readonly array $values;

    /** @var FiberLocal<int|null> */
    private readonly FiberLocal $currentPosition;

    private ?DisposedException $disposed = null;

    /**
     * @param array<T> $values
     */
    public function __construct(array $values)
    {
        $this->values = \array_values($values);
        $this->size = \count($values);
        $this->currentPosition = new FiberLocal(
            static fn () => throw new \Error('Call continue() before calling get()')
        );
    }

    #[\Override]
    public function continue(?Cancellation $cancellation = null): bool
    {
        if ($this->disposed) {
            throw $this->disposed;
        }

        $position = $this->position++;
        if ($position < $this->size) {
            $this->currentPosition->set($position);
            return true;
        }

        $this->currentPosition->set(null);
        return false;
    }

    #[\Override]
    public function getValue(): mixed
    {
        $position = $this->currentPosition->get();
        if ($position === null) {
            throw new \Error('continue() returned false, no value available afterwards');
        }

        return $this->values[$position];
    }

    #[\Override]
    public function getPosition(): int
    {
        $position = $this->currentPosition->get();
        if ($position === null) {
            throw new \Error('continue() returned false, no position available afterwards');
        }

        return $position;
    }

    #[\Override]
    public function isComplete(): bool
    {
        return $this->position >= $this->size;
    }

    #[\Override]
    public function dispose(): void
    {
        $this->disposed ??= new DisposedException;
    }

    #[\Override]
    public function getIterator(): \Traversable
    {
        while ($this->continue()) {
            yield $this->getPosition() => $this->getValue();
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\Pipeline\Internal;

use Amp\Cancellation;
use Amp\DeferredFuture;
use Amp\Future;
use Amp\Internal;
use Amp\Pipeline\ConcurrentIterator;
use Amp\Pipeline\DisposedException;
use Revolt\EventLoop;
use Revolt\EventLoop\FiberLocal;
use Revolt\EventLoop\Suspension;

/**
 * Class used internally by {@see Pipeline} implementations. Do not use this class in your code, instead compose your
 * class from one of the available classes implementing {@see ConcurrentIterator}.
 *
 * @internal
 *
 * @template T
 * @implements \IteratorAggregate<int, T>
 */
final class QueueState implements \IteratorAggregate
{
    private const CONTINUE = [null];

    private bool $completed = false;

    private ?\Throwable $exception = null;

    /** @var array<int, T> */
    private array $emittedValues = [];

    /** @var array<int, DeferredFuture<null>|Suspension<null>> */
    private array $backpressure = [];

    /** @var array<int, Suspension<array{int, T}|FiberLocal>> */
    private array $waiting = [];

    private int $consumePosition = 0;

    private int $emitPosition = 0;

    private ?array $resolutionTrace = null;

    private bool $disposed = false;

    private int $bufferSize;

    private int $positionOffset = 0;

    /** @var FiberLocal<int|null> */
    private readonly FiberLocal $currentPosition;

    /** @var FiberLocal<T|null> */
    private readonly FiberLocal $currentValue;

    public function __construct(int $bufferSize = 0)
    {
        if ($bufferSize < 0) {
            throw new \ValueError('Argument #1 ($bufferSize) must be greater than or equal to 0, got ' . $bufferSize);
        }

        $this->bufferSize = $bufferSize;
        $this->currentPosition = new FiberLocal(static fn () => throw new \Error('Call continue() before calling getPosition()'));
        $this->currentValue = new FiberLocal(static fn () => throw new \Error('Call continue() before calling getValue()'));
    }

    public function continue(?Cancellation $cancellation = null): bool
    {
        $position = $this->consumePosition++;
        $backpressurePosition = $position + $this->bufferSize;

        // Relieve backpressure from prior emit.
        if (isset($this->backpressure[$backpressurePosition])) {
            $backpressure = $this->backpressure[$backpressurePosition];
            unset($this->backpressure[$backpressurePosition]);
            $backpressure instanceof Suspension
                ? $backpressure->resume()
                : $backpressure->complete();
        }

        if (\array_key_exists($position, $this->emittedValues)) {
            $value = $this->emittedValues[$position];
            unset($this->emittedValues[$position]);
            $this->currentPosition->set($position - $this->positionOffset);
            $this->currentValue->set($value);
            return true;
        }

        if ($this->completed || $this->disposed) {
            $this->currentPosition->set(null);
            $this->currentValue->set(null);

            if ($this->exception) {
                throw $this->exception;
            }

            return false;
        }

        $this->currentValue->set(null); // Remove reference to current value while awaiting next value.

        // No value has been emitted, suspend fiber to await next value.
        $this->waiting[] = $suspension = EventLoop::getSuspension();

        if ($cancellation) {
            $waiting = &$this->waiting;
            $offset = &$this->positionOffset;
            $id = $cancellation->subscribe(static function (\Throwable $exception) use (
                &$waiting,
                &$offset,
                $suspension,
            ): void {
                foreach ($waiting as $key => $pending) {
                    if ($pending === $suspension) {
                        unset($waiting[$key]);
                        ++$offset;
                        $suspension->throw($exception);
                        return;
                    }
                }
            });
        }

        try {
            $emitted = $suspension->suspend();

            // This is just a marker, because we can't set fiber locals from other fibers
            if ($emitted === $this->currentPosition) {
                $this->currentPosition->set(null);
                $this->currentValue->set(null);

                return false;
            }

            [$position, $value] = $emitted;

            $this->currentPosition->set($position - $this->positionOffset);
            $this->currentValue->set($value);

            return true;
        } finally {
            /** @psalm-suppress PossiblyUndefinedVariable $id will be defined if $cancellation is not null. */
            $cancellation?->unsubscribe($id);
        }
    }

    /**
     * @return T
     */
    public function getValue(): mixed
    {
        if ($this->currentPosition->get() === null) {
            throw new \Error('Pipeline complete or awaiting next value, cannot call getValue()');
        }

        return $this->currentValue->get();
    }

    public function getPosition(): int
    {
        $position = $this->currentPosition->get();
        if ($position === null) {
            throw new \Error('Pipeline complete, cannot call getPosition()');
        }

        return $position;
    }

    public function hasPending(): bool
    {
        return \array_key_exists($this->consumePosition, $this->emittedValues);
    }

    /**
     * @see Pipeline::dispose()
     */
    public function dispose(): void
    {
        if ($this->disposed) {
            return;
        }

        if ($this->completed) {
            $this->disposed = true;
            $this->relieveBackPressure(new DisposedException());
            return;
        }

        $this->finalize(new DisposedException(), true);
    }

    /**
     * @param T $value
     *
     * @return array|null Returns [?\Throwable, mixed] or null if no send value is available.
     *
     * @throws \Error If the queue has completed.
     */
    private function doPush(mixed $value, int $position): ?array
    {
        if ($this->completed) {
            throw new \Error("Values cannot be enqueued after calling complete");
        }

        if (!empty($this->waiting)) {
            $key = \array_key_first($this->waiting);
            $suspension = $this->waiting[$key];
            unset($this->waiting[$key]);
            $suspension->resume([$position, $value]);

            if ($this->disposed && empty($this->waiting)) {
                $this->triggerDisposal();
                return self::CONTINUE; // Subsequent push() calls will throw.
            }

            if ($this->consumePosition > $position) {
                return self::CONTINUE;
            }

            return null;
        }

        if ($this->disposed) {
            \assert(isset($this->exception), "Failure exception must be set when disposed");
            // Pipeline has been disposed and no Fibers are still pending.
            return [$this->exception];
        }

        $this->emittedValues[$position] = $value;

        if (\count($this->emittedValues) > $this->bufferSize) {
            return null;
        }

        return self::CONTINUE;
    }

    /**
     * Emits a value from the pipeline. The returned promise is resolved once the emitted value has been consumed or
     * if the pipeline is completed, failed, or disposed.
     *
     * @param T $value Value to emit from the pipeline.
     *
     * @return Future Resolves once the value has been consumed on the pipeline.
     */
    public function pushAsync(mixed $value): Future
    {
        $position = $this->emitPosition++ + $this->positionOffset;
        $next = $this->doPush($value, $position);

        if ($next === null) {
            $this->backpressure[$position] = $deferred = new DeferredFuture;
            return $deferred->getFuture();
        }

        [$exception] = $next;

        if ($exception) {
            return Future::error($exception);
        }

        return Future::complete();
    }

    /**
     * Emits a value from the pipeline, suspending execution until the value is consumed.
     *
     * @param T $value Value to emit from the pipeline.
     */
    public function push(mixed $value): void
    {
        $position = $this->emitPosition++ + $this->positionOffset;
        $next = $this->doPush($value, $position);

        if ($next === null) {
            $this->backpressure[$position] = $suspension = EventLoop::getSuspension();
            $suspension->suspend();
            return;
        }

        [$exception] = $next;

        if ($exception) {
            throw $exception;
        }
    }

    /**
     * @return bool True if the pipeline has been completed or failed.
     */
    public function isComplete(): bool
    {
        return $this->completed;
    }

    /**
     * @return bool True if the pipeline has no values pending and has been completed.
     */
    public function isConsumed(): bool
    {
        return empty($this->emittedValues) && $this->completed;
    }

    /**
     * @return bool True if the pipeline was disposed.
     */
    public function isDisposed(): bool
    {
        return $this->disposed && empty($this->waiting);
    }

    /**
     * Completes the pipeline.
     *
     * @throws \Error If the iterator has already been completed.
     */
    public function complete(): void
    {
        $this->finalize();
    }

    /**
     * Fails the pipeline.
     */
    public function error(\Throwable $exception): void
    {
        $this->finalize($exception);
    }

    /**
     * @param \Throwable|null $exception Failure reason or null for success.
     * @param bool $disposed Flag if the generator was disposed.
     */
    private function finalize(?\Throwable $exception = null, bool $disposed = false): void
    {
        if ($this->completed) {
            $message = "Queue has already been completed";

            if (isset($this->resolutionTrace)) {
                /** @psalm-suppress ArgumentTypeCoercion $this->resolution trace generated from debug_backtrace() */
                $trace = Internal\formatStacktrace($this->resolutionTrace);
                $message .= ". Previous completion trace:\n\n{$trace}\n\n";
            } else {
                // @codeCoverageIgnoreStart
                $message .= ", define environment variable AMP_DEBUG or const AMP_DEBUG = true and enable assertions "
                    . "for a stacktrace of the previous resolution.";
                // @codeCoverageIgnoreEnd
            }

            throw new \Error($message);
        }

        $this->completed = !$disposed; // $disposed is false if complete() or error() invoked
        $this->disposed = $this->disposed ?: $disposed; // Once disposed, do not change flag

        if ($this->completed) { // Record stack trace when calling complete() or error()
            \assert((function () {
                if (Internal\isDebugEnabled()) {
                    $trace = \debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS);
                    \array_shift($trace); // remove current closure
                    $this->resolutionTrace = $trace;
                }

                return true;
            })());
        }

        if (isset($this->exception)) {
            return;
        }

        if ($exception !== null) {
            $this->exception = $exception;
        }

        if ($this->disposed) {
            $this->triggerDisposal();
        } else {
            $this->resolvePending();
        }
    }

    private function relieveBackPressure(?\Throwable $exception): void
    {
        $backPressure = $this->backpressure;
        $this->backpressure = [];

        foreach ($backPressure as $placeholder) {
            if ($exception) {
                $placeholder instanceof Suspension
                    ? $placeholder->throw($exception)
                    : $placeholder->error($exception);
            } else {
                $placeholder instanceof Suspension
                    ? $placeholder->resume()
                    : $placeholder->complete();
            }
        }
    }

    /**
     * Resolves all backpressure and outstanding calls for emitted values.
     */
    private function resolvePending(): void
    {
        $waiting = $this->waiting;
        $this->waiting = [];

        foreach ($waiting as $suspension) {
            if ($this->exception) {
                $suspension->throw($this->exception);
            } else {
                $suspension->resume($this->currentPosition);
            }
        }
    }

    /**
     * Fails pending {@see continue()} promises.
     */
    private function triggerDisposal(): void
    {
        \assert($this->disposed && $this->exception, "Pipeline was not disposed on triggering disposal");

        $this->relieveBackPressure($this->exception);
        $this->resolvePending();
    }

    #[\Override]
    public function getIterator(): \Traversable
    {
        while ($this->continue()) {
            yield $this->getPosition() => $this->getValue();
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\Pipeline\Internal;

use Amp\Cancellation;
use Amp\Future;
use Amp\Pipeline\ConcurrentIterator;
use function Amp\async;

/**
 * @internal
 *
 * @template-covariant T
 * @template-implements ConcurrentIterator<T>
 */
final class ConcurrentFlatMapIterator implements ConcurrentIterator
{
    /** @var ConcurrentIterator<T> */
    private readonly ConcurrentIterator $iterator;

    /**
     * @template R
     *
     * @param ConcurrentIterator<T> $iterator
     * @param \Closure(T, int):iterable<R> $flatMap
     */
    public function __construct(
        ConcurrentIterator $iterator,
        int $bufferSize,
        int $concurrency,
        bool $ordered,
        \Closure $flatMap,
    ) {
        $queue = new QueueState($bufferSize);
        $this->iterator = new ConcurrentQueueIterator($queue);

        $preOrder = $ordered ? new Sequence() : null;
        $postOrder = $ordered ? new Sequence() : null;

        $stop = FlatMapOperation::getStopMarker();

        $futures = [];

        for ($i = 0; $i < $concurrency; $i++) {
            $futures[] = async(static function () use (
                $queue,
                $iterator,
                $flatMap,
                $preOrder,
                $postOrder,
                $stop,
            ): void {
                foreach ($iterator as $position => $value) {
                    // Force ordering of concurrent coroutines regardless of the emitted order of the source iterator.
                    $preOrder?->barrier($position);

                    try {
                        $iterable = $flatMap($value, $position);
                    } catch (\Throwable $exception) {
                        $postOrder?->await($position);

                        $preOrder?->dispose();
                        $postOrder?->dispose();

                        throw $exception;
                    }

                    $postOrder?->await($position);

                    foreach ($iterable as $item) {
                        // Another concurrent coroutine already completed the queue
                        if ($queue->isComplete()) {
                            return;
                        }

                        if ($item === $stop) {
                            $queue->complete();

                            $preOrder?->dispose();
                            $postOrder?->dispose();

                            return;
                        }

                        $queue->push($item);
                    }

                    $postOrder?->resume($position);
                }
            });
        }

        async(static function () use ($futures, $queue): void {
            try {
                Future\await($futures);

                if (!$queue->isComplete()) {
                    $queue->complete();
                }
            } catch (\Throwable $e) {
                if (!$queue->isComplete()) {
                    $queue->error($e);
                }
            }
        });
    }

    #[\Override]
    public function continue(?Cancellation $cancellation = null): bool
    {
        return $this->iterator->continue($cancellation);
    }

    #[\Override]
    public function getValue(): mixed
    {
        return $this->iterator->getValue();
    }

    #[\Override]
    public function getPosition(): int
    {
        return $this->iterator->getPosition();
    }

    #[\Override]
    public function dispose(): void
    {
        $this->iterator->dispose();
    }

    #[\Override]
    public function isComplete(): bool
    {
        return $this->iterator->isComplete();
    }

    #[\Override]
    public function getIterator(): \Traversable
    {
        while ($this->continue()) {
            yield $this->getPosition() => $this->getValue();
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\Pipeline\Internal;

use Amp\Pipeline\ConcurrentIterator;

/**
 * @template T
 *
 * @internal
 */
final class SortOperation implements IntermediateOperation
{
    /**
     * @param \Closure(T, T):int $compare
     */
    public function __construct(private readonly \Closure $compare)
    {
    }

    /**
     * @param ConcurrentIterator<T> $source
     * @return ConcurrentIterator<T>
     */
    #[\Override]
    public function __invoke(ConcurrentIterator $source): ConcurrentIterator
    {
        $values = \iterator_to_array($source, false);
        \usort($values, $this->compare);

        return new ConcurrentArrayIterator($values);
    }
}
<?php declare(strict_types=1);

namespace Amp\Pipeline\Internal;

use Amp\Cancellation;
use Amp\Pipeline\ConcurrentIterator;

/**
 * @internal
 *
 * @template-covariant T
 * @implements ConcurrentIterator<T>
 */
final class ConcurrentQueueIterator implements ConcurrentIterator
{
    private readonly QueueState $state;

    public function __construct(QueueState $state)
    {
        $this->state = $state;
    }

    public function __destruct()
    {
        $this->state->dispose();
    }

    #[\Override]
    public function continue(?Cancellation $cancellation = null): bool
    {
        return $this->state->continue($cancellation);
    }

    #[\Override]
    public function getValue(): mixed
    {
        return $this->state->getValue();
    }

    #[\Override]
    public function getPosition(): int
    {
        return $this->state->getPosition();
    }

    #[\Override]
    public function dispose(): void
    {
        $this->state->dispose();
    }

    #[\Override]
    public function isComplete(): bool
    {
        return $this->state->isConsumed() || $this->state->isDisposed();
    }

    #[\Override]
    public function getIterator(): \Traversable
    {
        while ($this->state->continue()) {
            yield $this->state->getPosition() => $this->state->getValue();
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\Pipeline\Internal;

use Amp\Pipeline\ConcurrentIterator;

/**
 * @template T
 * @template R
 *
 * @internal
 */
final class FlatMapOperation implements IntermediateOperation
{
    public static function getStopMarker(): object
    {
        static $marker;

        return $marker ??= new \stdClass;
    }

    /**
     * @param \Closure(T, int):iterable<R> $flatMap
     */
    public function __construct(
        private readonly int $bufferSize,
        private readonly int $concurrency,
        private readonly bool $ordered,
        private readonly \Closure $flatMap
    ) {
    }

    #[\Override]
    public function __invoke(ConcurrentIterator $source): ConcurrentIterator
    {
        if ($this->concurrency === 1) {
            $stop = self::getStopMarker();

            return new ConcurrentIterableIterator((function () use ($source, $stop): iterable {
                foreach ($source as $position => $value) {
                    $iterable = ($this->flatMap)($value, $position);
                    foreach ($iterable as $item) {
                        if ($item === $stop) {
                            return;
                        }

                        yield $item;
                    }
                }
            })(), $this->bufferSize);
        }

        return new ConcurrentFlatMapIterator(
            $source,
            $this->bufferSize,
            $this->concurrency,
            $this->ordered,
            $this->flatMap,
        );
    }
}
<?php declare(strict_types=1);

namespace Amp\Pipeline\Internal;

use Amp\Cancellation;
use Amp\Pipeline\ConcurrentIterator;
use function Amp\async;

/**
 * @internal
 *
 * @template-covariant T
 * @template-implements ConcurrentIterator<T>
 */
final class ConcurrentIterableIterator implements ConcurrentIterator
{
    /** @var ConcurrentIterator<T> */
    private readonly ConcurrentIterator $iterator;

    /**
     * @param iterable<T> $iterable
     */
    public function __construct(iterable $iterable, int $bufferSize = 0)
    {
        if (\is_array($iterable)) {
            $this->iterator = new ConcurrentArrayIterator($iterable);
            return;
        }

        while ($iterable instanceof \IteratorAggregate) {
            if ($iterable instanceof ConcurrentIterator) {
                $this->iterator = $iterable;
                return;
            }

            $iterable = $iterable->getIterator();
        }

        $queue = new QueueState($bufferSize);
        $this->iterator = new ConcurrentQueueIterator($queue);

        async(static function () use ($queue, $iterable): void {
            try {
                foreach ($iterable as $value) {
                    $queue->push($value);
                }

                $queue->complete();
            } catch (\Throwable $e) {
                $queue->error($e);
            }
        });
    }

    #[\Override]
    public function continue(?Cancellation $cancellation = null): bool
    {
        return $this->iterator->continue($cancellation);
    }

    #[\Override]
    public function getValue(): mixed
    {
        return $this->iterator->getValue();
    }

    #[\Override]
    public function getPosition(): int
    {
        return $this->iterator->getPosition();
    }

    #[\Override]
    public function isComplete(): bool
    {
        return $this->iterator->isComplete();
    }

    #[\Override]
    public function dispose(): void
    {
        $this->iterator->dispose();
    }

    #[\Override]
    public function getIterator(): \Traversable
    {
        return $this->iterator;
    }
}
<?php declare(strict_types=1);

namespace Amp\Pipeline\Internal;

use Revolt\EventLoop;
use Revolt\EventLoop\Suspension;

/** @internal */
final class Sequence
{
    /** @var non-negative-int */
    private int $position = 0;

    /** @var array<int, Suspension> */
    private array $suspensions = [];

    public function await(int $position): void
    {
        if ($position <= $this->position) {
            return;
        }

        \assert(!isset($this->suspensions[$position]));

        $suspension = EventLoop::getSuspension();
        $this->suspensions[$position] = $suspension;
        $suspension->suspend();
    }

    public function resume(int $position): void
    {
        if ($position < $this->position) {
            return;
        }

        $newPosition = \max($position, $this->position) + 1;

        if ($newPosition === \PHP_INT_MAX) {
            foreach ($this->suspensions as $suspension) {
                $suspension->resume();
            }

            $this->suspensions = [];
        } else {
            for ($i = $this->position + 1; $i <= $newPosition; $i++) {
                if (isset($this->suspensions[$i])) {
                    $this->suspensions[$i]->resume();
                    unset($this->suspensions[$i]);
                }
            }
        }

        $this->position = $newPosition;
    }

    /**
     * Awaiting and immediately resuming acts as a barrier, ensuring callbacks are executed in order.
     * Note that resuming a suspension is async, so the code immediately following this call is executed
     * before resuming other coroutines.
     */
    public function barrier(int $position): void
    {
        $this->await($position);
        $this->resume($position);
    }

    public function dispose(): void
    {
        $this->resume(\PHP_INT_MAX - 1);
    }
}
{
    "name": "amphp/pipeline",
    "homepage": "https://amphp.org/pipeline",
    "description": "Asynchronous iterators and operators.",
    "support": {
        "issues": "https://github.com/amphp/pipeline/issues"
    },
    "keywords": [
        "iterator",
        "async",
        "non-blocking",
        "amp",
        "amphp",
        "io"
    ],
    "license": "MIT",
    "authors": [
        {
            "name": "Aaron Piotrowski",
            "email": "aaron@trowski.com"
        },
        {
            "name": "Niklas Keller",
            "email": "me@kelunik.com"
        }
    ],
    "require": {
        "php": ">=8.1",
        "amphp/amp": "^3",
        "revolt/event-loop": "^1"
    },
    "require-dev": {
        "amphp/php-cs-fixer-config": "^2",
        "amphp/phpunit-util": "^3",
        "phpunit/phpunit": "^9",
        "psalm/phar": "6.16.1"
    },
    "autoload": {
        "psr-4": {
            "Amp\\Pipeline\\": "src"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Amp\\Pipeline\\": "test"
        }
    }
}
<?php

$config = new Amp\CodeStyle\Config();
$config->getFinder()
    ->in(__DIR__ . '/examples')
    ->in(__DIR__ . '/src')
    ->in(__DIR__ . '/test');

$config->setCacheFile(__DIR__ . '/.php_cs.cache');

return $config;
{
    "symbol-whitelist": [
        "null",
        "true",
        "false",
        "static",
        "self",
        "parent",
        "array",
        "string",
        "int",
        "float",
        "bool",
        "iterable",
        "callable",
        "mixed",
        "void",
        "object",
        "random_int",
        "createDefaultResolver",
        "normalizeName",
        "dnsResolver",
        "idn_to_ascii",
        "INTL_IDNA_VARIANT_UTS46"
    ],
    "php-core-extensions": [
        "Core",
        "date",
        "pcre",
        "Phar",
        "Reflection",
        "SPL",
        "standard",
        "hash"
    ]
}
<?php declare(strict_types=1);

namespace Amp\Dns;

use Amp\Cancellation;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;

final class BlockingFallbackDnsResolver implements DnsResolver
{
    use ForbidCloning;
    use ForbidSerialization;

    #[\Override]
    public function resolve(string $name, ?int $typeRestriction = null, ?Cancellation $cancellation = null): array
    {
        if (!\in_array($typeRestriction, [DnsRecord::A, null], true)) {
            throw new DnsException("Query for '{$name}' failed, because loading the system's DNS configuration failed and querying records other than A records isn't supported in blocking fallback mode.");
        }

        return $this->query($name, DnsRecord::A);
    }

    #[\Override]
    public function query(string $name, int $type, ?Cancellation $cancellation = null): array
    {
        if ($type !== DnsRecord::A) {
            throw new DnsException("Query for '$name' failed, because loading the system's DNS configuration failed and querying records other than A records isn't supported in blocking fallback mode.");
        }

        $result = \gethostbynamel($name);
        if ($result === false) {
            throw new DnsException("Query for '$name' failed, because loading the system's DNS configuration failed and blocking fallback via gethostbynamel() failed, too.");
        }

        if ($result === []) {
            throw new MissingDnsRecordException("No records returned for '$name' using blocking fallback mode.");
        }

        $records = [];

        foreach ($result as $record) {
            $records[] = new DnsRecord($record, DnsRecord::A, null);
        }

        return $records;
    }
}
<?php declare(strict_types=1);

namespace Amp\Dns;

interface DnsConfigLoader
{
    /**
     * @throws DnsConfigException
     */
    public function loadConfig(): DnsConfig;
}
<?php declare(strict_types=1);

namespace Amp\Dns;

use Amp\Cancellation;

interface DnsResolver
{
    /**
     * Resolves a hostname name to an IP address [hostname as defined by RFC 3986].
     *
     * Upon success this method returns an array of Record objects. If the domain cannot be resolved,
     * a {@see DnsException} is thrown.
     *
     * A null $ttl value indicates the DNS name was resolved from the cache or the local hosts file.
     *
     * @param string $name The hostname to resolve.
     * @param int|null $typeRestriction Optional type restriction to `Record::A` or `Record::AAAA`, otherwise `null`.
     *
     * @return non-empty-list<DnsRecord>
     *
     * @throws MissingDnsRecordException
     * @throws DnsException
     */
    public function resolve(string $name, ?int $typeRestriction = null, ?Cancellation $cancellation = null): array;

    /**
     * Query specific DNS records.
     *
     * Upon success this method returns an array of Record objects. If no records of the given type are found,
     * a {@see DnsException} is thrown.
     *
     * @param string $name Record to question, A, AAAA and PTR queries are automatically normalized.
     * @param int $type Use constants of Amp\Dns\Record.
     *
     * @return non-empty-list<DnsRecord>
     *
     * @throws DnsException
     */
    public function query(string $name, int $type, ?Cancellation $cancellation = null): array;
}
<?php declare(strict_types=1);

namespace Amp\Dns;

use LibDNS\Records\ResourceQTypes;
use LibDNS\Records\ResourceTypes;

final class DnsRecord
{
    public const A = ResourceTypes::A;
    public const AAAA = ResourceTypes::AAAA;
    public const AFSDB = ResourceTypes::AFSDB;
    // public const APL = ResourceTypes::APL;
    public const CAA = ResourceTypes::CAA;
    public const CERT = ResourceTypes::CERT;
    public const CNAME = ResourceTypes::CNAME;
    public const DHCID = ResourceTypes::DHCID;
    public const DLV = ResourceTypes::DLV;
    public const DNAME = ResourceTypes::DNAME;
    public const DNSKEY = ResourceTypes::DNSKEY;
    public const DS = ResourceTypes::DS;
    public const HINFO = ResourceTypes::HINFO;
    // public const HIP = ResourceTypes::HIP;
    // public const IPSECKEY = ResourceTypes::IPSECKEY;
    public const KEY = ResourceTypes::KEY;
    public const KX = ResourceTypes::KX;
    public const ISDN = ResourceTypes::ISDN;
    public const LOC = ResourceTypes::LOC;
    public const MB = ResourceTypes::MB;
    public const MD = ResourceTypes::MD;
    public const MF = ResourceTypes::MF;
    public const MG = ResourceTypes::MG;
    public const MINFO = ResourceTypes::MINFO;
    public const MR = ResourceTypes::MR;
    public const MX = ResourceTypes::MX;
    public const NAPTR = ResourceTypes::NAPTR;
    public const NS = ResourceTypes::NS;
    // public const NSEC = ResourceTypes::NSEC;
    // public const NSEC3 = ResourceTypes::NSEC3;
    // public const NSEC3PARAM = ResourceTypes::NSEC3PARAM;
    public const NULL = ResourceTypes::NULL;
    public const PTR = ResourceTypes::PTR;
    public const RP = ResourceTypes::RP;
    // public const RRSIG = ResourceTypes::RRSIG;
    public const RT = ResourceTypes::RT;
    public const SIG = ResourceTypes::SIG;
    public const SOA = ResourceTypes::SOA;
    public const SPF = ResourceTypes::SPF;
    public const SRV = ResourceTypes::SRV;
    public const TXT = ResourceTypes::TXT;
    public const WKS = ResourceTypes::WKS;
    public const X25 = ResourceTypes::X25;

    public const AXFR = ResourceQTypes::AXFR;
    public const MAILB = ResourceQTypes::MAILB;
    public const MAILA = ResourceQTypes::MAILA;
    public const ALL = ResourceQTypes::ALL;

    /**
     * Converts a record type integer back into its name as defined in this class.
     *
     * Returns "unknown (<type>)" in case a name for this record is not known.
     *
     * @param int $type Record type as integer.
     *
     * @return string Name of the constant for this record in this class.
     */
    public static function getName(int $type): string
    {
        static $types;

        if (0 > $type || 0xffff < $type) {
            $message = \sprintf('%d does not correspond to a valid record type (must be between 0 and 65535).', $type);
            throw new \Error($message);
        }

        if ($types === null) {
            $types = \array_flip(
                (new \ReflectionClass(self::class))
                    ->getConstants()
            );
        }

        return $types[$type] ?? "unknown ({$type})";
    }

    public function __construct(
        private readonly string $value,
        private readonly int $type,
        private readonly ?int $ttl = null,
    ) {
    }

    public function getValue(): string
    {
        return $this->value;
    }

    public function getType(): int
    {
        return $this->type;
    }

    public function getTtl(): ?int
    {
        return $this->ttl;
    }
}
<?php declare(strict_types=1);

namespace Amp\Dns;

/**
 * @psalm-suppress ClassMustBeFinal
 */
class MissingDnsRecordException extends DnsException
{
}
<?php declare(strict_types=1);

namespace Amp\Dns;

/**
 * @psalm-suppress ClassMustBeFinal
 */
class DnsTimeoutException extends DnsException
{
}
<?php declare(strict_types=1);

namespace Amp\Dns;

class DnsException extends \Exception
{
}
<?php declare(strict_types=1);

namespace Amp\Dns;

use Amp\Cache\Cache;
use Amp\Cache\LocalCache;
use Amp\Cancellation;
use Amp\CompositeException;
use Amp\Dns\Internal\Socket;
use Amp\Dns\Internal\TcpSocket;
use Amp\Dns\Internal\UdpSocket;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Future;
use LibDNS\Messages\Message;
use LibDNS\Messages\MessageResponseCodes;
use LibDNS\Records\Question;
use LibDNS\Records\QuestionFactory;
use Revolt\EventLoop;
use function Amp\async;
use function Amp\now;

final class Rfc1035StubDnsResolver implements DnsResolver
{
    use ForbidCloning;
    use ForbidSerialization;

    public const CACHE_PREFIX = "amphp.dns.";

    private const CONFIG_NOT_LOADED = 0;
    private const CONFIG_LOADED = 1;
    private const CONFIG_FAILED = 2;

    private readonly DnsConfigLoader $configLoader;

    private readonly QuestionFactory $questionFactory;

    private ?DnsConfig $config = null;

    private int $configStatus = self::CONFIG_NOT_LOADED;

    private ?Future $pendingConfig = null;

    /** @var Cache<list<array{string, int}>> */
    private readonly Cache $cache;

    /** @var Socket[] */
    private array $sockets = [];

    /** @var Future[] */
    private array $pendingSockets = [];

    /** @var Future[] */
    private array $pendingQueries = [];

    private readonly string $gcCallbackId;

    private readonly BlockingFallbackDnsResolver $blockingFallbackResolver;

    private int $nextNameserver = 0;

    public function __construct(?Cache $cache = null, ?DnsConfigLoader $configLoader = null)
    {
        $this->cache = $cache ?? new LocalCache(256);
        $this->configLoader = $configLoader ?? (\PHP_OS_FAMILY === 'Windows'
                ? new WindowsDnsConfigLoader
                : new UnixDnsConfigLoader);

        $this->questionFactory = new QuestionFactory;
        $this->blockingFallbackResolver = new BlockingFallbackDnsResolver;

        $sockets = &$this->sockets;
        $this->gcCallbackId = EventLoop::repeat(5, static function () use (&$sockets): void {
            if (!$sockets) {
                return;
            }

            $now = now();
            foreach ($sockets as $key => $server) {
                if ($server->getLastActivity() < $now - 60.0) {
                    $server->close();
                    unset($sockets[$key]);
                }
            }
        });

        EventLoop::unreference($this->gcCallbackId);
    }

    public function __destruct()
    {
        EventLoop::cancel($this->gcCallbackId);
    }

    #[\Override]
    public function resolve(string $name, ?int $typeRestriction = null, ?Cancellation $cancellation = null): array
    {
        $recordTypes = match ($typeRestriction) {
            DnsRecord::A, DnsRecord::AAAA, => [$typeRestriction],
            null => [DnsRecord::A, DnsRecord::AAAA],
            default => throw new \Error("Invalid value for parameter 2: null|Record::A|Record::AAAA expected"),
        };

        $this->loadConfigIfNotLoaded();
        if ($this->configStatus === self::CONFIG_FAILED) {
            return $this->blockingFallbackResolver->resolve($name, $typeRestriction, $cancellation);
        }

        // Check if provided $name is an IP address.
        $isIp = \array_filter([
            DnsRecord::A => \filter_var($name, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4),
            DnsRecord::AAAA => \filter_var($name, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6),
        ]);

        // If the name is an IP address, return that IP (or throw if it does match the type restriction).
        if ($isIp) {
            $type = \array_key_first($isIp);
            if (!\in_array($type, $recordTypes, true)) {
                throw new DnsException("Got an IP address that is not valid for the restricted record type");
            }

            return [new DnsRecord($name, $type, null)];
        }

        $dots = \substr_count($name, ".");
        $trailingDot = $name !== '' && $name[-1] === ".";
        $name = normalizeName($name);

        if ($records = $this->queryHosts($name, $typeRestriction)) {
            return $records;
        }

        // Follow RFC 6761 and never send queries for localhost to the caching DNS server
        // Usually, these queries are already resolved via queryHosts()
        if ($name === 'localhost') {
            return $typeRestriction === DnsRecord::AAAA
                ? [new DnsRecord('::1', DnsRecord::AAAA, null)]
                : [new DnsRecord('127.0.0.1', DnsRecord::A, null)];
        }

        \assert($this->config !== null);

        $searchList = ['.'];
        if (!$trailingDot && $dots < $this->config->getNdots()) {
            $configuredSearchList = $this->config->getSearchList();
            if (\in_array('.', $configuredSearchList, true)) {
                $searchList = $configuredSearchList;
            } else {
                $searchList = \array_merge($configuredSearchList, $searchList);
            }
        }

        $sendQuery = $this->query(...);

        foreach ($searchList as $searchIndex => $search) {
            for ($redirects = 0; $redirects < 5; $redirects++) {
                $searchName = match ($search) {
                    '.' => $name,
                    default => $name . '.' . $search,
                };

                try {
                    [$exceptions, $records] = Future\awaitAll(\array_map(
                        static fn (int $recordType) => async($sendQuery, $searchName, $recordType, $cancellation),
                        $recordTypes,
                    ));

                    if (\count($exceptions) === \count($recordTypes)) {
                        $errors = [];

                        foreach ($exceptions as $reason) {
                            if ($reason instanceof MissingDnsRecordException) {
                                throw $reason;
                            }

                            if ($searchIndex < \count($searchList) - 1 && $this->shouldRetry($reason->getCode())) {
                                continue 2;
                            }

                            $errors[] = $reason->getMessage();
                        }

                        \assert(\count($exceptions) > 0); // For Psalm, CompositeException requires non-empty-array.

                        throw new DnsException(
                            "All query attempts failed for {$searchName}: " . \implode(", ", $errors),
                            0,
                            new CompositeException($exceptions)
                        );
                    }

                    /** @var non-empty-list<non-empty-list<DnsRecord>> $records */
                    return \array_merge(...$records);
                } catch (DnsException $e) {
                    if ($e instanceof MissingDnsRecordException) {
                        $alias = $this->searchForAliasRecord($searchName, $cancellation);
                        if ($alias !== null) {
                            $name = $alias;
                            continue;
                        }
                    }

                    if ($searchIndex < \count($searchList) - 1 && $this->shouldRetry($e->getCode())) {
                        continue 2;
                    }

                    throw $e;
                }
            }
        }

        throw new DnsException("Giving up resolution of '{$name}', too many redirects");
    }

    private function searchForAliasRecord(string $searchName, ?Cancellation $cancellation): ?string
    {
        foreach ([DnsRecord::CNAME, DnsRecord::DNAME] as $recordType) {
            try {
                $records = $this->query($searchName, $recordType, $cancellation);
                return $records[0]->getValue();
            } catch (MissingDnsRecordException) {
                continue;
            }
        }

        return null;
    }

    /**
     * Reloads the configuration in the background.
     *
     * Once it's finished, the configuration will be used for new requests.
     *
     * @throws DnsConfigException
     */
    public function reloadConfig(): DnsConfig
    {
        if ($this->pendingConfig) {
            return $this->pendingConfig->await();
        }

        $this->pendingConfig = async(function (): DnsConfig {
            try {
                $this->config = $this->configLoader->loadConfig();
                $this->configStatus = self::CONFIG_LOADED;
            } catch (DnsConfigException $e) {
                $this->configStatus = self::CONFIG_FAILED;

                throw $e;
            } finally {
                $this->pendingConfig = null;
            }

            return $this->config;
        });

        return $this->pendingConfig->await();
    }

    #[\Override]
    public function query(string $name, int $type, ?Cancellation $cancellation = null): array
    {
        $key = $name . "-" . $type;

        $future = $this->pendingQueries[$key] ??= async($this->sendQuery(...), $name, $type)
            ->finally(function () use ($key): void {
                unset($this->pendingQueries[$key]);
            });

        return $future->await($cancellation);
    }

    /**
     * @return non-empty-list<DnsRecord>
     */
    private function sendQuery(string $name, int $type): array
    {
        $this->loadConfigIfNotLoaded();
        if ($this->configStatus === self::CONFIG_FAILED) {
            return $this->blockingFallbackResolver->query($name, $type);
        }

        \assert($this->config !== null);

        $name = $this->normalizeName($name, $type);
        $question = $this->createQuestion($name, $type);

        if (null !== $cachedValue = $this->cache->get($this->getCacheKey($name, $type))) {
            if (!$cachedValue) {
                throw new MissingDnsRecordException("No records returned for {$name} (cached result)");
            }

            $result = [];

            foreach ($cachedValue as [$data, $type]) {
                $result[] = new DnsRecord($data, $type);
            }

            return $result;
        }

        $nameservers = $this->selectNameservers();
        $nameserversCount = \count($nameservers);
        $attempts = $this->config->getAttempts();
        $protocol = "udp";
        $attempt = 0;

        /** @var Socket $socket */
        $uri = $protocol . "://" . $nameservers[0];
        $socket = $this->getSocket($uri);

        $attemptDescription = [];

        while ($attempt < $attempts) {
            try {
                if (!$socket->isAlive()) {
                    unset($this->sockets[$uri]);
                    $socket->close();

                    $uri = $protocol . "://" . $nameservers[$attempt % $nameserversCount];
                    $socket = $this->getSocket($uri);
                }

                $attemptDescription[] = $uri;

                $response = $socket->ask($question, $this->config->getTimeout());
                $this->assertAcceptableResponse($response, $name);

                // UDP sockets are never reused, they're not in the $this->sockets map
                if ($protocol === "udp") {
                    $socket->close();
                }

                if ($response->isTruncated()) {
                    if ($protocol !== "tcp") {
                        // Retry with TCP, don't count attempt
                        $protocol = "tcp";
                        $uri = $protocol . "://" . $nameservers[$attempt % $nameserversCount];
                        $socket = $this->getSocket($uri);
                        continue;
                    }

                    throw new DnsException(
                        "Server returned a truncated response for '{$name}' (" . DnsRecord::getName($type) . ")",
                    );
                }

                $answers = $response->getAnswerRecords();
                $result = [];
                $ttls = [];

                /** @var \LibDNS\Records\Resource $record */
                foreach ($answers as $record) {
                    $recordType = $record->getType();
                    $result[$recordType][] = (string) $record->getData();

                    // Cache for max one day
                    $ttls[$recordType] = \min($ttls[$recordType] ?? 86400, $record->getTTL());
                }

                foreach ($result as $recordType => $records) {
                    // We don't care here whether storing in the cache fails
                    $this->cache->set(
                        $this->getCacheKey($name, $recordType),
                        \array_map(static fn (string $record) => [
                            $record,
                            $recordType,
                        ], $records),
                        $ttls[$recordType]
                    );
                }

                if (!isset($result[$type])) {
                    // "it MUST NOT cache it for longer than five (5) minutes" per RFC 2308 section 7.1
                    $this->cache->set($this->getCacheKey($name, $type), [], 300);
                    throw new MissingDnsRecordException(
                        "No records returned for '{$name}' (" . DnsRecord::getName($type) . ")",
                    );
                }

                return \array_map(static function ($data) use ($type, $ttls) {
                    return new DnsRecord($data, $type, $ttls[$type]);
                }, $result[$type]);
            } catch (DnsTimeoutException) {
                unset($this->sockets[$uri]);
                $socket->close();

                $uri = $protocol . "://" . $nameservers[++$attempt % $nameserversCount];
                $socket = $this->getSocket($uri);

                continue;
            }
        }

        throw new DnsTimeoutException(\sprintf(
            "No response for '%s' (%s) from any nameserver within %d seconds after %d attempts, tried %s",
            $name,
            DnsRecord::getName($type),
            $this->config->getTimeout(),
            $attempts,
            \implode(", ", $attemptDescription)
        ));
    }

    /**
     * @return list<DnsRecord>
     */
    private function queryHosts(string $name, ?int $typeRestriction = null): array
    {
        \assert($this->config !== null);

        $hosts = $this->config->getKnownHosts();
        $records = [];

        $returnIPv4 = $typeRestriction === null || $typeRestriction === DnsRecord::A;
        $returnIPv6 = $typeRestriction === null || $typeRestriction === DnsRecord::AAAA;

        if ($returnIPv4 && isset($hosts[DnsRecord::A][$name])) {
            $records[] = new DnsRecord($hosts[DnsRecord::A][$name], DnsRecord::A, null);
        }

        if ($returnIPv6 && isset($hosts[DnsRecord::AAAA][$name])) {
            $records[] = new DnsRecord($hosts[DnsRecord::AAAA][$name], DnsRecord::AAAA, null);
        }

        return $records;
    }

    private function normalizeName(string $name, int $type): string
    {
        if ($type === DnsRecord::PTR) {
            if (($packedIp = \inet_pton($name)) !== false) {
                if (isset($packedIp[4])) { // IPv6
                    $name = \wordwrap(\strrev(\bin2hex($packedIp)), 1, ".", true) . ".ip6.arpa";
                } else { // IPv4
                    /** @psalm-suppress PossiblyFalseOperand */
                    $name = \inet_ntop(\strrev($packedIp)) . ".in-addr.arpa";
                }
            }
        } elseif (\in_array($type, [DnsRecord::A, DnsRecord::AAAA], true)) {
            $name = normalizeName($name);
        }

        return $name;
    }

    private function createQuestion(string $name, int $type): Question
    {
        if (0 > $type || 0xffff < $type) {
            $message = \sprintf('%d does not correspond to a valid record type (must be between 0 and 65535).', $type);
            throw new \Error($message);
        }

        $question = $this->questionFactory->create($type);
        $question->setName($name);

        return $question;
    }

    private function getCacheKey(string $name, int $type): string
    {
        return self::CACHE_PREFIX . $name . "#" . $type;
    }

    private function getSocket(string $uri): Internal\Socket
    {
        // We use a new socket for each UDP request, as that increases the entropy and mitigates response forgery.
        if (\str_starts_with($uri, "udp")) {
            return UdpSocket::connect($uri);
        }

        // Over TCP we might reuse sockets if the server allows to keep them open. Sequence IDs in TCP are already
        // better than a random port. Additionally, a TCP connection is more expensive.
        if (isset($this->sockets[$uri])) {
            return $this->sockets[$uri];
        }

        if (isset($this->pendingSockets[$uri])) {
            return $this->pendingSockets[$uri]->await();
        }

        $future = async(function () use ($uri) {
            try {
                $socket = TcpSocket::connect($uri);
                $this->sockets[$uri] = $socket;
                return $socket;
            } finally {
                unset($this->pendingSockets[$uri]);
            }
        });

        $this->pendingSockets[$uri] = $future;

        return $future->await();
    }

    /**
     * @throws DnsException
     */
    private function assertAcceptableResponse(Message $response, string $name): void
    {
        if ($response->getResponseCode() !== 0) {
            // https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml
            $errors = [
                1 => 'FormErr',
                2 => 'ServFail',
                3 => 'NXDomain',
                4 => 'NotImp',
                5 => 'Refused',
                6 => 'YXDomain',
                7 => 'YXRRSet',
                8 => 'NXRRSet',
                9 => 'NotAuth',
                10 => 'NotZone',
                11 => 'DSOTYPENI',
                16 => 'BADVERS',
                17 => 'BADKEY',
                18 => 'BADTIME',
                19 => 'BADMODE',
                20 => 'BADNAME',
                21 => 'BADALG',
                22 => 'BADTRUNC',
                23 => 'BADCOOKIE',
            ];

            throw new DnsException(\sprintf(
                "Name resolution failed for '%s'; server returned error code: %d (%s)",
                $name,
                $response->getResponseCode(),
                $errors[$response->getResponseCode()] ?? 'UNKNOWN'
            ), $response->getResponseCode());
        }
    }

    private function selectNameservers(): array
    {
        \assert($this->config !== null);

        $nameservers = $this->config->getNameservers();

        if ($this->config->isRotationEnabled() && ($nameserversCount = \count($nameservers)) > 1) {
            $nameservers = \array_merge(
                \array_slice($nameservers, $this->nextNameserver),
                \array_slice($nameservers, 0, $this->nextNameserver)
            );
            $this->nextNameserver = ++$this->nextNameserver % $nameserversCount;
        }

        return $nameservers;
    }

    private function shouldRetry(int|string $code): bool
    {
        return \in_array($code, [
            MessageResponseCodes::SERVER_FAILURE,
            MessageResponseCodes::NAME_ERROR,
        ], true);
    }

    private function loadConfigIfNotLoaded(): void
    {
        if ($this->configStatus !== self::CONFIG_NOT_LOADED) {
            return;
        }

        try {
            $this->reloadConfig();
        } catch (DnsConfigException $e) {
            $message = "Could not load the system's DNS configuration; "
                . "falling back to synchronous, blocking resolver; "
                . \get_class($e) . ": " . $e->getMessage();

            try {
                \trigger_error(
                    $message,
                    \E_USER_WARNING
                );
            } catch (\Throwable) {
                \set_error_handler(null);
                \trigger_error(
                    $message,
                    \E_USER_WARNING
                );
                \restore_error_handler();
            }
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\Dns;

final class DnsConfig
{
    private array $nameservers;

    private array $knownHosts;

    private float $timeout = 3; // seconds.

    private int $attempts = 2;

    private array $searchList = [];

    private int $ndots = 1;

    private bool $rotation = false;

    /**
     * @throws DnsConfigException
     */
    public function __construct(array $nameservers, array $knownHosts = [])
    {
        if (\count($nameservers) < 1) {
            throw new DnsConfigException("At least one nameserver is required for a valid config");
        }

        foreach ($nameservers as $nameserver) {
            $this->validateNameserver($nameserver);
        }

        // Windows does not include localhost in its host file. Fetch it from the system instead
        if (!isset($knownHosts[DnsRecord::A]["localhost"]) && !isset($knownHosts[DnsRecord::AAAA]["localhost"])) {
            // PHP currently provides no way to **resolve** IPv6 hostnames (not even with fallback)
            $local = \gethostbyname("localhost");
            if ($local !== "localhost") {
                $knownHosts[DnsRecord::A]["localhost"] = $local;
            } else {
                $knownHosts[DnsRecord::AAAA]["localhost"] = '::1';
            }
        }

        $this->nameservers = $nameservers;
        $this->knownHosts = $knownHosts;
    }

    public function withSearchList(array $searchList): self
    {
        $self = clone $this;

        // Replace null with '.' for backward compatibility
        $self->searchList = \array_map(fn ($search) => $search ?? '.', $searchList);

        return $self;
    }

    /**
     * @throws DnsConfigException
     */
    public function withNdots(int $ndots): self
    {
        if ($ndots < 0) {
            throw new DnsConfigException("Invalid ndots ($ndots), must be greater or equal to 0");
        }

        $self = clone $this;
        $self->ndots = \min($ndots, 15);

        return $self;
    }

    public function withRotationEnabled(bool $enabled = true): self
    {
        $self = clone $this;
        $self->rotation = $enabled;

        return $self;
    }

    public function withTimeout(float $timeout): self
    {
        if ($timeout < 0) {
            throw new DnsConfigException("Invalid timeout ($timeout), must be 0 or greater");
        }

        $self = clone $this;
        $self->timeout = $timeout;

        return $self;
    }

    public function withAttempts(int $attempts): self
    {
        if ($attempts < 1) {
            throw new DnsConfigException("Invalid attempt count ($attempts), must be 1 or greater");
        }

        $self = clone $this;
        $self->attempts = $attempts;

        return $self;
    }

    public function getNameservers(): array
    {
        return $this->nameservers;
    }

    public function getKnownHosts(): array
    {
        return $this->knownHosts;
    }

    public function getTimeout(): float
    {
        return $this->timeout;
    }

    public function getAttempts(): int
    {
        return $this->attempts;
    }

    public function getSearchList(): array
    {
        return $this->searchList;
    }

    public function getNdots(): int
    {
        return $this->ndots;
    }

    public function isRotationEnabled(): bool
    {
        return $this->rotation;
    }

    /**
     * @throws DnsConfigException
     */
    private function validateNameserver(string $nameserver): void
    {
        if ($nameserver === "") {
            throw new DnsConfigException("Invalid nameserver: empty string");
        }

        if ($nameserver[0] === "[") { // IPv6
            $addrEnd = \strrpos($nameserver, "]");
            if ($addrEnd === false) {
                throw new DnsConfigException("Invalid nameserver: $nameserver");
            }

            $addr = \substr($nameserver, 1, $addrEnd - 1);
            $port = \substr($nameserver, $addrEnd + 1);

            if ($port !== "" && !\preg_match("(^:(\\d+)$)", $port)) {
                throw new DnsConfigException("Invalid nameserver: $nameserver");
            }

            $port = $port === "" ? 53 : \substr($port, 1);
        } else { // IPv4
            $arr = \explode(":", $nameserver, 2);

            if (\count($arr) === 2) {
                [$addr, $port] = $arr;
            } else {
                $addr = $arr[0];
                $port = 53;
            }
        }

        $addr = \trim($addr, "[]");
        $port = (int) $port;

        if (!\inet_pton($addr)) {
            throw new DnsConfigException("Invalid server IP: $addr");
        }

        if ($port < 1 || $port > 65535) {
            throw new DnsConfigException("Invalid server port: $port");
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\Dns;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;

final class UnixDnsConfigLoader implements DnsConfigLoader
{
    use ForbidCloning;
    use ForbidSerialization;

    public const MAX_NAMESERVERS = 3;
    public const MAX_DNS_SEARCH = 6;

    public const MAX_TIMEOUT = 30;
    public const MAX_ATTEMPTS = 5;
    public const MAX_NDOTS = 15;

    public const DEFAULT_TIMEOUT = 5;
    public const DEFAULT_ATTEMPTS = 2;
    public const DEFAULT_NDOTS = 1;

    public const DEFAULT_OPTIONS = [
        "timeout" => self::DEFAULT_TIMEOUT,
        "attempts" => self::DEFAULT_ATTEMPTS,
        "ndots" => self::DEFAULT_NDOTS,
        "rotate" => false,
    ];

    public function __construct(
        private readonly string $path = "/etc/resolv.conf",
        private readonly HostLoader $hostLoader = new HostLoader(),
    ) {
    }

    #[\Override]
    public function loadConfig(): DnsConfig
    {
        $nameservers = [];
        $searchList = [];
        $options = self::DEFAULT_OPTIONS;
        $haveLocaldomainEnv = false;

        /* Allow user to override the local domain definition.  */
        if ($localdomain = \getenv("LOCALDOMAIN")) {
            /* Set search list to be blank-separated strings from rest of
               env value.  Permits users of LOCALDOMAIN to still have a
               search list, and anyone to set the one that they want to use
               as an individual (even more important now that the rfc1535
               stuff restricts searches).  */
            $searchList = $this->splitOnWhitespace($localdomain);
            $haveLocaldomainEnv = true;
        }

        $fileContent = $this->readFile($this->path);

        $lines = \explode("\n", $fileContent);

        foreach ($lines as $line) {
            $line = \preg_split('#\s+#', $line, 2);
            \assert(\is_array($line)); // For Psalm.

            if (\count($line) !== 2) {
                continue;
            }

            [$type, $value] = $line;

            if ($type === "nameserver") {
                if (\count($nameservers) === self::MAX_NAMESERVERS) {
                    continue;
                }

                $value = \trim($value);
                $ip = \inet_pton($value);
                if ($ip === false) {
                    continue;
                }

                if (isset($ip[15])) { // IPv6
                    $nameservers[] = "[" . $value . "]:53";
                } else { // IPv4
                    $nameservers[] = $value . ":53";
                }
            } elseif ($type === "domain" && !$haveLocaldomainEnv) { // LOCALDOMAIN env overrides config
                $searchList = $this->splitOnWhitespace($value);
            } elseif ($type === "search" && !$haveLocaldomainEnv) { // LOCALDOMAIN env overrides config
                $searchList = $this->splitOnWhitespace($value);
            } elseif ($type === "options") {
                $option = $this->parseOption($value);
                if (\count($option) === 2) {
                    $options[$option[0]] = $option[1];
                }
            }
        }

        $hosts = $this->hostLoader->loadHosts();

        if (\count($searchList) === 0) {
            $hostname = \gethostname();
            if ($hostname === false) {
                throw new DnsConfigException("Could not determine hostname");
            }

            $dot = \strpos($hostname, ".");
            if ($dot !== false && $dot < \strlen($hostname)) {
                $searchList = [
                    \substr($hostname, $dot + 1),
                ];
            }
        }
        if (\count($searchList) > self::MAX_DNS_SEARCH) {
            $searchList = \array_slice($searchList, 0, self::MAX_DNS_SEARCH);
        }

        $resOptions = \getenv("RES_OPTIONS");
        if ($resOptions) {
            foreach ($this->splitOnWhitespace($resOptions) as $option) {
                $option = $this->parseOption($option);
                if (\count($option) === 2) {
                    $options[$option[0]] = $option[1];
                }
            }
        }

        \assert(\is_int($options["timeout"]) || \is_float($options["timeout"]));
        \assert(\is_int($options["attempts"]));
        \assert(\is_int($options["ndots"]));
        \assert(\is_bool($options["rotate"]));

        $config = new DnsConfig($nameservers, $hosts);

        return $config->withSearchList($searchList)
            ->withTimeout($options["timeout"])
            ->withAttempts($options["attempts"])
            ->withNdots($options["ndots"])
            ->withRotationEnabled($options["rotate"]);
    }

    private function readFile(string $path): string
    {
        \set_error_handler(static function (int $errno, string $message) use ($path) {
            throw new DnsConfigException("Could not read configuration file '{$path}' ({$errno}) $message");
        });

        try {
            // Blocking file access, but this file should be local and usually loaded only once.
            $contents = \file_get_contents($path);
        } finally {
            \restore_error_handler();
        }

        \assert(\is_string($contents)); // For Psalm, the error handler will throw for a false return case.

        return $contents;
    }

    private function splitOnWhitespace(string $names): array
    {
        $result = \preg_split("#\s+#", \trim($names));
        \assert(\is_array($result)); // For Psalm.

        return $result;
    }

    private function parseOption(string $option): array
    {
        $optline = \explode(':', $option, 2);
        [$name, $value] = $optline + [1 => null];

        switch ($name) {
            case "timeout":
                $value = (int) $value;
                if ($value < 0) {
                    return []; // don't overwrite option value
                }
                // The value for this option is silently capped to 30s
                return ["timeout", \min($value, self::MAX_TIMEOUT)];

            case "attempts":
                $value = (int) $value;
                if ($value < 0) {
                    return []; // don't overwrite option value
                }
                // The value for this option is silently capped to 5
                return ["attempts", \min($value, self::MAX_ATTEMPTS)];

            case "ndots":
                $value = (int) $value;
                if ($value < 0) {
                    return []; // don't overwrite option value
                }
                // The value for this option is silently capped to 15
                return ["ndots", \min($value, self::MAX_NDOTS)];

            case "rotate":
                return ["rotate", true];
        }

        return [];
    }
}
<?php declare(strict_types=1);

namespace Amp\Dns;

/**
 * MUST be thrown in case the config can't be read and no fallback is available.
 *
 * @psalm-suppress ClassMustBeFinal
 */
class DnsConfigException extends DnsException
{
    public function __construct(string $message, ?\Throwable $previous = null)
    {
        parent::__construct($message, 0, $previous);
    }
}
<?php declare(strict_types=1);

namespace Amp\Dns;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;

final class StaticDnsConfigLoader implements DnsConfigLoader
{
    use ForbidCloning;
    use ForbidSerialization;

    public function __construct(
        private readonly DnsConfig $config
    ) {
    }

    #[\Override]
    public function loadConfig(): DnsConfig
    {
        return $this->config;
    }
}
<?php declare(strict_types=1);

namespace Amp\Dns\Internal;

use Amp\CancelledException;
use Amp\DeferredFuture;
use Amp\Dns\DnsException;
use Amp\Dns\DnsTimeoutException;
use Amp\Parser\Parser;
use Amp\TimeoutCancellation;
use LibDNS\Decoder\DecoderFactory;
use LibDNS\Encoder\Encoder;
use LibDNS\Encoder\EncoderFactory;
use LibDNS\Messages\Message;
use Revolt\EventLoop;

/** @internal */
final class TcpSocket extends Socket
{
    /**
     * @throws DnsTimeoutException
     * @throws DnsException
     */
    #[\Override]
    public static function connect(string $uri, float $timeout = 5): self
    {
        $socket = self::openSocket($uri);

        \stream_set_blocking($socket, false);

        $deferred = new DeferredFuture;

        $watcher = EventLoop::onWritable($socket, static function (string $watcher) use ($socket, $deferred): void {
            EventLoop::cancel($watcher);

            $deferred->complete(new self($socket));
        });

        try {
            return $deferred->getFuture()->await(new TimeoutCancellation($timeout));
        } catch (CancelledException) {
            EventLoop::cancel($watcher);

            throw new DnsTimeoutException("Name resolution timed out, could not connect to server at $uri");
        }
    }

    private static function parser(callable $callback): \Generator
    {
        $decoder = (new DecoderFactory)->create();

        while (true) {
            /** @var string $length */
            $length = yield 2;
            /** @psalm-suppress PossiblyInvalidArrayAccess */
            $length = \unpack("n", $length)[1];

            $rawData = yield $length;
            $callback($decoder->decode($rawData));
        }
    }

    private readonly Encoder $encoder;
    private readonly \SplQueue $queue;
    private readonly Parser $parser;
    private bool $isAlive = true;

    /**
     * @param resource $socket
     */
    protected function __construct($socket)
    {
        parent::__construct($socket);

        $this->encoder = (new EncoderFactory)->create();
        $this->queue = new \SplQueue;
        $this->parser = new Parser(self::parser([$this->queue, 'push']));
    }

    #[\Override]
    public function isAlive(): bool
    {
        return $this->isAlive;
    }

    #[\Override]
    protected function send(Message $message): void
    {
        $data = $this->encoder->encode($message);
        try {
            $this->write(\pack("n", \strlen($data)) . $data);
        } catch (\Throwable $exception) {
            $this->isAlive = false;
            throw $exception;
        }
    }

    #[\Override]
    protected function receive(): Message
    {
        while ($this->queue->isEmpty()) {
            $chunk = $this->read();

            if ($chunk === null) {
                $this->isAlive = false;
                throw new DnsException("Reading from the server failed");
            }

            $this->parser->push($chunk);
        }

        return $this->queue->shift();
    }
}
<?php declare(strict_types=1);

namespace Amp\Dns\Internal;

use Amp\Dns\DnsException;
use LibDNS\Decoder\Decoder;
use LibDNS\Decoder\DecoderFactory;
use LibDNS\Encoder\Encoder;
use LibDNS\Encoder\EncoderFactory;
use LibDNS\Messages\Message;

/** @internal */
final class UdpSocket extends Socket
{
    /**
     * @throws DnsException
     */
    #[\Override]
    public static function connect(string $uri): self
    {
        return new self(self::openSocket($uri));
    }

    private readonly Encoder $encoder;
    private readonly Decoder $decoder;

    /**
     * @param resource $socket
     */
    protected function __construct($socket)
    {
        parent::__construct($socket);

        $this->encoder = (new EncoderFactory)->create();
        $this->decoder = (new DecoderFactory)->create();
    }

    #[\Override]
    public function isAlive(): bool
    {
        return true;
    }

    #[\Override]
    protected function send(Message $message): void
    {
        $data = $this->encoder->encode($message);
        $this->write($data);
    }

    #[\Override]
    protected function receive(): Message
    {
        while (true) {
            $data = $this->read();

            if ($data === null) {
                throw new DnsException("Reading from the server failed");
            }

            try {
                return $this->decoder->decode($data);
            } catch (\Exception) {
                $this->invalidPacketsReceived++;

                continue;
            }
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\Dns\Internal;

use Amp\ByteStream\ClosedException;
use Amp\ByteStream\ReadableResourceStream;
use Amp\ByteStream\StreamException;
use Amp\ByteStream\WritableResourceStream;
use Amp\Cancellation;
use Amp\DeferredFuture;
use Amp\Dns\DnsException;
use Amp\Dns\DnsTimeoutException;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use LibDNS\Messages\Message;
use LibDNS\Messages\MessageFactory;
use LibDNS\Messages\MessageTypes;
use LibDNS\Records\Question;
use Revolt\EventLoop;
use function Amp\now;
use function Amp\weakClosure;

/** @internal */
abstract class Socket
{
    use ForbidCloning;
    use ForbidSerialization;

    private const MAX_CONCURRENT_REQUESTS = 500;

    protected int $invalidPacketsReceived = 0;

    abstract public static function connect(string $uri): self;

    private readonly ReadableResourceStream $input;

    private readonly WritableResourceStream $output;

    /**
     * Contains already sent queries with no response yet. For UDP this is exactly zero or one item.
     *
     * @var \ArrayObject<int, object{deferred: DeferredFuture|null, question: Question}>
     */
    private readonly \ArrayObject $pending;

    private readonly MessageFactory $messageFactory;

    /** @var float Used for determining whether the socket can be garbage collected, because it's inactive. */
    private float $lastActivity;

    private bool $receiving = false;

    /** @var \SplQueue<EventLoop\Suspension> Queued requests if the number of concurrent requests is too large. */
    private readonly \SplQueue $queue;

    /**
     * @return resource
     */
    final protected static function openSocket(string $uri)
    {
        \set_error_handler(static fn () => true);

        try {
            $socket = \stream_socket_client($uri, $errno, $errstr, flags: \STREAM_CLIENT_ASYNC_CONNECT);
        } finally {
            \restore_error_handler();
        }

        if (!$socket) {
            throw new DnsException(\sprintf(
                'Connection to %s failed: (Error #%d) %s',
                $uri,
                $errno,
                $errstr,
            ));
        }

        return $socket;
    }

    /**
     * @param resource $socket
     */
    protected function __construct($socket)
    {
        $this->pending = new \ArrayObject();
        $this->queue = new \SplQueue();

        $this->input = new ReadableResourceStream($socket);
        $this->output = new WritableResourceStream($socket);
        $this->messageFactory = new MessageFactory();
        $this->lastActivity = now();
    }

    private function fetch(): void
    {
        EventLoop::queue(function (): void {
            try {
                try {
                    $message = $this->receive();
                } finally {
                    $this->lastActivity = now();
                    $this->receiving = false;
                }
            } catch (\Throwable $exception) {
                $this->handleError($exception);
                return;
            }

            $this->handleMessage($message);
        });
    }

    private function handleMessage(Message $message): void
    {
        $id = $message->getId();

        // Ignore duplicate and invalid responses.
        if (isset($this->pending[$id]) && $this->matchesQuestion($message, $this->pending[$id]->question)) {
            $pending = $this->pending[$id];
            unset($this->pending[$id]);

            $pending->deferred?->complete(static fn () => $message);
            $pending->deferred = null;
        }

        /** @psalm-suppress RedundantCondition */
        if (!$this->pending->count()) {
            $this->input->unreference();
        } elseif (!$this->receiving) {
            $this->input->reference();
            $this->receiving = true;
            $this->fetch();
        }
    }

    abstract public function isAlive(): bool;

    public function getLastActivity(): float
    {
        return $this->lastActivity;
    }

    /**
     * @throws DnsException
     */
    final public function ask(Question $question, float $timeout, ?Cancellation $cancellation = null): Message
    {
        $this->lastActivity = now();

        if ($this->pending->count() > self::MAX_CONCURRENT_REQUESTS) {
            $suspension = EventLoop::getSuspension();
            $this->queue->enqueue($suspension);
            $suspension->suspend();
        }

        do {
            $id = \random_int(0, 0xffff);
        } while (isset($this->pending[$id]));

        /** @var DeferredFuture<\Closure():Message> $deferred */
        $deferred = new DeferredFuture;

        $invalidPacketsReceived = &$this->invalidPacketsReceived;

        /** @psalm-suppress InaccessibleProperty, InvalidArgument $this->pending is an ArrayObject */
        $this->pending[$id] = new class($this->pending, $id, $deferred, $question, $timeout, $invalidPacketsReceived) {
            private readonly string $callbackId;

            public ?DeferredFuture $deferred;

            public function __construct(
                \ArrayObject $pending,
                int $id,
                DeferredFuture $deferred,
                public readonly Question $question,
                float $timeout,
                int &$invalidPacketsReceived
            ) {
                $this->deferred = $deferred;

                $this->callbackId = EventLoop::unreference(EventLoop::delay(
                    $timeout,
                    weakClosure(function () use ($id, $pending, $timeout, &$invalidPacketsReceived): void {
                        if ($invalidPacketsReceived > 0) {
                            $this->deferred?->complete(static fn () => throw new DnsTimeoutException(
                                "Didn't receive a response within {$timeout} seconds, but received {$invalidPacketsReceived} invalid packets on this socket"
                            ));
                        } else {
                            $this->deferred?->complete(static fn () => throw new DnsTimeoutException(
                                "Didn't receive a response within {$timeout} seconds."
                            ));
                        }

                        $this->deferred = null;

                        unset($pending[$id]);
                    }),
                ));
            }

            public function __destruct()
            {
                EventLoop::cancel($this->callbackId);
            }
        };

        $message = $this->createMessage($question, $id);

        try {
            $this->send($message);
        } catch (StreamException $exception) {
            $exception = new DnsException("Sending the request failed", 0, $exception);
            $this->handleError($exception);
            throw $exception;
        }

        $this->input->reference();

        if (!$this->receiving) {
            $this->receiving = true;
            $this->fetch();
        }

        try {
            $callback = $deferred->getFuture()->await($cancellation);
        } finally {
            /** @psalm-suppress TypeDoesNotContainType */
            if (!$this->pending->count()) {
                $this->input->unreference();
            }

            if (!$this->queue->isEmpty()) {
                $suspension = $this->queue->dequeue();
                $suspension->resume();
            }
        }

        return $callback();
    }

    final public function close(): void
    {
        $this->handleError(new ClosedException('Socket has been closed'));
    }

    /**
     * @throws StreamException
     */
    abstract protected function send(Message $message): void;

    /**
     * @throws DnsException
     */
    abstract protected function receive(): Message;

    final protected function read(): ?string
    {
        return $this->input->read();
    }

    /**
     * @throws ClosedException
     */
    final protected function write(string $data): void
    {
        $this->output->write($data);
    }

    final protected function createMessage(Question $question, int $id): Message
    {
        $request = $this->messageFactory->create(MessageTypes::QUERY);
        $request->getQuestionRecords()->add($question);
        $request->isRecursionDesired(true);
        $request->setID($id);

        return $request;
    }

    private function handleError(\Throwable $exception): void
    {
        $this->input->close();
        $this->output->close();

        if (!$exception instanceof DnsException) {
            $message = "Unexpected error during resolution: " . $exception->getMessage();
            $exception = new DnsException($message, 0, $exception);
        }

        foreach ($this->pending as $id => $pendingQuestion) {
            $pendingQuestion->deferred?->error($exception);
            $pendingQuestion->deferred = null;

            unset($this->pending[$id]);
        }

        while (!$this->queue->isEmpty()) {
            $this->queue->dequeue()->throw($exception);
        }
    }

    private function matchesQuestion(Message $message, Question $question): bool
    {
        if ($message->getType() !== MessageTypes::RESPONSE) {
            return false;
        }

        $questionRecords = $message->getQuestionRecords();

        // We only ever ask one question at a time
        if (\count($questionRecords) !== 1) {
            return false;
        }

        $questionRecord = $questionRecords->getIterator()->current();

        if ($questionRecord->getClass() !== $question->getClass()) {
            return false;
        }

        if ($questionRecord->getType() !== $question->getType()) {
            return false;
        }

        if ($questionRecord->getName()->getValue() !== $question->getName()->getValue()) {
            return false;
        }

        return true;
    }
}
<?php declare(strict_types=1);

namespace Amp\Dns;

/**
 * @psalm-suppress ClassMustBeFinal
 */
class InvalidNameException extends DnsException
{
}
<?php declare(strict_types=1);

namespace Amp\Dns;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Process\Process;
use function Amp\ByteStream\buffer;
use function Amp\ByteStream\splitLines;

final class WindowsDnsConfigLoader implements DnsConfigLoader
{
    use ForbidCloning;
    use ForbidSerialization;

    public function __construct(
        private readonly HostLoader $hostLoader = new HostLoader(),
    ) {
    }

    #[\Override]
    public function loadConfig(): DnsConfig
    {
        $powershell = Process::start([
            'powershell',
            '-Command',
            'Get-WmiObject -Class Win32_NetworkAdapterConfiguration |
                Select-Object -ExpandProperty DNSServerSearchOrder',
        ]);

        if ($powershell->join() !== 0) {
            throw new DnsConfigException("Could not fetch DNS servers from WMI: " . buffer($powershell->getStderr()));
        }

        $output = \iterator_to_array(splitLines($powershell->getStdout()));

        $nameservers = \array_reduce($output, static function (array $nameservers, string $address): array {
            $ip = \inet_pton($address);
            if ($ip === false) {
                return $nameservers;
            }

            if (isset($ip[15])) { // IPv6
                $nameservers[] = "[$address]:53";
            } elseif (isset($ip[3])) { // IPv4
                $nameservers[] = "$address:53";
            }

            return $nameservers;
        }, []);

        $hosts = $this->hostLoader->loadHosts();

        return new DnsConfig($nameservers, $hosts);
    }
}
<?php declare(strict_types=1);

namespace Amp\Dns;

use Amp\Cancellation;
use Revolt\EventLoop;

/**
 * Retrieve the application-wide DNS resolver instance.
 *
 * @param DnsResolver|null $dnsResolver Optionally specify a new default DNS resolver instance
 *
 * @return DnsResolver Returns the application-wide DNS resolver instance
 */
function dnsResolver(?DnsResolver $dnsResolver = null): DnsResolver
{
    static $map;
    $map ??= new \WeakMap();
    $driver = EventLoop::getDriver();

    if ($dnsResolver) {
        return $map[$driver] = $dnsResolver;
    }

    return $map[$driver] ??= createDefaultResolver();
}

/**
 * Create a new DNS resolver best-suited for the current environment.
 */
function createDefaultResolver(): DnsResolver
{
    return new Rfc1035StubDnsResolver;
}

/**
 * @throws DnsException
 *@see DnsResolver::resolve()
 *
 */
function resolve(string $name, ?int $typeRestriction = null, ?Cancellation $cancellation = null): array
{
    return dnsResolver()->resolve($name, $typeRestriction, $cancellation);
}

/**
 * @throws DnsException
 *@see DnsResolver::query()
 *
 */
function query(string $name, int $type, ?Cancellation $cancellation = null): array
{
    return dnsResolver()->query($name, $type, $cancellation);
}

/**
 * Checks whether a string is a valid DNS name.
 *
 * @param string $name String to check.
 */
function isValidName(string $name): bool
{
    try {
        normalizeName($name);
        return true;
    } catch (InvalidNameException) {
        return false;
    }
}

/**
 * Normalizes a DNS name and automatically checks it for validity.
 *
 * @param string $name DNS name.
 *
 * @return string Normalized DNS name.
 * @throws InvalidNameException If an invalid name or an IDN name without ext/intl being installed has been passed.
 */
function normalizeName(string $name): string
{
    static $pattern = '/^(?<name>[a-z0-9]([a-z0-9-_]{0,61}[a-z0-9])?)(\.(?&name))*\.?$/i';

    if (\function_exists('idn_to_ascii') && \defined('INTL_IDNA_VARIANT_UTS46')) {
        if (false === $result = \idn_to_ascii($name, 0, \INTL_IDNA_VARIANT_UTS46)) {
            throw new InvalidNameException("Name '{$name}' could not be processed for IDN.");
        }

        $name = $result;
    } elseif (\preg_match('/[\x80-\xff]/', $name)) {
        throw new InvalidNameException(
            "Name '{$name}' contains non-ASCII characters and IDN support is not available. " .
            "Verify that ext/intl is installed for IDN support and that ICU is at least version 4.6."
        );
    }

    if (isset($name[253]) || !\preg_match($pattern, $name)) {
        throw new InvalidNameException("Name '{$name}' is not a valid hostname.");
    }

    if ($name[-1] === '.') {
        $name = \substr($name, 0, -1);
    }

    return $name;
}
<?php declare(strict_types=1);

namespace Amp\Dns;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;

final class HostLoader
{
    use ForbidCloning;
    use ForbidSerialization;

    private readonly string $path;

    public function __construct(?string $path = null)
    {
        $this->path = $path ?? $this->getDefaultPath();
    }

    public function loadHosts(): array
    {
        try {
            $contents = $this->readFile($this->path);
        } catch (DnsConfigException) {
            return [];
        }

        $data = [];

        $lines = \array_filter(\array_map("trim", \explode("\n", $contents)));

        foreach ($lines as $line) {
            // Strip inline comments; # starts a comment anywhere on the line.
            if (($commentStart = \strpos($line, "#")) !== false) {
                $line = \substr($line, 0, $commentStart);
            }

            $parts = \preg_split('/\s+/', \trim($line));
            \assert(\is_array($parts)); // For Psalm.

            if (!($ip = \inet_pton($parts[0]))) {
                continue;
            }

            if (isset($ip[4])) {
                $key = DnsRecord::AAAA;
            } else {
                $key = DnsRecord::A;
            }

            for ($i = 1, $l = \count($parts); $i < $l; $i++) {
                try {
                    $normalizedName = normalizeName($parts[$i]);
                    $data[$key][$normalizedName] = $parts[0];
                } catch (InvalidNameException) {
                    // ignore invalid entries
                }
            }
        }

        return $data;
    }

    public function readFile(string $path): string
    {
        \set_error_handler(static function (int $errno, string $message) use ($path) {
            throw new DnsConfigException("Could not read configuration file '{$path}' ({$errno}): $message");
        });

        try {
            // Blocking file access, but this file should be local and usually loaded only once.
            $contents = \file_get_contents($path);
        } finally {
            \restore_error_handler();
        }

        \assert(\is_string($contents)); // For Psalm, the error handler will throw for a false return case.

        return $contents;
    }

    private function getDefaultPath(): string
    {
        return \PHP_OS_FAMILY === 'Windows'
            ? 'C:\Windows\system32\drivers\etc\hosts'
            : '/etc/hosts';
    }
}
{
    "name": "amphp/dns",
    "homepage": "https://github.com/amphp/dns",
    "description": "Async DNS resolution for Amp.",
    "keywords": [
        "dns",
        "resolve",
        "client",
        "async",
        "amp",
        "amphp"
    ],
    "license": "MIT",
    "authors": [
        {
            "name": "Chris Wright",
            "email": "addr@daverandom.com"
        },
        {
            "name": "Daniel Lowrey",
            "email": "rdlowrey@php.net"
        },
        {
            "name": "Bob Weinand",
            "email": "bobwei9@hotmail.com"
        },
        {
            "name": "Niklas Keller",
            "email": "me@kelunik.com"
        },
        {
            "name": "Aaron Piotrowski",
            "email": "aaron@trowski.com"
        }
    ],
    "require": {
        "php": ">=8.1",
        "ext-filter": "*",
        "ext-json": "*",
        "amphp/amp": "^3",
        "amphp/byte-stream": "^2",
        "amphp/cache": "^2",
        "amphp/parser": "^1",
        "amphp/process": "^2",
        "daverandom/libdns": "^2.0.2",
        "revolt/event-loop": "^1 || ^0.2"
    },
    "require-dev": {
        "amphp/phpunit-util": "^3",
        "phpunit/phpunit": "^9",
        "amphp/php-cs-fixer-config": "^2",
        "psalm/phar": "6.16.1"
    },
    "autoload": {
        "psr-4": {
            "Amp\\Dns\\": "src"
        },
        "files": [
            "src/functions.php"
        ]
    },
    "autoload-dev": {
        "psr-4": {
            "Amp\\Dns\\Test\\": "test"
        }
    },
    "scripts": {
        "check": [
            "@cs",
            "@test"
        ],
        "cs": "PHP_CS_FIXER_IGNORE_ENV=1 php-cs-fixer fix -v --diff --dry-run",
        "cs-fix": "PHP_CS_FIXER_IGNORE_ENV=1 php-cs-fixer fix -v --diff",
        "test": "@php -dzend.assertions=1 -dassert.exception=1 ./vendor/bin/phpunit --coverage-text"
    }
}
<?php declare(strict_types=1);

use Amp\File;
use Amp\Log\StreamHandler;
use Monolog\Formatter\LineFormatter;
use Monolog\Logger;

require dirname(__DIR__) . '/vendor/autoload.php';

// This example requires amphp/file to be installed.

$file = File\openFile(__DIR__ . '/example.log', 'w');

$handler = new StreamHandler($file);
$handler->setFormatter(new LineFormatter());

$logger = new Logger('hello-world');
$logger->pushHandler($handler);

$logger->debug("Hello, world!");
$logger->info("Hello, world!");
$logger->notice("Hello, world!");
$logger->warning("Hello, world!");
$logger->error("Hello, world!");
$logger->critical("Hello, world!");
$logger->alert("Hello, world!");
$logger->emergency("Hello, world!");
<?php declare(strict_types=1);

use Amp\ByteStream;
use Amp\Log\ConsoleFormatter;
use Amp\Log\StreamHandler;
use Monolog\Logger;

require dirname(__DIR__) . '/vendor/autoload.php';

$handler = new StreamHandler(ByteStream\getStdout());
$handler->setFormatter(new ConsoleFormatter());

$logger = new Logger('hello-world');
$logger->pushHandler($handler);

$logger->debug("Hello, world!");
$logger->info("Hello, world!");
$logger->notice("Hello, world!");
$logger->warning("Hello, world!");
$logger->error("Hello, world!");
$logger->critical("Hello, world!");
$logger->alert("Hello, world!");
$logger->emergency("Hello, world!");
<?php

$config = new Amp\CodeStyle\Config;

$config->getFinder()
    ->in(__DIR__ . '/examples')
    ->in(__DIR__ . '/src');

$config->setCacheFile(__DIR__ . '/.php-cs-fixer.cache');

return $config;
{
    "symbol-whitelist": [
        "null",
        "true",
        "false",
        "static",
        "self",
        "parent",
        "array",
        "string",
        "int",
        "float",
        "bool",
        "iterable",
        "callable",
        "mixed",
        "void",
        "object",
        "hasColorSupport",
        "PHP_WINDOWS_VERSION_BUILD",
        "PHP_WINDOWS_VERSION_MINOR",
        "PHP_WINDOWS_VERSION_MAJOR",
        "posix_isatty",
        "sapi_windows_vt100_support"
    ],
    "php-core-extensions": [
        "Core",
        "date",
        "pcre",
        "Phar",
        "Reflection",
        "SPL",
        "standard",
        "hash"
    ]
}
<?php declare(strict_types=1);

namespace Amp\Log;

use Amp\ByteStream\WritableStream;
use Monolog\Handler\AbstractProcessingHandler;
use Monolog\Level;
use Monolog\LogRecord;
use Psr\Log\LogLevel;

final class StreamHandler extends AbstractProcessingHandler
{
    private WritableStream $sink;

    /**
     * @param WritableStream $sink Stream to write the logs to
     * @param Level|value-of<Level>|LogLevel::* $level The minimum logging level at which this handler will be triggered
     * @param bool $bubble Whether the messages that are handled can bubble up the stack or not
     */
    public function __construct(WritableStream $sink, int|string|Level $level = LogLevel::DEBUG, bool $bubble = true)
    {
        /** @psalm-suppress PossiblyInvalidArgument */
        parent::__construct($level, $bubble);

        $this->sink = $sink;
    }

    /**
     * @param array{formatted: string}|LogRecord $record Array for Monolog v1.x or 2.x and LogRecord for v3.x.
     */
    protected function write(array|LogRecord $record): void
    {
        $formatted = \is_array($record) ? $record['formatted'] : $record->formatted;

        if (!\is_string($formatted)) {
            throw new \ValueError(\sprintf(
                '%s only supports writing string formatted log records, got "%s"',
                self::class,
                \get_debug_type($formatted),
            ));
        }

        $this->sink->write($formatted);
    }
}
<?php declare(strict_types=1);

namespace Amp\Log;

use Monolog\Formatter\LineFormatter;
use Monolog\LogRecord;
use Psr\Log\LogLevel;

final class ConsoleFormatter extends LineFormatter
{
    public const DEFAULT_FORMAT = "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\r\n";

    private readonly bool $ansify;

    public function __construct(
        ?string $format = null,
        ?string $dateFormat = null,
        bool $allowInlineLineBreaks = false,
        bool $ignoreEmptyContextAndExtra = false,
    ) {
        parent::__construct(
            $format ?? self::DEFAULT_FORMAT,
            $dateFormat,
            $allowInlineLineBreaks,
            $ignoreEmptyContextAndExtra
        );

        $this->includeStacktraces = false;
        $this->ansify = $this->determineAnsiColorOption();
    }

    /**
     * Applies ansi colors to log record for Monolog v1.x and v2.x.
     *
     * @param array{level_name: string, channel: string}|LogRecord $record Array for Monolog v1.x or 2.x
     *      and LogRecord for v3.x.
     */
    public function format(array|LogRecord $record): string
    {
        // For Monolog v1.x and v2.x
        if (\is_array($record) && $this->ansify) {
            $record['level_name'] = $this->ansifyLevel(\strtolower($record['level_name']));
            $record['channel'] = "\033[1m{$record['channel']}\033[0m";
        }

        /** @psalm-suppress PossiblyInvalidArgument */
        return parent::format($record);
    }

    /**
     * Applies ansi colors to log record for Monolog v3.x.
     */
    protected function normalizeRecord(LogRecord $record): array
    {
        $fields = parent::normalizeRecord($record);

        if ($this->ansify) {
            $fields['level_name'] = $this->ansifyLevel($record->level->toPsrLogLevel());
            $fields['channel'] = "\033[1m{$record->channel}\033[0m";
        }

        return $fields;
    }

    private function determineAnsiColorOption(): bool
    {
        $value = \getenv("AMP_LOG_COLOR");
        if ($value === false || $value === '') {
            $value = "auto";
        }

        return match (\strtolower($value)) {
            "1", "true", "on" => true,
            "0", "false", "off" => false,
            default => hasColorSupport(),
        };
    }

    private function ansifyLevel(string $level): string
    {
        return "\033[" . match ($level) {
            LogLevel::EMERGENCY => "43",
            LogLevel::ALERT => "45",
            LogLevel::CRITICAL => "41",
            LogLevel::ERROR => "0;31",
            LogLevel::WARNING => "0;33",
            LogLevel::NOTICE => "0;32",
            LogLevel::INFO => "0;34",
            LogLevel::DEBUG => "0;36",
            default => "40",
        } . "m" . $level . "\033[0m";
    }
}
<?php declare(strict_types=1);

namespace Amp\Log;

function hasColorSupport(): bool
{
    static $supported;

    if ($supported !== null) {
        return $supported;
    }

    \set_error_handler(static fn () => true);

    try {
        // @see https://github.com/symfony/symfony/blob/v4.0.6/src/Symfony/Component/Console/Output/StreamOutput.php#L91
        // @license https://github.com/symfony/symfony/blob/v4.0.6/LICENSE
        if (\PHP_OS_FAMILY === 'Windows') {
            /** @psalm-suppress UndefinedConstant */
            $windowsVersion = \sprintf(
                '%d.%d.%d',
                \PHP_WINDOWS_VERSION_MAJOR,
                \PHP_WINDOWS_VERSION_MINOR,
                \PHP_WINDOWS_VERSION_BUILD,
            );

            return $supported = (\function_exists('sapi_windows_vt100_support') && sapi_windows_vt100_support(\STDOUT))
                || $windowsVersion === '10.0.10586' // equals is correct here, newer versions use the above function
                || false !== \getenv('ANSICON')
                || 'ON' === \getenv('ConEmuANSI')
                || 'xterm' === \getenv('TERM');
        }

        if (\function_exists('posix_isatty')) {
            return $supported = \posix_isatty(\STDOUT);
        }
    } finally {
        \restore_error_handler();
    }

    return $supported = false;
}
{
    "name": "amphp/log",
    "homepage": "https://github.com/amphp/log",
    "description": "Non-blocking logging for PHP based on Amp, Revolt, and Monolog.",
    "keywords": [
        "log",
        "logging",
        "logger",
        "async",
        "non-blocking",
        "amp",
        "amphp"
    ],
    "license": "MIT",
    "authors": [
        {
            "name": "Aaron Piotrowski",
            "email": "aaron@trowski.com"
        },
        {
            "name": "Niklas Keller",
            "email": "me@kelunik.com"
        }
    ],
    "require": {
        "php": ">=8.1",
        "amphp/amp": "^3",
        "amphp/byte-stream": "^2",
        "monolog/monolog": "^3|^2|^1.23",
        "psr/log": "^3|^2|^1"
    },
    "require-dev": {
        "amphp/phpunit-util": "^3",
        "amphp/file": "^3",
        "amphp/php-cs-fixer-config": "^2",
        "phpunit/phpunit": "^9",
        "psalm/phar": "^5.6"
    },
    "autoload": {
        "psr-4": {
            "Amp\\Log\\": "src"
        },
        "files": [
            "src/functions.php"
        ]
    },
    "scripts": {
        "test": "@php -dzend.assertions=1 -dassert.exception=1 ./vendor/bin/phpunit",
        "code-style": "@php ./vendor/bin/php-cs-fixer fix"
    }
}
<?php

$config = new Amp\CodeStyle\Config();
$config->getFinder()
    ->in(__DIR__ . '/examples')
    ->in(__DIR__ . '/src')
    ->in(__DIR__ . '/test');

$config->setCacheFile(__DIR__ . '/.php_cs.cache');

return $config;
<?php declare(strict_types=1);

namespace Amp\Parser;

class Parser
{
    /** @var \Generator<array-key, int|string|null, string, void>|null */
    private ?\Generator $generator;

    /** @var list<string> */
    private array $buffers = [];

    private int $bufferLength = 0;

    /** @var int|string|null */
    private $delimiter;

    /**
     * @param \Generator<array-key, int|string|null, string, void> $generator
     *
     * @throws InvalidDelimiterError If the generator yields an invalid delimiter.
     * @throws \Throwable If the generator throws.
     */
    public function __construct(\Generator $generator)
    {
        $this->generator = $generator;
        $this->delimiter = $this->filterDelimiter($this->generator->current());

        if (!$this->generator->valid()) {
            $this->generator = null;
        }
    }

    /**
     * Cancels the generator parser and returns any remaining data in the internal buffer. Writing data after calling
     * this method will result in an error.
     */
    final public function cancel(): string
    {
        $buffer = \implode($this->buffers);

        $this->buffers = [];
        $this->generator = null;

        return $buffer;
    }

    /**
     * @return bool True if the parser can still receive more data to parse, false if it has ended and calling push
     *     will throw an exception.
     */
    final public function isValid(): bool
    {
        return $this->generator !== null;
    }

    /**
     * Adds data to the internal buffer and tries to continue parsing.
     *
     * @param string $data Data to append to the internal buffer.
     *
     * @throws InvalidDelimiterError If the generator yields an invalid delimiter.
     * @throws \Error If parsing has already been cancelled.
     * @throws \Throwable If the generator throws.
     */
    final public function push(string $data): void
    {
        if ($this->generator === null) {
            throw new \Error("The parser is no longer writable");
        }

        $length = \strlen($data);
        if ($length === 0) {
            return;
        }

        $this->bufferLength += $length;

        try {
            do {
                if (\is_int($this->delimiter) && $this->bufferLength < $this->delimiter) {
                    return;
                }

                if (!empty($this->buffers)) {
                    $this->buffers[] = $data;
                    $data = \implode($this->buffers);
                    $this->buffers = [];
                }

                if (\is_int($this->delimiter)) {
                    $cutAt = $retainFrom = $this->delimiter;
                } elseif (\is_string($this->delimiter)) {
                    if (($cutAt = \strpos($data, $this->delimiter)) === false) {
                        return;
                    }

                    $retainFrom = $cutAt + \strlen($this->delimiter);
                } else {
                    $cutAt = $retainFrom = $this->bufferLength;
                }

                if ($this->bufferLength > $cutAt) {
                    $send = \substr($data, 0, $cutAt);
                    $data = \substr($data, $retainFrom);
                } else {
                    $send = $data;
                    $data = '';
                }

                $this->bufferLength -= $retainFrom;

                $this->delimiter = $this->filterDelimiter($this->generator->send($send));

                if (!$this->generator->valid()) {
                    $this->generator = null;
                    return;
                }
            } while ($this->bufferLength);
        } catch (\Throwable $exception) {
            $this->generator = null;
            throw $exception;
        } finally {
            if (\strlen($data)) {
                $this->buffers[] = $data;
            }
        }
    }

    /**
     * @param mixed $delimiter Value yielded from Generator.
     * @return int|string|null
     */
    private function filterDelimiter($delimiter)
    {
        \assert($this->generator instanceof \Generator, "Invalid parser state");

        if ($delimiter !== null
            && (!\is_int($delimiter) || $delimiter <= 0)
            && (!\is_string($delimiter) || !\strlen($delimiter))
        ) {
            throw new InvalidDelimiterError(
                $this->generator,
                \sprintf(
                    "Invalid value yielded: Expected NULL, an int greater than 0, or a non-empty string; %s given",
                    \is_object($delimiter) ? \sprintf("instance of %s", \get_class($delimiter)) : \gettype($delimiter),
                )
            );
        }

        return $delimiter;
    }
}
<?php declare(strict_types=1);

namespace Amp\Parser;

class InvalidDelimiterError extends \Error
{
    public function __construct(\Generator $generator, string $prefix, ?\Throwable $previous = null)
    {
        $yielded = $generator->current();
        $prefix .= \sprintf(
            "; %s yielded at key %s",
            \is_object($yielded) ? \get_class($yielded) : \gettype($yielded),
            \var_export($generator->key(), true)
        );

        if (!$generator->valid()) {
            parent::__construct($prefix, 0, $previous);
            return;
        }

        $reflGen = new \ReflectionGenerator($generator);
        $exeGen = $reflGen->getExecutingGenerator();
        if ($isSubgenerator = ($exeGen !== $generator)) {
            $reflGen = new \ReflectionGenerator($exeGen);
        }

        parent::__construct(\sprintf(
            "%s on line %s in %s",
            $prefix,
            $reflGen->getExecutingLine(),
            $reflGen->getExecutingFile()
        ), 0, $previous);
    }
}
{
    "name": "amphp/parser",
    "homepage": "https://github.com/amphp/parser",
    "description": "A generator parser to make streaming parsers simple.",
    "support": {
        "issues": "https://github.com/amphp/parser/issues"
    },
    "keywords": [
        "stream",
        "async",
        "non-blocking",
        "parser"
    ],
    "license": "MIT",
    "authors": [
        {
            "name": "Aaron Piotrowski",
            "email": "aaron@trowski.com"
        },
        {
            "name": "Niklas Keller",
            "email": "me@kelunik.com"
        }
    ],
    "require": {
        "php": ">=7.4"
    },
    "require-dev": {
        "phpunit/phpunit": "^9",
        "amphp/php-cs-fixer-config": "^2",
        "psalm/phar": "^5.4"
    },
    "autoload": {
        "psr-4": {
            "Amp\\Parser\\": "src"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Amp\\Parser\\Test\\": "test"
        }
    }
}
<?php

$config = new Amp\CodeStyle\Config;
$config->getFinder()
    ->in(__DIR__ . '/src')
    ->in(__DIR__ . '/test');

$config->setCacheFile(__DIR__ . '/.php_cs.cache');

return $config;
<?php declare(strict_types=1);

namespace Amp\Sql\Common\Test;

use Amp\PHPUnit\AsyncTestCase;
use Amp\Sql\Common\Test\Stub\StubSqlPooledResult;
use Amp\Sql\Common\Test\Stub\StubSqlResult;
use function Amp\delay;

class SqlPooledResultTest extends AsyncTestCase
{
    public function testIdleConnectionsRemovedAfterTimeout()
    {
        $invoked = false;

        $release = function () use (&$invoked) {
            $invoked = true;
        };

        $expectedRow = ['column' => 'value'];

        $secondResult = new StubSqlResult([$expectedRow]);
        $firstResult = new StubSqlResult([$expectedRow], $secondResult);
        $pooledResult = new StubSqlPooledResult(new StubSqlResult([$expectedRow], $firstResult), $release);

        $iterator = $pooledResult->getIterator();

        self::assertSame($expectedRow, $iterator->current());

        self::assertFalse($invoked);

        $iterator->next();
        self::assertFalse($iterator->valid());

        self::assertFalse($invoked); // Next result set available.

        $pooledResult = $pooledResult->getNextResult();
        $iterator = $pooledResult->getIterator();

        self::assertSame($expectedRow, $iterator->current());

        $iterator->next();
        self::assertFalse($iterator->valid());

        $pooledResult = $pooledResult->getNextResult();
        unset($pooledResult); // Manually unset to trigger destructor.

        delay(0); // Tick event loop to dispose of result set.

        self::assertTrue($invoked); // No next result set, so release callback invoked.
    }

    public function testIteratorRetainsReference(): void
    {
        $expectedRow = ['column' => 'value'];
        $expectedRows = [$expectedRow, $expectedRow, $expectedRow];
        $stubResult = new StubSqlResult($expectedRows);

        $invoked = false;
        $release = function () use (&$invoked) {
            $invoked = true;
        };

        $iterationCount = 0;
        foreach ((new StubSqlPooledResult($stubResult, $release)) as $row) {
            ++$iterationCount;

            delay(0); // Tick event loop to allow entry into disposal function if queued in event loop.

            self::assertSame($expectedRow, $row);
            self::assertFalse($invoked);
        }

        self::assertSame(\count($expectedRows), $iterationCount);

        delay(0); // Tick event loop to dispose of result set.

        self::assertTrue($invoked);
    }
}
<?php declare(strict_types=1);

namespace Amp\Sql\Common\Test;

use Amp\Future;
use Amp\PHPUnit\AsyncTestCase;
use Amp\Sql\Common\SqlCommonConnectionPool;
use Amp\Sql\Common\Test\Stub\StubSqlPooledResult;
use Amp\Sql\SqlConfig;
use Amp\Sql\SqlConnection;
use Amp\Sql\SqlConnector;
use Amp\Sql\SqlResult;
use function Amp\async;
use function Amp\delay;

class SqlCommonConnectionPoolTest extends AsyncTestCase
{
    public function testInvalidMaxConnections()
    {
        $this->expectException(\Error::class);
        $this->expectExceptionMessage('Pool must contain at least one connection');

        $this->getMockBuilder(SqlCommonConnectionPool::class)
            ->setConstructorArgs([
                $this->createMock(SqlConfig::class),
                $this->createMock(SqlConnector::class),
                0,
            ])
            ->getMock();
    }

    private function createConnector(): SqlConnector
    {
        $now = \time();

        $connector = $this->createMock(SqlConnector::class);
        $connector->method('connect')
            ->willReturnCallback(function () use ($now): SqlConnection {
                $connection = $this->createMock(SqlConnection::class);
                $connection->method('getLastUsedAt')
                    ->willReturn($now);

                $connection->method('isClosed')
                    ->willReturn(false);

                $connection->method('query')
                    ->willReturnCallback(function () {
                        delay(0.1);
                        return $this->createMock(SqlResult::class);
                    });

                return $connection;
            });

        return $connector;
    }

    private function createPool(SqlConnector $connector, int $maxConnections = 100, int $idleTimeout = 10): SqlCommonConnectionPool
    {
        $pool = $this->getMockBuilder(SqlCommonConnectionPool::class)
            ->setConstructorArgs([
                $this->createMock(SqlConfig::class),
                $connector,
                $maxConnections,
                $idleTimeout,
            ])
            ->getMockForAbstractClass();

        $pool->method('createResult')
            ->willReturnCallback(fn (SqlResult $result, \Closure $release) => new StubSqlPooledResult($result, $release));

        return $pool;
    }

    public function testIdleConnectionsRemovedAfterTimeout()
    {
        $connector = $this->createConnector();
        $pool = $this->createPool($connector, 10, 2);

        $count = 3;

        $futures = [];
        for ($i = 0; $i < $count; ++$i) {
            $futures[] = async(fn () => $pool->query("SELECT $i"));
        }

        self::assertCount($count, Future\await($futures));

        unset($futures); // Remove references to results so they are destructed.

        self::assertSame($count, $pool->getConnectionCount());

        delay(1);

        self::assertSame($count, $pool->getConnectionCount());

        delay(1);

        self::assertSame(0, $pool->getConnectionCount());
    }

    public function testMaxConnectionCount()
    {
        $connector = $this->createConnector();
        $pool = $this->createPool($connector, $maxConnections = 3);

        $count = 10;

        $futures = [];
        for ($i = 0; $i < $count; ++$i) {
            $futures[] = async(fn () => \iterator_to_array($pool->query("SELECT $i")));
        }

        $expectedRuntime = 0.1 * \ceil($count / $maxConnections);

        $this->setMinimumRuntime($expectedRuntime);
        $this->setTimeout($expectedRuntime + 1);

        Future\await($futures);

        self::assertSame($maxConnections, $pool->getConnectionCount());
    }
}
<?php declare(strict_types=1);

namespace Amp\Sql\Common\Test;

use Amp\PHPUnit\AsyncTestCase;
use Amp\Sql\Common\RetrySqlConnector;
use Amp\Sql\SqlConfig;
use Amp\Sql\SqlConnection;
use Amp\Sql\SqlConnectionException;
use Amp\Sql\SqlConnector;

class RetrySqlConnectorTest extends AsyncTestCase
{
    public function testSuccessfulConnect()
    {
        $connector = $this->createMock(SqlConnector::class);
        $connector->expects($this->once())
            ->method('connect')
            ->willReturn($this->createMock(SqlConnection::class));

        $retry = new RetrySqlConnector($connector);

        $config = $this->getMockBuilder(SqlConfig::class)
            ->setConstructorArgs(['localhost', 5432])
            ->getMockForAbstractClass();

        $connection = $retry->connect($config);

        self::assertInstanceOf(SqlConnection::class, $connection);
    }

    public function testFirstTryFailConnect()
    {
        $connector = $this->createMock(SqlConnector::class);
        $connector->expects($this->exactly(2))
            ->method('connect')
            ->willReturnCallback(function (): SqlConnection {
                static $initial = true;

                if ($initial) {
                    $initial = false;
                    throw new SqlConnectionException;
                }

                return $this->createMock(SqlConnection::class);
            });

        $retry = new RetrySqlConnector($connector);

        $config = $this->getMockBuilder(SqlConfig::class)
            ->setConstructorArgs(['localhost', 5432])
            ->getMockForAbstractClass();

        $connection = $retry->connect($config);

        self::assertInstanceOf(SqlConnection::class, $connection);
    }

    public function testFailingConnect()
    {
        $tries = 3;

        $connector = $this->createMock(SqlConnector::class);
        $connector->expects($this->exactly($tries))
            ->method('connect')
            ->willThrowException(new SqlConnectionException);

        $retry = new RetrySqlConnector($connector, $tries);

        $config = $this->getMockBuilder(SqlConfig::class)
            ->setConstructorArgs(['localhost', 5432])
            ->getMockForAbstractClass();

        $this->expectException(SqlConnectionException::class);
        $this->expectExceptionMessage('Could not connect to database server');

        $connection = $retry->connect($config);
    }
}
<?php declare(strict_types=1);

namespace Amp\Sql\Common\Test\Stub;

use Amp\Sql\SqlResult;

final class StubSqlResult implements SqlResult, \IteratorAggregate
{
    private readonly array $rows;

    private int $current = 0;

    public function __construct(array $rows, private readonly ?SqlResult $next = null)
    {
        $this->rows = \array_values($rows);
    }

    public function getIterator(): \Iterator
    {
        yield from $this->rows;
    }

    public function fetchRow(): ?array
    {
        return $this->rows[$this->current++] ?? null;
    }

    public function getNextResult(): ?SqlResult
    {
        return $this->next;
    }

    public function getRowCount(): ?int
    {
        return \count($this->rows);
    }

    public function getColumnCount(): ?int
    {
        return null;
    }
}
<?php declare(strict_types=1);

namespace Amp\Sql\Common\Test\Stub;

use Amp\Sql\Common\SqlPooledResult;
use Amp\Sql\SqlResult;

final class StubSqlPooledResult extends SqlPooledResult
{
    protected static function newInstanceFrom(SqlResult $result, \Closure $release): self
    {
        return new self($result, $release);
    }
}
<?php declare(strict_types=1);

namespace Amp\Sql\Common\Test;

use Amp\PHPUnit\AsyncTestCase;
use Amp\Sql\Common\SqlStatementPool;
use Amp\Sql\SqlConnectionPool;
use Amp\Sql\SqlStatement;
use function Amp\delay;

class SqlStatementPoolTest extends AsyncTestCase
{
    public function testActiveStatementsRemainAfterTimeout()
    {
        $pool = $this->createMock(SqlConnectionPool::class);
        $pool->method('isClosed')
            ->willReturn(false);
        $pool->method('getIdleTimeout')
            ->willReturn(60);

        $statement = $this->createMock(SqlStatement::class);
        $statement->method('isClosed')
            ->willReturn(false);
        $statement->method('getQuery')
            ->willReturn('SELECT 1');
        $statement->method('getLastUsedAt')
            ->willReturn(\time());
        $statement->expects($this->once())
            ->method('execute');

        $statementPool = $this->getMockBuilder(SqlStatementPool::class)
            ->setConstructorArgs([$pool, 'SELECT 1', $this->createCallback(1, fn () => $statement)])
            ->getMockForAbstractClass();

        self::assertFalse($statementPool->isClosed());
        self::assertSame(\time(), $statementPool->getLastUsedAt());

        delay(1.5); // Give timeout watcher enough time to execute.

        $statementPool->execute();

        self::assertFalse($statementPool->isClosed());
        self::assertSame(\time(), $statementPool->getLastUsedAt());
    }

    public function testIdleStatementsRemovedAfterTimeout()
    {
        $pool = $this->createMock(SqlConnectionPool::class);
        $pool->method('isClosed')
            ->willReturn(false);
        $pool->method('getIdleTimeout')
            ->willReturn(1);

        $createStatement = function (): SqlStatement {
            $statement = $this->createMock(SqlStatement::class);
            $statement->method('isClosed')
                ->willReturn(false);
            $statement->method('getQuery')
                ->willReturn('SELECT 1');
            $statement->method('getLastUsedAt')
                ->willReturn(\time());
            $statement->expects($this->once())
                ->method('execute');

            return $statement;
        };

        $statementPool = $this->getMockBuilder(SqlStatementPool::class)
            ->setConstructorArgs([$pool, 'SELECT 1', $this->createCallback(2, $createStatement)])
            ->getMockForAbstractClass();

        self::assertFalse($statementPool->isClosed());
        self::assertSame(\time(), $statementPool->getLastUsedAt());

        $statementPool->execute();

        delay(1.5); // Give timeout watcher enough time to execute.

        $statementPool->execute();

        self::assertFalse($statementPool->isClosed());
        self::assertSame(\time(), $statementPool->getLastUsedAt());
    }
}
<?php declare(strict_types=1);

namespace Amp\Sql\Common;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Future;
use Amp\Sql\SqlResult;
use Revolt\EventLoop;
use function Amp\async;

/**
 * @template TFieldValue
 * @template TResult of SqlResult
 * @implements SqlResult<TFieldValue>
 * @implements \IteratorAggregate<int, array<string, TFieldValue>>
 */
abstract class SqlPooledResult implements SqlResult, \IteratorAggregate
{
    use ForbidCloning;
    use ForbidSerialization;

    /** @var Future<TResult|null>|null */
    private ?Future $next = null;

    /** @var \Iterator<int, array<string, TFieldValue>> */
    private readonly \Iterator $iterator;

    /**
     * @template Tr of SqlResult
     *
     * @param Tr $result
     * @param \Closure():void $release
     *
     * @return Tr
     */
    abstract protected static function newInstanceFrom(SqlResult $result, \Closure $release): SqlResult;

    /**
     * @param TResult $result Result object created by pooled connection or statement.
     * @param \Closure():void $release Callable to be invoked when the result set is destroyed.
     */
    public function __construct(private readonly SqlResult $result, private readonly \Closure $release)
    {
        if ($this->result instanceof SqlCommandResult) {
            $this->iterator = $this->result->getIterator();
            $this->next = self::fetchNextResult($this->result, $this->release);
            return;
        }

        $next = &$this->next;
        $this->iterator = (static function () use (&$next, $result, $release): \Generator {
            try {
                // Using foreach loop instead of yield from to avoid PHP bug,
                // see https://github.com/amphp/mysql/issues/133
                foreach ($result as $row) {
                    yield $row;
                }
            } catch (\Throwable $exception) {
                if (!$next) {
                    EventLoop::queue($release);
                }
                throw $exception;
            }

            $next ??= self::fetchNextResult($result, $release);
        })();
    }

    public function __destruct()
    {
        EventLoop::queue(self::dispose(...), $this->iterator);
    }

    private static function dispose(\Iterator $iterator): void
    {
        try {
            // Discard remaining rows in the result set.
            while ($iterator->valid()) {
                $iterator->next();
            }
        } catch (\Throwable) {
            // Ignore errors while discarding result.
        }
    }

    public function getIterator(): \Traversable
    {
        // Using a Generator to keep a reference to $this.
        yield from $this->iterator;
    }

    public function fetchRow(): ?array
    {
        if (!$this->iterator->valid()) {
            return null;
        }

        $current = $this->iterator->current();
        $this->iterator->next();
        return $current;
    }

    public function getRowCount(): ?int
    {
        return $this->result->getRowCount();
    }

    public function getColumnCount(): ?int
    {
        return $this->result->getColumnCount();
    }

    /**
     * @return TResult|null
     */
    public function getNextResult(): ?SqlResult
    {
        $this->next ??= self::fetchNextResult($this->result, $this->release);
        return $this->next->await();
    }

    /**
     * @template Tr of SqlResult
     *
     * @param Tr $result
     * @param \Closure():void $release
     *
     * @return Future<Tr|null>
     */
    private static function fetchNextResult(SqlResult $result, \Closure $release): Future
    {
        return async(static function () use ($result, $release): ?SqlResult {
            /** @var Tr|null $result */
            $result = $result->getNextResult();

            if ($result === null) {
                EventLoop::queue($release);
                return null;
            }

            return static::newInstanceFrom($result, $release);
        });
    }
}
<?php declare(strict_types=1);

namespace Amp\Sql\Common;

use Amp\DeferredFuture;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Sql\SqlConfig;
use Amp\Sql\SqlConnectionPool;
use Amp\Sql\SqlException;
use Amp\Sql\SqlResult;
use Amp\Sql\SqlStatement;
use Amp\Sql\SqlTransaction;
use Revolt\EventLoop;

/**
 * @template TConfig of SqlConfig
 * @template TResult of SqlResult
 * @template TStatement of SqlStatement<TResult>
 * @template TTransaction of SqlTransaction
 * @implements SqlStatement<TResult>
 */
abstract class SqlStatementPool implements SqlStatement
{
    use ForbidCloning;
    use ForbidSerialization;

    private readonly SqlConnectionPool $pool;

    /** @var \SplQueue<TStatement> */
    private readonly \SplQueue $statements;

    private readonly string $sql;

    private int $lastUsedAt;

    /** @var \Closure(string):TStatement */
    private readonly \Closure $prepare;

    private readonly DeferredFuture $onClose;

    /**
     * @param TResult $result
     * @param \Closure():void $release
     *
     * @return TResult
     */
    abstract protected function createResult(SqlResult $result, \Closure $release): SqlResult;

    /**
     * @param SqlConnectionPool<TConfig, TResult, TStatement, TTransaction> $pool Pool used to prepare statements for execution.
     * @param string $sql SQL statement to prepare
     * @param \Closure(string):TStatement $prepare Callable that returns a new prepared statement.
     */
    public function __construct(SqlConnectionPool $pool, string $sql, \Closure $prepare)
    {
        $this->lastUsedAt = \time();
        $this->statements = $statements = new \SplQueue;
        $this->pool = $pool;
        $this->prepare = $prepare;
        $this->sql = $sql;
        $this->onClose = $onClose = new DeferredFuture();

        $timeoutWatcher = EventLoop::repeat(1, static function () use ($pool, $statements): void {
            $now = \time();
            $idleTimeout = ((int) ($pool->getIdleTimeout() / 10)) ?: 1;

            while (!$statements->isEmpty()) {
                $statement = $statements->bottom();
                \assert($statement instanceof SqlStatement);

                if ($statement->getLastUsedAt() + $idleTimeout > $now) {
                    return;
                }

                $statements->shift();
            }
        });

        EventLoop::unreference($timeoutWatcher);
        $this->onClose(static fn () => EventLoop::cancel($timeoutWatcher));
    }

    public function __destruct()
    {
        $this->close();
    }

    /**
     * Unlike regular statements, as long as the pool is open this statement will not die.
     *
     * @return TResult
     */
    public function execute(array $params = []): SqlResult
    {
        if ($this->isClosed()) {
            throw new SqlException('The statement has been closed or the connection pool has been closed');
        }

        $this->lastUsedAt = \time();

        $statement = $this->pop();

        try {
            $result = $statement->execute($params);
        } catch (\Throwable $exception) {
            $this->push($statement);
            throw $exception;
        }

        return $this->createResult($result, fn () => $this->push($statement));
    }

    /**
     * Only retains statements if less than 10% of the pool is consumed by this statement and the pool has
     * available connections.
     *
     * @param TStatement $statement
     */
    protected function push(SqlStatement $statement): void
    {
        $maxConnections = $this->pool->getConnectionLimit();

        if ($this->statements->count() > ($maxConnections / 10)) {
            return;
        }

        if ($maxConnections === $this->pool->getConnectionCount() && $this->pool->getIdleConnectionCount() === 0) {
            return;
        }

        $this->statements->enqueue($statement);
    }

    /**
     * @return TStatement
     */
    protected function pop(): SqlStatement
    {
        while (!$this->statements->isEmpty()) {
            $statement = $this->statements->dequeue();
            \assert($statement instanceof SqlStatement);

            if (!$statement->isClosed()) {
                return $statement;
            }
        }

        return ($this->prepare)($this->sql);
    }

    final public function close(): void
    {
        if (!$this->onClose->isComplete()) {
            $this->onClose->complete();
        }
    }

    final public function isClosed(): bool
    {
        return $this->onClose->isComplete();
    }

    final public function onClose(\Closure $onClose): void
    {
        $this->onClose->getFuture()->finally($onClose);
    }

    final public function getQuery(): string
    {
        return $this->sql;
    }

    final public function getLastUsedAt(): int
    {
        return $this->lastUsedAt;
    }
}
<?php declare(strict_types=1);

namespace Amp\Sql\Common;

use Amp\DeferredFuture;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Future;
use Amp\Sql\SqlConfig;
use Amp\Sql\SqlConnection;
use Amp\Sql\SqlConnectionPool;
use Amp\Sql\SqlConnector;
use Amp\Sql\SqlException;
use Amp\Sql\SqlLink;
use Amp\Sql\SqlResult;
use Amp\Sql\SqlStatement;
use Amp\Sql\SqlTransaction;
use Amp\Sql\SqlTransactionIsolation;
use Amp\Sql\SqlTransactionIsolationLevel;
use Revolt\EventLoop;
use function Amp\async;

/**
 * @template TConfig of SqlConfig
 * @template TResult of SqlResult
 * @template TStatement of SqlStatement<TResult>
 * @template TTransaction of SqlTransaction
 * @template TConnection of SqlConnection<TConfig, TResult, TStatement, TTransaction>
 *
 * @implements SqlConnectionPool<TConfig, TResult, TStatement, TTransaction>
 */
abstract class SqlCommonConnectionPool implements SqlConnectionPool
{
    use ForbidCloning;
    use ForbidSerialization;

    public const DEFAULT_MAX_CONNECTIONS = 100;
    public const DEFAULT_IDLE_TIMEOUT = 60;

    /** @var \SplQueue<TConnection> */
    private readonly \SplQueue $idle;

    /** @var \SplObjectStorage<TConnection, null> */
    private readonly \SplObjectStorage $connections;

    /** @var Future<TConnection>|null */
    private ?Future $future = null;

    /** @var DeferredFuture<TConnection>|null */
    private ?DeferredFuture $awaitingConnection = null;

    private readonly DeferredFuture $onClose;

    /** @var \WeakMap<TStatement, true> */
    private \WeakMap $statements;

    /**
     * Creates a Statement of the appropriate type using the Statement object returned by the Link object and the
     * given release callable.
     *
     * @param TStatement $statement
     * @param \Closure():void $release
     *
     * @return TStatement
     */
    abstract protected function createStatement(SqlStatement $statement, \Closure $release): SqlStatement;

    /**
     * Creates a Result of the appropriate type using the Result object returned by the Link object and the
     * given release callable.
     *
     * @param TResult $result
     * @param \Closure():void $release
     * @return TResult
     */
    abstract protected function createResult(SqlResult $result, \Closure $release): SqlResult;

    /**
     * @param \Closure(string):TStatement $prepare
     *
     * @return TStatement
     */
    abstract protected function createStatementPool(string $sql, \Closure $prepare): SqlStatement;

    /**
     * Creates a Transaction of the appropriate type using the Transaction object returned by the Link object and the
     * given release callable.
     *
     * @param TTransaction $transaction
     * @param \Closure():void $release
     *
     * @return TTransaction
     */
    abstract protected function createTransaction(SqlTransaction $transaction, \Closure $release): SqlTransaction;

    /**
     * @param TConfig $config
     * @param SqlConnector<TConfig, TConnection> $connector
     * @param positive-int $maxConnections Maximum number of active connections in the pool.
     * @param positive-int $idleTimeout Number of seconds until idle connections are removed from the pool.
     */
    public function __construct(
        private readonly SqlConfig $config,
        private readonly SqlConnector $connector,
        private readonly int $maxConnections = self::DEFAULT_MAX_CONNECTIONS,
        private int $idleTimeout = self::DEFAULT_IDLE_TIMEOUT,
        private SqlTransactionIsolation $transactionIsolation = SqlTransactionIsolationLevel::Committed,
    ) {
        /** @psalm-suppress TypeDoesNotContainType */
        if ($this->idleTimeout < 1) {
            throw new \Error("The idle timeout must be 1 or greater");
        }

        /** @psalm-suppress TypeDoesNotContainType */
        if ($this->maxConnections < 1) {
            throw new \Error("Pool must contain at least one connection");
        }

        $this->connections = $connections = new \SplObjectStorage();

        /** @var \WeakMap<TStatement, true> For Psalm. */
        $this->statements = new \WeakMap();

        $this->idle = $idle = new \SplQueue();
        $this->onClose = new DeferredFuture();

        $idleTimeout = &$this->idleTimeout;

        $timeoutWatcher = EventLoop::repeat(1, static function () use (&$idleTimeout, $connections, $idle) {
            $now = \time();
            while (!$idle->isEmpty()) {
                $connection = $idle->bottom();
                \assert($connection instanceof SqlLink);

                if ($connection->getLastUsedAt() + $idleTimeout > $now) {
                    return;
                }

                // Close connection and remove it from the pool.
                $idle->shift();
                /** @psalm-suppress InvalidArgument SplObjectStorage::offsetUnset() expects an argument. */
                $connections->offsetUnset($connection);
                $connection->close();
            }
        });

        EventLoop::unreference($timeoutWatcher);
        $this->onClose(static fn () => EventLoop::cancel($timeoutWatcher));
    }

    public function __destruct()
    {
        $this->close();
    }

    public function getTransactionIsolation(): SqlTransactionIsolation
    {
        return $this->transactionIsolation;
    }

    public function setTransactionIsolation(SqlTransactionIsolation $isolation): void
    {
        $this->transactionIsolation = $isolation;
    }

    public function getConfig(): SqlConfig
    {
        return $this->config;
    }

    public function getIdleTimeout(): int
    {
        return $this->idleTimeout;
    }

    public function getLastUsedAt(): int
    {
        // Simple implementation... can be improved if needed.

        $time = 0;

        foreach ($this->connections as $connection) {
            \assert($connection instanceof SqlLink);
            if (($lastUsedAt = $connection->getLastUsedAt()) > $time) {
                $time = $lastUsedAt;
            }
        }

        return $time;
    }

    public function isClosed(): bool
    {
        return $this->onClose->isComplete();
    }

    public function onClose(\Closure $onClose): void
    {
        $this->onClose->getFuture()->finally($onClose);
    }

    /**
     * Close all connections in the pool. No further queries may be made after a pool is closed.
     */
    public function close(): void
    {
        if ($this->onClose->isComplete()) {
            return;
        }

        foreach ($this->connections as $connection) {
            // Avoid first class callable syntax to avoid psalm crash
            /** @psalm-suppress MissingClosureReturnType */
            async(fn () => $connection->close())->ignore();
        }

        foreach ($this->statements as $statement => $_) {
            $statement->close();
        }

        $this->onClose->complete();

        $this->awaitingConnection?->error(new SqlException("Connection pool closed"));
        $this->awaitingConnection = null;
    }

    /**
     * @return TConnection
     *
     * @throws SqlException
     */
    public function extractConnection(): SqlConnection
    {
        $connection = $this->pop();
        $this->connections->offsetUnset($connection);
        return $connection;
    }

    public function getConnectionCount(): int
    {
        return $this->connections->count();
    }

    public function getIdleConnectionCount(): int
    {
        return $this->idle->count();
    }

    public function getConnectionLimit(): int
    {
        return $this->maxConnections;
    }

    /**
     * @return TConnection
     *
     * @throws SqlException If creating a new connection fails.
     * @throws \Error If the pool has been closed.
     */
    protected function pop(): SqlConnection
    {
        if ($this->isClosed()) {
            throw new \Error("The pool has been closed");
        }

        while ($this->future !== null) {
            $this->future->await(); // Prevent simultaneous connection creation or waiting.
        }

        do {
            // While loop to ensure an idle connection is available after futures below are resolved.
            while ($this->idle->isEmpty()) {
                if ($this->connections->count() < $this->getConnectionLimit()) {
                    // Max connection count has not been reached, so open another connection.
                    try {
                        $this->future = async(fn () => $this->connector->connect($this->config));
                        $connection = $this->future->await();
                        /** @psalm-suppress DocblockTypeContradiction */
                        if (!$connection instanceof SqlLink) {
                            throw new \Error(\sprintf(
                                "%s::connect() must resolve to an instance of %s",
                                \get_class($this->connector),
                                SqlLink::class
                            ));
                        }
                    } finally {
                        $this->future = null;
                    }

                    if ($this->isClosed()) {
                        $connection->close();
                        break 2; // Break to throwing exception.
                    }

                    $this->connections->offsetSet($connection);
                    return $connection;
                }

                // All possible connections busy, so wait until one becomes available.
                try {
                    $this->awaitingConnection = new DeferredFuture;

                    $this->future = $this->awaitingConnection->getFuture();
                    // Connection will be pulled from $this->idle when future is resolved.
                    $this->future->await();
                } finally {
                    $this->future = null;
                }
            }

            $connection = $this->idle->dequeue();
            \assert($connection instanceof SqlLink);

            if (!$connection->isClosed()) {
                return $connection;
            }

            $this->connections->offsetUnset($connection);
        } while (!$this->isClosed());

        throw new SqlException("Pool closed before an active connection could be obtained");
    }

    /**
     * @param TConnection $connection
     *
     * @throws \Error If the connection is not part of this pool.
     */
    protected function push(SqlConnection $connection): void
    {
        \assert(isset($this->connections[$connection]), 'Connection is not part of this pool');

        $this->idle->enqueue($connection);

        $this->awaitingConnection?->complete($connection);
        $this->awaitingConnection = null;
    }

    public function query(string $sql): SqlResult
    {
        $connection = $this->pop();

        try {
            $result = $connection->query($sql);
        } catch (\Throwable $exception) {
            $this->push($connection);
            throw $exception;
        }

        return $this->createResult($result, fn () => $this->push($connection));
    }

    public function execute(string $sql, array $params = []): SqlResult
    {
        $connection = $this->pop();

        try {
            $result = $connection->execute($sql, $params);
        } catch (\Throwable $exception) {
            $this->push($connection);
            throw $exception;
        }

        return $this->createResult($result, fn () => $this->push($connection));
    }

    /**
     * Prepared statements returned by this method will stay alive as long as the pool remains open.
     */
    public function prepare(string $sql): SqlStatement
    {
        /** @psalm-suppress InvalidArgument Psalm is not properly detecting the templated return type. */
        $statement = $this->createStatementPool($sql, $this->prepareStatement(...));

        $this->statements[$statement] = true;

        /** @var TStatement $statement Psalm is not properly detecting the templated type. */
        return $statement;
    }

    /**
     * Prepares a new statement on an available connection.
     *
     * @return TStatement
     *
     * @throws SqlException
     */
    private function prepareStatement(string $sql): SqlStatement
    {
        $connection = $this->pop();

        try {
            $statement = $connection->prepare($sql);
        } catch (\Throwable $exception) {
            $this->push($connection);
            throw $exception;
        }

        return $this->createStatement($statement, fn () => $this->push($connection));
    }

    public function beginTransaction(): SqlTransaction
    {
        $connection = $this->pop();

        try {
            $connection->setTransactionIsolation($this->transactionIsolation);
            $transaction = $connection->beginTransaction();
        } catch (\Throwable $exception) {
            $this->push($connection);
            throw $exception;
        }

        return $this->createTransaction($transaction, fn () => $this->push($connection));
    }
}
<?php declare(strict_types=1);

namespace Amp\Sql\Common;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Sql\SqlException;
use Amp\Sql\SqlResult;
use Amp\Sql\SqlStatement;
use Revolt\EventLoop;

/**
 * @template TResult of SqlResult
 * @template TStatement of SqlStatement<TResult>
 *
 * @implements SqlStatement<TResult>
 */
abstract class SqlPooledStatement implements SqlStatement
{
    use ForbidCloning;
    use ForbidSerialization;

    /** @var null|\Closure():void */
    private ?\Closure $release;

    private int $refCount = 1;

    /**
     * Creates a Result of the appropriate type using the Result object returned by the Statement object and the
     * given release callable.
     *
     * @param TResult $result
     * @param \Closure():void $release
     *
     * @return TResult
     */
    abstract protected function createResult(SqlResult $result, \Closure $release): SqlResult;

    /**
     * @param TStatement $statement Statement object created by pooled connection.
     * @param \Closure():void $release Callable to be invoked when the statement and any associated results are
     *     destroyed.
     * @param (\Closure():void)|null $awaitBusyResource Callable invoked before executing the statement, which should
     *     wait if the parent resource is busy with another action (e.g., a nested transaction).
     */
    public function __construct(
        private readonly SqlStatement $statement,
        \Closure $release,
        private readonly ?\Closure $awaitBusyResource = null,
    ) {
        $refCount = &$this->refCount;
        $this->release = static function () use (&$refCount, $release): void {
            if (--$refCount === 0) {
                $release();
            }
        };
    }

    public function __destruct()
    {
        $this->dispose();
    }

    /**
     * @return TResult
     */
    public function execute(array $params = []): SqlResult
    {
        if (!$this->release) {
            throw new SqlException('The statement has been closed');
        }

        $this->awaitBusyResource && ($this->awaitBusyResource)();

        $result = $this->statement->execute($params);

        ++$this->refCount;
        return $this->createResult($result, $this->release);
    }

    private function dispose(): void
    {
        if ($this->release) {
            EventLoop::queue($this->release);
            $this->release = null;
        }
    }

    public function isClosed(): bool
    {
        return $this->statement->isClosed();
    }

    public function close(): void
    {
        $this->dispose();
        $this->statement->close();
    }

    public function onClose(\Closure $onClose): void
    {
        $this->statement->onClose($onClose);
    }

    public function getQuery(): string
    {
        return $this->statement->getQuery();
    }

    public function getLastUsedAt(): int
    {
        return $this->statement->getLastUsedAt();
    }
}
<?php declare(strict_types=1);

namespace Amp\Sql\Common;

use Amp\DeferredFuture;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Sql\SqlException;
use Amp\Sql\SqlResult;
use Amp\Sql\SqlStatement;
use Amp\Sql\SqlTransaction;
use Amp\Sql\SqlTransactionError;
use Amp\Sql\SqlTransactionIsolation;
use Revolt\EventLoop;

/**
 * @template TResult of SqlResult
 * @template TStatement of SqlStatement<TResult>
 * @template TTransaction of SqlTransaction
 * @template TNestedExecutor of SqlNestableTransactionExecutor<TResult, TStatement>
 *
 * @implements SqlTransaction<TResult, TStatement, TTransaction>
 */
abstract class SqlNestedTransaction implements SqlTransaction
{
    use ForbidCloning;
    use ForbidSerialization;

    /** @var \Closure():void */
    private readonly \Closure $release;

    private int $refCount = 1;

    private bool $active = true;

    private readonly DeferredFuture $onCommit;
    private readonly DeferredFuture $onRollback;
    private readonly DeferredFuture $onClose;

    private ?DeferredFuture $busy = null;

    private int $nextId = 1;

    /**
     * Creates a Result of the appropriate type using the Result object returned by the Link object and the
     * given release callable.
     *
     * @param TResult $result
     * @param \Closure():void $release
     *
     * @return TResult
     */
    abstract protected function createResult(SqlResult $result, \Closure $release): SqlResult;

    /**
     * @param TTransaction $transaction
     * @param TNestedExecutor $executor
     * @param non-empty-string $identifier
     * @param \Closure():void $release
     *
     * @return TTransaction
     */
    abstract protected function createNestedTransaction(
        SqlTransaction $transaction,
        SqlNestableTransactionExecutor $executor,
        string $identifier,
        \Closure $release,
    ): SqlTransaction;

    /**
     * @param TTransaction $transaction Transaction object created by connection.
     * @param TNestedExecutor $executor
     * @param non-empty-string $identifier
     * @param \Closure():void $release Callable to be invoked when the transaction completes or is destroyed.
     */
    public function __construct(
        private readonly SqlTransaction $transaction,
        private readonly SqlNestableTransactionExecutor $executor,
        private readonly string $identifier,
        \Closure $release,
    ) {
        $this->onCommit = new DeferredFuture();
        $this->onRollback = new DeferredFuture();
        $this->onClose = new DeferredFuture();

        $busy = &$this->busy;
        $refCount = &$this->refCount;
        $this->release = static function () use (&$busy, &$refCount, $release): void {
            $busy?->complete();
            $busy = null;

            if (--$refCount === 0) {
                $release();
            }
        };

        $this->onClose($this->release);

        if (!$this->transaction->isActive()) {
            $this->active = false;
            $this->onClose->complete();
        }
    }

    public function __destruct()
    {
        if ($this->onClose->isComplete()) {
            return;
        }

        $this->onClose->complete();

        if ($this->executor->isClosed()) {
            return;
        }

        $busy = &$this->busy;
        $transaction = $this->transaction;
        $executor = $this->executor;
        $identifier = $this->identifier;
        $onRollback = $this->onRollback;
        $onClose = $this->onClose;
        EventLoop::queue(static function () use (
            &$busy,
            $transaction,
            $executor,
            $identifier,
            $onRollback,
            $onClose,
        ): void {
            try {
                while ($busy) {
                    $busy->getFuture()->await();
                }

                if ($transaction->isActive() && !$executor->isClosed()) {
                    $executor->rollbackTo($identifier);
                }
            } catch (SqlException) {
                // Ignore failure if connection closes during query.
            } finally {
                $transaction->onRollback(static fn () => $onRollback->isComplete() || $onRollback->complete());
                $onClose->complete();
            }
        });
    }

    public function query(string $sql): SqlResult
    {
        $this->awaitPendingNestedTransaction();
        ++$this->refCount;

        try {
            $result = $this->executor->query($sql);
            return $this->createResult($result, $this->release);
        } catch (\Throwable $exception) {
            EventLoop::queue($this->release);
            throw $exception;
        }
    }

    public function prepare(string $sql): SqlStatement
    {
        $this->awaitPendingNestedTransaction();

        return $this->executor->prepare($sql);
    }

    public function execute(string $sql, array $params = []): SqlResult
    {
        $this->awaitPendingNestedTransaction();
        ++$this->refCount;

        try {
            $result = $this->executor->execute($sql, $params);
            return $this->createResult($result, $this->release);
        } catch (\Throwable $exception) {
            EventLoop::queue($this->release);
            throw $exception;
        }
    }

    public function beginTransaction(): SqlTransaction
    {
        $this->awaitPendingNestedTransaction();
        ++$this->refCount;
        $this->busy = new DeferredFuture();

        $identifier = $this->identifier . '-' . $this->nextId++;

        try {
            $this->executor->createSavepoint($identifier);
            return $this->createNestedTransaction($this->transaction, $this->executor, $identifier, $this->release);
        } catch (\Throwable $exception) {
            EventLoop::queue($this->release);
            throw $exception;
        }
    }

    public function isClosed(): bool
    {
        return $this->onClose->isComplete();
    }

    /**
     * Rolls back the transaction if it has not been committed.
     */
    public function close(): void
    {
        if (!$this->active) {
            return;
        }

        $this->rollback();
    }

    public function onClose(\Closure $onClose): void
    {
        $this->onClose->getFuture()->finally($onClose);
    }

    public function isActive(): bool
    {
        return $this->active && $this->transaction->isActive();
    }

    public function commit(): void
    {
        $this->active = false;
        $this->awaitPendingNestedTransaction();

        try {
            $this->executor->releaseSavepoint($this->identifier);
        } finally {
            $onCommit = $this->onCommit;
            $this->transaction->onCommit(static fn () => $onCommit->isComplete() || $onCommit->complete());

            $onRollback = $this->onRollback;
            $this->transaction->onRollback(static fn () => $onRollback->isComplete() || $onRollback->complete());

            $this->onClose->complete();
        }
    }

    public function rollback(): void
    {
        $this->active = false;
        $this->awaitPendingNestedTransaction();

        try {
            $this->executor->rollbackTo($this->identifier);
        } finally {
            $this->onRollback->complete();
            $this->onClose->complete();
        }
    }

    public function onCommit(\Closure $onCommit): void
    {
        $this->onCommit->getFuture()->finally($onCommit);
    }

    public function onRollback(\Closure $onRollback): void
    {
        $this->onRollback->getFuture()->finally($onRollback);
    }

    public function getSavepointIdentifier(): string
    {
        return $this->identifier;
    }

    public function getIsolation(): SqlTransactionIsolation
    {
        return $this->transaction->getIsolation();
    }

    public function getLastUsedAt(): int
    {
        return $this->transaction->getLastUsedAt();
    }

    private function awaitPendingNestedTransaction(): void
    {
        while ($this->busy) {
            $this->busy->getFuture()->await();
        }

        if ($this->isClosed()) {
            throw new SqlTransactionError('The transaction has already been committed or rolled back');
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\Sql\Common;

use Amp\Sql\SqlExecutor;
use Amp\Sql\SqlResult;
use Amp\Sql\SqlStatement;

/**
 * @template TResult of SqlResult
 * @template TStatement of SqlStatement<TResult>
 *
 * @extends SqlExecutor<TResult, TStatement>
 */
interface SqlNestableTransactionExecutor extends SqlExecutor
{
    /**
     * Commits the current transaction.
     */
    public function commit(): void;

    /**
     * Rolls back the current transaction.
     */
    public function rollback(): void;

    /**
     * Creates a savepoint with the given identifier.
     *
     * @param non-empty-string $identifier Savepoint identifier.
     */
    public function createSavepoint(string $identifier): void;

    /**
     * Rolls back to the savepoint with the given identifier.
     *
     * @param non-empty-string $identifier Savepoint identifier.
     */
    public function rollbackTo(string $identifier): void;

    /**
     * Releases the savepoint with the given identifier.
     *
     * @param non-empty-string $identifier Savepoint identifier.
     */
    public function releaseSavepoint(string $identifier): void;
}
<?php declare(strict_types=1);

namespace Amp\Sql\Common;

use Amp\Cancellation;
use Amp\CompositeException;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Sql\SqlConfig;
use Amp\Sql\SqlConnection;
use Amp\Sql\SqlConnectionException;
use Amp\Sql\SqlConnector;

/**
 * @template TConfig of SqlConfig
 * @template TConnection of SqlConnection
 * @implements SqlConnector<TConfig, TConnection>
 */
final class RetrySqlConnector implements SqlConnector
{
    use ForbidCloning;
    use ForbidSerialization;

    /**
     * @param SqlConnector<TConfig, TConnection> $connector
     */
    public function __construct(
        private readonly SqlConnector $connector,
        private readonly int $maxTries = 3,
    ) {
        if ($maxTries <= 0) {
            throw new \Error('The number of tries must be 1 or greater');
        }
    }

    public function connect(SqlConfig $config, ?Cancellation $cancellation = null): SqlConnection
    {
        $tries = 0;
        $exceptions = [];

        do {
            try {
                return $this->connector->connect($config, $cancellation);
            } catch (SqlConnectionException $exception) {
                $exceptions[] = $exception;
            }
        } while (++$tries < $this->maxTries);

        $name = $config->getHost() . ':' . $config->getPort();

        throw new SqlConnectionException(
            "Could not connect to database server at {$name} after {$tries} tries",
            0,
            new CompositeException($exceptions)
        );
    }
}
<?php declare(strict_types=1);

namespace Amp\Sql\Common;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Sql\SqlResult;
use Amp\Sql\SqlStatement;
use Amp\Sql\SqlTransaction;
use Amp\Sql\SqlTransactionIsolation;
use Revolt\EventLoop;

/**
 * @template TResult of SqlResult
 * @template TStatement of SqlStatement
 * @template TTransaction of SqlTransaction
 *
 * @implements SqlTransaction<TResult, TStatement, TTransaction>
 */
abstract class SqlPooledTransaction implements SqlTransaction
{
    use ForbidCloning;
    use ForbidSerialization;

    /** @var \Closure():void */
    private readonly \Closure $release;

    private int $refCount = 1;

    /**
     * Creates a Statement of the appropriate type using the Statement object returned by the Transaction object and
     * the given release callable.
     *
     * @param TStatement $statement
     * @param \Closure():void $release
     *
     * @return TStatement
     */
    abstract protected function createStatement(SqlStatement $statement, \Closure $release): SqlStatement;

    /**
     * Creates a Result of the appropriate type using the Result object returned by the Link object and the
     * given release callable.
     *
     * @param TResult $result
     * @param \Closure():void $release
     *
     * @return TResult
     */
    abstract protected function createResult(SqlResult $result, \Closure $release): SqlResult;

    /**
     * @param TTransaction $transaction
     * @param \Closure():void $release
     *
     * @return TTransaction
     */
    abstract protected function createTransaction(SqlTransaction $transaction, \Closure $release): SqlTransaction;

    /**
     * @param TTransaction $transaction Transaction object created by pooled connection.
     * @param \Closure():void $release Callable to be invoked when the transaction completes or is destroyed.
     */
    public function __construct(private readonly SqlTransaction $transaction, \Closure $release)
    {
        $refCount = &$this->refCount;
        $this->release = static function () use (&$refCount, $release): void {
            if (--$refCount === 0) {
                $release();
            }
        };

        $this->transaction->onClose($this->release);

        if (!$this->transaction->isActive()) {
            $this->close();
        }
    }

    public function query(string $sql): SqlResult
    {
        ++$this->refCount;

        try {
            $result = $this->transaction->query($sql);
            return $this->createResult($result, $this->release);
        } catch (\Throwable $exception) {
            EventLoop::queue($this->release);
            throw $exception;
        }
    }

    public function prepare(string $sql): SqlStatement
    {
        ++$this->refCount;

        try {
            $statement = $this->transaction->prepare($sql);
            return $this->createStatement($statement, $this->release);
        } catch (\Throwable $exception) {
            EventLoop::queue($this->release);
            throw $exception;
        }
    }

    public function execute(string $sql, array $params = []): SqlResult
    {
        ++$this->refCount;

        try {
            $result = $this->transaction->execute($sql, $params);
            return $this->createResult($result, $this->release);
        } catch (\Throwable $exception) {
            EventLoop::queue($this->release);
            throw $exception;
        }
    }

    public function beginTransaction(): SqlTransaction
    {
        ++$this->refCount;

        try {
            $transaction = $this->transaction->beginTransaction();
            return $this->createTransaction($transaction, $this->release);
        } catch (\Throwable $exception) {
            EventLoop::queue($this->release);
            throw $exception;
        }
    }

    public function isClosed(): bool
    {
        return $this->transaction->isClosed();
    }

    /**
     * Rolls back the transaction if it has not been committed.
     */
    public function close(): void
    {
        $this->transaction->close();
    }

    public function onClose(\Closure $onClose): void
    {
        $this->transaction->onClose($onClose);
    }

    public function isActive(): bool
    {
        return $this->transaction->isActive();
    }

    public function commit(): void
    {
        $this->transaction->commit();
    }

    public function rollback(): void
    {
        $this->transaction->rollback();
    }

    public function onCommit(\Closure $onCommit): void
    {
        $this->transaction->onCommit($onCommit);
    }

    public function onRollback(\Closure $onRollback): void
    {
        $this->transaction->onRollback($onRollback);
    }

    public function getSavepointIdentifier(): ?string
    {
        return $this->transaction->getSavepointIdentifier();
    }

    public function getIsolation(): SqlTransactionIsolation
    {
        return $this->transaction->getIsolation();
    }

    public function getLastUsedAt(): int
    {
        return $this->transaction->getLastUsedAt();
    }
}
<?php declare(strict_types=1);

namespace Amp\Sql\Common;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Future;
use Amp\Sql\SqlResult;

/**
 * @template TFieldValue
 * @template TResult of SqlResult
 * @implements SqlResult<TFieldValue>
 * @implements \IteratorAggregate<int, never>
 */
abstract class SqlCommandResult implements SqlResult, \IteratorAggregate
{
    use ForbidCloning;
    use ForbidSerialization;

    /**
     * @param Future<TResult|null> $nextResult
     */
    public function __construct(
        private readonly int $affectedRows,
        private readonly Future $nextResult
    ) {
    }

    final public function getIterator(): \EmptyIterator
    {
        return new \EmptyIterator;
    }

    /**
     * @return null Always returns null for command results.
     */
    final public function fetchRow(): ?array
    {
        return null;
    }

    /**
     * @return TResult|null
     */
    public function getNextResult(): ?SqlResult
    {
        return $this->nextResult->await();
    }

    /**
     * @return int Returns the number of rows affected by the command.
     */
    final public function getRowCount(): int
    {
        return $this->affectedRows;
    }

    /**
     * @return null Always returns null for command results.
     */
    final public function getColumnCount(): ?int
    {
        return null;
    }
}
<?php declare(strict_types=1);

namespace Amp\Sql\Common;

use Amp\DeferredFuture;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Sql\SqlException;
use Amp\Sql\SqlResult;
use Amp\Sql\SqlStatement;
use Amp\Sql\SqlTransaction;
use Amp\Sql\SqlTransactionError;
use Amp\Sql\SqlTransactionIsolation;
use Revolt\EventLoop;

/**
 * @template TResult of SqlResult
 * @template TStatement of SqlStatement<TResult>
 * @template TTransaction of SqlTransaction
 * @template TNestedExecutor of SqlNestableTransactionExecutor<TResult, TStatement>
 *
 * @implements SqlTransaction<TResult, TStatement, TTransaction>
 */
abstract class SqlConnectionTransaction implements SqlTransaction
{
    use ForbidCloning;
    use ForbidSerialization;

    /** @var \Closure():void */
    private readonly \Closure $release;

    private int $refCount = 1;

    private bool $active = true;

    private readonly DeferredFuture $onCommit;
    private readonly DeferredFuture $onRollback;
    private readonly DeferredFuture $onClose;

    private ?DeferredFuture $busy = null;

    /**
     * Creates a Result of the appropriate type using the Result object returned by the Link object and the
     * given release callable.
     *
     * @param TResult $result
     * @param \Closure():void $release
     *
     * @return TResult
     */
    abstract protected function createResult(SqlResult $result, \Closure $release): SqlResult;

    /**
     * Creates a Statement of the appropriate type using the Statement object returned by the Transaction object and
     * the given release callable.
     *
     * @param TStatement $statement
     * @param \Closure():void $release
     * @param \Closure():void $awaitBusyResource
     *
     * @return TStatement
     */
    abstract protected function createStatement(
        SqlStatement $statement,
        \Closure $release,
        \Closure $awaitBusyResource,
    ): SqlStatement;

    /**
     * @param TTransaction $transaction
     * @param TNestedExecutor $executor
     * @param non-empty-string $identifier
     * @param \Closure():void $release
     *
     * @return TTransaction
     */
    abstract protected function createNestedTransaction(
        SqlTransaction $transaction,
        SqlNestableTransactionExecutor $executor,
        string $identifier,
        \Closure $release,
    ): SqlTransaction;

    /**
     * @param TNestedExecutor $executor
     * @param \Closure():void $release
     */
    public function __construct(
        private readonly SqlNestableTransactionExecutor $executor,
        \Closure $release,
        private readonly SqlTransactionIsolation $isolation,
    ) {
        $busy = &$this->busy;
        $refCount = &$this->refCount;
        $this->release = static function () use (&$busy, &$refCount, $release): void {
            $busy?->complete();
            $busy = null;

            if (--$refCount === 0) {
                $release();
            }
        };

        $this->onCommit = new DeferredFuture();
        $this->onRollback = new DeferredFuture();
        $this->onClose = new DeferredFuture();

        $this->onClose($this->release);
    }

    public function __destruct()
    {
        if (!$this->active) {
            return;
        }

        if ($this->executor->isClosed()) {
            $this->onRollback->complete();
            $this->onClose->complete();
        }

        $busy = &$this->busy;
        $executor = $this->executor;
        $onRollback = $this->onRollback;
        $onClose = $this->onClose;
        EventLoop::queue(static function () use (&$busy, $executor, $onRollback, $onClose): void {
            try {
                while ($busy) {
                    $busy->getFuture()->await();
                }

                if (!$executor->isClosed()) {
                    $executor->rollback();
                }
            } catch (SqlException) {
                // Ignore failure if connection closes during query.
            } finally {
                $onRollback->complete();
                $onClose->complete();
            }
        });
    }

    public function getLastUsedAt(): int
    {
        return $this->executor->getLastUsedAt();
    }

    public function getSavepointIdentifier(): ?string
    {
        return null;
    }

    /**
     * Closes and rolls back all changes in the transaction.
     */
    public function close(): void
    {
        if (!$this->active) {
            return;
        }

        $this->rollback(); // Invokes $this->release callback.
    }

    public function isClosed(): bool
    {
        return $this->onClose->isComplete();
    }

    public function onClose(\Closure $onClose): void
    {
        $this->onClose->getFuture()->finally($onClose);
    }

    /**
     * @return bool True if the transaction is active, false if it has been committed or rolled back.
     */
    public function isActive(): bool
    {
        return $this->active && !$this->executor->isClosed();
    }

    public function getIsolation(): SqlTransactionIsolation
    {
        return $this->isolation;
    }

    /**
     * @throws SqlTransactionError If the transaction has been committed or rolled back.
     */
    public function query(string $sql): SqlResult
    {
        $this->awaitPendingNestedTransaction();

        ++$this->refCount;
        try {
            $result = $this->executor->query($sql);
        } catch (\Throwable $exception) {
            EventLoop::queue($this->release);
            throw $exception;
        }

        return $this->createResult($result, $this->release);
    }

    /**
     * @throws SqlTransactionError If the transaction has been committed or rolled back.
     *
     * @psalm-suppress InvalidReturnStatement, InvalidReturnType
     */
    public function prepare(string $sql): SqlStatement
    {
        $this->awaitPendingNestedTransaction();

        ++$this->refCount;
        try {
            $statement = $this->executor->prepare($sql);
        } catch (\Throwable $exception) {
            EventLoop::queue($this->release);
            throw $exception;
        }

        $busy = &$this->busy;
        return $this->createStatement($statement, $this->release, static function () use (&$busy): void {
            while ($busy) {
                $busy->getFuture()->await();
            }
        });
    }

    /**
     * @throws SqlTransactionError If the transaction has been committed or rolled back.
     */
    public function execute(string $sql, array $params = []): SqlResult
    {
        $this->awaitPendingNestedTransaction();

        ++$this->refCount;
        try {
            $result = $this->executor->execute($sql, $params);
        } catch (\Throwable $exception) {
            EventLoop::queue($this->release);
            throw $exception;
        }

        return $this->createResult($result, $this->release);
    }

    public function beginTransaction(): SqlTransaction
    {
        $this->awaitPendingNestedTransaction();

        ++$this->refCount;
        $this->busy = new DeferredFuture();
        try {
            $identifier = \bin2hex(\random_bytes(8));
            $this->executor->createSavepoint($identifier);
        } catch (\Throwable $exception) {
            EventLoop::queue($this->release);
            throw $exception;
        }

        /** @psalm-suppress InvalidArgument Recursive templates prevent satisfying this call. */
        return $this->createNestedTransaction($this, $this->executor, $identifier, $this->release);
    }

    /**
     * Commits the transaction and makes it inactive.
     *
     * @throws SqlTransactionError If the transaction has been committed or rolled back.
     */
    public function commit(): void
    {
        $this->active = false;
        $this->awaitPendingNestedTransaction();

        try {
            $this->executor->commit();
        } finally {
            $this->onCommit->complete();
            $this->onClose->complete();
        }
    }

    /**
     * Rolls back the transaction and makes it inactive.
     *
     * @throws SqlTransactionError If the transaction has been committed or rolled back.
     */
    public function rollback(): void
    {
        $this->active = false;
        $this->awaitPendingNestedTransaction();

        try {
            $this->executor->rollback();
        } finally {
            $this->onRollback->complete();
            $this->onClose->complete();
        }
    }

    public function onCommit(\Closure $onCommit): void
    {
        $this->onCommit->getFuture()->finally($onCommit);
    }

    public function onRollback(\Closure $onRollback): void
    {
        $this->onRollback->getFuture()->finally($onRollback);
    }

    private function awaitPendingNestedTransaction(): void
    {
        while ($this->busy) {
            $this->busy->getFuture()->await();
        }

        if ($this->isClosed()) {
            throw new SqlTransactionError("The transaction has been committed or rolled back");
        }
    }
}
{
    "name": "amphp/sql-common",
    "description": "Common classes for non-blocking SQL implementations.",
    "keywords": [
        "database",
        "db",
        "sql",
        "asynchronous",
        "async"
    ],
    "homepage": "https://amphp.org",
    "license": "MIT",
    "require": {
        "php": ">=8.1",
        "amphp/amp": "^3",
        "amphp/sql": "^2"
    },
    "require-dev": {
        "amphp/php-cs-fixer-config": "^2",
        "amphp/phpunit-util": "^3",
        "phpunit/phpunit": "^9",
        "psalm/phar": "5.23"
    },
    "autoload": {
        "psr-4": {
            "Amp\\Sql\\Common\\": "src"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Amp\\Sql\\Common\\Test\\": "test"
        }
    },
    "scripts": {
        "check": [
            "@cs",
            "@test"
        ],
        "cs": "php-cs-fixer fix -v --diff --dry-run",
        "cs-fix": "php-cs-fixer fix -v --diff",
        "test": "phpunit --coverage-text"
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\EventListener;

use Amp\File\File;
use Amp\File\Filesystem;
use Amp\File\Whence;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Http\Client\ApplicationInterceptor;
use Amp\Http\Client\Connection\Connection;
use Amp\Http\Client\Connection\Stream;
use Amp\Http\Client\EventListener;
use Amp\Http\Client\HttpException;
use Amp\Http\Client\Internal\HarAttributes;
use Amp\Http\Client\NetworkInterceptor;
use Amp\Http\Client\Request;
use Amp\Http\Client\Response;
use Amp\Http\HttpMessage;
use Amp\Socket\InternetAddress;
use Amp\Sync\LocalMutex;
use Revolt\EventLoop;
use function Amp\File\filesystem;
use function Amp\now;

final class LogHttpArchive implements EventListener
{
    use ForbidCloning;
    use ForbidSerialization;

    /**
     * @param non-empty-string $attributeName
     */
    private static function getDuration(Request $request, string $attributeName): float
    {
        if (!$request->hasAttribute($attributeName)) {
            return -1;
        }

        return $request->getAttribute($attributeName) ?? -1;
    }

    /**
     * @param non-empty-string $start
     * @param non-empty-string ...$ends
     */
    private static function getTime(Request $request, string $start, string ...$ends): float
    {
        if (!$request->hasAttribute($start)) {
            return -1;
        }

        foreach ($ends as $end) {
            if ($request->hasAttribute($end)) {
                return $request->getAttribute($end) - $request->getAttribute($start);
            }
        }

        return -1;
    }

    private static function formatHeaders(HttpMessage $message): array
    {
        $headers = [];

        foreach ($message->getHeaders() as $field => $values) {
            foreach ($values as $value) {
                $headers[] = [
                    'name' => $field,
                    'value' => $value,
                ];
            }
        }

        return $headers;
    }

    private static function formatEntry(Response $response): array
    {
        $request = $response->getRequest();

        $includeConnectTime = $request->hasAttribute(HarAttributes::INCLUDE_CONNECT_TIME)
            ? $request->getAttribute(HarAttributes::INCLUDE_CONNECT_TIME)
            : false;

        $connectDuration = $includeConnectTime ? self::getDuration(
            $request,
            HarAttributes::TIME_CONNECT
        ) : -1;

        $tlsHandshakeDuration = $includeConnectTime ? self::getDuration(
            $request,
            HarAttributes::TIME_SSL
        ) : -1;

        $sendDuration = self::getTime(
            $request,
            HarAttributes::TIME_SEND,
            HarAttributes::TIME_WAIT
        );

        $waitDuration = self::getTime(
            $request,
            HarAttributes::TIME_WAIT,
            HarAttributes::TIME_RECEIVE
        );

        $receiveDuration = self::getTime(
            $request,
            HarAttributes::TIME_RECEIVE,
            HarAttributes::TIME_COMPLETE
        );

        /** @psalm-suppress InvalidOperand */
        $blockedDuration = self::getTime(
            $request,
            HarAttributes::TIME_START,
            HarAttributes::TIME_COMPLETE
        ) - ($includeConnectTime ? $connectDuration : 0) - $sendDuration - $receiveDuration;

        $data = [
            'startedDateTime' => $request->getAttribute(HarAttributes::STARTED_DATE_TIME)->format(\DateTimeInterface::RFC3339_EXTENDED),
            'time' => self::getTime($request, HarAttributes::TIME_START, HarAttributes::TIME_COMPLETE),
            'request' => [
                'method' => $request->getMethod(),
                'url' => (string) $request->getUri()->withUserInfo(''),
                'httpVersion' => 'http/' . $request->getProtocolVersions()[0],
                'headers' => self::formatHeaders($request),
                'queryString' => [],
                'cookies' => [],
                'headersSize' => -1,
                'bodySize' => -1,
            ],
            'response' => [
                'status' => $response->getStatus(),
                'statusText' => $response->getReason(),
                'httpVersion' => 'http/' . $response->getProtocolVersion(),
                'headers' => self::formatHeaders($response),
                'cookies' => [],
                'redirectURL' => $response->getHeader('location') ?? '',
                'headersSize' => -1,
                'bodySize' => -1,
                'content' => [
                    'size' => (int) ($response->getHeader('content-length') ?? '-1'),
                    'mimeType' => $response->getHeader('content-type') ?? '',
                ],
            ],
            'cache' => [],
            'timings' => [
                'blocked' => $blockedDuration,
                'dns' => -1,
                'connect' => $connectDuration,
                'ssl' => $tlsHandshakeDuration,
                'send' => $sendDuration,
                'wait' => $waitDuration,
                'receive' => $receiveDuration,
            ],
        ];

        if ($request->hasAttribute(HarAttributes::SERVER_IP_ADDRESS)) {
            $data['serverIPAddress'] = $request->getAttribute(HarAttributes::SERVER_IP_ADDRESS);
        }

        return $data;
    }

    private LocalMutex $fileMutex;

    private Filesystem $filesystem;

    private ?File $fileHandle = null;

    private string $filePath;

    /** @psalm-suppress UnusedProperty False-positive. */
    private ?\Throwable $error = null;

    public function __construct(string $filePath, ?Filesystem $filesystem = null)
    {
        if (!\class_exists(Filesystem::class)) {
            throw new \Error("File request bodies require amphp/file to be installed");
        }

        $this->filePath = $filePath;
        $this->fileMutex = new LocalMutex;
        $this->filesystem = $filesystem ?? filesystem();
    }

    public function reset(): void
    {
        $this->rotate($this->filePath);
    }

    public function rotate(string $filePath): void
    {
        $lock = $this->fileMutex->acquire();

        // Will automatically reopen and reset the file
        $this->fileHandle = null;
        $this->filePath = $filePath;
        $this->error = null;

        $lock->release();
    }

    private function writeLog(Response $response): void
    {
        try {
            $response->getTrailers()->await();
        } catch (\Throwable) {
            // ignore, still log the remaining response times
        }

        try {
            $lock = $this->fileMutex->acquire();

            $firstEntry = $this->fileHandle === null;

            if ($firstEntry) {
                $this->fileHandle = $fileHandle = $this->filesystem->openFile($this->filePath, 'w');

                $header = '{"log":{"version":"1.2","creator":{"name":"amphp/http-client","version":"5.x"},"pages":[],"entries":[';

                $fileHandle->write($header);
            } else {
                $fileHandle = $this->fileHandle;

                \assert($fileHandle !== null);

                $fileHandle->seek(-3, Whence::Current);
            }

            $json = \json_encode(self::formatEntry($response), flags: \JSON_THROW_ON_ERROR);

            $fileHandle->write(($firstEntry ? '' : ',') . $json . ']}}');

            $lock->release();
        } catch (HttpException $e) {
            $this->error = $e;
        } catch (\Throwable $e) {
            $this->error = new HttpException('Writing HTTP archive log failed', previous: $e);
        }
    }

    #[\Override]
    public function requestStart(Request $request): void
    {
        if (!$request->hasAttribute(HarAttributes::STARTED_DATE_TIME)) {
            $request->setAttribute(HarAttributes::STARTED_DATE_TIME, new \DateTimeImmutable);
        }

        $this->addTiming(HarAttributes::TIME_START, $request);
    }

    #[\Override]
    public function connectionAcquired(Request $request, Connection $connection, int $streamCount): void
    {
        $request->setAttribute(HarAttributes::INCLUDE_CONNECT_TIME, $streamCount === 1);
        $request->setAttribute(HarAttributes::TIME_CONNECT, (int) ($connection->getConnectDuration() * 1000));

        $tlsHandshakeDuration = $connection->getTlsHandshakeDuration();
        if ($tlsHandshakeDuration !== null) {
            $request->setAttribute(HarAttributes::TIME_SSL, (int) ($tlsHandshakeDuration * 1000));
        } else {
            $request->setAttribute(HarAttributes::TIME_SSL, -1);
        }
    }

    #[\Override]
    public function requestHeaderStart(Request $request, Stream $stream): void
    {
        $address = $stream->getRemoteAddress();
        $host = match (true) {
            $address instanceof InternetAddress => $address->getAddress(),
            default => $address->toString(),
        };
        if (\strrpos($host, ':')) {
            $host = '[' . $host . ']';
        }

        $request->setAttribute(HarAttributes::SERVER_IP_ADDRESS, $host);
        $this->addTiming(HarAttributes::TIME_SEND, $request);
    }

    #[\Override]
    public function requestBodyEnd(Request $request, Stream $stream): void
    {
        $this->addTiming(HarAttributes::TIME_WAIT, $request);
    }

    #[\Override]
    public function responseHeaderStart(Request $request, Stream $stream): void
    {
        $this->addTiming(HarAttributes::TIME_RECEIVE, $request);
    }

    #[\Override]
    public function requestEnd(Request $request, Response $response): void
    {
        $this->addTiming(HarAttributes::TIME_COMPLETE, $request);

        EventLoop::queue(fn () => $this->writeLog($response));
    }

    /**
     * @param non-empty-string $key
     */
    private function addTiming(string $key, Request $request): void
    {
        if (!$request->hasAttribute($key)) {
            $request->setAttribute($key, now());
        }
    }

    #[\Override]
    public function requestFailed(Request $request, \Throwable $exception): void
    {
        // TODO: Log error to archive
    }

    #[\Override]
    public function requestHeaderEnd(Request $request, Stream $stream): void
    {
        // nothing to do
    }

    #[\Override]
    public function requestBodyStart(Request $request, Stream $stream): void
    {
        // nothing to do
    }

    #[\Override]
    public function requestBodyProgress(Request $request, Stream $stream): void
    {
        // nothing to do
    }

    #[\Override]
    public function responseHeaderEnd(Request $request, Stream $stream, Response $response): void
    {
        // nothing to do
    }

    #[\Override]
    public function responseBodyStart(Request $request, Stream $stream, Response $response): void
    {
        // nothing to do
    }

    #[\Override]
    public function responseBodyProgress(Request $request, Stream $stream, Response $response): void
    {
        // nothing to do
    }

    #[\Override]
    public function responseBodyEnd(Request $request, Stream $stream, Response $response): void
    {
        // nothing to do
    }

    #[\Override]
    public function applicationInterceptorStart(Request $request, ApplicationInterceptor $interceptor): void
    {
        // nothing to do
    }

    #[\Override]
    public function applicationInterceptorEnd(Request $request, ApplicationInterceptor $interceptor, Response $response): void
    {
        // nothing to do
    }

    #[\Override]
    public function networkInterceptorStart(Request $request, NetworkInterceptor $interceptor): void
    {
        // nothing to do
    }

    #[\Override]
    public function networkInterceptorEnd(Request $request, NetworkInterceptor $interceptor, Response $response): void
    {
        // nothing to do
    }

    #[\Override]
    public function push(Request $request): void
    {
        // nothing to do
    }

    #[\Override]
    public function requestRejected(Request $request): void
    {
        // nothing to do
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client;

final class MissingAttributeError extends \Error
{
    public function __construct(string $message)
    {
        parent::__construct($message);
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client;

class SocketException extends HttpException
{
}
<?php declare(strict_types=1);

namespace Amp\Http\Client;

final class TlsException extends SocketException
{
}
<?php declare(strict_types=1);

namespace Amp\Http\Client;

use Amp\ByteStream\BufferException;
use Amp\ByteStream\ReadableBuffer;
use Amp\ByteStream\ReadableStream;
use Amp\ByteStream\ReadableStreamChain;
use Amp\Http\Client\Internal\FormField;
use Amp\Http\Http1\Rfc7230;
use Amp\Http\InvalidHeaderException;
use League\Uri\QueryString;
use function Amp\ByteStream\buffer;

final class Form implements HttpContent
{
    /** @var FormField[] */
    private array $fields = [];

    private string $boundary;

    private bool $isMultipart = false;

    private bool $used = false;

    private ?ReadableStream $content = null;

    private ?int $contentLength = null;

    /**
     * @param string|null $boundary An optional multipart boundary string
     * @throws HttpException
     */
    public function __construct(?string $boundary = null)
    {
        try {
            $this->boundary = $boundary ?? \bin2hex(\random_bytes(16));
        } catch (\Exception $exception) {
            throw new HttpException('Failed to obtain random boundary', 0, $exception);
        }
    }

    public function addField(string $name, string $content, ?string $contentType = null): void
    {
        if ($this->used) {
            throw new \Error('Form body is already used and can no longer be modified');
        }

        $this->fields[] = new FormField($name, BufferedContent::fromString($content, $contentType));
    }

    /**
     * Adds each member of the array as an entry for the given key name. Array keys are persevered.
     *
     * @param array<string|array> $fields
     */
    public function addNestedFields(string $name, array $fields): void
    {
        foreach ($this->flattenArray($fields) as $key => $value) {
            $this->addField($name . $key, $value);
        }
    }

    /**
     * @return array<string, string>
     */
    private function flattenArray(array $fields): array
    {
        $result = [];
        foreach ($fields as $outerKey => $value) {
            $key = "[{$outerKey}]";
            if (!\is_array($value)) {
                $result[$key] = (string) $value;
                continue;
            }

            foreach ($this->flattenArray($value) as $innerKey => $flattened) {
                $result[$key . $innerKey] = $flattened;
            }
        }

        return $result;
    }

    public function addStream(string $name, HttpContent $content, ?string $filename = null): void
    {
        if ($this->used) {
            throw new \Error('Form body is already used and can no longer be modified');
        }

        $this->fields[] = new FormField($name, $content, $filename);
        $this->isMultipart = true;
    }

    /**
     * @param string $path Local file path. Filename will be provided to the server.
     * @throws HttpException
     */
    public function addFile(string $name, string $path, ?string $contentType = null): void
    {
        $this->addStream($name, StreamedContent::fromFile($path, $contentType), \basename($path));
    }

    #[\Override]
    public function getContent(): ReadableStream
    {
        $this->used = true;

        if ($this->content === null) {
            if ($this->isMultipart) {
                $this->content = $this->generateMultipartStream($this->getMultipartParts());
            } else {
                $this->content = new ReadableBuffer($this->generateFormEncodedBody());
            }
        }

        try {
            return $this->content;
        } finally {
            $this->content = null;
        }
    }

    #[\Override]
    public function getContentType(): string
    {
        return $this->isMultipart
            ? "multipart/form-data; boundary={$this->boundary}"
            : 'application/x-www-form-urlencoded';
    }

    /**
     * @throws HttpException
     */
    #[\Override]
    public function getContentLength(): ?int
    {
        if ($this->contentLength !== null) {
            return $this->contentLength;
        }

        if ($this->isMultipart) {
            $fields = $this->getMultipartParts();
            $length = 0;

            foreach ($fields as $field) {
                if (\is_string($field)) {
                    $length += \strlen($field);
                } else {
                    $contentLength = $field->getContentLength();
                    if ($contentLength === null) {
                        return null;
                    }

                    $length += $contentLength;
                }
            }

            return $this->contentLength = $length;
        }

        $body = $this->generateFormEncodedBody();
        $this->content = new ReadableBuffer($body);

        return $this->contentLength = \strlen($body);
    }

    /**
     * @throws HttpException
     */
    private function getMultipartParts(): array
    {
        try {
            $parts = [];

            foreach ($this->fields as $field) {
                $parts[] = "--{$this->boundary}\r\n" . Rfc7230::formatHeaderPairs($field->getHeaderPairs()) . "\r\n";
                $parts[] = $field;
                $parts[] = "\r\n";
            }

            $parts[] = "--{$this->boundary}--\r\n";

            return $parts;
        } catch (InvalidHeaderException|HttpException $e) {
            throw new HttpException('Failed to build request body', 0, $e);
        }
    }

    /**
     * @throws HttpException
     */
    private function generateFormEncodedBody(): string
    {
        $pairs = [];
        foreach ($this->fields as $field) {
            try {
                $pairs[] = [$field->getName(), buffer($field->getContent())];
            } catch (BufferException|HttpException $e) {
                throw new HttpException('Failed to build request body', 0, $e);
            }
        }

        /** @psalm-suppress InvalidArgument */
        return QueryString::build($pairs, '&', \PHP_QUERY_RFC1738) ?? '';
    }

    /**
     * @param (FormField|string)[] $parts
     * @throws HttpException
     */
    private function generateMultipartStream(array $parts): ReadableStream
    {
        $streams = [];
        foreach ($parts as $part) {
            if (\is_string($part)) {
                $streams[] = new ReadableBuffer($part);
            } else {
                $streams[] = $part->getContent();
            }
        }

        return new ReadableStreamChain(...$streams);
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client;

use Amp\ForbidSerialization;
use Amp\Future;
use Amp\Http\Client\Connection\UpgradedSocket;
use Amp\Http\Client\Internal\EventInvoker;
use Amp\Http\Client\Internal\Phase;
use Amp\Http\HttpMessage;
use Amp\Http\HttpRequest;
use League\Uri;
use Psr\Http\Message\UriInterface;
use function Amp\async;

/**
 * An HTTP request.
 *
 * @psalm-import-type HeaderParamValueType from HttpMessage
 * @psalm-import-type HeaderParamArrayType from HttpMessage
 * @psalm-type ProtocolVersion = '1.0'|'1.1'|'2'
 */
final class Request extends HttpRequest
{
    use ForbidSerialization;

    public const DEFAULT_HEADER_SIZE_LIMIT = 2 * 8192;
    public const DEFAULT_BODY_SIZE_LIMIT = 10485760;

    /** @var list<ProtocolVersion> */
    private array $protocolVersions = ['1.1', '2'];

    private HttpContent $body;

    private float $tcpConnectTimeout = 10;

    private float $tlsHandshakeTimeout = 10;

    private float $transferTimeout = 10;

    private float $inactivityTimeout = 10;

    /** @var non-negative-int */
    private int $bodySizeLimit = self::DEFAULT_BODY_SIZE_LIMIT;

    /** @var non-negative-int */
    private int $headerSizeLimit = self::DEFAULT_HEADER_SIZE_LIMIT;

    /** @var null|\Closure(Request, Future): void */
    private ?\Closure $onPush = null;

    /** @var null|\Closure(UpgradedSocket, Request, Response): void */
    private ?\Closure $onUpgrade = null;

    /** @var null|\Closure(Response): void */
    private ?\Closure $onInformationalResponse = null;

    /** @var array<non-empty-string, mixed> */
    private array $attributes = [];

    /** @var EventListener[] */
    private array $eventListeners = [];

    /**
     * @param non-empty-string $method
     */
    public function __construct(UriInterface|string $uri, string $method = "GET", HttpContent|string $body = '')
    {
        parent::__construct($method, $uri instanceof UriInterface ? $uri : $this->createUriFromString($uri));

        $this->setBody($body);
    }

    public function addEventListener(EventListener $eventListener): void
    {
        $this->eventListeners[\spl_object_id($eventListener)] = $eventListener;
    }

    /**
     * @return list<EventListener>
     */
    public function getEventListeners(): array
    {
        return \array_values($this->eventListeners);
    }

    /**
     * @return bool Whether processing the request might have already been started on the server.
     */
    public function isUnprocessed(): bool
    {
        if (EventInvoker::isRejected($this)) {
            return true;
        }

        return \in_array(EventInvoker::getPhase($this), [Phase::Unprocessed, Phase::Blocked, Phase::Connected], true);
    }

    /**
     * Retrieve the request's acceptable HTTP protocol versions.
     *
     * @return list<ProtocolVersion>
     */
    public function getProtocolVersions(): array
    {
        return $this->protocolVersions;
    }

    /**
     * Assign the request's acceptable HTTP protocol versions.
     *
     * The HTTP client might choose any of these.
     *
     * @param array<ProtocolVersion> $versions
     */
    public function setProtocolVersions(array $versions): void
    {
        $versions = \array_unique($versions);

        if (empty($versions)) {
            throw new \Error("Empty array of protocol versions provided, must not be empty.");
        }

        foreach ($versions as $version) {
            if (!\in_array($version, ["1.0", "1.1", "2"], true)) {
                throw new \Error(
                    "Invalid HTTP protocol version: " . $version
                );
            }
        }

        $this->protocolVersions = \array_values($versions);
    }

    /**
     * Specify the request's HTTP method verb.
     */
    #[\Override]
    public function setMethod(string $method): void
    {
        parent::setMethod($method);
    }

    /**
     * Specify the request's HTTP URI.
     */
    #[\Override]
    public function setUri(UriInterface|string $uri): void
    {
        parent::setUri($uri instanceof UriInterface ? $uri : $this->createUriFromString($uri));
    }

    /**
     * Assign a value for the specified header field by replacing any existing values for that field.
     *
     * @param non-empty-string $name Header name.
     * @param HeaderParamValueType $value Header value.
     */
    #[\Override]
    public function setHeader(string $name, array|string $value): void
    {
        if (($name[0] ?? ":") === ":") {
            throw new \Error("Header name cannot be empty or start with a colon (:)");
        }

        parent::setHeader($name, $value);
    }

    /**
     * Assign a value for the specified header field by adding a header line.
     *
     * @param non-empty-string $name Header name.
     * @param HeaderParamValueType $value Header value.
     */
    #[\Override]
    public function addHeader(string $name, array|string $value): void
    {
        if (($name[0] ?? ":") === ":") {
            throw new \Error("Header name cannot be empty or start with a colon (:)");
        }

        parent::addHeader($name, $value);
    }

    #[\Override]
    public function setHeaders(array $headers): void
    {
        /** @noinspection PhpUnhandledExceptionInspection */
        parent::setHeaders($headers);
    }

    #[\Override]
    public function replaceHeaders(array $headers): void
    {
        /** @noinspection PhpUnhandledExceptionInspection */
        parent::replaceHeaders($headers);
    }

    /**
     * Remove the specified header field from the message.
     *
     * @param string $name Header name.
     */
    #[\Override]
    public function removeHeader(string $name): void
    {
        parent::removeHeader($name);
    }

    #[\Override]
    public function setQueryParameter(string $key, array|string|null $value): void
    {
        parent::setQueryParameter($key, $value);
    }

    #[\Override]
    public function addQueryParameter(string $key, array|string|null $value): void
    {
        parent::addQueryParameter($key, $value);
    }

    #[\Override]
    public function setQueryParameters(array $parameters): void
    {
        parent::setQueryParameters($parameters);
    }

    #[\Override]
    public function replaceQueryParameters(array $parameters): void
    {
        parent::replaceQueryParameters($parameters);
    }

    #[\Override]
    public function removeQueryParameter(string $key): void
    {
        parent::removeQueryParameter($key);
    }

    #[\Override]
    public function removeQuery(): void
    {
        parent::removeQuery();
    }

    /**
     * Retrieve the request body.
     */
    public function getBody(): HttpContent
    {
        return $this->body;
    }

    /**
     * Assign the message entity body.
     */
    public function setBody(HttpContent|string $body): void
    {
        $this->body = \is_string($body) ? BufferedContent::fromString($body) : $body;
    }

    /**
     * Registers a callback to the request that is invoked when the server pushes an additional resource.
     * The callback is given two parameters: the Request generated from the pushed resource, and a future for the
     * Response containing the pushed resource. An HttpException, StreamException, or CancelledException can be thrown
     * to refuse the push. If no callback is registered, pushes are automatically rejected.
     *
     * Interceptors can mostly use {@see interceptPush()} instead.
     *
     * Example:
     * function (Request $request, Future $future): void {
     *     $uri = $request->getUri(); // URI of pushed resource.
     *     $response = $future->await(); // Wait for resource to arrive.
     *     // Use Response object from completed future.
     * }
     *
     * @param null|\Closure(Request, Future): void $onPush
     */
    public function setPushHandler(?\Closure $onPush): void
    {
        $this->onPush = $onPush;
    }

    /**
     * Allows interceptors to modify also pushed responses.
     *
     * If no push closure has been set by the application, the interceptor won't be invoked. If you want to enable
     * push in an interceptor without the application setting a push handler, you need to use {@see setPushHandler()}.
     *
     * @param \Closure(Request, Response): Response $interceptor Receives the response and might modify it or return a
     * new instance.
     */
    public function interceptPush(\Closure $interceptor): void
    {
        if ($this->onPush === null) {
            return;
        }

        $onPush = $this->onPush;
        $this->onPush = static function (Request $request, Future $future) use ($onPush, $interceptor): void {
            $future = async(function () use ($request, $interceptor, $future): Response {
                $response = $future->await();
                return $interceptor($request, $response);
            });

            $onPush($request, $future);
        };
    }

    /**
     * @return null|\Closure(Request, Future): void
     */
    public function getPushHandler(): ?\Closure
    {
        return $this->onPush;
    }

    /**
     * Registers a callback invoked if a 101 response is returned to the request.
     *
     * @param null|\Closure(UpgradedSocket, Request, Response): void $onUpgrade
     */
    public function setUpgradeHandler(?\Closure $onUpgrade): void
    {
        $this->onUpgrade = $onUpgrade;
    }

    /**
     * @return null|\Closure(UpgradedSocket, Request, Response): void
     */
    public function getUpgradeHandler(): ?\Closure
    {
        return $this->onUpgrade;
    }

    /**
     * Registers a callback invoked when a 1xx response is returned to the request (other than a 101).
     *
     * @param null|\Closure(Response): void $onInformationalResponse
     */
    public function setInformationalResponseHandler(?\Closure $onInformationalResponse): void
    {
        $this->onInformationalResponse = $onInformationalResponse;
    }

    /**
     * @return null|\Closure(Response): void
     */
    public function getInformationalResponseHandler(): ?\Closure
    {
        return $this->onInformationalResponse;
    }

    /**
     * @return float Timeout in seconds for the TCP connection.
     */
    public function getTcpConnectTimeout(): float
    {
        return $this->tcpConnectTimeout;
    }

    /**
     * Set the timeout in seconds for establishing the TCP connection. Use 0 for no timeout.
     */
    public function setTcpConnectTimeout(float $tcpConnectTimeout): void
    {
        $this->tcpConnectTimeout = \max(0, $tcpConnectTimeout);
    }

    /**
     * @return float Timeout in seconds for the TLS handshake.
     */
    public function getTlsHandshakeTimeout(): float
    {
        return $this->tlsHandshakeTimeout;
    }

    /**
     * Set the timeout in seconds for the TLS handshake. Use 0 for no timeout.
     */
    public function setTlsHandshakeTimeout(float $tlsHandshakeTimeout): void
    {
        $this->tlsHandshakeTimeout = \max(0, $tlsHandshakeTimeout);
    }

    /**
     * @return float Timeout in seconds for the HTTP transfer (not counting TCP connect and TLS handshake).
     */
    public function getTransferTimeout(): float
    {
        return $this->transferTimeout;
    }

    /**
     * @param float $transferTimeout The timeout in seconds for the entire HTTP request transfer. Use 0 for no
     *      transfer timeout.
     */
    public function setTransferTimeout(float $transferTimeout): void
    {
        $this->transferTimeout = \max(0, $transferTimeout);
    }

    /**
     * @return float Timeout in seconds since the last data was received before the request fails due to inactivity.
     */
    public function getInactivityTimeout(): float
    {
        return $this->inactivityTimeout;
    }

    /**
     * @param float $inactivityTimeout The timeout in seconds since the last data was received before the request
     *      fails due to inactivity. Use 0 for no inactivity timeout.
     */
    public function setInactivityTimeout(float $inactivityTimeout): void
    {
        $this->inactivityTimeout = \max(0, $inactivityTimeout);
    }

    /**
     * @return non-negative-int Size limit of the response headers. Applies only to HTTP/1.x requests.
     *      0 indicates no limit.
     */
    public function getHeaderSizeLimit(): int
    {
        return $this->headerSizeLimit;
    }

    /**
     * @param int $headerSizeLimit The size limit of response headers. Applies only to HTTP/1.x requests.
     *      Use 0 for no limit. Default is 16 KiB.
     */
    public function setHeaderSizeLimit(int $headerSizeLimit): void
    {
        $this->headerSizeLimit = \max(0, $headerSizeLimit);
    }

    /**
     * @return int Size limit of the response body. 0 indicates no limit.
     */
    public function getBodySizeLimit(): int
    {
        return $this->bodySizeLimit;
    }

    /**
     * @param int $bodySizeLimit The size limit of the response body. Use 0 for no limit. Default is 10 MiB.
     */
    public function setBodySizeLimit(int $bodySizeLimit): void
    {
        $this->bodySizeLimit = \max(0, $bodySizeLimit);
    }

    /**
     * @return array<non-empty-string, mixed> An array of all request attributes in the request's local storage,
     *      indexed by name.
     */
    public function getAttributes(): array
    {
        return $this->attributes;
    }

    /**
     * Check whether a variable with the given name exists in the request's local storage.
     *
     * Each request has its own local storage to which applications and interceptors may read and write data.
     * Other interceptors which are aware of this data can then access it without the server being tightly coupled to
     * specific implementations.
     *
     * @param non-empty-string $name Name of the attribute, should be namespaced with a vendor and package namespace
     *      like classes.
     */
    public function hasAttribute(string $name): bool
    {
        return \array_key_exists($name, $this->attributes);
    }

    /**
     * Retrieve a variable from the request's local storage.
     *
     * Each request has its own local storage to which applications and interceptors may read and write data.
     * Other interceptors which are aware of this data can then access it without the server being tightly coupled to
     * specific implementations.
     *
     * @param non-empty-string $name Name of the attribute, should be namespaced with a vendor and package namespace
     *      like classes.
     *
     * @throws MissingAttributeError If an attribute with the given name does not exist.
     */
    public function getAttribute(string $name): mixed
    {
        if (!$this->hasAttribute($name)) {
            throw new MissingAttributeError("The requested attribute '{$name}' does not exist");
        }

        return $this->attributes[$name];
    }

    /**
     * Assign a variable to the request's local storage.
     *
     * Each request has its own local storage to which applications and interceptors may read and write data.
     * Other interceptors which are aware of this data can then access it without the server being tightly coupled to
     * specific implementations.
     *
     * **Example**
     *
     * ```php
     * $request->setAttribute(Timing::class, $stopWatch);
     * ```
     *
     * @param non-empty-string $name Name of the attribute, should be namespaced with a vendor and package namespace
     *      like classes.
     * @param mixed $value Value of the attribute, might be any serializable value.
     */
    public function setAttribute(string $name, mixed $value): void
    {
        $this->attributes[$name] = $value;
    }

    /**
     * Remove an attribute from the request's local storage.
     *
     * @param non-empty-string $name Name of the attribute, should be namespaced with a vendor and package namespace
     *      like classes.
     *
     * @throws MissingAttributeError If an attribute with the given name does not exist.
     */
    public function removeAttribute(string $name): void
    {
        if (!$this->hasAttribute($name)) {
            throw new MissingAttributeError("The requested attribute '{$name}' does not exist");
        }

        unset($this->attributes[$name]);
    }

    /**
     * Remove all attributes from the request's local storage.
     */
    public function removeAttributes(): void
    {
        $this->attributes = [];
    }

    #[\Override]
    public function isIdempotent(): bool
    {
        // https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
        return \in_array($this->getMethod(), ['GET', 'HEAD', 'PUT', 'DELETE'], true);
    }

    private function createUriFromString(string $uri): UriInterface
    {
        return Uri\Http::new($uri);
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client;

use Amp\Cancellation;
use Amp\NullCancellation;

/**
 * Convenient HTTP client for use in applications and libraries, providing a default for the cancellation and
 * automatically cloning the passed request, so future application requests can re-use the same object again.
 */
final class HttpClient implements DelegateHttpClient
{
    /**
     * @param EventListener[] $eventListeners
     */
    public function __construct(
        private readonly DelegateHttpClient $httpClient,
        private readonly array $eventListeners,
    ) {
    }

    /**
     * Request a specific resource from an HTTP server.
     *
     * @throws HttpException
     */
    #[\Override]
    public function request(Request $request, ?Cancellation $cancellation = null): Response
    {
        return processRequest(
            $request,
            $this->eventListeners,
            fn () => $this->httpClient->request($request, $cancellation ?? new NullCancellation()),
        );
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client;

use Amp\Cancellation;
use Amp\Http\Client\Interceptor\DecompressResponse;

/**
 * Allows intercepting an application request to an HTTP resource.
 */
interface ApplicationInterceptor
{
    /**
     * Intercepts an application request to an HTTP resource.
     *
     * The implementation might modify the request, delegate the request handling to the `$httpClient`, and/or modify
     * the response after `$httpClient->request(...)` returns.
     *
     * An interceptor might also short-circuit and not delegate to the `$httpClient` at all.
     *
     * Any retry or follow-up request must use a new request instance instead of reusing `$request` to ensure a properly
     * working interceptor chain, e.g. the {@see DecompressResponse} interceptor only decodes a response if the
     * `accept-encoding` header isn't set manually. If the request is reused, the first attempt will set the header
     * and the second attempt will see the header and won't decode the response, because it thinks another interceptor
     * or the application itself will care about the decoding.
     */
    public function request(
        Request $request,
        Cancellation $cancellation,
        DelegateHttpClient $httpClient
    ): Response;
}
<?php declare(strict_types=1);

namespace Amp\Http\Client;

use Amp\ByteStream\Payload;
use Amp\ByteStream\ReadableStream;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Future;
use Amp\Http\HttpMessage;
use Amp\Http\HttpResponse;

/**
 * An HTTP response.
 *
 * @psalm-import-type HeaderParamValueType from HttpMessage
 * @psalm-import-type HeaderParamArrayType from HttpMessage
 * @psalm-type ProtocolVersion = '1.0'|'1.1'|'2'
 */
final class Response extends HttpResponse
{
    use ForbidSerialization;
    use ForbidCloning;

    /** @var ProtocolVersion */
    private string $protocolVersion;

    private Request $request;

    private Payload $body;

    /** @var Future<Trailers> */
    private Future $trailers;

    private ?Response $previousResponse;

    /**
     * @param ProtocolVersion $protocolVersion
     * @param HeaderParamArrayType $headers
     * @param Future<Trailers>|null $trailers
     *
     * @throws \Amp\Http\InvalidHeaderException
     */
    public function __construct(
        string $protocolVersion,
        int $status,
        ?string $reason,
        array $headers,
        ReadableStream|string|null $body,
        Request $request,
        ?Future $trailers = null,
        ?Response $previousResponse = null
    ) {
        parent::__construct($status, $reason);

        $this->setProtocolVersion($protocolVersion);
        $this->setHeaders($headers);
        $this->setBody($body);
        $this->request = $request;
        /** @noinspection PhpUnhandledExceptionInspection */
        $this->trailers = $trailers ?? Future::complete(new Trailers([]));
        $this->previousResponse = $previousResponse;
    }

    /**
     * Retrieve the HTTP protocol version used for the request.
     *
     * @return ProtocolVersion
     */
    public function getProtocolVersion(): string
    {
        return $this->protocolVersion;
    }

    /**
     * @param ProtocolVersion $protocolVersion
     */
    public function setProtocolVersion(string $protocolVersion): void
    {
        if (!\in_array($protocolVersion, ["1.0", "1.1", "2"], true)) {
            throw new \Error(
                "Invalid HTTP protocol version: " . $protocolVersion
            );
        }

        $this->protocolVersion = $protocolVersion;
    }

    #[\Override]
    public function setStatus(int $status, ?string $reason = null): void
    {
        parent::setStatus($status, $reason);
    }

    /**
     * Retrieve the Request instance that resulted in this Response instance.
     */
    public function getRequest(): Request
    {
        return $this->request;
    }

    public function setRequest(Request $request): void
    {
        $this->request = $request;
    }

    /**
     * Retrieve the original Request instance associated with this Response instance.
     *
     * A given Response may be the result of one or more redirects. This method is a shortcut to
     * access information from the original Request that led to this response.
     */
    public function getOriginalRequest(): Request
    {
        if (empty($this->previousResponse)) {
            return $this->request;
        }

        return $this->previousResponse->getOriginalRequest();
    }

    /**
     * Retrieve the original Response instance associated with this Response instance.
     *
     * A given Response may be the result of one or more redirects. This method is a shortcut to
     * access information from the original Response that led to this response.
     */
    public function getOriginalResponse(): Response
    {
        if (empty($this->previousResponse)) {
            return $this;
        }

        return $this->previousResponse->getOriginalResponse();
    }

    /**
     * If this Response is the result of a redirect traverse up the redirect history.
     */
    public function getPreviousResponse(): ?Response
    {
        return $this->previousResponse;
    }

    public function setPreviousResponse(?Response $previousResponse): void
    {
        $this->previousResponse = $previousResponse;
    }

    /**
     * Assign a value for the specified header field by replacing any existing values for that field.
     *
     * @param non-empty-string $name Header name.
     * @param HeaderParamValueType $value Header value.
     */
    #[\Override]
    public function setHeader(string $name, array|string $value): void
    {
        if (($name[0] ?? ":") === ":") {
            throw new \Error("Header name cannot be empty or start with a colon (:)");
        }

        parent::setHeader($name, $value);
    }

    /**
     * Assign a value for the specified header field by adding an additional header line.
     *
     * @param non-empty-string $name Header name.
     * @param HeaderParamValueType $value Header value.
     */
    #[\Override]
    public function addHeader(string $name, array|string $value): void
    {
        if (($name[0] ?? ":") === ":") {
            throw new \Error("Header name cannot be empty or start with a colon (:)");
        }

        parent::addHeader($name, $value);
    }

    #[\Override]
    public function setHeaders(array $headers): void
    {
        /** @noinspection PhpUnhandledExceptionInspection */
        parent::setHeaders($headers);
    }

    #[\Override]
    public function replaceHeaders(array $headers): void
    {
        /** @noinspection PhpUnhandledExceptionInspection */
        parent::replaceHeaders($headers);
    }

    /**
     * Remove the specified header field from the message.
     *
     * @param string $name Header name.
     */
    #[\Override]
    public function removeHeader(string $name): void
    {
        parent::removeHeader($name);
    }

    /**
     * Retrieve the response body.
     */
    public function getBody(): Payload
    {
        return $this->body;
    }

    public function setBody(ReadableStream|string|null $body): void
    {
        $this->body = match (true) {
            $body instanceof Payload => $body,
            $body instanceof ReadableStream, \is_string($body) => new Payload($body),
            $body === null => new Payload(''),
        };
    }

    /**
     * @return Future<Trailers>
     */
    public function getTrailers(): Future
    {
        return $this->trailers;
    }

    /**
     * @param Future<Trailers> $future
     */
    public function setTrailers(Future $future): void
    {
        $this->trailers = $future;
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client;

use Amp\ForbidSerialization;
use Amp\Http\HttpMessage;
use Amp\Http\InvalidHeaderException;

final class Trailers extends HttpMessage
{
    use ForbidSerialization;

    /** @see https://tools.ietf.org/html/rfc7230#section-4.1.2 */
    public const DISALLOWED_TRAILERS = [
        "authorization" => true,
        "content-encoding" => true,
        "content-length" => true,
        "content-range" => true,
        "content-type" => true,
        "cookie" => true,
        "expect" => true,
        "host" => true,
        "pragma" => true,
        "proxy-authenticate" => true,
        "proxy-authorization" => true,
        "range" => true,
        "te" => true,
        "trailer" => true,
        "transfer-encoding" => true,
        "www-authenticate" => true,
    ];

    /**
     * @param string[]|string[][] $headers
     *
     * @throws InvalidHeaderException Thrown if a disallowed field is in the header values.
     */
    public function __construct(array $headers)
    {
        if (!empty($headers)) {
            $this->setHeaders($headers);
        }

        if (\array_intersect_key($this->getHeaders(), self::DISALLOWED_TRAILERS)) {
            throw new InvalidHeaderException('Disallowed field in trailers');
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Connection;

use Amp\Cancellation;
use Amp\ForbidSerialization;
use Amp\Http\Client\Request;

final class UnlimitedConnectionPool implements ConnectionPool
{
    use ForbidSerialization;

    private ConnectionLimitingPool $pool;

    public function __construct(?ConnectionFactory $connectionFactory = null)
    {
        $this->pool = ConnectionLimitingPool::byAuthority(\PHP_INT_MAX, $connectionFactory);
    }

    public function __clone()
    {
        $this->pool = clone $this->pool;
    }

    public function getTotalConnectionAttempts(): int
    {
        return $this->pool->getTotalConnectionAttempts();
    }

    public function getTotalStreamRequests(): int
    {
        return $this->pool->getTotalStreamRequests();
    }

    public function getOpenConnectionCount(): int
    {
        return $this->pool->getOpenConnectionCount();
    }

    #[\Override]
    public function getStream(Request $request, Cancellation $cancellation): Stream
    {
        return $this->pool->getStream($request, $cancellation);
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Connection;

use Amp\Closable;
use Amp\Http\Client\Request;
use Amp\Socket\SocketAddress;
use Amp\Socket\TlsInfo;

interface Connection extends Closable
{
    /**
     * @return Stream|null Returns a stream for the given request, or null if no stream is available or if
     *                     the connection is not suited for the given request. The first request for a stream
     *                     on a new connection MUST return a Stream instance.
     */
    public function getStream(Request $request): ?Stream;

    public function isIdle(): bool;

    /**
     * @return string[] Array of supported protocol versions.
     */
    public function getProtocolVersions(): array;

    public function getLocalAddress(): SocketAddress;

    public function getRemoteAddress(): SocketAddress;

    public function getTlsInfo(): ?TlsInfo;

    /**
     * @return float|null Returns the TLS handshake duration if applicable in seconds.
     */
    public function getTlsHandshakeDuration(): ?float;

    /**
     * @return float Returns the total connect duration in seconds, including the TLS handshake.
     */
    public function getConnectDuration(): float;
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Connection;

use Amp\ByteStream\ReadableStreamIteratorAggregate;
use Amp\ByteStream\ResourceStream;
use Amp\Cancellation;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Socket\Socket;
use Amp\Socket\SocketAddress;
use Amp\Socket\TlsInfo;
use Amp\Socket\TlsState;

/**
 * @implements \IteratorAggregate<int, string>
 */
final class UpgradedSocket implements Socket, ResourceStream, \IteratorAggregate
{
    use ForbidCloning;
    use ForbidSerialization;
    use ReadableStreamIteratorAggregate;

    private ?string $buffer;

    /**
     * @param string $buffer Remaining buffer previously read from the socket.
     */
    public function __construct(private readonly Socket $socket, string $buffer)
    {
        $this->buffer = $buffer !== '' ? $buffer : null;
    }

    #[\Override]
    public function read(?Cancellation $cancellation = null, ?int $limit = null): ?string
    {
        if ($this->buffer !== null) {
            if ($limit !== null && $limit < \strlen($this->buffer)) {
                $buffer = \substr($this->buffer, 0, $limit);
                $this->buffer = \substr($this->buffer, $limit);

                return $buffer;
            }

            $buffer = $this->buffer;
            $this->buffer = null;

            return $buffer;
        }

        return $this->socket->read($cancellation);
    }

    #[\Override]
    public function close(): void
    {
        $this->socket->close();
    }

    public function __destruct()
    {
        $this->close();
    }

    #[\Override]
    public function write(string $bytes): void
    {
        $this->socket->write($bytes);
    }

    #[\Override]
    public function end(): void
    {
        $this->socket->end();
    }

    #[\Override]
    public function reference(): void
    {
        if ($this->socket instanceof ResourceStream) {
            $this->socket->reference();
        }
    }

    #[\Override]
    public function unreference(): void
    {
        if ($this->socket instanceof ResourceStream) {
            $this->socket->unreference();
        }
    }

    #[\Override]
    public function isClosed(): bool
    {
        return $this->socket->isClosed();
    }

    #[\Override]
    public function onClose(\Closure $onClose): void
    {
        $this->socket->onClose($onClose);
    }

    #[\Override]
    public function getLocalAddress(): SocketAddress
    {
        return $this->socket->getLocalAddress();
    }

    #[\Override]
    public function getRemoteAddress(): SocketAddress
    {
        return $this->socket->getRemoteAddress();
    }

    #[\Override]
    public function setupTls(?Cancellation $cancellation = null): void
    {
        $this->socket->setupTls($cancellation);
    }

    #[\Override]
    public function shutdownTls(?Cancellation $cancellation = null): void
    {
        $this->socket->shutdownTls();
    }

    #[\Override]
    public function isTlsConfigurationAvailable(): bool
    {
        return $this->socket->isTlsConfigurationAvailable();
    }

    #[\Override]
    public function getTlsState(): TlsState
    {
        return $this->socket->getTlsState();
    }

    #[\Override]
    public function getTlsInfo(): ?TlsInfo
    {
        return $this->socket->getTlsInfo();
    }

    #[\Override]
    public function isReadable(): bool
    {
        return $this->socket->isReadable();
    }

    #[\Override]
    public function isWritable(): bool
    {
        return $this->socket->isWritable();
    }

    #[\Override]
    public function getResource()
    {
        return $this->socket instanceof ResourceStream
            ? $this->socket->getResource()
            : null;
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Connection;

use Amp\ByteStream\ReadableIterableStream;
use Amp\ByteStream\ResourceStream;
use Amp\ByteStream\StreamException;
use Amp\Cancellation;
use Amp\CancelledException;
use Amp\CompositeCancellation;
use Amp\DeferredCancellation;
use Amp\DeferredFuture;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Future;
use Amp\Http;
use Amp\Http\Client\Connection\Internal\Http1Parser;
use Amp\Http\Client\Connection\Internal\RequestNormalizer;
use Amp\Http\Client\HttpException;
use Amp\Http\Client\Internal\ResponseBodyStream;
use Amp\Http\Client\InvalidRequestException;
use Amp\Http\Client\ParseException;
use Amp\Http\Client\Request;
use Amp\Http\Client\Response;
use Amp\Http\Client\SocketException;
use Amp\Http\Client\TimeoutException;
use Amp\Http\Http1\Rfc7230;
use Amp\Http\InvalidHeaderException;
use Amp\Pipeline\Queue;
use Amp\Socket\Socket;
use Amp\Socket\SocketAddress;
use Amp\Socket\TlsInfo;
use Amp\TimeoutCancellation;
use Revolt\EventLoop;
use function Amp\async;
use function Amp\Http\Client\events;
use function Amp\Http\Client\Internal\normalizeRequestPathWithQuery;
use function Amp\now;

/**
 * Socket client implementation.
 *
 * @see Client
 */
final class Http1Connection implements Connection
{
    use ForbidSerialization;
    use ForbidCloning;

    private const MAX_KEEP_ALIVE_TIMEOUT = 60;
    private const PROTOCOL_VERSIONS = ['1.0', '1.1'];

    private ?Socket $socket;

    private bool $busy = false;

    /** @var int Number of stream requests made on this connection. */
    private int $streamCounter = 0;

    /** @var int Number of requests made on this connection. */
    private int $requestCounter = 0;

    /** @var string|null Keep alive timeout watcher ID. */
    private ?string $timeoutWatcher = null;

    /** @var int Keep-Alive timeout from last response. */
    private int $priorTimeout = self::MAX_KEEP_ALIVE_TIMEOUT;

    /** @var list<\Closure():void>|null */
    private ?array $onClose = [];

    private float $lastUsedAt;

    private bool $explicitTimeout = false;

    private SocketAddress $localAddress;

    private SocketAddress $remoteAddress;

    private ?TlsInfo $tlsInfo;

    private ?Future $idleRead = null;

    public function __construct(
        Socket $socket,
        private readonly float $connectDuration,
        private readonly ?float $tlsHandshakeDuration,
        private readonly float $timeoutGracePeriod = 2,
    ) {
        $this->socket = $socket;
        $this->localAddress = $socket->getLocalAddress();
        $this->remoteAddress = $socket->getRemoteAddress();
        $this->tlsInfo = $socket->getTlsInfo();
        $this->lastUsedAt = now();
        $this->watchIdleConnection();
    }

    public function __destruct()
    {
        $this->close();
    }

    #[\Override]
    public function onClose(\Closure $onClose): void
    {
        if (!$this->socket || $this->socket->isClosed()) {
            EventLoop::queue($onClose);
            return;
        }

        $this->onClose[] = $onClose;
    }

    #[\Override]
    public function close(): void
    {
        $this->socket?->close();
        $this->free();
    }

    #[\Override]
    public function isClosed(): bool
    {
        return $this->socket?->isClosed() ?? true;
    }

    #[\Override]
    public function isIdle(): bool
    {
        return !$this->busy;
    }

    #[\Override]
    public function getLocalAddress(): SocketAddress
    {
        return $this->localAddress;
    }

    #[\Override]
    public function getRemoteAddress(): SocketAddress
    {
        return $this->remoteAddress;
    }

    #[\Override]
    public function getTlsInfo(): ?TlsInfo
    {
        return $this->tlsInfo;
    }

    #[\Override]
    public function getTlsHandshakeDuration(): ?float
    {
        return $this->tlsHandshakeDuration;
    }

    #[\Override]
    public function getProtocolVersions(): array
    {
        return self::PROTOCOL_VERSIONS;
    }

    #[\Override]
    public function getStream(Request $request): ?Stream
    {
        if ($this->busy || ($this->requestCounter && !$this->hasStreamFor($request))) {
            return null;
        }

        $this->busy = true;

        events()->connectionAcquired($request, $this, ++$this->streamCounter);

        return HttpStream::fromConnection($this, $this->request(...), $this->release(...));
    }

    private function free(): void
    {
        $this->socket = null;
        $this->idleRead = null;

        $this->lastUsedAt = 0;

        if ($this->timeoutWatcher !== null) {
            EventLoop::cancel($this->timeoutWatcher);
        }

        if ($this->onClose !== null) {
            $onClose = $this->onClose;
            $this->onClose = null;

            foreach ($onClose as $callback) {
                EventLoop::queue($callback);
            }
        }
    }

    private function hasStreamFor(Request $request): bool
    {
        return !$this->busy
            && $this->socket
            && !$this->socket->isClosed()
            && ($this->getRemainingTime() > 0 || $request->isIdempotent());
    }

    private function readChunk(float $timeout): ?string
    {
        $cancellation = $timeout > 0 ? new TimeoutCancellation($timeout) : null;

        if ($this->idleRead) {
            $future = $this->idleRead;
            $this->idleRead = null;
            return $future->await($cancellation);
        }

        return $this->socket?->read($cancellation);
    }

    private function request(Request $request, Cancellation $cancellation, Stream $stream): Response
    {
        ++$this->requestCounter;

        if ($this->socket instanceof ResourceStream) {
            $this->socket->reference();
        }

        if ($this->timeoutWatcher !== null) {
            EventLoop::cancel($this->timeoutWatcher);
            $this->timeoutWatcher = null;
        }

        RequestNormalizer::normalizeRequest($request);

        $protocolVersion = $this->determineProtocolVersion($request);

        $request->setProtocolVersions([$protocolVersion]);

        if ($request->getTransferTimeout() > 0) {
            $timeoutCancellation = new TimeoutCancellation($request->getTransferTimeout());
            $combinedCancellation = new CompositeCancellation($cancellation, $timeoutCancellation);
        } else {
            $combinedCancellation = $cancellation;
        }

        $cancellationId = $combinedCancellation->subscribe($this->close(...));

        $responseDeferred = new DeferredFuture();

        EventLoop::queue(function () use (
            $responseDeferred,
            $request,
            $stream,
            $protocolVersion,
            $combinedCancellation,
        ): void {
            try {
                $this->writeRequest($request, $stream, $protocolVersion, $combinedCancellation);
            } catch (\Throwable $exception) {
                if (!$responseDeferred->isComplete()) {
                    $responseDeferred->error($exception);
                }
            }
        });

        EventLoop::queue(function () use (
            $responseDeferred,
            $request,
            $stream,
            $cancellation,
            $combinedCancellation,
            $cancellationId,
        ): void {
            try {
                $response = $this->readResponse($request, $cancellation, $combinedCancellation, $stream);
                if (!$responseDeferred->isComplete()) {
                    $responseDeferred->complete($response);
                }
            } catch (\Throwable $exception) {
                $this->socket?->close();

                if (!$responseDeferred->isComplete()) {
                    $responseDeferred->error($exception);
                }
            } finally {
                $combinedCancellation->unsubscribe($cancellationId);
            }
        });

        return $responseDeferred->getFuture()->await($cancellation);
    }

    private function release(): void
    {
        $this->busy = false;
    }

    /**
     * @throws CancelledException
     * @throws HttpException
     * @throws ParseException
     * @throws SocketException
     *
     * @psalm-suppress UnusedVariable
     */
    private function readResponse(
        Request $request,
        Cancellation $originalCancellation,
        Cancellation $readingCancellation,
        Stream $stream,
    ): Response {
        $bodyEmitter = new Queue();
        $trailersDeferred = new DeferredFuture;
        $trailersDeferred->getFuture()->ignore();

        $trailers = [];
        $trailersCallback = static function (array $headers) use (&$trailers): void {
            $trailers = $headers;
        };

        $bodyDeferredCancellation = new DeferredCancellation;
        $bodyCancellation = new CompositeCancellation(
            $readingCancellation,
            $bodyDeferredCancellation->getCancellation(),
        );

        $parser = new Http1Parser(
            $request,
            $stream,
            $bodyEmitter->pushAsync(...),
            $bodyCancellation,
            $trailersCallback,
        );

        $start = now();
        $inactivityTimeout = $request->getInactivityTimeout();

        try {
            if ($this->socket === null) {
                throw new SocketException('Socket closed prior to response completion');
            }

            while (null !== $chunk = $this->readChunk($inactivityTimeout)) {
                parseChunk:
                $response = $parser->parse($chunk);
                if ($response === null) {
                    if ($this->socket === null) {
                        throw new SocketException('Socket closed prior to response completion');
                    }

                    continue;
                }

                $this->lastUsedAt = now();

                $status = $response->getStatus();

                if ($status === Http\HttpStatus::SWITCHING_PROTOCOLS) {
                    $connection = Http\parseHeaderTokens($response, 'connection');
                    if ($connection === null || !\in_array('upgrade', $connection, true)) {
                        throw new HttpException('Switching protocols response missing "Connection: upgrade" header');
                    }

                    if (!$response->hasHeader('upgrade')) {
                        throw new HttpException('Switching protocols response missing "Upgrade" header');
                    }

                    $trailersDeferred->complete($trailers);

                    return $this->handleUpgradeResponse($request, $response, $parser->getBuffer());
                }

                if ($status < 200) { // 1XX responses (excluding 101, handled above)
                    $onInformationalResponse = $request->getInformationalResponseHandler();

                    if ($onInformationalResponse !== null) {
                        $onInformationalResponse($response);
                    }

                    $chunk = $parser->getBuffer();

                    $parser = new Http1Parser(
                        $request,
                        $stream,
                        $bodyEmitter->pushAsync(...),
                        $bodyCancellation,
                        $trailersCallback,
                    );

                    goto parseChunk;
                }

                if ($status < 300 && $request->getMethod() === 'CONNECT') {
                    $trailersDeferred->complete($trailers);

                    return $this->handleUpgradeResponse($request, $response, $parser->getBuffer());
                }

                $response->setTrailers($trailersDeferred->getFuture());
                $response->setBody(new ResponseBodyStream(
                    new ReadableIterableStream($bodyEmitter->pipe()),
                    $bodyDeferredCancellation,
                ));

                [$requestTimeout, $explicitTimeout, $priorTimeout] = $this->determineKeepAliveTimeout($response);

                // Read body async
                EventLoop::queue(function () use (
                    $parser,
                    $request,
                    $requestTimeout,
                    $explicitTimeout,
                    $priorTimeout,
                    $inactivityTimeout,
                    $bodyEmitter,
                    $trailersDeferred,
                    $originalCancellation,
                    $readingCancellation,
                    $bodyCancellation,
                    $stream,
                    &$trailers,
                ) {
                    $closeId = $bodyCancellation->subscribe($this->close(...));

                    try {
                        // Required, otherwise responses without body hang
                        if (!$parser->isComplete()) {
                            // Directly parse again in case we already have the full body but aborted parsing
                            // to complete future with headers.
                            $chunk = null;

                            try {
                                do {
                                    $parser->parse($chunk);
                                    if ($parser->isComplete()) {
                                        break;
                                    }

                                    if ($this->socket === null) {
                                        throw new SocketException('Socket closed prior to response completion');
                                    }
                                } while (null !== $chunk = $this->readChunk($inactivityTimeout));
                            } catch (CancelledException $e) {
                                $this->close();
                                $originalCancellation->throwIfRequested();

                                throw new TimeoutException(
                                    'Inactivity timeout exceeded, more than ' . $inactivityTimeout
                                        . ' seconds elapsed from last data received',
                                    previous: $e,
                                );
                            }

                            $originalCancellation->throwIfRequested();

                            if ($readingCancellation->isRequested()) {
                                throw new TimeoutException('Allowed transfer timeout exceeded, took longer than ' . $request->getTransferTimeout() . ' s');
                            }

                            $bodyCancellation->throwIfRequested();

                            // Ignore check if neither content-length nor chunked encoding are given.
                            if (!$parser->isComplete() && $parser->getState() !== Http1Parser::BODY_IDENTITY_EOF) {
                                throw new SocketException('Socket disconnected prior to response completion');
                            }
                        }

                        $this->explicitTimeout = $explicitTimeout ?: $this->explicitTimeout;
                        $this->priorTimeout = $priorTimeout ?? $this->priorTimeout;

                        if ($requestTimeout > 0 && $parser->getState() !== Http1Parser::BODY_IDENTITY_EOF) {
                            $connectionRef = \WeakReference::create($this);
                            $this->timeoutWatcher = EventLoop::delay(
                                $requestTimeout,
                                static fn () => $connectionRef->get()?->close(),
                            );
                            EventLoop::unreference($this->timeoutWatcher);
                            $this->watchIdleConnection();
                        } else {
                            $this->close();
                        }

                        $this->busy = false;

                        $bodyEmitter->complete();
                        $trailersDeferred->complete($trailers);
                    } catch (\Throwable $e) {
                        $this->close();

                        $e = $this->wrapException($e);

                        $trailersDeferred->error($e);

                        if (!$e instanceof CancelledException) {
                            $e = new StreamException(
                                'HTTP response did not complete: ' . $e->getMessage(),
                                previous: $e,
                            );
                        }

                        $bodyEmitter->error($e);
                    } finally {
                        $bodyCancellation->unsubscribe($closeId);
                    }
                });

                return $response;
            }

            $originalCancellation->throwIfRequested();
            $readingCancellation->throwIfRequested();

            throw new SocketException(\sprintf(
                "Receiving the response headers for '%s' failed, because the socket to '%s' @ '%s' closed early with %d bytes received within %0.3f seconds",
                (string) $request->getUri()->withUserInfo(''),
                $request->getUri()->withUserInfo('')->getAuthority(),
                $this->socket?->getRemoteAddress()?->toString() ?? '???',
                \strlen($parser->getBuffer()),
                now() - $start
            ));
        } catch (HttpException $e) {
            $this->close();
            throw $e;
        } catch (CancelledException $e) {
            $this->close();

            // Throw original cancellation if it was requested.
            $originalCancellation->throwIfRequested();

            if ($readingCancellation->isRequested()) {
                throw new TimeoutException('Allowed transfer timeout exceeded, took longer than ' . $request->getTransferTimeout() . ' s', previous: $e);
            }

            throw new TimeoutException(
                'Inactivity timeout exceeded, more than ' . (string) $inactivityTimeout .
                ' seconds elapsed from last data received',
                previous: $e,
            );
        } catch (\Throwable $e) {
            $this->close();
            throw new SocketException('Receiving the response headers failed: ' . $e->getMessage(), 0, $e);
        }
    }

    private function handleUpgradeResponse(Request $request, Response $response, string $buffer): Response
    {
        if ($this->socket === null) {
            throw new SocketException('Socket closed while upgrading');
        }

        $socket = new UpgradedSocket($this->socket, $buffer);
        $this->free(); // Mark this connection as unusable without closing socket.

        if (($onUpgrade = $request->getUpgradeHandler()) === null) {
            $socket->close();

            throw new HttpException('CONNECT or upgrade request made without upgrade handler callback');
        }

        try {
            $onUpgrade($socket, $request, $response);
        } catch (\Throwable $exception) {
            $socket->close();

            throw new HttpException('Upgrade handler threw an exception', 0, $exception);
        }

        return $response;
    }

    /**
     * @return float Approximate number of milliseconds remaining until the connection is closed.
     */
    private function getRemainingTime(): float
    {
        $timestamp = $this->lastUsedAt + ($this->explicitTimeout ? $this->priorTimeout * 1000 : $this->timeoutGracePeriod);
        return \max(0, $timestamp - now());
    }

    /**
     * @return array{int, bool, int|null}
     */
    private function determineKeepAliveTimeout(Response $response): array
    {
        $request = $response->getRequest();

        $requestConnHeader = $request->getHeader('connection') ?? '';
        $responseConnHeader = $response->getHeader('connection') ?? '';

        if (!\strcasecmp($requestConnHeader, 'close')) {
            return [0, false, null];
        }

        if ($response->getProtocolVersion() === '1.0') {
            return [0, false, null];
        }

        if (!\strcasecmp($responseConnHeader, 'close')) {
            return [0, false, null];
        }

        $params = Http\parseMultipleHeaderFields($response, 'keep-alive')[0] ?? null;

        $timeout = (int) ($params['timeout'] ?? $this->priorTimeout);
        $timeout = \min(\max(0, $timeout), self::MAX_KEEP_ALIVE_TIMEOUT);

        return [$timeout, isset($params['timeout']), $timeout];
    }

    /**
     * @return '1.0'|'1.1'
     */
    private function determineProtocolVersion(Request $request): string
    {
        $protocolVersions = $request->getProtocolVersions();

        if (\in_array("1.1", $protocolVersions, true)) {
            return "1.1";
        }

        if (\in_array("1.0", $protocolVersions, true)) {
            return "1.0";
        }

        throw new InvalidRequestException(
            $request,
            "None of the requested protocol versions is supported: " . \implode(", ", $protocolVersions)
        );
    }

    private function writeRequest(
        Request $request,
        Stream $stream,
        string $protocolVersion,
        Cancellation $cancellation,
    ): void {
        try {
            $socket = $this->socket;
            if ($socket === null) {
                throw new SocketException('Socket closed before request started');
            }

            events()->requestHeaderStart($request, $stream);
            $rawHeaders = $this->generateRawHeader($request, $protocolVersion);
            $socket->write($rawHeaders);
            events()->requestHeaderEnd($request, $stream);

            if ($request->getMethod() === 'CONNECT') {
                events()->requestBodyStart($request, $stream);
                events()->requestBodyEnd($request, $stream);
                return;
            }

            $chunking = $request->getHeader("transfer-encoding") === "chunked";
            $remainingBytes = $request->getHeader("content-length");

            if ($remainingBytes !== null) {
                $remainingBytes = (int) $remainingBytes;
            }

            if ($chunking && $protocolVersion === "1.0") {
                throw new InvalidRequestException($request, "Can't send chunked bodies over HTTP/1.0");
            }

            events()->requestBodyStart($request, $stream);

            // We always buffer the last chunk to make sure we don't write $contentLength bytes if the body is too long.
            $buffer = "";
            $body = $request->getBody()->getContent();
            while (null !== $chunk = $body->read($cancellation)) {
                if ($chunk === "") {
                    continue;
                }

                if ($chunking) {
                    $chunk = \dechex(\strlen($chunk)) . "\r\n" . $chunk . "\r\n";
                } elseif ($remainingBytes !== null) {
                    $remainingBytes -= \strlen($chunk);

                    if ($remainingBytes < 0) {
                        throw new InvalidRequestException(
                            $request,
                            "Body contained more bytes than specified in Content-Length, aborting request"
                        );
                    }
                }

                $socket->write($buffer);
                events()->requestBodyProgress($request, $stream);
                $buffer = $chunk;
            }

            $cancellation->throwIfRequested();

            // Flush last buffered chunk.
            $socket->write($chunking ? $buffer . "0\r\n\r\n" : $buffer);
            events()->requestBodyProgress($request, $stream);

            if (!$chunking && $remainingBytes !== null && $remainingBytes > 0) {
                throw new InvalidRequestException(
                    $request,
                    "Body contained fewer bytes than specified in Content-Length, aborting request"
                );
            }

            events()->requestBodyEnd($request, $stream);
        } catch (StreamException $exception) {
            throw new SocketException('Socket disconnected prior to response completion', 0, $exception);
        }
    }

    /**
     * @throws HttpException
     */
    private function generateRawHeader(Request $request, string $protocolVersion): string
    {
        $uri = $request->getUri();
        $requestUri = normalizeRequestPathWithQuery($request);

        $method = $request->getMethod();

        if ($method === 'CONNECT') {
            $defaultPort = $uri->getScheme() === 'https' ? 443 : 80;
            $requestUri = $uri->getHost() . ':' . ($uri->getPort() ?? $defaultPort);
        }

        $header = $method . ' ' . $requestUri . ' HTTP/' . $protocolVersion . "\r\n";

        try {
            $header .= Rfc7230::formatHeaderPairs($request->getHeaderPairs());
        } catch (InvalidHeaderException $e) {
            throw new HttpException($e->getMessage());
        }

        return $header . "\r\n";
    }

    private function watchIdleConnection(): void
    {
        if ($this->socket instanceof ResourceStream) {
            $this->socket->unreference();
        }

        $socket = $this->socket;
        $connectionRef = \WeakReference::create($this);

        $this->idleRead = async(static function () use ($socket, $connectionRef): ?string {
            $chunk = null;
            try {
                $chunk = $socket?->read();
            } catch (\Throwable) {
                // Close connection below.
            }

            if ($chunk === null) {
                $connectionRef->get()?->close();
            }

            return $chunk;
        });
    }

    #[\Override]
    public function getConnectDuration(): float
    {
        return $this->connectDuration;
    }

    private function wrapException(\Throwable $exception): \Throwable
    {
        if ($exception instanceof HttpException || $exception instanceof CancelledException) {
            return $exception;
        }

        return new HttpException($exception->getMessage(), previous: $exception);
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Connection;

use Amp\Cancellation;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Http\Client\Request;
use Amp\Http\Client\Response;
use Amp\Sync\KeyedSemaphore;
use function Amp\async;

final class StreamLimitingPool implements ConnectionPool
{
    use ForbidCloning;
    use ForbidSerialization;

    public static function byHost(ConnectionPool $delegate, KeyedSemaphore $semaphore): self
    {
        return new self($delegate, $semaphore, static function (Request $request) {
            return $request->getUri()->getHost();
        });
    }

    public static function byStaticKey(
        ConnectionPool $delegate,
        KeyedSemaphore $semaphore,
        string $key = ''
    ): self {
        return new self($delegate, $semaphore, static function () use ($key) {
            return $key;
        });
    }

    public static function byCustomKey(
        ConnectionPool $delegate,
        KeyedSemaphore $semaphore,
        callable $requestToKeyMapper
    ): self {
        return new self($delegate, $semaphore, $requestToKeyMapper);
    }

    private ConnectionPool $delegate;

    private KeyedSemaphore $semaphore;

    /** @var callable */
    private $requestToKeyMapper;

    private function __construct(ConnectionPool $delegate, KeyedSemaphore $semaphore, callable $requestToKeyMapper)
    {
        $this->delegate = $delegate;
        $this->semaphore = $semaphore;
        $this->requestToKeyMapper = $requestToKeyMapper;
    }

    #[\Override]
    public function getStream(Request $request, Cancellation $cancellation): Stream
    {
        $lock = $this->semaphore->acquire(($this->requestToKeyMapper)($request));

        $stream = $this->delegate->getStream($request, $cancellation);

        return HttpStream::fromStream(
            $stream,
            static function (Request $request, Cancellation $cancellation) use (
                $stream,
                $lock
            ): Response {
                try {
                    $response = $stream->request($request, $cancellation);
                } catch (\Throwable $e) {
                    $lock->release();
                    throw $e;
                }

                // await response being completely received
                async(static function () use ($response, $lock): void {
                    try {
                        $response->getTrailers()->await();
                    } finally {
                        $lock->release();
                    }
                })->ignore();

                return $response;
            },
            static function () use ($lock): void {
                $lock->release();
            }
        );
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Connection;

use Amp\Cancellation;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Http\Client\HttpException;
use Amp\Http\Client\Request;
use Amp\Http\Client\Response;
use Amp\Socket\SocketAddress;
use Amp\Socket\TlsInfo;
use function Amp\Http\Client\processRequest;

/**
 * @psalm-type RequestCallbackType = callable(Request, Cancellation, HttpStream):Response
 * @psalm-type ReleaseCallbackType = callable():void
 */
final class HttpStream implements Stream
{
    use ForbidSerialization;
    use ForbidCloning;

    /**
     * @param RequestCallbackType $RequestCallbackType
     * @param ReleaseCallbackType $ReleaseCallbackType
     */
    public static function fromConnection(
        Connection $connection,
        callable $RequestCallbackType,
        callable $ReleaseCallbackType
    ): self {
        return new self(
            $connection->getLocalAddress(),
            $connection->getRemoteAddress(),
            $connection->getTlsInfo(),
            $RequestCallbackType,
            $ReleaseCallbackType,
        );
    }

    /**
     * @param RequestCallbackType $RequestCallbackType
     * @param ReleaseCallbackType $ReleaseCallbackType
     */
    public static function fromStream(Stream $stream, callable $RequestCallbackType, callable $ReleaseCallbackType): self
    {
        return new self(
            $stream->getLocalAddress(),
            $stream->getRemoteAddress(),
            $stream->getTlsInfo(),
            $RequestCallbackType,
            $ReleaseCallbackType,
        );
    }

    /** @var callable */
    private $RequestCallbackType;

    /** @var callable|null */
    private $ReleaseCallbackType;

    /**
     * @param RequestCallbackType $RequestCallbackType
     * @param ReleaseCallbackType $ReleaseCallbackType
     */
    private function __construct(
        private readonly SocketAddress $localAddress,
        private readonly SocketAddress $remoteAddress,
        private readonly ?TlsInfo $tlsInfo,
        callable $RequestCallbackType,
        callable $ReleaseCallbackType,
    ) {
        $this->RequestCallbackType = $RequestCallbackType;
        $this->ReleaseCallbackType = $ReleaseCallbackType;
    }

    public function __destruct()
    {
        if ($this->ReleaseCallbackType !== null) {
            ($this->ReleaseCallbackType)();
        }
    }

    /**
     * @throws HttpException
     */
    #[\Override]
    public function request(Request $request, Cancellation $cancellation): Response
    {
        if ($this->ReleaseCallbackType === null) {
            throw new \Error('A stream may only be used for a single request');
        }

        $this->ReleaseCallbackType = null;

        return processRequest($request, [], fn (): Response => ($this->RequestCallbackType)($request, $cancellation, $this));
    }

    #[\Override]
    public function getLocalAddress(): SocketAddress
    {
        return $this->localAddress;
    }

    #[\Override]
    public function getRemoteAddress(): SocketAddress
    {
        return $this->remoteAddress;
    }

    #[\Override]
    public function getTlsInfo(): ?TlsInfo
    {
        return $this->tlsInfo;
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Connection;

use Amp\Cancellation;
use Amp\Http\Client\Request;
use function Amp\Http\Client\events;

interface ConnectionFactory
{
    /**
     * Creates a new connection.
     *
     * The implementation should call appropriate event handlers via {@see events()}.
     */
    public function create(Request $request, Cancellation $cancellation): Connection;
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Connection;

use Amp\Cancellation;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Http\Client\Connection\Internal\Http2ConnectionProcessor;
use Amp\Http\Client\Request;
use Amp\Http\Client\Response;
use Amp\Socket\Socket;
use Amp\Socket\SocketAddress;
use Amp\Socket\TlsInfo;
use Amp\TimeoutCancellation;
use function Amp\Http\Client\events;

final class Http2Connection implements Connection
{
    use ForbidSerialization;
    use ForbidCloning;

    private const PROTOCOL_VERSIONS = ['2'];

    private readonly Http2ConnectionProcessor $processor;

    private int $streamCounter = 0;

    private int $requestCount = 0;

    public function __construct(
        private readonly Socket $socket,
        private readonly float $connectDuration,
        private readonly ?float $tlsHandshakeDuration
    ) {
        $this->processor = new Http2ConnectionProcessor($socket);
    }

    #[\Override]
    public function isIdle(): bool
    {
        return $this->processor->isIdle();
    }

    #[\Override]
    public function getProtocolVersions(): array
    {
        return self::PROTOCOL_VERSIONS;
    }

    public function initialize(?Cancellation $cancellation = null): void
    {
        $this->processor->initialize($cancellation ?? new TimeoutCancellation(5));
    }

    #[\Override]
    public function getStream(Request $request): ?Stream
    {
        if (!$this->processor->isInitialized()) {
            throw new \Error('The ' . __CLASS__ . '::initialize() invocation must be complete before using the connection');
        }

        if ($this->processor->isClosed() || $this->processor->getRemainingStreams() <= 0) {
            return null;
        }

        $this->processor->reserveStream();

        events()->connectionAcquired($request, $this, ++$this->streamCounter);

        return HttpStream::fromConnection($this, $this->request(...), $this->processor->unreserveStream(...));
    }

    #[\Override]
    public function onClose(\Closure $onClose): void
    {
        $this->processor->onClose($onClose);
    }

    #[\Override]
    public function close(): void
    {
        $this->processor->close();
    }

    #[\Override]
    public function isClosed(): bool
    {
        return $this->processor->isClosed();
    }

    #[\Override]
    public function getLocalAddress(): SocketAddress
    {
        return $this->socket->getLocalAddress();
    }

    #[\Override]
    public function getRemoteAddress(): SocketAddress
    {
        return $this->socket->getRemoteAddress();
    }

    #[\Override]
    public function getTlsInfo(): ?TlsInfo
    {
        return $this->socket->getTlsInfo();
    }

    private function request(Request $request, Cancellation $cancellation, Stream $stream): Response
    {
        $this->requestCount++;

        return $this->processor->request($request, $cancellation, $stream);
    }

    #[\Override]
    public function getTlsHandshakeDuration(): ?float
    {
        return $this->tlsHandshakeDuration;
    }

    #[\Override]
    public function getConnectDuration(): float
    {
        return $this->connectDuration;
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Connection;

use Amp\ByteStream\StreamException;
use Amp\Cancellation;
use Amp\CancelledException;
use Amp\CompositeCancellation;
use Amp\Http\Client\InvalidRequestException;
use Amp\Http\Client\Request;
use Amp\Http\Client\SocketException;
use Amp\Http\Client\TimeoutException;
use Amp\Http\Client\TlsException;
use Amp\Socket;
use Amp\Socket\ClientTlsContext;
use Amp\Socket\ConnectContext;
use Amp\TimeoutCancellation;
use function Amp\now;

final class DefaultConnectionFactory implements ConnectionFactory
{
    private readonly ConnectContext $connectContext;

    public function __construct(
        private readonly ?Socket\SocketConnector $connector = null,
        ?ConnectContext $connectContext = null,
    ) {
        $this->connectContext = $connectContext ?? new ConnectContext();
    }

    #[\Override]
    public function create(Request $request, Cancellation $cancellation): Connection
    {
        $connectStart = now();

        $connector = $this->connector ?? Socket\socketConnector();
        $connectContext = $this->connectContext;

        $uri = $request->getUri();
        $scheme = $uri->getScheme();

        if (!\in_array($scheme, ['http', 'https'], true)) {
            throw new InvalidRequestException($request, 'Invalid scheme provided in the request URI: ' . (string) $uri);
        }

        $isHttps = $scheme === 'https';
        $defaultPort = $isHttps ? 443 : 80;

        $host = $uri->getHost();
        $port = $uri->getPort() ?? $defaultPort;

        if ($host === '') {
            throw new InvalidRequestException($request, 'A host must be provided in the request URI: ' . (string) $uri);
        }

        $authority = $host . ':' . $port;
        $protocolVersions = $request->getProtocolVersions();

        $isConnect = $request->getMethod() === 'CONNECT';

        if ($isHttps) {
            $protocols = [];

            if (!$isConnect && \in_array('2', $protocolVersions, true)) {
                $protocols[] = 'h2';
            }

            if (\in_array('1.1', $protocolVersions, true) || \in_array('1.0', $protocolVersions, true)) {
                $protocols[] = 'http/1.1';
            }

            if (!$protocols) {
                throw new InvalidRequestException(
                    $request,
                    \sprintf(
                        "None of the requested protocol versions (%s) are supported by %s (HTTP/2 is only supported on HTTPS)",
                        \implode(', ', $protocolVersions),
                        self::class
                    )
                );
            }

            $tlsContext = ($connectContext->getTlsContext() ?? new ClientTlsContext(''))
                ->withApplicationLayerProtocols($protocols)
                ->withPeerCapturing();

            if ($protocols === ['http/1.1']) {
                // If we only have HTTP/1.1 available, don't set application layer protocols.
                // There are misbehaving sites like n11.com, see https://github.com/amphp/http-client/issues/255
                $tlsContext = $tlsContext->withApplicationLayerProtocols([]);
            }

            if ($tlsContext->getPeerName() === '') {
                $tlsContext = $tlsContext->withPeerName($host);
            }

            $connectContext = $connectContext->withTlsContext($tlsContext);
        }

        try {
            $socket = $connector->connect(
                'tcp://' . $authority,
                $connectContext->withConnectTimeout($request->getTcpConnectTimeout()),
                $cancellation
            );
        } catch (Socket\ConnectException $connectException) {
            throw new SocketException(\sprintf("Connection to '%s' failed", $authority), 0, $connectException);
        } catch (CancelledException) {
            // In case of a user cancellation request, throw the expected exception
            $cancellation->throwIfRequested();

            // Otherwise we ran into a timeout of our TimeoutCancellation
            throw new TimeoutException(\sprintf("Connection to '%s' timed out, took longer than " . $request->getTcpConnectTimeout() . ' s', $authority));
        }

        $tlsHandshakeDuration = null;

        if ($isHttps) {
            $tlsHandshakeStart = now();

            try {
                $tlsState = $socket->getTlsState();

                // Error if anything enabled TLS on a new connection before we can do it
                if ($tlsState !== Socket\TlsState::Disabled) {
                    $socket->close();

                    throw new TlsException('Failed to setup TLS connection, connection was in an unexpected TLS state (' . $tlsState->name . ')');
                }

                $socket->setupTls(new CompositeCancellation(
                    $cancellation,
                    new TimeoutCancellation($request->getTlsHandshakeTimeout())
                ));
            } catch (StreamException $streamException) {
                $socket->close();

                $errorMessage = $streamException->getMessage();
                \preg_match('/error:[0-9a-f]*:[^:]*:[^:]*:(.+)$/i', $errorMessage, $matches);
                $errorMessage = \trim($matches[1] ?? \explode('():', $errorMessage, 2)[1] ?? $errorMessage);

                throw new TlsException(\sprintf(
                    "Connection to '%s' @ '%s' closed during TLS handshake: %s",
                    $authority,
                    $socket->getRemoteAddress()->toString(),
                    $errorMessage,
                ), 0, $streamException);
            } catch (CancelledException) {
                $socket->close();

                // In case of a user cancellation request, throw the expected exception
                $cancellation->throwIfRequested();

                // Otherwise we ran into a timeout of our TimeoutCancellation
                throw new TimeoutException(\sprintf(
                    "TLS handshake with '%s' @ '%s' timed out, took longer than " . $request->getTlsHandshakeTimeout() . ' s',
                    $authority,
                    $socket->getRemoteAddress()->toString()
                ));
            }

            $tlsInfo = $socket->getTlsInfo();
            if ($tlsInfo === null) {
                $socket->close();

                throw new TlsException(\sprintf(
                    "Socket closed after TLS handshake with '%s' @ '%s'",
                    $authority,
                    $socket->getRemoteAddress()->toString()
                ));
            }

            $tlsHandshakeDuration = now() - $tlsHandshakeStart;
            $connectDuration = now() - $connectStart;

            if ($tlsInfo->getApplicationLayerProtocol() === 'h2') {
                $http2Connection = new Http2Connection($socket, $connectDuration, $tlsHandshakeDuration);
                $http2Connection->initialize($cancellation);

                return $http2Connection;
            }
        }

        $connectDuration = now() - $connectStart;

        // Treat the presence of only HTTP/2 as prior knowledge, see https://http2.github.io/http2-spec/#known-http
        if ($request->getProtocolVersions() === ['2']) {
            $http2Connection = new Http2Connection($socket, $connectDuration, $tlsHandshakeDuration);
            $http2Connection->initialize($cancellation);

            return $http2Connection;
        }

        if (!\array_intersect($request->getProtocolVersions(), ['1.0', '1.1'])) {
            $socket->close();

            throw new InvalidRequestException($request, \sprintf(
                "None of the requested protocol versions (%s) are supported by '%s' @ '%s'",
                \implode(', ', $protocolVersions),
                $authority,
                $socket->getRemoteAddress()->toString()
            ));
        }

        return new Http1Connection($socket, $connectDuration, $tlsHandshakeDuration);
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Connection;

use Amp\Cancellation;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Http\Client\HttpException;
use Amp\Http\Client\NetworkInterceptor;
use Amp\Http\Client\Request;
use Amp\Http\Client\Response;
use Amp\Socket\SocketAddress;
use Amp\Socket\TlsInfo;
use function Amp\Http\Client\events;
use function Amp\Http\Client\processRequest;

final class InterceptedStream implements Stream
{
    use ForbidCloning;
    use ForbidSerialization;

    private static \WeakMap $requestInterceptors;

    private ?NetworkInterceptor $interceptor;

    public function __construct(private readonly Stream $stream, NetworkInterceptor $interceptor)
    {
        $this->interceptor = $interceptor;
    }

    /**
     * @throws HttpException
     */
    #[\Override]
    public function request(Request $request, Cancellation $cancellation): Response
    {
        return processRequest($request, [], function () use ($request, $cancellation): Response {
            $interceptor = $this->interceptor;
            $this->interceptor = null;

            if (!$interceptor) {
                throw new \Error(__METHOD__ . ' may only be invoked once per instance. '
                    . 'If you need to implement retries or otherwise issue multiple requests, register an ApplicationInterceptor to do so.');
            }

            /** @psalm-suppress RedundantPropertyInitializationCheck */
            self::$requestInterceptors ??= new \WeakMap();
            $requestInterceptors = self::$requestInterceptors[$request] ?? [];
            $requestInterceptors[] = $interceptor;
            self::$requestInterceptors[$request] = $requestInterceptors;

            events()->networkInterceptorStart($request, $interceptor);

            $response = $interceptor->requestViaNetwork($request, $cancellation, $this->stream);

            events()->networkInterceptorEnd($request, $interceptor, $response);

            return $response;
        });
    }

    #[\Override]
    public function getLocalAddress(): SocketAddress
    {
        return $this->stream->getLocalAddress();
    }

    #[\Override]
    public function getRemoteAddress(): SocketAddress
    {
        return $this->stream->getRemoteAddress();
    }

    #[\Override]
    public function getTlsInfo(): ?TlsInfo
    {
        return $this->stream->getTlsInfo();
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Connection\Internal;

use Amp\ByteStream\ReadableBuffer;
use Amp\Cancellation;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Future;
use Amp\Http\Client\Connection\Stream;
use Amp\Http\Client\ParseException;
use Amp\Http\Client\Request;
use Amp\Http\Client\Response;
use Amp\Http\Http1\Rfc7230;
use Amp\Http\HttpMessage;
use Amp\Http\HttpStatus;
use Amp\Http\InvalidHeaderException;
use function Amp\Http\Client\events;
use function Amp\Http\mapHeaderPairs;

/**
 * @internal
 *
 * @psalm-import-type HeaderMapType from HttpMessage
 */
final class Http1Parser
{
    use ForbidSerialization;
    use ForbidCloning;

    private const STATUS_LINE_PATTERN = "#^
        HTTP/(?P<protocol>\d+\.\d+)[\x20\x09]+
        (?P<status>[1-9]\d\d)[\x20\x09]*
        (?P<reason>[^\x01-\x08\x10-\x19]*)
    $#ix";

    public const AWAITING_HEADERS = 0;
    public const BODY_IDENTITY = 1;
    public const BODY_IDENTITY_EOF = 2;
    public const BODY_CHUNKS = 3;
    public const TRAILERS_START = 4;
    public const TRAILERS = 5;

    /** @var \WeakReference<Response>|null */
    private ?\WeakReference $responseRef = null;

    private int $state = self::AWAITING_HEADERS;

    private bool $headersStarted = false;
    private bool $bodyStarted = false;

    private string $buffer = '';

    private ?int $remainingBodyBytes = null;

    private int $bodyBytesConsumed = 0;

    private bool $chunkedEncoding = false;

    private ?int $chunkLengthRemaining = null;

    private bool $complete = false;

    private readonly int $maxHeaderBytes;

    private readonly int $maxBodyBytes;

    /**
     * @param \Closure(string):Future $bodyDataCallback
     * @param \Closure(HeaderMapType):void $trailersCallback
     */
    public function __construct(
        private readonly Request $request,
        private readonly Stream $stream,
        private readonly \Closure $bodyDataCallback,
        private readonly Cancellation $bodyCancellation,
        private readonly \Closure $trailersCallback,
    ) {
        $this->maxHeaderBytes = $request->getHeaderSizeLimit();
        $this->maxBodyBytes = $request->getBodySizeLimit();
    }

    public function getBuffer(): string
    {
        return $this->buffer;
    }

    public function getState(): int
    {
        return $this->state;
    }

    public function buffer(string $data): void
    {
        $this->buffer .= $data;
    }

    /**
     * @throws ParseException
     *
     * @psalm-suppress UnevaluatedCode, UnusedVariable, InvalidReturnType
     */
    public function parse(?string $data = null): ?Response
    {
        if ($data !== null && $data !== '') {
            $this->buffer .= $data;
        }

        if ($this->buffer === '') {
            return null;
        }

        if ($this->complete) {
            throw new ParseException('Can\'t continue parsing, response is already complete', HttpStatus::BAD_REQUEST);
        }

        if (!$this->bodyStarted && \in_array($this->state, [self::BODY_CHUNKS, self::BODY_IDENTITY, self::BODY_IDENTITY_EOF], true)) {
            $this->bodyStarted = true;
            $response = $this->responseRef?->get();
            if ($response) {
                events()->responseBodyStart($this->request, $this->stream, $response);
                $response = null;
            }
        }

        switch ($this->state) {
            case self::AWAITING_HEADERS:
                goto headers;
            case self::BODY_IDENTITY:
                goto body_identity;
            case self::BODY_IDENTITY_EOF:
                goto body_identity_eof;
            case self::BODY_CHUNKS:
                goto body_chunks;
            case self::TRAILERS_START:
                goto trailers_start;
            case self::TRAILERS:
                goto trailers;
        }

        headers:
        {
            $startLineAndHeaders = $this->shiftHeadersFromBuffer();
            if ($startLineAndHeaders === null) {
                return null;
            }

            $startLineEndPos = \strpos($startLineAndHeaders, "\r\n");

            \assert($startLineEndPos !== false);

            $startLine = \substr($startLineAndHeaders, 0, $startLineEndPos);
            $rawHeaders = \substr($startLineAndHeaders, $startLineEndPos + 2);

            if (\preg_match(self::STATUS_LINE_PATTERN, $startLine, $match)) {
                $protocol = $match['protocol'];
                $statusCode = (int) $match['status'];
                $statusReason = \trim($match['reason']);
            } else {
                throw new ParseException('Invalid status line: ' . $startLine, HttpStatus::BAD_REQUEST);
            }

            if (!\in_array($protocol, ['1.0', '1.1'], true)) {
                throw new ParseException('Invalid protocol version: ' . $protocol, HttpStatus::BAD_REQUEST);
            }

            if ($rawHeaders !== '') {
                $headers = $this->parseRawHeaders($rawHeaders);
            } else {
                $headers = [];
            }

            $requestMethod = $this->request->getMethod();
            $skipBody = $statusCode < HttpStatus::OK
                || $statusCode === HttpStatus::NOT_MODIFIED
                || $statusCode === HttpStatus::NO_CONTENT
                || $requestMethod === 'HEAD'
                || $requestMethod === 'CONNECT';

            if ($skipBody) {
                $this->complete = true;
            } elseif ($this->chunkedEncoding) {
                $this->state = self::BODY_CHUNKS;
            } elseif ($this->remainingBodyBytes === null) {
                $this->state = self::BODY_IDENTITY_EOF;
            } elseif ($this->remainingBodyBytes > 0) {
                $this->state = self::BODY_IDENTITY;
            } else {
                $this->complete = true;
            }

            $response = new Response($protocol, $statusCode, $statusReason, [], new ReadableBuffer, $this->request);
            foreach ($headers as [$key, $value]) {
                $response->addHeader($key, $value);
            }

            $this->responseRef = \WeakReference::create($response);

            events()->responseHeaderEnd($this->request, $this->stream, $response);

            return $response;
        }

        body_identity:
        {
            if ($data !== null && $data !== '') {
                $response = $this->responseRef?->get();
                if ($response) {
                    events()->responseBodyProgress($this->request, $this->stream, $response);
                    $response = null;
                }
            }

            $bufferDataSize = \strlen($this->buffer);

            if ($bufferDataSize <= $this->remainingBodyBytes) {
                $chunk = $this->buffer;
                $this->buffer = '';
                $this->remainingBodyBytes -= $bufferDataSize;
                $this->addToBody($chunk);

                if ($this->remainingBodyBytes === 0) {
                    $this->complete = true;
                }

                return null;
            }

            $bodyData = \substr($this->buffer, 0, $this->remainingBodyBytes);
            $this->addToBody($bodyData);
            $this->buffer = \substr($this->buffer, $this->remainingBodyBytes);
            $this->remainingBodyBytes = 0;

            goto complete;
        }

        body_identity_eof:
        {
            if ($data !== null && $data !== '') {
                $response = $this->responseRef?->get();
                if ($response) {
                    events()->responseBodyProgress($this->request, $this->stream, $response);
                    $response = null;
                }
            }

            $this->addToBody($this->buffer);
            $this->buffer = '';
            return null;
        }

        body_chunks:
        {
            if ($data !== null && $data !== '') {
                $response = $this->responseRef?->get();
                if ($response) {
                    events()->responseBodyProgress($this->request, $this->stream, $response);
                    $response = null;
                }
            }

            if ($this->parseChunkedBody()) {
                $this->state = self::TRAILERS_START;
                goto trailers_start;
            }

            return null;
        }

        trailers_start:
        {
            $firstTwoBytes = \substr($this->buffer, 0, 2);

            if ($firstTwoBytes === "" || $firstTwoBytes === "\r") {
                return null;
            }

            if ($firstTwoBytes === "\r\n") {
                $this->buffer = \substr($this->buffer, 2);
                goto complete;
            }

            $this->state = self::TRAILERS;
            goto trailers;
        }

        trailers:
        {
            $trailers = $this->shiftHeadersFromBuffer();
            if ($trailers === null) {
                return null;
            }

            $this->parseTrailers($trailers);
            goto complete;
        }

        complete:
        {
            $response = $this->responseRef?->get();
            if ($response) {
                events()->responseBodyEnd($this->request, $this->stream, $response);
                $response = null;
            }

            $this->complete = true;

            return null;
        }
    }

    public function isComplete(): bool
    {
        return $this->complete;
    }

    /**
     * @throws ParseException
     */
    private function shiftHeadersFromBuffer(): ?string
    {
        $this->buffer = \ltrim($this->buffer, "\r\n");

        if (!$this->headersStarted && $this->buffer !== '') {
            $this->headersStarted = true;
            events()->responseHeaderStart($this->request, $this->stream);
        }

        if ($headersSize = \strpos($this->buffer, "\r\n\r\n")) {
            $headers = \substr($this->buffer, 0, $headersSize + 2);
            $this->buffer = \substr($this->buffer, $headersSize + 4);
        } else {
            $headersSize = \strlen($this->buffer);
            $headers = null;
        }

        if ($this->maxHeaderBytes > 0 && $headersSize > $this->maxHeaderBytes) {
            throw new ParseException(
                "Configured header size exceeded: {$headersSize} bytes received, while the configured " .
                "limit is {$this->maxHeaderBytes} bytes",
                HttpStatus::REQUEST_HEADER_FIELDS_TOO_LARGE,
            );
        }

        return $headers;
    }

    /**
     * @throws ParseException
     */
    private function parseRawHeaders(string $rawHeaders): array
    {
        // Legacy support for folded headers
        if (\strpos($rawHeaders, "\r\n\x20") || \strpos($rawHeaders, "\r\n\t")) {
            $rawHeaders = \preg_replace("/\r\n[\x20\t]++/", ' ', $rawHeaders);
            \assert(\is_string($rawHeaders)); // For Psalm.
        }

        try {
            $headers = Rfc7230::parseHeaderPairs($rawHeaders);
            $headerMap = mapHeaderPairs($headers);
        } catch (InvalidHeaderException $e) {
            throw new ParseException('Invalid headers', HttpStatus::BAD_REQUEST, $e);
        }

        if (isset($headerMap['transfer-encoding'])) {
            $transferEncodings = \explode(',', \strtolower(\implode(',', $headerMap['transfer-encoding'])));
            $transferEncodings = \array_map('trim', $transferEncodings);
            $this->chunkedEncoding = \in_array('chunked', $transferEncodings, true);
        } elseif (!empty($headerMap['content-length'])) {
            if (\count($headerMap['content-length']) > 1) {
                throw new ParseException('Can\'t determine body length, because multiple content-length ' .
                    'headers present in the response', HttpStatus::BAD_REQUEST);
            }

            $contentLength = $headerMap['content-length'][0];

            if (!\preg_match('/^(0|[1-9][0-9]*)$/', $contentLength)) {
                throw new ParseException(
                    'Can\'t determine body length, because the content-length header value is invalid',
                    HttpStatus::BAD_REQUEST,
                );
            }

            $this->remainingBodyBytes = (int) $contentLength;
        }

        return $headers;
    }

    /**
     * Decodes a chunked response body.
     *
     * @return bool Returns {@code true} if the body is complete, otherwise {@code false}.
     *
     * @throws ParseException
     *
     * @psalm-suppress InvalidReturnType
     */
    private function parseChunkedBody(): bool
    {
        if ($this->chunkLengthRemaining !== null) {
            goto decode_chunk;
        }

        determine_chunk_size:
        {
            if (false === ($lineEndPos = \strpos($this->buffer, "\r\n"))) {
                return false;
            }

            if ($lineEndPos === 0) {
                throw new ParseException('Invalid line; hexadecimal chunk size expected', HttpStatus::BAD_REQUEST);
            }

            $line = \substr($this->buffer, 0, $lineEndPos);
            $hex = \strtolower(\trim(\ltrim($line, '0'))) ?: '0';
            $dec = \hexdec($hex);

            if ($hex !== \dechex($dec)) {
                throw new ParseException('Invalid hexadecimal chunk size', HttpStatus::BAD_REQUEST);
            }

            $this->chunkLengthRemaining = $dec;
            $this->buffer = \substr($this->buffer, $lineEndPos + 2);

            if ($this->chunkLengthRemaining === 0) {
                return true;
            }
        }

        decode_chunk:
        {
            $bufferLength = \strlen($this->buffer);

            // These first two (extreme) edge cases prevent errors where the packet boundary ends after
            // the \r and before the \n at the end of a chunk.
            if ($bufferLength === $this->chunkLengthRemaining || $bufferLength === $this->chunkLengthRemaining + 1) {
                return false;
            }

            if ($bufferLength >= $this->chunkLengthRemaining + 2) {
                $chunk = \substr($this->buffer, 0, $this->chunkLengthRemaining);
                $this->buffer = \substr($this->buffer, $this->chunkLengthRemaining + 2);
                $this->chunkLengthRemaining = null;
                $this->addToBody($chunk);

                goto determine_chunk_size;
            }

            /** @noinspection SuspiciousAssignmentsInspection */
            $chunk = $this->buffer;
            $this->buffer = '';
            $this->chunkLengthRemaining -= $bufferLength;
            $this->addToBody($chunk);

            return false;
        }
    }

    /**
     * @throws ParseException
     */
    private function parseTrailers(string $trailers): void
    {
        try {
            $trailers = Rfc7230::parseHeaders($trailers);
        } catch (InvalidHeaderException $e) {
            throw new ParseException('Invalid trailers', HttpStatus::BAD_REQUEST, $e);
        }

        ($this->trailersCallback)($trailers);
    }

    /**
     * @throws ParseException
     */
    private function addToBody(string $data): void
    {
        $length = \strlen($data);
        if (!$length) {
            return;
        }

        $this->bodyBytesConsumed += $length;

        if ($this->maxBodyBytes > 0 && $this->bodyBytesConsumed > $this->maxBodyBytes) {
            throw new ParseException(
                "Configured body size exceeded: {$this->bodyBytesConsumed} bytes received," .
                " while the configured limit is {$this->maxBodyBytes} bytes",
                HttpStatus::PAYLOAD_TOO_LARGE,
            );
        }

        ($this->bodyDataCallback)($data)->await($this->bodyCancellation);
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Connection\Internal;

use Amp\ByteStream\ReadableBuffer;
use Amp\ByteStream\ReadableIterableStream;
use Amp\ByteStream\ResourceStream;
use Amp\ByteStream\StreamException;
use Amp\Cancellation;
use Amp\CancelledException;
use Amp\CompositeCancellation;
use Amp\DeferredCancellation;
use Amp\DeferredFuture;
use Amp\Future;
use Amp\Http\Client\Connection\HttpStream;
use Amp\Http\Client\Connection\Stream;
use Amp\Http\Client\HttpException;
use Amp\Http\Client\Internal\EventInvoker;
use Amp\Http\Client\Internal\Phase;
use Amp\Http\Client\Internal\ResponseBodyStream;
use Amp\Http\Client\InvalidRequestException;
use Amp\Http\Client\Request;
use Amp\Http\Client\Response;
use Amp\Http\Client\SocketException;
use Amp\Http\Client\TimeoutException;
use Amp\Http\Client\Trailers;
use Amp\Http\HPack;
use Amp\Http\Http2\Http2ConnectionException;
use Amp\Http\Http2\Http2Parser;
use Amp\Http\Http2\Http2Processor;
use Amp\Http\Http2\Http2StreamException;
use Amp\Http\HttpStatus;
use Amp\Http\InvalidHeaderException;
use Amp\Pipeline\Queue;
use Amp\Socket\InternetAddress;
use Amp\Socket\Socket;
use League\Uri;
use Revolt\EventLoop;
use function Amp\async;
use function Amp\Http\Client\events;
use function Amp\Http\Client\Internal\normalizeRequestPathWithQuery;

/** @internal */
final class Http2ConnectionProcessor implements Http2Processor
{
    private const PREFACE = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
    private const DEFAULT_MAX_FRAME_SIZE = 1 << 14;
    private const DEFAULT_WINDOW_SIZE = (1 << 16) - 1;
    private const MINIMUM_WINDOW = 512 * 1024;
    private const WINDOW_INCREMENT = 1024 * 1024;
    // Seconds to wait for pong (PING with ACK) frame before closing the connection.
    private const PONG_TIMEOUT = 5;

    /** @var string 64-bit for ping. */
    private string $counter = "aaaaaaaa";

    /** @var Http2Stream[] */
    private array $streams = [];

    private int $serverWindow = self::DEFAULT_WINDOW_SIZE;

    private int $clientWindow = self::DEFAULT_WINDOW_SIZE;

    private int $initialWindowSize = self::DEFAULT_WINDOW_SIZE;

    /** @var int<1, max> */
    private int $frameSizeLimit = self::DEFAULT_MAX_FRAME_SIZE;

    /** @var int Previous stream ID. */
    private int $streamId = -1;

    /** @var int Maximum number of streams that may be opened. Initially unlimited. */
    private int $concurrentStreamLimit = 2147483647;

    /** @var int Currently open or reserved streams. Initially unlimited. */
    private int $remainingStreams = 2147483647;

    private int $reservedStreams = 0;

    private readonly HPack $hpack;

    /** @var DeferredFuture<int>|null */
    private ?DeferredFuture $settings = null;

    private bool $initializeStarted = false;

    private bool $initialized = false;

    private ?string $pongWatcher = null;

    /** @var DeferredFuture<bool>|null */
    private ?DeferredFuture $pongDeferred = null;

    /** @var list<\Closure():void>|null */
    private ?array $onClose = [];

    private bool $hasTimeout = false;

    private bool $hasWriteError = false;

    private ?int $shutdown = null;

    /** @var Queue<string> */
    private readonly Queue $frameQueue;

    public function __construct(
        private readonly Socket $socket,
    ) {
        $this->hpack = new HPack();
        $this->frameQueue = new Queue();
    }

    public function __destruct()
    {
        $this->close();
    }

    public function isInitialized(): bool
    {
        return $this->initialized;
    }

    /**
     * Returns once the connection has been initialized. A stream cannot be obtained from the
     * connection until this method returned.
     */
    public function initialize(Cancellation $cancellation): void
    {
        if ($this->initializeStarted) {
            throw new \Error('Connection may only be initialized once');
        }

        $this->initializeStarted = true;

        if ($this->socket->isClosed()) {
            throw new SocketException('The socket closed before the connection could be initialized');
        }

        $this->settings = new DeferredFuture;
        $future = $this->settings->getFuture();

        EventLoop::queue($this->runReadFiber(...));
        EventLoop::queue($this->runWriteFiber(...));

        try {
            $future->await($cancellation);
        } catch (CancelledException $cancelledException) {
            $exception = new SocketException('Connecting cancelled', 0, $cancelledException);
            $this->shutdown($exception);
            throw $exception;
        }
    }

    /**
     * @param \Closure():void $onClose
     */
    public function onClose(\Closure $onClose): void
    {
        if ($this->onClose === null) {
            EventLoop::queue($onClose);
            return;
        }

        $this->onClose[] = $onClose;
    }

    public function close(): void
    {
        if ($this->shutdown !== null) {
            return;
        }

        $exception = new SocketException(\sprintf(
            "Socket from '%s' to '%s' closed",
            $this->socket->getLocalAddress()->toString(),
            $this->socket->getRemoteAddress()->toString(),
        ));

        $this->shutdown($exception);
    }

    #[\Override]
    public function handlePong(string $data): void
    {
        $this->cancelPongWatcher(true);
        $this->hasTimeout = false;
    }

    #[\Override]
    public function handlePing(string $data): void
    {
        $this->writeFrame(Http2Parser::PING, Http2Parser::ACK, 0, $data)->ignore();
    }

    #[\Override]
    public function handleShutdown(int $lastId, int $error, string $message): void
    {
        $message = \sprintf(
            "Received GOAWAY frame on '%s' from '%s' with error code %d and message '%s'",
            (string) $this->socket->getLocalAddress(),
            (string) $this->socket->getRemoteAddress(),
            $error,
            $message,
        );

        $this->shutdown(new SocketException($message, $error), $lastId);
    }

    #[\Override]
    public function handleStreamWindowIncrement(int $streamId, int $windowSize): void
    {
        $stream = $this->streams[$streamId] ?? null;
        if (!$stream) {
            return;
        }

        if ($stream->clientWindow + $windowSize > 2147483647) {
            $this->handleStreamException(new Http2StreamException(
                "Current window size plus new window exceeds maximum size",
                $streamId,
                Http2Parser::FLOW_CONTROL_ERROR
            ));

            return;
        }

        $stream->clientWindow += $windowSize;

        $this->writeBufferedData($stream)->ignore();
    }

    #[\Override]
    public function handleConnectionWindowIncrement(int $windowSize): void
    {
        if ($this->clientWindow + $windowSize > 2147483647) {
            $this->handleConnectionException(new Http2ConnectionException(
                "Current window size plus new window exceeds maximum size",
                Http2Parser::FLOW_CONTROL_ERROR
            ));

            return;
        }

        $this->clientWindow += $windowSize;

        foreach ($this->streams as $stream) {
            if ($this->clientWindow <= 0) {
                return;
            }

            if ($stream->requestBodyBuffer === '' || $stream->clientWindow <= 0) {
                continue;
            }

            $this->writeBufferedData($stream)->ignore();
        }
    }

    #[\Override]
    public function handleHeaders(int $streamId, array $pseudo, array $headers, bool $streamEnded): void
    {
        foreach ($pseudo as $name => $_value) {
            if (!isset(Http2Parser::KNOWN_RESPONSE_PSEUDO_HEADERS[$name])) {
                $this->handleStreamException(new Http2StreamException(
                    "Invalid pseudo header",
                    $streamId,
                    Http2Parser::PROTOCOL_ERROR
                ));

                return;
            }
        }

        $stream = $this->streams[$streamId] ?? null;
        if (!$stream) {
            return;
        }

        $stream->enableInactivityWatcher();

        $this->hasTimeout = false;

        if (!$stream->responsePending) {
            if ($stream->expectedLength && $stream->received !== $stream->expectedLength) {
                $diff = $stream->expectedLength - $stream->received;
                $this->handleStreamException(new Http2StreamException(
                    "Content length mismatch: " . \abs($diff) . ' bytes ' . ($diff > 0 ? ' missing' : 'too much'),
                    $streamId,
                    Http2Parser::PROTOCOL_ERROR
                ));

                return;
            }

            if (!empty($pseudo)) {
                $this->handleStreamException(new Http2StreamException(
                    "Trailers must not contain pseudo headers",
                    $streamId,
                    Http2Parser::PROTOCOL_ERROR
                ));

                return;
            }

            try {
                // Constructor checks for any disallowed fields
                $parsedTrailers = new Trailers($headers);
            } catch (InvalidHeaderException $exception) {
                $this->handleStreamException(new Http2StreamException(
                    "Disallowed field names in trailer",
                    $streamId,
                    Http2Parser::PROTOCOL_ERROR,
                    $exception
                ));

                return;
            }

            $trailers = $stream->trailers;
            $stream->trailers = null;

            \assert($trailers !== null);

            $trailers->complete($parsedTrailers);

            return;
        }

        events()->responseHeaderStart($stream->request, $stream->stream);

        $status = $pseudo[":status"] ?? null;

        if ($status === null) {
            $this->handleConnectionException(new Http2ConnectionException(
                "No status pseudo header in response",
                Http2Parser::PROTOCOL_ERROR
            ));

            return;
        }

        if (!\preg_match("/^[1-5]\d\d$/", $status)) {
            $this->handleStreamException(new Http2StreamException(
                "Invalid response status code: " . $pseudo[':status'],
                $streamId,
                Http2Parser::PROTOCOL_ERROR
            ));

            return;
        }

        if ($stream->response !== null) {
            $this->handleStreamException(new Http2StreamException(
                "Stream headers already received",
                $streamId,
                Http2Parser::PROTOCOL_ERROR
            ));

            return;
        }

        $status = (int) $status;

        if ($status === HttpStatus::SWITCHING_PROTOCOLS) {
            $this->handleConnectionException(new Http2ConnectionException(
                "Switching Protocols (101) is not part of HTTP/2",
                Http2Parser::PROTOCOL_ERROR
            ));

            return;
        }

        if ($status < 200) {
            $response = new Response(
                '2',
                $status,
                HttpStatus::getReason($status),
                $headers,
                new ReadableBuffer(),
                $stream->request,
            );

            events()->responseHeaderEnd($stream->request, $stream->stream, $response);

            $onInformationalResponse = $stream->request->getInformationalResponseHandler();
            $preResponseResolution = $stream->preResponseResolution;
            if ($onInformationalResponse !== null) {
                $stream->preResponseResolution = async(function () use (
                    $preResponseResolution,
                    $onInformationalResponse,
                    $streamId,
                    $response
                ): void {
                    $preResponseResolution?->await();

                    try {
                        $onInformationalResponse($response);
                    } catch (\Throwable) {
                        $this->handleStreamException(new Http2StreamException(
                            'Informational response handler threw an exception',
                            $streamId,
                            Http2Parser::CANCEL
                        ));
                    }
                });
            }

            return;
        }

        \assert($stream->body !== null && $stream->trailers !== null);
        $trailers = $stream->trailers->getFuture();
        $iterator = $stream->body->iterate();

        $deferredCancellation = new DeferredCancellation;
        $cancellation = $deferredCancellation->getCancellation();
        $body = new ResponseBodyStream(new ReadableIterableStream($iterator), $deferredCancellation);

        $cancellationId = $cancellation->subscribe(
            fn (CancelledException $exception) => $this->releaseStream($streamId, $exception, false),
        );

        $trailers
            ->finally(static fn () => $cancellation->unsubscribe($cancellationId))
            ->ignore();

        $stream->response = $response = new Response(
            '2',
            $status,
            HttpStatus::getReason($status),
            $headers,
            $body,
            $stream->request,
            $trailers,
        );

        events()->responseHeaderEnd($stream->request, $stream->stream, $response);

        \assert($stream->preResponseResolution === null);

        $stream->preResponseResolution = Future::complete();

        \assert($stream->pendingResponse !== null);

        $stream->responsePending = false;
        EventLoop::queue(static function () use ($response, $stream): void {
            try {
                $stream->requestHeaderCompletion->getFuture()->await();
                $stream->preResponseResolution?->await();
                $stream->pendingResponse?->complete($response);
            } catch (\Throwable $e) {
                $stream->pendingResponse?->error($e);
            }

            $stream->preResponseResolution = null;
            $stream->pendingResponse = null;
        });

        $this->increaseConnectionWindow();
        $this->increaseStreamWindow($stream);

        if (isset($headers["content-length"])) {
            if (\count($headers['content-length']) !== 1) {
                $this->handleStreamException(new Http2StreamException(
                    "Multiple content-length header values",
                    $streamId,
                    Http2Parser::PROTOCOL_ERROR
                ));

                return;
            }

            $contentLength = $headers["content-length"][0];
            if (!\preg_match('/^(0|[1-9][0-9]*)$/', $contentLength)) {
                $this->handleStreamException(new Http2StreamException(
                    "Invalid content-length header value",
                    $streamId,
                    Http2Parser::PROTOCOL_ERROR
                ));

                return;
            }

            if ($stream->request->getMethod() !== 'HEAD') {
                $stream->expectedLength = (int) $contentLength;
            }
        }
    }

    #[\Override]
    public function handlePushPromise(int $streamId, int $pushId, array $pseudo, array $headers): void
    {
        if ($pushId % 2 === 1) {
            $this->handleConnectionException(new Http2ConnectionException(
                "Invalid server initiated stream",
                Http2Parser::PROTOCOL_ERROR
            ));

            return;
        }

        foreach ($pseudo as $name => $_value) {
            if (!isset(Http2Parser::KNOWN_REQUEST_PSEUDO_HEADERS[$name])) {
                $this->handleStreamException(new Http2StreamException(
                    "Invalid pseudo header",
                    $pushId,
                    Http2Parser::PROTOCOL_ERROR
                ));

                return;
            }
        }

        if (!isset($pseudo[":method"], $pseudo[":path"], $pseudo[":scheme"], $pseudo[":authority"])
            || isset($headers["connection"])
            || $pseudo[":path"] === ''
            || (isset($headers["te"]) && \implode($headers["te"]) !== "trailers")
        ) {
            $this->handleStreamException(new Http2StreamException(
                "Invalid header values",
                $pushId,
                Http2Parser::PROTOCOL_ERROR
            ));

            return;
        }

        $method = $pseudo[":method"];
        $target = $pseudo[":path"];
        $scheme = $pseudo[":scheme"];
        $host = $pseudo[":authority"];
        $query = null;

        if ($method !== 'GET' && $method !== 'HEAD') {
            $this->handleStreamException(new Http2StreamException(
                "Pushed request method must be a safe method",
                $pushId,
                Http2Parser::PROTOCOL_ERROR
            ));

            return;
        }

        if (!\preg_match("#^([A-Z\d.\-]+|\[[\d:]+])(?::([1-9]\d*))?$#i", $host, $matches)) {
            $this->handleStreamException(new Http2StreamException(
                "Invalid pushed authority (host) name",
                $pushId,
                Http2Parser::PROTOCOL_ERROR
            ));

            return;
        }

        $address = $this->socket->getRemoteAddress();

        $host = $matches[1];
        $port = isset($matches[2]) ? (int) $matches[2] : match (true) {
            $address instanceof InternetAddress => $address->getPort(),
            default => null,
        };

        if (!isset($this->streams[$streamId])) {
            $this->handleStreamException(new Http2StreamException(
                "Parent stream {$streamId} is no longer open",
                $pushId,
                Http2Parser::PROTOCOL_ERROR
            ));

            return;
        }

        $parentStream = $this->streams[$streamId];
        $parentStream->enableInactivityWatcher();

        if (\strcasecmp($host, $parentStream->request->getUri()->getHost()) !== 0) {
            $this->handleStreamException(new Http2StreamException(
                "Authority does not match original request authority",
                $pushId,
                Http2Parser::PROTOCOL_ERROR
            ));

            return;
        }

        if ($position = \strpos($target, "#")) {
            $target = \substr($target, 0, $position);
        }

        if ($position = \strpos($target, "?")) {
            $query = \substr($target, $position + 1);
            $target = \substr($target, 0, $position);
        }

        try {
            $uri = Uri\Http::fromComponents([
                "scheme" => $scheme,
                "host" => $host,
                "port" => $port,
                "path" => $target,
                "query" => $query,
            ]);
        } catch (\Exception $exception) {
            $this->handleConnectionException(new Http2ConnectionException(
                "Invalid push URI: " . $exception->getMessage(),
                Http2Parser::PROTOCOL_ERROR
            ));

            return;
        }

        $request = new Request($uri, $method);
        $request->setHeaders($headers);
        $request->setProtocolVersions(['2']);
        $request->setPushHandler($parentStream->request->getPushHandler());
        $request->setHeaderSizeLimit($parentStream->request->getHeaderSizeLimit());
        $request->setBodySizeLimit($parentStream->request->getBodySizeLimit());
        $request->setInactivityTimeout($parentStream->request->getInactivityTimeout());
        $request->setTransferTimeout($parentStream->request->getTransferTimeout());

        foreach ($parentStream->request->getEventListeners() as $eventListener) {
            $request->addEventListener($eventListener);
        }

        $deferredCancellation = new DeferredCancellation();

        $cancellation = new CompositeCancellation(
            $parentStream->cancellation,
            $deferredCancellation->getCancellation(),
        );

        $stream = new Http2Stream(
            $pushId,
            $request,
            HttpStream::fromStream(
                $parentStream->stream,
                static function () {
                    throw new \Error('Calling Stream::request() on a pushed request is forbidden');
                },
                static function () {
                    // nothing to do
                }
            ),
            $cancellation,
            $this->createStreamTransferWatcher($pushId, $request->getTransferTimeout()),
            $this->createStreamInactivityWatcher($pushId, $request->getInactivityTimeout()),
            self::DEFAULT_WINDOW_SIZE,
            0,
        );

        events()->requestStart($request);
        events()->push($request);
        events()->requestHeaderStart($request, $stream->stream);
        events()->requestHeaderEnd($request, $stream->stream);
        events()->requestBodyStart($request, $stream->stream);
        events()->requestBodyEnd($request, $stream->stream);

        \assert($stream->pendingResponse !== null);

        $stream->pendingResponse->getFuture()
            ->map(function (Response $response) use ($request) {
                $trailers = $response->getTrailers();
                $trailers->map(fn () => events()->requestEnd($request, $response))->ignore();
                $trailers->catch(fn (\Throwable $exception) => events()->requestFailed($request, $exception))->ignore();
            })
            ->catch(fn (\Throwable $exception) => events()->requestFailed($request, $exception))
            ->ignore();

        $stream->dependency = $streamId;

        $this->streams[$pushId] = $stream;

        $stream->requestBodyCompletion->complete();

        if ($parentStream->request->getPushHandler() === null) {
            $stream->pendingResponse?->getFuture()->ignore();
            $this->handleStreamException(new Http2StreamException(
                "Push promise refused",
                $pushId,
                Http2Parser::CANCEL
            ));

            return;
        }

        EventLoop::queue(function () use ($pushId, $deferredCancellation, $stream, $cancellation): void {
            $cancellationId = $cancellation->subscribe(
                fn (CancelledException $exception) => $this->releaseStream($pushId, $exception, false),
            );

            $onPush = $stream->request->getPushHandler();

            try {
                \assert($onPush !== null);
                \assert($stream->pendingResponse !== null);

                $future = $stream->pendingResponse->getFuture()
                    ->finally(static fn () => $cancellation->unsubscribe($cancellationId));

                $onPush($stream->request, $future);
            } catch (HttpException|StreamException|CancelledException $exception) {
                $deferredCancellation->cancel($exception);
            } catch (\Throwable $exception) {
                $deferredCancellation->cancel($exception);
                throw $exception;
            }
        });
    }

    #[\Override]
    public function handlePriority(int $streamId, int $parentId, int $weight): void
    {
        $stream = $this->streams[$streamId] ?? null;
        if (!$stream) {
            return;
        }

        $stream->dependency = $parentId;
        $stream->weight = $weight;
    }

    #[\Override]
    public function handleStreamReset(int $streamId, int $errorCode): void
    {
        if (!isset($this->streams[$streamId])) {
            return;
        }

        $message = match ($errorCode) {
            Http2Parser::GRACEFUL_SHUTDOWN => 'Graceful shutdown',
            Http2Parser::PROTOCOL_ERROR => 'Protocol error',
            Http2Parser::INTERNAL_ERROR => 'Internal error',
            Http2Parser::FLOW_CONTROL_ERROR => 'Flow control error',
            Http2Parser::SETTINGS_TIMEOUT => 'Settings timeout',
            Http2Parser::STREAM_CLOSED => 'Stream closed',
            Http2Parser::FRAME_SIZE_ERROR => 'Frame size error',
            Http2Parser::REFUSED_STREAM => 'Stream refused',
            Http2Parser::CANCEL => 'Stream cancelled',
            Http2Parser::COMPRESSION_ERROR => 'Compression error',
            Http2Parser::CONNECT_ERROR => 'Connect error',
            Http2Parser::ENHANCE_YOUR_CALM => 'Enhance your calm',
            Http2Parser::INADEQUATE_SECURITY => 'Inadequate security',
            Http2Parser::HTTP_1_1_REQUIRED => 'HTTP/1.1 required',
            default => 'Unknown error' . $errorCode
        };

        $this->handleStreamException(new Http2StreamException("Stream closed by server: $message", $streamId, $errorCode));
    }

    #[\Override]
    public function handleStreamException(Http2StreamException $exception): void
    {
        $id = $exception->getStreamId();
        $code = $exception->getCode();

        $exception = new SocketException($exception->getMessage(), $code, $exception);

        $this->releaseStream($id, $exception, $code === Http2Parser::REFUSED_STREAM);
    }

    #[\Override]
    public function handleConnectionException(Http2ConnectionException $exception): void
    {
        $this->shutdown(new SocketException($exception->getMessage(), $exception->getCode(), $exception));
    }

    #[\Override]
    public function handleData(int $streamId, string $data): void
    {
        $length = \strlen($data);

        $this->serverWindow -= $length;

        $this->increaseConnectionWindow();

        $stream = $this->streams[$streamId] ?? null;
        if (!$stream) {
            return;
        }

        $stream->disableInactivityWatcher();

        if (!$stream->body) {
            $this->handleStreamException(new Http2StreamException(
                "Stream headers not complete or body already complete",
                $streamId,
                Http2Parser::PROTOCOL_ERROR
            ));

            return;
        }

        $stream->serverWindow -= $length;
        $stream->received += $length;
        $stream->bufferSize += $length;

        if ($stream->request->getBodySizeLimit() > 0 && $stream->received >= $stream->request->getBodySizeLimit()) {
            $this->handleStreamException(new Http2StreamException(
                "Body size limit exceeded",
                $streamId,
                Http2Parser::CANCEL
            ));

            return;
        }

        if ($stream->expectedLength !== null && $stream->received > $stream->expectedLength) {
            $this->handleStreamException(new Http2StreamException(
                "Body size exceeded content-length in header",
                $streamId,
                Http2Parser::CANCEL
            ));

            return;
        }

        $response = $stream->response;
        \assert($response !== null);

        if (!$stream->bodyStarted) {
            $stream->bodyStarted = true;
            events()->responseBodyStart($stream->request, $stream->stream, $response);
        }

        events()->responseBodyProgress($stream->request, $stream->stream, $response);

        $stream->body?->pushAsync($data)->map(function () use ($streamId, $length): void {
            $stream = $this->streams[$streamId] ?? null;
            // Stream may have closed while waiting for body data to be consumed.
            if (!$stream) {
                return;
            }

            $stream->bufferSize -= $length;
            if ($stream->bufferSize === 0) {
                $stream->enableInactivityWatcher();
            }

            $this->increaseStreamWindow($stream);
        })->ignore();
    }

    #[\Override]
    public function handleSettings(array $settings): void
    {
        foreach ($settings as $setting => $value) {
            $this->applySetting($setting, $value);
        }

        $this->writeFrame(Http2Parser::SETTINGS, Http2Parser::ACK)->ignore();

        if ($this->settings) {
            $this->initialized = true;
            $this->settings->complete($this->remainingStreams);
            $this->settings = null;
        }
    }

    #[\Override]
    public function handleStreamEnd(int $streamId): void
    {
        $stream = $this->streams[$streamId] ?? null;
        if (!$stream) {
            return;
        }

        if ($stream->expectedLength !== null && $stream->received !== $stream->expectedLength) {
            $this->handleStreamException(new Http2StreamException(
                "Body length does not match content-length header",
                $streamId,
                Http2Parser::PROTOCOL_ERROR
            ));

            return;
        }

        \assert($stream->body !== null);

        $stream->body->complete();
        $stream->body = null;

        $response = $stream->response;
        \assert($response !== null);

        if (EventInvoker::getPhase($stream->request) === Phase::ResponseHeaders) {
            events()->responseBodyStart($stream->request, $stream->stream, $response);
        }

        \assert($stream->response !== null);
        events()->responseBodyEnd($stream->request, $stream->stream, $response);

        // Trailers may have been received in handleHeaders(); if not, resolve with an empty set of trailers.
        if ($stream->trailers !== null) {
            $trailers = $stream->trailers;
            $stream->trailers = null;
            $trailers->complete(new Trailers([]));
        }

        $this->releaseStream($streamId, null, false);
    }

    public function isIdle(): bool
    {
        return empty($this->streams) && $this->reservedStreams === 0;
    }

    public function reserveStream(): void
    {
        if ($this->shutdown !== null || $this->hasWriteError || $this->hasTimeout) {
            throw new \Error("Can't reserve stream after shutdown started");
        }

        --$this->remainingStreams;
        ++$this->reservedStreams;
    }

    public function unreserveStream(): void
    {
        ++$this->remainingStreams;
        --$this->reservedStreams;

        \assert($this->remainingStreams <= $this->concurrentStreamLimit);
    }

    public function getRemainingStreams(): int
    {
        if ($this->shutdown !== null || $this->hasWriteError || $this->hasTimeout) {
            return 0;
        }

        return $this->remainingStreams;
    }

    public function request(Request $request, Cancellation $cancellation, Stream $stream): Response
    {
        try {
            if ($this->shutdown !== null) {
                throw new SocketException(\sprintf(
                    "Connection from '%s' to '%s' has already been shut down",
                    $this->socket->getLocalAddress()->toString(),
                    $this->socket->getRemoteAddress()->toString()
                ));
            }

            if ($this->hasTimeout && !$this->ping()->await()) {
                throw new SocketException(\sprintf(
                    "Socket to '%s' missed responding to PINGs",
                    $this->socket->getRemoteAddress()->toString()
                ));
            }

            RequestNormalizer::normalizeRequest($request);

            if ($request->getMethod() === 'CONNECT') {
                throw new HttpException("CONNECT requests are currently not supported on HTTP/2");
            }

            // Remove defunct HTTP/1.x headers.
            $request->removeHeader('host');
            $request->removeHeader('connection');
            $request->removeHeader('keep-alive');
            $request->removeHeader('transfer-encoding');
            $request->removeHeader('upgrade');

            $request->setProtocolVersions(['2']);

            if ($this->socket->isClosed()) {
                throw new SocketException(\sprintf(
                    "Socket to '%s' closed before the request could be sent",
                    $this->socket->getRemoteAddress()->toString()
                ));
            }
        } catch (\Throwable $exception) {
            throw $this->wrapException($exception, "Request initialization failed");
        }

        if ($this->socket instanceof ResourceStream) {
            $this->socket->reference();
        }

        // Assign a stream ID just before sending the first frame so another request cannot send a frame with
        // a higher ID prior to the initial frame of this stream.
        $streamId = $this->streamId += 2; // Client streams should be odd-numbered, starting at 1.

        $this->streams[$streamId] = $http2stream = new Http2Stream(
            id: $streamId,
            request: $request,
            stream: $stream,
            cancellation: $cancellation,
            transferWatcher: $this->createStreamTransferWatcher($streamId, $request->getTransferTimeout()),
            inactivityWatcher: $this->createStreamInactivityWatcher($streamId, $request->getInactivityTimeout()),
            serverWindow: self::DEFAULT_WINDOW_SIZE,
            clientWindow: $this->initialWindowSize,
        );

        $cancellation = $http2stream->cancellation; // Use CompositeCancellation from Http2Stream.

        $cancellationId = $cancellation->subscribe(
            fn (CancelledException $exception) => $this->releaseStream($streamId, $exception, false),
        );

        \assert($http2stream->pendingResponse !== null);
        $responseFuture = $http2stream->pendingResponse->getFuture();

        \assert($http2stream->trailers !== null);
        $http2stream->trailers->getFuture()
            ->finally(static fn () => $cancellation->unsubscribe($cancellationId))
            ->ignore();

        async(function () use ($request, $stream, $http2stream, $cancellation): void {
            events()->requestHeaderStart($request, $stream);

            $body = $request->getBody()->getContent();
            $chunk = $body->read($cancellation);

            $headers = $this->hpack->encode($this->generateHeaders($request));

            $flag = Http2Parser::END_HEADERS;
            if ($chunk === null) {
                $http2stream->ended = true;
                $flag |= Http2Parser::END_STREAM;
            }

            if (\strlen($headers) > $this->frameSizeLimit) {
                $split = \str_split($headers, $this->frameSizeLimit);

                $firstChunk = \array_shift($split);
                $lastChunk = \array_pop($split);
                \assert($lastChunk !== null); // For Psalm.

                $this->writeFrame(Http2Parser::HEADERS, stream: $http2stream->id, data: $firstChunk)->ignore();

                foreach ($split as $headerChunk) {
                    $this->writeFrame(Http2Parser::CONTINUATION, stream: $http2stream->id, data: $headerChunk)->ignore();
                }

                $this->writeFrame(Http2Parser::CONTINUATION, $flag, $http2stream->id, $lastChunk)->await();
            } else {
                $this->writeFrame(Http2Parser::HEADERS, $flag, $http2stream->id, $headers)->await();
            }

            $http2stream->requestHeaderCompletion->complete();

            events()->requestHeaderEnd($request, $stream);

            events()->requestBodyStart($request, $stream);

            if ($chunk === null) {
                $http2stream->requestBodyCompletion->complete();
            } else {
                $writeFuture = Future::complete();
                do {
                    // Wait for prior write to complete if we've buffered too much of the request body.
                    if (\strlen($http2stream->requestBodyBuffer) >= self::DEFAULT_MAX_FRAME_SIZE) {
                        $writeFuture->await($cancellation);
                    }

                    $writeFuture = $this->writeData($http2stream, $chunk);
                    events()->requestBodyProgress($request, $stream);

                    $chunk = $body->read($cancellation);
                } while ($chunk !== null);

                $http2stream->requestBodyCompletion->complete();

                $writeFuture->await($cancellation);
                $this->writeBufferedData($http2stream)->await($cancellation);
            }

            events()->requestBodyEnd($request, $stream);
        })->catch(function (\Throwable $exception) use ($http2stream, $cancellation, $cancellationId): void {
            $cancellation->unsubscribe($cancellationId);

            $exception = $this->wrapException(
                $exception,
                "Failed to write request (stream {$http2stream->id}) to socket",
            );

            if (!$http2stream->requestHeaderCompletion->isComplete()) {
                $http2stream->requestHeaderCompletion->error($exception);
            }

            if (!$http2stream->requestBodyCompletion->isComplete()) {
                $http2stream->requestBodyCompletion->error($exception);
            }

            $this->releaseStream($http2stream->id, $exception, false);
        });

        return $responseFuture->await();
    }

    public function isClosed(): bool
    {
        return $this->shutdown !== null || $this->socket->isClosed();
    }

    private function runReadFiber(): void
    {
        $parser = new Http2Parser($this, $this->hpack);

        try {
            $this->frameQueue->push(Http2Parser::PREFACE);

            $this->writeFrame(
                type: Http2Parser::SETTINGS,
                data: \pack(
                    "nNnNnNnN",
                    Http2Parser::ENABLE_PUSH,
                    1,
                    Http2Parser::MAX_CONCURRENT_STREAMS,
                    256,
                    Http2Parser::INITIAL_WINDOW_SIZE,
                    self::DEFAULT_WINDOW_SIZE,
                    Http2Parser::MAX_FRAME_SIZE,
                    self::DEFAULT_MAX_FRAME_SIZE
                )
            )->await();

            while (null !== $chunk = $this->socket->read()) {
                $parser->push($chunk);
            }
        } catch (\Throwable $exception) {
            $this->shutdown(new SocketException(
                "The HTTP/2 connection from '" . (string) $this->socket->getLocalAddress() . "' to '" .
                (string) $this->socket->getRemoteAddress() . "' closed due to an exception: " .
                $exception->getMessage(),
                Http2Parser::INTERNAL_ERROR,
                $exception,
            ));
        } finally {
            $parser->cancel();

            $this->cancelPongWatcher(false);

            if ($this->onClose !== null) {
                $onClose = $this->onClose;
                $this->onClose = null;

                foreach ($onClose as $callback) {
                    EventLoop::queue($callback);
                }
            }

            $this->frameQueue->complete();

            $this->shutdown();
        }
    }

    private function writeFrame(
        int $type,
        int $flags = Http2Parser::NO_FLAG,
        int $stream = 0,
        string $data = ''
    ): Future {
        if ($this->frameQueue->isComplete()) {
            return Future::complete();
        }

        return $this->frameQueue->pushAsync(Http2Parser::compileFrame($data, $type, $flags, $stream));
    }

    private function applySetting(int $setting, int $value): void
    {
        switch ($setting) {
            case Http2Parser::INITIAL_WINDOW_SIZE:
                if ($value < 0 || $value > 2147483647) { // (1 << 31) - 1
                    $this->handleConnectionException(new Http2ConnectionException(
                        "Invalid window size: {$value}",
                        Http2Parser::FLOW_CONTROL_ERROR
                    ));

                    return;
                }

                $priorWindowSize = $this->initialWindowSize;
                $this->initialWindowSize = $value;
                $difference = $this->initialWindowSize - $priorWindowSize;

                foreach ($this->streams as $stream) {
                    $stream->clientWindow += $difference;
                }

                // Settings ACK should be sent before HEADER or DATA frames.
                if ($difference > 0) {
                    EventLoop::queue(function (): void {
                        foreach ($this->streams as $stream) {
                            if ($this->clientWindow <= 0) {
                                return;
                            }

                            if ($stream->requestBodyBuffer === '' || $stream->clientWindow <= 0) {
                                continue;
                            }

                            try {
                                $this->writeBufferedData($stream)->await();
                            } catch (\Throwable $exception) {
                                $this->shutdown(new SocketException(
                                    'Failed to write to socket',
                                    Http2Parser::CONNECT_ERROR,
                                    $exception
                                ));
                            }
                        }
                    });
                }

                return;

            case Http2Parser::MAX_FRAME_SIZE:
                if ($value < 1 << 14 || $value >= 1 << 24) {
                    $this->handleConnectionException(new Http2ConnectionException(
                        "Invalid maximum frame size: {$value}",
                        Http2Parser::PROTOCOL_ERROR
                    ));

                    return;
                }

                $this->frameSizeLimit = $value;
                return;

            case Http2Parser::MAX_CONCURRENT_STREAMS:
                if ($value < 0 || $value > 2147483647) { // (1 << 31) - 1
                    $this->handleConnectionException(new Http2ConnectionException(
                        "Invalid concurrent streams value: {$value}",
                        Http2Parser::PROTOCOL_ERROR
                    ));

                    return;
                }

                $priorUsedStreams = $this->concurrentStreamLimit - $this->remainingStreams;

                $this->concurrentStreamLimit = $value;
                $this->remainingStreams = $this->concurrentStreamLimit - $priorUsedStreams;

                \assert($this->remainingStreams <= $this->concurrentStreamLimit);

                return;

            case Http2Parser::HEADER_TABLE_SIZE: // TODO Respect this setting from the server
            case Http2Parser::MAX_HEADER_LIST_SIZE: // TODO Respect this setting from the server
            case Http2Parser::ENABLE_PUSH: // No action needed.
            default: // Unknown setting, ignore (6.5.2).
                return;
        }
    }

    private function writeBufferedData(Http2Stream $stream): Future
    {
        if ($stream->ended) {
            return Future::complete();
        }

        $windowSize = \min($this->clientWindow, $stream->clientWindow);
        $length = \strlen($stream->requestBodyBuffer);

        if ($length <= $windowSize) {
            $stream->windowSizeIncrease?->complete();
            $stream->windowSizeIncrease = null;

            $this->clientWindow -= $length;
            $stream->clientWindow -= $length;

            if ($length > $this->frameSizeLimit) {
                $chunks = \str_split($stream->requestBodyBuffer, $this->frameSizeLimit);
                \assert($chunks !== false);

                $stream->requestBodyBuffer = \array_pop($chunks);

                foreach ($chunks as $chunk) {
                    $this->writeFrame(Http2Parser::DATA, Http2Parser::NO_FLAG, $stream->id, $chunk)->ignore();
                }
            }

            if ($stream->requestBodyCompletion->isComplete()) {
                $future = $this->writeFrame(
                    Http2Parser::DATA,
                    Http2Parser::END_STREAM,
                    $stream->id,
                    $stream->requestBodyBuffer
                );

                $stream->ended = true;
            } else {
                $future = $this->writeFrame(
                    Http2Parser::DATA,
                    Http2Parser::NO_FLAG,
                    $stream->id,
                    $stream->requestBodyBuffer
                );
            }

            $stream->requestBodyBuffer = "";
            $stream->enableInactivityWatcher();

            return $future;
        }

        if ($windowSize > 0) {
            // Read next body chunk if less than 8192 bytes will remain in the buffer
            if ($length - 8192 < $windowSize && $stream->windowSizeIncrease) {
                $stream->windowSizeIncrease->complete();
                $stream->windowSizeIncrease = null;
            }

            $data = $stream->requestBodyBuffer;
            $end = $windowSize - $this->frameSizeLimit;

            $stream->clientWindow -= $windowSize;
            $this->clientWindow -= $windowSize;

            for ($off = 0; $off < $end; $off += $this->frameSizeLimit) {
                $this->writeFrame(
                    Http2Parser::DATA,
                    Http2Parser::NO_FLAG,
                    $stream->id,
                    \substr($data, $off, $this->frameSizeLimit)
                )->ignore();
            }

            $future = $this->writeFrame(
                Http2Parser::DATA,
                Http2Parser::NO_FLAG,
                $stream->id,
                \substr($data, $off, $windowSize - $off)
            );

            $stream->requestBodyBuffer = \substr($data, $windowSize);
            $stream->enableInactivityWatcher();

            return $future;
        }

        if ($stream->windowSizeIncrease === null) {
            $stream->windowSizeIncrease = new DeferredFuture;
        }

        return $stream->windowSizeIncrease->getFuture();
    }

    private function releaseStream(int $streamId, ?\Throwable $exception, bool $unprocessed): void
    {
        $stream = $this->streams[$streamId] ?? null;
        if (!$stream) {
            return;
        }

        unset($this->streams[$streamId]);

        if ($streamId & 1) { // Client-initiated stream.
            $this->remainingStreams++;
            $this->reservedStreams--;

            \assert($this->remainingStreams <= $this->concurrentStreamLimit);
        }

        if ($stream->responsePending || $stream->body || $stream->trailers) {
            $exception ??= new SocketException(
                \sprintf("Stream %d closed unexpectedly", $streamId),
                Http2Parser::INTERNAL_ERROR
            );

            $exception = $this->wrapException($exception);

            $stream->responsePending = false;
            $stream->pendingResponse?->error($exception);
            $stream->pendingResponse = null;

            $stream->trailers?->error($exception);
            $stream->trailers = null;

            if (!$exception instanceof CancelledException) {
                $exception = new StreamException(
                    'HTTP response did not complete: ' . $exception->getMessage(),
                    previous: $exception,
                );
            }

            $stream->body?->error($exception);
            $stream->body = null;

            $this->writeFrame(
                Http2Parser::RST_STREAM,
                Http2Parser::NO_FLAG,
                $streamId,
                \pack("N", Http2Parser::CANCEL)
            )->ignore();

            if ($unprocessed) {
                events()->requestRejected($stream->request);
            }
        }

        $stream->cancel();

        if ($this->streams) {
            return;
        }

        if ($this->shutdown !== null) {
            $this->socket->close();
            return;
        }

        if ($this->socket instanceof ResourceStream) {
            $this->socket->unreference();
        }
    }

    /**
     * @return Future<bool> Resolved with true if a pong is received within the timeout, false if none is received.
     */
    private function ping(): Future
    {
        if ($this->onClose === null) {
            return Future::complete(false);
        }

        if ($this->pongDeferred !== null) {
            return $this->pongDeferred->getFuture();
        }

        $this->pongDeferred = $deferred = new DeferredFuture;
        $this->pongWatcher = EventLoop::delay(self::PONG_TIMEOUT, fn () => $this->cancelPongWatcher(false));

        $data = \PHP_VERSION_ID >= 80300
            ? $this->counter = \str_increment($this->counter)
            : $this->counter++;

        $this->writeFrame(Http2Parser::PING, data: $data)->ignore();

        return $deferred->getFuture();
    }

    private function cancelPongWatcher(bool $receivedPong): void
    {
        if ($this->pongWatcher !== null) {
            EventLoop::cancel($this->pongWatcher);
            $this->pongWatcher = null;
        }

        $this->pongDeferred?->complete($receivedPong);
        $this->pongDeferred = null;
    }

    /**
     * @param HttpException|null $reason Shutdown reason.
     * @param int|null $lastId ID of last processed frame if available.
     */
    private function shutdown(?HttpException $reason = null, ?int $lastId = null): void
    {
        if ($this->shutdown !== null) {
            return;
        }

        if ($this->settings !== null) {
            $message = "Connection closed before HTTP/2 settings could be received";
            $this->settings->error(new SocketException($message, 0, $reason));
            $this->settings = null;
        }

        $previous = $reason?->getPrevious();
        $previous = $previous instanceof Http2ConnectionException ? $previous : null;

        $code = $previous?->getCode() ?? Http2Parser::GRACEFUL_SHUTDOWN;

        $this->shutdown = $code;

        if ($lastId === null) {
            $message = match ($code) {
                Http2Parser::PROTOCOL_ERROR,
                Http2Parser::FLOW_CONTROL_ERROR,
                Http2Parser::FRAME_SIZE_ERROR,
                Http2Parser::COMPRESSION_ERROR,
                Http2Parser::SETTINGS_TIMEOUT,
                Http2Parser::ENHANCE_YOUR_CALM => $previous?->getMessage(),
                default => null,
            };

            $this->writeFrame(Http2Parser::GOAWAY, data: \pack('NN', 0, $code) . $message)->ignore();

            foreach ($this->streams as $id => $_stream) {
                $reason ??= $this->makeDefaultShutdownReason();
                $this->releaseStream($id, $reason, unprocessed: false);
            }

            return;
        }

        foreach ($this->streams as $id => $_stream) {
            if ($id <= $lastId) {
                continue;
            }

            $reason ??= $this->makeDefaultShutdownReason();
            $this->releaseStream($id, $reason, unprocessed: true);
        }
    }

    private function makeDefaultShutdownReason(): SocketException
    {
        return new SocketException(
            "The HTTP/2 connection from '" . (string) $this->socket->getLocalAddress() . "' to '"
            . (string) $this->socket->getRemoteAddress() . "' closed unexpectedly",
            Http2Parser::INTERNAL_ERROR,
        );
    }

    /**
     * @psalm-return list<list{string, string}>
     *
     * @throws InvalidRequestException
     */
    private function generateHeaders(Request $request): array
    {
        $uri = $request->getUri();
        $path = normalizeRequestPathWithQuery($request);

        $authority = $uri->getHost();
        if ($port = $uri->getPort()) {
            $authority .= ':' . $port;
        }

        $headers = [
            [":authority", $authority],
            [":path", $path],
            [":scheme", $uri->getScheme()],
            [":method", $request->getMethod()],
        ];

        foreach ($request->getHeaders() as $field => $values) {
            foreach ($values as $value) {
                if ($field === 'te' && $value !== 'trailers') {
                    continue;
                }

                $headers[] = [$field, $value];
            }
        }

        return $headers;
    }

    private function writeData(Http2Stream $stream, string $data): Future
    {
        $stream->requestBodyBuffer .= $data;

        return $this->writeBufferedData($stream);
    }

    private function increaseConnectionWindow(): void
    {
        $increase = 0;

        while ($this->serverWindow <= self::MINIMUM_WINDOW) {
            $this->serverWindow += self::WINDOW_INCREMENT;
            $increase += self::WINDOW_INCREMENT;
        }

        if ($increase > 0) {
            $this->writeFrame(Http2Parser::WINDOW_UPDATE, data: \pack("N", self::WINDOW_INCREMENT))->ignore();
        }
    }

    private function increaseStreamWindow(Http2Stream $stream): void
    {
        $minWindow = \min($stream->request->getBodySizeLimit(), self::MINIMUM_WINDOW);
        $increase = 0;

        while (($stream->serverWindow + $stream->bufferSize) <= $minWindow) {
            $stream->serverWindow += self::WINDOW_INCREMENT;
            $increase += self::WINDOW_INCREMENT;
        }

        if ($increase > 0) {
            $this->writeFrame(
                Http2Parser::WINDOW_UPDATE,
                Http2Parser::NO_FLAG,
                $stream->id,
                \pack("N", self::WINDOW_INCREMENT)
            )->ignore();
        }
    }

    private function createStreamInactivityWatcher(int $streamId, float $timeout): ?string
    {
        return $this->createStreamTimeoutWatcher(
            $streamId,
            $timeout,
            "Inactivity timeout exceeded, more than {$timeout} seconds elapsed from last data received",
        );
    }

    private function createStreamTransferWatcher(int $streamId, float $timeout): ?string
    {
        return $this->createStreamTimeoutWatcher(
            $streamId,
            $timeout,
            "Allowed transfer timeout exceeded, took longer than {$timeout} s",
        );
    }

    private function createStreamTimeoutWatcher(int $streamId, float $timeout, string $message): ?string
    {
        if ($timeout <= 0) {
            return null;
        }

        $watcher = EventLoop::delay($timeout, function () use ($streamId, $message): void {
            \assert(isset($this->streams[$streamId]), 'Stream watcher invoked after stream closed');
            $this->releaseStream($streamId, new TimeoutException($message), false);
        });

        EventLoop::unreference($watcher);

        return $watcher;
    }

    private function runWriteFiber(): void
    {
        try {
            $iterator = $this->frameQueue->iterate();

            while ($iterator->continue()) {
                if (!$this->socket->isWritable()) {
                    $this->hasWriteError = true;
                    $iterator->dispose();
                    return;
                }

                $frame = $iterator->getValue();
                $this->socket->write($frame);
            }
        } catch (\Throwable $exception) {
            $this->hasWriteError = true;

            $this->shutdown(new SocketException(
                "The HTTP/2 connection closed unexpectedly: " . $exception->getMessage(),
                Http2Parser::INTERNAL_ERROR,
                $exception,
            ));
        }
    }

    private function wrapException(\Throwable $exception, ?string $prefix = null): \Throwable
    {
        if ($exception instanceof HttpException || $exception instanceof CancelledException) {
            return $exception;
        }

        $message = $exception->getMessage();
        if ($prefix !== null) {
            $message = "{$prefix}: {$message}";
        }

        return new HttpException($message, 0, $exception);
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Connection\Internal;

use Amp\Cancellation;
use Amp\CompositeCancellation;
use Amp\DeferredCancellation;
use Amp\DeferredFuture;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Future;
use Amp\Http\Client\Connection\Stream;
use Amp\Http\Client\Request;
use Amp\Http\Client\Response;
use Amp\Pipeline\Queue;
use Revolt\EventLoop;

/**
 * Used in Http2ConnectionProcessor.
 *
 * @internal
 * @psalm-suppress PossiblyUnusedProperty
 */
final class Http2Stream
{
    use ForbidSerialization;
    use ForbidCloning;

    public ?Response $response = null;

    public ?DeferredFuture $pendingResponse;

    public ?Future $preResponseResolution = null;

    public bool $responsePending = true;

    public ?Queue $body;

    public bool $bodyStarted = false;

    public ?DeferredFuture $trailers;

    /** @var int Bytes received on the stream. */
    public int $received = 0;

    public int $bufferSize = 0;

    public bool $ended = false;

    public string $requestBodyBuffer = '';

    public readonly DeferredFuture $requestHeaderCompletion;

    public readonly DeferredFuture $requestBodyCompletion;

    /** @var int Integer between 1 and 256 */
    public int $weight = 16;

    public int $dependency = 0;

    public ?int $expectedLength = null;

    public ?DeferredFuture $windowSizeIncrease = null;

    private readonly DeferredCancellation $deferredCancellation;

    public readonly Cancellation $cancellation;

    public function __construct(
        public readonly int $id,
        public readonly Request $request,
        public readonly Stream $stream,
        Cancellation $cancellation,
        public readonly ?string $transferWatcher,
        public readonly ?string $inactivityWatcher,
        public int $serverWindow,
        public int $clientWindow,
    ) {
        $this->pendingResponse = new DeferredFuture();
        $this->requestHeaderCompletion = new DeferredFuture();
        $this->requestBodyCompletion = new DeferredFuture();
        $this->body = new Queue();

        $this->deferredCancellation = new DeferredCancellation();
        $this->cancellation = new CompositeCancellation($cancellation, $this->deferredCancellation->getCancellation());

        // Trailers future may never be exposed to the user if the request fails, so ignore.
        $this->trailers = new DeferredFuture();
        $this->trailers->getFuture()->ignore();
    }

    public function cancel(): void
    {
        $this->deferredCancellation->cancel();
    }

    public function __destruct()
    {
        if ($this->transferWatcher !== null) {
            EventLoop::cancel($this->transferWatcher);
        }

        if ($this->inactivityWatcher !== null) {
            EventLoop::cancel($this->inactivityWatcher);
        }

        $this->deferredCancellation->cancel();

        // Setting these to null due to PHP's random destruct order on shutdown to avoid errors from double completion.
        $this->pendingResponse = null;
        $this->body = null;
        $this->trailers = null;
    }

    public function disableInactivityWatcher(): void
    {
        if ($this->inactivityWatcher === null) {
            return;
        }

        EventLoop::disable($this->inactivityWatcher);
    }

    public function enableInactivityWatcher(): void
    {
        if ($this->inactivityWatcher === null) {
            return;
        }

        $watcher = $this->inactivityWatcher;

        EventLoop::disable($watcher);
        EventLoop::enable($watcher);
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Connection\Internal;

use Amp\Http\Client\HttpException;
use Amp\Http\Client\Request;

/** @internal */
final class RequestNormalizer
{
    /**
     * @throws HttpException
     */
    public static function normalizeRequest(Request $request): void
    {
        if (!$request->hasHeader('host')) {
            // Though servers are supposed to be able to handle standard port names on the end of the
            // Host header some fail to do this correctly. Thankfully PSR-7 recommends to strip the port
            // if it is the standard port for the given scheme.
            $request->setHeader('host', $request->getUri()->withUserInfo('')->getAuthority());
        }

        self::normalizeRequestBodyHeaders($request);

        // Always normalize this as last item, because we need to strip sensitive headers
        self::normalizeTraceRequest($request);
    }

    /**
     * @throws HttpException
     */
    private static function normalizeRequestBodyHeaders(Request $request): void
    {
        $body = $request->getBody();

        $contentType = $body->getContentType();
        if ($contentType !== null) {
            $previousContentType = $request->getHeaderArray('content-type');
            if ($previousContentType !== [] && $previousContentType !== [$contentType]) {
                throw new HttpException('Conflicting content type headers in request and request body: ' . \implode(', ', $previousContentType) . ' / ' . $contentType);
            }

            $request->setHeader('content-type', $contentType);
        }

        if ($request->hasHeader("transfer-encoding")) {
            $request->removeHeader("content-length");

            return;
        }

        $contentLength = $body->getContentLength();
        if ($contentLength === 0 && \in_array($request->getMethod(), ["CONNECT", "GET", "HEAD", "OPTIONS", "CONNECT", "TRACE"], true)) {
            $request->removeHeader('content-length');
            $request->removeHeader('transfer-encoding');
        } elseif ($contentLength !== null) {
            $request->setHeader('content-length', (string) $contentLength);
            $request->removeHeader('transfer-encoding');
        } else {
            $request->removeHeader('content-length');
            $request->setHeader("transfer-encoding", "chunked");
        }
    }

    private static function normalizeTraceRequest(Request $request): void
    {
        if ($request->getMethod() !== 'TRACE') {
            return;
        }

        // https://tools.ietf.org/html/rfc7231#section-4.3.8
        $request->setBody('');

        // Remove all body and sensitive headers
        $request->replaceHeaders([
            "transfer-encoding" => [],
            "content-length" => [],
            "authorization" => [],
            "proxy-authorization" => [],
            "cookie" => [],
        ]);
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Connection;

use Amp\Cancellation;
use Amp\Http\Client\DelegateHttpClient;
use Amp\Http\Client\Request;
use Amp\Http\Client\Response;
use Amp\Socket\SocketAddress;
use Amp\Socket\TlsInfo;

interface Stream extends DelegateHttpClient
{
    /**
     * Executes the request.
     *
     * This method may only be invoked once per instance.
     *
     * The implementation must ensure that events are called on {@see events()} and may use {@see request()} for that.
     *
     * @throws \Error Thrown if this method is called more than once.
     */
    #[\Override]
    public function request(Request $request, Cancellation $cancellation): Response;

    public function getLocalAddress(): SocketAddress;

    public function getRemoteAddress(): SocketAddress;

    public function getTlsInfo(): ?TlsInfo;
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Connection;

use Amp\Cancellation;
use Amp\Http\Client\Request;

interface ConnectionPool
{
    /**
     * Reserve a stream for a particular request.
     */
    public function getStream(Request $request, Cancellation $cancellation): Stream;
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Connection;

use Amp\Cancellation;
use Amp\CompositeException;
use Amp\DeferredFuture;
use Amp\ForbidSerialization;
use Amp\Future;
use Amp\Http\Client\Request;
use Amp\Http\Client\Response;
use Revolt\EventLoop;
use function Amp\async;

final class ConnectionLimitingPool implements ConnectionPool
{
    use ForbidSerialization;

    /**
     * Create a connection pool that limits the number of connections per authority.
     *
     * @param int $connectionLimit Maximum number of connections allowed to a single authority.
     */
    public static function byAuthority(int $connectionLimit, ?ConnectionFactory $connectionFactory = null): self
    {
        return new self($connectionLimit, $connectionFactory);
    }

    private static function formatUri(Request $request): string
    {
        $uri = $request->getUri();
        $scheme = $uri->getScheme();

        $isHttps = $scheme === 'https';
        $defaultPort = $isHttps ? 443 : 80;

        $host = $uri->getHost();
        $port = $uri->getPort() ?? $defaultPort;

        $authority = $host . ':' . $port;

        return $scheme . '://' . $authority;
    }

    private ConnectionFactory $connectionFactory;

    /** @var array<string, \ArrayObject<int, Future<Connection>>> */
    private array $connections = [];

    /** @var array<int, Connection> Idle connections, indexed by connection object ID. */
    private array $idleConnections = [];

    /** @var array<int, int> Map of connection object IDs to request counts. */
    private array $activeRequestCounts = [];

    /** @var array<string, array<int, DeferredFuture>> */
    private array $waiting = [];

    /** @var array<string, bool> Map of URIs to flags to wait for potential HTTP/2 connection. */
    private array $waitForPriorConnection = [];

    private int $totalConnectionAttempts = 0;

    private int $totalStreamRequests = 0;

    private int $openConnectionCount = 0;

    private function __construct(private readonly int $connectionLimit, ?ConnectionFactory $connectionFactory = null)
    {
        if ($connectionLimit < 1) {
            throw new \Error('The connection limit must be greater than 0');
        }

        $this->connectionFactory = $connectionFactory ?? new DefaultConnectionFactory();
    }

    public function __clone()
    {
        $this->connections = [];
        $this->totalConnectionAttempts = 0;
        $this->totalStreamRequests = 0;
        $this->openConnectionCount = 0;
    }

    public function getTotalConnectionAttempts(): int
    {
        return $this->totalConnectionAttempts;
    }

    public function getTotalStreamRequests(): int
    {
        return $this->totalStreamRequests;
    }

    public function getOpenConnectionCount(): int
    {
        return $this->openConnectionCount;
    }

    #[\Override]
    public function getStream(Request $request, Cancellation $cancellation): Stream
    {
        $this->totalStreamRequests++;

        $uri = self::formatUri($request);

        [$connection, $stream] = $this->getStreamFor($uri, $request, $cancellation);

        $connectionId = \spl_object_id($connection);
        $this->activeRequestCounts[$connectionId] = ($this->activeRequestCounts[$connectionId] ?? 0) + 1;
        unset($this->idleConnections[$connectionId]);

        $poolRef = \WeakReference::create($this);
        $releaseCallback = static function () use ($poolRef, $connection, $uri): void {
            $pool = $poolRef->get();
            if ($pool) {
                $pool->onReadyConnection($connection, $uri);
            } elseif ($connection->isIdle()) {
                $connection->close();
            }
        };

        return HttpStream::fromStream(
            $stream,
            function (Request $request, Cancellation $cancellation) use (
                $releaseCallback,
                $connection,
                $stream,
                $uri
            ): Response {
                try {
                    $response = $stream->request($request, $cancellation);
                } catch (\Throwable $e) {
                    $this->onReadyConnection($connection, $uri);
                    throw $e;
                }

                $response->getTrailers()->finally($releaseCallback)->ignore();

                return $response;
            },
            $releaseCallback,
        );
    }

    /**
     * @return array{Connection, Stream}
     */
    private function getStreamFor(string $uri, Request $request, Cancellation $cancellation): array
    {
        $isHttps = $request->getUri()->getScheme() === 'https';

        $connections = $this->connections[$uri] ?? [];

        do {
            foreach ($connections as $connectionFuture) {
                \assert($connectionFuture instanceof Future);

                try {
                    if ($isHttps && ($this->waitForPriorConnection[$uri] ?? true)) {
                        // Wait for first successful connection if using a secure connection (maybe we can use HTTP/2).
                        $connection = $connectionFuture->await();
                    } elseif ($connectionFuture->isComplete()) {
                        $connection = $connectionFuture->await();
                    } else {
                        continue;
                    }
                } catch (\Exception $exception) {
                    continue; // Ignore cancellations and errors of other requests.
                }

                \assert($connection instanceof Connection);

                $stream = $this->getStreamFromConnection($connection, $request);

                if ($stream === null) {
                    if (!$this->isAdditionalConnectionAllowed($uri) && $this->isConnectionIdle($connection)) {
                        $connection->close();
                        break;
                    }

                    continue; // No stream available for the given request.
                }

                return [$connection, $stream];
            }

            $deferred = new DeferredFuture;
            $futureFromDeferred = $deferred->getFuture();
            $deferredId = \spl_object_id($deferred);

            $this->waiting[$uri][$deferredId] = $deferred;

            if ($this->isAdditionalConnectionAllowed($uri)) {
                break;
            }

            $connection = $futureFromDeferred->await();

            \assert($connection instanceof Connection);

            $stream = $this->getStreamFromConnection($connection, $request);

            if ($stream === null) {
                continue; // Wait for a different connection to become available.
            }

            return [$connection, $stream];
        } while (true);

        $this->totalConnectionAttempts++;

        $connectionFuture = async($this->connectionFactory->create(...), $request, $cancellation);

        $futureId = \spl_object_id($connectionFuture);
        $this->connections[$uri] ??= new \ArrayObject();
        $this->connections[$uri][$futureId] = $connectionFuture;

        EventLoop::queue(function () use (
            $connectionFuture,
            $uri,
            $futureId,
            $isHttps
        ): void {
            try {
                $connection = $connectionFuture->await();
            } catch (\Throwable) {
                $this->dropConnection($uri, null, $futureId);
                return;
            }

            $connectionId = \spl_object_id($connection);
            $this->openConnectionCount++;

            if ($isHttps) {
                $this->waitForPriorConnection[$uri] = \in_array('2', $connection->getProtocolVersions(), true);
            }

            $poolRef = \WeakReference::create($this);
            $connection->onClose(static function () use ($poolRef, $uri, $connectionId, $futureId): void {
                $pool = $poolRef->get();
                if ($pool) {
                    $pool->openConnectionCount--;
                    $pool->dropConnection($uri, $connectionId, $futureId);
                }
            });
        });

        try {
            // Await both new connection future and deferred to reuse an existing connection.
            $connection = Future\awaitFirst([$connectionFuture, $futureFromDeferred]);
        } catch (CompositeException $exception) {
            [$exception] = $exception->getReasons(); // The first reason is why the connection failed.
            throw $exception;
        } finally {
            $this->removeWaiting($uri, $deferredId); // DeferredFuture no longer needed for this request.
        }

        \assert($connection instanceof Connection);

        $stream = $this->getStreamFromConnection($connection, $request);

        if ($stream === null) {
            // Potentially reused connection did not have an available stream for the given request.
            $connection = $connectionFuture->await(); // Wait for new connection request instead.

            $stream = $this->getStreamFromConnection($connection, $request);

            if ($stream === null) {
                // Other requests used the new connection first, so we need to go around again.
                return $this->getStreamFor($uri, $request, $cancellation);
            }
        }

        return [$connection, $stream];
    }

    private function getStreamFromConnection(Connection $connection, Request $request): ?Stream
    {
        if ($connection->isClosed()) {
            return null; // Connection closed during iteration over available connections.
        }

        if (!\array_intersect($request->getProtocolVersions(), $connection->getProtocolVersions())) {
            return null; // Connection does not support any of the requested protocol versions.
        }

        return $connection->getStream($request);
    }

    private function isAdditionalConnectionAllowed(string $uri): bool
    {
        return \count($this->connections[$uri] ?? []) < $this->connectionLimit;
    }

    private function onReadyConnection(Connection $connection, string $uri): void
    {
        $connectionId = \spl_object_id($connection);
        if (isset($this->activeRequestCounts[$connectionId])) {
            $this->activeRequestCounts[$connectionId]--;

            if ($this->activeRequestCounts[$connectionId] === 0) {
                while (\count($this->idleConnections) > 64) { // not customizable for now
                    $idleConnection = \reset($this->idleConnections);
                    $key = \key($this->idleConnections);
                    unset($this->idleConnections[$key]);
                    $idleConnection->close();
                }

                $this->idleConnections[$connectionId] = $connection;
            }
        }

        if (empty($this->waiting[$uri])) {
            return;
        }

        $deferred = \reset($this->waiting[$uri]);
        $this->removeWaiting($uri, \spl_object_id($deferred));
        $deferred->complete($connection);
    }

    private function isConnectionIdle(Connection $connection): bool
    {
        $connectionId = \spl_object_id($connection);

        \assert(
            !isset($this->activeRequestCounts[$connectionId])
            || $this->activeRequestCounts[$connectionId] >= 0
        );

        return ($this->activeRequestCounts[$connectionId] ?? 0) === 0;
    }

    private function removeWaiting(string $uri, int $deferredId): void
    {
        unset($this->waiting[$uri][$deferredId]);
        if (empty($this->waiting[$uri])) {
            unset($this->waiting[$uri]);
        }
    }

    private function dropConnection(string $uri, ?int $connectionId, int $futureId): void
    {
        unset($this->connections[$uri][$futureId]);
        if ($connectionId !== null) {
            unset($this->activeRequestCounts[$connectionId], $this->idleConnections[$connectionId]);
        }

        if (\count($this->connections[$uri]) === 0) {
            unset($this->connections[$uri], $this->waitForPriorConnection[$uri]);
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client;

final class ParseException extends HttpException
{
    public function __construct(string $message, int $code, ?\Throwable $previousException = null)
    {
        parent::__construct($message, $code, $previousException);
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client;

use Amp\Cancellation;

/**
 * Base HTTP client interface for use in {@see ApplicationInterceptor}.
 *
 * Applications and implementations should depend on {@see HttpClient} instead. The intent of this interface is to
 * allow static analysis tools to find interceptors that forget to pass the cancellation down, because the cancellation
 * is optional.
 *
 * The implementation must ensure that events are called on {@see events()} and may use {@see request()} for that.
 *
 * @see HttpClient
 */
interface DelegateHttpClient
{
    /**
     * Request a specific resource from an HTTP server.
     *
     * @throws HttpException
     */
    public function request(Request $request, Cancellation $cancellation): Response;
}
<?php declare(strict_types=1);

namespace Amp\Http\Client;

use Amp\ByteStream\ReadableStream;

interface HttpContent
{
    /**
     * Multiple calls should return a new stream every time or throw.
     *
     * @throws HttpException
     */
    public function getContent(): ReadableStream;

    /**
     * @throws HttpException
     */
    public function getContentLength(): ?int;

    /**
     * @throws HttpException
     */
    public function getContentType(): ?string;
}
<?php declare(strict_types=1);

namespace Amp\Http\Client;

final class TimeoutException extends HttpException
{
}
<?php declare(strict_types=1);

namespace Amp\Http\Client;

use Amp\ForbidSerialization;
use Amp\Http\Client\Connection\ConnectionPool;
use Amp\Http\Client\Connection\UnlimitedConnectionPool;
use Amp\Http\Client\Interceptor\DecompressResponse;
use Amp\Http\Client\Interceptor\FollowRedirects;
use Amp\Http\Client\Interceptor\ForbidUriUserInfo;
use Amp\Http\Client\Interceptor\RetryRequests;
use Amp\Http\Client\Interceptor\SetRequestHeaderIfUnset;

/**
 * Allows building an HttpClient instance.
 *
 * The builder is the recommended way to build an HttpClient instance.
 */
final class HttpClientBuilder
{
    use ForbidSerialization;

    public static function buildDefault(): HttpClient
    {
        return (new self)->build();
    }

    private ?ForbidUriUserInfo $forbidUriUserInfo;

    private ?RetryRequests $retryInterceptor;

    private ?FollowRedirects $followRedirectsInterceptor;

    private ?SetRequestHeaderIfUnset $defaultUserAgentInterceptor;

    private ?SetRequestHeaderIfUnset $defaultAcceptInterceptor;

    private ?DecompressResponse $defaultCompressionHandler;

    /** @var ApplicationInterceptor[] */
    private array $applicationInterceptors = [];

    /** @var NetworkInterceptor[] */
    private array $networkInterceptors = [];

    /** @var EventListener[] */
    private array $eventListeners = [];

    private ConnectionPool $pool;

    public function __construct()
    {
        $this->pool = new UnlimitedConnectionPool;
        $this->forbidUriUserInfo = new ForbidUriUserInfo;
        $this->followRedirectsInterceptor = new FollowRedirects(10);
        $this->retryInterceptor = new RetryRequests(2);
        $this->defaultAcceptInterceptor = new SetRequestHeaderIfUnset('accept', '*/*');
        $this->defaultUserAgentInterceptor = new SetRequestHeaderIfUnset('user-agent', 'amphp/http-client/5.x');
        $this->defaultCompressionHandler = new DecompressResponse;
    }

    public function build(): HttpClient
    {
        $client = new PooledHttpClient($this->pool);

        foreach ($this->networkInterceptors as $interceptor) {
            $client = $client->intercept($interceptor);
        }

        if ($this->defaultAcceptInterceptor) {
            $client = $client->intercept($this->defaultAcceptInterceptor);
        }

        if ($this->defaultUserAgentInterceptor) {
            $client = $client->intercept($this->defaultUserAgentInterceptor);
        }

        if ($this->defaultCompressionHandler) {
            $client = $client->intercept($this->defaultCompressionHandler);
        }

        foreach ($this->eventListeners as $eventListener) {
            $client = $client->listen($eventListener);
        }

        $applicationInterceptors = $this->applicationInterceptors;

        if ($this->followRedirectsInterceptor) {
            \array_unshift($applicationInterceptors, $this->followRedirectsInterceptor);
        }

        if ($this->forbidUriUserInfo) {
            \array_unshift($applicationInterceptors, $this->forbidUriUserInfo);
        }

        if ($this->retryInterceptor) {
            $applicationInterceptors[] = $this->retryInterceptor;
        }

        foreach (\array_reverse($applicationInterceptors) as $applicationInterceptor) {
            $client = new InterceptedHttpClient($client, $applicationInterceptor, $this->eventListeners);
        }

        return new HttpClient($client, $this->eventListeners);
    }

    /**
     * @param ConnectionPool $pool Connection pool to use.
     */
    public function usingPool(ConnectionPool $pool): self
    {
        $builder = clone $this;
        $builder->pool = $pool;

        return $builder;
    }

    /**
     * @param ApplicationInterceptor $interceptor This interceptor gets added to the interceptor queue, so interceptors
     *                                            are executed in the order given to this method.
     */
    public function intercept(ApplicationInterceptor $interceptor): self
    {
        if ($this->followRedirectsInterceptor !== null && $interceptor instanceof FollowRedirects) {
            throw new \Error('Disable automatic redirect following or use HttpClientBuilder::followRedirects() to customize redirects');
        }

        if ($this->retryInterceptor !== null && $interceptor instanceof RetryRequests) {
            throw new \Error('Disable automatic retries or use HttpClientBuilder::retry() to customize retries');
        }

        $builder = clone $this;
        $builder->applicationInterceptors[] = $interceptor;

        return $builder;
    }

    /**
     * @param NetworkInterceptor $interceptor This interceptor gets added to the interceptor queue, so interceptors
     *                                        are executed in the order given to this method.
     */
    public function interceptNetwork(NetworkInterceptor $interceptor): self
    {
        $builder = clone $this;
        $builder->networkInterceptors[] = $interceptor;

        return $builder;
    }

    /**
     * @param EventListener $eventListener This event listener gets added to the request automatically.
     */
    public function listen(EventListener $eventListener): self
    {
        $builder = clone $this;
        $builder->eventListeners[] = $eventListener;

        return $builder;
    }

    /**
     * @param int $retryLimit Maximum number of times a request may be retried. Only certain requests will be retried
     *                        automatically (GET, HEAD, PUT, and DELETE requests are automatically retried, or any
     *                        request that was indicated as unprocessed by the connection).
     */
    public function retry(int $retryLimit): self
    {
        $builder = clone $this;

        if ($retryLimit <= 0) {
            $builder->retryInterceptor = null;
        } else {
            $builder->retryInterceptor = new RetryRequests($retryLimit);
        }

        return $builder;
    }

    /**
     * @param int $limit Maximum number of redirects to follow. The client will automatically request the URI supplied
     *                   by a redirect response (3xx status codes) and returns that response instead.
     */
    public function followRedirects(int $limit = 10): self
    {
        $builder = clone $this;

        if ($limit <= 0) {
            $builder->followRedirectsInterceptor = null;
        } else {
            $builder->followRedirectsInterceptor = new FollowRedirects($limit);
        }

        return $builder;
    }

    /**
     * Removes the default restriction of user:password in request URIs.
     */
    public function allowDeprecatedUriUserInfo(): self
    {
        $builder = clone $this;
        $builder->forbidUriUserInfo = null;

        return $builder;
    }

    /**
     * Doesn't automatically set an 'accept' header.
     */
    public function skipDefaultAcceptHeader(): self
    {
        $builder = clone $this;
        $builder->defaultAcceptInterceptor = null;

        return $builder;
    }

    /**
     * Doesn't automatically set a 'user-agent' header.
     */
    public function skipDefaultUserAgent(): self
    {
        $builder = clone $this;
        $builder->defaultUserAgentInterceptor = null;

        return $builder;
    }

    /**
     * Doesn't automatically set an 'accept-encoding' header and decompress the response.
     */
    public function skipAutomaticCompression(): self
    {
        $builder = clone $this;
        $builder->defaultCompressionHandler = null;

        return $builder;
    }

    final protected function __clone()
    {
        // clone is automatically denied to all external calls
        // final protected instead of private to also force denial for all children
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client;

use Amp\ByteStream\ReadableBuffer;
use Amp\ByteStream\ReadableStream;
use Amp\File\Filesystem;
use Amp\File\FilesystemException;
use function Amp\File\read;

final class BufferedContent implements HttpContent
{
    public static function fromString(string $content, ?string $contentType = null): self
    {
        return new self($content, $contentType);
    }

    /**
     * Creates an instance using the given JSON serializable data with the content-type application/json.
     *
     * @param mixed $json Data which may be JSON serialized with {@see json_encode()}.
     */
    public static function fromJson(mixed $json): self
    {
        try {
            return self::fromString(\json_encode($json, flags: \JSON_THROW_ON_ERROR), 'application/json');
        } catch (\JsonException $exception) {
            throw new HttpException(
                'Exception thrown encoding JSON content: ' . $exception->getMessage(),
                previous: $exception,
            );
        }
    }

    public static function fromFile(
        string $path,
        ?string $contentType = null,
    ): self {
        if (!\class_exists(Filesystem::class)) {
            throw new \Error("File request bodies require amphp/file to be installed");
        }

        try {
            return self::fromString(read($path), $contentType);
        } catch (FilesystemException $filesystemException) {
            throw new HttpException('Failed to open file: ' . $path, 0, $filesystemException);
        }
    }

    private function __construct(
        private readonly string $content,
        private readonly ?string $contentType,
    ) {
    }

    #[\Override]
    public function getContent(): ReadableStream
    {
        return new ReadableBuffer($this->content);
    }

    #[\Override]
    public function getContentLength(): int
    {
        return \strlen($this->content);
    }

    #[\Override]
    public function getContentType(): ?string
    {
        return $this->contentType;
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client;

use Amp\Cancellation;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;

final class InterceptedHttpClient implements DelegateHttpClient
{
    use ForbidCloning;
    use ForbidSerialization;

    private static \WeakMap $requestInterceptors;

    /**
     * @param EventListener[] $eventListeners
     */
    public function __construct(
        private readonly DelegateHttpClient $httpClient,
        private readonly ApplicationInterceptor $interceptor,
        private readonly array $eventListeners,
    ) {
    }

    #[\Override]
    public function request(Request $request, Cancellation $cancellation): Response
    {
        return processRequest($request, $this->eventListeners, function () use ($request, $cancellation) {
            /** @psalm-suppress RedundantPropertyInitializationCheck */
            self::$requestInterceptors ??= new \WeakMap();

            $requestInterceptors = self::$requestInterceptors[$request] ?? [];
            $requestInterceptors[] = $this->interceptor;
            self::$requestInterceptors[$request] = $requestInterceptors;

            events()->applicationInterceptorStart($request, $this->interceptor);

            $response = $this->interceptor->request($request, $cancellation, $this->httpClient);

            events()->applicationInterceptorEnd($request, $this->interceptor, $response);

            return $response;
        });
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client;

class HttpException extends \Exception
{
}
<?php declare(strict_types=1);

namespace Amp\Http\Client;

use Amp\Cancellation;
use Amp\Http\Client\Connection\Stream;

/**
 * Allows intercepting an HTTP request after the connection to the remote server has been established.
 */
interface NetworkInterceptor
{
    /**
     * Intercepts an HTTP request after the connection to the remote server has been established.
     *
     * The implementation might modify the request and/or modify the response after `$stream->request(...)` returned.
     *
     * A NetworkInterceptor SHOULD NOT short-circuit and SHOULD delegate to the `$stream` passed as third argument
     * exactly once. The only exception to this is throwing an exception, e.g. because the TLS settings used are
     * unacceptable. If you need short circuits, use an {@see ApplicationInterceptor} instead.
     */
    public function requestViaNetwork(Request $request, Cancellation $cancellation, Stream $stream): Response;
}
<?php declare(strict_types=1);

namespace Amp\Http\Client;

use Amp\ByteStream\ReadableStream;
use Amp\File\Filesystem;
use Amp\File\FilesystemException;
use function Amp\File\getSize;
use function Amp\File\openFile;

final class StreamedContent implements HttpContent
{
    public static function fromStream(ReadableStream $content, ?int $contentLength = null, ?string $contentType = null): self
    {
        return new self($content, $contentLength, $contentType);
    }

    /**
     * @throws HttpException
     */
    public static function fromFile(
        string $path,
        ?string $contentType = null,
    ): self {
        if (!\class_exists(Filesystem::class)) {
            throw new \Error("File request bodies require amphp/file to be installed");
        }

        try {
            return self::fromStream(openFile($path, 'r'), getSize($path), $contentType);
        } catch (FilesystemException $filesystemException) {
            throw new HttpException('Failed to open file: ' . $path, 0, $filesystemException);
        }
    }

    private ?ReadableStream $content;

    private function __construct(
        ReadableStream $content,
        private readonly ?int $contentLength,
        private readonly ?string $contentType,
    ) {
        $this->content = $content;
    }

    #[\Override]
    public function getContent(): ReadableStream
    {
        if ($this->content === null) {
            throw new HttpException('The content has already been streamed and can\'t be streamed again');
        }

        try {
            return $this->content;
        } finally {
            $this->content = null;
        }
    }

    #[\Override]
    public function getContentLength(): ?int
    {
        return $this->contentLength;
    }

    #[\Override]
    public function getContentType(): ?string
    {
        return $this->contentType;
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Internal;

/** @internal */
enum Phase
{
    case Unprocessed;
    case Blocked;
    case Connected;
    case Push;
    case RequestHeaders;
    case RequestBody;
    case ServerProcessing;
    case ResponseHeaders;
    case ResponseBody;
    case Complete;
    case Failed;
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Internal;

use Amp\ByteStream\ReadableStream;
use Amp\ByteStream\ReadableStreamIteratorAggregate;
use Amp\ByteStream\StreamException;
use Amp\Cancellation;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;

/**
 * @internal
 * @implements \IteratorAggregate<int, string>
 */
final class SizeLimitingReadableStream implements ReadableStream, \IteratorAggregate
{
    use ForbidSerialization;
    use ForbidCloning;
    use ReadableStreamIteratorAggregate;

    private int $bytesRead = 0;

    private ?\Throwable $exception = null;

    public function __construct(
        private readonly ReadableStream $source,
        private readonly int $sizeLimit
    ) {
    }

    #[\Override]
    public function read(?Cancellation $cancellation = null): ?string
    {
        if ($this->exception) {
            throw $this->exception;
        }

        $chunk = $this->source->read($cancellation);

        if ($chunk !== null) {
            $this->bytesRead += \strlen($chunk);
            if ($this->bytesRead > $this->sizeLimit) {
                $this->source->close();

                throw $this->exception = new StreamException(
                    "Configured body size exceeded: {$this->bytesRead} bytes received, while the configured limit is {$this->sizeLimit} bytes",
                );
            }
        }

        return $chunk;
    }

    #[\Override]
    public function isReadable(): bool
    {
        return $this->source->isReadable();
    }

    #[\Override]
    public function close(): void
    {
        $this->source->close();
    }

    #[\Override]
    public function isClosed(): bool
    {
        return $this->source->isClosed();
    }

    #[\Override]
    public function onClose(\Closure $onClose): void
    {
        $this->source->onClose($onClose);
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Internal;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;

/** @internal */
final class HarAttributes
{
    use ForbidCloning;
    use ForbidSerialization;

    public const STARTED_DATE_TIME = 'amp.http.client.har.startedDateTime';
    public const SERVER_IP_ADDRESS = 'amp.http.client.har.serverIPAddress';

    public const TIME_START = 'amp.http.client.har.timings.start';
    public const TIME_SSL = 'amp.http.client.har.timings.ssl';
    public const TIME_CONNECT = 'amp.http.client.har.timings.connect';
    public const TIME_SEND = 'amp.http.client.har.timings.send';
    public const TIME_WAIT = 'amp.http.client.har.timings.wait';
    public const TIME_RECEIVE = 'amp.http.client.har.timings.receive';
    public const TIME_COMPLETE = 'amp.http.client.har.timings.complete';

    public const INCLUDE_CONNECT_TIME = 'amp.http.client.har.timings.connect.include';
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Internal;

use Amp\Http\Client\ApplicationInterceptor;
use Amp\Http\Client\Connection\Connection;
use Amp\Http\Client\Connection\Stream;
use Amp\Http\Client\EventListener;
use Amp\Http\Client\NetworkInterceptor;
use Amp\Http\Client\Request;
use Amp\Http\Client\Response;

/** @internal */
final class EventInvoker implements EventListener
{
    private static self $instance;

    public static function get(): self
    {
        /** @psalm-suppress RedundantPropertyInitializationCheck */
        return self::$instance ??= new self;
    }

    public static function getPhase(Request $request): Phase
    {
        return self::get()->requestPhase[$request] ?? Phase::Unprocessed;
    }

    public static function isRejected(Request $request): bool
    {
        return self::get()->requestRejected[$request] ?? false;
    }

    private \WeakMap $requestPhase;
    private \WeakMap $requestRejected;

    public function __construct()
    {
        $this->requestPhase = new \WeakMap();
        $this->requestRejected = new \WeakMap();
    }

    private function invoke(Request $request, \Closure $closure): void
    {
        foreach ($request->getEventListeners() as $eventListener) {
            $closure($eventListener, $request);
        }
    }

    #[\Override]
    public function requestStart(Request $request): void
    {
        if (self::isRejected($request)) {
            throw new \Error('Request has been rejected by the server. Use a new request for retries.');
        }

        $previousPhase = self::getPhase($request);
        if ($previousPhase !== Phase::Unprocessed) {
            throw new \Error('Invalid request phase transition from ' . $previousPhase->name . ' to Blocked');
        }

        $this->requestPhase[$request] = Phase::Blocked;

        $this->invoke($request, fn (EventListener $eventListener) => $eventListener->requestStart($request));
    }

    #[\Override]
    public function requestFailed(Request $request, \Throwable $exception): void
    {
        $previousPhase = self::getPhase($request);
        if (\in_array($previousPhase, [Phase::Complete, Phase::Failed], true)) {
            throw new \Error('Invalid request phase transition from ' . $previousPhase->name . ' to Failed');
        }

        $this->requestPhase[$request] = Phase::Failed;

        $this->invoke($request, fn (EventListener $eventListener) => $eventListener->requestFailed($request, $exception));
    }

    #[\Override]
    public function requestEnd(Request $request, Response $response): void
    {
        $previousPhase = self::getPhase($request);
        if (!\in_array($previousPhase, [Phase::Blocked, Phase::ResponseHeaders, Phase::ResponseBody], true)) {
            throw new \Error('Invalid request phase transition from ' . $previousPhase->name . ' to Complete');
        }

        $this->requestPhase[$request] = Phase::Complete;

        $this->invoke($request, fn (EventListener $eventListener) => $eventListener->requestEnd($request, $response));
    }

    #[\Override]
    public function requestRejected(Request $request): void
    {
        $this->requestRejected[$request] = true;

        $this->invoke($request, fn (EventListener $eventListener) => $eventListener->requestRejected($request));
    }

    #[\Override]
    public function connectionAcquired(Request $request, Connection $connection, int $streamCount): void
    {
        $previousPhase = self::getPhase($request);
        if ($previousPhase !== Phase::Blocked) {
            throw new \Error('Invalid request phase transition from ' . $previousPhase->name . ' to Connected');
        }

        $this->requestPhase[$request] = Phase::Connected;

        $this->invoke($request, fn (EventListener $eventListener) => $eventListener->connectionAcquired($request, $connection, $streamCount));
    }

    #[\Override]
    public function push(Request $request): void
    {
        $previousPhase = self::getPhase($request);
        if ($previousPhase !== Phase::Blocked) {
            throw new \Error('Invalid request phase transition from ' . $previousPhase->name . ' to Push');
        }

        $this->requestPhase[$request] = Phase::Push;

        $this->invoke($request, fn (EventListener $eventListener) => $eventListener->push($request));
    }

    #[\Override]
    public function requestHeaderStart(Request $request, Stream $stream): void
    {
        if (self::isRejected($request)) {
            throw new \Error('Request has been rejected by the server. Use a new request for retries.');
        }

        $previousPhase = self::getPhase($request);
        if ($previousPhase !== Phase::Connected && $previousPhase !== Phase::Push) {
            throw new \Error('Invalid request phase transition from ' . $previousPhase->name . ' to RequestHeaders');
        }

        $this->requestPhase[$request] = Phase::RequestHeaders;

        $this->invoke($request, fn (EventListener $eventListener) => $eventListener->requestHeaderStart($request, $stream));
    }

    #[\Override]
    public function requestHeaderEnd(Request $request, Stream $stream): void
    {
        $previousPhase = self::getPhase($request);
        if ($previousPhase !== Phase::RequestHeaders) {
            throw new \Error('Invalid request phase: ' . $previousPhase->name);
        }

        $this->invoke($request, fn (EventListener $eventListener) => $eventListener->requestHeaderEnd($request, $stream));
    }

    #[\Override]
    public function requestBodyStart(Request $request, Stream $stream): void
    {
        $previousPhase = self::getPhase($request);
        if ($previousPhase !== Phase::RequestHeaders) {
            throw new \Error('Invalid request phase transition from ' . $previousPhase->name . ' to RequestBody');
        }

        $this->requestPhase[$request] = Phase::RequestBody;

        $this->invoke($request, fn (EventListener $eventListener) => $eventListener->requestBodyStart($request, $stream));
    }

    #[\Override]
    public function requestBodyProgress(Request $request, Stream $stream): void
    {
        $previousPhase = self::getPhase($request);
        if (!\in_array($previousPhase, [
            Phase::RequestBody,
            Phase::ServerProcessing,
            Phase::ResponseHeaders,
            Phase::ResponseBody,
        ], true)) {
            throw new \Error('Invalid request phase: ' . $previousPhase->name);
        }

        $this->invoke($request, fn (EventListener $eventListener) => $eventListener->requestBodyProgress($request, $stream));
    }

    #[\Override]
    public function requestBodyEnd(Request $request, Stream $stream): void
    {
        $previousPhase = self::getPhase($request);
        if (!\in_array($previousPhase, [
            Phase::RequestBody,
            Phase::ServerProcessing,
            Phase::ResponseHeaders,
            Phase::ResponseBody,
        ], true)) {
            throw new \Error('Invalid request phase transition from ' . $previousPhase->name . ' to ServerProcessing');
        }

        if ($previousPhase === Phase::RequestBody) {
            $this->requestPhase[$request] = Phase::ServerProcessing;
        }

        $this->invoke($request, fn (EventListener $eventListener) => $eventListener->requestBodyEnd($request, $stream));
    }

    #[\Override]
    public function responseHeaderStart(Request $request, Stream $stream): void
    {
        $previousPhase = self::getPhase($request);
        if (!\in_array($previousPhase, [
            Phase::RequestHeaders,
            Phase::RequestBody,
            Phase::ServerProcessing,
            Phase::ResponseHeaders,
        ], true)) {
            throw new \Error('Invalid request phase transition from ' . $previousPhase->name . ' to ResponseHeaders');
        }

        $this->requestPhase[$request] = Phase::ResponseHeaders;

        $this->invoke($request, fn (EventListener $eventListener) => $eventListener->responseHeaderStart($request, $stream));
    }

    #[\Override]
    public function responseHeaderEnd(Request $request, Stream $stream, Response $response): void
    {
        $previousPhase = self::getPhase($request);
        if ($previousPhase !== Phase::ResponseHeaders) {
            throw new \Error('Invalid request phase: ' . $previousPhase->name);
        }

        $this->invoke($request, fn (EventListener $eventListener) => $eventListener->responseHeaderEnd($request, $stream, $response));
    }

    #[\Override]
    public function responseBodyStart(Request $request, Stream $stream, Response $response): void
    {
        $previousPhase = self::getPhase($request);
        if ($previousPhase !== Phase::ResponseHeaders) {
            throw new \Error('Invalid request phase transition from ' . $previousPhase->name . ' to ResponseBody');
        }

        $this->requestPhase[$request] = Phase::ResponseBody;

        $this->invoke($request, fn (EventListener $eventListener) => $eventListener->responseBodyStart($request, $stream, $response));
    }

    #[\Override]
    public function responseBodyProgress(Request $request, Stream $stream, Response $response): void
    {
        $previousPhase = self::getPhase($request);
        if ($previousPhase !== Phase::ResponseBody) {
            throw new \Error('Invalid request phase: ' . $previousPhase->name);
        }

        $this->invoke($request, fn (EventListener $eventListener) => $eventListener->responseBodyProgress($request, $stream, $response));
    }

    #[\Override]
    public function responseBodyEnd(Request $request, Stream $stream, Response $response): void
    {
        $previousPhase = self::getPhase($request);
        if ($previousPhase !== Phase::ResponseBody) {
            throw new \Error('Invalid request phase: ' . $previousPhase->name);
        }

        $this->invoke($request, fn (EventListener $eventListener) => $eventListener->responseBodyEnd($request, $stream, $response));
    }

    #[\Override]
    public function applicationInterceptorStart(Request $request, ApplicationInterceptor $interceptor): void
    {
        $previousPhase = self::getPhase($request);
        if ($previousPhase === Phase::Unprocessed) {
            throw new \Error('Invalid request phase: ' . $previousPhase->name);
        }

        $this->invoke($request, fn (EventListener $eventListener) => $eventListener->applicationInterceptorStart($request, $interceptor));
    }

    #[\Override]
    public function applicationInterceptorEnd(Request $request, ApplicationInterceptor $interceptor, Response $response): void
    {
        $previousPhase = self::getPhase($request);
        if ($previousPhase === Phase::Unprocessed) {
            throw new \Error('Invalid request phase: ' . $previousPhase->name);
        }

        $this->invoke($request, fn (EventListener $eventListener) => $eventListener->applicationInterceptorEnd($request, $interceptor, $response));
    }

    #[\Override]
    public function networkInterceptorStart(Request $request, NetworkInterceptor $interceptor): void
    {
        $previousPhase = self::getPhase($request);
        if ($previousPhase === Phase::Unprocessed) {
            throw new \Error('Invalid request phase: ' . $previousPhase->name);
        }

        $this->invoke($request, fn (EventListener $eventListener) => $eventListener->networkInterceptorStart($request, $interceptor));
    }

    #[\Override]
    public function networkInterceptorEnd(Request $request, NetworkInterceptor $interceptor, Response $response): void
    {
        $previousPhase = self::getPhase($request);
        if ($previousPhase === Phase::Unprocessed) {
            throw new \Error('Invalid request phase: ' . $previousPhase->name);
        }

        $this->invoke($request, fn (EventListener $eventListener) => $eventListener->networkInterceptorEnd($request, $interceptor, $response));
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Internal;

use Amp\ByteStream\ReadableStream;
use Amp\ByteStream\ReadableStreamIteratorAggregate;
use Amp\Cancellation;
use Amp\DeferredCancellation;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;

/**
 * @internal
 * @implements \IteratorAggregate<int, string>
 */
final class ResponseBodyStream implements ReadableStream, \IteratorAggregate
{
    use ForbidSerialization;
    use ForbidCloning;
    use ReadableStreamIteratorAggregate;

    private bool $successfulEnd = false;

    public function __construct(
        private readonly ReadableStream $body,
        private readonly DeferredCancellation $bodyCancellation
    ) {
    }

    #[\Override]
    public function read(?Cancellation $cancellation = null): ?string
    {
        $chunk = $this->body->read($cancellation);

        if ($chunk === null) {
            $this->successfulEnd = true;
        }

        return $chunk;
    }

    #[\Override]
    public function isReadable(): bool
    {
        return $this->body->isReadable();
    }

    public function __destruct()
    {
        if (!$this->successfulEnd) {
            $this->bodyCancellation->cancel();
        }
    }

    #[\Override]
    public function close(): void
    {
        $this->body->close();
    }

    #[\Override]
    public function isClosed(): bool
    {
        return $this->body->isClosed();
    }

    #[\Override]
    public function onClose(\Closure $onClose): void
    {
        $this->body->onClose($onClose);
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Internal;

use Amp\ByteStream\ReadableStream;
use Amp\Http\Client\HttpContent;
use Amp\Http\Client\HttpException;
use Amp\Http\HttpMessage;

/** @internal */
final class FormField extends HttpMessage implements HttpContent
{
    public function __construct(
        private readonly string $name,
        private readonly HttpContent $content,
        private readonly ?string $filename = null,
    ) {
        $contentType = $content->getContentType();

        if ($this->filename === null) {
            $this->replaceHeaders([
                'Content-Disposition' => "form-data; name=\"{$name}\"",
                'Content-Type' => $contentType === '' || $contentType === null ? [] : $contentType,
            ]);
        } else {
            $this->replaceHeaders([
                'Content-Disposition' => "form-data; name=\"{$name}\"; filename=\"{$filename}\"",
                'Content-Type' => $contentType === '' || $contentType === null ? [] : $contentType,
                'Content-Transfer-Encoding' => 'binary',
            ]);
        }
    }

    public function getName(): string
    {
        return $this->name;
    }

    /**
     * @throws HttpException
     */
    #[\Override]
    public function getContent(): ReadableStream
    {
        return $this->content->getContent();
    }

    /**
     * @throws HttpException
     */
    #[\Override]
    public function getContentLength(): ?int
    {
        return $this->content->getContentLength();
    }

    #[\Override]
    public function getContentType(): ?string
    {
        return $this->content->getContentType();
    }

    public function getFilename(): ?string
    {
        return $this->filename;
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Internal;

use Amp\Http\Client\InvalidRequestException;
use Amp\Http\Client\Request;

/**
 * @throws InvalidRequestException
 *
 * @internal
 */
function normalizeRequestPathWithQuery(Request $request): string
{
    $path = $request->getUri()->getPath();
    $query = $request->getUri()->getQuery();

    if ($path === '') {
        return '/' . ($query !== '' ? '?' . $query : '');
    }

    if ($path[0] !== '/') {
        throw new InvalidRequestException(
            $request,
            'Relative path (' . $path . ') is not allowed in the request URI'
        );
    }

    return $path . ($query !== '' ? '?' . $query : '');
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Interceptor;

use Amp\Http\Client\HttpException;
use Amp\Http\Client\Response;

/**
 * @psalm-suppress ClassMustBeFinal
 */
class TooManyRedirectsException extends HttpException
{
    private Response $response;

    public function __construct(Response $response)
    {
        parent::__construct("There were too many redirects");

        $this->response = $response;
    }

    public function getResponse(): Response
    {
        return $this->response;
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Interceptor;

use Amp\Cancellation;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Http\Client\ApplicationInterceptor;
use Amp\Http\Client\DelegateHttpClient;
use Amp\Http\Client\HttpException;
use Amp\Http\Client\Request;
use Amp\Http\Client\Response;
use League\Uri\Http;
use Psr\Http\Message\UriInterface;

final class MatchOrigin implements ApplicationInterceptor
{
    use ForbidCloning;
    use ForbidSerialization;

    /** @var ApplicationInterceptor[] */
    private readonly array $originMap;

    /**
     * @param ApplicationInterceptor[] $originMap
     *
     * @throws HttpException
     */
    public function __construct(array $originMap, private readonly ?ApplicationInterceptor $default = null)
    {
        $validatedMap = [];
        foreach ($originMap as $origin => $interceptor) {
            if (!$interceptor instanceof ApplicationInterceptor) {
                $type = \get_debug_type($interceptor);
                throw new HttpException('Origin map must be a map from origin to ApplicationInterceptor, got ' . $type);
            }

            $validatedMap[$this->checkOrigin($origin)] = $interceptor;
        }

        $this->originMap = $validatedMap;
    }

    #[\Override]
    public function request(
        Request $request,
        Cancellation $cancellation,
        DelegateHttpClient $httpClient
    ): Response {
        $interceptor = $this->originMap[$this->normalizeOrigin($request->getUri())] ?? $this->default;

        if (!$interceptor) {
            return $httpClient->request($request, $cancellation);
        }

        return $interceptor->request($request, $cancellation, $httpClient);
    }

    /**
     * @throws HttpException
     */
    private function checkOrigin(string $origin): string
    {
        try {
            $originUri = Http::new($origin);
        } catch (\Exception) {
            throw new HttpException("Invalid origin provided: parsing failed: " . $origin);
        }

        if (!\in_array($originUri->getScheme(), ['http', 'https'], true)) {
            throw new HttpException('Invalid origin with unsupported scheme: ' . $origin);
        }

        if ($originUri->getHost() === '') {
            throw new HttpException('Invalid origin without host: ' . $origin);
        }

        if ($originUri->getUserInfo() !== '') {
            throw new HttpException('Invalid origin with user info, which must not be present: ' . $origin);
        }

        if (!\in_array($originUri->getPath(), ['', '/'], true)) {
            throw new HttpException('Invalid origin with path, which must not be present: ' . $origin);
        }

        if ($originUri->getQuery() !== '') {
            throw new HttpException('Invalid origin with query, which must not be present: ' . $origin);
        }

        if ($originUri->getFragment() !== '') {
            throw new HttpException('Invalid origin with fragment, which must not be present: ' . $origin);
        }

        return $this->normalizeOrigin($originUri);
    }

    private function normalizeOrigin(UriInterface $uri): string
    {
        $defaultPort = $uri->getScheme() === 'https' ? 443 : 80;

        return $uri->getScheme() . '://' . $uri->getHost() . ':' . ($uri->getPort() ?? $defaultPort);
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Interceptor;

use Amp\Http\Client\Request;

final class AddRequestHeader extends ModifyRequest
{
    /**
     * @param non-empty-string $headerName
     */
    public function __construct(string $headerName, string ...$headerValues)
    {
        parent::__construct(static function (Request $request) use ($headerName, $headerValues) {
            $request->addHeader($headerName, $headerValues);

            return $request;
        });
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Interceptor;

use Amp\ByteStream\Compression\DecompressingReadableStream;
use Amp\Cancellation;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Http\Client\Connection\Stream;
use Amp\Http\Client\Internal\SizeLimitingReadableStream;
use Amp\Http\Client\NetworkInterceptor;
use Amp\Http\Client\Request;
use Amp\Http\Client\Response;

final class DecompressResponse implements NetworkInterceptor
{
    use ForbidCloning;
    use ForbidSerialization;

    private readonly bool $hasZlib;

    public function __construct()
    {
        $this->hasZlib = \extension_loaded('zlib');
    }

    #[\Override]
    public function requestViaNetwork(
        Request $request,
        Cancellation $cancellation,
        Stream $stream
    ): Response {
        // If a header is manually set, we won't interfere
        if ($request->hasHeader('accept-encoding')) {
            return $stream->request($request, $cancellation);
        }

        $this->addAcceptEncodingHeader($request);

        $request->interceptPush(function (Request $request, Response $response): Response {
            return $this->decompressResponse($response);
        });

        return $this->decompressResponse($stream->request($request, $cancellation));
    }

    private function addAcceptEncodingHeader(Request $request): void
    {
        if ($this->hasZlib) {
            $request->setHeader('Accept-Encoding', 'gzip, deflate, identity');
        }
    }

    private function decompressResponse(Response $response): Response
    {
        if (($encoding = $this->determineCompressionEncoding($response))) {
            $stream = new DecompressingReadableStream($response->getBody(), $encoding);

            $sizeLimit = $response->getRequest()->getBodySizeLimit();
            if ($sizeLimit > 0) {
                $stream = new SizeLimitingReadableStream($stream, $sizeLimit);
            }

            $response->setBody($stream);
            $response->removeHeader('content-encoding');
        }

        return $response;
    }

    private function determineCompressionEncoding(Response $response): int
    {
        if (!$this->hasZlib) {
            return 0;
        }

        if (!$response->hasHeader("content-encoding")) {
            return 0;
        }

        $contentEncoding = $response->getHeader("content-encoding");

        \assert($contentEncoding !== null);

        $contentEncodingHeader = \trim($contentEncoding);

        if (\strcasecmp($contentEncodingHeader, 'gzip') === 0) {
            return \ZLIB_ENCODING_GZIP;
        }

        if (\strcasecmp($contentEncodingHeader, 'deflate') === 0) {
            return \ZLIB_ENCODING_DEFLATE;
        }

        return 0;
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Interceptor;

use Amp\Http\Client\Response;

final class SetResponseHeaderIfUnset extends ModifyResponse
{
    /**
     * @param non-empty-string $headerName
     */
    public function __construct(string $headerName, string $headerValue, string ...$headerValues)
    {
        \array_unshift($headerValues, $headerValue);

        parent::__construct(static function (Response $response) use ($headerName, $headerValues) {
            if (!$response->hasHeader($headerName)) {
                $response->setHeader($headerName, $headerValues);
            }

            return $response;
        });
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Interceptor;

use Amp\Http\Client\Response;

final class RemoveResponseHeader extends ModifyResponse
{
    public function __construct(string $headerName)
    {
        parent::__construct(static function (Response $response) use ($headerName) {
            $response->removeHeader($headerName);

            return $response;
        });
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Interceptor;

use Amp\Http\Client\InvalidRequestException;
use Amp\Http\Client\Request;
use League\Uri\Http;

final class ResolveBaseUri extends ModifyRequest
{
    public function __construct(string $baseUri)
    {
        parent::__construct(
            fn (Request $request) => $request->setUri(
                Http::parse($request->getUri(), $baseUri)
                    ?? throw new InvalidRequestException($request, 'Invalid base URI provided: ' . $baseUri),
            ),
        );
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Interceptor;

use Amp\Http\Client\InvalidRequestException;
use Amp\Http\Client\Request;

final class ForbidUriUserInfo extends ModifyRequest
{
    public function __construct()
    {
        parent::__construct(static function (Request $request) {
            if ($request->getUri()->getUserInfo() !== '') {
                throw new InvalidRequestException(
                    $request,
                    'The user information (username:password) component of URIs has been deprecated '
                    . '(see https://tools.ietf.org/html/rfc3986#section-3.2.1 and https://tools.ietf.org/html/rfc7230#section-2.7.1); '
                    . 'Instead, set an "Authorization" header containing "Basic " . \\base64_encode("username:password"). '
                    . 'If you used HttpClientBuilder, you can use HttpClientBuilder::allowDeprecatedUriUserInfo() to disable this protection. '
                    . 'Doing so is strongly discouraged and you need to be aware of any interceptor using UriInterface::__toString(), which might expose the password in headers or logs.'
                );
            }
        });
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Interceptor;

use Amp\Cancellation;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Http\Client\ApplicationInterceptor;
use Amp\Http\Client\Connection\Stream;
use Amp\Http\Client\DelegateHttpClient;
use Amp\Http\Client\NetworkInterceptor;
use Amp\Http\Client\Request;
use Amp\Http\Client\Response;

class ModifyResponse implements NetworkInterceptor, ApplicationInterceptor
{
    use ForbidCloning;
    use ForbidSerialization;

    /**
     * @param \Closure(Response):Response $mapper
     */
    public function __construct(private readonly \Closure $mapper)
    {
    }

    #[\Override]
    final public function requestViaNetwork(
        Request $request,
        Cancellation $cancellation,
        Stream $stream
    ): Response {
        $response = $stream->request($request, $cancellation);
        $mappedResponse = ($this->mapper)($response);

        \assert($mappedResponse instanceof Response || $mappedResponse === null);

        return $mappedResponse ?? $response;
    }

    #[\Override]
    public function request(
        Request $request,
        Cancellation $cancellation,
        DelegateHttpClient $httpClient
    ): Response {
        $request->interceptPush(fn (Request $request, Response $response) => ($this->mapper)($response));

        $response = $httpClient->request($request, $cancellation);
        $mappedResponse = ($this->mapper)($response);

        \assert($mappedResponse instanceof Response || $mappedResponse === null);

        return $mappedResponse ?? $response;
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Interceptor;

use Amp\Http\Client\Request;

final class SetRequestHeaderIfUnset extends ModifyRequest
{
    /**
     * @param non-empty-string $headerName
     */
    public function __construct(string $headerName, string $headerValue, string ...$headerValues)
    {
        \array_unshift($headerValues, $headerValue);

        parent::__construct(static function (Request $request) use ($headerName, $headerValues) {
            if (!$request->hasHeader($headerName)) {
                $request->setHeader($headerName, $headerValues);
            }

            return $request;
        });
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Interceptor;

use Amp\ByteStream\StreamException;
use Amp\Cancellation;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Http\Client\ApplicationInterceptor;
use Amp\Http\Client\DelegateHttpClient;
use Amp\Http\Client\HttpException;
use Amp\Http\Client\Request;
use Amp\Http\Client\Response;
use League\Uri;
use Psr\Http\Message\UriInterface as PsrUri;

final class FollowRedirects implements ApplicationInterceptor
{
    use ForbidCloning;
    use ForbidSerialization;

    /**
     * Resolves the given path in $locationUri using $baseUri as a base URI. For example, a base URI of
     * http://example.com/example/path and a location path of 'to/resolve' will return a URI of
     * http://example.com/example/to/resolve.
     */
    public static function resolve(PsrUri $baseUri, PsrUri $locationUri): PsrUri
    {
        if ((string) $locationUri === '') {
            return $baseUri;
        }

        if ($locationUri->getScheme() !== '' || $locationUri->getHost() !== '') {
            $resultUri = $locationUri->withPath(self::removeDotSegments($locationUri->getPath()));

            if ($locationUri->getScheme() === '') {
                $resultUri = $resultUri->withScheme($baseUri->getScheme());
            }

            return $resultUri;
        }

        $baseUri = $baseUri->withQuery($locationUri->getQuery());
        $baseUri = $baseUri->withFragment($locationUri->getFragment());

        if ($locationUri->getPath() !== '' && \substr($locationUri->getPath(), 0, 1) === "/") {
            $baseUri = $baseUri->withPath(self::removeDotSegments($locationUri->getPath()));
        } else {
            $baseUri = $baseUri->withPath(self::mergePaths($baseUri->getPath(), $locationUri->getPath()));
        }

        return $baseUri;
    }

    /**
     * @link http://www.apps.ietf.org/rfc/rfc3986.html#sec-5.2.4
     */
    private static function removeDotSegments(string $input): string
    {
        $output = '';
        $patternA = ',^(\.\.?/),';
        $patternB1 = ',^(/\./),';
        $patternB2 = ',^(/\.)$,';
        $patternC = ',^(/\.\./|/\.\.),';
        // $patternD  = ',^(\.\.?)$,';
        $patternE = ',(/*[^/]*),';

        while ($input !== '') {
            \assert(\is_string($input)); // For Psalm.
            \assert(\is_string($output)); // For Psalm.

            if (\preg_match($patternA, $input)) {
                $input = \preg_replace($patternA, '', $input);
            } elseif (\preg_match($patternB1, $input, $match) || \preg_match($patternB2, $input, $match)) {
                $input = \preg_replace(",^" . $match[1] . ",", '/', $input);
            } elseif (\preg_match($patternC, $input, $match)) {
                $input = \preg_replace(',^' . \preg_quote($match[1], ',') . ',', '/', $input);
                $output = \preg_replace(',/([^/]+)$,', '', $output);
            } elseif ($input === '.' || $input === '..') { // pattern D
                $input = '';
            } elseif (\preg_match($patternE, $input, $match)) {
                $initialSegment = $match[1];
                $input = \preg_replace(',^' . \preg_quote($initialSegment, ',') . ',', '', $input, 1);
                $output .= $initialSegment;
            }
        }

        \assert(\is_string($output)); // For Psalm.

        return $output;
    }

    /**
     * @link http://tools.ietf.org/html/rfc3986#section-5.2.3
     */
    private static function mergePaths(string $basePath, string $pathToMerge): string
    {
        if ($pathToMerge === '') {
            return self::removeDotSegments($basePath);
        }

        if ($basePath === '') {
            return self::removeDotSegments('/' . $pathToMerge);
        }

        $parts = \explode('/', $basePath);
        \array_pop($parts);
        $parts[] = $pathToMerge;

        return self::removeDotSegments(\implode('/', $parts));
    }

    private readonly int $maxRedirects;

    private readonly bool $autoReferrer;

    public function __construct(int $limit, bool $autoReferrer = true)
    {
        if ($limit < 1) {
            throw new \Error("Invalid redirection limit: " . $limit);
        }

        $this->maxRedirects = $limit;
        $this->autoReferrer = $autoReferrer;
    }

    #[\Override]
    public function request(
        Request $request,
        Cancellation $cancellation,
        DelegateHttpClient $httpClient
    ): Response {
        // Don't follow redirects on pushes, just store the redirect in cache (if an interceptor is configured)

        $clonedRequest = $this->cloneRequest($request);

        $response = $httpClient->request($request, $cancellation);

        return $this->followRedirects($clonedRequest, $response, $httpClient, $cancellation);
    }

    private function followRedirects(
        Request $clonedRequest,
        Response $response,
        DelegateHttpClient $client,
        Cancellation $cancellation
    ): Response {
        $maxRedirects = $this->maxRedirects;
        $requestNr = 2;

        do {
            $request = $this->updateRequestForRedirect($clonedRequest, $response);
            if ($request === null) {
                return $response;
            }

            $clonedRequest = $this->cloneRequest($request);

            $redirectResponse = $client->request($request, $cancellation);
            $redirectResponse->setPreviousResponse($response);

            $response = $redirectResponse;
        } while (++$requestNr <= $maxRedirects + 1);

        if ($this->getRedirectUri($response) !== null) {
            throw new TooManyRedirectsException($response);
        }

        return $response;
    }

    private function cloneRequest(Request $originalRequest): Request
    {
        $request = clone $originalRequest;
        $request->setMethod('GET');
        $request->removeHeader('transfer-encoding');
        $request->removeHeader('content-length');
        $request->removeHeader('content-type');

        return $request;
    }

    private function updateRequestForRedirect(Request $request, Response $response): ?Request
    {
        $redirectUri = $this->getRedirectUri($response);
        if ($redirectUri === null) {
            return null;
        }

        $originalUri = $response->getRequest()->getUri();
        $isSameHost = $redirectUri->getAuthority() === $originalUri->getAuthority();

        $request->setUri($redirectUri);

        if (!$isSameHost) {
            // Avoid copying headers for security reasons, any interceptor headers will be added again,
            // but application headers will be discarded.
            $request->setHeaders([]);
        }

        if ($this->autoReferrer) {
            $this->assignRedirectRefererHeader($request, $originalUri, $redirectUri);
        }

        $this->discardResponseBody($response);

        return $request;
    }

    /**
     * Clients must not add a Referer header when leaving an unencrypted resource and redirecting to an encrypted
     * resource.
     *
     * @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec15.html#sec15.1.3
     */
    private function assignRedirectRefererHeader(
        Request $request,
        PsrUri $referrerUri,
        PsrUri $followUri
    ): void {
        $referrerIsEncrypted = $referrerUri->getScheme() === 'https';
        $destinationIsEncrypted = $followUri->getScheme() === 'https';

        if (!$referrerIsEncrypted || $destinationIsEncrypted) {
            $request->setHeader('Referer', (string) $referrerUri->withUserInfo('')->withFragment(''));
        } else {
            $request->removeHeader('Referer');
        }
    }

    private function getRedirectUri(Response $response): ?PsrUri
    {
        if (\count($response->getHeaderArray('location')) !== 1) {
            return null;
        }

        $status = $response->getStatus();
        $request = $response->getRequest();
        $method = $request->getMethod();

        if ($method !== 'GET' && \in_array($status, [307, 308], true)) {
            return null;
        }

        // We don't automatically follow:
        // - 300 (Multiple Choices)
        // - 304 (Not Modified)
        // - 305 (Use Proxy)
        if (!\in_array($status, [301, 302, 303, 307, 308], true)) {
            return null;
        }

        try {
            $header = $response->getHeader('location');
            \assert($header !== null); // see check above

            $locationUri = Uri\Http::new($header);
        } catch (\Exception $e) {
            return null;
        }

        return self::resolve($request->getUri(), $locationUri);
    }

    private function discardResponseBody(Response $response): void
    {
        // Discard response body of redirect responses
        $body = $response->getBody();

        try {
            /** @noinspection PhpStatementHasEmptyBodyInspection */
            /** @noinspection LoopWhichDoesNotLoopInspection */
            /** @noinspection MissingOrEmptyGroupStatementInspection */
            while (null !== $body->read()) {
                // discard
            }
        } catch (HttpException|StreamException $e) {
            // ignore streaming errors on previous responses
        } finally {
            unset($body);
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Interceptor;

use Amp\Http\Client\Request;

final class SetRequestHeader extends ModifyRequest
{
    /**
     * @param non-empty-string $headerName
     */
    public function __construct(string $headerName, string $headerValue, string ...$headerValues)
    {
        \array_unshift($headerValues, $headerValue);

        parent::__construct(static function (Request $request) use ($headerName, $headerValues) {
            $request->setHeader($headerName, $headerValues);

            return $request;
        });
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Interceptor;

use Amp\Cancellation;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Http\Client\ApplicationInterceptor;
use Amp\Http\Client\Connection\Stream;
use Amp\Http\Client\DelegateHttpClient;
use Amp\Http\Client\NetworkInterceptor;
use Amp\Http\Client\Request;
use Amp\Http\Client\Response;

class ModifyRequest implements NetworkInterceptor, ApplicationInterceptor
{
    use ForbidCloning;
    use ForbidSerialization;

    /**
     * @param \Closure(Request):(Request|null) $mapper
     */
    public function __construct(private readonly \Closure $mapper)
    {
    }

    #[\Override]
    final public function requestViaNetwork(
        Request $request,
        Cancellation $cancellation,
        Stream $stream
    ): Response {
        $mappedRequest = ($this->mapper)($request);

        \assert($mappedRequest instanceof Request || $mappedRequest === null);

        return $stream->request($mappedRequest ?? $request, $cancellation);
    }

    #[\Override]
    public function request(
        Request $request,
        Cancellation $cancellation,
        DelegateHttpClient $httpClient
    ): Response {
        $mappedRequest = ($this->mapper)($request);

        \assert($mappedRequest instanceof Request || $mappedRequest === null);

        return $httpClient->request($mappedRequest ?? $request, $cancellation);
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Interceptor;

use Amp\Http\Client\Response;

final class SetResponseHeader extends ModifyResponse
{
    /**
     * @param non-empty-string $headerName
     */
    public function __construct(string $headerName, string $headerValue, string ...$headerValues)
    {
        \array_unshift($headerValues, $headerValue);

        parent::__construct(static function (Response $response) use ($headerName, $headerValues) {
            $response->setHeader($headerName, $headerValues);

            return $response;
        });
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Interceptor;

use Amp\Cancellation;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Http\Client\ApplicationInterceptor;
use Amp\Http\Client\DelegateHttpClient;
use Amp\Http\Client\HttpException;
use Amp\Http\Client\Request;
use Amp\Http\Client\Response;

final class RetryRequests implements ApplicationInterceptor
{
    use ForbidCloning;
    use ForbidSerialization;

    public function __construct(private readonly int $retryLimit)
    {
    }

    #[\Override]
    public function request(
        Request $request,
        Cancellation $cancellation,
        DelegateHttpClient $httpClient
    ): Response {
        $attempt = 1;

        do {
            $clonedRequest = clone $request;

            try {
                return $httpClient->request($request, $cancellation);
            } catch (HttpException $exception) {
                if ($request->isIdempotent() || $request->isUnprocessed()) {
                    // Request was deemed retryable by connection, so carry on.
                    $request = $clonedRequest;
                    continue;
                }

                throw $exception;
            }
        } while ($attempt++ <= $this->retryLimit);

        /** @psalm-suppress PossiblyUndefinedVariable */
        throw $exception;
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Interceptor;

use Amp\Http\Client\Response;

final class AddResponseHeader extends ModifyResponse
{
    /**
     * @param non-empty-string $headerName
     */
    public function __construct(string $headerName, string ...$headerValues)
    {
        parent::__construct(static function (Response $response) use ($headerName, $headerValues) {
            $response->addHeader($headerName, $headerValues);

            return $response;
        });
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Interceptor;

use Amp\Http\Client\Request;

final class RemoveRequestHeader extends ModifyRequest
{
    public function __construct(string $headerName)
    {
        parent::__construct(static function (Request $request) use ($headerName) {
            $request->removeHeader($headerName);

            return $request;
        });
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Interceptor;

use Amp\Http\Client\Request;

final class SetRequestTimeout extends ModifyRequest
{
    public function __construct(
        float $tcpConnectTimeout = 10,
        float $tlsHandshakeTimeout = 10,
        float $transferTimeout = 10,
        ?float $inactivityTimeout = null,
    ) {
        parent::__construct(static function (Request $request) use (
            $tcpConnectTimeout,
            $tlsHandshakeTimeout,
            $transferTimeout,
            $inactivityTimeout
        ) {
            $request->setTcpConnectTimeout($tcpConnectTimeout);
            $request->setTlsHandshakeTimeout($tlsHandshakeTimeout);
            $request->setTransferTimeout($transferTimeout);

            if (null !== $inactivityTimeout) {
                $request->setInactivityTimeout($inactivityTimeout);
            }

            return $request;
        });
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client;

final class InvalidRequestException extends HttpException
{
    private Request $request;

    public function __construct(Request $request, string $message, int $code = 0, ?\Throwable $previous = null)
    {
        parent::__construct($message, $code, $previous);

        $this->request = $request;
    }

    public function getRequest(): Request
    {
        return $this->request;
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client;

use Amp\Cancellation;
use Amp\ForbidSerialization;
use Amp\Http\Client\Connection\ConnectionPool;
use Amp\Http\Client\Connection\InterceptedStream;
use Amp\Http\Client\Connection\UnlimitedConnectionPool;

final class PooledHttpClient implements DelegateHttpClient
{
    use ForbidSerialization;

    private ConnectionPool $connectionPool;

    /** @var NetworkInterceptor[] */
    private array $networkInterceptors = [];

    private array $eventListeners = [];

    public function __construct(?ConnectionPool $connectionPool = null)
    {
        $this->connectionPool = $connectionPool ?? new UnlimitedConnectionPool;
    }

    #[\Override]
    public function request(Request $request, Cancellation $cancellation): Response
    {
        return processRequest($request, $this->eventListeners, function () use ($request, $cancellation) {
            $stream = $this->connectionPool->getStream($request, $cancellation);

            foreach (\array_reverse($this->networkInterceptors) as $interceptor) {
                $stream = new InterceptedStream($stream, $interceptor);
            }

            return $stream->request($request, $cancellation);
        });
    }

    /**
     * Adds a network interceptor.
     *
     * Network interceptors are only invoked if the request requires network access, i.e. there's no short-circuit by
     * an application interceptor, e.g. a cache.
     *
     * Whether the given network interceptor will be respected for currently running requests is undefined.
     *
     * Any new requests have to take the new interceptor into account.
     */
    public function intercept(NetworkInterceptor $networkInterceptor): self
    {
        $clone = clone $this;
        $clone->networkInterceptors[] = $networkInterceptor;

        return $clone;
    }

    /**
     * Adds an event listener.
     *
     * Returns a new PooledHttpClient instance with the listener attached.
     */
    public function listen(EventListener $eventListener): self
    {
        $clone = clone $this;
        $clone->eventListeners[] = $eventListener;

        return $clone;
    }

    final protected function __clone()
    {
        // clone is automatically denied to all external calls
        // final protected instead of private to also force denial for all children
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client;

use Amp\Http\Client\Internal\EventInvoker;
use Amp\Http\Client\Internal\Phase;

function events(): EventListener
{
    return EventInvoker::get();
}

/**
 * @param array<EventListener> $eventListeners
 * @param \Closure(Request):Response $requestHandler
 */
function processRequest(Request $request, array $eventListeners, \Closure $requestHandler): Response
{
    if (EventInvoker::getPhase($request) !== Phase::Unprocessed) {
        return $requestHandler($request);
    }

    foreach ($eventListeners as $eventListener) {
        $request->addEventListener($eventListener);
    }

    events()->requestStart($request);

    try {
        $response = $requestHandler($request);
    } catch (\Throwable $exception) {
        events()->requestFailed($request, $exception);

        throw $exception;
    }

    $trailers = $response->getTrailers();

    $responseRef = \WeakReference::create($response);
    $trailers->map(function () use ($request, $responseRef): void {
        $response = $responseRef->get();
        if ($response) {
            events()->requestEnd($request, $response);
        }
    })->ignore();

    $trailers->catch(fn (\Throwable $exception) => events()->requestFailed($request, $exception))->ignore();

    return $response;
}
<?php declare(strict_types=1);

namespace Amp\Http\Client;

use Amp\Http\Client\Connection\Connection;
use Amp\Http\Client\Connection\Stream;

/**
 * Allows listening to more fine granular events than interceptors are able to achieve.
 *
 * All event listener methods might be called multiple times for a single request. The implementing listener is
 * responsible to detect another call, e.g. via attributes in the request.
 */
interface EventListener
{
    /**
     * Called when the request starts being processed by {@see DelegateHttpClient::request()}.
     */
    public function requestStart(Request $request): void;

    /**
     * Called when the request failed due to an exception.
     */
    public function requestFailed(Request $request, \Throwable $exception): void;

    /**
     * Called when the request completed with a complete response.
     */
    public function requestEnd(Request $request, Response $response): void;

    /**
     * Called when the request is rejected by the server and not yet processed.
     */
    public function requestRejected(Request $request): void;

    /**
     * Called before an application interceptor is invoked.
     */
    public function applicationInterceptorStart(Request $request, ApplicationInterceptor $interceptor): void;

    /**
     * Called after an application interceptor is returned.
     */
    public function applicationInterceptorEnd(Request $request, ApplicationInterceptor $interceptor, Response $response): void;

    /**
     * Called before a network interceptor is invoked.
     */
    public function networkInterceptorStart(Request $request, NetworkInterceptor $interceptor): void;

    /**
     * Called after a network interceptor is returned.
     */
    public function networkInterceptorEnd(Request $request, NetworkInterceptor $interceptor, Response $response): void;

    /**
     * Called after the connection for the request has been selected.
     *
     * @param int $streamCount The number of stream objects obtained from that connection so far, initially 1.
     */
    public function connectionAcquired(Request $request, Connection $connection, int $streamCount): void;

    /**
     * Called after the server initiated a push.
     */
    public function push(Request $request): void;

    /**
     * Called before the request headers are sent.
     */
    public function requestHeaderStart(Request $request, Stream $stream): void;

    /**
     * Called after the request headers have been sent.
     */
    public function requestHeaderEnd(Request $request, Stream $stream): void;

    /**
     * Called before the request body is sent.
     */
    public function requestBodyStart(Request $request, Stream $stream): void;

    /**
     * Called when a new chunk of the request body has been sent.
     */
    public function requestBodyProgress(Request $request, Stream $stream): void;

    /**
     * Called after the request body has been completely sent.
     */
    public function requestBodyEnd(Request $request, Stream $stream): void;

    /**
     * Called after the first chunk of the response headers has been received.
     */
    public function responseHeaderStart(Request $request, Stream $stream): void;

    /**
     * Called after the response headers have been completely received.
     */
    public function responseHeaderEnd(Request $request, Stream $stream, Response $response): void;

    /**
     * Called after the first chunk of the response body has been received.
     */
    public function responseBodyStart(Request $request, Stream $stream, Response $response): void;

    /**
     * Called after a new chunk of the response body has been received.
     */
    public function responseBodyProgress(Request $request, Stream $stream, Response $response): void;

    /**
     * Called after the response body has been completely received.
     */
    public function responseBodyEnd(Request $request, Stream $stream, Response $response): void;
}
{
    "name": "amphp/http-client",
    "homepage": "https://amphp.org/http-client",
    "description": "An advanced async HTTP client library for PHP, enabling efficient, non-blocking, and concurrent requests and responses.",
    "keywords": [
        "http",
        "rest",
        "client",
        "concurrent",
        "async",
        "non-blocking"
    ],
    "license": "MIT",
    "authors": [
        {
            "name": "Daniel Lowrey",
            "email": "rdlowrey@gmail.com"
        },
        {
            "name": "Niklas Keller",
            "email": "me@kelunik.com"
        },
        {
            "name": "Aaron Piotrowski",
            "email": "aaron@trowski.com"
        }
    ],
    "require": {
        "php": ">=8.1",
        "revolt/event-loop": "^1",
        "amphp/amp": "^3",
        "amphp/byte-stream": "^2",
        "amphp/hpack": "^3",
        "amphp/http": "^2",
        "amphp/pipeline": "^1",
        "amphp/socket": "^2",
        "amphp/sync": "^2",
        "league/uri": "^7",
        "league/uri-components": "^7",
        "league/uri-interfaces": "^7.1",
        "psr/http-message": "^1 | ^2"
    },
    "require-dev": {
        "ext-json": "*",
        "amphp/file": "^3 | ^4",
        "amphp/phpunit-util": "^3",
        "amphp/php-cs-fixer-config": "^2",
        "phpunit/phpunit": "^9",
        "amphp/http-server": "^3",
        "kelunik/link-header-rfc5988": "^1",
        "psalm/phar": "6.16.1"
    },
    "suggest": {
        "ext-zlib": "Allows using compression for response bodies.",
        "ext-json": "Required for logging HTTP archives",
        "amphp/file": "Required for file request bodies and HTTP archive logging"
    },
    "autoload": {
        "psr-4": {
            "Amp\\Http\\Client\\": "src"
        },
        "files": [
            "src/functions.php",
            "src/Internal/functions.php"
        ]
    },
    "autoload-dev": {
        "psr-4": {
            "Amp\\Http\\Client\\": "test"
        }
    },
    "conflict": {
        "amphp/file": "<3 | >=5"
    },
    "scripts": {
        "check": [
            "@cs",
            "@test"
        ],
        "cs": "PHP_CS_FIXER_IGNORE_ENV=1 php-cs-fixer fix -v --diff --dry-run",
        "cs-fix": "PHP_CS_FIXER_IGNORE_ENV=1 php-cs-fixer fix -v --diff",
        "test": "@php -dzend.assertions=1 -dassert.exception=1 ./vendor/bin/phpunit --coverage-text"
    }
}
<?php declare(strict_types=1);

use Amp\Http\Client\Cookie\CookieInterceptor;
use Amp\Http\Client\Cookie\FileCookieJar;
use Amp\Http\Client\Cookie\LocalCookieJar;
use Amp\Http\Client\HttpClientBuilder;
use Amp\Http\Client\Request;

require __DIR__ . '/../vendor/autoload.php';

$filename = $argv[1] ?? null;

$cookieJar = $filename
    ? new FileCookieJar(__DIR__ . "/{$filename}.cookies")
    : new LocalCookieJar;

$httpClient = (new HttpClientBuilder)
    ->interceptNetwork(new CookieInterceptor($cookieJar))
    ->build();

$firstResponse = $httpClient->request(new Request('https://google.com/'));

$secondResponse = $httpClient->request(new Request('https://google.com/'));

$otherDomainResponse = $httpClient->request(new Request('https://amphp.org/'));

print "== first request cookies ==\r\n";
print implode("\r\n", $firstResponse->getRequest()->getHeaderArray('cookie'));
print "\r\n\r\n";

print "== first response cookies ==\r\n";
print implode("\r\n", $firstResponse->getHeaderArray('set-cookie'));
print "\r\n\r\n";

print "== second request sends cookies back ==\r\n";
print implode("\r\n", $secondResponse->getRequest()->getHeaderArray('cookie'));
print "\r\n\r\n";

print "== other domain request does not send cookies ==\r\n";
print implode("\r\n", $otherDomainResponse->getRequest()->getHeaderArray('cookie'));
<?php

$config = new Amp\CodeStyle\Config;

$config->getFinder()
    ->in(__DIR__ . '/examples')
    ->in(__DIR__ . '/src')
    ->in(__DIR__ . '/test');

$config->setCacheFile(__DIR__ . '/.php_cs.cache');

return $config;
{
    "symbol-whitelist": [
        "null",
        "true",
        "false",
        "static",
        "self",
        "parent",
        "array",
        "string",
        "int",
        "float",
        "bool",
        "iterable",
        "callable",
        "void",
        "object",
        "mixed",
        "Amp\\ByteStream\\splitLines",
        "Amp\\File\\Filesystem",
        "Amp\\File\\filesystem"
    ],
    "php-core-extensions": [
        "Core",
        "date",
        "pcre",
        "Phar",
        "Reflection",
        "SPL",
        "standard"
    ]
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Cookie;

class FileCookieJarTest extends CookieJarTest
{
    protected function createJar(): CookieJar
    {
        return new FileCookieJar(\tempnam(\sys_get_temp_dir(), 'amphp-http-client-cookies-test-'));
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Cookie;

class InMemoryCookieJarTest extends CookieJarTest
{
    protected function createJar(): CookieJar
    {
        return new LocalCookieJar;
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Cookie;

use Amp\PHPUnit\AsyncTestCase;
use Psr\Http\Message\UriInterface as PsrUri;

abstract class CookieTest extends AsyncTestCase
{
    protected function getUri(string $scheme, string $host, string $path): PsrUri
    {
        $uri = $this->createMock(PsrUri::class);
        $uri->method('getScheme')
            ->willReturn(\strtolower($scheme));
        $uri->method('getHost')
            ->willReturn(\strtolower($host));
        $uri->method('getPath')
            ->willReturn($path);

        return $uri;
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Cookie;

use Amp\Http\Client\Connection\DefaultConnectionFactory;
use Amp\Http\Client\Connection\UnlimitedConnectionPool;
use Amp\Http\Client\HttpClient;
use Amp\Http\Client\HttpClientBuilder;
use Amp\Http\Client\Request;
use Amp\Http\Cookie\CookieAttributes;
use Amp\Http\Cookie\ResponseCookie;
use Amp\Http\HttpStatus;
use Amp\Http\Server\DefaultErrorHandler;
use Amp\Http\Server\Driver\DefaultHttpDriverFactory;
use Amp\Http\Server\HttpServer;
use Amp\Http\Server\RequestHandler\ClosureRequestHandler;
use Amp\Http\Server\Response as ServerResponse;
use Amp\Http\Server\SocketHttpServer;
use Amp\Socket;
use Psr\Log\NullLogger;

class ClientCookieTest extends CookieTest
{
    private HttpClient $client;

    private LocalCookieJar $jar;

    private HttpServer $server;

    private string $cookieHeader;

    public function setUp(): void
    {
        parent::setUp();

        $this->jar = new LocalCookieJar;

        $logger = new NullLogger();

        $this->server = SocketHttpServer::createForDirectAccess(
            $logger,
            httpDriverFactory: new DefaultHttpDriverFactory($logger, streamTimeout: 1, connectionTimeout: 1),
        );

        $this->server->expose(new Socket\InternetAddress('127.0.0.1', 0));

        $this->server->start(
            new ClosureRequestHandler(
                fn () => new ServerResponse(HttpStatus::OK, ['set-cookie' => $this->cookieHeader]),
            ),
            new DefaultErrorHandler(),
        );

        $socket = $this->server->getServers()[0] ?? self::fail('No socket servers created by HTTP server');

        $this->client = (new HttpClientBuilder)
            ->usingPool(
                new UnlimitedConnectionPool(
                    new DefaultConnectionFactory(
                        new Socket\StaticSocketConnector($socket->getAddress()->toString(), Socket\socketConnector())
                    ),
                ),
            )
            ->interceptNetwork(new CookieInterceptor($this->jar))
            ->build();
    }

    public function tearDown(): void
    {
        parent::tearDown();

        $this->server->stop();
    }

    /**
     * @dataProvider provideCookieDomainMatchData
     */
    public function testCookieAccepting(ResponseCookie $cookie, string $requestDomain, bool $accept): void
    {
        $this->cookieHeader = (string) $cookie;

        $response = $this->client->request(new Request('http://' . $requestDomain . '/'));
        $response->getBody()->buffer();

        $cookies = $this->jar->getAll();

        if ($accept) {
            $this->assertCount(1, $cookies);
        } else {
            $this->assertSame([], $cookies);
        }

        $this->server->stop();
    }

    public function provideCookieDomainMatchData(): array
    {
        return [
            [
                new ResponseCookie('foo', 'bar', CookieAttributes::empty()->withDomain('.foo.bar.example.com')),
                'foo.bar',
                false,
            ],
            [
                new ResponseCookie('foo', 'bar', CookieAttributes::empty()->withDomain('.example.com')),
                'example.com',
                true,
            ],
            [
                new ResponseCookie('foo', 'bar', CookieAttributes::empty()->withDomain('.example.com')),
                'www.example.com',
                true,
            ],
            [
                new ResponseCookie('foo', 'bar', CookieAttributes::empty()->withDomain('example.com')),
                'example.com',
                true,
            ],
            [
                new ResponseCookie('foo', 'bar', CookieAttributes::empty()->withDomain('example.com')),
                'www.example.com',
                true,
            ],
            [
                new ResponseCookie('foo', 'bar', CookieAttributes::empty()->withDomain('example.com')),
                'anotherexample.com',
                false,
            ],
            [
                new ResponseCookie('foo', 'bar', CookieAttributes::empty()->withDomain('anotherexample.com')),
                'example.com',
                false,
            ],
            [
                new ResponseCookie('foo', 'bar', CookieAttributes::empty()->withDomain('com')),
                'anotherexample.com',
                false,
            ],
            [
                new ResponseCookie('foo', 'bar', CookieAttributes::empty()->withDomain('.com')),
                'anotherexample.com',
                false,
            ],
            [new ResponseCookie('foo', 'bar', CookieAttributes::empty()->withDomain('')), 'example.com', true],
        ];
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Cookie;

use Amp\Http\Cookie\CookieAttributes;
use Amp\Http\Cookie\RequestCookie;
use Amp\Http\Cookie\ResponseCookie;

abstract class CookieJarTest extends CookieTest
{
    private CookieJar $jar;

    public function setUp(): void
    {
        parent::setUp();

        $this->jar = $this->createJar();
    }

    /**
     * @dataProvider provideCookieDomainMatchData
     */
    public function testCookieDomainMatching(ResponseCookie $cookie, string $domain, bool $returned): void
    {
        $this->jar->store($cookie);

        $requestCookies = $this->jar->get($this->getUri('https', $domain, '/'));

        if ($returned) {
            $requestCookie = new RequestCookie($cookie->getName(), $cookie->getValue());
            $this->assertSame((string) $requestCookie, \implode('; ', $requestCookies));
        } else {
            $this->assertSame([], $requestCookies);
        }
    }

    public function provideCookieDomainMatchData(): array
    {
        // See http://stackoverflow.com/a/1063760/2373138 for cases
        return [
            [
                new ResponseCookie('foo', 'bar', CookieAttributes::empty()->withDomain('.foo.bar.example.com')),
                'foo.bar',
                false,
            ], /* previous security issue */
            [
                new ResponseCookie('foo', 'bar', CookieAttributes::empty()->withDomain('.example.com')),
                'example.com',
                true,
            ],
            [
                new ResponseCookie('foo', 'bar', CookieAttributes::empty()->withDomain('.example.com')),
                'www.example.com',
                true,
            ],
            [
                new ResponseCookie('foo', 'bar', CookieAttributes::empty()->withDomain('example.com')),
                'example.com',
                true,
            ],
            [
                new ResponseCookie('foo', 'bar', CookieAttributes::empty()->withDomain('example.com')),
                'www.example.com',
                false,
            ],
            [
                new ResponseCookie('foo', 'bar', CookieAttributes::empty()->withDomain('example.com')),
                'anotherexample.com',
                false,
            ],
            [
                new ResponseCookie('foo', 'bar', CookieAttributes::empty()->withDomain('anotherexample.com')),
                'example.com',
                false,
            ],
        ];
    }

    abstract protected function createJar(): CookieJar;
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Cookie\Internal;

use Amp\Dns\InvalidNameException;
use PHPUnit\Framework\TestCase;

class PublicSuffixListTest extends TestCase
{
    /**
     * @dataProvider provideTestData
     * @requires extension intl
     *
     * @throws InvalidNameException
     */
    public function testWithData($domain, $expectation): void
    {
        $this->assertSame($expectation, PublicSuffixList::isPublicSuffix($domain));
    }

    public function provideTestData(): array
    {
        $lines = \file(__DIR__ . '/../fixture/public_suffix_list_tests.txt', \FILE_IGNORE_NEW_LINES | \FILE_SKIP_EMPTY_LINES);
        $lines = \array_filter($lines, static function ($line) {
            return !\str_starts_with($line, '//');
        });

        return \array_map(static function ($line) {
            $parts = \explode(' ', $line);

            return [
                $parts[0],
                (bool) $parts[1],
            ];
        }, $lines);
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Cookie;

use Amp\Http\Cookie\ResponseCookie;
use Psr\Http\Message\UriInterface as PsrUri;

final class NullCookieJar implements CookieJar
{
    public function get(PsrUri $uri): array
    {
        return [];
    }

    public function store(ResponseCookie ...$cookies): void
    {
        // nothing to do
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Cookie;

use Amp\Http\Client\HttpException;
use Amp\Http\Cookie\InvalidCookieException;
use Amp\Http\Cookie\RequestCookie;
use Amp\Http\Cookie\ResponseCookie;
use Psr\Http\Message\UriInterface as PsrUri;

final class LocalCookieJar implements CookieJar
{
    /**
     * Cookies stored by Domain -> Path -> Name.
     * @var array<string, array<string, array<string, ResponseCookie>>>
     */
    private array $cookies = [];

    public function store(ResponseCookie ...$cookies): void
    {
        foreach ($cookies as $cookie) {
            if ($cookie->getDomain() === '') {
                throw new HttpException("Can't store cookie without domain information.");
            }

            $this->cookies[$cookie->getDomain()][$cookie->getPath() ?: '/'][$cookie->getName()] = $cookie;
        }
    }

    public function get(PsrUri $uri): array
    {
        $this->clearExpiredCookies();

        $path = $uri->getPath() ?: '/';
        $domain = $uri->getHost();

        $isRequestSecure = $uri->getScheme() === 'https';

        $matches = [];

        foreach ($this->cookies as $cookieDomain => $domainCookies) {
            if (!$this->matchesDomain($domain, $cookieDomain)) {
                continue;
            }

            foreach ($domainCookies as $cookiePath => $pathCookies) {
                if (!$this->matchesPath($path, $cookiePath)) {
                    continue;
                }

                foreach ($pathCookies as $cookie) {
                    if ($isRequestSecure || !$cookie->isSecure()) {
                        try {
                            $matches[] = new RequestCookie($cookie->getName(), $cookie->getValue());
                        } catch (InvalidCookieException) {
                            // ignore cookie
                        }
                    }
                }
            }
        }

        return $matches;
    }

    /**
     * @return list<ResponseCookie>
     */
    public function getAll(): array
    {
        $cookies = [];

        foreach ($this->cookies as $cookiesPerDomain) {
            foreach ($cookiesPerDomain as $cookiesPerPath) {
                foreach ($cookiesPerPath as $cookie) {
                    $cookies[] = $cookie;
                }
            }
        }

        return $cookies;
    }

    public function clear(): void
    {
        $this->cookies = [];
    }

    private function clearExpiredCookies(): void
    {
        $now = \time();
        foreach ($this->cookies as $domain => $domainCookies) {
            foreach ($domainCookies as $path => $pathCookies) {
                foreach ($pathCookies as $name => $cookie) {
                    if (($cookie->getExpiry()?->getTimestamp() ?? $now) < $now) {
                        unset($this->cookies[$domain][$path][$name]);
                    }
                }
            }
        }
    }

    /**
     * @link http://tools.ietf.org/html/rfc6265#section-5.1.3
     */
    private function matchesDomain(string $requestDomain, string $cookieDomain): bool
    {
        if ($requestDomain === \ltrim($cookieDomain, '.')) {
            return true;
        }

        /** @noinspection SubStrUsedAsStrPosInspection */
        $isWildcardCookieDomain = $cookieDomain[0] === '.';
        if (!$isWildcardCookieDomain) {
            return false;
        }

        if (\filter_var($requestDomain, FILTER_VALIDATE_IP)) {
            return false;
        }

        if (\substr($requestDomain, 0, -\strlen($cookieDomain)) . $cookieDomain === $requestDomain) {
            return true;
        }

        return false;
    }

    /**
     * @link http://tools.ietf.org/html/rfc6265#section-5.1.4
     */
    private function matchesPath(string $requestPath, string $cookiePath): bool
    {
        if ($requestPath === $cookiePath) {
            return true;
        }

        if (!\str_starts_with($requestPath, $cookiePath)) {
            return false;
        }

        if ((\str_ends_with($cookiePath, '/') || $requestPath[\strlen($cookiePath)] === '/')) {
            return true;
        }

        return false;
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Cookie;

use Amp\Http\Client\HttpException;
use Amp\Http\Cookie\RequestCookie;
use Amp\Http\Cookie\ResponseCookie;
use Psr\Http\Message\UriInterface as PsrUri;

interface CookieJar
{
    /**
     * Retrieve all cookies matching the specified constraints.
     *
     * @return list<RequestCookie> Returns an array (possibly empty) of all cookie matches.
     */
    public function get(PsrUri $uri): array;

    /**
     * Store a cookie.
     *
     * @throws HttpException
     */
    public function store(ResponseCookie ...$cookies): void;
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Cookie\Internal;

use Amp\Dns\InvalidNameException;
use function Amp\Dns\normalizeName;

/** @internal */
final class PublicSuffixList
{
    private static bool $initialized = false;
    private static array $suffixPatterns;
    private static array $exceptionPatterns;

    /**
     * @throws InvalidNameException
     */
    public static function isPublicSuffix(string $domain): bool
    {
        if (!self::$initialized) {
            self::readList();
            self::$initialized = true;
        }

        $domain = normalizeName($domain);
        $domain = \implode('.', \array_reverse(\explode('.', \trim($domain, '.'))));

        foreach (self::$exceptionPatterns as $pattern) {
            if (\preg_match($pattern, $domain)) {
                return false;
            }
        }

        foreach (self::$suffixPatterns as $pattern) {
            if (\preg_match($pattern, $domain)) {
                return true;
            }
        }

        return false;
    }

    private static function readList(): void
    {
        $lines = \file(__DIR__ . '/../../res/public_suffix_list.dat', \FILE_IGNORE_NEW_LINES | \FILE_SKIP_EMPTY_LINES);

        $exceptions = [];
        $rules = [];

        foreach ($lines as $line) {
            if (\trim($line) === '') {
                continue;
            }

            if (\str_starts_with($line, '//')) {
                continue;
            }

            $rule = \strtok($line, " \t");

            try {
                if ($rule[0] === '!') {
                    $exceptions[] = self::toRegex(\substr($rule, 1), true);
                } else {
                    $rules[] = self::toRegex($rule, false);
                }
            } catch (InvalidNameException $e) {
                // ignore IDN rules if no IDN support is available
                // requests with IDNs will fail anyway then
            }
        }

        self::$exceptionPatterns = \array_map(static function ($list) {
            return '(^(?:' . \implode('|', $list) . ')$)i';
        }, \array_chunk($exceptions, 256));

        self::$suffixPatterns = \array_map(static function ($list) {
            return '(^(?:' . \implode('|', $list) . ')$)i';
        }, \array_chunk($rules, 256));
    }

    /**
     * @throws InvalidNameException
     */
    private static function toRegex(string $rule, bool $exception): string
    {
        $labels = \explode('.', $rule);

        foreach ($labels as $key => $label) {
            if ($label !== '*') {
                $labels[$key] = normalizeName($label);
            }
        }

        $rule = \implode('.', $labels);

        $regexParts = [];

        foreach (\explode('.', $rule) as $part) {
            if ($part === '*') {
                $regexParts[] = '[^.]+';
            } else {
                /** @noinspection PregQuoteUsageInspection */ // We use (), so we don't have that problem
                $regexParts[] = \preg_quote($part);
            }
        }

        return \array_reduce($regexParts, static function (string $carry, string $item) use ($exception): string {
            if ($carry === '') {
                return $item;
            }

            return $item . "(?:\\." . $carry . ')' . ($exception ? '' : '?');
        }, '');
    }

    private function __construct()
    {
        // no instances should be built
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Cookie;

use Amp\ByteStream;
use Amp\File;
use Amp\File\Filesystem;
use Amp\Future;
use Amp\Http\Client\HttpException;
use Amp\Http\Cookie\ResponseCookie;
use Amp\Sync\LocalMutex;
use Amp\Sync\Mutex;
use Psr\Http\Message\UriInterface as PsrUri;
use function Amp\async;

final class FileCookieJar implements CookieJar
{
    /** @var Future<LocalCookieJar>|null */
    private ?Future $cookieJar = null;

    private bool $persistSessionCookies = false;

    private readonly Filesystem $filesystem;

    public function __construct(
        private readonly string $storagePath,
        private readonly Mutex $mutex = new LocalMutex(),
        ?Filesystem $filesystem = null
    ) {
        if (!\class_exists(Filesystem::class)) {
            throw new \Error(self::class . ' requires amphp/file to be installed. Run composer require amphp/file to install it.');
        }

        $this->filesystem = $filesystem ?? File\filesystem();
    }

    public function enableSessionCookiePersistence(): void
    {
        $this->persistSessionCookies = true;
    }

    public function disableSessionCookiePersistence(): void
    {
        $this->persistSessionCookies = false;
    }

    public function get(PsrUri $uri): array
    {
        $cookieJar = $this->read();
        return $cookieJar->get($uri);
    }

    public function store(ResponseCookie ...$cookies): void
    {
        $cookieJar = $this->read();

        $cookieJar->store(...$cookies);

        $this->write($cookieJar);
    }

    private function read(): LocalCookieJar
    {
        $this->cookieJar ??= async(function (): LocalCookieJar {
            $lock = $this->mutex->acquire();

            $cookieJar = new LocalCookieJar;

            if (!$this->filesystem->exists($this->storagePath)) {
                return $cookieJar;
            }

            $file = $this->filesystem->openFile($this->storagePath, 'r');

            foreach (ByteStream\splitLines($file) as $line) {
                $line = \trim($line);

                if ($line) {
                    $cookie = ResponseCookie::fromHeader($line);
                    if ($cookie === null) {
                        continue;
                    }

                    try {
                        $cookieJar->store($cookie);
                    } catch (HttpException $e) {
                        // ignore invalid cookies in storage
                    }
                }
            }

            $file->close();
            $lock->release();

            return $cookieJar;
        });

        return $this->cookieJar->await();
    }

    private function write(LocalCookieJar $cookieJar): void
    {
        $lock = $this->mutex->acquire();

        if (!$this->filesystem->isDirectory(\dirname($this->storagePath))) {
            $this->filesystem->createDirectoryRecursively(\dirname($this->storagePath), 0755);

            if (!$this->filesystem->isDirectory(\dirname($this->storagePath))) {
                throw new HttpException('Failed to create cookie storage directory: ' . $this->storagePath);
            }
        }

        $now = \time();
        $file = $this->filesystem->openFile($this->storagePath, 'w');
        foreach ($cookieJar->getAll() as $cookie) {
            $expiry = $cookie->getExpiry();
            if ($expiry ? $expiry->getTimestamp() > $now : $this->persistSessionCookies) {
                $file->write($cookie . "\r\n");
            }
        }

        $file->close();
        $lock->release();
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Client\Cookie;

use Amp\Cancellation;
use Amp\Dns\InvalidNameException;
use Amp\Http\Client\Connection\Stream;
use Amp\Http\Client\Cookie\Internal\PublicSuffixList;
use Amp\Http\Client\HttpException;
use Amp\Http\Client\NetworkInterceptor;
use Amp\Http\Client\Request;
use Amp\Http\Client\Response;
use Amp\Http\Cookie\ResponseCookie;

final class CookieInterceptor implements NetworkInterceptor
{
    public function __construct(private readonly CookieJar $cookieJar)
    {
    }

    public function requestViaNetwork(Request $request, Cancellation $cancellation, Stream $stream): Response
    {
        $this->assignApplicableRequestCookies($request);

        $request->interceptPush(function (Request $request, Response $response): Response {
            $this->storeCookies($response);
            return $response;
        });

        $response = $stream->request($request, $cancellation);

        $this->storeCookies($response);

        return $response;
    }

    private function assignApplicableRequestCookies(Request $request): void
    {
        $applicableCookies = $this->cookieJar->get($request->getUri());

        if (!$applicableCookies) {
            return; // No cookies matched our request; we're finished.
        }

        $cookiePairs = [];
        foreach ($applicableCookies as $cookie) {
            $cookiePairs[] = (string) $cookie;
        }

        if ($request->hasHeader('cookie')) {
            \array_unshift($cookiePairs, $request->getHeader('cookie'));
        }

        $request->setHeader('cookie', \implode('; ', $cookiePairs));
    }

    private function createResponseCookie(string $requestDomain, string $rawCookieStr): ?ResponseCookie
    {
        try {
            $cookie = ResponseCookie::fromHeader($rawCookieStr);
            if ($cookie === null) {
                return null;
            }

            if (!$cookie->getDomain()) {
                $cookie = $cookie->withDomain($requestDomain);
            } else {
                // https://tools.ietf.org/html/rfc6265#section-4.1.2.3
                $cookieDomain = $cookie->getDomain();

                // If a domain is set, left dots are ignored and it's always a wildcard
                $cookieDomain = \ltrim($cookieDomain, '.');

                if ($cookieDomain !== $requestDomain) {
                    // ignore cookies on domains that are public suffixes
                    if (PublicSuffixList::isPublicSuffix($cookieDomain)) {
                        return null;
                    }

                    // cookie origin would not be included when sending the cookie
                    $cookieDomainLength = \strlen($cookieDomain);
                    if (\substr($requestDomain, 0, -$cookieDomainLength - 1) . '.' . $cookieDomain !== $requestDomain) {
                        return null;
                    }
                }

                // always add the dot, it's used internally for wildcard matching when an explicit domain is sent
                $cookie = $cookie->withDomain('.' . $cookieDomain);
            }

            return $cookie;
        } catch (InvalidNameException $e) {
            // Ignore malformed Set-Cookie headers
        }

        return null;
    }

    /**
     * @throws HttpException
     */
    private function storeCookies(Response $response): void
    {
        if ($response->hasHeader('set-cookie')) {
            $requestDomain = $response->getRequest()->getUri()->getHost();
            $rawCookies = $response->getHeaderArray('set-cookie');
            $cookies = [];

            foreach ($rawCookies as $rawCookie) {
                $cookie = $this->createResponseCookie($requestDomain, $rawCookie);
                if ($cookie !== null) {
                    $cookies[] = $cookie;
                }
            }

            if ($cookies) {
                $this->cookieJar->store(...$cookies);
            }
        }
    }
}
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

// Please pull this list from, and only from https://publicsuffix.org/list/public_suffix_list.dat,
// rather than any other VCS sites. Pulling from any other URL is not guaranteed to be supported.

// Instructions on pulling and using this list can be found at https://publicsuffix.org/list/.

// ===BEGIN ICANN DOMAINS===

// ac : http://nic.ac/rules.htm
ac
com.ac
edu.ac
gov.ac
net.ac
mil.ac
org.ac

// ad : https://en.wikipedia.org/wiki/.ad
ad
nom.ad

// ae : https://tdra.gov.ae/en/aeda/ae-policies
ae
co.ae
net.ae
org.ae
sch.ae
ac.ae
gov.ae
mil.ae

// aero : see https://www.information.aero/index.php?id=66
aero
accident-investigation.aero
accident-prevention.aero
aerobatic.aero
aeroclub.aero
aerodrome.aero
agents.aero
aircraft.aero
airline.aero
airport.aero
air-surveillance.aero
airtraffic.aero
air-traffic-control.aero
ambulance.aero
amusement.aero
association.aero
author.aero
ballooning.aero
broker.aero
caa.aero
cargo.aero
catering.aero
certification.aero
championship.aero
charter.aero
civilaviation.aero
club.aero
conference.aero
consultant.aero
consulting.aero
control.aero
council.aero
crew.aero
design.aero
dgca.aero
educator.aero
emergency.aero
engine.aero
engineer.aero
entertainment.aero
equipment.aero
exchange.aero
express.aero
federation.aero
flight.aero
fuel.aero
gliding.aero
government.aero
groundhandling.aero
group.aero
hanggliding.aero
homebuilt.aero
insurance.aero
journal.aero
journalist.aero
leasing.aero
logistics.aero
magazine.aero
maintenance.aero
media.aero
microlight.aero
modelling.aero
navigation.aero
parachuting.aero
paragliding.aero
passenger-association.aero
pilot.aero
press.aero
production.aero
recreation.aero
repbody.aero
res.aero
research.aero
rotorcraft.aero
safety.aero
scientist.aero
services.aero
show.aero
skydiving.aero
software.aero
student.aero
trader.aero
trading.aero
trainer.aero
union.aero
workinggroup.aero
works.aero

// af : http://www.nic.af/help.jsp
af
gov.af
com.af
org.af
net.af
edu.af

// ag : http://www.nic.ag/prices.htm
ag
com.ag
org.ag
net.ag
co.ag
nom.ag

// ai : http://nic.com.ai/
ai
off.ai
com.ai
net.ai
org.ai

// al : http://www.ert.gov.al/ert_alb/faq_det.html?Id=31
al
com.al
edu.al
gov.al
mil.al
net.al
org.al

// am : https://www.amnic.net/policy/en/Policy_EN.pdf
am
co.am
com.am
commune.am
net.am
org.am

// ao : https://en.wikipedia.org/wiki/.ao
// http://www.dns.ao/REGISTR.DOC
ao
ed.ao
gv.ao
og.ao
co.ao
pb.ao
it.ao

// aq : https://en.wikipedia.org/wiki/.aq
aq

// ar : https://nic.ar/es/nic-argentina/normativa
ar
bet.ar
com.ar
coop.ar
edu.ar
gob.ar
gov.ar
int.ar
mil.ar
musica.ar
mutual.ar
net.ar
org.ar
senasa.ar
tur.ar

// arpa : https://en.wikipedia.org/wiki/.arpa
// Confirmed by registry <iana-questions@icann.org> 2008-06-18
arpa
e164.arpa
in-addr.arpa
ip6.arpa
iris.arpa
uri.arpa
urn.arpa

// as : https://en.wikipedia.org/wiki/.as
as
gov.as

// asia : https://en.wikipedia.org/wiki/.asia
asia

// at : https://en.wikipedia.org/wiki/.at
// Confirmed by registry <it@nic.at> 2008-06-17
at
ac.at
co.at
gv.at
or.at
sth.ac.at

// au : https://en.wikipedia.org/wiki/.au
// http://www.auda.org.au/
au
// 2LDs
com.au
net.au
org.au
edu.au
gov.au
asn.au
id.au
// Historic 2LDs (closed to new registration, but sites still exist)
info.au
conf.au
oz.au
// CGDNs - http://www.cgdn.org.au/
act.au
nsw.au
nt.au
qld.au
sa.au
tas.au
vic.au
wa.au
// 3LDs
act.edu.au
catholic.edu.au
// eq.edu.au - Removed at the request of the Queensland Department of Education
nsw.edu.au
nt.edu.au
qld.edu.au
sa.edu.au
tas.edu.au
vic.edu.au
wa.edu.au
// act.gov.au  Bug 984824 - Removed at request of Greg Tankard
// nsw.gov.au  Bug 547985 - Removed at request of <Shae.Donelan@services.nsw.gov.au>
// nt.gov.au  Bug 940478 - Removed at request of Greg Connors <Greg.Connors@nt.gov.au>
qld.gov.au
sa.gov.au
tas.gov.au
vic.gov.au
wa.gov.au
// 4LDs
// education.tas.edu.au - Removed at the request of the Department of Education Tasmania
schools.nsw.edu.au

// aw : https://en.wikipedia.org/wiki/.aw
aw
com.aw

// ax : https://en.wikipedia.org/wiki/.ax
ax

// az : https://en.wikipedia.org/wiki/.az
az
com.az
net.az
int.az
gov.az
org.az
edu.az
info.az
pp.az
mil.az
name.az
pro.az
biz.az

// ba : http://nic.ba/users_data/files/pravilnik_o_registraciji.pdf
ba
com.ba
edu.ba
gov.ba
mil.ba
net.ba
org.ba

// bb : https://en.wikipedia.org/wiki/.bb
bb
biz.bb
co.bb
com.bb
edu.bb
gov.bb
info.bb
net.bb
org.bb
store.bb
tv.bb

// bd : https://en.wikipedia.org/wiki/.bd
*.bd

// be : https://en.wikipedia.org/wiki/.be
// Confirmed by registry <tech@dns.be> 2008-06-08
be
ac.be

// bf : https://en.wikipedia.org/wiki/.bf
bf
gov.bf

// bg : https://en.wikipedia.org/wiki/.bg
// https://www.register.bg/user/static/rules/en/index.html
bg
a.bg
b.bg
c.bg
d.bg
e.bg
f.bg
g.bg
h.bg
i.bg
j.bg
k.bg
l.bg
m.bg
n.bg
o.bg
p.bg
q.bg
r.bg
s.bg
t.bg
u.bg
v.bg
w.bg
x.bg
y.bg
z.bg
0.bg
1.bg
2.bg
3.bg
4.bg
5.bg
6.bg
7.bg
8.bg
9.bg

// bh : https://en.wikipedia.org/wiki/.bh
bh
com.bh
edu.bh
net.bh
org.bh
gov.bh

// bi : https://en.wikipedia.org/wiki/.bi
// http://whois.nic.bi/
bi
co.bi
com.bi
edu.bi
or.bi
org.bi

// biz : https://en.wikipedia.org/wiki/.biz
biz

// bj : https://nic.bj/bj-suffixes.txt
// submitted by registry <contact@nic.bj>
bj
africa.bj
agro.bj
architectes.bj
assur.bj
avocats.bj
co.bj
com.bj
eco.bj
econo.bj
edu.bj
info.bj
loisirs.bj
money.bj
net.bj
org.bj
ote.bj
resto.bj
restaurant.bj
tourism.bj
univ.bj

// bm : http://www.bermudanic.bm/dnr-text.txt
bm
com.bm
edu.bm
gov.bm
net.bm
org.bm

// bn : http://www.bnnic.bn/faqs
bn
com.bn
edu.bn
gov.bn
net.bn
org.bn

// bo : https://nic.bo/delegacion2015.php#h-1.10
bo
com.bo
edu.bo
gob.bo
int.bo
org.bo
net.bo
mil.bo
tv.bo
web.bo
// Social Domains
academia.bo
agro.bo
arte.bo
blog.bo
bolivia.bo
ciencia.bo
cooperativa.bo
democracia.bo
deporte.bo
ecologia.bo
economia.bo
empresa.bo
indigena.bo
industria.bo
info.bo
medicina.bo
movimiento.bo
musica.bo
natural.bo
nombre.bo
noticias.bo
patria.bo
politica.bo
profesional.bo
plurinacional.bo
pueblo.bo
revista.bo
salud.bo
tecnologia.bo
tksat.bo
transporte.bo
wiki.bo

// br : http://registro.br/dominio/categoria.html
// Submitted by registry <fneves@registro.br>
br
9guacu.br
abc.br
adm.br
adv.br
agr.br
aju.br
am.br
anani.br
aparecida.br
app.br
arq.br
art.br
ato.br
b.br
barueri.br
belem.br
bhz.br
bib.br
bio.br
blog.br
bmd.br
boavista.br
bsb.br
campinagrande.br
campinas.br
caxias.br
cim.br
cng.br
cnt.br
com.br
contagem.br
coop.br
coz.br
cri.br
cuiaba.br
curitiba.br
def.br
des.br
det.br
dev.br
ecn.br
eco.br
edu.br
emp.br
enf.br
eng.br
esp.br
etc.br
eti.br
far.br
feira.br
flog.br
floripa.br
fm.br
fnd.br
fortal.br
fot.br
foz.br
fst.br
g12.br
geo.br
ggf.br
goiania.br
gov.br
// gov.br 26 states + df https://en.wikipedia.org/wiki/States_of_Brazil
ac.gov.br
al.gov.br
am.gov.br
ap.gov.br
ba.gov.br
ce.gov.br
df.gov.br
es.gov.br
go.gov.br
ma.gov.br
mg.gov.br
ms.gov.br
mt.gov.br
pa.gov.br
pb.gov.br
pe.gov.br
pi.gov.br
pr.gov.br
rj.gov.br
rn.gov.br
ro.gov.br
rr.gov.br
rs.gov.br
sc.gov.br
se.gov.br
sp.gov.br
to.gov.br
gru.br
imb.br
ind.br
inf.br
jab.br
jampa.br
jdf.br
joinville.br
jor.br
jus.br
leg.br
lel.br
log.br
londrina.br
macapa.br
maceio.br
manaus.br
maringa.br
mat.br
med.br
mil.br
morena.br
mp.br
mus.br
natal.br
net.br
niteroi.br
*.nom.br
not.br
ntr.br
odo.br
ong.br
org.br
osasco.br
palmas.br
poa.br
ppg.br
pro.br
psc.br
psi.br
pvh.br
qsl.br
radio.br
rec.br
recife.br
rep.br
ribeirao.br
rio.br
riobranco.br
riopreto.br
salvador.br
sampa.br
santamaria.br
santoandre.br
saobernardo.br
saogonca.br
seg.br
sjc.br
slg.br
slz.br
sorocaba.br
srv.br
taxi.br
tc.br
tec.br
teo.br
the.br
tmp.br
trd.br
tur.br
tv.br
udi.br
vet.br
vix.br
vlog.br
wiki.br
zlg.br

// bs : http://www.nic.bs/rules.html
bs
com.bs
net.bs
org.bs
edu.bs
gov.bs

// bt : https://en.wikipedia.org/wiki/.bt
bt
com.bt
edu.bt
gov.bt
net.bt
org.bt

// bv : No registrations at this time.
// Submitted by registry <jarle@uninett.no>
bv

// bw : https://en.wikipedia.org/wiki/.bw
// http://www.gobin.info/domainname/bw.doc
// list of other 2nd level tlds ?
bw
co.bw
org.bw

// by : https://en.wikipedia.org/wiki/.by
// http://tld.by/rules_2006_en.html
// list of other 2nd level tlds ?
by
gov.by
mil.by
// Official information does not indicate that com.by is a reserved
// second-level domain, but it's being used as one (see www.google.com.by and
// www.yahoo.com.by, for example), so we list it here for safety's sake.
com.by

// http://hoster.by/
of.by

// bz : https://en.wikipedia.org/wiki/.bz
// http://www.belizenic.bz/
bz
com.bz
net.bz
org.bz
edu.bz
gov.bz

// ca : https://en.wikipedia.org/wiki/.ca
ca
// ca geographical names
ab.ca
bc.ca
mb.ca
nb.ca
nf.ca
nl.ca
ns.ca
nt.ca
nu.ca
on.ca
pe.ca
qc.ca
sk.ca
yk.ca
// gc.ca: https://en.wikipedia.org/wiki/.gc.ca
// see also: http://registry.gc.ca/en/SubdomainFAQ
gc.ca

// cat : https://en.wikipedia.org/wiki/.cat
cat

// cc : https://en.wikipedia.org/wiki/.cc
cc

// cd : https://en.wikipedia.org/wiki/.cd
// see also: https://www.nic.cd/domain/insertDomain_2.jsp?act=1
cd
gov.cd

// cf : https://en.wikipedia.org/wiki/.cf
cf

// cg : https://en.wikipedia.org/wiki/.cg
cg

// ch : https://en.wikipedia.org/wiki/.ch
ch

// ci : https://en.wikipedia.org/wiki/.ci
// http://www.nic.ci/index.php?page=charte
ci
org.ci
or.ci
com.ci
co.ci
edu.ci
ed.ci
ac.ci
net.ci
go.ci
asso.ci
aÃ©roport.ci
int.ci
presse.ci
md.ci
gouv.ci

// ck : https://en.wikipedia.org/wiki/.ck
*.ck
!www.ck

// cl : https://www.nic.cl
// Confirmed by .CL registry <hsalgado@nic.cl>
cl
co.cl
gob.cl
gov.cl
mil.cl

// cm : https://en.wikipedia.org/wiki/.cm plus bug 981927
cm
co.cm
com.cm
gov.cm
net.cm

// cn : https://en.wikipedia.org/wiki/.cn
// Submitted by registry <tanyaling@cnnic.cn>
cn
ac.cn
com.cn
edu.cn
gov.cn
net.cn
org.cn
mil.cn
å…¬å�¸.cn
ç½‘ç»œ.cn
ç¶²çµ¡.cn
// cn geographic names
ah.cn
bj.cn
cq.cn
fj.cn
gd.cn
gs.cn
gz.cn
gx.cn
ha.cn
hb.cn
he.cn
hi.cn
hl.cn
hn.cn
jl.cn
js.cn
jx.cn
ln.cn
nm.cn
nx.cn
qh.cn
sc.cn
sd.cn
sh.cn
sn.cn
sx.cn
tj.cn
xj.cn
xz.cn
yn.cn
zj.cn
hk.cn
mo.cn
tw.cn

// co : https://en.wikipedia.org/wiki/.co
// Submitted by registry <tecnico@uniandes.edu.co>
co
arts.co
com.co
edu.co
firm.co
gov.co
info.co
int.co
mil.co
net.co
nom.co
org.co
rec.co
web.co

// com : https://en.wikipedia.org/wiki/.com
com

// coop : https://en.wikipedia.org/wiki/.coop
coop

// cr : http://www.nic.cr/niccr_publico/showRegistroDominiosScreen.do
cr
ac.cr
co.cr
ed.cr
fi.cr
go.cr
or.cr
sa.cr

// cu : https://en.wikipedia.org/wiki/.cu
cu
com.cu
edu.cu
org.cu
net.cu
gov.cu
inf.cu

// cv : https://en.wikipedia.org/wiki/.cv
// cv : http://www.dns.cv/tldcv_portal/do?com=DS;5446457100;111;+PAGE(4000018)+K-CAT-CODIGO(RDOM)+RCNT(100); <- registration rules
cv
com.cv
edu.cv
int.cv
nome.cv
org.cv

// cw : http://www.una.cw/cw_registry/
// Confirmed by registry <registry@una.net> 2013-03-26
cw
com.cw
edu.cw
net.cw
org.cw

// cx : https://en.wikipedia.org/wiki/.cx
// list of other 2nd level tlds ?
cx
gov.cx

// cy : http://www.nic.cy/
// Submitted by registry Panayiotou Fotia <cydns@ucy.ac.cy>
// namespace policies URL https://www.nic.cy/portal//sites/default/files/symfonia_gia_eggrafi.pdf
cy
ac.cy
biz.cy
com.cy
ekloges.cy
gov.cy
ltd.cy
mil.cy
net.cy
org.cy
press.cy
pro.cy
tm.cy

// cz : https://en.wikipedia.org/wiki/.cz
cz

// de : https://en.wikipedia.org/wiki/.de
// Confirmed by registry <ops@denic.de> (with technical
// reservations) 2008-07-01
de

// dj : https://en.wikipedia.org/wiki/.dj
dj

// dk : https://en.wikipedia.org/wiki/.dk
// Confirmed by registry <robert@dk-hostmaster.dk> 2008-06-17
dk

// dm : https://en.wikipedia.org/wiki/.dm
dm
com.dm
net.dm
org.dm
edu.dm
gov.dm

// do : https://en.wikipedia.org/wiki/.do
do
art.do
com.do
edu.do
gob.do
gov.do
mil.do
net.do
org.do
sld.do
web.do

// dz : http://www.nic.dz/images/pdf_nic/charte.pdf
dz
art.dz
asso.dz
com.dz
edu.dz
gov.dz
org.dz
net.dz
pol.dz
soc.dz
tm.dz

// ec : http://www.nic.ec/reg/paso1.asp
// Submitted by registry <vabboud@nic.ec>
ec
com.ec
info.ec
net.ec
fin.ec
k12.ec
med.ec
pro.ec
org.ec
edu.ec
gov.ec
gob.ec
mil.ec

// edu : https://en.wikipedia.org/wiki/.edu
edu

// ee : http://www.eenet.ee/EENet/dom_reeglid.html#lisa_B
ee
edu.ee
gov.ee
riik.ee
lib.ee
med.ee
com.ee
pri.ee
aip.ee
org.ee
fie.ee

// eg : https://en.wikipedia.org/wiki/.eg
eg
com.eg
edu.eg
eun.eg
gov.eg
mil.eg
name.eg
net.eg
org.eg
sci.eg

// er : https://en.wikipedia.org/wiki/.er
*.er

// es : https://www.nic.es/site_ingles/ingles/dominios/index.html
es
com.es
nom.es
org.es
gob.es
edu.es

// et : https://en.wikipedia.org/wiki/.et
et
com.et
gov.et
org.et
edu.et
biz.et
name.et
info.et
net.et

// eu : https://en.wikipedia.org/wiki/.eu
eu

// fi : https://en.wikipedia.org/wiki/.fi
fi
// aland.fi : https://en.wikipedia.org/wiki/.ax
// This domain is being phased out in favor of .ax. As there are still many
// domains under aland.fi, we still keep it on the list until aland.fi is
// completely removed.
// TODO: Check for updates (expected to be phased out around Q1/2009)
aland.fi

// fj : http://domains.fj/
// Submitted by registry <garth.miller@cocca.org.nz> 2020-02-11
fj
ac.fj
biz.fj
com.fj
gov.fj
info.fj
mil.fj
name.fj
net.fj
org.fj
pro.fj

// fk : https://en.wikipedia.org/wiki/.fk
*.fk

// fm : https://en.wikipedia.org/wiki/.fm
com.fm
edu.fm
net.fm
org.fm
fm

// fo : https://en.wikipedia.org/wiki/.fo
fo

// fr : https://www.afnic.fr/ https://www.afnic.fr/wp-media/uploads/2022/12/afnic-naming-policy-2023-01-01.pdf
fr
asso.fr
com.fr
gouv.fr
nom.fr
prd.fr
tm.fr
// Other SLDs now selfmanaged out of AFNIC range. Former "domaines sectoriels", still registration suffixes
avoues.fr
cci.fr
greta.fr
huissier-justice.fr

// ga : https://en.wikipedia.org/wiki/.ga
ga

// gb : This registry is effectively dormant
// Submitted by registry <Damien.Shaw@ja.net>
gb

// gd : https://en.wikipedia.org/wiki/.gd
edu.gd
gov.gd
gd

// ge : http://www.nic.net.ge/policy_en.pdf
ge
com.ge
edu.ge
gov.ge
org.ge
mil.ge
net.ge
pvt.ge

// gf : https://en.wikipedia.org/wiki/.gf
gf

// gg : http://www.channelisles.net/register-domains/
// Confirmed by registry <nigel@channelisles.net> 2013-11-28
gg
co.gg
net.gg
org.gg

// gh : https://en.wikipedia.org/wiki/.gh
// see also: http://www.nic.gh/reg_now.php
// Although domains directly at second level are not possible at the moment,
// they have been possible for some time and may come back.
gh
com.gh
edu.gh
gov.gh
org.gh
mil.gh

// gi : http://www.nic.gi/rules.html
gi
com.gi
ltd.gi
gov.gi
mod.gi
edu.gi
org.gi

// gl : https://en.wikipedia.org/wiki/.gl
// http://nic.gl
gl
co.gl
com.gl
edu.gl
net.gl
org.gl

// gm : http://www.nic.gm/htmlpages%5Cgm-policy.htm
gm

// gn : http://psg.com/dns/gn/gn.txt
// Submitted by registry <randy@psg.com>
gn
ac.gn
com.gn
edu.gn
gov.gn
org.gn
net.gn

// gov : https://en.wikipedia.org/wiki/.gov
gov

// gp : http://www.nic.gp/index.php?lang=en
gp
com.gp
net.gp
mobi.gp
edu.gp
org.gp
asso.gp

// gq : https://en.wikipedia.org/wiki/.gq
gq

// gr : https://grweb.ics.forth.gr/english/1617-B-2005.html
// Submitted by registry <segred@ics.forth.gr>
gr
com.gr
edu.gr
net.gr
org.gr
gov.gr

// gs : https://en.wikipedia.org/wiki/.gs
gs

// gt : https://www.gt/sitio/registration_policy.php?lang=en
gt
com.gt
edu.gt
gob.gt
ind.gt
mil.gt
net.gt
org.gt

// gu : http://gadao.gov.gu/register.html
// University of Guam : https://www.uog.edu
// Submitted by uognoc@triton.uog.edu
gu
com.gu
edu.gu
gov.gu
guam.gu
info.gu
net.gu
org.gu
web.gu

// gw : https://en.wikipedia.org/wiki/.gw
// gw : https://nic.gw/regras/
gw

// gy : https://en.wikipedia.org/wiki/.gy
// http://registry.gy/
gy
co.gy
com.gy
edu.gy
gov.gy
net.gy
org.gy

// hk : https://www.hkirc.hk
// Submitted by registry <hk.tech@hkirc.hk>
hk
com.hk
edu.hk
gov.hk
idv.hk
net.hk
org.hk
å…¬å�¸.hk
æ•™è‚².hk
æ•Žè‚².hk
æ”¿åºœ.hk
å€‹äºº.hk
ä¸ªäºº.hk
ç®‡äºº.hk
ç¶²ç»œ.hk
ç½‘ç»œ.hk
ç»„ç¹”.hk
ç¶²çµ¡.hk
ç½‘çµ¡.hk
ç»„ç»‡.hk
çµ„ç¹”.hk
çµ„ç»‡.hk

// hm : https://en.wikipedia.org/wiki/.hm
hm

// hn : http://www.nic.hn/politicas/ps02,,05.html
hn
com.hn
edu.hn
org.hn
net.hn
mil.hn
gob.hn

// hr : http://www.dns.hr/documents/pdf/HRTLD-regulations.pdf
hr
iz.hr
from.hr
name.hr
com.hr

// ht : http://www.nic.ht/info/charte.cfm
ht
com.ht
shop.ht
firm.ht
info.ht
adult.ht
net.ht
pro.ht
org.ht
med.ht
art.ht
coop.ht
pol.ht
asso.ht
edu.ht
rel.ht
gouv.ht
perso.ht

// hu : http://www.domain.hu/domain/English/sld.html
// Confirmed by registry <pasztor@iszt.hu> 2008-06-12
hu
co.hu
info.hu
org.hu
priv.hu
sport.hu
tm.hu
2000.hu
agrar.hu
bolt.hu
casino.hu
city.hu
erotica.hu
erotika.hu
film.hu
forum.hu
games.hu
hotel.hu
ingatlan.hu
jogasz.hu
konyvelo.hu
lakas.hu
media.hu
news.hu
reklam.hu
sex.hu
shop.hu
suli.hu
szex.hu
tozsde.hu
utazas.hu
video.hu

// id : https://pandi.id/en/domain/registration-requirements/
id
ac.id
biz.id
co.id
desa.id
go.id
mil.id
my.id
net.id
or.id
ponpes.id
sch.id
web.id

// ie : https://en.wikipedia.org/wiki/.ie
ie
gov.ie

// il :         http://www.isoc.org.il/domains/
// see also:    https://en.isoc.org.il/il-cctld/registration-rules
// ISOC-IL      (operated by .il Registry)
il
ac.il
co.il
gov.il
idf.il
k12.il
muni.il
net.il
org.il
// xn--4dbrk0ce ("Israel", Hebrew) : IL
×™×©×¨×�×œ
// xn--4dbgdty6c.xn--4dbrk0ce.
×�×§×“×ž×™×”.×™×©×¨×�×œ
// xn--5dbhl8d.xn--4dbrk0ce.
×™×©×•×‘.×™×©×¨×�×œ
// xn--8dbq2a.xn--4dbrk0ce.
×¦×”×œ.×™×©×¨×�×œ
// xn--hebda8b.xn--4dbrk0ce.
×ž×ž×©×œ.×™×©×¨×�×œ

// im : https://www.nic.im/
// Submitted by registry <info@nic.im>
im
ac.im
co.im
com.im
ltd.co.im
net.im
org.im
plc.co.im
tt.im
tv.im

// in : https://en.wikipedia.org/wiki/.in
// see also: https://registry.in/policies
// Please note, that nic.in is not an official eTLD, but used by most
// government institutions.
in
5g.in
6g.in
ac.in
ai.in
am.in
bihar.in
biz.in
business.in
ca.in
cn.in
co.in
com.in
coop.in
cs.in
delhi.in
dr.in
edu.in
er.in
firm.in
gen.in
gov.in
gujarat.in
ind.in
info.in
int.in
internet.in
io.in
me.in
mil.in
net.in
nic.in
org.in
pg.in
post.in
pro.in
res.in
travel.in
tv.in
uk.in
up.in
us.in

// info : https://en.wikipedia.org/wiki/.info
info

// int : https://en.wikipedia.org/wiki/.int
// Confirmed by registry <iana-questions@icann.org> 2008-06-18
int
eu.int

// io : http://www.nic.io/rules.htm
// list of other 2nd level tlds ?
io
com.io

// iq : http://www.cmc.iq/english/iq/iqregister1.htm
iq
gov.iq
edu.iq
mil.iq
com.iq
org.iq
net.iq

// ir : http://www.nic.ir/Terms_and_Conditions_ir,_Appendix_1_Domain_Rules
// Also see http://www.nic.ir/Internationalized_Domain_Names
// Two <iran>.ir entries added at request of <tech-team@nic.ir>, 2010-04-16
ir
ac.ir
co.ir
gov.ir
id.ir
net.ir
org.ir
sch.ir
// xn--mgba3a4f16a.ir (<iran>.ir, Persian YEH)
Ø§ÛŒØ±Ø§Ù†.ir
// xn--mgba3a4fra.ir (<iran>.ir, Arabic YEH)
Ø§ÙŠØ±Ø§Ù†.ir

// is : http://www.isnic.is/domain/rules.php
// Confirmed by registry <marius@isgate.is> 2008-12-06
is
net.is
com.is
edu.is
gov.is
org.is
int.is

// it : https://en.wikipedia.org/wiki/.it
it
gov.it
edu.it
// Reserved geo-names (regions and provinces):
// https://www.nic.it/sites/default/files/archivio/docs/Regulation_assignation_v7.1.pdf
// Regions
abr.it
abruzzo.it
aosta-valley.it
aostavalley.it
bas.it
basilicata.it
cal.it
calabria.it
cam.it
campania.it
emilia-romagna.it
emiliaromagna.it
emr.it
friuli-v-giulia.it
friuli-ve-giulia.it
friuli-vegiulia.it
friuli-venezia-giulia.it
friuli-veneziagiulia.it
friuli-vgiulia.it
friuliv-giulia.it
friulive-giulia.it
friulivegiulia.it
friulivenezia-giulia.it
friuliveneziagiulia.it
friulivgiulia.it
fvg.it
laz.it
lazio.it
lig.it
liguria.it
lom.it
lombardia.it
lombardy.it
lucania.it
mar.it
marche.it
mol.it
molise.it
piedmont.it
piemonte.it
pmn.it
pug.it
puglia.it
sar.it
sardegna.it
sardinia.it
sic.it
sicilia.it
sicily.it
taa.it
tos.it
toscana.it
trentin-sud-tirol.it
trentin-sÃ¼d-tirol.it
trentin-sudtirol.it
trentin-sÃ¼dtirol.it
trentin-sued-tirol.it
trentin-suedtirol.it
trentino-a-adige.it
trentino-aadige.it
trentino-alto-adige.it
trentino-altoadige.it
trentino-s-tirol.it
trentino-stirol.it
trentino-sud-tirol.it
trentino-sÃ¼d-tirol.it
trentino-sudtirol.it
trentino-sÃ¼dtirol.it
trentino-sued-tirol.it
trentino-suedtirol.it
trentino.it
trentinoa-adige.it
trentinoaadige.it
trentinoalto-adige.it
trentinoaltoadige.it
trentinos-tirol.it
trentinostirol.it
trentinosud-tirol.it
trentinosÃ¼d-tirol.it
trentinosudtirol.it
trentinosÃ¼dtirol.it
trentinosued-tirol.it
trentinosuedtirol.it
trentinsud-tirol.it
trentinsÃ¼d-tirol.it
trentinsudtirol.it
trentinsÃ¼dtirol.it
trentinsued-tirol.it
trentinsuedtirol.it
tuscany.it
umb.it
umbria.it
val-d-aosta.it
val-daosta.it
vald-aosta.it
valdaosta.it
valle-aosta.it
valle-d-aosta.it
valle-daosta.it
valleaosta.it
valled-aosta.it
valledaosta.it
vallee-aoste.it
vallÃ©e-aoste.it
vallee-d-aoste.it
vallÃ©e-d-aoste.it
valleeaoste.it
vallÃ©eaoste.it
valleedaoste.it
vallÃ©edaoste.it
vao.it
vda.it
ven.it
veneto.it
// Provinces
ag.it
agrigento.it
al.it
alessandria.it
alto-adige.it
altoadige.it
an.it
ancona.it
andria-barletta-trani.it
andria-trani-barletta.it
andriabarlettatrani.it
andriatranibarletta.it
ao.it
aosta.it
aoste.it
ap.it
aq.it
aquila.it
ar.it
arezzo.it
ascoli-piceno.it
ascolipiceno.it
asti.it
at.it
av.it
avellino.it
ba.it
balsan-sudtirol.it
balsan-sÃ¼dtirol.it
balsan-suedtirol.it
balsan.it
bari.it
barletta-trani-andria.it
barlettatraniandria.it
belluno.it
benevento.it
bergamo.it
bg.it
bi.it
biella.it
bl.it
bn.it
bo.it
bologna.it
bolzano-altoadige.it
bolzano.it
bozen-sudtirol.it
bozen-sÃ¼dtirol.it
bozen-suedtirol.it
bozen.it
br.it
brescia.it
brindisi.it
bs.it
bt.it
bulsan-sudtirol.it
bulsan-sÃ¼dtirol.it
bulsan-suedtirol.it
bulsan.it
bz.it
ca.it
cagliari.it
caltanissetta.it
campidano-medio.it
campidanomedio.it
campobasso.it
carbonia-iglesias.it
carboniaiglesias.it
carrara-massa.it
carraramassa.it
caserta.it
catania.it
catanzaro.it
cb.it
ce.it
cesena-forli.it
cesena-forlÃ¬.it
cesenaforli.it
cesenaforlÃ¬.it
ch.it
chieti.it
ci.it
cl.it
cn.it
co.it
como.it
cosenza.it
cr.it
cremona.it
crotone.it
cs.it
ct.it
cuneo.it
cz.it
dell-ogliastra.it
dellogliastra.it
en.it
enna.it
fc.it
fe.it
fermo.it
ferrara.it
fg.it
fi.it
firenze.it
florence.it
fm.it
foggia.it
forli-cesena.it
forlÃ¬-cesena.it
forlicesena.it
forlÃ¬cesena.it
fr.it
frosinone.it
ge.it
genoa.it
genova.it
go.it
gorizia.it
gr.it
grosseto.it
iglesias-carbonia.it
iglesiascarbonia.it
im.it
imperia.it
is.it
isernia.it
kr.it
la-spezia.it
laquila.it
laspezia.it
latina.it
lc.it
le.it
lecce.it
lecco.it
li.it
livorno.it
lo.it
lodi.it
lt.it
lu.it
lucca.it
macerata.it
mantova.it
massa-carrara.it
massacarrara.it
matera.it
mb.it
mc.it
me.it
medio-campidano.it
mediocampidano.it
messina.it
mi.it
milan.it
milano.it
mn.it
mo.it
modena.it
monza-brianza.it
monza-e-della-brianza.it
monza.it
monzabrianza.it
monzaebrianza.it
monzaedellabrianza.it
ms.it
mt.it
na.it
naples.it
napoli.it
no.it
novara.it
nu.it
nuoro.it
og.it
ogliastra.it
olbia-tempio.it
olbiatempio.it
or.it
oristano.it
ot.it
pa.it
padova.it
padua.it
palermo.it
parma.it
pavia.it
pc.it
pd.it
pe.it
perugia.it
pesaro-urbino.it
pesarourbino.it
pescara.it
pg.it
pi.it
piacenza.it
pisa.it
pistoia.it
pn.it
po.it
pordenone.it
potenza.it
pr.it
prato.it
pt.it
pu.it
pv.it
pz.it
ra.it
ragusa.it
ravenna.it
rc.it
re.it
reggio-calabria.it
reggio-emilia.it
reggiocalabria.it
reggioemilia.it
rg.it
ri.it
rieti.it
rimini.it
rm.it
rn.it
ro.it
roma.it
rome.it
rovigo.it
sa.it
salerno.it
sassari.it
savona.it
si.it
siena.it
siracusa.it
so.it
sondrio.it
sp.it
sr.it
ss.it
suedtirol.it
sÃ¼dtirol.it
sv.it
ta.it
taranto.it
te.it
tempio-olbia.it
tempioolbia.it
teramo.it
terni.it
tn.it
to.it
torino.it
tp.it
tr.it
trani-andria-barletta.it
trani-barletta-andria.it
traniandriabarletta.it
tranibarlettaandria.it
trapani.it
trento.it
treviso.it
trieste.it
ts.it
turin.it
tv.it
ud.it
udine.it
urbino-pesaro.it
urbinopesaro.it
va.it
varese.it
vb.it
vc.it
ve.it
venezia.it
venice.it
verbania.it
vercelli.it
verona.it
vi.it
vibo-valentia.it
vibovalentia.it
vicenza.it
viterbo.it
vr.it
vs.it
vt.it
vv.it

// je : http://www.channelisles.net/register-domains/
// Confirmed by registry <nigel@channelisles.net> 2013-11-28
je
co.je
net.je
org.je

// jm : http://www.com.jm/register.html
*.jm

// jo : http://www.dns.jo/Registration_policy.aspx
jo
com.jo
org.jo
net.jo
edu.jo
sch.jo
gov.jo
mil.jo
name.jo

// jobs : https://en.wikipedia.org/wiki/.jobs
jobs

// jp : https://en.wikipedia.org/wiki/.jp
// http://jprs.co.jp/en/jpdomain.html
// Submitted by registry <info@jprs.jp>
jp
// jp organizational type names
ac.jp
ad.jp
co.jp
ed.jp
go.jp
gr.jp
lg.jp
ne.jp
or.jp
// jp prefecture type names
aichi.jp
akita.jp
aomori.jp
chiba.jp
ehime.jp
fukui.jp
fukuoka.jp
fukushima.jp
gifu.jp
gunma.jp
hiroshima.jp
hokkaido.jp
hyogo.jp
ibaraki.jp
ishikawa.jp
iwate.jp
kagawa.jp
kagoshima.jp
kanagawa.jp
kochi.jp
kumamoto.jp
kyoto.jp
mie.jp
miyagi.jp
miyazaki.jp
nagano.jp
nagasaki.jp
nara.jp
niigata.jp
oita.jp
okayama.jp
okinawa.jp
osaka.jp
saga.jp
saitama.jp
shiga.jp
shimane.jp
shizuoka.jp
tochigi.jp
tokushima.jp
tokyo.jp
tottori.jp
toyama.jp
wakayama.jp
yamagata.jp
yamaguchi.jp
yamanashi.jp
æ ƒæœ¨.jp
æ„›çŸ¥.jp
æ„›åª›.jp
å…µåº«.jp
ç†Šæœ¬.jp
èŒ¨åŸŽ.jp
åŒ—æµ·é�“.jp
å�ƒè‘‰.jp
å’Œæ­Œå±±.jp
é•·å´Ž.jp
é•·é‡Ž.jp
æ–°æ½Ÿ.jp
é�’æ£®.jp
é�™å²¡.jp
æ�±äº¬.jp
çŸ³å·�.jp
åŸ¼çŽ‰.jp
ä¸‰é‡�.jp
äº¬éƒ½.jp
ä½�è³€.jp
å¤§åˆ†.jp
å¤§é˜ª.jp
å¥ˆè‰¯.jp
å®®åŸŽ.jp
å®®å´Ž.jp
å¯Œå±±.jp
å±±å�£.jp
å±±å½¢.jp
å±±æ¢¨.jp
å²©æ‰‹.jp
å²�é˜œ.jp
å²¡å±±.jp
å³¶æ ¹.jp
åºƒå³¶.jp
å¾³å³¶.jp
æ²–ç¸„.jp
æ»‹è³€.jp
ç¥žå¥ˆå·�.jp
ç¦�äº•.jp
ç¦�å²¡.jp
ç¦�å³¶.jp
ç§‹ç”°.jp
ç¾¤é¦¬.jp
é¦™å·�.jp
é«˜çŸ¥.jp
é³¥å�–.jp
é¹¿å…�å³¶.jp
// jp geographic type names
// http://jprs.jp/doc/rule/saisoku-1.html
*.kawasaki.jp
*.kitakyushu.jp
*.kobe.jp
*.nagoya.jp
*.sapporo.jp
*.sendai.jp
*.yokohama.jp
!city.kawasaki.jp
!city.kitakyushu.jp
!city.kobe.jp
!city.nagoya.jp
!city.sapporo.jp
!city.sendai.jp
!city.yokohama.jp
// 4th level registration
aisai.aichi.jp
ama.aichi.jp
anjo.aichi.jp
asuke.aichi.jp
chiryu.aichi.jp
chita.aichi.jp
fuso.aichi.jp
gamagori.aichi.jp
handa.aichi.jp
hazu.aichi.jp
hekinan.aichi.jp
higashiura.aichi.jp
ichinomiya.aichi.jp
inazawa.aichi.jp
inuyama.aichi.jp
isshiki.aichi.jp
iwakura.aichi.jp
kanie.aichi.jp
kariya.aichi.jp
kasugai.aichi.jp
kira.aichi.jp
kiyosu.aichi.jp
komaki.aichi.jp
konan.aichi.jp
kota.aichi.jp
mihama.aichi.jp
miyoshi.aichi.jp
nishio.aichi.jp
nisshin.aichi.jp
obu.aichi.jp
oguchi.aichi.jp
oharu.aichi.jp
okazaki.aichi.jp
owariasahi.aichi.jp
seto.aichi.jp
shikatsu.aichi.jp
shinshiro.aichi.jp
shitara.aichi.jp
tahara.aichi.jp
takahama.aichi.jp
tobishima.aichi.jp
toei.aichi.jp
togo.aichi.jp
tokai.aichi.jp
tokoname.aichi.jp
toyoake.aichi.jp
toyohashi.aichi.jp
toyokawa.aichi.jp
toyone.aichi.jp
toyota.aichi.jp
tsushima.aichi.jp
yatomi.aichi.jp
akita.akita.jp
daisen.akita.jp
fujisato.akita.jp
gojome.akita.jp
hachirogata.akita.jp
happou.akita.jp
higashinaruse.akita.jp
honjo.akita.jp
honjyo.akita.jp
ikawa.akita.jp
kamikoani.akita.jp
kamioka.akita.jp
katagami.akita.jp
kazuno.akita.jp
kitaakita.akita.jp
kosaka.akita.jp
kyowa.akita.jp
misato.akita.jp
mitane.akita.jp
moriyoshi.akita.jp
nikaho.akita.jp
noshiro.akita.jp
odate.akita.jp
oga.akita.jp
ogata.akita.jp
semboku.akita.jp
yokote.akita.jp
yurihonjo.akita.jp
aomori.aomori.jp
gonohe.aomori.jp
hachinohe.aomori.jp
hashikami.aomori.jp
hiranai.aomori.jp
hirosaki.aomori.jp
itayanagi.aomori.jp
kuroishi.aomori.jp
misawa.aomori.jp
mutsu.aomori.jp
nakadomari.aomori.jp
noheji.aomori.jp
oirase.aomori.jp
owani.aomori.jp
rokunohe.aomori.jp
sannohe.aomori.jp
shichinohe.aomori.jp
shingo.aomori.jp
takko.aomori.jp
towada.aomori.jp
tsugaru.aomori.jp
tsuruta.aomori.jp
abiko.chiba.jp
asahi.chiba.jp
chonan.chiba.jp
chosei.chiba.jp
choshi.chiba.jp
chuo.chiba.jp
funabashi.chiba.jp
futtsu.chiba.jp
hanamigawa.chiba.jp
ichihara.chiba.jp
ichikawa.chiba.jp
ichinomiya.chiba.jp
inzai.chiba.jp
isumi.chiba.jp
kamagaya.chiba.jp
kamogawa.chiba.jp
kashiwa.chiba.jp
katori.chiba.jp
katsuura.chiba.jp
kimitsu.chiba.jp
kisarazu.chiba.jp
kozaki.chiba.jp
kujukuri.chiba.jp
kyonan.chiba.jp
matsudo.chiba.jp
midori.chiba.jp
mihama.chiba.jp
minamiboso.chiba.jp
mobara.chiba.jp
mutsuzawa.chiba.jp
nagara.chiba.jp
nagareyama.chiba.jp
narashino.chiba.jp
narita.chiba.jp
noda.chiba.jp
oamishirasato.chiba.jp
omigawa.chiba.jp
onjuku.chiba.jp
otaki.chiba.jp
sakae.chiba.jp
sakura.chiba.jp
shimofusa.chiba.jp
shirako.chiba.jp
shiroi.chiba.jp
shisui.chiba.jp
sodegaura.chiba.jp
sosa.chiba.jp
tako.chiba.jp
tateyama.chiba.jp
togane.chiba.jp
tohnosho.chiba.jp
tomisato.chiba.jp
urayasu.chiba.jp
yachimata.chiba.jp
yachiyo.chiba.jp
yokaichiba.chiba.jp
yokoshibahikari.chiba.jp
yotsukaido.chiba.jp
ainan.ehime.jp
honai.ehime.jp
ikata.ehime.jp
imabari.ehime.jp
iyo.ehime.jp
kamijima.ehime.jp
kihoku.ehime.jp
kumakogen.ehime.jp
masaki.ehime.jp
matsuno.ehime.jp
matsuyama.ehime.jp
namikata.ehime.jp
niihama.ehime.jp
ozu.ehime.jp
saijo.ehime.jp
seiyo.ehime.jp
shikokuchuo.ehime.jp
tobe.ehime.jp
toon.ehime.jp
uchiko.ehime.jp
uwajima.ehime.jp
yawatahama.ehime.jp
echizen.fukui.jp
eiheiji.fukui.jp
fukui.fukui.jp
ikeda.fukui.jp
katsuyama.fukui.jp
mihama.fukui.jp
minamiechizen.fukui.jp
obama.fukui.jp
ohi.fukui.jp
ono.fukui.jp
sabae.fukui.jp
sakai.fukui.jp
takahama.fukui.jp
tsuruga.fukui.jp
wakasa.fukui.jp
ashiya.fukuoka.jp
buzen.fukuoka.jp
chikugo.fukuoka.jp
chikuho.fukuoka.jp
chikujo.fukuoka.jp
chikushino.fukuoka.jp
chikuzen.fukuoka.jp
chuo.fukuoka.jp
dazaifu.fukuoka.jp
fukuchi.fukuoka.jp
hakata.fukuoka.jp
higashi.fukuoka.jp
hirokawa.fukuoka.jp
hisayama.fukuoka.jp
iizuka.fukuoka.jp
inatsuki.fukuoka.jp
kaho.fukuoka.jp
kasuga.fukuoka.jp
kasuya.fukuoka.jp
kawara.fukuoka.jp
keisen.fukuoka.jp
koga.fukuoka.jp
kurate.fukuoka.jp
kurogi.fukuoka.jp
kurume.fukuoka.jp
minami.fukuoka.jp
miyako.fukuoka.jp
miyama.fukuoka.jp
miyawaka.fukuoka.jp
mizumaki.fukuoka.jp
munakata.fukuoka.jp
nakagawa.fukuoka.jp
nakama.fukuoka.jp
nishi.fukuoka.jp
nogata.fukuoka.jp
ogori.fukuoka.jp
okagaki.fukuoka.jp
okawa.fukuoka.jp
oki.fukuoka.jp
omuta.fukuoka.jp
onga.fukuoka.jp
onojo.fukuoka.jp
oto.fukuoka.jp
saigawa.fukuoka.jp
sasaguri.fukuoka.jp
shingu.fukuoka.jp
shinyoshitomi.fukuoka.jp
shonai.fukuoka.jp
soeda.fukuoka.jp
sue.fukuoka.jp
tachiarai.fukuoka.jp
tagawa.fukuoka.jp
takata.fukuoka.jp
toho.fukuoka.jp
toyotsu.fukuoka.jp
tsuiki.fukuoka.jp
ukiha.fukuoka.jp
umi.fukuoka.jp
usui.fukuoka.jp
yamada.fukuoka.jp
yame.fukuoka.jp
yanagawa.fukuoka.jp
yukuhashi.fukuoka.jp
aizubange.fukushima.jp
aizumisato.fukushima.jp
aizuwakamatsu.fukushima.jp
asakawa.fukushima.jp
bandai.fukushima.jp
date.fukushima.jp
fukushima.fukushima.jp
furudono.fukushima.jp
futaba.fukushima.jp
hanawa.fukushima.jp
higashi.fukushima.jp
hirata.fukushima.jp
hirono.fukushima.jp
iitate.fukushima.jp
inawashiro.fukushima.jp
ishikawa.fukushima.jp
iwaki.fukushima.jp
izumizaki.fukushima.jp
kagamiishi.fukushima.jp
kaneyama.fukushima.jp
kawamata.fukushima.jp
kitakata.fukushima.jp
kitashiobara.fukushima.jp
koori.fukushima.jp
koriyama.fukushima.jp
kunimi.fukushima.jp
miharu.fukushima.jp
mishima.fukushima.jp
namie.fukushima.jp
nango.fukushima.jp
nishiaizu.fukushima.jp
nishigo.fukushima.jp
okuma.fukushima.jp
omotego.fukushima.jp
ono.fukushima.jp
otama.fukushima.jp
samegawa.fukushima.jp
shimogo.fukushima.jp
shirakawa.fukushima.jp
showa.fukushima.jp
soma.fukushima.jp
sukagawa.fukushima.jp
taishin.fukushima.jp
tamakawa.fukushima.jp
tanagura.fukushima.jp
tenei.fukushima.jp
yabuki.fukushima.jp
yamato.fukushima.jp
yamatsuri.fukushima.jp
yanaizu.fukushima.jp
yugawa.fukushima.jp
anpachi.gifu.jp
ena.gifu.jp
gifu.gifu.jp
ginan.gifu.jp
godo.gifu.jp
gujo.gifu.jp
hashima.gifu.jp
hichiso.gifu.jp
hida.gifu.jp
higashishirakawa.gifu.jp
ibigawa.gifu.jp
ikeda.gifu.jp
kakamigahara.gifu.jp
kani.gifu.jp
kasahara.gifu.jp
kasamatsu.gifu.jp
kawaue.gifu.jp
kitagata.gifu.jp
mino.gifu.jp
minokamo.gifu.jp
mitake.gifu.jp
mizunami.gifu.jp
motosu.gifu.jp
nakatsugawa.gifu.jp
ogaki.gifu.jp
sakahogi.gifu.jp
seki.gifu.jp
sekigahara.gifu.jp
shirakawa.gifu.jp
tajimi.gifu.jp
takayama.gifu.jp
tarui.gifu.jp
toki.gifu.jp
tomika.gifu.jp
wanouchi.gifu.jp
yamagata.gifu.jp
yaotsu.gifu.jp
yoro.gifu.jp
annaka.gunma.jp
chiyoda.gunma.jp
fujioka.gunma.jp
higashiagatsuma.gunma.jp
isesaki.gunma.jp
itakura.gunma.jp
kanna.gunma.jp
kanra.gunma.jp
katashina.gunma.jp
kawaba.gunma.jp
kiryu.gunma.jp
kusatsu.gunma.jp
maebashi.gunma.jp
meiwa.gunma.jp
midori.gunma.jp
minakami.gunma.jp
naganohara.gunma.jp
nakanojo.gunma.jp
nanmoku.gunma.jp
numata.gunma.jp
oizumi.gunma.jp
ora.gunma.jp
ota.gunma.jp
shibukawa.gunma.jp
shimonita.gunma.jp
shinto.gunma.jp
showa.gunma.jp
takasaki.gunma.jp
takayama.gunma.jp
tamamura.gunma.jp
tatebayashi.gunma.jp
tomioka.gunma.jp
tsukiyono.gunma.jp
tsumagoi.gunma.jp
ueno.gunma.jp
yoshioka.gunma.jp
asaminami.hiroshima.jp
daiwa.hiroshima.jp
etajima.hiroshima.jp
fuchu.hiroshima.jp
fukuyama.hiroshima.jp
hatsukaichi.hiroshima.jp
higashihiroshima.hiroshima.jp
hongo.hiroshima.jp
jinsekikogen.hiroshima.jp
kaita.hiroshima.jp
kui.hiroshima.jp
kumano.hiroshima.jp
kure.hiroshima.jp
mihara.hiroshima.jp
miyoshi.hiroshima.jp
naka.hiroshima.jp
onomichi.hiroshima.jp
osakikamijima.hiroshima.jp
otake.hiroshima.jp
saka.hiroshima.jp
sera.hiroshima.jp
seranishi.hiroshima.jp
shinichi.hiroshima.jp
shobara.hiroshima.jp
takehara.hiroshima.jp
abashiri.hokkaido.jp
abira.hokkaido.jp
aibetsu.hokkaido.jp
akabira.hokkaido.jp
akkeshi.hokkaido.jp
asahikawa.hokkaido.jp
ashibetsu.hokkaido.jp
ashoro.hokkaido.jp
assabu.hokkaido.jp
atsuma.hokkaido.jp
bibai.hokkaido.jp
biei.hokkaido.jp
bifuka.hokkaido.jp
bihoro.hokkaido.jp
biratori.hokkaido.jp
chippubetsu.hokkaido.jp
chitose.hokkaido.jp
date.hokkaido.jp
ebetsu.hokkaido.jp
embetsu.hokkaido.jp
eniwa.hokkaido.jp
erimo.hokkaido.jp
esan.hokkaido.jp
esashi.hokkaido.jp
fukagawa.hokkaido.jp
fukushima.hokkaido.jp
furano.hokkaido.jp
furubira.hokkaido.jp
haboro.hokkaido.jp
hakodate.hokkaido.jp
hamatonbetsu.hokkaido.jp
hidaka.hokkaido.jp
higashikagura.hokkaido.jp
higashikawa.hokkaido.jp
hiroo.hokkaido.jp
hokuryu.hokkaido.jp
hokuto.hokkaido.jp
honbetsu.hokkaido.jp
horokanai.hokkaido.jp
horonobe.hokkaido.jp
ikeda.hokkaido.jp
imakane.hokkaido.jp
ishikari.hokkaido.jp
iwamizawa.hokkaido.jp
iwanai.hokkaido.jp
kamifurano.hokkaido.jp
kamikawa.hokkaido.jp
kamishihoro.hokkaido.jp
kamisunagawa.hokkaido.jp
kamoenai.hokkaido.jp
kayabe.hokkaido.jp
kembuchi.hokkaido.jp
kikonai.hokkaido.jp
kimobetsu.hokkaido.jp
kitahiroshima.hokkaido.jp
kitami.hokkaido.jp
kiyosato.hokkaido.jp
koshimizu.hokkaido.jp
kunneppu.hokkaido.jp
kuriyama.hokkaido.jp
kuromatsunai.hokkaido.jp
kushiro.hokkaido.jp
kutchan.hokkaido.jp
kyowa.hokkaido.jp
mashike.hokkaido.jp
matsumae.hokkaido.jp
mikasa.hokkaido.jp
minamifurano.hokkaido.jp
mombetsu.hokkaido.jp
moseushi.hokkaido.jp
mukawa.hokkaido.jp
muroran.hokkaido.jp
naie.hokkaido.jp
nakagawa.hokkaido.jp
nakasatsunai.hokkaido.jp
nakatombetsu.hokkaido.jp
nanae.hokkaido.jp
nanporo.hokkaido.jp
nayoro.hokkaido.jp
nemuro.hokkaido.jp
niikappu.hokkaido.jp
niki.hokkaido.jp
nishiokoppe.hokkaido.jp
noboribetsu.hokkaido.jp
numata.hokkaido.jp
obihiro.hokkaido.jp
obira.hokkaido.jp
oketo.hokkaido.jp
okoppe.hokkaido.jp
otaru.hokkaido.jp
otobe.hokkaido.jp
otofuke.hokkaido.jp
otoineppu.hokkaido.jp
oumu.hokkaido.jp
ozora.hokkaido.jp
pippu.hokkaido.jp
rankoshi.hokkaido.jp
rebun.hokkaido.jp
rikubetsu.hokkaido.jp
rishiri.hokkaido.jp
rishirifuji.hokkaido.jp
saroma.hokkaido.jp
sarufutsu.hokkaido.jp
shakotan.hokkaido.jp
shari.hokkaido.jp
shibecha.hokkaido.jp
shibetsu.hokkaido.jp
shikabe.hokkaido.jp
shikaoi.hokkaido.jp
shimamaki.hokkaido.jp
shimizu.hokkaido.jp
shimokawa.hokkaido.jp
shinshinotsu.hokkaido.jp
shintoku.hokkaido.jp
shiranuka.hokkaido.jp
shiraoi.hokkaido.jp
shiriuchi.hokkaido.jp
sobetsu.hokkaido.jp
sunagawa.hokkaido.jp
taiki.hokkaido.jp
takasu.hokkaido.jp
takikawa.hokkaido.jp
takinoue.hokkaido.jp
teshikaga.hokkaido.jp
tobetsu.hokkaido.jp
tohma.hokkaido.jp
tomakomai.hokkaido.jp
tomari.hokkaido.jp
toya.hokkaido.jp
toyako.hokkaido.jp
toyotomi.hokkaido.jp
toyoura.hokkaido.jp
tsubetsu.hokkaido.jp
tsukigata.hokkaido.jp
urakawa.hokkaido.jp
urausu.hokkaido.jp
uryu.hokkaido.jp
utashinai.hokkaido.jp
wakkanai.hokkaido.jp
wassamu.hokkaido.jp
yakumo.hokkaido.jp
yoichi.hokkaido.jp
aioi.hyogo.jp
akashi.hyogo.jp
ako.hyogo.jp
amagasaki.hyogo.jp
aogaki.hyogo.jp
asago.hyogo.jp
ashiya.hyogo.jp
awaji.hyogo.jp
fukusaki.hyogo.jp
goshiki.hyogo.jp
harima.hyogo.jp
himeji.hyogo.jp
ichikawa.hyogo.jp
inagawa.hyogo.jp
itami.hyogo.jp
kakogawa.hyogo.jp
kamigori.hyogo.jp
kamikawa.hyogo.jp
kasai.hyogo.jp
kasuga.hyogo.jp
kawanishi.hyogo.jp
miki.hyogo.jp
minamiawaji.hyogo.jp
nishinomiya.hyogo.jp
nishiwaki.hyogo.jp
ono.hyogo.jp
sanda.hyogo.jp
sannan.hyogo.jp
sasayama.hyogo.jp
sayo.hyogo.jp
shingu.hyogo.jp
shinonsen.hyogo.jp
shiso.hyogo.jp
sumoto.hyogo.jp
taishi.hyogo.jp
taka.hyogo.jp
takarazuka.hyogo.jp
takasago.hyogo.jp
takino.hyogo.jp
tamba.hyogo.jp
tatsuno.hyogo.jp
toyooka.hyogo.jp
yabu.hyogo.jp
yashiro.hyogo.jp
yoka.hyogo.jp
yokawa.hyogo.jp
ami.ibaraki.jp
asahi.ibaraki.jp
bando.ibaraki.jp
chikusei.ibaraki.jp
daigo.ibaraki.jp
fujishiro.ibaraki.jp
hitachi.ibaraki.jp
hitachinaka.ibaraki.jp
hitachiomiya.ibaraki.jp
hitachiota.ibaraki.jp
ibaraki.ibaraki.jp
ina.ibaraki.jp
inashiki.ibaraki.jp
itako.ibaraki.jp
iwama.ibaraki.jp
joso.ibaraki.jp
kamisu.ibaraki.jp
kasama.ibaraki.jp
kashima.ibaraki.jp
kasumigaura.ibaraki.jp
koga.ibaraki.jp
miho.ibaraki.jp
mito.ibaraki.jp
moriya.ibaraki.jp
naka.ibaraki.jp
namegata.ibaraki.jp
oarai.ibaraki.jp
ogawa.ibaraki.jp
omitama.ibaraki.jp
ryugasaki.ibaraki.jp
sakai.ibaraki.jp
sakuragawa.ibaraki.jp
shimodate.ibaraki.jp
shimotsuma.ibaraki.jp
shirosato.ibaraki.jp
sowa.ibaraki.jp
suifu.ibaraki.jp
takahagi.ibaraki.jp
tamatsukuri.ibaraki.jp
tokai.ibaraki.jp
tomobe.ibaraki.jp
tone.ibaraki.jp
toride.ibaraki.jp
tsuchiura.ibaraki.jp
tsukuba.ibaraki.jp
uchihara.ibaraki.jp
ushiku.ibaraki.jp
yachiyo.ibaraki.jp
yamagata.ibaraki.jp
yawara.ibaraki.jp
yuki.ibaraki.jp
anamizu.ishikawa.jp
hakui.ishikawa.jp
hakusan.ishikawa.jp
kaga.ishikawa.jp
kahoku.ishikawa.jp
kanazawa.ishikawa.jp
kawakita.ishikawa.jp
komatsu.ishikawa.jp
nakanoto.ishikawa.jp
nanao.ishikawa.jp
nomi.ishikawa.jp
nonoichi.ishikawa.jp
noto.ishikawa.jp
shika.ishikawa.jp
suzu.ishikawa.jp
tsubata.ishikawa.jp
tsurugi.ishikawa.jp
uchinada.ishikawa.jp
wajima.ishikawa.jp
fudai.iwate.jp
fujisawa.iwate.jp
hanamaki.iwate.jp
hiraizumi.iwate.jp
hirono.iwate.jp
ichinohe.iwate.jp
ichinoseki.iwate.jp
iwaizumi.iwate.jp
iwate.iwate.jp
joboji.iwate.jp
kamaishi.iwate.jp
kanegasaki.iwate.jp
karumai.iwate.jp
kawai.iwate.jp
kitakami.iwate.jp
kuji.iwate.jp
kunohe.iwate.jp
kuzumaki.iwate.jp
miyako.iwate.jp
mizusawa.iwate.jp
morioka.iwate.jp
ninohe.iwate.jp
noda.iwate.jp
ofunato.iwate.jp
oshu.iwate.jp
otsuchi.iwate.jp
rikuzentakata.iwate.jp
shiwa.iwate.jp
shizukuishi.iwate.jp
sumita.iwate.jp
tanohata.iwate.jp
tono.iwate.jp
yahaba.iwate.jp
yamada.iwate.jp
ayagawa.kagawa.jp
higashikagawa.kagawa.jp
kanonji.kagawa.jp
kotohira.kagawa.jp
manno.kagawa.jp
marugame.kagawa.jp
mitoyo.kagawa.jp
naoshima.kagawa.jp
sanuki.kagawa.jp
tadotsu.kagawa.jp
takamatsu.kagawa.jp
tonosho.kagawa.jp
uchinomi.kagawa.jp
utazu.kagawa.jp
zentsuji.kagawa.jp
akune.kagoshima.jp
amami.kagoshima.jp
hioki.kagoshima.jp
isa.kagoshima.jp
isen.kagoshima.jp
izumi.kagoshima.jp
kagoshima.kagoshima.jp
kanoya.kagoshima.jp
kawanabe.kagoshima.jp
kinko.kagoshima.jp
kouyama.kagoshima.jp
makurazaki.kagoshima.jp
matsumoto.kagoshima.jp
minamitane.kagoshima.jp
nakatane.kagoshima.jp
nishinoomote.kagoshima.jp
satsumasendai.kagoshima.jp
soo.kagoshima.jp
tarumizu.kagoshima.jp
yusui.kagoshima.jp
aikawa.kanagawa.jp
atsugi.kanagawa.jp
ayase.kanagawa.jp
chigasaki.kanagawa.jp
ebina.kanagawa.jp
fujisawa.kanagawa.jp
hadano.kanagawa.jp
hakone.kanagawa.jp
hiratsuka.kanagawa.jp
isehara.kanagawa.jp
kaisei.kanagawa.jp
kamakura.kanagawa.jp
kiyokawa.kanagawa.jp
matsuda.kanagawa.jp
minamiashigara.kanagawa.jp
miura.kanagawa.jp
nakai.kanagawa.jp
ninomiya.kanagawa.jp
odawara.kanagawa.jp
oi.kanagawa.jp
oiso.kanagawa.jp
sagamihara.kanagawa.jp
samukawa.kanagawa.jp
tsukui.kanagawa.jp
yamakita.kanagawa.jp
yamato.kanagawa.jp
yokosuka.kanagawa.jp
yugawara.kanagawa.jp
zama.kanagawa.jp
zushi.kanagawa.jp
aki.kochi.jp
geisei.kochi.jp
hidaka.kochi.jp
higashitsuno.kochi.jp
ino.kochi.jp
kagami.kochi.jp
kami.kochi.jp
kitagawa.kochi.jp
kochi.kochi.jp
mihara.kochi.jp
motoyama.kochi.jp
muroto.kochi.jp
nahari.kochi.jp
nakamura.kochi.jp
nankoku.kochi.jp
nishitosa.kochi.jp
niyodogawa.kochi.jp
ochi.kochi.jp
okawa.kochi.jp
otoyo.kochi.jp
otsuki.kochi.jp
sakawa.kochi.jp
sukumo.kochi.jp
susaki.kochi.jp
tosa.kochi.jp
tosashimizu.kochi.jp
toyo.kochi.jp
tsuno.kochi.jp
umaji.kochi.jp
yasuda.kochi.jp
yusuhara.kochi.jp
amakusa.kumamoto.jp
arao.kumamoto.jp
aso.kumamoto.jp
choyo.kumamoto.jp
gyokuto.kumamoto.jp
kamiamakusa.kumamoto.jp
kikuchi.kumamoto.jp
kumamoto.kumamoto.jp
mashiki.kumamoto.jp
mifune.kumamoto.jp
minamata.kumamoto.jp
minamioguni.kumamoto.jp
nagasu.kumamoto.jp
nishihara.kumamoto.jp
oguni.kumamoto.jp
ozu.kumamoto.jp
sumoto.kumamoto.jp
takamori.kumamoto.jp
uki.kumamoto.jp
uto.kumamoto.jp
yamaga.kumamoto.jp
yamato.kumamoto.jp
yatsushiro.kumamoto.jp
ayabe.kyoto.jp
fukuchiyama.kyoto.jp
higashiyama.kyoto.jp
ide.kyoto.jp
ine.kyoto.jp
joyo.kyoto.jp
kameoka.kyoto.jp
kamo.kyoto.jp
kita.kyoto.jp
kizu.kyoto.jp
kumiyama.kyoto.jp
kyotamba.kyoto.jp
kyotanabe.kyoto.jp
kyotango.kyoto.jp
maizuru.kyoto.jp
minami.kyoto.jp
minamiyamashiro.kyoto.jp
miyazu.kyoto.jp
muko.kyoto.jp
nagaokakyo.kyoto.jp
nakagyo.kyoto.jp
nantan.kyoto.jp
oyamazaki.kyoto.jp
sakyo.kyoto.jp
seika.kyoto.jp
tanabe.kyoto.jp
uji.kyoto.jp
ujitawara.kyoto.jp
wazuka.kyoto.jp
yamashina.kyoto.jp
yawata.kyoto.jp
asahi.mie.jp
inabe.mie.jp
ise.mie.jp
kameyama.mie.jp
kawagoe.mie.jp
kiho.mie.jp
kisosaki.mie.jp
kiwa.mie.jp
komono.mie.jp
kumano.mie.jp
kuwana.mie.jp
matsusaka.mie.jp
meiwa.mie.jp
mihama.mie.jp
minamiise.mie.jp
misugi.mie.jp
miyama.mie.jp
nabari.mie.jp
shima.mie.jp
suzuka.mie.jp
tado.mie.jp
taiki.mie.jp
taki.mie.jp
tamaki.mie.jp
toba.mie.jp
tsu.mie.jp
udono.mie.jp
ureshino.mie.jp
watarai.mie.jp
yokkaichi.mie.jp
furukawa.miyagi.jp
higashimatsushima.miyagi.jp
ishinomaki.miyagi.jp
iwanuma.miyagi.jp
kakuda.miyagi.jp
kami.miyagi.jp
kawasaki.miyagi.jp
marumori.miyagi.jp
matsushima.miyagi.jp
minamisanriku.miyagi.jp
misato.miyagi.jp
murata.miyagi.jp
natori.miyagi.jp
ogawara.miyagi.jp
ohira.miyagi.jp
onagawa.miyagi.jp
osaki.miyagi.jp
rifu.miyagi.jp
semine.miyagi.jp
shibata.miyagi.jp
shichikashuku.miyagi.jp
shikama.miyagi.jp
shiogama.miyagi.jp
shiroishi.miyagi.jp
tagajo.miyagi.jp
taiwa.miyagi.jp
tome.miyagi.jp
tomiya.miyagi.jp
wakuya.miyagi.jp
watari.miyagi.jp
yamamoto.miyagi.jp
zao.miyagi.jp
aya.miyazaki.jp
ebino.miyazaki.jp
gokase.miyazaki.jp
hyuga.miyazaki.jp
kadogawa.miyazaki.jp
kawaminami.miyazaki.jp
kijo.miyazaki.jp
kitagawa.miyazaki.jp
kitakata.miyazaki.jp
kitaura.miyazaki.jp
kobayashi.miyazaki.jp
kunitomi.miyazaki.jp
kushima.miyazaki.jp
mimata.miyazaki.jp
miyakonojo.miyazaki.jp
miyazaki.miyazaki.jp
morotsuka.miyazaki.jp
nichinan.miyazaki.jp
nishimera.miyazaki.jp
nobeoka.miyazaki.jp
saito.miyazaki.jp
shiiba.miyazaki.jp
shintomi.miyazaki.jp
takaharu.miyazaki.jp
takanabe.miyazaki.jp
takazaki.miyazaki.jp
tsuno.miyazaki.jp
achi.nagano.jp
agematsu.nagano.jp
anan.nagano.jp
aoki.nagano.jp
asahi.nagano.jp
azumino.nagano.jp
chikuhoku.nagano.jp
chikuma.nagano.jp
chino.nagano.jp
fujimi.nagano.jp
hakuba.nagano.jp
hara.nagano.jp
hiraya.nagano.jp
iida.nagano.jp
iijima.nagano.jp
iiyama.nagano.jp
iizuna.nagano.jp
ikeda.nagano.jp
ikusaka.nagano.jp
ina.nagano.jp
karuizawa.nagano.jp
kawakami.nagano.jp
kiso.nagano.jp
kisofukushima.nagano.jp
kitaaiki.nagano.jp
komagane.nagano.jp
komoro.nagano.jp
matsukawa.nagano.jp
matsumoto.nagano.jp
miasa.nagano.jp
minamiaiki.nagano.jp
minamimaki.nagano.jp
minamiminowa.nagano.jp
minowa.nagano.jp
miyada.nagano.jp
miyota.nagano.jp
mochizuki.nagano.jp
nagano.nagano.jp
nagawa.nagano.jp
nagiso.nagano.jp
nakagawa.nagano.jp
nakano.nagano.jp
nozawaonsen.nagano.jp
obuse.nagano.jp
ogawa.nagano.jp
okaya.nagano.jp
omachi.nagano.jp
omi.nagano.jp
ookuwa.nagano.jp
ooshika.nagano.jp
otaki.nagano.jp
otari.nagano.jp
sakae.nagano.jp
sakaki.nagano.jp
saku.nagano.jp
sakuho.nagano.jp
shimosuwa.nagano.jp
shinanomachi.nagano.jp
shiojiri.nagano.jp
suwa.nagano.jp
suzaka.nagano.jp
takagi.nagano.jp
takamori.nagano.jp
takayama.nagano.jp
tateshina.nagano.jp
tatsuno.nagano.jp
togakushi.nagano.jp
togura.nagano.jp
tomi.nagano.jp
ueda.nagano.jp
wada.nagano.jp
yamagata.nagano.jp
yamanouchi.nagano.jp
yasaka.nagano.jp
yasuoka.nagano.jp
chijiwa.nagasaki.jp
futsu.nagasaki.jp
goto.nagasaki.jp
hasami.nagasaki.jp
hirado.nagasaki.jp
iki.nagasaki.jp
isahaya.nagasaki.jp
kawatana.nagasaki.jp
kuchinotsu.nagasaki.jp
matsuura.nagasaki.jp
nagasaki.nagasaki.jp
obama.nagasaki.jp
omura.nagasaki.jp
oseto.nagasaki.jp
saikai.nagasaki.jp
sasebo.nagasaki.jp
seihi.nagasaki.jp
shimabara.nagasaki.jp
shinkamigoto.nagasaki.jp
togitsu.nagasaki.jp
tsushima.nagasaki.jp
unzen.nagasaki.jp
ando.nara.jp
gose.nara.jp
heguri.nara.jp
higashiyoshino.nara.jp
ikaruga.nara.jp
ikoma.nara.jp
kamikitayama.nara.jp
kanmaki.nara.jp
kashiba.nara.jp
kashihara.nara.jp
katsuragi.nara.jp
kawai.nara.jp
kawakami.nara.jp
kawanishi.nara.jp
koryo.nara.jp
kurotaki.nara.jp
mitsue.nara.jp
miyake.nara.jp
nara.nara.jp
nosegawa.nara.jp
oji.nara.jp
ouda.nara.jp
oyodo.nara.jp
sakurai.nara.jp
sango.nara.jp
shimoichi.nara.jp
shimokitayama.nara.jp
shinjo.nara.jp
soni.nara.jp
takatori.nara.jp
tawaramoto.nara.jp
tenkawa.nara.jp
tenri.nara.jp
uda.nara.jp
yamatokoriyama.nara.jp
yamatotakada.nara.jp
yamazoe.nara.jp
yoshino.nara.jp
aga.niigata.jp
agano.niigata.jp
gosen.niigata.jp
itoigawa.niigata.jp
izumozaki.niigata.jp
joetsu.niigata.jp
kamo.niigata.jp
kariwa.niigata.jp
kashiwazaki.niigata.jp
minamiuonuma.niigata.jp
mitsuke.niigata.jp
muika.niigata.jp
murakami.niigata.jp
myoko.niigata.jp
nagaoka.niigata.jp
niigata.niigata.jp
ojiya.niigata.jp
omi.niigata.jp
sado.niigata.jp
sanjo.niigata.jp
seiro.niigata.jp
seirou.niigata.jp
sekikawa.niigata.jp
shibata.niigata.jp
tagami.niigata.jp
tainai.niigata.jp
tochio.niigata.jp
tokamachi.niigata.jp
tsubame.niigata.jp
tsunan.niigata.jp
uonuma.niigata.jp
yahiko.niigata.jp
yoita.niigata.jp
yuzawa.niigata.jp
beppu.oita.jp
bungoono.oita.jp
bungotakada.oita.jp
hasama.oita.jp
hiji.oita.jp
himeshima.oita.jp
hita.oita.jp
kamitsue.oita.jp
kokonoe.oita.jp
kuju.oita.jp
kunisaki.oita.jp
kusu.oita.jp
oita.oita.jp
saiki.oita.jp
taketa.oita.jp
tsukumi.oita.jp
usa.oita.jp
usuki.oita.jp
yufu.oita.jp
akaiwa.okayama.jp
asakuchi.okayama.jp
bizen.okayama.jp
hayashima.okayama.jp
ibara.okayama.jp
kagamino.okayama.jp
kasaoka.okayama.jp
kibichuo.okayama.jp
kumenan.okayama.jp
kurashiki.okayama.jp
maniwa.okayama.jp
misaki.okayama.jp
nagi.okayama.jp
niimi.okayama.jp
nishiawakura.okayama.jp
okayama.okayama.jp
satosho.okayama.jp
setouchi.okayama.jp
shinjo.okayama.jp
shoo.okayama.jp
soja.okayama.jp
takahashi.okayama.jp
tamano.okayama.jp
tsuyama.okayama.jp
wake.okayama.jp
yakage.okayama.jp
aguni.okinawa.jp
ginowan.okinawa.jp
ginoza.okinawa.jp
gushikami.okinawa.jp
haebaru.okinawa.jp
higashi.okinawa.jp
hirara.okinawa.jp
iheya.okinawa.jp
ishigaki.okinawa.jp
ishikawa.okinawa.jp
itoman.okinawa.jp
izena.okinawa.jp
kadena.okinawa.jp
kin.okinawa.jp
kitadaito.okinawa.jp
kitanakagusuku.okinawa.jp
kumejima.okinawa.jp
kunigami.okinawa.jp
minamidaito.okinawa.jp
motobu.okinawa.jp
nago.okinawa.jp
naha.okinawa.jp
nakagusuku.okinawa.jp
nakijin.okinawa.jp
nanjo.okinawa.jp
nishihara.okinawa.jp
ogimi.okinawa.jp
okinawa.okinawa.jp
onna.okinawa.jp
shimoji.okinawa.jp
taketomi.okinawa.jp
tarama.okinawa.jp
tokashiki.okinawa.jp
tomigusuku.okinawa.jp
tonaki.okinawa.jp
urasoe.okinawa.jp
uruma.okinawa.jp
yaese.okinawa.jp
yomitan.okinawa.jp
yonabaru.okinawa.jp
yonaguni.okinawa.jp
zamami.okinawa.jp
abeno.osaka.jp
chihayaakasaka.osaka.jp
chuo.osaka.jp
daito.osaka.jp
fujiidera.osaka.jp
habikino.osaka.jp
hannan.osaka.jp
higashiosaka.osaka.jp
higashisumiyoshi.osaka.jp
higashiyodogawa.osaka.jp
hirakata.osaka.jp
ibaraki.osaka.jp
ikeda.osaka.jp
izumi.osaka.jp
izumiotsu.osaka.jp
izumisano.osaka.jp
kadoma.osaka.jp
kaizuka.osaka.jp
kanan.osaka.jp
kashiwara.osaka.jp
katano.osaka.jp
kawachinagano.osaka.jp
kishiwada.osaka.jp
kita.osaka.jp
kumatori.osaka.jp
matsubara.osaka.jp
minato.osaka.jp
minoh.osaka.jp
misaki.osaka.jp
moriguchi.osaka.jp
neyagawa.osaka.jp
nishi.osaka.jp
nose.osaka.jp
osakasayama.osaka.jp
sakai.osaka.jp
sayama.osaka.jp
sennan.osaka.jp
settsu.osaka.jp
shijonawate.osaka.jp
shimamoto.osaka.jp
suita.osaka.jp
tadaoka.osaka.jp
taishi.osaka.jp
tajiri.osaka.jp
takaishi.osaka.jp
takatsuki.osaka.jp
tondabayashi.osaka.jp
toyonaka.osaka.jp
toyono.osaka.jp
yao.osaka.jp
ariake.saga.jp
arita.saga.jp
fukudomi.saga.jp
genkai.saga.jp
hamatama.saga.jp
hizen.saga.jp
imari.saga.jp
kamimine.saga.jp
kanzaki.saga.jp
karatsu.saga.jp
kashima.saga.jp
kitagata.saga.jp
kitahata.saga.jp
kiyama.saga.jp
kouhoku.saga.jp
kyuragi.saga.jp
nishiarita.saga.jp
ogi.saga.jp
omachi.saga.jp
ouchi.saga.jp
saga.saga.jp
shiroishi.saga.jp
taku.saga.jp
tara.saga.jp
tosu.saga.jp
yoshinogari.saga.jp
arakawa.saitama.jp
asaka.saitama.jp
chichibu.saitama.jp
fujimi.saitama.jp
fujimino.saitama.jp
fukaya.saitama.jp
hanno.saitama.jp
hanyu.saitama.jp
hasuda.saitama.jp
hatogaya.saitama.jp
hatoyama.saitama.jp
hidaka.saitama.jp
higashichichibu.saitama.jp
higashimatsuyama.saitama.jp
honjo.saitama.jp
ina.saitama.jp
iruma.saitama.jp
iwatsuki.saitama.jp
kamiizumi.saitama.jp
kamikawa.saitama.jp
kamisato.saitama.jp
kasukabe.saitama.jp
kawagoe.saitama.jp
kawaguchi.saitama.jp
kawajima.saitama.jp
kazo.saitama.jp
kitamoto.saitama.jp
koshigaya.saitama.jp
kounosu.saitama.jp
kuki.saitama.jp
kumagaya.saitama.jp
matsubushi.saitama.jp
minano.saitama.jp
misato.saitama.jp
miyashiro.saitama.jp
miyoshi.saitama.jp
moroyama.saitama.jp
nagatoro.saitama.jp
namegawa.saitama.jp
niiza.saitama.jp
ogano.saitama.jp
ogawa.saitama.jp
ogose.saitama.jp
okegawa.saitama.jp
omiya.saitama.jp
otaki.saitama.jp
ranzan.saitama.jp
ryokami.saitama.jp
saitama.saitama.jp
sakado.saitama.jp
satte.saitama.jp
sayama.saitama.jp
shiki.saitama.jp
shiraoka.saitama.jp
soka.saitama.jp
sugito.saitama.jp
toda.saitama.jp
tokigawa.saitama.jp
tokorozawa.saitama.jp
tsurugashima.saitama.jp
urawa.saitama.jp
warabi.saitama.jp
yashio.saitama.jp
yokoze.saitama.jp
yono.saitama.jp
yorii.saitama.jp
yoshida.saitama.jp
yoshikawa.saitama.jp
yoshimi.saitama.jp
aisho.shiga.jp
gamo.shiga.jp
higashiomi.shiga.jp
hikone.shiga.jp
koka.shiga.jp
konan.shiga.jp
kosei.shiga.jp
koto.shiga.jp
kusatsu.shiga.jp
maibara.shiga.jp
moriyama.shiga.jp
nagahama.shiga.jp
nishiazai.shiga.jp
notogawa.shiga.jp
omihachiman.shiga.jp
otsu.shiga.jp
ritto.shiga.jp
ryuoh.shiga.jp
takashima.shiga.jp
takatsuki.shiga.jp
torahime.shiga.jp
toyosato.shiga.jp
yasu.shiga.jp
akagi.shimane.jp
ama.shimane.jp
gotsu.shimane.jp
hamada.shimane.jp
higashiizumo.shimane.jp
hikawa.shimane.jp
hikimi.shimane.jp
izumo.shimane.jp
kakinoki.shimane.jp
masuda.shimane.jp
matsue.shimane.jp
misato.shimane.jp
nishinoshima.shimane.jp
ohda.shimane.jp
okinoshima.shimane.jp
okuizumo.shimane.jp
shimane.shimane.jp
tamayu.shimane.jp
tsuwano.shimane.jp
unnan.shimane.jp
yakumo.shimane.jp
yasugi.shimane.jp
yatsuka.shimane.jp
arai.shizuoka.jp
atami.shizuoka.jp
fuji.shizuoka.jp
fujieda.shizuoka.jp
fujikawa.shizuoka.jp
fujinomiya.shizuoka.jp
fukuroi.shizuoka.jp
gotemba.shizuoka.jp
haibara.shizuoka.jp
hamamatsu.shizuoka.jp
higashiizu.shizuoka.jp
ito.shizuoka.jp
iwata.shizuoka.jp
izu.shizuoka.jp
izunokuni.shizuoka.jp
kakegawa.shizuoka.jp
kannami.shizuoka.jp
kawanehon.shizuoka.jp
kawazu.shizuoka.jp
kikugawa.shizuoka.jp
kosai.shizuoka.jp
makinohara.shizuoka.jp
matsuzaki.shizuoka.jp
minamiizu.shizuoka.jp
mishima.shizuoka.jp
morimachi.shizuoka.jp
nishiizu.shizuoka.jp
numazu.shizuoka.jp
omaezaki.shizuoka.jp
shimada.shizuoka.jp
shimizu.shizuoka.jp
shimoda.shizuoka.jp
shizuoka.shizuoka.jp
susono.shizuoka.jp
yaizu.shizuoka.jp
yoshida.shizuoka.jp
ashikaga.tochigi.jp
bato.tochigi.jp
haga.tochigi.jp
ichikai.tochigi.jp
iwafune.tochigi.jp
kaminokawa.tochigi.jp
kanuma.tochigi.jp
karasuyama.tochigi.jp
kuroiso.tochigi.jp
mashiko.tochigi.jp
mibu.tochigi.jp
moka.tochigi.jp
motegi.tochigi.jp
nasu.tochigi.jp
nasushiobara.tochigi.jp
nikko.tochigi.jp
nishikata.tochigi.jp
nogi.tochigi.jp
ohira.tochigi.jp
ohtawara.tochigi.jp
oyama.tochigi.jp
sakura.tochigi.jp
sano.tochigi.jp
shimotsuke.tochigi.jp
shioya.tochigi.jp
takanezawa.tochigi.jp
tochigi.tochigi.jp
tsuga.tochigi.jp
ujiie.tochigi.jp
utsunomiya.tochigi.jp
yaita.tochigi.jp
aizumi.tokushima.jp
anan.tokushima.jp
ichiba.tokushima.jp
itano.tokushima.jp
kainan.tokushima.jp
komatsushima.tokushima.jp
matsushige.tokushima.jp
mima.tokushima.jp
minami.tokushima.jp
miyoshi.tokushima.jp
mugi.tokushima.jp
nakagawa.tokushima.jp
naruto.tokushima.jp
sanagochi.tokushima.jp
shishikui.tokushima.jp
tokushima.tokushima.jp
wajiki.tokushima.jp
adachi.tokyo.jp
akiruno.tokyo.jp
akishima.tokyo.jp
aogashima.tokyo.jp
arakawa.tokyo.jp
bunkyo.tokyo.jp
chiyoda.tokyo.jp
chofu.tokyo.jp
chuo.tokyo.jp
edogawa.tokyo.jp
fuchu.tokyo.jp
fussa.tokyo.jp
hachijo.tokyo.jp
hachioji.tokyo.jp
hamura.tokyo.jp
higashikurume.tokyo.jp
higashimurayama.tokyo.jp
higashiyamato.tokyo.jp
hino.tokyo.jp
hinode.tokyo.jp
hinohara.tokyo.jp
inagi.tokyo.jp
itabashi.tokyo.jp
katsushika.tokyo.jp
kita.tokyo.jp
kiyose.tokyo.jp
kodaira.tokyo.jp
koganei.tokyo.jp
kokubunji.tokyo.jp
komae.tokyo.jp
koto.tokyo.jp
kouzushima.tokyo.jp
kunitachi.tokyo.jp
machida.tokyo.jp
meguro.tokyo.jp
minato.tokyo.jp
mitaka.tokyo.jp
mizuho.tokyo.jp
musashimurayama.tokyo.jp
musashino.tokyo.jp
nakano.tokyo.jp
nerima.tokyo.jp
ogasawara.tokyo.jp
okutama.tokyo.jp
ome.tokyo.jp
oshima.tokyo.jp
ota.tokyo.jp
setagaya.tokyo.jp
shibuya.tokyo.jp
shinagawa.tokyo.jp
shinjuku.tokyo.jp
suginami.tokyo.jp
sumida.tokyo.jp
tachikawa.tokyo.jp
taito.tokyo.jp
tama.tokyo.jp
toshima.tokyo.jp
chizu.tottori.jp
hino.tottori.jp
kawahara.tottori.jp
koge.tottori.jp
kotoura.tottori.jp
misasa.tottori.jp
nanbu.tottori.jp
nichinan.tottori.jp
sakaiminato.tottori.jp
tottori.tottori.jp
wakasa.tottori.jp
yazu.tottori.jp
yonago.tottori.jp
asahi.toyama.jp
fuchu.toyama.jp
fukumitsu.toyama.jp
funahashi.toyama.jp
himi.toyama.jp
imizu.toyama.jp
inami.toyama.jp
johana.toyama.jp
kamiichi.toyama.jp
kurobe.toyama.jp
nakaniikawa.toyama.jp
namerikawa.toyama.jp
nanto.toyama.jp
nyuzen.toyama.jp
oyabe.toyama.jp
taira.toyama.jp
takaoka.toyama.jp
tateyama.toyama.jp
toga.toyama.jp
tonami.toyama.jp
toyama.toyama.jp
unazuki.toyama.jp
uozu.toyama.jp
yamada.toyama.jp
arida.wakayama.jp
aridagawa.wakayama.jp
gobo.wakayama.jp
hashimoto.wakayama.jp
hidaka.wakayama.jp
hirogawa.wakayama.jp
inami.wakayama.jp
iwade.wakayama.jp
kainan.wakayama.jp
kamitonda.wakayama.jp
katsuragi.wakayama.jp
kimino.wakayama.jp
kinokawa.wakayama.jp
kitayama.wakayama.jp
koya.wakayama.jp
koza.wakayama.jp
kozagawa.wakayama.jp
kudoyama.wakayama.jp
kushimoto.wakayama.jp
mihama.wakayama.jp
misato.wakayama.jp
nachikatsuura.wakayama.jp
shingu.wakayama.jp
shirahama.wakayama.jp
taiji.wakayama.jp
tanabe.wakayama.jp
wakayama.wakayama.jp
yuasa.wakayama.jp
yura.wakayama.jp
asahi.yamagata.jp
funagata.yamagata.jp
higashine.yamagata.jp
iide.yamagata.jp
kahoku.yamagata.jp
kaminoyama.yamagata.jp
kaneyama.yamagata.jp
kawanishi.yamagata.jp
mamurogawa.yamagata.jp
mikawa.yamagata.jp
murayama.yamagata.jp
nagai.yamagata.jp
nakayama.yamagata.jp
nanyo.yamagata.jp
nishikawa.yamagata.jp
obanazawa.yamagata.jp
oe.yamagata.jp
oguni.yamagata.jp
ohkura.yamagata.jp
oishida.yamagata.jp
sagae.yamagata.jp
sakata.yamagata.jp
sakegawa.yamagata.jp
shinjo.yamagata.jp
shirataka.yamagata.jp
shonai.yamagata.jp
takahata.yamagata.jp
tendo.yamagata.jp
tozawa.yamagata.jp
tsuruoka.yamagata.jp
yamagata.yamagata.jp
yamanobe.yamagata.jp
yonezawa.yamagata.jp
yuza.yamagata.jp
abu.yamaguchi.jp
hagi.yamaguchi.jp
hikari.yamaguchi.jp
hofu.yamaguchi.jp
iwakuni.yamaguchi.jp
kudamatsu.yamaguchi.jp
mitou.yamaguchi.jp
nagato.yamaguchi.jp
oshima.yamaguchi.jp
shimonoseki.yamaguchi.jp
shunan.yamaguchi.jp
tabuse.yamaguchi.jp
tokuyama.yamaguchi.jp
toyota.yamaguchi.jp
ube.yamaguchi.jp
yuu.yamaguchi.jp
chuo.yamanashi.jp
doshi.yamanashi.jp
fuefuki.yamanashi.jp
fujikawa.yamanashi.jp
fujikawaguchiko.yamanashi.jp
fujiyoshida.yamanashi.jp
hayakawa.yamanashi.jp
hokuto.yamanashi.jp
ichikawamisato.yamanashi.jp
kai.yamanashi.jp
kofu.yamanashi.jp
koshu.yamanashi.jp
kosuge.yamanashi.jp
minami-alps.yamanashi.jp
minobu.yamanashi.jp
nakamichi.yamanashi.jp
nanbu.yamanashi.jp
narusawa.yamanashi.jp
nirasaki.yamanashi.jp
nishikatsura.yamanashi.jp
oshino.yamanashi.jp
otsuki.yamanashi.jp
showa.yamanashi.jp
tabayama.yamanashi.jp
tsuru.yamanashi.jp
uenohara.yamanashi.jp
yamanakako.yamanashi.jp
yamanashi.yamanashi.jp

// ke : http://www.kenic.or.ke/index.php/en/ke-domains/ke-domains
ke
ac.ke
co.ke
go.ke
info.ke
me.ke
mobi.ke
ne.ke
or.ke
sc.ke

// kg : http://www.domain.kg/dmn_n.html
kg
org.kg
net.kg
com.kg
edu.kg
gov.kg
mil.kg

// kh : http://www.mptc.gov.kh/dns_registration.htm
*.kh

// ki : http://www.ki/dns/index.html
ki
edu.ki
biz.ki
net.ki
org.ki
gov.ki
info.ki
com.ki

// km : https://en.wikipedia.org/wiki/.km
// http://www.domaine.km/documents/charte.doc
km
org.km
nom.km
gov.km
prd.km
tm.km
edu.km
mil.km
ass.km
com.km
// These are only mentioned as proposed suggestions at domaine.km, but
// https://en.wikipedia.org/wiki/.km says they're available for registration:
coop.km
asso.km
presse.km
medecin.km
notaires.km
pharmaciens.km
veterinaire.km
gouv.km

// kn : https://en.wikipedia.org/wiki/.kn
// http://www.dot.kn/domainRules.html
kn
net.kn
org.kn
edu.kn
gov.kn

// kp : http://www.kcce.kp/en_index.php
kp
com.kp
edu.kp
gov.kp
org.kp
rep.kp
tra.kp

// kr : https://en.wikipedia.org/wiki/.kr
// see also: http://domain.nida.or.kr/eng/registration.jsp
kr
ac.kr
co.kr
es.kr
go.kr
hs.kr
kg.kr
mil.kr
ms.kr
ne.kr
or.kr
pe.kr
re.kr
sc.kr
// kr geographical names
busan.kr
chungbuk.kr
chungnam.kr
daegu.kr
daejeon.kr
gangwon.kr
gwangju.kr
gyeongbuk.kr
gyeonggi.kr
gyeongnam.kr
incheon.kr
jeju.kr
jeonbuk.kr
jeonnam.kr
seoul.kr
ulsan.kr

// kw : https://www.nic.kw/policies/
// Confirmed by registry <nic.tech@citra.gov.kw>
kw
com.kw
edu.kw
emb.kw
gov.kw
ind.kw
net.kw
org.kw

// ky : http://www.icta.ky/da_ky_reg_dom.php
// Confirmed by registry <kysupport@perimeterusa.com> 2008-06-17
ky
com.ky
edu.ky
net.ky
org.ky

// kz : https://en.wikipedia.org/wiki/.kz
// see also: http://www.nic.kz/rules/index.jsp
kz
org.kz
edu.kz
net.kz
gov.kz
mil.kz
com.kz

// la : https://en.wikipedia.org/wiki/.la
// Submitted by registry <gavin.brown@nic.la>
la
int.la
net.la
info.la
edu.la
gov.la
per.la
com.la
org.la

// lb : https://en.wikipedia.org/wiki/.lb
// Submitted by registry <randy@psg.com>
lb
com.lb
edu.lb
gov.lb
net.lb
org.lb

// lc : https://en.wikipedia.org/wiki/.lc
// see also: http://www.nic.lc/rules.htm
lc
com.lc
net.lc
co.lc
org.lc
edu.lc
gov.lc

// li : https://en.wikipedia.org/wiki/.li
li

// lk : https://www.nic.lk/index.php/domain-registration/lk-domain-naming-structure
lk
gov.lk
sch.lk
net.lk
int.lk
com.lk
org.lk
edu.lk
ngo.lk
soc.lk
web.lk
ltd.lk
assn.lk
grp.lk
hotel.lk
ac.lk

// lr : http://psg.com/dns/lr/lr.txt
// Submitted by registry <randy@psg.com>
lr
com.lr
edu.lr
gov.lr
org.lr
net.lr

// ls : http://www.nic.ls/
// Confirmed by registry <lsadmin@nic.ls>
ls
ac.ls
biz.ls
co.ls
edu.ls
gov.ls
info.ls
net.ls
org.ls
sc.ls

// lt : https://en.wikipedia.org/wiki/.lt
lt
// gov.lt : http://www.gov.lt/index_en.php
gov.lt

// lu : http://www.dns.lu/en/
lu

// lv : http://www.nic.lv/DNS/En/generic.php
lv
com.lv
edu.lv
gov.lv
org.lv
mil.lv
id.lv
net.lv
asn.lv
conf.lv

// ly : http://www.nic.ly/regulations.php
ly
com.ly
net.ly
gov.ly
plc.ly
edu.ly
sch.ly
med.ly
org.ly
id.ly

// ma : https://en.wikipedia.org/wiki/.ma
// http://www.anrt.ma/fr/admin/download/upload/file_fr782.pdf
ma
co.ma
net.ma
gov.ma
org.ma
ac.ma
press.ma

// mc : http://www.nic.mc/
mc
tm.mc
asso.mc

// md : https://en.wikipedia.org/wiki/.md
md

// me : https://en.wikipedia.org/wiki/.me
me
co.me
net.me
org.me
edu.me
ac.me
gov.me
its.me
priv.me

// mg : http://nic.mg/nicmg/?page_id=39
mg
org.mg
nom.mg
gov.mg
prd.mg
tm.mg
edu.mg
mil.mg
com.mg
co.mg

// mh : https://en.wikipedia.org/wiki/.mh
mh

// mil : https://en.wikipedia.org/wiki/.mil
mil

// mk : https://en.wikipedia.org/wiki/.mk
// see also: http://dns.marnet.net.mk/postapka.php
mk
com.mk
org.mk
net.mk
edu.mk
gov.mk
inf.mk
name.mk

// ml : http://www.gobin.info/domainname/ml-template.doc
// see also: https://en.wikipedia.org/wiki/.ml
ml
com.ml
edu.ml
gouv.ml
gov.ml
net.ml
org.ml
presse.ml

// mm : https://en.wikipedia.org/wiki/.mm
*.mm

// mn : https://en.wikipedia.org/wiki/.mn
mn
gov.mn
edu.mn
org.mn

// mo : http://www.monic.net.mo/
mo
com.mo
net.mo
org.mo
edu.mo
gov.mo

// mobi : https://en.wikipedia.org/wiki/.mobi
mobi

// mp : http://www.dot.mp/
// Confirmed by registry <dcamacho@saipan.com> 2008-06-17
mp

// mq : https://en.wikipedia.org/wiki/.mq
mq

// mr : https://en.wikipedia.org/wiki/.mr
mr
gov.mr

// ms : http://www.nic.ms/pdf/MS_Domain_Name_Rules.pdf
ms
com.ms
edu.ms
gov.ms
net.ms
org.ms

// mt : https://www.nic.org.mt/go/policy
// Submitted by registry <help@nic.org.mt>
mt
com.mt
edu.mt
net.mt
org.mt

// mu : https://en.wikipedia.org/wiki/.mu
mu
com.mu
net.mu
org.mu
gov.mu
ac.mu
co.mu
or.mu

// museum : https://welcome.museum/wp-content/uploads/2018/05/20180525-Registration-Policy-MUSEUM-EN_VF-2.pdf https://welcome.museum/buy-your-dot-museum-2/
museum

// mv : https://en.wikipedia.org/wiki/.mv
// "mv" included because, contra Wikipedia, google.mv exists.
mv
aero.mv
biz.mv
com.mv
coop.mv
edu.mv
gov.mv
info.mv
int.mv
mil.mv
museum.mv
name.mv
net.mv
org.mv
pro.mv

// mw : http://www.registrar.mw/
mw
ac.mw
biz.mw
co.mw
com.mw
coop.mw
edu.mw
gov.mw
int.mw
museum.mw
net.mw
org.mw

// mx : http://www.nic.mx/
// Submitted by registry <farias@nic.mx>
mx
com.mx
org.mx
gob.mx
edu.mx
net.mx

// my : http://www.mynic.my/
// Available strings: https://mynic.my/resources/domains/buying-a-domain/
my
biz.my
com.my
edu.my
gov.my
mil.my
name.my
net.my
org.my

// mz : http://www.uem.mz/
// Submitted by registry <antonio@uem.mz>
mz
ac.mz
adv.mz
co.mz
edu.mz
gov.mz
mil.mz
net.mz
org.mz

// na : http://www.na-nic.com.na/
// http://www.info.na/domain/
na
info.na
pro.na
name.na
school.na
or.na
dr.na
us.na
mx.na
ca.na
in.na
cc.na
tv.na
ws.na
mobi.na
co.na
com.na
org.na

// name : has 2nd-level tlds, but there's no list of them
name

// nc : http://www.cctld.nc/
nc
asso.nc
nom.nc

// ne : https://en.wikipedia.org/wiki/.ne
ne

// net : https://en.wikipedia.org/wiki/.net
net

// nf : https://en.wikipedia.org/wiki/.nf
nf
com.nf
net.nf
per.nf
rec.nf
web.nf
arts.nf
firm.nf
info.nf
other.nf
store.nf

// ng : http://www.nira.org.ng/index.php/join-us/register-ng-domain/189-nira-slds
ng
com.ng
edu.ng
gov.ng
i.ng
mil.ng
mobi.ng
name.ng
net.ng
org.ng
sch.ng

// ni : http://www.nic.ni/
ni
ac.ni
biz.ni
co.ni
com.ni
edu.ni
gob.ni
in.ni
info.ni
int.ni
mil.ni
net.ni
nom.ni
org.ni
web.ni

// nl : https://en.wikipedia.org/wiki/.nl
//      https://www.sidn.nl/
//      ccTLD for the Netherlands
nl

// no : https://www.norid.no/en/om-domenenavn/regelverk-for-no/
// Norid geographical second level domains : https://www.norid.no/en/om-domenenavn/regelverk-for-no/vedlegg-b/
// Norid category second level domains : https://www.norid.no/en/om-domenenavn/regelverk-for-no/vedlegg-c/
// Norid category second-level domains managed by parties other than Norid : https://www.norid.no/en/om-domenenavn/regelverk-for-no/vedlegg-d/
// RSS feed: https://teknisk.norid.no/en/feed/
no
// Norid category second level domains : https://www.norid.no/en/om-domenenavn/regelverk-for-no/vedlegg-c/
fhs.no
vgs.no
fylkesbibl.no
folkebibl.no
museum.no
idrett.no
priv.no
// Norid category second-level domains managed by parties other than Norid : https://www.norid.no/en/om-domenenavn/regelverk-for-no/vedlegg-d/
mil.no
stat.no
dep.no
kommune.no
herad.no
// Norid geographical second level domains : https://www.norid.no/en/om-domenenavn/regelverk-for-no/vedlegg-b/
// counties
aa.no
ah.no
bu.no
fm.no
hl.no
hm.no
jan-mayen.no
mr.no
nl.no
nt.no
of.no
ol.no
oslo.no
rl.no
sf.no
st.no
svalbard.no
tm.no
tr.no
va.no
vf.no
// primary and lower secondary schools per county
gs.aa.no
gs.ah.no
gs.bu.no
gs.fm.no
gs.hl.no
gs.hm.no
gs.jan-mayen.no
gs.mr.no
gs.nl.no
gs.nt.no
gs.of.no
gs.ol.no
gs.oslo.no
gs.rl.no
gs.sf.no
gs.st.no
gs.svalbard.no
gs.tm.no
gs.tr.no
gs.va.no
gs.vf.no
// cities
akrehamn.no
Ã¥krehamn.no
algard.no
Ã¥lgÃ¥rd.no
arna.no
brumunddal.no
bryne.no
bronnoysund.no
brÃ¸nnÃ¸ysund.no
drobak.no
drÃ¸bak.no
egersund.no
fetsund.no
floro.no
florÃ¸.no
fredrikstad.no
hokksund.no
honefoss.no
hÃ¸nefoss.no
jessheim.no
jorpeland.no
jÃ¸rpeland.no
kirkenes.no
kopervik.no
krokstadelva.no
langevag.no
langevÃ¥g.no
leirvik.no
mjondalen.no
mjÃ¸ndalen.no
mo-i-rana.no
mosjoen.no
mosjÃ¸en.no
nesoddtangen.no
orkanger.no
osoyro.no
osÃ¸yro.no
raholt.no
rÃ¥holt.no
sandnessjoen.no
sandnessjÃ¸en.no
skedsmokorset.no
slattum.no
spjelkavik.no
stathelle.no
stavern.no
stjordalshalsen.no
stjÃ¸rdalshalsen.no
tananger.no
tranby.no
vossevangen.no
// communities
afjord.no
Ã¥fjord.no
agdenes.no
al.no
Ã¥l.no
alesund.no
Ã¥lesund.no
alstahaug.no
alta.no
Ã¡ltÃ¡.no
alaheadju.no
Ã¡laheadju.no
alvdal.no
amli.no
Ã¥mli.no
amot.no
Ã¥mot.no
andebu.no
andoy.no
andÃ¸y.no
andasuolo.no
ardal.no
Ã¥rdal.no
aremark.no
arendal.no
Ã¥s.no
aseral.no
Ã¥seral.no
asker.no
askim.no
askvoll.no
askoy.no
askÃ¸y.no
asnes.no
Ã¥snes.no
audnedaln.no
aukra.no
aure.no
aurland.no
aurskog-holand.no
aurskog-hÃ¸land.no
austevoll.no
austrheim.no
averoy.no
averÃ¸y.no
balestrand.no
ballangen.no
balat.no
bÃ¡lÃ¡t.no
balsfjord.no
bahccavuotna.no
bÃ¡hccavuotna.no
bamble.no
bardu.no
beardu.no
beiarn.no
bajddar.no
bÃ¡jddar.no
baidar.no
bÃ¡idÃ¡r.no
berg.no
bergen.no
berlevag.no
berlevÃ¥g.no
bearalvahki.no
bearalvÃ¡hki.no
bindal.no
birkenes.no
bjarkoy.no
bjarkÃ¸y.no
bjerkreim.no
bjugn.no
bodo.no
bodÃ¸.no
badaddja.no
bÃ¥dÃ¥ddjÃ¥.no
budejju.no
bokn.no
bremanger.no
bronnoy.no
brÃ¸nnÃ¸y.no
bygland.no
bykle.no
barum.no
bÃ¦rum.no
bo.telemark.no
bÃ¸.telemark.no
bo.nordland.no
bÃ¸.nordland.no
bievat.no
bievÃ¡t.no
bomlo.no
bÃ¸mlo.no
batsfjord.no
bÃ¥tsfjord.no
bahcavuotna.no
bÃ¡hcavuotna.no
dovre.no
drammen.no
drangedal.no
dyroy.no
dyrÃ¸y.no
donna.no
dÃ¸nna.no
eid.no
eidfjord.no
eidsberg.no
eidskog.no
eidsvoll.no
eigersund.no
elverum.no
enebakk.no
engerdal.no
etne.no
etnedal.no
evenes.no
evenassi.no
evenÃ¡Å¡Å¡i.no
evje-og-hornnes.no
farsund.no
fauske.no
fuossko.no
fuoisku.no
fedje.no
fet.no
finnoy.no
finnÃ¸y.no
fitjar.no
fjaler.no
fjell.no
flakstad.no
flatanger.no
flekkefjord.no
flesberg.no
flora.no
fla.no
flÃ¥.no
folldal.no
forsand.no
fosnes.no
frei.no
frogn.no
froland.no
frosta.no
frana.no
frÃ¦na.no
froya.no
frÃ¸ya.no
fusa.no
fyresdal.no
forde.no
fÃ¸rde.no
gamvik.no
gangaviika.no
gÃ¡Å‹gaviika.no
gaular.no
gausdal.no
gildeskal.no
gildeskÃ¥l.no
giske.no
gjemnes.no
gjerdrum.no
gjerstad.no
gjesdal.no
gjovik.no
gjÃ¸vik.no
gloppen.no
gol.no
gran.no
grane.no
granvin.no
gratangen.no
grimstad.no
grong.no
kraanghke.no
krÃ¥anghke.no
grue.no
gulen.no
hadsel.no
halden.no
halsa.no
hamar.no
hamaroy.no
habmer.no
hÃ¡bmer.no
hapmir.no
hÃ¡pmir.no
hammerfest.no
hammarfeasta.no
hÃ¡mmÃ¡rfeasta.no
haram.no
hareid.no
harstad.no
hasvik.no
aknoluokta.no
Ã¡kÅ‹oluokta.no
hattfjelldal.no
aarborte.no
haugesund.no
hemne.no
hemnes.no
hemsedal.no
heroy.more-og-romsdal.no
herÃ¸y.mÃ¸re-og-romsdal.no
heroy.nordland.no
herÃ¸y.nordland.no
hitra.no
hjartdal.no
hjelmeland.no
hobol.no
hobÃ¸l.no
hof.no
hol.no
hole.no
holmestrand.no
holtalen.no
holtÃ¥len.no
hornindal.no
horten.no
hurdal.no
hurum.no
hvaler.no
hyllestad.no
hagebostad.no
hÃ¦gebostad.no
hoyanger.no
hÃ¸yanger.no
hoylandet.no
hÃ¸ylandet.no
ha.no
hÃ¥.no
ibestad.no
inderoy.no
inderÃ¸y.no
iveland.no
jevnaker.no
jondal.no
jolster.no
jÃ¸lster.no
karasjok.no
karasjohka.no
kÃ¡rÃ¡Å¡johka.no
karlsoy.no
galsa.no
gÃ¡lsÃ¡.no
karmoy.no
karmÃ¸y.no
kautokeino.no
guovdageaidnu.no
klepp.no
klabu.no
klÃ¦bu.no
kongsberg.no
kongsvinger.no
kragero.no
kragerÃ¸.no
kristiansand.no
kristiansund.no
krodsherad.no
krÃ¸dsherad.no
kvalsund.no
rahkkeravju.no
rÃ¡hkkerÃ¡vju.no
kvam.no
kvinesdal.no
kvinnherad.no
kviteseid.no
kvitsoy.no
kvitsÃ¸y.no
kvafjord.no
kvÃ¦fjord.no
giehtavuoatna.no
kvanangen.no
kvÃ¦nangen.no
navuotna.no
nÃ¡vuotna.no
kafjord.no
kÃ¥fjord.no
gaivuotna.no
gÃ¡ivuotna.no
larvik.no
lavangen.no
lavagis.no
loabat.no
loabÃ¡t.no
lebesby.no
davvesiida.no
leikanger.no
leirfjord.no
leka.no
leksvik.no
lenvik.no
leangaviika.no
leaÅ‹gaviika.no
lesja.no
levanger.no
lier.no
lierne.no
lillehammer.no
lillesand.no
lindesnes.no
lindas.no
lindÃ¥s.no
lom.no
loppa.no
lahppi.no
lÃ¡hppi.no
lund.no
lunner.no
luroy.no
lurÃ¸y.no
luster.no
lyngdal.no
lyngen.no
ivgu.no
lardal.no
lerdal.no
lÃ¦rdal.no
lodingen.no
lÃ¸dingen.no
lorenskog.no
lÃ¸renskog.no
loten.no
lÃ¸ten.no
malvik.no
masoy.no
mÃ¥sÃ¸y.no
muosat.no
muosÃ¡t.no
mandal.no
marker.no
marnardal.no
masfjorden.no
meland.no
meldal.no
melhus.no
meloy.no
melÃ¸y.no
meraker.no
merÃ¥ker.no
moareke.no
moÃ¥reke.no
midsund.no
midtre-gauldal.no
modalen.no
modum.no
molde.no
moskenes.no
moss.no
mosvik.no
malselv.no
mÃ¥lselv.no
malatvuopmi.no
mÃ¡latvuopmi.no
namdalseid.no
aejrie.no
namsos.no
namsskogan.no
naamesjevuemie.no
nÃ¥Ã¥mesjevuemie.no
laakesvuemie.no
nannestad.no
narvik.no
narviika.no
naustdal.no
nedre-eiker.no
nes.akershus.no
nes.buskerud.no
nesna.no
nesodden.no
nesseby.no
unjarga.no
unjÃ¡rga.no
nesset.no
nissedal.no
nittedal.no
nord-aurdal.no
nord-fron.no
nord-odal.no
norddal.no
nordkapp.no
davvenjarga.no
davvenjÃ¡rga.no
nordre-land.no
nordreisa.no
raisa.no
rÃ¡isa.no
nore-og-uvdal.no
notodden.no
naroy.no
nÃ¦rÃ¸y.no
notteroy.no
nÃ¸tterÃ¸y.no
odda.no
oksnes.no
Ã¸ksnes.no
oppdal.no
oppegard.no
oppegÃ¥rd.no
orkdal.no
orland.no
Ã¸rland.no
orskog.no
Ã¸rskog.no
orsta.no
Ã¸rsta.no
os.hedmark.no
os.hordaland.no
osen.no
osteroy.no
osterÃ¸y.no
ostre-toten.no
Ã¸stre-toten.no
overhalla.no
ovre-eiker.no
Ã¸vre-eiker.no
oyer.no
Ã¸yer.no
oygarden.no
Ã¸ygarden.no
oystre-slidre.no
Ã¸ystre-slidre.no
porsanger.no
porsangu.no
porsÃ¡Å‹gu.no
porsgrunn.no
radoy.no
radÃ¸y.no
rakkestad.no
rana.no
ruovat.no
randaberg.no
rauma.no
rendalen.no
rennebu.no
rennesoy.no
rennesÃ¸y.no
rindal.no
ringebu.no
ringerike.no
ringsaker.no
rissa.no
risor.no
risÃ¸r.no
roan.no
rollag.no
rygge.no
ralingen.no
rÃ¦lingen.no
rodoy.no
rÃ¸dÃ¸y.no
romskog.no
rÃ¸mskog.no
roros.no
rÃ¸ros.no
rost.no
rÃ¸st.no
royken.no
rÃ¸yken.no
royrvik.no
rÃ¸yrvik.no
rade.no
rÃ¥de.no
salangen.no
siellak.no
saltdal.no
salat.no
sÃ¡lÃ¡t.no
sÃ¡lat.no
samnanger.no
sande.more-og-romsdal.no
sande.mÃ¸re-og-romsdal.no
sande.vestfold.no
sandefjord.no
sandnes.no
sandoy.no
sandÃ¸y.no
sarpsborg.no
sauda.no
sauherad.no
sel.no
selbu.no
selje.no
seljord.no
sigdal.no
siljan.no
sirdal.no
skaun.no
skedsmo.no
ski.no
skien.no
skiptvet.no
skjervoy.no
skjervÃ¸y.no
skierva.no
skiervÃ¡.no
skjak.no
skjÃ¥k.no
skodje.no
skanland.no
skÃ¥nland.no
skanit.no
skÃ¡nit.no
smola.no
smÃ¸la.no
snillfjord.no
snasa.no
snÃ¥sa.no
snoasa.no
snaase.no
snÃ¥ase.no
sogndal.no
sokndal.no
sola.no
solund.no
songdalen.no
sortland.no
spydeberg.no
stange.no
stavanger.no
steigen.no
steinkjer.no
stjordal.no
stjÃ¸rdal.no
stokke.no
stor-elvdal.no
stord.no
stordal.no
storfjord.no
omasvuotna.no
strand.no
stranda.no
stryn.no
sula.no
suldal.no
sund.no
sunndal.no
surnadal.no
sveio.no
svelvik.no
sykkylven.no
sogne.no
sÃ¸gne.no
somna.no
sÃ¸mna.no
sondre-land.no
sÃ¸ndre-land.no
sor-aurdal.no
sÃ¸r-aurdal.no
sor-fron.no
sÃ¸r-fron.no
sor-odal.no
sÃ¸r-odal.no
sor-varanger.no
sÃ¸r-varanger.no
matta-varjjat.no
mÃ¡tta-vÃ¡rjjat.no
sorfold.no
sÃ¸rfold.no
sorreisa.no
sÃ¸rreisa.no
sorum.no
sÃ¸rum.no
tana.no
deatnu.no
time.no
tingvoll.no
tinn.no
tjeldsund.no
dielddanuorri.no
tjome.no
tjÃ¸me.no
tokke.no
tolga.no
torsken.no
tranoy.no
tranÃ¸y.no
tromso.no
tromsÃ¸.no
tromsa.no
romsa.no
trondheim.no
troandin.no
trysil.no
trana.no
trÃ¦na.no
trogstad.no
trÃ¸gstad.no
tvedestrand.no
tydal.no
tynset.no
tysfjord.no
divtasvuodna.no
divttasvuotna.no
tysnes.no
tysvar.no
tysvÃ¦r.no
tonsberg.no
tÃ¸nsberg.no
ullensaker.no
ullensvang.no
ulvik.no
utsira.no
vadso.no
vadsÃ¸.no
cahcesuolo.no
Ä�Ã¡hcesuolo.no
vaksdal.no
valle.no
vang.no
vanylven.no
vardo.no
vardÃ¸.no
varggat.no
vÃ¡rggÃ¡t.no
vefsn.no
vaapste.no
vega.no
vegarshei.no
vegÃ¥rshei.no
vennesla.no
verdal.no
verran.no
vestby.no
vestnes.no
vestre-slidre.no
vestre-toten.no
vestvagoy.no
vestvÃ¥gÃ¸y.no
vevelstad.no
vik.no
vikna.no
vindafjord.no
volda.no
voss.no
varoy.no
vÃ¦rÃ¸y.no
vagan.no
vÃ¥gan.no
voagat.no
vagsoy.no
vÃ¥gsÃ¸y.no
vaga.no
vÃ¥gÃ¥.no
valer.ostfold.no
vÃ¥ler.Ã¸stfold.no
valer.hedmark.no
vÃ¥ler.hedmark.no

// np : http://www.mos.com.np/register.html
*.np

// nr : http://cenpac.net.nr/dns/index.html
// Submitted by registry <technician@cenpac.net.nr>
nr
biz.nr
info.nr
gov.nr
edu.nr
org.nr
net.nr
com.nr

// nu : https://en.wikipedia.org/wiki/.nu
nu

// nz : https://en.wikipedia.org/wiki/.nz
// Submitted by registry <jay@nzrs.net.nz>
nz
ac.nz
co.nz
cri.nz
geek.nz
gen.nz
govt.nz
health.nz
iwi.nz
kiwi.nz
maori.nz
mil.nz
mÄ�ori.nz
net.nz
org.nz
parliament.nz
school.nz

// om : https://en.wikipedia.org/wiki/.om
om
co.om
com.om
edu.om
gov.om
med.om
museum.om
net.om
org.om
pro.om

// onion : https://tools.ietf.org/html/rfc7686
onion

// org : https://en.wikipedia.org/wiki/.org
org

// pa : http://www.nic.pa/
// Some additional second level "domains" resolve directly as hostnames, such as
// pannet.pa, so we add a rule for "pa".
pa
ac.pa
gob.pa
com.pa
org.pa
sld.pa
edu.pa
net.pa
ing.pa
abo.pa
med.pa
nom.pa

// pe : https://www.nic.pe/InformeFinalComision.pdf
pe
edu.pe
gob.pe
nom.pe
mil.pe
org.pe
com.pe
net.pe

// pf : http://www.gobin.info/domainname/formulaire-pf.pdf
pf
com.pf
org.pf
edu.pf

// pg : https://en.wikipedia.org/wiki/.pg
*.pg

// ph : http://www.domains.ph/FAQ2.asp
// Submitted by registry <jed@email.com.ph>
ph
com.ph
net.ph
org.ph
gov.ph
edu.ph
ngo.ph
mil.ph
i.ph

// pk : http://pk5.pknic.net.pk/pk5/msgNamepk.PK
pk
com.pk
net.pk
edu.pk
org.pk
fam.pk
biz.pk
web.pk
gov.pk
gob.pk
gok.pk
gon.pk
gop.pk
gos.pk
info.pk

// pl http://www.dns.pl/english/index.html
// Submitted by registry
pl
com.pl
net.pl
org.pl
// pl functional domains (http://www.dns.pl/english/index.html)
aid.pl
agro.pl
atm.pl
auto.pl
biz.pl
edu.pl
gmina.pl
gsm.pl
info.pl
mail.pl
miasta.pl
media.pl
mil.pl
nieruchomosci.pl
nom.pl
pc.pl
powiat.pl
priv.pl
realestate.pl
rel.pl
sex.pl
shop.pl
sklep.pl
sos.pl
szkola.pl
targi.pl
tm.pl
tourism.pl
travel.pl
turystyka.pl
// Government domains
gov.pl
ap.gov.pl
griw.gov.pl
ic.gov.pl
is.gov.pl
kmpsp.gov.pl
konsulat.gov.pl
kppsp.gov.pl
kwp.gov.pl
kwpsp.gov.pl
mup.gov.pl
mw.gov.pl
oia.gov.pl
oirm.gov.pl
oke.gov.pl
oow.gov.pl
oschr.gov.pl
oum.gov.pl
pa.gov.pl
pinb.gov.pl
piw.gov.pl
po.gov.pl
pr.gov.pl
psp.gov.pl
psse.gov.pl
pup.gov.pl
rzgw.gov.pl
sa.gov.pl
sdn.gov.pl
sko.gov.pl
so.gov.pl
sr.gov.pl
starostwo.gov.pl
ug.gov.pl
ugim.gov.pl
um.gov.pl
umig.gov.pl
upow.gov.pl
uppo.gov.pl
us.gov.pl
uw.gov.pl
uzs.gov.pl
wif.gov.pl
wiih.gov.pl
winb.gov.pl
wios.gov.pl
witd.gov.pl
wiw.gov.pl
wkz.gov.pl
wsa.gov.pl
wskr.gov.pl
wsse.gov.pl
wuoz.gov.pl
wzmiuw.gov.pl
zp.gov.pl
zpisdn.gov.pl
// pl regional domains (http://www.dns.pl/english/index.html)
augustow.pl
babia-gora.pl
bedzin.pl
beskidy.pl
bialowieza.pl
bialystok.pl
bielawa.pl
bieszczady.pl
boleslawiec.pl
bydgoszcz.pl
bytom.pl
cieszyn.pl
czeladz.pl
czest.pl
dlugoleka.pl
elblag.pl
elk.pl
glogow.pl
gniezno.pl
gorlice.pl
grajewo.pl
ilawa.pl
jaworzno.pl
jelenia-gora.pl
jgora.pl
kalisz.pl
kazimierz-dolny.pl
karpacz.pl
kartuzy.pl
kaszuby.pl
katowice.pl
kepno.pl
ketrzyn.pl
klodzko.pl
kobierzyce.pl
kolobrzeg.pl
konin.pl
konskowola.pl
kutno.pl
lapy.pl
lebork.pl
legnica.pl
lezajsk.pl
limanowa.pl
lomza.pl
lowicz.pl
lubin.pl
lukow.pl
malbork.pl
malopolska.pl
mazowsze.pl
mazury.pl
mielec.pl
mielno.pl
mragowo.pl
naklo.pl
nowaruda.pl
nysa.pl
olawa.pl
olecko.pl
olkusz.pl
olsztyn.pl
opoczno.pl
opole.pl
ostroda.pl
ostroleka.pl
ostrowiec.pl
ostrowwlkp.pl
pila.pl
pisz.pl
podhale.pl
podlasie.pl
polkowice.pl
pomorze.pl
pomorskie.pl
prochowice.pl
pruszkow.pl
przeworsk.pl
pulawy.pl
radom.pl
rawa-maz.pl
rybnik.pl
rzeszow.pl
sanok.pl
sejny.pl
slask.pl
slupsk.pl
sosnowiec.pl
stalowa-wola.pl
skoczow.pl
starachowice.pl
stargard.pl
suwalki.pl
swidnica.pl
swiebodzin.pl
swinoujscie.pl
szczecin.pl
szczytno.pl
tarnobrzeg.pl
tgory.pl
turek.pl
tychy.pl
ustka.pl
walbrzych.pl
warmia.pl
warszawa.pl
waw.pl
wegrow.pl
wielun.pl
wlocl.pl
wloclawek.pl
wodzislaw.pl
wolomin.pl
wroclaw.pl
zachpomor.pl
zagan.pl
zarow.pl
zgora.pl
zgorzelec.pl

// pm : https://www.afnic.fr/wp-media/uploads/2022/12/afnic-naming-policy-2023-01-01.pdf
pm

// pn : http://www.government.pn/PnRegistry/policies.htm
pn
gov.pn
co.pn
org.pn
edu.pn
net.pn

// post : https://en.wikipedia.org/wiki/.post
post

// pr : http://www.nic.pr/index.asp?f=1
pr
com.pr
net.pr
org.pr
gov.pr
edu.pr
isla.pr
pro.pr
biz.pr
info.pr
name.pr
// these aren't mentioned on nic.pr, but on https://en.wikipedia.org/wiki/.pr
est.pr
prof.pr
ac.pr

// pro : http://registry.pro/get-pro
pro
aaa.pro
aca.pro
acct.pro
avocat.pro
bar.pro
cpa.pro
eng.pro
jur.pro
law.pro
med.pro
recht.pro

// ps : https://en.wikipedia.org/wiki/.ps
// http://www.nic.ps/registration/policy.html#reg
ps
edu.ps
gov.ps
sec.ps
plo.ps
com.ps
org.ps
net.ps

// pt : https://www.dns.pt/en/domain/pt-terms-and-conditions-registration-rules/
pt
net.pt
gov.pt
org.pt
edu.pt
int.pt
publ.pt
com.pt
nome.pt

// pw : https://en.wikipedia.org/wiki/.pw
pw
co.pw
ne.pw
or.pw
ed.pw
go.pw
belau.pw

// py : http://www.nic.py/pautas.html#seccion_9
// Submitted by registry
py
com.py
coop.py
edu.py
gov.py
mil.py
net.py
org.py

// qa : http://domains.qa/en/
qa
com.qa
edu.qa
gov.qa
mil.qa
name.qa
net.qa
org.qa
sch.qa

// re : https://www.afnic.fr/wp-media/uploads/2022/12/afnic-naming-policy-2023-01-01.pdf
re
asso.re
com.re
nom.re

// ro : http://www.rotld.ro/
ro
arts.ro
com.ro
firm.ro
info.ro
nom.ro
nt.ro
org.ro
rec.ro
store.ro
tm.ro
www.ro

// rs : https://www.rnids.rs/en/domains/national-domains
rs
ac.rs
co.rs
edu.rs
gov.rs
in.rs
org.rs

// ru : https://cctld.ru/files/pdf/docs/en/rules_ru-rf.pdf
// Submitted by George Georgievsky <gug@cctld.ru>
ru

// rw : https://www.ricta.org.rw/sites/default/files/resources/registry_registrar_contract_0.pdf
rw
ac.rw
co.rw
coop.rw
gov.rw
mil.rw
net.rw
org.rw

// sa : http://www.nic.net.sa/
sa
com.sa
net.sa
org.sa
gov.sa
med.sa
pub.sa
edu.sa
sch.sa

// sb : http://www.sbnic.net.sb/
// Submitted by registry <lee.humphries@telekom.com.sb>
sb
com.sb
edu.sb
gov.sb
net.sb
org.sb

// sc : http://www.nic.sc/
sc
com.sc
gov.sc
net.sc
org.sc
edu.sc

// sd : http://www.isoc.sd/sudanic.isoc.sd/billing_pricing.htm
// Submitted by registry <admin@isoc.sd>
sd
com.sd
net.sd
org.sd
edu.sd
med.sd
tv.sd
gov.sd
info.sd

// se : https://en.wikipedia.org/wiki/.se
// Submitted by registry <patrik.wallstrom@iis.se>
se
a.se
ac.se
b.se
bd.se
brand.se
c.se
d.se
e.se
f.se
fh.se
fhsk.se
fhv.se
g.se
h.se
i.se
k.se
komforb.se
kommunalforbund.se
komvux.se
l.se
lanbib.se
m.se
n.se
naturbruksgymn.se
o.se
org.se
p.se
parti.se
pp.se
press.se
r.se
s.se
t.se
tm.se
u.se
w.se
x.se
y.se
z.se

// sg : http://www.nic.net.sg/page/registration-policies-procedures-and-guidelines
sg
com.sg
net.sg
org.sg
gov.sg
edu.sg
per.sg

// sh : http://nic.sh/rules.htm
sh
com.sh
net.sh
gov.sh
org.sh
mil.sh

// si : https://en.wikipedia.org/wiki/.si
si

// sj : No registrations at this time.
// Submitted by registry <jarle@uninett.no>
sj

// sk : https://en.wikipedia.org/wiki/.sk
// list of 2nd level domains ?
sk

// sl : http://www.nic.sl
// Submitted by registry <adam@neoip.com>
sl
com.sl
net.sl
edu.sl
gov.sl
org.sl

// sm : https://en.wikipedia.org/wiki/.sm
sm

// sn : https://en.wikipedia.org/wiki/.sn
sn
art.sn
com.sn
edu.sn
gouv.sn
org.sn
perso.sn
univ.sn

// so : http://sonic.so/policies/
so
com.so
edu.so
gov.so
me.so
net.so
org.so

// sr : https://en.wikipedia.org/wiki/.sr
sr

// ss : https://registry.nic.ss/
// Submitted by registry <technical@nic.ss>
ss
biz.ss
com.ss
edu.ss
gov.ss
me.ss
net.ss
org.ss
sch.ss

// st : http://www.nic.st/html/policyrules/
st
co.st
com.st
consulado.st
edu.st
embaixada.st
mil.st
net.st
org.st
principe.st
saotome.st
store.st

// su : https://en.wikipedia.org/wiki/.su
su

// sv : http://www.svnet.org.sv/niveldos.pdf
sv
com.sv
edu.sv
gob.sv
org.sv
red.sv

// sx : https://en.wikipedia.org/wiki/.sx
// Submitted by registry <jcvignes@openregistry.com>
sx
gov.sx

// sy : https://en.wikipedia.org/wiki/.sy
// see also: http://www.gobin.info/domainname/sy.doc
sy
edu.sy
gov.sy
net.sy
mil.sy
com.sy
org.sy

// sz : https://en.wikipedia.org/wiki/.sz
// http://www.sispa.org.sz/
sz
co.sz
ac.sz
org.sz

// tc : https://en.wikipedia.org/wiki/.tc
tc

// td : https://en.wikipedia.org/wiki/.td
td

// tel: https://en.wikipedia.org/wiki/.tel
// http://www.telnic.org/
tel

// tf : https://www.afnic.fr/wp-media/uploads/2022/12/afnic-naming-policy-2023-01-01.pdf
tf

// tg : https://en.wikipedia.org/wiki/.tg
// http://www.nic.tg/
tg

// th : https://en.wikipedia.org/wiki/.th
// Submitted by registry <krit@thains.co.th>
th
ac.th
co.th
go.th
in.th
mi.th
net.th
or.th

// tj : http://www.nic.tj/policy.html
tj
ac.tj
biz.tj
co.tj
com.tj
edu.tj
go.tj
gov.tj
int.tj
mil.tj
name.tj
net.tj
nic.tj
org.tj
test.tj
web.tj

// tk : https://en.wikipedia.org/wiki/.tk
tk

// tl : https://en.wikipedia.org/wiki/.tl
tl
gov.tl

// tm : http://www.nic.tm/local.html
tm
com.tm
co.tm
org.tm
net.tm
nom.tm
gov.tm
mil.tm
edu.tm

// tn : http://www.registre.tn/fr/
// https://whois.ati.tn/
tn
com.tn
ens.tn
fin.tn
gov.tn
ind.tn
info.tn
intl.tn
mincom.tn
nat.tn
net.tn
org.tn
perso.tn
tourism.tn

// to : https://en.wikipedia.org/wiki/.to
// Submitted by registry <egullich@colo.to>
to
com.to
gov.to
net.to
org.to
edu.to
mil.to

// tr : https://nic.tr/
// https://nic.tr/forms/eng/policies.pdf
// https://nic.tr/index.php?USRACTN=PRICELST
tr
av.tr
bbs.tr
bel.tr
biz.tr
com.tr
dr.tr
edu.tr
gen.tr
gov.tr
info.tr
mil.tr
k12.tr
kep.tr
name.tr
net.tr
org.tr
pol.tr
tel.tr
tsk.tr
tv.tr
web.tr
// Used by Northern Cyprus
nc.tr
// Used by government agencies of Northern Cyprus
gov.nc.tr

// tt : http://www.nic.tt/
tt
co.tt
com.tt
org.tt
net.tt
biz.tt
info.tt
pro.tt
int.tt
coop.tt
jobs.tt
mobi.tt
travel.tt
museum.tt
aero.tt
name.tt
gov.tt
edu.tt

// tv : https://en.wikipedia.org/wiki/.tv
// Not listing any 2LDs as reserved since none seem to exist in practice,
// Wikipedia notwithstanding.
tv

// tw : https://en.wikipedia.org/wiki/.tw
tw
edu.tw
gov.tw
mil.tw
com.tw
net.tw
org.tw
idv.tw
game.tw
ebiz.tw
club.tw
ç¶²è·¯.tw
çµ„ç¹”.tw
å•†æ¥­.tw

// tz : http://www.tznic.or.tz/index.php/domains
// Submitted by registry <manager@tznic.or.tz>
tz
ac.tz
co.tz
go.tz
hotel.tz
info.tz
me.tz
mil.tz
mobi.tz
ne.tz
or.tz
sc.tz
tv.tz

// ua : https://hostmaster.ua/policy/?ua
// Submitted by registry <dk@cctld.ua>
ua
// ua 2LD
com.ua
edu.ua
gov.ua
in.ua
net.ua
org.ua
// ua geographic names
// https://hostmaster.ua/2ld/
cherkassy.ua
cherkasy.ua
chernigov.ua
chernihiv.ua
chernivtsi.ua
chernovtsy.ua
ck.ua
cn.ua
cr.ua
crimea.ua
cv.ua
dn.ua
dnepropetrovsk.ua
dnipropetrovsk.ua
donetsk.ua
dp.ua
if.ua
ivano-frankivsk.ua
kh.ua
kharkiv.ua
kharkov.ua
kherson.ua
khmelnitskiy.ua
khmelnytskyi.ua
kiev.ua
kirovograd.ua
km.ua
kr.ua
kropyvnytskyi.ua
krym.ua
ks.ua
kv.ua
kyiv.ua
lg.ua
lt.ua
lugansk.ua
luhansk.ua
lutsk.ua
lv.ua
lviv.ua
mk.ua
mykolaiv.ua
nikolaev.ua
od.ua
odesa.ua
odessa.ua
pl.ua
poltava.ua
rivne.ua
rovno.ua
rv.ua
sb.ua
sebastopol.ua
sevastopol.ua
sm.ua
sumy.ua
te.ua
ternopil.ua
uz.ua
uzhgorod.ua
uzhhorod.ua
vinnica.ua
vinnytsia.ua
vn.ua
volyn.ua
yalta.ua
zakarpattia.ua
zaporizhzhe.ua
zaporizhzhia.ua
zhitomir.ua
zhytomyr.ua
zp.ua
zt.ua

// ug : https://www.registry.co.ug/
ug
co.ug
or.ug
ac.ug
sc.ug
go.ug
ne.ug
com.ug
org.ug

// uk : https://en.wikipedia.org/wiki/.uk
// Submitted by registry <Michael.Daly@nominet.org.uk>
uk
ac.uk
co.uk
gov.uk
ltd.uk
me.uk
net.uk
nhs.uk
org.uk
plc.uk
police.uk
*.sch.uk

// us : https://en.wikipedia.org/wiki/.us
us
dni.us
fed.us
isa.us
kids.us
nsn.us
// us geographic names
ak.us
al.us
ar.us
as.us
az.us
ca.us
co.us
ct.us
dc.us
de.us
fl.us
ga.us
gu.us
hi.us
ia.us
id.us
il.us
in.us
ks.us
ky.us
la.us
ma.us
md.us
me.us
mi.us
mn.us
mo.us
ms.us
mt.us
nc.us
nd.us
ne.us
nh.us
nj.us
nm.us
nv.us
ny.us
oh.us
ok.us
or.us
pa.us
pr.us
ri.us
sc.us
sd.us
tn.us
tx.us
ut.us
vi.us
vt.us
va.us
wa.us
wi.us
wv.us
wy.us
// The registrar notes several more specific domains available in each state,
// such as state.*.us, dst.*.us, etc., but resolution of these is somewhat
// haphazard; in some states these domains resolve as addresses, while in others
// only subdomains are available, or even nothing at all. We include the
// most common ones where it's clear that different sites are different
// entities.
k12.ak.us
k12.al.us
k12.ar.us
k12.as.us
k12.az.us
k12.ca.us
k12.co.us
k12.ct.us
k12.dc.us
k12.fl.us
k12.ga.us
k12.gu.us
// k12.hi.us  Bug 614565 - Hawaii has a state-wide DOE login
k12.ia.us
k12.id.us
k12.il.us
k12.in.us
k12.ks.us
k12.ky.us
k12.la.us
k12.ma.us
k12.md.us
k12.me.us
k12.mi.us
k12.mn.us
k12.mo.us
k12.ms.us
k12.mt.us
k12.nc.us
// k12.nd.us  Bug 1028347 - Removed at request of Travis Rosso <trossow@nd.gov>
k12.ne.us
k12.nh.us
k12.nj.us
k12.nm.us
k12.nv.us
k12.ny.us
k12.oh.us
k12.ok.us
k12.or.us
k12.pa.us
k12.pr.us
// k12.ri.us  Removed at request of Kim Cournoyer <netsupport@staff.ri.net>
k12.sc.us
// k12.sd.us  Bug 934131 - Removed at request of James Booze <James.Booze@k12.sd.us>
k12.tn.us
k12.tx.us
k12.ut.us
k12.vi.us
k12.vt.us
k12.va.us
k12.wa.us
k12.wi.us
// k12.wv.us  Bug 947705 - Removed at request of Verne Britton <verne@wvnet.edu>
k12.wy.us
cc.ak.us
cc.al.us
cc.ar.us
cc.as.us
cc.az.us
cc.ca.us
cc.co.us
cc.ct.us
cc.dc.us
cc.de.us
cc.fl.us
cc.ga.us
cc.gu.us
cc.hi.us
cc.ia.us
cc.id.us
cc.il.us
cc.in.us
cc.ks.us
cc.ky.us
cc.la.us
cc.ma.us
cc.md.us
cc.me.us
cc.mi.us
cc.mn.us
cc.mo.us
cc.ms.us
cc.mt.us
cc.nc.us
cc.nd.us
cc.ne.us
cc.nh.us
cc.nj.us
cc.nm.us
cc.nv.us
cc.ny.us
cc.oh.us
cc.ok.us
cc.or.us
cc.pa.us
cc.pr.us
cc.ri.us
cc.sc.us
cc.sd.us
cc.tn.us
cc.tx.us
cc.ut.us
cc.vi.us
cc.vt.us
cc.va.us
cc.wa.us
cc.wi.us
cc.wv.us
cc.wy.us
lib.ak.us
lib.al.us
lib.ar.us
lib.as.us
lib.az.us
lib.ca.us
lib.co.us
lib.ct.us
lib.dc.us
// lib.de.us  Issue #243 - Moved to Private section at request of Ed Moore <Ed.Moore@lib.de.us>
lib.fl.us
lib.ga.us
lib.gu.us
lib.hi.us
lib.ia.us
lib.id.us
lib.il.us
lib.in.us
lib.ks.us
lib.ky.us
lib.la.us
lib.ma.us
lib.md.us
lib.me.us
lib.mi.us
lib.mn.us
lib.mo.us
lib.ms.us
lib.mt.us
lib.nc.us
lib.nd.us
lib.ne.us
lib.nh.us
lib.nj.us
lib.nm.us
lib.nv.us
lib.ny.us
lib.oh.us
lib.ok.us
lib.or.us
lib.pa.us
lib.pr.us
lib.ri.us
lib.sc.us
lib.sd.us
lib.tn.us
lib.tx.us
lib.ut.us
lib.vi.us
lib.vt.us
lib.va.us
lib.wa.us
lib.wi.us
// lib.wv.us  Bug 941670 - Removed at request of Larry W Arnold <arnold@wvlc.lib.wv.us>
lib.wy.us
// k12.ma.us contains school districts in Massachusetts. The 4LDs are
//  managed independently except for private (PVT), charter (CHTR) and
//  parochial (PAROCH) schools.  Those are delegated directly to the
//  5LD operators.   <k12-ma-hostmaster _ at _ rsuc.gweep.net>
pvt.k12.ma.us
chtr.k12.ma.us
paroch.k12.ma.us
// Merit Network, Inc. maintains the registry for =~ /(k12|cc|lib).mi.us/ and the following
//    see also: http://domreg.merit.edu
//    see also: whois -h whois.domreg.merit.edu help
ann-arbor.mi.us
cog.mi.us
dst.mi.us
eaton.mi.us
gen.mi.us
mus.mi.us
tec.mi.us
washtenaw.mi.us

// uy : http://www.nic.org.uy/
uy
com.uy
edu.uy
gub.uy
mil.uy
net.uy
org.uy

// uz : http://www.reg.uz/
uz
co.uz
com.uz
net.uz
org.uz

// va : https://en.wikipedia.org/wiki/.va
va

// vc : https://en.wikipedia.org/wiki/.vc
// Submitted by registry <kshah@ca.afilias.info>
vc
com.vc
net.vc
org.vc
gov.vc
mil.vc
edu.vc

// ve : https://registro.nic.ve/
// Submitted by registry nic@nic.ve and nicve@conatel.gob.ve
ve
arts.ve
bib.ve
co.ve
com.ve
e12.ve
edu.ve
firm.ve
gob.ve
gov.ve
info.ve
int.ve
mil.ve
net.ve
nom.ve
org.ve
rar.ve
rec.ve
store.ve
tec.ve
web.ve

// vg : https://en.wikipedia.org/wiki/.vg
vg

// vi : http://www.nic.vi/newdomainform.htm
// http://www.nic.vi/Domain_Rules/body_domain_rules.html indicates some other
// TLDs are "reserved", such as edu.vi and gov.vi, but doesn't actually say they
// are available for registration (which they do not seem to be).
vi
co.vi
com.vi
k12.vi
net.vi
org.vi

// vn : https://www.vnnic.vn/en/domain/cctld-vn
// https://vnnic.vn/sites/default/files/tailieu/vn.cctld.domains.txt
vn
ac.vn
ai.vn
biz.vn
com.vn
edu.vn
gov.vn
health.vn
id.vn
info.vn
int.vn
io.vn
name.vn
net.vn
org.vn
pro.vn

// vn geographical names
angiang.vn
bacgiang.vn
backan.vn
baclieu.vn
bacninh.vn
baria-vungtau.vn
bentre.vn
binhdinh.vn
binhduong.vn
binhphuoc.vn
binhthuan.vn
camau.vn
cantho.vn
caobang.vn
daklak.vn
daknong.vn
danang.vn
dienbien.vn
dongnai.vn
dongthap.vn
gialai.vn
hagiang.vn
haiduong.vn
haiphong.vn
hanam.vn
hanoi.vn
hatinh.vn
haugiang.vn
hoabinh.vn
hungyen.vn
khanhhoa.vn
kiengiang.vn
kontum.vn
laichau.vn
lamdong.vn
langson.vn
laocai.vn
longan.vn
namdinh.vn
nghean.vn
ninhbinh.vn
ninhthuan.vn
phutho.vn
phuyen.vn
quangbinh.vn
quangnam.vn
quangngai.vn
quangninh.vn
quangtri.vn
soctrang.vn
sonla.vn
tayninh.vn
thaibinh.vn
thainguyen.vn
thanhhoa.vn
thanhphohochiminh.vn
thuathienhue.vn
tiengiang.vn
travinh.vn
tuyenquang.vn
vinhlong.vn
vinhphuc.vn
yenbai.vn

// vu : https://en.wikipedia.org/wiki/.vu
// http://www.vunic.vu/
vu
com.vu
edu.vu
net.vu
org.vu

// wf : https://www.afnic.fr/wp-media/uploads/2022/12/afnic-naming-policy-2023-01-01.pdf
wf

// ws : https://en.wikipedia.org/wiki/.ws
// http://samoanic.ws/index.dhtml
ws
com.ws
net.ws
org.ws
gov.ws
edu.ws

// yt : https://www.afnic.fr/wp-media/uploads/2022/12/afnic-naming-policy-2023-01-01.pdf
yt

// IDN ccTLDs
// When submitting patches, please maintain a sort by ISO 3166 ccTLD, then
// U-label, and follow this format:
// // A-Label ("<Latin renderings>", <language name>[, variant info]) : <ISO 3166 ccTLD>
// // [sponsoring org]
// U-Label

// xn--mgbaam7a8h ("Emerat", Arabic) : AE
// http://nic.ae/english/arabicdomain/rules.jsp
Ø§Ù…Ø§Ø±Ø§Øª

// xn--y9a3aq ("hye", Armenian) : AM
// ISOC AM (operated by .am Registry)
Õ°Õ¡Õµ

// xn--54b7fta0cc ("Bangla", Bangla) : BD
à¦¬à¦¾à¦‚à¦²à¦¾

// xn--90ae ("bg", Bulgarian) : BG
Ð±Ð³

// xn--mgbcpq6gpa1a ("albahrain", Arabic) : BH
Ø§Ù„Ø¨Ø­Ø±ÙŠÙ†

// xn--90ais ("bel", Belarusian/Russian Cyrillic) : BY
// Operated by .by registry
Ð±ÐµÐ»

// xn--fiqs8s ("Zhongguo/China", Chinese, Simplified) : CN
// CNNIC
// http://cnnic.cn/html/Dir/2005/10/11/3218.htm
ä¸­å›½

// xn--fiqz9s ("Zhongguo/China", Chinese, Traditional) : CN
// CNNIC
// http://cnnic.cn/html/Dir/2005/10/11/3218.htm
ä¸­åœ‹

// xn--lgbbat1ad8j ("Algeria/Al Jazair", Arabic) : DZ
Ø§Ù„Ø¬Ø²Ø§Ø¦Ø±

// xn--wgbh1c ("Egypt/Masr", Arabic) : EG
// http://www.dotmasr.eg/
Ù…ØµØ±

// xn--e1a4c ("eu", Cyrillic) : EU
// https://eurid.eu
ÐµÑŽ

// xn--qxa6a ("eu", Greek) : EU
// https://eurid.eu
ÎµÏ…

// xn--mgbah1a3hjkrd ("Mauritania", Arabic) : MR
Ù…ÙˆØ±ÙŠØªØ§Ù†ÙŠØ§

// xn--node ("ge", Georgian Mkhedruli) : GE
áƒ’áƒ”

// xn--qxam ("el", Greek) : GR
// Hellenic Ministry of Infrastructure, Transport, and Networks
ÎµÎ»

// xn--j6w193g ("Hong Kong", Chinese) : HK
// https://www.hkirc.hk
// Submitted by registry <hk.tech@hkirc.hk>
// https://www.hkirc.hk/content.jsp?id=30#!/34
é¦™æ¸¯
å…¬å�¸.é¦™æ¸¯
æ•™è‚².é¦™æ¸¯
æ”¿åºœ.é¦™æ¸¯
å€‹äºº.é¦™æ¸¯
ç¶²çµ¡.é¦™æ¸¯
çµ„ç¹”.é¦™æ¸¯

// xn--2scrj9c ("Bharat", Kannada) : IN
// India
à²­à²¾à²°à²¤

// xn--3hcrj9c ("Bharat", Oriya) : IN
// India
à¬­à¬¾à¬°à¬¤

// xn--45br5cyl ("Bharatam", Assamese) : IN
// India
à¦­à¦¾à§°à¦¤

// xn--h2breg3eve ("Bharatam", Sanskrit) : IN
// India
à¤­à¤¾à¤°à¤¤à¤®à¥�

// xn--h2brj9c8c ("Bharot", Santali) : IN
// India
à¤­à¤¾à¤°à¥‹à¤¤

// xn--mgbgu82a ("Bharat", Sindhi) : IN
// India
Ú€Ø§Ø±Øª

// xn--rvc1e0am3e ("Bharatam", Malayalam) : IN
// India
à´­à´¾à´°à´¤à´‚

// xn--h2brj9c ("Bharat", Devanagari) : IN
// India
à¤­à¤¾à¤°à¤¤

// xn--mgbbh1a ("Bharat", Kashmiri) : IN
// India
Ø¨Ø§Ø±Øª

// xn--mgbbh1a71e ("Bharat", Arabic) : IN
// India
Ø¨Ú¾Ø§Ø±Øª

// xn--fpcrj9c3d ("Bharat", Telugu) : IN
// India
à°­à°¾à°°à°¤à±�

// xn--gecrj9c ("Bharat", Gujarati) : IN
// India
àª­àª¾àª°àª¤

// xn--s9brj9c ("Bharat", Gurmukhi) : IN
// India
à¨­à¨¾à¨°à¨¤

// xn--45brj9c ("Bharat", Bengali) : IN
// India
à¦­à¦¾à¦°à¦¤

// xn--xkc2dl3a5ee0h ("India", Tamil) : IN
// India
à®‡à®¨à¯�à®¤à®¿à®¯à®¾

// xn--mgba3a4f16a ("Iran", Persian) : IR
Ø§ÛŒØ±Ø§Ù†

// xn--mgba3a4fra ("Iran", Arabic) : IR
Ø§ÙŠØ±Ø§Ù†

// xn--mgbtx2b ("Iraq", Arabic) : IQ
// Communications and Media Commission
Ø¹Ø±Ø§Ù‚

// xn--mgbayh7gpa ("al-Ordon", Arabic) : JO
// National Information Technology Center (NITC)
// Royal Scientific Society, Al-Jubeiha
Ø§Ù„Ø§Ø±Ø¯Ù†

// xn--3e0b707e ("Republic of Korea", Hangul) : KR
í•œêµ­

// xn--80ao21a ("Kaz", Kazakh) : KZ
Ò›Ð°Ð·

// xn--q7ce6a ("Lao", Lao) : LA
àº¥àº²àº§

// xn--fzc2c9e2c ("Lanka", Sinhalese-Sinhala) : LK
// https://nic.lk
à¶½à¶‚à¶šà·�

// xn--xkc2al3hye2a ("Ilangai", Tamil) : LK
// https://nic.lk
à®‡à®²à®™à¯�à®•à¯ˆ

// xn--mgbc0a9azcg ("Morocco/al-Maghrib", Arabic) : MA
Ø§Ù„Ù…ØºØ±Ø¨

// xn--d1alf ("mkd", Macedonian) : MK
// MARnet
Ð¼ÐºÐ´

// xn--l1acc ("mon", Mongolian) : MN
Ð¼Ð¾Ð½

// xn--mix891f ("Macao", Chinese, Traditional) : MO
// MONIC / HNET Asia (Registry Operator for .mo)
æ¾³é–€

// xn--mix082f ("Macao", Chinese, Simplified) : MO
æ¾³é—¨

// xn--mgbx4cd0ab ("Malaysia", Malay) : MY
Ù…Ù„ÙŠØ³ÙŠØ§

// xn--mgb9awbf ("Oman", Arabic) : OM
Ø¹Ù…Ø§Ù†

// xn--mgbai9azgqp6j ("Pakistan", Urdu/Arabic) : PK
Ù¾Ø§Ú©Ø³ØªØ§Ù†

// xn--mgbai9a5eva00b ("Pakistan", Urdu/Arabic, variant) : PK
Ù¾Ø§ÙƒØ³ØªØ§Ù†

// xn--ygbi2ammx ("Falasteen", Arabic) : PS
// The Palestinian National Internet Naming Authority (PNINA)
// http://www.pnina.ps
Ù�Ù„Ø³Ø·ÙŠÙ†

// xn--90a3ac ("srb", Cyrillic) : RS
// https://www.rnids.rs/en/domains/national-domains
Ñ�Ñ€Ð±
Ð¿Ñ€.Ñ�Ñ€Ð±
Ð¾Ñ€Ð³.Ñ�Ñ€Ð±
Ð¾Ð±Ñ€.Ñ�Ñ€Ð±
Ð¾Ð´.Ñ�Ñ€Ð±
ÑƒÐ¿Ñ€.Ñ�Ñ€Ð±
Ð°Ðº.Ñ�Ñ€Ð±

// xn--p1ai ("rf", Russian-Cyrillic) : RU
// https://cctld.ru/files/pdf/docs/en/rules_ru-rf.pdf
// Submitted by George Georgievsky <gug@cctld.ru>
Ñ€Ñ„

// xn--wgbl6a ("Qatar", Arabic) : QA
// http://www.ict.gov.qa/
Ù‚Ø·Ø±

// xn--mgberp4a5d4ar ("AlSaudiah", Arabic) : SA
// http://www.nic.net.sa/
Ø§Ù„Ø³Ø¹ÙˆØ¯ÙŠØ©

// xn--mgberp4a5d4a87g ("AlSaudiah", Arabic, variant)  : SA
Ø§Ù„Ø³Ø¹ÙˆØ¯ÛŒØ©

// xn--mgbqly7c0a67fbc ("AlSaudiah", Arabic, variant) : SA
Ø§Ù„Ø³Ø¹ÙˆØ¯ÛŒÛƒ

// xn--mgbqly7cvafr ("AlSaudiah", Arabic, variant) : SA
Ø§Ù„Ø³Ø¹ÙˆØ¯ÙŠÙ‡

// xn--mgbpl2fh ("sudan", Arabic) : SD
// Operated by .sd registry
Ø³ÙˆØ¯Ø§Ù†

// xn--yfro4i67o Singapore ("Singapore", Chinese) : SG
æ–°åŠ å�¡

// xn--clchc0ea0b2g2a9gcd ("Singapore", Tamil) : SG
à®šà®¿à®™à¯�à®•à®ªà¯�à®ªà¯‚à®°à¯�

// xn--ogbpf8fl ("Syria", Arabic) : SY
Ø³ÙˆØ±ÙŠØ©

// xn--mgbtf8fl ("Syria", Arabic, variant) : SY
Ø³ÙˆØ±ÙŠØ§

// xn--o3cw4h ("Thai", Thai) : TH
// http://www.thnic.co.th
à¹„à¸—à¸¢
à¸¨à¸¶à¸�à¸©à¸².à¹„à¸—à¸¢
à¸˜à¸¸à¸£à¸�à¸´à¸ˆ.à¹„à¸—à¸¢
à¸£à¸±à¸�à¸šà¸²à¸¥.à¹„à¸—à¸¢
à¸—à¸«à¸²à¸£.à¹„à¸—à¸¢
à¹€à¸™à¹‡à¸•.à¹„à¸—à¸¢
à¸­à¸‡à¸„à¹Œà¸�à¸£.à¹„à¸—à¸¢

// xn--pgbs0dh ("Tunisia", Arabic) : TN
// http://nic.tn
ØªÙˆÙ†Ø³

// xn--kpry57d ("Taiwan", Chinese, Traditional) : TW
// http://www.twnic.net/english/dn/dn_07a.htm
å�°ç�£

// xn--kprw13d ("Taiwan", Chinese, Simplified) : TW
// http://www.twnic.net/english/dn/dn_07a.htm
å�°æ¹¾

// xn--nnx388a ("Taiwan", Chinese, variant) : TW
è‡ºç�£

// xn--j1amh ("ukr", Cyrillic) : UA
ÑƒÐºÑ€

// xn--mgb2ddes ("AlYemen", Arabic) : YE
Ø§Ù„ÙŠÙ…Ù†

// xxx : http://icmregistry.com
xxx

// ye : http://www.y.net.ye/services/domain_name.htm
ye
com.ye
edu.ye
gov.ye
net.ye
mil.ye
org.ye

// za : https://www.zadna.org.za/content/page/domain-information/
ac.za
agric.za
alt.za
co.za
edu.za
gov.za
grondar.za
law.za
mil.za
net.za
ngo.za
nic.za
nis.za
nom.za
org.za
school.za
tm.za
web.za

// zm : https://zicta.zm/
// Submitted by registry <info@zicta.zm>
zm
ac.zm
biz.zm
co.zm
com.zm
edu.zm
gov.zm
info.zm
mil.zm
net.zm
org.zm
sch.zm

// zw : https://www.potraz.gov.zw/
// Confirmed by registry <bmtengwa@potraz.gov.zw> 2017-01-25
zw
ac.zw
co.zw
gov.zw
mil.zw
org.zw


// newGTLDs

// List of new gTLDs imported from https://www.icann.org/resources/registries/gtlds/v2/gtlds.json on 2023-09-30T15:11:25Z
// This list is auto-generated, don't edit it manually.
// aaa : American Automobile Association, Inc.
// https://www.iana.org/domains/root/db/aaa.html
aaa

// aarp : AARP
// https://www.iana.org/domains/root/db/aarp.html
aarp

// abb : ABB Ltd
// https://www.iana.org/domains/root/db/abb.html
abb

// abbott : Abbott Laboratories, Inc.
// https://www.iana.org/domains/root/db/abbott.html
abbott

// abbvie : AbbVie Inc.
// https://www.iana.org/domains/root/db/abbvie.html
abbvie

// abc : Disney Enterprises, Inc.
// https://www.iana.org/domains/root/db/abc.html
abc

// able : Able Inc.
// https://www.iana.org/domains/root/db/able.html
able

// abogado : Registry Services, LLC
// https://www.iana.org/domains/root/db/abogado.html
abogado

// abudhabi : Abu Dhabi Systems and Information Centre
// https://www.iana.org/domains/root/db/abudhabi.html
abudhabi

// academy : Binky Moon, LLC
// https://www.iana.org/domains/root/db/academy.html
academy

// accenture : Accenture plc
// https://www.iana.org/domains/root/db/accenture.html
accenture

// accountant : dot Accountant Limited
// https://www.iana.org/domains/root/db/accountant.html
accountant

// accountants : Binky Moon, LLC
// https://www.iana.org/domains/root/db/accountants.html
accountants

// aco : ACO Severin Ahlmann GmbH & Co. KG
// https://www.iana.org/domains/root/db/aco.html
aco

// actor : Dog Beach, LLC
// https://www.iana.org/domains/root/db/actor.html
actor

// ads : Charleston Road Registry Inc.
// https://www.iana.org/domains/root/db/ads.html
ads

// adult : ICM Registry AD LLC
// https://www.iana.org/domains/root/db/adult.html
adult

// aeg : Aktiebolaget Electrolux
// https://www.iana.org/domains/root/db/aeg.html
aeg

// aetna : Aetna Life Insurance Company
// https://www.iana.org/domains/root/db/aetna.html
aetna

// afl : Australian Football League
// https://www.iana.org/domains/root/db/afl.html
afl

// africa : ZA Central Registry NPC trading as Registry.Africa
// https://www.iana.org/domains/root/db/africa.html
africa

// agakhan : Fondation Aga Khan (Aga Khan Foundation)
// https://www.iana.org/domains/root/db/agakhan.html
agakhan

// agency : Binky Moon, LLC
// https://www.iana.org/domains/root/db/agency.html
agency

// aig : American International Group, Inc.
// https://www.iana.org/domains/root/db/aig.html
aig

// airbus : Airbus S.A.S.
// https://www.iana.org/domains/root/db/airbus.html
airbus

// airforce : Dog Beach, LLC
// https://www.iana.org/domains/root/db/airforce.html
airforce

// airtel : Bharti Airtel Limited
// https://www.iana.org/domains/root/db/airtel.html
airtel

// akdn : Fondation Aga Khan (Aga Khan Foundation)
// https://www.iana.org/domains/root/db/akdn.html
akdn

// alibaba : Alibaba Group Holding Limited
// https://www.iana.org/domains/root/db/alibaba.html
alibaba

// alipay : Alibaba Group Holding Limited
// https://www.iana.org/domains/root/db/alipay.html
alipay

// allfinanz : Allfinanz Deutsche VermÃ¶gensberatung Aktiengesellschaft
// https://www.iana.org/domains/root/db/allfinanz.html
allfinanz

// allstate : Allstate Fire and Casualty Insurance Company
// https://www.iana.org/domains/root/db/allstate.html
allstate

// ally : Ally Financial Inc.
// https://www.iana.org/domains/root/db/ally.html
ally

// alsace : Region Grand Est
// https://www.iana.org/domains/root/db/alsace.html
alsace

// alstom : ALSTOM
// https://www.iana.org/domains/root/db/alstom.html
alstom

// amazon : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/amazon.html
amazon

// americanexpress : American Express Travel Related Services Company, Inc.
// https://www.iana.org/domains/root/db/americanexpress.html
americanexpress

// americanfamily : AmFam, Inc.
// https://www.iana.org/domains/root/db/americanfamily.html
americanfamily

// amex : American Express Travel Related Services Company, Inc.
// https://www.iana.org/domains/root/db/amex.html
amex

// amfam : AmFam, Inc.
// https://www.iana.org/domains/root/db/amfam.html
amfam

// amica : Amica Mutual Insurance Company
// https://www.iana.org/domains/root/db/amica.html
amica

// amsterdam : Gemeente Amsterdam
// https://www.iana.org/domains/root/db/amsterdam.html
amsterdam

// analytics : Campus IP LLC
// https://www.iana.org/domains/root/db/analytics.html
analytics

// android : Charleston Road Registry Inc.
// https://www.iana.org/domains/root/db/android.html
android

// anquan : Beijing Qihu Keji Co., Ltd.
// https://www.iana.org/domains/root/db/anquan.html
anquan

// anz : Australia and New Zealand Banking Group Limited
// https://www.iana.org/domains/root/db/anz.html
anz

// aol : Oath Inc.
// https://www.iana.org/domains/root/db/aol.html
aol

// apartments : Binky Moon, LLC
// https://www.iana.org/domains/root/db/apartments.html
apartments

// app : Charleston Road Registry Inc.
// https://www.iana.org/domains/root/db/app.html
app

// apple : Apple Inc.
// https://www.iana.org/domains/root/db/apple.html
apple

// aquarelle : Aquarelle.com
// https://www.iana.org/domains/root/db/aquarelle.html
aquarelle

// arab : League of Arab States
// https://www.iana.org/domains/root/db/arab.html
arab

// aramco : Aramco Services Company
// https://www.iana.org/domains/root/db/aramco.html
aramco

// archi : Identity Digital Limited
// https://www.iana.org/domains/root/db/archi.html
archi

// army : Dog Beach, LLC
// https://www.iana.org/domains/root/db/army.html
army

// art : UK Creative Ideas Limited
// https://www.iana.org/domains/root/db/art.html
art

// arte : Association Relative Ã  la TÃ©lÃ©vision EuropÃ©enne G.E.I.E.
// https://www.iana.org/domains/root/db/arte.html
arte

// asda : Wal-Mart Stores, Inc.
// https://www.iana.org/domains/root/db/asda.html
asda

// associates : Binky Moon, LLC
// https://www.iana.org/domains/root/db/associates.html
associates

// athleta : The Gap, Inc.
// https://www.iana.org/domains/root/db/athleta.html
athleta

// attorney : Dog Beach, LLC
// https://www.iana.org/domains/root/db/attorney.html
attorney

// auction : Dog Beach, LLC
// https://www.iana.org/domains/root/db/auction.html
auction

// audi : AUDI Aktiengesellschaft
// https://www.iana.org/domains/root/db/audi.html
audi

// audible : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/audible.html
audible

// audio : XYZ.COM LLC
// https://www.iana.org/domains/root/db/audio.html
audio

// auspost : Australian Postal Corporation
// https://www.iana.org/domains/root/db/auspost.html
auspost

// author : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/author.html
author

// auto : XYZ.COM LLC
// https://www.iana.org/domains/root/db/auto.html
auto

// autos : XYZ.COM LLC
// https://www.iana.org/domains/root/db/autos.html
autos

// avianca : Avianca Inc.
// https://www.iana.org/domains/root/db/avianca.html
avianca

// aws : AWS Registry LLC
// https://www.iana.org/domains/root/db/aws.html
aws

// axa : AXA Group Operations SAS
// https://www.iana.org/domains/root/db/axa.html
axa

// azure : Microsoft Corporation
// https://www.iana.org/domains/root/db/azure.html
azure

// baby : XYZ.COM LLC
// https://www.iana.org/domains/root/db/baby.html
baby

// baidu : Baidu, Inc.
// https://www.iana.org/domains/root/db/baidu.html
baidu

// banamex : Citigroup Inc.
// https://www.iana.org/domains/root/db/banamex.html
banamex

// bananarepublic : The Gap, Inc.
// https://www.iana.org/domains/root/db/bananarepublic.html
bananarepublic

// band : Dog Beach, LLC
// https://www.iana.org/domains/root/db/band.html
band

// bank : fTLD Registry Services LLC
// https://www.iana.org/domains/root/db/bank.html
bank

// bar : Punto 2012 Sociedad Anonima Promotora de Inversion de Capital Variable
// https://www.iana.org/domains/root/db/bar.html
bar

// barcelona : Municipi de Barcelona
// https://www.iana.org/domains/root/db/barcelona.html
barcelona

// barclaycard : Barclays Bank PLC
// https://www.iana.org/domains/root/db/barclaycard.html
barclaycard

// barclays : Barclays Bank PLC
// https://www.iana.org/domains/root/db/barclays.html
barclays

// barefoot : Gallo Vineyards, Inc.
// https://www.iana.org/domains/root/db/barefoot.html
barefoot

// bargains : Binky Moon, LLC
// https://www.iana.org/domains/root/db/bargains.html
bargains

// baseball : MLB Advanced Media DH, LLC
// https://www.iana.org/domains/root/db/baseball.html
baseball

// basketball : FÃ©dÃ©ration Internationale de Basketball (FIBA)
// https://www.iana.org/domains/root/db/basketball.html
basketball

// bauhaus : Werkhaus GmbH
// https://www.iana.org/domains/root/db/bauhaus.html
bauhaus

// bayern : Bayern Connect GmbH
// https://www.iana.org/domains/root/db/bayern.html
bayern

// bbc : British Broadcasting Corporation
// https://www.iana.org/domains/root/db/bbc.html
bbc

// bbt : BB&T Corporation
// https://www.iana.org/domains/root/db/bbt.html
bbt

// bbva : BANCO BILBAO VIZCAYA ARGENTARIA, S.A.
// https://www.iana.org/domains/root/db/bbva.html
bbva

// bcg : The Boston Consulting Group, Inc.
// https://www.iana.org/domains/root/db/bcg.html
bcg

// bcn : Municipi de Barcelona
// https://www.iana.org/domains/root/db/bcn.html
bcn

// beats : Beats Electronics, LLC
// https://www.iana.org/domains/root/db/beats.html
beats

// beauty : XYZ.COM LLC
// https://www.iana.org/domains/root/db/beauty.html
beauty

// beer : Registry Services, LLC
// https://www.iana.org/domains/root/db/beer.html
beer

// bentley : Bentley Motors Limited
// https://www.iana.org/domains/root/db/bentley.html
bentley

// berlin : dotBERLIN GmbH & Co. KG
// https://www.iana.org/domains/root/db/berlin.html
berlin

// best : BestTLD Pty Ltd
// https://www.iana.org/domains/root/db/best.html
best

// bestbuy : BBY Solutions, Inc.
// https://www.iana.org/domains/root/db/bestbuy.html
bestbuy

// bet : Identity Digital Limited
// https://www.iana.org/domains/root/db/bet.html
bet

// bharti : Bharti Enterprises (Holding) Private Limited
// https://www.iana.org/domains/root/db/bharti.html
bharti

// bible : American Bible Society
// https://www.iana.org/domains/root/db/bible.html
bible

// bid : dot Bid Limited
// https://www.iana.org/domains/root/db/bid.html
bid

// bike : Binky Moon, LLC
// https://www.iana.org/domains/root/db/bike.html
bike

// bing : Microsoft Corporation
// https://www.iana.org/domains/root/db/bing.html
bing

// bingo : Binky Moon, LLC
// https://www.iana.org/domains/root/db/bingo.html
bingo

// bio : Identity Digital Limited
// https://www.iana.org/domains/root/db/bio.html
bio

// black : Identity Digital Limited
// https://www.iana.org/domains/root/db/black.html
black

// blackfriday : Registry Services, LLC
// https://www.iana.org/domains/root/db/blackfriday.html
blackfriday

// blockbuster : Dish DBS Corporation
// https://www.iana.org/domains/root/db/blockbuster.html
blockbuster

// blog : Knock Knock WHOIS There, LLC
// https://www.iana.org/domains/root/db/blog.html
blog

// bloomberg : Bloomberg IP Holdings LLC
// https://www.iana.org/domains/root/db/bloomberg.html
bloomberg

// blue : Identity Digital Limited
// https://www.iana.org/domains/root/db/blue.html
blue

// bms : Bristol-Myers Squibb Company
// https://www.iana.org/domains/root/db/bms.html
bms

// bmw : Bayerische Motoren Werke Aktiengesellschaft
// https://www.iana.org/domains/root/db/bmw.html
bmw

// bnpparibas : BNP Paribas
// https://www.iana.org/domains/root/db/bnpparibas.html
bnpparibas

// boats : XYZ.COM LLC
// https://www.iana.org/domains/root/db/boats.html
boats

// boehringer : Boehringer Ingelheim International GmbH
// https://www.iana.org/domains/root/db/boehringer.html
boehringer

// bofa : Bank of America Corporation
// https://www.iana.org/domains/root/db/bofa.html
bofa

// bom : NÃºcleo de InformaÃ§Ã£o e CoordenaÃ§Ã£o do Ponto BR - NIC.br
// https://www.iana.org/domains/root/db/bom.html
bom

// bond : ShortDot SA
// https://www.iana.org/domains/root/db/bond.html
bond

// boo : Charleston Road Registry Inc.
// https://www.iana.org/domains/root/db/boo.html
boo

// book : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/book.html
book

// booking : Booking.com B.V.
// https://www.iana.org/domains/root/db/booking.html
booking

// bosch : Robert Bosch GMBH
// https://www.iana.org/domains/root/db/bosch.html
bosch

// bostik : Bostik SA
// https://www.iana.org/domains/root/db/bostik.html
bostik

// boston : Registry Services, LLC
// https://www.iana.org/domains/root/db/boston.html
boston

// bot : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/bot.html
bot

// boutique : Binky Moon, LLC
// https://www.iana.org/domains/root/db/boutique.html
boutique

// box : Intercap Registry Inc.
// https://www.iana.org/domains/root/db/box.html
box

// bradesco : Banco Bradesco S.A.
// https://www.iana.org/domains/root/db/bradesco.html
bradesco

// bridgestone : Bridgestone Corporation
// https://www.iana.org/domains/root/db/bridgestone.html
bridgestone

// broadway : Celebrate Broadway, Inc.
// https://www.iana.org/domains/root/db/broadway.html
broadway

// broker : Dog Beach, LLC
// https://www.iana.org/domains/root/db/broker.html
broker

// brother : Brother Industries, Ltd.
// https://www.iana.org/domains/root/db/brother.html
brother

// brussels : DNS.be vzw
// https://www.iana.org/domains/root/db/brussels.html
brussels

// build : Plan Bee LLC
// https://www.iana.org/domains/root/db/build.html
build

// builders : Binky Moon, LLC
// https://www.iana.org/domains/root/db/builders.html
builders

// business : Binky Moon, LLC
// https://www.iana.org/domains/root/db/business.html
business

// buy : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/buy.html
buy

// buzz : DOTSTRATEGY CO.
// https://www.iana.org/domains/root/db/buzz.html
buzz

// bzh : Association www.bzh
// https://www.iana.org/domains/root/db/bzh.html
bzh

// cab : Binky Moon, LLC
// https://www.iana.org/domains/root/db/cab.html
cab

// cafe : Binky Moon, LLC
// https://www.iana.org/domains/root/db/cafe.html
cafe

// cal : Charleston Road Registry Inc.
// https://www.iana.org/domains/root/db/cal.html
cal

// call : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/call.html
call

// calvinklein : PVH gTLD Holdings LLC
// https://www.iana.org/domains/root/db/calvinklein.html
calvinklein

// cam : Cam Connecting SARL
// https://www.iana.org/domains/root/db/cam.html
cam

// camera : Binky Moon, LLC
// https://www.iana.org/domains/root/db/camera.html
camera

// camp : Binky Moon, LLC
// https://www.iana.org/domains/root/db/camp.html
camp

// canon : Canon Inc.
// https://www.iana.org/domains/root/db/canon.html
canon

// capetown : ZA Central Registry NPC trading as ZA Central Registry
// https://www.iana.org/domains/root/db/capetown.html
capetown

// capital : Binky Moon, LLC
// https://www.iana.org/domains/root/db/capital.html
capital

// capitalone : Capital One Financial Corporation
// https://www.iana.org/domains/root/db/capitalone.html
capitalone

// car : XYZ.COM LLC
// https://www.iana.org/domains/root/db/car.html
car

// caravan : Caravan International, Inc.
// https://www.iana.org/domains/root/db/caravan.html
caravan

// cards : Binky Moon, LLC
// https://www.iana.org/domains/root/db/cards.html
cards

// care : Binky Moon, LLC
// https://www.iana.org/domains/root/db/care.html
care

// career : dotCareer LLC
// https://www.iana.org/domains/root/db/career.html
career

// careers : Binky Moon, LLC
// https://www.iana.org/domains/root/db/careers.html
careers

// cars : XYZ.COM LLC
// https://www.iana.org/domains/root/db/cars.html
cars

// casa : Registry Services, LLC
// https://www.iana.org/domains/root/db/casa.html
casa

// case : Digity, LLC
// https://www.iana.org/domains/root/db/case.html
case

// cash : Binky Moon, LLC
// https://www.iana.org/domains/root/db/cash.html
cash

// casino : Binky Moon, LLC
// https://www.iana.org/domains/root/db/casino.html
casino

// catering : Binky Moon, LLC
// https://www.iana.org/domains/root/db/catering.html
catering

// catholic : Pontificium Consilium de Comunicationibus Socialibus (PCCS) (Pontifical Council for Social Communication)
// https://www.iana.org/domains/root/db/catholic.html
catholic

// cba : COMMONWEALTH BANK OF AUSTRALIA
// https://www.iana.org/domains/root/db/cba.html
cba

// cbn : The Christian Broadcasting Network, Inc.
// https://www.iana.org/domains/root/db/cbn.html
cbn

// cbre : CBRE, Inc.
// https://www.iana.org/domains/root/db/cbre.html
cbre

// cbs : CBS Domains Inc.
// https://www.iana.org/domains/root/db/cbs.html
cbs

// center : Binky Moon, LLC
// https://www.iana.org/domains/root/db/center.html
center

// ceo : XYZ.COM LLC
// https://www.iana.org/domains/root/db/ceo.html
ceo

// cern : European Organization for Nuclear Research ("CERN")
// https://www.iana.org/domains/root/db/cern.html
cern

// cfa : CFA Institute
// https://www.iana.org/domains/root/db/cfa.html
cfa

// cfd : ShortDot SA
// https://www.iana.org/domains/root/db/cfd.html
cfd

// chanel : Chanel International B.V.
// https://www.iana.org/domains/root/db/chanel.html
chanel

// channel : Charleston Road Registry Inc.
// https://www.iana.org/domains/root/db/channel.html
channel

// charity : Public Interest Registry
// https://www.iana.org/domains/root/db/charity.html
charity

// chase : JPMorgan Chase Bank, National Association
// https://www.iana.org/domains/root/db/chase.html
chase

// chat : Binky Moon, LLC
// https://www.iana.org/domains/root/db/chat.html
chat

// cheap : Binky Moon, LLC
// https://www.iana.org/domains/root/db/cheap.html
cheap

// chintai : CHINTAI Corporation
// https://www.iana.org/domains/root/db/chintai.html
chintai

// christmas : XYZ.COM LLC
// https://www.iana.org/domains/root/db/christmas.html
christmas

// chrome : Charleston Road Registry Inc.
// https://www.iana.org/domains/root/db/chrome.html
chrome

// church : Binky Moon, LLC
// https://www.iana.org/domains/root/db/church.html
church

// cipriani : Hotel Cipriani Srl
// https://www.iana.org/domains/root/db/cipriani.html
cipriani

// circle : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/circle.html
circle

// cisco : Cisco Technology, Inc.
// https://www.iana.org/domains/root/db/cisco.html
cisco

// citadel : Citadel Domain LLC
// https://www.iana.org/domains/root/db/citadel.html
citadel

// citi : Citigroup Inc.
// https://www.iana.org/domains/root/db/citi.html
citi

// citic : CITIC Group Corporation
// https://www.iana.org/domains/root/db/citic.html
citic

// city : Binky Moon, LLC
// https://www.iana.org/domains/root/db/city.html
city

// cityeats : Lifestyle Domain Holdings, Inc.
// https://www.iana.org/domains/root/db/cityeats.html
cityeats

// claims : Binky Moon, LLC
// https://www.iana.org/domains/root/db/claims.html
claims

// cleaning : Binky Moon, LLC
// https://www.iana.org/domains/root/db/cleaning.html
cleaning

// click : Internet Naming Company LLC
// https://www.iana.org/domains/root/db/click.html
click

// clinic : Binky Moon, LLC
// https://www.iana.org/domains/root/db/clinic.html
clinic

// clinique : The EstÃ©e Lauder Companies Inc.
// https://www.iana.org/domains/root/db/clinique.html
clinique

// clothing : Binky Moon, LLC
// https://www.iana.org/domains/root/db/clothing.html
clothing

// cloud : Aruba PEC S.p.A.
// https://www.iana.org/domains/root/db/cloud.html
cloud

// club : Registry Services, LLC
// https://www.iana.org/domains/root/db/club.html
club

// clubmed : Club MÃ©diterranÃ©e S.A.
// https://www.iana.org/domains/root/db/clubmed.html
clubmed

// coach : Binky Moon, LLC
// https://www.iana.org/domains/root/db/coach.html
coach

// codes : Binky Moon, LLC
// https://www.iana.org/domains/root/db/codes.html
codes

// coffee : Binky Moon, LLC
// https://www.iana.org/domains/root/db/coffee.html
coffee

// college : XYZ.COM LLC
// https://www.iana.org/domains/root/db/college.html
college

// cologne : dotKoeln GmbH
// https://www.iana.org/domains/root/db/cologne.html
cologne

// comcast : Comcast IP Holdings I, LLC
// https://www.iana.org/domains/root/db/comcast.html
comcast

// commbank : COMMONWEALTH BANK OF AUSTRALIA
// https://www.iana.org/domains/root/db/commbank.html
commbank

// community : Binky Moon, LLC
// https://www.iana.org/domains/root/db/community.html
community

// company : Binky Moon, LLC
// https://www.iana.org/domains/root/db/company.html
company

// compare : Registry Services, LLC
// https://www.iana.org/domains/root/db/compare.html
compare

// computer : Binky Moon, LLC
// https://www.iana.org/domains/root/db/computer.html
computer

// comsec : VeriSign, Inc.
// https://www.iana.org/domains/root/db/comsec.html
comsec

// condos : Binky Moon, LLC
// https://www.iana.org/domains/root/db/condos.html
condos

// construction : Binky Moon, LLC
// https://www.iana.org/domains/root/db/construction.html
construction

// consulting : Dog Beach, LLC
// https://www.iana.org/domains/root/db/consulting.html
consulting

// contact : Dog Beach, LLC
// https://www.iana.org/domains/root/db/contact.html
contact

// contractors : Binky Moon, LLC
// https://www.iana.org/domains/root/db/contractors.html
contractors

// cooking : Registry Services, LLC
// https://www.iana.org/domains/root/db/cooking.html
cooking

// cool : Binky Moon, LLC
// https://www.iana.org/domains/root/db/cool.html
cool

// corsica : CollectivitÃ© de Corse
// https://www.iana.org/domains/root/db/corsica.html
corsica

// country : Internet Naming Company LLC
// https://www.iana.org/domains/root/db/country.html
country

// coupon : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/coupon.html
coupon

// coupons : Binky Moon, LLC
// https://www.iana.org/domains/root/db/coupons.html
coupons

// courses : Registry Services, LLC
// https://www.iana.org/domains/root/db/courses.html
courses

// cpa : American Institute of Certified Public Accountants
// https://www.iana.org/domains/root/db/cpa.html
cpa

// credit : Binky Moon, LLC
// https://www.iana.org/domains/root/db/credit.html
credit

// creditcard : Binky Moon, LLC
// https://www.iana.org/domains/root/db/creditcard.html
creditcard

// creditunion : DotCooperation LLC
// https://www.iana.org/domains/root/db/creditunion.html
creditunion

// cricket : dot Cricket Limited
// https://www.iana.org/domains/root/db/cricket.html
cricket

// crown : Crown Equipment Corporation
// https://www.iana.org/domains/root/db/crown.html
crown

// crs : Federated Co-operatives Limited
// https://www.iana.org/domains/root/db/crs.html
crs

// cruise : Viking River Cruises (Bermuda) Ltd.
// https://www.iana.org/domains/root/db/cruise.html
cruise

// cruises : Binky Moon, LLC
// https://www.iana.org/domains/root/db/cruises.html
cruises

// cuisinella : SCHMIDT GROUPE S.A.S.
// https://www.iana.org/domains/root/db/cuisinella.html
cuisinella

// cymru : Nominet UK
// https://www.iana.org/domains/root/db/cymru.html
cymru

// cyou : ShortDot SA
// https://www.iana.org/domains/root/db/cyou.html
cyou

// dabur : Dabur India Limited
// https://www.iana.org/domains/root/db/dabur.html
dabur

// dad : Charleston Road Registry Inc.
// https://www.iana.org/domains/root/db/dad.html
dad

// dance : Dog Beach, LLC
// https://www.iana.org/domains/root/db/dance.html
dance

// data : Dish DBS Corporation
// https://www.iana.org/domains/root/db/data.html
data

// date : dot Date Limited
// https://www.iana.org/domains/root/db/date.html
date

// dating : Binky Moon, LLC
// https://www.iana.org/domains/root/db/dating.html
dating

// datsun : NISSAN MOTOR CO., LTD.
// https://www.iana.org/domains/root/db/datsun.html
datsun

// day : Charleston Road Registry Inc.
// https://www.iana.org/domains/root/db/day.html
day

// dclk : Charleston Road Registry Inc.
// https://www.iana.org/domains/root/db/dclk.html
dclk

// dds : Registry Services, LLC
// https://www.iana.org/domains/root/db/dds.html
dds

// deal : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/deal.html
deal

// dealer : Intercap Registry Inc.
// https://www.iana.org/domains/root/db/dealer.html
dealer

// deals : Binky Moon, LLC
// https://www.iana.org/domains/root/db/deals.html
deals

// degree : Dog Beach, LLC
// https://www.iana.org/domains/root/db/degree.html
degree

// delivery : Binky Moon, LLC
// https://www.iana.org/domains/root/db/delivery.html
delivery

// dell : Dell Inc.
// https://www.iana.org/domains/root/db/dell.html
dell

// deloitte : Deloitte Touche Tohmatsu
// https://www.iana.org/domains/root/db/deloitte.html
deloitte

// delta : Delta Air Lines, Inc.
// https://www.iana.org/domains/root/db/delta.html
delta

// democrat : Dog Beach, LLC
// https://www.iana.org/domains/root/db/democrat.html
democrat

// dental : Binky Moon, LLC
// https://www.iana.org/domains/root/db/dental.html
dental

// dentist : Dog Beach, LLC
// https://www.iana.org/domains/root/db/dentist.html
dentist

// desi : Desi Networks LLC
// https://www.iana.org/domains/root/db/desi.html
desi

// design : Registry Services, LLC
// https://www.iana.org/domains/root/db/design.html
design

// dev : Charleston Road Registry Inc.
// https://www.iana.org/domains/root/db/dev.html
dev

// dhl : Deutsche Post AG
// https://www.iana.org/domains/root/db/dhl.html
dhl

// diamonds : Binky Moon, LLC
// https://www.iana.org/domains/root/db/diamonds.html
diamonds

// diet : XYZ.COM LLC
// https://www.iana.org/domains/root/db/diet.html
diet

// digital : Binky Moon, LLC
// https://www.iana.org/domains/root/db/digital.html
digital

// direct : Binky Moon, LLC
// https://www.iana.org/domains/root/db/direct.html
direct

// directory : Binky Moon, LLC
// https://www.iana.org/domains/root/db/directory.html
directory

// discount : Binky Moon, LLC
// https://www.iana.org/domains/root/db/discount.html
discount

// discover : Discover Financial Services
// https://www.iana.org/domains/root/db/discover.html
discover

// dish : Dish DBS Corporation
// https://www.iana.org/domains/root/db/dish.html
dish

// diy : Lifestyle Domain Holdings, Inc.
// https://www.iana.org/domains/root/db/diy.html
diy

// dnp : Dai Nippon Printing Co., Ltd.
// https://www.iana.org/domains/root/db/dnp.html
dnp

// docs : Charleston Road Registry Inc.
// https://www.iana.org/domains/root/db/docs.html
docs

// doctor : Binky Moon, LLC
// https://www.iana.org/domains/root/db/doctor.html
doctor

// dog : Binky Moon, LLC
// https://www.iana.org/domains/root/db/dog.html
dog

// domains : Binky Moon, LLC
// https://www.iana.org/domains/root/db/domains.html
domains

// dot : Dish DBS Corporation
// https://www.iana.org/domains/root/db/dot.html
dot

// download : dot Support Limited
// https://www.iana.org/domains/root/db/download.html
download

// drive : Charleston Road Registry Inc.
// https://www.iana.org/domains/root/db/drive.html
drive

// dtv : Dish DBS Corporation
// https://www.iana.org/domains/root/db/dtv.html
dtv

// dubai : Dubai Smart Government Department
// https://www.iana.org/domains/root/db/dubai.html
dubai

// dunlop : The Goodyear Tire & Rubber Company
// https://www.iana.org/domains/root/db/dunlop.html
dunlop

// dupont : DuPont Specialty Products USA, LLC
// https://www.iana.org/domains/root/db/dupont.html
dupont

// durban : ZA Central Registry NPC trading as ZA Central Registry
// https://www.iana.org/domains/root/db/durban.html
durban

// dvag : Deutsche VermÃ¶gensberatung Aktiengesellschaft DVAG
// https://www.iana.org/domains/root/db/dvag.html
dvag

// dvr : DISH Technologies L.L.C.
// https://www.iana.org/domains/root/db/dvr.html
dvr

// earth : Interlink Systems Innovation Institute K.K.
// https://www.iana.org/domains/root/db/earth.html
earth

// eat : Charleston Road Registry Inc.
// https://www.iana.org/domains/root/db/eat.html
eat

// eco : Big Room Inc.
// https://www.iana.org/domains/root/db/eco.html
eco

// edeka : EDEKA Verband kaufmÃ¤nnischer Genossenschaften e.V.
// https://www.iana.org/domains/root/db/edeka.html
edeka

// education : Binky Moon, LLC
// https://www.iana.org/domains/root/db/education.html
education

// email : Binky Moon, LLC
// https://www.iana.org/domains/root/db/email.html
email

// emerck : Merck KGaA
// https://www.iana.org/domains/root/db/emerck.html
emerck

// energy : Binky Moon, LLC
// https://www.iana.org/domains/root/db/energy.html
energy

// engineer : Dog Beach, LLC
// https://www.iana.org/domains/root/db/engineer.html
engineer

// engineering : Binky Moon, LLC
// https://www.iana.org/domains/root/db/engineering.html
engineering

// enterprises : Binky Moon, LLC
// https://www.iana.org/domains/root/db/enterprises.html
enterprises

// epson : Seiko Epson Corporation
// https://www.iana.org/domains/root/db/epson.html
epson

// equipment : Binky Moon, LLC
// https://www.iana.org/domains/root/db/equipment.html
equipment

// ericsson : Telefonaktiebolaget L M Ericsson
// https://www.iana.org/domains/root/db/ericsson.html
ericsson

// erni : ERNI Group Holding AG
// https://www.iana.org/domains/root/db/erni.html
erni

// esq : Charleston Road Registry Inc.
// https://www.iana.org/domains/root/db/esq.html
esq

// estate : Binky Moon, LLC
// https://www.iana.org/domains/root/db/estate.html
estate

// etisalat : Emirates Telecommunications Corporation (trading as Etisalat)
// https://www.iana.org/domains/root/db/etisalat.html
etisalat

// eurovision : European Broadcasting Union (EBU)
// https://www.iana.org/domains/root/db/eurovision.html
eurovision

// eus : Puntueus Fundazioa
// https://www.iana.org/domains/root/db/eus.html
eus

// events : Binky Moon, LLC
// https://www.iana.org/domains/root/db/events.html
events

// exchange : Binky Moon, LLC
// https://www.iana.org/domains/root/db/exchange.html
exchange

// expert : Binky Moon, LLC
// https://www.iana.org/domains/root/db/expert.html
expert

// exposed : Binky Moon, LLC
// https://www.iana.org/domains/root/db/exposed.html
exposed

// express : Binky Moon, LLC
// https://www.iana.org/domains/root/db/express.html
express

// extraspace : Extra Space Storage LLC
// https://www.iana.org/domains/root/db/extraspace.html
extraspace

// fage : Fage International S.A.
// https://www.iana.org/domains/root/db/fage.html
fage

// fail : Binky Moon, LLC
// https://www.iana.org/domains/root/db/fail.html
fail

// fairwinds : FairWinds Partners, LLC
// https://www.iana.org/domains/root/db/fairwinds.html
fairwinds

// faith : dot Faith Limited
// https://www.iana.org/domains/root/db/faith.html
faith

// family : Dog Beach, LLC
// https://www.iana.org/domains/root/db/family.html
family

// fan : Dog Beach, LLC
// https://www.iana.org/domains/root/db/fan.html
fan

// fans : ZDNS International Limited
// https://www.iana.org/domains/root/db/fans.html
fans

// farm : Binky Moon, LLC
// https://www.iana.org/domains/root/db/farm.html
farm

// farmers : Farmers Insurance Exchange
// https://www.iana.org/domains/root/db/farmers.html
farmers

// fashion : Registry Services, LLC
// https://www.iana.org/domains/root/db/fashion.html
fashion

// fast : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/fast.html
fast

// fedex : Federal Express Corporation
// https://www.iana.org/domains/root/db/fedex.html
fedex

// feedback : Top Level Spectrum, Inc.
// https://www.iana.org/domains/root/db/feedback.html
feedback

// ferrari : Fiat Chrysler Automobiles N.V.
// https://www.iana.org/domains/root/db/ferrari.html
ferrari

// ferrero : Ferrero Trading Lux S.A.
// https://www.iana.org/domains/root/db/ferrero.html
ferrero

// fidelity : Fidelity Brokerage Services LLC
// https://www.iana.org/domains/root/db/fidelity.html
fidelity

// fido : Rogers Communications Canada Inc.
// https://www.iana.org/domains/root/db/fido.html
fido

// film : Motion Picture Domain Registry Pty Ltd
// https://www.iana.org/domains/root/db/film.html
film

// final : NÃºcleo de InformaÃ§Ã£o e CoordenaÃ§Ã£o do Ponto BR - NIC.br
// https://www.iana.org/domains/root/db/final.html
final

// finance : Binky Moon, LLC
// https://www.iana.org/domains/root/db/finance.html
finance

// financial : Binky Moon, LLC
// https://www.iana.org/domains/root/db/financial.html
financial

// fire : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/fire.html
fire

// firestone : Bridgestone Licensing Services, Inc
// https://www.iana.org/domains/root/db/firestone.html
firestone

// firmdale : Firmdale Holdings Limited
// https://www.iana.org/domains/root/db/firmdale.html
firmdale

// fish : Binky Moon, LLC
// https://www.iana.org/domains/root/db/fish.html
fish

// fishing : Registry Services, LLC
// https://www.iana.org/domains/root/db/fishing.html
fishing

// fit : Registry Services, LLC
// https://www.iana.org/domains/root/db/fit.html
fit

// fitness : Binky Moon, LLC
// https://www.iana.org/domains/root/db/fitness.html
fitness

// flickr : Flickr, Inc.
// https://www.iana.org/domains/root/db/flickr.html
flickr

// flights : Binky Moon, LLC
// https://www.iana.org/domains/root/db/flights.html
flights

// flir : FLIR Systems, Inc.
// https://www.iana.org/domains/root/db/flir.html
flir

// florist : Binky Moon, LLC
// https://www.iana.org/domains/root/db/florist.html
florist

// flowers : XYZ.COM LLC
// https://www.iana.org/domains/root/db/flowers.html
flowers

// fly : Charleston Road Registry Inc.
// https://www.iana.org/domains/root/db/fly.html
fly

// foo : Charleston Road Registry Inc.
// https://www.iana.org/domains/root/db/foo.html
foo

// food : Lifestyle Domain Holdings, Inc.
// https://www.iana.org/domains/root/db/food.html
food

// football : Binky Moon, LLC
// https://www.iana.org/domains/root/db/football.html
football

// ford : Ford Motor Company
// https://www.iana.org/domains/root/db/ford.html
ford

// forex : Dog Beach, LLC
// https://www.iana.org/domains/root/db/forex.html
forex

// forsale : Dog Beach, LLC
// https://www.iana.org/domains/root/db/forsale.html
forsale

// forum : Fegistry, LLC
// https://www.iana.org/domains/root/db/forum.html
forum

// foundation : Public Interest Registry
// https://www.iana.org/domains/root/db/foundation.html
foundation

// fox : FOX Registry, LLC
// https://www.iana.org/domains/root/db/fox.html
fox

// free : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/free.html
free

// fresenius : Fresenius Immobilien-Verwaltungs-GmbH
// https://www.iana.org/domains/root/db/fresenius.html
fresenius

// frl : FRLregistry B.V.
// https://www.iana.org/domains/root/db/frl.html
frl

// frogans : OP3FT
// https://www.iana.org/domains/root/db/frogans.html
frogans

// frontdoor : Lifestyle Domain Holdings, Inc.
// https://www.iana.org/domains/root/db/frontdoor.html
frontdoor

// frontier : Frontier Communications Corporation
// https://www.iana.org/domains/root/db/frontier.html
frontier

// ftr : Frontier Communications Corporation
// https://www.iana.org/domains/root/db/ftr.html
ftr

// fujitsu : Fujitsu Limited
// https://www.iana.org/domains/root/db/fujitsu.html
fujitsu

// fun : Radix FZC DMCC
// https://www.iana.org/domains/root/db/fun.html
fun

// fund : Binky Moon, LLC
// https://www.iana.org/domains/root/db/fund.html
fund

// furniture : Binky Moon, LLC
// https://www.iana.org/domains/root/db/furniture.html
furniture

// futbol : Dog Beach, LLC
// https://www.iana.org/domains/root/db/futbol.html
futbol

// fyi : Binky Moon, LLC
// https://www.iana.org/domains/root/db/fyi.html
fyi

// gal : AsociaciÃ³n puntoGAL
// https://www.iana.org/domains/root/db/gal.html
gal

// gallery : Binky Moon, LLC
// https://www.iana.org/domains/root/db/gallery.html
gallery

// gallo : Gallo Vineyards, Inc.
// https://www.iana.org/domains/root/db/gallo.html
gallo

// gallup : Gallup, Inc.
// https://www.iana.org/domains/root/db/gallup.html
gallup

// game : XYZ.COM LLC
// https://www.iana.org/domains/root/db/game.html
game

// games : Dog Beach, LLC
// https://www.iana.org/domains/root/db/games.html
games

// gap : The Gap, Inc.
// https://www.iana.org/domains/root/db/gap.html
gap

// garden : Registry Services, LLC
// https://www.iana.org/domains/root/db/garden.html
garden

// gay : Registry Services, LLC
// https://www.iana.org/domains/root/db/gay.html
gay

// gbiz : Charleston Road Registry Inc.
// https://www.iana.org/domains/root/db/gbiz.html
gbiz

// gdn : Joint Stock Company "Navigation-information systems"
// https://www.iana.org/domains/root/db/gdn.html
gdn

// gea : GEA Group Aktiengesellschaft
// https://www.iana.org/domains/root/db/gea.html
gea

// gent : Easyhost BV
// https://www.iana.org/domains/root/db/gent.html
gent

// genting : Resorts World Inc Pte. Ltd.
// https://www.iana.org/domains/root/db/genting.html
genting

// george : Wal-Mart Stores, Inc.
// https://www.iana.org/domains/root/db/george.html
george

// ggee : GMO Internet, Inc.
// https://www.iana.org/domains/root/db/ggee.html
ggee

// gift : DotGift, LLC
// https://www.iana.org/domains/root/db/gift.html
gift

// gifts : Binky Moon, LLC
// https://www.iana.org/domains/root/db/gifts.html
gifts

// gives : Public Interest Registry
// https://www.iana.org/domains/root/db/gives.html
gives

// giving : Public Interest Registry
// https://www.iana.org/domains/root/db/giving.html
giving

// glass : Binky Moon, LLC
// https://www.iana.org/domains/root/db/glass.html
glass

// gle : Charleston Road Registry Inc.
// https://www.iana.org/domains/root/db/gle.html
gle

// global : Identity Digital Limited
// https://www.iana.org/domains/root/db/global.html
global

// globo : Globo ComunicaÃ§Ã£o e ParticipaÃ§Ãµes S.A
// https://www.iana.org/domains/root/db/globo.html
globo

// gmail : Charleston Road Registry Inc.
// https://www.iana.org/domains/root/db/gmail.html
gmail

// gmbh : Binky Moon, LLC
// https://www.iana.org/domains/root/db/gmbh.html
gmbh

// gmo : GMO Internet, Inc.
// https://www.iana.org/domains/root/db/gmo.html
gmo

// gmx : 1&1 Mail & Media GmbH
// https://www.iana.org/domains/root/db/gmx.html
gmx

// godaddy : Go Daddy East, LLC
// https://www.iana.org/domains/root/db/godaddy.html
godaddy

// gold : Binky Moon, LLC
// https://www.iana.org/domains/root/db/gold.html
gold

// goldpoint : YODOBASHI CAMERA CO.,LTD.
// https://www.iana.org/domains/root/db/goldpoint.html
goldpoint

// golf : Binky Moon, LLC
// https://www.iana.org/domains/root/db/golf.html
golf

// goo : NTT Resonant Inc.
// https://www.iana.org/domains/root/db/goo.html
goo

// goodyear : The Goodyear Tire & Rubber Company
// https://www.iana.org/domains/root/db/goodyear.html
goodyear

// goog : Charleston Road Registry Inc.
// https://www.iana.org/domains/root/db/goog.html
goog

// google : Charleston Road Registry Inc.
// https://www.iana.org/domains/root/db/google.html
google

// gop : Republican State Leadership Committee, Inc.
// https://www.iana.org/domains/root/db/gop.html
gop

// got : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/got.html
got

// grainger : Grainger Registry Services, LLC
// https://www.iana.org/domains/root/db/grainger.html
grainger

// graphics : Binky Moon, LLC
// https://www.iana.org/domains/root/db/graphics.html
graphics

// gratis : Binky Moon, LLC
// https://www.iana.org/domains/root/db/gratis.html
gratis

// green : Identity Digital Limited
// https://www.iana.org/domains/root/db/green.html
green

// gripe : Binky Moon, LLC
// https://www.iana.org/domains/root/db/gripe.html
gripe

// grocery : Wal-Mart Stores, Inc.
// https://www.iana.org/domains/root/db/grocery.html
grocery

// group : Binky Moon, LLC
// https://www.iana.org/domains/root/db/group.html
group

// guardian : The Guardian Life Insurance Company of America
// https://www.iana.org/domains/root/db/guardian.html
guardian

// gucci : Guccio Gucci S.p.a.
// https://www.iana.org/domains/root/db/gucci.html
gucci

// guge : Charleston Road Registry Inc.
// https://www.iana.org/domains/root/db/guge.html
guge

// guide : Binky Moon, LLC
// https://www.iana.org/domains/root/db/guide.html
guide

// guitars : XYZ.COM LLC
// https://www.iana.org/domains/root/db/guitars.html
guitars

// guru : Binky Moon, LLC
// https://www.iana.org/domains/root/db/guru.html
guru

// hair : XYZ.COM LLC
// https://www.iana.org/domains/root/db/hair.html
hair

// hamburg : Hamburg Top-Level-Domain GmbH
// https://www.iana.org/domains/root/db/hamburg.html
hamburg

// hangout : Charleston Road Registry Inc.
// https://www.iana.org/domains/root/db/hangout.html
hangout

// haus : Dog Beach, LLC
// https://www.iana.org/domains/root/db/haus.html
haus

// hbo : HBO Registry Services, Inc.
// https://www.iana.org/domains/root/db/hbo.html
hbo

// hdfc : HOUSING DEVELOPMENT FINANCE CORPORATION LIMITED
// https://www.iana.org/domains/root/db/hdfc.html
hdfc

// hdfcbank : HDFC Bank Limited
// https://www.iana.org/domains/root/db/hdfcbank.html
hdfcbank

// health : Registry Services, LLC
// https://www.iana.org/domains/root/db/health.html
health

// healthcare : Binky Moon, LLC
// https://www.iana.org/domains/root/db/healthcare.html
healthcare

// help : Innovation service Limited
// https://www.iana.org/domains/root/db/help.html
help

// helsinki : City of Helsinki
// https://www.iana.org/domains/root/db/helsinki.html
helsinki

// here : Charleston Road Registry Inc.
// https://www.iana.org/domains/root/db/here.html
here

// hermes : HERMES INTERNATIONAL
// https://www.iana.org/domains/root/db/hermes.html
hermes

// hiphop : Dot Hip Hop, LLC
// https://www.iana.org/domains/root/db/hiphop.html
hiphop

// hisamitsu : Hisamitsu Pharmaceutical Co.,Inc.
// https://www.iana.org/domains/root/db/hisamitsu.html
hisamitsu

// hitachi : Hitachi, Ltd.
// https://www.iana.org/domains/root/db/hitachi.html
hitachi

// hiv : Internet Naming Company LLC
// https://www.iana.org/domains/root/db/hiv.html
hiv

// hkt : PCCW-HKT DataCom Services Limited
// https://www.iana.org/domains/root/db/hkt.html
hkt

// hockey : Binky Moon, LLC
// https://www.iana.org/domains/root/db/hockey.html
hockey

// holdings : Binky Moon, LLC
// https://www.iana.org/domains/root/db/holdings.html
holdings

// holiday : Binky Moon, LLC
// https://www.iana.org/domains/root/db/holiday.html
holiday

// homedepot : Home Depot Product Authority, LLC
// https://www.iana.org/domains/root/db/homedepot.html
homedepot

// homegoods : The TJX Companies, Inc.
// https://www.iana.org/domains/root/db/homegoods.html
homegoods

// homes : XYZ.COM LLC
// https://www.iana.org/domains/root/db/homes.html
homes

// homesense : The TJX Companies, Inc.
// https://www.iana.org/domains/root/db/homesense.html
homesense

// honda : Honda Motor Co., Ltd.
// https://www.iana.org/domains/root/db/honda.html
honda

// horse : Registry Services, LLC
// https://www.iana.org/domains/root/db/horse.html
horse

// hospital : Binky Moon, LLC
// https://www.iana.org/domains/root/db/hospital.html
hospital

// host : Radix FZC DMCC
// https://www.iana.org/domains/root/db/host.html
host

// hosting : XYZ.COM LLC
// https://www.iana.org/domains/root/db/hosting.html
hosting

// hot : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/hot.html
hot

// hotels : Booking.com B.V.
// https://www.iana.org/domains/root/db/hotels.html
hotels

// hotmail : Microsoft Corporation
// https://www.iana.org/domains/root/db/hotmail.html
hotmail

// house : Binky Moon, LLC
// https://www.iana.org/domains/root/db/house.html
house

// how : Charleston Road Registry Inc.
// https://www.iana.org/domains/root/db/how.html
how

// hsbc : HSBC Global Services (UK) Limited
// https://www.iana.org/domains/root/db/hsbc.html
hsbc

// hughes : Hughes Satellite Systems Corporation
// https://www.iana.org/domains/root/db/hughes.html
hughes

// hyatt : Hyatt GTLD, L.L.C.
// https://www.iana.org/domains/root/db/hyatt.html
hyatt

// hyundai : Hyundai Motor Company
// https://www.iana.org/domains/root/db/hyundai.html
hyundai

// ibm : International Business Machines Corporation
// https://www.iana.org/domains/root/db/ibm.html
ibm

// icbc : Industrial and Commercial Bank of China Limited
// https://www.iana.org/domains/root/db/icbc.html
icbc

// ice : IntercontinentalExchange, Inc.
// https://www.iana.org/domains/root/db/ice.html
ice

// icu : ShortDot SA
// https://www.iana.org/domains/root/db/icu.html
icu

// ieee : IEEE Global LLC
// https://www.iana.org/domains/root/db/ieee.html
ieee

// ifm : ifm electronic gmbh
// https://www.iana.org/domains/root/db/ifm.html
ifm

// ikano : Ikano S.A.
// https://www.iana.org/domains/root/db/ikano.html
ikano

// imamat : Fondation Aga Khan (Aga Khan Foundation)
// https://www.iana.org/domains/root/db/imamat.html
imamat

// imdb : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/imdb.html
imdb

// immo : Binky Moon, LLC
// https://www.iana.org/domains/root/db/immo.html
immo

// immobilien : Dog Beach, LLC
// https://www.iana.org/domains/root/db/immobilien.html
immobilien

// inc : Intercap Registry Inc.
// https://www.iana.org/domains/root/db/inc.html
inc

// industries : Binky Moon, LLC
// https://www.iana.org/domains/root/db/industries.html
industries

// infiniti : NISSAN MOTOR CO., LTD.
// https://www.iana.org/domains/root/db/infiniti.html
infiniti

// ing : Charleston Road Registry Inc.
// https://www.iana.org/domains/root/db/ing.html
ing

// ink : Registry Services, LLC
// https://www.iana.org/domains/root/db/ink.html
ink

// institute : Binky Moon, LLC
// https://www.iana.org/domains/root/db/institute.html
institute

// insurance : fTLD Registry Services LLC
// https://www.iana.org/domains/root/db/insurance.html
insurance

// insure : Binky Moon, LLC
// https://www.iana.org/domains/root/db/insure.html
insure

// international : Binky Moon, LLC
// https://www.iana.org/domains/root/db/international.html
international

// intuit : Intuit Administrative Services, Inc.
// https://www.iana.org/domains/root/db/intuit.html
intuit

// investments : Binky Moon, LLC
// https://www.iana.org/domains/root/db/investments.html
investments

// ipiranga : Ipiranga Produtos de Petroleo S.A.
// https://www.iana.org/domains/root/db/ipiranga.html
ipiranga

// irish : Binky Moon, LLC
// https://www.iana.org/domains/root/db/irish.html
irish

// ismaili : Fondation Aga Khan (Aga Khan Foundation)
// https://www.iana.org/domains/root/db/ismaili.html
ismaili

// ist : Istanbul Metropolitan Municipality
// https://www.iana.org/domains/root/db/ist.html
ist

// istanbul : Istanbul Metropolitan Municipality
// https://www.iana.org/domains/root/db/istanbul.html
istanbul

// itau : Itau Unibanco Holding S.A.
// https://www.iana.org/domains/root/db/itau.html
itau

// itv : ITV Services Limited
// https://www.iana.org/domains/root/db/itv.html
itv

// jaguar : Jaguar Land Rover Ltd
// https://www.iana.org/domains/root/db/jaguar.html
jaguar

// java : Oracle Corporation
// https://www.iana.org/domains/root/db/java.html
java

// jcb : JCB Co., Ltd.
// https://www.iana.org/domains/root/db/jcb.html
jcb

// jeep : FCA US LLC.
// https://www.iana.org/domains/root/db/jeep.html
jeep

// jetzt : Binky Moon, LLC
// https://www.iana.org/domains/root/db/jetzt.html
jetzt

// jewelry : Binky Moon, LLC
// https://www.iana.org/domains/root/db/jewelry.html
jewelry

// jio : Reliance Industries Limited
// https://www.iana.org/domains/root/db/jio.html
jio

// jll : Jones Lang LaSalle Incorporated
// https://www.iana.org/domains/root/db/jll.html
jll

// jmp : Matrix IP LLC
// https://www.iana.org/domains/root/db/jmp.html
jmp

// jnj : Johnson & Johnson Services, Inc.
// https://www.iana.org/domains/root/db/jnj.html
jnj

// joburg : ZA Central Registry NPC trading as ZA Central Registry
// https://www.iana.org/domains/root/db/joburg.html
joburg

// jot : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/jot.html
jot

// joy : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/joy.html
joy

// jpmorgan : JPMorgan Chase Bank, National Association
// https://www.iana.org/domains/root/db/jpmorgan.html
jpmorgan

// jprs : Japan Registry Services Co., Ltd.
// https://www.iana.org/domains/root/db/jprs.html
jprs

// juegos : Internet Naming Company LLC
// https://www.iana.org/domains/root/db/juegos.html
juegos

// juniper : JUNIPER NETWORKS, INC.
// https://www.iana.org/domains/root/db/juniper.html
juniper

// kaufen : Dog Beach, LLC
// https://www.iana.org/domains/root/db/kaufen.html
kaufen

// kddi : KDDI CORPORATION
// https://www.iana.org/domains/root/db/kddi.html
kddi

// kerryhotels : Kerry Trading Co. Limited
// https://www.iana.org/domains/root/db/kerryhotels.html
kerryhotels

// kerrylogistics : Kerry Trading Co. Limited
// https://www.iana.org/domains/root/db/kerrylogistics.html
kerrylogistics

// kerryproperties : Kerry Trading Co. Limited
// https://www.iana.org/domains/root/db/kerryproperties.html
kerryproperties

// kfh : Kuwait Finance House
// https://www.iana.org/domains/root/db/kfh.html
kfh

// kia : KIA MOTORS CORPORATION
// https://www.iana.org/domains/root/db/kia.html
kia

// kids : DotKids Foundation Limited
// https://www.iana.org/domains/root/db/kids.html
kids

// kim : Identity Digital Limited
// https://www.iana.org/domains/root/db/kim.html
kim

// kinder : Ferrero Trading Lux S.A.
// https://www.iana.org/domains/root/db/kinder.html
kinder

// kindle : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/kindle.html
kindle

// kitchen : Binky Moon, LLC
// https://www.iana.org/domains/root/db/kitchen.html
kitchen

// kiwi : DOT KIWI LIMITED
// https://www.iana.org/domains/root/db/kiwi.html
kiwi

// koeln : dotKoeln GmbH
// https://www.iana.org/domains/root/db/koeln.html
koeln

// komatsu : Komatsu Ltd.
// https://www.iana.org/domains/root/db/komatsu.html
komatsu

// kosher : Kosher Marketing Assets LLC
// https://www.iana.org/domains/root/db/kosher.html
kosher

// kpmg : KPMG International Cooperative (KPMG International Genossenschaft)
// https://www.iana.org/domains/root/db/kpmg.html
kpmg

// kpn : Koninklijke KPN N.V.
// https://www.iana.org/domains/root/db/kpn.html
kpn

// krd : KRG Department of Information Technology
// https://www.iana.org/domains/root/db/krd.html
krd

// kred : KredTLD Pty Ltd
// https://www.iana.org/domains/root/db/kred.html
kred

// kuokgroup : Kerry Trading Co. Limited
// https://www.iana.org/domains/root/db/kuokgroup.html
kuokgroup

// kyoto : Academic Institution: Kyoto Jyoho Gakuen
// https://www.iana.org/domains/root/db/kyoto.html
kyoto

// lacaixa : FundaciÃ³n Bancaria Caixa dâ€™Estalvis i Pensions de Barcelona, â€œla Caixaâ€�
// https://www.iana.org/domains/root/db/lacaixa.html
lacaixa

// lamborghini : Automobili Lamborghini S.p.A.
// https://www.iana.org/domains/root/db/lamborghini.html
lamborghini

// lamer : The EstÃ©e Lauder Companies Inc.
// https://www.iana.org/domains/root/db/lamer.html
lamer

// lancaster : LANCASTER
// https://www.iana.org/domains/root/db/lancaster.html
lancaster

// land : Binky Moon, LLC
// https://www.iana.org/domains/root/db/land.html
land

// landrover : Jaguar Land Rover Ltd
// https://www.iana.org/domains/root/db/landrover.html
landrover

// lanxess : LANXESS Corporation
// https://www.iana.org/domains/root/db/lanxess.html
lanxess

// lasalle : Jones Lang LaSalle Incorporated
// https://www.iana.org/domains/root/db/lasalle.html
lasalle

// lat : XYZ.COM LLC
// https://www.iana.org/domains/root/db/lat.html
lat

// latino : Dish DBS Corporation
// https://www.iana.org/domains/root/db/latino.html
latino

// latrobe : La Trobe University
// https://www.iana.org/domains/root/db/latrobe.html
latrobe

// law : Registry Services, LLC
// https://www.iana.org/domains/root/db/law.html
law

// lawyer : Dog Beach, LLC
// https://www.iana.org/domains/root/db/lawyer.html
lawyer

// lds : IRI Domain Management, LLC
// https://www.iana.org/domains/root/db/lds.html
lds

// lease : Binky Moon, LLC
// https://www.iana.org/domains/root/db/lease.html
lease

// leclerc : A.C.D. LEC Association des Centres Distributeurs Edouard Leclerc
// https://www.iana.org/domains/root/db/leclerc.html
leclerc

// lefrak : LeFrak Organization, Inc.
// https://www.iana.org/domains/root/db/lefrak.html
lefrak

// legal : Binky Moon, LLC
// https://www.iana.org/domains/root/db/legal.html
legal

// lego : LEGO Juris A/S
// https://www.iana.org/domains/root/db/lego.html
lego

// lexus : TOYOTA MOTOR CORPORATION
// https://www.iana.org/domains/root/db/lexus.html
lexus

// lgbt : Identity Digital Limited
// https://www.iana.org/domains/root/db/lgbt.html
lgbt

// lidl : Schwarz Domains und Services GmbH & Co. KG
// https://www.iana.org/domains/root/db/lidl.html
lidl

// life : Binky Moon, LLC
// https://www.iana.org/domains/root/db/life.html
life

// lifeinsurance : American Council of Life Insurers
// https://www.iana.org/domains/root/db/lifeinsurance.html
lifeinsurance

// lifestyle : Lifestyle Domain Holdings, Inc.
// https://www.iana.org/domains/root/db/lifestyle.html
lifestyle

// lighting : Binky Moon, LLC
// https://www.iana.org/domains/root/db/lighting.html
lighting

// like : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/like.html
like

// lilly : Eli Lilly and Company
// https://www.iana.org/domains/root/db/lilly.html
lilly

// limited : Binky Moon, LLC
// https://www.iana.org/domains/root/db/limited.html
limited

// limo : Binky Moon, LLC
// https://www.iana.org/domains/root/db/limo.html
limo

// lincoln : Ford Motor Company
// https://www.iana.org/domains/root/db/lincoln.html
lincoln

// link : Nova Registry Ltd
// https://www.iana.org/domains/root/db/link.html
link

// lipsy : Lipsy Ltd
// https://www.iana.org/domains/root/db/lipsy.html
lipsy

// live : Dog Beach, LLC
// https://www.iana.org/domains/root/db/live.html
live

// living : Lifestyle Domain Holdings, Inc.
// https://www.iana.org/domains/root/db/living.html
living

// llc : Identity Digital Limited
// https://www.iana.org/domains/root/db/llc.html
llc

// llp : Intercap Registry Inc.
// https://www.iana.org/domains/root/db/llp.html
llp

// loan : dot Loan Limited
// https://www.iana.org/domains/root/db/loan.html
loan

// loans : Binky Moon, LLC
// https://www.iana.org/domains/root/db/loans.html
loans

// locker : Orange Domains LLC
// https://www.iana.org/domains/root/db/locker.html
locker

// locus : Locus Analytics LLC
// https://www.iana.org/domains/root/db/locus.html
locus

// lol : XYZ.COM LLC
// https://www.iana.org/domains/root/db/lol.html
lol

// london : Dot London Domains Limited
// https://www.iana.org/domains/root/db/london.html
london

// lotte : Lotte Holdings Co., Ltd.
// https://www.iana.org/domains/root/db/lotte.html
lotte

// lotto : Identity Digital Limited
// https://www.iana.org/domains/root/db/lotto.html
lotto

// love : Merchant Law Group LLP
// https://www.iana.org/domains/root/db/love.html
love

// lpl : LPL Holdings, Inc.
// https://www.iana.org/domains/root/db/lpl.html
lpl

// lplfinancial : LPL Holdings, Inc.
// https://www.iana.org/domains/root/db/lplfinancial.html
lplfinancial

// ltd : Binky Moon, LLC
// https://www.iana.org/domains/root/db/ltd.html
ltd

// ltda : InterNetX, Corp
// https://www.iana.org/domains/root/db/ltda.html
ltda

// lundbeck : H. Lundbeck A/S
// https://www.iana.org/domains/root/db/lundbeck.html
lundbeck

// luxe : Registry Services, LLC
// https://www.iana.org/domains/root/db/luxe.html
luxe

// luxury : Luxury Partners, LLC
// https://www.iana.org/domains/root/db/luxury.html
luxury

// madrid : Comunidad de Madrid
// https://www.iana.org/domains/root/db/madrid.html
madrid

// maif : Mutuelle Assurance Instituteur France (MAIF)
// https://www.iana.org/domains/root/db/maif.html
maif

// maison : Binky Moon, LLC
// https://www.iana.org/domains/root/db/maison.html
maison

// makeup : XYZ.COM LLC
// https://www.iana.org/domains/root/db/makeup.html
makeup

// man : MAN SE
// https://www.iana.org/domains/root/db/man.html
man

// management : Binky Moon, LLC
// https://www.iana.org/domains/root/db/management.html
management

// mango : PUNTO FA S.L.
// https://www.iana.org/domains/root/db/mango.html
mango

// map : Charleston Road Registry Inc.
// https://www.iana.org/domains/root/db/map.html
map

// market : Dog Beach, LLC
// https://www.iana.org/domains/root/db/market.html
market

// marketing : Binky Moon, LLC
// https://www.iana.org/domains/root/db/marketing.html
marketing

// markets : Dog Beach, LLC
// https://www.iana.org/domains/root/db/markets.html
markets

// marriott : Marriott Worldwide Corporation
// https://www.iana.org/domains/root/db/marriott.html
marriott

// marshalls : The TJX Companies, Inc.
// https://www.iana.org/domains/root/db/marshalls.html
marshalls

// mattel : Mattel Sites, Inc.
// https://www.iana.org/domains/root/db/mattel.html
mattel

// mba : Binky Moon, LLC
// https://www.iana.org/domains/root/db/mba.html
mba

// mckinsey : McKinsey Holdings, Inc.
// https://www.iana.org/domains/root/db/mckinsey.html
mckinsey

// med : Medistry LLC
// https://www.iana.org/domains/root/db/med.html
med

// media : Binky Moon, LLC
// https://www.iana.org/domains/root/db/media.html
media

// meet : Charleston Road Registry Inc.
// https://www.iana.org/domains/root/db/meet.html
meet

// melbourne : The Crown in right of the State of Victoria, represented by its Department of State Development, Business and Innovation
// https://www.iana.org/domains/root/db/melbourne.html
melbourne

// meme : Charleston Road Registry Inc.
// https://www.iana.org/domains/root/db/meme.html
meme

// memorial : Dog Beach, LLC
// https://www.iana.org/domains/root/db/memorial.html
memorial

// men : Exclusive Registry Limited
// https://www.iana.org/domains/root/db/men.html
men

// menu : Dot Menu Registry, LLC
// https://www.iana.org/domains/root/db/menu.html
menu

// merckmsd : MSD Registry Holdings, Inc.
// https://www.iana.org/domains/root/db/merckmsd.html
merckmsd

// miami : Registry Services, LLC
// https://www.iana.org/domains/root/db/miami.html
miami

// microsoft : Microsoft Corporation
// https://www.iana.org/domains/root/db/microsoft.html
microsoft

// mini : Bayerische Motoren Werke Aktiengesellschaft
// https://www.iana.org/domains/root/db/mini.html
mini

// mint : Intuit Administrative Services, Inc.
// https://www.iana.org/domains/root/db/mint.html
mint

// mit : Massachusetts Institute of Technology
// https://www.iana.org/domains/root/db/mit.html
mit

// mitsubishi : Mitsubishi Corporation
// https://www.iana.org/domains/root/db/mitsubishi.html
mitsubishi

// mlb : MLB Advanced Media DH, LLC
// https://www.iana.org/domains/root/db/mlb.html
mlb

// mls : The Canadian Real Estate Association
// https://www.iana.org/domains/root/db/mls.html
mls

// mma : MMA IARD
// https://www.iana.org/domains/root/db/mma.html
mma

// mobile : Dish DBS Corporation
// https://www.iana.org/domains/root/db/mobile.html
mobile

// moda : Dog Beach, LLC
// https://www.iana.org/domains/root/db/moda.html
moda

// moe : Interlink Systems Innovation Institute K.K.
// https://www.iana.org/domains/root/db/moe.html
moe

// moi : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/moi.html
moi

// mom : XYZ.COM LLC
// https://www.iana.org/domains/root/db/mom.html
mom

// monash : Monash University
// https://www.iana.org/domains/root/db/monash.html
monash

// money : Binky Moon, LLC
// https://www.iana.org/domains/root/db/money.html
money

// monster : XYZ.COM LLC
// https://www.iana.org/domains/root/db/monster.html
monster

// mormon : IRI Domain Management, LLC
// https://www.iana.org/domains/root/db/mormon.html
mormon

// mortgage : Dog Beach, LLC
// https://www.iana.org/domains/root/db/mortgage.html
mortgage

// moscow : Foundation for Assistance for Internet Technologies and Infrastructure Development (FAITID)
// https://www.iana.org/domains/root/db/moscow.html
moscow

// moto : Motorola Trademark Holdings, LLC
// https://www.iana.org/domains/root/db/moto.html
moto

// motorcycles : XYZ.COM LLC
// https://www.iana.org/domains/root/db/motorcycles.html
motorcycles

// mov : Charleston Road Registry Inc.
// https://www.iana.org/domains/root/db/mov.html
mov

// movie : Binky Moon, LLC
// https://www.iana.org/domains/root/db/movie.html
movie

// msd : MSD Registry Holdings, Inc.
// https://www.iana.org/domains/root/db/msd.html
msd

// mtn : MTN Dubai Limited
// https://www.iana.org/domains/root/db/mtn.html
mtn

// mtr : MTR Corporation Limited
// https://www.iana.org/domains/root/db/mtr.html
mtr

// music : DotMusic Limited
// https://www.iana.org/domains/root/db/music.html
music

// nab : National Australia Bank Limited
// https://www.iana.org/domains/root/db/nab.html
nab

// nagoya : GMO Registry, Inc.
// https://www.iana.org/domains/root/db/nagoya.html
nagoya

// natura : NATURA COSMÃ‰TICOS S.A.
// https://www.iana.org/domains/root/db/natura.html
natura

// navy : Dog Beach, LLC
// https://www.iana.org/domains/root/db/navy.html
navy

// nba : NBA REGISTRY, LLC
// https://www.iana.org/domains/root/db/nba.html
nba

// nec : NEC Corporation
// https://www.iana.org/domains/root/db/nec.html
nec

// netbank : COMMONWEALTH BANK OF AUSTRALIA
// https://www.iana.org/domains/root/db/netbank.html
netbank

// netflix : Netflix, Inc.
// https://www.iana.org/domains/root/db/netflix.html
netflix

// network : Binky Moon, LLC
// https://www.iana.org/domains/root/db/network.html
network

// neustar : NeuStar, Inc.
// https://www.iana.org/domains/root/db/neustar.html
neustar

// new : Charleston Road Registry Inc.
// https://www.iana.org/domains/root/db/new.html
new

// news : Dog Beach, LLC
// https://www.iana.org/domains/root/db/news.html
news

// next : Next plc
// https://www.iana.org/domains/root/db/next.html
next

// nextdirect : Next plc
// https://www.iana.org/domains/root/db/nextdirect.html
nextdirect

// nexus : Charleston Road Registry Inc.
// https://www.iana.org/domains/root/db/nexus.html
nexus

// nfl : NFL Reg Ops LLC
// https://www.iana.org/domains/root/db/nfl.html
nfl

// ngo : Public Interest Registry
// https://www.iana.org/domains/root/db/ngo.html
ngo

// nhk : Japan Broadcasting Corporation (NHK)
// https://www.iana.org/domains/root/db/nhk.html
nhk

// nico : DWANGO Co., Ltd.
// https://www.iana.org/domains/root/db/nico.html
nico

// nike : NIKE, Inc.
// https://www.iana.org/domains/root/db/nike.html
nike

// nikon : NIKON CORPORATION
// https://www.iana.org/domains/root/db/nikon.html
nikon

// ninja : Dog Beach, LLC
// https://www.iana.org/domains/root/db/ninja.html
ninja

// nissan : NISSAN MOTOR CO., LTD.
// https://www.iana.org/domains/root/db/nissan.html
nissan

// nissay : Nippon Life Insurance Company
// https://www.iana.org/domains/root/db/nissay.html
nissay

// nokia : Nokia Corporation
// https://www.iana.org/domains/root/db/nokia.html
nokia

// norton : NortonLifeLock Inc.
// https://www.iana.org/domains/root/db/norton.html
norton

// now : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/now.html
now

// nowruz : Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti.
// https://www.iana.org/domains/root/db/nowruz.html
nowruz

// nowtv : Starbucks (HK) Limited
// https://www.iana.org/domains/root/db/nowtv.html
nowtv

// nra : NRA Holdings Company, INC.
// https://www.iana.org/domains/root/db/nra.html
nra

// nrw : Minds + Machines GmbH
// https://www.iana.org/domains/root/db/nrw.html
nrw

// ntt : NIPPON TELEGRAPH AND TELEPHONE CORPORATION
// https://www.iana.org/domains/root/db/ntt.html
ntt

// nyc : The City of New York by and through the New York City Department of Information Technology & Telecommunications
// https://www.iana.org/domains/root/db/nyc.html
nyc

// obi : OBI Group Holding SE & Co. KGaA
// https://www.iana.org/domains/root/db/obi.html
obi

// observer : Fegistry, LLC
// https://www.iana.org/domains/root/db/observer.html
observer

// office : Microsoft Corporation
// https://www.iana.org/domains/root/db/office.html
office

// okinawa : BRregistry, Inc.
// https://www.iana.org/domains/root/db/okinawa.html
okinawa

// olayan : Competrol (Luxembourg) Sarl
// https://www.iana.org/domains/root/db/olayan.html
olayan

// olayangroup : Competrol (Luxembourg) Sarl
// https://www.iana.org/domains/root/db/olayangroup.html
olayangroup

// oldnavy : The Gap, Inc.
// https://www.iana.org/domains/root/db/oldnavy.html
oldnavy

// ollo : Dish DBS Corporation
// https://www.iana.org/domains/root/db/ollo.html
ollo

// omega : The Swatch Group Ltd
// https://www.iana.org/domains/root/db/omega.html
omega

// one : One.com A/S
// https://www.iana.org/domains/root/db/one.html
one

// ong : Public Interest Registry
// https://www.iana.org/domains/root/db/ong.html
ong

// onl : iRegistry GmbH
// https://www.iana.org/domains/root/db/onl.html
onl

// online : Radix FZC DMCC
// https://www.iana.org/domains/root/db/online.html
online

// ooo : INFIBEAM AVENUES LIMITED
// https://www.iana.org/domains/root/db/ooo.html
ooo

// open : American Express Travel Related Services Company, Inc.
// https://www.iana.org/domains/root/db/open.html
open

// oracle : Oracle Corporation
// https://www.iana.org/domains/root/db/oracle.html
oracle

// orange : Orange Brand Services Limited
// https://www.iana.org/domains/root/db/orange.html
orange

// organic : Identity Digital Limited
// https://www.iana.org/domains/root/db/organic.html
organic

// origins : The EstÃ©e Lauder Companies Inc.
// https://www.iana.org/domains/root/db/origins.html
origins

// osaka : Osaka Registry Co., Ltd.
// https://www.iana.org/domains/root/db/osaka.html
osaka

// otsuka : Otsuka Holdings Co., Ltd.
// https://www.iana.org/domains/root/db/otsuka.html
otsuka

// ott : Dish DBS Corporation
// https://www.iana.org/domains/root/db/ott.html
ott

// ovh : MÃ©diaBC
// https://www.iana.org/domains/root/db/ovh.html
ovh

// page : Charleston Road Registry Inc.
// https://www.iana.org/domains/root/db/page.html
page

// panasonic : Panasonic Holdings Corporation
// https://www.iana.org/domains/root/db/panasonic.html
panasonic

// paris : City of Paris
// https://www.iana.org/domains/root/db/paris.html
paris

// pars : Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti.
// https://www.iana.org/domains/root/db/pars.html
pars

// partners : Binky Moon, LLC
// https://www.iana.org/domains/root/db/partners.html
partners

// parts : Binky Moon, LLC
// https://www.iana.org/domains/root/db/parts.html
parts

// party : Blue Sky Registry Limited
// https://www.iana.org/domains/root/db/party.html
party

// pay : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/pay.html
pay

// pccw : PCCW Enterprises Limited
// https://www.iana.org/domains/root/db/pccw.html
pccw

// pet : Identity Digital Limited
// https://www.iana.org/domains/root/db/pet.html
pet

// pfizer : Pfizer Inc.
// https://www.iana.org/domains/root/db/pfizer.html
pfizer

// pharmacy : National Association of Boards of Pharmacy
// https://www.iana.org/domains/root/db/pharmacy.html
pharmacy

// phd : Charleston Road Registry Inc.
// https://www.iana.org/domains/root/db/phd.html
phd

// philips : Koninklijke Philips N.V.
// https://www.iana.org/domains/root/db/philips.html
philips

// phone : Dish DBS Corporation
// https://www.iana.org/domains/root/db/phone.html
phone

// photo : Registry Services, LLC
// https://www.iana.org/domains/root/db/photo.html
photo

// photography : Binky Moon, LLC
// https://www.iana.org/domains/root/db/photography.html
photography

// photos : Binky Moon, LLC
// https://www.iana.org/domains/root/db/photos.html
photos

// physio : PhysBiz Pty Ltd
// https://www.iana.org/domains/root/db/physio.html
physio

// pics : XYZ.COM LLC
// https://www.iana.org/domains/root/db/pics.html
pics

// pictet : Pictet Europe S.A.
// https://www.iana.org/domains/root/db/pictet.html
pictet

// pictures : Binky Moon, LLC
// https://www.iana.org/domains/root/db/pictures.html
pictures

// pid : Top Level Spectrum, Inc.
// https://www.iana.org/domains/root/db/pid.html
pid

// pin : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/pin.html
pin

// ping : Ping Registry Provider, Inc.
// https://www.iana.org/domains/root/db/ping.html
ping

// pink : Identity Digital Limited
// https://www.iana.org/domains/root/db/pink.html
pink

// pioneer : Pioneer Corporation
// https://www.iana.org/domains/root/db/pioneer.html
pioneer

// pizza : Binky Moon, LLC
// https://www.iana.org/domains/root/db/pizza.html
pizza

// place : Binky Moon, LLC
// https://www.iana.org/domains/root/db/place.html
place

// play : Charleston Road Registry Inc.
// https://www.iana.org/domains/root/db/play.html
play

// playstation : Sony Interactive Entertainment Inc.
// https://www.iana.org/domains/root/db/playstation.html
playstation

// plumbing : Binky Moon, LLC
// https://www.iana.org/domains/root/db/plumbing.html
plumbing

// plus : Binky Moon, LLC
// https://www.iana.org/domains/root/db/plus.html
plus

// pnc : PNC Domain Co., LLC
// https://www.iana.org/domains/root/db/pnc.html
pnc

// pohl : Deutsche VermÃ¶gensberatung Aktiengesellschaft DVAG
// https://www.iana.org/domains/root/db/pohl.html
pohl

// poker : Identity Digital Limited
// https://www.iana.org/domains/root/db/poker.html
poker

// politie : Politie Nederland
// https://www.iana.org/domains/root/db/politie.html
politie

// porn : ICM Registry PN LLC
// https://www.iana.org/domains/root/db/porn.html
porn

// pramerica : Prudential Financial, Inc.
// https://www.iana.org/domains/root/db/pramerica.html
pramerica

// praxi : Praxi S.p.A.
// https://www.iana.org/domains/root/db/praxi.html
praxi

// press : Radix FZC DMCC
// https://www.iana.org/domains/root/db/press.html
press

// prime : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/prime.html
prime

// prod : Charleston Road Registry Inc.
// https://www.iana.org/domains/root/db/prod.html
prod

// productions : Binky Moon, LLC
// https://www.iana.org/domains/root/db/productions.html
productions

// prof : Charleston Road Registry Inc.
// https://www.iana.org/domains/root/db/prof.html
prof

// progressive : Progressive Casualty Insurance Company
// https://www.iana.org/domains/root/db/progressive.html
progressive

// promo : Identity Digital Limited
// https://www.iana.org/domains/root/db/promo.html
promo

// properties : Binky Moon, LLC
// https://www.iana.org/domains/root/db/properties.html
properties

// property : Internet Naming Company LLC
// https://www.iana.org/domains/root/db/property.html
property

// protection : XYZ.COM LLC
// https://www.iana.org/domains/root/db/protection.html
protection

// pru : Prudential Financial, Inc.
// https://www.iana.org/domains/root/db/pru.html
pru

// prudential : Prudential Financial, Inc.
// https://www.iana.org/domains/root/db/prudential.html
prudential

// pub : Dog Beach, LLC
// https://www.iana.org/domains/root/db/pub.html
pub

// pwc : PricewaterhouseCoopers LLP
// https://www.iana.org/domains/root/db/pwc.html
pwc

// qpon : dotQPON LLC
// https://www.iana.org/domains/root/db/qpon.html
qpon

// quebec : PointQuÃ©bec Inc
// https://www.iana.org/domains/root/db/quebec.html
quebec

// quest : XYZ.COM LLC
// https://www.iana.org/domains/root/db/quest.html
quest

// racing : Premier Registry Limited
// https://www.iana.org/domains/root/db/racing.html
racing

// radio : European Broadcasting Union (EBU)
// https://www.iana.org/domains/root/db/radio.html
radio

// read : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/read.html
read

// realestate : dotRealEstate LLC
// https://www.iana.org/domains/root/db/realestate.html
realestate

// realtor : Real Estate Domains LLC
// https://www.iana.org/domains/root/db/realtor.html
realtor

// realty : Internet Naming Company LLC
// https://www.iana.org/domains/root/db/realty.html
realty

// recipes : Binky Moon, LLC
// https://www.iana.org/domains/root/db/recipes.html
recipes

// red : Identity Digital Limited
// https://www.iana.org/domains/root/db/red.html
red

// redstone : Redstone Haute Couture Co., Ltd.
// https://www.iana.org/domains/root/db/redstone.html
redstone

// redumbrella : Travelers TLD, LLC
// https://www.iana.org/domains/root/db/redumbrella.html
redumbrella

// rehab : Dog Beach, LLC
// https://www.iana.org/domains/root/db/rehab.html
rehab

// reise : Binky Moon, LLC
// https://www.iana.org/domains/root/db/reise.html
reise

// reisen : Binky Moon, LLC
// https://www.iana.org/domains/root/db/reisen.html
reisen

// reit : National Association of Real Estate Investment Trusts, Inc.
// https://www.iana.org/domains/root/db/reit.html
reit

// reliance : Reliance Industries Limited
// https://www.iana.org/domains/root/db/reliance.html
reliance

// ren : ZDNS International Limited
// https://www.iana.org/domains/root/db/ren.html
ren

// rent : XYZ.COM LLC
// https://www.iana.org/domains/root/db/rent.html
rent

// rentals : Binky Moon, LLC
// https://www.iana.org/domains/root/db/rentals.html
rentals

// repair : Binky Moon, LLC
// https://www.iana.org/domains/root/db/repair.html
repair

// report : Binky Moon, LLC
// https://www.iana.org/domains/root/db/report.html
report

// republican : Dog Beach, LLC
// https://www.iana.org/domains/root/db/republican.html
republican

// rest : Punto 2012 Sociedad Anonima Promotora de Inversion de Capital Variable
// https://www.iana.org/domains/root/db/rest.html
rest

// restaurant : Binky Moon, LLC
// https://www.iana.org/domains/root/db/restaurant.html
restaurant

// review : dot Review Limited
// https://www.iana.org/domains/root/db/review.html
review

// reviews : Dog Beach, LLC
// https://www.iana.org/domains/root/db/reviews.html
reviews

// rexroth : Robert Bosch GMBH
// https://www.iana.org/domains/root/db/rexroth.html
rexroth

// rich : iRegistry GmbH
// https://www.iana.org/domains/root/db/rich.html
rich

// richardli : Pacific Century Asset Management (HK) Limited
// https://www.iana.org/domains/root/db/richardli.html
richardli

// ricoh : Ricoh Company, Ltd.
// https://www.iana.org/domains/root/db/ricoh.html
ricoh

// ril : Reliance Industries Limited
// https://www.iana.org/domains/root/db/ril.html
ril

// rio : Empresa Municipal de InformÃ¡tica SA - IPLANRIO
// https://www.iana.org/domains/root/db/rio.html
rio

// rip : Dog Beach, LLC
// https://www.iana.org/domains/root/db/rip.html
rip

// rocher : Ferrero Trading Lux S.A.
// https://www.iana.org/domains/root/db/rocher.html
rocher

// rocks : Dog Beach, LLC
// https://www.iana.org/domains/root/db/rocks.html
rocks

// rodeo : Registry Services, LLC
// https://www.iana.org/domains/root/db/rodeo.html
rodeo

// rogers : Rogers Communications Canada Inc.
// https://www.iana.org/domains/root/db/rogers.html
rogers

// room : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/room.html
room

// rsvp : Charleston Road Registry Inc.
// https://www.iana.org/domains/root/db/rsvp.html
rsvp

// rugby : World Rugby Strategic Developments Limited
// https://www.iana.org/domains/root/db/rugby.html
rugby

// ruhr : dotSaarland GmbH
// https://www.iana.org/domains/root/db/ruhr.html
ruhr

// run : Binky Moon, LLC
// https://www.iana.org/domains/root/db/run.html
run

// rwe : RWE AG
// https://www.iana.org/domains/root/db/rwe.html
rwe

// ryukyu : BRregistry, Inc.
// https://www.iana.org/domains/root/db/ryukyu.html
ryukyu

// saarland : dotSaarland GmbH
// https://www.iana.org/domains/root/db/saarland.html
saarland

// safe : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/safe.html
safe

// safety : Safety Registry Services, LLC.
// https://www.iana.org/domains/root/db/safety.html
safety

// sakura : SAKURA Internet Inc.
// https://www.iana.org/domains/root/db/sakura.html
sakura

// sale : Dog Beach, LLC
// https://www.iana.org/domains/root/db/sale.html
sale

// salon : Binky Moon, LLC
// https://www.iana.org/domains/root/db/salon.html
salon

// samsclub : Wal-Mart Stores, Inc.
// https://www.iana.org/domains/root/db/samsclub.html
samsclub

// samsung : SAMSUNG SDS CO., LTD
// https://www.iana.org/domains/root/db/samsung.html
samsung

// sandvik : Sandvik AB
// https://www.iana.org/domains/root/db/sandvik.html
sandvik

// sandvikcoromant : Sandvik AB
// https://www.iana.org/domains/root/db/sandvikcoromant.html
sandvikcoromant

// sanofi : Sanofi
// https://www.iana.org/domains/root/db/sanofi.html
sanofi

// sap : SAP AG
// https://www.iana.org/domains/root/db/sap.html
sap

// sarl : Binky Moon, LLC
// https://www.iana.org/domains/root/db/sarl.html
sarl

// sas : Research IP LLC
// https://www.iana.org/domains/root/db/sas.html
sas

// save : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/save.html
save

// saxo : Saxo Bank A/S
// https://www.iana.org/domains/root/db/saxo.html
saxo

// sbi : STATE BANK OF INDIA
// https://www.iana.org/domains/root/db/sbi.html
sbi

// sbs : ShortDot SA
// https://www.iana.org/domains/root/db/sbs.html
sbs

// sca : SVENSKA CELLULOSA AKTIEBOLAGET SCA (publ)
// https://www.iana.org/domains/root/db/sca.html
sca

// scb : The Siam Commercial Bank Public Company Limited ("SCB")
// https://www.iana.org/domains/root/db/scb.html
scb

// schaeffler : Schaeffler Technologies AG & Co. KG
// https://www.iana.org/domains/root/db/schaeffler.html
schaeffler

// schmidt : SCHMIDT GROUPE S.A.S.
// https://www.iana.org/domains/root/db/schmidt.html
schmidt

// scholarships : Scholarships.com, LLC
// https://www.iana.org/domains/root/db/scholarships.html
scholarships

// school : Binky Moon, LLC
// https://www.iana.org/domains/root/db/school.html
school

// schule : Binky Moon, LLC
// https://www.iana.org/domains/root/db/schule.html
schule

// schwarz : Schwarz Domains und Services GmbH & Co. KG
// https://www.iana.org/domains/root/db/schwarz.html
schwarz

// science : dot Science Limited
// https://www.iana.org/domains/root/db/science.html
science

// scot : Dot Scot Registry Limited
// https://www.iana.org/domains/root/db/scot.html
scot

// search : Charleston Road Registry Inc.
// https://www.iana.org/domains/root/db/search.html
search

// seat : SEAT, S.A. (Sociedad Unipersonal)
// https://www.iana.org/domains/root/db/seat.html
seat

// secure : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/secure.html
secure

// security : XYZ.COM LLC
// https://www.iana.org/domains/root/db/security.html
security

// seek : Seek Limited
// https://www.iana.org/domains/root/db/seek.html
seek

// select : Registry Services, LLC
// https://www.iana.org/domains/root/db/select.html
select

// sener : Sener IngenierÃ­a y Sistemas, S.A.
// https://www.iana.org/domains/root/db/sener.html
sener

// services : Binky Moon, LLC
// https://www.iana.org/domains/root/db/services.html
services

// seven : Seven West Media Ltd
// https://www.iana.org/domains/root/db/seven.html
seven

// sew : SEW-EURODRIVE GmbH & Co KG
// https://www.iana.org/domains/root/db/sew.html
sew

// sex : ICM Registry SX LLC
// https://www.iana.org/domains/root/db/sex.html
sex

// sexy : Internet Naming Company LLC
// https://www.iana.org/domains/root/db/sexy.html
sexy

// sfr : Societe Francaise du Radiotelephone - SFR
// https://www.iana.org/domains/root/db/sfr.html
sfr

// shangrila : Shangriâ€�La International Hotel Management Limited
// https://www.iana.org/domains/root/db/shangrila.html
shangrila

// sharp : Sharp Corporation
// https://www.iana.org/domains/root/db/sharp.html
sharp

// shaw : Shaw Cablesystems G.P.
// https://www.iana.org/domains/root/db/shaw.html
shaw

// shell : Shell Information Technology International Inc
// https://www.iana.org/domains/root/db/shell.html
shell

// shia : Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti.
// https://www.iana.org/domains/root/db/shia.html
shia

// shiksha : Identity Digital Limited
// https://www.iana.org/domains/root/db/shiksha.html
shiksha

// shoes : Binky Moon, LLC
// https://www.iana.org/domains/root/db/shoes.html
shoes

// shop : GMO Registry, Inc.
// https://www.iana.org/domains/root/db/shop.html
shop

// shopping : Binky Moon, LLC
// https://www.iana.org/domains/root/db/shopping.html
shopping

// shouji : Beijing Qihu Keji Co., Ltd.
// https://www.iana.org/domains/root/db/shouji.html
shouji

// show : Binky Moon, LLC
// https://www.iana.org/domains/root/db/show.html
show

// showtime : CBS Domains Inc.
// https://www.iana.org/domains/root/db/showtime.html
showtime

// silk : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/silk.html
silk

// sina : Sina Corporation
// https://www.iana.org/domains/root/db/sina.html
sina

// singles : Binky Moon, LLC
// https://www.iana.org/domains/root/db/singles.html
singles

// site : Radix FZC DMCC
// https://www.iana.org/domains/root/db/site.html
site

// ski : Identity Digital Limited
// https://www.iana.org/domains/root/db/ski.html
ski

// skin : XYZ.COM LLC
// https://www.iana.org/domains/root/db/skin.html
skin

// sky : Sky International AG
// https://www.iana.org/domains/root/db/sky.html
sky

// skype : Microsoft Corporation
// https://www.iana.org/domains/root/db/skype.html
skype

// sling : DISH Technologies L.L.C.
// https://www.iana.org/domains/root/db/sling.html
sling

// smart : Smart Communications, Inc. (SMART)
// https://www.iana.org/domains/root/db/smart.html
smart

// smile : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/smile.html
smile

// sncf : SociÃ©tÃ© Nationale SNCF
// https://www.iana.org/domains/root/db/sncf.html
sncf

// soccer : Binky Moon, LLC
// https://www.iana.org/domains/root/db/soccer.html
soccer

// social : Dog Beach, LLC
// https://www.iana.org/domains/root/db/social.html
social

// softbank : SoftBank Group Corp.
// https://www.iana.org/domains/root/db/softbank.html
softbank

// software : Dog Beach, LLC
// https://www.iana.org/domains/root/db/software.html
software

// sohu : Sohu.com Limited
// https://www.iana.org/domains/root/db/sohu.html
sohu

// solar : Binky Moon, LLC
// https://www.iana.org/domains/root/db/solar.html
solar

// solutions : Binky Moon, LLC
// https://www.iana.org/domains/root/db/solutions.html
solutions

// song : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/song.html
song

// sony : Sony Corporation
// https://www.iana.org/domains/root/db/sony.html
sony

// soy : Charleston Road Registry Inc.
// https://www.iana.org/domains/root/db/soy.html
soy

// spa : Asia Spa and Wellness Promotion Council Limited
// https://www.iana.org/domains/root/db/spa.html
spa

// space : Radix FZC DMCC
// https://www.iana.org/domains/root/db/space.html
space

// sport : SportAccord
// https://www.iana.org/domains/root/db/sport.html
sport

// spot : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/spot.html
spot

// srl : InterNetX, Corp
// https://www.iana.org/domains/root/db/srl.html
srl

// stada : STADA Arzneimittel AG
// https://www.iana.org/domains/root/db/stada.html
stada

// staples : Staples, Inc.
// https://www.iana.org/domains/root/db/staples.html
staples

// star : Star India Private Limited
// https://www.iana.org/domains/root/db/star.html
star

// statebank : STATE BANK OF INDIA
// https://www.iana.org/domains/root/db/statebank.html
statebank

// statefarm : State Farm Mutual Automobile Insurance Company
// https://www.iana.org/domains/root/db/statefarm.html
statefarm

// stc : Saudi Telecom Company
// https://www.iana.org/domains/root/db/stc.html
stc

// stcgroup : Saudi Telecom Company
// https://www.iana.org/domains/root/db/stcgroup.html
stcgroup

// stockholm : Stockholms kommun
// https://www.iana.org/domains/root/db/stockholm.html
stockholm

// storage : XYZ.COM LLC
// https://www.iana.org/domains/root/db/storage.html
storage

// store : Radix FZC DMCC
// https://www.iana.org/domains/root/db/store.html
store

// stream : dot Stream Limited
// https://www.iana.org/domains/root/db/stream.html
stream

// studio : Dog Beach, LLC
// https://www.iana.org/domains/root/db/studio.html
studio

// study : Registry Services, LLC
// https://www.iana.org/domains/root/db/study.html
study

// style : Binky Moon, LLC
// https://www.iana.org/domains/root/db/style.html
style

// sucks : Vox Populi Registry Ltd.
// https://www.iana.org/domains/root/db/sucks.html
sucks

// supplies : Binky Moon, LLC
// https://www.iana.org/domains/root/db/supplies.html
supplies

// supply : Binky Moon, LLC
// https://www.iana.org/domains/root/db/supply.html
supply

// support : Binky Moon, LLC
// https://www.iana.org/domains/root/db/support.html
support

// surf : Registry Services, LLC
// https://www.iana.org/domains/root/db/surf.html
surf

// surgery : Binky Moon, LLC
// https://www.iana.org/domains/root/db/surgery.html
surgery

// suzuki : SUZUKI MOTOR CORPORATION
// https://www.iana.org/domains/root/db/suzuki.html
suzuki

// swatch : The Swatch Group Ltd
// https://www.iana.org/domains/root/db/swatch.html
swatch

// swiss : Swiss Confederation
// https://www.iana.org/domains/root/db/swiss.html
swiss

// sydney : State of New South Wales, Department of Premier and Cabinet
// https://www.iana.org/domains/root/db/sydney.html
sydney

// systems : Binky Moon, LLC
// https://www.iana.org/domains/root/db/systems.html
systems

// tab : Tabcorp Holdings Limited
// https://www.iana.org/domains/root/db/tab.html
tab

// taipei : Taipei City Government
// https://www.iana.org/domains/root/db/taipei.html
taipei

// talk : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/talk.html
talk

// taobao : Alibaba Group Holding Limited
// https://www.iana.org/domains/root/db/taobao.html
taobao

// target : Target Domain Holdings, LLC
// https://www.iana.org/domains/root/db/target.html
target

// tatamotors : Tata Motors Ltd
// https://www.iana.org/domains/root/db/tatamotors.html
tatamotors

// tatar : Limited Liability Company "Coordination Center of Regional Domain of Tatarstan Republic"
// https://www.iana.org/domains/root/db/tatar.html
tatar

// tattoo : Registry Services, LLC
// https://www.iana.org/domains/root/db/tattoo.html
tattoo

// tax : Binky Moon, LLC
// https://www.iana.org/domains/root/db/tax.html
tax

// taxi : Binky Moon, LLC
// https://www.iana.org/domains/root/db/taxi.html
taxi

// tci : Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti.
// https://www.iana.org/domains/root/db/tci.html
tci

// tdk : TDK Corporation
// https://www.iana.org/domains/root/db/tdk.html
tdk

// team : Binky Moon, LLC
// https://www.iana.org/domains/root/db/team.html
team

// tech : Radix FZC DMCC
// https://www.iana.org/domains/root/db/tech.html
tech

// technology : Binky Moon, LLC
// https://www.iana.org/domains/root/db/technology.html
technology

// temasek : Temasek Holdings (Private) Limited
// https://www.iana.org/domains/root/db/temasek.html
temasek

// tennis : Binky Moon, LLC
// https://www.iana.org/domains/root/db/tennis.html
tennis

// teva : Teva Pharmaceutical Industries Limited
// https://www.iana.org/domains/root/db/teva.html
teva

// thd : Home Depot Product Authority, LLC
// https://www.iana.org/domains/root/db/thd.html
thd

// theater : Binky Moon, LLC
// https://www.iana.org/domains/root/db/theater.html
theater

// theatre : XYZ.COM LLC
// https://www.iana.org/domains/root/db/theatre.html
theatre

// tiaa : Teachers Insurance and Annuity Association of America
// https://www.iana.org/domains/root/db/tiaa.html
tiaa

// tickets : XYZ.COM LLC
// https://www.iana.org/domains/root/db/tickets.html
tickets

// tienda : Binky Moon, LLC
// https://www.iana.org/domains/root/db/tienda.html
tienda

// tips : Binky Moon, LLC
// https://www.iana.org/domains/root/db/tips.html
tips

// tires : Binky Moon, LLC
// https://www.iana.org/domains/root/db/tires.html
tires

// tirol : punkt Tirol GmbH
// https://www.iana.org/domains/root/db/tirol.html
tirol

// tjmaxx : The TJX Companies, Inc.
// https://www.iana.org/domains/root/db/tjmaxx.html
tjmaxx

// tjx : The TJX Companies, Inc.
// https://www.iana.org/domains/root/db/tjx.html
tjx

// tkmaxx : The TJX Companies, Inc.
// https://www.iana.org/domains/root/db/tkmaxx.html
tkmaxx

// tmall : Alibaba Group Holding Limited
// https://www.iana.org/domains/root/db/tmall.html
tmall

// today : Binky Moon, LLC
// https://www.iana.org/domains/root/db/today.html
today

// tokyo : GMO Registry, Inc.
// https://www.iana.org/domains/root/db/tokyo.html
tokyo

// tools : Binky Moon, LLC
// https://www.iana.org/domains/root/db/tools.html
tools

// top : .TOP Registry
// https://www.iana.org/domains/root/db/top.html
top

// toray : Toray Industries, Inc.
// https://www.iana.org/domains/root/db/toray.html
toray

// toshiba : TOSHIBA Corporation
// https://www.iana.org/domains/root/db/toshiba.html
toshiba

// total : TotalEnergies SE
// https://www.iana.org/domains/root/db/total.html
total

// tours : Binky Moon, LLC
// https://www.iana.org/domains/root/db/tours.html
tours

// town : Binky Moon, LLC
// https://www.iana.org/domains/root/db/town.html
town

// toyota : TOYOTA MOTOR CORPORATION
// https://www.iana.org/domains/root/db/toyota.html
toyota

// toys : Binky Moon, LLC
// https://www.iana.org/domains/root/db/toys.html
toys

// trade : Elite Registry Limited
// https://www.iana.org/domains/root/db/trade.html
trade

// trading : Dog Beach, LLC
// https://www.iana.org/domains/root/db/trading.html
trading

// training : Binky Moon, LLC
// https://www.iana.org/domains/root/db/training.html
training

// travel : Dog Beach, LLC
// https://www.iana.org/domains/root/db/travel.html
travel

// travelers : Travelers TLD, LLC
// https://www.iana.org/domains/root/db/travelers.html
travelers

// travelersinsurance : Travelers TLD, LLC
// https://www.iana.org/domains/root/db/travelersinsurance.html
travelersinsurance

// trust : Internet Naming Company LLC
// https://www.iana.org/domains/root/db/trust.html
trust

// trv : Travelers TLD, LLC
// https://www.iana.org/domains/root/db/trv.html
trv

// tube : Latin American Telecom LLC
// https://www.iana.org/domains/root/db/tube.html
tube

// tui : TUI AG
// https://www.iana.org/domains/root/db/tui.html
tui

// tunes : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/tunes.html
tunes

// tushu : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/tushu.html
tushu

// tvs : T V SUNDRAM IYENGAR  & SONS LIMITED
// https://www.iana.org/domains/root/db/tvs.html
tvs

// ubank : National Australia Bank Limited
// https://www.iana.org/domains/root/db/ubank.html
ubank

// ubs : UBS AG
// https://www.iana.org/domains/root/db/ubs.html
ubs

// unicom : China United Network Communications Corporation Limited
// https://www.iana.org/domains/root/db/unicom.html
unicom

// university : Binky Moon, LLC
// https://www.iana.org/domains/root/db/university.html
university

// uno : Radix FZC DMCC
// https://www.iana.org/domains/root/db/uno.html
uno

// uol : UBN INTERNET LTDA.
// https://www.iana.org/domains/root/db/uol.html
uol

// ups : UPS Market Driver, Inc.
// https://www.iana.org/domains/root/db/ups.html
ups

// vacations : Binky Moon, LLC
// https://www.iana.org/domains/root/db/vacations.html
vacations

// vana : Lifestyle Domain Holdings, Inc.
// https://www.iana.org/domains/root/db/vana.html
vana

// vanguard : The Vanguard Group, Inc.
// https://www.iana.org/domains/root/db/vanguard.html
vanguard

// vegas : Dot Vegas, Inc.
// https://www.iana.org/domains/root/db/vegas.html
vegas

// ventures : Binky Moon, LLC
// https://www.iana.org/domains/root/db/ventures.html
ventures

// verisign : VeriSign, Inc.
// https://www.iana.org/domains/root/db/verisign.html
verisign

// versicherung : tldbox GmbH
// https://www.iana.org/domains/root/db/versicherung.html
versicherung

// vet : Dog Beach, LLC
// https://www.iana.org/domains/root/db/vet.html
vet

// viajes : Binky Moon, LLC
// https://www.iana.org/domains/root/db/viajes.html
viajes

// video : Dog Beach, LLC
// https://www.iana.org/domains/root/db/video.html
video

// vig : VIENNA INSURANCE GROUP AG Wiener Versicherung Gruppe
// https://www.iana.org/domains/root/db/vig.html
vig

// viking : Viking River Cruises (Bermuda) Ltd.
// https://www.iana.org/domains/root/db/viking.html
viking

// villas : Binky Moon, LLC
// https://www.iana.org/domains/root/db/villas.html
villas

// vin : Binky Moon, LLC
// https://www.iana.org/domains/root/db/vin.html
vin

// vip : Registry Services, LLC
// https://www.iana.org/domains/root/db/vip.html
vip

// virgin : Virgin Enterprises Limited
// https://www.iana.org/domains/root/db/virgin.html
virgin

// visa : Visa Worldwide Pte. Limited
// https://www.iana.org/domains/root/db/visa.html
visa

// vision : Binky Moon, LLC
// https://www.iana.org/domains/root/db/vision.html
vision

// viva : Saudi Telecom Company
// https://www.iana.org/domains/root/db/viva.html
viva

// vivo : Telefonica Brasil S.A.
// https://www.iana.org/domains/root/db/vivo.html
vivo

// vlaanderen : DNS.be vzw
// https://www.iana.org/domains/root/db/vlaanderen.html
vlaanderen

// vodka : Registry Services, LLC
// https://www.iana.org/domains/root/db/vodka.html
vodka

// volkswagen : Volkswagen Group of America Inc.
// https://www.iana.org/domains/root/db/volkswagen.html
volkswagen

// volvo : Volvo Holding Sverige Aktiebolag
// https://www.iana.org/domains/root/db/volvo.html
volvo

// vote : Monolith Registry LLC
// https://www.iana.org/domains/root/db/vote.html
vote

// voting : Valuetainment Corp.
// https://www.iana.org/domains/root/db/voting.html
voting

// voto : Monolith Registry LLC
// https://www.iana.org/domains/root/db/voto.html
voto

// voyage : Binky Moon, LLC
// https://www.iana.org/domains/root/db/voyage.html
voyage

// wales : Nominet UK
// https://www.iana.org/domains/root/db/wales.html
wales

// walmart : Wal-Mart Stores, Inc.
// https://www.iana.org/domains/root/db/walmart.html
walmart

// walter : Sandvik AB
// https://www.iana.org/domains/root/db/walter.html
walter

// wang : Zodiac Wang Limited
// https://www.iana.org/domains/root/db/wang.html
wang

// wanggou : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/wanggou.html
wanggou

// watch : Binky Moon, LLC
// https://www.iana.org/domains/root/db/watch.html
watch

// watches : Identity Digital Limited
// https://www.iana.org/domains/root/db/watches.html
watches

// weather : International Business Machines Corporation
// https://www.iana.org/domains/root/db/weather.html
weather

// weatherchannel : International Business Machines Corporation
// https://www.iana.org/domains/root/db/weatherchannel.html
weatherchannel

// webcam : dot Webcam Limited
// https://www.iana.org/domains/root/db/webcam.html
webcam

// weber : Saint-Gobain Weber SA
// https://www.iana.org/domains/root/db/weber.html
weber

// website : Radix FZC DMCC
// https://www.iana.org/domains/root/db/website.html
website

// wedding : Registry Services, LLC
// https://www.iana.org/domains/root/db/wedding.html
wedding

// weibo : Sina Corporation
// https://www.iana.org/domains/root/db/weibo.html
weibo

// weir : Weir Group IP Limited
// https://www.iana.org/domains/root/db/weir.html
weir

// whoswho : Who's Who Registry
// https://www.iana.org/domains/root/db/whoswho.html
whoswho

// wien : punkt.wien GmbH
// https://www.iana.org/domains/root/db/wien.html
wien

// wiki : Registry Services, LLC
// https://www.iana.org/domains/root/db/wiki.html
wiki

// williamhill : William Hill Organization Limited
// https://www.iana.org/domains/root/db/williamhill.html
williamhill

// win : First Registry Limited
// https://www.iana.org/domains/root/db/win.html
win

// windows : Microsoft Corporation
// https://www.iana.org/domains/root/db/windows.html
windows

// wine : Binky Moon, LLC
// https://www.iana.org/domains/root/db/wine.html
wine

// winners : The TJX Companies, Inc.
// https://www.iana.org/domains/root/db/winners.html
winners

// wme : William Morris Endeavor Entertainment, LLC
// https://www.iana.org/domains/root/db/wme.html
wme

// wolterskluwer : Wolters Kluwer N.V.
// https://www.iana.org/domains/root/db/wolterskluwer.html
wolterskluwer

// woodside : Woodside Petroleum Limited
// https://www.iana.org/domains/root/db/woodside.html
woodside

// work : Registry Services, LLC
// https://www.iana.org/domains/root/db/work.html
work

// works : Binky Moon, LLC
// https://www.iana.org/domains/root/db/works.html
works

// world : Binky Moon, LLC
// https://www.iana.org/domains/root/db/world.html
world

// wow : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/wow.html
wow

// wtc : World Trade Centers Association, Inc.
// https://www.iana.org/domains/root/db/wtc.html
wtc

// wtf : Binky Moon, LLC
// https://www.iana.org/domains/root/db/wtf.html
wtf

// xbox : Microsoft Corporation
// https://www.iana.org/domains/root/db/xbox.html
xbox

// xerox : Xerox DNHC LLC
// https://www.iana.org/domains/root/db/xerox.html
xerox

// xfinity : Comcast IP Holdings I, LLC
// https://www.iana.org/domains/root/db/xfinity.html
xfinity

// xihuan : Beijing Qihu Keji Co., Ltd.
// https://www.iana.org/domains/root/db/xihuan.html
xihuan

// xin : Elegant Leader Limited
// https://www.iana.org/domains/root/db/xin.html
xin

// xn--11b4c3d : VeriSign Sarl
// https://www.iana.org/domains/root/db/xn--11b4c3d.html
à¤•à¥‰à¤®

// xn--1ck2e1b : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/xn--1ck2e1b.html
ã‚»ãƒ¼ãƒ«

// xn--1qqw23a : Guangzhou YU Wei Information Technology Co., Ltd.
// https://www.iana.org/domains/root/db/xn--1qqw23a.html
ä½›å±±

// xn--30rr7y : Excellent First Limited
// https://www.iana.org/domains/root/db/xn--30rr7y.html
æ…ˆå–„

// xn--3bst00m : Eagle Horizon Limited
// https://www.iana.org/domains/root/db/xn--3bst00m.html
é›†å›¢

// xn--3ds443g : TLD REGISTRY LIMITED OY
// https://www.iana.org/domains/root/db/xn--3ds443g.html
åœ¨çº¿

// xn--3pxu8k : VeriSign Sarl
// https://www.iana.org/domains/root/db/xn--3pxu8k.html
ç‚¹çœ‹

// xn--42c2d9a : VeriSign Sarl
// https://www.iana.org/domains/root/db/xn--42c2d9a.html
à¸„à¸­à¸¡

// xn--45q11c : Zodiac Gemini Ltd
// https://www.iana.org/domains/root/db/xn--45q11c.html
å…«å�¦

// xn--4gbrim : Helium TLDs Ltd
// https://www.iana.org/domains/root/db/xn--4gbrim.html
Ù…ÙˆÙ‚Ø¹

// xn--55qw42g : China Organizational Name Administration Center
// https://www.iana.org/domains/root/db/xn--55qw42g.html
å…¬ç›Š

// xn--55qx5d : China Internet Network Information Center (CNNIC)
// https://www.iana.org/domains/root/db/xn--55qx5d.html
å…¬å�¸

// xn--5su34j936bgsg : Shangriâ€�La International Hotel Management Limited
// https://www.iana.org/domains/root/db/xn--5su34j936bgsg.html
é¦™æ ¼é‡Œæ‹‰

// xn--5tzm5g : Global Website TLD Asia Limited
// https://www.iana.org/domains/root/db/xn--5tzm5g.html
ç½‘ç«™

// xn--6frz82g : Identity Digital Limited
// https://www.iana.org/domains/root/db/xn--6frz82g.html
ç§»åŠ¨

// xn--6qq986b3xl : Tycoon Treasure Limited
// https://www.iana.org/domains/root/db/xn--6qq986b3xl.html
æˆ‘çˆ±ä½ 

// xn--80adxhks : Foundation for Assistance for Internet Technologies and Infrastructure Development (FAITID)
// https://www.iana.org/domains/root/db/xn--80adxhks.html
Ð¼Ð¾Ñ�ÐºÐ²Ð°

// xn--80aqecdr1a : Pontificium Consilium de Comunicationibus Socialibus (PCCS) (Pontifical Council for Social Communication)
// https://www.iana.org/domains/root/db/xn--80aqecdr1a.html
ÐºÐ°Ñ‚Ð¾Ð»Ð¸Ðº

// xn--80asehdb : CORE Association
// https://www.iana.org/domains/root/db/xn--80asehdb.html
Ð¾Ð½Ð»Ð°Ð¹Ð½

// xn--80aswg : CORE Association
// https://www.iana.org/domains/root/db/xn--80aswg.html
Ñ�Ð°Ð¹Ñ‚

// xn--8y0a063a : China United Network Communications Corporation Limited
// https://www.iana.org/domains/root/db/xn--8y0a063a.html
è�”é€š

// xn--9dbq2a : VeriSign Sarl
// https://www.iana.org/domains/root/db/xn--9dbq2a.html
×§×•×�

// xn--9et52u : RISE VICTORY LIMITED
// https://www.iana.org/domains/root/db/xn--9et52u.html
æ—¶å°š

// xn--9krt00a : Sina Corporation
// https://www.iana.org/domains/root/db/xn--9krt00a.html
å¾®å�š

// xn--b4w605ferd : Temasek Holdings (Private) Limited
// https://www.iana.org/domains/root/db/xn--b4w605ferd.html
æ·¡é©¬é”¡

// xn--bck1b9a5dre4c : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/xn--bck1b9a5dre4c.html
ãƒ•ã‚¡ãƒƒã‚·ãƒ§ãƒ³

// xn--c1avg : Public Interest Registry
// https://www.iana.org/domains/root/db/xn--c1avg.html
Ð¾Ñ€Ð³

// xn--c2br7g : VeriSign Sarl
// https://www.iana.org/domains/root/db/xn--c2br7g.html
à¤¨à¥‡à¤Ÿ

// xn--cck2b3b : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/xn--cck2b3b.html
ã‚¹ãƒˆã‚¢

// xn--cckwcxetd : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/xn--cckwcxetd.html
ã‚¢ãƒžã‚¾ãƒ³

// xn--cg4bki : SAMSUNG SDS CO., LTD
// https://www.iana.org/domains/root/db/xn--cg4bki.html
ì‚¼ì„±

// xn--czr694b : Internet DotTrademark Organisation Limited
// https://www.iana.org/domains/root/db/xn--czr694b.html
å•†æ ‡

// xn--czrs0t : Binky Moon, LLC
// https://www.iana.org/domains/root/db/xn--czrs0t.html
å•†åº—

// xn--czru2d : Zodiac Aquarius Limited
// https://www.iana.org/domains/root/db/xn--czru2d.html
å•†åŸŽ

// xn--d1acj3b : The Foundation for Network Initiatives â€œThe Smart Internetâ€�
// https://www.iana.org/domains/root/db/xn--d1acj3b.html
Ð´ÐµÑ‚Ð¸

// xn--eckvdtc9d : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/xn--eckvdtc9d.html
ãƒ�ã‚¤ãƒ³ãƒˆ

// xn--efvy88h : Guangzhou YU Wei Information Technology Co., Ltd.
// https://www.iana.org/domains/root/db/xn--efvy88h.html
æ–°é—»

// xn--fct429k : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/xn--fct429k.html
å®¶é›»

// xn--fhbei : VeriSign Sarl
// https://www.iana.org/domains/root/db/xn--fhbei.html
ÙƒÙˆÙ…

// xn--fiq228c5hs : TLD REGISTRY LIMITED OY
// https://www.iana.org/domains/root/db/xn--fiq228c5hs.html
ä¸­æ–‡ç½‘

// xn--fiq64b : CITIC Group Corporation
// https://www.iana.org/domains/root/db/xn--fiq64b.html
ä¸­ä¿¡

// xn--fjq720a : Binky Moon, LLC
// https://www.iana.org/domains/root/db/xn--fjq720a.html
å¨±ä¹�

// xn--flw351e : Charleston Road Registry Inc.
// https://www.iana.org/domains/root/db/xn--flw351e.html
è°·æ­Œ

// xn--fzys8d69uvgm : PCCW Enterprises Limited
// https://www.iana.org/domains/root/db/xn--fzys8d69uvgm.html
é›»è¨Šç›ˆç§‘

// xn--g2xx48c : Nawang Heli(Xiamen) Network Service Co., LTD.
// https://www.iana.org/domains/root/db/xn--g2xx48c.html
è´­ç‰©

// xn--gckr3f0f : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/xn--gckr3f0f.html
ã‚¯ãƒ©ã‚¦ãƒ‰

// xn--gk3at1e : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/xn--gk3at1e.html
é€šè²©

// xn--hxt814e : Zodiac Taurus Limited
// https://www.iana.org/domains/root/db/xn--hxt814e.html
ç½‘åº—

// xn--i1b6b1a6a2e : Public Interest Registry
// https://www.iana.org/domains/root/db/xn--i1b6b1a6a2e.html
à¤¸à¤‚à¤—à¤ à¤¨

// xn--imr513n : Internet DotTrademark Organisation Limited
// https://www.iana.org/domains/root/db/xn--imr513n.html
é¤�åŽ…

// xn--io0a7i : China Internet Network Information Center (CNNIC)
// https://www.iana.org/domains/root/db/xn--io0a7i.html
ç½‘ç»œ

// xn--j1aef : VeriSign Sarl
// https://www.iana.org/domains/root/db/xn--j1aef.html
ÐºÐ¾Ð¼

// xn--jlq480n2rg : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/xn--jlq480n2rg.html
äºšé©¬é€Š

// xn--jvr189m : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/xn--jvr189m.html
é£Ÿå“�

// xn--kcrx77d1x4a : Koninklijke Philips N.V.
// https://www.iana.org/domains/root/db/xn--kcrx77d1x4a.html
é£žåˆ©æµ¦

// xn--kput3i : Beijing RITT-Net Technology Development Co., Ltd
// https://www.iana.org/domains/root/db/xn--kput3i.html
æ‰‹æœº

// xn--mgba3a3ejt : Aramco Services Company
// https://www.iana.org/domains/root/db/xn--mgba3a3ejt.html
Ø§Ø±Ø§Ù…ÙƒÙˆ

// xn--mgba7c0bbn0a : Competrol (Luxembourg) Sarl
// https://www.iana.org/domains/root/db/xn--mgba7c0bbn0a.html
Ø§Ù„Ø¹Ù„ÙŠØ§Ù†

// xn--mgbaakc7dvf : Emirates Telecommunications Corporation (trading as Etisalat)
// https://www.iana.org/domains/root/db/xn--mgbaakc7dvf.html
Ø§ØªØµØ§Ù„Ø§Øª

// xn--mgbab2bd : CORE Association
// https://www.iana.org/domains/root/db/xn--mgbab2bd.html
Ø¨Ø§Ø²Ø§Ø±

// xn--mgbca7dzdo : Abu Dhabi Systems and Information Centre
// https://www.iana.org/domains/root/db/xn--mgbca7dzdo.html
Ø§Ø¨ÙˆØ¸Ø¨ÙŠ

// xn--mgbi4ecexp : Pontificium Consilium de Comunicationibus Socialibus (PCCS) (Pontifical Council for Social Communication)
// https://www.iana.org/domains/root/db/xn--mgbi4ecexp.html
ÙƒØ§Ø«ÙˆÙ„ÙŠÙƒ

// xn--mgbt3dhd : Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti.
// https://www.iana.org/domains/root/db/xn--mgbt3dhd.html
Ù‡Ù…Ø±Ø§Ù‡

// xn--mk1bu44c : VeriSign Sarl
// https://www.iana.org/domains/root/db/xn--mk1bu44c.html
ë‹·ì»´

// xn--mxtq1m : Net-Chinese Co., Ltd.
// https://www.iana.org/domains/root/db/xn--mxtq1m.html
æ”¿åºœ

// xn--ngbc5azd : International Domain Registry Pty. Ltd.
// https://www.iana.org/domains/root/db/xn--ngbc5azd.html
Ø´Ø¨ÙƒØ©

// xn--ngbe9e0a : Kuwait Finance House
// https://www.iana.org/domains/root/db/xn--ngbe9e0a.html
Ø¨ÙŠØªÙƒ

// xn--ngbrx : League of Arab States
// https://www.iana.org/domains/root/db/xn--ngbrx.html
Ø¹Ø±Ø¨

// xn--nqv7f : Public Interest Registry
// https://www.iana.org/domains/root/db/xn--nqv7f.html
æœºæž„

// xn--nqv7fs00ema : Public Interest Registry
// https://www.iana.org/domains/root/db/xn--nqv7fs00ema.html
ç»„ç»‡æœºæž„

// xn--nyqy26a : Stable Tone Limited
// https://www.iana.org/domains/root/db/xn--nyqy26a.html
å�¥åº·

// xn--otu796d : Jiang Yu Liang Cai Technology Company Limited
// https://www.iana.org/domains/root/db/xn--otu796d.html
æ‹›è�˜

// xn--p1acf : Rusnames Limited
// https://www.iana.org/domains/root/db/xn--p1acf.html
Ñ€ÑƒÑ�

// xn--pssy2u : VeriSign Sarl
// https://www.iana.org/domains/root/db/xn--pssy2u.html
å¤§æ‹¿

// xn--q9jyb4c : Charleston Road Registry Inc.
// https://www.iana.org/domains/root/db/xn--q9jyb4c.html
ã�¿ã‚“ã�ª

// xn--qcka1pmc : Charleston Road Registry Inc.
// https://www.iana.org/domains/root/db/xn--qcka1pmc.html
ã‚°ãƒ¼ã‚°ãƒ«

// xn--rhqv96g : Stable Tone Limited
// https://www.iana.org/domains/root/db/xn--rhqv96g.html
ä¸–ç•Œ

// xn--rovu88b : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/xn--rovu88b.html
æ›¸ç±�

// xn--ses554g : KNET Co., Ltd.
// https://www.iana.org/domains/root/db/xn--ses554g.html
ç½‘å�€

// xn--t60b56a : VeriSign Sarl
// https://www.iana.org/domains/root/db/xn--t60b56a.html
ë‹·ë„·

// xn--tckwe : VeriSign Sarl
// https://www.iana.org/domains/root/db/xn--tckwe.html
ã‚³ãƒ 

// xn--tiq49xqyj : Pontificium Consilium de Comunicationibus Socialibus (PCCS) (Pontifical Council for Social Communication)
// https://www.iana.org/domains/root/db/xn--tiq49xqyj.html
å¤©ä¸»æ•™

// xn--unup4y : Binky Moon, LLC
// https://www.iana.org/domains/root/db/xn--unup4y.html
æ¸¸æˆ�

// xn--vermgensberater-ctb : Deutsche VermÃ¶gensberatung Aktiengesellschaft DVAG
// https://www.iana.org/domains/root/db/xn--vermgensberater-ctb.html
vermÃ¶gensberater

// xn--vermgensberatung-pwb : Deutsche VermÃ¶gensberatung Aktiengesellschaft DVAG
// https://www.iana.org/domains/root/db/xn--vermgensberatung-pwb.html
vermÃ¶gensberatung

// xn--vhquv : Binky Moon, LLC
// https://www.iana.org/domains/root/db/xn--vhquv.html
ä¼�ä¸š

// xn--vuq861b : Beijing Tele-info Technology Co., Ltd.
// https://www.iana.org/domains/root/db/xn--vuq861b.html
ä¿¡æ�¯

// xn--w4r85el8fhu5dnra : Kerry Trading Co. Limited
// https://www.iana.org/domains/root/db/xn--w4r85el8fhu5dnra.html
å˜‰é‡Œå¤§é…’åº—

// xn--w4rs40l : Kerry Trading Co. Limited
// https://www.iana.org/domains/root/db/xn--w4rs40l.html
å˜‰é‡Œ

// xn--xhq521b : Guangzhou YU Wei Information Technology Co., Ltd.
// https://www.iana.org/domains/root/db/xn--xhq521b.html
å¹¿ä¸œ

// xn--zfr164b : China Organizational Name Administration Center
// https://www.iana.org/domains/root/db/xn--zfr164b.html
æ”¿åŠ¡

// xyz : XYZ.COM LLC
// https://www.iana.org/domains/root/db/xyz.html
xyz

// yachts : XYZ.COM LLC
// https://www.iana.org/domains/root/db/yachts.html
yachts

// yahoo : Oath Inc.
// https://www.iana.org/domains/root/db/yahoo.html
yahoo

// yamaxun : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/yamaxun.html
yamaxun

// yandex : Yandex Europe B.V.
// https://www.iana.org/domains/root/db/yandex.html
yandex

// yodobashi : YODOBASHI CAMERA CO.,LTD.
// https://www.iana.org/domains/root/db/yodobashi.html
yodobashi

// yoga : Registry Services, LLC
// https://www.iana.org/domains/root/db/yoga.html
yoga

// yokohama : GMO Registry, Inc.
// https://www.iana.org/domains/root/db/yokohama.html
yokohama

// you : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/you.html
you

// youtube : Charleston Road Registry Inc.
// https://www.iana.org/domains/root/db/youtube.html
youtube

// yun : Beijing Qihu Keji Co., Ltd.
// https://www.iana.org/domains/root/db/yun.html
yun

// zappos : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/zappos.html
zappos

// zara : Industria de DiseÃ±o Textil, S.A. (INDITEX, S.A.)
// https://www.iana.org/domains/root/db/zara.html
zara

// zero : Amazon Registry Services, Inc.
// https://www.iana.org/domains/root/db/zero.html
zero

// zip : Charleston Road Registry Inc.
// https://www.iana.org/domains/root/db/zip.html
zip

// zone : Binky Moon, LLC
// https://www.iana.org/domains/root/db/zone.html
zone

// zuerich : Kanton ZÃ¼rich (Canton of Zurich)
// https://www.iana.org/domains/root/db/zuerich.html
zuerich


// ===END ICANN DOMAINS===
// ===BEGIN PRIVATE DOMAINS===
// (Note: these are in alphabetical order by company name)

// 1GB LLC : https://www.1gb.ua/
// Submitted by 1GB LLC <noc@1gb.com.ua>
cc.ua
inf.ua
ltd.ua

// 611coin : https://611project.org/
611.to

// Aaron Marais' Gitlab pages: https://lab.aaronleem.co.za
// Submitted by Aaron Marais <its_me@aaronleem.co.za>
graphox.us

// accesso Technology Group, plc. : https://accesso.com/
// Submitted by accesso Team <accessoecommerce@accesso.com>
*.devcdnaccesso.com

// Acorn Labs : https://acorn.io
// Submitted by Craig Jellick <domains@acorn.io>
*.on-acorn.io

// ActiveTrail: https://www.activetrail.biz/
// Submitted by Ofer Kalaora <postmaster@activetrail.com>
activetrail.biz

// Adobe : https://www.adobe.com/
// Submitted by Ian Boston <boston@adobe.com> and Lars Trieloff <trieloff@adobe.com>
adobeaemcloud.com
*.dev.adobeaemcloud.com
hlx.live
adobeaemcloud.net
hlx.page
hlx3.page

// Adobe Developer Platform : https://developer.adobe.com
// Submitted by Jesse MacFadyen<jessem@adobe.com>
adobeio-static.net
adobeioruntime.net

// Agnat sp. z o.o. : https://domena.pl
// Submitted by Przemyslaw Plewa <it-admin@domena.pl>
beep.pl

// Airkit : https://www.airkit.com/
// Submitted by Grant Cooksey <security@airkit.com>
airkitapps.com
airkitapps-au.com
airkitapps.eu

// Aiven: https://aiven.io/
// Submitted by Etienne Stalmans <security@aiven.io>
aivencloud.com

// Akamai : https://www.akamai.com/
// Submitted by Akamai Team <publicsuffixlist@akamai.com>
akadns.net
akamai.net
akamai-staging.net
akamaiedge.net
akamaiedge-staging.net
akamaihd.net
akamaihd-staging.net
akamaiorigin.net
akamaiorigin-staging.net
akamaized.net
akamaized-staging.net
edgekey.net
edgekey-staging.net
edgesuite.net
edgesuite-staging.net

// alboto.ca : http://alboto.ca
// Submitted by Anton Avramov <avramov@alboto.ca>
barsy.ca

// Alces Software Ltd : http://alces-software.com
// Submitted by Mark J. Titorenko <mark.titorenko@alces-software.com>
*.compute.estate
*.alces.network

// all-inkl.com : https://all-inkl.com
// Submitted by Werner Kaltofen <wk@all-inkl.com>
kasserver.com

// Altervista: https://www.altervista.org
// Submitted by Carlo Cannas <tech_staff@altervista.it>
altervista.org

// alwaysdata : https://www.alwaysdata.com
// Submitted by Cyril <admin@alwaysdata.com>
alwaysdata.net

// Amaze Software : https://amaze.co
// Submitted by Domain Admin <domainadmin@amaze.co>
myamaze.net

// Amazon : https://www.amazon.com/
// Submitted by AWS Security <psl-maintainers@amazon.com>
// Subsections of Amazon/subsidiaries will appear until "concludes" tag

// Amazon CloudFront
// Submitted by Donavan Miller <donavanm@amazon.com>
// Reference: 54144616-fd49-4435-8535-19c6a601bdb3
cloudfront.net

// Amazon EC2
// Submitted by Luke Wells <psl-maintainers@amazon.com>
// Reference: 4c38fa71-58ac-4768-99e5-689c1767e537
*.compute.amazonaws.com
*.compute-1.amazonaws.com
*.compute.amazonaws.com.cn
us-east-1.amazonaws.com

// Amazon S3
// Submitted by Luke Wells <psl-maintainers@amazon.com>
// Reference: d068bd97-f0a9-4838-a6d8-954b622ef4ae
s3.cn-north-1.amazonaws.com.cn
s3.dualstack.ap-northeast-1.amazonaws.com
s3.dualstack.ap-northeast-2.amazonaws.com
s3.ap-northeast-2.amazonaws.com
s3-website.ap-northeast-2.amazonaws.com
s3.dualstack.ap-south-1.amazonaws.com
s3.ap-south-1.amazonaws.com
s3-website.ap-south-1.amazonaws.com
s3.dualstack.ap-southeast-1.amazonaws.com
s3.dualstack.ap-southeast-2.amazonaws.com
s3.dualstack.ca-central-1.amazonaws.com
s3.ca-central-1.amazonaws.com
s3-website.ca-central-1.amazonaws.com
s3.dualstack.eu-central-1.amazonaws.com
s3.eu-central-1.amazonaws.com
s3-website.eu-central-1.amazonaws.com
s3.dualstack.eu-west-1.amazonaws.com
s3.dualstack.eu-west-2.amazonaws.com
s3.eu-west-2.amazonaws.com
s3-website.eu-west-2.amazonaws.com
s3.dualstack.eu-west-3.amazonaws.com
s3.eu-west-3.amazonaws.com
s3-website.eu-west-3.amazonaws.com
s3.amazonaws.com
s3-ap-northeast-1.amazonaws.com
s3-ap-northeast-2.amazonaws.com
s3-ap-south-1.amazonaws.com
s3-ap-southeast-1.amazonaws.com
s3-ap-southeast-2.amazonaws.com
s3-ca-central-1.amazonaws.com
s3-eu-central-1.amazonaws.com
s3-eu-west-1.amazonaws.com
s3-eu-west-2.amazonaws.com
s3-eu-west-3.amazonaws.com
s3-external-1.amazonaws.com
s3-fips-us-gov-west-1.amazonaws.com
s3-sa-east-1.amazonaws.com
s3-us-east-2.amazonaws.com
s3-us-gov-west-1.amazonaws.com
s3-us-west-1.amazonaws.com
s3-us-west-2.amazonaws.com
s3-website-ap-northeast-1.amazonaws.com
s3-website-ap-southeast-1.amazonaws.com
s3-website-ap-southeast-2.amazonaws.com
s3-website-eu-west-1.amazonaws.com
s3-website-sa-east-1.amazonaws.com
s3-website-us-east-1.amazonaws.com
s3-website-us-west-1.amazonaws.com
s3-website-us-west-2.amazonaws.com
s3.dualstack.sa-east-1.amazonaws.com
s3.dualstack.us-east-1.amazonaws.com
s3.dualstack.us-east-2.amazonaws.com
s3.us-east-2.amazonaws.com
s3-website.us-east-2.amazonaws.com

// Analytics on AWS
// Submitted by AWS Security <psl-maintainers@amazon.com>
// Reference: c02c3a80-f8a0-4fd2-b719-48ea8b7c28de
analytics-gateway.ap-northeast-1.amazonaws.com
analytics-gateway.eu-west-1.amazonaws.com
analytics-gateway.us-east-1.amazonaws.com
analytics-gateway.us-east-2.amazonaws.com
analytics-gateway.us-west-2.amazonaws.com

// AWS Cloud9
// Submitted by: AWS Security <psl-maintainers@amazon.com>
// Reference: 05c44955-977c-4b57-938a-f2af92733f9f
webview-assets.aws-cloud9.af-south-1.amazonaws.com
vfs.cloud9.af-south-1.amazonaws.com
webview-assets.cloud9.af-south-1.amazonaws.com
webview-assets.aws-cloud9.ap-east-1.amazonaws.com
vfs.cloud9.ap-east-1.amazonaws.com
webview-assets.cloud9.ap-east-1.amazonaws.com
webview-assets.aws-cloud9.ap-northeast-1.amazonaws.com
vfs.cloud9.ap-northeast-1.amazonaws.com
webview-assets.cloud9.ap-northeast-1.amazonaws.com
webview-assets.aws-cloud9.ap-northeast-2.amazonaws.com
vfs.cloud9.ap-northeast-2.amazonaws.com
webview-assets.cloud9.ap-northeast-2.amazonaws.com
webview-assets.aws-cloud9.ap-northeast-3.amazonaws.com
vfs.cloud9.ap-northeast-3.amazonaws.com
webview-assets.cloud9.ap-northeast-3.amazonaws.com
webview-assets.aws-cloud9.ap-south-1.amazonaws.com
vfs.cloud9.ap-south-1.amazonaws.com
webview-assets.cloud9.ap-south-1.amazonaws.com
webview-assets.aws-cloud9.ap-southeast-1.amazonaws.com
vfs.cloud9.ap-southeast-1.amazonaws.com
webview-assets.cloud9.ap-southeast-1.amazonaws.com
webview-assets.aws-cloud9.ap-southeast-2.amazonaws.com
vfs.cloud9.ap-southeast-2.amazonaws.com
webview-assets.cloud9.ap-southeast-2.amazonaws.com
webview-assets.aws-cloud9.ca-central-1.amazonaws.com
vfs.cloud9.ca-central-1.amazonaws.com
webview-assets.cloud9.ca-central-1.amazonaws.com
webview-assets.aws-cloud9.eu-central-1.amazonaws.com
vfs.cloud9.eu-central-1.amazonaws.com
webview-assets.cloud9.eu-central-1.amazonaws.com
webview-assets.aws-cloud9.eu-north-1.amazonaws.com
vfs.cloud9.eu-north-1.amazonaws.com
webview-assets.cloud9.eu-north-1.amazonaws.com
webview-assets.aws-cloud9.eu-south-1.amazonaws.com
vfs.cloud9.eu-south-1.amazonaws.com
webview-assets.cloud9.eu-south-1.amazonaws.com
webview-assets.aws-cloud9.eu-west-1.amazonaws.com
vfs.cloud9.eu-west-1.amazonaws.com
webview-assets.cloud9.eu-west-1.amazonaws.com
webview-assets.aws-cloud9.eu-west-2.amazonaws.com
vfs.cloud9.eu-west-2.amazonaws.com
webview-assets.cloud9.eu-west-2.amazonaws.com
webview-assets.aws-cloud9.eu-west-3.amazonaws.com
vfs.cloud9.eu-west-3.amazonaws.com
webview-assets.cloud9.eu-west-3.amazonaws.com
webview-assets.aws-cloud9.me-south-1.amazonaws.com
vfs.cloud9.me-south-1.amazonaws.com
webview-assets.cloud9.me-south-1.amazonaws.com
webview-assets.aws-cloud9.sa-east-1.amazonaws.com
vfs.cloud9.sa-east-1.amazonaws.com
webview-assets.cloud9.sa-east-1.amazonaws.com
webview-assets.aws-cloud9.us-east-1.amazonaws.com
vfs.cloud9.us-east-1.amazonaws.com
webview-assets.cloud9.us-east-1.amazonaws.com
webview-assets.aws-cloud9.us-east-2.amazonaws.com
vfs.cloud9.us-east-2.amazonaws.com
webview-assets.cloud9.us-east-2.amazonaws.com
webview-assets.aws-cloud9.us-west-1.amazonaws.com
vfs.cloud9.us-west-1.amazonaws.com
webview-assets.cloud9.us-west-1.amazonaws.com
webview-assets.aws-cloud9.us-west-2.amazonaws.com
vfs.cloud9.us-west-2.amazonaws.com
webview-assets.cloud9.us-west-2.amazonaws.com

// AWS Elastic Beanstalk
// Submitted by Luke Wells <psl-maintainers@amazon.com>
// Reference: aa202394-43a0-4857-b245-8db04549137e
cn-north-1.eb.amazonaws.com.cn
cn-northwest-1.eb.amazonaws.com.cn
elasticbeanstalk.com
ap-northeast-1.elasticbeanstalk.com
ap-northeast-2.elasticbeanstalk.com
ap-northeast-3.elasticbeanstalk.com
ap-south-1.elasticbeanstalk.com
ap-southeast-1.elasticbeanstalk.com
ap-southeast-2.elasticbeanstalk.com
ca-central-1.elasticbeanstalk.com
eu-central-1.elasticbeanstalk.com
eu-west-1.elasticbeanstalk.com
eu-west-2.elasticbeanstalk.com
eu-west-3.elasticbeanstalk.com
sa-east-1.elasticbeanstalk.com
us-east-1.elasticbeanstalk.com
us-east-2.elasticbeanstalk.com
us-gov-west-1.elasticbeanstalk.com
us-west-1.elasticbeanstalk.com
us-west-2.elasticbeanstalk.com

// (AWS) Elastic Load Balancing
// Submitted by Luke Wells <psl-maintainers@amazon.com>
// Reference: 12a3d528-1bac-4433-a359-a395867ffed2
*.elb.amazonaws.com.cn
*.elb.amazonaws.com

// AWS Global Accelerator
// Submitted by Daniel Massaguer <psl-maintainers@amazon.com>
// Reference: d916759d-a08b-4241-b536-4db887383a6a
awsglobalaccelerator.com

// eero
// Submitted by Yue Kang <eero-dynamic-dns@amazon.com>
// Reference: 264afe70-f62c-4c02-8ab9-b5281ed24461
eero.online
eero-stage.online

// concludes Amazon

// Amune : https://amune.org/
// Submitted by Team Amune <cert@amune.org>
t3l3p0rt.net
tele.amune.org

// Apigee : https://apigee.com/
// Submitted by Apigee Security Team <security@apigee.com>
apigee.io

// Apphud : https://apphud.com
// Submitted by Alexander Selivanov <alex@apphud.com>
siiites.com

// Appspace : https://www.appspace.com
// Submitted by Appspace Security Team <security@appspace.com>
appspacehosted.com
appspaceusercontent.com

// Appudo UG (haftungsbeschrÃ¤nkt) : https://www.appudo.com
// Submitted by Alexander Hochbaum <admin@appudo.com>
appudo.net

// Aptible : https://www.aptible.com/
// Submitted by Thomas Orozco <thomas@aptible.com>
on-aptible.com

// ASEINet : https://www.aseinet.com/
// Submitted by Asei SEKIGUCHI <mail@aseinet.com>
user.aseinet.ne.jp
gv.vc
d.gv.vc

// AsociaciÃ³n Amigos de la InformÃ¡tica "Euskalamiga" : http://encounter.eus/
// Submitted by Hector Martin <marcan@euskalencounter.org>
user.party.eus

// Association potager.org : https://potager.org/
// Submitted by Lunar <jardiniers@potager.org>
pimienta.org
poivron.org
potager.org
sweetpepper.org

// ASUSTOR Inc. : http://www.asustor.com
// Submitted by Vincent Tseng <vincenttseng@asustor.com>
myasustor.com

// Atlassian : https://atlassian.com
// Submitted by Sam Smyth <devloop@atlassian.com>
cdn.prod.atlassian-dev.net

// Authentick UG (haftungsbeschrÃ¤nkt) : https://authentick.net
// Submitted by Lukas Reschke <lukas@authentick.net>
translated.page

// Autocode : https://autocode.com
// Submitted by Jacob Lee <jacob@autocode.com>
autocode.dev

// AVM : https://avm.de
// Submitted by Andreas Weise <a.weise@avm.de>
myfritz.net

// AVStack Pte. Ltd. : https://avstack.io
// Submitted by Jasper Hugo <jasper@avstack.io>
onavstack.net

// AW AdvisorWebsites.com Software Inc : https://advisorwebsites.com
// Submitted by James Kennedy <domains@advisorwebsites.com>
*.awdev.ca
*.advisor.ws

// AZ.pl sp. z.o.o: https://az.pl
// Submitted by Krzysztof Wolski <krzysztof.wolski@home.eu>
ecommerce-shop.pl

// b-data GmbH : https://www.b-data.io
// Submitted by Olivier Benz <olivier.benz@b-data.ch>
b-data.io

// backplane : https://www.backplane.io
// Submitted by Anthony Voutas <anthony@backplane.io>
backplaneapp.io

// Balena : https://www.balena.io
// Submitted by Petros Angelatos <petrosagg@balena.io>
balena-devices.com

// University of Banja Luka : https://unibl.org
// Domains for Republic of Srpska administrative entity.
// Submitted by Marko Ivanovic <kormang@hotmail.rs>
rs.ba

// Banzai Cloud
// Submitted by Janos Matyas <info@banzaicloud.com>
*.banzai.cloud
app.banzaicloud.io
*.backyards.banzaicloud.io

// BASE, Inc. : https://binc.jp
// Submitted by Yuya NAGASAWA <public-suffix-list@binc.jp>
base.ec
official.ec
buyshop.jp
fashionstore.jp
handcrafted.jp
kawaiishop.jp
supersale.jp
theshop.jp
shopselect.net
base.shop

// BeagleBoard.org Foundation : https://beagleboard.org
// Submitted by Jason Kridner <jkridner@beagleboard.org>
beagleboard.io

// Beget Ltd
// Submitted by Lev Nekrasov <lnekrasov@beget.com>
*.beget.app

// BetaInABox
// Submitted by Adrian <adrian@betainabox.com>
betainabox.com

// BinaryLane : http://www.binarylane.com
// Submitted by Nathan O'Sullivan <nathan@mammoth.com.au>
bnr.la

// Bitbucket : http://bitbucket.org
// Submitted by Andy Ortlieb <aortlieb@atlassian.com>
bitbucket.io

// Blackbaud, Inc. : https://www.blackbaud.com
// Submitted by Paul Crowder <paul.crowder@blackbaud.com>
blackbaudcdn.net

// Blatech : http://www.blatech.net
// Submitted by Luke Bratch <luke@bratch.co.uk>
of.je

// Blue Bite, LLC : https://bluebite.com
// Submitted by Joshua Weiss <admin.engineering@bluebite.com>
bluebite.io

// Boomla : https://boomla.com
// Submitted by Tibor Halter <thalter@boomla.com>
boomla.net

// Boutir : https://www.boutir.com
// Submitted by Eric Ng Ka Ka <ngkaka@boutir.com>
boutir.com

// Boxfuse : https://boxfuse.com
// Submitted by Axel Fontaine <axel@boxfuse.com>
boxfuse.io

// bplaced : https://www.bplaced.net/
// Submitted by Miroslav Bozic <security@bplaced.net>
square7.ch
bplaced.com
bplaced.de
square7.de
bplaced.net
square7.net

// Brendly : https://brendly.rs
// Submitted by Dusan Radovanovic <dusan.radovanovic@brendly.rs>
shop.brendly.rs

// BrowserSafetyMark
// Submitted by Dave Tharp <browsersafetymark.io@quicinc.com>
browsersafetymark.io

// Bytemark Hosting : https://www.bytemark.co.uk
// Submitted by Paul Cammish <paul.cammish@bytemark.co.uk>
uk0.bigv.io
dh.bytemark.co.uk
vm.bytemark.co.uk

// Caf.js Labs LLC : https://www.cafjs.com
// Submitted by Antonio Lain <antlai@cafjs.com>
cafjs.com

// callidomus : https://www.callidomus.com/
// Submitted by Marcus Popp <admin@callidomus.com>
mycd.eu

// Canva Pty Ltd : https://canva.com/
// Submitted by Joel Aquilina <publicsuffixlist@canva.com>
canva-apps.cn
canva-apps.com

// Carrd : https://carrd.co
// Submitted by AJ <aj@carrd.co>
drr.ac
uwu.ai
carrd.co
crd.co
ju.mp

// CentralNic : http://www.centralnic.com/names/domains
// Submitted by registry <gavin.brown@centralnic.com>
ae.org
br.com
cn.com
com.de
com.se
de.com
eu.com
gb.net
hu.net
jp.net
jpn.com
mex.com
ru.com
sa.com
se.net
uk.com
uk.net
us.com
za.bz
za.com

// No longer operated by CentralNic, these entries should be adopted and/or removed by current operators
// Submitted by Gavin Brown <gavin.brown@centralnic.com>
ar.com
hu.com
kr.com
no.com
qc.com
uy.com

// Africa.com Web Solutions Ltd : https://registry.africa.com
// Submitted by Gavin Brown <gavin.brown@centralnic.com>
africa.com

// iDOT Services Limited : http://www.domain.gr.com
// Submitted by Gavin Brown <gavin.brown@centralnic.com>
gr.com

// Radix FZC : http://domains.in.net
// Submitted by Gavin Brown <gavin.brown@centralnic.com>
in.net
web.in

// US REGISTRY LLC : http://us.org
// Submitted by Gavin Brown <gavin.brown@centralnic.com>
us.org

// co.com Registry, LLC : https://registry.co.com
// Submitted by Gavin Brown <gavin.brown@centralnic.com>
co.com

// Roar Domains LLC : https://roar.basketball/
// Submitted by Gavin Brown <gavin.brown@centralnic.com>
aus.basketball
nz.basketball

// BRS Media : https://brsmedia.com/
// Submitted by Gavin Brown <gavin.brown@centralnic.com>
radio.am
radio.fm

// c.la : http://www.c.la/
c.la

// certmgr.org : https://certmgr.org
// Submitted by B. Blechschmidt <hostmaster@certmgr.org>
certmgr.org

// Cityhost LLC  : https://cityhost.ua
// Submitted by Maksym Rivtin <support@cityhost.net.ua>
cx.ua

// Civilized Discourse Construction Kit, Inc. : https://www.discourse.org/
// Submitted by Rishabh Nambiar & Michael Brown <team@discourse.org>
discourse.group
discourse.team

// Clever Cloud : https://www.clever-cloud.com/
// Submitted by Quentin Adam <noc@clever-cloud.com>
cleverapps.io

// Clerk : https://www.clerk.dev
// Submitted by Colin Sidoti <systems@clerk.dev>
clerk.app
clerkstage.app
*.lcl.dev
*.lclstage.dev
*.stg.dev
*.stgstage.dev

// ClickRising : https://clickrising.com/
// Submitted by Umut Gumeli <infrastructure-publicsuffixlist@clickrising.com>
clickrising.net

// Cloud66 : https://www.cloud66.com/
// Submitted by Khash Sajadi <khash@cloud66.com>
c66.me
cloud66.ws
cloud66.zone

// CloudAccess.net : https://www.cloudaccess.net/
// Submitted by Pawel Panek <noc@cloudaccess.net>
jdevcloud.com
wpdevcloud.com
cloudaccess.host
freesite.host
cloudaccess.net

// cloudControl : https://www.cloudcontrol.com/
// Submitted by Tobias Wilken <tw@cloudcontrol.com>
cloudcontrolled.com
cloudcontrolapp.com

// Cloudera, Inc. : https://www.cloudera.com/
// Submitted by Kedarnath Waikar <security@cloudera.com>
*.cloudera.site

// Cloudflare, Inc. : https://www.cloudflare.com/
// Submitted by Cloudflare Team <publicsuffixlist@cloudflare.com>
cf-ipfs.com
cloudflare-ipfs.com
trycloudflare.com
pages.dev
r2.dev
workers.dev

// Clovyr : https://clovyr.io
// Submitted by Patrick Nielsen <patrick@clovyr.io>
wnext.app

// co.ca : http://registry.co.ca/
co.ca

// Co & Co : https://co-co.nl/
// Submitted by Govert Versluis <govert@co-co.nl>
*.otap.co

// i-registry s.r.o. : http://www.i-registry.cz/
// Submitted by Martin Semrad <semrad@i-registry.cz>
co.cz

// CDN77.com : http://www.cdn77.com
// Submitted by Jan Krpes <jan.krpes@cdn77.com>
c.cdn77.org
cdn77-ssl.net
r.cdn77.net
rsc.cdn77.org
ssl.origin.cdn77-secure.org

// Cloud DNS Ltd : http://www.cloudns.net
// Submitted by Aleksander Hristov <noc@cloudns.net>
cloudns.asia
cloudns.biz
cloudns.club
cloudns.cc
cloudns.eu
cloudns.in
cloudns.info
cloudns.org
cloudns.pro
cloudns.pw
cloudns.us

// CNPY : https://cnpy.gdn
// Submitted by Angelo Gladding <angelo@lahacker.net>
cnpy.gdn

// Codeberg e. V. : https://codeberg.org
// Submitted by Moritz Marquardt <git@momar.de>
codeberg.page

// CoDNS B.V.
co.nl
co.no

// Combell.com : https://www.combell.com
// Submitted by Thomas Wouters <thomas.wouters@combellgroup.com>
webhosting.be
hosting-cluster.nl

// Coordination Center for TLD RU and XN--P1AI : https://cctld.ru/en/domains/domens_ru/reserved/
// Submitted by George Georgievsky <gug@cctld.ru>
ac.ru
edu.ru
gov.ru
int.ru
mil.ru
test.ru

// COSIMO GmbH : http://www.cosimo.de
// Submitted by Rene Marticke <rmarticke@cosimo.de>
dyn.cosidns.de
dynamisches-dns.de
dnsupdater.de
internet-dns.de
l-o-g-i-n.de
dynamic-dns.info
feste-ip.net
knx-server.net
static-access.net

// Craynic, s.r.o. : http://www.craynic.com/
// Submitted by Ales Krajnik <ales.krajnik@craynic.com>
realm.cz

// Cryptonomic : https://cryptonomic.net/
// Submitted by Andrew Cady <public-suffix-list@cryptonomic.net>
*.cryptonomic.net

// Cupcake : https://cupcake.io/
// Submitted by Jonathan Rudenberg <jonathan@cupcake.io>
cupcake.is

// Curv UG : https://curv-labs.de/
// Submitted by Marvin Wiesner <Marvin@curv-labs.de>
curv.dev

// Customer OCI - Oracle Dyn https://cloud.oracle.com/home https://dyn.com/dns/
// Submitted by Gregory Drake <support@dyn.com>
// Note: This is intended to also include customer-oci.com due to wildcards implicitly including the current label
*.customer-oci.com
*.oci.customer-oci.com
*.ocp.customer-oci.com
*.ocs.customer-oci.com

// cyon GmbH : https://www.cyon.ch/
// Submitted by Dominic Luechinger <dol@cyon.ch>
cyon.link
cyon.site

// Danger Science Group: https://dangerscience.com/
// Submitted by Skylar MacDonald <skylar@dangerscience.com>
fnwk.site
folionetwork.site
platform0.app

// Daplie, Inc : https://daplie.com
// Submitted by AJ ONeal <aj@daplie.com>
daplie.me
localhost.daplie.me

// Datto, Inc. : https://www.datto.com/
// Submitted by Philipp Heckel <ph@datto.com>
dattolocal.com
dattorelay.com
dattoweb.com
mydatto.com
dattolocal.net
mydatto.net

// Dansk.net : http://www.dansk.net/
// Submitted by Anani Voule <digital@digital.co.dk>
biz.dk
co.dk
firm.dk
reg.dk
store.dk

// dappnode.io : https://dappnode.io/
// Submitted by Abel Boldu / DAppNode Team <community@dappnode.io>
dyndns.dappnode.io

// dapps.earth : https://dapps.earth/
// Submitted by Daniil Burdakov <icqkill@gmail.com>
*.dapps.earth
*.bzz.dapps.earth

// Dark, Inc. : https://darklang.com
// Submitted by Paul Biggar <ops@darklang.com>
builtwithdark.com

// DataDetect, LLC. : https://datadetect.com
// Submitted by Andrew Banchich <abanchich@sceven.com>
demo.datadetect.com
instance.datadetect.com

// Datawire, Inc : https://www.datawire.io
// Submitted by Richard Li <secalert@datawire.io>
edgestack.me

// DDNS5 : https://ddns5.com
// Submitted by Cameron Elliott <cameron@cameronelliott.com>
ddns5.com

// Debian : https://www.debian.org/
// Submitted by Peter Palfrader / Debian Sysadmin Team <dsa-publicsuffixlist@debian.org>
debian.net

// Deno Land Inc : https://deno.com/
// Submitted by Luca Casonato <hostmaster@deno.com>
deno.dev
deno-staging.dev

// deSEC : https://desec.io/
// Submitted by Peter Thomassen <peter@desec.io>
dedyn.io

// Deta: https://www.deta.sh/
// Submitted by Aavash Shrestha <aavash@deta.sh>
deta.app
deta.dev

// Diher Solutions : https://diher.solutions
// Submitted by Didi Hermawan <mail@diher.solutions>
*.rss.my.id
*.diher.solutions

// Discord Inc : https://discord.com
// Submitted by Sahn Lam <slam@discordapp.com>
discordsays.com
discordsez.com

// DNS Africa Ltd https://dns.business
// Submitted by Calvin Browne <calvin@dns.business>
jozi.biz

// DNShome : https://www.dnshome.de/
// Submitted by Norbert Auler <mail@dnshome.de>
dnshome.de

// DotArai : https://www.dotarai.com/
// Submitted by Atsadawat Netcharadsang <atsadawat@dotarai.co.th>
online.th
shop.th

// DrayTek Corp. : https://www.draytek.com/
// Submitted by Paul Fang <mis@draytek.com>
drayddns.com

// DreamCommerce : https://shoper.pl/
// Submitted by Konrad Kotarba <konrad.kotarba@dreamcommerce.com>
shoparena.pl

// DreamHost : http://www.dreamhost.com/
// Submitted by Andrew Farmer <andrew.farmer@dreamhost.com>
dreamhosters.com

// Drobo : http://www.drobo.com/
// Submitted by Ricardo Padilha <rpadilha@drobo.com>
mydrobo.com

// Drud Holdings, LLC. : https://www.drud.com/
// Submitted by Kevin Bridges <kevin@drud.com>
drud.io
drud.us

// DuckDNS : http://www.duckdns.org/
// Submitted by Richard Harper <richard@duckdns.org>
duckdns.org

// Bip : https://bip.sh
// Submitted by Joel Kennedy <joel@bip.sh>
bip.sh

// bitbridge.net : Submitted by Craig Welch, abeliidev@gmail.com
bitbridge.net

// dy.fi : http://dy.fi/
// Submitted by Heikki Hannikainen <hessu@hes.iki.fi>
dy.fi
tunk.org

// DynDNS.com : http://www.dyndns.com/services/dns/dyndns/
dyndns-at-home.com
dyndns-at-work.com
dyndns-blog.com
dyndns-free.com
dyndns-home.com
dyndns-ip.com
dyndns-mail.com
dyndns-office.com
dyndns-pics.com
dyndns-remote.com
dyndns-server.com
dyndns-web.com
dyndns-wiki.com
dyndns-work.com
dyndns.biz
dyndns.info
dyndns.org
dyndns.tv
at-band-camp.net
ath.cx
barrel-of-knowledge.info
barrell-of-knowledge.info
better-than.tv
blogdns.com
blogdns.net
blogdns.org
blogsite.org
boldlygoingnowhere.org
broke-it.net
buyshouses.net
cechire.com
dnsalias.com
dnsalias.net
dnsalias.org
dnsdojo.com
dnsdojo.net
dnsdojo.org
does-it.net
doesntexist.com
doesntexist.org
dontexist.com
dontexist.net
dontexist.org
doomdns.com
doomdns.org
dvrdns.org
dyn-o-saur.com
dynalias.com
dynalias.net
dynalias.org
dynathome.net
dyndns.ws
endofinternet.net
endofinternet.org
endoftheinternet.org
est-a-la-maison.com
est-a-la-masion.com
est-le-patron.com
est-mon-blogueur.com
for-better.biz
for-more.biz
for-our.info
for-some.biz
for-the.biz
forgot.her.name
forgot.his.name
from-ak.com
from-al.com
from-ar.com
from-az.net
from-ca.com
from-co.net
from-ct.com
from-dc.com
from-de.com
from-fl.com
from-ga.com
from-hi.com
from-ia.com
from-id.com
from-il.com
from-in.com
from-ks.com
from-ky.com
from-la.net
from-ma.com
from-md.com
from-me.org
from-mi.com
from-mn.com
from-mo.com
from-ms.com
from-mt.com
from-nc.com
from-nd.com
from-ne.com
from-nh.com
from-nj.com
from-nm.com
from-nv.com
from-ny.net
from-oh.com
from-ok.com
from-or.com
from-pa.com
from-pr.com
from-ri.com
from-sc.com
from-sd.com
from-tn.com
from-tx.com
from-ut.com
from-va.com
from-vt.com
from-wa.com
from-wi.com
from-wv.com
from-wy.com
ftpaccess.cc
fuettertdasnetz.de
game-host.org
game-server.cc
getmyip.com
gets-it.net
go.dyndns.org
gotdns.com
gotdns.org
groks-the.info
groks-this.info
ham-radio-op.net
here-for-more.info
hobby-site.com
hobby-site.org
home.dyndns.org
homedns.org
homeftp.net
homeftp.org
homeip.net
homelinux.com
homelinux.net
homelinux.org
homeunix.com
homeunix.net
homeunix.org
iamallama.com
in-the-band.net
is-a-anarchist.com
is-a-blogger.com
is-a-bookkeeper.com
is-a-bruinsfan.org
is-a-bulls-fan.com
is-a-candidate.org
is-a-caterer.com
is-a-celticsfan.org
is-a-chef.com
is-a-chef.net
is-a-chef.org
is-a-conservative.com
is-a-cpa.com
is-a-cubicle-slave.com
is-a-democrat.com
is-a-designer.com
is-a-doctor.com
is-a-financialadvisor.com
is-a-geek.com
is-a-geek.net
is-a-geek.org
is-a-green.com
is-a-guru.com
is-a-hard-worker.com
is-a-hunter.com
is-a-knight.org
is-a-landscaper.com
is-a-lawyer.com
is-a-liberal.com
is-a-libertarian.com
is-a-linux-user.org
is-a-llama.com
is-a-musician.com
is-a-nascarfan.com
is-a-nurse.com
is-a-painter.com
is-a-patsfan.org
is-a-personaltrainer.com
is-a-photographer.com
is-a-player.com
is-a-republican.com
is-a-rockstar.com
is-a-socialist.com
is-a-soxfan.org
is-a-student.com
is-a-teacher.com
is-a-techie.com
is-a-therapist.com
is-an-accountant.com
is-an-actor.com
is-an-actress.com
is-an-anarchist.com
is-an-artist.com
is-an-engineer.com
is-an-entertainer.com
is-by.us
is-certified.com
is-found.org
is-gone.com
is-into-anime.com
is-into-cars.com
is-into-cartoons.com
is-into-games.com
is-leet.com
is-lost.org
is-not-certified.com
is-saved.org
is-slick.com
is-uberleet.com
is-very-bad.org
is-very-evil.org
is-very-good.org
is-very-nice.org
is-very-sweet.org
is-with-theband.com
isa-geek.com
isa-geek.net
isa-geek.org
isa-hockeynut.com
issmarterthanyou.com
isteingeek.de
istmein.de
kicks-ass.net
kicks-ass.org
knowsitall.info
land-4-sale.us
lebtimnetz.de
leitungsen.de
likes-pie.com
likescandy.com
merseine.nu
mine.nu
misconfused.org
mypets.ws
myphotos.cc
neat-url.com
office-on-the.net
on-the-web.tv
podzone.net
podzone.org
readmyblog.org
saves-the-whales.com
scrapper-site.net
scrapping.cc
selfip.biz
selfip.com
selfip.info
selfip.net
selfip.org
sells-for-less.com
sells-for-u.com
sells-it.net
sellsyourhome.org
servebbs.com
servebbs.net
servebbs.org
serveftp.net
serveftp.org
servegame.org
shacknet.nu
simple-url.com
space-to-rent.com
stuff-4-sale.org
stuff-4-sale.us
teaches-yoga.com
thruhere.net
traeumtgerade.de
webhop.biz
webhop.info
webhop.net
webhop.org
worse-than.tv
writesthisblog.com

// ddnss.de : https://www.ddnss.de/
// Submitted by Robert Niedziela <webmaster@ddnss.de>
ddnss.de
dyn.ddnss.de
dyndns.ddnss.de
dyndns1.de
dyn-ip24.de
home-webserver.de
dyn.home-webserver.de
myhome-server.de
ddnss.org

// Definima : http://www.definima.com/
// Submitted by Maxence Bitterli <maxence@definima.com>
definima.net
definima.io

// DigitalOcean App Platform : https://www.digitalocean.com/products/app-platform/
// Submitted by Braxton Huggins <psl-maintainers@digitalocean.com>
ondigitalocean.app

// DigitalOcean Spaces : https://www.digitalocean.com/products/spaces/
// Submitted by Robin H. Johnson <psl-maintainers@digitalocean.com>
*.digitaloceanspaces.com

// dnstrace.pro : https://dnstrace.pro/
// Submitted by Chris Partridge <chris@partridge.tech>
bci.dnstrace.pro

// Dynu.com : https://www.dynu.com/
// Submitted by Sue Ye <sue@dynu.com>
ddnsfree.com
ddnsgeek.com
giize.com
gleeze.com
kozow.com
loseyourip.com
ooguy.com
theworkpc.com
casacam.net
dynu.net
accesscam.org
camdvr.org
freeddns.org
mywire.org
webredirect.org
myddns.rocks
blogsite.xyz

// dynv6 : https://dynv6.com
// Submitted by Dominik Menke <dom@digineo.de>
dynv6.net

// E4YOU spol. s.r.o. : https://e4you.cz/
// Submitted by Vladimir Dudr <info@e4you.cz>
e4.cz

// Easypanel : https://easypanel.io
// Submitted by Andrei Canta <andrei@easypanel.io>
easypanel.app
easypanel.host

// Elementor : Elementor Ltd.
// Submitted by Anton Barkan <antonb@elementor.com>
elementor.cloud
elementor.cool

// En rootâ€½ : https://en-root.org
// Submitted by Emmanuel Raviart <emmanuel@raviart.com>
en-root.fr

// Enalean SAS: https://www.enalean.com
// Submitted by Thomas Cottier <thomas.cottier@enalean.com>
mytuleap.com
tuleap-partners.com

// Encoretivity AB: https://encore.dev
// Submitted by AndrÃ© Eriksson <andre@encore.dev>
encr.app
encoreapi.com

// ECG Robotics, Inc: https://ecgrobotics.org
// Submitted by <frc1533@ecgrobotics.org>
onred.one
staging.onred.one

// encoway GmbH : https://www.encoway.de
// Submitted by Marcel Daus <cloudops@encoway.de>
eu.encoway.cloud

// EU.org https://eu.org/
// Submitted by Pierre Beyssac <hostmaster@eu.org>
eu.org
al.eu.org
asso.eu.org
at.eu.org
au.eu.org
be.eu.org
bg.eu.org
ca.eu.org
cd.eu.org
ch.eu.org
cn.eu.org
cy.eu.org
cz.eu.org
de.eu.org
dk.eu.org
edu.eu.org
ee.eu.org
es.eu.org
fi.eu.org
fr.eu.org
gr.eu.org
hr.eu.org
hu.eu.org
ie.eu.org
il.eu.org
in.eu.org
int.eu.org
is.eu.org
it.eu.org
jp.eu.org
kr.eu.org
lt.eu.org
lu.eu.org
lv.eu.org
mc.eu.org
me.eu.org
mk.eu.org
mt.eu.org
my.eu.org
net.eu.org
ng.eu.org
nl.eu.org
no.eu.org
nz.eu.org
paris.eu.org
pl.eu.org
pt.eu.org
q-a.eu.org
ro.eu.org
ru.eu.org
se.eu.org
si.eu.org
sk.eu.org
tr.eu.org
uk.eu.org
us.eu.org

// Eurobyte : https://eurobyte.ru
// Submitted by Evgeniy Subbotin <e.subbotin@eurobyte.ru>
eurodir.ru

// Evennode : http://www.evennode.com/
// Submitted by Michal Kralik <support@evennode.com>
eu-1.evennode.com
eu-2.evennode.com
eu-3.evennode.com
eu-4.evennode.com
us-1.evennode.com
us-2.evennode.com
us-3.evennode.com
us-4.evennode.com

// eDirect Corp. : https://hosting.url.com.tw/
// Submitted by C.S. chang <cschang@corp.url.com.tw>
twmail.cc
twmail.net
twmail.org
mymailer.com.tw
url.tw

// Fabrica Technologies, Inc. : https://www.fabrica.dev/
// Submitted by Eric Jiang <eric@fabrica.dev>
onfabrica.com

// Facebook, Inc.
// Submitted by Peter Ruibal <public-suffix@fb.com>
apps.fbsbx.com

// FAITID : https://faitid.org/
// Submitted by Maxim Alzoba <tech.contact@faitid.org>
// https://www.flexireg.net/stat_info
ru.net
adygeya.ru
bashkiria.ru
bir.ru
cbg.ru
com.ru
dagestan.ru
grozny.ru
kalmykia.ru
kustanai.ru
marine.ru
mordovia.ru
msk.ru
mytis.ru
nalchik.ru
nov.ru
pyatigorsk.ru
spb.ru
vladikavkaz.ru
vladimir.ru
abkhazia.su
adygeya.su
aktyubinsk.su
arkhangelsk.su
armenia.su
ashgabad.su
azerbaijan.su
balashov.su
bashkiria.su
bryansk.su
bukhara.su
chimkent.su
dagestan.su
east-kazakhstan.su
exnet.su
georgia.su
grozny.su
ivanovo.su
jambyl.su
kalmykia.su
kaluga.su
karacol.su
karaganda.su
karelia.su
khakassia.su
krasnodar.su
kurgan.su
kustanai.su
lenug.su
mangyshlak.su
mordovia.su
msk.su
murmansk.su
nalchik.su
navoi.su
north-kazakhstan.su
nov.su
obninsk.su
penza.su
pokrovsk.su
sochi.su
spb.su
tashkent.su
termez.su
togliatti.su
troitsk.su
tselinograd.su
tula.su
tuva.su
vladikavkaz.su
vladimir.su
vologda.su

// Fancy Bits, LLC : http://getchannels.com
// Submitted by Aman Gupta <aman@getchannels.com>
channelsdvr.net
u.channelsdvr.net

// Fastly Inc. : http://www.fastly.com/
// Submitted by Fastly Security <security@fastly.com>
edgecompute.app
fastly-edge.com
fastly-terrarium.com
fastlylb.net
map.fastlylb.net
freetls.fastly.net
map.fastly.net
a.prod.fastly.net
global.prod.fastly.net
a.ssl.fastly.net
b.ssl.fastly.net
global.ssl.fastly.net

// Fastmail : https://www.fastmail.com/
// Submitted by Marc Bradshaw <marc@fastmailteam.com>
*.user.fm

// FASTVPS EESTI OU : https://fastvps.ru/
// Submitted by Likhachev Vasiliy <lihachev@fastvps.ru>
fastvps-server.com
fastvps.host
myfast.host
fastvps.site
myfast.space

// Fedora : https://fedoraproject.org/
// submitted by Patrick Uiterwijk <puiterwijk@fedoraproject.org>
fedorainfracloud.org
fedorapeople.org
cloud.fedoraproject.org
app.os.fedoraproject.org
app.os.stg.fedoraproject.org

// FearWorks Media Ltd. : https://fearworksmedia.co.uk
// submitted by Keith Fairley <domains@fearworksmedia.co.uk>
conn.uk
copro.uk
hosp.uk

// Fermax : https://fermax.com/
// submitted by Koen Van Isterdael <k.vanisterdael@fermax.be>
mydobiss.com

// FH Muenster : https://www.fh-muenster.de
// Submitted by Robin Naundorf <r.naundorf@fh-muenster.de>
fh-muenster.io

// Filegear Inc. : https://www.filegear.com
// Submitted by Jason Zhu <jason@owtware.com>
filegear.me
filegear-au.me
filegear-de.me
filegear-gb.me
filegear-ie.me
filegear-jp.me
filegear-sg.me

// Firebase, Inc.
// Submitted by Chris Raynor <chris@firebase.com>
firebaseapp.com

// Firewebkit : https://www.firewebkit.com
// Submitted by Majid Qureshi <mqureshi@amrayn.com>
fireweb.app

// FLAP : https://www.flap.cloud
// Submitted by Louis Chemineau <louis@chmn.me>
flap.id

// FlashDrive : https://flashdrive.io
// Submitted by Eric Chan <support@flashdrive.io>
onflashdrive.app
fldrv.com

// fly.io: https://fly.io
// Submitted by Kurt Mackey <kurt@fly.io>
fly.dev
edgeapp.net
shw.io

// Flynn : https://flynn.io
// Submitted by Jonathan Rudenberg <jonathan@flynn.io>
flynnhosting.net

// Forgerock : https://www.forgerock.com
// Submitted by Roderick Parr <roderick.parr@forgerock.com>
forgeblocks.com
id.forgerock.io

// Framer : https://www.framer.com
// Submitted by Koen Rouwhorst <koenrh@framer.com>
framer.app
framercanvas.com
framer.media
framer.photos
framer.website
framer.wiki

// Frusky MEDIA&PR : https://www.frusky.de
// Submitted by Victor Pupynin <hallo@frusky.de>
*.frusky.de

// RavPage : https://www.ravpage.co.il
// Submitted by Roni Horowitz <roni@responder.co.il>
ravpage.co.il

// Frederik Braun https://frederik-braun.com
// Submitted by Frederik Braun <fb@frederik-braun.com>
0e.vc

// Freebox : http://www.freebox.fr
// Submitted by Romain Fliedel <rfliedel@freebox.fr>
freebox-os.com
freeboxos.com
fbx-os.fr
fbxos.fr
freebox-os.fr
freeboxos.fr

// freedesktop.org : https://www.freedesktop.org
// Submitted by Daniel Stone <daniel@fooishbar.org>
freedesktop.org

// freemyip.com : https://freemyip.com
// Submitted by Cadence <contact@freemyip.com>
freemyip.com

// FunkFeuer - Verein zur FÃ¶rderung freier Netze : https://www.funkfeuer.at
// Submitted by Daniel A. Maierhofer <vorstand@funkfeuer.at>
wien.funkfeuer.at

// Futureweb OG : http://www.futureweb.at
// Submitted by Andreas Schnederle-Wagner <schnederle@futureweb.at>
*.futurecms.at
*.ex.futurecms.at
*.in.futurecms.at
futurehosting.at
futuremailing.at
*.ex.ortsinfo.at
*.kunden.ortsinfo.at
*.statics.cloud

// GDS : https://www.gov.uk/service-manual/technology/managing-domain-names
// Submitted by Stephen Ford <hostmaster@digital.cabinet-office.gov.uk>
independent-commission.uk
independent-inquest.uk
independent-inquiry.uk
independent-panel.uk
independent-review.uk
public-inquiry.uk
royal-commission.uk
campaign.gov.uk
service.gov.uk

// CDDO : https://www.gov.uk/guidance/get-an-api-domain-on-govuk
// Submitted by Jamie Tanna <jamie.tanna@digital.cabinet-office.gov.uk>
api.gov.uk

// Gehirn Inc. : https://www.gehirn.co.jp/
// Submitted by Kohei YOSHIDA <tech@gehirn.co.jp>
gehirn.ne.jp
usercontent.jp

// Gentlent, Inc. : https://www.gentlent.com
// Submitted by Tom Klein <tom@gentlent.com>
gentapps.com
gentlentapis.com
lab.ms
cdn-edges.net

// Ghost Foundation : https://ghost.org
// Submitted by Matt Hanley <security@ghost.org>
ghost.io

// GignoSystemJapan: http://gsj.bz
// Submitted by GignoSystemJapan <kakutou-ec@gsj.bz>
gsj.bz

// GitHub, Inc.
// Submitted by Patrick Toomey <security@github.com>
githubusercontent.com
githubpreview.dev
github.io

// GitLab, Inc.
// Submitted by Alex Hanselka <alex@gitlab.com>
gitlab.io

// Gitplac.si - https://gitplac.si
// Submitted by AljaÅ¾ Starc <me@aljaxus.eu>
gitapp.si
gitpage.si

// Glitch, Inc : https://glitch.com
// Submitted by Mads Hartmann <mads@glitch.com>
glitch.me

// Global NOG Alliance : https://nogalliance.org/
// Submitted by Sander Steffann <sander@nogalliance.org>
nog.community

// Globe Hosting SRL : https://www.globehosting.com/
// Submitted by Gavin Brown <gavin.brown@centralnic.com>
co.ro
shop.ro

// GMO Pepabo, Inc. : https://pepabo.com/
// Submitted by Hosting Div <admin@pepabo.com>
lolipop.io
angry.jp
babyblue.jp
babymilk.jp
backdrop.jp
bambina.jp
bitter.jp
blush.jp
boo.jp
boy.jp
boyfriend.jp
but.jp
candypop.jp
capoo.jp
catfood.jp
cheap.jp
chicappa.jp
chillout.jp
chips.jp
chowder.jp
chu.jp
ciao.jp
cocotte.jp
coolblog.jp
cranky.jp
cutegirl.jp
daa.jp
deca.jp
deci.jp
digick.jp
egoism.jp
fakefur.jp
fem.jp
flier.jp
floppy.jp
fool.jp
frenchkiss.jp
girlfriend.jp
girly.jp
gloomy.jp
gonna.jp
greater.jp
hacca.jp
heavy.jp
her.jp
hiho.jp
hippy.jp
holy.jp
hungry.jp
icurus.jp
itigo.jp
jellybean.jp
kikirara.jp
kill.jp
kilo.jp
kuron.jp
littlestar.jp
lolipopmc.jp
lolitapunk.jp
lomo.jp
lovepop.jp
lovesick.jp
main.jp
mods.jp
mond.jp
mongolian.jp
moo.jp
namaste.jp
nikita.jp
nobushi.jp
noor.jp
oops.jp
parallel.jp
parasite.jp
pecori.jp
peewee.jp
penne.jp
pepper.jp
perma.jp
pigboat.jp
pinoko.jp
punyu.jp
pupu.jp
pussycat.jp
pya.jp
raindrop.jp
readymade.jp
sadist.jp
schoolbus.jp
secret.jp
staba.jp
stripper.jp
sub.jp
sunnyday.jp
thick.jp
tonkotsu.jp
under.jp
upper.jp
velvet.jp
verse.jp
versus.jp
vivian.jp
watson.jp
weblike.jp
whitesnow.jp
zombie.jp
heteml.net

// GOV.UK Platform as a Service : https://www.cloud.service.gov.uk/
// Submitted by Tom Whitwell <gov-uk-paas-support@digital.cabinet-office.gov.uk>
cloudapps.digital
london.cloudapps.digital

// GOV.UK Pay : https://www.payments.service.gov.uk/
// Submitted by Richard Baker <richard.baker@digital.cabinet-office.gov.uk>
pymnt.uk

// UKHomeOffice : https://www.gov.uk/government/organisations/home-office
// Submitted by Jon Shanks <jon.shanks@digital.homeoffice.gov.uk>
homeoffice.gov.uk

// GlobeHosting, Inc.
// Submitted by Zoltan Egresi <egresi@globehosting.com>
ro.im

// GoIP DNS Services : http://www.goip.de
// Submitted by Christian Poulter <milchstrasse@goip.de>
goip.de

// Google, Inc.
// Submitted by Eduardo Vela <evn@google.com>
run.app
a.run.app
web.app
*.0emm.com
appspot.com
*.r.appspot.com
codespot.com
googleapis.com
googlecode.com
pagespeedmobilizer.com
publishproxy.com
withgoogle.com
withyoutube.com
*.gateway.dev
cloud.goog
translate.goog
*.usercontent.goog
cloudfunctions.net
blogspot.ae
blogspot.al
blogspot.am
blogspot.ba
blogspot.be
blogspot.bg
blogspot.bj
blogspot.ca
blogspot.cf
blogspot.ch
blogspot.cl
blogspot.co.at
blogspot.co.id
blogspot.co.il
blogspot.co.ke
blogspot.co.nz
blogspot.co.uk
blogspot.co.za
blogspot.com
blogspot.com.ar
blogspot.com.au
blogspot.com.br
blogspot.com.by
blogspot.com.co
blogspot.com.cy
blogspot.com.ee
blogspot.com.eg
blogspot.com.es
blogspot.com.mt
blogspot.com.ng
blogspot.com.tr
blogspot.com.uy
blogspot.cv
blogspot.cz
blogspot.de
blogspot.dk
blogspot.fi
blogspot.fr
blogspot.gr
blogspot.hk
blogspot.hr
blogspot.hu
blogspot.ie
blogspot.in
blogspot.is
blogspot.it
blogspot.jp
blogspot.kr
blogspot.li
blogspot.lt
blogspot.lu
blogspot.md
blogspot.mk
blogspot.mr
blogspot.mx
blogspot.my
blogspot.nl
blogspot.no
blogspot.pe
blogspot.pt
blogspot.qa
blogspot.re
blogspot.ro
blogspot.rs
blogspot.ru
blogspot.se
blogspot.sg
blogspot.si
blogspot.sk
blogspot.sn
blogspot.td
blogspot.tw
blogspot.ug
blogspot.vn

// Goupile : https://goupile.fr
// Submitted by Niels Martignene <hello@goupile.fr>
goupile.fr

// Government of the Netherlands: https://www.government.nl
// Submitted by <domeinnaam@minaz.nl>
gov.nl

// Group 53, LLC : https://www.group53.com
// Submitted by Tyler Todd <noc@nova53.net>
awsmppl.com

// GÃ¼nstigBestellen : https://gÃ¼nstigbestellen.de
// Submitted by Furkan Akkoc <info@hendelzon.de>
gÃ¼nstigbestellen.de
gÃ¼nstigliefern.de

// Hakaran group: http://hakaran.cz
// Submitted by Arseniy Sokolov <security@hakaran.cz>
fin.ci
free.hr
caa.li
ua.rs
conf.se

// Handshake : https://handshake.org
// Submitted by Mike Damm <md@md.vc>
hs.zone
hs.run

// Hashbang : https://hashbang.sh
hashbang.sh

// Hasura : https://hasura.io
// Submitted by Shahidh K Muhammed <shahidh@hasura.io>
hasura.app
hasura-app.io

// Heilbronn University of Applied Sciences - Faculty Informatics (GitLab Pages): https://www.hs-heilbronn.de
// Submitted by Richard Zowalla <mi-admin@hs-heilbronn.de>
pages.it.hs-heilbronn.de

// Hepforge : https://www.hepforge.org
// Submitted by David Grellscheid <admin@hepforge.org>
hepforge.org

// Heroku : https://www.heroku.com/
// Submitted by Tom Maher <tmaher@heroku.com>
herokuapp.com
herokussl.com

// Hibernating Rhinos
// Submitted by Oren Eini <oren@ravendb.net>
ravendb.cloud
ravendb.community
ravendb.me
development.run
ravendb.run

// home.pl S.A.: https://home.pl
// Submitted by Krzysztof Wolski <krzysztof.wolski@home.eu>
homesklep.pl

// Hong Kong Productivity Council: https://www.hkpc.org/
// Submitted by SECaaS Team <summchan@hkpc.org>
secaas.hk

// Hoplix : https://www.hoplix.com
// Submitted by Danilo De Franco<info@hoplix.shop>
hoplix.shop


// HOSTBIP REGISTRY : https://www.hostbip.com/
// Submitted by Atanunu Igbunuroghene <publicsuffixlist@hostbip.com>
orx.biz
biz.gl
col.ng
firm.ng
gen.ng
ltd.ng
ngo.ng
edu.scot
sch.so

// HostFly : https://www.ie.ua
// Submitted by Bohdan Dub <support@hostfly.com.ua>
ie.ua

// HostyHosting (hostyhosting.com)
hostyhosting.io

// HÃ¤kkinen.fi
// Submitted by Eero HÃ¤kkinen <Eero+psl@HÃ¤kkinen.fi>
hÃ¤kkinen.fi

// Ici la Lune : http://www.icilalune.com/
// Submitted by Simon Morvan <simon@icilalune.com>
*.moonscale.io
moonscale.net

// iki.fi
// Submitted by Hannu Aronsson <haa@iki.fi>
iki.fi

// iliad italia: https://www.iliad.it
// Submitted by Marios Makassikis <mmakassikis@freebox.fr>
ibxos.it
iliadboxos.it

// Impertrix Solutions : <https://impertrixcdn.com>
// Submitted by Zhixiang Zhao <csuite@impertrix.com>
impertrixcdn.com
impertrix.com

// Incsub, LLC: https://incsub.com/
// Submitted by Aaron Edwards <sysadmins@incsub.com>
smushcdn.com
wphostedmail.com
wpmucdn.com
tempurl.host
wpmudev.host

// Individual Network Berlin e.V. : https://www.in-berlin.de/
// Submitted by Christian Seitz <chris@in-berlin.de>
dyn-berlin.de
in-berlin.de
in-brb.de
in-butter.de
in-dsl.de
in-dsl.net
in-dsl.org
in-vpn.de
in-vpn.net
in-vpn.org

// info.at : http://www.info.at/
biz.at
info.at

// info.cx : http://info.cx
// Submitted by Jacob Slater <whois@igloo.to>
info.cx

// Interlegis : http://www.interlegis.leg.br
// Submitted by Gabriel Ferreira <registrobr@interlegis.leg.br>
ac.leg.br
al.leg.br
am.leg.br
ap.leg.br
ba.leg.br
ce.leg.br
df.leg.br
es.leg.br
go.leg.br
ma.leg.br
mg.leg.br
ms.leg.br
mt.leg.br
pa.leg.br
pb.leg.br
pe.leg.br
pi.leg.br
pr.leg.br
rj.leg.br
rn.leg.br
ro.leg.br
rr.leg.br
rs.leg.br
sc.leg.br
se.leg.br
sp.leg.br
to.leg.br

// intermetrics GmbH : https://pixolino.com/
// Submitted by Wolfgang Schwarz <admin@intermetrics.de>
pixolino.com

// Internet-Pro, LLP: https://netangels.ru/
// Submitted by Vasiliy Sheredeko <piphon@gmail.com>
na4u.ru

// iopsys software solutions AB : https://iopsys.eu/
// Submitted by Roman Azarenko <roman.azarenko@iopsys.eu>
iopsys.se

// IPiFony Systems, Inc. : https://www.ipifony.com/
// Submitted by Matthew Hardeman <mhardeman@ipifony.com>
ipifony.net

// IServ GmbH : https://iserv.de
// Submitted by Mario Hoberg <info@iserv.de>
iservschule.de
mein-iserv.de
schulplattform.de
schulserver.de
test-iserv.de
iserv.dev

// I-O DATA DEVICE, INC. : http://www.iodata.com/
// Submitted by Yuji Minagawa <domains-admin@iodata.jp>
iobb.net

// Jelastic, Inc. : https://jelastic.com/
// Submitted by Ihor Kolodyuk <ik@jelastic.com>
mel.cloudlets.com.au
cloud.interhostsolutions.be
mycloud.by
alp1.ae.flow.ch
appengine.flow.ch
es-1.axarnet.cloud
diadem.cloud
vip.jelastic.cloud
jele.cloud
it1.eur.aruba.jenv-aruba.cloud
it1.jenv-aruba.cloud
keliweb.cloud
cs.keliweb.cloud
oxa.cloud
tn.oxa.cloud
uk.oxa.cloud
primetel.cloud
uk.primetel.cloud
ca.reclaim.cloud
uk.reclaim.cloud
us.reclaim.cloud
ch.trendhosting.cloud
de.trendhosting.cloud
jele.club
amscompute.com
dopaas.com
paas.hosted-by-previder.com
rag-cloud.hosteur.com
rag-cloud-ch.hosteur.com
jcloud.ik-server.com
jcloud-ver-jpc.ik-server.com
demo.jelastic.com
kilatiron.com
paas.massivegrid.com
jed.wafaicloud.com
lon.wafaicloud.com
ryd.wafaicloud.com
j.scaleforce.com.cy
jelastic.dogado.eu
fi.cloudplatform.fi
demo.datacenter.fi
paas.datacenter.fi
jele.host
mircloud.host
paas.beebyte.io
sekd1.beebyteapp.io
jele.io
cloud-fr1.unispace.io
jc.neen.it
cloud.jelastic.open.tim.it
jcloud.kz
upaas.kazteleport.kz
cloudjiffy.net
fra1-de.cloudjiffy.net
west1-us.cloudjiffy.net
jls-sto1.elastx.net
jls-sto2.elastx.net
jls-sto3.elastx.net
faststacks.net
fr-1.paas.massivegrid.net
lon-1.paas.massivegrid.net
lon-2.paas.massivegrid.net
ny-1.paas.massivegrid.net
ny-2.paas.massivegrid.net
sg-1.paas.massivegrid.net
jelastic.saveincloud.net
nordeste-idc.saveincloud.net
j.scaleforce.net
jelastic.tsukaeru.net
sdscloud.pl
unicloud.pl
mircloud.ru
jelastic.regruhosting.ru
enscaled.sg
jele.site
jelastic.team
orangecloud.tn
j.layershift.co.uk
phx.enscaled.us
mircloud.us

// Jino : https://www.jino.ru
// Submitted by Sergey Ulyashin <ulyashin@jino.ru>
myjino.ru
*.hosting.myjino.ru
*.landing.myjino.ru
*.spectrum.myjino.ru
*.vps.myjino.ru

// Jotelulu S.L. : https://jotelulu.com
// Submitted by Daniel FariÃ±a <ingenieria@jotelulu.com>
jotelulu.cloud

// Joyent : https://www.joyent.com/
// Submitted by Brian Bennett <brian.bennett@joyent.com>
*.triton.zone
*.cns.joyent.com

// JS.ORG : http://dns.js.org
// Submitted by Stefan Keim <admin@js.org>
js.org

// KaasHosting : http://www.kaashosting.nl/
// Submitted by Wouter Bakker <hostmaster@kaashosting.nl>
kaas.gg
khplay.nl

// Kakao : https://www.kakaocorp.com/
// Submitted by JaeYoong Lee <cec@kakaocorp.com>
ktistory.com

// Kapsi : https://kapsi.fi
// Submitted by Tomi Juntunen <erani@kapsi.fi>
kapsi.fi

// Keyweb AG : https://www.keyweb.de
// Submitted by Martin Dannehl <postmaster@keymachine.de>
keymachine.de

// KingHost : https://king.host
// Submitted by Felipe Keller Braz <felipebraz@kinghost.com.br>
kinghost.net
uni5.net

// KnightPoint Systems, LLC : http://www.knightpoint.com/
// Submitted by Roy Keene <rkeene@knightpoint.com>
knightpoint.systems

// KoobinEvent, SL: https://www.koobin.com
// Submitted by IvÃ¡n Oliva <ivan.oliva@koobin.com>
koobin.events

// KUROKU LTD : https://kuroku.ltd/
// Submitted by DisposaBoy <security@oya.to>
oya.to

// Katholieke Universiteit Leuven: https://www.kuleuven.be
// Submitted by Abuse KU Leuven <abuse@kuleuven.be>
kuleuven.cloud
ezproxy.kuleuven.be

// .KRD : http://nic.krd/data/krd/Registration%20Policy.pdf
co.krd
edu.krd

// Krellian Ltd. : https://krellian.com
// Submitted by Ben Francis <ben@krellian.com>
krellian.net
webthings.io

// LCube - Professional hosting e.K. : https://www.lcube-webhosting.de
// Submitted by Lars Laehn <info@lcube.de>
git-repos.de
lcube-server.de
svn-repos.de

// Leadpages : https://www.leadpages.net
// Submitted by Greg Dallavalle <domains@leadpages.net>
leadpages.co
lpages.co
lpusercontent.com

// Lelux.fi : https://lelux.fi/
// Submitted by Lelux Admin <publisuffix@lelux.site>
lelux.site

// Lifetime Hosting : https://Lifetime.Hosting/
// Submitted by Mike Fillator <support@lifetime.hosting>
co.business
co.education
co.events
co.financial
co.network
co.place
co.technology

// Lightmaker Property Manager, Inc. : https://app.lmpm.com/
// Submitted by Greg Holland <greg.holland@lmpm.com>
app.lmpm.com

// linkyard ldt: https://www.linkyard.ch/
// Submitted by Mario Siegenthaler <mario.siegenthaler@linkyard.ch>
linkyard.cloud
linkyard-cloud.ch

// Linode : https://linode.com
// Submitted by <security@linode.com>
members.linode.com
*.nodebalancer.linode.com
*.linodeobjects.com
ip.linodeusercontent.com

// LiquidNet Ltd : http://www.liquidnetlimited.com/
// Submitted by Victor Velchev <admin@liquidnetlimited.com>
we.bs

// Localcert : https://localcert.dev
// Submitted by Lann Martin <security@localcert.dev>
*.user.localcert.dev

// localzone.xyz
// Submitted by Kenny Niehage <hello@yahe.sh>
localzone.xyz

// Log'in Line : https://www.loginline.com/
// Submitted by RÃ©mi Mach <remi.mach@loginline.com>
loginline.app
loginline.dev
loginline.io
loginline.services
loginline.site

// Lokalized : https://lokalized.nl
// Submitted by Noah Taheij <noah@lokalized.nl>
servers.run

// LÃµhmus Family, The
// Submitted by Heiki LÃµhmus <hostmaster at lohmus dot me>
lohmus.me

// LubMAN UMCS Sp. z o.o : https://lubman.pl/
// Submitted by Ireneusz Maliszewski <ireneusz.maliszewski@lubman.pl>
krasnik.pl
leczna.pl
lubartow.pl
lublin.pl
poniatowa.pl
swidnik.pl

// Lug.org.uk : https://lug.org.uk
// Submitted by Jon Spriggs <admin@lug.org.uk>
glug.org.uk
lug.org.uk
lugs.org.uk

// Lukanet Ltd : https://lukanet.com
// Submitted by Anton Avramov <register@lukanet.com>
barsy.bg
barsy.co.uk
barsyonline.co.uk
barsycenter.com
barsyonline.com
barsy.club
barsy.de
barsy.eu
barsy.in
barsy.info
barsy.io
barsy.me
barsy.menu
barsy.mobi
barsy.net
barsy.online
barsy.org
barsy.pro
barsy.pub
barsy.ro
barsy.shop
barsy.site
barsy.support
barsy.uk

// Magento Commerce
// Submitted by Damien Tournoud <dtournoud@magento.cloud>
*.magentosite.cloud

// May First - People Link : https://mayfirst.org/
// Submitted by Jamie McClelland <info@mayfirst.org>
mayfirst.info
mayfirst.org

// Mail.Ru Group : https://hb.cldmail.ru
// Submitted by Ilya Zaretskiy <zaretskiy@corp.mail.ru>
hb.cldmail.ru

// Mail Transfer Platform : https://www.neupeer.com
// Submitted by Li Hui <lihui@neupeer.com>
cn.vu

// Maze Play: https://www.mazeplay.com
// Submitted by Adam Humpherys <adam@mws.dev>
mazeplay.com

// mcpe.me : https://mcpe.me
// Submitted by Noa Heyl <hi@noa.dev>
mcpe.me

// McHost : https://mchost.ru
// Submitted by Evgeniy Subbotin <e.subbotin@mchost.ru>
mcdir.me
mcdir.ru
mcpre.ru
vps.mcdir.ru

// Mediatech : https://mediatech.by
// Submitted by Evgeniy Kozhuhovskiy <ugenk@mediatech.by>
mediatech.by
mediatech.dev

// Medicom Health : https://medicomhealth.com
// Submitted by Michael Olson <molson@medicomhealth.com>
hra.health

// Memset hosting : https://www.memset.com
// Submitted by Tom Whitwell <domains@memset.com>
miniserver.com
memset.net

// Messerli Informatik AG : https://www.messerli.ch/
// Submitted by Ruben Schmidmeister <psl-maintainers@messerli.ch>
messerli.app

// MetaCentrum, CESNET z.s.p.o. : https://www.metacentrum.cz/en/
// Submitted by ZdenÄ›k Å ustr <zdenek.sustr@cesnet.cz>
*.cloud.metacentrum.cz
custom.metacentrum.cz

// MetaCentrum, CESNET z.s.p.o. : https://www.metacentrum.cz/en/
// Submitted by Radim JanÄ�a <janca@cesnet.cz>
flt.cloud.muni.cz
usr.cloud.muni.cz

// Meteor Development Group : https://www.meteor.com/hosting
// Submitted by Pierre Carrier <pierre@meteor.com>
meteorapp.com
eu.meteorapp.com

// Michau Enterprises Limited : http://www.co.pl/
co.pl

// Microsoft Corporation : http://microsoft.com
// Submitted by Public Suffix List Admin <msftpsladmin@microsoft.com>
*.azurecontainer.io
azurewebsites.net
azure-mobile.net
cloudapp.net
azurestaticapps.net
1.azurestaticapps.net
2.azurestaticapps.net
3.azurestaticapps.net
centralus.azurestaticapps.net
eastasia.azurestaticapps.net
eastus2.azurestaticapps.net
westeurope.azurestaticapps.net
westus2.azurestaticapps.net

// minion.systems : http://minion.systems
// Submitted by Robert BÃ¶ttinger <r@minion.systems>
csx.cc

// Mintere : https://mintere.com/
// Submitted by Ben Aubin <security@mintere.com>
mintere.site

// MobileEducation, LLC : https://joinforte.com
// Submitted by Grayson Martin <grayson.martin@mobileeducation.us>
forte.id

// Mozilla Corporation : https://mozilla.com
// Submitted by Ben Francis <bfrancis@mozilla.com>
mozilla-iot.org

// Mozilla Foundation : https://mozilla.org/
// Submitted by glob <glob@mozilla.com>
bmoattachments.org

// MSK-IX : https://www.msk-ix.ru/
// Submitted by Khannanov Roman <r.khannanov@msk-ix.ru>
net.ru
org.ru
pp.ru

// Mythic Beasts : https://www.mythic-beasts.com
// Submitted by Paul Cammish <kelduum@mythic-beasts.com>
hostedpi.com
customer.mythic-beasts.com
caracal.mythic-beasts.com
fentiger.mythic-beasts.com
lynx.mythic-beasts.com
ocelot.mythic-beasts.com
oncilla.mythic-beasts.com
onza.mythic-beasts.com
sphinx.mythic-beasts.com
vs.mythic-beasts.com
x.mythic-beasts.com
yali.mythic-beasts.com
cust.retrosnub.co.uk

// Nabu Casa : https://www.nabucasa.com
// Submitted by Paulus Schoutsen <infra@nabucasa.com>
ui.nabu.casa

// Net at Work Gmbh : https://www.netatwork.de
// Submitted by Jan Jaeschke <jan.jaeschke@netatwork.de>
cloud.nospamproxy.com

// Netlify : https://www.netlify.com
// Submitted by Jessica Parsons <jessica@netlify.com>
netlify.app

// Neustar Inc.
// Submitted by Trung Tran <Trung.Tran@neustar.biz>
4u.com

// ngrok : https://ngrok.com/
// Submitted by Alan Shreve <alan@ngrok.com>
ngrok.app
ngrok-free.app
ngrok.dev
ngrok-free.dev
ngrok.io
ap.ngrok.io
au.ngrok.io
eu.ngrok.io
in.ngrok.io
jp.ngrok.io
sa.ngrok.io
us.ngrok.io
ngrok.pizza

// Nimbus Hosting Ltd. : https://www.nimbushosting.co.uk/
// Submitted by Nicholas Ford <nick@nimbushosting.co.uk>
nh-serv.co.uk

// NFSN, Inc. : https://www.NearlyFreeSpeech.NET/
// Submitted by Jeff Wheelhouse <support@nearlyfreespeech.net>
nfshost.com

// Noop : https://noop.app
// Submitted by Nathaniel Schweinberg <noop@rearc.io>
*.developer.app
noop.app

// Northflank Ltd. : https://northflank.com/
// Submitted by Marco Suter <marco@northflank.com>
*.northflank.app
*.build.run
*.code.run
*.database.run
*.migration.run

// Noticeable : https://noticeable.io
// Submitted by Laurent Pellegrino <security@noticeable.io>
noticeable.news

// Now-DNS : https://now-dns.com
// Submitted by Steve Russell <steve@now-dns.com>
dnsking.ch
mypi.co
n4t.co
001www.com
ddnslive.com
myiphost.com
forumz.info
16-b.it
32-b.it
64-b.it
soundcast.me
tcp4.me
dnsup.net
hicam.net
now-dns.net
ownip.net
vpndns.net
dynserv.org
now-dns.org
x443.pw
now-dns.top
ntdll.top
freeddns.us
crafting.xyz
zapto.xyz

// nsupdate.info : https://www.nsupdate.info/
// Submitted by Thomas Waldmann <info@nsupdate.info>
nsupdate.info
nerdpol.ovh

// No-IP.com : https://noip.com/
// Submitted by Deven Reza <publicsuffixlist@noip.com>
blogsyte.com
brasilia.me
cable-modem.org
ciscofreak.com
collegefan.org
couchpotatofries.org
damnserver.com
ddns.me
ditchyourip.com
dnsfor.me
dnsiskinky.com
dvrcam.info
dynns.com
eating-organic.net
fantasyleague.cc
geekgalaxy.com
golffan.us
health-carereform.com
homesecuritymac.com
homesecuritypc.com
hopto.me
ilovecollege.info
loginto.me
mlbfan.org
mmafan.biz
myactivedirectory.com
mydissent.net
myeffect.net
mymediapc.net
mypsx.net
mysecuritycamera.com
mysecuritycamera.net
mysecuritycamera.org
net-freaks.com
nflfan.org
nhlfan.net
no-ip.ca
no-ip.co.uk
no-ip.net
noip.us
onthewifi.com
pgafan.net
point2this.com
pointto.us
privatizehealthinsurance.net
quicksytes.com
read-books.org
securitytactics.com
serveexchange.com
servehumour.com
servep2p.com
servesarcasm.com
stufftoread.com
ufcfan.org
unusualperson.com
workisboring.com
3utilities.com
bounceme.net
ddns.net
ddnsking.com
gotdns.ch
hopto.org
myftp.biz
myftp.org
myvnc.com
no-ip.biz
no-ip.info
no-ip.org
noip.me
redirectme.net
servebeer.com
serveblog.net
servecounterstrike.com
serveftp.com
servegame.com
servehalflife.com
servehttp.com
serveirc.com
serveminecraft.net
servemp3.com
servepics.com
servequake.com
sytes.net
webhop.me
zapto.org

// NodeArt : https://nodeart.io
// Submitted by Konstantin Nosov <Nosov@nodeart.io>
stage.nodeart.io

// Nucleos Inc. : https://nucleos.com
// Submitted by Piotr Zduniak <piotr@nucleos.com>
pcloud.host

// NYC.mn : http://www.information.nyc.mn
// Submitted by Matthew Brown <mattbrown@nyc.mn>
nyc.mn

// Observable, Inc. : https://observablehq.com
// Submitted by Mike Bostock <dns@observablehq.com>
static.observableusercontent.com

// Octopodal Solutions, LLC. : https://ulterius.io/
// Submitted by Andrew Sampson <andrew@ulterius.io>
cya.gg

// OMG.LOL : <https://omg.lol>
// Submitted by Adam Newbold <adam@omg.lol>
omg.lol

// Omnibond Systems, LLC. : https://www.omnibond.com
// Submitted by Cole Estep <cole@omnibond.com>
cloudycluster.net

// OmniWe Limited: https://omniwe.com
// Submitted by Vicary Archangel <vicary@omniwe.com>
omniwe.site

// One.com: https://www.one.com/
// Submitted by Jacob Bunk Nielsen <jbn@one.com>
123hjemmeside.dk
123hjemmeside.no
123homepage.it
123kotisivu.fi
123minsida.se
123miweb.es
123paginaweb.pt
123sait.ru
123siteweb.fr
123webseite.at
123webseite.de
123website.be
123website.ch
123website.lu
123website.nl
service.one
simplesite.com
simplesite.com.br
simplesite.gr
simplesite.pl

// One Fold Media : http://www.onefoldmedia.com/
// Submitted by Eddie Jones <eddie@onefoldmedia.com>
nid.io

// Open Social : https://www.getopensocial.com/
// Submitted by Alexander Varwijk <security@getopensocial.com>
opensocial.site

// OpenCraft GmbH : http://opencraft.com/
// Submitted by Sven Marnach <sven@opencraft.com>
opencraft.hosting

// OpenResearch GmbH: https://openresearch.com/
// Submitted by Philipp Schmid <ops@openresearch.com>
orsites.com

// Opera Software, A.S.A.
// Submitted by Yngve Pettersen <yngve@opera.com>
operaunite.com

// Orange : https://www.orange.com
// Submitted by Alexandre Linte <alexandre.linte@orange.com>
tech.orange

// Oursky Limited : https://authgear.com/, https://skygear.io/
// Submitted by Authgear Team <hello@authgear.com>, Skygear Developer <hello@skygear.io>
authgear-staging.com
authgearapps.com
skygearapp.com

// OutSystems
// Submitted by Duarte Santos <domain-admin@outsystemscloud.com>
outsystemscloud.com

// OVHcloud: https://ovhcloud.com
// Submitted by Vincent CassÃ© <vincent.casse@ovhcloud.com>
*.webpaas.ovh.net
*.hosting.ovh.net

// OwnProvider GmbH: http://www.ownprovider.com
// Submitted by Jan Moennich <jan.moennich@ownprovider.com>
ownprovider.com
own.pm

// OwO : https://whats-th.is/
// Submitted by Dean Sheather <dean@deansheather.com>
*.owo.codes

// OX : http://www.ox.rs
// Submitted by Adam Grand <webmaster@mail.ox.rs>
ox.rs

// oy.lc
// Submitted by Charly Coste <changaco@changaco.oy.lc>
oy.lc

// Pagefog : https://pagefog.com/
// Submitted by Derek Myers <derek@pagefog.com>
pgfog.com

// Pagefront : https://www.pagefronthq.com/
// Submitted by Jason Kriss <jason@pagefronthq.com>
pagefrontapp.com

// PageXL : https://pagexl.com
// Submitted by Yann Guichard <yann@pagexl.com>
pagexl.com

// Paywhirl, Inc : https://paywhirl.com/
// Submitted by Daniel Netzer <dan@paywhirl.com>
*.paywhirl.com

// pcarrier.ca Software Inc: https://pcarrier.ca/
// Submitted by Pierre Carrier <pc@rrier.ca>
bar0.net
bar1.net
bar2.net
rdv.to

// .pl domains (grandfathered)
art.pl
gliwice.pl
krakow.pl
poznan.pl
wroc.pl
zakopane.pl

// Pantheon Systems, Inc. : https://pantheon.io/
// Submitted by Gary Dylina <gary@pantheon.io>
pantheonsite.io
gotpantheon.com

// Peplink | Pepwave : http://peplink.com/
// Submitted by Steve Leung <steveleung@peplink.com>
mypep.link

// Perspecta : https://perspecta.com/
// Submitted by Kenneth Van Alstyne <kvanalstyne@perspecta.com>
perspecta.cloud

// PE Ulyanov Kirill Sergeevich : https://airy.host
// Submitted by Kirill Ulyanov <k.ulyanov@airy.host>
lk3.ru

// Planet-Work : https://www.planet-work.com/
// Submitted by FrÃ©dÃ©ric VANNIÃˆRE <f.vanniere@planet-work.com>
on-web.fr

// Platform.sh : https://platform.sh
// Submitted by Nikola Kotur <nikola@platform.sh>
bc.platform.sh
ent.platform.sh
eu.platform.sh
us.platform.sh
*.platformsh.site
*.tst.site

// Platter: https://platter.dev
// Submitted by Patrick Flor <patrick@platter.dev>
platter-app.com
platter-app.dev
platterp.us

// Plesk : https://www.plesk.com/
// Submitted by Anton Akhtyamov <program-managers@plesk.com>
pdns.page
plesk.page
pleskns.com

// Port53 : https://port53.io/
// Submitted by Maximilian Schieder <maxi@zeug.co>
dyn53.io

// Porter : https://porter.run/
// Submitted by Rudraksh MK <rudi@porter.run>
onporter.run

// Positive Codes Technology Company : http://co.bn/faq.html
// Submitted by Zulfais <pc@co.bn>
co.bn

// Postman, Inc : https://postman.com
// Submitted by Rahul Dhawan <security@postman.com>
postman-echo.com
pstmn.io
mock.pstmn.io
httpbin.org

//prequalifyme.today : https://prequalifyme.today
//Submitted by DeepakTiwari deepak@ivylead.io
prequalifyme.today

// prgmr.com : https://prgmr.com/
// Submitted by Sarah Newman <owner@prgmr.com>
xen.prgmr.com

// priv.at : http://www.nic.priv.at/
// Submitted by registry <lendl@nic.at>
priv.at

// privacytools.io : https://www.privacytools.io/
// Submitted by Jonah Aragon <jonah@privacytools.io>
prvcy.page

// Protocol Labs : https://protocol.ai/
// Submitted by Michael Burns <noc@protocol.ai>
*.dweb.link

// Protonet GmbH : http://protonet.io
// Submitted by Martin Meier <admin@protonet.io>
protonet.io

// Publication Presse Communication SARL : https://ppcom.fr
// Submitted by Yaacov Akiba Slama <admin@chirurgiens-dentistes-en-france.fr>
chirurgiens-dentistes-en-france.fr
byen.site

// pubtls.org: https://www.pubtls.org
// Submitted by Kor Nielsen <kor@pubtls.org>
pubtls.org

// PythonAnywhere LLP: https://www.pythonanywhere.com
// Submitted by Giles Thomas <giles@pythonanywhere.com>
pythonanywhere.com
eu.pythonanywhere.com

// QOTO, Org.
// Submitted by Jeffrey Phillips Freeman <jeffrey.freeman@qoto.org>
qoto.io

// Qualifio : https://qualifio.com/
// Submitted by Xavier De Cock <xdecock@gmail.com>
qualifioapp.com

// Quality Unit: https://qualityunit.com
// Submitted by Vasyl Tsalko <vtsalko@qualityunit.com>
ladesk.com

// QuickBackend: https://www.quickbackend.com
// Submitted by Dani Biro <dani@pymet.com>
qbuser.com

// Rad Web Hosting: https://radwebhosting.com
// Submitted by Scott Claeys <s.claeys@radwebhosting.com>
cloudsite.builders

// Redgate Software: https://red-gate.com
// Submitted by Andrew Farries <andrew.farries@red-gate.com>
instances.spawn.cc

// Redstar Consultants : https://www.redstarconsultants.com/
// Submitted by Jons Slemmer <jons@redstarconsultants.com>
instantcloud.cn

// Russian Academy of Sciences
// Submitted by Tech Support <support@rasnet.ru>
ras.ru

// QA2
// Submitted by Daniel Dent (https://www.danieldent.com/)
qa2.com

// QCX
// Submitted by Cassandra Beelen <cassandra@beelen.one>
qcx.io
*.sys.qcx.io

// QNAP System Inc : https://www.qnap.com
// Submitted by Nick Chang <nickchang@qnap.com>
dev-myqnapcloud.com
alpha-myqnapcloud.com
myqnapcloud.com

// Quip : https://quip.com
// Submitted by Patrick Linehan <plinehan@quip.com>
*.quipelements.com

// Qutheory LLC : http://qutheory.io
// Submitted by Jonas Schwartz <jonas@qutheory.io>
vapor.cloud
vaporcloud.io

// Rackmaze LLC : https://www.rackmaze.com
// Submitted by Kirill Pertsev <kika@rackmaze.com>
rackmaze.com
rackmaze.net

// Rakuten Games, Inc : https://dev.viberplay.io
// Submitted by Joshua Zhang <public-suffix@rgames.jp>
g.vbrplsbx.io

// Rancher Labs, Inc : https://rancher.com
// Submitted by Vincent Fiduccia <domains@rancher.com>
*.on-k3s.io
*.on-rancher.cloud
*.on-rio.io

// Read The Docs, Inc : https://www.readthedocs.org
// Submitted by David Fischer <team@readthedocs.org>
readthedocs.io

// Red Hat, Inc. OpenShift : https://openshift.redhat.com/
// Submitted by Tim Kramer <tkramer@rhcloud.com>
rhcloud.com

// Render : https://render.com
// Submitted by Anurag Goel <dev@render.com>
app.render.com
onrender.com

// Repl.it : https://repl.it
// Submitted by Lincoln Bergeson <lincoln@replit.com>
firewalledreplit.co
id.firewalledreplit.co
repl.co
id.repl.co
repl.run

// Resin.io : https://resin.io
// Submitted by Tim Perry <tim@resin.io>
resindevice.io
devices.resinstaging.io

// RethinkDB : https://www.rethinkdb.com/
// Submitted by Chris Kastorff <info@rethinkdb.com>
hzc.io

// Revitalised Limited : http://www.revitalised.co.uk
// Submitted by Jack Price <jack@revitalised.co.uk>
wellbeingzone.eu
wellbeingzone.co.uk

// Rico Developments Limited : https://adimo.co
// Submitted by Colin Brown <hello@adimo.co>
adimo.co.uk

// Riseup Networks : https://riseup.net
// Submitted by Micah Anderson <micah@riseup.net>
itcouldbewor.se

// Rochester Institute of Technology : http://www.rit.edu/
// Submitted by Jennifer Herting <jchits@rit.edu>
git-pages.rit.edu

// Rocky Enterprise Software Foundation : https://resf.org
// Submitted by Neil Hanlon <neil@resf.org>
rocky.page

// Rusnames Limited: http://rusnames.ru/
// Submitted by Sergey Zotov <admin@rusnames.ru>
Ð±Ð¸Ð·.Ñ€ÑƒÑ�
ÐºÐ¾Ð¼.Ñ€ÑƒÑ�
ÐºÑ€Ñ‹Ð¼.Ñ€ÑƒÑ�
Ð¼Ð¸Ñ€.Ñ€ÑƒÑ�
Ð¼Ñ�Ðº.Ñ€ÑƒÑ�
Ð¾Ñ€Ð³.Ñ€ÑƒÑ�
Ñ�Ð°Ð¼Ð°Ñ€Ð°.Ñ€ÑƒÑ�
Ñ�Ð¾Ñ‡Ð¸.Ñ€ÑƒÑ�
Ñ�Ð¿Ð±.Ñ€ÑƒÑ�
Ñ�.Ñ€ÑƒÑ�

// SAKURA Internet Inc. : https://www.sakura.ad.jp/
// Submitted by Internet Service Department <rs-vendor-ml@sakura.ad.jp>
180r.com
dojin.com
sakuratan.com
sakuraweb.com
x0.com
2-d.jp
bona.jp
crap.jp
daynight.jp
eek.jp
flop.jp
halfmoon.jp
jeez.jp
matrix.jp
mimoza.jp
ivory.ne.jp
mail-box.ne.jp
mints.ne.jp
mokuren.ne.jp
opal.ne.jp
sakura.ne.jp
sumomo.ne.jp
topaz.ne.jp
netgamers.jp
nyanta.jp
o0o0.jp
rdy.jp
rgr.jp
rulez.jp
s3.isk01.sakurastorage.jp
s3.isk02.sakurastorage.jp
saloon.jp
sblo.jp
skr.jp
tank.jp
uh-oh.jp
undo.jp
rs.webaccel.jp
user.webaccel.jp
websozai.jp
xii.jp
squares.net
jpn.org
kirara.st
x0.to
from.tv
sakura.tv

// Salesforce.com, Inc. https://salesforce.com/
// Submitted by Michael Biven <mbiven@salesforce.com>
*.builder.code.com
*.dev-builder.code.com
*.stg-builder.code.com

// Sandstorm Development Group, Inc. : https://sandcats.io/
// Submitted by Asheesh Laroia <asheesh@sandstorm.io>
sandcats.io

// SBE network solutions GmbH : https://www.sbe.de/
// Submitted by Norman Meilick <nm@sbe.de>
logoip.de
logoip.com

// Scaleway : https://www.scaleway.com/
// Submitted by RÃ©my LÃ©one <rleone@scaleway.com>
fr-par-1.baremetal.scw.cloud
fr-par-2.baremetal.scw.cloud
nl-ams-1.baremetal.scw.cloud
fnc.fr-par.scw.cloud
functions.fnc.fr-par.scw.cloud
k8s.fr-par.scw.cloud
nodes.k8s.fr-par.scw.cloud
s3.fr-par.scw.cloud
s3-website.fr-par.scw.cloud
whm.fr-par.scw.cloud
priv.instances.scw.cloud
pub.instances.scw.cloud
k8s.scw.cloud
k8s.nl-ams.scw.cloud
nodes.k8s.nl-ams.scw.cloud
s3.nl-ams.scw.cloud
s3-website.nl-ams.scw.cloud
whm.nl-ams.scw.cloud
k8s.pl-waw.scw.cloud
nodes.k8s.pl-waw.scw.cloud
s3.pl-waw.scw.cloud
s3-website.pl-waw.scw.cloud
scalebook.scw.cloud
smartlabeling.scw.cloud
dedibox.fr

// schokokeks.org GbR : https://schokokeks.org/
// Submitted by Hanno BÃ¶ck <hanno@schokokeks.org>
schokokeks.net

// Scottish Government: https://www.gov.scot
// Submitted by Martin Ellis <martin.ellis@gov.scot>
gov.scot
service.gov.scot

// Scry Security : http://www.scrysec.com
// Submitted by Shante Adam <shante@skyhat.io>
scrysec.com

// Securepoint GmbH : https://www.securepoint.de
// Submitted by Erik Anders <erik.anders@securepoint.de>
firewall-gateway.com
firewall-gateway.de
my-gateway.de
my-router.de
spdns.de
spdns.eu
firewall-gateway.net
my-firewall.org
myfirewall.org
spdns.org

// Seidat : https://www.seidat.com
// Submitted by Artem Kondratev <accounts@seidat.com>
seidat.net

// Sellfy : https://sellfy.com
// Submitted by Yuriy Romadin <contact@sellfy.com>
sellfy.store

// Senseering GmbH : https://www.senseering.de
// Submitted by Felix MÃ¶nckemeyer <f.moenckemeyer@senseering.de>
senseering.net

// Sendmsg: https://www.sendmsg.co.il
// Submitted by Assaf Stern <domains@comstar.co.il>
minisite.ms

// Service Magnet : https://myservicemagnet.com
// Submitted by Dave Sanders <dave@myservicemagnet.com>
magnet.page

// Service Online LLC : http://drs.ua/
// Submitted by Serhii Bulakh <support@drs.ua>
biz.ua
co.ua
pp.ua

// Shift Crypto AG : https://shiftcrypto.ch
// Submitted by alex <alex@shiftcrypto.ch>
shiftcrypto.dev
shiftcrypto.io

// ShiftEdit : https://shiftedit.net/
// Submitted by Adam Jimenez <adam@shiftcreate.com>
shiftedit.io

// Shopblocks : http://www.shopblocks.com/
// Submitted by Alex Bowers <alex@shopblocks.com>
myshopblocks.com

// Shopify : https://www.shopify.com
// Submitted by Alex Richter <alex.richter@shopify.com>
myshopify.com

// Shopit : https://www.shopitcommerce.com/
// Submitted by Craig McMahon <craig@shopitcommerce.com>
shopitsite.com

// shopware AG : https://shopware.com
// Submitted by Jens KÃ¼per <cloud@shopware.com>
shopware.store

// Siemens Mobility GmbH
// Submitted by Oliver Graebner <security@mo-siemens.io>
mo-siemens.io

// SinaAppEngine : http://sae.sina.com.cn/
// Submitted by SinaAppEngine <saesupport@sinacloud.com>
1kapp.com
appchizi.com
applinzi.com
sinaapp.com
vipsinaapp.com

// Siteleaf : https://www.siteleaf.com/
// Submitted by Skylar Challand <support@siteleaf.com>
siteleaf.net

// Skyhat : http://www.skyhat.io
// Submitted by Shante Adam <shante@skyhat.io>
bounty-full.com
alpha.bounty-full.com
beta.bounty-full.com

// Smallregistry by Promopixel SARL: https://www.smallregistry.net
// Former AFNIC's SLDs
// Submitted by JÃ©rÃ´me Lipowicz <support@promopixel.com>
aeroport.fr
avocat.fr
chambagri.fr
chirurgiens-dentistes.fr
experts-comptables.fr
medecin.fr
notaires.fr
pharmacien.fr
port.fr
veterinaire.fr

// Small Technology Foundation : https://small-tech.org
// Submitted by Aral Balkan <aral@small-tech.org>
small-web.org

// Smoove.io : https://www.smoove.io/
// Submitted by Dan Kozak <dan@smoove.io>
vp4.me

// Snowflake Inc : https://www.snowflake.com/
// Submitted by Faith Olapade <faith.olapade@snowflake.com>
snowflake.app
privatelink.snowflake.app
streamlit.app
streamlitapp.com

// Snowplow Analytics : https://snowplowanalytics.com/
// Submitted by Ian Streeter <ian@snowplowanalytics.com>
try-snowplow.com

// SourceHut : https://sourcehut.org
// Submitted by Drew DeVault <sir@cmpwn.com>
srht.site

// Stackhero : https://www.stackhero.io
// Submitted by Adrien Gillon <adrien+public-suffix-list@stackhero.io>
stackhero-network.com

// Staclar : https://staclar.com
// Submitted by Q Misell <q@staclar.com>
musician.io
// Submitted by Matthias Merkel <matthias.merkel@staclar.com>
novecore.site

// staticland : https://static.land
// Submitted by Seth Vincent <sethvincent@gmail.com>
static.land
dev.static.land
sites.static.land

// Storebase : https://www.storebase.io
// Submitted by Tony Schirmer <tony@storebase.io>
storebase.store

// Strategic System Consulting (eApps Hosting): https://www.eapps.com/
// Submitted by Alex Oancea <aoancea@cloudscale365.com>
vps-host.net
atl.jelastic.vps-host.net
njs.jelastic.vps-host.net
ric.jelastic.vps-host.net

// Sony Interactive Entertainment LLC : https://sie.com/
// Submitted by David Coles <david.coles@sony.com>
playstation-cloud.com

// SourceLair PC : https://www.sourcelair.com
// Submitted by Antonis Kalipetis <akalipetis@sourcelair.com>
apps.lair.io
*.stolos.io

// SpaceKit : https://www.spacekit.io/
// Submitted by Reza Akhavan <spacekit.io@gmail.com>
spacekit.io

// SpeedPartner GmbH: https://www.speedpartner.de/
// Submitted by Stefan Neufeind <info@speedpartner.de>
customer.speedpartner.de

// Spreadshop (sprd.net AG) : https://www.spreadshop.com/
// Submitted by Martin Breest <security@spreadshop.com>
myspreadshop.at
myspreadshop.com.au
myspreadshop.be
myspreadshop.ca
myspreadshop.ch
myspreadshop.com
myspreadshop.de
myspreadshop.dk
myspreadshop.es
myspreadshop.fi
myspreadshop.fr
myspreadshop.ie
myspreadshop.it
myspreadshop.net
myspreadshop.nl
myspreadshop.no
myspreadshop.pl
myspreadshop.se
myspreadshop.co.uk

// Standard Library : https://stdlib.com
// Submitted by Jacob Lee <jacob@stdlib.com>
api.stdlib.com

// Storipress : https://storipress.com
// Submitted by Benno Liu <benno@storipress.com>
storipress.app

// Storj Labs Inc. : https://storj.io/
// Submitted by Philip Hutchins <hostmaster@storj.io>
storj.farm

// Studenten Net Twente : http://www.snt.utwente.nl/
// Submitted by Silke Hofstra <syscom@snt.utwente.nl>
utwente.io

// Student-Run Computing Facility : https://www.srcf.net/
// Submitted by Edwin Balani <sysadmins@srcf.net>
soc.srcf.net
user.srcf.net

// Sub 6 Limited: http://www.sub6.com
// Submitted by Dan Miller <dm@sub6.com>
temp-dns.com

// Supabase : https://supabase.io
// Submitted by Inian Parameshwaran <security@supabase.io>
supabase.co
supabase.in
supabase.net
su.paba.se

// Symfony, SAS : https://symfony.com/
// Submitted by Fabien Potencier <fabien@symfony.com>
*.s5y.io
*.sensiosite.cloud

// Syncloud : https://syncloud.org
// Submitted by Boris Rybalkin <syncloud@syncloud.it>
syncloud.it

// Synology, Inc. : https://www.synology.com/
// Submitted by Rony Weng <ronyweng@synology.com>
dscloud.biz
direct.quickconnect.cn
dsmynas.com
familyds.com
diskstation.me
dscloud.me
i234.me
myds.me
synology.me
dscloud.mobi
dsmynas.net
familyds.net
dsmynas.org
familyds.org
vpnplus.to
direct.quickconnect.to

// Tabit Technologies Ltd. : https://tabit.cloud/
// Submitted by Oren Agiv <oren@tabit.cloud>
tabitorder.co.il
mytabit.co.il
mytabit.com

// TAIFUN Software AG : http://taifun-software.de
// Submitted by Bjoern Henke <dev-server@taifun-software.de>
taifun-dns.de

// Tailscale Inc. : https://www.tailscale.com
// Submitted by David Anderson <danderson@tailscale.com>
beta.tailscale.net
ts.net

// TASK geographical domains (www.task.gda.pl/uslugi/dns)
gda.pl
gdansk.pl
gdynia.pl
med.pl
sopot.pl

// team.blue https://team.blue
// Submitted by Cedric Dubois <cedric.dubois@team.blue>
site.tb-hosting.com

// Teckids e.V. : https://www.teckids.org
// Submitted by Dominik George <dominik.george@teckids.org>
edugit.io
s3.teckids.org

// Telebit : https://telebit.cloud
// Submitted by AJ ONeal <aj@telebit.cloud>
telebit.app
telebit.io
*.telebit.xyz

// Thingdust AG : https://thingdust.com/
// Submitted by Adrian Imboden <adi@thingdust.com>
*.firenet.ch
*.svc.firenet.ch
reservd.com
thingdustdata.com
cust.dev.thingdust.io
cust.disrec.thingdust.io
cust.prod.thingdust.io
cust.testing.thingdust.io
reservd.dev.thingdust.io
reservd.disrec.thingdust.io
reservd.testing.thingdust.io

// ticket i/O GmbH : https://ticket.io
// Submitted by Christian Franke <it@ticket.io>
tickets.io

// Tlon.io : https://tlon.io
// Submitted by Mark Staarink <mark@tlon.io>
arvo.network
azimuth.network
tlon.network

// Tor Project, Inc. : https://torproject.org
// Submitted by Antoine BeauprÃ© <anarcat@torproject.org
torproject.net
pages.torproject.net

// TownNews.com : http://www.townnews.com
// Submitted by Dustin Ward <dward@townnews.com>
bloxcms.com
townnews-staging.com

// TrafficPlex GmbH : https://www.trafficplex.de/
// Submitted by Phillipp RÃ¶ll <phillipp.roell@trafficplex.de>
12hp.at
2ix.at
4lima.at
lima-city.at
12hp.ch
2ix.ch
4lima.ch
lima-city.ch
trafficplex.cloud
de.cool
12hp.de
2ix.de
4lima.de
lima-city.de
1337.pictures
clan.rip
lima-city.rocks
webspace.rocks
lima.zone

// TransIP : https://www.transip.nl
// Submitted by Rory Breuk <rbreuk@transip.nl>
*.transurl.be
*.transurl.eu
*.transurl.nl

// TransIP: https://www.transip.nl
// Submitted by Cedric Dubois <cedric.dubois@team.blue>
site.transip.me

// TuxFamily : http://tuxfamily.org
// Submitted by TuxFamily administrators <adm@staff.tuxfamily.org>
tuxfamily.org

// TwoDNS : https://www.twodns.de/
// Submitted by TwoDNS-Support <support@two-dns.de>
dd-dns.de
diskstation.eu
diskstation.org
dray-dns.de
draydns.de
dyn-vpn.de
dynvpn.de
mein-vigor.de
my-vigor.de
my-wan.de
syno-ds.de
synology-diskstation.de
synology-ds.de

// Typedream : https://typedream.com
// Submitted by Putri Karunia <putri@typedream.com>
typedream.app

// Typeform : https://www.typeform.com
// Submitted by Sergi Ferriz <sergi.ferriz@typeform.com>
pro.typeform.com

// Uberspace : https://uberspace.de
// Submitted by Moritz Werner <mwerner@jonaspasche.com>
uber.space
*.uberspace.de

// UDR Limited : http://www.udr.hk.com
// Submitted by registry <hostmaster@udr.hk.com>
hk.com
hk.org
ltd.hk
inc.hk

// UK Intis Telecom LTD : https://it.com
// Submitted by ITComdomains <to@it.com>
it.com

// UNIVERSAL DOMAIN REGISTRY : https://www.udr.org.yt/
// see also: whois -h whois.udr.org.yt help
// Submitted by Atanunu Igbunuroghene <publicsuffixlist@udr.org.yt>
name.pm
sch.tf
biz.wf
sch.wf
org.yt

// United Gameserver GmbH : https://united-gameserver.de
// Submitted by Stefan Schwarz <sysadm@united-gameserver.de>
virtualuser.de
virtual-user.de

// Upli : https://upli.io
// Submitted by Lenny Bakkalian <lenny.bakkalian@gmail.com>
upli.io

// urown.net : https://urown.net
// Submitted by Hostmaster <hostmaster@urown.net>
urown.cloud
dnsupdate.info

// .US
// Submitted by Ed Moore <Ed.Moore@lib.de.us>
lib.de.us

// VeryPositive SIA : http://very.lv
// Submitted by Danko Aleksejevs <danko@very.lv>
2038.io

// Vercel, Inc : https://vercel.com/
// Submitted by Connor Davis <security@vercel.com>
vercel.app
vercel.dev
now.sh

// Viprinet Europe GmbH : http://www.viprinet.com
// Submitted by Simon Kissel <hostmaster@viprinet.com>
router.management

// Virtual-Info : https://www.virtual-info.info/
// Submitted by Adnan RIHAN <hostmaster@v-info.info>
v-info.info

// Voorloper.com: https://voorloper.com
// Submitted by Nathan van Bakel <info@voorloper.com>
voorloper.cloud

// Voxel.sh DNS : https://voxel.sh/dns/
// Submitted by Mia Rehlinger <dns@voxel.sh>
neko.am
nyaa.am
be.ax
cat.ax
es.ax
eu.ax
gg.ax
mc.ax
us.ax
xy.ax
nl.ci
xx.gl
app.gp
blog.gt
de.gt
to.gt
be.gy
cc.hn
blog.kg
io.kg
jp.kg
tv.kg
uk.kg
us.kg
de.ls
at.md
de.md
jp.md
to.md
indie.porn
vxl.sh
ch.tc
me.tc
we.tc
nyan.to
at.vg
blog.vu
dev.vu
me.vu

// V.UA Domain Administrator : https://domain.v.ua/
// Submitted by Serhii Rostilo <sergey@rostilo.kiev.ua>
v.ua

// Vultr Objects : https://www.vultr.com/products/object-storage/
// Submitted by Niels Maumenee <storage@vultr.com>
*.vultrobjects.com

// Waffle Computer Inc., Ltd. : https://docs.waffleinfo.com
// Submitted by Masayuki Note <masa@blade.wafflecell.com>
wafflecell.com

// WebHare bv: https://www.webhare.com/
// Submitted by Arnold Hendriks <info@webhare.com>
*.webhare.dev

// WebHotelier Technologies Ltd: https://www.webhotelier.net/
// Submitted by Apostolos Tsakpinis <apostolos.tsakpinis@gmail.com>
reserve-online.net
reserve-online.com
bookonline.app
hotelwithflight.com

// WeDeploy by Liferay, Inc. : https://www.wedeploy.com
// Submitted by Henrique Vicente <security@wedeploy.com>
wedeploy.io
wedeploy.me
wedeploy.sh

// Western Digital Technologies, Inc : https://www.wdc.com
// Submitted by Jung Jin <jungseok.jin@wdc.com>
remotewd.com

// WIARD Enterprises : https://wiardweb.com
// Submitted by Kidd Hustle <kiddhustle@wiardweb.com>
pages.wiardweb.com

// Wikimedia Labs : https://wikitech.wikimedia.org
// Submitted by Arturo Borrero Gonzalez <aborrero@wikimedia.org>
wmflabs.org
toolforge.org
wmcloud.org

// WISP : https://wisp.gg
// Submitted by Stepan Fedotov <stepan@wisp.gg>
panel.gg
daemon.panel.gg

// Wizard Zines : https://wizardzines.com
// Submitted by Julia Evans <julia@wizardzines.com>
messwithdns.com

// WoltLab GmbH : https://www.woltlab.com
// Submitted by Tim DÃ¼sterhus <security@woltlab.cloud>
woltlab-demo.com
myforum.community
community-pro.de
diskussionsbereich.de
community-pro.net
meinforum.net

// Woods Valldata : https://www.woodsvalldata.co.uk/
// Submitted by Chris Whittle <chris.whittle@woodsvalldata.co.uk>
affinitylottery.org.uk
raffleentry.org.uk
weeklylottery.org.uk

// WP Engine : https://wpengine.com/
// Submitted by Michael Smith <michael.smith@wpengine.com>
// Submitted by Brandon DuRette <brandon.durette@wpengine.com>
wpenginepowered.com
js.wpenginepowered.com

// Wix.com, Inc. : https://www.wix.com
// Submitted by Shahar Talmi <shahar@wix.com>
wixsite.com
editorx.io
wixstudio.io
wix.run

// XenonCloud GbR: https://xenoncloud.net
// Submitted by Julian Uphoff <publicsuffixlist@xenoncloud.net>
half.host

// XnBay Technology : http://www.xnbay.com/
// Submitted by XnBay Developer <developer.xncloud@gmail.com>
xnbay.com
u2.xnbay.com
u2-local.xnbay.com

// XS4ALL Internet bv : https://www.xs4all.nl/
// Submitted by Daniel Mostertman <unixbeheer+publicsuffix@xs4all.net>
cistron.nl
demon.nl
xs4all.space

// Yandex.Cloud LLC: https://cloud.yandex.com
// Submitted by Alexander Lodin <security+psl@yandex-team.ru>
yandexcloud.net
storage.yandexcloud.net
website.yandexcloud.net

// YesCourse Pty Ltd : https://yescourse.com
// Submitted by Atul Bhouraskar <atul@yescourse.com>
official.academy

// Yola : https://www.yola.com/
// Submitted by Stefano Rivera <stefano@yola.com>
yolasite.com

// Yombo : https://yombo.net
// Submitted by Mitch Schwenk <mitch@yombo.net>
ybo.faith
yombo.me
homelink.one
ybo.party
ybo.review
ybo.science
ybo.trade

// Yunohost : https://yunohost.org
// Submitted by Valentin Grimaud <security@yunohost.org>
ynh.fr
nohost.me
noho.st

// ZaNiC : http://www.za.net/
// Submitted by registry <hostmaster@nic.za.net>
za.net
za.org

// Zine EOOD : https://zine.bg/
// Submitted by Martin Angelov <martin@zine.bg>
bss.design

// Zitcom A/S : https://www.zitcom.dk
// Submitted by Emil Stahl <esp@zitcom.dk>
basicserver.io
virtualserver.io
enterprisecloud.nu

// ===END PRIVATE DOMAINS==={
    "name": "amphp/http-client-cookies",
    "homepage": "https://github.com/amphp/http-client-cookies",
    "description": "Automatic cookie handling for Amp's HTTP client.",
    "keywords": [
        "http",
        "cookie",
        "cookies",
        "client",
        "async"
    ],
    "license": "MIT",
    "authors": [
        {
            "name": "Daniel Lowrey",
            "email": "rdlowrey@gmail.com"
        },
        {
            "name": "Niklas Keller",
            "email": "me@kelunik.com"
        }
    ],
    "require": {
        "php": ">=8.1",
        "ext-filter": "*",
        "amphp/amp": "^3",
        "amphp/dns": "^2",
        "amphp/http": "^2",
        "amphp/http-client": "^5",
        "amphp/sync": "^2",
        "psr/http-message": "^1|^2"
    },
    "require-dev": {
        "amphp/socket": "^2",
        "amphp/file": "^3",
        "amphp/http-server": "^3",
        "amphp/phpunit-util": "^3",
        "amphp/php-cs-fixer-config": "^2",
        "phpunit/phpunit": "^9",
        "psalm/phar": "^5.6"
    },
    "autoload": {
        "psr-4": {
            "Amp\\Http\\Client\\Cookie\\": "src"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Amp\\Http\\Client\\Cookie\\": "test"
        }
    },
    "conflict": {
        "amphp/file": "<3 || >=4"
    },
    "scripts": {
        "check": [
            "@cs",
            "@test"
        ],
        "cs": "PHP_CS_FIXER_IGNORE_ENV=1 php-cs-fixer fix -v --diff --dry-run",
        "cs-fix": "PHP_CS_FIXER_IGNORE_ENV=1 php-cs-fixer fix -v --diff",
        "test": "@php -dzend.assertions=1 -dassert.exception=1 ./vendor/bin/phpunit --coverage-text"
    }
}
{
    "symbol-whitelist": [
        "null",
        "true",
        "false",
        "static",
        "self",
        "parent",
        "array",
        "string",
        "int",
        "float",
        "bool",
        "iterable",
        "callable",
        "mixed",
        "void",
        "object",
        "createDefaultDriver",
        "deleteFile",
        "filesystem",
        "openFile",
        "eio_cancel",
        "eio_chmod",
        "eio_chown",
        "eio_close",
        "eio_event_loop",
        "eio_fstat",
        "eio_ftruncate",
        "eio_get_event_stream",
        "eio_get_last_error",
        "eio_init",
        "eio_link",
        "eio_lstat",
        "eio_mkdir",
        "eio_npending",
        "eio_open",
        "eio_poll",
        "eio_read",
        "eio_readdir",
        "eio_readlink",
        "eio_rename",
        "eio_rmdir",
        "eio_stat",
        "eio_symlink",
        "eio_unlink",
        "eio_utime",
        "eio_write",
        "EIO_O_APPEND",
        "EIO_O_CREAT",
        "EIO_O_EXCL",
        "EIO_O_FSYNC",
        "EIO_O_NONBLOCK",
        "EIO_O_RDONLY",
        "EIO_O_RDWR",
        "EIO_O_WRONLY",
        "EIO_O_TRUNC",
        "EIO_PRI_DEFAULT",
        "EIO_READDIR_DIRS_FIRST",
        "EIO_READDIR_STAT_ORDER",
        "EIO_S_IRUSR",
        "EIO_S_IWUSR",
        "EIO_S_IXUSR",
        "UV",
        "UVLoop",
        "uv_fs_chmod",
        "uv_fs_chown",
        "uv_fs_fstat",
        "uv_fs_ftruncate",
        "uv_fs_link",
        "uv_fs_lstat",
        "uv_fs_mkdir",
        "uv_fs_open",
        "uv_fs_read",
        "uv_fs_readdir",
        "uv_fs_readlink",
        "uv_fs_rename",
        "uv_fs_rmdir",
        "uv_fs_scandir",
        "uv_fs_stat",
        "uv_fs_symlink",
        "uv_fs_unlink",
        "uv_fs_utime",
        "uv_fs_write",
        "uv_fs_close",
        "uv_strerror"
    ],
    "php-core-extensions": [
        "Core",
        "date",
        "pcre",
        "Phar",
        "Reflection",
        "SPL",
        "standard",
        "hash"
    ]
}
<?php declare(strict_types=1);

namespace Amp\File\Test;

use Amp\Cache\Test\StringCacheTest;
use Amp\File\FileCache;
use Amp\Sync\LocalKeyedMutex;

class FileCacheTest extends StringCacheTest
{
    protected function setUp(): void
    {
        parent::setUp();
        Fixture::init();
    }

    protected function tearDown(): void
    {
        parent::tearDown();
        Fixture::clear();
    }

    protected function createCache(): FileCache
    {
        return new FileCache(Fixture::path(), new LocalKeyedMutex());
    }
}
<?php declare(strict_types=1);

namespace Amp\File\Test;

use Amp\File;
use Amp\File\FilesystemException;
use const Amp\Process\IS_WINDOWS;

abstract class FilesystemDriverTest extends FilesystemTest
{
    protected File\Filesystem $driver;

    public function testListFiles(): void
    {
        $fixtureDir = Fixture::path();
        $actual = $this->driver->listFiles($fixtureDir);
        $expected = IS_WINDOWS
            ? ["dir", "dirlink", "file", "filelink"]
            : ["dir", "dirlink", "fifo", "fifolink", "file", "filelink", "linkloop"];
        $this->assertSame($expected, $actual);
    }

    public function testListFilesThrowsIfPathNotADirectory(): void
    {
        $this->expectException(FilesystemException::class);

        $this->driver->listFiles(__FILE__);
    }

    public function testListFilesThrowsIfPathDoesntExist(): void
    {
        $this->expectException(FilesystemException::class);

        $path = Fixture::path() . "/nonexistent";
        $this->driver->listFiles($path);
    }

    public function testCreateSymlink(): void
    {
        $fixtureDir = Fixture::path();

        $original = "{$fixtureDir}/file";
        $link = "{$fixtureDir}/symlink.txt";
        $this->driver->createSymlink($original, $link);
        $this->assertTrue(\is_link($link));
        $this->driver->deleteFile($link);
    }

    public function testCreateSymlinkFailWhenLinkExists(): void
    {
        $this->expectException(FilesystemException::class);

        $fixtureDir = Fixture::path();
        $path = "{$fixtureDir}/file";
        $this->driver->createSymlink($path, $path);
    }

    public function testCreateHardlink(): void
    {
        $fixtureDir = Fixture::path();

        $original = "{$fixtureDir}/file";
        $link = "{$fixtureDir}/hardlink.txt";
        $this->driver->createHardlink($original, $link);
        $this->assertFileExists($link);
        $this->assertFalse(\is_link($link));
        $this->driver->deleteFile($link);
    }

    public function testCreateHardlinkFailWhenLinkExists(): void
    {
        $this->expectException(FilesystemException::class);

        $fixtureDir = Fixture::path();
        $path = "{$fixtureDir}/file";
        $this->driver->createHardlink($path, $path);
    }

    public function testResolveSymlink(): void
    {
        $fixtureDir = Fixture::path();

        $original = "{$fixtureDir}/file";
        $link = "{$fixtureDir}/symlink.txt";
        \symlink($original, $link);

        $this->assertSame(\str_replace('/', \DIRECTORY_SEPARATOR, $original), $this->driver->resolveSymlink($link));
    }

    public function symlinkPathProvider(): array
    {
        return [
            'nonExistingPath' => [
                static function () {
                    return Fixture::path() . '/' . \uniqid('amphp-test-', true);
                },
            ],
            'notLink' => [
                static function () {
                    return Fixture::path();
                },
            ],
        ];
    }

    /**
     * @dataProvider symlinkPathProvider
     *
     */
    public function testResolveSymlinkError(\Closure $linkResolver): void
    {
        $link = $linkResolver();

        $this->expectException(FilesystemException::class);

        $result = $this->driver->resolveSymlink($link);

        if (IS_WINDOWS) {
            self::assertNotSame($result, $link);

            $this->markTestSkipped('Build directory itself contains a symlink');
        }
    }

    public function testLinkStatus(): void
    {
        $fixtureDir = Fixture::path();

        $target = "{$fixtureDir}/file";
        $link = "{$fixtureDir}/symlink.txt";
        $this->driver->createSymlink($target, $link);
        $this->assertIsArray($this->driver->getLinkStatus($link));
        $this->driver->deleteFile($link);
    }

    public function testStatus(): void
    {
        $fixtureDir = Fixture::path();
        $path = "{$fixtureDir}/file";
        $stat = $this->driver->getStatus($path);
        $this->assertIsArray($stat);
        $this->assertSameStatus(\stat($path), $stat);
    }

    public function testDirectoryStatus(): void
    {
        $fixtureDir = Fixture::path();
        $stat = $this->driver->getStatus("{$fixtureDir}/dir");
        $this->assertIsArray($stat);
        $this->assertSameStatus(\stat("{$fixtureDir}/dir"), $stat);
    }

    public function testNonexistentPathStatusResolvesToNull(): void
    {
        $fixtureDir = Fixture::path();
        $stat = $this->driver->getStatus("{$fixtureDir}/nonexistent");
        $this->assertNull($stat);
    }

    public function testExists(): void
    {
        $fixtureDir = Fixture::path();
        $this->assertFalse($this->driver->exists("{$fixtureDir}/nonexistent"));
        $this->assertTrue($this->driver->exists("{$fixtureDir}/file"));
    }

    public function testRead(): void
    {
        $fixtureDir = Fixture::path();
        $this->assertSame("small", $this->driver->read("{$fixtureDir}/file"));
    }

    public function testSize(): void
    {
        $fixtureDir = Fixture::path();
        $path = "{$fixtureDir}/file";
        $stat = ($this->driver->getStatus($path));
        $size = $stat["size"];
        $this->assertSame($size, $this->driver->getSize($path));
    }

    public function testSizeFailsOnNonexistentPath(): void
    {
        $this->expectException(FilesystemException::class);

        $fixtureDir = Fixture::path();
        $path = "{$fixtureDir}/nonexistent";
        $this->driver->getSize($path);
    }

    public function testSizeFailsOnDirectoryPath(): void
    {
        $this->expectException(FilesystemException::class);

        $fixtureDir = Fixture::path();
        $path = "{$fixtureDir}/dir";
        $this->assertTrue($this->driver->isDirectory($path));
        $this->driver->getSize($path);
    }

    /**
     * @dataProvider dataForDirectoryCheck
     */
    public function testIsDirectory(bool $expectedResult, string $name): void
    {
        $fixtureDir = Fixture::path();
        $path = "{$fixtureDir}/{$name}";
        $this->assertSame($expectedResult, $this->driver->isDirectory($path));
    }

    public function dataForDirectoryCheck(): \Generator
    {
        yield 'file' => [false, 'file'];
        yield 'filelink' => [false, 'filelink'];
        yield 'dir' => [true, 'dir'];
        yield 'dirlink' => [true, 'dirlink'];
        if (\extension_loaded('posix')) {
            yield 'fifo' => [false, 'fifo'];
            yield 'fifolink' => [false, 'fifolink'];
        }
        if (!IS_WINDOWS) {
            yield 'linkloop' => [false, 'linkloop'];
        }
        yield 'nonexistent' => [false, 'nonexistent'];
    }

    /**
     * @dataProvider dataForFileCheck
     */
    public function testIsFile(bool $expectedResult, string $name): void
    {
        $fixtureDir = Fixture::path();
        $path = "{$fixtureDir}/{$name}";
        $this->assertSame($expectedResult, $this->driver->isFile($path));
    }

    public function dataForFileCheck(): \Generator
    {
        yield 'file' => [true, 'file'];
        yield 'filelink' => [true, 'filelink'];
        yield 'dir' => [false, 'dir'];
        yield 'dirlink' => [false, 'dirlink'];
        if (\extension_loaded('posix')) {
            yield 'fifo' => [false, 'fifo'];
            yield 'fifolink' => [false, 'fifolink'];
        }
        if (!IS_WINDOWS) {
            yield 'linkloop' => [false, 'linkloop'];
        }
        yield 'nonexistent' => [false, 'nonexistent'];
    }

    /**
     * @dataProvider dataForSymlinkCheck
     */
    public function testIsSymlink(bool $expectedResult, string $name): void
    {
        $fixtureDir = Fixture::path();
        $path = "{$fixtureDir}/{$name}";
        $this->assertSame($expectedResult, $this->driver->isSymlink($path));
    }

    public function dataForSymlinkCheck(): \Generator
    {
        yield 'file' => [false, 'file'];
        yield 'filelink' => [true, 'filelink'];
        yield 'dir' => [false, 'dir'];
        yield 'dirlink' => [true, 'dirlink'];
        if (\extension_loaded('posix')) {
            yield 'fifo' => [false, 'fifo'];
            yield 'fifolink' => [true, 'fifolink'];
        }
        if (!IS_WINDOWS) {
            yield 'linkloop' => [true, 'linkloop'];
        }
        yield 'nonexistent' => [false, 'nonexistent'];
    }

    public function testMove(): void
    {
        $fixtureDir = Fixture::path();

        $contents1 = "rename test";
        $old = "{$fixtureDir}/rename1.txt";
        $new = "{$fixtureDir}/rename2.txt";

        $this->driver->write($old, $contents1);
        $this->driver->move($old, $new);
        $contents2 = $this->driver->read($new);
        $this->driver->deleteFile($new);

        $this->assertSame($contents1, $contents2);
    }

    public function testMoveFailsOnNonexistentPath(): void
    {
        $fixtureDir = Fixture::path();
        $path = "{$fixtureDir}/nonexistent";

        $this->expectException(FilesystemException::class);

        $this->driver->move($path, $path);
    }

    public function testDeleteFile(): void
    {
        $fixtureDir = Fixture::path();
        $toUnlink = "{$fixtureDir}/unlink";
        $this->driver->getStatus($toUnlink);
        $this->driver->write($toUnlink, "unlink me");
        $this->driver->deleteFile($toUnlink);
        $this->assertNull($this->driver->getStatus($toUnlink));
    }

    public function testDeleteFileFailsOnNonexistentPath(): void
    {
        $fixtureDir = Fixture::path();
        $path = "{$fixtureDir}/nonexistent";

        $this->expectException(FilesystemException::class);

        $this->driver->deleteFile($path);
    }

    public function testDeleteFileFailsOnDirectory(): void
    {
        $fixtureDir = Fixture::path();
        $dir = "{$fixtureDir}/newdir";
        $this->driver->createDirectory($dir);

        $this->expectException(FilesystemException::class);

        $this->driver->deleteFile($dir);
    }

    public function testCreateAndDeleteDirectory(): void
    {
        $fixtureDir = Fixture::path();

        $dir = "{$fixtureDir}/newdir";

        \umask(0022);

        $this->driver->createDirectory($dir);
        $stat = $this->driver->getStatus($dir);

        if (IS_WINDOWS) {
            $this->assertSame('0777', $this->getPermissionsFromStatus($stat));
        } else {
            $this->assertSame('0755', $this->getPermissionsFromStatus($stat));
        }

        $this->driver->deleteDirectory($dir);
        $this->assertNull($this->driver->getStatus($dir));

        // test for 0, because previous array_filter made that not work
        $dir = "{$fixtureDir}/newdir/with/recursive/creation/0/1/2";

        $this->driver->createDirectoryRecursively($dir, 0764);
        $stat = $this->driver->getStatus($dir);

        if (IS_WINDOWS) {
            $this->assertSame('0777', $this->getPermissionsFromStatus($stat));
        } else {
            $this->assertSame('0744', $this->getPermissionsFromStatus($stat));
        }
    }

    public function testCreateDirectorySlashEnd(): void
    {
        $fixtureDir = Fixture::path();

        $dir = "{$fixtureDir}/newdir-with-trailing-slash/";

        $this->driver->createDirectory($dir, 0764);
        $this->assertTrue($this->driver->exists($dir));
    }

    public function testCreateDirectoryRecursivelySlashEnd(): void
    {
        $fixtureDir = Fixture::path();

        $dir = "{$fixtureDir}/newdir/with/recursive/creation/321/";

        $this->driver->createDirectoryRecursively($dir, 0764);
        $this->assertTrue($this->driver->exists($dir));
    }

    public function testCreateDirectoryRecursivelyExistsDir(): void
    {
        $this->expectNotToPerformAssertions();

        $this->driver->createDirectoryRecursively(__DIR__, 0764);
    }

    public function testCreateDirectoryRecursivelyExistsFile(): void
    {
        $this->expectException(FilesystemException::class);

        $this->driver->createDirectoryRecursively(__FILE__, 0764);
    }

    public function testCreateDirectoryFailsOnNonexistentPath(): void
    {
        $fixtureDir = Fixture::path();
        $path = "{$fixtureDir}/nonexistent/nonexistent";

        $this->expectException(FilesystemException::class);

        $this->driver->createDirectory($path);
    }

    public function testDeleteDirectoryFailsOnNonexistentPath(): void
    {
        $fixtureDir = Fixture::path();
        $path = "{$fixtureDir}/nonexistent";

        $this->expectException(FilesystemException::class);

        $this->driver->deleteDirectory($path);
    }

    public function testDeleteDirectoryFailsOnFile(): void
    {
        $fixtureDir = Fixture::path();
        $path = "{$fixtureDir}/file";

        $this->expectException(FilesystemException::class);

        $this->driver->deleteDirectory($path);
    }

    public function testMtime(): void
    {
        $fixtureDir = Fixture::path();
        $path = "{$fixtureDir}/file";
        $stat = $this->driver->getStatus($path);
        $statMtime = $stat["mtime"];
        $this->assertSame($statMtime, $this->driver->getModificationTime($path));
    }

    public function testMtimeFailsOnNonexistentPath(): void
    {
        $fixtureDir = Fixture::path();
        $path = "{$fixtureDir}/nonexistent";

        $this->expectException(FilesystemException::class);

        $this->driver->getModificationTime($path);
    }

    public function testAtime(): void
    {
        $fixtureDir = Fixture::path();
        $path = "{$fixtureDir}/file";
        $stat = $this->driver->getStatus($path);
        $statAtime = $stat["atime"];
        $this->assertSame($statAtime, $this->driver->getAccessTime($path));
    }

    public function testAtimeFailsOnNonexistentPath(): void
    {
        $fixtureDir = Fixture::path();
        $path = "{$fixtureDir}/nonexistent";

        $this->expectException(FilesystemException::class);

        $this->driver->getAccessTime($path);
    }

    public function testCtime(): void
    {
        $fixtureDir = Fixture::path();
        $path = "{$fixtureDir}/file";
        $stat = $this->driver->getStatus($path);
        $statCtime = $stat["ctime"];
        $this->assertSame($statCtime, $this->driver->getCreationTime($path));
    }

    public function testCtimeFailsOnNonexistentPath(): void
    {
        $fixtureDir = Fixture::path();
        $path = "{$fixtureDir}/nonexistent";

        $this->expectException(FilesystemException::class);

        $this->driver->getCreationTime($path);
    }

    /**
     * @group slow
     */
    public function testTouch(): void
    {
        $fixtureDir = Fixture::path();

        $touch = "{$fixtureDir}/touch";
        $this->driver->write($touch, "touch me");

        $oldStat = $this->driver->getStatus($touch);
        $this->driver->touch($touch, \time() + 10, \time() + 20);
        $newStat = $this->driver->getStatus($touch);
        $this->driver->deleteFile($touch);

        $this->assertTrue($newStat["atime"] > $oldStat["atime"]);
        $this->assertTrue($newStat["mtime"] > $oldStat["mtime"]);

        $this->driver->touch($touch);
        self::assertFileExists($touch);
    }

    /**
     * @group slow
     */
    public function testWrite(): void
    {
        $fixtureDir = Fixture::path();

        $contents1 = "write test longer";
        $contents2 = "write test";
        $path = "{$fixtureDir}/write.txt";

        $this->driver->write($path, $contents1);
        $this->assertSame($contents1, $this->driver->read($path));

        $this->driver->write($path, $contents2);
        $this->assertSame($contents2, $this->driver->read($path));

        $this->driver->write($path, $contents1);
        $this->assertSame($contents1, $this->driver->read($path));
    }

    public function testTouchFailsOnNonexistentPath(): void
    {
        $fixtureDir = Fixture::path();
        $path = "{$fixtureDir}/nonexistent/nonexistent";

        $this->expectException(FilesystemException::class);

        $this->driver->touch($path);
    }

    public function testChangePermissions(): void
    {
        $fixtureDir = Fixture::path();

        $path = "{$fixtureDir}/file";
        $stat = $this->driver->getStatus($path);
        $this->assertNotSame('0777', \substr(\decoct($stat['mode']), -4));
        $this->driver->changePermissions($path, 0777);
        $stat = $this->driver->getStatus($path);

        if (IS_WINDOWS) {
            $this->assertSame('0666', \substr(\decoct($stat['mode']), -4));
        } else {
            $this->assertSame('0777', \substr(\decoct($stat['mode']), -4));
        }
    }

    public function testChangePermissionsFailsOnNonexistentPath(): void
    {
        $fixtureDir = Fixture::path();
        $path = "{$fixtureDir}/nonexistent";

        $this->expectException(FilesystemException::class);

        $this->driver->changePermissions($path, 0777);
    }

    public function testChangeOwner(): void
    {
        if (IS_WINDOWS) {
            $this->markTestSkipped('Not supported on Windows');
        }

        $fixtureDir = Fixture::path();

        $path = "{$fixtureDir}/file";
        $this->driver->getStatus($path);
        $user = \fileowner($path);
        $this->driver->changeOwner($path, $user);
        self::assertSame($user, \fileowner($path));
    }

    public function testChangeOwnerFailsOnNonexistentPath(): void
    {
        if (IS_WINDOWS) {
            $this->markTestSkipped('Not supported on Windows');
        }

        $fixtureDir = Fixture::path();
        $path = "{$fixtureDir}/nonexistent";

        $this->expectException(FilesystemException::class);

        $this->driver->changeOwner($path, 0);
    }

    protected function setUp(): void
    {
        parent::setUp();

        $this->driver = new File\Filesystem($this->createDriver());
    }

    abstract protected function createDriver(): File\FilesystemDriver;

    private function assertSameStatus(array $expected, array $actual): void
    {
        $filter = function (array $stat) {
            $filtered = \array_filter(
                $stat,
                function (string $key): bool {
                    return !\is_numeric($key);
                },
                ARRAY_FILTER_USE_KEY
            );

            \ksort($filtered);

            return $filtered;
        };

        $this->assertSame($filter($expected), $filter($actual));
    }

    private function getPermissionsFromStatus(array $stat): string
    {
        return \substr(\decoct($stat["mode"]), 1);
    }
}
<?php declare(strict_types=1);

namespace Amp\File\Test;

use Amp\PHPUnit\AsyncTestCase;

abstract class FilesystemTest extends AsyncTestCase
{
    protected function setUp(): void
    {
        parent::setUp();
        Fixture::init();
    }

    protected function tearDown(): void
    {
        parent::tearDown();
        Fixture::clear();
    }
}
<?php declare(strict_types=1);

namespace Amp\File\Test;

use const Amp\Process\IS_WINDOWS;

final class Fixture
{
    private static string $fixtureId;

    public static function path(): string
    {
        if (empty(self::$fixtureId)) {
            self::$fixtureId = \uniqid('amphp-test-', true);
        }

        return \sys_get_temp_dir() . "/amphp_file_fixture/" . \str_replace("\\", ".", __CLASS__) . self::$fixtureId;
    }

    public static function init(): void
    {
        $fixtureDir = self::path();
        self::clear();
        if (!\mkdir($fixtureDir, $mode = 0777, $recursive = true)) {
            throw new \RuntimeException(
                "Failed creating temporary test fixture directory: {$fixtureDir}"
            );
        }
        if (!\mkdir($fixtureDir . "/dir", $mode = 0777, $recursive = true)) {
            throw new \RuntimeException(
                "Failed creating temporary test fixture directory"
            );
        }
        if (!\file_put_contents($fixtureDir . "/file", "small")) {
            throw new \RuntimeException(
                "Failed creating temporary test fixture file"
            );
        }
        if (!\symlink($fixtureDir . "/dir", $fixtureDir . "/dirlink")) {
            throw new \RuntimeException(
                "Failed creating temporary test fixture symlink to directory"
            );
        }
        if (!\symlink($fixtureDir . "/file", $fixtureDir . "/filelink")) {
            throw new \RuntimeException(
                "Failed creating temporary test fixture symlink to file"
            );
        }
        if (!IS_WINDOWS) {
            if (!\symlink($fixtureDir . "/linkloop", $fixtureDir . "/linkloop")) {
                throw new \RuntimeException(
                    "Failed creating temporary test fixture symlink loop"
                );
            }
        }
        if (\extension_loaded('posix')) {
            if (!\posix_mkfifo($fixtureDir . "/fifo", 0777)) {
                throw new \RuntimeException(
                    "Failed creating temporary test fixture fifo"
                );
            }
            if (!\symlink($fixtureDir . "/fifo", $fixtureDir . "/fifolink")) {
                throw new \RuntimeException(
                    "Failed creating temporary test fixture symlink to file"
                );
            }
        }
    }

    public static function clear(): void
    {
        \clearstatcache(true);

        $fixtureDir = self::path();
        if (!\file_exists($fixtureDir)) {
            return;
        }

        if (\stripos(\PHP_OS, "win") === 0) {
            \system('rd /Q /S "' . $fixtureDir . '"');
        } else {
            \system('/bin/rm -rf ' . \escapeshellarg($fixtureDir));
        }

        \clearstatcache(true);
    }
}
<?php declare(strict_types=1);

namespace Amp\File\Test\Driver;

use Amp\File;
use Amp\File\Driver\UvFilesystemDriver;
use Amp\File\Test\FilesystemDriverTest;
use Revolt\EventLoop;
use Revolt\EventLoop\Driver\UvDriver as UvLoopDriver;

class UvFilesystemDriverTest extends FilesystemDriverTest
{
    /**
     * @dataProvider symlinkPathProvider
     *
     */
    public function testResolveSymlinkError(\Closure $linkResolver): void
    {
        if (\version_compare(\phpversion('uv'), '0.3.0', '<')) {
            $this->markTestSkipped('UvDriver Test Skipped: Causes Crash');
        }

        parent::testResolveSymlinkError($linkResolver);
    }

    protected function createDriver(): File\FilesystemDriver
    {
        if (!\extension_loaded("uv")) {
            $this->markTestSkipped("ext-uv not loaded");
        }

        $loop = EventLoop::getDriver();

        if (!$loop instanceof UvLoopDriver) {
            $this->markTestSkipped("Loop driver must be using ext-uv");
        }

        return new UvFilesystemDriver($loop);
    }
}
<?php declare(strict_types=1);

namespace Amp\File\Test\Driver;

use Amp\File;
use Amp\File\Driver\UvFilesystemDriver;
use Amp\File\Test\AsyncFileTest;
use Revolt\EventLoop;
use Revolt\EventLoop\Driver\UvDriver as UvLoopDriver;

class UvFileTest extends AsyncFileTest
{
    protected function createDriver(): File\FilesystemDriver
    {
        if (!\extension_loaded("uv")) {
            $this->markTestSkipped("ext-uv not loaded");
        }

        $loop = EventLoop::getDriver();

        if (!$loop instanceof UvLoopDriver) {
            $this->markTestSkipped("Loop driver must be using ext-uv");
        }

        return new UvFilesystemDriver($loop);
    }
}
<?php declare(strict_types=1);

namespace Amp\File\Test\Driver;

use Amp\File;
use Amp\File\Driver\BlockingFilesystemDriver;
use Amp\File\Test\FileTest;

class BlockingFileTest extends FileTest
{
    protected function createDriver(): File\FilesystemDriver
    {
        return new BlockingFilesystemDriver;
    }
}
<?php declare(strict_types=1);

namespace Amp\File\Test\Driver;

use Amp\File;
use Amp\File\Driver\BlockingFilesystemDriver;
use Amp\File\Test\FilesystemDriverTest;

class StatusCachingFilesystemDriverTest extends FilesystemDriverTest
{
    protected function createDriver(): File\FilesystemDriver
    {
        return new File\Driver\StatusCachingFilesystemDriver(new BlockingFilesystemDriver);
    }
}
<?php declare(strict_types=1);

namespace Amp\File\Test\Driver;

use Amp\File;
use Amp\File\Driver\ParallelFilesystemDriver;
use Amp\File\Test\AsyncFileTest;
use Amp\Parallel\Worker\ContextWorkerPool;
use Amp\Parallel\Worker\WorkerPool;

class ParallelFileTest extends AsyncFileTest
{
    private const DEFAULT_WORKER_LIMIT = 8;

    private WorkerPool $pool;

    protected function createDriver(int $workerLimit = self::DEFAULT_WORKER_LIMIT): File\FilesystemDriver
    {
        $this->pool = new ContextWorkerPool($workerLimit);

        return new ParallelFilesystemDriver($this->pool);
    }

    protected function tearDown(): void
    {
        $this->pool->shutdown();
    }

    public function getWorkerLimits(): iterable
    {
        return \array_map(fn (int $count): array => [$count], \range(4, 16, 4));
    }

    /**
     * @dataProvider getWorkerLimits
     */
    public function testMultipleOpenFiles(int $maxCount)
    {
        $driver = $this->createDriver($maxCount);

        $files = [];
        for ($i = 0; $i < $maxCount * 2; ++$i) {
            $files[] = $driver->openFile(__FILE__, 'r');
        }

        foreach ($files as $file) {
            $file->close();
        }

        $this->assertSame($maxCount, $this->pool->getWorkerCount());
    }
}
<?php declare(strict_types=1);

namespace Amp\File\Test\Driver;

use Amp\File;
use Amp\File\Driver\ParallelFilesystemDriver;
use Amp\File\Test\FilesystemDriverTest;
use Amp\Parallel\Worker\ContextWorkerPool;
use Amp\Parallel\Worker\WorkerPool;

class ParallelFilesystemDriverTest extends FilesystemDriverTest
{
    private WorkerPool $pool;

    protected function createDriver(): File\FilesystemDriver
    {
        $this->pool = new ContextWorkerPool();

        return new ParallelFilesystemDriver($this->pool);
    }

    protected function tearDown(): void
    {
        $this->pool->shutdown();
    }
}
<?php declare(strict_types=1);

namespace Amp\File\Test\Driver;

use Amp\File;
use Amp\File\Driver\EioFilesystemDriver;
use Amp\File\Test\AsyncFileTest;
use Revolt\EventLoop;

class EioFileTest extends AsyncFileTest
{
    protected function createDriver(): File\FilesystemDriver
    {
        if (!\extension_loaded("eio")) {
            $this->markTestSkipped("eio extension not loaded");
        }

        return new EioFilesystemDriver(EventLoop::getDriver());
    }
}
<?php declare(strict_types=1);

namespace Amp\File\Test\Driver;

use Amp\File;
use Amp\File\Driver\BlockingFilesystemDriver;
use Amp\File\Test\FilesystemDriverTest;

class BlockingFilesystemDriverTest extends FilesystemDriverTest
{
    protected function createDriver(): File\FilesystemDriver
    {
        return new BlockingFilesystemDriver;
    }
}
<?php declare(strict_types=1);

namespace Amp\File\Test\Driver;

use Amp\File;
use Amp\File\Driver\BlockingFilesystemDriver;
use Amp\File\Test\FileTest;

class StatusCachingFileTest extends FileTest
{
    protected function createDriver(): File\FilesystemDriver
    {
        return new File\Driver\StatusCachingFilesystemDriver(new BlockingFilesystemDriver);
    }
}
<?php declare(strict_types=1);

namespace Amp\File\Test\Driver;

use Amp\File;
use Amp\File\Driver\EioFilesystemDriver;
use Amp\File\Test\FilesystemDriverTest;
use Revolt\EventLoop;

class EioFilesystemDriverTest extends FilesystemDriverTest
{
    protected function createDriver(): File\FilesystemDriver
    {
        if (!\extension_loaded("eio")) {
            $this->markTestSkipped("eio extension not loaded");
        }

        return new EioFilesystemDriver(EventLoop::getDriver());
    }
}
<?php declare(strict_types=1);

namespace Amp\File\Test;

use Amp\File\KeyedFileMutex;
use Amp\Sync\AbstractKeyedMutexTest;
use Amp\Sync\KeyedMutex;

final class KeyedFileMutexTest extends AbstractKeyedMutexTest
{
    protected function setUp(): void
    {
        parent::setUp();
        Fixture::init();
    }

    protected function tearDown(): void
    {
        parent::tearDown();
        Fixture::clear();
    }

    public function createMutex(): KeyedMutex
    {
        return new KeyedFileMutex(Fixture::path());
    }
}
<?php declare(strict_types=1);

namespace Amp\File\Test;

use Amp\ByteStream\ClosedException;
use Amp\File;
use Amp\File\Whence;

use function Amp\async;

abstract class FileTest extends FilesystemTest
{
    protected File\FilesystemDriver $driver;

    public function testWrite(): void
    {
        $path = Fixture::path() . "/write";
        $handle = $this->driver->openFile($path, "c+");
        $this->assertSame(0, $handle->tell());

        $handle->write("foo");
        $handle->write("bar");
        $handle->seek(0);
        $contents = $handle->read();
        $this->assertSame(6, $handle->tell());
        $this->assertTrue($handle->eof());
        $this->assertSame("foobar", $contents);

        $handle->close();

        $handle = $this->driver->openFile($path, "c+");
        $handle->seek(0, Whence::End);
        $this->assertSame(6, $handle->tell());

        $handle->close();
    }

    public function testWriteTruncate(): void
    {
        $path = Fixture::path() . "/write";
        $handle = $this->driver->openFile($path, "c+");
        $this->assertSame(0, $handle->tell());

        $handle->write("foo");
        $handle->write("bar");
        $handle->seek(0);
        $contents = $handle->read();
        $this->assertSame(6, $handle->tell());
        $this->assertTrue($handle->eof());
        $this->assertSame("foobar", $contents);

        $handle->close();

        $handle = $this->driver->openFile($path, "w+");
        $handle->seek(0, Whence::End);
        $this->assertSame(0, $handle->tell());

        $handle->close();
    }

    public function testEmptyWrite(): void
    {
        $path = Fixture::path() . "/write";

        $handle = $this->driver->openFile($path, "c+");
        $this->assertSame(0, $handle->tell());

        $handle->write("");
        $this->assertSame(0, $handle->tell());

        $handle->close();
    }

    public function testWriteAfterClose(): void
    {
        $path = Fixture::path() . "/write";
        $handle = $this->driver->openFile($path, "c+");
        $handle->close();

        $this->expectException(ClosedException::class);
        $handle->write("bar");
    }

    public function testDoubleClose(): void
    {
        $path = Fixture::path() . "/write";
        /** @var File\File $handle */
        $handle = $this->driver->openFile($path, "c+");
        $handle->close();
        $handle->close();

        $this->expectNotToPerformAssertions();
    }

    public function testWriteAfterEnd(): void
    {
        $path = Fixture::path() . "/write";
        $handle = $this->driver->openFile($path, "c+");
        $this->assertSame(0, $handle->tell());
        $handle->end("foo");

        $this->expectException(ClosedException::class);
        $handle->write("bar");
    }

    public function testWriteInAppendMode(): void
    {
        $path = Fixture::path() . "/write";
        $this->driver->write($path, 'previous');
        $handle = $this->driver->openFile($path, "a+");
        $this->assertSame(8, $handle->tell());
        $handle->write("bar");
        $handle->write("foo");
        $handle->write("baz");
        $this->assertSame(17, $handle->tell());
        $handle->seek(0);
        $this->assertSame(0, $handle->tell());
        $this->assertSame("previousbarfoobaz", $handle->read());
    }

    public function testReadInAppendMode(): void
    {
        $path = Fixture::path() . "/read-append";
        $this->driver->write($path, 'previous');
        $handle = $this->driver->openFile($path, "a+");
        $this->assertSame(8, $handle->tell());
        $this->assertNull($handle->read());
        $this->assertSame(0, $handle->seek(0));
        $this->assertSame('previous', $handle->read());
    }

    public function testReadingToEnd(): void
    {
        $handle = $this->driver->openFile(__FILE__, "r");
        $contents = "";
        $position = 0;

        $stat = $this->driver->getStatus(__FILE__);
        $chunkSize = (int) \floor(($stat["size"] / 5));

        while (!$handle->eof()) {
            $chunk = $handle->read(length: $chunkSize);
            $contents .= $chunk;
            $position += \strlen($chunk ?? '');
            $this->assertSame($position, $handle->tell());
        }

        $this->assertNull($handle->read());
        $this->assertSame($this->driver->read(__FILE__), $contents);

        $handle->close();
    }

    public function testSequentialReads(): void
    {
        $handle = $this->driver->openFile(__FILE__, "r");

        $contents = "";
        $contents .= $handle->read(length: 10);
        $contents .= $handle->read(length: 10);

        $expected = \substr($this->driver->read(__FILE__), 0, 20);
        $this->assertSame($expected, $contents);

        $handle->close();
    }

    public function testReadingFromOffset(): void
    {
        $handle = $this->driver->openFile(__FILE__, "r");
        $this->assertSame(0, $handle->tell());
        $handle->seek(10);
        $this->assertSame(10, $handle->tell());
        $chunk = $handle->read(length: 90);
        $this->assertSame(100, $handle->tell());
        $expected = \substr($this->driver->read(__FILE__), 10, 90);
        $this->assertSame($expected, $chunk);

        $handle->close();
    }

    public function testSeekThrowsOnInvalidWhence(): void
    {
        $this->expectException(\Error::class);

        $handle = $this->driver->openFile(__FILE__, "r");

        try {
            $handle->seek(0, 99999);
        } finally {
            $handle->close();
        }
    }

    public function testSeekSetCur(): void
    {
        $handle = $this->driver->openFile(__FILE__, "r");
        $this->assertSame(0, $handle->tell());
        $handle->seek(10);
        $this->assertSame(10, $handle->tell());
        $handle->seek(-10, File\Whence::Current);
        $this->assertSame(0, $handle->tell());
        $handle->close();
    }

    public function testSeekSetEnd(): void
    {
        $size = \filesize(__FILE__);
        $handle = $this->driver->openFile(__FILE__, "r");
        $this->assertSame(0, $handle->tell());
        $handle->seek(-10, File\Whence::End);
        $this->assertSame($size - 10, $handle->tell());
        $handle->close();
    }

    public function testPath(): void
    {
        $handle = $this->driver->openFile(__FILE__, "r");
        $this->assertSame(__FILE__, $handle->getPath());
        $handle->close();
    }

    public function testMode(): void
    {
        $handle = $this->driver->openFile(__FILE__, "r");
        $this->assertSame("r", $handle->getMode());
        $this->assertFalse($handle->isWritable());
        $handle->close();
    }

    public function testClose(): void
    {
        $handle = $this->driver->openFile(__FILE__, "r");
        $handle->close();

        $this->expectException(ClosedException::class);
        $handle->read();
    }

    public function testCloseWithPendingWrites(): void
    {
        $path = Fixture::path() . "/write";

        $handle = $this->driver->openFile($path, "c+");

        $data = "data";
        $writeFuture = async($handle->write(...), $data);
        $closeFuture = async($handle->close(...));

        $writeFuture->await();
        $closeFuture->await();

        $this->assertSame($data, $this->driver->read($path));
    }

    /**
     * @depends testWrite
     */
    public function testTruncateToSmallerSize(): void
    {
        $path = Fixture::path() . "/write";
        $handle = $this->driver->openFile($path, "c+");

        $handle->write("foo");
        $handle->write("bar");
        $handle->truncate(4);
        $handle->seek(0);
        $contents = $handle->read();
        $this->assertTrue($handle->eof());
        $this->assertSame("foob", $contents);

        $handle->write("bar");
        $this->assertSame(7, $handle->tell());
        $handle->seek(0);
        $contents = $handle->read();
        $this->assertSame("foobbar", $contents);

        $handle->close();
    }

    /**
     * @depends testWrite
     */
    public function testTruncateToLargerSize(): void
    {
        $path = Fixture::path() . "/write";
        $handle = $this->driver->openFile($path, "c+");

        $handle->write("foo");
        $handle->truncate(6);
        $this->assertSame(3, $handle->tell());
        $handle->seek(0);
        $contents = $handle->read();
        $this->assertTrue($handle->eof());
        $this->assertSame("foo\0\0\0", $contents);

        $handle->write("bar");
        $this->assertSame(9, $handle->tell());
        $handle->seek(0);
        $contents = $handle->read();
        $this->assertSame("foo\0\0\0bar", $contents);

        $handle->close();
    }

    abstract protected function createDriver(): File\FilesystemDriver;

    protected function setUp(): void
    {
        parent::setUp();

        $this->driver = $this->createDriver();
    }
}
<?php declare(strict_types=1);

namespace Amp\File\Test;

use Amp\File\FileMutex;
use Amp\Sync\AbstractMutexTest;
use Amp\Sync\Mutex;

final class FileMutexTest extends AbstractMutexTest
{
    public function createMutex(): Mutex
    {
        return new FileMutex(\tempnam(\sys_get_temp_dir(), 'mutex-') . '.lock');
    }
}
<?php declare(strict_types=1);

namespace Amp\File\Test;

use Amp\CancelledException;
use Amp\DeferredCancellation;
use Amp\File;
use Amp\File\PendingOperationError;
use function Amp\async;

abstract class AsyncFileTest extends FileTest
{
    public function testSimultaneousReads()
    {
        $this->expectException(PendingOperationError::class);

        $handle = $this->driver->openFile(__FILE__, "r");

        $future1 = async(fn () => $handle->read(length: 20));
        $future2 = async(fn () => $handle->read(length: 20));

        $expected = \substr(File\read(__FILE__), 0, 20);
        $this->assertSame($expected, $future1->await());

        $future2->await();
    }

    public function testSeekWhileReading()
    {
        $this->expectException(PendingOperationError::class);

        $handle = $this->driver->openFile(__FILE__, "r");

        $future1 = async(fn () => $handle->read(length: 10));
        $future2 = async(fn () => $handle->read(length: 0));

        $expected = \substr(File\read(__FILE__), 0, 10);
        $this->assertSame($expected, $future1->await());

        $future2->await();
    }

    public function testReadWhileWriting()
    {
        $this->expectException(PendingOperationError::class);

        $path = Fixture::path() . "/temp";

        $handle = $this->driver->openFile($path, "c+");

        $data = "test";

        $future1 = async(fn () => $handle->write($data));
        $future2 = async(fn () => $handle->read(length: 10));

        $this->assertNull($future1->await());

        $future2->await();
    }

    public function testWriteWhileReading()
    {
        $this->expectException(PendingOperationError::class);

        $path = Fixture::path() . "/temp";

        $handle = $this->driver->openFile($path, "c+");

        $future1 = async(fn () => $handle->read(length: 10));
        $future2 = async(fn () => $handle->write("test"));

        $this->assertNull($future1->await());

        $future2->await();
    }

    public function testCancelReadThenReadAgain()
    {
        $path = Fixture::path() . "/temp";

        $handle = $this->driver->openFile($path, "c+");

        $deferredCancellation = new DeferredCancellation();
        $deferredCancellation->cancel();

        $handle->write("test");
        $handle->seek(0);

        try {
            $handle->read(cancellation: $deferredCancellation->getCancellation(), length: 2);
            $handle->seek(0); // If the read succeeds (e.g.: ParallelFile), we need to seek back to 0.
        } catch (CancelledException) {
        }

        $this->assertSame("test", $handle->read());
    }
}
<?php declare(strict_types=1);

namespace Amp\File;

class FilesystemException extends \Exception
{
    public function __construct(string $message, ?\Throwable $previous = null)
    {
        parent::__construct($message, 0, $previous);
    }
}
<?php declare(strict_types=1);

namespace Amp\File;

use Amp\Cancellation;
use Amp\Sync\Lock;
use Amp\Sync\Mutex;
use Amp\Sync\SyncException;
use function Amp\delay;

final class FileMutex implements Mutex
{
    private const LATENCY_TIMEOUT = 0.01;
    private const DELAY_LIMIT = 1;

    private readonly Filesystem $filesystem;

    private readonly string $directory;

    /**
     * @param string $fileName Name of temporary file to use as a mutex.
     */
    public function __construct(private readonly string $fileName, ?Filesystem $filesystem = null)
    {
        $this->filesystem = $filesystem ?? filesystem();
        $this->directory = \dirname($this->fileName);
    }

    public function acquire(?Cancellation $cancellation = null): Lock
    {
        if (!$this->filesystem->isDirectory($this->directory)) {
            throw new SyncException(\sprintf('Directory of "%s" does not exist or is not a directory', $this->fileName));
        }

        // Try to create the lock file. If the file already exists, someone else
        // has the lock, so set an asynchronous timer and try again.
        for ($attempt = 0; true; ++$attempt) {
            try {
                $file = $this->filesystem->openFile($this->fileName, 'x');

                // Return a lock object that can be used to release the lock on the mutex.
                $lock = new Lock($this->release(...));

                $file->close();

                return $lock;
            } catch (FilesystemException) {
                delay(\min(self::DELAY_LIMIT, self::LATENCY_TIMEOUT * (2 ** $attempt)), cancellation: $cancellation);
            }
        }
    }

    /**
     * Releases the lock on the mutex.
     *
     * @throws SyncException
     */
    private function release(): void
    {
        try {
            $this->filesystem->deleteFile($this->fileName);
        } catch (\Throwable $exception) {
            throw new SyncException(
                'Failed to unlock the mutex file: ' . $this->fileName,
                previous: $exception,
            );
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\File\Driver;

use Amp\DeferredFuture;
use Amp\File\FilesystemDriver;
use Amp\File\FilesystemException;
use Amp\File\Internal;
use Revolt\EventLoop\Driver as EventLoopDriver;

final class UvFilesystemDriver implements FilesystemDriver
{
    /**
     * @param EventLoopDriver $driver The currently active loop driver.
     *
     * @return bool Determines if this driver can be used based on the environment.
     */
    public static function isSupported(EventLoopDriver $driver): bool
    {
        $uvVersion = \phpversion('uv');

        return $uvVersion && \version_compare($uvVersion, '0.3.0', '>=') && $driver->getHandle() instanceof \UVLoop;
    }

    private readonly \UVLoop $eventLoopHandle;

    private readonly Internal\UvPoll $poll;

    public function __construct(private readonly EventLoopDriver $driver)
    {
        if (!self::isSupported($driver)) {
            throw new \Error('Event loop did not return a compatible handle');
        }

        /** @psalm-suppress PropertyTypeCoercion */
        $this->eventLoopHandle = $driver->getHandle();
        $this->poll = new Internal\UvPoll($driver);
    }

    public function openFile(string $path, string $mode): UvFile
    {
        $flags = $this->parseMode($mode);
        $chmod = ($flags & \UV::O_CREAT) ? 0644 : 0;

        $deferred = new DeferredFuture;
        $this->poll->listen();

        \uv_fs_open($this->eventLoopHandle, $path, $flags, $chmod, function ($fileHandle) use ($mode, $path, $deferred): void {
            if (\is_resource($fileHandle)) {
                $this->onOpenHandle($fileHandle, $mode, $path, $deferred);
            } else {
                $deferred->error(new FilesystemException("Failed opening file '{$path}'"));
            }
        });

        try {
            return $deferred->getFuture()->await();
        } finally {
            $this->poll->done();
        }
    }

    public function getStatus(string $path): ?array
    {
        $deferred = new DeferredFuture;
        $this->poll->listen();

        $callback = static function ($stat) use ($deferred, $path): void {
            if (\is_int($stat)) {
                $deferred->complete(null);
                return;
            }

            // link is not a valid stat type but returned by the uv extension
            // change link to nlink
            if (isset($stat['link'])) {
                $stat['nlink'] = $stat['link'];

                unset($stat['link']);
            }

            $deferred->complete($stat);
        };

        \uv_fs_stat($this->eventLoopHandle, $path, $callback);

        try {
            return $deferred->getFuture()->await();
        } finally {
            $this->poll->done();
        }
    }

    public function getLinkStatus(string $path): ?array
    {
        $deferred = new DeferredFuture;
        $this->poll->listen();

        \uv_fs_lstat($this->eventLoopHandle, $path, static function ($stat) use ($deferred): void {
            $deferred->complete(\is_int($stat) ? null : $stat);
        });

        try {
            return $deferred->getFuture()->await();
        } finally {
            $this->poll->done();
        }
    }

    public function createSymlink(string $target, string $link): void
    {
        try {
            $this->poll->listen();

            $deferred = new DeferredFuture;
            $callback = $this->createGenericCallback($deferred, "Could not create symbolic link");
            \uv_fs_symlink($this->eventLoopHandle, $target, $link, \UV::S_IRWXU | \UV::S_IRUSR, $callback);

            $deferred->getFuture()->await();
        } finally {
            $this->poll->done();
        }
    }

    public function createHardlink(string $target, string $link): void
    {
        $deferred = new DeferredFuture;
        $this->poll->listen();

        \uv_fs_link($this->eventLoopHandle, $target, $link, $this->createGenericCallback($deferred, "Could not create hard link"));

        try {
            $deferred->getFuture()->await();
        } finally {
            $this->poll->done();
        }
    }

    public function resolveSymlink(string $target): string
    {
        $deferred = new DeferredFuture;
        $this->poll->listen();

        \uv_fs_readlink($this->eventLoopHandle, $target, static function ($target) use ($deferred): void {
            if (\is_int($target)) {
                $deferred->error(new FilesystemException("Could not read symbolic link"));
                return;
            }

            $deferred->complete($target);
        });

        try {
            return $deferred->getFuture()->await();
        } finally {
            $this->poll->done();
        }
    }

    public function move(string $from, string $to): void
    {
        $deferred = new DeferredFuture;
        $this->poll->listen();

        \uv_fs_rename($this->eventLoopHandle, $from, $to, $this->createGenericCallback($deferred, "Could not rename file"));

        try {
            $deferred->getFuture()->await();
        } finally {
            $this->poll->done();
        }
    }

    public function deleteFile(string $path): void
    {
        $deferred = new DeferredFuture;
        $this->poll->listen();

        \uv_fs_unlink($this->eventLoopHandle, $path, $this->createGenericCallback($deferred, "Could not unlink file"));

        try {
            $deferred->getFuture()->await();
        } finally {
            $this->poll->done();
        }
    }

    public function createDirectory(string $path, int $mode = 0777): void
    {
        $deferred = new DeferredFuture;
        $this->poll->listen();

        \uv_fs_mkdir($this->eventLoopHandle, $path, $mode, $this->createGenericCallback($deferred, "Could not create directory"));

        try {
            $deferred->getFuture()->await();
        } finally {
            $this->poll->done();
        }
    }

    public function createDirectoryRecursively(string $path, int $mode = 0777): void
    {
        $deferred = new DeferredFuture;
        $this->poll->listen();

        $path = \str_replace("/", DIRECTORY_SEPARATOR, $path);
        $path = \rtrim($path, DIRECTORY_SEPARATOR);
        $arrayPath = \explode(DIRECTORY_SEPARATOR, $path);
        $tmpPath = "";

        $callback = function () use (
            &$callback,
            &$arrayPath,
            &$tmpPath,
            $mode,
            $deferred
        ): void {
            $tmpPath .= DIRECTORY_SEPARATOR . \array_shift($arrayPath);

            if (empty($arrayPath)) {
                \uv_fs_mkdir(
                    $this->eventLoopHandle,
                    $tmpPath,
                    $mode,
                    $this->createGenericCallback($deferred, "Could not create directory")
                );
            } else {
                \uv_fs_mkdir($this->eventLoopHandle, $tmpPath, $mode, $callback);
            }
        };

        $callback();

        try {
            $deferred->getFuture()->await();
        } catch (FilesystemException $exception) {
            $result = $this->getStatus($path);
            if ($result !== null && ($result['mode'] & 0040000)) {
                return;
            }

            throw $exception;
        } finally {
            $this->poll->done();
        }
    }

    public function deleteDirectory(string $path): void
    {
        $deferred = new DeferredFuture;
        $this->poll->listen();

        \uv_fs_rmdir($this->eventLoopHandle, $path, $this->createGenericCallback($deferred, "Could not remove directory"));

        try {
            $deferred->getFuture()->await();
        } finally {
            $this->poll->done();
        }
    }

    public function listFiles(string $path): array
    {
        $deferred = new DeferredFuture;
        $this->poll->listen();

        /** @noinspection PhpUndefinedFunctionInspection */
        \uv_fs_scandir($this->eventLoopHandle, $path, static function ($data) use ($deferred, $path): void {
            if (\is_int($data) && $data !== 0) {
                $deferred->error(new FilesystemException("Failed reading contents from {$path}"));
            } elseif ($data === 0) {
                $deferred->complete([]);
            } else {
                $deferred->complete($data);
            }
        });

        try {
            return $deferred->getFuture()->await();
        } finally {
            $this->poll->done();
        }
    }

    public function changePermissions(string $path, int $mode): void
    {
        $deferred = new DeferredFuture;
        $this->poll->listen();

        $callback = $this->createGenericCallback($deferred, "Could not change file permissions");
        \uv_fs_chmod($this->eventLoopHandle, $path, $mode, $callback);

        try {
            $deferred->getFuture()->await();
        } finally {
            $this->poll->done();
        }
    }

    public function changeOwner(string $path, ?int $uid, ?int $gid): void
    {
        // @TODO Return a failure in windows environments
        $deferred = new DeferredFuture;
        $this->poll->listen();

        $callback = $this->createGenericCallback($deferred, "Could not change file owner");
        \uv_fs_chown($this->eventLoopHandle, $path, $uid ?? -1, $gid ?? -1, $callback);

        try {
            $deferred->getFuture()->await();
        } finally {
            $this->poll->done();
        }
    }

    public function touch(string $path, ?int $modificationTime, ?int $accessTime): void
    {
        $modificationTime = $modificationTime ?? \time();
        $accessTime = $accessTime ?? $modificationTime;

        if (!$this->getStatus($path)) {
            $this->openFile($path, 'c')->close();
        }

        $deferred = new DeferredFuture;
        $this->poll->listen();

        $callback = $this->createGenericCallback($deferred, "Could not touch file");
        \uv_fs_utime($this->eventLoopHandle, $path, $modificationTime, $accessTime, $callback);

        try {
            $deferred->getFuture()->await();
        } finally {
            $this->poll->done();
        }
    }

    public function read(string $path): string
    {
        $this->poll->listen();

        $fileHandle = $this->doFsOpen($path, flags: \UV::O_RDONLY, mode: 0);
        if (!$fileHandle) {
            throw new FilesystemException("Failed opening file handle: {$path}");
        }

        $deferred = new DeferredFuture;

        $stat = $this->doFsStat($fileHandle);

        if (empty($stat)) {
            $deferred->error(new FilesystemException("stat operation failed on open file handle"));
        } elseif (!$stat["isfile"]) {
            \uv_fs_close($this->eventLoopHandle, $fileHandle, static function () use ($deferred): void {
                $deferred->error(new FilesystemException("cannot buffer contents: path is not a file"));
            });
        } else {
            $buffer = $this->doFsRead($fileHandle, $stat["size"]);

            if ($buffer === null) {
                \uv_fs_close($this->eventLoopHandle, $fileHandle, static function () use ($deferred): void {
                    $deferred->error(new FilesystemException("read operation failed on open file handle"));
                });
            } else {
                \uv_fs_close($this->eventLoopHandle, $fileHandle, static function () use ($deferred, $buffer): void {
                    $deferred->complete($buffer);
                });
            }
        }

        try {
            return $deferred->getFuture()->await();
        } finally {
            $this->poll->done();
        }
    }

    public function write(string $path, string $contents): void
    {
        $flags = \UV::O_WRONLY | \UV::O_CREAT | \UV::O_TRUNC;
        $mode = \UV::S_IRWXU | \UV::S_IRUSR;

        $this->poll->listen();

        $fh = $this->doFsOpen($path, $flags, $mode);
        if (!$fh) {
            throw new FilesystemException("Failed opening write file handle");
        }

        $deferred = new DeferredFuture;

        \uv_fs_write($this->eventLoopHandle, $fh, $contents, 0, function ($fh, $result) use ($deferred): void {
            \uv_fs_close($this->eventLoopHandle, $fh, static function () use ($deferred, $result): void {
                if ($result < 0) {
                    $deferred->error(new FilesystemException(\uv_strerror($result)));
                } else {
                    $deferred->complete(null);
                }
            });
        });

        try {
            $deferred->getFuture()->await();
        } finally {
            $this->poll->done();
        }
    }

    private function parseMode(string $mode): int
    {
        $mode = \str_replace(['b', 't', 'e'], '', $mode);

        return match ($mode) {
            "r" => \UV::O_RDONLY,
            "r+" => \UV::O_RDWR,
            "c" => \UV::O_WRONLY | \UV::O_CREAT,
            "w" => \UV::O_WRONLY | \UV::O_CREAT | \UV::O_TRUNC,
            "c+" => \UV::O_RDWR | \UV::O_CREAT,
            "w+" => \UV::O_RDWR | \UV::O_CREAT | \UV::O_TRUNC,
            "a" => \UV::O_WRONLY | \UV::O_CREAT | \UV::O_APPEND,
            "a+" => \UV::O_RDWR | \UV::O_CREAT | \UV::O_APPEND,
            "x" => \UV::O_WRONLY | \UV::O_CREAT | \UV::O_EXCL,
            "x+" => \UV::O_RDWR | \UV::O_CREAT | \UV::O_EXCL,
            default => throw new \Error('Invalid file mode'),
        };
    }

    private function onOpenHandle(mixed $fileHandle, string $mode, string $path, DeferredFuture $deferred): void
    {
        if ($mode[0] === "w") {
            \uv_fs_ftruncate($this->eventLoopHandle, $fileHandle, 0, function ($fileHandle) use ($mode, $path, $deferred): void {
                if (\is_resource($fileHandle)) {
                    $this->finalizeHandle($fileHandle, 0, $mode, $path, $deferred);
                } else {
                    $deferred->error(new FilesystemException("Failed truncating file $path"));
                }
            });
        } else {
            \uv_fs_fstat($this->eventLoopHandle, $fileHandle, function ($fileHandle, $stat) use ($mode, $path, $deferred): void {
                if (\is_resource($fileHandle)) {
                    $this->finalizeHandle($fileHandle, $stat["size"], $mode, $path, $deferred);
                } else {
                    $deferred->error(new FilesystemException("Failed reading file size from open handle pointing to $path"));
                }
            });
        }
    }

    private function finalizeHandle(mixed $fileHandle, int $size, string $mode, string $path, DeferredFuture $deferred): void
    {
        $deferred->complete(new UvFile($this->driver, $this->poll, $fileHandle, $path, $mode, $size));
    }

    private function doFsOpen(string $path, int $flags, int $mode): mixed
    {
        $deferred = new DeferredFuture;

        \uv_fs_open($this->eventLoopHandle, $path, $flags, $mode, static function ($fh) use ($deferred) {
            $deferred->complete($fh);
        });

        return $deferred->getFuture()->await();
    }

    private function doFsStat(mixed $fileHandle): array
    {
        $deferred = new DeferredFuture;

        \uv_fs_fstat($this->eventLoopHandle, $fileHandle, static function ($fh, $stat) use ($deferred): void {
            if (\is_resource($fh)) {
                $stat["isdir"] = (bool) ($stat["mode"] & \UV::S_IFDIR);
                $stat["isfile"] = !$stat["isdir"];
                $deferred->complete($stat);
            } else {
                $deferred->complete();
            }
        });

        return $deferred->getFuture()->await();
    }

    private function doFsRead(mixed $fileHandle, int $length): ?string
    {
        $deferred = new DeferredFuture;

        $callback = static function ($readBytes, $buffer) use ($deferred): void {
            $deferred->complete($readBytes < 0 ? null : $buffer);
        };

        \uv_fs_read($this->eventLoopHandle, $fileHandle, 0, $length, $callback);

        return $deferred->getFuture()->await();
    }

    private function createGenericCallback(DeferredFuture $deferred, string $error): \Closure
    {
        return static function (int $result) use ($deferred, $error): void {
            if ($result !== 0) {
                $deferred->error(new FilesystemException($error));
                return;
            }

            $deferred->complete();
        };
    }
}
<?php declare(strict_types=1);

namespace Amp\File\Driver;

use Amp\ByteStream\ClosedException;
use Amp\ByteStream\StreamException;
use Amp\Cancellation;
use Amp\DeferredFuture;
use Amp\File\Internal;
use Amp\File\PendingOperationError;
use Amp\Future;

final class EioFile extends Internal\QueuedWritesFile
{
    private readonly Internal\EioPoll $poll;

    /** @var resource eio file handle. */
    private $fh;

    private ?Future $closing = null;

    private readonly DeferredFuture $onClose;

    /**
     * @param resource $fh
     */
    public function __construct(Internal\EioPoll $poll, $fh, string $path, string $mode, int $size)
    {
        parent::__construct($path, $mode, $size);

        $this->poll = $poll;
        $this->fh = $fh;

        $this->onClose = new DeferredFuture;
    }

    public function read(?Cancellation $cancellation = null, int $length = self::DEFAULT_READ_LENGTH): ?string
    {
        if ($this->isReading || !$this->queue->isEmpty()) {
            throw new PendingOperationError;
        }

        $this->isReading = true;

        $remaining = $this->size - $this->position;
        $length = \min($length, $remaining);

        $deferred = new DeferredFuture;
        $this->poll->listen();

        $onRead = function (DeferredFuture $deferred, $result, $req): void {
            $this->isReading = false;

            if ($deferred->isComplete()) {
                return;
            }

            if ($result === -1) {
                $error = \eio_get_last_error($req);
                if ($error === "Bad file descriptor") {
                    $deferred->error(new ClosedException("Reading from the file failed due to a closed handle"));
                } else {
                    $deferred->error(new StreamException("Reading from the file failed:" . $error));
                }
            } else {
                $this->position += \strlen($result);
                $deferred->complete(\strlen($result) ? $result : null);
            }
        };

        $request = \eio_read(
            $this->fh,
            $length,
            $this->position,
            \EIO_PRI_DEFAULT,
            $onRead,
            $deferred
        );

        $id = $cancellation?->subscribe(function (\Throwable $exception) use ($request, $deferred): void {
            $this->isReading = false;
            $deferred->error($exception);
            \eio_cancel($request);
        });

        try {
            return $deferred->getFuture()->await();
        } finally {
            /** @psalm-suppress PossiblyNullArgument $id is non-null if $cancellation is non-null */
            $cancellation?->unsubscribe($id);
            $this->poll->done();
        }
    }

    public function close(): void
    {
        if ($this->closing) {
            $this->closing->await();
            return;
        }

        $this->closing = $this->onClose->getFuture();
        $this->poll->listen();

        \eio_close($this->fh, \EIO_PRI_DEFAULT, static function (DeferredFuture $deferred): void {
            // Ignore errors when closing file, as the handle will become invalid anyway.
            $deferred->complete();
        }, $this->onClose);

        try {
            $this->closing->await();
        } finally {
            $this->poll->done();
        }
    }

    public function isClosed(): bool
    {
        return $this->closing !== null;
    }

    public function onClose(\Closure $onClose): void
    {
        $this->onClose->getFuture()->finally($onClose);
    }

    protected function push(string $data, int $position): Future
    {
        $length = \strlen($data);

        if ($length === 0) {
            return Future::complete();
        }

        $deferred = new DeferredFuture;
        $this->poll->listen();

        $onWrite = function (DeferredFuture $deferred, $result, $req) use ($position): void {
            if ($this->queue->isEmpty()) {
                $deferred->error(new ClosedException('No pending write, the file may have been closed'));
            }

            $this->queue->shift();

            if ($result === -1) {
                $error = \eio_get_last_error($req);
                if ($error === "Bad file descriptor") {
                    $deferred->error(new ClosedException("Writing to the file failed due to a closed handle"));
                } else {
                    $deferred->error(new StreamException("Writing to the file failed: " . $error));
                }
            } else {
                if ($this->position === $position) {
                    $this->position += $result;
                }

                $this->size = \max($this->size, $position + $result);

                $deferred->complete();
            }

            $this->poll->done();
        };

        \eio_write(
            $this->fh,
            $data,
            $length,
            $this->position,
            \EIO_PRI_DEFAULT,
            $onWrite,
            $deferred
        );

        return $deferred->getFuture();
    }

    protected function trim(int $size): Future
    {
        $deferred = new DeferredFuture;
        $this->poll->listen();

        $onTruncate = function (DeferredFuture $deferred, $result, $req) use ($size): void {
            if ($this->queue->isEmpty()) {
                $deferred->error(new ClosedException('No pending write, the file may have been closed'));
            }

            $this->queue->shift();
            if ($this->queue->isEmpty()) {
                $this->isReading = false;
            }

            if ($result === -1) {
                $error = \eio_get_last_error($req);
                if ($error === "Bad file descriptor") {
                    $deferred->error(new ClosedException("Truncating the file failed due to a closed handle"));
                } else {
                    $deferred->error(new StreamException("Truncating the file failed: " . $error));
                }
            } else {
                $this->size = $size;
                $this->poll->done();
                $deferred->complete();
            }
        };

        \eio_ftruncate(
            $this->fh,
            $size,
            \EIO_PRI_DEFAULT,
            $onTruncate,
            $deferred
        );

        return $deferred->getFuture();
    }
}
<?php declare(strict_types=1);

namespace Amp\File\Driver;

use Amp\ByteStream\ClosedException;
use Amp\ByteStream\ReadableStreamIteratorAggregate;
use Amp\ByteStream\StreamException;
use Amp\Cancellation;
use Amp\DeferredFuture;
use Amp\File\File;
use Amp\File\Internal;
use Amp\File\PendingOperationError;
use Amp\File\Whence;
use Amp\Future;
use Amp\Parallel\Worker\TaskFailureException;
use Amp\Parallel\Worker\WorkerException;
use Revolt\EventLoop;
use function Amp\async;

/**
 * @implements \IteratorAggregate<int, string>
 */
final class ParallelFile implements File, \IteratorAggregate
{
    use ReadableStreamIteratorAggregate;

    private ?int $id;

    private int $position;

    private int $size;

    /** @var bool True if an operation is pending. */
    private bool $busy = false;

    /** @var int Number of pending write operations. */
    private int $pendingWrites = 0;

    private bool $writable;

    private ?Future $closing = null;

    private readonly DeferredFuture $onClose;

    public function __construct(
        private readonly Internal\FileWorker $worker,
        int $id,
        private readonly string $path,
        int $size,
        private readonly string $mode,
    ) {
        $this->id = $id;
        $this->size = $size;
        $this->writable = $this->mode[0] !== 'r';
        $this->position = $this->mode[0] === 'a' ? $this->size : 0;

        $this->onClose = new DeferredFuture;
    }

    public function __destruct()
    {
        if ($this->id !== null && $this->worker->isRunning()) {
            $id = $this->id;
            $worker = $this->worker;
            EventLoop::queue(static fn () => $worker->execute(new Internal\FileTask('fclose', [], $id)));
        }

        if (!$this->onClose->isComplete()) {
            $this->onClose->complete();
        }
    }

    public function close(): void
    {
        if (!$this->worker->isRunning()) {
            return;
        }

        if ($this->closing) {
            $this->closing->await();
            return;
        }

        $this->writable = false;

        $this->closing = async(function (): void {
            $id = $this->id;
            $this->id = null;
            $this->worker->execute(new Internal\FileTask('fclose', [], $id));
        });

        try {
            $this->closing->await();
        } finally {
            $this->onClose->complete();
        }
    }

    public function isClosed(): bool
    {
        return $this->closing !== null;
    }

    public function onClose(\Closure $onClose): void
    {
        $this->onClose->getFuture()->finally($onClose);
    }

    public function truncate(int $size): void
    {
        if ($this->id === null) {
            throw new ClosedException("The file has been closed");
        }

        if ($this->busy) {
            throw new PendingOperationError;
        }

        if (!$this->writable) {
            throw new ClosedException("The file is no longer writable");
        }

        ++$this->pendingWrites;
        $this->busy = true;

        try {
            $this->worker->execute(new Internal\FileTask('ftruncate', [$size], $this->id));
            $this->size = $size;
        } catch (TaskFailureException $exception) {
            throw new StreamException("Reading from the file failed", 0, $exception);
        } catch (WorkerException $exception) {
            throw new StreamException("Sending the task to the worker failed", 0, $exception);
        } finally {
            if (--$this->pendingWrites === 0) {
                $this->busy = false;
            }
        }
    }

    public function eof(): bool
    {
        return $this->pendingWrites === 0 && $this->size <= $this->position;
    }

    public function read(?Cancellation $cancellation = null, int $length = self::DEFAULT_READ_LENGTH): ?string
    {
        if ($this->id === null) {
            throw new ClosedException("The file has been closed");
        }

        if ($this->busy) {
            throw new PendingOperationError;
        }

        $this->busy = true;

        try {
            $data = $this->worker->execute(new Internal\FileTask('fread', [$length], $this->id), $cancellation);

            if ($data !== null) {
                $this->position += \strlen($data);
            }
        } catch (TaskFailureException $exception) {
            throw new StreamException("Reading from the file failed", 0, $exception);
        } catch (WorkerException $exception) {
            throw new StreamException("Sending the task to the worker failed", 0, $exception);
        } finally {
            $this->busy = false;
        }

        return $data;
    }

    public function write(string $bytes): void
    {
        if ($this->id === null) {
            throw new ClosedException("The file has been closed");
        }

        if ($this->busy && $this->pendingWrites === 0) {
            throw new PendingOperationError;
        }

        if (!$this->writable) {
            throw new ClosedException("The file is no longer writable");
        }

        ++$this->pendingWrites;
        $this->busy = true;

        try {
            $this->worker->execute(new Internal\FileTask('fwrite', [$bytes], $this->id));
            $this->position += \strlen($bytes);
            $this->size = \max($this->position, $this->size);
        } catch (TaskFailureException $exception) {
            throw new StreamException("Writing to the file failed", 0, $exception);
        } catch (WorkerException $exception) {
            throw new StreamException("Sending the task to the worker failed", 0, $exception);
        } finally {
            if (--$this->pendingWrites === 0) {
                $this->busy = false;
            }
        }
    }

    public function end(): void
    {
        $this->writable = false;
        $this->close();
    }

    public function seek(int $position, Whence $whence = Whence::Start): int
    {
        if ($this->id === null) {
            throw new ClosedException("The file has been closed");
        }

        if ($this->busy) {
            throw new PendingOperationError;
        }

        switch ($whence) {
            case Whence::Start:
            case Whence::Current:
            case Whence::End:
                try {
                    $this->position = $this->worker->execute(
                        new Internal\FileTask('fseek', [$position, $whence], $this->id)
                    );

                    $this->size = \max($this->position, $this->size);

                    return $this->position;
                } catch (TaskFailureException $exception) {
                    throw new StreamException('Seeking in the file failed.', 0, $exception);
                } catch (WorkerException $exception) {
                    throw new StreamException("Sending the task to the worker failed", 0, $exception);
                }

            default:
                throw new \Error('Invalid whence value. Use Start, Current, or End.');
        }
    }

    public function tell(): int
    {
        return $this->position;
    }

    public function getPath(): string
    {
        return $this->path;
    }

    public function getMode(): string
    {
        return $this->mode;
    }

    public function isReadable(): bool
    {
        return $this->id !== null;
    }

    public function isSeekable(): bool
    {
        return $this->id !== null;
    }

    public function isWritable(): bool
    {
        return $this->id !== null && $this->writable;
    }
}
<?php declare(strict_types=1);

namespace Amp\File\Driver;

use Amp\File\FilesystemDriver;
use Amp\File\FilesystemException;

final class BlockingFilesystemDriver implements FilesystemDriver
{
    private readonly \Closure $errorHandler;

    public function __construct()
    {
        $this->errorHandler = static fn () => true;
    }

    public function openFile(string $path, string $mode): BlockingFile
    {
        $mode = \str_replace(['b', 't', 'e'], '', $mode);

        switch ($mode) {
            case "r":
            case "r+":
            case "w":
            case "w+":
            case "a":
            case "a+":
            case "x":
            case "x+":
            case "c":
            case "c+":
                break;

            default:
                throw new \Error("Invalid file mode: {$mode}");
        }

        try {
            \set_error_handler(static function (int $type, string $message) use ($path, $mode): never {
                throw new FilesystemException("Failed to open '{$path}' in mode '{$mode}': {$message}");
            });

            if (!$handle = \fopen($path, $mode . 'be')) {
                throw new FilesystemException("Failed to open '{$path}' in mode '{$mode}'");
            }

            return new BlockingFile($handle, $path, $mode);
        } finally {
            \restore_error_handler();
        }
    }

    public function getStatus(string $path): ?array
    {
        \clearstatcache(true, $path);
        \set_error_handler($this->errorHandler);

        try {
            return \stat($path) ?: null;
        } finally {
            \restore_error_handler();
        }
    }

    public function getLinkStatus(string $path): ?array
    {
        \clearstatcache(true, $path);
        \set_error_handler($this->errorHandler);

        try {
            return \lstat($path) ?: null;
        } finally {
            \restore_error_handler();
        }
    }

    public function createSymlink(string $target, string $link): void
    {
        try {
            \set_error_handler(static function (int $type, string $message) use ($target, $link): never {
                throw new FilesystemException("Could not create symbolic link '{$link}' to '{$target}': {$message}");
            });

            if (!\symlink($target, $link)) {
                throw new FilesystemException("Could not create symbolic link '{$link}' to '{$target}'");
            }
        } finally {
            \restore_error_handler();
        }
    }

    public function createHardlink(string $target, string $link): void
    {
        try {
            \set_error_handler(static function (int $type, string $message) use ($target, $link): never {
                throw new FilesystemException("Could not create hard link '{$link}' to '{$target}': {$message}");
            });

            if (!\link($target, $link)) {
                throw new FilesystemException("Could not create hard link '{$link}' to '{$target}'");
            }
        } finally {
            \restore_error_handler();
        }
    }

    public function resolveSymlink(string $target): string
    {
        try {
            \set_error_handler(static function (int $type, string $message) use ($target): never {
                throw new FilesystemException("Could not resolve symbolic link '{$target}': {$message}");
            });

            if (false === ($result = \readlink($target))) {
                throw new FilesystemException("Could not resolve symbolic link '{$target}'");
            }

            return $result;
        } finally {
            \restore_error_handler();
        }
    }

    public function move(string $from, string $to): void
    {
        try {
            \set_error_handler(static function (int $type, string $message) use ($from, $to): never {
                throw new FilesystemException("Could not move file from '{$from}' to '{$to}': {$message}");
            });

            if (!\rename($from, $to)) {
                throw new FilesystemException("Could not move file from '{$from}' to '{$to}'");
            }
        } finally {
            \restore_error_handler();
        }
    }

    public function deleteFile(string $path): void
    {
        try {
            \set_error_handler(static function (int $type, string $message) use ($path): never {
                throw new FilesystemException("Could not delete file '{$path}': {$message}");
            });

            if (!\unlink($path)) {
                throw new FilesystemException("Could not delete file '{$path}'");
            }
        } finally {
            \restore_error_handler();
        }
    }

    public function createDirectory(string $path, int $mode = 0777): void
    {
        try {
            \set_error_handler(static function (int $type, string $message) use ($path): never {
                throw new FilesystemException("Could not create directory '{$path}': {$message}");
            });

            /** @noinspection MkdirRaceConditionInspection */
            if (!\mkdir($path, $mode)) {
                throw new FilesystemException("Could not create directory '{$path}'");
            }
        } finally {
            \restore_error_handler();
        }
    }

    public function createDirectoryRecursively(string $path, int $mode = 0777): void
    {
        try {
            \set_error_handler(static function (int $type, string $message) use ($path): bool {
                if (!\is_dir($path)) {
                    throw new FilesystemException("Could not create directory '{$path}': {$message}");
                }

                return true;
            });

            if (\is_dir($path)) {
                return;
            }

            /** @noinspection MkdirRaceConditionInspection */
            if (!\mkdir($path, $mode, true)) {
                if (\is_dir($path)) {
                    return;
                }

                throw new FilesystemException("Could not create directory '{$path}'");
            }
        } finally {
            \restore_error_handler();
        }
    }

    public function deleteDirectory(string $path): void
    {
        try {
            \set_error_handler(static function (int $type, string $message) use ($path): never {
                throw new FilesystemException("Could not remove directory '{$path}': {$message}");
            });

            if (!\rmdir($path)) {
                throw new FilesystemException("Could not remove directory '{$path}'");
            }
        } finally {
            \restore_error_handler();
        }
    }

    public function listFiles(string $path): array
    {
        try {
            \set_error_handler(static function (int $type, string $message) use ($path): never {
                throw new FilesystemException("Failed to list files in '{$path}': {$message}");
            });

            if (!\is_dir($path)) {
                throw new FilesystemException("Failed to list files; '{$path}' is not a directory");
            }

            if ($arr = \scandir($path)) {
                \clearstatcache(true, $path);

                return \array_values(\array_filter($arr, static function ($el): bool {
                    return $el !== "." && $el !== "..";
                }));
            }

            throw new FilesystemException("Failed to list files in '{$path}'");
        } finally {
            \restore_error_handler();
        }
    }

    public function changePermissions(string $path, int $mode): void
    {
        try {
            \set_error_handler(static function (int $type, string $message) use ($path): never {
                throw new FilesystemException("Failed to change permissions for '{$path}': {$message}");
            });

            if (!\chmod($path, $mode)) {
                throw new FilesystemException("Failed to change permissions for '{$path}'");
            }
        } finally {
            \restore_error_handler();
        }
    }

    public function changeOwner(string $path, ?int $uid, ?int $gid): void
    {
        try {
            \set_error_handler(static function (int $type, string $message) use ($path): never {
                throw new FilesystemException("Failed to change owner for '{$path}': {$message}");
            });

            $uid ??= -1;
            $gid ??= -1;

            if ($uid !== -1 && !\chown($path, $uid)) {
                throw new FilesystemException("Failed to change owner for '{$path}'");
            }

            if ($gid !== -1 && !\chgrp($path, $gid)) {
                throw new FilesystemException("Failed to change owner for '{$path}'");
            }
        } finally {
            \restore_error_handler();
        }
    }

    public function touch(string $path, ?int $modificationTime, ?int $accessTime): void
    {
        try {
            \set_error_handler(static function (int $type, string $message) use ($path): never {
                throw new FilesystemException("Failed to touch '{$path}': {$message}");
            });

            $modificationTime = $modificationTime ?? \time();
            $accessTime = $accessTime ?? $modificationTime;

            if (!\touch($path, $modificationTime, $accessTime)) {
                throw new FilesystemException("Failed to touch '{$path}'");
            }
        } finally {
            \restore_error_handler();
        }
    }

    public function read(string $path): string
    {
        try {
            \set_error_handler(static function (int $type, string $message) use ($path): never {
                throw new FilesystemException("Failed to read '{$path}': {$message}");
            });

            if (false === ($result = \file_get_contents($path))) {
                throw new FilesystemException("Failed to read '{$path}'");
            }

            return $result;
        } finally {
            \restore_error_handler();
        }
    }

    public function write(string $path, string $contents): void
    {
        try {
            \set_error_handler(static function (int $type, string $message) use ($path): never {
                throw new FilesystemException("Failed to write to '{$path}': {$message}");
            });

            if (false === \file_put_contents($path, $contents)) {
                throw new FilesystemException("Failed to write to '{$path}'");
            }
        } finally {
            \restore_error_handler();
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\File\Driver;

use Amp\ByteStream\ClosedException;
use Amp\ByteStream\StreamException;
use Amp\Cancellation;
use Amp\DeferredFuture;
use Amp\File\Internal;
use Amp\File\PendingOperationError;
use Amp\Future;
use Revolt\EventLoop\Driver as EventLoopDriver;

final class UvFile extends Internal\QueuedWritesFile
{
    private readonly Internal\UvPoll $poll;

    private readonly \UVLoop $eventLoopHandle;

    /** @var resource */
    private $fh;

    private ?Future $closing = null;

    private readonly DeferredFuture $onClose;

    /**
     * @param Internal\UvPoll $poll Poll for keeping the loop active.
     * @param resource $fh File handle.
     */
    public function __construct(
        EventLoopDriver $driver,
        Internal\UvPoll $poll,
        $fh,
        string $path,
        string $mode,
        int $size,
    ) {
        parent::__construct($path, $mode, $size);

        $this->poll = $poll;
        $this->fh = $fh;

        /** @psalm-suppress PropertyTypeCoercion */
        $this->eventLoopHandle = $driver->getHandle();
        $this->onClose = new DeferredFuture;
    }

    public function read(?Cancellation $cancellation = null, int $length = self::DEFAULT_READ_LENGTH): ?string
    {
        if ($this->isReading || !$this->queue->isEmpty()) {
            throw new PendingOperationError;
        }

        $deferred = new DeferredFuture;
        $this->poll->listen();

        $this->isReading = true;

        $onRead = function ($result, $buffer) use ($deferred): void {
            $this->isReading = false;

            if ($deferred->isComplete()) {
                return;
            }

            if (\is_int($buffer)) {
                $error = \uv_strerror($buffer);
                if ($error === "bad file descriptor") {
                    $deferred->error(new ClosedException("Reading from the file failed due to a closed handle"));
                } else {
                    $deferred->error(new StreamException("Reading from the file failed: " . $error));
                }
                return;
            }

            $length = \strlen($buffer);
            $this->position += $length;
            $deferred->complete($length ? $buffer : null);
        };

        \uv_fs_read($this->eventLoopHandle, $this->fh, $this->position, $length, $onRead);

        $id = $cancellation?->subscribe(function (\Throwable $exception) use ($deferred): void {
            $this->isReading = false;
            $deferred->error($exception);
        });

        try {
            return $deferred->getFuture()->await();
        } finally {
            /** @psalm-suppress PossiblyNullArgument $id is non-null if $cancellation is non-null */
            $cancellation?->unsubscribe($id);
            $this->poll->done();
        }
    }

    public function close(): void
    {
        if ($this->closing) {
            $this->closing->await();
            return;
        }

        $deferred = $this->onClose;
        $this->closing = $deferred->getFuture();
        $this->poll->listen();

        \uv_fs_close($this->eventLoopHandle, $this->fh, static function () use ($deferred): void {
            // Ignore errors when closing file, as the handle will become invalid anyway.
            $deferred->complete();
        });

        try {
            $this->closing->await();
        } finally {
            $this->poll->done();
        }
    }

    public function isClosed(): bool
    {
        return $this->closing !== null;
    }

    public function onClose(\Closure $onClose): void
    {
        $this->onClose->getFuture()->finally($onClose);
    }

    protected function push(string $data, int $position): Future
    {
        $length = \strlen($data);

        if ($length === 0) {
            return Future::complete();
        }

        $deferred = new DeferredFuture;
        $this->poll->listen();

        $onWrite = function ($fh, $result) use ($deferred, $length, $position): void {
            if ($this->queue->isEmpty()) {
                $deferred->error(new ClosedException('No pending write, the file may have been closed'));
            }

            $this->queue->shift();

            if ($result < 0) {
                $error = \uv_strerror($result);
                if ($error === "bad file descriptor") {
                    $deferred->error(new ClosedException("Writing to the file failed due to a closed handle"));
                } else {
                    $deferred->error(new StreamException("Writing to the file failed: " . $error));
                }
            } else {
                if ($this->position === $position) {
                    $this->position += $length;
                }

                $this->size = \max($this->size, $position + $length);

                $deferred->complete();
            }

            $this->poll->done();
        };

        \uv_fs_write($this->eventLoopHandle, $this->fh, $data, $position, $onWrite);

        return $deferred->getFuture();
    }

    protected function trim(int $size): Future
    {
        $deferred = new DeferredFuture;
        $this->poll->listen();

        $onTruncate = function ($fh) use ($deferred, $size): void {
            if ($this->queue->isEmpty()) {
                $deferred->error(new ClosedException('No pending write, the file may have been closed'));
            }

            $this->queue->shift();

            $this->size = $size;
            $deferred->complete();
            $this->poll->done();
        };

        \uv_fs_ftruncate(
            $this->eventLoopHandle,
            $this->fh,
            $size,
            $onTruncate
        );

        return $deferred->getFuture();
    }
}
<?php declare(strict_types=1);

namespace Amp\File\Driver;

use Amp\File\FilesystemDriver;
use Amp\File\FilesystemException;
use Amp\File\Internal;
use Amp\Future;
use Amp\Parallel\Worker\ContextWorkerPool;
use Amp\Parallel\Worker\DelegatingWorkerPool;
use Amp\Parallel\Worker\LimitedWorkerPool;
use Amp\Parallel\Worker\TaskFailureThrowable;
use Amp\Parallel\Worker\Worker;
use Amp\Parallel\Worker\WorkerException;
use Amp\Parallel\Worker\WorkerPool;
use function Amp\async;

final class ParallelFilesystemDriver implements FilesystemDriver
{
    public const DEFAULT_WORKER_LIMIT = 8;

    private readonly WorkerPool $pool;

    /** @var positive-int Maximum number of workers to use for open files. */
    private readonly int $workerLimit;

    /** @var \WeakMap<Worker, int> */
    private \WeakMap $workerStorage;

    /** @var Future<Worker>|null Pending worker request */
    private ?Future $pendingWorker = null;

    /**
     * @param WorkerPool|null $pool Custom worker pool to use for file workers. If null, a new pool is created.
     * @param int|null $workerLimit [Deprecated] Maximum number of workers to use from the pool for open files. Instead
     *      of using this parameter, provide a pool with a limited number using an instance of {@see LimitedWorkerPool}
     *      such as {@see ContextWorkerPool}.
     */
    public function __construct(?WorkerPool $pool = null, ?int $workerLimit = null)
    {
        /** @var \WeakMap<Worker, int> For Psalm. */
        $this->workerStorage = new \WeakMap();

        if ($workerLimit !== null) {
            \trigger_error(
                'The $workerLimit parameter is deprecated and will be removed in the next major version.' .
                ' To limit the number of workers used from the given pool, use an instance of ' .
                LimitedWorkerPool::class . ' instead, such as ' . ContextWorkerPool::class . ' or ' .
                DelegatingWorkerPool::class,
                \E_USER_DEPRECATED,
            );
        }

        $workerLimit ??= $pool instanceof LimitedWorkerPool ? $pool->getWorkerLimit() : self::DEFAULT_WORKER_LIMIT;
        if ($workerLimit <= 0) {
            throw new \ValueError("Worker limit must be a positive integer");
        }

        $this->pool = $pool ?? new ContextWorkerPool($workerLimit);
        $this->workerLimit = $workerLimit;
    }

    public function openFile(string $path, string $mode): ParallelFile
    {
        $worker = $this->selectWorker();

        $workerStorage = $this->workerStorage;
        $worker = new Internal\FileWorker($worker, static function (Worker $worker) use ($workerStorage): void {
            if (!isset($workerStorage[$worker])) {
                return;
            }

            if (($workerStorage[$worker] -= 1) === 0 || !$worker->isRunning()) {
                unset($workerStorage[$worker]);
            }
        });

        try {
            [$id, $size, $mode] = $worker->execute(new Internal\FileTask("fopen", [$path, $mode]));
        } catch (TaskFailureThrowable $exception) {
            throw new FilesystemException("Could not open file", $exception);
        } catch (WorkerException $exception) {
            throw new FilesystemException("Could not send open request to worker", $exception);
        }

        return new ParallelFile($worker, $id, $path, $size, $mode);
    }

    private function selectWorker(): Worker
    {
        $this->pendingWorker?->await(); // Wait for any currently pending request for a worker.

        if ($this->workerStorage->count() < $this->workerLimit) {
            $this->pendingWorker = async($this->pool->getWorker(...));
            $worker = $this->pendingWorker->await();

            $this->workerStorage[$worker] = 1;

            return $worker;
        }

        $max = \PHP_INT_MAX;
        foreach ($this->workerStorage as $storedWorker => $count) {
            if ($count <= $max) {
                $worker = $storedWorker;
                $max = $count;
            }
        }

        \assert(isset($worker) && $worker instanceof Worker);

        if (!$worker->isRunning()) {
            unset($this->workerStorage[$worker]);
            return $this->selectWorker();
        }

        $this->workerStorage[$worker] += 1;

        return $worker;
    }

    public function deleteFile(string $path): void
    {
        $this->runFileTask(new Internal\FileTask("deleteFile", [$path]));
    }

    public function getStatus(string $path): ?array
    {
        return $this->runFileTask(new Internal\FileTask("getStatus", [$path]));
    }

    public function move(string $from, string $to): void
    {
        $this->runFileTask(new Internal\FileTask("move", [$from, $to]));
    }

    public function createHardlink(string $target, string $link): void
    {
        $this->runFileTask(new Internal\FileTask("createHardlink", [$target, $link]));
    }

    public function createSymlink(string $target, string $link): void
    {
        $this->runFileTask(new Internal\FileTask("createSymlink", [$target, $link]));
    }

    public function resolveSymlink(string $target): string
    {
        return $this->runFileTask(new Internal\FileTask("resolveSymlink", [$target]));
    }

    public function createDirectory(string $path, int $mode = 0777): void
    {
        $this->runFileTask(new Internal\FileTask("createDirectory", [$path, $mode]));
    }

    public function createDirectoryRecursively(string $path, int $mode = 0777): void
    {
        $this->runFileTask(new Internal\FileTask("createDirectoryRecursively", [$path, $mode]));
    }

    public function listFiles(string $path): array
    {
        return $this->runFileTask(new Internal\FileTask("listFiles", [$path]));
    }

    public function deleteDirectory(string $path): void
    {
        $this->runFileTask(new Internal\FileTask("deleteDirectory", [$path]));
    }

    public function changePermissions(string $path, int $mode): void
    {
        $this->runFileTask(new Internal\FileTask("changePermissions", [$path, $mode]));
    }

    public function changeOwner(string $path, ?int $uid, ?int $gid): void
    {
        $this->runFileTask(new Internal\FileTask("changeOwner", [$path, $uid, $gid]));
    }

    public function getLinkStatus(string $path): ?array
    {
        return $this->runFileTask(new Internal\FileTask("getLinkStatus", [$path]));
    }

    public function touch(string $path, ?int $modificationTime, ?int $accessTime): void
    {
        $this->runFileTask(
            new Internal\FileTask(
                "touch",
                [$path, $modificationTime, $accessTime]
            )
        );
    }

    public function read(string $path): string
    {
        return $this->runFileTask(new Internal\FileTask("read", [$path]));
    }

    public function write(string $path, string $contents): void
    {
        $this->runFileTask(new Internal\FileTask("write", [$path, $contents]));
    }

    private function runFileTask(Internal\FileTask $task): mixed
    {
        try {
            return $this->pool->submit($task)->await();
        } catch (TaskFailureThrowable $exception) {
            throw new FilesystemException("The file operation failed", $exception);
        } catch (WorkerException $exception) {
            throw new FilesystemException("Could not send the file task to worker", $exception);
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\File\Driver;

use Amp\ByteStream\ClosedException;
use Amp\ByteStream\ReadableStreamIteratorAggregate;
use Amp\ByteStream\StreamException;
use Amp\Cancellation;
use Amp\DeferredFuture;
use Amp\File\File;
use Amp\File\Whence;

/**
 * @implements \IteratorAggregate<int, string>
 */
final class BlockingFile implements File, \IteratorAggregate
{
    use ReadableStreamIteratorAggregate;

    /** @var resource|null */
    private $handle;

    private int $id;

    private readonly DeferredFuture $onClose;

    /**
     * @param resource $handle An open filesystem descriptor.
     * @param string $path File path.
     * @param string $mode File open mode.
     */
    public function __construct(
        $handle,
        private readonly string $path,
        private readonly string $mode,
    ) {
        $this->handle = $handle;
        $this->id = (int) $handle;

        if ($mode[0] === 'a') {
            \fseek($this->handle, 0, \SEEK_END);
        }

        $this->onClose = new DeferredFuture;
    }

    public function __destruct()
    {
        if ($this->handle !== null) {
            \fclose($this->handle);
        }

        if (!$this->onClose->isComplete()) {
            $this->onClose->complete();
        }
    }

    public function read(?Cancellation $cancellation = null, int $length = self::DEFAULT_READ_LENGTH): ?string
    {
        if ($this->handle === null) {
            throw new ClosedException("The file '{$this->path}' has been closed");
        }

        try {
            \set_error_handler(function (int $type, string $message): never {
                throw new StreamException("Failed reading from file '{$this->path}': {$message}");
            });

            $data = \fread($this->handle, $length);
            if ($data === false) {
                throw new StreamException("Failed reading from file '{$this->path}'");
            }

            return $data !== '' ? $data : null;
        } finally {
            \restore_error_handler();
        }
    }

    public function write(string $bytes): void
    {
        if ($this->handle === null) {
            throw new ClosedException("The file '{$this->path}' has been closed");
        }

        try {
            \set_error_handler(function (int $type, string $message): never {
                throw new StreamException("Failed writing to file '{$this->path}': {$message}");
            });

            $length = \fwrite($this->handle, $bytes);
            if ($length === false) {
                throw new StreamException("Failed writing to file '{$this->path}'");
            }
        } finally {
            \restore_error_handler();
        }
    }

    public function end(): void
    {
        try {
            $this->close();
        } catch (\Throwable) {
            // ignore any errors
        }
    }

    public function close(): void
    {
        if ($this->handle === null) {
            return;
        }

        $handle = $this->handle;
        $this->handle = null;

        if (!$this->onClose->isComplete()) {
            $this->onClose->complete();
        }

        try {
            \set_error_handler(function (int $type, string $message): never {
                throw new StreamException("Failed closing file '{$this->path}': {$message}");
            });

            if (\fclose($handle)) {
                return;
            }

            throw new StreamException("Failed closing file '{$this->path}'");
        } finally {
            \restore_error_handler();
        }
    }

    public function isClosed(): bool
    {
        return $this->handle === null;
    }

    public function onClose(\Closure $onClose): void
    {
        $this->onClose->getFuture()->finally($onClose);
    }

    public function truncate(int $size): void
    {
        if ($this->handle === null) {
            throw new ClosedException("The file '{$this->path}' has been closed");
        }

        try {
            \set_error_handler(function (int $type, string $message): never {
                throw new StreamException("Could not truncate file '{$this->path}': {$message}");
            });

            if (!\ftruncate($this->handle, $size)) {
                throw new StreamException("Could not truncate file '{$this->path}'");
            }
        } finally {
            \restore_error_handler();
        }
    }

    public function seek(int $position, Whence $whence = Whence::Start): int
    {
        if ($this->handle === null) {
            throw new ClosedException("The file '{$this->path}' has been closed");
        }

        $mode = match ($whence) {
            Whence::Start => SEEK_SET,
            Whence::Current => SEEK_CUR,
            Whence::End => SEEK_END,
            default => throw new \Error("Invalid whence parameter; Start, Current or End expected")
        };

        try {
            \set_error_handler(function (int $type, string $message): never {
                throw new StreamException("Could not seek in file '{$this->path}': {$message}");
            });

            if (\fseek($this->handle, $position, $mode) === -1) {
                throw new StreamException("Could not seek in file '{$this->path}'");
            }

            return $this->tell();
        } finally {
            \restore_error_handler();
        }
    }

    public function tell(): int
    {
        if ($this->handle === null) {
            throw new ClosedException("The file '{$this->path}' has been closed");
        }

        return \ftell($this->handle);
    }

    public function eof(): bool
    {
        if ($this->handle === null) {
            throw new ClosedException("The file '{$this->path}' has been closed");
        }

        return \feof($this->handle);
    }

    public function getPath(): string
    {
        return $this->path;
    }

    public function getMode(): string
    {
        return $this->mode;
    }

    public function isReadable(): bool
    {
        return $this->handle !== null;
    }

    public function isSeekable(): bool
    {
        return $this->handle !== null;
    }

    public function isWritable(): bool
    {
        return $this->handle !== null && $this->mode[0] !== 'r';
    }

    public function getId(): int
    {
        return $this->id;
    }
}
<?php declare(strict_types=1);

namespace Amp\File\Driver;

use Amp\File\FilesystemDriver;
use Amp\File\Internal\Cache;

final class StatusCachingFilesystemDriver implements FilesystemDriver
{
    private FilesystemDriver $driver;

    private Cache $statusCache;

    public function __construct(FilesystemDriver $driver)
    {
        $this->driver = $driver;
        $this->statusCache = new Cache(1000, 1024);
    }

    public function openFile(string $path, string $mode): StatusCachingFile
    {
        $file = $this->driver->openFile($path, $mode);

        $statusCache = $this->statusCache;
        return new StatusCachingFile($file, static fn () => $statusCache->delete($path));
    }

    public function getStatus(string $path): ?array
    {
        if ($cachedStat = $this->statusCache->get($path)) {
            return $cachedStat;
        }

        $stat = $this->driver->getStatus($path);
        if ($stat) {
            $this->statusCache->set($path, $stat, 1000);
        }

        return $stat;
    }

    public function getLinkStatus(string $path): ?array
    {
        return $this->driver->getLinkStatus($path);
    }

    public function createSymlink(string $target, string $link): void
    {
        try {
            $this->driver->createSymlink($target, $link);
        } finally {
            $this->statusCache->delete($target);
            $this->statusCache->delete($link);
        }
    }

    public function createHardlink(string $target, string $link): void
    {
        try {
            $this->driver->createHardlink($target, $link);
        } finally {
            $this->statusCache->delete($target);
            $this->statusCache->delete($link);
        }
    }

    public function resolveSymlink(string $target): string
    {
        return $this->driver->resolveSymlink($target);
    }

    public function move(string $from, string $to): void
    {
        try {
            $this->driver->move($from, $to);
        } finally {
            $this->statusCache->delete($from);
            $this->statusCache->delete($to);
        }
    }

    public function deleteFile(string $path): void
    {
        try {
            $this->driver->deleteFile($path);
        } finally {
            $this->statusCache->delete($path);
        }
    }

    public function createDirectory(string $path, int $mode = 0777): void
    {
        try {
            $this->driver->createDirectory($path, $mode);
        } finally {
            $this->statusCache->delete($path);
        }
    }

    public function createDirectoryRecursively(string $path, int $mode = 0777): void
    {
        try {
            $this->driver->createDirectoryRecursively($path, $mode);
        } finally {
            $this->statusCache->delete($path);
        }
    }

    public function deleteDirectory(string $path): void
    {
        try {
            $this->driver->deleteDirectory($path);
        } finally {
            $this->statusCache->delete($path);
        }
    }

    public function listFiles(string $path): array
    {
        return $this->driver->listFiles($path);
    }

    public function changePermissions(string $path, int $mode): void
    {
        try {
            $this->driver->changePermissions($path, $mode);
        } finally {
            $this->statusCache->delete($path);
        }
    }

    public function changeOwner(string $path, ?int $uid, ?int $gid): void
    {
        try {
            $this->driver->changeOwner($path, $uid, $gid);
        } finally {
            $this->statusCache->delete($path);
        }
    }

    public function touch(string $path, ?int $modificationTime, ?int $accessTime): void
    {
        try {
            $this->driver->touch($path, $modificationTime, $accessTime);
        } finally {
            $this->statusCache->delete($path);
        }
    }

    public function read(string $path): string
    {
        return $this->driver->read($path);
    }

    public function write(string $path, string $contents): void
    {
        try {
            $this->driver->write($path, $contents);
        } finally {
            $this->statusCache->delete($path);
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\File\Driver;

use Amp\ByteStream\ReadableStreamIteratorAggregate;
use Amp\Cancellation;
use Amp\File\File;
use Amp\File\Whence;

/**
 * @implements \IteratorAggregate<int, string>
 */
final class StatusCachingFile implements File, \IteratorAggregate
{
    use ReadableStreamIteratorAggregate;

    private readonly File $file;

    private readonly \Closure $invalidateCallback;

    /**
     * @param File $file Decorated instance.
     * @param \Closure $invalidateCallback Invalidation callback.
     *
     * @internal
     */
    public function __construct(File $file, \Closure $invalidateCallback)
    {
        $this->file = $file;
        $this->invalidateCallback = $invalidateCallback;
    }

    public function read(?Cancellation $cancellation = null, int $length = self::DEFAULT_READ_LENGTH): ?string
    {
        return $this->file->read($cancellation, $length);
    }

    public function write(string $bytes): void
    {
        try {
            $this->file->write($bytes);
        } finally {
            $this->invalidate();
        }
    }

    public function end(): void
    {
        try {
            $this->file->end();
        } finally {
            $this->invalidate();
        }
    }

    public function close(): void
    {
        $this->file->close();
    }

    public function isClosed(): bool
    {
        return $this->file->isClosed();
    }

    public function onClose(\Closure $onClose): void
    {
        $this->file->onClose($onClose);
    }

    public function seek(int $position, Whence $whence = Whence::Start): int
    {
        return $this->file->seek($position, $whence);
    }

    public function tell(): int
    {
        return $this->file->tell();
    }

    public function eof(): bool
    {
        return $this->file->eof();
    }

    public function getPath(): string
    {
        return $this->file->getPath();
    }

    public function getMode(): string
    {
        return $this->file->getMode();
    }

    public function truncate(int $size): void
    {
        try {
            $this->file->truncate($size);
        } finally {
            $this->invalidate();
        }
    }

    public function isReadable(): bool
    {
        return $this->file->isReadable();
    }

    public function isSeekable(): bool
    {
        return $this->file->isSeekable();
    }

    public function isWritable(): bool
    {
        return $this->file->isWritable();
    }

    private function invalidate(): void
    {
        ($this->invalidateCallback)();
    }
}
<?php declare(strict_types=1);

namespace Amp\File\Driver;

use Amp\DeferredFuture;
use Amp\File\FilesystemDriver;
use Amp\File\FilesystemException;
use Amp\File\Internal;
use Revolt\EventLoop\Driver as EventLoopDriver;

final class EioFilesystemDriver implements FilesystemDriver
{
    /**
     * @return bool Determines if this driver can be used based on the environment.
     */
    public static function isSupported(): bool
    {
        return \extension_loaded('eio');
    }

    private Internal\EioPoll $poll;

    public function __construct(EventLoopDriver $driver)
    {
        $this->poll = new Internal\EioPoll($driver);
    }

    public function openFile(string $path, string $mode): EioFile
    {
        $flags = \EIO_O_NONBLOCK | $this->parseMode($mode);
        if (\defined('\EIO_O_FSYNC')) {
            $flags |= \EIO_O_FSYNC;
        }

        $chmod = ($flags & \EIO_O_CREAT) ? 0644 : 0;

        $deferred = new DeferredFuture;
        $this->poll->listen();

        \eio_open(
            $path,
            $flags,
            $chmod,
            \EIO_PRI_DEFAULT,
            function (mixed $data, mixed $fileHandle, mixed $resource) use ($mode, $path, $deferred): void {
                if ($fileHandle === -1) {
                    $deferred->error(new FilesystemException(\eio_get_last_error($resource)));
                } else {
                    \eio_fstat(
                        $fileHandle,
                        \EIO_PRI_DEFAULT,
                        function (mixed $data, mixed $result, mixed $resource) use (
                            $fileHandle,
                            $path,
                            $mode,
                            $deferred
                        ) {
                            if ($result === -1) {
                                $deferred->error(new FilesystemException(\eio_get_last_error($resource)));
                            } else {
                                $handle = new EioFile($this->poll, $fileHandle, $path, $mode, $result["size"]);
                                $deferred->complete($handle);
                            }
                        }
                    );
                }
            }
        );

        try {
            return $deferred->getFuture()->await();
        } finally {
            $this->poll->done();
        }
    }

    public function getStatus(string $path): ?array
    {
        $deferred = new DeferredFuture;
        $this->poll->listen();

        $priority = \EIO_PRI_DEFAULT;
        \eio_stat($path, $priority, [$this, "onStat"], $deferred);

        try {
            return $deferred->getFuture()->await();
        } finally {
            $this->poll->done();
        }
    }

    public function getLinkStatus(string $path): ?array
    {
        $deferred = new DeferredFuture;
        $this->poll->listen();

        $priority = \EIO_PRI_DEFAULT;
        \eio_lstat($path, $priority, [$this, "onLstat"], $deferred);

        try {
            return $deferred->getFuture()->await();
        } finally {
            $this->poll->done();
        }
    }

    public function createSymlink(string $target, string $link): void
    {
        $deferred = new DeferredFuture;
        $this->poll->listen();

        $priority = \EIO_PRI_DEFAULT;
        \eio_symlink($target, $link, $priority, [$this, "onGenericResult"], $deferred);

        try {
            $deferred->getFuture()->await();
        } finally {
            $this->poll->done();
        }
    }

    public function createHardlink(string $target, string $link): void
    {
        $deferred = new DeferredFuture;
        $this->poll->listen();

        $priority = \EIO_PRI_DEFAULT;
        \eio_link($target, $link, $priority, [$this, "onGenericResult"], $deferred);

        try {
            $deferred->getFuture()->await();
        } finally {
            $this->poll->done();
        }
    }

    public function resolveSymlink(string $target): string
    {
        $deferred = new DeferredFuture;
        $this->poll->listen();

        $priority = \EIO_PRI_DEFAULT;
        /** @psalm-suppress InvalidArgument */
        \eio_readlink($target, $priority, [$this, "onReadlink"], $deferred);

        try {
            return $deferred->getFuture()->await();
        } finally {
            $this->poll->done();
        }
    }

    public function move(string $from, string $to): void
    {
        $deferred = new DeferredFuture;
        $this->poll->listen();

        $priority = \EIO_PRI_DEFAULT;
        \eio_rename($from, $to, $priority, [$this, "onGenericResult"], $deferred);

        try {
            $deferred->getFuture()->await();
        } finally {
            $this->poll->done();
        }
    }

    public function deleteFile(string $path): void
    {
        $deferred = new DeferredFuture;
        $this->poll->listen();

        $priority = \EIO_PRI_DEFAULT;
        $result = \eio_unlink($path, $priority, [$this, "onUnlink"], $deferred);

        // For a non-existent file eio_unlink immediately returns true and the callback is never called.
        /** @psalm-suppress TypeDoesNotContainType */
        if ($result === true) {
            $deferred->error(new FilesystemException('File does not exist.'));
        }

        try {
            $deferred->getFuture()->await();
        } finally {
            $this->poll->done();
        }
    }

    public function createDirectory(string $path, int $mode = 0777): void
    {
        $deferred = new DeferredFuture;
        $this->poll->listen();

        \eio_mkdir($path, $mode, \EIO_PRI_DEFAULT, [$this, "onGenericResult"], $deferred);

        try {
            $deferred->getFuture()->await();
        } finally {
            $this->poll->done();
        }
    }

    public function createDirectoryRecursively(string $path, int $mode = 0777): void
    {
        $deferred = new DeferredFuture;
        $this->poll->listen();

        $priority = \EIO_PRI_DEFAULT;

        $path = \str_replace("/", DIRECTORY_SEPARATOR, $path);
        $path = \rtrim($path, DIRECTORY_SEPARATOR);
        $arrayPath = \explode(DIRECTORY_SEPARATOR, $path);
        $tmpPath = "";

        $callback = function () use (
            &$callback,
            &$arrayPath,
            &$tmpPath,
            $mode,
            $priority,
            $deferred
        ): void {
            $tmpPath .= DIRECTORY_SEPARATOR . \array_shift($arrayPath);

            if (empty($arrayPath)) {
                \eio_mkdir($tmpPath, $mode, $priority, [$this, "onGenericResult"], $deferred);
            } else {
                \eio_mkdir($tmpPath, $mode, $priority, $callback);
            }
        };

        $callback();

        try {
            $deferred->getFuture()->await();
        } catch (FilesystemException $exception) {
            $result = $this->getStatus($path);
            if ($result !== null && ($result['mode'] & 0040000)) {
                return;
            }

            throw $exception;
        } finally {
            $this->poll->done();
        }
    }

    public function deleteDirectory(string $path): void
    {
        $deferred = new DeferredFuture;
        $this->poll->listen();

        $priority = \EIO_PRI_DEFAULT;
        \eio_rmdir($path, $priority, [$this, "onRmdir"], $deferred);

        try {
            $deferred->getFuture()->await();
        } finally {
            $this->poll->done();
        }
    }

    public function listFiles(string $path): array
    {
        $deferred = new DeferredFuture;
        $this->poll->listen();

        $flags = \EIO_READDIR_STAT_ORDER | \EIO_READDIR_DIRS_FIRST;
        $priority = \EIO_PRI_DEFAULT;

        /** @psalm-suppress InvalidArgument */
        \eio_readdir($path, $flags, $priority, [$this, "onScandir"], $deferred);

        try {
            return $deferred->getFuture()->await();
        } finally {
            $this->poll->done();
        }
    }

    public function changePermissions(string $path, int $mode): void
    {
        $deferred = new DeferredFuture;
        $this->poll->listen();

        $priority = \EIO_PRI_DEFAULT;
        \eio_chmod($path, $mode, $priority, [$this, "onGenericResult"], $deferred);

        try {
            $deferred->getFuture()->await();
        } finally {
            $this->poll->done();
        }
    }

    public function changeOwner(string $path, ?int $uid, ?int $gid): void
    {
        $deferred = new DeferredFuture;
        $this->poll->listen();

        $priority = \EIO_PRI_DEFAULT;
        \eio_chown($path, $uid ?? -1, $gid ?? -1, $priority, [$this, "onGenericResult"], $deferred);

        try {
            $deferred->getFuture()->await();
        } finally {
            $this->poll->done();
        }
    }

    public function touch(string $path, ?int $modificationTime, ?int $accessTime): void
    {
        $modificationTime = $modificationTime ?? \time();
        $accessTime = $accessTime ?? $modificationTime;

        if (!$this->getStatus($path)) {
            $this->openFile($path, 'c')->close();
        }

        $deferred = new DeferredFuture;
        $this->poll->listen();

        $priority = \EIO_PRI_DEFAULT;
        \eio_utime($path, $accessTime, $modificationTime, $priority, [$this, "onGenericResult"], $deferred);

        try {
            $deferred->getFuture()->await();
        } finally {
            $this->poll->done();
        }
    }

    public function read(string $path): string
    {
        $flags = \EIO_O_RDONLY;
        $mode = 0;
        $priority = \EIO_PRI_DEFAULT;

        $deferred = new DeferredFuture;
        $this->poll->listen();

        \eio_open($path, $flags, $mode, $priority, [$this, "onGetOpen"], $deferred);

        try {
            return $deferred->getFuture()->await();
        } finally {
            $this->poll->done();
        }
    }

    public function write(string $path, string $contents): void
    {
        $flags = \EIO_O_RDWR | \EIO_O_CREAT | \EIO_O_TRUNC;
        $mode = \EIO_S_IRUSR | \EIO_S_IWUSR | \EIO_S_IXUSR;
        $priority = \EIO_PRI_DEFAULT;

        $deferred = new DeferredFuture;
        $this->poll->listen();

        $data = [$contents, $deferred];
        \eio_open($path, $flags, $mode, $priority, [$this, "onPutOpen"], $data);

        try {
            $deferred->getFuture()->await();
        } finally {
            $this->poll->done();
        }
    }

    private function parseMode(string $mode): int
    {
        $mode = \str_replace(['b', 't', 'e'], '', $mode);

        switch ($mode) {
            case 'r':
                return \EIO_O_RDONLY;
            case 'r+':
                return \EIO_O_RDWR;
            case 'w':
                return \EIO_O_WRONLY | \EIO_O_TRUNC | \EIO_O_CREAT;
            case 'w+':
                return \EIO_O_RDWR | \EIO_O_TRUNC | \EIO_O_CREAT;
            case 'a':
                return \EIO_O_WRONLY | \EIO_O_APPEND | \EIO_O_CREAT;
            case 'a+':
                return \EIO_O_RDWR | \EIO_O_APPEND | \EIO_O_CREAT;
            case 'x':
                return \EIO_O_WRONLY | \EIO_O_CREAT | \EIO_O_EXCL;
            case 'x+':
                return \EIO_O_RDWR | \EIO_O_CREAT | \EIO_O_EXCL;
            case 'c':
                return \EIO_O_WRONLY | \EIO_O_CREAT;
            case 'c+':
                return \EIO_O_RDWR | \EIO_O_CREAT;
            default:
                throw new \Error('Invalid file mode: ' . $mode);
        }
    }

    private function onStat(DeferredFuture $deferred, mixed $result): void
    {
        if ($result === -1) {
            $deferred->complete();
        } else {
            $deferred->complete($result);
        }
    }

    private function onLstat(DeferredFuture $deferred, mixed $result): void
    {
        if ($result === -1) {
            $deferred->complete();
        } else {
            $deferred->complete($result);
        }
    }

    private function onReadlink(DeferredFuture $deferred, mixed $result, mixed $resource): void
    {
        if ($result === -1) {
            $deferred->error(new FilesystemException(\eio_get_last_error($resource)));
        } else {
            $deferred->complete($result);
        }
    }

    private function onGenericResult(DeferredFuture $deferred, mixed $result, mixed $resource): void
    {
        if ($result === -1) {
            $deferred->error(new FilesystemException(\eio_get_last_error($resource)));
        } else {
            $deferred->complete();
        }
    }

    private function onUnlink(DeferredFuture $deferred, mixed $result, mixed $resource): void
    {
        if ($result === -1) {
            $deferred->error(new FilesystemException(\eio_get_last_error($resource)));
        } else {
            $deferred->complete();
        }
    }

    private function onRmdir(DeferredFuture $deferred, mixed $result, mixed $resource): void
    {
        if ($result === -1) {
            $deferred->error(new FilesystemException(\eio_get_last_error($resource)));
        } else {
            $deferred->complete();
        }
    }

    private function onScandir(DeferredFuture $deferred, mixed $result, mixed $resource): void
    {
        if ($result === -1) {
            $deferred->error(new FilesystemException(\eio_get_last_error($resource)));
        } else {
            $result = $result["names"];
            \sort($result);
            $deferred->complete($result);
        }
    }

    private function onGetOpen(DeferredFuture $deferred, mixed $result, mixed $resource): void
    {
        if ($result === -1) {
            $deferred->error(new FilesystemException(\eio_get_last_error($resource)));
        } else {
            \eio_fstat($result, \EIO_PRI_DEFAULT, [$this, "onGetFstat"], [$result, $deferred]);
        }
    }

    private function onGetFstat(array $fileHandleAndDeferred, mixed $result, mixed $resource): void
    {
        [$fh, $deferred] = $fileHandleAndDeferred;

        if ($result === -1) {
            $deferred->error(new FilesystemException(\eio_get_last_error($resource)));
            return;
        }

        \eio_read($fh, $result["size"], 0, \EIO_PRI_DEFAULT, [$this, "onGetRead"], $fileHandleAndDeferred);
    }

    private function onGetRead(array $fileHandleAndDeferred, mixed $result, mixed $resource): void
    {
        [$fileHandle, $deferred] = $fileHandleAndDeferred;

        \eio_close($fileHandle);

        if ($result === -1) {
            $deferred->error(new FilesystemException(\eio_get_last_error($resource)));
        } else {
            $deferred->complete($result);
        }
    }

    private function onPutOpen(array $data, mixed $result, mixed $resource): void
    {
        [$contents, $deferred] = $data;

        if ($result === -1) {
            $deferred->error(new FilesystemException(\eio_get_last_error($resource)));
        } else {
            $length = \strlen($contents);
            $offset = 0;
            $priority = \EIO_PRI_DEFAULT;
            $callback = [$this, "onPutWrite"];
            $fhAndPromisor = [$result, $deferred];
            \eio_write($result, $contents, $length, $offset, $priority, $callback, $fhAndPromisor);
        }
    }

    private function onPutWrite(array $fileHandleAndDeferred, mixed $result, mixed $resource): void
    {
        [$fileHandle, $deferred] = $fileHandleAndDeferred;

        \eio_close($fileHandle);

        if ($result === -1) {
            $deferred->error(new FilesystemException(\eio_get_last_error($resource)));
        } else {
            $deferred->complete();
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\File;

enum Whence
{
    /**
     * Set position equal to offset bytes.
     */
    case Start;

    /**
     * Set position to current location plus offset.
     */
    case Current;

    /**
     * Set position to end-of-file plus offset.
     */
    case End;
}
<?php declare(strict_types=1);

namespace Amp\File;

use Amp\ByteStream\ReadableStream;
use Amp\ByteStream\WritableStream;
use Amp\Cancellation;

interface File extends ReadableStream, WritableStream
{
    public const DEFAULT_READ_LENGTH = 8192;

    /**
     * Read $length bytes from the open file handle.
     */
    public function read(?Cancellation $cancellation = null, int $length = self::DEFAULT_READ_LENGTH): ?string;

    /**
     * Set the internal pointer position.
     *
     * @return int New offset position.
     */
    public function seek(int $position, Whence $whence = Whence::Start): int;

    /**
     * Return the current internal offset position of the file handle.
     */
    public function tell(): int;

    /**
     * Test for being at the end of the stream (a.k.a. "end-of-file").
     */
    public function eof(): bool;

    /**
     * @return bool Seeking may become unavailable if the underlying source is closed or lost.
     */
    public function isSeekable(): bool;

    /**
     * Retrieve the path used when opening the file handle.
     */
    public function getPath(): string;

    /**
     * Retrieve the mode used when opening the file handle.
     */
    public function getMode(): string;

    /**
     * Truncates the file to the given length. If $size is larger than the current file size, the file is extended
     * with null bytes.
     *
     * @param int $size New file size.
     */
    public function truncate(int $size): void;
}
<?php declare(strict_types=1);

namespace Amp\File\Internal;

use Amp\ByteStream\ClosedException;
use Amp\ByteStream\ReadableStreamIteratorAggregate;
use Amp\Cancellation;
use Amp\File\File;
use Amp\File\PendingOperationError;
use Amp\File\Whence;
use Amp\Future;
use function Amp\async;

/**
 * @internal
 * @implements \IteratorAggregate<int, string>
 */
abstract class QueuedWritesFile implements File, \IteratorAggregate
{
    use ReadableStreamIteratorAggregate;

    /** @var \SplQueue<Future<null>> */
    protected readonly \SplQueue $queue;

    protected int $position;

    protected bool $isReading = false;

    private bool $writable;

    public function __construct(
        private readonly string $path,
        private readonly string $mode,
        protected int $size,
    ) {
        if (!\strlen($mode)) {
            throw new \ValueError('File mode cannot be empty');
        }

        $this->queue = new \SplQueue();
        $this->writable = !\str_contains($this->mode, 'r') || \str_contains($this->mode, '+');
        $this->position = \str_contains($this->mode, 'a') ? $this->size : 0;
    }

    public function __destruct()
    {
        async($this->close(...));
    }

    abstract public function read(
        ?Cancellation $cancellation = null,
        int $length = self::DEFAULT_READ_LENGTH,
    ): ?string;

    /**
     * @return Future<null>
     */
    abstract protected function push(string $data, int $position): Future;

    public function write(string $bytes): void
    {
        if ($this->isReading) {
            throw new PendingOperationError;
        }

        if (!$this->writable) {
            throw new ClosedException("The file is no longer writable");
        }

        if ($this->queue->isEmpty()) {
            $future = $this->push($bytes, $this->position);
        } else {
            $position = $this->position;
            /** @var Future $future */
            $future = $this->queue->top()->map(fn () => $this->push($bytes, $position)->await());
        }

        $this->queue->push($future);

        $future->await();
    }

    public function end(): void
    {
        $this->writable = false;

        if ($this->queue->isEmpty()) {
            $this->close();
            return;
        }

        $future = $this->queue->top()->finally($this->close(...));
        $this->queue->push($future);

        $future->await();
    }

    /**
     * @return Future<null>
     */
    abstract protected function trim(int $size): Future;

    public function truncate(int $size): void
    {
        if ($this->isReading) {
            throw new PendingOperationError;
        }

        if (!$this->writable) {
            throw new ClosedException("The file is no longer writable");
        }

        if ($this->queue->isEmpty()) {
            $future = $this->trim($size);
        } else {
            $future = $this->queue->top()->map(fn () => $this->trim($size)->await());
        }

        $this->queue->push($future);

        $future->await();
    }

    public function seek(int $position, Whence $whence = Whence::Start): int
    {
        if ($this->isReading) {
            throw new PendingOperationError;
        }

        return match ($whence) {
            Whence::Start => $this->position = $position,
            Whence::Current => $this->position += $position,
            Whence::End => $this->position = $this->size + $position,
            default => throw new \Error("Invalid whence parameter; Start, Current or End expected"),
        };
    }

    public function tell(): int
    {
        return $this->position;
    }

    public function eof(): bool
    {
        return $this->queue->isEmpty() && $this->size <= $this->position;
    }

    public function isReadable(): bool
    {
        return !$this->isClosed();
    }

    public function isSeekable(): bool
    {
        return !$this->isClosed();
    }

    public function isWritable(): bool
    {
        return $this->writable;
    }

    public function getPath(): string
    {
        return $this->path;
    }

    public function getMode(): string
    {
        return $this->mode;
    }
}
<?php declare(strict_types=1);

namespace Amp\File\Internal;

use Revolt\EventLoop;

/** @internal */
final class Cache
{
    private readonly object $sharedState;

    private readonly string $ttlWatcherId;

    /**
     * @param int      $gcInterval The frequency in milliseconds at which expired cache entries should be garbage
     *     collected.
     * @param int|null $maxSize The maximum size of cache array (number of elements).
     */
    public function __construct(int $gcInterval = 1000, private readonly ?int $maxSize = null)
    {
        // By using a shared state object we're able to use `__destruct()` for "normal" garbage collection of both this
        // instance and the loop's watcher. Otherwise, this object could only be GC'd when the TTL watcher was cancelled
        // at the loop layer.
        $this->sharedState = $sharedState = new class {
            /** @var array[] */
            public array $cache = [];
            /** @var int[] */
            public array $cacheTimeouts = [];

            public bool $isSortNeeded = false;

            public function collectGarbage(): void
            {
                $now = \time();

                if ($this->isSortNeeded) {
                    \asort($this->cacheTimeouts);
                    $this->isSortNeeded = false;
                }

                foreach ($this->cacheTimeouts as $key => $expiry) {
                    if ($now <= $expiry) {
                        break;
                    }

                    unset(
                        $this->cache[$key],
                        $this->cacheTimeouts[$key]
                    );
                }
            }
        };

        $this->ttlWatcherId = EventLoop::repeat($gcInterval, $sharedState->collectGarbage(...));

        EventLoop::unreference($this->ttlWatcherId);
    }

    public function __destruct()
    {
        $this->sharedState->cache = [];
        $this->sharedState->cacheTimeouts = [];

        EventLoop::cancel($this->ttlWatcherId);
    }

    public function get(string $key): ?array
    {
        if (!isset($this->sharedState->cache[$key])) {
            return null;
        }

        if (isset($this->sharedState->cacheTimeouts[$key]) && \time() > $this->sharedState->cacheTimeouts[$key]) {
            unset(
                $this->sharedState->cache[$key],
                $this->sharedState->cacheTimeouts[$key]
            );

            return null;
        }

        return $this->sharedState->cache[$key];
    }

    public function set(string $key, array $value, ?int $ttl = null): void
    {
        if ($ttl === null) {
            unset($this->sharedState->cacheTimeouts[$key]);
        } elseif ($ttl >= 0) {
            $expiry = \time() + $ttl;
            $this->sharedState->cacheTimeouts[$key] = $expiry;
            $this->sharedState->isSortNeeded = true;
        } else {
            throw new \Error("Invalid cache TTL ({$ttl}; integer >= 0 or null required");
        }

        unset($this->sharedState->cache[$key]);
        if (\count($this->sharedState->cache) === $this->maxSize) {
            \array_shift($this->sharedState->cache);
        }

        $this->sharedState->cache[$key] = $value;
    }

    public function delete(string $key): bool
    {
        $exists = isset($this->sharedState->cache[$key]);

        unset(
            $this->sharedState->cache[$key],
            $this->sharedState->cacheTimeouts[$key]
        );

        return $exists;
    }
}
<?php declare(strict_types=1);

namespace Amp\File\Internal;

use Revolt\EventLoop\Driver as EventLoopDriver;

/** @internal */
final class EioPoll
{
    /** @var resource */
    private static $stream;

    private readonly string $watcher;

    private int $requests = 0;

    public function __construct(private readonly EventLoopDriver $driver)
    {
        if (!self::$stream) {
            if (\function_exists('eio_init')) {
                eio_init();
            }
            self::$stream = \eio_get_event_stream();
        }

        $this->watcher = $this->driver->onReadable(self::$stream, static function (): void {
            while (\eio_npending()) {
                \eio_poll();
            }
        });

        $this->driver->disable($this->watcher);
    }

    public function __destruct()
    {
        $this->driver->cancel($this->watcher);

        // Ensure there are no active operations anymore. This is a safe-guard as some operations might not be
        // finished on loop exit due to not being awaited. This also ensures a clean shutdown for these if a PHP
        // execution context still exists.
        \eio_event_loop();
    }

    public function listen(): void
    {
        if ($this->requests++ === 0) {
            $this->driver->enable($this->watcher);
        }
    }

    public function done(): void
    {
        if (--$this->requests === 0) {
            $this->driver->disable($this->watcher);
        }

        \assert($this->requests >= 0);
    }
}
<?php declare(strict_types=1);

namespace Amp\File\Internal;

use Revolt\EventLoop\Driver as EventLoopDriver;

/** @internal */
final class UvPoll
{
    private readonly string $watcher;

    private int $requests = 0;

    public function __construct(private readonly EventLoopDriver $driver)
    {
        // Create dummy watcher to keep loop running while polling.

        /** @psalm-suppress InternalMethod */
        $this->watcher = $this->driver->repeat(600, static fn () => null);

        /** @psalm-suppress InternalMethod */
        $this->driver->disable($this->watcher);
    }

    public function __destruct()
    {
        $this->driver->cancel($this->watcher);
    }

    public function listen(): void
    {
        if ($this->requests++ === 0) {
            /** @psalm-suppress InternalMethod */
            $this->driver->enable($this->watcher);
        }
    }

    public function done(): void
    {
        if (--$this->requests === 0) {
            /** @psalm-suppress InternalMethod */
            $this->driver->disable($this->watcher);
        }

        \assert($this->requests >= 0);
    }
}
<?php declare(strict_types=1);

namespace Amp\File\Internal;

use Amp\Cancellation;
use Amp\Parallel\Worker\Task;
use Amp\Parallel\Worker\Worker;

/** @internal */
final class FileWorker
{
    /**
     * @param \Closure(Worker):void $push Closure to push the worker back into the queue.
     */
    public function __construct(
        private readonly Worker $worker,
        private readonly \Closure $push
    ) {
    }

    /**
     * Automatically pushes the worker back into the queue.
     */
    public function __destruct()
    {
        ($this->push)($this->worker);
    }

    public function isRunning(): bool
    {
        return $this->worker->isRunning();
    }

    public function isIdle(): bool
    {
        return $this->worker->isIdle();
    }

    public function execute(Task $task, ?Cancellation $cancellation = null): mixed
    {
        return $this->worker->submit($task, $cancellation)->await();
    }

    public function shutdown(): void
    {
        $this->worker->shutdown();
    }

    public function kill(): void
    {
        $this->worker->kill();
    }
}
<?php declare(strict_types=1);

namespace Amp\File\Internal;

use Amp\ByteStream\ClosedException;
use Amp\ByteStream\StreamException;
use Amp\Cache\CacheException;
use Amp\Cache\LocalCache;
use Amp\Cancellation;
use Amp\File\Driver\BlockingFile;
use Amp\File\Driver\BlockingFilesystemDriver;
use Amp\File\FilesystemException;
use Amp\Parallel\Worker\Task;
use Amp\Sync\Channel;

/**
 * @codeCoverageIgnore
 * @internal
 * @implements Task<mixed, never, never>
 */
final class FileTask implements Task
{
    private static ?LocalCache $cache = null;

    private static ?BlockingFilesystemDriver $driver = null;

    /**
     * @param int|null $id File ID.
     *
     * @throws \Error
     */
    public function __construct(
        private readonly string $operation,
        private readonly array $args = [],
        private readonly ?int $id = null,
    ) {
        if ($operation === '') {
            throw new \Error('Operation must be a non-empty string');
        }
    }

    /**
     * @throws FilesystemException
     * @throws CacheException
     * @throws ClosedException
     * @throws StreamException
     */
    public function run(Channel $channel, Cancellation $cancellation): mixed
    {
        $cache = self::$cache ??= new LocalCache();
        $driver = self::$driver ??= new BlockingFilesystemDriver();

        if ('f' === $this->operation[0]) {
            if ("fopen" === $this->operation) {
                $file = $driver->openFile(...$this->args);

                $size = $driver->getStatus($file->getPath())["size"]
                    ?? throw new FilesystemException("Could not determine file size");

                $id = $file->getId();
                $cache->set((string) $id, $file);

                return [$id, $size, $file->getMode()];
            }

            if ($this->id === null) {
                throw new FilesystemException("No file ID provided");
            }

            $id = (string) $this->id;

            $file = $cache->get($id);
            if ($file === null) {
                throw new FilesystemException(\sprintf(
                    "No file handle with the ID %d has been opened on the worker",
                    $this->id
                ));
            }

            if (!$file instanceof BlockingFile) {
                throw new FilesystemException("File storage found in inconsistent state");
            }

            switch ($this->operation) {
                case "fread":
                    return $file->read($cancellation, ...$this->args);

                case "fwrite":
                    $file->write(...$this->args);
                    return null;

                case "fseek":
                    return $file->seek(...$this->args);

                case "ftruncate":
                    $file->truncate(...$this->args);
                    return null;

                case "fclose":
                    $cache->delete($id);
                    $file->close();
                    return null;

                default:
                    throw new \Error('Invalid operation');
            }
        }

        switch ($this->operation) {
            case "getStatus":
            case "deleteFile":
            case "move":
            case "createHardlink":
            case "createSymlink":
            case "resolveSymlink":
            case "getLinkStatus":
            case "exists":
            case "createDirectory":
            case "createDirectoryRecursively":
            case "listFiles":
            case "deleteDirectory":
            case "changePermissions":
            case "changeOwner":
            case "touch":
            case "read":
            case "write":
                return $driver->{$this->operation}(...$this->args);

            default:
                throw new \Error("Invalid operation - " . $this->operation);
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\File;

use Amp\Cache\CacheException;
use Amp\Cache\StringCache;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Sync\KeyedMutex;
use Amp\Sync\Lock;
use Revolt\EventLoop;

/**
 * A cache which stores data in files in a directory.
 */
final class FileCache implements StringCache
{
    use ForbidCloning;
    use ForbidSerialization;

    private readonly Filesystem $filesystem;

    private readonly string $directory;

    private ?string $gcWatcher;

    public function __construct(
        string $directory,
        private readonly KeyedMutex $mutex,
        ?Filesystem $filesystem = null,
    ) {
        $filesystem ??= filesystem();
        $this->filesystem = $filesystem;
        $this->directory = $directory = \rtrim($directory, "/\\");

        $gcWatcher = static function () use ($directory, $mutex, $filesystem): void {
            try {
                $files = $filesystem->listFiles($directory);

                foreach ($files as $file) {
                    if (\strlen($file) !== 70 || !\str_ends_with($file, '.cache')) {
                        continue;
                    }

                    try {
                        $lock = $mutex->acquire($file);
                    } catch (\Throwable) {
                        continue;
                    }

                    try {
                        $handle = $filesystem->openFile($directory . '/' . $file, 'r');
                        $ttl = $handle->read(length: 4);

                        if ($ttl === null || \strlen($ttl) !== 4) {
                            $handle->close();
                            continue;
                        }

                        $ttl = \unpack('Nttl', $ttl)['ttl'];
                        if ($ttl < \time()) {
                            $filesystem->deleteFile($directory . '/' . $file);
                        }
                    } catch (\Throwable) {
                        // ignore
                    } finally {
                        $lock->release();
                    }
                }
            } catch (\Throwable) {
                // ignore
            }
        };

        // trigger once, so short running scripts also GC and don't grow forever
        EventLoop::defer($gcWatcher);

        $this->gcWatcher = EventLoop::repeat(300, $gcWatcher);

        EventLoop::unreference($this->gcWatcher);
    }

    public function __destruct()
    {
        if ($this->gcWatcher !== null) {
            EventLoop::cancel($this->gcWatcher);
        }
    }

    public function get(string $key): ?string
    {
        $filename = $this->getFilename($key);

        $lock = $this->lock($filename);

        try {
            $cacheContent = $this->filesystem->read($this->directory . '/' . $filename);

            if (\strlen($cacheContent) < 4) {
                return null;
            }

            $ttl = \unpack('Nttl', \substr($cacheContent, 0, 4))['ttl'];
            if ($ttl < \time()) {
                $this->filesystem->deleteFile($this->directory . '/' . $filename);

                return null;
            }

            $value = \substr($cacheContent, 4);

            \assert(\is_string($value));

            return $value;
        } catch (\Throwable) {
            return null;
        } finally {
            $lock->release();
        }
    }

    public function set(string $key, string $value, ?int $ttl = null): void
    {
        if ($ttl < 0) {
            throw new \Error("Invalid cache TTL ({$ttl}); integer >= 0 or null required");
        }

        $filename = $this->getFilename($key);

        $lock = $this->lock($filename);

        if ($ttl === null) {
            $ttl = \PHP_INT_MAX;
        } else {
            $ttl = \time() + $ttl;
        }

        $encodedTtl = \pack('N', $ttl);

        try {
            $this->filesystem->write($this->directory . '/' . $filename, $encodedTtl . $value);
        } finally {
            $lock->release();
        }
    }

    public function delete(string $key): ?bool
    {
        $filename = $this->getFilename($key);

        $lock = $this->lock($filename);

        try {
            $this->filesystem->deleteFile($this->directory . '/' . $filename);
        } catch (FilesystemException) {
            return false;
        } finally {
            $lock->release();
        }

        return true;
    }

    private static function getFilename(string $key): string
    {
        return \hash('sha256', $key) . '.cache';
    }

    private function lock(string $key): Lock
    {
        try {
            return $this->mutex->acquire($key);
        } catch (\Throwable $exception) {
            throw new CacheException(
                \sprintf('Exception thrown when obtaining the lock for key "%s"', $key),
                0,
                $exception
            );
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\File;

final class PendingOperationError extends \Error
{
    public function __construct(
        string $message = "The previous file operation must complete before another can be started",
        ?\Throwable $previous = null
    ) {
        parent::__construct($message, 0, $previous);
    }
}
<?php declare(strict_types=1);

namespace Amp\File;

interface FilesystemDriver
{
    /**
     * Open a handle for the specified path.
     *
     * @throws FilesystemException
     */
    public function openFile(string $path, string $mode): File;

    /**
     * Get file status; also known as stat operation.
     *
     * If the requested path does not exist, it returns {@code null}.
     *
     * @param string $path The file system path to stat.
     */
    public function getStatus(string $path): ?array;

    /**
     * Same as {@see FilesystemDriver::getStatus()} except if the path is a link then the link's data is returned.
     *
     * If the requested path does not exist, this method will return NULL.
     *
     * @param string $path The file system path to stat.
     *
     * @return array|null An associative array upon successful completion.
     */
    public function getLinkStatus(string $path): ?array;

    /**
     * Create a symlink $link pointing to the file/directory located at $target.
     *
     * @throws FilesystemException
     */
    public function createSymlink(string $target, string $link): void;

    /**
     * Create a hard link $link pointing to the file/directory located at $target.
     *
     * @throws FilesystemException
     */
    public function createHardlink(string $target, string $link): void;

    /**
     * Resolve the symlink at $path.
     *
     * @throws FilesystemException
     */
    public function resolveSymlink(string $target): string;

    /**
     * Move / rename a file or directory.
     *
     * @throws FilesystemException
     */
    public function move(string $from, string $to): void;

    /**
     * Delete a file.
     *
     * @throws FilesystemException
     */
    public function deleteFile(string $path): void;

    /**
     * Create a directory.
     *
     * @throws FilesystemException
     */
    public function createDirectory(string $path, int $mode = 0777): void;

    /**
     * Create a directory recursively.
     *
     * @throws FilesystemException If the operation fails.
     */
    public function createDirectoryRecursively(string $path, int $mode = 0777): void;

    /**
     * Delete a directory.
     *
     * @throws FilesystemException If the operation fails.
     */
    public function deleteDirectory(string $path): void;

    /**
     * Retrieve an array of files and directories inside the specified path.
     *
     * Dot entries are not included in the resulting array (i.e. "." and "..").
     *
     * @return list<string>
     *
     * @throws FilesystemException If the operation fails.
     */
    public function listFiles(string $path): array;

    /**
     * chmod a file or directory.
     *
     * @throws FilesystemException If the operation fails.
     */
    public function changePermissions(string $path, int $mode): void;

    /**
     * chown a file or directory.
     *
     * @throws FilesystemException If the operation fails.
     */
    public function changeOwner(string $path, ?int $uid, ?int $gid): void;

    /**
     * Update the access and modification time of the specified path.
     *
     * If the file does not exist it will be created automatically.
     *
     * @param int|null $modificationTime The touch time. If $time is not supplied, the current system time is used.
     * @param int|null $accessTime The access time. If $atime is not supplied, value passed to the $time parameter is
     *     used.
     *
     * @throws FilesystemException If the operation fails.
     */
    public function touch(string $path, ?int $modificationTime, ?int $accessTime): void;

    /**
     * Buffer the specified file's contents.
     *
     * @param string $path The file path from which to buffer contents.
     *
     * @return string The file contents.
     *
     * @throws FilesystemException If the operation fails.
     */
    public function read(string $path): string;

    /**
     * Write the contents string to the specified path.
     *
     * @param string $path The file path to which to $contents should be written.
     * @param string $contents The data to write to the specified $path.
     *
     * @throws FilesystemException If the operation fails.
     */
    public function write(string $path, string $contents): void;
}
<?php declare(strict_types=1);

namespace Amp\File;

use Amp\File\Driver\EioFilesystemDriver;
use Amp\File\Driver\ParallelFilesystemDriver;
use Amp\File\Driver\StatusCachingFilesystemDriver;
use Amp\File\Driver\UvFilesystemDriver;
use Revolt\EventLoop;

/**
 * Retrieve the application-wide filesystem instance.
 *
 * @param FilesystemDriver|null $driver Use the specified object as the application-wide filesystem instance.
 *
 */
function filesystem(?FilesystemDriver $driver = null): Filesystem
{
    static $map;
    $map ??= new \WeakMap();

    $loop = EventLoop::getDriver();

    if ($driver === null) {
        if (isset($map[$loop])) {
            return $map[$loop];
        }

        $driver = createDefaultDriver();

        if (!\defined("AMP_WORKER")) { // Prevent caching in workers, cache in parent instead.
            $driver = new StatusCachingFilesystemDriver($driver);
        }
    }

    return $map[$loop] = new Filesystem($driver);
}

/**
 * Create a new filesystem driver best-suited for the current environment.
 */
function createDefaultDriver(): FilesystemDriver
{
    $driver = EventLoop::getDriver();

    if (UvFilesystemDriver::isSupported($driver)) {
        return new UvFilesystemDriver($driver);
    }

    if (EioFilesystemDriver::isSupported()) {
        return new EioFilesystemDriver($driver);
    }

    return new ParallelFilesystemDriver;
}

/**
 * Open a handle for the specified path.
 *
 * @throws FilesystemException
 */
function openFile(string $path, string $mode): File
{
    return filesystem()->openFile($path, $mode);
}

/**
 * Execute a file stat operation.
 *
 * If the requested path does not exist the function will return NULL.
 *
 * @param string $path File system path.
 */
function getStatus(string $path): ?array
{
    return filesystem()->getStatus($path);
}

/**
 * Same as {@see Filesystem::getStatus()} except if the path is a link then the link's data is returned.
 *
 * If the requested path does not exist the function will return NULL.
 *
 * @param string $path File system path.
 */
function getLinkStatus(string $path): ?array
{
    return filesystem()->getLinkStatus($path);
}

/**
 * Does the specified path exist?
 *
 * This function should never resolve as a failure -- only a successful bool value
 * indicating the existence of the specified path.
 *
 * @param string $path File system path.
 */
function exists(string $path): bool
{
    return filesystem()->exists($path);
}

/**
 * Retrieve the size in bytes of the file at the specified path.
 *
 * @param string $path File system path.
 */
function getSize(string $path): int
{
    return filesystem()->getSize($path);
}

/**
 * Does the specified path exist and is it a directory?
 *
 * @param string $path File system path.
 */
function isDirectory(string $path): bool
{
    return filesystem()->isDirectory($path);
}

/**
 * Does the specified path exist and is it a file?
 *
 * @param string $path File system path.
 */
function isFile(string $path): bool
{
    return filesystem()->isFile($path);
}

/**
 * Does the specified path exist and is it a symlink?
 *
 * @param string $path File system path.
 */
function isSymlink(string $path): bool
{
    return filesystem()->isSymlink($path);
}

/**
 * Retrieve the path's last modification time as a unix timestamp.
 *
 * @param string $path File system path.
 */
function getModificationTime(string $path): int
{
    return filesystem()->getModificationTime($path);
}

/**
 * Retrieve the path's last access time as a unix timestamp.
 *
 * @param string $path File system path.
 */
function getAccessTime(string $path): int
{
    return filesystem()->getAccessTime($path);
}

/**
 * Retrieve the path's creation time as a unix timestamp.
 *
 * @param string $path File system path.
 */
function getCreationTime(string $path): int
{
    return filesystem()->getCreationTime($path);
}

/**
 * Create a symlink $link pointing to the file/directory located at $original.
 */
function createSymlink(string $original, string $link): void
{
    filesystem()->createSymlink($original, $link);
}

/**
 * Create a hard link $link pointing to the file/directory located at $target.
 */
function createHardlink(string $target, string $link): void
{
    filesystem()->createHardlink($target, $link);
}

/**
 * Resolve the symlink at $path.
 */
function resolveSymlink(string $path): string
{
    return filesystem()->resolveSymlink($path);
}

/**
 * Move / rename a file or directory.
 */
function move(string $from, string $to): void
{
    filesystem()->move($from, $to);
}

/**
 * Delete a file.
 */
function deleteFile(string $path): void
{
    filesystem()->deleteFile($path);
}

/**
 * Create a directory.
 */
function createDirectory(string $path, int $mode = 0777): void
{
    filesystem()->createDirectory($path, $mode);
}

/**
 * Create a directory recursively.
 */
function createDirectoryRecursively(string $path, int $mode = 0777): void
{
    filesystem()->createDirectoryRecursively($path, $mode);
}

/**
 * Delete a directory.
 */
function deleteDirectory(string $path): void
{
    filesystem()->deleteDirectory($path);
}

/**
 * Retrieve an array of files and directories inside the specified path.
 *
 * Dot entries are not included in the resulting array (i.e. "." and "..").
 *
 * @return list<string>
 */
function listFiles(string $path): array
{
    return filesystem()->listFiles($path);
}

/**
 * Change the permissions of a file or directory.
 */
function changePermissions(string $path, int $mode): void
{
    filesystem()->changePermissions($path, $mode);
}

/**
 * Change the ownership of a file or directory.
 *
 * @param int|null $uid null to ignore
 * @param int|null $gid null to ignore
 */
function changeOwner(string $path, ?int $uid, ?int $gid = null): void
{
    filesystem()->changeOwner($path, $uid, $gid);
}

/**
 * Update the access and modification time of the specified path.
 *
 * If the file does not exist it will be created automatically.
 *
 * @param int|null $modificationTime The touch time. If $time is not supplied, the current system time is used.
 * @param int|null $accessTime The access time. If not supplied, the modification time is used.
 */
function touch(string $path, ?int $modificationTime = null, ?int $accessTime = null): void
{
    filesystem()->touch($path, $modificationTime, $accessTime);
}

/**
 * Read the specified file's contents.
 *
 * @param string $path The file path from which to buffer contents.
 */
function read(string $path): string
{
    return filesystem()->read($path);
}

/**
 * Write the contents string to the specified path.
 *
 * @param string $path The file path to which to $contents should be written.
 * @param string $contents The data to write to the specified $path.
 */
function write(string $path, string $contents): void
{
    filesystem()->write($path, $contents);
}
<?php declare(strict_types=1);

namespace Amp\File;

use Amp\Cancellation;
use Amp\Sync\KeyedMutex;
use Amp\Sync\Lock;
use Amp\Sync\SyncException;
use function Amp\delay;

final class KeyedFileMutex implements KeyedMutex
{
    private const LATENCY_TIMEOUT = 0.01;
    private const DELAY_LIMIT = 1;

    private readonly Filesystem $filesystem;

    private readonly string $directory;

    /**
     * @param string $directory Directory in which to store key files.
     */
    public function __construct(string $directory, ?Filesystem $filesystem = null)
    {
        $this->filesystem = $filesystem ?? filesystem();
        $this->directory = \rtrim($directory, "/\\");
    }

    public function acquire(string $key, ?Cancellation $cancellation = null): Lock
    {
        if (!$this->filesystem->isDirectory($this->directory)) {
            throw new SyncException(\sprintf('Directory "%s" does not exist or is not a directory', $this->directory));
        }

        $filename = $this->getFilename($key);

        // Try to create the lock file. If the file already exists, someone else
        // has the lock, so set an asynchronous timer and try again.
        for ($attempt = 0; true; ++$attempt) {
            try {
                $file = $this->filesystem->openFile($filename, 'x');

                // Return a lock object that can be used to release the lock on the mutex.
                $lock = new Lock(fn () => $this->release($filename));

                $file->close();

                return $lock;
            } catch (FilesystemException) {
                delay(\min(self::DELAY_LIMIT, self::LATENCY_TIMEOUT * (2 ** $attempt)), cancellation: $cancellation);
            }
        }
    }

    /**
     * Releases the lock on the mutex.
     *
     * @throws SyncException
     */
    private function release(string $filename): void
    {
        try {
            $this->filesystem->deleteFile($filename);
        } catch (\Throwable $exception) {
            throw new SyncException(
                'Failed to unlock the mutex file: ' . $filename,
                previous: $exception,
            );
        }
    }

    private function getFilename(string $key): string
    {
        return $this->directory . '/' . \hash('sha256', $key) . '.lock';
    }
}
<?php declare(strict_types=1);

namespace Amp\File;

final class Filesystem
{
    private FilesystemDriver $driver;

    public function __construct(FilesystemDriver $driver)
    {
        $this->driver = $driver;
    }

    /**
     * Open a handle for the specified path.
     *
     * @throws FilesystemException
     */
    public function openFile(string $path, string $mode): File
    {
        return $this->driver->openFile($path, $mode);
    }

    /**
     * Execute a file stat operation.
     *
     * If the requested path does not exist, it will return NULL.
     *
     * @param string $path File system path.
     */
    public function getStatus(string $path): ?array
    {
        return $this->driver->getStatus($path);
    }

    /**
     * Same as {@see Filesystem::getStatus()} except if the path is a link then the link's data is returned.
     *
     * If the requested path does not exist, it will return NULL.
     *
     * @param string $path File system path.
     */
    public function getLinkStatus(string $path): ?array
    {
        return $this->driver->getLinkStatus($path);
    }

    /**
     * Does the specified path exist?
     *
     * This function should never resolve as a failure -- only a successful bool value
     * indicating the existence of the specified path.
     *
     * @param string $path File system path.
     */
    public function exists(string $path): bool
    {
        $result = $this->getStatus($path);

        return $result !== null;
    }

    /**
     * Retrieve the size in bytes of the file at the specified path.
     *
     * If the path does not exist or is not a regular file, this method will throw.
     *
     * @param string $path File system path.
     *
     * @return int Size in bytes.
     *
     * @throws FilesystemException If the path does not exist or is not a file.
     */
    public function getSize(string $path): int
    {
        $result = $this->getStatus($path);
        if ($result === null) {
            throw new FilesystemException("Failed to read file size for '{$path}'");
        }

        if ($result['mode'] & 0100000) {
            return $result["size"];
        }

        throw new FilesystemException("Failed to read file size for '{$path}'; specified path is not a regular file");
    }

    /**
     * Does the specified path exist and is it a directory?
     *
     * @param string $path File system path.
     */
    public function isDirectory(string $path): bool
    {
        $result = $this->getStatus($path);
        if ($result === null) {
            return false;
        }

        return (bool) ($result['mode'] & 0040000);
    }

    /**
     * Does the specified path exist and is it a file?
     *
     * @param string $path File system path.
     */
    public function isFile(string $path): bool
    {
        $result = $this->getStatus($path);
        if ($result === null) {
            return false;
        }

        return (bool) ($result['mode'] & 0100000);
    }

    /**
     * Does the specified path exist and is it a symlink?
     *
     * If the path does not exist, this method will return FALSE.
     *
     * @param string $path File system path.
     */
    public function isSymlink(string $path): bool
    {
        $result = $this->getLinkStatus($path);
        if ($result === null) {
            return false;
        }

        return ($result['mode'] & 0120000) === 0120000;
    }

    /**
     * Retrieve the path's last modification time as a unix timestamp.
     *
     * @param string $path File system path.
     *
     * @throws FilesystemException If the path does not exist.
     */
    public function getModificationTime(string $path): int
    {
        $result = $this->getStatus($path);
        if ($result === null) {
            throw new FilesystemException("Failed to read file modification time for '{$path}'");
        }

        return $result["mtime"];
    }

    /**
     * Retrieve the path's last access time as a unix timestamp.
     *
     * @param string $path File system path.
     *
     * @throws FilesystemException If the path does not exist.
     */
    public function getAccessTime(string $path): int
    {
        $result = $this->getStatus($path);
        if ($result === null) {
            throw new FilesystemException("Failed to read file access time for '{$path}'");
        }

        return $result["atime"];
    }

    /**
     * Retrieve the path's creation time as a unix timestamp.
     *
     * @param string $path File system path.
     *
     * @throws FilesystemException If the path does not exist.
     */
    public function getCreationTime(string $path): int
    {
        $result = $this->getStatus($path);
        if ($result === null) {
            throw new FilesystemException("Failed to read file creation time for '{$path}'");
        }

        return $result["ctime"];
    }

    /**
     * Create a symlink $link pointing to the file/directory located at $original.
     *
     * @throws FilesystemException If the operation fails.
     */
    public function createSymlink(string $original, string $link): void
    {
        $this->driver->createSymlink($original, $link);
    }

    /**
     * Create a hard link $link pointing to the file/directory located at $target.
     */
    public function createHardlink(string $target, string $link): void
    {
        $this->driver->createHardlink($target, $link);
    }

    /**
     * Resolve the symlink at $path.
     *
     * @throws FilesystemException
     */
    public function resolveSymlink(string $path): string
    {
        return $this->driver->resolveSymlink($path);
    }

    /**
     * Move / rename a file or directory.
     *
     *
     * @throws FilesystemException If the operation fails.
     */
    public function move(string $from, string $to): void
    {
        $this->driver->move($from, $to);
    }

    /**
     * Delete a file.
     *
     * @throws FilesystemException If the operation fails.
     */
    public function deleteFile(string $path): void
    {
        $this->driver->deleteFile($path);
    }

    /**
     * Create a directory.
     *
     * @throws FilesystemException If the operation fails.
     */
    public function createDirectory(string $path, int $mode = 0777): void
    {
        $this->driver->createDirectory($path, $mode);
    }

    /**
     * Create a directory recursively.
     *
     * @throws FilesystemException If the operation fails.
     */
    public function createDirectoryRecursively(string $path, int $mode = 0777): void
    {
        $this->driver->createDirectoryRecursively($path, $mode);
    }

    /**
     * Delete a directory.
     *
     * @throws FilesystemException If the operation fails.
     */
    public function deleteDirectory(string $path): void
    {
        $this->driver->deleteDirectory($path);
    }

    /**
     * Retrieve an array of files and directories inside the specified path.
     *
     * Dot entries are not included in the resulting array (i.e. "." and "..").
     *
     * @return list<string>
     *
     * @throws FilesystemException If the operation fails.
     */
    public function listFiles(string $path): array
    {
        return $this->driver->listFiles($path);
    }

    /**
     * Change permissions of a file or directory.
     */
    public function changePermissions(string $path, int $mode): void
    {
        $this->driver->changePermissions($path, $mode);
    }

    /**
     * Change ownership of a file or directory.
     *
     * @param int|null $uid null to ignore
     * @param int|null $gid null to ignore
     */
    public function changeOwner(string $path, ?int $uid, ?int $gid = null): void
    {
        $this->driver->changeOwner($path, $uid, $gid);
    }

    /**
     * Update the access and modification time of the specified path.
     *
     * If the file does not exist it will be created automatically.
     *
     * @param int|null $modificationTime The touch time. If $time is not supplied, the current system time is used.
     * @param int|null $accessTime The access time. If not supplied, the modification time is used.
     */
    public function touch(string $path, ?int $modificationTime = null, ?int $accessTime = null): void
    {
        $this->driver->touch($path, $modificationTime, $accessTime);
    }

    /**
     * Buffer the specified file's contents.
     *
     * @param string $path The file path from which to buffer contents.
     */
    public function read(string $path): string
    {
        return $this->driver->read($path);
    }

    /**
     * Write the contents string to the specified path.
     *
     * @param string $path The file path to which to $contents should be written.
     * @param string $contents The data to write to the specified $path.
     */
    public function write(string $path, string $contents): void
    {
        $this->driver->write($path, $contents);
    }
}
{
    "name": "amphp/file",
    "homepage": "https://github.com/amphp/file",
    "description": "Non-blocking access to the filesystem based on Amp and Revolt.",
    "support": {
        "issues": "https://github.com/amphp/file/issues"
    },
    "keywords": [
        "file",
        "disk",
        "static",
        "async",
        "non-blocking",
        "amp",
        "amphp",
        "io",
        "filesystem"
    ],
    "license": "MIT",
    "authors": [
        {
            "name": "Daniel Lowrey",
            "email": "rdlowrey@php.net"
        },
        {
            "name": "Aaron Piotrowski",
            "email": "aaron@trowski.com"
        },
        {
            "name": "Niklas Keller",
            "email": "me@kelunik.com"
        }
    ],
    "require": {
        "php": ">=8.1",
        "amphp/amp": "^3",
        "amphp/byte-stream": "^2",
        "amphp/cache": "^2",
        "amphp/parallel": "^2.3",
        "amphp/sync": "^2",
        "revolt/event-loop": "^1"
    },
    "require-dev": {
        "amphp/phpunit-util": "^3",
        "phpunit/phpunit": "^9",
        "psalm/phar": "5.22.2",
        "amphp/php-cs-fixer-config": "^2"
    },
    "suggest": {
        "ext-eio": "^2 || ^3",
        "ext-uv": "^0.3 || ^0.2"
    },
    "autoload": {
        "psr-4": {
            "Amp\\File\\": "src"
        },
        "files": ["src/functions.php"]
    },
    "autoload-dev": {
        "psr-4": {
            "Amp\\File\\Test\\": "test",
            "Amp\\Cache\\Test\\": "vendor/amphp/cache/test",
            "Amp\\Sync\\": "vendor/amphp/sync/test"
        }
    },
    "config": {
        "preferred-install": {
            "amphp/cache": "source",
            "amphp/sync": "source"
        }
    },
    "scripts": {
        "check": [
            "@code-style",
            "@test"
        ],
        "code-style": "php-cs-fixer fix -v --diff",
        "test": "@php -dzend.assertions=1 -dassert.exception=1 ./vendor/bin/phpunit --coverage-text"
    }
}
<?php declare(strict_types=1);

require __DIR__ . '/../vendor/autoload.php';

$root = __DIR__ . "/../vendor/http2jp/hpack-test-case";
$paths = glob("$root/*/*.json");

foreach ($paths as $path) {
    if (basename(dirname($path)) === "raw-data") {
        continue;
    }

    $data = json_decode(file_get_contents($path));
    $cases = [];
    foreach ($data->cases as $case) {
        foreach ($case->headers as &$header) {
            $header = (array) $header;
            $header = [key($header), current($header)];
        }
        $cases[$case->seqno] = [hex2bin($case->wire), $case->headers];
    }

    $tests[] = $cases;
}

$minDuration = \PHP_INT_MAX;
$minOps = \PHP_INT_MAX;

for ($i = 0; $i < 10; $i++) {
    $start = microtime(true);
    $ops = 0;

    foreach ($tests as $test) {
        $hpack = new Amp\Http\Internal\HPackNative;
        foreach ($cases as [$input, $output]) {
            $headers = $hpack->decode($input, 4096);
            $hpack->encode($headers);

            if ($headers !== $output) {
                print 'Invalid headers' . \PHP_EOL;
                exit(1);
            }

            $ops++;
        }
    }

    $duration = microtime(true) - $start;
    $minDuration = min($minDuration, $duration);
    $minOps = min($ops, $minOps);
}

print "$minOps in $minDuration seconds" . \PHP_EOL;
<?php

$config = new Amp\CodeStyle\Config();
$config->getFinder()
    ->in(__DIR__ . '/examples')
    ->in(__DIR__ . '/src')
    ->in(__DIR__ . '/test');

$config->setCacheFile(__DIR__ . '/.php_cs.cache');

return $config;
<?php

require __DIR__ . '/../../vendor/autoload.php';

use Amp\Http\HPack;

$fuzzer->setTarget(function (string $input) {
    (new HPack)->decode($input, 8192);
});

$fuzzer->setMaxLen(1024);
<?php

require __DIR__ . '/../../vendor/autoload.php';

use Amp\Http\Internal;

$fuzzer->setTarget(function (string $input) {
    $result1 = (new Internal\HPackNghttp2)->decode($input, 8192);
    $result2 = (new Internal\HPackNative)->decode($input, 8192);

    if ($result1 === null || $result2 === null) {
        return;
    }

    if ($result1 !== $result2) {
        throw new \Error('Mismatch: ' . var_export($result1, true) . ' / ' . var_export($result2, true));
    }
});

$fuzzer->setMaxLen(1024);
<?php

require __DIR__ . '/../../vendor/autoload.php';

use Amp\Http\HPack;

(new HPack)->decode(\file_get_contents($argv[1]), 8192);
<?php

/**
 * The MIT License (MIT).
 *
 * Copyright (c) 2017 Christian LÃ¼ck
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is furnished
 * to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

// Taken from https://github.com/leproxy/leproxy/blob/e2ca7917c17ac8b853800f5b390297a2a1525cf7/compile.php

$small = '';
$all = \token_get_all(\file_get_contents($argv[1]));

// search next non-whitespace/non-comment token
$next = function ($i) use (&$all) {
    for ($i = $i + 1; !isset($all[$i]) || \is_array($all[$i]) && ($all[$i][0] === T_COMMENT || $all[$i][0] === T_DOC_COMMENT || $all[$i][0] === T_WHITESPACE); ++$i);
    return $i;
};

// search previous non-whitespace/non-comment token
$prev = function ($i) use (&$all) {
    for ($i = $i -1; $i >= 0 && (!isset($all[$i]) || (\is_array($all[$i]) && ($all[$i][0] === T_COMMENT || $all[$i][0] === T_DOC_COMMENT || $all[$i][0] === T_WHITESPACE))); --$i);
    return $i;
};

$first = true;
foreach ($all as $i => $token) {
    if (\is_array($token) && ($token[0] === T_COMMENT || $token[0] === T_DOC_COMMENT)) {
        // remove all comments except first
        if ($first === true) {
            $first = false;
            continue;
        }
        unset($all[$i]);
    } elseif (\is_array($token) && $token[0] === T_PUBLIC) {
        // get next non-whitespace token after `public` visibility
        $token = $all[$next($i)];

        if (\is_array($token) && $token[0] === T_VARIABLE) {
            // use shorter variable notation `public $a` => `var $a`
            $all[$i] = [T_VAR, 'var'];
        } else {
            // remove unneeded public identifier `public static function a()` => `static function a()`
            unset($all[$i]);
        }
    } elseif (\is_array($token) && $token[0] === T_LNUMBER) {
        // Use shorter integer notation `0x0F` => `15` and `011` => `9`.
        // Technically, hex codes may be shorter for very large ints, but adding
        // another 2 leading chars is rarely worth it.
        // Optimizing floats is not really worth it, as they have many special
        // cases, such as e-notation and we would lose types for `0.0` => `0`.
        $all[$i][1] = (string) \intval($token[1], 0);
    } elseif (\is_array($token) && $token[0] === T_NEW) {
        // remove unneeded parenthesis for constructors without args `new a();` => `new a;`
        // jump over next token (class name), then next must be open parenthesis, followed by closing
        $open = $next($next($i));
        $close = $next($open);
        if ($all[$open] === '(' && $all[$close] === ')') {
            unset($all[$open], $all[$close]);
        }
    } elseif (\is_array($token) && $token[0] === T_STRING) {
        // replace certain functions with their shorter alias function name
        // http://php.net/manual/en/aliases.php
        static $replace = [
            'implode' => 'join',
            'fwrite' => 'fputs',
            'array_key_exists' => 'key_exists',
            'current' => 'pos',
        ];

        // check this has a replacement and "looks like" a function call
        // this works on a number of assumptions, such as not being aliased/namespaced
        if (isset($replace[$token[1]])) {
            $p = $all[$prev($i)];

            if ($all[$next($i)] === '(' && (!\is_array($p) || !\in_array($p[0], [T_FUNCTION, T_OBJECT_OPERATOR, T_DOUBLE_COLON, T_NEW]))) {
                $all[$i][1] = $replace[$all[$i][1]];
            }
        }
    } elseif (\is_array($token) && $token[0] === T_EXIT) {
        // replace `exit` with shorter alias `die`
        // it's a language construct, not a function (see above)
        $all[$i][1] = 'die';
    } elseif (\is_array($token) && $token[0] === T_RETURN) {
        // replace `return null;` with `return;`
        $t = $next($i);
        if (\is_array($all[$t]) && $all[$t][0] === T_STRING && $all[$t][1] === 'null' && $all[$next($t)] === ';') {
            unset($all[$t]);
        }
    }
}
$all = \array_values($all);
foreach ($all as $i => $token) {
    if (\is_array($token) && $token[0] === T_WHITESPACE) {
        if (\strpos($token[1], "\n") !== false) {
            $token = \strpos("()[]<>=+-*/%|,.:?!'\"\n", \substr($small, -1)) === false ? "\n" : '';
        } else {
            $last = \substr($small, -1);
            $next = isset($all[$i + 1]) ? \substr(\is_array($all[$i + 1]) ? $all[$i + 1][1] : $all[$i + 1], 0, 1) : ' ';

            $token = (\strpos('()[]{}<>;=+-*/%&|,.:?!@\'"' . "\r\n", $last) !== false || \strpos('()[]{}<>;=+-*/%&|,.:?!@\'"' . '\\$', $next) !== false) ? '' : ' ';
        }
    }

    $small .= isset($token[1]) ? $token[1] : $token;
}
\file_put_contents($argv[1], $small);
<?php declare(strict_types=1);

namespace Amp\Http;

use Amp\Http\Internal\HPackNative;
use Amp\Http\Internal\HPackNghttp2;

/**
 * @psalm-type HeaderArray = list<array{string, string}>
 */
final class HPack
{
    /** @var HPackNative|HPackNghttp2 */
    private $implementation;

    public function __construct(int $tableSizeLimit = 4096)
    {
        if (HPackNghttp2::isSupported()) {
            $this->implementation = new HPackNghttp2($tableSizeLimit);
        } else {
            $this->implementation = new HPackNative($tableSizeLimit);
        }
    }

    /**
     * @param string $input Input to decode.
     * @param int    $maxSize Maximum deflated size.
     *
     * @return array|null Decoded headers.
     */
    public function decode(string $input, int $maxSize): ?array
    {
        return $this->implementation->decode($input, $maxSize);
    }

    /**
     * @param HeaderArray $headers Headers to encode.
     *
     * @return string Encoded headers.
     *
     * @throws HPackException If encoding fails.
     */
    public function encode(array $headers): string
    {
        return $this->implementation->encode($headers);
    }
}
<?php declare(strict_types=1);

namespace Amp\Http;

final class HPackException extends \Exception
{
}
<?php declare(strict_types=1);
return[["\x00"=>["\xFF","\xC0"],"\x01"=>["\xFF","\xFF","\xB0"],"\x02"=>["\xFF","\xFF","\xFE","\x20"],"\x03"=>["\xFF","\xFF","\xFE","0"],"\x04"=>["\xFF","\xFF","\xFE","\x40"],"\x05"=>["\xFF","\xFF","\xFE","P"],"\x06"=>["\xFF","\xFF","\xFE","\x60"],"\x07"=>["\xFF","\xFF","\xFE","p"],"\x08"=>["\xFF","\xFF","\xFE","\x80"],"\x09"=>["\xFF","\xFF","\xEA"],"\x0A"=>["\xFF","\xFF","\xFF","\xF0"],"\x0B"=>["\xFF","\xFF","\xFE","\x90"],"\x0C"=>["\xFF","\xFF","\xFE","\xA0"],"\x0D"=>["\xFF","\xFF","\xFF","\xF4"],"\x0E"=>["\xFF","\xFF","\xFE","\xB0"],"\x0F"=>["\xFF","\xFF","\xFE","\xC0"],"\x10"=>["\xFF","\xFF","\xFE","\xD0"],"\x11"=>["\xFF","\xFF","\xFE","\xE0"],"\x12"=>["\xFF","\xFF","\xFE","\xF0"],"\x13"=>["\xFF","\xFF","\xFF","\x00"],"\x14"=>["\xFF","\xFF","\xFF","\x10"],"\x15"=>["\xFF","\xFF","\xFF","\x20"],"\x16"=>["\xFF","\xFF","\xFF","\xF8"],"\x17"=>["\xFF","\xFF","\xFF","0"],"\x18"=>["\xFF","\xFF","\xFF","\x40"],"\x19"=>["\xFF","\xFF","\xFF","P"],"\x1A"=>["\xFF","\xFF","\xFF","\x60"],"\x1B"=>["\xFF","\xFF","\xFF","p"],"\x1C"=>["\xFF","\xFF","\xFF","\x80"],"\x1D"=>["\xFF","\xFF","\xFF","\x90"],"\x1E"=>["\xFF","\xFF","\xFF","\xA0"],"\x1F"=>["\xFF","\xFF","\xFF","\xB0"],"\x20"=>["P"],"\x21"=>["\xFE","\x00"],"\x22"=>["\xFE","\x40"],"\x23"=>["\xFF","\xA0"],"\x24"=>["\xFF","\xC8"],"\x25"=>["T"],"\x26"=>["\xF8"],"\x27"=>["\xFF","\x40"],"\x28"=>["\xFE","\x80"],"\x29"=>["\xFE","\xC0"],"\x2A"=>["\xF9"],"\x2B"=>["\xFF","\x60"],"\x2C"=>["\xFA"],"-"=>["X"],"."=>["\x5C"],"\x2F"=>["\x60"],["\x00"],["\x08"],["\x10"],["d"],["h"],["l"],["p"],["t"],["x"],["\x7C"],"\x3A"=>["\xB8"],"\x3B"=>["\xFB"],"\x3C"=>["\xFF","\xF8"],"\x3D"=>["\x80"],"\x3E"=>["\xFF","\xB0"],"\x3F"=>["\xFF","\x00"],"\x40"=>["\xFF","\xD0"],"A"=>["\x84"],"B"=>["\xBA"],"C"=>["\xBC"],"D"=>["\xBE"],"E"=>["\xC0"],"F"=>["\xC2"],"G"=>["\xC4"],"H"=>["\xC6"],"I"=>["\xC8"],"J"=>["\xCA"],"K"=>["\xCC"],"L"=>["\xCE"],"M"=>["\xD0"],"N"=>["\xD2"],"O"=>["\xD4"],"P"=>["\xD6"],"Q"=>["\xD8"],"R"=>["\xDA"],"S"=>["\xDC"],"T"=>["\xDE"],"U"=>["\xE0"],"V"=>["\xE2"],"W"=>["\xE4"],"X"=>["\xFC"],"Y"=>["\xE6"],"Z"=>["\xFD"],"\x5B"=>["\xFF","\xD8"],"\x5C"=>["\xFF","\xFE","\x00"],"\x5D"=>["\xFF","\xE0"],"\x5E"=>["\xFF","\xF0"],"_"=>["\x88"],"\x60"=>["\xFF","\xFA"],"a"=>["\x18"],"b"=>["\x8C"],"c"=>["\x20"],"d"=>["\x90"],"e"=>["\x28"],"f"=>["\x94"],"g"=>["\x98"],"h"=>["\x9C"],"i"=>["0"],"j"=>["\xE8"],"k"=>["\xEA"],"l"=>["\xA0"],"m"=>["\xA4"],"n"=>["\xA8"],"o"=>["8"],"p"=>["\xAC"],"q"=>["\xEC"],"r"=>["\xB0"],"s"=>["\x40"],"t"=>["H"],"u"=>["\xB4"],"v"=>["\xEE"],"w"=>["\xF0"],"x"=>["\xF2"],"y"=>["\xF4"],"z"=>["\xF6"],"\x7B"=>["\xFF","\xFC"],"\x7C"=>["\xFF","\x80"],"\x7D"=>["\xFF","\xF4"],"~"=>["\xFF","\xE8"],"\x7F"=>["\xFF","\xFF","\xFF","\xC0"],"\x80"=>["\xFF","\xFE","\x60"],"\x81"=>["\xFF","\xFF","H"],"\x82"=>["\xFF","\xFE","p"],"\x83"=>["\xFF","\xFE","\x80"],"\x84"=>["\xFF","\xFF","L"],"\x85"=>["\xFF","\xFF","P"],"\x86"=>["\xFF","\xFF","T"],"\x87"=>["\xFF","\xFF","\xB2"],"\x88"=>["\xFF","\xFF","X"],"\x89"=>["\xFF","\xFF","\xB4"],"\x8A"=>["\xFF","\xFF","\xB6"],"\x8B"=>["\xFF","\xFF","\xB8"],"\x8C"=>["\xFF","\xFF","\xBA"],"\x8D"=>["\xFF","\xFF","\xBC"],"\x8E"=>["\xFF","\xFF","\xEB"],"\x8F"=>["\xFF","\xFF","\xBE"],"\x90"=>["\xFF","\xFF","\xEC"],"\x91"=>["\xFF","\xFF","\xED"],"\x92"=>["\xFF","\xFF","\x5C"],"\x93"=>["\xFF","\xFF","\xC0"],"\x94"=>["\xFF","\xFF","\xEE"],"\x95"=>["\xFF","\xFF","\xC2"],"\x96"=>["\xFF","\xFF","\xC4"],"\x97"=>["\xFF","\xFF","\xC6"],"\x98"=>["\xFF","\xFF","\xC8"],"\x99"=>["\xFF","\xFE","\xE0"],"\x9A"=>["\xFF","\xFF","\x60"],"\x9B"=>["\xFF","\xFF","\xCA"],"\x9C"=>["\xFF","\xFF","d"],"\x9D"=>["\xFF","\xFF","\xCC"],"\x9E"=>["\xFF","\xFF","\xCE"],"\x9F"=>["\xFF","\xFF","\xEF"],"\xA0"=>["\xFF","\xFF","h"],"\xA1"=>["\xFF","\xFE","\xE8"],"\xA2"=>["\xFF","\xFE","\x90"],"\xA3"=>["\xFF","\xFF","l"],"\xA4"=>["\xFF","\xFF","p"],"\xA5"=>["\xFF","\xFF","\xD0"],"\xA6"=>["\xFF","\xFF","\xD2"],"\xA7"=>["\xFF","\xFE","\xF0"],"\xA8"=>["\xFF","\xFF","\xD4"],"\xA9"=>["\xFF","\xFF","t"],"\xAA"=>["\xFF","\xFF","x"],"\xAB"=>["\xFF","\xFF","\xF0"],"\xAC"=>["\xFF","\xFE","\xF8"],"\xAD"=>["\xFF","\xFF","\x7C"],"\xAE"=>["\xFF","\xFF","\xD6"],"\xAF"=>["\xFF","\xFF","\xD8"],"\xB0"=>["\xFF","\xFF","\x00"],"\xB1"=>["\xFF","\xFF","\x08"],"\xB2"=>["\xFF","\xFF","\x80"],"\xB3"=>["\xFF","\xFF","\x10"],"\xB4"=>["\xFF","\xFF","\xDA"],"\xB5"=>["\xFF","\xFF","\x84"],"\xB6"=>["\xFF","\xFF","\xDC"],"\xB7"=>["\xFF","\xFF","\xDE"],"\xB8"=>["\xFF","\xFE","\xA0"],"\xB9"=>["\xFF","\xFF","\x88"],"\xBA"=>["\xFF","\xFF","\x8C"],"\xBB"=>["\xFF","\xFF","\x90"],"\xBC"=>["\xFF","\xFF","\xE0"],"\xBD"=>["\xFF","\xFF","\x94"],"\xBE"=>["\xFF","\xFF","\x98"],"\xBF"=>["\xFF","\xFF","\xE2"],"\xC0"=>["\xFF","\xFF","\xF8","\x00"],"\xC1"=>["\xFF","\xFF","\xF8","\x40"],"\xC2"=>["\xFF","\xFE","\xB0"],"\xC3"=>["\xFF","\xFE","\x20"],"\xC4"=>["\xFF","\xFF","\x9C"],"\xC5"=>["\xFF","\xFF","\xE4"],"\xC6"=>["\xFF","\xFF","\xA0"],"\xC7"=>["\xFF","\xFF","\xF6","\x00"],"\xC8"=>["\xFF","\xFF","\xF8","\x80"],"\xC9"=>["\xFF","\xFF","\xF8","\xC0"],"\xCA"=>["\xFF","\xFF","\xF9","\x00"],"\xCB"=>["\xFF","\xFF","\xFB","\xC0"],"\xCC"=>["\xFF","\xFF","\xFB","\xE0"],"\xCD"=>["\xFF","\xFF","\xF9","\x40"],"\xCE"=>["\xFF","\xFF","\xF1"],"\xCF"=>["\xFF","\xFF","\xF6","\x80"],"\xD0"=>["\xFF","\xFE","\x40"],"\xD1"=>["\xFF","\xFF","\x18"],"\xD2"=>["\xFF","\xFF","\xF9","\x80"],"\xD3"=>["\xFF","\xFF","\xFC","\x00"],"\xD4"=>["\xFF","\xFF","\xFC","\x20"],"\xD5"=>["\xFF","\xFF","\xF9","\xC0"],"\xD6"=>["\xFF","\xFF","\xFC","\x40"],"\xD7"=>["\xFF","\xFF","\xF2"],"\xD8"=>["\xFF","\xFF","\x20"],"\xD9"=>["\xFF","\xFF","\x28"],"\xDA"=>["\xFF","\xFF","\xFA","\x00"],"\xDB"=>["\xFF","\xFF","\xFA","\x40"],"\xDC"=>["\xFF","\xFF","\xFF","\xD0"],"\xDD"=>["\xFF","\xFF","\xFC","\x60"],"\xDE"=>["\xFF","\xFF","\xFC","\x80"],"\xDF"=>["\xFF","\xFF","\xFC","\xA0"],"\xE0"=>["\xFF","\xFE","\xC0"],"\xE1"=>["\xFF","\xFF","\xF3"],"\xE2"=>["\xFF","\xFE","\xD0"],"\xE3"=>["\xFF","\xFF","0"],"\xE4"=>["\xFF","\xFF","\xA4"],"\xE5"=>["\xFF","\xFF","8"],"\xE6"=>["\xFF","\xFF","\x40"],"\xE7"=>["\xFF","\xFF","\xE6"],"\xE8"=>["\xFF","\xFF","\xA8"],"\xE9"=>["\xFF","\xFF","\xAC"],"\xEA"=>["\xFF","\xFF","\xF7","\x00"],"\xEB"=>["\xFF","\xFF","\xF7","\x80"],"\xEC"=>["\xFF","\xFF","\xF4"],"\xED"=>["\xFF","\xFF","\xF5"],"\xEE"=>["\xFF","\xFF","\xFA","\x80"],"\xEF"=>["\xFF","\xFF","\xE8"],"\xF0"=>["\xFF","\xFF","\xFA","\xC0"],"\xF1"=>["\xFF","\xFF","\xFC","\xC0"],"\xF2"=>["\xFF","\xFF","\xFB","\x00"],"\xF3"=>["\xFF","\xFF","\xFB","\x40"],"\xF4"=>["\xFF","\xFF","\xFC","\xE0"],"\xF5"=>["\xFF","\xFF","\xFD","\x00"],"\xF6"=>["\xFF","\xFF","\xFD","\x20"],"\xF7"=>["\xFF","\xFF","\xFD","\x40"],"\xF8"=>["\xFF","\xFF","\xFD","\x60"],"\xF9"=>["\xFF","\xFF","\xFF","\xE0"],"\xFA"=>["\xFF","\xFF","\xFD","\x80"],"\xFB"=>["\xFF","\xFF","\xFD","\xA0"],"\xFC"=>["\xFF","\xFF","\xFD","\xC0"],"\xFD"=>["\xFF","\xFF","\xFD","\xE0"],"\xFE"=>["\xFF","\xFF","\xFE","\x00"],"\xFF"=>["\xFF","\xFF","\xFB","\x80"],],["\x00"=>["\x7F","\xE0"],"\x01"=>["\x7F","\xFF","\xD8"],"\x02"=>["\x7F","\xFF","\xFF","\x10"],"\x03"=>["\x7F","\xFF","\xFF","\x18"],"\x04"=>["\x7F","\xFF","\xFF","\x20"],"\x05"=>["\x7F","\xFF","\xFF","\x28"],"\x06"=>["\x7F","\xFF","\xFF","0"],"\x07"=>["\x7F","\xFF","\xFF","8"],"\x08"=>["\x7F","\xFF","\xFF","\x40"],"\x09"=>["\x7F","\xFF","\xF5","\x00"],"\x0A"=>["\x7F","\xFF","\xFF","\xF8"],"\x0B"=>["\x7F","\xFF","\xFF","H"],"\x0C"=>["\x7F","\xFF","\xFF","P"],"\x0D"=>["\x7F","\xFF","\xFF","\xFA"],"\x0E"=>["\x7F","\xFF","\xFF","X"],"\x0F"=>["\x7F","\xFF","\xFF","\x60"],"\x10"=>["\x7F","\xFF","\xFF","h"],"\x11"=>["\x7F","\xFF","\xFF","p"],"\x12"=>["\x7F","\xFF","\xFF","x"],"\x13"=>["\x7F","\xFF","\xFF","\x80"],"\x14"=>["\x7F","\xFF","\xFF","\x88"],"\x15"=>["\x7F","\xFF","\xFF","\x90"],"\x16"=>["\x7F","\xFF","\xFF","\xFC"],"\x17"=>["\x7F","\xFF","\xFF","\x98"],"\x18"=>["\x7F","\xFF","\xFF","\xA0"],"\x19"=>["\x7F","\xFF","\xFF","\xA8"],"\x1A"=>["\x7F","\xFF","\xFF","\xB0"],"\x1B"=>["\x7F","\xFF","\xFF","\xB8"],"\x1C"=>["\x7F","\xFF","\xFF","\xC0"],"\x1D"=>["\x7F","\xFF","\xFF","\xC8"],"\x1E"=>["\x7F","\xFF","\xFF","\xD0"],"\x1F"=>["\x7F","\xFF","\xFF","\xD8"],"\x20"=>["\x28"],"\x21"=>["\x7F","\x00"],"\x22"=>["\x7F","\x20"],"\x23"=>["\x7F","\xD0"],"\x24"=>["\x7F","\xE4"],"\x25"=>["\x2A"],"\x26"=>["\x7C","\x00"],"\x27"=>["\x7F","\xA0"],"\x28"=>["\x7F","\x40"],"\x29"=>["\x7F","\x60"],"\x2A"=>["\x7C","\x80"],"\x2B"=>["\x7F","\xB0"],"\x2C"=>["\x7D","\x00"],"-"=>["\x2C"],"."=>["."],"\x2F"=>["0"],["\x00"],["\x04"],["\x08"],["2"],["4"],["6"],["8"],["\x3A"],["\x3C"],["\x3E"],"\x3A"=>["\x5C"],"\x3B"=>["\x7D","\x80"],"\x3C"=>["\x7F","\xFC"],"\x3D"=>["\x40"],"\x3E"=>["\x7F","\xD8"],"\x3F"=>["\x7F","\x80"],"\x40"=>["\x7F","\xE8"],"A"=>["B"],"B"=>["\x5D"],"C"=>["\x5E"],"D"=>["_"],"E"=>["\x60"],"F"=>["a"],"G"=>["b"],"H"=>["c"],"I"=>["d"],"J"=>["e"],"K"=>["f"],"L"=>["g"],"M"=>["h"],"N"=>["i"],"O"=>["j"],"P"=>["k"],"Q"=>["l"],"R"=>["m"],"S"=>["n"],"T"=>["o"],"U"=>["p"],"V"=>["q"],"W"=>["r"],"X"=>["~","\x00"],"Y"=>["s"],"Z"=>["~","\x80"],"\x5B"=>["\x7F","\xEC"],"\x5C"=>["\x7F","\xFF","\x00"],"\x5D"=>["\x7F","\xF0"],"\x5E"=>["\x7F","\xF8"],"_"=>["D"],"\x60"=>["\x7F","\xFD"],"a"=>["\x0C"],"b"=>["F"],"c"=>["\x10"],"d"=>["H"],"e"=>["\x14"],"f"=>["J"],"g"=>["L"],"h"=>["N"],"i"=>["\x18"],"j"=>["t"],"k"=>["u"],"l"=>["P"],"m"=>["R"],"n"=>["T"],"o"=>["\x1C"],"p"=>["V"],"q"=>["v"],"r"=>["X"],"s"=>["\x20"],"t"=>["\x24"],"u"=>["Z"],"v"=>["w"],"w"=>["x"],"x"=>["y"],"y"=>["z"],"z"=>["\x7B"],"\x7B"=>["\x7F","\xFE"],"\x7C"=>["\x7F","\xC0"],"\x7D"=>["\x7F","\xFA"],"~"=>["\x7F","\xF4"],"\x7F"=>["\x7F","\xFF","\xFF","\xE0"],"\x80"=>["\x7F","\xFF","0"],"\x81"=>["\x7F","\xFF","\xA4"],"\x82"=>["\x7F","\xFF","8"],"\x83"=>["\x7F","\xFF","\x40"],"\x84"=>["\x7F","\xFF","\xA6"],"\x85"=>["\x7F","\xFF","\xA8"],"\x86"=>["\x7F","\xFF","\xAA"],"\x87"=>["\x7F","\xFF","\xD9"],"\x88"=>["\x7F","\xFF","\xAC"],"\x89"=>["\x7F","\xFF","\xDA"],"\x8A"=>["\x7F","\xFF","\xDB"],"\x8B"=>["\x7F","\xFF","\xDC"],"\x8C"=>["\x7F","\xFF","\xDD"],"\x8D"=>["\x7F","\xFF","\xDE"],"\x8E"=>["\x7F","\xFF","\xF5","\x80"],"\x8F"=>["\x7F","\xFF","\xDF"],"\x90"=>["\x7F","\xFF","\xF6","\x00"],"\x91"=>["\x7F","\xFF","\xF6","\x80"],"\x92"=>["\x7F","\xFF","\xAE"],"\x93"=>["\x7F","\xFF","\xE0"],"\x94"=>["\x7F","\xFF","\xF7","\x00"],"\x95"=>["\x7F","\xFF","\xE1"],"\x96"=>["\x7F","\xFF","\xE2"],"\x97"=>["\x7F","\xFF","\xE3"],"\x98"=>["\x7F","\xFF","\xE4"],"\x99"=>["\x7F","\xFF","p"],"\x9A"=>["\x7F","\xFF","\xB0"],"\x9B"=>["\x7F","\xFF","\xE5"],"\x9C"=>["\x7F","\xFF","\xB2"],"\x9D"=>["\x7F","\xFF","\xE6"],"\x9E"=>["\x7F","\xFF","\xE7"],"\x9F"=>["\x7F","\xFF","\xF7","\x80"],"\xA0"=>["\x7F","\xFF","\xB4"],"\xA1"=>["\x7F","\xFF","t"],"\xA2"=>["\x7F","\xFF","H"],"\xA3"=>["\x7F","\xFF","\xB6"],"\xA4"=>["\x7F","\xFF","\xB8"],"\xA5"=>["\x7F","\xFF","\xE8"],"\xA6"=>["\x7F","\xFF","\xE9"],"\xA7"=>["\x7F","\xFF","x"],"\xA8"=>["\x7F","\xFF","\xEA"],"\xA9"=>["\x7F","\xFF","\xBA"],"\xAA"=>["\x7F","\xFF","\xBC"],"\xAB"=>["\x7F","\xFF","\xF8","\x00"],"\xAC"=>["\x7F","\xFF","\x7C"],"\xAD"=>["\x7F","\xFF","\xBE"],"\xAE"=>["\x7F","\xFF","\xEB"],"\xAF"=>["\x7F","\xFF","\xEC"],"\xB0"=>["\x7F","\xFF","\x80"],"\xB1"=>["\x7F","\xFF","\x84"],"\xB2"=>["\x7F","\xFF","\xC0"],"\xB3"=>["\x7F","\xFF","\x88"],"\xB4"=>["\x7F","\xFF","\xED"],"\xB5"=>["\x7F","\xFF","\xC2"],"\xB6"=>["\x7F","\xFF","\xEE"],"\xB7"=>["\x7F","\xFF","\xEF"],"\xB8"=>["\x7F","\xFF","P"],"\xB9"=>["\x7F","\xFF","\xC4"],"\xBA"=>["\x7F","\xFF","\xC6"],"\xBB"=>["\x7F","\xFF","\xC8"],"\xBC"=>["\x7F","\xFF","\xF0"],"\xBD"=>["\x7F","\xFF","\xCA"],"\xBE"=>["\x7F","\xFF","\xCC"],"\xBF"=>["\x7F","\xFF","\xF1"],"\xC0"=>["\x7F","\xFF","\xFC","\x00"],"\xC1"=>["\x7F","\xFF","\xFC","\x20"],"\xC2"=>["\x7F","\xFF","X"],"\xC3"=>["\x7F","\xFF","\x10"],"\xC4"=>["\x7F","\xFF","\xCE"],"\xC5"=>["\x7F","\xFF","\xF2"],"\xC6"=>["\x7F","\xFF","\xD0"],"\xC7"=>["\x7F","\xFF","\xFB","\x00"],"\xC8"=>["\x7F","\xFF","\xFC","\x40"],"\xC9"=>["\x7F","\xFF","\xFC","\x60"],"\xCA"=>["\x7F","\xFF","\xFC","\x80"],"\xCB"=>["\x7F","\xFF","\xFD","\xE0"],"\xCC"=>["\x7F","\xFF","\xFD","\xF0"],"\xCD"=>["\x7F","\xFF","\xFC","\xA0"],"\xCE"=>["\x7F","\xFF","\xF8","\x80"],"\xCF"=>["\x7F","\xFF","\xFB","\x40"],"\xD0"=>["\x7F","\xFF","\x20"],"\xD1"=>["\x7F","\xFF","\x8C"],"\xD2"=>["\x7F","\xFF","\xFC","\xC0"],"\xD3"=>["\x7F","\xFF","\xFE","\x00"],"\xD4"=>["\x7F","\xFF","\xFE","\x10"],"\xD5"=>["\x7F","\xFF","\xFC","\xE0"],"\xD6"=>["\x7F","\xFF","\xFE","\x20"],"\xD7"=>["\x7F","\xFF","\xF9","\x00"],"\xD8"=>["\x7F","\xFF","\x90"],"\xD9"=>["\x7F","\xFF","\x94"],"\xDA"=>["\x7F","\xFF","\xFD","\x00"],"\xDB"=>["\x7F","\xFF","\xFD","\x20"],"\xDC"=>["\x7F","\xFF","\xFF","\xE8"],"\xDD"=>["\x7F","\xFF","\xFE","0"],"\xDE"=>["\x7F","\xFF","\xFE","\x40"],"\xDF"=>["\x7F","\xFF","\xFE","P"],"\xE0"=>["\x7F","\xFF","\x60"],"\xE1"=>["\x7F","\xFF","\xF9","\x80"],"\xE2"=>["\x7F","\xFF","h"],"\xE3"=>["\x7F","\xFF","\x98"],"\xE4"=>["\x7F","\xFF","\xD2"],"\xE5"=>["\x7F","\xFF","\x9C"],"\xE6"=>["\x7F","\xFF","\xA0"],"\xE7"=>["\x7F","\xFF","\xF3"],"\xE8"=>["\x7F","\xFF","\xD4"],"\xE9"=>["\x7F","\xFF","\xD6"],"\xEA"=>["\x7F","\xFF","\xFB","\x80"],"\xEB"=>["\x7F","\xFF","\xFB","\xC0"],"\xEC"=>["\x7F","\xFF","\xFA","\x00"],"\xED"=>["\x7F","\xFF","\xFA","\x80"],"\xEE"=>["\x7F","\xFF","\xFD","\x40"],"\xEF"=>["\x7F","\xFF","\xF4"],"\xF0"=>["\x7F","\xFF","\xFD","\x60"],"\xF1"=>["\x7F","\xFF","\xFE","\x60"],"\xF2"=>["\x7F","\xFF","\xFD","\x80"],"\xF3"=>["\x7F","\xFF","\xFD","\xA0"],"\xF4"=>["\x7F","\xFF","\xFE","p"],"\xF5"=>["\x7F","\xFF","\xFE","\x80"],"\xF6"=>["\x7F","\xFF","\xFE","\x90"],"\xF7"=>["\x7F","\xFF","\xFE","\xA0"],"\xF8"=>["\x7F","\xFF","\xFE","\xB0"],"\xF9"=>["\x7F","\xFF","\xFF","\xF0"],"\xFA"=>["\x7F","\xFF","\xFE","\xC0"],"\xFB"=>["\x7F","\xFF","\xFE","\xD0"],"\xFC"=>["\x7F","\xFF","\xFE","\xE0"],"\xFD"=>["\x7F","\xFF","\xFE","\xF0"],"\xFE"=>["\x7F","\xFF","\xFF","\x00"],"\xFF"=>["\x7F","\xFF","\xFD","\xC0"],],["\x00"=>["\x3F","\xF0"],"\x01"=>["\x3F","\xFF","\xEC","\x00"],"\x02"=>["\x3F","\xFF","\xFF","\x88"],"\x03"=>["\x3F","\xFF","\xFF","\x8C"],"\x04"=>["\x3F","\xFF","\xFF","\x90"],"\x05"=>["\x3F","\xFF","\xFF","\x94"],"\x06"=>["\x3F","\xFF","\xFF","\x98"],"\x07"=>["\x3F","\xFF","\xFF","\x9C"],"\x08"=>["\x3F","\xFF","\xFF","\xA0"],"\x09"=>["\x3F","\xFF","\xFA","\x80"],"\x0A"=>["\x3F","\xFF","\xFF","\xFC"],"\x0B"=>["\x3F","\xFF","\xFF","\xA4"],"\x0C"=>["\x3F","\xFF","\xFF","\xA8"],"\x0D"=>["\x3F","\xFF","\xFF","\xFD"],"\x0E"=>["\x3F","\xFF","\xFF","\xAC"],"\x0F"=>["\x3F","\xFF","\xFF","\xB0"],"\x10"=>["\x3F","\xFF","\xFF","\xB4"],"\x11"=>["\x3F","\xFF","\xFF","\xB8"],"\x12"=>["\x3F","\xFF","\xFF","\xBC"],"\x13"=>["\x3F","\xFF","\xFF","\xC0"],"\x14"=>["\x3F","\xFF","\xFF","\xC4"],"\x15"=>["\x3F","\xFF","\xFF","\xC8"],"\x16"=>["\x3F","\xFF","\xFF","\xFE"],"\x17"=>["\x3F","\xFF","\xFF","\xCC"],"\x18"=>["\x3F","\xFF","\xFF","\xD0"],"\x19"=>["\x3F","\xFF","\xFF","\xD4"],"\x1A"=>["\x3F","\xFF","\xFF","\xD8"],"\x1B"=>["\x3F","\xFF","\xFF","\xDC"],"\x1C"=>["\x3F","\xFF","\xFF","\xE0"],"\x1D"=>["\x3F","\xFF","\xFF","\xE4"],"\x1E"=>["\x3F","\xFF","\xFF","\xE8"],"\x1F"=>["\x3F","\xFF","\xFF","\xEC"],"\x20"=>["\x14"],"\x21"=>["\x3F","\x80"],"\x22"=>["\x3F","\x90"],"\x23"=>["\x3F","\xE8"],"\x24"=>["\x3F","\xF2"],"\x25"=>["\x15"],"\x26"=>["\x3E","\x00"],"\x27"=>["\x3F","\xD0"],"\x28"=>["\x3F","\xA0"],"\x29"=>["\x3F","\xB0"],"\x2A"=>["\x3E","\x40"],"\x2B"=>["\x3F","\xD8"],"\x2C"=>["\x3E","\x80"],"-"=>["\x16"],"."=>["\x17"],"\x2F"=>["\x18"],["\x00"],["\x02"],["\x04"],["\x19"],["\x1A"],["\x1B"],["\x1C"],["\x1D"],["\x1E"],["\x1F"],"\x3A"=>[".","\x00"],"\x3B"=>["\x3E","\xC0"],"\x3C"=>["\x3F","\xFE","\x00"],"\x3D"=>["\x20"],"\x3E"=>["\x3F","\xEC"],"\x3F"=>["\x3F","\xC0"],"\x40"=>["\x3F","\xF4"],"A"=>["\x21"],"B"=>[".","\x80"],"C"=>["\x2F","\x00"],"D"=>["\x2F","\x80"],"E"=>["0","\x00"],"F"=>["0","\x80"],"G"=>["1","\x00"],"H"=>["1","\x80"],"I"=>["2","\x00"],"J"=>["2","\x80"],"K"=>["3","\x00"],"L"=>["3","\x80"],"M"=>["4","\x00"],"N"=>["4","\x80"],"O"=>["5","\x00"],"P"=>["5","\x80"],"Q"=>["6","\x00"],"R"=>["6","\x80"],"S"=>["7","\x00"],"T"=>["7","\x80"],"U"=>["8","\x00"],"V"=>["8","\x80"],"W"=>["9","\x00"],"X"=>["\x3F","\x00"],"Y"=>["9","\x80"],"Z"=>["\x3F","\x40"],"\x5B"=>["\x3F","\xF6"],"\x5C"=>["\x3F","\xFF","\x80"],"\x5D"=>["\x3F","\xF8"],"\x5E"=>["\x3F","\xFC"],"_"=>["\x22"],"\x60"=>["\x3F","\xFE","\x80"],"a"=>["\x06"],"b"=>["\x23"],"c"=>["\x08"],"d"=>["\x24"],"e"=>["\x0A"],"f"=>["\x25"],"g"=>["\x26"],"h"=>["\x27"],"i"=>["\x0C"],"j"=>["\x3A","\x00"],"k"=>["\x3A","\x80"],"l"=>["\x28"],"m"=>["\x29"],"n"=>["\x2A"],"o"=>["\x0E"],"p"=>["\x2B"],"q"=>["\x3B","\x00"],"r"=>["\x2C"],"s"=>["\x10"],"t"=>["\x12"],"u"=>["-"],"v"=>["\x3B","\x80"],"w"=>["\x3C","\x00"],"x"=>["\x3C","\x80"],"y"=>["\x3D","\x00"],"z"=>["\x3D","\x80"],"\x7B"=>["\x3F","\xFF","\x00"],"\x7C"=>["\x3F","\xE0"],"\x7D"=>["\x3F","\xFD"],"~"=>["\x3F","\xFA"],"\x7F"=>["\x3F","\xFF","\xFF","\xF0"],"\x80"=>["\x3F","\xFF","\x98"],"\x81"=>["\x3F","\xFF","\xD2"],"\x82"=>["\x3F","\xFF","\x9C"],"\x83"=>["\x3F","\xFF","\xA0"],"\x84"=>["\x3F","\xFF","\xD3"],"\x85"=>["\x3F","\xFF","\xD4"],"\x86"=>["\x3F","\xFF","\xD5"],"\x87"=>["\x3F","\xFF","\xEC","\x80"],"\x88"=>["\x3F","\xFF","\xD6"],"\x89"=>["\x3F","\xFF","\xED","\x00"],"\x8A"=>["\x3F","\xFF","\xED","\x80"],"\x8B"=>["\x3F","\xFF","\xEE","\x00"],"\x8C"=>["\x3F","\xFF","\xEE","\x80"],"\x8D"=>["\x3F","\xFF","\xEF","\x00"],"\x8E"=>["\x3F","\xFF","\xFA","\xC0"],"\x8F"=>["\x3F","\xFF","\xEF","\x80"],"\x90"=>["\x3F","\xFF","\xFB","\x00"],"\x91"=>["\x3F","\xFF","\xFB","\x40"],"\x92"=>["\x3F","\xFF","\xD7"],"\x93"=>["\x3F","\xFF","\xF0","\x00"],"\x94"=>["\x3F","\xFF","\xFB","\x80"],"\x95"=>["\x3F","\xFF","\xF0","\x80"],"\x96"=>["\x3F","\xFF","\xF1","\x00"],"\x97"=>["\x3F","\xFF","\xF1","\x80"],"\x98"=>["\x3F","\xFF","\xF2","\x00"],"\x99"=>["\x3F","\xFF","\xB8"],"\x9A"=>["\x3F","\xFF","\xD8"],"\x9B"=>["\x3F","\xFF","\xF2","\x80"],"\x9C"=>["\x3F","\xFF","\xD9"],"\x9D"=>["\x3F","\xFF","\xF3","\x00"],"\x9E"=>["\x3F","\xFF","\xF3","\x80"],"\x9F"=>["\x3F","\xFF","\xFB","\xC0"],"\xA0"=>["\x3F","\xFF","\xDA"],"\xA1"=>["\x3F","\xFF","\xBA"],"\xA2"=>["\x3F","\xFF","\xA4"],"\xA3"=>["\x3F","\xFF","\xDB"],"\xA4"=>["\x3F","\xFF","\xDC"],"\xA5"=>["\x3F","\xFF","\xF4","\x00"],"\xA6"=>["\x3F","\xFF","\xF4","\x80"],"\xA7"=>["\x3F","\xFF","\xBC"],"\xA8"=>["\x3F","\xFF","\xF5","\x00"],"\xA9"=>["\x3F","\xFF","\xDD"],"\xAA"=>["\x3F","\xFF","\xDE"],"\xAB"=>["\x3F","\xFF","\xFC","\x00"],"\xAC"=>["\x3F","\xFF","\xBE"],"\xAD"=>["\x3F","\xFF","\xDF"],"\xAE"=>["\x3F","\xFF","\xF5","\x80"],"\xAF"=>["\x3F","\xFF","\xF6","\x00"],"\xB0"=>["\x3F","\xFF","\xC0"],"\xB1"=>["\x3F","\xFF","\xC2"],"\xB2"=>["\x3F","\xFF","\xE0"],"\xB3"=>["\x3F","\xFF","\xC4"],"\xB4"=>["\x3F","\xFF","\xF6","\x80"],"\xB5"=>["\x3F","\xFF","\xE1"],"\xB6"=>["\x3F","\xFF","\xF7","\x00"],"\xB7"=>["\x3F","\xFF","\xF7","\x80"],"\xB8"=>["\x3F","\xFF","\xA8"],"\xB9"=>["\x3F","\xFF","\xE2"],"\xBA"=>["\x3F","\xFF","\xE3"],"\xBB"=>["\x3F","\xFF","\xE4"],"\xBC"=>["\x3F","\xFF","\xF8","\x00"],"\xBD"=>["\x3F","\xFF","\xE5"],"\xBE"=>["\x3F","\xFF","\xE6"],"\xBF"=>["\x3F","\xFF","\xF8","\x80"],"\xC0"=>["\x3F","\xFF","\xFE","\x00"],"\xC1"=>["\x3F","\xFF","\xFE","\x10"],"\xC2"=>["\x3F","\xFF","\xAC"],"\xC3"=>["\x3F","\xFF","\x88"],"\xC4"=>["\x3F","\xFF","\xE7"],"\xC5"=>["\x3F","\xFF","\xF9","\x00"],"\xC6"=>["\x3F","\xFF","\xE8"],"\xC7"=>["\x3F","\xFF","\xFD","\x80"],"\xC8"=>["\x3F","\xFF","\xFE","\x20"],"\xC9"=>["\x3F","\xFF","\xFE","0"],"\xCA"=>["\x3F","\xFF","\xFE","\x40"],"\xCB"=>["\x3F","\xFF","\xFE","\xF0"],"\xCC"=>["\x3F","\xFF","\xFE","\xF8"],"\xCD"=>["\x3F","\xFF","\xFE","P"],"\xCE"=>["\x3F","\xFF","\xFC","\x40"],"\xCF"=>["\x3F","\xFF","\xFD","\xA0"],"\xD0"=>["\x3F","\xFF","\x90"],"\xD1"=>["\x3F","\xFF","\xC6"],"\xD2"=>["\x3F","\xFF","\xFE","\x60"],"\xD3"=>["\x3F","\xFF","\xFF","\x00"],"\xD4"=>["\x3F","\xFF","\xFF","\x08"],"\xD5"=>["\x3F","\xFF","\xFE","p"],"\xD6"=>["\x3F","\xFF","\xFF","\x10"],"\xD7"=>["\x3F","\xFF","\xFC","\x80"],"\xD8"=>["\x3F","\xFF","\xC8"],"\xD9"=>["\x3F","\xFF","\xCA"],"\xDA"=>["\x3F","\xFF","\xFE","\x80"],"\xDB"=>["\x3F","\xFF","\xFE","\x90"],"\xDC"=>["\x3F","\xFF","\xFF","\xF4"],"\xDD"=>["\x3F","\xFF","\xFF","\x18"],"\xDE"=>["\x3F","\xFF","\xFF","\x20"],"\xDF"=>["\x3F","\xFF","\xFF","\x28"],"\xE0"=>["\x3F","\xFF","\xB0"],"\xE1"=>["\x3F","\xFF","\xFC","\xC0"],"\xE2"=>["\x3F","\xFF","\xB4"],"\xE3"=>["\x3F","\xFF","\xCC"],"\xE4"=>["\x3F","\xFF","\xE9"],"\xE5"=>["\x3F","\xFF","\xCE"],"\xE6"=>["\x3F","\xFF","\xD0"],"\xE7"=>["\x3F","\xFF","\xF9","\x80"],"\xE8"=>["\x3F","\xFF","\xEA"],"\xE9"=>["\x3F","\xFF","\xEB"],"\xEA"=>["\x3F","\xFF","\xFD","\xC0"],"\xEB"=>["\x3F","\xFF","\xFD","\xE0"],"\xEC"=>["\x3F","\xFF","\xFD","\x00"],"\xED"=>["\x3F","\xFF","\xFD","\x40"],"\xEE"=>["\x3F","\xFF","\xFE","\xA0"],"\xEF"=>["\x3F","\xFF","\xFA","\x00"],"\xF0"=>["\x3F","\xFF","\xFE","\xB0"],"\xF1"=>["\x3F","\xFF","\xFF","0"],"\xF2"=>["\x3F","\xFF","\xFE","\xC0"],"\xF3"=>["\x3F","\xFF","\xFE","\xD0"],"\xF4"=>["\x3F","\xFF","\xFF","8"],"\xF5"=>["\x3F","\xFF","\xFF","\x40"],"\xF6"=>["\x3F","\xFF","\xFF","H"],"\xF7"=>["\x3F","\xFF","\xFF","P"],"\xF8"=>["\x3F","\xFF","\xFF","X"],"\xF9"=>["\x3F","\xFF","\xFF","\xF8"],"\xFA"=>["\x3F","\xFF","\xFF","\x60"],"\xFB"=>["\x3F","\xFF","\xFF","h"],"\xFC"=>["\x3F","\xFF","\xFF","p"],"\xFD"=>["\x3F","\xFF","\xFF","x"],"\xFE"=>["\x3F","\xFF","\xFF","\x80"],"\xFF"=>["\x3F","\xFF","\xFE","\xE0"],],["\x00"=>["\x1F","\xF8"],"\x01"=>["\x1F","\xFF","\xF6","\x00"],"\x02"=>["\x1F","\xFF","\xFF","\xC4"],"\x03"=>["\x1F","\xFF","\xFF","\xC6"],"\x04"=>["\x1F","\xFF","\xFF","\xC8"],"\x05"=>["\x1F","\xFF","\xFF","\xCA"],"\x06"=>["\x1F","\xFF","\xFF","\xCC"],"\x07"=>["\x1F","\xFF","\xFF","\xCE"],"\x08"=>["\x1F","\xFF","\xFF","\xD0"],"\x09"=>["\x1F","\xFF","\xFD","\x40"],"\x0A"=>["\x1F","\xFF","\xFF","\xFE","\x00"],"\x0B"=>["\x1F","\xFF","\xFF","\xD2"],"\x0C"=>["\x1F","\xFF","\xFF","\xD4"],"\x0D"=>["\x1F","\xFF","\xFF","\xFE","\x80"],"\x0E"=>["\x1F","\xFF","\xFF","\xD6"],"\x0F"=>["\x1F","\xFF","\xFF","\xD8"],"\x10"=>["\x1F","\xFF","\xFF","\xDA"],"\x11"=>["\x1F","\xFF","\xFF","\xDC"],"\x12"=>["\x1F","\xFF","\xFF","\xDE"],"\x13"=>["\x1F","\xFF","\xFF","\xE0"],"\x14"=>["\x1F","\xFF","\xFF","\xE2"],"\x15"=>["\x1F","\xFF","\xFF","\xE4"],"\x16"=>["\x1F","\xFF","\xFF","\xFF","\x00"],"\x17"=>["\x1F","\xFF","\xFF","\xE6"],"\x18"=>["\x1F","\xFF","\xFF","\xE8"],"\x19"=>["\x1F","\xFF","\xFF","\xEA"],"\x1A"=>["\x1F","\xFF","\xFF","\xEC"],"\x1B"=>["\x1F","\xFF","\xFF","\xEE"],"\x1C"=>["\x1F","\xFF","\xFF","\xF0"],"\x1D"=>["\x1F","\xFF","\xFF","\xF2"],"\x1E"=>["\x1F","\xFF","\xFF","\xF4"],"\x1F"=>["\x1F","\xFF","\xFF","\xF6"],"\x20"=>["\x0A","\x00"],"\x21"=>["\x1F","\xC0"],"\x22"=>["\x1F","\xC8"],"\x23"=>["\x1F","\xF4"],"\x24"=>["\x1F","\xF9"],"\x25"=>["\x0A","\x80"],"\x26"=>["\x1F","\x00"],"\x27"=>["\x1F","\xE8"],"\x28"=>["\x1F","\xD0"],"\x29"=>["\x1F","\xD8"],"\x2A"=>["\x1F","\x20"],"\x2B"=>["\x1F","\xEC"],"\x2C"=>["\x1F","\x40"],"-"=>["\x0B","\x00"],"."=>["\x0B","\x80"],"\x2F"=>["\x0C","\x00"],["\x00"],["\x01"],["\x02"],["\x0C","\x80"],["\x0D","\x00"],["\x0D","\x80"],["\x0E","\x00"],["\x0E","\x80"],["\x0F","\x00"],["\x0F","\x80"],"\x3A"=>["\x17","\x00"],"\x3B"=>["\x1F","\x60"],"\x3C"=>["\x1F","\xFF","\x00"],"\x3D"=>["\x10","\x00"],"\x3E"=>["\x1F","\xF6"],"\x3F"=>["\x1F","\xE0"],"\x40"=>["\x1F","\xFA"],"A"=>["\x10","\x80"],"B"=>["\x17","\x40"],"C"=>["\x17","\x80"],"D"=>["\x17","\xC0"],"E"=>["\x18","\x00"],"F"=>["\x18","\x40"],"G"=>["\x18","\x80"],"H"=>["\x18","\xC0"],"I"=>["\x19","\x00"],"J"=>["\x19","\x40"],"K"=>["\x19","\x80"],"L"=>["\x19","\xC0"],"M"=>["\x1A","\x00"],"N"=>["\x1A","\x40"],"O"=>["\x1A","\x80"],"P"=>["\x1A","\xC0"],"Q"=>["\x1B","\x00"],"R"=>["\x1B","\x40"],"S"=>["\x1B","\x80"],"T"=>["\x1B","\xC0"],"U"=>["\x1C","\x00"],"V"=>["\x1C","\x40"],"W"=>["\x1C","\x80"],"X"=>["\x1F","\x80"],"Y"=>["\x1C","\xC0"],"Z"=>["\x1F","\xA0"],"\x5B"=>["\x1F","\xFB"],"\x5C"=>["\x1F","\xFF","\xC0"],"\x5D"=>["\x1F","\xFC"],"\x5E"=>["\x1F","\xFE","\x00"],"_"=>["\x11","\x00"],"\x60"=>["\x1F","\xFF","\x40"],"a"=>["\x03"],"b"=>["\x11","\x80"],"c"=>["\x04"],"d"=>["\x12","\x00"],"e"=>["\x05"],"f"=>["\x12","\x80"],"g"=>["\x13","\x00"],"h"=>["\x13","\x80"],"i"=>["\x06"],"j"=>["\x1D","\x00"],"k"=>["\x1D","\x40"],"l"=>["\x14","\x00"],"m"=>["\x14","\x80"],"n"=>["\x15","\x00"],"o"=>["\x07"],"p"=>["\x15","\x80"],"q"=>["\x1D","\x80"],"r"=>["\x16","\x00"],"s"=>["\x08"],"t"=>["\x09"],"u"=>["\x16","\x80"],"v"=>["\x1D","\xC0"],"w"=>["\x1E","\x00"],"x"=>["\x1E","\x40"],"y"=>["\x1E","\x80"],"z"=>["\x1E","\xC0"],"\x7B"=>["\x1F","\xFF","\x80"],"\x7C"=>["\x1F","\xF0"],"\x7D"=>["\x1F","\xFE","\x80"],"~"=>["\x1F","\xFD"],"\x7F"=>["\x1F","\xFF","\xFF","\xF8"],"\x80"=>["\x1F","\xFF","\xCC"],"\x81"=>["\x1F","\xFF","\xE9","\x00"],"\x82"=>["\x1F","\xFF","\xCE"],"\x83"=>["\x1F","\xFF","\xD0"],"\x84"=>["\x1F","\xFF","\xE9","\x80"],"\x85"=>["\x1F","\xFF","\xEA","\x00"],"\x86"=>["\x1F","\xFF","\xEA","\x80"],"\x87"=>["\x1F","\xFF","\xF6","\x40"],"\x88"=>["\x1F","\xFF","\xEB","\x00"],"\x89"=>["\x1F","\xFF","\xF6","\x80"],"\x8A"=>["\x1F","\xFF","\xF6","\xC0"],"\x8B"=>["\x1F","\xFF","\xF7","\x00"],"\x8C"=>["\x1F","\xFF","\xF7","\x40"],"\x8D"=>["\x1F","\xFF","\xF7","\x80"],"\x8E"=>["\x1F","\xFF","\xFD","\x60"],"\x8F"=>["\x1F","\xFF","\xF7","\xC0"],"\x90"=>["\x1F","\xFF","\xFD","\x80"],"\x91"=>["\x1F","\xFF","\xFD","\xA0"],"\x92"=>["\x1F","\xFF","\xEB","\x80"],"\x93"=>["\x1F","\xFF","\xF8","\x00"],"\x94"=>["\x1F","\xFF","\xFD","\xC0"],"\x95"=>["\x1F","\xFF","\xF8","\x40"],"\x96"=>["\x1F","\xFF","\xF8","\x80"],"\x97"=>["\x1F","\xFF","\xF8","\xC0"],"\x98"=>["\x1F","\xFF","\xF9","\x00"],"\x99"=>["\x1F","\xFF","\xDC"],"\x9A"=>["\x1F","\xFF","\xEC","\x00"],"\x9B"=>["\x1F","\xFF","\xF9","\x40"],"\x9C"=>["\x1F","\xFF","\xEC","\x80"],"\x9D"=>["\x1F","\xFF","\xF9","\x80"],"\x9E"=>["\x1F","\xFF","\xF9","\xC0"],"\x9F"=>["\x1F","\xFF","\xFD","\xE0"],"\xA0"=>["\x1F","\xFF","\xED","\x00"],"\xA1"=>["\x1F","\xFF","\xDD"],"\xA2"=>["\x1F","\xFF","\xD2"],"\xA3"=>["\x1F","\xFF","\xED","\x80"],"\xA4"=>["\x1F","\xFF","\xEE","\x00"],"\xA5"=>["\x1F","\xFF","\xFA","\x00"],"\xA6"=>["\x1F","\xFF","\xFA","\x40"],"\xA7"=>["\x1F","\xFF","\xDE"],"\xA8"=>["\x1F","\xFF","\xFA","\x80"],"\xA9"=>["\x1F","\xFF","\xEE","\x80"],"\xAA"=>["\x1F","\xFF","\xEF","\x00"],"\xAB"=>["\x1F","\xFF","\xFE","\x00"],"\xAC"=>["\x1F","\xFF","\xDF"],"\xAD"=>["\x1F","\xFF","\xEF","\x80"],"\xAE"=>["\x1F","\xFF","\xFA","\xC0"],"\xAF"=>["\x1F","\xFF","\xFB","\x00"],"\xB0"=>["\x1F","\xFF","\xE0"],"\xB1"=>["\x1F","\xFF","\xE1"],"\xB2"=>["\x1F","\xFF","\xF0","\x00"],"\xB3"=>["\x1F","\xFF","\xE2"],"\xB4"=>["\x1F","\xFF","\xFB","\x40"],"\xB5"=>["\x1F","\xFF","\xF0","\x80"],"\xB6"=>["\x1F","\xFF","\xFB","\x80"],"\xB7"=>["\x1F","\xFF","\xFB","\xC0"],"\xB8"=>["\x1F","\xFF","\xD4"],"\xB9"=>["\x1F","\xFF","\xF1","\x00"],"\xBA"=>["\x1F","\xFF","\xF1","\x80"],"\xBB"=>["\x1F","\xFF","\xF2","\x00"],"\xBC"=>["\x1F","\xFF","\xFC","\x00"],"\xBD"=>["\x1F","\xFF","\xF2","\x80"],"\xBE"=>["\x1F","\xFF","\xF3","\x00"],"\xBF"=>["\x1F","\xFF","\xFC","\x40"],"\xC0"=>["\x1F","\xFF","\xFF","\x00"],"\xC1"=>["\x1F","\xFF","\xFF","\x08"],"\xC2"=>["\x1F","\xFF","\xD6"],"\xC3"=>["\x1F","\xFF","\xC4"],"\xC4"=>["\x1F","\xFF","\xF3","\x80"],"\xC5"=>["\x1F","\xFF","\xFC","\x80"],"\xC6"=>["\x1F","\xFF","\xF4","\x00"],"\xC7"=>["\x1F","\xFF","\xFE","\xC0"],"\xC8"=>["\x1F","\xFF","\xFF","\x10"],"\xC9"=>["\x1F","\xFF","\xFF","\x18"],"\xCA"=>["\x1F","\xFF","\xFF","\x20"],"\xCB"=>["\x1F","\xFF","\xFF","x"],"\xCC"=>["\x1F","\xFF","\xFF","\x7C"],"\xCD"=>["\x1F","\xFF","\xFF","\x28"],"\xCE"=>["\x1F","\xFF","\xFE","\x20"],"\xCF"=>["\x1F","\xFF","\xFE","\xD0"],"\xD0"=>["\x1F","\xFF","\xC8"],"\xD1"=>["\x1F","\xFF","\xE3"],"\xD2"=>["\x1F","\xFF","\xFF","0"],"\xD3"=>["\x1F","\xFF","\xFF","\x80"],"\xD4"=>["\x1F","\xFF","\xFF","\x84"],"\xD5"=>["\x1F","\xFF","\xFF","8"],"\xD6"=>["\x1F","\xFF","\xFF","\x88"],"\xD7"=>["\x1F","\xFF","\xFE","\x40"],"\xD8"=>["\x1F","\xFF","\xE4"],"\xD9"=>["\x1F","\xFF","\xE5"],"\xDA"=>["\x1F","\xFF","\xFF","\x40"],"\xDB"=>["\x1F","\xFF","\xFF","H"],"\xDC"=>["\x1F","\xFF","\xFF","\xFA"],"\xDD"=>["\x1F","\xFF","\xFF","\x8C"],"\xDE"=>["\x1F","\xFF","\xFF","\x90"],"\xDF"=>["\x1F","\xFF","\xFF","\x94"],"\xE0"=>["\x1F","\xFF","\xD8"],"\xE1"=>["\x1F","\xFF","\xFE","\x60"],"\xE2"=>["\x1F","\xFF","\xDA"],"\xE3"=>["\x1F","\xFF","\xE6"],"\xE4"=>["\x1F","\xFF","\xF4","\x80"],"\xE5"=>["\x1F","\xFF","\xE7"],"\xE6"=>["\x1F","\xFF","\xE8"],"\xE7"=>["\x1F","\xFF","\xFC","\xC0"],"\xE8"=>["\x1F","\xFF","\xF5","\x00"],"\xE9"=>["\x1F","\xFF","\xF5","\x80"],"\xEA"=>["\x1F","\xFF","\xFE","\xE0"],"\xEB"=>["\x1F","\xFF","\xFE","\xF0"],"\xEC"=>["\x1F","\xFF","\xFE","\x80"],"\xED"=>["\x1F","\xFF","\xFE","\xA0"],"\xEE"=>["\x1F","\xFF","\xFF","P"],"\xEF"=>["\x1F","\xFF","\xFD","\x00"],"\xF0"=>["\x1F","\xFF","\xFF","X"],"\xF1"=>["\x1F","\xFF","\xFF","\x98"],"\xF2"=>["\x1F","\xFF","\xFF","\x60"],"\xF3"=>["\x1F","\xFF","\xFF","h"],"\xF4"=>["\x1F","\xFF","\xFF","\x9C"],"\xF5"=>["\x1F","\xFF","\xFF","\xA0"],"\xF6"=>["\x1F","\xFF","\xFF","\xA4"],"\xF7"=>["\x1F","\xFF","\xFF","\xA8"],"\xF8"=>["\x1F","\xFF","\xFF","\xAC"],"\xF9"=>["\x1F","\xFF","\xFF","\xFC"],"\xFA"=>["\x1F","\xFF","\xFF","\xB0"],"\xFB"=>["\x1F","\xFF","\xFF","\xB4"],"\xFC"=>["\x1F","\xFF","\xFF","\xB8"],"\xFD"=>["\x1F","\xFF","\xFF","\xBC"],"\xFE"=>["\x1F","\xFF","\xFF","\xC0"],"\xFF"=>["\x1F","\xFF","\xFF","p"],],["\x00"=>["\x0F","\xFC","\x00"],"\x01"=>["\x0F","\xFF","\xFB","\x00"],"\x02"=>["\x0F","\xFF","\xFF","\xE2"],"\x03"=>["\x0F","\xFF","\xFF","\xE3"],"\x04"=>["\x0F","\xFF","\xFF","\xE4"],"\x05"=>["\x0F","\xFF","\xFF","\xE5"],"\x06"=>["\x0F","\xFF","\xFF","\xE6"],"\x07"=>["\x0F","\xFF","\xFF","\xE7"],"\x08"=>["\x0F","\xFF","\xFF","\xE8"],"\x09"=>["\x0F","\xFF","\xFE","\xA0"],"\x0A"=>["\x0F","\xFF","\xFF","\xFF","\x00"],"\x0B"=>["\x0F","\xFF","\xFF","\xE9"],"\x0C"=>["\x0F","\xFF","\xFF","\xEA"],"\x0D"=>["\x0F","\xFF","\xFF","\xFF","\x40"],"\x0E"=>["\x0F","\xFF","\xFF","\xEB"],"\x0F"=>["\x0F","\xFF","\xFF","\xEC"],"\x10"=>["\x0F","\xFF","\xFF","\xED"],"\x11"=>["\x0F","\xFF","\xFF","\xEE"],"\x12"=>["\x0F","\xFF","\xFF","\xEF"],"\x13"=>["\x0F","\xFF","\xFF","\xF0"],"\x14"=>["\x0F","\xFF","\xFF","\xF1"],"\x15"=>["\x0F","\xFF","\xFF","\xF2"],"\x16"=>["\x0F","\xFF","\xFF","\xFF","\x80"],"\x17"=>["\x0F","\xFF","\xFF","\xF3"],"\x18"=>["\x0F","\xFF","\xFF","\xF4"],"\x19"=>["\x0F","\xFF","\xFF","\xF5"],"\x1A"=>["\x0F","\xFF","\xFF","\xF6"],"\x1B"=>["\x0F","\xFF","\xFF","\xF7"],"\x1C"=>["\x0F","\xFF","\xFF","\xF8"],"\x1D"=>["\x0F","\xFF","\xFF","\xF9"],"\x1E"=>["\x0F","\xFF","\xFF","\xFA"],"\x1F"=>["\x0F","\xFF","\xFF","\xFB"],"\x20"=>["\x05","\x00"],"\x21"=>["\x0F","\xE0"],"\x22"=>["\x0F","\xE4"],"\x23"=>["\x0F","\xFA"],"\x24"=>["\x0F","\xFC","\x80"],"\x25"=>["\x05","\x40"],"\x26"=>["\x0F","\x80"],"\x27"=>["\x0F","\xF4"],"\x28"=>["\x0F","\xE8"],"\x29"=>["\x0F","\xEC"],"\x2A"=>["\x0F","\x90"],"\x2B"=>["\x0F","\xF6"],"\x2C"=>["\x0F","\xA0"],"-"=>["\x05","\x80"],"."=>["\x05","\xC0"],"\x2F"=>["\x06","\x00"],["\x00","\x00"],["\x00","\x80"],["\x01","\x00"],["\x06","\x40"],["\x06","\x80"],["\x06","\xC0"],["\x07","\x00"],["\x07","\x40"],["\x07","\x80"],["\x07","\xC0"],"\x3A"=>["\x0B","\x80"],"\x3B"=>["\x0F","\xB0"],"\x3C"=>["\x0F","\xFF","\x80"],"\x3D"=>["\x08","\x00"],"\x3E"=>["\x0F","\xFB"],"\x3F"=>["\x0F","\xF0"],"\x40"=>["\x0F","\xFD","\x00"],"A"=>["\x08","\x40"],"B"=>["\x0B","\xA0"],"C"=>["\x0B","\xC0"],"D"=>["\x0B","\xE0"],"E"=>["\x0C","\x00"],"F"=>["\x0C","\x20"],"G"=>["\x0C","\x40"],"H"=>["\x0C","\x60"],"I"=>["\x0C","\x80"],"J"=>["\x0C","\xA0"],"K"=>["\x0C","\xC0"],"L"=>["\x0C","\xE0"],"M"=>["\x0D","\x00"],"N"=>["\x0D","\x20"],"O"=>["\x0D","\x40"],"P"=>["\x0D","\x60"],"Q"=>["\x0D","\x80"],"R"=>["\x0D","\xA0"],"S"=>["\x0D","\xC0"],"T"=>["\x0D","\xE0"],"U"=>["\x0E","\x00"],"V"=>["\x0E","\x20"],"W"=>["\x0E","\x40"],"X"=>["\x0F","\xC0"],"Y"=>["\x0E","\x60"],"Z"=>["\x0F","\xD0"],"\x5B"=>["\x0F","\xFD","\x80"],"\x5C"=>["\x0F","\xFF","\xE0"],"\x5D"=>["\x0F","\xFE","\x00"],"\x5E"=>["\x0F","\xFF","\x00"],"_"=>["\x08","\x80"],"\x60"=>["\x0F","\xFF","\xA0"],"a"=>["\x01","\x80"],"b"=>["\x08","\xC0"],"c"=>["\x02","\x00"],"d"=>["\x09","\x00"],"e"=>["\x02","\x80"],"f"=>["\x09","\x40"],"g"=>["\x09","\x80"],"h"=>["\x09","\xC0"],"i"=>["\x03","\x00"],"j"=>["\x0E","\x80"],"k"=>["\x0E","\xA0"],"l"=>["\x0A","\x00"],"m"=>["\x0A","\x40"],"n"=>["\x0A","\x80"],"o"=>["\x03","\x80"],"p"=>["\x0A","\xC0"],"q"=>["\x0E","\xC0"],"r"=>["\x0B","\x00"],"s"=>["\x04","\x00"],"t"=>["\x04","\x80"],"u"=>["\x0B","\x40"],"v"=>["\x0E","\xE0"],"w"=>["\x0F","\x00"],"x"=>["\x0F","\x20"],"y"=>["\x0F","\x40"],"z"=>["\x0F","\x60"],"\x7B"=>["\x0F","\xFF","\xC0"],"\x7C"=>["\x0F","\xF8"],"\x7D"=>["\x0F","\xFF","\x40"],"~"=>["\x0F","\xFE","\x80"],"\x7F"=>["\x0F","\xFF","\xFF","\xFC"],"\x80"=>["\x0F","\xFF","\xE6"],"\x81"=>["\x0F","\xFF","\xF4","\x80"],"\x82"=>["\x0F","\xFF","\xE7"],"\x83"=>["\x0F","\xFF","\xE8"],"\x84"=>["\x0F","\xFF","\xF4","\xC0"],"\x85"=>["\x0F","\xFF","\xF5","\x00"],"\x86"=>["\x0F","\xFF","\xF5","\x40"],"\x87"=>["\x0F","\xFF","\xFB","\x20"],"\x88"=>["\x0F","\xFF","\xF5","\x80"],"\x89"=>["\x0F","\xFF","\xFB","\x40"],"\x8A"=>["\x0F","\xFF","\xFB","\x60"],"\x8B"=>["\x0F","\xFF","\xFB","\x80"],"\x8C"=>["\x0F","\xFF","\xFB","\xA0"],"\x8D"=>["\x0F","\xFF","\xFB","\xC0"],"\x8E"=>["\x0F","\xFF","\xFE","\xB0"],"\x8F"=>["\x0F","\xFF","\xFB","\xE0"],"\x90"=>["\x0F","\xFF","\xFE","\xC0"],"\x91"=>["\x0F","\xFF","\xFE","\xD0"],"\x92"=>["\x0F","\xFF","\xF5","\xC0"],"\x93"=>["\x0F","\xFF","\xFC","\x00"],"\x94"=>["\x0F","\xFF","\xFE","\xE0"],"\x95"=>["\x0F","\xFF","\xFC","\x20"],"\x96"=>["\x0F","\xFF","\xFC","\x40"],"\x97"=>["\x0F","\xFF","\xFC","\x60"],"\x98"=>["\x0F","\xFF","\xFC","\x80"],"\x99"=>["\x0F","\xFF","\xEE","\x00"],"\x9A"=>["\x0F","\xFF","\xF6","\x00"],"\x9B"=>["\x0F","\xFF","\xFC","\xA0"],"\x9C"=>["\x0F","\xFF","\xF6","\x40"],"\x9D"=>["\x0F","\xFF","\xFC","\xC0"],"\x9E"=>["\x0F","\xFF","\xFC","\xE0"],"\x9F"=>["\x0F","\xFF","\xFE","\xF0"],"\xA0"=>["\x0F","\xFF","\xF6","\x80"],"\xA1"=>["\x0F","\xFF","\xEE","\x80"],"\xA2"=>["\x0F","\xFF","\xE9"],"\xA3"=>["\x0F","\xFF","\xF6","\xC0"],"\xA4"=>["\x0F","\xFF","\xF7","\x00"],"\xA5"=>["\x0F","\xFF","\xFD","\x00"],"\xA6"=>["\x0F","\xFF","\xFD","\x20"],"\xA7"=>["\x0F","\xFF","\xEF","\x00"],"\xA8"=>["\x0F","\xFF","\xFD","\x40"],"\xA9"=>["\x0F","\xFF","\xF7","\x40"],"\xAA"=>["\x0F","\xFF","\xF7","\x80"],"\xAB"=>["\x0F","\xFF","\xFF","\x00"],"\xAC"=>["\x0F","\xFF","\xEF","\x80"],"\xAD"=>["\x0F","\xFF","\xF7","\xC0"],"\xAE"=>["\x0F","\xFF","\xFD","\x60"],"\xAF"=>["\x0F","\xFF","\xFD","\x80"],"\xB0"=>["\x0F","\xFF","\xF0","\x00"],"\xB1"=>["\x0F","\xFF","\xF0","\x80"],"\xB2"=>["\x0F","\xFF","\xF8","\x00"],"\xB3"=>["\x0F","\xFF","\xF1","\x00"],"\xB4"=>["\x0F","\xFF","\xFD","\xA0"],"\xB5"=>["\x0F","\xFF","\xF8","\x40"],"\xB6"=>["\x0F","\xFF","\xFD","\xC0"],"\xB7"=>["\x0F","\xFF","\xFD","\xE0"],"\xB8"=>["\x0F","\xFF","\xEA"],"\xB9"=>["\x0F","\xFF","\xF8","\x80"],"\xBA"=>["\x0F","\xFF","\xF8","\xC0"],"\xBB"=>["\x0F","\xFF","\xF9","\x00"],"\xBC"=>["\x0F","\xFF","\xFE","\x00"],"\xBD"=>["\x0F","\xFF","\xF9","\x40"],"\xBE"=>["\x0F","\xFF","\xF9","\x80"],"\xBF"=>["\x0F","\xFF","\xFE","\x20"],"\xC0"=>["\x0F","\xFF","\xFF","\x80"],"\xC1"=>["\x0F","\xFF","\xFF","\x84"],"\xC2"=>["\x0F","\xFF","\xEB"],"\xC3"=>["\x0F","\xFF","\xE2"],"\xC4"=>["\x0F","\xFF","\xF9","\xC0"],"\xC5"=>["\x0F","\xFF","\xFE","\x40"],"\xC6"=>["\x0F","\xFF","\xFA","\x00"],"\xC7"=>["\x0F","\xFF","\xFF","\x60"],"\xC8"=>["\x0F","\xFF","\xFF","\x88"],"\xC9"=>["\x0F","\xFF","\xFF","\x8C"],"\xCA"=>["\x0F","\xFF","\xFF","\x90"],"\xCB"=>["\x0F","\xFF","\xFF","\xBC"],"\xCC"=>["\x0F","\xFF","\xFF","\xBE"],"\xCD"=>["\x0F","\xFF","\xFF","\x94"],"\xCE"=>["\x0F","\xFF","\xFF","\x10"],"\xCF"=>["\x0F","\xFF","\xFF","h"],"\xD0"=>["\x0F","\xFF","\xE4"],"\xD1"=>["\x0F","\xFF","\xF1","\x80"],"\xD2"=>["\x0F","\xFF","\xFF","\x98"],"\xD3"=>["\x0F","\xFF","\xFF","\xC0"],"\xD4"=>["\x0F","\xFF","\xFF","\xC2"],"\xD5"=>["\x0F","\xFF","\xFF","\x9C"],"\xD6"=>["\x0F","\xFF","\xFF","\xC4"],"\xD7"=>["\x0F","\xFF","\xFF","\x20"],"\xD8"=>["\x0F","\xFF","\xF2","\x00"],"\xD9"=>["\x0F","\xFF","\xF2","\x80"],"\xDA"=>["\x0F","\xFF","\xFF","\xA0"],"\xDB"=>["\x0F","\xFF","\xFF","\xA4"],"\xDC"=>["\x0F","\xFF","\xFF","\xFD"],"\xDD"=>["\x0F","\xFF","\xFF","\xC6"],"\xDE"=>["\x0F","\xFF","\xFF","\xC8"],"\xDF"=>["\x0F","\xFF","\xFF","\xCA"],"\xE0"=>["\x0F","\xFF","\xEC"],"\xE1"=>["\x0F","\xFF","\xFF","0"],"\xE2"=>["\x0F","\xFF","\xED"],"\xE3"=>["\x0F","\xFF","\xF3","\x00"],"\xE4"=>["\x0F","\xFF","\xFA","\x40"],"\xE5"=>["\x0F","\xFF","\xF3","\x80"],"\xE6"=>["\x0F","\xFF","\xF4","\x00"],"\xE7"=>["\x0F","\xFF","\xFE","\x60"],"\xE8"=>["\x0F","\xFF","\xFA","\x80"],"\xE9"=>["\x0F","\xFF","\xFA","\xC0"],"\xEA"=>["\x0F","\xFF","\xFF","p"],"\xEB"=>["\x0F","\xFF","\xFF","x"],"\xEC"=>["\x0F","\xFF","\xFF","\x40"],"\xED"=>["\x0F","\xFF","\xFF","P"],"\xEE"=>["\x0F","\xFF","\xFF","\xA8"],"\xEF"=>["\x0F","\xFF","\xFE","\x80"],"\xF0"=>["\x0F","\xFF","\xFF","\xAC"],"\xF1"=>["\x0F","\xFF","\xFF","\xCC"],"\xF2"=>["\x0F","\xFF","\xFF","\xB0"],"\xF3"=>["\x0F","\xFF","\xFF","\xB4"],"\xF4"=>["\x0F","\xFF","\xFF","\xCE"],"\xF5"=>["\x0F","\xFF","\xFF","\xD0"],"\xF6"=>["\x0F","\xFF","\xFF","\xD2"],"\xF7"=>["\x0F","\xFF","\xFF","\xD4"],"\xF8"=>["\x0F","\xFF","\xFF","\xD6"],"\xF9"=>["\x0F","\xFF","\xFF","\xFE"],"\xFA"=>["\x0F","\xFF","\xFF","\xD8"],"\xFB"=>["\x0F","\xFF","\xFF","\xDA"],"\xFC"=>["\x0F","\xFF","\xFF","\xDC"],"\xFD"=>["\x0F","\xFF","\xFF","\xDE"],"\xFE"=>["\x0F","\xFF","\xFF","\xE0"],"\xFF"=>["\x0F","\xFF","\xFF","\xB8"],],["\x00"=>["\x07","\xFE","\x00"],"\x01"=>["\x07","\xFF","\xFD","\x80"],"\x02"=>["\x07","\xFF","\xFF","\xF1","\x00"],"\x03"=>["\x07","\xFF","\xFF","\xF1","\x80"],"\x04"=>["\x07","\xFF","\xFF","\xF2","\x00"],"\x05"=>["\x07","\xFF","\xFF","\xF2","\x80"],"\x06"=>["\x07","\xFF","\xFF","\xF3","\x00"],"\x07"=>["\x07","\xFF","\xFF","\xF3","\x80"],"\x08"=>["\x07","\xFF","\xFF","\xF4","\x00"],"\x09"=>["\x07","\xFF","\xFF","P"],"\x0A"=>["\x07","\xFF","\xFF","\xFF","\x80"],"\x0B"=>["\x07","\xFF","\xFF","\xF4","\x80"],"\x0C"=>["\x07","\xFF","\xFF","\xF5","\x00"],"\x0D"=>["\x07","\xFF","\xFF","\xFF","\xA0"],"\x0E"=>["\x07","\xFF","\xFF","\xF5","\x80"],"\x0F"=>["\x07","\xFF","\xFF","\xF6","\x00"],"\x10"=>["\x07","\xFF","\xFF","\xF6","\x80"],"\x11"=>["\x07","\xFF","\xFF","\xF7","\x00"],"\x12"=>["\x07","\xFF","\xFF","\xF7","\x80"],"\x13"=>["\x07","\xFF","\xFF","\xF8","\x00"],"\x14"=>["\x07","\xFF","\xFF","\xF8","\x80"],"\x15"=>["\x07","\xFF","\xFF","\xF9","\x00"],"\x16"=>["\x07","\xFF","\xFF","\xFF","\xC0"],"\x17"=>["\x07","\xFF","\xFF","\xF9","\x80"],"\x18"=>["\x07","\xFF","\xFF","\xFA","\x00"],"\x19"=>["\x07","\xFF","\xFF","\xFA","\x80"],"\x1A"=>["\x07","\xFF","\xFF","\xFB","\x00"],"\x1B"=>["\x07","\xFF","\xFF","\xFB","\x80"],"\x1C"=>["\x07","\xFF","\xFF","\xFC","\x00"],"\x1D"=>["\x07","\xFF","\xFF","\xFC","\x80"],"\x1E"=>["\x07","\xFF","\xFF","\xFD","\x00"],"\x1F"=>["\x07","\xFF","\xFF","\xFD","\x80"],"\x20"=>["\x02","\x80"],"\x21"=>["\x07","\xF0"],"\x22"=>["\x07","\xF2"],"\x23"=>["\x07","\xFD","\x00"],"\x24"=>["\x07","\xFE","\x40"],"\x25"=>["\x02","\xA0"],"\x26"=>["\x07","\xC0"],"\x27"=>["\x07","\xFA"],"\x28"=>["\x07","\xF4"],"\x29"=>["\x07","\xF6"],"\x2A"=>["\x07","\xC8"],"\x2B"=>["\x07","\xFB"],"\x2C"=>["\x07","\xD0"],"-"=>["\x02","\xC0"],"."=>["\x02","\xE0"],"\x2F"=>["\x03","\x00"],["\x00","\x00"],["\x00","\x40"],["\x00","\x80"],["\x03","\x20"],["\x03","\x40"],["\x03","\x60"],["\x03","\x80"],["\x03","\xA0"],["\x03","\xC0"],["\x03","\xE0"],"\x3A"=>["\x05","\xC0"],"\x3B"=>["\x07","\xD8"],"\x3C"=>["\x07","\xFF","\xC0"],"\x3D"=>["\x04","\x00"],"\x3E"=>["\x07","\xFD","\x80"],"\x3F"=>["\x07","\xF8"],"\x40"=>["\x07","\xFE","\x80"],"A"=>["\x04","\x20"],"B"=>["\x05","\xD0"],"C"=>["\x05","\xE0"],"D"=>["\x05","\xF0"],"E"=>["\x06","\x00"],"F"=>["\x06","\x10"],"G"=>["\x06","\x20"],"H"=>["\x06","0"],"I"=>["\x06","\x40"],"J"=>["\x06","P"],"K"=>["\x06","\x60"],"L"=>["\x06","p"],"M"=>["\x06","\x80"],"N"=>["\x06","\x90"],"O"=>["\x06","\xA0"],"P"=>["\x06","\xB0"],"Q"=>["\x06","\xC0"],"R"=>["\x06","\xD0"],"S"=>["\x06","\xE0"],"T"=>["\x06","\xF0"],"U"=>["\x07","\x00"],"V"=>["\x07","\x10"],"W"=>["\x07","\x20"],"X"=>["\x07","\xE0"],"Y"=>["\x07","0"],"Z"=>["\x07","\xE8"],"\x5B"=>["\x07","\xFE","\xC0"],"\x5C"=>["\x07","\xFF","\xF0"],"\x5D"=>["\x07","\xFF","\x00"],"\x5E"=>["\x07","\xFF","\x80"],"_"=>["\x04","\x40"],"\x60"=>["\x07","\xFF","\xD0"],"a"=>["\x00","\xC0"],"b"=>["\x04","\x60"],"c"=>["\x01","\x00"],"d"=>["\x04","\x80"],"e"=>["\x01","\x40"],"f"=>["\x04","\xA0"],"g"=>["\x04","\xC0"],"h"=>["\x04","\xE0"],"i"=>["\x01","\x80"],"j"=>["\x07","\x40"],"k"=>["\x07","P"],"l"=>["\x05","\x00"],"m"=>["\x05","\x20"],"n"=>["\x05","\x40"],"o"=>["\x01","\xC0"],"p"=>["\x05","\x60"],"q"=>["\x07","\x60"],"r"=>["\x05","\x80"],"s"=>["\x02","\x00"],"t"=>["\x02","\x40"],"u"=>["\x05","\xA0"],"v"=>["\x07","p"],"w"=>["\x07","\x80"],"x"=>["\x07","\x90"],"y"=>["\x07","\xA0"],"z"=>["\x07","\xB0"],"\x7B"=>["\x07","\xFF","\xE0"],"\x7C"=>["\x07","\xFC"],"\x7D"=>["\x07","\xFF","\xA0"],"~"=>["\x07","\xFF","\x40"],"\x7F"=>["\x07","\xFF","\xFF","\xFE","\x00"],"\x80"=>["\x07","\xFF","\xF3","\x00"],"\x81"=>["\x07","\xFF","\xFA","\x40"],"\x82"=>["\x07","\xFF","\xF3","\x80"],"\x83"=>["\x07","\xFF","\xF4","\x00"],"\x84"=>["\x07","\xFF","\xFA","\x60"],"\x85"=>["\x07","\xFF","\xFA","\x80"],"\x86"=>["\x07","\xFF","\xFA","\xA0"],"\x87"=>["\x07","\xFF","\xFD","\x90"],"\x88"=>["\x07","\xFF","\xFA","\xC0"],"\x89"=>["\x07","\xFF","\xFD","\xA0"],"\x8A"=>["\x07","\xFF","\xFD","\xB0"],"\x8B"=>["\x07","\xFF","\xFD","\xC0"],"\x8C"=>["\x07","\xFF","\xFD","\xD0"],"\x8D"=>["\x07","\xFF","\xFD","\xE0"],"\x8E"=>["\x07","\xFF","\xFF","X"],"\x8F"=>["\x07","\xFF","\xFD","\xF0"],"\x90"=>["\x07","\xFF","\xFF","\x60"],"\x91"=>["\x07","\xFF","\xFF","h"],"\x92"=>["\x07","\xFF","\xFA","\xE0"],"\x93"=>["\x07","\xFF","\xFE","\x00"],"\x94"=>["\x07","\xFF","\xFF","p"],"\x95"=>["\x07","\xFF","\xFE","\x10"],"\x96"=>["\x07","\xFF","\xFE","\x20"],"\x97"=>["\x07","\xFF","\xFE","0"],"\x98"=>["\x07","\xFF","\xFE","\x40"],"\x99"=>["\x07","\xFF","\xF7","\x00"],"\x9A"=>["\x07","\xFF","\xFB","\x00"],"\x9B"=>["\x07","\xFF","\xFE","P"],"\x9C"=>["\x07","\xFF","\xFB","\x20"],"\x9D"=>["\x07","\xFF","\xFE","\x60"],"\x9E"=>["\x07","\xFF","\xFE","p"],"\x9F"=>["\x07","\xFF","\xFF","x"],"\xA0"=>["\x07","\xFF","\xFB","\x40"],"\xA1"=>["\x07","\xFF","\xF7","\x40"],"\xA2"=>["\x07","\xFF","\xF4","\x80"],"\xA3"=>["\x07","\xFF","\xFB","\x60"],"\xA4"=>["\x07","\xFF","\xFB","\x80"],"\xA5"=>["\x07","\xFF","\xFE","\x80"],"\xA6"=>["\x07","\xFF","\xFE","\x90"],"\xA7"=>["\x07","\xFF","\xF7","\x80"],"\xA8"=>["\x07","\xFF","\xFE","\xA0"],"\xA9"=>["\x07","\xFF","\xFB","\xA0"],"\xAA"=>["\x07","\xFF","\xFB","\xC0"],"\xAB"=>["\x07","\xFF","\xFF","\x80"],"\xAC"=>["\x07","\xFF","\xF7","\xC0"],"\xAD"=>["\x07","\xFF","\xFB","\xE0"],"\xAE"=>["\x07","\xFF","\xFE","\xB0"],"\xAF"=>["\x07","\xFF","\xFE","\xC0"],"\xB0"=>["\x07","\xFF","\xF8","\x00"],"\xB1"=>["\x07","\xFF","\xF8","\x40"],"\xB2"=>["\x07","\xFF","\xFC","\x00"],"\xB3"=>["\x07","\xFF","\xF8","\x80"],"\xB4"=>["\x07","\xFF","\xFE","\xD0"],"\xB5"=>["\x07","\xFF","\xFC","\x20"],"\xB6"=>["\x07","\xFF","\xFE","\xE0"],"\xB7"=>["\x07","\xFF","\xFE","\xF0"],"\xB8"=>["\x07","\xFF","\xF5","\x00"],"\xB9"=>["\x07","\xFF","\xFC","\x40"],"\xBA"=>["\x07","\xFF","\xFC","\x60"],"\xBB"=>["\x07","\xFF","\xFC","\x80"],"\xBC"=>["\x07","\xFF","\xFF","\x00"],"\xBD"=>["\x07","\xFF","\xFC","\xA0"],"\xBE"=>["\x07","\xFF","\xFC","\xC0"],"\xBF"=>["\x07","\xFF","\xFF","\x10"],"\xC0"=>["\x07","\xFF","\xFF","\xC0"],"\xC1"=>["\x07","\xFF","\xFF","\xC2"],"\xC2"=>["\x07","\xFF","\xF5","\x80"],"\xC3"=>["\x07","\xFF","\xF1"],"\xC4"=>["\x07","\xFF","\xFC","\xE0"],"\xC5"=>["\x07","\xFF","\xFF","\x20"],"\xC6"=>["\x07","\xFF","\xFD","\x00"],"\xC7"=>["\x07","\xFF","\xFF","\xB0"],"\xC8"=>["\x07","\xFF","\xFF","\xC4"],"\xC9"=>["\x07","\xFF","\xFF","\xC6"],"\xCA"=>["\x07","\xFF","\xFF","\xC8"],"\xCB"=>["\x07","\xFF","\xFF","\xDE"],"\xCC"=>["\x07","\xFF","\xFF","\xDF"],"\xCD"=>["\x07","\xFF","\xFF","\xCA"],"\xCE"=>["\x07","\xFF","\xFF","\x88"],"\xCF"=>["\x07","\xFF","\xFF","\xB4"],"\xD0"=>["\x07","\xFF","\xF2"],"\xD1"=>["\x07","\xFF","\xF8","\xC0"],"\xD2"=>["\x07","\xFF","\xFF","\xCC"],"\xD3"=>["\x07","\xFF","\xFF","\xE0"],"\xD4"=>["\x07","\xFF","\xFF","\xE1"],"\xD5"=>["\x07","\xFF","\xFF","\xCE"],"\xD6"=>["\x07","\xFF","\xFF","\xE2"],"\xD7"=>["\x07","\xFF","\xFF","\x90"],"\xD8"=>["\x07","\xFF","\xF9","\x00"],"\xD9"=>["\x07","\xFF","\xF9","\x40"],"\xDA"=>["\x07","\xFF","\xFF","\xD0"],"\xDB"=>["\x07","\xFF","\xFF","\xD2"],"\xDC"=>["\x07","\xFF","\xFF","\xFE","\x80"],"\xDD"=>["\x07","\xFF","\xFF","\xE3"],"\xDE"=>["\x07","\xFF","\xFF","\xE4"],"\xDF"=>["\x07","\xFF","\xFF","\xE5"],"\xE0"=>["\x07","\xFF","\xF6","\x00"],"\xE1"=>["\x07","\xFF","\xFF","\x98"],"\xE2"=>["\x07","\xFF","\xF6","\x80"],"\xE3"=>["\x07","\xFF","\xF9","\x80"],"\xE4"=>["\x07","\xFF","\xFD","\x20"],"\xE5"=>["\x07","\xFF","\xF9","\xC0"],"\xE6"=>["\x07","\xFF","\xFA","\x00"],"\xE7"=>["\x07","\xFF","\xFF","0"],"\xE8"=>["\x07","\xFF","\xFD","\x40"],"\xE9"=>["\x07","\xFF","\xFD","\x60"],"\xEA"=>["\x07","\xFF","\xFF","\xB8"],"\xEB"=>["\x07","\xFF","\xFF","\xBC"],"\xEC"=>["\x07","\xFF","\xFF","\xA0"],"\xED"=>["\x07","\xFF","\xFF","\xA8"],"\xEE"=>["\x07","\xFF","\xFF","\xD4"],"\xEF"=>["\x07","\xFF","\xFF","\x40"],"\xF0"=>["\x07","\xFF","\xFF","\xD6"],"\xF1"=>["\x07","\xFF","\xFF","\xE6"],"\xF2"=>["\x07","\xFF","\xFF","\xD8"],"\xF3"=>["\x07","\xFF","\xFF","\xDA"],"\xF4"=>["\x07","\xFF","\xFF","\xE7"],"\xF5"=>["\x07","\xFF","\xFF","\xE8"],"\xF6"=>["\x07","\xFF","\xFF","\xE9"],"\xF7"=>["\x07","\xFF","\xFF","\xEA"],"\xF8"=>["\x07","\xFF","\xFF","\xEB"],"\xF9"=>["\x07","\xFF","\xFF","\xFF","\x00"],"\xFA"=>["\x07","\xFF","\xFF","\xEC"],"\xFB"=>["\x07","\xFF","\xFF","\xED"],"\xFC"=>["\x07","\xFF","\xFF","\xEE"],"\xFD"=>["\x07","\xFF","\xFF","\xEF"],"\xFE"=>["\x07","\xFF","\xFF","\xF0"],"\xFF"=>["\x07","\xFF","\xFF","\xDC"],],["\x00"=>["\x03","\xFF","\x00"],"\x01"=>["\x03","\xFF","\xFE","\xC0"],"\x02"=>["\x03","\xFF","\xFF","\xF8","\x80"],"\x03"=>["\x03","\xFF","\xFF","\xF8","\xC0"],"\x04"=>["\x03","\xFF","\xFF","\xF9","\x00"],"\x05"=>["\x03","\xFF","\xFF","\xF9","\x40"],"\x06"=>["\x03","\xFF","\xFF","\xF9","\x80"],"\x07"=>["\x03","\xFF","\xFF","\xF9","\xC0"],"\x08"=>["\x03","\xFF","\xFF","\xFA","\x00"],"\x09"=>["\x03","\xFF","\xFF","\xA8"],"\x0A"=>["\x03","\xFF","\xFF","\xFF","\xC0"],"\x0B"=>["\x03","\xFF","\xFF","\xFA","\x40"],"\x0C"=>["\x03","\xFF","\xFF","\xFA","\x80"],"\x0D"=>["\x03","\xFF","\xFF","\xFF","\xD0"],"\x0E"=>["\x03","\xFF","\xFF","\xFA","\xC0"],"\x0F"=>["\x03","\xFF","\xFF","\xFB","\x00"],"\x10"=>["\x03","\xFF","\xFF","\xFB","\x40"],"\x11"=>["\x03","\xFF","\xFF","\xFB","\x80"],"\x12"=>["\x03","\xFF","\xFF","\xFB","\xC0"],"\x13"=>["\x03","\xFF","\xFF","\xFC","\x00"],"\x14"=>["\x03","\xFF","\xFF","\xFC","\x40"],"\x15"=>["\x03","\xFF","\xFF","\xFC","\x80"],"\x16"=>["\x03","\xFF","\xFF","\xFF","\xE0"],"\x17"=>["\x03","\xFF","\xFF","\xFC","\xC0"],"\x18"=>["\x03","\xFF","\xFF","\xFD","\x00"],"\x19"=>["\x03","\xFF","\xFF","\xFD","\x40"],"\x1A"=>["\x03","\xFF","\xFF","\xFD","\x80"],"\x1B"=>["\x03","\xFF","\xFF","\xFD","\xC0"],"\x1C"=>["\x03","\xFF","\xFF","\xFE","\x00"],"\x1D"=>["\x03","\xFF","\xFF","\xFE","\x40"],"\x1E"=>["\x03","\xFF","\xFF","\xFE","\x80"],"\x1F"=>["\x03","\xFF","\xFF","\xFE","\xC0"],"\x20"=>["\x01","\x40"],"\x21"=>["\x03","\xF8"],"\x22"=>["\x03","\xF9"],"\x23"=>["\x03","\xFE","\x80"],"\x24"=>["\x03","\xFF","\x20"],"\x25"=>["\x01","P"],"\x26"=>["\x03","\xE0"],"\x27"=>["\x03","\xFD","\x00"],"\x28"=>["\x03","\xFA"],"\x29"=>["\x03","\xFB"],"\x2A"=>["\x03","\xE4"],"\x2B"=>["\x03","\xFD","\x80"],"\x2C"=>["\x03","\xE8"],"-"=>["\x01","\x60"],"."=>["\x01","p"],"\x2F"=>["\x01","\x80"],["\x00","\x00"],["\x00","\x20"],["\x00","\x40"],["\x01","\x90"],["\x01","\xA0"],["\x01","\xB0"],["\x01","\xC0"],["\x01","\xD0"],["\x01","\xE0"],["\x01","\xF0"],"\x3A"=>["\x02","\xE0"],"\x3B"=>["\x03","\xEC"],"\x3C"=>["\x03","\xFF","\xE0"],"\x3D"=>["\x02","\x00"],"\x3E"=>["\x03","\xFE","\xC0"],"\x3F"=>["\x03","\xFC"],"\x40"=>["\x03","\xFF","\x40"],"A"=>["\x02","\x10"],"B"=>["\x02","\xE8"],"C"=>["\x02","\xF0"],"D"=>["\x02","\xF8"],"E"=>["\x03","\x00"],"F"=>["\x03","\x08"],"G"=>["\x03","\x10"],"H"=>["\x03","\x18"],"I"=>["\x03","\x20"],"J"=>["\x03","\x28"],"K"=>["\x03","0"],"L"=>["\x03","8"],"M"=>["\x03","\x40"],"N"=>["\x03","H"],"O"=>["\x03","P"],"P"=>["\x03","X"],"Q"=>["\x03","\x60"],"R"=>["\x03","h"],"S"=>["\x03","p"],"T"=>["\x03","x"],"U"=>["\x03","\x80"],"V"=>["\x03","\x88"],"W"=>["\x03","\x90"],"X"=>["\x03","\xF0"],"Y"=>["\x03","\x98"],"Z"=>["\x03","\xF4"],"\x5B"=>["\x03","\xFF","\x60"],"\x5C"=>["\x03","\xFF","\xF8","\x00"],"\x5D"=>["\x03","\xFF","\x80"],"\x5E"=>["\x03","\xFF","\xC0"],"_"=>["\x02","\x20"],"\x60"=>["\x03","\xFF","\xE8"],"a"=>["\x00","\x60"],"b"=>["\x02","0"],"c"=>["\x00","\x80"],"d"=>["\x02","\x40"],"e"=>["\x00","\xA0"],"f"=>["\x02","P"],"g"=>["\x02","\x60"],"h"=>["\x02","p"],"i"=>["\x00","\xC0"],"j"=>["\x03","\xA0"],"k"=>["\x03","\xA8"],"l"=>["\x02","\x80"],"m"=>["\x02","\x90"],"n"=>["\x02","\xA0"],"o"=>["\x00","\xE0"],"p"=>["\x02","\xB0"],"q"=>["\x03","\xB0"],"r"=>["\x02","\xC0"],"s"=>["\x01","\x00"],"t"=>["\x01","\x20"],"u"=>["\x02","\xD0"],"v"=>["\x03","\xB8"],"w"=>["\x03","\xC0"],"x"=>["\x03","\xC8"],"y"=>["\x03","\xD0"],"z"=>["\x03","\xD8"],"\x7B"=>["\x03","\xFF","\xF0"],"\x7C"=>["\x03","\xFE","\x00"],"\x7D"=>["\x03","\xFF","\xD0"],"~"=>["\x03","\xFF","\xA0"],"\x7F"=>["\x03","\xFF","\xFF","\xFF","\x00"],"\x80"=>["\x03","\xFF","\xF9","\x80"],"\x81"=>["\x03","\xFF","\xFD","\x20"],"\x82"=>["\x03","\xFF","\xF9","\xC0"],"\x83"=>["\x03","\xFF","\xFA","\x00"],"\x84"=>["\x03","\xFF","\xFD","0"],"\x85"=>["\x03","\xFF","\xFD","\x40"],"\x86"=>["\x03","\xFF","\xFD","P"],"\x87"=>["\x03","\xFF","\xFE","\xC8"],"\x88"=>["\x03","\xFF","\xFD","\x60"],"\x89"=>["\x03","\xFF","\xFE","\xD0"],"\x8A"=>["\x03","\xFF","\xFE","\xD8"],"\x8B"=>["\x03","\xFF","\xFE","\xE0"],"\x8C"=>["\x03","\xFF","\xFE","\xE8"],"\x8D"=>["\x03","\xFF","\xFE","\xF0"],"\x8E"=>["\x03","\xFF","\xFF","\xAC"],"\x8F"=>["\x03","\xFF","\xFE","\xF8"],"\x90"=>["\x03","\xFF","\xFF","\xB0"],"\x91"=>["\x03","\xFF","\xFF","\xB4"],"\x92"=>["\x03","\xFF","\xFD","p"],"\x93"=>["\x03","\xFF","\xFF","\x00"],"\x94"=>["\x03","\xFF","\xFF","\xB8"],"\x95"=>["\x03","\xFF","\xFF","\x08"],"\x96"=>["\x03","\xFF","\xFF","\x10"],"\x97"=>["\x03","\xFF","\xFF","\x18"],"\x98"=>["\x03","\xFF","\xFF","\x20"],"\x99"=>["\x03","\xFF","\xFB","\x80"],"\x9A"=>["\x03","\xFF","\xFD","\x80"],"\x9B"=>["\x03","\xFF","\xFF","\x28"],"\x9C"=>["\x03","\xFF","\xFD","\x90"],"\x9D"=>["\x03","\xFF","\xFF","0"],"\x9E"=>["\x03","\xFF","\xFF","8"],"\x9F"=>["\x03","\xFF","\xFF","\xBC"],"\xA0"=>["\x03","\xFF","\xFD","\xA0"],"\xA1"=>["\x03","\xFF","\xFB","\xA0"],"\xA2"=>["\x03","\xFF","\xFA","\x40"],"\xA3"=>["\x03","\xFF","\xFD","\xB0"],"\xA4"=>["\x03","\xFF","\xFD","\xC0"],"\xA5"=>["\x03","\xFF","\xFF","\x40"],"\xA6"=>["\x03","\xFF","\xFF","H"],"\xA7"=>["\x03","\xFF","\xFB","\xC0"],"\xA8"=>["\x03","\xFF","\xFF","P"],"\xA9"=>["\x03","\xFF","\xFD","\xD0"],"\xAA"=>["\x03","\xFF","\xFD","\xE0"],"\xAB"=>["\x03","\xFF","\xFF","\xC0"],"\xAC"=>["\x03","\xFF","\xFB","\xE0"],"\xAD"=>["\x03","\xFF","\xFD","\xF0"],"\xAE"=>["\x03","\xFF","\xFF","X"],"\xAF"=>["\x03","\xFF","\xFF","\x60"],"\xB0"=>["\x03","\xFF","\xFC","\x00"],"\xB1"=>["\x03","\xFF","\xFC","\x20"],"\xB2"=>["\x03","\xFF","\xFE","\x00"],"\xB3"=>["\x03","\xFF","\xFC","\x40"],"\xB4"=>["\x03","\xFF","\xFF","h"],"\xB5"=>["\x03","\xFF","\xFE","\x10"],"\xB6"=>["\x03","\xFF","\xFF","p"],"\xB7"=>["\x03","\xFF","\xFF","x"],"\xB8"=>["\x03","\xFF","\xFA","\x80"],"\xB9"=>["\x03","\xFF","\xFE","\x20"],"\xBA"=>["\x03","\xFF","\xFE","0"],"\xBB"=>["\x03","\xFF","\xFE","\x40"],"\xBC"=>["\x03","\xFF","\xFF","\x80"],"\xBD"=>["\x03","\xFF","\xFE","P"],"\xBE"=>["\x03","\xFF","\xFE","\x60"],"\xBF"=>["\x03","\xFF","\xFF","\x88"],"\xC0"=>["\x03","\xFF","\xFF","\xE0"],"\xC1"=>["\x03","\xFF","\xFF","\xE1"],"\xC2"=>["\x03","\xFF","\xFA","\xC0"],"\xC3"=>["\x03","\xFF","\xF8","\x80"],"\xC4"=>["\x03","\xFF","\xFE","p"],"\xC5"=>["\x03","\xFF","\xFF","\x90"],"\xC6"=>["\x03","\xFF","\xFE","\x80"],"\xC7"=>["\x03","\xFF","\xFF","\xD8"],"\xC8"=>["\x03","\xFF","\xFF","\xE2"],"\xC9"=>["\x03","\xFF","\xFF","\xE3"],"\xCA"=>["\x03","\xFF","\xFF","\xE4"],"\xCB"=>["\x03","\xFF","\xFF","\xEF","\x00"],"\xCC"=>["\x03","\xFF","\xFF","\xEF","\x80"],"\xCD"=>["\x03","\xFF","\xFF","\xE5"],"\xCE"=>["\x03","\xFF","\xFF","\xC4"],"\xCF"=>["\x03","\xFF","\xFF","\xDA"],"\xD0"=>["\x03","\xFF","\xF9","\x00"],"\xD1"=>["\x03","\xFF","\xFC","\x60"],"\xD2"=>["\x03","\xFF","\xFF","\xE6"],"\xD3"=>["\x03","\xFF","\xFF","\xF0","\x00"],"\xD4"=>["\x03","\xFF","\xFF","\xF0","\x80"],"\xD5"=>["\x03","\xFF","\xFF","\xE7"],"\xD6"=>["\x03","\xFF","\xFF","\xF1","\x00"],"\xD7"=>["\x03","\xFF","\xFF","\xC8"],"\xD8"=>["\x03","\xFF","\xFC","\x80"],"\xD9"=>["\x03","\xFF","\xFC","\xA0"],"\xDA"=>["\x03","\xFF","\xFF","\xE8"],"\xDB"=>["\x03","\xFF","\xFF","\xE9"],"\xDC"=>["\x03","\xFF","\xFF","\xFF","\x40"],"\xDD"=>["\x03","\xFF","\xFF","\xF1","\x80"],"\xDE"=>["\x03","\xFF","\xFF","\xF2","\x00"],"\xDF"=>["\x03","\xFF","\xFF","\xF2","\x80"],"\xE0"=>["\x03","\xFF","\xFB","\x00"],"\xE1"=>["\x03","\xFF","\xFF","\xCC"],"\xE2"=>["\x03","\xFF","\xFB","\x40"],"\xE3"=>["\x03","\xFF","\xFC","\xC0"],"\xE4"=>["\x03","\xFF","\xFE","\x90"],"\xE5"=>["\x03","\xFF","\xFC","\xE0"],"\xE6"=>["\x03","\xFF","\xFD","\x00"],"\xE7"=>["\x03","\xFF","\xFF","\x98"],"\xE8"=>["\x03","\xFF","\xFE","\xA0"],"\xE9"=>["\x03","\xFF","\xFE","\xB0"],"\xEA"=>["\x03","\xFF","\xFF","\xDC"],"\xEB"=>["\x03","\xFF","\xFF","\xDE"],"\xEC"=>["\x03","\xFF","\xFF","\xD0"],"\xED"=>["\x03","\xFF","\xFF","\xD4"],"\xEE"=>["\x03","\xFF","\xFF","\xEA"],"\xEF"=>["\x03","\xFF","\xFF","\xA0"],"\xF0"=>["\x03","\xFF","\xFF","\xEB"],"\xF1"=>["\x03","\xFF","\xFF","\xF3","\x00"],"\xF2"=>["\x03","\xFF","\xFF","\xEC"],"\xF3"=>["\x03","\xFF","\xFF","\xED"],"\xF4"=>["\x03","\xFF","\xFF","\xF3","\x80"],"\xF5"=>["\x03","\xFF","\xFF","\xF4","\x00"],"\xF6"=>["\x03","\xFF","\xFF","\xF4","\x80"],"\xF7"=>["\x03","\xFF","\xFF","\xF5","\x00"],"\xF8"=>["\x03","\xFF","\xFF","\xF5","\x80"],"\xF9"=>["\x03","\xFF","\xFF","\xFF","\x80"],"\xFA"=>["\x03","\xFF","\xFF","\xF6","\x00"],"\xFB"=>["\x03","\xFF","\xFF","\xF6","\x80"],"\xFC"=>["\x03","\xFF","\xFF","\xF7","\x00"],"\xFD"=>["\x03","\xFF","\xFF","\xF7","\x80"],"\xFE"=>["\x03","\xFF","\xFF","\xF8","\x00"],"\xFF"=>["\x03","\xFF","\xFF","\xEE"],],["\x00"=>["\x01","\xFF","\x80"],"\x01"=>["\x01","\xFF","\xFF","\x60"],"\x02"=>["\x01","\xFF","\xFF","\xFC","\x40"],"\x03"=>["\x01","\xFF","\xFF","\xFC","\x60"],"\x04"=>["\x01","\xFF","\xFF","\xFC","\x80"],"\x05"=>["\x01","\xFF","\xFF","\xFC","\xA0"],"\x06"=>["\x01","\xFF","\xFF","\xFC","\xC0"],"\x07"=>["\x01","\xFF","\xFF","\xFC","\xE0"],"\x08"=>["\x01","\xFF","\xFF","\xFD","\x00"],"\x09"=>["\x01","\xFF","\xFF","\xD4"],"\x0A"=>["\x01","\xFF","\xFF","\xFF","\xE0"],"\x0B"=>["\x01","\xFF","\xFF","\xFD","\x20"],"\x0C"=>["\x01","\xFF","\xFF","\xFD","\x40"],"\x0D"=>["\x01","\xFF","\xFF","\xFF","\xE8"],"\x0E"=>["\x01","\xFF","\xFF","\xFD","\x60"],"\x0F"=>["\x01","\xFF","\xFF","\xFD","\x80"],"\x10"=>["\x01","\xFF","\xFF","\xFD","\xA0"],"\x11"=>["\x01","\xFF","\xFF","\xFD","\xC0"],"\x12"=>["\x01","\xFF","\xFF","\xFD","\xE0"],"\x13"=>["\x01","\xFF","\xFF","\xFE","\x00"],"\x14"=>["\x01","\xFF","\xFF","\xFE","\x20"],"\x15"=>["\x01","\xFF","\xFF","\xFE","\x40"],"\x16"=>["\x01","\xFF","\xFF","\xFF","\xF0"],"\x17"=>["\x01","\xFF","\xFF","\xFE","\x60"],"\x18"=>["\x01","\xFF","\xFF","\xFE","\x80"],"\x19"=>["\x01","\xFF","\xFF","\xFE","\xA0"],"\x1A"=>["\x01","\xFF","\xFF","\xFE","\xC0"],"\x1B"=>["\x01","\xFF","\xFF","\xFE","\xE0"],"\x1C"=>["\x01","\xFF","\xFF","\xFF","\x00"],"\x1D"=>["\x01","\xFF","\xFF","\xFF","\x20"],"\x1E"=>["\x01","\xFF","\xFF","\xFF","\x40"],"\x1F"=>["\x01","\xFF","\xFF","\xFF","\x60"],"\x20"=>["\x00","\xA0"],"\x21"=>["\x01","\xFC","\x00"],"\x22"=>["\x01","\xFC","\x80"],"\x23"=>["\x01","\xFF","\x40"],"\x24"=>["\x01","\xFF","\x90"],"\x25"=>["\x00","\xA8"],"\x26"=>["\x01","\xF0"],"\x27"=>["\x01","\xFE","\x80"],"\x28"=>["\x01","\xFD","\x00"],"\x29"=>["\x01","\xFD","\x80"],"\x2A"=>["\x01","\xF2"],"\x2B"=>["\x01","\xFE","\xC0"],"\x2C"=>["\x01","\xF4"],"-"=>["\x00","\xB0"],"."=>["\x00","\xB8"],"\x2F"=>["\x00","\xC0"],["\x00","\x00"],["\x00","\x10"],["\x00","\x20"],["\x00","\xC8"],["\x00","\xD0"],["\x00","\xD8"],["\x00","\xE0"],["\x00","\xE8"],["\x00","\xF0"],["\x00","\xF8"],"\x3A"=>["\x01","p"],"\x3B"=>["\x01","\xF6"],"\x3C"=>["\x01","\xFF","\xF0"],"\x3D"=>["\x01","\x00"],"\x3E"=>["\x01","\xFF","\x60"],"\x3F"=>["\x01","\xFE","\x00"],"\x40"=>["\x01","\xFF","\xA0"],"A"=>["\x01","\x08"],"B"=>["\x01","t"],"C"=>["\x01","x"],"D"=>["\x01","\x7C"],"E"=>["\x01","\x80"],"F"=>["\x01","\x84"],"G"=>["\x01","\x88"],"H"=>["\x01","\x8C"],"I"=>["\x01","\x90"],"J"=>["\x01","\x94"],"K"=>["\x01","\x98"],"L"=>["\x01","\x9C"],"M"=>["\x01","\xA0"],"N"=>["\x01","\xA4"],"O"=>["\x01","\xA8"],"P"=>["\x01","\xAC"],"Q"=>["\x01","\xB0"],"R"=>["\x01","\xB4"],"S"=>["\x01","\xB8"],"T"=>["\x01","\xBC"],"U"=>["\x01","\xC0"],"V"=>["\x01","\xC4"],"W"=>["\x01","\xC8"],"X"=>["\x01","\xF8"],"Y"=>["\x01","\xCC"],"Z"=>["\x01","\xFA"],"\x5B"=>["\x01","\xFF","\xB0"],"\x5C"=>["\x01","\xFF","\xFC","\x00"],"\x5D"=>["\x01","\xFF","\xC0"],"\x5E"=>["\x01","\xFF","\xE0"],"_"=>["\x01","\x10"],"\x60"=>["\x01","\xFF","\xF4"],"a"=>["\x00","0"],"b"=>["\x01","\x18"],"c"=>["\x00","\x40"],"d"=>["\x01","\x20"],"e"=>["\x00","P"],"f"=>["\x01","\x28"],"g"=>["\x01","0"],"h"=>["\x01","8"],"i"=>["\x00","\x60"],"j"=>["\x01","\xD0"],"k"=>["\x01","\xD4"],"l"=>["\x01","\x40"],"m"=>["\x01","H"],"n"=>["\x01","P"],"o"=>["\x00","p"],"p"=>["\x01","X"],"q"=>["\x01","\xD8"],"r"=>["\x01","\x60"],"s"=>["\x00","\x80"],"t"=>["\x00","\x90"],"u"=>["\x01","h"],"v"=>["\x01","\xDC"],"w"=>["\x01","\xE0"],"x"=>["\x01","\xE4"],"y"=>["\x01","\xE8"],"z"=>["\x01","\xEC"],"\x7B"=>["\x01","\xFF","\xF8"],"\x7C"=>["\x01","\xFF","\x00"],"\x7D"=>["\x01","\xFF","\xE8"],"~"=>["\x01","\xFF","\xD0"],"\x7F"=>["\x01","\xFF","\xFF","\xFF","\x80"],"\x80"=>["\x01","\xFF","\xFC","\xC0"],"\x81"=>["\x01","\xFF","\xFE","\x90"],"\x82"=>["\x01","\xFF","\xFC","\xE0"],"\x83"=>["\x01","\xFF","\xFD","\x00"],"\x84"=>["\x01","\xFF","\xFE","\x98"],"\x85"=>["\x01","\xFF","\xFE","\xA0"],"\x86"=>["\x01","\xFF","\xFE","\xA8"],"\x87"=>["\x01","\xFF","\xFF","d"],"\x88"=>["\x01","\xFF","\xFE","\xB0"],"\x89"=>["\x01","\xFF","\xFF","h"],"\x8A"=>["\x01","\xFF","\xFF","l"],"\x8B"=>["\x01","\xFF","\xFF","p"],"\x8C"=>["\x01","\xFF","\xFF","t"],"\x8D"=>["\x01","\xFF","\xFF","x"],"\x8E"=>["\x01","\xFF","\xFF","\xD6"],"\x8F"=>["\x01","\xFF","\xFF","\x7C"],"\x90"=>["\x01","\xFF","\xFF","\xD8"],"\x91"=>["\x01","\xFF","\xFF","\xDA"],"\x92"=>["\x01","\xFF","\xFE","\xB8"],"\x93"=>["\x01","\xFF","\xFF","\x80"],"\x94"=>["\x01","\xFF","\xFF","\xDC"],"\x95"=>["\x01","\xFF","\xFF","\x84"],"\x96"=>["\x01","\xFF","\xFF","\x88"],"\x97"=>["\x01","\xFF","\xFF","\x8C"],"\x98"=>["\x01","\xFF","\xFF","\x90"],"\x99"=>["\x01","\xFF","\xFD","\xC0"],"\x9A"=>["\x01","\xFF","\xFE","\xC0"],"\x9B"=>["\x01","\xFF","\xFF","\x94"],"\x9C"=>["\x01","\xFF","\xFE","\xC8"],"\x9D"=>["\x01","\xFF","\xFF","\x98"],"\x9E"=>["\x01","\xFF","\xFF","\x9C"],"\x9F"=>["\x01","\xFF","\xFF","\xDE"],"\xA0"=>["\x01","\xFF","\xFE","\xD0"],"\xA1"=>["\x01","\xFF","\xFD","\xD0"],"\xA2"=>["\x01","\xFF","\xFD","\x20"],"\xA3"=>["\x01","\xFF","\xFE","\xD8"],"\xA4"=>["\x01","\xFF","\xFE","\xE0"],"\xA5"=>["\x01","\xFF","\xFF","\xA0"],"\xA6"=>["\x01","\xFF","\xFF","\xA4"],"\xA7"=>["\x01","\xFF","\xFD","\xE0"],"\xA8"=>["\x01","\xFF","\xFF","\xA8"],"\xA9"=>["\x01","\xFF","\xFE","\xE8"],"\xAA"=>["\x01","\xFF","\xFE","\xF0"],"\xAB"=>["\x01","\xFF","\xFF","\xE0"],"\xAC"=>["\x01","\xFF","\xFD","\xF0"],"\xAD"=>["\x01","\xFF","\xFE","\xF8"],"\xAE"=>["\x01","\xFF","\xFF","\xAC"],"\xAF"=>["\x01","\xFF","\xFF","\xB0"],"\xB0"=>["\x01","\xFF","\xFE","\x00"],"\xB1"=>["\x01","\xFF","\xFE","\x10"],"\xB2"=>["\x01","\xFF","\xFF","\x00"],"\xB3"=>["\x01","\xFF","\xFE","\x20"],"\xB4"=>["\x01","\xFF","\xFF","\xB4"],"\xB5"=>["\x01","\xFF","\xFF","\x08"],"\xB6"=>["\x01","\xFF","\xFF","\xB8"],"\xB7"=>["\x01","\xFF","\xFF","\xBC"],"\xB8"=>["\x01","\xFF","\xFD","\x40"],"\xB9"=>["\x01","\xFF","\xFF","\x10"],"\xBA"=>["\x01","\xFF","\xFF","\x18"],"\xBB"=>["\x01","\xFF","\xFF","\x20"],"\xBC"=>["\x01","\xFF","\xFF","\xC0"],"\xBD"=>["\x01","\xFF","\xFF","\x28"],"\xBE"=>["\x01","\xFF","\xFF","0"],"\xBF"=>["\x01","\xFF","\xFF","\xC4"],"\xC0"=>["\x01","\xFF","\xFF","\xF0","\x00"],"\xC1"=>["\x01","\xFF","\xFF","\xF0","\x80"],"\xC2"=>["\x01","\xFF","\xFD","\x60"],"\xC3"=>["\x01","\xFF","\xFC","\x40"],"\xC4"=>["\x01","\xFF","\xFF","8"],"\xC5"=>["\x01","\xFF","\xFF","\xC8"],"\xC6"=>["\x01","\xFF","\xFF","\x40"],"\xC7"=>["\x01","\xFF","\xFF","\xEC"],"\xC8"=>["\x01","\xFF","\xFF","\xF1","\x00"],"\xC9"=>["\x01","\xFF","\xFF","\xF1","\x80"],"\xCA"=>["\x01","\xFF","\xFF","\xF2","\x00"],"\xCB"=>["\x01","\xFF","\xFF","\xF7","\x80"],"\xCC"=>["\x01","\xFF","\xFF","\xF7","\xC0"],"\xCD"=>["\x01","\xFF","\xFF","\xF2","\x80"],"\xCE"=>["\x01","\xFF","\xFF","\xE2"],"\xCF"=>["\x01","\xFF","\xFF","\xED"],"\xD0"=>["\x01","\xFF","\xFC","\x80"],"\xD1"=>["\x01","\xFF","\xFE","0"],"\xD2"=>["\x01","\xFF","\xFF","\xF3","\x00"],"\xD3"=>["\x01","\xFF","\xFF","\xF8","\x00"],"\xD4"=>["\x01","\xFF","\xFF","\xF8","\x40"],"\xD5"=>["\x01","\xFF","\xFF","\xF3","\x80"],"\xD6"=>["\x01","\xFF","\xFF","\xF8","\x80"],"\xD7"=>["\x01","\xFF","\xFF","\xE4"],"\xD8"=>["\x01","\xFF","\xFE","\x40"],"\xD9"=>["\x01","\xFF","\xFE","P"],"\xDA"=>["\x01","\xFF","\xFF","\xF4","\x00"],"\xDB"=>["\x01","\xFF","\xFF","\xF4","\x80"],"\xDC"=>["\x01","\xFF","\xFF","\xFF","\xA0"],"\xDD"=>["\x01","\xFF","\xFF","\xF8","\xC0"],"\xDE"=>["\x01","\xFF","\xFF","\xF9","\x00"],"\xDF"=>["\x01","\xFF","\xFF","\xF9","\x40"],"\xE0"=>["\x01","\xFF","\xFD","\x80"],"\xE1"=>["\x01","\xFF","\xFF","\xE6"],"\xE2"=>["\x01","\xFF","\xFD","\xA0"],"\xE3"=>["\x01","\xFF","\xFE","\x60"],"\xE4"=>["\x01","\xFF","\xFF","H"],"\xE5"=>["\x01","\xFF","\xFE","p"],"\xE6"=>["\x01","\xFF","\xFE","\x80"],"\xE7"=>["\x01","\xFF","\xFF","\xCC"],"\xE8"=>["\x01","\xFF","\xFF","P"],"\xE9"=>["\x01","\xFF","\xFF","X"],"\xEA"=>["\x01","\xFF","\xFF","\xEE"],"\xEB"=>["\x01","\xFF","\xFF","\xEF"],"\xEC"=>["\x01","\xFF","\xFF","\xE8"],"\xED"=>["\x01","\xFF","\xFF","\xEA"],"\xEE"=>["\x01","\xFF","\xFF","\xF5","\x00"],"\xEF"=>["\x01","\xFF","\xFF","\xD0"],"\xF0"=>["\x01","\xFF","\xFF","\xF5","\x80"],"\xF1"=>["\x01","\xFF","\xFF","\xF9","\x80"],"\xF2"=>["\x01","\xFF","\xFF","\xF6","\x00"],"\xF3"=>["\x01","\xFF","\xFF","\xF6","\x80"],"\xF4"=>["\x01","\xFF","\xFF","\xF9","\xC0"],"\xF5"=>["\x01","\xFF","\xFF","\xFA","\x00"],"\xF6"=>["\x01","\xFF","\xFF","\xFA","\x40"],"\xF7"=>["\x01","\xFF","\xFF","\xFA","\x80"],"\xF8"=>["\x01","\xFF","\xFF","\xFA","\xC0"],"\xF9"=>["\x01","\xFF","\xFF","\xFF","\xC0"],"\xFA"=>["\x01","\xFF","\xFF","\xFB","\x00"],"\xFB"=>["\x01","\xFF","\xFF","\xFB","\x40"],"\xFC"=>["\x01","\xFF","\xFF","\xFB","\x80"],"\xFD"=>["\x01","\xFF","\xFF","\xFB","\xC0"],"\xFE"=>["\x01","\xFF","\xFF","\xFC","\x00"],"\xFF"=>["\x01","\xFF","\xFF","\xF7","\x00"],],];
<?php declare(strict_types=1);
return[["\x00"=>[93,"0"],"\x01"=>[138,"0"],"\x02"=>[75,"0"],"\x03"=>[91,"0"],"\x04"=>[108,"0"],"\x05"=>[103,"0"],"\x06"=>[114,"0"],"\x07"=>[14,"0"],"\x08"=>[93,"1"],"\x09"=>[138,"1"],"\x0A"=>[75,"1"],"\x0B"=>[91,"1"],"\x0C"=>[108,"1"],"\x0D"=>[103,"1"],"\x0E"=>[114,"1"],"\x0F"=>[14,"1"],"\x10"=>[93,"2"],"\x11"=>[138,"2"],"\x12"=>[75,"2"],"\x13"=>[91,"2"],"\x14"=>[108,"2"],"\x15"=>[103,"2"],"\x16"=>[114,"2"],"\x17"=>[14,"2"],"\x18"=>[93,"a"],"\x19"=>[138,"a"],"\x1A"=>[75,"a"],"\x1B"=>[91,"a"],"\x1C"=>[108,"a"],"\x1D"=>[103,"a"],"\x1E"=>[114,"a"],"\x1F"=>[14,"a"],"\x20"=>[93,"c"],"\x21"=>[138,"c"],"\x22"=>[75,"c"],"\x23"=>[91,"c"],"\x24"=>[108,"c"],"\x25"=>[103,"c"],"\x26"=>[114,"c"],"\x27"=>[14,"c"],"\x28"=>[93,"e"],"\x29"=>[138,"e"],"\x2A"=>[75,"e"],"\x2B"=>[91,"e"],"\x2C"=>[108,"e"],"-"=>[103,"e"],"."=>[114,"e"],"\x2F"=>[14,"e"],[93,"i"],[138,"i"],[75,"i"],[91,"i"],[108,"i"],[103,"i"],[114,"i"],[14,"i"],[93,"o"],[138,"o"],"\x3A"=>[75,"o"],"\x3B"=>[91,"o"],"\x3C"=>[108,"o"],"\x3D"=>[103,"o"],"\x3E"=>[114,"o"],"\x3F"=>[14,"o"],"\x40"=>[93,"s"],"A"=>[138,"s"],"B"=>[75,"s"],"C"=>[91,"s"],"D"=>[108,"s"],"E"=>[103,"s"],"F"=>[114,"s"],"G"=>[14,"s"],"H"=>[93,"t"],"I"=>[138,"t"],"J"=>[75,"t"],"K"=>[91,"t"],"L"=>[108,"t"],"M"=>[103,"t"],"N"=>[114,"t"],"O"=>[14,"t"],"P"=>[94,"\x20"],"Q"=>[76,"\x20"],"R"=>[104,"\x20"],"S"=>[16,"\x20"],"T"=>[94,"\x25"],"U"=>[76,"\x25"],"V"=>[104,"\x25"],"W"=>[16,"\x25"],"X"=>[94,"-"],"Y"=>[76,"-"],"Z"=>[104,"-"],"\x5B"=>[16,"-"],"\x5C"=>[94,"."],"\x5D"=>[76,"."],"\x5E"=>[104,"."],"_"=>[16,"."],"\x60"=>[94,"\x2F"],"a"=>[76,"\x2F"],"b"=>[104,"\x2F"],"c"=>[16,"\x2F"],"d"=>[94,"3"],"e"=>[76,"3"],"f"=>[104,"3"],"g"=>[16,"3"],"h"=>[94,"4"],"i"=>[76,"4"],"j"=>[104,"4"],"k"=>[16,"4"],"l"=>[94,"5"],"m"=>[76,"5"],"n"=>[104,"5"],"o"=>[16,"5"],"p"=>[94,"6"],"q"=>[76,"6"],"r"=>[104,"6"],"s"=>[16,"6"],"t"=>[94,"7"],"u"=>[76,"7"],"v"=>[104,"7"],"w"=>[16,"7"],"x"=>[94,"8"],"y"=>[76,"8"],"z"=>[104,"8"],"\x7B"=>[16,"8"],"\x7C"=>[94,"9"],"\x7D"=>[76,"9"],"~"=>[104,"9"],"\x7F"=>[16,"9"],"\x80"=>[94,"\x3D"],"\x81"=>[76,"\x3D"],"\x82"=>[104,"\x3D"],"\x83"=>[16,"\x3D"],"\x84"=>[94,"A"],"\x85"=>[76,"A"],"\x86"=>[104,"A"],"\x87"=>[16,"A"],"\x88"=>[94,"_"],"\x89"=>[76,"_"],"\x8A"=>[104,"_"],"\x8B"=>[16,"_"],"\x8C"=>[94,"b"],"\x8D"=>[76,"b"],"\x8E"=>[104,"b"],"\x8F"=>[16,"b"],"\x90"=>[94,"d"],"\x91"=>[76,"d"],"\x92"=>[104,"d"],"\x93"=>[16,"d"],"\x94"=>[94,"f"],"\x95"=>[76,"f"],"\x96"=>[104,"f"],"\x97"=>[16,"f"],"\x98"=>[94,"g"],"\x99"=>[76,"g"],"\x9A"=>[104,"g"],"\x9B"=>[16,"g"],"\x9C"=>[94,"h"],"\x9D"=>[76,"h"],"\x9E"=>[104,"h"],"\x9F"=>[16,"h"],"\xA0"=>[94,"l"],"\xA1"=>[76,"l"],"\xA2"=>[104,"l"],"\xA3"=>[16,"l"],"\xA4"=>[94,"m"],"\xA5"=>[76,"m"],"\xA6"=>[104,"m"],"\xA7"=>[16,"m"],"\xA8"=>[94,"n"],"\xA9"=>[76,"n"],"\xAA"=>[104,"n"],"\xAB"=>[16,"n"],"\xAC"=>[94,"p"],"\xAD"=>[76,"p"],"\xAE"=>[104,"p"],"\xAF"=>[16,"p"],"\xB0"=>[94,"r"],"\xB1"=>[76,"r"],"\xB2"=>[104,"r"],"\xB3"=>[16,"r"],"\xB4"=>[94,"u"],"\xB5"=>[76,"u"],"\xB6"=>[104,"u"],"\xB7"=>[16,"u"],"\xB8"=>[77,"\x3A"],"\xB9"=>[18,"\x3A"],"\xBA"=>[77,"B"],"\xBB"=>[18,"B"],"\xBC"=>[77,"C"],"\xBD"=>[18,"C"],"\xBE"=>[77,"D"],"\xBF"=>[18,"D"],"\xC0"=>[77,"E"],"\xC1"=>[18,"E"],"\xC2"=>[77,"F"],"\xC3"=>[18,"F"],"\xC4"=>[77,"G"],"\xC5"=>[18,"G"],"\xC6"=>[77,"H"],"\xC7"=>[18,"H"],"\xC8"=>[77,"I"],"\xC9"=>[18,"I"],"\xCA"=>[77,"J"],"\xCB"=>[18,"J"],"\xCC"=>[77,"K"],"\xCD"=>[18,"K"],"\xCE"=>[77,"L"],"\xCF"=>[18,"L"],"\xD0"=>[77,"M"],"\xD1"=>[18,"M"],"\xD2"=>[77,"N"],"\xD3"=>[18,"N"],"\xD4"=>[77,"O"],"\xD5"=>[18,"O"],"\xD6"=>[77,"P"],"\xD7"=>[18,"P"],"\xD8"=>[77,"Q"],"\xD9"=>[18,"Q"],"\xDA"=>[77,"R"],"\xDB"=>[18,"R"],"\xDC"=>[77,"S"],"\xDD"=>[18,"S"],"\xDE"=>[77,"T"],"\xDF"=>[18,"T"],"\xE0"=>[77,"U"],"\xE1"=>[18,"U"],"\xE2"=>[77,"V"],"\xE3"=>[18,"V"],"\xE4"=>[77,"W"],"\xE5"=>[18,"W"],"\xE6"=>[77,"Y"],"\xE7"=>[18,"Y"],"\xE8"=>[77,"j"],"\xE9"=>[18,"j"],"\xEA"=>[77,"k"],"\xEB"=>[18,"k"],"\xEC"=>[77,"q"],"\xED"=>[18,"q"],"\xEE"=>[77,"v"],"\xEF"=>[18,"v"],"\xF0"=>[77,"w"],"\xF1"=>[18,"w"],"\xF2"=>[77,"x"],"\xF3"=>[18,"x"],"\xF4"=>[77,"y"],"\xF5"=>[18,"y"],"\xF6"=>[77,"z"],"\xF7"=>[18,"z"],"\xF8"=>[0,"\x26"],"\xF9"=>[0,"\x2A"],"\xFA"=>[0,"\x2C"],"\xFB"=>[0,"\x3B"],"\xFC"=>[0,"X"],"\xFD"=>[0,"Z"],"\xFE"=>[78,null],"\xFF"=>[8,null],],8=>["\x00"=>[77,"\x3F0"],"\x01"=>[18,"\x3F0"],"\x02"=>[77,"\x3F1"],"\x03"=>[18,"\x3F1"],"\x04"=>[77,"\x3F2"],"\x05"=>[18,"\x3F2"],"\x06"=>[77,"\x3Fa"],"\x07"=>[18,"\x3Fa"],"\x08"=>[77,"\x3Fc"],"\x09"=>[18,"\x3Fc"],"\x0A"=>[77,"\x3Fe"],"\x0B"=>[18,"\x3Fe"],"\x0C"=>[77,"\x3Fi"],"\x0D"=>[18,"\x3Fi"],"\x0E"=>[77,"\x3Fo"],"\x0F"=>[18,"\x3Fo"],"\x10"=>[77,"\x3Fs"],"\x11"=>[18,"\x3Fs"],"\x12"=>[77,"\x3Ft"],"\x13"=>[18,"\x3Ft"],"\x14"=>[0,"\x3F\x20"],"\x15"=>[0,"\x3F\x25"],"\x16"=>[0,"\x3F-"],"\x17"=>[0,"\x3F."],"\x18"=>[0,"\x3F\x2F"],"\x19"=>[0,"\x3F3"],"\x1A"=>[0,"\x3F4"],"\x1B"=>[0,"\x3F5"],"\x1C"=>[0,"\x3F6"],"\x1D"=>[0,"\x3F7"],"\x1E"=>[0,"\x3F8"],"\x1F"=>[0,"\x3F9"],"\x20"=>[0,"\x3F\x3D"],"\x21"=>[0,"\x3FA"],"\x22"=>[0,"\x3F_"],"\x23"=>[0,"\x3Fb"],"\x24"=>[0,"\x3Fd"],"\x25"=>[0,"\x3Ff"],"\x26"=>[0,"\x3Fg"],"\x27"=>[0,"\x3Fh"],"\x28"=>[0,"\x3Fl"],"\x29"=>[0,"\x3Fm"],"\x2A"=>[0,"\x3Fn"],"\x2B"=>[0,"\x3Fp"],"\x2C"=>[0,"\x3Fr"],"-"=>[0,"\x3Fu"],"."=>[100,"\x3F"],"\x2F"=>[110,"\x3F"],[111,"\x3F"],[115,"\x3F"],[116,"\x3F"],[118,"\x3F"],[119,"\x3F"],[122,"\x3F"],[123,"\x3F"],[125,"\x3F"],[126,"\x3F"],[129,"\x3F"],"\x3A"=>[143,"\x3F"],"\x3B"=>[148,"\x3F"],"\x3C"=>[151,"\x3F"],"\x3D"=>[153,"\x3F"],"\x3E"=>[83,"\x3F"],"\x3F"=>[10,"\x3F"],"\x40"=>[0,"\x270"],"A"=>[0,"\x271"],"B"=>[0,"\x272"],"C"=>[0,"\x27a"],"D"=>[0,"\x27c"],"E"=>[0,"\x27e"],"F"=>[0,"\x27i"],"G"=>[0,"\x27o"],"H"=>[0,"\x27s"],"I"=>[0,"\x27t"],"J"=>[73,"\x27"],"K"=>[88,"\x27"],"L"=>[89,"\x27"],"M"=>[96,"\x27"],"N"=>[97,"\x27"],"O"=>[99,"\x27"],"P"=>[106,"\x27"],"Q"=>[136,"\x27"],"R"=>[139,"\x27"],"S"=>[141,"\x27"],"T"=>[145,"\x27"],"U"=>[147,"\x27"],"V"=>[149,"\x27"],"W"=>[101,"\x27"],"X"=>[112,"\x27"],"Y"=>[117,"\x27"],"Z"=>[120,"\x27"],"\x5B"=>[124,"\x27"],"\x5C"=>[127,"\x27"],"\x5D"=>[144,"\x27"],"\x5E"=>[152,"\x27"],"_"=>[11,"\x27"],"\x60"=>[0,"\x2B0"],"a"=>[0,"\x2B1"],"b"=>[0,"\x2B2"],"c"=>[0,"\x2Ba"],"d"=>[0,"\x2Bc"],"e"=>[0,"\x2Be"],"f"=>[0,"\x2Bi"],"g"=>[0,"\x2Bo"],"h"=>[0,"\x2Bs"],"i"=>[0,"\x2Bt"],"j"=>[73,"\x2B"],"k"=>[88,"\x2B"],"l"=>[89,"\x2B"],"m"=>[96,"\x2B"],"n"=>[97,"\x2B"],"o"=>[99,"\x2B"],"p"=>[106,"\x2B"],"q"=>[136,"\x2B"],"r"=>[139,"\x2B"],"s"=>[141,"\x2B"],"t"=>[145,"\x2B"],"u"=>[147,"\x2B"],"v"=>[149,"\x2B"],"w"=>[101,"\x2B"],"x"=>[112,"\x2B"],"y"=>[117,"\x2B"],"z"=>[120,"\x2B"],"\x7B"=>[124,"\x2B"],"\x7C"=>[127,"\x2B"],"\x7D"=>[144,"\x2B"],"~"=>[152,"\x2B"],"\x7F"=>[11,"\x2B"],"\x80"=>[0,"\x7C0"],"\x81"=>[0,"\x7C1"],"\x82"=>[0,"\x7C2"],"\x83"=>[0,"\x7Ca"],"\x84"=>[0,"\x7Cc"],"\x85"=>[0,"\x7Ce"],"\x86"=>[0,"\x7Ci"],"\x87"=>[0,"\x7Co"],"\x88"=>[0,"\x7Cs"],"\x89"=>[0,"\x7Ct"],"\x8A"=>[73,"\x7C"],"\x8B"=>[88,"\x7C"],"\x8C"=>[89,"\x7C"],"\x8D"=>[96,"\x7C"],"\x8E"=>[97,"\x7C"],"\x8F"=>[99,"\x7C"],"\x90"=>[106,"\x7C"],"\x91"=>[136,"\x7C"],"\x92"=>[139,"\x7C"],"\x93"=>[141,"\x7C"],"\x94"=>[145,"\x7C"],"\x95"=>[147,"\x7C"],"\x96"=>[149,"\x7C"],"\x97"=>[101,"\x7C"],"\x98"=>[112,"\x7C"],"\x99"=>[117,"\x7C"],"\x9A"=>[120,"\x7C"],"\x9B"=>[124,"\x7C"],"\x9C"=>[127,"\x7C"],"\x9D"=>[144,"\x7C"],"\x9E"=>[152,"\x7C"],"\x9F"=>[11,"\x7C"],"\xA0"=>[92,"\x23"],"\xA1"=>[95,"\x23"],"\xA2"=>[137,"\x23"],"\xA3"=>[142,"\x23"],"\xA4"=>[150,"\x23"],"\xA5"=>[74,"\x23"],"\xA6"=>[90,"\x23"],"\xA7"=>[98,"\x23"],"\xA8"=>[107,"\x23"],"\xA9"=>[140,"\x23"],"\xAA"=>[146,"\x23"],"\xAB"=>[102,"\x23"],"\xAC"=>[113,"\x23"],"\xAD"=>[121,"\x23"],"\xAE"=>[128,"\x23"],"\xAF"=>[12,"\x23"],"\xB0"=>[92,"\x3E"],"\xB1"=>[95,"\x3E"],"\xB2"=>[137,"\x3E"],"\xB3"=>[142,"\x3E"],"\xB4"=>[150,"\x3E"],"\xB5"=>[74,"\x3E"],"\xB6"=>[90,"\x3E"],"\xB7"=>[98,"\x3E"],"\xB8"=>[107,"\x3E"],"\xB9"=>[140,"\x3E"],"\xBA"=>[146,"\x3E"],"\xBB"=>[102,"\x3E"],"\xBC"=>[113,"\x3E"],"\xBD"=>[121,"\x3E"],"\xBE"=>[128,"\x3E"],"\xBF"=>[12,"\x3E"],"\xC0"=>[93,"\x00"],"\xC1"=>[138,"\x00"],"\xC2"=>[75,"\x00"],"\xC3"=>[91,"\x00"],"\xC4"=>[108,"\x00"],"\xC5"=>[103,"\x00"],"\xC6"=>[114,"\x00"],"\xC7"=>[14,"\x00"],"\xC8"=>[93,"\x24"],"\xC9"=>[138,"\x24"],"\xCA"=>[75,"\x24"],"\xCB"=>[91,"\x24"],"\xCC"=>[108,"\x24"],"\xCD"=>[103,"\x24"],"\xCE"=>[114,"\x24"],"\xCF"=>[14,"\x24"],"\xD0"=>[93,"\x40"],"\xD1"=>[138,"\x40"],"\xD2"=>[75,"\x40"],"\xD3"=>[91,"\x40"],"\xD4"=>[108,"\x40"],"\xD5"=>[103,"\x40"],"\xD6"=>[114,"\x40"],"\xD7"=>[14,"\x40"],"\xD8"=>[93,"\x5B"],"\xD9"=>[138,"\x5B"],"\xDA"=>[75,"\x5B"],"\xDB"=>[91,"\x5B"],"\xDC"=>[108,"\x5B"],"\xDD"=>[103,"\x5B"],"\xDE"=>[114,"\x5B"],"\xDF"=>[14,"\x5B"],"\xE0"=>[93,"\x5D"],"\xE1"=>[138,"\x5D"],"\xE2"=>[75,"\x5D"],"\xE3"=>[91,"\x5D"],"\xE4"=>[108,"\x5D"],"\xE5"=>[103,"\x5D"],"\xE6"=>[114,"\x5D"],"\xE7"=>[14,"\x5D"],"\xE8"=>[93,"~"],"\xE9"=>[138,"~"],"\xEA"=>[75,"~"],"\xEB"=>[91,"~"],"\xEC"=>[108,"~"],"\xED"=>[103,"~"],"\xEE"=>[114,"~"],"\xEF"=>[14,"~"],"\xF0"=>[94,"\x5E"],"\xF1"=>[76,"\x5E"],"\xF2"=>[104,"\x5E"],"\xF3"=>[16,"\x5E"],"\xF4"=>[94,"\x7D"],"\xF5"=>[76,"\x7D"],"\xF6"=>[104,"\x7D"],"\xF7"=>[16,"\x7D"],"\xF8"=>[77,"\x3C"],"\xF9"=>[18,"\x3C"],"\xFA"=>[77,"\x60"],"\xFB"=>[18,"\x60"],"\xFC"=>[77,"\x7B"],"\xFD"=>[18,"\x7B"],"\xFE"=>[131,null],"\xFF"=>[20,null],],["\x00"=>[0,"\x210"],"\x01"=>[0,"\x211"],"\x02"=>[0,"\x212"],"\x03"=>[0,"\x21a"],"\x04"=>[0,"\x21c"],"\x05"=>[0,"\x21e"],"\x06"=>[0,"\x21i"],"\x07"=>[0,"\x21o"],"\x08"=>[0,"\x21s"],"\x09"=>[0,"\x21t"],"\x0A"=>[73,"\x21"],"\x0B"=>[88,"\x21"],"\x0C"=>[89,"\x21"],"\x0D"=>[96,"\x21"],"\x0E"=>[97,"\x21"],"\x0F"=>[99,"\x21"],"\x10"=>[106,"\x21"],"\x11"=>[136,"\x21"],"\x12"=>[139,"\x21"],"\x13"=>[141,"\x21"],"\x14"=>[145,"\x21"],"\x15"=>[147,"\x21"],"\x16"=>[149,"\x21"],"\x17"=>[101,"\x21"],"\x18"=>[112,"\x21"],"\x19"=>[117,"\x21"],"\x1A"=>[120,"\x21"],"\x1B"=>[124,"\x21"],"\x1C"=>[127,"\x21"],"\x1D"=>[144,"\x21"],"\x1E"=>[152,"\x21"],"\x1F"=>[11,"\x21"],"\x20"=>[0,"\x220"],"\x21"=>[0,"\x221"],"\x22"=>[0,"\x222"],"\x23"=>[0,"\x22a"],"\x24"=>[0,"\x22c"],"\x25"=>[0,"\x22e"],"\x26"=>[0,"\x22i"],"\x27"=>[0,"\x22o"],"\x28"=>[0,"\x22s"],"\x29"=>[0,"\x22t"],"\x2A"=>[73,"\x22"],"\x2B"=>[88,"\x22"],"\x2C"=>[89,"\x22"],"-"=>[96,"\x22"],"."=>[97,"\x22"],"\x2F"=>[99,"\x22"],[106,"\x22"],[136,"\x22"],[139,"\x22"],[141,"\x22"],[145,"\x22"],[147,"\x22"],[149,"\x22"],[101,"\x22"],[112,"\x22"],[117,"\x22"],"\x3A"=>[120,"\x22"],"\x3B"=>[124,"\x22"],"\x3C"=>[127,"\x22"],"\x3D"=>[144,"\x22"],"\x3E"=>[152,"\x22"],"\x3F"=>[11,"\x22"],"\x40"=>[0,"\x280"],"A"=>[0,"\x281"],"B"=>[0,"\x282"],"C"=>[0,"\x28a"],"D"=>[0,"\x28c"],"E"=>[0,"\x28e"],"F"=>[0,"\x28i"],"G"=>[0,"\x28o"],"H"=>[0,"\x28s"],"I"=>[0,"\x28t"],"J"=>[73,"\x28"],"K"=>[88,"\x28"],"L"=>[89,"\x28"],"M"=>[96,"\x28"],"N"=>[97,"\x28"],"O"=>[99,"\x28"],"P"=>[106,"\x28"],"Q"=>[136,"\x28"],"R"=>[139,"\x28"],"S"=>[141,"\x28"],"T"=>[145,"\x28"],"U"=>[147,"\x28"],"V"=>[149,"\x28"],"W"=>[101,"\x28"],"X"=>[112,"\x28"],"Y"=>[117,"\x28"],"Z"=>[120,"\x28"],"\x5B"=>[124,"\x28"],"\x5C"=>[127,"\x28"],"\x5D"=>[144,"\x28"],"\x5E"=>[152,"\x28"],"_"=>[11,"\x28"],"\x60"=>[0,"\x290"],"a"=>[0,"\x291"],"b"=>[0,"\x292"],"c"=>[0,"\x29a"],"d"=>[0,"\x29c"],"e"=>[0,"\x29e"],"f"=>[0,"\x29i"],"g"=>[0,"\x29o"],"h"=>[0,"\x29s"],"i"=>[0,"\x29t"],"j"=>[73,"\x29"],"k"=>[88,"\x29"],"l"=>[89,"\x29"],"m"=>[96,"\x29"],"n"=>[97,"\x29"],"o"=>[99,"\x29"],"p"=>[106,"\x29"],"q"=>[136,"\x29"],"r"=>[139,"\x29"],"s"=>[141,"\x29"],"t"=>[145,"\x29"],"u"=>[147,"\x29"],"v"=>[149,"\x29"],"w"=>[101,"\x29"],"x"=>[112,"\x29"],"y"=>[117,"\x29"],"z"=>[120,"\x29"],"\x7B"=>[124,"\x29"],"\x7C"=>[127,"\x29"],"\x7D"=>[144,"\x29"],"~"=>[152,"\x29"],"\x7F"=>[11,"\x29"],"\x80"=>[0,"\x3F0"],"\x81"=>[0,"\x3F1"],"\x82"=>[0,"\x3F2"],"\x83"=>[0,"\x3Fa"],"\x84"=>[0,"\x3Fc"],"\x85"=>[0,"\x3Fe"],"\x86"=>[0,"\x3Fi"],"\x87"=>[0,"\x3Fo"],"\x88"=>[0,"\x3Fs"],"\x89"=>[0,"\x3Ft"],"\x8A"=>[73,"\x3F"],"\x8B"=>[88,"\x3F"],"\x8C"=>[89,"\x3F"],"\x8D"=>[96,"\x3F"],"\x8E"=>[97,"\x3F"],"\x8F"=>[99,"\x3F"],"\x90"=>[106,"\x3F"],"\x91"=>[136,"\x3F"],"\x92"=>[139,"\x3F"],"\x93"=>[141,"\x3F"],"\x94"=>[145,"\x3F"],"\x95"=>[147,"\x3F"],"\x96"=>[149,"\x3F"],"\x97"=>[101,"\x3F"],"\x98"=>[112,"\x3F"],"\x99"=>[117,"\x3F"],"\x9A"=>[120,"\x3F"],"\x9B"=>[124,"\x3F"],"\x9C"=>[127,"\x3F"],"\x9D"=>[144,"\x3F"],"\x9E"=>[152,"\x3F"],"\x9F"=>[11,"\x3F"],"\xA0"=>[92,"\x27"],"\xA1"=>[95,"\x27"],"\xA2"=>[137,"\x27"],"\xA3"=>[142,"\x27"],"\xA4"=>[150,"\x27"],"\xA5"=>[74,"\x27"],"\xA6"=>[90,"\x27"],"\xA7"=>[98,"\x27"],"\xA8"=>[107,"\x27"],"\xA9"=>[140,"\x27"],"\xAA"=>[146,"\x27"],"\xAB"=>[102,"\x27"],"\xAC"=>[113,"\x27"],"\xAD"=>[121,"\x27"],"\xAE"=>[128,"\x27"],"\xAF"=>[12,"\x27"],"\xB0"=>[92,"\x2B"],"\xB1"=>[95,"\x2B"],"\xB2"=>[137,"\x2B"],"\xB3"=>[142,"\x2B"],"\xB4"=>[150,"\x2B"],"\xB5"=>[74,"\x2B"],"\xB6"=>[90,"\x2B"],"\xB7"=>[98,"\x2B"],"\xB8"=>[107,"\x2B"],"\xB9"=>[140,"\x2B"],"\xBA"=>[146,"\x2B"],"\xBB"=>[102,"\x2B"],"\xBC"=>[113,"\x2B"],"\xBD"=>[121,"\x2B"],"\xBE"=>[128,"\x2B"],"\xBF"=>[12,"\x2B"],"\xC0"=>[92,"\x7C"],"\xC1"=>[95,"\x7C"],"\xC2"=>[137,"\x7C"],"\xC3"=>[142,"\x7C"],"\xC4"=>[150,"\x7C"],"\xC5"=>[74,"\x7C"],"\xC6"=>[90,"\x7C"],"\xC7"=>[98,"\x7C"],"\xC8"=>[107,"\x7C"],"\xC9"=>[140,"\x7C"],"\xCA"=>[146,"\x7C"],"\xCB"=>[102,"\x7C"],"\xCC"=>[113,"\x7C"],"\xCD"=>[121,"\x7C"],"\xCE"=>[128,"\x7C"],"\xCF"=>[12,"\x7C"],"\xD0"=>[93,"\x23"],"\xD1"=>[138,"\x23"],"\xD2"=>[75,"\x23"],"\xD3"=>[91,"\x23"],"\xD4"=>[108,"\x23"],"\xD5"=>[103,"\x23"],"\xD6"=>[114,"\x23"],"\xD7"=>[14,"\x23"],"\xD8"=>[93,"\x3E"],"\xD9"=>[138,"\x3E"],"\xDA"=>[75,"\x3E"],"\xDB"=>[91,"\x3E"],"\xDC"=>[108,"\x3E"],"\xDD"=>[103,"\x3E"],"\xDE"=>[114,"\x3E"],"\xDF"=>[14,"\x3E"],"\xE0"=>[94,"\x00"],"\xE1"=>[76,"\x00"],"\xE2"=>[104,"\x00"],"\xE3"=>[16,"\x00"],"\xE4"=>[94,"\x24"],"\xE5"=>[76,"\x24"],"\xE6"=>[104,"\x24"],"\xE7"=>[16,"\x24"],"\xE8"=>[94,"\x40"],"\xE9"=>[76,"\x40"],"\xEA"=>[104,"\x40"],"\xEB"=>[16,"\x40"],"\xEC"=>[94,"\x5B"],"\xED"=>[76,"\x5B"],"\xEE"=>[104,"\x5B"],"\xEF"=>[16,"\x5B"],"\xF0"=>[94,"\x5D"],"\xF1"=>[76,"\x5D"],"\xF2"=>[104,"\x5D"],"\xF3"=>[16,"\x5D"],"\xF4"=>[94,"~"],"\xF5"=>[76,"~"],"\xF6"=>[104,"~"],"\xF7"=>[16,"~"],"\xF8"=>[77,"\x5E"],"\xF9"=>[18,"\x5E"],"\xFA"=>[77,"\x7D"],"\xFB"=>[18,"\x7D"],"\xFC"=>[0,"\x3C"],"\xFD"=>[0,"\x60"],"\xFE"=>[0,"\x7B"],"\xFF"=>[21,null],],["\x00"=>[77,"X0"],"\x01"=>[18,"X0"],"\x02"=>[77,"X1"],"\x03"=>[18,"X1"],"\x04"=>[77,"X2"],"\x05"=>[18,"X2"],"\x06"=>[77,"Xa"],"\x07"=>[18,"Xa"],"\x08"=>[77,"Xc"],"\x09"=>[18,"Xc"],"\x0A"=>[77,"Xe"],"\x0B"=>[18,"Xe"],"\x0C"=>[77,"Xi"],"\x0D"=>[18,"Xi"],"\x0E"=>[77,"Xo"],"\x0F"=>[18,"Xo"],"\x10"=>[77,"Xs"],"\x11"=>[18,"Xs"],"\x12"=>[77,"Xt"],"\x13"=>[18,"Xt"],"\x14"=>[0,"X\x20"],"\x15"=>[0,"X\x25"],"\x16"=>[0,"X-"],"\x17"=>[0,"X."],"\x18"=>[0,"X\x2F"],"\x19"=>[0,"X3"],"\x1A"=>[0,"X4"],"\x1B"=>[0,"X5"],"\x1C"=>[0,"X6"],"\x1D"=>[0,"X7"],"\x1E"=>[0,"X8"],"\x1F"=>[0,"X9"],"\x20"=>[0,"X\x3D"],"\x21"=>[0,"XA"],"\x22"=>[0,"X_"],"\x23"=>[0,"Xb"],"\x24"=>[0,"Xd"],"\x25"=>[0,"Xf"],"\x26"=>[0,"Xg"],"\x27"=>[0,"Xh"],"\x28"=>[0,"Xl"],"\x29"=>[0,"Xm"],"\x2A"=>[0,"Xn"],"\x2B"=>[0,"Xp"],"\x2C"=>[0,"Xr"],"-"=>[0,"Xu"],"."=>[100,"X"],"\x2F"=>[110,"X"],[111,"X"],[115,"X"],[116,"X"],[118,"X"],[119,"X"],[122,"X"],[123,"X"],[125,"X"],[126,"X"],[129,"X"],"\x3A"=>[143,"X"],"\x3B"=>[148,"X"],"\x3C"=>[151,"X"],"\x3D"=>[153,"X"],"\x3E"=>[83,"X"],"\x3F"=>[10,"X"],"\x40"=>[77,"Z0"],"A"=>[18,"Z0"],"B"=>[77,"Z1"],"C"=>[18,"Z1"],"D"=>[77,"Z2"],"E"=>[18,"Z2"],"F"=>[77,"Za"],"G"=>[18,"Za"],"H"=>[77,"Zc"],"I"=>[18,"Zc"],"J"=>[77,"Ze"],"K"=>[18,"Ze"],"L"=>[77,"Zi"],"M"=>[18,"Zi"],"N"=>[77,"Zo"],"O"=>[18,"Zo"],"P"=>[77,"Zs"],"Q"=>[18,"Zs"],"R"=>[77,"Zt"],"S"=>[18,"Zt"],"T"=>[0,"Z\x20"],"U"=>[0,"Z\x25"],"V"=>[0,"Z-"],"W"=>[0,"Z."],"X"=>[0,"Z\x2F"],"Y"=>[0,"Z3"],"Z"=>[0,"Z4"],"\x5B"=>[0,"Z5"],"\x5C"=>[0,"Z6"],"\x5D"=>[0,"Z7"],"\x5E"=>[0,"Z8"],"_"=>[0,"Z9"],"\x60"=>[0,"Z\x3D"],"a"=>[0,"ZA"],"b"=>[0,"Z_"],"c"=>[0,"Zb"],"d"=>[0,"Zd"],"e"=>[0,"Zf"],"f"=>[0,"Zg"],"g"=>[0,"Zh"],"h"=>[0,"Zl"],"i"=>[0,"Zm"],"j"=>[0,"Zn"],"k"=>[0,"Zp"],"l"=>[0,"Zr"],"m"=>[0,"Zu"],"n"=>[100,"Z"],"o"=>[110,"Z"],"p"=>[111,"Z"],"q"=>[115,"Z"],"r"=>[116,"Z"],"s"=>[118,"Z"],"t"=>[119,"Z"],"u"=>[122,"Z"],"v"=>[123,"Z"],"w"=>[125,"Z"],"x"=>[126,"Z"],"y"=>[129,"Z"],"z"=>[143,"Z"],"\x7B"=>[148,"Z"],"\x7C"=>[151,"Z"],"\x7D"=>[153,"Z"],"~"=>[83,"Z"],"\x7F"=>[10,"Z"],"\x80"=>[92,"\x21"],"\x81"=>[95,"\x21"],"\x82"=>[137,"\x21"],"\x83"=>[142,"\x21"],"\x84"=>[150,"\x21"],"\x85"=>[74,"\x21"],"\x86"=>[90,"\x21"],"\x87"=>[98,"\x21"],"\x88"=>[107,"\x21"],"\x89"=>[140,"\x21"],"\x8A"=>[146,"\x21"],"\x8B"=>[102,"\x21"],"\x8C"=>[113,"\x21"],"\x8D"=>[121,"\x21"],"\x8E"=>[128,"\x21"],"\x8F"=>[12,"\x21"],"\x90"=>[92,"\x22"],"\x91"=>[95,"\x22"],"\x92"=>[137,"\x22"],"\x93"=>[142,"\x22"],"\x94"=>[150,"\x22"],"\x95"=>[74,"\x22"],"\x96"=>[90,"\x22"],"\x97"=>[98,"\x22"],"\x98"=>[107,"\x22"],"\x99"=>[140,"\x22"],"\x9A"=>[146,"\x22"],"\x9B"=>[102,"\x22"],"\x9C"=>[113,"\x22"],"\x9D"=>[121,"\x22"],"\x9E"=>[128,"\x22"],"\x9F"=>[12,"\x22"],"\xA0"=>[92,"\x28"],"\xA1"=>[95,"\x28"],"\xA2"=>[137,"\x28"],"\xA3"=>[142,"\x28"],"\xA4"=>[150,"\x28"],"\xA5"=>[74,"\x28"],"\xA6"=>[90,"\x28"],"\xA7"=>[98,"\x28"],"\xA8"=>[107,"\x28"],"\xA9"=>[140,"\x28"],"\xAA"=>[146,"\x28"],"\xAB"=>[102,"\x28"],"\xAC"=>[113,"\x28"],"\xAD"=>[121,"\x28"],"\xAE"=>[128,"\x28"],"\xAF"=>[12,"\x28"],"\xB0"=>[92,"\x29"],"\xB1"=>[95,"\x29"],"\xB2"=>[137,"\x29"],"\xB3"=>[142,"\x29"],"\xB4"=>[150,"\x29"],"\xB5"=>[74,"\x29"],"\xB6"=>[90,"\x29"],"\xB7"=>[98,"\x29"],"\xB8"=>[107,"\x29"],"\xB9"=>[140,"\x29"],"\xBA"=>[146,"\x29"],"\xBB"=>[102,"\x29"],"\xBC"=>[113,"\x29"],"\xBD"=>[121,"\x29"],"\xBE"=>[128,"\x29"],"\xBF"=>[12,"\x29"],"\xC0"=>[92,"\x3F"],"\xC1"=>[95,"\x3F"],"\xC2"=>[137,"\x3F"],"\xC3"=>[142,"\x3F"],"\xC4"=>[150,"\x3F"],"\xC5"=>[74,"\x3F"],"\xC6"=>[90,"\x3F"],"\xC7"=>[98,"\x3F"],"\xC8"=>[107,"\x3F"],"\xC9"=>[140,"\x3F"],"\xCA"=>[146,"\x3F"],"\xCB"=>[102,"\x3F"],"\xCC"=>[113,"\x3F"],"\xCD"=>[121,"\x3F"],"\xCE"=>[128,"\x3F"],"\xCF"=>[12,"\x3F"],"\xD0"=>[93,"\x27"],"\xD1"=>[138,"\x27"],"\xD2"=>[75,"\x27"],"\xD3"=>[91,"\x27"],"\xD4"=>[108,"\x27"],"\xD5"=>[103,"\x27"],"\xD6"=>[114,"\x27"],"\xD7"=>[14,"\x27"],"\xD8"=>[93,"\x2B"],"\xD9"=>[138,"\x2B"],"\xDA"=>[75,"\x2B"],"\xDB"=>[91,"\x2B"],"\xDC"=>[108,"\x2B"],"\xDD"=>[103,"\x2B"],"\xDE"=>[114,"\x2B"],"\xDF"=>[14,"\x2B"],"\xE0"=>[93,"\x7C"],"\xE1"=>[138,"\x7C"],"\xE2"=>[75,"\x7C"],"\xE3"=>[91,"\x7C"],"\xE4"=>[108,"\x7C"],"\xE5"=>[103,"\x7C"],"\xE6"=>[114,"\x7C"],"\xE7"=>[14,"\x7C"],"\xE8"=>[94,"\x23"],"\xE9"=>[76,"\x23"],"\xEA"=>[104,"\x23"],"\xEB"=>[16,"\x23"],"\xEC"=>[94,"\x3E"],"\xED"=>[76,"\x3E"],"\xEE"=>[104,"\x3E"],"\xEF"=>[16,"\x3E"],"\xF0"=>[77,"\x00"],"\xF1"=>[18,"\x00"],"\xF2"=>[77,"\x24"],"\xF3"=>[18,"\x24"],"\xF4"=>[77,"\x40"],"\xF5"=>[18,"\x40"],"\xF6"=>[77,"\x5B"],"\xF7"=>[18,"\x5B"],"\xF8"=>[77,"\x5D"],"\xF9"=>[18,"\x5D"],"\xFA"=>[77,"~"],"\xFB"=>[18,"~"],"\xFC"=>[0,"\x5E"],"\xFD"=>[0,"\x7D"],"\xFE"=>[105,null],"\xFF"=>[22,null],],["\x00"=>[0,"\x260"],"\x01"=>[0,"\x261"],"\x02"=>[0,"\x262"],"\x03"=>[0,"\x26a"],"\x04"=>[0,"\x26c"],"\x05"=>[0,"\x26e"],"\x06"=>[0,"\x26i"],"\x07"=>[0,"\x26o"],"\x08"=>[0,"\x26s"],"\x09"=>[0,"\x26t"],"\x0A"=>[73,"\x26"],"\x0B"=>[88,"\x26"],"\x0C"=>[89,"\x26"],"\x0D"=>[96,"\x26"],"\x0E"=>[97,"\x26"],"\x0F"=>[99,"\x26"],"\x10"=>[106,"\x26"],"\x11"=>[136,"\x26"],"\x12"=>[139,"\x26"],"\x13"=>[141,"\x26"],"\x14"=>[145,"\x26"],"\x15"=>[147,"\x26"],"\x16"=>[149,"\x26"],"\x17"=>[101,"\x26"],"\x18"=>[112,"\x26"],"\x19"=>[117,"\x26"],"\x1A"=>[120,"\x26"],"\x1B"=>[124,"\x26"],"\x1C"=>[127,"\x26"],"\x1D"=>[144,"\x26"],"\x1E"=>[152,"\x26"],"\x1F"=>[11,"\x26"],"\x20"=>[0,"\x2A0"],"\x21"=>[0,"\x2A1"],"\x22"=>[0,"\x2A2"],"\x23"=>[0,"\x2Aa"],"\x24"=>[0,"\x2Ac"],"\x25"=>[0,"\x2Ae"],"\x26"=>[0,"\x2Ai"],"\x27"=>[0,"\x2Ao"],"\x28"=>[0,"\x2As"],"\x29"=>[0,"\x2At"],"\x2A"=>[73,"\x2A"],"\x2B"=>[88,"\x2A"],"\x2C"=>[89,"\x2A"],"-"=>[96,"\x2A"],"."=>[97,"\x2A"],"\x2F"=>[99,"\x2A"],[106,"\x2A"],[136,"\x2A"],[139,"\x2A"],[141,"\x2A"],[145,"\x2A"],[147,"\x2A"],[149,"\x2A"],[101,"\x2A"],[112,"\x2A"],[117,"\x2A"],"\x3A"=>[120,"\x2A"],"\x3B"=>[124,"\x2A"],"\x3C"=>[127,"\x2A"],"\x3D"=>[144,"\x2A"],"\x3E"=>[152,"\x2A"],"\x3F"=>[11,"\x2A"],"\x40"=>[0,"\x2C0"],"A"=>[0,"\x2C1"],"B"=>[0,"\x2C2"],"C"=>[0,"\x2Ca"],"D"=>[0,"\x2Cc"],"E"=>[0,"\x2Ce"],"F"=>[0,"\x2Ci"],"G"=>[0,"\x2Co"],"H"=>[0,"\x2Cs"],"I"=>[0,"\x2Ct"],"J"=>[73,"\x2C"],"K"=>[88,"\x2C"],"L"=>[89,"\x2C"],"M"=>[96,"\x2C"],"N"=>[97,"\x2C"],"O"=>[99,"\x2C"],"P"=>[106,"\x2C"],"Q"=>[136,"\x2C"],"R"=>[139,"\x2C"],"S"=>[141,"\x2C"],"T"=>[145,"\x2C"],"U"=>[147,"\x2C"],"V"=>[149,"\x2C"],"W"=>[101,"\x2C"],"X"=>[112,"\x2C"],"Y"=>[117,"\x2C"],"Z"=>[120,"\x2C"],"\x5B"=>[124,"\x2C"],"\x5C"=>[127,"\x2C"],"\x5D"=>[144,"\x2C"],"\x5E"=>[152,"\x2C"],"_"=>[11,"\x2C"],"\x60"=>[0,"\x3B0"],"a"=>[0,"\x3B1"],"b"=>[0,"\x3B2"],"c"=>[0,"\x3Ba"],"d"=>[0,"\x3Bc"],"e"=>[0,"\x3Be"],"f"=>[0,"\x3Bi"],"g"=>[0,"\x3Bo"],"h"=>[0,"\x3Bs"],"i"=>[0,"\x3Bt"],"j"=>[73,"\x3B"],"k"=>[88,"\x3B"],"l"=>[89,"\x3B"],"m"=>[96,"\x3B"],"n"=>[97,"\x3B"],"o"=>[99,"\x3B"],"p"=>[106,"\x3B"],"q"=>[136,"\x3B"],"r"=>[139,"\x3B"],"s"=>[141,"\x3B"],"t"=>[145,"\x3B"],"u"=>[147,"\x3B"],"v"=>[149,"\x3B"],"w"=>[101,"\x3B"],"x"=>[112,"\x3B"],"y"=>[117,"\x3B"],"z"=>[120,"\x3B"],"\x7B"=>[124,"\x3B"],"\x7C"=>[127,"\x3B"],"\x7D"=>[144,"\x3B"],"~"=>[152,"\x3B"],"\x7F"=>[11,"\x3B"],"\x80"=>[0,"X0"],"\x81"=>[0,"X1"],"\x82"=>[0,"X2"],"\x83"=>[0,"Xa"],"\x84"=>[0,"Xc"],"\x85"=>[0,"Xe"],"\x86"=>[0,"Xi"],"\x87"=>[0,"Xo"],"\x88"=>[0,"Xs"],"\x89"=>[0,"Xt"],"\x8A"=>[73,"X"],"\x8B"=>[88,"X"],"\x8C"=>[89,"X"],"\x8D"=>[96,"X"],"\x8E"=>[97,"X"],"\x8F"=>[99,"X"],"\x90"=>[106,"X"],"\x91"=>[136,"X"],"\x92"=>[139,"X"],"\x93"=>[141,"X"],"\x94"=>[145,"X"],"\x95"=>[147,"X"],"\x96"=>[149,"X"],"\x97"=>[101,"X"],"\x98"=>[112,"X"],"\x99"=>[117,"X"],"\x9A"=>[120,"X"],"\x9B"=>[124,"X"],"\x9C"=>[127,"X"],"\x9D"=>[144,"X"],"\x9E"=>[152,"X"],"\x9F"=>[11,"X"],"\xA0"=>[0,"Z0"],"\xA1"=>[0,"Z1"],"\xA2"=>[0,"Z2"],"\xA3"=>[0,"Za"],"\xA4"=>[0,"Zc"],"\xA5"=>[0,"Ze"],"\xA6"=>[0,"Zi"],"\xA7"=>[0,"Zo"],"\xA8"=>[0,"Zs"],"\xA9"=>[0,"Zt"],"\xAA"=>[73,"Z"],"\xAB"=>[88,"Z"],"\xAC"=>[89,"Z"],"\xAD"=>[96,"Z"],"\xAE"=>[97,"Z"],"\xAF"=>[99,"Z"],"\xB0"=>[106,"Z"],"\xB1"=>[136,"Z"],"\xB2"=>[139,"Z"],"\xB3"=>[141,"Z"],"\xB4"=>[145,"Z"],"\xB5"=>[147,"Z"],"\xB6"=>[149,"Z"],"\xB7"=>[101,"Z"],"\xB8"=>[112,"Z"],"\xB9"=>[117,"Z"],"\xBA"=>[120,"Z"],"\xBB"=>[124,"Z"],"\xBC"=>[127,"Z"],"\xBD"=>[144,"Z"],"\xBE"=>[152,"Z"],"\xBF"=>[11,"Z"],"\xC0"=>[93,"\x21"],"\xC1"=>[138,"\x21"],"\xC2"=>[75,"\x21"],"\xC3"=>[91,"\x21"],"\xC4"=>[108,"\x21"],"\xC5"=>[103,"\x21"],"\xC6"=>[114,"\x21"],"\xC7"=>[14,"\x21"],"\xC8"=>[93,"\x22"],"\xC9"=>[138,"\x22"],"\xCA"=>[75,"\x22"],"\xCB"=>[91,"\x22"],"\xCC"=>[108,"\x22"],"\xCD"=>[103,"\x22"],"\xCE"=>[114,"\x22"],"\xCF"=>[14,"\x22"],"\xD0"=>[93,"\x28"],"\xD1"=>[138,"\x28"],"\xD2"=>[75,"\x28"],"\xD3"=>[91,"\x28"],"\xD4"=>[108,"\x28"],"\xD5"=>[103,"\x28"],"\xD6"=>[114,"\x28"],"\xD7"=>[14,"\x28"],"\xD8"=>[93,"\x29"],"\xD9"=>[138,"\x29"],"\xDA"=>[75,"\x29"],"\xDB"=>[91,"\x29"],"\xDC"=>[108,"\x29"],"\xDD"=>[103,"\x29"],"\xDE"=>[114,"\x29"],"\xDF"=>[14,"\x29"],"\xE0"=>[93,"\x3F"],"\xE1"=>[138,"\x3F"],"\xE2"=>[75,"\x3F"],"\xE3"=>[91,"\x3F"],"\xE4"=>[108,"\x3F"],"\xE5"=>[103,"\x3F"],"\xE6"=>[114,"\x3F"],"\xE7"=>[14,"\x3F"],"\xE8"=>[94,"\x27"],"\xE9"=>[76,"\x27"],"\xEA"=>[104,"\x27"],"\xEB"=>[16,"\x27"],"\xEC"=>[94,"\x2B"],"\xED"=>[76,"\x2B"],"\xEE"=>[104,"\x2B"],"\xEF"=>[16,"\x2B"],"\xF0"=>[94,"\x7C"],"\xF1"=>[76,"\x7C"],"\xF2"=>[104,"\x7C"],"\xF3"=>[16,"\x7C"],"\xF4"=>[77,"\x23"],"\xF5"=>[18,"\x23"],"\xF6"=>[77,"\x3E"],"\xF7"=>[18,"\x3E"],"\xF8"=>[0,"\x00"],"\xF9"=>[0,"\x24"],"\xFA"=>[0,"\x40"],"\xFB"=>[0,"\x5B"],"\xFC"=>[0,"\x5D"],"\xFD"=>[0,"~"],"\xFE"=>[135,null],"\xFF"=>[24,null],],["\x00"=>[0,"w0"],"\x01"=>[0,"w1"],"\x02"=>[0,"w2"],"\x03"=>[0,"wa"],"\x04"=>[0,"wc"],"\x05"=>[0,"we"],"\x06"=>[0,"wi"],"\x07"=>[0,"wo"],"\x08"=>[0,"ws"],"\x09"=>[0,"wt"],"\x0A"=>[73,"w"],"\x0B"=>[88,"w"],"\x0C"=>[89,"w"],"\x0D"=>[96,"w"],"\x0E"=>[97,"w"],"\x0F"=>[99,"w"],"\x10"=>[106,"w"],"\x11"=>[136,"w"],"\x12"=>[139,"w"],"\x13"=>[141,"w"],"\x14"=>[145,"w"],"\x15"=>[147,"w"],"\x16"=>[149,"w"],"\x17"=>[101,"w"],"\x18"=>[112,"w"],"\x19"=>[117,"w"],"\x1A"=>[120,"w"],"\x1B"=>[124,"w"],"\x1C"=>[127,"w"],"\x1D"=>[144,"w"],"\x1E"=>[152,"w"],"\x1F"=>[11,"w"],"\x20"=>[0,"x0"],"\x21"=>[0,"x1"],"\x22"=>[0,"x2"],"\x23"=>[0,"xa"],"\x24"=>[0,"xc"],"\x25"=>[0,"xe"],"\x26"=>[0,"xi"],"\x27"=>[0,"xo"],"\x28"=>[0,"xs"],"\x29"=>[0,"xt"],"\x2A"=>[73,"x"],"\x2B"=>[88,"x"],"\x2C"=>[89,"x"],"-"=>[96,"x"],"."=>[97,"x"],"\x2F"=>[99,"x"],[106,"x"],[136,"x"],[139,"x"],[141,"x"],[145,"x"],[147,"x"],[149,"x"],[101,"x"],[112,"x"],[117,"x"],"\x3A"=>[120,"x"],"\x3B"=>[124,"x"],"\x3C"=>[127,"x"],"\x3D"=>[144,"x"],"\x3E"=>[152,"x"],"\x3F"=>[11,"x"],"\x40"=>[0,"y0"],"A"=>[0,"y1"],"B"=>[0,"y2"],"C"=>[0,"ya"],"D"=>[0,"yc"],"E"=>[0,"ye"],"F"=>[0,"yi"],"G"=>[0,"yo"],"H"=>[0,"ys"],"I"=>[0,"yt"],"J"=>[73,"y"],"K"=>[88,"y"],"L"=>[89,"y"],"M"=>[96,"y"],"N"=>[97,"y"],"O"=>[99,"y"],"P"=>[106,"y"],"Q"=>[136,"y"],"R"=>[139,"y"],"S"=>[141,"y"],"T"=>[145,"y"],"U"=>[147,"y"],"V"=>[149,"y"],"W"=>[101,"y"],"X"=>[112,"y"],"Y"=>[117,"y"],"Z"=>[120,"y"],"\x5B"=>[124,"y"],"\x5C"=>[127,"y"],"\x5D"=>[144,"y"],"\x5E"=>[152,"y"],"_"=>[11,"y"],"\x60"=>[0,"z0"],"a"=>[0,"z1"],"b"=>[0,"z2"],"c"=>[0,"za"],"d"=>[0,"zc"],"e"=>[0,"ze"],"f"=>[0,"zi"],"g"=>[0,"zo"],"h"=>[0,"zs"],"i"=>[0,"zt"],"j"=>[73,"z"],"k"=>[88,"z"],"l"=>[89,"z"],"m"=>[96,"z"],"n"=>[97,"z"],"o"=>[99,"z"],"p"=>[106,"z"],"q"=>[136,"z"],"r"=>[139,"z"],"s"=>[141,"z"],"t"=>[145,"z"],"u"=>[147,"z"],"v"=>[149,"z"],"w"=>[101,"z"],"x"=>[112,"z"],"y"=>[117,"z"],"z"=>[120,"z"],"\x7B"=>[124,"z"],"\x7C"=>[127,"z"],"\x7D"=>[144,"z"],"~"=>[152,"z"],"\x7F"=>[11,"z"],"\x80"=>[92,"\x26"],"\x81"=>[95,"\x26"],"\x82"=>[137,"\x26"],"\x83"=>[142,"\x26"],"\x84"=>[150,"\x26"],"\x85"=>[74,"\x26"],"\x86"=>[90,"\x26"],"\x87"=>[98,"\x26"],"\x88"=>[107,"\x26"],"\x89"=>[140,"\x26"],"\x8A"=>[146,"\x26"],"\x8B"=>[102,"\x26"],"\x8C"=>[113,"\x26"],"\x8D"=>[121,"\x26"],"\x8E"=>[128,"\x26"],"\x8F"=>[12,"\x26"],"\x90"=>[92,"\x2A"],"\x91"=>[95,"\x2A"],"\x92"=>[137,"\x2A"],"\x93"=>[142,"\x2A"],"\x94"=>[150,"\x2A"],"\x95"=>[74,"\x2A"],"\x96"=>[90,"\x2A"],"\x97"=>[98,"\x2A"],"\x98"=>[107,"\x2A"],"\x99"=>[140,"\x2A"],"\x9A"=>[146,"\x2A"],"\x9B"=>[102,"\x2A"],"\x9C"=>[113,"\x2A"],"\x9D"=>[121,"\x2A"],"\x9E"=>[128,"\x2A"],"\x9F"=>[12,"\x2A"],"\xA0"=>[92,"\x2C"],"\xA1"=>[95,"\x2C"],"\xA2"=>[137,"\x2C"],"\xA3"=>[142,"\x2C"],"\xA4"=>[150,"\x2C"],"\xA5"=>[74,"\x2C"],"\xA6"=>[90,"\x2C"],"\xA7"=>[98,"\x2C"],"\xA8"=>[107,"\x2C"],"\xA9"=>[140,"\x2C"],"\xAA"=>[146,"\x2C"],"\xAB"=>[102,"\x2C"],"\xAC"=>[113,"\x2C"],"\xAD"=>[121,"\x2C"],"\xAE"=>[128,"\x2C"],"\xAF"=>[12,"\x2C"],"\xB0"=>[92,"\x3B"],"\xB1"=>[95,"\x3B"],"\xB2"=>[137,"\x3B"],"\xB3"=>[142,"\x3B"],"\xB4"=>[150,"\x3B"],"\xB5"=>[74,"\x3B"],"\xB6"=>[90,"\x3B"],"\xB7"=>[98,"\x3B"],"\xB8"=>[107,"\x3B"],"\xB9"=>[140,"\x3B"],"\xBA"=>[146,"\x3B"],"\xBB"=>[102,"\x3B"],"\xBC"=>[113,"\x3B"],"\xBD"=>[121,"\x3B"],"\xBE"=>[128,"\x3B"],"\xBF"=>[12,"\x3B"],"\xC0"=>[92,"X"],"\xC1"=>[95,"X"],"\xC2"=>[137,"X"],"\xC3"=>[142,"X"],"\xC4"=>[150,"X"],"\xC5"=>[74,"X"],"\xC6"=>[90,"X"],"\xC7"=>[98,"X"],"\xC8"=>[107,"X"],"\xC9"=>[140,"X"],"\xCA"=>[146,"X"],"\xCB"=>[102,"X"],"\xCC"=>[113,"X"],"\xCD"=>[121,"X"],"\xCE"=>[128,"X"],"\xCF"=>[12,"X"],"\xD0"=>[92,"Z"],"\xD1"=>[95,"Z"],"\xD2"=>[137,"Z"],"\xD3"=>[142,"Z"],"\xD4"=>[150,"Z"],"\xD5"=>[74,"Z"],"\xD6"=>[90,"Z"],"\xD7"=>[98,"Z"],"\xD8"=>[107,"Z"],"\xD9"=>[140,"Z"],"\xDA"=>[146,"Z"],"\xDB"=>[102,"Z"],"\xDC"=>[113,"Z"],"\xDD"=>[121,"Z"],"\xDE"=>[128,"Z"],"\xDF"=>[12,"Z"],"\xE0"=>[94,"\x21"],"\xE1"=>[76,"\x21"],"\xE2"=>[104,"\x21"],"\xE3"=>[16,"\x21"],"\xE4"=>[94,"\x22"],"\xE5"=>[76,"\x22"],"\xE6"=>[104,"\x22"],"\xE7"=>[16,"\x22"],"\xE8"=>[94,"\x28"],"\xE9"=>[76,"\x28"],"\xEA"=>[104,"\x28"],"\xEB"=>[16,"\x28"],"\xEC"=>[94,"\x29"],"\xED"=>[76,"\x29"],"\xEE"=>[104,"\x29"],"\xEF"=>[16,"\x29"],"\xF0"=>[94,"\x3F"],"\xF1"=>[76,"\x3F"],"\xF2"=>[104,"\x3F"],"\xF3"=>[16,"\x3F"],"\xF4"=>[77,"\x27"],"\xF5"=>[18,"\x27"],"\xF6"=>[77,"\x2B"],"\xF7"=>[18,"\x2B"],"\xF8"=>[77,"\x7C"],"\xF9"=>[18,"\x7C"],"\xFA"=>[0,"\x23"],"\xFB"=>[0,"\x3E"],"\xFC"=>[13,null],"\xFD"=>[109,null],"\xFE"=>[134,null],"\xFF"=>[26,null],],["\x00"=>[94,"\x000"],"\x01"=>[76,"\x000"],"\x02"=>[104,"\x000"],"\x03"=>[16,"\x000"],"\x04"=>[94,"\x001"],"\x05"=>[76,"\x001"],"\x06"=>[104,"\x001"],"\x07"=>[16,"\x001"],"\x08"=>[94,"\x002"],"\x09"=>[76,"\x002"],"\x0A"=>[104,"\x002"],"\x0B"=>[16,"\x002"],"\x0C"=>[94,"\x00a"],"\x0D"=>[76,"\x00a"],"\x0E"=>[104,"\x00a"],"\x0F"=>[16,"\x00a"],"\x10"=>[94,"\x00c"],"\x11"=>[76,"\x00c"],"\x12"=>[104,"\x00c"],"\x13"=>[16,"\x00c"],"\x14"=>[94,"\x00e"],"\x15"=>[76,"\x00e"],"\x16"=>[104,"\x00e"],"\x17"=>[16,"\x00e"],"\x18"=>[94,"\x00i"],"\x19"=>[76,"\x00i"],"\x1A"=>[104,"\x00i"],"\x1B"=>[16,"\x00i"],"\x1C"=>[94,"\x00o"],"\x1D"=>[76,"\x00o"],"\x1E"=>[104,"\x00o"],"\x1F"=>[16,"\x00o"],"\x20"=>[94,"\x00s"],"\x21"=>[76,"\x00s"],"\x22"=>[104,"\x00s"],"\x23"=>[16,"\x00s"],"\x24"=>[94,"\x00t"],"\x25"=>[76,"\x00t"],"\x26"=>[104,"\x00t"],"\x27"=>[16,"\x00t"],"\x28"=>[77,"\x00\x20"],"\x29"=>[18,"\x00\x20"],"\x2A"=>[77,"\x00\x25"],"\x2B"=>[18,"\x00\x25"],"\x2C"=>[77,"\x00-"],"-"=>[18,"\x00-"],"."=>[77,"\x00."],"\x2F"=>[18,"\x00."],[77,"\x00\x2F"],[18,"\x00\x2F"],[77,"\x003"],[18,"\x003"],[77,"\x004"],[18,"\x004"],[77,"\x005"],[18,"\x005"],[77,"\x006"],[18,"\x006"],"\x3A"=>[77,"\x007"],"\x3B"=>[18,"\x007"],"\x3C"=>[77,"\x008"],"\x3D"=>[18,"\x008"],"\x3E"=>[77,"\x009"],"\x3F"=>[18,"\x009"],"\x40"=>[77,"\x00\x3D"],"A"=>[18,"\x00\x3D"],"B"=>[77,"\x00A"],"C"=>[18,"\x00A"],"D"=>[77,"\x00_"],"E"=>[18,"\x00_"],"F"=>[77,"\x00b"],"G"=>[18,"\x00b"],"H"=>[77,"\x00d"],"I"=>[18,"\x00d"],"J"=>[77,"\x00f"],"K"=>[18,"\x00f"],"L"=>[77,"\x00g"],"M"=>[18,"\x00g"],"N"=>[77,"\x00h"],"O"=>[18,"\x00h"],"P"=>[77,"\x00l"],"Q"=>[18,"\x00l"],"R"=>[77,"\x00m"],"S"=>[18,"\x00m"],"T"=>[77,"\x00n"],"U"=>[18,"\x00n"],"V"=>[77,"\x00p"],"W"=>[18,"\x00p"],"X"=>[77,"\x00r"],"Y"=>[18,"\x00r"],"Z"=>[77,"\x00u"],"\x5B"=>[18,"\x00u"],"\x5C"=>[0,"\x00\x3A"],"\x5D"=>[0,"\x00B"],"\x5E"=>[0,"\x00C"],"_"=>[0,"\x00D"],"\x60"=>[0,"\x00E"],"a"=>[0,"\x00F"],"b"=>[0,"\x00G"],"c"=>[0,"\x00H"],"d"=>[0,"\x00I"],"e"=>[0,"\x00J"],"f"=>[0,"\x00K"],"g"=>[0,"\x00L"],"h"=>[0,"\x00M"],"i"=>[0,"\x00N"],"j"=>[0,"\x00O"],"k"=>[0,"\x00P"],"l"=>[0,"\x00Q"],"m"=>[0,"\x00R"],"n"=>[0,"\x00S"],"o"=>[0,"\x00T"],"p"=>[0,"\x00U"],"q"=>[0,"\x00V"],"r"=>[0,"\x00W"],"s"=>[0,"\x00Y"],"t"=>[0,"\x00j"],"u"=>[0,"\x00k"],"v"=>[0,"\x00q"],"w"=>[0,"\x00v"],"x"=>[0,"\x00w"],"y"=>[0,"\x00x"],"z"=>[0,"\x00y"],"\x7B"=>[0,"\x00z"],"\x7C"=>[82,"\x00"],"\x7D"=>[87,"\x00"],"~"=>[130,"\x00"],"\x7F"=>[9,"\x00"],"\x80"=>[94,"\x240"],"\x81"=>[76,"\x240"],"\x82"=>[104,"\x240"],"\x83"=>[16,"\x240"],"\x84"=>[94,"\x241"],"\x85"=>[76,"\x241"],"\x86"=>[104,"\x241"],"\x87"=>[16,"\x241"],"\x88"=>[94,"\x242"],"\x89"=>[76,"\x242"],"\x8A"=>[104,"\x242"],"\x8B"=>[16,"\x242"],"\x8C"=>[94,"\x24a"],"\x8D"=>[76,"\x24a"],"\x8E"=>[104,"\x24a"],"\x8F"=>[16,"\x24a"],"\x90"=>[94,"\x24c"],"\x91"=>[76,"\x24c"],"\x92"=>[104,"\x24c"],"\x93"=>[16,"\x24c"],"\x94"=>[94,"\x24e"],"\x95"=>[76,"\x24e"],"\x96"=>[104,"\x24e"],"\x97"=>[16,"\x24e"],"\x98"=>[94,"\x24i"],"\x99"=>[76,"\x24i"],"\x9A"=>[104,"\x24i"],"\x9B"=>[16,"\x24i"],"\x9C"=>[94,"\x24o"],"\x9D"=>[76,"\x24o"],"\x9E"=>[104,"\x24o"],"\x9F"=>[16,"\x24o"],"\xA0"=>[94,"\x24s"],"\xA1"=>[76,"\x24s"],"\xA2"=>[104,"\x24s"],"\xA3"=>[16,"\x24s"],"\xA4"=>[94,"\x24t"],"\xA5"=>[76,"\x24t"],"\xA6"=>[104,"\x24t"],"\xA7"=>[16,"\x24t"],"\xA8"=>[77,"\x24\x20"],"\xA9"=>[18,"\x24\x20"],"\xAA"=>[77,"\x24\x25"],"\xAB"=>[18,"\x24\x25"],"\xAC"=>[77,"\x24-"],"\xAD"=>[18,"\x24-"],"\xAE"=>[77,"\x24."],"\xAF"=>[18,"\x24."],"\xB0"=>[77,"\x24\x2F"],"\xB1"=>[18,"\x24\x2F"],"\xB2"=>[77,"\x243"],"\xB3"=>[18,"\x243"],"\xB4"=>[77,"\x244"],"\xB5"=>[18,"\x244"],"\xB6"=>[77,"\x245"],"\xB7"=>[18,"\x245"],"\xB8"=>[77,"\x246"],"\xB9"=>[18,"\x246"],"\xBA"=>[77,"\x247"],"\xBB"=>[18,"\x247"],"\xBC"=>[77,"\x248"],"\xBD"=>[18,"\x248"],"\xBE"=>[77,"\x249"],"\xBF"=>[18,"\x249"],"\xC0"=>[77,"\x24\x3D"],"\xC1"=>[18,"\x24\x3D"],"\xC2"=>[77,"\x24A"],"\xC3"=>[18,"\x24A"],"\xC4"=>[77,"\x24_"],"\xC5"=>[18,"\x24_"],"\xC6"=>[77,"\x24b"],"\xC7"=>[18,"\x24b"],"\xC8"=>[77,"\x24d"],"\xC9"=>[18,"\x24d"],"\xCA"=>[77,"\x24f"],"\xCB"=>[18,"\x24f"],"\xCC"=>[77,"\x24g"],"\xCD"=>[18,"\x24g"],"\xCE"=>[77,"\x24h"],"\xCF"=>[18,"\x24h"],"\xD0"=>[77,"\x24l"],"\xD1"=>[18,"\x24l"],"\xD2"=>[77,"\x24m"],"\xD3"=>[18,"\x24m"],"\xD4"=>[77,"\x24n"],"\xD5"=>[18,"\x24n"],"\xD6"=>[77,"\x24p"],"\xD7"=>[18,"\x24p"],"\xD8"=>[77,"\x24r"],"\xD9"=>[18,"\x24r"],"\xDA"=>[77,"\x24u"],"\xDB"=>[18,"\x24u"],"\xDC"=>[0,"\x24\x3A"],"\xDD"=>[0,"\x24B"],"\xDE"=>[0,"\x24C"],"\xDF"=>[0,"\x24D"],"\xE0"=>[0,"\x24E"],"\xE1"=>[0,"\x24F"],"\xE2"=>[0,"\x24G"],"\xE3"=>[0,"\x24H"],"\xE4"=>[0,"\x24I"],"\xE5"=>[0,"\x24J"],"\xE6"=>[0,"\x24K"],"\xE7"=>[0,"\x24L"],"\xE8"=>[0,"\x24M"],"\xE9"=>[0,"\x24N"],"\xEA"=>[0,"\x24O"],"\xEB"=>[0,"\x24P"],"\xEC"=>[0,"\x24Q"],"\xED"=>[0,"\x24R"],"\xEE"=>[0,"\x24S"],"\xEF"=>[0,"\x24T"],"\xF0"=>[0,"\x24U"],"\xF1"=>[0,"\x24V"],"\xF2"=>[0,"\x24W"],"\xF3"=>[0,"\x24Y"],"\xF4"=>[0,"\x24j"],"\xF5"=>[0,"\x24k"],"\xF6"=>[0,"\x24q"],"\xF7"=>[0,"\x24v"],"\xF8"=>[0,"\x24w"],"\xF9"=>[0,"\x24x"],"\xFA"=>[0,"\x24y"],"\xFB"=>[0,"\x24z"],"\xFC"=>[82,"\x24"],"\xFD"=>[87,"\x24"],"\xFE"=>[130,"\x24"],"\xFF"=>[9,"\x24"],],["\x00"=>[92,"U"],"\x01"=>[95,"U"],"\x02"=>[137,"U"],"\x03"=>[142,"U"],"\x04"=>[150,"U"],"\x05"=>[74,"U"],"\x06"=>[90,"U"],"\x07"=>[98,"U"],"\x08"=>[107,"U"],"\x09"=>[140,"U"],"\x0A"=>[146,"U"],"\x0B"=>[102,"U"],"\x0C"=>[113,"U"],"\x0D"=>[121,"U"],"\x0E"=>[128,"U"],"\x0F"=>[12,"U"],"\x10"=>[92,"V"],"\x11"=>[95,"V"],"\x12"=>[137,"V"],"\x13"=>[142,"V"],"\x14"=>[150,"V"],"\x15"=>[74,"V"],"\x16"=>[90,"V"],"\x17"=>[98,"V"],"\x18"=>[107,"V"],"\x19"=>[140,"V"],"\x1A"=>[146,"V"],"\x1B"=>[102,"V"],"\x1C"=>[113,"V"],"\x1D"=>[121,"V"],"\x1E"=>[128,"V"],"\x1F"=>[12,"V"],"\x20"=>[92,"W"],"\x21"=>[95,"W"],"\x22"=>[137,"W"],"\x23"=>[142,"W"],"\x24"=>[150,"W"],"\x25"=>[74,"W"],"\x26"=>[90,"W"],"\x27"=>[98,"W"],"\x28"=>[107,"W"],"\x29"=>[140,"W"],"\x2A"=>[146,"W"],"\x2B"=>[102,"W"],"\x2C"=>[113,"W"],"-"=>[121,"W"],"."=>[128,"W"],"\x2F"=>[12,"W"],[92,"Y"],[95,"Y"],[137,"Y"],[142,"Y"],[150,"Y"],[74,"Y"],[90,"Y"],[98,"Y"],[107,"Y"],[140,"Y"],"\x3A"=>[146,"Y"],"\x3B"=>[102,"Y"],"\x3C"=>[113,"Y"],"\x3D"=>[121,"Y"],"\x3E"=>[128,"Y"],"\x3F"=>[12,"Y"],"\x40"=>[92,"j"],"A"=>[95,"j"],"B"=>[137,"j"],"C"=>[142,"j"],"D"=>[150,"j"],"E"=>[74,"j"],"F"=>[90,"j"],"G"=>[98,"j"],"H"=>[107,"j"],"I"=>[140,"j"],"J"=>[146,"j"],"K"=>[102,"j"],"L"=>[113,"j"],"M"=>[121,"j"],"N"=>[128,"j"],"O"=>[12,"j"],"P"=>[92,"k"],"Q"=>[95,"k"],"R"=>[137,"k"],"S"=>[142,"k"],"T"=>[150,"k"],"U"=>[74,"k"],"V"=>[90,"k"],"W"=>[98,"k"],"X"=>[107,"k"],"Y"=>[140,"k"],"Z"=>[146,"k"],"\x5B"=>[102,"k"],"\x5C"=>[113,"k"],"\x5D"=>[121,"k"],"\x5E"=>[128,"k"],"_"=>[12,"k"],"\x60"=>[92,"q"],"a"=>[95,"q"],"b"=>[137,"q"],"c"=>[142,"q"],"d"=>[150,"q"],"e"=>[74,"q"],"f"=>[90,"q"],"g"=>[98,"q"],"h"=>[107,"q"],"i"=>[140,"q"],"j"=>[146,"q"],"k"=>[102,"q"],"l"=>[113,"q"],"m"=>[121,"q"],"n"=>[128,"q"],"o"=>[12,"q"],"p"=>[92,"v"],"q"=>[95,"v"],"r"=>[137,"v"],"s"=>[142,"v"],"t"=>[150,"v"],"u"=>[74,"v"],"v"=>[90,"v"],"w"=>[98,"v"],"x"=>[107,"v"],"y"=>[140,"v"],"z"=>[146,"v"],"\x7B"=>[102,"v"],"\x7C"=>[113,"v"],"\x7D"=>[121,"v"],"~"=>[128,"v"],"\x7F"=>[12,"v"],"\x80"=>[92,"w"],"\x81"=>[95,"w"],"\x82"=>[137,"w"],"\x83"=>[142,"w"],"\x84"=>[150,"w"],"\x85"=>[74,"w"],"\x86"=>[90,"w"],"\x87"=>[98,"w"],"\x88"=>[107,"w"],"\x89"=>[140,"w"],"\x8A"=>[146,"w"],"\x8B"=>[102,"w"],"\x8C"=>[113,"w"],"\x8D"=>[121,"w"],"\x8E"=>[128,"w"],"\x8F"=>[12,"w"],"\x90"=>[92,"x"],"\x91"=>[95,"x"],"\x92"=>[137,"x"],"\x93"=>[142,"x"],"\x94"=>[150,"x"],"\x95"=>[74,"x"],"\x96"=>[90,"x"],"\x97"=>[98,"x"],"\x98"=>[107,"x"],"\x99"=>[140,"x"],"\x9A"=>[146,"x"],"\x9B"=>[102,"x"],"\x9C"=>[113,"x"],"\x9D"=>[121,"x"],"\x9E"=>[128,"x"],"\x9F"=>[12,"x"],"\xA0"=>[92,"y"],"\xA1"=>[95,"y"],"\xA2"=>[137,"y"],"\xA3"=>[142,"y"],"\xA4"=>[150,"y"],"\xA5"=>[74,"y"],"\xA6"=>[90,"y"],"\xA7"=>[98,"y"],"\xA8"=>[107,"y"],"\xA9"=>[140,"y"],"\xAA"=>[146,"y"],"\xAB"=>[102,"y"],"\xAC"=>[113,"y"],"\xAD"=>[121,"y"],"\xAE"=>[128,"y"],"\xAF"=>[12,"y"],"\xB0"=>[92,"z"],"\xB1"=>[95,"z"],"\xB2"=>[137,"z"],"\xB3"=>[142,"z"],"\xB4"=>[150,"z"],"\xB5"=>[74,"z"],"\xB6"=>[90,"z"],"\xB7"=>[98,"z"],"\xB8"=>[107,"z"],"\xB9"=>[140,"z"],"\xBA"=>[146,"z"],"\xBB"=>[102,"z"],"\xBC"=>[113,"z"],"\xBD"=>[121,"z"],"\xBE"=>[128,"z"],"\xBF"=>[12,"z"],"\xC0"=>[93,"\x26"],"\xC1"=>[138,"\x26"],"\xC2"=>[75,"\x26"],"\xC3"=>[91,"\x26"],"\xC4"=>[108,"\x26"],"\xC5"=>[103,"\x26"],"\xC6"=>[114,"\x26"],"\xC7"=>[14,"\x26"],"\xC8"=>[93,"\x2A"],"\xC9"=>[138,"\x2A"],"\xCA"=>[75,"\x2A"],"\xCB"=>[91,"\x2A"],"\xCC"=>[108,"\x2A"],"\xCD"=>[103,"\x2A"],"\xCE"=>[114,"\x2A"],"\xCF"=>[14,"\x2A"],"\xD0"=>[93,"\x2C"],"\xD1"=>[138,"\x2C"],"\xD2"=>[75,"\x2C"],"\xD3"=>[91,"\x2C"],"\xD4"=>[108,"\x2C"],"\xD5"=>[103,"\x2C"],"\xD6"=>[114,"\x2C"],"\xD7"=>[14,"\x2C"],"\xD8"=>[93,"\x3B"],"\xD9"=>[138,"\x3B"],"\xDA"=>[75,"\x3B"],"\xDB"=>[91,"\x3B"],"\xDC"=>[108,"\x3B"],"\xDD"=>[103,"\x3B"],"\xDE"=>[114,"\x3B"],"\xDF"=>[14,"\x3B"],"\xE0"=>[93,"X"],"\xE1"=>[138,"X"],"\xE2"=>[75,"X"],"\xE3"=>[91,"X"],"\xE4"=>[108,"X"],"\xE5"=>[103,"X"],"\xE6"=>[114,"X"],"\xE7"=>[14,"X"],"\xE8"=>[93,"Z"],"\xE9"=>[138,"Z"],"\xEA"=>[75,"Z"],"\xEB"=>[91,"Z"],"\xEC"=>[108,"Z"],"\xED"=>[103,"Z"],"\xEE"=>[114,"Z"],"\xEF"=>[14,"Z"],"\xF0"=>[77,"\x21"],"\xF1"=>[18,"\x21"],"\xF2"=>[77,"\x22"],"\xF3"=>[18,"\x22"],"\xF4"=>[77,"\x28"],"\xF5"=>[18,"\x28"],"\xF6"=>[77,"\x29"],"\xF7"=>[18,"\x29"],"\xF8"=>[77,"\x3F"],"\xF9"=>[18,"\x3F"],"\xFA"=>[0,"\x27"],"\xFB"=>[0,"\x2B"],"\xFC"=>[0,"\x7C"],"\xFD"=>[80,null],"\xFE"=>[15,null],"\xFF"=>[28,null],],["\x00"=>[77,"\x000"],"\x01"=>[18,"\x000"],"\x02"=>[77,"\x001"],"\x03"=>[18,"\x001"],"\x04"=>[77,"\x002"],"\x05"=>[18,"\x002"],"\x06"=>[77,"\x00a"],"\x07"=>[18,"\x00a"],"\x08"=>[77,"\x00c"],"\x09"=>[18,"\x00c"],"\x0A"=>[77,"\x00e"],"\x0B"=>[18,"\x00e"],"\x0C"=>[77,"\x00i"],"\x0D"=>[18,"\x00i"],"\x0E"=>[77,"\x00o"],"\x0F"=>[18,"\x00o"],"\x10"=>[77,"\x00s"],"\x11"=>[18,"\x00s"],"\x12"=>[77,"\x00t"],"\x13"=>[18,"\x00t"],"\x14"=>[0,"\x00\x20"],"\x15"=>[0,"\x00\x25"],"\x16"=>[0,"\x00-"],"\x17"=>[0,"\x00."],"\x18"=>[0,"\x00\x2F"],"\x19"=>[0,"\x003"],"\x1A"=>[0,"\x004"],"\x1B"=>[0,"\x005"],"\x1C"=>[0,"\x006"],"\x1D"=>[0,"\x007"],"\x1E"=>[0,"\x008"],"\x1F"=>[0,"\x009"],"\x20"=>[0,"\x00\x3D"],"\x21"=>[0,"\x00A"],"\x22"=>[0,"\x00_"],"\x23"=>[0,"\x00b"],"\x24"=>[0,"\x00d"],"\x25"=>[0,"\x00f"],"\x26"=>[0,"\x00g"],"\x27"=>[0,"\x00h"],"\x28"=>[0,"\x00l"],"\x29"=>[0,"\x00m"],"\x2A"=>[0,"\x00n"],"\x2B"=>[0,"\x00p"],"\x2C"=>[0,"\x00r"],"-"=>[0,"\x00u"],"."=>[100,"\x00"],"\x2F"=>[110,"\x00"],[111,"\x00"],[115,"\x00"],[116,"\x00"],[118,"\x00"],[119,"\x00"],[122,"\x00"],[123,"\x00"],[125,"\x00"],[126,"\x00"],[129,"\x00"],"\x3A"=>[143,"\x00"],"\x3B"=>[148,"\x00"],"\x3C"=>[151,"\x00"],"\x3D"=>[153,"\x00"],"\x3E"=>[83,"\x00"],"\x3F"=>[10,"\x00"],"\x40"=>[77,"\x240"],"A"=>[18,"\x240"],"B"=>[77,"\x241"],"C"=>[18,"\x241"],"D"=>[77,"\x242"],"E"=>[18,"\x242"],"F"=>[77,"\x24a"],"G"=>[18,"\x24a"],"H"=>[77,"\x24c"],"I"=>[18,"\x24c"],"J"=>[77,"\x24e"],"K"=>[18,"\x24e"],"L"=>[77,"\x24i"],"M"=>[18,"\x24i"],"N"=>[77,"\x24o"],"O"=>[18,"\x24o"],"P"=>[77,"\x24s"],"Q"=>[18,"\x24s"],"R"=>[77,"\x24t"],"S"=>[18,"\x24t"],"T"=>[0,"\x24\x20"],"U"=>[0,"\x24\x25"],"V"=>[0,"\x24-"],"W"=>[0,"\x24."],"X"=>[0,"\x24\x2F"],"Y"=>[0,"\x243"],"Z"=>[0,"\x244"],"\x5B"=>[0,"\x245"],"\x5C"=>[0,"\x246"],"\x5D"=>[0,"\x247"],"\x5E"=>[0,"\x248"],"_"=>[0,"\x249"],"\x60"=>[0,"\x24\x3D"],"a"=>[0,"\x24A"],"b"=>[0,"\x24_"],"c"=>[0,"\x24b"],"d"=>[0,"\x24d"],"e"=>[0,"\x24f"],"f"=>[0,"\x24g"],"g"=>[0,"\x24h"],"h"=>[0,"\x24l"],"i"=>[0,"\x24m"],"j"=>[0,"\x24n"],"k"=>[0,"\x24p"],"l"=>[0,"\x24r"],"m"=>[0,"\x24u"],"n"=>[100,"\x24"],"o"=>[110,"\x24"],"p"=>[111,"\x24"],"q"=>[115,"\x24"],"r"=>[116,"\x24"],"s"=>[118,"\x24"],"t"=>[119,"\x24"],"u"=>[122,"\x24"],"v"=>[123,"\x24"],"w"=>[125,"\x24"],"x"=>[126,"\x24"],"y"=>[129,"\x24"],"z"=>[143,"\x24"],"\x7B"=>[148,"\x24"],"\x7C"=>[151,"\x24"],"\x7D"=>[153,"\x24"],"~"=>[83,"\x24"],"\x7F"=>[10,"\x24"],"\x80"=>[77,"\x400"],"\x81"=>[18,"\x400"],"\x82"=>[77,"\x401"],"\x83"=>[18,"\x401"],"\x84"=>[77,"\x402"],"\x85"=>[18,"\x402"],"\x86"=>[77,"\x40a"],"\x87"=>[18,"\x40a"],"\x88"=>[77,"\x40c"],"\x89"=>[18,"\x40c"],"\x8A"=>[77,"\x40e"],"\x8B"=>[18,"\x40e"],"\x8C"=>[77,"\x40i"],"\x8D"=>[18,"\x40i"],"\x8E"=>[77,"\x40o"],"\x8F"=>[18,"\x40o"],"\x90"=>[77,"\x40s"],"\x91"=>[18,"\x40s"],"\x92"=>[77,"\x40t"],"\x93"=>[18,"\x40t"],"\x94"=>[0,"\x40\x20"],"\x95"=>[0,"\x40\x25"],"\x96"=>[0,"\x40-"],"\x97"=>[0,"\x40."],"\x98"=>[0,"\x40\x2F"],"\x99"=>[0,"\x403"],"\x9A"=>[0,"\x404"],"\x9B"=>[0,"\x405"],"\x9C"=>[0,"\x406"],"\x9D"=>[0,"\x407"],"\x9E"=>[0,"\x408"],"\x9F"=>[0,"\x409"],"\xA0"=>[0,"\x40\x3D"],"\xA1"=>[0,"\x40A"],"\xA2"=>[0,"\x40_"],"\xA3"=>[0,"\x40b"],"\xA4"=>[0,"\x40d"],"\xA5"=>[0,"\x40f"],"\xA6"=>[0,"\x40g"],"\xA7"=>[0,"\x40h"],"\xA8"=>[0,"\x40l"],"\xA9"=>[0,"\x40m"],"\xAA"=>[0,"\x40n"],"\xAB"=>[0,"\x40p"],"\xAC"=>[0,"\x40r"],"\xAD"=>[0,"\x40u"],"\xAE"=>[100,"\x40"],"\xAF"=>[110,"\x40"],"\xB0"=>[111,"\x40"],"\xB1"=>[115,"\x40"],"\xB2"=>[116,"\x40"],"\xB3"=>[118,"\x40"],"\xB4"=>[119,"\x40"],"\xB5"=>[122,"\x40"],"\xB6"=>[123,"\x40"],"\xB7"=>[125,"\x40"],"\xB8"=>[126,"\x40"],"\xB9"=>[129,"\x40"],"\xBA"=>[143,"\x40"],"\xBB"=>[148,"\x40"],"\xBC"=>[151,"\x40"],"\xBD"=>[153,"\x40"],"\xBE"=>[83,"\x40"],"\xBF"=>[10,"\x40"],"\xC0"=>[77,"\x5B0"],"\xC1"=>[18,"\x5B0"],"\xC2"=>[77,"\x5B1"],"\xC3"=>[18,"\x5B1"],"\xC4"=>[77,"\x5B2"],"\xC5"=>[18,"\x5B2"],"\xC6"=>[77,"\x5Ba"],"\xC7"=>[18,"\x5Ba"],"\xC8"=>[77,"\x5Bc"],"\xC9"=>[18,"\x5Bc"],"\xCA"=>[77,"\x5Be"],"\xCB"=>[18,"\x5Be"],"\xCC"=>[77,"\x5Bi"],"\xCD"=>[18,"\x5Bi"],"\xCE"=>[77,"\x5Bo"],"\xCF"=>[18,"\x5Bo"],"\xD0"=>[77,"\x5Bs"],"\xD1"=>[18,"\x5Bs"],"\xD2"=>[77,"\x5Bt"],"\xD3"=>[18,"\x5Bt"],"\xD4"=>[0,"\x5B\x20"],"\xD5"=>[0,"\x5B\x25"],"\xD6"=>[0,"\x5B-"],"\xD7"=>[0,"\x5B."],"\xD8"=>[0,"\x5B\x2F"],"\xD9"=>[0,"\x5B3"],"\xDA"=>[0,"\x5B4"],"\xDB"=>[0,"\x5B5"],"\xDC"=>[0,"\x5B6"],"\xDD"=>[0,"\x5B7"],"\xDE"=>[0,"\x5B8"],"\xDF"=>[0,"\x5B9"],"\xE0"=>[0,"\x5B\x3D"],"\xE1"=>[0,"\x5BA"],"\xE2"=>[0,"\x5B_"],"\xE3"=>[0,"\x5Bb"],"\xE4"=>[0,"\x5Bd"],"\xE5"=>[0,"\x5Bf"],"\xE6"=>[0,"\x5Bg"],"\xE7"=>[0,"\x5Bh"],"\xE8"=>[0,"\x5Bl"],"\xE9"=>[0,"\x5Bm"],"\xEA"=>[0,"\x5Bn"],"\xEB"=>[0,"\x5Bp"],"\xEC"=>[0,"\x5Br"],"\xED"=>[0,"\x5Bu"],"\xEE"=>[100,"\x5B"],"\xEF"=>[110,"\x5B"],"\xF0"=>[111,"\x5B"],"\xF1"=>[115,"\x5B"],"\xF2"=>[116,"\x5B"],"\xF3"=>[118,"\x5B"],"\xF4"=>[119,"\x5B"],"\xF5"=>[122,"\x5B"],"\xF6"=>[123,"\x5B"],"\xF7"=>[125,"\x5B"],"\xF8"=>[126,"\x5B"],"\xF9"=>[129,"\x5B"],"\xFA"=>[143,"\x5B"],"\xFB"=>[148,"\x5B"],"\xFC"=>[151,"\x5B"],"\xFD"=>[153,"\x5B"],"\xFE"=>[83,"\x5B"],"\xFF"=>[10,"\x5B"],],["\x00"=>[93,"E"],"\x01"=>[138,"E"],"\x02"=>[75,"E"],"\x03"=>[91,"E"],"\x04"=>[108,"E"],"\x05"=>[103,"E"],"\x06"=>[114,"E"],"\x07"=>[14,"E"],"\x08"=>[93,"F"],"\x09"=>[138,"F"],"\x0A"=>[75,"F"],"\x0B"=>[91,"F"],"\x0C"=>[108,"F"],"\x0D"=>[103,"F"],"\x0E"=>[114,"F"],"\x0F"=>[14,"F"],"\x10"=>[93,"G"],"\x11"=>[138,"G"],"\x12"=>[75,"G"],"\x13"=>[91,"G"],"\x14"=>[108,"G"],"\x15"=>[103,"G"],"\x16"=>[114,"G"],"\x17"=>[14,"G"],"\x18"=>[93,"H"],"\x19"=>[138,"H"],"\x1A"=>[75,"H"],"\x1B"=>[91,"H"],"\x1C"=>[108,"H"],"\x1D"=>[103,"H"],"\x1E"=>[114,"H"],"\x1F"=>[14,"H"],"\x20"=>[93,"I"],"\x21"=>[138,"I"],"\x22"=>[75,"I"],"\x23"=>[91,"I"],"\x24"=>[108,"I"],"\x25"=>[103,"I"],"\x26"=>[114,"I"],"\x27"=>[14,"I"],"\x28"=>[93,"J"],"\x29"=>[138,"J"],"\x2A"=>[75,"J"],"\x2B"=>[91,"J"],"\x2C"=>[108,"J"],"-"=>[103,"J"],"."=>[114,"J"],"\x2F"=>[14,"J"],[93,"K"],[138,"K"],[75,"K"],[91,"K"],[108,"K"],[103,"K"],[114,"K"],[14,"K"],[93,"L"],[138,"L"],"\x3A"=>[75,"L"],"\x3B"=>[91,"L"],"\x3C"=>[108,"L"],"\x3D"=>[103,"L"],"\x3E"=>[114,"L"],"\x3F"=>[14,"L"],"\x40"=>[93,"M"],"A"=>[138,"M"],"B"=>[75,"M"],"C"=>[91,"M"],"D"=>[108,"M"],"E"=>[103,"M"],"F"=>[114,"M"],"G"=>[14,"M"],"H"=>[93,"N"],"I"=>[138,"N"],"J"=>[75,"N"],"K"=>[91,"N"],"L"=>[108,"N"],"M"=>[103,"N"],"N"=>[114,"N"],"O"=>[14,"N"],"P"=>[93,"O"],"Q"=>[138,"O"],"R"=>[75,"O"],"S"=>[91,"O"],"T"=>[108,"O"],"U"=>[103,"O"],"V"=>[114,"O"],"W"=>[14,"O"],"X"=>[93,"P"],"Y"=>[138,"P"],"Z"=>[75,"P"],"\x5B"=>[91,"P"],"\x5C"=>[108,"P"],"\x5D"=>[103,"P"],"\x5E"=>[114,"P"],"_"=>[14,"P"],"\x60"=>[93,"Q"],"a"=>[138,"Q"],"b"=>[75,"Q"],"c"=>[91,"Q"],"d"=>[108,"Q"],"e"=>[103,"Q"],"f"=>[114,"Q"],"g"=>[14,"Q"],"h"=>[93,"R"],"i"=>[138,"R"],"j"=>[75,"R"],"k"=>[91,"R"],"l"=>[108,"R"],"m"=>[103,"R"],"n"=>[114,"R"],"o"=>[14,"R"],"p"=>[93,"S"],"q"=>[138,"S"],"r"=>[75,"S"],"s"=>[91,"S"],"t"=>[108,"S"],"u"=>[103,"S"],"v"=>[114,"S"],"w"=>[14,"S"],"x"=>[93,"T"],"y"=>[138,"T"],"z"=>[75,"T"],"\x7B"=>[91,"T"],"\x7C"=>[108,"T"],"\x7D"=>[103,"T"],"~"=>[114,"T"],"\x7F"=>[14,"T"],"\x80"=>[93,"U"],"\x81"=>[138,"U"],"\x82"=>[75,"U"],"\x83"=>[91,"U"],"\x84"=>[108,"U"],"\x85"=>[103,"U"],"\x86"=>[114,"U"],"\x87"=>[14,"U"],"\x88"=>[93,"V"],"\x89"=>[138,"V"],"\x8A"=>[75,"V"],"\x8B"=>[91,"V"],"\x8C"=>[108,"V"],"\x8D"=>[103,"V"],"\x8E"=>[114,"V"],"\x8F"=>[14,"V"],"\x90"=>[93,"W"],"\x91"=>[138,"W"],"\x92"=>[75,"W"],"\x93"=>[91,"W"],"\x94"=>[108,"W"],"\x95"=>[103,"W"],"\x96"=>[114,"W"],"\x97"=>[14,"W"],"\x98"=>[93,"Y"],"\x99"=>[138,"Y"],"\x9A"=>[75,"Y"],"\x9B"=>[91,"Y"],"\x9C"=>[108,"Y"],"\x9D"=>[103,"Y"],"\x9E"=>[114,"Y"],"\x9F"=>[14,"Y"],"\xA0"=>[93,"j"],"\xA1"=>[138,"j"],"\xA2"=>[75,"j"],"\xA3"=>[91,"j"],"\xA4"=>[108,"j"],"\xA5"=>[103,"j"],"\xA6"=>[114,"j"],"\xA7"=>[14,"j"],"\xA8"=>[93,"k"],"\xA9"=>[138,"k"],"\xAA"=>[75,"k"],"\xAB"=>[91,"k"],"\xAC"=>[108,"k"],"\xAD"=>[103,"k"],"\xAE"=>[114,"k"],"\xAF"=>[14,"k"],"\xB0"=>[93,"q"],"\xB1"=>[138,"q"],"\xB2"=>[75,"q"],"\xB3"=>[91,"q"],"\xB4"=>[108,"q"],"\xB5"=>[103,"q"],"\xB6"=>[114,"q"],"\xB7"=>[14,"q"],"\xB8"=>[93,"v"],"\xB9"=>[138,"v"],"\xBA"=>[75,"v"],"\xBB"=>[91,"v"],"\xBC"=>[108,"v"],"\xBD"=>[103,"v"],"\xBE"=>[114,"v"],"\xBF"=>[14,"v"],"\xC0"=>[93,"w"],"\xC1"=>[138,"w"],"\xC2"=>[75,"w"],"\xC3"=>[91,"w"],"\xC4"=>[108,"w"],"\xC5"=>[103,"w"],"\xC6"=>[114,"w"],"\xC7"=>[14,"w"],"\xC8"=>[93,"x"],"\xC9"=>[138,"x"],"\xCA"=>[75,"x"],"\xCB"=>[91,"x"],"\xCC"=>[108,"x"],"\xCD"=>[103,"x"],"\xCE"=>[114,"x"],"\xCF"=>[14,"x"],"\xD0"=>[93,"y"],"\xD1"=>[138,"y"],"\xD2"=>[75,"y"],"\xD3"=>[91,"y"],"\xD4"=>[108,"y"],"\xD5"=>[103,"y"],"\xD6"=>[114,"y"],"\xD7"=>[14,"y"],"\xD8"=>[93,"z"],"\xD9"=>[138,"z"],"\xDA"=>[75,"z"],"\xDB"=>[91,"z"],"\xDC"=>[108,"z"],"\xDD"=>[103,"z"],"\xDE"=>[114,"z"],"\xDF"=>[14,"z"],"\xE0"=>[94,"\x26"],"\xE1"=>[76,"\x26"],"\xE2"=>[104,"\x26"],"\xE3"=>[16,"\x26"],"\xE4"=>[94,"\x2A"],"\xE5"=>[76,"\x2A"],"\xE6"=>[104,"\x2A"],"\xE7"=>[16,"\x2A"],"\xE8"=>[94,"\x2C"],"\xE9"=>[76,"\x2C"],"\xEA"=>[104,"\x2C"],"\xEB"=>[16,"\x2C"],"\xEC"=>[94,"\x3B"],"\xED"=>[76,"\x3B"],"\xEE"=>[104,"\x3B"],"\xEF"=>[16,"\x3B"],"\xF0"=>[94,"X"],"\xF1"=>[76,"X"],"\xF2"=>[104,"X"],"\xF3"=>[16,"X"],"\xF4"=>[94,"Z"],"\xF5"=>[76,"Z"],"\xF6"=>[104,"Z"],"\xF7"=>[16,"Z"],"\xF8"=>[0,"\x21"],"\xF9"=>[0,"\x22"],"\xFA"=>[0,"\x28"],"\xFB"=>[0,"\x29"],"\xFC"=>[0,"\x3F"],"\xFD"=>[84,null],"\xFE"=>[81,null],"\xFF"=>[17,null],],["\x00"=>[0,"\x000"],"\x01"=>[0,"\x001"],"\x02"=>[0,"\x002"],"\x03"=>[0,"\x00a"],"\x04"=>[0,"\x00c"],"\x05"=>[0,"\x00e"],"\x06"=>[0,"\x00i"],"\x07"=>[0,"\x00o"],"\x08"=>[0,"\x00s"],"\x09"=>[0,"\x00t"],"\x0A"=>[73,"\x00"],"\x0B"=>[88,"\x00"],"\x0C"=>[89,"\x00"],"\x0D"=>[96,"\x00"],"\x0E"=>[97,"\x00"],"\x0F"=>[99,"\x00"],"\x10"=>[106,"\x00"],"\x11"=>[136,"\x00"],"\x12"=>[139,"\x00"],"\x13"=>[141,"\x00"],"\x14"=>[145,"\x00"],"\x15"=>[147,"\x00"],"\x16"=>[149,"\x00"],"\x17"=>[101,"\x00"],"\x18"=>[112,"\x00"],"\x19"=>[117,"\x00"],"\x1A"=>[120,"\x00"],"\x1B"=>[124,"\x00"],"\x1C"=>[127,"\x00"],"\x1D"=>[144,"\x00"],"\x1E"=>[152,"\x00"],"\x1F"=>[11,"\x00"],"\x20"=>[0,"\x240"],"\x21"=>[0,"\x241"],"\x22"=>[0,"\x242"],"\x23"=>[0,"\x24a"],"\x24"=>[0,"\x24c"],"\x25"=>[0,"\x24e"],"\x26"=>[0,"\x24i"],"\x27"=>[0,"\x24o"],"\x28"=>[0,"\x24s"],"\x29"=>[0,"\x24t"],"\x2A"=>[73,"\x24"],"\x2B"=>[88,"\x24"],"\x2C"=>[89,"\x24"],"-"=>[96,"\x24"],"."=>[97,"\x24"],"\x2F"=>[99,"\x24"],[106,"\x24"],[136,"\x24"],[139,"\x24"],[141,"\x24"],[145,"\x24"],[147,"\x24"],[149,"\x24"],[101,"\x24"],[112,"\x24"],[117,"\x24"],"\x3A"=>[120,"\x24"],"\x3B"=>[124,"\x24"],"\x3C"=>[127,"\x24"],"\x3D"=>[144,"\x24"],"\x3E"=>[152,"\x24"],"\x3F"=>[11,"\x24"],"\x40"=>[0,"\x400"],"A"=>[0,"\x401"],"B"=>[0,"\x402"],"C"=>[0,"\x40a"],"D"=>[0,"\x40c"],"E"=>[0,"\x40e"],"F"=>[0,"\x40i"],"G"=>[0,"\x40o"],"H"=>[0,"\x40s"],"I"=>[0,"\x40t"],"J"=>[73,"\x40"],"K"=>[88,"\x40"],"L"=>[89,"\x40"],"M"=>[96,"\x40"],"N"=>[97,"\x40"],"O"=>[99,"\x40"],"P"=>[106,"\x40"],"Q"=>[136,"\x40"],"R"=>[139,"\x40"],"S"=>[141,"\x40"],"T"=>[145,"\x40"],"U"=>[147,"\x40"],"V"=>[149,"\x40"],"W"=>[101,"\x40"],"X"=>[112,"\x40"],"Y"=>[117,"\x40"],"Z"=>[120,"\x40"],"\x5B"=>[124,"\x40"],"\x5C"=>[127,"\x40"],"\x5D"=>[144,"\x40"],"\x5E"=>[152,"\x40"],"_"=>[11,"\x40"],"\x60"=>[0,"\x5B0"],"a"=>[0,"\x5B1"],"b"=>[0,"\x5B2"],"c"=>[0,"\x5Ba"],"d"=>[0,"\x5Bc"],"e"=>[0,"\x5Be"],"f"=>[0,"\x5Bi"],"g"=>[0,"\x5Bo"],"h"=>[0,"\x5Bs"],"i"=>[0,"\x5Bt"],"j"=>[73,"\x5B"],"k"=>[88,"\x5B"],"l"=>[89,"\x5B"],"m"=>[96,"\x5B"],"n"=>[97,"\x5B"],"o"=>[99,"\x5B"],"p"=>[106,"\x5B"],"q"=>[136,"\x5B"],"r"=>[139,"\x5B"],"s"=>[141,"\x5B"],"t"=>[145,"\x5B"],"u"=>[147,"\x5B"],"v"=>[149,"\x5B"],"w"=>[101,"\x5B"],"x"=>[112,"\x5B"],"y"=>[117,"\x5B"],"z"=>[120,"\x5B"],"\x7B"=>[124,"\x5B"],"\x7C"=>[127,"\x5B"],"\x7D"=>[144,"\x5B"],"~"=>[152,"\x5B"],"\x7F"=>[11,"\x5B"],"\x80"=>[0,"\x5D0"],"\x81"=>[0,"\x5D1"],"\x82"=>[0,"\x5D2"],"\x83"=>[0,"\x5Da"],"\x84"=>[0,"\x5Dc"],"\x85"=>[0,"\x5De"],"\x86"=>[0,"\x5Di"],"\x87"=>[0,"\x5Do"],"\x88"=>[0,"\x5Ds"],"\x89"=>[0,"\x5Dt"],"\x8A"=>[73,"\x5D"],"\x8B"=>[88,"\x5D"],"\x8C"=>[89,"\x5D"],"\x8D"=>[96,"\x5D"],"\x8E"=>[97,"\x5D"],"\x8F"=>[99,"\x5D"],"\x90"=>[106,"\x5D"],"\x91"=>[136,"\x5D"],"\x92"=>[139,"\x5D"],"\x93"=>[141,"\x5D"],"\x94"=>[145,"\x5D"],"\x95"=>[147,"\x5D"],"\x96"=>[149,"\x5D"],"\x97"=>[101,"\x5D"],"\x98"=>[112,"\x5D"],"\x99"=>[117,"\x5D"],"\x9A"=>[120,"\x5D"],"\x9B"=>[124,"\x5D"],"\x9C"=>[127,"\x5D"],"\x9D"=>[144,"\x5D"],"\x9E"=>[152,"\x5D"],"\x9F"=>[11,"\x5D"],"\xA0"=>[0,"~0"],"\xA1"=>[0,"~1"],"\xA2"=>[0,"~2"],"\xA3"=>[0,"~a"],"\xA4"=>[0,"~c"],"\xA5"=>[0,"~e"],"\xA6"=>[0,"~i"],"\xA7"=>[0,"~o"],"\xA8"=>[0,"~s"],"\xA9"=>[0,"~t"],"\xAA"=>[73,"~"],"\xAB"=>[88,"~"],"\xAC"=>[89,"~"],"\xAD"=>[96,"~"],"\xAE"=>[97,"~"],"\xAF"=>[99,"~"],"\xB0"=>[106,"~"],"\xB1"=>[136,"~"],"\xB2"=>[139,"~"],"\xB3"=>[141,"~"],"\xB4"=>[145,"~"],"\xB5"=>[147,"~"],"\xB6"=>[149,"~"],"\xB7"=>[101,"~"],"\xB8"=>[112,"~"],"\xB9"=>[117,"~"],"\xBA"=>[120,"~"],"\xBB"=>[124,"~"],"\xBC"=>[127,"~"],"\xBD"=>[144,"~"],"\xBE"=>[152,"~"],"\xBF"=>[11,"~"],"\xC0"=>[92,"\x5E"],"\xC1"=>[95,"\x5E"],"\xC2"=>[137,"\x5E"],"\xC3"=>[142,"\x5E"],"\xC4"=>[150,"\x5E"],"\xC5"=>[74,"\x5E"],"\xC6"=>[90,"\x5E"],"\xC7"=>[98,"\x5E"],"\xC8"=>[107,"\x5E"],"\xC9"=>[140,"\x5E"],"\xCA"=>[146,"\x5E"],"\xCB"=>[102,"\x5E"],"\xCC"=>[113,"\x5E"],"\xCD"=>[121,"\x5E"],"\xCE"=>[128,"\x5E"],"\xCF"=>[12,"\x5E"],"\xD0"=>[92,"\x7D"],"\xD1"=>[95,"\x7D"],"\xD2"=>[137,"\x7D"],"\xD3"=>[142,"\x7D"],"\xD4"=>[150,"\x7D"],"\xD5"=>[74,"\x7D"],"\xD6"=>[90,"\x7D"],"\xD7"=>[98,"\x7D"],"\xD8"=>[107,"\x7D"],"\xD9"=>[140,"\x7D"],"\xDA"=>[146,"\x7D"],"\xDB"=>[102,"\x7D"],"\xDC"=>[113,"\x7D"],"\xDD"=>[121,"\x7D"],"\xDE"=>[128,"\x7D"],"\xDF"=>[12,"\x7D"],"\xE0"=>[93,"\x3C"],"\xE1"=>[138,"\x3C"],"\xE2"=>[75,"\x3C"],"\xE3"=>[91,"\x3C"],"\xE4"=>[108,"\x3C"],"\xE5"=>[103,"\x3C"],"\xE6"=>[114,"\x3C"],"\xE7"=>[14,"\x3C"],"\xE8"=>[93,"\x60"],"\xE9"=>[138,"\x60"],"\xEA"=>[75,"\x60"],"\xEB"=>[91,"\x60"],"\xEC"=>[108,"\x60"],"\xED"=>[103,"\x60"],"\xEE"=>[114,"\x60"],"\xEF"=>[14,"\x60"],"\xF0"=>[93,"\x7B"],"\xF1"=>[138,"\x7B"],"\xF2"=>[75,"\x7B"],"\xF3"=>[91,"\x7B"],"\xF4"=>[108,"\x7B"],"\xF5"=>[103,"\x7B"],"\xF6"=>[114,"\x7B"],"\xF7"=>[14,"\x7B"],"\xF8"=>[132,null],"\xF9"=>[156,null],"\xFA"=>[163,null],"\xFB"=>[184,null],"\xFC"=>[205,null],"\xFD"=>[160,null],"\xFE"=>[30,null],"\xFF"=>[39,null],],["\x00"=>[93,"\x3D"],"\x01"=>[138,"\x3D"],"\x02"=>[75,"\x3D"],"\x03"=>[91,"\x3D"],"\x04"=>[108,"\x3D"],"\x05"=>[103,"\x3D"],"\x06"=>[114,"\x3D"],"\x07"=>[14,"\x3D"],"\x08"=>[93,"A"],"\x09"=>[138,"A"],"\x0A"=>[75,"A"],"\x0B"=>[91,"A"],"\x0C"=>[108,"A"],"\x0D"=>[103,"A"],"\x0E"=>[114,"A"],"\x0F"=>[14,"A"],"\x10"=>[93,"_"],"\x11"=>[138,"_"],"\x12"=>[75,"_"],"\x13"=>[91,"_"],"\x14"=>[108,"_"],"\x15"=>[103,"_"],"\x16"=>[114,"_"],"\x17"=>[14,"_"],"\x18"=>[93,"b"],"\x19"=>[138,"b"],"\x1A"=>[75,"b"],"\x1B"=>[91,"b"],"\x1C"=>[108,"b"],"\x1D"=>[103,"b"],"\x1E"=>[114,"b"],"\x1F"=>[14,"b"],"\x20"=>[93,"d"],"\x21"=>[138,"d"],"\x22"=>[75,"d"],"\x23"=>[91,"d"],"\x24"=>[108,"d"],"\x25"=>[103,"d"],"\x26"=>[114,"d"],"\x27"=>[14,"d"],"\x28"=>[93,"f"],"\x29"=>[138,"f"],"\x2A"=>[75,"f"],"\x2B"=>[91,"f"],"\x2C"=>[108,"f"],"-"=>[103,"f"],"."=>[114,"f"],"\x2F"=>[14,"f"],[93,"g"],[138,"g"],[75,"g"],[91,"g"],[108,"g"],[103,"g"],[114,"g"],[14,"g"],[93,"h"],[138,"h"],"\x3A"=>[75,"h"],"\x3B"=>[91,"h"],"\x3C"=>[108,"h"],"\x3D"=>[103,"h"],"\x3E"=>[114,"h"],"\x3F"=>[14,"h"],"\x40"=>[93,"l"],"A"=>[138,"l"],"B"=>[75,"l"],"C"=>[91,"l"],"D"=>[108,"l"],"E"=>[103,"l"],"F"=>[114,"l"],"G"=>[14,"l"],"H"=>[93,"m"],"I"=>[138,"m"],"J"=>[75,"m"],"K"=>[91,"m"],"L"=>[108,"m"],"M"=>[103,"m"],"N"=>[114,"m"],"O"=>[14,"m"],"P"=>[93,"n"],"Q"=>[138,"n"],"R"=>[75,"n"],"S"=>[91,"n"],"T"=>[108,"n"],"U"=>[103,"n"],"V"=>[114,"n"],"W"=>[14,"n"],"X"=>[93,"p"],"Y"=>[138,"p"],"Z"=>[75,"p"],"\x5B"=>[91,"p"],"\x5C"=>[108,"p"],"\x5D"=>[103,"p"],"\x5E"=>[114,"p"],"_"=>[14,"p"],"\x60"=>[93,"r"],"a"=>[138,"r"],"b"=>[75,"r"],"c"=>[91,"r"],"d"=>[108,"r"],"e"=>[103,"r"],"f"=>[114,"r"],"g"=>[14,"r"],"h"=>[93,"u"],"i"=>[138,"u"],"j"=>[75,"u"],"k"=>[91,"u"],"l"=>[108,"u"],"m"=>[103,"u"],"n"=>[114,"u"],"o"=>[14,"u"],"p"=>[94,"\x3A"],"q"=>[76,"\x3A"],"r"=>[104,"\x3A"],"s"=>[16,"\x3A"],"t"=>[94,"B"],"u"=>[76,"B"],"v"=>[104,"B"],"w"=>[16,"B"],"x"=>[94,"C"],"y"=>[76,"C"],"z"=>[104,"C"],"\x7B"=>[16,"C"],"\x7C"=>[94,"D"],"\x7D"=>[76,"D"],"~"=>[104,"D"],"\x7F"=>[16,"D"],"\x80"=>[94,"E"],"\x81"=>[76,"E"],"\x82"=>[104,"E"],"\x83"=>[16,"E"],"\x84"=>[94,"F"],"\x85"=>[76,"F"],"\x86"=>[104,"F"],"\x87"=>[16,"F"],"\x88"=>[94,"G"],"\x89"=>[76,"G"],"\x8A"=>[104,"G"],"\x8B"=>[16,"G"],"\x8C"=>[94,"H"],"\x8D"=>[76,"H"],"\x8E"=>[104,"H"],"\x8F"=>[16,"H"],"\x90"=>[94,"I"],"\x91"=>[76,"I"],"\x92"=>[104,"I"],"\x93"=>[16,"I"],"\x94"=>[94,"J"],"\x95"=>[76,"J"],"\x96"=>[104,"J"],"\x97"=>[16,"J"],"\x98"=>[94,"K"],"\x99"=>[76,"K"],"\x9A"=>[104,"K"],"\x9B"=>[16,"K"],"\x9C"=>[94,"L"],"\x9D"=>[76,"L"],"\x9E"=>[104,"L"],"\x9F"=>[16,"L"],"\xA0"=>[94,"M"],"\xA1"=>[76,"M"],"\xA2"=>[104,"M"],"\xA3"=>[16,"M"],"\xA4"=>[94,"N"],"\xA5"=>[76,"N"],"\xA6"=>[104,"N"],"\xA7"=>[16,"N"],"\xA8"=>[94,"O"],"\xA9"=>[76,"O"],"\xAA"=>[104,"O"],"\xAB"=>[16,"O"],"\xAC"=>[94,"P"],"\xAD"=>[76,"P"],"\xAE"=>[104,"P"],"\xAF"=>[16,"P"],"\xB0"=>[94,"Q"],"\xB1"=>[76,"Q"],"\xB2"=>[104,"Q"],"\xB3"=>[16,"Q"],"\xB4"=>[94,"R"],"\xB5"=>[76,"R"],"\xB6"=>[104,"R"],"\xB7"=>[16,"R"],"\xB8"=>[94,"S"],"\xB9"=>[76,"S"],"\xBA"=>[104,"S"],"\xBB"=>[16,"S"],"\xBC"=>[94,"T"],"\xBD"=>[76,"T"],"\xBE"=>[104,"T"],"\xBF"=>[16,"T"],"\xC0"=>[94,"U"],"\xC1"=>[76,"U"],"\xC2"=>[104,"U"],"\xC3"=>[16,"U"],"\xC4"=>[94,"V"],"\xC5"=>[76,"V"],"\xC6"=>[104,"V"],"\xC7"=>[16,"V"],"\xC8"=>[94,"W"],"\xC9"=>[76,"W"],"\xCA"=>[104,"W"],"\xCB"=>[16,"W"],"\xCC"=>[94,"Y"],"\xCD"=>[76,"Y"],"\xCE"=>[104,"Y"],"\xCF"=>[16,"Y"],"\xD0"=>[94,"j"],"\xD1"=>[76,"j"],"\xD2"=>[104,"j"],"\xD3"=>[16,"j"],"\xD4"=>[94,"k"],"\xD5"=>[76,"k"],"\xD6"=>[104,"k"],"\xD7"=>[16,"k"],"\xD8"=>[94,"q"],"\xD9"=>[76,"q"],"\xDA"=>[104,"q"],"\xDB"=>[16,"q"],"\xDC"=>[94,"v"],"\xDD"=>[76,"v"],"\xDE"=>[104,"v"],"\xDF"=>[16,"v"],"\xE0"=>[94,"w"],"\xE1"=>[76,"w"],"\xE2"=>[104,"w"],"\xE3"=>[16,"w"],"\xE4"=>[94,"x"],"\xE5"=>[76,"x"],"\xE6"=>[104,"x"],"\xE7"=>[16,"x"],"\xE8"=>[94,"y"],"\xE9"=>[76,"y"],"\xEA"=>[104,"y"],"\xEB"=>[16,"y"],"\xEC"=>[94,"z"],"\xED"=>[76,"z"],"\xEE"=>[104,"z"],"\xEF"=>[16,"z"],"\xF0"=>[77,"\x26"],"\xF1"=>[18,"\x26"],"\xF2"=>[77,"\x2A"],"\xF3"=>[18,"\x2A"],"\xF4"=>[77,"\x2C"],"\xF5"=>[18,"\x2C"],"\xF6"=>[77,"\x3B"],"\xF7"=>[18,"\x3B"],"\xF8"=>[77,"X"],"\xF9"=>[18,"X"],"\xFA"=>[77,"Z"],"\xFB"=>[18,"Z"],"\xFC"=>[79,null],"\xFD"=>[86,null],"\xFE"=>[85,null],"\xFF"=>[19,null],],["\x00"=>[77,"\x7C0"],"\x01"=>[18,"\x7C0"],"\x02"=>[77,"\x7C1"],"\x03"=>[18,"\x7C1"],"\x04"=>[77,"\x7C2"],"\x05"=>[18,"\x7C2"],"\x06"=>[77,"\x7Ca"],"\x07"=>[18,"\x7Ca"],"\x08"=>[77,"\x7Cc"],"\x09"=>[18,"\x7Cc"],"\x0A"=>[77,"\x7Ce"],"\x0B"=>[18,"\x7Ce"],"\x0C"=>[77,"\x7Ci"],"\x0D"=>[18,"\x7Ci"],"\x0E"=>[77,"\x7Co"],"\x0F"=>[18,"\x7Co"],"\x10"=>[77,"\x7Cs"],"\x11"=>[18,"\x7Cs"],"\x12"=>[77,"\x7Ct"],"\x13"=>[18,"\x7Ct"],"\x14"=>[0,"\x7C\x20"],"\x15"=>[0,"\x7C\x25"],"\x16"=>[0,"\x7C-"],"\x17"=>[0,"\x7C."],"\x18"=>[0,"\x7C\x2F"],"\x19"=>[0,"\x7C3"],"\x1A"=>[0,"\x7C4"],"\x1B"=>[0,"\x7C5"],"\x1C"=>[0,"\x7C6"],"\x1D"=>[0,"\x7C7"],"\x1E"=>[0,"\x7C8"],"\x1F"=>[0,"\x7C9"],"\x20"=>[0,"\x7C\x3D"],"\x21"=>[0,"\x7CA"],"\x22"=>[0,"\x7C_"],"\x23"=>[0,"\x7Cb"],"\x24"=>[0,"\x7Cd"],"\x25"=>[0,"\x7Cf"],"\x26"=>[0,"\x7Cg"],"\x27"=>[0,"\x7Ch"],"\x28"=>[0,"\x7Cl"],"\x29"=>[0,"\x7Cm"],"\x2A"=>[0,"\x7Cn"],"\x2B"=>[0,"\x7Cp"],"\x2C"=>[0,"\x7Cr"],"-"=>[0,"\x7Cu"],"."=>[100,"\x7C"],"\x2F"=>[110,"\x7C"],[111,"\x7C"],[115,"\x7C"],[116,"\x7C"],[118,"\x7C"],[119,"\x7C"],[122,"\x7C"],[123,"\x7C"],[125,"\x7C"],[126,"\x7C"],[129,"\x7C"],"\x3A"=>[143,"\x7C"],"\x3B"=>[148,"\x7C"],"\x3C"=>[151,"\x7C"],"\x3D"=>[153,"\x7C"],"\x3E"=>[83,"\x7C"],"\x3F"=>[10,"\x7C"],"\x40"=>[0,"\x230"],"A"=>[0,"\x231"],"B"=>[0,"\x232"],"C"=>[0,"\x23a"],"D"=>[0,"\x23c"],"E"=>[0,"\x23e"],"F"=>[0,"\x23i"],"G"=>[0,"\x23o"],"H"=>[0,"\x23s"],"I"=>[0,"\x23t"],"J"=>[73,"\x23"],"K"=>[88,"\x23"],"L"=>[89,"\x23"],"M"=>[96,"\x23"],"N"=>[97,"\x23"],"O"=>[99,"\x23"],"P"=>[106,"\x23"],"Q"=>[136,"\x23"],"R"=>[139,"\x23"],"S"=>[141,"\x23"],"T"=>[145,"\x23"],"U"=>[147,"\x23"],"V"=>[149,"\x23"],"W"=>[101,"\x23"],"X"=>[112,"\x23"],"Y"=>[117,"\x23"],"Z"=>[120,"\x23"],"\x5B"=>[124,"\x23"],"\x5C"=>[127,"\x23"],"\x5D"=>[144,"\x23"],"\x5E"=>[152,"\x23"],"_"=>[11,"\x23"],"\x60"=>[0,"\x3E0"],"a"=>[0,"\x3E1"],"b"=>[0,"\x3E2"],"c"=>[0,"\x3Ea"],"d"=>[0,"\x3Ec"],"e"=>[0,"\x3Ee"],"f"=>[0,"\x3Ei"],"g"=>[0,"\x3Eo"],"h"=>[0,"\x3Es"],"i"=>[0,"\x3Et"],"j"=>[73,"\x3E"],"k"=>[88,"\x3E"],"l"=>[89,"\x3E"],"m"=>[96,"\x3E"],"n"=>[97,"\x3E"],"o"=>[99,"\x3E"],"p"=>[106,"\x3E"],"q"=>[136,"\x3E"],"r"=>[139,"\x3E"],"s"=>[141,"\x3E"],"t"=>[145,"\x3E"],"u"=>[147,"\x3E"],"v"=>[149,"\x3E"],"w"=>[101,"\x3E"],"x"=>[112,"\x3E"],"y"=>[117,"\x3E"],"z"=>[120,"\x3E"],"\x7B"=>[124,"\x3E"],"\x7C"=>[127,"\x3E"],"\x7D"=>[144,"\x3E"],"~"=>[152,"\x3E"],"\x7F"=>[11,"\x3E"],"\x80"=>[92,"\x00"],"\x81"=>[95,"\x00"],"\x82"=>[137,"\x00"],"\x83"=>[142,"\x00"],"\x84"=>[150,"\x00"],"\x85"=>[74,"\x00"],"\x86"=>[90,"\x00"],"\x87"=>[98,"\x00"],"\x88"=>[107,"\x00"],"\x89"=>[140,"\x00"],"\x8A"=>[146,"\x00"],"\x8B"=>[102,"\x00"],"\x8C"=>[113,"\x00"],"\x8D"=>[121,"\x00"],"\x8E"=>[128,"\x00"],"\x8F"=>[12,"\x00"],"\x90"=>[92,"\x24"],"\x91"=>[95,"\x24"],"\x92"=>[137,"\x24"],"\x93"=>[142,"\x24"],"\x94"=>[150,"\x24"],"\x95"=>[74,"\x24"],"\x96"=>[90,"\x24"],"\x97"=>[98,"\x24"],"\x98"=>[107,"\x24"],"\x99"=>[140,"\x24"],"\x9A"=>[146,"\x24"],"\x9B"=>[102,"\x24"],"\x9C"=>[113,"\x24"],"\x9D"=>[121,"\x24"],"\x9E"=>[128,"\x24"],"\x9F"=>[12,"\x24"],"\xA0"=>[92,"\x40"],"\xA1"=>[95,"\x40"],"\xA2"=>[137,"\x40"],"\xA3"=>[142,"\x40"],"\xA4"=>[150,"\x40"],"\xA5"=>[74,"\x40"],"\xA6"=>[90,"\x40"],"\xA7"=>[98,"\x40"],"\xA8"=>[107,"\x40"],"\xA9"=>[140,"\x40"],"\xAA"=>[146,"\x40"],"\xAB"=>[102,"\x40"],"\xAC"=>[113,"\x40"],"\xAD"=>[121,"\x40"],"\xAE"=>[128,"\x40"],"\xAF"=>[12,"\x40"],"\xB0"=>[92,"\x5B"],"\xB1"=>[95,"\x5B"],"\xB2"=>[137,"\x5B"],"\xB3"=>[142,"\x5B"],"\xB4"=>[150,"\x5B"],"\xB5"=>[74,"\x5B"],"\xB6"=>[90,"\x5B"],"\xB7"=>[98,"\x5B"],"\xB8"=>[107,"\x5B"],"\xB9"=>[140,"\x5B"],"\xBA"=>[146,"\x5B"],"\xBB"=>[102,"\x5B"],"\xBC"=>[113,"\x5B"],"\xBD"=>[121,"\x5B"],"\xBE"=>[128,"\x5B"],"\xBF"=>[12,"\x5B"],"\xC0"=>[92,"\x5D"],"\xC1"=>[95,"\x5D"],"\xC2"=>[137,"\x5D"],"\xC3"=>[142,"\x5D"],"\xC4"=>[150,"\x5D"],"\xC5"=>[74,"\x5D"],"\xC6"=>[90,"\x5D"],"\xC7"=>[98,"\x5D"],"\xC8"=>[107,"\x5D"],"\xC9"=>[140,"\x5D"],"\xCA"=>[146,"\x5D"],"\xCB"=>[102,"\x5D"],"\xCC"=>[113,"\x5D"],"\xCD"=>[121,"\x5D"],"\xCE"=>[128,"\x5D"],"\xCF"=>[12,"\x5D"],"\xD0"=>[92,"~"],"\xD1"=>[95,"~"],"\xD2"=>[137,"~"],"\xD3"=>[142,"~"],"\xD4"=>[150,"~"],"\xD5"=>[74,"~"],"\xD6"=>[90,"~"],"\xD7"=>[98,"~"],"\xD8"=>[107,"~"],"\xD9"=>[140,"~"],"\xDA"=>[146,"~"],"\xDB"=>[102,"~"],"\xDC"=>[113,"~"],"\xDD"=>[121,"~"],"\xDE"=>[128,"~"],"\xDF"=>[12,"~"],"\xE0"=>[93,"\x5E"],"\xE1"=>[138,"\x5E"],"\xE2"=>[75,"\x5E"],"\xE3"=>[91,"\x5E"],"\xE4"=>[108,"\x5E"],"\xE5"=>[103,"\x5E"],"\xE6"=>[114,"\x5E"],"\xE7"=>[14,"\x5E"],"\xE8"=>[93,"\x7D"],"\xE9"=>[138,"\x7D"],"\xEA"=>[75,"\x7D"],"\xEB"=>[91,"\x7D"],"\xEC"=>[108,"\x7D"],"\xED"=>[103,"\x7D"],"\xEE"=>[114,"\x7D"],"\xEF"=>[14,"\x7D"],"\xF0"=>[94,"\x3C"],"\xF1"=>[76,"\x3C"],"\xF2"=>[104,"\x3C"],"\xF3"=>[16,"\x3C"],"\xF4"=>[94,"\x60"],"\xF5"=>[76,"\x60"],"\xF6"=>[104,"\x60"],"\xF7"=>[16,"\x60"],"\xF8"=>[94,"\x7B"],"\xF9"=>[76,"\x7B"],"\xFA"=>[104,"\x7B"],"\xFB"=>[16,"\x7B"],"\xFC"=>[133,null],"\xFD"=>[164,null],"\xFE"=>[161,null],"\xFF"=>[31,null],],["\x00"=>[93,"\xB0"],"\x01"=>[138,"\xB0"],"\x02"=>[75,"\xB0"],"\x03"=>[91,"\xB0"],"\x04"=>[108,"\xB0"],"\x05"=>[103,"\xB0"],"\x06"=>[114,"\xB0"],"\x07"=>[14,"\xB0"],"\x08"=>[93,"\xB1"],"\x09"=>[138,"\xB1"],"\x0A"=>[75,"\xB1"],"\x0B"=>[91,"\xB1"],"\x0C"=>[108,"\xB1"],"\x0D"=>[103,"\xB1"],"\x0E"=>[114,"\xB1"],"\x0F"=>[14,"\xB1"],"\x10"=>[93,"\xB3"],"\x11"=>[138,"\xB3"],"\x12"=>[75,"\xB3"],"\x13"=>[91,"\xB3"],"\x14"=>[108,"\xB3"],"\x15"=>[103,"\xB3"],"\x16"=>[114,"\xB3"],"\x17"=>[14,"\xB3"],"\x18"=>[93,"\xD1"],"\x19"=>[138,"\xD1"],"\x1A"=>[75,"\xD1"],"\x1B"=>[91,"\xD1"],"\x1C"=>[108,"\xD1"],"\x1D"=>[103,"\xD1"],"\x1E"=>[114,"\xD1"],"\x1F"=>[14,"\xD1"],"\x20"=>[93,"\xD8"],"\x21"=>[138,"\xD8"],"\x22"=>[75,"\xD8"],"\x23"=>[91,"\xD8"],"\x24"=>[108,"\xD8"],"\x25"=>[103,"\xD8"],"\x26"=>[114,"\xD8"],"\x27"=>[14,"\xD8"],"\x28"=>[93,"\xD9"],"\x29"=>[138,"\xD9"],"\x2A"=>[75,"\xD9"],"\x2B"=>[91,"\xD9"],"\x2C"=>[108,"\xD9"],"-"=>[103,"\xD9"],"."=>[114,"\xD9"],"\x2F"=>[14,"\xD9"],[93,"\xE3"],[138,"\xE3"],[75,"\xE3"],[91,"\xE3"],[108,"\xE3"],[103,"\xE3"],[114,"\xE3"],[14,"\xE3"],[93,"\xE5"],[138,"\xE5"],"\x3A"=>[75,"\xE5"],"\x3B"=>[91,"\xE5"],"\x3C"=>[108,"\xE5"],"\x3D"=>[103,"\xE5"],"\x3E"=>[114,"\xE5"],"\x3F"=>[14,"\xE5"],"\x40"=>[93,"\xE6"],"A"=>[138,"\xE6"],"B"=>[75,"\xE6"],"C"=>[91,"\xE6"],"D"=>[108,"\xE6"],"E"=>[103,"\xE6"],"F"=>[114,"\xE6"],"G"=>[14,"\xE6"],"H"=>[94,"\x81"],"I"=>[76,"\x81"],"J"=>[104,"\x81"],"K"=>[16,"\x81"],"L"=>[94,"\x84"],"M"=>[76,"\x84"],"N"=>[104,"\x84"],"O"=>[16,"\x84"],"P"=>[94,"\x85"],"Q"=>[76,"\x85"],"R"=>[104,"\x85"],"S"=>[16,"\x85"],"T"=>[94,"\x86"],"U"=>[76,"\x86"],"V"=>[104,"\x86"],"W"=>[16,"\x86"],"X"=>[94,"\x88"],"Y"=>[76,"\x88"],"Z"=>[104,"\x88"],"\x5B"=>[16,"\x88"],"\x5C"=>[94,"\x92"],"\x5D"=>[76,"\x92"],"\x5E"=>[104,"\x92"],"_"=>[16,"\x92"],"\x60"=>[94,"\x9A"],"a"=>[76,"\x9A"],"b"=>[104,"\x9A"],"c"=>[16,"\x9A"],"d"=>[94,"\x9C"],"e"=>[76,"\x9C"],"f"=>[104,"\x9C"],"g"=>[16,"\x9C"],"h"=>[94,"\xA0"],"i"=>[76,"\xA0"],"j"=>[104,"\xA0"],"k"=>[16,"\xA0"],"l"=>[94,"\xA3"],"m"=>[76,"\xA3"],"n"=>[104,"\xA3"],"o"=>[16,"\xA3"],"p"=>[94,"\xA4"],"q"=>[76,"\xA4"],"r"=>[104,"\xA4"],"s"=>[16,"\xA4"],"t"=>[94,"\xA9"],"u"=>[76,"\xA9"],"v"=>[104,"\xA9"],"w"=>[16,"\xA9"],"x"=>[94,"\xAA"],"y"=>[76,"\xAA"],"z"=>[104,"\xAA"],"\x7B"=>[16,"\xAA"],"\x7C"=>[94,"\xAD"],"\x7D"=>[76,"\xAD"],"~"=>[104,"\xAD"],"\x7F"=>[16,"\xAD"],"\x80"=>[94,"\xB2"],"\x81"=>[76,"\xB2"],"\x82"=>[104,"\xB2"],"\x83"=>[16,"\xB2"],"\x84"=>[94,"\xB5"],"\x85"=>[76,"\xB5"],"\x86"=>[104,"\xB5"],"\x87"=>[16,"\xB5"],"\x88"=>[94,"\xB9"],"\x89"=>[76,"\xB9"],"\x8A"=>[104,"\xB9"],"\x8B"=>[16,"\xB9"],"\x8C"=>[94,"\xBA"],"\x8D"=>[76,"\xBA"],"\x8E"=>[104,"\xBA"],"\x8F"=>[16,"\xBA"],"\x90"=>[94,"\xBB"],"\x91"=>[76,"\xBB"],"\x92"=>[104,"\xBB"],"\x93"=>[16,"\xBB"],"\x94"=>[94,"\xBD"],"\x95"=>[76,"\xBD"],"\x96"=>[104,"\xBD"],"\x97"=>[16,"\xBD"],"\x98"=>[94,"\xBE"],"\x99"=>[76,"\xBE"],"\x9A"=>[104,"\xBE"],"\x9B"=>[16,"\xBE"],"\x9C"=>[94,"\xC4"],"\x9D"=>[76,"\xC4"],"\x9E"=>[104,"\xC4"],"\x9F"=>[16,"\xC4"],"\xA0"=>[94,"\xC6"],"\xA1"=>[76,"\xC6"],"\xA2"=>[104,"\xC6"],"\xA3"=>[16,"\xC6"],"\xA4"=>[94,"\xE4"],"\xA5"=>[76,"\xE4"],"\xA6"=>[104,"\xE4"],"\xA7"=>[16,"\xE4"],"\xA8"=>[94,"\xE8"],"\xA9"=>[76,"\xE8"],"\xAA"=>[104,"\xE8"],"\xAB"=>[16,"\xE8"],"\xAC"=>[94,"\xE9"],"\xAD"=>[76,"\xE9"],"\xAE"=>[104,"\xE9"],"\xAF"=>[16,"\xE9"],"\xB0"=>[77,"\x01"],"\xB1"=>[18,"\x01"],"\xB2"=>[77,"\x87"],"\xB3"=>[18,"\x87"],"\xB4"=>[77,"\x89"],"\xB5"=>[18,"\x89"],"\xB6"=>[77,"\x8A"],"\xB7"=>[18,"\x8A"],"\xB8"=>[77,"\x8B"],"\xB9"=>[18,"\x8B"],"\xBA"=>[77,"\x8C"],"\xBB"=>[18,"\x8C"],"\xBC"=>[77,"\x8D"],"\xBD"=>[18,"\x8D"],"\xBE"=>[77,"\x8F"],"\xBF"=>[18,"\x8F"],"\xC0"=>[77,"\x93"],"\xC1"=>[18,"\x93"],"\xC2"=>[77,"\x95"],"\xC3"=>[18,"\x95"],"\xC4"=>[77,"\x96"],"\xC5"=>[18,"\x96"],"\xC6"=>[77,"\x97"],"\xC7"=>[18,"\x97"],"\xC8"=>[77,"\x98"],"\xC9"=>[18,"\x98"],"\xCA"=>[77,"\x9B"],"\xCB"=>[18,"\x9B"],"\xCC"=>[77,"\x9D"],"\xCD"=>[18,"\x9D"],"\xCE"=>[77,"\x9E"],"\xCF"=>[18,"\x9E"],"\xD0"=>[77,"\xA5"],"\xD1"=>[18,"\xA5"],"\xD2"=>[77,"\xA6"],"\xD3"=>[18,"\xA6"],"\xD4"=>[77,"\xA8"],"\xD5"=>[18,"\xA8"],"\xD6"=>[77,"\xAE"],"\xD7"=>[18,"\xAE"],"\xD8"=>[77,"\xAF"],"\xD9"=>[18,"\xAF"],"\xDA"=>[77,"\xB4"],"\xDB"=>[18,"\xB4"],"\xDC"=>[77,"\xB6"],"\xDD"=>[18,"\xB6"],"\xDE"=>[77,"\xB7"],"\xDF"=>[18,"\xB7"],"\xE0"=>[77,"\xBC"],"\xE1"=>[18,"\xBC"],"\xE2"=>[77,"\xBF"],"\xE3"=>[18,"\xBF"],"\xE4"=>[77,"\xC5"],"\xE5"=>[18,"\xC5"],"\xE6"=>[77,"\xE7"],"\xE7"=>[18,"\xE7"],"\xE8"=>[77,"\xEF"],"\xE9"=>[18,"\xEF"],"\xEA"=>[0,"\x09"],"\xEB"=>[0,"\x8E"],"\xEC"=>[0,"\x90"],"\xED"=>[0,"\x91"],"\xEE"=>[0,"\x94"],"\xEF"=>[0,"\x9F"],"\xF0"=>[0,"\xAB"],"\xF1"=>[0,"\xCE"],"\xF2"=>[0,"\xD7"],"\xF3"=>[0,"\xE1"],"\xF4"=>[0,"\xEC"],"\xF5"=>[0,"\xED"],"\xF6"=>[225,null],"\xF7"=>[251,null],"\xF8"=>[218,null],"\xF9"=>[229,null],"\xFA"=>[244,null],"\xFB"=>[231,null],"\xFC"=>[236,null],"\xFD"=>[256,null],"\xFE"=>[32,null],"\xFF"=>[52,null],],["\x00"=>[92,"\x5C"],"\x01"=>[95,"\x5C"],"\x02"=>[137,"\x5C"],"\x03"=>[142,"\x5C"],"\x04"=>[150,"\x5C"],"\x05"=>[74,"\x5C"],"\x06"=>[90,"\x5C"],"\x07"=>[98,"\x5C"],"\x08"=>[107,"\x5C"],"\x09"=>[140,"\x5C"],"\x0A"=>[146,"\x5C"],"\x0B"=>[102,"\x5C"],"\x0C"=>[113,"\x5C"],"\x0D"=>[121,"\x5C"],"\x0E"=>[128,"\x5C"],"\x0F"=>[12,"\x5C"],"\x10"=>[92,"\xC3"],"\x11"=>[95,"\xC3"],"\x12"=>[137,"\xC3"],"\x13"=>[142,"\xC3"],"\x14"=>[150,"\xC3"],"\x15"=>[74,"\xC3"],"\x16"=>[90,"\xC3"],"\x17"=>[98,"\xC3"],"\x18"=>[107,"\xC3"],"\x19"=>[140,"\xC3"],"\x1A"=>[146,"\xC3"],"\x1B"=>[102,"\xC3"],"\x1C"=>[113,"\xC3"],"\x1D"=>[121,"\xC3"],"\x1E"=>[128,"\xC3"],"\x1F"=>[12,"\xC3"],"\x20"=>[92,"\xD0"],"\x21"=>[95,"\xD0"],"\x22"=>[137,"\xD0"],"\x23"=>[142,"\xD0"],"\x24"=>[150,"\xD0"],"\x25"=>[74,"\xD0"],"\x26"=>[90,"\xD0"],"\x27"=>[98,"\xD0"],"\x28"=>[107,"\xD0"],"\x29"=>[140,"\xD0"],"\x2A"=>[146,"\xD0"],"\x2B"=>[102,"\xD0"],"\x2C"=>[113,"\xD0"],"-"=>[121,"\xD0"],"."=>[128,"\xD0"],"\x2F"=>[12,"\xD0"],[93,"\x80"],[138,"\x80"],[75,"\x80"],[91,"\x80"],[108,"\x80"],[103,"\x80"],[114,"\x80"],[14,"\x80"],[93,"\x82"],[138,"\x82"],"\x3A"=>[75,"\x82"],"\x3B"=>[91,"\x82"],"\x3C"=>[108,"\x82"],"\x3D"=>[103,"\x82"],"\x3E"=>[114,"\x82"],"\x3F"=>[14,"\x82"],"\x40"=>[93,"\x83"],"A"=>[138,"\x83"],"B"=>[75,"\x83"],"C"=>[91,"\x83"],"D"=>[108,"\x83"],"E"=>[103,"\x83"],"F"=>[114,"\x83"],"G"=>[14,"\x83"],"H"=>[93,"\xA2"],"I"=>[138,"\xA2"],"J"=>[75,"\xA2"],"K"=>[91,"\xA2"],"L"=>[108,"\xA2"],"M"=>[103,"\xA2"],"N"=>[114,"\xA2"],"O"=>[14,"\xA2"],"P"=>[93,"\xB8"],"Q"=>[138,"\xB8"],"R"=>[75,"\xB8"],"S"=>[91,"\xB8"],"T"=>[108,"\xB8"],"U"=>[103,"\xB8"],"V"=>[114,"\xB8"],"W"=>[14,"\xB8"],"X"=>[93,"\xC2"],"Y"=>[138,"\xC2"],"Z"=>[75,"\xC2"],"\x5B"=>[91,"\xC2"],"\x5C"=>[108,"\xC2"],"\x5D"=>[103,"\xC2"],"\x5E"=>[114,"\xC2"],"_"=>[14,"\xC2"],"\x60"=>[93,"\xE0"],"a"=>[138,"\xE0"],"b"=>[75,"\xE0"],"c"=>[91,"\xE0"],"d"=>[108,"\xE0"],"e"=>[103,"\xE0"],"f"=>[114,"\xE0"],"g"=>[14,"\xE0"],"h"=>[93,"\xE2"],"i"=>[138,"\xE2"],"j"=>[75,"\xE2"],"k"=>[91,"\xE2"],"l"=>[108,"\xE2"],"m"=>[103,"\xE2"],"n"=>[114,"\xE2"],"o"=>[14,"\xE2"],"p"=>[94,"\x99"],"q"=>[76,"\x99"],"r"=>[104,"\x99"],"s"=>[16,"\x99"],"t"=>[94,"\xA1"],"u"=>[76,"\xA1"],"v"=>[104,"\xA1"],"w"=>[16,"\xA1"],"x"=>[94,"\xA7"],"y"=>[76,"\xA7"],"z"=>[104,"\xA7"],"\x7B"=>[16,"\xA7"],"\x7C"=>[94,"\xAC"],"\x7D"=>[76,"\xAC"],"~"=>[104,"\xAC"],"\x7F"=>[16,"\xAC"],"\x80"=>[94,"\xB0"],"\x81"=>[76,"\xB0"],"\x82"=>[104,"\xB0"],"\x83"=>[16,"\xB0"],"\x84"=>[94,"\xB1"],"\x85"=>[76,"\xB1"],"\x86"=>[104,"\xB1"],"\x87"=>[16,"\xB1"],"\x88"=>[94,"\xB3"],"\x89"=>[76,"\xB3"],"\x8A"=>[104,"\xB3"],"\x8B"=>[16,"\xB3"],"\x8C"=>[94,"\xD1"],"\x8D"=>[76,"\xD1"],"\x8E"=>[104,"\xD1"],"\x8F"=>[16,"\xD1"],"\x90"=>[94,"\xD8"],"\x91"=>[76,"\xD8"],"\x92"=>[104,"\xD8"],"\x93"=>[16,"\xD8"],"\x94"=>[94,"\xD9"],"\x95"=>[76,"\xD9"],"\x96"=>[104,"\xD9"],"\x97"=>[16,"\xD9"],"\x98"=>[94,"\xE3"],"\x99"=>[76,"\xE3"],"\x9A"=>[104,"\xE3"],"\x9B"=>[16,"\xE3"],"\x9C"=>[94,"\xE5"],"\x9D"=>[76,"\xE5"],"\x9E"=>[104,"\xE5"],"\x9F"=>[16,"\xE5"],"\xA0"=>[94,"\xE6"],"\xA1"=>[76,"\xE6"],"\xA2"=>[104,"\xE6"],"\xA3"=>[16,"\xE6"],"\xA4"=>[77,"\x81"],"\xA5"=>[18,"\x81"],"\xA6"=>[77,"\x84"],"\xA7"=>[18,"\x84"],"\xA8"=>[77,"\x85"],"\xA9"=>[18,"\x85"],"\xAA"=>[77,"\x86"],"\xAB"=>[18,"\x86"],"\xAC"=>[77,"\x88"],"\xAD"=>[18,"\x88"],"\xAE"=>[77,"\x92"],"\xAF"=>[18,"\x92"],"\xB0"=>[77,"\x9A"],"\xB1"=>[18,"\x9A"],"\xB2"=>[77,"\x9C"],"\xB3"=>[18,"\x9C"],"\xB4"=>[77,"\xA0"],"\xB5"=>[18,"\xA0"],"\xB6"=>[77,"\xA3"],"\xB7"=>[18,"\xA3"],"\xB8"=>[77,"\xA4"],"\xB9"=>[18,"\xA4"],"\xBA"=>[77,"\xA9"],"\xBB"=>[18,"\xA9"],"\xBC"=>[77,"\xAA"],"\xBD"=>[18,"\xAA"],"\xBE"=>[77,"\xAD"],"\xBF"=>[18,"\xAD"],"\xC0"=>[77,"\xB2"],"\xC1"=>[18,"\xB2"],"\xC2"=>[77,"\xB5"],"\xC3"=>[18,"\xB5"],"\xC4"=>[77,"\xB9"],"\xC5"=>[18,"\xB9"],"\xC6"=>[77,"\xBA"],"\xC7"=>[18,"\xBA"],"\xC8"=>[77,"\xBB"],"\xC9"=>[18,"\xBB"],"\xCA"=>[77,"\xBD"],"\xCB"=>[18,"\xBD"],"\xCC"=>[77,"\xBE"],"\xCD"=>[18,"\xBE"],"\xCE"=>[77,"\xC4"],"\xCF"=>[18,"\xC4"],"\xD0"=>[77,"\xC6"],"\xD1"=>[18,"\xC6"],"\xD2"=>[77,"\xE4"],"\xD3"=>[18,"\xE4"],"\xD4"=>[77,"\xE8"],"\xD5"=>[18,"\xE8"],"\xD6"=>[77,"\xE9"],"\xD7"=>[18,"\xE9"],"\xD8"=>[0,"\x01"],"\xD9"=>[0,"\x87"],"\xDA"=>[0,"\x89"],"\xDB"=>[0,"\x8A"],"\xDC"=>[0,"\x8B"],"\xDD"=>[0,"\x8C"],"\xDE"=>[0,"\x8D"],"\xDF"=>[0,"\x8F"],"\xE0"=>[0,"\x93"],"\xE1"=>[0,"\x95"],"\xE2"=>[0,"\x96"],"\xE3"=>[0,"\x97"],"\xE4"=>[0,"\x98"],"\xE5"=>[0,"\x9B"],"\xE6"=>[0,"\x9D"],"\xE7"=>[0,"\x9E"],"\xE8"=>[0,"\xA5"],"\xE9"=>[0,"\xA6"],"\xEA"=>[0,"\xA8"],"\xEB"=>[0,"\xAE"],"\xEC"=>[0,"\xAF"],"\xED"=>[0,"\xB4"],"\xEE"=>[0,"\xB6"],"\xEF"=>[0,"\xB7"],"\xF0"=>[0,"\xBC"],"\xF1"=>[0,"\xBF"],"\xF2"=>[0,"\xC5"],"\xF3"=>[0,"\xE7"],"\xF4"=>[0,"\xEF"],"\xF5"=>[48,null],"\xF6"=>[172,null],"\xF7"=>[178,null],"\xF8"=>[198,null],"\xF9"=>[241,null],"\xFA"=>[252,null],"\xFB"=>[226,null],"\xFC"=>[219,null],"\xFD"=>[232,null],"\xFE"=>[237,null],"\xFF"=>[33,null],],["\x00"=>[94,"\x7B0"],"\x01"=>[76,"\x7B0"],"\x02"=>[104,"\x7B0"],"\x03"=>[16,"\x7B0"],"\x04"=>[94,"\x7B1"],"\x05"=>[76,"\x7B1"],"\x06"=>[104,"\x7B1"],"\x07"=>[16,"\x7B1"],"\x08"=>[94,"\x7B2"],"\x09"=>[76,"\x7B2"],"\x0A"=>[104,"\x7B2"],"\x0B"=>[16,"\x7B2"],"\x0C"=>[94,"\x7Ba"],"\x0D"=>[76,"\x7Ba"],"\x0E"=>[104,"\x7Ba"],"\x0F"=>[16,"\x7Ba"],"\x10"=>[94,"\x7Bc"],"\x11"=>[76,"\x7Bc"],"\x12"=>[104,"\x7Bc"],"\x13"=>[16,"\x7Bc"],"\x14"=>[94,"\x7Be"],"\x15"=>[76,"\x7Be"],"\x16"=>[104,"\x7Be"],"\x17"=>[16,"\x7Be"],"\x18"=>[94,"\x7Bi"],"\x19"=>[76,"\x7Bi"],"\x1A"=>[104,"\x7Bi"],"\x1B"=>[16,"\x7Bi"],"\x1C"=>[94,"\x7Bo"],"\x1D"=>[76,"\x7Bo"],"\x1E"=>[104,"\x7Bo"],"\x1F"=>[16,"\x7Bo"],"\x20"=>[94,"\x7Bs"],"\x21"=>[76,"\x7Bs"],"\x22"=>[104,"\x7Bs"],"\x23"=>[16,"\x7Bs"],"\x24"=>[94,"\x7Bt"],"\x25"=>[76,"\x7Bt"],"\x26"=>[104,"\x7Bt"],"\x27"=>[16,"\x7Bt"],"\x28"=>[77,"\x7B\x20"],"\x29"=>[18,"\x7B\x20"],"\x2A"=>[77,"\x7B\x25"],"\x2B"=>[18,"\x7B\x25"],"\x2C"=>[77,"\x7B-"],"-"=>[18,"\x7B-"],"."=>[77,"\x7B."],"\x2F"=>[18,"\x7B."],[77,"\x7B\x2F"],[18,"\x7B\x2F"],[77,"\x7B3"],[18,"\x7B3"],[77,"\x7B4"],[18,"\x7B4"],[77,"\x7B5"],[18,"\x7B5"],[77,"\x7B6"],[18,"\x7B6"],"\x3A"=>[77,"\x7B7"],"\x3B"=>[18,"\x7B7"],"\x3C"=>[77,"\x7B8"],"\x3D"=>[18,"\x7B8"],"\x3E"=>[77,"\x7B9"],"\x3F"=>[18,"\x7B9"],"\x40"=>[77,"\x7B\x3D"],"A"=>[18,"\x7B\x3D"],"B"=>[77,"\x7BA"],"C"=>[18,"\x7BA"],"D"=>[77,"\x7B_"],"E"=>[18,"\x7B_"],"F"=>[77,"\x7Bb"],"G"=>[18,"\x7Bb"],"H"=>[77,"\x7Bd"],"I"=>[18,"\x7Bd"],"J"=>[77,"\x7Bf"],"K"=>[18,"\x7Bf"],"L"=>[77,"\x7Bg"],"M"=>[18,"\x7Bg"],"N"=>[77,"\x7Bh"],"O"=>[18,"\x7Bh"],"P"=>[77,"\x7Bl"],"Q"=>[18,"\x7Bl"],"R"=>[77,"\x7Bm"],"S"=>[18,"\x7Bm"],"T"=>[77,"\x7Bn"],"U"=>[18,"\x7Bn"],"V"=>[77,"\x7Bp"],"W"=>[18,"\x7Bp"],"X"=>[77,"\x7Br"],"Y"=>[18,"\x7Br"],"Z"=>[77,"\x7Bu"],"\x5B"=>[18,"\x7Bu"],"\x5C"=>[0,"\x7B\x3A"],"\x5D"=>[0,"\x7BB"],"\x5E"=>[0,"\x7BC"],"_"=>[0,"\x7BD"],"\x60"=>[0,"\x7BE"],"a"=>[0,"\x7BF"],"b"=>[0,"\x7BG"],"c"=>[0,"\x7BH"],"d"=>[0,"\x7BI"],"e"=>[0,"\x7BJ"],"f"=>[0,"\x7BK"],"g"=>[0,"\x7BL"],"h"=>[0,"\x7BM"],"i"=>[0,"\x7BN"],"j"=>[0,"\x7BO"],"k"=>[0,"\x7BP"],"l"=>[0,"\x7BQ"],"m"=>[0,"\x7BR"],"n"=>[0,"\x7BS"],"o"=>[0,"\x7BT"],"p"=>[0,"\x7BU"],"q"=>[0,"\x7BV"],"r"=>[0,"\x7BW"],"s"=>[0,"\x7BY"],"t"=>[0,"\x7Bj"],"u"=>[0,"\x7Bk"],"v"=>[0,"\x7Bq"],"w"=>[0,"\x7Bv"],"x"=>[0,"\x7Bw"],"y"=>[0,"\x7Bx"],"z"=>[0,"\x7By"],"\x7B"=>[0,"\x7Bz"],"\x7C"=>[82,"\x7B"],"\x7D"=>[87,"\x7B"],"~"=>[130,"\x7B"],"\x7F"=>[9,"\x7B"],"\x80"=>[93,"\x5C"],"\x81"=>[138,"\x5C"],"\x82"=>[75,"\x5C"],"\x83"=>[91,"\x5C"],"\x84"=>[108,"\x5C"],"\x85"=>[103,"\x5C"],"\x86"=>[114,"\x5C"],"\x87"=>[14,"\x5C"],"\x88"=>[93,"\xC3"],"\x89"=>[138,"\xC3"],"\x8A"=>[75,"\xC3"],"\x8B"=>[91,"\xC3"],"\x8C"=>[108,"\xC3"],"\x8D"=>[103,"\xC3"],"\x8E"=>[114,"\xC3"],"\x8F"=>[14,"\xC3"],"\x90"=>[93,"\xD0"],"\x91"=>[138,"\xD0"],"\x92"=>[75,"\xD0"],"\x93"=>[91,"\xD0"],"\x94"=>[108,"\xD0"],"\x95"=>[103,"\xD0"],"\x96"=>[114,"\xD0"],"\x97"=>[14,"\xD0"],"\x98"=>[94,"\x80"],"\x99"=>[76,"\x80"],"\x9A"=>[104,"\x80"],"\x9B"=>[16,"\x80"],"\x9C"=>[94,"\x82"],"\x9D"=>[76,"\x82"],"\x9E"=>[104,"\x82"],"\x9F"=>[16,"\x82"],"\xA0"=>[94,"\x83"],"\xA1"=>[76,"\x83"],"\xA2"=>[104,"\x83"],"\xA3"=>[16,"\x83"],"\xA4"=>[94,"\xA2"],"\xA5"=>[76,"\xA2"],"\xA6"=>[104,"\xA2"],"\xA7"=>[16,"\xA2"],"\xA8"=>[94,"\xB8"],"\xA9"=>[76,"\xB8"],"\xAA"=>[104,"\xB8"],"\xAB"=>[16,"\xB8"],"\xAC"=>[94,"\xC2"],"\xAD"=>[76,"\xC2"],"\xAE"=>[104,"\xC2"],"\xAF"=>[16,"\xC2"],"\xB0"=>[94,"\xE0"],"\xB1"=>[76,"\xE0"],"\xB2"=>[104,"\xE0"],"\xB3"=>[16,"\xE0"],"\xB4"=>[94,"\xE2"],"\xB5"=>[76,"\xE2"],"\xB6"=>[104,"\xE2"],"\xB7"=>[16,"\xE2"],"\xB8"=>[77,"\x99"],"\xB9"=>[18,"\x99"],"\xBA"=>[77,"\xA1"],"\xBB"=>[18,"\xA1"],"\xBC"=>[77,"\xA7"],"\xBD"=>[18,"\xA7"],"\xBE"=>[77,"\xAC"],"\xBF"=>[18,"\xAC"],"\xC0"=>[77,"\xB0"],"\xC1"=>[18,"\xB0"],"\xC2"=>[77,"\xB1"],"\xC3"=>[18,"\xB1"],"\xC4"=>[77,"\xB3"],"\xC5"=>[18,"\xB3"],"\xC6"=>[77,"\xD1"],"\xC7"=>[18,"\xD1"],"\xC8"=>[77,"\xD8"],"\xC9"=>[18,"\xD8"],"\xCA"=>[77,"\xD9"],"\xCB"=>[18,"\xD9"],"\xCC"=>[77,"\xE3"],"\xCD"=>[18,"\xE3"],"\xCE"=>[77,"\xE5"],"\xCF"=>[18,"\xE5"],"\xD0"=>[77,"\xE6"],"\xD1"=>[18,"\xE6"],"\xD2"=>[0,"\x81"],"\xD3"=>[0,"\x84"],"\xD4"=>[0,"\x85"],"\xD5"=>[0,"\x86"],"\xD6"=>[0,"\x88"],"\xD7"=>[0,"\x92"],"\xD8"=>[0,"\x9A"],"\xD9"=>[0,"\x9C"],"\xDA"=>[0,"\xA0"],"\xDB"=>[0,"\xA3"],"\xDC"=>[0,"\xA4"],"\xDD"=>[0,"\xA9"],"\xDE"=>[0,"\xAA"],"\xDF"=>[0,"\xAD"],"\xE0"=>[0,"\xB2"],"\xE1"=>[0,"\xB5"],"\xE2"=>[0,"\xB9"],"\xE3"=>[0,"\xBA"],"\xE4"=>[0,"\xBB"],"\xE5"=>[0,"\xBD"],"\xE6"=>[0,"\xBE"],"\xE7"=>[0,"\xC4"],"\xE8"=>[0,"\xC6"],"\xE9"=>[0,"\xE4"],"\xEA"=>[0,"\xE8"],"\xEB"=>[0,"\xE9"],"\xEC"=>[23,null],"\xED"=>[168,null],"\xEE"=>[169,null],"\xEF"=>[171,null],"\xF0"=>[174,null],"\xF1"=>[179,null],"\xF2"=>[180,null],"\xF3"=>[188,null],"\xF4"=>[192,null],"\xF5"=>[196,null],"\xF6"=>[201,null],"\xF7"=>[210,null],"\xF8"=>[215,null],"\xF9"=>[222,null],"\xFA"=>[49,null],"\xFB"=>[173,null],"\xFC"=>[199,null],"\xFD"=>[227,null],"\xFE"=>[220,null],"\xFF"=>[34,null],],["\x00"=>[94,"\x010"],"\x01"=>[76,"\x010"],"\x02"=>[104,"\x010"],"\x03"=>[16,"\x010"],"\x04"=>[94,"\x011"],"\x05"=>[76,"\x011"],"\x06"=>[104,"\x011"],"\x07"=>[16,"\x011"],"\x08"=>[94,"\x012"],"\x09"=>[76,"\x012"],"\x0A"=>[104,"\x012"],"\x0B"=>[16,"\x012"],"\x0C"=>[94,"\x01a"],"\x0D"=>[76,"\x01a"],"\x0E"=>[104,"\x01a"],"\x0F"=>[16,"\x01a"],"\x10"=>[94,"\x01c"],"\x11"=>[76,"\x01c"],"\x12"=>[104,"\x01c"],"\x13"=>[16,"\x01c"],"\x14"=>[94,"\x01e"],"\x15"=>[76,"\x01e"],"\x16"=>[104,"\x01e"],"\x17"=>[16,"\x01e"],"\x18"=>[94,"\x01i"],"\x19"=>[76,"\x01i"],"\x1A"=>[104,"\x01i"],"\x1B"=>[16,"\x01i"],"\x1C"=>[94,"\x01o"],"\x1D"=>[76,"\x01o"],"\x1E"=>[104,"\x01o"],"\x1F"=>[16,"\x01o"],"\x20"=>[94,"\x01s"],"\x21"=>[76,"\x01s"],"\x22"=>[104,"\x01s"],"\x23"=>[16,"\x01s"],"\x24"=>[94,"\x01t"],"\x25"=>[76,"\x01t"],"\x26"=>[104,"\x01t"],"\x27"=>[16,"\x01t"],"\x28"=>[77,"\x01\x20"],"\x29"=>[18,"\x01\x20"],"\x2A"=>[77,"\x01\x25"],"\x2B"=>[18,"\x01\x25"],"\x2C"=>[77,"\x01-"],"-"=>[18,"\x01-"],"."=>[77,"\x01."],"\x2F"=>[18,"\x01."],[77,"\x01\x2F"],[18,"\x01\x2F"],[77,"\x013"],[18,"\x013"],[77,"\x014"],[18,"\x014"],[77,"\x015"],[18,"\x015"],[77,"\x016"],[18,"\x016"],"\x3A"=>[77,"\x017"],"\x3B"=>[18,"\x017"],"\x3C"=>[77,"\x018"],"\x3D"=>[18,"\x018"],"\x3E"=>[77,"\x019"],"\x3F"=>[18,"\x019"],"\x40"=>[77,"\x01\x3D"],"A"=>[18,"\x01\x3D"],"B"=>[77,"\x01A"],"C"=>[18,"\x01A"],"D"=>[77,"\x01_"],"E"=>[18,"\x01_"],"F"=>[77,"\x01b"],"G"=>[18,"\x01b"],"H"=>[77,"\x01d"],"I"=>[18,"\x01d"],"J"=>[77,"\x01f"],"K"=>[18,"\x01f"],"L"=>[77,"\x01g"],"M"=>[18,"\x01g"],"N"=>[77,"\x01h"],"O"=>[18,"\x01h"],"P"=>[77,"\x01l"],"Q"=>[18,"\x01l"],"R"=>[77,"\x01m"],"S"=>[18,"\x01m"],"T"=>[77,"\x01n"],"U"=>[18,"\x01n"],"V"=>[77,"\x01p"],"W"=>[18,"\x01p"],"X"=>[77,"\x01r"],"Y"=>[18,"\x01r"],"Z"=>[77,"\x01u"],"\x5B"=>[18,"\x01u"],"\x5C"=>[0,"\x01\x3A"],"\x5D"=>[0,"\x01B"],"\x5E"=>[0,"\x01C"],"_"=>[0,"\x01D"],"\x60"=>[0,"\x01E"],"a"=>[0,"\x01F"],"b"=>[0,"\x01G"],"c"=>[0,"\x01H"],"d"=>[0,"\x01I"],"e"=>[0,"\x01J"],"f"=>[0,"\x01K"],"g"=>[0,"\x01L"],"h"=>[0,"\x01M"],"i"=>[0,"\x01N"],"j"=>[0,"\x01O"],"k"=>[0,"\x01P"],"l"=>[0,"\x01Q"],"m"=>[0,"\x01R"],"n"=>[0,"\x01S"],"o"=>[0,"\x01T"],"p"=>[0,"\x01U"],"q"=>[0,"\x01V"],"r"=>[0,"\x01W"],"s"=>[0,"\x01Y"],"t"=>[0,"\x01j"],"u"=>[0,"\x01k"],"v"=>[0,"\x01q"],"w"=>[0,"\x01v"],"x"=>[0,"\x01w"],"y"=>[0,"\x01x"],"z"=>[0,"\x01y"],"\x7B"=>[0,"\x01z"],"\x7C"=>[82,"\x01"],"\x7D"=>[87,"\x01"],"~"=>[130,"\x01"],"\x7F"=>[9,"\x01"],"\x80"=>[94,"\x870"],"\x81"=>[76,"\x870"],"\x82"=>[104,"\x870"],"\x83"=>[16,"\x870"],"\x84"=>[94,"\x871"],"\x85"=>[76,"\x871"],"\x86"=>[104,"\x871"],"\x87"=>[16,"\x871"],"\x88"=>[94,"\x872"],"\x89"=>[76,"\x872"],"\x8A"=>[104,"\x872"],"\x8B"=>[16,"\x872"],"\x8C"=>[94,"\x87a"],"\x8D"=>[76,"\x87a"],"\x8E"=>[104,"\x87a"],"\x8F"=>[16,"\x87a"],"\x90"=>[94,"\x87c"],"\x91"=>[76,"\x87c"],"\x92"=>[104,"\x87c"],"\x93"=>[16,"\x87c"],"\x94"=>[94,"\x87e"],"\x95"=>[76,"\x87e"],"\x96"=>[104,"\x87e"],"\x97"=>[16,"\x87e"],"\x98"=>[94,"\x87i"],"\x99"=>[76,"\x87i"],"\x9A"=>[104,"\x87i"],"\x9B"=>[16,"\x87i"],"\x9C"=>[94,"\x87o"],"\x9D"=>[76,"\x87o"],"\x9E"=>[104,"\x87o"],"\x9F"=>[16,"\x87o"],"\xA0"=>[94,"\x87s"],"\xA1"=>[76,"\x87s"],"\xA2"=>[104,"\x87s"],"\xA3"=>[16,"\x87s"],"\xA4"=>[94,"\x87t"],"\xA5"=>[76,"\x87t"],"\xA6"=>[104,"\x87t"],"\xA7"=>[16,"\x87t"],"\xA8"=>[77,"\x87\x20"],"\xA9"=>[18,"\x87\x20"],"\xAA"=>[77,"\x87\x25"],"\xAB"=>[18,"\x87\x25"],"\xAC"=>[77,"\x87-"],"\xAD"=>[18,"\x87-"],"\xAE"=>[77,"\x87."],"\xAF"=>[18,"\x87."],"\xB0"=>[77,"\x87\x2F"],"\xB1"=>[18,"\x87\x2F"],"\xB2"=>[77,"\x873"],"\xB3"=>[18,"\x873"],"\xB4"=>[77,"\x874"],"\xB5"=>[18,"\x874"],"\xB6"=>[77,"\x875"],"\xB7"=>[18,"\x875"],"\xB8"=>[77,"\x876"],"\xB9"=>[18,"\x876"],"\xBA"=>[77,"\x877"],"\xBB"=>[18,"\x877"],"\xBC"=>[77,"\x878"],"\xBD"=>[18,"\x878"],"\xBE"=>[77,"\x879"],"\xBF"=>[18,"\x879"],"\xC0"=>[77,"\x87\x3D"],"\xC1"=>[18,"\x87\x3D"],"\xC2"=>[77,"\x87A"],"\xC3"=>[18,"\x87A"],"\xC4"=>[77,"\x87_"],"\xC5"=>[18,"\x87_"],"\xC6"=>[77,"\x87b"],"\xC7"=>[18,"\x87b"],"\xC8"=>[77,"\x87d"],"\xC9"=>[18,"\x87d"],"\xCA"=>[77,"\x87f"],"\xCB"=>[18,"\x87f"],"\xCC"=>[77,"\x87g"],"\xCD"=>[18,"\x87g"],"\xCE"=>[77,"\x87h"],"\xCF"=>[18,"\x87h"],"\xD0"=>[77,"\x87l"],"\xD1"=>[18,"\x87l"],"\xD2"=>[77,"\x87m"],"\xD3"=>[18,"\x87m"],"\xD4"=>[77,"\x87n"],"\xD5"=>[18,"\x87n"],"\xD6"=>[77,"\x87p"],"\xD7"=>[18,"\x87p"],"\xD8"=>[77,"\x87r"],"\xD9"=>[18,"\x87r"],"\xDA"=>[77,"\x87u"],"\xDB"=>[18,"\x87u"],"\xDC"=>[0,"\x87\x3A"],"\xDD"=>[0,"\x87B"],"\xDE"=>[0,"\x87C"],"\xDF"=>[0,"\x87D"],"\xE0"=>[0,"\x87E"],"\xE1"=>[0,"\x87F"],"\xE2"=>[0,"\x87G"],"\xE3"=>[0,"\x87H"],"\xE4"=>[0,"\x87I"],"\xE5"=>[0,"\x87J"],"\xE6"=>[0,"\x87K"],"\xE7"=>[0,"\x87L"],"\xE8"=>[0,"\x87M"],"\xE9"=>[0,"\x87N"],"\xEA"=>[0,"\x87O"],"\xEB"=>[0,"\x87P"],"\xEC"=>[0,"\x87Q"],"\xED"=>[0,"\x87R"],"\xEE"=>[0,"\x87S"],"\xEF"=>[0,"\x87T"],"\xF0"=>[0,"\x87U"],"\xF1"=>[0,"\x87V"],"\xF2"=>[0,"\x87W"],"\xF3"=>[0,"\x87Y"],"\xF4"=>[0,"\x87j"],"\xF5"=>[0,"\x87k"],"\xF6"=>[0,"\x87q"],"\xF7"=>[0,"\x87v"],"\xF8"=>[0,"\x87w"],"\xF9"=>[0,"\x87x"],"\xFA"=>[0,"\x87y"],"\xFB"=>[0,"\x87z"],"\xFC"=>[82,"\x87"],"\xFD"=>[87,"\x87"],"\xFE"=>[130,"\x87"],"\xFF"=>[9,"\x87"],],["\x00"=>[77,"\x3C0"],"\x01"=>[18,"\x3C0"],"\x02"=>[77,"\x3C1"],"\x03"=>[18,"\x3C1"],"\x04"=>[77,"\x3C2"],"\x05"=>[18,"\x3C2"],"\x06"=>[77,"\x3Ca"],"\x07"=>[18,"\x3Ca"],"\x08"=>[77,"\x3Cc"],"\x09"=>[18,"\x3Cc"],"\x0A"=>[77,"\x3Ce"],"\x0B"=>[18,"\x3Ce"],"\x0C"=>[77,"\x3Ci"],"\x0D"=>[18,"\x3Ci"],"\x0E"=>[77,"\x3Co"],"\x0F"=>[18,"\x3Co"],"\x10"=>[77,"\x3Cs"],"\x11"=>[18,"\x3Cs"],"\x12"=>[77,"\x3Ct"],"\x13"=>[18,"\x3Ct"],"\x14"=>[0,"\x3C\x20"],"\x15"=>[0,"\x3C\x25"],"\x16"=>[0,"\x3C-"],"\x17"=>[0,"\x3C."],"\x18"=>[0,"\x3C\x2F"],"\x19"=>[0,"\x3C3"],"\x1A"=>[0,"\x3C4"],"\x1B"=>[0,"\x3C5"],"\x1C"=>[0,"\x3C6"],"\x1D"=>[0,"\x3C7"],"\x1E"=>[0,"\x3C8"],"\x1F"=>[0,"\x3C9"],"\x20"=>[0,"\x3C\x3D"],"\x21"=>[0,"\x3CA"],"\x22"=>[0,"\x3C_"],"\x23"=>[0,"\x3Cb"],"\x24"=>[0,"\x3Cd"],"\x25"=>[0,"\x3Cf"],"\x26"=>[0,"\x3Cg"],"\x27"=>[0,"\x3Ch"],"\x28"=>[0,"\x3Cl"],"\x29"=>[0,"\x3Cm"],"\x2A"=>[0,"\x3Cn"],"\x2B"=>[0,"\x3Cp"],"\x2C"=>[0,"\x3Cr"],"-"=>[0,"\x3Cu"],"."=>[100,"\x3C"],"\x2F"=>[110,"\x3C"],[111,"\x3C"],[115,"\x3C"],[116,"\x3C"],[118,"\x3C"],[119,"\x3C"],[122,"\x3C"],[123,"\x3C"],[125,"\x3C"],[126,"\x3C"],[129,"\x3C"],"\x3A"=>[143,"\x3C"],"\x3B"=>[148,"\x3C"],"\x3C"=>[151,"\x3C"],"\x3D"=>[153,"\x3C"],"\x3E"=>[83,"\x3C"],"\x3F"=>[10,"\x3C"],"\x40"=>[77,"\x600"],"A"=>[18,"\x600"],"B"=>[77,"\x601"],"C"=>[18,"\x601"],"D"=>[77,"\x602"],"E"=>[18,"\x602"],"F"=>[77,"\x60a"],"G"=>[18,"\x60a"],"H"=>[77,"\x60c"],"I"=>[18,"\x60c"],"J"=>[77,"\x60e"],"K"=>[18,"\x60e"],"L"=>[77,"\x60i"],"M"=>[18,"\x60i"],"N"=>[77,"\x60o"],"O"=>[18,"\x60o"],"P"=>[77,"\x60s"],"Q"=>[18,"\x60s"],"R"=>[77,"\x60t"],"S"=>[18,"\x60t"],"T"=>[0,"\x60\x20"],"U"=>[0,"\x60\x25"],"V"=>[0,"\x60-"],"W"=>[0,"\x60."],"X"=>[0,"\x60\x2F"],"Y"=>[0,"\x603"],"Z"=>[0,"\x604"],"\x5B"=>[0,"\x605"],"\x5C"=>[0,"\x606"],"\x5D"=>[0,"\x607"],"\x5E"=>[0,"\x608"],"_"=>[0,"\x609"],"\x60"=>[0,"\x60\x3D"],"a"=>[0,"\x60A"],"b"=>[0,"\x60_"],"c"=>[0,"\x60b"],"d"=>[0,"\x60d"],"e"=>[0,"\x60f"],"f"=>[0,"\x60g"],"g"=>[0,"\x60h"],"h"=>[0,"\x60l"],"i"=>[0,"\x60m"],"j"=>[0,"\x60n"],"k"=>[0,"\x60p"],"l"=>[0,"\x60r"],"m"=>[0,"\x60u"],"n"=>[100,"\x60"],"o"=>[110,"\x60"],"p"=>[111,"\x60"],"q"=>[115,"\x60"],"r"=>[116,"\x60"],"s"=>[118,"\x60"],"t"=>[119,"\x60"],"u"=>[122,"\x60"],"v"=>[123,"\x60"],"w"=>[125,"\x60"],"x"=>[126,"\x60"],"y"=>[129,"\x60"],"z"=>[143,"\x60"],"\x7B"=>[148,"\x60"],"\x7C"=>[151,"\x60"],"\x7D"=>[153,"\x60"],"~"=>[83,"\x60"],"\x7F"=>[10,"\x60"],"\x80"=>[77,"\x7B0"],"\x81"=>[18,"\x7B0"],"\x82"=>[77,"\x7B1"],"\x83"=>[18,"\x7B1"],"\x84"=>[77,"\x7B2"],"\x85"=>[18,"\x7B2"],"\x86"=>[77,"\x7Ba"],"\x87"=>[18,"\x7Ba"],"\x88"=>[77,"\x7Bc"],"\x89"=>[18,"\x7Bc"],"\x8A"=>[77,"\x7Be"],"\x8B"=>[18,"\x7Be"],"\x8C"=>[77,"\x7Bi"],"\x8D"=>[18,"\x7Bi"],"\x8E"=>[77,"\x7Bo"],"\x8F"=>[18,"\x7Bo"],"\x90"=>[77,"\x7Bs"],"\x91"=>[18,"\x7Bs"],"\x92"=>[77,"\x7Bt"],"\x93"=>[18,"\x7Bt"],"\x94"=>[0,"\x7B\x20"],"\x95"=>[0,"\x7B\x25"],"\x96"=>[0,"\x7B-"],"\x97"=>[0,"\x7B."],"\x98"=>[0,"\x7B\x2F"],"\x99"=>[0,"\x7B3"],"\x9A"=>[0,"\x7B4"],"\x9B"=>[0,"\x7B5"],"\x9C"=>[0,"\x7B6"],"\x9D"=>[0,"\x7B7"],"\x9E"=>[0,"\x7B8"],"\x9F"=>[0,"\x7B9"],"\xA0"=>[0,"\x7B\x3D"],"\xA1"=>[0,"\x7BA"],"\xA2"=>[0,"\x7B_"],"\xA3"=>[0,"\x7Bb"],"\xA4"=>[0,"\x7Bd"],"\xA5"=>[0,"\x7Bf"],"\xA6"=>[0,"\x7Bg"],"\xA7"=>[0,"\x7Bh"],"\xA8"=>[0,"\x7Bl"],"\xA9"=>[0,"\x7Bm"],"\xAA"=>[0,"\x7Bn"],"\xAB"=>[0,"\x7Bp"],"\xAC"=>[0,"\x7Br"],"\xAD"=>[0,"\x7Bu"],"\xAE"=>[100,"\x7B"],"\xAF"=>[110,"\x7B"],"\xB0"=>[111,"\x7B"],"\xB1"=>[115,"\x7B"],"\xB2"=>[116,"\x7B"],"\xB3"=>[118,"\x7B"],"\xB4"=>[119,"\x7B"],"\xB5"=>[122,"\x7B"],"\xB6"=>[123,"\x7B"],"\xB7"=>[125,"\x7B"],"\xB8"=>[126,"\x7B"],"\xB9"=>[129,"\x7B"],"\xBA"=>[143,"\x7B"],"\xBB"=>[148,"\x7B"],"\xBC"=>[151,"\x7B"],"\xBD"=>[153,"\x7B"],"\xBE"=>[83,"\x7B"],"\xBF"=>[10,"\x7B"],"\xC0"=>[94,"\x5C"],"\xC1"=>[76,"\x5C"],"\xC2"=>[104,"\x5C"],"\xC3"=>[16,"\x5C"],"\xC4"=>[94,"\xC3"],"\xC5"=>[76,"\xC3"],"\xC6"=>[104,"\xC3"],"\xC7"=>[16,"\xC3"],"\xC8"=>[94,"\xD0"],"\xC9"=>[76,"\xD0"],"\xCA"=>[104,"\xD0"],"\xCB"=>[16,"\xD0"],"\xCC"=>[77,"\x80"],"\xCD"=>[18,"\x80"],"\xCE"=>[77,"\x82"],"\xCF"=>[18,"\x82"],"\xD0"=>[77,"\x83"],"\xD1"=>[18,"\x83"],"\xD2"=>[77,"\xA2"],"\xD3"=>[18,"\xA2"],"\xD4"=>[77,"\xB8"],"\xD5"=>[18,"\xB8"],"\xD6"=>[77,"\xC2"],"\xD7"=>[18,"\xC2"],"\xD8"=>[77,"\xE0"],"\xD9"=>[18,"\xE0"],"\xDA"=>[77,"\xE2"],"\xDB"=>[18,"\xE2"],"\xDC"=>[0,"\x99"],"\xDD"=>[0,"\xA1"],"\xDE"=>[0,"\xA7"],"\xDF"=>[0,"\xAC"],"\xE0"=>[0,"\xB0"],"\xE1"=>[0,"\xB1"],"\xE2"=>[0,"\xB3"],"\xE3"=>[0,"\xD1"],"\xE4"=>[0,"\xD8"],"\xE5"=>[0,"\xD9"],"\xE6"=>[0,"\xE3"],"\xE7"=>[0,"\xE5"],"\xE8"=>[0,"\xE6"],"\xE9"=>[157,null],"\xEA"=>[165,null],"\xEB"=>[167,null],"\xEC"=>[185,null],"\xED"=>[189,null],"\xEE"=>[190,null],"\xEF"=>[197,null],"\xF0"=>[206,null],"\xF1"=>[212,null],"\xF2"=>[213,null],"\xF3"=>[217,null],"\xF4"=>[223,null],"\xF5"=>[250,null],"\xF6"=>[25,null],"\xF7"=>[170,null],"\xF8"=>[175,null],"\xF9"=>[181,null],"\xFA"=>[193,null],"\xFB"=>[202,null],"\xFC"=>[216,null],"\xFD"=>[50,null],"\xFE"=>[200,null],"\xFF"=>[35,null],],["\x00"=>[77,"\x010"],"\x01"=>[18,"\x010"],"\x02"=>[77,"\x011"],"\x03"=>[18,"\x011"],"\x04"=>[77,"\x012"],"\x05"=>[18,"\x012"],"\x06"=>[77,"\x01a"],"\x07"=>[18,"\x01a"],"\x08"=>[77,"\x01c"],"\x09"=>[18,"\x01c"],"\x0A"=>[77,"\x01e"],"\x0B"=>[18,"\x01e"],"\x0C"=>[77,"\x01i"],"\x0D"=>[18,"\x01i"],"\x0E"=>[77,"\x01o"],"\x0F"=>[18,"\x01o"],"\x10"=>[77,"\x01s"],"\x11"=>[18,"\x01s"],"\x12"=>[77,"\x01t"],"\x13"=>[18,"\x01t"],"\x14"=>[0,"\x01\x20"],"\x15"=>[0,"\x01\x25"],"\x16"=>[0,"\x01-"],"\x17"=>[0,"\x01."],"\x18"=>[0,"\x01\x2F"],"\x19"=>[0,"\x013"],"\x1A"=>[0,"\x014"],"\x1B"=>[0,"\x015"],"\x1C"=>[0,"\x016"],"\x1D"=>[0,"\x017"],"\x1E"=>[0,"\x018"],"\x1F"=>[0,"\x019"],"\x20"=>[0,"\x01\x3D"],"\x21"=>[0,"\x01A"],"\x22"=>[0,"\x01_"],"\x23"=>[0,"\x01b"],"\x24"=>[0,"\x01d"],"\x25"=>[0,"\x01f"],"\x26"=>[0,"\x01g"],"\x27"=>[0,"\x01h"],"\x28"=>[0,"\x01l"],"\x29"=>[0,"\x01m"],"\x2A"=>[0,"\x01n"],"\x2B"=>[0,"\x01p"],"\x2C"=>[0,"\x01r"],"-"=>[0,"\x01u"],"."=>[100,"\x01"],"\x2F"=>[110,"\x01"],[111,"\x01"],[115,"\x01"],[116,"\x01"],[118,"\x01"],[119,"\x01"],[122,"\x01"],[123,"\x01"],[125,"\x01"],[126,"\x01"],[129,"\x01"],"\x3A"=>[143,"\x01"],"\x3B"=>[148,"\x01"],"\x3C"=>[151,"\x01"],"\x3D"=>[153,"\x01"],"\x3E"=>[83,"\x01"],"\x3F"=>[10,"\x01"],"\x40"=>[77,"\x870"],"A"=>[18,"\x870"],"B"=>[77,"\x871"],"C"=>[18,"\x871"],"D"=>[77,"\x872"],"E"=>[18,"\x872"],"F"=>[77,"\x87a"],"G"=>[18,"\x87a"],"H"=>[77,"\x87c"],"I"=>[18,"\x87c"],"J"=>[77,"\x87e"],"K"=>[18,"\x87e"],"L"=>[77,"\x87i"],"M"=>[18,"\x87i"],"N"=>[77,"\x87o"],"O"=>[18,"\x87o"],"P"=>[77,"\x87s"],"Q"=>[18,"\x87s"],"R"=>[77,"\x87t"],"S"=>[18,"\x87t"],"T"=>[0,"\x87\x20"],"U"=>[0,"\x87\x25"],"V"=>[0,"\x87-"],"W"=>[0,"\x87."],"X"=>[0,"\x87\x2F"],"Y"=>[0,"\x873"],"Z"=>[0,"\x874"],"\x5B"=>[0,"\x875"],"\x5C"=>[0,"\x876"],"\x5D"=>[0,"\x877"],"\x5E"=>[0,"\x878"],"_"=>[0,"\x879"],"\x60"=>[0,"\x87\x3D"],"a"=>[0,"\x87A"],"b"=>[0,"\x87_"],"c"=>[0,"\x87b"],"d"=>[0,"\x87d"],"e"=>[0,"\x87f"],"f"=>[0,"\x87g"],"g"=>[0,"\x87h"],"h"=>[0,"\x87l"],"i"=>[0,"\x87m"],"j"=>[0,"\x87n"],"k"=>[0,"\x87p"],"l"=>[0,"\x87r"],"m"=>[0,"\x87u"],"n"=>[100,"\x87"],"o"=>[110,"\x87"],"p"=>[111,"\x87"],"q"=>[115,"\x87"],"r"=>[116,"\x87"],"s"=>[118,"\x87"],"t"=>[119,"\x87"],"u"=>[122,"\x87"],"v"=>[123,"\x87"],"w"=>[125,"\x87"],"x"=>[126,"\x87"],"y"=>[129,"\x87"],"z"=>[143,"\x87"],"\x7B"=>[148,"\x87"],"\x7C"=>[151,"\x87"],"\x7D"=>[153,"\x87"],"~"=>[83,"\x87"],"\x7F"=>[10,"\x87"],"\x80"=>[77,"\x890"],"\x81"=>[18,"\x890"],"\x82"=>[77,"\x891"],"\x83"=>[18,"\x891"],"\x84"=>[77,"\x892"],"\x85"=>[18,"\x892"],"\x86"=>[77,"\x89a"],"\x87"=>[18,"\x89a"],"\x88"=>[77,"\x89c"],"\x89"=>[18,"\x89c"],"\x8A"=>[77,"\x89e"],"\x8B"=>[18,"\x89e"],"\x8C"=>[77,"\x89i"],"\x8D"=>[18,"\x89i"],"\x8E"=>[77,"\x89o"],"\x8F"=>[18,"\x89o"],"\x90"=>[77,"\x89s"],"\x91"=>[18,"\x89s"],"\x92"=>[77,"\x89t"],"\x93"=>[18,"\x89t"],"\x94"=>[0,"\x89\x20"],"\x95"=>[0,"\x89\x25"],"\x96"=>[0,"\x89-"],"\x97"=>[0,"\x89."],"\x98"=>[0,"\x89\x2F"],"\x99"=>[0,"\x893"],"\x9A"=>[0,"\x894"],"\x9B"=>[0,"\x895"],"\x9C"=>[0,"\x896"],"\x9D"=>[0,"\x897"],"\x9E"=>[0,"\x898"],"\x9F"=>[0,"\x899"],"\xA0"=>[0,"\x89\x3D"],"\xA1"=>[0,"\x89A"],"\xA2"=>[0,"\x89_"],"\xA3"=>[0,"\x89b"],"\xA4"=>[0,"\x89d"],"\xA5"=>[0,"\x89f"],"\xA6"=>[0,"\x89g"],"\xA7"=>[0,"\x89h"],"\xA8"=>[0,"\x89l"],"\xA9"=>[0,"\x89m"],"\xAA"=>[0,"\x89n"],"\xAB"=>[0,"\x89p"],"\xAC"=>[0,"\x89r"],"\xAD"=>[0,"\x89u"],"\xAE"=>[100,"\x89"],"\xAF"=>[110,"\x89"],"\xB0"=>[111,"\x89"],"\xB1"=>[115,"\x89"],"\xB2"=>[116,"\x89"],"\xB3"=>[118,"\x89"],"\xB4"=>[119,"\x89"],"\xB5"=>[122,"\x89"],"\xB6"=>[123,"\x89"],"\xB7"=>[125,"\x89"],"\xB8"=>[126,"\x89"],"\xB9"=>[129,"\x89"],"\xBA"=>[143,"\x89"],"\xBB"=>[148,"\x89"],"\xBC"=>[151,"\x89"],"\xBD"=>[153,"\x89"],"\xBE"=>[83,"\x89"],"\xBF"=>[10,"\x89"],"\xC0"=>[77,"\x8A0"],"\xC1"=>[18,"\x8A0"],"\xC2"=>[77,"\x8A1"],"\xC3"=>[18,"\x8A1"],"\xC4"=>[77,"\x8A2"],"\xC5"=>[18,"\x8A2"],"\xC6"=>[77,"\x8Aa"],"\xC7"=>[18,"\x8Aa"],"\xC8"=>[77,"\x8Ac"],"\xC9"=>[18,"\x8Ac"],"\xCA"=>[77,"\x8Ae"],"\xCB"=>[18,"\x8Ae"],"\xCC"=>[77,"\x8Ai"],"\xCD"=>[18,"\x8Ai"],"\xCE"=>[77,"\x8Ao"],"\xCF"=>[18,"\x8Ao"],"\xD0"=>[77,"\x8As"],"\xD1"=>[18,"\x8As"],"\xD2"=>[77,"\x8At"],"\xD3"=>[18,"\x8At"],"\xD4"=>[0,"\x8A\x20"],"\xD5"=>[0,"\x8A\x25"],"\xD6"=>[0,"\x8A-"],"\xD7"=>[0,"\x8A."],"\xD8"=>[0,"\x8A\x2F"],"\xD9"=>[0,"\x8A3"],"\xDA"=>[0,"\x8A4"],"\xDB"=>[0,"\x8A5"],"\xDC"=>[0,"\x8A6"],"\xDD"=>[0,"\x8A7"],"\xDE"=>[0,"\x8A8"],"\xDF"=>[0,"\x8A9"],"\xE0"=>[0,"\x8A\x3D"],"\xE1"=>[0,"\x8AA"],"\xE2"=>[0,"\x8A_"],"\xE3"=>[0,"\x8Ab"],"\xE4"=>[0,"\x8Ad"],"\xE5"=>[0,"\x8Af"],"\xE6"=>[0,"\x8Ag"],"\xE7"=>[0,"\x8Ah"],"\xE8"=>[0,"\x8Al"],"\xE9"=>[0,"\x8Am"],"\xEA"=>[0,"\x8An"],"\xEB"=>[0,"\x8Ap"],"\xEC"=>[0,"\x8Ar"],"\xED"=>[0,"\x8Au"],"\xEE"=>[100,"\x8A"],"\xEF"=>[110,"\x8A"],"\xF0"=>[111,"\x8A"],"\xF1"=>[115,"\x8A"],"\xF2"=>[116,"\x8A"],"\xF3"=>[118,"\x8A"],"\xF4"=>[119,"\x8A"],"\xF5"=>[122,"\x8A"],"\xF6"=>[123,"\x8A"],"\xF7"=>[125,"\x8A"],"\xF8"=>[126,"\x8A"],"\xF9"=>[129,"\x8A"],"\xFA"=>[143,"\x8A"],"\xFB"=>[148,"\x8A"],"\xFC"=>[151,"\x8A"],"\xFD"=>[153,"\x8A"],"\xFE"=>[83,"\x8A"],"\xFF"=>[10,"\x8A"],],["\x00"=>[77,"\x5E0"],"\x01"=>[18,"\x5E0"],"\x02"=>[77,"\x5E1"],"\x03"=>[18,"\x5E1"],"\x04"=>[77,"\x5E2"],"\x05"=>[18,"\x5E2"],"\x06"=>[77,"\x5Ea"],"\x07"=>[18,"\x5Ea"],"\x08"=>[77,"\x5Ec"],"\x09"=>[18,"\x5Ec"],"\x0A"=>[77,"\x5Ee"],"\x0B"=>[18,"\x5Ee"],"\x0C"=>[77,"\x5Ei"],"\x0D"=>[18,"\x5Ei"],"\x0E"=>[77,"\x5Eo"],"\x0F"=>[18,"\x5Eo"],"\x10"=>[77,"\x5Es"],"\x11"=>[18,"\x5Es"],"\x12"=>[77,"\x5Et"],"\x13"=>[18,"\x5Et"],"\x14"=>[0,"\x5E\x20"],"\x15"=>[0,"\x5E\x25"],"\x16"=>[0,"\x5E-"],"\x17"=>[0,"\x5E."],"\x18"=>[0,"\x5E\x2F"],"\x19"=>[0,"\x5E3"],"\x1A"=>[0,"\x5E4"],"\x1B"=>[0,"\x5E5"],"\x1C"=>[0,"\x5E6"],"\x1D"=>[0,"\x5E7"],"\x1E"=>[0,"\x5E8"],"\x1F"=>[0,"\x5E9"],"\x20"=>[0,"\x5E\x3D"],"\x21"=>[0,"\x5EA"],"\x22"=>[0,"\x5E_"],"\x23"=>[0,"\x5Eb"],"\x24"=>[0,"\x5Ed"],"\x25"=>[0,"\x5Ef"],"\x26"=>[0,"\x5Eg"],"\x27"=>[0,"\x5Eh"],"\x28"=>[0,"\x5El"],"\x29"=>[0,"\x5Em"],"\x2A"=>[0,"\x5En"],"\x2B"=>[0,"\x5Ep"],"\x2C"=>[0,"\x5Er"],"-"=>[0,"\x5Eu"],"."=>[100,"\x5E"],"\x2F"=>[110,"\x5E"],[111,"\x5E"],[115,"\x5E"],[116,"\x5E"],[118,"\x5E"],[119,"\x5E"],[122,"\x5E"],[123,"\x5E"],[125,"\x5E"],[126,"\x5E"],[129,"\x5E"],"\x3A"=>[143,"\x5E"],"\x3B"=>[148,"\x5E"],"\x3C"=>[151,"\x5E"],"\x3D"=>[153,"\x5E"],"\x3E"=>[83,"\x5E"],"\x3F"=>[10,"\x5E"],"\x40"=>[77,"\x7D0"],"A"=>[18,"\x7D0"],"B"=>[77,"\x7D1"],"C"=>[18,"\x7D1"],"D"=>[77,"\x7D2"],"E"=>[18,"\x7D2"],"F"=>[77,"\x7Da"],"G"=>[18,"\x7Da"],"H"=>[77,"\x7Dc"],"I"=>[18,"\x7Dc"],"J"=>[77,"\x7De"],"K"=>[18,"\x7De"],"L"=>[77,"\x7Di"],"M"=>[18,"\x7Di"],"N"=>[77,"\x7Do"],"O"=>[18,"\x7Do"],"P"=>[77,"\x7Ds"],"Q"=>[18,"\x7Ds"],"R"=>[77,"\x7Dt"],"S"=>[18,"\x7Dt"],"T"=>[0,"\x7D\x20"],"U"=>[0,"\x7D\x25"],"V"=>[0,"\x7D-"],"W"=>[0,"\x7D."],"X"=>[0,"\x7D\x2F"],"Y"=>[0,"\x7D3"],"Z"=>[0,"\x7D4"],"\x5B"=>[0,"\x7D5"],"\x5C"=>[0,"\x7D6"],"\x5D"=>[0,"\x7D7"],"\x5E"=>[0,"\x7D8"],"_"=>[0,"\x7D9"],"\x60"=>[0,"\x7D\x3D"],"a"=>[0,"\x7DA"],"b"=>[0,"\x7D_"],"c"=>[0,"\x7Db"],"d"=>[0,"\x7Dd"],"e"=>[0,"\x7Df"],"f"=>[0,"\x7Dg"],"g"=>[0,"\x7Dh"],"h"=>[0,"\x7Dl"],"i"=>[0,"\x7Dm"],"j"=>[0,"\x7Dn"],"k"=>[0,"\x7Dp"],"l"=>[0,"\x7Dr"],"m"=>[0,"\x7Du"],"n"=>[100,"\x7D"],"o"=>[110,"\x7D"],"p"=>[111,"\x7D"],"q"=>[115,"\x7D"],"r"=>[116,"\x7D"],"s"=>[118,"\x7D"],"t"=>[119,"\x7D"],"u"=>[122,"\x7D"],"v"=>[123,"\x7D"],"w"=>[125,"\x7D"],"x"=>[126,"\x7D"],"y"=>[129,"\x7D"],"z"=>[143,"\x7D"],"\x7B"=>[148,"\x7D"],"\x7C"=>[151,"\x7D"],"\x7D"=>[153,"\x7D"],"~"=>[83,"\x7D"],"\x7F"=>[10,"\x7D"],"\x80"=>[0,"\x3C0"],"\x81"=>[0,"\x3C1"],"\x82"=>[0,"\x3C2"],"\x83"=>[0,"\x3Ca"],"\x84"=>[0,"\x3Cc"],"\x85"=>[0,"\x3Ce"],"\x86"=>[0,"\x3Ci"],"\x87"=>[0,"\x3Co"],"\x88"=>[0,"\x3Cs"],"\x89"=>[0,"\x3Ct"],"\x8A"=>[73,"\x3C"],"\x8B"=>[88,"\x3C"],"\x8C"=>[89,"\x3C"],"\x8D"=>[96,"\x3C"],"\x8E"=>[97,"\x3C"],"\x8F"=>[99,"\x3C"],"\x90"=>[106,"\x3C"],"\x91"=>[136,"\x3C"],"\x92"=>[139,"\x3C"],"\x93"=>[141,"\x3C"],"\x94"=>[145,"\x3C"],"\x95"=>[147,"\x3C"],"\x96"=>[149,"\x3C"],"\x97"=>[101,"\x3C"],"\x98"=>[112,"\x3C"],"\x99"=>[117,"\x3C"],"\x9A"=>[120,"\x3C"],"\x9B"=>[124,"\x3C"],"\x9C"=>[127,"\x3C"],"\x9D"=>[144,"\x3C"],"\x9E"=>[152,"\x3C"],"\x9F"=>[11,"\x3C"],"\xA0"=>[0,"\x600"],"\xA1"=>[0,"\x601"],"\xA2"=>[0,"\x602"],"\xA3"=>[0,"\x60a"],"\xA4"=>[0,"\x60c"],"\xA5"=>[0,"\x60e"],"\xA6"=>[0,"\x60i"],"\xA7"=>[0,"\x60o"],"\xA8"=>[0,"\x60s"],"\xA9"=>[0,"\x60t"],"\xAA"=>[73,"\x60"],"\xAB"=>[88,"\x60"],"\xAC"=>[89,"\x60"],"\xAD"=>[96,"\x60"],"\xAE"=>[97,"\x60"],"\xAF"=>[99,"\x60"],"\xB0"=>[106,"\x60"],"\xB1"=>[136,"\x60"],"\xB2"=>[139,"\x60"],"\xB3"=>[141,"\x60"],"\xB4"=>[145,"\x60"],"\xB5"=>[147,"\x60"],"\xB6"=>[149,"\x60"],"\xB7"=>[101,"\x60"],"\xB8"=>[112,"\x60"],"\xB9"=>[117,"\x60"],"\xBA"=>[120,"\x60"],"\xBB"=>[124,"\x60"],"\xBC"=>[127,"\x60"],"\xBD"=>[144,"\x60"],"\xBE"=>[152,"\x60"],"\xBF"=>[11,"\x60"],"\xC0"=>[0,"\x7B0"],"\xC1"=>[0,"\x7B1"],"\xC2"=>[0,"\x7B2"],"\xC3"=>[0,"\x7Ba"],"\xC4"=>[0,"\x7Bc"],"\xC5"=>[0,"\x7Be"],"\xC6"=>[0,"\x7Bi"],"\xC7"=>[0,"\x7Bo"],"\xC8"=>[0,"\x7Bs"],"\xC9"=>[0,"\x7Bt"],"\xCA"=>[73,"\x7B"],"\xCB"=>[88,"\x7B"],"\xCC"=>[89,"\x7B"],"\xCD"=>[96,"\x7B"],"\xCE"=>[97,"\x7B"],"\xCF"=>[99,"\x7B"],"\xD0"=>[106,"\x7B"],"\xD1"=>[136,"\x7B"],"\xD2"=>[139,"\x7B"],"\xD3"=>[141,"\x7B"],"\xD4"=>[145,"\x7B"],"\xD5"=>[147,"\x7B"],"\xD6"=>[149,"\x7B"],"\xD7"=>[101,"\x7B"],"\xD8"=>[112,"\x7B"],"\xD9"=>[117,"\x7B"],"\xDA"=>[120,"\x7B"],"\xDB"=>[124,"\x7B"],"\xDC"=>[127,"\x7B"],"\xDD"=>[144,"\x7B"],"\xDE"=>[152,"\x7B"],"\xDF"=>[11,"\x7B"],"\xE0"=>[77,"\x5C"],"\xE1"=>[18,"\x5C"],"\xE2"=>[77,"\xC3"],"\xE3"=>[18,"\xC3"],"\xE4"=>[77,"\xD0"],"\xE5"=>[18,"\xD0"],"\xE6"=>[0,"\x80"],"\xE7"=>[0,"\x82"],"\xE8"=>[0,"\x83"],"\xE9"=>[0,"\xA2"],"\xEA"=>[0,"\xB8"],"\xEB"=>[0,"\xC2"],"\xEC"=>[0,"\xE0"],"\xED"=>[0,"\xE2"],"\xEE"=>[182,null],"\xEF"=>[195,null],"\xF0"=>[203,null],"\xF1"=>[209,null],"\xF2"=>[242,null],"\xF3"=>[249,null],"\xF4"=>[158,null],"\xF5"=>[166,null],"\xF6"=>[186,null],"\xF7"=>[191,null],"\xF8"=>[207,null],"\xF9"=>[214,null],"\xFA"=>[224,null],"\xFB"=>[27,null],"\xFC"=>[176,null],"\xFD"=>[194,null],"\xFE"=>[51,null],"\xFF"=>[36,null],],["\x00"=>[0,"\x010"],"\x01"=>[0,"\x011"],"\x02"=>[0,"\x012"],"\x03"=>[0,"\x01a"],"\x04"=>[0,"\x01c"],"\x05"=>[0,"\x01e"],"\x06"=>[0,"\x01i"],"\x07"=>[0,"\x01o"],"\x08"=>[0,"\x01s"],"\x09"=>[0,"\x01t"],"\x0A"=>[73,"\x01"],"\x0B"=>[88,"\x01"],"\x0C"=>[89,"\x01"],"\x0D"=>[96,"\x01"],"\x0E"=>[97,"\x01"],"\x0F"=>[99,"\x01"],"\x10"=>[106,"\x01"],"\x11"=>[136,"\x01"],"\x12"=>[139,"\x01"],"\x13"=>[141,"\x01"],"\x14"=>[145,"\x01"],"\x15"=>[147,"\x01"],"\x16"=>[149,"\x01"],"\x17"=>[101,"\x01"],"\x18"=>[112,"\x01"],"\x19"=>[117,"\x01"],"\x1A"=>[120,"\x01"],"\x1B"=>[124,"\x01"],"\x1C"=>[127,"\x01"],"\x1D"=>[144,"\x01"],"\x1E"=>[152,"\x01"],"\x1F"=>[11,"\x01"],"\x20"=>[0,"\x870"],"\x21"=>[0,"\x871"],"\x22"=>[0,"\x872"],"\x23"=>[0,"\x87a"],"\x24"=>[0,"\x87c"],"\x25"=>[0,"\x87e"],"\x26"=>[0,"\x87i"],"\x27"=>[0,"\x87o"],"\x28"=>[0,"\x87s"],"\x29"=>[0,"\x87t"],"\x2A"=>[73,"\x87"],"\x2B"=>[88,"\x87"],"\x2C"=>[89,"\x87"],"-"=>[96,"\x87"],"."=>[97,"\x87"],"\x2F"=>[99,"\x87"],[106,"\x87"],[136,"\x87"],[139,"\x87"],[141,"\x87"],[145,"\x87"],[147,"\x87"],[149,"\x87"],[101,"\x87"],[112,"\x87"],[117,"\x87"],"\x3A"=>[120,"\x87"],"\x3B"=>[124,"\x87"],"\x3C"=>[127,"\x87"],"\x3D"=>[144,"\x87"],"\x3E"=>[152,"\x87"],"\x3F"=>[11,"\x87"],"\x40"=>[0,"\x890"],"A"=>[0,"\x891"],"B"=>[0,"\x892"],"C"=>[0,"\x89a"],"D"=>[0,"\x89c"],"E"=>[0,"\x89e"],"F"=>[0,"\x89i"],"G"=>[0,"\x89o"],"H"=>[0,"\x89s"],"I"=>[0,"\x89t"],"J"=>[73,"\x89"],"K"=>[88,"\x89"],"L"=>[89,"\x89"],"M"=>[96,"\x89"],"N"=>[97,"\x89"],"O"=>[99,"\x89"],"P"=>[106,"\x89"],"Q"=>[136,"\x89"],"R"=>[139,"\x89"],"S"=>[141,"\x89"],"T"=>[145,"\x89"],"U"=>[147,"\x89"],"V"=>[149,"\x89"],"W"=>[101,"\x89"],"X"=>[112,"\x89"],"Y"=>[117,"\x89"],"Z"=>[120,"\x89"],"\x5B"=>[124,"\x89"],"\x5C"=>[127,"\x89"],"\x5D"=>[144,"\x89"],"\x5E"=>[152,"\x89"],"_"=>[11,"\x89"],"\x60"=>[0,"\x8A0"],"a"=>[0,"\x8A1"],"b"=>[0,"\x8A2"],"c"=>[0,"\x8Aa"],"d"=>[0,"\x8Ac"],"e"=>[0,"\x8Ae"],"f"=>[0,"\x8Ai"],"g"=>[0,"\x8Ao"],"h"=>[0,"\x8As"],"i"=>[0,"\x8At"],"j"=>[73,"\x8A"],"k"=>[88,"\x8A"],"l"=>[89,"\x8A"],"m"=>[96,"\x8A"],"n"=>[97,"\x8A"],"o"=>[99,"\x8A"],"p"=>[106,"\x8A"],"q"=>[136,"\x8A"],"r"=>[139,"\x8A"],"s"=>[141,"\x8A"],"t"=>[145,"\x8A"],"u"=>[147,"\x8A"],"v"=>[149,"\x8A"],"w"=>[101,"\x8A"],"x"=>[112,"\x8A"],"y"=>[117,"\x8A"],"z"=>[120,"\x8A"],"\x7B"=>[124,"\x8A"],"\x7C"=>[127,"\x8A"],"\x7D"=>[144,"\x8A"],"~"=>[152,"\x8A"],"\x7F"=>[11,"\x8A"],"\x80"=>[0,"\x8B0"],"\x81"=>[0,"\x8B1"],"\x82"=>[0,"\x8B2"],"\x83"=>[0,"\x8Ba"],"\x84"=>[0,"\x8Bc"],"\x85"=>[0,"\x8Be"],"\x86"=>[0,"\x8Bi"],"\x87"=>[0,"\x8Bo"],"\x88"=>[0,"\x8Bs"],"\x89"=>[0,"\x8Bt"],"\x8A"=>[73,"\x8B"],"\x8B"=>[88,"\x8B"],"\x8C"=>[89,"\x8B"],"\x8D"=>[96,"\x8B"],"\x8E"=>[97,"\x8B"],"\x8F"=>[99,"\x8B"],"\x90"=>[106,"\x8B"],"\x91"=>[136,"\x8B"],"\x92"=>[139,"\x8B"],"\x93"=>[141,"\x8B"],"\x94"=>[145,"\x8B"],"\x95"=>[147,"\x8B"],"\x96"=>[149,"\x8B"],"\x97"=>[101,"\x8B"],"\x98"=>[112,"\x8B"],"\x99"=>[117,"\x8B"],"\x9A"=>[120,"\x8B"],"\x9B"=>[124,"\x8B"],"\x9C"=>[127,"\x8B"],"\x9D"=>[144,"\x8B"],"\x9E"=>[152,"\x8B"],"\x9F"=>[11,"\x8B"],"\xA0"=>[0,"\x8C0"],"\xA1"=>[0,"\x8C1"],"\xA2"=>[0,"\x8C2"],"\xA3"=>[0,"\x8Ca"],"\xA4"=>[0,"\x8Cc"],"\xA5"=>[0,"\x8Ce"],"\xA6"=>[0,"\x8Ci"],"\xA7"=>[0,"\x8Co"],"\xA8"=>[0,"\x8Cs"],"\xA9"=>[0,"\x8Ct"],"\xAA"=>[73,"\x8C"],"\xAB"=>[88,"\x8C"],"\xAC"=>[89,"\x8C"],"\xAD"=>[96,"\x8C"],"\xAE"=>[97,"\x8C"],"\xAF"=>[99,"\x8C"],"\xB0"=>[106,"\x8C"],"\xB1"=>[136,"\x8C"],"\xB2"=>[139,"\x8C"],"\xB3"=>[141,"\x8C"],"\xB4"=>[145,"\x8C"],"\xB5"=>[147,"\x8C"],"\xB6"=>[149,"\x8C"],"\xB7"=>[101,"\x8C"],"\xB8"=>[112,"\x8C"],"\xB9"=>[117,"\x8C"],"\xBA"=>[120,"\x8C"],"\xBB"=>[124,"\x8C"],"\xBC"=>[127,"\x8C"],"\xBD"=>[144,"\x8C"],"\xBE"=>[152,"\x8C"],"\xBF"=>[11,"\x8C"],"\xC0"=>[0,"\x8D0"],"\xC1"=>[0,"\x8D1"],"\xC2"=>[0,"\x8D2"],"\xC3"=>[0,"\x8Da"],"\xC4"=>[0,"\x8Dc"],"\xC5"=>[0,"\x8De"],"\xC6"=>[0,"\x8Di"],"\xC7"=>[0,"\x8Do"],"\xC8"=>[0,"\x8Ds"],"\xC9"=>[0,"\x8Dt"],"\xCA"=>[73,"\x8D"],"\xCB"=>[88,"\x8D"],"\xCC"=>[89,"\x8D"],"\xCD"=>[96,"\x8D"],"\xCE"=>[97,"\x8D"],"\xCF"=>[99,"\x8D"],"\xD0"=>[106,"\x8D"],"\xD1"=>[136,"\x8D"],"\xD2"=>[139,"\x8D"],"\xD3"=>[141,"\x8D"],"\xD4"=>[145,"\x8D"],"\xD5"=>[147,"\x8D"],"\xD6"=>[149,"\x8D"],"\xD7"=>[101,"\x8D"],"\xD8"=>[112,"\x8D"],"\xD9"=>[117,"\x8D"],"\xDA"=>[120,"\x8D"],"\xDB"=>[124,"\x8D"],"\xDC"=>[127,"\x8D"],"\xDD"=>[144,"\x8D"],"\xDE"=>[152,"\x8D"],"\xDF"=>[11,"\x8D"],"\xE0"=>[0,"\x8F0"],"\xE1"=>[0,"\x8F1"],"\xE2"=>[0,"\x8F2"],"\xE3"=>[0,"\x8Fa"],"\xE4"=>[0,"\x8Fc"],"\xE5"=>[0,"\x8Fe"],"\xE6"=>[0,"\x8Fi"],"\xE7"=>[0,"\x8Fo"],"\xE8"=>[0,"\x8Fs"],"\xE9"=>[0,"\x8Ft"],"\xEA"=>[73,"\x8F"],"\xEB"=>[88,"\x8F"],"\xEC"=>[89,"\x8F"],"\xED"=>[96,"\x8F"],"\xEE"=>[97,"\x8F"],"\xEF"=>[99,"\x8F"],"\xF0"=>[106,"\x8F"],"\xF1"=>[136,"\x8F"],"\xF2"=>[139,"\x8F"],"\xF3"=>[141,"\x8F"],"\xF4"=>[145,"\x8F"],"\xF5"=>[147,"\x8F"],"\xF6"=>[149,"\x8F"],"\xF7"=>[101,"\x8F"],"\xF8"=>[112,"\x8F"],"\xF9"=>[117,"\x8F"],"\xFA"=>[120,"\x8F"],"\xFB"=>[124,"\x8F"],"\xFC"=>[127,"\x8F"],"\xFD"=>[144,"\x8F"],"\xFE"=>[152,"\x8F"],"\xFF"=>[11,"\x8F"],],["\x00"=>[77,"\x5D0"],"\x01"=>[18,"\x5D0"],"\x02"=>[77,"\x5D1"],"\x03"=>[18,"\x5D1"],"\x04"=>[77,"\x5D2"],"\x05"=>[18,"\x5D2"],"\x06"=>[77,"\x5Da"],"\x07"=>[18,"\x5Da"],"\x08"=>[77,"\x5Dc"],"\x09"=>[18,"\x5Dc"],"\x0A"=>[77,"\x5De"],"\x0B"=>[18,"\x5De"],"\x0C"=>[77,"\x5Di"],"\x0D"=>[18,"\x5Di"],"\x0E"=>[77,"\x5Do"],"\x0F"=>[18,"\x5Do"],"\x10"=>[77,"\x5Ds"],"\x11"=>[18,"\x5Ds"],"\x12"=>[77,"\x5Dt"],"\x13"=>[18,"\x5Dt"],"\x14"=>[0,"\x5D\x20"],"\x15"=>[0,"\x5D\x25"],"\x16"=>[0,"\x5D-"],"\x17"=>[0,"\x5D."],"\x18"=>[0,"\x5D\x2F"],"\x19"=>[0,"\x5D3"],"\x1A"=>[0,"\x5D4"],"\x1B"=>[0,"\x5D5"],"\x1C"=>[0,"\x5D6"],"\x1D"=>[0,"\x5D7"],"\x1E"=>[0,"\x5D8"],"\x1F"=>[0,"\x5D9"],"\x20"=>[0,"\x5D\x3D"],"\x21"=>[0,"\x5DA"],"\x22"=>[0,"\x5D_"],"\x23"=>[0,"\x5Db"],"\x24"=>[0,"\x5Dd"],"\x25"=>[0,"\x5Df"],"\x26"=>[0,"\x5Dg"],"\x27"=>[0,"\x5Dh"],"\x28"=>[0,"\x5Dl"],"\x29"=>[0,"\x5Dm"],"\x2A"=>[0,"\x5Dn"],"\x2B"=>[0,"\x5Dp"],"\x2C"=>[0,"\x5Dr"],"-"=>[0,"\x5Du"],"."=>[100,"\x5D"],"\x2F"=>[110,"\x5D"],[111,"\x5D"],[115,"\x5D"],[116,"\x5D"],[118,"\x5D"],[119,"\x5D"],[122,"\x5D"],[123,"\x5D"],[125,"\x5D"],[126,"\x5D"],[129,"\x5D"],"\x3A"=>[143,"\x5D"],"\x3B"=>[148,"\x5D"],"\x3C"=>[151,"\x5D"],"\x3D"=>[153,"\x5D"],"\x3E"=>[83,"\x5D"],"\x3F"=>[10,"\x5D"],"\x40"=>[77,"~0"],"A"=>[18,"~0"],"B"=>[77,"~1"],"C"=>[18,"~1"],"D"=>[77,"~2"],"E"=>[18,"~2"],"F"=>[77,"~a"],"G"=>[18,"~a"],"H"=>[77,"~c"],"I"=>[18,"~c"],"J"=>[77,"~e"],"K"=>[18,"~e"],"L"=>[77,"~i"],"M"=>[18,"~i"],"N"=>[77,"~o"],"O"=>[18,"~o"],"P"=>[77,"~s"],"Q"=>[18,"~s"],"R"=>[77,"~t"],"S"=>[18,"~t"],"T"=>[0,"~\x20"],"U"=>[0,"~\x25"],"V"=>[0,"~-"],"W"=>[0,"~."],"X"=>[0,"~\x2F"],"Y"=>[0,"~3"],"Z"=>[0,"~4"],"\x5B"=>[0,"~5"],"\x5C"=>[0,"~6"],"\x5D"=>[0,"~7"],"\x5E"=>[0,"~8"],"_"=>[0,"~9"],"\x60"=>[0,"~\x3D"],"a"=>[0,"~A"],"b"=>[0,"~_"],"c"=>[0,"~b"],"d"=>[0,"~d"],"e"=>[0,"~f"],"f"=>[0,"~g"],"g"=>[0,"~h"],"h"=>[0,"~l"],"i"=>[0,"~m"],"j"=>[0,"~n"],"k"=>[0,"~p"],"l"=>[0,"~r"],"m"=>[0,"~u"],"n"=>[100,"~"],"o"=>[110,"~"],"p"=>[111,"~"],"q"=>[115,"~"],"r"=>[116,"~"],"s"=>[118,"~"],"t"=>[119,"~"],"u"=>[122,"~"],"v"=>[123,"~"],"w"=>[125,"~"],"x"=>[126,"~"],"y"=>[129,"~"],"z"=>[143,"~"],"\x7B"=>[148,"~"],"\x7C"=>[151,"~"],"\x7D"=>[153,"~"],"~"=>[83,"~"],"\x7F"=>[10,"~"],"\x80"=>[0,"\x5E0"],"\x81"=>[0,"\x5E1"],"\x82"=>[0,"\x5E2"],"\x83"=>[0,"\x5Ea"],"\x84"=>[0,"\x5Ec"],"\x85"=>[0,"\x5Ee"],"\x86"=>[0,"\x5Ei"],"\x87"=>[0,"\x5Eo"],"\x88"=>[0,"\x5Es"],"\x89"=>[0,"\x5Et"],"\x8A"=>[73,"\x5E"],"\x8B"=>[88,"\x5E"],"\x8C"=>[89,"\x5E"],"\x8D"=>[96,"\x5E"],"\x8E"=>[97,"\x5E"],"\x8F"=>[99,"\x5E"],"\x90"=>[106,"\x5E"],"\x91"=>[136,"\x5E"],"\x92"=>[139,"\x5E"],"\x93"=>[141,"\x5E"],"\x94"=>[145,"\x5E"],"\x95"=>[147,"\x5E"],"\x96"=>[149,"\x5E"],"\x97"=>[101,"\x5E"],"\x98"=>[112,"\x5E"],"\x99"=>[117,"\x5E"],"\x9A"=>[120,"\x5E"],"\x9B"=>[124,"\x5E"],"\x9C"=>[127,"\x5E"],"\x9D"=>[144,"\x5E"],"\x9E"=>[152,"\x5E"],"\x9F"=>[11,"\x5E"],"\xA0"=>[0,"\x7D0"],"\xA1"=>[0,"\x7D1"],"\xA2"=>[0,"\x7D2"],"\xA3"=>[0,"\x7Da"],"\xA4"=>[0,"\x7Dc"],"\xA5"=>[0,"\x7De"],"\xA6"=>[0,"\x7Di"],"\xA7"=>[0,"\x7Do"],"\xA8"=>[0,"\x7Ds"],"\xA9"=>[0,"\x7Dt"],"\xAA"=>[73,"\x7D"],"\xAB"=>[88,"\x7D"],"\xAC"=>[89,"\x7D"],"\xAD"=>[96,"\x7D"],"\xAE"=>[97,"\x7D"],"\xAF"=>[99,"\x7D"],"\xB0"=>[106,"\x7D"],"\xB1"=>[136,"\x7D"],"\xB2"=>[139,"\x7D"],"\xB3"=>[141,"\x7D"],"\xB4"=>[145,"\x7D"],"\xB5"=>[147,"\x7D"],"\xB6"=>[149,"\x7D"],"\xB7"=>[101,"\x7D"],"\xB8"=>[112,"\x7D"],"\xB9"=>[117,"\x7D"],"\xBA"=>[120,"\x7D"],"\xBB"=>[124,"\x7D"],"\xBC"=>[127,"\x7D"],"\xBD"=>[144,"\x7D"],"\xBE"=>[152,"\x7D"],"\xBF"=>[11,"\x7D"],"\xC0"=>[92,"\x3C"],"\xC1"=>[95,"\x3C"],"\xC2"=>[137,"\x3C"],"\xC3"=>[142,"\x3C"],"\xC4"=>[150,"\x3C"],"\xC5"=>[74,"\x3C"],"\xC6"=>[90,"\x3C"],"\xC7"=>[98,"\x3C"],"\xC8"=>[107,"\x3C"],"\xC9"=>[140,"\x3C"],"\xCA"=>[146,"\x3C"],"\xCB"=>[102,"\x3C"],"\xCC"=>[113,"\x3C"],"\xCD"=>[121,"\x3C"],"\xCE"=>[128,"\x3C"],"\xCF"=>[12,"\x3C"],"\xD0"=>[92,"\x60"],"\xD1"=>[95,"\x60"],"\xD2"=>[137,"\x60"],"\xD3"=>[142,"\x60"],"\xD4"=>[150,"\x60"],"\xD5"=>[74,"\x60"],"\xD6"=>[90,"\x60"],"\xD7"=>[98,"\x60"],"\xD8"=>[107,"\x60"],"\xD9"=>[140,"\x60"],"\xDA"=>[146,"\x60"],"\xDB"=>[102,"\x60"],"\xDC"=>[113,"\x60"],"\xDD"=>[121,"\x60"],"\xDE"=>[128,"\x60"],"\xDF"=>[12,"\x60"],"\xE0"=>[92,"\x7B"],"\xE1"=>[95,"\x7B"],"\xE2"=>[137,"\x7B"],"\xE3"=>[142,"\x7B"],"\xE4"=>[150,"\x7B"],"\xE5"=>[74,"\x7B"],"\xE6"=>[90,"\x7B"],"\xE7"=>[98,"\x7B"],"\xE8"=>[107,"\x7B"],"\xE9"=>[140,"\x7B"],"\xEA"=>[146,"\x7B"],"\xEB"=>[102,"\x7B"],"\xEC"=>[113,"\x7B"],"\xED"=>[121,"\x7B"],"\xEE"=>[128,"\x7B"],"\xEF"=>[12,"\x7B"],"\xF0"=>[0,"\x5C"],"\xF1"=>[0,"\xC3"],"\xF2"=>[0,"\xD0"],"\xF3"=>[155,null],"\xF4"=>[162,null],"\xF5"=>[211,null],"\xF6"=>[248,null],"\xF7"=>[183,null],"\xF8"=>[204,null],"\xF9"=>[243,null],"\xFA"=>[159,null],"\xFB"=>[187,null],"\xFC"=>[208,null],"\xFD"=>[29,null],"\xFE"=>[177,null],"\xFF"=>[37,null],],["\x00"=>[0,"\xC60"],"\x01"=>[0,"\xC61"],"\x02"=>[0,"\xC62"],"\x03"=>[0,"\xC6a"],"\x04"=>[0,"\xC6c"],"\x05"=>[0,"\xC6e"],"\x06"=>[0,"\xC6i"],"\x07"=>[0,"\xC6o"],"\x08"=>[0,"\xC6s"],"\x09"=>[0,"\xC6t"],"\x0A"=>[73,"\xC6"],"\x0B"=>[88,"\xC6"],"\x0C"=>[89,"\xC6"],"\x0D"=>[96,"\xC6"],"\x0E"=>[97,"\xC6"],"\x0F"=>[99,"\xC6"],"\x10"=>[106,"\xC6"],"\x11"=>[136,"\xC6"],"\x12"=>[139,"\xC6"],"\x13"=>[141,"\xC6"],"\x14"=>[145,"\xC6"],"\x15"=>[147,"\xC6"],"\x16"=>[149,"\xC6"],"\x17"=>[101,"\xC6"],"\x18"=>[112,"\xC6"],"\x19"=>[117,"\xC6"],"\x1A"=>[120,"\xC6"],"\x1B"=>[124,"\xC6"],"\x1C"=>[127,"\xC6"],"\x1D"=>[144,"\xC6"],"\x1E"=>[152,"\xC6"],"\x1F"=>[11,"\xC6"],"\x20"=>[0,"\xE40"],"\x21"=>[0,"\xE41"],"\x22"=>[0,"\xE42"],"\x23"=>[0,"\xE4a"],"\x24"=>[0,"\xE4c"],"\x25"=>[0,"\xE4e"],"\x26"=>[0,"\xE4i"],"\x27"=>[0,"\xE4o"],"\x28"=>[0,"\xE4s"],"\x29"=>[0,"\xE4t"],"\x2A"=>[73,"\xE4"],"\x2B"=>[88,"\xE4"],"\x2C"=>[89,"\xE4"],"-"=>[96,"\xE4"],"."=>[97,"\xE4"],"\x2F"=>[99,"\xE4"],[106,"\xE4"],[136,"\xE4"],[139,"\xE4"],[141,"\xE4"],[145,"\xE4"],[147,"\xE4"],[149,"\xE4"],[101,"\xE4"],[112,"\xE4"],[117,"\xE4"],"\x3A"=>[120,"\xE4"],"\x3B"=>[124,"\xE4"],"\x3C"=>[127,"\xE4"],"\x3D"=>[144,"\xE4"],"\x3E"=>[152,"\xE4"],"\x3F"=>[11,"\xE4"],"\x40"=>[0,"\xE80"],"A"=>[0,"\xE81"],"B"=>[0,"\xE82"],"C"=>[0,"\xE8a"],"D"=>[0,"\xE8c"],"E"=>[0,"\xE8e"],"F"=>[0,"\xE8i"],"G"=>[0,"\xE8o"],"H"=>[0,"\xE8s"],"I"=>[0,"\xE8t"],"J"=>[73,"\xE8"],"K"=>[88,"\xE8"],"L"=>[89,"\xE8"],"M"=>[96,"\xE8"],"N"=>[97,"\xE8"],"O"=>[99,"\xE8"],"P"=>[106,"\xE8"],"Q"=>[136,"\xE8"],"R"=>[139,"\xE8"],"S"=>[141,"\xE8"],"T"=>[145,"\xE8"],"U"=>[147,"\xE8"],"V"=>[149,"\xE8"],"W"=>[101,"\xE8"],"X"=>[112,"\xE8"],"Y"=>[117,"\xE8"],"Z"=>[120,"\xE8"],"\x5B"=>[124,"\xE8"],"\x5C"=>[127,"\xE8"],"\x5D"=>[144,"\xE8"],"\x5E"=>[152,"\xE8"],"_"=>[11,"\xE8"],"\x60"=>[0,"\xE90"],"a"=>[0,"\xE91"],"b"=>[0,"\xE92"],"c"=>[0,"\xE9a"],"d"=>[0,"\xE9c"],"e"=>[0,"\xE9e"],"f"=>[0,"\xE9i"],"g"=>[0,"\xE9o"],"h"=>[0,"\xE9s"],"i"=>[0,"\xE9t"],"j"=>[73,"\xE9"],"k"=>[88,"\xE9"],"l"=>[89,"\xE9"],"m"=>[96,"\xE9"],"n"=>[97,"\xE9"],"o"=>[99,"\xE9"],"p"=>[106,"\xE9"],"q"=>[136,"\xE9"],"r"=>[139,"\xE9"],"s"=>[141,"\xE9"],"t"=>[145,"\xE9"],"u"=>[147,"\xE9"],"v"=>[149,"\xE9"],"w"=>[101,"\xE9"],"x"=>[112,"\xE9"],"y"=>[117,"\xE9"],"z"=>[120,"\xE9"],"\x7B"=>[124,"\xE9"],"\x7C"=>[127,"\xE9"],"\x7D"=>[144,"\xE9"],"~"=>[152,"\xE9"],"\x7F"=>[11,"\xE9"],"\x80"=>[92,"\x01"],"\x81"=>[95,"\x01"],"\x82"=>[137,"\x01"],"\x83"=>[142,"\x01"],"\x84"=>[150,"\x01"],"\x85"=>[74,"\x01"],"\x86"=>[90,"\x01"],"\x87"=>[98,"\x01"],"\x88"=>[107,"\x01"],"\x89"=>[140,"\x01"],"\x8A"=>[146,"\x01"],"\x8B"=>[102,"\x01"],"\x8C"=>[113,"\x01"],"\x8D"=>[121,"\x01"],"\x8E"=>[128,"\x01"],"\x8F"=>[12,"\x01"],"\x90"=>[92,"\x87"],"\x91"=>[95,"\x87"],"\x92"=>[137,"\x87"],"\x93"=>[142,"\x87"],"\x94"=>[150,"\x87"],"\x95"=>[74,"\x87"],"\x96"=>[90,"\x87"],"\x97"=>[98,"\x87"],"\x98"=>[107,"\x87"],"\x99"=>[140,"\x87"],"\x9A"=>[146,"\x87"],"\x9B"=>[102,"\x87"],"\x9C"=>[113,"\x87"],"\x9D"=>[121,"\x87"],"\x9E"=>[128,"\x87"],"\x9F"=>[12,"\x87"],"\xA0"=>[92,"\x89"],"\xA1"=>[95,"\x89"],"\xA2"=>[137,"\x89"],"\xA3"=>[142,"\x89"],"\xA4"=>[150,"\x89"],"\xA5"=>[74,"\x89"],"\xA6"=>[90,"\x89"],"\xA7"=>[98,"\x89"],"\xA8"=>[107,"\x89"],"\xA9"=>[140,"\x89"],"\xAA"=>[146,"\x89"],"\xAB"=>[102,"\x89"],"\xAC"=>[113,"\x89"],"\xAD"=>[121,"\x89"],"\xAE"=>[128,"\x89"],"\xAF"=>[12,"\x89"],"\xB0"=>[92,"\x8A"],"\xB1"=>[95,"\x8A"],"\xB2"=>[137,"\x8A"],"\xB3"=>[142,"\x8A"],"\xB4"=>[150,"\x8A"],"\xB5"=>[74,"\x8A"],"\xB6"=>[90,"\x8A"],"\xB7"=>[98,"\x8A"],"\xB8"=>[107,"\x8A"],"\xB9"=>[140,"\x8A"],"\xBA"=>[146,"\x8A"],"\xBB"=>[102,"\x8A"],"\xBC"=>[113,"\x8A"],"\xBD"=>[121,"\x8A"],"\xBE"=>[128,"\x8A"],"\xBF"=>[12,"\x8A"],"\xC0"=>[92,"\x8B"],"\xC1"=>[95,"\x8B"],"\xC2"=>[137,"\x8B"],"\xC3"=>[142,"\x8B"],"\xC4"=>[150,"\x8B"],"\xC5"=>[74,"\x8B"],"\xC6"=>[90,"\x8B"],"\xC7"=>[98,"\x8B"],"\xC8"=>[107,"\x8B"],"\xC9"=>[140,"\x8B"],"\xCA"=>[146,"\x8B"],"\xCB"=>[102,"\x8B"],"\xCC"=>[113,"\x8B"],"\xCD"=>[121,"\x8B"],"\xCE"=>[128,"\x8B"],"\xCF"=>[12,"\x8B"],"\xD0"=>[92,"\x8C"],"\xD1"=>[95,"\x8C"],"\xD2"=>[137,"\x8C"],"\xD3"=>[142,"\x8C"],"\xD4"=>[150,"\x8C"],"\xD5"=>[74,"\x8C"],"\xD6"=>[90,"\x8C"],"\xD7"=>[98,"\x8C"],"\xD8"=>[107,"\x8C"],"\xD9"=>[140,"\x8C"],"\xDA"=>[146,"\x8C"],"\xDB"=>[102,"\x8C"],"\xDC"=>[113,"\x8C"],"\xDD"=>[121,"\x8C"],"\xDE"=>[128,"\x8C"],"\xDF"=>[12,"\x8C"],"\xE0"=>[92,"\x8D"],"\xE1"=>[95,"\x8D"],"\xE2"=>[137,"\x8D"],"\xE3"=>[142,"\x8D"],"\xE4"=>[150,"\x8D"],"\xE5"=>[74,"\x8D"],"\xE6"=>[90,"\x8D"],"\xE7"=>[98,"\x8D"],"\xE8"=>[107,"\x8D"],"\xE9"=>[140,"\x8D"],"\xEA"=>[146,"\x8D"],"\xEB"=>[102,"\x8D"],"\xEC"=>[113,"\x8D"],"\xED"=>[121,"\x8D"],"\xEE"=>[128,"\x8D"],"\xEF"=>[12,"\x8D"],"\xF0"=>[92,"\x8F"],"\xF1"=>[95,"\x8F"],"\xF2"=>[137,"\x8F"],"\xF3"=>[142,"\x8F"],"\xF4"=>[150,"\x8F"],"\xF5"=>[74,"\x8F"],"\xF6"=>[90,"\x8F"],"\xF7"=>[98,"\x8F"],"\xF8"=>[107,"\x8F"],"\xF9"=>[140,"\x8F"],"\xFA"=>[146,"\x8F"],"\xFB"=>[102,"\x8F"],"\xFC"=>[113,"\x8F"],"\xFD"=>[121,"\x8F"],"\xFE"=>[128,"\x8F"],"\xFF"=>[12,"\x8F"],],["\x00"=>[92,"\xB2"],"\x01"=>[95,"\xB2"],"\x02"=>[137,"\xB2"],"\x03"=>[142,"\xB2"],"\x04"=>[150,"\xB2"],"\x05"=>[74,"\xB2"],"\x06"=>[90,"\xB2"],"\x07"=>[98,"\xB2"],"\x08"=>[107,"\xB2"],"\x09"=>[140,"\xB2"],"\x0A"=>[146,"\xB2"],"\x0B"=>[102,"\xB2"],"\x0C"=>[113,"\xB2"],"\x0D"=>[121,"\xB2"],"\x0E"=>[128,"\xB2"],"\x0F"=>[12,"\xB2"],"\x10"=>[92,"\xB5"],"\x11"=>[95,"\xB5"],"\x12"=>[137,"\xB5"],"\x13"=>[142,"\xB5"],"\x14"=>[150,"\xB5"],"\x15"=>[74,"\xB5"],"\x16"=>[90,"\xB5"],"\x17"=>[98,"\xB5"],"\x18"=>[107,"\xB5"],"\x19"=>[140,"\xB5"],"\x1A"=>[146,"\xB5"],"\x1B"=>[102,"\xB5"],"\x1C"=>[113,"\xB5"],"\x1D"=>[121,"\xB5"],"\x1E"=>[128,"\xB5"],"\x1F"=>[12,"\xB5"],"\x20"=>[92,"\xB9"],"\x21"=>[95,"\xB9"],"\x22"=>[137,"\xB9"],"\x23"=>[142,"\xB9"],"\x24"=>[150,"\xB9"],"\x25"=>[74,"\xB9"],"\x26"=>[90,"\xB9"],"\x27"=>[98,"\xB9"],"\x28"=>[107,"\xB9"],"\x29"=>[140,"\xB9"],"\x2A"=>[146,"\xB9"],"\x2B"=>[102,"\xB9"],"\x2C"=>[113,"\xB9"],"-"=>[121,"\xB9"],"."=>[128,"\xB9"],"\x2F"=>[12,"\xB9"],[92,"\xBA"],[95,"\xBA"],[137,"\xBA"],[142,"\xBA"],[150,"\xBA"],[74,"\xBA"],[90,"\xBA"],[98,"\xBA"],[107,"\xBA"],[140,"\xBA"],"\x3A"=>[146,"\xBA"],"\x3B"=>[102,"\xBA"],"\x3C"=>[113,"\xBA"],"\x3D"=>[121,"\xBA"],"\x3E"=>[128,"\xBA"],"\x3F"=>[12,"\xBA"],"\x40"=>[92,"\xBB"],"A"=>[95,"\xBB"],"B"=>[137,"\xBB"],"C"=>[142,"\xBB"],"D"=>[150,"\xBB"],"E"=>[74,"\xBB"],"F"=>[90,"\xBB"],"G"=>[98,"\xBB"],"H"=>[107,"\xBB"],"I"=>[140,"\xBB"],"J"=>[146,"\xBB"],"K"=>[102,"\xBB"],"L"=>[113,"\xBB"],"M"=>[121,"\xBB"],"N"=>[128,"\xBB"],"O"=>[12,"\xBB"],"P"=>[92,"\xBD"],"Q"=>[95,"\xBD"],"R"=>[137,"\xBD"],"S"=>[142,"\xBD"],"T"=>[150,"\xBD"],"U"=>[74,"\xBD"],"V"=>[90,"\xBD"],"W"=>[98,"\xBD"],"X"=>[107,"\xBD"],"Y"=>[140,"\xBD"],"Z"=>[146,"\xBD"],"\x5B"=>[102,"\xBD"],"\x5C"=>[113,"\xBD"],"\x5D"=>[121,"\xBD"],"\x5E"=>[128,"\xBD"],"_"=>[12,"\xBD"],"\x60"=>[92,"\xBE"],"a"=>[95,"\xBE"],"b"=>[137,"\xBE"],"c"=>[142,"\xBE"],"d"=>[150,"\xBE"],"e"=>[74,"\xBE"],"f"=>[90,"\xBE"],"g"=>[98,"\xBE"],"h"=>[107,"\xBE"],"i"=>[140,"\xBE"],"j"=>[146,"\xBE"],"k"=>[102,"\xBE"],"l"=>[113,"\xBE"],"m"=>[121,"\xBE"],"n"=>[128,"\xBE"],"o"=>[12,"\xBE"],"p"=>[92,"\xC4"],"q"=>[95,"\xC4"],"r"=>[137,"\xC4"],"s"=>[142,"\xC4"],"t"=>[150,"\xC4"],"u"=>[74,"\xC4"],"v"=>[90,"\xC4"],"w"=>[98,"\xC4"],"x"=>[107,"\xC4"],"y"=>[140,"\xC4"],"z"=>[146,"\xC4"],"\x7B"=>[102,"\xC4"],"\x7C"=>[113,"\xC4"],"\x7D"=>[121,"\xC4"],"~"=>[128,"\xC4"],"\x7F"=>[12,"\xC4"],"\x80"=>[92,"\xC6"],"\x81"=>[95,"\xC6"],"\x82"=>[137,"\xC6"],"\x83"=>[142,"\xC6"],"\x84"=>[150,"\xC6"],"\x85"=>[74,"\xC6"],"\x86"=>[90,"\xC6"],"\x87"=>[98,"\xC6"],"\x88"=>[107,"\xC6"],"\x89"=>[140,"\xC6"],"\x8A"=>[146,"\xC6"],"\x8B"=>[102,"\xC6"],"\x8C"=>[113,"\xC6"],"\x8D"=>[121,"\xC6"],"\x8E"=>[128,"\xC6"],"\x8F"=>[12,"\xC6"],"\x90"=>[92,"\xE4"],"\x91"=>[95,"\xE4"],"\x92"=>[137,"\xE4"],"\x93"=>[142,"\xE4"],"\x94"=>[150,"\xE4"],"\x95"=>[74,"\xE4"],"\x96"=>[90,"\xE4"],"\x97"=>[98,"\xE4"],"\x98"=>[107,"\xE4"],"\x99"=>[140,"\xE4"],"\x9A"=>[146,"\xE4"],"\x9B"=>[102,"\xE4"],"\x9C"=>[113,"\xE4"],"\x9D"=>[121,"\xE4"],"\x9E"=>[128,"\xE4"],"\x9F"=>[12,"\xE4"],"\xA0"=>[92,"\xE8"],"\xA1"=>[95,"\xE8"],"\xA2"=>[137,"\xE8"],"\xA3"=>[142,"\xE8"],"\xA4"=>[150,"\xE8"],"\xA5"=>[74,"\xE8"],"\xA6"=>[90,"\xE8"],"\xA7"=>[98,"\xE8"],"\xA8"=>[107,"\xE8"],"\xA9"=>[140,"\xE8"],"\xAA"=>[146,"\xE8"],"\xAB"=>[102,"\xE8"],"\xAC"=>[113,"\xE8"],"\xAD"=>[121,"\xE8"],"\xAE"=>[128,"\xE8"],"\xAF"=>[12,"\xE8"],"\xB0"=>[92,"\xE9"],"\xB1"=>[95,"\xE9"],"\xB2"=>[137,"\xE9"],"\xB3"=>[142,"\xE9"],"\xB4"=>[150,"\xE9"],"\xB5"=>[74,"\xE9"],"\xB6"=>[90,"\xE9"],"\xB7"=>[98,"\xE9"],"\xB8"=>[107,"\xE9"],"\xB9"=>[140,"\xE9"],"\xBA"=>[146,"\xE9"],"\xBB"=>[102,"\xE9"],"\xBC"=>[113,"\xE9"],"\xBD"=>[121,"\xE9"],"\xBE"=>[128,"\xE9"],"\xBF"=>[12,"\xE9"],"\xC0"=>[93,"\x01"],"\xC1"=>[138,"\x01"],"\xC2"=>[75,"\x01"],"\xC3"=>[91,"\x01"],"\xC4"=>[108,"\x01"],"\xC5"=>[103,"\x01"],"\xC6"=>[114,"\x01"],"\xC7"=>[14,"\x01"],"\xC8"=>[93,"\x87"],"\xC9"=>[138,"\x87"],"\xCA"=>[75,"\x87"],"\xCB"=>[91,"\x87"],"\xCC"=>[108,"\x87"],"\xCD"=>[103,"\x87"],"\xCE"=>[114,"\x87"],"\xCF"=>[14,"\x87"],"\xD0"=>[93,"\x89"],"\xD1"=>[138,"\x89"],"\xD2"=>[75,"\x89"],"\xD3"=>[91,"\x89"],"\xD4"=>[108,"\x89"],"\xD5"=>[103,"\x89"],"\xD6"=>[114,"\x89"],"\xD7"=>[14,"\x89"],"\xD8"=>[93,"\x8A"],"\xD9"=>[138,"\x8A"],"\xDA"=>[75,"\x8A"],"\xDB"=>[91,"\x8A"],"\xDC"=>[108,"\x8A"],"\xDD"=>[103,"\x8A"],"\xDE"=>[114,"\x8A"],"\xDF"=>[14,"\x8A"],"\xE0"=>[93,"\x8B"],"\xE1"=>[138,"\x8B"],"\xE2"=>[75,"\x8B"],"\xE3"=>[91,"\x8B"],"\xE4"=>[108,"\x8B"],"\xE5"=>[103,"\x8B"],"\xE6"=>[114,"\x8B"],"\xE7"=>[14,"\x8B"],"\xE8"=>[93,"\x8C"],"\xE9"=>[138,"\x8C"],"\xEA"=>[75,"\x8C"],"\xEB"=>[91,"\x8C"],"\xEC"=>[108,"\x8C"],"\xED"=>[103,"\x8C"],"\xEE"=>[114,"\x8C"],"\xEF"=>[14,"\x8C"],"\xF0"=>[93,"\x8D"],"\xF1"=>[138,"\x8D"],"\xF2"=>[75,"\x8D"],"\xF3"=>[91,"\x8D"],"\xF4"=>[108,"\x8D"],"\xF5"=>[103,"\x8D"],"\xF6"=>[114,"\x8D"],"\xF7"=>[14,"\x8D"],"\xF8"=>[93,"\x8F"],"\xF9"=>[138,"\x8F"],"\xFA"=>[75,"\x8F"],"\xFB"=>[91,"\x8F"],"\xFC"=>[108,"\x8F"],"\xFD"=>[103,"\x8F"],"\xFE"=>[114,"\x8F"],"\xFF"=>[14,"\x8F"],],["\x00"=>[93,"\xB2"],"\x01"=>[138,"\xB2"],"\x02"=>[75,"\xB2"],"\x03"=>[91,"\xB2"],"\x04"=>[108,"\xB2"],"\x05"=>[103,"\xB2"],"\x06"=>[114,"\xB2"],"\x07"=>[14,"\xB2"],"\x08"=>[93,"\xB5"],"\x09"=>[138,"\xB5"],"\x0A"=>[75,"\xB5"],"\x0B"=>[91,"\xB5"],"\x0C"=>[108,"\xB5"],"\x0D"=>[103,"\xB5"],"\x0E"=>[114,"\xB5"],"\x0F"=>[14,"\xB5"],"\x10"=>[93,"\xB9"],"\x11"=>[138,"\xB9"],"\x12"=>[75,"\xB9"],"\x13"=>[91,"\xB9"],"\x14"=>[108,"\xB9"],"\x15"=>[103,"\xB9"],"\x16"=>[114,"\xB9"],"\x17"=>[14,"\xB9"],"\x18"=>[93,"\xBA"],"\x19"=>[138,"\xBA"],"\x1A"=>[75,"\xBA"],"\x1B"=>[91,"\xBA"],"\x1C"=>[108,"\xBA"],"\x1D"=>[103,"\xBA"],"\x1E"=>[114,"\xBA"],"\x1F"=>[14,"\xBA"],"\x20"=>[93,"\xBB"],"\x21"=>[138,"\xBB"],"\x22"=>[75,"\xBB"],"\x23"=>[91,"\xBB"],"\x24"=>[108,"\xBB"],"\x25"=>[103,"\xBB"],"\x26"=>[114,"\xBB"],"\x27"=>[14,"\xBB"],"\x28"=>[93,"\xBD"],"\x29"=>[138,"\xBD"],"\x2A"=>[75,"\xBD"],"\x2B"=>[91,"\xBD"],"\x2C"=>[108,"\xBD"],"-"=>[103,"\xBD"],"."=>[114,"\xBD"],"\x2F"=>[14,"\xBD"],[93,"\xBE"],[138,"\xBE"],[75,"\xBE"],[91,"\xBE"],[108,"\xBE"],[103,"\xBE"],[114,"\xBE"],[14,"\xBE"],[93,"\xC4"],[138,"\xC4"],"\x3A"=>[75,"\xC4"],"\x3B"=>[91,"\xC4"],"\x3C"=>[108,"\xC4"],"\x3D"=>[103,"\xC4"],"\x3E"=>[114,"\xC4"],"\x3F"=>[14,"\xC4"],"\x40"=>[93,"\xC6"],"A"=>[138,"\xC6"],"B"=>[75,"\xC6"],"C"=>[91,"\xC6"],"D"=>[108,"\xC6"],"E"=>[103,"\xC6"],"F"=>[114,"\xC6"],"G"=>[14,"\xC6"],"H"=>[93,"\xE4"],"I"=>[138,"\xE4"],"J"=>[75,"\xE4"],"K"=>[91,"\xE4"],"L"=>[108,"\xE4"],"M"=>[103,"\xE4"],"N"=>[114,"\xE4"],"O"=>[14,"\xE4"],"P"=>[93,"\xE8"],"Q"=>[138,"\xE8"],"R"=>[75,"\xE8"],"S"=>[91,"\xE8"],"T"=>[108,"\xE8"],"U"=>[103,"\xE8"],"V"=>[114,"\xE8"],"W"=>[14,"\xE8"],"X"=>[93,"\xE9"],"Y"=>[138,"\xE9"],"Z"=>[75,"\xE9"],"\x5B"=>[91,"\xE9"],"\x5C"=>[108,"\xE9"],"\x5D"=>[103,"\xE9"],"\x5E"=>[114,"\xE9"],"_"=>[14,"\xE9"],"\x60"=>[94,"\x01"],"a"=>[76,"\x01"],"b"=>[104,"\x01"],"c"=>[16,"\x01"],"d"=>[94,"\x87"],"e"=>[76,"\x87"],"f"=>[104,"\x87"],"g"=>[16,"\x87"],"h"=>[94,"\x89"],"i"=>[76,"\x89"],"j"=>[104,"\x89"],"k"=>[16,"\x89"],"l"=>[94,"\x8A"],"m"=>[76,"\x8A"],"n"=>[104,"\x8A"],"o"=>[16,"\x8A"],"p"=>[94,"\x8B"],"q"=>[76,"\x8B"],"r"=>[104,"\x8B"],"s"=>[16,"\x8B"],"t"=>[94,"\x8C"],"u"=>[76,"\x8C"],"v"=>[104,"\x8C"],"w"=>[16,"\x8C"],"x"=>[94,"\x8D"],"y"=>[76,"\x8D"],"z"=>[104,"\x8D"],"\x7B"=>[16,"\x8D"],"\x7C"=>[94,"\x8F"],"\x7D"=>[76,"\x8F"],"~"=>[104,"\x8F"],"\x7F"=>[16,"\x8F"],"\x80"=>[94,"\x93"],"\x81"=>[76,"\x93"],"\x82"=>[104,"\x93"],"\x83"=>[16,"\x93"],"\x84"=>[94,"\x95"],"\x85"=>[76,"\x95"],"\x86"=>[104,"\x95"],"\x87"=>[16,"\x95"],"\x88"=>[94,"\x96"],"\x89"=>[76,"\x96"],"\x8A"=>[104,"\x96"],"\x8B"=>[16,"\x96"],"\x8C"=>[94,"\x97"],"\x8D"=>[76,"\x97"],"\x8E"=>[104,"\x97"],"\x8F"=>[16,"\x97"],"\x90"=>[94,"\x98"],"\x91"=>[76,"\x98"],"\x92"=>[104,"\x98"],"\x93"=>[16,"\x98"],"\x94"=>[94,"\x9B"],"\x95"=>[76,"\x9B"],"\x96"=>[104,"\x9B"],"\x97"=>[16,"\x9B"],"\x98"=>[94,"\x9D"],"\x99"=>[76,"\x9D"],"\x9A"=>[104,"\x9D"],"\x9B"=>[16,"\x9D"],"\x9C"=>[94,"\x9E"],"\x9D"=>[76,"\x9E"],"\x9E"=>[104,"\x9E"],"\x9F"=>[16,"\x9E"],"\xA0"=>[94,"\xA5"],"\xA1"=>[76,"\xA5"],"\xA2"=>[104,"\xA5"],"\xA3"=>[16,"\xA5"],"\xA4"=>[94,"\xA6"],"\xA5"=>[76,"\xA6"],"\xA6"=>[104,"\xA6"],"\xA7"=>[16,"\xA6"],"\xA8"=>[94,"\xA8"],"\xA9"=>[76,"\xA8"],"\xAA"=>[104,"\xA8"],"\xAB"=>[16,"\xA8"],"\xAC"=>[94,"\xAE"],"\xAD"=>[76,"\xAE"],"\xAE"=>[104,"\xAE"],"\xAF"=>[16,"\xAE"],"\xB0"=>[94,"\xAF"],"\xB1"=>[76,"\xAF"],"\xB2"=>[104,"\xAF"],"\xB3"=>[16,"\xAF"],"\xB4"=>[94,"\xB4"],"\xB5"=>[76,"\xB4"],"\xB6"=>[104,"\xB4"],"\xB7"=>[16,"\xB4"],"\xB8"=>[94,"\xB6"],"\xB9"=>[76,"\xB6"],"\xBA"=>[104,"\xB6"],"\xBB"=>[16,"\xB6"],"\xBC"=>[94,"\xB7"],"\xBD"=>[76,"\xB7"],"\xBE"=>[104,"\xB7"],"\xBF"=>[16,"\xB7"],"\xC0"=>[94,"\xBC"],"\xC1"=>[76,"\xBC"],"\xC2"=>[104,"\xBC"],"\xC3"=>[16,"\xBC"],"\xC4"=>[94,"\xBF"],"\xC5"=>[76,"\xBF"],"\xC6"=>[104,"\xBF"],"\xC7"=>[16,"\xBF"],"\xC8"=>[94,"\xC5"],"\xC9"=>[76,"\xC5"],"\xCA"=>[104,"\xC5"],"\xCB"=>[16,"\xC5"],"\xCC"=>[94,"\xE7"],"\xCD"=>[76,"\xE7"],"\xCE"=>[104,"\xE7"],"\xCF"=>[16,"\xE7"],"\xD0"=>[94,"\xEF"],"\xD1"=>[76,"\xEF"],"\xD2"=>[104,"\xEF"],"\xD3"=>[16,"\xEF"],"\xD4"=>[77,"\x09"],"\xD5"=>[18,"\x09"],"\xD6"=>[77,"\x8E"],"\xD7"=>[18,"\x8E"],"\xD8"=>[77,"\x90"],"\xD9"=>[18,"\x90"],"\xDA"=>[77,"\x91"],"\xDB"=>[18,"\x91"],"\xDC"=>[77,"\x94"],"\xDD"=>[18,"\x94"],"\xDE"=>[77,"\x9F"],"\xDF"=>[18,"\x9F"],"\xE0"=>[77,"\xAB"],"\xE1"=>[18,"\xAB"],"\xE2"=>[77,"\xCE"],"\xE3"=>[18,"\xCE"],"\xE4"=>[77,"\xD7"],"\xE5"=>[18,"\xD7"],"\xE6"=>[77,"\xE1"],"\xE7"=>[18,"\xE1"],"\xE8"=>[77,"\xEC"],"\xE9"=>[18,"\xEC"],"\xEA"=>[77,"\xED"],"\xEB"=>[18,"\xED"],"\xEC"=>[0,"\xC7"],"\xED"=>[0,"\xCF"],"\xEE"=>[0,"\xEA"],"\xEF"=>[0,"\xEB"],"\xF0"=>[221,null],"\xF1"=>[228,null],"\xF2"=>[230,null],"\xF3"=>[235,null],"\xF4"=>[245,null],"\xF5"=>[253,null],"\xF6"=>[255,null],"\xF7"=>[234,null],"\xF8"=>[239,null],"\xF9"=>[247,null],"\xFA"=>[258,null],"\xFB"=>[261,null],"\xFC"=>[41,null],"\xFD"=>[47,null],"\xFE"=>[64,null],"\xFF"=>[57,null],],["\x00"=>[0,"\xFE0"],"\x01"=>[0,"\xFE1"],"\x02"=>[0,"\xFE2"],"\x03"=>[0,"\xFEa"],"\x04"=>[0,"\xFEc"],"\x05"=>[0,"\xFEe"],"\x06"=>[0,"\xFEi"],"\x07"=>[0,"\xFEo"],"\x08"=>[0,"\xFEs"],"\x09"=>[0,"\xFEt"],"\x0A"=>[73,"\xFE"],"\x0B"=>[88,"\xFE"],"\x0C"=>[89,"\xFE"],"\x0D"=>[96,"\xFE"],"\x0E"=>[97,"\xFE"],"\x0F"=>[99,"\xFE"],"\x10"=>[106,"\xFE"],"\x11"=>[136,"\xFE"],"\x12"=>[139,"\xFE"],"\x13"=>[141,"\xFE"],"\x14"=>[145,"\xFE"],"\x15"=>[147,"\xFE"],"\x16"=>[149,"\xFE"],"\x17"=>[101,"\xFE"],"\x18"=>[112,"\xFE"],"\x19"=>[117,"\xFE"],"\x1A"=>[120,"\xFE"],"\x1B"=>[124,"\xFE"],"\x1C"=>[127,"\xFE"],"\x1D"=>[144,"\xFE"],"\x1E"=>[152,"\xFE"],"\x1F"=>[11,"\xFE"],"\x20"=>[92,"\x02"],"\x21"=>[95,"\x02"],"\x22"=>[137,"\x02"],"\x23"=>[142,"\x02"],"\x24"=>[150,"\x02"],"\x25"=>[74,"\x02"],"\x26"=>[90,"\x02"],"\x27"=>[98,"\x02"],"\x28"=>[107,"\x02"],"\x29"=>[140,"\x02"],"\x2A"=>[146,"\x02"],"\x2B"=>[102,"\x02"],"\x2C"=>[113,"\x02"],"-"=>[121,"\x02"],"."=>[128,"\x02"],"\x2F"=>[12,"\x02"],[92,"\x03"],[95,"\x03"],[137,"\x03"],[142,"\x03"],[150,"\x03"],[74,"\x03"],[90,"\x03"],[98,"\x03"],[107,"\x03"],[140,"\x03"],"\x3A"=>[146,"\x03"],"\x3B"=>[102,"\x03"],"\x3C"=>[113,"\x03"],"\x3D"=>[121,"\x03"],"\x3E"=>[128,"\x03"],"\x3F"=>[12,"\x03"],"\x40"=>[92,"\x04"],"A"=>[95,"\x04"],"B"=>[137,"\x04"],"C"=>[142,"\x04"],"D"=>[150,"\x04"],"E"=>[74,"\x04"],"F"=>[90,"\x04"],"G"=>[98,"\x04"],"H"=>[107,"\x04"],"I"=>[140,"\x04"],"J"=>[146,"\x04"],"K"=>[102,"\x04"],"L"=>[113,"\x04"],"M"=>[121,"\x04"],"N"=>[128,"\x04"],"O"=>[12,"\x04"],"P"=>[92,"\x05"],"Q"=>[95,"\x05"],"R"=>[137,"\x05"],"S"=>[142,"\x05"],"T"=>[150,"\x05"],"U"=>[74,"\x05"],"V"=>[90,"\x05"],"W"=>[98,"\x05"],"X"=>[107,"\x05"],"Y"=>[140,"\x05"],"Z"=>[146,"\x05"],"\x5B"=>[102,"\x05"],"\x5C"=>[113,"\x05"],"\x5D"=>[121,"\x05"],"\x5E"=>[128,"\x05"],"_"=>[12,"\x05"],"\x60"=>[92,"\x06"],"a"=>[95,"\x06"],"b"=>[137,"\x06"],"c"=>[142,"\x06"],"d"=>[150,"\x06"],"e"=>[74,"\x06"],"f"=>[90,"\x06"],"g"=>[98,"\x06"],"h"=>[107,"\x06"],"i"=>[140,"\x06"],"j"=>[146,"\x06"],"k"=>[102,"\x06"],"l"=>[113,"\x06"],"m"=>[121,"\x06"],"n"=>[128,"\x06"],"o"=>[12,"\x06"],"p"=>[92,"\x07"],"q"=>[95,"\x07"],"r"=>[137,"\x07"],"s"=>[142,"\x07"],"t"=>[150,"\x07"],"u"=>[74,"\x07"],"v"=>[90,"\x07"],"w"=>[98,"\x07"],"x"=>[107,"\x07"],"y"=>[140,"\x07"],"z"=>[146,"\x07"],"\x7B"=>[102,"\x07"],"\x7C"=>[113,"\x07"],"\x7D"=>[121,"\x07"],"~"=>[128,"\x07"],"\x7F"=>[12,"\x07"],"\x80"=>[92,"\x08"],"\x81"=>[95,"\x08"],"\x82"=>[137,"\x08"],"\x83"=>[142,"\x08"],"\x84"=>[150,"\x08"],"\x85"=>[74,"\x08"],"\x86"=>[90,"\x08"],"\x87"=>[98,"\x08"],"\x88"=>[107,"\x08"],"\x89"=>[140,"\x08"],"\x8A"=>[146,"\x08"],"\x8B"=>[102,"\x08"],"\x8C"=>[113,"\x08"],"\x8D"=>[121,"\x08"],"\x8E"=>[128,"\x08"],"\x8F"=>[12,"\x08"],"\x90"=>[92,"\x0B"],"\x91"=>[95,"\x0B"],"\x92"=>[137,"\x0B"],"\x93"=>[142,"\x0B"],"\x94"=>[150,"\x0B"],"\x95"=>[74,"\x0B"],"\x96"=>[90,"\x0B"],"\x97"=>[98,"\x0B"],"\x98"=>[107,"\x0B"],"\x99"=>[140,"\x0B"],"\x9A"=>[146,"\x0B"],"\x9B"=>[102,"\x0B"],"\x9C"=>[113,"\x0B"],"\x9D"=>[121,"\x0B"],"\x9E"=>[128,"\x0B"],"\x9F"=>[12,"\x0B"],"\xA0"=>[92,"\x0C"],"\xA1"=>[95,"\x0C"],"\xA2"=>[137,"\x0C"],"\xA3"=>[142,"\x0C"],"\xA4"=>[150,"\x0C"],"\xA5"=>[74,"\x0C"],"\xA6"=>[90,"\x0C"],"\xA7"=>[98,"\x0C"],"\xA8"=>[107,"\x0C"],"\xA9"=>[140,"\x0C"],"\xAA"=>[146,"\x0C"],"\xAB"=>[102,"\x0C"],"\xAC"=>[113,"\x0C"],"\xAD"=>[121,"\x0C"],"\xAE"=>[128,"\x0C"],"\xAF"=>[12,"\x0C"],"\xB0"=>[92,"\x0E"],"\xB1"=>[95,"\x0E"],"\xB2"=>[137,"\x0E"],"\xB3"=>[142,"\x0E"],"\xB4"=>[150,"\x0E"],"\xB5"=>[74,"\x0E"],"\xB6"=>[90,"\x0E"],"\xB7"=>[98,"\x0E"],"\xB8"=>[107,"\x0E"],"\xB9"=>[140,"\x0E"],"\xBA"=>[146,"\x0E"],"\xBB"=>[102,"\x0E"],"\xBC"=>[113,"\x0E"],"\xBD"=>[121,"\x0E"],"\xBE"=>[128,"\x0E"],"\xBF"=>[12,"\x0E"],"\xC0"=>[92,"\x0F"],"\xC1"=>[95,"\x0F"],"\xC2"=>[137,"\x0F"],"\xC3"=>[142,"\x0F"],"\xC4"=>[150,"\x0F"],"\xC5"=>[74,"\x0F"],"\xC6"=>[90,"\x0F"],"\xC7"=>[98,"\x0F"],"\xC8"=>[107,"\x0F"],"\xC9"=>[140,"\x0F"],"\xCA"=>[146,"\x0F"],"\xCB"=>[102,"\x0F"],"\xCC"=>[113,"\x0F"],"\xCD"=>[121,"\x0F"],"\xCE"=>[128,"\x0F"],"\xCF"=>[12,"\x0F"],"\xD0"=>[92,"\x10"],"\xD1"=>[95,"\x10"],"\xD2"=>[137,"\x10"],"\xD3"=>[142,"\x10"],"\xD4"=>[150,"\x10"],"\xD5"=>[74,"\x10"],"\xD6"=>[90,"\x10"],"\xD7"=>[98,"\x10"],"\xD8"=>[107,"\x10"],"\xD9"=>[140,"\x10"],"\xDA"=>[146,"\x10"],"\xDB"=>[102,"\x10"],"\xDC"=>[113,"\x10"],"\xDD"=>[121,"\x10"],"\xDE"=>[128,"\x10"],"\xDF"=>[12,"\x10"],"\xE0"=>[92,"\x11"],"\xE1"=>[95,"\x11"],"\xE2"=>[137,"\x11"],"\xE3"=>[142,"\x11"],"\xE4"=>[150,"\x11"],"\xE5"=>[74,"\x11"],"\xE6"=>[90,"\x11"],"\xE7"=>[98,"\x11"],"\xE8"=>[107,"\x11"],"\xE9"=>[140,"\x11"],"\xEA"=>[146,"\x11"],"\xEB"=>[102,"\x11"],"\xEC"=>[113,"\x11"],"\xED"=>[121,"\x11"],"\xEE"=>[128,"\x11"],"\xEF"=>[12,"\x11"],"\xF0"=>[92,"\x12"],"\xF1"=>[95,"\x12"],"\xF2"=>[137,"\x12"],"\xF3"=>[142,"\x12"],"\xF4"=>[150,"\x12"],"\xF5"=>[74,"\x12"],"\xF6"=>[90,"\x12"],"\xF7"=>[98,"\x12"],"\xF8"=>[107,"\x12"],"\xF9"=>[140,"\x12"],"\xFA"=>[146,"\x12"],"\xFB"=>[102,"\x12"],"\xFC"=>[113,"\x12"],"\xFD"=>[121,"\x12"],"\xFE"=>[128,"\x12"],"\xFF"=>[12,"\x12"],],["\x00"=>[92,"\xFE"],"\x01"=>[95,"\xFE"],"\x02"=>[137,"\xFE"],"\x03"=>[142,"\xFE"],"\x04"=>[150,"\xFE"],"\x05"=>[74,"\xFE"],"\x06"=>[90,"\xFE"],"\x07"=>[98,"\xFE"],"\x08"=>[107,"\xFE"],"\x09"=>[140,"\xFE"],"\x0A"=>[146,"\xFE"],"\x0B"=>[102,"\xFE"],"\x0C"=>[113,"\xFE"],"\x0D"=>[121,"\xFE"],"\x0E"=>[128,"\xFE"],"\x0F"=>[12,"\xFE"],"\x10"=>[93,"\x02"],"\x11"=>[138,"\x02"],"\x12"=>[75,"\x02"],"\x13"=>[91,"\x02"],"\x14"=>[108,"\x02"],"\x15"=>[103,"\x02"],"\x16"=>[114,"\x02"],"\x17"=>[14,"\x02"],"\x18"=>[93,"\x03"],"\x19"=>[138,"\x03"],"\x1A"=>[75,"\x03"],"\x1B"=>[91,"\x03"],"\x1C"=>[108,"\x03"],"\x1D"=>[103,"\x03"],"\x1E"=>[114,"\x03"],"\x1F"=>[14,"\x03"],"\x20"=>[93,"\x04"],"\x21"=>[138,"\x04"],"\x22"=>[75,"\x04"],"\x23"=>[91,"\x04"],"\x24"=>[108,"\x04"],"\x25"=>[103,"\x04"],"\x26"=>[114,"\x04"],"\x27"=>[14,"\x04"],"\x28"=>[93,"\x05"],"\x29"=>[138,"\x05"],"\x2A"=>[75,"\x05"],"\x2B"=>[91,"\x05"],"\x2C"=>[108,"\x05"],"-"=>[103,"\x05"],"."=>[114,"\x05"],"\x2F"=>[14,"\x05"],[93,"\x06"],[138,"\x06"],[75,"\x06"],[91,"\x06"],[108,"\x06"],[103,"\x06"],[114,"\x06"],[14,"\x06"],[93,"\x07"],[138,"\x07"],"\x3A"=>[75,"\x07"],"\x3B"=>[91,"\x07"],"\x3C"=>[108,"\x07"],"\x3D"=>[103,"\x07"],"\x3E"=>[114,"\x07"],"\x3F"=>[14,"\x07"],"\x40"=>[93,"\x08"],"A"=>[138,"\x08"],"B"=>[75,"\x08"],"C"=>[91,"\x08"],"D"=>[108,"\x08"],"E"=>[103,"\x08"],"F"=>[114,"\x08"],"G"=>[14,"\x08"],"H"=>[93,"\x0B"],"I"=>[138,"\x0B"],"J"=>[75,"\x0B"],"K"=>[91,"\x0B"],"L"=>[108,"\x0B"],"M"=>[103,"\x0B"],"N"=>[114,"\x0B"],"O"=>[14,"\x0B"],"P"=>[93,"\x0C"],"Q"=>[138,"\x0C"],"R"=>[75,"\x0C"],"S"=>[91,"\x0C"],"T"=>[108,"\x0C"],"U"=>[103,"\x0C"],"V"=>[114,"\x0C"],"W"=>[14,"\x0C"],"X"=>[93,"\x0E"],"Y"=>[138,"\x0E"],"Z"=>[75,"\x0E"],"\x5B"=>[91,"\x0E"],"\x5C"=>[108,"\x0E"],"\x5D"=>[103,"\x0E"],"\x5E"=>[114,"\x0E"],"_"=>[14,"\x0E"],"\x60"=>[93,"\x0F"],"a"=>[138,"\x0F"],"b"=>[75,"\x0F"],"c"=>[91,"\x0F"],"d"=>[108,"\x0F"],"e"=>[103,"\x0F"],"f"=>[114,"\x0F"],"g"=>[14,"\x0F"],"h"=>[93,"\x10"],"i"=>[138,"\x10"],"j"=>[75,"\x10"],"k"=>[91,"\x10"],"l"=>[108,"\x10"],"m"=>[103,"\x10"],"n"=>[114,"\x10"],"o"=>[14,"\x10"],"p"=>[93,"\x11"],"q"=>[138,"\x11"],"r"=>[75,"\x11"],"s"=>[91,"\x11"],"t"=>[108,"\x11"],"u"=>[103,"\x11"],"v"=>[114,"\x11"],"w"=>[14,"\x11"],"x"=>[93,"\x12"],"y"=>[138,"\x12"],"z"=>[75,"\x12"],"\x7B"=>[91,"\x12"],"\x7C"=>[108,"\x12"],"\x7D"=>[103,"\x12"],"~"=>[114,"\x12"],"\x7F"=>[14,"\x12"],"\x80"=>[93,"\x13"],"\x81"=>[138,"\x13"],"\x82"=>[75,"\x13"],"\x83"=>[91,"\x13"],"\x84"=>[108,"\x13"],"\x85"=>[103,"\x13"],"\x86"=>[114,"\x13"],"\x87"=>[14,"\x13"],"\x88"=>[93,"\x14"],"\x89"=>[138,"\x14"],"\x8A"=>[75,"\x14"],"\x8B"=>[91,"\x14"],"\x8C"=>[108,"\x14"],"\x8D"=>[103,"\x14"],"\x8E"=>[114,"\x14"],"\x8F"=>[14,"\x14"],"\x90"=>[93,"\x15"],"\x91"=>[138,"\x15"],"\x92"=>[75,"\x15"],"\x93"=>[91,"\x15"],"\x94"=>[108,"\x15"],"\x95"=>[103,"\x15"],"\x96"=>[114,"\x15"],"\x97"=>[14,"\x15"],"\x98"=>[93,"\x17"],"\x99"=>[138,"\x17"],"\x9A"=>[75,"\x17"],"\x9B"=>[91,"\x17"],"\x9C"=>[108,"\x17"],"\x9D"=>[103,"\x17"],"\x9E"=>[114,"\x17"],"\x9F"=>[14,"\x17"],"\xA0"=>[93,"\x18"],"\xA1"=>[138,"\x18"],"\xA2"=>[75,"\x18"],"\xA3"=>[91,"\x18"],"\xA4"=>[108,"\x18"],"\xA5"=>[103,"\x18"],"\xA6"=>[114,"\x18"],"\xA7"=>[14,"\x18"],"\xA8"=>[93,"\x19"],"\xA9"=>[138,"\x19"],"\xAA"=>[75,"\x19"],"\xAB"=>[91,"\x19"],"\xAC"=>[108,"\x19"],"\xAD"=>[103,"\x19"],"\xAE"=>[114,"\x19"],"\xAF"=>[14,"\x19"],"\xB0"=>[93,"\x1A"],"\xB1"=>[138,"\x1A"],"\xB2"=>[75,"\x1A"],"\xB3"=>[91,"\x1A"],"\xB4"=>[108,"\x1A"],"\xB5"=>[103,"\x1A"],"\xB6"=>[114,"\x1A"],"\xB7"=>[14,"\x1A"],"\xB8"=>[93,"\x1B"],"\xB9"=>[138,"\x1B"],"\xBA"=>[75,"\x1B"],"\xBB"=>[91,"\x1B"],"\xBC"=>[108,"\x1B"],"\xBD"=>[103,"\x1B"],"\xBE"=>[114,"\x1B"],"\xBF"=>[14,"\x1B"],"\xC0"=>[93,"\x1C"],"\xC1"=>[138,"\x1C"],"\xC2"=>[75,"\x1C"],"\xC3"=>[91,"\x1C"],"\xC4"=>[108,"\x1C"],"\xC5"=>[103,"\x1C"],"\xC6"=>[114,"\x1C"],"\xC7"=>[14,"\x1C"],"\xC8"=>[93,"\x1D"],"\xC9"=>[138,"\x1D"],"\xCA"=>[75,"\x1D"],"\xCB"=>[91,"\x1D"],"\xCC"=>[108,"\x1D"],"\xCD"=>[103,"\x1D"],"\xCE"=>[114,"\x1D"],"\xCF"=>[14,"\x1D"],"\xD0"=>[93,"\x1E"],"\xD1"=>[138,"\x1E"],"\xD2"=>[75,"\x1E"],"\xD3"=>[91,"\x1E"],"\xD4"=>[108,"\x1E"],"\xD5"=>[103,"\x1E"],"\xD6"=>[114,"\x1E"],"\xD7"=>[14,"\x1E"],"\xD8"=>[93,"\x1F"],"\xD9"=>[138,"\x1F"],"\xDA"=>[75,"\x1F"],"\xDB"=>[91,"\x1F"],"\xDC"=>[108,"\x1F"],"\xDD"=>[103,"\x1F"],"\xDE"=>[114,"\x1F"],"\xDF"=>[14,"\x1F"],"\xE0"=>[93,"\x7F"],"\xE1"=>[138,"\x7F"],"\xE2"=>[75,"\x7F"],"\xE3"=>[91,"\x7F"],"\xE4"=>[108,"\x7F"],"\xE5"=>[103,"\x7F"],"\xE6"=>[114,"\x7F"],"\xE7"=>[14,"\x7F"],"\xE8"=>[93,"\xDC"],"\xE9"=>[138,"\xDC"],"\xEA"=>[75,"\xDC"],"\xEB"=>[91,"\xDC"],"\xEC"=>[108,"\xDC"],"\xED"=>[103,"\xDC"],"\xEE"=>[114,"\xDC"],"\xEF"=>[14,"\xDC"],"\xF0"=>[93,"\xF9"],"\xF1"=>[138,"\xF9"],"\xF2"=>[75,"\xF9"],"\xF3"=>[91,"\xF9"],"\xF4"=>[108,"\xF9"],"\xF5"=>[103,"\xF9"],"\xF6"=>[114,"\xF9"],"\xF7"=>[14,"\xF9"],"\xF8"=>[77,"\x0A"],"\xF9"=>[18,"\x0A"],"\xFA"=>[77,"\x0D"],"\xFB"=>[18,"\x0D"],"\xFC"=>[77,"\x16"],"\xFD"=>[18,"\x16"],"\xFE"=>[77,""],"\xFF"=>[18,""],],["\x00"=>[93,"\xD3"],"\x01"=>[138,"\xD3"],"\x02"=>[75,"\xD3"],"\x03"=>[91,"\xD3"],"\x04"=>[108,"\xD3"],"\x05"=>[103,"\xD3"],"\x06"=>[114,"\xD3"],"\x07"=>[14,"\xD3"],"\x08"=>[93,"\xD4"],"\x09"=>[138,"\xD4"],"\x0A"=>[75,"\xD4"],"\x0B"=>[91,"\xD4"],"\x0C"=>[108,"\xD4"],"\x0D"=>[103,"\xD4"],"\x0E"=>[114,"\xD4"],"\x0F"=>[14,"\xD4"],"\x10"=>[93,"\xD6"],"\x11"=>[138,"\xD6"],"\x12"=>[75,"\xD6"],"\x13"=>[91,"\xD6"],"\x14"=>[108,"\xD6"],"\x15"=>[103,"\xD6"],"\x16"=>[114,"\xD6"],"\x17"=>[14,"\xD6"],"\x18"=>[93,"\xDD"],"\x19"=>[138,"\xDD"],"\x1A"=>[75,"\xDD"],"\x1B"=>[91,"\xDD"],"\x1C"=>[108,"\xDD"],"\x1D"=>[103,"\xDD"],"\x1E"=>[114,"\xDD"],"\x1F"=>[14,"\xDD"],"\x20"=>[93,"\xDE"],"\x21"=>[138,"\xDE"],"\x22"=>[75,"\xDE"],"\x23"=>[91,"\xDE"],"\x24"=>[108,"\xDE"],"\x25"=>[103,"\xDE"],"\x26"=>[114,"\xDE"],"\x27"=>[14,"\xDE"],"\x28"=>[93,"\xDF"],"\x29"=>[138,"\xDF"],"\x2A"=>[75,"\xDF"],"\x2B"=>[91,"\xDF"],"\x2C"=>[108,"\xDF"],"-"=>[103,"\xDF"],"."=>[114,"\xDF"],"\x2F"=>[14,"\xDF"],[93,"\xF1"],[138,"\xF1"],[75,"\xF1"],[91,"\xF1"],[108,"\xF1"],[103,"\xF1"],[114,"\xF1"],[14,"\xF1"],[93,"\xF4"],[138,"\xF4"],"\x3A"=>[75,"\xF4"],"\x3B"=>[91,"\xF4"],"\x3C"=>[108,"\xF4"],"\x3D"=>[103,"\xF4"],"\x3E"=>[114,"\xF4"],"\x3F"=>[14,"\xF4"],"\x40"=>[93,"\xF5"],"A"=>[138,"\xF5"],"B"=>[75,"\xF5"],"C"=>[91,"\xF5"],"D"=>[108,"\xF5"],"E"=>[103,"\xF5"],"F"=>[114,"\xF5"],"G"=>[14,"\xF5"],"H"=>[93,"\xF6"],"I"=>[138,"\xF6"],"J"=>[75,"\xF6"],"K"=>[91,"\xF6"],"L"=>[108,"\xF6"],"M"=>[103,"\xF6"],"N"=>[114,"\xF6"],"O"=>[14,"\xF6"],"P"=>[93,"\xF7"],"Q"=>[138,"\xF7"],"R"=>[75,"\xF7"],"S"=>[91,"\xF7"],"T"=>[108,"\xF7"],"U"=>[103,"\xF7"],"V"=>[114,"\xF7"],"W"=>[14,"\xF7"],"X"=>[93,"\xF8"],"Y"=>[138,"\xF8"],"Z"=>[75,"\xF8"],"\x5B"=>[91,"\xF8"],"\x5C"=>[108,"\xF8"],"\x5D"=>[103,"\xF8"],"\x5E"=>[114,"\xF8"],"_"=>[14,"\xF8"],"\x60"=>[93,"\xFA"],"a"=>[138,"\xFA"],"b"=>[75,"\xFA"],"c"=>[91,"\xFA"],"d"=>[108,"\xFA"],"e"=>[103,"\xFA"],"f"=>[114,"\xFA"],"g"=>[14,"\xFA"],"h"=>[93,"\xFB"],"i"=>[138,"\xFB"],"j"=>[75,"\xFB"],"k"=>[91,"\xFB"],"l"=>[108,"\xFB"],"m"=>[103,"\xFB"],"n"=>[114,"\xFB"],"o"=>[14,"\xFB"],"p"=>[93,"\xFC"],"q"=>[138,"\xFC"],"r"=>[75,"\xFC"],"s"=>[91,"\xFC"],"t"=>[108,"\xFC"],"u"=>[103,"\xFC"],"v"=>[114,"\xFC"],"w"=>[14,"\xFC"],"x"=>[93,"\xFD"],"y"=>[138,"\xFD"],"z"=>[75,"\xFD"],"\x7B"=>[91,"\xFD"],"\x7C"=>[108,"\xFD"],"\x7D"=>[103,"\xFD"],"~"=>[114,"\xFD"],"\x7F"=>[14,"\xFD"],"\x80"=>[93,"\xFE"],"\x81"=>[138,"\xFE"],"\x82"=>[75,"\xFE"],"\x83"=>[91,"\xFE"],"\x84"=>[108,"\xFE"],"\x85"=>[103,"\xFE"],"\x86"=>[114,"\xFE"],"\x87"=>[14,"\xFE"],"\x88"=>[94,"\x02"],"\x89"=>[76,"\x02"],"\x8A"=>[104,"\x02"],"\x8B"=>[16,"\x02"],"\x8C"=>[94,"\x03"],"\x8D"=>[76,"\x03"],"\x8E"=>[104,"\x03"],"\x8F"=>[16,"\x03"],"\x90"=>[94,"\x04"],"\x91"=>[76,"\x04"],"\x92"=>[104,"\x04"],"\x93"=>[16,"\x04"],"\x94"=>[94,"\x05"],"\x95"=>[76,"\x05"],"\x96"=>[104,"\x05"],"\x97"=>[16,"\x05"],"\x98"=>[94,"\x06"],"\x99"=>[76,"\x06"],"\x9A"=>[104,"\x06"],"\x9B"=>[16,"\x06"],"\x9C"=>[94,"\x07"],"\x9D"=>[76,"\x07"],"\x9E"=>[104,"\x07"],"\x9F"=>[16,"\x07"],"\xA0"=>[94,"\x08"],"\xA1"=>[76,"\x08"],"\xA2"=>[104,"\x08"],"\xA3"=>[16,"\x08"],"\xA4"=>[94,"\x0B"],"\xA5"=>[76,"\x0B"],"\xA6"=>[104,"\x0B"],"\xA7"=>[16,"\x0B"],"\xA8"=>[94,"\x0C"],"\xA9"=>[76,"\x0C"],"\xAA"=>[104,"\x0C"],"\xAB"=>[16,"\x0C"],"\xAC"=>[94,"\x0E"],"\xAD"=>[76,"\x0E"],"\xAE"=>[104,"\x0E"],"\xAF"=>[16,"\x0E"],"\xB0"=>[94,"\x0F"],"\xB1"=>[76,"\x0F"],"\xB2"=>[104,"\x0F"],"\xB3"=>[16,"\x0F"],"\xB4"=>[94,"\x10"],"\xB5"=>[76,"\x10"],"\xB6"=>[104,"\x10"],"\xB7"=>[16,"\x10"],"\xB8"=>[94,"\x11"],"\xB9"=>[76,"\x11"],"\xBA"=>[104,"\x11"],"\xBB"=>[16,"\x11"],"\xBC"=>[94,"\x12"],"\xBD"=>[76,"\x12"],"\xBE"=>[104,"\x12"],"\xBF"=>[16,"\x12"],"\xC0"=>[94,"\x13"],"\xC1"=>[76,"\x13"],"\xC2"=>[104,"\x13"],"\xC3"=>[16,"\x13"],"\xC4"=>[94,"\x14"],"\xC5"=>[76,"\x14"],"\xC6"=>[104,"\x14"],"\xC7"=>[16,"\x14"],"\xC8"=>[94,"\x15"],"\xC9"=>[76,"\x15"],"\xCA"=>[104,"\x15"],"\xCB"=>[16,"\x15"],"\xCC"=>[94,"\x17"],"\xCD"=>[76,"\x17"],"\xCE"=>[104,"\x17"],"\xCF"=>[16,"\x17"],"\xD0"=>[94,"\x18"],"\xD1"=>[76,"\x18"],"\xD2"=>[104,"\x18"],"\xD3"=>[16,"\x18"],"\xD4"=>[94,"\x19"],"\xD5"=>[76,"\x19"],"\xD6"=>[104,"\x19"],"\xD7"=>[16,"\x19"],"\xD8"=>[94,"\x1A"],"\xD9"=>[76,"\x1A"],"\xDA"=>[104,"\x1A"],"\xDB"=>[16,"\x1A"],"\xDC"=>[94,"\x1B"],"\xDD"=>[76,"\x1B"],"\xDE"=>[104,"\x1B"],"\xDF"=>[16,"\x1B"],"\xE0"=>[94,"\x1C"],"\xE1"=>[76,"\x1C"],"\xE2"=>[104,"\x1C"],"\xE3"=>[16,"\x1C"],"\xE4"=>[94,"\x1D"],"\xE5"=>[76,"\x1D"],"\xE6"=>[104,"\x1D"],"\xE7"=>[16,"\x1D"],"\xE8"=>[94,"\x1E"],"\xE9"=>[76,"\x1E"],"\xEA"=>[104,"\x1E"],"\xEB"=>[16,"\x1E"],"\xEC"=>[94,"\x1F"],"\xED"=>[76,"\x1F"],"\xEE"=>[104,"\x1F"],"\xEF"=>[16,"\x1F"],"\xF0"=>[94,"\x7F"],"\xF1"=>[76,"\x7F"],"\xF2"=>[104,"\x7F"],"\xF3"=>[16,"\x7F"],"\xF4"=>[94,"\xDC"],"\xF5"=>[76,"\xDC"],"\xF6"=>[104,"\xDC"],"\xF7"=>[16,"\xDC"],"\xF8"=>[94,"\xF9"],"\xF9"=>[76,"\xF9"],"\xFA"=>[104,"\xF9"],"\xFB"=>[16,"\xF9"],"\xFC"=>[0,"\x0A"],"\xFD"=>[0,"\x0D"],"\xFE"=>[0,"\x16"],"\xFF"=>[0,""],],["\x00"=>[93,"\xC0"],"\x01"=>[138,"\xC0"],"\x02"=>[75,"\xC0"],"\x03"=>[91,"\xC0"],"\x04"=>[108,"\xC0"],"\x05"=>[103,"\xC0"],"\x06"=>[114,"\xC0"],"\x07"=>[14,"\xC0"],"\x08"=>[93,"\xC1"],"\x09"=>[138,"\xC1"],"\x0A"=>[75,"\xC1"],"\x0B"=>[91,"\xC1"],"\x0C"=>[108,"\xC1"],"\x0D"=>[103,"\xC1"],"\x0E"=>[114,"\xC1"],"\x0F"=>[14,"\xC1"],"\x10"=>[93,"\xC8"],"\x11"=>[138,"\xC8"],"\x12"=>[75,"\xC8"],"\x13"=>[91,"\xC8"],"\x14"=>[108,"\xC8"],"\x15"=>[103,"\xC8"],"\x16"=>[114,"\xC8"],"\x17"=>[14,"\xC8"],"\x18"=>[93,"\xC9"],"\x19"=>[138,"\xC9"],"\x1A"=>[75,"\xC9"],"\x1B"=>[91,"\xC9"],"\x1C"=>[108,"\xC9"],"\x1D"=>[103,"\xC9"],"\x1E"=>[114,"\xC9"],"\x1F"=>[14,"\xC9"],"\x20"=>[93,"\xCA"],"\x21"=>[138,"\xCA"],"\x22"=>[75,"\xCA"],"\x23"=>[91,"\xCA"],"\x24"=>[108,"\xCA"],"\x25"=>[103,"\xCA"],"\x26"=>[114,"\xCA"],"\x27"=>[14,"\xCA"],"\x28"=>[93,"\xCD"],"\x29"=>[138,"\xCD"],"\x2A"=>[75,"\xCD"],"\x2B"=>[91,"\xCD"],"\x2C"=>[108,"\xCD"],"-"=>[103,"\xCD"],"."=>[114,"\xCD"],"\x2F"=>[14,"\xCD"],[93,"\xD2"],[138,"\xD2"],[75,"\xD2"],[91,"\xD2"],[108,"\xD2"],[103,"\xD2"],[114,"\xD2"],[14,"\xD2"],[93,"\xD5"],[138,"\xD5"],"\x3A"=>[75,"\xD5"],"\x3B"=>[91,"\xD5"],"\x3C"=>[108,"\xD5"],"\x3D"=>[103,"\xD5"],"\x3E"=>[114,"\xD5"],"\x3F"=>[14,"\xD5"],"\x40"=>[93,"\xDA"],"A"=>[138,"\xDA"],"B"=>[75,"\xDA"],"C"=>[91,"\xDA"],"D"=>[108,"\xDA"],"E"=>[103,"\xDA"],"F"=>[114,"\xDA"],"G"=>[14,"\xDA"],"H"=>[93,"\xDB"],"I"=>[138,"\xDB"],"J"=>[75,"\xDB"],"K"=>[91,"\xDB"],"L"=>[108,"\xDB"],"M"=>[103,"\xDB"],"N"=>[114,"\xDB"],"O"=>[14,"\xDB"],"P"=>[93,"\xEE"],"Q"=>[138,"\xEE"],"R"=>[75,"\xEE"],"S"=>[91,"\xEE"],"T"=>[108,"\xEE"],"U"=>[103,"\xEE"],"V"=>[114,"\xEE"],"W"=>[14,"\xEE"],"X"=>[93,"\xF0"],"Y"=>[138,"\xF0"],"Z"=>[75,"\xF0"],"\x5B"=>[91,"\xF0"],"\x5C"=>[108,"\xF0"],"\x5D"=>[103,"\xF0"],"\x5E"=>[114,"\xF0"],"_"=>[14,"\xF0"],"\x60"=>[93,"\xF2"],"a"=>[138,"\xF2"],"b"=>[75,"\xF2"],"c"=>[91,"\xF2"],"d"=>[108,"\xF2"],"e"=>[103,"\xF2"],"f"=>[114,"\xF2"],"g"=>[14,"\xF2"],"h"=>[93,"\xF3"],"i"=>[138,"\xF3"],"j"=>[75,"\xF3"],"k"=>[91,"\xF3"],"l"=>[108,"\xF3"],"m"=>[103,"\xF3"],"n"=>[114,"\xF3"],"o"=>[14,"\xF3"],"p"=>[93,"\xFF"],"q"=>[138,"\xFF"],"r"=>[75,"\xFF"],"s"=>[91,"\xFF"],"t"=>[108,"\xFF"],"u"=>[103,"\xFF"],"v"=>[114,"\xFF"],"w"=>[14,"\xFF"],"x"=>[94,"\xCB"],"y"=>[76,"\xCB"],"z"=>[104,"\xCB"],"\x7B"=>[16,"\xCB"],"\x7C"=>[94,"\xCC"],"\x7D"=>[76,"\xCC"],"~"=>[104,"\xCC"],"\x7F"=>[16,"\xCC"],"\x80"=>[94,"\xD3"],"\x81"=>[76,"\xD3"],"\x82"=>[104,"\xD3"],"\x83"=>[16,"\xD3"],"\x84"=>[94,"\xD4"],"\x85"=>[76,"\xD4"],"\x86"=>[104,"\xD4"],"\x87"=>[16,"\xD4"],"\x88"=>[94,"\xD6"],"\x89"=>[76,"\xD6"],"\x8A"=>[104,"\xD6"],"\x8B"=>[16,"\xD6"],"\x8C"=>[94,"\xDD"],"\x8D"=>[76,"\xDD"],"\x8E"=>[104,"\xDD"],"\x8F"=>[16,"\xDD"],"\x90"=>[94,"\xDE"],"\x91"=>[76,"\xDE"],"\x92"=>[104,"\xDE"],"\x93"=>[16,"\xDE"],"\x94"=>[94,"\xDF"],"\x95"=>[76,"\xDF"],"\x96"=>[104,"\xDF"],"\x97"=>[16,"\xDF"],"\x98"=>[94,"\xF1"],"\x99"=>[76,"\xF1"],"\x9A"=>[104,"\xF1"],"\x9B"=>[16,"\xF1"],"\x9C"=>[94,"\xF4"],"\x9D"=>[76,"\xF4"],"\x9E"=>[104,"\xF4"],"\x9F"=>[16,"\xF4"],"\xA0"=>[94,"\xF5"],"\xA1"=>[76,"\xF5"],"\xA2"=>[104,"\xF5"],"\xA3"=>[16,"\xF5"],"\xA4"=>[94,"\xF6"],"\xA5"=>[76,"\xF6"],"\xA6"=>[104,"\xF6"],"\xA7"=>[16,"\xF6"],"\xA8"=>[94,"\xF7"],"\xA9"=>[76,"\xF7"],"\xAA"=>[104,"\xF7"],"\xAB"=>[16,"\xF7"],"\xAC"=>[94,"\xF8"],"\xAD"=>[76,"\xF8"],"\xAE"=>[104,"\xF8"],"\xAF"=>[16,"\xF8"],"\xB0"=>[94,"\xFA"],"\xB1"=>[76,"\xFA"],"\xB2"=>[104,"\xFA"],"\xB3"=>[16,"\xFA"],"\xB4"=>[94,"\xFB"],"\xB5"=>[76,"\xFB"],"\xB6"=>[104,"\xFB"],"\xB7"=>[16,"\xFB"],"\xB8"=>[94,"\xFC"],"\xB9"=>[76,"\xFC"],"\xBA"=>[104,"\xFC"],"\xBB"=>[16,"\xFC"],"\xBC"=>[94,"\xFD"],"\xBD"=>[76,"\xFD"],"\xBE"=>[104,"\xFD"],"\xBF"=>[16,"\xFD"],"\xC0"=>[94,"\xFE"],"\xC1"=>[76,"\xFE"],"\xC2"=>[104,"\xFE"],"\xC3"=>[16,"\xFE"],"\xC4"=>[77,"\x02"],"\xC5"=>[18,"\x02"],"\xC6"=>[77,"\x03"],"\xC7"=>[18,"\x03"],"\xC8"=>[77,"\x04"],"\xC9"=>[18,"\x04"],"\xCA"=>[77,"\x05"],"\xCB"=>[18,"\x05"],"\xCC"=>[77,"\x06"],"\xCD"=>[18,"\x06"],"\xCE"=>[77,"\x07"],"\xCF"=>[18,"\x07"],"\xD0"=>[77,"\x08"],"\xD1"=>[18,"\x08"],"\xD2"=>[77,"\x0B"],"\xD3"=>[18,"\x0B"],"\xD4"=>[77,"\x0C"],"\xD5"=>[18,"\x0C"],"\xD6"=>[77,"\x0E"],"\xD7"=>[18,"\x0E"],"\xD8"=>[77,"\x0F"],"\xD9"=>[18,"\x0F"],"\xDA"=>[77,"\x10"],"\xDB"=>[18,"\x10"],"\xDC"=>[77,"\x11"],"\xDD"=>[18,"\x11"],"\xDE"=>[77,"\x12"],"\xDF"=>[18,"\x12"],"\xE0"=>[77,"\x13"],"\xE1"=>[18,"\x13"],"\xE2"=>[77,"\x14"],"\xE3"=>[18,"\x14"],"\xE4"=>[77,"\x15"],"\xE5"=>[18,"\x15"],"\xE6"=>[77,"\x17"],"\xE7"=>[18,"\x17"],"\xE8"=>[77,"\x18"],"\xE9"=>[18,"\x18"],"\xEA"=>[77,"\x19"],"\xEB"=>[18,"\x19"],"\xEC"=>[77,"\x1A"],"\xED"=>[18,"\x1A"],"\xEE"=>[77,"\x1B"],"\xEF"=>[18,"\x1B"],"\xF0"=>[77,"\x1C"],"\xF1"=>[18,"\x1C"],"\xF2"=>[77,"\x1D"],"\xF3"=>[18,"\x1D"],"\xF4"=>[77,"\x1E"],"\xF5"=>[18,"\x1E"],"\xF6"=>[77,"\x1F"],"\xF7"=>[18,"\x1F"],"\xF8"=>[77,"\x7F"],"\xF9"=>[18,"\x7F"],"\xFA"=>[77,"\xDC"],"\xFB"=>[18,"\xDC"],"\xFC"=>[77,"\xF9"],"\xFD"=>[18,"\xF9"],"\xFE"=>[53,null],"\xFF"=>[66,null],],["\x00"=>[92,"\xAB"],"\x01"=>[95,"\xAB"],"\x02"=>[137,"\xAB"],"\x03"=>[142,"\xAB"],"\x04"=>[150,"\xAB"],"\x05"=>[74,"\xAB"],"\x06"=>[90,"\xAB"],"\x07"=>[98,"\xAB"],"\x08"=>[107,"\xAB"],"\x09"=>[140,"\xAB"],"\x0A"=>[146,"\xAB"],"\x0B"=>[102,"\xAB"],"\x0C"=>[113,"\xAB"],"\x0D"=>[121,"\xAB"],"\x0E"=>[128,"\xAB"],"\x0F"=>[12,"\xAB"],"\x10"=>[92,"\xCE"],"\x11"=>[95,"\xCE"],"\x12"=>[137,"\xCE"],"\x13"=>[142,"\xCE"],"\x14"=>[150,"\xCE"],"\x15"=>[74,"\xCE"],"\x16"=>[90,"\xCE"],"\x17"=>[98,"\xCE"],"\x18"=>[107,"\xCE"],"\x19"=>[140,"\xCE"],"\x1A"=>[146,"\xCE"],"\x1B"=>[102,"\xCE"],"\x1C"=>[113,"\xCE"],"\x1D"=>[121,"\xCE"],"\x1E"=>[128,"\xCE"],"\x1F"=>[12,"\xCE"],"\x20"=>[92,"\xD7"],"\x21"=>[95,"\xD7"],"\x22"=>[137,"\xD7"],"\x23"=>[142,"\xD7"],"\x24"=>[150,"\xD7"],"\x25"=>[74,"\xD7"],"\x26"=>[90,"\xD7"],"\x27"=>[98,"\xD7"],"\x28"=>[107,"\xD7"],"\x29"=>[140,"\xD7"],"\x2A"=>[146,"\xD7"],"\x2B"=>[102,"\xD7"],"\x2C"=>[113,"\xD7"],"-"=>[121,"\xD7"],"."=>[128,"\xD7"],"\x2F"=>[12,"\xD7"],[92,"\xE1"],[95,"\xE1"],[137,"\xE1"],[142,"\xE1"],[150,"\xE1"],[74,"\xE1"],[90,"\xE1"],[98,"\xE1"],[107,"\xE1"],[140,"\xE1"],"\x3A"=>[146,"\xE1"],"\x3B"=>[102,"\xE1"],"\x3C"=>[113,"\xE1"],"\x3D"=>[121,"\xE1"],"\x3E"=>[128,"\xE1"],"\x3F"=>[12,"\xE1"],"\x40"=>[92,"\xEC"],"A"=>[95,"\xEC"],"B"=>[137,"\xEC"],"C"=>[142,"\xEC"],"D"=>[150,"\xEC"],"E"=>[74,"\xEC"],"F"=>[90,"\xEC"],"G"=>[98,"\xEC"],"H"=>[107,"\xEC"],"I"=>[140,"\xEC"],"J"=>[146,"\xEC"],"K"=>[102,"\xEC"],"L"=>[113,"\xEC"],"M"=>[121,"\xEC"],"N"=>[128,"\xEC"],"O"=>[12,"\xEC"],"P"=>[92,"\xED"],"Q"=>[95,"\xED"],"R"=>[137,"\xED"],"S"=>[142,"\xED"],"T"=>[150,"\xED"],"U"=>[74,"\xED"],"V"=>[90,"\xED"],"W"=>[98,"\xED"],"X"=>[107,"\xED"],"Y"=>[140,"\xED"],"Z"=>[146,"\xED"],"\x5B"=>[102,"\xED"],"\x5C"=>[113,"\xED"],"\x5D"=>[121,"\xED"],"\x5E"=>[128,"\xED"],"_"=>[12,"\xED"],"\x60"=>[93,"\xC7"],"a"=>[138,"\xC7"],"b"=>[75,"\xC7"],"c"=>[91,"\xC7"],"d"=>[108,"\xC7"],"e"=>[103,"\xC7"],"f"=>[114,"\xC7"],"g"=>[14,"\xC7"],"h"=>[93,"\xCF"],"i"=>[138,"\xCF"],"j"=>[75,"\xCF"],"k"=>[91,"\xCF"],"l"=>[108,"\xCF"],"m"=>[103,"\xCF"],"n"=>[114,"\xCF"],"o"=>[14,"\xCF"],"p"=>[93,"\xEA"],"q"=>[138,"\xEA"],"r"=>[75,"\xEA"],"s"=>[91,"\xEA"],"t"=>[108,"\xEA"],"u"=>[103,"\xEA"],"v"=>[114,"\xEA"],"w"=>[14,"\xEA"],"x"=>[93,"\xEB"],"y"=>[138,"\xEB"],"z"=>[75,"\xEB"],"\x7B"=>[91,"\xEB"],"\x7C"=>[108,"\xEB"],"\x7D"=>[103,"\xEB"],"~"=>[114,"\xEB"],"\x7F"=>[14,"\xEB"],"\x80"=>[94,"\xC0"],"\x81"=>[76,"\xC0"],"\x82"=>[104,"\xC0"],"\x83"=>[16,"\xC0"],"\x84"=>[94,"\xC1"],"\x85"=>[76,"\xC1"],"\x86"=>[104,"\xC1"],"\x87"=>[16,"\xC1"],"\x88"=>[94,"\xC8"],"\x89"=>[76,"\xC8"],"\x8A"=>[104,"\xC8"],"\x8B"=>[16,"\xC8"],"\x8C"=>[94,"\xC9"],"\x8D"=>[76,"\xC9"],"\x8E"=>[104,"\xC9"],"\x8F"=>[16,"\xC9"],"\x90"=>[94,"\xCA"],"\x91"=>[76,"\xCA"],"\x92"=>[104,"\xCA"],"\x93"=>[16,"\xCA"],"\x94"=>[94,"\xCD"],"\x95"=>[76,"\xCD"],"\x96"=>[104,"\xCD"],"\x97"=>[16,"\xCD"],"\x98"=>[94,"\xD2"],"\x99"=>[76,"\xD2"],"\x9A"=>[104,"\xD2"],"\x9B"=>[16,"\xD2"],"\x9C"=>[94,"\xD5"],"\x9D"=>[76,"\xD5"],"\x9E"=>[104,"\xD5"],"\x9F"=>[16,"\xD5"],"\xA0"=>[94,"\xDA"],"\xA1"=>[76,"\xDA"],"\xA2"=>[104,"\xDA"],"\xA3"=>[16,"\xDA"],"\xA4"=>[94,"\xDB"],"\xA5"=>[76,"\xDB"],"\xA6"=>[104,"\xDB"],"\xA7"=>[16,"\xDB"],"\xA8"=>[94,"\xEE"],"\xA9"=>[76,"\xEE"],"\xAA"=>[104,"\xEE"],"\xAB"=>[16,"\xEE"],"\xAC"=>[94,"\xF0"],"\xAD"=>[76,"\xF0"],"\xAE"=>[104,"\xF0"],"\xAF"=>[16,"\xF0"],"\xB0"=>[94,"\xF2"],"\xB1"=>[76,"\xF2"],"\xB2"=>[104,"\xF2"],"\xB3"=>[16,"\xF2"],"\xB4"=>[94,"\xF3"],"\xB5"=>[76,"\xF3"],"\xB6"=>[104,"\xF3"],"\xB7"=>[16,"\xF3"],"\xB8"=>[94,"\xFF"],"\xB9"=>[76,"\xFF"],"\xBA"=>[104,"\xFF"],"\xBB"=>[16,"\xFF"],"\xBC"=>[77,"\xCB"],"\xBD"=>[18,"\xCB"],"\xBE"=>[77,"\xCC"],"\xBF"=>[18,"\xCC"],"\xC0"=>[77,"\xD3"],"\xC1"=>[18,"\xD3"],"\xC2"=>[77,"\xD4"],"\xC3"=>[18,"\xD4"],"\xC4"=>[77,"\xD6"],"\xC5"=>[18,"\xD6"],"\xC6"=>[77,"\xDD"],"\xC7"=>[18,"\xDD"],"\xC8"=>[77,"\xDE"],"\xC9"=>[18,"\xDE"],"\xCA"=>[77,"\xDF"],"\xCB"=>[18,"\xDF"],"\xCC"=>[77,"\xF1"],"\xCD"=>[18,"\xF1"],"\xCE"=>[77,"\xF4"],"\xCF"=>[18,"\xF4"],"\xD0"=>[77,"\xF5"],"\xD1"=>[18,"\xF5"],"\xD2"=>[77,"\xF6"],"\xD3"=>[18,"\xF6"],"\xD4"=>[77,"\xF7"],"\xD5"=>[18,"\xF7"],"\xD6"=>[77,"\xF8"],"\xD7"=>[18,"\xF8"],"\xD8"=>[77,"\xFA"],"\xD9"=>[18,"\xFA"],"\xDA"=>[77,"\xFB"],"\xDB"=>[18,"\xFB"],"\xDC"=>[77,"\xFC"],"\xDD"=>[18,"\xFC"],"\xDE"=>[77,"\xFD"],"\xDF"=>[18,"\xFD"],"\xE0"=>[77,"\xFE"],"\xE1"=>[18,"\xFE"],"\xE2"=>[0,"\x02"],"\xE3"=>[0,"\x03"],"\xE4"=>[0,"\x04"],"\xE5"=>[0,"\x05"],"\xE6"=>[0,"\x06"],"\xE7"=>[0,"\x07"],"\xE8"=>[0,"\x08"],"\xE9"=>[0,"\x0B"],"\xEA"=>[0,"\x0C"],"\xEB"=>[0,"\x0E"],"\xEC"=>[0,"\x0F"],"\xED"=>[0,"\x10"],"\xEE"=>[0,"\x11"],"\xEF"=>[0,"\x12"],"\xF0"=>[0,"\x13"],"\xF1"=>[0,"\x14"],"\xF2"=>[0,"\x15"],"\xF3"=>[0,"\x17"],"\xF4"=>[0,"\x18"],"\xF5"=>[0,"\x19"],"\xF6"=>[0,"\x1A"],"\xF7"=>[0,"\x1B"],"\xF8"=>[0,"\x1C"],"\xF9"=>[0,"\x1D"],"\xFA"=>[0,"\x1E"],"\xFB"=>[0,"\x1F"],"\xFC"=>[0,"\x7F"],"\xFD"=>[0,"\xDC"],"\xFE"=>[0,"\xF9"],"\xFF"=>[54,null],],["\x00"=>[92,"\xBC"],"\x01"=>[95,"\xBC"],"\x02"=>[137,"\xBC"],"\x03"=>[142,"\xBC"],"\x04"=>[150,"\xBC"],"\x05"=>[74,"\xBC"],"\x06"=>[90,"\xBC"],"\x07"=>[98,"\xBC"],"\x08"=>[107,"\xBC"],"\x09"=>[140,"\xBC"],"\x0A"=>[146,"\xBC"],"\x0B"=>[102,"\xBC"],"\x0C"=>[113,"\xBC"],"\x0D"=>[121,"\xBC"],"\x0E"=>[128,"\xBC"],"\x0F"=>[12,"\xBC"],"\x10"=>[92,"\xBF"],"\x11"=>[95,"\xBF"],"\x12"=>[137,"\xBF"],"\x13"=>[142,"\xBF"],"\x14"=>[150,"\xBF"],"\x15"=>[74,"\xBF"],"\x16"=>[90,"\xBF"],"\x17"=>[98,"\xBF"],"\x18"=>[107,"\xBF"],"\x19"=>[140,"\xBF"],"\x1A"=>[146,"\xBF"],"\x1B"=>[102,"\xBF"],"\x1C"=>[113,"\xBF"],"\x1D"=>[121,"\xBF"],"\x1E"=>[128,"\xBF"],"\x1F"=>[12,"\xBF"],"\x20"=>[92,"\xC5"],"\x21"=>[95,"\xC5"],"\x22"=>[137,"\xC5"],"\x23"=>[142,"\xC5"],"\x24"=>[150,"\xC5"],"\x25"=>[74,"\xC5"],"\x26"=>[90,"\xC5"],"\x27"=>[98,"\xC5"],"\x28"=>[107,"\xC5"],"\x29"=>[140,"\xC5"],"\x2A"=>[146,"\xC5"],"\x2B"=>[102,"\xC5"],"\x2C"=>[113,"\xC5"],"-"=>[121,"\xC5"],"."=>[128,"\xC5"],"\x2F"=>[12,"\xC5"],[92,"\xE7"],[95,"\xE7"],[137,"\xE7"],[142,"\xE7"],[150,"\xE7"],[74,"\xE7"],[90,"\xE7"],[98,"\xE7"],[107,"\xE7"],[140,"\xE7"],"\x3A"=>[146,"\xE7"],"\x3B"=>[102,"\xE7"],"\x3C"=>[113,"\xE7"],"\x3D"=>[121,"\xE7"],"\x3E"=>[128,"\xE7"],"\x3F"=>[12,"\xE7"],"\x40"=>[92,"\xEF"],"A"=>[95,"\xEF"],"B"=>[137,"\xEF"],"C"=>[142,"\xEF"],"D"=>[150,"\xEF"],"E"=>[74,"\xEF"],"F"=>[90,"\xEF"],"G"=>[98,"\xEF"],"H"=>[107,"\xEF"],"I"=>[140,"\xEF"],"J"=>[146,"\xEF"],"K"=>[102,"\xEF"],"L"=>[113,"\xEF"],"M"=>[121,"\xEF"],"N"=>[128,"\xEF"],"O"=>[12,"\xEF"],"P"=>[93,"\x09"],"Q"=>[138,"\x09"],"R"=>[75,"\x09"],"S"=>[91,"\x09"],"T"=>[108,"\x09"],"U"=>[103,"\x09"],"V"=>[114,"\x09"],"W"=>[14,"\x09"],"X"=>[93,"\x8E"],"Y"=>[138,"\x8E"],"Z"=>[75,"\x8E"],"\x5B"=>[91,"\x8E"],"\x5C"=>[108,"\x8E"],"\x5D"=>[103,"\x8E"],"\x5E"=>[114,"\x8E"],"_"=>[14,"\x8E"],"\x60"=>[93,"\x90"],"a"=>[138,"\x90"],"b"=>[75,"\x90"],"c"=>[91,"\x90"],"d"=>[108,"\x90"],"e"=>[103,"\x90"],"f"=>[114,"\x90"],"g"=>[14,"\x90"],"h"=>[93,"\x91"],"i"=>[138,"\x91"],"j"=>[75,"\x91"],"k"=>[91,"\x91"],"l"=>[108,"\x91"],"m"=>[103,"\x91"],"n"=>[114,"\x91"],"o"=>[14,"\x91"],"p"=>[93,"\x94"],"q"=>[138,"\x94"],"r"=>[75,"\x94"],"s"=>[91,"\x94"],"t"=>[108,"\x94"],"u"=>[103,"\x94"],"v"=>[114,"\x94"],"w"=>[14,"\x94"],"x"=>[93,"\x9F"],"y"=>[138,"\x9F"],"z"=>[75,"\x9F"],"\x7B"=>[91,"\x9F"],"\x7C"=>[108,"\x9F"],"\x7D"=>[103,"\x9F"],"~"=>[114,"\x9F"],"\x7F"=>[14,"\x9F"],"\x80"=>[93,"\xAB"],"\x81"=>[138,"\xAB"],"\x82"=>[75,"\xAB"],"\x83"=>[91,"\xAB"],"\x84"=>[108,"\xAB"],"\x85"=>[103,"\xAB"],"\x86"=>[114,"\xAB"],"\x87"=>[14,"\xAB"],"\x88"=>[93,"\xCE"],"\x89"=>[138,"\xCE"],"\x8A"=>[75,"\xCE"],"\x8B"=>[91,"\xCE"],"\x8C"=>[108,"\xCE"],"\x8D"=>[103,"\xCE"],"\x8E"=>[114,"\xCE"],"\x8F"=>[14,"\xCE"],"\x90"=>[93,"\xD7"],"\x91"=>[138,"\xD7"],"\x92"=>[75,"\xD7"],"\x93"=>[91,"\xD7"],"\x94"=>[108,"\xD7"],"\x95"=>[103,"\xD7"],"\x96"=>[114,"\xD7"],"\x97"=>[14,"\xD7"],"\x98"=>[93,"\xE1"],"\x99"=>[138,"\xE1"],"\x9A"=>[75,"\xE1"],"\x9B"=>[91,"\xE1"],"\x9C"=>[108,"\xE1"],"\x9D"=>[103,"\xE1"],"\x9E"=>[114,"\xE1"],"\x9F"=>[14,"\xE1"],"\xA0"=>[93,"\xEC"],"\xA1"=>[138,"\xEC"],"\xA2"=>[75,"\xEC"],"\xA3"=>[91,"\xEC"],"\xA4"=>[108,"\xEC"],"\xA5"=>[103,"\xEC"],"\xA6"=>[114,"\xEC"],"\xA7"=>[14,"\xEC"],"\xA8"=>[93,"\xED"],"\xA9"=>[138,"\xED"],"\xAA"=>[75,"\xED"],"\xAB"=>[91,"\xED"],"\xAC"=>[108,"\xED"],"\xAD"=>[103,"\xED"],"\xAE"=>[114,"\xED"],"\xAF"=>[14,"\xED"],"\xB0"=>[94,"\xC7"],"\xB1"=>[76,"\xC7"],"\xB2"=>[104,"\xC7"],"\xB3"=>[16,"\xC7"],"\xB4"=>[94,"\xCF"],"\xB5"=>[76,"\xCF"],"\xB6"=>[104,"\xCF"],"\xB7"=>[16,"\xCF"],"\xB8"=>[94,"\xEA"],"\xB9"=>[76,"\xEA"],"\xBA"=>[104,"\xEA"],"\xBB"=>[16,"\xEA"],"\xBC"=>[94,"\xEB"],"\xBD"=>[76,"\xEB"],"\xBE"=>[104,"\xEB"],"\xBF"=>[16,"\xEB"],"\xC0"=>[77,"\xC0"],"\xC1"=>[18,"\xC0"],"\xC2"=>[77,"\xC1"],"\xC3"=>[18,"\xC1"],"\xC4"=>[77,"\xC8"],"\xC5"=>[18,"\xC8"],"\xC6"=>[77,"\xC9"],"\xC7"=>[18,"\xC9"],"\xC8"=>[77,"\xCA"],"\xC9"=>[18,"\xCA"],"\xCA"=>[77,"\xCD"],"\xCB"=>[18,"\xCD"],"\xCC"=>[77,"\xD2"],"\xCD"=>[18,"\xD2"],"\xCE"=>[77,"\xD5"],"\xCF"=>[18,"\xD5"],"\xD0"=>[77,"\xDA"],"\xD1"=>[18,"\xDA"],"\xD2"=>[77,"\xDB"],"\xD3"=>[18,"\xDB"],"\xD4"=>[77,"\xEE"],"\xD5"=>[18,"\xEE"],"\xD6"=>[77,"\xF0"],"\xD7"=>[18,"\xF0"],"\xD8"=>[77,"\xF2"],"\xD9"=>[18,"\xF2"],"\xDA"=>[77,"\xF3"],"\xDB"=>[18,"\xF3"],"\xDC"=>[77,"\xFF"],"\xDD"=>[18,"\xFF"],"\xDE"=>[0,"\xCB"],"\xDF"=>[0,"\xCC"],"\xE0"=>[0,"\xD3"],"\xE1"=>[0,"\xD4"],"\xE2"=>[0,"\xD6"],"\xE3"=>[0,"\xDD"],"\xE4"=>[0,"\xDE"],"\xE5"=>[0,"\xDF"],"\xE6"=>[0,"\xF1"],"\xE7"=>[0,"\xF4"],"\xE8"=>[0,"\xF5"],"\xE9"=>[0,"\xF6"],"\xEA"=>[0,"\xF7"],"\xEB"=>[0,"\xF8"],"\xEC"=>[0,"\xFA"],"\xED"=>[0,"\xFB"],"\xEE"=>[0,"\xFC"],"\xEF"=>[0,"\xFD"],"\xF0"=>[0,"\xFE"],"\xF1"=>[38,null],"\xF2"=>[42,null],"\xF3"=>[44,null],"\xF4"=>[45,null],"\xF5"=>[58,null],"\xF6"=>[59,null],"\xF7"=>[61,null],"\xF8"=>[62,null],"\xF9"=>[65,null],"\xFA"=>[67,null],"\xFB"=>[69,null],"\xFC"=>[70,null],"\xFD"=>[72,null],"\xFE"=>[154,null],"\xFF"=>[55,null],],["\x00"=>[94,"\x020"],"\x01"=>[76,"\x020"],"\x02"=>[104,"\x020"],"\x03"=>[16,"\x020"],"\x04"=>[94,"\x021"],"\x05"=>[76,"\x021"],"\x06"=>[104,"\x021"],"\x07"=>[16,"\x021"],"\x08"=>[94,"\x022"],"\x09"=>[76,"\x022"],"\x0A"=>[104,"\x022"],"\x0B"=>[16,"\x022"],"\x0C"=>[94,"\x02a"],"\x0D"=>[76,"\x02a"],"\x0E"=>[104,"\x02a"],"\x0F"=>[16,"\x02a"],"\x10"=>[94,"\x02c"],"\x11"=>[76,"\x02c"],"\x12"=>[104,"\x02c"],"\x13"=>[16,"\x02c"],"\x14"=>[94,"\x02e"],"\x15"=>[76,"\x02e"],"\x16"=>[104,"\x02e"],"\x17"=>[16,"\x02e"],"\x18"=>[94,"\x02i"],"\x19"=>[76,"\x02i"],"\x1A"=>[104,"\x02i"],"\x1B"=>[16,"\x02i"],"\x1C"=>[94,"\x02o"],"\x1D"=>[76,"\x02o"],"\x1E"=>[104,"\x02o"],"\x1F"=>[16,"\x02o"],"\x20"=>[94,"\x02s"],"\x21"=>[76,"\x02s"],"\x22"=>[104,"\x02s"],"\x23"=>[16,"\x02s"],"\x24"=>[94,"\x02t"],"\x25"=>[76,"\x02t"],"\x26"=>[104,"\x02t"],"\x27"=>[16,"\x02t"],"\x28"=>[77,"\x02\x20"],"\x29"=>[18,"\x02\x20"],"\x2A"=>[77,"\x02\x25"],"\x2B"=>[18,"\x02\x25"],"\x2C"=>[77,"\x02-"],"-"=>[18,"\x02-"],"."=>[77,"\x02."],"\x2F"=>[18,"\x02."],[77,"\x02\x2F"],[18,"\x02\x2F"],[77,"\x023"],[18,"\x023"],[77,"\x024"],[18,"\x024"],[77,"\x025"],[18,"\x025"],[77,"\x026"],[18,"\x026"],"\x3A"=>[77,"\x027"],"\x3B"=>[18,"\x027"],"\x3C"=>[77,"\x028"],"\x3D"=>[18,"\x028"],"\x3E"=>[77,"\x029"],"\x3F"=>[18,"\x029"],"\x40"=>[77,"\x02\x3D"],"A"=>[18,"\x02\x3D"],"B"=>[77,"\x02A"],"C"=>[18,"\x02A"],"D"=>[77,"\x02_"],"E"=>[18,"\x02_"],"F"=>[77,"\x02b"],"G"=>[18,"\x02b"],"H"=>[77,"\x02d"],"I"=>[18,"\x02d"],"J"=>[77,"\x02f"],"K"=>[18,"\x02f"],"L"=>[77,"\x02g"],"M"=>[18,"\x02g"],"N"=>[77,"\x02h"],"O"=>[18,"\x02h"],"P"=>[77,"\x02l"],"Q"=>[18,"\x02l"],"R"=>[77,"\x02m"],"S"=>[18,"\x02m"],"T"=>[77,"\x02n"],"U"=>[18,"\x02n"],"V"=>[77,"\x02p"],"W"=>[18,"\x02p"],"X"=>[77,"\x02r"],"Y"=>[18,"\x02r"],"Z"=>[77,"\x02u"],"\x5B"=>[18,"\x02u"],"\x5C"=>[0,"\x02\x3A"],"\x5D"=>[0,"\x02B"],"\x5E"=>[0,"\x02C"],"_"=>[0,"\x02D"],"\x60"=>[0,"\x02E"],"a"=>[0,"\x02F"],"b"=>[0,"\x02G"],"c"=>[0,"\x02H"],"d"=>[0,"\x02I"],"e"=>[0,"\x02J"],"f"=>[0,"\x02K"],"g"=>[0,"\x02L"],"h"=>[0,"\x02M"],"i"=>[0,"\x02N"],"j"=>[0,"\x02O"],"k"=>[0,"\x02P"],"l"=>[0,"\x02Q"],"m"=>[0,"\x02R"],"n"=>[0,"\x02S"],"o"=>[0,"\x02T"],"p"=>[0,"\x02U"],"q"=>[0,"\x02V"],"r"=>[0,"\x02W"],"s"=>[0,"\x02Y"],"t"=>[0,"\x02j"],"u"=>[0,"\x02k"],"v"=>[0,"\x02q"],"w"=>[0,"\x02v"],"x"=>[0,"\x02w"],"y"=>[0,"\x02x"],"z"=>[0,"\x02y"],"\x7B"=>[0,"\x02z"],"\x7C"=>[82,"\x02"],"\x7D"=>[87,"\x02"],"~"=>[130,"\x02"],"\x7F"=>[9,"\x02"],"\x80"=>[94,"\x030"],"\x81"=>[76,"\x030"],"\x82"=>[104,"\x030"],"\x83"=>[16,"\x030"],"\x84"=>[94,"\x031"],"\x85"=>[76,"\x031"],"\x86"=>[104,"\x031"],"\x87"=>[16,"\x031"],"\x88"=>[94,"\x032"],"\x89"=>[76,"\x032"],"\x8A"=>[104,"\x032"],"\x8B"=>[16,"\x032"],"\x8C"=>[94,"\x03a"],"\x8D"=>[76,"\x03a"],"\x8E"=>[104,"\x03a"],"\x8F"=>[16,"\x03a"],"\x90"=>[94,"\x03c"],"\x91"=>[76,"\x03c"],"\x92"=>[104,"\x03c"],"\x93"=>[16,"\x03c"],"\x94"=>[94,"\x03e"],"\x95"=>[76,"\x03e"],"\x96"=>[104,"\x03e"],"\x97"=>[16,"\x03e"],"\x98"=>[94,"\x03i"],"\x99"=>[76,"\x03i"],"\x9A"=>[104,"\x03i"],"\x9B"=>[16,"\x03i"],"\x9C"=>[94,"\x03o"],"\x9D"=>[76,"\x03o"],"\x9E"=>[104,"\x03o"],"\x9F"=>[16,"\x03o"],"\xA0"=>[94,"\x03s"],"\xA1"=>[76,"\x03s"],"\xA2"=>[104,"\x03s"],"\xA3"=>[16,"\x03s"],"\xA4"=>[94,"\x03t"],"\xA5"=>[76,"\x03t"],"\xA6"=>[104,"\x03t"],"\xA7"=>[16,"\x03t"],"\xA8"=>[77,"\x03\x20"],"\xA9"=>[18,"\x03\x20"],"\xAA"=>[77,"\x03\x25"],"\xAB"=>[18,"\x03\x25"],"\xAC"=>[77,"\x03-"],"\xAD"=>[18,"\x03-"],"\xAE"=>[77,"\x03."],"\xAF"=>[18,"\x03."],"\xB0"=>[77,"\x03\x2F"],"\xB1"=>[18,"\x03\x2F"],"\xB2"=>[77,"\x033"],"\xB3"=>[18,"\x033"],"\xB4"=>[77,"\x034"],"\xB5"=>[18,"\x034"],"\xB6"=>[77,"\x035"],"\xB7"=>[18,"\x035"],"\xB8"=>[77,"\x036"],"\xB9"=>[18,"\x036"],"\xBA"=>[77,"\x037"],"\xBB"=>[18,"\x037"],"\xBC"=>[77,"\x038"],"\xBD"=>[18,"\x038"],"\xBE"=>[77,"\x039"],"\xBF"=>[18,"\x039"],"\xC0"=>[77,"\x03\x3D"],"\xC1"=>[18,"\x03\x3D"],"\xC2"=>[77,"\x03A"],"\xC3"=>[18,"\x03A"],"\xC4"=>[77,"\x03_"],"\xC5"=>[18,"\x03_"],"\xC6"=>[77,"\x03b"],"\xC7"=>[18,"\x03b"],"\xC8"=>[77,"\x03d"],"\xC9"=>[18,"\x03d"],"\xCA"=>[77,"\x03f"],"\xCB"=>[18,"\x03f"],"\xCC"=>[77,"\x03g"],"\xCD"=>[18,"\x03g"],"\xCE"=>[77,"\x03h"],"\xCF"=>[18,"\x03h"],"\xD0"=>[77,"\x03l"],"\xD1"=>[18,"\x03l"],"\xD2"=>[77,"\x03m"],"\xD3"=>[18,"\x03m"],"\xD4"=>[77,"\x03n"],"\xD5"=>[18,"\x03n"],"\xD6"=>[77,"\x03p"],"\xD7"=>[18,"\x03p"],"\xD8"=>[77,"\x03r"],"\xD9"=>[18,"\x03r"],"\xDA"=>[77,"\x03u"],"\xDB"=>[18,"\x03u"],"\xDC"=>[0,"\x03\x3A"],"\xDD"=>[0,"\x03B"],"\xDE"=>[0,"\x03C"],"\xDF"=>[0,"\x03D"],"\xE0"=>[0,"\x03E"],"\xE1"=>[0,"\x03F"],"\xE2"=>[0,"\x03G"],"\xE3"=>[0,"\x03H"],"\xE4"=>[0,"\x03I"],"\xE5"=>[0,"\x03J"],"\xE6"=>[0,"\x03K"],"\xE7"=>[0,"\x03L"],"\xE8"=>[0,"\x03M"],"\xE9"=>[0,"\x03N"],"\xEA"=>[0,"\x03O"],"\xEB"=>[0,"\x03P"],"\xEC"=>[0,"\x03Q"],"\xED"=>[0,"\x03R"],"\xEE"=>[0,"\x03S"],"\xEF"=>[0,"\x03T"],"\xF0"=>[0,"\x03U"],"\xF1"=>[0,"\x03V"],"\xF2"=>[0,"\x03W"],"\xF3"=>[0,"\x03Y"],"\xF4"=>[0,"\x03j"],"\xF5"=>[0,"\x03k"],"\xF6"=>[0,"\x03q"],"\xF7"=>[0,"\x03v"],"\xF8"=>[0,"\x03w"],"\xF9"=>[0,"\x03x"],"\xFA"=>[0,"\x03y"],"\xFB"=>[0,"\x03z"],"\xFC"=>[82,"\x03"],"\xFD"=>[87,"\x03"],"\xFE"=>[130,"\x03"],"\xFF"=>[9,"\x03"],],["\x00"=>[93,"\x93"],"\x01"=>[138,"\x93"],"\x02"=>[75,"\x93"],"\x03"=>[91,"\x93"],"\x04"=>[108,"\x93"],"\x05"=>[103,"\x93"],"\x06"=>[114,"\x93"],"\x07"=>[14,"\x93"],"\x08"=>[93,"\x95"],"\x09"=>[138,"\x95"],"\x0A"=>[75,"\x95"],"\x0B"=>[91,"\x95"],"\x0C"=>[108,"\x95"],"\x0D"=>[103,"\x95"],"\x0E"=>[114,"\x95"],"\x0F"=>[14,"\x95"],"\x10"=>[93,"\x96"],"\x11"=>[138,"\x96"],"\x12"=>[75,"\x96"],"\x13"=>[91,"\x96"],"\x14"=>[108,"\x96"],"\x15"=>[103,"\x96"],"\x16"=>[114,"\x96"],"\x17"=>[14,"\x96"],"\x18"=>[93,"\x97"],"\x19"=>[138,"\x97"],"\x1A"=>[75,"\x97"],"\x1B"=>[91,"\x97"],"\x1C"=>[108,"\x97"],"\x1D"=>[103,"\x97"],"\x1E"=>[114,"\x97"],"\x1F"=>[14,"\x97"],"\x20"=>[93,"\x98"],"\x21"=>[138,"\x98"],"\x22"=>[75,"\x98"],"\x23"=>[91,"\x98"],"\x24"=>[108,"\x98"],"\x25"=>[103,"\x98"],"\x26"=>[114,"\x98"],"\x27"=>[14,"\x98"],"\x28"=>[93,"\x9B"],"\x29"=>[138,"\x9B"],"\x2A"=>[75,"\x9B"],"\x2B"=>[91,"\x9B"],"\x2C"=>[108,"\x9B"],"-"=>[103,"\x9B"],"."=>[114,"\x9B"],"\x2F"=>[14,"\x9B"],[93,"\x9D"],[138,"\x9D"],[75,"\x9D"],[91,"\x9D"],[108,"\x9D"],[103,"\x9D"],[114,"\x9D"],[14,"\x9D"],[93,"\x9E"],[138,"\x9E"],"\x3A"=>[75,"\x9E"],"\x3B"=>[91,"\x9E"],"\x3C"=>[108,"\x9E"],"\x3D"=>[103,"\x9E"],"\x3E"=>[114,"\x9E"],"\x3F"=>[14,"\x9E"],"\x40"=>[93,"\xA5"],"A"=>[138,"\xA5"],"B"=>[75,"\xA5"],"C"=>[91,"\xA5"],"D"=>[108,"\xA5"],"E"=>[103,"\xA5"],"F"=>[114,"\xA5"],"G"=>[14,"\xA5"],"H"=>[93,"\xA6"],"I"=>[138,"\xA6"],"J"=>[75,"\xA6"],"K"=>[91,"\xA6"],"L"=>[108,"\xA6"],"M"=>[103,"\xA6"],"N"=>[114,"\xA6"],"O"=>[14,"\xA6"],"P"=>[93,"\xA8"],"Q"=>[138,"\xA8"],"R"=>[75,"\xA8"],"S"=>[91,"\xA8"],"T"=>[108,"\xA8"],"U"=>[103,"\xA8"],"V"=>[114,"\xA8"],"W"=>[14,"\xA8"],"X"=>[93,"\xAE"],"Y"=>[138,"\xAE"],"Z"=>[75,"\xAE"],"\x5B"=>[91,"\xAE"],"\x5C"=>[108,"\xAE"],"\x5D"=>[103,"\xAE"],"\x5E"=>[114,"\xAE"],"_"=>[14,"\xAE"],"\x60"=>[93,"\xAF"],"a"=>[138,"\xAF"],"b"=>[75,"\xAF"],"c"=>[91,"\xAF"],"d"=>[108,"\xAF"],"e"=>[103,"\xAF"],"f"=>[114,"\xAF"],"g"=>[14,"\xAF"],"h"=>[93,"\xB4"],"i"=>[138,"\xB4"],"j"=>[75,"\xB4"],"k"=>[91,"\xB4"],"l"=>[108,"\xB4"],"m"=>[103,"\xB4"],"n"=>[114,"\xB4"],"o"=>[14,"\xB4"],"p"=>[93,"\xB6"],"q"=>[138,"\xB6"],"r"=>[75,"\xB6"],"s"=>[91,"\xB6"],"t"=>[108,"\xB6"],"u"=>[103,"\xB6"],"v"=>[114,"\xB6"],"w"=>[14,"\xB6"],"x"=>[93,"\xB7"],"y"=>[138,"\xB7"],"z"=>[75,"\xB7"],"\x7B"=>[91,"\xB7"],"\x7C"=>[108,"\xB7"],"\x7D"=>[103,"\xB7"],"~"=>[114,"\xB7"],"\x7F"=>[14,"\xB7"],"\x80"=>[93,"\xBC"],"\x81"=>[138,"\xBC"],"\x82"=>[75,"\xBC"],"\x83"=>[91,"\xBC"],"\x84"=>[108,"\xBC"],"\x85"=>[103,"\xBC"],"\x86"=>[114,"\xBC"],"\x87"=>[14,"\xBC"],"\x88"=>[93,"\xBF"],"\x89"=>[138,"\xBF"],"\x8A"=>[75,"\xBF"],"\x8B"=>[91,"\xBF"],"\x8C"=>[108,"\xBF"],"\x8D"=>[103,"\xBF"],"\x8E"=>[114,"\xBF"],"\x8F"=>[14,"\xBF"],"\x90"=>[93,"\xC5"],"\x91"=>[138,"\xC5"],"\x92"=>[75,"\xC5"],"\x93"=>[91,"\xC5"],"\x94"=>[108,"\xC5"],"\x95"=>[103,"\xC5"],"\x96"=>[114,"\xC5"],"\x97"=>[14,"\xC5"],"\x98"=>[93,"\xE7"],"\x99"=>[138,"\xE7"],"\x9A"=>[75,"\xE7"],"\x9B"=>[91,"\xE7"],"\x9C"=>[108,"\xE7"],"\x9D"=>[103,"\xE7"],"\x9E"=>[114,"\xE7"],"\x9F"=>[14,"\xE7"],"\xA0"=>[93,"\xEF"],"\xA1"=>[138,"\xEF"],"\xA2"=>[75,"\xEF"],"\xA3"=>[91,"\xEF"],"\xA4"=>[108,"\xEF"],"\xA5"=>[103,"\xEF"],"\xA6"=>[114,"\xEF"],"\xA7"=>[14,"\xEF"],"\xA8"=>[94,"\x09"],"\xA9"=>[76,"\x09"],"\xAA"=>[104,"\x09"],"\xAB"=>[16,"\x09"],"\xAC"=>[94,"\x8E"],"\xAD"=>[76,"\x8E"],"\xAE"=>[104,"\x8E"],"\xAF"=>[16,"\x8E"],"\xB0"=>[94,"\x90"],"\xB1"=>[76,"\x90"],"\xB2"=>[104,"\x90"],"\xB3"=>[16,"\x90"],"\xB4"=>[94,"\x91"],"\xB5"=>[76,"\x91"],"\xB6"=>[104,"\x91"],"\xB7"=>[16,"\x91"],"\xB8"=>[94,"\x94"],"\xB9"=>[76,"\x94"],"\xBA"=>[104,"\x94"],"\xBB"=>[16,"\x94"],"\xBC"=>[94,"\x9F"],"\xBD"=>[76,"\x9F"],"\xBE"=>[104,"\x9F"],"\xBF"=>[16,"\x9F"],"\xC0"=>[94,"\xAB"],"\xC1"=>[76,"\xAB"],"\xC2"=>[104,"\xAB"],"\xC3"=>[16,"\xAB"],"\xC4"=>[94,"\xCE"],"\xC5"=>[76,"\xCE"],"\xC6"=>[104,"\xCE"],"\xC7"=>[16,"\xCE"],"\xC8"=>[94,"\xD7"],"\xC9"=>[76,"\xD7"],"\xCA"=>[104,"\xD7"],"\xCB"=>[16,"\xD7"],"\xCC"=>[94,"\xE1"],"\xCD"=>[76,"\xE1"],"\xCE"=>[104,"\xE1"],"\xCF"=>[16,"\xE1"],"\xD0"=>[94,"\xEC"],"\xD1"=>[76,"\xEC"],"\xD2"=>[104,"\xEC"],"\xD3"=>[16,"\xEC"],"\xD4"=>[94,"\xED"],"\xD5"=>[76,"\xED"],"\xD6"=>[104,"\xED"],"\xD7"=>[16,"\xED"],"\xD8"=>[77,"\xC7"],"\xD9"=>[18,"\xC7"],"\xDA"=>[77,"\xCF"],"\xDB"=>[18,"\xCF"],"\xDC"=>[77,"\xEA"],"\xDD"=>[18,"\xEA"],"\xDE"=>[77,"\xEB"],"\xDF"=>[18,"\xEB"],"\xE0"=>[0,"\xC0"],"\xE1"=>[0,"\xC1"],"\xE2"=>[0,"\xC8"],"\xE3"=>[0,"\xC9"],"\xE4"=>[0,"\xCA"],"\xE5"=>[0,"\xCD"],"\xE6"=>[0,"\xD2"],"\xE7"=>[0,"\xD5"],"\xE8"=>[0,"\xDA"],"\xE9"=>[0,"\xDB"],"\xEA"=>[0,"\xEE"],"\xEB"=>[0,"\xF0"],"\xEC"=>[0,"\xF2"],"\xED"=>[0,"\xF3"],"\xEE"=>[0,"\xFF"],"\xEF"=>[233,null],"\xF0"=>[238,null],"\xF1"=>[240,null],"\xF2"=>[246,null],"\xF3"=>[254,null],"\xF4"=>[257,null],"\xF5"=>[259,null],"\xF6"=>[260,null],"\xF7"=>[262,null],"\xF8"=>[40,null],"\xF9"=>[43,null],"\xFA"=>[46,null],"\xFB"=>[60,null],"\xFC"=>[63,null],"\xFD"=>[68,null],"\xFE"=>[71,null],"\xFF"=>[56,null],],["\x00"=>[94,"\xFE0"],"\x01"=>[76,"\xFE0"],"\x02"=>[104,"\xFE0"],"\x03"=>[16,"\xFE0"],"\x04"=>[94,"\xFE1"],"\x05"=>[76,"\xFE1"],"\x06"=>[104,"\xFE1"],"\x07"=>[16,"\xFE1"],"\x08"=>[94,"\xFE2"],"\x09"=>[76,"\xFE2"],"\x0A"=>[104,"\xFE2"],"\x0B"=>[16,"\xFE2"],"\x0C"=>[94,"\xFEa"],"\x0D"=>[76,"\xFEa"],"\x0E"=>[104,"\xFEa"],"\x0F"=>[16,"\xFEa"],"\x10"=>[94,"\xFEc"],"\x11"=>[76,"\xFEc"],"\x12"=>[104,"\xFEc"],"\x13"=>[16,"\xFEc"],"\x14"=>[94,"\xFEe"],"\x15"=>[76,"\xFEe"],"\x16"=>[104,"\xFEe"],"\x17"=>[16,"\xFEe"],"\x18"=>[94,"\xFEi"],"\x19"=>[76,"\xFEi"],"\x1A"=>[104,"\xFEi"],"\x1B"=>[16,"\xFEi"],"\x1C"=>[94,"\xFEo"],"\x1D"=>[76,"\xFEo"],"\x1E"=>[104,"\xFEo"],"\x1F"=>[16,"\xFEo"],"\x20"=>[94,"\xFEs"],"\x21"=>[76,"\xFEs"],"\x22"=>[104,"\xFEs"],"\x23"=>[16,"\xFEs"],"\x24"=>[94,"\xFEt"],"\x25"=>[76,"\xFEt"],"\x26"=>[104,"\xFEt"],"\x27"=>[16,"\xFEt"],"\x28"=>[77,"\xFE\x20"],"\x29"=>[18,"\xFE\x20"],"\x2A"=>[77,"\xFE\x25"],"\x2B"=>[18,"\xFE\x25"],"\x2C"=>[77,"\xFE-"],"-"=>[18,"\xFE-"],"."=>[77,"\xFE."],"\x2F"=>[18,"\xFE."],[77,"\xFE\x2F"],[18,"\xFE\x2F"],[77,"\xFE3"],[18,"\xFE3"],[77,"\xFE4"],[18,"\xFE4"],[77,"\xFE5"],[18,"\xFE5"],[77,"\xFE6"],[18,"\xFE6"],"\x3A"=>[77,"\xFE7"],"\x3B"=>[18,"\xFE7"],"\x3C"=>[77,"\xFE8"],"\x3D"=>[18,"\xFE8"],"\x3E"=>[77,"\xFE9"],"\x3F"=>[18,"\xFE9"],"\x40"=>[77,"\xFE\x3D"],"A"=>[18,"\xFE\x3D"],"B"=>[77,"\xFEA"],"C"=>[18,"\xFEA"],"D"=>[77,"\xFE_"],"E"=>[18,"\xFE_"],"F"=>[77,"\xFEb"],"G"=>[18,"\xFEb"],"H"=>[77,"\xFEd"],"I"=>[18,"\xFEd"],"J"=>[77,"\xFEf"],"K"=>[18,"\xFEf"],"L"=>[77,"\xFEg"],"M"=>[18,"\xFEg"],"N"=>[77,"\xFEh"],"O"=>[18,"\xFEh"],"P"=>[77,"\xFEl"],"Q"=>[18,"\xFEl"],"R"=>[77,"\xFEm"],"S"=>[18,"\xFEm"],"T"=>[77,"\xFEn"],"U"=>[18,"\xFEn"],"V"=>[77,"\xFEp"],"W"=>[18,"\xFEp"],"X"=>[77,"\xFEr"],"Y"=>[18,"\xFEr"],"Z"=>[77,"\xFEu"],"\x5B"=>[18,"\xFEu"],"\x5C"=>[0,"\xFE\x3A"],"\x5D"=>[0,"\xFEB"],"\x5E"=>[0,"\xFEC"],"_"=>[0,"\xFED"],"\x60"=>[0,"\xFEE"],"a"=>[0,"\xFEF"],"b"=>[0,"\xFEG"],"c"=>[0,"\xFEH"],"d"=>[0,"\xFEI"],"e"=>[0,"\xFEJ"],"f"=>[0,"\xFEK"],"g"=>[0,"\xFEL"],"h"=>[0,"\xFEM"],"i"=>[0,"\xFEN"],"j"=>[0,"\xFEO"],"k"=>[0,"\xFEP"],"l"=>[0,"\xFEQ"],"m"=>[0,"\xFER"],"n"=>[0,"\xFES"],"o"=>[0,"\xFET"],"p"=>[0,"\xFEU"],"q"=>[0,"\xFEV"],"r"=>[0,"\xFEW"],"s"=>[0,"\xFEY"],"t"=>[0,"\xFEj"],"u"=>[0,"\xFEk"],"v"=>[0,"\xFEq"],"w"=>[0,"\xFEv"],"x"=>[0,"\xFEw"],"y"=>[0,"\xFEx"],"z"=>[0,"\xFEy"],"\x7B"=>[0,"\xFEz"],"\x7C"=>[82,"\xFE"],"\x7D"=>[87,"\xFE"],"~"=>[130,"\xFE"],"\x7F"=>[9,"\xFE"],"\x80"=>[77,"\x020"],"\x81"=>[18,"\x020"],"\x82"=>[77,"\x021"],"\x83"=>[18,"\x021"],"\x84"=>[77,"\x022"],"\x85"=>[18,"\x022"],"\x86"=>[77,"\x02a"],"\x87"=>[18,"\x02a"],"\x88"=>[77,"\x02c"],"\x89"=>[18,"\x02c"],"\x8A"=>[77,"\x02e"],"\x8B"=>[18,"\x02e"],"\x8C"=>[77,"\x02i"],"\x8D"=>[18,"\x02i"],"\x8E"=>[77,"\x02o"],"\x8F"=>[18,"\x02o"],"\x90"=>[77,"\x02s"],"\x91"=>[18,"\x02s"],"\x92"=>[77,"\x02t"],"\x93"=>[18,"\x02t"],"\x94"=>[0,"\x02\x20"],"\x95"=>[0,"\x02\x25"],"\x96"=>[0,"\x02-"],"\x97"=>[0,"\x02."],"\x98"=>[0,"\x02\x2F"],"\x99"=>[0,"\x023"],"\x9A"=>[0,"\x024"],"\x9B"=>[0,"\x025"],"\x9C"=>[0,"\x026"],"\x9D"=>[0,"\x027"],"\x9E"=>[0,"\x028"],"\x9F"=>[0,"\x029"],"\xA0"=>[0,"\x02\x3D"],"\xA1"=>[0,"\x02A"],"\xA2"=>[0,"\x02_"],"\xA3"=>[0,"\x02b"],"\xA4"=>[0,"\x02d"],"\xA5"=>[0,"\x02f"],"\xA6"=>[0,"\x02g"],"\xA7"=>[0,"\x02h"],"\xA8"=>[0,"\x02l"],"\xA9"=>[0,"\x02m"],"\xAA"=>[0,"\x02n"],"\xAB"=>[0,"\x02p"],"\xAC"=>[0,"\x02r"],"\xAD"=>[0,"\x02u"],"\xAE"=>[100,"\x02"],"\xAF"=>[110,"\x02"],"\xB0"=>[111,"\x02"],"\xB1"=>[115,"\x02"],"\xB2"=>[116,"\x02"],"\xB3"=>[118,"\x02"],"\xB4"=>[119,"\x02"],"\xB5"=>[122,"\x02"],"\xB6"=>[123,"\x02"],"\xB7"=>[125,"\x02"],"\xB8"=>[126,"\x02"],"\xB9"=>[129,"\x02"],"\xBA"=>[143,"\x02"],"\xBB"=>[148,"\x02"],"\xBC"=>[151,"\x02"],"\xBD"=>[153,"\x02"],"\xBE"=>[83,"\x02"],"\xBF"=>[10,"\x02"],"\xC0"=>[77,"\x030"],"\xC1"=>[18,"\x030"],"\xC2"=>[77,"\x031"],"\xC3"=>[18,"\x031"],"\xC4"=>[77,"\x032"],"\xC5"=>[18,"\x032"],"\xC6"=>[77,"\x03a"],"\xC7"=>[18,"\x03a"],"\xC8"=>[77,"\x03c"],"\xC9"=>[18,"\x03c"],"\xCA"=>[77,"\x03e"],"\xCB"=>[18,"\x03e"],"\xCC"=>[77,"\x03i"],"\xCD"=>[18,"\x03i"],"\xCE"=>[77,"\x03o"],"\xCF"=>[18,"\x03o"],"\xD0"=>[77,"\x03s"],"\xD1"=>[18,"\x03s"],"\xD2"=>[77,"\x03t"],"\xD3"=>[18,"\x03t"],"\xD4"=>[0,"\x03\x20"],"\xD5"=>[0,"\x03\x25"],"\xD6"=>[0,"\x03-"],"\xD7"=>[0,"\x03."],"\xD8"=>[0,"\x03\x2F"],"\xD9"=>[0,"\x033"],"\xDA"=>[0,"\x034"],"\xDB"=>[0,"\x035"],"\xDC"=>[0,"\x036"],"\xDD"=>[0,"\x037"],"\xDE"=>[0,"\x038"],"\xDF"=>[0,"\x039"],"\xE0"=>[0,"\x03\x3D"],"\xE1"=>[0,"\x03A"],"\xE2"=>[0,"\x03_"],"\xE3"=>[0,"\x03b"],"\xE4"=>[0,"\x03d"],"\xE5"=>[0,"\x03f"],"\xE6"=>[0,"\x03g"],"\xE7"=>[0,"\x03h"],"\xE8"=>[0,"\x03l"],"\xE9"=>[0,"\x03m"],"\xEA"=>[0,"\x03n"],"\xEB"=>[0,"\x03p"],"\xEC"=>[0,"\x03r"],"\xED"=>[0,"\x03u"],"\xEE"=>[100,"\x03"],"\xEF"=>[110,"\x03"],"\xF0"=>[111,"\x03"],"\xF1"=>[115,"\x03"],"\xF2"=>[116,"\x03"],"\xF3"=>[118,"\x03"],"\xF4"=>[119,"\x03"],"\xF5"=>[122,"\x03"],"\xF6"=>[123,"\x03"],"\xF7"=>[125,"\x03"],"\xF8"=>[126,"\x03"],"\xF9"=>[129,"\x03"],"\xFA"=>[143,"\x03"],"\xFB"=>[148,"\x03"],"\xFC"=>[151,"\x03"],"\xFD"=>[153,"\x03"],"\xFE"=>[83,"\x03"],"\xFF"=>[10,"\x03"],],["\x00"=>[77,"\xFE0"],"\x01"=>[18,"\xFE0"],"\x02"=>[77,"\xFE1"],"\x03"=>[18,"\xFE1"],"\x04"=>[77,"\xFE2"],"\x05"=>[18,"\xFE2"],"\x06"=>[77,"\xFEa"],"\x07"=>[18,"\xFEa"],"\x08"=>[77,"\xFEc"],"\x09"=>[18,"\xFEc"],"\x0A"=>[77,"\xFEe"],"\x0B"=>[18,"\xFEe"],"\x0C"=>[77,"\xFEi"],"\x0D"=>[18,"\xFEi"],"\x0E"=>[77,"\xFEo"],"\x0F"=>[18,"\xFEo"],"\x10"=>[77,"\xFEs"],"\x11"=>[18,"\xFEs"],"\x12"=>[77,"\xFEt"],"\x13"=>[18,"\xFEt"],"\x14"=>[0,"\xFE\x20"],"\x15"=>[0,"\xFE\x25"],"\x16"=>[0,"\xFE-"],"\x17"=>[0,"\xFE."],"\x18"=>[0,"\xFE\x2F"],"\x19"=>[0,"\xFE3"],"\x1A"=>[0,"\xFE4"],"\x1B"=>[0,"\xFE5"],"\x1C"=>[0,"\xFE6"],"\x1D"=>[0,"\xFE7"],"\x1E"=>[0,"\xFE8"],"\x1F"=>[0,"\xFE9"],"\x20"=>[0,"\xFE\x3D"],"\x21"=>[0,"\xFEA"],"\x22"=>[0,"\xFE_"],"\x23"=>[0,"\xFEb"],"\x24"=>[0,"\xFEd"],"\x25"=>[0,"\xFEf"],"\x26"=>[0,"\xFEg"],"\x27"=>[0,"\xFEh"],"\x28"=>[0,"\xFEl"],"\x29"=>[0,"\xFEm"],"\x2A"=>[0,"\xFEn"],"\x2B"=>[0,"\xFEp"],"\x2C"=>[0,"\xFEr"],"-"=>[0,"\xFEu"],"."=>[100,"\xFE"],"\x2F"=>[110,"\xFE"],[111,"\xFE"],[115,"\xFE"],[116,"\xFE"],[118,"\xFE"],[119,"\xFE"],[122,"\xFE"],[123,"\xFE"],[125,"\xFE"],[126,"\xFE"],[129,"\xFE"],"\x3A"=>[143,"\xFE"],"\x3B"=>[148,"\xFE"],"\x3C"=>[151,"\xFE"],"\x3D"=>[153,"\xFE"],"\x3E"=>[83,"\xFE"],"\x3F"=>[10,"\xFE"],"\x40"=>[0,"\x020"],"A"=>[0,"\x021"],"B"=>[0,"\x022"],"C"=>[0,"\x02a"],"D"=>[0,"\x02c"],"E"=>[0,"\x02e"],"F"=>[0,"\x02i"],"G"=>[0,"\x02o"],"H"=>[0,"\x02s"],"I"=>[0,"\x02t"],"J"=>[73,"\x02"],"K"=>[88,"\x02"],"L"=>[89,"\x02"],"M"=>[96,"\x02"],"N"=>[97,"\x02"],"O"=>[99,"\x02"],"P"=>[106,"\x02"],"Q"=>[136,"\x02"],"R"=>[139,"\x02"],"S"=>[141,"\x02"],"T"=>[145,"\x02"],"U"=>[147,"\x02"],"V"=>[149,"\x02"],"W"=>[101,"\x02"],"X"=>[112,"\x02"],"Y"=>[117,"\x02"],"Z"=>[120,"\x02"],"\x5B"=>[124,"\x02"],"\x5C"=>[127,"\x02"],"\x5D"=>[144,"\x02"],"\x5E"=>[152,"\x02"],"_"=>[11,"\x02"],"\x60"=>[0,"\x030"],"a"=>[0,"\x031"],"b"=>[0,"\x032"],"c"=>[0,"\x03a"],"d"=>[0,"\x03c"],"e"=>[0,"\x03e"],"f"=>[0,"\x03i"],"g"=>[0,"\x03o"],"h"=>[0,"\x03s"],"i"=>[0,"\x03t"],"j"=>[73,"\x03"],"k"=>[88,"\x03"],"l"=>[89,"\x03"],"m"=>[96,"\x03"],"n"=>[97,"\x03"],"o"=>[99,"\x03"],"p"=>[106,"\x03"],"q"=>[136,"\x03"],"r"=>[139,"\x03"],"s"=>[141,"\x03"],"t"=>[145,"\x03"],"u"=>[147,"\x03"],"v"=>[149,"\x03"],"w"=>[101,"\x03"],"x"=>[112,"\x03"],"y"=>[117,"\x03"],"z"=>[120,"\x03"],"\x7B"=>[124,"\x03"],"\x7C"=>[127,"\x03"],"\x7D"=>[144,"\x03"],"~"=>[152,"\x03"],"\x7F"=>[11,"\x03"],"\x80"=>[0,"\x040"],"\x81"=>[0,"\x041"],"\x82"=>[0,"\x042"],"\x83"=>[0,"\x04a"],"\x84"=>[0,"\x04c"],"\x85"=>[0,"\x04e"],"\x86"=>[0,"\x04i"],"\x87"=>[0,"\x04o"],"\x88"=>[0,"\x04s"],"\x89"=>[0,"\x04t"],"\x8A"=>[73,"\x04"],"\x8B"=>[88,"\x04"],"\x8C"=>[89,"\x04"],"\x8D"=>[96,"\x04"],"\x8E"=>[97,"\x04"],"\x8F"=>[99,"\x04"],"\x90"=>[106,"\x04"],"\x91"=>[136,"\x04"],"\x92"=>[139,"\x04"],"\x93"=>[141,"\x04"],"\x94"=>[145,"\x04"],"\x95"=>[147,"\x04"],"\x96"=>[149,"\x04"],"\x97"=>[101,"\x04"],"\x98"=>[112,"\x04"],"\x99"=>[117,"\x04"],"\x9A"=>[120,"\x04"],"\x9B"=>[124,"\x04"],"\x9C"=>[127,"\x04"],"\x9D"=>[144,"\x04"],"\x9E"=>[152,"\x04"],"\x9F"=>[11,"\x04"],"\xA0"=>[0,"\x050"],"\xA1"=>[0,"\x051"],"\xA2"=>[0,"\x052"],"\xA3"=>[0,"\x05a"],"\xA4"=>[0,"\x05c"],"\xA5"=>[0,"\x05e"],"\xA6"=>[0,"\x05i"],"\xA7"=>[0,"\x05o"],"\xA8"=>[0,"\x05s"],"\xA9"=>[0,"\x05t"],"\xAA"=>[73,"\x05"],"\xAB"=>[88,"\x05"],"\xAC"=>[89,"\x05"],"\xAD"=>[96,"\x05"],"\xAE"=>[97,"\x05"],"\xAF"=>[99,"\x05"],"\xB0"=>[106,"\x05"],"\xB1"=>[136,"\x05"],"\xB2"=>[139,"\x05"],"\xB3"=>[141,"\x05"],"\xB4"=>[145,"\x05"],"\xB5"=>[147,"\x05"],"\xB6"=>[149,"\x05"],"\xB7"=>[101,"\x05"],"\xB8"=>[112,"\x05"],"\xB9"=>[117,"\x05"],"\xBA"=>[120,"\x05"],"\xBB"=>[124,"\x05"],"\xBC"=>[127,"\x05"],"\xBD"=>[144,"\x05"],"\xBE"=>[152,"\x05"],"\xBF"=>[11,"\x05"],"\xC0"=>[0,"\x060"],"\xC1"=>[0,"\x061"],"\xC2"=>[0,"\x062"],"\xC3"=>[0,"\x06a"],"\xC4"=>[0,"\x06c"],"\xC5"=>[0,"\x06e"],"\xC6"=>[0,"\x06i"],"\xC7"=>[0,"\x06o"],"\xC8"=>[0,"\x06s"],"\xC9"=>[0,"\x06t"],"\xCA"=>[73,"\x06"],"\xCB"=>[88,"\x06"],"\xCC"=>[89,"\x06"],"\xCD"=>[96,"\x06"],"\xCE"=>[97,"\x06"],"\xCF"=>[99,"\x06"],"\xD0"=>[106,"\x06"],"\xD1"=>[136,"\x06"],"\xD2"=>[139,"\x06"],"\xD3"=>[141,"\x06"],"\xD4"=>[145,"\x06"],"\xD5"=>[147,"\x06"],"\xD6"=>[149,"\x06"],"\xD7"=>[101,"\x06"],"\xD8"=>[112,"\x06"],"\xD9"=>[117,"\x06"],"\xDA"=>[120,"\x06"],"\xDB"=>[124,"\x06"],"\xDC"=>[127,"\x06"],"\xDD"=>[144,"\x06"],"\xDE"=>[152,"\x06"],"\xDF"=>[11,"\x06"],"\xE0"=>[0,"\x070"],"\xE1"=>[0,"\x071"],"\xE2"=>[0,"\x072"],"\xE3"=>[0,"\x07a"],"\xE4"=>[0,"\x07c"],"\xE5"=>[0,"\x07e"],"\xE6"=>[0,"\x07i"],"\xE7"=>[0,"\x07o"],"\xE8"=>[0,"\x07s"],"\xE9"=>[0,"\x07t"],"\xEA"=>[73,"\x07"],"\xEB"=>[88,"\x07"],"\xEC"=>[89,"\x07"],"\xED"=>[96,"\x07"],"\xEE"=>[97,"\x07"],"\xEF"=>[99,"\x07"],"\xF0"=>[106,"\x07"],"\xF1"=>[136,"\x07"],"\xF2"=>[139,"\x07"],"\xF3"=>[141,"\x07"],"\xF4"=>[145,"\x07"],"\xF5"=>[147,"\x07"],"\xF6"=>[149,"\x07"],"\xF7"=>[101,"\x07"],"\xF8"=>[112,"\x07"],"\xF9"=>[117,"\x07"],"\xFA"=>[120,"\x07"],"\xFB"=>[124,"\x07"],"\xFC"=>[127,"\x07"],"\xFD"=>[144,"\x07"],"\xFE"=>[152,"\x07"],"\xFF"=>[11,"\x07"],],["\x00"=>[94,"\x040"],"\x01"=>[76,"\x040"],"\x02"=>[104,"\x040"],"\x03"=>[16,"\x040"],"\x04"=>[94,"\x041"],"\x05"=>[76,"\x041"],"\x06"=>[104,"\x041"],"\x07"=>[16,"\x041"],"\x08"=>[94,"\x042"],"\x09"=>[76,"\x042"],"\x0A"=>[104,"\x042"],"\x0B"=>[16,"\x042"],"\x0C"=>[94,"\x04a"],"\x0D"=>[76,"\x04a"],"\x0E"=>[104,"\x04a"],"\x0F"=>[16,"\x04a"],"\x10"=>[94,"\x04c"],"\x11"=>[76,"\x04c"],"\x12"=>[104,"\x04c"],"\x13"=>[16,"\x04c"],"\x14"=>[94,"\x04e"],"\x15"=>[76,"\x04e"],"\x16"=>[104,"\x04e"],"\x17"=>[16,"\x04e"],"\x18"=>[94,"\x04i"],"\x19"=>[76,"\x04i"],"\x1A"=>[104,"\x04i"],"\x1B"=>[16,"\x04i"],"\x1C"=>[94,"\x04o"],"\x1D"=>[76,"\x04o"],"\x1E"=>[104,"\x04o"],"\x1F"=>[16,"\x04o"],"\x20"=>[94,"\x04s"],"\x21"=>[76,"\x04s"],"\x22"=>[104,"\x04s"],"\x23"=>[16,"\x04s"],"\x24"=>[94,"\x04t"],"\x25"=>[76,"\x04t"],"\x26"=>[104,"\x04t"],"\x27"=>[16,"\x04t"],"\x28"=>[77,"\x04\x20"],"\x29"=>[18,"\x04\x20"],"\x2A"=>[77,"\x04\x25"],"\x2B"=>[18,"\x04\x25"],"\x2C"=>[77,"\x04-"],"-"=>[18,"\x04-"],"."=>[77,"\x04."],"\x2F"=>[18,"\x04."],[77,"\x04\x2F"],[18,"\x04\x2F"],[77,"\x043"],[18,"\x043"],[77,"\x044"],[18,"\x044"],[77,"\x045"],[18,"\x045"],[77,"\x046"],[18,"\x046"],"\x3A"=>[77,"\x047"],"\x3B"=>[18,"\x047"],"\x3C"=>[77,"\x048"],"\x3D"=>[18,"\x048"],"\x3E"=>[77,"\x049"],"\x3F"=>[18,"\x049"],"\x40"=>[77,"\x04\x3D"],"A"=>[18,"\x04\x3D"],"B"=>[77,"\x04A"],"C"=>[18,"\x04A"],"D"=>[77,"\x04_"],"E"=>[18,"\x04_"],"F"=>[77,"\x04b"],"G"=>[18,"\x04b"],"H"=>[77,"\x04d"],"I"=>[18,"\x04d"],"J"=>[77,"\x04f"],"K"=>[18,"\x04f"],"L"=>[77,"\x04g"],"M"=>[18,"\x04g"],"N"=>[77,"\x04h"],"O"=>[18,"\x04h"],"P"=>[77,"\x04l"],"Q"=>[18,"\x04l"],"R"=>[77,"\x04m"],"S"=>[18,"\x04m"],"T"=>[77,"\x04n"],"U"=>[18,"\x04n"],"V"=>[77,"\x04p"],"W"=>[18,"\x04p"],"X"=>[77,"\x04r"],"Y"=>[18,"\x04r"],"Z"=>[77,"\x04u"],"\x5B"=>[18,"\x04u"],"\x5C"=>[0,"\x04\x3A"],"\x5D"=>[0,"\x04B"],"\x5E"=>[0,"\x04C"],"_"=>[0,"\x04D"],"\x60"=>[0,"\x04E"],"a"=>[0,"\x04F"],"b"=>[0,"\x04G"],"c"=>[0,"\x04H"],"d"=>[0,"\x04I"],"e"=>[0,"\x04J"],"f"=>[0,"\x04K"],"g"=>[0,"\x04L"],"h"=>[0,"\x04M"],"i"=>[0,"\x04N"],"j"=>[0,"\x04O"],"k"=>[0,"\x04P"],"l"=>[0,"\x04Q"],"m"=>[0,"\x04R"],"n"=>[0,"\x04S"],"o"=>[0,"\x04T"],"p"=>[0,"\x04U"],"q"=>[0,"\x04V"],"r"=>[0,"\x04W"],"s"=>[0,"\x04Y"],"t"=>[0,"\x04j"],"u"=>[0,"\x04k"],"v"=>[0,"\x04q"],"w"=>[0,"\x04v"],"x"=>[0,"\x04w"],"y"=>[0,"\x04x"],"z"=>[0,"\x04y"],"\x7B"=>[0,"\x04z"],"\x7C"=>[82,"\x04"],"\x7D"=>[87,"\x04"],"~"=>[130,"\x04"],"\x7F"=>[9,"\x04"],"\x80"=>[94,"\x050"],"\x81"=>[76,"\x050"],"\x82"=>[104,"\x050"],"\x83"=>[16,"\x050"],"\x84"=>[94,"\x051"],"\x85"=>[76,"\x051"],"\x86"=>[104,"\x051"],"\x87"=>[16,"\x051"],"\x88"=>[94,"\x052"],"\x89"=>[76,"\x052"],"\x8A"=>[104,"\x052"],"\x8B"=>[16,"\x052"],"\x8C"=>[94,"\x05a"],"\x8D"=>[76,"\x05a"],"\x8E"=>[104,"\x05a"],"\x8F"=>[16,"\x05a"],"\x90"=>[94,"\x05c"],"\x91"=>[76,"\x05c"],"\x92"=>[104,"\x05c"],"\x93"=>[16,"\x05c"],"\x94"=>[94,"\x05e"],"\x95"=>[76,"\x05e"],"\x96"=>[104,"\x05e"],"\x97"=>[16,"\x05e"],"\x98"=>[94,"\x05i"],"\x99"=>[76,"\x05i"],"\x9A"=>[104,"\x05i"],"\x9B"=>[16,"\x05i"],"\x9C"=>[94,"\x05o"],"\x9D"=>[76,"\x05o"],"\x9E"=>[104,"\x05o"],"\x9F"=>[16,"\x05o"],"\xA0"=>[94,"\x05s"],"\xA1"=>[76,"\x05s"],"\xA2"=>[104,"\x05s"],"\xA3"=>[16,"\x05s"],"\xA4"=>[94,"\x05t"],"\xA5"=>[76,"\x05t"],"\xA6"=>[104,"\x05t"],"\xA7"=>[16,"\x05t"],"\xA8"=>[77,"\x05\x20"],"\xA9"=>[18,"\x05\x20"],"\xAA"=>[77,"\x05\x25"],"\xAB"=>[18,"\x05\x25"],"\xAC"=>[77,"\x05-"],"\xAD"=>[18,"\x05-"],"\xAE"=>[77,"\x05."],"\xAF"=>[18,"\x05."],"\xB0"=>[77,"\x05\x2F"],"\xB1"=>[18,"\x05\x2F"],"\xB2"=>[77,"\x053"],"\xB3"=>[18,"\x053"],"\xB4"=>[77,"\x054"],"\xB5"=>[18,"\x054"],"\xB6"=>[77,"\x055"],"\xB7"=>[18,"\x055"],"\xB8"=>[77,"\x056"],"\xB9"=>[18,"\x056"],"\xBA"=>[77,"\x057"],"\xBB"=>[18,"\x057"],"\xBC"=>[77,"\x058"],"\xBD"=>[18,"\x058"],"\xBE"=>[77,"\x059"],"\xBF"=>[18,"\x059"],"\xC0"=>[77,"\x05\x3D"],"\xC1"=>[18,"\x05\x3D"],"\xC2"=>[77,"\x05A"],"\xC3"=>[18,"\x05A"],"\xC4"=>[77,"\x05_"],"\xC5"=>[18,"\x05_"],"\xC6"=>[77,"\x05b"],"\xC7"=>[18,"\x05b"],"\xC8"=>[77,"\x05d"],"\xC9"=>[18,"\x05d"],"\xCA"=>[77,"\x05f"],"\xCB"=>[18,"\x05f"],"\xCC"=>[77,"\x05g"],"\xCD"=>[18,"\x05g"],"\xCE"=>[77,"\x05h"],"\xCF"=>[18,"\x05h"],"\xD0"=>[77,"\x05l"],"\xD1"=>[18,"\x05l"],"\xD2"=>[77,"\x05m"],"\xD3"=>[18,"\x05m"],"\xD4"=>[77,"\x05n"],"\xD5"=>[18,"\x05n"],"\xD6"=>[77,"\x05p"],"\xD7"=>[18,"\x05p"],"\xD8"=>[77,"\x05r"],"\xD9"=>[18,"\x05r"],"\xDA"=>[77,"\x05u"],"\xDB"=>[18,"\x05u"],"\xDC"=>[0,"\x05\x3A"],"\xDD"=>[0,"\x05B"],"\xDE"=>[0,"\x05C"],"\xDF"=>[0,"\x05D"],"\xE0"=>[0,"\x05E"],"\xE1"=>[0,"\x05F"],"\xE2"=>[0,"\x05G"],"\xE3"=>[0,"\x05H"],"\xE4"=>[0,"\x05I"],"\xE5"=>[0,"\x05J"],"\xE6"=>[0,"\x05K"],"\xE7"=>[0,"\x05L"],"\xE8"=>[0,"\x05M"],"\xE9"=>[0,"\x05N"],"\xEA"=>[0,"\x05O"],"\xEB"=>[0,"\x05P"],"\xEC"=>[0,"\x05Q"],"\xED"=>[0,"\x05R"],"\xEE"=>[0,"\x05S"],"\xEF"=>[0,"\x05T"],"\xF0"=>[0,"\x05U"],"\xF1"=>[0,"\x05V"],"\xF2"=>[0,"\x05W"],"\xF3"=>[0,"\x05Y"],"\xF4"=>[0,"\x05j"],"\xF5"=>[0,"\x05k"],"\xF6"=>[0,"\x05q"],"\xF7"=>[0,"\x05v"],"\xF8"=>[0,"\x05w"],"\xF9"=>[0,"\x05x"],"\xFA"=>[0,"\x05y"],"\xFB"=>[0,"\x05z"],"\xFC"=>[82,"\x05"],"\xFD"=>[87,"\x05"],"\xFE"=>[130,"\x05"],"\xFF"=>[9,"\x05"],],["\x00"=>[77,"\x040"],"\x01"=>[18,"\x040"],"\x02"=>[77,"\x041"],"\x03"=>[18,"\x041"],"\x04"=>[77,"\x042"],"\x05"=>[18,"\x042"],"\x06"=>[77,"\x04a"],"\x07"=>[18,"\x04a"],"\x08"=>[77,"\x04c"],"\x09"=>[18,"\x04c"],"\x0A"=>[77,"\x04e"],"\x0B"=>[18,"\x04e"],"\x0C"=>[77,"\x04i"],"\x0D"=>[18,"\x04i"],"\x0E"=>[77,"\x04o"],"\x0F"=>[18,"\x04o"],"\x10"=>[77,"\x04s"],"\x11"=>[18,"\x04s"],"\x12"=>[77,"\x04t"],"\x13"=>[18,"\x04t"],"\x14"=>[0,"\x04\x20"],"\x15"=>[0,"\x04\x25"],"\x16"=>[0,"\x04-"],"\x17"=>[0,"\x04."],"\x18"=>[0,"\x04\x2F"],"\x19"=>[0,"\x043"],"\x1A"=>[0,"\x044"],"\x1B"=>[0,"\x045"],"\x1C"=>[0,"\x046"],"\x1D"=>[0,"\x047"],"\x1E"=>[0,"\x048"],"\x1F"=>[0,"\x049"],"\x20"=>[0,"\x04\x3D"],"\x21"=>[0,"\x04A"],"\x22"=>[0,"\x04_"],"\x23"=>[0,"\x04b"],"\x24"=>[0,"\x04d"],"\x25"=>[0,"\x04f"],"\x26"=>[0,"\x04g"],"\x27"=>[0,"\x04h"],"\x28"=>[0,"\x04l"],"\x29"=>[0,"\x04m"],"\x2A"=>[0,"\x04n"],"\x2B"=>[0,"\x04p"],"\x2C"=>[0,"\x04r"],"-"=>[0,"\x04u"],"."=>[100,"\x04"],"\x2F"=>[110,"\x04"],[111,"\x04"],[115,"\x04"],[116,"\x04"],[118,"\x04"],[119,"\x04"],[122,"\x04"],[123,"\x04"],[125,"\x04"],[126,"\x04"],[129,"\x04"],"\x3A"=>[143,"\x04"],"\x3B"=>[148,"\x04"],"\x3C"=>[151,"\x04"],"\x3D"=>[153,"\x04"],"\x3E"=>[83,"\x04"],"\x3F"=>[10,"\x04"],"\x40"=>[77,"\x050"],"A"=>[18,"\x050"],"B"=>[77,"\x051"],"C"=>[18,"\x051"],"D"=>[77,"\x052"],"E"=>[18,"\x052"],"F"=>[77,"\x05a"],"G"=>[18,"\x05a"],"H"=>[77,"\x05c"],"I"=>[18,"\x05c"],"J"=>[77,"\x05e"],"K"=>[18,"\x05e"],"L"=>[77,"\x05i"],"M"=>[18,"\x05i"],"N"=>[77,"\x05o"],"O"=>[18,"\x05o"],"P"=>[77,"\x05s"],"Q"=>[18,"\x05s"],"R"=>[77,"\x05t"],"S"=>[18,"\x05t"],"T"=>[0,"\x05\x20"],"U"=>[0,"\x05\x25"],"V"=>[0,"\x05-"],"W"=>[0,"\x05."],"X"=>[0,"\x05\x2F"],"Y"=>[0,"\x053"],"Z"=>[0,"\x054"],"\x5B"=>[0,"\x055"],"\x5C"=>[0,"\x056"],"\x5D"=>[0,"\x057"],"\x5E"=>[0,"\x058"],"_"=>[0,"\x059"],"\x60"=>[0,"\x05\x3D"],"a"=>[0,"\x05A"],"b"=>[0,"\x05_"],"c"=>[0,"\x05b"],"d"=>[0,"\x05d"],"e"=>[0,"\x05f"],"f"=>[0,"\x05g"],"g"=>[0,"\x05h"],"h"=>[0,"\x05l"],"i"=>[0,"\x05m"],"j"=>[0,"\x05n"],"k"=>[0,"\x05p"],"l"=>[0,"\x05r"],"m"=>[0,"\x05u"],"n"=>[100,"\x05"],"o"=>[110,"\x05"],"p"=>[111,"\x05"],"q"=>[115,"\x05"],"r"=>[116,"\x05"],"s"=>[118,"\x05"],"t"=>[119,"\x05"],"u"=>[122,"\x05"],"v"=>[123,"\x05"],"w"=>[125,"\x05"],"x"=>[126,"\x05"],"y"=>[129,"\x05"],"z"=>[143,"\x05"],"\x7B"=>[148,"\x05"],"\x7C"=>[151,"\x05"],"\x7D"=>[153,"\x05"],"~"=>[83,"\x05"],"\x7F"=>[10,"\x05"],"\x80"=>[77,"\x060"],"\x81"=>[18,"\x060"],"\x82"=>[77,"\x061"],"\x83"=>[18,"\x061"],"\x84"=>[77,"\x062"],"\x85"=>[18,"\x062"],"\x86"=>[77,"\x06a"],"\x87"=>[18,"\x06a"],"\x88"=>[77,"\x06c"],"\x89"=>[18,"\x06c"],"\x8A"=>[77,"\x06e"],"\x8B"=>[18,"\x06e"],"\x8C"=>[77,"\x06i"],"\x8D"=>[18,"\x06i"],"\x8E"=>[77,"\x06o"],"\x8F"=>[18,"\x06o"],"\x90"=>[77,"\x06s"],"\x91"=>[18,"\x06s"],"\x92"=>[77,"\x06t"],"\x93"=>[18,"\x06t"],"\x94"=>[0,"\x06\x20"],"\x95"=>[0,"\x06\x25"],"\x96"=>[0,"\x06-"],"\x97"=>[0,"\x06."],"\x98"=>[0,"\x06\x2F"],"\x99"=>[0,"\x063"],"\x9A"=>[0,"\x064"],"\x9B"=>[0,"\x065"],"\x9C"=>[0,"\x066"],"\x9D"=>[0,"\x067"],"\x9E"=>[0,"\x068"],"\x9F"=>[0,"\x069"],"\xA0"=>[0,"\x06\x3D"],"\xA1"=>[0,"\x06A"],"\xA2"=>[0,"\x06_"],"\xA3"=>[0,"\x06b"],"\xA4"=>[0,"\x06d"],"\xA5"=>[0,"\x06f"],"\xA6"=>[0,"\x06g"],"\xA7"=>[0,"\x06h"],"\xA8"=>[0,"\x06l"],"\xA9"=>[0,"\x06m"],"\xAA"=>[0,"\x06n"],"\xAB"=>[0,"\x06p"],"\xAC"=>[0,"\x06r"],"\xAD"=>[0,"\x06u"],"\xAE"=>[100,"\x06"],"\xAF"=>[110,"\x06"],"\xB0"=>[111,"\x06"],"\xB1"=>[115,"\x06"],"\xB2"=>[116,"\x06"],"\xB3"=>[118,"\x06"],"\xB4"=>[119,"\x06"],"\xB5"=>[122,"\x06"],"\xB6"=>[123,"\x06"],"\xB7"=>[125,"\x06"],"\xB8"=>[126,"\x06"],"\xB9"=>[129,"\x06"],"\xBA"=>[143,"\x06"],"\xBB"=>[148,"\x06"],"\xBC"=>[151,"\x06"],"\xBD"=>[153,"\x06"],"\xBE"=>[83,"\x06"],"\xBF"=>[10,"\x06"],"\xC0"=>[77,"\x070"],"\xC1"=>[18,"\x070"],"\xC2"=>[77,"\x071"],"\xC3"=>[18,"\x071"],"\xC4"=>[77,"\x072"],"\xC5"=>[18,"\x072"],"\xC6"=>[77,"\x07a"],"\xC7"=>[18,"\x07a"],"\xC8"=>[77,"\x07c"],"\xC9"=>[18,"\x07c"],"\xCA"=>[77,"\x07e"],"\xCB"=>[18,"\x07e"],"\xCC"=>[77,"\x07i"],"\xCD"=>[18,"\x07i"],"\xCE"=>[77,"\x07o"],"\xCF"=>[18,"\x07o"],"\xD0"=>[77,"\x07s"],"\xD1"=>[18,"\x07s"],"\xD2"=>[77,"\x07t"],"\xD3"=>[18,"\x07t"],"\xD4"=>[0,"\x07\x20"],"\xD5"=>[0,"\x07\x25"],"\xD6"=>[0,"\x07-"],"\xD7"=>[0,"\x07."],"\xD8"=>[0,"\x07\x2F"],"\xD9"=>[0,"\x073"],"\xDA"=>[0,"\x074"],"\xDB"=>[0,"\x075"],"\xDC"=>[0,"\x076"],"\xDD"=>[0,"\x077"],"\xDE"=>[0,"\x078"],"\xDF"=>[0,"\x079"],"\xE0"=>[0,"\x07\x3D"],"\xE1"=>[0,"\x07A"],"\xE2"=>[0,"\x07_"],"\xE3"=>[0,"\x07b"],"\xE4"=>[0,"\x07d"],"\xE5"=>[0,"\x07f"],"\xE6"=>[0,"\x07g"],"\xE7"=>[0,"\x07h"],"\xE8"=>[0,"\x07l"],"\xE9"=>[0,"\x07m"],"\xEA"=>[0,"\x07n"],"\xEB"=>[0,"\x07p"],"\xEC"=>[0,"\x07r"],"\xED"=>[0,"\x07u"],"\xEE"=>[100,"\x07"],"\xEF"=>[110,"\x07"],"\xF0"=>[111,"\x07"],"\xF1"=>[115,"\x07"],"\xF2"=>[116,"\x07"],"\xF3"=>[118,"\x07"],"\xF4"=>[119,"\x07"],"\xF5"=>[122,"\x07"],"\xF6"=>[123,"\x07"],"\xF7"=>[125,"\x07"],"\xF8"=>[126,"\x07"],"\xF9"=>[129,"\x07"],"\xFA"=>[143,"\x07"],"\xFB"=>[148,"\x07"],"\xFC"=>[151,"\x07"],"\xFD"=>[153,"\x07"],"\xFE"=>[83,"\x07"],"\xFF"=>[10,"\x07"],],["\x00"=>[94,"\x060"],"\x01"=>[76,"\x060"],"\x02"=>[104,"\x060"],"\x03"=>[16,"\x060"],"\x04"=>[94,"\x061"],"\x05"=>[76,"\x061"],"\x06"=>[104,"\x061"],"\x07"=>[16,"\x061"],"\x08"=>[94,"\x062"],"\x09"=>[76,"\x062"],"\x0A"=>[104,"\x062"],"\x0B"=>[16,"\x062"],"\x0C"=>[94,"\x06a"],"\x0D"=>[76,"\x06a"],"\x0E"=>[104,"\x06a"],"\x0F"=>[16,"\x06a"],"\x10"=>[94,"\x06c"],"\x11"=>[76,"\x06c"],"\x12"=>[104,"\x06c"],"\x13"=>[16,"\x06c"],"\x14"=>[94,"\x06e"],"\x15"=>[76,"\x06e"],"\x16"=>[104,"\x06e"],"\x17"=>[16,"\x06e"],"\x18"=>[94,"\x06i"],"\x19"=>[76,"\x06i"],"\x1A"=>[104,"\x06i"],"\x1B"=>[16,"\x06i"],"\x1C"=>[94,"\x06o"],"\x1D"=>[76,"\x06o"],"\x1E"=>[104,"\x06o"],"\x1F"=>[16,"\x06o"],"\x20"=>[94,"\x06s"],"\x21"=>[76,"\x06s"],"\x22"=>[104,"\x06s"],"\x23"=>[16,"\x06s"],"\x24"=>[94,"\x06t"],"\x25"=>[76,"\x06t"],"\x26"=>[104,"\x06t"],"\x27"=>[16,"\x06t"],"\x28"=>[77,"\x06\x20"],"\x29"=>[18,"\x06\x20"],"\x2A"=>[77,"\x06\x25"],"\x2B"=>[18,"\x06\x25"],"\x2C"=>[77,"\x06-"],"-"=>[18,"\x06-"],"."=>[77,"\x06."],"\x2F"=>[18,"\x06."],[77,"\x06\x2F"],[18,"\x06\x2F"],[77,"\x063"],[18,"\x063"],[77,"\x064"],[18,"\x064"],[77,"\x065"],[18,"\x065"],[77,"\x066"],[18,"\x066"],"\x3A"=>[77,"\x067"],"\x3B"=>[18,"\x067"],"\x3C"=>[77,"\x068"],"\x3D"=>[18,"\x068"],"\x3E"=>[77,"\x069"],"\x3F"=>[18,"\x069"],"\x40"=>[77,"\x06\x3D"],"A"=>[18,"\x06\x3D"],"B"=>[77,"\x06A"],"C"=>[18,"\x06A"],"D"=>[77,"\x06_"],"E"=>[18,"\x06_"],"F"=>[77,"\x06b"],"G"=>[18,"\x06b"],"H"=>[77,"\x06d"],"I"=>[18,"\x06d"],"J"=>[77,"\x06f"],"K"=>[18,"\x06f"],"L"=>[77,"\x06g"],"M"=>[18,"\x06g"],"N"=>[77,"\x06h"],"O"=>[18,"\x06h"],"P"=>[77,"\x06l"],"Q"=>[18,"\x06l"],"R"=>[77,"\x06m"],"S"=>[18,"\x06m"],"T"=>[77,"\x06n"],"U"=>[18,"\x06n"],"V"=>[77,"\x06p"],"W"=>[18,"\x06p"],"X"=>[77,"\x06r"],"Y"=>[18,"\x06r"],"Z"=>[77,"\x06u"],"\x5B"=>[18,"\x06u"],"\x5C"=>[0,"\x06\x3A"],"\x5D"=>[0,"\x06B"],"\x5E"=>[0,"\x06C"],"_"=>[0,"\x06D"],"\x60"=>[0,"\x06E"],"a"=>[0,"\x06F"],"b"=>[0,"\x06G"],"c"=>[0,"\x06H"],"d"=>[0,"\x06I"],"e"=>[0,"\x06J"],"f"=>[0,"\x06K"],"g"=>[0,"\x06L"],"h"=>[0,"\x06M"],"i"=>[0,"\x06N"],"j"=>[0,"\x06O"],"k"=>[0,"\x06P"],"l"=>[0,"\x06Q"],"m"=>[0,"\x06R"],"n"=>[0,"\x06S"],"o"=>[0,"\x06T"],"p"=>[0,"\x06U"],"q"=>[0,"\x06V"],"r"=>[0,"\x06W"],"s"=>[0,"\x06Y"],"t"=>[0,"\x06j"],"u"=>[0,"\x06k"],"v"=>[0,"\x06q"],"w"=>[0,"\x06v"],"x"=>[0,"\x06w"],"y"=>[0,"\x06x"],"z"=>[0,"\x06y"],"\x7B"=>[0,"\x06z"],"\x7C"=>[82,"\x06"],"\x7D"=>[87,"\x06"],"~"=>[130,"\x06"],"\x7F"=>[9,"\x06"],"\x80"=>[94,"\x070"],"\x81"=>[76,"\x070"],"\x82"=>[104,"\x070"],"\x83"=>[16,"\x070"],"\x84"=>[94,"\x071"],"\x85"=>[76,"\x071"],"\x86"=>[104,"\x071"],"\x87"=>[16,"\x071"],"\x88"=>[94,"\x072"],"\x89"=>[76,"\x072"],"\x8A"=>[104,"\x072"],"\x8B"=>[16,"\x072"],"\x8C"=>[94,"\x07a"],"\x8D"=>[76,"\x07a"],"\x8E"=>[104,"\x07a"],"\x8F"=>[16,"\x07a"],"\x90"=>[94,"\x07c"],"\x91"=>[76,"\x07c"],"\x92"=>[104,"\x07c"],"\x93"=>[16,"\x07c"],"\x94"=>[94,"\x07e"],"\x95"=>[76,"\x07e"],"\x96"=>[104,"\x07e"],"\x97"=>[16,"\x07e"],"\x98"=>[94,"\x07i"],"\x99"=>[76,"\x07i"],"\x9A"=>[104,"\x07i"],"\x9B"=>[16,"\x07i"],"\x9C"=>[94,"\x07o"],"\x9D"=>[76,"\x07o"],"\x9E"=>[104,"\x07o"],"\x9F"=>[16,"\x07o"],"\xA0"=>[94,"\x07s"],"\xA1"=>[76,"\x07s"],"\xA2"=>[104,"\x07s"],"\xA3"=>[16,"\x07s"],"\xA4"=>[94,"\x07t"],"\xA5"=>[76,"\x07t"],"\xA6"=>[104,"\x07t"],"\xA7"=>[16,"\x07t"],"\xA8"=>[77,"\x07\x20"],"\xA9"=>[18,"\x07\x20"],"\xAA"=>[77,"\x07\x25"],"\xAB"=>[18,"\x07\x25"],"\xAC"=>[77,"\x07-"],"\xAD"=>[18,"\x07-"],"\xAE"=>[77,"\x07."],"\xAF"=>[18,"\x07."],"\xB0"=>[77,"\x07\x2F"],"\xB1"=>[18,"\x07\x2F"],"\xB2"=>[77,"\x073"],"\xB3"=>[18,"\x073"],"\xB4"=>[77,"\x074"],"\xB5"=>[18,"\x074"],"\xB6"=>[77,"\x075"],"\xB7"=>[18,"\x075"],"\xB8"=>[77,"\x076"],"\xB9"=>[18,"\x076"],"\xBA"=>[77,"\x077"],"\xBB"=>[18,"\x077"],"\xBC"=>[77,"\x078"],"\xBD"=>[18,"\x078"],"\xBE"=>[77,"\x079"],"\xBF"=>[18,"\x079"],"\xC0"=>[77,"\x07\x3D"],"\xC1"=>[18,"\x07\x3D"],"\xC2"=>[77,"\x07A"],"\xC3"=>[18,"\x07A"],"\xC4"=>[77,"\x07_"],"\xC5"=>[18,"\x07_"],"\xC6"=>[77,"\x07b"],"\xC7"=>[18,"\x07b"],"\xC8"=>[77,"\x07d"],"\xC9"=>[18,"\x07d"],"\xCA"=>[77,"\x07f"],"\xCB"=>[18,"\x07f"],"\xCC"=>[77,"\x07g"],"\xCD"=>[18,"\x07g"],"\xCE"=>[77,"\x07h"],"\xCF"=>[18,"\x07h"],"\xD0"=>[77,"\x07l"],"\xD1"=>[18,"\x07l"],"\xD2"=>[77,"\x07m"],"\xD3"=>[18,"\x07m"],"\xD4"=>[77,"\x07n"],"\xD5"=>[18,"\x07n"],"\xD6"=>[77,"\x07p"],"\xD7"=>[18,"\x07p"],"\xD8"=>[77,"\x07r"],"\xD9"=>[18,"\x07r"],"\xDA"=>[77,"\x07u"],"\xDB"=>[18,"\x07u"],"\xDC"=>[0,"\x07\x3A"],"\xDD"=>[0,"\x07B"],"\xDE"=>[0,"\x07C"],"\xDF"=>[0,"\x07D"],"\xE0"=>[0,"\x07E"],"\xE1"=>[0,"\x07F"],"\xE2"=>[0,"\x07G"],"\xE3"=>[0,"\x07H"],"\xE4"=>[0,"\x07I"],"\xE5"=>[0,"\x07J"],"\xE6"=>[0,"\x07K"],"\xE7"=>[0,"\x07L"],"\xE8"=>[0,"\x07M"],"\xE9"=>[0,"\x07N"],"\xEA"=>[0,"\x07O"],"\xEB"=>[0,"\x07P"],"\xEC"=>[0,"\x07Q"],"\xED"=>[0,"\x07R"],"\xEE"=>[0,"\x07S"],"\xEF"=>[0,"\x07T"],"\xF0"=>[0,"\x07U"],"\xF1"=>[0,"\x07V"],"\xF2"=>[0,"\x07W"],"\xF3"=>[0,"\x07Y"],"\xF4"=>[0,"\x07j"],"\xF5"=>[0,"\x07k"],"\xF6"=>[0,"\x07q"],"\xF7"=>[0,"\x07v"],"\xF8"=>[0,"\x07w"],"\xF9"=>[0,"\x07x"],"\xFA"=>[0,"\x07y"],"\xFB"=>[0,"\x07z"],"\xFC"=>[82,"\x07"],"\xFD"=>[87,"\x07"],"\xFE"=>[130,"\x07"],"\xFF"=>[9,"\x07"],],["\x00"=>[94,"\x080"],"\x01"=>[76,"\x080"],"\x02"=>[104,"\x080"],"\x03"=>[16,"\x080"],"\x04"=>[94,"\x081"],"\x05"=>[76,"\x081"],"\x06"=>[104,"\x081"],"\x07"=>[16,"\x081"],"\x08"=>[94,"\x082"],"\x09"=>[76,"\x082"],"\x0A"=>[104,"\x082"],"\x0B"=>[16,"\x082"],"\x0C"=>[94,"\x08a"],"\x0D"=>[76,"\x08a"],"\x0E"=>[104,"\x08a"],"\x0F"=>[16,"\x08a"],"\x10"=>[94,"\x08c"],"\x11"=>[76,"\x08c"],"\x12"=>[104,"\x08c"],"\x13"=>[16,"\x08c"],"\x14"=>[94,"\x08e"],"\x15"=>[76,"\x08e"],"\x16"=>[104,"\x08e"],"\x17"=>[16,"\x08e"],"\x18"=>[94,"\x08i"],"\x19"=>[76,"\x08i"],"\x1A"=>[104,"\x08i"],"\x1B"=>[16,"\x08i"],"\x1C"=>[94,"\x08o"],"\x1D"=>[76,"\x08o"],"\x1E"=>[104,"\x08o"],"\x1F"=>[16,"\x08o"],"\x20"=>[94,"\x08s"],"\x21"=>[76,"\x08s"],"\x22"=>[104,"\x08s"],"\x23"=>[16,"\x08s"],"\x24"=>[94,"\x08t"],"\x25"=>[76,"\x08t"],"\x26"=>[104,"\x08t"],"\x27"=>[16,"\x08t"],"\x28"=>[77,"\x08\x20"],"\x29"=>[18,"\x08\x20"],"\x2A"=>[77,"\x08\x25"],"\x2B"=>[18,"\x08\x25"],"\x2C"=>[77,"\x08-"],"-"=>[18,"\x08-"],"."=>[77,"\x08."],"\x2F"=>[18,"\x08."],[77,"\x08\x2F"],[18,"\x08\x2F"],[77,"\x083"],[18,"\x083"],[77,"\x084"],[18,"\x084"],[77,"\x085"],[18,"\x085"],[77,"\x086"],[18,"\x086"],"\x3A"=>[77,"\x087"],"\x3B"=>[18,"\x087"],"\x3C"=>[77,"\x088"],"\x3D"=>[18,"\x088"],"\x3E"=>[77,"\x089"],"\x3F"=>[18,"\x089"],"\x40"=>[77,"\x08\x3D"],"A"=>[18,"\x08\x3D"],"B"=>[77,"\x08A"],"C"=>[18,"\x08A"],"D"=>[77,"\x08_"],"E"=>[18,"\x08_"],"F"=>[77,"\x08b"],"G"=>[18,"\x08b"],"H"=>[77,"\x08d"],"I"=>[18,"\x08d"],"J"=>[77,"\x08f"],"K"=>[18,"\x08f"],"L"=>[77,"\x08g"],"M"=>[18,"\x08g"],"N"=>[77,"\x08h"],"O"=>[18,"\x08h"],"P"=>[77,"\x08l"],"Q"=>[18,"\x08l"],"R"=>[77,"\x08m"],"S"=>[18,"\x08m"],"T"=>[77,"\x08n"],"U"=>[18,"\x08n"],"V"=>[77,"\x08p"],"W"=>[18,"\x08p"],"X"=>[77,"\x08r"],"Y"=>[18,"\x08r"],"Z"=>[77,"\x08u"],"\x5B"=>[18,"\x08u"],"\x5C"=>[0,"\x08\x3A"],"\x5D"=>[0,"\x08B"],"\x5E"=>[0,"\x08C"],"_"=>[0,"\x08D"],"\x60"=>[0,"\x08E"],"a"=>[0,"\x08F"],"b"=>[0,"\x08G"],"c"=>[0,"\x08H"],"d"=>[0,"\x08I"],"e"=>[0,"\x08J"],"f"=>[0,"\x08K"],"g"=>[0,"\x08L"],"h"=>[0,"\x08M"],"i"=>[0,"\x08N"],"j"=>[0,"\x08O"],"k"=>[0,"\x08P"],"l"=>[0,"\x08Q"],"m"=>[0,"\x08R"],"n"=>[0,"\x08S"],"o"=>[0,"\x08T"],"p"=>[0,"\x08U"],"q"=>[0,"\x08V"],"r"=>[0,"\x08W"],"s"=>[0,"\x08Y"],"t"=>[0,"\x08j"],"u"=>[0,"\x08k"],"v"=>[0,"\x08q"],"w"=>[0,"\x08v"],"x"=>[0,"\x08w"],"y"=>[0,"\x08x"],"z"=>[0,"\x08y"],"\x7B"=>[0,"\x08z"],"\x7C"=>[82,"\x08"],"\x7D"=>[87,"\x08"],"~"=>[130,"\x08"],"\x7F"=>[9,"\x08"],"\x80"=>[94,"\x0B0"],"\x81"=>[76,"\x0B0"],"\x82"=>[104,"\x0B0"],"\x83"=>[16,"\x0B0"],"\x84"=>[94,"\x0B1"],"\x85"=>[76,"\x0B1"],"\x86"=>[104,"\x0B1"],"\x87"=>[16,"\x0B1"],"\x88"=>[94,"\x0B2"],"\x89"=>[76,"\x0B2"],"\x8A"=>[104,"\x0B2"],"\x8B"=>[16,"\x0B2"],"\x8C"=>[94,"\x0Ba"],"\x8D"=>[76,"\x0Ba"],"\x8E"=>[104,"\x0Ba"],"\x8F"=>[16,"\x0Ba"],"\x90"=>[94,"\x0Bc"],"\x91"=>[76,"\x0Bc"],"\x92"=>[104,"\x0Bc"],"\x93"=>[16,"\x0Bc"],"\x94"=>[94,"\x0Be"],"\x95"=>[76,"\x0Be"],"\x96"=>[104,"\x0Be"],"\x97"=>[16,"\x0Be"],"\x98"=>[94,"\x0Bi"],"\x99"=>[76,"\x0Bi"],"\x9A"=>[104,"\x0Bi"],"\x9B"=>[16,"\x0Bi"],"\x9C"=>[94,"\x0Bo"],"\x9D"=>[76,"\x0Bo"],"\x9E"=>[104,"\x0Bo"],"\x9F"=>[16,"\x0Bo"],"\xA0"=>[94,"\x0Bs"],"\xA1"=>[76,"\x0Bs"],"\xA2"=>[104,"\x0Bs"],"\xA3"=>[16,"\x0Bs"],"\xA4"=>[94,"\x0Bt"],"\xA5"=>[76,"\x0Bt"],"\xA6"=>[104,"\x0Bt"],"\xA7"=>[16,"\x0Bt"],"\xA8"=>[77,"\x0B\x20"],"\xA9"=>[18,"\x0B\x20"],"\xAA"=>[77,"\x0B\x25"],"\xAB"=>[18,"\x0B\x25"],"\xAC"=>[77,"\x0B-"],"\xAD"=>[18,"\x0B-"],"\xAE"=>[77,"\x0B."],"\xAF"=>[18,"\x0B."],"\xB0"=>[77,"\x0B\x2F"],"\xB1"=>[18,"\x0B\x2F"],"\xB2"=>[77,"\x0B3"],"\xB3"=>[18,"\x0B3"],"\xB4"=>[77,"\x0B4"],"\xB5"=>[18,"\x0B4"],"\xB6"=>[77,"\x0B5"],"\xB7"=>[18,"\x0B5"],"\xB8"=>[77,"\x0B6"],"\xB9"=>[18,"\x0B6"],"\xBA"=>[77,"\x0B7"],"\xBB"=>[18,"\x0B7"],"\xBC"=>[77,"\x0B8"],"\xBD"=>[18,"\x0B8"],"\xBE"=>[77,"\x0B9"],"\xBF"=>[18,"\x0B9"],"\xC0"=>[77,"\x0B\x3D"],"\xC1"=>[18,"\x0B\x3D"],"\xC2"=>[77,"\x0BA"],"\xC3"=>[18,"\x0BA"],"\xC4"=>[77,"\x0B_"],"\xC5"=>[18,"\x0B_"],"\xC6"=>[77,"\x0Bb"],"\xC7"=>[18,"\x0Bb"],"\xC8"=>[77,"\x0Bd"],"\xC9"=>[18,"\x0Bd"],"\xCA"=>[77,"\x0Bf"],"\xCB"=>[18,"\x0Bf"],"\xCC"=>[77,"\x0Bg"],"\xCD"=>[18,"\x0Bg"],"\xCE"=>[77,"\x0Bh"],"\xCF"=>[18,"\x0Bh"],"\xD0"=>[77,"\x0Bl"],"\xD1"=>[18,"\x0Bl"],"\xD2"=>[77,"\x0Bm"],"\xD3"=>[18,"\x0Bm"],"\xD4"=>[77,"\x0Bn"],"\xD5"=>[18,"\x0Bn"],"\xD6"=>[77,"\x0Bp"],"\xD7"=>[18,"\x0Bp"],"\xD8"=>[77,"\x0Br"],"\xD9"=>[18,"\x0Br"],"\xDA"=>[77,"\x0Bu"],"\xDB"=>[18,"\x0Bu"],"\xDC"=>[0,"\x0B\x3A"],"\xDD"=>[0,"\x0BB"],"\xDE"=>[0,"\x0BC"],"\xDF"=>[0,"\x0BD"],"\xE0"=>[0,"\x0BE"],"\xE1"=>[0,"\x0BF"],"\xE2"=>[0,"\x0BG"],"\xE3"=>[0,"\x0BH"],"\xE4"=>[0,"\x0BI"],"\xE5"=>[0,"\x0BJ"],"\xE6"=>[0,"\x0BK"],"\xE7"=>[0,"\x0BL"],"\xE8"=>[0,"\x0BM"],"\xE9"=>[0,"\x0BN"],"\xEA"=>[0,"\x0BO"],"\xEB"=>[0,"\x0BP"],"\xEC"=>[0,"\x0BQ"],"\xED"=>[0,"\x0BR"],"\xEE"=>[0,"\x0BS"],"\xEF"=>[0,"\x0BT"],"\xF0"=>[0,"\x0BU"],"\xF1"=>[0,"\x0BV"],"\xF2"=>[0,"\x0BW"],"\xF3"=>[0,"\x0BY"],"\xF4"=>[0,"\x0Bj"],"\xF5"=>[0,"\x0Bk"],"\xF6"=>[0,"\x0Bq"],"\xF7"=>[0,"\x0Bv"],"\xF8"=>[0,"\x0Bw"],"\xF9"=>[0,"\x0Bx"],"\xFA"=>[0,"\x0By"],"\xFB"=>[0,"\x0Bz"],"\xFC"=>[82,"\x0B"],"\xFD"=>[87,"\x0B"],"\xFE"=>[130,"\x0B"],"\xFF"=>[9,"\x0B"],],["\x00"=>[77,"\x080"],"\x01"=>[18,"\x080"],"\x02"=>[77,"\x081"],"\x03"=>[18,"\x081"],"\x04"=>[77,"\x082"],"\x05"=>[18,"\x082"],"\x06"=>[77,"\x08a"],"\x07"=>[18,"\x08a"],"\x08"=>[77,"\x08c"],"\x09"=>[18,"\x08c"],"\x0A"=>[77,"\x08e"],"\x0B"=>[18,"\x08e"],"\x0C"=>[77,"\x08i"],"\x0D"=>[18,"\x08i"],"\x0E"=>[77,"\x08o"],"\x0F"=>[18,"\x08o"],"\x10"=>[77,"\x08s"],"\x11"=>[18,"\x08s"],"\x12"=>[77,"\x08t"],"\x13"=>[18,"\x08t"],"\x14"=>[0,"\x08\x20"],"\x15"=>[0,"\x08\x25"],"\x16"=>[0,"\x08-"],"\x17"=>[0,"\x08."],"\x18"=>[0,"\x08\x2F"],"\x19"=>[0,"\x083"],"\x1A"=>[0,"\x084"],"\x1B"=>[0,"\x085"],"\x1C"=>[0,"\x086"],"\x1D"=>[0,"\x087"],"\x1E"=>[0,"\x088"],"\x1F"=>[0,"\x089"],"\x20"=>[0,"\x08\x3D"],"\x21"=>[0,"\x08A"],"\x22"=>[0,"\x08_"],"\x23"=>[0,"\x08b"],"\x24"=>[0,"\x08d"],"\x25"=>[0,"\x08f"],"\x26"=>[0,"\x08g"],"\x27"=>[0,"\x08h"],"\x28"=>[0,"\x08l"],"\x29"=>[0,"\x08m"],"\x2A"=>[0,"\x08n"],"\x2B"=>[0,"\x08p"],"\x2C"=>[0,"\x08r"],"-"=>[0,"\x08u"],"."=>[100,"\x08"],"\x2F"=>[110,"\x08"],[111,"\x08"],[115,"\x08"],[116,"\x08"],[118,"\x08"],[119,"\x08"],[122,"\x08"],[123,"\x08"],[125,"\x08"],[126,"\x08"],[129,"\x08"],"\x3A"=>[143,"\x08"],"\x3B"=>[148,"\x08"],"\x3C"=>[151,"\x08"],"\x3D"=>[153,"\x08"],"\x3E"=>[83,"\x08"],"\x3F"=>[10,"\x08"],"\x40"=>[77,"\x0B0"],"A"=>[18,"\x0B0"],"B"=>[77,"\x0B1"],"C"=>[18,"\x0B1"],"D"=>[77,"\x0B2"],"E"=>[18,"\x0B2"],"F"=>[77,"\x0Ba"],"G"=>[18,"\x0Ba"],"H"=>[77,"\x0Bc"],"I"=>[18,"\x0Bc"],"J"=>[77,"\x0Be"],"K"=>[18,"\x0Be"],"L"=>[77,"\x0Bi"],"M"=>[18,"\x0Bi"],"N"=>[77,"\x0Bo"],"O"=>[18,"\x0Bo"],"P"=>[77,"\x0Bs"],"Q"=>[18,"\x0Bs"],"R"=>[77,"\x0Bt"],"S"=>[18,"\x0Bt"],"T"=>[0,"\x0B\x20"],"U"=>[0,"\x0B\x25"],"V"=>[0,"\x0B-"],"W"=>[0,"\x0B."],"X"=>[0,"\x0B\x2F"],"Y"=>[0,"\x0B3"],"Z"=>[0,"\x0B4"],"\x5B"=>[0,"\x0B5"],"\x5C"=>[0,"\x0B6"],"\x5D"=>[0,"\x0B7"],"\x5E"=>[0,"\x0B8"],"_"=>[0,"\x0B9"],"\x60"=>[0,"\x0B\x3D"],"a"=>[0,"\x0BA"],"b"=>[0,"\x0B_"],"c"=>[0,"\x0Bb"],"d"=>[0,"\x0Bd"],"e"=>[0,"\x0Bf"],"f"=>[0,"\x0Bg"],"g"=>[0,"\x0Bh"],"h"=>[0,"\x0Bl"],"i"=>[0,"\x0Bm"],"j"=>[0,"\x0Bn"],"k"=>[0,"\x0Bp"],"l"=>[0,"\x0Br"],"m"=>[0,"\x0Bu"],"n"=>[100,"\x0B"],"o"=>[110,"\x0B"],"p"=>[111,"\x0B"],"q"=>[115,"\x0B"],"r"=>[116,"\x0B"],"s"=>[118,"\x0B"],"t"=>[119,"\x0B"],"u"=>[122,"\x0B"],"v"=>[123,"\x0B"],"w"=>[125,"\x0B"],"x"=>[126,"\x0B"],"y"=>[129,"\x0B"],"z"=>[143,"\x0B"],"\x7B"=>[148,"\x0B"],"\x7C"=>[151,"\x0B"],"\x7D"=>[153,"\x0B"],"~"=>[83,"\x0B"],"\x7F"=>[10,"\x0B"],"\x80"=>[77,"\x0C0"],"\x81"=>[18,"\x0C0"],"\x82"=>[77,"\x0C1"],"\x83"=>[18,"\x0C1"],"\x84"=>[77,"\x0C2"],"\x85"=>[18,"\x0C2"],"\x86"=>[77,"\x0Ca"],"\x87"=>[18,"\x0Ca"],"\x88"=>[77,"\x0Cc"],"\x89"=>[18,"\x0Cc"],"\x8A"=>[77,"\x0Ce"],"\x8B"=>[18,"\x0Ce"],"\x8C"=>[77,"\x0Ci"],"\x8D"=>[18,"\x0Ci"],"\x8E"=>[77,"\x0Co"],"\x8F"=>[18,"\x0Co"],"\x90"=>[77,"\x0Cs"],"\x91"=>[18,"\x0Cs"],"\x92"=>[77,"\x0Ct"],"\x93"=>[18,"\x0Ct"],"\x94"=>[0,"\x0C\x20"],"\x95"=>[0,"\x0C\x25"],"\x96"=>[0,"\x0C-"],"\x97"=>[0,"\x0C."],"\x98"=>[0,"\x0C\x2F"],"\x99"=>[0,"\x0C3"],"\x9A"=>[0,"\x0C4"],"\x9B"=>[0,"\x0C5"],"\x9C"=>[0,"\x0C6"],"\x9D"=>[0,"\x0C7"],"\x9E"=>[0,"\x0C8"],"\x9F"=>[0,"\x0C9"],"\xA0"=>[0,"\x0C\x3D"],"\xA1"=>[0,"\x0CA"],"\xA2"=>[0,"\x0C_"],"\xA3"=>[0,"\x0Cb"],"\xA4"=>[0,"\x0Cd"],"\xA5"=>[0,"\x0Cf"],"\xA6"=>[0,"\x0Cg"],"\xA7"=>[0,"\x0Ch"],"\xA8"=>[0,"\x0Cl"],"\xA9"=>[0,"\x0Cm"],"\xAA"=>[0,"\x0Cn"],"\xAB"=>[0,"\x0Cp"],"\xAC"=>[0,"\x0Cr"],"\xAD"=>[0,"\x0Cu"],"\xAE"=>[100,"\x0C"],"\xAF"=>[110,"\x0C"],"\xB0"=>[111,"\x0C"],"\xB1"=>[115,"\x0C"],"\xB2"=>[116,"\x0C"],"\xB3"=>[118,"\x0C"],"\xB4"=>[119,"\x0C"],"\xB5"=>[122,"\x0C"],"\xB6"=>[123,"\x0C"],"\xB7"=>[125,"\x0C"],"\xB8"=>[126,"\x0C"],"\xB9"=>[129,"\x0C"],"\xBA"=>[143,"\x0C"],"\xBB"=>[148,"\x0C"],"\xBC"=>[151,"\x0C"],"\xBD"=>[153,"\x0C"],"\xBE"=>[83,"\x0C"],"\xBF"=>[10,"\x0C"],"\xC0"=>[77,"\x0E0"],"\xC1"=>[18,"\x0E0"],"\xC2"=>[77,"\x0E1"],"\xC3"=>[18,"\x0E1"],"\xC4"=>[77,"\x0E2"],"\xC5"=>[18,"\x0E2"],"\xC6"=>[77,"\x0Ea"],"\xC7"=>[18,"\x0Ea"],"\xC8"=>[77,"\x0Ec"],"\xC9"=>[18,"\x0Ec"],"\xCA"=>[77,"\x0Ee"],"\xCB"=>[18,"\x0Ee"],"\xCC"=>[77,"\x0Ei"],"\xCD"=>[18,"\x0Ei"],"\xCE"=>[77,"\x0Eo"],"\xCF"=>[18,"\x0Eo"],"\xD0"=>[77,"\x0Es"],"\xD1"=>[18,"\x0Es"],"\xD2"=>[77,"\x0Et"],"\xD3"=>[18,"\x0Et"],"\xD4"=>[0,"\x0E\x20"],"\xD5"=>[0,"\x0E\x25"],"\xD6"=>[0,"\x0E-"],"\xD7"=>[0,"\x0E."],"\xD8"=>[0,"\x0E\x2F"],"\xD9"=>[0,"\x0E3"],"\xDA"=>[0,"\x0E4"],"\xDB"=>[0,"\x0E5"],"\xDC"=>[0,"\x0E6"],"\xDD"=>[0,"\x0E7"],"\xDE"=>[0,"\x0E8"],"\xDF"=>[0,"\x0E9"],"\xE0"=>[0,"\x0E\x3D"],"\xE1"=>[0,"\x0EA"],"\xE2"=>[0,"\x0E_"],"\xE3"=>[0,"\x0Eb"],"\xE4"=>[0,"\x0Ed"],"\xE5"=>[0,"\x0Ef"],"\xE6"=>[0,"\x0Eg"],"\xE7"=>[0,"\x0Eh"],"\xE8"=>[0,"\x0El"],"\xE9"=>[0,"\x0Em"],"\xEA"=>[0,"\x0En"],"\xEB"=>[0,"\x0Ep"],"\xEC"=>[0,"\x0Er"],"\xED"=>[0,"\x0Eu"],"\xEE"=>[100,"\x0E"],"\xEF"=>[110,"\x0E"],"\xF0"=>[111,"\x0E"],"\xF1"=>[115,"\x0E"],"\xF2"=>[116,"\x0E"],"\xF3"=>[118,"\x0E"],"\xF4"=>[119,"\x0E"],"\xF5"=>[122,"\x0E"],"\xF6"=>[123,"\x0E"],"\xF7"=>[125,"\x0E"],"\xF8"=>[126,"\x0E"],"\xF9"=>[129,"\x0E"],"\xFA"=>[143,"\x0E"],"\xFB"=>[148,"\x0E"],"\xFC"=>[151,"\x0E"],"\xFD"=>[153,"\x0E"],"\xFE"=>[83,"\x0E"],"\xFF"=>[10,"\x0E"],],["\x00"=>[0,"\x080"],"\x01"=>[0,"\x081"],"\x02"=>[0,"\x082"],"\x03"=>[0,"\x08a"],"\x04"=>[0,"\x08c"],"\x05"=>[0,"\x08e"],"\x06"=>[0,"\x08i"],"\x07"=>[0,"\x08o"],"\x08"=>[0,"\x08s"],"\x09"=>[0,"\x08t"],"\x0A"=>[73,"\x08"],"\x0B"=>[88,"\x08"],"\x0C"=>[89,"\x08"],"\x0D"=>[96,"\x08"],"\x0E"=>[97,"\x08"],"\x0F"=>[99,"\x08"],"\x10"=>[106,"\x08"],"\x11"=>[136,"\x08"],"\x12"=>[139,"\x08"],"\x13"=>[141,"\x08"],"\x14"=>[145,"\x08"],"\x15"=>[147,"\x08"],"\x16"=>[149,"\x08"],"\x17"=>[101,"\x08"],"\x18"=>[112,"\x08"],"\x19"=>[117,"\x08"],"\x1A"=>[120,"\x08"],"\x1B"=>[124,"\x08"],"\x1C"=>[127,"\x08"],"\x1D"=>[144,"\x08"],"\x1E"=>[152,"\x08"],"\x1F"=>[11,"\x08"],"\x20"=>[0,"\x0B0"],"\x21"=>[0,"\x0B1"],"\x22"=>[0,"\x0B2"],"\x23"=>[0,"\x0Ba"],"\x24"=>[0,"\x0Bc"],"\x25"=>[0,"\x0Be"],"\x26"=>[0,"\x0Bi"],"\x27"=>[0,"\x0Bo"],"\x28"=>[0,"\x0Bs"],"\x29"=>[0,"\x0Bt"],"\x2A"=>[73,"\x0B"],"\x2B"=>[88,"\x0B"],"\x2C"=>[89,"\x0B"],"-"=>[96,"\x0B"],"."=>[97,"\x0B"],"\x2F"=>[99,"\x0B"],[106,"\x0B"],[136,"\x0B"],[139,"\x0B"],[141,"\x0B"],[145,"\x0B"],[147,"\x0B"],[149,"\x0B"],[101,"\x0B"],[112,"\x0B"],[117,"\x0B"],"\x3A"=>[120,"\x0B"],"\x3B"=>[124,"\x0B"],"\x3C"=>[127,"\x0B"],"\x3D"=>[144,"\x0B"],"\x3E"=>[152,"\x0B"],"\x3F"=>[11,"\x0B"],"\x40"=>[0,"\x0C0"],"A"=>[0,"\x0C1"],"B"=>[0,"\x0C2"],"C"=>[0,"\x0Ca"],"D"=>[0,"\x0Cc"],"E"=>[0,"\x0Ce"],"F"=>[0,"\x0Ci"],"G"=>[0,"\x0Co"],"H"=>[0,"\x0Cs"],"I"=>[0,"\x0Ct"],"J"=>[73,"\x0C"],"K"=>[88,"\x0C"],"L"=>[89,"\x0C"],"M"=>[96,"\x0C"],"N"=>[97,"\x0C"],"O"=>[99,"\x0C"],"P"=>[106,"\x0C"],"Q"=>[136,"\x0C"],"R"=>[139,"\x0C"],"S"=>[141,"\x0C"],"T"=>[145,"\x0C"],"U"=>[147,"\x0C"],"V"=>[149,"\x0C"],"W"=>[101,"\x0C"],"X"=>[112,"\x0C"],"Y"=>[117,"\x0C"],"Z"=>[120,"\x0C"],"\x5B"=>[124,"\x0C"],"\x5C"=>[127,"\x0C"],"\x5D"=>[144,"\x0C"],"\x5E"=>[152,"\x0C"],"_"=>[11,"\x0C"],"\x60"=>[0,"\x0E0"],"a"=>[0,"\x0E1"],"b"=>[0,"\x0E2"],"c"=>[0,"\x0Ea"],"d"=>[0,"\x0Ec"],"e"=>[0,"\x0Ee"],"f"=>[0,"\x0Ei"],"g"=>[0,"\x0Eo"],"h"=>[0,"\x0Es"],"i"=>[0,"\x0Et"],"j"=>[73,"\x0E"],"k"=>[88,"\x0E"],"l"=>[89,"\x0E"],"m"=>[96,"\x0E"],"n"=>[97,"\x0E"],"o"=>[99,"\x0E"],"p"=>[106,"\x0E"],"q"=>[136,"\x0E"],"r"=>[139,"\x0E"],"s"=>[141,"\x0E"],"t"=>[145,"\x0E"],"u"=>[147,"\x0E"],"v"=>[149,"\x0E"],"w"=>[101,"\x0E"],"x"=>[112,"\x0E"],"y"=>[117,"\x0E"],"z"=>[120,"\x0E"],"\x7B"=>[124,"\x0E"],"\x7C"=>[127,"\x0E"],"\x7D"=>[144,"\x0E"],"~"=>[152,"\x0E"],"\x7F"=>[11,"\x0E"],"\x80"=>[0,"\x0F0"],"\x81"=>[0,"\x0F1"],"\x82"=>[0,"\x0F2"],"\x83"=>[0,"\x0Fa"],"\x84"=>[0,"\x0Fc"],"\x85"=>[0,"\x0Fe"],"\x86"=>[0,"\x0Fi"],"\x87"=>[0,"\x0Fo"],"\x88"=>[0,"\x0Fs"],"\x89"=>[0,"\x0Ft"],"\x8A"=>[73,"\x0F"],"\x8B"=>[88,"\x0F"],"\x8C"=>[89,"\x0F"],"\x8D"=>[96,"\x0F"],"\x8E"=>[97,"\x0F"],"\x8F"=>[99,"\x0F"],"\x90"=>[106,"\x0F"],"\x91"=>[136,"\x0F"],"\x92"=>[139,"\x0F"],"\x93"=>[141,"\x0F"],"\x94"=>[145,"\x0F"],"\x95"=>[147,"\x0F"],"\x96"=>[149,"\x0F"],"\x97"=>[101,"\x0F"],"\x98"=>[112,"\x0F"],"\x99"=>[117,"\x0F"],"\x9A"=>[120,"\x0F"],"\x9B"=>[124,"\x0F"],"\x9C"=>[127,"\x0F"],"\x9D"=>[144,"\x0F"],"\x9E"=>[152,"\x0F"],"\x9F"=>[11,"\x0F"],"\xA0"=>[0,"\x100"],"\xA1"=>[0,"\x101"],"\xA2"=>[0,"\x102"],"\xA3"=>[0,"\x10a"],"\xA4"=>[0,"\x10c"],"\xA5"=>[0,"\x10e"],"\xA6"=>[0,"\x10i"],"\xA7"=>[0,"\x10o"],"\xA8"=>[0,"\x10s"],"\xA9"=>[0,"\x10t"],"\xAA"=>[73,"\x10"],"\xAB"=>[88,"\x10"],"\xAC"=>[89,"\x10"],"\xAD"=>[96,"\x10"],"\xAE"=>[97,"\x10"],"\xAF"=>[99,"\x10"],"\xB0"=>[106,"\x10"],"\xB1"=>[136,"\x10"],"\xB2"=>[139,"\x10"],"\xB3"=>[141,"\x10"],"\xB4"=>[145,"\x10"],"\xB5"=>[147,"\x10"],"\xB6"=>[149,"\x10"],"\xB7"=>[101,"\x10"],"\xB8"=>[112,"\x10"],"\xB9"=>[117,"\x10"],"\xBA"=>[120,"\x10"],"\xBB"=>[124,"\x10"],"\xBC"=>[127,"\x10"],"\xBD"=>[144,"\x10"],"\xBE"=>[152,"\x10"],"\xBF"=>[11,"\x10"],"\xC0"=>[0,"\x110"],"\xC1"=>[0,"\x111"],"\xC2"=>[0,"\x112"],"\xC3"=>[0,"\x11a"],"\xC4"=>[0,"\x11c"],"\xC5"=>[0,"\x11e"],"\xC6"=>[0,"\x11i"],"\xC7"=>[0,"\x11o"],"\xC8"=>[0,"\x11s"],"\xC9"=>[0,"\x11t"],"\xCA"=>[73,"\x11"],"\xCB"=>[88,"\x11"],"\xCC"=>[89,"\x11"],"\xCD"=>[96,"\x11"],"\xCE"=>[97,"\x11"],"\xCF"=>[99,"\x11"],"\xD0"=>[106,"\x11"],"\xD1"=>[136,"\x11"],"\xD2"=>[139,"\x11"],"\xD3"=>[141,"\x11"],"\xD4"=>[145,"\x11"],"\xD5"=>[147,"\x11"],"\xD6"=>[149,"\x11"],"\xD7"=>[101,"\x11"],"\xD8"=>[112,"\x11"],"\xD9"=>[117,"\x11"],"\xDA"=>[120,"\x11"],"\xDB"=>[124,"\x11"],"\xDC"=>[127,"\x11"],"\xDD"=>[144,"\x11"],"\xDE"=>[152,"\x11"],"\xDF"=>[11,"\x11"],"\xE0"=>[0,"\x120"],"\xE1"=>[0,"\x121"],"\xE2"=>[0,"\x122"],"\xE3"=>[0,"\x12a"],"\xE4"=>[0,"\x12c"],"\xE5"=>[0,"\x12e"],"\xE6"=>[0,"\x12i"],"\xE7"=>[0,"\x12o"],"\xE8"=>[0,"\x12s"],"\xE9"=>[0,"\x12t"],"\xEA"=>[73,"\x12"],"\xEB"=>[88,"\x12"],"\xEC"=>[89,"\x12"],"\xED"=>[96,"\x12"],"\xEE"=>[97,"\x12"],"\xEF"=>[99,"\x12"],"\xF0"=>[106,"\x12"],"\xF1"=>[136,"\x12"],"\xF2"=>[139,"\x12"],"\xF3"=>[141,"\x12"],"\xF4"=>[145,"\x12"],"\xF5"=>[147,"\x12"],"\xF6"=>[149,"\x12"],"\xF7"=>[101,"\x12"],"\xF8"=>[112,"\x12"],"\xF9"=>[117,"\x12"],"\xFA"=>[120,"\x12"],"\xFB"=>[124,"\x12"],"\xFC"=>[127,"\x12"],"\xFD"=>[144,"\x12"],"\xFE"=>[152,"\x12"],"\xFF"=>[11,"\x12"],],["\x00"=>[94,"\x090"],"\x01"=>[76,"\x090"],"\x02"=>[104,"\x090"],"\x03"=>[16,"\x090"],"\x04"=>[94,"\x091"],"\x05"=>[76,"\x091"],"\x06"=>[104,"\x091"],"\x07"=>[16,"\x091"],"\x08"=>[94,"\x092"],"\x09"=>[76,"\x092"],"\x0A"=>[104,"\x092"],"\x0B"=>[16,"\x092"],"\x0C"=>[94,"\x09a"],"\x0D"=>[76,"\x09a"],"\x0E"=>[104,"\x09a"],"\x0F"=>[16,"\x09a"],"\x10"=>[94,"\x09c"],"\x11"=>[76,"\x09c"],"\x12"=>[104,"\x09c"],"\x13"=>[16,"\x09c"],"\x14"=>[94,"\x09e"],"\x15"=>[76,"\x09e"],"\x16"=>[104,"\x09e"],"\x17"=>[16,"\x09e"],"\x18"=>[94,"\x09i"],"\x19"=>[76,"\x09i"],"\x1A"=>[104,"\x09i"],"\x1B"=>[16,"\x09i"],"\x1C"=>[94,"\x09o"],"\x1D"=>[76,"\x09o"],"\x1E"=>[104,"\x09o"],"\x1F"=>[16,"\x09o"],"\x20"=>[94,"\x09s"],"\x21"=>[76,"\x09s"],"\x22"=>[104,"\x09s"],"\x23"=>[16,"\x09s"],"\x24"=>[94,"\x09t"],"\x25"=>[76,"\x09t"],"\x26"=>[104,"\x09t"],"\x27"=>[16,"\x09t"],"\x28"=>[77,"\x09\x20"],"\x29"=>[18,"\x09\x20"],"\x2A"=>[77,"\x09\x25"],"\x2B"=>[18,"\x09\x25"],"\x2C"=>[77,"\x09-"],"-"=>[18,"\x09-"],"."=>[77,"\x09."],"\x2F"=>[18,"\x09."],[77,"\x09\x2F"],[18,"\x09\x2F"],[77,"\x093"],[18,"\x093"],[77,"\x094"],[18,"\x094"],[77,"\x095"],[18,"\x095"],[77,"\x096"],[18,"\x096"],"\x3A"=>[77,"\x097"],"\x3B"=>[18,"\x097"],"\x3C"=>[77,"\x098"],"\x3D"=>[18,"\x098"],"\x3E"=>[77,"\x099"],"\x3F"=>[18,"\x099"],"\x40"=>[77,"\x09\x3D"],"A"=>[18,"\x09\x3D"],"B"=>[77,"\x09A"],"C"=>[18,"\x09A"],"D"=>[77,"\x09_"],"E"=>[18,"\x09_"],"F"=>[77,"\x09b"],"G"=>[18,"\x09b"],"H"=>[77,"\x09d"],"I"=>[18,"\x09d"],"J"=>[77,"\x09f"],"K"=>[18,"\x09f"],"L"=>[77,"\x09g"],"M"=>[18,"\x09g"],"N"=>[77,"\x09h"],"O"=>[18,"\x09h"],"P"=>[77,"\x09l"],"Q"=>[18,"\x09l"],"R"=>[77,"\x09m"],"S"=>[18,"\x09m"],"T"=>[77,"\x09n"],"U"=>[18,"\x09n"],"V"=>[77,"\x09p"],"W"=>[18,"\x09p"],"X"=>[77,"\x09r"],"Y"=>[18,"\x09r"],"Z"=>[77,"\x09u"],"\x5B"=>[18,"\x09u"],"\x5C"=>[0,"\x09\x3A"],"\x5D"=>[0,"\x09B"],"\x5E"=>[0,"\x09C"],"_"=>[0,"\x09D"],"\x60"=>[0,"\x09E"],"a"=>[0,"\x09F"],"b"=>[0,"\x09G"],"c"=>[0,"\x09H"],"d"=>[0,"\x09I"],"e"=>[0,"\x09J"],"f"=>[0,"\x09K"],"g"=>[0,"\x09L"],"h"=>[0,"\x09M"],"i"=>[0,"\x09N"],"j"=>[0,"\x09O"],"k"=>[0,"\x09P"],"l"=>[0,"\x09Q"],"m"=>[0,"\x09R"],"n"=>[0,"\x09S"],"o"=>[0,"\x09T"],"p"=>[0,"\x09U"],"q"=>[0,"\x09V"],"r"=>[0,"\x09W"],"s"=>[0,"\x09Y"],"t"=>[0,"\x09j"],"u"=>[0,"\x09k"],"v"=>[0,"\x09q"],"w"=>[0,"\x09v"],"x"=>[0,"\x09w"],"y"=>[0,"\x09x"],"z"=>[0,"\x09y"],"\x7B"=>[0,"\x09z"],"\x7C"=>[82,"\x09"],"\x7D"=>[87,"\x09"],"~"=>[130,"\x09"],"\x7F"=>[9,"\x09"],"\x80"=>[94,"\x8E0"],"\x81"=>[76,"\x8E0"],"\x82"=>[104,"\x8E0"],"\x83"=>[16,"\x8E0"],"\x84"=>[94,"\x8E1"],"\x85"=>[76,"\x8E1"],"\x86"=>[104,"\x8E1"],"\x87"=>[16,"\x8E1"],"\x88"=>[94,"\x8E2"],"\x89"=>[76,"\x8E2"],"\x8A"=>[104,"\x8E2"],"\x8B"=>[16,"\x8E2"],"\x8C"=>[94,"\x8Ea"],"\x8D"=>[76,"\x8Ea"],"\x8E"=>[104,"\x8Ea"],"\x8F"=>[16,"\x8Ea"],"\x90"=>[94,"\x8Ec"],"\x91"=>[76,"\x8Ec"],"\x92"=>[104,"\x8Ec"],"\x93"=>[16,"\x8Ec"],"\x94"=>[94,"\x8Ee"],"\x95"=>[76,"\x8Ee"],"\x96"=>[104,"\x8Ee"],"\x97"=>[16,"\x8Ee"],"\x98"=>[94,"\x8Ei"],"\x99"=>[76,"\x8Ei"],"\x9A"=>[104,"\x8Ei"],"\x9B"=>[16,"\x8Ei"],"\x9C"=>[94,"\x8Eo"],"\x9D"=>[76,"\x8Eo"],"\x9E"=>[104,"\x8Eo"],"\x9F"=>[16,"\x8Eo"],"\xA0"=>[94,"\x8Es"],"\xA1"=>[76,"\x8Es"],"\xA2"=>[104,"\x8Es"],"\xA3"=>[16,"\x8Es"],"\xA4"=>[94,"\x8Et"],"\xA5"=>[76,"\x8Et"],"\xA6"=>[104,"\x8Et"],"\xA7"=>[16,"\x8Et"],"\xA8"=>[77,"\x8E\x20"],"\xA9"=>[18,"\x8E\x20"],"\xAA"=>[77,"\x8E\x25"],"\xAB"=>[18,"\x8E\x25"],"\xAC"=>[77,"\x8E-"],"\xAD"=>[18,"\x8E-"],"\xAE"=>[77,"\x8E."],"\xAF"=>[18,"\x8E."],"\xB0"=>[77,"\x8E\x2F"],"\xB1"=>[18,"\x8E\x2F"],"\xB2"=>[77,"\x8E3"],"\xB3"=>[18,"\x8E3"],"\xB4"=>[77,"\x8E4"],"\xB5"=>[18,"\x8E4"],"\xB6"=>[77,"\x8E5"],"\xB7"=>[18,"\x8E5"],"\xB8"=>[77,"\x8E6"],"\xB9"=>[18,"\x8E6"],"\xBA"=>[77,"\x8E7"],"\xBB"=>[18,"\x8E7"],"\xBC"=>[77,"\x8E8"],"\xBD"=>[18,"\x8E8"],"\xBE"=>[77,"\x8E9"],"\xBF"=>[18,"\x8E9"],"\xC0"=>[77,"\x8E\x3D"],"\xC1"=>[18,"\x8E\x3D"],"\xC2"=>[77,"\x8EA"],"\xC3"=>[18,"\x8EA"],"\xC4"=>[77,"\x8E_"],"\xC5"=>[18,"\x8E_"],"\xC6"=>[77,"\x8Eb"],"\xC7"=>[18,"\x8Eb"],"\xC8"=>[77,"\x8Ed"],"\xC9"=>[18,"\x8Ed"],"\xCA"=>[77,"\x8Ef"],"\xCB"=>[18,"\x8Ef"],"\xCC"=>[77,"\x8Eg"],"\xCD"=>[18,"\x8Eg"],"\xCE"=>[77,"\x8Eh"],"\xCF"=>[18,"\x8Eh"],"\xD0"=>[77,"\x8El"],"\xD1"=>[18,"\x8El"],"\xD2"=>[77,"\x8Em"],"\xD3"=>[18,"\x8Em"],"\xD4"=>[77,"\x8En"],"\xD5"=>[18,"\x8En"],"\xD6"=>[77,"\x8Ep"],"\xD7"=>[18,"\x8Ep"],"\xD8"=>[77,"\x8Er"],"\xD9"=>[18,"\x8Er"],"\xDA"=>[77,"\x8Eu"],"\xDB"=>[18,"\x8Eu"],"\xDC"=>[0,"\x8E\x3A"],"\xDD"=>[0,"\x8EB"],"\xDE"=>[0,"\x8EC"],"\xDF"=>[0,"\x8ED"],"\xE0"=>[0,"\x8EE"],"\xE1"=>[0,"\x8EF"],"\xE2"=>[0,"\x8EG"],"\xE3"=>[0,"\x8EH"],"\xE4"=>[0,"\x8EI"],"\xE5"=>[0,"\x8EJ"],"\xE6"=>[0,"\x8EK"],"\xE7"=>[0,"\x8EL"],"\xE8"=>[0,"\x8EM"],"\xE9"=>[0,"\x8EN"],"\xEA"=>[0,"\x8EO"],"\xEB"=>[0,"\x8EP"],"\xEC"=>[0,"\x8EQ"],"\xED"=>[0,"\x8ER"],"\xEE"=>[0,"\x8ES"],"\xEF"=>[0,"\x8ET"],"\xF0"=>[0,"\x8EU"],"\xF1"=>[0,"\x8EV"],"\xF2"=>[0,"\x8EW"],"\xF3"=>[0,"\x8EY"],"\xF4"=>[0,"\x8Ej"],"\xF5"=>[0,"\x8Ek"],"\xF6"=>[0,"\x8Eq"],"\xF7"=>[0,"\x8Ev"],"\xF8"=>[0,"\x8Ew"],"\xF9"=>[0,"\x8Ex"],"\xFA"=>[0,"\x8Ey"],"\xFB"=>[0,"\x8Ez"],"\xFC"=>[82,"\x8E"],"\xFD"=>[87,"\x8E"],"\xFE"=>[130,"\x8E"],"\xFF"=>[9,"\x8E"],],["\x00"=>[94,"\xEF0"],"\x01"=>[76,"\xEF0"],"\x02"=>[104,"\xEF0"],"\x03"=>[16,"\xEF0"],"\x04"=>[94,"\xEF1"],"\x05"=>[76,"\xEF1"],"\x06"=>[104,"\xEF1"],"\x07"=>[16,"\xEF1"],"\x08"=>[94,"\xEF2"],"\x09"=>[76,"\xEF2"],"\x0A"=>[104,"\xEF2"],"\x0B"=>[16,"\xEF2"],"\x0C"=>[94,"\xEFa"],"\x0D"=>[76,"\xEFa"],"\x0E"=>[104,"\xEFa"],"\x0F"=>[16,"\xEFa"],"\x10"=>[94,"\xEFc"],"\x11"=>[76,"\xEFc"],"\x12"=>[104,"\xEFc"],"\x13"=>[16,"\xEFc"],"\x14"=>[94,"\xEFe"],"\x15"=>[76,"\xEFe"],"\x16"=>[104,"\xEFe"],"\x17"=>[16,"\xEFe"],"\x18"=>[94,"\xEFi"],"\x19"=>[76,"\xEFi"],"\x1A"=>[104,"\xEFi"],"\x1B"=>[16,"\xEFi"],"\x1C"=>[94,"\xEFo"],"\x1D"=>[76,"\xEFo"],"\x1E"=>[104,"\xEFo"],"\x1F"=>[16,"\xEFo"],"\x20"=>[94,"\xEFs"],"\x21"=>[76,"\xEFs"],"\x22"=>[104,"\xEFs"],"\x23"=>[16,"\xEFs"],"\x24"=>[94,"\xEFt"],"\x25"=>[76,"\xEFt"],"\x26"=>[104,"\xEFt"],"\x27"=>[16,"\xEFt"],"\x28"=>[77,"\xEF\x20"],"\x29"=>[18,"\xEF\x20"],"\x2A"=>[77,"\xEF\x25"],"\x2B"=>[18,"\xEF\x25"],"\x2C"=>[77,"\xEF-"],"-"=>[18,"\xEF-"],"."=>[77,"\xEF."],"\x2F"=>[18,"\xEF."],[77,"\xEF\x2F"],[18,"\xEF\x2F"],[77,"\xEF3"],[18,"\xEF3"],[77,"\xEF4"],[18,"\xEF4"],[77,"\xEF5"],[18,"\xEF5"],[77,"\xEF6"],[18,"\xEF6"],"\x3A"=>[77,"\xEF7"],"\x3B"=>[18,"\xEF7"],"\x3C"=>[77,"\xEF8"],"\x3D"=>[18,"\xEF8"],"\x3E"=>[77,"\xEF9"],"\x3F"=>[18,"\xEF9"],"\x40"=>[77,"\xEF\x3D"],"A"=>[18,"\xEF\x3D"],"B"=>[77,"\xEFA"],"C"=>[18,"\xEFA"],"D"=>[77,"\xEF_"],"E"=>[18,"\xEF_"],"F"=>[77,"\xEFb"],"G"=>[18,"\xEFb"],"H"=>[77,"\xEFd"],"I"=>[18,"\xEFd"],"J"=>[77,"\xEFf"],"K"=>[18,"\xEFf"],"L"=>[77,"\xEFg"],"M"=>[18,"\xEFg"],"N"=>[77,"\xEFh"],"O"=>[18,"\xEFh"],"P"=>[77,"\xEFl"],"Q"=>[18,"\xEFl"],"R"=>[77,"\xEFm"],"S"=>[18,"\xEFm"],"T"=>[77,"\xEFn"],"U"=>[18,"\xEFn"],"V"=>[77,"\xEFp"],"W"=>[18,"\xEFp"],"X"=>[77,"\xEFr"],"Y"=>[18,"\xEFr"],"Z"=>[77,"\xEFu"],"\x5B"=>[18,"\xEFu"],"\x5C"=>[0,"\xEF\x3A"],"\x5D"=>[0,"\xEFB"],"\x5E"=>[0,"\xEFC"],"_"=>[0,"\xEFD"],"\x60"=>[0,"\xEFE"],"a"=>[0,"\xEFF"],"b"=>[0,"\xEFG"],"c"=>[0,"\xEFH"],"d"=>[0,"\xEFI"],"e"=>[0,"\xEFJ"],"f"=>[0,"\xEFK"],"g"=>[0,"\xEFL"],"h"=>[0,"\xEFM"],"i"=>[0,"\xEFN"],"j"=>[0,"\xEFO"],"k"=>[0,"\xEFP"],"l"=>[0,"\xEFQ"],"m"=>[0,"\xEFR"],"n"=>[0,"\xEFS"],"o"=>[0,"\xEFT"],"p"=>[0,"\xEFU"],"q"=>[0,"\xEFV"],"r"=>[0,"\xEFW"],"s"=>[0,"\xEFY"],"t"=>[0,"\xEFj"],"u"=>[0,"\xEFk"],"v"=>[0,"\xEFq"],"w"=>[0,"\xEFv"],"x"=>[0,"\xEFw"],"y"=>[0,"\xEFx"],"z"=>[0,"\xEFy"],"\x7B"=>[0,"\xEFz"],"\x7C"=>[82,"\xEF"],"\x7D"=>[87,"\xEF"],"~"=>[130,"\xEF"],"\x7F"=>[9,"\xEF"],"\x80"=>[77,"\x090"],"\x81"=>[18,"\x090"],"\x82"=>[77,"\x091"],"\x83"=>[18,"\x091"],"\x84"=>[77,"\x092"],"\x85"=>[18,"\x092"],"\x86"=>[77,"\x09a"],"\x87"=>[18,"\x09a"],"\x88"=>[77,"\x09c"],"\x89"=>[18,"\x09c"],"\x8A"=>[77,"\x09e"],"\x8B"=>[18,"\x09e"],"\x8C"=>[77,"\x09i"],"\x8D"=>[18,"\x09i"],"\x8E"=>[77,"\x09o"],"\x8F"=>[18,"\x09o"],"\x90"=>[77,"\x09s"],"\x91"=>[18,"\x09s"],"\x92"=>[77,"\x09t"],"\x93"=>[18,"\x09t"],"\x94"=>[0,"\x09\x20"],"\x95"=>[0,"\x09\x25"],"\x96"=>[0,"\x09-"],"\x97"=>[0,"\x09."],"\x98"=>[0,"\x09\x2F"],"\x99"=>[0,"\x093"],"\x9A"=>[0,"\x094"],"\x9B"=>[0,"\x095"],"\x9C"=>[0,"\x096"],"\x9D"=>[0,"\x097"],"\x9E"=>[0,"\x098"],"\x9F"=>[0,"\x099"],"\xA0"=>[0,"\x09\x3D"],"\xA1"=>[0,"\x09A"],"\xA2"=>[0,"\x09_"],"\xA3"=>[0,"\x09b"],"\xA4"=>[0,"\x09d"],"\xA5"=>[0,"\x09f"],"\xA6"=>[0,"\x09g"],"\xA7"=>[0,"\x09h"],"\xA8"=>[0,"\x09l"],"\xA9"=>[0,"\x09m"],"\xAA"=>[0,"\x09n"],"\xAB"=>[0,"\x09p"],"\xAC"=>[0,"\x09r"],"\xAD"=>[0,"\x09u"],"\xAE"=>[100,"\x09"],"\xAF"=>[110,"\x09"],"\xB0"=>[111,"\x09"],"\xB1"=>[115,"\x09"],"\xB2"=>[116,"\x09"],"\xB3"=>[118,"\x09"],"\xB4"=>[119,"\x09"],"\xB5"=>[122,"\x09"],"\xB6"=>[123,"\x09"],"\xB7"=>[125,"\x09"],"\xB8"=>[126,"\x09"],"\xB9"=>[129,"\x09"],"\xBA"=>[143,"\x09"],"\xBB"=>[148,"\x09"],"\xBC"=>[151,"\x09"],"\xBD"=>[153,"\x09"],"\xBE"=>[83,"\x09"],"\xBF"=>[10,"\x09"],"\xC0"=>[77,"\x8E0"],"\xC1"=>[18,"\x8E0"],"\xC2"=>[77,"\x8E1"],"\xC3"=>[18,"\x8E1"],"\xC4"=>[77,"\x8E2"],"\xC5"=>[18,"\x8E2"],"\xC6"=>[77,"\x8Ea"],"\xC7"=>[18,"\x8Ea"],"\xC8"=>[77,"\x8Ec"],"\xC9"=>[18,"\x8Ec"],"\xCA"=>[77,"\x8Ee"],"\xCB"=>[18,"\x8Ee"],"\xCC"=>[77,"\x8Ei"],"\xCD"=>[18,"\x8Ei"],"\xCE"=>[77,"\x8Eo"],"\xCF"=>[18,"\x8Eo"],"\xD0"=>[77,"\x8Es"],"\xD1"=>[18,"\x8Es"],"\xD2"=>[77,"\x8Et"],"\xD3"=>[18,"\x8Et"],"\xD4"=>[0,"\x8E\x20"],"\xD5"=>[0,"\x8E\x25"],"\xD6"=>[0,"\x8E-"],"\xD7"=>[0,"\x8E."],"\xD8"=>[0,"\x8E\x2F"],"\xD9"=>[0,"\x8E3"],"\xDA"=>[0,"\x8E4"],"\xDB"=>[0,"\x8E5"],"\xDC"=>[0,"\x8E6"],"\xDD"=>[0,"\x8E7"],"\xDE"=>[0,"\x8E8"],"\xDF"=>[0,"\x8E9"],"\xE0"=>[0,"\x8E\x3D"],"\xE1"=>[0,"\x8EA"],"\xE2"=>[0,"\x8E_"],"\xE3"=>[0,"\x8Eb"],"\xE4"=>[0,"\x8Ed"],"\xE5"=>[0,"\x8Ef"],"\xE6"=>[0,"\x8Eg"],"\xE7"=>[0,"\x8Eh"],"\xE8"=>[0,"\x8El"],"\xE9"=>[0,"\x8Em"],"\xEA"=>[0,"\x8En"],"\xEB"=>[0,"\x8Ep"],"\xEC"=>[0,"\x8Er"],"\xED"=>[0,"\x8Eu"],"\xEE"=>[100,"\x8E"],"\xEF"=>[110,"\x8E"],"\xF0"=>[111,"\x8E"],"\xF1"=>[115,"\x8E"],"\xF2"=>[116,"\x8E"],"\xF3"=>[118,"\x8E"],"\xF4"=>[119,"\x8E"],"\xF5"=>[122,"\x8E"],"\xF6"=>[123,"\x8E"],"\xF7"=>[125,"\x8E"],"\xF8"=>[126,"\x8E"],"\xF9"=>[129,"\x8E"],"\xFA"=>[143,"\x8E"],"\xFB"=>[148,"\x8E"],"\xFC"=>[151,"\x8E"],"\xFD"=>[153,"\x8E"],"\xFE"=>[83,"\x8E"],"\xFF"=>[10,"\x8E"],],["\x00"=>[77,"\xEF0"],"\x01"=>[18,"\xEF0"],"\x02"=>[77,"\xEF1"],"\x03"=>[18,"\xEF1"],"\x04"=>[77,"\xEF2"],"\x05"=>[18,"\xEF2"],"\x06"=>[77,"\xEFa"],"\x07"=>[18,"\xEFa"],"\x08"=>[77,"\xEFc"],"\x09"=>[18,"\xEFc"],"\x0A"=>[77,"\xEFe"],"\x0B"=>[18,"\xEFe"],"\x0C"=>[77,"\xEFi"],"\x0D"=>[18,"\xEFi"],"\x0E"=>[77,"\xEFo"],"\x0F"=>[18,"\xEFo"],"\x10"=>[77,"\xEFs"],"\x11"=>[18,"\xEFs"],"\x12"=>[77,"\xEFt"],"\x13"=>[18,"\xEFt"],"\x14"=>[0,"\xEF\x20"],"\x15"=>[0,"\xEF\x25"],"\x16"=>[0,"\xEF-"],"\x17"=>[0,"\xEF."],"\x18"=>[0,"\xEF\x2F"],"\x19"=>[0,"\xEF3"],"\x1A"=>[0,"\xEF4"],"\x1B"=>[0,"\xEF5"],"\x1C"=>[0,"\xEF6"],"\x1D"=>[0,"\xEF7"],"\x1E"=>[0,"\xEF8"],"\x1F"=>[0,"\xEF9"],"\x20"=>[0,"\xEF\x3D"],"\x21"=>[0,"\xEFA"],"\x22"=>[0,"\xEF_"],"\x23"=>[0,"\xEFb"],"\x24"=>[0,"\xEFd"],"\x25"=>[0,"\xEFf"],"\x26"=>[0,"\xEFg"],"\x27"=>[0,"\xEFh"],"\x28"=>[0,"\xEFl"],"\x29"=>[0,"\xEFm"],"\x2A"=>[0,"\xEFn"],"\x2B"=>[0,"\xEFp"],"\x2C"=>[0,"\xEFr"],"-"=>[0,"\xEFu"],"."=>[100,"\xEF"],"\x2F"=>[110,"\xEF"],[111,"\xEF"],[115,"\xEF"],[116,"\xEF"],[118,"\xEF"],[119,"\xEF"],[122,"\xEF"],[123,"\xEF"],[125,"\xEF"],[126,"\xEF"],[129,"\xEF"],"\x3A"=>[143,"\xEF"],"\x3B"=>[148,"\xEF"],"\x3C"=>[151,"\xEF"],"\x3D"=>[153,"\xEF"],"\x3E"=>[83,"\xEF"],"\x3F"=>[10,"\xEF"],"\x40"=>[0,"\x090"],"A"=>[0,"\x091"],"B"=>[0,"\x092"],"C"=>[0,"\x09a"],"D"=>[0,"\x09c"],"E"=>[0,"\x09e"],"F"=>[0,"\x09i"],"G"=>[0,"\x09o"],"H"=>[0,"\x09s"],"I"=>[0,"\x09t"],"J"=>[73,"\x09"],"K"=>[88,"\x09"],"L"=>[89,"\x09"],"M"=>[96,"\x09"],"N"=>[97,"\x09"],"O"=>[99,"\x09"],"P"=>[106,"\x09"],"Q"=>[136,"\x09"],"R"=>[139,"\x09"],"S"=>[141,"\x09"],"T"=>[145,"\x09"],"U"=>[147,"\x09"],"V"=>[149,"\x09"],"W"=>[101,"\x09"],"X"=>[112,"\x09"],"Y"=>[117,"\x09"],"Z"=>[120,"\x09"],"\x5B"=>[124,"\x09"],"\x5C"=>[127,"\x09"],"\x5D"=>[144,"\x09"],"\x5E"=>[152,"\x09"],"_"=>[11,"\x09"],"\x60"=>[0,"\x8E0"],"a"=>[0,"\x8E1"],"b"=>[0,"\x8E2"],"c"=>[0,"\x8Ea"],"d"=>[0,"\x8Ec"],"e"=>[0,"\x8Ee"],"f"=>[0,"\x8Ei"],"g"=>[0,"\x8Eo"],"h"=>[0,"\x8Es"],"i"=>[0,"\x8Et"],"j"=>[73,"\x8E"],"k"=>[88,"\x8E"],"l"=>[89,"\x8E"],"m"=>[96,"\x8E"],"n"=>[97,"\x8E"],"o"=>[99,"\x8E"],"p"=>[106,"\x8E"],"q"=>[136,"\x8E"],"r"=>[139,"\x8E"],"s"=>[141,"\x8E"],"t"=>[145,"\x8E"],"u"=>[147,"\x8E"],"v"=>[149,"\x8E"],"w"=>[101,"\x8E"],"x"=>[112,"\x8E"],"y"=>[117,"\x8E"],"z"=>[120,"\x8E"],"\x7B"=>[124,"\x8E"],"\x7C"=>[127,"\x8E"],"\x7D"=>[144,"\x8E"],"~"=>[152,"\x8E"],"\x7F"=>[11,"\x8E"],"\x80"=>[0,"\x900"],"\x81"=>[0,"\x901"],"\x82"=>[0,"\x902"],"\x83"=>[0,"\x90a"],"\x84"=>[0,"\x90c"],"\x85"=>[0,"\x90e"],"\x86"=>[0,"\x90i"],"\x87"=>[0,"\x90o"],"\x88"=>[0,"\x90s"],"\x89"=>[0,"\x90t"],"\x8A"=>[73,"\x90"],"\x8B"=>[88,"\x90"],"\x8C"=>[89,"\x90"],"\x8D"=>[96,"\x90"],"\x8E"=>[97,"\x90"],"\x8F"=>[99,"\x90"],"\x90"=>[106,"\x90"],"\x91"=>[136,"\x90"],"\x92"=>[139,"\x90"],"\x93"=>[141,"\x90"],"\x94"=>[145,"\x90"],"\x95"=>[147,"\x90"],"\x96"=>[149,"\x90"],"\x97"=>[101,"\x90"],"\x98"=>[112,"\x90"],"\x99"=>[117,"\x90"],"\x9A"=>[120,"\x90"],"\x9B"=>[124,"\x90"],"\x9C"=>[127,"\x90"],"\x9D"=>[144,"\x90"],"\x9E"=>[152,"\x90"],"\x9F"=>[11,"\x90"],"\xA0"=>[0,"\x910"],"\xA1"=>[0,"\x911"],"\xA2"=>[0,"\x912"],"\xA3"=>[0,"\x91a"],"\xA4"=>[0,"\x91c"],"\xA5"=>[0,"\x91e"],"\xA6"=>[0,"\x91i"],"\xA7"=>[0,"\x91o"],"\xA8"=>[0,"\x91s"],"\xA9"=>[0,"\x91t"],"\xAA"=>[73,"\x91"],"\xAB"=>[88,"\x91"],"\xAC"=>[89,"\x91"],"\xAD"=>[96,"\x91"],"\xAE"=>[97,"\x91"],"\xAF"=>[99,"\x91"],"\xB0"=>[106,"\x91"],"\xB1"=>[136,"\x91"],"\xB2"=>[139,"\x91"],"\xB3"=>[141,"\x91"],"\xB4"=>[145,"\x91"],"\xB5"=>[147,"\x91"],"\xB6"=>[149,"\x91"],"\xB7"=>[101,"\x91"],"\xB8"=>[112,"\x91"],"\xB9"=>[117,"\x91"],"\xBA"=>[120,"\x91"],"\xBB"=>[124,"\x91"],"\xBC"=>[127,"\x91"],"\xBD"=>[144,"\x91"],"\xBE"=>[152,"\x91"],"\xBF"=>[11,"\x91"],"\xC0"=>[0,"\x940"],"\xC1"=>[0,"\x941"],"\xC2"=>[0,"\x942"],"\xC3"=>[0,"\x94a"],"\xC4"=>[0,"\x94c"],"\xC5"=>[0,"\x94e"],"\xC6"=>[0,"\x94i"],"\xC7"=>[0,"\x94o"],"\xC8"=>[0,"\x94s"],"\xC9"=>[0,"\x94t"],"\xCA"=>[73,"\x94"],"\xCB"=>[88,"\x94"],"\xCC"=>[89,"\x94"],"\xCD"=>[96,"\x94"],"\xCE"=>[97,"\x94"],"\xCF"=>[99,"\x94"],"\xD0"=>[106,"\x94"],"\xD1"=>[136,"\x94"],"\xD2"=>[139,"\x94"],"\xD3"=>[141,"\x94"],"\xD4"=>[145,"\x94"],"\xD5"=>[147,"\x94"],"\xD6"=>[149,"\x94"],"\xD7"=>[101,"\x94"],"\xD8"=>[112,"\x94"],"\xD9"=>[117,"\x94"],"\xDA"=>[120,"\x94"],"\xDB"=>[124,"\x94"],"\xDC"=>[127,"\x94"],"\xDD"=>[144,"\x94"],"\xDE"=>[152,"\x94"],"\xDF"=>[11,"\x94"],"\xE0"=>[0,"\x9F0"],"\xE1"=>[0,"\x9F1"],"\xE2"=>[0,"\x9F2"],"\xE3"=>[0,"\x9Fa"],"\xE4"=>[0,"\x9Fc"],"\xE5"=>[0,"\x9Fe"],"\xE6"=>[0,"\x9Fi"],"\xE7"=>[0,"\x9Fo"],"\xE8"=>[0,"\x9Fs"],"\xE9"=>[0,"\x9Ft"],"\xEA"=>[73,"\x9F"],"\xEB"=>[88,"\x9F"],"\xEC"=>[89,"\x9F"],"\xED"=>[96,"\x9F"],"\xEE"=>[97,"\x9F"],"\xEF"=>[99,"\x9F"],"\xF0"=>[106,"\x9F"],"\xF1"=>[136,"\x9F"],"\xF2"=>[139,"\x9F"],"\xF3"=>[141,"\x9F"],"\xF4"=>[145,"\x9F"],"\xF5"=>[147,"\x9F"],"\xF6"=>[149,"\x9F"],"\xF7"=>[101,"\x9F"],"\xF8"=>[112,"\x9F"],"\xF9"=>[117,"\x9F"],"\xFA"=>[120,"\x9F"],"\xFB"=>[124,"\x9F"],"\xFC"=>[127,"\x9F"],"\xFD"=>[144,"\x9F"],"\xFE"=>[152,"\x9F"],"\xFF"=>[11,"\x9F"],],["\x00"=>[0,"\xBC0"],"\x01"=>[0,"\xBC1"],"\x02"=>[0,"\xBC2"],"\x03"=>[0,"\xBCa"],"\x04"=>[0,"\xBCc"],"\x05"=>[0,"\xBCe"],"\x06"=>[0,"\xBCi"],"\x07"=>[0,"\xBCo"],"\x08"=>[0,"\xBCs"],"\x09"=>[0,"\xBCt"],"\x0A"=>[73,"\xBC"],"\x0B"=>[88,"\xBC"],"\x0C"=>[89,"\xBC"],"\x0D"=>[96,"\xBC"],"\x0E"=>[97,"\xBC"],"\x0F"=>[99,"\xBC"],"\x10"=>[106,"\xBC"],"\x11"=>[136,"\xBC"],"\x12"=>[139,"\xBC"],"\x13"=>[141,"\xBC"],"\x14"=>[145,"\xBC"],"\x15"=>[147,"\xBC"],"\x16"=>[149,"\xBC"],"\x17"=>[101,"\xBC"],"\x18"=>[112,"\xBC"],"\x19"=>[117,"\xBC"],"\x1A"=>[120,"\xBC"],"\x1B"=>[124,"\xBC"],"\x1C"=>[127,"\xBC"],"\x1D"=>[144,"\xBC"],"\x1E"=>[152,"\xBC"],"\x1F"=>[11,"\xBC"],"\x20"=>[0,"\xBF0"],"\x21"=>[0,"\xBF1"],"\x22"=>[0,"\xBF2"],"\x23"=>[0,"\xBFa"],"\x24"=>[0,"\xBFc"],"\x25"=>[0,"\xBFe"],"\x26"=>[0,"\xBFi"],"\x27"=>[0,"\xBFo"],"\x28"=>[0,"\xBFs"],"\x29"=>[0,"\xBFt"],"\x2A"=>[73,"\xBF"],"\x2B"=>[88,"\xBF"],"\x2C"=>[89,"\xBF"],"-"=>[96,"\xBF"],"."=>[97,"\xBF"],"\x2F"=>[99,"\xBF"],[106,"\xBF"],[136,"\xBF"],[139,"\xBF"],[141,"\xBF"],[145,"\xBF"],[147,"\xBF"],[149,"\xBF"],[101,"\xBF"],[112,"\xBF"],[117,"\xBF"],"\x3A"=>[120,"\xBF"],"\x3B"=>[124,"\xBF"],"\x3C"=>[127,"\xBF"],"\x3D"=>[144,"\xBF"],"\x3E"=>[152,"\xBF"],"\x3F"=>[11,"\xBF"],"\x40"=>[0,"\xC50"],"A"=>[0,"\xC51"],"B"=>[0,"\xC52"],"C"=>[0,"\xC5a"],"D"=>[0,"\xC5c"],"E"=>[0,"\xC5e"],"F"=>[0,"\xC5i"],"G"=>[0,"\xC5o"],"H"=>[0,"\xC5s"],"I"=>[0,"\xC5t"],"J"=>[73,"\xC5"],"K"=>[88,"\xC5"],"L"=>[89,"\xC5"],"M"=>[96,"\xC5"],"N"=>[97,"\xC5"],"O"=>[99,"\xC5"],"P"=>[106,"\xC5"],"Q"=>[136,"\xC5"],"R"=>[139,"\xC5"],"S"=>[141,"\xC5"],"T"=>[145,"\xC5"],"U"=>[147,"\xC5"],"V"=>[149,"\xC5"],"W"=>[101,"\xC5"],"X"=>[112,"\xC5"],"Y"=>[117,"\xC5"],"Z"=>[120,"\xC5"],"\x5B"=>[124,"\xC5"],"\x5C"=>[127,"\xC5"],"\x5D"=>[144,"\xC5"],"\x5E"=>[152,"\xC5"],"_"=>[11,"\xC5"],"\x60"=>[0,"\xE70"],"a"=>[0,"\xE71"],"b"=>[0,"\xE72"],"c"=>[0,"\xE7a"],"d"=>[0,"\xE7c"],"e"=>[0,"\xE7e"],"f"=>[0,"\xE7i"],"g"=>[0,"\xE7o"],"h"=>[0,"\xE7s"],"i"=>[0,"\xE7t"],"j"=>[73,"\xE7"],"k"=>[88,"\xE7"],"l"=>[89,"\xE7"],"m"=>[96,"\xE7"],"n"=>[97,"\xE7"],"o"=>[99,"\xE7"],"p"=>[106,"\xE7"],"q"=>[136,"\xE7"],"r"=>[139,"\xE7"],"s"=>[141,"\xE7"],"t"=>[145,"\xE7"],"u"=>[147,"\xE7"],"v"=>[149,"\xE7"],"w"=>[101,"\xE7"],"x"=>[112,"\xE7"],"y"=>[117,"\xE7"],"z"=>[120,"\xE7"],"\x7B"=>[124,"\xE7"],"\x7C"=>[127,"\xE7"],"\x7D"=>[144,"\xE7"],"~"=>[152,"\xE7"],"\x7F"=>[11,"\xE7"],"\x80"=>[0,"\xEF0"],"\x81"=>[0,"\xEF1"],"\x82"=>[0,"\xEF2"],"\x83"=>[0,"\xEFa"],"\x84"=>[0,"\xEFc"],"\x85"=>[0,"\xEFe"],"\x86"=>[0,"\xEFi"],"\x87"=>[0,"\xEFo"],"\x88"=>[0,"\xEFs"],"\x89"=>[0,"\xEFt"],"\x8A"=>[73,"\xEF"],"\x8B"=>[88,"\xEF"],"\x8C"=>[89,"\xEF"],"\x8D"=>[96,"\xEF"],"\x8E"=>[97,"\xEF"],"\x8F"=>[99,"\xEF"],"\x90"=>[106,"\xEF"],"\x91"=>[136,"\xEF"],"\x92"=>[139,"\xEF"],"\x93"=>[141,"\xEF"],"\x94"=>[145,"\xEF"],"\x95"=>[147,"\xEF"],"\x96"=>[149,"\xEF"],"\x97"=>[101,"\xEF"],"\x98"=>[112,"\xEF"],"\x99"=>[117,"\xEF"],"\x9A"=>[120,"\xEF"],"\x9B"=>[124,"\xEF"],"\x9C"=>[127,"\xEF"],"\x9D"=>[144,"\xEF"],"\x9E"=>[152,"\xEF"],"\x9F"=>[11,"\xEF"],"\xA0"=>[92,"\x09"],"\xA1"=>[95,"\x09"],"\xA2"=>[137,"\x09"],"\xA3"=>[142,"\x09"],"\xA4"=>[150,"\x09"],"\xA5"=>[74,"\x09"],"\xA6"=>[90,"\x09"],"\xA7"=>[98,"\x09"],"\xA8"=>[107,"\x09"],"\xA9"=>[140,"\x09"],"\xAA"=>[146,"\x09"],"\xAB"=>[102,"\x09"],"\xAC"=>[113,"\x09"],"\xAD"=>[121,"\x09"],"\xAE"=>[128,"\x09"],"\xAF"=>[12,"\x09"],"\xB0"=>[92,"\x8E"],"\xB1"=>[95,"\x8E"],"\xB2"=>[137,"\x8E"],"\xB3"=>[142,"\x8E"],"\xB4"=>[150,"\x8E"],"\xB5"=>[74,"\x8E"],"\xB6"=>[90,"\x8E"],"\xB7"=>[98,"\x8E"],"\xB8"=>[107,"\x8E"],"\xB9"=>[140,"\x8E"],"\xBA"=>[146,"\x8E"],"\xBB"=>[102,"\x8E"],"\xBC"=>[113,"\x8E"],"\xBD"=>[121,"\x8E"],"\xBE"=>[128,"\x8E"],"\xBF"=>[12,"\x8E"],"\xC0"=>[92,"\x90"],"\xC1"=>[95,"\x90"],"\xC2"=>[137,"\x90"],"\xC3"=>[142,"\x90"],"\xC4"=>[150,"\x90"],"\xC5"=>[74,"\x90"],"\xC6"=>[90,"\x90"],"\xC7"=>[98,"\x90"],"\xC8"=>[107,"\x90"],"\xC9"=>[140,"\x90"],"\xCA"=>[146,"\x90"],"\xCB"=>[102,"\x90"],"\xCC"=>[113,"\x90"],"\xCD"=>[121,"\x90"],"\xCE"=>[128,"\x90"],"\xCF"=>[12,"\x90"],"\xD0"=>[92,"\x91"],"\xD1"=>[95,"\x91"],"\xD2"=>[137,"\x91"],"\xD3"=>[142,"\x91"],"\xD4"=>[150,"\x91"],"\xD5"=>[74,"\x91"],"\xD6"=>[90,"\x91"],"\xD7"=>[98,"\x91"],"\xD8"=>[107,"\x91"],"\xD9"=>[140,"\x91"],"\xDA"=>[146,"\x91"],"\xDB"=>[102,"\x91"],"\xDC"=>[113,"\x91"],"\xDD"=>[121,"\x91"],"\xDE"=>[128,"\x91"],"\xDF"=>[12,"\x91"],"\xE0"=>[92,"\x94"],"\xE1"=>[95,"\x94"],"\xE2"=>[137,"\x94"],"\xE3"=>[142,"\x94"],"\xE4"=>[150,"\x94"],"\xE5"=>[74,"\x94"],"\xE6"=>[90,"\x94"],"\xE7"=>[98,"\x94"],"\xE8"=>[107,"\x94"],"\xE9"=>[140,"\x94"],"\xEA"=>[146,"\x94"],"\xEB"=>[102,"\x94"],"\xEC"=>[113,"\x94"],"\xED"=>[121,"\x94"],"\xEE"=>[128,"\x94"],"\xEF"=>[12,"\x94"],"\xF0"=>[92,"\x9F"],"\xF1"=>[95,"\x9F"],"\xF2"=>[137,"\x9F"],"\xF3"=>[142,"\x9F"],"\xF4"=>[150,"\x9F"],"\xF5"=>[74,"\x9F"],"\xF6"=>[90,"\x9F"],"\xF7"=>[98,"\x9F"],"\xF8"=>[107,"\x9F"],"\xF9"=>[140,"\x9F"],"\xFA"=>[146,"\x9F"],"\xFB"=>[102,"\x9F"],"\xFC"=>[113,"\x9F"],"\xFD"=>[121,"\x9F"],"\xFE"=>[128,"\x9F"],"\xFF"=>[12,"\x9F"],],["\x00"=>[92,"\x13"],"\x01"=>[95,"\x13"],"\x02"=>[137,"\x13"],"\x03"=>[142,"\x13"],"\x04"=>[150,"\x13"],"\x05"=>[74,"\x13"],"\x06"=>[90,"\x13"],"\x07"=>[98,"\x13"],"\x08"=>[107,"\x13"],"\x09"=>[140,"\x13"],"\x0A"=>[146,"\x13"],"\x0B"=>[102,"\x13"],"\x0C"=>[113,"\x13"],"\x0D"=>[121,"\x13"],"\x0E"=>[128,"\x13"],"\x0F"=>[12,"\x13"],"\x10"=>[92,"\x14"],"\x11"=>[95,"\x14"],"\x12"=>[137,"\x14"],"\x13"=>[142,"\x14"],"\x14"=>[150,"\x14"],"\x15"=>[74,"\x14"],"\x16"=>[90,"\x14"],"\x17"=>[98,"\x14"],"\x18"=>[107,"\x14"],"\x19"=>[140,"\x14"],"\x1A"=>[146,"\x14"],"\x1B"=>[102,"\x14"],"\x1C"=>[113,"\x14"],"\x1D"=>[121,"\x14"],"\x1E"=>[128,"\x14"],"\x1F"=>[12,"\x14"],"\x20"=>[92,"\x15"],"\x21"=>[95,"\x15"],"\x22"=>[137,"\x15"],"\x23"=>[142,"\x15"],"\x24"=>[150,"\x15"],"\x25"=>[74,"\x15"],"\x26"=>[90,"\x15"],"\x27"=>[98,"\x15"],"\x28"=>[107,"\x15"],"\x29"=>[140,"\x15"],"\x2A"=>[146,"\x15"],"\x2B"=>[102,"\x15"],"\x2C"=>[113,"\x15"],"-"=>[121,"\x15"],"."=>[128,"\x15"],"\x2F"=>[12,"\x15"],[92,"\x17"],[95,"\x17"],[137,"\x17"],[142,"\x17"],[150,"\x17"],[74,"\x17"],[90,"\x17"],[98,"\x17"],[107,"\x17"],[140,"\x17"],"\x3A"=>[146,"\x17"],"\x3B"=>[102,"\x17"],"\x3C"=>[113,"\x17"],"\x3D"=>[121,"\x17"],"\x3E"=>[128,"\x17"],"\x3F"=>[12,"\x17"],"\x40"=>[92,"\x18"],"A"=>[95,"\x18"],"B"=>[137,"\x18"],"C"=>[142,"\x18"],"D"=>[150,"\x18"],"E"=>[74,"\x18"],"F"=>[90,"\x18"],"G"=>[98,"\x18"],"H"=>[107,"\x18"],"I"=>[140,"\x18"],"J"=>[146,"\x18"],"K"=>[102,"\x18"],"L"=>[113,"\x18"],"M"=>[121,"\x18"],"N"=>[128,"\x18"],"O"=>[12,"\x18"],"P"=>[92,"\x19"],"Q"=>[95,"\x19"],"R"=>[137,"\x19"],"S"=>[142,"\x19"],"T"=>[150,"\x19"],"U"=>[74,"\x19"],"V"=>[90,"\x19"],"W"=>[98,"\x19"],"X"=>[107,"\x19"],"Y"=>[140,"\x19"],"Z"=>[146,"\x19"],"\x5B"=>[102,"\x19"],"\x5C"=>[113,"\x19"],"\x5D"=>[121,"\x19"],"\x5E"=>[128,"\x19"],"_"=>[12,"\x19"],"\x60"=>[92,"\x1A"],"a"=>[95,"\x1A"],"b"=>[137,"\x1A"],"c"=>[142,"\x1A"],"d"=>[150,"\x1A"],"e"=>[74,"\x1A"],"f"=>[90,"\x1A"],"g"=>[98,"\x1A"],"h"=>[107,"\x1A"],"i"=>[140,"\x1A"],"j"=>[146,"\x1A"],"k"=>[102,"\x1A"],"l"=>[113,"\x1A"],"m"=>[121,"\x1A"],"n"=>[128,"\x1A"],"o"=>[12,"\x1A"],"p"=>[92,"\x1B"],"q"=>[95,"\x1B"],"r"=>[137,"\x1B"],"s"=>[142,"\x1B"],"t"=>[150,"\x1B"],"u"=>[74,"\x1B"],"v"=>[90,"\x1B"],"w"=>[98,"\x1B"],"x"=>[107,"\x1B"],"y"=>[140,"\x1B"],"z"=>[146,"\x1B"],"\x7B"=>[102,"\x1B"],"\x7C"=>[113,"\x1B"],"\x7D"=>[121,"\x1B"],"~"=>[128,"\x1B"],"\x7F"=>[12,"\x1B"],"\x80"=>[92,"\x1C"],"\x81"=>[95,"\x1C"],"\x82"=>[137,"\x1C"],"\x83"=>[142,"\x1C"],"\x84"=>[150,"\x1C"],"\x85"=>[74,"\x1C"],"\x86"=>[90,"\x1C"],"\x87"=>[98,"\x1C"],"\x88"=>[107,"\x1C"],"\x89"=>[140,"\x1C"],"\x8A"=>[146,"\x1C"],"\x8B"=>[102,"\x1C"],"\x8C"=>[113,"\x1C"],"\x8D"=>[121,"\x1C"],"\x8E"=>[128,"\x1C"],"\x8F"=>[12,"\x1C"],"\x90"=>[92,"\x1D"],"\x91"=>[95,"\x1D"],"\x92"=>[137,"\x1D"],"\x93"=>[142,"\x1D"],"\x94"=>[150,"\x1D"],"\x95"=>[74,"\x1D"],"\x96"=>[90,"\x1D"],"\x97"=>[98,"\x1D"],"\x98"=>[107,"\x1D"],"\x99"=>[140,"\x1D"],"\x9A"=>[146,"\x1D"],"\x9B"=>[102,"\x1D"],"\x9C"=>[113,"\x1D"],"\x9D"=>[121,"\x1D"],"\x9E"=>[128,"\x1D"],"\x9F"=>[12,"\x1D"],"\xA0"=>[92,"\x1E"],"\xA1"=>[95,"\x1E"],"\xA2"=>[137,"\x1E"],"\xA3"=>[142,"\x1E"],"\xA4"=>[150,"\x1E"],"\xA5"=>[74,"\x1E"],"\xA6"=>[90,"\x1E"],"\xA7"=>[98,"\x1E"],"\xA8"=>[107,"\x1E"],"\xA9"=>[140,"\x1E"],"\xAA"=>[146,"\x1E"],"\xAB"=>[102,"\x1E"],"\xAC"=>[113,"\x1E"],"\xAD"=>[121,"\x1E"],"\xAE"=>[128,"\x1E"],"\xAF"=>[12,"\x1E"],"\xB0"=>[92,"\x1F"],"\xB1"=>[95,"\x1F"],"\xB2"=>[137,"\x1F"],"\xB3"=>[142,"\x1F"],"\xB4"=>[150,"\x1F"],"\xB5"=>[74,"\x1F"],"\xB6"=>[90,"\x1F"],"\xB7"=>[98,"\x1F"],"\xB8"=>[107,"\x1F"],"\xB9"=>[140,"\x1F"],"\xBA"=>[146,"\x1F"],"\xBB"=>[102,"\x1F"],"\xBC"=>[113,"\x1F"],"\xBD"=>[121,"\x1F"],"\xBE"=>[128,"\x1F"],"\xBF"=>[12,"\x1F"],"\xC0"=>[92,"\x7F"],"\xC1"=>[95,"\x7F"],"\xC2"=>[137,"\x7F"],"\xC3"=>[142,"\x7F"],"\xC4"=>[150,"\x7F"],"\xC5"=>[74,"\x7F"],"\xC6"=>[90,"\x7F"],"\xC7"=>[98,"\x7F"],"\xC8"=>[107,"\x7F"],"\xC9"=>[140,"\x7F"],"\xCA"=>[146,"\x7F"],"\xCB"=>[102,"\x7F"],"\xCC"=>[113,"\x7F"],"\xCD"=>[121,"\x7F"],"\xCE"=>[128,"\x7F"],"\xCF"=>[12,"\x7F"],"\xD0"=>[92,"\xDC"],"\xD1"=>[95,"\xDC"],"\xD2"=>[137,"\xDC"],"\xD3"=>[142,"\xDC"],"\xD4"=>[150,"\xDC"],"\xD5"=>[74,"\xDC"],"\xD6"=>[90,"\xDC"],"\xD7"=>[98,"\xDC"],"\xD8"=>[107,"\xDC"],"\xD9"=>[140,"\xDC"],"\xDA"=>[146,"\xDC"],"\xDB"=>[102,"\xDC"],"\xDC"=>[113,"\xDC"],"\xDD"=>[121,"\xDC"],"\xDE"=>[128,"\xDC"],"\xDF"=>[12,"\xDC"],"\xE0"=>[92,"\xF9"],"\xE1"=>[95,"\xF9"],"\xE2"=>[137,"\xF9"],"\xE3"=>[142,"\xF9"],"\xE4"=>[150,"\xF9"],"\xE5"=>[74,"\xF9"],"\xE6"=>[90,"\xF9"],"\xE7"=>[98,"\xF9"],"\xE8"=>[107,"\xF9"],"\xE9"=>[140,"\xF9"],"\xEA"=>[146,"\xF9"],"\xEB"=>[102,"\xF9"],"\xEC"=>[113,"\xF9"],"\xED"=>[121,"\xF9"],"\xEE"=>[128,"\xF9"],"\xEF"=>[12,"\xF9"],"\xF0"=>[94,"\x0A"],"\xF1"=>[76,"\x0A"],"\xF2"=>[104,"\x0A"],"\xF3"=>[16,"\x0A"],"\xF4"=>[94,"\x0D"],"\xF5"=>[76,"\x0D"],"\xF6"=>[104,"\x0D"],"\xF7"=>[16,"\x0D"],"\xF8"=>[94,"\x16"],"\xF9"=>[76,"\x16"],"\xFA"=>[104,"\x16"],"\xFB"=>[16,"\x16"],"\xFC"=>[94,""],"\xFD"=>[76,""],"\xFE"=>[104,""],"\xFF"=>[16,""],],["\x00"=>[94,"\x0A0"],"\x01"=>[76,"\x0A0"],"\x02"=>[104,"\x0A0"],"\x03"=>[16,"\x0A0"],"\x04"=>[94,"\x0A1"],"\x05"=>[76,"\x0A1"],"\x06"=>[104,"\x0A1"],"\x07"=>[16,"\x0A1"],"\x08"=>[94,"\x0A2"],"\x09"=>[76,"\x0A2"],"\x0A"=>[104,"\x0A2"],"\x0B"=>[16,"\x0A2"],"\x0C"=>[94,"\x0Aa"],"\x0D"=>[76,"\x0Aa"],"\x0E"=>[104,"\x0Aa"],"\x0F"=>[16,"\x0Aa"],"\x10"=>[94,"\x0Ac"],"\x11"=>[76,"\x0Ac"],"\x12"=>[104,"\x0Ac"],"\x13"=>[16,"\x0Ac"],"\x14"=>[94,"\x0Ae"],"\x15"=>[76,"\x0Ae"],"\x16"=>[104,"\x0Ae"],"\x17"=>[16,"\x0Ae"],"\x18"=>[94,"\x0Ai"],"\x19"=>[76,"\x0Ai"],"\x1A"=>[104,"\x0Ai"],"\x1B"=>[16,"\x0Ai"],"\x1C"=>[94,"\x0Ao"],"\x1D"=>[76,"\x0Ao"],"\x1E"=>[104,"\x0Ao"],"\x1F"=>[16,"\x0Ao"],"\x20"=>[94,"\x0As"],"\x21"=>[76,"\x0As"],"\x22"=>[104,"\x0As"],"\x23"=>[16,"\x0As"],"\x24"=>[94,"\x0At"],"\x25"=>[76,"\x0At"],"\x26"=>[104,"\x0At"],"\x27"=>[16,"\x0At"],"\x28"=>[77,"\x0A\x20"],"\x29"=>[18,"\x0A\x20"],"\x2A"=>[77,"\x0A\x25"],"\x2B"=>[18,"\x0A\x25"],"\x2C"=>[77,"\x0A-"],"-"=>[18,"\x0A-"],"."=>[77,"\x0A."],"\x2F"=>[18,"\x0A."],[77,"\x0A\x2F"],[18,"\x0A\x2F"],[77,"\x0A3"],[18,"\x0A3"],[77,"\x0A4"],[18,"\x0A4"],[77,"\x0A5"],[18,"\x0A5"],[77,"\x0A6"],[18,"\x0A6"],"\x3A"=>[77,"\x0A7"],"\x3B"=>[18,"\x0A7"],"\x3C"=>[77,"\x0A8"],"\x3D"=>[18,"\x0A8"],"\x3E"=>[77,"\x0A9"],"\x3F"=>[18,"\x0A9"],"\x40"=>[77,"\x0A\x3D"],"A"=>[18,"\x0A\x3D"],"B"=>[77,"\x0AA"],"C"=>[18,"\x0AA"],"D"=>[77,"\x0A_"],"E"=>[18,"\x0A_"],"F"=>[77,"\x0Ab"],"G"=>[18,"\x0Ab"],"H"=>[77,"\x0Ad"],"I"=>[18,"\x0Ad"],"J"=>[77,"\x0Af"],"K"=>[18,"\x0Af"],"L"=>[77,"\x0Ag"],"M"=>[18,"\x0Ag"],"N"=>[77,"\x0Ah"],"O"=>[18,"\x0Ah"],"P"=>[77,"\x0Al"],"Q"=>[18,"\x0Al"],"R"=>[77,"\x0Am"],"S"=>[18,"\x0Am"],"T"=>[77,"\x0An"],"U"=>[18,"\x0An"],"V"=>[77,"\x0Ap"],"W"=>[18,"\x0Ap"],"X"=>[77,"\x0Ar"],"Y"=>[18,"\x0Ar"],"Z"=>[77,"\x0Au"],"\x5B"=>[18,"\x0Au"],"\x5C"=>[0,"\x0A\x3A"],"\x5D"=>[0,"\x0AB"],"\x5E"=>[0,"\x0AC"],"_"=>[0,"\x0AD"],"\x60"=>[0,"\x0AE"],"a"=>[0,"\x0AF"],"b"=>[0,"\x0AG"],"c"=>[0,"\x0AH"],"d"=>[0,"\x0AI"],"e"=>[0,"\x0AJ"],"f"=>[0,"\x0AK"],"g"=>[0,"\x0AL"],"h"=>[0,"\x0AM"],"i"=>[0,"\x0AN"],"j"=>[0,"\x0AO"],"k"=>[0,"\x0AP"],"l"=>[0,"\x0AQ"],"m"=>[0,"\x0AR"],"n"=>[0,"\x0AS"],"o"=>[0,"\x0AT"],"p"=>[0,"\x0AU"],"q"=>[0,"\x0AV"],"r"=>[0,"\x0AW"],"s"=>[0,"\x0AY"],"t"=>[0,"\x0Aj"],"u"=>[0,"\x0Ak"],"v"=>[0,"\x0Aq"],"w"=>[0,"\x0Av"],"x"=>[0,"\x0Aw"],"y"=>[0,"\x0Ax"],"z"=>[0,"\x0Ay"],"\x7B"=>[0,"\x0Az"],"\x7C"=>[82,"\x0A"],"\x7D"=>[87,"\x0A"],"~"=>[130,"\x0A"],"\x7F"=>[9,"\x0A"],"\x80"=>[94,"\x0D0"],"\x81"=>[76,"\x0D0"],"\x82"=>[104,"\x0D0"],"\x83"=>[16,"\x0D0"],"\x84"=>[94,"\x0D1"],"\x85"=>[76,"\x0D1"],"\x86"=>[104,"\x0D1"],"\x87"=>[16,"\x0D1"],"\x88"=>[94,"\x0D2"],"\x89"=>[76,"\x0D2"],"\x8A"=>[104,"\x0D2"],"\x8B"=>[16,"\x0D2"],"\x8C"=>[94,"\x0Da"],"\x8D"=>[76,"\x0Da"],"\x8E"=>[104,"\x0Da"],"\x8F"=>[16,"\x0Da"],"\x90"=>[94,"\x0Dc"],"\x91"=>[76,"\x0Dc"],"\x92"=>[104,"\x0Dc"],"\x93"=>[16,"\x0Dc"],"\x94"=>[94,"\x0De"],"\x95"=>[76,"\x0De"],"\x96"=>[104,"\x0De"],"\x97"=>[16,"\x0De"],"\x98"=>[94,"\x0Di"],"\x99"=>[76,"\x0Di"],"\x9A"=>[104,"\x0Di"],"\x9B"=>[16,"\x0Di"],"\x9C"=>[94,"\x0Do"],"\x9D"=>[76,"\x0Do"],"\x9E"=>[104,"\x0Do"],"\x9F"=>[16,"\x0Do"],"\xA0"=>[94,"\x0Ds"],"\xA1"=>[76,"\x0Ds"],"\xA2"=>[104,"\x0Ds"],"\xA3"=>[16,"\x0Ds"],"\xA4"=>[94,"\x0Dt"],"\xA5"=>[76,"\x0Dt"],"\xA6"=>[104,"\x0Dt"],"\xA7"=>[16,"\x0Dt"],"\xA8"=>[77,"\x0D\x20"],"\xA9"=>[18,"\x0D\x20"],"\xAA"=>[77,"\x0D\x25"],"\xAB"=>[18,"\x0D\x25"],"\xAC"=>[77,"\x0D-"],"\xAD"=>[18,"\x0D-"],"\xAE"=>[77,"\x0D."],"\xAF"=>[18,"\x0D."],"\xB0"=>[77,"\x0D\x2F"],"\xB1"=>[18,"\x0D\x2F"],"\xB2"=>[77,"\x0D3"],"\xB3"=>[18,"\x0D3"],"\xB4"=>[77,"\x0D4"],"\xB5"=>[18,"\x0D4"],"\xB6"=>[77,"\x0D5"],"\xB7"=>[18,"\x0D5"],"\xB8"=>[77,"\x0D6"],"\xB9"=>[18,"\x0D6"],"\xBA"=>[77,"\x0D7"],"\xBB"=>[18,"\x0D7"],"\xBC"=>[77,"\x0D8"],"\xBD"=>[18,"\x0D8"],"\xBE"=>[77,"\x0D9"],"\xBF"=>[18,"\x0D9"],"\xC0"=>[77,"\x0D\x3D"],"\xC1"=>[18,"\x0D\x3D"],"\xC2"=>[77,"\x0DA"],"\xC3"=>[18,"\x0DA"],"\xC4"=>[77,"\x0D_"],"\xC5"=>[18,"\x0D_"],"\xC6"=>[77,"\x0Db"],"\xC7"=>[18,"\x0Db"],"\xC8"=>[77,"\x0Dd"],"\xC9"=>[18,"\x0Dd"],"\xCA"=>[77,"\x0Df"],"\xCB"=>[18,"\x0Df"],"\xCC"=>[77,"\x0Dg"],"\xCD"=>[18,"\x0Dg"],"\xCE"=>[77,"\x0Dh"],"\xCF"=>[18,"\x0Dh"],"\xD0"=>[77,"\x0Dl"],"\xD1"=>[18,"\x0Dl"],"\xD2"=>[77,"\x0Dm"],"\xD3"=>[18,"\x0Dm"],"\xD4"=>[77,"\x0Dn"],"\xD5"=>[18,"\x0Dn"],"\xD6"=>[77,"\x0Dp"],"\xD7"=>[18,"\x0Dp"],"\xD8"=>[77,"\x0Dr"],"\xD9"=>[18,"\x0Dr"],"\xDA"=>[77,"\x0Du"],"\xDB"=>[18,"\x0Du"],"\xDC"=>[0,"\x0D\x3A"],"\xDD"=>[0,"\x0DB"],"\xDE"=>[0,"\x0DC"],"\xDF"=>[0,"\x0DD"],"\xE0"=>[0,"\x0DE"],"\xE1"=>[0,"\x0DF"],"\xE2"=>[0,"\x0DG"],"\xE3"=>[0,"\x0DH"],"\xE4"=>[0,"\x0DI"],"\xE5"=>[0,"\x0DJ"],"\xE6"=>[0,"\x0DK"],"\xE7"=>[0,"\x0DL"],"\xE8"=>[0,"\x0DM"],"\xE9"=>[0,"\x0DN"],"\xEA"=>[0,"\x0DO"],"\xEB"=>[0,"\x0DP"],"\xEC"=>[0,"\x0DQ"],"\xED"=>[0,"\x0DR"],"\xEE"=>[0,"\x0DS"],"\xEF"=>[0,"\x0DT"],"\xF0"=>[0,"\x0DU"],"\xF1"=>[0,"\x0DV"],"\xF2"=>[0,"\x0DW"],"\xF3"=>[0,"\x0DY"],"\xF4"=>[0,"\x0Dj"],"\xF5"=>[0,"\x0Dk"],"\xF6"=>[0,"\x0Dq"],"\xF7"=>[0,"\x0Dv"],"\xF8"=>[0,"\x0Dw"],"\xF9"=>[0,"\x0Dx"],"\xFA"=>[0,"\x0Dy"],"\xFB"=>[0,"\x0Dz"],"\xFC"=>[82,"\x0D"],"\xFD"=>[87,"\x0D"],"\xFE"=>[130,"\x0D"],"\xFF"=>[9,"\x0D"],],["\x00"=>[77,"\x0A0"],"\x01"=>[18,"\x0A0"],"\x02"=>[77,"\x0A1"],"\x03"=>[18,"\x0A1"],"\x04"=>[77,"\x0A2"],"\x05"=>[18,"\x0A2"],"\x06"=>[77,"\x0Aa"],"\x07"=>[18,"\x0Aa"],"\x08"=>[77,"\x0Ac"],"\x09"=>[18,"\x0Ac"],"\x0A"=>[77,"\x0Ae"],"\x0B"=>[18,"\x0Ae"],"\x0C"=>[77,"\x0Ai"],"\x0D"=>[18,"\x0Ai"],"\x0E"=>[77,"\x0Ao"],"\x0F"=>[18,"\x0Ao"],"\x10"=>[77,"\x0As"],"\x11"=>[18,"\x0As"],"\x12"=>[77,"\x0At"],"\x13"=>[18,"\x0At"],"\x14"=>[0,"\x0A\x20"],"\x15"=>[0,"\x0A\x25"],"\x16"=>[0,"\x0A-"],"\x17"=>[0,"\x0A."],"\x18"=>[0,"\x0A\x2F"],"\x19"=>[0,"\x0A3"],"\x1A"=>[0,"\x0A4"],"\x1B"=>[0,"\x0A5"],"\x1C"=>[0,"\x0A6"],"\x1D"=>[0,"\x0A7"],"\x1E"=>[0,"\x0A8"],"\x1F"=>[0,"\x0A9"],"\x20"=>[0,"\x0A\x3D"],"\x21"=>[0,"\x0AA"],"\x22"=>[0,"\x0A_"],"\x23"=>[0,"\x0Ab"],"\x24"=>[0,"\x0Ad"],"\x25"=>[0,"\x0Af"],"\x26"=>[0,"\x0Ag"],"\x27"=>[0,"\x0Ah"],"\x28"=>[0,"\x0Al"],"\x29"=>[0,"\x0Am"],"\x2A"=>[0,"\x0An"],"\x2B"=>[0,"\x0Ap"],"\x2C"=>[0,"\x0Ar"],"-"=>[0,"\x0Au"],"."=>[100,"\x0A"],"\x2F"=>[110,"\x0A"],[111,"\x0A"],[115,"\x0A"],[116,"\x0A"],[118,"\x0A"],[119,"\x0A"],[122,"\x0A"],[123,"\x0A"],[125,"\x0A"],[126,"\x0A"],[129,"\x0A"],"\x3A"=>[143,"\x0A"],"\x3B"=>[148,"\x0A"],"\x3C"=>[151,"\x0A"],"\x3D"=>[153,"\x0A"],"\x3E"=>[83,"\x0A"],"\x3F"=>[10,"\x0A"],"\x40"=>[77,"\x0D0"],"A"=>[18,"\x0D0"],"B"=>[77,"\x0D1"],"C"=>[18,"\x0D1"],"D"=>[77,"\x0D2"],"E"=>[18,"\x0D2"],"F"=>[77,"\x0Da"],"G"=>[18,"\x0Da"],"H"=>[77,"\x0Dc"],"I"=>[18,"\x0Dc"],"J"=>[77,"\x0De"],"K"=>[18,"\x0De"],"L"=>[77,"\x0Di"],"M"=>[18,"\x0Di"],"N"=>[77,"\x0Do"],"O"=>[18,"\x0Do"],"P"=>[77,"\x0Ds"],"Q"=>[18,"\x0Ds"],"R"=>[77,"\x0Dt"],"S"=>[18,"\x0Dt"],"T"=>[0,"\x0D\x20"],"U"=>[0,"\x0D\x25"],"V"=>[0,"\x0D-"],"W"=>[0,"\x0D."],"X"=>[0,"\x0D\x2F"],"Y"=>[0,"\x0D3"],"Z"=>[0,"\x0D4"],"\x5B"=>[0,"\x0D5"],"\x5C"=>[0,"\x0D6"],"\x5D"=>[0,"\x0D7"],"\x5E"=>[0,"\x0D8"],"_"=>[0,"\x0D9"],"\x60"=>[0,"\x0D\x3D"],"a"=>[0,"\x0DA"],"b"=>[0,"\x0D_"],"c"=>[0,"\x0Db"],"d"=>[0,"\x0Dd"],"e"=>[0,"\x0Df"],"f"=>[0,"\x0Dg"],"g"=>[0,"\x0Dh"],"h"=>[0,"\x0Dl"],"i"=>[0,"\x0Dm"],"j"=>[0,"\x0Dn"],"k"=>[0,"\x0Dp"],"l"=>[0,"\x0Dr"],"m"=>[0,"\x0Du"],"n"=>[100,"\x0D"],"o"=>[110,"\x0D"],"p"=>[111,"\x0D"],"q"=>[115,"\x0D"],"r"=>[116,"\x0D"],"s"=>[118,"\x0D"],"t"=>[119,"\x0D"],"u"=>[122,"\x0D"],"v"=>[123,"\x0D"],"w"=>[125,"\x0D"],"x"=>[126,"\x0D"],"y"=>[129,"\x0D"],"z"=>[143,"\x0D"],"\x7B"=>[148,"\x0D"],"\x7C"=>[151,"\x0D"],"\x7D"=>[153,"\x0D"],"~"=>[83,"\x0D"],"\x7F"=>[10,"\x0D"],"\x80"=>[77,"\x160"],"\x81"=>[18,"\x160"],"\x82"=>[77,"\x161"],"\x83"=>[18,"\x161"],"\x84"=>[77,"\x162"],"\x85"=>[18,"\x162"],"\x86"=>[77,"\x16a"],"\x87"=>[18,"\x16a"],"\x88"=>[77,"\x16c"],"\x89"=>[18,"\x16c"],"\x8A"=>[77,"\x16e"],"\x8B"=>[18,"\x16e"],"\x8C"=>[77,"\x16i"],"\x8D"=>[18,"\x16i"],"\x8E"=>[77,"\x16o"],"\x8F"=>[18,"\x16o"],"\x90"=>[77,"\x16s"],"\x91"=>[18,"\x16s"],"\x92"=>[77,"\x16t"],"\x93"=>[18,"\x16t"],"\x94"=>[0,"\x16\x20"],"\x95"=>[0,"\x16\x25"],"\x96"=>[0,"\x16-"],"\x97"=>[0,"\x16."],"\x98"=>[0,"\x16\x2F"],"\x99"=>[0,"\x163"],"\x9A"=>[0,"\x164"],"\x9B"=>[0,"\x165"],"\x9C"=>[0,"\x166"],"\x9D"=>[0,"\x167"],"\x9E"=>[0,"\x168"],"\x9F"=>[0,"\x169"],"\xA0"=>[0,"\x16\x3D"],"\xA1"=>[0,"\x16A"],"\xA2"=>[0,"\x16_"],"\xA3"=>[0,"\x16b"],"\xA4"=>[0,"\x16d"],"\xA5"=>[0,"\x16f"],"\xA6"=>[0,"\x16g"],"\xA7"=>[0,"\x16h"],"\xA8"=>[0,"\x16l"],"\xA9"=>[0,"\x16m"],"\xAA"=>[0,"\x16n"],"\xAB"=>[0,"\x16p"],"\xAC"=>[0,"\x16r"],"\xAD"=>[0,"\x16u"],"\xAE"=>[100,"\x16"],"\xAF"=>[110,"\x16"],"\xB0"=>[111,"\x16"],"\xB1"=>[115,"\x16"],"\xB2"=>[116,"\x16"],"\xB3"=>[118,"\x16"],"\xB4"=>[119,"\x16"],"\xB5"=>[122,"\x16"],"\xB6"=>[123,"\x16"],"\xB7"=>[125,"\x16"],"\xB8"=>[126,"\x16"],"\xB9"=>[129,"\x16"],"\xBA"=>[143,"\x16"],"\xBB"=>[148,"\x16"],"\xBC"=>[151,"\x16"],"\xBD"=>[153,"\x16"],"\xBE"=>[83,"\x16"],"\xBF"=>[10,"\x16"],"\xC0"=>[77,""],"\xC1"=>[18,""],"\xC2"=>[77,""],"\xC3"=>[18,""],"\xC4"=>[77,""],"\xC5"=>[18,""],"\xC6"=>[77,""],"\xC7"=>[18,""],"\xC8"=>[77,""],"\xC9"=>[18,""],"\xCA"=>[77,""],"\xCB"=>[18,""],"\xCC"=>[77,""],"\xCD"=>[18,""],"\xCE"=>[77,""],"\xCF"=>[18,""],"\xD0"=>[77,""],"\xD1"=>[18,""],"\xD2"=>[77,""],"\xD3"=>[18,""],"\xD4"=>[0,""],"\xD5"=>[0,""],"\xD6"=>[0,""],"\xD7"=>[0,""],"\xD8"=>[0,""],"\xD9"=>[0,""],"\xDA"=>[0,""],"\xDB"=>[0,""],"\xDC"=>[0,""],"\xDD"=>[0,""],"\xDE"=>[0,""],"\xDF"=>[0,""],"\xE0"=>[0,""],"\xE1"=>[0,""],"\xE2"=>[0,""],"\xE3"=>[0,""],"\xE4"=>[0,""],"\xE5"=>[0,""],"\xE6"=>[0,""],"\xE7"=>[0,""],"\xE8"=>[0,""],"\xE9"=>[0,""],"\xEA"=>[0,""],"\xEB"=>[0,""],"\xEC"=>[0,""],"\xED"=>[0,""],"\xEE"=>[100,""],"\xEF"=>[110,""],"\xF0"=>[111,""],"\xF1"=>[115,""],"\xF2"=>[116,""],"\xF3"=>[118,""],"\xF4"=>[119,""],"\xF5"=>[122,""],"\xF6"=>[123,""],"\xF7"=>[125,""],"\xF8"=>[126,""],"\xF9"=>[129,""],"\xFA"=>[143,""],"\xFB"=>[148,""],"\xFC"=>[151,""],"\xFD"=>[153,""],"\xFE"=>[83,""],"\xFF"=>[10,""],],["\x00"=>[94,"\xF90"],"\x01"=>[76,"\xF90"],"\x02"=>[104,"\xF90"],"\x03"=>[16,"\xF90"],"\x04"=>[94,"\xF91"],"\x05"=>[76,"\xF91"],"\x06"=>[104,"\xF91"],"\x07"=>[16,"\xF91"],"\x08"=>[94,"\xF92"],"\x09"=>[76,"\xF92"],"\x0A"=>[104,"\xF92"],"\x0B"=>[16,"\xF92"],"\x0C"=>[94,"\xF9a"],"\x0D"=>[76,"\xF9a"],"\x0E"=>[104,"\xF9a"],"\x0F"=>[16,"\xF9a"],"\x10"=>[94,"\xF9c"],"\x11"=>[76,"\xF9c"],"\x12"=>[104,"\xF9c"],"\x13"=>[16,"\xF9c"],"\x14"=>[94,"\xF9e"],"\x15"=>[76,"\xF9e"],"\x16"=>[104,"\xF9e"],"\x17"=>[16,"\xF9e"],"\x18"=>[94,"\xF9i"],"\x19"=>[76,"\xF9i"],"\x1A"=>[104,"\xF9i"],"\x1B"=>[16,"\xF9i"],"\x1C"=>[94,"\xF9o"],"\x1D"=>[76,"\xF9o"],"\x1E"=>[104,"\xF9o"],"\x1F"=>[16,"\xF9o"],"\x20"=>[94,"\xF9s"],"\x21"=>[76,"\xF9s"],"\x22"=>[104,"\xF9s"],"\x23"=>[16,"\xF9s"],"\x24"=>[94,"\xF9t"],"\x25"=>[76,"\xF9t"],"\x26"=>[104,"\xF9t"],"\x27"=>[16,"\xF9t"],"\x28"=>[77,"\xF9\x20"],"\x29"=>[18,"\xF9\x20"],"\x2A"=>[77,"\xF9\x25"],"\x2B"=>[18,"\xF9\x25"],"\x2C"=>[77,"\xF9-"],"-"=>[18,"\xF9-"],"."=>[77,"\xF9."],"\x2F"=>[18,"\xF9."],[77,"\xF9\x2F"],[18,"\xF9\x2F"],[77,"\xF93"],[18,"\xF93"],[77,"\xF94"],[18,"\xF94"],[77,"\xF95"],[18,"\xF95"],[77,"\xF96"],[18,"\xF96"],"\x3A"=>[77,"\xF97"],"\x3B"=>[18,"\xF97"],"\x3C"=>[77,"\xF98"],"\x3D"=>[18,"\xF98"],"\x3E"=>[77,"\xF99"],"\x3F"=>[18,"\xF99"],"\x40"=>[77,"\xF9\x3D"],"A"=>[18,"\xF9\x3D"],"B"=>[77,"\xF9A"],"C"=>[18,"\xF9A"],"D"=>[77,"\xF9_"],"E"=>[18,"\xF9_"],"F"=>[77,"\xF9b"],"G"=>[18,"\xF9b"],"H"=>[77,"\xF9d"],"I"=>[18,"\xF9d"],"J"=>[77,"\xF9f"],"K"=>[18,"\xF9f"],"L"=>[77,"\xF9g"],"M"=>[18,"\xF9g"],"N"=>[77,"\xF9h"],"O"=>[18,"\xF9h"],"P"=>[77,"\xF9l"],"Q"=>[18,"\xF9l"],"R"=>[77,"\xF9m"],"S"=>[18,"\xF9m"],"T"=>[77,"\xF9n"],"U"=>[18,"\xF9n"],"V"=>[77,"\xF9p"],"W"=>[18,"\xF9p"],"X"=>[77,"\xF9r"],"Y"=>[18,"\xF9r"],"Z"=>[77,"\xF9u"],"\x5B"=>[18,"\xF9u"],"\x5C"=>[0,"\xF9\x3A"],"\x5D"=>[0,"\xF9B"],"\x5E"=>[0,"\xF9C"],"_"=>[0,"\xF9D"],"\x60"=>[0,"\xF9E"],"a"=>[0,"\xF9F"],"b"=>[0,"\xF9G"],"c"=>[0,"\xF9H"],"d"=>[0,"\xF9I"],"e"=>[0,"\xF9J"],"f"=>[0,"\xF9K"],"g"=>[0,"\xF9L"],"h"=>[0,"\xF9M"],"i"=>[0,"\xF9N"],"j"=>[0,"\xF9O"],"k"=>[0,"\xF9P"],"l"=>[0,"\xF9Q"],"m"=>[0,"\xF9R"],"n"=>[0,"\xF9S"],"o"=>[0,"\xF9T"],"p"=>[0,"\xF9U"],"q"=>[0,"\xF9V"],"r"=>[0,"\xF9W"],"s"=>[0,"\xF9Y"],"t"=>[0,"\xF9j"],"u"=>[0,"\xF9k"],"v"=>[0,"\xF9q"],"w"=>[0,"\xF9v"],"x"=>[0,"\xF9w"],"y"=>[0,"\xF9x"],"z"=>[0,"\xF9y"],"\x7B"=>[0,"\xF9z"],"\x7C"=>[82,"\xF9"],"\x7D"=>[87,"\xF9"],"~"=>[130,"\xF9"],"\x7F"=>[9,"\xF9"],"\x80"=>[0,"\x0A0"],"\x81"=>[0,"\x0A1"],"\x82"=>[0,"\x0A2"],"\x83"=>[0,"\x0Aa"],"\x84"=>[0,"\x0Ac"],"\x85"=>[0,"\x0Ae"],"\x86"=>[0,"\x0Ai"],"\x87"=>[0,"\x0Ao"],"\x88"=>[0,"\x0As"],"\x89"=>[0,"\x0At"],"\x8A"=>[73,"\x0A"],"\x8B"=>[88,"\x0A"],"\x8C"=>[89,"\x0A"],"\x8D"=>[96,"\x0A"],"\x8E"=>[97,"\x0A"],"\x8F"=>[99,"\x0A"],"\x90"=>[106,"\x0A"],"\x91"=>[136,"\x0A"],"\x92"=>[139,"\x0A"],"\x93"=>[141,"\x0A"],"\x94"=>[145,"\x0A"],"\x95"=>[147,"\x0A"],"\x96"=>[149,"\x0A"],"\x97"=>[101,"\x0A"],"\x98"=>[112,"\x0A"],"\x99"=>[117,"\x0A"],"\x9A"=>[120,"\x0A"],"\x9B"=>[124,"\x0A"],"\x9C"=>[127,"\x0A"],"\x9D"=>[144,"\x0A"],"\x9E"=>[152,"\x0A"],"\x9F"=>[11,"\x0A"],"\xA0"=>[0,"\x0D0"],"\xA1"=>[0,"\x0D1"],"\xA2"=>[0,"\x0D2"],"\xA3"=>[0,"\x0Da"],"\xA4"=>[0,"\x0Dc"],"\xA5"=>[0,"\x0De"],"\xA6"=>[0,"\x0Di"],"\xA7"=>[0,"\x0Do"],"\xA8"=>[0,"\x0Ds"],"\xA9"=>[0,"\x0Dt"],"\xAA"=>[73,"\x0D"],"\xAB"=>[88,"\x0D"],"\xAC"=>[89,"\x0D"],"\xAD"=>[96,"\x0D"],"\xAE"=>[97,"\x0D"],"\xAF"=>[99,"\x0D"],"\xB0"=>[106,"\x0D"],"\xB1"=>[136,"\x0D"],"\xB2"=>[139,"\x0D"],"\xB3"=>[141,"\x0D"],"\xB4"=>[145,"\x0D"],"\xB5"=>[147,"\x0D"],"\xB6"=>[149,"\x0D"],"\xB7"=>[101,"\x0D"],"\xB8"=>[112,"\x0D"],"\xB9"=>[117,"\x0D"],"\xBA"=>[120,"\x0D"],"\xBB"=>[124,"\x0D"],"\xBC"=>[127,"\x0D"],"\xBD"=>[144,"\x0D"],"\xBE"=>[152,"\x0D"],"\xBF"=>[11,"\x0D"],"\xC0"=>[0,"\x160"],"\xC1"=>[0,"\x161"],"\xC2"=>[0,"\x162"],"\xC3"=>[0,"\x16a"],"\xC4"=>[0,"\x16c"],"\xC5"=>[0,"\x16e"],"\xC6"=>[0,"\x16i"],"\xC7"=>[0,"\x16o"],"\xC8"=>[0,"\x16s"],"\xC9"=>[0,"\x16t"],"\xCA"=>[73,"\x16"],"\xCB"=>[88,"\x16"],"\xCC"=>[89,"\x16"],"\xCD"=>[96,"\x16"],"\xCE"=>[97,"\x16"],"\xCF"=>[99,"\x16"],"\xD0"=>[106,"\x16"],"\xD1"=>[136,"\x16"],"\xD2"=>[139,"\x16"],"\xD3"=>[141,"\x16"],"\xD4"=>[145,"\x16"],"\xD5"=>[147,"\x16"],"\xD6"=>[149,"\x16"],"\xD7"=>[101,"\x16"],"\xD8"=>[112,"\x16"],"\xD9"=>[117,"\x16"],"\xDA"=>[120,"\x16"],"\xDB"=>[124,"\x16"],"\xDC"=>[127,"\x16"],"\xDD"=>[144,"\x16"],"\xDE"=>[152,"\x16"],"\xDF"=>[11,"\x16"],"\xE0"=>[0,""],"\xE1"=>[0,""],"\xE2"=>[0,""],"\xE3"=>[0,""],"\xE4"=>[0,""],"\xE5"=>[0,""],"\xE6"=>[0,""],"\xE7"=>[0,""],"\xE8"=>[0,""],"\xE9"=>[0,""],"\xEA"=>[73,""],"\xEB"=>[88,""],"\xEC"=>[89,""],"\xED"=>[96,""],"\xEE"=>[97,""],"\xEF"=>[99,""],"\xF0"=>[106,""],"\xF1"=>[136,""],"\xF2"=>[139,""],"\xF3"=>[141,""],"\xF4"=>[145,""],"\xF5"=>[147,""],"\xF6"=>[149,""],"\xF7"=>[101,""],"\xF8"=>[112,""],"\xF9"=>[117,""],"\xFA"=>[120,""],"\xFB"=>[124,""],"\xFC"=>[127,""],"\xFD"=>[144,""],"\xFE"=>[152,""],"\xFF"=>[11,""],],["\x00"=>[77,"\x7F0"],"\x01"=>[18,"\x7F0"],"\x02"=>[77,"\x7F1"],"\x03"=>[18,"\x7F1"],"\x04"=>[77,"\x7F2"],"\x05"=>[18,"\x7F2"],"\x06"=>[77,"\x7Fa"],"\x07"=>[18,"\x7Fa"],"\x08"=>[77,"\x7Fc"],"\x09"=>[18,"\x7Fc"],"\x0A"=>[77,"\x7Fe"],"\x0B"=>[18,"\x7Fe"],"\x0C"=>[77,"\x7Fi"],"\x0D"=>[18,"\x7Fi"],"\x0E"=>[77,"\x7Fo"],"\x0F"=>[18,"\x7Fo"],"\x10"=>[77,"\x7Fs"],"\x11"=>[18,"\x7Fs"],"\x12"=>[77,"\x7Ft"],"\x13"=>[18,"\x7Ft"],"\x14"=>[0,"\x7F\x20"],"\x15"=>[0,"\x7F\x25"],"\x16"=>[0,"\x7F-"],"\x17"=>[0,"\x7F."],"\x18"=>[0,"\x7F\x2F"],"\x19"=>[0,"\x7F3"],"\x1A"=>[0,"\x7F4"],"\x1B"=>[0,"\x7F5"],"\x1C"=>[0,"\x7F6"],"\x1D"=>[0,"\x7F7"],"\x1E"=>[0,"\x7F8"],"\x1F"=>[0,"\x7F9"],"\x20"=>[0,"\x7F\x3D"],"\x21"=>[0,"\x7FA"],"\x22"=>[0,"\x7F_"],"\x23"=>[0,"\x7Fb"],"\x24"=>[0,"\x7Fd"],"\x25"=>[0,"\x7Ff"],"\x26"=>[0,"\x7Fg"],"\x27"=>[0,"\x7Fh"],"\x28"=>[0,"\x7Fl"],"\x29"=>[0,"\x7Fm"],"\x2A"=>[0,"\x7Fn"],"\x2B"=>[0,"\x7Fp"],"\x2C"=>[0,"\x7Fr"],"-"=>[0,"\x7Fu"],"."=>[100,"\x7F"],"\x2F"=>[110,"\x7F"],[111,"\x7F"],[115,"\x7F"],[116,"\x7F"],[118,"\x7F"],[119,"\x7F"],[122,"\x7F"],[123,"\x7F"],[125,"\x7F"],[126,"\x7F"],[129,"\x7F"],"\x3A"=>[143,"\x7F"],"\x3B"=>[148,"\x7F"],"\x3C"=>[151,"\x7F"],"\x3D"=>[153,"\x7F"],"\x3E"=>[83,"\x7F"],"\x3F"=>[10,"\x7F"],"\x40"=>[77,"\xDC0"],"A"=>[18,"\xDC0"],"B"=>[77,"\xDC1"],"C"=>[18,"\xDC1"],"D"=>[77,"\xDC2"],"E"=>[18,"\xDC2"],"F"=>[77,"\xDCa"],"G"=>[18,"\xDCa"],"H"=>[77,"\xDCc"],"I"=>[18,"\xDCc"],"J"=>[77,"\xDCe"],"K"=>[18,"\xDCe"],"L"=>[77,"\xDCi"],"M"=>[18,"\xDCi"],"N"=>[77,"\xDCo"],"O"=>[18,"\xDCo"],"P"=>[77,"\xDCs"],"Q"=>[18,"\xDCs"],"R"=>[77,"\xDCt"],"S"=>[18,"\xDCt"],"T"=>[0,"\xDC\x20"],"U"=>[0,"\xDC\x25"],"V"=>[0,"\xDC-"],"W"=>[0,"\xDC."],"X"=>[0,"\xDC\x2F"],"Y"=>[0,"\xDC3"],"Z"=>[0,"\xDC4"],"\x5B"=>[0,"\xDC5"],"\x5C"=>[0,"\xDC6"],"\x5D"=>[0,"\xDC7"],"\x5E"=>[0,"\xDC8"],"_"=>[0,"\xDC9"],"\x60"=>[0,"\xDC\x3D"],"a"=>[0,"\xDCA"],"b"=>[0,"\xDC_"],"c"=>[0,"\xDCb"],"d"=>[0,"\xDCd"],"e"=>[0,"\xDCf"],"f"=>[0,"\xDCg"],"g"=>[0,"\xDCh"],"h"=>[0,"\xDCl"],"i"=>[0,"\xDCm"],"j"=>[0,"\xDCn"],"k"=>[0,"\xDCp"],"l"=>[0,"\xDCr"],"m"=>[0,"\xDCu"],"n"=>[100,"\xDC"],"o"=>[110,"\xDC"],"p"=>[111,"\xDC"],"q"=>[115,"\xDC"],"r"=>[116,"\xDC"],"s"=>[118,"\xDC"],"t"=>[119,"\xDC"],"u"=>[122,"\xDC"],"v"=>[123,"\xDC"],"w"=>[125,"\xDC"],"x"=>[126,"\xDC"],"y"=>[129,"\xDC"],"z"=>[143,"\xDC"],"\x7B"=>[148,"\xDC"],"\x7C"=>[151,"\xDC"],"\x7D"=>[153,"\xDC"],"~"=>[83,"\xDC"],"\x7F"=>[10,"\xDC"],"\x80"=>[77,"\xF90"],"\x81"=>[18,"\xF90"],"\x82"=>[77,"\xF91"],"\x83"=>[18,"\xF91"],"\x84"=>[77,"\xF92"],"\x85"=>[18,"\xF92"],"\x86"=>[77,"\xF9a"],"\x87"=>[18,"\xF9a"],"\x88"=>[77,"\xF9c"],"\x89"=>[18,"\xF9c"],"\x8A"=>[77,"\xF9e"],"\x8B"=>[18,"\xF9e"],"\x8C"=>[77,"\xF9i"],"\x8D"=>[18,"\xF9i"],"\x8E"=>[77,"\xF9o"],"\x8F"=>[18,"\xF9o"],"\x90"=>[77,"\xF9s"],"\x91"=>[18,"\xF9s"],"\x92"=>[77,"\xF9t"],"\x93"=>[18,"\xF9t"],"\x94"=>[0,"\xF9\x20"],"\x95"=>[0,"\xF9\x25"],"\x96"=>[0,"\xF9-"],"\x97"=>[0,"\xF9."],"\x98"=>[0,"\xF9\x2F"],"\x99"=>[0,"\xF93"],"\x9A"=>[0,"\xF94"],"\x9B"=>[0,"\xF95"],"\x9C"=>[0,"\xF96"],"\x9D"=>[0,"\xF97"],"\x9E"=>[0,"\xF98"],"\x9F"=>[0,"\xF99"],"\xA0"=>[0,"\xF9\x3D"],"\xA1"=>[0,"\xF9A"],"\xA2"=>[0,"\xF9_"],"\xA3"=>[0,"\xF9b"],"\xA4"=>[0,"\xF9d"],"\xA5"=>[0,"\xF9f"],"\xA6"=>[0,"\xF9g"],"\xA7"=>[0,"\xF9h"],"\xA8"=>[0,"\xF9l"],"\xA9"=>[0,"\xF9m"],"\xAA"=>[0,"\xF9n"],"\xAB"=>[0,"\xF9p"],"\xAC"=>[0,"\xF9r"],"\xAD"=>[0,"\xF9u"],"\xAE"=>[100,"\xF9"],"\xAF"=>[110,"\xF9"],"\xB0"=>[111,"\xF9"],"\xB1"=>[115,"\xF9"],"\xB2"=>[116,"\xF9"],"\xB3"=>[118,"\xF9"],"\xB4"=>[119,"\xF9"],"\xB5"=>[122,"\xF9"],"\xB6"=>[123,"\xF9"],"\xB7"=>[125,"\xF9"],"\xB8"=>[126,"\xF9"],"\xB9"=>[129,"\xF9"],"\xBA"=>[143,"\xF9"],"\xBB"=>[148,"\xF9"],"\xBC"=>[151,"\xF9"],"\xBD"=>[153,"\xF9"],"\xBE"=>[83,"\xF9"],"\xBF"=>[10,"\xF9"],"\xC0"=>[92,"\x0A"],"\xC1"=>[95,"\x0A"],"\xC2"=>[137,"\x0A"],"\xC3"=>[142,"\x0A"],"\xC4"=>[150,"\x0A"],"\xC5"=>[74,"\x0A"],"\xC6"=>[90,"\x0A"],"\xC7"=>[98,"\x0A"],"\xC8"=>[107,"\x0A"],"\xC9"=>[140,"\x0A"],"\xCA"=>[146,"\x0A"],"\xCB"=>[102,"\x0A"],"\xCC"=>[113,"\x0A"],"\xCD"=>[121,"\x0A"],"\xCE"=>[128,"\x0A"],"\xCF"=>[12,"\x0A"],"\xD0"=>[92,"\x0D"],"\xD1"=>[95,"\x0D"],"\xD2"=>[137,"\x0D"],"\xD3"=>[142,"\x0D"],"\xD4"=>[150,"\x0D"],"\xD5"=>[74,"\x0D"],"\xD6"=>[90,"\x0D"],"\xD7"=>[98,"\x0D"],"\xD8"=>[107,"\x0D"],"\xD9"=>[140,"\x0D"],"\xDA"=>[146,"\x0D"],"\xDB"=>[102,"\x0D"],"\xDC"=>[113,"\x0D"],"\xDD"=>[121,"\x0D"],"\xDE"=>[128,"\x0D"],"\xDF"=>[12,"\x0D"],"\xE0"=>[92,"\x16"],"\xE1"=>[95,"\x16"],"\xE2"=>[137,"\x16"],"\xE3"=>[142,"\x16"],"\xE4"=>[150,"\x16"],"\xE5"=>[74,"\x16"],"\xE6"=>[90,"\x16"],"\xE7"=>[98,"\x16"],"\xE8"=>[107,"\x16"],"\xE9"=>[140,"\x16"],"\xEA"=>[146,"\x16"],"\xEB"=>[102,"\x16"],"\xEC"=>[113,"\x16"],"\xED"=>[121,"\x16"],"\xEE"=>[128,"\x16"],"\xEF"=>[12,"\x16"],"\xF0"=>[92,""],"\xF1"=>[95,""],"\xF2"=>[137,""],"\xF3"=>[142,""],"\xF4"=>[150,""],"\xF5"=>[74,""],"\xF6"=>[90,""],"\xF7"=>[98,""],"\xF8"=>[107,""],"\xF9"=>[140,""],"\xFA"=>[146,""],"\xFB"=>[102,""],"\xFC"=>[113,""],"\xFD"=>[121,""],"\xFE"=>[128,""],"\xFF"=>[12,""],],["\x00"=>[0,"\x1C0"],"\x01"=>[0,"\x1C1"],"\x02"=>[0,"\x1C2"],"\x03"=>[0,"\x1Ca"],"\x04"=>[0,"\x1Cc"],"\x05"=>[0,"\x1Ce"],"\x06"=>[0,"\x1Ci"],"\x07"=>[0,"\x1Co"],"\x08"=>[0,"\x1Cs"],"\x09"=>[0,"\x1Ct"],"\x0A"=>[73,"\x1C"],"\x0B"=>[88,"\x1C"],"\x0C"=>[89,"\x1C"],"\x0D"=>[96,"\x1C"],"\x0E"=>[97,"\x1C"],"\x0F"=>[99,"\x1C"],"\x10"=>[106,"\x1C"],"\x11"=>[136,"\x1C"],"\x12"=>[139,"\x1C"],"\x13"=>[141,"\x1C"],"\x14"=>[145,"\x1C"],"\x15"=>[147,"\x1C"],"\x16"=>[149,"\x1C"],"\x17"=>[101,"\x1C"],"\x18"=>[112,"\x1C"],"\x19"=>[117,"\x1C"],"\x1A"=>[120,"\x1C"],"\x1B"=>[124,"\x1C"],"\x1C"=>[127,"\x1C"],"\x1D"=>[144,"\x1C"],"\x1E"=>[152,"\x1C"],"\x1F"=>[11,"\x1C"],"\x20"=>[0,"\x1D0"],"\x21"=>[0,"\x1D1"],"\x22"=>[0,"\x1D2"],"\x23"=>[0,"\x1Da"],"\x24"=>[0,"\x1Dc"],"\x25"=>[0,"\x1De"],"\x26"=>[0,"\x1Di"],"\x27"=>[0,"\x1Do"],"\x28"=>[0,"\x1Ds"],"\x29"=>[0,"\x1Dt"],"\x2A"=>[73,"\x1D"],"\x2B"=>[88,"\x1D"],"\x2C"=>[89,"\x1D"],"-"=>[96,"\x1D"],"."=>[97,"\x1D"],"\x2F"=>[99,"\x1D"],[106,"\x1D"],[136,"\x1D"],[139,"\x1D"],[141,"\x1D"],[145,"\x1D"],[147,"\x1D"],[149,"\x1D"],[101,"\x1D"],[112,"\x1D"],[117,"\x1D"],"\x3A"=>[120,"\x1D"],"\x3B"=>[124,"\x1D"],"\x3C"=>[127,"\x1D"],"\x3D"=>[144,"\x1D"],"\x3E"=>[152,"\x1D"],"\x3F"=>[11,"\x1D"],"\x40"=>[0,"\x1E0"],"A"=>[0,"\x1E1"],"B"=>[0,"\x1E2"],"C"=>[0,"\x1Ea"],"D"=>[0,"\x1Ec"],"E"=>[0,"\x1Ee"],"F"=>[0,"\x1Ei"],"G"=>[0,"\x1Eo"],"H"=>[0,"\x1Es"],"I"=>[0,"\x1Et"],"J"=>[73,"\x1E"],"K"=>[88,"\x1E"],"L"=>[89,"\x1E"],"M"=>[96,"\x1E"],"N"=>[97,"\x1E"],"O"=>[99,"\x1E"],"P"=>[106,"\x1E"],"Q"=>[136,"\x1E"],"R"=>[139,"\x1E"],"S"=>[141,"\x1E"],"T"=>[145,"\x1E"],"U"=>[147,"\x1E"],"V"=>[149,"\x1E"],"W"=>[101,"\x1E"],"X"=>[112,"\x1E"],"Y"=>[117,"\x1E"],"Z"=>[120,"\x1E"],"\x5B"=>[124,"\x1E"],"\x5C"=>[127,"\x1E"],"\x5D"=>[144,"\x1E"],"\x5E"=>[152,"\x1E"],"_"=>[11,"\x1E"],"\x60"=>[0,"\x1F0"],"a"=>[0,"\x1F1"],"b"=>[0,"\x1F2"],"c"=>[0,"\x1Fa"],"d"=>[0,"\x1Fc"],"e"=>[0,"\x1Fe"],"f"=>[0,"\x1Fi"],"g"=>[0,"\x1Fo"],"h"=>[0,"\x1Fs"],"i"=>[0,"\x1Ft"],"j"=>[73,"\x1F"],"k"=>[88,"\x1F"],"l"=>[89,"\x1F"],"m"=>[96,"\x1F"],"n"=>[97,"\x1F"],"o"=>[99,"\x1F"],"p"=>[106,"\x1F"],"q"=>[136,"\x1F"],"r"=>[139,"\x1F"],"s"=>[141,"\x1F"],"t"=>[145,"\x1F"],"u"=>[147,"\x1F"],"v"=>[149,"\x1F"],"w"=>[101,"\x1F"],"x"=>[112,"\x1F"],"y"=>[117,"\x1F"],"z"=>[120,"\x1F"],"\x7B"=>[124,"\x1F"],"\x7C"=>[127,"\x1F"],"\x7D"=>[144,"\x1F"],"~"=>[152,"\x1F"],"\x7F"=>[11,"\x1F"],"\x80"=>[0,"\x7F0"],"\x81"=>[0,"\x7F1"],"\x82"=>[0,"\x7F2"],"\x83"=>[0,"\x7Fa"],"\x84"=>[0,"\x7Fc"],"\x85"=>[0,"\x7Fe"],"\x86"=>[0,"\x7Fi"],"\x87"=>[0,"\x7Fo"],"\x88"=>[0,"\x7Fs"],"\x89"=>[0,"\x7Ft"],"\x8A"=>[73,"\x7F"],"\x8B"=>[88,"\x7F"],"\x8C"=>[89,"\x7F"],"\x8D"=>[96,"\x7F"],"\x8E"=>[97,"\x7F"],"\x8F"=>[99,"\x7F"],"\x90"=>[106,"\x7F"],"\x91"=>[136,"\x7F"],"\x92"=>[139,"\x7F"],"\x93"=>[141,"\x7F"],"\x94"=>[145,"\x7F"],"\x95"=>[147,"\x7F"],"\x96"=>[149,"\x7F"],"\x97"=>[101,"\x7F"],"\x98"=>[112,"\x7F"],"\x99"=>[117,"\x7F"],"\x9A"=>[120,"\x7F"],"\x9B"=>[124,"\x7F"],"\x9C"=>[127,"\x7F"],"\x9D"=>[144,"\x7F"],"\x9E"=>[152,"\x7F"],"\x9F"=>[11,"\x7F"],"\xA0"=>[0,"\xDC0"],"\xA1"=>[0,"\xDC1"],"\xA2"=>[0,"\xDC2"],"\xA3"=>[0,"\xDCa"],"\xA4"=>[0,"\xDCc"],"\xA5"=>[0,"\xDCe"],"\xA6"=>[0,"\xDCi"],"\xA7"=>[0,"\xDCo"],"\xA8"=>[0,"\xDCs"],"\xA9"=>[0,"\xDCt"],"\xAA"=>[73,"\xDC"],"\xAB"=>[88,"\xDC"],"\xAC"=>[89,"\xDC"],"\xAD"=>[96,"\xDC"],"\xAE"=>[97,"\xDC"],"\xAF"=>[99,"\xDC"],"\xB0"=>[106,"\xDC"],"\xB1"=>[136,"\xDC"],"\xB2"=>[139,"\xDC"],"\xB3"=>[141,"\xDC"],"\xB4"=>[145,"\xDC"],"\xB5"=>[147,"\xDC"],"\xB6"=>[149,"\xDC"],"\xB7"=>[101,"\xDC"],"\xB8"=>[112,"\xDC"],"\xB9"=>[117,"\xDC"],"\xBA"=>[120,"\xDC"],"\xBB"=>[124,"\xDC"],"\xBC"=>[127,"\xDC"],"\xBD"=>[144,"\xDC"],"\xBE"=>[152,"\xDC"],"\xBF"=>[11,"\xDC"],"\xC0"=>[0,"\xF90"],"\xC1"=>[0,"\xF91"],"\xC2"=>[0,"\xF92"],"\xC3"=>[0,"\xF9a"],"\xC4"=>[0,"\xF9c"],"\xC5"=>[0,"\xF9e"],"\xC6"=>[0,"\xF9i"],"\xC7"=>[0,"\xF9o"],"\xC8"=>[0,"\xF9s"],"\xC9"=>[0,"\xF9t"],"\xCA"=>[73,"\xF9"],"\xCB"=>[88,"\xF9"],"\xCC"=>[89,"\xF9"],"\xCD"=>[96,"\xF9"],"\xCE"=>[97,"\xF9"],"\xCF"=>[99,"\xF9"],"\xD0"=>[106,"\xF9"],"\xD1"=>[136,"\xF9"],"\xD2"=>[139,"\xF9"],"\xD3"=>[141,"\xF9"],"\xD4"=>[145,"\xF9"],"\xD5"=>[147,"\xF9"],"\xD6"=>[149,"\xF9"],"\xD7"=>[101,"\xF9"],"\xD8"=>[112,"\xF9"],"\xD9"=>[117,"\xF9"],"\xDA"=>[120,"\xF9"],"\xDB"=>[124,"\xF9"],"\xDC"=>[127,"\xF9"],"\xDD"=>[144,"\xF9"],"\xDE"=>[152,"\xF9"],"\xDF"=>[11,"\xF9"],"\xE0"=>[93,"\x0A"],"\xE1"=>[138,"\x0A"],"\xE2"=>[75,"\x0A"],"\xE3"=>[91,"\x0A"],"\xE4"=>[108,"\x0A"],"\xE5"=>[103,"\x0A"],"\xE6"=>[114,"\x0A"],"\xE7"=>[14,"\x0A"],"\xE8"=>[93,"\x0D"],"\xE9"=>[138,"\x0D"],"\xEA"=>[75,"\x0D"],"\xEB"=>[91,"\x0D"],"\xEC"=>[108,"\x0D"],"\xED"=>[103,"\x0D"],"\xEE"=>[114,"\x0D"],"\xEF"=>[14,"\x0D"],"\xF0"=>[93,"\x16"],"\xF1"=>[138,"\x16"],"\xF2"=>[75,"\x16"],"\xF3"=>[91,"\x16"],"\xF4"=>[108,"\x16"],"\xF5"=>[103,"\x16"],"\xF6"=>[114,"\x16"],"\xF7"=>[14,"\x16"],"\xF8"=>[93,""],"\xF9"=>[138,""],"\xFA"=>[75,""],"\xFB"=>[91,""],"\xFC"=>[108,""],"\xFD"=>[103,""],"\xFE"=>[114,""],"\xFF"=>[14,""],],["\x00"=>[94,"\x0C0"],"\x01"=>[76,"\x0C0"],"\x02"=>[104,"\x0C0"],"\x03"=>[16,"\x0C0"],"\x04"=>[94,"\x0C1"],"\x05"=>[76,"\x0C1"],"\x06"=>[104,"\x0C1"],"\x07"=>[16,"\x0C1"],"\x08"=>[94,"\x0C2"],"\x09"=>[76,"\x0C2"],"\x0A"=>[104,"\x0C2"],"\x0B"=>[16,"\x0C2"],"\x0C"=>[94,"\x0Ca"],"\x0D"=>[76,"\x0Ca"],"\x0E"=>[104,"\x0Ca"],"\x0F"=>[16,"\x0Ca"],"\x10"=>[94,"\x0Cc"],"\x11"=>[76,"\x0Cc"],"\x12"=>[104,"\x0Cc"],"\x13"=>[16,"\x0Cc"],"\x14"=>[94,"\x0Ce"],"\x15"=>[76,"\x0Ce"],"\x16"=>[104,"\x0Ce"],"\x17"=>[16,"\x0Ce"],"\x18"=>[94,"\x0Ci"],"\x19"=>[76,"\x0Ci"],"\x1A"=>[104,"\x0Ci"],"\x1B"=>[16,"\x0Ci"],"\x1C"=>[94,"\x0Co"],"\x1D"=>[76,"\x0Co"],"\x1E"=>[104,"\x0Co"],"\x1F"=>[16,"\x0Co"],"\x20"=>[94,"\x0Cs"],"\x21"=>[76,"\x0Cs"],"\x22"=>[104,"\x0Cs"],"\x23"=>[16,"\x0Cs"],"\x24"=>[94,"\x0Ct"],"\x25"=>[76,"\x0Ct"],"\x26"=>[104,"\x0Ct"],"\x27"=>[16,"\x0Ct"],"\x28"=>[77,"\x0C\x20"],"\x29"=>[18,"\x0C\x20"],"\x2A"=>[77,"\x0C\x25"],"\x2B"=>[18,"\x0C\x25"],"\x2C"=>[77,"\x0C-"],"-"=>[18,"\x0C-"],"."=>[77,"\x0C."],"\x2F"=>[18,"\x0C."],[77,"\x0C\x2F"],[18,"\x0C\x2F"],[77,"\x0C3"],[18,"\x0C3"],[77,"\x0C4"],[18,"\x0C4"],[77,"\x0C5"],[18,"\x0C5"],[77,"\x0C6"],[18,"\x0C6"],"\x3A"=>[77,"\x0C7"],"\x3B"=>[18,"\x0C7"],"\x3C"=>[77,"\x0C8"],"\x3D"=>[18,"\x0C8"],"\x3E"=>[77,"\x0C9"],"\x3F"=>[18,"\x0C9"],"\x40"=>[77,"\x0C\x3D"],"A"=>[18,"\x0C\x3D"],"B"=>[77,"\x0CA"],"C"=>[18,"\x0CA"],"D"=>[77,"\x0C_"],"E"=>[18,"\x0C_"],"F"=>[77,"\x0Cb"],"G"=>[18,"\x0Cb"],"H"=>[77,"\x0Cd"],"I"=>[18,"\x0Cd"],"J"=>[77,"\x0Cf"],"K"=>[18,"\x0Cf"],"L"=>[77,"\x0Cg"],"M"=>[18,"\x0Cg"],"N"=>[77,"\x0Ch"],"O"=>[18,"\x0Ch"],"P"=>[77,"\x0Cl"],"Q"=>[18,"\x0Cl"],"R"=>[77,"\x0Cm"],"S"=>[18,"\x0Cm"],"T"=>[77,"\x0Cn"],"U"=>[18,"\x0Cn"],"V"=>[77,"\x0Cp"],"W"=>[18,"\x0Cp"],"X"=>[77,"\x0Cr"],"Y"=>[18,"\x0Cr"],"Z"=>[77,"\x0Cu"],"\x5B"=>[18,"\x0Cu"],"\x5C"=>[0,"\x0C\x3A"],"\x5D"=>[0,"\x0CB"],"\x5E"=>[0,"\x0CC"],"_"=>[0,"\x0CD"],"\x60"=>[0,"\x0CE"],"a"=>[0,"\x0CF"],"b"=>[0,"\x0CG"],"c"=>[0,"\x0CH"],"d"=>[0,"\x0CI"],"e"=>[0,"\x0CJ"],"f"=>[0,"\x0CK"],"g"=>[0,"\x0CL"],"h"=>[0,"\x0CM"],"i"=>[0,"\x0CN"],"j"=>[0,"\x0CO"],"k"=>[0,"\x0CP"],"l"=>[0,"\x0CQ"],"m"=>[0,"\x0CR"],"n"=>[0,"\x0CS"],"o"=>[0,"\x0CT"],"p"=>[0,"\x0CU"],"q"=>[0,"\x0CV"],"r"=>[0,"\x0CW"],"s"=>[0,"\x0CY"],"t"=>[0,"\x0Cj"],"u"=>[0,"\x0Ck"],"v"=>[0,"\x0Cq"],"w"=>[0,"\x0Cv"],"x"=>[0,"\x0Cw"],"y"=>[0,"\x0Cx"],"z"=>[0,"\x0Cy"],"\x7B"=>[0,"\x0Cz"],"\x7C"=>[82,"\x0C"],"\x7D"=>[87,"\x0C"],"~"=>[130,"\x0C"],"\x7F"=>[9,"\x0C"],"\x80"=>[94,"\x0E0"],"\x81"=>[76,"\x0E0"],"\x82"=>[104,"\x0E0"],"\x83"=>[16,"\x0E0"],"\x84"=>[94,"\x0E1"],"\x85"=>[76,"\x0E1"],"\x86"=>[104,"\x0E1"],"\x87"=>[16,"\x0E1"],"\x88"=>[94,"\x0E2"],"\x89"=>[76,"\x0E2"],"\x8A"=>[104,"\x0E2"],"\x8B"=>[16,"\x0E2"],"\x8C"=>[94,"\x0Ea"],"\x8D"=>[76,"\x0Ea"],"\x8E"=>[104,"\x0Ea"],"\x8F"=>[16,"\x0Ea"],"\x90"=>[94,"\x0Ec"],"\x91"=>[76,"\x0Ec"],"\x92"=>[104,"\x0Ec"],"\x93"=>[16,"\x0Ec"],"\x94"=>[94,"\x0Ee"],"\x95"=>[76,"\x0Ee"],"\x96"=>[104,"\x0Ee"],"\x97"=>[16,"\x0Ee"],"\x98"=>[94,"\x0Ei"],"\x99"=>[76,"\x0Ei"],"\x9A"=>[104,"\x0Ei"],"\x9B"=>[16,"\x0Ei"],"\x9C"=>[94,"\x0Eo"],"\x9D"=>[76,"\x0Eo"],"\x9E"=>[104,"\x0Eo"],"\x9F"=>[16,"\x0Eo"],"\xA0"=>[94,"\x0Es"],"\xA1"=>[76,"\x0Es"],"\xA2"=>[104,"\x0Es"],"\xA3"=>[16,"\x0Es"],"\xA4"=>[94,"\x0Et"],"\xA5"=>[76,"\x0Et"],"\xA6"=>[104,"\x0Et"],"\xA7"=>[16,"\x0Et"],"\xA8"=>[77,"\x0E\x20"],"\xA9"=>[18,"\x0E\x20"],"\xAA"=>[77,"\x0E\x25"],"\xAB"=>[18,"\x0E\x25"],"\xAC"=>[77,"\x0E-"],"\xAD"=>[18,"\x0E-"],"\xAE"=>[77,"\x0E."],"\xAF"=>[18,"\x0E."],"\xB0"=>[77,"\x0E\x2F"],"\xB1"=>[18,"\x0E\x2F"],"\xB2"=>[77,"\x0E3"],"\xB3"=>[18,"\x0E3"],"\xB4"=>[77,"\x0E4"],"\xB5"=>[18,"\x0E4"],"\xB6"=>[77,"\x0E5"],"\xB7"=>[18,"\x0E5"],"\xB8"=>[77,"\x0E6"],"\xB9"=>[18,"\x0E6"],"\xBA"=>[77,"\x0E7"],"\xBB"=>[18,"\x0E7"],"\xBC"=>[77,"\x0E8"],"\xBD"=>[18,"\x0E8"],"\xBE"=>[77,"\x0E9"],"\xBF"=>[18,"\x0E9"],"\xC0"=>[77,"\x0E\x3D"],"\xC1"=>[18,"\x0E\x3D"],"\xC2"=>[77,"\x0EA"],"\xC3"=>[18,"\x0EA"],"\xC4"=>[77,"\x0E_"],"\xC5"=>[18,"\x0E_"],"\xC6"=>[77,"\x0Eb"],"\xC7"=>[18,"\x0Eb"],"\xC8"=>[77,"\x0Ed"],"\xC9"=>[18,"\x0Ed"],"\xCA"=>[77,"\x0Ef"],"\xCB"=>[18,"\x0Ef"],"\xCC"=>[77,"\x0Eg"],"\xCD"=>[18,"\x0Eg"],"\xCE"=>[77,"\x0Eh"],"\xCF"=>[18,"\x0Eh"],"\xD0"=>[77,"\x0El"],"\xD1"=>[18,"\x0El"],"\xD2"=>[77,"\x0Em"],"\xD3"=>[18,"\x0Em"],"\xD4"=>[77,"\x0En"],"\xD5"=>[18,"\x0En"],"\xD6"=>[77,"\x0Ep"],"\xD7"=>[18,"\x0Ep"],"\xD8"=>[77,"\x0Er"],"\xD9"=>[18,"\x0Er"],"\xDA"=>[77,"\x0Eu"],"\xDB"=>[18,"\x0Eu"],"\xDC"=>[0,"\x0E\x3A"],"\xDD"=>[0,"\x0EB"],"\xDE"=>[0,"\x0EC"],"\xDF"=>[0,"\x0ED"],"\xE0"=>[0,"\x0EE"],"\xE1"=>[0,"\x0EF"],"\xE2"=>[0,"\x0EG"],"\xE3"=>[0,"\x0EH"],"\xE4"=>[0,"\x0EI"],"\xE5"=>[0,"\x0EJ"],"\xE6"=>[0,"\x0EK"],"\xE7"=>[0,"\x0EL"],"\xE8"=>[0,"\x0EM"],"\xE9"=>[0,"\x0EN"],"\xEA"=>[0,"\x0EO"],"\xEB"=>[0,"\x0EP"],"\xEC"=>[0,"\x0EQ"],"\xED"=>[0,"\x0ER"],"\xEE"=>[0,"\x0ES"],"\xEF"=>[0,"\x0ET"],"\xF0"=>[0,"\x0EU"],"\xF1"=>[0,"\x0EV"],"\xF2"=>[0,"\x0EW"],"\xF3"=>[0,"\x0EY"],"\xF4"=>[0,"\x0Ej"],"\xF5"=>[0,"\x0Ek"],"\xF6"=>[0,"\x0Eq"],"\xF7"=>[0,"\x0Ev"],"\xF8"=>[0,"\x0Ew"],"\xF9"=>[0,"\x0Ex"],"\xFA"=>[0,"\x0Ey"],"\xFB"=>[0,"\x0Ez"],"\xFC"=>[82,"\x0E"],"\xFD"=>[87,"\x0E"],"\xFE"=>[130,"\x0E"],"\xFF"=>[9,"\x0E"],],["\x00"=>[94,"\x0F0"],"\x01"=>[76,"\x0F0"],"\x02"=>[104,"\x0F0"],"\x03"=>[16,"\x0F0"],"\x04"=>[94,"\x0F1"],"\x05"=>[76,"\x0F1"],"\x06"=>[104,"\x0F1"],"\x07"=>[16,"\x0F1"],"\x08"=>[94,"\x0F2"],"\x09"=>[76,"\x0F2"],"\x0A"=>[104,"\x0F2"],"\x0B"=>[16,"\x0F2"],"\x0C"=>[94,"\x0Fa"],"\x0D"=>[76,"\x0Fa"],"\x0E"=>[104,"\x0Fa"],"\x0F"=>[16,"\x0Fa"],"\x10"=>[94,"\x0Fc"],"\x11"=>[76,"\x0Fc"],"\x12"=>[104,"\x0Fc"],"\x13"=>[16,"\x0Fc"],"\x14"=>[94,"\x0Fe"],"\x15"=>[76,"\x0Fe"],"\x16"=>[104,"\x0Fe"],"\x17"=>[16,"\x0Fe"],"\x18"=>[94,"\x0Fi"],"\x19"=>[76,"\x0Fi"],"\x1A"=>[104,"\x0Fi"],"\x1B"=>[16,"\x0Fi"],"\x1C"=>[94,"\x0Fo"],"\x1D"=>[76,"\x0Fo"],"\x1E"=>[104,"\x0Fo"],"\x1F"=>[16,"\x0Fo"],"\x20"=>[94,"\x0Fs"],"\x21"=>[76,"\x0Fs"],"\x22"=>[104,"\x0Fs"],"\x23"=>[16,"\x0Fs"],"\x24"=>[94,"\x0Ft"],"\x25"=>[76,"\x0Ft"],"\x26"=>[104,"\x0Ft"],"\x27"=>[16,"\x0Ft"],"\x28"=>[77,"\x0F\x20"],"\x29"=>[18,"\x0F\x20"],"\x2A"=>[77,"\x0F\x25"],"\x2B"=>[18,"\x0F\x25"],"\x2C"=>[77,"\x0F-"],"-"=>[18,"\x0F-"],"."=>[77,"\x0F."],"\x2F"=>[18,"\x0F."],[77,"\x0F\x2F"],[18,"\x0F\x2F"],[77,"\x0F3"],[18,"\x0F3"],[77,"\x0F4"],[18,"\x0F4"],[77,"\x0F5"],[18,"\x0F5"],[77,"\x0F6"],[18,"\x0F6"],"\x3A"=>[77,"\x0F7"],"\x3B"=>[18,"\x0F7"],"\x3C"=>[77,"\x0F8"],"\x3D"=>[18,"\x0F8"],"\x3E"=>[77,"\x0F9"],"\x3F"=>[18,"\x0F9"],"\x40"=>[77,"\x0F\x3D"],"A"=>[18,"\x0F\x3D"],"B"=>[77,"\x0FA"],"C"=>[18,"\x0FA"],"D"=>[77,"\x0F_"],"E"=>[18,"\x0F_"],"F"=>[77,"\x0Fb"],"G"=>[18,"\x0Fb"],"H"=>[77,"\x0Fd"],"I"=>[18,"\x0Fd"],"J"=>[77,"\x0Ff"],"K"=>[18,"\x0Ff"],"L"=>[77,"\x0Fg"],"M"=>[18,"\x0Fg"],"N"=>[77,"\x0Fh"],"O"=>[18,"\x0Fh"],"P"=>[77,"\x0Fl"],"Q"=>[18,"\x0Fl"],"R"=>[77,"\x0Fm"],"S"=>[18,"\x0Fm"],"T"=>[77,"\x0Fn"],"U"=>[18,"\x0Fn"],"V"=>[77,"\x0Fp"],"W"=>[18,"\x0Fp"],"X"=>[77,"\x0Fr"],"Y"=>[18,"\x0Fr"],"Z"=>[77,"\x0Fu"],"\x5B"=>[18,"\x0Fu"],"\x5C"=>[0,"\x0F\x3A"],"\x5D"=>[0,"\x0FB"],"\x5E"=>[0,"\x0FC"],"_"=>[0,"\x0FD"],"\x60"=>[0,"\x0FE"],"a"=>[0,"\x0FF"],"b"=>[0,"\x0FG"],"c"=>[0,"\x0FH"],"d"=>[0,"\x0FI"],"e"=>[0,"\x0FJ"],"f"=>[0,"\x0FK"],"g"=>[0,"\x0FL"],"h"=>[0,"\x0FM"],"i"=>[0,"\x0FN"],"j"=>[0,"\x0FO"],"k"=>[0,"\x0FP"],"l"=>[0,"\x0FQ"],"m"=>[0,"\x0FR"],"n"=>[0,"\x0FS"],"o"=>[0,"\x0FT"],"p"=>[0,"\x0FU"],"q"=>[0,"\x0FV"],"r"=>[0,"\x0FW"],"s"=>[0,"\x0FY"],"t"=>[0,"\x0Fj"],"u"=>[0,"\x0Fk"],"v"=>[0,"\x0Fq"],"w"=>[0,"\x0Fv"],"x"=>[0,"\x0Fw"],"y"=>[0,"\x0Fx"],"z"=>[0,"\x0Fy"],"\x7B"=>[0,"\x0Fz"],"\x7C"=>[82,"\x0F"],"\x7D"=>[87,"\x0F"],"~"=>[130,"\x0F"],"\x7F"=>[9,"\x0F"],"\x80"=>[94,"\x100"],"\x81"=>[76,"\x100"],"\x82"=>[104,"\x100"],"\x83"=>[16,"\x100"],"\x84"=>[94,"\x101"],"\x85"=>[76,"\x101"],"\x86"=>[104,"\x101"],"\x87"=>[16,"\x101"],"\x88"=>[94,"\x102"],"\x89"=>[76,"\x102"],"\x8A"=>[104,"\x102"],"\x8B"=>[16,"\x102"],"\x8C"=>[94,"\x10a"],"\x8D"=>[76,"\x10a"],"\x8E"=>[104,"\x10a"],"\x8F"=>[16,"\x10a"],"\x90"=>[94,"\x10c"],"\x91"=>[76,"\x10c"],"\x92"=>[104,"\x10c"],"\x93"=>[16,"\x10c"],"\x94"=>[94,"\x10e"],"\x95"=>[76,"\x10e"],"\x96"=>[104,"\x10e"],"\x97"=>[16,"\x10e"],"\x98"=>[94,"\x10i"],"\x99"=>[76,"\x10i"],"\x9A"=>[104,"\x10i"],"\x9B"=>[16,"\x10i"],"\x9C"=>[94,"\x10o"],"\x9D"=>[76,"\x10o"],"\x9E"=>[104,"\x10o"],"\x9F"=>[16,"\x10o"],"\xA0"=>[94,"\x10s"],"\xA1"=>[76,"\x10s"],"\xA2"=>[104,"\x10s"],"\xA3"=>[16,"\x10s"],"\xA4"=>[94,"\x10t"],"\xA5"=>[76,"\x10t"],"\xA6"=>[104,"\x10t"],"\xA7"=>[16,"\x10t"],"\xA8"=>[77,"\x10\x20"],"\xA9"=>[18,"\x10\x20"],"\xAA"=>[77,"\x10\x25"],"\xAB"=>[18,"\x10\x25"],"\xAC"=>[77,"\x10-"],"\xAD"=>[18,"\x10-"],"\xAE"=>[77,"\x10."],"\xAF"=>[18,"\x10."],"\xB0"=>[77,"\x10\x2F"],"\xB1"=>[18,"\x10\x2F"],"\xB2"=>[77,"\x103"],"\xB3"=>[18,"\x103"],"\xB4"=>[77,"\x104"],"\xB5"=>[18,"\x104"],"\xB6"=>[77,"\x105"],"\xB7"=>[18,"\x105"],"\xB8"=>[77,"\x106"],"\xB9"=>[18,"\x106"],"\xBA"=>[77,"\x107"],"\xBB"=>[18,"\x107"],"\xBC"=>[77,"\x108"],"\xBD"=>[18,"\x108"],"\xBE"=>[77,"\x109"],"\xBF"=>[18,"\x109"],"\xC0"=>[77,"\x10\x3D"],"\xC1"=>[18,"\x10\x3D"],"\xC2"=>[77,"\x10A"],"\xC3"=>[18,"\x10A"],"\xC4"=>[77,"\x10_"],"\xC5"=>[18,"\x10_"],"\xC6"=>[77,"\x10b"],"\xC7"=>[18,"\x10b"],"\xC8"=>[77,"\x10d"],"\xC9"=>[18,"\x10d"],"\xCA"=>[77,"\x10f"],"\xCB"=>[18,"\x10f"],"\xCC"=>[77,"\x10g"],"\xCD"=>[18,"\x10g"],"\xCE"=>[77,"\x10h"],"\xCF"=>[18,"\x10h"],"\xD0"=>[77,"\x10l"],"\xD1"=>[18,"\x10l"],"\xD2"=>[77,"\x10m"],"\xD3"=>[18,"\x10m"],"\xD4"=>[77,"\x10n"],"\xD5"=>[18,"\x10n"],"\xD6"=>[77,"\x10p"],"\xD7"=>[18,"\x10p"],"\xD8"=>[77,"\x10r"],"\xD9"=>[18,"\x10r"],"\xDA"=>[77,"\x10u"],"\xDB"=>[18,"\x10u"],"\xDC"=>[0,"\x10\x3A"],"\xDD"=>[0,"\x10B"],"\xDE"=>[0,"\x10C"],"\xDF"=>[0,"\x10D"],"\xE0"=>[0,"\x10E"],"\xE1"=>[0,"\x10F"],"\xE2"=>[0,"\x10G"],"\xE3"=>[0,"\x10H"],"\xE4"=>[0,"\x10I"],"\xE5"=>[0,"\x10J"],"\xE6"=>[0,"\x10K"],"\xE7"=>[0,"\x10L"],"\xE8"=>[0,"\x10M"],"\xE9"=>[0,"\x10N"],"\xEA"=>[0,"\x10O"],"\xEB"=>[0,"\x10P"],"\xEC"=>[0,"\x10Q"],"\xED"=>[0,"\x10R"],"\xEE"=>[0,"\x10S"],"\xEF"=>[0,"\x10T"],"\xF0"=>[0,"\x10U"],"\xF1"=>[0,"\x10V"],"\xF2"=>[0,"\x10W"],"\xF3"=>[0,"\x10Y"],"\xF4"=>[0,"\x10j"],"\xF5"=>[0,"\x10k"],"\xF6"=>[0,"\x10q"],"\xF7"=>[0,"\x10v"],"\xF8"=>[0,"\x10w"],"\xF9"=>[0,"\x10x"],"\xFA"=>[0,"\x10y"],"\xFB"=>[0,"\x10z"],"\xFC"=>[82,"\x10"],"\xFD"=>[87,"\x10"],"\xFE"=>[130,"\x10"],"\xFF"=>[9,"\x10"],],["\x00"=>[77,"\x0F0"],"\x01"=>[18,"\x0F0"],"\x02"=>[77,"\x0F1"],"\x03"=>[18,"\x0F1"],"\x04"=>[77,"\x0F2"],"\x05"=>[18,"\x0F2"],"\x06"=>[77,"\x0Fa"],"\x07"=>[18,"\x0Fa"],"\x08"=>[77,"\x0Fc"],"\x09"=>[18,"\x0Fc"],"\x0A"=>[77,"\x0Fe"],"\x0B"=>[18,"\x0Fe"],"\x0C"=>[77,"\x0Fi"],"\x0D"=>[18,"\x0Fi"],"\x0E"=>[77,"\x0Fo"],"\x0F"=>[18,"\x0Fo"],"\x10"=>[77,"\x0Fs"],"\x11"=>[18,"\x0Fs"],"\x12"=>[77,"\x0Ft"],"\x13"=>[18,"\x0Ft"],"\x14"=>[0,"\x0F\x20"],"\x15"=>[0,"\x0F\x25"],"\x16"=>[0,"\x0F-"],"\x17"=>[0,"\x0F."],"\x18"=>[0,"\x0F\x2F"],"\x19"=>[0,"\x0F3"],"\x1A"=>[0,"\x0F4"],"\x1B"=>[0,"\x0F5"],"\x1C"=>[0,"\x0F6"],"\x1D"=>[0,"\x0F7"],"\x1E"=>[0,"\x0F8"],"\x1F"=>[0,"\x0F9"],"\x20"=>[0,"\x0F\x3D"],"\x21"=>[0,"\x0FA"],"\x22"=>[0,"\x0F_"],"\x23"=>[0,"\x0Fb"],"\x24"=>[0,"\x0Fd"],"\x25"=>[0,"\x0Ff"],"\x26"=>[0,"\x0Fg"],"\x27"=>[0,"\x0Fh"],"\x28"=>[0,"\x0Fl"],"\x29"=>[0,"\x0Fm"],"\x2A"=>[0,"\x0Fn"],"\x2B"=>[0,"\x0Fp"],"\x2C"=>[0,"\x0Fr"],"-"=>[0,"\x0Fu"],"."=>[100,"\x0F"],"\x2F"=>[110,"\x0F"],[111,"\x0F"],[115,"\x0F"],[116,"\x0F"],[118,"\x0F"],[119,"\x0F"],[122,"\x0F"],[123,"\x0F"],[125,"\x0F"],[126,"\x0F"],[129,"\x0F"],"\x3A"=>[143,"\x0F"],"\x3B"=>[148,"\x0F"],"\x3C"=>[151,"\x0F"],"\x3D"=>[153,"\x0F"],"\x3E"=>[83,"\x0F"],"\x3F"=>[10,"\x0F"],"\x40"=>[77,"\x100"],"A"=>[18,"\x100"],"B"=>[77,"\x101"],"C"=>[18,"\x101"],"D"=>[77,"\x102"],"E"=>[18,"\x102"],"F"=>[77,"\x10a"],"G"=>[18,"\x10a"],"H"=>[77,"\x10c"],"I"=>[18,"\x10c"],"J"=>[77,"\x10e"],"K"=>[18,"\x10e"],"L"=>[77,"\x10i"],"M"=>[18,"\x10i"],"N"=>[77,"\x10o"],"O"=>[18,"\x10o"],"P"=>[77,"\x10s"],"Q"=>[18,"\x10s"],"R"=>[77,"\x10t"],"S"=>[18,"\x10t"],"T"=>[0,"\x10\x20"],"U"=>[0,"\x10\x25"],"V"=>[0,"\x10-"],"W"=>[0,"\x10."],"X"=>[0,"\x10\x2F"],"Y"=>[0,"\x103"],"Z"=>[0,"\x104"],"\x5B"=>[0,"\x105"],"\x5C"=>[0,"\x106"],"\x5D"=>[0,"\x107"],"\x5E"=>[0,"\x108"],"_"=>[0,"\x109"],"\x60"=>[0,"\x10\x3D"],"a"=>[0,"\x10A"],"b"=>[0,"\x10_"],"c"=>[0,"\x10b"],"d"=>[0,"\x10d"],"e"=>[0,"\x10f"],"f"=>[0,"\x10g"],"g"=>[0,"\x10h"],"h"=>[0,"\x10l"],"i"=>[0,"\x10m"],"j"=>[0,"\x10n"],"k"=>[0,"\x10p"],"l"=>[0,"\x10r"],"m"=>[0,"\x10u"],"n"=>[100,"\x10"],"o"=>[110,"\x10"],"p"=>[111,"\x10"],"q"=>[115,"\x10"],"r"=>[116,"\x10"],"s"=>[118,"\x10"],"t"=>[119,"\x10"],"u"=>[122,"\x10"],"v"=>[123,"\x10"],"w"=>[125,"\x10"],"x"=>[126,"\x10"],"y"=>[129,"\x10"],"z"=>[143,"\x10"],"\x7B"=>[148,"\x10"],"\x7C"=>[151,"\x10"],"\x7D"=>[153,"\x10"],"~"=>[83,"\x10"],"\x7F"=>[10,"\x10"],"\x80"=>[77,"\x110"],"\x81"=>[18,"\x110"],"\x82"=>[77,"\x111"],"\x83"=>[18,"\x111"],"\x84"=>[77,"\x112"],"\x85"=>[18,"\x112"],"\x86"=>[77,"\x11a"],"\x87"=>[18,"\x11a"],"\x88"=>[77,"\x11c"],"\x89"=>[18,"\x11c"],"\x8A"=>[77,"\x11e"],"\x8B"=>[18,"\x11e"],"\x8C"=>[77,"\x11i"],"\x8D"=>[18,"\x11i"],"\x8E"=>[77,"\x11o"],"\x8F"=>[18,"\x11o"],"\x90"=>[77,"\x11s"],"\x91"=>[18,"\x11s"],"\x92"=>[77,"\x11t"],"\x93"=>[18,"\x11t"],"\x94"=>[0,"\x11\x20"],"\x95"=>[0,"\x11\x25"],"\x96"=>[0,"\x11-"],"\x97"=>[0,"\x11."],"\x98"=>[0,"\x11\x2F"],"\x99"=>[0,"\x113"],"\x9A"=>[0,"\x114"],"\x9B"=>[0,"\x115"],"\x9C"=>[0,"\x116"],"\x9D"=>[0,"\x117"],"\x9E"=>[0,"\x118"],"\x9F"=>[0,"\x119"],"\xA0"=>[0,"\x11\x3D"],"\xA1"=>[0,"\x11A"],"\xA2"=>[0,"\x11_"],"\xA3"=>[0,"\x11b"],"\xA4"=>[0,"\x11d"],"\xA5"=>[0,"\x11f"],"\xA6"=>[0,"\x11g"],"\xA7"=>[0,"\x11h"],"\xA8"=>[0,"\x11l"],"\xA9"=>[0,"\x11m"],"\xAA"=>[0,"\x11n"],"\xAB"=>[0,"\x11p"],"\xAC"=>[0,"\x11r"],"\xAD"=>[0,"\x11u"],"\xAE"=>[100,"\x11"],"\xAF"=>[110,"\x11"],"\xB0"=>[111,"\x11"],"\xB1"=>[115,"\x11"],"\xB2"=>[116,"\x11"],"\xB3"=>[118,"\x11"],"\xB4"=>[119,"\x11"],"\xB5"=>[122,"\x11"],"\xB6"=>[123,"\x11"],"\xB7"=>[125,"\x11"],"\xB8"=>[126,"\x11"],"\xB9"=>[129,"\x11"],"\xBA"=>[143,"\x11"],"\xBB"=>[148,"\x11"],"\xBC"=>[151,"\x11"],"\xBD"=>[153,"\x11"],"\xBE"=>[83,"\x11"],"\xBF"=>[10,"\x11"],"\xC0"=>[77,"\x120"],"\xC1"=>[18,"\x120"],"\xC2"=>[77,"\x121"],"\xC3"=>[18,"\x121"],"\xC4"=>[77,"\x122"],"\xC5"=>[18,"\x122"],"\xC6"=>[77,"\x12a"],"\xC7"=>[18,"\x12a"],"\xC8"=>[77,"\x12c"],"\xC9"=>[18,"\x12c"],"\xCA"=>[77,"\x12e"],"\xCB"=>[18,"\x12e"],"\xCC"=>[77,"\x12i"],"\xCD"=>[18,"\x12i"],"\xCE"=>[77,"\x12o"],"\xCF"=>[18,"\x12o"],"\xD0"=>[77,"\x12s"],"\xD1"=>[18,"\x12s"],"\xD2"=>[77,"\x12t"],"\xD3"=>[18,"\x12t"],"\xD4"=>[0,"\x12\x20"],"\xD5"=>[0,"\x12\x25"],"\xD6"=>[0,"\x12-"],"\xD7"=>[0,"\x12."],"\xD8"=>[0,"\x12\x2F"],"\xD9"=>[0,"\x123"],"\xDA"=>[0,"\x124"],"\xDB"=>[0,"\x125"],"\xDC"=>[0,"\x126"],"\xDD"=>[0,"\x127"],"\xDE"=>[0,"\x128"],"\xDF"=>[0,"\x129"],"\xE0"=>[0,"\x12\x3D"],"\xE1"=>[0,"\x12A"],"\xE2"=>[0,"\x12_"],"\xE3"=>[0,"\x12b"],"\xE4"=>[0,"\x12d"],"\xE5"=>[0,"\x12f"],"\xE6"=>[0,"\x12g"],"\xE7"=>[0,"\x12h"],"\xE8"=>[0,"\x12l"],"\xE9"=>[0,"\x12m"],"\xEA"=>[0,"\x12n"],"\xEB"=>[0,"\x12p"],"\xEC"=>[0,"\x12r"],"\xED"=>[0,"\x12u"],"\xEE"=>[100,"\x12"],"\xEF"=>[110,"\x12"],"\xF0"=>[111,"\x12"],"\xF1"=>[115,"\x12"],"\xF2"=>[116,"\x12"],"\xF3"=>[118,"\x12"],"\xF4"=>[119,"\x12"],"\xF5"=>[122,"\x12"],"\xF6"=>[123,"\x12"],"\xF7"=>[125,"\x12"],"\xF8"=>[126,"\x12"],"\xF9"=>[129,"\x12"],"\xFA"=>[143,"\x12"],"\xFB"=>[148,"\x12"],"\xFC"=>[151,"\x12"],"\xFD"=>[153,"\x12"],"\xFE"=>[83,"\x12"],"\xFF"=>[10,"\x12"],],["\x00"=>[94,"\x110"],"\x01"=>[76,"\x110"],"\x02"=>[104,"\x110"],"\x03"=>[16,"\x110"],"\x04"=>[94,"\x111"],"\x05"=>[76,"\x111"],"\x06"=>[104,"\x111"],"\x07"=>[16,"\x111"],"\x08"=>[94,"\x112"],"\x09"=>[76,"\x112"],"\x0A"=>[104,"\x112"],"\x0B"=>[16,"\x112"],"\x0C"=>[94,"\x11a"],"\x0D"=>[76,"\x11a"],"\x0E"=>[104,"\x11a"],"\x0F"=>[16,"\x11a"],"\x10"=>[94,"\x11c"],"\x11"=>[76,"\x11c"],"\x12"=>[104,"\x11c"],"\x13"=>[16,"\x11c"],"\x14"=>[94,"\x11e"],"\x15"=>[76,"\x11e"],"\x16"=>[104,"\x11e"],"\x17"=>[16,"\x11e"],"\x18"=>[94,"\x11i"],"\x19"=>[76,"\x11i"],"\x1A"=>[104,"\x11i"],"\x1B"=>[16,"\x11i"],"\x1C"=>[94,"\x11o"],"\x1D"=>[76,"\x11o"],"\x1E"=>[104,"\x11o"],"\x1F"=>[16,"\x11o"],"\x20"=>[94,"\x11s"],"\x21"=>[76,"\x11s"],"\x22"=>[104,"\x11s"],"\x23"=>[16,"\x11s"],"\x24"=>[94,"\x11t"],"\x25"=>[76,"\x11t"],"\x26"=>[104,"\x11t"],"\x27"=>[16,"\x11t"],"\x28"=>[77,"\x11\x20"],"\x29"=>[18,"\x11\x20"],"\x2A"=>[77,"\x11\x25"],"\x2B"=>[18,"\x11\x25"],"\x2C"=>[77,"\x11-"],"-"=>[18,"\x11-"],"."=>[77,"\x11."],"\x2F"=>[18,"\x11."],[77,"\x11\x2F"],[18,"\x11\x2F"],[77,"\x113"],[18,"\x113"],[77,"\x114"],[18,"\x114"],[77,"\x115"],[18,"\x115"],[77,"\x116"],[18,"\x116"],"\x3A"=>[77,"\x117"],"\x3B"=>[18,"\x117"],"\x3C"=>[77,"\x118"],"\x3D"=>[18,"\x118"],"\x3E"=>[77,"\x119"],"\x3F"=>[18,"\x119"],"\x40"=>[77,"\x11\x3D"],"A"=>[18,"\x11\x3D"],"B"=>[77,"\x11A"],"C"=>[18,"\x11A"],"D"=>[77,"\x11_"],"E"=>[18,"\x11_"],"F"=>[77,"\x11b"],"G"=>[18,"\x11b"],"H"=>[77,"\x11d"],"I"=>[18,"\x11d"],"J"=>[77,"\x11f"],"K"=>[18,"\x11f"],"L"=>[77,"\x11g"],"M"=>[18,"\x11g"],"N"=>[77,"\x11h"],"O"=>[18,"\x11h"],"P"=>[77,"\x11l"],"Q"=>[18,"\x11l"],"R"=>[77,"\x11m"],"S"=>[18,"\x11m"],"T"=>[77,"\x11n"],"U"=>[18,"\x11n"],"V"=>[77,"\x11p"],"W"=>[18,"\x11p"],"X"=>[77,"\x11r"],"Y"=>[18,"\x11r"],"Z"=>[77,"\x11u"],"\x5B"=>[18,"\x11u"],"\x5C"=>[0,"\x11\x3A"],"\x5D"=>[0,"\x11B"],"\x5E"=>[0,"\x11C"],"_"=>[0,"\x11D"],"\x60"=>[0,"\x11E"],"a"=>[0,"\x11F"],"b"=>[0,"\x11G"],"c"=>[0,"\x11H"],"d"=>[0,"\x11I"],"e"=>[0,"\x11J"],"f"=>[0,"\x11K"],"g"=>[0,"\x11L"],"h"=>[0,"\x11M"],"i"=>[0,"\x11N"],"j"=>[0,"\x11O"],"k"=>[0,"\x11P"],"l"=>[0,"\x11Q"],"m"=>[0,"\x11R"],"n"=>[0,"\x11S"],"o"=>[0,"\x11T"],"p"=>[0,"\x11U"],"q"=>[0,"\x11V"],"r"=>[0,"\x11W"],"s"=>[0,"\x11Y"],"t"=>[0,"\x11j"],"u"=>[0,"\x11k"],"v"=>[0,"\x11q"],"w"=>[0,"\x11v"],"x"=>[0,"\x11w"],"y"=>[0,"\x11x"],"z"=>[0,"\x11y"],"\x7B"=>[0,"\x11z"],"\x7C"=>[82,"\x11"],"\x7D"=>[87,"\x11"],"~"=>[130,"\x11"],"\x7F"=>[9,"\x11"],"\x80"=>[94,"\x120"],"\x81"=>[76,"\x120"],"\x82"=>[104,"\x120"],"\x83"=>[16,"\x120"],"\x84"=>[94,"\x121"],"\x85"=>[76,"\x121"],"\x86"=>[104,"\x121"],"\x87"=>[16,"\x121"],"\x88"=>[94,"\x122"],"\x89"=>[76,"\x122"],"\x8A"=>[104,"\x122"],"\x8B"=>[16,"\x122"],"\x8C"=>[94,"\x12a"],"\x8D"=>[76,"\x12a"],"\x8E"=>[104,"\x12a"],"\x8F"=>[16,"\x12a"],"\x90"=>[94,"\x12c"],"\x91"=>[76,"\x12c"],"\x92"=>[104,"\x12c"],"\x93"=>[16,"\x12c"],"\x94"=>[94,"\x12e"],"\x95"=>[76,"\x12e"],"\x96"=>[104,"\x12e"],"\x97"=>[16,"\x12e"],"\x98"=>[94,"\x12i"],"\x99"=>[76,"\x12i"],"\x9A"=>[104,"\x12i"],"\x9B"=>[16,"\x12i"],"\x9C"=>[94,"\x12o"],"\x9D"=>[76,"\x12o"],"\x9E"=>[104,"\x12o"],"\x9F"=>[16,"\x12o"],"\xA0"=>[94,"\x12s"],"\xA1"=>[76,"\x12s"],"\xA2"=>[104,"\x12s"],"\xA3"=>[16,"\x12s"],"\xA4"=>[94,"\x12t"],"\xA5"=>[76,"\x12t"],"\xA6"=>[104,"\x12t"],"\xA7"=>[16,"\x12t"],"\xA8"=>[77,"\x12\x20"],"\xA9"=>[18,"\x12\x20"],"\xAA"=>[77,"\x12\x25"],"\xAB"=>[18,"\x12\x25"],"\xAC"=>[77,"\x12-"],"\xAD"=>[18,"\x12-"],"\xAE"=>[77,"\x12."],"\xAF"=>[18,"\x12."],"\xB0"=>[77,"\x12\x2F"],"\xB1"=>[18,"\x12\x2F"],"\xB2"=>[77,"\x123"],"\xB3"=>[18,"\x123"],"\xB4"=>[77,"\x124"],"\xB5"=>[18,"\x124"],"\xB6"=>[77,"\x125"],"\xB7"=>[18,"\x125"],"\xB8"=>[77,"\x126"],"\xB9"=>[18,"\x126"],"\xBA"=>[77,"\x127"],"\xBB"=>[18,"\x127"],"\xBC"=>[77,"\x128"],"\xBD"=>[18,"\x128"],"\xBE"=>[77,"\x129"],"\xBF"=>[18,"\x129"],"\xC0"=>[77,"\x12\x3D"],"\xC1"=>[18,"\x12\x3D"],"\xC2"=>[77,"\x12A"],"\xC3"=>[18,"\x12A"],"\xC4"=>[77,"\x12_"],"\xC5"=>[18,"\x12_"],"\xC6"=>[77,"\x12b"],"\xC7"=>[18,"\x12b"],"\xC8"=>[77,"\x12d"],"\xC9"=>[18,"\x12d"],"\xCA"=>[77,"\x12f"],"\xCB"=>[18,"\x12f"],"\xCC"=>[77,"\x12g"],"\xCD"=>[18,"\x12g"],"\xCE"=>[77,"\x12h"],"\xCF"=>[18,"\x12h"],"\xD0"=>[77,"\x12l"],"\xD1"=>[18,"\x12l"],"\xD2"=>[77,"\x12m"],"\xD3"=>[18,"\x12m"],"\xD4"=>[77,"\x12n"],"\xD5"=>[18,"\x12n"],"\xD6"=>[77,"\x12p"],"\xD7"=>[18,"\x12p"],"\xD8"=>[77,"\x12r"],"\xD9"=>[18,"\x12r"],"\xDA"=>[77,"\x12u"],"\xDB"=>[18,"\x12u"],"\xDC"=>[0,"\x12\x3A"],"\xDD"=>[0,"\x12B"],"\xDE"=>[0,"\x12C"],"\xDF"=>[0,"\x12D"],"\xE0"=>[0,"\x12E"],"\xE1"=>[0,"\x12F"],"\xE2"=>[0,"\x12G"],"\xE3"=>[0,"\x12H"],"\xE4"=>[0,"\x12I"],"\xE5"=>[0,"\x12J"],"\xE6"=>[0,"\x12K"],"\xE7"=>[0,"\x12L"],"\xE8"=>[0,"\x12M"],"\xE9"=>[0,"\x12N"],"\xEA"=>[0,"\x12O"],"\xEB"=>[0,"\x12P"],"\xEC"=>[0,"\x12Q"],"\xED"=>[0,"\x12R"],"\xEE"=>[0,"\x12S"],"\xEF"=>[0,"\x12T"],"\xF0"=>[0,"\x12U"],"\xF1"=>[0,"\x12V"],"\xF2"=>[0,"\x12W"],"\xF3"=>[0,"\x12Y"],"\xF4"=>[0,"\x12j"],"\xF5"=>[0,"\x12k"],"\xF6"=>[0,"\x12q"],"\xF7"=>[0,"\x12v"],"\xF8"=>[0,"\x12w"],"\xF9"=>[0,"\x12x"],"\xFA"=>[0,"\x12y"],"\xFB"=>[0,"\x12z"],"\xFC"=>[82,"\x12"],"\xFD"=>[87,"\x12"],"\xFE"=>[130,"\x12"],"\xFF"=>[9,"\x12"],],["\x00"=>[94,"\x130"],"\x01"=>[76,"\x130"],"\x02"=>[104,"\x130"],"\x03"=>[16,"\x130"],"\x04"=>[94,"\x131"],"\x05"=>[76,"\x131"],"\x06"=>[104,"\x131"],"\x07"=>[16,"\x131"],"\x08"=>[94,"\x132"],"\x09"=>[76,"\x132"],"\x0A"=>[104,"\x132"],"\x0B"=>[16,"\x132"],"\x0C"=>[94,"\x13a"],"\x0D"=>[76,"\x13a"],"\x0E"=>[104,"\x13a"],"\x0F"=>[16,"\x13a"],"\x10"=>[94,"\x13c"],"\x11"=>[76,"\x13c"],"\x12"=>[104,"\x13c"],"\x13"=>[16,"\x13c"],"\x14"=>[94,"\x13e"],"\x15"=>[76,"\x13e"],"\x16"=>[104,"\x13e"],"\x17"=>[16,"\x13e"],"\x18"=>[94,"\x13i"],"\x19"=>[76,"\x13i"],"\x1A"=>[104,"\x13i"],"\x1B"=>[16,"\x13i"],"\x1C"=>[94,"\x13o"],"\x1D"=>[76,"\x13o"],"\x1E"=>[104,"\x13o"],"\x1F"=>[16,"\x13o"],"\x20"=>[94,"\x13s"],"\x21"=>[76,"\x13s"],"\x22"=>[104,"\x13s"],"\x23"=>[16,"\x13s"],"\x24"=>[94,"\x13t"],"\x25"=>[76,"\x13t"],"\x26"=>[104,"\x13t"],"\x27"=>[16,"\x13t"],"\x28"=>[77,"\x13\x20"],"\x29"=>[18,"\x13\x20"],"\x2A"=>[77,"\x13\x25"],"\x2B"=>[18,"\x13\x25"],"\x2C"=>[77,"\x13-"],"-"=>[18,"\x13-"],"."=>[77,"\x13."],"\x2F"=>[18,"\x13."],[77,"\x13\x2F"],[18,"\x13\x2F"],[77,"\x133"],[18,"\x133"],[77,"\x134"],[18,"\x134"],[77,"\x135"],[18,"\x135"],[77,"\x136"],[18,"\x136"],"\x3A"=>[77,"\x137"],"\x3B"=>[18,"\x137"],"\x3C"=>[77,"\x138"],"\x3D"=>[18,"\x138"],"\x3E"=>[77,"\x139"],"\x3F"=>[18,"\x139"],"\x40"=>[77,"\x13\x3D"],"A"=>[18,"\x13\x3D"],"B"=>[77,"\x13A"],"C"=>[18,"\x13A"],"D"=>[77,"\x13_"],"E"=>[18,"\x13_"],"F"=>[77,"\x13b"],"G"=>[18,"\x13b"],"H"=>[77,"\x13d"],"I"=>[18,"\x13d"],"J"=>[77,"\x13f"],"K"=>[18,"\x13f"],"L"=>[77,"\x13g"],"M"=>[18,"\x13g"],"N"=>[77,"\x13h"],"O"=>[18,"\x13h"],"P"=>[77,"\x13l"],"Q"=>[18,"\x13l"],"R"=>[77,"\x13m"],"S"=>[18,"\x13m"],"T"=>[77,"\x13n"],"U"=>[18,"\x13n"],"V"=>[77,"\x13p"],"W"=>[18,"\x13p"],"X"=>[77,"\x13r"],"Y"=>[18,"\x13r"],"Z"=>[77,"\x13u"],"\x5B"=>[18,"\x13u"],"\x5C"=>[0,"\x13\x3A"],"\x5D"=>[0,"\x13B"],"\x5E"=>[0,"\x13C"],"_"=>[0,"\x13D"],"\x60"=>[0,"\x13E"],"a"=>[0,"\x13F"],"b"=>[0,"\x13G"],"c"=>[0,"\x13H"],"d"=>[0,"\x13I"],"e"=>[0,"\x13J"],"f"=>[0,"\x13K"],"g"=>[0,"\x13L"],"h"=>[0,"\x13M"],"i"=>[0,"\x13N"],"j"=>[0,"\x13O"],"k"=>[0,"\x13P"],"l"=>[0,"\x13Q"],"m"=>[0,"\x13R"],"n"=>[0,"\x13S"],"o"=>[0,"\x13T"],"p"=>[0,"\x13U"],"q"=>[0,"\x13V"],"r"=>[0,"\x13W"],"s"=>[0,"\x13Y"],"t"=>[0,"\x13j"],"u"=>[0,"\x13k"],"v"=>[0,"\x13q"],"w"=>[0,"\x13v"],"x"=>[0,"\x13w"],"y"=>[0,"\x13x"],"z"=>[0,"\x13y"],"\x7B"=>[0,"\x13z"],"\x7C"=>[82,"\x13"],"\x7D"=>[87,"\x13"],"~"=>[130,"\x13"],"\x7F"=>[9,"\x13"],"\x80"=>[94,"\x140"],"\x81"=>[76,"\x140"],"\x82"=>[104,"\x140"],"\x83"=>[16,"\x140"],"\x84"=>[94,"\x141"],"\x85"=>[76,"\x141"],"\x86"=>[104,"\x141"],"\x87"=>[16,"\x141"],"\x88"=>[94,"\x142"],"\x89"=>[76,"\x142"],"\x8A"=>[104,"\x142"],"\x8B"=>[16,"\x142"],"\x8C"=>[94,"\x14a"],"\x8D"=>[76,"\x14a"],"\x8E"=>[104,"\x14a"],"\x8F"=>[16,"\x14a"],"\x90"=>[94,"\x14c"],"\x91"=>[76,"\x14c"],"\x92"=>[104,"\x14c"],"\x93"=>[16,"\x14c"],"\x94"=>[94,"\x14e"],"\x95"=>[76,"\x14e"],"\x96"=>[104,"\x14e"],"\x97"=>[16,"\x14e"],"\x98"=>[94,"\x14i"],"\x99"=>[76,"\x14i"],"\x9A"=>[104,"\x14i"],"\x9B"=>[16,"\x14i"],"\x9C"=>[94,"\x14o"],"\x9D"=>[76,"\x14o"],"\x9E"=>[104,"\x14o"],"\x9F"=>[16,"\x14o"],"\xA0"=>[94,"\x14s"],"\xA1"=>[76,"\x14s"],"\xA2"=>[104,"\x14s"],"\xA3"=>[16,"\x14s"],"\xA4"=>[94,"\x14t"],"\xA5"=>[76,"\x14t"],"\xA6"=>[104,"\x14t"],"\xA7"=>[16,"\x14t"],"\xA8"=>[77,"\x14\x20"],"\xA9"=>[18,"\x14\x20"],"\xAA"=>[77,"\x14\x25"],"\xAB"=>[18,"\x14\x25"],"\xAC"=>[77,"\x14-"],"\xAD"=>[18,"\x14-"],"\xAE"=>[77,"\x14."],"\xAF"=>[18,"\x14."],"\xB0"=>[77,"\x14\x2F"],"\xB1"=>[18,"\x14\x2F"],"\xB2"=>[77,"\x143"],"\xB3"=>[18,"\x143"],"\xB4"=>[77,"\x144"],"\xB5"=>[18,"\x144"],"\xB6"=>[77,"\x145"],"\xB7"=>[18,"\x145"],"\xB8"=>[77,"\x146"],"\xB9"=>[18,"\x146"],"\xBA"=>[77,"\x147"],"\xBB"=>[18,"\x147"],"\xBC"=>[77,"\x148"],"\xBD"=>[18,"\x148"],"\xBE"=>[77,"\x149"],"\xBF"=>[18,"\x149"],"\xC0"=>[77,"\x14\x3D"],"\xC1"=>[18,"\x14\x3D"],"\xC2"=>[77,"\x14A"],"\xC3"=>[18,"\x14A"],"\xC4"=>[77,"\x14_"],"\xC5"=>[18,"\x14_"],"\xC6"=>[77,"\x14b"],"\xC7"=>[18,"\x14b"],"\xC8"=>[77,"\x14d"],"\xC9"=>[18,"\x14d"],"\xCA"=>[77,"\x14f"],"\xCB"=>[18,"\x14f"],"\xCC"=>[77,"\x14g"],"\xCD"=>[18,"\x14g"],"\xCE"=>[77,"\x14h"],"\xCF"=>[18,"\x14h"],"\xD0"=>[77,"\x14l"],"\xD1"=>[18,"\x14l"],"\xD2"=>[77,"\x14m"],"\xD3"=>[18,"\x14m"],"\xD4"=>[77,"\x14n"],"\xD5"=>[18,"\x14n"],"\xD6"=>[77,"\x14p"],"\xD7"=>[18,"\x14p"],"\xD8"=>[77,"\x14r"],"\xD9"=>[18,"\x14r"],"\xDA"=>[77,"\x14u"],"\xDB"=>[18,"\x14u"],"\xDC"=>[0,"\x14\x3A"],"\xDD"=>[0,"\x14B"],"\xDE"=>[0,"\x14C"],"\xDF"=>[0,"\x14D"],"\xE0"=>[0,"\x14E"],"\xE1"=>[0,"\x14F"],"\xE2"=>[0,"\x14G"],"\xE3"=>[0,"\x14H"],"\xE4"=>[0,"\x14I"],"\xE5"=>[0,"\x14J"],"\xE6"=>[0,"\x14K"],"\xE7"=>[0,"\x14L"],"\xE8"=>[0,"\x14M"],"\xE9"=>[0,"\x14N"],"\xEA"=>[0,"\x14O"],"\xEB"=>[0,"\x14P"],"\xEC"=>[0,"\x14Q"],"\xED"=>[0,"\x14R"],"\xEE"=>[0,"\x14S"],"\xEF"=>[0,"\x14T"],"\xF0"=>[0,"\x14U"],"\xF1"=>[0,"\x14V"],"\xF2"=>[0,"\x14W"],"\xF3"=>[0,"\x14Y"],"\xF4"=>[0,"\x14j"],"\xF5"=>[0,"\x14k"],"\xF6"=>[0,"\x14q"],"\xF7"=>[0,"\x14v"],"\xF8"=>[0,"\x14w"],"\xF9"=>[0,"\x14x"],"\xFA"=>[0,"\x14y"],"\xFB"=>[0,"\x14z"],"\xFC"=>[82,"\x14"],"\xFD"=>[87,"\x14"],"\xFE"=>[130,"\x14"],"\xFF"=>[9,"\x14"],],["\x00"=>[77,"\x130"],"\x01"=>[18,"\x130"],"\x02"=>[77,"\x131"],"\x03"=>[18,"\x131"],"\x04"=>[77,"\x132"],"\x05"=>[18,"\x132"],"\x06"=>[77,"\x13a"],"\x07"=>[18,"\x13a"],"\x08"=>[77,"\x13c"],"\x09"=>[18,"\x13c"],"\x0A"=>[77,"\x13e"],"\x0B"=>[18,"\x13e"],"\x0C"=>[77,"\x13i"],"\x0D"=>[18,"\x13i"],"\x0E"=>[77,"\x13o"],"\x0F"=>[18,"\x13o"],"\x10"=>[77,"\x13s"],"\x11"=>[18,"\x13s"],"\x12"=>[77,"\x13t"],"\x13"=>[18,"\x13t"],"\x14"=>[0,"\x13\x20"],"\x15"=>[0,"\x13\x25"],"\x16"=>[0,"\x13-"],"\x17"=>[0,"\x13."],"\x18"=>[0,"\x13\x2F"],"\x19"=>[0,"\x133"],"\x1A"=>[0,"\x134"],"\x1B"=>[0,"\x135"],"\x1C"=>[0,"\x136"],"\x1D"=>[0,"\x137"],"\x1E"=>[0,"\x138"],"\x1F"=>[0,"\x139"],"\x20"=>[0,"\x13\x3D"],"\x21"=>[0,"\x13A"],"\x22"=>[0,"\x13_"],"\x23"=>[0,"\x13b"],"\x24"=>[0,"\x13d"],"\x25"=>[0,"\x13f"],"\x26"=>[0,"\x13g"],"\x27"=>[0,"\x13h"],"\x28"=>[0,"\x13l"],"\x29"=>[0,"\x13m"],"\x2A"=>[0,"\x13n"],"\x2B"=>[0,"\x13p"],"\x2C"=>[0,"\x13r"],"-"=>[0,"\x13u"],"."=>[100,"\x13"],"\x2F"=>[110,"\x13"],[111,"\x13"],[115,"\x13"],[116,"\x13"],[118,"\x13"],[119,"\x13"],[122,"\x13"],[123,"\x13"],[125,"\x13"],[126,"\x13"],[129,"\x13"],"\x3A"=>[143,"\x13"],"\x3B"=>[148,"\x13"],"\x3C"=>[151,"\x13"],"\x3D"=>[153,"\x13"],"\x3E"=>[83,"\x13"],"\x3F"=>[10,"\x13"],"\x40"=>[77,"\x140"],"A"=>[18,"\x140"],"B"=>[77,"\x141"],"C"=>[18,"\x141"],"D"=>[77,"\x142"],"E"=>[18,"\x142"],"F"=>[77,"\x14a"],"G"=>[18,"\x14a"],"H"=>[77,"\x14c"],"I"=>[18,"\x14c"],"J"=>[77,"\x14e"],"K"=>[18,"\x14e"],"L"=>[77,"\x14i"],"M"=>[18,"\x14i"],"N"=>[77,"\x14o"],"O"=>[18,"\x14o"],"P"=>[77,"\x14s"],"Q"=>[18,"\x14s"],"R"=>[77,"\x14t"],"S"=>[18,"\x14t"],"T"=>[0,"\x14\x20"],"U"=>[0,"\x14\x25"],"V"=>[0,"\x14-"],"W"=>[0,"\x14."],"X"=>[0,"\x14\x2F"],"Y"=>[0,"\x143"],"Z"=>[0,"\x144"],"\x5B"=>[0,"\x145"],"\x5C"=>[0,"\x146"],"\x5D"=>[0,"\x147"],"\x5E"=>[0,"\x148"],"_"=>[0,"\x149"],"\x60"=>[0,"\x14\x3D"],"a"=>[0,"\x14A"],"b"=>[0,"\x14_"],"c"=>[0,"\x14b"],"d"=>[0,"\x14d"],"e"=>[0,"\x14f"],"f"=>[0,"\x14g"],"g"=>[0,"\x14h"],"h"=>[0,"\x14l"],"i"=>[0,"\x14m"],"j"=>[0,"\x14n"],"k"=>[0,"\x14p"],"l"=>[0,"\x14r"],"m"=>[0,"\x14u"],"n"=>[100,"\x14"],"o"=>[110,"\x14"],"p"=>[111,"\x14"],"q"=>[115,"\x14"],"r"=>[116,"\x14"],"s"=>[118,"\x14"],"t"=>[119,"\x14"],"u"=>[122,"\x14"],"v"=>[123,"\x14"],"w"=>[125,"\x14"],"x"=>[126,"\x14"],"y"=>[129,"\x14"],"z"=>[143,"\x14"],"\x7B"=>[148,"\x14"],"\x7C"=>[151,"\x14"],"\x7D"=>[153,"\x14"],"~"=>[83,"\x14"],"\x7F"=>[10,"\x14"],"\x80"=>[77,"\x150"],"\x81"=>[18,"\x150"],"\x82"=>[77,"\x151"],"\x83"=>[18,"\x151"],"\x84"=>[77,"\x152"],"\x85"=>[18,"\x152"],"\x86"=>[77,"\x15a"],"\x87"=>[18,"\x15a"],"\x88"=>[77,"\x15c"],"\x89"=>[18,"\x15c"],"\x8A"=>[77,"\x15e"],"\x8B"=>[18,"\x15e"],"\x8C"=>[77,"\x15i"],"\x8D"=>[18,"\x15i"],"\x8E"=>[77,"\x15o"],"\x8F"=>[18,"\x15o"],"\x90"=>[77,"\x15s"],"\x91"=>[18,"\x15s"],"\x92"=>[77,"\x15t"],"\x93"=>[18,"\x15t"],"\x94"=>[0,"\x15\x20"],"\x95"=>[0,"\x15\x25"],"\x96"=>[0,"\x15-"],"\x97"=>[0,"\x15."],"\x98"=>[0,"\x15\x2F"],"\x99"=>[0,"\x153"],"\x9A"=>[0,"\x154"],"\x9B"=>[0,"\x155"],"\x9C"=>[0,"\x156"],"\x9D"=>[0,"\x157"],"\x9E"=>[0,"\x158"],"\x9F"=>[0,"\x159"],"\xA0"=>[0,"\x15\x3D"],"\xA1"=>[0,"\x15A"],"\xA2"=>[0,"\x15_"],"\xA3"=>[0,"\x15b"],"\xA4"=>[0,"\x15d"],"\xA5"=>[0,"\x15f"],"\xA6"=>[0,"\x15g"],"\xA7"=>[0,"\x15h"],"\xA8"=>[0,"\x15l"],"\xA9"=>[0,"\x15m"],"\xAA"=>[0,"\x15n"],"\xAB"=>[0,"\x15p"],"\xAC"=>[0,"\x15r"],"\xAD"=>[0,"\x15u"],"\xAE"=>[100,"\x15"],"\xAF"=>[110,"\x15"],"\xB0"=>[111,"\x15"],"\xB1"=>[115,"\x15"],"\xB2"=>[116,"\x15"],"\xB3"=>[118,"\x15"],"\xB4"=>[119,"\x15"],"\xB5"=>[122,"\x15"],"\xB6"=>[123,"\x15"],"\xB7"=>[125,"\x15"],"\xB8"=>[126,"\x15"],"\xB9"=>[129,"\x15"],"\xBA"=>[143,"\x15"],"\xBB"=>[148,"\x15"],"\xBC"=>[151,"\x15"],"\xBD"=>[153,"\x15"],"\xBE"=>[83,"\x15"],"\xBF"=>[10,"\x15"],"\xC0"=>[77,"\x170"],"\xC1"=>[18,"\x170"],"\xC2"=>[77,"\x171"],"\xC3"=>[18,"\x171"],"\xC4"=>[77,"\x172"],"\xC5"=>[18,"\x172"],"\xC6"=>[77,"\x17a"],"\xC7"=>[18,"\x17a"],"\xC8"=>[77,"\x17c"],"\xC9"=>[18,"\x17c"],"\xCA"=>[77,"\x17e"],"\xCB"=>[18,"\x17e"],"\xCC"=>[77,"\x17i"],"\xCD"=>[18,"\x17i"],"\xCE"=>[77,"\x17o"],"\xCF"=>[18,"\x17o"],"\xD0"=>[77,"\x17s"],"\xD1"=>[18,"\x17s"],"\xD2"=>[77,"\x17t"],"\xD3"=>[18,"\x17t"],"\xD4"=>[0,"\x17\x20"],"\xD5"=>[0,"\x17\x25"],"\xD6"=>[0,"\x17-"],"\xD7"=>[0,"\x17."],"\xD8"=>[0,"\x17\x2F"],"\xD9"=>[0,"\x173"],"\xDA"=>[0,"\x174"],"\xDB"=>[0,"\x175"],"\xDC"=>[0,"\x176"],"\xDD"=>[0,"\x177"],"\xDE"=>[0,"\x178"],"\xDF"=>[0,"\x179"],"\xE0"=>[0,"\x17\x3D"],"\xE1"=>[0,"\x17A"],"\xE2"=>[0,"\x17_"],"\xE3"=>[0,"\x17b"],"\xE4"=>[0,"\x17d"],"\xE5"=>[0,"\x17f"],"\xE6"=>[0,"\x17g"],"\xE7"=>[0,"\x17h"],"\xE8"=>[0,"\x17l"],"\xE9"=>[0,"\x17m"],"\xEA"=>[0,"\x17n"],"\xEB"=>[0,"\x17p"],"\xEC"=>[0,"\x17r"],"\xED"=>[0,"\x17u"],"\xEE"=>[100,"\x17"],"\xEF"=>[110,"\x17"],"\xF0"=>[111,"\x17"],"\xF1"=>[115,"\x17"],"\xF2"=>[116,"\x17"],"\xF3"=>[118,"\x17"],"\xF4"=>[119,"\x17"],"\xF5"=>[122,"\x17"],"\xF6"=>[123,"\x17"],"\xF7"=>[125,"\x17"],"\xF8"=>[126,"\x17"],"\xF9"=>[129,"\x17"],"\xFA"=>[143,"\x17"],"\xFB"=>[148,"\x17"],"\xFC"=>[151,"\x17"],"\xFD"=>[153,"\x17"],"\xFE"=>[83,"\x17"],"\xFF"=>[10,"\x17"],],["\x00"=>[0,"\x130"],"\x01"=>[0,"\x131"],"\x02"=>[0,"\x132"],"\x03"=>[0,"\x13a"],"\x04"=>[0,"\x13c"],"\x05"=>[0,"\x13e"],"\x06"=>[0,"\x13i"],"\x07"=>[0,"\x13o"],"\x08"=>[0,"\x13s"],"\x09"=>[0,"\x13t"],"\x0A"=>[73,"\x13"],"\x0B"=>[88,"\x13"],"\x0C"=>[89,"\x13"],"\x0D"=>[96,"\x13"],"\x0E"=>[97,"\x13"],"\x0F"=>[99,"\x13"],"\x10"=>[106,"\x13"],"\x11"=>[136,"\x13"],"\x12"=>[139,"\x13"],"\x13"=>[141,"\x13"],"\x14"=>[145,"\x13"],"\x15"=>[147,"\x13"],"\x16"=>[149,"\x13"],"\x17"=>[101,"\x13"],"\x18"=>[112,"\x13"],"\x19"=>[117,"\x13"],"\x1A"=>[120,"\x13"],"\x1B"=>[124,"\x13"],"\x1C"=>[127,"\x13"],"\x1D"=>[144,"\x13"],"\x1E"=>[152,"\x13"],"\x1F"=>[11,"\x13"],"\x20"=>[0,"\x140"],"\x21"=>[0,"\x141"],"\x22"=>[0,"\x142"],"\x23"=>[0,"\x14a"],"\x24"=>[0,"\x14c"],"\x25"=>[0,"\x14e"],"\x26"=>[0,"\x14i"],"\x27"=>[0,"\x14o"],"\x28"=>[0,"\x14s"],"\x29"=>[0,"\x14t"],"\x2A"=>[73,"\x14"],"\x2B"=>[88,"\x14"],"\x2C"=>[89,"\x14"],"-"=>[96,"\x14"],"."=>[97,"\x14"],"\x2F"=>[99,"\x14"],[106,"\x14"],[136,"\x14"],[139,"\x14"],[141,"\x14"],[145,"\x14"],[147,"\x14"],[149,"\x14"],[101,"\x14"],[112,"\x14"],[117,"\x14"],"\x3A"=>[120,"\x14"],"\x3B"=>[124,"\x14"],"\x3C"=>[127,"\x14"],"\x3D"=>[144,"\x14"],"\x3E"=>[152,"\x14"],"\x3F"=>[11,"\x14"],"\x40"=>[0,"\x150"],"A"=>[0,"\x151"],"B"=>[0,"\x152"],"C"=>[0,"\x15a"],"D"=>[0,"\x15c"],"E"=>[0,"\x15e"],"F"=>[0,"\x15i"],"G"=>[0,"\x15o"],"H"=>[0,"\x15s"],"I"=>[0,"\x15t"],"J"=>[73,"\x15"],"K"=>[88,"\x15"],"L"=>[89,"\x15"],"M"=>[96,"\x15"],"N"=>[97,"\x15"],"O"=>[99,"\x15"],"P"=>[106,"\x15"],"Q"=>[136,"\x15"],"R"=>[139,"\x15"],"S"=>[141,"\x15"],"T"=>[145,"\x15"],"U"=>[147,"\x15"],"V"=>[149,"\x15"],"W"=>[101,"\x15"],"X"=>[112,"\x15"],"Y"=>[117,"\x15"],"Z"=>[120,"\x15"],"\x5B"=>[124,"\x15"],"\x5C"=>[127,"\x15"],"\x5D"=>[144,"\x15"],"\x5E"=>[152,"\x15"],"_"=>[11,"\x15"],"\x60"=>[0,"\x170"],"a"=>[0,"\x171"],"b"=>[0,"\x172"],"c"=>[0,"\x17a"],"d"=>[0,"\x17c"],"e"=>[0,"\x17e"],"f"=>[0,"\x17i"],"g"=>[0,"\x17o"],"h"=>[0,"\x17s"],"i"=>[0,"\x17t"],"j"=>[73,"\x17"],"k"=>[88,"\x17"],"l"=>[89,"\x17"],"m"=>[96,"\x17"],"n"=>[97,"\x17"],"o"=>[99,"\x17"],"p"=>[106,"\x17"],"q"=>[136,"\x17"],"r"=>[139,"\x17"],"s"=>[141,"\x17"],"t"=>[145,"\x17"],"u"=>[147,"\x17"],"v"=>[149,"\x17"],"w"=>[101,"\x17"],"x"=>[112,"\x17"],"y"=>[117,"\x17"],"z"=>[120,"\x17"],"\x7B"=>[124,"\x17"],"\x7C"=>[127,"\x17"],"\x7D"=>[144,"\x17"],"~"=>[152,"\x17"],"\x7F"=>[11,"\x17"],"\x80"=>[0,"\x180"],"\x81"=>[0,"\x181"],"\x82"=>[0,"\x182"],"\x83"=>[0,"\x18a"],"\x84"=>[0,"\x18c"],"\x85"=>[0,"\x18e"],"\x86"=>[0,"\x18i"],"\x87"=>[0,"\x18o"],"\x88"=>[0,"\x18s"],"\x89"=>[0,"\x18t"],"\x8A"=>[73,"\x18"],"\x8B"=>[88,"\x18"],"\x8C"=>[89,"\x18"],"\x8D"=>[96,"\x18"],"\x8E"=>[97,"\x18"],"\x8F"=>[99,"\x18"],"\x90"=>[106,"\x18"],"\x91"=>[136,"\x18"],"\x92"=>[139,"\x18"],"\x93"=>[141,"\x18"],"\x94"=>[145,"\x18"],"\x95"=>[147,"\x18"],"\x96"=>[149,"\x18"],"\x97"=>[101,"\x18"],"\x98"=>[112,"\x18"],"\x99"=>[117,"\x18"],"\x9A"=>[120,"\x18"],"\x9B"=>[124,"\x18"],"\x9C"=>[127,"\x18"],"\x9D"=>[144,"\x18"],"\x9E"=>[152,"\x18"],"\x9F"=>[11,"\x18"],"\xA0"=>[0,"\x190"],"\xA1"=>[0,"\x191"],"\xA2"=>[0,"\x192"],"\xA3"=>[0,"\x19a"],"\xA4"=>[0,"\x19c"],"\xA5"=>[0,"\x19e"],"\xA6"=>[0,"\x19i"],"\xA7"=>[0,"\x19o"],"\xA8"=>[0,"\x19s"],"\xA9"=>[0,"\x19t"],"\xAA"=>[73,"\x19"],"\xAB"=>[88,"\x19"],"\xAC"=>[89,"\x19"],"\xAD"=>[96,"\x19"],"\xAE"=>[97,"\x19"],"\xAF"=>[99,"\x19"],"\xB0"=>[106,"\x19"],"\xB1"=>[136,"\x19"],"\xB2"=>[139,"\x19"],"\xB3"=>[141,"\x19"],"\xB4"=>[145,"\x19"],"\xB5"=>[147,"\x19"],"\xB6"=>[149,"\x19"],"\xB7"=>[101,"\x19"],"\xB8"=>[112,"\x19"],"\xB9"=>[117,"\x19"],"\xBA"=>[120,"\x19"],"\xBB"=>[124,"\x19"],"\xBC"=>[127,"\x19"],"\xBD"=>[144,"\x19"],"\xBE"=>[152,"\x19"],"\xBF"=>[11,"\x19"],"\xC0"=>[0,"\x1A0"],"\xC1"=>[0,"\x1A1"],"\xC2"=>[0,"\x1A2"],"\xC3"=>[0,"\x1Aa"],"\xC4"=>[0,"\x1Ac"],"\xC5"=>[0,"\x1Ae"],"\xC6"=>[0,"\x1Ai"],"\xC7"=>[0,"\x1Ao"],"\xC8"=>[0,"\x1As"],"\xC9"=>[0,"\x1At"],"\xCA"=>[73,"\x1A"],"\xCB"=>[88,"\x1A"],"\xCC"=>[89,"\x1A"],"\xCD"=>[96,"\x1A"],"\xCE"=>[97,"\x1A"],"\xCF"=>[99,"\x1A"],"\xD0"=>[106,"\x1A"],"\xD1"=>[136,"\x1A"],"\xD2"=>[139,"\x1A"],"\xD3"=>[141,"\x1A"],"\xD4"=>[145,"\x1A"],"\xD5"=>[147,"\x1A"],"\xD6"=>[149,"\x1A"],"\xD7"=>[101,"\x1A"],"\xD8"=>[112,"\x1A"],"\xD9"=>[117,"\x1A"],"\xDA"=>[120,"\x1A"],"\xDB"=>[124,"\x1A"],"\xDC"=>[127,"\x1A"],"\xDD"=>[144,"\x1A"],"\xDE"=>[152,"\x1A"],"\xDF"=>[11,"\x1A"],"\xE0"=>[0,"\x1B0"],"\xE1"=>[0,"\x1B1"],"\xE2"=>[0,"\x1B2"],"\xE3"=>[0,"\x1Ba"],"\xE4"=>[0,"\x1Bc"],"\xE5"=>[0,"\x1Be"],"\xE6"=>[0,"\x1Bi"],"\xE7"=>[0,"\x1Bo"],"\xE8"=>[0,"\x1Bs"],"\xE9"=>[0,"\x1Bt"],"\xEA"=>[73,"\x1B"],"\xEB"=>[88,"\x1B"],"\xEC"=>[89,"\x1B"],"\xED"=>[96,"\x1B"],"\xEE"=>[97,"\x1B"],"\xEF"=>[99,"\x1B"],"\xF0"=>[106,"\x1B"],"\xF1"=>[136,"\x1B"],"\xF2"=>[139,"\x1B"],"\xF3"=>[141,"\x1B"],"\xF4"=>[145,"\x1B"],"\xF5"=>[147,"\x1B"],"\xF6"=>[149,"\x1B"],"\xF7"=>[101,"\x1B"],"\xF8"=>[112,"\x1B"],"\xF9"=>[117,"\x1B"],"\xFA"=>[120,"\x1B"],"\xFB"=>[124,"\x1B"],"\xFC"=>[127,"\x1B"],"\xFD"=>[144,"\x1B"],"\xFE"=>[152,"\x1B"],"\xFF"=>[11,"\x1B"],],["\x00"=>[94,"\x150"],"\x01"=>[76,"\x150"],"\x02"=>[104,"\x150"],"\x03"=>[16,"\x150"],"\x04"=>[94,"\x151"],"\x05"=>[76,"\x151"],"\x06"=>[104,"\x151"],"\x07"=>[16,"\x151"],"\x08"=>[94,"\x152"],"\x09"=>[76,"\x152"],"\x0A"=>[104,"\x152"],"\x0B"=>[16,"\x152"],"\x0C"=>[94,"\x15a"],"\x0D"=>[76,"\x15a"],"\x0E"=>[104,"\x15a"],"\x0F"=>[16,"\x15a"],"\x10"=>[94,"\x15c"],"\x11"=>[76,"\x15c"],"\x12"=>[104,"\x15c"],"\x13"=>[16,"\x15c"],"\x14"=>[94,"\x15e"],"\x15"=>[76,"\x15e"],"\x16"=>[104,"\x15e"],"\x17"=>[16,"\x15e"],"\x18"=>[94,"\x15i"],"\x19"=>[76,"\x15i"],"\x1A"=>[104,"\x15i"],"\x1B"=>[16,"\x15i"],"\x1C"=>[94,"\x15o"],"\x1D"=>[76,"\x15o"],"\x1E"=>[104,"\x15o"],"\x1F"=>[16,"\x15o"],"\x20"=>[94,"\x15s"],"\x21"=>[76,"\x15s"],"\x22"=>[104,"\x15s"],"\x23"=>[16,"\x15s"],"\x24"=>[94,"\x15t"],"\x25"=>[76,"\x15t"],"\x26"=>[104,"\x15t"],"\x27"=>[16,"\x15t"],"\x28"=>[77,"\x15\x20"],"\x29"=>[18,"\x15\x20"],"\x2A"=>[77,"\x15\x25"],"\x2B"=>[18,"\x15\x25"],"\x2C"=>[77,"\x15-"],"-"=>[18,"\x15-"],"."=>[77,"\x15."],"\x2F"=>[18,"\x15."],[77,"\x15\x2F"],[18,"\x15\x2F"],[77,"\x153"],[18,"\x153"],[77,"\x154"],[18,"\x154"],[77,"\x155"],[18,"\x155"],[77,"\x156"],[18,"\x156"],"\x3A"=>[77,"\x157"],"\x3B"=>[18,"\x157"],"\x3C"=>[77,"\x158"],"\x3D"=>[18,"\x158"],"\x3E"=>[77,"\x159"],"\x3F"=>[18,"\x159"],"\x40"=>[77,"\x15\x3D"],"A"=>[18,"\x15\x3D"],"B"=>[77,"\x15A"],"C"=>[18,"\x15A"],"D"=>[77,"\x15_"],"E"=>[18,"\x15_"],"F"=>[77,"\x15b"],"G"=>[18,"\x15b"],"H"=>[77,"\x15d"],"I"=>[18,"\x15d"],"J"=>[77,"\x15f"],"K"=>[18,"\x15f"],"L"=>[77,"\x15g"],"M"=>[18,"\x15g"],"N"=>[77,"\x15h"],"O"=>[18,"\x15h"],"P"=>[77,"\x15l"],"Q"=>[18,"\x15l"],"R"=>[77,"\x15m"],"S"=>[18,"\x15m"],"T"=>[77,"\x15n"],"U"=>[18,"\x15n"],"V"=>[77,"\x15p"],"W"=>[18,"\x15p"],"X"=>[77,"\x15r"],"Y"=>[18,"\x15r"],"Z"=>[77,"\x15u"],"\x5B"=>[18,"\x15u"],"\x5C"=>[0,"\x15\x3A"],"\x5D"=>[0,"\x15B"],"\x5E"=>[0,"\x15C"],"_"=>[0,"\x15D"],"\x60"=>[0,"\x15E"],"a"=>[0,"\x15F"],"b"=>[0,"\x15G"],"c"=>[0,"\x15H"],"d"=>[0,"\x15I"],"e"=>[0,"\x15J"],"f"=>[0,"\x15K"],"g"=>[0,"\x15L"],"h"=>[0,"\x15M"],"i"=>[0,"\x15N"],"j"=>[0,"\x15O"],"k"=>[0,"\x15P"],"l"=>[0,"\x15Q"],"m"=>[0,"\x15R"],"n"=>[0,"\x15S"],"o"=>[0,"\x15T"],"p"=>[0,"\x15U"],"q"=>[0,"\x15V"],"r"=>[0,"\x15W"],"s"=>[0,"\x15Y"],"t"=>[0,"\x15j"],"u"=>[0,"\x15k"],"v"=>[0,"\x15q"],"w"=>[0,"\x15v"],"x"=>[0,"\x15w"],"y"=>[0,"\x15x"],"z"=>[0,"\x15y"],"\x7B"=>[0,"\x15z"],"\x7C"=>[82,"\x15"],"\x7D"=>[87,"\x15"],"~"=>[130,"\x15"],"\x7F"=>[9,"\x15"],"\x80"=>[94,"\x170"],"\x81"=>[76,"\x170"],"\x82"=>[104,"\x170"],"\x83"=>[16,"\x170"],"\x84"=>[94,"\x171"],"\x85"=>[76,"\x171"],"\x86"=>[104,"\x171"],"\x87"=>[16,"\x171"],"\x88"=>[94,"\x172"],"\x89"=>[76,"\x172"],"\x8A"=>[104,"\x172"],"\x8B"=>[16,"\x172"],"\x8C"=>[94,"\x17a"],"\x8D"=>[76,"\x17a"],"\x8E"=>[104,"\x17a"],"\x8F"=>[16,"\x17a"],"\x90"=>[94,"\x17c"],"\x91"=>[76,"\x17c"],"\x92"=>[104,"\x17c"],"\x93"=>[16,"\x17c"],"\x94"=>[94,"\x17e"],"\x95"=>[76,"\x17e"],"\x96"=>[104,"\x17e"],"\x97"=>[16,"\x17e"],"\x98"=>[94,"\x17i"],"\x99"=>[76,"\x17i"],"\x9A"=>[104,"\x17i"],"\x9B"=>[16,"\x17i"],"\x9C"=>[94,"\x17o"],"\x9D"=>[76,"\x17o"],"\x9E"=>[104,"\x17o"],"\x9F"=>[16,"\x17o"],"\xA0"=>[94,"\x17s"],"\xA1"=>[76,"\x17s"],"\xA2"=>[104,"\x17s"],"\xA3"=>[16,"\x17s"],"\xA4"=>[94,"\x17t"],"\xA5"=>[76,"\x17t"],"\xA6"=>[104,"\x17t"],"\xA7"=>[16,"\x17t"],"\xA8"=>[77,"\x17\x20"],"\xA9"=>[18,"\x17\x20"],"\xAA"=>[77,"\x17\x25"],"\xAB"=>[18,"\x17\x25"],"\xAC"=>[77,"\x17-"],"\xAD"=>[18,"\x17-"],"\xAE"=>[77,"\x17."],"\xAF"=>[18,"\x17."],"\xB0"=>[77,"\x17\x2F"],"\xB1"=>[18,"\x17\x2F"],"\xB2"=>[77,"\x173"],"\xB3"=>[18,"\x173"],"\xB4"=>[77,"\x174"],"\xB5"=>[18,"\x174"],"\xB6"=>[77,"\x175"],"\xB7"=>[18,"\x175"],"\xB8"=>[77,"\x176"],"\xB9"=>[18,"\x176"],"\xBA"=>[77,"\x177"],"\xBB"=>[18,"\x177"],"\xBC"=>[77,"\x178"],"\xBD"=>[18,"\x178"],"\xBE"=>[77,"\x179"],"\xBF"=>[18,"\x179"],"\xC0"=>[77,"\x17\x3D"],"\xC1"=>[18,"\x17\x3D"],"\xC2"=>[77,"\x17A"],"\xC3"=>[18,"\x17A"],"\xC4"=>[77,"\x17_"],"\xC5"=>[18,"\x17_"],"\xC6"=>[77,"\x17b"],"\xC7"=>[18,"\x17b"],"\xC8"=>[77,"\x17d"],"\xC9"=>[18,"\x17d"],"\xCA"=>[77,"\x17f"],"\xCB"=>[18,"\x17f"],"\xCC"=>[77,"\x17g"],"\xCD"=>[18,"\x17g"],"\xCE"=>[77,"\x17h"],"\xCF"=>[18,"\x17h"],"\xD0"=>[77,"\x17l"],"\xD1"=>[18,"\x17l"],"\xD2"=>[77,"\x17m"],"\xD3"=>[18,"\x17m"],"\xD4"=>[77,"\x17n"],"\xD5"=>[18,"\x17n"],"\xD6"=>[77,"\x17p"],"\xD7"=>[18,"\x17p"],"\xD8"=>[77,"\x17r"],"\xD9"=>[18,"\x17r"],"\xDA"=>[77,"\x17u"],"\xDB"=>[18,"\x17u"],"\xDC"=>[0,"\x17\x3A"],"\xDD"=>[0,"\x17B"],"\xDE"=>[0,"\x17C"],"\xDF"=>[0,"\x17D"],"\xE0"=>[0,"\x17E"],"\xE1"=>[0,"\x17F"],"\xE2"=>[0,"\x17G"],"\xE3"=>[0,"\x17H"],"\xE4"=>[0,"\x17I"],"\xE5"=>[0,"\x17J"],"\xE6"=>[0,"\x17K"],"\xE7"=>[0,"\x17L"],"\xE8"=>[0,"\x17M"],"\xE9"=>[0,"\x17N"],"\xEA"=>[0,"\x17O"],"\xEB"=>[0,"\x17P"],"\xEC"=>[0,"\x17Q"],"\xED"=>[0,"\x17R"],"\xEE"=>[0,"\x17S"],"\xEF"=>[0,"\x17T"],"\xF0"=>[0,"\x17U"],"\xF1"=>[0,"\x17V"],"\xF2"=>[0,"\x17W"],"\xF3"=>[0,"\x17Y"],"\xF4"=>[0,"\x17j"],"\xF5"=>[0,"\x17k"],"\xF6"=>[0,"\x17q"],"\xF7"=>[0,"\x17v"],"\xF8"=>[0,"\x17w"],"\xF9"=>[0,"\x17x"],"\xFA"=>[0,"\x17y"],"\xFB"=>[0,"\x17z"],"\xFC"=>[82,"\x17"],"\xFD"=>[87,"\x17"],"\xFE"=>[130,"\x17"],"\xFF"=>[9,"\x17"],],["\x00"=>[94,"\x160"],"\x01"=>[76,"\x160"],"\x02"=>[104,"\x160"],"\x03"=>[16,"\x160"],"\x04"=>[94,"\x161"],"\x05"=>[76,"\x161"],"\x06"=>[104,"\x161"],"\x07"=>[16,"\x161"],"\x08"=>[94,"\x162"],"\x09"=>[76,"\x162"],"\x0A"=>[104,"\x162"],"\x0B"=>[16,"\x162"],"\x0C"=>[94,"\x16a"],"\x0D"=>[76,"\x16a"],"\x0E"=>[104,"\x16a"],"\x0F"=>[16,"\x16a"],"\x10"=>[94,"\x16c"],"\x11"=>[76,"\x16c"],"\x12"=>[104,"\x16c"],"\x13"=>[16,"\x16c"],"\x14"=>[94,"\x16e"],"\x15"=>[76,"\x16e"],"\x16"=>[104,"\x16e"],"\x17"=>[16,"\x16e"],"\x18"=>[94,"\x16i"],"\x19"=>[76,"\x16i"],"\x1A"=>[104,"\x16i"],"\x1B"=>[16,"\x16i"],"\x1C"=>[94,"\x16o"],"\x1D"=>[76,"\x16o"],"\x1E"=>[104,"\x16o"],"\x1F"=>[16,"\x16o"],"\x20"=>[94,"\x16s"],"\x21"=>[76,"\x16s"],"\x22"=>[104,"\x16s"],"\x23"=>[16,"\x16s"],"\x24"=>[94,"\x16t"],"\x25"=>[76,"\x16t"],"\x26"=>[104,"\x16t"],"\x27"=>[16,"\x16t"],"\x28"=>[77,"\x16\x20"],"\x29"=>[18,"\x16\x20"],"\x2A"=>[77,"\x16\x25"],"\x2B"=>[18,"\x16\x25"],"\x2C"=>[77,"\x16-"],"-"=>[18,"\x16-"],"."=>[77,"\x16."],"\x2F"=>[18,"\x16."],[77,"\x16\x2F"],[18,"\x16\x2F"],[77,"\x163"],[18,"\x163"],[77,"\x164"],[18,"\x164"],[77,"\x165"],[18,"\x165"],[77,"\x166"],[18,"\x166"],"\x3A"=>[77,"\x167"],"\x3B"=>[18,"\x167"],"\x3C"=>[77,"\x168"],"\x3D"=>[18,"\x168"],"\x3E"=>[77,"\x169"],"\x3F"=>[18,"\x169"],"\x40"=>[77,"\x16\x3D"],"A"=>[18,"\x16\x3D"],"B"=>[77,"\x16A"],"C"=>[18,"\x16A"],"D"=>[77,"\x16_"],"E"=>[18,"\x16_"],"F"=>[77,"\x16b"],"G"=>[18,"\x16b"],"H"=>[77,"\x16d"],"I"=>[18,"\x16d"],"J"=>[77,"\x16f"],"K"=>[18,"\x16f"],"L"=>[77,"\x16g"],"M"=>[18,"\x16g"],"N"=>[77,"\x16h"],"O"=>[18,"\x16h"],"P"=>[77,"\x16l"],"Q"=>[18,"\x16l"],"R"=>[77,"\x16m"],"S"=>[18,"\x16m"],"T"=>[77,"\x16n"],"U"=>[18,"\x16n"],"V"=>[77,"\x16p"],"W"=>[18,"\x16p"],"X"=>[77,"\x16r"],"Y"=>[18,"\x16r"],"Z"=>[77,"\x16u"],"\x5B"=>[18,"\x16u"],"\x5C"=>[0,"\x16\x3A"],"\x5D"=>[0,"\x16B"],"\x5E"=>[0,"\x16C"],"_"=>[0,"\x16D"],"\x60"=>[0,"\x16E"],"a"=>[0,"\x16F"],"b"=>[0,"\x16G"],"c"=>[0,"\x16H"],"d"=>[0,"\x16I"],"e"=>[0,"\x16J"],"f"=>[0,"\x16K"],"g"=>[0,"\x16L"],"h"=>[0,"\x16M"],"i"=>[0,"\x16N"],"j"=>[0,"\x16O"],"k"=>[0,"\x16P"],"l"=>[0,"\x16Q"],"m"=>[0,"\x16R"],"n"=>[0,"\x16S"],"o"=>[0,"\x16T"],"p"=>[0,"\x16U"],"q"=>[0,"\x16V"],"r"=>[0,"\x16W"],"s"=>[0,"\x16Y"],"t"=>[0,"\x16j"],"u"=>[0,"\x16k"],"v"=>[0,"\x16q"],"w"=>[0,"\x16v"],"x"=>[0,"\x16w"],"y"=>[0,"\x16x"],"z"=>[0,"\x16y"],"\x7B"=>[0,"\x16z"],"\x7C"=>[82,"\x16"],"\x7D"=>[87,"\x16"],"~"=>[130,"\x16"],"\x7F"=>[9,"\x16"],"\x80"=>[94,""],"\x81"=>[76,""],"\x82"=>[104,""],"\x83"=>[16,""],"\x84"=>[94,""],"\x85"=>[76,""],"\x86"=>[104,""],"\x87"=>[16,""],"\x88"=>[94,""],"\x89"=>[76,""],"\x8A"=>[104,""],"\x8B"=>[16,""],"\x8C"=>[94,""],"\x8D"=>[76,""],"\x8E"=>[104,""],"\x8F"=>[16,""],"\x90"=>[94,""],"\x91"=>[76,""],"\x92"=>[104,""],"\x93"=>[16,""],"\x94"=>[94,""],"\x95"=>[76,""],"\x96"=>[104,""],"\x97"=>[16,""],"\x98"=>[94,""],"\x99"=>[76,""],"\x9A"=>[104,""],"\x9B"=>[16,""],"\x9C"=>[94,""],"\x9D"=>[76,""],"\x9E"=>[104,""],"\x9F"=>[16,""],"\xA0"=>[94,""],"\xA1"=>[76,""],"\xA2"=>[104,""],"\xA3"=>[16,""],"\xA4"=>[94,""],"\xA5"=>[76,""],"\xA6"=>[104,""],"\xA7"=>[16,""],"\xA8"=>[77,""],"\xA9"=>[18,""],"\xAA"=>[77,""],"\xAB"=>[18,""],"\xAC"=>[77,""],"\xAD"=>[18,""],"\xAE"=>[77,""],"\xAF"=>[18,""],"\xB0"=>[77,""],"\xB1"=>[18,""],"\xB2"=>[77,""],"\xB3"=>[18,""],"\xB4"=>[77,""],"\xB5"=>[18,""],"\xB6"=>[77,""],"\xB7"=>[18,""],"\xB8"=>[77,""],"\xB9"=>[18,""],"\xBA"=>[77,""],"\xBB"=>[18,""],"\xBC"=>[77,""],"\xBD"=>[18,""],"\xBE"=>[77,""],"\xBF"=>[18,""],"\xC0"=>[77,""],"\xC1"=>[18,""],"\xC2"=>[77,""],"\xC3"=>[18,""],"\xC4"=>[77,""],"\xC5"=>[18,""],"\xC6"=>[77,""],"\xC7"=>[18,""],"\xC8"=>[77,""],"\xC9"=>[18,""],"\xCA"=>[77,""],"\xCB"=>[18,""],"\xCC"=>[77,""],"\xCD"=>[18,""],"\xCE"=>[77,""],"\xCF"=>[18,""],"\xD0"=>[77,""],"\xD1"=>[18,""],"\xD2"=>[77,""],"\xD3"=>[18,""],"\xD4"=>[77,""],"\xD5"=>[18,""],"\xD6"=>[77,""],"\xD7"=>[18,""],"\xD8"=>[77,""],"\xD9"=>[18,""],"\xDA"=>[77,""],"\xDB"=>[18,""],"\xDC"=>[0,""],"\xDD"=>[0,""],"\xDE"=>[0,""],"\xDF"=>[0,""],"\xE0"=>[0,""],"\xE1"=>[0,""],"\xE2"=>[0,""],"\xE3"=>[0,""],"\xE4"=>[0,""],"\xE5"=>[0,""],"\xE6"=>[0,""],"\xE7"=>[0,""],"\xE8"=>[0,""],"\xE9"=>[0,""],"\xEA"=>[0,""],"\xEB"=>[0,""],"\xEC"=>[0,""],"\xED"=>[0,""],"\xEE"=>[0,""],"\xEF"=>[0,""],"\xF0"=>[0,""],"\xF1"=>[0,""],"\xF2"=>[0,""],"\xF3"=>[0,""],"\xF4"=>[0,""],"\xF5"=>[0,""],"\xF6"=>[0,""],"\xF7"=>[0,""],"\xF8"=>[0,""],"\xF9"=>[0,""],"\xFA"=>[0,""],"\xFB"=>[0,""],"\xFC"=>[82,""],"\xFD"=>[87,""],"\xFE"=>[130,""],"\xFF"=>[9,""],],["\x00"=>[94,"\x180"],"\x01"=>[76,"\x180"],"\x02"=>[104,"\x180"],"\x03"=>[16,"\x180"],"\x04"=>[94,"\x181"],"\x05"=>[76,"\x181"],"\x06"=>[104,"\x181"],"\x07"=>[16,"\x181"],"\x08"=>[94,"\x182"],"\x09"=>[76,"\x182"],"\x0A"=>[104,"\x182"],"\x0B"=>[16,"\x182"],"\x0C"=>[94,"\x18a"],"\x0D"=>[76,"\x18a"],"\x0E"=>[104,"\x18a"],"\x0F"=>[16,"\x18a"],"\x10"=>[94,"\x18c"],"\x11"=>[76,"\x18c"],"\x12"=>[104,"\x18c"],"\x13"=>[16,"\x18c"],"\x14"=>[94,"\x18e"],"\x15"=>[76,"\x18e"],"\x16"=>[104,"\x18e"],"\x17"=>[16,"\x18e"],"\x18"=>[94,"\x18i"],"\x19"=>[76,"\x18i"],"\x1A"=>[104,"\x18i"],"\x1B"=>[16,"\x18i"],"\x1C"=>[94,"\x18o"],"\x1D"=>[76,"\x18o"],"\x1E"=>[104,"\x18o"],"\x1F"=>[16,"\x18o"],"\x20"=>[94,"\x18s"],"\x21"=>[76,"\x18s"],"\x22"=>[104,"\x18s"],"\x23"=>[16,"\x18s"],"\x24"=>[94,"\x18t"],"\x25"=>[76,"\x18t"],"\x26"=>[104,"\x18t"],"\x27"=>[16,"\x18t"],"\x28"=>[77,"\x18\x20"],"\x29"=>[18,"\x18\x20"],"\x2A"=>[77,"\x18\x25"],"\x2B"=>[18,"\x18\x25"],"\x2C"=>[77,"\x18-"],"-"=>[18,"\x18-"],"."=>[77,"\x18."],"\x2F"=>[18,"\x18."],[77,"\x18\x2F"],[18,"\x18\x2F"],[77,"\x183"],[18,"\x183"],[77,"\x184"],[18,"\x184"],[77,"\x185"],[18,"\x185"],[77,"\x186"],[18,"\x186"],"\x3A"=>[77,"\x187"],"\x3B"=>[18,"\x187"],"\x3C"=>[77,"\x188"],"\x3D"=>[18,"\x188"],"\x3E"=>[77,"\x189"],"\x3F"=>[18,"\x189"],"\x40"=>[77,"\x18\x3D"],"A"=>[18,"\x18\x3D"],"B"=>[77,"\x18A"],"C"=>[18,"\x18A"],"D"=>[77,"\x18_"],"E"=>[18,"\x18_"],"F"=>[77,"\x18b"],"G"=>[18,"\x18b"],"H"=>[77,"\x18d"],"I"=>[18,"\x18d"],"J"=>[77,"\x18f"],"K"=>[18,"\x18f"],"L"=>[77,"\x18g"],"M"=>[18,"\x18g"],"N"=>[77,"\x18h"],"O"=>[18,"\x18h"],"P"=>[77,"\x18l"],"Q"=>[18,"\x18l"],"R"=>[77,"\x18m"],"S"=>[18,"\x18m"],"T"=>[77,"\x18n"],"U"=>[18,"\x18n"],"V"=>[77,"\x18p"],"W"=>[18,"\x18p"],"X"=>[77,"\x18r"],"Y"=>[18,"\x18r"],"Z"=>[77,"\x18u"],"\x5B"=>[18,"\x18u"],"\x5C"=>[0,"\x18\x3A"],"\x5D"=>[0,"\x18B"],"\x5E"=>[0,"\x18C"],"_"=>[0,"\x18D"],"\x60"=>[0,"\x18E"],"a"=>[0,"\x18F"],"b"=>[0,"\x18G"],"c"=>[0,"\x18H"],"d"=>[0,"\x18I"],"e"=>[0,"\x18J"],"f"=>[0,"\x18K"],"g"=>[0,"\x18L"],"h"=>[0,"\x18M"],"i"=>[0,"\x18N"],"j"=>[0,"\x18O"],"k"=>[0,"\x18P"],"l"=>[0,"\x18Q"],"m"=>[0,"\x18R"],"n"=>[0,"\x18S"],"o"=>[0,"\x18T"],"p"=>[0,"\x18U"],"q"=>[0,"\x18V"],"r"=>[0,"\x18W"],"s"=>[0,"\x18Y"],"t"=>[0,"\x18j"],"u"=>[0,"\x18k"],"v"=>[0,"\x18q"],"w"=>[0,"\x18v"],"x"=>[0,"\x18w"],"y"=>[0,"\x18x"],"z"=>[0,"\x18y"],"\x7B"=>[0,"\x18z"],"\x7C"=>[82,"\x18"],"\x7D"=>[87,"\x18"],"~"=>[130,"\x18"],"\x7F"=>[9,"\x18"],"\x80"=>[94,"\x190"],"\x81"=>[76,"\x190"],"\x82"=>[104,"\x190"],"\x83"=>[16,"\x190"],"\x84"=>[94,"\x191"],"\x85"=>[76,"\x191"],"\x86"=>[104,"\x191"],"\x87"=>[16,"\x191"],"\x88"=>[94,"\x192"],"\x89"=>[76,"\x192"],"\x8A"=>[104,"\x192"],"\x8B"=>[16,"\x192"],"\x8C"=>[94,"\x19a"],"\x8D"=>[76,"\x19a"],"\x8E"=>[104,"\x19a"],"\x8F"=>[16,"\x19a"],"\x90"=>[94,"\x19c"],"\x91"=>[76,"\x19c"],"\x92"=>[104,"\x19c"],"\x93"=>[16,"\x19c"],"\x94"=>[94,"\x19e"],"\x95"=>[76,"\x19e"],"\x96"=>[104,"\x19e"],"\x97"=>[16,"\x19e"],"\x98"=>[94,"\x19i"],"\x99"=>[76,"\x19i"],"\x9A"=>[104,"\x19i"],"\x9B"=>[16,"\x19i"],"\x9C"=>[94,"\x19o"],"\x9D"=>[76,"\x19o"],"\x9E"=>[104,"\x19o"],"\x9F"=>[16,"\x19o"],"\xA0"=>[94,"\x19s"],"\xA1"=>[76,"\x19s"],"\xA2"=>[104,"\x19s"],"\xA3"=>[16,"\x19s"],"\xA4"=>[94,"\x19t"],"\xA5"=>[76,"\x19t"],"\xA6"=>[104,"\x19t"],"\xA7"=>[16,"\x19t"],"\xA8"=>[77,"\x19\x20"],"\xA9"=>[18,"\x19\x20"],"\xAA"=>[77,"\x19\x25"],"\xAB"=>[18,"\x19\x25"],"\xAC"=>[77,"\x19-"],"\xAD"=>[18,"\x19-"],"\xAE"=>[77,"\x19."],"\xAF"=>[18,"\x19."],"\xB0"=>[77,"\x19\x2F"],"\xB1"=>[18,"\x19\x2F"],"\xB2"=>[77,"\x193"],"\xB3"=>[18,"\x193"],"\xB4"=>[77,"\x194"],"\xB5"=>[18,"\x194"],"\xB6"=>[77,"\x195"],"\xB7"=>[18,"\x195"],"\xB8"=>[77,"\x196"],"\xB9"=>[18,"\x196"],"\xBA"=>[77,"\x197"],"\xBB"=>[18,"\x197"],"\xBC"=>[77,"\x198"],"\xBD"=>[18,"\x198"],"\xBE"=>[77,"\x199"],"\xBF"=>[18,"\x199"],"\xC0"=>[77,"\x19\x3D"],"\xC1"=>[18,"\x19\x3D"],"\xC2"=>[77,"\x19A"],"\xC3"=>[18,"\x19A"],"\xC4"=>[77,"\x19_"],"\xC5"=>[18,"\x19_"],"\xC6"=>[77,"\x19b"],"\xC7"=>[18,"\x19b"],"\xC8"=>[77,"\x19d"],"\xC9"=>[18,"\x19d"],"\xCA"=>[77,"\x19f"],"\xCB"=>[18,"\x19f"],"\xCC"=>[77,"\x19g"],"\xCD"=>[18,"\x19g"],"\xCE"=>[77,"\x19h"],"\xCF"=>[18,"\x19h"],"\xD0"=>[77,"\x19l"],"\xD1"=>[18,"\x19l"],"\xD2"=>[77,"\x19m"],"\xD3"=>[18,"\x19m"],"\xD4"=>[77,"\x19n"],"\xD5"=>[18,"\x19n"],"\xD6"=>[77,"\x19p"],"\xD7"=>[18,"\x19p"],"\xD8"=>[77,"\x19r"],"\xD9"=>[18,"\x19r"],"\xDA"=>[77,"\x19u"],"\xDB"=>[18,"\x19u"],"\xDC"=>[0,"\x19\x3A"],"\xDD"=>[0,"\x19B"],"\xDE"=>[0,"\x19C"],"\xDF"=>[0,"\x19D"],"\xE0"=>[0,"\x19E"],"\xE1"=>[0,"\x19F"],"\xE2"=>[0,"\x19G"],"\xE3"=>[0,"\x19H"],"\xE4"=>[0,"\x19I"],"\xE5"=>[0,"\x19J"],"\xE6"=>[0,"\x19K"],"\xE7"=>[0,"\x19L"],"\xE8"=>[0,"\x19M"],"\xE9"=>[0,"\x19N"],"\xEA"=>[0,"\x19O"],"\xEB"=>[0,"\x19P"],"\xEC"=>[0,"\x19Q"],"\xED"=>[0,"\x19R"],"\xEE"=>[0,"\x19S"],"\xEF"=>[0,"\x19T"],"\xF0"=>[0,"\x19U"],"\xF1"=>[0,"\x19V"],"\xF2"=>[0,"\x19W"],"\xF3"=>[0,"\x19Y"],"\xF4"=>[0,"\x19j"],"\xF5"=>[0,"\x19k"],"\xF6"=>[0,"\x19q"],"\xF7"=>[0,"\x19v"],"\xF8"=>[0,"\x19w"],"\xF9"=>[0,"\x19x"],"\xFA"=>[0,"\x19y"],"\xFB"=>[0,"\x19z"],"\xFC"=>[82,"\x19"],"\xFD"=>[87,"\x19"],"\xFE"=>[130,"\x19"],"\xFF"=>[9,"\x19"],],["\x00"=>[77,"\x180"],"\x01"=>[18,"\x180"],"\x02"=>[77,"\x181"],"\x03"=>[18,"\x181"],"\x04"=>[77,"\x182"],"\x05"=>[18,"\x182"],"\x06"=>[77,"\x18a"],"\x07"=>[18,"\x18a"],"\x08"=>[77,"\x18c"],"\x09"=>[18,"\x18c"],"\x0A"=>[77,"\x18e"],"\x0B"=>[18,"\x18e"],"\x0C"=>[77,"\x18i"],"\x0D"=>[18,"\x18i"],"\x0E"=>[77,"\x18o"],"\x0F"=>[18,"\x18o"],"\x10"=>[77,"\x18s"],"\x11"=>[18,"\x18s"],"\x12"=>[77,"\x18t"],"\x13"=>[18,"\x18t"],"\x14"=>[0,"\x18\x20"],"\x15"=>[0,"\x18\x25"],"\x16"=>[0,"\x18-"],"\x17"=>[0,"\x18."],"\x18"=>[0,"\x18\x2F"],"\x19"=>[0,"\x183"],"\x1A"=>[0,"\x184"],"\x1B"=>[0,"\x185"],"\x1C"=>[0,"\x186"],"\x1D"=>[0,"\x187"],"\x1E"=>[0,"\x188"],"\x1F"=>[0,"\x189"],"\x20"=>[0,"\x18\x3D"],"\x21"=>[0,"\x18A"],"\x22"=>[0,"\x18_"],"\x23"=>[0,"\x18b"],"\x24"=>[0,"\x18d"],"\x25"=>[0,"\x18f"],"\x26"=>[0,"\x18g"],"\x27"=>[0,"\x18h"],"\x28"=>[0,"\x18l"],"\x29"=>[0,"\x18m"],"\x2A"=>[0,"\x18n"],"\x2B"=>[0,"\x18p"],"\x2C"=>[0,"\x18r"],"-"=>[0,"\x18u"],"."=>[100,"\x18"],"\x2F"=>[110,"\x18"],[111,"\x18"],[115,"\x18"],[116,"\x18"],[118,"\x18"],[119,"\x18"],[122,"\x18"],[123,"\x18"],[125,"\x18"],[126,"\x18"],[129,"\x18"],"\x3A"=>[143,"\x18"],"\x3B"=>[148,"\x18"],"\x3C"=>[151,"\x18"],"\x3D"=>[153,"\x18"],"\x3E"=>[83,"\x18"],"\x3F"=>[10,"\x18"],"\x40"=>[77,"\x190"],"A"=>[18,"\x190"],"B"=>[77,"\x191"],"C"=>[18,"\x191"],"D"=>[77,"\x192"],"E"=>[18,"\x192"],"F"=>[77,"\x19a"],"G"=>[18,"\x19a"],"H"=>[77,"\x19c"],"I"=>[18,"\x19c"],"J"=>[77,"\x19e"],"K"=>[18,"\x19e"],"L"=>[77,"\x19i"],"M"=>[18,"\x19i"],"N"=>[77,"\x19o"],"O"=>[18,"\x19o"],"P"=>[77,"\x19s"],"Q"=>[18,"\x19s"],"R"=>[77,"\x19t"],"S"=>[18,"\x19t"],"T"=>[0,"\x19\x20"],"U"=>[0,"\x19\x25"],"V"=>[0,"\x19-"],"W"=>[0,"\x19."],"X"=>[0,"\x19\x2F"],"Y"=>[0,"\x193"],"Z"=>[0,"\x194"],"\x5B"=>[0,"\x195"],"\x5C"=>[0,"\x196"],"\x5D"=>[0,"\x197"],"\x5E"=>[0,"\x198"],"_"=>[0,"\x199"],"\x60"=>[0,"\x19\x3D"],"a"=>[0,"\x19A"],"b"=>[0,"\x19_"],"c"=>[0,"\x19b"],"d"=>[0,"\x19d"],"e"=>[0,"\x19f"],"f"=>[0,"\x19g"],"g"=>[0,"\x19h"],"h"=>[0,"\x19l"],"i"=>[0,"\x19m"],"j"=>[0,"\x19n"],"k"=>[0,"\x19p"],"l"=>[0,"\x19r"],"m"=>[0,"\x19u"],"n"=>[100,"\x19"],"o"=>[110,"\x19"],"p"=>[111,"\x19"],"q"=>[115,"\x19"],"r"=>[116,"\x19"],"s"=>[118,"\x19"],"t"=>[119,"\x19"],"u"=>[122,"\x19"],"v"=>[123,"\x19"],"w"=>[125,"\x19"],"x"=>[126,"\x19"],"y"=>[129,"\x19"],"z"=>[143,"\x19"],"\x7B"=>[148,"\x19"],"\x7C"=>[151,"\x19"],"\x7D"=>[153,"\x19"],"~"=>[83,"\x19"],"\x7F"=>[10,"\x19"],"\x80"=>[77,"\x1A0"],"\x81"=>[18,"\x1A0"],"\x82"=>[77,"\x1A1"],"\x83"=>[18,"\x1A1"],"\x84"=>[77,"\x1A2"],"\x85"=>[18,"\x1A2"],"\x86"=>[77,"\x1Aa"],"\x87"=>[18,"\x1Aa"],"\x88"=>[77,"\x1Ac"],"\x89"=>[18,"\x1Ac"],"\x8A"=>[77,"\x1Ae"],"\x8B"=>[18,"\x1Ae"],"\x8C"=>[77,"\x1Ai"],"\x8D"=>[18,"\x1Ai"],"\x8E"=>[77,"\x1Ao"],"\x8F"=>[18,"\x1Ao"],"\x90"=>[77,"\x1As"],"\x91"=>[18,"\x1As"],"\x92"=>[77,"\x1At"],"\x93"=>[18,"\x1At"],"\x94"=>[0,"\x1A\x20"],"\x95"=>[0,"\x1A\x25"],"\x96"=>[0,"\x1A-"],"\x97"=>[0,"\x1A."],"\x98"=>[0,"\x1A\x2F"],"\x99"=>[0,"\x1A3"],"\x9A"=>[0,"\x1A4"],"\x9B"=>[0,"\x1A5"],"\x9C"=>[0,"\x1A6"],"\x9D"=>[0,"\x1A7"],"\x9E"=>[0,"\x1A8"],"\x9F"=>[0,"\x1A9"],"\xA0"=>[0,"\x1A\x3D"],"\xA1"=>[0,"\x1AA"],"\xA2"=>[0,"\x1A_"],"\xA3"=>[0,"\x1Ab"],"\xA4"=>[0,"\x1Ad"],"\xA5"=>[0,"\x1Af"],"\xA6"=>[0,"\x1Ag"],"\xA7"=>[0,"\x1Ah"],"\xA8"=>[0,"\x1Al"],"\xA9"=>[0,"\x1Am"],"\xAA"=>[0,"\x1An"],"\xAB"=>[0,"\x1Ap"],"\xAC"=>[0,"\x1Ar"],"\xAD"=>[0,"\x1Au"],"\xAE"=>[100,"\x1A"],"\xAF"=>[110,"\x1A"],"\xB0"=>[111,"\x1A"],"\xB1"=>[115,"\x1A"],"\xB2"=>[116,"\x1A"],"\xB3"=>[118,"\x1A"],"\xB4"=>[119,"\x1A"],"\xB5"=>[122,"\x1A"],"\xB6"=>[123,"\x1A"],"\xB7"=>[125,"\x1A"],"\xB8"=>[126,"\x1A"],"\xB9"=>[129,"\x1A"],"\xBA"=>[143,"\x1A"],"\xBB"=>[148,"\x1A"],"\xBC"=>[151,"\x1A"],"\xBD"=>[153,"\x1A"],"\xBE"=>[83,"\x1A"],"\xBF"=>[10,"\x1A"],"\xC0"=>[77,"\x1B0"],"\xC1"=>[18,"\x1B0"],"\xC2"=>[77,"\x1B1"],"\xC3"=>[18,"\x1B1"],"\xC4"=>[77,"\x1B2"],"\xC5"=>[18,"\x1B2"],"\xC6"=>[77,"\x1Ba"],"\xC7"=>[18,"\x1Ba"],"\xC8"=>[77,"\x1Bc"],"\xC9"=>[18,"\x1Bc"],"\xCA"=>[77,"\x1Be"],"\xCB"=>[18,"\x1Be"],"\xCC"=>[77,"\x1Bi"],"\xCD"=>[18,"\x1Bi"],"\xCE"=>[77,"\x1Bo"],"\xCF"=>[18,"\x1Bo"],"\xD0"=>[77,"\x1Bs"],"\xD1"=>[18,"\x1Bs"],"\xD2"=>[77,"\x1Bt"],"\xD3"=>[18,"\x1Bt"],"\xD4"=>[0,"\x1B\x20"],"\xD5"=>[0,"\x1B\x25"],"\xD6"=>[0,"\x1B-"],"\xD7"=>[0,"\x1B."],"\xD8"=>[0,"\x1B\x2F"],"\xD9"=>[0,"\x1B3"],"\xDA"=>[0,"\x1B4"],"\xDB"=>[0,"\x1B5"],"\xDC"=>[0,"\x1B6"],"\xDD"=>[0,"\x1B7"],"\xDE"=>[0,"\x1B8"],"\xDF"=>[0,"\x1B9"],"\xE0"=>[0,"\x1B\x3D"],"\xE1"=>[0,"\x1BA"],"\xE2"=>[0,"\x1B_"],"\xE3"=>[0,"\x1Bb"],"\xE4"=>[0,"\x1Bd"],"\xE5"=>[0,"\x1Bf"],"\xE6"=>[0,"\x1Bg"],"\xE7"=>[0,"\x1Bh"],"\xE8"=>[0,"\x1Bl"],"\xE9"=>[0,"\x1Bm"],"\xEA"=>[0,"\x1Bn"],"\xEB"=>[0,"\x1Bp"],"\xEC"=>[0,"\x1Br"],"\xED"=>[0,"\x1Bu"],"\xEE"=>[100,"\x1B"],"\xEF"=>[110,"\x1B"],"\xF0"=>[111,"\x1B"],"\xF1"=>[115,"\x1B"],"\xF2"=>[116,"\x1B"],"\xF3"=>[118,"\x1B"],"\xF4"=>[119,"\x1B"],"\xF5"=>[122,"\x1B"],"\xF6"=>[123,"\x1B"],"\xF7"=>[125,"\x1B"],"\xF8"=>[126,"\x1B"],"\xF9"=>[129,"\x1B"],"\xFA"=>[143,"\x1B"],"\xFB"=>[148,"\x1B"],"\xFC"=>[151,"\x1B"],"\xFD"=>[153,"\x1B"],"\xFE"=>[83,"\x1B"],"\xFF"=>[10,"\x1B"],],["\x00"=>[94,"\x1A0"],"\x01"=>[76,"\x1A0"],"\x02"=>[104,"\x1A0"],"\x03"=>[16,"\x1A0"],"\x04"=>[94,"\x1A1"],"\x05"=>[76,"\x1A1"],"\x06"=>[104,"\x1A1"],"\x07"=>[16,"\x1A1"],"\x08"=>[94,"\x1A2"],"\x09"=>[76,"\x1A2"],"\x0A"=>[104,"\x1A2"],"\x0B"=>[16,"\x1A2"],"\x0C"=>[94,"\x1Aa"],"\x0D"=>[76,"\x1Aa"],"\x0E"=>[104,"\x1Aa"],"\x0F"=>[16,"\x1Aa"],"\x10"=>[94,"\x1Ac"],"\x11"=>[76,"\x1Ac"],"\x12"=>[104,"\x1Ac"],"\x13"=>[16,"\x1Ac"],"\x14"=>[94,"\x1Ae"],"\x15"=>[76,"\x1Ae"],"\x16"=>[104,"\x1Ae"],"\x17"=>[16,"\x1Ae"],"\x18"=>[94,"\x1Ai"],"\x19"=>[76,"\x1Ai"],"\x1A"=>[104,"\x1Ai"],"\x1B"=>[16,"\x1Ai"],"\x1C"=>[94,"\x1Ao"],"\x1D"=>[76,"\x1Ao"],"\x1E"=>[104,"\x1Ao"],"\x1F"=>[16,"\x1Ao"],"\x20"=>[94,"\x1As"],"\x21"=>[76,"\x1As"],"\x22"=>[104,"\x1As"],"\x23"=>[16,"\x1As"],"\x24"=>[94,"\x1At"],"\x25"=>[76,"\x1At"],"\x26"=>[104,"\x1At"],"\x27"=>[16,"\x1At"],"\x28"=>[77,"\x1A\x20"],"\x29"=>[18,"\x1A\x20"],"\x2A"=>[77,"\x1A\x25"],"\x2B"=>[18,"\x1A\x25"],"\x2C"=>[77,"\x1A-"],"-"=>[18,"\x1A-"],"."=>[77,"\x1A."],"\x2F"=>[18,"\x1A."],[77,"\x1A\x2F"],[18,"\x1A\x2F"],[77,"\x1A3"],[18,"\x1A3"],[77,"\x1A4"],[18,"\x1A4"],[77,"\x1A5"],[18,"\x1A5"],[77,"\x1A6"],[18,"\x1A6"],"\x3A"=>[77,"\x1A7"],"\x3B"=>[18,"\x1A7"],"\x3C"=>[77,"\x1A8"],"\x3D"=>[18,"\x1A8"],"\x3E"=>[77,"\x1A9"],"\x3F"=>[18,"\x1A9"],"\x40"=>[77,"\x1A\x3D"],"A"=>[18,"\x1A\x3D"],"B"=>[77,"\x1AA"],"C"=>[18,"\x1AA"],"D"=>[77,"\x1A_"],"E"=>[18,"\x1A_"],"F"=>[77,"\x1Ab"],"G"=>[18,"\x1Ab"],"H"=>[77,"\x1Ad"],"I"=>[18,"\x1Ad"],"J"=>[77,"\x1Af"],"K"=>[18,"\x1Af"],"L"=>[77,"\x1Ag"],"M"=>[18,"\x1Ag"],"N"=>[77,"\x1Ah"],"O"=>[18,"\x1Ah"],"P"=>[77,"\x1Al"],"Q"=>[18,"\x1Al"],"R"=>[77,"\x1Am"],"S"=>[18,"\x1Am"],"T"=>[77,"\x1An"],"U"=>[18,"\x1An"],"V"=>[77,"\x1Ap"],"W"=>[18,"\x1Ap"],"X"=>[77,"\x1Ar"],"Y"=>[18,"\x1Ar"],"Z"=>[77,"\x1Au"],"\x5B"=>[18,"\x1Au"],"\x5C"=>[0,"\x1A\x3A"],"\x5D"=>[0,"\x1AB"],"\x5E"=>[0,"\x1AC"],"_"=>[0,"\x1AD"],"\x60"=>[0,"\x1AE"],"a"=>[0,"\x1AF"],"b"=>[0,"\x1AG"],"c"=>[0,"\x1AH"],"d"=>[0,"\x1AI"],"e"=>[0,"\x1AJ"],"f"=>[0,"\x1AK"],"g"=>[0,"\x1AL"],"h"=>[0,"\x1AM"],"i"=>[0,"\x1AN"],"j"=>[0,"\x1AO"],"k"=>[0,"\x1AP"],"l"=>[0,"\x1AQ"],"m"=>[0,"\x1AR"],"n"=>[0,"\x1AS"],"o"=>[0,"\x1AT"],"p"=>[0,"\x1AU"],"q"=>[0,"\x1AV"],"r"=>[0,"\x1AW"],"s"=>[0,"\x1AY"],"t"=>[0,"\x1Aj"],"u"=>[0,"\x1Ak"],"v"=>[0,"\x1Aq"],"w"=>[0,"\x1Av"],"x"=>[0,"\x1Aw"],"y"=>[0,"\x1Ax"],"z"=>[0,"\x1Ay"],"\x7B"=>[0,"\x1Az"],"\x7C"=>[82,"\x1A"],"\x7D"=>[87,"\x1A"],"~"=>[130,"\x1A"],"\x7F"=>[9,"\x1A"],"\x80"=>[94,"\x1B0"],"\x81"=>[76,"\x1B0"],"\x82"=>[104,"\x1B0"],"\x83"=>[16,"\x1B0"],"\x84"=>[94,"\x1B1"],"\x85"=>[76,"\x1B1"],"\x86"=>[104,"\x1B1"],"\x87"=>[16,"\x1B1"],"\x88"=>[94,"\x1B2"],"\x89"=>[76,"\x1B2"],"\x8A"=>[104,"\x1B2"],"\x8B"=>[16,"\x1B2"],"\x8C"=>[94,"\x1Ba"],"\x8D"=>[76,"\x1Ba"],"\x8E"=>[104,"\x1Ba"],"\x8F"=>[16,"\x1Ba"],"\x90"=>[94,"\x1Bc"],"\x91"=>[76,"\x1Bc"],"\x92"=>[104,"\x1Bc"],"\x93"=>[16,"\x1Bc"],"\x94"=>[94,"\x1Be"],"\x95"=>[76,"\x1Be"],"\x96"=>[104,"\x1Be"],"\x97"=>[16,"\x1Be"],"\x98"=>[94,"\x1Bi"],"\x99"=>[76,"\x1Bi"],"\x9A"=>[104,"\x1Bi"],"\x9B"=>[16,"\x1Bi"],"\x9C"=>[94,"\x1Bo"],"\x9D"=>[76,"\x1Bo"],"\x9E"=>[104,"\x1Bo"],"\x9F"=>[16,"\x1Bo"],"\xA0"=>[94,"\x1Bs"],"\xA1"=>[76,"\x1Bs"],"\xA2"=>[104,"\x1Bs"],"\xA3"=>[16,"\x1Bs"],"\xA4"=>[94,"\x1Bt"],"\xA5"=>[76,"\x1Bt"],"\xA6"=>[104,"\x1Bt"],"\xA7"=>[16,"\x1Bt"],"\xA8"=>[77,"\x1B\x20"],"\xA9"=>[18,"\x1B\x20"],"\xAA"=>[77,"\x1B\x25"],"\xAB"=>[18,"\x1B\x25"],"\xAC"=>[77,"\x1B-"],"\xAD"=>[18,"\x1B-"],"\xAE"=>[77,"\x1B."],"\xAF"=>[18,"\x1B."],"\xB0"=>[77,"\x1B\x2F"],"\xB1"=>[18,"\x1B\x2F"],"\xB2"=>[77,"\x1B3"],"\xB3"=>[18,"\x1B3"],"\xB4"=>[77,"\x1B4"],"\xB5"=>[18,"\x1B4"],"\xB6"=>[77,"\x1B5"],"\xB7"=>[18,"\x1B5"],"\xB8"=>[77,"\x1B6"],"\xB9"=>[18,"\x1B6"],"\xBA"=>[77,"\x1B7"],"\xBB"=>[18,"\x1B7"],"\xBC"=>[77,"\x1B8"],"\xBD"=>[18,"\x1B8"],"\xBE"=>[77,"\x1B9"],"\xBF"=>[18,"\x1B9"],"\xC0"=>[77,"\x1B\x3D"],"\xC1"=>[18,"\x1B\x3D"],"\xC2"=>[77,"\x1BA"],"\xC3"=>[18,"\x1BA"],"\xC4"=>[77,"\x1B_"],"\xC5"=>[18,"\x1B_"],"\xC6"=>[77,"\x1Bb"],"\xC7"=>[18,"\x1Bb"],"\xC8"=>[77,"\x1Bd"],"\xC9"=>[18,"\x1Bd"],"\xCA"=>[77,"\x1Bf"],"\xCB"=>[18,"\x1Bf"],"\xCC"=>[77,"\x1Bg"],"\xCD"=>[18,"\x1Bg"],"\xCE"=>[77,"\x1Bh"],"\xCF"=>[18,"\x1Bh"],"\xD0"=>[77,"\x1Bl"],"\xD1"=>[18,"\x1Bl"],"\xD2"=>[77,"\x1Bm"],"\xD3"=>[18,"\x1Bm"],"\xD4"=>[77,"\x1Bn"],"\xD5"=>[18,"\x1Bn"],"\xD6"=>[77,"\x1Bp"],"\xD7"=>[18,"\x1Bp"],"\xD8"=>[77,"\x1Br"],"\xD9"=>[18,"\x1Br"],"\xDA"=>[77,"\x1Bu"],"\xDB"=>[18,"\x1Bu"],"\xDC"=>[0,"\x1B\x3A"],"\xDD"=>[0,"\x1BB"],"\xDE"=>[0,"\x1BC"],"\xDF"=>[0,"\x1BD"],"\xE0"=>[0,"\x1BE"],"\xE1"=>[0,"\x1BF"],"\xE2"=>[0,"\x1BG"],"\xE3"=>[0,"\x1BH"],"\xE4"=>[0,"\x1BI"],"\xE5"=>[0,"\x1BJ"],"\xE6"=>[0,"\x1BK"],"\xE7"=>[0,"\x1BL"],"\xE8"=>[0,"\x1BM"],"\xE9"=>[0,"\x1BN"],"\xEA"=>[0,"\x1BO"],"\xEB"=>[0,"\x1BP"],"\xEC"=>[0,"\x1BQ"],"\xED"=>[0,"\x1BR"],"\xEE"=>[0,"\x1BS"],"\xEF"=>[0,"\x1BT"],"\xF0"=>[0,"\x1BU"],"\xF1"=>[0,"\x1BV"],"\xF2"=>[0,"\x1BW"],"\xF3"=>[0,"\x1BY"],"\xF4"=>[0,"\x1Bj"],"\xF5"=>[0,"\x1Bk"],"\xF6"=>[0,"\x1Bq"],"\xF7"=>[0,"\x1Bv"],"\xF8"=>[0,"\x1Bw"],"\xF9"=>[0,"\x1Bx"],"\xFA"=>[0,"\x1By"],"\xFB"=>[0,"\x1Bz"],"\xFC"=>[82,"\x1B"],"\xFD"=>[87,"\x1B"],"\xFE"=>[130,"\x1B"],"\xFF"=>[9,"\x1B"],],["\x00"=>[94,"\x1C0"],"\x01"=>[76,"\x1C0"],"\x02"=>[104,"\x1C0"],"\x03"=>[16,"\x1C0"],"\x04"=>[94,"\x1C1"],"\x05"=>[76,"\x1C1"],"\x06"=>[104,"\x1C1"],"\x07"=>[16,"\x1C1"],"\x08"=>[94,"\x1C2"],"\x09"=>[76,"\x1C2"],"\x0A"=>[104,"\x1C2"],"\x0B"=>[16,"\x1C2"],"\x0C"=>[94,"\x1Ca"],"\x0D"=>[76,"\x1Ca"],"\x0E"=>[104,"\x1Ca"],"\x0F"=>[16,"\x1Ca"],"\x10"=>[94,"\x1Cc"],"\x11"=>[76,"\x1Cc"],"\x12"=>[104,"\x1Cc"],"\x13"=>[16,"\x1Cc"],"\x14"=>[94,"\x1Ce"],"\x15"=>[76,"\x1Ce"],"\x16"=>[104,"\x1Ce"],"\x17"=>[16,"\x1Ce"],"\x18"=>[94,"\x1Ci"],"\x19"=>[76,"\x1Ci"],"\x1A"=>[104,"\x1Ci"],"\x1B"=>[16,"\x1Ci"],"\x1C"=>[94,"\x1Co"],"\x1D"=>[76,"\x1Co"],"\x1E"=>[104,"\x1Co"],"\x1F"=>[16,"\x1Co"],"\x20"=>[94,"\x1Cs"],"\x21"=>[76,"\x1Cs"],"\x22"=>[104,"\x1Cs"],"\x23"=>[16,"\x1Cs"],"\x24"=>[94,"\x1Ct"],"\x25"=>[76,"\x1Ct"],"\x26"=>[104,"\x1Ct"],"\x27"=>[16,"\x1Ct"],"\x28"=>[77,"\x1C\x20"],"\x29"=>[18,"\x1C\x20"],"\x2A"=>[77,"\x1C\x25"],"\x2B"=>[18,"\x1C\x25"],"\x2C"=>[77,"\x1C-"],"-"=>[18,"\x1C-"],"."=>[77,"\x1C."],"\x2F"=>[18,"\x1C."],[77,"\x1C\x2F"],[18,"\x1C\x2F"],[77,"\x1C3"],[18,"\x1C3"],[77,"\x1C4"],[18,"\x1C4"],[77,"\x1C5"],[18,"\x1C5"],[77,"\x1C6"],[18,"\x1C6"],"\x3A"=>[77,"\x1C7"],"\x3B"=>[18,"\x1C7"],"\x3C"=>[77,"\x1C8"],"\x3D"=>[18,"\x1C8"],"\x3E"=>[77,"\x1C9"],"\x3F"=>[18,"\x1C9"],"\x40"=>[77,"\x1C\x3D"],"A"=>[18,"\x1C\x3D"],"B"=>[77,"\x1CA"],"C"=>[18,"\x1CA"],"D"=>[77,"\x1C_"],"E"=>[18,"\x1C_"],"F"=>[77,"\x1Cb"],"G"=>[18,"\x1Cb"],"H"=>[77,"\x1Cd"],"I"=>[18,"\x1Cd"],"J"=>[77,"\x1Cf"],"K"=>[18,"\x1Cf"],"L"=>[77,"\x1Cg"],"M"=>[18,"\x1Cg"],"N"=>[77,"\x1Ch"],"O"=>[18,"\x1Ch"],"P"=>[77,"\x1Cl"],"Q"=>[18,"\x1Cl"],"R"=>[77,"\x1Cm"],"S"=>[18,"\x1Cm"],"T"=>[77,"\x1Cn"],"U"=>[18,"\x1Cn"],"V"=>[77,"\x1Cp"],"W"=>[18,"\x1Cp"],"X"=>[77,"\x1Cr"],"Y"=>[18,"\x1Cr"],"Z"=>[77,"\x1Cu"],"\x5B"=>[18,"\x1Cu"],"\x5C"=>[0,"\x1C\x3A"],"\x5D"=>[0,"\x1CB"],"\x5E"=>[0,"\x1CC"],"_"=>[0,"\x1CD"],"\x60"=>[0,"\x1CE"],"a"=>[0,"\x1CF"],"b"=>[0,"\x1CG"],"c"=>[0,"\x1CH"],"d"=>[0,"\x1CI"],"e"=>[0,"\x1CJ"],"f"=>[0,"\x1CK"],"g"=>[0,"\x1CL"],"h"=>[0,"\x1CM"],"i"=>[0,"\x1CN"],"j"=>[0,"\x1CO"],"k"=>[0,"\x1CP"],"l"=>[0,"\x1CQ"],"m"=>[0,"\x1CR"],"n"=>[0,"\x1CS"],"o"=>[0,"\x1CT"],"p"=>[0,"\x1CU"],"q"=>[0,"\x1CV"],"r"=>[0,"\x1CW"],"s"=>[0,"\x1CY"],"t"=>[0,"\x1Cj"],"u"=>[0,"\x1Ck"],"v"=>[0,"\x1Cq"],"w"=>[0,"\x1Cv"],"x"=>[0,"\x1Cw"],"y"=>[0,"\x1Cx"],"z"=>[0,"\x1Cy"],"\x7B"=>[0,"\x1Cz"],"\x7C"=>[82,"\x1C"],"\x7D"=>[87,"\x1C"],"~"=>[130,"\x1C"],"\x7F"=>[9,"\x1C"],"\x80"=>[94,"\x1D0"],"\x81"=>[76,"\x1D0"],"\x82"=>[104,"\x1D0"],"\x83"=>[16,"\x1D0"],"\x84"=>[94,"\x1D1"],"\x85"=>[76,"\x1D1"],"\x86"=>[104,"\x1D1"],"\x87"=>[16,"\x1D1"],"\x88"=>[94,"\x1D2"],"\x89"=>[76,"\x1D2"],"\x8A"=>[104,"\x1D2"],"\x8B"=>[16,"\x1D2"],"\x8C"=>[94,"\x1Da"],"\x8D"=>[76,"\x1Da"],"\x8E"=>[104,"\x1Da"],"\x8F"=>[16,"\x1Da"],"\x90"=>[94,"\x1Dc"],"\x91"=>[76,"\x1Dc"],"\x92"=>[104,"\x1Dc"],"\x93"=>[16,"\x1Dc"],"\x94"=>[94,"\x1De"],"\x95"=>[76,"\x1De"],"\x96"=>[104,"\x1De"],"\x97"=>[16,"\x1De"],"\x98"=>[94,"\x1Di"],"\x99"=>[76,"\x1Di"],"\x9A"=>[104,"\x1Di"],"\x9B"=>[16,"\x1Di"],"\x9C"=>[94,"\x1Do"],"\x9D"=>[76,"\x1Do"],"\x9E"=>[104,"\x1Do"],"\x9F"=>[16,"\x1Do"],"\xA0"=>[94,"\x1Ds"],"\xA1"=>[76,"\x1Ds"],"\xA2"=>[104,"\x1Ds"],"\xA3"=>[16,"\x1Ds"],"\xA4"=>[94,"\x1Dt"],"\xA5"=>[76,"\x1Dt"],"\xA6"=>[104,"\x1Dt"],"\xA7"=>[16,"\x1Dt"],"\xA8"=>[77,"\x1D\x20"],"\xA9"=>[18,"\x1D\x20"],"\xAA"=>[77,"\x1D\x25"],"\xAB"=>[18,"\x1D\x25"],"\xAC"=>[77,"\x1D-"],"\xAD"=>[18,"\x1D-"],"\xAE"=>[77,"\x1D."],"\xAF"=>[18,"\x1D."],"\xB0"=>[77,"\x1D\x2F"],"\xB1"=>[18,"\x1D\x2F"],"\xB2"=>[77,"\x1D3"],"\xB3"=>[18,"\x1D3"],"\xB4"=>[77,"\x1D4"],"\xB5"=>[18,"\x1D4"],"\xB6"=>[77,"\x1D5"],"\xB7"=>[18,"\x1D5"],"\xB8"=>[77,"\x1D6"],"\xB9"=>[18,"\x1D6"],"\xBA"=>[77,"\x1D7"],"\xBB"=>[18,"\x1D7"],"\xBC"=>[77,"\x1D8"],"\xBD"=>[18,"\x1D8"],"\xBE"=>[77,"\x1D9"],"\xBF"=>[18,"\x1D9"],"\xC0"=>[77,"\x1D\x3D"],"\xC1"=>[18,"\x1D\x3D"],"\xC2"=>[77,"\x1DA"],"\xC3"=>[18,"\x1DA"],"\xC4"=>[77,"\x1D_"],"\xC5"=>[18,"\x1D_"],"\xC6"=>[77,"\x1Db"],"\xC7"=>[18,"\x1Db"],"\xC8"=>[77,"\x1Dd"],"\xC9"=>[18,"\x1Dd"],"\xCA"=>[77,"\x1Df"],"\xCB"=>[18,"\x1Df"],"\xCC"=>[77,"\x1Dg"],"\xCD"=>[18,"\x1Dg"],"\xCE"=>[77,"\x1Dh"],"\xCF"=>[18,"\x1Dh"],"\xD0"=>[77,"\x1Dl"],"\xD1"=>[18,"\x1Dl"],"\xD2"=>[77,"\x1Dm"],"\xD3"=>[18,"\x1Dm"],"\xD4"=>[77,"\x1Dn"],"\xD5"=>[18,"\x1Dn"],"\xD6"=>[77,"\x1Dp"],"\xD7"=>[18,"\x1Dp"],"\xD8"=>[77,"\x1Dr"],"\xD9"=>[18,"\x1Dr"],"\xDA"=>[77,"\x1Du"],"\xDB"=>[18,"\x1Du"],"\xDC"=>[0,"\x1D\x3A"],"\xDD"=>[0,"\x1DB"],"\xDE"=>[0,"\x1DC"],"\xDF"=>[0,"\x1DD"],"\xE0"=>[0,"\x1DE"],"\xE1"=>[0,"\x1DF"],"\xE2"=>[0,"\x1DG"],"\xE3"=>[0,"\x1DH"],"\xE4"=>[0,"\x1DI"],"\xE5"=>[0,"\x1DJ"],"\xE6"=>[0,"\x1DK"],"\xE7"=>[0,"\x1DL"],"\xE8"=>[0,"\x1DM"],"\xE9"=>[0,"\x1DN"],"\xEA"=>[0,"\x1DO"],"\xEB"=>[0,"\x1DP"],"\xEC"=>[0,"\x1DQ"],"\xED"=>[0,"\x1DR"],"\xEE"=>[0,"\x1DS"],"\xEF"=>[0,"\x1DT"],"\xF0"=>[0,"\x1DU"],"\xF1"=>[0,"\x1DV"],"\xF2"=>[0,"\x1DW"],"\xF3"=>[0,"\x1DY"],"\xF4"=>[0,"\x1Dj"],"\xF5"=>[0,"\x1Dk"],"\xF6"=>[0,"\x1Dq"],"\xF7"=>[0,"\x1Dv"],"\xF8"=>[0,"\x1Dw"],"\xF9"=>[0,"\x1Dx"],"\xFA"=>[0,"\x1Dy"],"\xFB"=>[0,"\x1Dz"],"\xFC"=>[82,"\x1D"],"\xFD"=>[87,"\x1D"],"\xFE"=>[130,"\x1D"],"\xFF"=>[9,"\x1D"],],["\x00"=>[77,"\x1C0"],"\x01"=>[18,"\x1C0"],"\x02"=>[77,"\x1C1"],"\x03"=>[18,"\x1C1"],"\x04"=>[77,"\x1C2"],"\x05"=>[18,"\x1C2"],"\x06"=>[77,"\x1Ca"],"\x07"=>[18,"\x1Ca"],"\x08"=>[77,"\x1Cc"],"\x09"=>[18,"\x1Cc"],"\x0A"=>[77,"\x1Ce"],"\x0B"=>[18,"\x1Ce"],"\x0C"=>[77,"\x1Ci"],"\x0D"=>[18,"\x1Ci"],"\x0E"=>[77,"\x1Co"],"\x0F"=>[18,"\x1Co"],"\x10"=>[77,"\x1Cs"],"\x11"=>[18,"\x1Cs"],"\x12"=>[77,"\x1Ct"],"\x13"=>[18,"\x1Ct"],"\x14"=>[0,"\x1C\x20"],"\x15"=>[0,"\x1C\x25"],"\x16"=>[0,"\x1C-"],"\x17"=>[0,"\x1C."],"\x18"=>[0,"\x1C\x2F"],"\x19"=>[0,"\x1C3"],"\x1A"=>[0,"\x1C4"],"\x1B"=>[0,"\x1C5"],"\x1C"=>[0,"\x1C6"],"\x1D"=>[0,"\x1C7"],"\x1E"=>[0,"\x1C8"],"\x1F"=>[0,"\x1C9"],"\x20"=>[0,"\x1C\x3D"],"\x21"=>[0,"\x1CA"],"\x22"=>[0,"\x1C_"],"\x23"=>[0,"\x1Cb"],"\x24"=>[0,"\x1Cd"],"\x25"=>[0,"\x1Cf"],"\x26"=>[0,"\x1Cg"],"\x27"=>[0,"\x1Ch"],"\x28"=>[0,"\x1Cl"],"\x29"=>[0,"\x1Cm"],"\x2A"=>[0,"\x1Cn"],"\x2B"=>[0,"\x1Cp"],"\x2C"=>[0,"\x1Cr"],"-"=>[0,"\x1Cu"],"."=>[100,"\x1C"],"\x2F"=>[110,"\x1C"],[111,"\x1C"],[115,"\x1C"],[116,"\x1C"],[118,"\x1C"],[119,"\x1C"],[122,"\x1C"],[123,"\x1C"],[125,"\x1C"],[126,"\x1C"],[129,"\x1C"],"\x3A"=>[143,"\x1C"],"\x3B"=>[148,"\x1C"],"\x3C"=>[151,"\x1C"],"\x3D"=>[153,"\x1C"],"\x3E"=>[83,"\x1C"],"\x3F"=>[10,"\x1C"],"\x40"=>[77,"\x1D0"],"A"=>[18,"\x1D0"],"B"=>[77,"\x1D1"],"C"=>[18,"\x1D1"],"D"=>[77,"\x1D2"],"E"=>[18,"\x1D2"],"F"=>[77,"\x1Da"],"G"=>[18,"\x1Da"],"H"=>[77,"\x1Dc"],"I"=>[18,"\x1Dc"],"J"=>[77,"\x1De"],"K"=>[18,"\x1De"],"L"=>[77,"\x1Di"],"M"=>[18,"\x1Di"],"N"=>[77,"\x1Do"],"O"=>[18,"\x1Do"],"P"=>[77,"\x1Ds"],"Q"=>[18,"\x1Ds"],"R"=>[77,"\x1Dt"],"S"=>[18,"\x1Dt"],"T"=>[0,"\x1D\x20"],"U"=>[0,"\x1D\x25"],"V"=>[0,"\x1D-"],"W"=>[0,"\x1D."],"X"=>[0,"\x1D\x2F"],"Y"=>[0,"\x1D3"],"Z"=>[0,"\x1D4"],"\x5B"=>[0,"\x1D5"],"\x5C"=>[0,"\x1D6"],"\x5D"=>[0,"\x1D7"],"\x5E"=>[0,"\x1D8"],"_"=>[0,"\x1D9"],"\x60"=>[0,"\x1D\x3D"],"a"=>[0,"\x1DA"],"b"=>[0,"\x1D_"],"c"=>[0,"\x1Db"],"d"=>[0,"\x1Dd"],"e"=>[0,"\x1Df"],"f"=>[0,"\x1Dg"],"g"=>[0,"\x1Dh"],"h"=>[0,"\x1Dl"],"i"=>[0,"\x1Dm"],"j"=>[0,"\x1Dn"],"k"=>[0,"\x1Dp"],"l"=>[0,"\x1Dr"],"m"=>[0,"\x1Du"],"n"=>[100,"\x1D"],"o"=>[110,"\x1D"],"p"=>[111,"\x1D"],"q"=>[115,"\x1D"],"r"=>[116,"\x1D"],"s"=>[118,"\x1D"],"t"=>[119,"\x1D"],"u"=>[122,"\x1D"],"v"=>[123,"\x1D"],"w"=>[125,"\x1D"],"x"=>[126,"\x1D"],"y"=>[129,"\x1D"],"z"=>[143,"\x1D"],"\x7B"=>[148,"\x1D"],"\x7C"=>[151,"\x1D"],"\x7D"=>[153,"\x1D"],"~"=>[83,"\x1D"],"\x7F"=>[10,"\x1D"],"\x80"=>[77,"\x1E0"],"\x81"=>[18,"\x1E0"],"\x82"=>[77,"\x1E1"],"\x83"=>[18,"\x1E1"],"\x84"=>[77,"\x1E2"],"\x85"=>[18,"\x1E2"],"\x86"=>[77,"\x1Ea"],"\x87"=>[18,"\x1Ea"],"\x88"=>[77,"\x1Ec"],"\x89"=>[18,"\x1Ec"],"\x8A"=>[77,"\x1Ee"],"\x8B"=>[18,"\x1Ee"],"\x8C"=>[77,"\x1Ei"],"\x8D"=>[18,"\x1Ei"],"\x8E"=>[77,"\x1Eo"],"\x8F"=>[18,"\x1Eo"],"\x90"=>[77,"\x1Es"],"\x91"=>[18,"\x1Es"],"\x92"=>[77,"\x1Et"],"\x93"=>[18,"\x1Et"],"\x94"=>[0,"\x1E\x20"],"\x95"=>[0,"\x1E\x25"],"\x96"=>[0,"\x1E-"],"\x97"=>[0,"\x1E."],"\x98"=>[0,"\x1E\x2F"],"\x99"=>[0,"\x1E3"],"\x9A"=>[0,"\x1E4"],"\x9B"=>[0,"\x1E5"],"\x9C"=>[0,"\x1E6"],"\x9D"=>[0,"\x1E7"],"\x9E"=>[0,"\x1E8"],"\x9F"=>[0,"\x1E9"],"\xA0"=>[0,"\x1E\x3D"],"\xA1"=>[0,"\x1EA"],"\xA2"=>[0,"\x1E_"],"\xA3"=>[0,"\x1Eb"],"\xA4"=>[0,"\x1Ed"],"\xA5"=>[0,"\x1Ef"],"\xA6"=>[0,"\x1Eg"],"\xA7"=>[0,"\x1Eh"],"\xA8"=>[0,"\x1El"],"\xA9"=>[0,"\x1Em"],"\xAA"=>[0,"\x1En"],"\xAB"=>[0,"\x1Ep"],"\xAC"=>[0,"\x1Er"],"\xAD"=>[0,"\x1Eu"],"\xAE"=>[100,"\x1E"],"\xAF"=>[110,"\x1E"],"\xB0"=>[111,"\x1E"],"\xB1"=>[115,"\x1E"],"\xB2"=>[116,"\x1E"],"\xB3"=>[118,"\x1E"],"\xB4"=>[119,"\x1E"],"\xB5"=>[122,"\x1E"],"\xB6"=>[123,"\x1E"],"\xB7"=>[125,"\x1E"],"\xB8"=>[126,"\x1E"],"\xB9"=>[129,"\x1E"],"\xBA"=>[143,"\x1E"],"\xBB"=>[148,"\x1E"],"\xBC"=>[151,"\x1E"],"\xBD"=>[153,"\x1E"],"\xBE"=>[83,"\x1E"],"\xBF"=>[10,"\x1E"],"\xC0"=>[77,"\x1F0"],"\xC1"=>[18,"\x1F0"],"\xC2"=>[77,"\x1F1"],"\xC3"=>[18,"\x1F1"],"\xC4"=>[77,"\x1F2"],"\xC5"=>[18,"\x1F2"],"\xC6"=>[77,"\x1Fa"],"\xC7"=>[18,"\x1Fa"],"\xC8"=>[77,"\x1Fc"],"\xC9"=>[18,"\x1Fc"],"\xCA"=>[77,"\x1Fe"],"\xCB"=>[18,"\x1Fe"],"\xCC"=>[77,"\x1Fi"],"\xCD"=>[18,"\x1Fi"],"\xCE"=>[77,"\x1Fo"],"\xCF"=>[18,"\x1Fo"],"\xD0"=>[77,"\x1Fs"],"\xD1"=>[18,"\x1Fs"],"\xD2"=>[77,"\x1Ft"],"\xD3"=>[18,"\x1Ft"],"\xD4"=>[0,"\x1F\x20"],"\xD5"=>[0,"\x1F\x25"],"\xD6"=>[0,"\x1F-"],"\xD7"=>[0,"\x1F."],"\xD8"=>[0,"\x1F\x2F"],"\xD9"=>[0,"\x1F3"],"\xDA"=>[0,"\x1F4"],"\xDB"=>[0,"\x1F5"],"\xDC"=>[0,"\x1F6"],"\xDD"=>[0,"\x1F7"],"\xDE"=>[0,"\x1F8"],"\xDF"=>[0,"\x1F9"],"\xE0"=>[0,"\x1F\x3D"],"\xE1"=>[0,"\x1FA"],"\xE2"=>[0,"\x1F_"],"\xE3"=>[0,"\x1Fb"],"\xE4"=>[0,"\x1Fd"],"\xE5"=>[0,"\x1Ff"],"\xE6"=>[0,"\x1Fg"],"\xE7"=>[0,"\x1Fh"],"\xE8"=>[0,"\x1Fl"],"\xE9"=>[0,"\x1Fm"],"\xEA"=>[0,"\x1Fn"],"\xEB"=>[0,"\x1Fp"],"\xEC"=>[0,"\x1Fr"],"\xED"=>[0,"\x1Fu"],"\xEE"=>[100,"\x1F"],"\xEF"=>[110,"\x1F"],"\xF0"=>[111,"\x1F"],"\xF1"=>[115,"\x1F"],"\xF2"=>[116,"\x1F"],"\xF3"=>[118,"\x1F"],"\xF4"=>[119,"\x1F"],"\xF5"=>[122,"\x1F"],"\xF6"=>[123,"\x1F"],"\xF7"=>[125,"\x1F"],"\xF8"=>[126,"\x1F"],"\xF9"=>[129,"\x1F"],"\xFA"=>[143,"\x1F"],"\xFB"=>[148,"\x1F"],"\xFC"=>[151,"\x1F"],"\xFD"=>[153,"\x1F"],"\xFE"=>[83,"\x1F"],"\xFF"=>[10,"\x1F"],],["\x00"=>[94,"\x1E0"],"\x01"=>[76,"\x1E0"],"\x02"=>[104,"\x1E0"],"\x03"=>[16,"\x1E0"],"\x04"=>[94,"\x1E1"],"\x05"=>[76,"\x1E1"],"\x06"=>[104,"\x1E1"],"\x07"=>[16,"\x1E1"],"\x08"=>[94,"\x1E2"],"\x09"=>[76,"\x1E2"],"\x0A"=>[104,"\x1E2"],"\x0B"=>[16,"\x1E2"],"\x0C"=>[94,"\x1Ea"],"\x0D"=>[76,"\x1Ea"],"\x0E"=>[104,"\x1Ea"],"\x0F"=>[16,"\x1Ea"],"\x10"=>[94,"\x1Ec"],"\x11"=>[76,"\x1Ec"],"\x12"=>[104,"\x1Ec"],"\x13"=>[16,"\x1Ec"],"\x14"=>[94,"\x1Ee"],"\x15"=>[76,"\x1Ee"],"\x16"=>[104,"\x1Ee"],"\x17"=>[16,"\x1Ee"],"\x18"=>[94,"\x1Ei"],"\x19"=>[76,"\x1Ei"],"\x1A"=>[104,"\x1Ei"],"\x1B"=>[16,"\x1Ei"],"\x1C"=>[94,"\x1Eo"],"\x1D"=>[76,"\x1Eo"],"\x1E"=>[104,"\x1Eo"],"\x1F"=>[16,"\x1Eo"],"\x20"=>[94,"\x1Es"],"\x21"=>[76,"\x1Es"],"\x22"=>[104,"\x1Es"],"\x23"=>[16,"\x1Es"],"\x24"=>[94,"\x1Et"],"\x25"=>[76,"\x1Et"],"\x26"=>[104,"\x1Et"],"\x27"=>[16,"\x1Et"],"\x28"=>[77,"\x1E\x20"],"\x29"=>[18,"\x1E\x20"],"\x2A"=>[77,"\x1E\x25"],"\x2B"=>[18,"\x1E\x25"],"\x2C"=>[77,"\x1E-"],"-"=>[18,"\x1E-"],"."=>[77,"\x1E."],"\x2F"=>[18,"\x1E."],[77,"\x1E\x2F"],[18,"\x1E\x2F"],[77,"\x1E3"],[18,"\x1E3"],[77,"\x1E4"],[18,"\x1E4"],[77,"\x1E5"],[18,"\x1E5"],[77,"\x1E6"],[18,"\x1E6"],"\x3A"=>[77,"\x1E7"],"\x3B"=>[18,"\x1E7"],"\x3C"=>[77,"\x1E8"],"\x3D"=>[18,"\x1E8"],"\x3E"=>[77,"\x1E9"],"\x3F"=>[18,"\x1E9"],"\x40"=>[77,"\x1E\x3D"],"A"=>[18,"\x1E\x3D"],"B"=>[77,"\x1EA"],"C"=>[18,"\x1EA"],"D"=>[77,"\x1E_"],"E"=>[18,"\x1E_"],"F"=>[77,"\x1Eb"],"G"=>[18,"\x1Eb"],"H"=>[77,"\x1Ed"],"I"=>[18,"\x1Ed"],"J"=>[77,"\x1Ef"],"K"=>[18,"\x1Ef"],"L"=>[77,"\x1Eg"],"M"=>[18,"\x1Eg"],"N"=>[77,"\x1Eh"],"O"=>[18,"\x1Eh"],"P"=>[77,"\x1El"],"Q"=>[18,"\x1El"],"R"=>[77,"\x1Em"],"S"=>[18,"\x1Em"],"T"=>[77,"\x1En"],"U"=>[18,"\x1En"],"V"=>[77,"\x1Ep"],"W"=>[18,"\x1Ep"],"X"=>[77,"\x1Er"],"Y"=>[18,"\x1Er"],"Z"=>[77,"\x1Eu"],"\x5B"=>[18,"\x1Eu"],"\x5C"=>[0,"\x1E\x3A"],"\x5D"=>[0,"\x1EB"],"\x5E"=>[0,"\x1EC"],"_"=>[0,"\x1ED"],"\x60"=>[0,"\x1EE"],"a"=>[0,"\x1EF"],"b"=>[0,"\x1EG"],"c"=>[0,"\x1EH"],"d"=>[0,"\x1EI"],"e"=>[0,"\x1EJ"],"f"=>[0,"\x1EK"],"g"=>[0,"\x1EL"],"h"=>[0,"\x1EM"],"i"=>[0,"\x1EN"],"j"=>[0,"\x1EO"],"k"=>[0,"\x1EP"],"l"=>[0,"\x1EQ"],"m"=>[0,"\x1ER"],"n"=>[0,"\x1ES"],"o"=>[0,"\x1ET"],"p"=>[0,"\x1EU"],"q"=>[0,"\x1EV"],"r"=>[0,"\x1EW"],"s"=>[0,"\x1EY"],"t"=>[0,"\x1Ej"],"u"=>[0,"\x1Ek"],"v"=>[0,"\x1Eq"],"w"=>[0,"\x1Ev"],"x"=>[0,"\x1Ew"],"y"=>[0,"\x1Ex"],"z"=>[0,"\x1Ey"],"\x7B"=>[0,"\x1Ez"],"\x7C"=>[82,"\x1E"],"\x7D"=>[87,"\x1E"],"~"=>[130,"\x1E"],"\x7F"=>[9,"\x1E"],"\x80"=>[94,"\x1F0"],"\x81"=>[76,"\x1F0"],"\x82"=>[104,"\x1F0"],"\x83"=>[16,"\x1F0"],"\x84"=>[94,"\x1F1"],"\x85"=>[76,"\x1F1"],"\x86"=>[104,"\x1F1"],"\x87"=>[16,"\x1F1"],"\x88"=>[94,"\x1F2"],"\x89"=>[76,"\x1F2"],"\x8A"=>[104,"\x1F2"],"\x8B"=>[16,"\x1F2"],"\x8C"=>[94,"\x1Fa"],"\x8D"=>[76,"\x1Fa"],"\x8E"=>[104,"\x1Fa"],"\x8F"=>[16,"\x1Fa"],"\x90"=>[94,"\x1Fc"],"\x91"=>[76,"\x1Fc"],"\x92"=>[104,"\x1Fc"],"\x93"=>[16,"\x1Fc"],"\x94"=>[94,"\x1Fe"],"\x95"=>[76,"\x1Fe"],"\x96"=>[104,"\x1Fe"],"\x97"=>[16,"\x1Fe"],"\x98"=>[94,"\x1Fi"],"\x99"=>[76,"\x1Fi"],"\x9A"=>[104,"\x1Fi"],"\x9B"=>[16,"\x1Fi"],"\x9C"=>[94,"\x1Fo"],"\x9D"=>[76,"\x1Fo"],"\x9E"=>[104,"\x1Fo"],"\x9F"=>[16,"\x1Fo"],"\xA0"=>[94,"\x1Fs"],"\xA1"=>[76,"\x1Fs"],"\xA2"=>[104,"\x1Fs"],"\xA3"=>[16,"\x1Fs"],"\xA4"=>[94,"\x1Ft"],"\xA5"=>[76,"\x1Ft"],"\xA6"=>[104,"\x1Ft"],"\xA7"=>[16,"\x1Ft"],"\xA8"=>[77,"\x1F\x20"],"\xA9"=>[18,"\x1F\x20"],"\xAA"=>[77,"\x1F\x25"],"\xAB"=>[18,"\x1F\x25"],"\xAC"=>[77,"\x1F-"],"\xAD"=>[18,"\x1F-"],"\xAE"=>[77,"\x1F."],"\xAF"=>[18,"\x1F."],"\xB0"=>[77,"\x1F\x2F"],"\xB1"=>[18,"\x1F\x2F"],"\xB2"=>[77,"\x1F3"],"\xB3"=>[18,"\x1F3"],"\xB4"=>[77,"\x1F4"],"\xB5"=>[18,"\x1F4"],"\xB6"=>[77,"\x1F5"],"\xB7"=>[18,"\x1F5"],"\xB8"=>[77,"\x1F6"],"\xB9"=>[18,"\x1F6"],"\xBA"=>[77,"\x1F7"],"\xBB"=>[18,"\x1F7"],"\xBC"=>[77,"\x1F8"],"\xBD"=>[18,"\x1F8"],"\xBE"=>[77,"\x1F9"],"\xBF"=>[18,"\x1F9"],"\xC0"=>[77,"\x1F\x3D"],"\xC1"=>[18,"\x1F\x3D"],"\xC2"=>[77,"\x1FA"],"\xC3"=>[18,"\x1FA"],"\xC4"=>[77,"\x1F_"],"\xC5"=>[18,"\x1F_"],"\xC6"=>[77,"\x1Fb"],"\xC7"=>[18,"\x1Fb"],"\xC8"=>[77,"\x1Fd"],"\xC9"=>[18,"\x1Fd"],"\xCA"=>[77,"\x1Ff"],"\xCB"=>[18,"\x1Ff"],"\xCC"=>[77,"\x1Fg"],"\xCD"=>[18,"\x1Fg"],"\xCE"=>[77,"\x1Fh"],"\xCF"=>[18,"\x1Fh"],"\xD0"=>[77,"\x1Fl"],"\xD1"=>[18,"\x1Fl"],"\xD2"=>[77,"\x1Fm"],"\xD3"=>[18,"\x1Fm"],"\xD4"=>[77,"\x1Fn"],"\xD5"=>[18,"\x1Fn"],"\xD6"=>[77,"\x1Fp"],"\xD7"=>[18,"\x1Fp"],"\xD8"=>[77,"\x1Fr"],"\xD9"=>[18,"\x1Fr"],"\xDA"=>[77,"\x1Fu"],"\xDB"=>[18,"\x1Fu"],"\xDC"=>[0,"\x1F\x3A"],"\xDD"=>[0,"\x1FB"],"\xDE"=>[0,"\x1FC"],"\xDF"=>[0,"\x1FD"],"\xE0"=>[0,"\x1FE"],"\xE1"=>[0,"\x1FF"],"\xE2"=>[0,"\x1FG"],"\xE3"=>[0,"\x1FH"],"\xE4"=>[0,"\x1FI"],"\xE5"=>[0,"\x1FJ"],"\xE6"=>[0,"\x1FK"],"\xE7"=>[0,"\x1FL"],"\xE8"=>[0,"\x1FM"],"\xE9"=>[0,"\x1FN"],"\xEA"=>[0,"\x1FO"],"\xEB"=>[0,"\x1FP"],"\xEC"=>[0,"\x1FQ"],"\xED"=>[0,"\x1FR"],"\xEE"=>[0,"\x1FS"],"\xEF"=>[0,"\x1FT"],"\xF0"=>[0,"\x1FU"],"\xF1"=>[0,"\x1FV"],"\xF2"=>[0,"\x1FW"],"\xF3"=>[0,"\x1FY"],"\xF4"=>[0,"\x1Fj"],"\xF5"=>[0,"\x1Fk"],"\xF6"=>[0,"\x1Fq"],"\xF7"=>[0,"\x1Fv"],"\xF8"=>[0,"\x1Fw"],"\xF9"=>[0,"\x1Fx"],"\xFA"=>[0,"\x1Fy"],"\xFB"=>[0,"\x1Fz"],"\xFC"=>[82,"\x1F"],"\xFD"=>[87,"\x1F"],"\xFE"=>[130,"\x1F"],"\xFF"=>[9,"\x1F"],],["\x00"=>[94,"\x200"],"\x01"=>[76,"\x200"],"\x02"=>[104,"\x200"],"\x03"=>[16,"\x200"],"\x04"=>[94,"\x201"],"\x05"=>[76,"\x201"],"\x06"=>[104,"\x201"],"\x07"=>[16,"\x201"],"\x08"=>[94,"\x202"],"\x09"=>[76,"\x202"],"\x0A"=>[104,"\x202"],"\x0B"=>[16,"\x202"],"\x0C"=>[94,"\x20a"],"\x0D"=>[76,"\x20a"],"\x0E"=>[104,"\x20a"],"\x0F"=>[16,"\x20a"],"\x10"=>[94,"\x20c"],"\x11"=>[76,"\x20c"],"\x12"=>[104,"\x20c"],"\x13"=>[16,"\x20c"],"\x14"=>[94,"\x20e"],"\x15"=>[76,"\x20e"],"\x16"=>[104,"\x20e"],"\x17"=>[16,"\x20e"],"\x18"=>[94,"\x20i"],"\x19"=>[76,"\x20i"],"\x1A"=>[104,"\x20i"],"\x1B"=>[16,"\x20i"],"\x1C"=>[94,"\x20o"],"\x1D"=>[76,"\x20o"],"\x1E"=>[104,"\x20o"],"\x1F"=>[16,"\x20o"],"\x20"=>[94,"\x20s"],"\x21"=>[76,"\x20s"],"\x22"=>[104,"\x20s"],"\x23"=>[16,"\x20s"],"\x24"=>[94,"\x20t"],"\x25"=>[76,"\x20t"],"\x26"=>[104,"\x20t"],"\x27"=>[16,"\x20t"],"\x28"=>[77,"\x20\x20"],"\x29"=>[18,"\x20\x20"],"\x2A"=>[77,"\x20\x25"],"\x2B"=>[18,"\x20\x25"],"\x2C"=>[77,"\x20-"],"-"=>[18,"\x20-"],"."=>[77,"\x20."],"\x2F"=>[18,"\x20."],[77,"\x20\x2F"],[18,"\x20\x2F"],[77,"\x203"],[18,"\x203"],[77,"\x204"],[18,"\x204"],[77,"\x205"],[18,"\x205"],[77,"\x206"],[18,"\x206"],"\x3A"=>[77,"\x207"],"\x3B"=>[18,"\x207"],"\x3C"=>[77,"\x208"],"\x3D"=>[18,"\x208"],"\x3E"=>[77,"\x209"],"\x3F"=>[18,"\x209"],"\x40"=>[77,"\x20\x3D"],"A"=>[18,"\x20\x3D"],"B"=>[77,"\x20A"],"C"=>[18,"\x20A"],"D"=>[77,"\x20_"],"E"=>[18,"\x20_"],"F"=>[77,"\x20b"],"G"=>[18,"\x20b"],"H"=>[77,"\x20d"],"I"=>[18,"\x20d"],"J"=>[77,"\x20f"],"K"=>[18,"\x20f"],"L"=>[77,"\x20g"],"M"=>[18,"\x20g"],"N"=>[77,"\x20h"],"O"=>[18,"\x20h"],"P"=>[77,"\x20l"],"Q"=>[18,"\x20l"],"R"=>[77,"\x20m"],"S"=>[18,"\x20m"],"T"=>[77,"\x20n"],"U"=>[18,"\x20n"],"V"=>[77,"\x20p"],"W"=>[18,"\x20p"],"X"=>[77,"\x20r"],"Y"=>[18,"\x20r"],"Z"=>[77,"\x20u"],"\x5B"=>[18,"\x20u"],"\x5C"=>[0,"\x20\x3A"],"\x5D"=>[0,"\x20B"],"\x5E"=>[0,"\x20C"],"_"=>[0,"\x20D"],"\x60"=>[0,"\x20E"],"a"=>[0,"\x20F"],"b"=>[0,"\x20G"],"c"=>[0,"\x20H"],"d"=>[0,"\x20I"],"e"=>[0,"\x20J"],"f"=>[0,"\x20K"],"g"=>[0,"\x20L"],"h"=>[0,"\x20M"],"i"=>[0,"\x20N"],"j"=>[0,"\x20O"],"k"=>[0,"\x20P"],"l"=>[0,"\x20Q"],"m"=>[0,"\x20R"],"n"=>[0,"\x20S"],"o"=>[0,"\x20T"],"p"=>[0,"\x20U"],"q"=>[0,"\x20V"],"r"=>[0,"\x20W"],"s"=>[0,"\x20Y"],"t"=>[0,"\x20j"],"u"=>[0,"\x20k"],"v"=>[0,"\x20q"],"w"=>[0,"\x20v"],"x"=>[0,"\x20w"],"y"=>[0,"\x20x"],"z"=>[0,"\x20y"],"\x7B"=>[0,"\x20z"],"\x7C"=>[82,"\x20"],"\x7D"=>[87,"\x20"],"~"=>[130,"\x20"],"\x7F"=>[9,"\x20"],"\x80"=>[94,"\x250"],"\x81"=>[76,"\x250"],"\x82"=>[104,"\x250"],"\x83"=>[16,"\x250"],"\x84"=>[94,"\x251"],"\x85"=>[76,"\x251"],"\x86"=>[104,"\x251"],"\x87"=>[16,"\x251"],"\x88"=>[94,"\x252"],"\x89"=>[76,"\x252"],"\x8A"=>[104,"\x252"],"\x8B"=>[16,"\x252"],"\x8C"=>[94,"\x25a"],"\x8D"=>[76,"\x25a"],"\x8E"=>[104,"\x25a"],"\x8F"=>[16,"\x25a"],"\x90"=>[94,"\x25c"],"\x91"=>[76,"\x25c"],"\x92"=>[104,"\x25c"],"\x93"=>[16,"\x25c"],"\x94"=>[94,"\x25e"],"\x95"=>[76,"\x25e"],"\x96"=>[104,"\x25e"],"\x97"=>[16,"\x25e"],"\x98"=>[94,"\x25i"],"\x99"=>[76,"\x25i"],"\x9A"=>[104,"\x25i"],"\x9B"=>[16,"\x25i"],"\x9C"=>[94,"\x25o"],"\x9D"=>[76,"\x25o"],"\x9E"=>[104,"\x25o"],"\x9F"=>[16,"\x25o"],"\xA0"=>[94,"\x25s"],"\xA1"=>[76,"\x25s"],"\xA2"=>[104,"\x25s"],"\xA3"=>[16,"\x25s"],"\xA4"=>[94,"\x25t"],"\xA5"=>[76,"\x25t"],"\xA6"=>[104,"\x25t"],"\xA7"=>[16,"\x25t"],"\xA8"=>[77,"\x25\x20"],"\xA9"=>[18,"\x25\x20"],"\xAA"=>[77,"\x25\x25"],"\xAB"=>[18,"\x25\x25"],"\xAC"=>[77,"\x25-"],"\xAD"=>[18,"\x25-"],"\xAE"=>[77,"\x25."],"\xAF"=>[18,"\x25."],"\xB0"=>[77,"\x25\x2F"],"\xB1"=>[18,"\x25\x2F"],"\xB2"=>[77,"\x253"],"\xB3"=>[18,"\x253"],"\xB4"=>[77,"\x254"],"\xB5"=>[18,"\x254"],"\xB6"=>[77,"\x255"],"\xB7"=>[18,"\x255"],"\xB8"=>[77,"\x256"],"\xB9"=>[18,"\x256"],"\xBA"=>[77,"\x257"],"\xBB"=>[18,"\x257"],"\xBC"=>[77,"\x258"],"\xBD"=>[18,"\x258"],"\xBE"=>[77,"\x259"],"\xBF"=>[18,"\x259"],"\xC0"=>[77,"\x25\x3D"],"\xC1"=>[18,"\x25\x3D"],"\xC2"=>[77,"\x25A"],"\xC3"=>[18,"\x25A"],"\xC4"=>[77,"\x25_"],"\xC5"=>[18,"\x25_"],"\xC6"=>[77,"\x25b"],"\xC7"=>[18,"\x25b"],"\xC8"=>[77,"\x25d"],"\xC9"=>[18,"\x25d"],"\xCA"=>[77,"\x25f"],"\xCB"=>[18,"\x25f"],"\xCC"=>[77,"\x25g"],"\xCD"=>[18,"\x25g"],"\xCE"=>[77,"\x25h"],"\xCF"=>[18,"\x25h"],"\xD0"=>[77,"\x25l"],"\xD1"=>[18,"\x25l"],"\xD2"=>[77,"\x25m"],"\xD3"=>[18,"\x25m"],"\xD4"=>[77,"\x25n"],"\xD5"=>[18,"\x25n"],"\xD6"=>[77,"\x25p"],"\xD7"=>[18,"\x25p"],"\xD8"=>[77,"\x25r"],"\xD9"=>[18,"\x25r"],"\xDA"=>[77,"\x25u"],"\xDB"=>[18,"\x25u"],"\xDC"=>[0,"\x25\x3A"],"\xDD"=>[0,"\x25B"],"\xDE"=>[0,"\x25C"],"\xDF"=>[0,"\x25D"],"\xE0"=>[0,"\x25E"],"\xE1"=>[0,"\x25F"],"\xE2"=>[0,"\x25G"],"\xE3"=>[0,"\x25H"],"\xE4"=>[0,"\x25I"],"\xE5"=>[0,"\x25J"],"\xE6"=>[0,"\x25K"],"\xE7"=>[0,"\x25L"],"\xE8"=>[0,"\x25M"],"\xE9"=>[0,"\x25N"],"\xEA"=>[0,"\x25O"],"\xEB"=>[0,"\x25P"],"\xEC"=>[0,"\x25Q"],"\xED"=>[0,"\x25R"],"\xEE"=>[0,"\x25S"],"\xEF"=>[0,"\x25T"],"\xF0"=>[0,"\x25U"],"\xF1"=>[0,"\x25V"],"\xF2"=>[0,"\x25W"],"\xF3"=>[0,"\x25Y"],"\xF4"=>[0,"\x25j"],"\xF5"=>[0,"\x25k"],"\xF6"=>[0,"\x25q"],"\xF7"=>[0,"\x25v"],"\xF8"=>[0,"\x25w"],"\xF9"=>[0,"\x25x"],"\xFA"=>[0,"\x25y"],"\xFB"=>[0,"\x25z"],"\xFC"=>[82,"\x25"],"\xFD"=>[87,"\x25"],"\xFE"=>[130,"\x25"],"\xFF"=>[9,"\x25"],],["\x00"=>[77,"\x200"],"\x01"=>[18,"\x200"],"\x02"=>[77,"\x201"],"\x03"=>[18,"\x201"],"\x04"=>[77,"\x202"],"\x05"=>[18,"\x202"],"\x06"=>[77,"\x20a"],"\x07"=>[18,"\x20a"],"\x08"=>[77,"\x20c"],"\x09"=>[18,"\x20c"],"\x0A"=>[77,"\x20e"],"\x0B"=>[18,"\x20e"],"\x0C"=>[77,"\x20i"],"\x0D"=>[18,"\x20i"],"\x0E"=>[77,"\x20o"],"\x0F"=>[18,"\x20o"],"\x10"=>[77,"\x20s"],"\x11"=>[18,"\x20s"],"\x12"=>[77,"\x20t"],"\x13"=>[18,"\x20t"],"\x14"=>[0,"\x20\x20"],"\x15"=>[0,"\x20\x25"],"\x16"=>[0,"\x20-"],"\x17"=>[0,"\x20."],"\x18"=>[0,"\x20\x2F"],"\x19"=>[0,"\x203"],"\x1A"=>[0,"\x204"],"\x1B"=>[0,"\x205"],"\x1C"=>[0,"\x206"],"\x1D"=>[0,"\x207"],"\x1E"=>[0,"\x208"],"\x1F"=>[0,"\x209"],"\x20"=>[0,"\x20\x3D"],"\x21"=>[0,"\x20A"],"\x22"=>[0,"\x20_"],"\x23"=>[0,"\x20b"],"\x24"=>[0,"\x20d"],"\x25"=>[0,"\x20f"],"\x26"=>[0,"\x20g"],"\x27"=>[0,"\x20h"],"\x28"=>[0,"\x20l"],"\x29"=>[0,"\x20m"],"\x2A"=>[0,"\x20n"],"\x2B"=>[0,"\x20p"],"\x2C"=>[0,"\x20r"],"-"=>[0,"\x20u"],"."=>[100,"\x20"],"\x2F"=>[110,"\x20"],[111,"\x20"],[115,"\x20"],[116,"\x20"],[118,"\x20"],[119,"\x20"],[122,"\x20"],[123,"\x20"],[125,"\x20"],[126,"\x20"],[129,"\x20"],"\x3A"=>[143,"\x20"],"\x3B"=>[148,"\x20"],"\x3C"=>[151,"\x20"],"\x3D"=>[153,"\x20"],"\x3E"=>[83,"\x20"],"\x3F"=>[10,"\x20"],"\x40"=>[77,"\x250"],"A"=>[18,"\x250"],"B"=>[77,"\x251"],"C"=>[18,"\x251"],"D"=>[77,"\x252"],"E"=>[18,"\x252"],"F"=>[77,"\x25a"],"G"=>[18,"\x25a"],"H"=>[77,"\x25c"],"I"=>[18,"\x25c"],"J"=>[77,"\x25e"],"K"=>[18,"\x25e"],"L"=>[77,"\x25i"],"M"=>[18,"\x25i"],"N"=>[77,"\x25o"],"O"=>[18,"\x25o"],"P"=>[77,"\x25s"],"Q"=>[18,"\x25s"],"R"=>[77,"\x25t"],"S"=>[18,"\x25t"],"T"=>[0,"\x25\x20"],"U"=>[0,"\x25\x25"],"V"=>[0,"\x25-"],"W"=>[0,"\x25."],"X"=>[0,"\x25\x2F"],"Y"=>[0,"\x253"],"Z"=>[0,"\x254"],"\x5B"=>[0,"\x255"],"\x5C"=>[0,"\x256"],"\x5D"=>[0,"\x257"],"\x5E"=>[0,"\x258"],"_"=>[0,"\x259"],"\x60"=>[0,"\x25\x3D"],"a"=>[0,"\x25A"],"b"=>[0,"\x25_"],"c"=>[0,"\x25b"],"d"=>[0,"\x25d"],"e"=>[0,"\x25f"],"f"=>[0,"\x25g"],"g"=>[0,"\x25h"],"h"=>[0,"\x25l"],"i"=>[0,"\x25m"],"j"=>[0,"\x25n"],"k"=>[0,"\x25p"],"l"=>[0,"\x25r"],"m"=>[0,"\x25u"],"n"=>[100,"\x25"],"o"=>[110,"\x25"],"p"=>[111,"\x25"],"q"=>[115,"\x25"],"r"=>[116,"\x25"],"s"=>[118,"\x25"],"t"=>[119,"\x25"],"u"=>[122,"\x25"],"v"=>[123,"\x25"],"w"=>[125,"\x25"],"x"=>[126,"\x25"],"y"=>[129,"\x25"],"z"=>[143,"\x25"],"\x7B"=>[148,"\x25"],"\x7C"=>[151,"\x25"],"\x7D"=>[153,"\x25"],"~"=>[83,"\x25"],"\x7F"=>[10,"\x25"],"\x80"=>[77,"-0"],"\x81"=>[18,"-0"],"\x82"=>[77,"-1"],"\x83"=>[18,"-1"],"\x84"=>[77,"-2"],"\x85"=>[18,"-2"],"\x86"=>[77,"-a"],"\x87"=>[18,"-a"],"\x88"=>[77,"-c"],"\x89"=>[18,"-c"],"\x8A"=>[77,"-e"],"\x8B"=>[18,"-e"],"\x8C"=>[77,"-i"],"\x8D"=>[18,"-i"],"\x8E"=>[77,"-o"],"\x8F"=>[18,"-o"],"\x90"=>[77,"-s"],"\x91"=>[18,"-s"],"\x92"=>[77,"-t"],"\x93"=>[18,"-t"],"\x94"=>[0,"-\x20"],"\x95"=>[0,"-\x25"],"\x96"=>[0,"--"],"\x97"=>[0,"-."],"\x98"=>[0,"-\x2F"],"\x99"=>[0,"-3"],"\x9A"=>[0,"-4"],"\x9B"=>[0,"-5"],"\x9C"=>[0,"-6"],"\x9D"=>[0,"-7"],"\x9E"=>[0,"-8"],"\x9F"=>[0,"-9"],"\xA0"=>[0,"-\x3D"],"\xA1"=>[0,"-A"],"\xA2"=>[0,"-_"],"\xA3"=>[0,"-b"],"\xA4"=>[0,"-d"],"\xA5"=>[0,"-f"],"\xA6"=>[0,"-g"],"\xA7"=>[0,"-h"],"\xA8"=>[0,"-l"],"\xA9"=>[0,"-m"],"\xAA"=>[0,"-n"],"\xAB"=>[0,"-p"],"\xAC"=>[0,"-r"],"\xAD"=>[0,"-u"],"\xAE"=>[100,"-"],"\xAF"=>[110,"-"],"\xB0"=>[111,"-"],"\xB1"=>[115,"-"],"\xB2"=>[116,"-"],"\xB3"=>[118,"-"],"\xB4"=>[119,"-"],"\xB5"=>[122,"-"],"\xB6"=>[123,"-"],"\xB7"=>[125,"-"],"\xB8"=>[126,"-"],"\xB9"=>[129,"-"],"\xBA"=>[143,"-"],"\xBB"=>[148,"-"],"\xBC"=>[151,"-"],"\xBD"=>[153,"-"],"\xBE"=>[83,"-"],"\xBF"=>[10,"-"],"\xC0"=>[77,".0"],"\xC1"=>[18,".0"],"\xC2"=>[77,".1"],"\xC3"=>[18,".1"],"\xC4"=>[77,".2"],"\xC5"=>[18,".2"],"\xC6"=>[77,".a"],"\xC7"=>[18,".a"],"\xC8"=>[77,".c"],"\xC9"=>[18,".c"],"\xCA"=>[77,".e"],"\xCB"=>[18,".e"],"\xCC"=>[77,".i"],"\xCD"=>[18,".i"],"\xCE"=>[77,".o"],"\xCF"=>[18,".o"],"\xD0"=>[77,".s"],"\xD1"=>[18,".s"],"\xD2"=>[77,".t"],"\xD3"=>[18,".t"],"\xD4"=>[0,".\x20"],"\xD5"=>[0,".\x25"],"\xD6"=>[0,".-"],"\xD7"=>[0,".."],"\xD8"=>[0,".\x2F"],"\xD9"=>[0,".3"],"\xDA"=>[0,".4"],"\xDB"=>[0,".5"],"\xDC"=>[0,".6"],"\xDD"=>[0,".7"],"\xDE"=>[0,".8"],"\xDF"=>[0,".9"],"\xE0"=>[0,".\x3D"],"\xE1"=>[0,".A"],"\xE2"=>[0,"._"],"\xE3"=>[0,".b"],"\xE4"=>[0,".d"],"\xE5"=>[0,".f"],"\xE6"=>[0,".g"],"\xE7"=>[0,".h"],"\xE8"=>[0,".l"],"\xE9"=>[0,".m"],"\xEA"=>[0,".n"],"\xEB"=>[0,".p"],"\xEC"=>[0,".r"],"\xED"=>[0,".u"],"\xEE"=>[100,"."],"\xEF"=>[110,"."],"\xF0"=>[111,"."],"\xF1"=>[115,"."],"\xF2"=>[116,"."],"\xF3"=>[118,"."],"\xF4"=>[119,"."],"\xF5"=>[122,"."],"\xF6"=>[123,"."],"\xF7"=>[125,"."],"\xF8"=>[126,"."],"\xF9"=>[129,"."],"\xFA"=>[143,"."],"\xFB"=>[148,"."],"\xFC"=>[151,"."],"\xFD"=>[153,"."],"\xFE"=>[83,"."],"\xFF"=>[10,"."],],["\x00"=>[77,"s0"],"\x01"=>[18,"s0"],"\x02"=>[77,"s1"],"\x03"=>[18,"s1"],"\x04"=>[77,"s2"],"\x05"=>[18,"s2"],"\x06"=>[77,"sa"],"\x07"=>[18,"sa"],"\x08"=>[77,"sc"],"\x09"=>[18,"sc"],"\x0A"=>[77,"se"],"\x0B"=>[18,"se"],"\x0C"=>[77,"si"],"\x0D"=>[18,"si"],"\x0E"=>[77,"so"],"\x0F"=>[18,"so"],"\x10"=>[77,"ss"],"\x11"=>[18,"ss"],"\x12"=>[77,"st"],"\x13"=>[18,"st"],"\x14"=>[0,"s\x20"],"\x15"=>[0,"s\x25"],"\x16"=>[0,"s-"],"\x17"=>[0,"s."],"\x18"=>[0,"s\x2F"],"\x19"=>[0,"s3"],"\x1A"=>[0,"s4"],"\x1B"=>[0,"s5"],"\x1C"=>[0,"s6"],"\x1D"=>[0,"s7"],"\x1E"=>[0,"s8"],"\x1F"=>[0,"s9"],"\x20"=>[0,"s\x3D"],"\x21"=>[0,"sA"],"\x22"=>[0,"s_"],"\x23"=>[0,"sb"],"\x24"=>[0,"sd"],"\x25"=>[0,"sf"],"\x26"=>[0,"sg"],"\x27"=>[0,"sh"],"\x28"=>[0,"sl"],"\x29"=>[0,"sm"],"\x2A"=>[0,"sn"],"\x2B"=>[0,"sp"],"\x2C"=>[0,"sr"],"-"=>[0,"su"],"."=>[100,"s"],"\x2F"=>[110,"s"],[111,"s"],[115,"s"],[116,"s"],[118,"s"],[119,"s"],[122,"s"],[123,"s"],[125,"s"],[126,"s"],[129,"s"],"\x3A"=>[143,"s"],"\x3B"=>[148,"s"],"\x3C"=>[151,"s"],"\x3D"=>[153,"s"],"\x3E"=>[83,"s"],"\x3F"=>[10,"s"],"\x40"=>[77,"t0"],"A"=>[18,"t0"],"B"=>[77,"t1"],"C"=>[18,"t1"],"D"=>[77,"t2"],"E"=>[18,"t2"],"F"=>[77,"ta"],"G"=>[18,"ta"],"H"=>[77,"tc"],"I"=>[18,"tc"],"J"=>[77,"te"],"K"=>[18,"te"],"L"=>[77,"ti"],"M"=>[18,"ti"],"N"=>[77,"to"],"O"=>[18,"to"],"P"=>[77,"ts"],"Q"=>[18,"ts"],"R"=>[77,"tt"],"S"=>[18,"tt"],"T"=>[0,"t\x20"],"U"=>[0,"t\x25"],"V"=>[0,"t-"],"W"=>[0,"t."],"X"=>[0,"t\x2F"],"Y"=>[0,"t3"],"Z"=>[0,"t4"],"\x5B"=>[0,"t5"],"\x5C"=>[0,"t6"],"\x5D"=>[0,"t7"],"\x5E"=>[0,"t8"],"_"=>[0,"t9"],"\x60"=>[0,"t\x3D"],"a"=>[0,"tA"],"b"=>[0,"t_"],"c"=>[0,"tb"],"d"=>[0,"td"],"e"=>[0,"tf"],"f"=>[0,"tg"],"g"=>[0,"th"],"h"=>[0,"tl"],"i"=>[0,"tm"],"j"=>[0,"tn"],"k"=>[0,"tp"],"l"=>[0,"tr"],"m"=>[0,"tu"],"n"=>[100,"t"],"o"=>[110,"t"],"p"=>[111,"t"],"q"=>[115,"t"],"r"=>[116,"t"],"s"=>[118,"t"],"t"=>[119,"t"],"u"=>[122,"t"],"v"=>[123,"t"],"w"=>[125,"t"],"x"=>[126,"t"],"y"=>[129,"t"],"z"=>[143,"t"],"\x7B"=>[148,"t"],"\x7C"=>[151,"t"],"\x7D"=>[153,"t"],"~"=>[83,"t"],"\x7F"=>[10,"t"],"\x80"=>[0,"\x200"],"\x81"=>[0,"\x201"],"\x82"=>[0,"\x202"],"\x83"=>[0,"\x20a"],"\x84"=>[0,"\x20c"],"\x85"=>[0,"\x20e"],"\x86"=>[0,"\x20i"],"\x87"=>[0,"\x20o"],"\x88"=>[0,"\x20s"],"\x89"=>[0,"\x20t"],"\x8A"=>[73,"\x20"],"\x8B"=>[88,"\x20"],"\x8C"=>[89,"\x20"],"\x8D"=>[96,"\x20"],"\x8E"=>[97,"\x20"],"\x8F"=>[99,"\x20"],"\x90"=>[106,"\x20"],"\x91"=>[136,"\x20"],"\x92"=>[139,"\x20"],"\x93"=>[141,"\x20"],"\x94"=>[145,"\x20"],"\x95"=>[147,"\x20"],"\x96"=>[149,"\x20"],"\x97"=>[101,"\x20"],"\x98"=>[112,"\x20"],"\x99"=>[117,"\x20"],"\x9A"=>[120,"\x20"],"\x9B"=>[124,"\x20"],"\x9C"=>[127,"\x20"],"\x9D"=>[144,"\x20"],"\x9E"=>[152,"\x20"],"\x9F"=>[11,"\x20"],"\xA0"=>[0,"\x250"],"\xA1"=>[0,"\x251"],"\xA2"=>[0,"\x252"],"\xA3"=>[0,"\x25a"],"\xA4"=>[0,"\x25c"],"\xA5"=>[0,"\x25e"],"\xA6"=>[0,"\x25i"],"\xA7"=>[0,"\x25o"],"\xA8"=>[0,"\x25s"],"\xA9"=>[0,"\x25t"],"\xAA"=>[73,"\x25"],"\xAB"=>[88,"\x25"],"\xAC"=>[89,"\x25"],"\xAD"=>[96,"\x25"],"\xAE"=>[97,"\x25"],"\xAF"=>[99,"\x25"],"\xB0"=>[106,"\x25"],"\xB1"=>[136,"\x25"],"\xB2"=>[139,"\x25"],"\xB3"=>[141,"\x25"],"\xB4"=>[145,"\x25"],"\xB5"=>[147,"\x25"],"\xB6"=>[149,"\x25"],"\xB7"=>[101,"\x25"],"\xB8"=>[112,"\x25"],"\xB9"=>[117,"\x25"],"\xBA"=>[120,"\x25"],"\xBB"=>[124,"\x25"],"\xBC"=>[127,"\x25"],"\xBD"=>[144,"\x25"],"\xBE"=>[152,"\x25"],"\xBF"=>[11,"\x25"],"\xC0"=>[0,"-0"],"\xC1"=>[0,"-1"],"\xC2"=>[0,"-2"],"\xC3"=>[0,"-a"],"\xC4"=>[0,"-c"],"\xC5"=>[0,"-e"],"\xC6"=>[0,"-i"],"\xC7"=>[0,"-o"],"\xC8"=>[0,"-s"],"\xC9"=>[0,"-t"],"\xCA"=>[73,"-"],"\xCB"=>[88,"-"],"\xCC"=>[89,"-"],"\xCD"=>[96,"-"],"\xCE"=>[97,"-"],"\xCF"=>[99,"-"],"\xD0"=>[106,"-"],"\xD1"=>[136,"-"],"\xD2"=>[139,"-"],"\xD3"=>[141,"-"],"\xD4"=>[145,"-"],"\xD5"=>[147,"-"],"\xD6"=>[149,"-"],"\xD7"=>[101,"-"],"\xD8"=>[112,"-"],"\xD9"=>[117,"-"],"\xDA"=>[120,"-"],"\xDB"=>[124,"-"],"\xDC"=>[127,"-"],"\xDD"=>[144,"-"],"\xDE"=>[152,"-"],"\xDF"=>[11,"-"],"\xE0"=>[0,".0"],"\xE1"=>[0,".1"],"\xE2"=>[0,".2"],"\xE3"=>[0,".a"],"\xE4"=>[0,".c"],"\xE5"=>[0,".e"],"\xE6"=>[0,".i"],"\xE7"=>[0,".o"],"\xE8"=>[0,".s"],"\xE9"=>[0,".t"],"\xEA"=>[73,"."],"\xEB"=>[88,"."],"\xEC"=>[89,"."],"\xED"=>[96,"."],"\xEE"=>[97,"."],"\xEF"=>[99,"."],"\xF0"=>[106,"."],"\xF1"=>[136,"."],"\xF2"=>[139,"."],"\xF3"=>[141,"."],"\xF4"=>[145,"."],"\xF5"=>[147,"."],"\xF6"=>[149,"."],"\xF7"=>[101,"."],"\xF8"=>[112,"."],"\xF9"=>[117,"."],"\xFA"=>[120,"."],"\xFB"=>[124,"."],"\xFC"=>[127,"."],"\xFD"=>[144,"."],"\xFE"=>[152,"."],"\xFF"=>[11,"."],],["\x00"=>[0,"s0"],"\x01"=>[0,"s1"],"\x02"=>[0,"s2"],"\x03"=>[0,"sa"],"\x04"=>[0,"sc"],"\x05"=>[0,"se"],"\x06"=>[0,"si"],"\x07"=>[0,"so"],"\x08"=>[0,"ss"],"\x09"=>[0,"st"],"\x0A"=>[73,"s"],"\x0B"=>[88,"s"],"\x0C"=>[89,"s"],"\x0D"=>[96,"s"],"\x0E"=>[97,"s"],"\x0F"=>[99,"s"],"\x10"=>[106,"s"],"\x11"=>[136,"s"],"\x12"=>[139,"s"],"\x13"=>[141,"s"],"\x14"=>[145,"s"],"\x15"=>[147,"s"],"\x16"=>[149,"s"],"\x17"=>[101,"s"],"\x18"=>[112,"s"],"\x19"=>[117,"s"],"\x1A"=>[120,"s"],"\x1B"=>[124,"s"],"\x1C"=>[127,"s"],"\x1D"=>[144,"s"],"\x1E"=>[152,"s"],"\x1F"=>[11,"s"],"\x20"=>[0,"t0"],"\x21"=>[0,"t1"],"\x22"=>[0,"t2"],"\x23"=>[0,"ta"],"\x24"=>[0,"tc"],"\x25"=>[0,"te"],"\x26"=>[0,"ti"],"\x27"=>[0,"to"],"\x28"=>[0,"ts"],"\x29"=>[0,"tt"],"\x2A"=>[73,"t"],"\x2B"=>[88,"t"],"\x2C"=>[89,"t"],"-"=>[96,"t"],"."=>[97,"t"],"\x2F"=>[99,"t"],[106,"t"],[136,"t"],[139,"t"],[141,"t"],[145,"t"],[147,"t"],[149,"t"],[101,"t"],[112,"t"],[117,"t"],"\x3A"=>[120,"t"],"\x3B"=>[124,"t"],"\x3C"=>[127,"t"],"\x3D"=>[144,"t"],"\x3E"=>[152,"t"],"\x3F"=>[11,"t"],"\x40"=>[92,"\x20"],"A"=>[95,"\x20"],"B"=>[137,"\x20"],"C"=>[142,"\x20"],"D"=>[150,"\x20"],"E"=>[74,"\x20"],"F"=>[90,"\x20"],"G"=>[98,"\x20"],"H"=>[107,"\x20"],"I"=>[140,"\x20"],"J"=>[146,"\x20"],"K"=>[102,"\x20"],"L"=>[113,"\x20"],"M"=>[121,"\x20"],"N"=>[128,"\x20"],"O"=>[12,"\x20"],"P"=>[92,"\x25"],"Q"=>[95,"\x25"],"R"=>[137,"\x25"],"S"=>[142,"\x25"],"T"=>[150,"\x25"],"U"=>[74,"\x25"],"V"=>[90,"\x25"],"W"=>[98,"\x25"],"X"=>[107,"\x25"],"Y"=>[140,"\x25"],"Z"=>[146,"\x25"],"\x5B"=>[102,"\x25"],"\x5C"=>[113,"\x25"],"\x5D"=>[121,"\x25"],"\x5E"=>[128,"\x25"],"_"=>[12,"\x25"],"\x60"=>[92,"-"],"a"=>[95,"-"],"b"=>[137,"-"],"c"=>[142,"-"],"d"=>[150,"-"],"e"=>[74,"-"],"f"=>[90,"-"],"g"=>[98,"-"],"h"=>[107,"-"],"i"=>[140,"-"],"j"=>[146,"-"],"k"=>[102,"-"],"l"=>[113,"-"],"m"=>[121,"-"],"n"=>[128,"-"],"o"=>[12,"-"],"p"=>[92,"."],"q"=>[95,"."],"r"=>[137,"."],"s"=>[142,"."],"t"=>[150,"."],"u"=>[74,"."],"v"=>[90,"."],"w"=>[98,"."],"x"=>[107,"."],"y"=>[140,"."],"z"=>[146,"."],"\x7B"=>[102,"."],"\x7C"=>[113,"."],"\x7D"=>[121,"."],"~"=>[128,"."],"\x7F"=>[12,"."],"\x80"=>[92,"\x2F"],"\x81"=>[95,"\x2F"],"\x82"=>[137,"\x2F"],"\x83"=>[142,"\x2F"],"\x84"=>[150,"\x2F"],"\x85"=>[74,"\x2F"],"\x86"=>[90,"\x2F"],"\x87"=>[98,"\x2F"],"\x88"=>[107,"\x2F"],"\x89"=>[140,"\x2F"],"\x8A"=>[146,"\x2F"],"\x8B"=>[102,"\x2F"],"\x8C"=>[113,"\x2F"],"\x8D"=>[121,"\x2F"],"\x8E"=>[128,"\x2F"],"\x8F"=>[12,"\x2F"],"\x90"=>[92,"3"],"\x91"=>[95,"3"],"\x92"=>[137,"3"],"\x93"=>[142,"3"],"\x94"=>[150,"3"],"\x95"=>[74,"3"],"\x96"=>[90,"3"],"\x97"=>[98,"3"],"\x98"=>[107,"3"],"\x99"=>[140,"3"],"\x9A"=>[146,"3"],"\x9B"=>[102,"3"],"\x9C"=>[113,"3"],"\x9D"=>[121,"3"],"\x9E"=>[128,"3"],"\x9F"=>[12,"3"],"\xA0"=>[92,"4"],"\xA1"=>[95,"4"],"\xA2"=>[137,"4"],"\xA3"=>[142,"4"],"\xA4"=>[150,"4"],"\xA5"=>[74,"4"],"\xA6"=>[90,"4"],"\xA7"=>[98,"4"],"\xA8"=>[107,"4"],"\xA9"=>[140,"4"],"\xAA"=>[146,"4"],"\xAB"=>[102,"4"],"\xAC"=>[113,"4"],"\xAD"=>[121,"4"],"\xAE"=>[128,"4"],"\xAF"=>[12,"4"],"\xB0"=>[92,"5"],"\xB1"=>[95,"5"],"\xB2"=>[137,"5"],"\xB3"=>[142,"5"],"\xB4"=>[150,"5"],"\xB5"=>[74,"5"],"\xB6"=>[90,"5"],"\xB7"=>[98,"5"],"\xB8"=>[107,"5"],"\xB9"=>[140,"5"],"\xBA"=>[146,"5"],"\xBB"=>[102,"5"],"\xBC"=>[113,"5"],"\xBD"=>[121,"5"],"\xBE"=>[128,"5"],"\xBF"=>[12,"5"],"\xC0"=>[92,"6"],"\xC1"=>[95,"6"],"\xC2"=>[137,"6"],"\xC3"=>[142,"6"],"\xC4"=>[150,"6"],"\xC5"=>[74,"6"],"\xC6"=>[90,"6"],"\xC7"=>[98,"6"],"\xC8"=>[107,"6"],"\xC9"=>[140,"6"],"\xCA"=>[146,"6"],"\xCB"=>[102,"6"],"\xCC"=>[113,"6"],"\xCD"=>[121,"6"],"\xCE"=>[128,"6"],"\xCF"=>[12,"6"],"\xD0"=>[92,"7"],"\xD1"=>[95,"7"],"\xD2"=>[137,"7"],"\xD3"=>[142,"7"],"\xD4"=>[150,"7"],"\xD5"=>[74,"7"],"\xD6"=>[90,"7"],"\xD7"=>[98,"7"],"\xD8"=>[107,"7"],"\xD9"=>[140,"7"],"\xDA"=>[146,"7"],"\xDB"=>[102,"7"],"\xDC"=>[113,"7"],"\xDD"=>[121,"7"],"\xDE"=>[128,"7"],"\xDF"=>[12,"7"],"\xE0"=>[92,"8"],"\xE1"=>[95,"8"],"\xE2"=>[137,"8"],"\xE3"=>[142,"8"],"\xE4"=>[150,"8"],"\xE5"=>[74,"8"],"\xE6"=>[90,"8"],"\xE7"=>[98,"8"],"\xE8"=>[107,"8"],"\xE9"=>[140,"8"],"\xEA"=>[146,"8"],"\xEB"=>[102,"8"],"\xEC"=>[113,"8"],"\xED"=>[121,"8"],"\xEE"=>[128,"8"],"\xEF"=>[12,"8"],"\xF0"=>[92,"9"],"\xF1"=>[95,"9"],"\xF2"=>[137,"9"],"\xF3"=>[142,"9"],"\xF4"=>[150,"9"],"\xF5"=>[74,"9"],"\xF6"=>[90,"9"],"\xF7"=>[98,"9"],"\xF8"=>[107,"9"],"\xF9"=>[140,"9"],"\xFA"=>[146,"9"],"\xFB"=>[102,"9"],"\xFC"=>[113,"9"],"\xFD"=>[121,"9"],"\xFE"=>[128,"9"],"\xFF"=>[12,"9"],],["\x00"=>[92,"0"],"\x01"=>[95,"0"],"\x02"=>[137,"0"],"\x03"=>[142,"0"],"\x04"=>[150,"0"],"\x05"=>[74,"0"],"\x06"=>[90,"0"],"\x07"=>[98,"0"],"\x08"=>[107,"0"],"\x09"=>[140,"0"],"\x0A"=>[146,"0"],"\x0B"=>[102,"0"],"\x0C"=>[113,"0"],"\x0D"=>[121,"0"],"\x0E"=>[128,"0"],"\x0F"=>[12,"0"],"\x10"=>[92,"1"],"\x11"=>[95,"1"],"\x12"=>[137,"1"],"\x13"=>[142,"1"],"\x14"=>[150,"1"],"\x15"=>[74,"1"],"\x16"=>[90,"1"],"\x17"=>[98,"1"],"\x18"=>[107,"1"],"\x19"=>[140,"1"],"\x1A"=>[146,"1"],"\x1B"=>[102,"1"],"\x1C"=>[113,"1"],"\x1D"=>[121,"1"],"\x1E"=>[128,"1"],"\x1F"=>[12,"1"],"\x20"=>[92,"2"],"\x21"=>[95,"2"],"\x22"=>[137,"2"],"\x23"=>[142,"2"],"\x24"=>[150,"2"],"\x25"=>[74,"2"],"\x26"=>[90,"2"],"\x27"=>[98,"2"],"\x28"=>[107,"2"],"\x29"=>[140,"2"],"\x2A"=>[146,"2"],"\x2B"=>[102,"2"],"\x2C"=>[113,"2"],"-"=>[121,"2"],"."=>[128,"2"],"\x2F"=>[12,"2"],[92,"a"],[95,"a"],[137,"a"],[142,"a"],[150,"a"],[74,"a"],[90,"a"],[98,"a"],[107,"a"],[140,"a"],"\x3A"=>[146,"a"],"\x3B"=>[102,"a"],"\x3C"=>[113,"a"],"\x3D"=>[121,"a"],"\x3E"=>[128,"a"],"\x3F"=>[12,"a"],"\x40"=>[92,"c"],"A"=>[95,"c"],"B"=>[137,"c"],"C"=>[142,"c"],"D"=>[150,"c"],"E"=>[74,"c"],"F"=>[90,"c"],"G"=>[98,"c"],"H"=>[107,"c"],"I"=>[140,"c"],"J"=>[146,"c"],"K"=>[102,"c"],"L"=>[113,"c"],"M"=>[121,"c"],"N"=>[128,"c"],"O"=>[12,"c"],"P"=>[92,"e"],"Q"=>[95,"e"],"R"=>[137,"e"],"S"=>[142,"e"],"T"=>[150,"e"],"U"=>[74,"e"],"V"=>[90,"e"],"W"=>[98,"e"],"X"=>[107,"e"],"Y"=>[140,"e"],"Z"=>[146,"e"],"\x5B"=>[102,"e"],"\x5C"=>[113,"e"],"\x5D"=>[121,"e"],"\x5E"=>[128,"e"],"_"=>[12,"e"],"\x60"=>[92,"i"],"a"=>[95,"i"],"b"=>[137,"i"],"c"=>[142,"i"],"d"=>[150,"i"],"e"=>[74,"i"],"f"=>[90,"i"],"g"=>[98,"i"],"h"=>[107,"i"],"i"=>[140,"i"],"j"=>[146,"i"],"k"=>[102,"i"],"l"=>[113,"i"],"m"=>[121,"i"],"n"=>[128,"i"],"o"=>[12,"i"],"p"=>[92,"o"],"q"=>[95,"o"],"r"=>[137,"o"],"s"=>[142,"o"],"t"=>[150,"o"],"u"=>[74,"o"],"v"=>[90,"o"],"w"=>[98,"o"],"x"=>[107,"o"],"y"=>[140,"o"],"z"=>[146,"o"],"\x7B"=>[102,"o"],"\x7C"=>[113,"o"],"\x7D"=>[121,"o"],"~"=>[128,"o"],"\x7F"=>[12,"o"],"\x80"=>[92,"s"],"\x81"=>[95,"s"],"\x82"=>[137,"s"],"\x83"=>[142,"s"],"\x84"=>[150,"s"],"\x85"=>[74,"s"],"\x86"=>[90,"s"],"\x87"=>[98,"s"],"\x88"=>[107,"s"],"\x89"=>[140,"s"],"\x8A"=>[146,"s"],"\x8B"=>[102,"s"],"\x8C"=>[113,"s"],"\x8D"=>[121,"s"],"\x8E"=>[128,"s"],"\x8F"=>[12,"s"],"\x90"=>[92,"t"],"\x91"=>[95,"t"],"\x92"=>[137,"t"],"\x93"=>[142,"t"],"\x94"=>[150,"t"],"\x95"=>[74,"t"],"\x96"=>[90,"t"],"\x97"=>[98,"t"],"\x98"=>[107,"t"],"\x99"=>[140,"t"],"\x9A"=>[146,"t"],"\x9B"=>[102,"t"],"\x9C"=>[113,"t"],"\x9D"=>[121,"t"],"\x9E"=>[128,"t"],"\x9F"=>[12,"t"],"\xA0"=>[93,"\x20"],"\xA1"=>[138,"\x20"],"\xA2"=>[75,"\x20"],"\xA3"=>[91,"\x20"],"\xA4"=>[108,"\x20"],"\xA5"=>[103,"\x20"],"\xA6"=>[114,"\x20"],"\xA7"=>[14,"\x20"],"\xA8"=>[93,"\x25"],"\xA9"=>[138,"\x25"],"\xAA"=>[75,"\x25"],"\xAB"=>[91,"\x25"],"\xAC"=>[108,"\x25"],"\xAD"=>[103,"\x25"],"\xAE"=>[114,"\x25"],"\xAF"=>[14,"\x25"],"\xB0"=>[93,"-"],"\xB1"=>[138,"-"],"\xB2"=>[75,"-"],"\xB3"=>[91,"-"],"\xB4"=>[108,"-"],"\xB5"=>[103,"-"],"\xB6"=>[114,"-"],"\xB7"=>[14,"-"],"\xB8"=>[93,"."],"\xB9"=>[138,"."],"\xBA"=>[75,"."],"\xBB"=>[91,"."],"\xBC"=>[108,"."],"\xBD"=>[103,"."],"\xBE"=>[114,"."],"\xBF"=>[14,"."],"\xC0"=>[93,"\x2F"],"\xC1"=>[138,"\x2F"],"\xC2"=>[75,"\x2F"],"\xC3"=>[91,"\x2F"],"\xC4"=>[108,"\x2F"],"\xC5"=>[103,"\x2F"],"\xC6"=>[114,"\x2F"],"\xC7"=>[14,"\x2F"],"\xC8"=>[93,"3"],"\xC9"=>[138,"3"],"\xCA"=>[75,"3"],"\xCB"=>[91,"3"],"\xCC"=>[108,"3"],"\xCD"=>[103,"3"],"\xCE"=>[114,"3"],"\xCF"=>[14,"3"],"\xD0"=>[93,"4"],"\xD1"=>[138,"4"],"\xD2"=>[75,"4"],"\xD3"=>[91,"4"],"\xD4"=>[108,"4"],"\xD5"=>[103,"4"],"\xD6"=>[114,"4"],"\xD7"=>[14,"4"],"\xD8"=>[93,"5"],"\xD9"=>[138,"5"],"\xDA"=>[75,"5"],"\xDB"=>[91,"5"],"\xDC"=>[108,"5"],"\xDD"=>[103,"5"],"\xDE"=>[114,"5"],"\xDF"=>[14,"5"],"\xE0"=>[93,"6"],"\xE1"=>[138,"6"],"\xE2"=>[75,"6"],"\xE3"=>[91,"6"],"\xE4"=>[108,"6"],"\xE5"=>[103,"6"],"\xE6"=>[114,"6"],"\xE7"=>[14,"6"],"\xE8"=>[93,"7"],"\xE9"=>[138,"7"],"\xEA"=>[75,"7"],"\xEB"=>[91,"7"],"\xEC"=>[108,"7"],"\xED"=>[103,"7"],"\xEE"=>[114,"7"],"\xEF"=>[14,"7"],"\xF0"=>[93,"8"],"\xF1"=>[138,"8"],"\xF2"=>[75,"8"],"\xF3"=>[91,"8"],"\xF4"=>[108,"8"],"\xF5"=>[103,"8"],"\xF6"=>[114,"8"],"\xF7"=>[14,"8"],"\xF8"=>[93,"9"],"\xF9"=>[138,"9"],"\xFA"=>[75,"9"],"\xFB"=>[91,"9"],"\xFC"=>[108,"9"],"\xFD"=>[103,"9"],"\xFE"=>[114,"9"],"\xFF"=>[14,"9"],],["\x00"=>[77,"\x210"],"\x01"=>[18,"\x210"],"\x02"=>[77,"\x211"],"\x03"=>[18,"\x211"],"\x04"=>[77,"\x212"],"\x05"=>[18,"\x212"],"\x06"=>[77,"\x21a"],"\x07"=>[18,"\x21a"],"\x08"=>[77,"\x21c"],"\x09"=>[18,"\x21c"],"\x0A"=>[77,"\x21e"],"\x0B"=>[18,"\x21e"],"\x0C"=>[77,"\x21i"],"\x0D"=>[18,"\x21i"],"\x0E"=>[77,"\x21o"],"\x0F"=>[18,"\x21o"],"\x10"=>[77,"\x21s"],"\x11"=>[18,"\x21s"],"\x12"=>[77,"\x21t"],"\x13"=>[18,"\x21t"],"\x14"=>[0,"\x21\x20"],"\x15"=>[0,"\x21\x25"],"\x16"=>[0,"\x21-"],"\x17"=>[0,"\x21."],"\x18"=>[0,"\x21\x2F"],"\x19"=>[0,"\x213"],"\x1A"=>[0,"\x214"],"\x1B"=>[0,"\x215"],"\x1C"=>[0,"\x216"],"\x1D"=>[0,"\x217"],"\x1E"=>[0,"\x218"],"\x1F"=>[0,"\x219"],"\x20"=>[0,"\x21\x3D"],"\x21"=>[0,"\x21A"],"\x22"=>[0,"\x21_"],"\x23"=>[0,"\x21b"],"\x24"=>[0,"\x21d"],"\x25"=>[0,"\x21f"],"\x26"=>[0,"\x21g"],"\x27"=>[0,"\x21h"],"\x28"=>[0,"\x21l"],"\x29"=>[0,"\x21m"],"\x2A"=>[0,"\x21n"],"\x2B"=>[0,"\x21p"],"\x2C"=>[0,"\x21r"],"-"=>[0,"\x21u"],"."=>[100,"\x21"],"\x2F"=>[110,"\x21"],[111,"\x21"],[115,"\x21"],[116,"\x21"],[118,"\x21"],[119,"\x21"],[122,"\x21"],[123,"\x21"],[125,"\x21"],[126,"\x21"],[129,"\x21"],"\x3A"=>[143,"\x21"],"\x3B"=>[148,"\x21"],"\x3C"=>[151,"\x21"],"\x3D"=>[153,"\x21"],"\x3E"=>[83,"\x21"],"\x3F"=>[10,"\x21"],"\x40"=>[77,"\x220"],"A"=>[18,"\x220"],"B"=>[77,"\x221"],"C"=>[18,"\x221"],"D"=>[77,"\x222"],"E"=>[18,"\x222"],"F"=>[77,"\x22a"],"G"=>[18,"\x22a"],"H"=>[77,"\x22c"],"I"=>[18,"\x22c"],"J"=>[77,"\x22e"],"K"=>[18,"\x22e"],"L"=>[77,"\x22i"],"M"=>[18,"\x22i"],"N"=>[77,"\x22o"],"O"=>[18,"\x22o"],"P"=>[77,"\x22s"],"Q"=>[18,"\x22s"],"R"=>[77,"\x22t"],"S"=>[18,"\x22t"],"T"=>[0,"\x22\x20"],"U"=>[0,"\x22\x25"],"V"=>[0,"\x22-"],"W"=>[0,"\x22."],"X"=>[0,"\x22\x2F"],"Y"=>[0,"\x223"],"Z"=>[0,"\x224"],"\x5B"=>[0,"\x225"],"\x5C"=>[0,"\x226"],"\x5D"=>[0,"\x227"],"\x5E"=>[0,"\x228"],"_"=>[0,"\x229"],"\x60"=>[0,"\x22\x3D"],"a"=>[0,"\x22A"],"b"=>[0,"\x22_"],"c"=>[0,"\x22b"],"d"=>[0,"\x22d"],"e"=>[0,"\x22f"],"f"=>[0,"\x22g"],"g"=>[0,"\x22h"],"h"=>[0,"\x22l"],"i"=>[0,"\x22m"],"j"=>[0,"\x22n"],"k"=>[0,"\x22p"],"l"=>[0,"\x22r"],"m"=>[0,"\x22u"],"n"=>[100,"\x22"],"o"=>[110,"\x22"],"p"=>[111,"\x22"],"q"=>[115,"\x22"],"r"=>[116,"\x22"],"s"=>[118,"\x22"],"t"=>[119,"\x22"],"u"=>[122,"\x22"],"v"=>[123,"\x22"],"w"=>[125,"\x22"],"x"=>[126,"\x22"],"y"=>[129,"\x22"],"z"=>[143,"\x22"],"\x7B"=>[148,"\x22"],"\x7C"=>[151,"\x22"],"\x7D"=>[153,"\x22"],"~"=>[83,"\x22"],"\x7F"=>[10,"\x22"],"\x80"=>[77,"\x280"],"\x81"=>[18,"\x280"],"\x82"=>[77,"\x281"],"\x83"=>[18,"\x281"],"\x84"=>[77,"\x282"],"\x85"=>[18,"\x282"],"\x86"=>[77,"\x28a"],"\x87"=>[18,"\x28a"],"\x88"=>[77,"\x28c"],"\x89"=>[18,"\x28c"],"\x8A"=>[77,"\x28e"],"\x8B"=>[18,"\x28e"],"\x8C"=>[77,"\x28i"],"\x8D"=>[18,"\x28i"],"\x8E"=>[77,"\x28o"],"\x8F"=>[18,"\x28o"],"\x90"=>[77,"\x28s"],"\x91"=>[18,"\x28s"],"\x92"=>[77,"\x28t"],"\x93"=>[18,"\x28t"],"\x94"=>[0,"\x28\x20"],"\x95"=>[0,"\x28\x25"],"\x96"=>[0,"\x28-"],"\x97"=>[0,"\x28."],"\x98"=>[0,"\x28\x2F"],"\x99"=>[0,"\x283"],"\x9A"=>[0,"\x284"],"\x9B"=>[0,"\x285"],"\x9C"=>[0,"\x286"],"\x9D"=>[0,"\x287"],"\x9E"=>[0,"\x288"],"\x9F"=>[0,"\x289"],"\xA0"=>[0,"\x28\x3D"],"\xA1"=>[0,"\x28A"],"\xA2"=>[0,"\x28_"],"\xA3"=>[0,"\x28b"],"\xA4"=>[0,"\x28d"],"\xA5"=>[0,"\x28f"],"\xA6"=>[0,"\x28g"],"\xA7"=>[0,"\x28h"],"\xA8"=>[0,"\x28l"],"\xA9"=>[0,"\x28m"],"\xAA"=>[0,"\x28n"],"\xAB"=>[0,"\x28p"],"\xAC"=>[0,"\x28r"],"\xAD"=>[0,"\x28u"],"\xAE"=>[100,"\x28"],"\xAF"=>[110,"\x28"],"\xB0"=>[111,"\x28"],"\xB1"=>[115,"\x28"],"\xB2"=>[116,"\x28"],"\xB3"=>[118,"\x28"],"\xB4"=>[119,"\x28"],"\xB5"=>[122,"\x28"],"\xB6"=>[123,"\x28"],"\xB7"=>[125,"\x28"],"\xB8"=>[126,"\x28"],"\xB9"=>[129,"\x28"],"\xBA"=>[143,"\x28"],"\xBB"=>[148,"\x28"],"\xBC"=>[151,"\x28"],"\xBD"=>[153,"\x28"],"\xBE"=>[83,"\x28"],"\xBF"=>[10,"\x28"],"\xC0"=>[77,"\x290"],"\xC1"=>[18,"\x290"],"\xC2"=>[77,"\x291"],"\xC3"=>[18,"\x291"],"\xC4"=>[77,"\x292"],"\xC5"=>[18,"\x292"],"\xC6"=>[77,"\x29a"],"\xC7"=>[18,"\x29a"],"\xC8"=>[77,"\x29c"],"\xC9"=>[18,"\x29c"],"\xCA"=>[77,"\x29e"],"\xCB"=>[18,"\x29e"],"\xCC"=>[77,"\x29i"],"\xCD"=>[18,"\x29i"],"\xCE"=>[77,"\x29o"],"\xCF"=>[18,"\x29o"],"\xD0"=>[77,"\x29s"],"\xD1"=>[18,"\x29s"],"\xD2"=>[77,"\x29t"],"\xD3"=>[18,"\x29t"],"\xD4"=>[0,"\x29\x20"],"\xD5"=>[0,"\x29\x25"],"\xD6"=>[0,"\x29-"],"\xD7"=>[0,"\x29."],"\xD8"=>[0,"\x29\x2F"],"\xD9"=>[0,"\x293"],"\xDA"=>[0,"\x294"],"\xDB"=>[0,"\x295"],"\xDC"=>[0,"\x296"],"\xDD"=>[0,"\x297"],"\xDE"=>[0,"\x298"],"\xDF"=>[0,"\x299"],"\xE0"=>[0,"\x29\x3D"],"\xE1"=>[0,"\x29A"],"\xE2"=>[0,"\x29_"],"\xE3"=>[0,"\x29b"],"\xE4"=>[0,"\x29d"],"\xE5"=>[0,"\x29f"],"\xE6"=>[0,"\x29g"],"\xE7"=>[0,"\x29h"],"\xE8"=>[0,"\x29l"],"\xE9"=>[0,"\x29m"],"\xEA"=>[0,"\x29n"],"\xEB"=>[0,"\x29p"],"\xEC"=>[0,"\x29r"],"\xED"=>[0,"\x29u"],"\xEE"=>[100,"\x29"],"\xEF"=>[110,"\x29"],"\xF0"=>[111,"\x29"],"\xF1"=>[115,"\x29"],"\xF2"=>[116,"\x29"],"\xF3"=>[118,"\x29"],"\xF4"=>[119,"\x29"],"\xF5"=>[122,"\x29"],"\xF6"=>[123,"\x29"],"\xF7"=>[125,"\x29"],"\xF8"=>[126,"\x29"],"\xF9"=>[129,"\x29"],"\xFA"=>[143,"\x29"],"\xFB"=>[148,"\x29"],"\xFC"=>[151,"\x29"],"\xFD"=>[153,"\x29"],"\xFE"=>[83,"\x29"],"\xFF"=>[10,"\x29"],],["\x00"=>[94,"\x210"],"\x01"=>[76,"\x210"],"\x02"=>[104,"\x210"],"\x03"=>[16,"\x210"],"\x04"=>[94,"\x211"],"\x05"=>[76,"\x211"],"\x06"=>[104,"\x211"],"\x07"=>[16,"\x211"],"\x08"=>[94,"\x212"],"\x09"=>[76,"\x212"],"\x0A"=>[104,"\x212"],"\x0B"=>[16,"\x212"],"\x0C"=>[94,"\x21a"],"\x0D"=>[76,"\x21a"],"\x0E"=>[104,"\x21a"],"\x0F"=>[16,"\x21a"],"\x10"=>[94,"\x21c"],"\x11"=>[76,"\x21c"],"\x12"=>[104,"\x21c"],"\x13"=>[16,"\x21c"],"\x14"=>[94,"\x21e"],"\x15"=>[76,"\x21e"],"\x16"=>[104,"\x21e"],"\x17"=>[16,"\x21e"],"\x18"=>[94,"\x21i"],"\x19"=>[76,"\x21i"],"\x1A"=>[104,"\x21i"],"\x1B"=>[16,"\x21i"],"\x1C"=>[94,"\x21o"],"\x1D"=>[76,"\x21o"],"\x1E"=>[104,"\x21o"],"\x1F"=>[16,"\x21o"],"\x20"=>[94,"\x21s"],"\x21"=>[76,"\x21s"],"\x22"=>[104,"\x21s"],"\x23"=>[16,"\x21s"],"\x24"=>[94,"\x21t"],"\x25"=>[76,"\x21t"],"\x26"=>[104,"\x21t"],"\x27"=>[16,"\x21t"],"\x28"=>[77,"\x21\x20"],"\x29"=>[18,"\x21\x20"],"\x2A"=>[77,"\x21\x25"],"\x2B"=>[18,"\x21\x25"],"\x2C"=>[77,"\x21-"],"-"=>[18,"\x21-"],"."=>[77,"\x21."],"\x2F"=>[18,"\x21."],[77,"\x21\x2F"],[18,"\x21\x2F"],[77,"\x213"],[18,"\x213"],[77,"\x214"],[18,"\x214"],[77,"\x215"],[18,"\x215"],[77,"\x216"],[18,"\x216"],"\x3A"=>[77,"\x217"],"\x3B"=>[18,"\x217"],"\x3C"=>[77,"\x218"],"\x3D"=>[18,"\x218"],"\x3E"=>[77,"\x219"],"\x3F"=>[18,"\x219"],"\x40"=>[77,"\x21\x3D"],"A"=>[18,"\x21\x3D"],"B"=>[77,"\x21A"],"C"=>[18,"\x21A"],"D"=>[77,"\x21_"],"E"=>[18,"\x21_"],"F"=>[77,"\x21b"],"G"=>[18,"\x21b"],"H"=>[77,"\x21d"],"I"=>[18,"\x21d"],"J"=>[77,"\x21f"],"K"=>[18,"\x21f"],"L"=>[77,"\x21g"],"M"=>[18,"\x21g"],"N"=>[77,"\x21h"],"O"=>[18,"\x21h"],"P"=>[77,"\x21l"],"Q"=>[18,"\x21l"],"R"=>[77,"\x21m"],"S"=>[18,"\x21m"],"T"=>[77,"\x21n"],"U"=>[18,"\x21n"],"V"=>[77,"\x21p"],"W"=>[18,"\x21p"],"X"=>[77,"\x21r"],"Y"=>[18,"\x21r"],"Z"=>[77,"\x21u"],"\x5B"=>[18,"\x21u"],"\x5C"=>[0,"\x21\x3A"],"\x5D"=>[0,"\x21B"],"\x5E"=>[0,"\x21C"],"_"=>[0,"\x21D"],"\x60"=>[0,"\x21E"],"a"=>[0,"\x21F"],"b"=>[0,"\x21G"],"c"=>[0,"\x21H"],"d"=>[0,"\x21I"],"e"=>[0,"\x21J"],"f"=>[0,"\x21K"],"g"=>[0,"\x21L"],"h"=>[0,"\x21M"],"i"=>[0,"\x21N"],"j"=>[0,"\x21O"],"k"=>[0,"\x21P"],"l"=>[0,"\x21Q"],"m"=>[0,"\x21R"],"n"=>[0,"\x21S"],"o"=>[0,"\x21T"],"p"=>[0,"\x21U"],"q"=>[0,"\x21V"],"r"=>[0,"\x21W"],"s"=>[0,"\x21Y"],"t"=>[0,"\x21j"],"u"=>[0,"\x21k"],"v"=>[0,"\x21q"],"w"=>[0,"\x21v"],"x"=>[0,"\x21w"],"y"=>[0,"\x21x"],"z"=>[0,"\x21y"],"\x7B"=>[0,"\x21z"],"\x7C"=>[82,"\x21"],"\x7D"=>[87,"\x21"],"~"=>[130,"\x21"],"\x7F"=>[9,"\x21"],"\x80"=>[94,"\x220"],"\x81"=>[76,"\x220"],"\x82"=>[104,"\x220"],"\x83"=>[16,"\x220"],"\x84"=>[94,"\x221"],"\x85"=>[76,"\x221"],"\x86"=>[104,"\x221"],"\x87"=>[16,"\x221"],"\x88"=>[94,"\x222"],"\x89"=>[76,"\x222"],"\x8A"=>[104,"\x222"],"\x8B"=>[16,"\x222"],"\x8C"=>[94,"\x22a"],"\x8D"=>[76,"\x22a"],"\x8E"=>[104,"\x22a"],"\x8F"=>[16,"\x22a"],"\x90"=>[94,"\x22c"],"\x91"=>[76,"\x22c"],"\x92"=>[104,"\x22c"],"\x93"=>[16,"\x22c"],"\x94"=>[94,"\x22e"],"\x95"=>[76,"\x22e"],"\x96"=>[104,"\x22e"],"\x97"=>[16,"\x22e"],"\x98"=>[94,"\x22i"],"\x99"=>[76,"\x22i"],"\x9A"=>[104,"\x22i"],"\x9B"=>[16,"\x22i"],"\x9C"=>[94,"\x22o"],"\x9D"=>[76,"\x22o"],"\x9E"=>[104,"\x22o"],"\x9F"=>[16,"\x22o"],"\xA0"=>[94,"\x22s"],"\xA1"=>[76,"\x22s"],"\xA2"=>[104,"\x22s"],"\xA3"=>[16,"\x22s"],"\xA4"=>[94,"\x22t"],"\xA5"=>[76,"\x22t"],"\xA6"=>[104,"\x22t"],"\xA7"=>[16,"\x22t"],"\xA8"=>[77,"\x22\x20"],"\xA9"=>[18,"\x22\x20"],"\xAA"=>[77,"\x22\x25"],"\xAB"=>[18,"\x22\x25"],"\xAC"=>[77,"\x22-"],"\xAD"=>[18,"\x22-"],"\xAE"=>[77,"\x22."],"\xAF"=>[18,"\x22."],"\xB0"=>[77,"\x22\x2F"],"\xB1"=>[18,"\x22\x2F"],"\xB2"=>[77,"\x223"],"\xB3"=>[18,"\x223"],"\xB4"=>[77,"\x224"],"\xB5"=>[18,"\x224"],"\xB6"=>[77,"\x225"],"\xB7"=>[18,"\x225"],"\xB8"=>[77,"\x226"],"\xB9"=>[18,"\x226"],"\xBA"=>[77,"\x227"],"\xBB"=>[18,"\x227"],"\xBC"=>[77,"\x228"],"\xBD"=>[18,"\x228"],"\xBE"=>[77,"\x229"],"\xBF"=>[18,"\x229"],"\xC0"=>[77,"\x22\x3D"],"\xC1"=>[18,"\x22\x3D"],"\xC2"=>[77,"\x22A"],"\xC3"=>[18,"\x22A"],"\xC4"=>[77,"\x22_"],"\xC5"=>[18,"\x22_"],"\xC6"=>[77,"\x22b"],"\xC7"=>[18,"\x22b"],"\xC8"=>[77,"\x22d"],"\xC9"=>[18,"\x22d"],"\xCA"=>[77,"\x22f"],"\xCB"=>[18,"\x22f"],"\xCC"=>[77,"\x22g"],"\xCD"=>[18,"\x22g"],"\xCE"=>[77,"\x22h"],"\xCF"=>[18,"\x22h"],"\xD0"=>[77,"\x22l"],"\xD1"=>[18,"\x22l"],"\xD2"=>[77,"\x22m"],"\xD3"=>[18,"\x22m"],"\xD4"=>[77,"\x22n"],"\xD5"=>[18,"\x22n"],"\xD6"=>[77,"\x22p"],"\xD7"=>[18,"\x22p"],"\xD8"=>[77,"\x22r"],"\xD9"=>[18,"\x22r"],"\xDA"=>[77,"\x22u"],"\xDB"=>[18,"\x22u"],"\xDC"=>[0,"\x22\x3A"],"\xDD"=>[0,"\x22B"],"\xDE"=>[0,"\x22C"],"\xDF"=>[0,"\x22D"],"\xE0"=>[0,"\x22E"],"\xE1"=>[0,"\x22F"],"\xE2"=>[0,"\x22G"],"\xE3"=>[0,"\x22H"],"\xE4"=>[0,"\x22I"],"\xE5"=>[0,"\x22J"],"\xE6"=>[0,"\x22K"],"\xE7"=>[0,"\x22L"],"\xE8"=>[0,"\x22M"],"\xE9"=>[0,"\x22N"],"\xEA"=>[0,"\x22O"],"\xEB"=>[0,"\x22P"],"\xEC"=>[0,"\x22Q"],"\xED"=>[0,"\x22R"],"\xEE"=>[0,"\x22S"],"\xEF"=>[0,"\x22T"],"\xF0"=>[0,"\x22U"],"\xF1"=>[0,"\x22V"],"\xF2"=>[0,"\x22W"],"\xF3"=>[0,"\x22Y"],"\xF4"=>[0,"\x22j"],"\xF5"=>[0,"\x22k"],"\xF6"=>[0,"\x22q"],"\xF7"=>[0,"\x22v"],"\xF8"=>[0,"\x22w"],"\xF9"=>[0,"\x22x"],"\xFA"=>[0,"\x22y"],"\xFB"=>[0,"\x22z"],"\xFC"=>[82,"\x22"],"\xFD"=>[87,"\x22"],"\xFE"=>[130,"\x22"],"\xFF"=>[9,"\x22"],],["\x00"=>[94,"\x230"],"\x01"=>[76,"\x230"],"\x02"=>[104,"\x230"],"\x03"=>[16,"\x230"],"\x04"=>[94,"\x231"],"\x05"=>[76,"\x231"],"\x06"=>[104,"\x231"],"\x07"=>[16,"\x231"],"\x08"=>[94,"\x232"],"\x09"=>[76,"\x232"],"\x0A"=>[104,"\x232"],"\x0B"=>[16,"\x232"],"\x0C"=>[94,"\x23a"],"\x0D"=>[76,"\x23a"],"\x0E"=>[104,"\x23a"],"\x0F"=>[16,"\x23a"],"\x10"=>[94,"\x23c"],"\x11"=>[76,"\x23c"],"\x12"=>[104,"\x23c"],"\x13"=>[16,"\x23c"],"\x14"=>[94,"\x23e"],"\x15"=>[76,"\x23e"],"\x16"=>[104,"\x23e"],"\x17"=>[16,"\x23e"],"\x18"=>[94,"\x23i"],"\x19"=>[76,"\x23i"],"\x1A"=>[104,"\x23i"],"\x1B"=>[16,"\x23i"],"\x1C"=>[94,"\x23o"],"\x1D"=>[76,"\x23o"],"\x1E"=>[104,"\x23o"],"\x1F"=>[16,"\x23o"],"\x20"=>[94,"\x23s"],"\x21"=>[76,"\x23s"],"\x22"=>[104,"\x23s"],"\x23"=>[16,"\x23s"],"\x24"=>[94,"\x23t"],"\x25"=>[76,"\x23t"],"\x26"=>[104,"\x23t"],"\x27"=>[16,"\x23t"],"\x28"=>[77,"\x23\x20"],"\x29"=>[18,"\x23\x20"],"\x2A"=>[77,"\x23\x25"],"\x2B"=>[18,"\x23\x25"],"\x2C"=>[77,"\x23-"],"-"=>[18,"\x23-"],"."=>[77,"\x23."],"\x2F"=>[18,"\x23."],[77,"\x23\x2F"],[18,"\x23\x2F"],[77,"\x233"],[18,"\x233"],[77,"\x234"],[18,"\x234"],[77,"\x235"],[18,"\x235"],[77,"\x236"],[18,"\x236"],"\x3A"=>[77,"\x237"],"\x3B"=>[18,"\x237"],"\x3C"=>[77,"\x238"],"\x3D"=>[18,"\x238"],"\x3E"=>[77,"\x239"],"\x3F"=>[18,"\x239"],"\x40"=>[77,"\x23\x3D"],"A"=>[18,"\x23\x3D"],"B"=>[77,"\x23A"],"C"=>[18,"\x23A"],"D"=>[77,"\x23_"],"E"=>[18,"\x23_"],"F"=>[77,"\x23b"],"G"=>[18,"\x23b"],"H"=>[77,"\x23d"],"I"=>[18,"\x23d"],"J"=>[77,"\x23f"],"K"=>[18,"\x23f"],"L"=>[77,"\x23g"],"M"=>[18,"\x23g"],"N"=>[77,"\x23h"],"O"=>[18,"\x23h"],"P"=>[77,"\x23l"],"Q"=>[18,"\x23l"],"R"=>[77,"\x23m"],"S"=>[18,"\x23m"],"T"=>[77,"\x23n"],"U"=>[18,"\x23n"],"V"=>[77,"\x23p"],"W"=>[18,"\x23p"],"X"=>[77,"\x23r"],"Y"=>[18,"\x23r"],"Z"=>[77,"\x23u"],"\x5B"=>[18,"\x23u"],"\x5C"=>[0,"\x23\x3A"],"\x5D"=>[0,"\x23B"],"\x5E"=>[0,"\x23C"],"_"=>[0,"\x23D"],"\x60"=>[0,"\x23E"],"a"=>[0,"\x23F"],"b"=>[0,"\x23G"],"c"=>[0,"\x23H"],"d"=>[0,"\x23I"],"e"=>[0,"\x23J"],"f"=>[0,"\x23K"],"g"=>[0,"\x23L"],"h"=>[0,"\x23M"],"i"=>[0,"\x23N"],"j"=>[0,"\x23O"],"k"=>[0,"\x23P"],"l"=>[0,"\x23Q"],"m"=>[0,"\x23R"],"n"=>[0,"\x23S"],"o"=>[0,"\x23T"],"p"=>[0,"\x23U"],"q"=>[0,"\x23V"],"r"=>[0,"\x23W"],"s"=>[0,"\x23Y"],"t"=>[0,"\x23j"],"u"=>[0,"\x23k"],"v"=>[0,"\x23q"],"w"=>[0,"\x23v"],"x"=>[0,"\x23w"],"y"=>[0,"\x23x"],"z"=>[0,"\x23y"],"\x7B"=>[0,"\x23z"],"\x7C"=>[82,"\x23"],"\x7D"=>[87,"\x23"],"~"=>[130,"\x23"],"\x7F"=>[9,"\x23"],"\x80"=>[94,"\x3E0"],"\x81"=>[76,"\x3E0"],"\x82"=>[104,"\x3E0"],"\x83"=>[16,"\x3E0"],"\x84"=>[94,"\x3E1"],"\x85"=>[76,"\x3E1"],"\x86"=>[104,"\x3E1"],"\x87"=>[16,"\x3E1"],"\x88"=>[94,"\x3E2"],"\x89"=>[76,"\x3E2"],"\x8A"=>[104,"\x3E2"],"\x8B"=>[16,"\x3E2"],"\x8C"=>[94,"\x3Ea"],"\x8D"=>[76,"\x3Ea"],"\x8E"=>[104,"\x3Ea"],"\x8F"=>[16,"\x3Ea"],"\x90"=>[94,"\x3Ec"],"\x91"=>[76,"\x3Ec"],"\x92"=>[104,"\x3Ec"],"\x93"=>[16,"\x3Ec"],"\x94"=>[94,"\x3Ee"],"\x95"=>[76,"\x3Ee"],"\x96"=>[104,"\x3Ee"],"\x97"=>[16,"\x3Ee"],"\x98"=>[94,"\x3Ei"],"\x99"=>[76,"\x3Ei"],"\x9A"=>[104,"\x3Ei"],"\x9B"=>[16,"\x3Ei"],"\x9C"=>[94,"\x3Eo"],"\x9D"=>[76,"\x3Eo"],"\x9E"=>[104,"\x3Eo"],"\x9F"=>[16,"\x3Eo"],"\xA0"=>[94,"\x3Es"],"\xA1"=>[76,"\x3Es"],"\xA2"=>[104,"\x3Es"],"\xA3"=>[16,"\x3Es"],"\xA4"=>[94,"\x3Et"],"\xA5"=>[76,"\x3Et"],"\xA6"=>[104,"\x3Et"],"\xA7"=>[16,"\x3Et"],"\xA8"=>[77,"\x3E\x20"],"\xA9"=>[18,"\x3E\x20"],"\xAA"=>[77,"\x3E\x25"],"\xAB"=>[18,"\x3E\x25"],"\xAC"=>[77,"\x3E-"],"\xAD"=>[18,"\x3E-"],"\xAE"=>[77,"\x3E."],"\xAF"=>[18,"\x3E."],"\xB0"=>[77,"\x3E\x2F"],"\xB1"=>[18,"\x3E\x2F"],"\xB2"=>[77,"\x3E3"],"\xB3"=>[18,"\x3E3"],"\xB4"=>[77,"\x3E4"],"\xB5"=>[18,"\x3E4"],"\xB6"=>[77,"\x3E5"],"\xB7"=>[18,"\x3E5"],"\xB8"=>[77,"\x3E6"],"\xB9"=>[18,"\x3E6"],"\xBA"=>[77,"\x3E7"],"\xBB"=>[18,"\x3E7"],"\xBC"=>[77,"\x3E8"],"\xBD"=>[18,"\x3E8"],"\xBE"=>[77,"\x3E9"],"\xBF"=>[18,"\x3E9"],"\xC0"=>[77,"\x3E\x3D"],"\xC1"=>[18,"\x3E\x3D"],"\xC2"=>[77,"\x3EA"],"\xC3"=>[18,"\x3EA"],"\xC4"=>[77,"\x3E_"],"\xC5"=>[18,"\x3E_"],"\xC6"=>[77,"\x3Eb"],"\xC7"=>[18,"\x3Eb"],"\xC8"=>[77,"\x3Ed"],"\xC9"=>[18,"\x3Ed"],"\xCA"=>[77,"\x3Ef"],"\xCB"=>[18,"\x3Ef"],"\xCC"=>[77,"\x3Eg"],"\xCD"=>[18,"\x3Eg"],"\xCE"=>[77,"\x3Eh"],"\xCF"=>[18,"\x3Eh"],"\xD0"=>[77,"\x3El"],"\xD1"=>[18,"\x3El"],"\xD2"=>[77,"\x3Em"],"\xD3"=>[18,"\x3Em"],"\xD4"=>[77,"\x3En"],"\xD5"=>[18,"\x3En"],"\xD6"=>[77,"\x3Ep"],"\xD7"=>[18,"\x3Ep"],"\xD8"=>[77,"\x3Er"],"\xD9"=>[18,"\x3Er"],"\xDA"=>[77,"\x3Eu"],"\xDB"=>[18,"\x3Eu"],"\xDC"=>[0,"\x3E\x3A"],"\xDD"=>[0,"\x3EB"],"\xDE"=>[0,"\x3EC"],"\xDF"=>[0,"\x3ED"],"\xE0"=>[0,"\x3EE"],"\xE1"=>[0,"\x3EF"],"\xE2"=>[0,"\x3EG"],"\xE3"=>[0,"\x3EH"],"\xE4"=>[0,"\x3EI"],"\xE5"=>[0,"\x3EJ"],"\xE6"=>[0,"\x3EK"],"\xE7"=>[0,"\x3EL"],"\xE8"=>[0,"\x3EM"],"\xE9"=>[0,"\x3EN"],"\xEA"=>[0,"\x3EO"],"\xEB"=>[0,"\x3EP"],"\xEC"=>[0,"\x3EQ"],"\xED"=>[0,"\x3ER"],"\xEE"=>[0,"\x3ES"],"\xEF"=>[0,"\x3ET"],"\xF0"=>[0,"\x3EU"],"\xF1"=>[0,"\x3EV"],"\xF2"=>[0,"\x3EW"],"\xF3"=>[0,"\x3EY"],"\xF4"=>[0,"\x3Ej"],"\xF5"=>[0,"\x3Ek"],"\xF6"=>[0,"\x3Eq"],"\xF7"=>[0,"\x3Ev"],"\xF8"=>[0,"\x3Ew"],"\xF9"=>[0,"\x3Ex"],"\xFA"=>[0,"\x3Ey"],"\xFB"=>[0,"\x3Ez"],"\xFC"=>[82,"\x3E"],"\xFD"=>[87,"\x3E"],"\xFE"=>[130,"\x3E"],"\xFF"=>[9,"\x3E"],],["\x00"=>[94,"\x7C0"],"\x01"=>[76,"\x7C0"],"\x02"=>[104,"\x7C0"],"\x03"=>[16,"\x7C0"],"\x04"=>[94,"\x7C1"],"\x05"=>[76,"\x7C1"],"\x06"=>[104,"\x7C1"],"\x07"=>[16,"\x7C1"],"\x08"=>[94,"\x7C2"],"\x09"=>[76,"\x7C2"],"\x0A"=>[104,"\x7C2"],"\x0B"=>[16,"\x7C2"],"\x0C"=>[94,"\x7Ca"],"\x0D"=>[76,"\x7Ca"],"\x0E"=>[104,"\x7Ca"],"\x0F"=>[16,"\x7Ca"],"\x10"=>[94,"\x7Cc"],"\x11"=>[76,"\x7Cc"],"\x12"=>[104,"\x7Cc"],"\x13"=>[16,"\x7Cc"],"\x14"=>[94,"\x7Ce"],"\x15"=>[76,"\x7Ce"],"\x16"=>[104,"\x7Ce"],"\x17"=>[16,"\x7Ce"],"\x18"=>[94,"\x7Ci"],"\x19"=>[76,"\x7Ci"],"\x1A"=>[104,"\x7Ci"],"\x1B"=>[16,"\x7Ci"],"\x1C"=>[94,"\x7Co"],"\x1D"=>[76,"\x7Co"],"\x1E"=>[104,"\x7Co"],"\x1F"=>[16,"\x7Co"],"\x20"=>[94,"\x7Cs"],"\x21"=>[76,"\x7Cs"],"\x22"=>[104,"\x7Cs"],"\x23"=>[16,"\x7Cs"],"\x24"=>[94,"\x7Ct"],"\x25"=>[76,"\x7Ct"],"\x26"=>[104,"\x7Ct"],"\x27"=>[16,"\x7Ct"],"\x28"=>[77,"\x7C\x20"],"\x29"=>[18,"\x7C\x20"],"\x2A"=>[77,"\x7C\x25"],"\x2B"=>[18,"\x7C\x25"],"\x2C"=>[77,"\x7C-"],"-"=>[18,"\x7C-"],"."=>[77,"\x7C."],"\x2F"=>[18,"\x7C."],[77,"\x7C\x2F"],[18,"\x7C\x2F"],[77,"\x7C3"],[18,"\x7C3"],[77,"\x7C4"],[18,"\x7C4"],[77,"\x7C5"],[18,"\x7C5"],[77,"\x7C6"],[18,"\x7C6"],"\x3A"=>[77,"\x7C7"],"\x3B"=>[18,"\x7C7"],"\x3C"=>[77,"\x7C8"],"\x3D"=>[18,"\x7C8"],"\x3E"=>[77,"\x7C9"],"\x3F"=>[18,"\x7C9"],"\x40"=>[77,"\x7C\x3D"],"A"=>[18,"\x7C\x3D"],"B"=>[77,"\x7CA"],"C"=>[18,"\x7CA"],"D"=>[77,"\x7C_"],"E"=>[18,"\x7C_"],"F"=>[77,"\x7Cb"],"G"=>[18,"\x7Cb"],"H"=>[77,"\x7Cd"],"I"=>[18,"\x7Cd"],"J"=>[77,"\x7Cf"],"K"=>[18,"\x7Cf"],"L"=>[77,"\x7Cg"],"M"=>[18,"\x7Cg"],"N"=>[77,"\x7Ch"],"O"=>[18,"\x7Ch"],"P"=>[77,"\x7Cl"],"Q"=>[18,"\x7Cl"],"R"=>[77,"\x7Cm"],"S"=>[18,"\x7Cm"],"T"=>[77,"\x7Cn"],"U"=>[18,"\x7Cn"],"V"=>[77,"\x7Cp"],"W"=>[18,"\x7Cp"],"X"=>[77,"\x7Cr"],"Y"=>[18,"\x7Cr"],"Z"=>[77,"\x7Cu"],"\x5B"=>[18,"\x7Cu"],"\x5C"=>[0,"\x7C\x3A"],"\x5D"=>[0,"\x7CB"],"\x5E"=>[0,"\x7CC"],"_"=>[0,"\x7CD"],"\x60"=>[0,"\x7CE"],"a"=>[0,"\x7CF"],"b"=>[0,"\x7CG"],"c"=>[0,"\x7CH"],"d"=>[0,"\x7CI"],"e"=>[0,"\x7CJ"],"f"=>[0,"\x7CK"],"g"=>[0,"\x7CL"],"h"=>[0,"\x7CM"],"i"=>[0,"\x7CN"],"j"=>[0,"\x7CO"],"k"=>[0,"\x7CP"],"l"=>[0,"\x7CQ"],"m"=>[0,"\x7CR"],"n"=>[0,"\x7CS"],"o"=>[0,"\x7CT"],"p"=>[0,"\x7CU"],"q"=>[0,"\x7CV"],"r"=>[0,"\x7CW"],"s"=>[0,"\x7CY"],"t"=>[0,"\x7Cj"],"u"=>[0,"\x7Ck"],"v"=>[0,"\x7Cq"],"w"=>[0,"\x7Cv"],"x"=>[0,"\x7Cw"],"y"=>[0,"\x7Cx"],"z"=>[0,"\x7Cy"],"\x7B"=>[0,"\x7Cz"],"\x7C"=>[82,"\x7C"],"\x7D"=>[87,"\x7C"],"~"=>[130,"\x7C"],"\x7F"=>[9,"\x7C"],"\x80"=>[77,"\x230"],"\x81"=>[18,"\x230"],"\x82"=>[77,"\x231"],"\x83"=>[18,"\x231"],"\x84"=>[77,"\x232"],"\x85"=>[18,"\x232"],"\x86"=>[77,"\x23a"],"\x87"=>[18,"\x23a"],"\x88"=>[77,"\x23c"],"\x89"=>[18,"\x23c"],"\x8A"=>[77,"\x23e"],"\x8B"=>[18,"\x23e"],"\x8C"=>[77,"\x23i"],"\x8D"=>[18,"\x23i"],"\x8E"=>[77,"\x23o"],"\x8F"=>[18,"\x23o"],"\x90"=>[77,"\x23s"],"\x91"=>[18,"\x23s"],"\x92"=>[77,"\x23t"],"\x93"=>[18,"\x23t"],"\x94"=>[0,"\x23\x20"],"\x95"=>[0,"\x23\x25"],"\x96"=>[0,"\x23-"],"\x97"=>[0,"\x23."],"\x98"=>[0,"\x23\x2F"],"\x99"=>[0,"\x233"],"\x9A"=>[0,"\x234"],"\x9B"=>[0,"\x235"],"\x9C"=>[0,"\x236"],"\x9D"=>[0,"\x237"],"\x9E"=>[0,"\x238"],"\x9F"=>[0,"\x239"],"\xA0"=>[0,"\x23\x3D"],"\xA1"=>[0,"\x23A"],"\xA2"=>[0,"\x23_"],"\xA3"=>[0,"\x23b"],"\xA4"=>[0,"\x23d"],"\xA5"=>[0,"\x23f"],"\xA6"=>[0,"\x23g"],"\xA7"=>[0,"\x23h"],"\xA8"=>[0,"\x23l"],"\xA9"=>[0,"\x23m"],"\xAA"=>[0,"\x23n"],"\xAB"=>[0,"\x23p"],"\xAC"=>[0,"\x23r"],"\xAD"=>[0,"\x23u"],"\xAE"=>[100,"\x23"],"\xAF"=>[110,"\x23"],"\xB0"=>[111,"\x23"],"\xB1"=>[115,"\x23"],"\xB2"=>[116,"\x23"],"\xB3"=>[118,"\x23"],"\xB4"=>[119,"\x23"],"\xB5"=>[122,"\x23"],"\xB6"=>[123,"\x23"],"\xB7"=>[125,"\x23"],"\xB8"=>[126,"\x23"],"\xB9"=>[129,"\x23"],"\xBA"=>[143,"\x23"],"\xBB"=>[148,"\x23"],"\xBC"=>[151,"\x23"],"\xBD"=>[153,"\x23"],"\xBE"=>[83,"\x23"],"\xBF"=>[10,"\x23"],"\xC0"=>[77,"\x3E0"],"\xC1"=>[18,"\x3E0"],"\xC2"=>[77,"\x3E1"],"\xC3"=>[18,"\x3E1"],"\xC4"=>[77,"\x3E2"],"\xC5"=>[18,"\x3E2"],"\xC6"=>[77,"\x3Ea"],"\xC7"=>[18,"\x3Ea"],"\xC8"=>[77,"\x3Ec"],"\xC9"=>[18,"\x3Ec"],"\xCA"=>[77,"\x3Ee"],"\xCB"=>[18,"\x3Ee"],"\xCC"=>[77,"\x3Ei"],"\xCD"=>[18,"\x3Ei"],"\xCE"=>[77,"\x3Eo"],"\xCF"=>[18,"\x3Eo"],"\xD0"=>[77,"\x3Es"],"\xD1"=>[18,"\x3Es"],"\xD2"=>[77,"\x3Et"],"\xD3"=>[18,"\x3Et"],"\xD4"=>[0,"\x3E\x20"],"\xD5"=>[0,"\x3E\x25"],"\xD6"=>[0,"\x3E-"],"\xD7"=>[0,"\x3E."],"\xD8"=>[0,"\x3E\x2F"],"\xD9"=>[0,"\x3E3"],"\xDA"=>[0,"\x3E4"],"\xDB"=>[0,"\x3E5"],"\xDC"=>[0,"\x3E6"],"\xDD"=>[0,"\x3E7"],"\xDE"=>[0,"\x3E8"],"\xDF"=>[0,"\x3E9"],"\xE0"=>[0,"\x3E\x3D"],"\xE1"=>[0,"\x3EA"],"\xE2"=>[0,"\x3E_"],"\xE3"=>[0,"\x3Eb"],"\xE4"=>[0,"\x3Ed"],"\xE5"=>[0,"\x3Ef"],"\xE6"=>[0,"\x3Eg"],"\xE7"=>[0,"\x3Eh"],"\xE8"=>[0,"\x3El"],"\xE9"=>[0,"\x3Em"],"\xEA"=>[0,"\x3En"],"\xEB"=>[0,"\x3Ep"],"\xEC"=>[0,"\x3Er"],"\xED"=>[0,"\x3Eu"],"\xEE"=>[100,"\x3E"],"\xEF"=>[110,"\x3E"],"\xF0"=>[111,"\x3E"],"\xF1"=>[115,"\x3E"],"\xF2"=>[116,"\x3E"],"\xF3"=>[118,"\x3E"],"\xF4"=>[119,"\x3E"],"\xF5"=>[122,"\x3E"],"\xF6"=>[123,"\x3E"],"\xF7"=>[125,"\x3E"],"\xF8"=>[126,"\x3E"],"\xF9"=>[129,"\x3E"],"\xFA"=>[143,"\x3E"],"\xFB"=>[148,"\x3E"],"\xFC"=>[151,"\x3E"],"\xFD"=>[153,"\x3E"],"\xFE"=>[83,"\x3E"],"\xFF"=>[10,"\x3E"],],["\x00"=>[94,"\x260"],"\x01"=>[76,"\x260"],"\x02"=>[104,"\x260"],"\x03"=>[16,"\x260"],"\x04"=>[94,"\x261"],"\x05"=>[76,"\x261"],"\x06"=>[104,"\x261"],"\x07"=>[16,"\x261"],"\x08"=>[94,"\x262"],"\x09"=>[76,"\x262"],"\x0A"=>[104,"\x262"],"\x0B"=>[16,"\x262"],"\x0C"=>[94,"\x26a"],"\x0D"=>[76,"\x26a"],"\x0E"=>[104,"\x26a"],"\x0F"=>[16,"\x26a"],"\x10"=>[94,"\x26c"],"\x11"=>[76,"\x26c"],"\x12"=>[104,"\x26c"],"\x13"=>[16,"\x26c"],"\x14"=>[94,"\x26e"],"\x15"=>[76,"\x26e"],"\x16"=>[104,"\x26e"],"\x17"=>[16,"\x26e"],"\x18"=>[94,"\x26i"],"\x19"=>[76,"\x26i"],"\x1A"=>[104,"\x26i"],"\x1B"=>[16,"\x26i"],"\x1C"=>[94,"\x26o"],"\x1D"=>[76,"\x26o"],"\x1E"=>[104,"\x26o"],"\x1F"=>[16,"\x26o"],"\x20"=>[94,"\x26s"],"\x21"=>[76,"\x26s"],"\x22"=>[104,"\x26s"],"\x23"=>[16,"\x26s"],"\x24"=>[94,"\x26t"],"\x25"=>[76,"\x26t"],"\x26"=>[104,"\x26t"],"\x27"=>[16,"\x26t"],"\x28"=>[77,"\x26\x20"],"\x29"=>[18,"\x26\x20"],"\x2A"=>[77,"\x26\x25"],"\x2B"=>[18,"\x26\x25"],"\x2C"=>[77,"\x26-"],"-"=>[18,"\x26-"],"."=>[77,"\x26."],"\x2F"=>[18,"\x26."],[77,"\x26\x2F"],[18,"\x26\x2F"],[77,"\x263"],[18,"\x263"],[77,"\x264"],[18,"\x264"],[77,"\x265"],[18,"\x265"],[77,"\x266"],[18,"\x266"],"\x3A"=>[77,"\x267"],"\x3B"=>[18,"\x267"],"\x3C"=>[77,"\x268"],"\x3D"=>[18,"\x268"],"\x3E"=>[77,"\x269"],"\x3F"=>[18,"\x269"],"\x40"=>[77,"\x26\x3D"],"A"=>[18,"\x26\x3D"],"B"=>[77,"\x26A"],"C"=>[18,"\x26A"],"D"=>[77,"\x26_"],"E"=>[18,"\x26_"],"F"=>[77,"\x26b"],"G"=>[18,"\x26b"],"H"=>[77,"\x26d"],"I"=>[18,"\x26d"],"J"=>[77,"\x26f"],"K"=>[18,"\x26f"],"L"=>[77,"\x26g"],"M"=>[18,"\x26g"],"N"=>[77,"\x26h"],"O"=>[18,"\x26h"],"P"=>[77,"\x26l"],"Q"=>[18,"\x26l"],"R"=>[77,"\x26m"],"S"=>[18,"\x26m"],"T"=>[77,"\x26n"],"U"=>[18,"\x26n"],"V"=>[77,"\x26p"],"W"=>[18,"\x26p"],"X"=>[77,"\x26r"],"Y"=>[18,"\x26r"],"Z"=>[77,"\x26u"],"\x5B"=>[18,"\x26u"],"\x5C"=>[0,"\x26\x3A"],"\x5D"=>[0,"\x26B"],"\x5E"=>[0,"\x26C"],"_"=>[0,"\x26D"],"\x60"=>[0,"\x26E"],"a"=>[0,"\x26F"],"b"=>[0,"\x26G"],"c"=>[0,"\x26H"],"d"=>[0,"\x26I"],"e"=>[0,"\x26J"],"f"=>[0,"\x26K"],"g"=>[0,"\x26L"],"h"=>[0,"\x26M"],"i"=>[0,"\x26N"],"j"=>[0,"\x26O"],"k"=>[0,"\x26P"],"l"=>[0,"\x26Q"],"m"=>[0,"\x26R"],"n"=>[0,"\x26S"],"o"=>[0,"\x26T"],"p"=>[0,"\x26U"],"q"=>[0,"\x26V"],"r"=>[0,"\x26W"],"s"=>[0,"\x26Y"],"t"=>[0,"\x26j"],"u"=>[0,"\x26k"],"v"=>[0,"\x26q"],"w"=>[0,"\x26v"],"x"=>[0,"\x26w"],"y"=>[0,"\x26x"],"z"=>[0,"\x26y"],"\x7B"=>[0,"\x26z"],"\x7C"=>[82,"\x26"],"\x7D"=>[87,"\x26"],"~"=>[130,"\x26"],"\x7F"=>[9,"\x26"],"\x80"=>[94,"\x2A0"],"\x81"=>[76,"\x2A0"],"\x82"=>[104,"\x2A0"],"\x83"=>[16,"\x2A0"],"\x84"=>[94,"\x2A1"],"\x85"=>[76,"\x2A1"],"\x86"=>[104,"\x2A1"],"\x87"=>[16,"\x2A1"],"\x88"=>[94,"\x2A2"],"\x89"=>[76,"\x2A2"],"\x8A"=>[104,"\x2A2"],"\x8B"=>[16,"\x2A2"],"\x8C"=>[94,"\x2Aa"],"\x8D"=>[76,"\x2Aa"],"\x8E"=>[104,"\x2Aa"],"\x8F"=>[16,"\x2Aa"],"\x90"=>[94,"\x2Ac"],"\x91"=>[76,"\x2Ac"],"\x92"=>[104,"\x2Ac"],"\x93"=>[16,"\x2Ac"],"\x94"=>[94,"\x2Ae"],"\x95"=>[76,"\x2Ae"],"\x96"=>[104,"\x2Ae"],"\x97"=>[16,"\x2Ae"],"\x98"=>[94,"\x2Ai"],"\x99"=>[76,"\x2Ai"],"\x9A"=>[104,"\x2Ai"],"\x9B"=>[16,"\x2Ai"],"\x9C"=>[94,"\x2Ao"],"\x9D"=>[76,"\x2Ao"],"\x9E"=>[104,"\x2Ao"],"\x9F"=>[16,"\x2Ao"],"\xA0"=>[94,"\x2As"],"\xA1"=>[76,"\x2As"],"\xA2"=>[104,"\x2As"],"\xA3"=>[16,"\x2As"],"\xA4"=>[94,"\x2At"],"\xA5"=>[76,"\x2At"],"\xA6"=>[104,"\x2At"],"\xA7"=>[16,"\x2At"],"\xA8"=>[77,"\x2A\x20"],"\xA9"=>[18,"\x2A\x20"],"\xAA"=>[77,"\x2A\x25"],"\xAB"=>[18,"\x2A\x25"],"\xAC"=>[77,"\x2A-"],"\xAD"=>[18,"\x2A-"],"\xAE"=>[77,"\x2A."],"\xAF"=>[18,"\x2A."],"\xB0"=>[77,"\x2A\x2F"],"\xB1"=>[18,"\x2A\x2F"],"\xB2"=>[77,"\x2A3"],"\xB3"=>[18,"\x2A3"],"\xB4"=>[77,"\x2A4"],"\xB5"=>[18,"\x2A4"],"\xB6"=>[77,"\x2A5"],"\xB7"=>[18,"\x2A5"],"\xB8"=>[77,"\x2A6"],"\xB9"=>[18,"\x2A6"],"\xBA"=>[77,"\x2A7"],"\xBB"=>[18,"\x2A7"],"\xBC"=>[77,"\x2A8"],"\xBD"=>[18,"\x2A8"],"\xBE"=>[77,"\x2A9"],"\xBF"=>[18,"\x2A9"],"\xC0"=>[77,"\x2A\x3D"],"\xC1"=>[18,"\x2A\x3D"],"\xC2"=>[77,"\x2AA"],"\xC3"=>[18,"\x2AA"],"\xC4"=>[77,"\x2A_"],"\xC5"=>[18,"\x2A_"],"\xC6"=>[77,"\x2Ab"],"\xC7"=>[18,"\x2Ab"],"\xC8"=>[77,"\x2Ad"],"\xC9"=>[18,"\x2Ad"],"\xCA"=>[77,"\x2Af"],"\xCB"=>[18,"\x2Af"],"\xCC"=>[77,"\x2Ag"],"\xCD"=>[18,"\x2Ag"],"\xCE"=>[77,"\x2Ah"],"\xCF"=>[18,"\x2Ah"],"\xD0"=>[77,"\x2Al"],"\xD1"=>[18,"\x2Al"],"\xD2"=>[77,"\x2Am"],"\xD3"=>[18,"\x2Am"],"\xD4"=>[77,"\x2An"],"\xD5"=>[18,"\x2An"],"\xD6"=>[77,"\x2Ap"],"\xD7"=>[18,"\x2Ap"],"\xD8"=>[77,"\x2Ar"],"\xD9"=>[18,"\x2Ar"],"\xDA"=>[77,"\x2Au"],"\xDB"=>[18,"\x2Au"],"\xDC"=>[0,"\x2A\x3A"],"\xDD"=>[0,"\x2AB"],"\xDE"=>[0,"\x2AC"],"\xDF"=>[0,"\x2AD"],"\xE0"=>[0,"\x2AE"],"\xE1"=>[0,"\x2AF"],"\xE2"=>[0,"\x2AG"],"\xE3"=>[0,"\x2AH"],"\xE4"=>[0,"\x2AI"],"\xE5"=>[0,"\x2AJ"],"\xE6"=>[0,"\x2AK"],"\xE7"=>[0,"\x2AL"],"\xE8"=>[0,"\x2AM"],"\xE9"=>[0,"\x2AN"],"\xEA"=>[0,"\x2AO"],"\xEB"=>[0,"\x2AP"],"\xEC"=>[0,"\x2AQ"],"\xED"=>[0,"\x2AR"],"\xEE"=>[0,"\x2AS"],"\xEF"=>[0,"\x2AT"],"\xF0"=>[0,"\x2AU"],"\xF1"=>[0,"\x2AV"],"\xF2"=>[0,"\x2AW"],"\xF3"=>[0,"\x2AY"],"\xF4"=>[0,"\x2Aj"],"\xF5"=>[0,"\x2Ak"],"\xF6"=>[0,"\x2Aq"],"\xF7"=>[0,"\x2Av"],"\xF8"=>[0,"\x2Aw"],"\xF9"=>[0,"\x2Ax"],"\xFA"=>[0,"\x2Ay"],"\xFB"=>[0,"\x2Az"],"\xFC"=>[82,"\x2A"],"\xFD"=>[87,"\x2A"],"\xFE"=>[130,"\x2A"],"\xFF"=>[9,"\x2A"],],["\x00"=>[77,"\x260"],"\x01"=>[18,"\x260"],"\x02"=>[77,"\x261"],"\x03"=>[18,"\x261"],"\x04"=>[77,"\x262"],"\x05"=>[18,"\x262"],"\x06"=>[77,"\x26a"],"\x07"=>[18,"\x26a"],"\x08"=>[77,"\x26c"],"\x09"=>[18,"\x26c"],"\x0A"=>[77,"\x26e"],"\x0B"=>[18,"\x26e"],"\x0C"=>[77,"\x26i"],"\x0D"=>[18,"\x26i"],"\x0E"=>[77,"\x26o"],"\x0F"=>[18,"\x26o"],"\x10"=>[77,"\x26s"],"\x11"=>[18,"\x26s"],"\x12"=>[77,"\x26t"],"\x13"=>[18,"\x26t"],"\x14"=>[0,"\x26\x20"],"\x15"=>[0,"\x26\x25"],"\x16"=>[0,"\x26-"],"\x17"=>[0,"\x26."],"\x18"=>[0,"\x26\x2F"],"\x19"=>[0,"\x263"],"\x1A"=>[0,"\x264"],"\x1B"=>[0,"\x265"],"\x1C"=>[0,"\x266"],"\x1D"=>[0,"\x267"],"\x1E"=>[0,"\x268"],"\x1F"=>[0,"\x269"],"\x20"=>[0,"\x26\x3D"],"\x21"=>[0,"\x26A"],"\x22"=>[0,"\x26_"],"\x23"=>[0,"\x26b"],"\x24"=>[0,"\x26d"],"\x25"=>[0,"\x26f"],"\x26"=>[0,"\x26g"],"\x27"=>[0,"\x26h"],"\x28"=>[0,"\x26l"],"\x29"=>[0,"\x26m"],"\x2A"=>[0,"\x26n"],"\x2B"=>[0,"\x26p"],"\x2C"=>[0,"\x26r"],"-"=>[0,"\x26u"],"."=>[100,"\x26"],"\x2F"=>[110,"\x26"],[111,"\x26"],[115,"\x26"],[116,"\x26"],[118,"\x26"],[119,"\x26"],[122,"\x26"],[123,"\x26"],[125,"\x26"],[126,"\x26"],[129,"\x26"],"\x3A"=>[143,"\x26"],"\x3B"=>[148,"\x26"],"\x3C"=>[151,"\x26"],"\x3D"=>[153,"\x26"],"\x3E"=>[83,"\x26"],"\x3F"=>[10,"\x26"],"\x40"=>[77,"\x2A0"],"A"=>[18,"\x2A0"],"B"=>[77,"\x2A1"],"C"=>[18,"\x2A1"],"D"=>[77,"\x2A2"],"E"=>[18,"\x2A2"],"F"=>[77,"\x2Aa"],"G"=>[18,"\x2Aa"],"H"=>[77,"\x2Ac"],"I"=>[18,"\x2Ac"],"J"=>[77,"\x2Ae"],"K"=>[18,"\x2Ae"],"L"=>[77,"\x2Ai"],"M"=>[18,"\x2Ai"],"N"=>[77,"\x2Ao"],"O"=>[18,"\x2Ao"],"P"=>[77,"\x2As"],"Q"=>[18,"\x2As"],"R"=>[77,"\x2At"],"S"=>[18,"\x2At"],"T"=>[0,"\x2A\x20"],"U"=>[0,"\x2A\x25"],"V"=>[0,"\x2A-"],"W"=>[0,"\x2A."],"X"=>[0,"\x2A\x2F"],"Y"=>[0,"\x2A3"],"Z"=>[0,"\x2A4"],"\x5B"=>[0,"\x2A5"],"\x5C"=>[0,"\x2A6"],"\x5D"=>[0,"\x2A7"],"\x5E"=>[0,"\x2A8"],"_"=>[0,"\x2A9"],"\x60"=>[0,"\x2A\x3D"],"a"=>[0,"\x2AA"],"b"=>[0,"\x2A_"],"c"=>[0,"\x2Ab"],"d"=>[0,"\x2Ad"],"e"=>[0,"\x2Af"],"f"=>[0,"\x2Ag"],"g"=>[0,"\x2Ah"],"h"=>[0,"\x2Al"],"i"=>[0,"\x2Am"],"j"=>[0,"\x2An"],"k"=>[0,"\x2Ap"],"l"=>[0,"\x2Ar"],"m"=>[0,"\x2Au"],"n"=>[100,"\x2A"],"o"=>[110,"\x2A"],"p"=>[111,"\x2A"],"q"=>[115,"\x2A"],"r"=>[116,"\x2A"],"s"=>[118,"\x2A"],"t"=>[119,"\x2A"],"u"=>[122,"\x2A"],"v"=>[123,"\x2A"],"w"=>[125,"\x2A"],"x"=>[126,"\x2A"],"y"=>[129,"\x2A"],"z"=>[143,"\x2A"],"\x7B"=>[148,"\x2A"],"\x7C"=>[151,"\x2A"],"\x7D"=>[153,"\x2A"],"~"=>[83,"\x2A"],"\x7F"=>[10,"\x2A"],"\x80"=>[77,"\x2C0"],"\x81"=>[18,"\x2C0"],"\x82"=>[77,"\x2C1"],"\x83"=>[18,"\x2C1"],"\x84"=>[77,"\x2C2"],"\x85"=>[18,"\x2C2"],"\x86"=>[77,"\x2Ca"],"\x87"=>[18,"\x2Ca"],"\x88"=>[77,"\x2Cc"],"\x89"=>[18,"\x2Cc"],"\x8A"=>[77,"\x2Ce"],"\x8B"=>[18,"\x2Ce"],"\x8C"=>[77,"\x2Ci"],"\x8D"=>[18,"\x2Ci"],"\x8E"=>[77,"\x2Co"],"\x8F"=>[18,"\x2Co"],"\x90"=>[77,"\x2Cs"],"\x91"=>[18,"\x2Cs"],"\x92"=>[77,"\x2Ct"],"\x93"=>[18,"\x2Ct"],"\x94"=>[0,"\x2C\x20"],"\x95"=>[0,"\x2C\x25"],"\x96"=>[0,"\x2C-"],"\x97"=>[0,"\x2C."],"\x98"=>[0,"\x2C\x2F"],"\x99"=>[0,"\x2C3"],"\x9A"=>[0,"\x2C4"],"\x9B"=>[0,"\x2C5"],"\x9C"=>[0,"\x2C6"],"\x9D"=>[0,"\x2C7"],"\x9E"=>[0,"\x2C8"],"\x9F"=>[0,"\x2C9"],"\xA0"=>[0,"\x2C\x3D"],"\xA1"=>[0,"\x2CA"],"\xA2"=>[0,"\x2C_"],"\xA3"=>[0,"\x2Cb"],"\xA4"=>[0,"\x2Cd"],"\xA5"=>[0,"\x2Cf"],"\xA6"=>[0,"\x2Cg"],"\xA7"=>[0,"\x2Ch"],"\xA8"=>[0,"\x2Cl"],"\xA9"=>[0,"\x2Cm"],"\xAA"=>[0,"\x2Cn"],"\xAB"=>[0,"\x2Cp"],"\xAC"=>[0,"\x2Cr"],"\xAD"=>[0,"\x2Cu"],"\xAE"=>[100,"\x2C"],"\xAF"=>[110,"\x2C"],"\xB0"=>[111,"\x2C"],"\xB1"=>[115,"\x2C"],"\xB2"=>[116,"\x2C"],"\xB3"=>[118,"\x2C"],"\xB4"=>[119,"\x2C"],"\xB5"=>[122,"\x2C"],"\xB6"=>[123,"\x2C"],"\xB7"=>[125,"\x2C"],"\xB8"=>[126,"\x2C"],"\xB9"=>[129,"\x2C"],"\xBA"=>[143,"\x2C"],"\xBB"=>[148,"\x2C"],"\xBC"=>[151,"\x2C"],"\xBD"=>[153,"\x2C"],"\xBE"=>[83,"\x2C"],"\xBF"=>[10,"\x2C"],"\xC0"=>[77,"\x3B0"],"\xC1"=>[18,"\x3B0"],"\xC2"=>[77,"\x3B1"],"\xC3"=>[18,"\x3B1"],"\xC4"=>[77,"\x3B2"],"\xC5"=>[18,"\x3B2"],"\xC6"=>[77,"\x3Ba"],"\xC7"=>[18,"\x3Ba"],"\xC8"=>[77,"\x3Bc"],"\xC9"=>[18,"\x3Bc"],"\xCA"=>[77,"\x3Be"],"\xCB"=>[18,"\x3Be"],"\xCC"=>[77,"\x3Bi"],"\xCD"=>[18,"\x3Bi"],"\xCE"=>[77,"\x3Bo"],"\xCF"=>[18,"\x3Bo"],"\xD0"=>[77,"\x3Bs"],"\xD1"=>[18,"\x3Bs"],"\xD2"=>[77,"\x3Bt"],"\xD3"=>[18,"\x3Bt"],"\xD4"=>[0,"\x3B\x20"],"\xD5"=>[0,"\x3B\x25"],"\xD6"=>[0,"\x3B-"],"\xD7"=>[0,"\x3B."],"\xD8"=>[0,"\x3B\x2F"],"\xD9"=>[0,"\x3B3"],"\xDA"=>[0,"\x3B4"],"\xDB"=>[0,"\x3B5"],"\xDC"=>[0,"\x3B6"],"\xDD"=>[0,"\x3B7"],"\xDE"=>[0,"\x3B8"],"\xDF"=>[0,"\x3B9"],"\xE0"=>[0,"\x3B\x3D"],"\xE1"=>[0,"\x3BA"],"\xE2"=>[0,"\x3B_"],"\xE3"=>[0,"\x3Bb"],"\xE4"=>[0,"\x3Bd"],"\xE5"=>[0,"\x3Bf"],"\xE6"=>[0,"\x3Bg"],"\xE7"=>[0,"\x3Bh"],"\xE8"=>[0,"\x3Bl"],"\xE9"=>[0,"\x3Bm"],"\xEA"=>[0,"\x3Bn"],"\xEB"=>[0,"\x3Bp"],"\xEC"=>[0,"\x3Br"],"\xED"=>[0,"\x3Bu"],"\xEE"=>[100,"\x3B"],"\xEF"=>[110,"\x3B"],"\xF0"=>[111,"\x3B"],"\xF1"=>[115,"\x3B"],"\xF2"=>[116,"\x3B"],"\xF3"=>[118,"\x3B"],"\xF4"=>[119,"\x3B"],"\xF5"=>[122,"\x3B"],"\xF6"=>[123,"\x3B"],"\xF7"=>[125,"\x3B"],"\xF8"=>[126,"\x3B"],"\xF9"=>[129,"\x3B"],"\xFA"=>[143,"\x3B"],"\xFB"=>[148,"\x3B"],"\xFC"=>[151,"\x3B"],"\xFD"=>[153,"\x3B"],"\xFE"=>[83,"\x3B"],"\xFF"=>[10,"\x3B"],],["\x00"=>[94,"\x270"],"\x01"=>[76,"\x270"],"\x02"=>[104,"\x270"],"\x03"=>[16,"\x270"],"\x04"=>[94,"\x271"],"\x05"=>[76,"\x271"],"\x06"=>[104,"\x271"],"\x07"=>[16,"\x271"],"\x08"=>[94,"\x272"],"\x09"=>[76,"\x272"],"\x0A"=>[104,"\x272"],"\x0B"=>[16,"\x272"],"\x0C"=>[94,"\x27a"],"\x0D"=>[76,"\x27a"],"\x0E"=>[104,"\x27a"],"\x0F"=>[16,"\x27a"],"\x10"=>[94,"\x27c"],"\x11"=>[76,"\x27c"],"\x12"=>[104,"\x27c"],"\x13"=>[16,"\x27c"],"\x14"=>[94,"\x27e"],"\x15"=>[76,"\x27e"],"\x16"=>[104,"\x27e"],"\x17"=>[16,"\x27e"],"\x18"=>[94,"\x27i"],"\x19"=>[76,"\x27i"],"\x1A"=>[104,"\x27i"],"\x1B"=>[16,"\x27i"],"\x1C"=>[94,"\x27o"],"\x1D"=>[76,"\x27o"],"\x1E"=>[104,"\x27o"],"\x1F"=>[16,"\x27o"],"\x20"=>[94,"\x27s"],"\x21"=>[76,"\x27s"],"\x22"=>[104,"\x27s"],"\x23"=>[16,"\x27s"],"\x24"=>[94,"\x27t"],"\x25"=>[76,"\x27t"],"\x26"=>[104,"\x27t"],"\x27"=>[16,"\x27t"],"\x28"=>[77,"\x27\x20"],"\x29"=>[18,"\x27\x20"],"\x2A"=>[77,"\x27\x25"],"\x2B"=>[18,"\x27\x25"],"\x2C"=>[77,"\x27-"],"-"=>[18,"\x27-"],"."=>[77,"\x27."],"\x2F"=>[18,"\x27."],[77,"\x27\x2F"],[18,"\x27\x2F"],[77,"\x273"],[18,"\x273"],[77,"\x274"],[18,"\x274"],[77,"\x275"],[18,"\x275"],[77,"\x276"],[18,"\x276"],"\x3A"=>[77,"\x277"],"\x3B"=>[18,"\x277"],"\x3C"=>[77,"\x278"],"\x3D"=>[18,"\x278"],"\x3E"=>[77,"\x279"],"\x3F"=>[18,"\x279"],"\x40"=>[77,"\x27\x3D"],"A"=>[18,"\x27\x3D"],"B"=>[77,"\x27A"],"C"=>[18,"\x27A"],"D"=>[77,"\x27_"],"E"=>[18,"\x27_"],"F"=>[77,"\x27b"],"G"=>[18,"\x27b"],"H"=>[77,"\x27d"],"I"=>[18,"\x27d"],"J"=>[77,"\x27f"],"K"=>[18,"\x27f"],"L"=>[77,"\x27g"],"M"=>[18,"\x27g"],"N"=>[77,"\x27h"],"O"=>[18,"\x27h"],"P"=>[77,"\x27l"],"Q"=>[18,"\x27l"],"R"=>[77,"\x27m"],"S"=>[18,"\x27m"],"T"=>[77,"\x27n"],"U"=>[18,"\x27n"],"V"=>[77,"\x27p"],"W"=>[18,"\x27p"],"X"=>[77,"\x27r"],"Y"=>[18,"\x27r"],"Z"=>[77,"\x27u"],"\x5B"=>[18,"\x27u"],"\x5C"=>[0,"\x27\x3A"],"\x5D"=>[0,"\x27B"],"\x5E"=>[0,"\x27C"],"_"=>[0,"\x27D"],"\x60"=>[0,"\x27E"],"a"=>[0,"\x27F"],"b"=>[0,"\x27G"],"c"=>[0,"\x27H"],"d"=>[0,"\x27I"],"e"=>[0,"\x27J"],"f"=>[0,"\x27K"],"g"=>[0,"\x27L"],"h"=>[0,"\x27M"],"i"=>[0,"\x27N"],"j"=>[0,"\x27O"],"k"=>[0,"\x27P"],"l"=>[0,"\x27Q"],"m"=>[0,"\x27R"],"n"=>[0,"\x27S"],"o"=>[0,"\x27T"],"p"=>[0,"\x27U"],"q"=>[0,"\x27V"],"r"=>[0,"\x27W"],"s"=>[0,"\x27Y"],"t"=>[0,"\x27j"],"u"=>[0,"\x27k"],"v"=>[0,"\x27q"],"w"=>[0,"\x27v"],"x"=>[0,"\x27w"],"y"=>[0,"\x27x"],"z"=>[0,"\x27y"],"\x7B"=>[0,"\x27z"],"\x7C"=>[82,"\x27"],"\x7D"=>[87,"\x27"],"~"=>[130,"\x27"],"\x7F"=>[9,"\x27"],"\x80"=>[94,"\x2B0"],"\x81"=>[76,"\x2B0"],"\x82"=>[104,"\x2B0"],"\x83"=>[16,"\x2B0"],"\x84"=>[94,"\x2B1"],"\x85"=>[76,"\x2B1"],"\x86"=>[104,"\x2B1"],"\x87"=>[16,"\x2B1"],"\x88"=>[94,"\x2B2"],"\x89"=>[76,"\x2B2"],"\x8A"=>[104,"\x2B2"],"\x8B"=>[16,"\x2B2"],"\x8C"=>[94,"\x2Ba"],"\x8D"=>[76,"\x2Ba"],"\x8E"=>[104,"\x2Ba"],"\x8F"=>[16,"\x2Ba"],"\x90"=>[94,"\x2Bc"],"\x91"=>[76,"\x2Bc"],"\x92"=>[104,"\x2Bc"],"\x93"=>[16,"\x2Bc"],"\x94"=>[94,"\x2Be"],"\x95"=>[76,"\x2Be"],"\x96"=>[104,"\x2Be"],"\x97"=>[16,"\x2Be"],"\x98"=>[94,"\x2Bi"],"\x99"=>[76,"\x2Bi"],"\x9A"=>[104,"\x2Bi"],"\x9B"=>[16,"\x2Bi"],"\x9C"=>[94,"\x2Bo"],"\x9D"=>[76,"\x2Bo"],"\x9E"=>[104,"\x2Bo"],"\x9F"=>[16,"\x2Bo"],"\xA0"=>[94,"\x2Bs"],"\xA1"=>[76,"\x2Bs"],"\xA2"=>[104,"\x2Bs"],"\xA3"=>[16,"\x2Bs"],"\xA4"=>[94,"\x2Bt"],"\xA5"=>[76,"\x2Bt"],"\xA6"=>[104,"\x2Bt"],"\xA7"=>[16,"\x2Bt"],"\xA8"=>[77,"\x2B\x20"],"\xA9"=>[18,"\x2B\x20"],"\xAA"=>[77,"\x2B\x25"],"\xAB"=>[18,"\x2B\x25"],"\xAC"=>[77,"\x2B-"],"\xAD"=>[18,"\x2B-"],"\xAE"=>[77,"\x2B."],"\xAF"=>[18,"\x2B."],"\xB0"=>[77,"\x2B\x2F"],"\xB1"=>[18,"\x2B\x2F"],"\xB2"=>[77,"\x2B3"],"\xB3"=>[18,"\x2B3"],"\xB4"=>[77,"\x2B4"],"\xB5"=>[18,"\x2B4"],"\xB6"=>[77,"\x2B5"],"\xB7"=>[18,"\x2B5"],"\xB8"=>[77,"\x2B6"],"\xB9"=>[18,"\x2B6"],"\xBA"=>[77,"\x2B7"],"\xBB"=>[18,"\x2B7"],"\xBC"=>[77,"\x2B8"],"\xBD"=>[18,"\x2B8"],"\xBE"=>[77,"\x2B9"],"\xBF"=>[18,"\x2B9"],"\xC0"=>[77,"\x2B\x3D"],"\xC1"=>[18,"\x2B\x3D"],"\xC2"=>[77,"\x2BA"],"\xC3"=>[18,"\x2BA"],"\xC4"=>[77,"\x2B_"],"\xC5"=>[18,"\x2B_"],"\xC6"=>[77,"\x2Bb"],"\xC7"=>[18,"\x2Bb"],"\xC8"=>[77,"\x2Bd"],"\xC9"=>[18,"\x2Bd"],"\xCA"=>[77,"\x2Bf"],"\xCB"=>[18,"\x2Bf"],"\xCC"=>[77,"\x2Bg"],"\xCD"=>[18,"\x2Bg"],"\xCE"=>[77,"\x2Bh"],"\xCF"=>[18,"\x2Bh"],"\xD0"=>[77,"\x2Bl"],"\xD1"=>[18,"\x2Bl"],"\xD2"=>[77,"\x2Bm"],"\xD3"=>[18,"\x2Bm"],"\xD4"=>[77,"\x2Bn"],"\xD5"=>[18,"\x2Bn"],"\xD6"=>[77,"\x2Bp"],"\xD7"=>[18,"\x2Bp"],"\xD8"=>[77,"\x2Br"],"\xD9"=>[18,"\x2Br"],"\xDA"=>[77,"\x2Bu"],"\xDB"=>[18,"\x2Bu"],"\xDC"=>[0,"\x2B\x3A"],"\xDD"=>[0,"\x2BB"],"\xDE"=>[0,"\x2BC"],"\xDF"=>[0,"\x2BD"],"\xE0"=>[0,"\x2BE"],"\xE1"=>[0,"\x2BF"],"\xE2"=>[0,"\x2BG"],"\xE3"=>[0,"\x2BH"],"\xE4"=>[0,"\x2BI"],"\xE5"=>[0,"\x2BJ"],"\xE6"=>[0,"\x2BK"],"\xE7"=>[0,"\x2BL"],"\xE8"=>[0,"\x2BM"],"\xE9"=>[0,"\x2BN"],"\xEA"=>[0,"\x2BO"],"\xEB"=>[0,"\x2BP"],"\xEC"=>[0,"\x2BQ"],"\xED"=>[0,"\x2BR"],"\xEE"=>[0,"\x2BS"],"\xEF"=>[0,"\x2BT"],"\xF0"=>[0,"\x2BU"],"\xF1"=>[0,"\x2BV"],"\xF2"=>[0,"\x2BW"],"\xF3"=>[0,"\x2BY"],"\xF4"=>[0,"\x2Bj"],"\xF5"=>[0,"\x2Bk"],"\xF6"=>[0,"\x2Bq"],"\xF7"=>[0,"\x2Bv"],"\xF8"=>[0,"\x2Bw"],"\xF9"=>[0,"\x2Bx"],"\xFA"=>[0,"\x2By"],"\xFB"=>[0,"\x2Bz"],"\xFC"=>[82,"\x2B"],"\xFD"=>[87,"\x2B"],"\xFE"=>[130,"\x2B"],"\xFF"=>[9,"\x2B"],],["\x00"=>[94,"\x3F0"],"\x01"=>[76,"\x3F0"],"\x02"=>[104,"\x3F0"],"\x03"=>[16,"\x3F0"],"\x04"=>[94,"\x3F1"],"\x05"=>[76,"\x3F1"],"\x06"=>[104,"\x3F1"],"\x07"=>[16,"\x3F1"],"\x08"=>[94,"\x3F2"],"\x09"=>[76,"\x3F2"],"\x0A"=>[104,"\x3F2"],"\x0B"=>[16,"\x3F2"],"\x0C"=>[94,"\x3Fa"],"\x0D"=>[76,"\x3Fa"],"\x0E"=>[104,"\x3Fa"],"\x0F"=>[16,"\x3Fa"],"\x10"=>[94,"\x3Fc"],"\x11"=>[76,"\x3Fc"],"\x12"=>[104,"\x3Fc"],"\x13"=>[16,"\x3Fc"],"\x14"=>[94,"\x3Fe"],"\x15"=>[76,"\x3Fe"],"\x16"=>[104,"\x3Fe"],"\x17"=>[16,"\x3Fe"],"\x18"=>[94,"\x3Fi"],"\x19"=>[76,"\x3Fi"],"\x1A"=>[104,"\x3Fi"],"\x1B"=>[16,"\x3Fi"],"\x1C"=>[94,"\x3Fo"],"\x1D"=>[76,"\x3Fo"],"\x1E"=>[104,"\x3Fo"],"\x1F"=>[16,"\x3Fo"],"\x20"=>[94,"\x3Fs"],"\x21"=>[76,"\x3Fs"],"\x22"=>[104,"\x3Fs"],"\x23"=>[16,"\x3Fs"],"\x24"=>[94,"\x3Ft"],"\x25"=>[76,"\x3Ft"],"\x26"=>[104,"\x3Ft"],"\x27"=>[16,"\x3Ft"],"\x28"=>[77,"\x3F\x20"],"\x29"=>[18,"\x3F\x20"],"\x2A"=>[77,"\x3F\x25"],"\x2B"=>[18,"\x3F\x25"],"\x2C"=>[77,"\x3F-"],"-"=>[18,"\x3F-"],"."=>[77,"\x3F."],"\x2F"=>[18,"\x3F."],[77,"\x3F\x2F"],[18,"\x3F\x2F"],[77,"\x3F3"],[18,"\x3F3"],[77,"\x3F4"],[18,"\x3F4"],[77,"\x3F5"],[18,"\x3F5"],[77,"\x3F6"],[18,"\x3F6"],"\x3A"=>[77,"\x3F7"],"\x3B"=>[18,"\x3F7"],"\x3C"=>[77,"\x3F8"],"\x3D"=>[18,"\x3F8"],"\x3E"=>[77,"\x3F9"],"\x3F"=>[18,"\x3F9"],"\x40"=>[77,"\x3F\x3D"],"A"=>[18,"\x3F\x3D"],"B"=>[77,"\x3FA"],"C"=>[18,"\x3FA"],"D"=>[77,"\x3F_"],"E"=>[18,"\x3F_"],"F"=>[77,"\x3Fb"],"G"=>[18,"\x3Fb"],"H"=>[77,"\x3Fd"],"I"=>[18,"\x3Fd"],"J"=>[77,"\x3Ff"],"K"=>[18,"\x3Ff"],"L"=>[77,"\x3Fg"],"M"=>[18,"\x3Fg"],"N"=>[77,"\x3Fh"],"O"=>[18,"\x3Fh"],"P"=>[77,"\x3Fl"],"Q"=>[18,"\x3Fl"],"R"=>[77,"\x3Fm"],"S"=>[18,"\x3Fm"],"T"=>[77,"\x3Fn"],"U"=>[18,"\x3Fn"],"V"=>[77,"\x3Fp"],"W"=>[18,"\x3Fp"],"X"=>[77,"\x3Fr"],"Y"=>[18,"\x3Fr"],"Z"=>[77,"\x3Fu"],"\x5B"=>[18,"\x3Fu"],"\x5C"=>[0,"\x3F\x3A"],"\x5D"=>[0,"\x3FB"],"\x5E"=>[0,"\x3FC"],"_"=>[0,"\x3FD"],"\x60"=>[0,"\x3FE"],"a"=>[0,"\x3FF"],"b"=>[0,"\x3FG"],"c"=>[0,"\x3FH"],"d"=>[0,"\x3FI"],"e"=>[0,"\x3FJ"],"f"=>[0,"\x3FK"],"g"=>[0,"\x3FL"],"h"=>[0,"\x3FM"],"i"=>[0,"\x3FN"],"j"=>[0,"\x3FO"],"k"=>[0,"\x3FP"],"l"=>[0,"\x3FQ"],"m"=>[0,"\x3FR"],"n"=>[0,"\x3FS"],"o"=>[0,"\x3FT"],"p"=>[0,"\x3FU"],"q"=>[0,"\x3FV"],"r"=>[0,"\x3FW"],"s"=>[0,"\x3FY"],"t"=>[0,"\x3Fj"],"u"=>[0,"\x3Fk"],"v"=>[0,"\x3Fq"],"w"=>[0,"\x3Fv"],"x"=>[0,"\x3Fw"],"y"=>[0,"\x3Fx"],"z"=>[0,"\x3Fy"],"\x7B"=>[0,"\x3Fz"],"\x7C"=>[82,"\x3F"],"\x7D"=>[87,"\x3F"],"~"=>[130,"\x3F"],"\x7F"=>[9,"\x3F"],"\x80"=>[77,"\x270"],"\x81"=>[18,"\x270"],"\x82"=>[77,"\x271"],"\x83"=>[18,"\x271"],"\x84"=>[77,"\x272"],"\x85"=>[18,"\x272"],"\x86"=>[77,"\x27a"],"\x87"=>[18,"\x27a"],"\x88"=>[77,"\x27c"],"\x89"=>[18,"\x27c"],"\x8A"=>[77,"\x27e"],"\x8B"=>[18,"\x27e"],"\x8C"=>[77,"\x27i"],"\x8D"=>[18,"\x27i"],"\x8E"=>[77,"\x27o"],"\x8F"=>[18,"\x27o"],"\x90"=>[77,"\x27s"],"\x91"=>[18,"\x27s"],"\x92"=>[77,"\x27t"],"\x93"=>[18,"\x27t"],"\x94"=>[0,"\x27\x20"],"\x95"=>[0,"\x27\x25"],"\x96"=>[0,"\x27-"],"\x97"=>[0,"\x27."],"\x98"=>[0,"\x27\x2F"],"\x99"=>[0,"\x273"],"\x9A"=>[0,"\x274"],"\x9B"=>[0,"\x275"],"\x9C"=>[0,"\x276"],"\x9D"=>[0,"\x277"],"\x9E"=>[0,"\x278"],"\x9F"=>[0,"\x279"],"\xA0"=>[0,"\x27\x3D"],"\xA1"=>[0,"\x27A"],"\xA2"=>[0,"\x27_"],"\xA3"=>[0,"\x27b"],"\xA4"=>[0,"\x27d"],"\xA5"=>[0,"\x27f"],"\xA6"=>[0,"\x27g"],"\xA7"=>[0,"\x27h"],"\xA8"=>[0,"\x27l"],"\xA9"=>[0,"\x27m"],"\xAA"=>[0,"\x27n"],"\xAB"=>[0,"\x27p"],"\xAC"=>[0,"\x27r"],"\xAD"=>[0,"\x27u"],"\xAE"=>[100,"\x27"],"\xAF"=>[110,"\x27"],"\xB0"=>[111,"\x27"],"\xB1"=>[115,"\x27"],"\xB2"=>[116,"\x27"],"\xB3"=>[118,"\x27"],"\xB4"=>[119,"\x27"],"\xB5"=>[122,"\x27"],"\xB6"=>[123,"\x27"],"\xB7"=>[125,"\x27"],"\xB8"=>[126,"\x27"],"\xB9"=>[129,"\x27"],"\xBA"=>[143,"\x27"],"\xBB"=>[148,"\x27"],"\xBC"=>[151,"\x27"],"\xBD"=>[153,"\x27"],"\xBE"=>[83,"\x27"],"\xBF"=>[10,"\x27"],"\xC0"=>[77,"\x2B0"],"\xC1"=>[18,"\x2B0"],"\xC2"=>[77,"\x2B1"],"\xC3"=>[18,"\x2B1"],"\xC4"=>[77,"\x2B2"],"\xC5"=>[18,"\x2B2"],"\xC6"=>[77,"\x2Ba"],"\xC7"=>[18,"\x2Ba"],"\xC8"=>[77,"\x2Bc"],"\xC9"=>[18,"\x2Bc"],"\xCA"=>[77,"\x2Be"],"\xCB"=>[18,"\x2Be"],"\xCC"=>[77,"\x2Bi"],"\xCD"=>[18,"\x2Bi"],"\xCE"=>[77,"\x2Bo"],"\xCF"=>[18,"\x2Bo"],"\xD0"=>[77,"\x2Bs"],"\xD1"=>[18,"\x2Bs"],"\xD2"=>[77,"\x2Bt"],"\xD3"=>[18,"\x2Bt"],"\xD4"=>[0,"\x2B\x20"],"\xD5"=>[0,"\x2B\x25"],"\xD6"=>[0,"\x2B-"],"\xD7"=>[0,"\x2B."],"\xD8"=>[0,"\x2B\x2F"],"\xD9"=>[0,"\x2B3"],"\xDA"=>[0,"\x2B4"],"\xDB"=>[0,"\x2B5"],"\xDC"=>[0,"\x2B6"],"\xDD"=>[0,"\x2B7"],"\xDE"=>[0,"\x2B8"],"\xDF"=>[0,"\x2B9"],"\xE0"=>[0,"\x2B\x3D"],"\xE1"=>[0,"\x2BA"],"\xE2"=>[0,"\x2B_"],"\xE3"=>[0,"\x2Bb"],"\xE4"=>[0,"\x2Bd"],"\xE5"=>[0,"\x2Bf"],"\xE6"=>[0,"\x2Bg"],"\xE7"=>[0,"\x2Bh"],"\xE8"=>[0,"\x2Bl"],"\xE9"=>[0,"\x2Bm"],"\xEA"=>[0,"\x2Bn"],"\xEB"=>[0,"\x2Bp"],"\xEC"=>[0,"\x2Br"],"\xED"=>[0,"\x2Bu"],"\xEE"=>[100,"\x2B"],"\xEF"=>[110,"\x2B"],"\xF0"=>[111,"\x2B"],"\xF1"=>[115,"\x2B"],"\xF2"=>[116,"\x2B"],"\xF3"=>[118,"\x2B"],"\xF4"=>[119,"\x2B"],"\xF5"=>[122,"\x2B"],"\xF6"=>[123,"\x2B"],"\xF7"=>[125,"\x2B"],"\xF8"=>[126,"\x2B"],"\xF9"=>[129,"\x2B"],"\xFA"=>[143,"\x2B"],"\xFB"=>[148,"\x2B"],"\xFC"=>[151,"\x2B"],"\xFD"=>[153,"\x2B"],"\xFE"=>[83,"\x2B"],"\xFF"=>[10,"\x2B"],],["\x00"=>[94,"\x280"],"\x01"=>[76,"\x280"],"\x02"=>[104,"\x280"],"\x03"=>[16,"\x280"],"\x04"=>[94,"\x281"],"\x05"=>[76,"\x281"],"\x06"=>[104,"\x281"],"\x07"=>[16,"\x281"],"\x08"=>[94,"\x282"],"\x09"=>[76,"\x282"],"\x0A"=>[104,"\x282"],"\x0B"=>[16,"\x282"],"\x0C"=>[94,"\x28a"],"\x0D"=>[76,"\x28a"],"\x0E"=>[104,"\x28a"],"\x0F"=>[16,"\x28a"],"\x10"=>[94,"\x28c"],"\x11"=>[76,"\x28c"],"\x12"=>[104,"\x28c"],"\x13"=>[16,"\x28c"],"\x14"=>[94,"\x28e"],"\x15"=>[76,"\x28e"],"\x16"=>[104,"\x28e"],"\x17"=>[16,"\x28e"],"\x18"=>[94,"\x28i"],"\x19"=>[76,"\x28i"],"\x1A"=>[104,"\x28i"],"\x1B"=>[16,"\x28i"],"\x1C"=>[94,"\x28o"],"\x1D"=>[76,"\x28o"],"\x1E"=>[104,"\x28o"],"\x1F"=>[16,"\x28o"],"\x20"=>[94,"\x28s"],"\x21"=>[76,"\x28s"],"\x22"=>[104,"\x28s"],"\x23"=>[16,"\x28s"],"\x24"=>[94,"\x28t"],"\x25"=>[76,"\x28t"],"\x26"=>[104,"\x28t"],"\x27"=>[16,"\x28t"],"\x28"=>[77,"\x28\x20"],"\x29"=>[18,"\x28\x20"],"\x2A"=>[77,"\x28\x25"],"\x2B"=>[18,"\x28\x25"],"\x2C"=>[77,"\x28-"],"-"=>[18,"\x28-"],"."=>[77,"\x28."],"\x2F"=>[18,"\x28."],[77,"\x28\x2F"],[18,"\x28\x2F"],[77,"\x283"],[18,"\x283"],[77,"\x284"],[18,"\x284"],[77,"\x285"],[18,"\x285"],[77,"\x286"],[18,"\x286"],"\x3A"=>[77,"\x287"],"\x3B"=>[18,"\x287"],"\x3C"=>[77,"\x288"],"\x3D"=>[18,"\x288"],"\x3E"=>[77,"\x289"],"\x3F"=>[18,"\x289"],"\x40"=>[77,"\x28\x3D"],"A"=>[18,"\x28\x3D"],"B"=>[77,"\x28A"],"C"=>[18,"\x28A"],"D"=>[77,"\x28_"],"E"=>[18,"\x28_"],"F"=>[77,"\x28b"],"G"=>[18,"\x28b"],"H"=>[77,"\x28d"],"I"=>[18,"\x28d"],"J"=>[77,"\x28f"],"K"=>[18,"\x28f"],"L"=>[77,"\x28g"],"M"=>[18,"\x28g"],"N"=>[77,"\x28h"],"O"=>[18,"\x28h"],"P"=>[77,"\x28l"],"Q"=>[18,"\x28l"],"R"=>[77,"\x28m"],"S"=>[18,"\x28m"],"T"=>[77,"\x28n"],"U"=>[18,"\x28n"],"V"=>[77,"\x28p"],"W"=>[18,"\x28p"],"X"=>[77,"\x28r"],"Y"=>[18,"\x28r"],"Z"=>[77,"\x28u"],"\x5B"=>[18,"\x28u"],"\x5C"=>[0,"\x28\x3A"],"\x5D"=>[0,"\x28B"],"\x5E"=>[0,"\x28C"],"_"=>[0,"\x28D"],"\x60"=>[0,"\x28E"],"a"=>[0,"\x28F"],"b"=>[0,"\x28G"],"c"=>[0,"\x28H"],"d"=>[0,"\x28I"],"e"=>[0,"\x28J"],"f"=>[0,"\x28K"],"g"=>[0,"\x28L"],"h"=>[0,"\x28M"],"i"=>[0,"\x28N"],"j"=>[0,"\x28O"],"k"=>[0,"\x28P"],"l"=>[0,"\x28Q"],"m"=>[0,"\x28R"],"n"=>[0,"\x28S"],"o"=>[0,"\x28T"],"p"=>[0,"\x28U"],"q"=>[0,"\x28V"],"r"=>[0,"\x28W"],"s"=>[0,"\x28Y"],"t"=>[0,"\x28j"],"u"=>[0,"\x28k"],"v"=>[0,"\x28q"],"w"=>[0,"\x28v"],"x"=>[0,"\x28w"],"y"=>[0,"\x28x"],"z"=>[0,"\x28y"],"\x7B"=>[0,"\x28z"],"\x7C"=>[82,"\x28"],"\x7D"=>[87,"\x28"],"~"=>[130,"\x28"],"\x7F"=>[9,"\x28"],"\x80"=>[94,"\x290"],"\x81"=>[76,"\x290"],"\x82"=>[104,"\x290"],"\x83"=>[16,"\x290"],"\x84"=>[94,"\x291"],"\x85"=>[76,"\x291"],"\x86"=>[104,"\x291"],"\x87"=>[16,"\x291"],"\x88"=>[94,"\x292"],"\x89"=>[76,"\x292"],"\x8A"=>[104,"\x292"],"\x8B"=>[16,"\x292"],"\x8C"=>[94,"\x29a"],"\x8D"=>[76,"\x29a"],"\x8E"=>[104,"\x29a"],"\x8F"=>[16,"\x29a"],"\x90"=>[94,"\x29c"],"\x91"=>[76,"\x29c"],"\x92"=>[104,"\x29c"],"\x93"=>[16,"\x29c"],"\x94"=>[94,"\x29e"],"\x95"=>[76,"\x29e"],"\x96"=>[104,"\x29e"],"\x97"=>[16,"\x29e"],"\x98"=>[94,"\x29i"],"\x99"=>[76,"\x29i"],"\x9A"=>[104,"\x29i"],"\x9B"=>[16,"\x29i"],"\x9C"=>[94,"\x29o"],"\x9D"=>[76,"\x29o"],"\x9E"=>[104,"\x29o"],"\x9F"=>[16,"\x29o"],"\xA0"=>[94,"\x29s"],"\xA1"=>[76,"\x29s"],"\xA2"=>[104,"\x29s"],"\xA3"=>[16,"\x29s"],"\xA4"=>[94,"\x29t"],"\xA5"=>[76,"\x29t"],"\xA6"=>[104,"\x29t"],"\xA7"=>[16,"\x29t"],"\xA8"=>[77,"\x29\x20"],"\xA9"=>[18,"\x29\x20"],"\xAA"=>[77,"\x29\x25"],"\xAB"=>[18,"\x29\x25"],"\xAC"=>[77,"\x29-"],"\xAD"=>[18,"\x29-"],"\xAE"=>[77,"\x29."],"\xAF"=>[18,"\x29."],"\xB0"=>[77,"\x29\x2F"],"\xB1"=>[18,"\x29\x2F"],"\xB2"=>[77,"\x293"],"\xB3"=>[18,"\x293"],"\xB4"=>[77,"\x294"],"\xB5"=>[18,"\x294"],"\xB6"=>[77,"\x295"],"\xB7"=>[18,"\x295"],"\xB8"=>[77,"\x296"],"\xB9"=>[18,"\x296"],"\xBA"=>[77,"\x297"],"\xBB"=>[18,"\x297"],"\xBC"=>[77,"\x298"],"\xBD"=>[18,"\x298"],"\xBE"=>[77,"\x299"],"\xBF"=>[18,"\x299"],"\xC0"=>[77,"\x29\x3D"],"\xC1"=>[18,"\x29\x3D"],"\xC2"=>[77,"\x29A"],"\xC3"=>[18,"\x29A"],"\xC4"=>[77,"\x29_"],"\xC5"=>[18,"\x29_"],"\xC6"=>[77,"\x29b"],"\xC7"=>[18,"\x29b"],"\xC8"=>[77,"\x29d"],"\xC9"=>[18,"\x29d"],"\xCA"=>[77,"\x29f"],"\xCB"=>[18,"\x29f"],"\xCC"=>[77,"\x29g"],"\xCD"=>[18,"\x29g"],"\xCE"=>[77,"\x29h"],"\xCF"=>[18,"\x29h"],"\xD0"=>[77,"\x29l"],"\xD1"=>[18,"\x29l"],"\xD2"=>[77,"\x29m"],"\xD3"=>[18,"\x29m"],"\xD4"=>[77,"\x29n"],"\xD5"=>[18,"\x29n"],"\xD6"=>[77,"\x29p"],"\xD7"=>[18,"\x29p"],"\xD8"=>[77,"\x29r"],"\xD9"=>[18,"\x29r"],"\xDA"=>[77,"\x29u"],"\xDB"=>[18,"\x29u"],"\xDC"=>[0,"\x29\x3A"],"\xDD"=>[0,"\x29B"],"\xDE"=>[0,"\x29C"],"\xDF"=>[0,"\x29D"],"\xE0"=>[0,"\x29E"],"\xE1"=>[0,"\x29F"],"\xE2"=>[0,"\x29G"],"\xE3"=>[0,"\x29H"],"\xE4"=>[0,"\x29I"],"\xE5"=>[0,"\x29J"],"\xE6"=>[0,"\x29K"],"\xE7"=>[0,"\x29L"],"\xE8"=>[0,"\x29M"],"\xE9"=>[0,"\x29N"],"\xEA"=>[0,"\x29O"],"\xEB"=>[0,"\x29P"],"\xEC"=>[0,"\x29Q"],"\xED"=>[0,"\x29R"],"\xEE"=>[0,"\x29S"],"\xEF"=>[0,"\x29T"],"\xF0"=>[0,"\x29U"],"\xF1"=>[0,"\x29V"],"\xF2"=>[0,"\x29W"],"\xF3"=>[0,"\x29Y"],"\xF4"=>[0,"\x29j"],"\xF5"=>[0,"\x29k"],"\xF6"=>[0,"\x29q"],"\xF7"=>[0,"\x29v"],"\xF8"=>[0,"\x29w"],"\xF9"=>[0,"\x29x"],"\xFA"=>[0,"\x29y"],"\xFB"=>[0,"\x29z"],"\xFC"=>[82,"\x29"],"\xFD"=>[87,"\x29"],"\xFE"=>[130,"\x29"],"\xFF"=>[9,"\x29"],],["\x00"=>[94,"\x2C0"],"\x01"=>[76,"\x2C0"],"\x02"=>[104,"\x2C0"],"\x03"=>[16,"\x2C0"],"\x04"=>[94,"\x2C1"],"\x05"=>[76,"\x2C1"],"\x06"=>[104,"\x2C1"],"\x07"=>[16,"\x2C1"],"\x08"=>[94,"\x2C2"],"\x09"=>[76,"\x2C2"],"\x0A"=>[104,"\x2C2"],"\x0B"=>[16,"\x2C2"],"\x0C"=>[94,"\x2Ca"],"\x0D"=>[76,"\x2Ca"],"\x0E"=>[104,"\x2Ca"],"\x0F"=>[16,"\x2Ca"],"\x10"=>[94,"\x2Cc"],"\x11"=>[76,"\x2Cc"],"\x12"=>[104,"\x2Cc"],"\x13"=>[16,"\x2Cc"],"\x14"=>[94,"\x2Ce"],"\x15"=>[76,"\x2Ce"],"\x16"=>[104,"\x2Ce"],"\x17"=>[16,"\x2Ce"],"\x18"=>[94,"\x2Ci"],"\x19"=>[76,"\x2Ci"],"\x1A"=>[104,"\x2Ci"],"\x1B"=>[16,"\x2Ci"],"\x1C"=>[94,"\x2Co"],"\x1D"=>[76,"\x2Co"],"\x1E"=>[104,"\x2Co"],"\x1F"=>[16,"\x2Co"],"\x20"=>[94,"\x2Cs"],"\x21"=>[76,"\x2Cs"],"\x22"=>[104,"\x2Cs"],"\x23"=>[16,"\x2Cs"],"\x24"=>[94,"\x2Ct"],"\x25"=>[76,"\x2Ct"],"\x26"=>[104,"\x2Ct"],"\x27"=>[16,"\x2Ct"],"\x28"=>[77,"\x2C\x20"],"\x29"=>[18,"\x2C\x20"],"\x2A"=>[77,"\x2C\x25"],"\x2B"=>[18,"\x2C\x25"],"\x2C"=>[77,"\x2C-"],"-"=>[18,"\x2C-"],"."=>[77,"\x2C."],"\x2F"=>[18,"\x2C."],[77,"\x2C\x2F"],[18,"\x2C\x2F"],[77,"\x2C3"],[18,"\x2C3"],[77,"\x2C4"],[18,"\x2C4"],[77,"\x2C5"],[18,"\x2C5"],[77,"\x2C6"],[18,"\x2C6"],"\x3A"=>[77,"\x2C7"],"\x3B"=>[18,"\x2C7"],"\x3C"=>[77,"\x2C8"],"\x3D"=>[18,"\x2C8"],"\x3E"=>[77,"\x2C9"],"\x3F"=>[18,"\x2C9"],"\x40"=>[77,"\x2C\x3D"],"A"=>[18,"\x2C\x3D"],"B"=>[77,"\x2CA"],"C"=>[18,"\x2CA"],"D"=>[77,"\x2C_"],"E"=>[18,"\x2C_"],"F"=>[77,"\x2Cb"],"G"=>[18,"\x2Cb"],"H"=>[77,"\x2Cd"],"I"=>[18,"\x2Cd"],"J"=>[77,"\x2Cf"],"K"=>[18,"\x2Cf"],"L"=>[77,"\x2Cg"],"M"=>[18,"\x2Cg"],"N"=>[77,"\x2Ch"],"O"=>[18,"\x2Ch"],"P"=>[77,"\x2Cl"],"Q"=>[18,"\x2Cl"],"R"=>[77,"\x2Cm"],"S"=>[18,"\x2Cm"],"T"=>[77,"\x2Cn"],"U"=>[18,"\x2Cn"],"V"=>[77,"\x2Cp"],"W"=>[18,"\x2Cp"],"X"=>[77,"\x2Cr"],"Y"=>[18,"\x2Cr"],"Z"=>[77,"\x2Cu"],"\x5B"=>[18,"\x2Cu"],"\x5C"=>[0,"\x2C\x3A"],"\x5D"=>[0,"\x2CB"],"\x5E"=>[0,"\x2CC"],"_"=>[0,"\x2CD"],"\x60"=>[0,"\x2CE"],"a"=>[0,"\x2CF"],"b"=>[0,"\x2CG"],"c"=>[0,"\x2CH"],"d"=>[0,"\x2CI"],"e"=>[0,"\x2CJ"],"f"=>[0,"\x2CK"],"g"=>[0,"\x2CL"],"h"=>[0,"\x2CM"],"i"=>[0,"\x2CN"],"j"=>[0,"\x2CO"],"k"=>[0,"\x2CP"],"l"=>[0,"\x2CQ"],"m"=>[0,"\x2CR"],"n"=>[0,"\x2CS"],"o"=>[0,"\x2CT"],"p"=>[0,"\x2CU"],"q"=>[0,"\x2CV"],"r"=>[0,"\x2CW"],"s"=>[0,"\x2CY"],"t"=>[0,"\x2Cj"],"u"=>[0,"\x2Ck"],"v"=>[0,"\x2Cq"],"w"=>[0,"\x2Cv"],"x"=>[0,"\x2Cw"],"y"=>[0,"\x2Cx"],"z"=>[0,"\x2Cy"],"\x7B"=>[0,"\x2Cz"],"\x7C"=>[82,"\x2C"],"\x7D"=>[87,"\x2C"],"~"=>[130,"\x2C"],"\x7F"=>[9,"\x2C"],"\x80"=>[94,"\x3B0"],"\x81"=>[76,"\x3B0"],"\x82"=>[104,"\x3B0"],"\x83"=>[16,"\x3B0"],"\x84"=>[94,"\x3B1"],"\x85"=>[76,"\x3B1"],"\x86"=>[104,"\x3B1"],"\x87"=>[16,"\x3B1"],"\x88"=>[94,"\x3B2"],"\x89"=>[76,"\x3B2"],"\x8A"=>[104,"\x3B2"],"\x8B"=>[16,"\x3B2"],"\x8C"=>[94,"\x3Ba"],"\x8D"=>[76,"\x3Ba"],"\x8E"=>[104,"\x3Ba"],"\x8F"=>[16,"\x3Ba"],"\x90"=>[94,"\x3Bc"],"\x91"=>[76,"\x3Bc"],"\x92"=>[104,"\x3Bc"],"\x93"=>[16,"\x3Bc"],"\x94"=>[94,"\x3Be"],"\x95"=>[76,"\x3Be"],"\x96"=>[104,"\x3Be"],"\x97"=>[16,"\x3Be"],"\x98"=>[94,"\x3Bi"],"\x99"=>[76,"\x3Bi"],"\x9A"=>[104,"\x3Bi"],"\x9B"=>[16,"\x3Bi"],"\x9C"=>[94,"\x3Bo"],"\x9D"=>[76,"\x3Bo"],"\x9E"=>[104,"\x3Bo"],"\x9F"=>[16,"\x3Bo"],"\xA0"=>[94,"\x3Bs"],"\xA1"=>[76,"\x3Bs"],"\xA2"=>[104,"\x3Bs"],"\xA3"=>[16,"\x3Bs"],"\xA4"=>[94,"\x3Bt"],"\xA5"=>[76,"\x3Bt"],"\xA6"=>[104,"\x3Bt"],"\xA7"=>[16,"\x3Bt"],"\xA8"=>[77,"\x3B\x20"],"\xA9"=>[18,"\x3B\x20"],"\xAA"=>[77,"\x3B\x25"],"\xAB"=>[18,"\x3B\x25"],"\xAC"=>[77,"\x3B-"],"\xAD"=>[18,"\x3B-"],"\xAE"=>[77,"\x3B."],"\xAF"=>[18,"\x3B."],"\xB0"=>[77,"\x3B\x2F"],"\xB1"=>[18,"\x3B\x2F"],"\xB2"=>[77,"\x3B3"],"\xB3"=>[18,"\x3B3"],"\xB4"=>[77,"\x3B4"],"\xB5"=>[18,"\x3B4"],"\xB6"=>[77,"\x3B5"],"\xB7"=>[18,"\x3B5"],"\xB8"=>[77,"\x3B6"],"\xB9"=>[18,"\x3B6"],"\xBA"=>[77,"\x3B7"],"\xBB"=>[18,"\x3B7"],"\xBC"=>[77,"\x3B8"],"\xBD"=>[18,"\x3B8"],"\xBE"=>[77,"\x3B9"],"\xBF"=>[18,"\x3B9"],"\xC0"=>[77,"\x3B\x3D"],"\xC1"=>[18,"\x3B\x3D"],"\xC2"=>[77,"\x3BA"],"\xC3"=>[18,"\x3BA"],"\xC4"=>[77,"\x3B_"],"\xC5"=>[18,"\x3B_"],"\xC6"=>[77,"\x3Bb"],"\xC7"=>[18,"\x3Bb"],"\xC8"=>[77,"\x3Bd"],"\xC9"=>[18,"\x3Bd"],"\xCA"=>[77,"\x3Bf"],"\xCB"=>[18,"\x3Bf"],"\xCC"=>[77,"\x3Bg"],"\xCD"=>[18,"\x3Bg"],"\xCE"=>[77,"\x3Bh"],"\xCF"=>[18,"\x3Bh"],"\xD0"=>[77,"\x3Bl"],"\xD1"=>[18,"\x3Bl"],"\xD2"=>[77,"\x3Bm"],"\xD3"=>[18,"\x3Bm"],"\xD4"=>[77,"\x3Bn"],"\xD5"=>[18,"\x3Bn"],"\xD6"=>[77,"\x3Bp"],"\xD7"=>[18,"\x3Bp"],"\xD8"=>[77,"\x3Br"],"\xD9"=>[18,"\x3Br"],"\xDA"=>[77,"\x3Bu"],"\xDB"=>[18,"\x3Bu"],"\xDC"=>[0,"\x3B\x3A"],"\xDD"=>[0,"\x3BB"],"\xDE"=>[0,"\x3BC"],"\xDF"=>[0,"\x3BD"],"\xE0"=>[0,"\x3BE"],"\xE1"=>[0,"\x3BF"],"\xE2"=>[0,"\x3BG"],"\xE3"=>[0,"\x3BH"],"\xE4"=>[0,"\x3BI"],"\xE5"=>[0,"\x3BJ"],"\xE6"=>[0,"\x3BK"],"\xE7"=>[0,"\x3BL"],"\xE8"=>[0,"\x3BM"],"\xE9"=>[0,"\x3BN"],"\xEA"=>[0,"\x3BO"],"\xEB"=>[0,"\x3BP"],"\xEC"=>[0,"\x3BQ"],"\xED"=>[0,"\x3BR"],"\xEE"=>[0,"\x3BS"],"\xEF"=>[0,"\x3BT"],"\xF0"=>[0,"\x3BU"],"\xF1"=>[0,"\x3BV"],"\xF2"=>[0,"\x3BW"],"\xF3"=>[0,"\x3BY"],"\xF4"=>[0,"\x3Bj"],"\xF5"=>[0,"\x3Bk"],"\xF6"=>[0,"\x3Bq"],"\xF7"=>[0,"\x3Bv"],"\xF8"=>[0,"\x3Bw"],"\xF9"=>[0,"\x3Bx"],"\xFA"=>[0,"\x3By"],"\xFB"=>[0,"\x3Bz"],"\xFC"=>[82,"\x3B"],"\xFD"=>[87,"\x3B"],"\xFE"=>[130,"\x3B"],"\xFF"=>[9,"\x3B"],],["\x00"=>[94,"-0"],"\x01"=>[76,"-0"],"\x02"=>[104,"-0"],"\x03"=>[16,"-0"],"\x04"=>[94,"-1"],"\x05"=>[76,"-1"],"\x06"=>[104,"-1"],"\x07"=>[16,"-1"],"\x08"=>[94,"-2"],"\x09"=>[76,"-2"],"\x0A"=>[104,"-2"],"\x0B"=>[16,"-2"],"\x0C"=>[94,"-a"],"\x0D"=>[76,"-a"],"\x0E"=>[104,"-a"],"\x0F"=>[16,"-a"],"\x10"=>[94,"-c"],"\x11"=>[76,"-c"],"\x12"=>[104,"-c"],"\x13"=>[16,"-c"],"\x14"=>[94,"-e"],"\x15"=>[76,"-e"],"\x16"=>[104,"-e"],"\x17"=>[16,"-e"],"\x18"=>[94,"-i"],"\x19"=>[76,"-i"],"\x1A"=>[104,"-i"],"\x1B"=>[16,"-i"],"\x1C"=>[94,"-o"],"\x1D"=>[76,"-o"],"\x1E"=>[104,"-o"],"\x1F"=>[16,"-o"],"\x20"=>[94,"-s"],"\x21"=>[76,"-s"],"\x22"=>[104,"-s"],"\x23"=>[16,"-s"],"\x24"=>[94,"-t"],"\x25"=>[76,"-t"],"\x26"=>[104,"-t"],"\x27"=>[16,"-t"],"\x28"=>[77,"-\x20"],"\x29"=>[18,"-\x20"],"\x2A"=>[77,"-\x25"],"\x2B"=>[18,"-\x25"],"\x2C"=>[77,"--"],"-"=>[18,"--"],"."=>[77,"-."],"\x2F"=>[18,"-."],[77,"-\x2F"],[18,"-\x2F"],[77,"-3"],[18,"-3"],[77,"-4"],[18,"-4"],[77,"-5"],[18,"-5"],[77,"-6"],[18,"-6"],"\x3A"=>[77,"-7"],"\x3B"=>[18,"-7"],"\x3C"=>[77,"-8"],"\x3D"=>[18,"-8"],"\x3E"=>[77,"-9"],"\x3F"=>[18,"-9"],"\x40"=>[77,"-\x3D"],"A"=>[18,"-\x3D"],"B"=>[77,"-A"],"C"=>[18,"-A"],"D"=>[77,"-_"],"E"=>[18,"-_"],"F"=>[77,"-b"],"G"=>[18,"-b"],"H"=>[77,"-d"],"I"=>[18,"-d"],"J"=>[77,"-f"],"K"=>[18,"-f"],"L"=>[77,"-g"],"M"=>[18,"-g"],"N"=>[77,"-h"],"O"=>[18,"-h"],"P"=>[77,"-l"],"Q"=>[18,"-l"],"R"=>[77,"-m"],"S"=>[18,"-m"],"T"=>[77,"-n"],"U"=>[18,"-n"],"V"=>[77,"-p"],"W"=>[18,"-p"],"X"=>[77,"-r"],"Y"=>[18,"-r"],"Z"=>[77,"-u"],"\x5B"=>[18,"-u"],"\x5C"=>[0,"-\x3A"],"\x5D"=>[0,"-B"],"\x5E"=>[0,"-C"],"_"=>[0,"-D"],"\x60"=>[0,"-E"],"a"=>[0,"-F"],"b"=>[0,"-G"],"c"=>[0,"-H"],"d"=>[0,"-I"],"e"=>[0,"-J"],"f"=>[0,"-K"],"g"=>[0,"-L"],"h"=>[0,"-M"],"i"=>[0,"-N"],"j"=>[0,"-O"],"k"=>[0,"-P"],"l"=>[0,"-Q"],"m"=>[0,"-R"],"n"=>[0,"-S"],"o"=>[0,"-T"],"p"=>[0,"-U"],"q"=>[0,"-V"],"r"=>[0,"-W"],"s"=>[0,"-Y"],"t"=>[0,"-j"],"u"=>[0,"-k"],"v"=>[0,"-q"],"w"=>[0,"-v"],"x"=>[0,"-w"],"y"=>[0,"-x"],"z"=>[0,"-y"],"\x7B"=>[0,"-z"],"\x7C"=>[82,"-"],"\x7D"=>[87,"-"],"~"=>[130,"-"],"\x7F"=>[9,"-"],"\x80"=>[94,".0"],"\x81"=>[76,".0"],"\x82"=>[104,".0"],"\x83"=>[16,".0"],"\x84"=>[94,".1"],"\x85"=>[76,".1"],"\x86"=>[104,".1"],"\x87"=>[16,".1"],"\x88"=>[94,".2"],"\x89"=>[76,".2"],"\x8A"=>[104,".2"],"\x8B"=>[16,".2"],"\x8C"=>[94,".a"],"\x8D"=>[76,".a"],"\x8E"=>[104,".a"],"\x8F"=>[16,".a"],"\x90"=>[94,".c"],"\x91"=>[76,".c"],"\x92"=>[104,".c"],"\x93"=>[16,".c"],"\x94"=>[94,".e"],"\x95"=>[76,".e"],"\x96"=>[104,".e"],"\x97"=>[16,".e"],"\x98"=>[94,".i"],"\x99"=>[76,".i"],"\x9A"=>[104,".i"],"\x9B"=>[16,".i"],"\x9C"=>[94,".o"],"\x9D"=>[76,".o"],"\x9E"=>[104,".o"],"\x9F"=>[16,".o"],"\xA0"=>[94,".s"],"\xA1"=>[76,".s"],"\xA2"=>[104,".s"],"\xA3"=>[16,".s"],"\xA4"=>[94,".t"],"\xA5"=>[76,".t"],"\xA6"=>[104,".t"],"\xA7"=>[16,".t"],"\xA8"=>[77,".\x20"],"\xA9"=>[18,".\x20"],"\xAA"=>[77,".\x25"],"\xAB"=>[18,".\x25"],"\xAC"=>[77,".-"],"\xAD"=>[18,".-"],"\xAE"=>[77,".."],"\xAF"=>[18,".."],"\xB0"=>[77,".\x2F"],"\xB1"=>[18,".\x2F"],"\xB2"=>[77,".3"],"\xB3"=>[18,".3"],"\xB4"=>[77,".4"],"\xB5"=>[18,".4"],"\xB6"=>[77,".5"],"\xB7"=>[18,".5"],"\xB8"=>[77,".6"],"\xB9"=>[18,".6"],"\xBA"=>[77,".7"],"\xBB"=>[18,".7"],"\xBC"=>[77,".8"],"\xBD"=>[18,".8"],"\xBE"=>[77,".9"],"\xBF"=>[18,".9"],"\xC0"=>[77,".\x3D"],"\xC1"=>[18,".\x3D"],"\xC2"=>[77,".A"],"\xC3"=>[18,".A"],"\xC4"=>[77,"._"],"\xC5"=>[18,"._"],"\xC6"=>[77,".b"],"\xC7"=>[18,".b"],"\xC8"=>[77,".d"],"\xC9"=>[18,".d"],"\xCA"=>[77,".f"],"\xCB"=>[18,".f"],"\xCC"=>[77,".g"],"\xCD"=>[18,".g"],"\xCE"=>[77,".h"],"\xCF"=>[18,".h"],"\xD0"=>[77,".l"],"\xD1"=>[18,".l"],"\xD2"=>[77,".m"],"\xD3"=>[18,".m"],"\xD4"=>[77,".n"],"\xD5"=>[18,".n"],"\xD6"=>[77,".p"],"\xD7"=>[18,".p"],"\xD8"=>[77,".r"],"\xD9"=>[18,".r"],"\xDA"=>[77,".u"],"\xDB"=>[18,".u"],"\xDC"=>[0,".\x3A"],"\xDD"=>[0,".B"],"\xDE"=>[0,".C"],"\xDF"=>[0,".D"],"\xE0"=>[0,".E"],"\xE1"=>[0,".F"],"\xE2"=>[0,".G"],"\xE3"=>[0,".H"],"\xE4"=>[0,".I"],"\xE5"=>[0,".J"],"\xE6"=>[0,".K"],"\xE7"=>[0,".L"],"\xE8"=>[0,".M"],"\xE9"=>[0,".N"],"\xEA"=>[0,".O"],"\xEB"=>[0,".P"],"\xEC"=>[0,".Q"],"\xED"=>[0,".R"],"\xEE"=>[0,".S"],"\xEF"=>[0,".T"],"\xF0"=>[0,".U"],"\xF1"=>[0,".V"],"\xF2"=>[0,".W"],"\xF3"=>[0,".Y"],"\xF4"=>[0,".j"],"\xF5"=>[0,".k"],"\xF6"=>[0,".q"],"\xF7"=>[0,".v"],"\xF8"=>[0,".w"],"\xF9"=>[0,".x"],"\xFA"=>[0,".y"],"\xFB"=>[0,".z"],"\xFC"=>[82,"."],"\xFD"=>[87,"."],"\xFE"=>[130,"."],"\xFF"=>[9,"."],],["\x00"=>[94,"\x2F0"],"\x01"=>[76,"\x2F0"],"\x02"=>[104,"\x2F0"],"\x03"=>[16,"\x2F0"],"\x04"=>[94,"\x2F1"],"\x05"=>[76,"\x2F1"],"\x06"=>[104,"\x2F1"],"\x07"=>[16,"\x2F1"],"\x08"=>[94,"\x2F2"],"\x09"=>[76,"\x2F2"],"\x0A"=>[104,"\x2F2"],"\x0B"=>[16,"\x2F2"],"\x0C"=>[94,"\x2Fa"],"\x0D"=>[76,"\x2Fa"],"\x0E"=>[104,"\x2Fa"],"\x0F"=>[16,"\x2Fa"],"\x10"=>[94,"\x2Fc"],"\x11"=>[76,"\x2Fc"],"\x12"=>[104,"\x2Fc"],"\x13"=>[16,"\x2Fc"],"\x14"=>[94,"\x2Fe"],"\x15"=>[76,"\x2Fe"],"\x16"=>[104,"\x2Fe"],"\x17"=>[16,"\x2Fe"],"\x18"=>[94,"\x2Fi"],"\x19"=>[76,"\x2Fi"],"\x1A"=>[104,"\x2Fi"],"\x1B"=>[16,"\x2Fi"],"\x1C"=>[94,"\x2Fo"],"\x1D"=>[76,"\x2Fo"],"\x1E"=>[104,"\x2Fo"],"\x1F"=>[16,"\x2Fo"],"\x20"=>[94,"\x2Fs"],"\x21"=>[76,"\x2Fs"],"\x22"=>[104,"\x2Fs"],"\x23"=>[16,"\x2Fs"],"\x24"=>[94,"\x2Ft"],"\x25"=>[76,"\x2Ft"],"\x26"=>[104,"\x2Ft"],"\x27"=>[16,"\x2Ft"],"\x28"=>[77,"\x2F\x20"],"\x29"=>[18,"\x2F\x20"],"\x2A"=>[77,"\x2F\x25"],"\x2B"=>[18,"\x2F\x25"],"\x2C"=>[77,"\x2F-"],"-"=>[18,"\x2F-"],"."=>[77,"\x2F."],"\x2F"=>[18,"\x2F."],[77,"\x2F\x2F"],[18,"\x2F\x2F"],[77,"\x2F3"],[18,"\x2F3"],[77,"\x2F4"],[18,"\x2F4"],[77,"\x2F5"],[18,"\x2F5"],[77,"\x2F6"],[18,"\x2F6"],"\x3A"=>[77,"\x2F7"],"\x3B"=>[18,"\x2F7"],"\x3C"=>[77,"\x2F8"],"\x3D"=>[18,"\x2F8"],"\x3E"=>[77,"\x2F9"],"\x3F"=>[18,"\x2F9"],"\x40"=>[77,"\x2F\x3D"],"A"=>[18,"\x2F\x3D"],"B"=>[77,"\x2FA"],"C"=>[18,"\x2FA"],"D"=>[77,"\x2F_"],"E"=>[18,"\x2F_"],"F"=>[77,"\x2Fb"],"G"=>[18,"\x2Fb"],"H"=>[77,"\x2Fd"],"I"=>[18,"\x2Fd"],"J"=>[77,"\x2Ff"],"K"=>[18,"\x2Ff"],"L"=>[77,"\x2Fg"],"M"=>[18,"\x2Fg"],"N"=>[77,"\x2Fh"],"O"=>[18,"\x2Fh"],"P"=>[77,"\x2Fl"],"Q"=>[18,"\x2Fl"],"R"=>[77,"\x2Fm"],"S"=>[18,"\x2Fm"],"T"=>[77,"\x2Fn"],"U"=>[18,"\x2Fn"],"V"=>[77,"\x2Fp"],"W"=>[18,"\x2Fp"],"X"=>[77,"\x2Fr"],"Y"=>[18,"\x2Fr"],"Z"=>[77,"\x2Fu"],"\x5B"=>[18,"\x2Fu"],"\x5C"=>[0,"\x2F\x3A"],"\x5D"=>[0,"\x2FB"],"\x5E"=>[0,"\x2FC"],"_"=>[0,"\x2FD"],"\x60"=>[0,"\x2FE"],"a"=>[0,"\x2FF"],"b"=>[0,"\x2FG"],"c"=>[0,"\x2FH"],"d"=>[0,"\x2FI"],"e"=>[0,"\x2FJ"],"f"=>[0,"\x2FK"],"g"=>[0,"\x2FL"],"h"=>[0,"\x2FM"],"i"=>[0,"\x2FN"],"j"=>[0,"\x2FO"],"k"=>[0,"\x2FP"],"l"=>[0,"\x2FQ"],"m"=>[0,"\x2FR"],"n"=>[0,"\x2FS"],"o"=>[0,"\x2FT"],"p"=>[0,"\x2FU"],"q"=>[0,"\x2FV"],"r"=>[0,"\x2FW"],"s"=>[0,"\x2FY"],"t"=>[0,"\x2Fj"],"u"=>[0,"\x2Fk"],"v"=>[0,"\x2Fq"],"w"=>[0,"\x2Fv"],"x"=>[0,"\x2Fw"],"y"=>[0,"\x2Fx"],"z"=>[0,"\x2Fy"],"\x7B"=>[0,"\x2Fz"],"\x7C"=>[82,"\x2F"],"\x7D"=>[87,"\x2F"],"~"=>[130,"\x2F"],"\x7F"=>[9,"\x2F"],"\x80"=>[94,"30"],"\x81"=>[76,"30"],"\x82"=>[104,"30"],"\x83"=>[16,"30"],"\x84"=>[94,"31"],"\x85"=>[76,"31"],"\x86"=>[104,"31"],"\x87"=>[16,"31"],"\x88"=>[94,"32"],"\x89"=>[76,"32"],"\x8A"=>[104,"32"],"\x8B"=>[16,"32"],"\x8C"=>[94,"3a"],"\x8D"=>[76,"3a"],"\x8E"=>[104,"3a"],"\x8F"=>[16,"3a"],"\x90"=>[94,"3c"],"\x91"=>[76,"3c"],"\x92"=>[104,"3c"],"\x93"=>[16,"3c"],"\x94"=>[94,"3e"],"\x95"=>[76,"3e"],"\x96"=>[104,"3e"],"\x97"=>[16,"3e"],"\x98"=>[94,"3i"],"\x99"=>[76,"3i"],"\x9A"=>[104,"3i"],"\x9B"=>[16,"3i"],"\x9C"=>[94,"3o"],"\x9D"=>[76,"3o"],"\x9E"=>[104,"3o"],"\x9F"=>[16,"3o"],"\xA0"=>[94,"3s"],"\xA1"=>[76,"3s"],"\xA2"=>[104,"3s"],"\xA3"=>[16,"3s"],"\xA4"=>[94,"3t"],"\xA5"=>[76,"3t"],"\xA6"=>[104,"3t"],"\xA7"=>[16,"3t"],"\xA8"=>[77,"3\x20"],"\xA9"=>[18,"3\x20"],"\xAA"=>[77,"3\x25"],"\xAB"=>[18,"3\x25"],"\xAC"=>[77,"3-"],"\xAD"=>[18,"3-"],"\xAE"=>[77,"3."],"\xAF"=>[18,"3."],"\xB0"=>[77,"3\x2F"],"\xB1"=>[18,"3\x2F"],"\xB2"=>[77,"33"],"\xB3"=>[18,"33"],"\xB4"=>[77,"34"],"\xB5"=>[18,"34"],"\xB6"=>[77,"35"],"\xB7"=>[18,"35"],"\xB8"=>[77,"36"],"\xB9"=>[18,"36"],"\xBA"=>[77,"37"],"\xBB"=>[18,"37"],"\xBC"=>[77,"38"],"\xBD"=>[18,"38"],"\xBE"=>[77,"39"],"\xBF"=>[18,"39"],"\xC0"=>[77,"3\x3D"],"\xC1"=>[18,"3\x3D"],"\xC2"=>[77,"3A"],"\xC3"=>[18,"3A"],"\xC4"=>[77,"3_"],"\xC5"=>[18,"3_"],"\xC6"=>[77,"3b"],"\xC7"=>[18,"3b"],"\xC8"=>[77,"3d"],"\xC9"=>[18,"3d"],"\xCA"=>[77,"3f"],"\xCB"=>[18,"3f"],"\xCC"=>[77,"3g"],"\xCD"=>[18,"3g"],"\xCE"=>[77,"3h"],"\xCF"=>[18,"3h"],"\xD0"=>[77,"3l"],"\xD1"=>[18,"3l"],"\xD2"=>[77,"3m"],"\xD3"=>[18,"3m"],"\xD4"=>[77,"3n"],"\xD5"=>[18,"3n"],"\xD6"=>[77,"3p"],"\xD7"=>[18,"3p"],"\xD8"=>[77,"3r"],"\xD9"=>[18,"3r"],"\xDA"=>[77,"3u"],"\xDB"=>[18,"3u"],"\xDC"=>[0,"3\x3A"],"\xDD"=>[0,"3B"],"\xDE"=>[0,"3C"],"\xDF"=>[0,"3D"],"\xE0"=>[0,"3E"],"\xE1"=>[0,"3F"],"\xE2"=>[0,"3G"],"\xE3"=>[0,"3H"],"\xE4"=>[0,"3I"],"\xE5"=>[0,"3J"],"\xE6"=>[0,"3K"],"\xE7"=>[0,"3L"],"\xE8"=>[0,"3M"],"\xE9"=>[0,"3N"],"\xEA"=>[0,"3O"],"\xEB"=>[0,"3P"],"\xEC"=>[0,"3Q"],"\xED"=>[0,"3R"],"\xEE"=>[0,"3S"],"\xEF"=>[0,"3T"],"\xF0"=>[0,"3U"],"\xF1"=>[0,"3V"],"\xF2"=>[0,"3W"],"\xF3"=>[0,"3Y"],"\xF4"=>[0,"3j"],"\xF5"=>[0,"3k"],"\xF6"=>[0,"3q"],"\xF7"=>[0,"3v"],"\xF8"=>[0,"3w"],"\xF9"=>[0,"3x"],"\xFA"=>[0,"3y"],"\xFB"=>[0,"3z"],"\xFC"=>[82,"3"],"\xFD"=>[87,"3"],"\xFE"=>[130,"3"],"\xFF"=>[9,"3"],],["\x00"=>[77,"\x2F0"],"\x01"=>[18,"\x2F0"],"\x02"=>[77,"\x2F1"],"\x03"=>[18,"\x2F1"],"\x04"=>[77,"\x2F2"],"\x05"=>[18,"\x2F2"],"\x06"=>[77,"\x2Fa"],"\x07"=>[18,"\x2Fa"],"\x08"=>[77,"\x2Fc"],"\x09"=>[18,"\x2Fc"],"\x0A"=>[77,"\x2Fe"],"\x0B"=>[18,"\x2Fe"],"\x0C"=>[77,"\x2Fi"],"\x0D"=>[18,"\x2Fi"],"\x0E"=>[77,"\x2Fo"],"\x0F"=>[18,"\x2Fo"],"\x10"=>[77,"\x2Fs"],"\x11"=>[18,"\x2Fs"],"\x12"=>[77,"\x2Ft"],"\x13"=>[18,"\x2Ft"],"\x14"=>[0,"\x2F\x20"],"\x15"=>[0,"\x2F\x25"],"\x16"=>[0,"\x2F-"],"\x17"=>[0,"\x2F."],"\x18"=>[0,"\x2F\x2F"],"\x19"=>[0,"\x2F3"],"\x1A"=>[0,"\x2F4"],"\x1B"=>[0,"\x2F5"],"\x1C"=>[0,"\x2F6"],"\x1D"=>[0,"\x2F7"],"\x1E"=>[0,"\x2F8"],"\x1F"=>[0,"\x2F9"],"\x20"=>[0,"\x2F\x3D"],"\x21"=>[0,"\x2FA"],"\x22"=>[0,"\x2F_"],"\x23"=>[0,"\x2Fb"],"\x24"=>[0,"\x2Fd"],"\x25"=>[0,"\x2Ff"],"\x26"=>[0,"\x2Fg"],"\x27"=>[0,"\x2Fh"],"\x28"=>[0,"\x2Fl"],"\x29"=>[0,"\x2Fm"],"\x2A"=>[0,"\x2Fn"],"\x2B"=>[0,"\x2Fp"],"\x2C"=>[0,"\x2Fr"],"-"=>[0,"\x2Fu"],"."=>[100,"\x2F"],"\x2F"=>[110,"\x2F"],[111,"\x2F"],[115,"\x2F"],[116,"\x2F"],[118,"\x2F"],[119,"\x2F"],[122,"\x2F"],[123,"\x2F"],[125,"\x2F"],[126,"\x2F"],[129,"\x2F"],"\x3A"=>[143,"\x2F"],"\x3B"=>[148,"\x2F"],"\x3C"=>[151,"\x2F"],"\x3D"=>[153,"\x2F"],"\x3E"=>[83,"\x2F"],"\x3F"=>[10,"\x2F"],"\x40"=>[77,"30"],"A"=>[18,"30"],"B"=>[77,"31"],"C"=>[18,"31"],"D"=>[77,"32"],"E"=>[18,"32"],"F"=>[77,"3a"],"G"=>[18,"3a"],"H"=>[77,"3c"],"I"=>[18,"3c"],"J"=>[77,"3e"],"K"=>[18,"3e"],"L"=>[77,"3i"],"M"=>[18,"3i"],"N"=>[77,"3o"],"O"=>[18,"3o"],"P"=>[77,"3s"],"Q"=>[18,"3s"],"R"=>[77,"3t"],"S"=>[18,"3t"],"T"=>[0,"3\x20"],"U"=>[0,"3\x25"],"V"=>[0,"3-"],"W"=>[0,"3."],"X"=>[0,"3\x2F"],"Y"=>[0,"33"],"Z"=>[0,"34"],"\x5B"=>[0,"35"],"\x5C"=>[0,"36"],"\x5D"=>[0,"37"],"\x5E"=>[0,"38"],"_"=>[0,"39"],"\x60"=>[0,"3\x3D"],"a"=>[0,"3A"],"b"=>[0,"3_"],"c"=>[0,"3b"],"d"=>[0,"3d"],"e"=>[0,"3f"],"f"=>[0,"3g"],"g"=>[0,"3h"],"h"=>[0,"3l"],"i"=>[0,"3m"],"j"=>[0,"3n"],"k"=>[0,"3p"],"l"=>[0,"3r"],"m"=>[0,"3u"],"n"=>[100,"3"],"o"=>[110,"3"],"p"=>[111,"3"],"q"=>[115,"3"],"r"=>[116,"3"],"s"=>[118,"3"],"t"=>[119,"3"],"u"=>[122,"3"],"v"=>[123,"3"],"w"=>[125,"3"],"x"=>[126,"3"],"y"=>[129,"3"],"z"=>[143,"3"],"\x7B"=>[148,"3"],"\x7C"=>[151,"3"],"\x7D"=>[153,"3"],"~"=>[83,"3"],"\x7F"=>[10,"3"],"\x80"=>[77,"40"],"\x81"=>[18,"40"],"\x82"=>[77,"41"],"\x83"=>[18,"41"],"\x84"=>[77,"42"],"\x85"=>[18,"42"],"\x86"=>[77,"4a"],"\x87"=>[18,"4a"],"\x88"=>[77,"4c"],"\x89"=>[18,"4c"],"\x8A"=>[77,"4e"],"\x8B"=>[18,"4e"],"\x8C"=>[77,"4i"],"\x8D"=>[18,"4i"],"\x8E"=>[77,"4o"],"\x8F"=>[18,"4o"],"\x90"=>[77,"4s"],"\x91"=>[18,"4s"],"\x92"=>[77,"4t"],"\x93"=>[18,"4t"],"\x94"=>[0,"4\x20"],"\x95"=>[0,"4\x25"],"\x96"=>[0,"4-"],"\x97"=>[0,"4."],"\x98"=>[0,"4\x2F"],"\x99"=>[0,"43"],"\x9A"=>[0,"44"],"\x9B"=>[0,"45"],"\x9C"=>[0,"46"],"\x9D"=>[0,"47"],"\x9E"=>[0,"48"],"\x9F"=>[0,"49"],"\xA0"=>[0,"4\x3D"],"\xA1"=>[0,"4A"],"\xA2"=>[0,"4_"],"\xA3"=>[0,"4b"],"\xA4"=>[0,"4d"],"\xA5"=>[0,"4f"],"\xA6"=>[0,"4g"],"\xA7"=>[0,"4h"],"\xA8"=>[0,"4l"],"\xA9"=>[0,"4m"],"\xAA"=>[0,"4n"],"\xAB"=>[0,"4p"],"\xAC"=>[0,"4r"],"\xAD"=>[0,"4u"],"\xAE"=>[100,"4"],"\xAF"=>[110,"4"],"\xB0"=>[111,"4"],"\xB1"=>[115,"4"],"\xB2"=>[116,"4"],"\xB3"=>[118,"4"],"\xB4"=>[119,"4"],"\xB5"=>[122,"4"],"\xB6"=>[123,"4"],"\xB7"=>[125,"4"],"\xB8"=>[126,"4"],"\xB9"=>[129,"4"],"\xBA"=>[143,"4"],"\xBB"=>[148,"4"],"\xBC"=>[151,"4"],"\xBD"=>[153,"4"],"\xBE"=>[83,"4"],"\xBF"=>[10,"4"],"\xC0"=>[77,"50"],"\xC1"=>[18,"50"],"\xC2"=>[77,"51"],"\xC3"=>[18,"51"],"\xC4"=>[77,"52"],"\xC5"=>[18,"52"],"\xC6"=>[77,"5a"],"\xC7"=>[18,"5a"],"\xC8"=>[77,"5c"],"\xC9"=>[18,"5c"],"\xCA"=>[77,"5e"],"\xCB"=>[18,"5e"],"\xCC"=>[77,"5i"],"\xCD"=>[18,"5i"],"\xCE"=>[77,"5o"],"\xCF"=>[18,"5o"],"\xD0"=>[77,"5s"],"\xD1"=>[18,"5s"],"\xD2"=>[77,"5t"],"\xD3"=>[18,"5t"],"\xD4"=>[0,"5\x20"],"\xD5"=>[0,"5\x25"],"\xD6"=>[0,"5-"],"\xD7"=>[0,"5."],"\xD8"=>[0,"5\x2F"],"\xD9"=>[0,"53"],"\xDA"=>[0,"54"],"\xDB"=>[0,"55"],"\xDC"=>[0,"56"],"\xDD"=>[0,"57"],"\xDE"=>[0,"58"],"\xDF"=>[0,"59"],"\xE0"=>[0,"5\x3D"],"\xE1"=>[0,"5A"],"\xE2"=>[0,"5_"],"\xE3"=>[0,"5b"],"\xE4"=>[0,"5d"],"\xE5"=>[0,"5f"],"\xE6"=>[0,"5g"],"\xE7"=>[0,"5h"],"\xE8"=>[0,"5l"],"\xE9"=>[0,"5m"],"\xEA"=>[0,"5n"],"\xEB"=>[0,"5p"],"\xEC"=>[0,"5r"],"\xED"=>[0,"5u"],"\xEE"=>[100,"5"],"\xEF"=>[110,"5"],"\xF0"=>[111,"5"],"\xF1"=>[115,"5"],"\xF2"=>[116,"5"],"\xF3"=>[118,"5"],"\xF4"=>[119,"5"],"\xF5"=>[122,"5"],"\xF6"=>[123,"5"],"\xF7"=>[125,"5"],"\xF8"=>[126,"5"],"\xF9"=>[129,"5"],"\xFA"=>[143,"5"],"\xFB"=>[148,"5"],"\xFC"=>[151,"5"],"\xFD"=>[153,"5"],"\xFE"=>[83,"5"],"\xFF"=>[10,"5"],],["\x00"=>[0,"\x2F0"],"\x01"=>[0,"\x2F1"],"\x02"=>[0,"\x2F2"],"\x03"=>[0,"\x2Fa"],"\x04"=>[0,"\x2Fc"],"\x05"=>[0,"\x2Fe"],"\x06"=>[0,"\x2Fi"],"\x07"=>[0,"\x2Fo"],"\x08"=>[0,"\x2Fs"],"\x09"=>[0,"\x2Ft"],"\x0A"=>[73,"\x2F"],"\x0B"=>[88,"\x2F"],"\x0C"=>[89,"\x2F"],"\x0D"=>[96,"\x2F"],"\x0E"=>[97,"\x2F"],"\x0F"=>[99,"\x2F"],"\x10"=>[106,"\x2F"],"\x11"=>[136,"\x2F"],"\x12"=>[139,"\x2F"],"\x13"=>[141,"\x2F"],"\x14"=>[145,"\x2F"],"\x15"=>[147,"\x2F"],"\x16"=>[149,"\x2F"],"\x17"=>[101,"\x2F"],"\x18"=>[112,"\x2F"],"\x19"=>[117,"\x2F"],"\x1A"=>[120,"\x2F"],"\x1B"=>[124,"\x2F"],"\x1C"=>[127,"\x2F"],"\x1D"=>[144,"\x2F"],"\x1E"=>[152,"\x2F"],"\x1F"=>[11,"\x2F"],"\x20"=>[0,"30"],"\x21"=>[0,"31"],"\x22"=>[0,"32"],"\x23"=>[0,"3a"],"\x24"=>[0,"3c"],"\x25"=>[0,"3e"],"\x26"=>[0,"3i"],"\x27"=>[0,"3o"],"\x28"=>[0,"3s"],"\x29"=>[0,"3t"],"\x2A"=>[73,"3"],"\x2B"=>[88,"3"],"\x2C"=>[89,"3"],"-"=>[96,"3"],"."=>[97,"3"],"\x2F"=>[99,"3"],[106,"3"],[136,"3"],[139,"3"],[141,"3"],[145,"3"],[147,"3"],[149,"3"],[101,"3"],[112,"3"],[117,"3"],"\x3A"=>[120,"3"],"\x3B"=>[124,"3"],"\x3C"=>[127,"3"],"\x3D"=>[144,"3"],"\x3E"=>[152,"3"],"\x3F"=>[11,"3"],"\x40"=>[0,"40"],"A"=>[0,"41"],"B"=>[0,"42"],"C"=>[0,"4a"],"D"=>[0,"4c"],"E"=>[0,"4e"],"F"=>[0,"4i"],"G"=>[0,"4o"],"H"=>[0,"4s"],"I"=>[0,"4t"],"J"=>[73,"4"],"K"=>[88,"4"],"L"=>[89,"4"],"M"=>[96,"4"],"N"=>[97,"4"],"O"=>[99,"4"],"P"=>[106,"4"],"Q"=>[136,"4"],"R"=>[139,"4"],"S"=>[141,"4"],"T"=>[145,"4"],"U"=>[147,"4"],"V"=>[149,"4"],"W"=>[101,"4"],"X"=>[112,"4"],"Y"=>[117,"4"],"Z"=>[120,"4"],"\x5B"=>[124,"4"],"\x5C"=>[127,"4"],"\x5D"=>[144,"4"],"\x5E"=>[152,"4"],"_"=>[11,"4"],"\x60"=>[0,"50"],"a"=>[0,"51"],"b"=>[0,"52"],"c"=>[0,"5a"],"d"=>[0,"5c"],"e"=>[0,"5e"],"f"=>[0,"5i"],"g"=>[0,"5o"],"h"=>[0,"5s"],"i"=>[0,"5t"],"j"=>[73,"5"],"k"=>[88,"5"],"l"=>[89,"5"],"m"=>[96,"5"],"n"=>[97,"5"],"o"=>[99,"5"],"p"=>[106,"5"],"q"=>[136,"5"],"r"=>[139,"5"],"s"=>[141,"5"],"t"=>[145,"5"],"u"=>[147,"5"],"v"=>[149,"5"],"w"=>[101,"5"],"x"=>[112,"5"],"y"=>[117,"5"],"z"=>[120,"5"],"\x7B"=>[124,"5"],"\x7C"=>[127,"5"],"\x7D"=>[144,"5"],"~"=>[152,"5"],"\x7F"=>[11,"5"],"\x80"=>[0,"60"],"\x81"=>[0,"61"],"\x82"=>[0,"62"],"\x83"=>[0,"6a"],"\x84"=>[0,"6c"],"\x85"=>[0,"6e"],"\x86"=>[0,"6i"],"\x87"=>[0,"6o"],"\x88"=>[0,"6s"],"\x89"=>[0,"6t"],"\x8A"=>[73,"6"],"\x8B"=>[88,"6"],"\x8C"=>[89,"6"],"\x8D"=>[96,"6"],"\x8E"=>[97,"6"],"\x8F"=>[99,"6"],"\x90"=>[106,"6"],"\x91"=>[136,"6"],"\x92"=>[139,"6"],"\x93"=>[141,"6"],"\x94"=>[145,"6"],"\x95"=>[147,"6"],"\x96"=>[149,"6"],"\x97"=>[101,"6"],"\x98"=>[112,"6"],"\x99"=>[117,"6"],"\x9A"=>[120,"6"],"\x9B"=>[124,"6"],"\x9C"=>[127,"6"],"\x9D"=>[144,"6"],"\x9E"=>[152,"6"],"\x9F"=>[11,"6"],"\xA0"=>[0,"70"],"\xA1"=>[0,"71"],"\xA2"=>[0,"72"],"\xA3"=>[0,"7a"],"\xA4"=>[0,"7c"],"\xA5"=>[0,"7e"],"\xA6"=>[0,"7i"],"\xA7"=>[0,"7o"],"\xA8"=>[0,"7s"],"\xA9"=>[0,"7t"],"\xAA"=>[73,"7"],"\xAB"=>[88,"7"],"\xAC"=>[89,"7"],"\xAD"=>[96,"7"],"\xAE"=>[97,"7"],"\xAF"=>[99,"7"],"\xB0"=>[106,"7"],"\xB1"=>[136,"7"],"\xB2"=>[139,"7"],"\xB3"=>[141,"7"],"\xB4"=>[145,"7"],"\xB5"=>[147,"7"],"\xB6"=>[149,"7"],"\xB7"=>[101,"7"],"\xB8"=>[112,"7"],"\xB9"=>[117,"7"],"\xBA"=>[120,"7"],"\xBB"=>[124,"7"],"\xBC"=>[127,"7"],"\xBD"=>[144,"7"],"\xBE"=>[152,"7"],"\xBF"=>[11,"7"],"\xC0"=>[0,"80"],"\xC1"=>[0,"81"],"\xC2"=>[0,"82"],"\xC3"=>[0,"8a"],"\xC4"=>[0,"8c"],"\xC5"=>[0,"8e"],"\xC6"=>[0,"8i"],"\xC7"=>[0,"8o"],"\xC8"=>[0,"8s"],"\xC9"=>[0,"8t"],"\xCA"=>[73,"8"],"\xCB"=>[88,"8"],"\xCC"=>[89,"8"],"\xCD"=>[96,"8"],"\xCE"=>[97,"8"],"\xCF"=>[99,"8"],"\xD0"=>[106,"8"],"\xD1"=>[136,"8"],"\xD2"=>[139,"8"],"\xD3"=>[141,"8"],"\xD4"=>[145,"8"],"\xD5"=>[147,"8"],"\xD6"=>[149,"8"],"\xD7"=>[101,"8"],"\xD8"=>[112,"8"],"\xD9"=>[117,"8"],"\xDA"=>[120,"8"],"\xDB"=>[124,"8"],"\xDC"=>[127,"8"],"\xDD"=>[144,"8"],"\xDE"=>[152,"8"],"\xDF"=>[11,"8"],"\xE0"=>[0,"90"],"\xE1"=>[0,"91"],"\xE2"=>[0,"92"],"\xE3"=>[0,"9a"],"\xE4"=>[0,"9c"],"\xE5"=>[0,"9e"],"\xE6"=>[0,"9i"],"\xE7"=>[0,"9o"],"\xE8"=>[0,"9s"],"\xE9"=>[0,"9t"],"\xEA"=>[73,"9"],"\xEB"=>[88,"9"],"\xEC"=>[89,"9"],"\xED"=>[96,"9"],"\xEE"=>[97,"9"],"\xEF"=>[99,"9"],"\xF0"=>[106,"9"],"\xF1"=>[136,"9"],"\xF2"=>[139,"9"],"\xF3"=>[141,"9"],"\xF4"=>[145,"9"],"\xF5"=>[147,"9"],"\xF6"=>[149,"9"],"\xF7"=>[101,"9"],"\xF8"=>[112,"9"],"\xF9"=>[117,"9"],"\xFA"=>[120,"9"],"\xFB"=>[124,"9"],"\xFC"=>[127,"9"],"\xFD"=>[144,"9"],"\xFE"=>[152,"9"],"\xFF"=>[11,"9"],],["\x00"=>[94,"00"],"\x01"=>[76,"00"],"\x02"=>[104,"00"],"\x03"=>[16,"00"],"\x04"=>[94,"01"],"\x05"=>[76,"01"],"\x06"=>[104,"01"],"\x07"=>[16,"01"],"\x08"=>[94,"02"],"\x09"=>[76,"02"],"\x0A"=>[104,"02"],"\x0B"=>[16,"02"],"\x0C"=>[94,"0a"],"\x0D"=>[76,"0a"],"\x0E"=>[104,"0a"],"\x0F"=>[16,"0a"],"\x10"=>[94,"0c"],"\x11"=>[76,"0c"],"\x12"=>[104,"0c"],"\x13"=>[16,"0c"],"\x14"=>[94,"0e"],"\x15"=>[76,"0e"],"\x16"=>[104,"0e"],"\x17"=>[16,"0e"],"\x18"=>[94,"0i"],"\x19"=>[76,"0i"],"\x1A"=>[104,"0i"],"\x1B"=>[16,"0i"],"\x1C"=>[94,"0o"],"\x1D"=>[76,"0o"],"\x1E"=>[104,"0o"],"\x1F"=>[16,"0o"],"\x20"=>[94,"0s"],"\x21"=>[76,"0s"],"\x22"=>[104,"0s"],"\x23"=>[16,"0s"],"\x24"=>[94,"0t"],"\x25"=>[76,"0t"],"\x26"=>[104,"0t"],"\x27"=>[16,"0t"],"\x28"=>[77,"0\x20"],"\x29"=>[18,"0\x20"],"\x2A"=>[77,"0\x25"],"\x2B"=>[18,"0\x25"],"\x2C"=>[77,"0-"],"-"=>[18,"0-"],"."=>[77,"0."],"\x2F"=>[18,"0."],[77,"0\x2F"],[18,"0\x2F"],[77,"03"],[18,"03"],[77,"04"],[18,"04"],[77,"05"],[18,"05"],[77,"06"],[18,"06"],"\x3A"=>[77,"07"],"\x3B"=>[18,"07"],"\x3C"=>[77,"08"],"\x3D"=>[18,"08"],"\x3E"=>[77,"09"],"\x3F"=>[18,"09"],"\x40"=>[77,"0\x3D"],"A"=>[18,"0\x3D"],"B"=>[77,"0A"],"C"=>[18,"0A"],"D"=>[77,"0_"],"E"=>[18,"0_"],"F"=>[77,"0b"],"G"=>[18,"0b"],"H"=>[77,"0d"],"I"=>[18,"0d"],"J"=>[77,"0f"],"K"=>[18,"0f"],"L"=>[77,"0g"],"M"=>[18,"0g"],"N"=>[77,"0h"],"O"=>[18,"0h"],"P"=>[77,"0l"],"Q"=>[18,"0l"],"R"=>[77,"0m"],"S"=>[18,"0m"],"T"=>[77,"0n"],"U"=>[18,"0n"],"V"=>[77,"0p"],"W"=>[18,"0p"],"X"=>[77,"0r"],"Y"=>[18,"0r"],"Z"=>[77,"0u"],"\x5B"=>[18,"0u"],"\x5C"=>[0,"0\x3A"],"\x5D"=>[0,"0B"],"\x5E"=>[0,"0C"],"_"=>[0,"0D"],"\x60"=>[0,"0E"],"a"=>[0,"0F"],"b"=>[0,"0G"],"c"=>[0,"0H"],"d"=>[0,"0I"],"e"=>[0,"0J"],"f"=>[0,"0K"],"g"=>[0,"0L"],"h"=>[0,"0M"],"i"=>[0,"0N"],"j"=>[0,"0O"],"k"=>[0,"0P"],"l"=>[0,"0Q"],"m"=>[0,"0R"],"n"=>[0,"0S"],"o"=>[0,"0T"],"p"=>[0,"0U"],"q"=>[0,"0V"],"r"=>[0,"0W"],"s"=>[0,"0Y"],"t"=>[0,"0j"],"u"=>[0,"0k"],"v"=>[0,"0q"],"w"=>[0,"0v"],"x"=>[0,"0w"],"y"=>[0,"0x"],"z"=>[0,"0y"],"\x7B"=>[0,"0z"],"\x7C"=>[82,"0"],"\x7D"=>[87,"0"],"~"=>[130,"0"],"\x7F"=>[9,"0"],"\x80"=>[94,"10"],"\x81"=>[76,"10"],"\x82"=>[104,"10"],"\x83"=>[16,"10"],"\x84"=>[94,"11"],"\x85"=>[76,"11"],"\x86"=>[104,"11"],"\x87"=>[16,"11"],"\x88"=>[94,"12"],"\x89"=>[76,"12"],"\x8A"=>[104,"12"],"\x8B"=>[16,"12"],"\x8C"=>[94,"1a"],"\x8D"=>[76,"1a"],"\x8E"=>[104,"1a"],"\x8F"=>[16,"1a"],"\x90"=>[94,"1c"],"\x91"=>[76,"1c"],"\x92"=>[104,"1c"],"\x93"=>[16,"1c"],"\x94"=>[94,"1e"],"\x95"=>[76,"1e"],"\x96"=>[104,"1e"],"\x97"=>[16,"1e"],"\x98"=>[94,"1i"],"\x99"=>[76,"1i"],"\x9A"=>[104,"1i"],"\x9B"=>[16,"1i"],"\x9C"=>[94,"1o"],"\x9D"=>[76,"1o"],"\x9E"=>[104,"1o"],"\x9F"=>[16,"1o"],"\xA0"=>[94,"1s"],"\xA1"=>[76,"1s"],"\xA2"=>[104,"1s"],"\xA3"=>[16,"1s"],"\xA4"=>[94,"1t"],"\xA5"=>[76,"1t"],"\xA6"=>[104,"1t"],"\xA7"=>[16,"1t"],"\xA8"=>[77,"1\x20"],"\xA9"=>[18,"1\x20"],"\xAA"=>[77,"1\x25"],"\xAB"=>[18,"1\x25"],"\xAC"=>[77,"1-"],"\xAD"=>[18,"1-"],"\xAE"=>[77,"1."],"\xAF"=>[18,"1."],"\xB0"=>[77,"1\x2F"],"\xB1"=>[18,"1\x2F"],"\xB2"=>[77,"13"],"\xB3"=>[18,"13"],"\xB4"=>[77,"14"],"\xB5"=>[18,"14"],"\xB6"=>[77,"15"],"\xB7"=>[18,"15"],"\xB8"=>[77,"16"],"\xB9"=>[18,"16"],"\xBA"=>[77,"17"],"\xBB"=>[18,"17"],"\xBC"=>[77,"18"],"\xBD"=>[18,"18"],"\xBE"=>[77,"19"],"\xBF"=>[18,"19"],"\xC0"=>[77,"1\x3D"],"\xC1"=>[18,"1\x3D"],"\xC2"=>[77,"1A"],"\xC3"=>[18,"1A"],"\xC4"=>[77,"1_"],"\xC5"=>[18,"1_"],"\xC6"=>[77,"1b"],"\xC7"=>[18,"1b"],"\xC8"=>[77,"1d"],"\xC9"=>[18,"1d"],"\xCA"=>[77,"1f"],"\xCB"=>[18,"1f"],"\xCC"=>[77,"1g"],"\xCD"=>[18,"1g"],"\xCE"=>[77,"1h"],"\xCF"=>[18,"1h"],"\xD0"=>[77,"1l"],"\xD1"=>[18,"1l"],"\xD2"=>[77,"1m"],"\xD3"=>[18,"1m"],"\xD4"=>[77,"1n"],"\xD5"=>[18,"1n"],"\xD6"=>[77,"1p"],"\xD7"=>[18,"1p"],"\xD8"=>[77,"1r"],"\xD9"=>[18,"1r"],"\xDA"=>[77,"1u"],"\xDB"=>[18,"1u"],"\xDC"=>[0,"1\x3A"],"\xDD"=>[0,"1B"],"\xDE"=>[0,"1C"],"\xDF"=>[0,"1D"],"\xE0"=>[0,"1E"],"\xE1"=>[0,"1F"],"\xE2"=>[0,"1G"],"\xE3"=>[0,"1H"],"\xE4"=>[0,"1I"],"\xE5"=>[0,"1J"],"\xE6"=>[0,"1K"],"\xE7"=>[0,"1L"],"\xE8"=>[0,"1M"],"\xE9"=>[0,"1N"],"\xEA"=>[0,"1O"],"\xEB"=>[0,"1P"],"\xEC"=>[0,"1Q"],"\xED"=>[0,"1R"],"\xEE"=>[0,"1S"],"\xEF"=>[0,"1T"],"\xF0"=>[0,"1U"],"\xF1"=>[0,"1V"],"\xF2"=>[0,"1W"],"\xF3"=>[0,"1Y"],"\xF4"=>[0,"1j"],"\xF5"=>[0,"1k"],"\xF6"=>[0,"1q"],"\xF7"=>[0,"1v"],"\xF8"=>[0,"1w"],"\xF9"=>[0,"1x"],"\xFA"=>[0,"1y"],"\xFB"=>[0,"1z"],"\xFC"=>[82,"1"],"\xFD"=>[87,"1"],"\xFE"=>[130,"1"],"\xFF"=>[9,"1"],],["\x00"=>[77,"00"],"\x01"=>[18,"00"],"\x02"=>[77,"01"],"\x03"=>[18,"01"],"\x04"=>[77,"02"],"\x05"=>[18,"02"],"\x06"=>[77,"0a"],"\x07"=>[18,"0a"],"\x08"=>[77,"0c"],"\x09"=>[18,"0c"],"\x0A"=>[77,"0e"],"\x0B"=>[18,"0e"],"\x0C"=>[77,"0i"],"\x0D"=>[18,"0i"],"\x0E"=>[77,"0o"],"\x0F"=>[18,"0o"],"\x10"=>[77,"0s"],"\x11"=>[18,"0s"],"\x12"=>[77,"0t"],"\x13"=>[18,"0t"],"\x14"=>[0,"0\x20"],"\x15"=>[0,"0\x25"],"\x16"=>[0,"0-"],"\x17"=>[0,"0."],"\x18"=>[0,"0\x2F"],"\x19"=>[0,"03"],"\x1A"=>[0,"04"],"\x1B"=>[0,"05"],"\x1C"=>[0,"06"],"\x1D"=>[0,"07"],"\x1E"=>[0,"08"],"\x1F"=>[0,"09"],"\x20"=>[0,"0\x3D"],"\x21"=>[0,"0A"],"\x22"=>[0,"0_"],"\x23"=>[0,"0b"],"\x24"=>[0,"0d"],"\x25"=>[0,"0f"],"\x26"=>[0,"0g"],"\x27"=>[0,"0h"],"\x28"=>[0,"0l"],"\x29"=>[0,"0m"],"\x2A"=>[0,"0n"],"\x2B"=>[0,"0p"],"\x2C"=>[0,"0r"],"-"=>[0,"0u"],"."=>[100,"0"],"\x2F"=>[110,"0"],[111,"0"],[115,"0"],[116,"0"],[118,"0"],[119,"0"],[122,"0"],[123,"0"],[125,"0"],[126,"0"],[129,"0"],"\x3A"=>[143,"0"],"\x3B"=>[148,"0"],"\x3C"=>[151,"0"],"\x3D"=>[153,"0"],"\x3E"=>[83,"0"],"\x3F"=>[10,"0"],"\x40"=>[77,"10"],"A"=>[18,"10"],"B"=>[77,"11"],"C"=>[18,"11"],"D"=>[77,"12"],"E"=>[18,"12"],"F"=>[77,"1a"],"G"=>[18,"1a"],"H"=>[77,"1c"],"I"=>[18,"1c"],"J"=>[77,"1e"],"K"=>[18,"1e"],"L"=>[77,"1i"],"M"=>[18,"1i"],"N"=>[77,"1o"],"O"=>[18,"1o"],"P"=>[77,"1s"],"Q"=>[18,"1s"],"R"=>[77,"1t"],"S"=>[18,"1t"],"T"=>[0,"1\x20"],"U"=>[0,"1\x25"],"V"=>[0,"1-"],"W"=>[0,"1."],"X"=>[0,"1\x2F"],"Y"=>[0,"13"],"Z"=>[0,"14"],"\x5B"=>[0,"15"],"\x5C"=>[0,"16"],"\x5D"=>[0,"17"],"\x5E"=>[0,"18"],"_"=>[0,"19"],"\x60"=>[0,"1\x3D"],"a"=>[0,"1A"],"b"=>[0,"1_"],"c"=>[0,"1b"],"d"=>[0,"1d"],"e"=>[0,"1f"],"f"=>[0,"1g"],"g"=>[0,"1h"],"h"=>[0,"1l"],"i"=>[0,"1m"],"j"=>[0,"1n"],"k"=>[0,"1p"],"l"=>[0,"1r"],"m"=>[0,"1u"],"n"=>[100,"1"],"o"=>[110,"1"],"p"=>[111,"1"],"q"=>[115,"1"],"r"=>[116,"1"],"s"=>[118,"1"],"t"=>[119,"1"],"u"=>[122,"1"],"v"=>[123,"1"],"w"=>[125,"1"],"x"=>[126,"1"],"y"=>[129,"1"],"z"=>[143,"1"],"\x7B"=>[148,"1"],"\x7C"=>[151,"1"],"\x7D"=>[153,"1"],"~"=>[83,"1"],"\x7F"=>[10,"1"],"\x80"=>[77,"20"],"\x81"=>[18,"20"],"\x82"=>[77,"21"],"\x83"=>[18,"21"],"\x84"=>[77,"22"],"\x85"=>[18,"22"],"\x86"=>[77,"2a"],"\x87"=>[18,"2a"],"\x88"=>[77,"2c"],"\x89"=>[18,"2c"],"\x8A"=>[77,"2e"],"\x8B"=>[18,"2e"],"\x8C"=>[77,"2i"],"\x8D"=>[18,"2i"],"\x8E"=>[77,"2o"],"\x8F"=>[18,"2o"],"\x90"=>[77,"2s"],"\x91"=>[18,"2s"],"\x92"=>[77,"2t"],"\x93"=>[18,"2t"],"\x94"=>[0,"2\x20"],"\x95"=>[0,"2\x25"],"\x96"=>[0,"2-"],"\x97"=>[0,"2."],"\x98"=>[0,"2\x2F"],"\x99"=>[0,"23"],"\x9A"=>[0,"24"],"\x9B"=>[0,"25"],"\x9C"=>[0,"26"],"\x9D"=>[0,"27"],"\x9E"=>[0,"28"],"\x9F"=>[0,"29"],"\xA0"=>[0,"2\x3D"],"\xA1"=>[0,"2A"],"\xA2"=>[0,"2_"],"\xA3"=>[0,"2b"],"\xA4"=>[0,"2d"],"\xA5"=>[0,"2f"],"\xA6"=>[0,"2g"],"\xA7"=>[0,"2h"],"\xA8"=>[0,"2l"],"\xA9"=>[0,"2m"],"\xAA"=>[0,"2n"],"\xAB"=>[0,"2p"],"\xAC"=>[0,"2r"],"\xAD"=>[0,"2u"],"\xAE"=>[100,"2"],"\xAF"=>[110,"2"],"\xB0"=>[111,"2"],"\xB1"=>[115,"2"],"\xB2"=>[116,"2"],"\xB3"=>[118,"2"],"\xB4"=>[119,"2"],"\xB5"=>[122,"2"],"\xB6"=>[123,"2"],"\xB7"=>[125,"2"],"\xB8"=>[126,"2"],"\xB9"=>[129,"2"],"\xBA"=>[143,"2"],"\xBB"=>[148,"2"],"\xBC"=>[151,"2"],"\xBD"=>[153,"2"],"\xBE"=>[83,"2"],"\xBF"=>[10,"2"],"\xC0"=>[77,"a0"],"\xC1"=>[18,"a0"],"\xC2"=>[77,"a1"],"\xC3"=>[18,"a1"],"\xC4"=>[77,"a2"],"\xC5"=>[18,"a2"],"\xC6"=>[77,"aa"],"\xC7"=>[18,"aa"],"\xC8"=>[77,"ac"],"\xC9"=>[18,"ac"],"\xCA"=>[77,"ae"],"\xCB"=>[18,"ae"],"\xCC"=>[77,"ai"],"\xCD"=>[18,"ai"],"\xCE"=>[77,"ao"],"\xCF"=>[18,"ao"],"\xD0"=>[77,"as"],"\xD1"=>[18,"as"],"\xD2"=>[77,"at"],"\xD3"=>[18,"at"],"\xD4"=>[0,"a\x20"],"\xD5"=>[0,"a\x25"],"\xD6"=>[0,"a-"],"\xD7"=>[0,"a."],"\xD8"=>[0,"a\x2F"],"\xD9"=>[0,"a3"],"\xDA"=>[0,"a4"],"\xDB"=>[0,"a5"],"\xDC"=>[0,"a6"],"\xDD"=>[0,"a7"],"\xDE"=>[0,"a8"],"\xDF"=>[0,"a9"],"\xE0"=>[0,"a\x3D"],"\xE1"=>[0,"aA"],"\xE2"=>[0,"a_"],"\xE3"=>[0,"ab"],"\xE4"=>[0,"ad"],"\xE5"=>[0,"af"],"\xE6"=>[0,"ag"],"\xE7"=>[0,"ah"],"\xE8"=>[0,"al"],"\xE9"=>[0,"am"],"\xEA"=>[0,"an"],"\xEB"=>[0,"ap"],"\xEC"=>[0,"ar"],"\xED"=>[0,"au"],"\xEE"=>[100,"a"],"\xEF"=>[110,"a"],"\xF0"=>[111,"a"],"\xF1"=>[115,"a"],"\xF2"=>[116,"a"],"\xF3"=>[118,"a"],"\xF4"=>[119,"a"],"\xF5"=>[122,"a"],"\xF6"=>[123,"a"],"\xF7"=>[125,"a"],"\xF8"=>[126,"a"],"\xF9"=>[129,"a"],"\xFA"=>[143,"a"],"\xFB"=>[148,"a"],"\xFC"=>[151,"a"],"\xFD"=>[153,"a"],"\xFE"=>[83,"a"],"\xFF"=>[10,"a"],],["\x00"=>[0,"00"],"\x01"=>[0,"01"],"\x02"=>[0,"02"],"\x03"=>[0,"0a"],"\x04"=>[0,"0c"],"\x05"=>[0,"0e"],"\x06"=>[0,"0i"],"\x07"=>[0,"0o"],"\x08"=>[0,"0s"],"\x09"=>[0,"0t"],"\x0A"=>[73,"0"],"\x0B"=>[88,"0"],"\x0C"=>[89,"0"],"\x0D"=>[96,"0"],"\x0E"=>[97,"0"],"\x0F"=>[99,"0"],"\x10"=>[106,"0"],"\x11"=>[136,"0"],"\x12"=>[139,"0"],"\x13"=>[141,"0"],"\x14"=>[145,"0"],"\x15"=>[147,"0"],"\x16"=>[149,"0"],"\x17"=>[101,"0"],"\x18"=>[112,"0"],"\x19"=>[117,"0"],"\x1A"=>[120,"0"],"\x1B"=>[124,"0"],"\x1C"=>[127,"0"],"\x1D"=>[144,"0"],"\x1E"=>[152,"0"],"\x1F"=>[11,"0"],"\x20"=>[0,"10"],"\x21"=>[0,"11"],"\x22"=>[0,"12"],"\x23"=>[0,"1a"],"\x24"=>[0,"1c"],"\x25"=>[0,"1e"],"\x26"=>[0,"1i"],"\x27"=>[0,"1o"],"\x28"=>[0,"1s"],"\x29"=>[0,"1t"],"\x2A"=>[73,"1"],"\x2B"=>[88,"1"],"\x2C"=>[89,"1"],"-"=>[96,"1"],"."=>[97,"1"],"\x2F"=>[99,"1"],[106,"1"],[136,"1"],[139,"1"],[141,"1"],[145,"1"],[147,"1"],[149,"1"],[101,"1"],[112,"1"],[117,"1"],"\x3A"=>[120,"1"],"\x3B"=>[124,"1"],"\x3C"=>[127,"1"],"\x3D"=>[144,"1"],"\x3E"=>[152,"1"],"\x3F"=>[11,"1"],"\x40"=>[0,"20"],"A"=>[0,"21"],"B"=>[0,"22"],"C"=>[0,"2a"],"D"=>[0,"2c"],"E"=>[0,"2e"],"F"=>[0,"2i"],"G"=>[0,"2o"],"H"=>[0,"2s"],"I"=>[0,"2t"],"J"=>[73,"2"],"K"=>[88,"2"],"L"=>[89,"2"],"M"=>[96,"2"],"N"=>[97,"2"],"O"=>[99,"2"],"P"=>[106,"2"],"Q"=>[136,"2"],"R"=>[139,"2"],"S"=>[141,"2"],"T"=>[145,"2"],"U"=>[147,"2"],"V"=>[149,"2"],"W"=>[101,"2"],"X"=>[112,"2"],"Y"=>[117,"2"],"Z"=>[120,"2"],"\x5B"=>[124,"2"],"\x5C"=>[127,"2"],"\x5D"=>[144,"2"],"\x5E"=>[152,"2"],"_"=>[11,"2"],"\x60"=>[0,"a0"],"a"=>[0,"a1"],"b"=>[0,"a2"],"c"=>[0,"aa"],"d"=>[0,"ac"],"e"=>[0,"ae"],"f"=>[0,"ai"],"g"=>[0,"ao"],"h"=>[0,"as"],"i"=>[0,"at"],"j"=>[73,"a"],"k"=>[88,"a"],"l"=>[89,"a"],"m"=>[96,"a"],"n"=>[97,"a"],"o"=>[99,"a"],"p"=>[106,"a"],"q"=>[136,"a"],"r"=>[139,"a"],"s"=>[141,"a"],"t"=>[145,"a"],"u"=>[147,"a"],"v"=>[149,"a"],"w"=>[101,"a"],"x"=>[112,"a"],"y"=>[117,"a"],"z"=>[120,"a"],"\x7B"=>[124,"a"],"\x7C"=>[127,"a"],"\x7D"=>[144,"a"],"~"=>[152,"a"],"\x7F"=>[11,"a"],"\x80"=>[0,"c0"],"\x81"=>[0,"c1"],"\x82"=>[0,"c2"],"\x83"=>[0,"ca"],"\x84"=>[0,"cc"],"\x85"=>[0,"ce"],"\x86"=>[0,"ci"],"\x87"=>[0,"co"],"\x88"=>[0,"cs"],"\x89"=>[0,"ct"],"\x8A"=>[73,"c"],"\x8B"=>[88,"c"],"\x8C"=>[89,"c"],"\x8D"=>[96,"c"],"\x8E"=>[97,"c"],"\x8F"=>[99,"c"],"\x90"=>[106,"c"],"\x91"=>[136,"c"],"\x92"=>[139,"c"],"\x93"=>[141,"c"],"\x94"=>[145,"c"],"\x95"=>[147,"c"],"\x96"=>[149,"c"],"\x97"=>[101,"c"],"\x98"=>[112,"c"],"\x99"=>[117,"c"],"\x9A"=>[120,"c"],"\x9B"=>[124,"c"],"\x9C"=>[127,"c"],"\x9D"=>[144,"c"],"\x9E"=>[152,"c"],"\x9F"=>[11,"c"],"\xA0"=>[0,"e0"],"\xA1"=>[0,"e1"],"\xA2"=>[0,"e2"],"\xA3"=>[0,"ea"],"\xA4"=>[0,"ec"],"\xA5"=>[0,"ee"],"\xA6"=>[0,"ei"],"\xA7"=>[0,"eo"],"\xA8"=>[0,"es"],"\xA9"=>[0,"et"],"\xAA"=>[73,"e"],"\xAB"=>[88,"e"],"\xAC"=>[89,"e"],"\xAD"=>[96,"e"],"\xAE"=>[97,"e"],"\xAF"=>[99,"e"],"\xB0"=>[106,"e"],"\xB1"=>[136,"e"],"\xB2"=>[139,"e"],"\xB3"=>[141,"e"],"\xB4"=>[145,"e"],"\xB5"=>[147,"e"],"\xB6"=>[149,"e"],"\xB7"=>[101,"e"],"\xB8"=>[112,"e"],"\xB9"=>[117,"e"],"\xBA"=>[120,"e"],"\xBB"=>[124,"e"],"\xBC"=>[127,"e"],"\xBD"=>[144,"e"],"\xBE"=>[152,"e"],"\xBF"=>[11,"e"],"\xC0"=>[0,"i0"],"\xC1"=>[0,"i1"],"\xC2"=>[0,"i2"],"\xC3"=>[0,"ia"],"\xC4"=>[0,"ic"],"\xC5"=>[0,"ie"],"\xC6"=>[0,"ii"],"\xC7"=>[0,"io"],"\xC8"=>[0,"is"],"\xC9"=>[0,"it"],"\xCA"=>[73,"i"],"\xCB"=>[88,"i"],"\xCC"=>[89,"i"],"\xCD"=>[96,"i"],"\xCE"=>[97,"i"],"\xCF"=>[99,"i"],"\xD0"=>[106,"i"],"\xD1"=>[136,"i"],"\xD2"=>[139,"i"],"\xD3"=>[141,"i"],"\xD4"=>[145,"i"],"\xD5"=>[147,"i"],"\xD6"=>[149,"i"],"\xD7"=>[101,"i"],"\xD8"=>[112,"i"],"\xD9"=>[117,"i"],"\xDA"=>[120,"i"],"\xDB"=>[124,"i"],"\xDC"=>[127,"i"],"\xDD"=>[144,"i"],"\xDE"=>[152,"i"],"\xDF"=>[11,"i"],"\xE0"=>[0,"o0"],"\xE1"=>[0,"o1"],"\xE2"=>[0,"o2"],"\xE3"=>[0,"oa"],"\xE4"=>[0,"oc"],"\xE5"=>[0,"oe"],"\xE6"=>[0,"oi"],"\xE7"=>[0,"oo"],"\xE8"=>[0,"os"],"\xE9"=>[0,"ot"],"\xEA"=>[73,"o"],"\xEB"=>[88,"o"],"\xEC"=>[89,"o"],"\xED"=>[96,"o"],"\xEE"=>[97,"o"],"\xEF"=>[99,"o"],"\xF0"=>[106,"o"],"\xF1"=>[136,"o"],"\xF2"=>[139,"o"],"\xF3"=>[141,"o"],"\xF4"=>[145,"o"],"\xF5"=>[147,"o"],"\xF6"=>[149,"o"],"\xF7"=>[101,"o"],"\xF8"=>[112,"o"],"\xF9"=>[117,"o"],"\xFA"=>[120,"o"],"\xFB"=>[124,"o"],"\xFC"=>[127,"o"],"\xFD"=>[144,"o"],"\xFE"=>[152,"o"],"\xFF"=>[11,"o"],],["\x00"=>[94,"20"],"\x01"=>[76,"20"],"\x02"=>[104,"20"],"\x03"=>[16,"20"],"\x04"=>[94,"21"],"\x05"=>[76,"21"],"\x06"=>[104,"21"],"\x07"=>[16,"21"],"\x08"=>[94,"22"],"\x09"=>[76,"22"],"\x0A"=>[104,"22"],"\x0B"=>[16,"22"],"\x0C"=>[94,"2a"],"\x0D"=>[76,"2a"],"\x0E"=>[104,"2a"],"\x0F"=>[16,"2a"],"\x10"=>[94,"2c"],"\x11"=>[76,"2c"],"\x12"=>[104,"2c"],"\x13"=>[16,"2c"],"\x14"=>[94,"2e"],"\x15"=>[76,"2e"],"\x16"=>[104,"2e"],"\x17"=>[16,"2e"],"\x18"=>[94,"2i"],"\x19"=>[76,"2i"],"\x1A"=>[104,"2i"],"\x1B"=>[16,"2i"],"\x1C"=>[94,"2o"],"\x1D"=>[76,"2o"],"\x1E"=>[104,"2o"],"\x1F"=>[16,"2o"],"\x20"=>[94,"2s"],"\x21"=>[76,"2s"],"\x22"=>[104,"2s"],"\x23"=>[16,"2s"],"\x24"=>[94,"2t"],"\x25"=>[76,"2t"],"\x26"=>[104,"2t"],"\x27"=>[16,"2t"],"\x28"=>[77,"2\x20"],"\x29"=>[18,"2\x20"],"\x2A"=>[77,"2\x25"],"\x2B"=>[18,"2\x25"],"\x2C"=>[77,"2-"],"-"=>[18,"2-"],"."=>[77,"2."],"\x2F"=>[18,"2."],[77,"2\x2F"],[18,"2\x2F"],[77,"23"],[18,"23"],[77,"24"],[18,"24"],[77,"25"],[18,"25"],[77,"26"],[18,"26"],"\x3A"=>[77,"27"],"\x3B"=>[18,"27"],"\x3C"=>[77,"28"],"\x3D"=>[18,"28"],"\x3E"=>[77,"29"],"\x3F"=>[18,"29"],"\x40"=>[77,"2\x3D"],"A"=>[18,"2\x3D"],"B"=>[77,"2A"],"C"=>[18,"2A"],"D"=>[77,"2_"],"E"=>[18,"2_"],"F"=>[77,"2b"],"G"=>[18,"2b"],"H"=>[77,"2d"],"I"=>[18,"2d"],"J"=>[77,"2f"],"K"=>[18,"2f"],"L"=>[77,"2g"],"M"=>[18,"2g"],"N"=>[77,"2h"],"O"=>[18,"2h"],"P"=>[77,"2l"],"Q"=>[18,"2l"],"R"=>[77,"2m"],"S"=>[18,"2m"],"T"=>[77,"2n"],"U"=>[18,"2n"],"V"=>[77,"2p"],"W"=>[18,"2p"],"X"=>[77,"2r"],"Y"=>[18,"2r"],"Z"=>[77,"2u"],"\x5B"=>[18,"2u"],"\x5C"=>[0,"2\x3A"],"\x5D"=>[0,"2B"],"\x5E"=>[0,"2C"],"_"=>[0,"2D"],"\x60"=>[0,"2E"],"a"=>[0,"2F"],"b"=>[0,"2G"],"c"=>[0,"2H"],"d"=>[0,"2I"],"e"=>[0,"2J"],"f"=>[0,"2K"],"g"=>[0,"2L"],"h"=>[0,"2M"],"i"=>[0,"2N"],"j"=>[0,"2O"],"k"=>[0,"2P"],"l"=>[0,"2Q"],"m"=>[0,"2R"],"n"=>[0,"2S"],"o"=>[0,"2T"],"p"=>[0,"2U"],"q"=>[0,"2V"],"r"=>[0,"2W"],"s"=>[0,"2Y"],"t"=>[0,"2j"],"u"=>[0,"2k"],"v"=>[0,"2q"],"w"=>[0,"2v"],"x"=>[0,"2w"],"y"=>[0,"2x"],"z"=>[0,"2y"],"\x7B"=>[0,"2z"],"\x7C"=>[82,"2"],"\x7D"=>[87,"2"],"~"=>[130,"2"],"\x7F"=>[9,"2"],"\x80"=>[94,"a0"],"\x81"=>[76,"a0"],"\x82"=>[104,"a0"],"\x83"=>[16,"a0"],"\x84"=>[94,"a1"],"\x85"=>[76,"a1"],"\x86"=>[104,"a1"],"\x87"=>[16,"a1"],"\x88"=>[94,"a2"],"\x89"=>[76,"a2"],"\x8A"=>[104,"a2"],"\x8B"=>[16,"a2"],"\x8C"=>[94,"aa"],"\x8D"=>[76,"aa"],"\x8E"=>[104,"aa"],"\x8F"=>[16,"aa"],"\x90"=>[94,"ac"],"\x91"=>[76,"ac"],"\x92"=>[104,"ac"],"\x93"=>[16,"ac"],"\x94"=>[94,"ae"],"\x95"=>[76,"ae"],"\x96"=>[104,"ae"],"\x97"=>[16,"ae"],"\x98"=>[94,"ai"],"\x99"=>[76,"ai"],"\x9A"=>[104,"ai"],"\x9B"=>[16,"ai"],"\x9C"=>[94,"ao"],"\x9D"=>[76,"ao"],"\x9E"=>[104,"ao"],"\x9F"=>[16,"ao"],"\xA0"=>[94,"as"],"\xA1"=>[76,"as"],"\xA2"=>[104,"as"],"\xA3"=>[16,"as"],"\xA4"=>[94,"at"],"\xA5"=>[76,"at"],"\xA6"=>[104,"at"],"\xA7"=>[16,"at"],"\xA8"=>[77,"a\x20"],"\xA9"=>[18,"a\x20"],"\xAA"=>[77,"a\x25"],"\xAB"=>[18,"a\x25"],"\xAC"=>[77,"a-"],"\xAD"=>[18,"a-"],"\xAE"=>[77,"a."],"\xAF"=>[18,"a."],"\xB0"=>[77,"a\x2F"],"\xB1"=>[18,"a\x2F"],"\xB2"=>[77,"a3"],"\xB3"=>[18,"a3"],"\xB4"=>[77,"a4"],"\xB5"=>[18,"a4"],"\xB6"=>[77,"a5"],"\xB7"=>[18,"a5"],"\xB8"=>[77,"a6"],"\xB9"=>[18,"a6"],"\xBA"=>[77,"a7"],"\xBB"=>[18,"a7"],"\xBC"=>[77,"a8"],"\xBD"=>[18,"a8"],"\xBE"=>[77,"a9"],"\xBF"=>[18,"a9"],"\xC0"=>[77,"a\x3D"],"\xC1"=>[18,"a\x3D"],"\xC2"=>[77,"aA"],"\xC3"=>[18,"aA"],"\xC4"=>[77,"a_"],"\xC5"=>[18,"a_"],"\xC6"=>[77,"ab"],"\xC7"=>[18,"ab"],"\xC8"=>[77,"ad"],"\xC9"=>[18,"ad"],"\xCA"=>[77,"af"],"\xCB"=>[18,"af"],"\xCC"=>[77,"ag"],"\xCD"=>[18,"ag"],"\xCE"=>[77,"ah"],"\xCF"=>[18,"ah"],"\xD0"=>[77,"al"],"\xD1"=>[18,"al"],"\xD2"=>[77,"am"],"\xD3"=>[18,"am"],"\xD4"=>[77,"an"],"\xD5"=>[18,"an"],"\xD6"=>[77,"ap"],"\xD7"=>[18,"ap"],"\xD8"=>[77,"ar"],"\xD9"=>[18,"ar"],"\xDA"=>[77,"au"],"\xDB"=>[18,"au"],"\xDC"=>[0,"a\x3A"],"\xDD"=>[0,"aB"],"\xDE"=>[0,"aC"],"\xDF"=>[0,"aD"],"\xE0"=>[0,"aE"],"\xE1"=>[0,"aF"],"\xE2"=>[0,"aG"],"\xE3"=>[0,"aH"],"\xE4"=>[0,"aI"],"\xE5"=>[0,"aJ"],"\xE6"=>[0,"aK"],"\xE7"=>[0,"aL"],"\xE8"=>[0,"aM"],"\xE9"=>[0,"aN"],"\xEA"=>[0,"aO"],"\xEB"=>[0,"aP"],"\xEC"=>[0,"aQ"],"\xED"=>[0,"aR"],"\xEE"=>[0,"aS"],"\xEF"=>[0,"aT"],"\xF0"=>[0,"aU"],"\xF1"=>[0,"aV"],"\xF2"=>[0,"aW"],"\xF3"=>[0,"aY"],"\xF4"=>[0,"aj"],"\xF5"=>[0,"ak"],"\xF6"=>[0,"aq"],"\xF7"=>[0,"av"],"\xF8"=>[0,"aw"],"\xF9"=>[0,"ax"],"\xFA"=>[0,"ay"],"\xFB"=>[0,"az"],"\xFC"=>[82,"a"],"\xFD"=>[87,"a"],"\xFE"=>[130,"a"],"\xFF"=>[9,"a"],],["\x00"=>[94,"40"],"\x01"=>[76,"40"],"\x02"=>[104,"40"],"\x03"=>[16,"40"],"\x04"=>[94,"41"],"\x05"=>[76,"41"],"\x06"=>[104,"41"],"\x07"=>[16,"41"],"\x08"=>[94,"42"],"\x09"=>[76,"42"],"\x0A"=>[104,"42"],"\x0B"=>[16,"42"],"\x0C"=>[94,"4a"],"\x0D"=>[76,"4a"],"\x0E"=>[104,"4a"],"\x0F"=>[16,"4a"],"\x10"=>[94,"4c"],"\x11"=>[76,"4c"],"\x12"=>[104,"4c"],"\x13"=>[16,"4c"],"\x14"=>[94,"4e"],"\x15"=>[76,"4e"],"\x16"=>[104,"4e"],"\x17"=>[16,"4e"],"\x18"=>[94,"4i"],"\x19"=>[76,"4i"],"\x1A"=>[104,"4i"],"\x1B"=>[16,"4i"],"\x1C"=>[94,"4o"],"\x1D"=>[76,"4o"],"\x1E"=>[104,"4o"],"\x1F"=>[16,"4o"],"\x20"=>[94,"4s"],"\x21"=>[76,"4s"],"\x22"=>[104,"4s"],"\x23"=>[16,"4s"],"\x24"=>[94,"4t"],"\x25"=>[76,"4t"],"\x26"=>[104,"4t"],"\x27"=>[16,"4t"],"\x28"=>[77,"4\x20"],"\x29"=>[18,"4\x20"],"\x2A"=>[77,"4\x25"],"\x2B"=>[18,"4\x25"],"\x2C"=>[77,"4-"],"-"=>[18,"4-"],"."=>[77,"4."],"\x2F"=>[18,"4."],[77,"4\x2F"],[18,"4\x2F"],[77,"43"],[18,"43"],[77,"44"],[18,"44"],[77,"45"],[18,"45"],[77,"46"],[18,"46"],"\x3A"=>[77,"47"],"\x3B"=>[18,"47"],"\x3C"=>[77,"48"],"\x3D"=>[18,"48"],"\x3E"=>[77,"49"],"\x3F"=>[18,"49"],"\x40"=>[77,"4\x3D"],"A"=>[18,"4\x3D"],"B"=>[77,"4A"],"C"=>[18,"4A"],"D"=>[77,"4_"],"E"=>[18,"4_"],"F"=>[77,"4b"],"G"=>[18,"4b"],"H"=>[77,"4d"],"I"=>[18,"4d"],"J"=>[77,"4f"],"K"=>[18,"4f"],"L"=>[77,"4g"],"M"=>[18,"4g"],"N"=>[77,"4h"],"O"=>[18,"4h"],"P"=>[77,"4l"],"Q"=>[18,"4l"],"R"=>[77,"4m"],"S"=>[18,"4m"],"T"=>[77,"4n"],"U"=>[18,"4n"],"V"=>[77,"4p"],"W"=>[18,"4p"],"X"=>[77,"4r"],"Y"=>[18,"4r"],"Z"=>[77,"4u"],"\x5B"=>[18,"4u"],"\x5C"=>[0,"4\x3A"],"\x5D"=>[0,"4B"],"\x5E"=>[0,"4C"],"_"=>[0,"4D"],"\x60"=>[0,"4E"],"a"=>[0,"4F"],"b"=>[0,"4G"],"c"=>[0,"4H"],"d"=>[0,"4I"],"e"=>[0,"4J"],"f"=>[0,"4K"],"g"=>[0,"4L"],"h"=>[0,"4M"],"i"=>[0,"4N"],"j"=>[0,"4O"],"k"=>[0,"4P"],"l"=>[0,"4Q"],"m"=>[0,"4R"],"n"=>[0,"4S"],"o"=>[0,"4T"],"p"=>[0,"4U"],"q"=>[0,"4V"],"r"=>[0,"4W"],"s"=>[0,"4Y"],"t"=>[0,"4j"],"u"=>[0,"4k"],"v"=>[0,"4q"],"w"=>[0,"4v"],"x"=>[0,"4w"],"y"=>[0,"4x"],"z"=>[0,"4y"],"\x7B"=>[0,"4z"],"\x7C"=>[82,"4"],"\x7D"=>[87,"4"],"~"=>[130,"4"],"\x7F"=>[9,"4"],"\x80"=>[94,"50"],"\x81"=>[76,"50"],"\x82"=>[104,"50"],"\x83"=>[16,"50"],"\x84"=>[94,"51"],"\x85"=>[76,"51"],"\x86"=>[104,"51"],"\x87"=>[16,"51"],"\x88"=>[94,"52"],"\x89"=>[76,"52"],"\x8A"=>[104,"52"],"\x8B"=>[16,"52"],"\x8C"=>[94,"5a"],"\x8D"=>[76,"5a"],"\x8E"=>[104,"5a"],"\x8F"=>[16,"5a"],"\x90"=>[94,"5c"],"\x91"=>[76,"5c"],"\x92"=>[104,"5c"],"\x93"=>[16,"5c"],"\x94"=>[94,"5e"],"\x95"=>[76,"5e"],"\x96"=>[104,"5e"],"\x97"=>[16,"5e"],"\x98"=>[94,"5i"],"\x99"=>[76,"5i"],"\x9A"=>[104,"5i"],"\x9B"=>[16,"5i"],"\x9C"=>[94,"5o"],"\x9D"=>[76,"5o"],"\x9E"=>[104,"5o"],"\x9F"=>[16,"5o"],"\xA0"=>[94,"5s"],"\xA1"=>[76,"5s"],"\xA2"=>[104,"5s"],"\xA3"=>[16,"5s"],"\xA4"=>[94,"5t"],"\xA5"=>[76,"5t"],"\xA6"=>[104,"5t"],"\xA7"=>[16,"5t"],"\xA8"=>[77,"5\x20"],"\xA9"=>[18,"5\x20"],"\xAA"=>[77,"5\x25"],"\xAB"=>[18,"5\x25"],"\xAC"=>[77,"5-"],"\xAD"=>[18,"5-"],"\xAE"=>[77,"5."],"\xAF"=>[18,"5."],"\xB0"=>[77,"5\x2F"],"\xB1"=>[18,"5\x2F"],"\xB2"=>[77,"53"],"\xB3"=>[18,"53"],"\xB4"=>[77,"54"],"\xB5"=>[18,"54"],"\xB6"=>[77,"55"],"\xB7"=>[18,"55"],"\xB8"=>[77,"56"],"\xB9"=>[18,"56"],"\xBA"=>[77,"57"],"\xBB"=>[18,"57"],"\xBC"=>[77,"58"],"\xBD"=>[18,"58"],"\xBE"=>[77,"59"],"\xBF"=>[18,"59"],"\xC0"=>[77,"5\x3D"],"\xC1"=>[18,"5\x3D"],"\xC2"=>[77,"5A"],"\xC3"=>[18,"5A"],"\xC4"=>[77,"5_"],"\xC5"=>[18,"5_"],"\xC6"=>[77,"5b"],"\xC7"=>[18,"5b"],"\xC8"=>[77,"5d"],"\xC9"=>[18,"5d"],"\xCA"=>[77,"5f"],"\xCB"=>[18,"5f"],"\xCC"=>[77,"5g"],"\xCD"=>[18,"5g"],"\xCE"=>[77,"5h"],"\xCF"=>[18,"5h"],"\xD0"=>[77,"5l"],"\xD1"=>[18,"5l"],"\xD2"=>[77,"5m"],"\xD3"=>[18,"5m"],"\xD4"=>[77,"5n"],"\xD5"=>[18,"5n"],"\xD6"=>[77,"5p"],"\xD7"=>[18,"5p"],"\xD8"=>[77,"5r"],"\xD9"=>[18,"5r"],"\xDA"=>[77,"5u"],"\xDB"=>[18,"5u"],"\xDC"=>[0,"5\x3A"],"\xDD"=>[0,"5B"],"\xDE"=>[0,"5C"],"\xDF"=>[0,"5D"],"\xE0"=>[0,"5E"],"\xE1"=>[0,"5F"],"\xE2"=>[0,"5G"],"\xE3"=>[0,"5H"],"\xE4"=>[0,"5I"],"\xE5"=>[0,"5J"],"\xE6"=>[0,"5K"],"\xE7"=>[0,"5L"],"\xE8"=>[0,"5M"],"\xE9"=>[0,"5N"],"\xEA"=>[0,"5O"],"\xEB"=>[0,"5P"],"\xEC"=>[0,"5Q"],"\xED"=>[0,"5R"],"\xEE"=>[0,"5S"],"\xEF"=>[0,"5T"],"\xF0"=>[0,"5U"],"\xF1"=>[0,"5V"],"\xF2"=>[0,"5W"],"\xF3"=>[0,"5Y"],"\xF4"=>[0,"5j"],"\xF5"=>[0,"5k"],"\xF6"=>[0,"5q"],"\xF7"=>[0,"5v"],"\xF8"=>[0,"5w"],"\xF9"=>[0,"5x"],"\xFA"=>[0,"5y"],"\xFB"=>[0,"5z"],"\xFC"=>[82,"5"],"\xFD"=>[87,"5"],"\xFE"=>[130,"5"],"\xFF"=>[9,"5"],],["\x00"=>[94,"60"],"\x01"=>[76,"60"],"\x02"=>[104,"60"],"\x03"=>[16,"60"],"\x04"=>[94,"61"],"\x05"=>[76,"61"],"\x06"=>[104,"61"],"\x07"=>[16,"61"],"\x08"=>[94,"62"],"\x09"=>[76,"62"],"\x0A"=>[104,"62"],"\x0B"=>[16,"62"],"\x0C"=>[94,"6a"],"\x0D"=>[76,"6a"],"\x0E"=>[104,"6a"],"\x0F"=>[16,"6a"],"\x10"=>[94,"6c"],"\x11"=>[76,"6c"],"\x12"=>[104,"6c"],"\x13"=>[16,"6c"],"\x14"=>[94,"6e"],"\x15"=>[76,"6e"],"\x16"=>[104,"6e"],"\x17"=>[16,"6e"],"\x18"=>[94,"6i"],"\x19"=>[76,"6i"],"\x1A"=>[104,"6i"],"\x1B"=>[16,"6i"],"\x1C"=>[94,"6o"],"\x1D"=>[76,"6o"],"\x1E"=>[104,"6o"],"\x1F"=>[16,"6o"],"\x20"=>[94,"6s"],"\x21"=>[76,"6s"],"\x22"=>[104,"6s"],"\x23"=>[16,"6s"],"\x24"=>[94,"6t"],"\x25"=>[76,"6t"],"\x26"=>[104,"6t"],"\x27"=>[16,"6t"],"\x28"=>[77,"6\x20"],"\x29"=>[18,"6\x20"],"\x2A"=>[77,"6\x25"],"\x2B"=>[18,"6\x25"],"\x2C"=>[77,"6-"],"-"=>[18,"6-"],"."=>[77,"6."],"\x2F"=>[18,"6."],[77,"6\x2F"],[18,"6\x2F"],[77,"63"],[18,"63"],[77,"64"],[18,"64"],[77,"65"],[18,"65"],[77,"66"],[18,"66"],"\x3A"=>[77,"67"],"\x3B"=>[18,"67"],"\x3C"=>[77,"68"],"\x3D"=>[18,"68"],"\x3E"=>[77,"69"],"\x3F"=>[18,"69"],"\x40"=>[77,"6\x3D"],"A"=>[18,"6\x3D"],"B"=>[77,"6A"],"C"=>[18,"6A"],"D"=>[77,"6_"],"E"=>[18,"6_"],"F"=>[77,"6b"],"G"=>[18,"6b"],"H"=>[77,"6d"],"I"=>[18,"6d"],"J"=>[77,"6f"],"K"=>[18,"6f"],"L"=>[77,"6g"],"M"=>[18,"6g"],"N"=>[77,"6h"],"O"=>[18,"6h"],"P"=>[77,"6l"],"Q"=>[18,"6l"],"R"=>[77,"6m"],"S"=>[18,"6m"],"T"=>[77,"6n"],"U"=>[18,"6n"],"V"=>[77,"6p"],"W"=>[18,"6p"],"X"=>[77,"6r"],"Y"=>[18,"6r"],"Z"=>[77,"6u"],"\x5B"=>[18,"6u"],"\x5C"=>[0,"6\x3A"],"\x5D"=>[0,"6B"],"\x5E"=>[0,"6C"],"_"=>[0,"6D"],"\x60"=>[0,"6E"],"a"=>[0,"6F"],"b"=>[0,"6G"],"c"=>[0,"6H"],"d"=>[0,"6I"],"e"=>[0,"6J"],"f"=>[0,"6K"],"g"=>[0,"6L"],"h"=>[0,"6M"],"i"=>[0,"6N"],"j"=>[0,"6O"],"k"=>[0,"6P"],"l"=>[0,"6Q"],"m"=>[0,"6R"],"n"=>[0,"6S"],"o"=>[0,"6T"],"p"=>[0,"6U"],"q"=>[0,"6V"],"r"=>[0,"6W"],"s"=>[0,"6Y"],"t"=>[0,"6j"],"u"=>[0,"6k"],"v"=>[0,"6q"],"w"=>[0,"6v"],"x"=>[0,"6w"],"y"=>[0,"6x"],"z"=>[0,"6y"],"\x7B"=>[0,"6z"],"\x7C"=>[82,"6"],"\x7D"=>[87,"6"],"~"=>[130,"6"],"\x7F"=>[9,"6"],"\x80"=>[94,"70"],"\x81"=>[76,"70"],"\x82"=>[104,"70"],"\x83"=>[16,"70"],"\x84"=>[94,"71"],"\x85"=>[76,"71"],"\x86"=>[104,"71"],"\x87"=>[16,"71"],"\x88"=>[94,"72"],"\x89"=>[76,"72"],"\x8A"=>[104,"72"],"\x8B"=>[16,"72"],"\x8C"=>[94,"7a"],"\x8D"=>[76,"7a"],"\x8E"=>[104,"7a"],"\x8F"=>[16,"7a"],"\x90"=>[94,"7c"],"\x91"=>[76,"7c"],"\x92"=>[104,"7c"],"\x93"=>[16,"7c"],"\x94"=>[94,"7e"],"\x95"=>[76,"7e"],"\x96"=>[104,"7e"],"\x97"=>[16,"7e"],"\x98"=>[94,"7i"],"\x99"=>[76,"7i"],"\x9A"=>[104,"7i"],"\x9B"=>[16,"7i"],"\x9C"=>[94,"7o"],"\x9D"=>[76,"7o"],"\x9E"=>[104,"7o"],"\x9F"=>[16,"7o"],"\xA0"=>[94,"7s"],"\xA1"=>[76,"7s"],"\xA2"=>[104,"7s"],"\xA3"=>[16,"7s"],"\xA4"=>[94,"7t"],"\xA5"=>[76,"7t"],"\xA6"=>[104,"7t"],"\xA7"=>[16,"7t"],"\xA8"=>[77,"7\x20"],"\xA9"=>[18,"7\x20"],"\xAA"=>[77,"7\x25"],"\xAB"=>[18,"7\x25"],"\xAC"=>[77,"7-"],"\xAD"=>[18,"7-"],"\xAE"=>[77,"7."],"\xAF"=>[18,"7."],"\xB0"=>[77,"7\x2F"],"\xB1"=>[18,"7\x2F"],"\xB2"=>[77,"73"],"\xB3"=>[18,"73"],"\xB4"=>[77,"74"],"\xB5"=>[18,"74"],"\xB6"=>[77,"75"],"\xB7"=>[18,"75"],"\xB8"=>[77,"76"],"\xB9"=>[18,"76"],"\xBA"=>[77,"77"],"\xBB"=>[18,"77"],"\xBC"=>[77,"78"],"\xBD"=>[18,"78"],"\xBE"=>[77,"79"],"\xBF"=>[18,"79"],"\xC0"=>[77,"7\x3D"],"\xC1"=>[18,"7\x3D"],"\xC2"=>[77,"7A"],"\xC3"=>[18,"7A"],"\xC4"=>[77,"7_"],"\xC5"=>[18,"7_"],"\xC6"=>[77,"7b"],"\xC7"=>[18,"7b"],"\xC8"=>[77,"7d"],"\xC9"=>[18,"7d"],"\xCA"=>[77,"7f"],"\xCB"=>[18,"7f"],"\xCC"=>[77,"7g"],"\xCD"=>[18,"7g"],"\xCE"=>[77,"7h"],"\xCF"=>[18,"7h"],"\xD0"=>[77,"7l"],"\xD1"=>[18,"7l"],"\xD2"=>[77,"7m"],"\xD3"=>[18,"7m"],"\xD4"=>[77,"7n"],"\xD5"=>[18,"7n"],"\xD6"=>[77,"7p"],"\xD7"=>[18,"7p"],"\xD8"=>[77,"7r"],"\xD9"=>[18,"7r"],"\xDA"=>[77,"7u"],"\xDB"=>[18,"7u"],"\xDC"=>[0,"7\x3A"],"\xDD"=>[0,"7B"],"\xDE"=>[0,"7C"],"\xDF"=>[0,"7D"],"\xE0"=>[0,"7E"],"\xE1"=>[0,"7F"],"\xE2"=>[0,"7G"],"\xE3"=>[0,"7H"],"\xE4"=>[0,"7I"],"\xE5"=>[0,"7J"],"\xE6"=>[0,"7K"],"\xE7"=>[0,"7L"],"\xE8"=>[0,"7M"],"\xE9"=>[0,"7N"],"\xEA"=>[0,"7O"],"\xEB"=>[0,"7P"],"\xEC"=>[0,"7Q"],"\xED"=>[0,"7R"],"\xEE"=>[0,"7S"],"\xEF"=>[0,"7T"],"\xF0"=>[0,"7U"],"\xF1"=>[0,"7V"],"\xF2"=>[0,"7W"],"\xF3"=>[0,"7Y"],"\xF4"=>[0,"7j"],"\xF5"=>[0,"7k"],"\xF6"=>[0,"7q"],"\xF7"=>[0,"7v"],"\xF8"=>[0,"7w"],"\xF9"=>[0,"7x"],"\xFA"=>[0,"7y"],"\xFB"=>[0,"7z"],"\xFC"=>[82,"7"],"\xFD"=>[87,"7"],"\xFE"=>[130,"7"],"\xFF"=>[9,"7"],],["\x00"=>[77,"60"],"\x01"=>[18,"60"],"\x02"=>[77,"61"],"\x03"=>[18,"61"],"\x04"=>[77,"62"],"\x05"=>[18,"62"],"\x06"=>[77,"6a"],"\x07"=>[18,"6a"],"\x08"=>[77,"6c"],"\x09"=>[18,"6c"],"\x0A"=>[77,"6e"],"\x0B"=>[18,"6e"],"\x0C"=>[77,"6i"],"\x0D"=>[18,"6i"],"\x0E"=>[77,"6o"],"\x0F"=>[18,"6o"],"\x10"=>[77,"6s"],"\x11"=>[18,"6s"],"\x12"=>[77,"6t"],"\x13"=>[18,"6t"],"\x14"=>[0,"6\x20"],"\x15"=>[0,"6\x25"],"\x16"=>[0,"6-"],"\x17"=>[0,"6."],"\x18"=>[0,"6\x2F"],"\x19"=>[0,"63"],"\x1A"=>[0,"64"],"\x1B"=>[0,"65"],"\x1C"=>[0,"66"],"\x1D"=>[0,"67"],"\x1E"=>[0,"68"],"\x1F"=>[0,"69"],"\x20"=>[0,"6\x3D"],"\x21"=>[0,"6A"],"\x22"=>[0,"6_"],"\x23"=>[0,"6b"],"\x24"=>[0,"6d"],"\x25"=>[0,"6f"],"\x26"=>[0,"6g"],"\x27"=>[0,"6h"],"\x28"=>[0,"6l"],"\x29"=>[0,"6m"],"\x2A"=>[0,"6n"],"\x2B"=>[0,"6p"],"\x2C"=>[0,"6r"],"-"=>[0,"6u"],"."=>[100,"6"],"\x2F"=>[110,"6"],[111,"6"],[115,"6"],[116,"6"],[118,"6"],[119,"6"],[122,"6"],[123,"6"],[125,"6"],[126,"6"],[129,"6"],"\x3A"=>[143,"6"],"\x3B"=>[148,"6"],"\x3C"=>[151,"6"],"\x3D"=>[153,"6"],"\x3E"=>[83,"6"],"\x3F"=>[10,"6"],"\x40"=>[77,"70"],"A"=>[18,"70"],"B"=>[77,"71"],"C"=>[18,"71"],"D"=>[77,"72"],"E"=>[18,"72"],"F"=>[77,"7a"],"G"=>[18,"7a"],"H"=>[77,"7c"],"I"=>[18,"7c"],"J"=>[77,"7e"],"K"=>[18,"7e"],"L"=>[77,"7i"],"M"=>[18,"7i"],"N"=>[77,"7o"],"O"=>[18,"7o"],"P"=>[77,"7s"],"Q"=>[18,"7s"],"R"=>[77,"7t"],"S"=>[18,"7t"],"T"=>[0,"7\x20"],"U"=>[0,"7\x25"],"V"=>[0,"7-"],"W"=>[0,"7."],"X"=>[0,"7\x2F"],"Y"=>[0,"73"],"Z"=>[0,"74"],"\x5B"=>[0,"75"],"\x5C"=>[0,"76"],"\x5D"=>[0,"77"],"\x5E"=>[0,"78"],"_"=>[0,"79"],"\x60"=>[0,"7\x3D"],"a"=>[0,"7A"],"b"=>[0,"7_"],"c"=>[0,"7b"],"d"=>[0,"7d"],"e"=>[0,"7f"],"f"=>[0,"7g"],"g"=>[0,"7h"],"h"=>[0,"7l"],"i"=>[0,"7m"],"j"=>[0,"7n"],"k"=>[0,"7p"],"l"=>[0,"7r"],"m"=>[0,"7u"],"n"=>[100,"7"],"o"=>[110,"7"],"p"=>[111,"7"],"q"=>[115,"7"],"r"=>[116,"7"],"s"=>[118,"7"],"t"=>[119,"7"],"u"=>[122,"7"],"v"=>[123,"7"],"w"=>[125,"7"],"x"=>[126,"7"],"y"=>[129,"7"],"z"=>[143,"7"],"\x7B"=>[148,"7"],"\x7C"=>[151,"7"],"\x7D"=>[153,"7"],"~"=>[83,"7"],"\x7F"=>[10,"7"],"\x80"=>[77,"80"],"\x81"=>[18,"80"],"\x82"=>[77,"81"],"\x83"=>[18,"81"],"\x84"=>[77,"82"],"\x85"=>[18,"82"],"\x86"=>[77,"8a"],"\x87"=>[18,"8a"],"\x88"=>[77,"8c"],"\x89"=>[18,"8c"],"\x8A"=>[77,"8e"],"\x8B"=>[18,"8e"],"\x8C"=>[77,"8i"],"\x8D"=>[18,"8i"],"\x8E"=>[77,"8o"],"\x8F"=>[18,"8o"],"\x90"=>[77,"8s"],"\x91"=>[18,"8s"],"\x92"=>[77,"8t"],"\x93"=>[18,"8t"],"\x94"=>[0,"8\x20"],"\x95"=>[0,"8\x25"],"\x96"=>[0,"8-"],"\x97"=>[0,"8."],"\x98"=>[0,"8\x2F"],"\x99"=>[0,"83"],"\x9A"=>[0,"84"],"\x9B"=>[0,"85"],"\x9C"=>[0,"86"],"\x9D"=>[0,"87"],"\x9E"=>[0,"88"],"\x9F"=>[0,"89"],"\xA0"=>[0,"8\x3D"],"\xA1"=>[0,"8A"],"\xA2"=>[0,"8_"],"\xA3"=>[0,"8b"],"\xA4"=>[0,"8d"],"\xA5"=>[0,"8f"],"\xA6"=>[0,"8g"],"\xA7"=>[0,"8h"],"\xA8"=>[0,"8l"],"\xA9"=>[0,"8m"],"\xAA"=>[0,"8n"],"\xAB"=>[0,"8p"],"\xAC"=>[0,"8r"],"\xAD"=>[0,"8u"],"\xAE"=>[100,"8"],"\xAF"=>[110,"8"],"\xB0"=>[111,"8"],"\xB1"=>[115,"8"],"\xB2"=>[116,"8"],"\xB3"=>[118,"8"],"\xB4"=>[119,"8"],"\xB5"=>[122,"8"],"\xB6"=>[123,"8"],"\xB7"=>[125,"8"],"\xB8"=>[126,"8"],"\xB9"=>[129,"8"],"\xBA"=>[143,"8"],"\xBB"=>[148,"8"],"\xBC"=>[151,"8"],"\xBD"=>[153,"8"],"\xBE"=>[83,"8"],"\xBF"=>[10,"8"],"\xC0"=>[77,"90"],"\xC1"=>[18,"90"],"\xC2"=>[77,"91"],"\xC3"=>[18,"91"],"\xC4"=>[77,"92"],"\xC5"=>[18,"92"],"\xC6"=>[77,"9a"],"\xC7"=>[18,"9a"],"\xC8"=>[77,"9c"],"\xC9"=>[18,"9c"],"\xCA"=>[77,"9e"],"\xCB"=>[18,"9e"],"\xCC"=>[77,"9i"],"\xCD"=>[18,"9i"],"\xCE"=>[77,"9o"],"\xCF"=>[18,"9o"],"\xD0"=>[77,"9s"],"\xD1"=>[18,"9s"],"\xD2"=>[77,"9t"],"\xD3"=>[18,"9t"],"\xD4"=>[0,"9\x20"],"\xD5"=>[0,"9\x25"],"\xD6"=>[0,"9-"],"\xD7"=>[0,"9."],"\xD8"=>[0,"9\x2F"],"\xD9"=>[0,"93"],"\xDA"=>[0,"94"],"\xDB"=>[0,"95"],"\xDC"=>[0,"96"],"\xDD"=>[0,"97"],"\xDE"=>[0,"98"],"\xDF"=>[0,"99"],"\xE0"=>[0,"9\x3D"],"\xE1"=>[0,"9A"],"\xE2"=>[0,"9_"],"\xE3"=>[0,"9b"],"\xE4"=>[0,"9d"],"\xE5"=>[0,"9f"],"\xE6"=>[0,"9g"],"\xE7"=>[0,"9h"],"\xE8"=>[0,"9l"],"\xE9"=>[0,"9m"],"\xEA"=>[0,"9n"],"\xEB"=>[0,"9p"],"\xEC"=>[0,"9r"],"\xED"=>[0,"9u"],"\xEE"=>[100,"9"],"\xEF"=>[110,"9"],"\xF0"=>[111,"9"],"\xF1"=>[115,"9"],"\xF2"=>[116,"9"],"\xF3"=>[118,"9"],"\xF4"=>[119,"9"],"\xF5"=>[122,"9"],"\xF6"=>[123,"9"],"\xF7"=>[125,"9"],"\xF8"=>[126,"9"],"\xF9"=>[129,"9"],"\xFA"=>[143,"9"],"\xFB"=>[148,"9"],"\xFC"=>[151,"9"],"\xFD"=>[153,"9"],"\xFE"=>[83,"9"],"\xFF"=>[10,"9"],],["\x00"=>[94,"80"],"\x01"=>[76,"80"],"\x02"=>[104,"80"],"\x03"=>[16,"80"],"\x04"=>[94,"81"],"\x05"=>[76,"81"],"\x06"=>[104,"81"],"\x07"=>[16,"81"],"\x08"=>[94,"82"],"\x09"=>[76,"82"],"\x0A"=>[104,"82"],"\x0B"=>[16,"82"],"\x0C"=>[94,"8a"],"\x0D"=>[76,"8a"],"\x0E"=>[104,"8a"],"\x0F"=>[16,"8a"],"\x10"=>[94,"8c"],"\x11"=>[76,"8c"],"\x12"=>[104,"8c"],"\x13"=>[16,"8c"],"\x14"=>[94,"8e"],"\x15"=>[76,"8e"],"\x16"=>[104,"8e"],"\x17"=>[16,"8e"],"\x18"=>[94,"8i"],"\x19"=>[76,"8i"],"\x1A"=>[104,"8i"],"\x1B"=>[16,"8i"],"\x1C"=>[94,"8o"],"\x1D"=>[76,"8o"],"\x1E"=>[104,"8o"],"\x1F"=>[16,"8o"],"\x20"=>[94,"8s"],"\x21"=>[76,"8s"],"\x22"=>[104,"8s"],"\x23"=>[16,"8s"],"\x24"=>[94,"8t"],"\x25"=>[76,"8t"],"\x26"=>[104,"8t"],"\x27"=>[16,"8t"],"\x28"=>[77,"8\x20"],"\x29"=>[18,"8\x20"],"\x2A"=>[77,"8\x25"],"\x2B"=>[18,"8\x25"],"\x2C"=>[77,"8-"],"-"=>[18,"8-"],"."=>[77,"8."],"\x2F"=>[18,"8."],[77,"8\x2F"],[18,"8\x2F"],[77,"83"],[18,"83"],[77,"84"],[18,"84"],[77,"85"],[18,"85"],[77,"86"],[18,"86"],"\x3A"=>[77,"87"],"\x3B"=>[18,"87"],"\x3C"=>[77,"88"],"\x3D"=>[18,"88"],"\x3E"=>[77,"89"],"\x3F"=>[18,"89"],"\x40"=>[77,"8\x3D"],"A"=>[18,"8\x3D"],"B"=>[77,"8A"],"C"=>[18,"8A"],"D"=>[77,"8_"],"E"=>[18,"8_"],"F"=>[77,"8b"],"G"=>[18,"8b"],"H"=>[77,"8d"],"I"=>[18,"8d"],"J"=>[77,"8f"],"K"=>[18,"8f"],"L"=>[77,"8g"],"M"=>[18,"8g"],"N"=>[77,"8h"],"O"=>[18,"8h"],"P"=>[77,"8l"],"Q"=>[18,"8l"],"R"=>[77,"8m"],"S"=>[18,"8m"],"T"=>[77,"8n"],"U"=>[18,"8n"],"V"=>[77,"8p"],"W"=>[18,"8p"],"X"=>[77,"8r"],"Y"=>[18,"8r"],"Z"=>[77,"8u"],"\x5B"=>[18,"8u"],"\x5C"=>[0,"8\x3A"],"\x5D"=>[0,"8B"],"\x5E"=>[0,"8C"],"_"=>[0,"8D"],"\x60"=>[0,"8E"],"a"=>[0,"8F"],"b"=>[0,"8G"],"c"=>[0,"8H"],"d"=>[0,"8I"],"e"=>[0,"8J"],"f"=>[0,"8K"],"g"=>[0,"8L"],"h"=>[0,"8M"],"i"=>[0,"8N"],"j"=>[0,"8O"],"k"=>[0,"8P"],"l"=>[0,"8Q"],"m"=>[0,"8R"],"n"=>[0,"8S"],"o"=>[0,"8T"],"p"=>[0,"8U"],"q"=>[0,"8V"],"r"=>[0,"8W"],"s"=>[0,"8Y"],"t"=>[0,"8j"],"u"=>[0,"8k"],"v"=>[0,"8q"],"w"=>[0,"8v"],"x"=>[0,"8w"],"y"=>[0,"8x"],"z"=>[0,"8y"],"\x7B"=>[0,"8z"],"\x7C"=>[82,"8"],"\x7D"=>[87,"8"],"~"=>[130,"8"],"\x7F"=>[9,"8"],"\x80"=>[94,"90"],"\x81"=>[76,"90"],"\x82"=>[104,"90"],"\x83"=>[16,"90"],"\x84"=>[94,"91"],"\x85"=>[76,"91"],"\x86"=>[104,"91"],"\x87"=>[16,"91"],"\x88"=>[94,"92"],"\x89"=>[76,"92"],"\x8A"=>[104,"92"],"\x8B"=>[16,"92"],"\x8C"=>[94,"9a"],"\x8D"=>[76,"9a"],"\x8E"=>[104,"9a"],"\x8F"=>[16,"9a"],"\x90"=>[94,"9c"],"\x91"=>[76,"9c"],"\x92"=>[104,"9c"],"\x93"=>[16,"9c"],"\x94"=>[94,"9e"],"\x95"=>[76,"9e"],"\x96"=>[104,"9e"],"\x97"=>[16,"9e"],"\x98"=>[94,"9i"],"\x99"=>[76,"9i"],"\x9A"=>[104,"9i"],"\x9B"=>[16,"9i"],"\x9C"=>[94,"9o"],"\x9D"=>[76,"9o"],"\x9E"=>[104,"9o"],"\x9F"=>[16,"9o"],"\xA0"=>[94,"9s"],"\xA1"=>[76,"9s"],"\xA2"=>[104,"9s"],"\xA3"=>[16,"9s"],"\xA4"=>[94,"9t"],"\xA5"=>[76,"9t"],"\xA6"=>[104,"9t"],"\xA7"=>[16,"9t"],"\xA8"=>[77,"9\x20"],"\xA9"=>[18,"9\x20"],"\xAA"=>[77,"9\x25"],"\xAB"=>[18,"9\x25"],"\xAC"=>[77,"9-"],"\xAD"=>[18,"9-"],"\xAE"=>[77,"9."],"\xAF"=>[18,"9."],"\xB0"=>[77,"9\x2F"],"\xB1"=>[18,"9\x2F"],"\xB2"=>[77,"93"],"\xB3"=>[18,"93"],"\xB4"=>[77,"94"],"\xB5"=>[18,"94"],"\xB6"=>[77,"95"],"\xB7"=>[18,"95"],"\xB8"=>[77,"96"],"\xB9"=>[18,"96"],"\xBA"=>[77,"97"],"\xBB"=>[18,"97"],"\xBC"=>[77,"98"],"\xBD"=>[18,"98"],"\xBE"=>[77,"99"],"\xBF"=>[18,"99"],"\xC0"=>[77,"9\x3D"],"\xC1"=>[18,"9\x3D"],"\xC2"=>[77,"9A"],"\xC3"=>[18,"9A"],"\xC4"=>[77,"9_"],"\xC5"=>[18,"9_"],"\xC6"=>[77,"9b"],"\xC7"=>[18,"9b"],"\xC8"=>[77,"9d"],"\xC9"=>[18,"9d"],"\xCA"=>[77,"9f"],"\xCB"=>[18,"9f"],"\xCC"=>[77,"9g"],"\xCD"=>[18,"9g"],"\xCE"=>[77,"9h"],"\xCF"=>[18,"9h"],"\xD0"=>[77,"9l"],"\xD1"=>[18,"9l"],"\xD2"=>[77,"9m"],"\xD3"=>[18,"9m"],"\xD4"=>[77,"9n"],"\xD5"=>[18,"9n"],"\xD6"=>[77,"9p"],"\xD7"=>[18,"9p"],"\xD8"=>[77,"9r"],"\xD9"=>[18,"9r"],"\xDA"=>[77,"9u"],"\xDB"=>[18,"9u"],"\xDC"=>[0,"9\x3A"],"\xDD"=>[0,"9B"],"\xDE"=>[0,"9C"],"\xDF"=>[0,"9D"],"\xE0"=>[0,"9E"],"\xE1"=>[0,"9F"],"\xE2"=>[0,"9G"],"\xE3"=>[0,"9H"],"\xE4"=>[0,"9I"],"\xE5"=>[0,"9J"],"\xE6"=>[0,"9K"],"\xE7"=>[0,"9L"],"\xE8"=>[0,"9M"],"\xE9"=>[0,"9N"],"\xEA"=>[0,"9O"],"\xEB"=>[0,"9P"],"\xEC"=>[0,"9Q"],"\xED"=>[0,"9R"],"\xEE"=>[0,"9S"],"\xEF"=>[0,"9T"],"\xF0"=>[0,"9U"],"\xF1"=>[0,"9V"],"\xF2"=>[0,"9W"],"\xF3"=>[0,"9Y"],"\xF4"=>[0,"9j"],"\xF5"=>[0,"9k"],"\xF6"=>[0,"9q"],"\xF7"=>[0,"9v"],"\xF8"=>[0,"9w"],"\xF9"=>[0,"9x"],"\xFA"=>[0,"9y"],"\xFB"=>[0,"9z"],"\xFC"=>[82,"9"],"\xFD"=>[87,"9"],"\xFE"=>[130,"9"],"\xFF"=>[9,"9"],],["\x00"=>[94,"\x3A0"],"\x01"=>[76,"\x3A0"],"\x02"=>[104,"\x3A0"],"\x03"=>[16,"\x3A0"],"\x04"=>[94,"\x3A1"],"\x05"=>[76,"\x3A1"],"\x06"=>[104,"\x3A1"],"\x07"=>[16,"\x3A1"],"\x08"=>[94,"\x3A2"],"\x09"=>[76,"\x3A2"],"\x0A"=>[104,"\x3A2"],"\x0B"=>[16,"\x3A2"],"\x0C"=>[94,"\x3Aa"],"\x0D"=>[76,"\x3Aa"],"\x0E"=>[104,"\x3Aa"],"\x0F"=>[16,"\x3Aa"],"\x10"=>[94,"\x3Ac"],"\x11"=>[76,"\x3Ac"],"\x12"=>[104,"\x3Ac"],"\x13"=>[16,"\x3Ac"],"\x14"=>[94,"\x3Ae"],"\x15"=>[76,"\x3Ae"],"\x16"=>[104,"\x3Ae"],"\x17"=>[16,"\x3Ae"],"\x18"=>[94,"\x3Ai"],"\x19"=>[76,"\x3Ai"],"\x1A"=>[104,"\x3Ai"],"\x1B"=>[16,"\x3Ai"],"\x1C"=>[94,"\x3Ao"],"\x1D"=>[76,"\x3Ao"],"\x1E"=>[104,"\x3Ao"],"\x1F"=>[16,"\x3Ao"],"\x20"=>[94,"\x3As"],"\x21"=>[76,"\x3As"],"\x22"=>[104,"\x3As"],"\x23"=>[16,"\x3As"],"\x24"=>[94,"\x3At"],"\x25"=>[76,"\x3At"],"\x26"=>[104,"\x3At"],"\x27"=>[16,"\x3At"],"\x28"=>[77,"\x3A\x20"],"\x29"=>[18,"\x3A\x20"],"\x2A"=>[77,"\x3A\x25"],"\x2B"=>[18,"\x3A\x25"],"\x2C"=>[77,"\x3A-"],"-"=>[18,"\x3A-"],"."=>[77,"\x3A."],"\x2F"=>[18,"\x3A."],[77,"\x3A\x2F"],[18,"\x3A\x2F"],[77,"\x3A3"],[18,"\x3A3"],[77,"\x3A4"],[18,"\x3A4"],[77,"\x3A5"],[18,"\x3A5"],[77,"\x3A6"],[18,"\x3A6"],"\x3A"=>[77,"\x3A7"],"\x3B"=>[18,"\x3A7"],"\x3C"=>[77,"\x3A8"],"\x3D"=>[18,"\x3A8"],"\x3E"=>[77,"\x3A9"],"\x3F"=>[18,"\x3A9"],"\x40"=>[77,"\x3A\x3D"],"A"=>[18,"\x3A\x3D"],"B"=>[77,"\x3AA"],"C"=>[18,"\x3AA"],"D"=>[77,"\x3A_"],"E"=>[18,"\x3A_"],"F"=>[77,"\x3Ab"],"G"=>[18,"\x3Ab"],"H"=>[77,"\x3Ad"],"I"=>[18,"\x3Ad"],"J"=>[77,"\x3Af"],"K"=>[18,"\x3Af"],"L"=>[77,"\x3Ag"],"M"=>[18,"\x3Ag"],"N"=>[77,"\x3Ah"],"O"=>[18,"\x3Ah"],"P"=>[77,"\x3Al"],"Q"=>[18,"\x3Al"],"R"=>[77,"\x3Am"],"S"=>[18,"\x3Am"],"T"=>[77,"\x3An"],"U"=>[18,"\x3An"],"V"=>[77,"\x3Ap"],"W"=>[18,"\x3Ap"],"X"=>[77,"\x3Ar"],"Y"=>[18,"\x3Ar"],"Z"=>[77,"\x3Au"],"\x5B"=>[18,"\x3Au"],"\x5C"=>[0,"\x3A\x3A"],"\x5D"=>[0,"\x3AB"],"\x5E"=>[0,"\x3AC"],"_"=>[0,"\x3AD"],"\x60"=>[0,"\x3AE"],"a"=>[0,"\x3AF"],"b"=>[0,"\x3AG"],"c"=>[0,"\x3AH"],"d"=>[0,"\x3AI"],"e"=>[0,"\x3AJ"],"f"=>[0,"\x3AK"],"g"=>[0,"\x3AL"],"h"=>[0,"\x3AM"],"i"=>[0,"\x3AN"],"j"=>[0,"\x3AO"],"k"=>[0,"\x3AP"],"l"=>[0,"\x3AQ"],"m"=>[0,"\x3AR"],"n"=>[0,"\x3AS"],"o"=>[0,"\x3AT"],"p"=>[0,"\x3AU"],"q"=>[0,"\x3AV"],"r"=>[0,"\x3AW"],"s"=>[0,"\x3AY"],"t"=>[0,"\x3Aj"],"u"=>[0,"\x3Ak"],"v"=>[0,"\x3Aq"],"w"=>[0,"\x3Av"],"x"=>[0,"\x3Aw"],"y"=>[0,"\x3Ax"],"z"=>[0,"\x3Ay"],"\x7B"=>[0,"\x3Az"],"\x7C"=>[82,"\x3A"],"\x7D"=>[87,"\x3A"],"~"=>[130,"\x3A"],"\x7F"=>[9,"\x3A"],"\x80"=>[94,"B0"],"\x81"=>[76,"B0"],"\x82"=>[104,"B0"],"\x83"=>[16,"B0"],"\x84"=>[94,"B1"],"\x85"=>[76,"B1"],"\x86"=>[104,"B1"],"\x87"=>[16,"B1"],"\x88"=>[94,"B2"],"\x89"=>[76,"B2"],"\x8A"=>[104,"B2"],"\x8B"=>[16,"B2"],"\x8C"=>[94,"Ba"],"\x8D"=>[76,"Ba"],"\x8E"=>[104,"Ba"],"\x8F"=>[16,"Ba"],"\x90"=>[94,"Bc"],"\x91"=>[76,"Bc"],"\x92"=>[104,"Bc"],"\x93"=>[16,"Bc"],"\x94"=>[94,"Be"],"\x95"=>[76,"Be"],"\x96"=>[104,"Be"],"\x97"=>[16,"Be"],"\x98"=>[94,"Bi"],"\x99"=>[76,"Bi"],"\x9A"=>[104,"Bi"],"\x9B"=>[16,"Bi"],"\x9C"=>[94,"Bo"],"\x9D"=>[76,"Bo"],"\x9E"=>[104,"Bo"],"\x9F"=>[16,"Bo"],"\xA0"=>[94,"Bs"],"\xA1"=>[76,"Bs"],"\xA2"=>[104,"Bs"],"\xA3"=>[16,"Bs"],"\xA4"=>[94,"Bt"],"\xA5"=>[76,"Bt"],"\xA6"=>[104,"Bt"],"\xA7"=>[16,"Bt"],"\xA8"=>[77,"B\x20"],"\xA9"=>[18,"B\x20"],"\xAA"=>[77,"B\x25"],"\xAB"=>[18,"B\x25"],"\xAC"=>[77,"B-"],"\xAD"=>[18,"B-"],"\xAE"=>[77,"B."],"\xAF"=>[18,"B."],"\xB0"=>[77,"B\x2F"],"\xB1"=>[18,"B\x2F"],"\xB2"=>[77,"B3"],"\xB3"=>[18,"B3"],"\xB4"=>[77,"B4"],"\xB5"=>[18,"B4"],"\xB6"=>[77,"B5"],"\xB7"=>[18,"B5"],"\xB8"=>[77,"B6"],"\xB9"=>[18,"B6"],"\xBA"=>[77,"B7"],"\xBB"=>[18,"B7"],"\xBC"=>[77,"B8"],"\xBD"=>[18,"B8"],"\xBE"=>[77,"B9"],"\xBF"=>[18,"B9"],"\xC0"=>[77,"B\x3D"],"\xC1"=>[18,"B\x3D"],"\xC2"=>[77,"BA"],"\xC3"=>[18,"BA"],"\xC4"=>[77,"B_"],"\xC5"=>[18,"B_"],"\xC6"=>[77,"Bb"],"\xC7"=>[18,"Bb"],"\xC8"=>[77,"Bd"],"\xC9"=>[18,"Bd"],"\xCA"=>[77,"Bf"],"\xCB"=>[18,"Bf"],"\xCC"=>[77,"Bg"],"\xCD"=>[18,"Bg"],"\xCE"=>[77,"Bh"],"\xCF"=>[18,"Bh"],"\xD0"=>[77,"Bl"],"\xD1"=>[18,"Bl"],"\xD2"=>[77,"Bm"],"\xD3"=>[18,"Bm"],"\xD4"=>[77,"Bn"],"\xD5"=>[18,"Bn"],"\xD6"=>[77,"Bp"],"\xD7"=>[18,"Bp"],"\xD8"=>[77,"Br"],"\xD9"=>[18,"Br"],"\xDA"=>[77,"Bu"],"\xDB"=>[18,"Bu"],"\xDC"=>[0,"B\x3A"],"\xDD"=>[0,"BB"],"\xDE"=>[0,"BC"],"\xDF"=>[0,"BD"],"\xE0"=>[0,"BE"],"\xE1"=>[0,"BF"],"\xE2"=>[0,"BG"],"\xE3"=>[0,"BH"],"\xE4"=>[0,"BI"],"\xE5"=>[0,"BJ"],"\xE6"=>[0,"BK"],"\xE7"=>[0,"BL"],"\xE8"=>[0,"BM"],"\xE9"=>[0,"BN"],"\xEA"=>[0,"BO"],"\xEB"=>[0,"BP"],"\xEC"=>[0,"BQ"],"\xED"=>[0,"BR"],"\xEE"=>[0,"BS"],"\xEF"=>[0,"BT"],"\xF0"=>[0,"BU"],"\xF1"=>[0,"BV"],"\xF2"=>[0,"BW"],"\xF3"=>[0,"BY"],"\xF4"=>[0,"Bj"],"\xF5"=>[0,"Bk"],"\xF6"=>[0,"Bq"],"\xF7"=>[0,"Bv"],"\xF8"=>[0,"Bw"],"\xF9"=>[0,"Bx"],"\xFA"=>[0,"By"],"\xFB"=>[0,"Bz"],"\xFC"=>[82,"B"],"\xFD"=>[87,"B"],"\xFE"=>[130,"B"],"\xFF"=>[9,"B"],],["\x00"=>[77,"\x3A0"],"\x01"=>[18,"\x3A0"],"\x02"=>[77,"\x3A1"],"\x03"=>[18,"\x3A1"],"\x04"=>[77,"\x3A2"],"\x05"=>[18,"\x3A2"],"\x06"=>[77,"\x3Aa"],"\x07"=>[18,"\x3Aa"],"\x08"=>[77,"\x3Ac"],"\x09"=>[18,"\x3Ac"],"\x0A"=>[77,"\x3Ae"],"\x0B"=>[18,"\x3Ae"],"\x0C"=>[77,"\x3Ai"],"\x0D"=>[18,"\x3Ai"],"\x0E"=>[77,"\x3Ao"],"\x0F"=>[18,"\x3Ao"],"\x10"=>[77,"\x3As"],"\x11"=>[18,"\x3As"],"\x12"=>[77,"\x3At"],"\x13"=>[18,"\x3At"],"\x14"=>[0,"\x3A\x20"],"\x15"=>[0,"\x3A\x25"],"\x16"=>[0,"\x3A-"],"\x17"=>[0,"\x3A."],"\x18"=>[0,"\x3A\x2F"],"\x19"=>[0,"\x3A3"],"\x1A"=>[0,"\x3A4"],"\x1B"=>[0,"\x3A5"],"\x1C"=>[0,"\x3A6"],"\x1D"=>[0,"\x3A7"],"\x1E"=>[0,"\x3A8"],"\x1F"=>[0,"\x3A9"],"\x20"=>[0,"\x3A\x3D"],"\x21"=>[0,"\x3AA"],"\x22"=>[0,"\x3A_"],"\x23"=>[0,"\x3Ab"],"\x24"=>[0,"\x3Ad"],"\x25"=>[0,"\x3Af"],"\x26"=>[0,"\x3Ag"],"\x27"=>[0,"\x3Ah"],"\x28"=>[0,"\x3Al"],"\x29"=>[0,"\x3Am"],"\x2A"=>[0,"\x3An"],"\x2B"=>[0,"\x3Ap"],"\x2C"=>[0,"\x3Ar"],"-"=>[0,"\x3Au"],"."=>[100,"\x3A"],"\x2F"=>[110,"\x3A"],[111,"\x3A"],[115,"\x3A"],[116,"\x3A"],[118,"\x3A"],[119,"\x3A"],[122,"\x3A"],[123,"\x3A"],[125,"\x3A"],[126,"\x3A"],[129,"\x3A"],"\x3A"=>[143,"\x3A"],"\x3B"=>[148,"\x3A"],"\x3C"=>[151,"\x3A"],"\x3D"=>[153,"\x3A"],"\x3E"=>[83,"\x3A"],"\x3F"=>[10,"\x3A"],"\x40"=>[77,"B0"],"A"=>[18,"B0"],"B"=>[77,"B1"],"C"=>[18,"B1"],"D"=>[77,"B2"],"E"=>[18,"B2"],"F"=>[77,"Ba"],"G"=>[18,"Ba"],"H"=>[77,"Bc"],"I"=>[18,"Bc"],"J"=>[77,"Be"],"K"=>[18,"Be"],"L"=>[77,"Bi"],"M"=>[18,"Bi"],"N"=>[77,"Bo"],"O"=>[18,"Bo"],"P"=>[77,"Bs"],"Q"=>[18,"Bs"],"R"=>[77,"Bt"],"S"=>[18,"Bt"],"T"=>[0,"B\x20"],"U"=>[0,"B\x25"],"V"=>[0,"B-"],"W"=>[0,"B."],"X"=>[0,"B\x2F"],"Y"=>[0,"B3"],"Z"=>[0,"B4"],"\x5B"=>[0,"B5"],"\x5C"=>[0,"B6"],"\x5D"=>[0,"B7"],"\x5E"=>[0,"B8"],"_"=>[0,"B9"],"\x60"=>[0,"B\x3D"],"a"=>[0,"BA"],"b"=>[0,"B_"],"c"=>[0,"Bb"],"d"=>[0,"Bd"],"e"=>[0,"Bf"],"f"=>[0,"Bg"],"g"=>[0,"Bh"],"h"=>[0,"Bl"],"i"=>[0,"Bm"],"j"=>[0,"Bn"],"k"=>[0,"Bp"],"l"=>[0,"Br"],"m"=>[0,"Bu"],"n"=>[100,"B"],"o"=>[110,"B"],"p"=>[111,"B"],"q"=>[115,"B"],"r"=>[116,"B"],"s"=>[118,"B"],"t"=>[119,"B"],"u"=>[122,"B"],"v"=>[123,"B"],"w"=>[125,"B"],"x"=>[126,"B"],"y"=>[129,"B"],"z"=>[143,"B"],"\x7B"=>[148,"B"],"\x7C"=>[151,"B"],"\x7D"=>[153,"B"],"~"=>[83,"B"],"\x7F"=>[10,"B"],"\x80"=>[77,"C0"],"\x81"=>[18,"C0"],"\x82"=>[77,"C1"],"\x83"=>[18,"C1"],"\x84"=>[77,"C2"],"\x85"=>[18,"C2"],"\x86"=>[77,"Ca"],"\x87"=>[18,"Ca"],"\x88"=>[77,"Cc"],"\x89"=>[18,"Cc"],"\x8A"=>[77,"Ce"],"\x8B"=>[18,"Ce"],"\x8C"=>[77,"Ci"],"\x8D"=>[18,"Ci"],"\x8E"=>[77,"Co"],"\x8F"=>[18,"Co"],"\x90"=>[77,"Cs"],"\x91"=>[18,"Cs"],"\x92"=>[77,"Ct"],"\x93"=>[18,"Ct"],"\x94"=>[0,"C\x20"],"\x95"=>[0,"C\x25"],"\x96"=>[0,"C-"],"\x97"=>[0,"C."],"\x98"=>[0,"C\x2F"],"\x99"=>[0,"C3"],"\x9A"=>[0,"C4"],"\x9B"=>[0,"C5"],"\x9C"=>[0,"C6"],"\x9D"=>[0,"C7"],"\x9E"=>[0,"C8"],"\x9F"=>[0,"C9"],"\xA0"=>[0,"C\x3D"],"\xA1"=>[0,"CA"],"\xA2"=>[0,"C_"],"\xA3"=>[0,"Cb"],"\xA4"=>[0,"Cd"],"\xA5"=>[0,"Cf"],"\xA6"=>[0,"Cg"],"\xA7"=>[0,"Ch"],"\xA8"=>[0,"Cl"],"\xA9"=>[0,"Cm"],"\xAA"=>[0,"Cn"],"\xAB"=>[0,"Cp"],"\xAC"=>[0,"Cr"],"\xAD"=>[0,"Cu"],"\xAE"=>[100,"C"],"\xAF"=>[110,"C"],"\xB0"=>[111,"C"],"\xB1"=>[115,"C"],"\xB2"=>[116,"C"],"\xB3"=>[118,"C"],"\xB4"=>[119,"C"],"\xB5"=>[122,"C"],"\xB6"=>[123,"C"],"\xB7"=>[125,"C"],"\xB8"=>[126,"C"],"\xB9"=>[129,"C"],"\xBA"=>[143,"C"],"\xBB"=>[148,"C"],"\xBC"=>[151,"C"],"\xBD"=>[153,"C"],"\xBE"=>[83,"C"],"\xBF"=>[10,"C"],"\xC0"=>[77,"D0"],"\xC1"=>[18,"D0"],"\xC2"=>[77,"D1"],"\xC3"=>[18,"D1"],"\xC4"=>[77,"D2"],"\xC5"=>[18,"D2"],"\xC6"=>[77,"Da"],"\xC7"=>[18,"Da"],"\xC8"=>[77,"Dc"],"\xC9"=>[18,"Dc"],"\xCA"=>[77,"De"],"\xCB"=>[18,"De"],"\xCC"=>[77,"Di"],"\xCD"=>[18,"Di"],"\xCE"=>[77,"Do"],"\xCF"=>[18,"Do"],"\xD0"=>[77,"Ds"],"\xD1"=>[18,"Ds"],"\xD2"=>[77,"Dt"],"\xD3"=>[18,"Dt"],"\xD4"=>[0,"D\x20"],"\xD5"=>[0,"D\x25"],"\xD6"=>[0,"D-"],"\xD7"=>[0,"D."],"\xD8"=>[0,"D\x2F"],"\xD9"=>[0,"D3"],"\xDA"=>[0,"D4"],"\xDB"=>[0,"D5"],"\xDC"=>[0,"D6"],"\xDD"=>[0,"D7"],"\xDE"=>[0,"D8"],"\xDF"=>[0,"D9"],"\xE0"=>[0,"D\x3D"],"\xE1"=>[0,"DA"],"\xE2"=>[0,"D_"],"\xE3"=>[0,"Db"],"\xE4"=>[0,"Dd"],"\xE5"=>[0,"Df"],"\xE6"=>[0,"Dg"],"\xE7"=>[0,"Dh"],"\xE8"=>[0,"Dl"],"\xE9"=>[0,"Dm"],"\xEA"=>[0,"Dn"],"\xEB"=>[0,"Dp"],"\xEC"=>[0,"Dr"],"\xED"=>[0,"Du"],"\xEE"=>[100,"D"],"\xEF"=>[110,"D"],"\xF0"=>[111,"D"],"\xF1"=>[115,"D"],"\xF2"=>[116,"D"],"\xF3"=>[118,"D"],"\xF4"=>[119,"D"],"\xF5"=>[122,"D"],"\xF6"=>[123,"D"],"\xF7"=>[125,"D"],"\xF8"=>[126,"D"],"\xF9"=>[129,"D"],"\xFA"=>[143,"D"],"\xFB"=>[148,"D"],"\xFC"=>[151,"D"],"\xFD"=>[153,"D"],"\xFE"=>[83,"D"],"\xFF"=>[10,"D"],],["\x00"=>[77,"r0"],"\x01"=>[18,"r0"],"\x02"=>[77,"r1"],"\x03"=>[18,"r1"],"\x04"=>[77,"r2"],"\x05"=>[18,"r2"],"\x06"=>[77,"ra"],"\x07"=>[18,"ra"],"\x08"=>[77,"rc"],"\x09"=>[18,"rc"],"\x0A"=>[77,"re"],"\x0B"=>[18,"re"],"\x0C"=>[77,"ri"],"\x0D"=>[18,"ri"],"\x0E"=>[77,"ro"],"\x0F"=>[18,"ro"],"\x10"=>[77,"rs"],"\x11"=>[18,"rs"],"\x12"=>[77,"rt"],"\x13"=>[18,"rt"],"\x14"=>[0,"r\x20"],"\x15"=>[0,"r\x25"],"\x16"=>[0,"r-"],"\x17"=>[0,"r."],"\x18"=>[0,"r\x2F"],"\x19"=>[0,"r3"],"\x1A"=>[0,"r4"],"\x1B"=>[0,"r5"],"\x1C"=>[0,"r6"],"\x1D"=>[0,"r7"],"\x1E"=>[0,"r8"],"\x1F"=>[0,"r9"],"\x20"=>[0,"r\x3D"],"\x21"=>[0,"rA"],"\x22"=>[0,"r_"],"\x23"=>[0,"rb"],"\x24"=>[0,"rd"],"\x25"=>[0,"rf"],"\x26"=>[0,"rg"],"\x27"=>[0,"rh"],"\x28"=>[0,"rl"],"\x29"=>[0,"rm"],"\x2A"=>[0,"rn"],"\x2B"=>[0,"rp"],"\x2C"=>[0,"rr"],"-"=>[0,"ru"],"."=>[100,"r"],"\x2F"=>[110,"r"],[111,"r"],[115,"r"],[116,"r"],[118,"r"],[119,"r"],[122,"r"],[123,"r"],[125,"r"],[126,"r"],[129,"r"],"\x3A"=>[143,"r"],"\x3B"=>[148,"r"],"\x3C"=>[151,"r"],"\x3D"=>[153,"r"],"\x3E"=>[83,"r"],"\x3F"=>[10,"r"],"\x40"=>[77,"u0"],"A"=>[18,"u0"],"B"=>[77,"u1"],"C"=>[18,"u1"],"D"=>[77,"u2"],"E"=>[18,"u2"],"F"=>[77,"ua"],"G"=>[18,"ua"],"H"=>[77,"uc"],"I"=>[18,"uc"],"J"=>[77,"ue"],"K"=>[18,"ue"],"L"=>[77,"ui"],"M"=>[18,"ui"],"N"=>[77,"uo"],"O"=>[18,"uo"],"P"=>[77,"us"],"Q"=>[18,"us"],"R"=>[77,"ut"],"S"=>[18,"ut"],"T"=>[0,"u\x20"],"U"=>[0,"u\x25"],"V"=>[0,"u-"],"W"=>[0,"u."],"X"=>[0,"u\x2F"],"Y"=>[0,"u3"],"Z"=>[0,"u4"],"\x5B"=>[0,"u5"],"\x5C"=>[0,"u6"],"\x5D"=>[0,"u7"],"\x5E"=>[0,"u8"],"_"=>[0,"u9"],"\x60"=>[0,"u\x3D"],"a"=>[0,"uA"],"b"=>[0,"u_"],"c"=>[0,"ub"],"d"=>[0,"ud"],"e"=>[0,"uf"],"f"=>[0,"ug"],"g"=>[0,"uh"],"h"=>[0,"ul"],"i"=>[0,"um"],"j"=>[0,"un"],"k"=>[0,"up"],"l"=>[0,"ur"],"m"=>[0,"uu"],"n"=>[100,"u"],"o"=>[110,"u"],"p"=>[111,"u"],"q"=>[115,"u"],"r"=>[116,"u"],"s"=>[118,"u"],"t"=>[119,"u"],"u"=>[122,"u"],"v"=>[123,"u"],"w"=>[125,"u"],"x"=>[126,"u"],"y"=>[129,"u"],"z"=>[143,"u"],"\x7B"=>[148,"u"],"\x7C"=>[151,"u"],"\x7D"=>[153,"u"],"~"=>[83,"u"],"\x7F"=>[10,"u"],"\x80"=>[0,"\x3A0"],"\x81"=>[0,"\x3A1"],"\x82"=>[0,"\x3A2"],"\x83"=>[0,"\x3Aa"],"\x84"=>[0,"\x3Ac"],"\x85"=>[0,"\x3Ae"],"\x86"=>[0,"\x3Ai"],"\x87"=>[0,"\x3Ao"],"\x88"=>[0,"\x3As"],"\x89"=>[0,"\x3At"],"\x8A"=>[73,"\x3A"],"\x8B"=>[88,"\x3A"],"\x8C"=>[89,"\x3A"],"\x8D"=>[96,"\x3A"],"\x8E"=>[97,"\x3A"],"\x8F"=>[99,"\x3A"],"\x90"=>[106,"\x3A"],"\x91"=>[136,"\x3A"],"\x92"=>[139,"\x3A"],"\x93"=>[141,"\x3A"],"\x94"=>[145,"\x3A"],"\x95"=>[147,"\x3A"],"\x96"=>[149,"\x3A"],"\x97"=>[101,"\x3A"],"\x98"=>[112,"\x3A"],"\x99"=>[117,"\x3A"],"\x9A"=>[120,"\x3A"],"\x9B"=>[124,"\x3A"],"\x9C"=>[127,"\x3A"],"\x9D"=>[144,"\x3A"],"\x9E"=>[152,"\x3A"],"\x9F"=>[11,"\x3A"],"\xA0"=>[0,"B0"],"\xA1"=>[0,"B1"],"\xA2"=>[0,"B2"],"\xA3"=>[0,"Ba"],"\xA4"=>[0,"Bc"],"\xA5"=>[0,"Be"],"\xA6"=>[0,"Bi"],"\xA7"=>[0,"Bo"],"\xA8"=>[0,"Bs"],"\xA9"=>[0,"Bt"],"\xAA"=>[73,"B"],"\xAB"=>[88,"B"],"\xAC"=>[89,"B"],"\xAD"=>[96,"B"],"\xAE"=>[97,"B"],"\xAF"=>[99,"B"],"\xB0"=>[106,"B"],"\xB1"=>[136,"B"],"\xB2"=>[139,"B"],"\xB3"=>[141,"B"],"\xB4"=>[145,"B"],"\xB5"=>[147,"B"],"\xB6"=>[149,"B"],"\xB7"=>[101,"B"],"\xB8"=>[112,"B"],"\xB9"=>[117,"B"],"\xBA"=>[120,"B"],"\xBB"=>[124,"B"],"\xBC"=>[127,"B"],"\xBD"=>[144,"B"],"\xBE"=>[152,"B"],"\xBF"=>[11,"B"],"\xC0"=>[0,"C0"],"\xC1"=>[0,"C1"],"\xC2"=>[0,"C2"],"\xC3"=>[0,"Ca"],"\xC4"=>[0,"Cc"],"\xC5"=>[0,"Ce"],"\xC6"=>[0,"Ci"],"\xC7"=>[0,"Co"],"\xC8"=>[0,"Cs"],"\xC9"=>[0,"Ct"],"\xCA"=>[73,"C"],"\xCB"=>[88,"C"],"\xCC"=>[89,"C"],"\xCD"=>[96,"C"],"\xCE"=>[97,"C"],"\xCF"=>[99,"C"],"\xD0"=>[106,"C"],"\xD1"=>[136,"C"],"\xD2"=>[139,"C"],"\xD3"=>[141,"C"],"\xD4"=>[145,"C"],"\xD5"=>[147,"C"],"\xD6"=>[149,"C"],"\xD7"=>[101,"C"],"\xD8"=>[112,"C"],"\xD9"=>[117,"C"],"\xDA"=>[120,"C"],"\xDB"=>[124,"C"],"\xDC"=>[127,"C"],"\xDD"=>[144,"C"],"\xDE"=>[152,"C"],"\xDF"=>[11,"C"],"\xE0"=>[0,"D0"],"\xE1"=>[0,"D1"],"\xE2"=>[0,"D2"],"\xE3"=>[0,"Da"],"\xE4"=>[0,"Dc"],"\xE5"=>[0,"De"],"\xE6"=>[0,"Di"],"\xE7"=>[0,"Do"],"\xE8"=>[0,"Ds"],"\xE9"=>[0,"Dt"],"\xEA"=>[73,"D"],"\xEB"=>[88,"D"],"\xEC"=>[89,"D"],"\xED"=>[96,"D"],"\xEE"=>[97,"D"],"\xEF"=>[99,"D"],"\xF0"=>[106,"D"],"\xF1"=>[136,"D"],"\xF2"=>[139,"D"],"\xF3"=>[141,"D"],"\xF4"=>[145,"D"],"\xF5"=>[147,"D"],"\xF6"=>[149,"D"],"\xF7"=>[101,"D"],"\xF8"=>[112,"D"],"\xF9"=>[117,"D"],"\xFA"=>[120,"D"],"\xFB"=>[124,"D"],"\xFC"=>[127,"D"],"\xFD"=>[144,"D"],"\xFE"=>[152,"D"],"\xFF"=>[11,"D"],],["\x00"=>[0,"l0"],"\x01"=>[0,"l1"],"\x02"=>[0,"l2"],"\x03"=>[0,"la"],"\x04"=>[0,"lc"],"\x05"=>[0,"le"],"\x06"=>[0,"li"],"\x07"=>[0,"lo"],"\x08"=>[0,"ls"],"\x09"=>[0,"lt"],"\x0A"=>[73,"l"],"\x0B"=>[88,"l"],"\x0C"=>[89,"l"],"\x0D"=>[96,"l"],"\x0E"=>[97,"l"],"\x0F"=>[99,"l"],"\x10"=>[106,"l"],"\x11"=>[136,"l"],"\x12"=>[139,"l"],"\x13"=>[141,"l"],"\x14"=>[145,"l"],"\x15"=>[147,"l"],"\x16"=>[149,"l"],"\x17"=>[101,"l"],"\x18"=>[112,"l"],"\x19"=>[117,"l"],"\x1A"=>[120,"l"],"\x1B"=>[124,"l"],"\x1C"=>[127,"l"],"\x1D"=>[144,"l"],"\x1E"=>[152,"l"],"\x1F"=>[11,"l"],"\x20"=>[0,"m0"],"\x21"=>[0,"m1"],"\x22"=>[0,"m2"],"\x23"=>[0,"ma"],"\x24"=>[0,"mc"],"\x25"=>[0,"me"],"\x26"=>[0,"mi"],"\x27"=>[0,"mo"],"\x28"=>[0,"ms"],"\x29"=>[0,"mt"],"\x2A"=>[73,"m"],"\x2B"=>[88,"m"],"\x2C"=>[89,"m"],"-"=>[96,"m"],"."=>[97,"m"],"\x2F"=>[99,"m"],[106,"m"],[136,"m"],[139,"m"],[141,"m"],[145,"m"],[147,"m"],[149,"m"],[101,"m"],[112,"m"],[117,"m"],"\x3A"=>[120,"m"],"\x3B"=>[124,"m"],"\x3C"=>[127,"m"],"\x3D"=>[144,"m"],"\x3E"=>[152,"m"],"\x3F"=>[11,"m"],"\x40"=>[0,"n0"],"A"=>[0,"n1"],"B"=>[0,"n2"],"C"=>[0,"na"],"D"=>[0,"nc"],"E"=>[0,"ne"],"F"=>[0,"ni"],"G"=>[0,"no"],"H"=>[0,"ns"],"I"=>[0,"nt"],"J"=>[73,"n"],"K"=>[88,"n"],"L"=>[89,"n"],"M"=>[96,"n"],"N"=>[97,"n"],"O"=>[99,"n"],"P"=>[106,"n"],"Q"=>[136,"n"],"R"=>[139,"n"],"S"=>[141,"n"],"T"=>[145,"n"],"U"=>[147,"n"],"V"=>[149,"n"],"W"=>[101,"n"],"X"=>[112,"n"],"Y"=>[117,"n"],"Z"=>[120,"n"],"\x5B"=>[124,"n"],"\x5C"=>[127,"n"],"\x5D"=>[144,"n"],"\x5E"=>[152,"n"],"_"=>[11,"n"],"\x60"=>[0,"p0"],"a"=>[0,"p1"],"b"=>[0,"p2"],"c"=>[0,"pa"],"d"=>[0,"pc"],"e"=>[0,"pe"],"f"=>[0,"pi"],"g"=>[0,"po"],"h"=>[0,"ps"],"i"=>[0,"pt"],"j"=>[73,"p"],"k"=>[88,"p"],"l"=>[89,"p"],"m"=>[96,"p"],"n"=>[97,"p"],"o"=>[99,"p"],"p"=>[106,"p"],"q"=>[136,"p"],"r"=>[139,"p"],"s"=>[141,"p"],"t"=>[145,"p"],"u"=>[147,"p"],"v"=>[149,"p"],"w"=>[101,"p"],"x"=>[112,"p"],"y"=>[117,"p"],"z"=>[120,"p"],"\x7B"=>[124,"p"],"\x7C"=>[127,"p"],"\x7D"=>[144,"p"],"~"=>[152,"p"],"\x7F"=>[11,"p"],"\x80"=>[0,"r0"],"\x81"=>[0,"r1"],"\x82"=>[0,"r2"],"\x83"=>[0,"ra"],"\x84"=>[0,"rc"],"\x85"=>[0,"re"],"\x86"=>[0,"ri"],"\x87"=>[0,"ro"],"\x88"=>[0,"rs"],"\x89"=>[0,"rt"],"\x8A"=>[73,"r"],"\x8B"=>[88,"r"],"\x8C"=>[89,"r"],"\x8D"=>[96,"r"],"\x8E"=>[97,"r"],"\x8F"=>[99,"r"],"\x90"=>[106,"r"],"\x91"=>[136,"r"],"\x92"=>[139,"r"],"\x93"=>[141,"r"],"\x94"=>[145,"r"],"\x95"=>[147,"r"],"\x96"=>[149,"r"],"\x97"=>[101,"r"],"\x98"=>[112,"r"],"\x99"=>[117,"r"],"\x9A"=>[120,"r"],"\x9B"=>[124,"r"],"\x9C"=>[127,"r"],"\x9D"=>[144,"r"],"\x9E"=>[152,"r"],"\x9F"=>[11,"r"],"\xA0"=>[0,"u0"],"\xA1"=>[0,"u1"],"\xA2"=>[0,"u2"],"\xA3"=>[0,"ua"],"\xA4"=>[0,"uc"],"\xA5"=>[0,"ue"],"\xA6"=>[0,"ui"],"\xA7"=>[0,"uo"],"\xA8"=>[0,"us"],"\xA9"=>[0,"ut"],"\xAA"=>[73,"u"],"\xAB"=>[88,"u"],"\xAC"=>[89,"u"],"\xAD"=>[96,"u"],"\xAE"=>[97,"u"],"\xAF"=>[99,"u"],"\xB0"=>[106,"u"],"\xB1"=>[136,"u"],"\xB2"=>[139,"u"],"\xB3"=>[141,"u"],"\xB4"=>[145,"u"],"\xB5"=>[147,"u"],"\xB6"=>[149,"u"],"\xB7"=>[101,"u"],"\xB8"=>[112,"u"],"\xB9"=>[117,"u"],"\xBA"=>[120,"u"],"\xBB"=>[124,"u"],"\xBC"=>[127,"u"],"\xBD"=>[144,"u"],"\xBE"=>[152,"u"],"\xBF"=>[11,"u"],"\xC0"=>[92,"\x3A"],"\xC1"=>[95,"\x3A"],"\xC2"=>[137,"\x3A"],"\xC3"=>[142,"\x3A"],"\xC4"=>[150,"\x3A"],"\xC5"=>[74,"\x3A"],"\xC6"=>[90,"\x3A"],"\xC7"=>[98,"\x3A"],"\xC8"=>[107,"\x3A"],"\xC9"=>[140,"\x3A"],"\xCA"=>[146,"\x3A"],"\xCB"=>[102,"\x3A"],"\xCC"=>[113,"\x3A"],"\xCD"=>[121,"\x3A"],"\xCE"=>[128,"\x3A"],"\xCF"=>[12,"\x3A"],"\xD0"=>[92,"B"],"\xD1"=>[95,"B"],"\xD2"=>[137,"B"],"\xD3"=>[142,"B"],"\xD4"=>[150,"B"],"\xD5"=>[74,"B"],"\xD6"=>[90,"B"],"\xD7"=>[98,"B"],"\xD8"=>[107,"B"],"\xD9"=>[140,"B"],"\xDA"=>[146,"B"],"\xDB"=>[102,"B"],"\xDC"=>[113,"B"],"\xDD"=>[121,"B"],"\xDE"=>[128,"B"],"\xDF"=>[12,"B"],"\xE0"=>[92,"C"],"\xE1"=>[95,"C"],"\xE2"=>[137,"C"],"\xE3"=>[142,"C"],"\xE4"=>[150,"C"],"\xE5"=>[74,"C"],"\xE6"=>[90,"C"],"\xE7"=>[98,"C"],"\xE8"=>[107,"C"],"\xE9"=>[140,"C"],"\xEA"=>[146,"C"],"\xEB"=>[102,"C"],"\xEC"=>[113,"C"],"\xED"=>[121,"C"],"\xEE"=>[128,"C"],"\xEF"=>[12,"C"],"\xF0"=>[92,"D"],"\xF1"=>[95,"D"],"\xF2"=>[137,"D"],"\xF3"=>[142,"D"],"\xF4"=>[150,"D"],"\xF5"=>[74,"D"],"\xF6"=>[90,"D"],"\xF7"=>[98,"D"],"\xF8"=>[107,"D"],"\xF9"=>[140,"D"],"\xFA"=>[146,"D"],"\xFB"=>[102,"D"],"\xFC"=>[113,"D"],"\xFD"=>[121,"D"],"\xFE"=>[128,"D"],"\xFF"=>[12,"D"],],["\x00"=>[92,"\x3D"],"\x01"=>[95,"\x3D"],"\x02"=>[137,"\x3D"],"\x03"=>[142,"\x3D"],"\x04"=>[150,"\x3D"],"\x05"=>[74,"\x3D"],"\x06"=>[90,"\x3D"],"\x07"=>[98,"\x3D"],"\x08"=>[107,"\x3D"],"\x09"=>[140,"\x3D"],"\x0A"=>[146,"\x3D"],"\x0B"=>[102,"\x3D"],"\x0C"=>[113,"\x3D"],"\x0D"=>[121,"\x3D"],"\x0E"=>[128,"\x3D"],"\x0F"=>[12,"\x3D"],"\x10"=>[92,"A"],"\x11"=>[95,"A"],"\x12"=>[137,"A"],"\x13"=>[142,"A"],"\x14"=>[150,"A"],"\x15"=>[74,"A"],"\x16"=>[90,"A"],"\x17"=>[98,"A"],"\x18"=>[107,"A"],"\x19"=>[140,"A"],"\x1A"=>[146,"A"],"\x1B"=>[102,"A"],"\x1C"=>[113,"A"],"\x1D"=>[121,"A"],"\x1E"=>[128,"A"],"\x1F"=>[12,"A"],"\x20"=>[92,"_"],"\x21"=>[95,"_"],"\x22"=>[137,"_"],"\x23"=>[142,"_"],"\x24"=>[150,"_"],"\x25"=>[74,"_"],"\x26"=>[90,"_"],"\x27"=>[98,"_"],"\x28"=>[107,"_"],"\x29"=>[140,"_"],"\x2A"=>[146,"_"],"\x2B"=>[102,"_"],"\x2C"=>[113,"_"],"-"=>[121,"_"],"."=>[128,"_"],"\x2F"=>[12,"_"],[92,"b"],[95,"b"],[137,"b"],[142,"b"],[150,"b"],[74,"b"],[90,"b"],[98,"b"],[107,"b"],[140,"b"],"\x3A"=>[146,"b"],"\x3B"=>[102,"b"],"\x3C"=>[113,"b"],"\x3D"=>[121,"b"],"\x3E"=>[128,"b"],"\x3F"=>[12,"b"],"\x40"=>[92,"d"],"A"=>[95,"d"],"B"=>[137,"d"],"C"=>[142,"d"],"D"=>[150,"d"],"E"=>[74,"d"],"F"=>[90,"d"],"G"=>[98,"d"],"H"=>[107,"d"],"I"=>[140,"d"],"J"=>[146,"d"],"K"=>[102,"d"],"L"=>[113,"d"],"M"=>[121,"d"],"N"=>[128,"d"],"O"=>[12,"d"],"P"=>[92,"f"],"Q"=>[95,"f"],"R"=>[137,"f"],"S"=>[142,"f"],"T"=>[150,"f"],"U"=>[74,"f"],"V"=>[90,"f"],"W"=>[98,"f"],"X"=>[107,"f"],"Y"=>[140,"f"],"Z"=>[146,"f"],"\x5B"=>[102,"f"],"\x5C"=>[113,"f"],"\x5D"=>[121,"f"],"\x5E"=>[128,"f"],"_"=>[12,"f"],"\x60"=>[92,"g"],"a"=>[95,"g"],"b"=>[137,"g"],"c"=>[142,"g"],"d"=>[150,"g"],"e"=>[74,"g"],"f"=>[90,"g"],"g"=>[98,"g"],"h"=>[107,"g"],"i"=>[140,"g"],"j"=>[146,"g"],"k"=>[102,"g"],"l"=>[113,"g"],"m"=>[121,"g"],"n"=>[128,"g"],"o"=>[12,"g"],"p"=>[92,"h"],"q"=>[95,"h"],"r"=>[137,"h"],"s"=>[142,"h"],"t"=>[150,"h"],"u"=>[74,"h"],"v"=>[90,"h"],"w"=>[98,"h"],"x"=>[107,"h"],"y"=>[140,"h"],"z"=>[146,"h"],"\x7B"=>[102,"h"],"\x7C"=>[113,"h"],"\x7D"=>[121,"h"],"~"=>[128,"h"],"\x7F"=>[12,"h"],"\x80"=>[92,"l"],"\x81"=>[95,"l"],"\x82"=>[137,"l"],"\x83"=>[142,"l"],"\x84"=>[150,"l"],"\x85"=>[74,"l"],"\x86"=>[90,"l"],"\x87"=>[98,"l"],"\x88"=>[107,"l"],"\x89"=>[140,"l"],"\x8A"=>[146,"l"],"\x8B"=>[102,"l"],"\x8C"=>[113,"l"],"\x8D"=>[121,"l"],"\x8E"=>[128,"l"],"\x8F"=>[12,"l"],"\x90"=>[92,"m"],"\x91"=>[95,"m"],"\x92"=>[137,"m"],"\x93"=>[142,"m"],"\x94"=>[150,"m"],"\x95"=>[74,"m"],"\x96"=>[90,"m"],"\x97"=>[98,"m"],"\x98"=>[107,"m"],"\x99"=>[140,"m"],"\x9A"=>[146,"m"],"\x9B"=>[102,"m"],"\x9C"=>[113,"m"],"\x9D"=>[121,"m"],"\x9E"=>[128,"m"],"\x9F"=>[12,"m"],"\xA0"=>[92,"n"],"\xA1"=>[95,"n"],"\xA2"=>[137,"n"],"\xA3"=>[142,"n"],"\xA4"=>[150,"n"],"\xA5"=>[74,"n"],"\xA6"=>[90,"n"],"\xA7"=>[98,"n"],"\xA8"=>[107,"n"],"\xA9"=>[140,"n"],"\xAA"=>[146,"n"],"\xAB"=>[102,"n"],"\xAC"=>[113,"n"],"\xAD"=>[121,"n"],"\xAE"=>[128,"n"],"\xAF"=>[12,"n"],"\xB0"=>[92,"p"],"\xB1"=>[95,"p"],"\xB2"=>[137,"p"],"\xB3"=>[142,"p"],"\xB4"=>[150,"p"],"\xB5"=>[74,"p"],"\xB6"=>[90,"p"],"\xB7"=>[98,"p"],"\xB8"=>[107,"p"],"\xB9"=>[140,"p"],"\xBA"=>[146,"p"],"\xBB"=>[102,"p"],"\xBC"=>[113,"p"],"\xBD"=>[121,"p"],"\xBE"=>[128,"p"],"\xBF"=>[12,"p"],"\xC0"=>[92,"r"],"\xC1"=>[95,"r"],"\xC2"=>[137,"r"],"\xC3"=>[142,"r"],"\xC4"=>[150,"r"],"\xC5"=>[74,"r"],"\xC6"=>[90,"r"],"\xC7"=>[98,"r"],"\xC8"=>[107,"r"],"\xC9"=>[140,"r"],"\xCA"=>[146,"r"],"\xCB"=>[102,"r"],"\xCC"=>[113,"r"],"\xCD"=>[121,"r"],"\xCE"=>[128,"r"],"\xCF"=>[12,"r"],"\xD0"=>[92,"u"],"\xD1"=>[95,"u"],"\xD2"=>[137,"u"],"\xD3"=>[142,"u"],"\xD4"=>[150,"u"],"\xD5"=>[74,"u"],"\xD6"=>[90,"u"],"\xD7"=>[98,"u"],"\xD8"=>[107,"u"],"\xD9"=>[140,"u"],"\xDA"=>[146,"u"],"\xDB"=>[102,"u"],"\xDC"=>[113,"u"],"\xDD"=>[121,"u"],"\xDE"=>[128,"u"],"\xDF"=>[12,"u"],"\xE0"=>[93,"\x3A"],"\xE1"=>[138,"\x3A"],"\xE2"=>[75,"\x3A"],"\xE3"=>[91,"\x3A"],"\xE4"=>[108,"\x3A"],"\xE5"=>[103,"\x3A"],"\xE6"=>[114,"\x3A"],"\xE7"=>[14,"\x3A"],"\xE8"=>[93,"B"],"\xE9"=>[138,"B"],"\xEA"=>[75,"B"],"\xEB"=>[91,"B"],"\xEC"=>[108,"B"],"\xED"=>[103,"B"],"\xEE"=>[114,"B"],"\xEF"=>[14,"B"],"\xF0"=>[93,"C"],"\xF1"=>[138,"C"],"\xF2"=>[75,"C"],"\xF3"=>[91,"C"],"\xF4"=>[108,"C"],"\xF5"=>[103,"C"],"\xF6"=>[114,"C"],"\xF7"=>[14,"C"],"\xF8"=>[93,"D"],"\xF9"=>[138,"D"],"\xFA"=>[75,"D"],"\xFB"=>[91,"D"],"\xFC"=>[108,"D"],"\xFD"=>[103,"D"],"\xFE"=>[114,"D"],"\xFF"=>[14,"D"],],["\x00"=>[94,"\x3C0"],"\x01"=>[76,"\x3C0"],"\x02"=>[104,"\x3C0"],"\x03"=>[16,"\x3C0"],"\x04"=>[94,"\x3C1"],"\x05"=>[76,"\x3C1"],"\x06"=>[104,"\x3C1"],"\x07"=>[16,"\x3C1"],"\x08"=>[94,"\x3C2"],"\x09"=>[76,"\x3C2"],"\x0A"=>[104,"\x3C2"],"\x0B"=>[16,"\x3C2"],"\x0C"=>[94,"\x3Ca"],"\x0D"=>[76,"\x3Ca"],"\x0E"=>[104,"\x3Ca"],"\x0F"=>[16,"\x3Ca"],"\x10"=>[94,"\x3Cc"],"\x11"=>[76,"\x3Cc"],"\x12"=>[104,"\x3Cc"],"\x13"=>[16,"\x3Cc"],"\x14"=>[94,"\x3Ce"],"\x15"=>[76,"\x3Ce"],"\x16"=>[104,"\x3Ce"],"\x17"=>[16,"\x3Ce"],"\x18"=>[94,"\x3Ci"],"\x19"=>[76,"\x3Ci"],"\x1A"=>[104,"\x3Ci"],"\x1B"=>[16,"\x3Ci"],"\x1C"=>[94,"\x3Co"],"\x1D"=>[76,"\x3Co"],"\x1E"=>[104,"\x3Co"],"\x1F"=>[16,"\x3Co"],"\x20"=>[94,"\x3Cs"],"\x21"=>[76,"\x3Cs"],"\x22"=>[104,"\x3Cs"],"\x23"=>[16,"\x3Cs"],"\x24"=>[94,"\x3Ct"],"\x25"=>[76,"\x3Ct"],"\x26"=>[104,"\x3Ct"],"\x27"=>[16,"\x3Ct"],"\x28"=>[77,"\x3C\x20"],"\x29"=>[18,"\x3C\x20"],"\x2A"=>[77,"\x3C\x25"],"\x2B"=>[18,"\x3C\x25"],"\x2C"=>[77,"\x3C-"],"-"=>[18,"\x3C-"],"."=>[77,"\x3C."],"\x2F"=>[18,"\x3C."],[77,"\x3C\x2F"],[18,"\x3C\x2F"],[77,"\x3C3"],[18,"\x3C3"],[77,"\x3C4"],[18,"\x3C4"],[77,"\x3C5"],[18,"\x3C5"],[77,"\x3C6"],[18,"\x3C6"],"\x3A"=>[77,"\x3C7"],"\x3B"=>[18,"\x3C7"],"\x3C"=>[77,"\x3C8"],"\x3D"=>[18,"\x3C8"],"\x3E"=>[77,"\x3C9"],"\x3F"=>[18,"\x3C9"],"\x40"=>[77,"\x3C\x3D"],"A"=>[18,"\x3C\x3D"],"B"=>[77,"\x3CA"],"C"=>[18,"\x3CA"],"D"=>[77,"\x3C_"],"E"=>[18,"\x3C_"],"F"=>[77,"\x3Cb"],"G"=>[18,"\x3Cb"],"H"=>[77,"\x3Cd"],"I"=>[18,"\x3Cd"],"J"=>[77,"\x3Cf"],"K"=>[18,"\x3Cf"],"L"=>[77,"\x3Cg"],"M"=>[18,"\x3Cg"],"N"=>[77,"\x3Ch"],"O"=>[18,"\x3Ch"],"P"=>[77,"\x3Cl"],"Q"=>[18,"\x3Cl"],"R"=>[77,"\x3Cm"],"S"=>[18,"\x3Cm"],"T"=>[77,"\x3Cn"],"U"=>[18,"\x3Cn"],"V"=>[77,"\x3Cp"],"W"=>[18,"\x3Cp"],"X"=>[77,"\x3Cr"],"Y"=>[18,"\x3Cr"],"Z"=>[77,"\x3Cu"],"\x5B"=>[18,"\x3Cu"],"\x5C"=>[0,"\x3C\x3A"],"\x5D"=>[0,"\x3CB"],"\x5E"=>[0,"\x3CC"],"_"=>[0,"\x3CD"],"\x60"=>[0,"\x3CE"],"a"=>[0,"\x3CF"],"b"=>[0,"\x3CG"],"c"=>[0,"\x3CH"],"d"=>[0,"\x3CI"],"e"=>[0,"\x3CJ"],"f"=>[0,"\x3CK"],"g"=>[0,"\x3CL"],"h"=>[0,"\x3CM"],"i"=>[0,"\x3CN"],"j"=>[0,"\x3CO"],"k"=>[0,"\x3CP"],"l"=>[0,"\x3CQ"],"m"=>[0,"\x3CR"],"n"=>[0,"\x3CS"],"o"=>[0,"\x3CT"],"p"=>[0,"\x3CU"],"q"=>[0,"\x3CV"],"r"=>[0,"\x3CW"],"s"=>[0,"\x3CY"],"t"=>[0,"\x3Cj"],"u"=>[0,"\x3Ck"],"v"=>[0,"\x3Cq"],"w"=>[0,"\x3Cv"],"x"=>[0,"\x3Cw"],"y"=>[0,"\x3Cx"],"z"=>[0,"\x3Cy"],"\x7B"=>[0,"\x3Cz"],"\x7C"=>[82,"\x3C"],"\x7D"=>[87,"\x3C"],"~"=>[130,"\x3C"],"\x7F"=>[9,"\x3C"],"\x80"=>[94,"\x600"],"\x81"=>[76,"\x600"],"\x82"=>[104,"\x600"],"\x83"=>[16,"\x600"],"\x84"=>[94,"\x601"],"\x85"=>[76,"\x601"],"\x86"=>[104,"\x601"],"\x87"=>[16,"\x601"],"\x88"=>[94,"\x602"],"\x89"=>[76,"\x602"],"\x8A"=>[104,"\x602"],"\x8B"=>[16,"\x602"],"\x8C"=>[94,"\x60a"],"\x8D"=>[76,"\x60a"],"\x8E"=>[104,"\x60a"],"\x8F"=>[16,"\x60a"],"\x90"=>[94,"\x60c"],"\x91"=>[76,"\x60c"],"\x92"=>[104,"\x60c"],"\x93"=>[16,"\x60c"],"\x94"=>[94,"\x60e"],"\x95"=>[76,"\x60e"],"\x96"=>[104,"\x60e"],"\x97"=>[16,"\x60e"],"\x98"=>[94,"\x60i"],"\x99"=>[76,"\x60i"],"\x9A"=>[104,"\x60i"],"\x9B"=>[16,"\x60i"],"\x9C"=>[94,"\x60o"],"\x9D"=>[76,"\x60o"],"\x9E"=>[104,"\x60o"],"\x9F"=>[16,"\x60o"],"\xA0"=>[94,"\x60s"],"\xA1"=>[76,"\x60s"],"\xA2"=>[104,"\x60s"],"\xA3"=>[16,"\x60s"],"\xA4"=>[94,"\x60t"],"\xA5"=>[76,"\x60t"],"\xA6"=>[104,"\x60t"],"\xA7"=>[16,"\x60t"],"\xA8"=>[77,"\x60\x20"],"\xA9"=>[18,"\x60\x20"],"\xAA"=>[77,"\x60\x25"],"\xAB"=>[18,"\x60\x25"],"\xAC"=>[77,"\x60-"],"\xAD"=>[18,"\x60-"],"\xAE"=>[77,"\x60."],"\xAF"=>[18,"\x60."],"\xB0"=>[77,"\x60\x2F"],"\xB1"=>[18,"\x60\x2F"],"\xB2"=>[77,"\x603"],"\xB3"=>[18,"\x603"],"\xB4"=>[77,"\x604"],"\xB5"=>[18,"\x604"],"\xB6"=>[77,"\x605"],"\xB7"=>[18,"\x605"],"\xB8"=>[77,"\x606"],"\xB9"=>[18,"\x606"],"\xBA"=>[77,"\x607"],"\xBB"=>[18,"\x607"],"\xBC"=>[77,"\x608"],"\xBD"=>[18,"\x608"],"\xBE"=>[77,"\x609"],"\xBF"=>[18,"\x609"],"\xC0"=>[77,"\x60\x3D"],"\xC1"=>[18,"\x60\x3D"],"\xC2"=>[77,"\x60A"],"\xC3"=>[18,"\x60A"],"\xC4"=>[77,"\x60_"],"\xC5"=>[18,"\x60_"],"\xC6"=>[77,"\x60b"],"\xC7"=>[18,"\x60b"],"\xC8"=>[77,"\x60d"],"\xC9"=>[18,"\x60d"],"\xCA"=>[77,"\x60f"],"\xCB"=>[18,"\x60f"],"\xCC"=>[77,"\x60g"],"\xCD"=>[18,"\x60g"],"\xCE"=>[77,"\x60h"],"\xCF"=>[18,"\x60h"],"\xD0"=>[77,"\x60l"],"\xD1"=>[18,"\x60l"],"\xD2"=>[77,"\x60m"],"\xD3"=>[18,"\x60m"],"\xD4"=>[77,"\x60n"],"\xD5"=>[18,"\x60n"],"\xD6"=>[77,"\x60p"],"\xD7"=>[18,"\x60p"],"\xD8"=>[77,"\x60r"],"\xD9"=>[18,"\x60r"],"\xDA"=>[77,"\x60u"],"\xDB"=>[18,"\x60u"],"\xDC"=>[0,"\x60\x3A"],"\xDD"=>[0,"\x60B"],"\xDE"=>[0,"\x60C"],"\xDF"=>[0,"\x60D"],"\xE0"=>[0,"\x60E"],"\xE1"=>[0,"\x60F"],"\xE2"=>[0,"\x60G"],"\xE3"=>[0,"\x60H"],"\xE4"=>[0,"\x60I"],"\xE5"=>[0,"\x60J"],"\xE6"=>[0,"\x60K"],"\xE7"=>[0,"\x60L"],"\xE8"=>[0,"\x60M"],"\xE9"=>[0,"\x60N"],"\xEA"=>[0,"\x60O"],"\xEB"=>[0,"\x60P"],"\xEC"=>[0,"\x60Q"],"\xED"=>[0,"\x60R"],"\xEE"=>[0,"\x60S"],"\xEF"=>[0,"\x60T"],"\xF0"=>[0,"\x60U"],"\xF1"=>[0,"\x60V"],"\xF2"=>[0,"\x60W"],"\xF3"=>[0,"\x60Y"],"\xF4"=>[0,"\x60j"],"\xF5"=>[0,"\x60k"],"\xF6"=>[0,"\x60q"],"\xF7"=>[0,"\x60v"],"\xF8"=>[0,"\x60w"],"\xF9"=>[0,"\x60x"],"\xFA"=>[0,"\x60y"],"\xFB"=>[0,"\x60z"],"\xFC"=>[82,"\x60"],"\xFD"=>[87,"\x60"],"\xFE"=>[130,"\x60"],"\xFF"=>[9,"\x60"],],["\x00"=>[94,"\x3D0"],"\x01"=>[76,"\x3D0"],"\x02"=>[104,"\x3D0"],"\x03"=>[16,"\x3D0"],"\x04"=>[94,"\x3D1"],"\x05"=>[76,"\x3D1"],"\x06"=>[104,"\x3D1"],"\x07"=>[16,"\x3D1"],"\x08"=>[94,"\x3D2"],"\x09"=>[76,"\x3D2"],"\x0A"=>[104,"\x3D2"],"\x0B"=>[16,"\x3D2"],"\x0C"=>[94,"\x3Da"],"\x0D"=>[76,"\x3Da"],"\x0E"=>[104,"\x3Da"],"\x0F"=>[16,"\x3Da"],"\x10"=>[94,"\x3Dc"],"\x11"=>[76,"\x3Dc"],"\x12"=>[104,"\x3Dc"],"\x13"=>[16,"\x3Dc"],"\x14"=>[94,"\x3De"],"\x15"=>[76,"\x3De"],"\x16"=>[104,"\x3De"],"\x17"=>[16,"\x3De"],"\x18"=>[94,"\x3Di"],"\x19"=>[76,"\x3Di"],"\x1A"=>[104,"\x3Di"],"\x1B"=>[16,"\x3Di"],"\x1C"=>[94,"\x3Do"],"\x1D"=>[76,"\x3Do"],"\x1E"=>[104,"\x3Do"],"\x1F"=>[16,"\x3Do"],"\x20"=>[94,"\x3Ds"],"\x21"=>[76,"\x3Ds"],"\x22"=>[104,"\x3Ds"],"\x23"=>[16,"\x3Ds"],"\x24"=>[94,"\x3Dt"],"\x25"=>[76,"\x3Dt"],"\x26"=>[104,"\x3Dt"],"\x27"=>[16,"\x3Dt"],"\x28"=>[77,"\x3D\x20"],"\x29"=>[18,"\x3D\x20"],"\x2A"=>[77,"\x3D\x25"],"\x2B"=>[18,"\x3D\x25"],"\x2C"=>[77,"\x3D-"],"-"=>[18,"\x3D-"],"."=>[77,"\x3D."],"\x2F"=>[18,"\x3D."],[77,"\x3D\x2F"],[18,"\x3D\x2F"],[77,"\x3D3"],[18,"\x3D3"],[77,"\x3D4"],[18,"\x3D4"],[77,"\x3D5"],[18,"\x3D5"],[77,"\x3D6"],[18,"\x3D6"],"\x3A"=>[77,"\x3D7"],"\x3B"=>[18,"\x3D7"],"\x3C"=>[77,"\x3D8"],"\x3D"=>[18,"\x3D8"],"\x3E"=>[77,"\x3D9"],"\x3F"=>[18,"\x3D9"],"\x40"=>[77,"\x3D\x3D"],"A"=>[18,"\x3D\x3D"],"B"=>[77,"\x3DA"],"C"=>[18,"\x3DA"],"D"=>[77,"\x3D_"],"E"=>[18,"\x3D_"],"F"=>[77,"\x3Db"],"G"=>[18,"\x3Db"],"H"=>[77,"\x3Dd"],"I"=>[18,"\x3Dd"],"J"=>[77,"\x3Df"],"K"=>[18,"\x3Df"],"L"=>[77,"\x3Dg"],"M"=>[18,"\x3Dg"],"N"=>[77,"\x3Dh"],"O"=>[18,"\x3Dh"],"P"=>[77,"\x3Dl"],"Q"=>[18,"\x3Dl"],"R"=>[77,"\x3Dm"],"S"=>[18,"\x3Dm"],"T"=>[77,"\x3Dn"],"U"=>[18,"\x3Dn"],"V"=>[77,"\x3Dp"],"W"=>[18,"\x3Dp"],"X"=>[77,"\x3Dr"],"Y"=>[18,"\x3Dr"],"Z"=>[77,"\x3Du"],"\x5B"=>[18,"\x3Du"],"\x5C"=>[0,"\x3D\x3A"],"\x5D"=>[0,"\x3DB"],"\x5E"=>[0,"\x3DC"],"_"=>[0,"\x3DD"],"\x60"=>[0,"\x3DE"],"a"=>[0,"\x3DF"],"b"=>[0,"\x3DG"],"c"=>[0,"\x3DH"],"d"=>[0,"\x3DI"],"e"=>[0,"\x3DJ"],"f"=>[0,"\x3DK"],"g"=>[0,"\x3DL"],"h"=>[0,"\x3DM"],"i"=>[0,"\x3DN"],"j"=>[0,"\x3DO"],"k"=>[0,"\x3DP"],"l"=>[0,"\x3DQ"],"m"=>[0,"\x3DR"],"n"=>[0,"\x3DS"],"o"=>[0,"\x3DT"],"p"=>[0,"\x3DU"],"q"=>[0,"\x3DV"],"r"=>[0,"\x3DW"],"s"=>[0,"\x3DY"],"t"=>[0,"\x3Dj"],"u"=>[0,"\x3Dk"],"v"=>[0,"\x3Dq"],"w"=>[0,"\x3Dv"],"x"=>[0,"\x3Dw"],"y"=>[0,"\x3Dx"],"z"=>[0,"\x3Dy"],"\x7B"=>[0,"\x3Dz"],"\x7C"=>[82,"\x3D"],"\x7D"=>[87,"\x3D"],"~"=>[130,"\x3D"],"\x7F"=>[9,"\x3D"],"\x80"=>[94,"A0"],"\x81"=>[76,"A0"],"\x82"=>[104,"A0"],"\x83"=>[16,"A0"],"\x84"=>[94,"A1"],"\x85"=>[76,"A1"],"\x86"=>[104,"A1"],"\x87"=>[16,"A1"],"\x88"=>[94,"A2"],"\x89"=>[76,"A2"],"\x8A"=>[104,"A2"],"\x8B"=>[16,"A2"],"\x8C"=>[94,"Aa"],"\x8D"=>[76,"Aa"],"\x8E"=>[104,"Aa"],"\x8F"=>[16,"Aa"],"\x90"=>[94,"Ac"],"\x91"=>[76,"Ac"],"\x92"=>[104,"Ac"],"\x93"=>[16,"Ac"],"\x94"=>[94,"Ae"],"\x95"=>[76,"Ae"],"\x96"=>[104,"Ae"],"\x97"=>[16,"Ae"],"\x98"=>[94,"Ai"],"\x99"=>[76,"Ai"],"\x9A"=>[104,"Ai"],"\x9B"=>[16,"Ai"],"\x9C"=>[94,"Ao"],"\x9D"=>[76,"Ao"],"\x9E"=>[104,"Ao"],"\x9F"=>[16,"Ao"],"\xA0"=>[94,"As"],"\xA1"=>[76,"As"],"\xA2"=>[104,"As"],"\xA3"=>[16,"As"],"\xA4"=>[94,"At"],"\xA5"=>[76,"At"],"\xA6"=>[104,"At"],"\xA7"=>[16,"At"],"\xA8"=>[77,"A\x20"],"\xA9"=>[18,"A\x20"],"\xAA"=>[77,"A\x25"],"\xAB"=>[18,"A\x25"],"\xAC"=>[77,"A-"],"\xAD"=>[18,"A-"],"\xAE"=>[77,"A."],"\xAF"=>[18,"A."],"\xB0"=>[77,"A\x2F"],"\xB1"=>[18,"A\x2F"],"\xB2"=>[77,"A3"],"\xB3"=>[18,"A3"],"\xB4"=>[77,"A4"],"\xB5"=>[18,"A4"],"\xB6"=>[77,"A5"],"\xB7"=>[18,"A5"],"\xB8"=>[77,"A6"],"\xB9"=>[18,"A6"],"\xBA"=>[77,"A7"],"\xBB"=>[18,"A7"],"\xBC"=>[77,"A8"],"\xBD"=>[18,"A8"],"\xBE"=>[77,"A9"],"\xBF"=>[18,"A9"],"\xC0"=>[77,"A\x3D"],"\xC1"=>[18,"A\x3D"],"\xC2"=>[77,"AA"],"\xC3"=>[18,"AA"],"\xC4"=>[77,"A_"],"\xC5"=>[18,"A_"],"\xC6"=>[77,"Ab"],"\xC7"=>[18,"Ab"],"\xC8"=>[77,"Ad"],"\xC9"=>[18,"Ad"],"\xCA"=>[77,"Af"],"\xCB"=>[18,"Af"],"\xCC"=>[77,"Ag"],"\xCD"=>[18,"Ag"],"\xCE"=>[77,"Ah"],"\xCF"=>[18,"Ah"],"\xD0"=>[77,"Al"],"\xD1"=>[18,"Al"],"\xD2"=>[77,"Am"],"\xD3"=>[18,"Am"],"\xD4"=>[77,"An"],"\xD5"=>[18,"An"],"\xD6"=>[77,"Ap"],"\xD7"=>[18,"Ap"],"\xD8"=>[77,"Ar"],"\xD9"=>[18,"Ar"],"\xDA"=>[77,"Au"],"\xDB"=>[18,"Au"],"\xDC"=>[0,"A\x3A"],"\xDD"=>[0,"AB"],"\xDE"=>[0,"AC"],"\xDF"=>[0,"AD"],"\xE0"=>[0,"AE"],"\xE1"=>[0,"AF"],"\xE2"=>[0,"AG"],"\xE3"=>[0,"AH"],"\xE4"=>[0,"AI"],"\xE5"=>[0,"AJ"],"\xE6"=>[0,"AK"],"\xE7"=>[0,"AL"],"\xE8"=>[0,"AM"],"\xE9"=>[0,"AN"],"\xEA"=>[0,"AO"],"\xEB"=>[0,"AP"],"\xEC"=>[0,"AQ"],"\xED"=>[0,"AR"],"\xEE"=>[0,"AS"],"\xEF"=>[0,"AT"],"\xF0"=>[0,"AU"],"\xF1"=>[0,"AV"],"\xF2"=>[0,"AW"],"\xF3"=>[0,"AY"],"\xF4"=>[0,"Aj"],"\xF5"=>[0,"Ak"],"\xF6"=>[0,"Aq"],"\xF7"=>[0,"Av"],"\xF8"=>[0,"Aw"],"\xF9"=>[0,"Ax"],"\xFA"=>[0,"Ay"],"\xFB"=>[0,"Az"],"\xFC"=>[82,"A"],"\xFD"=>[87,"A"],"\xFE"=>[130,"A"],"\xFF"=>[9,"A"],],["\x00"=>[77,"\x3D0"],"\x01"=>[18,"\x3D0"],"\x02"=>[77,"\x3D1"],"\x03"=>[18,"\x3D1"],"\x04"=>[77,"\x3D2"],"\x05"=>[18,"\x3D2"],"\x06"=>[77,"\x3Da"],"\x07"=>[18,"\x3Da"],"\x08"=>[77,"\x3Dc"],"\x09"=>[18,"\x3Dc"],"\x0A"=>[77,"\x3De"],"\x0B"=>[18,"\x3De"],"\x0C"=>[77,"\x3Di"],"\x0D"=>[18,"\x3Di"],"\x0E"=>[77,"\x3Do"],"\x0F"=>[18,"\x3Do"],"\x10"=>[77,"\x3Ds"],"\x11"=>[18,"\x3Ds"],"\x12"=>[77,"\x3Dt"],"\x13"=>[18,"\x3Dt"],"\x14"=>[0,"\x3D\x20"],"\x15"=>[0,"\x3D\x25"],"\x16"=>[0,"\x3D-"],"\x17"=>[0,"\x3D."],"\x18"=>[0,"\x3D\x2F"],"\x19"=>[0,"\x3D3"],"\x1A"=>[0,"\x3D4"],"\x1B"=>[0,"\x3D5"],"\x1C"=>[0,"\x3D6"],"\x1D"=>[0,"\x3D7"],"\x1E"=>[0,"\x3D8"],"\x1F"=>[0,"\x3D9"],"\x20"=>[0,"\x3D\x3D"],"\x21"=>[0,"\x3DA"],"\x22"=>[0,"\x3D_"],"\x23"=>[0,"\x3Db"],"\x24"=>[0,"\x3Dd"],"\x25"=>[0,"\x3Df"],"\x26"=>[0,"\x3Dg"],"\x27"=>[0,"\x3Dh"],"\x28"=>[0,"\x3Dl"],"\x29"=>[0,"\x3Dm"],"\x2A"=>[0,"\x3Dn"],"\x2B"=>[0,"\x3Dp"],"\x2C"=>[0,"\x3Dr"],"-"=>[0,"\x3Du"],"."=>[100,"\x3D"],"\x2F"=>[110,"\x3D"],[111,"\x3D"],[115,"\x3D"],[116,"\x3D"],[118,"\x3D"],[119,"\x3D"],[122,"\x3D"],[123,"\x3D"],[125,"\x3D"],[126,"\x3D"],[129,"\x3D"],"\x3A"=>[143,"\x3D"],"\x3B"=>[148,"\x3D"],"\x3C"=>[151,"\x3D"],"\x3D"=>[153,"\x3D"],"\x3E"=>[83,"\x3D"],"\x3F"=>[10,"\x3D"],"\x40"=>[77,"A0"],"A"=>[18,"A0"],"B"=>[77,"A1"],"C"=>[18,"A1"],"D"=>[77,"A2"],"E"=>[18,"A2"],"F"=>[77,"Aa"],"G"=>[18,"Aa"],"H"=>[77,"Ac"],"I"=>[18,"Ac"],"J"=>[77,"Ae"],"K"=>[18,"Ae"],"L"=>[77,"Ai"],"M"=>[18,"Ai"],"N"=>[77,"Ao"],"O"=>[18,"Ao"],"P"=>[77,"As"],"Q"=>[18,"As"],"R"=>[77,"At"],"S"=>[18,"At"],"T"=>[0,"A\x20"],"U"=>[0,"A\x25"],"V"=>[0,"A-"],"W"=>[0,"A."],"X"=>[0,"A\x2F"],"Y"=>[0,"A3"],"Z"=>[0,"A4"],"\x5B"=>[0,"A5"],"\x5C"=>[0,"A6"],"\x5D"=>[0,"A7"],"\x5E"=>[0,"A8"],"_"=>[0,"A9"],"\x60"=>[0,"A\x3D"],"a"=>[0,"AA"],"b"=>[0,"A_"],"c"=>[0,"Ab"],"d"=>[0,"Ad"],"e"=>[0,"Af"],"f"=>[0,"Ag"],"g"=>[0,"Ah"],"h"=>[0,"Al"],"i"=>[0,"Am"],"j"=>[0,"An"],"k"=>[0,"Ap"],"l"=>[0,"Ar"],"m"=>[0,"Au"],"n"=>[100,"A"],"o"=>[110,"A"],"p"=>[111,"A"],"q"=>[115,"A"],"r"=>[116,"A"],"s"=>[118,"A"],"t"=>[119,"A"],"u"=>[122,"A"],"v"=>[123,"A"],"w"=>[125,"A"],"x"=>[126,"A"],"y"=>[129,"A"],"z"=>[143,"A"],"\x7B"=>[148,"A"],"\x7C"=>[151,"A"],"\x7D"=>[153,"A"],"~"=>[83,"A"],"\x7F"=>[10,"A"],"\x80"=>[77,"_0"],"\x81"=>[18,"_0"],"\x82"=>[77,"_1"],"\x83"=>[18,"_1"],"\x84"=>[77,"_2"],"\x85"=>[18,"_2"],"\x86"=>[77,"_a"],"\x87"=>[18,"_a"],"\x88"=>[77,"_c"],"\x89"=>[18,"_c"],"\x8A"=>[77,"_e"],"\x8B"=>[18,"_e"],"\x8C"=>[77,"_i"],"\x8D"=>[18,"_i"],"\x8E"=>[77,"_o"],"\x8F"=>[18,"_o"],"\x90"=>[77,"_s"],"\x91"=>[18,"_s"],"\x92"=>[77,"_t"],"\x93"=>[18,"_t"],"\x94"=>[0,"_\x20"],"\x95"=>[0,"_\x25"],"\x96"=>[0,"_-"],"\x97"=>[0,"_."],"\x98"=>[0,"_\x2F"],"\x99"=>[0,"_3"],"\x9A"=>[0,"_4"],"\x9B"=>[0,"_5"],"\x9C"=>[0,"_6"],"\x9D"=>[0,"_7"],"\x9E"=>[0,"_8"],"\x9F"=>[0,"_9"],"\xA0"=>[0,"_\x3D"],"\xA1"=>[0,"_A"],"\xA2"=>[0,"__"],"\xA3"=>[0,"_b"],"\xA4"=>[0,"_d"],"\xA5"=>[0,"_f"],"\xA6"=>[0,"_g"],"\xA7"=>[0,"_h"],"\xA8"=>[0,"_l"],"\xA9"=>[0,"_m"],"\xAA"=>[0,"_n"],"\xAB"=>[0,"_p"],"\xAC"=>[0,"_r"],"\xAD"=>[0,"_u"],"\xAE"=>[100,"_"],"\xAF"=>[110,"_"],"\xB0"=>[111,"_"],"\xB1"=>[115,"_"],"\xB2"=>[116,"_"],"\xB3"=>[118,"_"],"\xB4"=>[119,"_"],"\xB5"=>[122,"_"],"\xB6"=>[123,"_"],"\xB7"=>[125,"_"],"\xB8"=>[126,"_"],"\xB9"=>[129,"_"],"\xBA"=>[143,"_"],"\xBB"=>[148,"_"],"\xBC"=>[151,"_"],"\xBD"=>[153,"_"],"\xBE"=>[83,"_"],"\xBF"=>[10,"_"],"\xC0"=>[77,"b0"],"\xC1"=>[18,"b0"],"\xC2"=>[77,"b1"],"\xC3"=>[18,"b1"],"\xC4"=>[77,"b2"],"\xC5"=>[18,"b2"],"\xC6"=>[77,"ba"],"\xC7"=>[18,"ba"],"\xC8"=>[77,"bc"],"\xC9"=>[18,"bc"],"\xCA"=>[77,"be"],"\xCB"=>[18,"be"],"\xCC"=>[77,"bi"],"\xCD"=>[18,"bi"],"\xCE"=>[77,"bo"],"\xCF"=>[18,"bo"],"\xD0"=>[77,"bs"],"\xD1"=>[18,"bs"],"\xD2"=>[77,"bt"],"\xD3"=>[18,"bt"],"\xD4"=>[0,"b\x20"],"\xD5"=>[0,"b\x25"],"\xD6"=>[0,"b-"],"\xD7"=>[0,"b."],"\xD8"=>[0,"b\x2F"],"\xD9"=>[0,"b3"],"\xDA"=>[0,"b4"],"\xDB"=>[0,"b5"],"\xDC"=>[0,"b6"],"\xDD"=>[0,"b7"],"\xDE"=>[0,"b8"],"\xDF"=>[0,"b9"],"\xE0"=>[0,"b\x3D"],"\xE1"=>[0,"bA"],"\xE2"=>[0,"b_"],"\xE3"=>[0,"bb"],"\xE4"=>[0,"bd"],"\xE5"=>[0,"bf"],"\xE6"=>[0,"bg"],"\xE7"=>[0,"bh"],"\xE8"=>[0,"bl"],"\xE9"=>[0,"bm"],"\xEA"=>[0,"bn"],"\xEB"=>[0,"bp"],"\xEC"=>[0,"br"],"\xED"=>[0,"bu"],"\xEE"=>[100,"b"],"\xEF"=>[110,"b"],"\xF0"=>[111,"b"],"\xF1"=>[115,"b"],"\xF2"=>[116,"b"],"\xF3"=>[118,"b"],"\xF4"=>[119,"b"],"\xF5"=>[122,"b"],"\xF6"=>[123,"b"],"\xF7"=>[125,"b"],"\xF8"=>[126,"b"],"\xF9"=>[129,"b"],"\xFA"=>[143,"b"],"\xFB"=>[148,"b"],"\xFC"=>[151,"b"],"\xFD"=>[153,"b"],"\xFE"=>[83,"b"],"\xFF"=>[10,"b"],],["\x00"=>[0,"\x3D0"],"\x01"=>[0,"\x3D1"],"\x02"=>[0,"\x3D2"],"\x03"=>[0,"\x3Da"],"\x04"=>[0,"\x3Dc"],"\x05"=>[0,"\x3De"],"\x06"=>[0,"\x3Di"],"\x07"=>[0,"\x3Do"],"\x08"=>[0,"\x3Ds"],"\x09"=>[0,"\x3Dt"],"\x0A"=>[73,"\x3D"],"\x0B"=>[88,"\x3D"],"\x0C"=>[89,"\x3D"],"\x0D"=>[96,"\x3D"],"\x0E"=>[97,"\x3D"],"\x0F"=>[99,"\x3D"],"\x10"=>[106,"\x3D"],"\x11"=>[136,"\x3D"],"\x12"=>[139,"\x3D"],"\x13"=>[141,"\x3D"],"\x14"=>[145,"\x3D"],"\x15"=>[147,"\x3D"],"\x16"=>[149,"\x3D"],"\x17"=>[101,"\x3D"],"\x18"=>[112,"\x3D"],"\x19"=>[117,"\x3D"],"\x1A"=>[120,"\x3D"],"\x1B"=>[124,"\x3D"],"\x1C"=>[127,"\x3D"],"\x1D"=>[144,"\x3D"],"\x1E"=>[152,"\x3D"],"\x1F"=>[11,"\x3D"],"\x20"=>[0,"A0"],"\x21"=>[0,"A1"],"\x22"=>[0,"A2"],"\x23"=>[0,"Aa"],"\x24"=>[0,"Ac"],"\x25"=>[0,"Ae"],"\x26"=>[0,"Ai"],"\x27"=>[0,"Ao"],"\x28"=>[0,"As"],"\x29"=>[0,"At"],"\x2A"=>[73,"A"],"\x2B"=>[88,"A"],"\x2C"=>[89,"A"],"-"=>[96,"A"],"."=>[97,"A"],"\x2F"=>[99,"A"],[106,"A"],[136,"A"],[139,"A"],[141,"A"],[145,"A"],[147,"A"],[149,"A"],[101,"A"],[112,"A"],[117,"A"],"\x3A"=>[120,"A"],"\x3B"=>[124,"A"],"\x3C"=>[127,"A"],"\x3D"=>[144,"A"],"\x3E"=>[152,"A"],"\x3F"=>[11,"A"],"\x40"=>[0,"_0"],"A"=>[0,"_1"],"B"=>[0,"_2"],"C"=>[0,"_a"],"D"=>[0,"_c"],"E"=>[0,"_e"],"F"=>[0,"_i"],"G"=>[0,"_o"],"H"=>[0,"_s"],"I"=>[0,"_t"],"J"=>[73,"_"],"K"=>[88,"_"],"L"=>[89,"_"],"M"=>[96,"_"],"N"=>[97,"_"],"O"=>[99,"_"],"P"=>[106,"_"],"Q"=>[136,"_"],"R"=>[139,"_"],"S"=>[141,"_"],"T"=>[145,"_"],"U"=>[147,"_"],"V"=>[149,"_"],"W"=>[101,"_"],"X"=>[112,"_"],"Y"=>[117,"_"],"Z"=>[120,"_"],"\x5B"=>[124,"_"],"\x5C"=>[127,"_"],"\x5D"=>[144,"_"],"\x5E"=>[152,"_"],"_"=>[11,"_"],"\x60"=>[0,"b0"],"a"=>[0,"b1"],"b"=>[0,"b2"],"c"=>[0,"ba"],"d"=>[0,"bc"],"e"=>[0,"be"],"f"=>[0,"bi"],"g"=>[0,"bo"],"h"=>[0,"bs"],"i"=>[0,"bt"],"j"=>[73,"b"],"k"=>[88,"b"],"l"=>[89,"b"],"m"=>[96,"b"],"n"=>[97,"b"],"o"=>[99,"b"],"p"=>[106,"b"],"q"=>[136,"b"],"r"=>[139,"b"],"s"=>[141,"b"],"t"=>[145,"b"],"u"=>[147,"b"],"v"=>[149,"b"],"w"=>[101,"b"],"x"=>[112,"b"],"y"=>[117,"b"],"z"=>[120,"b"],"\x7B"=>[124,"b"],"\x7C"=>[127,"b"],"\x7D"=>[144,"b"],"~"=>[152,"b"],"\x7F"=>[11,"b"],"\x80"=>[0,"d0"],"\x81"=>[0,"d1"],"\x82"=>[0,"d2"],"\x83"=>[0,"da"],"\x84"=>[0,"dc"],"\x85"=>[0,"de"],"\x86"=>[0,"di"],"\x87"=>[0,"do"],"\x88"=>[0,"ds"],"\x89"=>[0,"dt"],"\x8A"=>[73,"d"],"\x8B"=>[88,"d"],"\x8C"=>[89,"d"],"\x8D"=>[96,"d"],"\x8E"=>[97,"d"],"\x8F"=>[99,"d"],"\x90"=>[106,"d"],"\x91"=>[136,"d"],"\x92"=>[139,"d"],"\x93"=>[141,"d"],"\x94"=>[145,"d"],"\x95"=>[147,"d"],"\x96"=>[149,"d"],"\x97"=>[101,"d"],"\x98"=>[112,"d"],"\x99"=>[117,"d"],"\x9A"=>[120,"d"],"\x9B"=>[124,"d"],"\x9C"=>[127,"d"],"\x9D"=>[144,"d"],"\x9E"=>[152,"d"],"\x9F"=>[11,"d"],"\xA0"=>[0,"f0"],"\xA1"=>[0,"f1"],"\xA2"=>[0,"f2"],"\xA3"=>[0,"fa"],"\xA4"=>[0,"fc"],"\xA5"=>[0,"fe"],"\xA6"=>[0,"fi"],"\xA7"=>[0,"fo"],"\xA8"=>[0,"fs"],"\xA9"=>[0,"ft"],"\xAA"=>[73,"f"],"\xAB"=>[88,"f"],"\xAC"=>[89,"f"],"\xAD"=>[96,"f"],"\xAE"=>[97,"f"],"\xAF"=>[99,"f"],"\xB0"=>[106,"f"],"\xB1"=>[136,"f"],"\xB2"=>[139,"f"],"\xB3"=>[141,"f"],"\xB4"=>[145,"f"],"\xB5"=>[147,"f"],"\xB6"=>[149,"f"],"\xB7"=>[101,"f"],"\xB8"=>[112,"f"],"\xB9"=>[117,"f"],"\xBA"=>[120,"f"],"\xBB"=>[124,"f"],"\xBC"=>[127,"f"],"\xBD"=>[144,"f"],"\xBE"=>[152,"f"],"\xBF"=>[11,"f"],"\xC0"=>[0,"g0"],"\xC1"=>[0,"g1"],"\xC2"=>[0,"g2"],"\xC3"=>[0,"ga"],"\xC4"=>[0,"gc"],"\xC5"=>[0,"ge"],"\xC6"=>[0,"gi"],"\xC7"=>[0,"go"],"\xC8"=>[0,"gs"],"\xC9"=>[0,"gt"],"\xCA"=>[73,"g"],"\xCB"=>[88,"g"],"\xCC"=>[89,"g"],"\xCD"=>[96,"g"],"\xCE"=>[97,"g"],"\xCF"=>[99,"g"],"\xD0"=>[106,"g"],"\xD1"=>[136,"g"],"\xD2"=>[139,"g"],"\xD3"=>[141,"g"],"\xD4"=>[145,"g"],"\xD5"=>[147,"g"],"\xD6"=>[149,"g"],"\xD7"=>[101,"g"],"\xD8"=>[112,"g"],"\xD9"=>[117,"g"],"\xDA"=>[120,"g"],"\xDB"=>[124,"g"],"\xDC"=>[127,"g"],"\xDD"=>[144,"g"],"\xDE"=>[152,"g"],"\xDF"=>[11,"g"],"\xE0"=>[0,"h0"],"\xE1"=>[0,"h1"],"\xE2"=>[0,"h2"],"\xE3"=>[0,"ha"],"\xE4"=>[0,"hc"],"\xE5"=>[0,"he"],"\xE6"=>[0,"hi"],"\xE7"=>[0,"ho"],"\xE8"=>[0,"hs"],"\xE9"=>[0,"ht"],"\xEA"=>[73,"h"],"\xEB"=>[88,"h"],"\xEC"=>[89,"h"],"\xED"=>[96,"h"],"\xEE"=>[97,"h"],"\xEF"=>[99,"h"],"\xF0"=>[106,"h"],"\xF1"=>[136,"h"],"\xF2"=>[139,"h"],"\xF3"=>[141,"h"],"\xF4"=>[145,"h"],"\xF5"=>[147,"h"],"\xF6"=>[149,"h"],"\xF7"=>[101,"h"],"\xF8"=>[112,"h"],"\xF9"=>[117,"h"],"\xFA"=>[120,"h"],"\xFB"=>[124,"h"],"\xFC"=>[127,"h"],"\xFD"=>[144,"h"],"\xFE"=>[152,"h"],"\xFF"=>[11,"h"],],["\x00"=>[94,"\x400"],"\x01"=>[76,"\x400"],"\x02"=>[104,"\x400"],"\x03"=>[16,"\x400"],"\x04"=>[94,"\x401"],"\x05"=>[76,"\x401"],"\x06"=>[104,"\x401"],"\x07"=>[16,"\x401"],"\x08"=>[94,"\x402"],"\x09"=>[76,"\x402"],"\x0A"=>[104,"\x402"],"\x0B"=>[16,"\x402"],"\x0C"=>[94,"\x40a"],"\x0D"=>[76,"\x40a"],"\x0E"=>[104,"\x40a"],"\x0F"=>[16,"\x40a"],"\x10"=>[94,"\x40c"],"\x11"=>[76,"\x40c"],"\x12"=>[104,"\x40c"],"\x13"=>[16,"\x40c"],"\x14"=>[94,"\x40e"],"\x15"=>[76,"\x40e"],"\x16"=>[104,"\x40e"],"\x17"=>[16,"\x40e"],"\x18"=>[94,"\x40i"],"\x19"=>[76,"\x40i"],"\x1A"=>[104,"\x40i"],"\x1B"=>[16,"\x40i"],"\x1C"=>[94,"\x40o"],"\x1D"=>[76,"\x40o"],"\x1E"=>[104,"\x40o"],"\x1F"=>[16,"\x40o"],"\x20"=>[94,"\x40s"],"\x21"=>[76,"\x40s"],"\x22"=>[104,"\x40s"],"\x23"=>[16,"\x40s"],"\x24"=>[94,"\x40t"],"\x25"=>[76,"\x40t"],"\x26"=>[104,"\x40t"],"\x27"=>[16,"\x40t"],"\x28"=>[77,"\x40\x20"],"\x29"=>[18,"\x40\x20"],"\x2A"=>[77,"\x40\x25"],"\x2B"=>[18,"\x40\x25"],"\x2C"=>[77,"\x40-"],"-"=>[18,"\x40-"],"."=>[77,"\x40."],"\x2F"=>[18,"\x40."],[77,"\x40\x2F"],[18,"\x40\x2F"],[77,"\x403"],[18,"\x403"],[77,"\x404"],[18,"\x404"],[77,"\x405"],[18,"\x405"],[77,"\x406"],[18,"\x406"],"\x3A"=>[77,"\x407"],"\x3B"=>[18,"\x407"],"\x3C"=>[77,"\x408"],"\x3D"=>[18,"\x408"],"\x3E"=>[77,"\x409"],"\x3F"=>[18,"\x409"],"\x40"=>[77,"\x40\x3D"],"A"=>[18,"\x40\x3D"],"B"=>[77,"\x40A"],"C"=>[18,"\x40A"],"D"=>[77,"\x40_"],"E"=>[18,"\x40_"],"F"=>[77,"\x40b"],"G"=>[18,"\x40b"],"H"=>[77,"\x40d"],"I"=>[18,"\x40d"],"J"=>[77,"\x40f"],"K"=>[18,"\x40f"],"L"=>[77,"\x40g"],"M"=>[18,"\x40g"],"N"=>[77,"\x40h"],"O"=>[18,"\x40h"],"P"=>[77,"\x40l"],"Q"=>[18,"\x40l"],"R"=>[77,"\x40m"],"S"=>[18,"\x40m"],"T"=>[77,"\x40n"],"U"=>[18,"\x40n"],"V"=>[77,"\x40p"],"W"=>[18,"\x40p"],"X"=>[77,"\x40r"],"Y"=>[18,"\x40r"],"Z"=>[77,"\x40u"],"\x5B"=>[18,"\x40u"],"\x5C"=>[0,"\x40\x3A"],"\x5D"=>[0,"\x40B"],"\x5E"=>[0,"\x40C"],"_"=>[0,"\x40D"],"\x60"=>[0,"\x40E"],"a"=>[0,"\x40F"],"b"=>[0,"\x40G"],"c"=>[0,"\x40H"],"d"=>[0,"\x40I"],"e"=>[0,"\x40J"],"f"=>[0,"\x40K"],"g"=>[0,"\x40L"],"h"=>[0,"\x40M"],"i"=>[0,"\x40N"],"j"=>[0,"\x40O"],"k"=>[0,"\x40P"],"l"=>[0,"\x40Q"],"m"=>[0,"\x40R"],"n"=>[0,"\x40S"],"o"=>[0,"\x40T"],"p"=>[0,"\x40U"],"q"=>[0,"\x40V"],"r"=>[0,"\x40W"],"s"=>[0,"\x40Y"],"t"=>[0,"\x40j"],"u"=>[0,"\x40k"],"v"=>[0,"\x40q"],"w"=>[0,"\x40v"],"x"=>[0,"\x40w"],"y"=>[0,"\x40x"],"z"=>[0,"\x40y"],"\x7B"=>[0,"\x40z"],"\x7C"=>[82,"\x40"],"\x7D"=>[87,"\x40"],"~"=>[130,"\x40"],"\x7F"=>[9,"\x40"],"\x80"=>[94,"\x5B0"],"\x81"=>[76,"\x5B0"],"\x82"=>[104,"\x5B0"],"\x83"=>[16,"\x5B0"],"\x84"=>[94,"\x5B1"],"\x85"=>[76,"\x5B1"],"\x86"=>[104,"\x5B1"],"\x87"=>[16,"\x5B1"],"\x88"=>[94,"\x5B2"],"\x89"=>[76,"\x5B2"],"\x8A"=>[104,"\x5B2"],"\x8B"=>[16,"\x5B2"],"\x8C"=>[94,"\x5Ba"],"\x8D"=>[76,"\x5Ba"],"\x8E"=>[104,"\x5Ba"],"\x8F"=>[16,"\x5Ba"],"\x90"=>[94,"\x5Bc"],"\x91"=>[76,"\x5Bc"],"\x92"=>[104,"\x5Bc"],"\x93"=>[16,"\x5Bc"],"\x94"=>[94,"\x5Be"],"\x95"=>[76,"\x5Be"],"\x96"=>[104,"\x5Be"],"\x97"=>[16,"\x5Be"],"\x98"=>[94,"\x5Bi"],"\x99"=>[76,"\x5Bi"],"\x9A"=>[104,"\x5Bi"],"\x9B"=>[16,"\x5Bi"],"\x9C"=>[94,"\x5Bo"],"\x9D"=>[76,"\x5Bo"],"\x9E"=>[104,"\x5Bo"],"\x9F"=>[16,"\x5Bo"],"\xA0"=>[94,"\x5Bs"],"\xA1"=>[76,"\x5Bs"],"\xA2"=>[104,"\x5Bs"],"\xA3"=>[16,"\x5Bs"],"\xA4"=>[94,"\x5Bt"],"\xA5"=>[76,"\x5Bt"],"\xA6"=>[104,"\x5Bt"],"\xA7"=>[16,"\x5Bt"],"\xA8"=>[77,"\x5B\x20"],"\xA9"=>[18,"\x5B\x20"],"\xAA"=>[77,"\x5B\x25"],"\xAB"=>[18,"\x5B\x25"],"\xAC"=>[77,"\x5B-"],"\xAD"=>[18,"\x5B-"],"\xAE"=>[77,"\x5B."],"\xAF"=>[18,"\x5B."],"\xB0"=>[77,"\x5B\x2F"],"\xB1"=>[18,"\x5B\x2F"],"\xB2"=>[77,"\x5B3"],"\xB3"=>[18,"\x5B3"],"\xB4"=>[77,"\x5B4"],"\xB5"=>[18,"\x5B4"],"\xB6"=>[77,"\x5B5"],"\xB7"=>[18,"\x5B5"],"\xB8"=>[77,"\x5B6"],"\xB9"=>[18,"\x5B6"],"\xBA"=>[77,"\x5B7"],"\xBB"=>[18,"\x5B7"],"\xBC"=>[77,"\x5B8"],"\xBD"=>[18,"\x5B8"],"\xBE"=>[77,"\x5B9"],"\xBF"=>[18,"\x5B9"],"\xC0"=>[77,"\x5B\x3D"],"\xC1"=>[18,"\x5B\x3D"],"\xC2"=>[77,"\x5BA"],"\xC3"=>[18,"\x5BA"],"\xC4"=>[77,"\x5B_"],"\xC5"=>[18,"\x5B_"],"\xC6"=>[77,"\x5Bb"],"\xC7"=>[18,"\x5Bb"],"\xC8"=>[77,"\x5Bd"],"\xC9"=>[18,"\x5Bd"],"\xCA"=>[77,"\x5Bf"],"\xCB"=>[18,"\x5Bf"],"\xCC"=>[77,"\x5Bg"],"\xCD"=>[18,"\x5Bg"],"\xCE"=>[77,"\x5Bh"],"\xCF"=>[18,"\x5Bh"],"\xD0"=>[77,"\x5Bl"],"\xD1"=>[18,"\x5Bl"],"\xD2"=>[77,"\x5Bm"],"\xD3"=>[18,"\x5Bm"],"\xD4"=>[77,"\x5Bn"],"\xD5"=>[18,"\x5Bn"],"\xD6"=>[77,"\x5Bp"],"\xD7"=>[18,"\x5Bp"],"\xD8"=>[77,"\x5Br"],"\xD9"=>[18,"\x5Br"],"\xDA"=>[77,"\x5Bu"],"\xDB"=>[18,"\x5Bu"],"\xDC"=>[0,"\x5B\x3A"],"\xDD"=>[0,"\x5BB"],"\xDE"=>[0,"\x5BC"],"\xDF"=>[0,"\x5BD"],"\xE0"=>[0,"\x5BE"],"\xE1"=>[0,"\x5BF"],"\xE2"=>[0,"\x5BG"],"\xE3"=>[0,"\x5BH"],"\xE4"=>[0,"\x5BI"],"\xE5"=>[0,"\x5BJ"],"\xE6"=>[0,"\x5BK"],"\xE7"=>[0,"\x5BL"],"\xE8"=>[0,"\x5BM"],"\xE9"=>[0,"\x5BN"],"\xEA"=>[0,"\x5BO"],"\xEB"=>[0,"\x5BP"],"\xEC"=>[0,"\x5BQ"],"\xED"=>[0,"\x5BR"],"\xEE"=>[0,"\x5BS"],"\xEF"=>[0,"\x5BT"],"\xF0"=>[0,"\x5BU"],"\xF1"=>[0,"\x5BV"],"\xF2"=>[0,"\x5BW"],"\xF3"=>[0,"\x5BY"],"\xF4"=>[0,"\x5Bj"],"\xF5"=>[0,"\x5Bk"],"\xF6"=>[0,"\x5Bq"],"\xF7"=>[0,"\x5Bv"],"\xF8"=>[0,"\x5Bw"],"\xF9"=>[0,"\x5Bx"],"\xFA"=>[0,"\x5By"],"\xFB"=>[0,"\x5Bz"],"\xFC"=>[82,"\x5B"],"\xFD"=>[87,"\x5B"],"\xFE"=>[130,"\x5B"],"\xFF"=>[9,"\x5B"],],["\x00"=>[94,"C0"],"\x01"=>[76,"C0"],"\x02"=>[104,"C0"],"\x03"=>[16,"C0"],"\x04"=>[94,"C1"],"\x05"=>[76,"C1"],"\x06"=>[104,"C1"],"\x07"=>[16,"C1"],"\x08"=>[94,"C2"],"\x09"=>[76,"C2"],"\x0A"=>[104,"C2"],"\x0B"=>[16,"C2"],"\x0C"=>[94,"Ca"],"\x0D"=>[76,"Ca"],"\x0E"=>[104,"Ca"],"\x0F"=>[16,"Ca"],"\x10"=>[94,"Cc"],"\x11"=>[76,"Cc"],"\x12"=>[104,"Cc"],"\x13"=>[16,"Cc"],"\x14"=>[94,"Ce"],"\x15"=>[76,"Ce"],"\x16"=>[104,"Ce"],"\x17"=>[16,"Ce"],"\x18"=>[94,"Ci"],"\x19"=>[76,"Ci"],"\x1A"=>[104,"Ci"],"\x1B"=>[16,"Ci"],"\x1C"=>[94,"Co"],"\x1D"=>[76,"Co"],"\x1E"=>[104,"Co"],"\x1F"=>[16,"Co"],"\x20"=>[94,"Cs"],"\x21"=>[76,"Cs"],"\x22"=>[104,"Cs"],"\x23"=>[16,"Cs"],"\x24"=>[94,"Ct"],"\x25"=>[76,"Ct"],"\x26"=>[104,"Ct"],"\x27"=>[16,"Ct"],"\x28"=>[77,"C\x20"],"\x29"=>[18,"C\x20"],"\x2A"=>[77,"C\x25"],"\x2B"=>[18,"C\x25"],"\x2C"=>[77,"C-"],"-"=>[18,"C-"],"."=>[77,"C."],"\x2F"=>[18,"C."],[77,"C\x2F"],[18,"C\x2F"],[77,"C3"],[18,"C3"],[77,"C4"],[18,"C4"],[77,"C5"],[18,"C5"],[77,"C6"],[18,"C6"],"\x3A"=>[77,"C7"],"\x3B"=>[18,"C7"],"\x3C"=>[77,"C8"],"\x3D"=>[18,"C8"],"\x3E"=>[77,"C9"],"\x3F"=>[18,"C9"],"\x40"=>[77,"C\x3D"],"A"=>[18,"C\x3D"],"B"=>[77,"CA"],"C"=>[18,"CA"],"D"=>[77,"C_"],"E"=>[18,"C_"],"F"=>[77,"Cb"],"G"=>[18,"Cb"],"H"=>[77,"Cd"],"I"=>[18,"Cd"],"J"=>[77,"Cf"],"K"=>[18,"Cf"],"L"=>[77,"Cg"],"M"=>[18,"Cg"],"N"=>[77,"Ch"],"O"=>[18,"Ch"],"P"=>[77,"Cl"],"Q"=>[18,"Cl"],"R"=>[77,"Cm"],"S"=>[18,"Cm"],"T"=>[77,"Cn"],"U"=>[18,"Cn"],"V"=>[77,"Cp"],"W"=>[18,"Cp"],"X"=>[77,"Cr"],"Y"=>[18,"Cr"],"Z"=>[77,"Cu"],"\x5B"=>[18,"Cu"],"\x5C"=>[0,"C\x3A"],"\x5D"=>[0,"CB"],"\x5E"=>[0,"CC"],"_"=>[0,"CD"],"\x60"=>[0,"CE"],"a"=>[0,"CF"],"b"=>[0,"CG"],"c"=>[0,"CH"],"d"=>[0,"CI"],"e"=>[0,"CJ"],"f"=>[0,"CK"],"g"=>[0,"CL"],"h"=>[0,"CM"],"i"=>[0,"CN"],"j"=>[0,"CO"],"k"=>[0,"CP"],"l"=>[0,"CQ"],"m"=>[0,"CR"],"n"=>[0,"CS"],"o"=>[0,"CT"],"p"=>[0,"CU"],"q"=>[0,"CV"],"r"=>[0,"CW"],"s"=>[0,"CY"],"t"=>[0,"Cj"],"u"=>[0,"Ck"],"v"=>[0,"Cq"],"w"=>[0,"Cv"],"x"=>[0,"Cw"],"y"=>[0,"Cx"],"z"=>[0,"Cy"],"\x7B"=>[0,"Cz"],"\x7C"=>[82,"C"],"\x7D"=>[87,"C"],"~"=>[130,"C"],"\x7F"=>[9,"C"],"\x80"=>[94,"D0"],"\x81"=>[76,"D0"],"\x82"=>[104,"D0"],"\x83"=>[16,"D0"],"\x84"=>[94,"D1"],"\x85"=>[76,"D1"],"\x86"=>[104,"D1"],"\x87"=>[16,"D1"],"\x88"=>[94,"D2"],"\x89"=>[76,"D2"],"\x8A"=>[104,"D2"],"\x8B"=>[16,"D2"],"\x8C"=>[94,"Da"],"\x8D"=>[76,"Da"],"\x8E"=>[104,"Da"],"\x8F"=>[16,"Da"],"\x90"=>[94,"Dc"],"\x91"=>[76,"Dc"],"\x92"=>[104,"Dc"],"\x93"=>[16,"Dc"],"\x94"=>[94,"De"],"\x95"=>[76,"De"],"\x96"=>[104,"De"],"\x97"=>[16,"De"],"\x98"=>[94,"Di"],"\x99"=>[76,"Di"],"\x9A"=>[104,"Di"],"\x9B"=>[16,"Di"],"\x9C"=>[94,"Do"],"\x9D"=>[76,"Do"],"\x9E"=>[104,"Do"],"\x9F"=>[16,"Do"],"\xA0"=>[94,"Ds"],"\xA1"=>[76,"Ds"],"\xA2"=>[104,"Ds"],"\xA3"=>[16,"Ds"],"\xA4"=>[94,"Dt"],"\xA5"=>[76,"Dt"],"\xA6"=>[104,"Dt"],"\xA7"=>[16,"Dt"],"\xA8"=>[77,"D\x20"],"\xA9"=>[18,"D\x20"],"\xAA"=>[77,"D\x25"],"\xAB"=>[18,"D\x25"],"\xAC"=>[77,"D-"],"\xAD"=>[18,"D-"],"\xAE"=>[77,"D."],"\xAF"=>[18,"D."],"\xB0"=>[77,"D\x2F"],"\xB1"=>[18,"D\x2F"],"\xB2"=>[77,"D3"],"\xB3"=>[18,"D3"],"\xB4"=>[77,"D4"],"\xB5"=>[18,"D4"],"\xB6"=>[77,"D5"],"\xB7"=>[18,"D5"],"\xB8"=>[77,"D6"],"\xB9"=>[18,"D6"],"\xBA"=>[77,"D7"],"\xBB"=>[18,"D7"],"\xBC"=>[77,"D8"],"\xBD"=>[18,"D8"],"\xBE"=>[77,"D9"],"\xBF"=>[18,"D9"],"\xC0"=>[77,"D\x3D"],"\xC1"=>[18,"D\x3D"],"\xC2"=>[77,"DA"],"\xC3"=>[18,"DA"],"\xC4"=>[77,"D_"],"\xC5"=>[18,"D_"],"\xC6"=>[77,"Db"],"\xC7"=>[18,"Db"],"\xC8"=>[77,"Dd"],"\xC9"=>[18,"Dd"],"\xCA"=>[77,"Df"],"\xCB"=>[18,"Df"],"\xCC"=>[77,"Dg"],"\xCD"=>[18,"Dg"],"\xCE"=>[77,"Dh"],"\xCF"=>[18,"Dh"],"\xD0"=>[77,"Dl"],"\xD1"=>[18,"Dl"],"\xD2"=>[77,"Dm"],"\xD3"=>[18,"Dm"],"\xD4"=>[77,"Dn"],"\xD5"=>[18,"Dn"],"\xD6"=>[77,"Dp"],"\xD7"=>[18,"Dp"],"\xD8"=>[77,"Dr"],"\xD9"=>[18,"Dr"],"\xDA"=>[77,"Du"],"\xDB"=>[18,"Du"],"\xDC"=>[0,"D\x3A"],"\xDD"=>[0,"DB"],"\xDE"=>[0,"DC"],"\xDF"=>[0,"DD"],"\xE0"=>[0,"DE"],"\xE1"=>[0,"DF"],"\xE2"=>[0,"DG"],"\xE3"=>[0,"DH"],"\xE4"=>[0,"DI"],"\xE5"=>[0,"DJ"],"\xE6"=>[0,"DK"],"\xE7"=>[0,"DL"],"\xE8"=>[0,"DM"],"\xE9"=>[0,"DN"],"\xEA"=>[0,"DO"],"\xEB"=>[0,"DP"],"\xEC"=>[0,"DQ"],"\xED"=>[0,"DR"],"\xEE"=>[0,"DS"],"\xEF"=>[0,"DT"],"\xF0"=>[0,"DU"],"\xF1"=>[0,"DV"],"\xF2"=>[0,"DW"],"\xF3"=>[0,"DY"],"\xF4"=>[0,"Dj"],"\xF5"=>[0,"Dk"],"\xF6"=>[0,"Dq"],"\xF7"=>[0,"Dv"],"\xF8"=>[0,"Dw"],"\xF9"=>[0,"Dx"],"\xFA"=>[0,"Dy"],"\xFB"=>[0,"Dz"],"\xFC"=>[82,"D"],"\xFD"=>[87,"D"],"\xFE"=>[130,"D"],"\xFF"=>[9,"D"],],["\x00"=>[94,"E0"],"\x01"=>[76,"E0"],"\x02"=>[104,"E0"],"\x03"=>[16,"E0"],"\x04"=>[94,"E1"],"\x05"=>[76,"E1"],"\x06"=>[104,"E1"],"\x07"=>[16,"E1"],"\x08"=>[94,"E2"],"\x09"=>[76,"E2"],"\x0A"=>[104,"E2"],"\x0B"=>[16,"E2"],"\x0C"=>[94,"Ea"],"\x0D"=>[76,"Ea"],"\x0E"=>[104,"Ea"],"\x0F"=>[16,"Ea"],"\x10"=>[94,"Ec"],"\x11"=>[76,"Ec"],"\x12"=>[104,"Ec"],"\x13"=>[16,"Ec"],"\x14"=>[94,"Ee"],"\x15"=>[76,"Ee"],"\x16"=>[104,"Ee"],"\x17"=>[16,"Ee"],"\x18"=>[94,"Ei"],"\x19"=>[76,"Ei"],"\x1A"=>[104,"Ei"],"\x1B"=>[16,"Ei"],"\x1C"=>[94,"Eo"],"\x1D"=>[76,"Eo"],"\x1E"=>[104,"Eo"],"\x1F"=>[16,"Eo"],"\x20"=>[94,"Es"],"\x21"=>[76,"Es"],"\x22"=>[104,"Es"],"\x23"=>[16,"Es"],"\x24"=>[94,"Et"],"\x25"=>[76,"Et"],"\x26"=>[104,"Et"],"\x27"=>[16,"Et"],"\x28"=>[77,"E\x20"],"\x29"=>[18,"E\x20"],"\x2A"=>[77,"E\x25"],"\x2B"=>[18,"E\x25"],"\x2C"=>[77,"E-"],"-"=>[18,"E-"],"."=>[77,"E."],"\x2F"=>[18,"E."],[77,"E\x2F"],[18,"E\x2F"],[77,"E3"],[18,"E3"],[77,"E4"],[18,"E4"],[77,"E5"],[18,"E5"],[77,"E6"],[18,"E6"],"\x3A"=>[77,"E7"],"\x3B"=>[18,"E7"],"\x3C"=>[77,"E8"],"\x3D"=>[18,"E8"],"\x3E"=>[77,"E9"],"\x3F"=>[18,"E9"],"\x40"=>[77,"E\x3D"],"A"=>[18,"E\x3D"],"B"=>[77,"EA"],"C"=>[18,"EA"],"D"=>[77,"E_"],"E"=>[18,"E_"],"F"=>[77,"Eb"],"G"=>[18,"Eb"],"H"=>[77,"Ed"],"I"=>[18,"Ed"],"J"=>[77,"Ef"],"K"=>[18,"Ef"],"L"=>[77,"Eg"],"M"=>[18,"Eg"],"N"=>[77,"Eh"],"O"=>[18,"Eh"],"P"=>[77,"El"],"Q"=>[18,"El"],"R"=>[77,"Em"],"S"=>[18,"Em"],"T"=>[77,"En"],"U"=>[18,"En"],"V"=>[77,"Ep"],"W"=>[18,"Ep"],"X"=>[77,"Er"],"Y"=>[18,"Er"],"Z"=>[77,"Eu"],"\x5B"=>[18,"Eu"],"\x5C"=>[0,"E\x3A"],"\x5D"=>[0,"EB"],"\x5E"=>[0,"EC"],"_"=>[0,"ED"],"\x60"=>[0,"EE"],"a"=>[0,"EF"],"b"=>[0,"EG"],"c"=>[0,"EH"],"d"=>[0,"EI"],"e"=>[0,"EJ"],"f"=>[0,"EK"],"g"=>[0,"EL"],"h"=>[0,"EM"],"i"=>[0,"EN"],"j"=>[0,"EO"],"k"=>[0,"EP"],"l"=>[0,"EQ"],"m"=>[0,"ER"],"n"=>[0,"ES"],"o"=>[0,"ET"],"p"=>[0,"EU"],"q"=>[0,"EV"],"r"=>[0,"EW"],"s"=>[0,"EY"],"t"=>[0,"Ej"],"u"=>[0,"Ek"],"v"=>[0,"Eq"],"w"=>[0,"Ev"],"x"=>[0,"Ew"],"y"=>[0,"Ex"],"z"=>[0,"Ey"],"\x7B"=>[0,"Ez"],"\x7C"=>[82,"E"],"\x7D"=>[87,"E"],"~"=>[130,"E"],"\x7F"=>[9,"E"],"\x80"=>[94,"F0"],"\x81"=>[76,"F0"],"\x82"=>[104,"F0"],"\x83"=>[16,"F0"],"\x84"=>[94,"F1"],"\x85"=>[76,"F1"],"\x86"=>[104,"F1"],"\x87"=>[16,"F1"],"\x88"=>[94,"F2"],"\x89"=>[76,"F2"],"\x8A"=>[104,"F2"],"\x8B"=>[16,"F2"],"\x8C"=>[94,"Fa"],"\x8D"=>[76,"Fa"],"\x8E"=>[104,"Fa"],"\x8F"=>[16,"Fa"],"\x90"=>[94,"Fc"],"\x91"=>[76,"Fc"],"\x92"=>[104,"Fc"],"\x93"=>[16,"Fc"],"\x94"=>[94,"Fe"],"\x95"=>[76,"Fe"],"\x96"=>[104,"Fe"],"\x97"=>[16,"Fe"],"\x98"=>[94,"Fi"],"\x99"=>[76,"Fi"],"\x9A"=>[104,"Fi"],"\x9B"=>[16,"Fi"],"\x9C"=>[94,"Fo"],"\x9D"=>[76,"Fo"],"\x9E"=>[104,"Fo"],"\x9F"=>[16,"Fo"],"\xA0"=>[94,"Fs"],"\xA1"=>[76,"Fs"],"\xA2"=>[104,"Fs"],"\xA3"=>[16,"Fs"],"\xA4"=>[94,"Ft"],"\xA5"=>[76,"Ft"],"\xA6"=>[104,"Ft"],"\xA7"=>[16,"Ft"],"\xA8"=>[77,"F\x20"],"\xA9"=>[18,"F\x20"],"\xAA"=>[77,"F\x25"],"\xAB"=>[18,"F\x25"],"\xAC"=>[77,"F-"],"\xAD"=>[18,"F-"],"\xAE"=>[77,"F."],"\xAF"=>[18,"F."],"\xB0"=>[77,"F\x2F"],"\xB1"=>[18,"F\x2F"],"\xB2"=>[77,"F3"],"\xB3"=>[18,"F3"],"\xB4"=>[77,"F4"],"\xB5"=>[18,"F4"],"\xB6"=>[77,"F5"],"\xB7"=>[18,"F5"],"\xB8"=>[77,"F6"],"\xB9"=>[18,"F6"],"\xBA"=>[77,"F7"],"\xBB"=>[18,"F7"],"\xBC"=>[77,"F8"],"\xBD"=>[18,"F8"],"\xBE"=>[77,"F9"],"\xBF"=>[18,"F9"],"\xC0"=>[77,"F\x3D"],"\xC1"=>[18,"F\x3D"],"\xC2"=>[77,"FA"],"\xC3"=>[18,"FA"],"\xC4"=>[77,"F_"],"\xC5"=>[18,"F_"],"\xC6"=>[77,"Fb"],"\xC7"=>[18,"Fb"],"\xC8"=>[77,"Fd"],"\xC9"=>[18,"Fd"],"\xCA"=>[77,"Ff"],"\xCB"=>[18,"Ff"],"\xCC"=>[77,"Fg"],"\xCD"=>[18,"Fg"],"\xCE"=>[77,"Fh"],"\xCF"=>[18,"Fh"],"\xD0"=>[77,"Fl"],"\xD1"=>[18,"Fl"],"\xD2"=>[77,"Fm"],"\xD3"=>[18,"Fm"],"\xD4"=>[77,"Fn"],"\xD5"=>[18,"Fn"],"\xD6"=>[77,"Fp"],"\xD7"=>[18,"Fp"],"\xD8"=>[77,"Fr"],"\xD9"=>[18,"Fr"],"\xDA"=>[77,"Fu"],"\xDB"=>[18,"Fu"],"\xDC"=>[0,"F\x3A"],"\xDD"=>[0,"FB"],"\xDE"=>[0,"FC"],"\xDF"=>[0,"FD"],"\xE0"=>[0,"FE"],"\xE1"=>[0,"FF"],"\xE2"=>[0,"FG"],"\xE3"=>[0,"FH"],"\xE4"=>[0,"FI"],"\xE5"=>[0,"FJ"],"\xE6"=>[0,"FK"],"\xE7"=>[0,"FL"],"\xE8"=>[0,"FM"],"\xE9"=>[0,"FN"],"\xEA"=>[0,"FO"],"\xEB"=>[0,"FP"],"\xEC"=>[0,"FQ"],"\xED"=>[0,"FR"],"\xEE"=>[0,"FS"],"\xEF"=>[0,"FT"],"\xF0"=>[0,"FU"],"\xF1"=>[0,"FV"],"\xF2"=>[0,"FW"],"\xF3"=>[0,"FY"],"\xF4"=>[0,"Fj"],"\xF5"=>[0,"Fk"],"\xF6"=>[0,"Fq"],"\xF7"=>[0,"Fv"],"\xF8"=>[0,"Fw"],"\xF9"=>[0,"Fx"],"\xFA"=>[0,"Fy"],"\xFB"=>[0,"Fz"],"\xFC"=>[82,"F"],"\xFD"=>[87,"F"],"\xFE"=>[130,"F"],"\xFF"=>[9,"F"],],["\x00"=>[77,"E0"],"\x01"=>[18,"E0"],"\x02"=>[77,"E1"],"\x03"=>[18,"E1"],"\x04"=>[77,"E2"],"\x05"=>[18,"E2"],"\x06"=>[77,"Ea"],"\x07"=>[18,"Ea"],"\x08"=>[77,"Ec"],"\x09"=>[18,"Ec"],"\x0A"=>[77,"Ee"],"\x0B"=>[18,"Ee"],"\x0C"=>[77,"Ei"],"\x0D"=>[18,"Ei"],"\x0E"=>[77,"Eo"],"\x0F"=>[18,"Eo"],"\x10"=>[77,"Es"],"\x11"=>[18,"Es"],"\x12"=>[77,"Et"],"\x13"=>[18,"Et"],"\x14"=>[0,"E\x20"],"\x15"=>[0,"E\x25"],"\x16"=>[0,"E-"],"\x17"=>[0,"E."],"\x18"=>[0,"E\x2F"],"\x19"=>[0,"E3"],"\x1A"=>[0,"E4"],"\x1B"=>[0,"E5"],"\x1C"=>[0,"E6"],"\x1D"=>[0,"E7"],"\x1E"=>[0,"E8"],"\x1F"=>[0,"E9"],"\x20"=>[0,"E\x3D"],"\x21"=>[0,"EA"],"\x22"=>[0,"E_"],"\x23"=>[0,"Eb"],"\x24"=>[0,"Ed"],"\x25"=>[0,"Ef"],"\x26"=>[0,"Eg"],"\x27"=>[0,"Eh"],"\x28"=>[0,"El"],"\x29"=>[0,"Em"],"\x2A"=>[0,"En"],"\x2B"=>[0,"Ep"],"\x2C"=>[0,"Er"],"-"=>[0,"Eu"],"."=>[100,"E"],"\x2F"=>[110,"E"],[111,"E"],[115,"E"],[116,"E"],[118,"E"],[119,"E"],[122,"E"],[123,"E"],[125,"E"],[126,"E"],[129,"E"],"\x3A"=>[143,"E"],"\x3B"=>[148,"E"],"\x3C"=>[151,"E"],"\x3D"=>[153,"E"],"\x3E"=>[83,"E"],"\x3F"=>[10,"E"],"\x40"=>[77,"F0"],"A"=>[18,"F0"],"B"=>[77,"F1"],"C"=>[18,"F1"],"D"=>[77,"F2"],"E"=>[18,"F2"],"F"=>[77,"Fa"],"G"=>[18,"Fa"],"H"=>[77,"Fc"],"I"=>[18,"Fc"],"J"=>[77,"Fe"],"K"=>[18,"Fe"],"L"=>[77,"Fi"],"M"=>[18,"Fi"],"N"=>[77,"Fo"],"O"=>[18,"Fo"],"P"=>[77,"Fs"],"Q"=>[18,"Fs"],"R"=>[77,"Ft"],"S"=>[18,"Ft"],"T"=>[0,"F\x20"],"U"=>[0,"F\x25"],"V"=>[0,"F-"],"W"=>[0,"F."],"X"=>[0,"F\x2F"],"Y"=>[0,"F3"],"Z"=>[0,"F4"],"\x5B"=>[0,"F5"],"\x5C"=>[0,"F6"],"\x5D"=>[0,"F7"],"\x5E"=>[0,"F8"],"_"=>[0,"F9"],"\x60"=>[0,"F\x3D"],"a"=>[0,"FA"],"b"=>[0,"F_"],"c"=>[0,"Fb"],"d"=>[0,"Fd"],"e"=>[0,"Ff"],"f"=>[0,"Fg"],"g"=>[0,"Fh"],"h"=>[0,"Fl"],"i"=>[0,"Fm"],"j"=>[0,"Fn"],"k"=>[0,"Fp"],"l"=>[0,"Fr"],"m"=>[0,"Fu"],"n"=>[100,"F"],"o"=>[110,"F"],"p"=>[111,"F"],"q"=>[115,"F"],"r"=>[116,"F"],"s"=>[118,"F"],"t"=>[119,"F"],"u"=>[122,"F"],"v"=>[123,"F"],"w"=>[125,"F"],"x"=>[126,"F"],"y"=>[129,"F"],"z"=>[143,"F"],"\x7B"=>[148,"F"],"\x7C"=>[151,"F"],"\x7D"=>[153,"F"],"~"=>[83,"F"],"\x7F"=>[10,"F"],"\x80"=>[77,"G0"],"\x81"=>[18,"G0"],"\x82"=>[77,"G1"],"\x83"=>[18,"G1"],"\x84"=>[77,"G2"],"\x85"=>[18,"G2"],"\x86"=>[77,"Ga"],"\x87"=>[18,"Ga"],"\x88"=>[77,"Gc"],"\x89"=>[18,"Gc"],"\x8A"=>[77,"Ge"],"\x8B"=>[18,"Ge"],"\x8C"=>[77,"Gi"],"\x8D"=>[18,"Gi"],"\x8E"=>[77,"Go"],"\x8F"=>[18,"Go"],"\x90"=>[77,"Gs"],"\x91"=>[18,"Gs"],"\x92"=>[77,"Gt"],"\x93"=>[18,"Gt"],"\x94"=>[0,"G\x20"],"\x95"=>[0,"G\x25"],"\x96"=>[0,"G-"],"\x97"=>[0,"G."],"\x98"=>[0,"G\x2F"],"\x99"=>[0,"G3"],"\x9A"=>[0,"G4"],"\x9B"=>[0,"G5"],"\x9C"=>[0,"G6"],"\x9D"=>[0,"G7"],"\x9E"=>[0,"G8"],"\x9F"=>[0,"G9"],"\xA0"=>[0,"G\x3D"],"\xA1"=>[0,"GA"],"\xA2"=>[0,"G_"],"\xA3"=>[0,"Gb"],"\xA4"=>[0,"Gd"],"\xA5"=>[0,"Gf"],"\xA6"=>[0,"Gg"],"\xA7"=>[0,"Gh"],"\xA8"=>[0,"Gl"],"\xA9"=>[0,"Gm"],"\xAA"=>[0,"Gn"],"\xAB"=>[0,"Gp"],"\xAC"=>[0,"Gr"],"\xAD"=>[0,"Gu"],"\xAE"=>[100,"G"],"\xAF"=>[110,"G"],"\xB0"=>[111,"G"],"\xB1"=>[115,"G"],"\xB2"=>[116,"G"],"\xB3"=>[118,"G"],"\xB4"=>[119,"G"],"\xB5"=>[122,"G"],"\xB6"=>[123,"G"],"\xB7"=>[125,"G"],"\xB8"=>[126,"G"],"\xB9"=>[129,"G"],"\xBA"=>[143,"G"],"\xBB"=>[148,"G"],"\xBC"=>[151,"G"],"\xBD"=>[153,"G"],"\xBE"=>[83,"G"],"\xBF"=>[10,"G"],"\xC0"=>[77,"H0"],"\xC1"=>[18,"H0"],"\xC2"=>[77,"H1"],"\xC3"=>[18,"H1"],"\xC4"=>[77,"H2"],"\xC5"=>[18,"H2"],"\xC6"=>[77,"Ha"],"\xC7"=>[18,"Ha"],"\xC8"=>[77,"Hc"],"\xC9"=>[18,"Hc"],"\xCA"=>[77,"He"],"\xCB"=>[18,"He"],"\xCC"=>[77,"Hi"],"\xCD"=>[18,"Hi"],"\xCE"=>[77,"Ho"],"\xCF"=>[18,"Ho"],"\xD0"=>[77,"Hs"],"\xD1"=>[18,"Hs"],"\xD2"=>[77,"Ht"],"\xD3"=>[18,"Ht"],"\xD4"=>[0,"H\x20"],"\xD5"=>[0,"H\x25"],"\xD6"=>[0,"H-"],"\xD7"=>[0,"H."],"\xD8"=>[0,"H\x2F"],"\xD9"=>[0,"H3"],"\xDA"=>[0,"H4"],"\xDB"=>[0,"H5"],"\xDC"=>[0,"H6"],"\xDD"=>[0,"H7"],"\xDE"=>[0,"H8"],"\xDF"=>[0,"H9"],"\xE0"=>[0,"H\x3D"],"\xE1"=>[0,"HA"],"\xE2"=>[0,"H_"],"\xE3"=>[0,"Hb"],"\xE4"=>[0,"Hd"],"\xE5"=>[0,"Hf"],"\xE6"=>[0,"Hg"],"\xE7"=>[0,"Hh"],"\xE8"=>[0,"Hl"],"\xE9"=>[0,"Hm"],"\xEA"=>[0,"Hn"],"\xEB"=>[0,"Hp"],"\xEC"=>[0,"Hr"],"\xED"=>[0,"Hu"],"\xEE"=>[100,"H"],"\xEF"=>[110,"H"],"\xF0"=>[111,"H"],"\xF1"=>[115,"H"],"\xF2"=>[116,"H"],"\xF3"=>[118,"H"],"\xF4"=>[119,"H"],"\xF5"=>[122,"H"],"\xF6"=>[123,"H"],"\xF7"=>[125,"H"],"\xF8"=>[126,"H"],"\xF9"=>[129,"H"],"\xFA"=>[143,"H"],"\xFB"=>[148,"H"],"\xFC"=>[151,"H"],"\xFD"=>[153,"H"],"\xFE"=>[83,"H"],"\xFF"=>[10,"H"],],["\x00"=>[0,"E0"],"\x01"=>[0,"E1"],"\x02"=>[0,"E2"],"\x03"=>[0,"Ea"],"\x04"=>[0,"Ec"],"\x05"=>[0,"Ee"],"\x06"=>[0,"Ei"],"\x07"=>[0,"Eo"],"\x08"=>[0,"Es"],"\x09"=>[0,"Et"],"\x0A"=>[73,"E"],"\x0B"=>[88,"E"],"\x0C"=>[89,"E"],"\x0D"=>[96,"E"],"\x0E"=>[97,"E"],"\x0F"=>[99,"E"],"\x10"=>[106,"E"],"\x11"=>[136,"E"],"\x12"=>[139,"E"],"\x13"=>[141,"E"],"\x14"=>[145,"E"],"\x15"=>[147,"E"],"\x16"=>[149,"E"],"\x17"=>[101,"E"],"\x18"=>[112,"E"],"\x19"=>[117,"E"],"\x1A"=>[120,"E"],"\x1B"=>[124,"E"],"\x1C"=>[127,"E"],"\x1D"=>[144,"E"],"\x1E"=>[152,"E"],"\x1F"=>[11,"E"],"\x20"=>[0,"F0"],"\x21"=>[0,"F1"],"\x22"=>[0,"F2"],"\x23"=>[0,"Fa"],"\x24"=>[0,"Fc"],"\x25"=>[0,"Fe"],"\x26"=>[0,"Fi"],"\x27"=>[0,"Fo"],"\x28"=>[0,"Fs"],"\x29"=>[0,"Ft"],"\x2A"=>[73,"F"],"\x2B"=>[88,"F"],"\x2C"=>[89,"F"],"-"=>[96,"F"],"."=>[97,"F"],"\x2F"=>[99,"F"],[106,"F"],[136,"F"],[139,"F"],[141,"F"],[145,"F"],[147,"F"],[149,"F"],[101,"F"],[112,"F"],[117,"F"],"\x3A"=>[120,"F"],"\x3B"=>[124,"F"],"\x3C"=>[127,"F"],"\x3D"=>[144,"F"],"\x3E"=>[152,"F"],"\x3F"=>[11,"F"],"\x40"=>[0,"G0"],"A"=>[0,"G1"],"B"=>[0,"G2"],"C"=>[0,"Ga"],"D"=>[0,"Gc"],"E"=>[0,"Ge"],"F"=>[0,"Gi"],"G"=>[0,"Go"],"H"=>[0,"Gs"],"I"=>[0,"Gt"],"J"=>[73,"G"],"K"=>[88,"G"],"L"=>[89,"G"],"M"=>[96,"G"],"N"=>[97,"G"],"O"=>[99,"G"],"P"=>[106,"G"],"Q"=>[136,"G"],"R"=>[139,"G"],"S"=>[141,"G"],"T"=>[145,"G"],"U"=>[147,"G"],"V"=>[149,"G"],"W"=>[101,"G"],"X"=>[112,"G"],"Y"=>[117,"G"],"Z"=>[120,"G"],"\x5B"=>[124,"G"],"\x5C"=>[127,"G"],"\x5D"=>[144,"G"],"\x5E"=>[152,"G"],"_"=>[11,"G"],"\x60"=>[0,"H0"],"a"=>[0,"H1"],"b"=>[0,"H2"],"c"=>[0,"Ha"],"d"=>[0,"Hc"],"e"=>[0,"He"],"f"=>[0,"Hi"],"g"=>[0,"Ho"],"h"=>[0,"Hs"],"i"=>[0,"Ht"],"j"=>[73,"H"],"k"=>[88,"H"],"l"=>[89,"H"],"m"=>[96,"H"],"n"=>[97,"H"],"o"=>[99,"H"],"p"=>[106,"H"],"q"=>[136,"H"],"r"=>[139,"H"],"s"=>[141,"H"],"t"=>[145,"H"],"u"=>[147,"H"],"v"=>[149,"H"],"w"=>[101,"H"],"x"=>[112,"H"],"y"=>[117,"H"],"z"=>[120,"H"],"\x7B"=>[124,"H"],"\x7C"=>[127,"H"],"\x7D"=>[144,"H"],"~"=>[152,"H"],"\x7F"=>[11,"H"],"\x80"=>[0,"I0"],"\x81"=>[0,"I1"],"\x82"=>[0,"I2"],"\x83"=>[0,"Ia"],"\x84"=>[0,"Ic"],"\x85"=>[0,"Ie"],"\x86"=>[0,"Ii"],"\x87"=>[0,"Io"],"\x88"=>[0,"Is"],"\x89"=>[0,"It"],"\x8A"=>[73,"I"],"\x8B"=>[88,"I"],"\x8C"=>[89,"I"],"\x8D"=>[96,"I"],"\x8E"=>[97,"I"],"\x8F"=>[99,"I"],"\x90"=>[106,"I"],"\x91"=>[136,"I"],"\x92"=>[139,"I"],"\x93"=>[141,"I"],"\x94"=>[145,"I"],"\x95"=>[147,"I"],"\x96"=>[149,"I"],"\x97"=>[101,"I"],"\x98"=>[112,"I"],"\x99"=>[117,"I"],"\x9A"=>[120,"I"],"\x9B"=>[124,"I"],"\x9C"=>[127,"I"],"\x9D"=>[144,"I"],"\x9E"=>[152,"I"],"\x9F"=>[11,"I"],"\xA0"=>[0,"J0"],"\xA1"=>[0,"J1"],"\xA2"=>[0,"J2"],"\xA3"=>[0,"Ja"],"\xA4"=>[0,"Jc"],"\xA5"=>[0,"Je"],"\xA6"=>[0,"Ji"],"\xA7"=>[0,"Jo"],"\xA8"=>[0,"Js"],"\xA9"=>[0,"Jt"],"\xAA"=>[73,"J"],"\xAB"=>[88,"J"],"\xAC"=>[89,"J"],"\xAD"=>[96,"J"],"\xAE"=>[97,"J"],"\xAF"=>[99,"J"],"\xB0"=>[106,"J"],"\xB1"=>[136,"J"],"\xB2"=>[139,"J"],"\xB3"=>[141,"J"],"\xB4"=>[145,"J"],"\xB5"=>[147,"J"],"\xB6"=>[149,"J"],"\xB7"=>[101,"J"],"\xB8"=>[112,"J"],"\xB9"=>[117,"J"],"\xBA"=>[120,"J"],"\xBB"=>[124,"J"],"\xBC"=>[127,"J"],"\xBD"=>[144,"J"],"\xBE"=>[152,"J"],"\xBF"=>[11,"J"],"\xC0"=>[0,"K0"],"\xC1"=>[0,"K1"],"\xC2"=>[0,"K2"],"\xC3"=>[0,"Ka"],"\xC4"=>[0,"Kc"],"\xC5"=>[0,"Ke"],"\xC6"=>[0,"Ki"],"\xC7"=>[0,"Ko"],"\xC8"=>[0,"Ks"],"\xC9"=>[0,"Kt"],"\xCA"=>[73,"K"],"\xCB"=>[88,"K"],"\xCC"=>[89,"K"],"\xCD"=>[96,"K"],"\xCE"=>[97,"K"],"\xCF"=>[99,"K"],"\xD0"=>[106,"K"],"\xD1"=>[136,"K"],"\xD2"=>[139,"K"],"\xD3"=>[141,"K"],"\xD4"=>[145,"K"],"\xD5"=>[147,"K"],"\xD6"=>[149,"K"],"\xD7"=>[101,"K"],"\xD8"=>[112,"K"],"\xD9"=>[117,"K"],"\xDA"=>[120,"K"],"\xDB"=>[124,"K"],"\xDC"=>[127,"K"],"\xDD"=>[144,"K"],"\xDE"=>[152,"K"],"\xDF"=>[11,"K"],"\xE0"=>[0,"L0"],"\xE1"=>[0,"L1"],"\xE2"=>[0,"L2"],"\xE3"=>[0,"La"],"\xE4"=>[0,"Lc"],"\xE5"=>[0,"Le"],"\xE6"=>[0,"Li"],"\xE7"=>[0,"Lo"],"\xE8"=>[0,"Ls"],"\xE9"=>[0,"Lt"],"\xEA"=>[73,"L"],"\xEB"=>[88,"L"],"\xEC"=>[89,"L"],"\xED"=>[96,"L"],"\xEE"=>[97,"L"],"\xEF"=>[99,"L"],"\xF0"=>[106,"L"],"\xF1"=>[136,"L"],"\xF2"=>[139,"L"],"\xF3"=>[141,"L"],"\xF4"=>[145,"L"],"\xF5"=>[147,"L"],"\xF6"=>[149,"L"],"\xF7"=>[101,"L"],"\xF8"=>[112,"L"],"\xF9"=>[117,"L"],"\xFA"=>[120,"L"],"\xFB"=>[124,"L"],"\xFC"=>[127,"L"],"\xFD"=>[144,"L"],"\xFE"=>[152,"L"],"\xFF"=>[11,"L"],],["\x00"=>[92,"E"],"\x01"=>[95,"E"],"\x02"=>[137,"E"],"\x03"=>[142,"E"],"\x04"=>[150,"E"],"\x05"=>[74,"E"],"\x06"=>[90,"E"],"\x07"=>[98,"E"],"\x08"=>[107,"E"],"\x09"=>[140,"E"],"\x0A"=>[146,"E"],"\x0B"=>[102,"E"],"\x0C"=>[113,"E"],"\x0D"=>[121,"E"],"\x0E"=>[128,"E"],"\x0F"=>[12,"E"],"\x10"=>[92,"F"],"\x11"=>[95,"F"],"\x12"=>[137,"F"],"\x13"=>[142,"F"],"\x14"=>[150,"F"],"\x15"=>[74,"F"],"\x16"=>[90,"F"],"\x17"=>[98,"F"],"\x18"=>[107,"F"],"\x19"=>[140,"F"],"\x1A"=>[146,"F"],"\x1B"=>[102,"F"],"\x1C"=>[113,"F"],"\x1D"=>[121,"F"],"\x1E"=>[128,"F"],"\x1F"=>[12,"F"],"\x20"=>[92,"G"],"\x21"=>[95,"G"],"\x22"=>[137,"G"],"\x23"=>[142,"G"],"\x24"=>[150,"G"],"\x25"=>[74,"G"],"\x26"=>[90,"G"],"\x27"=>[98,"G"],"\x28"=>[107,"G"],"\x29"=>[140,"G"],"\x2A"=>[146,"G"],"\x2B"=>[102,"G"],"\x2C"=>[113,"G"],"-"=>[121,"G"],"."=>[128,"G"],"\x2F"=>[12,"G"],[92,"H"],[95,"H"],[137,"H"],[142,"H"],[150,"H"],[74,"H"],[90,"H"],[98,"H"],[107,"H"],[140,"H"],"\x3A"=>[146,"H"],"\x3B"=>[102,"H"],"\x3C"=>[113,"H"],"\x3D"=>[121,"H"],"\x3E"=>[128,"H"],"\x3F"=>[12,"H"],"\x40"=>[92,"I"],"A"=>[95,"I"],"B"=>[137,"I"],"C"=>[142,"I"],"D"=>[150,"I"],"E"=>[74,"I"],"F"=>[90,"I"],"G"=>[98,"I"],"H"=>[107,"I"],"I"=>[140,"I"],"J"=>[146,"I"],"K"=>[102,"I"],"L"=>[113,"I"],"M"=>[121,"I"],"N"=>[128,"I"],"O"=>[12,"I"],"P"=>[92,"J"],"Q"=>[95,"J"],"R"=>[137,"J"],"S"=>[142,"J"],"T"=>[150,"J"],"U"=>[74,"J"],"V"=>[90,"J"],"W"=>[98,"J"],"X"=>[107,"J"],"Y"=>[140,"J"],"Z"=>[146,"J"],"\x5B"=>[102,"J"],"\x5C"=>[113,"J"],"\x5D"=>[121,"J"],"\x5E"=>[128,"J"],"_"=>[12,"J"],"\x60"=>[92,"K"],"a"=>[95,"K"],"b"=>[137,"K"],"c"=>[142,"K"],"d"=>[150,"K"],"e"=>[74,"K"],"f"=>[90,"K"],"g"=>[98,"K"],"h"=>[107,"K"],"i"=>[140,"K"],"j"=>[146,"K"],"k"=>[102,"K"],"l"=>[113,"K"],"m"=>[121,"K"],"n"=>[128,"K"],"o"=>[12,"K"],"p"=>[92,"L"],"q"=>[95,"L"],"r"=>[137,"L"],"s"=>[142,"L"],"t"=>[150,"L"],"u"=>[74,"L"],"v"=>[90,"L"],"w"=>[98,"L"],"x"=>[107,"L"],"y"=>[140,"L"],"z"=>[146,"L"],"\x7B"=>[102,"L"],"\x7C"=>[113,"L"],"\x7D"=>[121,"L"],"~"=>[128,"L"],"\x7F"=>[12,"L"],"\x80"=>[92,"M"],"\x81"=>[95,"M"],"\x82"=>[137,"M"],"\x83"=>[142,"M"],"\x84"=>[150,"M"],"\x85"=>[74,"M"],"\x86"=>[90,"M"],"\x87"=>[98,"M"],"\x88"=>[107,"M"],"\x89"=>[140,"M"],"\x8A"=>[146,"M"],"\x8B"=>[102,"M"],"\x8C"=>[113,"M"],"\x8D"=>[121,"M"],"\x8E"=>[128,"M"],"\x8F"=>[12,"M"],"\x90"=>[92,"N"],"\x91"=>[95,"N"],"\x92"=>[137,"N"],"\x93"=>[142,"N"],"\x94"=>[150,"N"],"\x95"=>[74,"N"],"\x96"=>[90,"N"],"\x97"=>[98,"N"],"\x98"=>[107,"N"],"\x99"=>[140,"N"],"\x9A"=>[146,"N"],"\x9B"=>[102,"N"],"\x9C"=>[113,"N"],"\x9D"=>[121,"N"],"\x9E"=>[128,"N"],"\x9F"=>[12,"N"],"\xA0"=>[92,"O"],"\xA1"=>[95,"O"],"\xA2"=>[137,"O"],"\xA3"=>[142,"O"],"\xA4"=>[150,"O"],"\xA5"=>[74,"O"],"\xA6"=>[90,"O"],"\xA7"=>[98,"O"],"\xA8"=>[107,"O"],"\xA9"=>[140,"O"],"\xAA"=>[146,"O"],"\xAB"=>[102,"O"],"\xAC"=>[113,"O"],"\xAD"=>[121,"O"],"\xAE"=>[128,"O"],"\xAF"=>[12,"O"],"\xB0"=>[92,"P"],"\xB1"=>[95,"P"],"\xB2"=>[137,"P"],"\xB3"=>[142,"P"],"\xB4"=>[150,"P"],"\xB5"=>[74,"P"],"\xB6"=>[90,"P"],"\xB7"=>[98,"P"],"\xB8"=>[107,"P"],"\xB9"=>[140,"P"],"\xBA"=>[146,"P"],"\xBB"=>[102,"P"],"\xBC"=>[113,"P"],"\xBD"=>[121,"P"],"\xBE"=>[128,"P"],"\xBF"=>[12,"P"],"\xC0"=>[92,"Q"],"\xC1"=>[95,"Q"],"\xC2"=>[137,"Q"],"\xC3"=>[142,"Q"],"\xC4"=>[150,"Q"],"\xC5"=>[74,"Q"],"\xC6"=>[90,"Q"],"\xC7"=>[98,"Q"],"\xC8"=>[107,"Q"],"\xC9"=>[140,"Q"],"\xCA"=>[146,"Q"],"\xCB"=>[102,"Q"],"\xCC"=>[113,"Q"],"\xCD"=>[121,"Q"],"\xCE"=>[128,"Q"],"\xCF"=>[12,"Q"],"\xD0"=>[92,"R"],"\xD1"=>[95,"R"],"\xD2"=>[137,"R"],"\xD3"=>[142,"R"],"\xD4"=>[150,"R"],"\xD5"=>[74,"R"],"\xD6"=>[90,"R"],"\xD7"=>[98,"R"],"\xD8"=>[107,"R"],"\xD9"=>[140,"R"],"\xDA"=>[146,"R"],"\xDB"=>[102,"R"],"\xDC"=>[113,"R"],"\xDD"=>[121,"R"],"\xDE"=>[128,"R"],"\xDF"=>[12,"R"],"\xE0"=>[92,"S"],"\xE1"=>[95,"S"],"\xE2"=>[137,"S"],"\xE3"=>[142,"S"],"\xE4"=>[150,"S"],"\xE5"=>[74,"S"],"\xE6"=>[90,"S"],"\xE7"=>[98,"S"],"\xE8"=>[107,"S"],"\xE9"=>[140,"S"],"\xEA"=>[146,"S"],"\xEB"=>[102,"S"],"\xEC"=>[113,"S"],"\xED"=>[121,"S"],"\xEE"=>[128,"S"],"\xEF"=>[12,"S"],"\xF0"=>[92,"T"],"\xF1"=>[95,"T"],"\xF2"=>[137,"T"],"\xF3"=>[142,"T"],"\xF4"=>[150,"T"],"\xF5"=>[74,"T"],"\xF6"=>[90,"T"],"\xF7"=>[98,"T"],"\xF8"=>[107,"T"],"\xF9"=>[140,"T"],"\xFA"=>[146,"T"],"\xFB"=>[102,"T"],"\xFC"=>[113,"T"],"\xFD"=>[121,"T"],"\xFE"=>[128,"T"],"\xFF"=>[12,"T"],],["\x00"=>[94,"G0"],"\x01"=>[76,"G0"],"\x02"=>[104,"G0"],"\x03"=>[16,"G0"],"\x04"=>[94,"G1"],"\x05"=>[76,"G1"],"\x06"=>[104,"G1"],"\x07"=>[16,"G1"],"\x08"=>[94,"G2"],"\x09"=>[76,"G2"],"\x0A"=>[104,"G2"],"\x0B"=>[16,"G2"],"\x0C"=>[94,"Ga"],"\x0D"=>[76,"Ga"],"\x0E"=>[104,"Ga"],"\x0F"=>[16,"Ga"],"\x10"=>[94,"Gc"],"\x11"=>[76,"Gc"],"\x12"=>[104,"Gc"],"\x13"=>[16,"Gc"],"\x14"=>[94,"Ge"],"\x15"=>[76,"Ge"],"\x16"=>[104,"Ge"],"\x17"=>[16,"Ge"],"\x18"=>[94,"Gi"],"\x19"=>[76,"Gi"],"\x1A"=>[104,"Gi"],"\x1B"=>[16,"Gi"],"\x1C"=>[94,"Go"],"\x1D"=>[76,"Go"],"\x1E"=>[104,"Go"],"\x1F"=>[16,"Go"],"\x20"=>[94,"Gs"],"\x21"=>[76,"Gs"],"\x22"=>[104,"Gs"],"\x23"=>[16,"Gs"],"\x24"=>[94,"Gt"],"\x25"=>[76,"Gt"],"\x26"=>[104,"Gt"],"\x27"=>[16,"Gt"],"\x28"=>[77,"G\x20"],"\x29"=>[18,"G\x20"],"\x2A"=>[77,"G\x25"],"\x2B"=>[18,"G\x25"],"\x2C"=>[77,"G-"],"-"=>[18,"G-"],"."=>[77,"G."],"\x2F"=>[18,"G."],[77,"G\x2F"],[18,"G\x2F"],[77,"G3"],[18,"G3"],[77,"G4"],[18,"G4"],[77,"G5"],[18,"G5"],[77,"G6"],[18,"G6"],"\x3A"=>[77,"G7"],"\x3B"=>[18,"G7"],"\x3C"=>[77,"G8"],"\x3D"=>[18,"G8"],"\x3E"=>[77,"G9"],"\x3F"=>[18,"G9"],"\x40"=>[77,"G\x3D"],"A"=>[18,"G\x3D"],"B"=>[77,"GA"],"C"=>[18,"GA"],"D"=>[77,"G_"],"E"=>[18,"G_"],"F"=>[77,"Gb"],"G"=>[18,"Gb"],"H"=>[77,"Gd"],"I"=>[18,"Gd"],"J"=>[77,"Gf"],"K"=>[18,"Gf"],"L"=>[77,"Gg"],"M"=>[18,"Gg"],"N"=>[77,"Gh"],"O"=>[18,"Gh"],"P"=>[77,"Gl"],"Q"=>[18,"Gl"],"R"=>[77,"Gm"],"S"=>[18,"Gm"],"T"=>[77,"Gn"],"U"=>[18,"Gn"],"V"=>[77,"Gp"],"W"=>[18,"Gp"],"X"=>[77,"Gr"],"Y"=>[18,"Gr"],"Z"=>[77,"Gu"],"\x5B"=>[18,"Gu"],"\x5C"=>[0,"G\x3A"],"\x5D"=>[0,"GB"],"\x5E"=>[0,"GC"],"_"=>[0,"GD"],"\x60"=>[0,"GE"],"a"=>[0,"GF"],"b"=>[0,"GG"],"c"=>[0,"GH"],"d"=>[0,"GI"],"e"=>[0,"GJ"],"f"=>[0,"GK"],"g"=>[0,"GL"],"h"=>[0,"GM"],"i"=>[0,"GN"],"j"=>[0,"GO"],"k"=>[0,"GP"],"l"=>[0,"GQ"],"m"=>[0,"GR"],"n"=>[0,"GS"],"o"=>[0,"GT"],"p"=>[0,"GU"],"q"=>[0,"GV"],"r"=>[0,"GW"],"s"=>[0,"GY"],"t"=>[0,"Gj"],"u"=>[0,"Gk"],"v"=>[0,"Gq"],"w"=>[0,"Gv"],"x"=>[0,"Gw"],"y"=>[0,"Gx"],"z"=>[0,"Gy"],"\x7B"=>[0,"Gz"],"\x7C"=>[82,"G"],"\x7D"=>[87,"G"],"~"=>[130,"G"],"\x7F"=>[9,"G"],"\x80"=>[94,"H0"],"\x81"=>[76,"H0"],"\x82"=>[104,"H0"],"\x83"=>[16,"H0"],"\x84"=>[94,"H1"],"\x85"=>[76,"H1"],"\x86"=>[104,"H1"],"\x87"=>[16,"H1"],"\x88"=>[94,"H2"],"\x89"=>[76,"H2"],"\x8A"=>[104,"H2"],"\x8B"=>[16,"H2"],"\x8C"=>[94,"Ha"],"\x8D"=>[76,"Ha"],"\x8E"=>[104,"Ha"],"\x8F"=>[16,"Ha"],"\x90"=>[94,"Hc"],"\x91"=>[76,"Hc"],"\x92"=>[104,"Hc"],"\x93"=>[16,"Hc"],"\x94"=>[94,"He"],"\x95"=>[76,"He"],"\x96"=>[104,"He"],"\x97"=>[16,"He"],"\x98"=>[94,"Hi"],"\x99"=>[76,"Hi"],"\x9A"=>[104,"Hi"],"\x9B"=>[16,"Hi"],"\x9C"=>[94,"Ho"],"\x9D"=>[76,"Ho"],"\x9E"=>[104,"Ho"],"\x9F"=>[16,"Ho"],"\xA0"=>[94,"Hs"],"\xA1"=>[76,"Hs"],"\xA2"=>[104,"Hs"],"\xA3"=>[16,"Hs"],"\xA4"=>[94,"Ht"],"\xA5"=>[76,"Ht"],"\xA6"=>[104,"Ht"],"\xA7"=>[16,"Ht"],"\xA8"=>[77,"H\x20"],"\xA9"=>[18,"H\x20"],"\xAA"=>[77,"H\x25"],"\xAB"=>[18,"H\x25"],"\xAC"=>[77,"H-"],"\xAD"=>[18,"H-"],"\xAE"=>[77,"H."],"\xAF"=>[18,"H."],"\xB0"=>[77,"H\x2F"],"\xB1"=>[18,"H\x2F"],"\xB2"=>[77,"H3"],"\xB3"=>[18,"H3"],"\xB4"=>[77,"H4"],"\xB5"=>[18,"H4"],"\xB6"=>[77,"H5"],"\xB7"=>[18,"H5"],"\xB8"=>[77,"H6"],"\xB9"=>[18,"H6"],"\xBA"=>[77,"H7"],"\xBB"=>[18,"H7"],"\xBC"=>[77,"H8"],"\xBD"=>[18,"H8"],"\xBE"=>[77,"H9"],"\xBF"=>[18,"H9"],"\xC0"=>[77,"H\x3D"],"\xC1"=>[18,"H\x3D"],"\xC2"=>[77,"HA"],"\xC3"=>[18,"HA"],"\xC4"=>[77,"H_"],"\xC5"=>[18,"H_"],"\xC6"=>[77,"Hb"],"\xC7"=>[18,"Hb"],"\xC8"=>[77,"Hd"],"\xC9"=>[18,"Hd"],"\xCA"=>[77,"Hf"],"\xCB"=>[18,"Hf"],"\xCC"=>[77,"Hg"],"\xCD"=>[18,"Hg"],"\xCE"=>[77,"Hh"],"\xCF"=>[18,"Hh"],"\xD0"=>[77,"Hl"],"\xD1"=>[18,"Hl"],"\xD2"=>[77,"Hm"],"\xD3"=>[18,"Hm"],"\xD4"=>[77,"Hn"],"\xD5"=>[18,"Hn"],"\xD6"=>[77,"Hp"],"\xD7"=>[18,"Hp"],"\xD8"=>[77,"Hr"],"\xD9"=>[18,"Hr"],"\xDA"=>[77,"Hu"],"\xDB"=>[18,"Hu"],"\xDC"=>[0,"H\x3A"],"\xDD"=>[0,"HB"],"\xDE"=>[0,"HC"],"\xDF"=>[0,"HD"],"\xE0"=>[0,"HE"],"\xE1"=>[0,"HF"],"\xE2"=>[0,"HG"],"\xE3"=>[0,"HH"],"\xE4"=>[0,"HI"],"\xE5"=>[0,"HJ"],"\xE6"=>[0,"HK"],"\xE7"=>[0,"HL"],"\xE8"=>[0,"HM"],"\xE9"=>[0,"HN"],"\xEA"=>[0,"HO"],"\xEB"=>[0,"HP"],"\xEC"=>[0,"HQ"],"\xED"=>[0,"HR"],"\xEE"=>[0,"HS"],"\xEF"=>[0,"HT"],"\xF0"=>[0,"HU"],"\xF1"=>[0,"HV"],"\xF2"=>[0,"HW"],"\xF3"=>[0,"HY"],"\xF4"=>[0,"Hj"],"\xF5"=>[0,"Hk"],"\xF6"=>[0,"Hq"],"\xF7"=>[0,"Hv"],"\xF8"=>[0,"Hw"],"\xF9"=>[0,"Hx"],"\xFA"=>[0,"Hy"],"\xFB"=>[0,"Hz"],"\xFC"=>[82,"H"],"\xFD"=>[87,"H"],"\xFE"=>[130,"H"],"\xFF"=>[9,"H"],],["\x00"=>[94,"I0"],"\x01"=>[76,"I0"],"\x02"=>[104,"I0"],"\x03"=>[16,"I0"],"\x04"=>[94,"I1"],"\x05"=>[76,"I1"],"\x06"=>[104,"I1"],"\x07"=>[16,"I1"],"\x08"=>[94,"I2"],"\x09"=>[76,"I2"],"\x0A"=>[104,"I2"],"\x0B"=>[16,"I2"],"\x0C"=>[94,"Ia"],"\x0D"=>[76,"Ia"],"\x0E"=>[104,"Ia"],"\x0F"=>[16,"Ia"],"\x10"=>[94,"Ic"],"\x11"=>[76,"Ic"],"\x12"=>[104,"Ic"],"\x13"=>[16,"Ic"],"\x14"=>[94,"Ie"],"\x15"=>[76,"Ie"],"\x16"=>[104,"Ie"],"\x17"=>[16,"Ie"],"\x18"=>[94,"Ii"],"\x19"=>[76,"Ii"],"\x1A"=>[104,"Ii"],"\x1B"=>[16,"Ii"],"\x1C"=>[94,"Io"],"\x1D"=>[76,"Io"],"\x1E"=>[104,"Io"],"\x1F"=>[16,"Io"],"\x20"=>[94,"Is"],"\x21"=>[76,"Is"],"\x22"=>[104,"Is"],"\x23"=>[16,"Is"],"\x24"=>[94,"It"],"\x25"=>[76,"It"],"\x26"=>[104,"It"],"\x27"=>[16,"It"],"\x28"=>[77,"I\x20"],"\x29"=>[18,"I\x20"],"\x2A"=>[77,"I\x25"],"\x2B"=>[18,"I\x25"],"\x2C"=>[77,"I-"],"-"=>[18,"I-"],"."=>[77,"I."],"\x2F"=>[18,"I."],[77,"I\x2F"],[18,"I\x2F"],[77,"I3"],[18,"I3"],[77,"I4"],[18,"I4"],[77,"I5"],[18,"I5"],[77,"I6"],[18,"I6"],"\x3A"=>[77,"I7"],"\x3B"=>[18,"I7"],"\x3C"=>[77,"I8"],"\x3D"=>[18,"I8"],"\x3E"=>[77,"I9"],"\x3F"=>[18,"I9"],"\x40"=>[77,"I\x3D"],"A"=>[18,"I\x3D"],"B"=>[77,"IA"],"C"=>[18,"IA"],"D"=>[77,"I_"],"E"=>[18,"I_"],"F"=>[77,"Ib"],"G"=>[18,"Ib"],"H"=>[77,"Id"],"I"=>[18,"Id"],"J"=>[77,"If"],"K"=>[18,"If"],"L"=>[77,"Ig"],"M"=>[18,"Ig"],"N"=>[77,"Ih"],"O"=>[18,"Ih"],"P"=>[77,"Il"],"Q"=>[18,"Il"],"R"=>[77,"Im"],"S"=>[18,"Im"],"T"=>[77,"In"],"U"=>[18,"In"],"V"=>[77,"Ip"],"W"=>[18,"Ip"],"X"=>[77,"Ir"],"Y"=>[18,"Ir"],"Z"=>[77,"Iu"],"\x5B"=>[18,"Iu"],"\x5C"=>[0,"I\x3A"],"\x5D"=>[0,"IB"],"\x5E"=>[0,"IC"],"_"=>[0,"ID"],"\x60"=>[0,"IE"],"a"=>[0,"IF"],"b"=>[0,"IG"],"c"=>[0,"IH"],"d"=>[0,"II"],"e"=>[0,"IJ"],"f"=>[0,"IK"],"g"=>[0,"IL"],"h"=>[0,"IM"],"i"=>[0,"IN"],"j"=>[0,"IO"],"k"=>[0,"IP"],"l"=>[0,"IQ"],"m"=>[0,"IR"],"n"=>[0,"IS"],"o"=>[0,"IT"],"p"=>[0,"IU"],"q"=>[0,"IV"],"r"=>[0,"IW"],"s"=>[0,"IY"],"t"=>[0,"Ij"],"u"=>[0,"Ik"],"v"=>[0,"Iq"],"w"=>[0,"Iv"],"x"=>[0,"Iw"],"y"=>[0,"Ix"],"z"=>[0,"Iy"],"\x7B"=>[0,"Iz"],"\x7C"=>[82,"I"],"\x7D"=>[87,"I"],"~"=>[130,"I"],"\x7F"=>[9,"I"],"\x80"=>[94,"J0"],"\x81"=>[76,"J0"],"\x82"=>[104,"J0"],"\x83"=>[16,"J0"],"\x84"=>[94,"J1"],"\x85"=>[76,"J1"],"\x86"=>[104,"J1"],"\x87"=>[16,"J1"],"\x88"=>[94,"J2"],"\x89"=>[76,"J2"],"\x8A"=>[104,"J2"],"\x8B"=>[16,"J2"],"\x8C"=>[94,"Ja"],"\x8D"=>[76,"Ja"],"\x8E"=>[104,"Ja"],"\x8F"=>[16,"Ja"],"\x90"=>[94,"Jc"],"\x91"=>[76,"Jc"],"\x92"=>[104,"Jc"],"\x93"=>[16,"Jc"],"\x94"=>[94,"Je"],"\x95"=>[76,"Je"],"\x96"=>[104,"Je"],"\x97"=>[16,"Je"],"\x98"=>[94,"Ji"],"\x99"=>[76,"Ji"],"\x9A"=>[104,"Ji"],"\x9B"=>[16,"Ji"],"\x9C"=>[94,"Jo"],"\x9D"=>[76,"Jo"],"\x9E"=>[104,"Jo"],"\x9F"=>[16,"Jo"],"\xA0"=>[94,"Js"],"\xA1"=>[76,"Js"],"\xA2"=>[104,"Js"],"\xA3"=>[16,"Js"],"\xA4"=>[94,"Jt"],"\xA5"=>[76,"Jt"],"\xA6"=>[104,"Jt"],"\xA7"=>[16,"Jt"],"\xA8"=>[77,"J\x20"],"\xA9"=>[18,"J\x20"],"\xAA"=>[77,"J\x25"],"\xAB"=>[18,"J\x25"],"\xAC"=>[77,"J-"],"\xAD"=>[18,"J-"],"\xAE"=>[77,"J."],"\xAF"=>[18,"J."],"\xB0"=>[77,"J\x2F"],"\xB1"=>[18,"J\x2F"],"\xB2"=>[77,"J3"],"\xB3"=>[18,"J3"],"\xB4"=>[77,"J4"],"\xB5"=>[18,"J4"],"\xB6"=>[77,"J5"],"\xB7"=>[18,"J5"],"\xB8"=>[77,"J6"],"\xB9"=>[18,"J6"],"\xBA"=>[77,"J7"],"\xBB"=>[18,"J7"],"\xBC"=>[77,"J8"],"\xBD"=>[18,"J8"],"\xBE"=>[77,"J9"],"\xBF"=>[18,"J9"],"\xC0"=>[77,"J\x3D"],"\xC1"=>[18,"J\x3D"],"\xC2"=>[77,"JA"],"\xC3"=>[18,"JA"],"\xC4"=>[77,"J_"],"\xC5"=>[18,"J_"],"\xC6"=>[77,"Jb"],"\xC7"=>[18,"Jb"],"\xC8"=>[77,"Jd"],"\xC9"=>[18,"Jd"],"\xCA"=>[77,"Jf"],"\xCB"=>[18,"Jf"],"\xCC"=>[77,"Jg"],"\xCD"=>[18,"Jg"],"\xCE"=>[77,"Jh"],"\xCF"=>[18,"Jh"],"\xD0"=>[77,"Jl"],"\xD1"=>[18,"Jl"],"\xD2"=>[77,"Jm"],"\xD3"=>[18,"Jm"],"\xD4"=>[77,"Jn"],"\xD5"=>[18,"Jn"],"\xD6"=>[77,"Jp"],"\xD7"=>[18,"Jp"],"\xD8"=>[77,"Jr"],"\xD9"=>[18,"Jr"],"\xDA"=>[77,"Ju"],"\xDB"=>[18,"Ju"],"\xDC"=>[0,"J\x3A"],"\xDD"=>[0,"JB"],"\xDE"=>[0,"JC"],"\xDF"=>[0,"JD"],"\xE0"=>[0,"JE"],"\xE1"=>[0,"JF"],"\xE2"=>[0,"JG"],"\xE3"=>[0,"JH"],"\xE4"=>[0,"JI"],"\xE5"=>[0,"JJ"],"\xE6"=>[0,"JK"],"\xE7"=>[0,"JL"],"\xE8"=>[0,"JM"],"\xE9"=>[0,"JN"],"\xEA"=>[0,"JO"],"\xEB"=>[0,"JP"],"\xEC"=>[0,"JQ"],"\xED"=>[0,"JR"],"\xEE"=>[0,"JS"],"\xEF"=>[0,"JT"],"\xF0"=>[0,"JU"],"\xF1"=>[0,"JV"],"\xF2"=>[0,"JW"],"\xF3"=>[0,"JY"],"\xF4"=>[0,"Jj"],"\xF5"=>[0,"Jk"],"\xF6"=>[0,"Jq"],"\xF7"=>[0,"Jv"],"\xF8"=>[0,"Jw"],"\xF9"=>[0,"Jx"],"\xFA"=>[0,"Jy"],"\xFB"=>[0,"Jz"],"\xFC"=>[82,"J"],"\xFD"=>[87,"J"],"\xFE"=>[130,"J"],"\xFF"=>[9,"J"],],["\x00"=>[77,"I0"],"\x01"=>[18,"I0"],"\x02"=>[77,"I1"],"\x03"=>[18,"I1"],"\x04"=>[77,"I2"],"\x05"=>[18,"I2"],"\x06"=>[77,"Ia"],"\x07"=>[18,"Ia"],"\x08"=>[77,"Ic"],"\x09"=>[18,"Ic"],"\x0A"=>[77,"Ie"],"\x0B"=>[18,"Ie"],"\x0C"=>[77,"Ii"],"\x0D"=>[18,"Ii"],"\x0E"=>[77,"Io"],"\x0F"=>[18,"Io"],"\x10"=>[77,"Is"],"\x11"=>[18,"Is"],"\x12"=>[77,"It"],"\x13"=>[18,"It"],"\x14"=>[0,"I\x20"],"\x15"=>[0,"I\x25"],"\x16"=>[0,"I-"],"\x17"=>[0,"I."],"\x18"=>[0,"I\x2F"],"\x19"=>[0,"I3"],"\x1A"=>[0,"I4"],"\x1B"=>[0,"I5"],"\x1C"=>[0,"I6"],"\x1D"=>[0,"I7"],"\x1E"=>[0,"I8"],"\x1F"=>[0,"I9"],"\x20"=>[0,"I\x3D"],"\x21"=>[0,"IA"],"\x22"=>[0,"I_"],"\x23"=>[0,"Ib"],"\x24"=>[0,"Id"],"\x25"=>[0,"If"],"\x26"=>[0,"Ig"],"\x27"=>[0,"Ih"],"\x28"=>[0,"Il"],"\x29"=>[0,"Im"],"\x2A"=>[0,"In"],"\x2B"=>[0,"Ip"],"\x2C"=>[0,"Ir"],"-"=>[0,"Iu"],"."=>[100,"I"],"\x2F"=>[110,"I"],[111,"I"],[115,"I"],[116,"I"],[118,"I"],[119,"I"],[122,"I"],[123,"I"],[125,"I"],[126,"I"],[129,"I"],"\x3A"=>[143,"I"],"\x3B"=>[148,"I"],"\x3C"=>[151,"I"],"\x3D"=>[153,"I"],"\x3E"=>[83,"I"],"\x3F"=>[10,"I"],"\x40"=>[77,"J0"],"A"=>[18,"J0"],"B"=>[77,"J1"],"C"=>[18,"J1"],"D"=>[77,"J2"],"E"=>[18,"J2"],"F"=>[77,"Ja"],"G"=>[18,"Ja"],"H"=>[77,"Jc"],"I"=>[18,"Jc"],"J"=>[77,"Je"],"K"=>[18,"Je"],"L"=>[77,"Ji"],"M"=>[18,"Ji"],"N"=>[77,"Jo"],"O"=>[18,"Jo"],"P"=>[77,"Js"],"Q"=>[18,"Js"],"R"=>[77,"Jt"],"S"=>[18,"Jt"],"T"=>[0,"J\x20"],"U"=>[0,"J\x25"],"V"=>[0,"J-"],"W"=>[0,"J."],"X"=>[0,"J\x2F"],"Y"=>[0,"J3"],"Z"=>[0,"J4"],"\x5B"=>[0,"J5"],"\x5C"=>[0,"J6"],"\x5D"=>[0,"J7"],"\x5E"=>[0,"J8"],"_"=>[0,"J9"],"\x60"=>[0,"J\x3D"],"a"=>[0,"JA"],"b"=>[0,"J_"],"c"=>[0,"Jb"],"d"=>[0,"Jd"],"e"=>[0,"Jf"],"f"=>[0,"Jg"],"g"=>[0,"Jh"],"h"=>[0,"Jl"],"i"=>[0,"Jm"],"j"=>[0,"Jn"],"k"=>[0,"Jp"],"l"=>[0,"Jr"],"m"=>[0,"Ju"],"n"=>[100,"J"],"o"=>[110,"J"],"p"=>[111,"J"],"q"=>[115,"J"],"r"=>[116,"J"],"s"=>[118,"J"],"t"=>[119,"J"],"u"=>[122,"J"],"v"=>[123,"J"],"w"=>[125,"J"],"x"=>[126,"J"],"y"=>[129,"J"],"z"=>[143,"J"],"\x7B"=>[148,"J"],"\x7C"=>[151,"J"],"\x7D"=>[153,"J"],"~"=>[83,"J"],"\x7F"=>[10,"J"],"\x80"=>[77,"K0"],"\x81"=>[18,"K0"],"\x82"=>[77,"K1"],"\x83"=>[18,"K1"],"\x84"=>[77,"K2"],"\x85"=>[18,"K2"],"\x86"=>[77,"Ka"],"\x87"=>[18,"Ka"],"\x88"=>[77,"Kc"],"\x89"=>[18,"Kc"],"\x8A"=>[77,"Ke"],"\x8B"=>[18,"Ke"],"\x8C"=>[77,"Ki"],"\x8D"=>[18,"Ki"],"\x8E"=>[77,"Ko"],"\x8F"=>[18,"Ko"],"\x90"=>[77,"Ks"],"\x91"=>[18,"Ks"],"\x92"=>[77,"Kt"],"\x93"=>[18,"Kt"],"\x94"=>[0,"K\x20"],"\x95"=>[0,"K\x25"],"\x96"=>[0,"K-"],"\x97"=>[0,"K."],"\x98"=>[0,"K\x2F"],"\x99"=>[0,"K3"],"\x9A"=>[0,"K4"],"\x9B"=>[0,"K5"],"\x9C"=>[0,"K6"],"\x9D"=>[0,"K7"],"\x9E"=>[0,"K8"],"\x9F"=>[0,"K9"],"\xA0"=>[0,"K\x3D"],"\xA1"=>[0,"KA"],"\xA2"=>[0,"K_"],"\xA3"=>[0,"Kb"],"\xA4"=>[0,"Kd"],"\xA5"=>[0,"Kf"],"\xA6"=>[0,"Kg"],"\xA7"=>[0,"Kh"],"\xA8"=>[0,"Kl"],"\xA9"=>[0,"Km"],"\xAA"=>[0,"Kn"],"\xAB"=>[0,"Kp"],"\xAC"=>[0,"Kr"],"\xAD"=>[0,"Ku"],"\xAE"=>[100,"K"],"\xAF"=>[110,"K"],"\xB0"=>[111,"K"],"\xB1"=>[115,"K"],"\xB2"=>[116,"K"],"\xB3"=>[118,"K"],"\xB4"=>[119,"K"],"\xB5"=>[122,"K"],"\xB6"=>[123,"K"],"\xB7"=>[125,"K"],"\xB8"=>[126,"K"],"\xB9"=>[129,"K"],"\xBA"=>[143,"K"],"\xBB"=>[148,"K"],"\xBC"=>[151,"K"],"\xBD"=>[153,"K"],"\xBE"=>[83,"K"],"\xBF"=>[10,"K"],"\xC0"=>[77,"L0"],"\xC1"=>[18,"L0"],"\xC2"=>[77,"L1"],"\xC3"=>[18,"L1"],"\xC4"=>[77,"L2"],"\xC5"=>[18,"L2"],"\xC6"=>[77,"La"],"\xC7"=>[18,"La"],"\xC8"=>[77,"Lc"],"\xC9"=>[18,"Lc"],"\xCA"=>[77,"Le"],"\xCB"=>[18,"Le"],"\xCC"=>[77,"Li"],"\xCD"=>[18,"Li"],"\xCE"=>[77,"Lo"],"\xCF"=>[18,"Lo"],"\xD0"=>[77,"Ls"],"\xD1"=>[18,"Ls"],"\xD2"=>[77,"Lt"],"\xD3"=>[18,"Lt"],"\xD4"=>[0,"L\x20"],"\xD5"=>[0,"L\x25"],"\xD6"=>[0,"L-"],"\xD7"=>[0,"L."],"\xD8"=>[0,"L\x2F"],"\xD9"=>[0,"L3"],"\xDA"=>[0,"L4"],"\xDB"=>[0,"L5"],"\xDC"=>[0,"L6"],"\xDD"=>[0,"L7"],"\xDE"=>[0,"L8"],"\xDF"=>[0,"L9"],"\xE0"=>[0,"L\x3D"],"\xE1"=>[0,"LA"],"\xE2"=>[0,"L_"],"\xE3"=>[0,"Lb"],"\xE4"=>[0,"Ld"],"\xE5"=>[0,"Lf"],"\xE6"=>[0,"Lg"],"\xE7"=>[0,"Lh"],"\xE8"=>[0,"Ll"],"\xE9"=>[0,"Lm"],"\xEA"=>[0,"Ln"],"\xEB"=>[0,"Lp"],"\xEC"=>[0,"Lr"],"\xED"=>[0,"Lu"],"\xEE"=>[100,"L"],"\xEF"=>[110,"L"],"\xF0"=>[111,"L"],"\xF1"=>[115,"L"],"\xF2"=>[116,"L"],"\xF3"=>[118,"L"],"\xF4"=>[119,"L"],"\xF5"=>[122,"L"],"\xF6"=>[123,"L"],"\xF7"=>[125,"L"],"\xF8"=>[126,"L"],"\xF9"=>[129,"L"],"\xFA"=>[143,"L"],"\xFB"=>[148,"L"],"\xFC"=>[151,"L"],"\xFD"=>[153,"L"],"\xFE"=>[83,"L"],"\xFF"=>[10,"L"],],["\x00"=>[94,"K0"],"\x01"=>[76,"K0"],"\x02"=>[104,"K0"],"\x03"=>[16,"K0"],"\x04"=>[94,"K1"],"\x05"=>[76,"K1"],"\x06"=>[104,"K1"],"\x07"=>[16,"K1"],"\x08"=>[94,"K2"],"\x09"=>[76,"K2"],"\x0A"=>[104,"K2"],"\x0B"=>[16,"K2"],"\x0C"=>[94,"Ka"],"\x0D"=>[76,"Ka"],"\x0E"=>[104,"Ka"],"\x0F"=>[16,"Ka"],"\x10"=>[94,"Kc"],"\x11"=>[76,"Kc"],"\x12"=>[104,"Kc"],"\x13"=>[16,"Kc"],"\x14"=>[94,"Ke"],"\x15"=>[76,"Ke"],"\x16"=>[104,"Ke"],"\x17"=>[16,"Ke"],"\x18"=>[94,"Ki"],"\x19"=>[76,"Ki"],"\x1A"=>[104,"Ki"],"\x1B"=>[16,"Ki"],"\x1C"=>[94,"Ko"],"\x1D"=>[76,"Ko"],"\x1E"=>[104,"Ko"],"\x1F"=>[16,"Ko"],"\x20"=>[94,"Ks"],"\x21"=>[76,"Ks"],"\x22"=>[104,"Ks"],"\x23"=>[16,"Ks"],"\x24"=>[94,"Kt"],"\x25"=>[76,"Kt"],"\x26"=>[104,"Kt"],"\x27"=>[16,"Kt"],"\x28"=>[77,"K\x20"],"\x29"=>[18,"K\x20"],"\x2A"=>[77,"K\x25"],"\x2B"=>[18,"K\x25"],"\x2C"=>[77,"K-"],"-"=>[18,"K-"],"."=>[77,"K."],"\x2F"=>[18,"K."],[77,"K\x2F"],[18,"K\x2F"],[77,"K3"],[18,"K3"],[77,"K4"],[18,"K4"],[77,"K5"],[18,"K5"],[77,"K6"],[18,"K6"],"\x3A"=>[77,"K7"],"\x3B"=>[18,"K7"],"\x3C"=>[77,"K8"],"\x3D"=>[18,"K8"],"\x3E"=>[77,"K9"],"\x3F"=>[18,"K9"],"\x40"=>[77,"K\x3D"],"A"=>[18,"K\x3D"],"B"=>[77,"KA"],"C"=>[18,"KA"],"D"=>[77,"K_"],"E"=>[18,"K_"],"F"=>[77,"Kb"],"G"=>[18,"Kb"],"H"=>[77,"Kd"],"I"=>[18,"Kd"],"J"=>[77,"Kf"],"K"=>[18,"Kf"],"L"=>[77,"Kg"],"M"=>[18,"Kg"],"N"=>[77,"Kh"],"O"=>[18,"Kh"],"P"=>[77,"Kl"],"Q"=>[18,"Kl"],"R"=>[77,"Km"],"S"=>[18,"Km"],"T"=>[77,"Kn"],"U"=>[18,"Kn"],"V"=>[77,"Kp"],"W"=>[18,"Kp"],"X"=>[77,"Kr"],"Y"=>[18,"Kr"],"Z"=>[77,"Ku"],"\x5B"=>[18,"Ku"],"\x5C"=>[0,"K\x3A"],"\x5D"=>[0,"KB"],"\x5E"=>[0,"KC"],"_"=>[0,"KD"],"\x60"=>[0,"KE"],"a"=>[0,"KF"],"b"=>[0,"KG"],"c"=>[0,"KH"],"d"=>[0,"KI"],"e"=>[0,"KJ"],"f"=>[0,"KK"],"g"=>[0,"KL"],"h"=>[0,"KM"],"i"=>[0,"KN"],"j"=>[0,"KO"],"k"=>[0,"KP"],"l"=>[0,"KQ"],"m"=>[0,"KR"],"n"=>[0,"KS"],"o"=>[0,"KT"],"p"=>[0,"KU"],"q"=>[0,"KV"],"r"=>[0,"KW"],"s"=>[0,"KY"],"t"=>[0,"Kj"],"u"=>[0,"Kk"],"v"=>[0,"Kq"],"w"=>[0,"Kv"],"x"=>[0,"Kw"],"y"=>[0,"Kx"],"z"=>[0,"Ky"],"\x7B"=>[0,"Kz"],"\x7C"=>[82,"K"],"\x7D"=>[87,"K"],"~"=>[130,"K"],"\x7F"=>[9,"K"],"\x80"=>[94,"L0"],"\x81"=>[76,"L0"],"\x82"=>[104,"L0"],"\x83"=>[16,"L0"],"\x84"=>[94,"L1"],"\x85"=>[76,"L1"],"\x86"=>[104,"L1"],"\x87"=>[16,"L1"],"\x88"=>[94,"L2"],"\x89"=>[76,"L2"],"\x8A"=>[104,"L2"],"\x8B"=>[16,"L2"],"\x8C"=>[94,"La"],"\x8D"=>[76,"La"],"\x8E"=>[104,"La"],"\x8F"=>[16,"La"],"\x90"=>[94,"Lc"],"\x91"=>[76,"Lc"],"\x92"=>[104,"Lc"],"\x93"=>[16,"Lc"],"\x94"=>[94,"Le"],"\x95"=>[76,"Le"],"\x96"=>[104,"Le"],"\x97"=>[16,"Le"],"\x98"=>[94,"Li"],"\x99"=>[76,"Li"],"\x9A"=>[104,"Li"],"\x9B"=>[16,"Li"],"\x9C"=>[94,"Lo"],"\x9D"=>[76,"Lo"],"\x9E"=>[104,"Lo"],"\x9F"=>[16,"Lo"],"\xA0"=>[94,"Ls"],"\xA1"=>[76,"Ls"],"\xA2"=>[104,"Ls"],"\xA3"=>[16,"Ls"],"\xA4"=>[94,"Lt"],"\xA5"=>[76,"Lt"],"\xA6"=>[104,"Lt"],"\xA7"=>[16,"Lt"],"\xA8"=>[77,"L\x20"],"\xA9"=>[18,"L\x20"],"\xAA"=>[77,"L\x25"],"\xAB"=>[18,"L\x25"],"\xAC"=>[77,"L-"],"\xAD"=>[18,"L-"],"\xAE"=>[77,"L."],"\xAF"=>[18,"L."],"\xB0"=>[77,"L\x2F"],"\xB1"=>[18,"L\x2F"],"\xB2"=>[77,"L3"],"\xB3"=>[18,"L3"],"\xB4"=>[77,"L4"],"\xB5"=>[18,"L4"],"\xB6"=>[77,"L5"],"\xB7"=>[18,"L5"],"\xB8"=>[77,"L6"],"\xB9"=>[18,"L6"],"\xBA"=>[77,"L7"],"\xBB"=>[18,"L7"],"\xBC"=>[77,"L8"],"\xBD"=>[18,"L8"],"\xBE"=>[77,"L9"],"\xBF"=>[18,"L9"],"\xC0"=>[77,"L\x3D"],"\xC1"=>[18,"L\x3D"],"\xC2"=>[77,"LA"],"\xC3"=>[18,"LA"],"\xC4"=>[77,"L_"],"\xC5"=>[18,"L_"],"\xC6"=>[77,"Lb"],"\xC7"=>[18,"Lb"],"\xC8"=>[77,"Ld"],"\xC9"=>[18,"Ld"],"\xCA"=>[77,"Lf"],"\xCB"=>[18,"Lf"],"\xCC"=>[77,"Lg"],"\xCD"=>[18,"Lg"],"\xCE"=>[77,"Lh"],"\xCF"=>[18,"Lh"],"\xD0"=>[77,"Ll"],"\xD1"=>[18,"Ll"],"\xD2"=>[77,"Lm"],"\xD3"=>[18,"Lm"],"\xD4"=>[77,"Ln"],"\xD5"=>[18,"Ln"],"\xD6"=>[77,"Lp"],"\xD7"=>[18,"Lp"],"\xD8"=>[77,"Lr"],"\xD9"=>[18,"Lr"],"\xDA"=>[77,"Lu"],"\xDB"=>[18,"Lu"],"\xDC"=>[0,"L\x3A"],"\xDD"=>[0,"LB"],"\xDE"=>[0,"LC"],"\xDF"=>[0,"LD"],"\xE0"=>[0,"LE"],"\xE1"=>[0,"LF"],"\xE2"=>[0,"LG"],"\xE3"=>[0,"LH"],"\xE4"=>[0,"LI"],"\xE5"=>[0,"LJ"],"\xE6"=>[0,"LK"],"\xE7"=>[0,"LL"],"\xE8"=>[0,"LM"],"\xE9"=>[0,"LN"],"\xEA"=>[0,"LO"],"\xEB"=>[0,"LP"],"\xEC"=>[0,"LQ"],"\xED"=>[0,"LR"],"\xEE"=>[0,"LS"],"\xEF"=>[0,"LT"],"\xF0"=>[0,"LU"],"\xF1"=>[0,"LV"],"\xF2"=>[0,"LW"],"\xF3"=>[0,"LY"],"\xF4"=>[0,"Lj"],"\xF5"=>[0,"Lk"],"\xF6"=>[0,"Lq"],"\xF7"=>[0,"Lv"],"\xF8"=>[0,"Lw"],"\xF9"=>[0,"Lx"],"\xFA"=>[0,"Ly"],"\xFB"=>[0,"Lz"],"\xFC"=>[82,"L"],"\xFD"=>[87,"L"],"\xFE"=>[130,"L"],"\xFF"=>[9,"L"],],["\x00"=>[94,"M0"],"\x01"=>[76,"M0"],"\x02"=>[104,"M0"],"\x03"=>[16,"M0"],"\x04"=>[94,"M1"],"\x05"=>[76,"M1"],"\x06"=>[104,"M1"],"\x07"=>[16,"M1"],"\x08"=>[94,"M2"],"\x09"=>[76,"M2"],"\x0A"=>[104,"M2"],"\x0B"=>[16,"M2"],"\x0C"=>[94,"Ma"],"\x0D"=>[76,"Ma"],"\x0E"=>[104,"Ma"],"\x0F"=>[16,"Ma"],"\x10"=>[94,"Mc"],"\x11"=>[76,"Mc"],"\x12"=>[104,"Mc"],"\x13"=>[16,"Mc"],"\x14"=>[94,"Me"],"\x15"=>[76,"Me"],"\x16"=>[104,"Me"],"\x17"=>[16,"Me"],"\x18"=>[94,"Mi"],"\x19"=>[76,"Mi"],"\x1A"=>[104,"Mi"],"\x1B"=>[16,"Mi"],"\x1C"=>[94,"Mo"],"\x1D"=>[76,"Mo"],"\x1E"=>[104,"Mo"],"\x1F"=>[16,"Mo"],"\x20"=>[94,"Ms"],"\x21"=>[76,"Ms"],"\x22"=>[104,"Ms"],"\x23"=>[16,"Ms"],"\x24"=>[94,"Mt"],"\x25"=>[76,"Mt"],"\x26"=>[104,"Mt"],"\x27"=>[16,"Mt"],"\x28"=>[77,"M\x20"],"\x29"=>[18,"M\x20"],"\x2A"=>[77,"M\x25"],"\x2B"=>[18,"M\x25"],"\x2C"=>[77,"M-"],"-"=>[18,"M-"],"."=>[77,"M."],"\x2F"=>[18,"M."],[77,"M\x2F"],[18,"M\x2F"],[77,"M3"],[18,"M3"],[77,"M4"],[18,"M4"],[77,"M5"],[18,"M5"],[77,"M6"],[18,"M6"],"\x3A"=>[77,"M7"],"\x3B"=>[18,"M7"],"\x3C"=>[77,"M8"],"\x3D"=>[18,"M8"],"\x3E"=>[77,"M9"],"\x3F"=>[18,"M9"],"\x40"=>[77,"M\x3D"],"A"=>[18,"M\x3D"],"B"=>[77,"MA"],"C"=>[18,"MA"],"D"=>[77,"M_"],"E"=>[18,"M_"],"F"=>[77,"Mb"],"G"=>[18,"Mb"],"H"=>[77,"Md"],"I"=>[18,"Md"],"J"=>[77,"Mf"],"K"=>[18,"Mf"],"L"=>[77,"Mg"],"M"=>[18,"Mg"],"N"=>[77,"Mh"],"O"=>[18,"Mh"],"P"=>[77,"Ml"],"Q"=>[18,"Ml"],"R"=>[77,"Mm"],"S"=>[18,"Mm"],"T"=>[77,"Mn"],"U"=>[18,"Mn"],"V"=>[77,"Mp"],"W"=>[18,"Mp"],"X"=>[77,"Mr"],"Y"=>[18,"Mr"],"Z"=>[77,"Mu"],"\x5B"=>[18,"Mu"],"\x5C"=>[0,"M\x3A"],"\x5D"=>[0,"MB"],"\x5E"=>[0,"MC"],"_"=>[0,"MD"],"\x60"=>[0,"ME"],"a"=>[0,"MF"],"b"=>[0,"MG"],"c"=>[0,"MH"],"d"=>[0,"MI"],"e"=>[0,"MJ"],"f"=>[0,"MK"],"g"=>[0,"ML"],"h"=>[0,"MM"],"i"=>[0,"MN"],"j"=>[0,"MO"],"k"=>[0,"MP"],"l"=>[0,"MQ"],"m"=>[0,"MR"],"n"=>[0,"MS"],"o"=>[0,"MT"],"p"=>[0,"MU"],"q"=>[0,"MV"],"r"=>[0,"MW"],"s"=>[0,"MY"],"t"=>[0,"Mj"],"u"=>[0,"Mk"],"v"=>[0,"Mq"],"w"=>[0,"Mv"],"x"=>[0,"Mw"],"y"=>[0,"Mx"],"z"=>[0,"My"],"\x7B"=>[0,"Mz"],"\x7C"=>[82,"M"],"\x7D"=>[87,"M"],"~"=>[130,"M"],"\x7F"=>[9,"M"],"\x80"=>[94,"N0"],"\x81"=>[76,"N0"],"\x82"=>[104,"N0"],"\x83"=>[16,"N0"],"\x84"=>[94,"N1"],"\x85"=>[76,"N1"],"\x86"=>[104,"N1"],"\x87"=>[16,"N1"],"\x88"=>[94,"N2"],"\x89"=>[76,"N2"],"\x8A"=>[104,"N2"],"\x8B"=>[16,"N2"],"\x8C"=>[94,"Na"],"\x8D"=>[76,"Na"],"\x8E"=>[104,"Na"],"\x8F"=>[16,"Na"],"\x90"=>[94,"Nc"],"\x91"=>[76,"Nc"],"\x92"=>[104,"Nc"],"\x93"=>[16,"Nc"],"\x94"=>[94,"Ne"],"\x95"=>[76,"Ne"],"\x96"=>[104,"Ne"],"\x97"=>[16,"Ne"],"\x98"=>[94,"Ni"],"\x99"=>[76,"Ni"],"\x9A"=>[104,"Ni"],"\x9B"=>[16,"Ni"],"\x9C"=>[94,"No"],"\x9D"=>[76,"No"],"\x9E"=>[104,"No"],"\x9F"=>[16,"No"],"\xA0"=>[94,"Ns"],"\xA1"=>[76,"Ns"],"\xA2"=>[104,"Ns"],"\xA3"=>[16,"Ns"],"\xA4"=>[94,"Nt"],"\xA5"=>[76,"Nt"],"\xA6"=>[104,"Nt"],"\xA7"=>[16,"Nt"],"\xA8"=>[77,"N\x20"],"\xA9"=>[18,"N\x20"],"\xAA"=>[77,"N\x25"],"\xAB"=>[18,"N\x25"],"\xAC"=>[77,"N-"],"\xAD"=>[18,"N-"],"\xAE"=>[77,"N."],"\xAF"=>[18,"N."],"\xB0"=>[77,"N\x2F"],"\xB1"=>[18,"N\x2F"],"\xB2"=>[77,"N3"],"\xB3"=>[18,"N3"],"\xB4"=>[77,"N4"],"\xB5"=>[18,"N4"],"\xB6"=>[77,"N5"],"\xB7"=>[18,"N5"],"\xB8"=>[77,"N6"],"\xB9"=>[18,"N6"],"\xBA"=>[77,"N7"],"\xBB"=>[18,"N7"],"\xBC"=>[77,"N8"],"\xBD"=>[18,"N8"],"\xBE"=>[77,"N9"],"\xBF"=>[18,"N9"],"\xC0"=>[77,"N\x3D"],"\xC1"=>[18,"N\x3D"],"\xC2"=>[77,"NA"],"\xC3"=>[18,"NA"],"\xC4"=>[77,"N_"],"\xC5"=>[18,"N_"],"\xC6"=>[77,"Nb"],"\xC7"=>[18,"Nb"],"\xC8"=>[77,"Nd"],"\xC9"=>[18,"Nd"],"\xCA"=>[77,"Nf"],"\xCB"=>[18,"Nf"],"\xCC"=>[77,"Ng"],"\xCD"=>[18,"Ng"],"\xCE"=>[77,"Nh"],"\xCF"=>[18,"Nh"],"\xD0"=>[77,"Nl"],"\xD1"=>[18,"Nl"],"\xD2"=>[77,"Nm"],"\xD3"=>[18,"Nm"],"\xD4"=>[77,"Nn"],"\xD5"=>[18,"Nn"],"\xD6"=>[77,"Np"],"\xD7"=>[18,"Np"],"\xD8"=>[77,"Nr"],"\xD9"=>[18,"Nr"],"\xDA"=>[77,"Nu"],"\xDB"=>[18,"Nu"],"\xDC"=>[0,"N\x3A"],"\xDD"=>[0,"NB"],"\xDE"=>[0,"NC"],"\xDF"=>[0,"ND"],"\xE0"=>[0,"NE"],"\xE1"=>[0,"NF"],"\xE2"=>[0,"NG"],"\xE3"=>[0,"NH"],"\xE4"=>[0,"NI"],"\xE5"=>[0,"NJ"],"\xE6"=>[0,"NK"],"\xE7"=>[0,"NL"],"\xE8"=>[0,"NM"],"\xE9"=>[0,"NN"],"\xEA"=>[0,"NO"],"\xEB"=>[0,"NP"],"\xEC"=>[0,"NQ"],"\xED"=>[0,"NR"],"\xEE"=>[0,"NS"],"\xEF"=>[0,"NT"],"\xF0"=>[0,"NU"],"\xF1"=>[0,"NV"],"\xF2"=>[0,"NW"],"\xF3"=>[0,"NY"],"\xF4"=>[0,"Nj"],"\xF5"=>[0,"Nk"],"\xF6"=>[0,"Nq"],"\xF7"=>[0,"Nv"],"\xF8"=>[0,"Nw"],"\xF9"=>[0,"Nx"],"\xFA"=>[0,"Ny"],"\xFB"=>[0,"Nz"],"\xFC"=>[82,"N"],"\xFD"=>[87,"N"],"\xFE"=>[130,"N"],"\xFF"=>[9,"N"],],["\x00"=>[77,"M0"],"\x01"=>[18,"M0"],"\x02"=>[77,"M1"],"\x03"=>[18,"M1"],"\x04"=>[77,"M2"],"\x05"=>[18,"M2"],"\x06"=>[77,"Ma"],"\x07"=>[18,"Ma"],"\x08"=>[77,"Mc"],"\x09"=>[18,"Mc"],"\x0A"=>[77,"Me"],"\x0B"=>[18,"Me"],"\x0C"=>[77,"Mi"],"\x0D"=>[18,"Mi"],"\x0E"=>[77,"Mo"],"\x0F"=>[18,"Mo"],"\x10"=>[77,"Ms"],"\x11"=>[18,"Ms"],"\x12"=>[77,"Mt"],"\x13"=>[18,"Mt"],"\x14"=>[0,"M\x20"],"\x15"=>[0,"M\x25"],"\x16"=>[0,"M-"],"\x17"=>[0,"M."],"\x18"=>[0,"M\x2F"],"\x19"=>[0,"M3"],"\x1A"=>[0,"M4"],"\x1B"=>[0,"M5"],"\x1C"=>[0,"M6"],"\x1D"=>[0,"M7"],"\x1E"=>[0,"M8"],"\x1F"=>[0,"M9"],"\x20"=>[0,"M\x3D"],"\x21"=>[0,"MA"],"\x22"=>[0,"M_"],"\x23"=>[0,"Mb"],"\x24"=>[0,"Md"],"\x25"=>[0,"Mf"],"\x26"=>[0,"Mg"],"\x27"=>[0,"Mh"],"\x28"=>[0,"Ml"],"\x29"=>[0,"Mm"],"\x2A"=>[0,"Mn"],"\x2B"=>[0,"Mp"],"\x2C"=>[0,"Mr"],"-"=>[0,"Mu"],"."=>[100,"M"],"\x2F"=>[110,"M"],[111,"M"],[115,"M"],[116,"M"],[118,"M"],[119,"M"],[122,"M"],[123,"M"],[125,"M"],[126,"M"],[129,"M"],"\x3A"=>[143,"M"],"\x3B"=>[148,"M"],"\x3C"=>[151,"M"],"\x3D"=>[153,"M"],"\x3E"=>[83,"M"],"\x3F"=>[10,"M"],"\x40"=>[77,"N0"],"A"=>[18,"N0"],"B"=>[77,"N1"],"C"=>[18,"N1"],"D"=>[77,"N2"],"E"=>[18,"N2"],"F"=>[77,"Na"],"G"=>[18,"Na"],"H"=>[77,"Nc"],"I"=>[18,"Nc"],"J"=>[77,"Ne"],"K"=>[18,"Ne"],"L"=>[77,"Ni"],"M"=>[18,"Ni"],"N"=>[77,"No"],"O"=>[18,"No"],"P"=>[77,"Ns"],"Q"=>[18,"Ns"],"R"=>[77,"Nt"],"S"=>[18,"Nt"],"T"=>[0,"N\x20"],"U"=>[0,"N\x25"],"V"=>[0,"N-"],"W"=>[0,"N."],"X"=>[0,"N\x2F"],"Y"=>[0,"N3"],"Z"=>[0,"N4"],"\x5B"=>[0,"N5"],"\x5C"=>[0,"N6"],"\x5D"=>[0,"N7"],"\x5E"=>[0,"N8"],"_"=>[0,"N9"],"\x60"=>[0,"N\x3D"],"a"=>[0,"NA"],"b"=>[0,"N_"],"c"=>[0,"Nb"],"d"=>[0,"Nd"],"e"=>[0,"Nf"],"f"=>[0,"Ng"],"g"=>[0,"Nh"],"h"=>[0,"Nl"],"i"=>[0,"Nm"],"j"=>[0,"Nn"],"k"=>[0,"Np"],"l"=>[0,"Nr"],"m"=>[0,"Nu"],"n"=>[100,"N"],"o"=>[110,"N"],"p"=>[111,"N"],"q"=>[115,"N"],"r"=>[116,"N"],"s"=>[118,"N"],"t"=>[119,"N"],"u"=>[122,"N"],"v"=>[123,"N"],"w"=>[125,"N"],"x"=>[126,"N"],"y"=>[129,"N"],"z"=>[143,"N"],"\x7B"=>[148,"N"],"\x7C"=>[151,"N"],"\x7D"=>[153,"N"],"~"=>[83,"N"],"\x7F"=>[10,"N"],"\x80"=>[77,"O0"],"\x81"=>[18,"O0"],"\x82"=>[77,"O1"],"\x83"=>[18,"O1"],"\x84"=>[77,"O2"],"\x85"=>[18,"O2"],"\x86"=>[77,"Oa"],"\x87"=>[18,"Oa"],"\x88"=>[77,"Oc"],"\x89"=>[18,"Oc"],"\x8A"=>[77,"Oe"],"\x8B"=>[18,"Oe"],"\x8C"=>[77,"Oi"],"\x8D"=>[18,"Oi"],"\x8E"=>[77,"Oo"],"\x8F"=>[18,"Oo"],"\x90"=>[77,"Os"],"\x91"=>[18,"Os"],"\x92"=>[77,"Ot"],"\x93"=>[18,"Ot"],"\x94"=>[0,"O\x20"],"\x95"=>[0,"O\x25"],"\x96"=>[0,"O-"],"\x97"=>[0,"O."],"\x98"=>[0,"O\x2F"],"\x99"=>[0,"O3"],"\x9A"=>[0,"O4"],"\x9B"=>[0,"O5"],"\x9C"=>[0,"O6"],"\x9D"=>[0,"O7"],"\x9E"=>[0,"O8"],"\x9F"=>[0,"O9"],"\xA0"=>[0,"O\x3D"],"\xA1"=>[0,"OA"],"\xA2"=>[0,"O_"],"\xA3"=>[0,"Ob"],"\xA4"=>[0,"Od"],"\xA5"=>[0,"Of"],"\xA6"=>[0,"Og"],"\xA7"=>[0,"Oh"],"\xA8"=>[0,"Ol"],"\xA9"=>[0,"Om"],"\xAA"=>[0,"On"],"\xAB"=>[0,"Op"],"\xAC"=>[0,"Or"],"\xAD"=>[0,"Ou"],"\xAE"=>[100,"O"],"\xAF"=>[110,"O"],"\xB0"=>[111,"O"],"\xB1"=>[115,"O"],"\xB2"=>[116,"O"],"\xB3"=>[118,"O"],"\xB4"=>[119,"O"],"\xB5"=>[122,"O"],"\xB6"=>[123,"O"],"\xB7"=>[125,"O"],"\xB8"=>[126,"O"],"\xB9"=>[129,"O"],"\xBA"=>[143,"O"],"\xBB"=>[148,"O"],"\xBC"=>[151,"O"],"\xBD"=>[153,"O"],"\xBE"=>[83,"O"],"\xBF"=>[10,"O"],"\xC0"=>[77,"P0"],"\xC1"=>[18,"P0"],"\xC2"=>[77,"P1"],"\xC3"=>[18,"P1"],"\xC4"=>[77,"P2"],"\xC5"=>[18,"P2"],"\xC6"=>[77,"Pa"],"\xC7"=>[18,"Pa"],"\xC8"=>[77,"Pc"],"\xC9"=>[18,"Pc"],"\xCA"=>[77,"Pe"],"\xCB"=>[18,"Pe"],"\xCC"=>[77,"Pi"],"\xCD"=>[18,"Pi"],"\xCE"=>[77,"Po"],"\xCF"=>[18,"Po"],"\xD0"=>[77,"Ps"],"\xD1"=>[18,"Ps"],"\xD2"=>[77,"Pt"],"\xD3"=>[18,"Pt"],"\xD4"=>[0,"P\x20"],"\xD5"=>[0,"P\x25"],"\xD6"=>[0,"P-"],"\xD7"=>[0,"P."],"\xD8"=>[0,"P\x2F"],"\xD9"=>[0,"P3"],"\xDA"=>[0,"P4"],"\xDB"=>[0,"P5"],"\xDC"=>[0,"P6"],"\xDD"=>[0,"P7"],"\xDE"=>[0,"P8"],"\xDF"=>[0,"P9"],"\xE0"=>[0,"P\x3D"],"\xE1"=>[0,"PA"],"\xE2"=>[0,"P_"],"\xE3"=>[0,"Pb"],"\xE4"=>[0,"Pd"],"\xE5"=>[0,"Pf"],"\xE6"=>[0,"Pg"],"\xE7"=>[0,"Ph"],"\xE8"=>[0,"Pl"],"\xE9"=>[0,"Pm"],"\xEA"=>[0,"Pn"],"\xEB"=>[0,"Pp"],"\xEC"=>[0,"Pr"],"\xED"=>[0,"Pu"],"\xEE"=>[100,"P"],"\xEF"=>[110,"P"],"\xF0"=>[111,"P"],"\xF1"=>[115,"P"],"\xF2"=>[116,"P"],"\xF3"=>[118,"P"],"\xF4"=>[119,"P"],"\xF5"=>[122,"P"],"\xF6"=>[123,"P"],"\xF7"=>[125,"P"],"\xF8"=>[126,"P"],"\xF9"=>[129,"P"],"\xFA"=>[143,"P"],"\xFB"=>[148,"P"],"\xFC"=>[151,"P"],"\xFD"=>[153,"P"],"\xFE"=>[83,"P"],"\xFF"=>[10,"P"],],["\x00"=>[0,"M0"],"\x01"=>[0,"M1"],"\x02"=>[0,"M2"],"\x03"=>[0,"Ma"],"\x04"=>[0,"Mc"],"\x05"=>[0,"Me"],"\x06"=>[0,"Mi"],"\x07"=>[0,"Mo"],"\x08"=>[0,"Ms"],"\x09"=>[0,"Mt"],"\x0A"=>[73,"M"],"\x0B"=>[88,"M"],"\x0C"=>[89,"M"],"\x0D"=>[96,"M"],"\x0E"=>[97,"M"],"\x0F"=>[99,"M"],"\x10"=>[106,"M"],"\x11"=>[136,"M"],"\x12"=>[139,"M"],"\x13"=>[141,"M"],"\x14"=>[145,"M"],"\x15"=>[147,"M"],"\x16"=>[149,"M"],"\x17"=>[101,"M"],"\x18"=>[112,"M"],"\x19"=>[117,"M"],"\x1A"=>[120,"M"],"\x1B"=>[124,"M"],"\x1C"=>[127,"M"],"\x1D"=>[144,"M"],"\x1E"=>[152,"M"],"\x1F"=>[11,"M"],"\x20"=>[0,"N0"],"\x21"=>[0,"N1"],"\x22"=>[0,"N2"],"\x23"=>[0,"Na"],"\x24"=>[0,"Nc"],"\x25"=>[0,"Ne"],"\x26"=>[0,"Ni"],"\x27"=>[0,"No"],"\x28"=>[0,"Ns"],"\x29"=>[0,"Nt"],"\x2A"=>[73,"N"],"\x2B"=>[88,"N"],"\x2C"=>[89,"N"],"-"=>[96,"N"],"."=>[97,"N"],"\x2F"=>[99,"N"],[106,"N"],[136,"N"],[139,"N"],[141,"N"],[145,"N"],[147,"N"],[149,"N"],[101,"N"],[112,"N"],[117,"N"],"\x3A"=>[120,"N"],"\x3B"=>[124,"N"],"\x3C"=>[127,"N"],"\x3D"=>[144,"N"],"\x3E"=>[152,"N"],"\x3F"=>[11,"N"],"\x40"=>[0,"O0"],"A"=>[0,"O1"],"B"=>[0,"O2"],"C"=>[0,"Oa"],"D"=>[0,"Oc"],"E"=>[0,"Oe"],"F"=>[0,"Oi"],"G"=>[0,"Oo"],"H"=>[0,"Os"],"I"=>[0,"Ot"],"J"=>[73,"O"],"K"=>[88,"O"],"L"=>[89,"O"],"M"=>[96,"O"],"N"=>[97,"O"],"O"=>[99,"O"],"P"=>[106,"O"],"Q"=>[136,"O"],"R"=>[139,"O"],"S"=>[141,"O"],"T"=>[145,"O"],"U"=>[147,"O"],"V"=>[149,"O"],"W"=>[101,"O"],"X"=>[112,"O"],"Y"=>[117,"O"],"Z"=>[120,"O"],"\x5B"=>[124,"O"],"\x5C"=>[127,"O"],"\x5D"=>[144,"O"],"\x5E"=>[152,"O"],"_"=>[11,"O"],"\x60"=>[0,"P0"],"a"=>[0,"P1"],"b"=>[0,"P2"],"c"=>[0,"Pa"],"d"=>[0,"Pc"],"e"=>[0,"Pe"],"f"=>[0,"Pi"],"g"=>[0,"Po"],"h"=>[0,"Ps"],"i"=>[0,"Pt"],"j"=>[73,"P"],"k"=>[88,"P"],"l"=>[89,"P"],"m"=>[96,"P"],"n"=>[97,"P"],"o"=>[99,"P"],"p"=>[106,"P"],"q"=>[136,"P"],"r"=>[139,"P"],"s"=>[141,"P"],"t"=>[145,"P"],"u"=>[147,"P"],"v"=>[149,"P"],"w"=>[101,"P"],"x"=>[112,"P"],"y"=>[117,"P"],"z"=>[120,"P"],"\x7B"=>[124,"P"],"\x7C"=>[127,"P"],"\x7D"=>[144,"P"],"~"=>[152,"P"],"\x7F"=>[11,"P"],"\x80"=>[0,"Q0"],"\x81"=>[0,"Q1"],"\x82"=>[0,"Q2"],"\x83"=>[0,"Qa"],"\x84"=>[0,"Qc"],"\x85"=>[0,"Qe"],"\x86"=>[0,"Qi"],"\x87"=>[0,"Qo"],"\x88"=>[0,"Qs"],"\x89"=>[0,"Qt"],"\x8A"=>[73,"Q"],"\x8B"=>[88,"Q"],"\x8C"=>[89,"Q"],"\x8D"=>[96,"Q"],"\x8E"=>[97,"Q"],"\x8F"=>[99,"Q"],"\x90"=>[106,"Q"],"\x91"=>[136,"Q"],"\x92"=>[139,"Q"],"\x93"=>[141,"Q"],"\x94"=>[145,"Q"],"\x95"=>[147,"Q"],"\x96"=>[149,"Q"],"\x97"=>[101,"Q"],"\x98"=>[112,"Q"],"\x99"=>[117,"Q"],"\x9A"=>[120,"Q"],"\x9B"=>[124,"Q"],"\x9C"=>[127,"Q"],"\x9D"=>[144,"Q"],"\x9E"=>[152,"Q"],"\x9F"=>[11,"Q"],"\xA0"=>[0,"R0"],"\xA1"=>[0,"R1"],"\xA2"=>[0,"R2"],"\xA3"=>[0,"Ra"],"\xA4"=>[0,"Rc"],"\xA5"=>[0,"Re"],"\xA6"=>[0,"Ri"],"\xA7"=>[0,"Ro"],"\xA8"=>[0,"Rs"],"\xA9"=>[0,"Rt"],"\xAA"=>[73,"R"],"\xAB"=>[88,"R"],"\xAC"=>[89,"R"],"\xAD"=>[96,"R"],"\xAE"=>[97,"R"],"\xAF"=>[99,"R"],"\xB0"=>[106,"R"],"\xB1"=>[136,"R"],"\xB2"=>[139,"R"],"\xB3"=>[141,"R"],"\xB4"=>[145,"R"],"\xB5"=>[147,"R"],"\xB6"=>[149,"R"],"\xB7"=>[101,"R"],"\xB8"=>[112,"R"],"\xB9"=>[117,"R"],"\xBA"=>[120,"R"],"\xBB"=>[124,"R"],"\xBC"=>[127,"R"],"\xBD"=>[144,"R"],"\xBE"=>[152,"R"],"\xBF"=>[11,"R"],"\xC0"=>[0,"S0"],"\xC1"=>[0,"S1"],"\xC2"=>[0,"S2"],"\xC3"=>[0,"Sa"],"\xC4"=>[0,"Sc"],"\xC5"=>[0,"Se"],"\xC6"=>[0,"Si"],"\xC7"=>[0,"So"],"\xC8"=>[0,"Ss"],"\xC9"=>[0,"St"],"\xCA"=>[73,"S"],"\xCB"=>[88,"S"],"\xCC"=>[89,"S"],"\xCD"=>[96,"S"],"\xCE"=>[97,"S"],"\xCF"=>[99,"S"],"\xD0"=>[106,"S"],"\xD1"=>[136,"S"],"\xD2"=>[139,"S"],"\xD3"=>[141,"S"],"\xD4"=>[145,"S"],"\xD5"=>[147,"S"],"\xD6"=>[149,"S"],"\xD7"=>[101,"S"],"\xD8"=>[112,"S"],"\xD9"=>[117,"S"],"\xDA"=>[120,"S"],"\xDB"=>[124,"S"],"\xDC"=>[127,"S"],"\xDD"=>[144,"S"],"\xDE"=>[152,"S"],"\xDF"=>[11,"S"],"\xE0"=>[0,"T0"],"\xE1"=>[0,"T1"],"\xE2"=>[0,"T2"],"\xE3"=>[0,"Ta"],"\xE4"=>[0,"Tc"],"\xE5"=>[0,"Te"],"\xE6"=>[0,"Ti"],"\xE7"=>[0,"To"],"\xE8"=>[0,"Ts"],"\xE9"=>[0,"Tt"],"\xEA"=>[73,"T"],"\xEB"=>[88,"T"],"\xEC"=>[89,"T"],"\xED"=>[96,"T"],"\xEE"=>[97,"T"],"\xEF"=>[99,"T"],"\xF0"=>[106,"T"],"\xF1"=>[136,"T"],"\xF2"=>[139,"T"],"\xF3"=>[141,"T"],"\xF4"=>[145,"T"],"\xF5"=>[147,"T"],"\xF6"=>[149,"T"],"\xF7"=>[101,"T"],"\xF8"=>[112,"T"],"\xF9"=>[117,"T"],"\xFA"=>[120,"T"],"\xFB"=>[124,"T"],"\xFC"=>[127,"T"],"\xFD"=>[144,"T"],"\xFE"=>[152,"T"],"\xFF"=>[11,"T"],],["\x00"=>[94,"O0"],"\x01"=>[76,"O0"],"\x02"=>[104,"O0"],"\x03"=>[16,"O0"],"\x04"=>[94,"O1"],"\x05"=>[76,"O1"],"\x06"=>[104,"O1"],"\x07"=>[16,"O1"],"\x08"=>[94,"O2"],"\x09"=>[76,"O2"],"\x0A"=>[104,"O2"],"\x0B"=>[16,"O2"],"\x0C"=>[94,"Oa"],"\x0D"=>[76,"Oa"],"\x0E"=>[104,"Oa"],"\x0F"=>[16,"Oa"],"\x10"=>[94,"Oc"],"\x11"=>[76,"Oc"],"\x12"=>[104,"Oc"],"\x13"=>[16,"Oc"],"\x14"=>[94,"Oe"],"\x15"=>[76,"Oe"],"\x16"=>[104,"Oe"],"\x17"=>[16,"Oe"],"\x18"=>[94,"Oi"],"\x19"=>[76,"Oi"],"\x1A"=>[104,"Oi"],"\x1B"=>[16,"Oi"],"\x1C"=>[94,"Oo"],"\x1D"=>[76,"Oo"],"\x1E"=>[104,"Oo"],"\x1F"=>[16,"Oo"],"\x20"=>[94,"Os"],"\x21"=>[76,"Os"],"\x22"=>[104,"Os"],"\x23"=>[16,"Os"],"\x24"=>[94,"Ot"],"\x25"=>[76,"Ot"],"\x26"=>[104,"Ot"],"\x27"=>[16,"Ot"],"\x28"=>[77,"O\x20"],"\x29"=>[18,"O\x20"],"\x2A"=>[77,"O\x25"],"\x2B"=>[18,"O\x25"],"\x2C"=>[77,"O-"],"-"=>[18,"O-"],"."=>[77,"O."],"\x2F"=>[18,"O."],[77,"O\x2F"],[18,"O\x2F"],[77,"O3"],[18,"O3"],[77,"O4"],[18,"O4"],[77,"O5"],[18,"O5"],[77,"O6"],[18,"O6"],"\x3A"=>[77,"O7"],"\x3B"=>[18,"O7"],"\x3C"=>[77,"O8"],"\x3D"=>[18,"O8"],"\x3E"=>[77,"O9"],"\x3F"=>[18,"O9"],"\x40"=>[77,"O\x3D"],"A"=>[18,"O\x3D"],"B"=>[77,"OA"],"C"=>[18,"OA"],"D"=>[77,"O_"],"E"=>[18,"O_"],"F"=>[77,"Ob"],"G"=>[18,"Ob"],"H"=>[77,"Od"],"I"=>[18,"Od"],"J"=>[77,"Of"],"K"=>[18,"Of"],"L"=>[77,"Og"],"M"=>[18,"Og"],"N"=>[77,"Oh"],"O"=>[18,"Oh"],"P"=>[77,"Ol"],"Q"=>[18,"Ol"],"R"=>[77,"Om"],"S"=>[18,"Om"],"T"=>[77,"On"],"U"=>[18,"On"],"V"=>[77,"Op"],"W"=>[18,"Op"],"X"=>[77,"Or"],"Y"=>[18,"Or"],"Z"=>[77,"Ou"],"\x5B"=>[18,"Ou"],"\x5C"=>[0,"O\x3A"],"\x5D"=>[0,"OB"],"\x5E"=>[0,"OC"],"_"=>[0,"OD"],"\x60"=>[0,"OE"],"a"=>[0,"OF"],"b"=>[0,"OG"],"c"=>[0,"OH"],"d"=>[0,"OI"],"e"=>[0,"OJ"],"f"=>[0,"OK"],"g"=>[0,"OL"],"h"=>[0,"OM"],"i"=>[0,"ON"],"j"=>[0,"OO"],"k"=>[0,"OP"],"l"=>[0,"OQ"],"m"=>[0,"OR"],"n"=>[0,"OS"],"o"=>[0,"OT"],"p"=>[0,"OU"],"q"=>[0,"OV"],"r"=>[0,"OW"],"s"=>[0,"OY"],"t"=>[0,"Oj"],"u"=>[0,"Ok"],"v"=>[0,"Oq"],"w"=>[0,"Ov"],"x"=>[0,"Ow"],"y"=>[0,"Ox"],"z"=>[0,"Oy"],"\x7B"=>[0,"Oz"],"\x7C"=>[82,"O"],"\x7D"=>[87,"O"],"~"=>[130,"O"],"\x7F"=>[9,"O"],"\x80"=>[94,"P0"],"\x81"=>[76,"P0"],"\x82"=>[104,"P0"],"\x83"=>[16,"P0"],"\x84"=>[94,"P1"],"\x85"=>[76,"P1"],"\x86"=>[104,"P1"],"\x87"=>[16,"P1"],"\x88"=>[94,"P2"],"\x89"=>[76,"P2"],"\x8A"=>[104,"P2"],"\x8B"=>[16,"P2"],"\x8C"=>[94,"Pa"],"\x8D"=>[76,"Pa"],"\x8E"=>[104,"Pa"],"\x8F"=>[16,"Pa"],"\x90"=>[94,"Pc"],"\x91"=>[76,"Pc"],"\x92"=>[104,"Pc"],"\x93"=>[16,"Pc"],"\x94"=>[94,"Pe"],"\x95"=>[76,"Pe"],"\x96"=>[104,"Pe"],"\x97"=>[16,"Pe"],"\x98"=>[94,"Pi"],"\x99"=>[76,"Pi"],"\x9A"=>[104,"Pi"],"\x9B"=>[16,"Pi"],"\x9C"=>[94,"Po"],"\x9D"=>[76,"Po"],"\x9E"=>[104,"Po"],"\x9F"=>[16,"Po"],"\xA0"=>[94,"Ps"],"\xA1"=>[76,"Ps"],"\xA2"=>[104,"Ps"],"\xA3"=>[16,"Ps"],"\xA4"=>[94,"Pt"],"\xA5"=>[76,"Pt"],"\xA6"=>[104,"Pt"],"\xA7"=>[16,"Pt"],"\xA8"=>[77,"P\x20"],"\xA9"=>[18,"P\x20"],"\xAA"=>[77,"P\x25"],"\xAB"=>[18,"P\x25"],"\xAC"=>[77,"P-"],"\xAD"=>[18,"P-"],"\xAE"=>[77,"P."],"\xAF"=>[18,"P."],"\xB0"=>[77,"P\x2F"],"\xB1"=>[18,"P\x2F"],"\xB2"=>[77,"P3"],"\xB3"=>[18,"P3"],"\xB4"=>[77,"P4"],"\xB5"=>[18,"P4"],"\xB6"=>[77,"P5"],"\xB7"=>[18,"P5"],"\xB8"=>[77,"P6"],"\xB9"=>[18,"P6"],"\xBA"=>[77,"P7"],"\xBB"=>[18,"P7"],"\xBC"=>[77,"P8"],"\xBD"=>[18,"P8"],"\xBE"=>[77,"P9"],"\xBF"=>[18,"P9"],"\xC0"=>[77,"P\x3D"],"\xC1"=>[18,"P\x3D"],"\xC2"=>[77,"PA"],"\xC3"=>[18,"PA"],"\xC4"=>[77,"P_"],"\xC5"=>[18,"P_"],"\xC6"=>[77,"Pb"],"\xC7"=>[18,"Pb"],"\xC8"=>[77,"Pd"],"\xC9"=>[18,"Pd"],"\xCA"=>[77,"Pf"],"\xCB"=>[18,"Pf"],"\xCC"=>[77,"Pg"],"\xCD"=>[18,"Pg"],"\xCE"=>[77,"Ph"],"\xCF"=>[18,"Ph"],"\xD0"=>[77,"Pl"],"\xD1"=>[18,"Pl"],"\xD2"=>[77,"Pm"],"\xD3"=>[18,"Pm"],"\xD4"=>[77,"Pn"],"\xD5"=>[18,"Pn"],"\xD6"=>[77,"Pp"],"\xD7"=>[18,"Pp"],"\xD8"=>[77,"Pr"],"\xD9"=>[18,"Pr"],"\xDA"=>[77,"Pu"],"\xDB"=>[18,"Pu"],"\xDC"=>[0,"P\x3A"],"\xDD"=>[0,"PB"],"\xDE"=>[0,"PC"],"\xDF"=>[0,"PD"],"\xE0"=>[0,"PE"],"\xE1"=>[0,"PF"],"\xE2"=>[0,"PG"],"\xE3"=>[0,"PH"],"\xE4"=>[0,"PI"],"\xE5"=>[0,"PJ"],"\xE6"=>[0,"PK"],"\xE7"=>[0,"PL"],"\xE8"=>[0,"PM"],"\xE9"=>[0,"PN"],"\xEA"=>[0,"PO"],"\xEB"=>[0,"PP"],"\xEC"=>[0,"PQ"],"\xED"=>[0,"PR"],"\xEE"=>[0,"PS"],"\xEF"=>[0,"PT"],"\xF0"=>[0,"PU"],"\xF1"=>[0,"PV"],"\xF2"=>[0,"PW"],"\xF3"=>[0,"PY"],"\xF4"=>[0,"Pj"],"\xF5"=>[0,"Pk"],"\xF6"=>[0,"Pq"],"\xF7"=>[0,"Pv"],"\xF8"=>[0,"Pw"],"\xF9"=>[0,"Px"],"\xFA"=>[0,"Py"],"\xFB"=>[0,"Pz"],"\xFC"=>[82,"P"],"\xFD"=>[87,"P"],"\xFE"=>[130,"P"],"\xFF"=>[9,"P"],],["\x00"=>[94,"Q0"],"\x01"=>[76,"Q0"],"\x02"=>[104,"Q0"],"\x03"=>[16,"Q0"],"\x04"=>[94,"Q1"],"\x05"=>[76,"Q1"],"\x06"=>[104,"Q1"],"\x07"=>[16,"Q1"],"\x08"=>[94,"Q2"],"\x09"=>[76,"Q2"],"\x0A"=>[104,"Q2"],"\x0B"=>[16,"Q2"],"\x0C"=>[94,"Qa"],"\x0D"=>[76,"Qa"],"\x0E"=>[104,"Qa"],"\x0F"=>[16,"Qa"],"\x10"=>[94,"Qc"],"\x11"=>[76,"Qc"],"\x12"=>[104,"Qc"],"\x13"=>[16,"Qc"],"\x14"=>[94,"Qe"],"\x15"=>[76,"Qe"],"\x16"=>[104,"Qe"],"\x17"=>[16,"Qe"],"\x18"=>[94,"Qi"],"\x19"=>[76,"Qi"],"\x1A"=>[104,"Qi"],"\x1B"=>[16,"Qi"],"\x1C"=>[94,"Qo"],"\x1D"=>[76,"Qo"],"\x1E"=>[104,"Qo"],"\x1F"=>[16,"Qo"],"\x20"=>[94,"Qs"],"\x21"=>[76,"Qs"],"\x22"=>[104,"Qs"],"\x23"=>[16,"Qs"],"\x24"=>[94,"Qt"],"\x25"=>[76,"Qt"],"\x26"=>[104,"Qt"],"\x27"=>[16,"Qt"],"\x28"=>[77,"Q\x20"],"\x29"=>[18,"Q\x20"],"\x2A"=>[77,"Q\x25"],"\x2B"=>[18,"Q\x25"],"\x2C"=>[77,"Q-"],"-"=>[18,"Q-"],"."=>[77,"Q."],"\x2F"=>[18,"Q."],[77,"Q\x2F"],[18,"Q\x2F"],[77,"Q3"],[18,"Q3"],[77,"Q4"],[18,"Q4"],[77,"Q5"],[18,"Q5"],[77,"Q6"],[18,"Q6"],"\x3A"=>[77,"Q7"],"\x3B"=>[18,"Q7"],"\x3C"=>[77,"Q8"],"\x3D"=>[18,"Q8"],"\x3E"=>[77,"Q9"],"\x3F"=>[18,"Q9"],"\x40"=>[77,"Q\x3D"],"A"=>[18,"Q\x3D"],"B"=>[77,"QA"],"C"=>[18,"QA"],"D"=>[77,"Q_"],"E"=>[18,"Q_"],"F"=>[77,"Qb"],"G"=>[18,"Qb"],"H"=>[77,"Qd"],"I"=>[18,"Qd"],"J"=>[77,"Qf"],"K"=>[18,"Qf"],"L"=>[77,"Qg"],"M"=>[18,"Qg"],"N"=>[77,"Qh"],"O"=>[18,"Qh"],"P"=>[77,"Ql"],"Q"=>[18,"Ql"],"R"=>[77,"Qm"],"S"=>[18,"Qm"],"T"=>[77,"Qn"],"U"=>[18,"Qn"],"V"=>[77,"Qp"],"W"=>[18,"Qp"],"X"=>[77,"Qr"],"Y"=>[18,"Qr"],"Z"=>[77,"Qu"],"\x5B"=>[18,"Qu"],"\x5C"=>[0,"Q\x3A"],"\x5D"=>[0,"QB"],"\x5E"=>[0,"QC"],"_"=>[0,"QD"],"\x60"=>[0,"QE"],"a"=>[0,"QF"],"b"=>[0,"QG"],"c"=>[0,"QH"],"d"=>[0,"QI"],"e"=>[0,"QJ"],"f"=>[0,"QK"],"g"=>[0,"QL"],"h"=>[0,"QM"],"i"=>[0,"QN"],"j"=>[0,"QO"],"k"=>[0,"QP"],"l"=>[0,"QQ"],"m"=>[0,"QR"],"n"=>[0,"QS"],"o"=>[0,"QT"],"p"=>[0,"QU"],"q"=>[0,"QV"],"r"=>[0,"QW"],"s"=>[0,"QY"],"t"=>[0,"Qj"],"u"=>[0,"Qk"],"v"=>[0,"Qq"],"w"=>[0,"Qv"],"x"=>[0,"Qw"],"y"=>[0,"Qx"],"z"=>[0,"Qy"],"\x7B"=>[0,"Qz"],"\x7C"=>[82,"Q"],"\x7D"=>[87,"Q"],"~"=>[130,"Q"],"\x7F"=>[9,"Q"],"\x80"=>[94,"R0"],"\x81"=>[76,"R0"],"\x82"=>[104,"R0"],"\x83"=>[16,"R0"],"\x84"=>[94,"R1"],"\x85"=>[76,"R1"],"\x86"=>[104,"R1"],"\x87"=>[16,"R1"],"\x88"=>[94,"R2"],"\x89"=>[76,"R2"],"\x8A"=>[104,"R2"],"\x8B"=>[16,"R2"],"\x8C"=>[94,"Ra"],"\x8D"=>[76,"Ra"],"\x8E"=>[104,"Ra"],"\x8F"=>[16,"Ra"],"\x90"=>[94,"Rc"],"\x91"=>[76,"Rc"],"\x92"=>[104,"Rc"],"\x93"=>[16,"Rc"],"\x94"=>[94,"Re"],"\x95"=>[76,"Re"],"\x96"=>[104,"Re"],"\x97"=>[16,"Re"],"\x98"=>[94,"Ri"],"\x99"=>[76,"Ri"],"\x9A"=>[104,"Ri"],"\x9B"=>[16,"Ri"],"\x9C"=>[94,"Ro"],"\x9D"=>[76,"Ro"],"\x9E"=>[104,"Ro"],"\x9F"=>[16,"Ro"],"\xA0"=>[94,"Rs"],"\xA1"=>[76,"Rs"],"\xA2"=>[104,"Rs"],"\xA3"=>[16,"Rs"],"\xA4"=>[94,"Rt"],"\xA5"=>[76,"Rt"],"\xA6"=>[104,"Rt"],"\xA7"=>[16,"Rt"],"\xA8"=>[77,"R\x20"],"\xA9"=>[18,"R\x20"],"\xAA"=>[77,"R\x25"],"\xAB"=>[18,"R\x25"],"\xAC"=>[77,"R-"],"\xAD"=>[18,"R-"],"\xAE"=>[77,"R."],"\xAF"=>[18,"R."],"\xB0"=>[77,"R\x2F"],"\xB1"=>[18,"R\x2F"],"\xB2"=>[77,"R3"],"\xB3"=>[18,"R3"],"\xB4"=>[77,"R4"],"\xB5"=>[18,"R4"],"\xB6"=>[77,"R5"],"\xB7"=>[18,"R5"],"\xB8"=>[77,"R6"],"\xB9"=>[18,"R6"],"\xBA"=>[77,"R7"],"\xBB"=>[18,"R7"],"\xBC"=>[77,"R8"],"\xBD"=>[18,"R8"],"\xBE"=>[77,"R9"],"\xBF"=>[18,"R9"],"\xC0"=>[77,"R\x3D"],"\xC1"=>[18,"R\x3D"],"\xC2"=>[77,"RA"],"\xC3"=>[18,"RA"],"\xC4"=>[77,"R_"],"\xC5"=>[18,"R_"],"\xC6"=>[77,"Rb"],"\xC7"=>[18,"Rb"],"\xC8"=>[77,"Rd"],"\xC9"=>[18,"Rd"],"\xCA"=>[77,"Rf"],"\xCB"=>[18,"Rf"],"\xCC"=>[77,"Rg"],"\xCD"=>[18,"Rg"],"\xCE"=>[77,"Rh"],"\xCF"=>[18,"Rh"],"\xD0"=>[77,"Rl"],"\xD1"=>[18,"Rl"],"\xD2"=>[77,"Rm"],"\xD3"=>[18,"Rm"],"\xD4"=>[77,"Rn"],"\xD5"=>[18,"Rn"],"\xD6"=>[77,"Rp"],"\xD7"=>[18,"Rp"],"\xD8"=>[77,"Rr"],"\xD9"=>[18,"Rr"],"\xDA"=>[77,"Ru"],"\xDB"=>[18,"Ru"],"\xDC"=>[0,"R\x3A"],"\xDD"=>[0,"RB"],"\xDE"=>[0,"RC"],"\xDF"=>[0,"RD"],"\xE0"=>[0,"RE"],"\xE1"=>[0,"RF"],"\xE2"=>[0,"RG"],"\xE3"=>[0,"RH"],"\xE4"=>[0,"RI"],"\xE5"=>[0,"RJ"],"\xE6"=>[0,"RK"],"\xE7"=>[0,"RL"],"\xE8"=>[0,"RM"],"\xE9"=>[0,"RN"],"\xEA"=>[0,"RO"],"\xEB"=>[0,"RP"],"\xEC"=>[0,"RQ"],"\xED"=>[0,"RR"],"\xEE"=>[0,"RS"],"\xEF"=>[0,"RT"],"\xF0"=>[0,"RU"],"\xF1"=>[0,"RV"],"\xF2"=>[0,"RW"],"\xF3"=>[0,"RY"],"\xF4"=>[0,"Rj"],"\xF5"=>[0,"Rk"],"\xF6"=>[0,"Rq"],"\xF7"=>[0,"Rv"],"\xF8"=>[0,"Rw"],"\xF9"=>[0,"Rx"],"\xFA"=>[0,"Ry"],"\xFB"=>[0,"Rz"],"\xFC"=>[82,"R"],"\xFD"=>[87,"R"],"\xFE"=>[130,"R"],"\xFF"=>[9,"R"],],["\x00"=>[77,"Q0"],"\x01"=>[18,"Q0"],"\x02"=>[77,"Q1"],"\x03"=>[18,"Q1"],"\x04"=>[77,"Q2"],"\x05"=>[18,"Q2"],"\x06"=>[77,"Qa"],"\x07"=>[18,"Qa"],"\x08"=>[77,"Qc"],"\x09"=>[18,"Qc"],"\x0A"=>[77,"Qe"],"\x0B"=>[18,"Qe"],"\x0C"=>[77,"Qi"],"\x0D"=>[18,"Qi"],"\x0E"=>[77,"Qo"],"\x0F"=>[18,"Qo"],"\x10"=>[77,"Qs"],"\x11"=>[18,"Qs"],"\x12"=>[77,"Qt"],"\x13"=>[18,"Qt"],"\x14"=>[0,"Q\x20"],"\x15"=>[0,"Q\x25"],"\x16"=>[0,"Q-"],"\x17"=>[0,"Q."],"\x18"=>[0,"Q\x2F"],"\x19"=>[0,"Q3"],"\x1A"=>[0,"Q4"],"\x1B"=>[0,"Q5"],"\x1C"=>[0,"Q6"],"\x1D"=>[0,"Q7"],"\x1E"=>[0,"Q8"],"\x1F"=>[0,"Q9"],"\x20"=>[0,"Q\x3D"],"\x21"=>[0,"QA"],"\x22"=>[0,"Q_"],"\x23"=>[0,"Qb"],"\x24"=>[0,"Qd"],"\x25"=>[0,"Qf"],"\x26"=>[0,"Qg"],"\x27"=>[0,"Qh"],"\x28"=>[0,"Ql"],"\x29"=>[0,"Qm"],"\x2A"=>[0,"Qn"],"\x2B"=>[0,"Qp"],"\x2C"=>[0,"Qr"],"-"=>[0,"Qu"],"."=>[100,"Q"],"\x2F"=>[110,"Q"],[111,"Q"],[115,"Q"],[116,"Q"],[118,"Q"],[119,"Q"],[122,"Q"],[123,"Q"],[125,"Q"],[126,"Q"],[129,"Q"],"\x3A"=>[143,"Q"],"\x3B"=>[148,"Q"],"\x3C"=>[151,"Q"],"\x3D"=>[153,"Q"],"\x3E"=>[83,"Q"],"\x3F"=>[10,"Q"],"\x40"=>[77,"R0"],"A"=>[18,"R0"],"B"=>[77,"R1"],"C"=>[18,"R1"],"D"=>[77,"R2"],"E"=>[18,"R2"],"F"=>[77,"Ra"],"G"=>[18,"Ra"],"H"=>[77,"Rc"],"I"=>[18,"Rc"],"J"=>[77,"Re"],"K"=>[18,"Re"],"L"=>[77,"Ri"],"M"=>[18,"Ri"],"N"=>[77,"Ro"],"O"=>[18,"Ro"],"P"=>[77,"Rs"],"Q"=>[18,"Rs"],"R"=>[77,"Rt"],"S"=>[18,"Rt"],"T"=>[0,"R\x20"],"U"=>[0,"R\x25"],"V"=>[0,"R-"],"W"=>[0,"R."],"X"=>[0,"R\x2F"],"Y"=>[0,"R3"],"Z"=>[0,"R4"],"\x5B"=>[0,"R5"],"\x5C"=>[0,"R6"],"\x5D"=>[0,"R7"],"\x5E"=>[0,"R8"],"_"=>[0,"R9"],"\x60"=>[0,"R\x3D"],"a"=>[0,"RA"],"b"=>[0,"R_"],"c"=>[0,"Rb"],"d"=>[0,"Rd"],"e"=>[0,"Rf"],"f"=>[0,"Rg"],"g"=>[0,"Rh"],"h"=>[0,"Rl"],"i"=>[0,"Rm"],"j"=>[0,"Rn"],"k"=>[0,"Rp"],"l"=>[0,"Rr"],"m"=>[0,"Ru"],"n"=>[100,"R"],"o"=>[110,"R"],"p"=>[111,"R"],"q"=>[115,"R"],"r"=>[116,"R"],"s"=>[118,"R"],"t"=>[119,"R"],"u"=>[122,"R"],"v"=>[123,"R"],"w"=>[125,"R"],"x"=>[126,"R"],"y"=>[129,"R"],"z"=>[143,"R"],"\x7B"=>[148,"R"],"\x7C"=>[151,"R"],"\x7D"=>[153,"R"],"~"=>[83,"R"],"\x7F"=>[10,"R"],"\x80"=>[77,"S0"],"\x81"=>[18,"S0"],"\x82"=>[77,"S1"],"\x83"=>[18,"S1"],"\x84"=>[77,"S2"],"\x85"=>[18,"S2"],"\x86"=>[77,"Sa"],"\x87"=>[18,"Sa"],"\x88"=>[77,"Sc"],"\x89"=>[18,"Sc"],"\x8A"=>[77,"Se"],"\x8B"=>[18,"Se"],"\x8C"=>[77,"Si"],"\x8D"=>[18,"Si"],"\x8E"=>[77,"So"],"\x8F"=>[18,"So"],"\x90"=>[77,"Ss"],"\x91"=>[18,"Ss"],"\x92"=>[77,"St"],"\x93"=>[18,"St"],"\x94"=>[0,"S\x20"],"\x95"=>[0,"S\x25"],"\x96"=>[0,"S-"],"\x97"=>[0,"S."],"\x98"=>[0,"S\x2F"],"\x99"=>[0,"S3"],"\x9A"=>[0,"S4"],"\x9B"=>[0,"S5"],"\x9C"=>[0,"S6"],"\x9D"=>[0,"S7"],"\x9E"=>[0,"S8"],"\x9F"=>[0,"S9"],"\xA0"=>[0,"S\x3D"],"\xA1"=>[0,"SA"],"\xA2"=>[0,"S_"],"\xA3"=>[0,"Sb"],"\xA4"=>[0,"Sd"],"\xA5"=>[0,"Sf"],"\xA6"=>[0,"Sg"],"\xA7"=>[0,"Sh"],"\xA8"=>[0,"Sl"],"\xA9"=>[0,"Sm"],"\xAA"=>[0,"Sn"],"\xAB"=>[0,"Sp"],"\xAC"=>[0,"Sr"],"\xAD"=>[0,"Su"],"\xAE"=>[100,"S"],"\xAF"=>[110,"S"],"\xB0"=>[111,"S"],"\xB1"=>[115,"S"],"\xB2"=>[116,"S"],"\xB3"=>[118,"S"],"\xB4"=>[119,"S"],"\xB5"=>[122,"S"],"\xB6"=>[123,"S"],"\xB7"=>[125,"S"],"\xB8"=>[126,"S"],"\xB9"=>[129,"S"],"\xBA"=>[143,"S"],"\xBB"=>[148,"S"],"\xBC"=>[151,"S"],"\xBD"=>[153,"S"],"\xBE"=>[83,"S"],"\xBF"=>[10,"S"],"\xC0"=>[77,"T0"],"\xC1"=>[18,"T0"],"\xC2"=>[77,"T1"],"\xC3"=>[18,"T1"],"\xC4"=>[77,"T2"],"\xC5"=>[18,"T2"],"\xC6"=>[77,"Ta"],"\xC7"=>[18,"Ta"],"\xC8"=>[77,"Tc"],"\xC9"=>[18,"Tc"],"\xCA"=>[77,"Te"],"\xCB"=>[18,"Te"],"\xCC"=>[77,"Ti"],"\xCD"=>[18,"Ti"],"\xCE"=>[77,"To"],"\xCF"=>[18,"To"],"\xD0"=>[77,"Ts"],"\xD1"=>[18,"Ts"],"\xD2"=>[77,"Tt"],"\xD3"=>[18,"Tt"],"\xD4"=>[0,"T\x20"],"\xD5"=>[0,"T\x25"],"\xD6"=>[0,"T-"],"\xD7"=>[0,"T."],"\xD8"=>[0,"T\x2F"],"\xD9"=>[0,"T3"],"\xDA"=>[0,"T4"],"\xDB"=>[0,"T5"],"\xDC"=>[0,"T6"],"\xDD"=>[0,"T7"],"\xDE"=>[0,"T8"],"\xDF"=>[0,"T9"],"\xE0"=>[0,"T\x3D"],"\xE1"=>[0,"TA"],"\xE2"=>[0,"T_"],"\xE3"=>[0,"Tb"],"\xE4"=>[0,"Td"],"\xE5"=>[0,"Tf"],"\xE6"=>[0,"Tg"],"\xE7"=>[0,"Th"],"\xE8"=>[0,"Tl"],"\xE9"=>[0,"Tm"],"\xEA"=>[0,"Tn"],"\xEB"=>[0,"Tp"],"\xEC"=>[0,"Tr"],"\xED"=>[0,"Tu"],"\xEE"=>[100,"T"],"\xEF"=>[110,"T"],"\xF0"=>[111,"T"],"\xF1"=>[115,"T"],"\xF2"=>[116,"T"],"\xF3"=>[118,"T"],"\xF4"=>[119,"T"],"\xF5"=>[122,"T"],"\xF6"=>[123,"T"],"\xF7"=>[125,"T"],"\xF8"=>[126,"T"],"\xF9"=>[129,"T"],"\xFA"=>[143,"T"],"\xFB"=>[148,"T"],"\xFC"=>[151,"T"],"\xFD"=>[153,"T"],"\xFE"=>[83,"T"],"\xFF"=>[10,"T"],],["\x00"=>[94,"S0"],"\x01"=>[76,"S0"],"\x02"=>[104,"S0"],"\x03"=>[16,"S0"],"\x04"=>[94,"S1"],"\x05"=>[76,"S1"],"\x06"=>[104,"S1"],"\x07"=>[16,"S1"],"\x08"=>[94,"S2"],"\x09"=>[76,"S2"],"\x0A"=>[104,"S2"],"\x0B"=>[16,"S2"],"\x0C"=>[94,"Sa"],"\x0D"=>[76,"Sa"],"\x0E"=>[104,"Sa"],"\x0F"=>[16,"Sa"],"\x10"=>[94,"Sc"],"\x11"=>[76,"Sc"],"\x12"=>[104,"Sc"],"\x13"=>[16,"Sc"],"\x14"=>[94,"Se"],"\x15"=>[76,"Se"],"\x16"=>[104,"Se"],"\x17"=>[16,"Se"],"\x18"=>[94,"Si"],"\x19"=>[76,"Si"],"\x1A"=>[104,"Si"],"\x1B"=>[16,"Si"],"\x1C"=>[94,"So"],"\x1D"=>[76,"So"],"\x1E"=>[104,"So"],"\x1F"=>[16,"So"],"\x20"=>[94,"Ss"],"\x21"=>[76,"Ss"],"\x22"=>[104,"Ss"],"\x23"=>[16,"Ss"],"\x24"=>[94,"St"],"\x25"=>[76,"St"],"\x26"=>[104,"St"],"\x27"=>[16,"St"],"\x28"=>[77,"S\x20"],"\x29"=>[18,"S\x20"],"\x2A"=>[77,"S\x25"],"\x2B"=>[18,"S\x25"],"\x2C"=>[77,"S-"],"-"=>[18,"S-"],"."=>[77,"S."],"\x2F"=>[18,"S."],[77,"S\x2F"],[18,"S\x2F"],[77,"S3"],[18,"S3"],[77,"S4"],[18,"S4"],[77,"S5"],[18,"S5"],[77,"S6"],[18,"S6"],"\x3A"=>[77,"S7"],"\x3B"=>[18,"S7"],"\x3C"=>[77,"S8"],"\x3D"=>[18,"S8"],"\x3E"=>[77,"S9"],"\x3F"=>[18,"S9"],"\x40"=>[77,"S\x3D"],"A"=>[18,"S\x3D"],"B"=>[77,"SA"],"C"=>[18,"SA"],"D"=>[77,"S_"],"E"=>[18,"S_"],"F"=>[77,"Sb"],"G"=>[18,"Sb"],"H"=>[77,"Sd"],"I"=>[18,"Sd"],"J"=>[77,"Sf"],"K"=>[18,"Sf"],"L"=>[77,"Sg"],"M"=>[18,"Sg"],"N"=>[77,"Sh"],"O"=>[18,"Sh"],"P"=>[77,"Sl"],"Q"=>[18,"Sl"],"R"=>[77,"Sm"],"S"=>[18,"Sm"],"T"=>[77,"Sn"],"U"=>[18,"Sn"],"V"=>[77,"Sp"],"W"=>[18,"Sp"],"X"=>[77,"Sr"],"Y"=>[18,"Sr"],"Z"=>[77,"Su"],"\x5B"=>[18,"Su"],"\x5C"=>[0,"S\x3A"],"\x5D"=>[0,"SB"],"\x5E"=>[0,"SC"],"_"=>[0,"SD"],"\x60"=>[0,"SE"],"a"=>[0,"SF"],"b"=>[0,"SG"],"c"=>[0,"SH"],"d"=>[0,"SI"],"e"=>[0,"SJ"],"f"=>[0,"SK"],"g"=>[0,"SL"],"h"=>[0,"SM"],"i"=>[0,"SN"],"j"=>[0,"SO"],"k"=>[0,"SP"],"l"=>[0,"SQ"],"m"=>[0,"SR"],"n"=>[0,"SS"],"o"=>[0,"ST"],"p"=>[0,"SU"],"q"=>[0,"SV"],"r"=>[0,"SW"],"s"=>[0,"SY"],"t"=>[0,"Sj"],"u"=>[0,"Sk"],"v"=>[0,"Sq"],"w"=>[0,"Sv"],"x"=>[0,"Sw"],"y"=>[0,"Sx"],"z"=>[0,"Sy"],"\x7B"=>[0,"Sz"],"\x7C"=>[82,"S"],"\x7D"=>[87,"S"],"~"=>[130,"S"],"\x7F"=>[9,"S"],"\x80"=>[94,"T0"],"\x81"=>[76,"T0"],"\x82"=>[104,"T0"],"\x83"=>[16,"T0"],"\x84"=>[94,"T1"],"\x85"=>[76,"T1"],"\x86"=>[104,"T1"],"\x87"=>[16,"T1"],"\x88"=>[94,"T2"],"\x89"=>[76,"T2"],"\x8A"=>[104,"T2"],"\x8B"=>[16,"T2"],"\x8C"=>[94,"Ta"],"\x8D"=>[76,"Ta"],"\x8E"=>[104,"Ta"],"\x8F"=>[16,"Ta"],"\x90"=>[94,"Tc"],"\x91"=>[76,"Tc"],"\x92"=>[104,"Tc"],"\x93"=>[16,"Tc"],"\x94"=>[94,"Te"],"\x95"=>[76,"Te"],"\x96"=>[104,"Te"],"\x97"=>[16,"Te"],"\x98"=>[94,"Ti"],"\x99"=>[76,"Ti"],"\x9A"=>[104,"Ti"],"\x9B"=>[16,"Ti"],"\x9C"=>[94,"To"],"\x9D"=>[76,"To"],"\x9E"=>[104,"To"],"\x9F"=>[16,"To"],"\xA0"=>[94,"Ts"],"\xA1"=>[76,"Ts"],"\xA2"=>[104,"Ts"],"\xA3"=>[16,"Ts"],"\xA4"=>[94,"Tt"],"\xA5"=>[76,"Tt"],"\xA6"=>[104,"Tt"],"\xA7"=>[16,"Tt"],"\xA8"=>[77,"T\x20"],"\xA9"=>[18,"T\x20"],"\xAA"=>[77,"T\x25"],"\xAB"=>[18,"T\x25"],"\xAC"=>[77,"T-"],"\xAD"=>[18,"T-"],"\xAE"=>[77,"T."],"\xAF"=>[18,"T."],"\xB0"=>[77,"T\x2F"],"\xB1"=>[18,"T\x2F"],"\xB2"=>[77,"T3"],"\xB3"=>[18,"T3"],"\xB4"=>[77,"T4"],"\xB5"=>[18,"T4"],"\xB6"=>[77,"T5"],"\xB7"=>[18,"T5"],"\xB8"=>[77,"T6"],"\xB9"=>[18,"T6"],"\xBA"=>[77,"T7"],"\xBB"=>[18,"T7"],"\xBC"=>[77,"T8"],"\xBD"=>[18,"T8"],"\xBE"=>[77,"T9"],"\xBF"=>[18,"T9"],"\xC0"=>[77,"T\x3D"],"\xC1"=>[18,"T\x3D"],"\xC2"=>[77,"TA"],"\xC3"=>[18,"TA"],"\xC4"=>[77,"T_"],"\xC5"=>[18,"T_"],"\xC6"=>[77,"Tb"],"\xC7"=>[18,"Tb"],"\xC8"=>[77,"Td"],"\xC9"=>[18,"Td"],"\xCA"=>[77,"Tf"],"\xCB"=>[18,"Tf"],"\xCC"=>[77,"Tg"],"\xCD"=>[18,"Tg"],"\xCE"=>[77,"Th"],"\xCF"=>[18,"Th"],"\xD0"=>[77,"Tl"],"\xD1"=>[18,"Tl"],"\xD2"=>[77,"Tm"],"\xD3"=>[18,"Tm"],"\xD4"=>[77,"Tn"],"\xD5"=>[18,"Tn"],"\xD6"=>[77,"Tp"],"\xD7"=>[18,"Tp"],"\xD8"=>[77,"Tr"],"\xD9"=>[18,"Tr"],"\xDA"=>[77,"Tu"],"\xDB"=>[18,"Tu"],"\xDC"=>[0,"T\x3A"],"\xDD"=>[0,"TB"],"\xDE"=>[0,"TC"],"\xDF"=>[0,"TD"],"\xE0"=>[0,"TE"],"\xE1"=>[0,"TF"],"\xE2"=>[0,"TG"],"\xE3"=>[0,"TH"],"\xE4"=>[0,"TI"],"\xE5"=>[0,"TJ"],"\xE6"=>[0,"TK"],"\xE7"=>[0,"TL"],"\xE8"=>[0,"TM"],"\xE9"=>[0,"TN"],"\xEA"=>[0,"TO"],"\xEB"=>[0,"TP"],"\xEC"=>[0,"TQ"],"\xED"=>[0,"TR"],"\xEE"=>[0,"TS"],"\xEF"=>[0,"TT"],"\xF0"=>[0,"TU"],"\xF1"=>[0,"TV"],"\xF2"=>[0,"TW"],"\xF3"=>[0,"TY"],"\xF4"=>[0,"Tj"],"\xF5"=>[0,"Tk"],"\xF6"=>[0,"Tq"],"\xF7"=>[0,"Tv"],"\xF8"=>[0,"Tw"],"\xF9"=>[0,"Tx"],"\xFA"=>[0,"Ty"],"\xFB"=>[0,"Tz"],"\xFC"=>[82,"T"],"\xFD"=>[87,"T"],"\xFE"=>[130,"T"],"\xFF"=>[9,"T"],],["\x00"=>[94,"U0"],"\x01"=>[76,"U0"],"\x02"=>[104,"U0"],"\x03"=>[16,"U0"],"\x04"=>[94,"U1"],"\x05"=>[76,"U1"],"\x06"=>[104,"U1"],"\x07"=>[16,"U1"],"\x08"=>[94,"U2"],"\x09"=>[76,"U2"],"\x0A"=>[104,"U2"],"\x0B"=>[16,"U2"],"\x0C"=>[94,"Ua"],"\x0D"=>[76,"Ua"],"\x0E"=>[104,"Ua"],"\x0F"=>[16,"Ua"],"\x10"=>[94,"Uc"],"\x11"=>[76,"Uc"],"\x12"=>[104,"Uc"],"\x13"=>[16,"Uc"],"\x14"=>[94,"Ue"],"\x15"=>[76,"Ue"],"\x16"=>[104,"Ue"],"\x17"=>[16,"Ue"],"\x18"=>[94,"Ui"],"\x19"=>[76,"Ui"],"\x1A"=>[104,"Ui"],"\x1B"=>[16,"Ui"],"\x1C"=>[94,"Uo"],"\x1D"=>[76,"Uo"],"\x1E"=>[104,"Uo"],"\x1F"=>[16,"Uo"],"\x20"=>[94,"Us"],"\x21"=>[76,"Us"],"\x22"=>[104,"Us"],"\x23"=>[16,"Us"],"\x24"=>[94,"Ut"],"\x25"=>[76,"Ut"],"\x26"=>[104,"Ut"],"\x27"=>[16,"Ut"],"\x28"=>[77,"U\x20"],"\x29"=>[18,"U\x20"],"\x2A"=>[77,"U\x25"],"\x2B"=>[18,"U\x25"],"\x2C"=>[77,"U-"],"-"=>[18,"U-"],"."=>[77,"U."],"\x2F"=>[18,"U."],[77,"U\x2F"],[18,"U\x2F"],[77,"U3"],[18,"U3"],[77,"U4"],[18,"U4"],[77,"U5"],[18,"U5"],[77,"U6"],[18,"U6"],"\x3A"=>[77,"U7"],"\x3B"=>[18,"U7"],"\x3C"=>[77,"U8"],"\x3D"=>[18,"U8"],"\x3E"=>[77,"U9"],"\x3F"=>[18,"U9"],"\x40"=>[77,"U\x3D"],"A"=>[18,"U\x3D"],"B"=>[77,"UA"],"C"=>[18,"UA"],"D"=>[77,"U_"],"E"=>[18,"U_"],"F"=>[77,"Ub"],"G"=>[18,"Ub"],"H"=>[77,"Ud"],"I"=>[18,"Ud"],"J"=>[77,"Uf"],"K"=>[18,"Uf"],"L"=>[77,"Ug"],"M"=>[18,"Ug"],"N"=>[77,"Uh"],"O"=>[18,"Uh"],"P"=>[77,"Ul"],"Q"=>[18,"Ul"],"R"=>[77,"Um"],"S"=>[18,"Um"],"T"=>[77,"Un"],"U"=>[18,"Un"],"V"=>[77,"Up"],"W"=>[18,"Up"],"X"=>[77,"Ur"],"Y"=>[18,"Ur"],"Z"=>[77,"Uu"],"\x5B"=>[18,"Uu"],"\x5C"=>[0,"U\x3A"],"\x5D"=>[0,"UB"],"\x5E"=>[0,"UC"],"_"=>[0,"UD"],"\x60"=>[0,"UE"],"a"=>[0,"UF"],"b"=>[0,"UG"],"c"=>[0,"UH"],"d"=>[0,"UI"],"e"=>[0,"UJ"],"f"=>[0,"UK"],"g"=>[0,"UL"],"h"=>[0,"UM"],"i"=>[0,"UN"],"j"=>[0,"UO"],"k"=>[0,"UP"],"l"=>[0,"UQ"],"m"=>[0,"UR"],"n"=>[0,"US"],"o"=>[0,"UT"],"p"=>[0,"UU"],"q"=>[0,"UV"],"r"=>[0,"UW"],"s"=>[0,"UY"],"t"=>[0,"Uj"],"u"=>[0,"Uk"],"v"=>[0,"Uq"],"w"=>[0,"Uv"],"x"=>[0,"Uw"],"y"=>[0,"Ux"],"z"=>[0,"Uy"],"\x7B"=>[0,"Uz"],"\x7C"=>[82,"U"],"\x7D"=>[87,"U"],"~"=>[130,"U"],"\x7F"=>[9,"U"],"\x80"=>[94,"V0"],"\x81"=>[76,"V0"],"\x82"=>[104,"V0"],"\x83"=>[16,"V0"],"\x84"=>[94,"V1"],"\x85"=>[76,"V1"],"\x86"=>[104,"V1"],"\x87"=>[16,"V1"],"\x88"=>[94,"V2"],"\x89"=>[76,"V2"],"\x8A"=>[104,"V2"],"\x8B"=>[16,"V2"],"\x8C"=>[94,"Va"],"\x8D"=>[76,"Va"],"\x8E"=>[104,"Va"],"\x8F"=>[16,"Va"],"\x90"=>[94,"Vc"],"\x91"=>[76,"Vc"],"\x92"=>[104,"Vc"],"\x93"=>[16,"Vc"],"\x94"=>[94,"Ve"],"\x95"=>[76,"Ve"],"\x96"=>[104,"Ve"],"\x97"=>[16,"Ve"],"\x98"=>[94,"Vi"],"\x99"=>[76,"Vi"],"\x9A"=>[104,"Vi"],"\x9B"=>[16,"Vi"],"\x9C"=>[94,"Vo"],"\x9D"=>[76,"Vo"],"\x9E"=>[104,"Vo"],"\x9F"=>[16,"Vo"],"\xA0"=>[94,"Vs"],"\xA1"=>[76,"Vs"],"\xA2"=>[104,"Vs"],"\xA3"=>[16,"Vs"],"\xA4"=>[94,"Vt"],"\xA5"=>[76,"Vt"],"\xA6"=>[104,"Vt"],"\xA7"=>[16,"Vt"],"\xA8"=>[77,"V\x20"],"\xA9"=>[18,"V\x20"],"\xAA"=>[77,"V\x25"],"\xAB"=>[18,"V\x25"],"\xAC"=>[77,"V-"],"\xAD"=>[18,"V-"],"\xAE"=>[77,"V."],"\xAF"=>[18,"V."],"\xB0"=>[77,"V\x2F"],"\xB1"=>[18,"V\x2F"],"\xB2"=>[77,"V3"],"\xB3"=>[18,"V3"],"\xB4"=>[77,"V4"],"\xB5"=>[18,"V4"],"\xB6"=>[77,"V5"],"\xB7"=>[18,"V5"],"\xB8"=>[77,"V6"],"\xB9"=>[18,"V6"],"\xBA"=>[77,"V7"],"\xBB"=>[18,"V7"],"\xBC"=>[77,"V8"],"\xBD"=>[18,"V8"],"\xBE"=>[77,"V9"],"\xBF"=>[18,"V9"],"\xC0"=>[77,"V\x3D"],"\xC1"=>[18,"V\x3D"],"\xC2"=>[77,"VA"],"\xC3"=>[18,"VA"],"\xC4"=>[77,"V_"],"\xC5"=>[18,"V_"],"\xC6"=>[77,"Vb"],"\xC7"=>[18,"Vb"],"\xC8"=>[77,"Vd"],"\xC9"=>[18,"Vd"],"\xCA"=>[77,"Vf"],"\xCB"=>[18,"Vf"],"\xCC"=>[77,"Vg"],"\xCD"=>[18,"Vg"],"\xCE"=>[77,"Vh"],"\xCF"=>[18,"Vh"],"\xD0"=>[77,"Vl"],"\xD1"=>[18,"Vl"],"\xD2"=>[77,"Vm"],"\xD3"=>[18,"Vm"],"\xD4"=>[77,"Vn"],"\xD5"=>[18,"Vn"],"\xD6"=>[77,"Vp"],"\xD7"=>[18,"Vp"],"\xD8"=>[77,"Vr"],"\xD9"=>[18,"Vr"],"\xDA"=>[77,"Vu"],"\xDB"=>[18,"Vu"],"\xDC"=>[0,"V\x3A"],"\xDD"=>[0,"VB"],"\xDE"=>[0,"VC"],"\xDF"=>[0,"VD"],"\xE0"=>[0,"VE"],"\xE1"=>[0,"VF"],"\xE2"=>[0,"VG"],"\xE3"=>[0,"VH"],"\xE4"=>[0,"VI"],"\xE5"=>[0,"VJ"],"\xE6"=>[0,"VK"],"\xE7"=>[0,"VL"],"\xE8"=>[0,"VM"],"\xE9"=>[0,"VN"],"\xEA"=>[0,"VO"],"\xEB"=>[0,"VP"],"\xEC"=>[0,"VQ"],"\xED"=>[0,"VR"],"\xEE"=>[0,"VS"],"\xEF"=>[0,"VT"],"\xF0"=>[0,"VU"],"\xF1"=>[0,"VV"],"\xF2"=>[0,"VW"],"\xF3"=>[0,"VY"],"\xF4"=>[0,"Vj"],"\xF5"=>[0,"Vk"],"\xF6"=>[0,"Vq"],"\xF7"=>[0,"Vv"],"\xF8"=>[0,"Vw"],"\xF9"=>[0,"Vx"],"\xFA"=>[0,"Vy"],"\xFB"=>[0,"Vz"],"\xFC"=>[82,"V"],"\xFD"=>[87,"V"],"\xFE"=>[130,"V"],"\xFF"=>[9,"V"],],["\x00"=>[77,"U0"],"\x01"=>[18,"U0"],"\x02"=>[77,"U1"],"\x03"=>[18,"U1"],"\x04"=>[77,"U2"],"\x05"=>[18,"U2"],"\x06"=>[77,"Ua"],"\x07"=>[18,"Ua"],"\x08"=>[77,"Uc"],"\x09"=>[18,"Uc"],"\x0A"=>[77,"Ue"],"\x0B"=>[18,"Ue"],"\x0C"=>[77,"Ui"],"\x0D"=>[18,"Ui"],"\x0E"=>[77,"Uo"],"\x0F"=>[18,"Uo"],"\x10"=>[77,"Us"],"\x11"=>[18,"Us"],"\x12"=>[77,"Ut"],"\x13"=>[18,"Ut"],"\x14"=>[0,"U\x20"],"\x15"=>[0,"U\x25"],"\x16"=>[0,"U-"],"\x17"=>[0,"U."],"\x18"=>[0,"U\x2F"],"\x19"=>[0,"U3"],"\x1A"=>[0,"U4"],"\x1B"=>[0,"U5"],"\x1C"=>[0,"U6"],"\x1D"=>[0,"U7"],"\x1E"=>[0,"U8"],"\x1F"=>[0,"U9"],"\x20"=>[0,"U\x3D"],"\x21"=>[0,"UA"],"\x22"=>[0,"U_"],"\x23"=>[0,"Ub"],"\x24"=>[0,"Ud"],"\x25"=>[0,"Uf"],"\x26"=>[0,"Ug"],"\x27"=>[0,"Uh"],"\x28"=>[0,"Ul"],"\x29"=>[0,"Um"],"\x2A"=>[0,"Un"],"\x2B"=>[0,"Up"],"\x2C"=>[0,"Ur"],"-"=>[0,"Uu"],"."=>[100,"U"],"\x2F"=>[110,"U"],[111,"U"],[115,"U"],[116,"U"],[118,"U"],[119,"U"],[122,"U"],[123,"U"],[125,"U"],[126,"U"],[129,"U"],"\x3A"=>[143,"U"],"\x3B"=>[148,"U"],"\x3C"=>[151,"U"],"\x3D"=>[153,"U"],"\x3E"=>[83,"U"],"\x3F"=>[10,"U"],"\x40"=>[77,"V0"],"A"=>[18,"V0"],"B"=>[77,"V1"],"C"=>[18,"V1"],"D"=>[77,"V2"],"E"=>[18,"V2"],"F"=>[77,"Va"],"G"=>[18,"Va"],"H"=>[77,"Vc"],"I"=>[18,"Vc"],"J"=>[77,"Ve"],"K"=>[18,"Ve"],"L"=>[77,"Vi"],"M"=>[18,"Vi"],"N"=>[77,"Vo"],"O"=>[18,"Vo"],"P"=>[77,"Vs"],"Q"=>[18,"Vs"],"R"=>[77,"Vt"],"S"=>[18,"Vt"],"T"=>[0,"V\x20"],"U"=>[0,"V\x25"],"V"=>[0,"V-"],"W"=>[0,"V."],"X"=>[0,"V\x2F"],"Y"=>[0,"V3"],"Z"=>[0,"V4"],"\x5B"=>[0,"V5"],"\x5C"=>[0,"V6"],"\x5D"=>[0,"V7"],"\x5E"=>[0,"V8"],"_"=>[0,"V9"],"\x60"=>[0,"V\x3D"],"a"=>[0,"VA"],"b"=>[0,"V_"],"c"=>[0,"Vb"],"d"=>[0,"Vd"],"e"=>[0,"Vf"],"f"=>[0,"Vg"],"g"=>[0,"Vh"],"h"=>[0,"Vl"],"i"=>[0,"Vm"],"j"=>[0,"Vn"],"k"=>[0,"Vp"],"l"=>[0,"Vr"],"m"=>[0,"Vu"],"n"=>[100,"V"],"o"=>[110,"V"],"p"=>[111,"V"],"q"=>[115,"V"],"r"=>[116,"V"],"s"=>[118,"V"],"t"=>[119,"V"],"u"=>[122,"V"],"v"=>[123,"V"],"w"=>[125,"V"],"x"=>[126,"V"],"y"=>[129,"V"],"z"=>[143,"V"],"\x7B"=>[148,"V"],"\x7C"=>[151,"V"],"\x7D"=>[153,"V"],"~"=>[83,"V"],"\x7F"=>[10,"V"],"\x80"=>[77,"W0"],"\x81"=>[18,"W0"],"\x82"=>[77,"W1"],"\x83"=>[18,"W1"],"\x84"=>[77,"W2"],"\x85"=>[18,"W2"],"\x86"=>[77,"Wa"],"\x87"=>[18,"Wa"],"\x88"=>[77,"Wc"],"\x89"=>[18,"Wc"],"\x8A"=>[77,"We"],"\x8B"=>[18,"We"],"\x8C"=>[77,"Wi"],"\x8D"=>[18,"Wi"],"\x8E"=>[77,"Wo"],"\x8F"=>[18,"Wo"],"\x90"=>[77,"Ws"],"\x91"=>[18,"Ws"],"\x92"=>[77,"Wt"],"\x93"=>[18,"Wt"],"\x94"=>[0,"W\x20"],"\x95"=>[0,"W\x25"],"\x96"=>[0,"W-"],"\x97"=>[0,"W."],"\x98"=>[0,"W\x2F"],"\x99"=>[0,"W3"],"\x9A"=>[0,"W4"],"\x9B"=>[0,"W5"],"\x9C"=>[0,"W6"],"\x9D"=>[0,"W7"],"\x9E"=>[0,"W8"],"\x9F"=>[0,"W9"],"\xA0"=>[0,"W\x3D"],"\xA1"=>[0,"WA"],"\xA2"=>[0,"W_"],"\xA3"=>[0,"Wb"],"\xA4"=>[0,"Wd"],"\xA5"=>[0,"Wf"],"\xA6"=>[0,"Wg"],"\xA7"=>[0,"Wh"],"\xA8"=>[0,"Wl"],"\xA9"=>[0,"Wm"],"\xAA"=>[0,"Wn"],"\xAB"=>[0,"Wp"],"\xAC"=>[0,"Wr"],"\xAD"=>[0,"Wu"],"\xAE"=>[100,"W"],"\xAF"=>[110,"W"],"\xB0"=>[111,"W"],"\xB1"=>[115,"W"],"\xB2"=>[116,"W"],"\xB3"=>[118,"W"],"\xB4"=>[119,"W"],"\xB5"=>[122,"W"],"\xB6"=>[123,"W"],"\xB7"=>[125,"W"],"\xB8"=>[126,"W"],"\xB9"=>[129,"W"],"\xBA"=>[143,"W"],"\xBB"=>[148,"W"],"\xBC"=>[151,"W"],"\xBD"=>[153,"W"],"\xBE"=>[83,"W"],"\xBF"=>[10,"W"],"\xC0"=>[77,"Y0"],"\xC1"=>[18,"Y0"],"\xC2"=>[77,"Y1"],"\xC3"=>[18,"Y1"],"\xC4"=>[77,"Y2"],"\xC5"=>[18,"Y2"],"\xC6"=>[77,"Ya"],"\xC7"=>[18,"Ya"],"\xC8"=>[77,"Yc"],"\xC9"=>[18,"Yc"],"\xCA"=>[77,"Ye"],"\xCB"=>[18,"Ye"],"\xCC"=>[77,"Yi"],"\xCD"=>[18,"Yi"],"\xCE"=>[77,"Yo"],"\xCF"=>[18,"Yo"],"\xD0"=>[77,"Ys"],"\xD1"=>[18,"Ys"],"\xD2"=>[77,"Yt"],"\xD3"=>[18,"Yt"],"\xD4"=>[0,"Y\x20"],"\xD5"=>[0,"Y\x25"],"\xD6"=>[0,"Y-"],"\xD7"=>[0,"Y."],"\xD8"=>[0,"Y\x2F"],"\xD9"=>[0,"Y3"],"\xDA"=>[0,"Y4"],"\xDB"=>[0,"Y5"],"\xDC"=>[0,"Y6"],"\xDD"=>[0,"Y7"],"\xDE"=>[0,"Y8"],"\xDF"=>[0,"Y9"],"\xE0"=>[0,"Y\x3D"],"\xE1"=>[0,"YA"],"\xE2"=>[0,"Y_"],"\xE3"=>[0,"Yb"],"\xE4"=>[0,"Yd"],"\xE5"=>[0,"Yf"],"\xE6"=>[0,"Yg"],"\xE7"=>[0,"Yh"],"\xE8"=>[0,"Yl"],"\xE9"=>[0,"Ym"],"\xEA"=>[0,"Yn"],"\xEB"=>[0,"Yp"],"\xEC"=>[0,"Yr"],"\xED"=>[0,"Yu"],"\xEE"=>[100,"Y"],"\xEF"=>[110,"Y"],"\xF0"=>[111,"Y"],"\xF1"=>[115,"Y"],"\xF2"=>[116,"Y"],"\xF3"=>[118,"Y"],"\xF4"=>[119,"Y"],"\xF5"=>[122,"Y"],"\xF6"=>[123,"Y"],"\xF7"=>[125,"Y"],"\xF8"=>[126,"Y"],"\xF9"=>[129,"Y"],"\xFA"=>[143,"Y"],"\xFB"=>[148,"Y"],"\xFC"=>[151,"Y"],"\xFD"=>[153,"Y"],"\xFE"=>[83,"Y"],"\xFF"=>[10,"Y"],],["\x00"=>[0,"U0"],"\x01"=>[0,"U1"],"\x02"=>[0,"U2"],"\x03"=>[0,"Ua"],"\x04"=>[0,"Uc"],"\x05"=>[0,"Ue"],"\x06"=>[0,"Ui"],"\x07"=>[0,"Uo"],"\x08"=>[0,"Us"],"\x09"=>[0,"Ut"],"\x0A"=>[73,"U"],"\x0B"=>[88,"U"],"\x0C"=>[89,"U"],"\x0D"=>[96,"U"],"\x0E"=>[97,"U"],"\x0F"=>[99,"U"],"\x10"=>[106,"U"],"\x11"=>[136,"U"],"\x12"=>[139,"U"],"\x13"=>[141,"U"],"\x14"=>[145,"U"],"\x15"=>[147,"U"],"\x16"=>[149,"U"],"\x17"=>[101,"U"],"\x18"=>[112,"U"],"\x19"=>[117,"U"],"\x1A"=>[120,"U"],"\x1B"=>[124,"U"],"\x1C"=>[127,"U"],"\x1D"=>[144,"U"],"\x1E"=>[152,"U"],"\x1F"=>[11,"U"],"\x20"=>[0,"V0"],"\x21"=>[0,"V1"],"\x22"=>[0,"V2"],"\x23"=>[0,"Va"],"\x24"=>[0,"Vc"],"\x25"=>[0,"Ve"],"\x26"=>[0,"Vi"],"\x27"=>[0,"Vo"],"\x28"=>[0,"Vs"],"\x29"=>[0,"Vt"],"\x2A"=>[73,"V"],"\x2B"=>[88,"V"],"\x2C"=>[89,"V"],"-"=>[96,"V"],"."=>[97,"V"],"\x2F"=>[99,"V"],[106,"V"],[136,"V"],[139,"V"],[141,"V"],[145,"V"],[147,"V"],[149,"V"],[101,"V"],[112,"V"],[117,"V"],"\x3A"=>[120,"V"],"\x3B"=>[124,"V"],"\x3C"=>[127,"V"],"\x3D"=>[144,"V"],"\x3E"=>[152,"V"],"\x3F"=>[11,"V"],"\x40"=>[0,"W0"],"A"=>[0,"W1"],"B"=>[0,"W2"],"C"=>[0,"Wa"],"D"=>[0,"Wc"],"E"=>[0,"We"],"F"=>[0,"Wi"],"G"=>[0,"Wo"],"H"=>[0,"Ws"],"I"=>[0,"Wt"],"J"=>[73,"W"],"K"=>[88,"W"],"L"=>[89,"W"],"M"=>[96,"W"],"N"=>[97,"W"],"O"=>[99,"W"],"P"=>[106,"W"],"Q"=>[136,"W"],"R"=>[139,"W"],"S"=>[141,"W"],"T"=>[145,"W"],"U"=>[147,"W"],"V"=>[149,"W"],"W"=>[101,"W"],"X"=>[112,"W"],"Y"=>[117,"W"],"Z"=>[120,"W"],"\x5B"=>[124,"W"],"\x5C"=>[127,"W"],"\x5D"=>[144,"W"],"\x5E"=>[152,"W"],"_"=>[11,"W"],"\x60"=>[0,"Y0"],"a"=>[0,"Y1"],"b"=>[0,"Y2"],"c"=>[0,"Ya"],"d"=>[0,"Yc"],"e"=>[0,"Ye"],"f"=>[0,"Yi"],"g"=>[0,"Yo"],"h"=>[0,"Ys"],"i"=>[0,"Yt"],"j"=>[73,"Y"],"k"=>[88,"Y"],"l"=>[89,"Y"],"m"=>[96,"Y"],"n"=>[97,"Y"],"o"=>[99,"Y"],"p"=>[106,"Y"],"q"=>[136,"Y"],"r"=>[139,"Y"],"s"=>[141,"Y"],"t"=>[145,"Y"],"u"=>[147,"Y"],"v"=>[149,"Y"],"w"=>[101,"Y"],"x"=>[112,"Y"],"y"=>[117,"Y"],"z"=>[120,"Y"],"\x7B"=>[124,"Y"],"\x7C"=>[127,"Y"],"\x7D"=>[144,"Y"],"~"=>[152,"Y"],"\x7F"=>[11,"Y"],"\x80"=>[0,"j0"],"\x81"=>[0,"j1"],"\x82"=>[0,"j2"],"\x83"=>[0,"ja"],"\x84"=>[0,"jc"],"\x85"=>[0,"je"],"\x86"=>[0,"ji"],"\x87"=>[0,"jo"],"\x88"=>[0,"js"],"\x89"=>[0,"jt"],"\x8A"=>[73,"j"],"\x8B"=>[88,"j"],"\x8C"=>[89,"j"],"\x8D"=>[96,"j"],"\x8E"=>[97,"j"],"\x8F"=>[99,"j"],"\x90"=>[106,"j"],"\x91"=>[136,"j"],"\x92"=>[139,"j"],"\x93"=>[141,"j"],"\x94"=>[145,"j"],"\x95"=>[147,"j"],"\x96"=>[149,"j"],"\x97"=>[101,"j"],"\x98"=>[112,"j"],"\x99"=>[117,"j"],"\x9A"=>[120,"j"],"\x9B"=>[124,"j"],"\x9C"=>[127,"j"],"\x9D"=>[144,"j"],"\x9E"=>[152,"j"],"\x9F"=>[11,"j"],"\xA0"=>[0,"k0"],"\xA1"=>[0,"k1"],"\xA2"=>[0,"k2"],"\xA3"=>[0,"ka"],"\xA4"=>[0,"kc"],"\xA5"=>[0,"ke"],"\xA6"=>[0,"ki"],"\xA7"=>[0,"ko"],"\xA8"=>[0,"ks"],"\xA9"=>[0,"kt"],"\xAA"=>[73,"k"],"\xAB"=>[88,"k"],"\xAC"=>[89,"k"],"\xAD"=>[96,"k"],"\xAE"=>[97,"k"],"\xAF"=>[99,"k"],"\xB0"=>[106,"k"],"\xB1"=>[136,"k"],"\xB2"=>[139,"k"],"\xB3"=>[141,"k"],"\xB4"=>[145,"k"],"\xB5"=>[147,"k"],"\xB6"=>[149,"k"],"\xB7"=>[101,"k"],"\xB8"=>[112,"k"],"\xB9"=>[117,"k"],"\xBA"=>[120,"k"],"\xBB"=>[124,"k"],"\xBC"=>[127,"k"],"\xBD"=>[144,"k"],"\xBE"=>[152,"k"],"\xBF"=>[11,"k"],"\xC0"=>[0,"q0"],"\xC1"=>[0,"q1"],"\xC2"=>[0,"q2"],"\xC3"=>[0,"qa"],"\xC4"=>[0,"qc"],"\xC5"=>[0,"qe"],"\xC6"=>[0,"qi"],"\xC7"=>[0,"qo"],"\xC8"=>[0,"qs"],"\xC9"=>[0,"qt"],"\xCA"=>[73,"q"],"\xCB"=>[88,"q"],"\xCC"=>[89,"q"],"\xCD"=>[96,"q"],"\xCE"=>[97,"q"],"\xCF"=>[99,"q"],"\xD0"=>[106,"q"],"\xD1"=>[136,"q"],"\xD2"=>[139,"q"],"\xD3"=>[141,"q"],"\xD4"=>[145,"q"],"\xD5"=>[147,"q"],"\xD6"=>[149,"q"],"\xD7"=>[101,"q"],"\xD8"=>[112,"q"],"\xD9"=>[117,"q"],"\xDA"=>[120,"q"],"\xDB"=>[124,"q"],"\xDC"=>[127,"q"],"\xDD"=>[144,"q"],"\xDE"=>[152,"q"],"\xDF"=>[11,"q"],"\xE0"=>[0,"v0"],"\xE1"=>[0,"v1"],"\xE2"=>[0,"v2"],"\xE3"=>[0,"va"],"\xE4"=>[0,"vc"],"\xE5"=>[0,"ve"],"\xE6"=>[0,"vi"],"\xE7"=>[0,"vo"],"\xE8"=>[0,"vs"],"\xE9"=>[0,"vt"],"\xEA"=>[73,"v"],"\xEB"=>[88,"v"],"\xEC"=>[89,"v"],"\xED"=>[96,"v"],"\xEE"=>[97,"v"],"\xEF"=>[99,"v"],"\xF0"=>[106,"v"],"\xF1"=>[136,"v"],"\xF2"=>[139,"v"],"\xF3"=>[141,"v"],"\xF4"=>[145,"v"],"\xF5"=>[147,"v"],"\xF6"=>[149,"v"],"\xF7"=>[101,"v"],"\xF8"=>[112,"v"],"\xF9"=>[117,"v"],"\xFA"=>[120,"v"],"\xFB"=>[124,"v"],"\xFC"=>[127,"v"],"\xFD"=>[144,"v"],"\xFE"=>[152,"v"],"\xFF"=>[11,"v"],],["\x00"=>[94,"W0"],"\x01"=>[76,"W0"],"\x02"=>[104,"W0"],"\x03"=>[16,"W0"],"\x04"=>[94,"W1"],"\x05"=>[76,"W1"],"\x06"=>[104,"W1"],"\x07"=>[16,"W1"],"\x08"=>[94,"W2"],"\x09"=>[76,"W2"],"\x0A"=>[104,"W2"],"\x0B"=>[16,"W2"],"\x0C"=>[94,"Wa"],"\x0D"=>[76,"Wa"],"\x0E"=>[104,"Wa"],"\x0F"=>[16,"Wa"],"\x10"=>[94,"Wc"],"\x11"=>[76,"Wc"],"\x12"=>[104,"Wc"],"\x13"=>[16,"Wc"],"\x14"=>[94,"We"],"\x15"=>[76,"We"],"\x16"=>[104,"We"],"\x17"=>[16,"We"],"\x18"=>[94,"Wi"],"\x19"=>[76,"Wi"],"\x1A"=>[104,"Wi"],"\x1B"=>[16,"Wi"],"\x1C"=>[94,"Wo"],"\x1D"=>[76,"Wo"],"\x1E"=>[104,"Wo"],"\x1F"=>[16,"Wo"],"\x20"=>[94,"Ws"],"\x21"=>[76,"Ws"],"\x22"=>[104,"Ws"],"\x23"=>[16,"Ws"],"\x24"=>[94,"Wt"],"\x25"=>[76,"Wt"],"\x26"=>[104,"Wt"],"\x27"=>[16,"Wt"],"\x28"=>[77,"W\x20"],"\x29"=>[18,"W\x20"],"\x2A"=>[77,"W\x25"],"\x2B"=>[18,"W\x25"],"\x2C"=>[77,"W-"],"-"=>[18,"W-"],"."=>[77,"W."],"\x2F"=>[18,"W."],[77,"W\x2F"],[18,"W\x2F"],[77,"W3"],[18,"W3"],[77,"W4"],[18,"W4"],[77,"W5"],[18,"W5"],[77,"W6"],[18,"W6"],"\x3A"=>[77,"W7"],"\x3B"=>[18,"W7"],"\x3C"=>[77,"W8"],"\x3D"=>[18,"W8"],"\x3E"=>[77,"W9"],"\x3F"=>[18,"W9"],"\x40"=>[77,"W\x3D"],"A"=>[18,"W\x3D"],"B"=>[77,"WA"],"C"=>[18,"WA"],"D"=>[77,"W_"],"E"=>[18,"W_"],"F"=>[77,"Wb"],"G"=>[18,"Wb"],"H"=>[77,"Wd"],"I"=>[18,"Wd"],"J"=>[77,"Wf"],"K"=>[18,"Wf"],"L"=>[77,"Wg"],"M"=>[18,"Wg"],"N"=>[77,"Wh"],"O"=>[18,"Wh"],"P"=>[77,"Wl"],"Q"=>[18,"Wl"],"R"=>[77,"Wm"],"S"=>[18,"Wm"],"T"=>[77,"Wn"],"U"=>[18,"Wn"],"V"=>[77,"Wp"],"W"=>[18,"Wp"],"X"=>[77,"Wr"],"Y"=>[18,"Wr"],"Z"=>[77,"Wu"],"\x5B"=>[18,"Wu"],"\x5C"=>[0,"W\x3A"],"\x5D"=>[0,"WB"],"\x5E"=>[0,"WC"],"_"=>[0,"WD"],"\x60"=>[0,"WE"],"a"=>[0,"WF"],"b"=>[0,"WG"],"c"=>[0,"WH"],"d"=>[0,"WI"],"e"=>[0,"WJ"],"f"=>[0,"WK"],"g"=>[0,"WL"],"h"=>[0,"WM"],"i"=>[0,"WN"],"j"=>[0,"WO"],"k"=>[0,"WP"],"l"=>[0,"WQ"],"m"=>[0,"WR"],"n"=>[0,"WS"],"o"=>[0,"WT"],"p"=>[0,"WU"],"q"=>[0,"WV"],"r"=>[0,"WW"],"s"=>[0,"WY"],"t"=>[0,"Wj"],"u"=>[0,"Wk"],"v"=>[0,"Wq"],"w"=>[0,"Wv"],"x"=>[0,"Ww"],"y"=>[0,"Wx"],"z"=>[0,"Wy"],"\x7B"=>[0,"Wz"],"\x7C"=>[82,"W"],"\x7D"=>[87,"W"],"~"=>[130,"W"],"\x7F"=>[9,"W"],"\x80"=>[94,"Y0"],"\x81"=>[76,"Y0"],"\x82"=>[104,"Y0"],"\x83"=>[16,"Y0"],"\x84"=>[94,"Y1"],"\x85"=>[76,"Y1"],"\x86"=>[104,"Y1"],"\x87"=>[16,"Y1"],"\x88"=>[94,"Y2"],"\x89"=>[76,"Y2"],"\x8A"=>[104,"Y2"],"\x8B"=>[16,"Y2"],"\x8C"=>[94,"Ya"],"\x8D"=>[76,"Ya"],"\x8E"=>[104,"Ya"],"\x8F"=>[16,"Ya"],"\x90"=>[94,"Yc"],"\x91"=>[76,"Yc"],"\x92"=>[104,"Yc"],"\x93"=>[16,"Yc"],"\x94"=>[94,"Ye"],"\x95"=>[76,"Ye"],"\x96"=>[104,"Ye"],"\x97"=>[16,"Ye"],"\x98"=>[94,"Yi"],"\x99"=>[76,"Yi"],"\x9A"=>[104,"Yi"],"\x9B"=>[16,"Yi"],"\x9C"=>[94,"Yo"],"\x9D"=>[76,"Yo"],"\x9E"=>[104,"Yo"],"\x9F"=>[16,"Yo"],"\xA0"=>[94,"Ys"],"\xA1"=>[76,"Ys"],"\xA2"=>[104,"Ys"],"\xA3"=>[16,"Ys"],"\xA4"=>[94,"Yt"],"\xA5"=>[76,"Yt"],"\xA6"=>[104,"Yt"],"\xA7"=>[16,"Yt"],"\xA8"=>[77,"Y\x20"],"\xA9"=>[18,"Y\x20"],"\xAA"=>[77,"Y\x25"],"\xAB"=>[18,"Y\x25"],"\xAC"=>[77,"Y-"],"\xAD"=>[18,"Y-"],"\xAE"=>[77,"Y."],"\xAF"=>[18,"Y."],"\xB0"=>[77,"Y\x2F"],"\xB1"=>[18,"Y\x2F"],"\xB2"=>[77,"Y3"],"\xB3"=>[18,"Y3"],"\xB4"=>[77,"Y4"],"\xB5"=>[18,"Y4"],"\xB6"=>[77,"Y5"],"\xB7"=>[18,"Y5"],"\xB8"=>[77,"Y6"],"\xB9"=>[18,"Y6"],"\xBA"=>[77,"Y7"],"\xBB"=>[18,"Y7"],"\xBC"=>[77,"Y8"],"\xBD"=>[18,"Y8"],"\xBE"=>[77,"Y9"],"\xBF"=>[18,"Y9"],"\xC0"=>[77,"Y\x3D"],"\xC1"=>[18,"Y\x3D"],"\xC2"=>[77,"YA"],"\xC3"=>[18,"YA"],"\xC4"=>[77,"Y_"],"\xC5"=>[18,"Y_"],"\xC6"=>[77,"Yb"],"\xC7"=>[18,"Yb"],"\xC8"=>[77,"Yd"],"\xC9"=>[18,"Yd"],"\xCA"=>[77,"Yf"],"\xCB"=>[18,"Yf"],"\xCC"=>[77,"Yg"],"\xCD"=>[18,"Yg"],"\xCE"=>[77,"Yh"],"\xCF"=>[18,"Yh"],"\xD0"=>[77,"Yl"],"\xD1"=>[18,"Yl"],"\xD2"=>[77,"Ym"],"\xD3"=>[18,"Ym"],"\xD4"=>[77,"Yn"],"\xD5"=>[18,"Yn"],"\xD6"=>[77,"Yp"],"\xD7"=>[18,"Yp"],"\xD8"=>[77,"Yr"],"\xD9"=>[18,"Yr"],"\xDA"=>[77,"Yu"],"\xDB"=>[18,"Yu"],"\xDC"=>[0,"Y\x3A"],"\xDD"=>[0,"YB"],"\xDE"=>[0,"YC"],"\xDF"=>[0,"YD"],"\xE0"=>[0,"YE"],"\xE1"=>[0,"YF"],"\xE2"=>[0,"YG"],"\xE3"=>[0,"YH"],"\xE4"=>[0,"YI"],"\xE5"=>[0,"YJ"],"\xE6"=>[0,"YK"],"\xE7"=>[0,"YL"],"\xE8"=>[0,"YM"],"\xE9"=>[0,"YN"],"\xEA"=>[0,"YO"],"\xEB"=>[0,"YP"],"\xEC"=>[0,"YQ"],"\xED"=>[0,"YR"],"\xEE"=>[0,"YS"],"\xEF"=>[0,"YT"],"\xF0"=>[0,"YU"],"\xF1"=>[0,"YV"],"\xF2"=>[0,"YW"],"\xF3"=>[0,"YY"],"\xF4"=>[0,"Yj"],"\xF5"=>[0,"Yk"],"\xF6"=>[0,"Yq"],"\xF7"=>[0,"Yv"],"\xF8"=>[0,"Yw"],"\xF9"=>[0,"Yx"],"\xFA"=>[0,"Yy"],"\xFB"=>[0,"Yz"],"\xFC"=>[82,"Y"],"\xFD"=>[87,"Y"],"\xFE"=>[130,"Y"],"\xFF"=>[9,"Y"],],["\x00"=>[94,"X0"],"\x01"=>[76,"X0"],"\x02"=>[104,"X0"],"\x03"=>[16,"X0"],"\x04"=>[94,"X1"],"\x05"=>[76,"X1"],"\x06"=>[104,"X1"],"\x07"=>[16,"X1"],"\x08"=>[94,"X2"],"\x09"=>[76,"X2"],"\x0A"=>[104,"X2"],"\x0B"=>[16,"X2"],"\x0C"=>[94,"Xa"],"\x0D"=>[76,"Xa"],"\x0E"=>[104,"Xa"],"\x0F"=>[16,"Xa"],"\x10"=>[94,"Xc"],"\x11"=>[76,"Xc"],"\x12"=>[104,"Xc"],"\x13"=>[16,"Xc"],"\x14"=>[94,"Xe"],"\x15"=>[76,"Xe"],"\x16"=>[104,"Xe"],"\x17"=>[16,"Xe"],"\x18"=>[94,"Xi"],"\x19"=>[76,"Xi"],"\x1A"=>[104,"Xi"],"\x1B"=>[16,"Xi"],"\x1C"=>[94,"Xo"],"\x1D"=>[76,"Xo"],"\x1E"=>[104,"Xo"],"\x1F"=>[16,"Xo"],"\x20"=>[94,"Xs"],"\x21"=>[76,"Xs"],"\x22"=>[104,"Xs"],"\x23"=>[16,"Xs"],"\x24"=>[94,"Xt"],"\x25"=>[76,"Xt"],"\x26"=>[104,"Xt"],"\x27"=>[16,"Xt"],"\x28"=>[77,"X\x20"],"\x29"=>[18,"X\x20"],"\x2A"=>[77,"X\x25"],"\x2B"=>[18,"X\x25"],"\x2C"=>[77,"X-"],"-"=>[18,"X-"],"."=>[77,"X."],"\x2F"=>[18,"X."],[77,"X\x2F"],[18,"X\x2F"],[77,"X3"],[18,"X3"],[77,"X4"],[18,"X4"],[77,"X5"],[18,"X5"],[77,"X6"],[18,"X6"],"\x3A"=>[77,"X7"],"\x3B"=>[18,"X7"],"\x3C"=>[77,"X8"],"\x3D"=>[18,"X8"],"\x3E"=>[77,"X9"],"\x3F"=>[18,"X9"],"\x40"=>[77,"X\x3D"],"A"=>[18,"X\x3D"],"B"=>[77,"XA"],"C"=>[18,"XA"],"D"=>[77,"X_"],"E"=>[18,"X_"],"F"=>[77,"Xb"],"G"=>[18,"Xb"],"H"=>[77,"Xd"],"I"=>[18,"Xd"],"J"=>[77,"Xf"],"K"=>[18,"Xf"],"L"=>[77,"Xg"],"M"=>[18,"Xg"],"N"=>[77,"Xh"],"O"=>[18,"Xh"],"P"=>[77,"Xl"],"Q"=>[18,"Xl"],"R"=>[77,"Xm"],"S"=>[18,"Xm"],"T"=>[77,"Xn"],"U"=>[18,"Xn"],"V"=>[77,"Xp"],"W"=>[18,"Xp"],"X"=>[77,"Xr"],"Y"=>[18,"Xr"],"Z"=>[77,"Xu"],"\x5B"=>[18,"Xu"],"\x5C"=>[0,"X\x3A"],"\x5D"=>[0,"XB"],"\x5E"=>[0,"XC"],"_"=>[0,"XD"],"\x60"=>[0,"XE"],"a"=>[0,"XF"],"b"=>[0,"XG"],"c"=>[0,"XH"],"d"=>[0,"XI"],"e"=>[0,"XJ"],"f"=>[0,"XK"],"g"=>[0,"XL"],"h"=>[0,"XM"],"i"=>[0,"XN"],"j"=>[0,"XO"],"k"=>[0,"XP"],"l"=>[0,"XQ"],"m"=>[0,"XR"],"n"=>[0,"XS"],"o"=>[0,"XT"],"p"=>[0,"XU"],"q"=>[0,"XV"],"r"=>[0,"XW"],"s"=>[0,"XY"],"t"=>[0,"Xj"],"u"=>[0,"Xk"],"v"=>[0,"Xq"],"w"=>[0,"Xv"],"x"=>[0,"Xw"],"y"=>[0,"Xx"],"z"=>[0,"Xy"],"\x7B"=>[0,"Xz"],"\x7C"=>[82,"X"],"\x7D"=>[87,"X"],"~"=>[130,"X"],"\x7F"=>[9,"X"],"\x80"=>[94,"Z0"],"\x81"=>[76,"Z0"],"\x82"=>[104,"Z0"],"\x83"=>[16,"Z0"],"\x84"=>[94,"Z1"],"\x85"=>[76,"Z1"],"\x86"=>[104,"Z1"],"\x87"=>[16,"Z1"],"\x88"=>[94,"Z2"],"\x89"=>[76,"Z2"],"\x8A"=>[104,"Z2"],"\x8B"=>[16,"Z2"],"\x8C"=>[94,"Za"],"\x8D"=>[76,"Za"],"\x8E"=>[104,"Za"],"\x8F"=>[16,"Za"],"\x90"=>[94,"Zc"],"\x91"=>[76,"Zc"],"\x92"=>[104,"Zc"],"\x93"=>[16,"Zc"],"\x94"=>[94,"Ze"],"\x95"=>[76,"Ze"],"\x96"=>[104,"Ze"],"\x97"=>[16,"Ze"],"\x98"=>[94,"Zi"],"\x99"=>[76,"Zi"],"\x9A"=>[104,"Zi"],"\x9B"=>[16,"Zi"],"\x9C"=>[94,"Zo"],"\x9D"=>[76,"Zo"],"\x9E"=>[104,"Zo"],"\x9F"=>[16,"Zo"],"\xA0"=>[94,"Zs"],"\xA1"=>[76,"Zs"],"\xA2"=>[104,"Zs"],"\xA3"=>[16,"Zs"],"\xA4"=>[94,"Zt"],"\xA5"=>[76,"Zt"],"\xA6"=>[104,"Zt"],"\xA7"=>[16,"Zt"],"\xA8"=>[77,"Z\x20"],"\xA9"=>[18,"Z\x20"],"\xAA"=>[77,"Z\x25"],"\xAB"=>[18,"Z\x25"],"\xAC"=>[77,"Z-"],"\xAD"=>[18,"Z-"],"\xAE"=>[77,"Z."],"\xAF"=>[18,"Z."],"\xB0"=>[77,"Z\x2F"],"\xB1"=>[18,"Z\x2F"],"\xB2"=>[77,"Z3"],"\xB3"=>[18,"Z3"],"\xB4"=>[77,"Z4"],"\xB5"=>[18,"Z4"],"\xB6"=>[77,"Z5"],"\xB7"=>[18,"Z5"],"\xB8"=>[77,"Z6"],"\xB9"=>[18,"Z6"],"\xBA"=>[77,"Z7"],"\xBB"=>[18,"Z7"],"\xBC"=>[77,"Z8"],"\xBD"=>[18,"Z8"],"\xBE"=>[77,"Z9"],"\xBF"=>[18,"Z9"],"\xC0"=>[77,"Z\x3D"],"\xC1"=>[18,"Z\x3D"],"\xC2"=>[77,"ZA"],"\xC3"=>[18,"ZA"],"\xC4"=>[77,"Z_"],"\xC5"=>[18,"Z_"],"\xC6"=>[77,"Zb"],"\xC7"=>[18,"Zb"],"\xC8"=>[77,"Zd"],"\xC9"=>[18,"Zd"],"\xCA"=>[77,"Zf"],"\xCB"=>[18,"Zf"],"\xCC"=>[77,"Zg"],"\xCD"=>[18,"Zg"],"\xCE"=>[77,"Zh"],"\xCF"=>[18,"Zh"],"\xD0"=>[77,"Zl"],"\xD1"=>[18,"Zl"],"\xD2"=>[77,"Zm"],"\xD3"=>[18,"Zm"],"\xD4"=>[77,"Zn"],"\xD5"=>[18,"Zn"],"\xD6"=>[77,"Zp"],"\xD7"=>[18,"Zp"],"\xD8"=>[77,"Zr"],"\xD9"=>[18,"Zr"],"\xDA"=>[77,"Zu"],"\xDB"=>[18,"Zu"],"\xDC"=>[0,"Z\x3A"],"\xDD"=>[0,"ZB"],"\xDE"=>[0,"ZC"],"\xDF"=>[0,"ZD"],"\xE0"=>[0,"ZE"],"\xE1"=>[0,"ZF"],"\xE2"=>[0,"ZG"],"\xE3"=>[0,"ZH"],"\xE4"=>[0,"ZI"],"\xE5"=>[0,"ZJ"],"\xE6"=>[0,"ZK"],"\xE7"=>[0,"ZL"],"\xE8"=>[0,"ZM"],"\xE9"=>[0,"ZN"],"\xEA"=>[0,"ZO"],"\xEB"=>[0,"ZP"],"\xEC"=>[0,"ZQ"],"\xED"=>[0,"ZR"],"\xEE"=>[0,"ZS"],"\xEF"=>[0,"ZT"],"\xF0"=>[0,"ZU"],"\xF1"=>[0,"ZV"],"\xF2"=>[0,"ZW"],"\xF3"=>[0,"ZY"],"\xF4"=>[0,"Zj"],"\xF5"=>[0,"Zk"],"\xF6"=>[0,"Zq"],"\xF7"=>[0,"Zv"],"\xF8"=>[0,"Zw"],"\xF9"=>[0,"Zx"],"\xFA"=>[0,"Zy"],"\xFB"=>[0,"Zz"],"\xFC"=>[82,"Z"],"\xFD"=>[87,"Z"],"\xFE"=>[130,"Z"],"\xFF"=>[9,"Z"],],["\x00"=>[0,"\x5C0"],"\x01"=>[0,"\x5C1"],"\x02"=>[0,"\x5C2"],"\x03"=>[0,"\x5Ca"],"\x04"=>[0,"\x5Cc"],"\x05"=>[0,"\x5Ce"],"\x06"=>[0,"\x5Ci"],"\x07"=>[0,"\x5Co"],"\x08"=>[0,"\x5Cs"],"\x09"=>[0,"\x5Ct"],"\x0A"=>[73,"\x5C"],"\x0B"=>[88,"\x5C"],"\x0C"=>[89,"\x5C"],"\x0D"=>[96,"\x5C"],"\x0E"=>[97,"\x5C"],"\x0F"=>[99,"\x5C"],"\x10"=>[106,"\x5C"],"\x11"=>[136,"\x5C"],"\x12"=>[139,"\x5C"],"\x13"=>[141,"\x5C"],"\x14"=>[145,"\x5C"],"\x15"=>[147,"\x5C"],"\x16"=>[149,"\x5C"],"\x17"=>[101,"\x5C"],"\x18"=>[112,"\x5C"],"\x19"=>[117,"\x5C"],"\x1A"=>[120,"\x5C"],"\x1B"=>[124,"\x5C"],"\x1C"=>[127,"\x5C"],"\x1D"=>[144,"\x5C"],"\x1E"=>[152,"\x5C"],"\x1F"=>[11,"\x5C"],"\x20"=>[0,"\xC30"],"\x21"=>[0,"\xC31"],"\x22"=>[0,"\xC32"],"\x23"=>[0,"\xC3a"],"\x24"=>[0,"\xC3c"],"\x25"=>[0,"\xC3e"],"\x26"=>[0,"\xC3i"],"\x27"=>[0,"\xC3o"],"\x28"=>[0,"\xC3s"],"\x29"=>[0,"\xC3t"],"\x2A"=>[73,"\xC3"],"\x2B"=>[88,"\xC3"],"\x2C"=>[89,"\xC3"],"-"=>[96,"\xC3"],"."=>[97,"\xC3"],"\x2F"=>[99,"\xC3"],[106,"\xC3"],[136,"\xC3"],[139,"\xC3"],[141,"\xC3"],[145,"\xC3"],[147,"\xC3"],[149,"\xC3"],[101,"\xC3"],[112,"\xC3"],[117,"\xC3"],"\x3A"=>[120,"\xC3"],"\x3B"=>[124,"\xC3"],"\x3C"=>[127,"\xC3"],"\x3D"=>[144,"\xC3"],"\x3E"=>[152,"\xC3"],"\x3F"=>[11,"\xC3"],"\x40"=>[0,"\xD00"],"A"=>[0,"\xD01"],"B"=>[0,"\xD02"],"C"=>[0,"\xD0a"],"D"=>[0,"\xD0c"],"E"=>[0,"\xD0e"],"F"=>[0,"\xD0i"],"G"=>[0,"\xD0o"],"H"=>[0,"\xD0s"],"I"=>[0,"\xD0t"],"J"=>[73,"\xD0"],"K"=>[88,"\xD0"],"L"=>[89,"\xD0"],"M"=>[96,"\xD0"],"N"=>[97,"\xD0"],"O"=>[99,"\xD0"],"P"=>[106,"\xD0"],"Q"=>[136,"\xD0"],"R"=>[139,"\xD0"],"S"=>[141,"\xD0"],"T"=>[145,"\xD0"],"U"=>[147,"\xD0"],"V"=>[149,"\xD0"],"W"=>[101,"\xD0"],"X"=>[112,"\xD0"],"Y"=>[117,"\xD0"],"Z"=>[120,"\xD0"],"\x5B"=>[124,"\xD0"],"\x5C"=>[127,"\xD0"],"\x5D"=>[144,"\xD0"],"\x5E"=>[152,"\xD0"],"_"=>[11,"\xD0"],"\x60"=>[92,"\x80"],"a"=>[95,"\x80"],"b"=>[137,"\x80"],"c"=>[142,"\x80"],"d"=>[150,"\x80"],"e"=>[74,"\x80"],"f"=>[90,"\x80"],"g"=>[98,"\x80"],"h"=>[107,"\x80"],"i"=>[140,"\x80"],"j"=>[146,"\x80"],"k"=>[102,"\x80"],"l"=>[113,"\x80"],"m"=>[121,"\x80"],"n"=>[128,"\x80"],"o"=>[12,"\x80"],"p"=>[92,"\x82"],"q"=>[95,"\x82"],"r"=>[137,"\x82"],"s"=>[142,"\x82"],"t"=>[150,"\x82"],"u"=>[74,"\x82"],"v"=>[90,"\x82"],"w"=>[98,"\x82"],"x"=>[107,"\x82"],"y"=>[140,"\x82"],"z"=>[146,"\x82"],"\x7B"=>[102,"\x82"],"\x7C"=>[113,"\x82"],"\x7D"=>[121,"\x82"],"~"=>[128,"\x82"],"\x7F"=>[12,"\x82"],"\x80"=>[92,"\x83"],"\x81"=>[95,"\x83"],"\x82"=>[137,"\x83"],"\x83"=>[142,"\x83"],"\x84"=>[150,"\x83"],"\x85"=>[74,"\x83"],"\x86"=>[90,"\x83"],"\x87"=>[98,"\x83"],"\x88"=>[107,"\x83"],"\x89"=>[140,"\x83"],"\x8A"=>[146,"\x83"],"\x8B"=>[102,"\x83"],"\x8C"=>[113,"\x83"],"\x8D"=>[121,"\x83"],"\x8E"=>[128,"\x83"],"\x8F"=>[12,"\x83"],"\x90"=>[92,"\xA2"],"\x91"=>[95,"\xA2"],"\x92"=>[137,"\xA2"],"\x93"=>[142,"\xA2"],"\x94"=>[150,"\xA2"],"\x95"=>[74,"\xA2"],"\x96"=>[90,"\xA2"],"\x97"=>[98,"\xA2"],"\x98"=>[107,"\xA2"],"\x99"=>[140,"\xA2"],"\x9A"=>[146,"\xA2"],"\x9B"=>[102,"\xA2"],"\x9C"=>[113,"\xA2"],"\x9D"=>[121,"\xA2"],"\x9E"=>[128,"\xA2"],"\x9F"=>[12,"\xA2"],"\xA0"=>[92,"\xB8"],"\xA1"=>[95,"\xB8"],"\xA2"=>[137,"\xB8"],"\xA3"=>[142,"\xB8"],"\xA4"=>[150,"\xB8"],"\xA5"=>[74,"\xB8"],"\xA6"=>[90,"\xB8"],"\xA7"=>[98,"\xB8"],"\xA8"=>[107,"\xB8"],"\xA9"=>[140,"\xB8"],"\xAA"=>[146,"\xB8"],"\xAB"=>[102,"\xB8"],"\xAC"=>[113,"\xB8"],"\xAD"=>[121,"\xB8"],"\xAE"=>[128,"\xB8"],"\xAF"=>[12,"\xB8"],"\xB0"=>[92,"\xC2"],"\xB1"=>[95,"\xC2"],"\xB2"=>[137,"\xC2"],"\xB3"=>[142,"\xC2"],"\xB4"=>[150,"\xC2"],"\xB5"=>[74,"\xC2"],"\xB6"=>[90,"\xC2"],"\xB7"=>[98,"\xC2"],"\xB8"=>[107,"\xC2"],"\xB9"=>[140,"\xC2"],"\xBA"=>[146,"\xC2"],"\xBB"=>[102,"\xC2"],"\xBC"=>[113,"\xC2"],"\xBD"=>[121,"\xC2"],"\xBE"=>[128,"\xC2"],"\xBF"=>[12,"\xC2"],"\xC0"=>[92,"\xE0"],"\xC1"=>[95,"\xE0"],"\xC2"=>[137,"\xE0"],"\xC3"=>[142,"\xE0"],"\xC4"=>[150,"\xE0"],"\xC5"=>[74,"\xE0"],"\xC6"=>[90,"\xE0"],"\xC7"=>[98,"\xE0"],"\xC8"=>[107,"\xE0"],"\xC9"=>[140,"\xE0"],"\xCA"=>[146,"\xE0"],"\xCB"=>[102,"\xE0"],"\xCC"=>[113,"\xE0"],"\xCD"=>[121,"\xE0"],"\xCE"=>[128,"\xE0"],"\xCF"=>[12,"\xE0"],"\xD0"=>[92,"\xE2"],"\xD1"=>[95,"\xE2"],"\xD2"=>[137,"\xE2"],"\xD3"=>[142,"\xE2"],"\xD4"=>[150,"\xE2"],"\xD5"=>[74,"\xE2"],"\xD6"=>[90,"\xE2"],"\xD7"=>[98,"\xE2"],"\xD8"=>[107,"\xE2"],"\xD9"=>[140,"\xE2"],"\xDA"=>[146,"\xE2"],"\xDB"=>[102,"\xE2"],"\xDC"=>[113,"\xE2"],"\xDD"=>[121,"\xE2"],"\xDE"=>[128,"\xE2"],"\xDF"=>[12,"\xE2"],"\xE0"=>[93,"\x99"],"\xE1"=>[138,"\x99"],"\xE2"=>[75,"\x99"],"\xE3"=>[91,"\x99"],"\xE4"=>[108,"\x99"],"\xE5"=>[103,"\x99"],"\xE6"=>[114,"\x99"],"\xE7"=>[14,"\x99"],"\xE8"=>[93,"\xA1"],"\xE9"=>[138,"\xA1"],"\xEA"=>[75,"\xA1"],"\xEB"=>[91,"\xA1"],"\xEC"=>[108,"\xA1"],"\xED"=>[103,"\xA1"],"\xEE"=>[114,"\xA1"],"\xEF"=>[14,"\xA1"],"\xF0"=>[93,"\xA7"],"\xF1"=>[138,"\xA7"],"\xF2"=>[75,"\xA7"],"\xF3"=>[91,"\xA7"],"\xF4"=>[108,"\xA7"],"\xF5"=>[103,"\xA7"],"\xF6"=>[114,"\xA7"],"\xF7"=>[14,"\xA7"],"\xF8"=>[93,"\xAC"],"\xF9"=>[138,"\xAC"],"\xFA"=>[75,"\xAC"],"\xFB"=>[91,"\xAC"],"\xFC"=>[108,"\xAC"],"\xFD"=>[103,"\xAC"],"\xFE"=>[114,"\xAC"],"\xFF"=>[14,"\xAC"],],["\x00"=>[94,"\x5C0"],"\x01"=>[76,"\x5C0"],"\x02"=>[104,"\x5C0"],"\x03"=>[16,"\x5C0"],"\x04"=>[94,"\x5C1"],"\x05"=>[76,"\x5C1"],"\x06"=>[104,"\x5C1"],"\x07"=>[16,"\x5C1"],"\x08"=>[94,"\x5C2"],"\x09"=>[76,"\x5C2"],"\x0A"=>[104,"\x5C2"],"\x0B"=>[16,"\x5C2"],"\x0C"=>[94,"\x5Ca"],"\x0D"=>[76,"\x5Ca"],"\x0E"=>[104,"\x5Ca"],"\x0F"=>[16,"\x5Ca"],"\x10"=>[94,"\x5Cc"],"\x11"=>[76,"\x5Cc"],"\x12"=>[104,"\x5Cc"],"\x13"=>[16,"\x5Cc"],"\x14"=>[94,"\x5Ce"],"\x15"=>[76,"\x5Ce"],"\x16"=>[104,"\x5Ce"],"\x17"=>[16,"\x5Ce"],"\x18"=>[94,"\x5Ci"],"\x19"=>[76,"\x5Ci"],"\x1A"=>[104,"\x5Ci"],"\x1B"=>[16,"\x5Ci"],"\x1C"=>[94,"\x5Co"],"\x1D"=>[76,"\x5Co"],"\x1E"=>[104,"\x5Co"],"\x1F"=>[16,"\x5Co"],"\x20"=>[94,"\x5Cs"],"\x21"=>[76,"\x5Cs"],"\x22"=>[104,"\x5Cs"],"\x23"=>[16,"\x5Cs"],"\x24"=>[94,"\x5Ct"],"\x25"=>[76,"\x5Ct"],"\x26"=>[104,"\x5Ct"],"\x27"=>[16,"\x5Ct"],"\x28"=>[77,"\x5C\x20"],"\x29"=>[18,"\x5C\x20"],"\x2A"=>[77,"\x5C\x25"],"\x2B"=>[18,"\x5C\x25"],"\x2C"=>[77,"\x5C-"],"-"=>[18,"\x5C-"],"."=>[77,"\x5C."],"\x2F"=>[18,"\x5C."],[77,"\x5C\x2F"],[18,"\x5C\x2F"],[77,"\x5C3"],[18,"\x5C3"],[77,"\x5C4"],[18,"\x5C4"],[77,"\x5C5"],[18,"\x5C5"],[77,"\x5C6"],[18,"\x5C6"],"\x3A"=>[77,"\x5C7"],"\x3B"=>[18,"\x5C7"],"\x3C"=>[77,"\x5C8"],"\x3D"=>[18,"\x5C8"],"\x3E"=>[77,"\x5C9"],"\x3F"=>[18,"\x5C9"],"\x40"=>[77,"\x5C\x3D"],"A"=>[18,"\x5C\x3D"],"B"=>[77,"\x5CA"],"C"=>[18,"\x5CA"],"D"=>[77,"\x5C_"],"E"=>[18,"\x5C_"],"F"=>[77,"\x5Cb"],"G"=>[18,"\x5Cb"],"H"=>[77,"\x5Cd"],"I"=>[18,"\x5Cd"],"J"=>[77,"\x5Cf"],"K"=>[18,"\x5Cf"],"L"=>[77,"\x5Cg"],"M"=>[18,"\x5Cg"],"N"=>[77,"\x5Ch"],"O"=>[18,"\x5Ch"],"P"=>[77,"\x5Cl"],"Q"=>[18,"\x5Cl"],"R"=>[77,"\x5Cm"],"S"=>[18,"\x5Cm"],"T"=>[77,"\x5Cn"],"U"=>[18,"\x5Cn"],"V"=>[77,"\x5Cp"],"W"=>[18,"\x5Cp"],"X"=>[77,"\x5Cr"],"Y"=>[18,"\x5Cr"],"Z"=>[77,"\x5Cu"],"\x5B"=>[18,"\x5Cu"],"\x5C"=>[0,"\x5C\x3A"],"\x5D"=>[0,"\x5CB"],"\x5E"=>[0,"\x5CC"],"_"=>[0,"\x5CD"],"\x60"=>[0,"\x5CE"],"a"=>[0,"\x5CF"],"b"=>[0,"\x5CG"],"c"=>[0,"\x5CH"],"d"=>[0,"\x5CI"],"e"=>[0,"\x5CJ"],"f"=>[0,"\x5CK"],"g"=>[0,"\x5CL"],"h"=>[0,"\x5CM"],"i"=>[0,"\x5CN"],"j"=>[0,"\x5CO"],"k"=>[0,"\x5CP"],"l"=>[0,"\x5CQ"],"m"=>[0,"\x5CR"],"n"=>[0,"\x5CS"],"o"=>[0,"\x5CT"],"p"=>[0,"\x5CU"],"q"=>[0,"\x5CV"],"r"=>[0,"\x5CW"],"s"=>[0,"\x5CY"],"t"=>[0,"\x5Cj"],"u"=>[0,"\x5Ck"],"v"=>[0,"\x5Cq"],"w"=>[0,"\x5Cv"],"x"=>[0,"\x5Cw"],"y"=>[0,"\x5Cx"],"z"=>[0,"\x5Cy"],"\x7B"=>[0,"\x5Cz"],"\x7C"=>[82,"\x5C"],"\x7D"=>[87,"\x5C"],"~"=>[130,"\x5C"],"\x7F"=>[9,"\x5C"],"\x80"=>[94,"\xC30"],"\x81"=>[76,"\xC30"],"\x82"=>[104,"\xC30"],"\x83"=>[16,"\xC30"],"\x84"=>[94,"\xC31"],"\x85"=>[76,"\xC31"],"\x86"=>[104,"\xC31"],"\x87"=>[16,"\xC31"],"\x88"=>[94,"\xC32"],"\x89"=>[76,"\xC32"],"\x8A"=>[104,"\xC32"],"\x8B"=>[16,"\xC32"],"\x8C"=>[94,"\xC3a"],"\x8D"=>[76,"\xC3a"],"\x8E"=>[104,"\xC3a"],"\x8F"=>[16,"\xC3a"],"\x90"=>[94,"\xC3c"],"\x91"=>[76,"\xC3c"],"\x92"=>[104,"\xC3c"],"\x93"=>[16,"\xC3c"],"\x94"=>[94,"\xC3e"],"\x95"=>[76,"\xC3e"],"\x96"=>[104,"\xC3e"],"\x97"=>[16,"\xC3e"],"\x98"=>[94,"\xC3i"],"\x99"=>[76,"\xC3i"],"\x9A"=>[104,"\xC3i"],"\x9B"=>[16,"\xC3i"],"\x9C"=>[94,"\xC3o"],"\x9D"=>[76,"\xC3o"],"\x9E"=>[104,"\xC3o"],"\x9F"=>[16,"\xC3o"],"\xA0"=>[94,"\xC3s"],"\xA1"=>[76,"\xC3s"],"\xA2"=>[104,"\xC3s"],"\xA3"=>[16,"\xC3s"],"\xA4"=>[94,"\xC3t"],"\xA5"=>[76,"\xC3t"],"\xA6"=>[104,"\xC3t"],"\xA7"=>[16,"\xC3t"],"\xA8"=>[77,"\xC3\x20"],"\xA9"=>[18,"\xC3\x20"],"\xAA"=>[77,"\xC3\x25"],"\xAB"=>[18,"\xC3\x25"],"\xAC"=>[77,"\xC3-"],"\xAD"=>[18,"\xC3-"],"\xAE"=>[77,"\xC3."],"\xAF"=>[18,"\xC3."],"\xB0"=>[77,"\xC3\x2F"],"\xB1"=>[18,"\xC3\x2F"],"\xB2"=>[77,"\xC33"],"\xB3"=>[18,"\xC33"],"\xB4"=>[77,"\xC34"],"\xB5"=>[18,"\xC34"],"\xB6"=>[77,"\xC35"],"\xB7"=>[18,"\xC35"],"\xB8"=>[77,"\xC36"],"\xB9"=>[18,"\xC36"],"\xBA"=>[77,"\xC37"],"\xBB"=>[18,"\xC37"],"\xBC"=>[77,"\xC38"],"\xBD"=>[18,"\xC38"],"\xBE"=>[77,"\xC39"],"\xBF"=>[18,"\xC39"],"\xC0"=>[77,"\xC3\x3D"],"\xC1"=>[18,"\xC3\x3D"],"\xC2"=>[77,"\xC3A"],"\xC3"=>[18,"\xC3A"],"\xC4"=>[77,"\xC3_"],"\xC5"=>[18,"\xC3_"],"\xC6"=>[77,"\xC3b"],"\xC7"=>[18,"\xC3b"],"\xC8"=>[77,"\xC3d"],"\xC9"=>[18,"\xC3d"],"\xCA"=>[77,"\xC3f"],"\xCB"=>[18,"\xC3f"],"\xCC"=>[77,"\xC3g"],"\xCD"=>[18,"\xC3g"],"\xCE"=>[77,"\xC3h"],"\xCF"=>[18,"\xC3h"],"\xD0"=>[77,"\xC3l"],"\xD1"=>[18,"\xC3l"],"\xD2"=>[77,"\xC3m"],"\xD3"=>[18,"\xC3m"],"\xD4"=>[77,"\xC3n"],"\xD5"=>[18,"\xC3n"],"\xD6"=>[77,"\xC3p"],"\xD7"=>[18,"\xC3p"],"\xD8"=>[77,"\xC3r"],"\xD9"=>[18,"\xC3r"],"\xDA"=>[77,"\xC3u"],"\xDB"=>[18,"\xC3u"],"\xDC"=>[0,"\xC3\x3A"],"\xDD"=>[0,"\xC3B"],"\xDE"=>[0,"\xC3C"],"\xDF"=>[0,"\xC3D"],"\xE0"=>[0,"\xC3E"],"\xE1"=>[0,"\xC3F"],"\xE2"=>[0,"\xC3G"],"\xE3"=>[0,"\xC3H"],"\xE4"=>[0,"\xC3I"],"\xE5"=>[0,"\xC3J"],"\xE6"=>[0,"\xC3K"],"\xE7"=>[0,"\xC3L"],"\xE8"=>[0,"\xC3M"],"\xE9"=>[0,"\xC3N"],"\xEA"=>[0,"\xC3O"],"\xEB"=>[0,"\xC3P"],"\xEC"=>[0,"\xC3Q"],"\xED"=>[0,"\xC3R"],"\xEE"=>[0,"\xC3S"],"\xEF"=>[0,"\xC3T"],"\xF0"=>[0,"\xC3U"],"\xF1"=>[0,"\xC3V"],"\xF2"=>[0,"\xC3W"],"\xF3"=>[0,"\xC3Y"],"\xF4"=>[0,"\xC3j"],"\xF5"=>[0,"\xC3k"],"\xF6"=>[0,"\xC3q"],"\xF7"=>[0,"\xC3v"],"\xF8"=>[0,"\xC3w"],"\xF9"=>[0,"\xC3x"],"\xFA"=>[0,"\xC3y"],"\xFB"=>[0,"\xC3z"],"\xFC"=>[82,"\xC3"],"\xFD"=>[87,"\xC3"],"\xFE"=>[130,"\xC3"],"\xFF"=>[9,"\xC3"],],["\x00"=>[77,"\x5C0"],"\x01"=>[18,"\x5C0"],"\x02"=>[77,"\x5C1"],"\x03"=>[18,"\x5C1"],"\x04"=>[77,"\x5C2"],"\x05"=>[18,"\x5C2"],"\x06"=>[77,"\x5Ca"],"\x07"=>[18,"\x5Ca"],"\x08"=>[77,"\x5Cc"],"\x09"=>[18,"\x5Cc"],"\x0A"=>[77,"\x5Ce"],"\x0B"=>[18,"\x5Ce"],"\x0C"=>[77,"\x5Ci"],"\x0D"=>[18,"\x5Ci"],"\x0E"=>[77,"\x5Co"],"\x0F"=>[18,"\x5Co"],"\x10"=>[77,"\x5Cs"],"\x11"=>[18,"\x5Cs"],"\x12"=>[77,"\x5Ct"],"\x13"=>[18,"\x5Ct"],"\x14"=>[0,"\x5C\x20"],"\x15"=>[0,"\x5C\x25"],"\x16"=>[0,"\x5C-"],"\x17"=>[0,"\x5C."],"\x18"=>[0,"\x5C\x2F"],"\x19"=>[0,"\x5C3"],"\x1A"=>[0,"\x5C4"],"\x1B"=>[0,"\x5C5"],"\x1C"=>[0,"\x5C6"],"\x1D"=>[0,"\x5C7"],"\x1E"=>[0,"\x5C8"],"\x1F"=>[0,"\x5C9"],"\x20"=>[0,"\x5C\x3D"],"\x21"=>[0,"\x5CA"],"\x22"=>[0,"\x5C_"],"\x23"=>[0,"\x5Cb"],"\x24"=>[0,"\x5Cd"],"\x25"=>[0,"\x5Cf"],"\x26"=>[0,"\x5Cg"],"\x27"=>[0,"\x5Ch"],"\x28"=>[0,"\x5Cl"],"\x29"=>[0,"\x5Cm"],"\x2A"=>[0,"\x5Cn"],"\x2B"=>[0,"\x5Cp"],"\x2C"=>[0,"\x5Cr"],"-"=>[0,"\x5Cu"],"."=>[100,"\x5C"],"\x2F"=>[110,"\x5C"],[111,"\x5C"],[115,"\x5C"],[116,"\x5C"],[118,"\x5C"],[119,"\x5C"],[122,"\x5C"],[123,"\x5C"],[125,"\x5C"],[126,"\x5C"],[129,"\x5C"],"\x3A"=>[143,"\x5C"],"\x3B"=>[148,"\x5C"],"\x3C"=>[151,"\x5C"],"\x3D"=>[153,"\x5C"],"\x3E"=>[83,"\x5C"],"\x3F"=>[10,"\x5C"],"\x40"=>[77,"\xC30"],"A"=>[18,"\xC30"],"B"=>[77,"\xC31"],"C"=>[18,"\xC31"],"D"=>[77,"\xC32"],"E"=>[18,"\xC32"],"F"=>[77,"\xC3a"],"G"=>[18,"\xC3a"],"H"=>[77,"\xC3c"],"I"=>[18,"\xC3c"],"J"=>[77,"\xC3e"],"K"=>[18,"\xC3e"],"L"=>[77,"\xC3i"],"M"=>[18,"\xC3i"],"N"=>[77,"\xC3o"],"O"=>[18,"\xC3o"],"P"=>[77,"\xC3s"],"Q"=>[18,"\xC3s"],"R"=>[77,"\xC3t"],"S"=>[18,"\xC3t"],"T"=>[0,"\xC3\x20"],"U"=>[0,"\xC3\x25"],"V"=>[0,"\xC3-"],"W"=>[0,"\xC3."],"X"=>[0,"\xC3\x2F"],"Y"=>[0,"\xC33"],"Z"=>[0,"\xC34"],"\x5B"=>[0,"\xC35"],"\x5C"=>[0,"\xC36"],"\x5D"=>[0,"\xC37"],"\x5E"=>[0,"\xC38"],"_"=>[0,"\xC39"],"\x60"=>[0,"\xC3\x3D"],"a"=>[0,"\xC3A"],"b"=>[0,"\xC3_"],"c"=>[0,"\xC3b"],"d"=>[0,"\xC3d"],"e"=>[0,"\xC3f"],"f"=>[0,"\xC3g"],"g"=>[0,"\xC3h"],"h"=>[0,"\xC3l"],"i"=>[0,"\xC3m"],"j"=>[0,"\xC3n"],"k"=>[0,"\xC3p"],"l"=>[0,"\xC3r"],"m"=>[0,"\xC3u"],"n"=>[100,"\xC3"],"o"=>[110,"\xC3"],"p"=>[111,"\xC3"],"q"=>[115,"\xC3"],"r"=>[116,"\xC3"],"s"=>[118,"\xC3"],"t"=>[119,"\xC3"],"u"=>[122,"\xC3"],"v"=>[123,"\xC3"],"w"=>[125,"\xC3"],"x"=>[126,"\xC3"],"y"=>[129,"\xC3"],"z"=>[143,"\xC3"],"\x7B"=>[148,"\xC3"],"\x7C"=>[151,"\xC3"],"\x7D"=>[153,"\xC3"],"~"=>[83,"\xC3"],"\x7F"=>[10,"\xC3"],"\x80"=>[77,"\xD00"],"\x81"=>[18,"\xD00"],"\x82"=>[77,"\xD01"],"\x83"=>[18,"\xD01"],"\x84"=>[77,"\xD02"],"\x85"=>[18,"\xD02"],"\x86"=>[77,"\xD0a"],"\x87"=>[18,"\xD0a"],"\x88"=>[77,"\xD0c"],"\x89"=>[18,"\xD0c"],"\x8A"=>[77,"\xD0e"],"\x8B"=>[18,"\xD0e"],"\x8C"=>[77,"\xD0i"],"\x8D"=>[18,"\xD0i"],"\x8E"=>[77,"\xD0o"],"\x8F"=>[18,"\xD0o"],"\x90"=>[77,"\xD0s"],"\x91"=>[18,"\xD0s"],"\x92"=>[77,"\xD0t"],"\x93"=>[18,"\xD0t"],"\x94"=>[0,"\xD0\x20"],"\x95"=>[0,"\xD0\x25"],"\x96"=>[0,"\xD0-"],"\x97"=>[0,"\xD0."],"\x98"=>[0,"\xD0\x2F"],"\x99"=>[0,"\xD03"],"\x9A"=>[0,"\xD04"],"\x9B"=>[0,"\xD05"],"\x9C"=>[0,"\xD06"],"\x9D"=>[0,"\xD07"],"\x9E"=>[0,"\xD08"],"\x9F"=>[0,"\xD09"],"\xA0"=>[0,"\xD0\x3D"],"\xA1"=>[0,"\xD0A"],"\xA2"=>[0,"\xD0_"],"\xA3"=>[0,"\xD0b"],"\xA4"=>[0,"\xD0d"],"\xA5"=>[0,"\xD0f"],"\xA6"=>[0,"\xD0g"],"\xA7"=>[0,"\xD0h"],"\xA8"=>[0,"\xD0l"],"\xA9"=>[0,"\xD0m"],"\xAA"=>[0,"\xD0n"],"\xAB"=>[0,"\xD0p"],"\xAC"=>[0,"\xD0r"],"\xAD"=>[0,"\xD0u"],"\xAE"=>[100,"\xD0"],"\xAF"=>[110,"\xD0"],"\xB0"=>[111,"\xD0"],"\xB1"=>[115,"\xD0"],"\xB2"=>[116,"\xD0"],"\xB3"=>[118,"\xD0"],"\xB4"=>[119,"\xD0"],"\xB5"=>[122,"\xD0"],"\xB6"=>[123,"\xD0"],"\xB7"=>[125,"\xD0"],"\xB8"=>[126,"\xD0"],"\xB9"=>[129,"\xD0"],"\xBA"=>[143,"\xD0"],"\xBB"=>[148,"\xD0"],"\xBC"=>[151,"\xD0"],"\xBD"=>[153,"\xD0"],"\xBE"=>[83,"\xD0"],"\xBF"=>[10,"\xD0"],"\xC0"=>[0,"\x800"],"\xC1"=>[0,"\x801"],"\xC2"=>[0,"\x802"],"\xC3"=>[0,"\x80a"],"\xC4"=>[0,"\x80c"],"\xC5"=>[0,"\x80e"],"\xC6"=>[0,"\x80i"],"\xC7"=>[0,"\x80o"],"\xC8"=>[0,"\x80s"],"\xC9"=>[0,"\x80t"],"\xCA"=>[73,"\x80"],"\xCB"=>[88,"\x80"],"\xCC"=>[89,"\x80"],"\xCD"=>[96,"\x80"],"\xCE"=>[97,"\x80"],"\xCF"=>[99,"\x80"],"\xD0"=>[106,"\x80"],"\xD1"=>[136,"\x80"],"\xD2"=>[139,"\x80"],"\xD3"=>[141,"\x80"],"\xD4"=>[145,"\x80"],"\xD5"=>[147,"\x80"],"\xD6"=>[149,"\x80"],"\xD7"=>[101,"\x80"],"\xD8"=>[112,"\x80"],"\xD9"=>[117,"\x80"],"\xDA"=>[120,"\x80"],"\xDB"=>[124,"\x80"],"\xDC"=>[127,"\x80"],"\xDD"=>[144,"\x80"],"\xDE"=>[152,"\x80"],"\xDF"=>[11,"\x80"],"\xE0"=>[0,"\x820"],"\xE1"=>[0,"\x821"],"\xE2"=>[0,"\x822"],"\xE3"=>[0,"\x82a"],"\xE4"=>[0,"\x82c"],"\xE5"=>[0,"\x82e"],"\xE6"=>[0,"\x82i"],"\xE7"=>[0,"\x82o"],"\xE8"=>[0,"\x82s"],"\xE9"=>[0,"\x82t"],"\xEA"=>[73,"\x82"],"\xEB"=>[88,"\x82"],"\xEC"=>[89,"\x82"],"\xED"=>[96,"\x82"],"\xEE"=>[97,"\x82"],"\xEF"=>[99,"\x82"],"\xF0"=>[106,"\x82"],"\xF1"=>[136,"\x82"],"\xF2"=>[139,"\x82"],"\xF3"=>[141,"\x82"],"\xF4"=>[145,"\x82"],"\xF5"=>[147,"\x82"],"\xF6"=>[149,"\x82"],"\xF7"=>[101,"\x82"],"\xF8"=>[112,"\x82"],"\xF9"=>[117,"\x82"],"\xFA"=>[120,"\x82"],"\xFB"=>[124,"\x82"],"\xFC"=>[127,"\x82"],"\xFD"=>[144,"\x82"],"\xFE"=>[152,"\x82"],"\xFF"=>[11,"\x82"],],["\x00"=>[94,"\x5D0"],"\x01"=>[76,"\x5D0"],"\x02"=>[104,"\x5D0"],"\x03"=>[16,"\x5D0"],"\x04"=>[94,"\x5D1"],"\x05"=>[76,"\x5D1"],"\x06"=>[104,"\x5D1"],"\x07"=>[16,"\x5D1"],"\x08"=>[94,"\x5D2"],"\x09"=>[76,"\x5D2"],"\x0A"=>[104,"\x5D2"],"\x0B"=>[16,"\x5D2"],"\x0C"=>[94,"\x5Da"],"\x0D"=>[76,"\x5Da"],"\x0E"=>[104,"\x5Da"],"\x0F"=>[16,"\x5Da"],"\x10"=>[94,"\x5Dc"],"\x11"=>[76,"\x5Dc"],"\x12"=>[104,"\x5Dc"],"\x13"=>[16,"\x5Dc"],"\x14"=>[94,"\x5De"],"\x15"=>[76,"\x5De"],"\x16"=>[104,"\x5De"],"\x17"=>[16,"\x5De"],"\x18"=>[94,"\x5Di"],"\x19"=>[76,"\x5Di"],"\x1A"=>[104,"\x5Di"],"\x1B"=>[16,"\x5Di"],"\x1C"=>[94,"\x5Do"],"\x1D"=>[76,"\x5Do"],"\x1E"=>[104,"\x5Do"],"\x1F"=>[16,"\x5Do"],"\x20"=>[94,"\x5Ds"],"\x21"=>[76,"\x5Ds"],"\x22"=>[104,"\x5Ds"],"\x23"=>[16,"\x5Ds"],"\x24"=>[94,"\x5Dt"],"\x25"=>[76,"\x5Dt"],"\x26"=>[104,"\x5Dt"],"\x27"=>[16,"\x5Dt"],"\x28"=>[77,"\x5D\x20"],"\x29"=>[18,"\x5D\x20"],"\x2A"=>[77,"\x5D\x25"],"\x2B"=>[18,"\x5D\x25"],"\x2C"=>[77,"\x5D-"],"-"=>[18,"\x5D-"],"."=>[77,"\x5D."],"\x2F"=>[18,"\x5D."],[77,"\x5D\x2F"],[18,"\x5D\x2F"],[77,"\x5D3"],[18,"\x5D3"],[77,"\x5D4"],[18,"\x5D4"],[77,"\x5D5"],[18,"\x5D5"],[77,"\x5D6"],[18,"\x5D6"],"\x3A"=>[77,"\x5D7"],"\x3B"=>[18,"\x5D7"],"\x3C"=>[77,"\x5D8"],"\x3D"=>[18,"\x5D8"],"\x3E"=>[77,"\x5D9"],"\x3F"=>[18,"\x5D9"],"\x40"=>[77,"\x5D\x3D"],"A"=>[18,"\x5D\x3D"],"B"=>[77,"\x5DA"],"C"=>[18,"\x5DA"],"D"=>[77,"\x5D_"],"E"=>[18,"\x5D_"],"F"=>[77,"\x5Db"],"G"=>[18,"\x5Db"],"H"=>[77,"\x5Dd"],"I"=>[18,"\x5Dd"],"J"=>[77,"\x5Df"],"K"=>[18,"\x5Df"],"L"=>[77,"\x5Dg"],"M"=>[18,"\x5Dg"],"N"=>[77,"\x5Dh"],"O"=>[18,"\x5Dh"],"P"=>[77,"\x5Dl"],"Q"=>[18,"\x5Dl"],"R"=>[77,"\x5Dm"],"S"=>[18,"\x5Dm"],"T"=>[77,"\x5Dn"],"U"=>[18,"\x5Dn"],"V"=>[77,"\x5Dp"],"W"=>[18,"\x5Dp"],"X"=>[77,"\x5Dr"],"Y"=>[18,"\x5Dr"],"Z"=>[77,"\x5Du"],"\x5B"=>[18,"\x5Du"],"\x5C"=>[0,"\x5D\x3A"],"\x5D"=>[0,"\x5DB"],"\x5E"=>[0,"\x5DC"],"_"=>[0,"\x5DD"],"\x60"=>[0,"\x5DE"],"a"=>[0,"\x5DF"],"b"=>[0,"\x5DG"],"c"=>[0,"\x5DH"],"d"=>[0,"\x5DI"],"e"=>[0,"\x5DJ"],"f"=>[0,"\x5DK"],"g"=>[0,"\x5DL"],"h"=>[0,"\x5DM"],"i"=>[0,"\x5DN"],"j"=>[0,"\x5DO"],"k"=>[0,"\x5DP"],"l"=>[0,"\x5DQ"],"m"=>[0,"\x5DR"],"n"=>[0,"\x5DS"],"o"=>[0,"\x5DT"],"p"=>[0,"\x5DU"],"q"=>[0,"\x5DV"],"r"=>[0,"\x5DW"],"s"=>[0,"\x5DY"],"t"=>[0,"\x5Dj"],"u"=>[0,"\x5Dk"],"v"=>[0,"\x5Dq"],"w"=>[0,"\x5Dv"],"x"=>[0,"\x5Dw"],"y"=>[0,"\x5Dx"],"z"=>[0,"\x5Dy"],"\x7B"=>[0,"\x5Dz"],"\x7C"=>[82,"\x5D"],"\x7D"=>[87,"\x5D"],"~"=>[130,"\x5D"],"\x7F"=>[9,"\x5D"],"\x80"=>[94,"~0"],"\x81"=>[76,"~0"],"\x82"=>[104,"~0"],"\x83"=>[16,"~0"],"\x84"=>[94,"~1"],"\x85"=>[76,"~1"],"\x86"=>[104,"~1"],"\x87"=>[16,"~1"],"\x88"=>[94,"~2"],"\x89"=>[76,"~2"],"\x8A"=>[104,"~2"],"\x8B"=>[16,"~2"],"\x8C"=>[94,"~a"],"\x8D"=>[76,"~a"],"\x8E"=>[104,"~a"],"\x8F"=>[16,"~a"],"\x90"=>[94,"~c"],"\x91"=>[76,"~c"],"\x92"=>[104,"~c"],"\x93"=>[16,"~c"],"\x94"=>[94,"~e"],"\x95"=>[76,"~e"],"\x96"=>[104,"~e"],"\x97"=>[16,"~e"],"\x98"=>[94,"~i"],"\x99"=>[76,"~i"],"\x9A"=>[104,"~i"],"\x9B"=>[16,"~i"],"\x9C"=>[94,"~o"],"\x9D"=>[76,"~o"],"\x9E"=>[104,"~o"],"\x9F"=>[16,"~o"],"\xA0"=>[94,"~s"],"\xA1"=>[76,"~s"],"\xA2"=>[104,"~s"],"\xA3"=>[16,"~s"],"\xA4"=>[94,"~t"],"\xA5"=>[76,"~t"],"\xA6"=>[104,"~t"],"\xA7"=>[16,"~t"],"\xA8"=>[77,"~\x20"],"\xA9"=>[18,"~\x20"],"\xAA"=>[77,"~\x25"],"\xAB"=>[18,"~\x25"],"\xAC"=>[77,"~-"],"\xAD"=>[18,"~-"],"\xAE"=>[77,"~."],"\xAF"=>[18,"~."],"\xB0"=>[77,"~\x2F"],"\xB1"=>[18,"~\x2F"],"\xB2"=>[77,"~3"],"\xB3"=>[18,"~3"],"\xB4"=>[77,"~4"],"\xB5"=>[18,"~4"],"\xB6"=>[77,"~5"],"\xB7"=>[18,"~5"],"\xB8"=>[77,"~6"],"\xB9"=>[18,"~6"],"\xBA"=>[77,"~7"],"\xBB"=>[18,"~7"],"\xBC"=>[77,"~8"],"\xBD"=>[18,"~8"],"\xBE"=>[77,"~9"],"\xBF"=>[18,"~9"],"\xC0"=>[77,"~\x3D"],"\xC1"=>[18,"~\x3D"],"\xC2"=>[77,"~A"],"\xC3"=>[18,"~A"],"\xC4"=>[77,"~_"],"\xC5"=>[18,"~_"],"\xC6"=>[77,"~b"],"\xC7"=>[18,"~b"],"\xC8"=>[77,"~d"],"\xC9"=>[18,"~d"],"\xCA"=>[77,"~f"],"\xCB"=>[18,"~f"],"\xCC"=>[77,"~g"],"\xCD"=>[18,"~g"],"\xCE"=>[77,"~h"],"\xCF"=>[18,"~h"],"\xD0"=>[77,"~l"],"\xD1"=>[18,"~l"],"\xD2"=>[77,"~m"],"\xD3"=>[18,"~m"],"\xD4"=>[77,"~n"],"\xD5"=>[18,"~n"],"\xD6"=>[77,"~p"],"\xD7"=>[18,"~p"],"\xD8"=>[77,"~r"],"\xD9"=>[18,"~r"],"\xDA"=>[77,"~u"],"\xDB"=>[18,"~u"],"\xDC"=>[0,"~\x3A"],"\xDD"=>[0,"~B"],"\xDE"=>[0,"~C"],"\xDF"=>[0,"~D"],"\xE0"=>[0,"~E"],"\xE1"=>[0,"~F"],"\xE2"=>[0,"~G"],"\xE3"=>[0,"~H"],"\xE4"=>[0,"~I"],"\xE5"=>[0,"~J"],"\xE6"=>[0,"~K"],"\xE7"=>[0,"~L"],"\xE8"=>[0,"~M"],"\xE9"=>[0,"~N"],"\xEA"=>[0,"~O"],"\xEB"=>[0,"~P"],"\xEC"=>[0,"~Q"],"\xED"=>[0,"~R"],"\xEE"=>[0,"~S"],"\xEF"=>[0,"~T"],"\xF0"=>[0,"~U"],"\xF1"=>[0,"~V"],"\xF2"=>[0,"~W"],"\xF3"=>[0,"~Y"],"\xF4"=>[0,"~j"],"\xF5"=>[0,"~k"],"\xF6"=>[0,"~q"],"\xF7"=>[0,"~v"],"\xF8"=>[0,"~w"],"\xF9"=>[0,"~x"],"\xFA"=>[0,"~y"],"\xFB"=>[0,"~z"],"\xFC"=>[82,"~"],"\xFD"=>[87,"~"],"\xFE"=>[130,"~"],"\xFF"=>[9,"~"],],["\x00"=>[94,"\x5E0"],"\x01"=>[76,"\x5E0"],"\x02"=>[104,"\x5E0"],"\x03"=>[16,"\x5E0"],"\x04"=>[94,"\x5E1"],"\x05"=>[76,"\x5E1"],"\x06"=>[104,"\x5E1"],"\x07"=>[16,"\x5E1"],"\x08"=>[94,"\x5E2"],"\x09"=>[76,"\x5E2"],"\x0A"=>[104,"\x5E2"],"\x0B"=>[16,"\x5E2"],"\x0C"=>[94,"\x5Ea"],"\x0D"=>[76,"\x5Ea"],"\x0E"=>[104,"\x5Ea"],"\x0F"=>[16,"\x5Ea"],"\x10"=>[94,"\x5Ec"],"\x11"=>[76,"\x5Ec"],"\x12"=>[104,"\x5Ec"],"\x13"=>[16,"\x5Ec"],"\x14"=>[94,"\x5Ee"],"\x15"=>[76,"\x5Ee"],"\x16"=>[104,"\x5Ee"],"\x17"=>[16,"\x5Ee"],"\x18"=>[94,"\x5Ei"],"\x19"=>[76,"\x5Ei"],"\x1A"=>[104,"\x5Ei"],"\x1B"=>[16,"\x5Ei"],"\x1C"=>[94,"\x5Eo"],"\x1D"=>[76,"\x5Eo"],"\x1E"=>[104,"\x5Eo"],"\x1F"=>[16,"\x5Eo"],"\x20"=>[94,"\x5Es"],"\x21"=>[76,"\x5Es"],"\x22"=>[104,"\x5Es"],"\x23"=>[16,"\x5Es"],"\x24"=>[94,"\x5Et"],"\x25"=>[76,"\x5Et"],"\x26"=>[104,"\x5Et"],"\x27"=>[16,"\x5Et"],"\x28"=>[77,"\x5E\x20"],"\x29"=>[18,"\x5E\x20"],"\x2A"=>[77,"\x5E\x25"],"\x2B"=>[18,"\x5E\x25"],"\x2C"=>[77,"\x5E-"],"-"=>[18,"\x5E-"],"."=>[77,"\x5E."],"\x2F"=>[18,"\x5E."],[77,"\x5E\x2F"],[18,"\x5E\x2F"],[77,"\x5E3"],[18,"\x5E3"],[77,"\x5E4"],[18,"\x5E4"],[77,"\x5E5"],[18,"\x5E5"],[77,"\x5E6"],[18,"\x5E6"],"\x3A"=>[77,"\x5E7"],"\x3B"=>[18,"\x5E7"],"\x3C"=>[77,"\x5E8"],"\x3D"=>[18,"\x5E8"],"\x3E"=>[77,"\x5E9"],"\x3F"=>[18,"\x5E9"],"\x40"=>[77,"\x5E\x3D"],"A"=>[18,"\x5E\x3D"],"B"=>[77,"\x5EA"],"C"=>[18,"\x5EA"],"D"=>[77,"\x5E_"],"E"=>[18,"\x5E_"],"F"=>[77,"\x5Eb"],"G"=>[18,"\x5Eb"],"H"=>[77,"\x5Ed"],"I"=>[18,"\x5Ed"],"J"=>[77,"\x5Ef"],"K"=>[18,"\x5Ef"],"L"=>[77,"\x5Eg"],"M"=>[18,"\x5Eg"],"N"=>[77,"\x5Eh"],"O"=>[18,"\x5Eh"],"P"=>[77,"\x5El"],"Q"=>[18,"\x5El"],"R"=>[77,"\x5Em"],"S"=>[18,"\x5Em"],"T"=>[77,"\x5En"],"U"=>[18,"\x5En"],"V"=>[77,"\x5Ep"],"W"=>[18,"\x5Ep"],"X"=>[77,"\x5Er"],"Y"=>[18,"\x5Er"],"Z"=>[77,"\x5Eu"],"\x5B"=>[18,"\x5Eu"],"\x5C"=>[0,"\x5E\x3A"],"\x5D"=>[0,"\x5EB"],"\x5E"=>[0,"\x5EC"],"_"=>[0,"\x5ED"],"\x60"=>[0,"\x5EE"],"a"=>[0,"\x5EF"],"b"=>[0,"\x5EG"],"c"=>[0,"\x5EH"],"d"=>[0,"\x5EI"],"e"=>[0,"\x5EJ"],"f"=>[0,"\x5EK"],"g"=>[0,"\x5EL"],"h"=>[0,"\x5EM"],"i"=>[0,"\x5EN"],"j"=>[0,"\x5EO"],"k"=>[0,"\x5EP"],"l"=>[0,"\x5EQ"],"m"=>[0,"\x5ER"],"n"=>[0,"\x5ES"],"o"=>[0,"\x5ET"],"p"=>[0,"\x5EU"],"q"=>[0,"\x5EV"],"r"=>[0,"\x5EW"],"s"=>[0,"\x5EY"],"t"=>[0,"\x5Ej"],"u"=>[0,"\x5Ek"],"v"=>[0,"\x5Eq"],"w"=>[0,"\x5Ev"],"x"=>[0,"\x5Ew"],"y"=>[0,"\x5Ex"],"z"=>[0,"\x5Ey"],"\x7B"=>[0,"\x5Ez"],"\x7C"=>[82,"\x5E"],"\x7D"=>[87,"\x5E"],"~"=>[130,"\x5E"],"\x7F"=>[9,"\x5E"],"\x80"=>[94,"\x7D0"],"\x81"=>[76,"\x7D0"],"\x82"=>[104,"\x7D0"],"\x83"=>[16,"\x7D0"],"\x84"=>[94,"\x7D1"],"\x85"=>[76,"\x7D1"],"\x86"=>[104,"\x7D1"],"\x87"=>[16,"\x7D1"],"\x88"=>[94,"\x7D2"],"\x89"=>[76,"\x7D2"],"\x8A"=>[104,"\x7D2"],"\x8B"=>[16,"\x7D2"],"\x8C"=>[94,"\x7Da"],"\x8D"=>[76,"\x7Da"],"\x8E"=>[104,"\x7Da"],"\x8F"=>[16,"\x7Da"],"\x90"=>[94,"\x7Dc"],"\x91"=>[76,"\x7Dc"],"\x92"=>[104,"\x7Dc"],"\x93"=>[16,"\x7Dc"],"\x94"=>[94,"\x7De"],"\x95"=>[76,"\x7De"],"\x96"=>[104,"\x7De"],"\x97"=>[16,"\x7De"],"\x98"=>[94,"\x7Di"],"\x99"=>[76,"\x7Di"],"\x9A"=>[104,"\x7Di"],"\x9B"=>[16,"\x7Di"],"\x9C"=>[94,"\x7Do"],"\x9D"=>[76,"\x7Do"],"\x9E"=>[104,"\x7Do"],"\x9F"=>[16,"\x7Do"],"\xA0"=>[94,"\x7Ds"],"\xA1"=>[76,"\x7Ds"],"\xA2"=>[104,"\x7Ds"],"\xA3"=>[16,"\x7Ds"],"\xA4"=>[94,"\x7Dt"],"\xA5"=>[76,"\x7Dt"],"\xA6"=>[104,"\x7Dt"],"\xA7"=>[16,"\x7Dt"],"\xA8"=>[77,"\x7D\x20"],"\xA9"=>[18,"\x7D\x20"],"\xAA"=>[77,"\x7D\x25"],"\xAB"=>[18,"\x7D\x25"],"\xAC"=>[77,"\x7D-"],"\xAD"=>[18,"\x7D-"],"\xAE"=>[77,"\x7D."],"\xAF"=>[18,"\x7D."],"\xB0"=>[77,"\x7D\x2F"],"\xB1"=>[18,"\x7D\x2F"],"\xB2"=>[77,"\x7D3"],"\xB3"=>[18,"\x7D3"],"\xB4"=>[77,"\x7D4"],"\xB5"=>[18,"\x7D4"],"\xB6"=>[77,"\x7D5"],"\xB7"=>[18,"\x7D5"],"\xB8"=>[77,"\x7D6"],"\xB9"=>[18,"\x7D6"],"\xBA"=>[77,"\x7D7"],"\xBB"=>[18,"\x7D7"],"\xBC"=>[77,"\x7D8"],"\xBD"=>[18,"\x7D8"],"\xBE"=>[77,"\x7D9"],"\xBF"=>[18,"\x7D9"],"\xC0"=>[77,"\x7D\x3D"],"\xC1"=>[18,"\x7D\x3D"],"\xC2"=>[77,"\x7DA"],"\xC3"=>[18,"\x7DA"],"\xC4"=>[77,"\x7D_"],"\xC5"=>[18,"\x7D_"],"\xC6"=>[77,"\x7Db"],"\xC7"=>[18,"\x7Db"],"\xC8"=>[77,"\x7Dd"],"\xC9"=>[18,"\x7Dd"],"\xCA"=>[77,"\x7Df"],"\xCB"=>[18,"\x7Df"],"\xCC"=>[77,"\x7Dg"],"\xCD"=>[18,"\x7Dg"],"\xCE"=>[77,"\x7Dh"],"\xCF"=>[18,"\x7Dh"],"\xD0"=>[77,"\x7Dl"],"\xD1"=>[18,"\x7Dl"],"\xD2"=>[77,"\x7Dm"],"\xD3"=>[18,"\x7Dm"],"\xD4"=>[77,"\x7Dn"],"\xD5"=>[18,"\x7Dn"],"\xD6"=>[77,"\x7Dp"],"\xD7"=>[18,"\x7Dp"],"\xD8"=>[77,"\x7Dr"],"\xD9"=>[18,"\x7Dr"],"\xDA"=>[77,"\x7Du"],"\xDB"=>[18,"\x7Du"],"\xDC"=>[0,"\x7D\x3A"],"\xDD"=>[0,"\x7DB"],"\xDE"=>[0,"\x7DC"],"\xDF"=>[0,"\x7DD"],"\xE0"=>[0,"\x7DE"],"\xE1"=>[0,"\x7DF"],"\xE2"=>[0,"\x7DG"],"\xE3"=>[0,"\x7DH"],"\xE4"=>[0,"\x7DI"],"\xE5"=>[0,"\x7DJ"],"\xE6"=>[0,"\x7DK"],"\xE7"=>[0,"\x7DL"],"\xE8"=>[0,"\x7DM"],"\xE9"=>[0,"\x7DN"],"\xEA"=>[0,"\x7DO"],"\xEB"=>[0,"\x7DP"],"\xEC"=>[0,"\x7DQ"],"\xED"=>[0,"\x7DR"],"\xEE"=>[0,"\x7DS"],"\xEF"=>[0,"\x7DT"],"\xF0"=>[0,"\x7DU"],"\xF1"=>[0,"\x7DV"],"\xF2"=>[0,"\x7DW"],"\xF3"=>[0,"\x7DY"],"\xF4"=>[0,"\x7Dj"],"\xF5"=>[0,"\x7Dk"],"\xF6"=>[0,"\x7Dq"],"\xF7"=>[0,"\x7Dv"],"\xF8"=>[0,"\x7Dw"],"\xF9"=>[0,"\x7Dx"],"\xFA"=>[0,"\x7Dy"],"\xFB"=>[0,"\x7Dz"],"\xFC"=>[82,"\x7D"],"\xFD"=>[87,"\x7D"],"\xFE"=>[130,"\x7D"],"\xFF"=>[9,"\x7D"],],["\x00"=>[94,"_0"],"\x01"=>[76,"_0"],"\x02"=>[104,"_0"],"\x03"=>[16,"_0"],"\x04"=>[94,"_1"],"\x05"=>[76,"_1"],"\x06"=>[104,"_1"],"\x07"=>[16,"_1"],"\x08"=>[94,"_2"],"\x09"=>[76,"_2"],"\x0A"=>[104,"_2"],"\x0B"=>[16,"_2"],"\x0C"=>[94,"_a"],"\x0D"=>[76,"_a"],"\x0E"=>[104,"_a"],"\x0F"=>[16,"_a"],"\x10"=>[94,"_c"],"\x11"=>[76,"_c"],"\x12"=>[104,"_c"],"\x13"=>[16,"_c"],"\x14"=>[94,"_e"],"\x15"=>[76,"_e"],"\x16"=>[104,"_e"],"\x17"=>[16,"_e"],"\x18"=>[94,"_i"],"\x19"=>[76,"_i"],"\x1A"=>[104,"_i"],"\x1B"=>[16,"_i"],"\x1C"=>[94,"_o"],"\x1D"=>[76,"_o"],"\x1E"=>[104,"_o"],"\x1F"=>[16,"_o"],"\x20"=>[94,"_s"],"\x21"=>[76,"_s"],"\x22"=>[104,"_s"],"\x23"=>[16,"_s"],"\x24"=>[94,"_t"],"\x25"=>[76,"_t"],"\x26"=>[104,"_t"],"\x27"=>[16,"_t"],"\x28"=>[77,"_\x20"],"\x29"=>[18,"_\x20"],"\x2A"=>[77,"_\x25"],"\x2B"=>[18,"_\x25"],"\x2C"=>[77,"_-"],"-"=>[18,"_-"],"."=>[77,"_."],"\x2F"=>[18,"_."],[77,"_\x2F"],[18,"_\x2F"],[77,"_3"],[18,"_3"],[77,"_4"],[18,"_4"],[77,"_5"],[18,"_5"],[77,"_6"],[18,"_6"],"\x3A"=>[77,"_7"],"\x3B"=>[18,"_7"],"\x3C"=>[77,"_8"],"\x3D"=>[18,"_8"],"\x3E"=>[77,"_9"],"\x3F"=>[18,"_9"],"\x40"=>[77,"_\x3D"],"A"=>[18,"_\x3D"],"B"=>[77,"_A"],"C"=>[18,"_A"],"D"=>[77,"__"],"E"=>[18,"__"],"F"=>[77,"_b"],"G"=>[18,"_b"],"H"=>[77,"_d"],"I"=>[18,"_d"],"J"=>[77,"_f"],"K"=>[18,"_f"],"L"=>[77,"_g"],"M"=>[18,"_g"],"N"=>[77,"_h"],"O"=>[18,"_h"],"P"=>[77,"_l"],"Q"=>[18,"_l"],"R"=>[77,"_m"],"S"=>[18,"_m"],"T"=>[77,"_n"],"U"=>[18,"_n"],"V"=>[77,"_p"],"W"=>[18,"_p"],"X"=>[77,"_r"],"Y"=>[18,"_r"],"Z"=>[77,"_u"],"\x5B"=>[18,"_u"],"\x5C"=>[0,"_\x3A"],"\x5D"=>[0,"_B"],"\x5E"=>[0,"_C"],"_"=>[0,"_D"],"\x60"=>[0,"_E"],"a"=>[0,"_F"],"b"=>[0,"_G"],"c"=>[0,"_H"],"d"=>[0,"_I"],"e"=>[0,"_J"],"f"=>[0,"_K"],"g"=>[0,"_L"],"h"=>[0,"_M"],"i"=>[0,"_N"],"j"=>[0,"_O"],"k"=>[0,"_P"],"l"=>[0,"_Q"],"m"=>[0,"_R"],"n"=>[0,"_S"],"o"=>[0,"_T"],"p"=>[0,"_U"],"q"=>[0,"_V"],"r"=>[0,"_W"],"s"=>[0,"_Y"],"t"=>[0,"_j"],"u"=>[0,"_k"],"v"=>[0,"_q"],"w"=>[0,"_v"],"x"=>[0,"_w"],"y"=>[0,"_x"],"z"=>[0,"_y"],"\x7B"=>[0,"_z"],"\x7C"=>[82,"_"],"\x7D"=>[87,"_"],"~"=>[130,"_"],"\x7F"=>[9,"_"],"\x80"=>[94,"b0"],"\x81"=>[76,"b0"],"\x82"=>[104,"b0"],"\x83"=>[16,"b0"],"\x84"=>[94,"b1"],"\x85"=>[76,"b1"],"\x86"=>[104,"b1"],"\x87"=>[16,"b1"],"\x88"=>[94,"b2"],"\x89"=>[76,"b2"],"\x8A"=>[104,"b2"],"\x8B"=>[16,"b2"],"\x8C"=>[94,"ba"],"\x8D"=>[76,"ba"],"\x8E"=>[104,"ba"],"\x8F"=>[16,"ba"],"\x90"=>[94,"bc"],"\x91"=>[76,"bc"],"\x92"=>[104,"bc"],"\x93"=>[16,"bc"],"\x94"=>[94,"be"],"\x95"=>[76,"be"],"\x96"=>[104,"be"],"\x97"=>[16,"be"],"\x98"=>[94,"bi"],"\x99"=>[76,"bi"],"\x9A"=>[104,"bi"],"\x9B"=>[16,"bi"],"\x9C"=>[94,"bo"],"\x9D"=>[76,"bo"],"\x9E"=>[104,"bo"],"\x9F"=>[16,"bo"],"\xA0"=>[94,"bs"],"\xA1"=>[76,"bs"],"\xA2"=>[104,"bs"],"\xA3"=>[16,"bs"],"\xA4"=>[94,"bt"],"\xA5"=>[76,"bt"],"\xA6"=>[104,"bt"],"\xA7"=>[16,"bt"],"\xA8"=>[77,"b\x20"],"\xA9"=>[18,"b\x20"],"\xAA"=>[77,"b\x25"],"\xAB"=>[18,"b\x25"],"\xAC"=>[77,"b-"],"\xAD"=>[18,"b-"],"\xAE"=>[77,"b."],"\xAF"=>[18,"b."],"\xB0"=>[77,"b\x2F"],"\xB1"=>[18,"b\x2F"],"\xB2"=>[77,"b3"],"\xB3"=>[18,"b3"],"\xB4"=>[77,"b4"],"\xB5"=>[18,"b4"],"\xB6"=>[77,"b5"],"\xB7"=>[18,"b5"],"\xB8"=>[77,"b6"],"\xB9"=>[18,"b6"],"\xBA"=>[77,"b7"],"\xBB"=>[18,"b7"],"\xBC"=>[77,"b8"],"\xBD"=>[18,"b8"],"\xBE"=>[77,"b9"],"\xBF"=>[18,"b9"],"\xC0"=>[77,"b\x3D"],"\xC1"=>[18,"b\x3D"],"\xC2"=>[77,"bA"],"\xC3"=>[18,"bA"],"\xC4"=>[77,"b_"],"\xC5"=>[18,"b_"],"\xC6"=>[77,"bb"],"\xC7"=>[18,"bb"],"\xC8"=>[77,"bd"],"\xC9"=>[18,"bd"],"\xCA"=>[77,"bf"],"\xCB"=>[18,"bf"],"\xCC"=>[77,"bg"],"\xCD"=>[18,"bg"],"\xCE"=>[77,"bh"],"\xCF"=>[18,"bh"],"\xD0"=>[77,"bl"],"\xD1"=>[18,"bl"],"\xD2"=>[77,"bm"],"\xD3"=>[18,"bm"],"\xD4"=>[77,"bn"],"\xD5"=>[18,"bn"],"\xD6"=>[77,"bp"],"\xD7"=>[18,"bp"],"\xD8"=>[77,"br"],"\xD9"=>[18,"br"],"\xDA"=>[77,"bu"],"\xDB"=>[18,"bu"],"\xDC"=>[0,"b\x3A"],"\xDD"=>[0,"bB"],"\xDE"=>[0,"bC"],"\xDF"=>[0,"bD"],"\xE0"=>[0,"bE"],"\xE1"=>[0,"bF"],"\xE2"=>[0,"bG"],"\xE3"=>[0,"bH"],"\xE4"=>[0,"bI"],"\xE5"=>[0,"bJ"],"\xE6"=>[0,"bK"],"\xE7"=>[0,"bL"],"\xE8"=>[0,"bM"],"\xE9"=>[0,"bN"],"\xEA"=>[0,"bO"],"\xEB"=>[0,"bP"],"\xEC"=>[0,"bQ"],"\xED"=>[0,"bR"],"\xEE"=>[0,"bS"],"\xEF"=>[0,"bT"],"\xF0"=>[0,"bU"],"\xF1"=>[0,"bV"],"\xF2"=>[0,"bW"],"\xF3"=>[0,"bY"],"\xF4"=>[0,"bj"],"\xF5"=>[0,"bk"],"\xF6"=>[0,"bq"],"\xF7"=>[0,"bv"],"\xF8"=>[0,"bw"],"\xF9"=>[0,"bx"],"\xFA"=>[0,"by"],"\xFB"=>[0,"bz"],"\xFC"=>[82,"b"],"\xFD"=>[87,"b"],"\xFE"=>[130,"b"],"\xFF"=>[9,"b"],],["\x00"=>[94,"c0"],"\x01"=>[76,"c0"],"\x02"=>[104,"c0"],"\x03"=>[16,"c0"],"\x04"=>[94,"c1"],"\x05"=>[76,"c1"],"\x06"=>[104,"c1"],"\x07"=>[16,"c1"],"\x08"=>[94,"c2"],"\x09"=>[76,"c2"],"\x0A"=>[104,"c2"],"\x0B"=>[16,"c2"],"\x0C"=>[94,"ca"],"\x0D"=>[76,"ca"],"\x0E"=>[104,"ca"],"\x0F"=>[16,"ca"],"\x10"=>[94,"cc"],"\x11"=>[76,"cc"],"\x12"=>[104,"cc"],"\x13"=>[16,"cc"],"\x14"=>[94,"ce"],"\x15"=>[76,"ce"],"\x16"=>[104,"ce"],"\x17"=>[16,"ce"],"\x18"=>[94,"ci"],"\x19"=>[76,"ci"],"\x1A"=>[104,"ci"],"\x1B"=>[16,"ci"],"\x1C"=>[94,"co"],"\x1D"=>[76,"co"],"\x1E"=>[104,"co"],"\x1F"=>[16,"co"],"\x20"=>[94,"cs"],"\x21"=>[76,"cs"],"\x22"=>[104,"cs"],"\x23"=>[16,"cs"],"\x24"=>[94,"ct"],"\x25"=>[76,"ct"],"\x26"=>[104,"ct"],"\x27"=>[16,"ct"],"\x28"=>[77,"c\x20"],"\x29"=>[18,"c\x20"],"\x2A"=>[77,"c\x25"],"\x2B"=>[18,"c\x25"],"\x2C"=>[77,"c-"],"-"=>[18,"c-"],"."=>[77,"c."],"\x2F"=>[18,"c."],[77,"c\x2F"],[18,"c\x2F"],[77,"c3"],[18,"c3"],[77,"c4"],[18,"c4"],[77,"c5"],[18,"c5"],[77,"c6"],[18,"c6"],"\x3A"=>[77,"c7"],"\x3B"=>[18,"c7"],"\x3C"=>[77,"c8"],"\x3D"=>[18,"c8"],"\x3E"=>[77,"c9"],"\x3F"=>[18,"c9"],"\x40"=>[77,"c\x3D"],"A"=>[18,"c\x3D"],"B"=>[77,"cA"],"C"=>[18,"cA"],"D"=>[77,"c_"],"E"=>[18,"c_"],"F"=>[77,"cb"],"G"=>[18,"cb"],"H"=>[77,"cd"],"I"=>[18,"cd"],"J"=>[77,"cf"],"K"=>[18,"cf"],"L"=>[77,"cg"],"M"=>[18,"cg"],"N"=>[77,"ch"],"O"=>[18,"ch"],"P"=>[77,"cl"],"Q"=>[18,"cl"],"R"=>[77,"cm"],"S"=>[18,"cm"],"T"=>[77,"cn"],"U"=>[18,"cn"],"V"=>[77,"cp"],"W"=>[18,"cp"],"X"=>[77,"cr"],"Y"=>[18,"cr"],"Z"=>[77,"cu"],"\x5B"=>[18,"cu"],"\x5C"=>[0,"c\x3A"],"\x5D"=>[0,"cB"],"\x5E"=>[0,"cC"],"_"=>[0,"cD"],"\x60"=>[0,"cE"],"a"=>[0,"cF"],"b"=>[0,"cG"],"c"=>[0,"cH"],"d"=>[0,"cI"],"e"=>[0,"cJ"],"f"=>[0,"cK"],"g"=>[0,"cL"],"h"=>[0,"cM"],"i"=>[0,"cN"],"j"=>[0,"cO"],"k"=>[0,"cP"],"l"=>[0,"cQ"],"m"=>[0,"cR"],"n"=>[0,"cS"],"o"=>[0,"cT"],"p"=>[0,"cU"],"q"=>[0,"cV"],"r"=>[0,"cW"],"s"=>[0,"cY"],"t"=>[0,"cj"],"u"=>[0,"ck"],"v"=>[0,"cq"],"w"=>[0,"cv"],"x"=>[0,"cw"],"y"=>[0,"cx"],"z"=>[0,"cy"],"\x7B"=>[0,"cz"],"\x7C"=>[82,"c"],"\x7D"=>[87,"c"],"~"=>[130,"c"],"\x7F"=>[9,"c"],"\x80"=>[94,"e0"],"\x81"=>[76,"e0"],"\x82"=>[104,"e0"],"\x83"=>[16,"e0"],"\x84"=>[94,"e1"],"\x85"=>[76,"e1"],"\x86"=>[104,"e1"],"\x87"=>[16,"e1"],"\x88"=>[94,"e2"],"\x89"=>[76,"e2"],"\x8A"=>[104,"e2"],"\x8B"=>[16,"e2"],"\x8C"=>[94,"ea"],"\x8D"=>[76,"ea"],"\x8E"=>[104,"ea"],"\x8F"=>[16,"ea"],"\x90"=>[94,"ec"],"\x91"=>[76,"ec"],"\x92"=>[104,"ec"],"\x93"=>[16,"ec"],"\x94"=>[94,"ee"],"\x95"=>[76,"ee"],"\x96"=>[104,"ee"],"\x97"=>[16,"ee"],"\x98"=>[94,"ei"],"\x99"=>[76,"ei"],"\x9A"=>[104,"ei"],"\x9B"=>[16,"ei"],"\x9C"=>[94,"eo"],"\x9D"=>[76,"eo"],"\x9E"=>[104,"eo"],"\x9F"=>[16,"eo"],"\xA0"=>[94,"es"],"\xA1"=>[76,"es"],"\xA2"=>[104,"es"],"\xA3"=>[16,"es"],"\xA4"=>[94,"et"],"\xA5"=>[76,"et"],"\xA6"=>[104,"et"],"\xA7"=>[16,"et"],"\xA8"=>[77,"e\x20"],"\xA9"=>[18,"e\x20"],"\xAA"=>[77,"e\x25"],"\xAB"=>[18,"e\x25"],"\xAC"=>[77,"e-"],"\xAD"=>[18,"e-"],"\xAE"=>[77,"e."],"\xAF"=>[18,"e."],"\xB0"=>[77,"e\x2F"],"\xB1"=>[18,"e\x2F"],"\xB2"=>[77,"e3"],"\xB3"=>[18,"e3"],"\xB4"=>[77,"e4"],"\xB5"=>[18,"e4"],"\xB6"=>[77,"e5"],"\xB7"=>[18,"e5"],"\xB8"=>[77,"e6"],"\xB9"=>[18,"e6"],"\xBA"=>[77,"e7"],"\xBB"=>[18,"e7"],"\xBC"=>[77,"e8"],"\xBD"=>[18,"e8"],"\xBE"=>[77,"e9"],"\xBF"=>[18,"e9"],"\xC0"=>[77,"e\x3D"],"\xC1"=>[18,"e\x3D"],"\xC2"=>[77,"eA"],"\xC3"=>[18,"eA"],"\xC4"=>[77,"e_"],"\xC5"=>[18,"e_"],"\xC6"=>[77,"eb"],"\xC7"=>[18,"eb"],"\xC8"=>[77,"ed"],"\xC9"=>[18,"ed"],"\xCA"=>[77,"ef"],"\xCB"=>[18,"ef"],"\xCC"=>[77,"eg"],"\xCD"=>[18,"eg"],"\xCE"=>[77,"eh"],"\xCF"=>[18,"eh"],"\xD0"=>[77,"el"],"\xD1"=>[18,"el"],"\xD2"=>[77,"em"],"\xD3"=>[18,"em"],"\xD4"=>[77,"en"],"\xD5"=>[18,"en"],"\xD6"=>[77,"ep"],"\xD7"=>[18,"ep"],"\xD8"=>[77,"er"],"\xD9"=>[18,"er"],"\xDA"=>[77,"eu"],"\xDB"=>[18,"eu"],"\xDC"=>[0,"e\x3A"],"\xDD"=>[0,"eB"],"\xDE"=>[0,"eC"],"\xDF"=>[0,"eD"],"\xE0"=>[0,"eE"],"\xE1"=>[0,"eF"],"\xE2"=>[0,"eG"],"\xE3"=>[0,"eH"],"\xE4"=>[0,"eI"],"\xE5"=>[0,"eJ"],"\xE6"=>[0,"eK"],"\xE7"=>[0,"eL"],"\xE8"=>[0,"eM"],"\xE9"=>[0,"eN"],"\xEA"=>[0,"eO"],"\xEB"=>[0,"eP"],"\xEC"=>[0,"eQ"],"\xED"=>[0,"eR"],"\xEE"=>[0,"eS"],"\xEF"=>[0,"eT"],"\xF0"=>[0,"eU"],"\xF1"=>[0,"eV"],"\xF2"=>[0,"eW"],"\xF3"=>[0,"eY"],"\xF4"=>[0,"ej"],"\xF5"=>[0,"ek"],"\xF6"=>[0,"eq"],"\xF7"=>[0,"ev"],"\xF8"=>[0,"ew"],"\xF9"=>[0,"ex"],"\xFA"=>[0,"ey"],"\xFB"=>[0,"ez"],"\xFC"=>[82,"e"],"\xFD"=>[87,"e"],"\xFE"=>[130,"e"],"\xFF"=>[9,"e"],],["\x00"=>[77,"c0"],"\x01"=>[18,"c0"],"\x02"=>[77,"c1"],"\x03"=>[18,"c1"],"\x04"=>[77,"c2"],"\x05"=>[18,"c2"],"\x06"=>[77,"ca"],"\x07"=>[18,"ca"],"\x08"=>[77,"cc"],"\x09"=>[18,"cc"],"\x0A"=>[77,"ce"],"\x0B"=>[18,"ce"],"\x0C"=>[77,"ci"],"\x0D"=>[18,"ci"],"\x0E"=>[77,"co"],"\x0F"=>[18,"co"],"\x10"=>[77,"cs"],"\x11"=>[18,"cs"],"\x12"=>[77,"ct"],"\x13"=>[18,"ct"],"\x14"=>[0,"c\x20"],"\x15"=>[0,"c\x25"],"\x16"=>[0,"c-"],"\x17"=>[0,"c."],"\x18"=>[0,"c\x2F"],"\x19"=>[0,"c3"],"\x1A"=>[0,"c4"],"\x1B"=>[0,"c5"],"\x1C"=>[0,"c6"],"\x1D"=>[0,"c7"],"\x1E"=>[0,"c8"],"\x1F"=>[0,"c9"],"\x20"=>[0,"c\x3D"],"\x21"=>[0,"cA"],"\x22"=>[0,"c_"],"\x23"=>[0,"cb"],"\x24"=>[0,"cd"],"\x25"=>[0,"cf"],"\x26"=>[0,"cg"],"\x27"=>[0,"ch"],"\x28"=>[0,"cl"],"\x29"=>[0,"cm"],"\x2A"=>[0,"cn"],"\x2B"=>[0,"cp"],"\x2C"=>[0,"cr"],"-"=>[0,"cu"],"."=>[100,"c"],"\x2F"=>[110,"c"],[111,"c"],[115,"c"],[116,"c"],[118,"c"],[119,"c"],[122,"c"],[123,"c"],[125,"c"],[126,"c"],[129,"c"],"\x3A"=>[143,"c"],"\x3B"=>[148,"c"],"\x3C"=>[151,"c"],"\x3D"=>[153,"c"],"\x3E"=>[83,"c"],"\x3F"=>[10,"c"],"\x40"=>[77,"e0"],"A"=>[18,"e0"],"B"=>[77,"e1"],"C"=>[18,"e1"],"D"=>[77,"e2"],"E"=>[18,"e2"],"F"=>[77,"ea"],"G"=>[18,"ea"],"H"=>[77,"ec"],"I"=>[18,"ec"],"J"=>[77,"ee"],"K"=>[18,"ee"],"L"=>[77,"ei"],"M"=>[18,"ei"],"N"=>[77,"eo"],"O"=>[18,"eo"],"P"=>[77,"es"],"Q"=>[18,"es"],"R"=>[77,"et"],"S"=>[18,"et"],"T"=>[0,"e\x20"],"U"=>[0,"e\x25"],"V"=>[0,"e-"],"W"=>[0,"e."],"X"=>[0,"e\x2F"],"Y"=>[0,"e3"],"Z"=>[0,"e4"],"\x5B"=>[0,"e5"],"\x5C"=>[0,"e6"],"\x5D"=>[0,"e7"],"\x5E"=>[0,"e8"],"_"=>[0,"e9"],"\x60"=>[0,"e\x3D"],"a"=>[0,"eA"],"b"=>[0,"e_"],"c"=>[0,"eb"],"d"=>[0,"ed"],"e"=>[0,"ef"],"f"=>[0,"eg"],"g"=>[0,"eh"],"h"=>[0,"el"],"i"=>[0,"em"],"j"=>[0,"en"],"k"=>[0,"ep"],"l"=>[0,"er"],"m"=>[0,"eu"],"n"=>[100,"e"],"o"=>[110,"e"],"p"=>[111,"e"],"q"=>[115,"e"],"r"=>[116,"e"],"s"=>[118,"e"],"t"=>[119,"e"],"u"=>[122,"e"],"v"=>[123,"e"],"w"=>[125,"e"],"x"=>[126,"e"],"y"=>[129,"e"],"z"=>[143,"e"],"\x7B"=>[148,"e"],"\x7C"=>[151,"e"],"\x7D"=>[153,"e"],"~"=>[83,"e"],"\x7F"=>[10,"e"],"\x80"=>[77,"i0"],"\x81"=>[18,"i0"],"\x82"=>[77,"i1"],"\x83"=>[18,"i1"],"\x84"=>[77,"i2"],"\x85"=>[18,"i2"],"\x86"=>[77,"ia"],"\x87"=>[18,"ia"],"\x88"=>[77,"ic"],"\x89"=>[18,"ic"],"\x8A"=>[77,"ie"],"\x8B"=>[18,"ie"],"\x8C"=>[77,"ii"],"\x8D"=>[18,"ii"],"\x8E"=>[77,"io"],"\x8F"=>[18,"io"],"\x90"=>[77,"is"],"\x91"=>[18,"is"],"\x92"=>[77,"it"],"\x93"=>[18,"it"],"\x94"=>[0,"i\x20"],"\x95"=>[0,"i\x25"],"\x96"=>[0,"i-"],"\x97"=>[0,"i."],"\x98"=>[0,"i\x2F"],"\x99"=>[0,"i3"],"\x9A"=>[0,"i4"],"\x9B"=>[0,"i5"],"\x9C"=>[0,"i6"],"\x9D"=>[0,"i7"],"\x9E"=>[0,"i8"],"\x9F"=>[0,"i9"],"\xA0"=>[0,"i\x3D"],"\xA1"=>[0,"iA"],"\xA2"=>[0,"i_"],"\xA3"=>[0,"ib"],"\xA4"=>[0,"id"],"\xA5"=>[0,"if"],"\xA6"=>[0,"ig"],"\xA7"=>[0,"ih"],"\xA8"=>[0,"il"],"\xA9"=>[0,"im"],"\xAA"=>[0,"in"],"\xAB"=>[0,"ip"],"\xAC"=>[0,"ir"],"\xAD"=>[0,"iu"],"\xAE"=>[100,"i"],"\xAF"=>[110,"i"],"\xB0"=>[111,"i"],"\xB1"=>[115,"i"],"\xB2"=>[116,"i"],"\xB3"=>[118,"i"],"\xB4"=>[119,"i"],"\xB5"=>[122,"i"],"\xB6"=>[123,"i"],"\xB7"=>[125,"i"],"\xB8"=>[126,"i"],"\xB9"=>[129,"i"],"\xBA"=>[143,"i"],"\xBB"=>[148,"i"],"\xBC"=>[151,"i"],"\xBD"=>[153,"i"],"\xBE"=>[83,"i"],"\xBF"=>[10,"i"],"\xC0"=>[77,"o0"],"\xC1"=>[18,"o0"],"\xC2"=>[77,"o1"],"\xC3"=>[18,"o1"],"\xC4"=>[77,"o2"],"\xC5"=>[18,"o2"],"\xC6"=>[77,"oa"],"\xC7"=>[18,"oa"],"\xC8"=>[77,"oc"],"\xC9"=>[18,"oc"],"\xCA"=>[77,"oe"],"\xCB"=>[18,"oe"],"\xCC"=>[77,"oi"],"\xCD"=>[18,"oi"],"\xCE"=>[77,"oo"],"\xCF"=>[18,"oo"],"\xD0"=>[77,"os"],"\xD1"=>[18,"os"],"\xD2"=>[77,"ot"],"\xD3"=>[18,"ot"],"\xD4"=>[0,"o\x20"],"\xD5"=>[0,"o\x25"],"\xD6"=>[0,"o-"],"\xD7"=>[0,"o."],"\xD8"=>[0,"o\x2F"],"\xD9"=>[0,"o3"],"\xDA"=>[0,"o4"],"\xDB"=>[0,"o5"],"\xDC"=>[0,"o6"],"\xDD"=>[0,"o7"],"\xDE"=>[0,"o8"],"\xDF"=>[0,"o9"],"\xE0"=>[0,"o\x3D"],"\xE1"=>[0,"oA"],"\xE2"=>[0,"o_"],"\xE3"=>[0,"ob"],"\xE4"=>[0,"od"],"\xE5"=>[0,"of"],"\xE6"=>[0,"og"],"\xE7"=>[0,"oh"],"\xE8"=>[0,"ol"],"\xE9"=>[0,"om"],"\xEA"=>[0,"on"],"\xEB"=>[0,"op"],"\xEC"=>[0,"or"],"\xED"=>[0,"ou"],"\xEE"=>[100,"o"],"\xEF"=>[110,"o"],"\xF0"=>[111,"o"],"\xF1"=>[115,"o"],"\xF2"=>[116,"o"],"\xF3"=>[118,"o"],"\xF4"=>[119,"o"],"\xF5"=>[122,"o"],"\xF6"=>[123,"o"],"\xF7"=>[125,"o"],"\xF8"=>[126,"o"],"\xF9"=>[129,"o"],"\xFA"=>[143,"o"],"\xFB"=>[148,"o"],"\xFC"=>[151,"o"],"\xFD"=>[153,"o"],"\xFE"=>[83,"o"],"\xFF"=>[10,"o"],],["\x00"=>[94,"d0"],"\x01"=>[76,"d0"],"\x02"=>[104,"d0"],"\x03"=>[16,"d0"],"\x04"=>[94,"d1"],"\x05"=>[76,"d1"],"\x06"=>[104,"d1"],"\x07"=>[16,"d1"],"\x08"=>[94,"d2"],"\x09"=>[76,"d2"],"\x0A"=>[104,"d2"],"\x0B"=>[16,"d2"],"\x0C"=>[94,"da"],"\x0D"=>[76,"da"],"\x0E"=>[104,"da"],"\x0F"=>[16,"da"],"\x10"=>[94,"dc"],"\x11"=>[76,"dc"],"\x12"=>[104,"dc"],"\x13"=>[16,"dc"],"\x14"=>[94,"de"],"\x15"=>[76,"de"],"\x16"=>[104,"de"],"\x17"=>[16,"de"],"\x18"=>[94,"di"],"\x19"=>[76,"di"],"\x1A"=>[104,"di"],"\x1B"=>[16,"di"],"\x1C"=>[94,"do"],"\x1D"=>[76,"do"],"\x1E"=>[104,"do"],"\x1F"=>[16,"do"],"\x20"=>[94,"ds"],"\x21"=>[76,"ds"],"\x22"=>[104,"ds"],"\x23"=>[16,"ds"],"\x24"=>[94,"dt"],"\x25"=>[76,"dt"],"\x26"=>[104,"dt"],"\x27"=>[16,"dt"],"\x28"=>[77,"d\x20"],"\x29"=>[18,"d\x20"],"\x2A"=>[77,"d\x25"],"\x2B"=>[18,"d\x25"],"\x2C"=>[77,"d-"],"-"=>[18,"d-"],"."=>[77,"d."],"\x2F"=>[18,"d."],[77,"d\x2F"],[18,"d\x2F"],[77,"d3"],[18,"d3"],[77,"d4"],[18,"d4"],[77,"d5"],[18,"d5"],[77,"d6"],[18,"d6"],"\x3A"=>[77,"d7"],"\x3B"=>[18,"d7"],"\x3C"=>[77,"d8"],"\x3D"=>[18,"d8"],"\x3E"=>[77,"d9"],"\x3F"=>[18,"d9"],"\x40"=>[77,"d\x3D"],"A"=>[18,"d\x3D"],"B"=>[77,"dA"],"C"=>[18,"dA"],"D"=>[77,"d_"],"E"=>[18,"d_"],"F"=>[77,"db"],"G"=>[18,"db"],"H"=>[77,"dd"],"I"=>[18,"dd"],"J"=>[77,"df"],"K"=>[18,"df"],"L"=>[77,"dg"],"M"=>[18,"dg"],"N"=>[77,"dh"],"O"=>[18,"dh"],"P"=>[77,"dl"],"Q"=>[18,"dl"],"R"=>[77,"dm"],"S"=>[18,"dm"],"T"=>[77,"dn"],"U"=>[18,"dn"],"V"=>[77,"dp"],"W"=>[18,"dp"],"X"=>[77,"dr"],"Y"=>[18,"dr"],"Z"=>[77,"du"],"\x5B"=>[18,"du"],"\x5C"=>[0,"d\x3A"],"\x5D"=>[0,"dB"],"\x5E"=>[0,"dC"],"_"=>[0,"dD"],"\x60"=>[0,"dE"],"a"=>[0,"dF"],"b"=>[0,"dG"],"c"=>[0,"dH"],"d"=>[0,"dI"],"e"=>[0,"dJ"],"f"=>[0,"dK"],"g"=>[0,"dL"],"h"=>[0,"dM"],"i"=>[0,"dN"],"j"=>[0,"dO"],"k"=>[0,"dP"],"l"=>[0,"dQ"],"m"=>[0,"dR"],"n"=>[0,"dS"],"o"=>[0,"dT"],"p"=>[0,"dU"],"q"=>[0,"dV"],"r"=>[0,"dW"],"s"=>[0,"dY"],"t"=>[0,"dj"],"u"=>[0,"dk"],"v"=>[0,"dq"],"w"=>[0,"dv"],"x"=>[0,"dw"],"y"=>[0,"dx"],"z"=>[0,"dy"],"\x7B"=>[0,"dz"],"\x7C"=>[82,"d"],"\x7D"=>[87,"d"],"~"=>[130,"d"],"\x7F"=>[9,"d"],"\x80"=>[94,"f0"],"\x81"=>[76,"f0"],"\x82"=>[104,"f0"],"\x83"=>[16,"f0"],"\x84"=>[94,"f1"],"\x85"=>[76,"f1"],"\x86"=>[104,"f1"],"\x87"=>[16,"f1"],"\x88"=>[94,"f2"],"\x89"=>[76,"f2"],"\x8A"=>[104,"f2"],"\x8B"=>[16,"f2"],"\x8C"=>[94,"fa"],"\x8D"=>[76,"fa"],"\x8E"=>[104,"fa"],"\x8F"=>[16,"fa"],"\x90"=>[94,"fc"],"\x91"=>[76,"fc"],"\x92"=>[104,"fc"],"\x93"=>[16,"fc"],"\x94"=>[94,"fe"],"\x95"=>[76,"fe"],"\x96"=>[104,"fe"],"\x97"=>[16,"fe"],"\x98"=>[94,"fi"],"\x99"=>[76,"fi"],"\x9A"=>[104,"fi"],"\x9B"=>[16,"fi"],"\x9C"=>[94,"fo"],"\x9D"=>[76,"fo"],"\x9E"=>[104,"fo"],"\x9F"=>[16,"fo"],"\xA0"=>[94,"fs"],"\xA1"=>[76,"fs"],"\xA2"=>[104,"fs"],"\xA3"=>[16,"fs"],"\xA4"=>[94,"ft"],"\xA5"=>[76,"ft"],"\xA6"=>[104,"ft"],"\xA7"=>[16,"ft"],"\xA8"=>[77,"f\x20"],"\xA9"=>[18,"f\x20"],"\xAA"=>[77,"f\x25"],"\xAB"=>[18,"f\x25"],"\xAC"=>[77,"f-"],"\xAD"=>[18,"f-"],"\xAE"=>[77,"f."],"\xAF"=>[18,"f."],"\xB0"=>[77,"f\x2F"],"\xB1"=>[18,"f\x2F"],"\xB2"=>[77,"f3"],"\xB3"=>[18,"f3"],"\xB4"=>[77,"f4"],"\xB5"=>[18,"f4"],"\xB6"=>[77,"f5"],"\xB7"=>[18,"f5"],"\xB8"=>[77,"f6"],"\xB9"=>[18,"f6"],"\xBA"=>[77,"f7"],"\xBB"=>[18,"f7"],"\xBC"=>[77,"f8"],"\xBD"=>[18,"f8"],"\xBE"=>[77,"f9"],"\xBF"=>[18,"f9"],"\xC0"=>[77,"f\x3D"],"\xC1"=>[18,"f\x3D"],"\xC2"=>[77,"fA"],"\xC3"=>[18,"fA"],"\xC4"=>[77,"f_"],"\xC5"=>[18,"f_"],"\xC6"=>[77,"fb"],"\xC7"=>[18,"fb"],"\xC8"=>[77,"fd"],"\xC9"=>[18,"fd"],"\xCA"=>[77,"ff"],"\xCB"=>[18,"ff"],"\xCC"=>[77,"fg"],"\xCD"=>[18,"fg"],"\xCE"=>[77,"fh"],"\xCF"=>[18,"fh"],"\xD0"=>[77,"fl"],"\xD1"=>[18,"fl"],"\xD2"=>[77,"fm"],"\xD3"=>[18,"fm"],"\xD4"=>[77,"fn"],"\xD5"=>[18,"fn"],"\xD6"=>[77,"fp"],"\xD7"=>[18,"fp"],"\xD8"=>[77,"fr"],"\xD9"=>[18,"fr"],"\xDA"=>[77,"fu"],"\xDB"=>[18,"fu"],"\xDC"=>[0,"f\x3A"],"\xDD"=>[0,"fB"],"\xDE"=>[0,"fC"],"\xDF"=>[0,"fD"],"\xE0"=>[0,"fE"],"\xE1"=>[0,"fF"],"\xE2"=>[0,"fG"],"\xE3"=>[0,"fH"],"\xE4"=>[0,"fI"],"\xE5"=>[0,"fJ"],"\xE6"=>[0,"fK"],"\xE7"=>[0,"fL"],"\xE8"=>[0,"fM"],"\xE9"=>[0,"fN"],"\xEA"=>[0,"fO"],"\xEB"=>[0,"fP"],"\xEC"=>[0,"fQ"],"\xED"=>[0,"fR"],"\xEE"=>[0,"fS"],"\xEF"=>[0,"fT"],"\xF0"=>[0,"fU"],"\xF1"=>[0,"fV"],"\xF2"=>[0,"fW"],"\xF3"=>[0,"fY"],"\xF4"=>[0,"fj"],"\xF5"=>[0,"fk"],"\xF6"=>[0,"fq"],"\xF7"=>[0,"fv"],"\xF8"=>[0,"fw"],"\xF9"=>[0,"fx"],"\xFA"=>[0,"fy"],"\xFB"=>[0,"fz"],"\xFC"=>[82,"f"],"\xFD"=>[87,"f"],"\xFE"=>[130,"f"],"\xFF"=>[9,"f"],],["\x00"=>[77,"d0"],"\x01"=>[18,"d0"],"\x02"=>[77,"d1"],"\x03"=>[18,"d1"],"\x04"=>[77,"d2"],"\x05"=>[18,"d2"],"\x06"=>[77,"da"],"\x07"=>[18,"da"],"\x08"=>[77,"dc"],"\x09"=>[18,"dc"],"\x0A"=>[77,"de"],"\x0B"=>[18,"de"],"\x0C"=>[77,"di"],"\x0D"=>[18,"di"],"\x0E"=>[77,"do"],"\x0F"=>[18,"do"],"\x10"=>[77,"ds"],"\x11"=>[18,"ds"],"\x12"=>[77,"dt"],"\x13"=>[18,"dt"],"\x14"=>[0,"d\x20"],"\x15"=>[0,"d\x25"],"\x16"=>[0,"d-"],"\x17"=>[0,"d."],"\x18"=>[0,"d\x2F"],"\x19"=>[0,"d3"],"\x1A"=>[0,"d4"],"\x1B"=>[0,"d5"],"\x1C"=>[0,"d6"],"\x1D"=>[0,"d7"],"\x1E"=>[0,"d8"],"\x1F"=>[0,"d9"],"\x20"=>[0,"d\x3D"],"\x21"=>[0,"dA"],"\x22"=>[0,"d_"],"\x23"=>[0,"db"],"\x24"=>[0,"dd"],"\x25"=>[0,"df"],"\x26"=>[0,"dg"],"\x27"=>[0,"dh"],"\x28"=>[0,"dl"],"\x29"=>[0,"dm"],"\x2A"=>[0,"dn"],"\x2B"=>[0,"dp"],"\x2C"=>[0,"dr"],"-"=>[0,"du"],"."=>[100,"d"],"\x2F"=>[110,"d"],[111,"d"],[115,"d"],[116,"d"],[118,"d"],[119,"d"],[122,"d"],[123,"d"],[125,"d"],[126,"d"],[129,"d"],"\x3A"=>[143,"d"],"\x3B"=>[148,"d"],"\x3C"=>[151,"d"],"\x3D"=>[153,"d"],"\x3E"=>[83,"d"],"\x3F"=>[10,"d"],"\x40"=>[77,"f0"],"A"=>[18,"f0"],"B"=>[77,"f1"],"C"=>[18,"f1"],"D"=>[77,"f2"],"E"=>[18,"f2"],"F"=>[77,"fa"],"G"=>[18,"fa"],"H"=>[77,"fc"],"I"=>[18,"fc"],"J"=>[77,"fe"],"K"=>[18,"fe"],"L"=>[77,"fi"],"M"=>[18,"fi"],"N"=>[77,"fo"],"O"=>[18,"fo"],"P"=>[77,"fs"],"Q"=>[18,"fs"],"R"=>[77,"ft"],"S"=>[18,"ft"],"T"=>[0,"f\x20"],"U"=>[0,"f\x25"],"V"=>[0,"f-"],"W"=>[0,"f."],"X"=>[0,"f\x2F"],"Y"=>[0,"f3"],"Z"=>[0,"f4"],"\x5B"=>[0,"f5"],"\x5C"=>[0,"f6"],"\x5D"=>[0,"f7"],"\x5E"=>[0,"f8"],"_"=>[0,"f9"],"\x60"=>[0,"f\x3D"],"a"=>[0,"fA"],"b"=>[0,"f_"],"c"=>[0,"fb"],"d"=>[0,"fd"],"e"=>[0,"ff"],"f"=>[0,"fg"],"g"=>[0,"fh"],"h"=>[0,"fl"],"i"=>[0,"fm"],"j"=>[0,"fn"],"k"=>[0,"fp"],"l"=>[0,"fr"],"m"=>[0,"fu"],"n"=>[100,"f"],"o"=>[110,"f"],"p"=>[111,"f"],"q"=>[115,"f"],"r"=>[116,"f"],"s"=>[118,"f"],"t"=>[119,"f"],"u"=>[122,"f"],"v"=>[123,"f"],"w"=>[125,"f"],"x"=>[126,"f"],"y"=>[129,"f"],"z"=>[143,"f"],"\x7B"=>[148,"f"],"\x7C"=>[151,"f"],"\x7D"=>[153,"f"],"~"=>[83,"f"],"\x7F"=>[10,"f"],"\x80"=>[77,"g0"],"\x81"=>[18,"g0"],"\x82"=>[77,"g1"],"\x83"=>[18,"g1"],"\x84"=>[77,"g2"],"\x85"=>[18,"g2"],"\x86"=>[77,"ga"],"\x87"=>[18,"ga"],"\x88"=>[77,"gc"],"\x89"=>[18,"gc"],"\x8A"=>[77,"ge"],"\x8B"=>[18,"ge"],"\x8C"=>[77,"gi"],"\x8D"=>[18,"gi"],"\x8E"=>[77,"go"],"\x8F"=>[18,"go"],"\x90"=>[77,"gs"],"\x91"=>[18,"gs"],"\x92"=>[77,"gt"],"\x93"=>[18,"gt"],"\x94"=>[0,"g\x20"],"\x95"=>[0,"g\x25"],"\x96"=>[0,"g-"],"\x97"=>[0,"g."],"\x98"=>[0,"g\x2F"],"\x99"=>[0,"g3"],"\x9A"=>[0,"g4"],"\x9B"=>[0,"g5"],"\x9C"=>[0,"g6"],"\x9D"=>[0,"g7"],"\x9E"=>[0,"g8"],"\x9F"=>[0,"g9"],"\xA0"=>[0,"g\x3D"],"\xA1"=>[0,"gA"],"\xA2"=>[0,"g_"],"\xA3"=>[0,"gb"],"\xA4"=>[0,"gd"],"\xA5"=>[0,"gf"],"\xA6"=>[0,"gg"],"\xA7"=>[0,"gh"],"\xA8"=>[0,"gl"],"\xA9"=>[0,"gm"],"\xAA"=>[0,"gn"],"\xAB"=>[0,"gp"],"\xAC"=>[0,"gr"],"\xAD"=>[0,"gu"],"\xAE"=>[100,"g"],"\xAF"=>[110,"g"],"\xB0"=>[111,"g"],"\xB1"=>[115,"g"],"\xB2"=>[116,"g"],"\xB3"=>[118,"g"],"\xB4"=>[119,"g"],"\xB5"=>[122,"g"],"\xB6"=>[123,"g"],"\xB7"=>[125,"g"],"\xB8"=>[126,"g"],"\xB9"=>[129,"g"],"\xBA"=>[143,"g"],"\xBB"=>[148,"g"],"\xBC"=>[151,"g"],"\xBD"=>[153,"g"],"\xBE"=>[83,"g"],"\xBF"=>[10,"g"],"\xC0"=>[77,"h0"],"\xC1"=>[18,"h0"],"\xC2"=>[77,"h1"],"\xC3"=>[18,"h1"],"\xC4"=>[77,"h2"],"\xC5"=>[18,"h2"],"\xC6"=>[77,"ha"],"\xC7"=>[18,"ha"],"\xC8"=>[77,"hc"],"\xC9"=>[18,"hc"],"\xCA"=>[77,"he"],"\xCB"=>[18,"he"],"\xCC"=>[77,"hi"],"\xCD"=>[18,"hi"],"\xCE"=>[77,"ho"],"\xCF"=>[18,"ho"],"\xD0"=>[77,"hs"],"\xD1"=>[18,"hs"],"\xD2"=>[77,"ht"],"\xD3"=>[18,"ht"],"\xD4"=>[0,"h\x20"],"\xD5"=>[0,"h\x25"],"\xD6"=>[0,"h-"],"\xD7"=>[0,"h."],"\xD8"=>[0,"h\x2F"],"\xD9"=>[0,"h3"],"\xDA"=>[0,"h4"],"\xDB"=>[0,"h5"],"\xDC"=>[0,"h6"],"\xDD"=>[0,"h7"],"\xDE"=>[0,"h8"],"\xDF"=>[0,"h9"],"\xE0"=>[0,"h\x3D"],"\xE1"=>[0,"hA"],"\xE2"=>[0,"h_"],"\xE3"=>[0,"hb"],"\xE4"=>[0,"hd"],"\xE5"=>[0,"hf"],"\xE6"=>[0,"hg"],"\xE7"=>[0,"hh"],"\xE8"=>[0,"hl"],"\xE9"=>[0,"hm"],"\xEA"=>[0,"hn"],"\xEB"=>[0,"hp"],"\xEC"=>[0,"hr"],"\xED"=>[0,"hu"],"\xEE"=>[100,"h"],"\xEF"=>[110,"h"],"\xF0"=>[111,"h"],"\xF1"=>[115,"h"],"\xF2"=>[116,"h"],"\xF3"=>[118,"h"],"\xF4"=>[119,"h"],"\xF5"=>[122,"h"],"\xF6"=>[123,"h"],"\xF7"=>[125,"h"],"\xF8"=>[126,"h"],"\xF9"=>[129,"h"],"\xFA"=>[143,"h"],"\xFB"=>[148,"h"],"\xFC"=>[151,"h"],"\xFD"=>[153,"h"],"\xFE"=>[83,"h"],"\xFF"=>[10,"h"],],["\x00"=>[94,"g0"],"\x01"=>[76,"g0"],"\x02"=>[104,"g0"],"\x03"=>[16,"g0"],"\x04"=>[94,"g1"],"\x05"=>[76,"g1"],"\x06"=>[104,"g1"],"\x07"=>[16,"g1"],"\x08"=>[94,"g2"],"\x09"=>[76,"g2"],"\x0A"=>[104,"g2"],"\x0B"=>[16,"g2"],"\x0C"=>[94,"ga"],"\x0D"=>[76,"ga"],"\x0E"=>[104,"ga"],"\x0F"=>[16,"ga"],"\x10"=>[94,"gc"],"\x11"=>[76,"gc"],"\x12"=>[104,"gc"],"\x13"=>[16,"gc"],"\x14"=>[94,"ge"],"\x15"=>[76,"ge"],"\x16"=>[104,"ge"],"\x17"=>[16,"ge"],"\x18"=>[94,"gi"],"\x19"=>[76,"gi"],"\x1A"=>[104,"gi"],"\x1B"=>[16,"gi"],"\x1C"=>[94,"go"],"\x1D"=>[76,"go"],"\x1E"=>[104,"go"],"\x1F"=>[16,"go"],"\x20"=>[94,"gs"],"\x21"=>[76,"gs"],"\x22"=>[104,"gs"],"\x23"=>[16,"gs"],"\x24"=>[94,"gt"],"\x25"=>[76,"gt"],"\x26"=>[104,"gt"],"\x27"=>[16,"gt"],"\x28"=>[77,"g\x20"],"\x29"=>[18,"g\x20"],"\x2A"=>[77,"g\x25"],"\x2B"=>[18,"g\x25"],"\x2C"=>[77,"g-"],"-"=>[18,"g-"],"."=>[77,"g."],"\x2F"=>[18,"g."],[77,"g\x2F"],[18,"g\x2F"],[77,"g3"],[18,"g3"],[77,"g4"],[18,"g4"],[77,"g5"],[18,"g5"],[77,"g6"],[18,"g6"],"\x3A"=>[77,"g7"],"\x3B"=>[18,"g7"],"\x3C"=>[77,"g8"],"\x3D"=>[18,"g8"],"\x3E"=>[77,"g9"],"\x3F"=>[18,"g9"],"\x40"=>[77,"g\x3D"],"A"=>[18,"g\x3D"],"B"=>[77,"gA"],"C"=>[18,"gA"],"D"=>[77,"g_"],"E"=>[18,"g_"],"F"=>[77,"gb"],"G"=>[18,"gb"],"H"=>[77,"gd"],"I"=>[18,"gd"],"J"=>[77,"gf"],"K"=>[18,"gf"],"L"=>[77,"gg"],"M"=>[18,"gg"],"N"=>[77,"gh"],"O"=>[18,"gh"],"P"=>[77,"gl"],"Q"=>[18,"gl"],"R"=>[77,"gm"],"S"=>[18,"gm"],"T"=>[77,"gn"],"U"=>[18,"gn"],"V"=>[77,"gp"],"W"=>[18,"gp"],"X"=>[77,"gr"],"Y"=>[18,"gr"],"Z"=>[77,"gu"],"\x5B"=>[18,"gu"],"\x5C"=>[0,"g\x3A"],"\x5D"=>[0,"gB"],"\x5E"=>[0,"gC"],"_"=>[0,"gD"],"\x60"=>[0,"gE"],"a"=>[0,"gF"],"b"=>[0,"gG"],"c"=>[0,"gH"],"d"=>[0,"gI"],"e"=>[0,"gJ"],"f"=>[0,"gK"],"g"=>[0,"gL"],"h"=>[0,"gM"],"i"=>[0,"gN"],"j"=>[0,"gO"],"k"=>[0,"gP"],"l"=>[0,"gQ"],"m"=>[0,"gR"],"n"=>[0,"gS"],"o"=>[0,"gT"],"p"=>[0,"gU"],"q"=>[0,"gV"],"r"=>[0,"gW"],"s"=>[0,"gY"],"t"=>[0,"gj"],"u"=>[0,"gk"],"v"=>[0,"gq"],"w"=>[0,"gv"],"x"=>[0,"gw"],"y"=>[0,"gx"],"z"=>[0,"gy"],"\x7B"=>[0,"gz"],"\x7C"=>[82,"g"],"\x7D"=>[87,"g"],"~"=>[130,"g"],"\x7F"=>[9,"g"],"\x80"=>[94,"h0"],"\x81"=>[76,"h0"],"\x82"=>[104,"h0"],"\x83"=>[16,"h0"],"\x84"=>[94,"h1"],"\x85"=>[76,"h1"],"\x86"=>[104,"h1"],"\x87"=>[16,"h1"],"\x88"=>[94,"h2"],"\x89"=>[76,"h2"],"\x8A"=>[104,"h2"],"\x8B"=>[16,"h2"],"\x8C"=>[94,"ha"],"\x8D"=>[76,"ha"],"\x8E"=>[104,"ha"],"\x8F"=>[16,"ha"],"\x90"=>[94,"hc"],"\x91"=>[76,"hc"],"\x92"=>[104,"hc"],"\x93"=>[16,"hc"],"\x94"=>[94,"he"],"\x95"=>[76,"he"],"\x96"=>[104,"he"],"\x97"=>[16,"he"],"\x98"=>[94,"hi"],"\x99"=>[76,"hi"],"\x9A"=>[104,"hi"],"\x9B"=>[16,"hi"],"\x9C"=>[94,"ho"],"\x9D"=>[76,"ho"],"\x9E"=>[104,"ho"],"\x9F"=>[16,"ho"],"\xA0"=>[94,"hs"],"\xA1"=>[76,"hs"],"\xA2"=>[104,"hs"],"\xA3"=>[16,"hs"],"\xA4"=>[94,"ht"],"\xA5"=>[76,"ht"],"\xA6"=>[104,"ht"],"\xA7"=>[16,"ht"],"\xA8"=>[77,"h\x20"],"\xA9"=>[18,"h\x20"],"\xAA"=>[77,"h\x25"],"\xAB"=>[18,"h\x25"],"\xAC"=>[77,"h-"],"\xAD"=>[18,"h-"],"\xAE"=>[77,"h."],"\xAF"=>[18,"h."],"\xB0"=>[77,"h\x2F"],"\xB1"=>[18,"h\x2F"],"\xB2"=>[77,"h3"],"\xB3"=>[18,"h3"],"\xB4"=>[77,"h4"],"\xB5"=>[18,"h4"],"\xB6"=>[77,"h5"],"\xB7"=>[18,"h5"],"\xB8"=>[77,"h6"],"\xB9"=>[18,"h6"],"\xBA"=>[77,"h7"],"\xBB"=>[18,"h7"],"\xBC"=>[77,"h8"],"\xBD"=>[18,"h8"],"\xBE"=>[77,"h9"],"\xBF"=>[18,"h9"],"\xC0"=>[77,"h\x3D"],"\xC1"=>[18,"h\x3D"],"\xC2"=>[77,"hA"],"\xC3"=>[18,"hA"],"\xC4"=>[77,"h_"],"\xC5"=>[18,"h_"],"\xC6"=>[77,"hb"],"\xC7"=>[18,"hb"],"\xC8"=>[77,"hd"],"\xC9"=>[18,"hd"],"\xCA"=>[77,"hf"],"\xCB"=>[18,"hf"],"\xCC"=>[77,"hg"],"\xCD"=>[18,"hg"],"\xCE"=>[77,"hh"],"\xCF"=>[18,"hh"],"\xD0"=>[77,"hl"],"\xD1"=>[18,"hl"],"\xD2"=>[77,"hm"],"\xD3"=>[18,"hm"],"\xD4"=>[77,"hn"],"\xD5"=>[18,"hn"],"\xD6"=>[77,"hp"],"\xD7"=>[18,"hp"],"\xD8"=>[77,"hr"],"\xD9"=>[18,"hr"],"\xDA"=>[77,"hu"],"\xDB"=>[18,"hu"],"\xDC"=>[0,"h\x3A"],"\xDD"=>[0,"hB"],"\xDE"=>[0,"hC"],"\xDF"=>[0,"hD"],"\xE0"=>[0,"hE"],"\xE1"=>[0,"hF"],"\xE2"=>[0,"hG"],"\xE3"=>[0,"hH"],"\xE4"=>[0,"hI"],"\xE5"=>[0,"hJ"],"\xE6"=>[0,"hK"],"\xE7"=>[0,"hL"],"\xE8"=>[0,"hM"],"\xE9"=>[0,"hN"],"\xEA"=>[0,"hO"],"\xEB"=>[0,"hP"],"\xEC"=>[0,"hQ"],"\xED"=>[0,"hR"],"\xEE"=>[0,"hS"],"\xEF"=>[0,"hT"],"\xF0"=>[0,"hU"],"\xF1"=>[0,"hV"],"\xF2"=>[0,"hW"],"\xF3"=>[0,"hY"],"\xF4"=>[0,"hj"],"\xF5"=>[0,"hk"],"\xF6"=>[0,"hq"],"\xF7"=>[0,"hv"],"\xF8"=>[0,"hw"],"\xF9"=>[0,"hx"],"\xFA"=>[0,"hy"],"\xFB"=>[0,"hz"],"\xFC"=>[82,"h"],"\xFD"=>[87,"h"],"\xFE"=>[130,"h"],"\xFF"=>[9,"h"],],["\x00"=>[94,"i0"],"\x01"=>[76,"i0"],"\x02"=>[104,"i0"],"\x03"=>[16,"i0"],"\x04"=>[94,"i1"],"\x05"=>[76,"i1"],"\x06"=>[104,"i1"],"\x07"=>[16,"i1"],"\x08"=>[94,"i2"],"\x09"=>[76,"i2"],"\x0A"=>[104,"i2"],"\x0B"=>[16,"i2"],"\x0C"=>[94,"ia"],"\x0D"=>[76,"ia"],"\x0E"=>[104,"ia"],"\x0F"=>[16,"ia"],"\x10"=>[94,"ic"],"\x11"=>[76,"ic"],"\x12"=>[104,"ic"],"\x13"=>[16,"ic"],"\x14"=>[94,"ie"],"\x15"=>[76,"ie"],"\x16"=>[104,"ie"],"\x17"=>[16,"ie"],"\x18"=>[94,"ii"],"\x19"=>[76,"ii"],"\x1A"=>[104,"ii"],"\x1B"=>[16,"ii"],"\x1C"=>[94,"io"],"\x1D"=>[76,"io"],"\x1E"=>[104,"io"],"\x1F"=>[16,"io"],"\x20"=>[94,"is"],"\x21"=>[76,"is"],"\x22"=>[104,"is"],"\x23"=>[16,"is"],"\x24"=>[94,"it"],"\x25"=>[76,"it"],"\x26"=>[104,"it"],"\x27"=>[16,"it"],"\x28"=>[77,"i\x20"],"\x29"=>[18,"i\x20"],"\x2A"=>[77,"i\x25"],"\x2B"=>[18,"i\x25"],"\x2C"=>[77,"i-"],"-"=>[18,"i-"],"."=>[77,"i."],"\x2F"=>[18,"i."],[77,"i\x2F"],[18,"i\x2F"],[77,"i3"],[18,"i3"],[77,"i4"],[18,"i4"],[77,"i5"],[18,"i5"],[77,"i6"],[18,"i6"],"\x3A"=>[77,"i7"],"\x3B"=>[18,"i7"],"\x3C"=>[77,"i8"],"\x3D"=>[18,"i8"],"\x3E"=>[77,"i9"],"\x3F"=>[18,"i9"],"\x40"=>[77,"i\x3D"],"A"=>[18,"i\x3D"],"B"=>[77,"iA"],"C"=>[18,"iA"],"D"=>[77,"i_"],"E"=>[18,"i_"],"F"=>[77,"ib"],"G"=>[18,"ib"],"H"=>[77,"id"],"I"=>[18,"id"],"J"=>[77,"if"],"K"=>[18,"if"],"L"=>[77,"ig"],"M"=>[18,"ig"],"N"=>[77,"ih"],"O"=>[18,"ih"],"P"=>[77,"il"],"Q"=>[18,"il"],"R"=>[77,"im"],"S"=>[18,"im"],"T"=>[77,"in"],"U"=>[18,"in"],"V"=>[77,"ip"],"W"=>[18,"ip"],"X"=>[77,"ir"],"Y"=>[18,"ir"],"Z"=>[77,"iu"],"\x5B"=>[18,"iu"],"\x5C"=>[0,"i\x3A"],"\x5D"=>[0,"iB"],"\x5E"=>[0,"iC"],"_"=>[0,"iD"],"\x60"=>[0,"iE"],"a"=>[0,"iF"],"b"=>[0,"iG"],"c"=>[0,"iH"],"d"=>[0,"iI"],"e"=>[0,"iJ"],"f"=>[0,"iK"],"g"=>[0,"iL"],"h"=>[0,"iM"],"i"=>[0,"iN"],"j"=>[0,"iO"],"k"=>[0,"iP"],"l"=>[0,"iQ"],"m"=>[0,"iR"],"n"=>[0,"iS"],"o"=>[0,"iT"],"p"=>[0,"iU"],"q"=>[0,"iV"],"r"=>[0,"iW"],"s"=>[0,"iY"],"t"=>[0,"ij"],"u"=>[0,"ik"],"v"=>[0,"iq"],"w"=>[0,"iv"],"x"=>[0,"iw"],"y"=>[0,"ix"],"z"=>[0,"iy"],"\x7B"=>[0,"iz"],"\x7C"=>[82,"i"],"\x7D"=>[87,"i"],"~"=>[130,"i"],"\x7F"=>[9,"i"],"\x80"=>[94,"o0"],"\x81"=>[76,"o0"],"\x82"=>[104,"o0"],"\x83"=>[16,"o0"],"\x84"=>[94,"o1"],"\x85"=>[76,"o1"],"\x86"=>[104,"o1"],"\x87"=>[16,"o1"],"\x88"=>[94,"o2"],"\x89"=>[76,"o2"],"\x8A"=>[104,"o2"],"\x8B"=>[16,"o2"],"\x8C"=>[94,"oa"],"\x8D"=>[76,"oa"],"\x8E"=>[104,"oa"],"\x8F"=>[16,"oa"],"\x90"=>[94,"oc"],"\x91"=>[76,"oc"],"\x92"=>[104,"oc"],"\x93"=>[16,"oc"],"\x94"=>[94,"oe"],"\x95"=>[76,"oe"],"\x96"=>[104,"oe"],"\x97"=>[16,"oe"],"\x98"=>[94,"oi"],"\x99"=>[76,"oi"],"\x9A"=>[104,"oi"],"\x9B"=>[16,"oi"],"\x9C"=>[94,"oo"],"\x9D"=>[76,"oo"],"\x9E"=>[104,"oo"],"\x9F"=>[16,"oo"],"\xA0"=>[94,"os"],"\xA1"=>[76,"os"],"\xA2"=>[104,"os"],"\xA3"=>[16,"os"],"\xA4"=>[94,"ot"],"\xA5"=>[76,"ot"],"\xA6"=>[104,"ot"],"\xA7"=>[16,"ot"],"\xA8"=>[77,"o\x20"],"\xA9"=>[18,"o\x20"],"\xAA"=>[77,"o\x25"],"\xAB"=>[18,"o\x25"],"\xAC"=>[77,"o-"],"\xAD"=>[18,"o-"],"\xAE"=>[77,"o."],"\xAF"=>[18,"o."],"\xB0"=>[77,"o\x2F"],"\xB1"=>[18,"o\x2F"],"\xB2"=>[77,"o3"],"\xB3"=>[18,"o3"],"\xB4"=>[77,"o4"],"\xB5"=>[18,"o4"],"\xB6"=>[77,"o5"],"\xB7"=>[18,"o5"],"\xB8"=>[77,"o6"],"\xB9"=>[18,"o6"],"\xBA"=>[77,"o7"],"\xBB"=>[18,"o7"],"\xBC"=>[77,"o8"],"\xBD"=>[18,"o8"],"\xBE"=>[77,"o9"],"\xBF"=>[18,"o9"],"\xC0"=>[77,"o\x3D"],"\xC1"=>[18,"o\x3D"],"\xC2"=>[77,"oA"],"\xC3"=>[18,"oA"],"\xC4"=>[77,"o_"],"\xC5"=>[18,"o_"],"\xC6"=>[77,"ob"],"\xC7"=>[18,"ob"],"\xC8"=>[77,"od"],"\xC9"=>[18,"od"],"\xCA"=>[77,"of"],"\xCB"=>[18,"of"],"\xCC"=>[77,"og"],"\xCD"=>[18,"og"],"\xCE"=>[77,"oh"],"\xCF"=>[18,"oh"],"\xD0"=>[77,"ol"],"\xD1"=>[18,"ol"],"\xD2"=>[77,"om"],"\xD3"=>[18,"om"],"\xD4"=>[77,"on"],"\xD5"=>[18,"on"],"\xD6"=>[77,"op"],"\xD7"=>[18,"op"],"\xD8"=>[77,"or"],"\xD9"=>[18,"or"],"\xDA"=>[77,"ou"],"\xDB"=>[18,"ou"],"\xDC"=>[0,"o\x3A"],"\xDD"=>[0,"oB"],"\xDE"=>[0,"oC"],"\xDF"=>[0,"oD"],"\xE0"=>[0,"oE"],"\xE1"=>[0,"oF"],"\xE2"=>[0,"oG"],"\xE3"=>[0,"oH"],"\xE4"=>[0,"oI"],"\xE5"=>[0,"oJ"],"\xE6"=>[0,"oK"],"\xE7"=>[0,"oL"],"\xE8"=>[0,"oM"],"\xE9"=>[0,"oN"],"\xEA"=>[0,"oO"],"\xEB"=>[0,"oP"],"\xEC"=>[0,"oQ"],"\xED"=>[0,"oR"],"\xEE"=>[0,"oS"],"\xEF"=>[0,"oT"],"\xF0"=>[0,"oU"],"\xF1"=>[0,"oV"],"\xF2"=>[0,"oW"],"\xF3"=>[0,"oY"],"\xF4"=>[0,"oj"],"\xF5"=>[0,"ok"],"\xF6"=>[0,"oq"],"\xF7"=>[0,"ov"],"\xF8"=>[0,"ow"],"\xF9"=>[0,"ox"],"\xFA"=>[0,"oy"],"\xFB"=>[0,"oz"],"\xFC"=>[82,"o"],"\xFD"=>[87,"o"],"\xFE"=>[130,"o"],"\xFF"=>[9,"o"],],["\x00"=>[94,"j0"],"\x01"=>[76,"j0"],"\x02"=>[104,"j0"],"\x03"=>[16,"j0"],"\x04"=>[94,"j1"],"\x05"=>[76,"j1"],"\x06"=>[104,"j1"],"\x07"=>[16,"j1"],"\x08"=>[94,"j2"],"\x09"=>[76,"j2"],"\x0A"=>[104,"j2"],"\x0B"=>[16,"j2"],"\x0C"=>[94,"ja"],"\x0D"=>[76,"ja"],"\x0E"=>[104,"ja"],"\x0F"=>[16,"ja"],"\x10"=>[94,"jc"],"\x11"=>[76,"jc"],"\x12"=>[104,"jc"],"\x13"=>[16,"jc"],"\x14"=>[94,"je"],"\x15"=>[76,"je"],"\x16"=>[104,"je"],"\x17"=>[16,"je"],"\x18"=>[94,"ji"],"\x19"=>[76,"ji"],"\x1A"=>[104,"ji"],"\x1B"=>[16,"ji"],"\x1C"=>[94,"jo"],"\x1D"=>[76,"jo"],"\x1E"=>[104,"jo"],"\x1F"=>[16,"jo"],"\x20"=>[94,"js"],"\x21"=>[76,"js"],"\x22"=>[104,"js"],"\x23"=>[16,"js"],"\x24"=>[94,"jt"],"\x25"=>[76,"jt"],"\x26"=>[104,"jt"],"\x27"=>[16,"jt"],"\x28"=>[77,"j\x20"],"\x29"=>[18,"j\x20"],"\x2A"=>[77,"j\x25"],"\x2B"=>[18,"j\x25"],"\x2C"=>[77,"j-"],"-"=>[18,"j-"],"."=>[77,"j."],"\x2F"=>[18,"j."],[77,"j\x2F"],[18,"j\x2F"],[77,"j3"],[18,"j3"],[77,"j4"],[18,"j4"],[77,"j5"],[18,"j5"],[77,"j6"],[18,"j6"],"\x3A"=>[77,"j7"],"\x3B"=>[18,"j7"],"\x3C"=>[77,"j8"],"\x3D"=>[18,"j8"],"\x3E"=>[77,"j9"],"\x3F"=>[18,"j9"],"\x40"=>[77,"j\x3D"],"A"=>[18,"j\x3D"],"B"=>[77,"jA"],"C"=>[18,"jA"],"D"=>[77,"j_"],"E"=>[18,"j_"],"F"=>[77,"jb"],"G"=>[18,"jb"],"H"=>[77,"jd"],"I"=>[18,"jd"],"J"=>[77,"jf"],"K"=>[18,"jf"],"L"=>[77,"jg"],"M"=>[18,"jg"],"N"=>[77,"jh"],"O"=>[18,"jh"],"P"=>[77,"jl"],"Q"=>[18,"jl"],"R"=>[77,"jm"],"S"=>[18,"jm"],"T"=>[77,"jn"],"U"=>[18,"jn"],"V"=>[77,"jp"],"W"=>[18,"jp"],"X"=>[77,"jr"],"Y"=>[18,"jr"],"Z"=>[77,"ju"],"\x5B"=>[18,"ju"],"\x5C"=>[0,"j\x3A"],"\x5D"=>[0,"jB"],"\x5E"=>[0,"jC"],"_"=>[0,"jD"],"\x60"=>[0,"jE"],"a"=>[0,"jF"],"b"=>[0,"jG"],"c"=>[0,"jH"],"d"=>[0,"jI"],"e"=>[0,"jJ"],"f"=>[0,"jK"],"g"=>[0,"jL"],"h"=>[0,"jM"],"i"=>[0,"jN"],"j"=>[0,"jO"],"k"=>[0,"jP"],"l"=>[0,"jQ"],"m"=>[0,"jR"],"n"=>[0,"jS"],"o"=>[0,"jT"],"p"=>[0,"jU"],"q"=>[0,"jV"],"r"=>[0,"jW"],"s"=>[0,"jY"],"t"=>[0,"jj"],"u"=>[0,"jk"],"v"=>[0,"jq"],"w"=>[0,"jv"],"x"=>[0,"jw"],"y"=>[0,"jx"],"z"=>[0,"jy"],"\x7B"=>[0,"jz"],"\x7C"=>[82,"j"],"\x7D"=>[87,"j"],"~"=>[130,"j"],"\x7F"=>[9,"j"],"\x80"=>[94,"k0"],"\x81"=>[76,"k0"],"\x82"=>[104,"k0"],"\x83"=>[16,"k0"],"\x84"=>[94,"k1"],"\x85"=>[76,"k1"],"\x86"=>[104,"k1"],"\x87"=>[16,"k1"],"\x88"=>[94,"k2"],"\x89"=>[76,"k2"],"\x8A"=>[104,"k2"],"\x8B"=>[16,"k2"],"\x8C"=>[94,"ka"],"\x8D"=>[76,"ka"],"\x8E"=>[104,"ka"],"\x8F"=>[16,"ka"],"\x90"=>[94,"kc"],"\x91"=>[76,"kc"],"\x92"=>[104,"kc"],"\x93"=>[16,"kc"],"\x94"=>[94,"ke"],"\x95"=>[76,"ke"],"\x96"=>[104,"ke"],"\x97"=>[16,"ke"],"\x98"=>[94,"ki"],"\x99"=>[76,"ki"],"\x9A"=>[104,"ki"],"\x9B"=>[16,"ki"],"\x9C"=>[94,"ko"],"\x9D"=>[76,"ko"],"\x9E"=>[104,"ko"],"\x9F"=>[16,"ko"],"\xA0"=>[94,"ks"],"\xA1"=>[76,"ks"],"\xA2"=>[104,"ks"],"\xA3"=>[16,"ks"],"\xA4"=>[94,"kt"],"\xA5"=>[76,"kt"],"\xA6"=>[104,"kt"],"\xA7"=>[16,"kt"],"\xA8"=>[77,"k\x20"],"\xA9"=>[18,"k\x20"],"\xAA"=>[77,"k\x25"],"\xAB"=>[18,"k\x25"],"\xAC"=>[77,"k-"],"\xAD"=>[18,"k-"],"\xAE"=>[77,"k."],"\xAF"=>[18,"k."],"\xB0"=>[77,"k\x2F"],"\xB1"=>[18,"k\x2F"],"\xB2"=>[77,"k3"],"\xB3"=>[18,"k3"],"\xB4"=>[77,"k4"],"\xB5"=>[18,"k4"],"\xB6"=>[77,"k5"],"\xB7"=>[18,"k5"],"\xB8"=>[77,"k6"],"\xB9"=>[18,"k6"],"\xBA"=>[77,"k7"],"\xBB"=>[18,"k7"],"\xBC"=>[77,"k8"],"\xBD"=>[18,"k8"],"\xBE"=>[77,"k9"],"\xBF"=>[18,"k9"],"\xC0"=>[77,"k\x3D"],"\xC1"=>[18,"k\x3D"],"\xC2"=>[77,"kA"],"\xC3"=>[18,"kA"],"\xC4"=>[77,"k_"],"\xC5"=>[18,"k_"],"\xC6"=>[77,"kb"],"\xC7"=>[18,"kb"],"\xC8"=>[77,"kd"],"\xC9"=>[18,"kd"],"\xCA"=>[77,"kf"],"\xCB"=>[18,"kf"],"\xCC"=>[77,"kg"],"\xCD"=>[18,"kg"],"\xCE"=>[77,"kh"],"\xCF"=>[18,"kh"],"\xD0"=>[77,"kl"],"\xD1"=>[18,"kl"],"\xD2"=>[77,"km"],"\xD3"=>[18,"km"],"\xD4"=>[77,"kn"],"\xD5"=>[18,"kn"],"\xD6"=>[77,"kp"],"\xD7"=>[18,"kp"],"\xD8"=>[77,"kr"],"\xD9"=>[18,"kr"],"\xDA"=>[77,"ku"],"\xDB"=>[18,"ku"],"\xDC"=>[0,"k\x3A"],"\xDD"=>[0,"kB"],"\xDE"=>[0,"kC"],"\xDF"=>[0,"kD"],"\xE0"=>[0,"kE"],"\xE1"=>[0,"kF"],"\xE2"=>[0,"kG"],"\xE3"=>[0,"kH"],"\xE4"=>[0,"kI"],"\xE5"=>[0,"kJ"],"\xE6"=>[0,"kK"],"\xE7"=>[0,"kL"],"\xE8"=>[0,"kM"],"\xE9"=>[0,"kN"],"\xEA"=>[0,"kO"],"\xEB"=>[0,"kP"],"\xEC"=>[0,"kQ"],"\xED"=>[0,"kR"],"\xEE"=>[0,"kS"],"\xEF"=>[0,"kT"],"\xF0"=>[0,"kU"],"\xF1"=>[0,"kV"],"\xF2"=>[0,"kW"],"\xF3"=>[0,"kY"],"\xF4"=>[0,"kj"],"\xF5"=>[0,"kk"],"\xF6"=>[0,"kq"],"\xF7"=>[0,"kv"],"\xF8"=>[0,"kw"],"\xF9"=>[0,"kx"],"\xFA"=>[0,"ky"],"\xFB"=>[0,"kz"],"\xFC"=>[82,"k"],"\xFD"=>[87,"k"],"\xFE"=>[130,"k"],"\xFF"=>[9,"k"],],["\x00"=>[77,"j0"],"\x01"=>[18,"j0"],"\x02"=>[77,"j1"],"\x03"=>[18,"j1"],"\x04"=>[77,"j2"],"\x05"=>[18,"j2"],"\x06"=>[77,"ja"],"\x07"=>[18,"ja"],"\x08"=>[77,"jc"],"\x09"=>[18,"jc"],"\x0A"=>[77,"je"],"\x0B"=>[18,"je"],"\x0C"=>[77,"ji"],"\x0D"=>[18,"ji"],"\x0E"=>[77,"jo"],"\x0F"=>[18,"jo"],"\x10"=>[77,"js"],"\x11"=>[18,"js"],"\x12"=>[77,"jt"],"\x13"=>[18,"jt"],"\x14"=>[0,"j\x20"],"\x15"=>[0,"j\x25"],"\x16"=>[0,"j-"],"\x17"=>[0,"j."],"\x18"=>[0,"j\x2F"],"\x19"=>[0,"j3"],"\x1A"=>[0,"j4"],"\x1B"=>[0,"j5"],"\x1C"=>[0,"j6"],"\x1D"=>[0,"j7"],"\x1E"=>[0,"j8"],"\x1F"=>[0,"j9"],"\x20"=>[0,"j\x3D"],"\x21"=>[0,"jA"],"\x22"=>[0,"j_"],"\x23"=>[0,"jb"],"\x24"=>[0,"jd"],"\x25"=>[0,"jf"],"\x26"=>[0,"jg"],"\x27"=>[0,"jh"],"\x28"=>[0,"jl"],"\x29"=>[0,"jm"],"\x2A"=>[0,"jn"],"\x2B"=>[0,"jp"],"\x2C"=>[0,"jr"],"-"=>[0,"ju"],"."=>[100,"j"],"\x2F"=>[110,"j"],[111,"j"],[115,"j"],[116,"j"],[118,"j"],[119,"j"],[122,"j"],[123,"j"],[125,"j"],[126,"j"],[129,"j"],"\x3A"=>[143,"j"],"\x3B"=>[148,"j"],"\x3C"=>[151,"j"],"\x3D"=>[153,"j"],"\x3E"=>[83,"j"],"\x3F"=>[10,"j"],"\x40"=>[77,"k0"],"A"=>[18,"k0"],"B"=>[77,"k1"],"C"=>[18,"k1"],"D"=>[77,"k2"],"E"=>[18,"k2"],"F"=>[77,"ka"],"G"=>[18,"ka"],"H"=>[77,"kc"],"I"=>[18,"kc"],"J"=>[77,"ke"],"K"=>[18,"ke"],"L"=>[77,"ki"],"M"=>[18,"ki"],"N"=>[77,"ko"],"O"=>[18,"ko"],"P"=>[77,"ks"],"Q"=>[18,"ks"],"R"=>[77,"kt"],"S"=>[18,"kt"],"T"=>[0,"k\x20"],"U"=>[0,"k\x25"],"V"=>[0,"k-"],"W"=>[0,"k."],"X"=>[0,"k\x2F"],"Y"=>[0,"k3"],"Z"=>[0,"k4"],"\x5B"=>[0,"k5"],"\x5C"=>[0,"k6"],"\x5D"=>[0,"k7"],"\x5E"=>[0,"k8"],"_"=>[0,"k9"],"\x60"=>[0,"k\x3D"],"a"=>[0,"kA"],"b"=>[0,"k_"],"c"=>[0,"kb"],"d"=>[0,"kd"],"e"=>[0,"kf"],"f"=>[0,"kg"],"g"=>[0,"kh"],"h"=>[0,"kl"],"i"=>[0,"km"],"j"=>[0,"kn"],"k"=>[0,"kp"],"l"=>[0,"kr"],"m"=>[0,"ku"],"n"=>[100,"k"],"o"=>[110,"k"],"p"=>[111,"k"],"q"=>[115,"k"],"r"=>[116,"k"],"s"=>[118,"k"],"t"=>[119,"k"],"u"=>[122,"k"],"v"=>[123,"k"],"w"=>[125,"k"],"x"=>[126,"k"],"y"=>[129,"k"],"z"=>[143,"k"],"\x7B"=>[148,"k"],"\x7C"=>[151,"k"],"\x7D"=>[153,"k"],"~"=>[83,"k"],"\x7F"=>[10,"k"],"\x80"=>[77,"q0"],"\x81"=>[18,"q0"],"\x82"=>[77,"q1"],"\x83"=>[18,"q1"],"\x84"=>[77,"q2"],"\x85"=>[18,"q2"],"\x86"=>[77,"qa"],"\x87"=>[18,"qa"],"\x88"=>[77,"qc"],"\x89"=>[18,"qc"],"\x8A"=>[77,"qe"],"\x8B"=>[18,"qe"],"\x8C"=>[77,"qi"],"\x8D"=>[18,"qi"],"\x8E"=>[77,"qo"],"\x8F"=>[18,"qo"],"\x90"=>[77,"qs"],"\x91"=>[18,"qs"],"\x92"=>[77,"qt"],"\x93"=>[18,"qt"],"\x94"=>[0,"q\x20"],"\x95"=>[0,"q\x25"],"\x96"=>[0,"q-"],"\x97"=>[0,"q."],"\x98"=>[0,"q\x2F"],"\x99"=>[0,"q3"],"\x9A"=>[0,"q4"],"\x9B"=>[0,"q5"],"\x9C"=>[0,"q6"],"\x9D"=>[0,"q7"],"\x9E"=>[0,"q8"],"\x9F"=>[0,"q9"],"\xA0"=>[0,"q\x3D"],"\xA1"=>[0,"qA"],"\xA2"=>[0,"q_"],"\xA3"=>[0,"qb"],"\xA4"=>[0,"qd"],"\xA5"=>[0,"qf"],"\xA6"=>[0,"qg"],"\xA7"=>[0,"qh"],"\xA8"=>[0,"ql"],"\xA9"=>[0,"qm"],"\xAA"=>[0,"qn"],"\xAB"=>[0,"qp"],"\xAC"=>[0,"qr"],"\xAD"=>[0,"qu"],"\xAE"=>[100,"q"],"\xAF"=>[110,"q"],"\xB0"=>[111,"q"],"\xB1"=>[115,"q"],"\xB2"=>[116,"q"],"\xB3"=>[118,"q"],"\xB4"=>[119,"q"],"\xB5"=>[122,"q"],"\xB6"=>[123,"q"],"\xB7"=>[125,"q"],"\xB8"=>[126,"q"],"\xB9"=>[129,"q"],"\xBA"=>[143,"q"],"\xBB"=>[148,"q"],"\xBC"=>[151,"q"],"\xBD"=>[153,"q"],"\xBE"=>[83,"q"],"\xBF"=>[10,"q"],"\xC0"=>[77,"v0"],"\xC1"=>[18,"v0"],"\xC2"=>[77,"v1"],"\xC3"=>[18,"v1"],"\xC4"=>[77,"v2"],"\xC5"=>[18,"v2"],"\xC6"=>[77,"va"],"\xC7"=>[18,"va"],"\xC8"=>[77,"vc"],"\xC9"=>[18,"vc"],"\xCA"=>[77,"ve"],"\xCB"=>[18,"ve"],"\xCC"=>[77,"vi"],"\xCD"=>[18,"vi"],"\xCE"=>[77,"vo"],"\xCF"=>[18,"vo"],"\xD0"=>[77,"vs"],"\xD1"=>[18,"vs"],"\xD2"=>[77,"vt"],"\xD3"=>[18,"vt"],"\xD4"=>[0,"v\x20"],"\xD5"=>[0,"v\x25"],"\xD6"=>[0,"v-"],"\xD7"=>[0,"v."],"\xD8"=>[0,"v\x2F"],"\xD9"=>[0,"v3"],"\xDA"=>[0,"v4"],"\xDB"=>[0,"v5"],"\xDC"=>[0,"v6"],"\xDD"=>[0,"v7"],"\xDE"=>[0,"v8"],"\xDF"=>[0,"v9"],"\xE0"=>[0,"v\x3D"],"\xE1"=>[0,"vA"],"\xE2"=>[0,"v_"],"\xE3"=>[0,"vb"],"\xE4"=>[0,"vd"],"\xE5"=>[0,"vf"],"\xE6"=>[0,"vg"],"\xE7"=>[0,"vh"],"\xE8"=>[0,"vl"],"\xE9"=>[0,"vm"],"\xEA"=>[0,"vn"],"\xEB"=>[0,"vp"],"\xEC"=>[0,"vr"],"\xED"=>[0,"vu"],"\xEE"=>[100,"v"],"\xEF"=>[110,"v"],"\xF0"=>[111,"v"],"\xF1"=>[115,"v"],"\xF2"=>[116,"v"],"\xF3"=>[118,"v"],"\xF4"=>[119,"v"],"\xF5"=>[122,"v"],"\xF6"=>[123,"v"],"\xF7"=>[125,"v"],"\xF8"=>[126,"v"],"\xF9"=>[129,"v"],"\xFA"=>[143,"v"],"\xFB"=>[148,"v"],"\xFC"=>[151,"v"],"\xFD"=>[153,"v"],"\xFE"=>[83,"v"],"\xFF"=>[10,"v"],],["\x00"=>[94,"l0"],"\x01"=>[76,"l0"],"\x02"=>[104,"l0"],"\x03"=>[16,"l0"],"\x04"=>[94,"l1"],"\x05"=>[76,"l1"],"\x06"=>[104,"l1"],"\x07"=>[16,"l1"],"\x08"=>[94,"l2"],"\x09"=>[76,"l2"],"\x0A"=>[104,"l2"],"\x0B"=>[16,"l2"],"\x0C"=>[94,"la"],"\x0D"=>[76,"la"],"\x0E"=>[104,"la"],"\x0F"=>[16,"la"],"\x10"=>[94,"lc"],"\x11"=>[76,"lc"],"\x12"=>[104,"lc"],"\x13"=>[16,"lc"],"\x14"=>[94,"le"],"\x15"=>[76,"le"],"\x16"=>[104,"le"],"\x17"=>[16,"le"],"\x18"=>[94,"li"],"\x19"=>[76,"li"],"\x1A"=>[104,"li"],"\x1B"=>[16,"li"],"\x1C"=>[94,"lo"],"\x1D"=>[76,"lo"],"\x1E"=>[104,"lo"],"\x1F"=>[16,"lo"],"\x20"=>[94,"ls"],"\x21"=>[76,"ls"],"\x22"=>[104,"ls"],"\x23"=>[16,"ls"],"\x24"=>[94,"lt"],"\x25"=>[76,"lt"],"\x26"=>[104,"lt"],"\x27"=>[16,"lt"],"\x28"=>[77,"l\x20"],"\x29"=>[18,"l\x20"],"\x2A"=>[77,"l\x25"],"\x2B"=>[18,"l\x25"],"\x2C"=>[77,"l-"],"-"=>[18,"l-"],"."=>[77,"l."],"\x2F"=>[18,"l."],[77,"l\x2F"],[18,"l\x2F"],[77,"l3"],[18,"l3"],[77,"l4"],[18,"l4"],[77,"l5"],[18,"l5"],[77,"l6"],[18,"l6"],"\x3A"=>[77,"l7"],"\x3B"=>[18,"l7"],"\x3C"=>[77,"l8"],"\x3D"=>[18,"l8"],"\x3E"=>[77,"l9"],"\x3F"=>[18,"l9"],"\x40"=>[77,"l\x3D"],"A"=>[18,"l\x3D"],"B"=>[77,"lA"],"C"=>[18,"lA"],"D"=>[77,"l_"],"E"=>[18,"l_"],"F"=>[77,"lb"],"G"=>[18,"lb"],"H"=>[77,"ld"],"I"=>[18,"ld"],"J"=>[77,"lf"],"K"=>[18,"lf"],"L"=>[77,"lg"],"M"=>[18,"lg"],"N"=>[77,"lh"],"O"=>[18,"lh"],"P"=>[77,"ll"],"Q"=>[18,"ll"],"R"=>[77,"lm"],"S"=>[18,"lm"],"T"=>[77,"ln"],"U"=>[18,"ln"],"V"=>[77,"lp"],"W"=>[18,"lp"],"X"=>[77,"lr"],"Y"=>[18,"lr"],"Z"=>[77,"lu"],"\x5B"=>[18,"lu"],"\x5C"=>[0,"l\x3A"],"\x5D"=>[0,"lB"],"\x5E"=>[0,"lC"],"_"=>[0,"lD"],"\x60"=>[0,"lE"],"a"=>[0,"lF"],"b"=>[0,"lG"],"c"=>[0,"lH"],"d"=>[0,"lI"],"e"=>[0,"lJ"],"f"=>[0,"lK"],"g"=>[0,"lL"],"h"=>[0,"lM"],"i"=>[0,"lN"],"j"=>[0,"lO"],"k"=>[0,"lP"],"l"=>[0,"lQ"],"m"=>[0,"lR"],"n"=>[0,"lS"],"o"=>[0,"lT"],"p"=>[0,"lU"],"q"=>[0,"lV"],"r"=>[0,"lW"],"s"=>[0,"lY"],"t"=>[0,"lj"],"u"=>[0,"lk"],"v"=>[0,"lq"],"w"=>[0,"lv"],"x"=>[0,"lw"],"y"=>[0,"lx"],"z"=>[0,"ly"],"\x7B"=>[0,"lz"],"\x7C"=>[82,"l"],"\x7D"=>[87,"l"],"~"=>[130,"l"],"\x7F"=>[9,"l"],"\x80"=>[94,"m0"],"\x81"=>[76,"m0"],"\x82"=>[104,"m0"],"\x83"=>[16,"m0"],"\x84"=>[94,"m1"],"\x85"=>[76,"m1"],"\x86"=>[104,"m1"],"\x87"=>[16,"m1"],"\x88"=>[94,"m2"],"\x89"=>[76,"m2"],"\x8A"=>[104,"m2"],"\x8B"=>[16,"m2"],"\x8C"=>[94,"ma"],"\x8D"=>[76,"ma"],"\x8E"=>[104,"ma"],"\x8F"=>[16,"ma"],"\x90"=>[94,"mc"],"\x91"=>[76,"mc"],"\x92"=>[104,"mc"],"\x93"=>[16,"mc"],"\x94"=>[94,"me"],"\x95"=>[76,"me"],"\x96"=>[104,"me"],"\x97"=>[16,"me"],"\x98"=>[94,"mi"],"\x99"=>[76,"mi"],"\x9A"=>[104,"mi"],"\x9B"=>[16,"mi"],"\x9C"=>[94,"mo"],"\x9D"=>[76,"mo"],"\x9E"=>[104,"mo"],"\x9F"=>[16,"mo"],"\xA0"=>[94,"ms"],"\xA1"=>[76,"ms"],"\xA2"=>[104,"ms"],"\xA3"=>[16,"ms"],"\xA4"=>[94,"mt"],"\xA5"=>[76,"mt"],"\xA6"=>[104,"mt"],"\xA7"=>[16,"mt"],"\xA8"=>[77,"m\x20"],"\xA9"=>[18,"m\x20"],"\xAA"=>[77,"m\x25"],"\xAB"=>[18,"m\x25"],"\xAC"=>[77,"m-"],"\xAD"=>[18,"m-"],"\xAE"=>[77,"m."],"\xAF"=>[18,"m."],"\xB0"=>[77,"m\x2F"],"\xB1"=>[18,"m\x2F"],"\xB2"=>[77,"m3"],"\xB3"=>[18,"m3"],"\xB4"=>[77,"m4"],"\xB5"=>[18,"m4"],"\xB6"=>[77,"m5"],"\xB7"=>[18,"m5"],"\xB8"=>[77,"m6"],"\xB9"=>[18,"m6"],"\xBA"=>[77,"m7"],"\xBB"=>[18,"m7"],"\xBC"=>[77,"m8"],"\xBD"=>[18,"m8"],"\xBE"=>[77,"m9"],"\xBF"=>[18,"m9"],"\xC0"=>[77,"m\x3D"],"\xC1"=>[18,"m\x3D"],"\xC2"=>[77,"mA"],"\xC3"=>[18,"mA"],"\xC4"=>[77,"m_"],"\xC5"=>[18,"m_"],"\xC6"=>[77,"mb"],"\xC7"=>[18,"mb"],"\xC8"=>[77,"md"],"\xC9"=>[18,"md"],"\xCA"=>[77,"mf"],"\xCB"=>[18,"mf"],"\xCC"=>[77,"mg"],"\xCD"=>[18,"mg"],"\xCE"=>[77,"mh"],"\xCF"=>[18,"mh"],"\xD0"=>[77,"ml"],"\xD1"=>[18,"ml"],"\xD2"=>[77,"mm"],"\xD3"=>[18,"mm"],"\xD4"=>[77,"mn"],"\xD5"=>[18,"mn"],"\xD6"=>[77,"mp"],"\xD7"=>[18,"mp"],"\xD8"=>[77,"mr"],"\xD9"=>[18,"mr"],"\xDA"=>[77,"mu"],"\xDB"=>[18,"mu"],"\xDC"=>[0,"m\x3A"],"\xDD"=>[0,"mB"],"\xDE"=>[0,"mC"],"\xDF"=>[0,"mD"],"\xE0"=>[0,"mE"],"\xE1"=>[0,"mF"],"\xE2"=>[0,"mG"],"\xE3"=>[0,"mH"],"\xE4"=>[0,"mI"],"\xE5"=>[0,"mJ"],"\xE6"=>[0,"mK"],"\xE7"=>[0,"mL"],"\xE8"=>[0,"mM"],"\xE9"=>[0,"mN"],"\xEA"=>[0,"mO"],"\xEB"=>[0,"mP"],"\xEC"=>[0,"mQ"],"\xED"=>[0,"mR"],"\xEE"=>[0,"mS"],"\xEF"=>[0,"mT"],"\xF0"=>[0,"mU"],"\xF1"=>[0,"mV"],"\xF2"=>[0,"mW"],"\xF3"=>[0,"mY"],"\xF4"=>[0,"mj"],"\xF5"=>[0,"mk"],"\xF6"=>[0,"mq"],"\xF7"=>[0,"mv"],"\xF8"=>[0,"mw"],"\xF9"=>[0,"mx"],"\xFA"=>[0,"my"],"\xFB"=>[0,"mz"],"\xFC"=>[82,"m"],"\xFD"=>[87,"m"],"\xFE"=>[130,"m"],"\xFF"=>[9,"m"],],["\x00"=>[77,"l0"],"\x01"=>[18,"l0"],"\x02"=>[77,"l1"],"\x03"=>[18,"l1"],"\x04"=>[77,"l2"],"\x05"=>[18,"l2"],"\x06"=>[77,"la"],"\x07"=>[18,"la"],"\x08"=>[77,"lc"],"\x09"=>[18,"lc"],"\x0A"=>[77,"le"],"\x0B"=>[18,"le"],"\x0C"=>[77,"li"],"\x0D"=>[18,"li"],"\x0E"=>[77,"lo"],"\x0F"=>[18,"lo"],"\x10"=>[77,"ls"],"\x11"=>[18,"ls"],"\x12"=>[77,"lt"],"\x13"=>[18,"lt"],"\x14"=>[0,"l\x20"],"\x15"=>[0,"l\x25"],"\x16"=>[0,"l-"],"\x17"=>[0,"l."],"\x18"=>[0,"l\x2F"],"\x19"=>[0,"l3"],"\x1A"=>[0,"l4"],"\x1B"=>[0,"l5"],"\x1C"=>[0,"l6"],"\x1D"=>[0,"l7"],"\x1E"=>[0,"l8"],"\x1F"=>[0,"l9"],"\x20"=>[0,"l\x3D"],"\x21"=>[0,"lA"],"\x22"=>[0,"l_"],"\x23"=>[0,"lb"],"\x24"=>[0,"ld"],"\x25"=>[0,"lf"],"\x26"=>[0,"lg"],"\x27"=>[0,"lh"],"\x28"=>[0,"ll"],"\x29"=>[0,"lm"],"\x2A"=>[0,"ln"],"\x2B"=>[0,"lp"],"\x2C"=>[0,"lr"],"-"=>[0,"lu"],"."=>[100,"l"],"\x2F"=>[110,"l"],[111,"l"],[115,"l"],[116,"l"],[118,"l"],[119,"l"],[122,"l"],[123,"l"],[125,"l"],[126,"l"],[129,"l"],"\x3A"=>[143,"l"],"\x3B"=>[148,"l"],"\x3C"=>[151,"l"],"\x3D"=>[153,"l"],"\x3E"=>[83,"l"],"\x3F"=>[10,"l"],"\x40"=>[77,"m0"],"A"=>[18,"m0"],"B"=>[77,"m1"],"C"=>[18,"m1"],"D"=>[77,"m2"],"E"=>[18,"m2"],"F"=>[77,"ma"],"G"=>[18,"ma"],"H"=>[77,"mc"],"I"=>[18,"mc"],"J"=>[77,"me"],"K"=>[18,"me"],"L"=>[77,"mi"],"M"=>[18,"mi"],"N"=>[77,"mo"],"O"=>[18,"mo"],"P"=>[77,"ms"],"Q"=>[18,"ms"],"R"=>[77,"mt"],"S"=>[18,"mt"],"T"=>[0,"m\x20"],"U"=>[0,"m\x25"],"V"=>[0,"m-"],"W"=>[0,"m."],"X"=>[0,"m\x2F"],"Y"=>[0,"m3"],"Z"=>[0,"m4"],"\x5B"=>[0,"m5"],"\x5C"=>[0,"m6"],"\x5D"=>[0,"m7"],"\x5E"=>[0,"m8"],"_"=>[0,"m9"],"\x60"=>[0,"m\x3D"],"a"=>[0,"mA"],"b"=>[0,"m_"],"c"=>[0,"mb"],"d"=>[0,"md"],"e"=>[0,"mf"],"f"=>[0,"mg"],"g"=>[0,"mh"],"h"=>[0,"ml"],"i"=>[0,"mm"],"j"=>[0,"mn"],"k"=>[0,"mp"],"l"=>[0,"mr"],"m"=>[0,"mu"],"n"=>[100,"m"],"o"=>[110,"m"],"p"=>[111,"m"],"q"=>[115,"m"],"r"=>[116,"m"],"s"=>[118,"m"],"t"=>[119,"m"],"u"=>[122,"m"],"v"=>[123,"m"],"w"=>[125,"m"],"x"=>[126,"m"],"y"=>[129,"m"],"z"=>[143,"m"],"\x7B"=>[148,"m"],"\x7C"=>[151,"m"],"\x7D"=>[153,"m"],"~"=>[83,"m"],"\x7F"=>[10,"m"],"\x80"=>[77,"n0"],"\x81"=>[18,"n0"],"\x82"=>[77,"n1"],"\x83"=>[18,"n1"],"\x84"=>[77,"n2"],"\x85"=>[18,"n2"],"\x86"=>[77,"na"],"\x87"=>[18,"na"],"\x88"=>[77,"nc"],"\x89"=>[18,"nc"],"\x8A"=>[77,"ne"],"\x8B"=>[18,"ne"],"\x8C"=>[77,"ni"],"\x8D"=>[18,"ni"],"\x8E"=>[77,"no"],"\x8F"=>[18,"no"],"\x90"=>[77,"ns"],"\x91"=>[18,"ns"],"\x92"=>[77,"nt"],"\x93"=>[18,"nt"],"\x94"=>[0,"n\x20"],"\x95"=>[0,"n\x25"],"\x96"=>[0,"n-"],"\x97"=>[0,"n."],"\x98"=>[0,"n\x2F"],"\x99"=>[0,"n3"],"\x9A"=>[0,"n4"],"\x9B"=>[0,"n5"],"\x9C"=>[0,"n6"],"\x9D"=>[0,"n7"],"\x9E"=>[0,"n8"],"\x9F"=>[0,"n9"],"\xA0"=>[0,"n\x3D"],"\xA1"=>[0,"nA"],"\xA2"=>[0,"n_"],"\xA3"=>[0,"nb"],"\xA4"=>[0,"nd"],"\xA5"=>[0,"nf"],"\xA6"=>[0,"ng"],"\xA7"=>[0,"nh"],"\xA8"=>[0,"nl"],"\xA9"=>[0,"nm"],"\xAA"=>[0,"nn"],"\xAB"=>[0,"np"],"\xAC"=>[0,"nr"],"\xAD"=>[0,"nu"],"\xAE"=>[100,"n"],"\xAF"=>[110,"n"],"\xB0"=>[111,"n"],"\xB1"=>[115,"n"],"\xB2"=>[116,"n"],"\xB3"=>[118,"n"],"\xB4"=>[119,"n"],"\xB5"=>[122,"n"],"\xB6"=>[123,"n"],"\xB7"=>[125,"n"],"\xB8"=>[126,"n"],"\xB9"=>[129,"n"],"\xBA"=>[143,"n"],"\xBB"=>[148,"n"],"\xBC"=>[151,"n"],"\xBD"=>[153,"n"],"\xBE"=>[83,"n"],"\xBF"=>[10,"n"],"\xC0"=>[77,"p0"],"\xC1"=>[18,"p0"],"\xC2"=>[77,"p1"],"\xC3"=>[18,"p1"],"\xC4"=>[77,"p2"],"\xC5"=>[18,"p2"],"\xC6"=>[77,"pa"],"\xC7"=>[18,"pa"],"\xC8"=>[77,"pc"],"\xC9"=>[18,"pc"],"\xCA"=>[77,"pe"],"\xCB"=>[18,"pe"],"\xCC"=>[77,"pi"],"\xCD"=>[18,"pi"],"\xCE"=>[77,"po"],"\xCF"=>[18,"po"],"\xD0"=>[77,"ps"],"\xD1"=>[18,"ps"],"\xD2"=>[77,"pt"],"\xD3"=>[18,"pt"],"\xD4"=>[0,"p\x20"],"\xD5"=>[0,"p\x25"],"\xD6"=>[0,"p-"],"\xD7"=>[0,"p."],"\xD8"=>[0,"p\x2F"],"\xD9"=>[0,"p3"],"\xDA"=>[0,"p4"],"\xDB"=>[0,"p5"],"\xDC"=>[0,"p6"],"\xDD"=>[0,"p7"],"\xDE"=>[0,"p8"],"\xDF"=>[0,"p9"],"\xE0"=>[0,"p\x3D"],"\xE1"=>[0,"pA"],"\xE2"=>[0,"p_"],"\xE3"=>[0,"pb"],"\xE4"=>[0,"pd"],"\xE5"=>[0,"pf"],"\xE6"=>[0,"pg"],"\xE7"=>[0,"ph"],"\xE8"=>[0,"pl"],"\xE9"=>[0,"pm"],"\xEA"=>[0,"pn"],"\xEB"=>[0,"pp"],"\xEC"=>[0,"pr"],"\xED"=>[0,"pu"],"\xEE"=>[100,"p"],"\xEF"=>[110,"p"],"\xF0"=>[111,"p"],"\xF1"=>[115,"p"],"\xF2"=>[116,"p"],"\xF3"=>[118,"p"],"\xF4"=>[119,"p"],"\xF5"=>[122,"p"],"\xF6"=>[123,"p"],"\xF7"=>[125,"p"],"\xF8"=>[126,"p"],"\xF9"=>[129,"p"],"\xFA"=>[143,"p"],"\xFB"=>[148,"p"],"\xFC"=>[151,"p"],"\xFD"=>[153,"p"],"\xFE"=>[83,"p"],"\xFF"=>[10,"p"],],["\x00"=>[94,"n0"],"\x01"=>[76,"n0"],"\x02"=>[104,"n0"],"\x03"=>[16,"n0"],"\x04"=>[94,"n1"],"\x05"=>[76,"n1"],"\x06"=>[104,"n1"],"\x07"=>[16,"n1"],"\x08"=>[94,"n2"],"\x09"=>[76,"n2"],"\x0A"=>[104,"n2"],"\x0B"=>[16,"n2"],"\x0C"=>[94,"na"],"\x0D"=>[76,"na"],"\x0E"=>[104,"na"],"\x0F"=>[16,"na"],"\x10"=>[94,"nc"],"\x11"=>[76,"nc"],"\x12"=>[104,"nc"],"\x13"=>[16,"nc"],"\x14"=>[94,"ne"],"\x15"=>[76,"ne"],"\x16"=>[104,"ne"],"\x17"=>[16,"ne"],"\x18"=>[94,"ni"],"\x19"=>[76,"ni"],"\x1A"=>[104,"ni"],"\x1B"=>[16,"ni"],"\x1C"=>[94,"no"],"\x1D"=>[76,"no"],"\x1E"=>[104,"no"],"\x1F"=>[16,"no"],"\x20"=>[94,"ns"],"\x21"=>[76,"ns"],"\x22"=>[104,"ns"],"\x23"=>[16,"ns"],"\x24"=>[94,"nt"],"\x25"=>[76,"nt"],"\x26"=>[104,"nt"],"\x27"=>[16,"nt"],"\x28"=>[77,"n\x20"],"\x29"=>[18,"n\x20"],"\x2A"=>[77,"n\x25"],"\x2B"=>[18,"n\x25"],"\x2C"=>[77,"n-"],"-"=>[18,"n-"],"."=>[77,"n."],"\x2F"=>[18,"n."],[77,"n\x2F"],[18,"n\x2F"],[77,"n3"],[18,"n3"],[77,"n4"],[18,"n4"],[77,"n5"],[18,"n5"],[77,"n6"],[18,"n6"],"\x3A"=>[77,"n7"],"\x3B"=>[18,"n7"],"\x3C"=>[77,"n8"],"\x3D"=>[18,"n8"],"\x3E"=>[77,"n9"],"\x3F"=>[18,"n9"],"\x40"=>[77,"n\x3D"],"A"=>[18,"n\x3D"],"B"=>[77,"nA"],"C"=>[18,"nA"],"D"=>[77,"n_"],"E"=>[18,"n_"],"F"=>[77,"nb"],"G"=>[18,"nb"],"H"=>[77,"nd"],"I"=>[18,"nd"],"J"=>[77,"nf"],"K"=>[18,"nf"],"L"=>[77,"ng"],"M"=>[18,"ng"],"N"=>[77,"nh"],"O"=>[18,"nh"],"P"=>[77,"nl"],"Q"=>[18,"nl"],"R"=>[77,"nm"],"S"=>[18,"nm"],"T"=>[77,"nn"],"U"=>[18,"nn"],"V"=>[77,"np"],"W"=>[18,"np"],"X"=>[77,"nr"],"Y"=>[18,"nr"],"Z"=>[77,"nu"],"\x5B"=>[18,"nu"],"\x5C"=>[0,"n\x3A"],"\x5D"=>[0,"nB"],"\x5E"=>[0,"nC"],"_"=>[0,"nD"],"\x60"=>[0,"nE"],"a"=>[0,"nF"],"b"=>[0,"nG"],"c"=>[0,"nH"],"d"=>[0,"nI"],"e"=>[0,"nJ"],"f"=>[0,"nK"],"g"=>[0,"nL"],"h"=>[0,"nM"],"i"=>[0,"nN"],"j"=>[0,"nO"],"k"=>[0,"nP"],"l"=>[0,"nQ"],"m"=>[0,"nR"],"n"=>[0,"nS"],"o"=>[0,"nT"],"p"=>[0,"nU"],"q"=>[0,"nV"],"r"=>[0,"nW"],"s"=>[0,"nY"],"t"=>[0,"nj"],"u"=>[0,"nk"],"v"=>[0,"nq"],"w"=>[0,"nv"],"x"=>[0,"nw"],"y"=>[0,"nx"],"z"=>[0,"ny"],"\x7B"=>[0,"nz"],"\x7C"=>[82,"n"],"\x7D"=>[87,"n"],"~"=>[130,"n"],"\x7F"=>[9,"n"],"\x80"=>[94,"p0"],"\x81"=>[76,"p0"],"\x82"=>[104,"p0"],"\x83"=>[16,"p0"],"\x84"=>[94,"p1"],"\x85"=>[76,"p1"],"\x86"=>[104,"p1"],"\x87"=>[16,"p1"],"\x88"=>[94,"p2"],"\x89"=>[76,"p2"],"\x8A"=>[104,"p2"],"\x8B"=>[16,"p2"],"\x8C"=>[94,"pa"],"\x8D"=>[76,"pa"],"\x8E"=>[104,"pa"],"\x8F"=>[16,"pa"],"\x90"=>[94,"pc"],"\x91"=>[76,"pc"],"\x92"=>[104,"pc"],"\x93"=>[16,"pc"],"\x94"=>[94,"pe"],"\x95"=>[76,"pe"],"\x96"=>[104,"pe"],"\x97"=>[16,"pe"],"\x98"=>[94,"pi"],"\x99"=>[76,"pi"],"\x9A"=>[104,"pi"],"\x9B"=>[16,"pi"],"\x9C"=>[94,"po"],"\x9D"=>[76,"po"],"\x9E"=>[104,"po"],"\x9F"=>[16,"po"],"\xA0"=>[94,"ps"],"\xA1"=>[76,"ps"],"\xA2"=>[104,"ps"],"\xA3"=>[16,"ps"],"\xA4"=>[94,"pt"],"\xA5"=>[76,"pt"],"\xA6"=>[104,"pt"],"\xA7"=>[16,"pt"],"\xA8"=>[77,"p\x20"],"\xA9"=>[18,"p\x20"],"\xAA"=>[77,"p\x25"],"\xAB"=>[18,"p\x25"],"\xAC"=>[77,"p-"],"\xAD"=>[18,"p-"],"\xAE"=>[77,"p."],"\xAF"=>[18,"p."],"\xB0"=>[77,"p\x2F"],"\xB1"=>[18,"p\x2F"],"\xB2"=>[77,"p3"],"\xB3"=>[18,"p3"],"\xB4"=>[77,"p4"],"\xB5"=>[18,"p4"],"\xB6"=>[77,"p5"],"\xB7"=>[18,"p5"],"\xB8"=>[77,"p6"],"\xB9"=>[18,"p6"],"\xBA"=>[77,"p7"],"\xBB"=>[18,"p7"],"\xBC"=>[77,"p8"],"\xBD"=>[18,"p8"],"\xBE"=>[77,"p9"],"\xBF"=>[18,"p9"],"\xC0"=>[77,"p\x3D"],"\xC1"=>[18,"p\x3D"],"\xC2"=>[77,"pA"],"\xC3"=>[18,"pA"],"\xC4"=>[77,"p_"],"\xC5"=>[18,"p_"],"\xC6"=>[77,"pb"],"\xC7"=>[18,"pb"],"\xC8"=>[77,"pd"],"\xC9"=>[18,"pd"],"\xCA"=>[77,"pf"],"\xCB"=>[18,"pf"],"\xCC"=>[77,"pg"],"\xCD"=>[18,"pg"],"\xCE"=>[77,"ph"],"\xCF"=>[18,"ph"],"\xD0"=>[77,"pl"],"\xD1"=>[18,"pl"],"\xD2"=>[77,"pm"],"\xD3"=>[18,"pm"],"\xD4"=>[77,"pn"],"\xD5"=>[18,"pn"],"\xD6"=>[77,"pp"],"\xD7"=>[18,"pp"],"\xD8"=>[77,"pr"],"\xD9"=>[18,"pr"],"\xDA"=>[77,"pu"],"\xDB"=>[18,"pu"],"\xDC"=>[0,"p\x3A"],"\xDD"=>[0,"pB"],"\xDE"=>[0,"pC"],"\xDF"=>[0,"pD"],"\xE0"=>[0,"pE"],"\xE1"=>[0,"pF"],"\xE2"=>[0,"pG"],"\xE3"=>[0,"pH"],"\xE4"=>[0,"pI"],"\xE5"=>[0,"pJ"],"\xE6"=>[0,"pK"],"\xE7"=>[0,"pL"],"\xE8"=>[0,"pM"],"\xE9"=>[0,"pN"],"\xEA"=>[0,"pO"],"\xEB"=>[0,"pP"],"\xEC"=>[0,"pQ"],"\xED"=>[0,"pR"],"\xEE"=>[0,"pS"],"\xEF"=>[0,"pT"],"\xF0"=>[0,"pU"],"\xF1"=>[0,"pV"],"\xF2"=>[0,"pW"],"\xF3"=>[0,"pY"],"\xF4"=>[0,"pj"],"\xF5"=>[0,"pk"],"\xF6"=>[0,"pq"],"\xF7"=>[0,"pv"],"\xF8"=>[0,"pw"],"\xF9"=>[0,"px"],"\xFA"=>[0,"py"],"\xFB"=>[0,"pz"],"\xFC"=>[82,"p"],"\xFD"=>[87,"p"],"\xFE"=>[130,"p"],"\xFF"=>[9,"p"],],["\x00"=>[94,"q0"],"\x01"=>[76,"q0"],"\x02"=>[104,"q0"],"\x03"=>[16,"q0"],"\x04"=>[94,"q1"],"\x05"=>[76,"q1"],"\x06"=>[104,"q1"],"\x07"=>[16,"q1"],"\x08"=>[94,"q2"],"\x09"=>[76,"q2"],"\x0A"=>[104,"q2"],"\x0B"=>[16,"q2"],"\x0C"=>[94,"qa"],"\x0D"=>[76,"qa"],"\x0E"=>[104,"qa"],"\x0F"=>[16,"qa"],"\x10"=>[94,"qc"],"\x11"=>[76,"qc"],"\x12"=>[104,"qc"],"\x13"=>[16,"qc"],"\x14"=>[94,"qe"],"\x15"=>[76,"qe"],"\x16"=>[104,"qe"],"\x17"=>[16,"qe"],"\x18"=>[94,"qi"],"\x19"=>[76,"qi"],"\x1A"=>[104,"qi"],"\x1B"=>[16,"qi"],"\x1C"=>[94,"qo"],"\x1D"=>[76,"qo"],"\x1E"=>[104,"qo"],"\x1F"=>[16,"qo"],"\x20"=>[94,"qs"],"\x21"=>[76,"qs"],"\x22"=>[104,"qs"],"\x23"=>[16,"qs"],"\x24"=>[94,"qt"],"\x25"=>[76,"qt"],"\x26"=>[104,"qt"],"\x27"=>[16,"qt"],"\x28"=>[77,"q\x20"],"\x29"=>[18,"q\x20"],"\x2A"=>[77,"q\x25"],"\x2B"=>[18,"q\x25"],"\x2C"=>[77,"q-"],"-"=>[18,"q-"],"."=>[77,"q."],"\x2F"=>[18,"q."],[77,"q\x2F"],[18,"q\x2F"],[77,"q3"],[18,"q3"],[77,"q4"],[18,"q4"],[77,"q5"],[18,"q5"],[77,"q6"],[18,"q6"],"\x3A"=>[77,"q7"],"\x3B"=>[18,"q7"],"\x3C"=>[77,"q8"],"\x3D"=>[18,"q8"],"\x3E"=>[77,"q9"],"\x3F"=>[18,"q9"],"\x40"=>[77,"q\x3D"],"A"=>[18,"q\x3D"],"B"=>[77,"qA"],"C"=>[18,"qA"],"D"=>[77,"q_"],"E"=>[18,"q_"],"F"=>[77,"qb"],"G"=>[18,"qb"],"H"=>[77,"qd"],"I"=>[18,"qd"],"J"=>[77,"qf"],"K"=>[18,"qf"],"L"=>[77,"qg"],"M"=>[18,"qg"],"N"=>[77,"qh"],"O"=>[18,"qh"],"P"=>[77,"ql"],"Q"=>[18,"ql"],"R"=>[77,"qm"],"S"=>[18,"qm"],"T"=>[77,"qn"],"U"=>[18,"qn"],"V"=>[77,"qp"],"W"=>[18,"qp"],"X"=>[77,"qr"],"Y"=>[18,"qr"],"Z"=>[77,"qu"],"\x5B"=>[18,"qu"],"\x5C"=>[0,"q\x3A"],"\x5D"=>[0,"qB"],"\x5E"=>[0,"qC"],"_"=>[0,"qD"],"\x60"=>[0,"qE"],"a"=>[0,"qF"],"b"=>[0,"qG"],"c"=>[0,"qH"],"d"=>[0,"qI"],"e"=>[0,"qJ"],"f"=>[0,"qK"],"g"=>[0,"qL"],"h"=>[0,"qM"],"i"=>[0,"qN"],"j"=>[0,"qO"],"k"=>[0,"qP"],"l"=>[0,"qQ"],"m"=>[0,"qR"],"n"=>[0,"qS"],"o"=>[0,"qT"],"p"=>[0,"qU"],"q"=>[0,"qV"],"r"=>[0,"qW"],"s"=>[0,"qY"],"t"=>[0,"qj"],"u"=>[0,"qk"],"v"=>[0,"qq"],"w"=>[0,"qv"],"x"=>[0,"qw"],"y"=>[0,"qx"],"z"=>[0,"qy"],"\x7B"=>[0,"qz"],"\x7C"=>[82,"q"],"\x7D"=>[87,"q"],"~"=>[130,"q"],"\x7F"=>[9,"q"],"\x80"=>[94,"v0"],"\x81"=>[76,"v0"],"\x82"=>[104,"v0"],"\x83"=>[16,"v0"],"\x84"=>[94,"v1"],"\x85"=>[76,"v1"],"\x86"=>[104,"v1"],"\x87"=>[16,"v1"],"\x88"=>[94,"v2"],"\x89"=>[76,"v2"],"\x8A"=>[104,"v2"],"\x8B"=>[16,"v2"],"\x8C"=>[94,"va"],"\x8D"=>[76,"va"],"\x8E"=>[104,"va"],"\x8F"=>[16,"va"],"\x90"=>[94,"vc"],"\x91"=>[76,"vc"],"\x92"=>[104,"vc"],"\x93"=>[16,"vc"],"\x94"=>[94,"ve"],"\x95"=>[76,"ve"],"\x96"=>[104,"ve"],"\x97"=>[16,"ve"],"\x98"=>[94,"vi"],"\x99"=>[76,"vi"],"\x9A"=>[104,"vi"],"\x9B"=>[16,"vi"],"\x9C"=>[94,"vo"],"\x9D"=>[76,"vo"],"\x9E"=>[104,"vo"],"\x9F"=>[16,"vo"],"\xA0"=>[94,"vs"],"\xA1"=>[76,"vs"],"\xA2"=>[104,"vs"],"\xA3"=>[16,"vs"],"\xA4"=>[94,"vt"],"\xA5"=>[76,"vt"],"\xA6"=>[104,"vt"],"\xA7"=>[16,"vt"],"\xA8"=>[77,"v\x20"],"\xA9"=>[18,"v\x20"],"\xAA"=>[77,"v\x25"],"\xAB"=>[18,"v\x25"],"\xAC"=>[77,"v-"],"\xAD"=>[18,"v-"],"\xAE"=>[77,"v."],"\xAF"=>[18,"v."],"\xB0"=>[77,"v\x2F"],"\xB1"=>[18,"v\x2F"],"\xB2"=>[77,"v3"],"\xB3"=>[18,"v3"],"\xB4"=>[77,"v4"],"\xB5"=>[18,"v4"],"\xB6"=>[77,"v5"],"\xB7"=>[18,"v5"],"\xB8"=>[77,"v6"],"\xB9"=>[18,"v6"],"\xBA"=>[77,"v7"],"\xBB"=>[18,"v7"],"\xBC"=>[77,"v8"],"\xBD"=>[18,"v8"],"\xBE"=>[77,"v9"],"\xBF"=>[18,"v9"],"\xC0"=>[77,"v\x3D"],"\xC1"=>[18,"v\x3D"],"\xC2"=>[77,"vA"],"\xC3"=>[18,"vA"],"\xC4"=>[77,"v_"],"\xC5"=>[18,"v_"],"\xC6"=>[77,"vb"],"\xC7"=>[18,"vb"],"\xC8"=>[77,"vd"],"\xC9"=>[18,"vd"],"\xCA"=>[77,"vf"],"\xCB"=>[18,"vf"],"\xCC"=>[77,"vg"],"\xCD"=>[18,"vg"],"\xCE"=>[77,"vh"],"\xCF"=>[18,"vh"],"\xD0"=>[77,"vl"],"\xD1"=>[18,"vl"],"\xD2"=>[77,"vm"],"\xD3"=>[18,"vm"],"\xD4"=>[77,"vn"],"\xD5"=>[18,"vn"],"\xD6"=>[77,"vp"],"\xD7"=>[18,"vp"],"\xD8"=>[77,"vr"],"\xD9"=>[18,"vr"],"\xDA"=>[77,"vu"],"\xDB"=>[18,"vu"],"\xDC"=>[0,"v\x3A"],"\xDD"=>[0,"vB"],"\xDE"=>[0,"vC"],"\xDF"=>[0,"vD"],"\xE0"=>[0,"vE"],"\xE1"=>[0,"vF"],"\xE2"=>[0,"vG"],"\xE3"=>[0,"vH"],"\xE4"=>[0,"vI"],"\xE5"=>[0,"vJ"],"\xE6"=>[0,"vK"],"\xE7"=>[0,"vL"],"\xE8"=>[0,"vM"],"\xE9"=>[0,"vN"],"\xEA"=>[0,"vO"],"\xEB"=>[0,"vP"],"\xEC"=>[0,"vQ"],"\xED"=>[0,"vR"],"\xEE"=>[0,"vS"],"\xEF"=>[0,"vT"],"\xF0"=>[0,"vU"],"\xF1"=>[0,"vV"],"\xF2"=>[0,"vW"],"\xF3"=>[0,"vY"],"\xF4"=>[0,"vj"],"\xF5"=>[0,"vk"],"\xF6"=>[0,"vq"],"\xF7"=>[0,"vv"],"\xF8"=>[0,"vw"],"\xF9"=>[0,"vx"],"\xFA"=>[0,"vy"],"\xFB"=>[0,"vz"],"\xFC"=>[82,"v"],"\xFD"=>[87,"v"],"\xFE"=>[130,"v"],"\xFF"=>[9,"v"],],["\x00"=>[94,"r0"],"\x01"=>[76,"r0"],"\x02"=>[104,"r0"],"\x03"=>[16,"r0"],"\x04"=>[94,"r1"],"\x05"=>[76,"r1"],"\x06"=>[104,"r1"],"\x07"=>[16,"r1"],"\x08"=>[94,"r2"],"\x09"=>[76,"r2"],"\x0A"=>[104,"r2"],"\x0B"=>[16,"r2"],"\x0C"=>[94,"ra"],"\x0D"=>[76,"ra"],"\x0E"=>[104,"ra"],"\x0F"=>[16,"ra"],"\x10"=>[94,"rc"],"\x11"=>[76,"rc"],"\x12"=>[104,"rc"],"\x13"=>[16,"rc"],"\x14"=>[94,"re"],"\x15"=>[76,"re"],"\x16"=>[104,"re"],"\x17"=>[16,"re"],"\x18"=>[94,"ri"],"\x19"=>[76,"ri"],"\x1A"=>[104,"ri"],"\x1B"=>[16,"ri"],"\x1C"=>[94,"ro"],"\x1D"=>[76,"ro"],"\x1E"=>[104,"ro"],"\x1F"=>[16,"ro"],"\x20"=>[94,"rs"],"\x21"=>[76,"rs"],"\x22"=>[104,"rs"],"\x23"=>[16,"rs"],"\x24"=>[94,"rt"],"\x25"=>[76,"rt"],"\x26"=>[104,"rt"],"\x27"=>[16,"rt"],"\x28"=>[77,"r\x20"],"\x29"=>[18,"r\x20"],"\x2A"=>[77,"r\x25"],"\x2B"=>[18,"r\x25"],"\x2C"=>[77,"r-"],"-"=>[18,"r-"],"."=>[77,"r."],"\x2F"=>[18,"r."],[77,"r\x2F"],[18,"r\x2F"],[77,"r3"],[18,"r3"],[77,"r4"],[18,"r4"],[77,"r5"],[18,"r5"],[77,"r6"],[18,"r6"],"\x3A"=>[77,"r7"],"\x3B"=>[18,"r7"],"\x3C"=>[77,"r8"],"\x3D"=>[18,"r8"],"\x3E"=>[77,"r9"],"\x3F"=>[18,"r9"],"\x40"=>[77,"r\x3D"],"A"=>[18,"r\x3D"],"B"=>[77,"rA"],"C"=>[18,"rA"],"D"=>[77,"r_"],"E"=>[18,"r_"],"F"=>[77,"rb"],"G"=>[18,"rb"],"H"=>[77,"rd"],"I"=>[18,"rd"],"J"=>[77,"rf"],"K"=>[18,"rf"],"L"=>[77,"rg"],"M"=>[18,"rg"],"N"=>[77,"rh"],"O"=>[18,"rh"],"P"=>[77,"rl"],"Q"=>[18,"rl"],"R"=>[77,"rm"],"S"=>[18,"rm"],"T"=>[77,"rn"],"U"=>[18,"rn"],"V"=>[77,"rp"],"W"=>[18,"rp"],"X"=>[77,"rr"],"Y"=>[18,"rr"],"Z"=>[77,"ru"],"\x5B"=>[18,"ru"],"\x5C"=>[0,"r\x3A"],"\x5D"=>[0,"rB"],"\x5E"=>[0,"rC"],"_"=>[0,"rD"],"\x60"=>[0,"rE"],"a"=>[0,"rF"],"b"=>[0,"rG"],"c"=>[0,"rH"],"d"=>[0,"rI"],"e"=>[0,"rJ"],"f"=>[0,"rK"],"g"=>[0,"rL"],"h"=>[0,"rM"],"i"=>[0,"rN"],"j"=>[0,"rO"],"k"=>[0,"rP"],"l"=>[0,"rQ"],"m"=>[0,"rR"],"n"=>[0,"rS"],"o"=>[0,"rT"],"p"=>[0,"rU"],"q"=>[0,"rV"],"r"=>[0,"rW"],"s"=>[0,"rY"],"t"=>[0,"rj"],"u"=>[0,"rk"],"v"=>[0,"rq"],"w"=>[0,"rv"],"x"=>[0,"rw"],"y"=>[0,"rx"],"z"=>[0,"ry"],"\x7B"=>[0,"rz"],"\x7C"=>[82,"r"],"\x7D"=>[87,"r"],"~"=>[130,"r"],"\x7F"=>[9,"r"],"\x80"=>[94,"u0"],"\x81"=>[76,"u0"],"\x82"=>[104,"u0"],"\x83"=>[16,"u0"],"\x84"=>[94,"u1"],"\x85"=>[76,"u1"],"\x86"=>[104,"u1"],"\x87"=>[16,"u1"],"\x88"=>[94,"u2"],"\x89"=>[76,"u2"],"\x8A"=>[104,"u2"],"\x8B"=>[16,"u2"],"\x8C"=>[94,"ua"],"\x8D"=>[76,"ua"],"\x8E"=>[104,"ua"],"\x8F"=>[16,"ua"],"\x90"=>[94,"uc"],"\x91"=>[76,"uc"],"\x92"=>[104,"uc"],"\x93"=>[16,"uc"],"\x94"=>[94,"ue"],"\x95"=>[76,"ue"],"\x96"=>[104,"ue"],"\x97"=>[16,"ue"],"\x98"=>[94,"ui"],"\x99"=>[76,"ui"],"\x9A"=>[104,"ui"],"\x9B"=>[16,"ui"],"\x9C"=>[94,"uo"],"\x9D"=>[76,"uo"],"\x9E"=>[104,"uo"],"\x9F"=>[16,"uo"],"\xA0"=>[94,"us"],"\xA1"=>[76,"us"],"\xA2"=>[104,"us"],"\xA3"=>[16,"us"],"\xA4"=>[94,"ut"],"\xA5"=>[76,"ut"],"\xA6"=>[104,"ut"],"\xA7"=>[16,"ut"],"\xA8"=>[77,"u\x20"],"\xA9"=>[18,"u\x20"],"\xAA"=>[77,"u\x25"],"\xAB"=>[18,"u\x25"],"\xAC"=>[77,"u-"],"\xAD"=>[18,"u-"],"\xAE"=>[77,"u."],"\xAF"=>[18,"u."],"\xB0"=>[77,"u\x2F"],"\xB1"=>[18,"u\x2F"],"\xB2"=>[77,"u3"],"\xB3"=>[18,"u3"],"\xB4"=>[77,"u4"],"\xB5"=>[18,"u4"],"\xB6"=>[77,"u5"],"\xB7"=>[18,"u5"],"\xB8"=>[77,"u6"],"\xB9"=>[18,"u6"],"\xBA"=>[77,"u7"],"\xBB"=>[18,"u7"],"\xBC"=>[77,"u8"],"\xBD"=>[18,"u8"],"\xBE"=>[77,"u9"],"\xBF"=>[18,"u9"],"\xC0"=>[77,"u\x3D"],"\xC1"=>[18,"u\x3D"],"\xC2"=>[77,"uA"],"\xC3"=>[18,"uA"],"\xC4"=>[77,"u_"],"\xC5"=>[18,"u_"],"\xC6"=>[77,"ub"],"\xC7"=>[18,"ub"],"\xC8"=>[77,"ud"],"\xC9"=>[18,"ud"],"\xCA"=>[77,"uf"],"\xCB"=>[18,"uf"],"\xCC"=>[77,"ug"],"\xCD"=>[18,"ug"],"\xCE"=>[77,"uh"],"\xCF"=>[18,"uh"],"\xD0"=>[77,"ul"],"\xD1"=>[18,"ul"],"\xD2"=>[77,"um"],"\xD3"=>[18,"um"],"\xD4"=>[77,"un"],"\xD5"=>[18,"un"],"\xD6"=>[77,"up"],"\xD7"=>[18,"up"],"\xD8"=>[77,"ur"],"\xD9"=>[18,"ur"],"\xDA"=>[77,"uu"],"\xDB"=>[18,"uu"],"\xDC"=>[0,"u\x3A"],"\xDD"=>[0,"uB"],"\xDE"=>[0,"uC"],"\xDF"=>[0,"uD"],"\xE0"=>[0,"uE"],"\xE1"=>[0,"uF"],"\xE2"=>[0,"uG"],"\xE3"=>[0,"uH"],"\xE4"=>[0,"uI"],"\xE5"=>[0,"uJ"],"\xE6"=>[0,"uK"],"\xE7"=>[0,"uL"],"\xE8"=>[0,"uM"],"\xE9"=>[0,"uN"],"\xEA"=>[0,"uO"],"\xEB"=>[0,"uP"],"\xEC"=>[0,"uQ"],"\xED"=>[0,"uR"],"\xEE"=>[0,"uS"],"\xEF"=>[0,"uT"],"\xF0"=>[0,"uU"],"\xF1"=>[0,"uV"],"\xF2"=>[0,"uW"],"\xF3"=>[0,"uY"],"\xF4"=>[0,"uj"],"\xF5"=>[0,"uk"],"\xF6"=>[0,"uq"],"\xF7"=>[0,"uv"],"\xF8"=>[0,"uw"],"\xF9"=>[0,"ux"],"\xFA"=>[0,"uy"],"\xFB"=>[0,"uz"],"\xFC"=>[82,"u"],"\xFD"=>[87,"u"],"\xFE"=>[130,"u"],"\xFF"=>[9,"u"],],["\x00"=>[94,"s0"],"\x01"=>[76,"s0"],"\x02"=>[104,"s0"],"\x03"=>[16,"s0"],"\x04"=>[94,"s1"],"\x05"=>[76,"s1"],"\x06"=>[104,"s1"],"\x07"=>[16,"s1"],"\x08"=>[94,"s2"],"\x09"=>[76,"s2"],"\x0A"=>[104,"s2"],"\x0B"=>[16,"s2"],"\x0C"=>[94,"sa"],"\x0D"=>[76,"sa"],"\x0E"=>[104,"sa"],"\x0F"=>[16,"sa"],"\x10"=>[94,"sc"],"\x11"=>[76,"sc"],"\x12"=>[104,"sc"],"\x13"=>[16,"sc"],"\x14"=>[94,"se"],"\x15"=>[76,"se"],"\x16"=>[104,"se"],"\x17"=>[16,"se"],"\x18"=>[94,"si"],"\x19"=>[76,"si"],"\x1A"=>[104,"si"],"\x1B"=>[16,"si"],"\x1C"=>[94,"so"],"\x1D"=>[76,"so"],"\x1E"=>[104,"so"],"\x1F"=>[16,"so"],"\x20"=>[94,"ss"],"\x21"=>[76,"ss"],"\x22"=>[104,"ss"],"\x23"=>[16,"ss"],"\x24"=>[94,"st"],"\x25"=>[76,"st"],"\x26"=>[104,"st"],"\x27"=>[16,"st"],"\x28"=>[77,"s\x20"],"\x29"=>[18,"s\x20"],"\x2A"=>[77,"s\x25"],"\x2B"=>[18,"s\x25"],"\x2C"=>[77,"s-"],"-"=>[18,"s-"],"."=>[77,"s."],"\x2F"=>[18,"s."],[77,"s\x2F"],[18,"s\x2F"],[77,"s3"],[18,"s3"],[77,"s4"],[18,"s4"],[77,"s5"],[18,"s5"],[77,"s6"],[18,"s6"],"\x3A"=>[77,"s7"],"\x3B"=>[18,"s7"],"\x3C"=>[77,"s8"],"\x3D"=>[18,"s8"],"\x3E"=>[77,"s9"],"\x3F"=>[18,"s9"],"\x40"=>[77,"s\x3D"],"A"=>[18,"s\x3D"],"B"=>[77,"sA"],"C"=>[18,"sA"],"D"=>[77,"s_"],"E"=>[18,"s_"],"F"=>[77,"sb"],"G"=>[18,"sb"],"H"=>[77,"sd"],"I"=>[18,"sd"],"J"=>[77,"sf"],"K"=>[18,"sf"],"L"=>[77,"sg"],"M"=>[18,"sg"],"N"=>[77,"sh"],"O"=>[18,"sh"],"P"=>[77,"sl"],"Q"=>[18,"sl"],"R"=>[77,"sm"],"S"=>[18,"sm"],"T"=>[77,"sn"],"U"=>[18,"sn"],"V"=>[77,"sp"],"W"=>[18,"sp"],"X"=>[77,"sr"],"Y"=>[18,"sr"],"Z"=>[77,"su"],"\x5B"=>[18,"su"],"\x5C"=>[0,"s\x3A"],"\x5D"=>[0,"sB"],"\x5E"=>[0,"sC"],"_"=>[0,"sD"],"\x60"=>[0,"sE"],"a"=>[0,"sF"],"b"=>[0,"sG"],"c"=>[0,"sH"],"d"=>[0,"sI"],"e"=>[0,"sJ"],"f"=>[0,"sK"],"g"=>[0,"sL"],"h"=>[0,"sM"],"i"=>[0,"sN"],"j"=>[0,"sO"],"k"=>[0,"sP"],"l"=>[0,"sQ"],"m"=>[0,"sR"],"n"=>[0,"sS"],"o"=>[0,"sT"],"p"=>[0,"sU"],"q"=>[0,"sV"],"r"=>[0,"sW"],"s"=>[0,"sY"],"t"=>[0,"sj"],"u"=>[0,"sk"],"v"=>[0,"sq"],"w"=>[0,"sv"],"x"=>[0,"sw"],"y"=>[0,"sx"],"z"=>[0,"sy"],"\x7B"=>[0,"sz"],"\x7C"=>[82,"s"],"\x7D"=>[87,"s"],"~"=>[130,"s"],"\x7F"=>[9,"s"],"\x80"=>[94,"t0"],"\x81"=>[76,"t0"],"\x82"=>[104,"t0"],"\x83"=>[16,"t0"],"\x84"=>[94,"t1"],"\x85"=>[76,"t1"],"\x86"=>[104,"t1"],"\x87"=>[16,"t1"],"\x88"=>[94,"t2"],"\x89"=>[76,"t2"],"\x8A"=>[104,"t2"],"\x8B"=>[16,"t2"],"\x8C"=>[94,"ta"],"\x8D"=>[76,"ta"],"\x8E"=>[104,"ta"],"\x8F"=>[16,"ta"],"\x90"=>[94,"tc"],"\x91"=>[76,"tc"],"\x92"=>[104,"tc"],"\x93"=>[16,"tc"],"\x94"=>[94,"te"],"\x95"=>[76,"te"],"\x96"=>[104,"te"],"\x97"=>[16,"te"],"\x98"=>[94,"ti"],"\x99"=>[76,"ti"],"\x9A"=>[104,"ti"],"\x9B"=>[16,"ti"],"\x9C"=>[94,"to"],"\x9D"=>[76,"to"],"\x9E"=>[104,"to"],"\x9F"=>[16,"to"],"\xA0"=>[94,"ts"],"\xA1"=>[76,"ts"],"\xA2"=>[104,"ts"],"\xA3"=>[16,"ts"],"\xA4"=>[94,"tt"],"\xA5"=>[76,"tt"],"\xA6"=>[104,"tt"],"\xA7"=>[16,"tt"],"\xA8"=>[77,"t\x20"],"\xA9"=>[18,"t\x20"],"\xAA"=>[77,"t\x25"],"\xAB"=>[18,"t\x25"],"\xAC"=>[77,"t-"],"\xAD"=>[18,"t-"],"\xAE"=>[77,"t."],"\xAF"=>[18,"t."],"\xB0"=>[77,"t\x2F"],"\xB1"=>[18,"t\x2F"],"\xB2"=>[77,"t3"],"\xB3"=>[18,"t3"],"\xB4"=>[77,"t4"],"\xB5"=>[18,"t4"],"\xB6"=>[77,"t5"],"\xB7"=>[18,"t5"],"\xB8"=>[77,"t6"],"\xB9"=>[18,"t6"],"\xBA"=>[77,"t7"],"\xBB"=>[18,"t7"],"\xBC"=>[77,"t8"],"\xBD"=>[18,"t8"],"\xBE"=>[77,"t9"],"\xBF"=>[18,"t9"],"\xC0"=>[77,"t\x3D"],"\xC1"=>[18,"t\x3D"],"\xC2"=>[77,"tA"],"\xC3"=>[18,"tA"],"\xC4"=>[77,"t_"],"\xC5"=>[18,"t_"],"\xC6"=>[77,"tb"],"\xC7"=>[18,"tb"],"\xC8"=>[77,"td"],"\xC9"=>[18,"td"],"\xCA"=>[77,"tf"],"\xCB"=>[18,"tf"],"\xCC"=>[77,"tg"],"\xCD"=>[18,"tg"],"\xCE"=>[77,"th"],"\xCF"=>[18,"th"],"\xD0"=>[77,"tl"],"\xD1"=>[18,"tl"],"\xD2"=>[77,"tm"],"\xD3"=>[18,"tm"],"\xD4"=>[77,"tn"],"\xD5"=>[18,"tn"],"\xD6"=>[77,"tp"],"\xD7"=>[18,"tp"],"\xD8"=>[77,"tr"],"\xD9"=>[18,"tr"],"\xDA"=>[77,"tu"],"\xDB"=>[18,"tu"],"\xDC"=>[0,"t\x3A"],"\xDD"=>[0,"tB"],"\xDE"=>[0,"tC"],"\xDF"=>[0,"tD"],"\xE0"=>[0,"tE"],"\xE1"=>[0,"tF"],"\xE2"=>[0,"tG"],"\xE3"=>[0,"tH"],"\xE4"=>[0,"tI"],"\xE5"=>[0,"tJ"],"\xE6"=>[0,"tK"],"\xE7"=>[0,"tL"],"\xE8"=>[0,"tM"],"\xE9"=>[0,"tN"],"\xEA"=>[0,"tO"],"\xEB"=>[0,"tP"],"\xEC"=>[0,"tQ"],"\xED"=>[0,"tR"],"\xEE"=>[0,"tS"],"\xEF"=>[0,"tT"],"\xF0"=>[0,"tU"],"\xF1"=>[0,"tV"],"\xF2"=>[0,"tW"],"\xF3"=>[0,"tY"],"\xF4"=>[0,"tj"],"\xF5"=>[0,"tk"],"\xF6"=>[0,"tq"],"\xF7"=>[0,"tv"],"\xF8"=>[0,"tw"],"\xF9"=>[0,"tx"],"\xFA"=>[0,"ty"],"\xFB"=>[0,"tz"],"\xFC"=>[82,"t"],"\xFD"=>[87,"t"],"\xFE"=>[130,"t"],"\xFF"=>[9,"t"],],["\x00"=>[94,"w0"],"\x01"=>[76,"w0"],"\x02"=>[104,"w0"],"\x03"=>[16,"w0"],"\x04"=>[94,"w1"],"\x05"=>[76,"w1"],"\x06"=>[104,"w1"],"\x07"=>[16,"w1"],"\x08"=>[94,"w2"],"\x09"=>[76,"w2"],"\x0A"=>[104,"w2"],"\x0B"=>[16,"w2"],"\x0C"=>[94,"wa"],"\x0D"=>[76,"wa"],"\x0E"=>[104,"wa"],"\x0F"=>[16,"wa"],"\x10"=>[94,"wc"],"\x11"=>[76,"wc"],"\x12"=>[104,"wc"],"\x13"=>[16,"wc"],"\x14"=>[94,"we"],"\x15"=>[76,"we"],"\x16"=>[104,"we"],"\x17"=>[16,"we"],"\x18"=>[94,"wi"],"\x19"=>[76,"wi"],"\x1A"=>[104,"wi"],"\x1B"=>[16,"wi"],"\x1C"=>[94,"wo"],"\x1D"=>[76,"wo"],"\x1E"=>[104,"wo"],"\x1F"=>[16,"wo"],"\x20"=>[94,"ws"],"\x21"=>[76,"ws"],"\x22"=>[104,"ws"],"\x23"=>[16,"ws"],"\x24"=>[94,"wt"],"\x25"=>[76,"wt"],"\x26"=>[104,"wt"],"\x27"=>[16,"wt"],"\x28"=>[77,"w\x20"],"\x29"=>[18,"w\x20"],"\x2A"=>[77,"w\x25"],"\x2B"=>[18,"w\x25"],"\x2C"=>[77,"w-"],"-"=>[18,"w-"],"."=>[77,"w."],"\x2F"=>[18,"w."],[77,"w\x2F"],[18,"w\x2F"],[77,"w3"],[18,"w3"],[77,"w4"],[18,"w4"],[77,"w5"],[18,"w5"],[77,"w6"],[18,"w6"],"\x3A"=>[77,"w7"],"\x3B"=>[18,"w7"],"\x3C"=>[77,"w8"],"\x3D"=>[18,"w8"],"\x3E"=>[77,"w9"],"\x3F"=>[18,"w9"],"\x40"=>[77,"w\x3D"],"A"=>[18,"w\x3D"],"B"=>[77,"wA"],"C"=>[18,"wA"],"D"=>[77,"w_"],"E"=>[18,"w_"],"F"=>[77,"wb"],"G"=>[18,"wb"],"H"=>[77,"wd"],"I"=>[18,"wd"],"J"=>[77,"wf"],"K"=>[18,"wf"],"L"=>[77,"wg"],"M"=>[18,"wg"],"N"=>[77,"wh"],"O"=>[18,"wh"],"P"=>[77,"wl"],"Q"=>[18,"wl"],"R"=>[77,"wm"],"S"=>[18,"wm"],"T"=>[77,"wn"],"U"=>[18,"wn"],"V"=>[77,"wp"],"W"=>[18,"wp"],"X"=>[77,"wr"],"Y"=>[18,"wr"],"Z"=>[77,"wu"],"\x5B"=>[18,"wu"],"\x5C"=>[0,"w\x3A"],"\x5D"=>[0,"wB"],"\x5E"=>[0,"wC"],"_"=>[0,"wD"],"\x60"=>[0,"wE"],"a"=>[0,"wF"],"b"=>[0,"wG"],"c"=>[0,"wH"],"d"=>[0,"wI"],"e"=>[0,"wJ"],"f"=>[0,"wK"],"g"=>[0,"wL"],"h"=>[0,"wM"],"i"=>[0,"wN"],"j"=>[0,"wO"],"k"=>[0,"wP"],"l"=>[0,"wQ"],"m"=>[0,"wR"],"n"=>[0,"wS"],"o"=>[0,"wT"],"p"=>[0,"wU"],"q"=>[0,"wV"],"r"=>[0,"wW"],"s"=>[0,"wY"],"t"=>[0,"wj"],"u"=>[0,"wk"],"v"=>[0,"wq"],"w"=>[0,"wv"],"x"=>[0,"ww"],"y"=>[0,"wx"],"z"=>[0,"wy"],"\x7B"=>[0,"wz"],"\x7C"=>[82,"w"],"\x7D"=>[87,"w"],"~"=>[130,"w"],"\x7F"=>[9,"w"],"\x80"=>[94,"x0"],"\x81"=>[76,"x0"],"\x82"=>[104,"x0"],"\x83"=>[16,"x0"],"\x84"=>[94,"x1"],"\x85"=>[76,"x1"],"\x86"=>[104,"x1"],"\x87"=>[16,"x1"],"\x88"=>[94,"x2"],"\x89"=>[76,"x2"],"\x8A"=>[104,"x2"],"\x8B"=>[16,"x2"],"\x8C"=>[94,"xa"],"\x8D"=>[76,"xa"],"\x8E"=>[104,"xa"],"\x8F"=>[16,"xa"],"\x90"=>[94,"xc"],"\x91"=>[76,"xc"],"\x92"=>[104,"xc"],"\x93"=>[16,"xc"],"\x94"=>[94,"xe"],"\x95"=>[76,"xe"],"\x96"=>[104,"xe"],"\x97"=>[16,"xe"],"\x98"=>[94,"xi"],"\x99"=>[76,"xi"],"\x9A"=>[104,"xi"],"\x9B"=>[16,"xi"],"\x9C"=>[94,"xo"],"\x9D"=>[76,"xo"],"\x9E"=>[104,"xo"],"\x9F"=>[16,"xo"],"\xA0"=>[94,"xs"],"\xA1"=>[76,"xs"],"\xA2"=>[104,"xs"],"\xA3"=>[16,"xs"],"\xA4"=>[94,"xt"],"\xA5"=>[76,"xt"],"\xA6"=>[104,"xt"],"\xA7"=>[16,"xt"],"\xA8"=>[77,"x\x20"],"\xA9"=>[18,"x\x20"],"\xAA"=>[77,"x\x25"],"\xAB"=>[18,"x\x25"],"\xAC"=>[77,"x-"],"\xAD"=>[18,"x-"],"\xAE"=>[77,"x."],"\xAF"=>[18,"x."],"\xB0"=>[77,"x\x2F"],"\xB1"=>[18,"x\x2F"],"\xB2"=>[77,"x3"],"\xB3"=>[18,"x3"],"\xB4"=>[77,"x4"],"\xB5"=>[18,"x4"],"\xB6"=>[77,"x5"],"\xB7"=>[18,"x5"],"\xB8"=>[77,"x6"],"\xB9"=>[18,"x6"],"\xBA"=>[77,"x7"],"\xBB"=>[18,"x7"],"\xBC"=>[77,"x8"],"\xBD"=>[18,"x8"],"\xBE"=>[77,"x9"],"\xBF"=>[18,"x9"],"\xC0"=>[77,"x\x3D"],"\xC1"=>[18,"x\x3D"],"\xC2"=>[77,"xA"],"\xC3"=>[18,"xA"],"\xC4"=>[77,"x_"],"\xC5"=>[18,"x_"],"\xC6"=>[77,"xb"],"\xC7"=>[18,"xb"],"\xC8"=>[77,"xd"],"\xC9"=>[18,"xd"],"\xCA"=>[77,"xf"],"\xCB"=>[18,"xf"],"\xCC"=>[77,"xg"],"\xCD"=>[18,"xg"],"\xCE"=>[77,"xh"],"\xCF"=>[18,"xh"],"\xD0"=>[77,"xl"],"\xD1"=>[18,"xl"],"\xD2"=>[77,"xm"],"\xD3"=>[18,"xm"],"\xD4"=>[77,"xn"],"\xD5"=>[18,"xn"],"\xD6"=>[77,"xp"],"\xD7"=>[18,"xp"],"\xD8"=>[77,"xr"],"\xD9"=>[18,"xr"],"\xDA"=>[77,"xu"],"\xDB"=>[18,"xu"],"\xDC"=>[0,"x\x3A"],"\xDD"=>[0,"xB"],"\xDE"=>[0,"xC"],"\xDF"=>[0,"xD"],"\xE0"=>[0,"xE"],"\xE1"=>[0,"xF"],"\xE2"=>[0,"xG"],"\xE3"=>[0,"xH"],"\xE4"=>[0,"xI"],"\xE5"=>[0,"xJ"],"\xE6"=>[0,"xK"],"\xE7"=>[0,"xL"],"\xE8"=>[0,"xM"],"\xE9"=>[0,"xN"],"\xEA"=>[0,"xO"],"\xEB"=>[0,"xP"],"\xEC"=>[0,"xQ"],"\xED"=>[0,"xR"],"\xEE"=>[0,"xS"],"\xEF"=>[0,"xT"],"\xF0"=>[0,"xU"],"\xF1"=>[0,"xV"],"\xF2"=>[0,"xW"],"\xF3"=>[0,"xY"],"\xF4"=>[0,"xj"],"\xF5"=>[0,"xk"],"\xF6"=>[0,"xq"],"\xF7"=>[0,"xv"],"\xF8"=>[0,"xw"],"\xF9"=>[0,"xx"],"\xFA"=>[0,"xy"],"\xFB"=>[0,"xz"],"\xFC"=>[82,"x"],"\xFD"=>[87,"x"],"\xFE"=>[130,"x"],"\xFF"=>[9,"x"],],["\x00"=>[77,"w0"],"\x01"=>[18,"w0"],"\x02"=>[77,"w1"],"\x03"=>[18,"w1"],"\x04"=>[77,"w2"],"\x05"=>[18,"w2"],"\x06"=>[77,"wa"],"\x07"=>[18,"wa"],"\x08"=>[77,"wc"],"\x09"=>[18,"wc"],"\x0A"=>[77,"we"],"\x0B"=>[18,"we"],"\x0C"=>[77,"wi"],"\x0D"=>[18,"wi"],"\x0E"=>[77,"wo"],"\x0F"=>[18,"wo"],"\x10"=>[77,"ws"],"\x11"=>[18,"ws"],"\x12"=>[77,"wt"],"\x13"=>[18,"wt"],"\x14"=>[0,"w\x20"],"\x15"=>[0,"w\x25"],"\x16"=>[0,"w-"],"\x17"=>[0,"w."],"\x18"=>[0,"w\x2F"],"\x19"=>[0,"w3"],"\x1A"=>[0,"w4"],"\x1B"=>[0,"w5"],"\x1C"=>[0,"w6"],"\x1D"=>[0,"w7"],"\x1E"=>[0,"w8"],"\x1F"=>[0,"w9"],"\x20"=>[0,"w\x3D"],"\x21"=>[0,"wA"],"\x22"=>[0,"w_"],"\x23"=>[0,"wb"],"\x24"=>[0,"wd"],"\x25"=>[0,"wf"],"\x26"=>[0,"wg"],"\x27"=>[0,"wh"],"\x28"=>[0,"wl"],"\x29"=>[0,"wm"],"\x2A"=>[0,"wn"],"\x2B"=>[0,"wp"],"\x2C"=>[0,"wr"],"-"=>[0,"wu"],"."=>[100,"w"],"\x2F"=>[110,"w"],[111,"w"],[115,"w"],[116,"w"],[118,"w"],[119,"w"],[122,"w"],[123,"w"],[125,"w"],[126,"w"],[129,"w"],"\x3A"=>[143,"w"],"\x3B"=>[148,"w"],"\x3C"=>[151,"w"],"\x3D"=>[153,"w"],"\x3E"=>[83,"w"],"\x3F"=>[10,"w"],"\x40"=>[77,"x0"],"A"=>[18,"x0"],"B"=>[77,"x1"],"C"=>[18,"x1"],"D"=>[77,"x2"],"E"=>[18,"x2"],"F"=>[77,"xa"],"G"=>[18,"xa"],"H"=>[77,"xc"],"I"=>[18,"xc"],"J"=>[77,"xe"],"K"=>[18,"xe"],"L"=>[77,"xi"],"M"=>[18,"xi"],"N"=>[77,"xo"],"O"=>[18,"xo"],"P"=>[77,"xs"],"Q"=>[18,"xs"],"R"=>[77,"xt"],"S"=>[18,"xt"],"T"=>[0,"x\x20"],"U"=>[0,"x\x25"],"V"=>[0,"x-"],"W"=>[0,"x."],"X"=>[0,"x\x2F"],"Y"=>[0,"x3"],"Z"=>[0,"x4"],"\x5B"=>[0,"x5"],"\x5C"=>[0,"x6"],"\x5D"=>[0,"x7"],"\x5E"=>[0,"x8"],"_"=>[0,"x9"],"\x60"=>[0,"x\x3D"],"a"=>[0,"xA"],"b"=>[0,"x_"],"c"=>[0,"xb"],"d"=>[0,"xd"],"e"=>[0,"xf"],"f"=>[0,"xg"],"g"=>[0,"xh"],"h"=>[0,"xl"],"i"=>[0,"xm"],"j"=>[0,"xn"],"k"=>[0,"xp"],"l"=>[0,"xr"],"m"=>[0,"xu"],"n"=>[100,"x"],"o"=>[110,"x"],"p"=>[111,"x"],"q"=>[115,"x"],"r"=>[116,"x"],"s"=>[118,"x"],"t"=>[119,"x"],"u"=>[122,"x"],"v"=>[123,"x"],"w"=>[125,"x"],"x"=>[126,"x"],"y"=>[129,"x"],"z"=>[143,"x"],"\x7B"=>[148,"x"],"\x7C"=>[151,"x"],"\x7D"=>[153,"x"],"~"=>[83,"x"],"\x7F"=>[10,"x"],"\x80"=>[77,"y0"],"\x81"=>[18,"y0"],"\x82"=>[77,"y1"],"\x83"=>[18,"y1"],"\x84"=>[77,"y2"],"\x85"=>[18,"y2"],"\x86"=>[77,"ya"],"\x87"=>[18,"ya"],"\x88"=>[77,"yc"],"\x89"=>[18,"yc"],"\x8A"=>[77,"ye"],"\x8B"=>[18,"ye"],"\x8C"=>[77,"yi"],"\x8D"=>[18,"yi"],"\x8E"=>[77,"yo"],"\x8F"=>[18,"yo"],"\x90"=>[77,"ys"],"\x91"=>[18,"ys"],"\x92"=>[77,"yt"],"\x93"=>[18,"yt"],"\x94"=>[0,"y\x20"],"\x95"=>[0,"y\x25"],"\x96"=>[0,"y-"],"\x97"=>[0,"y."],"\x98"=>[0,"y\x2F"],"\x99"=>[0,"y3"],"\x9A"=>[0,"y4"],"\x9B"=>[0,"y5"],"\x9C"=>[0,"y6"],"\x9D"=>[0,"y7"],"\x9E"=>[0,"y8"],"\x9F"=>[0,"y9"],"\xA0"=>[0,"y\x3D"],"\xA1"=>[0,"yA"],"\xA2"=>[0,"y_"],"\xA3"=>[0,"yb"],"\xA4"=>[0,"yd"],"\xA5"=>[0,"yf"],"\xA6"=>[0,"yg"],"\xA7"=>[0,"yh"],"\xA8"=>[0,"yl"],"\xA9"=>[0,"ym"],"\xAA"=>[0,"yn"],"\xAB"=>[0,"yp"],"\xAC"=>[0,"yr"],"\xAD"=>[0,"yu"],"\xAE"=>[100,"y"],"\xAF"=>[110,"y"],"\xB0"=>[111,"y"],"\xB1"=>[115,"y"],"\xB2"=>[116,"y"],"\xB3"=>[118,"y"],"\xB4"=>[119,"y"],"\xB5"=>[122,"y"],"\xB6"=>[123,"y"],"\xB7"=>[125,"y"],"\xB8"=>[126,"y"],"\xB9"=>[129,"y"],"\xBA"=>[143,"y"],"\xBB"=>[148,"y"],"\xBC"=>[151,"y"],"\xBD"=>[153,"y"],"\xBE"=>[83,"y"],"\xBF"=>[10,"y"],"\xC0"=>[77,"z0"],"\xC1"=>[18,"z0"],"\xC2"=>[77,"z1"],"\xC3"=>[18,"z1"],"\xC4"=>[77,"z2"],"\xC5"=>[18,"z2"],"\xC6"=>[77,"za"],"\xC7"=>[18,"za"],"\xC8"=>[77,"zc"],"\xC9"=>[18,"zc"],"\xCA"=>[77,"ze"],"\xCB"=>[18,"ze"],"\xCC"=>[77,"zi"],"\xCD"=>[18,"zi"],"\xCE"=>[77,"zo"],"\xCF"=>[18,"zo"],"\xD0"=>[77,"zs"],"\xD1"=>[18,"zs"],"\xD2"=>[77,"zt"],"\xD3"=>[18,"zt"],"\xD4"=>[0,"z\x20"],"\xD5"=>[0,"z\x25"],"\xD6"=>[0,"z-"],"\xD7"=>[0,"z."],"\xD8"=>[0,"z\x2F"],"\xD9"=>[0,"z3"],"\xDA"=>[0,"z4"],"\xDB"=>[0,"z5"],"\xDC"=>[0,"z6"],"\xDD"=>[0,"z7"],"\xDE"=>[0,"z8"],"\xDF"=>[0,"z9"],"\xE0"=>[0,"z\x3D"],"\xE1"=>[0,"zA"],"\xE2"=>[0,"z_"],"\xE3"=>[0,"zb"],"\xE4"=>[0,"zd"],"\xE5"=>[0,"zf"],"\xE6"=>[0,"zg"],"\xE7"=>[0,"zh"],"\xE8"=>[0,"zl"],"\xE9"=>[0,"zm"],"\xEA"=>[0,"zn"],"\xEB"=>[0,"zp"],"\xEC"=>[0,"zr"],"\xED"=>[0,"zu"],"\xEE"=>[100,"z"],"\xEF"=>[110,"z"],"\xF0"=>[111,"z"],"\xF1"=>[115,"z"],"\xF2"=>[116,"z"],"\xF3"=>[118,"z"],"\xF4"=>[119,"z"],"\xF5"=>[122,"z"],"\xF6"=>[123,"z"],"\xF7"=>[125,"z"],"\xF8"=>[126,"z"],"\xF9"=>[129,"z"],"\xFA"=>[143,"z"],"\xFB"=>[148,"z"],"\xFC"=>[151,"z"],"\xFD"=>[153,"z"],"\xFE"=>[83,"z"],"\xFF"=>[10,"z"],],["\x00"=>[94,"y0"],"\x01"=>[76,"y0"],"\x02"=>[104,"y0"],"\x03"=>[16,"y0"],"\x04"=>[94,"y1"],"\x05"=>[76,"y1"],"\x06"=>[104,"y1"],"\x07"=>[16,"y1"],"\x08"=>[94,"y2"],"\x09"=>[76,"y2"],"\x0A"=>[104,"y2"],"\x0B"=>[16,"y2"],"\x0C"=>[94,"ya"],"\x0D"=>[76,"ya"],"\x0E"=>[104,"ya"],"\x0F"=>[16,"ya"],"\x10"=>[94,"yc"],"\x11"=>[76,"yc"],"\x12"=>[104,"yc"],"\x13"=>[16,"yc"],"\x14"=>[94,"ye"],"\x15"=>[76,"ye"],"\x16"=>[104,"ye"],"\x17"=>[16,"ye"],"\x18"=>[94,"yi"],"\x19"=>[76,"yi"],"\x1A"=>[104,"yi"],"\x1B"=>[16,"yi"],"\x1C"=>[94,"yo"],"\x1D"=>[76,"yo"],"\x1E"=>[104,"yo"],"\x1F"=>[16,"yo"],"\x20"=>[94,"ys"],"\x21"=>[76,"ys"],"\x22"=>[104,"ys"],"\x23"=>[16,"ys"],"\x24"=>[94,"yt"],"\x25"=>[76,"yt"],"\x26"=>[104,"yt"],"\x27"=>[16,"yt"],"\x28"=>[77,"y\x20"],"\x29"=>[18,"y\x20"],"\x2A"=>[77,"y\x25"],"\x2B"=>[18,"y\x25"],"\x2C"=>[77,"y-"],"-"=>[18,"y-"],"."=>[77,"y."],"\x2F"=>[18,"y."],[77,"y\x2F"],[18,"y\x2F"],[77,"y3"],[18,"y3"],[77,"y4"],[18,"y4"],[77,"y5"],[18,"y5"],[77,"y6"],[18,"y6"],"\x3A"=>[77,"y7"],"\x3B"=>[18,"y7"],"\x3C"=>[77,"y8"],"\x3D"=>[18,"y8"],"\x3E"=>[77,"y9"],"\x3F"=>[18,"y9"],"\x40"=>[77,"y\x3D"],"A"=>[18,"y\x3D"],"B"=>[77,"yA"],"C"=>[18,"yA"],"D"=>[77,"y_"],"E"=>[18,"y_"],"F"=>[77,"yb"],"G"=>[18,"yb"],"H"=>[77,"yd"],"I"=>[18,"yd"],"J"=>[77,"yf"],"K"=>[18,"yf"],"L"=>[77,"yg"],"M"=>[18,"yg"],"N"=>[77,"yh"],"O"=>[18,"yh"],"P"=>[77,"yl"],"Q"=>[18,"yl"],"R"=>[77,"ym"],"S"=>[18,"ym"],"T"=>[77,"yn"],"U"=>[18,"yn"],"V"=>[77,"yp"],"W"=>[18,"yp"],"X"=>[77,"yr"],"Y"=>[18,"yr"],"Z"=>[77,"yu"],"\x5B"=>[18,"yu"],"\x5C"=>[0,"y\x3A"],"\x5D"=>[0,"yB"],"\x5E"=>[0,"yC"],"_"=>[0,"yD"],"\x60"=>[0,"yE"],"a"=>[0,"yF"],"b"=>[0,"yG"],"c"=>[0,"yH"],"d"=>[0,"yI"],"e"=>[0,"yJ"],"f"=>[0,"yK"],"g"=>[0,"yL"],"h"=>[0,"yM"],"i"=>[0,"yN"],"j"=>[0,"yO"],"k"=>[0,"yP"],"l"=>[0,"yQ"],"m"=>[0,"yR"],"n"=>[0,"yS"],"o"=>[0,"yT"],"p"=>[0,"yU"],"q"=>[0,"yV"],"r"=>[0,"yW"],"s"=>[0,"yY"],"t"=>[0,"yj"],"u"=>[0,"yk"],"v"=>[0,"yq"],"w"=>[0,"yv"],"x"=>[0,"yw"],"y"=>[0,"yx"],"z"=>[0,"yy"],"\x7B"=>[0,"yz"],"\x7C"=>[82,"y"],"\x7D"=>[87,"y"],"~"=>[130,"y"],"\x7F"=>[9,"y"],"\x80"=>[94,"z0"],"\x81"=>[76,"z0"],"\x82"=>[104,"z0"],"\x83"=>[16,"z0"],"\x84"=>[94,"z1"],"\x85"=>[76,"z1"],"\x86"=>[104,"z1"],"\x87"=>[16,"z1"],"\x88"=>[94,"z2"],"\x89"=>[76,"z2"],"\x8A"=>[104,"z2"],"\x8B"=>[16,"z2"],"\x8C"=>[94,"za"],"\x8D"=>[76,"za"],"\x8E"=>[104,"za"],"\x8F"=>[16,"za"],"\x90"=>[94,"zc"],"\x91"=>[76,"zc"],"\x92"=>[104,"zc"],"\x93"=>[16,"zc"],"\x94"=>[94,"ze"],"\x95"=>[76,"ze"],"\x96"=>[104,"ze"],"\x97"=>[16,"ze"],"\x98"=>[94,"zi"],"\x99"=>[76,"zi"],"\x9A"=>[104,"zi"],"\x9B"=>[16,"zi"],"\x9C"=>[94,"zo"],"\x9D"=>[76,"zo"],"\x9E"=>[104,"zo"],"\x9F"=>[16,"zo"],"\xA0"=>[94,"zs"],"\xA1"=>[76,"zs"],"\xA2"=>[104,"zs"],"\xA3"=>[16,"zs"],"\xA4"=>[94,"zt"],"\xA5"=>[76,"zt"],"\xA6"=>[104,"zt"],"\xA7"=>[16,"zt"],"\xA8"=>[77,"z\x20"],"\xA9"=>[18,"z\x20"],"\xAA"=>[77,"z\x25"],"\xAB"=>[18,"z\x25"],"\xAC"=>[77,"z-"],"\xAD"=>[18,"z-"],"\xAE"=>[77,"z."],"\xAF"=>[18,"z."],"\xB0"=>[77,"z\x2F"],"\xB1"=>[18,"z\x2F"],"\xB2"=>[77,"z3"],"\xB3"=>[18,"z3"],"\xB4"=>[77,"z4"],"\xB5"=>[18,"z4"],"\xB6"=>[77,"z5"],"\xB7"=>[18,"z5"],"\xB8"=>[77,"z6"],"\xB9"=>[18,"z6"],"\xBA"=>[77,"z7"],"\xBB"=>[18,"z7"],"\xBC"=>[77,"z8"],"\xBD"=>[18,"z8"],"\xBE"=>[77,"z9"],"\xBF"=>[18,"z9"],"\xC0"=>[77,"z\x3D"],"\xC1"=>[18,"z\x3D"],"\xC2"=>[77,"zA"],"\xC3"=>[18,"zA"],"\xC4"=>[77,"z_"],"\xC5"=>[18,"z_"],"\xC6"=>[77,"zb"],"\xC7"=>[18,"zb"],"\xC8"=>[77,"zd"],"\xC9"=>[18,"zd"],"\xCA"=>[77,"zf"],"\xCB"=>[18,"zf"],"\xCC"=>[77,"zg"],"\xCD"=>[18,"zg"],"\xCE"=>[77,"zh"],"\xCF"=>[18,"zh"],"\xD0"=>[77,"zl"],"\xD1"=>[18,"zl"],"\xD2"=>[77,"zm"],"\xD3"=>[18,"zm"],"\xD4"=>[77,"zn"],"\xD5"=>[18,"zn"],"\xD6"=>[77,"zp"],"\xD7"=>[18,"zp"],"\xD8"=>[77,"zr"],"\xD9"=>[18,"zr"],"\xDA"=>[77,"zu"],"\xDB"=>[18,"zu"],"\xDC"=>[0,"z\x3A"],"\xDD"=>[0,"zB"],"\xDE"=>[0,"zC"],"\xDF"=>[0,"zD"],"\xE0"=>[0,"zE"],"\xE1"=>[0,"zF"],"\xE2"=>[0,"zG"],"\xE3"=>[0,"zH"],"\xE4"=>[0,"zI"],"\xE5"=>[0,"zJ"],"\xE6"=>[0,"zK"],"\xE7"=>[0,"zL"],"\xE8"=>[0,"zM"],"\xE9"=>[0,"zN"],"\xEA"=>[0,"zO"],"\xEB"=>[0,"zP"],"\xEC"=>[0,"zQ"],"\xED"=>[0,"zR"],"\xEE"=>[0,"zS"],"\xEF"=>[0,"zT"],"\xF0"=>[0,"zU"],"\xF1"=>[0,"zV"],"\xF2"=>[0,"zW"],"\xF3"=>[0,"zY"],"\xF4"=>[0,"zj"],"\xF5"=>[0,"zk"],"\xF6"=>[0,"zq"],"\xF7"=>[0,"zv"],"\xF8"=>[0,"zw"],"\xF9"=>[0,"zx"],"\xFA"=>[0,"zy"],"\xFB"=>[0,"zz"],"\xFC"=>[82,"z"],"\xFD"=>[87,"z"],"\xFE"=>[130,"z"],"\xFF"=>[9,"z"],],["\x00"=>[94,"\x7F0"],"\x01"=>[76,"\x7F0"],"\x02"=>[104,"\x7F0"],"\x03"=>[16,"\x7F0"],"\x04"=>[94,"\x7F1"],"\x05"=>[76,"\x7F1"],"\x06"=>[104,"\x7F1"],"\x07"=>[16,"\x7F1"],"\x08"=>[94,"\x7F2"],"\x09"=>[76,"\x7F2"],"\x0A"=>[104,"\x7F2"],"\x0B"=>[16,"\x7F2"],"\x0C"=>[94,"\x7Fa"],"\x0D"=>[76,"\x7Fa"],"\x0E"=>[104,"\x7Fa"],"\x0F"=>[16,"\x7Fa"],"\x10"=>[94,"\x7Fc"],"\x11"=>[76,"\x7Fc"],"\x12"=>[104,"\x7Fc"],"\x13"=>[16,"\x7Fc"],"\x14"=>[94,"\x7Fe"],"\x15"=>[76,"\x7Fe"],"\x16"=>[104,"\x7Fe"],"\x17"=>[16,"\x7Fe"],"\x18"=>[94,"\x7Fi"],"\x19"=>[76,"\x7Fi"],"\x1A"=>[104,"\x7Fi"],"\x1B"=>[16,"\x7Fi"],"\x1C"=>[94,"\x7Fo"],"\x1D"=>[76,"\x7Fo"],"\x1E"=>[104,"\x7Fo"],"\x1F"=>[16,"\x7Fo"],"\x20"=>[94,"\x7Fs"],"\x21"=>[76,"\x7Fs"],"\x22"=>[104,"\x7Fs"],"\x23"=>[16,"\x7Fs"],"\x24"=>[94,"\x7Ft"],"\x25"=>[76,"\x7Ft"],"\x26"=>[104,"\x7Ft"],"\x27"=>[16,"\x7Ft"],"\x28"=>[77,"\x7F\x20"],"\x29"=>[18,"\x7F\x20"],"\x2A"=>[77,"\x7F\x25"],"\x2B"=>[18,"\x7F\x25"],"\x2C"=>[77,"\x7F-"],"-"=>[18,"\x7F-"],"."=>[77,"\x7F."],"\x2F"=>[18,"\x7F."],[77,"\x7F\x2F"],[18,"\x7F\x2F"],[77,"\x7F3"],[18,"\x7F3"],[77,"\x7F4"],[18,"\x7F4"],[77,"\x7F5"],[18,"\x7F5"],[77,"\x7F6"],[18,"\x7F6"],"\x3A"=>[77,"\x7F7"],"\x3B"=>[18,"\x7F7"],"\x3C"=>[77,"\x7F8"],"\x3D"=>[18,"\x7F8"],"\x3E"=>[77,"\x7F9"],"\x3F"=>[18,"\x7F9"],"\x40"=>[77,"\x7F\x3D"],"A"=>[18,"\x7F\x3D"],"B"=>[77,"\x7FA"],"C"=>[18,"\x7FA"],"D"=>[77,"\x7F_"],"E"=>[18,"\x7F_"],"F"=>[77,"\x7Fb"],"G"=>[18,"\x7Fb"],"H"=>[77,"\x7Fd"],"I"=>[18,"\x7Fd"],"J"=>[77,"\x7Ff"],"K"=>[18,"\x7Ff"],"L"=>[77,"\x7Fg"],"M"=>[18,"\x7Fg"],"N"=>[77,"\x7Fh"],"O"=>[18,"\x7Fh"],"P"=>[77,"\x7Fl"],"Q"=>[18,"\x7Fl"],"R"=>[77,"\x7Fm"],"S"=>[18,"\x7Fm"],"T"=>[77,"\x7Fn"],"U"=>[18,"\x7Fn"],"V"=>[77,"\x7Fp"],"W"=>[18,"\x7Fp"],"X"=>[77,"\x7Fr"],"Y"=>[18,"\x7Fr"],"Z"=>[77,"\x7Fu"],"\x5B"=>[18,"\x7Fu"],"\x5C"=>[0,"\x7F\x3A"],"\x5D"=>[0,"\x7FB"],"\x5E"=>[0,"\x7FC"],"_"=>[0,"\x7FD"],"\x60"=>[0,"\x7FE"],"a"=>[0,"\x7FF"],"b"=>[0,"\x7FG"],"c"=>[0,"\x7FH"],"d"=>[0,"\x7FI"],"e"=>[0,"\x7FJ"],"f"=>[0,"\x7FK"],"g"=>[0,"\x7FL"],"h"=>[0,"\x7FM"],"i"=>[0,"\x7FN"],"j"=>[0,"\x7FO"],"k"=>[0,"\x7FP"],"l"=>[0,"\x7FQ"],"m"=>[0,"\x7FR"],"n"=>[0,"\x7FS"],"o"=>[0,"\x7FT"],"p"=>[0,"\x7FU"],"q"=>[0,"\x7FV"],"r"=>[0,"\x7FW"],"s"=>[0,"\x7FY"],"t"=>[0,"\x7Fj"],"u"=>[0,"\x7Fk"],"v"=>[0,"\x7Fq"],"w"=>[0,"\x7Fv"],"x"=>[0,"\x7Fw"],"y"=>[0,"\x7Fx"],"z"=>[0,"\x7Fy"],"\x7B"=>[0,"\x7Fz"],"\x7C"=>[82,"\x7F"],"\x7D"=>[87,"\x7F"],"~"=>[130,"\x7F"],"\x7F"=>[9,"\x7F"],"\x80"=>[94,"\xDC0"],"\x81"=>[76,"\xDC0"],"\x82"=>[104,"\xDC0"],"\x83"=>[16,"\xDC0"],"\x84"=>[94,"\xDC1"],"\x85"=>[76,"\xDC1"],"\x86"=>[104,"\xDC1"],"\x87"=>[16,"\xDC1"],"\x88"=>[94,"\xDC2"],"\x89"=>[76,"\xDC2"],"\x8A"=>[104,"\xDC2"],"\x8B"=>[16,"\xDC2"],"\x8C"=>[94,"\xDCa"],"\x8D"=>[76,"\xDCa"],"\x8E"=>[104,"\xDCa"],"\x8F"=>[16,"\xDCa"],"\x90"=>[94,"\xDCc"],"\x91"=>[76,"\xDCc"],"\x92"=>[104,"\xDCc"],"\x93"=>[16,"\xDCc"],"\x94"=>[94,"\xDCe"],"\x95"=>[76,"\xDCe"],"\x96"=>[104,"\xDCe"],"\x97"=>[16,"\xDCe"],"\x98"=>[94,"\xDCi"],"\x99"=>[76,"\xDCi"],"\x9A"=>[104,"\xDCi"],"\x9B"=>[16,"\xDCi"],"\x9C"=>[94,"\xDCo"],"\x9D"=>[76,"\xDCo"],"\x9E"=>[104,"\xDCo"],"\x9F"=>[16,"\xDCo"],"\xA0"=>[94,"\xDCs"],"\xA1"=>[76,"\xDCs"],"\xA2"=>[104,"\xDCs"],"\xA3"=>[16,"\xDCs"],"\xA4"=>[94,"\xDCt"],"\xA5"=>[76,"\xDCt"],"\xA6"=>[104,"\xDCt"],"\xA7"=>[16,"\xDCt"],"\xA8"=>[77,"\xDC\x20"],"\xA9"=>[18,"\xDC\x20"],"\xAA"=>[77,"\xDC\x25"],"\xAB"=>[18,"\xDC\x25"],"\xAC"=>[77,"\xDC-"],"\xAD"=>[18,"\xDC-"],"\xAE"=>[77,"\xDC."],"\xAF"=>[18,"\xDC."],"\xB0"=>[77,"\xDC\x2F"],"\xB1"=>[18,"\xDC\x2F"],"\xB2"=>[77,"\xDC3"],"\xB3"=>[18,"\xDC3"],"\xB4"=>[77,"\xDC4"],"\xB5"=>[18,"\xDC4"],"\xB6"=>[77,"\xDC5"],"\xB7"=>[18,"\xDC5"],"\xB8"=>[77,"\xDC6"],"\xB9"=>[18,"\xDC6"],"\xBA"=>[77,"\xDC7"],"\xBB"=>[18,"\xDC7"],"\xBC"=>[77,"\xDC8"],"\xBD"=>[18,"\xDC8"],"\xBE"=>[77,"\xDC9"],"\xBF"=>[18,"\xDC9"],"\xC0"=>[77,"\xDC\x3D"],"\xC1"=>[18,"\xDC\x3D"],"\xC2"=>[77,"\xDCA"],"\xC3"=>[18,"\xDCA"],"\xC4"=>[77,"\xDC_"],"\xC5"=>[18,"\xDC_"],"\xC6"=>[77,"\xDCb"],"\xC7"=>[18,"\xDCb"],"\xC8"=>[77,"\xDCd"],"\xC9"=>[18,"\xDCd"],"\xCA"=>[77,"\xDCf"],"\xCB"=>[18,"\xDCf"],"\xCC"=>[77,"\xDCg"],"\xCD"=>[18,"\xDCg"],"\xCE"=>[77,"\xDCh"],"\xCF"=>[18,"\xDCh"],"\xD0"=>[77,"\xDCl"],"\xD1"=>[18,"\xDCl"],"\xD2"=>[77,"\xDCm"],"\xD3"=>[18,"\xDCm"],"\xD4"=>[77,"\xDCn"],"\xD5"=>[18,"\xDCn"],"\xD6"=>[77,"\xDCp"],"\xD7"=>[18,"\xDCp"],"\xD8"=>[77,"\xDCr"],"\xD9"=>[18,"\xDCr"],"\xDA"=>[77,"\xDCu"],"\xDB"=>[18,"\xDCu"],"\xDC"=>[0,"\xDC\x3A"],"\xDD"=>[0,"\xDCB"],"\xDE"=>[0,"\xDCC"],"\xDF"=>[0,"\xDCD"],"\xE0"=>[0,"\xDCE"],"\xE1"=>[0,"\xDCF"],"\xE2"=>[0,"\xDCG"],"\xE3"=>[0,"\xDCH"],"\xE4"=>[0,"\xDCI"],"\xE5"=>[0,"\xDCJ"],"\xE6"=>[0,"\xDCK"],"\xE7"=>[0,"\xDCL"],"\xE8"=>[0,"\xDCM"],"\xE9"=>[0,"\xDCN"],"\xEA"=>[0,"\xDCO"],"\xEB"=>[0,"\xDCP"],"\xEC"=>[0,"\xDCQ"],"\xED"=>[0,"\xDCR"],"\xEE"=>[0,"\xDCS"],"\xEF"=>[0,"\xDCT"],"\xF0"=>[0,"\xDCU"],"\xF1"=>[0,"\xDCV"],"\xF2"=>[0,"\xDCW"],"\xF3"=>[0,"\xDCY"],"\xF4"=>[0,"\xDCj"],"\xF5"=>[0,"\xDCk"],"\xF6"=>[0,"\xDCq"],"\xF7"=>[0,"\xDCv"],"\xF8"=>[0,"\xDCw"],"\xF9"=>[0,"\xDCx"],"\xFA"=>[0,"\xDCy"],"\xFB"=>[0,"\xDCz"],"\xFC"=>[82,"\xDC"],"\xFD"=>[87,"\xDC"],"\xFE"=>[130,"\xDC"],"\xFF"=>[9,"\xDC"],],["\x00"=>[94,"\x800"],"\x01"=>[76,"\x800"],"\x02"=>[104,"\x800"],"\x03"=>[16,"\x800"],"\x04"=>[94,"\x801"],"\x05"=>[76,"\x801"],"\x06"=>[104,"\x801"],"\x07"=>[16,"\x801"],"\x08"=>[94,"\x802"],"\x09"=>[76,"\x802"],"\x0A"=>[104,"\x802"],"\x0B"=>[16,"\x802"],"\x0C"=>[94,"\x80a"],"\x0D"=>[76,"\x80a"],"\x0E"=>[104,"\x80a"],"\x0F"=>[16,"\x80a"],"\x10"=>[94,"\x80c"],"\x11"=>[76,"\x80c"],"\x12"=>[104,"\x80c"],"\x13"=>[16,"\x80c"],"\x14"=>[94,"\x80e"],"\x15"=>[76,"\x80e"],"\x16"=>[104,"\x80e"],"\x17"=>[16,"\x80e"],"\x18"=>[94,"\x80i"],"\x19"=>[76,"\x80i"],"\x1A"=>[104,"\x80i"],"\x1B"=>[16,"\x80i"],"\x1C"=>[94,"\x80o"],"\x1D"=>[76,"\x80o"],"\x1E"=>[104,"\x80o"],"\x1F"=>[16,"\x80o"],"\x20"=>[94,"\x80s"],"\x21"=>[76,"\x80s"],"\x22"=>[104,"\x80s"],"\x23"=>[16,"\x80s"],"\x24"=>[94,"\x80t"],"\x25"=>[76,"\x80t"],"\x26"=>[104,"\x80t"],"\x27"=>[16,"\x80t"],"\x28"=>[77,"\x80\x20"],"\x29"=>[18,"\x80\x20"],"\x2A"=>[77,"\x80\x25"],"\x2B"=>[18,"\x80\x25"],"\x2C"=>[77,"\x80-"],"-"=>[18,"\x80-"],"."=>[77,"\x80."],"\x2F"=>[18,"\x80."],[77,"\x80\x2F"],[18,"\x80\x2F"],[77,"\x803"],[18,"\x803"],[77,"\x804"],[18,"\x804"],[77,"\x805"],[18,"\x805"],[77,"\x806"],[18,"\x806"],"\x3A"=>[77,"\x807"],"\x3B"=>[18,"\x807"],"\x3C"=>[77,"\x808"],"\x3D"=>[18,"\x808"],"\x3E"=>[77,"\x809"],"\x3F"=>[18,"\x809"],"\x40"=>[77,"\x80\x3D"],"A"=>[18,"\x80\x3D"],"B"=>[77,"\x80A"],"C"=>[18,"\x80A"],"D"=>[77,"\x80_"],"E"=>[18,"\x80_"],"F"=>[77,"\x80b"],"G"=>[18,"\x80b"],"H"=>[77,"\x80d"],"I"=>[18,"\x80d"],"J"=>[77,"\x80f"],"K"=>[18,"\x80f"],"L"=>[77,"\x80g"],"M"=>[18,"\x80g"],"N"=>[77,"\x80h"],"O"=>[18,"\x80h"],"P"=>[77,"\x80l"],"Q"=>[18,"\x80l"],"R"=>[77,"\x80m"],"S"=>[18,"\x80m"],"T"=>[77,"\x80n"],"U"=>[18,"\x80n"],"V"=>[77,"\x80p"],"W"=>[18,"\x80p"],"X"=>[77,"\x80r"],"Y"=>[18,"\x80r"],"Z"=>[77,"\x80u"],"\x5B"=>[18,"\x80u"],"\x5C"=>[0,"\x80\x3A"],"\x5D"=>[0,"\x80B"],"\x5E"=>[0,"\x80C"],"_"=>[0,"\x80D"],"\x60"=>[0,"\x80E"],"a"=>[0,"\x80F"],"b"=>[0,"\x80G"],"c"=>[0,"\x80H"],"d"=>[0,"\x80I"],"e"=>[0,"\x80J"],"f"=>[0,"\x80K"],"g"=>[0,"\x80L"],"h"=>[0,"\x80M"],"i"=>[0,"\x80N"],"j"=>[0,"\x80O"],"k"=>[0,"\x80P"],"l"=>[0,"\x80Q"],"m"=>[0,"\x80R"],"n"=>[0,"\x80S"],"o"=>[0,"\x80T"],"p"=>[0,"\x80U"],"q"=>[0,"\x80V"],"r"=>[0,"\x80W"],"s"=>[0,"\x80Y"],"t"=>[0,"\x80j"],"u"=>[0,"\x80k"],"v"=>[0,"\x80q"],"w"=>[0,"\x80v"],"x"=>[0,"\x80w"],"y"=>[0,"\x80x"],"z"=>[0,"\x80y"],"\x7B"=>[0,"\x80z"],"\x7C"=>[82,"\x80"],"\x7D"=>[87,"\x80"],"~"=>[130,"\x80"],"\x7F"=>[9,"\x80"],"\x80"=>[94,"\x820"],"\x81"=>[76,"\x820"],"\x82"=>[104,"\x820"],"\x83"=>[16,"\x820"],"\x84"=>[94,"\x821"],"\x85"=>[76,"\x821"],"\x86"=>[104,"\x821"],"\x87"=>[16,"\x821"],"\x88"=>[94,"\x822"],"\x89"=>[76,"\x822"],"\x8A"=>[104,"\x822"],"\x8B"=>[16,"\x822"],"\x8C"=>[94,"\x82a"],"\x8D"=>[76,"\x82a"],"\x8E"=>[104,"\x82a"],"\x8F"=>[16,"\x82a"],"\x90"=>[94,"\x82c"],"\x91"=>[76,"\x82c"],"\x92"=>[104,"\x82c"],"\x93"=>[16,"\x82c"],"\x94"=>[94,"\x82e"],"\x95"=>[76,"\x82e"],"\x96"=>[104,"\x82e"],"\x97"=>[16,"\x82e"],"\x98"=>[94,"\x82i"],"\x99"=>[76,"\x82i"],"\x9A"=>[104,"\x82i"],"\x9B"=>[16,"\x82i"],"\x9C"=>[94,"\x82o"],"\x9D"=>[76,"\x82o"],"\x9E"=>[104,"\x82o"],"\x9F"=>[16,"\x82o"],"\xA0"=>[94,"\x82s"],"\xA1"=>[76,"\x82s"],"\xA2"=>[104,"\x82s"],"\xA3"=>[16,"\x82s"],"\xA4"=>[94,"\x82t"],"\xA5"=>[76,"\x82t"],"\xA6"=>[104,"\x82t"],"\xA7"=>[16,"\x82t"],"\xA8"=>[77,"\x82\x20"],"\xA9"=>[18,"\x82\x20"],"\xAA"=>[77,"\x82\x25"],"\xAB"=>[18,"\x82\x25"],"\xAC"=>[77,"\x82-"],"\xAD"=>[18,"\x82-"],"\xAE"=>[77,"\x82."],"\xAF"=>[18,"\x82."],"\xB0"=>[77,"\x82\x2F"],"\xB1"=>[18,"\x82\x2F"],"\xB2"=>[77,"\x823"],"\xB3"=>[18,"\x823"],"\xB4"=>[77,"\x824"],"\xB5"=>[18,"\x824"],"\xB6"=>[77,"\x825"],"\xB7"=>[18,"\x825"],"\xB8"=>[77,"\x826"],"\xB9"=>[18,"\x826"],"\xBA"=>[77,"\x827"],"\xBB"=>[18,"\x827"],"\xBC"=>[77,"\x828"],"\xBD"=>[18,"\x828"],"\xBE"=>[77,"\x829"],"\xBF"=>[18,"\x829"],"\xC0"=>[77,"\x82\x3D"],"\xC1"=>[18,"\x82\x3D"],"\xC2"=>[77,"\x82A"],"\xC3"=>[18,"\x82A"],"\xC4"=>[77,"\x82_"],"\xC5"=>[18,"\x82_"],"\xC6"=>[77,"\x82b"],"\xC7"=>[18,"\x82b"],"\xC8"=>[77,"\x82d"],"\xC9"=>[18,"\x82d"],"\xCA"=>[77,"\x82f"],"\xCB"=>[18,"\x82f"],"\xCC"=>[77,"\x82g"],"\xCD"=>[18,"\x82g"],"\xCE"=>[77,"\x82h"],"\xCF"=>[18,"\x82h"],"\xD0"=>[77,"\x82l"],"\xD1"=>[18,"\x82l"],"\xD2"=>[77,"\x82m"],"\xD3"=>[18,"\x82m"],"\xD4"=>[77,"\x82n"],"\xD5"=>[18,"\x82n"],"\xD6"=>[77,"\x82p"],"\xD7"=>[18,"\x82p"],"\xD8"=>[77,"\x82r"],"\xD9"=>[18,"\x82r"],"\xDA"=>[77,"\x82u"],"\xDB"=>[18,"\x82u"],"\xDC"=>[0,"\x82\x3A"],"\xDD"=>[0,"\x82B"],"\xDE"=>[0,"\x82C"],"\xDF"=>[0,"\x82D"],"\xE0"=>[0,"\x82E"],"\xE1"=>[0,"\x82F"],"\xE2"=>[0,"\x82G"],"\xE3"=>[0,"\x82H"],"\xE4"=>[0,"\x82I"],"\xE5"=>[0,"\x82J"],"\xE6"=>[0,"\x82K"],"\xE7"=>[0,"\x82L"],"\xE8"=>[0,"\x82M"],"\xE9"=>[0,"\x82N"],"\xEA"=>[0,"\x82O"],"\xEB"=>[0,"\x82P"],"\xEC"=>[0,"\x82Q"],"\xED"=>[0,"\x82R"],"\xEE"=>[0,"\x82S"],"\xEF"=>[0,"\x82T"],"\xF0"=>[0,"\x82U"],"\xF1"=>[0,"\x82V"],"\xF2"=>[0,"\x82W"],"\xF3"=>[0,"\x82Y"],"\xF4"=>[0,"\x82j"],"\xF5"=>[0,"\x82k"],"\xF6"=>[0,"\x82q"],"\xF7"=>[0,"\x82v"],"\xF8"=>[0,"\x82w"],"\xF9"=>[0,"\x82x"],"\xFA"=>[0,"\x82y"],"\xFB"=>[0,"\x82z"],"\xFC"=>[82,"\x82"],"\xFD"=>[87,"\x82"],"\xFE"=>[130,"\x82"],"\xFF"=>[9,"\x82"],],["\x00"=>[94,"\xD00"],"\x01"=>[76,"\xD00"],"\x02"=>[104,"\xD00"],"\x03"=>[16,"\xD00"],"\x04"=>[94,"\xD01"],"\x05"=>[76,"\xD01"],"\x06"=>[104,"\xD01"],"\x07"=>[16,"\xD01"],"\x08"=>[94,"\xD02"],"\x09"=>[76,"\xD02"],"\x0A"=>[104,"\xD02"],"\x0B"=>[16,"\xD02"],"\x0C"=>[94,"\xD0a"],"\x0D"=>[76,"\xD0a"],"\x0E"=>[104,"\xD0a"],"\x0F"=>[16,"\xD0a"],"\x10"=>[94,"\xD0c"],"\x11"=>[76,"\xD0c"],"\x12"=>[104,"\xD0c"],"\x13"=>[16,"\xD0c"],"\x14"=>[94,"\xD0e"],"\x15"=>[76,"\xD0e"],"\x16"=>[104,"\xD0e"],"\x17"=>[16,"\xD0e"],"\x18"=>[94,"\xD0i"],"\x19"=>[76,"\xD0i"],"\x1A"=>[104,"\xD0i"],"\x1B"=>[16,"\xD0i"],"\x1C"=>[94,"\xD0o"],"\x1D"=>[76,"\xD0o"],"\x1E"=>[104,"\xD0o"],"\x1F"=>[16,"\xD0o"],"\x20"=>[94,"\xD0s"],"\x21"=>[76,"\xD0s"],"\x22"=>[104,"\xD0s"],"\x23"=>[16,"\xD0s"],"\x24"=>[94,"\xD0t"],"\x25"=>[76,"\xD0t"],"\x26"=>[104,"\xD0t"],"\x27"=>[16,"\xD0t"],"\x28"=>[77,"\xD0\x20"],"\x29"=>[18,"\xD0\x20"],"\x2A"=>[77,"\xD0\x25"],"\x2B"=>[18,"\xD0\x25"],"\x2C"=>[77,"\xD0-"],"-"=>[18,"\xD0-"],"."=>[77,"\xD0."],"\x2F"=>[18,"\xD0."],[77,"\xD0\x2F"],[18,"\xD0\x2F"],[77,"\xD03"],[18,"\xD03"],[77,"\xD04"],[18,"\xD04"],[77,"\xD05"],[18,"\xD05"],[77,"\xD06"],[18,"\xD06"],"\x3A"=>[77,"\xD07"],"\x3B"=>[18,"\xD07"],"\x3C"=>[77,"\xD08"],"\x3D"=>[18,"\xD08"],"\x3E"=>[77,"\xD09"],"\x3F"=>[18,"\xD09"],"\x40"=>[77,"\xD0\x3D"],"A"=>[18,"\xD0\x3D"],"B"=>[77,"\xD0A"],"C"=>[18,"\xD0A"],"D"=>[77,"\xD0_"],"E"=>[18,"\xD0_"],"F"=>[77,"\xD0b"],"G"=>[18,"\xD0b"],"H"=>[77,"\xD0d"],"I"=>[18,"\xD0d"],"J"=>[77,"\xD0f"],"K"=>[18,"\xD0f"],"L"=>[77,"\xD0g"],"M"=>[18,"\xD0g"],"N"=>[77,"\xD0h"],"O"=>[18,"\xD0h"],"P"=>[77,"\xD0l"],"Q"=>[18,"\xD0l"],"R"=>[77,"\xD0m"],"S"=>[18,"\xD0m"],"T"=>[77,"\xD0n"],"U"=>[18,"\xD0n"],"V"=>[77,"\xD0p"],"W"=>[18,"\xD0p"],"X"=>[77,"\xD0r"],"Y"=>[18,"\xD0r"],"Z"=>[77,"\xD0u"],"\x5B"=>[18,"\xD0u"],"\x5C"=>[0,"\xD0\x3A"],"\x5D"=>[0,"\xD0B"],"\x5E"=>[0,"\xD0C"],"_"=>[0,"\xD0D"],"\x60"=>[0,"\xD0E"],"a"=>[0,"\xD0F"],"b"=>[0,"\xD0G"],"c"=>[0,"\xD0H"],"d"=>[0,"\xD0I"],"e"=>[0,"\xD0J"],"f"=>[0,"\xD0K"],"g"=>[0,"\xD0L"],"h"=>[0,"\xD0M"],"i"=>[0,"\xD0N"],"j"=>[0,"\xD0O"],"k"=>[0,"\xD0P"],"l"=>[0,"\xD0Q"],"m"=>[0,"\xD0R"],"n"=>[0,"\xD0S"],"o"=>[0,"\xD0T"],"p"=>[0,"\xD0U"],"q"=>[0,"\xD0V"],"r"=>[0,"\xD0W"],"s"=>[0,"\xD0Y"],"t"=>[0,"\xD0j"],"u"=>[0,"\xD0k"],"v"=>[0,"\xD0q"],"w"=>[0,"\xD0v"],"x"=>[0,"\xD0w"],"y"=>[0,"\xD0x"],"z"=>[0,"\xD0y"],"\x7B"=>[0,"\xD0z"],"\x7C"=>[82,"\xD0"],"\x7D"=>[87,"\xD0"],"~"=>[130,"\xD0"],"\x7F"=>[9,"\xD0"],"\x80"=>[77,"\x800"],"\x81"=>[18,"\x800"],"\x82"=>[77,"\x801"],"\x83"=>[18,"\x801"],"\x84"=>[77,"\x802"],"\x85"=>[18,"\x802"],"\x86"=>[77,"\x80a"],"\x87"=>[18,"\x80a"],"\x88"=>[77,"\x80c"],"\x89"=>[18,"\x80c"],"\x8A"=>[77,"\x80e"],"\x8B"=>[18,"\x80e"],"\x8C"=>[77,"\x80i"],"\x8D"=>[18,"\x80i"],"\x8E"=>[77,"\x80o"],"\x8F"=>[18,"\x80o"],"\x90"=>[77,"\x80s"],"\x91"=>[18,"\x80s"],"\x92"=>[77,"\x80t"],"\x93"=>[18,"\x80t"],"\x94"=>[0,"\x80\x20"],"\x95"=>[0,"\x80\x25"],"\x96"=>[0,"\x80-"],"\x97"=>[0,"\x80."],"\x98"=>[0,"\x80\x2F"],"\x99"=>[0,"\x803"],"\x9A"=>[0,"\x804"],"\x9B"=>[0,"\x805"],"\x9C"=>[0,"\x806"],"\x9D"=>[0,"\x807"],"\x9E"=>[0,"\x808"],"\x9F"=>[0,"\x809"],"\xA0"=>[0,"\x80\x3D"],"\xA1"=>[0,"\x80A"],"\xA2"=>[0,"\x80_"],"\xA3"=>[0,"\x80b"],"\xA4"=>[0,"\x80d"],"\xA5"=>[0,"\x80f"],"\xA6"=>[0,"\x80g"],"\xA7"=>[0,"\x80h"],"\xA8"=>[0,"\x80l"],"\xA9"=>[0,"\x80m"],"\xAA"=>[0,"\x80n"],"\xAB"=>[0,"\x80p"],"\xAC"=>[0,"\x80r"],"\xAD"=>[0,"\x80u"],"\xAE"=>[100,"\x80"],"\xAF"=>[110,"\x80"],"\xB0"=>[111,"\x80"],"\xB1"=>[115,"\x80"],"\xB2"=>[116,"\x80"],"\xB3"=>[118,"\x80"],"\xB4"=>[119,"\x80"],"\xB5"=>[122,"\x80"],"\xB6"=>[123,"\x80"],"\xB7"=>[125,"\x80"],"\xB8"=>[126,"\x80"],"\xB9"=>[129,"\x80"],"\xBA"=>[143,"\x80"],"\xBB"=>[148,"\x80"],"\xBC"=>[151,"\x80"],"\xBD"=>[153,"\x80"],"\xBE"=>[83,"\x80"],"\xBF"=>[10,"\x80"],"\xC0"=>[77,"\x820"],"\xC1"=>[18,"\x820"],"\xC2"=>[77,"\x821"],"\xC3"=>[18,"\x821"],"\xC4"=>[77,"\x822"],"\xC5"=>[18,"\x822"],"\xC6"=>[77,"\x82a"],"\xC7"=>[18,"\x82a"],"\xC8"=>[77,"\x82c"],"\xC9"=>[18,"\x82c"],"\xCA"=>[77,"\x82e"],"\xCB"=>[18,"\x82e"],"\xCC"=>[77,"\x82i"],"\xCD"=>[18,"\x82i"],"\xCE"=>[77,"\x82o"],"\xCF"=>[18,"\x82o"],"\xD0"=>[77,"\x82s"],"\xD1"=>[18,"\x82s"],"\xD2"=>[77,"\x82t"],"\xD3"=>[18,"\x82t"],"\xD4"=>[0,"\x82\x20"],"\xD5"=>[0,"\x82\x25"],"\xD6"=>[0,"\x82-"],"\xD7"=>[0,"\x82."],"\xD8"=>[0,"\x82\x2F"],"\xD9"=>[0,"\x823"],"\xDA"=>[0,"\x824"],"\xDB"=>[0,"\x825"],"\xDC"=>[0,"\x826"],"\xDD"=>[0,"\x827"],"\xDE"=>[0,"\x828"],"\xDF"=>[0,"\x829"],"\xE0"=>[0,"\x82\x3D"],"\xE1"=>[0,"\x82A"],"\xE2"=>[0,"\x82_"],"\xE3"=>[0,"\x82b"],"\xE4"=>[0,"\x82d"],"\xE5"=>[0,"\x82f"],"\xE6"=>[0,"\x82g"],"\xE7"=>[0,"\x82h"],"\xE8"=>[0,"\x82l"],"\xE9"=>[0,"\x82m"],"\xEA"=>[0,"\x82n"],"\xEB"=>[0,"\x82p"],"\xEC"=>[0,"\x82r"],"\xED"=>[0,"\x82u"],"\xEE"=>[100,"\x82"],"\xEF"=>[110,"\x82"],"\xF0"=>[111,"\x82"],"\xF1"=>[115,"\x82"],"\xF2"=>[116,"\x82"],"\xF3"=>[118,"\x82"],"\xF4"=>[119,"\x82"],"\xF5"=>[122,"\x82"],"\xF6"=>[123,"\x82"],"\xF7"=>[125,"\x82"],"\xF8"=>[126,"\x82"],"\xF9"=>[129,"\x82"],"\xFA"=>[143,"\x82"],"\xFB"=>[148,"\x82"],"\xFC"=>[151,"\x82"],"\xFD"=>[153,"\x82"],"\xFE"=>[83,"\x82"],"\xFF"=>[10,"\x82"],],["\x00"=>[94,"\x810"],"\x01"=>[76,"\x810"],"\x02"=>[104,"\x810"],"\x03"=>[16,"\x810"],"\x04"=>[94,"\x811"],"\x05"=>[76,"\x811"],"\x06"=>[104,"\x811"],"\x07"=>[16,"\x811"],"\x08"=>[94,"\x812"],"\x09"=>[76,"\x812"],"\x0A"=>[104,"\x812"],"\x0B"=>[16,"\x812"],"\x0C"=>[94,"\x81a"],"\x0D"=>[76,"\x81a"],"\x0E"=>[104,"\x81a"],"\x0F"=>[16,"\x81a"],"\x10"=>[94,"\x81c"],"\x11"=>[76,"\x81c"],"\x12"=>[104,"\x81c"],"\x13"=>[16,"\x81c"],"\x14"=>[94,"\x81e"],"\x15"=>[76,"\x81e"],"\x16"=>[104,"\x81e"],"\x17"=>[16,"\x81e"],"\x18"=>[94,"\x81i"],"\x19"=>[76,"\x81i"],"\x1A"=>[104,"\x81i"],"\x1B"=>[16,"\x81i"],"\x1C"=>[94,"\x81o"],"\x1D"=>[76,"\x81o"],"\x1E"=>[104,"\x81o"],"\x1F"=>[16,"\x81o"],"\x20"=>[94,"\x81s"],"\x21"=>[76,"\x81s"],"\x22"=>[104,"\x81s"],"\x23"=>[16,"\x81s"],"\x24"=>[94,"\x81t"],"\x25"=>[76,"\x81t"],"\x26"=>[104,"\x81t"],"\x27"=>[16,"\x81t"],"\x28"=>[77,"\x81\x20"],"\x29"=>[18,"\x81\x20"],"\x2A"=>[77,"\x81\x25"],"\x2B"=>[18,"\x81\x25"],"\x2C"=>[77,"\x81-"],"-"=>[18,"\x81-"],"."=>[77,"\x81."],"\x2F"=>[18,"\x81."],[77,"\x81\x2F"],[18,"\x81\x2F"],[77,"\x813"],[18,"\x813"],[77,"\x814"],[18,"\x814"],[77,"\x815"],[18,"\x815"],[77,"\x816"],[18,"\x816"],"\x3A"=>[77,"\x817"],"\x3B"=>[18,"\x817"],"\x3C"=>[77,"\x818"],"\x3D"=>[18,"\x818"],"\x3E"=>[77,"\x819"],"\x3F"=>[18,"\x819"],"\x40"=>[77,"\x81\x3D"],"A"=>[18,"\x81\x3D"],"B"=>[77,"\x81A"],"C"=>[18,"\x81A"],"D"=>[77,"\x81_"],"E"=>[18,"\x81_"],"F"=>[77,"\x81b"],"G"=>[18,"\x81b"],"H"=>[77,"\x81d"],"I"=>[18,"\x81d"],"J"=>[77,"\x81f"],"K"=>[18,"\x81f"],"L"=>[77,"\x81g"],"M"=>[18,"\x81g"],"N"=>[77,"\x81h"],"O"=>[18,"\x81h"],"P"=>[77,"\x81l"],"Q"=>[18,"\x81l"],"R"=>[77,"\x81m"],"S"=>[18,"\x81m"],"T"=>[77,"\x81n"],"U"=>[18,"\x81n"],"V"=>[77,"\x81p"],"W"=>[18,"\x81p"],"X"=>[77,"\x81r"],"Y"=>[18,"\x81r"],"Z"=>[77,"\x81u"],"\x5B"=>[18,"\x81u"],"\x5C"=>[0,"\x81\x3A"],"\x5D"=>[0,"\x81B"],"\x5E"=>[0,"\x81C"],"_"=>[0,"\x81D"],"\x60"=>[0,"\x81E"],"a"=>[0,"\x81F"],"b"=>[0,"\x81G"],"c"=>[0,"\x81H"],"d"=>[0,"\x81I"],"e"=>[0,"\x81J"],"f"=>[0,"\x81K"],"g"=>[0,"\x81L"],"h"=>[0,"\x81M"],"i"=>[0,"\x81N"],"j"=>[0,"\x81O"],"k"=>[0,"\x81P"],"l"=>[0,"\x81Q"],"m"=>[0,"\x81R"],"n"=>[0,"\x81S"],"o"=>[0,"\x81T"],"p"=>[0,"\x81U"],"q"=>[0,"\x81V"],"r"=>[0,"\x81W"],"s"=>[0,"\x81Y"],"t"=>[0,"\x81j"],"u"=>[0,"\x81k"],"v"=>[0,"\x81q"],"w"=>[0,"\x81v"],"x"=>[0,"\x81w"],"y"=>[0,"\x81x"],"z"=>[0,"\x81y"],"\x7B"=>[0,"\x81z"],"\x7C"=>[82,"\x81"],"\x7D"=>[87,"\x81"],"~"=>[130,"\x81"],"\x7F"=>[9,"\x81"],"\x80"=>[94,"\x840"],"\x81"=>[76,"\x840"],"\x82"=>[104,"\x840"],"\x83"=>[16,"\x840"],"\x84"=>[94,"\x841"],"\x85"=>[76,"\x841"],"\x86"=>[104,"\x841"],"\x87"=>[16,"\x841"],"\x88"=>[94,"\x842"],"\x89"=>[76,"\x842"],"\x8A"=>[104,"\x842"],"\x8B"=>[16,"\x842"],"\x8C"=>[94,"\x84a"],"\x8D"=>[76,"\x84a"],"\x8E"=>[104,"\x84a"],"\x8F"=>[16,"\x84a"],"\x90"=>[94,"\x84c"],"\x91"=>[76,"\x84c"],"\x92"=>[104,"\x84c"],"\x93"=>[16,"\x84c"],"\x94"=>[94,"\x84e"],"\x95"=>[76,"\x84e"],"\x96"=>[104,"\x84e"],"\x97"=>[16,"\x84e"],"\x98"=>[94,"\x84i"],"\x99"=>[76,"\x84i"],"\x9A"=>[104,"\x84i"],"\x9B"=>[16,"\x84i"],"\x9C"=>[94,"\x84o"],"\x9D"=>[76,"\x84o"],"\x9E"=>[104,"\x84o"],"\x9F"=>[16,"\x84o"],"\xA0"=>[94,"\x84s"],"\xA1"=>[76,"\x84s"],"\xA2"=>[104,"\x84s"],"\xA3"=>[16,"\x84s"],"\xA4"=>[94,"\x84t"],"\xA5"=>[76,"\x84t"],"\xA6"=>[104,"\x84t"],"\xA7"=>[16,"\x84t"],"\xA8"=>[77,"\x84\x20"],"\xA9"=>[18,"\x84\x20"],"\xAA"=>[77,"\x84\x25"],"\xAB"=>[18,"\x84\x25"],"\xAC"=>[77,"\x84-"],"\xAD"=>[18,"\x84-"],"\xAE"=>[77,"\x84."],"\xAF"=>[18,"\x84."],"\xB0"=>[77,"\x84\x2F"],"\xB1"=>[18,"\x84\x2F"],"\xB2"=>[77,"\x843"],"\xB3"=>[18,"\x843"],"\xB4"=>[77,"\x844"],"\xB5"=>[18,"\x844"],"\xB6"=>[77,"\x845"],"\xB7"=>[18,"\x845"],"\xB8"=>[77,"\x846"],"\xB9"=>[18,"\x846"],"\xBA"=>[77,"\x847"],"\xBB"=>[18,"\x847"],"\xBC"=>[77,"\x848"],"\xBD"=>[18,"\x848"],"\xBE"=>[77,"\x849"],"\xBF"=>[18,"\x849"],"\xC0"=>[77,"\x84\x3D"],"\xC1"=>[18,"\x84\x3D"],"\xC2"=>[77,"\x84A"],"\xC3"=>[18,"\x84A"],"\xC4"=>[77,"\x84_"],"\xC5"=>[18,"\x84_"],"\xC6"=>[77,"\x84b"],"\xC7"=>[18,"\x84b"],"\xC8"=>[77,"\x84d"],"\xC9"=>[18,"\x84d"],"\xCA"=>[77,"\x84f"],"\xCB"=>[18,"\x84f"],"\xCC"=>[77,"\x84g"],"\xCD"=>[18,"\x84g"],"\xCE"=>[77,"\x84h"],"\xCF"=>[18,"\x84h"],"\xD0"=>[77,"\x84l"],"\xD1"=>[18,"\x84l"],"\xD2"=>[77,"\x84m"],"\xD3"=>[18,"\x84m"],"\xD4"=>[77,"\x84n"],"\xD5"=>[18,"\x84n"],"\xD6"=>[77,"\x84p"],"\xD7"=>[18,"\x84p"],"\xD8"=>[77,"\x84r"],"\xD9"=>[18,"\x84r"],"\xDA"=>[77,"\x84u"],"\xDB"=>[18,"\x84u"],"\xDC"=>[0,"\x84\x3A"],"\xDD"=>[0,"\x84B"],"\xDE"=>[0,"\x84C"],"\xDF"=>[0,"\x84D"],"\xE0"=>[0,"\x84E"],"\xE1"=>[0,"\x84F"],"\xE2"=>[0,"\x84G"],"\xE3"=>[0,"\x84H"],"\xE4"=>[0,"\x84I"],"\xE5"=>[0,"\x84J"],"\xE6"=>[0,"\x84K"],"\xE7"=>[0,"\x84L"],"\xE8"=>[0,"\x84M"],"\xE9"=>[0,"\x84N"],"\xEA"=>[0,"\x84O"],"\xEB"=>[0,"\x84P"],"\xEC"=>[0,"\x84Q"],"\xED"=>[0,"\x84R"],"\xEE"=>[0,"\x84S"],"\xEF"=>[0,"\x84T"],"\xF0"=>[0,"\x84U"],"\xF1"=>[0,"\x84V"],"\xF2"=>[0,"\x84W"],"\xF3"=>[0,"\x84Y"],"\xF4"=>[0,"\x84j"],"\xF5"=>[0,"\x84k"],"\xF6"=>[0,"\x84q"],"\xF7"=>[0,"\x84v"],"\xF8"=>[0,"\x84w"],"\xF9"=>[0,"\x84x"],"\xFA"=>[0,"\x84y"],"\xFB"=>[0,"\x84z"],"\xFC"=>[82,"\x84"],"\xFD"=>[87,"\x84"],"\xFE"=>[130,"\x84"],"\xFF"=>[9,"\x84"],],["\x00"=>[94,"\xE60"],"\x01"=>[76,"\xE60"],"\x02"=>[104,"\xE60"],"\x03"=>[16,"\xE60"],"\x04"=>[94,"\xE61"],"\x05"=>[76,"\xE61"],"\x06"=>[104,"\xE61"],"\x07"=>[16,"\xE61"],"\x08"=>[94,"\xE62"],"\x09"=>[76,"\xE62"],"\x0A"=>[104,"\xE62"],"\x0B"=>[16,"\xE62"],"\x0C"=>[94,"\xE6a"],"\x0D"=>[76,"\xE6a"],"\x0E"=>[104,"\xE6a"],"\x0F"=>[16,"\xE6a"],"\x10"=>[94,"\xE6c"],"\x11"=>[76,"\xE6c"],"\x12"=>[104,"\xE6c"],"\x13"=>[16,"\xE6c"],"\x14"=>[94,"\xE6e"],"\x15"=>[76,"\xE6e"],"\x16"=>[104,"\xE6e"],"\x17"=>[16,"\xE6e"],"\x18"=>[94,"\xE6i"],"\x19"=>[76,"\xE6i"],"\x1A"=>[104,"\xE6i"],"\x1B"=>[16,"\xE6i"],"\x1C"=>[94,"\xE6o"],"\x1D"=>[76,"\xE6o"],"\x1E"=>[104,"\xE6o"],"\x1F"=>[16,"\xE6o"],"\x20"=>[94,"\xE6s"],"\x21"=>[76,"\xE6s"],"\x22"=>[104,"\xE6s"],"\x23"=>[16,"\xE6s"],"\x24"=>[94,"\xE6t"],"\x25"=>[76,"\xE6t"],"\x26"=>[104,"\xE6t"],"\x27"=>[16,"\xE6t"],"\x28"=>[77,"\xE6\x20"],"\x29"=>[18,"\xE6\x20"],"\x2A"=>[77,"\xE6\x25"],"\x2B"=>[18,"\xE6\x25"],"\x2C"=>[77,"\xE6-"],"-"=>[18,"\xE6-"],"."=>[77,"\xE6."],"\x2F"=>[18,"\xE6."],[77,"\xE6\x2F"],[18,"\xE6\x2F"],[77,"\xE63"],[18,"\xE63"],[77,"\xE64"],[18,"\xE64"],[77,"\xE65"],[18,"\xE65"],[77,"\xE66"],[18,"\xE66"],"\x3A"=>[77,"\xE67"],"\x3B"=>[18,"\xE67"],"\x3C"=>[77,"\xE68"],"\x3D"=>[18,"\xE68"],"\x3E"=>[77,"\xE69"],"\x3F"=>[18,"\xE69"],"\x40"=>[77,"\xE6\x3D"],"A"=>[18,"\xE6\x3D"],"B"=>[77,"\xE6A"],"C"=>[18,"\xE6A"],"D"=>[77,"\xE6_"],"E"=>[18,"\xE6_"],"F"=>[77,"\xE6b"],"G"=>[18,"\xE6b"],"H"=>[77,"\xE6d"],"I"=>[18,"\xE6d"],"J"=>[77,"\xE6f"],"K"=>[18,"\xE6f"],"L"=>[77,"\xE6g"],"M"=>[18,"\xE6g"],"N"=>[77,"\xE6h"],"O"=>[18,"\xE6h"],"P"=>[77,"\xE6l"],"Q"=>[18,"\xE6l"],"R"=>[77,"\xE6m"],"S"=>[18,"\xE6m"],"T"=>[77,"\xE6n"],"U"=>[18,"\xE6n"],"V"=>[77,"\xE6p"],"W"=>[18,"\xE6p"],"X"=>[77,"\xE6r"],"Y"=>[18,"\xE6r"],"Z"=>[77,"\xE6u"],"\x5B"=>[18,"\xE6u"],"\x5C"=>[0,"\xE6\x3A"],"\x5D"=>[0,"\xE6B"],"\x5E"=>[0,"\xE6C"],"_"=>[0,"\xE6D"],"\x60"=>[0,"\xE6E"],"a"=>[0,"\xE6F"],"b"=>[0,"\xE6G"],"c"=>[0,"\xE6H"],"d"=>[0,"\xE6I"],"e"=>[0,"\xE6J"],"f"=>[0,"\xE6K"],"g"=>[0,"\xE6L"],"h"=>[0,"\xE6M"],"i"=>[0,"\xE6N"],"j"=>[0,"\xE6O"],"k"=>[0,"\xE6P"],"l"=>[0,"\xE6Q"],"m"=>[0,"\xE6R"],"n"=>[0,"\xE6S"],"o"=>[0,"\xE6T"],"p"=>[0,"\xE6U"],"q"=>[0,"\xE6V"],"r"=>[0,"\xE6W"],"s"=>[0,"\xE6Y"],"t"=>[0,"\xE6j"],"u"=>[0,"\xE6k"],"v"=>[0,"\xE6q"],"w"=>[0,"\xE6v"],"x"=>[0,"\xE6w"],"y"=>[0,"\xE6x"],"z"=>[0,"\xE6y"],"\x7B"=>[0,"\xE6z"],"\x7C"=>[82,"\xE6"],"\x7D"=>[87,"\xE6"],"~"=>[130,"\xE6"],"\x7F"=>[9,"\xE6"],"\x80"=>[77,"\x810"],"\x81"=>[18,"\x810"],"\x82"=>[77,"\x811"],"\x83"=>[18,"\x811"],"\x84"=>[77,"\x812"],"\x85"=>[18,"\x812"],"\x86"=>[77,"\x81a"],"\x87"=>[18,"\x81a"],"\x88"=>[77,"\x81c"],"\x89"=>[18,"\x81c"],"\x8A"=>[77,"\x81e"],"\x8B"=>[18,"\x81e"],"\x8C"=>[77,"\x81i"],"\x8D"=>[18,"\x81i"],"\x8E"=>[77,"\x81o"],"\x8F"=>[18,"\x81o"],"\x90"=>[77,"\x81s"],"\x91"=>[18,"\x81s"],"\x92"=>[77,"\x81t"],"\x93"=>[18,"\x81t"],"\x94"=>[0,"\x81\x20"],"\x95"=>[0,"\x81\x25"],"\x96"=>[0,"\x81-"],"\x97"=>[0,"\x81."],"\x98"=>[0,"\x81\x2F"],"\x99"=>[0,"\x813"],"\x9A"=>[0,"\x814"],"\x9B"=>[0,"\x815"],"\x9C"=>[0,"\x816"],"\x9D"=>[0,"\x817"],"\x9E"=>[0,"\x818"],"\x9F"=>[0,"\x819"],"\xA0"=>[0,"\x81\x3D"],"\xA1"=>[0,"\x81A"],"\xA2"=>[0,"\x81_"],"\xA3"=>[0,"\x81b"],"\xA4"=>[0,"\x81d"],"\xA5"=>[0,"\x81f"],"\xA6"=>[0,"\x81g"],"\xA7"=>[0,"\x81h"],"\xA8"=>[0,"\x81l"],"\xA9"=>[0,"\x81m"],"\xAA"=>[0,"\x81n"],"\xAB"=>[0,"\x81p"],"\xAC"=>[0,"\x81r"],"\xAD"=>[0,"\x81u"],"\xAE"=>[100,"\x81"],"\xAF"=>[110,"\x81"],"\xB0"=>[111,"\x81"],"\xB1"=>[115,"\x81"],"\xB2"=>[116,"\x81"],"\xB3"=>[118,"\x81"],"\xB4"=>[119,"\x81"],"\xB5"=>[122,"\x81"],"\xB6"=>[123,"\x81"],"\xB7"=>[125,"\x81"],"\xB8"=>[126,"\x81"],"\xB9"=>[129,"\x81"],"\xBA"=>[143,"\x81"],"\xBB"=>[148,"\x81"],"\xBC"=>[151,"\x81"],"\xBD"=>[153,"\x81"],"\xBE"=>[83,"\x81"],"\xBF"=>[10,"\x81"],"\xC0"=>[77,"\x840"],"\xC1"=>[18,"\x840"],"\xC2"=>[77,"\x841"],"\xC3"=>[18,"\x841"],"\xC4"=>[77,"\x842"],"\xC5"=>[18,"\x842"],"\xC6"=>[77,"\x84a"],"\xC7"=>[18,"\x84a"],"\xC8"=>[77,"\x84c"],"\xC9"=>[18,"\x84c"],"\xCA"=>[77,"\x84e"],"\xCB"=>[18,"\x84e"],"\xCC"=>[77,"\x84i"],"\xCD"=>[18,"\x84i"],"\xCE"=>[77,"\x84o"],"\xCF"=>[18,"\x84o"],"\xD0"=>[77,"\x84s"],"\xD1"=>[18,"\x84s"],"\xD2"=>[77,"\x84t"],"\xD3"=>[18,"\x84t"],"\xD4"=>[0,"\x84\x20"],"\xD5"=>[0,"\x84\x25"],"\xD6"=>[0,"\x84-"],"\xD7"=>[0,"\x84."],"\xD8"=>[0,"\x84\x2F"],"\xD9"=>[0,"\x843"],"\xDA"=>[0,"\x844"],"\xDB"=>[0,"\x845"],"\xDC"=>[0,"\x846"],"\xDD"=>[0,"\x847"],"\xDE"=>[0,"\x848"],"\xDF"=>[0,"\x849"],"\xE0"=>[0,"\x84\x3D"],"\xE1"=>[0,"\x84A"],"\xE2"=>[0,"\x84_"],"\xE3"=>[0,"\x84b"],"\xE4"=>[0,"\x84d"],"\xE5"=>[0,"\x84f"],"\xE6"=>[0,"\x84g"],"\xE7"=>[0,"\x84h"],"\xE8"=>[0,"\x84l"],"\xE9"=>[0,"\x84m"],"\xEA"=>[0,"\x84n"],"\xEB"=>[0,"\x84p"],"\xEC"=>[0,"\x84r"],"\xED"=>[0,"\x84u"],"\xEE"=>[100,"\x84"],"\xEF"=>[110,"\x84"],"\xF0"=>[111,"\x84"],"\xF1"=>[115,"\x84"],"\xF2"=>[116,"\x84"],"\xF3"=>[118,"\x84"],"\xF4"=>[119,"\x84"],"\xF5"=>[122,"\x84"],"\xF6"=>[123,"\x84"],"\xF7"=>[125,"\x84"],"\xF8"=>[126,"\x84"],"\xF9"=>[129,"\x84"],"\xFA"=>[143,"\x84"],"\xFB"=>[148,"\x84"],"\xFC"=>[151,"\x84"],"\xFD"=>[153,"\x84"],"\xFE"=>[83,"\x84"],"\xFF"=>[10,"\x84"],],["\x00"=>[77,"\xE60"],"\x01"=>[18,"\xE60"],"\x02"=>[77,"\xE61"],"\x03"=>[18,"\xE61"],"\x04"=>[77,"\xE62"],"\x05"=>[18,"\xE62"],"\x06"=>[77,"\xE6a"],"\x07"=>[18,"\xE6a"],"\x08"=>[77,"\xE6c"],"\x09"=>[18,"\xE6c"],"\x0A"=>[77,"\xE6e"],"\x0B"=>[18,"\xE6e"],"\x0C"=>[77,"\xE6i"],"\x0D"=>[18,"\xE6i"],"\x0E"=>[77,"\xE6o"],"\x0F"=>[18,"\xE6o"],"\x10"=>[77,"\xE6s"],"\x11"=>[18,"\xE6s"],"\x12"=>[77,"\xE6t"],"\x13"=>[18,"\xE6t"],"\x14"=>[0,"\xE6\x20"],"\x15"=>[0,"\xE6\x25"],"\x16"=>[0,"\xE6-"],"\x17"=>[0,"\xE6."],"\x18"=>[0,"\xE6\x2F"],"\x19"=>[0,"\xE63"],"\x1A"=>[0,"\xE64"],"\x1B"=>[0,"\xE65"],"\x1C"=>[0,"\xE66"],"\x1D"=>[0,"\xE67"],"\x1E"=>[0,"\xE68"],"\x1F"=>[0,"\xE69"],"\x20"=>[0,"\xE6\x3D"],"\x21"=>[0,"\xE6A"],"\x22"=>[0,"\xE6_"],"\x23"=>[0,"\xE6b"],"\x24"=>[0,"\xE6d"],"\x25"=>[0,"\xE6f"],"\x26"=>[0,"\xE6g"],"\x27"=>[0,"\xE6h"],"\x28"=>[0,"\xE6l"],"\x29"=>[0,"\xE6m"],"\x2A"=>[0,"\xE6n"],"\x2B"=>[0,"\xE6p"],"\x2C"=>[0,"\xE6r"],"-"=>[0,"\xE6u"],"."=>[100,"\xE6"],"\x2F"=>[110,"\xE6"],[111,"\xE6"],[115,"\xE6"],[116,"\xE6"],[118,"\xE6"],[119,"\xE6"],[122,"\xE6"],[123,"\xE6"],[125,"\xE6"],[126,"\xE6"],[129,"\xE6"],"\x3A"=>[143,"\xE6"],"\x3B"=>[148,"\xE6"],"\x3C"=>[151,"\xE6"],"\x3D"=>[153,"\xE6"],"\x3E"=>[83,"\xE6"],"\x3F"=>[10,"\xE6"],"\x40"=>[0,"\x810"],"A"=>[0,"\x811"],"B"=>[0,"\x812"],"C"=>[0,"\x81a"],"D"=>[0,"\x81c"],"E"=>[0,"\x81e"],"F"=>[0,"\x81i"],"G"=>[0,"\x81o"],"H"=>[0,"\x81s"],"I"=>[0,"\x81t"],"J"=>[73,"\x81"],"K"=>[88,"\x81"],"L"=>[89,"\x81"],"M"=>[96,"\x81"],"N"=>[97,"\x81"],"O"=>[99,"\x81"],"P"=>[106,"\x81"],"Q"=>[136,"\x81"],"R"=>[139,"\x81"],"S"=>[141,"\x81"],"T"=>[145,"\x81"],"U"=>[147,"\x81"],"V"=>[149,"\x81"],"W"=>[101,"\x81"],"X"=>[112,"\x81"],"Y"=>[117,"\x81"],"Z"=>[120,"\x81"],"\x5B"=>[124,"\x81"],"\x5C"=>[127,"\x81"],"\x5D"=>[144,"\x81"],"\x5E"=>[152,"\x81"],"_"=>[11,"\x81"],"\x60"=>[0,"\x840"],"a"=>[0,"\x841"],"b"=>[0,"\x842"],"c"=>[0,"\x84a"],"d"=>[0,"\x84c"],"e"=>[0,"\x84e"],"f"=>[0,"\x84i"],"g"=>[0,"\x84o"],"h"=>[0,"\x84s"],"i"=>[0,"\x84t"],"j"=>[73,"\x84"],"k"=>[88,"\x84"],"l"=>[89,"\x84"],"m"=>[96,"\x84"],"n"=>[97,"\x84"],"o"=>[99,"\x84"],"p"=>[106,"\x84"],"q"=>[136,"\x84"],"r"=>[139,"\x84"],"s"=>[141,"\x84"],"t"=>[145,"\x84"],"u"=>[147,"\x84"],"v"=>[149,"\x84"],"w"=>[101,"\x84"],"x"=>[112,"\x84"],"y"=>[117,"\x84"],"z"=>[120,"\x84"],"\x7B"=>[124,"\x84"],"\x7C"=>[127,"\x84"],"\x7D"=>[144,"\x84"],"~"=>[152,"\x84"],"\x7F"=>[11,"\x84"],"\x80"=>[0,"\x850"],"\x81"=>[0,"\x851"],"\x82"=>[0,"\x852"],"\x83"=>[0,"\x85a"],"\x84"=>[0,"\x85c"],"\x85"=>[0,"\x85e"],"\x86"=>[0,"\x85i"],"\x87"=>[0,"\x85o"],"\x88"=>[0,"\x85s"],"\x89"=>[0,"\x85t"],"\x8A"=>[73,"\x85"],"\x8B"=>[88,"\x85"],"\x8C"=>[89,"\x85"],"\x8D"=>[96,"\x85"],"\x8E"=>[97,"\x85"],"\x8F"=>[99,"\x85"],"\x90"=>[106,"\x85"],"\x91"=>[136,"\x85"],"\x92"=>[139,"\x85"],"\x93"=>[141,"\x85"],"\x94"=>[145,"\x85"],"\x95"=>[147,"\x85"],"\x96"=>[149,"\x85"],"\x97"=>[101,"\x85"],"\x98"=>[112,"\x85"],"\x99"=>[117,"\x85"],"\x9A"=>[120,"\x85"],"\x9B"=>[124,"\x85"],"\x9C"=>[127,"\x85"],"\x9D"=>[144,"\x85"],"\x9E"=>[152,"\x85"],"\x9F"=>[11,"\x85"],"\xA0"=>[0,"\x860"],"\xA1"=>[0,"\x861"],"\xA2"=>[0,"\x862"],"\xA3"=>[0,"\x86a"],"\xA4"=>[0,"\x86c"],"\xA5"=>[0,"\x86e"],"\xA6"=>[0,"\x86i"],"\xA7"=>[0,"\x86o"],"\xA8"=>[0,"\x86s"],"\xA9"=>[0,"\x86t"],"\xAA"=>[73,"\x86"],"\xAB"=>[88,"\x86"],"\xAC"=>[89,"\x86"],"\xAD"=>[96,"\x86"],"\xAE"=>[97,"\x86"],"\xAF"=>[99,"\x86"],"\xB0"=>[106,"\x86"],"\xB1"=>[136,"\x86"],"\xB2"=>[139,"\x86"],"\xB3"=>[141,"\x86"],"\xB4"=>[145,"\x86"],"\xB5"=>[147,"\x86"],"\xB6"=>[149,"\x86"],"\xB7"=>[101,"\x86"],"\xB8"=>[112,"\x86"],"\xB9"=>[117,"\x86"],"\xBA"=>[120,"\x86"],"\xBB"=>[124,"\x86"],"\xBC"=>[127,"\x86"],"\xBD"=>[144,"\x86"],"\xBE"=>[152,"\x86"],"\xBF"=>[11,"\x86"],"\xC0"=>[0,"\x880"],"\xC1"=>[0,"\x881"],"\xC2"=>[0,"\x882"],"\xC3"=>[0,"\x88a"],"\xC4"=>[0,"\x88c"],"\xC5"=>[0,"\x88e"],"\xC6"=>[0,"\x88i"],"\xC7"=>[0,"\x88o"],"\xC8"=>[0,"\x88s"],"\xC9"=>[0,"\x88t"],"\xCA"=>[73,"\x88"],"\xCB"=>[88,"\x88"],"\xCC"=>[89,"\x88"],"\xCD"=>[96,"\x88"],"\xCE"=>[97,"\x88"],"\xCF"=>[99,"\x88"],"\xD0"=>[106,"\x88"],"\xD1"=>[136,"\x88"],"\xD2"=>[139,"\x88"],"\xD3"=>[141,"\x88"],"\xD4"=>[145,"\x88"],"\xD5"=>[147,"\x88"],"\xD6"=>[149,"\x88"],"\xD7"=>[101,"\x88"],"\xD8"=>[112,"\x88"],"\xD9"=>[117,"\x88"],"\xDA"=>[120,"\x88"],"\xDB"=>[124,"\x88"],"\xDC"=>[127,"\x88"],"\xDD"=>[144,"\x88"],"\xDE"=>[152,"\x88"],"\xDF"=>[11,"\x88"],"\xE0"=>[0,"\x920"],"\xE1"=>[0,"\x921"],"\xE2"=>[0,"\x922"],"\xE3"=>[0,"\x92a"],"\xE4"=>[0,"\x92c"],"\xE5"=>[0,"\x92e"],"\xE6"=>[0,"\x92i"],"\xE7"=>[0,"\x92o"],"\xE8"=>[0,"\x92s"],"\xE9"=>[0,"\x92t"],"\xEA"=>[73,"\x92"],"\xEB"=>[88,"\x92"],"\xEC"=>[89,"\x92"],"\xED"=>[96,"\x92"],"\xEE"=>[97,"\x92"],"\xEF"=>[99,"\x92"],"\xF0"=>[106,"\x92"],"\xF1"=>[136,"\x92"],"\xF2"=>[139,"\x92"],"\xF3"=>[141,"\x92"],"\xF4"=>[145,"\x92"],"\xF5"=>[147,"\x92"],"\xF6"=>[149,"\x92"],"\xF7"=>[101,"\x92"],"\xF8"=>[112,"\x92"],"\xF9"=>[117,"\x92"],"\xFA"=>[120,"\x92"],"\xFB"=>[124,"\x92"],"\xFC"=>[127,"\x92"],"\xFD"=>[144,"\x92"],"\xFE"=>[152,"\x92"],"\xFF"=>[11,"\x92"],],["\x00"=>[0,"\xE60"],"\x01"=>[0,"\xE61"],"\x02"=>[0,"\xE62"],"\x03"=>[0,"\xE6a"],"\x04"=>[0,"\xE6c"],"\x05"=>[0,"\xE6e"],"\x06"=>[0,"\xE6i"],"\x07"=>[0,"\xE6o"],"\x08"=>[0,"\xE6s"],"\x09"=>[0,"\xE6t"],"\x0A"=>[73,"\xE6"],"\x0B"=>[88,"\xE6"],"\x0C"=>[89,"\xE6"],"\x0D"=>[96,"\xE6"],"\x0E"=>[97,"\xE6"],"\x0F"=>[99,"\xE6"],"\x10"=>[106,"\xE6"],"\x11"=>[136,"\xE6"],"\x12"=>[139,"\xE6"],"\x13"=>[141,"\xE6"],"\x14"=>[145,"\xE6"],"\x15"=>[147,"\xE6"],"\x16"=>[149,"\xE6"],"\x17"=>[101,"\xE6"],"\x18"=>[112,"\xE6"],"\x19"=>[117,"\xE6"],"\x1A"=>[120,"\xE6"],"\x1B"=>[124,"\xE6"],"\x1C"=>[127,"\xE6"],"\x1D"=>[144,"\xE6"],"\x1E"=>[152,"\xE6"],"\x1F"=>[11,"\xE6"],"\x20"=>[92,"\x81"],"\x21"=>[95,"\x81"],"\x22"=>[137,"\x81"],"\x23"=>[142,"\x81"],"\x24"=>[150,"\x81"],"\x25"=>[74,"\x81"],"\x26"=>[90,"\x81"],"\x27"=>[98,"\x81"],"\x28"=>[107,"\x81"],"\x29"=>[140,"\x81"],"\x2A"=>[146,"\x81"],"\x2B"=>[102,"\x81"],"\x2C"=>[113,"\x81"],"-"=>[121,"\x81"],"."=>[128,"\x81"],"\x2F"=>[12,"\x81"],[92,"\x84"],[95,"\x84"],[137,"\x84"],[142,"\x84"],[150,"\x84"],[74,"\x84"],[90,"\x84"],[98,"\x84"],[107,"\x84"],[140,"\x84"],"\x3A"=>[146,"\x84"],"\x3B"=>[102,"\x84"],"\x3C"=>[113,"\x84"],"\x3D"=>[121,"\x84"],"\x3E"=>[128,"\x84"],"\x3F"=>[12,"\x84"],"\x40"=>[92,"\x85"],"A"=>[95,"\x85"],"B"=>[137,"\x85"],"C"=>[142,"\x85"],"D"=>[150,"\x85"],"E"=>[74,"\x85"],"F"=>[90,"\x85"],"G"=>[98,"\x85"],"H"=>[107,"\x85"],"I"=>[140,"\x85"],"J"=>[146,"\x85"],"K"=>[102,"\x85"],"L"=>[113,"\x85"],"M"=>[121,"\x85"],"N"=>[128,"\x85"],"O"=>[12,"\x85"],"P"=>[92,"\x86"],"Q"=>[95,"\x86"],"R"=>[137,"\x86"],"S"=>[142,"\x86"],"T"=>[150,"\x86"],"U"=>[74,"\x86"],"V"=>[90,"\x86"],"W"=>[98,"\x86"],"X"=>[107,"\x86"],"Y"=>[140,"\x86"],"Z"=>[146,"\x86"],"\x5B"=>[102,"\x86"],"\x5C"=>[113,"\x86"],"\x5D"=>[121,"\x86"],"\x5E"=>[128,"\x86"],"_"=>[12,"\x86"],"\x60"=>[92,"\x88"],"a"=>[95,"\x88"],"b"=>[137,"\x88"],"c"=>[142,"\x88"],"d"=>[150,"\x88"],"e"=>[74,"\x88"],"f"=>[90,"\x88"],"g"=>[98,"\x88"],"h"=>[107,"\x88"],"i"=>[140,"\x88"],"j"=>[146,"\x88"],"k"=>[102,"\x88"],"l"=>[113,"\x88"],"m"=>[121,"\x88"],"n"=>[128,"\x88"],"o"=>[12,"\x88"],"p"=>[92,"\x92"],"q"=>[95,"\x92"],"r"=>[137,"\x92"],"s"=>[142,"\x92"],"t"=>[150,"\x92"],"u"=>[74,"\x92"],"v"=>[90,"\x92"],"w"=>[98,"\x92"],"x"=>[107,"\x92"],"y"=>[140,"\x92"],"z"=>[146,"\x92"],"\x7B"=>[102,"\x92"],"\x7C"=>[113,"\x92"],"\x7D"=>[121,"\x92"],"~"=>[128,"\x92"],"\x7F"=>[12,"\x92"],"\x80"=>[92,"\x9A"],"\x81"=>[95,"\x9A"],"\x82"=>[137,"\x9A"],"\x83"=>[142,"\x9A"],"\x84"=>[150,"\x9A"],"\x85"=>[74,"\x9A"],"\x86"=>[90,"\x9A"],"\x87"=>[98,"\x9A"],"\x88"=>[107,"\x9A"],"\x89"=>[140,"\x9A"],"\x8A"=>[146,"\x9A"],"\x8B"=>[102,"\x9A"],"\x8C"=>[113,"\x9A"],"\x8D"=>[121,"\x9A"],"\x8E"=>[128,"\x9A"],"\x8F"=>[12,"\x9A"],"\x90"=>[92,"\x9C"],"\x91"=>[95,"\x9C"],"\x92"=>[137,"\x9C"],"\x93"=>[142,"\x9C"],"\x94"=>[150,"\x9C"],"\x95"=>[74,"\x9C"],"\x96"=>[90,"\x9C"],"\x97"=>[98,"\x9C"],"\x98"=>[107,"\x9C"],"\x99"=>[140,"\x9C"],"\x9A"=>[146,"\x9C"],"\x9B"=>[102,"\x9C"],"\x9C"=>[113,"\x9C"],"\x9D"=>[121,"\x9C"],"\x9E"=>[128,"\x9C"],"\x9F"=>[12,"\x9C"],"\xA0"=>[92,"\xA0"],"\xA1"=>[95,"\xA0"],"\xA2"=>[137,"\xA0"],"\xA3"=>[142,"\xA0"],"\xA4"=>[150,"\xA0"],"\xA5"=>[74,"\xA0"],"\xA6"=>[90,"\xA0"],"\xA7"=>[98,"\xA0"],"\xA8"=>[107,"\xA0"],"\xA9"=>[140,"\xA0"],"\xAA"=>[146,"\xA0"],"\xAB"=>[102,"\xA0"],"\xAC"=>[113,"\xA0"],"\xAD"=>[121,"\xA0"],"\xAE"=>[128,"\xA0"],"\xAF"=>[12,"\xA0"],"\xB0"=>[92,"\xA3"],"\xB1"=>[95,"\xA3"],"\xB2"=>[137,"\xA3"],"\xB3"=>[142,"\xA3"],"\xB4"=>[150,"\xA3"],"\xB5"=>[74,"\xA3"],"\xB6"=>[90,"\xA3"],"\xB7"=>[98,"\xA3"],"\xB8"=>[107,"\xA3"],"\xB9"=>[140,"\xA3"],"\xBA"=>[146,"\xA3"],"\xBB"=>[102,"\xA3"],"\xBC"=>[113,"\xA3"],"\xBD"=>[121,"\xA3"],"\xBE"=>[128,"\xA3"],"\xBF"=>[12,"\xA3"],"\xC0"=>[92,"\xA4"],"\xC1"=>[95,"\xA4"],"\xC2"=>[137,"\xA4"],"\xC3"=>[142,"\xA4"],"\xC4"=>[150,"\xA4"],"\xC5"=>[74,"\xA4"],"\xC6"=>[90,"\xA4"],"\xC7"=>[98,"\xA4"],"\xC8"=>[107,"\xA4"],"\xC9"=>[140,"\xA4"],"\xCA"=>[146,"\xA4"],"\xCB"=>[102,"\xA4"],"\xCC"=>[113,"\xA4"],"\xCD"=>[121,"\xA4"],"\xCE"=>[128,"\xA4"],"\xCF"=>[12,"\xA4"],"\xD0"=>[92,"\xA9"],"\xD1"=>[95,"\xA9"],"\xD2"=>[137,"\xA9"],"\xD3"=>[142,"\xA9"],"\xD4"=>[150,"\xA9"],"\xD5"=>[74,"\xA9"],"\xD6"=>[90,"\xA9"],"\xD7"=>[98,"\xA9"],"\xD8"=>[107,"\xA9"],"\xD9"=>[140,"\xA9"],"\xDA"=>[146,"\xA9"],"\xDB"=>[102,"\xA9"],"\xDC"=>[113,"\xA9"],"\xDD"=>[121,"\xA9"],"\xDE"=>[128,"\xA9"],"\xDF"=>[12,"\xA9"],"\xE0"=>[92,"\xAA"],"\xE1"=>[95,"\xAA"],"\xE2"=>[137,"\xAA"],"\xE3"=>[142,"\xAA"],"\xE4"=>[150,"\xAA"],"\xE5"=>[74,"\xAA"],"\xE6"=>[90,"\xAA"],"\xE7"=>[98,"\xAA"],"\xE8"=>[107,"\xAA"],"\xE9"=>[140,"\xAA"],"\xEA"=>[146,"\xAA"],"\xEB"=>[102,"\xAA"],"\xEC"=>[113,"\xAA"],"\xED"=>[121,"\xAA"],"\xEE"=>[128,"\xAA"],"\xEF"=>[12,"\xAA"],"\xF0"=>[92,"\xAD"],"\xF1"=>[95,"\xAD"],"\xF2"=>[137,"\xAD"],"\xF3"=>[142,"\xAD"],"\xF4"=>[150,"\xAD"],"\xF5"=>[74,"\xAD"],"\xF6"=>[90,"\xAD"],"\xF7"=>[98,"\xAD"],"\xF8"=>[107,"\xAD"],"\xF9"=>[140,"\xAD"],"\xFA"=>[146,"\xAD"],"\xFB"=>[102,"\xAD"],"\xFC"=>[113,"\xAD"],"\xFD"=>[121,"\xAD"],"\xFE"=>[128,"\xAD"],"\xFF"=>[12,"\xAD"],],["\x00"=>[92,"\xB0"],"\x01"=>[95,"\xB0"],"\x02"=>[137,"\xB0"],"\x03"=>[142,"\xB0"],"\x04"=>[150,"\xB0"],"\x05"=>[74,"\xB0"],"\x06"=>[90,"\xB0"],"\x07"=>[98,"\xB0"],"\x08"=>[107,"\xB0"],"\x09"=>[140,"\xB0"],"\x0A"=>[146,"\xB0"],"\x0B"=>[102,"\xB0"],"\x0C"=>[113,"\xB0"],"\x0D"=>[121,"\xB0"],"\x0E"=>[128,"\xB0"],"\x0F"=>[12,"\xB0"],"\x10"=>[92,"\xB1"],"\x11"=>[95,"\xB1"],"\x12"=>[137,"\xB1"],"\x13"=>[142,"\xB1"],"\x14"=>[150,"\xB1"],"\x15"=>[74,"\xB1"],"\x16"=>[90,"\xB1"],"\x17"=>[98,"\xB1"],"\x18"=>[107,"\xB1"],"\x19"=>[140,"\xB1"],"\x1A"=>[146,"\xB1"],"\x1B"=>[102,"\xB1"],"\x1C"=>[113,"\xB1"],"\x1D"=>[121,"\xB1"],"\x1E"=>[128,"\xB1"],"\x1F"=>[12,"\xB1"],"\x20"=>[92,"\xB3"],"\x21"=>[95,"\xB3"],"\x22"=>[137,"\xB3"],"\x23"=>[142,"\xB3"],"\x24"=>[150,"\xB3"],"\x25"=>[74,"\xB3"],"\x26"=>[90,"\xB3"],"\x27"=>[98,"\xB3"],"\x28"=>[107,"\xB3"],"\x29"=>[140,"\xB3"],"\x2A"=>[146,"\xB3"],"\x2B"=>[102,"\xB3"],"\x2C"=>[113,"\xB3"],"-"=>[121,"\xB3"],"."=>[128,"\xB3"],"\x2F"=>[12,"\xB3"],[92,"\xD1"],[95,"\xD1"],[137,"\xD1"],[142,"\xD1"],[150,"\xD1"],[74,"\xD1"],[90,"\xD1"],[98,"\xD1"],[107,"\xD1"],[140,"\xD1"],"\x3A"=>[146,"\xD1"],"\x3B"=>[102,"\xD1"],"\x3C"=>[113,"\xD1"],"\x3D"=>[121,"\xD1"],"\x3E"=>[128,"\xD1"],"\x3F"=>[12,"\xD1"],"\x40"=>[92,"\xD8"],"A"=>[95,"\xD8"],"B"=>[137,"\xD8"],"C"=>[142,"\xD8"],"D"=>[150,"\xD8"],"E"=>[74,"\xD8"],"F"=>[90,"\xD8"],"G"=>[98,"\xD8"],"H"=>[107,"\xD8"],"I"=>[140,"\xD8"],"J"=>[146,"\xD8"],"K"=>[102,"\xD8"],"L"=>[113,"\xD8"],"M"=>[121,"\xD8"],"N"=>[128,"\xD8"],"O"=>[12,"\xD8"],"P"=>[92,"\xD9"],"Q"=>[95,"\xD9"],"R"=>[137,"\xD9"],"S"=>[142,"\xD9"],"T"=>[150,"\xD9"],"U"=>[74,"\xD9"],"V"=>[90,"\xD9"],"W"=>[98,"\xD9"],"X"=>[107,"\xD9"],"Y"=>[140,"\xD9"],"Z"=>[146,"\xD9"],"\x5B"=>[102,"\xD9"],"\x5C"=>[113,"\xD9"],"\x5D"=>[121,"\xD9"],"\x5E"=>[128,"\xD9"],"_"=>[12,"\xD9"],"\x60"=>[92,"\xE3"],"a"=>[95,"\xE3"],"b"=>[137,"\xE3"],"c"=>[142,"\xE3"],"d"=>[150,"\xE3"],"e"=>[74,"\xE3"],"f"=>[90,"\xE3"],"g"=>[98,"\xE3"],"h"=>[107,"\xE3"],"i"=>[140,"\xE3"],"j"=>[146,"\xE3"],"k"=>[102,"\xE3"],"l"=>[113,"\xE3"],"m"=>[121,"\xE3"],"n"=>[128,"\xE3"],"o"=>[12,"\xE3"],"p"=>[92,"\xE5"],"q"=>[95,"\xE5"],"r"=>[137,"\xE5"],"s"=>[142,"\xE5"],"t"=>[150,"\xE5"],"u"=>[74,"\xE5"],"v"=>[90,"\xE5"],"w"=>[98,"\xE5"],"x"=>[107,"\xE5"],"y"=>[140,"\xE5"],"z"=>[146,"\xE5"],"\x7B"=>[102,"\xE5"],"\x7C"=>[113,"\xE5"],"\x7D"=>[121,"\xE5"],"~"=>[128,"\xE5"],"\x7F"=>[12,"\xE5"],"\x80"=>[92,"\xE6"],"\x81"=>[95,"\xE6"],"\x82"=>[137,"\xE6"],"\x83"=>[142,"\xE6"],"\x84"=>[150,"\xE6"],"\x85"=>[74,"\xE6"],"\x86"=>[90,"\xE6"],"\x87"=>[98,"\xE6"],"\x88"=>[107,"\xE6"],"\x89"=>[140,"\xE6"],"\x8A"=>[146,"\xE6"],"\x8B"=>[102,"\xE6"],"\x8C"=>[113,"\xE6"],"\x8D"=>[121,"\xE6"],"\x8E"=>[128,"\xE6"],"\x8F"=>[12,"\xE6"],"\x90"=>[93,"\x81"],"\x91"=>[138,"\x81"],"\x92"=>[75,"\x81"],"\x93"=>[91,"\x81"],"\x94"=>[108,"\x81"],"\x95"=>[103,"\x81"],"\x96"=>[114,"\x81"],"\x97"=>[14,"\x81"],"\x98"=>[93,"\x84"],"\x99"=>[138,"\x84"],"\x9A"=>[75,"\x84"],"\x9B"=>[91,"\x84"],"\x9C"=>[108,"\x84"],"\x9D"=>[103,"\x84"],"\x9E"=>[114,"\x84"],"\x9F"=>[14,"\x84"],"\xA0"=>[93,"\x85"],"\xA1"=>[138,"\x85"],"\xA2"=>[75,"\x85"],"\xA3"=>[91,"\x85"],"\xA4"=>[108,"\x85"],"\xA5"=>[103,"\x85"],"\xA6"=>[114,"\x85"],"\xA7"=>[14,"\x85"],"\xA8"=>[93,"\x86"],"\xA9"=>[138,"\x86"],"\xAA"=>[75,"\x86"],"\xAB"=>[91,"\x86"],"\xAC"=>[108,"\x86"],"\xAD"=>[103,"\x86"],"\xAE"=>[114,"\x86"],"\xAF"=>[14,"\x86"],"\xB0"=>[93,"\x88"],"\xB1"=>[138,"\x88"],"\xB2"=>[75,"\x88"],"\xB3"=>[91,"\x88"],"\xB4"=>[108,"\x88"],"\xB5"=>[103,"\x88"],"\xB6"=>[114,"\x88"],"\xB7"=>[14,"\x88"],"\xB8"=>[93,"\x92"],"\xB9"=>[138,"\x92"],"\xBA"=>[75,"\x92"],"\xBB"=>[91,"\x92"],"\xBC"=>[108,"\x92"],"\xBD"=>[103,"\x92"],"\xBE"=>[114,"\x92"],"\xBF"=>[14,"\x92"],"\xC0"=>[93,"\x9A"],"\xC1"=>[138,"\x9A"],"\xC2"=>[75,"\x9A"],"\xC3"=>[91,"\x9A"],"\xC4"=>[108,"\x9A"],"\xC5"=>[103,"\x9A"],"\xC6"=>[114,"\x9A"],"\xC7"=>[14,"\x9A"],"\xC8"=>[93,"\x9C"],"\xC9"=>[138,"\x9C"],"\xCA"=>[75,"\x9C"],"\xCB"=>[91,"\x9C"],"\xCC"=>[108,"\x9C"],"\xCD"=>[103,"\x9C"],"\xCE"=>[114,"\x9C"],"\xCF"=>[14,"\x9C"],"\xD0"=>[93,"\xA0"],"\xD1"=>[138,"\xA0"],"\xD2"=>[75,"\xA0"],"\xD3"=>[91,"\xA0"],"\xD4"=>[108,"\xA0"],"\xD5"=>[103,"\xA0"],"\xD6"=>[114,"\xA0"],"\xD7"=>[14,"\xA0"],"\xD8"=>[93,"\xA3"],"\xD9"=>[138,"\xA3"],"\xDA"=>[75,"\xA3"],"\xDB"=>[91,"\xA3"],"\xDC"=>[108,"\xA3"],"\xDD"=>[103,"\xA3"],"\xDE"=>[114,"\xA3"],"\xDF"=>[14,"\xA3"],"\xE0"=>[93,"\xA4"],"\xE1"=>[138,"\xA4"],"\xE2"=>[75,"\xA4"],"\xE3"=>[91,"\xA4"],"\xE4"=>[108,"\xA4"],"\xE5"=>[103,"\xA4"],"\xE6"=>[114,"\xA4"],"\xE7"=>[14,"\xA4"],"\xE8"=>[93,"\xA9"],"\xE9"=>[138,"\xA9"],"\xEA"=>[75,"\xA9"],"\xEB"=>[91,"\xA9"],"\xEC"=>[108,"\xA9"],"\xED"=>[103,"\xA9"],"\xEE"=>[114,"\xA9"],"\xEF"=>[14,"\xA9"],"\xF0"=>[93,"\xAA"],"\xF1"=>[138,"\xAA"],"\xF2"=>[75,"\xAA"],"\xF3"=>[91,"\xAA"],"\xF4"=>[108,"\xAA"],"\xF5"=>[103,"\xAA"],"\xF6"=>[114,"\xAA"],"\xF7"=>[14,"\xAA"],"\xF8"=>[93,"\xAD"],"\xF9"=>[138,"\xAD"],"\xFA"=>[75,"\xAD"],"\xFB"=>[91,"\xAD"],"\xFC"=>[108,"\xAD"],"\xFD"=>[103,"\xAD"],"\xFE"=>[114,"\xAD"],"\xFF"=>[14,"\xAD"],],["\x00"=>[94,"\x830"],"\x01"=>[76,"\x830"],"\x02"=>[104,"\x830"],"\x03"=>[16,"\x830"],"\x04"=>[94,"\x831"],"\x05"=>[76,"\x831"],"\x06"=>[104,"\x831"],"\x07"=>[16,"\x831"],"\x08"=>[94,"\x832"],"\x09"=>[76,"\x832"],"\x0A"=>[104,"\x832"],"\x0B"=>[16,"\x832"],"\x0C"=>[94,"\x83a"],"\x0D"=>[76,"\x83a"],"\x0E"=>[104,"\x83a"],"\x0F"=>[16,"\x83a"],"\x10"=>[94,"\x83c"],"\x11"=>[76,"\x83c"],"\x12"=>[104,"\x83c"],"\x13"=>[16,"\x83c"],"\x14"=>[94,"\x83e"],"\x15"=>[76,"\x83e"],"\x16"=>[104,"\x83e"],"\x17"=>[16,"\x83e"],"\x18"=>[94,"\x83i"],"\x19"=>[76,"\x83i"],"\x1A"=>[104,"\x83i"],"\x1B"=>[16,"\x83i"],"\x1C"=>[94,"\x83o"],"\x1D"=>[76,"\x83o"],"\x1E"=>[104,"\x83o"],"\x1F"=>[16,"\x83o"],"\x20"=>[94,"\x83s"],"\x21"=>[76,"\x83s"],"\x22"=>[104,"\x83s"],"\x23"=>[16,"\x83s"],"\x24"=>[94,"\x83t"],"\x25"=>[76,"\x83t"],"\x26"=>[104,"\x83t"],"\x27"=>[16,"\x83t"],"\x28"=>[77,"\x83\x20"],"\x29"=>[18,"\x83\x20"],"\x2A"=>[77,"\x83\x25"],"\x2B"=>[18,"\x83\x25"],"\x2C"=>[77,"\x83-"],"-"=>[18,"\x83-"],"."=>[77,"\x83."],"\x2F"=>[18,"\x83."],[77,"\x83\x2F"],[18,"\x83\x2F"],[77,"\x833"],[18,"\x833"],[77,"\x834"],[18,"\x834"],[77,"\x835"],[18,"\x835"],[77,"\x836"],[18,"\x836"],"\x3A"=>[77,"\x837"],"\x3B"=>[18,"\x837"],"\x3C"=>[77,"\x838"],"\x3D"=>[18,"\x838"],"\x3E"=>[77,"\x839"],"\x3F"=>[18,"\x839"],"\x40"=>[77,"\x83\x3D"],"A"=>[18,"\x83\x3D"],"B"=>[77,"\x83A"],"C"=>[18,"\x83A"],"D"=>[77,"\x83_"],"E"=>[18,"\x83_"],"F"=>[77,"\x83b"],"G"=>[18,"\x83b"],"H"=>[77,"\x83d"],"I"=>[18,"\x83d"],"J"=>[77,"\x83f"],"K"=>[18,"\x83f"],"L"=>[77,"\x83g"],"M"=>[18,"\x83g"],"N"=>[77,"\x83h"],"O"=>[18,"\x83h"],"P"=>[77,"\x83l"],"Q"=>[18,"\x83l"],"R"=>[77,"\x83m"],"S"=>[18,"\x83m"],"T"=>[77,"\x83n"],"U"=>[18,"\x83n"],"V"=>[77,"\x83p"],"W"=>[18,"\x83p"],"X"=>[77,"\x83r"],"Y"=>[18,"\x83r"],"Z"=>[77,"\x83u"],"\x5B"=>[18,"\x83u"],"\x5C"=>[0,"\x83\x3A"],"\x5D"=>[0,"\x83B"],"\x5E"=>[0,"\x83C"],"_"=>[0,"\x83D"],"\x60"=>[0,"\x83E"],"a"=>[0,"\x83F"],"b"=>[0,"\x83G"],"c"=>[0,"\x83H"],"d"=>[0,"\x83I"],"e"=>[0,"\x83J"],"f"=>[0,"\x83K"],"g"=>[0,"\x83L"],"h"=>[0,"\x83M"],"i"=>[0,"\x83N"],"j"=>[0,"\x83O"],"k"=>[0,"\x83P"],"l"=>[0,"\x83Q"],"m"=>[0,"\x83R"],"n"=>[0,"\x83S"],"o"=>[0,"\x83T"],"p"=>[0,"\x83U"],"q"=>[0,"\x83V"],"r"=>[0,"\x83W"],"s"=>[0,"\x83Y"],"t"=>[0,"\x83j"],"u"=>[0,"\x83k"],"v"=>[0,"\x83q"],"w"=>[0,"\x83v"],"x"=>[0,"\x83w"],"y"=>[0,"\x83x"],"z"=>[0,"\x83y"],"\x7B"=>[0,"\x83z"],"\x7C"=>[82,"\x83"],"\x7D"=>[87,"\x83"],"~"=>[130,"\x83"],"\x7F"=>[9,"\x83"],"\x80"=>[94,"\xA20"],"\x81"=>[76,"\xA20"],"\x82"=>[104,"\xA20"],"\x83"=>[16,"\xA20"],"\x84"=>[94,"\xA21"],"\x85"=>[76,"\xA21"],"\x86"=>[104,"\xA21"],"\x87"=>[16,"\xA21"],"\x88"=>[94,"\xA22"],"\x89"=>[76,"\xA22"],"\x8A"=>[104,"\xA22"],"\x8B"=>[16,"\xA22"],"\x8C"=>[94,"\xA2a"],"\x8D"=>[76,"\xA2a"],"\x8E"=>[104,"\xA2a"],"\x8F"=>[16,"\xA2a"],"\x90"=>[94,"\xA2c"],"\x91"=>[76,"\xA2c"],"\x92"=>[104,"\xA2c"],"\x93"=>[16,"\xA2c"],"\x94"=>[94,"\xA2e"],"\x95"=>[76,"\xA2e"],"\x96"=>[104,"\xA2e"],"\x97"=>[16,"\xA2e"],"\x98"=>[94,"\xA2i"],"\x99"=>[76,"\xA2i"],"\x9A"=>[104,"\xA2i"],"\x9B"=>[16,"\xA2i"],"\x9C"=>[94,"\xA2o"],"\x9D"=>[76,"\xA2o"],"\x9E"=>[104,"\xA2o"],"\x9F"=>[16,"\xA2o"],"\xA0"=>[94,"\xA2s"],"\xA1"=>[76,"\xA2s"],"\xA2"=>[104,"\xA2s"],"\xA3"=>[16,"\xA2s"],"\xA4"=>[94,"\xA2t"],"\xA5"=>[76,"\xA2t"],"\xA6"=>[104,"\xA2t"],"\xA7"=>[16,"\xA2t"],"\xA8"=>[77,"\xA2\x20"],"\xA9"=>[18,"\xA2\x20"],"\xAA"=>[77,"\xA2\x25"],"\xAB"=>[18,"\xA2\x25"],"\xAC"=>[77,"\xA2-"],"\xAD"=>[18,"\xA2-"],"\xAE"=>[77,"\xA2."],"\xAF"=>[18,"\xA2."],"\xB0"=>[77,"\xA2\x2F"],"\xB1"=>[18,"\xA2\x2F"],"\xB2"=>[77,"\xA23"],"\xB3"=>[18,"\xA23"],"\xB4"=>[77,"\xA24"],"\xB5"=>[18,"\xA24"],"\xB6"=>[77,"\xA25"],"\xB7"=>[18,"\xA25"],"\xB8"=>[77,"\xA26"],"\xB9"=>[18,"\xA26"],"\xBA"=>[77,"\xA27"],"\xBB"=>[18,"\xA27"],"\xBC"=>[77,"\xA28"],"\xBD"=>[18,"\xA28"],"\xBE"=>[77,"\xA29"],"\xBF"=>[18,"\xA29"],"\xC0"=>[77,"\xA2\x3D"],"\xC1"=>[18,"\xA2\x3D"],"\xC2"=>[77,"\xA2A"],"\xC3"=>[18,"\xA2A"],"\xC4"=>[77,"\xA2_"],"\xC5"=>[18,"\xA2_"],"\xC6"=>[77,"\xA2b"],"\xC7"=>[18,"\xA2b"],"\xC8"=>[77,"\xA2d"],"\xC9"=>[18,"\xA2d"],"\xCA"=>[77,"\xA2f"],"\xCB"=>[18,"\xA2f"],"\xCC"=>[77,"\xA2g"],"\xCD"=>[18,"\xA2g"],"\xCE"=>[77,"\xA2h"],"\xCF"=>[18,"\xA2h"],"\xD0"=>[77,"\xA2l"],"\xD1"=>[18,"\xA2l"],"\xD2"=>[77,"\xA2m"],"\xD3"=>[18,"\xA2m"],"\xD4"=>[77,"\xA2n"],"\xD5"=>[18,"\xA2n"],"\xD6"=>[77,"\xA2p"],"\xD7"=>[18,"\xA2p"],"\xD8"=>[77,"\xA2r"],"\xD9"=>[18,"\xA2r"],"\xDA"=>[77,"\xA2u"],"\xDB"=>[18,"\xA2u"],"\xDC"=>[0,"\xA2\x3A"],"\xDD"=>[0,"\xA2B"],"\xDE"=>[0,"\xA2C"],"\xDF"=>[0,"\xA2D"],"\xE0"=>[0,"\xA2E"],"\xE1"=>[0,"\xA2F"],"\xE2"=>[0,"\xA2G"],"\xE3"=>[0,"\xA2H"],"\xE4"=>[0,"\xA2I"],"\xE5"=>[0,"\xA2J"],"\xE6"=>[0,"\xA2K"],"\xE7"=>[0,"\xA2L"],"\xE8"=>[0,"\xA2M"],"\xE9"=>[0,"\xA2N"],"\xEA"=>[0,"\xA2O"],"\xEB"=>[0,"\xA2P"],"\xEC"=>[0,"\xA2Q"],"\xED"=>[0,"\xA2R"],"\xEE"=>[0,"\xA2S"],"\xEF"=>[0,"\xA2T"],"\xF0"=>[0,"\xA2U"],"\xF1"=>[0,"\xA2V"],"\xF2"=>[0,"\xA2W"],"\xF3"=>[0,"\xA2Y"],"\xF4"=>[0,"\xA2j"],"\xF5"=>[0,"\xA2k"],"\xF6"=>[0,"\xA2q"],"\xF7"=>[0,"\xA2v"],"\xF8"=>[0,"\xA2w"],"\xF9"=>[0,"\xA2x"],"\xFA"=>[0,"\xA2y"],"\xFB"=>[0,"\xA2z"],"\xFC"=>[82,"\xA2"],"\xFD"=>[87,"\xA2"],"\xFE"=>[130,"\xA2"],"\xFF"=>[9,"\xA2"],],["\x00"=>[77,"\x830"],"\x01"=>[18,"\x830"],"\x02"=>[77,"\x831"],"\x03"=>[18,"\x831"],"\x04"=>[77,"\x832"],"\x05"=>[18,"\x832"],"\x06"=>[77,"\x83a"],"\x07"=>[18,"\x83a"],"\x08"=>[77,"\x83c"],"\x09"=>[18,"\x83c"],"\x0A"=>[77,"\x83e"],"\x0B"=>[18,"\x83e"],"\x0C"=>[77,"\x83i"],"\x0D"=>[18,"\x83i"],"\x0E"=>[77,"\x83o"],"\x0F"=>[18,"\x83o"],"\x10"=>[77,"\x83s"],"\x11"=>[18,"\x83s"],"\x12"=>[77,"\x83t"],"\x13"=>[18,"\x83t"],"\x14"=>[0,"\x83\x20"],"\x15"=>[0,"\x83\x25"],"\x16"=>[0,"\x83-"],"\x17"=>[0,"\x83."],"\x18"=>[0,"\x83\x2F"],"\x19"=>[0,"\x833"],"\x1A"=>[0,"\x834"],"\x1B"=>[0,"\x835"],"\x1C"=>[0,"\x836"],"\x1D"=>[0,"\x837"],"\x1E"=>[0,"\x838"],"\x1F"=>[0,"\x839"],"\x20"=>[0,"\x83\x3D"],"\x21"=>[0,"\x83A"],"\x22"=>[0,"\x83_"],"\x23"=>[0,"\x83b"],"\x24"=>[0,"\x83d"],"\x25"=>[0,"\x83f"],"\x26"=>[0,"\x83g"],"\x27"=>[0,"\x83h"],"\x28"=>[0,"\x83l"],"\x29"=>[0,"\x83m"],"\x2A"=>[0,"\x83n"],"\x2B"=>[0,"\x83p"],"\x2C"=>[0,"\x83r"],"-"=>[0,"\x83u"],"."=>[100,"\x83"],"\x2F"=>[110,"\x83"],[111,"\x83"],[115,"\x83"],[116,"\x83"],[118,"\x83"],[119,"\x83"],[122,"\x83"],[123,"\x83"],[125,"\x83"],[126,"\x83"],[129,"\x83"],"\x3A"=>[143,"\x83"],"\x3B"=>[148,"\x83"],"\x3C"=>[151,"\x83"],"\x3D"=>[153,"\x83"],"\x3E"=>[83,"\x83"],"\x3F"=>[10,"\x83"],"\x40"=>[77,"\xA20"],"A"=>[18,"\xA20"],"B"=>[77,"\xA21"],"C"=>[18,"\xA21"],"D"=>[77,"\xA22"],"E"=>[18,"\xA22"],"F"=>[77,"\xA2a"],"G"=>[18,"\xA2a"],"H"=>[77,"\xA2c"],"I"=>[18,"\xA2c"],"J"=>[77,"\xA2e"],"K"=>[18,"\xA2e"],"L"=>[77,"\xA2i"],"M"=>[18,"\xA2i"],"N"=>[77,"\xA2o"],"O"=>[18,"\xA2o"],"P"=>[77,"\xA2s"],"Q"=>[18,"\xA2s"],"R"=>[77,"\xA2t"],"S"=>[18,"\xA2t"],"T"=>[0,"\xA2\x20"],"U"=>[0,"\xA2\x25"],"V"=>[0,"\xA2-"],"W"=>[0,"\xA2."],"X"=>[0,"\xA2\x2F"],"Y"=>[0,"\xA23"],"Z"=>[0,"\xA24"],"\x5B"=>[0,"\xA25"],"\x5C"=>[0,"\xA26"],"\x5D"=>[0,"\xA27"],"\x5E"=>[0,"\xA28"],"_"=>[0,"\xA29"],"\x60"=>[0,"\xA2\x3D"],"a"=>[0,"\xA2A"],"b"=>[0,"\xA2_"],"c"=>[0,"\xA2b"],"d"=>[0,"\xA2d"],"e"=>[0,"\xA2f"],"f"=>[0,"\xA2g"],"g"=>[0,"\xA2h"],"h"=>[0,"\xA2l"],"i"=>[0,"\xA2m"],"j"=>[0,"\xA2n"],"k"=>[0,"\xA2p"],"l"=>[0,"\xA2r"],"m"=>[0,"\xA2u"],"n"=>[100,"\xA2"],"o"=>[110,"\xA2"],"p"=>[111,"\xA2"],"q"=>[115,"\xA2"],"r"=>[116,"\xA2"],"s"=>[118,"\xA2"],"t"=>[119,"\xA2"],"u"=>[122,"\xA2"],"v"=>[123,"\xA2"],"w"=>[125,"\xA2"],"x"=>[126,"\xA2"],"y"=>[129,"\xA2"],"z"=>[143,"\xA2"],"\x7B"=>[148,"\xA2"],"\x7C"=>[151,"\xA2"],"\x7D"=>[153,"\xA2"],"~"=>[83,"\xA2"],"\x7F"=>[10,"\xA2"],"\x80"=>[77,"\xB80"],"\x81"=>[18,"\xB80"],"\x82"=>[77,"\xB81"],"\x83"=>[18,"\xB81"],"\x84"=>[77,"\xB82"],"\x85"=>[18,"\xB82"],"\x86"=>[77,"\xB8a"],"\x87"=>[18,"\xB8a"],"\x88"=>[77,"\xB8c"],"\x89"=>[18,"\xB8c"],"\x8A"=>[77,"\xB8e"],"\x8B"=>[18,"\xB8e"],"\x8C"=>[77,"\xB8i"],"\x8D"=>[18,"\xB8i"],"\x8E"=>[77,"\xB8o"],"\x8F"=>[18,"\xB8o"],"\x90"=>[77,"\xB8s"],"\x91"=>[18,"\xB8s"],"\x92"=>[77,"\xB8t"],"\x93"=>[18,"\xB8t"],"\x94"=>[0,"\xB8\x20"],"\x95"=>[0,"\xB8\x25"],"\x96"=>[0,"\xB8-"],"\x97"=>[0,"\xB8."],"\x98"=>[0,"\xB8\x2F"],"\x99"=>[0,"\xB83"],"\x9A"=>[0,"\xB84"],"\x9B"=>[0,"\xB85"],"\x9C"=>[0,"\xB86"],"\x9D"=>[0,"\xB87"],"\x9E"=>[0,"\xB88"],"\x9F"=>[0,"\xB89"],"\xA0"=>[0,"\xB8\x3D"],"\xA1"=>[0,"\xB8A"],"\xA2"=>[0,"\xB8_"],"\xA3"=>[0,"\xB8b"],"\xA4"=>[0,"\xB8d"],"\xA5"=>[0,"\xB8f"],"\xA6"=>[0,"\xB8g"],"\xA7"=>[0,"\xB8h"],"\xA8"=>[0,"\xB8l"],"\xA9"=>[0,"\xB8m"],"\xAA"=>[0,"\xB8n"],"\xAB"=>[0,"\xB8p"],"\xAC"=>[0,"\xB8r"],"\xAD"=>[0,"\xB8u"],"\xAE"=>[100,"\xB8"],"\xAF"=>[110,"\xB8"],"\xB0"=>[111,"\xB8"],"\xB1"=>[115,"\xB8"],"\xB2"=>[116,"\xB8"],"\xB3"=>[118,"\xB8"],"\xB4"=>[119,"\xB8"],"\xB5"=>[122,"\xB8"],"\xB6"=>[123,"\xB8"],"\xB7"=>[125,"\xB8"],"\xB8"=>[126,"\xB8"],"\xB9"=>[129,"\xB8"],"\xBA"=>[143,"\xB8"],"\xBB"=>[148,"\xB8"],"\xBC"=>[151,"\xB8"],"\xBD"=>[153,"\xB8"],"\xBE"=>[83,"\xB8"],"\xBF"=>[10,"\xB8"],"\xC0"=>[77,"\xC20"],"\xC1"=>[18,"\xC20"],"\xC2"=>[77,"\xC21"],"\xC3"=>[18,"\xC21"],"\xC4"=>[77,"\xC22"],"\xC5"=>[18,"\xC22"],"\xC6"=>[77,"\xC2a"],"\xC7"=>[18,"\xC2a"],"\xC8"=>[77,"\xC2c"],"\xC9"=>[18,"\xC2c"],"\xCA"=>[77,"\xC2e"],"\xCB"=>[18,"\xC2e"],"\xCC"=>[77,"\xC2i"],"\xCD"=>[18,"\xC2i"],"\xCE"=>[77,"\xC2o"],"\xCF"=>[18,"\xC2o"],"\xD0"=>[77,"\xC2s"],"\xD1"=>[18,"\xC2s"],"\xD2"=>[77,"\xC2t"],"\xD3"=>[18,"\xC2t"],"\xD4"=>[0,"\xC2\x20"],"\xD5"=>[0,"\xC2\x25"],"\xD6"=>[0,"\xC2-"],"\xD7"=>[0,"\xC2."],"\xD8"=>[0,"\xC2\x2F"],"\xD9"=>[0,"\xC23"],"\xDA"=>[0,"\xC24"],"\xDB"=>[0,"\xC25"],"\xDC"=>[0,"\xC26"],"\xDD"=>[0,"\xC27"],"\xDE"=>[0,"\xC28"],"\xDF"=>[0,"\xC29"],"\xE0"=>[0,"\xC2\x3D"],"\xE1"=>[0,"\xC2A"],"\xE2"=>[0,"\xC2_"],"\xE3"=>[0,"\xC2b"],"\xE4"=>[0,"\xC2d"],"\xE5"=>[0,"\xC2f"],"\xE6"=>[0,"\xC2g"],"\xE7"=>[0,"\xC2h"],"\xE8"=>[0,"\xC2l"],"\xE9"=>[0,"\xC2m"],"\xEA"=>[0,"\xC2n"],"\xEB"=>[0,"\xC2p"],"\xEC"=>[0,"\xC2r"],"\xED"=>[0,"\xC2u"],"\xEE"=>[100,"\xC2"],"\xEF"=>[110,"\xC2"],"\xF0"=>[111,"\xC2"],"\xF1"=>[115,"\xC2"],"\xF2"=>[116,"\xC2"],"\xF3"=>[118,"\xC2"],"\xF4"=>[119,"\xC2"],"\xF5"=>[122,"\xC2"],"\xF6"=>[123,"\xC2"],"\xF7"=>[125,"\xC2"],"\xF8"=>[126,"\xC2"],"\xF9"=>[129,"\xC2"],"\xFA"=>[143,"\xC2"],"\xFB"=>[148,"\xC2"],"\xFC"=>[151,"\xC2"],"\xFD"=>[153,"\xC2"],"\xFE"=>[83,"\xC2"],"\xFF"=>[10,"\xC2"],],["\x00"=>[0,"\x830"],"\x01"=>[0,"\x831"],"\x02"=>[0,"\x832"],"\x03"=>[0,"\x83a"],"\x04"=>[0,"\x83c"],"\x05"=>[0,"\x83e"],"\x06"=>[0,"\x83i"],"\x07"=>[0,"\x83o"],"\x08"=>[0,"\x83s"],"\x09"=>[0,"\x83t"],"\x0A"=>[73,"\x83"],"\x0B"=>[88,"\x83"],"\x0C"=>[89,"\x83"],"\x0D"=>[96,"\x83"],"\x0E"=>[97,"\x83"],"\x0F"=>[99,"\x83"],"\x10"=>[106,"\x83"],"\x11"=>[136,"\x83"],"\x12"=>[139,"\x83"],"\x13"=>[141,"\x83"],"\x14"=>[145,"\x83"],"\x15"=>[147,"\x83"],"\x16"=>[149,"\x83"],"\x17"=>[101,"\x83"],"\x18"=>[112,"\x83"],"\x19"=>[117,"\x83"],"\x1A"=>[120,"\x83"],"\x1B"=>[124,"\x83"],"\x1C"=>[127,"\x83"],"\x1D"=>[144,"\x83"],"\x1E"=>[152,"\x83"],"\x1F"=>[11,"\x83"],"\x20"=>[0,"\xA20"],"\x21"=>[0,"\xA21"],"\x22"=>[0,"\xA22"],"\x23"=>[0,"\xA2a"],"\x24"=>[0,"\xA2c"],"\x25"=>[0,"\xA2e"],"\x26"=>[0,"\xA2i"],"\x27"=>[0,"\xA2o"],"\x28"=>[0,"\xA2s"],"\x29"=>[0,"\xA2t"],"\x2A"=>[73,"\xA2"],"\x2B"=>[88,"\xA2"],"\x2C"=>[89,"\xA2"],"-"=>[96,"\xA2"],"."=>[97,"\xA2"],"\x2F"=>[99,"\xA2"],[106,"\xA2"],[136,"\xA2"],[139,"\xA2"],[141,"\xA2"],[145,"\xA2"],[147,"\xA2"],[149,"\xA2"],[101,"\xA2"],[112,"\xA2"],[117,"\xA2"],"\x3A"=>[120,"\xA2"],"\x3B"=>[124,"\xA2"],"\x3C"=>[127,"\xA2"],"\x3D"=>[144,"\xA2"],"\x3E"=>[152,"\xA2"],"\x3F"=>[11,"\xA2"],"\x40"=>[0,"\xB80"],"A"=>[0,"\xB81"],"B"=>[0,"\xB82"],"C"=>[0,"\xB8a"],"D"=>[0,"\xB8c"],"E"=>[0,"\xB8e"],"F"=>[0,"\xB8i"],"G"=>[0,"\xB8o"],"H"=>[0,"\xB8s"],"I"=>[0,"\xB8t"],"J"=>[73,"\xB8"],"K"=>[88,"\xB8"],"L"=>[89,"\xB8"],"M"=>[96,"\xB8"],"N"=>[97,"\xB8"],"O"=>[99,"\xB8"],"P"=>[106,"\xB8"],"Q"=>[136,"\xB8"],"R"=>[139,"\xB8"],"S"=>[141,"\xB8"],"T"=>[145,"\xB8"],"U"=>[147,"\xB8"],"V"=>[149,"\xB8"],"W"=>[101,"\xB8"],"X"=>[112,"\xB8"],"Y"=>[117,"\xB8"],"Z"=>[120,"\xB8"],"\x5B"=>[124,"\xB8"],"\x5C"=>[127,"\xB8"],"\x5D"=>[144,"\xB8"],"\x5E"=>[152,"\xB8"],"_"=>[11,"\xB8"],"\x60"=>[0,"\xC20"],"a"=>[0,"\xC21"],"b"=>[0,"\xC22"],"c"=>[0,"\xC2a"],"d"=>[0,"\xC2c"],"e"=>[0,"\xC2e"],"f"=>[0,"\xC2i"],"g"=>[0,"\xC2o"],"h"=>[0,"\xC2s"],"i"=>[0,"\xC2t"],"j"=>[73,"\xC2"],"k"=>[88,"\xC2"],"l"=>[89,"\xC2"],"m"=>[96,"\xC2"],"n"=>[97,"\xC2"],"o"=>[99,"\xC2"],"p"=>[106,"\xC2"],"q"=>[136,"\xC2"],"r"=>[139,"\xC2"],"s"=>[141,"\xC2"],"t"=>[145,"\xC2"],"u"=>[147,"\xC2"],"v"=>[149,"\xC2"],"w"=>[101,"\xC2"],"x"=>[112,"\xC2"],"y"=>[117,"\xC2"],"z"=>[120,"\xC2"],"\x7B"=>[124,"\xC2"],"\x7C"=>[127,"\xC2"],"\x7D"=>[144,"\xC2"],"~"=>[152,"\xC2"],"\x7F"=>[11,"\xC2"],"\x80"=>[0,"\xE00"],"\x81"=>[0,"\xE01"],"\x82"=>[0,"\xE02"],"\x83"=>[0,"\xE0a"],"\x84"=>[0,"\xE0c"],"\x85"=>[0,"\xE0e"],"\x86"=>[0,"\xE0i"],"\x87"=>[0,"\xE0o"],"\x88"=>[0,"\xE0s"],"\x89"=>[0,"\xE0t"],"\x8A"=>[73,"\xE0"],"\x8B"=>[88,"\xE0"],"\x8C"=>[89,"\xE0"],"\x8D"=>[96,"\xE0"],"\x8E"=>[97,"\xE0"],"\x8F"=>[99,"\xE0"],"\x90"=>[106,"\xE0"],"\x91"=>[136,"\xE0"],"\x92"=>[139,"\xE0"],"\x93"=>[141,"\xE0"],"\x94"=>[145,"\xE0"],"\x95"=>[147,"\xE0"],"\x96"=>[149,"\xE0"],"\x97"=>[101,"\xE0"],"\x98"=>[112,"\xE0"],"\x99"=>[117,"\xE0"],"\x9A"=>[120,"\xE0"],"\x9B"=>[124,"\xE0"],"\x9C"=>[127,"\xE0"],"\x9D"=>[144,"\xE0"],"\x9E"=>[152,"\xE0"],"\x9F"=>[11,"\xE0"],"\xA0"=>[0,"\xE20"],"\xA1"=>[0,"\xE21"],"\xA2"=>[0,"\xE22"],"\xA3"=>[0,"\xE2a"],"\xA4"=>[0,"\xE2c"],"\xA5"=>[0,"\xE2e"],"\xA6"=>[0,"\xE2i"],"\xA7"=>[0,"\xE2o"],"\xA8"=>[0,"\xE2s"],"\xA9"=>[0,"\xE2t"],"\xAA"=>[73,"\xE2"],"\xAB"=>[88,"\xE2"],"\xAC"=>[89,"\xE2"],"\xAD"=>[96,"\xE2"],"\xAE"=>[97,"\xE2"],"\xAF"=>[99,"\xE2"],"\xB0"=>[106,"\xE2"],"\xB1"=>[136,"\xE2"],"\xB2"=>[139,"\xE2"],"\xB3"=>[141,"\xE2"],"\xB4"=>[145,"\xE2"],"\xB5"=>[147,"\xE2"],"\xB6"=>[149,"\xE2"],"\xB7"=>[101,"\xE2"],"\xB8"=>[112,"\xE2"],"\xB9"=>[117,"\xE2"],"\xBA"=>[120,"\xE2"],"\xBB"=>[124,"\xE2"],"\xBC"=>[127,"\xE2"],"\xBD"=>[144,"\xE2"],"\xBE"=>[152,"\xE2"],"\xBF"=>[11,"\xE2"],"\xC0"=>[92,"\x99"],"\xC1"=>[95,"\x99"],"\xC2"=>[137,"\x99"],"\xC3"=>[142,"\x99"],"\xC4"=>[150,"\x99"],"\xC5"=>[74,"\x99"],"\xC6"=>[90,"\x99"],"\xC7"=>[98,"\x99"],"\xC8"=>[107,"\x99"],"\xC9"=>[140,"\x99"],"\xCA"=>[146,"\x99"],"\xCB"=>[102,"\x99"],"\xCC"=>[113,"\x99"],"\xCD"=>[121,"\x99"],"\xCE"=>[128,"\x99"],"\xCF"=>[12,"\x99"],"\xD0"=>[92,"\xA1"],"\xD1"=>[95,"\xA1"],"\xD2"=>[137,"\xA1"],"\xD3"=>[142,"\xA1"],"\xD4"=>[150,"\xA1"],"\xD5"=>[74,"\xA1"],"\xD6"=>[90,"\xA1"],"\xD7"=>[98,"\xA1"],"\xD8"=>[107,"\xA1"],"\xD9"=>[140,"\xA1"],"\xDA"=>[146,"\xA1"],"\xDB"=>[102,"\xA1"],"\xDC"=>[113,"\xA1"],"\xDD"=>[121,"\xA1"],"\xDE"=>[128,"\xA1"],"\xDF"=>[12,"\xA1"],"\xE0"=>[92,"\xA7"],"\xE1"=>[95,"\xA7"],"\xE2"=>[137,"\xA7"],"\xE3"=>[142,"\xA7"],"\xE4"=>[150,"\xA7"],"\xE5"=>[74,"\xA7"],"\xE6"=>[90,"\xA7"],"\xE7"=>[98,"\xA7"],"\xE8"=>[107,"\xA7"],"\xE9"=>[140,"\xA7"],"\xEA"=>[146,"\xA7"],"\xEB"=>[102,"\xA7"],"\xEC"=>[113,"\xA7"],"\xED"=>[121,"\xA7"],"\xEE"=>[128,"\xA7"],"\xEF"=>[12,"\xA7"],"\xF0"=>[92,"\xAC"],"\xF1"=>[95,"\xAC"],"\xF2"=>[137,"\xAC"],"\xF3"=>[142,"\xAC"],"\xF4"=>[150,"\xAC"],"\xF5"=>[74,"\xAC"],"\xF6"=>[90,"\xAC"],"\xF7"=>[98,"\xAC"],"\xF8"=>[107,"\xAC"],"\xF9"=>[140,"\xAC"],"\xFA"=>[146,"\xAC"],"\xFB"=>[102,"\xAC"],"\xFC"=>[113,"\xAC"],"\xFD"=>[121,"\xAC"],"\xFE"=>[128,"\xAC"],"\xFF"=>[12,"\xAC"],],["\x00"=>[94,"\x850"],"\x01"=>[76,"\x850"],"\x02"=>[104,"\x850"],"\x03"=>[16,"\x850"],"\x04"=>[94,"\x851"],"\x05"=>[76,"\x851"],"\x06"=>[104,"\x851"],"\x07"=>[16,"\x851"],"\x08"=>[94,"\x852"],"\x09"=>[76,"\x852"],"\x0A"=>[104,"\x852"],"\x0B"=>[16,"\x852"],"\x0C"=>[94,"\x85a"],"\x0D"=>[76,"\x85a"],"\x0E"=>[104,"\x85a"],"\x0F"=>[16,"\x85a"],"\x10"=>[94,"\x85c"],"\x11"=>[76,"\x85c"],"\x12"=>[104,"\x85c"],"\x13"=>[16,"\x85c"],"\x14"=>[94,"\x85e"],"\x15"=>[76,"\x85e"],"\x16"=>[104,"\x85e"],"\x17"=>[16,"\x85e"],"\x18"=>[94,"\x85i"],"\x19"=>[76,"\x85i"],"\x1A"=>[104,"\x85i"],"\x1B"=>[16,"\x85i"],"\x1C"=>[94,"\x85o"],"\x1D"=>[76,"\x85o"],"\x1E"=>[104,"\x85o"],"\x1F"=>[16,"\x85o"],"\x20"=>[94,"\x85s"],"\x21"=>[76,"\x85s"],"\x22"=>[104,"\x85s"],"\x23"=>[16,"\x85s"],"\x24"=>[94,"\x85t"],"\x25"=>[76,"\x85t"],"\x26"=>[104,"\x85t"],"\x27"=>[16,"\x85t"],"\x28"=>[77,"\x85\x20"],"\x29"=>[18,"\x85\x20"],"\x2A"=>[77,"\x85\x25"],"\x2B"=>[18,"\x85\x25"],"\x2C"=>[77,"\x85-"],"-"=>[18,"\x85-"],"."=>[77,"\x85."],"\x2F"=>[18,"\x85."],[77,"\x85\x2F"],[18,"\x85\x2F"],[77,"\x853"],[18,"\x853"],[77,"\x854"],[18,"\x854"],[77,"\x855"],[18,"\x855"],[77,"\x856"],[18,"\x856"],"\x3A"=>[77,"\x857"],"\x3B"=>[18,"\x857"],"\x3C"=>[77,"\x858"],"\x3D"=>[18,"\x858"],"\x3E"=>[77,"\x859"],"\x3F"=>[18,"\x859"],"\x40"=>[77,"\x85\x3D"],"A"=>[18,"\x85\x3D"],"B"=>[77,"\x85A"],"C"=>[18,"\x85A"],"D"=>[77,"\x85_"],"E"=>[18,"\x85_"],"F"=>[77,"\x85b"],"G"=>[18,"\x85b"],"H"=>[77,"\x85d"],"I"=>[18,"\x85d"],"J"=>[77,"\x85f"],"K"=>[18,"\x85f"],"L"=>[77,"\x85g"],"M"=>[18,"\x85g"],"N"=>[77,"\x85h"],"O"=>[18,"\x85h"],"P"=>[77,"\x85l"],"Q"=>[18,"\x85l"],"R"=>[77,"\x85m"],"S"=>[18,"\x85m"],"T"=>[77,"\x85n"],"U"=>[18,"\x85n"],"V"=>[77,"\x85p"],"W"=>[18,"\x85p"],"X"=>[77,"\x85r"],"Y"=>[18,"\x85r"],"Z"=>[77,"\x85u"],"\x5B"=>[18,"\x85u"],"\x5C"=>[0,"\x85\x3A"],"\x5D"=>[0,"\x85B"],"\x5E"=>[0,"\x85C"],"_"=>[0,"\x85D"],"\x60"=>[0,"\x85E"],"a"=>[0,"\x85F"],"b"=>[0,"\x85G"],"c"=>[0,"\x85H"],"d"=>[0,"\x85I"],"e"=>[0,"\x85J"],"f"=>[0,"\x85K"],"g"=>[0,"\x85L"],"h"=>[0,"\x85M"],"i"=>[0,"\x85N"],"j"=>[0,"\x85O"],"k"=>[0,"\x85P"],"l"=>[0,"\x85Q"],"m"=>[0,"\x85R"],"n"=>[0,"\x85S"],"o"=>[0,"\x85T"],"p"=>[0,"\x85U"],"q"=>[0,"\x85V"],"r"=>[0,"\x85W"],"s"=>[0,"\x85Y"],"t"=>[0,"\x85j"],"u"=>[0,"\x85k"],"v"=>[0,"\x85q"],"w"=>[0,"\x85v"],"x"=>[0,"\x85w"],"y"=>[0,"\x85x"],"z"=>[0,"\x85y"],"\x7B"=>[0,"\x85z"],"\x7C"=>[82,"\x85"],"\x7D"=>[87,"\x85"],"~"=>[130,"\x85"],"\x7F"=>[9,"\x85"],"\x80"=>[94,"\x860"],"\x81"=>[76,"\x860"],"\x82"=>[104,"\x860"],"\x83"=>[16,"\x860"],"\x84"=>[94,"\x861"],"\x85"=>[76,"\x861"],"\x86"=>[104,"\x861"],"\x87"=>[16,"\x861"],"\x88"=>[94,"\x862"],"\x89"=>[76,"\x862"],"\x8A"=>[104,"\x862"],"\x8B"=>[16,"\x862"],"\x8C"=>[94,"\x86a"],"\x8D"=>[76,"\x86a"],"\x8E"=>[104,"\x86a"],"\x8F"=>[16,"\x86a"],"\x90"=>[94,"\x86c"],"\x91"=>[76,"\x86c"],"\x92"=>[104,"\x86c"],"\x93"=>[16,"\x86c"],"\x94"=>[94,"\x86e"],"\x95"=>[76,"\x86e"],"\x96"=>[104,"\x86e"],"\x97"=>[16,"\x86e"],"\x98"=>[94,"\x86i"],"\x99"=>[76,"\x86i"],"\x9A"=>[104,"\x86i"],"\x9B"=>[16,"\x86i"],"\x9C"=>[94,"\x86o"],"\x9D"=>[76,"\x86o"],"\x9E"=>[104,"\x86o"],"\x9F"=>[16,"\x86o"],"\xA0"=>[94,"\x86s"],"\xA1"=>[76,"\x86s"],"\xA2"=>[104,"\x86s"],"\xA3"=>[16,"\x86s"],"\xA4"=>[94,"\x86t"],"\xA5"=>[76,"\x86t"],"\xA6"=>[104,"\x86t"],"\xA7"=>[16,"\x86t"],"\xA8"=>[77,"\x86\x20"],"\xA9"=>[18,"\x86\x20"],"\xAA"=>[77,"\x86\x25"],"\xAB"=>[18,"\x86\x25"],"\xAC"=>[77,"\x86-"],"\xAD"=>[18,"\x86-"],"\xAE"=>[77,"\x86."],"\xAF"=>[18,"\x86."],"\xB0"=>[77,"\x86\x2F"],"\xB1"=>[18,"\x86\x2F"],"\xB2"=>[77,"\x863"],"\xB3"=>[18,"\x863"],"\xB4"=>[77,"\x864"],"\xB5"=>[18,"\x864"],"\xB6"=>[77,"\x865"],"\xB7"=>[18,"\x865"],"\xB8"=>[77,"\x866"],"\xB9"=>[18,"\x866"],"\xBA"=>[77,"\x867"],"\xBB"=>[18,"\x867"],"\xBC"=>[77,"\x868"],"\xBD"=>[18,"\x868"],"\xBE"=>[77,"\x869"],"\xBF"=>[18,"\x869"],"\xC0"=>[77,"\x86\x3D"],"\xC1"=>[18,"\x86\x3D"],"\xC2"=>[77,"\x86A"],"\xC3"=>[18,"\x86A"],"\xC4"=>[77,"\x86_"],"\xC5"=>[18,"\x86_"],"\xC6"=>[77,"\x86b"],"\xC7"=>[18,"\x86b"],"\xC8"=>[77,"\x86d"],"\xC9"=>[18,"\x86d"],"\xCA"=>[77,"\x86f"],"\xCB"=>[18,"\x86f"],"\xCC"=>[77,"\x86g"],"\xCD"=>[18,"\x86g"],"\xCE"=>[77,"\x86h"],"\xCF"=>[18,"\x86h"],"\xD0"=>[77,"\x86l"],"\xD1"=>[18,"\x86l"],"\xD2"=>[77,"\x86m"],"\xD3"=>[18,"\x86m"],"\xD4"=>[77,"\x86n"],"\xD5"=>[18,"\x86n"],"\xD6"=>[77,"\x86p"],"\xD7"=>[18,"\x86p"],"\xD8"=>[77,"\x86r"],"\xD9"=>[18,"\x86r"],"\xDA"=>[77,"\x86u"],"\xDB"=>[18,"\x86u"],"\xDC"=>[0,"\x86\x3A"],"\xDD"=>[0,"\x86B"],"\xDE"=>[0,"\x86C"],"\xDF"=>[0,"\x86D"],"\xE0"=>[0,"\x86E"],"\xE1"=>[0,"\x86F"],"\xE2"=>[0,"\x86G"],"\xE3"=>[0,"\x86H"],"\xE4"=>[0,"\x86I"],"\xE5"=>[0,"\x86J"],"\xE6"=>[0,"\x86K"],"\xE7"=>[0,"\x86L"],"\xE8"=>[0,"\x86M"],"\xE9"=>[0,"\x86N"],"\xEA"=>[0,"\x86O"],"\xEB"=>[0,"\x86P"],"\xEC"=>[0,"\x86Q"],"\xED"=>[0,"\x86R"],"\xEE"=>[0,"\x86S"],"\xEF"=>[0,"\x86T"],"\xF0"=>[0,"\x86U"],"\xF1"=>[0,"\x86V"],"\xF2"=>[0,"\x86W"],"\xF3"=>[0,"\x86Y"],"\xF4"=>[0,"\x86j"],"\xF5"=>[0,"\x86k"],"\xF6"=>[0,"\x86q"],"\xF7"=>[0,"\x86v"],"\xF8"=>[0,"\x86w"],"\xF9"=>[0,"\x86x"],"\xFA"=>[0,"\x86y"],"\xFB"=>[0,"\x86z"],"\xFC"=>[82,"\x86"],"\xFD"=>[87,"\x86"],"\xFE"=>[130,"\x86"],"\xFF"=>[9,"\x86"],],["\x00"=>[77,"\x850"],"\x01"=>[18,"\x850"],"\x02"=>[77,"\x851"],"\x03"=>[18,"\x851"],"\x04"=>[77,"\x852"],"\x05"=>[18,"\x852"],"\x06"=>[77,"\x85a"],"\x07"=>[18,"\x85a"],"\x08"=>[77,"\x85c"],"\x09"=>[18,"\x85c"],"\x0A"=>[77,"\x85e"],"\x0B"=>[18,"\x85e"],"\x0C"=>[77,"\x85i"],"\x0D"=>[18,"\x85i"],"\x0E"=>[77,"\x85o"],"\x0F"=>[18,"\x85o"],"\x10"=>[77,"\x85s"],"\x11"=>[18,"\x85s"],"\x12"=>[77,"\x85t"],"\x13"=>[18,"\x85t"],"\x14"=>[0,"\x85\x20"],"\x15"=>[0,"\x85\x25"],"\x16"=>[0,"\x85-"],"\x17"=>[0,"\x85."],"\x18"=>[0,"\x85\x2F"],"\x19"=>[0,"\x853"],"\x1A"=>[0,"\x854"],"\x1B"=>[0,"\x855"],"\x1C"=>[0,"\x856"],"\x1D"=>[0,"\x857"],"\x1E"=>[0,"\x858"],"\x1F"=>[0,"\x859"],"\x20"=>[0,"\x85\x3D"],"\x21"=>[0,"\x85A"],"\x22"=>[0,"\x85_"],"\x23"=>[0,"\x85b"],"\x24"=>[0,"\x85d"],"\x25"=>[0,"\x85f"],"\x26"=>[0,"\x85g"],"\x27"=>[0,"\x85h"],"\x28"=>[0,"\x85l"],"\x29"=>[0,"\x85m"],"\x2A"=>[0,"\x85n"],"\x2B"=>[0,"\x85p"],"\x2C"=>[0,"\x85r"],"-"=>[0,"\x85u"],"."=>[100,"\x85"],"\x2F"=>[110,"\x85"],[111,"\x85"],[115,"\x85"],[116,"\x85"],[118,"\x85"],[119,"\x85"],[122,"\x85"],[123,"\x85"],[125,"\x85"],[126,"\x85"],[129,"\x85"],"\x3A"=>[143,"\x85"],"\x3B"=>[148,"\x85"],"\x3C"=>[151,"\x85"],"\x3D"=>[153,"\x85"],"\x3E"=>[83,"\x85"],"\x3F"=>[10,"\x85"],"\x40"=>[77,"\x860"],"A"=>[18,"\x860"],"B"=>[77,"\x861"],"C"=>[18,"\x861"],"D"=>[77,"\x862"],"E"=>[18,"\x862"],"F"=>[77,"\x86a"],"G"=>[18,"\x86a"],"H"=>[77,"\x86c"],"I"=>[18,"\x86c"],"J"=>[77,"\x86e"],"K"=>[18,"\x86e"],"L"=>[77,"\x86i"],"M"=>[18,"\x86i"],"N"=>[77,"\x86o"],"O"=>[18,"\x86o"],"P"=>[77,"\x86s"],"Q"=>[18,"\x86s"],"R"=>[77,"\x86t"],"S"=>[18,"\x86t"],"T"=>[0,"\x86\x20"],"U"=>[0,"\x86\x25"],"V"=>[0,"\x86-"],"W"=>[0,"\x86."],"X"=>[0,"\x86\x2F"],"Y"=>[0,"\x863"],"Z"=>[0,"\x864"],"\x5B"=>[0,"\x865"],"\x5C"=>[0,"\x866"],"\x5D"=>[0,"\x867"],"\x5E"=>[0,"\x868"],"_"=>[0,"\x869"],"\x60"=>[0,"\x86\x3D"],"a"=>[0,"\x86A"],"b"=>[0,"\x86_"],"c"=>[0,"\x86b"],"d"=>[0,"\x86d"],"e"=>[0,"\x86f"],"f"=>[0,"\x86g"],"g"=>[0,"\x86h"],"h"=>[0,"\x86l"],"i"=>[0,"\x86m"],"j"=>[0,"\x86n"],"k"=>[0,"\x86p"],"l"=>[0,"\x86r"],"m"=>[0,"\x86u"],"n"=>[100,"\x86"],"o"=>[110,"\x86"],"p"=>[111,"\x86"],"q"=>[115,"\x86"],"r"=>[116,"\x86"],"s"=>[118,"\x86"],"t"=>[119,"\x86"],"u"=>[122,"\x86"],"v"=>[123,"\x86"],"w"=>[125,"\x86"],"x"=>[126,"\x86"],"y"=>[129,"\x86"],"z"=>[143,"\x86"],"\x7B"=>[148,"\x86"],"\x7C"=>[151,"\x86"],"\x7D"=>[153,"\x86"],"~"=>[83,"\x86"],"\x7F"=>[10,"\x86"],"\x80"=>[77,"\x880"],"\x81"=>[18,"\x880"],"\x82"=>[77,"\x881"],"\x83"=>[18,"\x881"],"\x84"=>[77,"\x882"],"\x85"=>[18,"\x882"],"\x86"=>[77,"\x88a"],"\x87"=>[18,"\x88a"],"\x88"=>[77,"\x88c"],"\x89"=>[18,"\x88c"],"\x8A"=>[77,"\x88e"],"\x8B"=>[18,"\x88e"],"\x8C"=>[77,"\x88i"],"\x8D"=>[18,"\x88i"],"\x8E"=>[77,"\x88o"],"\x8F"=>[18,"\x88o"],"\x90"=>[77,"\x88s"],"\x91"=>[18,"\x88s"],"\x92"=>[77,"\x88t"],"\x93"=>[18,"\x88t"],"\x94"=>[0,"\x88\x20"],"\x95"=>[0,"\x88\x25"],"\x96"=>[0,"\x88-"],"\x97"=>[0,"\x88."],"\x98"=>[0,"\x88\x2F"],"\x99"=>[0,"\x883"],"\x9A"=>[0,"\x884"],"\x9B"=>[0,"\x885"],"\x9C"=>[0,"\x886"],"\x9D"=>[0,"\x887"],"\x9E"=>[0,"\x888"],"\x9F"=>[0,"\x889"],"\xA0"=>[0,"\x88\x3D"],"\xA1"=>[0,"\x88A"],"\xA2"=>[0,"\x88_"],"\xA3"=>[0,"\x88b"],"\xA4"=>[0,"\x88d"],"\xA5"=>[0,"\x88f"],"\xA6"=>[0,"\x88g"],"\xA7"=>[0,"\x88h"],"\xA8"=>[0,"\x88l"],"\xA9"=>[0,"\x88m"],"\xAA"=>[0,"\x88n"],"\xAB"=>[0,"\x88p"],"\xAC"=>[0,"\x88r"],"\xAD"=>[0,"\x88u"],"\xAE"=>[100,"\x88"],"\xAF"=>[110,"\x88"],"\xB0"=>[111,"\x88"],"\xB1"=>[115,"\x88"],"\xB2"=>[116,"\x88"],"\xB3"=>[118,"\x88"],"\xB4"=>[119,"\x88"],"\xB5"=>[122,"\x88"],"\xB6"=>[123,"\x88"],"\xB7"=>[125,"\x88"],"\xB8"=>[126,"\x88"],"\xB9"=>[129,"\x88"],"\xBA"=>[143,"\x88"],"\xBB"=>[148,"\x88"],"\xBC"=>[151,"\x88"],"\xBD"=>[153,"\x88"],"\xBE"=>[83,"\x88"],"\xBF"=>[10,"\x88"],"\xC0"=>[77,"\x920"],"\xC1"=>[18,"\x920"],"\xC2"=>[77,"\x921"],"\xC3"=>[18,"\x921"],"\xC4"=>[77,"\x922"],"\xC5"=>[18,"\x922"],"\xC6"=>[77,"\x92a"],"\xC7"=>[18,"\x92a"],"\xC8"=>[77,"\x92c"],"\xC9"=>[18,"\x92c"],"\xCA"=>[77,"\x92e"],"\xCB"=>[18,"\x92e"],"\xCC"=>[77,"\x92i"],"\xCD"=>[18,"\x92i"],"\xCE"=>[77,"\x92o"],"\xCF"=>[18,"\x92o"],"\xD0"=>[77,"\x92s"],"\xD1"=>[18,"\x92s"],"\xD2"=>[77,"\x92t"],"\xD3"=>[18,"\x92t"],"\xD4"=>[0,"\x92\x20"],"\xD5"=>[0,"\x92\x25"],"\xD6"=>[0,"\x92-"],"\xD7"=>[0,"\x92."],"\xD8"=>[0,"\x92\x2F"],"\xD9"=>[0,"\x923"],"\xDA"=>[0,"\x924"],"\xDB"=>[0,"\x925"],"\xDC"=>[0,"\x926"],"\xDD"=>[0,"\x927"],"\xDE"=>[0,"\x928"],"\xDF"=>[0,"\x929"],"\xE0"=>[0,"\x92\x3D"],"\xE1"=>[0,"\x92A"],"\xE2"=>[0,"\x92_"],"\xE3"=>[0,"\x92b"],"\xE4"=>[0,"\x92d"],"\xE5"=>[0,"\x92f"],"\xE6"=>[0,"\x92g"],"\xE7"=>[0,"\x92h"],"\xE8"=>[0,"\x92l"],"\xE9"=>[0,"\x92m"],"\xEA"=>[0,"\x92n"],"\xEB"=>[0,"\x92p"],"\xEC"=>[0,"\x92r"],"\xED"=>[0,"\x92u"],"\xEE"=>[100,"\x92"],"\xEF"=>[110,"\x92"],"\xF0"=>[111,"\x92"],"\xF1"=>[115,"\x92"],"\xF2"=>[116,"\x92"],"\xF3"=>[118,"\x92"],"\xF4"=>[119,"\x92"],"\xF5"=>[122,"\x92"],"\xF6"=>[123,"\x92"],"\xF7"=>[125,"\x92"],"\xF8"=>[126,"\x92"],"\xF9"=>[129,"\x92"],"\xFA"=>[143,"\x92"],"\xFB"=>[148,"\x92"],"\xFC"=>[151,"\x92"],"\xFD"=>[153,"\x92"],"\xFE"=>[83,"\x92"],"\xFF"=>[10,"\x92"],],["\x00"=>[94,"\x880"],"\x01"=>[76,"\x880"],"\x02"=>[104,"\x880"],"\x03"=>[16,"\x880"],"\x04"=>[94,"\x881"],"\x05"=>[76,"\x881"],"\x06"=>[104,"\x881"],"\x07"=>[16,"\x881"],"\x08"=>[94,"\x882"],"\x09"=>[76,"\x882"],"\x0A"=>[104,"\x882"],"\x0B"=>[16,"\x882"],"\x0C"=>[94,"\x88a"],"\x0D"=>[76,"\x88a"],"\x0E"=>[104,"\x88a"],"\x0F"=>[16,"\x88a"],"\x10"=>[94,"\x88c"],"\x11"=>[76,"\x88c"],"\x12"=>[104,"\x88c"],"\x13"=>[16,"\x88c"],"\x14"=>[94,"\x88e"],"\x15"=>[76,"\x88e"],"\x16"=>[104,"\x88e"],"\x17"=>[16,"\x88e"],"\x18"=>[94,"\x88i"],"\x19"=>[76,"\x88i"],"\x1A"=>[104,"\x88i"],"\x1B"=>[16,"\x88i"],"\x1C"=>[94,"\x88o"],"\x1D"=>[76,"\x88o"],"\x1E"=>[104,"\x88o"],"\x1F"=>[16,"\x88o"],"\x20"=>[94,"\x88s"],"\x21"=>[76,"\x88s"],"\x22"=>[104,"\x88s"],"\x23"=>[16,"\x88s"],"\x24"=>[94,"\x88t"],"\x25"=>[76,"\x88t"],"\x26"=>[104,"\x88t"],"\x27"=>[16,"\x88t"],"\x28"=>[77,"\x88\x20"],"\x29"=>[18,"\x88\x20"],"\x2A"=>[77,"\x88\x25"],"\x2B"=>[18,"\x88\x25"],"\x2C"=>[77,"\x88-"],"-"=>[18,"\x88-"],"."=>[77,"\x88."],"\x2F"=>[18,"\x88."],[77,"\x88\x2F"],[18,"\x88\x2F"],[77,"\x883"],[18,"\x883"],[77,"\x884"],[18,"\x884"],[77,"\x885"],[18,"\x885"],[77,"\x886"],[18,"\x886"],"\x3A"=>[77,"\x887"],"\x3B"=>[18,"\x887"],"\x3C"=>[77,"\x888"],"\x3D"=>[18,"\x888"],"\x3E"=>[77,"\x889"],"\x3F"=>[18,"\x889"],"\x40"=>[77,"\x88\x3D"],"A"=>[18,"\x88\x3D"],"B"=>[77,"\x88A"],"C"=>[18,"\x88A"],"D"=>[77,"\x88_"],"E"=>[18,"\x88_"],"F"=>[77,"\x88b"],"G"=>[18,"\x88b"],"H"=>[77,"\x88d"],"I"=>[18,"\x88d"],"J"=>[77,"\x88f"],"K"=>[18,"\x88f"],"L"=>[77,"\x88g"],"M"=>[18,"\x88g"],"N"=>[77,"\x88h"],"O"=>[18,"\x88h"],"P"=>[77,"\x88l"],"Q"=>[18,"\x88l"],"R"=>[77,"\x88m"],"S"=>[18,"\x88m"],"T"=>[77,"\x88n"],"U"=>[18,"\x88n"],"V"=>[77,"\x88p"],"W"=>[18,"\x88p"],"X"=>[77,"\x88r"],"Y"=>[18,"\x88r"],"Z"=>[77,"\x88u"],"\x5B"=>[18,"\x88u"],"\x5C"=>[0,"\x88\x3A"],"\x5D"=>[0,"\x88B"],"\x5E"=>[0,"\x88C"],"_"=>[0,"\x88D"],"\x60"=>[0,"\x88E"],"a"=>[0,"\x88F"],"b"=>[0,"\x88G"],"c"=>[0,"\x88H"],"d"=>[0,"\x88I"],"e"=>[0,"\x88J"],"f"=>[0,"\x88K"],"g"=>[0,"\x88L"],"h"=>[0,"\x88M"],"i"=>[0,"\x88N"],"j"=>[0,"\x88O"],"k"=>[0,"\x88P"],"l"=>[0,"\x88Q"],"m"=>[0,"\x88R"],"n"=>[0,"\x88S"],"o"=>[0,"\x88T"],"p"=>[0,"\x88U"],"q"=>[0,"\x88V"],"r"=>[0,"\x88W"],"s"=>[0,"\x88Y"],"t"=>[0,"\x88j"],"u"=>[0,"\x88k"],"v"=>[0,"\x88q"],"w"=>[0,"\x88v"],"x"=>[0,"\x88w"],"y"=>[0,"\x88x"],"z"=>[0,"\x88y"],"\x7B"=>[0,"\x88z"],"\x7C"=>[82,"\x88"],"\x7D"=>[87,"\x88"],"~"=>[130,"\x88"],"\x7F"=>[9,"\x88"],"\x80"=>[94,"\x920"],"\x81"=>[76,"\x920"],"\x82"=>[104,"\x920"],"\x83"=>[16,"\x920"],"\x84"=>[94,"\x921"],"\x85"=>[76,"\x921"],"\x86"=>[104,"\x921"],"\x87"=>[16,"\x921"],"\x88"=>[94,"\x922"],"\x89"=>[76,"\x922"],"\x8A"=>[104,"\x922"],"\x8B"=>[16,"\x922"],"\x8C"=>[94,"\x92a"],"\x8D"=>[76,"\x92a"],"\x8E"=>[104,"\x92a"],"\x8F"=>[16,"\x92a"],"\x90"=>[94,"\x92c"],"\x91"=>[76,"\x92c"],"\x92"=>[104,"\x92c"],"\x93"=>[16,"\x92c"],"\x94"=>[94,"\x92e"],"\x95"=>[76,"\x92e"],"\x96"=>[104,"\x92e"],"\x97"=>[16,"\x92e"],"\x98"=>[94,"\x92i"],"\x99"=>[76,"\x92i"],"\x9A"=>[104,"\x92i"],"\x9B"=>[16,"\x92i"],"\x9C"=>[94,"\x92o"],"\x9D"=>[76,"\x92o"],"\x9E"=>[104,"\x92o"],"\x9F"=>[16,"\x92o"],"\xA0"=>[94,"\x92s"],"\xA1"=>[76,"\x92s"],"\xA2"=>[104,"\x92s"],"\xA3"=>[16,"\x92s"],"\xA4"=>[94,"\x92t"],"\xA5"=>[76,"\x92t"],"\xA6"=>[104,"\x92t"],"\xA7"=>[16,"\x92t"],"\xA8"=>[77,"\x92\x20"],"\xA9"=>[18,"\x92\x20"],"\xAA"=>[77,"\x92\x25"],"\xAB"=>[18,"\x92\x25"],"\xAC"=>[77,"\x92-"],"\xAD"=>[18,"\x92-"],"\xAE"=>[77,"\x92."],"\xAF"=>[18,"\x92."],"\xB0"=>[77,"\x92\x2F"],"\xB1"=>[18,"\x92\x2F"],"\xB2"=>[77,"\x923"],"\xB3"=>[18,"\x923"],"\xB4"=>[77,"\x924"],"\xB5"=>[18,"\x924"],"\xB6"=>[77,"\x925"],"\xB7"=>[18,"\x925"],"\xB8"=>[77,"\x926"],"\xB9"=>[18,"\x926"],"\xBA"=>[77,"\x927"],"\xBB"=>[18,"\x927"],"\xBC"=>[77,"\x928"],"\xBD"=>[18,"\x928"],"\xBE"=>[77,"\x929"],"\xBF"=>[18,"\x929"],"\xC0"=>[77,"\x92\x3D"],"\xC1"=>[18,"\x92\x3D"],"\xC2"=>[77,"\x92A"],"\xC3"=>[18,"\x92A"],"\xC4"=>[77,"\x92_"],"\xC5"=>[18,"\x92_"],"\xC6"=>[77,"\x92b"],"\xC7"=>[18,"\x92b"],"\xC8"=>[77,"\x92d"],"\xC9"=>[18,"\x92d"],"\xCA"=>[77,"\x92f"],"\xCB"=>[18,"\x92f"],"\xCC"=>[77,"\x92g"],"\xCD"=>[18,"\x92g"],"\xCE"=>[77,"\x92h"],"\xCF"=>[18,"\x92h"],"\xD0"=>[77,"\x92l"],"\xD1"=>[18,"\x92l"],"\xD2"=>[77,"\x92m"],"\xD3"=>[18,"\x92m"],"\xD4"=>[77,"\x92n"],"\xD5"=>[18,"\x92n"],"\xD6"=>[77,"\x92p"],"\xD7"=>[18,"\x92p"],"\xD8"=>[77,"\x92r"],"\xD9"=>[18,"\x92r"],"\xDA"=>[77,"\x92u"],"\xDB"=>[18,"\x92u"],"\xDC"=>[0,"\x92\x3A"],"\xDD"=>[0,"\x92B"],"\xDE"=>[0,"\x92C"],"\xDF"=>[0,"\x92D"],"\xE0"=>[0,"\x92E"],"\xE1"=>[0,"\x92F"],"\xE2"=>[0,"\x92G"],"\xE3"=>[0,"\x92H"],"\xE4"=>[0,"\x92I"],"\xE5"=>[0,"\x92J"],"\xE6"=>[0,"\x92K"],"\xE7"=>[0,"\x92L"],"\xE8"=>[0,"\x92M"],"\xE9"=>[0,"\x92N"],"\xEA"=>[0,"\x92O"],"\xEB"=>[0,"\x92P"],"\xEC"=>[0,"\x92Q"],"\xED"=>[0,"\x92R"],"\xEE"=>[0,"\x92S"],"\xEF"=>[0,"\x92T"],"\xF0"=>[0,"\x92U"],"\xF1"=>[0,"\x92V"],"\xF2"=>[0,"\x92W"],"\xF3"=>[0,"\x92Y"],"\xF4"=>[0,"\x92j"],"\xF5"=>[0,"\x92k"],"\xF6"=>[0,"\x92q"],"\xF7"=>[0,"\x92v"],"\xF8"=>[0,"\x92w"],"\xF9"=>[0,"\x92x"],"\xFA"=>[0,"\x92y"],"\xFB"=>[0,"\x92z"],"\xFC"=>[82,"\x92"],"\xFD"=>[87,"\x92"],"\xFE"=>[130,"\x92"],"\xFF"=>[9,"\x92"],],["\x00"=>[94,"\x890"],"\x01"=>[76,"\x890"],"\x02"=>[104,"\x890"],"\x03"=>[16,"\x890"],"\x04"=>[94,"\x891"],"\x05"=>[76,"\x891"],"\x06"=>[104,"\x891"],"\x07"=>[16,"\x891"],"\x08"=>[94,"\x892"],"\x09"=>[76,"\x892"],"\x0A"=>[104,"\x892"],"\x0B"=>[16,"\x892"],"\x0C"=>[94,"\x89a"],"\x0D"=>[76,"\x89a"],"\x0E"=>[104,"\x89a"],"\x0F"=>[16,"\x89a"],"\x10"=>[94,"\x89c"],"\x11"=>[76,"\x89c"],"\x12"=>[104,"\x89c"],"\x13"=>[16,"\x89c"],"\x14"=>[94,"\x89e"],"\x15"=>[76,"\x89e"],"\x16"=>[104,"\x89e"],"\x17"=>[16,"\x89e"],"\x18"=>[94,"\x89i"],"\x19"=>[76,"\x89i"],"\x1A"=>[104,"\x89i"],"\x1B"=>[16,"\x89i"],"\x1C"=>[94,"\x89o"],"\x1D"=>[76,"\x89o"],"\x1E"=>[104,"\x89o"],"\x1F"=>[16,"\x89o"],"\x20"=>[94,"\x89s"],"\x21"=>[76,"\x89s"],"\x22"=>[104,"\x89s"],"\x23"=>[16,"\x89s"],"\x24"=>[94,"\x89t"],"\x25"=>[76,"\x89t"],"\x26"=>[104,"\x89t"],"\x27"=>[16,"\x89t"],"\x28"=>[77,"\x89\x20"],"\x29"=>[18,"\x89\x20"],"\x2A"=>[77,"\x89\x25"],"\x2B"=>[18,"\x89\x25"],"\x2C"=>[77,"\x89-"],"-"=>[18,"\x89-"],"."=>[77,"\x89."],"\x2F"=>[18,"\x89."],[77,"\x89\x2F"],[18,"\x89\x2F"],[77,"\x893"],[18,"\x893"],[77,"\x894"],[18,"\x894"],[77,"\x895"],[18,"\x895"],[77,"\x896"],[18,"\x896"],"\x3A"=>[77,"\x897"],"\x3B"=>[18,"\x897"],"\x3C"=>[77,"\x898"],"\x3D"=>[18,"\x898"],"\x3E"=>[77,"\x899"],"\x3F"=>[18,"\x899"],"\x40"=>[77,"\x89\x3D"],"A"=>[18,"\x89\x3D"],"B"=>[77,"\x89A"],"C"=>[18,"\x89A"],"D"=>[77,"\x89_"],"E"=>[18,"\x89_"],"F"=>[77,"\x89b"],"G"=>[18,"\x89b"],"H"=>[77,"\x89d"],"I"=>[18,"\x89d"],"J"=>[77,"\x89f"],"K"=>[18,"\x89f"],"L"=>[77,"\x89g"],"M"=>[18,"\x89g"],"N"=>[77,"\x89h"],"O"=>[18,"\x89h"],"P"=>[77,"\x89l"],"Q"=>[18,"\x89l"],"R"=>[77,"\x89m"],"S"=>[18,"\x89m"],"T"=>[77,"\x89n"],"U"=>[18,"\x89n"],"V"=>[77,"\x89p"],"W"=>[18,"\x89p"],"X"=>[77,"\x89r"],"Y"=>[18,"\x89r"],"Z"=>[77,"\x89u"],"\x5B"=>[18,"\x89u"],"\x5C"=>[0,"\x89\x3A"],"\x5D"=>[0,"\x89B"],"\x5E"=>[0,"\x89C"],"_"=>[0,"\x89D"],"\x60"=>[0,"\x89E"],"a"=>[0,"\x89F"],"b"=>[0,"\x89G"],"c"=>[0,"\x89H"],"d"=>[0,"\x89I"],"e"=>[0,"\x89J"],"f"=>[0,"\x89K"],"g"=>[0,"\x89L"],"h"=>[0,"\x89M"],"i"=>[0,"\x89N"],"j"=>[0,"\x89O"],"k"=>[0,"\x89P"],"l"=>[0,"\x89Q"],"m"=>[0,"\x89R"],"n"=>[0,"\x89S"],"o"=>[0,"\x89T"],"p"=>[0,"\x89U"],"q"=>[0,"\x89V"],"r"=>[0,"\x89W"],"s"=>[0,"\x89Y"],"t"=>[0,"\x89j"],"u"=>[0,"\x89k"],"v"=>[0,"\x89q"],"w"=>[0,"\x89v"],"x"=>[0,"\x89w"],"y"=>[0,"\x89x"],"z"=>[0,"\x89y"],"\x7B"=>[0,"\x89z"],"\x7C"=>[82,"\x89"],"\x7D"=>[87,"\x89"],"~"=>[130,"\x89"],"\x7F"=>[9,"\x89"],"\x80"=>[94,"\x8A0"],"\x81"=>[76,"\x8A0"],"\x82"=>[104,"\x8A0"],"\x83"=>[16,"\x8A0"],"\x84"=>[94,"\x8A1"],"\x85"=>[76,"\x8A1"],"\x86"=>[104,"\x8A1"],"\x87"=>[16,"\x8A1"],"\x88"=>[94,"\x8A2"],"\x89"=>[76,"\x8A2"],"\x8A"=>[104,"\x8A2"],"\x8B"=>[16,"\x8A2"],"\x8C"=>[94,"\x8Aa"],"\x8D"=>[76,"\x8Aa"],"\x8E"=>[104,"\x8Aa"],"\x8F"=>[16,"\x8Aa"],"\x90"=>[94,"\x8Ac"],"\x91"=>[76,"\x8Ac"],"\x92"=>[104,"\x8Ac"],"\x93"=>[16,"\x8Ac"],"\x94"=>[94,"\x8Ae"],"\x95"=>[76,"\x8Ae"],"\x96"=>[104,"\x8Ae"],"\x97"=>[16,"\x8Ae"],"\x98"=>[94,"\x8Ai"],"\x99"=>[76,"\x8Ai"],"\x9A"=>[104,"\x8Ai"],"\x9B"=>[16,"\x8Ai"],"\x9C"=>[94,"\x8Ao"],"\x9D"=>[76,"\x8Ao"],"\x9E"=>[104,"\x8Ao"],"\x9F"=>[16,"\x8Ao"],"\xA0"=>[94,"\x8As"],"\xA1"=>[76,"\x8As"],"\xA2"=>[104,"\x8As"],"\xA3"=>[16,"\x8As"],"\xA4"=>[94,"\x8At"],"\xA5"=>[76,"\x8At"],"\xA6"=>[104,"\x8At"],"\xA7"=>[16,"\x8At"],"\xA8"=>[77,"\x8A\x20"],"\xA9"=>[18,"\x8A\x20"],"\xAA"=>[77,"\x8A\x25"],"\xAB"=>[18,"\x8A\x25"],"\xAC"=>[77,"\x8A-"],"\xAD"=>[18,"\x8A-"],"\xAE"=>[77,"\x8A."],"\xAF"=>[18,"\x8A."],"\xB0"=>[77,"\x8A\x2F"],"\xB1"=>[18,"\x8A\x2F"],"\xB2"=>[77,"\x8A3"],"\xB3"=>[18,"\x8A3"],"\xB4"=>[77,"\x8A4"],"\xB5"=>[18,"\x8A4"],"\xB6"=>[77,"\x8A5"],"\xB7"=>[18,"\x8A5"],"\xB8"=>[77,"\x8A6"],"\xB9"=>[18,"\x8A6"],"\xBA"=>[77,"\x8A7"],"\xBB"=>[18,"\x8A7"],"\xBC"=>[77,"\x8A8"],"\xBD"=>[18,"\x8A8"],"\xBE"=>[77,"\x8A9"],"\xBF"=>[18,"\x8A9"],"\xC0"=>[77,"\x8A\x3D"],"\xC1"=>[18,"\x8A\x3D"],"\xC2"=>[77,"\x8AA"],"\xC3"=>[18,"\x8AA"],"\xC4"=>[77,"\x8A_"],"\xC5"=>[18,"\x8A_"],"\xC6"=>[77,"\x8Ab"],"\xC7"=>[18,"\x8Ab"],"\xC8"=>[77,"\x8Ad"],"\xC9"=>[18,"\x8Ad"],"\xCA"=>[77,"\x8Af"],"\xCB"=>[18,"\x8Af"],"\xCC"=>[77,"\x8Ag"],"\xCD"=>[18,"\x8Ag"],"\xCE"=>[77,"\x8Ah"],"\xCF"=>[18,"\x8Ah"],"\xD0"=>[77,"\x8Al"],"\xD1"=>[18,"\x8Al"],"\xD2"=>[77,"\x8Am"],"\xD3"=>[18,"\x8Am"],"\xD4"=>[77,"\x8An"],"\xD5"=>[18,"\x8An"],"\xD6"=>[77,"\x8Ap"],"\xD7"=>[18,"\x8Ap"],"\xD8"=>[77,"\x8Ar"],"\xD9"=>[18,"\x8Ar"],"\xDA"=>[77,"\x8Au"],"\xDB"=>[18,"\x8Au"],"\xDC"=>[0,"\x8A\x3A"],"\xDD"=>[0,"\x8AB"],"\xDE"=>[0,"\x8AC"],"\xDF"=>[0,"\x8AD"],"\xE0"=>[0,"\x8AE"],"\xE1"=>[0,"\x8AF"],"\xE2"=>[0,"\x8AG"],"\xE3"=>[0,"\x8AH"],"\xE4"=>[0,"\x8AI"],"\xE5"=>[0,"\x8AJ"],"\xE6"=>[0,"\x8AK"],"\xE7"=>[0,"\x8AL"],"\xE8"=>[0,"\x8AM"],"\xE9"=>[0,"\x8AN"],"\xEA"=>[0,"\x8AO"],"\xEB"=>[0,"\x8AP"],"\xEC"=>[0,"\x8AQ"],"\xED"=>[0,"\x8AR"],"\xEE"=>[0,"\x8AS"],"\xEF"=>[0,"\x8AT"],"\xF0"=>[0,"\x8AU"],"\xF1"=>[0,"\x8AV"],"\xF2"=>[0,"\x8AW"],"\xF3"=>[0,"\x8AY"],"\xF4"=>[0,"\x8Aj"],"\xF5"=>[0,"\x8Ak"],"\xF6"=>[0,"\x8Aq"],"\xF7"=>[0,"\x8Av"],"\xF8"=>[0,"\x8Aw"],"\xF9"=>[0,"\x8Ax"],"\xFA"=>[0,"\x8Ay"],"\xFB"=>[0,"\x8Az"],"\xFC"=>[82,"\x8A"],"\xFD"=>[87,"\x8A"],"\xFE"=>[130,"\x8A"],"\xFF"=>[9,"\x8A"],],["\x00"=>[94,"\x8B0"],"\x01"=>[76,"\x8B0"],"\x02"=>[104,"\x8B0"],"\x03"=>[16,"\x8B0"],"\x04"=>[94,"\x8B1"],"\x05"=>[76,"\x8B1"],"\x06"=>[104,"\x8B1"],"\x07"=>[16,"\x8B1"],"\x08"=>[94,"\x8B2"],"\x09"=>[76,"\x8B2"],"\x0A"=>[104,"\x8B2"],"\x0B"=>[16,"\x8B2"],"\x0C"=>[94,"\x8Ba"],"\x0D"=>[76,"\x8Ba"],"\x0E"=>[104,"\x8Ba"],"\x0F"=>[16,"\x8Ba"],"\x10"=>[94,"\x8Bc"],"\x11"=>[76,"\x8Bc"],"\x12"=>[104,"\x8Bc"],"\x13"=>[16,"\x8Bc"],"\x14"=>[94,"\x8Be"],"\x15"=>[76,"\x8Be"],"\x16"=>[104,"\x8Be"],"\x17"=>[16,"\x8Be"],"\x18"=>[94,"\x8Bi"],"\x19"=>[76,"\x8Bi"],"\x1A"=>[104,"\x8Bi"],"\x1B"=>[16,"\x8Bi"],"\x1C"=>[94,"\x8Bo"],"\x1D"=>[76,"\x8Bo"],"\x1E"=>[104,"\x8Bo"],"\x1F"=>[16,"\x8Bo"],"\x20"=>[94,"\x8Bs"],"\x21"=>[76,"\x8Bs"],"\x22"=>[104,"\x8Bs"],"\x23"=>[16,"\x8Bs"],"\x24"=>[94,"\x8Bt"],"\x25"=>[76,"\x8Bt"],"\x26"=>[104,"\x8Bt"],"\x27"=>[16,"\x8Bt"],"\x28"=>[77,"\x8B\x20"],"\x29"=>[18,"\x8B\x20"],"\x2A"=>[77,"\x8B\x25"],"\x2B"=>[18,"\x8B\x25"],"\x2C"=>[77,"\x8B-"],"-"=>[18,"\x8B-"],"."=>[77,"\x8B."],"\x2F"=>[18,"\x8B."],[77,"\x8B\x2F"],[18,"\x8B\x2F"],[77,"\x8B3"],[18,"\x8B3"],[77,"\x8B4"],[18,"\x8B4"],[77,"\x8B5"],[18,"\x8B5"],[77,"\x8B6"],[18,"\x8B6"],"\x3A"=>[77,"\x8B7"],"\x3B"=>[18,"\x8B7"],"\x3C"=>[77,"\x8B8"],"\x3D"=>[18,"\x8B8"],"\x3E"=>[77,"\x8B9"],"\x3F"=>[18,"\x8B9"],"\x40"=>[77,"\x8B\x3D"],"A"=>[18,"\x8B\x3D"],"B"=>[77,"\x8BA"],"C"=>[18,"\x8BA"],"D"=>[77,"\x8B_"],"E"=>[18,"\x8B_"],"F"=>[77,"\x8Bb"],"G"=>[18,"\x8Bb"],"H"=>[77,"\x8Bd"],"I"=>[18,"\x8Bd"],"J"=>[77,"\x8Bf"],"K"=>[18,"\x8Bf"],"L"=>[77,"\x8Bg"],"M"=>[18,"\x8Bg"],"N"=>[77,"\x8Bh"],"O"=>[18,"\x8Bh"],"P"=>[77,"\x8Bl"],"Q"=>[18,"\x8Bl"],"R"=>[77,"\x8Bm"],"S"=>[18,"\x8Bm"],"T"=>[77,"\x8Bn"],"U"=>[18,"\x8Bn"],"V"=>[77,"\x8Bp"],"W"=>[18,"\x8Bp"],"X"=>[77,"\x8Br"],"Y"=>[18,"\x8Br"],"Z"=>[77,"\x8Bu"],"\x5B"=>[18,"\x8Bu"],"\x5C"=>[0,"\x8B\x3A"],"\x5D"=>[0,"\x8BB"],"\x5E"=>[0,"\x8BC"],"_"=>[0,"\x8BD"],"\x60"=>[0,"\x8BE"],"a"=>[0,"\x8BF"],"b"=>[0,"\x8BG"],"c"=>[0,"\x8BH"],"d"=>[0,"\x8BI"],"e"=>[0,"\x8BJ"],"f"=>[0,"\x8BK"],"g"=>[0,"\x8BL"],"h"=>[0,"\x8BM"],"i"=>[0,"\x8BN"],"j"=>[0,"\x8BO"],"k"=>[0,"\x8BP"],"l"=>[0,"\x8BQ"],"m"=>[0,"\x8BR"],"n"=>[0,"\x8BS"],"o"=>[0,"\x8BT"],"p"=>[0,"\x8BU"],"q"=>[0,"\x8BV"],"r"=>[0,"\x8BW"],"s"=>[0,"\x8BY"],"t"=>[0,"\x8Bj"],"u"=>[0,"\x8Bk"],"v"=>[0,"\x8Bq"],"w"=>[0,"\x8Bv"],"x"=>[0,"\x8Bw"],"y"=>[0,"\x8Bx"],"z"=>[0,"\x8By"],"\x7B"=>[0,"\x8Bz"],"\x7C"=>[82,"\x8B"],"\x7D"=>[87,"\x8B"],"~"=>[130,"\x8B"],"\x7F"=>[9,"\x8B"],"\x80"=>[94,"\x8C0"],"\x81"=>[76,"\x8C0"],"\x82"=>[104,"\x8C0"],"\x83"=>[16,"\x8C0"],"\x84"=>[94,"\x8C1"],"\x85"=>[76,"\x8C1"],"\x86"=>[104,"\x8C1"],"\x87"=>[16,"\x8C1"],"\x88"=>[94,"\x8C2"],"\x89"=>[76,"\x8C2"],"\x8A"=>[104,"\x8C2"],"\x8B"=>[16,"\x8C2"],"\x8C"=>[94,"\x8Ca"],"\x8D"=>[76,"\x8Ca"],"\x8E"=>[104,"\x8Ca"],"\x8F"=>[16,"\x8Ca"],"\x90"=>[94,"\x8Cc"],"\x91"=>[76,"\x8Cc"],"\x92"=>[104,"\x8Cc"],"\x93"=>[16,"\x8Cc"],"\x94"=>[94,"\x8Ce"],"\x95"=>[76,"\x8Ce"],"\x96"=>[104,"\x8Ce"],"\x97"=>[16,"\x8Ce"],"\x98"=>[94,"\x8Ci"],"\x99"=>[76,"\x8Ci"],"\x9A"=>[104,"\x8Ci"],"\x9B"=>[16,"\x8Ci"],"\x9C"=>[94,"\x8Co"],"\x9D"=>[76,"\x8Co"],"\x9E"=>[104,"\x8Co"],"\x9F"=>[16,"\x8Co"],"\xA0"=>[94,"\x8Cs"],"\xA1"=>[76,"\x8Cs"],"\xA2"=>[104,"\x8Cs"],"\xA3"=>[16,"\x8Cs"],"\xA4"=>[94,"\x8Ct"],"\xA5"=>[76,"\x8Ct"],"\xA6"=>[104,"\x8Ct"],"\xA7"=>[16,"\x8Ct"],"\xA8"=>[77,"\x8C\x20"],"\xA9"=>[18,"\x8C\x20"],"\xAA"=>[77,"\x8C\x25"],"\xAB"=>[18,"\x8C\x25"],"\xAC"=>[77,"\x8C-"],"\xAD"=>[18,"\x8C-"],"\xAE"=>[77,"\x8C."],"\xAF"=>[18,"\x8C."],"\xB0"=>[77,"\x8C\x2F"],"\xB1"=>[18,"\x8C\x2F"],"\xB2"=>[77,"\x8C3"],"\xB3"=>[18,"\x8C3"],"\xB4"=>[77,"\x8C4"],"\xB5"=>[18,"\x8C4"],"\xB6"=>[77,"\x8C5"],"\xB7"=>[18,"\x8C5"],"\xB8"=>[77,"\x8C6"],"\xB9"=>[18,"\x8C6"],"\xBA"=>[77,"\x8C7"],"\xBB"=>[18,"\x8C7"],"\xBC"=>[77,"\x8C8"],"\xBD"=>[18,"\x8C8"],"\xBE"=>[77,"\x8C9"],"\xBF"=>[18,"\x8C9"],"\xC0"=>[77,"\x8C\x3D"],"\xC1"=>[18,"\x8C\x3D"],"\xC2"=>[77,"\x8CA"],"\xC3"=>[18,"\x8CA"],"\xC4"=>[77,"\x8C_"],"\xC5"=>[18,"\x8C_"],"\xC6"=>[77,"\x8Cb"],"\xC7"=>[18,"\x8Cb"],"\xC8"=>[77,"\x8Cd"],"\xC9"=>[18,"\x8Cd"],"\xCA"=>[77,"\x8Cf"],"\xCB"=>[18,"\x8Cf"],"\xCC"=>[77,"\x8Cg"],"\xCD"=>[18,"\x8Cg"],"\xCE"=>[77,"\x8Ch"],"\xCF"=>[18,"\x8Ch"],"\xD0"=>[77,"\x8Cl"],"\xD1"=>[18,"\x8Cl"],"\xD2"=>[77,"\x8Cm"],"\xD3"=>[18,"\x8Cm"],"\xD4"=>[77,"\x8Cn"],"\xD5"=>[18,"\x8Cn"],"\xD6"=>[77,"\x8Cp"],"\xD7"=>[18,"\x8Cp"],"\xD8"=>[77,"\x8Cr"],"\xD9"=>[18,"\x8Cr"],"\xDA"=>[77,"\x8Cu"],"\xDB"=>[18,"\x8Cu"],"\xDC"=>[0,"\x8C\x3A"],"\xDD"=>[0,"\x8CB"],"\xDE"=>[0,"\x8CC"],"\xDF"=>[0,"\x8CD"],"\xE0"=>[0,"\x8CE"],"\xE1"=>[0,"\x8CF"],"\xE2"=>[0,"\x8CG"],"\xE3"=>[0,"\x8CH"],"\xE4"=>[0,"\x8CI"],"\xE5"=>[0,"\x8CJ"],"\xE6"=>[0,"\x8CK"],"\xE7"=>[0,"\x8CL"],"\xE8"=>[0,"\x8CM"],"\xE9"=>[0,"\x8CN"],"\xEA"=>[0,"\x8CO"],"\xEB"=>[0,"\x8CP"],"\xEC"=>[0,"\x8CQ"],"\xED"=>[0,"\x8CR"],"\xEE"=>[0,"\x8CS"],"\xEF"=>[0,"\x8CT"],"\xF0"=>[0,"\x8CU"],"\xF1"=>[0,"\x8CV"],"\xF2"=>[0,"\x8CW"],"\xF3"=>[0,"\x8CY"],"\xF4"=>[0,"\x8Cj"],"\xF5"=>[0,"\x8Ck"],"\xF6"=>[0,"\x8Cq"],"\xF7"=>[0,"\x8Cv"],"\xF8"=>[0,"\x8Cw"],"\xF9"=>[0,"\x8Cx"],"\xFA"=>[0,"\x8Cy"],"\xFB"=>[0,"\x8Cz"],"\xFC"=>[82,"\x8C"],"\xFD"=>[87,"\x8C"],"\xFE"=>[130,"\x8C"],"\xFF"=>[9,"\x8C"],],["\x00"=>[77,"\x8B0"],"\x01"=>[18,"\x8B0"],"\x02"=>[77,"\x8B1"],"\x03"=>[18,"\x8B1"],"\x04"=>[77,"\x8B2"],"\x05"=>[18,"\x8B2"],"\x06"=>[77,"\x8Ba"],"\x07"=>[18,"\x8Ba"],"\x08"=>[77,"\x8Bc"],"\x09"=>[18,"\x8Bc"],"\x0A"=>[77,"\x8Be"],"\x0B"=>[18,"\x8Be"],"\x0C"=>[77,"\x8Bi"],"\x0D"=>[18,"\x8Bi"],"\x0E"=>[77,"\x8Bo"],"\x0F"=>[18,"\x8Bo"],"\x10"=>[77,"\x8Bs"],"\x11"=>[18,"\x8Bs"],"\x12"=>[77,"\x8Bt"],"\x13"=>[18,"\x8Bt"],"\x14"=>[0,"\x8B\x20"],"\x15"=>[0,"\x8B\x25"],"\x16"=>[0,"\x8B-"],"\x17"=>[0,"\x8B."],"\x18"=>[0,"\x8B\x2F"],"\x19"=>[0,"\x8B3"],"\x1A"=>[0,"\x8B4"],"\x1B"=>[0,"\x8B5"],"\x1C"=>[0,"\x8B6"],"\x1D"=>[0,"\x8B7"],"\x1E"=>[0,"\x8B8"],"\x1F"=>[0,"\x8B9"],"\x20"=>[0,"\x8B\x3D"],"\x21"=>[0,"\x8BA"],"\x22"=>[0,"\x8B_"],"\x23"=>[0,"\x8Bb"],"\x24"=>[0,"\x8Bd"],"\x25"=>[0,"\x8Bf"],"\x26"=>[0,"\x8Bg"],"\x27"=>[0,"\x8Bh"],"\x28"=>[0,"\x8Bl"],"\x29"=>[0,"\x8Bm"],"\x2A"=>[0,"\x8Bn"],"\x2B"=>[0,"\x8Bp"],"\x2C"=>[0,"\x8Br"],"-"=>[0,"\x8Bu"],"."=>[100,"\x8B"],"\x2F"=>[110,"\x8B"],[111,"\x8B"],[115,"\x8B"],[116,"\x8B"],[118,"\x8B"],[119,"\x8B"],[122,"\x8B"],[123,"\x8B"],[125,"\x8B"],[126,"\x8B"],[129,"\x8B"],"\x3A"=>[143,"\x8B"],"\x3B"=>[148,"\x8B"],"\x3C"=>[151,"\x8B"],"\x3D"=>[153,"\x8B"],"\x3E"=>[83,"\x8B"],"\x3F"=>[10,"\x8B"],"\x40"=>[77,"\x8C0"],"A"=>[18,"\x8C0"],"B"=>[77,"\x8C1"],"C"=>[18,"\x8C1"],"D"=>[77,"\x8C2"],"E"=>[18,"\x8C2"],"F"=>[77,"\x8Ca"],"G"=>[18,"\x8Ca"],"H"=>[77,"\x8Cc"],"I"=>[18,"\x8Cc"],"J"=>[77,"\x8Ce"],"K"=>[18,"\x8Ce"],"L"=>[77,"\x8Ci"],"M"=>[18,"\x8Ci"],"N"=>[77,"\x8Co"],"O"=>[18,"\x8Co"],"P"=>[77,"\x8Cs"],"Q"=>[18,"\x8Cs"],"R"=>[77,"\x8Ct"],"S"=>[18,"\x8Ct"],"T"=>[0,"\x8C\x20"],"U"=>[0,"\x8C\x25"],"V"=>[0,"\x8C-"],"W"=>[0,"\x8C."],"X"=>[0,"\x8C\x2F"],"Y"=>[0,"\x8C3"],"Z"=>[0,"\x8C4"],"\x5B"=>[0,"\x8C5"],"\x5C"=>[0,"\x8C6"],"\x5D"=>[0,"\x8C7"],"\x5E"=>[0,"\x8C8"],"_"=>[0,"\x8C9"],"\x60"=>[0,"\x8C\x3D"],"a"=>[0,"\x8CA"],"b"=>[0,"\x8C_"],"c"=>[0,"\x8Cb"],"d"=>[0,"\x8Cd"],"e"=>[0,"\x8Cf"],"f"=>[0,"\x8Cg"],"g"=>[0,"\x8Ch"],"h"=>[0,"\x8Cl"],"i"=>[0,"\x8Cm"],"j"=>[0,"\x8Cn"],"k"=>[0,"\x8Cp"],"l"=>[0,"\x8Cr"],"m"=>[0,"\x8Cu"],"n"=>[100,"\x8C"],"o"=>[110,"\x8C"],"p"=>[111,"\x8C"],"q"=>[115,"\x8C"],"r"=>[116,"\x8C"],"s"=>[118,"\x8C"],"t"=>[119,"\x8C"],"u"=>[122,"\x8C"],"v"=>[123,"\x8C"],"w"=>[125,"\x8C"],"x"=>[126,"\x8C"],"y"=>[129,"\x8C"],"z"=>[143,"\x8C"],"\x7B"=>[148,"\x8C"],"\x7C"=>[151,"\x8C"],"\x7D"=>[153,"\x8C"],"~"=>[83,"\x8C"],"\x7F"=>[10,"\x8C"],"\x80"=>[77,"\x8D0"],"\x81"=>[18,"\x8D0"],"\x82"=>[77,"\x8D1"],"\x83"=>[18,"\x8D1"],"\x84"=>[77,"\x8D2"],"\x85"=>[18,"\x8D2"],"\x86"=>[77,"\x8Da"],"\x87"=>[18,"\x8Da"],"\x88"=>[77,"\x8Dc"],"\x89"=>[18,"\x8Dc"],"\x8A"=>[77,"\x8De"],"\x8B"=>[18,"\x8De"],"\x8C"=>[77,"\x8Di"],"\x8D"=>[18,"\x8Di"],"\x8E"=>[77,"\x8Do"],"\x8F"=>[18,"\x8Do"],"\x90"=>[77,"\x8Ds"],"\x91"=>[18,"\x8Ds"],"\x92"=>[77,"\x8Dt"],"\x93"=>[18,"\x8Dt"],"\x94"=>[0,"\x8D\x20"],"\x95"=>[0,"\x8D\x25"],"\x96"=>[0,"\x8D-"],"\x97"=>[0,"\x8D."],"\x98"=>[0,"\x8D\x2F"],"\x99"=>[0,"\x8D3"],"\x9A"=>[0,"\x8D4"],"\x9B"=>[0,"\x8D5"],"\x9C"=>[0,"\x8D6"],"\x9D"=>[0,"\x8D7"],"\x9E"=>[0,"\x8D8"],"\x9F"=>[0,"\x8D9"],"\xA0"=>[0,"\x8D\x3D"],"\xA1"=>[0,"\x8DA"],"\xA2"=>[0,"\x8D_"],"\xA3"=>[0,"\x8Db"],"\xA4"=>[0,"\x8Dd"],"\xA5"=>[0,"\x8Df"],"\xA6"=>[0,"\x8Dg"],"\xA7"=>[0,"\x8Dh"],"\xA8"=>[0,"\x8Dl"],"\xA9"=>[0,"\x8Dm"],"\xAA"=>[0,"\x8Dn"],"\xAB"=>[0,"\x8Dp"],"\xAC"=>[0,"\x8Dr"],"\xAD"=>[0,"\x8Du"],"\xAE"=>[100,"\x8D"],"\xAF"=>[110,"\x8D"],"\xB0"=>[111,"\x8D"],"\xB1"=>[115,"\x8D"],"\xB2"=>[116,"\x8D"],"\xB3"=>[118,"\x8D"],"\xB4"=>[119,"\x8D"],"\xB5"=>[122,"\x8D"],"\xB6"=>[123,"\x8D"],"\xB7"=>[125,"\x8D"],"\xB8"=>[126,"\x8D"],"\xB9"=>[129,"\x8D"],"\xBA"=>[143,"\x8D"],"\xBB"=>[148,"\x8D"],"\xBC"=>[151,"\x8D"],"\xBD"=>[153,"\x8D"],"\xBE"=>[83,"\x8D"],"\xBF"=>[10,"\x8D"],"\xC0"=>[77,"\x8F0"],"\xC1"=>[18,"\x8F0"],"\xC2"=>[77,"\x8F1"],"\xC3"=>[18,"\x8F1"],"\xC4"=>[77,"\x8F2"],"\xC5"=>[18,"\x8F2"],"\xC6"=>[77,"\x8Fa"],"\xC7"=>[18,"\x8Fa"],"\xC8"=>[77,"\x8Fc"],"\xC9"=>[18,"\x8Fc"],"\xCA"=>[77,"\x8Fe"],"\xCB"=>[18,"\x8Fe"],"\xCC"=>[77,"\x8Fi"],"\xCD"=>[18,"\x8Fi"],"\xCE"=>[77,"\x8Fo"],"\xCF"=>[18,"\x8Fo"],"\xD0"=>[77,"\x8Fs"],"\xD1"=>[18,"\x8Fs"],"\xD2"=>[77,"\x8Ft"],"\xD3"=>[18,"\x8Ft"],"\xD4"=>[0,"\x8F\x20"],"\xD5"=>[0,"\x8F\x25"],"\xD6"=>[0,"\x8F-"],"\xD7"=>[0,"\x8F."],"\xD8"=>[0,"\x8F\x2F"],"\xD9"=>[0,"\x8F3"],"\xDA"=>[0,"\x8F4"],"\xDB"=>[0,"\x8F5"],"\xDC"=>[0,"\x8F6"],"\xDD"=>[0,"\x8F7"],"\xDE"=>[0,"\x8F8"],"\xDF"=>[0,"\x8F9"],"\xE0"=>[0,"\x8F\x3D"],"\xE1"=>[0,"\x8FA"],"\xE2"=>[0,"\x8F_"],"\xE3"=>[0,"\x8Fb"],"\xE4"=>[0,"\x8Fd"],"\xE5"=>[0,"\x8Ff"],"\xE6"=>[0,"\x8Fg"],"\xE7"=>[0,"\x8Fh"],"\xE8"=>[0,"\x8Fl"],"\xE9"=>[0,"\x8Fm"],"\xEA"=>[0,"\x8Fn"],"\xEB"=>[0,"\x8Fp"],"\xEC"=>[0,"\x8Fr"],"\xED"=>[0,"\x8Fu"],"\xEE"=>[100,"\x8F"],"\xEF"=>[110,"\x8F"],"\xF0"=>[111,"\x8F"],"\xF1"=>[115,"\x8F"],"\xF2"=>[116,"\x8F"],"\xF3"=>[118,"\x8F"],"\xF4"=>[119,"\x8F"],"\xF5"=>[122,"\x8F"],"\xF6"=>[123,"\x8F"],"\xF7"=>[125,"\x8F"],"\xF8"=>[126,"\x8F"],"\xF9"=>[129,"\x8F"],"\xFA"=>[143,"\x8F"],"\xFB"=>[148,"\x8F"],"\xFC"=>[151,"\x8F"],"\xFD"=>[153,"\x8F"],"\xFE"=>[83,"\x8F"],"\xFF"=>[10,"\x8F"],],["\x00"=>[94,"\x8D0"],"\x01"=>[76,"\x8D0"],"\x02"=>[104,"\x8D0"],"\x03"=>[16,"\x8D0"],"\x04"=>[94,"\x8D1"],"\x05"=>[76,"\x8D1"],"\x06"=>[104,"\x8D1"],"\x07"=>[16,"\x8D1"],"\x08"=>[94,"\x8D2"],"\x09"=>[76,"\x8D2"],"\x0A"=>[104,"\x8D2"],"\x0B"=>[16,"\x8D2"],"\x0C"=>[94,"\x8Da"],"\x0D"=>[76,"\x8Da"],"\x0E"=>[104,"\x8Da"],"\x0F"=>[16,"\x8Da"],"\x10"=>[94,"\x8Dc"],"\x11"=>[76,"\x8Dc"],"\x12"=>[104,"\x8Dc"],"\x13"=>[16,"\x8Dc"],"\x14"=>[94,"\x8De"],"\x15"=>[76,"\x8De"],"\x16"=>[104,"\x8De"],"\x17"=>[16,"\x8De"],"\x18"=>[94,"\x8Di"],"\x19"=>[76,"\x8Di"],"\x1A"=>[104,"\x8Di"],"\x1B"=>[16,"\x8Di"],"\x1C"=>[94,"\x8Do"],"\x1D"=>[76,"\x8Do"],"\x1E"=>[104,"\x8Do"],"\x1F"=>[16,"\x8Do"],"\x20"=>[94,"\x8Ds"],"\x21"=>[76,"\x8Ds"],"\x22"=>[104,"\x8Ds"],"\x23"=>[16,"\x8Ds"],"\x24"=>[94,"\x8Dt"],"\x25"=>[76,"\x8Dt"],"\x26"=>[104,"\x8Dt"],"\x27"=>[16,"\x8Dt"],"\x28"=>[77,"\x8D\x20"],"\x29"=>[18,"\x8D\x20"],"\x2A"=>[77,"\x8D\x25"],"\x2B"=>[18,"\x8D\x25"],"\x2C"=>[77,"\x8D-"],"-"=>[18,"\x8D-"],"."=>[77,"\x8D."],"\x2F"=>[18,"\x8D."],[77,"\x8D\x2F"],[18,"\x8D\x2F"],[77,"\x8D3"],[18,"\x8D3"],[77,"\x8D4"],[18,"\x8D4"],[77,"\x8D5"],[18,"\x8D5"],[77,"\x8D6"],[18,"\x8D6"],"\x3A"=>[77,"\x8D7"],"\x3B"=>[18,"\x8D7"],"\x3C"=>[77,"\x8D8"],"\x3D"=>[18,"\x8D8"],"\x3E"=>[77,"\x8D9"],"\x3F"=>[18,"\x8D9"],"\x40"=>[77,"\x8D\x3D"],"A"=>[18,"\x8D\x3D"],"B"=>[77,"\x8DA"],"C"=>[18,"\x8DA"],"D"=>[77,"\x8D_"],"E"=>[18,"\x8D_"],"F"=>[77,"\x8Db"],"G"=>[18,"\x8Db"],"H"=>[77,"\x8Dd"],"I"=>[18,"\x8Dd"],"J"=>[77,"\x8Df"],"K"=>[18,"\x8Df"],"L"=>[77,"\x8Dg"],"M"=>[18,"\x8Dg"],"N"=>[77,"\x8Dh"],"O"=>[18,"\x8Dh"],"P"=>[77,"\x8Dl"],"Q"=>[18,"\x8Dl"],"R"=>[77,"\x8Dm"],"S"=>[18,"\x8Dm"],"T"=>[77,"\x8Dn"],"U"=>[18,"\x8Dn"],"V"=>[77,"\x8Dp"],"W"=>[18,"\x8Dp"],"X"=>[77,"\x8Dr"],"Y"=>[18,"\x8Dr"],"Z"=>[77,"\x8Du"],"\x5B"=>[18,"\x8Du"],"\x5C"=>[0,"\x8D\x3A"],"\x5D"=>[0,"\x8DB"],"\x5E"=>[0,"\x8DC"],"_"=>[0,"\x8DD"],"\x60"=>[0,"\x8DE"],"a"=>[0,"\x8DF"],"b"=>[0,"\x8DG"],"c"=>[0,"\x8DH"],"d"=>[0,"\x8DI"],"e"=>[0,"\x8DJ"],"f"=>[0,"\x8DK"],"g"=>[0,"\x8DL"],"h"=>[0,"\x8DM"],"i"=>[0,"\x8DN"],"j"=>[0,"\x8DO"],"k"=>[0,"\x8DP"],"l"=>[0,"\x8DQ"],"m"=>[0,"\x8DR"],"n"=>[0,"\x8DS"],"o"=>[0,"\x8DT"],"p"=>[0,"\x8DU"],"q"=>[0,"\x8DV"],"r"=>[0,"\x8DW"],"s"=>[0,"\x8DY"],"t"=>[0,"\x8Dj"],"u"=>[0,"\x8Dk"],"v"=>[0,"\x8Dq"],"w"=>[0,"\x8Dv"],"x"=>[0,"\x8Dw"],"y"=>[0,"\x8Dx"],"z"=>[0,"\x8Dy"],"\x7B"=>[0,"\x8Dz"],"\x7C"=>[82,"\x8D"],"\x7D"=>[87,"\x8D"],"~"=>[130,"\x8D"],"\x7F"=>[9,"\x8D"],"\x80"=>[94,"\x8F0"],"\x81"=>[76,"\x8F0"],"\x82"=>[104,"\x8F0"],"\x83"=>[16,"\x8F0"],"\x84"=>[94,"\x8F1"],"\x85"=>[76,"\x8F1"],"\x86"=>[104,"\x8F1"],"\x87"=>[16,"\x8F1"],"\x88"=>[94,"\x8F2"],"\x89"=>[76,"\x8F2"],"\x8A"=>[104,"\x8F2"],"\x8B"=>[16,"\x8F2"],"\x8C"=>[94,"\x8Fa"],"\x8D"=>[76,"\x8Fa"],"\x8E"=>[104,"\x8Fa"],"\x8F"=>[16,"\x8Fa"],"\x90"=>[94,"\x8Fc"],"\x91"=>[76,"\x8Fc"],"\x92"=>[104,"\x8Fc"],"\x93"=>[16,"\x8Fc"],"\x94"=>[94,"\x8Fe"],"\x95"=>[76,"\x8Fe"],"\x96"=>[104,"\x8Fe"],"\x97"=>[16,"\x8Fe"],"\x98"=>[94,"\x8Fi"],"\x99"=>[76,"\x8Fi"],"\x9A"=>[104,"\x8Fi"],"\x9B"=>[16,"\x8Fi"],"\x9C"=>[94,"\x8Fo"],"\x9D"=>[76,"\x8Fo"],"\x9E"=>[104,"\x8Fo"],"\x9F"=>[16,"\x8Fo"],"\xA0"=>[94,"\x8Fs"],"\xA1"=>[76,"\x8Fs"],"\xA2"=>[104,"\x8Fs"],"\xA3"=>[16,"\x8Fs"],"\xA4"=>[94,"\x8Ft"],"\xA5"=>[76,"\x8Ft"],"\xA6"=>[104,"\x8Ft"],"\xA7"=>[16,"\x8Ft"],"\xA8"=>[77,"\x8F\x20"],"\xA9"=>[18,"\x8F\x20"],"\xAA"=>[77,"\x8F\x25"],"\xAB"=>[18,"\x8F\x25"],"\xAC"=>[77,"\x8F-"],"\xAD"=>[18,"\x8F-"],"\xAE"=>[77,"\x8F."],"\xAF"=>[18,"\x8F."],"\xB0"=>[77,"\x8F\x2F"],"\xB1"=>[18,"\x8F\x2F"],"\xB2"=>[77,"\x8F3"],"\xB3"=>[18,"\x8F3"],"\xB4"=>[77,"\x8F4"],"\xB5"=>[18,"\x8F4"],"\xB6"=>[77,"\x8F5"],"\xB7"=>[18,"\x8F5"],"\xB8"=>[77,"\x8F6"],"\xB9"=>[18,"\x8F6"],"\xBA"=>[77,"\x8F7"],"\xBB"=>[18,"\x8F7"],"\xBC"=>[77,"\x8F8"],"\xBD"=>[18,"\x8F8"],"\xBE"=>[77,"\x8F9"],"\xBF"=>[18,"\x8F9"],"\xC0"=>[77,"\x8F\x3D"],"\xC1"=>[18,"\x8F\x3D"],"\xC2"=>[77,"\x8FA"],"\xC3"=>[18,"\x8FA"],"\xC4"=>[77,"\x8F_"],"\xC5"=>[18,"\x8F_"],"\xC6"=>[77,"\x8Fb"],"\xC7"=>[18,"\x8Fb"],"\xC8"=>[77,"\x8Fd"],"\xC9"=>[18,"\x8Fd"],"\xCA"=>[77,"\x8Ff"],"\xCB"=>[18,"\x8Ff"],"\xCC"=>[77,"\x8Fg"],"\xCD"=>[18,"\x8Fg"],"\xCE"=>[77,"\x8Fh"],"\xCF"=>[18,"\x8Fh"],"\xD0"=>[77,"\x8Fl"],"\xD1"=>[18,"\x8Fl"],"\xD2"=>[77,"\x8Fm"],"\xD3"=>[18,"\x8Fm"],"\xD4"=>[77,"\x8Fn"],"\xD5"=>[18,"\x8Fn"],"\xD6"=>[77,"\x8Fp"],"\xD7"=>[18,"\x8Fp"],"\xD8"=>[77,"\x8Fr"],"\xD9"=>[18,"\x8Fr"],"\xDA"=>[77,"\x8Fu"],"\xDB"=>[18,"\x8Fu"],"\xDC"=>[0,"\x8F\x3A"],"\xDD"=>[0,"\x8FB"],"\xDE"=>[0,"\x8FC"],"\xDF"=>[0,"\x8FD"],"\xE0"=>[0,"\x8FE"],"\xE1"=>[0,"\x8FF"],"\xE2"=>[0,"\x8FG"],"\xE3"=>[0,"\x8FH"],"\xE4"=>[0,"\x8FI"],"\xE5"=>[0,"\x8FJ"],"\xE6"=>[0,"\x8FK"],"\xE7"=>[0,"\x8FL"],"\xE8"=>[0,"\x8FM"],"\xE9"=>[0,"\x8FN"],"\xEA"=>[0,"\x8FO"],"\xEB"=>[0,"\x8FP"],"\xEC"=>[0,"\x8FQ"],"\xED"=>[0,"\x8FR"],"\xEE"=>[0,"\x8FS"],"\xEF"=>[0,"\x8FT"],"\xF0"=>[0,"\x8FU"],"\xF1"=>[0,"\x8FV"],"\xF2"=>[0,"\x8FW"],"\xF3"=>[0,"\x8FY"],"\xF4"=>[0,"\x8Fj"],"\xF5"=>[0,"\x8Fk"],"\xF6"=>[0,"\x8Fq"],"\xF7"=>[0,"\x8Fv"],"\xF8"=>[0,"\x8Fw"],"\xF9"=>[0,"\x8Fx"],"\xFA"=>[0,"\x8Fy"],"\xFB"=>[0,"\x8Fz"],"\xFC"=>[82,"\x8F"],"\xFD"=>[87,"\x8F"],"\xFE"=>[130,"\x8F"],"\xFF"=>[9,"\x8F"],],["\x00"=>[94,"\x900"],"\x01"=>[76,"\x900"],"\x02"=>[104,"\x900"],"\x03"=>[16,"\x900"],"\x04"=>[94,"\x901"],"\x05"=>[76,"\x901"],"\x06"=>[104,"\x901"],"\x07"=>[16,"\x901"],"\x08"=>[94,"\x902"],"\x09"=>[76,"\x902"],"\x0A"=>[104,"\x902"],"\x0B"=>[16,"\x902"],"\x0C"=>[94,"\x90a"],"\x0D"=>[76,"\x90a"],"\x0E"=>[104,"\x90a"],"\x0F"=>[16,"\x90a"],"\x10"=>[94,"\x90c"],"\x11"=>[76,"\x90c"],"\x12"=>[104,"\x90c"],"\x13"=>[16,"\x90c"],"\x14"=>[94,"\x90e"],"\x15"=>[76,"\x90e"],"\x16"=>[104,"\x90e"],"\x17"=>[16,"\x90e"],"\x18"=>[94,"\x90i"],"\x19"=>[76,"\x90i"],"\x1A"=>[104,"\x90i"],"\x1B"=>[16,"\x90i"],"\x1C"=>[94,"\x90o"],"\x1D"=>[76,"\x90o"],"\x1E"=>[104,"\x90o"],"\x1F"=>[16,"\x90o"],"\x20"=>[94,"\x90s"],"\x21"=>[76,"\x90s"],"\x22"=>[104,"\x90s"],"\x23"=>[16,"\x90s"],"\x24"=>[94,"\x90t"],"\x25"=>[76,"\x90t"],"\x26"=>[104,"\x90t"],"\x27"=>[16,"\x90t"],"\x28"=>[77,"\x90\x20"],"\x29"=>[18,"\x90\x20"],"\x2A"=>[77,"\x90\x25"],"\x2B"=>[18,"\x90\x25"],"\x2C"=>[77,"\x90-"],"-"=>[18,"\x90-"],"."=>[77,"\x90."],"\x2F"=>[18,"\x90."],[77,"\x90\x2F"],[18,"\x90\x2F"],[77,"\x903"],[18,"\x903"],[77,"\x904"],[18,"\x904"],[77,"\x905"],[18,"\x905"],[77,"\x906"],[18,"\x906"],"\x3A"=>[77,"\x907"],"\x3B"=>[18,"\x907"],"\x3C"=>[77,"\x908"],"\x3D"=>[18,"\x908"],"\x3E"=>[77,"\x909"],"\x3F"=>[18,"\x909"],"\x40"=>[77,"\x90\x3D"],"A"=>[18,"\x90\x3D"],"B"=>[77,"\x90A"],"C"=>[18,"\x90A"],"D"=>[77,"\x90_"],"E"=>[18,"\x90_"],"F"=>[77,"\x90b"],"G"=>[18,"\x90b"],"H"=>[77,"\x90d"],"I"=>[18,"\x90d"],"J"=>[77,"\x90f"],"K"=>[18,"\x90f"],"L"=>[77,"\x90g"],"M"=>[18,"\x90g"],"N"=>[77,"\x90h"],"O"=>[18,"\x90h"],"P"=>[77,"\x90l"],"Q"=>[18,"\x90l"],"R"=>[77,"\x90m"],"S"=>[18,"\x90m"],"T"=>[77,"\x90n"],"U"=>[18,"\x90n"],"V"=>[77,"\x90p"],"W"=>[18,"\x90p"],"X"=>[77,"\x90r"],"Y"=>[18,"\x90r"],"Z"=>[77,"\x90u"],"\x5B"=>[18,"\x90u"],"\x5C"=>[0,"\x90\x3A"],"\x5D"=>[0,"\x90B"],"\x5E"=>[0,"\x90C"],"_"=>[0,"\x90D"],"\x60"=>[0,"\x90E"],"a"=>[0,"\x90F"],"b"=>[0,"\x90G"],"c"=>[0,"\x90H"],"d"=>[0,"\x90I"],"e"=>[0,"\x90J"],"f"=>[0,"\x90K"],"g"=>[0,"\x90L"],"h"=>[0,"\x90M"],"i"=>[0,"\x90N"],"j"=>[0,"\x90O"],"k"=>[0,"\x90P"],"l"=>[0,"\x90Q"],"m"=>[0,"\x90R"],"n"=>[0,"\x90S"],"o"=>[0,"\x90T"],"p"=>[0,"\x90U"],"q"=>[0,"\x90V"],"r"=>[0,"\x90W"],"s"=>[0,"\x90Y"],"t"=>[0,"\x90j"],"u"=>[0,"\x90k"],"v"=>[0,"\x90q"],"w"=>[0,"\x90v"],"x"=>[0,"\x90w"],"y"=>[0,"\x90x"],"z"=>[0,"\x90y"],"\x7B"=>[0,"\x90z"],"\x7C"=>[82,"\x90"],"\x7D"=>[87,"\x90"],"~"=>[130,"\x90"],"\x7F"=>[9,"\x90"],"\x80"=>[94,"\x910"],"\x81"=>[76,"\x910"],"\x82"=>[104,"\x910"],"\x83"=>[16,"\x910"],"\x84"=>[94,"\x911"],"\x85"=>[76,"\x911"],"\x86"=>[104,"\x911"],"\x87"=>[16,"\x911"],"\x88"=>[94,"\x912"],"\x89"=>[76,"\x912"],"\x8A"=>[104,"\x912"],"\x8B"=>[16,"\x912"],"\x8C"=>[94,"\x91a"],"\x8D"=>[76,"\x91a"],"\x8E"=>[104,"\x91a"],"\x8F"=>[16,"\x91a"],"\x90"=>[94,"\x91c"],"\x91"=>[76,"\x91c"],"\x92"=>[104,"\x91c"],"\x93"=>[16,"\x91c"],"\x94"=>[94,"\x91e"],"\x95"=>[76,"\x91e"],"\x96"=>[104,"\x91e"],"\x97"=>[16,"\x91e"],"\x98"=>[94,"\x91i"],"\x99"=>[76,"\x91i"],"\x9A"=>[104,"\x91i"],"\x9B"=>[16,"\x91i"],"\x9C"=>[94,"\x91o"],"\x9D"=>[76,"\x91o"],"\x9E"=>[104,"\x91o"],"\x9F"=>[16,"\x91o"],"\xA0"=>[94,"\x91s"],"\xA1"=>[76,"\x91s"],"\xA2"=>[104,"\x91s"],"\xA3"=>[16,"\x91s"],"\xA4"=>[94,"\x91t"],"\xA5"=>[76,"\x91t"],"\xA6"=>[104,"\x91t"],"\xA7"=>[16,"\x91t"],"\xA8"=>[77,"\x91\x20"],"\xA9"=>[18,"\x91\x20"],"\xAA"=>[77,"\x91\x25"],"\xAB"=>[18,"\x91\x25"],"\xAC"=>[77,"\x91-"],"\xAD"=>[18,"\x91-"],"\xAE"=>[77,"\x91."],"\xAF"=>[18,"\x91."],"\xB0"=>[77,"\x91\x2F"],"\xB1"=>[18,"\x91\x2F"],"\xB2"=>[77,"\x913"],"\xB3"=>[18,"\x913"],"\xB4"=>[77,"\x914"],"\xB5"=>[18,"\x914"],"\xB6"=>[77,"\x915"],"\xB7"=>[18,"\x915"],"\xB8"=>[77,"\x916"],"\xB9"=>[18,"\x916"],"\xBA"=>[77,"\x917"],"\xBB"=>[18,"\x917"],"\xBC"=>[77,"\x918"],"\xBD"=>[18,"\x918"],"\xBE"=>[77,"\x919"],"\xBF"=>[18,"\x919"],"\xC0"=>[77,"\x91\x3D"],"\xC1"=>[18,"\x91\x3D"],"\xC2"=>[77,"\x91A"],"\xC3"=>[18,"\x91A"],"\xC4"=>[77,"\x91_"],"\xC5"=>[18,"\x91_"],"\xC6"=>[77,"\x91b"],"\xC7"=>[18,"\x91b"],"\xC8"=>[77,"\x91d"],"\xC9"=>[18,"\x91d"],"\xCA"=>[77,"\x91f"],"\xCB"=>[18,"\x91f"],"\xCC"=>[77,"\x91g"],"\xCD"=>[18,"\x91g"],"\xCE"=>[77,"\x91h"],"\xCF"=>[18,"\x91h"],"\xD0"=>[77,"\x91l"],"\xD1"=>[18,"\x91l"],"\xD2"=>[77,"\x91m"],"\xD3"=>[18,"\x91m"],"\xD4"=>[77,"\x91n"],"\xD5"=>[18,"\x91n"],"\xD6"=>[77,"\x91p"],"\xD7"=>[18,"\x91p"],"\xD8"=>[77,"\x91r"],"\xD9"=>[18,"\x91r"],"\xDA"=>[77,"\x91u"],"\xDB"=>[18,"\x91u"],"\xDC"=>[0,"\x91\x3A"],"\xDD"=>[0,"\x91B"],"\xDE"=>[0,"\x91C"],"\xDF"=>[0,"\x91D"],"\xE0"=>[0,"\x91E"],"\xE1"=>[0,"\x91F"],"\xE2"=>[0,"\x91G"],"\xE3"=>[0,"\x91H"],"\xE4"=>[0,"\x91I"],"\xE5"=>[0,"\x91J"],"\xE6"=>[0,"\x91K"],"\xE7"=>[0,"\x91L"],"\xE8"=>[0,"\x91M"],"\xE9"=>[0,"\x91N"],"\xEA"=>[0,"\x91O"],"\xEB"=>[0,"\x91P"],"\xEC"=>[0,"\x91Q"],"\xED"=>[0,"\x91R"],"\xEE"=>[0,"\x91S"],"\xEF"=>[0,"\x91T"],"\xF0"=>[0,"\x91U"],"\xF1"=>[0,"\x91V"],"\xF2"=>[0,"\x91W"],"\xF3"=>[0,"\x91Y"],"\xF4"=>[0,"\x91j"],"\xF5"=>[0,"\x91k"],"\xF6"=>[0,"\x91q"],"\xF7"=>[0,"\x91v"],"\xF8"=>[0,"\x91w"],"\xF9"=>[0,"\x91x"],"\xFA"=>[0,"\x91y"],"\xFB"=>[0,"\x91z"],"\xFC"=>[82,"\x91"],"\xFD"=>[87,"\x91"],"\xFE"=>[130,"\x91"],"\xFF"=>[9,"\x91"],],["\x00"=>[77,"\x900"],"\x01"=>[18,"\x900"],"\x02"=>[77,"\x901"],"\x03"=>[18,"\x901"],"\x04"=>[77,"\x902"],"\x05"=>[18,"\x902"],"\x06"=>[77,"\x90a"],"\x07"=>[18,"\x90a"],"\x08"=>[77,"\x90c"],"\x09"=>[18,"\x90c"],"\x0A"=>[77,"\x90e"],"\x0B"=>[18,"\x90e"],"\x0C"=>[77,"\x90i"],"\x0D"=>[18,"\x90i"],"\x0E"=>[77,"\x90o"],"\x0F"=>[18,"\x90o"],"\x10"=>[77,"\x90s"],"\x11"=>[18,"\x90s"],"\x12"=>[77,"\x90t"],"\x13"=>[18,"\x90t"],"\x14"=>[0,"\x90\x20"],"\x15"=>[0,"\x90\x25"],"\x16"=>[0,"\x90-"],"\x17"=>[0,"\x90."],"\x18"=>[0,"\x90\x2F"],"\x19"=>[0,"\x903"],"\x1A"=>[0,"\x904"],"\x1B"=>[0,"\x905"],"\x1C"=>[0,"\x906"],"\x1D"=>[0,"\x907"],"\x1E"=>[0,"\x908"],"\x1F"=>[0,"\x909"],"\x20"=>[0,"\x90\x3D"],"\x21"=>[0,"\x90A"],"\x22"=>[0,"\x90_"],"\x23"=>[0,"\x90b"],"\x24"=>[0,"\x90d"],"\x25"=>[0,"\x90f"],"\x26"=>[0,"\x90g"],"\x27"=>[0,"\x90h"],"\x28"=>[0,"\x90l"],"\x29"=>[0,"\x90m"],"\x2A"=>[0,"\x90n"],"\x2B"=>[0,"\x90p"],"\x2C"=>[0,"\x90r"],"-"=>[0,"\x90u"],"."=>[100,"\x90"],"\x2F"=>[110,"\x90"],[111,"\x90"],[115,"\x90"],[116,"\x90"],[118,"\x90"],[119,"\x90"],[122,"\x90"],[123,"\x90"],[125,"\x90"],[126,"\x90"],[129,"\x90"],"\x3A"=>[143,"\x90"],"\x3B"=>[148,"\x90"],"\x3C"=>[151,"\x90"],"\x3D"=>[153,"\x90"],"\x3E"=>[83,"\x90"],"\x3F"=>[10,"\x90"],"\x40"=>[77,"\x910"],"A"=>[18,"\x910"],"B"=>[77,"\x911"],"C"=>[18,"\x911"],"D"=>[77,"\x912"],"E"=>[18,"\x912"],"F"=>[77,"\x91a"],"G"=>[18,"\x91a"],"H"=>[77,"\x91c"],"I"=>[18,"\x91c"],"J"=>[77,"\x91e"],"K"=>[18,"\x91e"],"L"=>[77,"\x91i"],"M"=>[18,"\x91i"],"N"=>[77,"\x91o"],"O"=>[18,"\x91o"],"P"=>[77,"\x91s"],"Q"=>[18,"\x91s"],"R"=>[77,"\x91t"],"S"=>[18,"\x91t"],"T"=>[0,"\x91\x20"],"U"=>[0,"\x91\x25"],"V"=>[0,"\x91-"],"W"=>[0,"\x91."],"X"=>[0,"\x91\x2F"],"Y"=>[0,"\x913"],"Z"=>[0,"\x914"],"\x5B"=>[0,"\x915"],"\x5C"=>[0,"\x916"],"\x5D"=>[0,"\x917"],"\x5E"=>[0,"\x918"],"_"=>[0,"\x919"],"\x60"=>[0,"\x91\x3D"],"a"=>[0,"\x91A"],"b"=>[0,"\x91_"],"c"=>[0,"\x91b"],"d"=>[0,"\x91d"],"e"=>[0,"\x91f"],"f"=>[0,"\x91g"],"g"=>[0,"\x91h"],"h"=>[0,"\x91l"],"i"=>[0,"\x91m"],"j"=>[0,"\x91n"],"k"=>[0,"\x91p"],"l"=>[0,"\x91r"],"m"=>[0,"\x91u"],"n"=>[100,"\x91"],"o"=>[110,"\x91"],"p"=>[111,"\x91"],"q"=>[115,"\x91"],"r"=>[116,"\x91"],"s"=>[118,"\x91"],"t"=>[119,"\x91"],"u"=>[122,"\x91"],"v"=>[123,"\x91"],"w"=>[125,"\x91"],"x"=>[126,"\x91"],"y"=>[129,"\x91"],"z"=>[143,"\x91"],"\x7B"=>[148,"\x91"],"\x7C"=>[151,"\x91"],"\x7D"=>[153,"\x91"],"~"=>[83,"\x91"],"\x7F"=>[10,"\x91"],"\x80"=>[77,"\x940"],"\x81"=>[18,"\x940"],"\x82"=>[77,"\x941"],"\x83"=>[18,"\x941"],"\x84"=>[77,"\x942"],"\x85"=>[18,"\x942"],"\x86"=>[77,"\x94a"],"\x87"=>[18,"\x94a"],"\x88"=>[77,"\x94c"],"\x89"=>[18,"\x94c"],"\x8A"=>[77,"\x94e"],"\x8B"=>[18,"\x94e"],"\x8C"=>[77,"\x94i"],"\x8D"=>[18,"\x94i"],"\x8E"=>[77,"\x94o"],"\x8F"=>[18,"\x94o"],"\x90"=>[77,"\x94s"],"\x91"=>[18,"\x94s"],"\x92"=>[77,"\x94t"],"\x93"=>[18,"\x94t"],"\x94"=>[0,"\x94\x20"],"\x95"=>[0,"\x94\x25"],"\x96"=>[0,"\x94-"],"\x97"=>[0,"\x94."],"\x98"=>[0,"\x94\x2F"],"\x99"=>[0,"\x943"],"\x9A"=>[0,"\x944"],"\x9B"=>[0,"\x945"],"\x9C"=>[0,"\x946"],"\x9D"=>[0,"\x947"],"\x9E"=>[0,"\x948"],"\x9F"=>[0,"\x949"],"\xA0"=>[0,"\x94\x3D"],"\xA1"=>[0,"\x94A"],"\xA2"=>[0,"\x94_"],"\xA3"=>[0,"\x94b"],"\xA4"=>[0,"\x94d"],"\xA5"=>[0,"\x94f"],"\xA6"=>[0,"\x94g"],"\xA7"=>[0,"\x94h"],"\xA8"=>[0,"\x94l"],"\xA9"=>[0,"\x94m"],"\xAA"=>[0,"\x94n"],"\xAB"=>[0,"\x94p"],"\xAC"=>[0,"\x94r"],"\xAD"=>[0,"\x94u"],"\xAE"=>[100,"\x94"],"\xAF"=>[110,"\x94"],"\xB0"=>[111,"\x94"],"\xB1"=>[115,"\x94"],"\xB2"=>[116,"\x94"],"\xB3"=>[118,"\x94"],"\xB4"=>[119,"\x94"],"\xB5"=>[122,"\x94"],"\xB6"=>[123,"\x94"],"\xB7"=>[125,"\x94"],"\xB8"=>[126,"\x94"],"\xB9"=>[129,"\x94"],"\xBA"=>[143,"\x94"],"\xBB"=>[148,"\x94"],"\xBC"=>[151,"\x94"],"\xBD"=>[153,"\x94"],"\xBE"=>[83,"\x94"],"\xBF"=>[10,"\x94"],"\xC0"=>[77,"\x9F0"],"\xC1"=>[18,"\x9F0"],"\xC2"=>[77,"\x9F1"],"\xC3"=>[18,"\x9F1"],"\xC4"=>[77,"\x9F2"],"\xC5"=>[18,"\x9F2"],"\xC6"=>[77,"\x9Fa"],"\xC7"=>[18,"\x9Fa"],"\xC8"=>[77,"\x9Fc"],"\xC9"=>[18,"\x9Fc"],"\xCA"=>[77,"\x9Fe"],"\xCB"=>[18,"\x9Fe"],"\xCC"=>[77,"\x9Fi"],"\xCD"=>[18,"\x9Fi"],"\xCE"=>[77,"\x9Fo"],"\xCF"=>[18,"\x9Fo"],"\xD0"=>[77,"\x9Fs"],"\xD1"=>[18,"\x9Fs"],"\xD2"=>[77,"\x9Ft"],"\xD3"=>[18,"\x9Ft"],"\xD4"=>[0,"\x9F\x20"],"\xD5"=>[0,"\x9F\x25"],"\xD6"=>[0,"\x9F-"],"\xD7"=>[0,"\x9F."],"\xD8"=>[0,"\x9F\x2F"],"\xD9"=>[0,"\x9F3"],"\xDA"=>[0,"\x9F4"],"\xDB"=>[0,"\x9F5"],"\xDC"=>[0,"\x9F6"],"\xDD"=>[0,"\x9F7"],"\xDE"=>[0,"\x9F8"],"\xDF"=>[0,"\x9F9"],"\xE0"=>[0,"\x9F\x3D"],"\xE1"=>[0,"\x9FA"],"\xE2"=>[0,"\x9F_"],"\xE3"=>[0,"\x9Fb"],"\xE4"=>[0,"\x9Fd"],"\xE5"=>[0,"\x9Ff"],"\xE6"=>[0,"\x9Fg"],"\xE7"=>[0,"\x9Fh"],"\xE8"=>[0,"\x9Fl"],"\xE9"=>[0,"\x9Fm"],"\xEA"=>[0,"\x9Fn"],"\xEB"=>[0,"\x9Fp"],"\xEC"=>[0,"\x9Fr"],"\xED"=>[0,"\x9Fu"],"\xEE"=>[100,"\x9F"],"\xEF"=>[110,"\x9F"],"\xF0"=>[111,"\x9F"],"\xF1"=>[115,"\x9F"],"\xF2"=>[116,"\x9F"],"\xF3"=>[118,"\x9F"],"\xF4"=>[119,"\x9F"],"\xF5"=>[122,"\x9F"],"\xF6"=>[123,"\x9F"],"\xF7"=>[125,"\x9F"],"\xF8"=>[126,"\x9F"],"\xF9"=>[129,"\x9F"],"\xFA"=>[143,"\x9F"],"\xFB"=>[148,"\x9F"],"\xFC"=>[151,"\x9F"],"\xFD"=>[153,"\x9F"],"\xFE"=>[83,"\x9F"],"\xFF"=>[10,"\x9F"],],["\x00"=>[94,"\x930"],"\x01"=>[76,"\x930"],"\x02"=>[104,"\x930"],"\x03"=>[16,"\x930"],"\x04"=>[94,"\x931"],"\x05"=>[76,"\x931"],"\x06"=>[104,"\x931"],"\x07"=>[16,"\x931"],"\x08"=>[94,"\x932"],"\x09"=>[76,"\x932"],"\x0A"=>[104,"\x932"],"\x0B"=>[16,"\x932"],"\x0C"=>[94,"\x93a"],"\x0D"=>[76,"\x93a"],"\x0E"=>[104,"\x93a"],"\x0F"=>[16,"\x93a"],"\x10"=>[94,"\x93c"],"\x11"=>[76,"\x93c"],"\x12"=>[104,"\x93c"],"\x13"=>[16,"\x93c"],"\x14"=>[94,"\x93e"],"\x15"=>[76,"\x93e"],"\x16"=>[104,"\x93e"],"\x17"=>[16,"\x93e"],"\x18"=>[94,"\x93i"],"\x19"=>[76,"\x93i"],"\x1A"=>[104,"\x93i"],"\x1B"=>[16,"\x93i"],"\x1C"=>[94,"\x93o"],"\x1D"=>[76,"\x93o"],"\x1E"=>[104,"\x93o"],"\x1F"=>[16,"\x93o"],"\x20"=>[94,"\x93s"],"\x21"=>[76,"\x93s"],"\x22"=>[104,"\x93s"],"\x23"=>[16,"\x93s"],"\x24"=>[94,"\x93t"],"\x25"=>[76,"\x93t"],"\x26"=>[104,"\x93t"],"\x27"=>[16,"\x93t"],"\x28"=>[77,"\x93\x20"],"\x29"=>[18,"\x93\x20"],"\x2A"=>[77,"\x93\x25"],"\x2B"=>[18,"\x93\x25"],"\x2C"=>[77,"\x93-"],"-"=>[18,"\x93-"],"."=>[77,"\x93."],"\x2F"=>[18,"\x93."],[77,"\x93\x2F"],[18,"\x93\x2F"],[77,"\x933"],[18,"\x933"],[77,"\x934"],[18,"\x934"],[77,"\x935"],[18,"\x935"],[77,"\x936"],[18,"\x936"],"\x3A"=>[77,"\x937"],"\x3B"=>[18,"\x937"],"\x3C"=>[77,"\x938"],"\x3D"=>[18,"\x938"],"\x3E"=>[77,"\x939"],"\x3F"=>[18,"\x939"],"\x40"=>[77,"\x93\x3D"],"A"=>[18,"\x93\x3D"],"B"=>[77,"\x93A"],"C"=>[18,"\x93A"],"D"=>[77,"\x93_"],"E"=>[18,"\x93_"],"F"=>[77,"\x93b"],"G"=>[18,"\x93b"],"H"=>[77,"\x93d"],"I"=>[18,"\x93d"],"J"=>[77,"\x93f"],"K"=>[18,"\x93f"],"L"=>[77,"\x93g"],"M"=>[18,"\x93g"],"N"=>[77,"\x93h"],"O"=>[18,"\x93h"],"P"=>[77,"\x93l"],"Q"=>[18,"\x93l"],"R"=>[77,"\x93m"],"S"=>[18,"\x93m"],"T"=>[77,"\x93n"],"U"=>[18,"\x93n"],"V"=>[77,"\x93p"],"W"=>[18,"\x93p"],"X"=>[77,"\x93r"],"Y"=>[18,"\x93r"],"Z"=>[77,"\x93u"],"\x5B"=>[18,"\x93u"],"\x5C"=>[0,"\x93\x3A"],"\x5D"=>[0,"\x93B"],"\x5E"=>[0,"\x93C"],"_"=>[0,"\x93D"],"\x60"=>[0,"\x93E"],"a"=>[0,"\x93F"],"b"=>[0,"\x93G"],"c"=>[0,"\x93H"],"d"=>[0,"\x93I"],"e"=>[0,"\x93J"],"f"=>[0,"\x93K"],"g"=>[0,"\x93L"],"h"=>[0,"\x93M"],"i"=>[0,"\x93N"],"j"=>[0,"\x93O"],"k"=>[0,"\x93P"],"l"=>[0,"\x93Q"],"m"=>[0,"\x93R"],"n"=>[0,"\x93S"],"o"=>[0,"\x93T"],"p"=>[0,"\x93U"],"q"=>[0,"\x93V"],"r"=>[0,"\x93W"],"s"=>[0,"\x93Y"],"t"=>[0,"\x93j"],"u"=>[0,"\x93k"],"v"=>[0,"\x93q"],"w"=>[0,"\x93v"],"x"=>[0,"\x93w"],"y"=>[0,"\x93x"],"z"=>[0,"\x93y"],"\x7B"=>[0,"\x93z"],"\x7C"=>[82,"\x93"],"\x7D"=>[87,"\x93"],"~"=>[130,"\x93"],"\x7F"=>[9,"\x93"],"\x80"=>[94,"\x950"],"\x81"=>[76,"\x950"],"\x82"=>[104,"\x950"],"\x83"=>[16,"\x950"],"\x84"=>[94,"\x951"],"\x85"=>[76,"\x951"],"\x86"=>[104,"\x951"],"\x87"=>[16,"\x951"],"\x88"=>[94,"\x952"],"\x89"=>[76,"\x952"],"\x8A"=>[104,"\x952"],"\x8B"=>[16,"\x952"],"\x8C"=>[94,"\x95a"],"\x8D"=>[76,"\x95a"],"\x8E"=>[104,"\x95a"],"\x8F"=>[16,"\x95a"],"\x90"=>[94,"\x95c"],"\x91"=>[76,"\x95c"],"\x92"=>[104,"\x95c"],"\x93"=>[16,"\x95c"],"\x94"=>[94,"\x95e"],"\x95"=>[76,"\x95e"],"\x96"=>[104,"\x95e"],"\x97"=>[16,"\x95e"],"\x98"=>[94,"\x95i"],"\x99"=>[76,"\x95i"],"\x9A"=>[104,"\x95i"],"\x9B"=>[16,"\x95i"],"\x9C"=>[94,"\x95o"],"\x9D"=>[76,"\x95o"],"\x9E"=>[104,"\x95o"],"\x9F"=>[16,"\x95o"],"\xA0"=>[94,"\x95s"],"\xA1"=>[76,"\x95s"],"\xA2"=>[104,"\x95s"],"\xA3"=>[16,"\x95s"],"\xA4"=>[94,"\x95t"],"\xA5"=>[76,"\x95t"],"\xA6"=>[104,"\x95t"],"\xA7"=>[16,"\x95t"],"\xA8"=>[77,"\x95\x20"],"\xA9"=>[18,"\x95\x20"],"\xAA"=>[77,"\x95\x25"],"\xAB"=>[18,"\x95\x25"],"\xAC"=>[77,"\x95-"],"\xAD"=>[18,"\x95-"],"\xAE"=>[77,"\x95."],"\xAF"=>[18,"\x95."],"\xB0"=>[77,"\x95\x2F"],"\xB1"=>[18,"\x95\x2F"],"\xB2"=>[77,"\x953"],"\xB3"=>[18,"\x953"],"\xB4"=>[77,"\x954"],"\xB5"=>[18,"\x954"],"\xB6"=>[77,"\x955"],"\xB7"=>[18,"\x955"],"\xB8"=>[77,"\x956"],"\xB9"=>[18,"\x956"],"\xBA"=>[77,"\x957"],"\xBB"=>[18,"\x957"],"\xBC"=>[77,"\x958"],"\xBD"=>[18,"\x958"],"\xBE"=>[77,"\x959"],"\xBF"=>[18,"\x959"],"\xC0"=>[77,"\x95\x3D"],"\xC1"=>[18,"\x95\x3D"],"\xC2"=>[77,"\x95A"],"\xC3"=>[18,"\x95A"],"\xC4"=>[77,"\x95_"],"\xC5"=>[18,"\x95_"],"\xC6"=>[77,"\x95b"],"\xC7"=>[18,"\x95b"],"\xC8"=>[77,"\x95d"],"\xC9"=>[18,"\x95d"],"\xCA"=>[77,"\x95f"],"\xCB"=>[18,"\x95f"],"\xCC"=>[77,"\x95g"],"\xCD"=>[18,"\x95g"],"\xCE"=>[77,"\x95h"],"\xCF"=>[18,"\x95h"],"\xD0"=>[77,"\x95l"],"\xD1"=>[18,"\x95l"],"\xD2"=>[77,"\x95m"],"\xD3"=>[18,"\x95m"],"\xD4"=>[77,"\x95n"],"\xD5"=>[18,"\x95n"],"\xD6"=>[77,"\x95p"],"\xD7"=>[18,"\x95p"],"\xD8"=>[77,"\x95r"],"\xD9"=>[18,"\x95r"],"\xDA"=>[77,"\x95u"],"\xDB"=>[18,"\x95u"],"\xDC"=>[0,"\x95\x3A"],"\xDD"=>[0,"\x95B"],"\xDE"=>[0,"\x95C"],"\xDF"=>[0,"\x95D"],"\xE0"=>[0,"\x95E"],"\xE1"=>[0,"\x95F"],"\xE2"=>[0,"\x95G"],"\xE3"=>[0,"\x95H"],"\xE4"=>[0,"\x95I"],"\xE5"=>[0,"\x95J"],"\xE6"=>[0,"\x95K"],"\xE7"=>[0,"\x95L"],"\xE8"=>[0,"\x95M"],"\xE9"=>[0,"\x95N"],"\xEA"=>[0,"\x95O"],"\xEB"=>[0,"\x95P"],"\xEC"=>[0,"\x95Q"],"\xED"=>[0,"\x95R"],"\xEE"=>[0,"\x95S"],"\xEF"=>[0,"\x95T"],"\xF0"=>[0,"\x95U"],"\xF1"=>[0,"\x95V"],"\xF2"=>[0,"\x95W"],"\xF3"=>[0,"\x95Y"],"\xF4"=>[0,"\x95j"],"\xF5"=>[0,"\x95k"],"\xF6"=>[0,"\x95q"],"\xF7"=>[0,"\x95v"],"\xF8"=>[0,"\x95w"],"\xF9"=>[0,"\x95x"],"\xFA"=>[0,"\x95y"],"\xFB"=>[0,"\x95z"],"\xFC"=>[82,"\x95"],"\xFD"=>[87,"\x95"],"\xFE"=>[130,"\x95"],"\xFF"=>[9,"\x95"],],["\x00"=>[77,"\x930"],"\x01"=>[18,"\x930"],"\x02"=>[77,"\x931"],"\x03"=>[18,"\x931"],"\x04"=>[77,"\x932"],"\x05"=>[18,"\x932"],"\x06"=>[77,"\x93a"],"\x07"=>[18,"\x93a"],"\x08"=>[77,"\x93c"],"\x09"=>[18,"\x93c"],"\x0A"=>[77,"\x93e"],"\x0B"=>[18,"\x93e"],"\x0C"=>[77,"\x93i"],"\x0D"=>[18,"\x93i"],"\x0E"=>[77,"\x93o"],"\x0F"=>[18,"\x93o"],"\x10"=>[77,"\x93s"],"\x11"=>[18,"\x93s"],"\x12"=>[77,"\x93t"],"\x13"=>[18,"\x93t"],"\x14"=>[0,"\x93\x20"],"\x15"=>[0,"\x93\x25"],"\x16"=>[0,"\x93-"],"\x17"=>[0,"\x93."],"\x18"=>[0,"\x93\x2F"],"\x19"=>[0,"\x933"],"\x1A"=>[0,"\x934"],"\x1B"=>[0,"\x935"],"\x1C"=>[0,"\x936"],"\x1D"=>[0,"\x937"],"\x1E"=>[0,"\x938"],"\x1F"=>[0,"\x939"],"\x20"=>[0,"\x93\x3D"],"\x21"=>[0,"\x93A"],"\x22"=>[0,"\x93_"],"\x23"=>[0,"\x93b"],"\x24"=>[0,"\x93d"],"\x25"=>[0,"\x93f"],"\x26"=>[0,"\x93g"],"\x27"=>[0,"\x93h"],"\x28"=>[0,"\x93l"],"\x29"=>[0,"\x93m"],"\x2A"=>[0,"\x93n"],"\x2B"=>[0,"\x93p"],"\x2C"=>[0,"\x93r"],"-"=>[0,"\x93u"],"."=>[100,"\x93"],"\x2F"=>[110,"\x93"],[111,"\x93"],[115,"\x93"],[116,"\x93"],[118,"\x93"],[119,"\x93"],[122,"\x93"],[123,"\x93"],[125,"\x93"],[126,"\x93"],[129,"\x93"],"\x3A"=>[143,"\x93"],"\x3B"=>[148,"\x93"],"\x3C"=>[151,"\x93"],"\x3D"=>[153,"\x93"],"\x3E"=>[83,"\x93"],"\x3F"=>[10,"\x93"],"\x40"=>[77,"\x950"],"A"=>[18,"\x950"],"B"=>[77,"\x951"],"C"=>[18,"\x951"],"D"=>[77,"\x952"],"E"=>[18,"\x952"],"F"=>[77,"\x95a"],"G"=>[18,"\x95a"],"H"=>[77,"\x95c"],"I"=>[18,"\x95c"],"J"=>[77,"\x95e"],"K"=>[18,"\x95e"],"L"=>[77,"\x95i"],"M"=>[18,"\x95i"],"N"=>[77,"\x95o"],"O"=>[18,"\x95o"],"P"=>[77,"\x95s"],"Q"=>[18,"\x95s"],"R"=>[77,"\x95t"],"S"=>[18,"\x95t"],"T"=>[0,"\x95\x20"],"U"=>[0,"\x95\x25"],"V"=>[0,"\x95-"],"W"=>[0,"\x95."],"X"=>[0,"\x95\x2F"],"Y"=>[0,"\x953"],"Z"=>[0,"\x954"],"\x5B"=>[0,"\x955"],"\x5C"=>[0,"\x956"],"\x5D"=>[0,"\x957"],"\x5E"=>[0,"\x958"],"_"=>[0,"\x959"],"\x60"=>[0,"\x95\x3D"],"a"=>[0,"\x95A"],"b"=>[0,"\x95_"],"c"=>[0,"\x95b"],"d"=>[0,"\x95d"],"e"=>[0,"\x95f"],"f"=>[0,"\x95g"],"g"=>[0,"\x95h"],"h"=>[0,"\x95l"],"i"=>[0,"\x95m"],"j"=>[0,"\x95n"],"k"=>[0,"\x95p"],"l"=>[0,"\x95r"],"m"=>[0,"\x95u"],"n"=>[100,"\x95"],"o"=>[110,"\x95"],"p"=>[111,"\x95"],"q"=>[115,"\x95"],"r"=>[116,"\x95"],"s"=>[118,"\x95"],"t"=>[119,"\x95"],"u"=>[122,"\x95"],"v"=>[123,"\x95"],"w"=>[125,"\x95"],"x"=>[126,"\x95"],"y"=>[129,"\x95"],"z"=>[143,"\x95"],"\x7B"=>[148,"\x95"],"\x7C"=>[151,"\x95"],"\x7D"=>[153,"\x95"],"~"=>[83,"\x95"],"\x7F"=>[10,"\x95"],"\x80"=>[77,"\x960"],"\x81"=>[18,"\x960"],"\x82"=>[77,"\x961"],"\x83"=>[18,"\x961"],"\x84"=>[77,"\x962"],"\x85"=>[18,"\x962"],"\x86"=>[77,"\x96a"],"\x87"=>[18,"\x96a"],"\x88"=>[77,"\x96c"],"\x89"=>[18,"\x96c"],"\x8A"=>[77,"\x96e"],"\x8B"=>[18,"\x96e"],"\x8C"=>[77,"\x96i"],"\x8D"=>[18,"\x96i"],"\x8E"=>[77,"\x96o"],"\x8F"=>[18,"\x96o"],"\x90"=>[77,"\x96s"],"\x91"=>[18,"\x96s"],"\x92"=>[77,"\x96t"],"\x93"=>[18,"\x96t"],"\x94"=>[0,"\x96\x20"],"\x95"=>[0,"\x96\x25"],"\x96"=>[0,"\x96-"],"\x97"=>[0,"\x96."],"\x98"=>[0,"\x96\x2F"],"\x99"=>[0,"\x963"],"\x9A"=>[0,"\x964"],"\x9B"=>[0,"\x965"],"\x9C"=>[0,"\x966"],"\x9D"=>[0,"\x967"],"\x9E"=>[0,"\x968"],"\x9F"=>[0,"\x969"],"\xA0"=>[0,"\x96\x3D"],"\xA1"=>[0,"\x96A"],"\xA2"=>[0,"\x96_"],"\xA3"=>[0,"\x96b"],"\xA4"=>[0,"\x96d"],"\xA5"=>[0,"\x96f"],"\xA6"=>[0,"\x96g"],"\xA7"=>[0,"\x96h"],"\xA8"=>[0,"\x96l"],"\xA9"=>[0,"\x96m"],"\xAA"=>[0,"\x96n"],"\xAB"=>[0,"\x96p"],"\xAC"=>[0,"\x96r"],"\xAD"=>[0,"\x96u"],"\xAE"=>[100,"\x96"],"\xAF"=>[110,"\x96"],"\xB0"=>[111,"\x96"],"\xB1"=>[115,"\x96"],"\xB2"=>[116,"\x96"],"\xB3"=>[118,"\x96"],"\xB4"=>[119,"\x96"],"\xB5"=>[122,"\x96"],"\xB6"=>[123,"\x96"],"\xB7"=>[125,"\x96"],"\xB8"=>[126,"\x96"],"\xB9"=>[129,"\x96"],"\xBA"=>[143,"\x96"],"\xBB"=>[148,"\x96"],"\xBC"=>[151,"\x96"],"\xBD"=>[153,"\x96"],"\xBE"=>[83,"\x96"],"\xBF"=>[10,"\x96"],"\xC0"=>[77,"\x970"],"\xC1"=>[18,"\x970"],"\xC2"=>[77,"\x971"],"\xC3"=>[18,"\x971"],"\xC4"=>[77,"\x972"],"\xC5"=>[18,"\x972"],"\xC6"=>[77,"\x97a"],"\xC7"=>[18,"\x97a"],"\xC8"=>[77,"\x97c"],"\xC9"=>[18,"\x97c"],"\xCA"=>[77,"\x97e"],"\xCB"=>[18,"\x97e"],"\xCC"=>[77,"\x97i"],"\xCD"=>[18,"\x97i"],"\xCE"=>[77,"\x97o"],"\xCF"=>[18,"\x97o"],"\xD0"=>[77,"\x97s"],"\xD1"=>[18,"\x97s"],"\xD2"=>[77,"\x97t"],"\xD3"=>[18,"\x97t"],"\xD4"=>[0,"\x97\x20"],"\xD5"=>[0,"\x97\x25"],"\xD6"=>[0,"\x97-"],"\xD7"=>[0,"\x97."],"\xD8"=>[0,"\x97\x2F"],"\xD9"=>[0,"\x973"],"\xDA"=>[0,"\x974"],"\xDB"=>[0,"\x975"],"\xDC"=>[0,"\x976"],"\xDD"=>[0,"\x977"],"\xDE"=>[0,"\x978"],"\xDF"=>[0,"\x979"],"\xE0"=>[0,"\x97\x3D"],"\xE1"=>[0,"\x97A"],"\xE2"=>[0,"\x97_"],"\xE3"=>[0,"\x97b"],"\xE4"=>[0,"\x97d"],"\xE5"=>[0,"\x97f"],"\xE6"=>[0,"\x97g"],"\xE7"=>[0,"\x97h"],"\xE8"=>[0,"\x97l"],"\xE9"=>[0,"\x97m"],"\xEA"=>[0,"\x97n"],"\xEB"=>[0,"\x97p"],"\xEC"=>[0,"\x97r"],"\xED"=>[0,"\x97u"],"\xEE"=>[100,"\x97"],"\xEF"=>[110,"\x97"],"\xF0"=>[111,"\x97"],"\xF1"=>[115,"\x97"],"\xF2"=>[116,"\x97"],"\xF3"=>[118,"\x97"],"\xF4"=>[119,"\x97"],"\xF5"=>[122,"\x97"],"\xF6"=>[123,"\x97"],"\xF7"=>[125,"\x97"],"\xF8"=>[126,"\x97"],"\xF9"=>[129,"\x97"],"\xFA"=>[143,"\x97"],"\xFB"=>[148,"\x97"],"\xFC"=>[151,"\x97"],"\xFD"=>[153,"\x97"],"\xFE"=>[83,"\x97"],"\xFF"=>[10,"\x97"],],["\x00"=>[0,"\x930"],"\x01"=>[0,"\x931"],"\x02"=>[0,"\x932"],"\x03"=>[0,"\x93a"],"\x04"=>[0,"\x93c"],"\x05"=>[0,"\x93e"],"\x06"=>[0,"\x93i"],"\x07"=>[0,"\x93o"],"\x08"=>[0,"\x93s"],"\x09"=>[0,"\x93t"],"\x0A"=>[73,"\x93"],"\x0B"=>[88,"\x93"],"\x0C"=>[89,"\x93"],"\x0D"=>[96,"\x93"],"\x0E"=>[97,"\x93"],"\x0F"=>[99,"\x93"],"\x10"=>[106,"\x93"],"\x11"=>[136,"\x93"],"\x12"=>[139,"\x93"],"\x13"=>[141,"\x93"],"\x14"=>[145,"\x93"],"\x15"=>[147,"\x93"],"\x16"=>[149,"\x93"],"\x17"=>[101,"\x93"],"\x18"=>[112,"\x93"],"\x19"=>[117,"\x93"],"\x1A"=>[120,"\x93"],"\x1B"=>[124,"\x93"],"\x1C"=>[127,"\x93"],"\x1D"=>[144,"\x93"],"\x1E"=>[152,"\x93"],"\x1F"=>[11,"\x93"],"\x20"=>[0,"\x950"],"\x21"=>[0,"\x951"],"\x22"=>[0,"\x952"],"\x23"=>[0,"\x95a"],"\x24"=>[0,"\x95c"],"\x25"=>[0,"\x95e"],"\x26"=>[0,"\x95i"],"\x27"=>[0,"\x95o"],"\x28"=>[0,"\x95s"],"\x29"=>[0,"\x95t"],"\x2A"=>[73,"\x95"],"\x2B"=>[88,"\x95"],"\x2C"=>[89,"\x95"],"-"=>[96,"\x95"],"."=>[97,"\x95"],"\x2F"=>[99,"\x95"],[106,"\x95"],[136,"\x95"],[139,"\x95"],[141,"\x95"],[145,"\x95"],[147,"\x95"],[149,"\x95"],[101,"\x95"],[112,"\x95"],[117,"\x95"],"\x3A"=>[120,"\x95"],"\x3B"=>[124,"\x95"],"\x3C"=>[127,"\x95"],"\x3D"=>[144,"\x95"],"\x3E"=>[152,"\x95"],"\x3F"=>[11,"\x95"],"\x40"=>[0,"\x960"],"A"=>[0,"\x961"],"B"=>[0,"\x962"],"C"=>[0,"\x96a"],"D"=>[0,"\x96c"],"E"=>[0,"\x96e"],"F"=>[0,"\x96i"],"G"=>[0,"\x96o"],"H"=>[0,"\x96s"],"I"=>[0,"\x96t"],"J"=>[73,"\x96"],"K"=>[88,"\x96"],"L"=>[89,"\x96"],"M"=>[96,"\x96"],"N"=>[97,"\x96"],"O"=>[99,"\x96"],"P"=>[106,"\x96"],"Q"=>[136,"\x96"],"R"=>[139,"\x96"],"S"=>[141,"\x96"],"T"=>[145,"\x96"],"U"=>[147,"\x96"],"V"=>[149,"\x96"],"W"=>[101,"\x96"],"X"=>[112,"\x96"],"Y"=>[117,"\x96"],"Z"=>[120,"\x96"],"\x5B"=>[124,"\x96"],"\x5C"=>[127,"\x96"],"\x5D"=>[144,"\x96"],"\x5E"=>[152,"\x96"],"_"=>[11,"\x96"],"\x60"=>[0,"\x970"],"a"=>[0,"\x971"],"b"=>[0,"\x972"],"c"=>[0,"\x97a"],"d"=>[0,"\x97c"],"e"=>[0,"\x97e"],"f"=>[0,"\x97i"],"g"=>[0,"\x97o"],"h"=>[0,"\x97s"],"i"=>[0,"\x97t"],"j"=>[73,"\x97"],"k"=>[88,"\x97"],"l"=>[89,"\x97"],"m"=>[96,"\x97"],"n"=>[97,"\x97"],"o"=>[99,"\x97"],"p"=>[106,"\x97"],"q"=>[136,"\x97"],"r"=>[139,"\x97"],"s"=>[141,"\x97"],"t"=>[145,"\x97"],"u"=>[147,"\x97"],"v"=>[149,"\x97"],"w"=>[101,"\x97"],"x"=>[112,"\x97"],"y"=>[117,"\x97"],"z"=>[120,"\x97"],"\x7B"=>[124,"\x97"],"\x7C"=>[127,"\x97"],"\x7D"=>[144,"\x97"],"~"=>[152,"\x97"],"\x7F"=>[11,"\x97"],"\x80"=>[0,"\x980"],"\x81"=>[0,"\x981"],"\x82"=>[0,"\x982"],"\x83"=>[0,"\x98a"],"\x84"=>[0,"\x98c"],"\x85"=>[0,"\x98e"],"\x86"=>[0,"\x98i"],"\x87"=>[0,"\x98o"],"\x88"=>[0,"\x98s"],"\x89"=>[0,"\x98t"],"\x8A"=>[73,"\x98"],"\x8B"=>[88,"\x98"],"\x8C"=>[89,"\x98"],"\x8D"=>[96,"\x98"],"\x8E"=>[97,"\x98"],"\x8F"=>[99,"\x98"],"\x90"=>[106,"\x98"],"\x91"=>[136,"\x98"],"\x92"=>[139,"\x98"],"\x93"=>[141,"\x98"],"\x94"=>[145,"\x98"],"\x95"=>[147,"\x98"],"\x96"=>[149,"\x98"],"\x97"=>[101,"\x98"],"\x98"=>[112,"\x98"],"\x99"=>[117,"\x98"],"\x9A"=>[120,"\x98"],"\x9B"=>[124,"\x98"],"\x9C"=>[127,"\x98"],"\x9D"=>[144,"\x98"],"\x9E"=>[152,"\x98"],"\x9F"=>[11,"\x98"],"\xA0"=>[0,"\x9B0"],"\xA1"=>[0,"\x9B1"],"\xA2"=>[0,"\x9B2"],"\xA3"=>[0,"\x9Ba"],"\xA4"=>[0,"\x9Bc"],"\xA5"=>[0,"\x9Be"],"\xA6"=>[0,"\x9Bi"],"\xA7"=>[0,"\x9Bo"],"\xA8"=>[0,"\x9Bs"],"\xA9"=>[0,"\x9Bt"],"\xAA"=>[73,"\x9B"],"\xAB"=>[88,"\x9B"],"\xAC"=>[89,"\x9B"],"\xAD"=>[96,"\x9B"],"\xAE"=>[97,"\x9B"],"\xAF"=>[99,"\x9B"],"\xB0"=>[106,"\x9B"],"\xB1"=>[136,"\x9B"],"\xB2"=>[139,"\x9B"],"\xB3"=>[141,"\x9B"],"\xB4"=>[145,"\x9B"],"\xB5"=>[147,"\x9B"],"\xB6"=>[149,"\x9B"],"\xB7"=>[101,"\x9B"],"\xB8"=>[112,"\x9B"],"\xB9"=>[117,"\x9B"],"\xBA"=>[120,"\x9B"],"\xBB"=>[124,"\x9B"],"\xBC"=>[127,"\x9B"],"\xBD"=>[144,"\x9B"],"\xBE"=>[152,"\x9B"],"\xBF"=>[11,"\x9B"],"\xC0"=>[0,"\x9D0"],"\xC1"=>[0,"\x9D1"],"\xC2"=>[0,"\x9D2"],"\xC3"=>[0,"\x9Da"],"\xC4"=>[0,"\x9Dc"],"\xC5"=>[0,"\x9De"],"\xC6"=>[0,"\x9Di"],"\xC7"=>[0,"\x9Do"],"\xC8"=>[0,"\x9Ds"],"\xC9"=>[0,"\x9Dt"],"\xCA"=>[73,"\x9D"],"\xCB"=>[88,"\x9D"],"\xCC"=>[89,"\x9D"],"\xCD"=>[96,"\x9D"],"\xCE"=>[97,"\x9D"],"\xCF"=>[99,"\x9D"],"\xD0"=>[106,"\x9D"],"\xD1"=>[136,"\x9D"],"\xD2"=>[139,"\x9D"],"\xD3"=>[141,"\x9D"],"\xD4"=>[145,"\x9D"],"\xD5"=>[147,"\x9D"],"\xD6"=>[149,"\x9D"],"\xD7"=>[101,"\x9D"],"\xD8"=>[112,"\x9D"],"\xD9"=>[117,"\x9D"],"\xDA"=>[120,"\x9D"],"\xDB"=>[124,"\x9D"],"\xDC"=>[127,"\x9D"],"\xDD"=>[144,"\x9D"],"\xDE"=>[152,"\x9D"],"\xDF"=>[11,"\x9D"],"\xE0"=>[0,"\x9E0"],"\xE1"=>[0,"\x9E1"],"\xE2"=>[0,"\x9E2"],"\xE3"=>[0,"\x9Ea"],"\xE4"=>[0,"\x9Ec"],"\xE5"=>[0,"\x9Ee"],"\xE6"=>[0,"\x9Ei"],"\xE7"=>[0,"\x9Eo"],"\xE8"=>[0,"\x9Es"],"\xE9"=>[0,"\x9Et"],"\xEA"=>[73,"\x9E"],"\xEB"=>[88,"\x9E"],"\xEC"=>[89,"\x9E"],"\xED"=>[96,"\x9E"],"\xEE"=>[97,"\x9E"],"\xEF"=>[99,"\x9E"],"\xF0"=>[106,"\x9E"],"\xF1"=>[136,"\x9E"],"\xF2"=>[139,"\x9E"],"\xF3"=>[141,"\x9E"],"\xF4"=>[145,"\x9E"],"\xF5"=>[147,"\x9E"],"\xF6"=>[149,"\x9E"],"\xF7"=>[101,"\x9E"],"\xF8"=>[112,"\x9E"],"\xF9"=>[117,"\x9E"],"\xFA"=>[120,"\x9E"],"\xFB"=>[124,"\x9E"],"\xFC"=>[127,"\x9E"],"\xFD"=>[144,"\x9E"],"\xFE"=>[152,"\x9E"],"\xFF"=>[11,"\x9E"],],["\x00"=>[92,"\x93"],"\x01"=>[95,"\x93"],"\x02"=>[137,"\x93"],"\x03"=>[142,"\x93"],"\x04"=>[150,"\x93"],"\x05"=>[74,"\x93"],"\x06"=>[90,"\x93"],"\x07"=>[98,"\x93"],"\x08"=>[107,"\x93"],"\x09"=>[140,"\x93"],"\x0A"=>[146,"\x93"],"\x0B"=>[102,"\x93"],"\x0C"=>[113,"\x93"],"\x0D"=>[121,"\x93"],"\x0E"=>[128,"\x93"],"\x0F"=>[12,"\x93"],"\x10"=>[92,"\x95"],"\x11"=>[95,"\x95"],"\x12"=>[137,"\x95"],"\x13"=>[142,"\x95"],"\x14"=>[150,"\x95"],"\x15"=>[74,"\x95"],"\x16"=>[90,"\x95"],"\x17"=>[98,"\x95"],"\x18"=>[107,"\x95"],"\x19"=>[140,"\x95"],"\x1A"=>[146,"\x95"],"\x1B"=>[102,"\x95"],"\x1C"=>[113,"\x95"],"\x1D"=>[121,"\x95"],"\x1E"=>[128,"\x95"],"\x1F"=>[12,"\x95"],"\x20"=>[92,"\x96"],"\x21"=>[95,"\x96"],"\x22"=>[137,"\x96"],"\x23"=>[142,"\x96"],"\x24"=>[150,"\x96"],"\x25"=>[74,"\x96"],"\x26"=>[90,"\x96"],"\x27"=>[98,"\x96"],"\x28"=>[107,"\x96"],"\x29"=>[140,"\x96"],"\x2A"=>[146,"\x96"],"\x2B"=>[102,"\x96"],"\x2C"=>[113,"\x96"],"-"=>[121,"\x96"],"."=>[128,"\x96"],"\x2F"=>[12,"\x96"],[92,"\x97"],[95,"\x97"],[137,"\x97"],[142,"\x97"],[150,"\x97"],[74,"\x97"],[90,"\x97"],[98,"\x97"],[107,"\x97"],[140,"\x97"],"\x3A"=>[146,"\x97"],"\x3B"=>[102,"\x97"],"\x3C"=>[113,"\x97"],"\x3D"=>[121,"\x97"],"\x3E"=>[128,"\x97"],"\x3F"=>[12,"\x97"],"\x40"=>[92,"\x98"],"A"=>[95,"\x98"],"B"=>[137,"\x98"],"C"=>[142,"\x98"],"D"=>[150,"\x98"],"E"=>[74,"\x98"],"F"=>[90,"\x98"],"G"=>[98,"\x98"],"H"=>[107,"\x98"],"I"=>[140,"\x98"],"J"=>[146,"\x98"],"K"=>[102,"\x98"],"L"=>[113,"\x98"],"M"=>[121,"\x98"],"N"=>[128,"\x98"],"O"=>[12,"\x98"],"P"=>[92,"\x9B"],"Q"=>[95,"\x9B"],"R"=>[137,"\x9B"],"S"=>[142,"\x9B"],"T"=>[150,"\x9B"],"U"=>[74,"\x9B"],"V"=>[90,"\x9B"],"W"=>[98,"\x9B"],"X"=>[107,"\x9B"],"Y"=>[140,"\x9B"],"Z"=>[146,"\x9B"],"\x5B"=>[102,"\x9B"],"\x5C"=>[113,"\x9B"],"\x5D"=>[121,"\x9B"],"\x5E"=>[128,"\x9B"],"_"=>[12,"\x9B"],"\x60"=>[92,"\x9D"],"a"=>[95,"\x9D"],"b"=>[137,"\x9D"],"c"=>[142,"\x9D"],"d"=>[150,"\x9D"],"e"=>[74,"\x9D"],"f"=>[90,"\x9D"],"g"=>[98,"\x9D"],"h"=>[107,"\x9D"],"i"=>[140,"\x9D"],"j"=>[146,"\x9D"],"k"=>[102,"\x9D"],"l"=>[113,"\x9D"],"m"=>[121,"\x9D"],"n"=>[128,"\x9D"],"o"=>[12,"\x9D"],"p"=>[92,"\x9E"],"q"=>[95,"\x9E"],"r"=>[137,"\x9E"],"s"=>[142,"\x9E"],"t"=>[150,"\x9E"],"u"=>[74,"\x9E"],"v"=>[90,"\x9E"],"w"=>[98,"\x9E"],"x"=>[107,"\x9E"],"y"=>[140,"\x9E"],"z"=>[146,"\x9E"],"\x7B"=>[102,"\x9E"],"\x7C"=>[113,"\x9E"],"\x7D"=>[121,"\x9E"],"~"=>[128,"\x9E"],"\x7F"=>[12,"\x9E"],"\x80"=>[92,"\xA5"],"\x81"=>[95,"\xA5"],"\x82"=>[137,"\xA5"],"\x83"=>[142,"\xA5"],"\x84"=>[150,"\xA5"],"\x85"=>[74,"\xA5"],"\x86"=>[90,"\xA5"],"\x87"=>[98,"\xA5"],"\x88"=>[107,"\xA5"],"\x89"=>[140,"\xA5"],"\x8A"=>[146,"\xA5"],"\x8B"=>[102,"\xA5"],"\x8C"=>[113,"\xA5"],"\x8D"=>[121,"\xA5"],"\x8E"=>[128,"\xA5"],"\x8F"=>[12,"\xA5"],"\x90"=>[92,"\xA6"],"\x91"=>[95,"\xA6"],"\x92"=>[137,"\xA6"],"\x93"=>[142,"\xA6"],"\x94"=>[150,"\xA6"],"\x95"=>[74,"\xA6"],"\x96"=>[90,"\xA6"],"\x97"=>[98,"\xA6"],"\x98"=>[107,"\xA6"],"\x99"=>[140,"\xA6"],"\x9A"=>[146,"\xA6"],"\x9B"=>[102,"\xA6"],"\x9C"=>[113,"\xA6"],"\x9D"=>[121,"\xA6"],"\x9E"=>[128,"\xA6"],"\x9F"=>[12,"\xA6"],"\xA0"=>[92,"\xA8"],"\xA1"=>[95,"\xA8"],"\xA2"=>[137,"\xA8"],"\xA3"=>[142,"\xA8"],"\xA4"=>[150,"\xA8"],"\xA5"=>[74,"\xA8"],"\xA6"=>[90,"\xA8"],"\xA7"=>[98,"\xA8"],"\xA8"=>[107,"\xA8"],"\xA9"=>[140,"\xA8"],"\xAA"=>[146,"\xA8"],"\xAB"=>[102,"\xA8"],"\xAC"=>[113,"\xA8"],"\xAD"=>[121,"\xA8"],"\xAE"=>[128,"\xA8"],"\xAF"=>[12,"\xA8"],"\xB0"=>[92,"\xAE"],"\xB1"=>[95,"\xAE"],"\xB2"=>[137,"\xAE"],"\xB3"=>[142,"\xAE"],"\xB4"=>[150,"\xAE"],"\xB5"=>[74,"\xAE"],"\xB6"=>[90,"\xAE"],"\xB7"=>[98,"\xAE"],"\xB8"=>[107,"\xAE"],"\xB9"=>[140,"\xAE"],"\xBA"=>[146,"\xAE"],"\xBB"=>[102,"\xAE"],"\xBC"=>[113,"\xAE"],"\xBD"=>[121,"\xAE"],"\xBE"=>[128,"\xAE"],"\xBF"=>[12,"\xAE"],"\xC0"=>[92,"\xAF"],"\xC1"=>[95,"\xAF"],"\xC2"=>[137,"\xAF"],"\xC3"=>[142,"\xAF"],"\xC4"=>[150,"\xAF"],"\xC5"=>[74,"\xAF"],"\xC6"=>[90,"\xAF"],"\xC7"=>[98,"\xAF"],"\xC8"=>[107,"\xAF"],"\xC9"=>[140,"\xAF"],"\xCA"=>[146,"\xAF"],"\xCB"=>[102,"\xAF"],"\xCC"=>[113,"\xAF"],"\xCD"=>[121,"\xAF"],"\xCE"=>[128,"\xAF"],"\xCF"=>[12,"\xAF"],"\xD0"=>[92,"\xB4"],"\xD1"=>[95,"\xB4"],"\xD2"=>[137,"\xB4"],"\xD3"=>[142,"\xB4"],"\xD4"=>[150,"\xB4"],"\xD5"=>[74,"\xB4"],"\xD6"=>[90,"\xB4"],"\xD7"=>[98,"\xB4"],"\xD8"=>[107,"\xB4"],"\xD9"=>[140,"\xB4"],"\xDA"=>[146,"\xB4"],"\xDB"=>[102,"\xB4"],"\xDC"=>[113,"\xB4"],"\xDD"=>[121,"\xB4"],"\xDE"=>[128,"\xB4"],"\xDF"=>[12,"\xB4"],"\xE0"=>[92,"\xB6"],"\xE1"=>[95,"\xB6"],"\xE2"=>[137,"\xB6"],"\xE3"=>[142,"\xB6"],"\xE4"=>[150,"\xB6"],"\xE5"=>[74,"\xB6"],"\xE6"=>[90,"\xB6"],"\xE7"=>[98,"\xB6"],"\xE8"=>[107,"\xB6"],"\xE9"=>[140,"\xB6"],"\xEA"=>[146,"\xB6"],"\xEB"=>[102,"\xB6"],"\xEC"=>[113,"\xB6"],"\xED"=>[121,"\xB6"],"\xEE"=>[128,"\xB6"],"\xEF"=>[12,"\xB6"],"\xF0"=>[92,"\xB7"],"\xF1"=>[95,"\xB7"],"\xF2"=>[137,"\xB7"],"\xF3"=>[142,"\xB7"],"\xF4"=>[150,"\xB7"],"\xF5"=>[74,"\xB7"],"\xF6"=>[90,"\xB7"],"\xF7"=>[98,"\xB7"],"\xF8"=>[107,"\xB7"],"\xF9"=>[140,"\xB7"],"\xFA"=>[146,"\xB7"],"\xFB"=>[102,"\xB7"],"\xFC"=>[113,"\xB7"],"\xFD"=>[121,"\xB7"],"\xFE"=>[128,"\xB7"],"\xFF"=>[12,"\xB7"],],["\x00"=>[94,"\x940"],"\x01"=>[76,"\x940"],"\x02"=>[104,"\x940"],"\x03"=>[16,"\x940"],"\x04"=>[94,"\x941"],"\x05"=>[76,"\x941"],"\x06"=>[104,"\x941"],"\x07"=>[16,"\x941"],"\x08"=>[94,"\x942"],"\x09"=>[76,"\x942"],"\x0A"=>[104,"\x942"],"\x0B"=>[16,"\x942"],"\x0C"=>[94,"\x94a"],"\x0D"=>[76,"\x94a"],"\x0E"=>[104,"\x94a"],"\x0F"=>[16,"\x94a"],"\x10"=>[94,"\x94c"],"\x11"=>[76,"\x94c"],"\x12"=>[104,"\x94c"],"\x13"=>[16,"\x94c"],"\x14"=>[94,"\x94e"],"\x15"=>[76,"\x94e"],"\x16"=>[104,"\x94e"],"\x17"=>[16,"\x94e"],"\x18"=>[94,"\x94i"],"\x19"=>[76,"\x94i"],"\x1A"=>[104,"\x94i"],"\x1B"=>[16,"\x94i"],"\x1C"=>[94,"\x94o"],"\x1D"=>[76,"\x94o"],"\x1E"=>[104,"\x94o"],"\x1F"=>[16,"\x94o"],"\x20"=>[94,"\x94s"],"\x21"=>[76,"\x94s"],"\x22"=>[104,"\x94s"],"\x23"=>[16,"\x94s"],"\x24"=>[94,"\x94t"],"\x25"=>[76,"\x94t"],"\x26"=>[104,"\x94t"],"\x27"=>[16,"\x94t"],"\x28"=>[77,"\x94\x20"],"\x29"=>[18,"\x94\x20"],"\x2A"=>[77,"\x94\x25"],"\x2B"=>[18,"\x94\x25"],"\x2C"=>[77,"\x94-"],"-"=>[18,"\x94-"],"."=>[77,"\x94."],"\x2F"=>[18,"\x94."],[77,"\x94\x2F"],[18,"\x94\x2F"],[77,"\x943"],[18,"\x943"],[77,"\x944"],[18,"\x944"],[77,"\x945"],[18,"\x945"],[77,"\x946"],[18,"\x946"],"\x3A"=>[77,"\x947"],"\x3B"=>[18,"\x947"],"\x3C"=>[77,"\x948"],"\x3D"=>[18,"\x948"],"\x3E"=>[77,"\x949"],"\x3F"=>[18,"\x949"],"\x40"=>[77,"\x94\x3D"],"A"=>[18,"\x94\x3D"],"B"=>[77,"\x94A"],"C"=>[18,"\x94A"],"D"=>[77,"\x94_"],"E"=>[18,"\x94_"],"F"=>[77,"\x94b"],"G"=>[18,"\x94b"],"H"=>[77,"\x94d"],"I"=>[18,"\x94d"],"J"=>[77,"\x94f"],"K"=>[18,"\x94f"],"L"=>[77,"\x94g"],"M"=>[18,"\x94g"],"N"=>[77,"\x94h"],"O"=>[18,"\x94h"],"P"=>[77,"\x94l"],"Q"=>[18,"\x94l"],"R"=>[77,"\x94m"],"S"=>[18,"\x94m"],"T"=>[77,"\x94n"],"U"=>[18,"\x94n"],"V"=>[77,"\x94p"],"W"=>[18,"\x94p"],"X"=>[77,"\x94r"],"Y"=>[18,"\x94r"],"Z"=>[77,"\x94u"],"\x5B"=>[18,"\x94u"],"\x5C"=>[0,"\x94\x3A"],"\x5D"=>[0,"\x94B"],"\x5E"=>[0,"\x94C"],"_"=>[0,"\x94D"],"\x60"=>[0,"\x94E"],"a"=>[0,"\x94F"],"b"=>[0,"\x94G"],"c"=>[0,"\x94H"],"d"=>[0,"\x94I"],"e"=>[0,"\x94J"],"f"=>[0,"\x94K"],"g"=>[0,"\x94L"],"h"=>[0,"\x94M"],"i"=>[0,"\x94N"],"j"=>[0,"\x94O"],"k"=>[0,"\x94P"],"l"=>[0,"\x94Q"],"m"=>[0,"\x94R"],"n"=>[0,"\x94S"],"o"=>[0,"\x94T"],"p"=>[0,"\x94U"],"q"=>[0,"\x94V"],"r"=>[0,"\x94W"],"s"=>[0,"\x94Y"],"t"=>[0,"\x94j"],"u"=>[0,"\x94k"],"v"=>[0,"\x94q"],"w"=>[0,"\x94v"],"x"=>[0,"\x94w"],"y"=>[0,"\x94x"],"z"=>[0,"\x94y"],"\x7B"=>[0,"\x94z"],"\x7C"=>[82,"\x94"],"\x7D"=>[87,"\x94"],"~"=>[130,"\x94"],"\x7F"=>[9,"\x94"],"\x80"=>[94,"\x9F0"],"\x81"=>[76,"\x9F0"],"\x82"=>[104,"\x9F0"],"\x83"=>[16,"\x9F0"],"\x84"=>[94,"\x9F1"],"\x85"=>[76,"\x9F1"],"\x86"=>[104,"\x9F1"],"\x87"=>[16,"\x9F1"],"\x88"=>[94,"\x9F2"],"\x89"=>[76,"\x9F2"],"\x8A"=>[104,"\x9F2"],"\x8B"=>[16,"\x9F2"],"\x8C"=>[94,"\x9Fa"],"\x8D"=>[76,"\x9Fa"],"\x8E"=>[104,"\x9Fa"],"\x8F"=>[16,"\x9Fa"],"\x90"=>[94,"\x9Fc"],"\x91"=>[76,"\x9Fc"],"\x92"=>[104,"\x9Fc"],"\x93"=>[16,"\x9Fc"],"\x94"=>[94,"\x9Fe"],"\x95"=>[76,"\x9Fe"],"\x96"=>[104,"\x9Fe"],"\x97"=>[16,"\x9Fe"],"\x98"=>[94,"\x9Fi"],"\x99"=>[76,"\x9Fi"],"\x9A"=>[104,"\x9Fi"],"\x9B"=>[16,"\x9Fi"],"\x9C"=>[94,"\x9Fo"],"\x9D"=>[76,"\x9Fo"],"\x9E"=>[104,"\x9Fo"],"\x9F"=>[16,"\x9Fo"],"\xA0"=>[94,"\x9Fs"],"\xA1"=>[76,"\x9Fs"],"\xA2"=>[104,"\x9Fs"],"\xA3"=>[16,"\x9Fs"],"\xA4"=>[94,"\x9Ft"],"\xA5"=>[76,"\x9Ft"],"\xA6"=>[104,"\x9Ft"],"\xA7"=>[16,"\x9Ft"],"\xA8"=>[77,"\x9F\x20"],"\xA9"=>[18,"\x9F\x20"],"\xAA"=>[77,"\x9F\x25"],"\xAB"=>[18,"\x9F\x25"],"\xAC"=>[77,"\x9F-"],"\xAD"=>[18,"\x9F-"],"\xAE"=>[77,"\x9F."],"\xAF"=>[18,"\x9F."],"\xB0"=>[77,"\x9F\x2F"],"\xB1"=>[18,"\x9F\x2F"],"\xB2"=>[77,"\x9F3"],"\xB3"=>[18,"\x9F3"],"\xB4"=>[77,"\x9F4"],"\xB5"=>[18,"\x9F4"],"\xB6"=>[77,"\x9F5"],"\xB7"=>[18,"\x9F5"],"\xB8"=>[77,"\x9F6"],"\xB9"=>[18,"\x9F6"],"\xBA"=>[77,"\x9F7"],"\xBB"=>[18,"\x9F7"],"\xBC"=>[77,"\x9F8"],"\xBD"=>[18,"\x9F8"],"\xBE"=>[77,"\x9F9"],"\xBF"=>[18,"\x9F9"],"\xC0"=>[77,"\x9F\x3D"],"\xC1"=>[18,"\x9F\x3D"],"\xC2"=>[77,"\x9FA"],"\xC3"=>[18,"\x9FA"],"\xC4"=>[77,"\x9F_"],"\xC5"=>[18,"\x9F_"],"\xC6"=>[77,"\x9Fb"],"\xC7"=>[18,"\x9Fb"],"\xC8"=>[77,"\x9Fd"],"\xC9"=>[18,"\x9Fd"],"\xCA"=>[77,"\x9Ff"],"\xCB"=>[18,"\x9Ff"],"\xCC"=>[77,"\x9Fg"],"\xCD"=>[18,"\x9Fg"],"\xCE"=>[77,"\x9Fh"],"\xCF"=>[18,"\x9Fh"],"\xD0"=>[77,"\x9Fl"],"\xD1"=>[18,"\x9Fl"],"\xD2"=>[77,"\x9Fm"],"\xD3"=>[18,"\x9Fm"],"\xD4"=>[77,"\x9Fn"],"\xD5"=>[18,"\x9Fn"],"\xD6"=>[77,"\x9Fp"],"\xD7"=>[18,"\x9Fp"],"\xD8"=>[77,"\x9Fr"],"\xD9"=>[18,"\x9Fr"],"\xDA"=>[77,"\x9Fu"],"\xDB"=>[18,"\x9Fu"],"\xDC"=>[0,"\x9F\x3A"],"\xDD"=>[0,"\x9FB"],"\xDE"=>[0,"\x9FC"],"\xDF"=>[0,"\x9FD"],"\xE0"=>[0,"\x9FE"],"\xE1"=>[0,"\x9FF"],"\xE2"=>[0,"\x9FG"],"\xE3"=>[0,"\x9FH"],"\xE4"=>[0,"\x9FI"],"\xE5"=>[0,"\x9FJ"],"\xE6"=>[0,"\x9FK"],"\xE7"=>[0,"\x9FL"],"\xE8"=>[0,"\x9FM"],"\xE9"=>[0,"\x9FN"],"\xEA"=>[0,"\x9FO"],"\xEB"=>[0,"\x9FP"],"\xEC"=>[0,"\x9FQ"],"\xED"=>[0,"\x9FR"],"\xEE"=>[0,"\x9FS"],"\xEF"=>[0,"\x9FT"],"\xF0"=>[0,"\x9FU"],"\xF1"=>[0,"\x9FV"],"\xF2"=>[0,"\x9FW"],"\xF3"=>[0,"\x9FY"],"\xF4"=>[0,"\x9Fj"],"\xF5"=>[0,"\x9Fk"],"\xF6"=>[0,"\x9Fq"],"\xF7"=>[0,"\x9Fv"],"\xF8"=>[0,"\x9Fw"],"\xF9"=>[0,"\x9Fx"],"\xFA"=>[0,"\x9Fy"],"\xFB"=>[0,"\x9Fz"],"\xFC"=>[82,"\x9F"],"\xFD"=>[87,"\x9F"],"\xFE"=>[130,"\x9F"],"\xFF"=>[9,"\x9F"],],["\x00"=>[94,"\x960"],"\x01"=>[76,"\x960"],"\x02"=>[104,"\x960"],"\x03"=>[16,"\x960"],"\x04"=>[94,"\x961"],"\x05"=>[76,"\x961"],"\x06"=>[104,"\x961"],"\x07"=>[16,"\x961"],"\x08"=>[94,"\x962"],"\x09"=>[76,"\x962"],"\x0A"=>[104,"\x962"],"\x0B"=>[16,"\x962"],"\x0C"=>[94,"\x96a"],"\x0D"=>[76,"\x96a"],"\x0E"=>[104,"\x96a"],"\x0F"=>[16,"\x96a"],"\x10"=>[94,"\x96c"],"\x11"=>[76,"\x96c"],"\x12"=>[104,"\x96c"],"\x13"=>[16,"\x96c"],"\x14"=>[94,"\x96e"],"\x15"=>[76,"\x96e"],"\x16"=>[104,"\x96e"],"\x17"=>[16,"\x96e"],"\x18"=>[94,"\x96i"],"\x19"=>[76,"\x96i"],"\x1A"=>[104,"\x96i"],"\x1B"=>[16,"\x96i"],"\x1C"=>[94,"\x96o"],"\x1D"=>[76,"\x96o"],"\x1E"=>[104,"\x96o"],"\x1F"=>[16,"\x96o"],"\x20"=>[94,"\x96s"],"\x21"=>[76,"\x96s"],"\x22"=>[104,"\x96s"],"\x23"=>[16,"\x96s"],"\x24"=>[94,"\x96t"],"\x25"=>[76,"\x96t"],"\x26"=>[104,"\x96t"],"\x27"=>[16,"\x96t"],"\x28"=>[77,"\x96\x20"],"\x29"=>[18,"\x96\x20"],"\x2A"=>[77,"\x96\x25"],"\x2B"=>[18,"\x96\x25"],"\x2C"=>[77,"\x96-"],"-"=>[18,"\x96-"],"."=>[77,"\x96."],"\x2F"=>[18,"\x96."],[77,"\x96\x2F"],[18,"\x96\x2F"],[77,"\x963"],[18,"\x963"],[77,"\x964"],[18,"\x964"],[77,"\x965"],[18,"\x965"],[77,"\x966"],[18,"\x966"],"\x3A"=>[77,"\x967"],"\x3B"=>[18,"\x967"],"\x3C"=>[77,"\x968"],"\x3D"=>[18,"\x968"],"\x3E"=>[77,"\x969"],"\x3F"=>[18,"\x969"],"\x40"=>[77,"\x96\x3D"],"A"=>[18,"\x96\x3D"],"B"=>[77,"\x96A"],"C"=>[18,"\x96A"],"D"=>[77,"\x96_"],"E"=>[18,"\x96_"],"F"=>[77,"\x96b"],"G"=>[18,"\x96b"],"H"=>[77,"\x96d"],"I"=>[18,"\x96d"],"J"=>[77,"\x96f"],"K"=>[18,"\x96f"],"L"=>[77,"\x96g"],"M"=>[18,"\x96g"],"N"=>[77,"\x96h"],"O"=>[18,"\x96h"],"P"=>[77,"\x96l"],"Q"=>[18,"\x96l"],"R"=>[77,"\x96m"],"S"=>[18,"\x96m"],"T"=>[77,"\x96n"],"U"=>[18,"\x96n"],"V"=>[77,"\x96p"],"W"=>[18,"\x96p"],"X"=>[77,"\x96r"],"Y"=>[18,"\x96r"],"Z"=>[77,"\x96u"],"\x5B"=>[18,"\x96u"],"\x5C"=>[0,"\x96\x3A"],"\x5D"=>[0,"\x96B"],"\x5E"=>[0,"\x96C"],"_"=>[0,"\x96D"],"\x60"=>[0,"\x96E"],"a"=>[0,"\x96F"],"b"=>[0,"\x96G"],"c"=>[0,"\x96H"],"d"=>[0,"\x96I"],"e"=>[0,"\x96J"],"f"=>[0,"\x96K"],"g"=>[0,"\x96L"],"h"=>[0,"\x96M"],"i"=>[0,"\x96N"],"j"=>[0,"\x96O"],"k"=>[0,"\x96P"],"l"=>[0,"\x96Q"],"m"=>[0,"\x96R"],"n"=>[0,"\x96S"],"o"=>[0,"\x96T"],"p"=>[0,"\x96U"],"q"=>[0,"\x96V"],"r"=>[0,"\x96W"],"s"=>[0,"\x96Y"],"t"=>[0,"\x96j"],"u"=>[0,"\x96k"],"v"=>[0,"\x96q"],"w"=>[0,"\x96v"],"x"=>[0,"\x96w"],"y"=>[0,"\x96x"],"z"=>[0,"\x96y"],"\x7B"=>[0,"\x96z"],"\x7C"=>[82,"\x96"],"\x7D"=>[87,"\x96"],"~"=>[130,"\x96"],"\x7F"=>[9,"\x96"],"\x80"=>[94,"\x970"],"\x81"=>[76,"\x970"],"\x82"=>[104,"\x970"],"\x83"=>[16,"\x970"],"\x84"=>[94,"\x971"],"\x85"=>[76,"\x971"],"\x86"=>[104,"\x971"],"\x87"=>[16,"\x971"],"\x88"=>[94,"\x972"],"\x89"=>[76,"\x972"],"\x8A"=>[104,"\x972"],"\x8B"=>[16,"\x972"],"\x8C"=>[94,"\x97a"],"\x8D"=>[76,"\x97a"],"\x8E"=>[104,"\x97a"],"\x8F"=>[16,"\x97a"],"\x90"=>[94,"\x97c"],"\x91"=>[76,"\x97c"],"\x92"=>[104,"\x97c"],"\x93"=>[16,"\x97c"],"\x94"=>[94,"\x97e"],"\x95"=>[76,"\x97e"],"\x96"=>[104,"\x97e"],"\x97"=>[16,"\x97e"],"\x98"=>[94,"\x97i"],"\x99"=>[76,"\x97i"],"\x9A"=>[104,"\x97i"],"\x9B"=>[16,"\x97i"],"\x9C"=>[94,"\x97o"],"\x9D"=>[76,"\x97o"],"\x9E"=>[104,"\x97o"],"\x9F"=>[16,"\x97o"],"\xA0"=>[94,"\x97s"],"\xA1"=>[76,"\x97s"],"\xA2"=>[104,"\x97s"],"\xA3"=>[16,"\x97s"],"\xA4"=>[94,"\x97t"],"\xA5"=>[76,"\x97t"],"\xA6"=>[104,"\x97t"],"\xA7"=>[16,"\x97t"],"\xA8"=>[77,"\x97\x20"],"\xA9"=>[18,"\x97\x20"],"\xAA"=>[77,"\x97\x25"],"\xAB"=>[18,"\x97\x25"],"\xAC"=>[77,"\x97-"],"\xAD"=>[18,"\x97-"],"\xAE"=>[77,"\x97."],"\xAF"=>[18,"\x97."],"\xB0"=>[77,"\x97\x2F"],"\xB1"=>[18,"\x97\x2F"],"\xB2"=>[77,"\x973"],"\xB3"=>[18,"\x973"],"\xB4"=>[77,"\x974"],"\xB5"=>[18,"\x974"],"\xB6"=>[77,"\x975"],"\xB7"=>[18,"\x975"],"\xB8"=>[77,"\x976"],"\xB9"=>[18,"\x976"],"\xBA"=>[77,"\x977"],"\xBB"=>[18,"\x977"],"\xBC"=>[77,"\x978"],"\xBD"=>[18,"\x978"],"\xBE"=>[77,"\x979"],"\xBF"=>[18,"\x979"],"\xC0"=>[77,"\x97\x3D"],"\xC1"=>[18,"\x97\x3D"],"\xC2"=>[77,"\x97A"],"\xC3"=>[18,"\x97A"],"\xC4"=>[77,"\x97_"],"\xC5"=>[18,"\x97_"],"\xC6"=>[77,"\x97b"],"\xC7"=>[18,"\x97b"],"\xC8"=>[77,"\x97d"],"\xC9"=>[18,"\x97d"],"\xCA"=>[77,"\x97f"],"\xCB"=>[18,"\x97f"],"\xCC"=>[77,"\x97g"],"\xCD"=>[18,"\x97g"],"\xCE"=>[77,"\x97h"],"\xCF"=>[18,"\x97h"],"\xD0"=>[77,"\x97l"],"\xD1"=>[18,"\x97l"],"\xD2"=>[77,"\x97m"],"\xD3"=>[18,"\x97m"],"\xD4"=>[77,"\x97n"],"\xD5"=>[18,"\x97n"],"\xD6"=>[77,"\x97p"],"\xD7"=>[18,"\x97p"],"\xD8"=>[77,"\x97r"],"\xD9"=>[18,"\x97r"],"\xDA"=>[77,"\x97u"],"\xDB"=>[18,"\x97u"],"\xDC"=>[0,"\x97\x3A"],"\xDD"=>[0,"\x97B"],"\xDE"=>[0,"\x97C"],"\xDF"=>[0,"\x97D"],"\xE0"=>[0,"\x97E"],"\xE1"=>[0,"\x97F"],"\xE2"=>[0,"\x97G"],"\xE3"=>[0,"\x97H"],"\xE4"=>[0,"\x97I"],"\xE5"=>[0,"\x97J"],"\xE6"=>[0,"\x97K"],"\xE7"=>[0,"\x97L"],"\xE8"=>[0,"\x97M"],"\xE9"=>[0,"\x97N"],"\xEA"=>[0,"\x97O"],"\xEB"=>[0,"\x97P"],"\xEC"=>[0,"\x97Q"],"\xED"=>[0,"\x97R"],"\xEE"=>[0,"\x97S"],"\xEF"=>[0,"\x97T"],"\xF0"=>[0,"\x97U"],"\xF1"=>[0,"\x97V"],"\xF2"=>[0,"\x97W"],"\xF3"=>[0,"\x97Y"],"\xF4"=>[0,"\x97j"],"\xF5"=>[0,"\x97k"],"\xF6"=>[0,"\x97q"],"\xF7"=>[0,"\x97v"],"\xF8"=>[0,"\x97w"],"\xF9"=>[0,"\x97x"],"\xFA"=>[0,"\x97y"],"\xFB"=>[0,"\x97z"],"\xFC"=>[82,"\x97"],"\xFD"=>[87,"\x97"],"\xFE"=>[130,"\x97"],"\xFF"=>[9,"\x97"],],["\x00"=>[94,"\x980"],"\x01"=>[76,"\x980"],"\x02"=>[104,"\x980"],"\x03"=>[16,"\x980"],"\x04"=>[94,"\x981"],"\x05"=>[76,"\x981"],"\x06"=>[104,"\x981"],"\x07"=>[16,"\x981"],"\x08"=>[94,"\x982"],"\x09"=>[76,"\x982"],"\x0A"=>[104,"\x982"],"\x0B"=>[16,"\x982"],"\x0C"=>[94,"\x98a"],"\x0D"=>[76,"\x98a"],"\x0E"=>[104,"\x98a"],"\x0F"=>[16,"\x98a"],"\x10"=>[94,"\x98c"],"\x11"=>[76,"\x98c"],"\x12"=>[104,"\x98c"],"\x13"=>[16,"\x98c"],"\x14"=>[94,"\x98e"],"\x15"=>[76,"\x98e"],"\x16"=>[104,"\x98e"],"\x17"=>[16,"\x98e"],"\x18"=>[94,"\x98i"],"\x19"=>[76,"\x98i"],"\x1A"=>[104,"\x98i"],"\x1B"=>[16,"\x98i"],"\x1C"=>[94,"\x98o"],"\x1D"=>[76,"\x98o"],"\x1E"=>[104,"\x98o"],"\x1F"=>[16,"\x98o"],"\x20"=>[94,"\x98s"],"\x21"=>[76,"\x98s"],"\x22"=>[104,"\x98s"],"\x23"=>[16,"\x98s"],"\x24"=>[94,"\x98t"],"\x25"=>[76,"\x98t"],"\x26"=>[104,"\x98t"],"\x27"=>[16,"\x98t"],"\x28"=>[77,"\x98\x20"],"\x29"=>[18,"\x98\x20"],"\x2A"=>[77,"\x98\x25"],"\x2B"=>[18,"\x98\x25"],"\x2C"=>[77,"\x98-"],"-"=>[18,"\x98-"],"."=>[77,"\x98."],"\x2F"=>[18,"\x98."],[77,"\x98\x2F"],[18,"\x98\x2F"],[77,"\x983"],[18,"\x983"],[77,"\x984"],[18,"\x984"],[77,"\x985"],[18,"\x985"],[77,"\x986"],[18,"\x986"],"\x3A"=>[77,"\x987"],"\x3B"=>[18,"\x987"],"\x3C"=>[77,"\x988"],"\x3D"=>[18,"\x988"],"\x3E"=>[77,"\x989"],"\x3F"=>[18,"\x989"],"\x40"=>[77,"\x98\x3D"],"A"=>[18,"\x98\x3D"],"B"=>[77,"\x98A"],"C"=>[18,"\x98A"],"D"=>[77,"\x98_"],"E"=>[18,"\x98_"],"F"=>[77,"\x98b"],"G"=>[18,"\x98b"],"H"=>[77,"\x98d"],"I"=>[18,"\x98d"],"J"=>[77,"\x98f"],"K"=>[18,"\x98f"],"L"=>[77,"\x98g"],"M"=>[18,"\x98g"],"N"=>[77,"\x98h"],"O"=>[18,"\x98h"],"P"=>[77,"\x98l"],"Q"=>[18,"\x98l"],"R"=>[77,"\x98m"],"S"=>[18,"\x98m"],"T"=>[77,"\x98n"],"U"=>[18,"\x98n"],"V"=>[77,"\x98p"],"W"=>[18,"\x98p"],"X"=>[77,"\x98r"],"Y"=>[18,"\x98r"],"Z"=>[77,"\x98u"],"\x5B"=>[18,"\x98u"],"\x5C"=>[0,"\x98\x3A"],"\x5D"=>[0,"\x98B"],"\x5E"=>[0,"\x98C"],"_"=>[0,"\x98D"],"\x60"=>[0,"\x98E"],"a"=>[0,"\x98F"],"b"=>[0,"\x98G"],"c"=>[0,"\x98H"],"d"=>[0,"\x98I"],"e"=>[0,"\x98J"],"f"=>[0,"\x98K"],"g"=>[0,"\x98L"],"h"=>[0,"\x98M"],"i"=>[0,"\x98N"],"j"=>[0,"\x98O"],"k"=>[0,"\x98P"],"l"=>[0,"\x98Q"],"m"=>[0,"\x98R"],"n"=>[0,"\x98S"],"o"=>[0,"\x98T"],"p"=>[0,"\x98U"],"q"=>[0,"\x98V"],"r"=>[0,"\x98W"],"s"=>[0,"\x98Y"],"t"=>[0,"\x98j"],"u"=>[0,"\x98k"],"v"=>[0,"\x98q"],"w"=>[0,"\x98v"],"x"=>[0,"\x98w"],"y"=>[0,"\x98x"],"z"=>[0,"\x98y"],"\x7B"=>[0,"\x98z"],"\x7C"=>[82,"\x98"],"\x7D"=>[87,"\x98"],"~"=>[130,"\x98"],"\x7F"=>[9,"\x98"],"\x80"=>[94,"\x9B0"],"\x81"=>[76,"\x9B0"],"\x82"=>[104,"\x9B0"],"\x83"=>[16,"\x9B0"],"\x84"=>[94,"\x9B1"],"\x85"=>[76,"\x9B1"],"\x86"=>[104,"\x9B1"],"\x87"=>[16,"\x9B1"],"\x88"=>[94,"\x9B2"],"\x89"=>[76,"\x9B2"],"\x8A"=>[104,"\x9B2"],"\x8B"=>[16,"\x9B2"],"\x8C"=>[94,"\x9Ba"],"\x8D"=>[76,"\x9Ba"],"\x8E"=>[104,"\x9Ba"],"\x8F"=>[16,"\x9Ba"],"\x90"=>[94,"\x9Bc"],"\x91"=>[76,"\x9Bc"],"\x92"=>[104,"\x9Bc"],"\x93"=>[16,"\x9Bc"],"\x94"=>[94,"\x9Be"],"\x95"=>[76,"\x9Be"],"\x96"=>[104,"\x9Be"],"\x97"=>[16,"\x9Be"],"\x98"=>[94,"\x9Bi"],"\x99"=>[76,"\x9Bi"],"\x9A"=>[104,"\x9Bi"],"\x9B"=>[16,"\x9Bi"],"\x9C"=>[94,"\x9Bo"],"\x9D"=>[76,"\x9Bo"],"\x9E"=>[104,"\x9Bo"],"\x9F"=>[16,"\x9Bo"],"\xA0"=>[94,"\x9Bs"],"\xA1"=>[76,"\x9Bs"],"\xA2"=>[104,"\x9Bs"],"\xA3"=>[16,"\x9Bs"],"\xA4"=>[94,"\x9Bt"],"\xA5"=>[76,"\x9Bt"],"\xA6"=>[104,"\x9Bt"],"\xA7"=>[16,"\x9Bt"],"\xA8"=>[77,"\x9B\x20"],"\xA9"=>[18,"\x9B\x20"],"\xAA"=>[77,"\x9B\x25"],"\xAB"=>[18,"\x9B\x25"],"\xAC"=>[77,"\x9B-"],"\xAD"=>[18,"\x9B-"],"\xAE"=>[77,"\x9B."],"\xAF"=>[18,"\x9B."],"\xB0"=>[77,"\x9B\x2F"],"\xB1"=>[18,"\x9B\x2F"],"\xB2"=>[77,"\x9B3"],"\xB3"=>[18,"\x9B3"],"\xB4"=>[77,"\x9B4"],"\xB5"=>[18,"\x9B4"],"\xB6"=>[77,"\x9B5"],"\xB7"=>[18,"\x9B5"],"\xB8"=>[77,"\x9B6"],"\xB9"=>[18,"\x9B6"],"\xBA"=>[77,"\x9B7"],"\xBB"=>[18,"\x9B7"],"\xBC"=>[77,"\x9B8"],"\xBD"=>[18,"\x9B8"],"\xBE"=>[77,"\x9B9"],"\xBF"=>[18,"\x9B9"],"\xC0"=>[77,"\x9B\x3D"],"\xC1"=>[18,"\x9B\x3D"],"\xC2"=>[77,"\x9BA"],"\xC3"=>[18,"\x9BA"],"\xC4"=>[77,"\x9B_"],"\xC5"=>[18,"\x9B_"],"\xC6"=>[77,"\x9Bb"],"\xC7"=>[18,"\x9Bb"],"\xC8"=>[77,"\x9Bd"],"\xC9"=>[18,"\x9Bd"],"\xCA"=>[77,"\x9Bf"],"\xCB"=>[18,"\x9Bf"],"\xCC"=>[77,"\x9Bg"],"\xCD"=>[18,"\x9Bg"],"\xCE"=>[77,"\x9Bh"],"\xCF"=>[18,"\x9Bh"],"\xD0"=>[77,"\x9Bl"],"\xD1"=>[18,"\x9Bl"],"\xD2"=>[77,"\x9Bm"],"\xD3"=>[18,"\x9Bm"],"\xD4"=>[77,"\x9Bn"],"\xD5"=>[18,"\x9Bn"],"\xD6"=>[77,"\x9Bp"],"\xD7"=>[18,"\x9Bp"],"\xD8"=>[77,"\x9Br"],"\xD9"=>[18,"\x9Br"],"\xDA"=>[77,"\x9Bu"],"\xDB"=>[18,"\x9Bu"],"\xDC"=>[0,"\x9B\x3A"],"\xDD"=>[0,"\x9BB"],"\xDE"=>[0,"\x9BC"],"\xDF"=>[0,"\x9BD"],"\xE0"=>[0,"\x9BE"],"\xE1"=>[0,"\x9BF"],"\xE2"=>[0,"\x9BG"],"\xE3"=>[0,"\x9BH"],"\xE4"=>[0,"\x9BI"],"\xE5"=>[0,"\x9BJ"],"\xE6"=>[0,"\x9BK"],"\xE7"=>[0,"\x9BL"],"\xE8"=>[0,"\x9BM"],"\xE9"=>[0,"\x9BN"],"\xEA"=>[0,"\x9BO"],"\xEB"=>[0,"\x9BP"],"\xEC"=>[0,"\x9BQ"],"\xED"=>[0,"\x9BR"],"\xEE"=>[0,"\x9BS"],"\xEF"=>[0,"\x9BT"],"\xF0"=>[0,"\x9BU"],"\xF1"=>[0,"\x9BV"],"\xF2"=>[0,"\x9BW"],"\xF3"=>[0,"\x9BY"],"\xF4"=>[0,"\x9Bj"],"\xF5"=>[0,"\x9Bk"],"\xF6"=>[0,"\x9Bq"],"\xF7"=>[0,"\x9Bv"],"\xF8"=>[0,"\x9Bw"],"\xF9"=>[0,"\x9Bx"],"\xFA"=>[0,"\x9By"],"\xFB"=>[0,"\x9Bz"],"\xFC"=>[82,"\x9B"],"\xFD"=>[87,"\x9B"],"\xFE"=>[130,"\x9B"],"\xFF"=>[9,"\x9B"],],["\x00"=>[77,"\x980"],"\x01"=>[18,"\x980"],"\x02"=>[77,"\x981"],"\x03"=>[18,"\x981"],"\x04"=>[77,"\x982"],"\x05"=>[18,"\x982"],"\x06"=>[77,"\x98a"],"\x07"=>[18,"\x98a"],"\x08"=>[77,"\x98c"],"\x09"=>[18,"\x98c"],"\x0A"=>[77,"\x98e"],"\x0B"=>[18,"\x98e"],"\x0C"=>[77,"\x98i"],"\x0D"=>[18,"\x98i"],"\x0E"=>[77,"\x98o"],"\x0F"=>[18,"\x98o"],"\x10"=>[77,"\x98s"],"\x11"=>[18,"\x98s"],"\x12"=>[77,"\x98t"],"\x13"=>[18,"\x98t"],"\x14"=>[0,"\x98\x20"],"\x15"=>[0,"\x98\x25"],"\x16"=>[0,"\x98-"],"\x17"=>[0,"\x98."],"\x18"=>[0,"\x98\x2F"],"\x19"=>[0,"\x983"],"\x1A"=>[0,"\x984"],"\x1B"=>[0,"\x985"],"\x1C"=>[0,"\x986"],"\x1D"=>[0,"\x987"],"\x1E"=>[0,"\x988"],"\x1F"=>[0,"\x989"],"\x20"=>[0,"\x98\x3D"],"\x21"=>[0,"\x98A"],"\x22"=>[0,"\x98_"],"\x23"=>[0,"\x98b"],"\x24"=>[0,"\x98d"],"\x25"=>[0,"\x98f"],"\x26"=>[0,"\x98g"],"\x27"=>[0,"\x98h"],"\x28"=>[0,"\x98l"],"\x29"=>[0,"\x98m"],"\x2A"=>[0,"\x98n"],"\x2B"=>[0,"\x98p"],"\x2C"=>[0,"\x98r"],"-"=>[0,"\x98u"],"."=>[100,"\x98"],"\x2F"=>[110,"\x98"],[111,"\x98"],[115,"\x98"],[116,"\x98"],[118,"\x98"],[119,"\x98"],[122,"\x98"],[123,"\x98"],[125,"\x98"],[126,"\x98"],[129,"\x98"],"\x3A"=>[143,"\x98"],"\x3B"=>[148,"\x98"],"\x3C"=>[151,"\x98"],"\x3D"=>[153,"\x98"],"\x3E"=>[83,"\x98"],"\x3F"=>[10,"\x98"],"\x40"=>[77,"\x9B0"],"A"=>[18,"\x9B0"],"B"=>[77,"\x9B1"],"C"=>[18,"\x9B1"],"D"=>[77,"\x9B2"],"E"=>[18,"\x9B2"],"F"=>[77,"\x9Ba"],"G"=>[18,"\x9Ba"],"H"=>[77,"\x9Bc"],"I"=>[18,"\x9Bc"],"J"=>[77,"\x9Be"],"K"=>[18,"\x9Be"],"L"=>[77,"\x9Bi"],"M"=>[18,"\x9Bi"],"N"=>[77,"\x9Bo"],"O"=>[18,"\x9Bo"],"P"=>[77,"\x9Bs"],"Q"=>[18,"\x9Bs"],"R"=>[77,"\x9Bt"],"S"=>[18,"\x9Bt"],"T"=>[0,"\x9B\x20"],"U"=>[0,"\x9B\x25"],"V"=>[0,"\x9B-"],"W"=>[0,"\x9B."],"X"=>[0,"\x9B\x2F"],"Y"=>[0,"\x9B3"],"Z"=>[0,"\x9B4"],"\x5B"=>[0,"\x9B5"],"\x5C"=>[0,"\x9B6"],"\x5D"=>[0,"\x9B7"],"\x5E"=>[0,"\x9B8"],"_"=>[0,"\x9B9"],"\x60"=>[0,"\x9B\x3D"],"a"=>[0,"\x9BA"],"b"=>[0,"\x9B_"],"c"=>[0,"\x9Bb"],"d"=>[0,"\x9Bd"],"e"=>[0,"\x9Bf"],"f"=>[0,"\x9Bg"],"g"=>[0,"\x9Bh"],"h"=>[0,"\x9Bl"],"i"=>[0,"\x9Bm"],"j"=>[0,"\x9Bn"],"k"=>[0,"\x9Bp"],"l"=>[0,"\x9Br"],"m"=>[0,"\x9Bu"],"n"=>[100,"\x9B"],"o"=>[110,"\x9B"],"p"=>[111,"\x9B"],"q"=>[115,"\x9B"],"r"=>[116,"\x9B"],"s"=>[118,"\x9B"],"t"=>[119,"\x9B"],"u"=>[122,"\x9B"],"v"=>[123,"\x9B"],"w"=>[125,"\x9B"],"x"=>[126,"\x9B"],"y"=>[129,"\x9B"],"z"=>[143,"\x9B"],"\x7B"=>[148,"\x9B"],"\x7C"=>[151,"\x9B"],"\x7D"=>[153,"\x9B"],"~"=>[83,"\x9B"],"\x7F"=>[10,"\x9B"],"\x80"=>[77,"\x9D0"],"\x81"=>[18,"\x9D0"],"\x82"=>[77,"\x9D1"],"\x83"=>[18,"\x9D1"],"\x84"=>[77,"\x9D2"],"\x85"=>[18,"\x9D2"],"\x86"=>[77,"\x9Da"],"\x87"=>[18,"\x9Da"],"\x88"=>[77,"\x9Dc"],"\x89"=>[18,"\x9Dc"],"\x8A"=>[77,"\x9De"],"\x8B"=>[18,"\x9De"],"\x8C"=>[77,"\x9Di"],"\x8D"=>[18,"\x9Di"],"\x8E"=>[77,"\x9Do"],"\x8F"=>[18,"\x9Do"],"\x90"=>[77,"\x9Ds"],"\x91"=>[18,"\x9Ds"],"\x92"=>[77,"\x9Dt"],"\x93"=>[18,"\x9Dt"],"\x94"=>[0,"\x9D\x20"],"\x95"=>[0,"\x9D\x25"],"\x96"=>[0,"\x9D-"],"\x97"=>[0,"\x9D."],"\x98"=>[0,"\x9D\x2F"],"\x99"=>[0,"\x9D3"],"\x9A"=>[0,"\x9D4"],"\x9B"=>[0,"\x9D5"],"\x9C"=>[0,"\x9D6"],"\x9D"=>[0,"\x9D7"],"\x9E"=>[0,"\x9D8"],"\x9F"=>[0,"\x9D9"],"\xA0"=>[0,"\x9D\x3D"],"\xA1"=>[0,"\x9DA"],"\xA2"=>[0,"\x9D_"],"\xA3"=>[0,"\x9Db"],"\xA4"=>[0,"\x9Dd"],"\xA5"=>[0,"\x9Df"],"\xA6"=>[0,"\x9Dg"],"\xA7"=>[0,"\x9Dh"],"\xA8"=>[0,"\x9Dl"],"\xA9"=>[0,"\x9Dm"],"\xAA"=>[0,"\x9Dn"],"\xAB"=>[0,"\x9Dp"],"\xAC"=>[0,"\x9Dr"],"\xAD"=>[0,"\x9Du"],"\xAE"=>[100,"\x9D"],"\xAF"=>[110,"\x9D"],"\xB0"=>[111,"\x9D"],"\xB1"=>[115,"\x9D"],"\xB2"=>[116,"\x9D"],"\xB3"=>[118,"\x9D"],"\xB4"=>[119,"\x9D"],"\xB5"=>[122,"\x9D"],"\xB6"=>[123,"\x9D"],"\xB7"=>[125,"\x9D"],"\xB8"=>[126,"\x9D"],"\xB9"=>[129,"\x9D"],"\xBA"=>[143,"\x9D"],"\xBB"=>[148,"\x9D"],"\xBC"=>[151,"\x9D"],"\xBD"=>[153,"\x9D"],"\xBE"=>[83,"\x9D"],"\xBF"=>[10,"\x9D"],"\xC0"=>[77,"\x9E0"],"\xC1"=>[18,"\x9E0"],"\xC2"=>[77,"\x9E1"],"\xC3"=>[18,"\x9E1"],"\xC4"=>[77,"\x9E2"],"\xC5"=>[18,"\x9E2"],"\xC6"=>[77,"\x9Ea"],"\xC7"=>[18,"\x9Ea"],"\xC8"=>[77,"\x9Ec"],"\xC9"=>[18,"\x9Ec"],"\xCA"=>[77,"\x9Ee"],"\xCB"=>[18,"\x9Ee"],"\xCC"=>[77,"\x9Ei"],"\xCD"=>[18,"\x9Ei"],"\xCE"=>[77,"\x9Eo"],"\xCF"=>[18,"\x9Eo"],"\xD0"=>[77,"\x9Es"],"\xD1"=>[18,"\x9Es"],"\xD2"=>[77,"\x9Et"],"\xD3"=>[18,"\x9Et"],"\xD4"=>[0,"\x9E\x20"],"\xD5"=>[0,"\x9E\x25"],"\xD6"=>[0,"\x9E-"],"\xD7"=>[0,"\x9E."],"\xD8"=>[0,"\x9E\x2F"],"\xD9"=>[0,"\x9E3"],"\xDA"=>[0,"\x9E4"],"\xDB"=>[0,"\x9E5"],"\xDC"=>[0,"\x9E6"],"\xDD"=>[0,"\x9E7"],"\xDE"=>[0,"\x9E8"],"\xDF"=>[0,"\x9E9"],"\xE0"=>[0,"\x9E\x3D"],"\xE1"=>[0,"\x9EA"],"\xE2"=>[0,"\x9E_"],"\xE3"=>[0,"\x9Eb"],"\xE4"=>[0,"\x9Ed"],"\xE5"=>[0,"\x9Ef"],"\xE6"=>[0,"\x9Eg"],"\xE7"=>[0,"\x9Eh"],"\xE8"=>[0,"\x9El"],"\xE9"=>[0,"\x9Em"],"\xEA"=>[0,"\x9En"],"\xEB"=>[0,"\x9Ep"],"\xEC"=>[0,"\x9Er"],"\xED"=>[0,"\x9Eu"],"\xEE"=>[100,"\x9E"],"\xEF"=>[110,"\x9E"],"\xF0"=>[111,"\x9E"],"\xF1"=>[115,"\x9E"],"\xF2"=>[116,"\x9E"],"\xF3"=>[118,"\x9E"],"\xF4"=>[119,"\x9E"],"\xF5"=>[122,"\x9E"],"\xF6"=>[123,"\x9E"],"\xF7"=>[125,"\x9E"],"\xF8"=>[126,"\x9E"],"\xF9"=>[129,"\x9E"],"\xFA"=>[143,"\x9E"],"\xFB"=>[148,"\x9E"],"\xFC"=>[151,"\x9E"],"\xFD"=>[153,"\x9E"],"\xFE"=>[83,"\x9E"],"\xFF"=>[10,"\x9E"],],["\x00"=>[94,"\x990"],"\x01"=>[76,"\x990"],"\x02"=>[104,"\x990"],"\x03"=>[16,"\x990"],"\x04"=>[94,"\x991"],"\x05"=>[76,"\x991"],"\x06"=>[104,"\x991"],"\x07"=>[16,"\x991"],"\x08"=>[94,"\x992"],"\x09"=>[76,"\x992"],"\x0A"=>[104,"\x992"],"\x0B"=>[16,"\x992"],"\x0C"=>[94,"\x99a"],"\x0D"=>[76,"\x99a"],"\x0E"=>[104,"\x99a"],"\x0F"=>[16,"\x99a"],"\x10"=>[94,"\x99c"],"\x11"=>[76,"\x99c"],"\x12"=>[104,"\x99c"],"\x13"=>[16,"\x99c"],"\x14"=>[94,"\x99e"],"\x15"=>[76,"\x99e"],"\x16"=>[104,"\x99e"],"\x17"=>[16,"\x99e"],"\x18"=>[94,"\x99i"],"\x19"=>[76,"\x99i"],"\x1A"=>[104,"\x99i"],"\x1B"=>[16,"\x99i"],"\x1C"=>[94,"\x99o"],"\x1D"=>[76,"\x99o"],"\x1E"=>[104,"\x99o"],"\x1F"=>[16,"\x99o"],"\x20"=>[94,"\x99s"],"\x21"=>[76,"\x99s"],"\x22"=>[104,"\x99s"],"\x23"=>[16,"\x99s"],"\x24"=>[94,"\x99t"],"\x25"=>[76,"\x99t"],"\x26"=>[104,"\x99t"],"\x27"=>[16,"\x99t"],"\x28"=>[77,"\x99\x20"],"\x29"=>[18,"\x99\x20"],"\x2A"=>[77,"\x99\x25"],"\x2B"=>[18,"\x99\x25"],"\x2C"=>[77,"\x99-"],"-"=>[18,"\x99-"],"."=>[77,"\x99."],"\x2F"=>[18,"\x99."],[77,"\x99\x2F"],[18,"\x99\x2F"],[77,"\x993"],[18,"\x993"],[77,"\x994"],[18,"\x994"],[77,"\x995"],[18,"\x995"],[77,"\x996"],[18,"\x996"],"\x3A"=>[77,"\x997"],"\x3B"=>[18,"\x997"],"\x3C"=>[77,"\x998"],"\x3D"=>[18,"\x998"],"\x3E"=>[77,"\x999"],"\x3F"=>[18,"\x999"],"\x40"=>[77,"\x99\x3D"],"A"=>[18,"\x99\x3D"],"B"=>[77,"\x99A"],"C"=>[18,"\x99A"],"D"=>[77,"\x99_"],"E"=>[18,"\x99_"],"F"=>[77,"\x99b"],"G"=>[18,"\x99b"],"H"=>[77,"\x99d"],"I"=>[18,"\x99d"],"J"=>[77,"\x99f"],"K"=>[18,"\x99f"],"L"=>[77,"\x99g"],"M"=>[18,"\x99g"],"N"=>[77,"\x99h"],"O"=>[18,"\x99h"],"P"=>[77,"\x99l"],"Q"=>[18,"\x99l"],"R"=>[77,"\x99m"],"S"=>[18,"\x99m"],"T"=>[77,"\x99n"],"U"=>[18,"\x99n"],"V"=>[77,"\x99p"],"W"=>[18,"\x99p"],"X"=>[77,"\x99r"],"Y"=>[18,"\x99r"],"Z"=>[77,"\x99u"],"\x5B"=>[18,"\x99u"],"\x5C"=>[0,"\x99\x3A"],"\x5D"=>[0,"\x99B"],"\x5E"=>[0,"\x99C"],"_"=>[0,"\x99D"],"\x60"=>[0,"\x99E"],"a"=>[0,"\x99F"],"b"=>[0,"\x99G"],"c"=>[0,"\x99H"],"d"=>[0,"\x99I"],"e"=>[0,"\x99J"],"f"=>[0,"\x99K"],"g"=>[0,"\x99L"],"h"=>[0,"\x99M"],"i"=>[0,"\x99N"],"j"=>[0,"\x99O"],"k"=>[0,"\x99P"],"l"=>[0,"\x99Q"],"m"=>[0,"\x99R"],"n"=>[0,"\x99S"],"o"=>[0,"\x99T"],"p"=>[0,"\x99U"],"q"=>[0,"\x99V"],"r"=>[0,"\x99W"],"s"=>[0,"\x99Y"],"t"=>[0,"\x99j"],"u"=>[0,"\x99k"],"v"=>[0,"\x99q"],"w"=>[0,"\x99v"],"x"=>[0,"\x99w"],"y"=>[0,"\x99x"],"z"=>[0,"\x99y"],"\x7B"=>[0,"\x99z"],"\x7C"=>[82,"\x99"],"\x7D"=>[87,"\x99"],"~"=>[130,"\x99"],"\x7F"=>[9,"\x99"],"\x80"=>[94,"\xA10"],"\x81"=>[76,"\xA10"],"\x82"=>[104,"\xA10"],"\x83"=>[16,"\xA10"],"\x84"=>[94,"\xA11"],"\x85"=>[76,"\xA11"],"\x86"=>[104,"\xA11"],"\x87"=>[16,"\xA11"],"\x88"=>[94,"\xA12"],"\x89"=>[76,"\xA12"],"\x8A"=>[104,"\xA12"],"\x8B"=>[16,"\xA12"],"\x8C"=>[94,"\xA1a"],"\x8D"=>[76,"\xA1a"],"\x8E"=>[104,"\xA1a"],"\x8F"=>[16,"\xA1a"],"\x90"=>[94,"\xA1c"],"\x91"=>[76,"\xA1c"],"\x92"=>[104,"\xA1c"],"\x93"=>[16,"\xA1c"],"\x94"=>[94,"\xA1e"],"\x95"=>[76,"\xA1e"],"\x96"=>[104,"\xA1e"],"\x97"=>[16,"\xA1e"],"\x98"=>[94,"\xA1i"],"\x99"=>[76,"\xA1i"],"\x9A"=>[104,"\xA1i"],"\x9B"=>[16,"\xA1i"],"\x9C"=>[94,"\xA1o"],"\x9D"=>[76,"\xA1o"],"\x9E"=>[104,"\xA1o"],"\x9F"=>[16,"\xA1o"],"\xA0"=>[94,"\xA1s"],"\xA1"=>[76,"\xA1s"],"\xA2"=>[104,"\xA1s"],"\xA3"=>[16,"\xA1s"],"\xA4"=>[94,"\xA1t"],"\xA5"=>[76,"\xA1t"],"\xA6"=>[104,"\xA1t"],"\xA7"=>[16,"\xA1t"],"\xA8"=>[77,"\xA1\x20"],"\xA9"=>[18,"\xA1\x20"],"\xAA"=>[77,"\xA1\x25"],"\xAB"=>[18,"\xA1\x25"],"\xAC"=>[77,"\xA1-"],"\xAD"=>[18,"\xA1-"],"\xAE"=>[77,"\xA1."],"\xAF"=>[18,"\xA1."],"\xB0"=>[77,"\xA1\x2F"],"\xB1"=>[18,"\xA1\x2F"],"\xB2"=>[77,"\xA13"],"\xB3"=>[18,"\xA13"],"\xB4"=>[77,"\xA14"],"\xB5"=>[18,"\xA14"],"\xB6"=>[77,"\xA15"],"\xB7"=>[18,"\xA15"],"\xB8"=>[77,"\xA16"],"\xB9"=>[18,"\xA16"],"\xBA"=>[77,"\xA17"],"\xBB"=>[18,"\xA17"],"\xBC"=>[77,"\xA18"],"\xBD"=>[18,"\xA18"],"\xBE"=>[77,"\xA19"],"\xBF"=>[18,"\xA19"],"\xC0"=>[77,"\xA1\x3D"],"\xC1"=>[18,"\xA1\x3D"],"\xC2"=>[77,"\xA1A"],"\xC3"=>[18,"\xA1A"],"\xC4"=>[77,"\xA1_"],"\xC5"=>[18,"\xA1_"],"\xC6"=>[77,"\xA1b"],"\xC7"=>[18,"\xA1b"],"\xC8"=>[77,"\xA1d"],"\xC9"=>[18,"\xA1d"],"\xCA"=>[77,"\xA1f"],"\xCB"=>[18,"\xA1f"],"\xCC"=>[77,"\xA1g"],"\xCD"=>[18,"\xA1g"],"\xCE"=>[77,"\xA1h"],"\xCF"=>[18,"\xA1h"],"\xD0"=>[77,"\xA1l"],"\xD1"=>[18,"\xA1l"],"\xD2"=>[77,"\xA1m"],"\xD3"=>[18,"\xA1m"],"\xD4"=>[77,"\xA1n"],"\xD5"=>[18,"\xA1n"],"\xD6"=>[77,"\xA1p"],"\xD7"=>[18,"\xA1p"],"\xD8"=>[77,"\xA1r"],"\xD9"=>[18,"\xA1r"],"\xDA"=>[77,"\xA1u"],"\xDB"=>[18,"\xA1u"],"\xDC"=>[0,"\xA1\x3A"],"\xDD"=>[0,"\xA1B"],"\xDE"=>[0,"\xA1C"],"\xDF"=>[0,"\xA1D"],"\xE0"=>[0,"\xA1E"],"\xE1"=>[0,"\xA1F"],"\xE2"=>[0,"\xA1G"],"\xE3"=>[0,"\xA1H"],"\xE4"=>[0,"\xA1I"],"\xE5"=>[0,"\xA1J"],"\xE6"=>[0,"\xA1K"],"\xE7"=>[0,"\xA1L"],"\xE8"=>[0,"\xA1M"],"\xE9"=>[0,"\xA1N"],"\xEA"=>[0,"\xA1O"],"\xEB"=>[0,"\xA1P"],"\xEC"=>[0,"\xA1Q"],"\xED"=>[0,"\xA1R"],"\xEE"=>[0,"\xA1S"],"\xEF"=>[0,"\xA1T"],"\xF0"=>[0,"\xA1U"],"\xF1"=>[0,"\xA1V"],"\xF2"=>[0,"\xA1W"],"\xF3"=>[0,"\xA1Y"],"\xF4"=>[0,"\xA1j"],"\xF5"=>[0,"\xA1k"],"\xF6"=>[0,"\xA1q"],"\xF7"=>[0,"\xA1v"],"\xF8"=>[0,"\xA1w"],"\xF9"=>[0,"\xA1x"],"\xFA"=>[0,"\xA1y"],"\xFB"=>[0,"\xA1z"],"\xFC"=>[82,"\xA1"],"\xFD"=>[87,"\xA1"],"\xFE"=>[130,"\xA1"],"\xFF"=>[9,"\xA1"],],["\x00"=>[77,"\x990"],"\x01"=>[18,"\x990"],"\x02"=>[77,"\x991"],"\x03"=>[18,"\x991"],"\x04"=>[77,"\x992"],"\x05"=>[18,"\x992"],"\x06"=>[77,"\x99a"],"\x07"=>[18,"\x99a"],"\x08"=>[77,"\x99c"],"\x09"=>[18,"\x99c"],"\x0A"=>[77,"\x99e"],"\x0B"=>[18,"\x99e"],"\x0C"=>[77,"\x99i"],"\x0D"=>[18,"\x99i"],"\x0E"=>[77,"\x99o"],"\x0F"=>[18,"\x99o"],"\x10"=>[77,"\x99s"],"\x11"=>[18,"\x99s"],"\x12"=>[77,"\x99t"],"\x13"=>[18,"\x99t"],"\x14"=>[0,"\x99\x20"],"\x15"=>[0,"\x99\x25"],"\x16"=>[0,"\x99-"],"\x17"=>[0,"\x99."],"\x18"=>[0,"\x99\x2F"],"\x19"=>[0,"\x993"],"\x1A"=>[0,"\x994"],"\x1B"=>[0,"\x995"],"\x1C"=>[0,"\x996"],"\x1D"=>[0,"\x997"],"\x1E"=>[0,"\x998"],"\x1F"=>[0,"\x999"],"\x20"=>[0,"\x99\x3D"],"\x21"=>[0,"\x99A"],"\x22"=>[0,"\x99_"],"\x23"=>[0,"\x99b"],"\x24"=>[0,"\x99d"],"\x25"=>[0,"\x99f"],"\x26"=>[0,"\x99g"],"\x27"=>[0,"\x99h"],"\x28"=>[0,"\x99l"],"\x29"=>[0,"\x99m"],"\x2A"=>[0,"\x99n"],"\x2B"=>[0,"\x99p"],"\x2C"=>[0,"\x99r"],"-"=>[0,"\x99u"],"."=>[100,"\x99"],"\x2F"=>[110,"\x99"],[111,"\x99"],[115,"\x99"],[116,"\x99"],[118,"\x99"],[119,"\x99"],[122,"\x99"],[123,"\x99"],[125,"\x99"],[126,"\x99"],[129,"\x99"],"\x3A"=>[143,"\x99"],"\x3B"=>[148,"\x99"],"\x3C"=>[151,"\x99"],"\x3D"=>[153,"\x99"],"\x3E"=>[83,"\x99"],"\x3F"=>[10,"\x99"],"\x40"=>[77,"\xA10"],"A"=>[18,"\xA10"],"B"=>[77,"\xA11"],"C"=>[18,"\xA11"],"D"=>[77,"\xA12"],"E"=>[18,"\xA12"],"F"=>[77,"\xA1a"],"G"=>[18,"\xA1a"],"H"=>[77,"\xA1c"],"I"=>[18,"\xA1c"],"J"=>[77,"\xA1e"],"K"=>[18,"\xA1e"],"L"=>[77,"\xA1i"],"M"=>[18,"\xA1i"],"N"=>[77,"\xA1o"],"O"=>[18,"\xA1o"],"P"=>[77,"\xA1s"],"Q"=>[18,"\xA1s"],"R"=>[77,"\xA1t"],"S"=>[18,"\xA1t"],"T"=>[0,"\xA1\x20"],"U"=>[0,"\xA1\x25"],"V"=>[0,"\xA1-"],"W"=>[0,"\xA1."],"X"=>[0,"\xA1\x2F"],"Y"=>[0,"\xA13"],"Z"=>[0,"\xA14"],"\x5B"=>[0,"\xA15"],"\x5C"=>[0,"\xA16"],"\x5D"=>[0,"\xA17"],"\x5E"=>[0,"\xA18"],"_"=>[0,"\xA19"],"\x60"=>[0,"\xA1\x3D"],"a"=>[0,"\xA1A"],"b"=>[0,"\xA1_"],"c"=>[0,"\xA1b"],"d"=>[0,"\xA1d"],"e"=>[0,"\xA1f"],"f"=>[0,"\xA1g"],"g"=>[0,"\xA1h"],"h"=>[0,"\xA1l"],"i"=>[0,"\xA1m"],"j"=>[0,"\xA1n"],"k"=>[0,"\xA1p"],"l"=>[0,"\xA1r"],"m"=>[0,"\xA1u"],"n"=>[100,"\xA1"],"o"=>[110,"\xA1"],"p"=>[111,"\xA1"],"q"=>[115,"\xA1"],"r"=>[116,"\xA1"],"s"=>[118,"\xA1"],"t"=>[119,"\xA1"],"u"=>[122,"\xA1"],"v"=>[123,"\xA1"],"w"=>[125,"\xA1"],"x"=>[126,"\xA1"],"y"=>[129,"\xA1"],"z"=>[143,"\xA1"],"\x7B"=>[148,"\xA1"],"\x7C"=>[151,"\xA1"],"\x7D"=>[153,"\xA1"],"~"=>[83,"\xA1"],"\x7F"=>[10,"\xA1"],"\x80"=>[77,"\xA70"],"\x81"=>[18,"\xA70"],"\x82"=>[77,"\xA71"],"\x83"=>[18,"\xA71"],"\x84"=>[77,"\xA72"],"\x85"=>[18,"\xA72"],"\x86"=>[77,"\xA7a"],"\x87"=>[18,"\xA7a"],"\x88"=>[77,"\xA7c"],"\x89"=>[18,"\xA7c"],"\x8A"=>[77,"\xA7e"],"\x8B"=>[18,"\xA7e"],"\x8C"=>[77,"\xA7i"],"\x8D"=>[18,"\xA7i"],"\x8E"=>[77,"\xA7o"],"\x8F"=>[18,"\xA7o"],"\x90"=>[77,"\xA7s"],"\x91"=>[18,"\xA7s"],"\x92"=>[77,"\xA7t"],"\x93"=>[18,"\xA7t"],"\x94"=>[0,"\xA7\x20"],"\x95"=>[0,"\xA7\x25"],"\x96"=>[0,"\xA7-"],"\x97"=>[0,"\xA7."],"\x98"=>[0,"\xA7\x2F"],"\x99"=>[0,"\xA73"],"\x9A"=>[0,"\xA74"],"\x9B"=>[0,"\xA75"],"\x9C"=>[0,"\xA76"],"\x9D"=>[0,"\xA77"],"\x9E"=>[0,"\xA78"],"\x9F"=>[0,"\xA79"],"\xA0"=>[0,"\xA7\x3D"],"\xA1"=>[0,"\xA7A"],"\xA2"=>[0,"\xA7_"],"\xA3"=>[0,"\xA7b"],"\xA4"=>[0,"\xA7d"],"\xA5"=>[0,"\xA7f"],"\xA6"=>[0,"\xA7g"],"\xA7"=>[0,"\xA7h"],"\xA8"=>[0,"\xA7l"],"\xA9"=>[0,"\xA7m"],"\xAA"=>[0,"\xA7n"],"\xAB"=>[0,"\xA7p"],"\xAC"=>[0,"\xA7r"],"\xAD"=>[0,"\xA7u"],"\xAE"=>[100,"\xA7"],"\xAF"=>[110,"\xA7"],"\xB0"=>[111,"\xA7"],"\xB1"=>[115,"\xA7"],"\xB2"=>[116,"\xA7"],"\xB3"=>[118,"\xA7"],"\xB4"=>[119,"\xA7"],"\xB5"=>[122,"\xA7"],"\xB6"=>[123,"\xA7"],"\xB7"=>[125,"\xA7"],"\xB8"=>[126,"\xA7"],"\xB9"=>[129,"\xA7"],"\xBA"=>[143,"\xA7"],"\xBB"=>[148,"\xA7"],"\xBC"=>[151,"\xA7"],"\xBD"=>[153,"\xA7"],"\xBE"=>[83,"\xA7"],"\xBF"=>[10,"\xA7"],"\xC0"=>[77,"\xAC0"],"\xC1"=>[18,"\xAC0"],"\xC2"=>[77,"\xAC1"],"\xC3"=>[18,"\xAC1"],"\xC4"=>[77,"\xAC2"],"\xC5"=>[18,"\xAC2"],"\xC6"=>[77,"\xACa"],"\xC7"=>[18,"\xACa"],"\xC8"=>[77,"\xACc"],"\xC9"=>[18,"\xACc"],"\xCA"=>[77,"\xACe"],"\xCB"=>[18,"\xACe"],"\xCC"=>[77,"\xACi"],"\xCD"=>[18,"\xACi"],"\xCE"=>[77,"\xACo"],"\xCF"=>[18,"\xACo"],"\xD0"=>[77,"\xACs"],"\xD1"=>[18,"\xACs"],"\xD2"=>[77,"\xACt"],"\xD3"=>[18,"\xACt"],"\xD4"=>[0,"\xAC\x20"],"\xD5"=>[0,"\xAC\x25"],"\xD6"=>[0,"\xAC-"],"\xD7"=>[0,"\xAC."],"\xD8"=>[0,"\xAC\x2F"],"\xD9"=>[0,"\xAC3"],"\xDA"=>[0,"\xAC4"],"\xDB"=>[0,"\xAC5"],"\xDC"=>[0,"\xAC6"],"\xDD"=>[0,"\xAC7"],"\xDE"=>[0,"\xAC8"],"\xDF"=>[0,"\xAC9"],"\xE0"=>[0,"\xAC\x3D"],"\xE1"=>[0,"\xACA"],"\xE2"=>[0,"\xAC_"],"\xE3"=>[0,"\xACb"],"\xE4"=>[0,"\xACd"],"\xE5"=>[0,"\xACf"],"\xE6"=>[0,"\xACg"],"\xE7"=>[0,"\xACh"],"\xE8"=>[0,"\xACl"],"\xE9"=>[0,"\xACm"],"\xEA"=>[0,"\xACn"],"\xEB"=>[0,"\xACp"],"\xEC"=>[0,"\xACr"],"\xED"=>[0,"\xACu"],"\xEE"=>[100,"\xAC"],"\xEF"=>[110,"\xAC"],"\xF0"=>[111,"\xAC"],"\xF1"=>[115,"\xAC"],"\xF2"=>[116,"\xAC"],"\xF3"=>[118,"\xAC"],"\xF4"=>[119,"\xAC"],"\xF5"=>[122,"\xAC"],"\xF6"=>[123,"\xAC"],"\xF7"=>[125,"\xAC"],"\xF8"=>[126,"\xAC"],"\xF9"=>[129,"\xAC"],"\xFA"=>[143,"\xAC"],"\xFB"=>[148,"\xAC"],"\xFC"=>[151,"\xAC"],"\xFD"=>[153,"\xAC"],"\xFE"=>[83,"\xAC"],"\xFF"=>[10,"\xAC"],],["\x00"=>[77,"\xE00"],"\x01"=>[18,"\xE00"],"\x02"=>[77,"\xE01"],"\x03"=>[18,"\xE01"],"\x04"=>[77,"\xE02"],"\x05"=>[18,"\xE02"],"\x06"=>[77,"\xE0a"],"\x07"=>[18,"\xE0a"],"\x08"=>[77,"\xE0c"],"\x09"=>[18,"\xE0c"],"\x0A"=>[77,"\xE0e"],"\x0B"=>[18,"\xE0e"],"\x0C"=>[77,"\xE0i"],"\x0D"=>[18,"\xE0i"],"\x0E"=>[77,"\xE0o"],"\x0F"=>[18,"\xE0o"],"\x10"=>[77,"\xE0s"],"\x11"=>[18,"\xE0s"],"\x12"=>[77,"\xE0t"],"\x13"=>[18,"\xE0t"],"\x14"=>[0,"\xE0\x20"],"\x15"=>[0,"\xE0\x25"],"\x16"=>[0,"\xE0-"],"\x17"=>[0,"\xE0."],"\x18"=>[0,"\xE0\x2F"],"\x19"=>[0,"\xE03"],"\x1A"=>[0,"\xE04"],"\x1B"=>[0,"\xE05"],"\x1C"=>[0,"\xE06"],"\x1D"=>[0,"\xE07"],"\x1E"=>[0,"\xE08"],"\x1F"=>[0,"\xE09"],"\x20"=>[0,"\xE0\x3D"],"\x21"=>[0,"\xE0A"],"\x22"=>[0,"\xE0_"],"\x23"=>[0,"\xE0b"],"\x24"=>[0,"\xE0d"],"\x25"=>[0,"\xE0f"],"\x26"=>[0,"\xE0g"],"\x27"=>[0,"\xE0h"],"\x28"=>[0,"\xE0l"],"\x29"=>[0,"\xE0m"],"\x2A"=>[0,"\xE0n"],"\x2B"=>[0,"\xE0p"],"\x2C"=>[0,"\xE0r"],"-"=>[0,"\xE0u"],"."=>[100,"\xE0"],"\x2F"=>[110,"\xE0"],[111,"\xE0"],[115,"\xE0"],[116,"\xE0"],[118,"\xE0"],[119,"\xE0"],[122,"\xE0"],[123,"\xE0"],[125,"\xE0"],[126,"\xE0"],[129,"\xE0"],"\x3A"=>[143,"\xE0"],"\x3B"=>[148,"\xE0"],"\x3C"=>[151,"\xE0"],"\x3D"=>[153,"\xE0"],"\x3E"=>[83,"\xE0"],"\x3F"=>[10,"\xE0"],"\x40"=>[77,"\xE20"],"A"=>[18,"\xE20"],"B"=>[77,"\xE21"],"C"=>[18,"\xE21"],"D"=>[77,"\xE22"],"E"=>[18,"\xE22"],"F"=>[77,"\xE2a"],"G"=>[18,"\xE2a"],"H"=>[77,"\xE2c"],"I"=>[18,"\xE2c"],"J"=>[77,"\xE2e"],"K"=>[18,"\xE2e"],"L"=>[77,"\xE2i"],"M"=>[18,"\xE2i"],"N"=>[77,"\xE2o"],"O"=>[18,"\xE2o"],"P"=>[77,"\xE2s"],"Q"=>[18,"\xE2s"],"R"=>[77,"\xE2t"],"S"=>[18,"\xE2t"],"T"=>[0,"\xE2\x20"],"U"=>[0,"\xE2\x25"],"V"=>[0,"\xE2-"],"W"=>[0,"\xE2."],"X"=>[0,"\xE2\x2F"],"Y"=>[0,"\xE23"],"Z"=>[0,"\xE24"],"\x5B"=>[0,"\xE25"],"\x5C"=>[0,"\xE26"],"\x5D"=>[0,"\xE27"],"\x5E"=>[0,"\xE28"],"_"=>[0,"\xE29"],"\x60"=>[0,"\xE2\x3D"],"a"=>[0,"\xE2A"],"b"=>[0,"\xE2_"],"c"=>[0,"\xE2b"],"d"=>[0,"\xE2d"],"e"=>[0,"\xE2f"],"f"=>[0,"\xE2g"],"g"=>[0,"\xE2h"],"h"=>[0,"\xE2l"],"i"=>[0,"\xE2m"],"j"=>[0,"\xE2n"],"k"=>[0,"\xE2p"],"l"=>[0,"\xE2r"],"m"=>[0,"\xE2u"],"n"=>[100,"\xE2"],"o"=>[110,"\xE2"],"p"=>[111,"\xE2"],"q"=>[115,"\xE2"],"r"=>[116,"\xE2"],"s"=>[118,"\xE2"],"t"=>[119,"\xE2"],"u"=>[122,"\xE2"],"v"=>[123,"\xE2"],"w"=>[125,"\xE2"],"x"=>[126,"\xE2"],"y"=>[129,"\xE2"],"z"=>[143,"\xE2"],"\x7B"=>[148,"\xE2"],"\x7C"=>[151,"\xE2"],"\x7D"=>[153,"\xE2"],"~"=>[83,"\xE2"],"\x7F"=>[10,"\xE2"],"\x80"=>[0,"\x990"],"\x81"=>[0,"\x991"],"\x82"=>[0,"\x992"],"\x83"=>[0,"\x99a"],"\x84"=>[0,"\x99c"],"\x85"=>[0,"\x99e"],"\x86"=>[0,"\x99i"],"\x87"=>[0,"\x99o"],"\x88"=>[0,"\x99s"],"\x89"=>[0,"\x99t"],"\x8A"=>[73,"\x99"],"\x8B"=>[88,"\x99"],"\x8C"=>[89,"\x99"],"\x8D"=>[96,"\x99"],"\x8E"=>[97,"\x99"],"\x8F"=>[99,"\x99"],"\x90"=>[106,"\x99"],"\x91"=>[136,"\x99"],"\x92"=>[139,"\x99"],"\x93"=>[141,"\x99"],"\x94"=>[145,"\x99"],"\x95"=>[147,"\x99"],"\x96"=>[149,"\x99"],"\x97"=>[101,"\x99"],"\x98"=>[112,"\x99"],"\x99"=>[117,"\x99"],"\x9A"=>[120,"\x99"],"\x9B"=>[124,"\x99"],"\x9C"=>[127,"\x99"],"\x9D"=>[144,"\x99"],"\x9E"=>[152,"\x99"],"\x9F"=>[11,"\x99"],"\xA0"=>[0,"\xA10"],"\xA1"=>[0,"\xA11"],"\xA2"=>[0,"\xA12"],"\xA3"=>[0,"\xA1a"],"\xA4"=>[0,"\xA1c"],"\xA5"=>[0,"\xA1e"],"\xA6"=>[0,"\xA1i"],"\xA7"=>[0,"\xA1o"],"\xA8"=>[0,"\xA1s"],"\xA9"=>[0,"\xA1t"],"\xAA"=>[73,"\xA1"],"\xAB"=>[88,"\xA1"],"\xAC"=>[89,"\xA1"],"\xAD"=>[96,"\xA1"],"\xAE"=>[97,"\xA1"],"\xAF"=>[99,"\xA1"],"\xB0"=>[106,"\xA1"],"\xB1"=>[136,"\xA1"],"\xB2"=>[139,"\xA1"],"\xB3"=>[141,"\xA1"],"\xB4"=>[145,"\xA1"],"\xB5"=>[147,"\xA1"],"\xB6"=>[149,"\xA1"],"\xB7"=>[101,"\xA1"],"\xB8"=>[112,"\xA1"],"\xB9"=>[117,"\xA1"],"\xBA"=>[120,"\xA1"],"\xBB"=>[124,"\xA1"],"\xBC"=>[127,"\xA1"],"\xBD"=>[144,"\xA1"],"\xBE"=>[152,"\xA1"],"\xBF"=>[11,"\xA1"],"\xC0"=>[0,"\xA70"],"\xC1"=>[0,"\xA71"],"\xC2"=>[0,"\xA72"],"\xC3"=>[0,"\xA7a"],"\xC4"=>[0,"\xA7c"],"\xC5"=>[0,"\xA7e"],"\xC6"=>[0,"\xA7i"],"\xC7"=>[0,"\xA7o"],"\xC8"=>[0,"\xA7s"],"\xC9"=>[0,"\xA7t"],"\xCA"=>[73,"\xA7"],"\xCB"=>[88,"\xA7"],"\xCC"=>[89,"\xA7"],"\xCD"=>[96,"\xA7"],"\xCE"=>[97,"\xA7"],"\xCF"=>[99,"\xA7"],"\xD0"=>[106,"\xA7"],"\xD1"=>[136,"\xA7"],"\xD2"=>[139,"\xA7"],"\xD3"=>[141,"\xA7"],"\xD4"=>[145,"\xA7"],"\xD5"=>[147,"\xA7"],"\xD6"=>[149,"\xA7"],"\xD7"=>[101,"\xA7"],"\xD8"=>[112,"\xA7"],"\xD9"=>[117,"\xA7"],"\xDA"=>[120,"\xA7"],"\xDB"=>[124,"\xA7"],"\xDC"=>[127,"\xA7"],"\xDD"=>[144,"\xA7"],"\xDE"=>[152,"\xA7"],"\xDF"=>[11,"\xA7"],"\xE0"=>[0,"\xAC0"],"\xE1"=>[0,"\xAC1"],"\xE2"=>[0,"\xAC2"],"\xE3"=>[0,"\xACa"],"\xE4"=>[0,"\xACc"],"\xE5"=>[0,"\xACe"],"\xE6"=>[0,"\xACi"],"\xE7"=>[0,"\xACo"],"\xE8"=>[0,"\xACs"],"\xE9"=>[0,"\xACt"],"\xEA"=>[73,"\xAC"],"\xEB"=>[88,"\xAC"],"\xEC"=>[89,"\xAC"],"\xED"=>[96,"\xAC"],"\xEE"=>[97,"\xAC"],"\xEF"=>[99,"\xAC"],"\xF0"=>[106,"\xAC"],"\xF1"=>[136,"\xAC"],"\xF2"=>[139,"\xAC"],"\xF3"=>[141,"\xAC"],"\xF4"=>[145,"\xAC"],"\xF5"=>[147,"\xAC"],"\xF6"=>[149,"\xAC"],"\xF7"=>[101,"\xAC"],"\xF8"=>[112,"\xAC"],"\xF9"=>[117,"\xAC"],"\xFA"=>[120,"\xAC"],"\xFB"=>[124,"\xAC"],"\xFC"=>[127,"\xAC"],"\xFD"=>[144,"\xAC"],"\xFE"=>[152,"\xAC"],"\xFF"=>[11,"\xAC"],],["\x00"=>[94,"\x9A0"],"\x01"=>[76,"\x9A0"],"\x02"=>[104,"\x9A0"],"\x03"=>[16,"\x9A0"],"\x04"=>[94,"\x9A1"],"\x05"=>[76,"\x9A1"],"\x06"=>[104,"\x9A1"],"\x07"=>[16,"\x9A1"],"\x08"=>[94,"\x9A2"],"\x09"=>[76,"\x9A2"],"\x0A"=>[104,"\x9A2"],"\x0B"=>[16,"\x9A2"],"\x0C"=>[94,"\x9Aa"],"\x0D"=>[76,"\x9Aa"],"\x0E"=>[104,"\x9Aa"],"\x0F"=>[16,"\x9Aa"],"\x10"=>[94,"\x9Ac"],"\x11"=>[76,"\x9Ac"],"\x12"=>[104,"\x9Ac"],"\x13"=>[16,"\x9Ac"],"\x14"=>[94,"\x9Ae"],"\x15"=>[76,"\x9Ae"],"\x16"=>[104,"\x9Ae"],"\x17"=>[16,"\x9Ae"],"\x18"=>[94,"\x9Ai"],"\x19"=>[76,"\x9Ai"],"\x1A"=>[104,"\x9Ai"],"\x1B"=>[16,"\x9Ai"],"\x1C"=>[94,"\x9Ao"],"\x1D"=>[76,"\x9Ao"],"\x1E"=>[104,"\x9Ao"],"\x1F"=>[16,"\x9Ao"],"\x20"=>[94,"\x9As"],"\x21"=>[76,"\x9As"],"\x22"=>[104,"\x9As"],"\x23"=>[16,"\x9As"],"\x24"=>[94,"\x9At"],"\x25"=>[76,"\x9At"],"\x26"=>[104,"\x9At"],"\x27"=>[16,"\x9At"],"\x28"=>[77,"\x9A\x20"],"\x29"=>[18,"\x9A\x20"],"\x2A"=>[77,"\x9A\x25"],"\x2B"=>[18,"\x9A\x25"],"\x2C"=>[77,"\x9A-"],"-"=>[18,"\x9A-"],"."=>[77,"\x9A."],"\x2F"=>[18,"\x9A."],[77,"\x9A\x2F"],[18,"\x9A\x2F"],[77,"\x9A3"],[18,"\x9A3"],[77,"\x9A4"],[18,"\x9A4"],[77,"\x9A5"],[18,"\x9A5"],[77,"\x9A6"],[18,"\x9A6"],"\x3A"=>[77,"\x9A7"],"\x3B"=>[18,"\x9A7"],"\x3C"=>[77,"\x9A8"],"\x3D"=>[18,"\x9A8"],"\x3E"=>[77,"\x9A9"],"\x3F"=>[18,"\x9A9"],"\x40"=>[77,"\x9A\x3D"],"A"=>[18,"\x9A\x3D"],"B"=>[77,"\x9AA"],"C"=>[18,"\x9AA"],"D"=>[77,"\x9A_"],"E"=>[18,"\x9A_"],"F"=>[77,"\x9Ab"],"G"=>[18,"\x9Ab"],"H"=>[77,"\x9Ad"],"I"=>[18,"\x9Ad"],"J"=>[77,"\x9Af"],"K"=>[18,"\x9Af"],"L"=>[77,"\x9Ag"],"M"=>[18,"\x9Ag"],"N"=>[77,"\x9Ah"],"O"=>[18,"\x9Ah"],"P"=>[77,"\x9Al"],"Q"=>[18,"\x9Al"],"R"=>[77,"\x9Am"],"S"=>[18,"\x9Am"],"T"=>[77,"\x9An"],"U"=>[18,"\x9An"],"V"=>[77,"\x9Ap"],"W"=>[18,"\x9Ap"],"X"=>[77,"\x9Ar"],"Y"=>[18,"\x9Ar"],"Z"=>[77,"\x9Au"],"\x5B"=>[18,"\x9Au"],"\x5C"=>[0,"\x9A\x3A"],"\x5D"=>[0,"\x9AB"],"\x5E"=>[0,"\x9AC"],"_"=>[0,"\x9AD"],"\x60"=>[0,"\x9AE"],"a"=>[0,"\x9AF"],"b"=>[0,"\x9AG"],"c"=>[0,"\x9AH"],"d"=>[0,"\x9AI"],"e"=>[0,"\x9AJ"],"f"=>[0,"\x9AK"],"g"=>[0,"\x9AL"],"h"=>[0,"\x9AM"],"i"=>[0,"\x9AN"],"j"=>[0,"\x9AO"],"k"=>[0,"\x9AP"],"l"=>[0,"\x9AQ"],"m"=>[0,"\x9AR"],"n"=>[0,"\x9AS"],"o"=>[0,"\x9AT"],"p"=>[0,"\x9AU"],"q"=>[0,"\x9AV"],"r"=>[0,"\x9AW"],"s"=>[0,"\x9AY"],"t"=>[0,"\x9Aj"],"u"=>[0,"\x9Ak"],"v"=>[0,"\x9Aq"],"w"=>[0,"\x9Av"],"x"=>[0,"\x9Aw"],"y"=>[0,"\x9Ax"],"z"=>[0,"\x9Ay"],"\x7B"=>[0,"\x9Az"],"\x7C"=>[82,"\x9A"],"\x7D"=>[87,"\x9A"],"~"=>[130,"\x9A"],"\x7F"=>[9,"\x9A"],"\x80"=>[94,"\x9C0"],"\x81"=>[76,"\x9C0"],"\x82"=>[104,"\x9C0"],"\x83"=>[16,"\x9C0"],"\x84"=>[94,"\x9C1"],"\x85"=>[76,"\x9C1"],"\x86"=>[104,"\x9C1"],"\x87"=>[16,"\x9C1"],"\x88"=>[94,"\x9C2"],"\x89"=>[76,"\x9C2"],"\x8A"=>[104,"\x9C2"],"\x8B"=>[16,"\x9C2"],"\x8C"=>[94,"\x9Ca"],"\x8D"=>[76,"\x9Ca"],"\x8E"=>[104,"\x9Ca"],"\x8F"=>[16,"\x9Ca"],"\x90"=>[94,"\x9Cc"],"\x91"=>[76,"\x9Cc"],"\x92"=>[104,"\x9Cc"],"\x93"=>[16,"\x9Cc"],"\x94"=>[94,"\x9Ce"],"\x95"=>[76,"\x9Ce"],"\x96"=>[104,"\x9Ce"],"\x97"=>[16,"\x9Ce"],"\x98"=>[94,"\x9Ci"],"\x99"=>[76,"\x9Ci"],"\x9A"=>[104,"\x9Ci"],"\x9B"=>[16,"\x9Ci"],"\x9C"=>[94,"\x9Co"],"\x9D"=>[76,"\x9Co"],"\x9E"=>[104,"\x9Co"],"\x9F"=>[16,"\x9Co"],"\xA0"=>[94,"\x9Cs"],"\xA1"=>[76,"\x9Cs"],"\xA2"=>[104,"\x9Cs"],"\xA3"=>[16,"\x9Cs"],"\xA4"=>[94,"\x9Ct"],"\xA5"=>[76,"\x9Ct"],"\xA6"=>[104,"\x9Ct"],"\xA7"=>[16,"\x9Ct"],"\xA8"=>[77,"\x9C\x20"],"\xA9"=>[18,"\x9C\x20"],"\xAA"=>[77,"\x9C\x25"],"\xAB"=>[18,"\x9C\x25"],"\xAC"=>[77,"\x9C-"],"\xAD"=>[18,"\x9C-"],"\xAE"=>[77,"\x9C."],"\xAF"=>[18,"\x9C."],"\xB0"=>[77,"\x9C\x2F"],"\xB1"=>[18,"\x9C\x2F"],"\xB2"=>[77,"\x9C3"],"\xB3"=>[18,"\x9C3"],"\xB4"=>[77,"\x9C4"],"\xB5"=>[18,"\x9C4"],"\xB6"=>[77,"\x9C5"],"\xB7"=>[18,"\x9C5"],"\xB8"=>[77,"\x9C6"],"\xB9"=>[18,"\x9C6"],"\xBA"=>[77,"\x9C7"],"\xBB"=>[18,"\x9C7"],"\xBC"=>[77,"\x9C8"],"\xBD"=>[18,"\x9C8"],"\xBE"=>[77,"\x9C9"],"\xBF"=>[18,"\x9C9"],"\xC0"=>[77,"\x9C\x3D"],"\xC1"=>[18,"\x9C\x3D"],"\xC2"=>[77,"\x9CA"],"\xC3"=>[18,"\x9CA"],"\xC4"=>[77,"\x9C_"],"\xC5"=>[18,"\x9C_"],"\xC6"=>[77,"\x9Cb"],"\xC7"=>[18,"\x9Cb"],"\xC8"=>[77,"\x9Cd"],"\xC9"=>[18,"\x9Cd"],"\xCA"=>[77,"\x9Cf"],"\xCB"=>[18,"\x9Cf"],"\xCC"=>[77,"\x9Cg"],"\xCD"=>[18,"\x9Cg"],"\xCE"=>[77,"\x9Ch"],"\xCF"=>[18,"\x9Ch"],"\xD0"=>[77,"\x9Cl"],"\xD1"=>[18,"\x9Cl"],"\xD2"=>[77,"\x9Cm"],"\xD3"=>[18,"\x9Cm"],"\xD4"=>[77,"\x9Cn"],"\xD5"=>[18,"\x9Cn"],"\xD6"=>[77,"\x9Cp"],"\xD7"=>[18,"\x9Cp"],"\xD8"=>[77,"\x9Cr"],"\xD9"=>[18,"\x9Cr"],"\xDA"=>[77,"\x9Cu"],"\xDB"=>[18,"\x9Cu"],"\xDC"=>[0,"\x9C\x3A"],"\xDD"=>[0,"\x9CB"],"\xDE"=>[0,"\x9CC"],"\xDF"=>[0,"\x9CD"],"\xE0"=>[0,"\x9CE"],"\xE1"=>[0,"\x9CF"],"\xE2"=>[0,"\x9CG"],"\xE3"=>[0,"\x9CH"],"\xE4"=>[0,"\x9CI"],"\xE5"=>[0,"\x9CJ"],"\xE6"=>[0,"\x9CK"],"\xE7"=>[0,"\x9CL"],"\xE8"=>[0,"\x9CM"],"\xE9"=>[0,"\x9CN"],"\xEA"=>[0,"\x9CO"],"\xEB"=>[0,"\x9CP"],"\xEC"=>[0,"\x9CQ"],"\xED"=>[0,"\x9CR"],"\xEE"=>[0,"\x9CS"],"\xEF"=>[0,"\x9CT"],"\xF0"=>[0,"\x9CU"],"\xF1"=>[0,"\x9CV"],"\xF2"=>[0,"\x9CW"],"\xF3"=>[0,"\x9CY"],"\xF4"=>[0,"\x9Cj"],"\xF5"=>[0,"\x9Ck"],"\xF6"=>[0,"\x9Cq"],"\xF7"=>[0,"\x9Cv"],"\xF8"=>[0,"\x9Cw"],"\xF9"=>[0,"\x9Cx"],"\xFA"=>[0,"\x9Cy"],"\xFB"=>[0,"\x9Cz"],"\xFC"=>[82,"\x9C"],"\xFD"=>[87,"\x9C"],"\xFE"=>[130,"\x9C"],"\xFF"=>[9,"\x9C"],],["\x00"=>[77,"\x9A0"],"\x01"=>[18,"\x9A0"],"\x02"=>[77,"\x9A1"],"\x03"=>[18,"\x9A1"],"\x04"=>[77,"\x9A2"],"\x05"=>[18,"\x9A2"],"\x06"=>[77,"\x9Aa"],"\x07"=>[18,"\x9Aa"],"\x08"=>[77,"\x9Ac"],"\x09"=>[18,"\x9Ac"],"\x0A"=>[77,"\x9Ae"],"\x0B"=>[18,"\x9Ae"],"\x0C"=>[77,"\x9Ai"],"\x0D"=>[18,"\x9Ai"],"\x0E"=>[77,"\x9Ao"],"\x0F"=>[18,"\x9Ao"],"\x10"=>[77,"\x9As"],"\x11"=>[18,"\x9As"],"\x12"=>[77,"\x9At"],"\x13"=>[18,"\x9At"],"\x14"=>[0,"\x9A\x20"],"\x15"=>[0,"\x9A\x25"],"\x16"=>[0,"\x9A-"],"\x17"=>[0,"\x9A."],"\x18"=>[0,"\x9A\x2F"],"\x19"=>[0,"\x9A3"],"\x1A"=>[0,"\x9A4"],"\x1B"=>[0,"\x9A5"],"\x1C"=>[0,"\x9A6"],"\x1D"=>[0,"\x9A7"],"\x1E"=>[0,"\x9A8"],"\x1F"=>[0,"\x9A9"],"\x20"=>[0,"\x9A\x3D"],"\x21"=>[0,"\x9AA"],"\x22"=>[0,"\x9A_"],"\x23"=>[0,"\x9Ab"],"\x24"=>[0,"\x9Ad"],"\x25"=>[0,"\x9Af"],"\x26"=>[0,"\x9Ag"],"\x27"=>[0,"\x9Ah"],"\x28"=>[0,"\x9Al"],"\x29"=>[0,"\x9Am"],"\x2A"=>[0,"\x9An"],"\x2B"=>[0,"\x9Ap"],"\x2C"=>[0,"\x9Ar"],"-"=>[0,"\x9Au"],"."=>[100,"\x9A"],"\x2F"=>[110,"\x9A"],[111,"\x9A"],[115,"\x9A"],[116,"\x9A"],[118,"\x9A"],[119,"\x9A"],[122,"\x9A"],[123,"\x9A"],[125,"\x9A"],[126,"\x9A"],[129,"\x9A"],"\x3A"=>[143,"\x9A"],"\x3B"=>[148,"\x9A"],"\x3C"=>[151,"\x9A"],"\x3D"=>[153,"\x9A"],"\x3E"=>[83,"\x9A"],"\x3F"=>[10,"\x9A"],"\x40"=>[77,"\x9C0"],"A"=>[18,"\x9C0"],"B"=>[77,"\x9C1"],"C"=>[18,"\x9C1"],"D"=>[77,"\x9C2"],"E"=>[18,"\x9C2"],"F"=>[77,"\x9Ca"],"G"=>[18,"\x9Ca"],"H"=>[77,"\x9Cc"],"I"=>[18,"\x9Cc"],"J"=>[77,"\x9Ce"],"K"=>[18,"\x9Ce"],"L"=>[77,"\x9Ci"],"M"=>[18,"\x9Ci"],"N"=>[77,"\x9Co"],"O"=>[18,"\x9Co"],"P"=>[77,"\x9Cs"],"Q"=>[18,"\x9Cs"],"R"=>[77,"\x9Ct"],"S"=>[18,"\x9Ct"],"T"=>[0,"\x9C\x20"],"U"=>[0,"\x9C\x25"],"V"=>[0,"\x9C-"],"W"=>[0,"\x9C."],"X"=>[0,"\x9C\x2F"],"Y"=>[0,"\x9C3"],"Z"=>[0,"\x9C4"],"\x5B"=>[0,"\x9C5"],"\x5C"=>[0,"\x9C6"],"\x5D"=>[0,"\x9C7"],"\x5E"=>[0,"\x9C8"],"_"=>[0,"\x9C9"],"\x60"=>[0,"\x9C\x3D"],"a"=>[0,"\x9CA"],"b"=>[0,"\x9C_"],"c"=>[0,"\x9Cb"],"d"=>[0,"\x9Cd"],"e"=>[0,"\x9Cf"],"f"=>[0,"\x9Cg"],"g"=>[0,"\x9Ch"],"h"=>[0,"\x9Cl"],"i"=>[0,"\x9Cm"],"j"=>[0,"\x9Cn"],"k"=>[0,"\x9Cp"],"l"=>[0,"\x9Cr"],"m"=>[0,"\x9Cu"],"n"=>[100,"\x9C"],"o"=>[110,"\x9C"],"p"=>[111,"\x9C"],"q"=>[115,"\x9C"],"r"=>[116,"\x9C"],"s"=>[118,"\x9C"],"t"=>[119,"\x9C"],"u"=>[122,"\x9C"],"v"=>[123,"\x9C"],"w"=>[125,"\x9C"],"x"=>[126,"\x9C"],"y"=>[129,"\x9C"],"z"=>[143,"\x9C"],"\x7B"=>[148,"\x9C"],"\x7C"=>[151,"\x9C"],"\x7D"=>[153,"\x9C"],"~"=>[83,"\x9C"],"\x7F"=>[10,"\x9C"],"\x80"=>[77,"\xA00"],"\x81"=>[18,"\xA00"],"\x82"=>[77,"\xA01"],"\x83"=>[18,"\xA01"],"\x84"=>[77,"\xA02"],"\x85"=>[18,"\xA02"],"\x86"=>[77,"\xA0a"],"\x87"=>[18,"\xA0a"],"\x88"=>[77,"\xA0c"],"\x89"=>[18,"\xA0c"],"\x8A"=>[77,"\xA0e"],"\x8B"=>[18,"\xA0e"],"\x8C"=>[77,"\xA0i"],"\x8D"=>[18,"\xA0i"],"\x8E"=>[77,"\xA0o"],"\x8F"=>[18,"\xA0o"],"\x90"=>[77,"\xA0s"],"\x91"=>[18,"\xA0s"],"\x92"=>[77,"\xA0t"],"\x93"=>[18,"\xA0t"],"\x94"=>[0,"\xA0\x20"],"\x95"=>[0,"\xA0\x25"],"\x96"=>[0,"\xA0-"],"\x97"=>[0,"\xA0."],"\x98"=>[0,"\xA0\x2F"],"\x99"=>[0,"\xA03"],"\x9A"=>[0,"\xA04"],"\x9B"=>[0,"\xA05"],"\x9C"=>[0,"\xA06"],"\x9D"=>[0,"\xA07"],"\x9E"=>[0,"\xA08"],"\x9F"=>[0,"\xA09"],"\xA0"=>[0,"\xA0\x3D"],"\xA1"=>[0,"\xA0A"],"\xA2"=>[0,"\xA0_"],"\xA3"=>[0,"\xA0b"],"\xA4"=>[0,"\xA0d"],"\xA5"=>[0,"\xA0f"],"\xA6"=>[0,"\xA0g"],"\xA7"=>[0,"\xA0h"],"\xA8"=>[0,"\xA0l"],"\xA9"=>[0,"\xA0m"],"\xAA"=>[0,"\xA0n"],"\xAB"=>[0,"\xA0p"],"\xAC"=>[0,"\xA0r"],"\xAD"=>[0,"\xA0u"],"\xAE"=>[100,"\xA0"],"\xAF"=>[110,"\xA0"],"\xB0"=>[111,"\xA0"],"\xB1"=>[115,"\xA0"],"\xB2"=>[116,"\xA0"],"\xB3"=>[118,"\xA0"],"\xB4"=>[119,"\xA0"],"\xB5"=>[122,"\xA0"],"\xB6"=>[123,"\xA0"],"\xB7"=>[125,"\xA0"],"\xB8"=>[126,"\xA0"],"\xB9"=>[129,"\xA0"],"\xBA"=>[143,"\xA0"],"\xBB"=>[148,"\xA0"],"\xBC"=>[151,"\xA0"],"\xBD"=>[153,"\xA0"],"\xBE"=>[83,"\xA0"],"\xBF"=>[10,"\xA0"],"\xC0"=>[77,"\xA30"],"\xC1"=>[18,"\xA30"],"\xC2"=>[77,"\xA31"],"\xC3"=>[18,"\xA31"],"\xC4"=>[77,"\xA32"],"\xC5"=>[18,"\xA32"],"\xC6"=>[77,"\xA3a"],"\xC7"=>[18,"\xA3a"],"\xC8"=>[77,"\xA3c"],"\xC9"=>[18,"\xA3c"],"\xCA"=>[77,"\xA3e"],"\xCB"=>[18,"\xA3e"],"\xCC"=>[77,"\xA3i"],"\xCD"=>[18,"\xA3i"],"\xCE"=>[77,"\xA3o"],"\xCF"=>[18,"\xA3o"],"\xD0"=>[77,"\xA3s"],"\xD1"=>[18,"\xA3s"],"\xD2"=>[77,"\xA3t"],"\xD3"=>[18,"\xA3t"],"\xD4"=>[0,"\xA3\x20"],"\xD5"=>[0,"\xA3\x25"],"\xD6"=>[0,"\xA3-"],"\xD7"=>[0,"\xA3."],"\xD8"=>[0,"\xA3\x2F"],"\xD9"=>[0,"\xA33"],"\xDA"=>[0,"\xA34"],"\xDB"=>[0,"\xA35"],"\xDC"=>[0,"\xA36"],"\xDD"=>[0,"\xA37"],"\xDE"=>[0,"\xA38"],"\xDF"=>[0,"\xA39"],"\xE0"=>[0,"\xA3\x3D"],"\xE1"=>[0,"\xA3A"],"\xE2"=>[0,"\xA3_"],"\xE3"=>[0,"\xA3b"],"\xE4"=>[0,"\xA3d"],"\xE5"=>[0,"\xA3f"],"\xE6"=>[0,"\xA3g"],"\xE7"=>[0,"\xA3h"],"\xE8"=>[0,"\xA3l"],"\xE9"=>[0,"\xA3m"],"\xEA"=>[0,"\xA3n"],"\xEB"=>[0,"\xA3p"],"\xEC"=>[0,"\xA3r"],"\xED"=>[0,"\xA3u"],"\xEE"=>[100,"\xA3"],"\xEF"=>[110,"\xA3"],"\xF0"=>[111,"\xA3"],"\xF1"=>[115,"\xA3"],"\xF2"=>[116,"\xA3"],"\xF3"=>[118,"\xA3"],"\xF4"=>[119,"\xA3"],"\xF5"=>[122,"\xA3"],"\xF6"=>[123,"\xA3"],"\xF7"=>[125,"\xA3"],"\xF8"=>[126,"\xA3"],"\xF9"=>[129,"\xA3"],"\xFA"=>[143,"\xA3"],"\xFB"=>[148,"\xA3"],"\xFC"=>[151,"\xA3"],"\xFD"=>[153,"\xA3"],"\xFE"=>[83,"\xA3"],"\xFF"=>[10,"\xA3"],],["\x00"=>[0,"\x9A0"],"\x01"=>[0,"\x9A1"],"\x02"=>[0,"\x9A2"],"\x03"=>[0,"\x9Aa"],"\x04"=>[0,"\x9Ac"],"\x05"=>[0,"\x9Ae"],"\x06"=>[0,"\x9Ai"],"\x07"=>[0,"\x9Ao"],"\x08"=>[0,"\x9As"],"\x09"=>[0,"\x9At"],"\x0A"=>[73,"\x9A"],"\x0B"=>[88,"\x9A"],"\x0C"=>[89,"\x9A"],"\x0D"=>[96,"\x9A"],"\x0E"=>[97,"\x9A"],"\x0F"=>[99,"\x9A"],"\x10"=>[106,"\x9A"],"\x11"=>[136,"\x9A"],"\x12"=>[139,"\x9A"],"\x13"=>[141,"\x9A"],"\x14"=>[145,"\x9A"],"\x15"=>[147,"\x9A"],"\x16"=>[149,"\x9A"],"\x17"=>[101,"\x9A"],"\x18"=>[112,"\x9A"],"\x19"=>[117,"\x9A"],"\x1A"=>[120,"\x9A"],"\x1B"=>[124,"\x9A"],"\x1C"=>[127,"\x9A"],"\x1D"=>[144,"\x9A"],"\x1E"=>[152,"\x9A"],"\x1F"=>[11,"\x9A"],"\x20"=>[0,"\x9C0"],"\x21"=>[0,"\x9C1"],"\x22"=>[0,"\x9C2"],"\x23"=>[0,"\x9Ca"],"\x24"=>[0,"\x9Cc"],"\x25"=>[0,"\x9Ce"],"\x26"=>[0,"\x9Ci"],"\x27"=>[0,"\x9Co"],"\x28"=>[0,"\x9Cs"],"\x29"=>[0,"\x9Ct"],"\x2A"=>[73,"\x9C"],"\x2B"=>[88,"\x9C"],"\x2C"=>[89,"\x9C"],"-"=>[96,"\x9C"],"."=>[97,"\x9C"],"\x2F"=>[99,"\x9C"],[106,"\x9C"],[136,"\x9C"],[139,"\x9C"],[141,"\x9C"],[145,"\x9C"],[147,"\x9C"],[149,"\x9C"],[101,"\x9C"],[112,"\x9C"],[117,"\x9C"],"\x3A"=>[120,"\x9C"],"\x3B"=>[124,"\x9C"],"\x3C"=>[127,"\x9C"],"\x3D"=>[144,"\x9C"],"\x3E"=>[152,"\x9C"],"\x3F"=>[11,"\x9C"],"\x40"=>[0,"\xA00"],"A"=>[0,"\xA01"],"B"=>[0,"\xA02"],"C"=>[0,"\xA0a"],"D"=>[0,"\xA0c"],"E"=>[0,"\xA0e"],"F"=>[0,"\xA0i"],"G"=>[0,"\xA0o"],"H"=>[0,"\xA0s"],"I"=>[0,"\xA0t"],"J"=>[73,"\xA0"],"K"=>[88,"\xA0"],"L"=>[89,"\xA0"],"M"=>[96,"\xA0"],"N"=>[97,"\xA0"],"O"=>[99,"\xA0"],"P"=>[106,"\xA0"],"Q"=>[136,"\xA0"],"R"=>[139,"\xA0"],"S"=>[141,"\xA0"],"T"=>[145,"\xA0"],"U"=>[147,"\xA0"],"V"=>[149,"\xA0"],"W"=>[101,"\xA0"],"X"=>[112,"\xA0"],"Y"=>[117,"\xA0"],"Z"=>[120,"\xA0"],"\x5B"=>[124,"\xA0"],"\x5C"=>[127,"\xA0"],"\x5D"=>[144,"\xA0"],"\x5E"=>[152,"\xA0"],"_"=>[11,"\xA0"],"\x60"=>[0,"\xA30"],"a"=>[0,"\xA31"],"b"=>[0,"\xA32"],"c"=>[0,"\xA3a"],"d"=>[0,"\xA3c"],"e"=>[0,"\xA3e"],"f"=>[0,"\xA3i"],"g"=>[0,"\xA3o"],"h"=>[0,"\xA3s"],"i"=>[0,"\xA3t"],"j"=>[73,"\xA3"],"k"=>[88,"\xA3"],"l"=>[89,"\xA3"],"m"=>[96,"\xA3"],"n"=>[97,"\xA3"],"o"=>[99,"\xA3"],"p"=>[106,"\xA3"],"q"=>[136,"\xA3"],"r"=>[139,"\xA3"],"s"=>[141,"\xA3"],"t"=>[145,"\xA3"],"u"=>[147,"\xA3"],"v"=>[149,"\xA3"],"w"=>[101,"\xA3"],"x"=>[112,"\xA3"],"y"=>[117,"\xA3"],"z"=>[120,"\xA3"],"\x7B"=>[124,"\xA3"],"\x7C"=>[127,"\xA3"],"\x7D"=>[144,"\xA3"],"~"=>[152,"\xA3"],"\x7F"=>[11,"\xA3"],"\x80"=>[0,"\xA40"],"\x81"=>[0,"\xA41"],"\x82"=>[0,"\xA42"],"\x83"=>[0,"\xA4a"],"\x84"=>[0,"\xA4c"],"\x85"=>[0,"\xA4e"],"\x86"=>[0,"\xA4i"],"\x87"=>[0,"\xA4o"],"\x88"=>[0,"\xA4s"],"\x89"=>[0,"\xA4t"],"\x8A"=>[73,"\xA4"],"\x8B"=>[88,"\xA4"],"\x8C"=>[89,"\xA4"],"\x8D"=>[96,"\xA4"],"\x8E"=>[97,"\xA4"],"\x8F"=>[99,"\xA4"],"\x90"=>[106,"\xA4"],"\x91"=>[136,"\xA4"],"\x92"=>[139,"\xA4"],"\x93"=>[141,"\xA4"],"\x94"=>[145,"\xA4"],"\x95"=>[147,"\xA4"],"\x96"=>[149,"\xA4"],"\x97"=>[101,"\xA4"],"\x98"=>[112,"\xA4"],"\x99"=>[117,"\xA4"],"\x9A"=>[120,"\xA4"],"\x9B"=>[124,"\xA4"],"\x9C"=>[127,"\xA4"],"\x9D"=>[144,"\xA4"],"\x9E"=>[152,"\xA4"],"\x9F"=>[11,"\xA4"],"\xA0"=>[0,"\xA90"],"\xA1"=>[0,"\xA91"],"\xA2"=>[0,"\xA92"],"\xA3"=>[0,"\xA9a"],"\xA4"=>[0,"\xA9c"],"\xA5"=>[0,"\xA9e"],"\xA6"=>[0,"\xA9i"],"\xA7"=>[0,"\xA9o"],"\xA8"=>[0,"\xA9s"],"\xA9"=>[0,"\xA9t"],"\xAA"=>[73,"\xA9"],"\xAB"=>[88,"\xA9"],"\xAC"=>[89,"\xA9"],"\xAD"=>[96,"\xA9"],"\xAE"=>[97,"\xA9"],"\xAF"=>[99,"\xA9"],"\xB0"=>[106,"\xA9"],"\xB1"=>[136,"\xA9"],"\xB2"=>[139,"\xA9"],"\xB3"=>[141,"\xA9"],"\xB4"=>[145,"\xA9"],"\xB5"=>[147,"\xA9"],"\xB6"=>[149,"\xA9"],"\xB7"=>[101,"\xA9"],"\xB8"=>[112,"\xA9"],"\xB9"=>[117,"\xA9"],"\xBA"=>[120,"\xA9"],"\xBB"=>[124,"\xA9"],"\xBC"=>[127,"\xA9"],"\xBD"=>[144,"\xA9"],"\xBE"=>[152,"\xA9"],"\xBF"=>[11,"\xA9"],"\xC0"=>[0,"\xAA0"],"\xC1"=>[0,"\xAA1"],"\xC2"=>[0,"\xAA2"],"\xC3"=>[0,"\xAAa"],"\xC4"=>[0,"\xAAc"],"\xC5"=>[0,"\xAAe"],"\xC6"=>[0,"\xAAi"],"\xC7"=>[0,"\xAAo"],"\xC8"=>[0,"\xAAs"],"\xC9"=>[0,"\xAAt"],"\xCA"=>[73,"\xAA"],"\xCB"=>[88,"\xAA"],"\xCC"=>[89,"\xAA"],"\xCD"=>[96,"\xAA"],"\xCE"=>[97,"\xAA"],"\xCF"=>[99,"\xAA"],"\xD0"=>[106,"\xAA"],"\xD1"=>[136,"\xAA"],"\xD2"=>[139,"\xAA"],"\xD3"=>[141,"\xAA"],"\xD4"=>[145,"\xAA"],"\xD5"=>[147,"\xAA"],"\xD6"=>[149,"\xAA"],"\xD7"=>[101,"\xAA"],"\xD8"=>[112,"\xAA"],"\xD9"=>[117,"\xAA"],"\xDA"=>[120,"\xAA"],"\xDB"=>[124,"\xAA"],"\xDC"=>[127,"\xAA"],"\xDD"=>[144,"\xAA"],"\xDE"=>[152,"\xAA"],"\xDF"=>[11,"\xAA"],"\xE0"=>[0,"\xAD0"],"\xE1"=>[0,"\xAD1"],"\xE2"=>[0,"\xAD2"],"\xE3"=>[0,"\xADa"],"\xE4"=>[0,"\xADc"],"\xE5"=>[0,"\xADe"],"\xE6"=>[0,"\xADi"],"\xE7"=>[0,"\xADo"],"\xE8"=>[0,"\xADs"],"\xE9"=>[0,"\xADt"],"\xEA"=>[73,"\xAD"],"\xEB"=>[88,"\xAD"],"\xEC"=>[89,"\xAD"],"\xED"=>[96,"\xAD"],"\xEE"=>[97,"\xAD"],"\xEF"=>[99,"\xAD"],"\xF0"=>[106,"\xAD"],"\xF1"=>[136,"\xAD"],"\xF2"=>[139,"\xAD"],"\xF3"=>[141,"\xAD"],"\xF4"=>[145,"\xAD"],"\xF5"=>[147,"\xAD"],"\xF6"=>[149,"\xAD"],"\xF7"=>[101,"\xAD"],"\xF8"=>[112,"\xAD"],"\xF9"=>[117,"\xAD"],"\xFA"=>[120,"\xAD"],"\xFB"=>[124,"\xAD"],"\xFC"=>[127,"\xAD"],"\xFD"=>[144,"\xAD"],"\xFE"=>[152,"\xAD"],"\xFF"=>[11,"\xAD"],],["\x00"=>[94,"\x9D0"],"\x01"=>[76,"\x9D0"],"\x02"=>[104,"\x9D0"],"\x03"=>[16,"\x9D0"],"\x04"=>[94,"\x9D1"],"\x05"=>[76,"\x9D1"],"\x06"=>[104,"\x9D1"],"\x07"=>[16,"\x9D1"],"\x08"=>[94,"\x9D2"],"\x09"=>[76,"\x9D2"],"\x0A"=>[104,"\x9D2"],"\x0B"=>[16,"\x9D2"],"\x0C"=>[94,"\x9Da"],"\x0D"=>[76,"\x9Da"],"\x0E"=>[104,"\x9Da"],"\x0F"=>[16,"\x9Da"],"\x10"=>[94,"\x9Dc"],"\x11"=>[76,"\x9Dc"],"\x12"=>[104,"\x9Dc"],"\x13"=>[16,"\x9Dc"],"\x14"=>[94,"\x9De"],"\x15"=>[76,"\x9De"],"\x16"=>[104,"\x9De"],"\x17"=>[16,"\x9De"],"\x18"=>[94,"\x9Di"],"\x19"=>[76,"\x9Di"],"\x1A"=>[104,"\x9Di"],"\x1B"=>[16,"\x9Di"],"\x1C"=>[94,"\x9Do"],"\x1D"=>[76,"\x9Do"],"\x1E"=>[104,"\x9Do"],"\x1F"=>[16,"\x9Do"],"\x20"=>[94,"\x9Ds"],"\x21"=>[76,"\x9Ds"],"\x22"=>[104,"\x9Ds"],"\x23"=>[16,"\x9Ds"],"\x24"=>[94,"\x9Dt"],"\x25"=>[76,"\x9Dt"],"\x26"=>[104,"\x9Dt"],"\x27"=>[16,"\x9Dt"],"\x28"=>[77,"\x9D\x20"],"\x29"=>[18,"\x9D\x20"],"\x2A"=>[77,"\x9D\x25"],"\x2B"=>[18,"\x9D\x25"],"\x2C"=>[77,"\x9D-"],"-"=>[18,"\x9D-"],"."=>[77,"\x9D."],"\x2F"=>[18,"\x9D."],[77,"\x9D\x2F"],[18,"\x9D\x2F"],[77,"\x9D3"],[18,"\x9D3"],[77,"\x9D4"],[18,"\x9D4"],[77,"\x9D5"],[18,"\x9D5"],[77,"\x9D6"],[18,"\x9D6"],"\x3A"=>[77,"\x9D7"],"\x3B"=>[18,"\x9D7"],"\x3C"=>[77,"\x9D8"],"\x3D"=>[18,"\x9D8"],"\x3E"=>[77,"\x9D9"],"\x3F"=>[18,"\x9D9"],"\x40"=>[77,"\x9D\x3D"],"A"=>[18,"\x9D\x3D"],"B"=>[77,"\x9DA"],"C"=>[18,"\x9DA"],"D"=>[77,"\x9D_"],"E"=>[18,"\x9D_"],"F"=>[77,"\x9Db"],"G"=>[18,"\x9Db"],"H"=>[77,"\x9Dd"],"I"=>[18,"\x9Dd"],"J"=>[77,"\x9Df"],"K"=>[18,"\x9Df"],"L"=>[77,"\x9Dg"],"M"=>[18,"\x9Dg"],"N"=>[77,"\x9Dh"],"O"=>[18,"\x9Dh"],"P"=>[77,"\x9Dl"],"Q"=>[18,"\x9Dl"],"R"=>[77,"\x9Dm"],"S"=>[18,"\x9Dm"],"T"=>[77,"\x9Dn"],"U"=>[18,"\x9Dn"],"V"=>[77,"\x9Dp"],"W"=>[18,"\x9Dp"],"X"=>[77,"\x9Dr"],"Y"=>[18,"\x9Dr"],"Z"=>[77,"\x9Du"],"\x5B"=>[18,"\x9Du"],"\x5C"=>[0,"\x9D\x3A"],"\x5D"=>[0,"\x9DB"],"\x5E"=>[0,"\x9DC"],"_"=>[0,"\x9DD"],"\x60"=>[0,"\x9DE"],"a"=>[0,"\x9DF"],"b"=>[0,"\x9DG"],"c"=>[0,"\x9DH"],"d"=>[0,"\x9DI"],"e"=>[0,"\x9DJ"],"f"=>[0,"\x9DK"],"g"=>[0,"\x9DL"],"h"=>[0,"\x9DM"],"i"=>[0,"\x9DN"],"j"=>[0,"\x9DO"],"k"=>[0,"\x9DP"],"l"=>[0,"\x9DQ"],"m"=>[0,"\x9DR"],"n"=>[0,"\x9DS"],"o"=>[0,"\x9DT"],"p"=>[0,"\x9DU"],"q"=>[0,"\x9DV"],"r"=>[0,"\x9DW"],"s"=>[0,"\x9DY"],"t"=>[0,"\x9Dj"],"u"=>[0,"\x9Dk"],"v"=>[0,"\x9Dq"],"w"=>[0,"\x9Dv"],"x"=>[0,"\x9Dw"],"y"=>[0,"\x9Dx"],"z"=>[0,"\x9Dy"],"\x7B"=>[0,"\x9Dz"],"\x7C"=>[82,"\x9D"],"\x7D"=>[87,"\x9D"],"~"=>[130,"\x9D"],"\x7F"=>[9,"\x9D"],"\x80"=>[94,"\x9E0"],"\x81"=>[76,"\x9E0"],"\x82"=>[104,"\x9E0"],"\x83"=>[16,"\x9E0"],"\x84"=>[94,"\x9E1"],"\x85"=>[76,"\x9E1"],"\x86"=>[104,"\x9E1"],"\x87"=>[16,"\x9E1"],"\x88"=>[94,"\x9E2"],"\x89"=>[76,"\x9E2"],"\x8A"=>[104,"\x9E2"],"\x8B"=>[16,"\x9E2"],"\x8C"=>[94,"\x9Ea"],"\x8D"=>[76,"\x9Ea"],"\x8E"=>[104,"\x9Ea"],"\x8F"=>[16,"\x9Ea"],"\x90"=>[94,"\x9Ec"],"\x91"=>[76,"\x9Ec"],"\x92"=>[104,"\x9Ec"],"\x93"=>[16,"\x9Ec"],"\x94"=>[94,"\x9Ee"],"\x95"=>[76,"\x9Ee"],"\x96"=>[104,"\x9Ee"],"\x97"=>[16,"\x9Ee"],"\x98"=>[94,"\x9Ei"],"\x99"=>[76,"\x9Ei"],"\x9A"=>[104,"\x9Ei"],"\x9B"=>[16,"\x9Ei"],"\x9C"=>[94,"\x9Eo"],"\x9D"=>[76,"\x9Eo"],"\x9E"=>[104,"\x9Eo"],"\x9F"=>[16,"\x9Eo"],"\xA0"=>[94,"\x9Es"],"\xA1"=>[76,"\x9Es"],"\xA2"=>[104,"\x9Es"],"\xA3"=>[16,"\x9Es"],"\xA4"=>[94,"\x9Et"],"\xA5"=>[76,"\x9Et"],"\xA6"=>[104,"\x9Et"],"\xA7"=>[16,"\x9Et"],"\xA8"=>[77,"\x9E\x20"],"\xA9"=>[18,"\x9E\x20"],"\xAA"=>[77,"\x9E\x25"],"\xAB"=>[18,"\x9E\x25"],"\xAC"=>[77,"\x9E-"],"\xAD"=>[18,"\x9E-"],"\xAE"=>[77,"\x9E."],"\xAF"=>[18,"\x9E."],"\xB0"=>[77,"\x9E\x2F"],"\xB1"=>[18,"\x9E\x2F"],"\xB2"=>[77,"\x9E3"],"\xB3"=>[18,"\x9E3"],"\xB4"=>[77,"\x9E4"],"\xB5"=>[18,"\x9E4"],"\xB6"=>[77,"\x9E5"],"\xB7"=>[18,"\x9E5"],"\xB8"=>[77,"\x9E6"],"\xB9"=>[18,"\x9E6"],"\xBA"=>[77,"\x9E7"],"\xBB"=>[18,"\x9E7"],"\xBC"=>[77,"\x9E8"],"\xBD"=>[18,"\x9E8"],"\xBE"=>[77,"\x9E9"],"\xBF"=>[18,"\x9E9"],"\xC0"=>[77,"\x9E\x3D"],"\xC1"=>[18,"\x9E\x3D"],"\xC2"=>[77,"\x9EA"],"\xC3"=>[18,"\x9EA"],"\xC4"=>[77,"\x9E_"],"\xC5"=>[18,"\x9E_"],"\xC6"=>[77,"\x9Eb"],"\xC7"=>[18,"\x9Eb"],"\xC8"=>[77,"\x9Ed"],"\xC9"=>[18,"\x9Ed"],"\xCA"=>[77,"\x9Ef"],"\xCB"=>[18,"\x9Ef"],"\xCC"=>[77,"\x9Eg"],"\xCD"=>[18,"\x9Eg"],"\xCE"=>[77,"\x9Eh"],"\xCF"=>[18,"\x9Eh"],"\xD0"=>[77,"\x9El"],"\xD1"=>[18,"\x9El"],"\xD2"=>[77,"\x9Em"],"\xD3"=>[18,"\x9Em"],"\xD4"=>[77,"\x9En"],"\xD5"=>[18,"\x9En"],"\xD6"=>[77,"\x9Ep"],"\xD7"=>[18,"\x9Ep"],"\xD8"=>[77,"\x9Er"],"\xD9"=>[18,"\x9Er"],"\xDA"=>[77,"\x9Eu"],"\xDB"=>[18,"\x9Eu"],"\xDC"=>[0,"\x9E\x3A"],"\xDD"=>[0,"\x9EB"],"\xDE"=>[0,"\x9EC"],"\xDF"=>[0,"\x9ED"],"\xE0"=>[0,"\x9EE"],"\xE1"=>[0,"\x9EF"],"\xE2"=>[0,"\x9EG"],"\xE3"=>[0,"\x9EH"],"\xE4"=>[0,"\x9EI"],"\xE5"=>[0,"\x9EJ"],"\xE6"=>[0,"\x9EK"],"\xE7"=>[0,"\x9EL"],"\xE8"=>[0,"\x9EM"],"\xE9"=>[0,"\x9EN"],"\xEA"=>[0,"\x9EO"],"\xEB"=>[0,"\x9EP"],"\xEC"=>[0,"\x9EQ"],"\xED"=>[0,"\x9ER"],"\xEE"=>[0,"\x9ES"],"\xEF"=>[0,"\x9ET"],"\xF0"=>[0,"\x9EU"],"\xF1"=>[0,"\x9EV"],"\xF2"=>[0,"\x9EW"],"\xF3"=>[0,"\x9EY"],"\xF4"=>[0,"\x9Ej"],"\xF5"=>[0,"\x9Ek"],"\xF6"=>[0,"\x9Eq"],"\xF7"=>[0,"\x9Ev"],"\xF8"=>[0,"\x9Ew"],"\xF9"=>[0,"\x9Ex"],"\xFA"=>[0,"\x9Ey"],"\xFB"=>[0,"\x9Ez"],"\xFC"=>[82,"\x9E"],"\xFD"=>[87,"\x9E"],"\xFE"=>[130,"\x9E"],"\xFF"=>[9,"\x9E"],],["\x00"=>[94,"\xA00"],"\x01"=>[76,"\xA00"],"\x02"=>[104,"\xA00"],"\x03"=>[16,"\xA00"],"\x04"=>[94,"\xA01"],"\x05"=>[76,"\xA01"],"\x06"=>[104,"\xA01"],"\x07"=>[16,"\xA01"],"\x08"=>[94,"\xA02"],"\x09"=>[76,"\xA02"],"\x0A"=>[104,"\xA02"],"\x0B"=>[16,"\xA02"],"\x0C"=>[94,"\xA0a"],"\x0D"=>[76,"\xA0a"],"\x0E"=>[104,"\xA0a"],"\x0F"=>[16,"\xA0a"],"\x10"=>[94,"\xA0c"],"\x11"=>[76,"\xA0c"],"\x12"=>[104,"\xA0c"],"\x13"=>[16,"\xA0c"],"\x14"=>[94,"\xA0e"],"\x15"=>[76,"\xA0e"],"\x16"=>[104,"\xA0e"],"\x17"=>[16,"\xA0e"],"\x18"=>[94,"\xA0i"],"\x19"=>[76,"\xA0i"],"\x1A"=>[104,"\xA0i"],"\x1B"=>[16,"\xA0i"],"\x1C"=>[94,"\xA0o"],"\x1D"=>[76,"\xA0o"],"\x1E"=>[104,"\xA0o"],"\x1F"=>[16,"\xA0o"],"\x20"=>[94,"\xA0s"],"\x21"=>[76,"\xA0s"],"\x22"=>[104,"\xA0s"],"\x23"=>[16,"\xA0s"],"\x24"=>[94,"\xA0t"],"\x25"=>[76,"\xA0t"],"\x26"=>[104,"\xA0t"],"\x27"=>[16,"\xA0t"],"\x28"=>[77,"\xA0\x20"],"\x29"=>[18,"\xA0\x20"],"\x2A"=>[77,"\xA0\x25"],"\x2B"=>[18,"\xA0\x25"],"\x2C"=>[77,"\xA0-"],"-"=>[18,"\xA0-"],"."=>[77,"\xA0."],"\x2F"=>[18,"\xA0."],[77,"\xA0\x2F"],[18,"\xA0\x2F"],[77,"\xA03"],[18,"\xA03"],[77,"\xA04"],[18,"\xA04"],[77,"\xA05"],[18,"\xA05"],[77,"\xA06"],[18,"\xA06"],"\x3A"=>[77,"\xA07"],"\x3B"=>[18,"\xA07"],"\x3C"=>[77,"\xA08"],"\x3D"=>[18,"\xA08"],"\x3E"=>[77,"\xA09"],"\x3F"=>[18,"\xA09"],"\x40"=>[77,"\xA0\x3D"],"A"=>[18,"\xA0\x3D"],"B"=>[77,"\xA0A"],"C"=>[18,"\xA0A"],"D"=>[77,"\xA0_"],"E"=>[18,"\xA0_"],"F"=>[77,"\xA0b"],"G"=>[18,"\xA0b"],"H"=>[77,"\xA0d"],"I"=>[18,"\xA0d"],"J"=>[77,"\xA0f"],"K"=>[18,"\xA0f"],"L"=>[77,"\xA0g"],"M"=>[18,"\xA0g"],"N"=>[77,"\xA0h"],"O"=>[18,"\xA0h"],"P"=>[77,"\xA0l"],"Q"=>[18,"\xA0l"],"R"=>[77,"\xA0m"],"S"=>[18,"\xA0m"],"T"=>[77,"\xA0n"],"U"=>[18,"\xA0n"],"V"=>[77,"\xA0p"],"W"=>[18,"\xA0p"],"X"=>[77,"\xA0r"],"Y"=>[18,"\xA0r"],"Z"=>[77,"\xA0u"],"\x5B"=>[18,"\xA0u"],"\x5C"=>[0,"\xA0\x3A"],"\x5D"=>[0,"\xA0B"],"\x5E"=>[0,"\xA0C"],"_"=>[0,"\xA0D"],"\x60"=>[0,"\xA0E"],"a"=>[0,"\xA0F"],"b"=>[0,"\xA0G"],"c"=>[0,"\xA0H"],"d"=>[0,"\xA0I"],"e"=>[0,"\xA0J"],"f"=>[0,"\xA0K"],"g"=>[0,"\xA0L"],"h"=>[0,"\xA0M"],"i"=>[0,"\xA0N"],"j"=>[0,"\xA0O"],"k"=>[0,"\xA0P"],"l"=>[0,"\xA0Q"],"m"=>[0,"\xA0R"],"n"=>[0,"\xA0S"],"o"=>[0,"\xA0T"],"p"=>[0,"\xA0U"],"q"=>[0,"\xA0V"],"r"=>[0,"\xA0W"],"s"=>[0,"\xA0Y"],"t"=>[0,"\xA0j"],"u"=>[0,"\xA0k"],"v"=>[0,"\xA0q"],"w"=>[0,"\xA0v"],"x"=>[0,"\xA0w"],"y"=>[0,"\xA0x"],"z"=>[0,"\xA0y"],"\x7B"=>[0,"\xA0z"],"\x7C"=>[82,"\xA0"],"\x7D"=>[87,"\xA0"],"~"=>[130,"\xA0"],"\x7F"=>[9,"\xA0"],"\x80"=>[94,"\xA30"],"\x81"=>[76,"\xA30"],"\x82"=>[104,"\xA30"],"\x83"=>[16,"\xA30"],"\x84"=>[94,"\xA31"],"\x85"=>[76,"\xA31"],"\x86"=>[104,"\xA31"],"\x87"=>[16,"\xA31"],"\x88"=>[94,"\xA32"],"\x89"=>[76,"\xA32"],"\x8A"=>[104,"\xA32"],"\x8B"=>[16,"\xA32"],"\x8C"=>[94,"\xA3a"],"\x8D"=>[76,"\xA3a"],"\x8E"=>[104,"\xA3a"],"\x8F"=>[16,"\xA3a"],"\x90"=>[94,"\xA3c"],"\x91"=>[76,"\xA3c"],"\x92"=>[104,"\xA3c"],"\x93"=>[16,"\xA3c"],"\x94"=>[94,"\xA3e"],"\x95"=>[76,"\xA3e"],"\x96"=>[104,"\xA3e"],"\x97"=>[16,"\xA3e"],"\x98"=>[94,"\xA3i"],"\x99"=>[76,"\xA3i"],"\x9A"=>[104,"\xA3i"],"\x9B"=>[16,"\xA3i"],"\x9C"=>[94,"\xA3o"],"\x9D"=>[76,"\xA3o"],"\x9E"=>[104,"\xA3o"],"\x9F"=>[16,"\xA3o"],"\xA0"=>[94,"\xA3s"],"\xA1"=>[76,"\xA3s"],"\xA2"=>[104,"\xA3s"],"\xA3"=>[16,"\xA3s"],"\xA4"=>[94,"\xA3t"],"\xA5"=>[76,"\xA3t"],"\xA6"=>[104,"\xA3t"],"\xA7"=>[16,"\xA3t"],"\xA8"=>[77,"\xA3\x20"],"\xA9"=>[18,"\xA3\x20"],"\xAA"=>[77,"\xA3\x25"],"\xAB"=>[18,"\xA3\x25"],"\xAC"=>[77,"\xA3-"],"\xAD"=>[18,"\xA3-"],"\xAE"=>[77,"\xA3."],"\xAF"=>[18,"\xA3."],"\xB0"=>[77,"\xA3\x2F"],"\xB1"=>[18,"\xA3\x2F"],"\xB2"=>[77,"\xA33"],"\xB3"=>[18,"\xA33"],"\xB4"=>[77,"\xA34"],"\xB5"=>[18,"\xA34"],"\xB6"=>[77,"\xA35"],"\xB7"=>[18,"\xA35"],"\xB8"=>[77,"\xA36"],"\xB9"=>[18,"\xA36"],"\xBA"=>[77,"\xA37"],"\xBB"=>[18,"\xA37"],"\xBC"=>[77,"\xA38"],"\xBD"=>[18,"\xA38"],"\xBE"=>[77,"\xA39"],"\xBF"=>[18,"\xA39"],"\xC0"=>[77,"\xA3\x3D"],"\xC1"=>[18,"\xA3\x3D"],"\xC2"=>[77,"\xA3A"],"\xC3"=>[18,"\xA3A"],"\xC4"=>[77,"\xA3_"],"\xC5"=>[18,"\xA3_"],"\xC6"=>[77,"\xA3b"],"\xC7"=>[18,"\xA3b"],"\xC8"=>[77,"\xA3d"],"\xC9"=>[18,"\xA3d"],"\xCA"=>[77,"\xA3f"],"\xCB"=>[18,"\xA3f"],"\xCC"=>[77,"\xA3g"],"\xCD"=>[18,"\xA3g"],"\xCE"=>[77,"\xA3h"],"\xCF"=>[18,"\xA3h"],"\xD0"=>[77,"\xA3l"],"\xD1"=>[18,"\xA3l"],"\xD2"=>[77,"\xA3m"],"\xD3"=>[18,"\xA3m"],"\xD4"=>[77,"\xA3n"],"\xD5"=>[18,"\xA3n"],"\xD6"=>[77,"\xA3p"],"\xD7"=>[18,"\xA3p"],"\xD8"=>[77,"\xA3r"],"\xD9"=>[18,"\xA3r"],"\xDA"=>[77,"\xA3u"],"\xDB"=>[18,"\xA3u"],"\xDC"=>[0,"\xA3\x3A"],"\xDD"=>[0,"\xA3B"],"\xDE"=>[0,"\xA3C"],"\xDF"=>[0,"\xA3D"],"\xE0"=>[0,"\xA3E"],"\xE1"=>[0,"\xA3F"],"\xE2"=>[0,"\xA3G"],"\xE3"=>[0,"\xA3H"],"\xE4"=>[0,"\xA3I"],"\xE5"=>[0,"\xA3J"],"\xE6"=>[0,"\xA3K"],"\xE7"=>[0,"\xA3L"],"\xE8"=>[0,"\xA3M"],"\xE9"=>[0,"\xA3N"],"\xEA"=>[0,"\xA3O"],"\xEB"=>[0,"\xA3P"],"\xEC"=>[0,"\xA3Q"],"\xED"=>[0,"\xA3R"],"\xEE"=>[0,"\xA3S"],"\xEF"=>[0,"\xA3T"],"\xF0"=>[0,"\xA3U"],"\xF1"=>[0,"\xA3V"],"\xF2"=>[0,"\xA3W"],"\xF3"=>[0,"\xA3Y"],"\xF4"=>[0,"\xA3j"],"\xF5"=>[0,"\xA3k"],"\xF6"=>[0,"\xA3q"],"\xF7"=>[0,"\xA3v"],"\xF8"=>[0,"\xA3w"],"\xF9"=>[0,"\xA3x"],"\xFA"=>[0,"\xA3y"],"\xFB"=>[0,"\xA3z"],"\xFC"=>[82,"\xA3"],"\xFD"=>[87,"\xA3"],"\xFE"=>[130,"\xA3"],"\xFF"=>[9,"\xA3"],],["\x00"=>[94,"\xA40"],"\x01"=>[76,"\xA40"],"\x02"=>[104,"\xA40"],"\x03"=>[16,"\xA40"],"\x04"=>[94,"\xA41"],"\x05"=>[76,"\xA41"],"\x06"=>[104,"\xA41"],"\x07"=>[16,"\xA41"],"\x08"=>[94,"\xA42"],"\x09"=>[76,"\xA42"],"\x0A"=>[104,"\xA42"],"\x0B"=>[16,"\xA42"],"\x0C"=>[94,"\xA4a"],"\x0D"=>[76,"\xA4a"],"\x0E"=>[104,"\xA4a"],"\x0F"=>[16,"\xA4a"],"\x10"=>[94,"\xA4c"],"\x11"=>[76,"\xA4c"],"\x12"=>[104,"\xA4c"],"\x13"=>[16,"\xA4c"],"\x14"=>[94,"\xA4e"],"\x15"=>[76,"\xA4e"],"\x16"=>[104,"\xA4e"],"\x17"=>[16,"\xA4e"],"\x18"=>[94,"\xA4i"],"\x19"=>[76,"\xA4i"],"\x1A"=>[104,"\xA4i"],"\x1B"=>[16,"\xA4i"],"\x1C"=>[94,"\xA4o"],"\x1D"=>[76,"\xA4o"],"\x1E"=>[104,"\xA4o"],"\x1F"=>[16,"\xA4o"],"\x20"=>[94,"\xA4s"],"\x21"=>[76,"\xA4s"],"\x22"=>[104,"\xA4s"],"\x23"=>[16,"\xA4s"],"\x24"=>[94,"\xA4t"],"\x25"=>[76,"\xA4t"],"\x26"=>[104,"\xA4t"],"\x27"=>[16,"\xA4t"],"\x28"=>[77,"\xA4\x20"],"\x29"=>[18,"\xA4\x20"],"\x2A"=>[77,"\xA4\x25"],"\x2B"=>[18,"\xA4\x25"],"\x2C"=>[77,"\xA4-"],"-"=>[18,"\xA4-"],"."=>[77,"\xA4."],"\x2F"=>[18,"\xA4."],[77,"\xA4\x2F"],[18,"\xA4\x2F"],[77,"\xA43"],[18,"\xA43"],[77,"\xA44"],[18,"\xA44"],[77,"\xA45"],[18,"\xA45"],[77,"\xA46"],[18,"\xA46"],"\x3A"=>[77,"\xA47"],"\x3B"=>[18,"\xA47"],"\x3C"=>[77,"\xA48"],"\x3D"=>[18,"\xA48"],"\x3E"=>[77,"\xA49"],"\x3F"=>[18,"\xA49"],"\x40"=>[77,"\xA4\x3D"],"A"=>[18,"\xA4\x3D"],"B"=>[77,"\xA4A"],"C"=>[18,"\xA4A"],"D"=>[77,"\xA4_"],"E"=>[18,"\xA4_"],"F"=>[77,"\xA4b"],"G"=>[18,"\xA4b"],"H"=>[77,"\xA4d"],"I"=>[18,"\xA4d"],"J"=>[77,"\xA4f"],"K"=>[18,"\xA4f"],"L"=>[77,"\xA4g"],"M"=>[18,"\xA4g"],"N"=>[77,"\xA4h"],"O"=>[18,"\xA4h"],"P"=>[77,"\xA4l"],"Q"=>[18,"\xA4l"],"R"=>[77,"\xA4m"],"S"=>[18,"\xA4m"],"T"=>[77,"\xA4n"],"U"=>[18,"\xA4n"],"V"=>[77,"\xA4p"],"W"=>[18,"\xA4p"],"X"=>[77,"\xA4r"],"Y"=>[18,"\xA4r"],"Z"=>[77,"\xA4u"],"\x5B"=>[18,"\xA4u"],"\x5C"=>[0,"\xA4\x3A"],"\x5D"=>[0,"\xA4B"],"\x5E"=>[0,"\xA4C"],"_"=>[0,"\xA4D"],"\x60"=>[0,"\xA4E"],"a"=>[0,"\xA4F"],"b"=>[0,"\xA4G"],"c"=>[0,"\xA4H"],"d"=>[0,"\xA4I"],"e"=>[0,"\xA4J"],"f"=>[0,"\xA4K"],"g"=>[0,"\xA4L"],"h"=>[0,"\xA4M"],"i"=>[0,"\xA4N"],"j"=>[0,"\xA4O"],"k"=>[0,"\xA4P"],"l"=>[0,"\xA4Q"],"m"=>[0,"\xA4R"],"n"=>[0,"\xA4S"],"o"=>[0,"\xA4T"],"p"=>[0,"\xA4U"],"q"=>[0,"\xA4V"],"r"=>[0,"\xA4W"],"s"=>[0,"\xA4Y"],"t"=>[0,"\xA4j"],"u"=>[0,"\xA4k"],"v"=>[0,"\xA4q"],"w"=>[0,"\xA4v"],"x"=>[0,"\xA4w"],"y"=>[0,"\xA4x"],"z"=>[0,"\xA4y"],"\x7B"=>[0,"\xA4z"],"\x7C"=>[82,"\xA4"],"\x7D"=>[87,"\xA4"],"~"=>[130,"\xA4"],"\x7F"=>[9,"\xA4"],"\x80"=>[94,"\xA90"],"\x81"=>[76,"\xA90"],"\x82"=>[104,"\xA90"],"\x83"=>[16,"\xA90"],"\x84"=>[94,"\xA91"],"\x85"=>[76,"\xA91"],"\x86"=>[104,"\xA91"],"\x87"=>[16,"\xA91"],"\x88"=>[94,"\xA92"],"\x89"=>[76,"\xA92"],"\x8A"=>[104,"\xA92"],"\x8B"=>[16,"\xA92"],"\x8C"=>[94,"\xA9a"],"\x8D"=>[76,"\xA9a"],"\x8E"=>[104,"\xA9a"],"\x8F"=>[16,"\xA9a"],"\x90"=>[94,"\xA9c"],"\x91"=>[76,"\xA9c"],"\x92"=>[104,"\xA9c"],"\x93"=>[16,"\xA9c"],"\x94"=>[94,"\xA9e"],"\x95"=>[76,"\xA9e"],"\x96"=>[104,"\xA9e"],"\x97"=>[16,"\xA9e"],"\x98"=>[94,"\xA9i"],"\x99"=>[76,"\xA9i"],"\x9A"=>[104,"\xA9i"],"\x9B"=>[16,"\xA9i"],"\x9C"=>[94,"\xA9o"],"\x9D"=>[76,"\xA9o"],"\x9E"=>[104,"\xA9o"],"\x9F"=>[16,"\xA9o"],"\xA0"=>[94,"\xA9s"],"\xA1"=>[76,"\xA9s"],"\xA2"=>[104,"\xA9s"],"\xA3"=>[16,"\xA9s"],"\xA4"=>[94,"\xA9t"],"\xA5"=>[76,"\xA9t"],"\xA6"=>[104,"\xA9t"],"\xA7"=>[16,"\xA9t"],"\xA8"=>[77,"\xA9\x20"],"\xA9"=>[18,"\xA9\x20"],"\xAA"=>[77,"\xA9\x25"],"\xAB"=>[18,"\xA9\x25"],"\xAC"=>[77,"\xA9-"],"\xAD"=>[18,"\xA9-"],"\xAE"=>[77,"\xA9."],"\xAF"=>[18,"\xA9."],"\xB0"=>[77,"\xA9\x2F"],"\xB1"=>[18,"\xA9\x2F"],"\xB2"=>[77,"\xA93"],"\xB3"=>[18,"\xA93"],"\xB4"=>[77,"\xA94"],"\xB5"=>[18,"\xA94"],"\xB6"=>[77,"\xA95"],"\xB7"=>[18,"\xA95"],"\xB8"=>[77,"\xA96"],"\xB9"=>[18,"\xA96"],"\xBA"=>[77,"\xA97"],"\xBB"=>[18,"\xA97"],"\xBC"=>[77,"\xA98"],"\xBD"=>[18,"\xA98"],"\xBE"=>[77,"\xA99"],"\xBF"=>[18,"\xA99"],"\xC0"=>[77,"\xA9\x3D"],"\xC1"=>[18,"\xA9\x3D"],"\xC2"=>[77,"\xA9A"],"\xC3"=>[18,"\xA9A"],"\xC4"=>[77,"\xA9_"],"\xC5"=>[18,"\xA9_"],"\xC6"=>[77,"\xA9b"],"\xC7"=>[18,"\xA9b"],"\xC8"=>[77,"\xA9d"],"\xC9"=>[18,"\xA9d"],"\xCA"=>[77,"\xA9f"],"\xCB"=>[18,"\xA9f"],"\xCC"=>[77,"\xA9g"],"\xCD"=>[18,"\xA9g"],"\xCE"=>[77,"\xA9h"],"\xCF"=>[18,"\xA9h"],"\xD0"=>[77,"\xA9l"],"\xD1"=>[18,"\xA9l"],"\xD2"=>[77,"\xA9m"],"\xD3"=>[18,"\xA9m"],"\xD4"=>[77,"\xA9n"],"\xD5"=>[18,"\xA9n"],"\xD6"=>[77,"\xA9p"],"\xD7"=>[18,"\xA9p"],"\xD8"=>[77,"\xA9r"],"\xD9"=>[18,"\xA9r"],"\xDA"=>[77,"\xA9u"],"\xDB"=>[18,"\xA9u"],"\xDC"=>[0,"\xA9\x3A"],"\xDD"=>[0,"\xA9B"],"\xDE"=>[0,"\xA9C"],"\xDF"=>[0,"\xA9D"],"\xE0"=>[0,"\xA9E"],"\xE1"=>[0,"\xA9F"],"\xE2"=>[0,"\xA9G"],"\xE3"=>[0,"\xA9H"],"\xE4"=>[0,"\xA9I"],"\xE5"=>[0,"\xA9J"],"\xE6"=>[0,"\xA9K"],"\xE7"=>[0,"\xA9L"],"\xE8"=>[0,"\xA9M"],"\xE9"=>[0,"\xA9N"],"\xEA"=>[0,"\xA9O"],"\xEB"=>[0,"\xA9P"],"\xEC"=>[0,"\xA9Q"],"\xED"=>[0,"\xA9R"],"\xEE"=>[0,"\xA9S"],"\xEF"=>[0,"\xA9T"],"\xF0"=>[0,"\xA9U"],"\xF1"=>[0,"\xA9V"],"\xF2"=>[0,"\xA9W"],"\xF3"=>[0,"\xA9Y"],"\xF4"=>[0,"\xA9j"],"\xF5"=>[0,"\xA9k"],"\xF6"=>[0,"\xA9q"],"\xF7"=>[0,"\xA9v"],"\xF8"=>[0,"\xA9w"],"\xF9"=>[0,"\xA9x"],"\xFA"=>[0,"\xA9y"],"\xFB"=>[0,"\xA9z"],"\xFC"=>[82,"\xA9"],"\xFD"=>[87,"\xA9"],"\xFE"=>[130,"\xA9"],"\xFF"=>[9,"\xA9"],],["\x00"=>[77,"\xA40"],"\x01"=>[18,"\xA40"],"\x02"=>[77,"\xA41"],"\x03"=>[18,"\xA41"],"\x04"=>[77,"\xA42"],"\x05"=>[18,"\xA42"],"\x06"=>[77,"\xA4a"],"\x07"=>[18,"\xA4a"],"\x08"=>[77,"\xA4c"],"\x09"=>[18,"\xA4c"],"\x0A"=>[77,"\xA4e"],"\x0B"=>[18,"\xA4e"],"\x0C"=>[77,"\xA4i"],"\x0D"=>[18,"\xA4i"],"\x0E"=>[77,"\xA4o"],"\x0F"=>[18,"\xA4o"],"\x10"=>[77,"\xA4s"],"\x11"=>[18,"\xA4s"],"\x12"=>[77,"\xA4t"],"\x13"=>[18,"\xA4t"],"\x14"=>[0,"\xA4\x20"],"\x15"=>[0,"\xA4\x25"],"\x16"=>[0,"\xA4-"],"\x17"=>[0,"\xA4."],"\x18"=>[0,"\xA4\x2F"],"\x19"=>[0,"\xA43"],"\x1A"=>[0,"\xA44"],"\x1B"=>[0,"\xA45"],"\x1C"=>[0,"\xA46"],"\x1D"=>[0,"\xA47"],"\x1E"=>[0,"\xA48"],"\x1F"=>[0,"\xA49"],"\x20"=>[0,"\xA4\x3D"],"\x21"=>[0,"\xA4A"],"\x22"=>[0,"\xA4_"],"\x23"=>[0,"\xA4b"],"\x24"=>[0,"\xA4d"],"\x25"=>[0,"\xA4f"],"\x26"=>[0,"\xA4g"],"\x27"=>[0,"\xA4h"],"\x28"=>[0,"\xA4l"],"\x29"=>[0,"\xA4m"],"\x2A"=>[0,"\xA4n"],"\x2B"=>[0,"\xA4p"],"\x2C"=>[0,"\xA4r"],"-"=>[0,"\xA4u"],"."=>[100,"\xA4"],"\x2F"=>[110,"\xA4"],[111,"\xA4"],[115,"\xA4"],[116,"\xA4"],[118,"\xA4"],[119,"\xA4"],[122,"\xA4"],[123,"\xA4"],[125,"\xA4"],[126,"\xA4"],[129,"\xA4"],"\x3A"=>[143,"\xA4"],"\x3B"=>[148,"\xA4"],"\x3C"=>[151,"\xA4"],"\x3D"=>[153,"\xA4"],"\x3E"=>[83,"\xA4"],"\x3F"=>[10,"\xA4"],"\x40"=>[77,"\xA90"],"A"=>[18,"\xA90"],"B"=>[77,"\xA91"],"C"=>[18,"\xA91"],"D"=>[77,"\xA92"],"E"=>[18,"\xA92"],"F"=>[77,"\xA9a"],"G"=>[18,"\xA9a"],"H"=>[77,"\xA9c"],"I"=>[18,"\xA9c"],"J"=>[77,"\xA9e"],"K"=>[18,"\xA9e"],"L"=>[77,"\xA9i"],"M"=>[18,"\xA9i"],"N"=>[77,"\xA9o"],"O"=>[18,"\xA9o"],"P"=>[77,"\xA9s"],"Q"=>[18,"\xA9s"],"R"=>[77,"\xA9t"],"S"=>[18,"\xA9t"],"T"=>[0,"\xA9\x20"],"U"=>[0,"\xA9\x25"],"V"=>[0,"\xA9-"],"W"=>[0,"\xA9."],"X"=>[0,"\xA9\x2F"],"Y"=>[0,"\xA93"],"Z"=>[0,"\xA94"],"\x5B"=>[0,"\xA95"],"\x5C"=>[0,"\xA96"],"\x5D"=>[0,"\xA97"],"\x5E"=>[0,"\xA98"],"_"=>[0,"\xA99"],"\x60"=>[0,"\xA9\x3D"],"a"=>[0,"\xA9A"],"b"=>[0,"\xA9_"],"c"=>[0,"\xA9b"],"d"=>[0,"\xA9d"],"e"=>[0,"\xA9f"],"f"=>[0,"\xA9g"],"g"=>[0,"\xA9h"],"h"=>[0,"\xA9l"],"i"=>[0,"\xA9m"],"j"=>[0,"\xA9n"],"k"=>[0,"\xA9p"],"l"=>[0,"\xA9r"],"m"=>[0,"\xA9u"],"n"=>[100,"\xA9"],"o"=>[110,"\xA9"],"p"=>[111,"\xA9"],"q"=>[115,"\xA9"],"r"=>[116,"\xA9"],"s"=>[118,"\xA9"],"t"=>[119,"\xA9"],"u"=>[122,"\xA9"],"v"=>[123,"\xA9"],"w"=>[125,"\xA9"],"x"=>[126,"\xA9"],"y"=>[129,"\xA9"],"z"=>[143,"\xA9"],"\x7B"=>[148,"\xA9"],"\x7C"=>[151,"\xA9"],"\x7D"=>[153,"\xA9"],"~"=>[83,"\xA9"],"\x7F"=>[10,"\xA9"],"\x80"=>[77,"\xAA0"],"\x81"=>[18,"\xAA0"],"\x82"=>[77,"\xAA1"],"\x83"=>[18,"\xAA1"],"\x84"=>[77,"\xAA2"],"\x85"=>[18,"\xAA2"],"\x86"=>[77,"\xAAa"],"\x87"=>[18,"\xAAa"],"\x88"=>[77,"\xAAc"],"\x89"=>[18,"\xAAc"],"\x8A"=>[77,"\xAAe"],"\x8B"=>[18,"\xAAe"],"\x8C"=>[77,"\xAAi"],"\x8D"=>[18,"\xAAi"],"\x8E"=>[77,"\xAAo"],"\x8F"=>[18,"\xAAo"],"\x90"=>[77,"\xAAs"],"\x91"=>[18,"\xAAs"],"\x92"=>[77,"\xAAt"],"\x93"=>[18,"\xAAt"],"\x94"=>[0,"\xAA\x20"],"\x95"=>[0,"\xAA\x25"],"\x96"=>[0,"\xAA-"],"\x97"=>[0,"\xAA."],"\x98"=>[0,"\xAA\x2F"],"\x99"=>[0,"\xAA3"],"\x9A"=>[0,"\xAA4"],"\x9B"=>[0,"\xAA5"],"\x9C"=>[0,"\xAA6"],"\x9D"=>[0,"\xAA7"],"\x9E"=>[0,"\xAA8"],"\x9F"=>[0,"\xAA9"],"\xA0"=>[0,"\xAA\x3D"],"\xA1"=>[0,"\xAAA"],"\xA2"=>[0,"\xAA_"],"\xA3"=>[0,"\xAAb"],"\xA4"=>[0,"\xAAd"],"\xA5"=>[0,"\xAAf"],"\xA6"=>[0,"\xAAg"],"\xA7"=>[0,"\xAAh"],"\xA8"=>[0,"\xAAl"],"\xA9"=>[0,"\xAAm"],"\xAA"=>[0,"\xAAn"],"\xAB"=>[0,"\xAAp"],"\xAC"=>[0,"\xAAr"],"\xAD"=>[0,"\xAAu"],"\xAE"=>[100,"\xAA"],"\xAF"=>[110,"\xAA"],"\xB0"=>[111,"\xAA"],"\xB1"=>[115,"\xAA"],"\xB2"=>[116,"\xAA"],"\xB3"=>[118,"\xAA"],"\xB4"=>[119,"\xAA"],"\xB5"=>[122,"\xAA"],"\xB6"=>[123,"\xAA"],"\xB7"=>[125,"\xAA"],"\xB8"=>[126,"\xAA"],"\xB9"=>[129,"\xAA"],"\xBA"=>[143,"\xAA"],"\xBB"=>[148,"\xAA"],"\xBC"=>[151,"\xAA"],"\xBD"=>[153,"\xAA"],"\xBE"=>[83,"\xAA"],"\xBF"=>[10,"\xAA"],"\xC0"=>[77,"\xAD0"],"\xC1"=>[18,"\xAD0"],"\xC2"=>[77,"\xAD1"],"\xC3"=>[18,"\xAD1"],"\xC4"=>[77,"\xAD2"],"\xC5"=>[18,"\xAD2"],"\xC6"=>[77,"\xADa"],"\xC7"=>[18,"\xADa"],"\xC8"=>[77,"\xADc"],"\xC9"=>[18,"\xADc"],"\xCA"=>[77,"\xADe"],"\xCB"=>[18,"\xADe"],"\xCC"=>[77,"\xADi"],"\xCD"=>[18,"\xADi"],"\xCE"=>[77,"\xADo"],"\xCF"=>[18,"\xADo"],"\xD0"=>[77,"\xADs"],"\xD1"=>[18,"\xADs"],"\xD2"=>[77,"\xADt"],"\xD3"=>[18,"\xADt"],"\xD4"=>[0,"\xAD\x20"],"\xD5"=>[0,"\xAD\x25"],"\xD6"=>[0,"\xAD-"],"\xD7"=>[0,"\xAD."],"\xD8"=>[0,"\xAD\x2F"],"\xD9"=>[0,"\xAD3"],"\xDA"=>[0,"\xAD4"],"\xDB"=>[0,"\xAD5"],"\xDC"=>[0,"\xAD6"],"\xDD"=>[0,"\xAD7"],"\xDE"=>[0,"\xAD8"],"\xDF"=>[0,"\xAD9"],"\xE0"=>[0,"\xAD\x3D"],"\xE1"=>[0,"\xADA"],"\xE2"=>[0,"\xAD_"],"\xE3"=>[0,"\xADb"],"\xE4"=>[0,"\xADd"],"\xE5"=>[0,"\xADf"],"\xE6"=>[0,"\xADg"],"\xE7"=>[0,"\xADh"],"\xE8"=>[0,"\xADl"],"\xE9"=>[0,"\xADm"],"\xEA"=>[0,"\xADn"],"\xEB"=>[0,"\xADp"],"\xEC"=>[0,"\xADr"],"\xED"=>[0,"\xADu"],"\xEE"=>[100,"\xAD"],"\xEF"=>[110,"\xAD"],"\xF0"=>[111,"\xAD"],"\xF1"=>[115,"\xAD"],"\xF2"=>[116,"\xAD"],"\xF3"=>[118,"\xAD"],"\xF4"=>[119,"\xAD"],"\xF5"=>[122,"\xAD"],"\xF6"=>[123,"\xAD"],"\xF7"=>[125,"\xAD"],"\xF8"=>[126,"\xAD"],"\xF9"=>[129,"\xAD"],"\xFA"=>[143,"\xAD"],"\xFB"=>[148,"\xAD"],"\xFC"=>[151,"\xAD"],"\xFD"=>[153,"\xAD"],"\xFE"=>[83,"\xAD"],"\xFF"=>[10,"\xAD"],],["\x00"=>[94,"\xA50"],"\x01"=>[76,"\xA50"],"\x02"=>[104,"\xA50"],"\x03"=>[16,"\xA50"],"\x04"=>[94,"\xA51"],"\x05"=>[76,"\xA51"],"\x06"=>[104,"\xA51"],"\x07"=>[16,"\xA51"],"\x08"=>[94,"\xA52"],"\x09"=>[76,"\xA52"],"\x0A"=>[104,"\xA52"],"\x0B"=>[16,"\xA52"],"\x0C"=>[94,"\xA5a"],"\x0D"=>[76,"\xA5a"],"\x0E"=>[104,"\xA5a"],"\x0F"=>[16,"\xA5a"],"\x10"=>[94,"\xA5c"],"\x11"=>[76,"\xA5c"],"\x12"=>[104,"\xA5c"],"\x13"=>[16,"\xA5c"],"\x14"=>[94,"\xA5e"],"\x15"=>[76,"\xA5e"],"\x16"=>[104,"\xA5e"],"\x17"=>[16,"\xA5e"],"\x18"=>[94,"\xA5i"],"\x19"=>[76,"\xA5i"],"\x1A"=>[104,"\xA5i"],"\x1B"=>[16,"\xA5i"],"\x1C"=>[94,"\xA5o"],"\x1D"=>[76,"\xA5o"],"\x1E"=>[104,"\xA5o"],"\x1F"=>[16,"\xA5o"],"\x20"=>[94,"\xA5s"],"\x21"=>[76,"\xA5s"],"\x22"=>[104,"\xA5s"],"\x23"=>[16,"\xA5s"],"\x24"=>[94,"\xA5t"],"\x25"=>[76,"\xA5t"],"\x26"=>[104,"\xA5t"],"\x27"=>[16,"\xA5t"],"\x28"=>[77,"\xA5\x20"],"\x29"=>[18,"\xA5\x20"],"\x2A"=>[77,"\xA5\x25"],"\x2B"=>[18,"\xA5\x25"],"\x2C"=>[77,"\xA5-"],"-"=>[18,"\xA5-"],"."=>[77,"\xA5."],"\x2F"=>[18,"\xA5."],[77,"\xA5\x2F"],[18,"\xA5\x2F"],[77,"\xA53"],[18,"\xA53"],[77,"\xA54"],[18,"\xA54"],[77,"\xA55"],[18,"\xA55"],[77,"\xA56"],[18,"\xA56"],"\x3A"=>[77,"\xA57"],"\x3B"=>[18,"\xA57"],"\x3C"=>[77,"\xA58"],"\x3D"=>[18,"\xA58"],"\x3E"=>[77,"\xA59"],"\x3F"=>[18,"\xA59"],"\x40"=>[77,"\xA5\x3D"],"A"=>[18,"\xA5\x3D"],"B"=>[77,"\xA5A"],"C"=>[18,"\xA5A"],"D"=>[77,"\xA5_"],"E"=>[18,"\xA5_"],"F"=>[77,"\xA5b"],"G"=>[18,"\xA5b"],"H"=>[77,"\xA5d"],"I"=>[18,"\xA5d"],"J"=>[77,"\xA5f"],"K"=>[18,"\xA5f"],"L"=>[77,"\xA5g"],"M"=>[18,"\xA5g"],"N"=>[77,"\xA5h"],"O"=>[18,"\xA5h"],"P"=>[77,"\xA5l"],"Q"=>[18,"\xA5l"],"R"=>[77,"\xA5m"],"S"=>[18,"\xA5m"],"T"=>[77,"\xA5n"],"U"=>[18,"\xA5n"],"V"=>[77,"\xA5p"],"W"=>[18,"\xA5p"],"X"=>[77,"\xA5r"],"Y"=>[18,"\xA5r"],"Z"=>[77,"\xA5u"],"\x5B"=>[18,"\xA5u"],"\x5C"=>[0,"\xA5\x3A"],"\x5D"=>[0,"\xA5B"],"\x5E"=>[0,"\xA5C"],"_"=>[0,"\xA5D"],"\x60"=>[0,"\xA5E"],"a"=>[0,"\xA5F"],"b"=>[0,"\xA5G"],"c"=>[0,"\xA5H"],"d"=>[0,"\xA5I"],"e"=>[0,"\xA5J"],"f"=>[0,"\xA5K"],"g"=>[0,"\xA5L"],"h"=>[0,"\xA5M"],"i"=>[0,"\xA5N"],"j"=>[0,"\xA5O"],"k"=>[0,"\xA5P"],"l"=>[0,"\xA5Q"],"m"=>[0,"\xA5R"],"n"=>[0,"\xA5S"],"o"=>[0,"\xA5T"],"p"=>[0,"\xA5U"],"q"=>[0,"\xA5V"],"r"=>[0,"\xA5W"],"s"=>[0,"\xA5Y"],"t"=>[0,"\xA5j"],"u"=>[0,"\xA5k"],"v"=>[0,"\xA5q"],"w"=>[0,"\xA5v"],"x"=>[0,"\xA5w"],"y"=>[0,"\xA5x"],"z"=>[0,"\xA5y"],"\x7B"=>[0,"\xA5z"],"\x7C"=>[82,"\xA5"],"\x7D"=>[87,"\xA5"],"~"=>[130,"\xA5"],"\x7F"=>[9,"\xA5"],"\x80"=>[94,"\xA60"],"\x81"=>[76,"\xA60"],"\x82"=>[104,"\xA60"],"\x83"=>[16,"\xA60"],"\x84"=>[94,"\xA61"],"\x85"=>[76,"\xA61"],"\x86"=>[104,"\xA61"],"\x87"=>[16,"\xA61"],"\x88"=>[94,"\xA62"],"\x89"=>[76,"\xA62"],"\x8A"=>[104,"\xA62"],"\x8B"=>[16,"\xA62"],"\x8C"=>[94,"\xA6a"],"\x8D"=>[76,"\xA6a"],"\x8E"=>[104,"\xA6a"],"\x8F"=>[16,"\xA6a"],"\x90"=>[94,"\xA6c"],"\x91"=>[76,"\xA6c"],"\x92"=>[104,"\xA6c"],"\x93"=>[16,"\xA6c"],"\x94"=>[94,"\xA6e"],"\x95"=>[76,"\xA6e"],"\x96"=>[104,"\xA6e"],"\x97"=>[16,"\xA6e"],"\x98"=>[94,"\xA6i"],"\x99"=>[76,"\xA6i"],"\x9A"=>[104,"\xA6i"],"\x9B"=>[16,"\xA6i"],"\x9C"=>[94,"\xA6o"],"\x9D"=>[76,"\xA6o"],"\x9E"=>[104,"\xA6o"],"\x9F"=>[16,"\xA6o"],"\xA0"=>[94,"\xA6s"],"\xA1"=>[76,"\xA6s"],"\xA2"=>[104,"\xA6s"],"\xA3"=>[16,"\xA6s"],"\xA4"=>[94,"\xA6t"],"\xA5"=>[76,"\xA6t"],"\xA6"=>[104,"\xA6t"],"\xA7"=>[16,"\xA6t"],"\xA8"=>[77,"\xA6\x20"],"\xA9"=>[18,"\xA6\x20"],"\xAA"=>[77,"\xA6\x25"],"\xAB"=>[18,"\xA6\x25"],"\xAC"=>[77,"\xA6-"],"\xAD"=>[18,"\xA6-"],"\xAE"=>[77,"\xA6."],"\xAF"=>[18,"\xA6."],"\xB0"=>[77,"\xA6\x2F"],"\xB1"=>[18,"\xA6\x2F"],"\xB2"=>[77,"\xA63"],"\xB3"=>[18,"\xA63"],"\xB4"=>[77,"\xA64"],"\xB5"=>[18,"\xA64"],"\xB6"=>[77,"\xA65"],"\xB7"=>[18,"\xA65"],"\xB8"=>[77,"\xA66"],"\xB9"=>[18,"\xA66"],"\xBA"=>[77,"\xA67"],"\xBB"=>[18,"\xA67"],"\xBC"=>[77,"\xA68"],"\xBD"=>[18,"\xA68"],"\xBE"=>[77,"\xA69"],"\xBF"=>[18,"\xA69"],"\xC0"=>[77,"\xA6\x3D"],"\xC1"=>[18,"\xA6\x3D"],"\xC2"=>[77,"\xA6A"],"\xC3"=>[18,"\xA6A"],"\xC4"=>[77,"\xA6_"],"\xC5"=>[18,"\xA6_"],"\xC6"=>[77,"\xA6b"],"\xC7"=>[18,"\xA6b"],"\xC8"=>[77,"\xA6d"],"\xC9"=>[18,"\xA6d"],"\xCA"=>[77,"\xA6f"],"\xCB"=>[18,"\xA6f"],"\xCC"=>[77,"\xA6g"],"\xCD"=>[18,"\xA6g"],"\xCE"=>[77,"\xA6h"],"\xCF"=>[18,"\xA6h"],"\xD0"=>[77,"\xA6l"],"\xD1"=>[18,"\xA6l"],"\xD2"=>[77,"\xA6m"],"\xD3"=>[18,"\xA6m"],"\xD4"=>[77,"\xA6n"],"\xD5"=>[18,"\xA6n"],"\xD6"=>[77,"\xA6p"],"\xD7"=>[18,"\xA6p"],"\xD8"=>[77,"\xA6r"],"\xD9"=>[18,"\xA6r"],"\xDA"=>[77,"\xA6u"],"\xDB"=>[18,"\xA6u"],"\xDC"=>[0,"\xA6\x3A"],"\xDD"=>[0,"\xA6B"],"\xDE"=>[0,"\xA6C"],"\xDF"=>[0,"\xA6D"],"\xE0"=>[0,"\xA6E"],"\xE1"=>[0,"\xA6F"],"\xE2"=>[0,"\xA6G"],"\xE3"=>[0,"\xA6H"],"\xE4"=>[0,"\xA6I"],"\xE5"=>[0,"\xA6J"],"\xE6"=>[0,"\xA6K"],"\xE7"=>[0,"\xA6L"],"\xE8"=>[0,"\xA6M"],"\xE9"=>[0,"\xA6N"],"\xEA"=>[0,"\xA6O"],"\xEB"=>[0,"\xA6P"],"\xEC"=>[0,"\xA6Q"],"\xED"=>[0,"\xA6R"],"\xEE"=>[0,"\xA6S"],"\xEF"=>[0,"\xA6T"],"\xF0"=>[0,"\xA6U"],"\xF1"=>[0,"\xA6V"],"\xF2"=>[0,"\xA6W"],"\xF3"=>[0,"\xA6Y"],"\xF4"=>[0,"\xA6j"],"\xF5"=>[0,"\xA6k"],"\xF6"=>[0,"\xA6q"],"\xF7"=>[0,"\xA6v"],"\xF8"=>[0,"\xA6w"],"\xF9"=>[0,"\xA6x"],"\xFA"=>[0,"\xA6y"],"\xFB"=>[0,"\xA6z"],"\xFC"=>[82,"\xA6"],"\xFD"=>[87,"\xA6"],"\xFE"=>[130,"\xA6"],"\xFF"=>[9,"\xA6"],],["\x00"=>[77,"\xA50"],"\x01"=>[18,"\xA50"],"\x02"=>[77,"\xA51"],"\x03"=>[18,"\xA51"],"\x04"=>[77,"\xA52"],"\x05"=>[18,"\xA52"],"\x06"=>[77,"\xA5a"],"\x07"=>[18,"\xA5a"],"\x08"=>[77,"\xA5c"],"\x09"=>[18,"\xA5c"],"\x0A"=>[77,"\xA5e"],"\x0B"=>[18,"\xA5e"],"\x0C"=>[77,"\xA5i"],"\x0D"=>[18,"\xA5i"],"\x0E"=>[77,"\xA5o"],"\x0F"=>[18,"\xA5o"],"\x10"=>[77,"\xA5s"],"\x11"=>[18,"\xA5s"],"\x12"=>[77,"\xA5t"],"\x13"=>[18,"\xA5t"],"\x14"=>[0,"\xA5\x20"],"\x15"=>[0,"\xA5\x25"],"\x16"=>[0,"\xA5-"],"\x17"=>[0,"\xA5."],"\x18"=>[0,"\xA5\x2F"],"\x19"=>[0,"\xA53"],"\x1A"=>[0,"\xA54"],"\x1B"=>[0,"\xA55"],"\x1C"=>[0,"\xA56"],"\x1D"=>[0,"\xA57"],"\x1E"=>[0,"\xA58"],"\x1F"=>[0,"\xA59"],"\x20"=>[0,"\xA5\x3D"],"\x21"=>[0,"\xA5A"],"\x22"=>[0,"\xA5_"],"\x23"=>[0,"\xA5b"],"\x24"=>[0,"\xA5d"],"\x25"=>[0,"\xA5f"],"\x26"=>[0,"\xA5g"],"\x27"=>[0,"\xA5h"],"\x28"=>[0,"\xA5l"],"\x29"=>[0,"\xA5m"],"\x2A"=>[0,"\xA5n"],"\x2B"=>[0,"\xA5p"],"\x2C"=>[0,"\xA5r"],"-"=>[0,"\xA5u"],"."=>[100,"\xA5"],"\x2F"=>[110,"\xA5"],[111,"\xA5"],[115,"\xA5"],[116,"\xA5"],[118,"\xA5"],[119,"\xA5"],[122,"\xA5"],[123,"\xA5"],[125,"\xA5"],[126,"\xA5"],[129,"\xA5"],"\x3A"=>[143,"\xA5"],"\x3B"=>[148,"\xA5"],"\x3C"=>[151,"\xA5"],"\x3D"=>[153,"\xA5"],"\x3E"=>[83,"\xA5"],"\x3F"=>[10,"\xA5"],"\x40"=>[77,"\xA60"],"A"=>[18,"\xA60"],"B"=>[77,"\xA61"],"C"=>[18,"\xA61"],"D"=>[77,"\xA62"],"E"=>[18,"\xA62"],"F"=>[77,"\xA6a"],"G"=>[18,"\xA6a"],"H"=>[77,"\xA6c"],"I"=>[18,"\xA6c"],"J"=>[77,"\xA6e"],"K"=>[18,"\xA6e"],"L"=>[77,"\xA6i"],"M"=>[18,"\xA6i"],"N"=>[77,"\xA6o"],"O"=>[18,"\xA6o"],"P"=>[77,"\xA6s"],"Q"=>[18,"\xA6s"],"R"=>[77,"\xA6t"],"S"=>[18,"\xA6t"],"T"=>[0,"\xA6\x20"],"U"=>[0,"\xA6\x25"],"V"=>[0,"\xA6-"],"W"=>[0,"\xA6."],"X"=>[0,"\xA6\x2F"],"Y"=>[0,"\xA63"],"Z"=>[0,"\xA64"],"\x5B"=>[0,"\xA65"],"\x5C"=>[0,"\xA66"],"\x5D"=>[0,"\xA67"],"\x5E"=>[0,"\xA68"],"_"=>[0,"\xA69"],"\x60"=>[0,"\xA6\x3D"],"a"=>[0,"\xA6A"],"b"=>[0,"\xA6_"],"c"=>[0,"\xA6b"],"d"=>[0,"\xA6d"],"e"=>[0,"\xA6f"],"f"=>[0,"\xA6g"],"g"=>[0,"\xA6h"],"h"=>[0,"\xA6l"],"i"=>[0,"\xA6m"],"j"=>[0,"\xA6n"],"k"=>[0,"\xA6p"],"l"=>[0,"\xA6r"],"m"=>[0,"\xA6u"],"n"=>[100,"\xA6"],"o"=>[110,"\xA6"],"p"=>[111,"\xA6"],"q"=>[115,"\xA6"],"r"=>[116,"\xA6"],"s"=>[118,"\xA6"],"t"=>[119,"\xA6"],"u"=>[122,"\xA6"],"v"=>[123,"\xA6"],"w"=>[125,"\xA6"],"x"=>[126,"\xA6"],"y"=>[129,"\xA6"],"z"=>[143,"\xA6"],"\x7B"=>[148,"\xA6"],"\x7C"=>[151,"\xA6"],"\x7D"=>[153,"\xA6"],"~"=>[83,"\xA6"],"\x7F"=>[10,"\xA6"],"\x80"=>[77,"\xA80"],"\x81"=>[18,"\xA80"],"\x82"=>[77,"\xA81"],"\x83"=>[18,"\xA81"],"\x84"=>[77,"\xA82"],"\x85"=>[18,"\xA82"],"\x86"=>[77,"\xA8a"],"\x87"=>[18,"\xA8a"],"\x88"=>[77,"\xA8c"],"\x89"=>[18,"\xA8c"],"\x8A"=>[77,"\xA8e"],"\x8B"=>[18,"\xA8e"],"\x8C"=>[77,"\xA8i"],"\x8D"=>[18,"\xA8i"],"\x8E"=>[77,"\xA8o"],"\x8F"=>[18,"\xA8o"],"\x90"=>[77,"\xA8s"],"\x91"=>[18,"\xA8s"],"\x92"=>[77,"\xA8t"],"\x93"=>[18,"\xA8t"],"\x94"=>[0,"\xA8\x20"],"\x95"=>[0,"\xA8\x25"],"\x96"=>[0,"\xA8-"],"\x97"=>[0,"\xA8."],"\x98"=>[0,"\xA8\x2F"],"\x99"=>[0,"\xA83"],"\x9A"=>[0,"\xA84"],"\x9B"=>[0,"\xA85"],"\x9C"=>[0,"\xA86"],"\x9D"=>[0,"\xA87"],"\x9E"=>[0,"\xA88"],"\x9F"=>[0,"\xA89"],"\xA0"=>[0,"\xA8\x3D"],"\xA1"=>[0,"\xA8A"],"\xA2"=>[0,"\xA8_"],"\xA3"=>[0,"\xA8b"],"\xA4"=>[0,"\xA8d"],"\xA5"=>[0,"\xA8f"],"\xA6"=>[0,"\xA8g"],"\xA7"=>[0,"\xA8h"],"\xA8"=>[0,"\xA8l"],"\xA9"=>[0,"\xA8m"],"\xAA"=>[0,"\xA8n"],"\xAB"=>[0,"\xA8p"],"\xAC"=>[0,"\xA8r"],"\xAD"=>[0,"\xA8u"],"\xAE"=>[100,"\xA8"],"\xAF"=>[110,"\xA8"],"\xB0"=>[111,"\xA8"],"\xB1"=>[115,"\xA8"],"\xB2"=>[116,"\xA8"],"\xB3"=>[118,"\xA8"],"\xB4"=>[119,"\xA8"],"\xB5"=>[122,"\xA8"],"\xB6"=>[123,"\xA8"],"\xB7"=>[125,"\xA8"],"\xB8"=>[126,"\xA8"],"\xB9"=>[129,"\xA8"],"\xBA"=>[143,"\xA8"],"\xBB"=>[148,"\xA8"],"\xBC"=>[151,"\xA8"],"\xBD"=>[153,"\xA8"],"\xBE"=>[83,"\xA8"],"\xBF"=>[10,"\xA8"],"\xC0"=>[77,"\xAE0"],"\xC1"=>[18,"\xAE0"],"\xC2"=>[77,"\xAE1"],"\xC3"=>[18,"\xAE1"],"\xC4"=>[77,"\xAE2"],"\xC5"=>[18,"\xAE2"],"\xC6"=>[77,"\xAEa"],"\xC7"=>[18,"\xAEa"],"\xC8"=>[77,"\xAEc"],"\xC9"=>[18,"\xAEc"],"\xCA"=>[77,"\xAEe"],"\xCB"=>[18,"\xAEe"],"\xCC"=>[77,"\xAEi"],"\xCD"=>[18,"\xAEi"],"\xCE"=>[77,"\xAEo"],"\xCF"=>[18,"\xAEo"],"\xD0"=>[77,"\xAEs"],"\xD1"=>[18,"\xAEs"],"\xD2"=>[77,"\xAEt"],"\xD3"=>[18,"\xAEt"],"\xD4"=>[0,"\xAE\x20"],"\xD5"=>[0,"\xAE\x25"],"\xD6"=>[0,"\xAE-"],"\xD7"=>[0,"\xAE."],"\xD8"=>[0,"\xAE\x2F"],"\xD9"=>[0,"\xAE3"],"\xDA"=>[0,"\xAE4"],"\xDB"=>[0,"\xAE5"],"\xDC"=>[0,"\xAE6"],"\xDD"=>[0,"\xAE7"],"\xDE"=>[0,"\xAE8"],"\xDF"=>[0,"\xAE9"],"\xE0"=>[0,"\xAE\x3D"],"\xE1"=>[0,"\xAEA"],"\xE2"=>[0,"\xAE_"],"\xE3"=>[0,"\xAEb"],"\xE4"=>[0,"\xAEd"],"\xE5"=>[0,"\xAEf"],"\xE6"=>[0,"\xAEg"],"\xE7"=>[0,"\xAEh"],"\xE8"=>[0,"\xAEl"],"\xE9"=>[0,"\xAEm"],"\xEA"=>[0,"\xAEn"],"\xEB"=>[0,"\xAEp"],"\xEC"=>[0,"\xAEr"],"\xED"=>[0,"\xAEu"],"\xEE"=>[100,"\xAE"],"\xEF"=>[110,"\xAE"],"\xF0"=>[111,"\xAE"],"\xF1"=>[115,"\xAE"],"\xF2"=>[116,"\xAE"],"\xF3"=>[118,"\xAE"],"\xF4"=>[119,"\xAE"],"\xF5"=>[122,"\xAE"],"\xF6"=>[123,"\xAE"],"\xF7"=>[125,"\xAE"],"\xF8"=>[126,"\xAE"],"\xF9"=>[129,"\xAE"],"\xFA"=>[143,"\xAE"],"\xFB"=>[148,"\xAE"],"\xFC"=>[151,"\xAE"],"\xFD"=>[153,"\xAE"],"\xFE"=>[83,"\xAE"],"\xFF"=>[10,"\xAE"],],["\x00"=>[0,"\xA50"],"\x01"=>[0,"\xA51"],"\x02"=>[0,"\xA52"],"\x03"=>[0,"\xA5a"],"\x04"=>[0,"\xA5c"],"\x05"=>[0,"\xA5e"],"\x06"=>[0,"\xA5i"],"\x07"=>[0,"\xA5o"],"\x08"=>[0,"\xA5s"],"\x09"=>[0,"\xA5t"],"\x0A"=>[73,"\xA5"],"\x0B"=>[88,"\xA5"],"\x0C"=>[89,"\xA5"],"\x0D"=>[96,"\xA5"],"\x0E"=>[97,"\xA5"],"\x0F"=>[99,"\xA5"],"\x10"=>[106,"\xA5"],"\x11"=>[136,"\xA5"],"\x12"=>[139,"\xA5"],"\x13"=>[141,"\xA5"],"\x14"=>[145,"\xA5"],"\x15"=>[147,"\xA5"],"\x16"=>[149,"\xA5"],"\x17"=>[101,"\xA5"],"\x18"=>[112,"\xA5"],"\x19"=>[117,"\xA5"],"\x1A"=>[120,"\xA5"],"\x1B"=>[124,"\xA5"],"\x1C"=>[127,"\xA5"],"\x1D"=>[144,"\xA5"],"\x1E"=>[152,"\xA5"],"\x1F"=>[11,"\xA5"],"\x20"=>[0,"\xA60"],"\x21"=>[0,"\xA61"],"\x22"=>[0,"\xA62"],"\x23"=>[0,"\xA6a"],"\x24"=>[0,"\xA6c"],"\x25"=>[0,"\xA6e"],"\x26"=>[0,"\xA6i"],"\x27"=>[0,"\xA6o"],"\x28"=>[0,"\xA6s"],"\x29"=>[0,"\xA6t"],"\x2A"=>[73,"\xA6"],"\x2B"=>[88,"\xA6"],"\x2C"=>[89,"\xA6"],"-"=>[96,"\xA6"],"."=>[97,"\xA6"],"\x2F"=>[99,"\xA6"],[106,"\xA6"],[136,"\xA6"],[139,"\xA6"],[141,"\xA6"],[145,"\xA6"],[147,"\xA6"],[149,"\xA6"],[101,"\xA6"],[112,"\xA6"],[117,"\xA6"],"\x3A"=>[120,"\xA6"],"\x3B"=>[124,"\xA6"],"\x3C"=>[127,"\xA6"],"\x3D"=>[144,"\xA6"],"\x3E"=>[152,"\xA6"],"\x3F"=>[11,"\xA6"],"\x40"=>[0,"\xA80"],"A"=>[0,"\xA81"],"B"=>[0,"\xA82"],"C"=>[0,"\xA8a"],"D"=>[0,"\xA8c"],"E"=>[0,"\xA8e"],"F"=>[0,"\xA8i"],"G"=>[0,"\xA8o"],"H"=>[0,"\xA8s"],"I"=>[0,"\xA8t"],"J"=>[73,"\xA8"],"K"=>[88,"\xA8"],"L"=>[89,"\xA8"],"M"=>[96,"\xA8"],"N"=>[97,"\xA8"],"O"=>[99,"\xA8"],"P"=>[106,"\xA8"],"Q"=>[136,"\xA8"],"R"=>[139,"\xA8"],"S"=>[141,"\xA8"],"T"=>[145,"\xA8"],"U"=>[147,"\xA8"],"V"=>[149,"\xA8"],"W"=>[101,"\xA8"],"X"=>[112,"\xA8"],"Y"=>[117,"\xA8"],"Z"=>[120,"\xA8"],"\x5B"=>[124,"\xA8"],"\x5C"=>[127,"\xA8"],"\x5D"=>[144,"\xA8"],"\x5E"=>[152,"\xA8"],"_"=>[11,"\xA8"],"\x60"=>[0,"\xAE0"],"a"=>[0,"\xAE1"],"b"=>[0,"\xAE2"],"c"=>[0,"\xAEa"],"d"=>[0,"\xAEc"],"e"=>[0,"\xAEe"],"f"=>[0,"\xAEi"],"g"=>[0,"\xAEo"],"h"=>[0,"\xAEs"],"i"=>[0,"\xAEt"],"j"=>[73,"\xAE"],"k"=>[88,"\xAE"],"l"=>[89,"\xAE"],"m"=>[96,"\xAE"],"n"=>[97,"\xAE"],"o"=>[99,"\xAE"],"p"=>[106,"\xAE"],"q"=>[136,"\xAE"],"r"=>[139,"\xAE"],"s"=>[141,"\xAE"],"t"=>[145,"\xAE"],"u"=>[147,"\xAE"],"v"=>[149,"\xAE"],"w"=>[101,"\xAE"],"x"=>[112,"\xAE"],"y"=>[117,"\xAE"],"z"=>[120,"\xAE"],"\x7B"=>[124,"\xAE"],"\x7C"=>[127,"\xAE"],"\x7D"=>[144,"\xAE"],"~"=>[152,"\xAE"],"\x7F"=>[11,"\xAE"],"\x80"=>[0,"\xAF0"],"\x81"=>[0,"\xAF1"],"\x82"=>[0,"\xAF2"],"\x83"=>[0,"\xAFa"],"\x84"=>[0,"\xAFc"],"\x85"=>[0,"\xAFe"],"\x86"=>[0,"\xAFi"],"\x87"=>[0,"\xAFo"],"\x88"=>[0,"\xAFs"],"\x89"=>[0,"\xAFt"],"\x8A"=>[73,"\xAF"],"\x8B"=>[88,"\xAF"],"\x8C"=>[89,"\xAF"],"\x8D"=>[96,"\xAF"],"\x8E"=>[97,"\xAF"],"\x8F"=>[99,"\xAF"],"\x90"=>[106,"\xAF"],"\x91"=>[136,"\xAF"],"\x92"=>[139,"\xAF"],"\x93"=>[141,"\xAF"],"\x94"=>[145,"\xAF"],"\x95"=>[147,"\xAF"],"\x96"=>[149,"\xAF"],"\x97"=>[101,"\xAF"],"\x98"=>[112,"\xAF"],"\x99"=>[117,"\xAF"],"\x9A"=>[120,"\xAF"],"\x9B"=>[124,"\xAF"],"\x9C"=>[127,"\xAF"],"\x9D"=>[144,"\xAF"],"\x9E"=>[152,"\xAF"],"\x9F"=>[11,"\xAF"],"\xA0"=>[0,"\xB40"],"\xA1"=>[0,"\xB41"],"\xA2"=>[0,"\xB42"],"\xA3"=>[0,"\xB4a"],"\xA4"=>[0,"\xB4c"],"\xA5"=>[0,"\xB4e"],"\xA6"=>[0,"\xB4i"],"\xA7"=>[0,"\xB4o"],"\xA8"=>[0,"\xB4s"],"\xA9"=>[0,"\xB4t"],"\xAA"=>[73,"\xB4"],"\xAB"=>[88,"\xB4"],"\xAC"=>[89,"\xB4"],"\xAD"=>[96,"\xB4"],"\xAE"=>[97,"\xB4"],"\xAF"=>[99,"\xB4"],"\xB0"=>[106,"\xB4"],"\xB1"=>[136,"\xB4"],"\xB2"=>[139,"\xB4"],"\xB3"=>[141,"\xB4"],"\xB4"=>[145,"\xB4"],"\xB5"=>[147,"\xB4"],"\xB6"=>[149,"\xB4"],"\xB7"=>[101,"\xB4"],"\xB8"=>[112,"\xB4"],"\xB9"=>[117,"\xB4"],"\xBA"=>[120,"\xB4"],"\xBB"=>[124,"\xB4"],"\xBC"=>[127,"\xB4"],"\xBD"=>[144,"\xB4"],"\xBE"=>[152,"\xB4"],"\xBF"=>[11,"\xB4"],"\xC0"=>[0,"\xB60"],"\xC1"=>[0,"\xB61"],"\xC2"=>[0,"\xB62"],"\xC3"=>[0,"\xB6a"],"\xC4"=>[0,"\xB6c"],"\xC5"=>[0,"\xB6e"],"\xC6"=>[0,"\xB6i"],"\xC7"=>[0,"\xB6o"],"\xC8"=>[0,"\xB6s"],"\xC9"=>[0,"\xB6t"],"\xCA"=>[73,"\xB6"],"\xCB"=>[88,"\xB6"],"\xCC"=>[89,"\xB6"],"\xCD"=>[96,"\xB6"],"\xCE"=>[97,"\xB6"],"\xCF"=>[99,"\xB6"],"\xD0"=>[106,"\xB6"],"\xD1"=>[136,"\xB6"],"\xD2"=>[139,"\xB6"],"\xD3"=>[141,"\xB6"],"\xD4"=>[145,"\xB6"],"\xD5"=>[147,"\xB6"],"\xD6"=>[149,"\xB6"],"\xD7"=>[101,"\xB6"],"\xD8"=>[112,"\xB6"],"\xD9"=>[117,"\xB6"],"\xDA"=>[120,"\xB6"],"\xDB"=>[124,"\xB6"],"\xDC"=>[127,"\xB6"],"\xDD"=>[144,"\xB6"],"\xDE"=>[152,"\xB6"],"\xDF"=>[11,"\xB6"],"\xE0"=>[0,"\xB70"],"\xE1"=>[0,"\xB71"],"\xE2"=>[0,"\xB72"],"\xE3"=>[0,"\xB7a"],"\xE4"=>[0,"\xB7c"],"\xE5"=>[0,"\xB7e"],"\xE6"=>[0,"\xB7i"],"\xE7"=>[0,"\xB7o"],"\xE8"=>[0,"\xB7s"],"\xE9"=>[0,"\xB7t"],"\xEA"=>[73,"\xB7"],"\xEB"=>[88,"\xB7"],"\xEC"=>[89,"\xB7"],"\xED"=>[96,"\xB7"],"\xEE"=>[97,"\xB7"],"\xEF"=>[99,"\xB7"],"\xF0"=>[106,"\xB7"],"\xF1"=>[136,"\xB7"],"\xF2"=>[139,"\xB7"],"\xF3"=>[141,"\xB7"],"\xF4"=>[145,"\xB7"],"\xF5"=>[147,"\xB7"],"\xF6"=>[149,"\xB7"],"\xF7"=>[101,"\xB7"],"\xF8"=>[112,"\xB7"],"\xF9"=>[117,"\xB7"],"\xFA"=>[120,"\xB7"],"\xFB"=>[124,"\xB7"],"\xFC"=>[127,"\xB7"],"\xFD"=>[144,"\xB7"],"\xFE"=>[152,"\xB7"],"\xFF"=>[11,"\xB7"],],["\x00"=>[94,"\xA70"],"\x01"=>[76,"\xA70"],"\x02"=>[104,"\xA70"],"\x03"=>[16,"\xA70"],"\x04"=>[94,"\xA71"],"\x05"=>[76,"\xA71"],"\x06"=>[104,"\xA71"],"\x07"=>[16,"\xA71"],"\x08"=>[94,"\xA72"],"\x09"=>[76,"\xA72"],"\x0A"=>[104,"\xA72"],"\x0B"=>[16,"\xA72"],"\x0C"=>[94,"\xA7a"],"\x0D"=>[76,"\xA7a"],"\x0E"=>[104,"\xA7a"],"\x0F"=>[16,"\xA7a"],"\x10"=>[94,"\xA7c"],"\x11"=>[76,"\xA7c"],"\x12"=>[104,"\xA7c"],"\x13"=>[16,"\xA7c"],"\x14"=>[94,"\xA7e"],"\x15"=>[76,"\xA7e"],"\x16"=>[104,"\xA7e"],"\x17"=>[16,"\xA7e"],"\x18"=>[94,"\xA7i"],"\x19"=>[76,"\xA7i"],"\x1A"=>[104,"\xA7i"],"\x1B"=>[16,"\xA7i"],"\x1C"=>[94,"\xA7o"],"\x1D"=>[76,"\xA7o"],"\x1E"=>[104,"\xA7o"],"\x1F"=>[16,"\xA7o"],"\x20"=>[94,"\xA7s"],"\x21"=>[76,"\xA7s"],"\x22"=>[104,"\xA7s"],"\x23"=>[16,"\xA7s"],"\x24"=>[94,"\xA7t"],"\x25"=>[76,"\xA7t"],"\x26"=>[104,"\xA7t"],"\x27"=>[16,"\xA7t"],"\x28"=>[77,"\xA7\x20"],"\x29"=>[18,"\xA7\x20"],"\x2A"=>[77,"\xA7\x25"],"\x2B"=>[18,"\xA7\x25"],"\x2C"=>[77,"\xA7-"],"-"=>[18,"\xA7-"],"."=>[77,"\xA7."],"\x2F"=>[18,"\xA7."],[77,"\xA7\x2F"],[18,"\xA7\x2F"],[77,"\xA73"],[18,"\xA73"],[77,"\xA74"],[18,"\xA74"],[77,"\xA75"],[18,"\xA75"],[77,"\xA76"],[18,"\xA76"],"\x3A"=>[77,"\xA77"],"\x3B"=>[18,"\xA77"],"\x3C"=>[77,"\xA78"],"\x3D"=>[18,"\xA78"],"\x3E"=>[77,"\xA79"],"\x3F"=>[18,"\xA79"],"\x40"=>[77,"\xA7\x3D"],"A"=>[18,"\xA7\x3D"],"B"=>[77,"\xA7A"],"C"=>[18,"\xA7A"],"D"=>[77,"\xA7_"],"E"=>[18,"\xA7_"],"F"=>[77,"\xA7b"],"G"=>[18,"\xA7b"],"H"=>[77,"\xA7d"],"I"=>[18,"\xA7d"],"J"=>[77,"\xA7f"],"K"=>[18,"\xA7f"],"L"=>[77,"\xA7g"],"M"=>[18,"\xA7g"],"N"=>[77,"\xA7h"],"O"=>[18,"\xA7h"],"P"=>[77,"\xA7l"],"Q"=>[18,"\xA7l"],"R"=>[77,"\xA7m"],"S"=>[18,"\xA7m"],"T"=>[77,"\xA7n"],"U"=>[18,"\xA7n"],"V"=>[77,"\xA7p"],"W"=>[18,"\xA7p"],"X"=>[77,"\xA7r"],"Y"=>[18,"\xA7r"],"Z"=>[77,"\xA7u"],"\x5B"=>[18,"\xA7u"],"\x5C"=>[0,"\xA7\x3A"],"\x5D"=>[0,"\xA7B"],"\x5E"=>[0,"\xA7C"],"_"=>[0,"\xA7D"],"\x60"=>[0,"\xA7E"],"a"=>[0,"\xA7F"],"b"=>[0,"\xA7G"],"c"=>[0,"\xA7H"],"d"=>[0,"\xA7I"],"e"=>[0,"\xA7J"],"f"=>[0,"\xA7K"],"g"=>[0,"\xA7L"],"h"=>[0,"\xA7M"],"i"=>[0,"\xA7N"],"j"=>[0,"\xA7O"],"k"=>[0,"\xA7P"],"l"=>[0,"\xA7Q"],"m"=>[0,"\xA7R"],"n"=>[0,"\xA7S"],"o"=>[0,"\xA7T"],"p"=>[0,"\xA7U"],"q"=>[0,"\xA7V"],"r"=>[0,"\xA7W"],"s"=>[0,"\xA7Y"],"t"=>[0,"\xA7j"],"u"=>[0,"\xA7k"],"v"=>[0,"\xA7q"],"w"=>[0,"\xA7v"],"x"=>[0,"\xA7w"],"y"=>[0,"\xA7x"],"z"=>[0,"\xA7y"],"\x7B"=>[0,"\xA7z"],"\x7C"=>[82,"\xA7"],"\x7D"=>[87,"\xA7"],"~"=>[130,"\xA7"],"\x7F"=>[9,"\xA7"],"\x80"=>[94,"\xAC0"],"\x81"=>[76,"\xAC0"],"\x82"=>[104,"\xAC0"],"\x83"=>[16,"\xAC0"],"\x84"=>[94,"\xAC1"],"\x85"=>[76,"\xAC1"],"\x86"=>[104,"\xAC1"],"\x87"=>[16,"\xAC1"],"\x88"=>[94,"\xAC2"],"\x89"=>[76,"\xAC2"],"\x8A"=>[104,"\xAC2"],"\x8B"=>[16,"\xAC2"],"\x8C"=>[94,"\xACa"],"\x8D"=>[76,"\xACa"],"\x8E"=>[104,"\xACa"],"\x8F"=>[16,"\xACa"],"\x90"=>[94,"\xACc"],"\x91"=>[76,"\xACc"],"\x92"=>[104,"\xACc"],"\x93"=>[16,"\xACc"],"\x94"=>[94,"\xACe"],"\x95"=>[76,"\xACe"],"\x96"=>[104,"\xACe"],"\x97"=>[16,"\xACe"],"\x98"=>[94,"\xACi"],"\x99"=>[76,"\xACi"],"\x9A"=>[104,"\xACi"],"\x9B"=>[16,"\xACi"],"\x9C"=>[94,"\xACo"],"\x9D"=>[76,"\xACo"],"\x9E"=>[104,"\xACo"],"\x9F"=>[16,"\xACo"],"\xA0"=>[94,"\xACs"],"\xA1"=>[76,"\xACs"],"\xA2"=>[104,"\xACs"],"\xA3"=>[16,"\xACs"],"\xA4"=>[94,"\xACt"],"\xA5"=>[76,"\xACt"],"\xA6"=>[104,"\xACt"],"\xA7"=>[16,"\xACt"],"\xA8"=>[77,"\xAC\x20"],"\xA9"=>[18,"\xAC\x20"],"\xAA"=>[77,"\xAC\x25"],"\xAB"=>[18,"\xAC\x25"],"\xAC"=>[77,"\xAC-"],"\xAD"=>[18,"\xAC-"],"\xAE"=>[77,"\xAC."],"\xAF"=>[18,"\xAC."],"\xB0"=>[77,"\xAC\x2F"],"\xB1"=>[18,"\xAC\x2F"],"\xB2"=>[77,"\xAC3"],"\xB3"=>[18,"\xAC3"],"\xB4"=>[77,"\xAC4"],"\xB5"=>[18,"\xAC4"],"\xB6"=>[77,"\xAC5"],"\xB7"=>[18,"\xAC5"],"\xB8"=>[77,"\xAC6"],"\xB9"=>[18,"\xAC6"],"\xBA"=>[77,"\xAC7"],"\xBB"=>[18,"\xAC7"],"\xBC"=>[77,"\xAC8"],"\xBD"=>[18,"\xAC8"],"\xBE"=>[77,"\xAC9"],"\xBF"=>[18,"\xAC9"],"\xC0"=>[77,"\xAC\x3D"],"\xC1"=>[18,"\xAC\x3D"],"\xC2"=>[77,"\xACA"],"\xC3"=>[18,"\xACA"],"\xC4"=>[77,"\xAC_"],"\xC5"=>[18,"\xAC_"],"\xC6"=>[77,"\xACb"],"\xC7"=>[18,"\xACb"],"\xC8"=>[77,"\xACd"],"\xC9"=>[18,"\xACd"],"\xCA"=>[77,"\xACf"],"\xCB"=>[18,"\xACf"],"\xCC"=>[77,"\xACg"],"\xCD"=>[18,"\xACg"],"\xCE"=>[77,"\xACh"],"\xCF"=>[18,"\xACh"],"\xD0"=>[77,"\xACl"],"\xD1"=>[18,"\xACl"],"\xD2"=>[77,"\xACm"],"\xD3"=>[18,"\xACm"],"\xD4"=>[77,"\xACn"],"\xD5"=>[18,"\xACn"],"\xD6"=>[77,"\xACp"],"\xD7"=>[18,"\xACp"],"\xD8"=>[77,"\xACr"],"\xD9"=>[18,"\xACr"],"\xDA"=>[77,"\xACu"],"\xDB"=>[18,"\xACu"],"\xDC"=>[0,"\xAC\x3A"],"\xDD"=>[0,"\xACB"],"\xDE"=>[0,"\xACC"],"\xDF"=>[0,"\xACD"],"\xE0"=>[0,"\xACE"],"\xE1"=>[0,"\xACF"],"\xE2"=>[0,"\xACG"],"\xE3"=>[0,"\xACH"],"\xE4"=>[0,"\xACI"],"\xE5"=>[0,"\xACJ"],"\xE6"=>[0,"\xACK"],"\xE7"=>[0,"\xACL"],"\xE8"=>[0,"\xACM"],"\xE9"=>[0,"\xACN"],"\xEA"=>[0,"\xACO"],"\xEB"=>[0,"\xACP"],"\xEC"=>[0,"\xACQ"],"\xED"=>[0,"\xACR"],"\xEE"=>[0,"\xACS"],"\xEF"=>[0,"\xACT"],"\xF0"=>[0,"\xACU"],"\xF1"=>[0,"\xACV"],"\xF2"=>[0,"\xACW"],"\xF3"=>[0,"\xACY"],"\xF4"=>[0,"\xACj"],"\xF5"=>[0,"\xACk"],"\xF6"=>[0,"\xACq"],"\xF7"=>[0,"\xACv"],"\xF8"=>[0,"\xACw"],"\xF9"=>[0,"\xACx"],"\xFA"=>[0,"\xACy"],"\xFB"=>[0,"\xACz"],"\xFC"=>[82,"\xAC"],"\xFD"=>[87,"\xAC"],"\xFE"=>[130,"\xAC"],"\xFF"=>[9,"\xAC"],],["\x00"=>[94,"\xA80"],"\x01"=>[76,"\xA80"],"\x02"=>[104,"\xA80"],"\x03"=>[16,"\xA80"],"\x04"=>[94,"\xA81"],"\x05"=>[76,"\xA81"],"\x06"=>[104,"\xA81"],"\x07"=>[16,"\xA81"],"\x08"=>[94,"\xA82"],"\x09"=>[76,"\xA82"],"\x0A"=>[104,"\xA82"],"\x0B"=>[16,"\xA82"],"\x0C"=>[94,"\xA8a"],"\x0D"=>[76,"\xA8a"],"\x0E"=>[104,"\xA8a"],"\x0F"=>[16,"\xA8a"],"\x10"=>[94,"\xA8c"],"\x11"=>[76,"\xA8c"],"\x12"=>[104,"\xA8c"],"\x13"=>[16,"\xA8c"],"\x14"=>[94,"\xA8e"],"\x15"=>[76,"\xA8e"],"\x16"=>[104,"\xA8e"],"\x17"=>[16,"\xA8e"],"\x18"=>[94,"\xA8i"],"\x19"=>[76,"\xA8i"],"\x1A"=>[104,"\xA8i"],"\x1B"=>[16,"\xA8i"],"\x1C"=>[94,"\xA8o"],"\x1D"=>[76,"\xA8o"],"\x1E"=>[104,"\xA8o"],"\x1F"=>[16,"\xA8o"],"\x20"=>[94,"\xA8s"],"\x21"=>[76,"\xA8s"],"\x22"=>[104,"\xA8s"],"\x23"=>[16,"\xA8s"],"\x24"=>[94,"\xA8t"],"\x25"=>[76,"\xA8t"],"\x26"=>[104,"\xA8t"],"\x27"=>[16,"\xA8t"],"\x28"=>[77,"\xA8\x20"],"\x29"=>[18,"\xA8\x20"],"\x2A"=>[77,"\xA8\x25"],"\x2B"=>[18,"\xA8\x25"],"\x2C"=>[77,"\xA8-"],"-"=>[18,"\xA8-"],"."=>[77,"\xA8."],"\x2F"=>[18,"\xA8."],[77,"\xA8\x2F"],[18,"\xA8\x2F"],[77,"\xA83"],[18,"\xA83"],[77,"\xA84"],[18,"\xA84"],[77,"\xA85"],[18,"\xA85"],[77,"\xA86"],[18,"\xA86"],"\x3A"=>[77,"\xA87"],"\x3B"=>[18,"\xA87"],"\x3C"=>[77,"\xA88"],"\x3D"=>[18,"\xA88"],"\x3E"=>[77,"\xA89"],"\x3F"=>[18,"\xA89"],"\x40"=>[77,"\xA8\x3D"],"A"=>[18,"\xA8\x3D"],"B"=>[77,"\xA8A"],"C"=>[18,"\xA8A"],"D"=>[77,"\xA8_"],"E"=>[18,"\xA8_"],"F"=>[77,"\xA8b"],"G"=>[18,"\xA8b"],"H"=>[77,"\xA8d"],"I"=>[18,"\xA8d"],"J"=>[77,"\xA8f"],"K"=>[18,"\xA8f"],"L"=>[77,"\xA8g"],"M"=>[18,"\xA8g"],"N"=>[77,"\xA8h"],"O"=>[18,"\xA8h"],"P"=>[77,"\xA8l"],"Q"=>[18,"\xA8l"],"R"=>[77,"\xA8m"],"S"=>[18,"\xA8m"],"T"=>[77,"\xA8n"],"U"=>[18,"\xA8n"],"V"=>[77,"\xA8p"],"W"=>[18,"\xA8p"],"X"=>[77,"\xA8r"],"Y"=>[18,"\xA8r"],"Z"=>[77,"\xA8u"],"\x5B"=>[18,"\xA8u"],"\x5C"=>[0,"\xA8\x3A"],"\x5D"=>[0,"\xA8B"],"\x5E"=>[0,"\xA8C"],"_"=>[0,"\xA8D"],"\x60"=>[0,"\xA8E"],"a"=>[0,"\xA8F"],"b"=>[0,"\xA8G"],"c"=>[0,"\xA8H"],"d"=>[0,"\xA8I"],"e"=>[0,"\xA8J"],"f"=>[0,"\xA8K"],"g"=>[0,"\xA8L"],"h"=>[0,"\xA8M"],"i"=>[0,"\xA8N"],"j"=>[0,"\xA8O"],"k"=>[0,"\xA8P"],"l"=>[0,"\xA8Q"],"m"=>[0,"\xA8R"],"n"=>[0,"\xA8S"],"o"=>[0,"\xA8T"],"p"=>[0,"\xA8U"],"q"=>[0,"\xA8V"],"r"=>[0,"\xA8W"],"s"=>[0,"\xA8Y"],"t"=>[0,"\xA8j"],"u"=>[0,"\xA8k"],"v"=>[0,"\xA8q"],"w"=>[0,"\xA8v"],"x"=>[0,"\xA8w"],"y"=>[0,"\xA8x"],"z"=>[0,"\xA8y"],"\x7B"=>[0,"\xA8z"],"\x7C"=>[82,"\xA8"],"\x7D"=>[87,"\xA8"],"~"=>[130,"\xA8"],"\x7F"=>[9,"\xA8"],"\x80"=>[94,"\xAE0"],"\x81"=>[76,"\xAE0"],"\x82"=>[104,"\xAE0"],"\x83"=>[16,"\xAE0"],"\x84"=>[94,"\xAE1"],"\x85"=>[76,"\xAE1"],"\x86"=>[104,"\xAE1"],"\x87"=>[16,"\xAE1"],"\x88"=>[94,"\xAE2"],"\x89"=>[76,"\xAE2"],"\x8A"=>[104,"\xAE2"],"\x8B"=>[16,"\xAE2"],"\x8C"=>[94,"\xAEa"],"\x8D"=>[76,"\xAEa"],"\x8E"=>[104,"\xAEa"],"\x8F"=>[16,"\xAEa"],"\x90"=>[94,"\xAEc"],"\x91"=>[76,"\xAEc"],"\x92"=>[104,"\xAEc"],"\x93"=>[16,"\xAEc"],"\x94"=>[94,"\xAEe"],"\x95"=>[76,"\xAEe"],"\x96"=>[104,"\xAEe"],"\x97"=>[16,"\xAEe"],"\x98"=>[94,"\xAEi"],"\x99"=>[76,"\xAEi"],"\x9A"=>[104,"\xAEi"],"\x9B"=>[16,"\xAEi"],"\x9C"=>[94,"\xAEo"],"\x9D"=>[76,"\xAEo"],"\x9E"=>[104,"\xAEo"],"\x9F"=>[16,"\xAEo"],"\xA0"=>[94,"\xAEs"],"\xA1"=>[76,"\xAEs"],"\xA2"=>[104,"\xAEs"],"\xA3"=>[16,"\xAEs"],"\xA4"=>[94,"\xAEt"],"\xA5"=>[76,"\xAEt"],"\xA6"=>[104,"\xAEt"],"\xA7"=>[16,"\xAEt"],"\xA8"=>[77,"\xAE\x20"],"\xA9"=>[18,"\xAE\x20"],"\xAA"=>[77,"\xAE\x25"],"\xAB"=>[18,"\xAE\x25"],"\xAC"=>[77,"\xAE-"],"\xAD"=>[18,"\xAE-"],"\xAE"=>[77,"\xAE."],"\xAF"=>[18,"\xAE."],"\xB0"=>[77,"\xAE\x2F"],"\xB1"=>[18,"\xAE\x2F"],"\xB2"=>[77,"\xAE3"],"\xB3"=>[18,"\xAE3"],"\xB4"=>[77,"\xAE4"],"\xB5"=>[18,"\xAE4"],"\xB6"=>[77,"\xAE5"],"\xB7"=>[18,"\xAE5"],"\xB8"=>[77,"\xAE6"],"\xB9"=>[18,"\xAE6"],"\xBA"=>[77,"\xAE7"],"\xBB"=>[18,"\xAE7"],"\xBC"=>[77,"\xAE8"],"\xBD"=>[18,"\xAE8"],"\xBE"=>[77,"\xAE9"],"\xBF"=>[18,"\xAE9"],"\xC0"=>[77,"\xAE\x3D"],"\xC1"=>[18,"\xAE\x3D"],"\xC2"=>[77,"\xAEA"],"\xC3"=>[18,"\xAEA"],"\xC4"=>[77,"\xAE_"],"\xC5"=>[18,"\xAE_"],"\xC6"=>[77,"\xAEb"],"\xC7"=>[18,"\xAEb"],"\xC8"=>[77,"\xAEd"],"\xC9"=>[18,"\xAEd"],"\xCA"=>[77,"\xAEf"],"\xCB"=>[18,"\xAEf"],"\xCC"=>[77,"\xAEg"],"\xCD"=>[18,"\xAEg"],"\xCE"=>[77,"\xAEh"],"\xCF"=>[18,"\xAEh"],"\xD0"=>[77,"\xAEl"],"\xD1"=>[18,"\xAEl"],"\xD2"=>[77,"\xAEm"],"\xD3"=>[18,"\xAEm"],"\xD4"=>[77,"\xAEn"],"\xD5"=>[18,"\xAEn"],"\xD6"=>[77,"\xAEp"],"\xD7"=>[18,"\xAEp"],"\xD8"=>[77,"\xAEr"],"\xD9"=>[18,"\xAEr"],"\xDA"=>[77,"\xAEu"],"\xDB"=>[18,"\xAEu"],"\xDC"=>[0,"\xAE\x3A"],"\xDD"=>[0,"\xAEB"],"\xDE"=>[0,"\xAEC"],"\xDF"=>[0,"\xAED"],"\xE0"=>[0,"\xAEE"],"\xE1"=>[0,"\xAEF"],"\xE2"=>[0,"\xAEG"],"\xE3"=>[0,"\xAEH"],"\xE4"=>[0,"\xAEI"],"\xE5"=>[0,"\xAEJ"],"\xE6"=>[0,"\xAEK"],"\xE7"=>[0,"\xAEL"],"\xE8"=>[0,"\xAEM"],"\xE9"=>[0,"\xAEN"],"\xEA"=>[0,"\xAEO"],"\xEB"=>[0,"\xAEP"],"\xEC"=>[0,"\xAEQ"],"\xED"=>[0,"\xAER"],"\xEE"=>[0,"\xAES"],"\xEF"=>[0,"\xAET"],"\xF0"=>[0,"\xAEU"],"\xF1"=>[0,"\xAEV"],"\xF2"=>[0,"\xAEW"],"\xF3"=>[0,"\xAEY"],"\xF4"=>[0,"\xAEj"],"\xF5"=>[0,"\xAEk"],"\xF6"=>[0,"\xAEq"],"\xF7"=>[0,"\xAEv"],"\xF8"=>[0,"\xAEw"],"\xF9"=>[0,"\xAEx"],"\xFA"=>[0,"\xAEy"],"\xFB"=>[0,"\xAEz"],"\xFC"=>[82,"\xAE"],"\xFD"=>[87,"\xAE"],"\xFE"=>[130,"\xAE"],"\xFF"=>[9,"\xAE"],],["\x00"=>[94,"\xAA0"],"\x01"=>[76,"\xAA0"],"\x02"=>[104,"\xAA0"],"\x03"=>[16,"\xAA0"],"\x04"=>[94,"\xAA1"],"\x05"=>[76,"\xAA1"],"\x06"=>[104,"\xAA1"],"\x07"=>[16,"\xAA1"],"\x08"=>[94,"\xAA2"],"\x09"=>[76,"\xAA2"],"\x0A"=>[104,"\xAA2"],"\x0B"=>[16,"\xAA2"],"\x0C"=>[94,"\xAAa"],"\x0D"=>[76,"\xAAa"],"\x0E"=>[104,"\xAAa"],"\x0F"=>[16,"\xAAa"],"\x10"=>[94,"\xAAc"],"\x11"=>[76,"\xAAc"],"\x12"=>[104,"\xAAc"],"\x13"=>[16,"\xAAc"],"\x14"=>[94,"\xAAe"],"\x15"=>[76,"\xAAe"],"\x16"=>[104,"\xAAe"],"\x17"=>[16,"\xAAe"],"\x18"=>[94,"\xAAi"],"\x19"=>[76,"\xAAi"],"\x1A"=>[104,"\xAAi"],"\x1B"=>[16,"\xAAi"],"\x1C"=>[94,"\xAAo"],"\x1D"=>[76,"\xAAo"],"\x1E"=>[104,"\xAAo"],"\x1F"=>[16,"\xAAo"],"\x20"=>[94,"\xAAs"],"\x21"=>[76,"\xAAs"],"\x22"=>[104,"\xAAs"],"\x23"=>[16,"\xAAs"],"\x24"=>[94,"\xAAt"],"\x25"=>[76,"\xAAt"],"\x26"=>[104,"\xAAt"],"\x27"=>[16,"\xAAt"],"\x28"=>[77,"\xAA\x20"],"\x29"=>[18,"\xAA\x20"],"\x2A"=>[77,"\xAA\x25"],"\x2B"=>[18,"\xAA\x25"],"\x2C"=>[77,"\xAA-"],"-"=>[18,"\xAA-"],"."=>[77,"\xAA."],"\x2F"=>[18,"\xAA."],[77,"\xAA\x2F"],[18,"\xAA\x2F"],[77,"\xAA3"],[18,"\xAA3"],[77,"\xAA4"],[18,"\xAA4"],[77,"\xAA5"],[18,"\xAA5"],[77,"\xAA6"],[18,"\xAA6"],"\x3A"=>[77,"\xAA7"],"\x3B"=>[18,"\xAA7"],"\x3C"=>[77,"\xAA8"],"\x3D"=>[18,"\xAA8"],"\x3E"=>[77,"\xAA9"],"\x3F"=>[18,"\xAA9"],"\x40"=>[77,"\xAA\x3D"],"A"=>[18,"\xAA\x3D"],"B"=>[77,"\xAAA"],"C"=>[18,"\xAAA"],"D"=>[77,"\xAA_"],"E"=>[18,"\xAA_"],"F"=>[77,"\xAAb"],"G"=>[18,"\xAAb"],"H"=>[77,"\xAAd"],"I"=>[18,"\xAAd"],"J"=>[77,"\xAAf"],"K"=>[18,"\xAAf"],"L"=>[77,"\xAAg"],"M"=>[18,"\xAAg"],"N"=>[77,"\xAAh"],"O"=>[18,"\xAAh"],"P"=>[77,"\xAAl"],"Q"=>[18,"\xAAl"],"R"=>[77,"\xAAm"],"S"=>[18,"\xAAm"],"T"=>[77,"\xAAn"],"U"=>[18,"\xAAn"],"V"=>[77,"\xAAp"],"W"=>[18,"\xAAp"],"X"=>[77,"\xAAr"],"Y"=>[18,"\xAAr"],"Z"=>[77,"\xAAu"],"\x5B"=>[18,"\xAAu"],"\x5C"=>[0,"\xAA\x3A"],"\x5D"=>[0,"\xAAB"],"\x5E"=>[0,"\xAAC"],"_"=>[0,"\xAAD"],"\x60"=>[0,"\xAAE"],"a"=>[0,"\xAAF"],"b"=>[0,"\xAAG"],"c"=>[0,"\xAAH"],"d"=>[0,"\xAAI"],"e"=>[0,"\xAAJ"],"f"=>[0,"\xAAK"],"g"=>[0,"\xAAL"],"h"=>[0,"\xAAM"],"i"=>[0,"\xAAN"],"j"=>[0,"\xAAO"],"k"=>[0,"\xAAP"],"l"=>[0,"\xAAQ"],"m"=>[0,"\xAAR"],"n"=>[0,"\xAAS"],"o"=>[0,"\xAAT"],"p"=>[0,"\xAAU"],"q"=>[0,"\xAAV"],"r"=>[0,"\xAAW"],"s"=>[0,"\xAAY"],"t"=>[0,"\xAAj"],"u"=>[0,"\xAAk"],"v"=>[0,"\xAAq"],"w"=>[0,"\xAAv"],"x"=>[0,"\xAAw"],"y"=>[0,"\xAAx"],"z"=>[0,"\xAAy"],"\x7B"=>[0,"\xAAz"],"\x7C"=>[82,"\xAA"],"\x7D"=>[87,"\xAA"],"~"=>[130,"\xAA"],"\x7F"=>[9,"\xAA"],"\x80"=>[94,"\xAD0"],"\x81"=>[76,"\xAD0"],"\x82"=>[104,"\xAD0"],"\x83"=>[16,"\xAD0"],"\x84"=>[94,"\xAD1"],"\x85"=>[76,"\xAD1"],"\x86"=>[104,"\xAD1"],"\x87"=>[16,"\xAD1"],"\x88"=>[94,"\xAD2"],"\x89"=>[76,"\xAD2"],"\x8A"=>[104,"\xAD2"],"\x8B"=>[16,"\xAD2"],"\x8C"=>[94,"\xADa"],"\x8D"=>[76,"\xADa"],"\x8E"=>[104,"\xADa"],"\x8F"=>[16,"\xADa"],"\x90"=>[94,"\xADc"],"\x91"=>[76,"\xADc"],"\x92"=>[104,"\xADc"],"\x93"=>[16,"\xADc"],"\x94"=>[94,"\xADe"],"\x95"=>[76,"\xADe"],"\x96"=>[104,"\xADe"],"\x97"=>[16,"\xADe"],"\x98"=>[94,"\xADi"],"\x99"=>[76,"\xADi"],"\x9A"=>[104,"\xADi"],"\x9B"=>[16,"\xADi"],"\x9C"=>[94,"\xADo"],"\x9D"=>[76,"\xADo"],"\x9E"=>[104,"\xADo"],"\x9F"=>[16,"\xADo"],"\xA0"=>[94,"\xADs"],"\xA1"=>[76,"\xADs"],"\xA2"=>[104,"\xADs"],"\xA3"=>[16,"\xADs"],"\xA4"=>[94,"\xADt"],"\xA5"=>[76,"\xADt"],"\xA6"=>[104,"\xADt"],"\xA7"=>[16,"\xADt"],"\xA8"=>[77,"\xAD\x20"],"\xA9"=>[18,"\xAD\x20"],"\xAA"=>[77,"\xAD\x25"],"\xAB"=>[18,"\xAD\x25"],"\xAC"=>[77,"\xAD-"],"\xAD"=>[18,"\xAD-"],"\xAE"=>[77,"\xAD."],"\xAF"=>[18,"\xAD."],"\xB0"=>[77,"\xAD\x2F"],"\xB1"=>[18,"\xAD\x2F"],"\xB2"=>[77,"\xAD3"],"\xB3"=>[18,"\xAD3"],"\xB4"=>[77,"\xAD4"],"\xB5"=>[18,"\xAD4"],"\xB6"=>[77,"\xAD5"],"\xB7"=>[18,"\xAD5"],"\xB8"=>[77,"\xAD6"],"\xB9"=>[18,"\xAD6"],"\xBA"=>[77,"\xAD7"],"\xBB"=>[18,"\xAD7"],"\xBC"=>[77,"\xAD8"],"\xBD"=>[18,"\xAD8"],"\xBE"=>[77,"\xAD9"],"\xBF"=>[18,"\xAD9"],"\xC0"=>[77,"\xAD\x3D"],"\xC1"=>[18,"\xAD\x3D"],"\xC2"=>[77,"\xADA"],"\xC3"=>[18,"\xADA"],"\xC4"=>[77,"\xAD_"],"\xC5"=>[18,"\xAD_"],"\xC6"=>[77,"\xADb"],"\xC7"=>[18,"\xADb"],"\xC8"=>[77,"\xADd"],"\xC9"=>[18,"\xADd"],"\xCA"=>[77,"\xADf"],"\xCB"=>[18,"\xADf"],"\xCC"=>[77,"\xADg"],"\xCD"=>[18,"\xADg"],"\xCE"=>[77,"\xADh"],"\xCF"=>[18,"\xADh"],"\xD0"=>[77,"\xADl"],"\xD1"=>[18,"\xADl"],"\xD2"=>[77,"\xADm"],"\xD3"=>[18,"\xADm"],"\xD4"=>[77,"\xADn"],"\xD5"=>[18,"\xADn"],"\xD6"=>[77,"\xADp"],"\xD7"=>[18,"\xADp"],"\xD8"=>[77,"\xADr"],"\xD9"=>[18,"\xADr"],"\xDA"=>[77,"\xADu"],"\xDB"=>[18,"\xADu"],"\xDC"=>[0,"\xAD\x3A"],"\xDD"=>[0,"\xADB"],"\xDE"=>[0,"\xADC"],"\xDF"=>[0,"\xADD"],"\xE0"=>[0,"\xADE"],"\xE1"=>[0,"\xADF"],"\xE2"=>[0,"\xADG"],"\xE3"=>[0,"\xADH"],"\xE4"=>[0,"\xADI"],"\xE5"=>[0,"\xADJ"],"\xE6"=>[0,"\xADK"],"\xE7"=>[0,"\xADL"],"\xE8"=>[0,"\xADM"],"\xE9"=>[0,"\xADN"],"\xEA"=>[0,"\xADO"],"\xEB"=>[0,"\xADP"],"\xEC"=>[0,"\xADQ"],"\xED"=>[0,"\xADR"],"\xEE"=>[0,"\xADS"],"\xEF"=>[0,"\xADT"],"\xF0"=>[0,"\xADU"],"\xF1"=>[0,"\xADV"],"\xF2"=>[0,"\xADW"],"\xF3"=>[0,"\xADY"],"\xF4"=>[0,"\xADj"],"\xF5"=>[0,"\xADk"],"\xF6"=>[0,"\xADq"],"\xF7"=>[0,"\xADv"],"\xF8"=>[0,"\xADw"],"\xF9"=>[0,"\xADx"],"\xFA"=>[0,"\xADy"],"\xFB"=>[0,"\xADz"],"\xFC"=>[82,"\xAD"],"\xFD"=>[87,"\xAD"],"\xFE"=>[130,"\xAD"],"\xFF"=>[9,"\xAD"],],["\x00"=>[94,"\xAB0"],"\x01"=>[76,"\xAB0"],"\x02"=>[104,"\xAB0"],"\x03"=>[16,"\xAB0"],"\x04"=>[94,"\xAB1"],"\x05"=>[76,"\xAB1"],"\x06"=>[104,"\xAB1"],"\x07"=>[16,"\xAB1"],"\x08"=>[94,"\xAB2"],"\x09"=>[76,"\xAB2"],"\x0A"=>[104,"\xAB2"],"\x0B"=>[16,"\xAB2"],"\x0C"=>[94,"\xABa"],"\x0D"=>[76,"\xABa"],"\x0E"=>[104,"\xABa"],"\x0F"=>[16,"\xABa"],"\x10"=>[94,"\xABc"],"\x11"=>[76,"\xABc"],"\x12"=>[104,"\xABc"],"\x13"=>[16,"\xABc"],"\x14"=>[94,"\xABe"],"\x15"=>[76,"\xABe"],"\x16"=>[104,"\xABe"],"\x17"=>[16,"\xABe"],"\x18"=>[94,"\xABi"],"\x19"=>[76,"\xABi"],"\x1A"=>[104,"\xABi"],"\x1B"=>[16,"\xABi"],"\x1C"=>[94,"\xABo"],"\x1D"=>[76,"\xABo"],"\x1E"=>[104,"\xABo"],"\x1F"=>[16,"\xABo"],"\x20"=>[94,"\xABs"],"\x21"=>[76,"\xABs"],"\x22"=>[104,"\xABs"],"\x23"=>[16,"\xABs"],"\x24"=>[94,"\xABt"],"\x25"=>[76,"\xABt"],"\x26"=>[104,"\xABt"],"\x27"=>[16,"\xABt"],"\x28"=>[77,"\xAB\x20"],"\x29"=>[18,"\xAB\x20"],"\x2A"=>[77,"\xAB\x25"],"\x2B"=>[18,"\xAB\x25"],"\x2C"=>[77,"\xAB-"],"-"=>[18,"\xAB-"],"."=>[77,"\xAB."],"\x2F"=>[18,"\xAB."],[77,"\xAB\x2F"],[18,"\xAB\x2F"],[77,"\xAB3"],[18,"\xAB3"],[77,"\xAB4"],[18,"\xAB4"],[77,"\xAB5"],[18,"\xAB5"],[77,"\xAB6"],[18,"\xAB6"],"\x3A"=>[77,"\xAB7"],"\x3B"=>[18,"\xAB7"],"\x3C"=>[77,"\xAB8"],"\x3D"=>[18,"\xAB8"],"\x3E"=>[77,"\xAB9"],"\x3F"=>[18,"\xAB9"],"\x40"=>[77,"\xAB\x3D"],"A"=>[18,"\xAB\x3D"],"B"=>[77,"\xABA"],"C"=>[18,"\xABA"],"D"=>[77,"\xAB_"],"E"=>[18,"\xAB_"],"F"=>[77,"\xABb"],"G"=>[18,"\xABb"],"H"=>[77,"\xABd"],"I"=>[18,"\xABd"],"J"=>[77,"\xABf"],"K"=>[18,"\xABf"],"L"=>[77,"\xABg"],"M"=>[18,"\xABg"],"N"=>[77,"\xABh"],"O"=>[18,"\xABh"],"P"=>[77,"\xABl"],"Q"=>[18,"\xABl"],"R"=>[77,"\xABm"],"S"=>[18,"\xABm"],"T"=>[77,"\xABn"],"U"=>[18,"\xABn"],"V"=>[77,"\xABp"],"W"=>[18,"\xABp"],"X"=>[77,"\xABr"],"Y"=>[18,"\xABr"],"Z"=>[77,"\xABu"],"\x5B"=>[18,"\xABu"],"\x5C"=>[0,"\xAB\x3A"],"\x5D"=>[0,"\xABB"],"\x5E"=>[0,"\xABC"],"_"=>[0,"\xABD"],"\x60"=>[0,"\xABE"],"a"=>[0,"\xABF"],"b"=>[0,"\xABG"],"c"=>[0,"\xABH"],"d"=>[0,"\xABI"],"e"=>[0,"\xABJ"],"f"=>[0,"\xABK"],"g"=>[0,"\xABL"],"h"=>[0,"\xABM"],"i"=>[0,"\xABN"],"j"=>[0,"\xABO"],"k"=>[0,"\xABP"],"l"=>[0,"\xABQ"],"m"=>[0,"\xABR"],"n"=>[0,"\xABS"],"o"=>[0,"\xABT"],"p"=>[0,"\xABU"],"q"=>[0,"\xABV"],"r"=>[0,"\xABW"],"s"=>[0,"\xABY"],"t"=>[0,"\xABj"],"u"=>[0,"\xABk"],"v"=>[0,"\xABq"],"w"=>[0,"\xABv"],"x"=>[0,"\xABw"],"y"=>[0,"\xABx"],"z"=>[0,"\xABy"],"\x7B"=>[0,"\xABz"],"\x7C"=>[82,"\xAB"],"\x7D"=>[87,"\xAB"],"~"=>[130,"\xAB"],"\x7F"=>[9,"\xAB"],"\x80"=>[94,"\xCE0"],"\x81"=>[76,"\xCE0"],"\x82"=>[104,"\xCE0"],"\x83"=>[16,"\xCE0"],"\x84"=>[94,"\xCE1"],"\x85"=>[76,"\xCE1"],"\x86"=>[104,"\xCE1"],"\x87"=>[16,"\xCE1"],"\x88"=>[94,"\xCE2"],"\x89"=>[76,"\xCE2"],"\x8A"=>[104,"\xCE2"],"\x8B"=>[16,"\xCE2"],"\x8C"=>[94,"\xCEa"],"\x8D"=>[76,"\xCEa"],"\x8E"=>[104,"\xCEa"],"\x8F"=>[16,"\xCEa"],"\x90"=>[94,"\xCEc"],"\x91"=>[76,"\xCEc"],"\x92"=>[104,"\xCEc"],"\x93"=>[16,"\xCEc"],"\x94"=>[94,"\xCEe"],"\x95"=>[76,"\xCEe"],"\x96"=>[104,"\xCEe"],"\x97"=>[16,"\xCEe"],"\x98"=>[94,"\xCEi"],"\x99"=>[76,"\xCEi"],"\x9A"=>[104,"\xCEi"],"\x9B"=>[16,"\xCEi"],"\x9C"=>[94,"\xCEo"],"\x9D"=>[76,"\xCEo"],"\x9E"=>[104,"\xCEo"],"\x9F"=>[16,"\xCEo"],"\xA0"=>[94,"\xCEs"],"\xA1"=>[76,"\xCEs"],"\xA2"=>[104,"\xCEs"],"\xA3"=>[16,"\xCEs"],"\xA4"=>[94,"\xCEt"],"\xA5"=>[76,"\xCEt"],"\xA6"=>[104,"\xCEt"],"\xA7"=>[16,"\xCEt"],"\xA8"=>[77,"\xCE\x20"],"\xA9"=>[18,"\xCE\x20"],"\xAA"=>[77,"\xCE\x25"],"\xAB"=>[18,"\xCE\x25"],"\xAC"=>[77,"\xCE-"],"\xAD"=>[18,"\xCE-"],"\xAE"=>[77,"\xCE."],"\xAF"=>[18,"\xCE."],"\xB0"=>[77,"\xCE\x2F"],"\xB1"=>[18,"\xCE\x2F"],"\xB2"=>[77,"\xCE3"],"\xB3"=>[18,"\xCE3"],"\xB4"=>[77,"\xCE4"],"\xB5"=>[18,"\xCE4"],"\xB6"=>[77,"\xCE5"],"\xB7"=>[18,"\xCE5"],"\xB8"=>[77,"\xCE6"],"\xB9"=>[18,"\xCE6"],"\xBA"=>[77,"\xCE7"],"\xBB"=>[18,"\xCE7"],"\xBC"=>[77,"\xCE8"],"\xBD"=>[18,"\xCE8"],"\xBE"=>[77,"\xCE9"],"\xBF"=>[18,"\xCE9"],"\xC0"=>[77,"\xCE\x3D"],"\xC1"=>[18,"\xCE\x3D"],"\xC2"=>[77,"\xCEA"],"\xC3"=>[18,"\xCEA"],"\xC4"=>[77,"\xCE_"],"\xC5"=>[18,"\xCE_"],"\xC6"=>[77,"\xCEb"],"\xC7"=>[18,"\xCEb"],"\xC8"=>[77,"\xCEd"],"\xC9"=>[18,"\xCEd"],"\xCA"=>[77,"\xCEf"],"\xCB"=>[18,"\xCEf"],"\xCC"=>[77,"\xCEg"],"\xCD"=>[18,"\xCEg"],"\xCE"=>[77,"\xCEh"],"\xCF"=>[18,"\xCEh"],"\xD0"=>[77,"\xCEl"],"\xD1"=>[18,"\xCEl"],"\xD2"=>[77,"\xCEm"],"\xD3"=>[18,"\xCEm"],"\xD4"=>[77,"\xCEn"],"\xD5"=>[18,"\xCEn"],"\xD6"=>[77,"\xCEp"],"\xD7"=>[18,"\xCEp"],"\xD8"=>[77,"\xCEr"],"\xD9"=>[18,"\xCEr"],"\xDA"=>[77,"\xCEu"],"\xDB"=>[18,"\xCEu"],"\xDC"=>[0,"\xCE\x3A"],"\xDD"=>[0,"\xCEB"],"\xDE"=>[0,"\xCEC"],"\xDF"=>[0,"\xCED"],"\xE0"=>[0,"\xCEE"],"\xE1"=>[0,"\xCEF"],"\xE2"=>[0,"\xCEG"],"\xE3"=>[0,"\xCEH"],"\xE4"=>[0,"\xCEI"],"\xE5"=>[0,"\xCEJ"],"\xE6"=>[0,"\xCEK"],"\xE7"=>[0,"\xCEL"],"\xE8"=>[0,"\xCEM"],"\xE9"=>[0,"\xCEN"],"\xEA"=>[0,"\xCEO"],"\xEB"=>[0,"\xCEP"],"\xEC"=>[0,"\xCEQ"],"\xED"=>[0,"\xCER"],"\xEE"=>[0,"\xCES"],"\xEF"=>[0,"\xCET"],"\xF0"=>[0,"\xCEU"],"\xF1"=>[0,"\xCEV"],"\xF2"=>[0,"\xCEW"],"\xF3"=>[0,"\xCEY"],"\xF4"=>[0,"\xCEj"],"\xF5"=>[0,"\xCEk"],"\xF6"=>[0,"\xCEq"],"\xF7"=>[0,"\xCEv"],"\xF8"=>[0,"\xCEw"],"\xF9"=>[0,"\xCEx"],"\xFA"=>[0,"\xCEy"],"\xFB"=>[0,"\xCEz"],"\xFC"=>[82,"\xCE"],"\xFD"=>[87,"\xCE"],"\xFE"=>[130,"\xCE"],"\xFF"=>[9,"\xCE"],],["\x00"=>[77,"\xAB0"],"\x01"=>[18,"\xAB0"],"\x02"=>[77,"\xAB1"],"\x03"=>[18,"\xAB1"],"\x04"=>[77,"\xAB2"],"\x05"=>[18,"\xAB2"],"\x06"=>[77,"\xABa"],"\x07"=>[18,"\xABa"],"\x08"=>[77,"\xABc"],"\x09"=>[18,"\xABc"],"\x0A"=>[77,"\xABe"],"\x0B"=>[18,"\xABe"],"\x0C"=>[77,"\xABi"],"\x0D"=>[18,"\xABi"],"\x0E"=>[77,"\xABo"],"\x0F"=>[18,"\xABo"],"\x10"=>[77,"\xABs"],"\x11"=>[18,"\xABs"],"\x12"=>[77,"\xABt"],"\x13"=>[18,"\xABt"],"\x14"=>[0,"\xAB\x20"],"\x15"=>[0,"\xAB\x25"],"\x16"=>[0,"\xAB-"],"\x17"=>[0,"\xAB."],"\x18"=>[0,"\xAB\x2F"],"\x19"=>[0,"\xAB3"],"\x1A"=>[0,"\xAB4"],"\x1B"=>[0,"\xAB5"],"\x1C"=>[0,"\xAB6"],"\x1D"=>[0,"\xAB7"],"\x1E"=>[0,"\xAB8"],"\x1F"=>[0,"\xAB9"],"\x20"=>[0,"\xAB\x3D"],"\x21"=>[0,"\xABA"],"\x22"=>[0,"\xAB_"],"\x23"=>[0,"\xABb"],"\x24"=>[0,"\xABd"],"\x25"=>[0,"\xABf"],"\x26"=>[0,"\xABg"],"\x27"=>[0,"\xABh"],"\x28"=>[0,"\xABl"],"\x29"=>[0,"\xABm"],"\x2A"=>[0,"\xABn"],"\x2B"=>[0,"\xABp"],"\x2C"=>[0,"\xABr"],"-"=>[0,"\xABu"],"."=>[100,"\xAB"],"\x2F"=>[110,"\xAB"],[111,"\xAB"],[115,"\xAB"],[116,"\xAB"],[118,"\xAB"],[119,"\xAB"],[122,"\xAB"],[123,"\xAB"],[125,"\xAB"],[126,"\xAB"],[129,"\xAB"],"\x3A"=>[143,"\xAB"],"\x3B"=>[148,"\xAB"],"\x3C"=>[151,"\xAB"],"\x3D"=>[153,"\xAB"],"\x3E"=>[83,"\xAB"],"\x3F"=>[10,"\xAB"],"\x40"=>[77,"\xCE0"],"A"=>[18,"\xCE0"],"B"=>[77,"\xCE1"],"C"=>[18,"\xCE1"],"D"=>[77,"\xCE2"],"E"=>[18,"\xCE2"],"F"=>[77,"\xCEa"],"G"=>[18,"\xCEa"],"H"=>[77,"\xCEc"],"I"=>[18,"\xCEc"],"J"=>[77,"\xCEe"],"K"=>[18,"\xCEe"],"L"=>[77,"\xCEi"],"M"=>[18,"\xCEi"],"N"=>[77,"\xCEo"],"O"=>[18,"\xCEo"],"P"=>[77,"\xCEs"],"Q"=>[18,"\xCEs"],"R"=>[77,"\xCEt"],"S"=>[18,"\xCEt"],"T"=>[0,"\xCE\x20"],"U"=>[0,"\xCE\x25"],"V"=>[0,"\xCE-"],"W"=>[0,"\xCE."],"X"=>[0,"\xCE\x2F"],"Y"=>[0,"\xCE3"],"Z"=>[0,"\xCE4"],"\x5B"=>[0,"\xCE5"],"\x5C"=>[0,"\xCE6"],"\x5D"=>[0,"\xCE7"],"\x5E"=>[0,"\xCE8"],"_"=>[0,"\xCE9"],"\x60"=>[0,"\xCE\x3D"],"a"=>[0,"\xCEA"],"b"=>[0,"\xCE_"],"c"=>[0,"\xCEb"],"d"=>[0,"\xCEd"],"e"=>[0,"\xCEf"],"f"=>[0,"\xCEg"],"g"=>[0,"\xCEh"],"h"=>[0,"\xCEl"],"i"=>[0,"\xCEm"],"j"=>[0,"\xCEn"],"k"=>[0,"\xCEp"],"l"=>[0,"\xCEr"],"m"=>[0,"\xCEu"],"n"=>[100,"\xCE"],"o"=>[110,"\xCE"],"p"=>[111,"\xCE"],"q"=>[115,"\xCE"],"r"=>[116,"\xCE"],"s"=>[118,"\xCE"],"t"=>[119,"\xCE"],"u"=>[122,"\xCE"],"v"=>[123,"\xCE"],"w"=>[125,"\xCE"],"x"=>[126,"\xCE"],"y"=>[129,"\xCE"],"z"=>[143,"\xCE"],"\x7B"=>[148,"\xCE"],"\x7C"=>[151,"\xCE"],"\x7D"=>[153,"\xCE"],"~"=>[83,"\xCE"],"\x7F"=>[10,"\xCE"],"\x80"=>[77,"\xD70"],"\x81"=>[18,"\xD70"],"\x82"=>[77,"\xD71"],"\x83"=>[18,"\xD71"],"\x84"=>[77,"\xD72"],"\x85"=>[18,"\xD72"],"\x86"=>[77,"\xD7a"],"\x87"=>[18,"\xD7a"],"\x88"=>[77,"\xD7c"],"\x89"=>[18,"\xD7c"],"\x8A"=>[77,"\xD7e"],"\x8B"=>[18,"\xD7e"],"\x8C"=>[77,"\xD7i"],"\x8D"=>[18,"\xD7i"],"\x8E"=>[77,"\xD7o"],"\x8F"=>[18,"\xD7o"],"\x90"=>[77,"\xD7s"],"\x91"=>[18,"\xD7s"],"\x92"=>[77,"\xD7t"],"\x93"=>[18,"\xD7t"],"\x94"=>[0,"\xD7\x20"],"\x95"=>[0,"\xD7\x25"],"\x96"=>[0,"\xD7-"],"\x97"=>[0,"\xD7."],"\x98"=>[0,"\xD7\x2F"],"\x99"=>[0,"\xD73"],"\x9A"=>[0,"\xD74"],"\x9B"=>[0,"\xD75"],"\x9C"=>[0,"\xD76"],"\x9D"=>[0,"\xD77"],"\x9E"=>[0,"\xD78"],"\x9F"=>[0,"\xD79"],"\xA0"=>[0,"\xD7\x3D"],"\xA1"=>[0,"\xD7A"],"\xA2"=>[0,"\xD7_"],"\xA3"=>[0,"\xD7b"],"\xA4"=>[0,"\xD7d"],"\xA5"=>[0,"\xD7f"],"\xA6"=>[0,"\xD7g"],"\xA7"=>[0,"\xD7h"],"\xA8"=>[0,"\xD7l"],"\xA9"=>[0,"\xD7m"],"\xAA"=>[0,"\xD7n"],"\xAB"=>[0,"\xD7p"],"\xAC"=>[0,"\xD7r"],"\xAD"=>[0,"\xD7u"],"\xAE"=>[100,"\xD7"],"\xAF"=>[110,"\xD7"],"\xB0"=>[111,"\xD7"],"\xB1"=>[115,"\xD7"],"\xB2"=>[116,"\xD7"],"\xB3"=>[118,"\xD7"],"\xB4"=>[119,"\xD7"],"\xB5"=>[122,"\xD7"],"\xB6"=>[123,"\xD7"],"\xB7"=>[125,"\xD7"],"\xB8"=>[126,"\xD7"],"\xB9"=>[129,"\xD7"],"\xBA"=>[143,"\xD7"],"\xBB"=>[148,"\xD7"],"\xBC"=>[151,"\xD7"],"\xBD"=>[153,"\xD7"],"\xBE"=>[83,"\xD7"],"\xBF"=>[10,"\xD7"],"\xC0"=>[77,"\xE10"],"\xC1"=>[18,"\xE10"],"\xC2"=>[77,"\xE11"],"\xC3"=>[18,"\xE11"],"\xC4"=>[77,"\xE12"],"\xC5"=>[18,"\xE12"],"\xC6"=>[77,"\xE1a"],"\xC7"=>[18,"\xE1a"],"\xC8"=>[77,"\xE1c"],"\xC9"=>[18,"\xE1c"],"\xCA"=>[77,"\xE1e"],"\xCB"=>[18,"\xE1e"],"\xCC"=>[77,"\xE1i"],"\xCD"=>[18,"\xE1i"],"\xCE"=>[77,"\xE1o"],"\xCF"=>[18,"\xE1o"],"\xD0"=>[77,"\xE1s"],"\xD1"=>[18,"\xE1s"],"\xD2"=>[77,"\xE1t"],"\xD3"=>[18,"\xE1t"],"\xD4"=>[0,"\xE1\x20"],"\xD5"=>[0,"\xE1\x25"],"\xD6"=>[0,"\xE1-"],"\xD7"=>[0,"\xE1."],"\xD8"=>[0,"\xE1\x2F"],"\xD9"=>[0,"\xE13"],"\xDA"=>[0,"\xE14"],"\xDB"=>[0,"\xE15"],"\xDC"=>[0,"\xE16"],"\xDD"=>[0,"\xE17"],"\xDE"=>[0,"\xE18"],"\xDF"=>[0,"\xE19"],"\xE0"=>[0,"\xE1\x3D"],"\xE1"=>[0,"\xE1A"],"\xE2"=>[0,"\xE1_"],"\xE3"=>[0,"\xE1b"],"\xE4"=>[0,"\xE1d"],"\xE5"=>[0,"\xE1f"],"\xE6"=>[0,"\xE1g"],"\xE7"=>[0,"\xE1h"],"\xE8"=>[0,"\xE1l"],"\xE9"=>[0,"\xE1m"],"\xEA"=>[0,"\xE1n"],"\xEB"=>[0,"\xE1p"],"\xEC"=>[0,"\xE1r"],"\xED"=>[0,"\xE1u"],"\xEE"=>[100,"\xE1"],"\xEF"=>[110,"\xE1"],"\xF0"=>[111,"\xE1"],"\xF1"=>[115,"\xE1"],"\xF2"=>[116,"\xE1"],"\xF3"=>[118,"\xE1"],"\xF4"=>[119,"\xE1"],"\xF5"=>[122,"\xE1"],"\xF6"=>[123,"\xE1"],"\xF7"=>[125,"\xE1"],"\xF8"=>[126,"\xE1"],"\xF9"=>[129,"\xE1"],"\xFA"=>[143,"\xE1"],"\xFB"=>[148,"\xE1"],"\xFC"=>[151,"\xE1"],"\xFD"=>[153,"\xE1"],"\xFE"=>[83,"\xE1"],"\xFF"=>[10,"\xE1"],],["\x00"=>[0,"\xAB0"],"\x01"=>[0,"\xAB1"],"\x02"=>[0,"\xAB2"],"\x03"=>[0,"\xABa"],"\x04"=>[0,"\xABc"],"\x05"=>[0,"\xABe"],"\x06"=>[0,"\xABi"],"\x07"=>[0,"\xABo"],"\x08"=>[0,"\xABs"],"\x09"=>[0,"\xABt"],"\x0A"=>[73,"\xAB"],"\x0B"=>[88,"\xAB"],"\x0C"=>[89,"\xAB"],"\x0D"=>[96,"\xAB"],"\x0E"=>[97,"\xAB"],"\x0F"=>[99,"\xAB"],"\x10"=>[106,"\xAB"],"\x11"=>[136,"\xAB"],"\x12"=>[139,"\xAB"],"\x13"=>[141,"\xAB"],"\x14"=>[145,"\xAB"],"\x15"=>[147,"\xAB"],"\x16"=>[149,"\xAB"],"\x17"=>[101,"\xAB"],"\x18"=>[112,"\xAB"],"\x19"=>[117,"\xAB"],"\x1A"=>[120,"\xAB"],"\x1B"=>[124,"\xAB"],"\x1C"=>[127,"\xAB"],"\x1D"=>[144,"\xAB"],"\x1E"=>[152,"\xAB"],"\x1F"=>[11,"\xAB"],"\x20"=>[0,"\xCE0"],"\x21"=>[0,"\xCE1"],"\x22"=>[0,"\xCE2"],"\x23"=>[0,"\xCEa"],"\x24"=>[0,"\xCEc"],"\x25"=>[0,"\xCEe"],"\x26"=>[0,"\xCEi"],"\x27"=>[0,"\xCEo"],"\x28"=>[0,"\xCEs"],"\x29"=>[0,"\xCEt"],"\x2A"=>[73,"\xCE"],"\x2B"=>[88,"\xCE"],"\x2C"=>[89,"\xCE"],"-"=>[96,"\xCE"],"."=>[97,"\xCE"],"\x2F"=>[99,"\xCE"],[106,"\xCE"],[136,"\xCE"],[139,"\xCE"],[141,"\xCE"],[145,"\xCE"],[147,"\xCE"],[149,"\xCE"],[101,"\xCE"],[112,"\xCE"],[117,"\xCE"],"\x3A"=>[120,"\xCE"],"\x3B"=>[124,"\xCE"],"\x3C"=>[127,"\xCE"],"\x3D"=>[144,"\xCE"],"\x3E"=>[152,"\xCE"],"\x3F"=>[11,"\xCE"],"\x40"=>[0,"\xD70"],"A"=>[0,"\xD71"],"B"=>[0,"\xD72"],"C"=>[0,"\xD7a"],"D"=>[0,"\xD7c"],"E"=>[0,"\xD7e"],"F"=>[0,"\xD7i"],"G"=>[0,"\xD7o"],"H"=>[0,"\xD7s"],"I"=>[0,"\xD7t"],"J"=>[73,"\xD7"],"K"=>[88,"\xD7"],"L"=>[89,"\xD7"],"M"=>[96,"\xD7"],"N"=>[97,"\xD7"],"O"=>[99,"\xD7"],"P"=>[106,"\xD7"],"Q"=>[136,"\xD7"],"R"=>[139,"\xD7"],"S"=>[141,"\xD7"],"T"=>[145,"\xD7"],"U"=>[147,"\xD7"],"V"=>[149,"\xD7"],"W"=>[101,"\xD7"],"X"=>[112,"\xD7"],"Y"=>[117,"\xD7"],"Z"=>[120,"\xD7"],"\x5B"=>[124,"\xD7"],"\x5C"=>[127,"\xD7"],"\x5D"=>[144,"\xD7"],"\x5E"=>[152,"\xD7"],"_"=>[11,"\xD7"],"\x60"=>[0,"\xE10"],"a"=>[0,"\xE11"],"b"=>[0,"\xE12"],"c"=>[0,"\xE1a"],"d"=>[0,"\xE1c"],"e"=>[0,"\xE1e"],"f"=>[0,"\xE1i"],"g"=>[0,"\xE1o"],"h"=>[0,"\xE1s"],"i"=>[0,"\xE1t"],"j"=>[73,"\xE1"],"k"=>[88,"\xE1"],"l"=>[89,"\xE1"],"m"=>[96,"\xE1"],"n"=>[97,"\xE1"],"o"=>[99,"\xE1"],"p"=>[106,"\xE1"],"q"=>[136,"\xE1"],"r"=>[139,"\xE1"],"s"=>[141,"\xE1"],"t"=>[145,"\xE1"],"u"=>[147,"\xE1"],"v"=>[149,"\xE1"],"w"=>[101,"\xE1"],"x"=>[112,"\xE1"],"y"=>[117,"\xE1"],"z"=>[120,"\xE1"],"\x7B"=>[124,"\xE1"],"\x7C"=>[127,"\xE1"],"\x7D"=>[144,"\xE1"],"~"=>[152,"\xE1"],"\x7F"=>[11,"\xE1"],"\x80"=>[0,"\xEC0"],"\x81"=>[0,"\xEC1"],"\x82"=>[0,"\xEC2"],"\x83"=>[0,"\xECa"],"\x84"=>[0,"\xECc"],"\x85"=>[0,"\xECe"],"\x86"=>[0,"\xECi"],"\x87"=>[0,"\xECo"],"\x88"=>[0,"\xECs"],"\x89"=>[0,"\xECt"],"\x8A"=>[73,"\xEC"],"\x8B"=>[88,"\xEC"],"\x8C"=>[89,"\xEC"],"\x8D"=>[96,"\xEC"],"\x8E"=>[97,"\xEC"],"\x8F"=>[99,"\xEC"],"\x90"=>[106,"\xEC"],"\x91"=>[136,"\xEC"],"\x92"=>[139,"\xEC"],"\x93"=>[141,"\xEC"],"\x94"=>[145,"\xEC"],"\x95"=>[147,"\xEC"],"\x96"=>[149,"\xEC"],"\x97"=>[101,"\xEC"],"\x98"=>[112,"\xEC"],"\x99"=>[117,"\xEC"],"\x9A"=>[120,"\xEC"],"\x9B"=>[124,"\xEC"],"\x9C"=>[127,"\xEC"],"\x9D"=>[144,"\xEC"],"\x9E"=>[152,"\xEC"],"\x9F"=>[11,"\xEC"],"\xA0"=>[0,"\xED0"],"\xA1"=>[0,"\xED1"],"\xA2"=>[0,"\xED2"],"\xA3"=>[0,"\xEDa"],"\xA4"=>[0,"\xEDc"],"\xA5"=>[0,"\xEDe"],"\xA6"=>[0,"\xEDi"],"\xA7"=>[0,"\xEDo"],"\xA8"=>[0,"\xEDs"],"\xA9"=>[0,"\xEDt"],"\xAA"=>[73,"\xED"],"\xAB"=>[88,"\xED"],"\xAC"=>[89,"\xED"],"\xAD"=>[96,"\xED"],"\xAE"=>[97,"\xED"],"\xAF"=>[99,"\xED"],"\xB0"=>[106,"\xED"],"\xB1"=>[136,"\xED"],"\xB2"=>[139,"\xED"],"\xB3"=>[141,"\xED"],"\xB4"=>[145,"\xED"],"\xB5"=>[147,"\xED"],"\xB6"=>[149,"\xED"],"\xB7"=>[101,"\xED"],"\xB8"=>[112,"\xED"],"\xB9"=>[117,"\xED"],"\xBA"=>[120,"\xED"],"\xBB"=>[124,"\xED"],"\xBC"=>[127,"\xED"],"\xBD"=>[144,"\xED"],"\xBE"=>[152,"\xED"],"\xBF"=>[11,"\xED"],"\xC0"=>[92,"\xC7"],"\xC1"=>[95,"\xC7"],"\xC2"=>[137,"\xC7"],"\xC3"=>[142,"\xC7"],"\xC4"=>[150,"\xC7"],"\xC5"=>[74,"\xC7"],"\xC6"=>[90,"\xC7"],"\xC7"=>[98,"\xC7"],"\xC8"=>[107,"\xC7"],"\xC9"=>[140,"\xC7"],"\xCA"=>[146,"\xC7"],"\xCB"=>[102,"\xC7"],"\xCC"=>[113,"\xC7"],"\xCD"=>[121,"\xC7"],"\xCE"=>[128,"\xC7"],"\xCF"=>[12,"\xC7"],"\xD0"=>[92,"\xCF"],"\xD1"=>[95,"\xCF"],"\xD2"=>[137,"\xCF"],"\xD3"=>[142,"\xCF"],"\xD4"=>[150,"\xCF"],"\xD5"=>[74,"\xCF"],"\xD6"=>[90,"\xCF"],"\xD7"=>[98,"\xCF"],"\xD8"=>[107,"\xCF"],"\xD9"=>[140,"\xCF"],"\xDA"=>[146,"\xCF"],"\xDB"=>[102,"\xCF"],"\xDC"=>[113,"\xCF"],"\xDD"=>[121,"\xCF"],"\xDE"=>[128,"\xCF"],"\xDF"=>[12,"\xCF"],"\xE0"=>[92,"\xEA"],"\xE1"=>[95,"\xEA"],"\xE2"=>[137,"\xEA"],"\xE3"=>[142,"\xEA"],"\xE4"=>[150,"\xEA"],"\xE5"=>[74,"\xEA"],"\xE6"=>[90,"\xEA"],"\xE7"=>[98,"\xEA"],"\xE8"=>[107,"\xEA"],"\xE9"=>[140,"\xEA"],"\xEA"=>[146,"\xEA"],"\xEB"=>[102,"\xEA"],"\xEC"=>[113,"\xEA"],"\xED"=>[121,"\xEA"],"\xEE"=>[128,"\xEA"],"\xEF"=>[12,"\xEA"],"\xF0"=>[92,"\xEB"],"\xF1"=>[95,"\xEB"],"\xF2"=>[137,"\xEB"],"\xF3"=>[142,"\xEB"],"\xF4"=>[150,"\xEB"],"\xF5"=>[74,"\xEB"],"\xF6"=>[90,"\xEB"],"\xF7"=>[98,"\xEB"],"\xF8"=>[107,"\xEB"],"\xF9"=>[140,"\xEB"],"\xFA"=>[146,"\xEB"],"\xFB"=>[102,"\xEB"],"\xFC"=>[113,"\xEB"],"\xFD"=>[121,"\xEB"],"\xFE"=>[128,"\xEB"],"\xFF"=>[12,"\xEB"],],["\x00"=>[94,"\xAF0"],"\x01"=>[76,"\xAF0"],"\x02"=>[104,"\xAF0"],"\x03"=>[16,"\xAF0"],"\x04"=>[94,"\xAF1"],"\x05"=>[76,"\xAF1"],"\x06"=>[104,"\xAF1"],"\x07"=>[16,"\xAF1"],"\x08"=>[94,"\xAF2"],"\x09"=>[76,"\xAF2"],"\x0A"=>[104,"\xAF2"],"\x0B"=>[16,"\xAF2"],"\x0C"=>[94,"\xAFa"],"\x0D"=>[76,"\xAFa"],"\x0E"=>[104,"\xAFa"],"\x0F"=>[16,"\xAFa"],"\x10"=>[94,"\xAFc"],"\x11"=>[76,"\xAFc"],"\x12"=>[104,"\xAFc"],"\x13"=>[16,"\xAFc"],"\x14"=>[94,"\xAFe"],"\x15"=>[76,"\xAFe"],"\x16"=>[104,"\xAFe"],"\x17"=>[16,"\xAFe"],"\x18"=>[94,"\xAFi"],"\x19"=>[76,"\xAFi"],"\x1A"=>[104,"\xAFi"],"\x1B"=>[16,"\xAFi"],"\x1C"=>[94,"\xAFo"],"\x1D"=>[76,"\xAFo"],"\x1E"=>[104,"\xAFo"],"\x1F"=>[16,"\xAFo"],"\x20"=>[94,"\xAFs"],"\x21"=>[76,"\xAFs"],"\x22"=>[104,"\xAFs"],"\x23"=>[16,"\xAFs"],"\x24"=>[94,"\xAFt"],"\x25"=>[76,"\xAFt"],"\x26"=>[104,"\xAFt"],"\x27"=>[16,"\xAFt"],"\x28"=>[77,"\xAF\x20"],"\x29"=>[18,"\xAF\x20"],"\x2A"=>[77,"\xAF\x25"],"\x2B"=>[18,"\xAF\x25"],"\x2C"=>[77,"\xAF-"],"-"=>[18,"\xAF-"],"."=>[77,"\xAF."],"\x2F"=>[18,"\xAF."],[77,"\xAF\x2F"],[18,"\xAF\x2F"],[77,"\xAF3"],[18,"\xAF3"],[77,"\xAF4"],[18,"\xAF4"],[77,"\xAF5"],[18,"\xAF5"],[77,"\xAF6"],[18,"\xAF6"],"\x3A"=>[77,"\xAF7"],"\x3B"=>[18,"\xAF7"],"\x3C"=>[77,"\xAF8"],"\x3D"=>[18,"\xAF8"],"\x3E"=>[77,"\xAF9"],"\x3F"=>[18,"\xAF9"],"\x40"=>[77,"\xAF\x3D"],"A"=>[18,"\xAF\x3D"],"B"=>[77,"\xAFA"],"C"=>[18,"\xAFA"],"D"=>[77,"\xAF_"],"E"=>[18,"\xAF_"],"F"=>[77,"\xAFb"],"G"=>[18,"\xAFb"],"H"=>[77,"\xAFd"],"I"=>[18,"\xAFd"],"J"=>[77,"\xAFf"],"K"=>[18,"\xAFf"],"L"=>[77,"\xAFg"],"M"=>[18,"\xAFg"],"N"=>[77,"\xAFh"],"O"=>[18,"\xAFh"],"P"=>[77,"\xAFl"],"Q"=>[18,"\xAFl"],"R"=>[77,"\xAFm"],"S"=>[18,"\xAFm"],"T"=>[77,"\xAFn"],"U"=>[18,"\xAFn"],"V"=>[77,"\xAFp"],"W"=>[18,"\xAFp"],"X"=>[77,"\xAFr"],"Y"=>[18,"\xAFr"],"Z"=>[77,"\xAFu"],"\x5B"=>[18,"\xAFu"],"\x5C"=>[0,"\xAF\x3A"],"\x5D"=>[0,"\xAFB"],"\x5E"=>[0,"\xAFC"],"_"=>[0,"\xAFD"],"\x60"=>[0,"\xAFE"],"a"=>[0,"\xAFF"],"b"=>[0,"\xAFG"],"c"=>[0,"\xAFH"],"d"=>[0,"\xAFI"],"e"=>[0,"\xAFJ"],"f"=>[0,"\xAFK"],"g"=>[0,"\xAFL"],"h"=>[0,"\xAFM"],"i"=>[0,"\xAFN"],"j"=>[0,"\xAFO"],"k"=>[0,"\xAFP"],"l"=>[0,"\xAFQ"],"m"=>[0,"\xAFR"],"n"=>[0,"\xAFS"],"o"=>[0,"\xAFT"],"p"=>[0,"\xAFU"],"q"=>[0,"\xAFV"],"r"=>[0,"\xAFW"],"s"=>[0,"\xAFY"],"t"=>[0,"\xAFj"],"u"=>[0,"\xAFk"],"v"=>[0,"\xAFq"],"w"=>[0,"\xAFv"],"x"=>[0,"\xAFw"],"y"=>[0,"\xAFx"],"z"=>[0,"\xAFy"],"\x7B"=>[0,"\xAFz"],"\x7C"=>[82,"\xAF"],"\x7D"=>[87,"\xAF"],"~"=>[130,"\xAF"],"\x7F"=>[9,"\xAF"],"\x80"=>[94,"\xB40"],"\x81"=>[76,"\xB40"],"\x82"=>[104,"\xB40"],"\x83"=>[16,"\xB40"],"\x84"=>[94,"\xB41"],"\x85"=>[76,"\xB41"],"\x86"=>[104,"\xB41"],"\x87"=>[16,"\xB41"],"\x88"=>[94,"\xB42"],"\x89"=>[76,"\xB42"],"\x8A"=>[104,"\xB42"],"\x8B"=>[16,"\xB42"],"\x8C"=>[94,"\xB4a"],"\x8D"=>[76,"\xB4a"],"\x8E"=>[104,"\xB4a"],"\x8F"=>[16,"\xB4a"],"\x90"=>[94,"\xB4c"],"\x91"=>[76,"\xB4c"],"\x92"=>[104,"\xB4c"],"\x93"=>[16,"\xB4c"],"\x94"=>[94,"\xB4e"],"\x95"=>[76,"\xB4e"],"\x96"=>[104,"\xB4e"],"\x97"=>[16,"\xB4e"],"\x98"=>[94,"\xB4i"],"\x99"=>[76,"\xB4i"],"\x9A"=>[104,"\xB4i"],"\x9B"=>[16,"\xB4i"],"\x9C"=>[94,"\xB4o"],"\x9D"=>[76,"\xB4o"],"\x9E"=>[104,"\xB4o"],"\x9F"=>[16,"\xB4o"],"\xA0"=>[94,"\xB4s"],"\xA1"=>[76,"\xB4s"],"\xA2"=>[104,"\xB4s"],"\xA3"=>[16,"\xB4s"],"\xA4"=>[94,"\xB4t"],"\xA5"=>[76,"\xB4t"],"\xA6"=>[104,"\xB4t"],"\xA7"=>[16,"\xB4t"],"\xA8"=>[77,"\xB4\x20"],"\xA9"=>[18,"\xB4\x20"],"\xAA"=>[77,"\xB4\x25"],"\xAB"=>[18,"\xB4\x25"],"\xAC"=>[77,"\xB4-"],"\xAD"=>[18,"\xB4-"],"\xAE"=>[77,"\xB4."],"\xAF"=>[18,"\xB4."],"\xB0"=>[77,"\xB4\x2F"],"\xB1"=>[18,"\xB4\x2F"],"\xB2"=>[77,"\xB43"],"\xB3"=>[18,"\xB43"],"\xB4"=>[77,"\xB44"],"\xB5"=>[18,"\xB44"],"\xB6"=>[77,"\xB45"],"\xB7"=>[18,"\xB45"],"\xB8"=>[77,"\xB46"],"\xB9"=>[18,"\xB46"],"\xBA"=>[77,"\xB47"],"\xBB"=>[18,"\xB47"],"\xBC"=>[77,"\xB48"],"\xBD"=>[18,"\xB48"],"\xBE"=>[77,"\xB49"],"\xBF"=>[18,"\xB49"],"\xC0"=>[77,"\xB4\x3D"],"\xC1"=>[18,"\xB4\x3D"],"\xC2"=>[77,"\xB4A"],"\xC3"=>[18,"\xB4A"],"\xC4"=>[77,"\xB4_"],"\xC5"=>[18,"\xB4_"],"\xC6"=>[77,"\xB4b"],"\xC7"=>[18,"\xB4b"],"\xC8"=>[77,"\xB4d"],"\xC9"=>[18,"\xB4d"],"\xCA"=>[77,"\xB4f"],"\xCB"=>[18,"\xB4f"],"\xCC"=>[77,"\xB4g"],"\xCD"=>[18,"\xB4g"],"\xCE"=>[77,"\xB4h"],"\xCF"=>[18,"\xB4h"],"\xD0"=>[77,"\xB4l"],"\xD1"=>[18,"\xB4l"],"\xD2"=>[77,"\xB4m"],"\xD3"=>[18,"\xB4m"],"\xD4"=>[77,"\xB4n"],"\xD5"=>[18,"\xB4n"],"\xD6"=>[77,"\xB4p"],"\xD7"=>[18,"\xB4p"],"\xD8"=>[77,"\xB4r"],"\xD9"=>[18,"\xB4r"],"\xDA"=>[77,"\xB4u"],"\xDB"=>[18,"\xB4u"],"\xDC"=>[0,"\xB4\x3A"],"\xDD"=>[0,"\xB4B"],"\xDE"=>[0,"\xB4C"],"\xDF"=>[0,"\xB4D"],"\xE0"=>[0,"\xB4E"],"\xE1"=>[0,"\xB4F"],"\xE2"=>[0,"\xB4G"],"\xE3"=>[0,"\xB4H"],"\xE4"=>[0,"\xB4I"],"\xE5"=>[0,"\xB4J"],"\xE6"=>[0,"\xB4K"],"\xE7"=>[0,"\xB4L"],"\xE8"=>[0,"\xB4M"],"\xE9"=>[0,"\xB4N"],"\xEA"=>[0,"\xB4O"],"\xEB"=>[0,"\xB4P"],"\xEC"=>[0,"\xB4Q"],"\xED"=>[0,"\xB4R"],"\xEE"=>[0,"\xB4S"],"\xEF"=>[0,"\xB4T"],"\xF0"=>[0,"\xB4U"],"\xF1"=>[0,"\xB4V"],"\xF2"=>[0,"\xB4W"],"\xF3"=>[0,"\xB4Y"],"\xF4"=>[0,"\xB4j"],"\xF5"=>[0,"\xB4k"],"\xF6"=>[0,"\xB4q"],"\xF7"=>[0,"\xB4v"],"\xF8"=>[0,"\xB4w"],"\xF9"=>[0,"\xB4x"],"\xFA"=>[0,"\xB4y"],"\xFB"=>[0,"\xB4z"],"\xFC"=>[82,"\xB4"],"\xFD"=>[87,"\xB4"],"\xFE"=>[130,"\xB4"],"\xFF"=>[9,"\xB4"],],["\x00"=>[77,"\xAF0"],"\x01"=>[18,"\xAF0"],"\x02"=>[77,"\xAF1"],"\x03"=>[18,"\xAF1"],"\x04"=>[77,"\xAF2"],"\x05"=>[18,"\xAF2"],"\x06"=>[77,"\xAFa"],"\x07"=>[18,"\xAFa"],"\x08"=>[77,"\xAFc"],"\x09"=>[18,"\xAFc"],"\x0A"=>[77,"\xAFe"],"\x0B"=>[18,"\xAFe"],"\x0C"=>[77,"\xAFi"],"\x0D"=>[18,"\xAFi"],"\x0E"=>[77,"\xAFo"],"\x0F"=>[18,"\xAFo"],"\x10"=>[77,"\xAFs"],"\x11"=>[18,"\xAFs"],"\x12"=>[77,"\xAFt"],"\x13"=>[18,"\xAFt"],"\x14"=>[0,"\xAF\x20"],"\x15"=>[0,"\xAF\x25"],"\x16"=>[0,"\xAF-"],"\x17"=>[0,"\xAF."],"\x18"=>[0,"\xAF\x2F"],"\x19"=>[0,"\xAF3"],"\x1A"=>[0,"\xAF4"],"\x1B"=>[0,"\xAF5"],"\x1C"=>[0,"\xAF6"],"\x1D"=>[0,"\xAF7"],"\x1E"=>[0,"\xAF8"],"\x1F"=>[0,"\xAF9"],"\x20"=>[0,"\xAF\x3D"],"\x21"=>[0,"\xAFA"],"\x22"=>[0,"\xAF_"],"\x23"=>[0,"\xAFb"],"\x24"=>[0,"\xAFd"],"\x25"=>[0,"\xAFf"],"\x26"=>[0,"\xAFg"],"\x27"=>[0,"\xAFh"],"\x28"=>[0,"\xAFl"],"\x29"=>[0,"\xAFm"],"\x2A"=>[0,"\xAFn"],"\x2B"=>[0,"\xAFp"],"\x2C"=>[0,"\xAFr"],"-"=>[0,"\xAFu"],"."=>[100,"\xAF"],"\x2F"=>[110,"\xAF"],[111,"\xAF"],[115,"\xAF"],[116,"\xAF"],[118,"\xAF"],[119,"\xAF"],[122,"\xAF"],[123,"\xAF"],[125,"\xAF"],[126,"\xAF"],[129,"\xAF"],"\x3A"=>[143,"\xAF"],"\x3B"=>[148,"\xAF"],"\x3C"=>[151,"\xAF"],"\x3D"=>[153,"\xAF"],"\x3E"=>[83,"\xAF"],"\x3F"=>[10,"\xAF"],"\x40"=>[77,"\xB40"],"A"=>[18,"\xB40"],"B"=>[77,"\xB41"],"C"=>[18,"\xB41"],"D"=>[77,"\xB42"],"E"=>[18,"\xB42"],"F"=>[77,"\xB4a"],"G"=>[18,"\xB4a"],"H"=>[77,"\xB4c"],"I"=>[18,"\xB4c"],"J"=>[77,"\xB4e"],"K"=>[18,"\xB4e"],"L"=>[77,"\xB4i"],"M"=>[18,"\xB4i"],"N"=>[77,"\xB4o"],"O"=>[18,"\xB4o"],"P"=>[77,"\xB4s"],"Q"=>[18,"\xB4s"],"R"=>[77,"\xB4t"],"S"=>[18,"\xB4t"],"T"=>[0,"\xB4\x20"],"U"=>[0,"\xB4\x25"],"V"=>[0,"\xB4-"],"W"=>[0,"\xB4."],"X"=>[0,"\xB4\x2F"],"Y"=>[0,"\xB43"],"Z"=>[0,"\xB44"],"\x5B"=>[0,"\xB45"],"\x5C"=>[0,"\xB46"],"\x5D"=>[0,"\xB47"],"\x5E"=>[0,"\xB48"],"_"=>[0,"\xB49"],"\x60"=>[0,"\xB4\x3D"],"a"=>[0,"\xB4A"],"b"=>[0,"\xB4_"],"c"=>[0,"\xB4b"],"d"=>[0,"\xB4d"],"e"=>[0,"\xB4f"],"f"=>[0,"\xB4g"],"g"=>[0,"\xB4h"],"h"=>[0,"\xB4l"],"i"=>[0,"\xB4m"],"j"=>[0,"\xB4n"],"k"=>[0,"\xB4p"],"l"=>[0,"\xB4r"],"m"=>[0,"\xB4u"],"n"=>[100,"\xB4"],"o"=>[110,"\xB4"],"p"=>[111,"\xB4"],"q"=>[115,"\xB4"],"r"=>[116,"\xB4"],"s"=>[118,"\xB4"],"t"=>[119,"\xB4"],"u"=>[122,"\xB4"],"v"=>[123,"\xB4"],"w"=>[125,"\xB4"],"x"=>[126,"\xB4"],"y"=>[129,"\xB4"],"z"=>[143,"\xB4"],"\x7B"=>[148,"\xB4"],"\x7C"=>[151,"\xB4"],"\x7D"=>[153,"\xB4"],"~"=>[83,"\xB4"],"\x7F"=>[10,"\xB4"],"\x80"=>[77,"\xB60"],"\x81"=>[18,"\xB60"],"\x82"=>[77,"\xB61"],"\x83"=>[18,"\xB61"],"\x84"=>[77,"\xB62"],"\x85"=>[18,"\xB62"],"\x86"=>[77,"\xB6a"],"\x87"=>[18,"\xB6a"],"\x88"=>[77,"\xB6c"],"\x89"=>[18,"\xB6c"],"\x8A"=>[77,"\xB6e"],"\x8B"=>[18,"\xB6e"],"\x8C"=>[77,"\xB6i"],"\x8D"=>[18,"\xB6i"],"\x8E"=>[77,"\xB6o"],"\x8F"=>[18,"\xB6o"],"\x90"=>[77,"\xB6s"],"\x91"=>[18,"\xB6s"],"\x92"=>[77,"\xB6t"],"\x93"=>[18,"\xB6t"],"\x94"=>[0,"\xB6\x20"],"\x95"=>[0,"\xB6\x25"],"\x96"=>[0,"\xB6-"],"\x97"=>[0,"\xB6."],"\x98"=>[0,"\xB6\x2F"],"\x99"=>[0,"\xB63"],"\x9A"=>[0,"\xB64"],"\x9B"=>[0,"\xB65"],"\x9C"=>[0,"\xB66"],"\x9D"=>[0,"\xB67"],"\x9E"=>[0,"\xB68"],"\x9F"=>[0,"\xB69"],"\xA0"=>[0,"\xB6\x3D"],"\xA1"=>[0,"\xB6A"],"\xA2"=>[0,"\xB6_"],"\xA3"=>[0,"\xB6b"],"\xA4"=>[0,"\xB6d"],"\xA5"=>[0,"\xB6f"],"\xA6"=>[0,"\xB6g"],"\xA7"=>[0,"\xB6h"],"\xA8"=>[0,"\xB6l"],"\xA9"=>[0,"\xB6m"],"\xAA"=>[0,"\xB6n"],"\xAB"=>[0,"\xB6p"],"\xAC"=>[0,"\xB6r"],"\xAD"=>[0,"\xB6u"],"\xAE"=>[100,"\xB6"],"\xAF"=>[110,"\xB6"],"\xB0"=>[111,"\xB6"],"\xB1"=>[115,"\xB6"],"\xB2"=>[116,"\xB6"],"\xB3"=>[118,"\xB6"],"\xB4"=>[119,"\xB6"],"\xB5"=>[122,"\xB6"],"\xB6"=>[123,"\xB6"],"\xB7"=>[125,"\xB6"],"\xB8"=>[126,"\xB6"],"\xB9"=>[129,"\xB6"],"\xBA"=>[143,"\xB6"],"\xBB"=>[148,"\xB6"],"\xBC"=>[151,"\xB6"],"\xBD"=>[153,"\xB6"],"\xBE"=>[83,"\xB6"],"\xBF"=>[10,"\xB6"],"\xC0"=>[77,"\xB70"],"\xC1"=>[18,"\xB70"],"\xC2"=>[77,"\xB71"],"\xC3"=>[18,"\xB71"],"\xC4"=>[77,"\xB72"],"\xC5"=>[18,"\xB72"],"\xC6"=>[77,"\xB7a"],"\xC7"=>[18,"\xB7a"],"\xC8"=>[77,"\xB7c"],"\xC9"=>[18,"\xB7c"],"\xCA"=>[77,"\xB7e"],"\xCB"=>[18,"\xB7e"],"\xCC"=>[77,"\xB7i"],"\xCD"=>[18,"\xB7i"],"\xCE"=>[77,"\xB7o"],"\xCF"=>[18,"\xB7o"],"\xD0"=>[77,"\xB7s"],"\xD1"=>[18,"\xB7s"],"\xD2"=>[77,"\xB7t"],"\xD3"=>[18,"\xB7t"],"\xD4"=>[0,"\xB7\x20"],"\xD5"=>[0,"\xB7\x25"],"\xD6"=>[0,"\xB7-"],"\xD7"=>[0,"\xB7."],"\xD8"=>[0,"\xB7\x2F"],"\xD9"=>[0,"\xB73"],"\xDA"=>[0,"\xB74"],"\xDB"=>[0,"\xB75"],"\xDC"=>[0,"\xB76"],"\xDD"=>[0,"\xB77"],"\xDE"=>[0,"\xB78"],"\xDF"=>[0,"\xB79"],"\xE0"=>[0,"\xB7\x3D"],"\xE1"=>[0,"\xB7A"],"\xE2"=>[0,"\xB7_"],"\xE3"=>[0,"\xB7b"],"\xE4"=>[0,"\xB7d"],"\xE5"=>[0,"\xB7f"],"\xE6"=>[0,"\xB7g"],"\xE7"=>[0,"\xB7h"],"\xE8"=>[0,"\xB7l"],"\xE9"=>[0,"\xB7m"],"\xEA"=>[0,"\xB7n"],"\xEB"=>[0,"\xB7p"],"\xEC"=>[0,"\xB7r"],"\xED"=>[0,"\xB7u"],"\xEE"=>[100,"\xB7"],"\xEF"=>[110,"\xB7"],"\xF0"=>[111,"\xB7"],"\xF1"=>[115,"\xB7"],"\xF2"=>[116,"\xB7"],"\xF3"=>[118,"\xB7"],"\xF4"=>[119,"\xB7"],"\xF5"=>[122,"\xB7"],"\xF6"=>[123,"\xB7"],"\xF7"=>[125,"\xB7"],"\xF8"=>[126,"\xB7"],"\xF9"=>[129,"\xB7"],"\xFA"=>[143,"\xB7"],"\xFB"=>[148,"\xB7"],"\xFC"=>[151,"\xB7"],"\xFD"=>[153,"\xB7"],"\xFE"=>[83,"\xB7"],"\xFF"=>[10,"\xB7"],],["\x00"=>[94,"\xB00"],"\x01"=>[76,"\xB00"],"\x02"=>[104,"\xB00"],"\x03"=>[16,"\xB00"],"\x04"=>[94,"\xB01"],"\x05"=>[76,"\xB01"],"\x06"=>[104,"\xB01"],"\x07"=>[16,"\xB01"],"\x08"=>[94,"\xB02"],"\x09"=>[76,"\xB02"],"\x0A"=>[104,"\xB02"],"\x0B"=>[16,"\xB02"],"\x0C"=>[94,"\xB0a"],"\x0D"=>[76,"\xB0a"],"\x0E"=>[104,"\xB0a"],"\x0F"=>[16,"\xB0a"],"\x10"=>[94,"\xB0c"],"\x11"=>[76,"\xB0c"],"\x12"=>[104,"\xB0c"],"\x13"=>[16,"\xB0c"],"\x14"=>[94,"\xB0e"],"\x15"=>[76,"\xB0e"],"\x16"=>[104,"\xB0e"],"\x17"=>[16,"\xB0e"],"\x18"=>[94,"\xB0i"],"\x19"=>[76,"\xB0i"],"\x1A"=>[104,"\xB0i"],"\x1B"=>[16,"\xB0i"],"\x1C"=>[94,"\xB0o"],"\x1D"=>[76,"\xB0o"],"\x1E"=>[104,"\xB0o"],"\x1F"=>[16,"\xB0o"],"\x20"=>[94,"\xB0s"],"\x21"=>[76,"\xB0s"],"\x22"=>[104,"\xB0s"],"\x23"=>[16,"\xB0s"],"\x24"=>[94,"\xB0t"],"\x25"=>[76,"\xB0t"],"\x26"=>[104,"\xB0t"],"\x27"=>[16,"\xB0t"],"\x28"=>[77,"\xB0\x20"],"\x29"=>[18,"\xB0\x20"],"\x2A"=>[77,"\xB0\x25"],"\x2B"=>[18,"\xB0\x25"],"\x2C"=>[77,"\xB0-"],"-"=>[18,"\xB0-"],"."=>[77,"\xB0."],"\x2F"=>[18,"\xB0."],[77,"\xB0\x2F"],[18,"\xB0\x2F"],[77,"\xB03"],[18,"\xB03"],[77,"\xB04"],[18,"\xB04"],[77,"\xB05"],[18,"\xB05"],[77,"\xB06"],[18,"\xB06"],"\x3A"=>[77,"\xB07"],"\x3B"=>[18,"\xB07"],"\x3C"=>[77,"\xB08"],"\x3D"=>[18,"\xB08"],"\x3E"=>[77,"\xB09"],"\x3F"=>[18,"\xB09"],"\x40"=>[77,"\xB0\x3D"],"A"=>[18,"\xB0\x3D"],"B"=>[77,"\xB0A"],"C"=>[18,"\xB0A"],"D"=>[77,"\xB0_"],"E"=>[18,"\xB0_"],"F"=>[77,"\xB0b"],"G"=>[18,"\xB0b"],"H"=>[77,"\xB0d"],"I"=>[18,"\xB0d"],"J"=>[77,"\xB0f"],"K"=>[18,"\xB0f"],"L"=>[77,"\xB0g"],"M"=>[18,"\xB0g"],"N"=>[77,"\xB0h"],"O"=>[18,"\xB0h"],"P"=>[77,"\xB0l"],"Q"=>[18,"\xB0l"],"R"=>[77,"\xB0m"],"S"=>[18,"\xB0m"],"T"=>[77,"\xB0n"],"U"=>[18,"\xB0n"],"V"=>[77,"\xB0p"],"W"=>[18,"\xB0p"],"X"=>[77,"\xB0r"],"Y"=>[18,"\xB0r"],"Z"=>[77,"\xB0u"],"\x5B"=>[18,"\xB0u"],"\x5C"=>[0,"\xB0\x3A"],"\x5D"=>[0,"\xB0B"],"\x5E"=>[0,"\xB0C"],"_"=>[0,"\xB0D"],"\x60"=>[0,"\xB0E"],"a"=>[0,"\xB0F"],"b"=>[0,"\xB0G"],"c"=>[0,"\xB0H"],"d"=>[0,"\xB0I"],"e"=>[0,"\xB0J"],"f"=>[0,"\xB0K"],"g"=>[0,"\xB0L"],"h"=>[0,"\xB0M"],"i"=>[0,"\xB0N"],"j"=>[0,"\xB0O"],"k"=>[0,"\xB0P"],"l"=>[0,"\xB0Q"],"m"=>[0,"\xB0R"],"n"=>[0,"\xB0S"],"o"=>[0,"\xB0T"],"p"=>[0,"\xB0U"],"q"=>[0,"\xB0V"],"r"=>[0,"\xB0W"],"s"=>[0,"\xB0Y"],"t"=>[0,"\xB0j"],"u"=>[0,"\xB0k"],"v"=>[0,"\xB0q"],"w"=>[0,"\xB0v"],"x"=>[0,"\xB0w"],"y"=>[0,"\xB0x"],"z"=>[0,"\xB0y"],"\x7B"=>[0,"\xB0z"],"\x7C"=>[82,"\xB0"],"\x7D"=>[87,"\xB0"],"~"=>[130,"\xB0"],"\x7F"=>[9,"\xB0"],"\x80"=>[94,"\xB10"],"\x81"=>[76,"\xB10"],"\x82"=>[104,"\xB10"],"\x83"=>[16,"\xB10"],"\x84"=>[94,"\xB11"],"\x85"=>[76,"\xB11"],"\x86"=>[104,"\xB11"],"\x87"=>[16,"\xB11"],"\x88"=>[94,"\xB12"],"\x89"=>[76,"\xB12"],"\x8A"=>[104,"\xB12"],"\x8B"=>[16,"\xB12"],"\x8C"=>[94,"\xB1a"],"\x8D"=>[76,"\xB1a"],"\x8E"=>[104,"\xB1a"],"\x8F"=>[16,"\xB1a"],"\x90"=>[94,"\xB1c"],"\x91"=>[76,"\xB1c"],"\x92"=>[104,"\xB1c"],"\x93"=>[16,"\xB1c"],"\x94"=>[94,"\xB1e"],"\x95"=>[76,"\xB1e"],"\x96"=>[104,"\xB1e"],"\x97"=>[16,"\xB1e"],"\x98"=>[94,"\xB1i"],"\x99"=>[76,"\xB1i"],"\x9A"=>[104,"\xB1i"],"\x9B"=>[16,"\xB1i"],"\x9C"=>[94,"\xB1o"],"\x9D"=>[76,"\xB1o"],"\x9E"=>[104,"\xB1o"],"\x9F"=>[16,"\xB1o"],"\xA0"=>[94,"\xB1s"],"\xA1"=>[76,"\xB1s"],"\xA2"=>[104,"\xB1s"],"\xA3"=>[16,"\xB1s"],"\xA4"=>[94,"\xB1t"],"\xA5"=>[76,"\xB1t"],"\xA6"=>[104,"\xB1t"],"\xA7"=>[16,"\xB1t"],"\xA8"=>[77,"\xB1\x20"],"\xA9"=>[18,"\xB1\x20"],"\xAA"=>[77,"\xB1\x25"],"\xAB"=>[18,"\xB1\x25"],"\xAC"=>[77,"\xB1-"],"\xAD"=>[18,"\xB1-"],"\xAE"=>[77,"\xB1."],"\xAF"=>[18,"\xB1."],"\xB0"=>[77,"\xB1\x2F"],"\xB1"=>[18,"\xB1\x2F"],"\xB2"=>[77,"\xB13"],"\xB3"=>[18,"\xB13"],"\xB4"=>[77,"\xB14"],"\xB5"=>[18,"\xB14"],"\xB6"=>[77,"\xB15"],"\xB7"=>[18,"\xB15"],"\xB8"=>[77,"\xB16"],"\xB9"=>[18,"\xB16"],"\xBA"=>[77,"\xB17"],"\xBB"=>[18,"\xB17"],"\xBC"=>[77,"\xB18"],"\xBD"=>[18,"\xB18"],"\xBE"=>[77,"\xB19"],"\xBF"=>[18,"\xB19"],"\xC0"=>[77,"\xB1\x3D"],"\xC1"=>[18,"\xB1\x3D"],"\xC2"=>[77,"\xB1A"],"\xC3"=>[18,"\xB1A"],"\xC4"=>[77,"\xB1_"],"\xC5"=>[18,"\xB1_"],"\xC6"=>[77,"\xB1b"],"\xC7"=>[18,"\xB1b"],"\xC8"=>[77,"\xB1d"],"\xC9"=>[18,"\xB1d"],"\xCA"=>[77,"\xB1f"],"\xCB"=>[18,"\xB1f"],"\xCC"=>[77,"\xB1g"],"\xCD"=>[18,"\xB1g"],"\xCE"=>[77,"\xB1h"],"\xCF"=>[18,"\xB1h"],"\xD0"=>[77,"\xB1l"],"\xD1"=>[18,"\xB1l"],"\xD2"=>[77,"\xB1m"],"\xD3"=>[18,"\xB1m"],"\xD4"=>[77,"\xB1n"],"\xD5"=>[18,"\xB1n"],"\xD6"=>[77,"\xB1p"],"\xD7"=>[18,"\xB1p"],"\xD8"=>[77,"\xB1r"],"\xD9"=>[18,"\xB1r"],"\xDA"=>[77,"\xB1u"],"\xDB"=>[18,"\xB1u"],"\xDC"=>[0,"\xB1\x3A"],"\xDD"=>[0,"\xB1B"],"\xDE"=>[0,"\xB1C"],"\xDF"=>[0,"\xB1D"],"\xE0"=>[0,"\xB1E"],"\xE1"=>[0,"\xB1F"],"\xE2"=>[0,"\xB1G"],"\xE3"=>[0,"\xB1H"],"\xE4"=>[0,"\xB1I"],"\xE5"=>[0,"\xB1J"],"\xE6"=>[0,"\xB1K"],"\xE7"=>[0,"\xB1L"],"\xE8"=>[0,"\xB1M"],"\xE9"=>[0,"\xB1N"],"\xEA"=>[0,"\xB1O"],"\xEB"=>[0,"\xB1P"],"\xEC"=>[0,"\xB1Q"],"\xED"=>[0,"\xB1R"],"\xEE"=>[0,"\xB1S"],"\xEF"=>[0,"\xB1T"],"\xF0"=>[0,"\xB1U"],"\xF1"=>[0,"\xB1V"],"\xF2"=>[0,"\xB1W"],"\xF3"=>[0,"\xB1Y"],"\xF4"=>[0,"\xB1j"],"\xF5"=>[0,"\xB1k"],"\xF6"=>[0,"\xB1q"],"\xF7"=>[0,"\xB1v"],"\xF8"=>[0,"\xB1w"],"\xF9"=>[0,"\xB1x"],"\xFA"=>[0,"\xB1y"],"\xFB"=>[0,"\xB1z"],"\xFC"=>[82,"\xB1"],"\xFD"=>[87,"\xB1"],"\xFE"=>[130,"\xB1"],"\xFF"=>[9,"\xB1"],],["\x00"=>[77,"\xB00"],"\x01"=>[18,"\xB00"],"\x02"=>[77,"\xB01"],"\x03"=>[18,"\xB01"],"\x04"=>[77,"\xB02"],"\x05"=>[18,"\xB02"],"\x06"=>[77,"\xB0a"],"\x07"=>[18,"\xB0a"],"\x08"=>[77,"\xB0c"],"\x09"=>[18,"\xB0c"],"\x0A"=>[77,"\xB0e"],"\x0B"=>[18,"\xB0e"],"\x0C"=>[77,"\xB0i"],"\x0D"=>[18,"\xB0i"],"\x0E"=>[77,"\xB0o"],"\x0F"=>[18,"\xB0o"],"\x10"=>[77,"\xB0s"],"\x11"=>[18,"\xB0s"],"\x12"=>[77,"\xB0t"],"\x13"=>[18,"\xB0t"],"\x14"=>[0,"\xB0\x20"],"\x15"=>[0,"\xB0\x25"],"\x16"=>[0,"\xB0-"],"\x17"=>[0,"\xB0."],"\x18"=>[0,"\xB0\x2F"],"\x19"=>[0,"\xB03"],"\x1A"=>[0,"\xB04"],"\x1B"=>[0,"\xB05"],"\x1C"=>[0,"\xB06"],"\x1D"=>[0,"\xB07"],"\x1E"=>[0,"\xB08"],"\x1F"=>[0,"\xB09"],"\x20"=>[0,"\xB0\x3D"],"\x21"=>[0,"\xB0A"],"\x22"=>[0,"\xB0_"],"\x23"=>[0,"\xB0b"],"\x24"=>[0,"\xB0d"],"\x25"=>[0,"\xB0f"],"\x26"=>[0,"\xB0g"],"\x27"=>[0,"\xB0h"],"\x28"=>[0,"\xB0l"],"\x29"=>[0,"\xB0m"],"\x2A"=>[0,"\xB0n"],"\x2B"=>[0,"\xB0p"],"\x2C"=>[0,"\xB0r"],"-"=>[0,"\xB0u"],"."=>[100,"\xB0"],"\x2F"=>[110,"\xB0"],[111,"\xB0"],[115,"\xB0"],[116,"\xB0"],[118,"\xB0"],[119,"\xB0"],[122,"\xB0"],[123,"\xB0"],[125,"\xB0"],[126,"\xB0"],[129,"\xB0"],"\x3A"=>[143,"\xB0"],"\x3B"=>[148,"\xB0"],"\x3C"=>[151,"\xB0"],"\x3D"=>[153,"\xB0"],"\x3E"=>[83,"\xB0"],"\x3F"=>[10,"\xB0"],"\x40"=>[77,"\xB10"],"A"=>[18,"\xB10"],"B"=>[77,"\xB11"],"C"=>[18,"\xB11"],"D"=>[77,"\xB12"],"E"=>[18,"\xB12"],"F"=>[77,"\xB1a"],"G"=>[18,"\xB1a"],"H"=>[77,"\xB1c"],"I"=>[18,"\xB1c"],"J"=>[77,"\xB1e"],"K"=>[18,"\xB1e"],"L"=>[77,"\xB1i"],"M"=>[18,"\xB1i"],"N"=>[77,"\xB1o"],"O"=>[18,"\xB1o"],"P"=>[77,"\xB1s"],"Q"=>[18,"\xB1s"],"R"=>[77,"\xB1t"],"S"=>[18,"\xB1t"],"T"=>[0,"\xB1\x20"],"U"=>[0,"\xB1\x25"],"V"=>[0,"\xB1-"],"W"=>[0,"\xB1."],"X"=>[0,"\xB1\x2F"],"Y"=>[0,"\xB13"],"Z"=>[0,"\xB14"],"\x5B"=>[0,"\xB15"],"\x5C"=>[0,"\xB16"],"\x5D"=>[0,"\xB17"],"\x5E"=>[0,"\xB18"],"_"=>[0,"\xB19"],"\x60"=>[0,"\xB1\x3D"],"a"=>[0,"\xB1A"],"b"=>[0,"\xB1_"],"c"=>[0,"\xB1b"],"d"=>[0,"\xB1d"],"e"=>[0,"\xB1f"],"f"=>[0,"\xB1g"],"g"=>[0,"\xB1h"],"h"=>[0,"\xB1l"],"i"=>[0,"\xB1m"],"j"=>[0,"\xB1n"],"k"=>[0,"\xB1p"],"l"=>[0,"\xB1r"],"m"=>[0,"\xB1u"],"n"=>[100,"\xB1"],"o"=>[110,"\xB1"],"p"=>[111,"\xB1"],"q"=>[115,"\xB1"],"r"=>[116,"\xB1"],"s"=>[118,"\xB1"],"t"=>[119,"\xB1"],"u"=>[122,"\xB1"],"v"=>[123,"\xB1"],"w"=>[125,"\xB1"],"x"=>[126,"\xB1"],"y"=>[129,"\xB1"],"z"=>[143,"\xB1"],"\x7B"=>[148,"\xB1"],"\x7C"=>[151,"\xB1"],"\x7D"=>[153,"\xB1"],"~"=>[83,"\xB1"],"\x7F"=>[10,"\xB1"],"\x80"=>[77,"\xB30"],"\x81"=>[18,"\xB30"],"\x82"=>[77,"\xB31"],"\x83"=>[18,"\xB31"],"\x84"=>[77,"\xB32"],"\x85"=>[18,"\xB32"],"\x86"=>[77,"\xB3a"],"\x87"=>[18,"\xB3a"],"\x88"=>[77,"\xB3c"],"\x89"=>[18,"\xB3c"],"\x8A"=>[77,"\xB3e"],"\x8B"=>[18,"\xB3e"],"\x8C"=>[77,"\xB3i"],"\x8D"=>[18,"\xB3i"],"\x8E"=>[77,"\xB3o"],"\x8F"=>[18,"\xB3o"],"\x90"=>[77,"\xB3s"],"\x91"=>[18,"\xB3s"],"\x92"=>[77,"\xB3t"],"\x93"=>[18,"\xB3t"],"\x94"=>[0,"\xB3\x20"],"\x95"=>[0,"\xB3\x25"],"\x96"=>[0,"\xB3-"],"\x97"=>[0,"\xB3."],"\x98"=>[0,"\xB3\x2F"],"\x99"=>[0,"\xB33"],"\x9A"=>[0,"\xB34"],"\x9B"=>[0,"\xB35"],"\x9C"=>[0,"\xB36"],"\x9D"=>[0,"\xB37"],"\x9E"=>[0,"\xB38"],"\x9F"=>[0,"\xB39"],"\xA0"=>[0,"\xB3\x3D"],"\xA1"=>[0,"\xB3A"],"\xA2"=>[0,"\xB3_"],"\xA3"=>[0,"\xB3b"],"\xA4"=>[0,"\xB3d"],"\xA5"=>[0,"\xB3f"],"\xA6"=>[0,"\xB3g"],"\xA7"=>[0,"\xB3h"],"\xA8"=>[0,"\xB3l"],"\xA9"=>[0,"\xB3m"],"\xAA"=>[0,"\xB3n"],"\xAB"=>[0,"\xB3p"],"\xAC"=>[0,"\xB3r"],"\xAD"=>[0,"\xB3u"],"\xAE"=>[100,"\xB3"],"\xAF"=>[110,"\xB3"],"\xB0"=>[111,"\xB3"],"\xB1"=>[115,"\xB3"],"\xB2"=>[116,"\xB3"],"\xB3"=>[118,"\xB3"],"\xB4"=>[119,"\xB3"],"\xB5"=>[122,"\xB3"],"\xB6"=>[123,"\xB3"],"\xB7"=>[125,"\xB3"],"\xB8"=>[126,"\xB3"],"\xB9"=>[129,"\xB3"],"\xBA"=>[143,"\xB3"],"\xBB"=>[148,"\xB3"],"\xBC"=>[151,"\xB3"],"\xBD"=>[153,"\xB3"],"\xBE"=>[83,"\xB3"],"\xBF"=>[10,"\xB3"],"\xC0"=>[77,"\xD10"],"\xC1"=>[18,"\xD10"],"\xC2"=>[77,"\xD11"],"\xC3"=>[18,"\xD11"],"\xC4"=>[77,"\xD12"],"\xC5"=>[18,"\xD12"],"\xC6"=>[77,"\xD1a"],"\xC7"=>[18,"\xD1a"],"\xC8"=>[77,"\xD1c"],"\xC9"=>[18,"\xD1c"],"\xCA"=>[77,"\xD1e"],"\xCB"=>[18,"\xD1e"],"\xCC"=>[77,"\xD1i"],"\xCD"=>[18,"\xD1i"],"\xCE"=>[77,"\xD1o"],"\xCF"=>[18,"\xD1o"],"\xD0"=>[77,"\xD1s"],"\xD1"=>[18,"\xD1s"],"\xD2"=>[77,"\xD1t"],"\xD3"=>[18,"\xD1t"],"\xD4"=>[0,"\xD1\x20"],"\xD5"=>[0,"\xD1\x25"],"\xD6"=>[0,"\xD1-"],"\xD7"=>[0,"\xD1."],"\xD8"=>[0,"\xD1\x2F"],"\xD9"=>[0,"\xD13"],"\xDA"=>[0,"\xD14"],"\xDB"=>[0,"\xD15"],"\xDC"=>[0,"\xD16"],"\xDD"=>[0,"\xD17"],"\xDE"=>[0,"\xD18"],"\xDF"=>[0,"\xD19"],"\xE0"=>[0,"\xD1\x3D"],"\xE1"=>[0,"\xD1A"],"\xE2"=>[0,"\xD1_"],"\xE3"=>[0,"\xD1b"],"\xE4"=>[0,"\xD1d"],"\xE5"=>[0,"\xD1f"],"\xE6"=>[0,"\xD1g"],"\xE7"=>[0,"\xD1h"],"\xE8"=>[0,"\xD1l"],"\xE9"=>[0,"\xD1m"],"\xEA"=>[0,"\xD1n"],"\xEB"=>[0,"\xD1p"],"\xEC"=>[0,"\xD1r"],"\xED"=>[0,"\xD1u"],"\xEE"=>[100,"\xD1"],"\xEF"=>[110,"\xD1"],"\xF0"=>[111,"\xD1"],"\xF1"=>[115,"\xD1"],"\xF2"=>[116,"\xD1"],"\xF3"=>[118,"\xD1"],"\xF4"=>[119,"\xD1"],"\xF5"=>[122,"\xD1"],"\xF6"=>[123,"\xD1"],"\xF7"=>[125,"\xD1"],"\xF8"=>[126,"\xD1"],"\xF9"=>[129,"\xD1"],"\xFA"=>[143,"\xD1"],"\xFB"=>[148,"\xD1"],"\xFC"=>[151,"\xD1"],"\xFD"=>[153,"\xD1"],"\xFE"=>[83,"\xD1"],"\xFF"=>[10,"\xD1"],],["\x00"=>[0,"\xB00"],"\x01"=>[0,"\xB01"],"\x02"=>[0,"\xB02"],"\x03"=>[0,"\xB0a"],"\x04"=>[0,"\xB0c"],"\x05"=>[0,"\xB0e"],"\x06"=>[0,"\xB0i"],"\x07"=>[0,"\xB0o"],"\x08"=>[0,"\xB0s"],"\x09"=>[0,"\xB0t"],"\x0A"=>[73,"\xB0"],"\x0B"=>[88,"\xB0"],"\x0C"=>[89,"\xB0"],"\x0D"=>[96,"\xB0"],"\x0E"=>[97,"\xB0"],"\x0F"=>[99,"\xB0"],"\x10"=>[106,"\xB0"],"\x11"=>[136,"\xB0"],"\x12"=>[139,"\xB0"],"\x13"=>[141,"\xB0"],"\x14"=>[145,"\xB0"],"\x15"=>[147,"\xB0"],"\x16"=>[149,"\xB0"],"\x17"=>[101,"\xB0"],"\x18"=>[112,"\xB0"],"\x19"=>[117,"\xB0"],"\x1A"=>[120,"\xB0"],"\x1B"=>[124,"\xB0"],"\x1C"=>[127,"\xB0"],"\x1D"=>[144,"\xB0"],"\x1E"=>[152,"\xB0"],"\x1F"=>[11,"\xB0"],"\x20"=>[0,"\xB10"],"\x21"=>[0,"\xB11"],"\x22"=>[0,"\xB12"],"\x23"=>[0,"\xB1a"],"\x24"=>[0,"\xB1c"],"\x25"=>[0,"\xB1e"],"\x26"=>[0,"\xB1i"],"\x27"=>[0,"\xB1o"],"\x28"=>[0,"\xB1s"],"\x29"=>[0,"\xB1t"],"\x2A"=>[73,"\xB1"],"\x2B"=>[88,"\xB1"],"\x2C"=>[89,"\xB1"],"-"=>[96,"\xB1"],"."=>[97,"\xB1"],"\x2F"=>[99,"\xB1"],[106,"\xB1"],[136,"\xB1"],[139,"\xB1"],[141,"\xB1"],[145,"\xB1"],[147,"\xB1"],[149,"\xB1"],[101,"\xB1"],[112,"\xB1"],[117,"\xB1"],"\x3A"=>[120,"\xB1"],"\x3B"=>[124,"\xB1"],"\x3C"=>[127,"\xB1"],"\x3D"=>[144,"\xB1"],"\x3E"=>[152,"\xB1"],"\x3F"=>[11,"\xB1"],"\x40"=>[0,"\xB30"],"A"=>[0,"\xB31"],"B"=>[0,"\xB32"],"C"=>[0,"\xB3a"],"D"=>[0,"\xB3c"],"E"=>[0,"\xB3e"],"F"=>[0,"\xB3i"],"G"=>[0,"\xB3o"],"H"=>[0,"\xB3s"],"I"=>[0,"\xB3t"],"J"=>[73,"\xB3"],"K"=>[88,"\xB3"],"L"=>[89,"\xB3"],"M"=>[96,"\xB3"],"N"=>[97,"\xB3"],"O"=>[99,"\xB3"],"P"=>[106,"\xB3"],"Q"=>[136,"\xB3"],"R"=>[139,"\xB3"],"S"=>[141,"\xB3"],"T"=>[145,"\xB3"],"U"=>[147,"\xB3"],"V"=>[149,"\xB3"],"W"=>[101,"\xB3"],"X"=>[112,"\xB3"],"Y"=>[117,"\xB3"],"Z"=>[120,"\xB3"],"\x5B"=>[124,"\xB3"],"\x5C"=>[127,"\xB3"],"\x5D"=>[144,"\xB3"],"\x5E"=>[152,"\xB3"],"_"=>[11,"\xB3"],"\x60"=>[0,"\xD10"],"a"=>[0,"\xD11"],"b"=>[0,"\xD12"],"c"=>[0,"\xD1a"],"d"=>[0,"\xD1c"],"e"=>[0,"\xD1e"],"f"=>[0,"\xD1i"],"g"=>[0,"\xD1o"],"h"=>[0,"\xD1s"],"i"=>[0,"\xD1t"],"j"=>[73,"\xD1"],"k"=>[88,"\xD1"],"l"=>[89,"\xD1"],"m"=>[96,"\xD1"],"n"=>[97,"\xD1"],"o"=>[99,"\xD1"],"p"=>[106,"\xD1"],"q"=>[136,"\xD1"],"r"=>[139,"\xD1"],"s"=>[141,"\xD1"],"t"=>[145,"\xD1"],"u"=>[147,"\xD1"],"v"=>[149,"\xD1"],"w"=>[101,"\xD1"],"x"=>[112,"\xD1"],"y"=>[117,"\xD1"],"z"=>[120,"\xD1"],"\x7B"=>[124,"\xD1"],"\x7C"=>[127,"\xD1"],"\x7D"=>[144,"\xD1"],"~"=>[152,"\xD1"],"\x7F"=>[11,"\xD1"],"\x80"=>[0,"\xD80"],"\x81"=>[0,"\xD81"],"\x82"=>[0,"\xD82"],"\x83"=>[0,"\xD8a"],"\x84"=>[0,"\xD8c"],"\x85"=>[0,"\xD8e"],"\x86"=>[0,"\xD8i"],"\x87"=>[0,"\xD8o"],"\x88"=>[0,"\xD8s"],"\x89"=>[0,"\xD8t"],"\x8A"=>[73,"\xD8"],"\x8B"=>[88,"\xD8"],"\x8C"=>[89,"\xD8"],"\x8D"=>[96,"\xD8"],"\x8E"=>[97,"\xD8"],"\x8F"=>[99,"\xD8"],"\x90"=>[106,"\xD8"],"\x91"=>[136,"\xD8"],"\x92"=>[139,"\xD8"],"\x93"=>[141,"\xD8"],"\x94"=>[145,"\xD8"],"\x95"=>[147,"\xD8"],"\x96"=>[149,"\xD8"],"\x97"=>[101,"\xD8"],"\x98"=>[112,"\xD8"],"\x99"=>[117,"\xD8"],"\x9A"=>[120,"\xD8"],"\x9B"=>[124,"\xD8"],"\x9C"=>[127,"\xD8"],"\x9D"=>[144,"\xD8"],"\x9E"=>[152,"\xD8"],"\x9F"=>[11,"\xD8"],"\xA0"=>[0,"\xD90"],"\xA1"=>[0,"\xD91"],"\xA2"=>[0,"\xD92"],"\xA3"=>[0,"\xD9a"],"\xA4"=>[0,"\xD9c"],"\xA5"=>[0,"\xD9e"],"\xA6"=>[0,"\xD9i"],"\xA7"=>[0,"\xD9o"],"\xA8"=>[0,"\xD9s"],"\xA9"=>[0,"\xD9t"],"\xAA"=>[73,"\xD9"],"\xAB"=>[88,"\xD9"],"\xAC"=>[89,"\xD9"],"\xAD"=>[96,"\xD9"],"\xAE"=>[97,"\xD9"],"\xAF"=>[99,"\xD9"],"\xB0"=>[106,"\xD9"],"\xB1"=>[136,"\xD9"],"\xB2"=>[139,"\xD9"],"\xB3"=>[141,"\xD9"],"\xB4"=>[145,"\xD9"],"\xB5"=>[147,"\xD9"],"\xB6"=>[149,"\xD9"],"\xB7"=>[101,"\xD9"],"\xB8"=>[112,"\xD9"],"\xB9"=>[117,"\xD9"],"\xBA"=>[120,"\xD9"],"\xBB"=>[124,"\xD9"],"\xBC"=>[127,"\xD9"],"\xBD"=>[144,"\xD9"],"\xBE"=>[152,"\xD9"],"\xBF"=>[11,"\xD9"],"\xC0"=>[0,"\xE30"],"\xC1"=>[0,"\xE31"],"\xC2"=>[0,"\xE32"],"\xC3"=>[0,"\xE3a"],"\xC4"=>[0,"\xE3c"],"\xC5"=>[0,"\xE3e"],"\xC6"=>[0,"\xE3i"],"\xC7"=>[0,"\xE3o"],"\xC8"=>[0,"\xE3s"],"\xC9"=>[0,"\xE3t"],"\xCA"=>[73,"\xE3"],"\xCB"=>[88,"\xE3"],"\xCC"=>[89,"\xE3"],"\xCD"=>[96,"\xE3"],"\xCE"=>[97,"\xE3"],"\xCF"=>[99,"\xE3"],"\xD0"=>[106,"\xE3"],"\xD1"=>[136,"\xE3"],"\xD2"=>[139,"\xE3"],"\xD3"=>[141,"\xE3"],"\xD4"=>[145,"\xE3"],"\xD5"=>[147,"\xE3"],"\xD6"=>[149,"\xE3"],"\xD7"=>[101,"\xE3"],"\xD8"=>[112,"\xE3"],"\xD9"=>[117,"\xE3"],"\xDA"=>[120,"\xE3"],"\xDB"=>[124,"\xE3"],"\xDC"=>[127,"\xE3"],"\xDD"=>[144,"\xE3"],"\xDE"=>[152,"\xE3"],"\xDF"=>[11,"\xE3"],"\xE0"=>[0,"\xE50"],"\xE1"=>[0,"\xE51"],"\xE2"=>[0,"\xE52"],"\xE3"=>[0,"\xE5a"],"\xE4"=>[0,"\xE5c"],"\xE5"=>[0,"\xE5e"],"\xE6"=>[0,"\xE5i"],"\xE7"=>[0,"\xE5o"],"\xE8"=>[0,"\xE5s"],"\xE9"=>[0,"\xE5t"],"\xEA"=>[73,"\xE5"],"\xEB"=>[88,"\xE5"],"\xEC"=>[89,"\xE5"],"\xED"=>[96,"\xE5"],"\xEE"=>[97,"\xE5"],"\xEF"=>[99,"\xE5"],"\xF0"=>[106,"\xE5"],"\xF1"=>[136,"\xE5"],"\xF2"=>[139,"\xE5"],"\xF3"=>[141,"\xE5"],"\xF4"=>[145,"\xE5"],"\xF5"=>[147,"\xE5"],"\xF6"=>[149,"\xE5"],"\xF7"=>[101,"\xE5"],"\xF8"=>[112,"\xE5"],"\xF9"=>[117,"\xE5"],"\xFA"=>[120,"\xE5"],"\xFB"=>[124,"\xE5"],"\xFC"=>[127,"\xE5"],"\xFD"=>[144,"\xE5"],"\xFE"=>[152,"\xE5"],"\xFF"=>[11,"\xE5"],],["\x00"=>[94,"\xB20"],"\x01"=>[76,"\xB20"],"\x02"=>[104,"\xB20"],"\x03"=>[16,"\xB20"],"\x04"=>[94,"\xB21"],"\x05"=>[76,"\xB21"],"\x06"=>[104,"\xB21"],"\x07"=>[16,"\xB21"],"\x08"=>[94,"\xB22"],"\x09"=>[76,"\xB22"],"\x0A"=>[104,"\xB22"],"\x0B"=>[16,"\xB22"],"\x0C"=>[94,"\xB2a"],"\x0D"=>[76,"\xB2a"],"\x0E"=>[104,"\xB2a"],"\x0F"=>[16,"\xB2a"],"\x10"=>[94,"\xB2c"],"\x11"=>[76,"\xB2c"],"\x12"=>[104,"\xB2c"],"\x13"=>[16,"\xB2c"],"\x14"=>[94,"\xB2e"],"\x15"=>[76,"\xB2e"],"\x16"=>[104,"\xB2e"],"\x17"=>[16,"\xB2e"],"\x18"=>[94,"\xB2i"],"\x19"=>[76,"\xB2i"],"\x1A"=>[104,"\xB2i"],"\x1B"=>[16,"\xB2i"],"\x1C"=>[94,"\xB2o"],"\x1D"=>[76,"\xB2o"],"\x1E"=>[104,"\xB2o"],"\x1F"=>[16,"\xB2o"],"\x20"=>[94,"\xB2s"],"\x21"=>[76,"\xB2s"],"\x22"=>[104,"\xB2s"],"\x23"=>[16,"\xB2s"],"\x24"=>[94,"\xB2t"],"\x25"=>[76,"\xB2t"],"\x26"=>[104,"\xB2t"],"\x27"=>[16,"\xB2t"],"\x28"=>[77,"\xB2\x20"],"\x29"=>[18,"\xB2\x20"],"\x2A"=>[77,"\xB2\x25"],"\x2B"=>[18,"\xB2\x25"],"\x2C"=>[77,"\xB2-"],"-"=>[18,"\xB2-"],"."=>[77,"\xB2."],"\x2F"=>[18,"\xB2."],[77,"\xB2\x2F"],[18,"\xB2\x2F"],[77,"\xB23"],[18,"\xB23"],[77,"\xB24"],[18,"\xB24"],[77,"\xB25"],[18,"\xB25"],[77,"\xB26"],[18,"\xB26"],"\x3A"=>[77,"\xB27"],"\x3B"=>[18,"\xB27"],"\x3C"=>[77,"\xB28"],"\x3D"=>[18,"\xB28"],"\x3E"=>[77,"\xB29"],"\x3F"=>[18,"\xB29"],"\x40"=>[77,"\xB2\x3D"],"A"=>[18,"\xB2\x3D"],"B"=>[77,"\xB2A"],"C"=>[18,"\xB2A"],"D"=>[77,"\xB2_"],"E"=>[18,"\xB2_"],"F"=>[77,"\xB2b"],"G"=>[18,"\xB2b"],"H"=>[77,"\xB2d"],"I"=>[18,"\xB2d"],"J"=>[77,"\xB2f"],"K"=>[18,"\xB2f"],"L"=>[77,"\xB2g"],"M"=>[18,"\xB2g"],"N"=>[77,"\xB2h"],"O"=>[18,"\xB2h"],"P"=>[77,"\xB2l"],"Q"=>[18,"\xB2l"],"R"=>[77,"\xB2m"],"S"=>[18,"\xB2m"],"T"=>[77,"\xB2n"],"U"=>[18,"\xB2n"],"V"=>[77,"\xB2p"],"W"=>[18,"\xB2p"],"X"=>[77,"\xB2r"],"Y"=>[18,"\xB2r"],"Z"=>[77,"\xB2u"],"\x5B"=>[18,"\xB2u"],"\x5C"=>[0,"\xB2\x3A"],"\x5D"=>[0,"\xB2B"],"\x5E"=>[0,"\xB2C"],"_"=>[0,"\xB2D"],"\x60"=>[0,"\xB2E"],"a"=>[0,"\xB2F"],"b"=>[0,"\xB2G"],"c"=>[0,"\xB2H"],"d"=>[0,"\xB2I"],"e"=>[0,"\xB2J"],"f"=>[0,"\xB2K"],"g"=>[0,"\xB2L"],"h"=>[0,"\xB2M"],"i"=>[0,"\xB2N"],"j"=>[0,"\xB2O"],"k"=>[0,"\xB2P"],"l"=>[0,"\xB2Q"],"m"=>[0,"\xB2R"],"n"=>[0,"\xB2S"],"o"=>[0,"\xB2T"],"p"=>[0,"\xB2U"],"q"=>[0,"\xB2V"],"r"=>[0,"\xB2W"],"s"=>[0,"\xB2Y"],"t"=>[0,"\xB2j"],"u"=>[0,"\xB2k"],"v"=>[0,"\xB2q"],"w"=>[0,"\xB2v"],"x"=>[0,"\xB2w"],"y"=>[0,"\xB2x"],"z"=>[0,"\xB2y"],"\x7B"=>[0,"\xB2z"],"\x7C"=>[82,"\xB2"],"\x7D"=>[87,"\xB2"],"~"=>[130,"\xB2"],"\x7F"=>[9,"\xB2"],"\x80"=>[94,"\xB50"],"\x81"=>[76,"\xB50"],"\x82"=>[104,"\xB50"],"\x83"=>[16,"\xB50"],"\x84"=>[94,"\xB51"],"\x85"=>[76,"\xB51"],"\x86"=>[104,"\xB51"],"\x87"=>[16,"\xB51"],"\x88"=>[94,"\xB52"],"\x89"=>[76,"\xB52"],"\x8A"=>[104,"\xB52"],"\x8B"=>[16,"\xB52"],"\x8C"=>[94,"\xB5a"],"\x8D"=>[76,"\xB5a"],"\x8E"=>[104,"\xB5a"],"\x8F"=>[16,"\xB5a"],"\x90"=>[94,"\xB5c"],"\x91"=>[76,"\xB5c"],"\x92"=>[104,"\xB5c"],"\x93"=>[16,"\xB5c"],"\x94"=>[94,"\xB5e"],"\x95"=>[76,"\xB5e"],"\x96"=>[104,"\xB5e"],"\x97"=>[16,"\xB5e"],"\x98"=>[94,"\xB5i"],"\x99"=>[76,"\xB5i"],"\x9A"=>[104,"\xB5i"],"\x9B"=>[16,"\xB5i"],"\x9C"=>[94,"\xB5o"],"\x9D"=>[76,"\xB5o"],"\x9E"=>[104,"\xB5o"],"\x9F"=>[16,"\xB5o"],"\xA0"=>[94,"\xB5s"],"\xA1"=>[76,"\xB5s"],"\xA2"=>[104,"\xB5s"],"\xA3"=>[16,"\xB5s"],"\xA4"=>[94,"\xB5t"],"\xA5"=>[76,"\xB5t"],"\xA6"=>[104,"\xB5t"],"\xA7"=>[16,"\xB5t"],"\xA8"=>[77,"\xB5\x20"],"\xA9"=>[18,"\xB5\x20"],"\xAA"=>[77,"\xB5\x25"],"\xAB"=>[18,"\xB5\x25"],"\xAC"=>[77,"\xB5-"],"\xAD"=>[18,"\xB5-"],"\xAE"=>[77,"\xB5."],"\xAF"=>[18,"\xB5."],"\xB0"=>[77,"\xB5\x2F"],"\xB1"=>[18,"\xB5\x2F"],"\xB2"=>[77,"\xB53"],"\xB3"=>[18,"\xB53"],"\xB4"=>[77,"\xB54"],"\xB5"=>[18,"\xB54"],"\xB6"=>[77,"\xB55"],"\xB7"=>[18,"\xB55"],"\xB8"=>[77,"\xB56"],"\xB9"=>[18,"\xB56"],"\xBA"=>[77,"\xB57"],"\xBB"=>[18,"\xB57"],"\xBC"=>[77,"\xB58"],"\xBD"=>[18,"\xB58"],"\xBE"=>[77,"\xB59"],"\xBF"=>[18,"\xB59"],"\xC0"=>[77,"\xB5\x3D"],"\xC1"=>[18,"\xB5\x3D"],"\xC2"=>[77,"\xB5A"],"\xC3"=>[18,"\xB5A"],"\xC4"=>[77,"\xB5_"],"\xC5"=>[18,"\xB5_"],"\xC6"=>[77,"\xB5b"],"\xC7"=>[18,"\xB5b"],"\xC8"=>[77,"\xB5d"],"\xC9"=>[18,"\xB5d"],"\xCA"=>[77,"\xB5f"],"\xCB"=>[18,"\xB5f"],"\xCC"=>[77,"\xB5g"],"\xCD"=>[18,"\xB5g"],"\xCE"=>[77,"\xB5h"],"\xCF"=>[18,"\xB5h"],"\xD0"=>[77,"\xB5l"],"\xD1"=>[18,"\xB5l"],"\xD2"=>[77,"\xB5m"],"\xD3"=>[18,"\xB5m"],"\xD4"=>[77,"\xB5n"],"\xD5"=>[18,"\xB5n"],"\xD6"=>[77,"\xB5p"],"\xD7"=>[18,"\xB5p"],"\xD8"=>[77,"\xB5r"],"\xD9"=>[18,"\xB5r"],"\xDA"=>[77,"\xB5u"],"\xDB"=>[18,"\xB5u"],"\xDC"=>[0,"\xB5\x3A"],"\xDD"=>[0,"\xB5B"],"\xDE"=>[0,"\xB5C"],"\xDF"=>[0,"\xB5D"],"\xE0"=>[0,"\xB5E"],"\xE1"=>[0,"\xB5F"],"\xE2"=>[0,"\xB5G"],"\xE3"=>[0,"\xB5H"],"\xE4"=>[0,"\xB5I"],"\xE5"=>[0,"\xB5J"],"\xE6"=>[0,"\xB5K"],"\xE7"=>[0,"\xB5L"],"\xE8"=>[0,"\xB5M"],"\xE9"=>[0,"\xB5N"],"\xEA"=>[0,"\xB5O"],"\xEB"=>[0,"\xB5P"],"\xEC"=>[0,"\xB5Q"],"\xED"=>[0,"\xB5R"],"\xEE"=>[0,"\xB5S"],"\xEF"=>[0,"\xB5T"],"\xF0"=>[0,"\xB5U"],"\xF1"=>[0,"\xB5V"],"\xF2"=>[0,"\xB5W"],"\xF3"=>[0,"\xB5Y"],"\xF4"=>[0,"\xB5j"],"\xF5"=>[0,"\xB5k"],"\xF6"=>[0,"\xB5q"],"\xF7"=>[0,"\xB5v"],"\xF8"=>[0,"\xB5w"],"\xF9"=>[0,"\xB5x"],"\xFA"=>[0,"\xB5y"],"\xFB"=>[0,"\xB5z"],"\xFC"=>[82,"\xB5"],"\xFD"=>[87,"\xB5"],"\xFE"=>[130,"\xB5"],"\xFF"=>[9,"\xB5"],],["\x00"=>[77,"\xB20"],"\x01"=>[18,"\xB20"],"\x02"=>[77,"\xB21"],"\x03"=>[18,"\xB21"],"\x04"=>[77,"\xB22"],"\x05"=>[18,"\xB22"],"\x06"=>[77,"\xB2a"],"\x07"=>[18,"\xB2a"],"\x08"=>[77,"\xB2c"],"\x09"=>[18,"\xB2c"],"\x0A"=>[77,"\xB2e"],"\x0B"=>[18,"\xB2e"],"\x0C"=>[77,"\xB2i"],"\x0D"=>[18,"\xB2i"],"\x0E"=>[77,"\xB2o"],"\x0F"=>[18,"\xB2o"],"\x10"=>[77,"\xB2s"],"\x11"=>[18,"\xB2s"],"\x12"=>[77,"\xB2t"],"\x13"=>[18,"\xB2t"],"\x14"=>[0,"\xB2\x20"],"\x15"=>[0,"\xB2\x25"],"\x16"=>[0,"\xB2-"],"\x17"=>[0,"\xB2."],"\x18"=>[0,"\xB2\x2F"],"\x19"=>[0,"\xB23"],"\x1A"=>[0,"\xB24"],"\x1B"=>[0,"\xB25"],"\x1C"=>[0,"\xB26"],"\x1D"=>[0,"\xB27"],"\x1E"=>[0,"\xB28"],"\x1F"=>[0,"\xB29"],"\x20"=>[0,"\xB2\x3D"],"\x21"=>[0,"\xB2A"],"\x22"=>[0,"\xB2_"],"\x23"=>[0,"\xB2b"],"\x24"=>[0,"\xB2d"],"\x25"=>[0,"\xB2f"],"\x26"=>[0,"\xB2g"],"\x27"=>[0,"\xB2h"],"\x28"=>[0,"\xB2l"],"\x29"=>[0,"\xB2m"],"\x2A"=>[0,"\xB2n"],"\x2B"=>[0,"\xB2p"],"\x2C"=>[0,"\xB2r"],"-"=>[0,"\xB2u"],"."=>[100,"\xB2"],"\x2F"=>[110,"\xB2"],[111,"\xB2"],[115,"\xB2"],[116,"\xB2"],[118,"\xB2"],[119,"\xB2"],[122,"\xB2"],[123,"\xB2"],[125,"\xB2"],[126,"\xB2"],[129,"\xB2"],"\x3A"=>[143,"\xB2"],"\x3B"=>[148,"\xB2"],"\x3C"=>[151,"\xB2"],"\x3D"=>[153,"\xB2"],"\x3E"=>[83,"\xB2"],"\x3F"=>[10,"\xB2"],"\x40"=>[77,"\xB50"],"A"=>[18,"\xB50"],"B"=>[77,"\xB51"],"C"=>[18,"\xB51"],"D"=>[77,"\xB52"],"E"=>[18,"\xB52"],"F"=>[77,"\xB5a"],"G"=>[18,"\xB5a"],"H"=>[77,"\xB5c"],"I"=>[18,"\xB5c"],"J"=>[77,"\xB5e"],"K"=>[18,"\xB5e"],"L"=>[77,"\xB5i"],"M"=>[18,"\xB5i"],"N"=>[77,"\xB5o"],"O"=>[18,"\xB5o"],"P"=>[77,"\xB5s"],"Q"=>[18,"\xB5s"],"R"=>[77,"\xB5t"],"S"=>[18,"\xB5t"],"T"=>[0,"\xB5\x20"],"U"=>[0,"\xB5\x25"],"V"=>[0,"\xB5-"],"W"=>[0,"\xB5."],"X"=>[0,"\xB5\x2F"],"Y"=>[0,"\xB53"],"Z"=>[0,"\xB54"],"\x5B"=>[0,"\xB55"],"\x5C"=>[0,"\xB56"],"\x5D"=>[0,"\xB57"],"\x5E"=>[0,"\xB58"],"_"=>[0,"\xB59"],"\x60"=>[0,"\xB5\x3D"],"a"=>[0,"\xB5A"],"b"=>[0,"\xB5_"],"c"=>[0,"\xB5b"],"d"=>[0,"\xB5d"],"e"=>[0,"\xB5f"],"f"=>[0,"\xB5g"],"g"=>[0,"\xB5h"],"h"=>[0,"\xB5l"],"i"=>[0,"\xB5m"],"j"=>[0,"\xB5n"],"k"=>[0,"\xB5p"],"l"=>[0,"\xB5r"],"m"=>[0,"\xB5u"],"n"=>[100,"\xB5"],"o"=>[110,"\xB5"],"p"=>[111,"\xB5"],"q"=>[115,"\xB5"],"r"=>[116,"\xB5"],"s"=>[118,"\xB5"],"t"=>[119,"\xB5"],"u"=>[122,"\xB5"],"v"=>[123,"\xB5"],"w"=>[125,"\xB5"],"x"=>[126,"\xB5"],"y"=>[129,"\xB5"],"z"=>[143,"\xB5"],"\x7B"=>[148,"\xB5"],"\x7C"=>[151,"\xB5"],"\x7D"=>[153,"\xB5"],"~"=>[83,"\xB5"],"\x7F"=>[10,"\xB5"],"\x80"=>[77,"\xB90"],"\x81"=>[18,"\xB90"],"\x82"=>[77,"\xB91"],"\x83"=>[18,"\xB91"],"\x84"=>[77,"\xB92"],"\x85"=>[18,"\xB92"],"\x86"=>[77,"\xB9a"],"\x87"=>[18,"\xB9a"],"\x88"=>[77,"\xB9c"],"\x89"=>[18,"\xB9c"],"\x8A"=>[77,"\xB9e"],"\x8B"=>[18,"\xB9e"],"\x8C"=>[77,"\xB9i"],"\x8D"=>[18,"\xB9i"],"\x8E"=>[77,"\xB9o"],"\x8F"=>[18,"\xB9o"],"\x90"=>[77,"\xB9s"],"\x91"=>[18,"\xB9s"],"\x92"=>[77,"\xB9t"],"\x93"=>[18,"\xB9t"],"\x94"=>[0,"\xB9\x20"],"\x95"=>[0,"\xB9\x25"],"\x96"=>[0,"\xB9-"],"\x97"=>[0,"\xB9."],"\x98"=>[0,"\xB9\x2F"],"\x99"=>[0,"\xB93"],"\x9A"=>[0,"\xB94"],"\x9B"=>[0,"\xB95"],"\x9C"=>[0,"\xB96"],"\x9D"=>[0,"\xB97"],"\x9E"=>[0,"\xB98"],"\x9F"=>[0,"\xB99"],"\xA0"=>[0,"\xB9\x3D"],"\xA1"=>[0,"\xB9A"],"\xA2"=>[0,"\xB9_"],"\xA3"=>[0,"\xB9b"],"\xA4"=>[0,"\xB9d"],"\xA5"=>[0,"\xB9f"],"\xA6"=>[0,"\xB9g"],"\xA7"=>[0,"\xB9h"],"\xA8"=>[0,"\xB9l"],"\xA9"=>[0,"\xB9m"],"\xAA"=>[0,"\xB9n"],"\xAB"=>[0,"\xB9p"],"\xAC"=>[0,"\xB9r"],"\xAD"=>[0,"\xB9u"],"\xAE"=>[100,"\xB9"],"\xAF"=>[110,"\xB9"],"\xB0"=>[111,"\xB9"],"\xB1"=>[115,"\xB9"],"\xB2"=>[116,"\xB9"],"\xB3"=>[118,"\xB9"],"\xB4"=>[119,"\xB9"],"\xB5"=>[122,"\xB9"],"\xB6"=>[123,"\xB9"],"\xB7"=>[125,"\xB9"],"\xB8"=>[126,"\xB9"],"\xB9"=>[129,"\xB9"],"\xBA"=>[143,"\xB9"],"\xBB"=>[148,"\xB9"],"\xBC"=>[151,"\xB9"],"\xBD"=>[153,"\xB9"],"\xBE"=>[83,"\xB9"],"\xBF"=>[10,"\xB9"],"\xC0"=>[77,"\xBA0"],"\xC1"=>[18,"\xBA0"],"\xC2"=>[77,"\xBA1"],"\xC3"=>[18,"\xBA1"],"\xC4"=>[77,"\xBA2"],"\xC5"=>[18,"\xBA2"],"\xC6"=>[77,"\xBAa"],"\xC7"=>[18,"\xBAa"],"\xC8"=>[77,"\xBAc"],"\xC9"=>[18,"\xBAc"],"\xCA"=>[77,"\xBAe"],"\xCB"=>[18,"\xBAe"],"\xCC"=>[77,"\xBAi"],"\xCD"=>[18,"\xBAi"],"\xCE"=>[77,"\xBAo"],"\xCF"=>[18,"\xBAo"],"\xD0"=>[77,"\xBAs"],"\xD1"=>[18,"\xBAs"],"\xD2"=>[77,"\xBAt"],"\xD3"=>[18,"\xBAt"],"\xD4"=>[0,"\xBA\x20"],"\xD5"=>[0,"\xBA\x25"],"\xD6"=>[0,"\xBA-"],"\xD7"=>[0,"\xBA."],"\xD8"=>[0,"\xBA\x2F"],"\xD9"=>[0,"\xBA3"],"\xDA"=>[0,"\xBA4"],"\xDB"=>[0,"\xBA5"],"\xDC"=>[0,"\xBA6"],"\xDD"=>[0,"\xBA7"],"\xDE"=>[0,"\xBA8"],"\xDF"=>[0,"\xBA9"],"\xE0"=>[0,"\xBA\x3D"],"\xE1"=>[0,"\xBAA"],"\xE2"=>[0,"\xBA_"],"\xE3"=>[0,"\xBAb"],"\xE4"=>[0,"\xBAd"],"\xE5"=>[0,"\xBAf"],"\xE6"=>[0,"\xBAg"],"\xE7"=>[0,"\xBAh"],"\xE8"=>[0,"\xBAl"],"\xE9"=>[0,"\xBAm"],"\xEA"=>[0,"\xBAn"],"\xEB"=>[0,"\xBAp"],"\xEC"=>[0,"\xBAr"],"\xED"=>[0,"\xBAu"],"\xEE"=>[100,"\xBA"],"\xEF"=>[110,"\xBA"],"\xF0"=>[111,"\xBA"],"\xF1"=>[115,"\xBA"],"\xF2"=>[116,"\xBA"],"\xF3"=>[118,"\xBA"],"\xF4"=>[119,"\xBA"],"\xF5"=>[122,"\xBA"],"\xF6"=>[123,"\xBA"],"\xF7"=>[125,"\xBA"],"\xF8"=>[126,"\xBA"],"\xF9"=>[129,"\xBA"],"\xFA"=>[143,"\xBA"],"\xFB"=>[148,"\xBA"],"\xFC"=>[151,"\xBA"],"\xFD"=>[153,"\xBA"],"\xFE"=>[83,"\xBA"],"\xFF"=>[10,"\xBA"],],["\x00"=>[0,"\xB20"],"\x01"=>[0,"\xB21"],"\x02"=>[0,"\xB22"],"\x03"=>[0,"\xB2a"],"\x04"=>[0,"\xB2c"],"\x05"=>[0,"\xB2e"],"\x06"=>[0,"\xB2i"],"\x07"=>[0,"\xB2o"],"\x08"=>[0,"\xB2s"],"\x09"=>[0,"\xB2t"],"\x0A"=>[73,"\xB2"],"\x0B"=>[88,"\xB2"],"\x0C"=>[89,"\xB2"],"\x0D"=>[96,"\xB2"],"\x0E"=>[97,"\xB2"],"\x0F"=>[99,"\xB2"],"\x10"=>[106,"\xB2"],"\x11"=>[136,"\xB2"],"\x12"=>[139,"\xB2"],"\x13"=>[141,"\xB2"],"\x14"=>[145,"\xB2"],"\x15"=>[147,"\xB2"],"\x16"=>[149,"\xB2"],"\x17"=>[101,"\xB2"],"\x18"=>[112,"\xB2"],"\x19"=>[117,"\xB2"],"\x1A"=>[120,"\xB2"],"\x1B"=>[124,"\xB2"],"\x1C"=>[127,"\xB2"],"\x1D"=>[144,"\xB2"],"\x1E"=>[152,"\xB2"],"\x1F"=>[11,"\xB2"],"\x20"=>[0,"\xB50"],"\x21"=>[0,"\xB51"],"\x22"=>[0,"\xB52"],"\x23"=>[0,"\xB5a"],"\x24"=>[0,"\xB5c"],"\x25"=>[0,"\xB5e"],"\x26"=>[0,"\xB5i"],"\x27"=>[0,"\xB5o"],"\x28"=>[0,"\xB5s"],"\x29"=>[0,"\xB5t"],"\x2A"=>[73,"\xB5"],"\x2B"=>[88,"\xB5"],"\x2C"=>[89,"\xB5"],"-"=>[96,"\xB5"],"."=>[97,"\xB5"],"\x2F"=>[99,"\xB5"],[106,"\xB5"],[136,"\xB5"],[139,"\xB5"],[141,"\xB5"],[145,"\xB5"],[147,"\xB5"],[149,"\xB5"],[101,"\xB5"],[112,"\xB5"],[117,"\xB5"],"\x3A"=>[120,"\xB5"],"\x3B"=>[124,"\xB5"],"\x3C"=>[127,"\xB5"],"\x3D"=>[144,"\xB5"],"\x3E"=>[152,"\xB5"],"\x3F"=>[11,"\xB5"],"\x40"=>[0,"\xB90"],"A"=>[0,"\xB91"],"B"=>[0,"\xB92"],"C"=>[0,"\xB9a"],"D"=>[0,"\xB9c"],"E"=>[0,"\xB9e"],"F"=>[0,"\xB9i"],"G"=>[0,"\xB9o"],"H"=>[0,"\xB9s"],"I"=>[0,"\xB9t"],"J"=>[73,"\xB9"],"K"=>[88,"\xB9"],"L"=>[89,"\xB9"],"M"=>[96,"\xB9"],"N"=>[97,"\xB9"],"O"=>[99,"\xB9"],"P"=>[106,"\xB9"],"Q"=>[136,"\xB9"],"R"=>[139,"\xB9"],"S"=>[141,"\xB9"],"T"=>[145,"\xB9"],"U"=>[147,"\xB9"],"V"=>[149,"\xB9"],"W"=>[101,"\xB9"],"X"=>[112,"\xB9"],"Y"=>[117,"\xB9"],"Z"=>[120,"\xB9"],"\x5B"=>[124,"\xB9"],"\x5C"=>[127,"\xB9"],"\x5D"=>[144,"\xB9"],"\x5E"=>[152,"\xB9"],"_"=>[11,"\xB9"],"\x60"=>[0,"\xBA0"],"a"=>[0,"\xBA1"],"b"=>[0,"\xBA2"],"c"=>[0,"\xBAa"],"d"=>[0,"\xBAc"],"e"=>[0,"\xBAe"],"f"=>[0,"\xBAi"],"g"=>[0,"\xBAo"],"h"=>[0,"\xBAs"],"i"=>[0,"\xBAt"],"j"=>[73,"\xBA"],"k"=>[88,"\xBA"],"l"=>[89,"\xBA"],"m"=>[96,"\xBA"],"n"=>[97,"\xBA"],"o"=>[99,"\xBA"],"p"=>[106,"\xBA"],"q"=>[136,"\xBA"],"r"=>[139,"\xBA"],"s"=>[141,"\xBA"],"t"=>[145,"\xBA"],"u"=>[147,"\xBA"],"v"=>[149,"\xBA"],"w"=>[101,"\xBA"],"x"=>[112,"\xBA"],"y"=>[117,"\xBA"],"z"=>[120,"\xBA"],"\x7B"=>[124,"\xBA"],"\x7C"=>[127,"\xBA"],"\x7D"=>[144,"\xBA"],"~"=>[152,"\xBA"],"\x7F"=>[11,"\xBA"],"\x80"=>[0,"\xBB0"],"\x81"=>[0,"\xBB1"],"\x82"=>[0,"\xBB2"],"\x83"=>[0,"\xBBa"],"\x84"=>[0,"\xBBc"],"\x85"=>[0,"\xBBe"],"\x86"=>[0,"\xBBi"],"\x87"=>[0,"\xBBo"],"\x88"=>[0,"\xBBs"],"\x89"=>[0,"\xBBt"],"\x8A"=>[73,"\xBB"],"\x8B"=>[88,"\xBB"],"\x8C"=>[89,"\xBB"],"\x8D"=>[96,"\xBB"],"\x8E"=>[97,"\xBB"],"\x8F"=>[99,"\xBB"],"\x90"=>[106,"\xBB"],"\x91"=>[136,"\xBB"],"\x92"=>[139,"\xBB"],"\x93"=>[141,"\xBB"],"\x94"=>[145,"\xBB"],"\x95"=>[147,"\xBB"],"\x96"=>[149,"\xBB"],"\x97"=>[101,"\xBB"],"\x98"=>[112,"\xBB"],"\x99"=>[117,"\xBB"],"\x9A"=>[120,"\xBB"],"\x9B"=>[124,"\xBB"],"\x9C"=>[127,"\xBB"],"\x9D"=>[144,"\xBB"],"\x9E"=>[152,"\xBB"],"\x9F"=>[11,"\xBB"],"\xA0"=>[0,"\xBD0"],"\xA1"=>[0,"\xBD1"],"\xA2"=>[0,"\xBD2"],"\xA3"=>[0,"\xBDa"],"\xA4"=>[0,"\xBDc"],"\xA5"=>[0,"\xBDe"],"\xA6"=>[0,"\xBDi"],"\xA7"=>[0,"\xBDo"],"\xA8"=>[0,"\xBDs"],"\xA9"=>[0,"\xBDt"],"\xAA"=>[73,"\xBD"],"\xAB"=>[88,"\xBD"],"\xAC"=>[89,"\xBD"],"\xAD"=>[96,"\xBD"],"\xAE"=>[97,"\xBD"],"\xAF"=>[99,"\xBD"],"\xB0"=>[106,"\xBD"],"\xB1"=>[136,"\xBD"],"\xB2"=>[139,"\xBD"],"\xB3"=>[141,"\xBD"],"\xB4"=>[145,"\xBD"],"\xB5"=>[147,"\xBD"],"\xB6"=>[149,"\xBD"],"\xB7"=>[101,"\xBD"],"\xB8"=>[112,"\xBD"],"\xB9"=>[117,"\xBD"],"\xBA"=>[120,"\xBD"],"\xBB"=>[124,"\xBD"],"\xBC"=>[127,"\xBD"],"\xBD"=>[144,"\xBD"],"\xBE"=>[152,"\xBD"],"\xBF"=>[11,"\xBD"],"\xC0"=>[0,"\xBE0"],"\xC1"=>[0,"\xBE1"],"\xC2"=>[0,"\xBE2"],"\xC3"=>[0,"\xBEa"],"\xC4"=>[0,"\xBEc"],"\xC5"=>[0,"\xBEe"],"\xC6"=>[0,"\xBEi"],"\xC7"=>[0,"\xBEo"],"\xC8"=>[0,"\xBEs"],"\xC9"=>[0,"\xBEt"],"\xCA"=>[73,"\xBE"],"\xCB"=>[88,"\xBE"],"\xCC"=>[89,"\xBE"],"\xCD"=>[96,"\xBE"],"\xCE"=>[97,"\xBE"],"\xCF"=>[99,"\xBE"],"\xD0"=>[106,"\xBE"],"\xD1"=>[136,"\xBE"],"\xD2"=>[139,"\xBE"],"\xD3"=>[141,"\xBE"],"\xD4"=>[145,"\xBE"],"\xD5"=>[147,"\xBE"],"\xD6"=>[149,"\xBE"],"\xD7"=>[101,"\xBE"],"\xD8"=>[112,"\xBE"],"\xD9"=>[117,"\xBE"],"\xDA"=>[120,"\xBE"],"\xDB"=>[124,"\xBE"],"\xDC"=>[127,"\xBE"],"\xDD"=>[144,"\xBE"],"\xDE"=>[152,"\xBE"],"\xDF"=>[11,"\xBE"],"\xE0"=>[0,"\xC40"],"\xE1"=>[0,"\xC41"],"\xE2"=>[0,"\xC42"],"\xE3"=>[0,"\xC4a"],"\xE4"=>[0,"\xC4c"],"\xE5"=>[0,"\xC4e"],"\xE6"=>[0,"\xC4i"],"\xE7"=>[0,"\xC4o"],"\xE8"=>[0,"\xC4s"],"\xE9"=>[0,"\xC4t"],"\xEA"=>[73,"\xC4"],"\xEB"=>[88,"\xC4"],"\xEC"=>[89,"\xC4"],"\xED"=>[96,"\xC4"],"\xEE"=>[97,"\xC4"],"\xEF"=>[99,"\xC4"],"\xF0"=>[106,"\xC4"],"\xF1"=>[136,"\xC4"],"\xF2"=>[139,"\xC4"],"\xF3"=>[141,"\xC4"],"\xF4"=>[145,"\xC4"],"\xF5"=>[147,"\xC4"],"\xF6"=>[149,"\xC4"],"\xF7"=>[101,"\xC4"],"\xF8"=>[112,"\xC4"],"\xF9"=>[117,"\xC4"],"\xFA"=>[120,"\xC4"],"\xFB"=>[124,"\xC4"],"\xFC"=>[127,"\xC4"],"\xFD"=>[144,"\xC4"],"\xFE"=>[152,"\xC4"],"\xFF"=>[11,"\xC4"],],["\x00"=>[94,"\xB30"],"\x01"=>[76,"\xB30"],"\x02"=>[104,"\xB30"],"\x03"=>[16,"\xB30"],"\x04"=>[94,"\xB31"],"\x05"=>[76,"\xB31"],"\x06"=>[104,"\xB31"],"\x07"=>[16,"\xB31"],"\x08"=>[94,"\xB32"],"\x09"=>[76,"\xB32"],"\x0A"=>[104,"\xB32"],"\x0B"=>[16,"\xB32"],"\x0C"=>[94,"\xB3a"],"\x0D"=>[76,"\xB3a"],"\x0E"=>[104,"\xB3a"],"\x0F"=>[16,"\xB3a"],"\x10"=>[94,"\xB3c"],"\x11"=>[76,"\xB3c"],"\x12"=>[104,"\xB3c"],"\x13"=>[16,"\xB3c"],"\x14"=>[94,"\xB3e"],"\x15"=>[76,"\xB3e"],"\x16"=>[104,"\xB3e"],"\x17"=>[16,"\xB3e"],"\x18"=>[94,"\xB3i"],"\x19"=>[76,"\xB3i"],"\x1A"=>[104,"\xB3i"],"\x1B"=>[16,"\xB3i"],"\x1C"=>[94,"\xB3o"],"\x1D"=>[76,"\xB3o"],"\x1E"=>[104,"\xB3o"],"\x1F"=>[16,"\xB3o"],"\x20"=>[94,"\xB3s"],"\x21"=>[76,"\xB3s"],"\x22"=>[104,"\xB3s"],"\x23"=>[16,"\xB3s"],"\x24"=>[94,"\xB3t"],"\x25"=>[76,"\xB3t"],"\x26"=>[104,"\xB3t"],"\x27"=>[16,"\xB3t"],"\x28"=>[77,"\xB3\x20"],"\x29"=>[18,"\xB3\x20"],"\x2A"=>[77,"\xB3\x25"],"\x2B"=>[18,"\xB3\x25"],"\x2C"=>[77,"\xB3-"],"-"=>[18,"\xB3-"],"."=>[77,"\xB3."],"\x2F"=>[18,"\xB3."],[77,"\xB3\x2F"],[18,"\xB3\x2F"],[77,"\xB33"],[18,"\xB33"],[77,"\xB34"],[18,"\xB34"],[77,"\xB35"],[18,"\xB35"],[77,"\xB36"],[18,"\xB36"],"\x3A"=>[77,"\xB37"],"\x3B"=>[18,"\xB37"],"\x3C"=>[77,"\xB38"],"\x3D"=>[18,"\xB38"],"\x3E"=>[77,"\xB39"],"\x3F"=>[18,"\xB39"],"\x40"=>[77,"\xB3\x3D"],"A"=>[18,"\xB3\x3D"],"B"=>[77,"\xB3A"],"C"=>[18,"\xB3A"],"D"=>[77,"\xB3_"],"E"=>[18,"\xB3_"],"F"=>[77,"\xB3b"],"G"=>[18,"\xB3b"],"H"=>[77,"\xB3d"],"I"=>[18,"\xB3d"],"J"=>[77,"\xB3f"],"K"=>[18,"\xB3f"],"L"=>[77,"\xB3g"],"M"=>[18,"\xB3g"],"N"=>[77,"\xB3h"],"O"=>[18,"\xB3h"],"P"=>[77,"\xB3l"],"Q"=>[18,"\xB3l"],"R"=>[77,"\xB3m"],"S"=>[18,"\xB3m"],"T"=>[77,"\xB3n"],"U"=>[18,"\xB3n"],"V"=>[77,"\xB3p"],"W"=>[18,"\xB3p"],"X"=>[77,"\xB3r"],"Y"=>[18,"\xB3r"],"Z"=>[77,"\xB3u"],"\x5B"=>[18,"\xB3u"],"\x5C"=>[0,"\xB3\x3A"],"\x5D"=>[0,"\xB3B"],"\x5E"=>[0,"\xB3C"],"_"=>[0,"\xB3D"],"\x60"=>[0,"\xB3E"],"a"=>[0,"\xB3F"],"b"=>[0,"\xB3G"],"c"=>[0,"\xB3H"],"d"=>[0,"\xB3I"],"e"=>[0,"\xB3J"],"f"=>[0,"\xB3K"],"g"=>[0,"\xB3L"],"h"=>[0,"\xB3M"],"i"=>[0,"\xB3N"],"j"=>[0,"\xB3O"],"k"=>[0,"\xB3P"],"l"=>[0,"\xB3Q"],"m"=>[0,"\xB3R"],"n"=>[0,"\xB3S"],"o"=>[0,"\xB3T"],"p"=>[0,"\xB3U"],"q"=>[0,"\xB3V"],"r"=>[0,"\xB3W"],"s"=>[0,"\xB3Y"],"t"=>[0,"\xB3j"],"u"=>[0,"\xB3k"],"v"=>[0,"\xB3q"],"w"=>[0,"\xB3v"],"x"=>[0,"\xB3w"],"y"=>[0,"\xB3x"],"z"=>[0,"\xB3y"],"\x7B"=>[0,"\xB3z"],"\x7C"=>[82,"\xB3"],"\x7D"=>[87,"\xB3"],"~"=>[130,"\xB3"],"\x7F"=>[9,"\xB3"],"\x80"=>[94,"\xD10"],"\x81"=>[76,"\xD10"],"\x82"=>[104,"\xD10"],"\x83"=>[16,"\xD10"],"\x84"=>[94,"\xD11"],"\x85"=>[76,"\xD11"],"\x86"=>[104,"\xD11"],"\x87"=>[16,"\xD11"],"\x88"=>[94,"\xD12"],"\x89"=>[76,"\xD12"],"\x8A"=>[104,"\xD12"],"\x8B"=>[16,"\xD12"],"\x8C"=>[94,"\xD1a"],"\x8D"=>[76,"\xD1a"],"\x8E"=>[104,"\xD1a"],"\x8F"=>[16,"\xD1a"],"\x90"=>[94,"\xD1c"],"\x91"=>[76,"\xD1c"],"\x92"=>[104,"\xD1c"],"\x93"=>[16,"\xD1c"],"\x94"=>[94,"\xD1e"],"\x95"=>[76,"\xD1e"],"\x96"=>[104,"\xD1e"],"\x97"=>[16,"\xD1e"],"\x98"=>[94,"\xD1i"],"\x99"=>[76,"\xD1i"],"\x9A"=>[104,"\xD1i"],"\x9B"=>[16,"\xD1i"],"\x9C"=>[94,"\xD1o"],"\x9D"=>[76,"\xD1o"],"\x9E"=>[104,"\xD1o"],"\x9F"=>[16,"\xD1o"],"\xA0"=>[94,"\xD1s"],"\xA1"=>[76,"\xD1s"],"\xA2"=>[104,"\xD1s"],"\xA3"=>[16,"\xD1s"],"\xA4"=>[94,"\xD1t"],"\xA5"=>[76,"\xD1t"],"\xA6"=>[104,"\xD1t"],"\xA7"=>[16,"\xD1t"],"\xA8"=>[77,"\xD1\x20"],"\xA9"=>[18,"\xD1\x20"],"\xAA"=>[77,"\xD1\x25"],"\xAB"=>[18,"\xD1\x25"],"\xAC"=>[77,"\xD1-"],"\xAD"=>[18,"\xD1-"],"\xAE"=>[77,"\xD1."],"\xAF"=>[18,"\xD1."],"\xB0"=>[77,"\xD1\x2F"],"\xB1"=>[18,"\xD1\x2F"],"\xB2"=>[77,"\xD13"],"\xB3"=>[18,"\xD13"],"\xB4"=>[77,"\xD14"],"\xB5"=>[18,"\xD14"],"\xB6"=>[77,"\xD15"],"\xB7"=>[18,"\xD15"],"\xB8"=>[77,"\xD16"],"\xB9"=>[18,"\xD16"],"\xBA"=>[77,"\xD17"],"\xBB"=>[18,"\xD17"],"\xBC"=>[77,"\xD18"],"\xBD"=>[18,"\xD18"],"\xBE"=>[77,"\xD19"],"\xBF"=>[18,"\xD19"],"\xC0"=>[77,"\xD1\x3D"],"\xC1"=>[18,"\xD1\x3D"],"\xC2"=>[77,"\xD1A"],"\xC3"=>[18,"\xD1A"],"\xC4"=>[77,"\xD1_"],"\xC5"=>[18,"\xD1_"],"\xC6"=>[77,"\xD1b"],"\xC7"=>[18,"\xD1b"],"\xC8"=>[77,"\xD1d"],"\xC9"=>[18,"\xD1d"],"\xCA"=>[77,"\xD1f"],"\xCB"=>[18,"\xD1f"],"\xCC"=>[77,"\xD1g"],"\xCD"=>[18,"\xD1g"],"\xCE"=>[77,"\xD1h"],"\xCF"=>[18,"\xD1h"],"\xD0"=>[77,"\xD1l"],"\xD1"=>[18,"\xD1l"],"\xD2"=>[77,"\xD1m"],"\xD3"=>[18,"\xD1m"],"\xD4"=>[77,"\xD1n"],"\xD5"=>[18,"\xD1n"],"\xD6"=>[77,"\xD1p"],"\xD7"=>[18,"\xD1p"],"\xD8"=>[77,"\xD1r"],"\xD9"=>[18,"\xD1r"],"\xDA"=>[77,"\xD1u"],"\xDB"=>[18,"\xD1u"],"\xDC"=>[0,"\xD1\x3A"],"\xDD"=>[0,"\xD1B"],"\xDE"=>[0,"\xD1C"],"\xDF"=>[0,"\xD1D"],"\xE0"=>[0,"\xD1E"],"\xE1"=>[0,"\xD1F"],"\xE2"=>[0,"\xD1G"],"\xE3"=>[0,"\xD1H"],"\xE4"=>[0,"\xD1I"],"\xE5"=>[0,"\xD1J"],"\xE6"=>[0,"\xD1K"],"\xE7"=>[0,"\xD1L"],"\xE8"=>[0,"\xD1M"],"\xE9"=>[0,"\xD1N"],"\xEA"=>[0,"\xD1O"],"\xEB"=>[0,"\xD1P"],"\xEC"=>[0,"\xD1Q"],"\xED"=>[0,"\xD1R"],"\xEE"=>[0,"\xD1S"],"\xEF"=>[0,"\xD1T"],"\xF0"=>[0,"\xD1U"],"\xF1"=>[0,"\xD1V"],"\xF2"=>[0,"\xD1W"],"\xF3"=>[0,"\xD1Y"],"\xF4"=>[0,"\xD1j"],"\xF5"=>[0,"\xD1k"],"\xF6"=>[0,"\xD1q"],"\xF7"=>[0,"\xD1v"],"\xF8"=>[0,"\xD1w"],"\xF9"=>[0,"\xD1x"],"\xFA"=>[0,"\xD1y"],"\xFB"=>[0,"\xD1z"],"\xFC"=>[82,"\xD1"],"\xFD"=>[87,"\xD1"],"\xFE"=>[130,"\xD1"],"\xFF"=>[9,"\xD1"],],["\x00"=>[94,"\xB60"],"\x01"=>[76,"\xB60"],"\x02"=>[104,"\xB60"],"\x03"=>[16,"\xB60"],"\x04"=>[94,"\xB61"],"\x05"=>[76,"\xB61"],"\x06"=>[104,"\xB61"],"\x07"=>[16,"\xB61"],"\x08"=>[94,"\xB62"],"\x09"=>[76,"\xB62"],"\x0A"=>[104,"\xB62"],"\x0B"=>[16,"\xB62"],"\x0C"=>[94,"\xB6a"],"\x0D"=>[76,"\xB6a"],"\x0E"=>[104,"\xB6a"],"\x0F"=>[16,"\xB6a"],"\x10"=>[94,"\xB6c"],"\x11"=>[76,"\xB6c"],"\x12"=>[104,"\xB6c"],"\x13"=>[16,"\xB6c"],"\x14"=>[94,"\xB6e"],"\x15"=>[76,"\xB6e"],"\x16"=>[104,"\xB6e"],"\x17"=>[16,"\xB6e"],"\x18"=>[94,"\xB6i"],"\x19"=>[76,"\xB6i"],"\x1A"=>[104,"\xB6i"],"\x1B"=>[16,"\xB6i"],"\x1C"=>[94,"\xB6o"],"\x1D"=>[76,"\xB6o"],"\x1E"=>[104,"\xB6o"],"\x1F"=>[16,"\xB6o"],"\x20"=>[94,"\xB6s"],"\x21"=>[76,"\xB6s"],"\x22"=>[104,"\xB6s"],"\x23"=>[16,"\xB6s"],"\x24"=>[94,"\xB6t"],"\x25"=>[76,"\xB6t"],"\x26"=>[104,"\xB6t"],"\x27"=>[16,"\xB6t"],"\x28"=>[77,"\xB6\x20"],"\x29"=>[18,"\xB6\x20"],"\x2A"=>[77,"\xB6\x25"],"\x2B"=>[18,"\xB6\x25"],"\x2C"=>[77,"\xB6-"],"-"=>[18,"\xB6-"],"."=>[77,"\xB6."],"\x2F"=>[18,"\xB6."],[77,"\xB6\x2F"],[18,"\xB6\x2F"],[77,"\xB63"],[18,"\xB63"],[77,"\xB64"],[18,"\xB64"],[77,"\xB65"],[18,"\xB65"],[77,"\xB66"],[18,"\xB66"],"\x3A"=>[77,"\xB67"],"\x3B"=>[18,"\xB67"],"\x3C"=>[77,"\xB68"],"\x3D"=>[18,"\xB68"],"\x3E"=>[77,"\xB69"],"\x3F"=>[18,"\xB69"],"\x40"=>[77,"\xB6\x3D"],"A"=>[18,"\xB6\x3D"],"B"=>[77,"\xB6A"],"C"=>[18,"\xB6A"],"D"=>[77,"\xB6_"],"E"=>[18,"\xB6_"],"F"=>[77,"\xB6b"],"G"=>[18,"\xB6b"],"H"=>[77,"\xB6d"],"I"=>[18,"\xB6d"],"J"=>[77,"\xB6f"],"K"=>[18,"\xB6f"],"L"=>[77,"\xB6g"],"M"=>[18,"\xB6g"],"N"=>[77,"\xB6h"],"O"=>[18,"\xB6h"],"P"=>[77,"\xB6l"],"Q"=>[18,"\xB6l"],"R"=>[77,"\xB6m"],"S"=>[18,"\xB6m"],"T"=>[77,"\xB6n"],"U"=>[18,"\xB6n"],"V"=>[77,"\xB6p"],"W"=>[18,"\xB6p"],"X"=>[77,"\xB6r"],"Y"=>[18,"\xB6r"],"Z"=>[77,"\xB6u"],"\x5B"=>[18,"\xB6u"],"\x5C"=>[0,"\xB6\x3A"],"\x5D"=>[0,"\xB6B"],"\x5E"=>[0,"\xB6C"],"_"=>[0,"\xB6D"],"\x60"=>[0,"\xB6E"],"a"=>[0,"\xB6F"],"b"=>[0,"\xB6G"],"c"=>[0,"\xB6H"],"d"=>[0,"\xB6I"],"e"=>[0,"\xB6J"],"f"=>[0,"\xB6K"],"g"=>[0,"\xB6L"],"h"=>[0,"\xB6M"],"i"=>[0,"\xB6N"],"j"=>[0,"\xB6O"],"k"=>[0,"\xB6P"],"l"=>[0,"\xB6Q"],"m"=>[0,"\xB6R"],"n"=>[0,"\xB6S"],"o"=>[0,"\xB6T"],"p"=>[0,"\xB6U"],"q"=>[0,"\xB6V"],"r"=>[0,"\xB6W"],"s"=>[0,"\xB6Y"],"t"=>[0,"\xB6j"],"u"=>[0,"\xB6k"],"v"=>[0,"\xB6q"],"w"=>[0,"\xB6v"],"x"=>[0,"\xB6w"],"y"=>[0,"\xB6x"],"z"=>[0,"\xB6y"],"\x7B"=>[0,"\xB6z"],"\x7C"=>[82,"\xB6"],"\x7D"=>[87,"\xB6"],"~"=>[130,"\xB6"],"\x7F"=>[9,"\xB6"],"\x80"=>[94,"\xB70"],"\x81"=>[76,"\xB70"],"\x82"=>[104,"\xB70"],"\x83"=>[16,"\xB70"],"\x84"=>[94,"\xB71"],"\x85"=>[76,"\xB71"],"\x86"=>[104,"\xB71"],"\x87"=>[16,"\xB71"],"\x88"=>[94,"\xB72"],"\x89"=>[76,"\xB72"],"\x8A"=>[104,"\xB72"],"\x8B"=>[16,"\xB72"],"\x8C"=>[94,"\xB7a"],"\x8D"=>[76,"\xB7a"],"\x8E"=>[104,"\xB7a"],"\x8F"=>[16,"\xB7a"],"\x90"=>[94,"\xB7c"],"\x91"=>[76,"\xB7c"],"\x92"=>[104,"\xB7c"],"\x93"=>[16,"\xB7c"],"\x94"=>[94,"\xB7e"],"\x95"=>[76,"\xB7e"],"\x96"=>[104,"\xB7e"],"\x97"=>[16,"\xB7e"],"\x98"=>[94,"\xB7i"],"\x99"=>[76,"\xB7i"],"\x9A"=>[104,"\xB7i"],"\x9B"=>[16,"\xB7i"],"\x9C"=>[94,"\xB7o"],"\x9D"=>[76,"\xB7o"],"\x9E"=>[104,"\xB7o"],"\x9F"=>[16,"\xB7o"],"\xA0"=>[94,"\xB7s"],"\xA1"=>[76,"\xB7s"],"\xA2"=>[104,"\xB7s"],"\xA3"=>[16,"\xB7s"],"\xA4"=>[94,"\xB7t"],"\xA5"=>[76,"\xB7t"],"\xA6"=>[104,"\xB7t"],"\xA7"=>[16,"\xB7t"],"\xA8"=>[77,"\xB7\x20"],"\xA9"=>[18,"\xB7\x20"],"\xAA"=>[77,"\xB7\x25"],"\xAB"=>[18,"\xB7\x25"],"\xAC"=>[77,"\xB7-"],"\xAD"=>[18,"\xB7-"],"\xAE"=>[77,"\xB7."],"\xAF"=>[18,"\xB7."],"\xB0"=>[77,"\xB7\x2F"],"\xB1"=>[18,"\xB7\x2F"],"\xB2"=>[77,"\xB73"],"\xB3"=>[18,"\xB73"],"\xB4"=>[77,"\xB74"],"\xB5"=>[18,"\xB74"],"\xB6"=>[77,"\xB75"],"\xB7"=>[18,"\xB75"],"\xB8"=>[77,"\xB76"],"\xB9"=>[18,"\xB76"],"\xBA"=>[77,"\xB77"],"\xBB"=>[18,"\xB77"],"\xBC"=>[77,"\xB78"],"\xBD"=>[18,"\xB78"],"\xBE"=>[77,"\xB79"],"\xBF"=>[18,"\xB79"],"\xC0"=>[77,"\xB7\x3D"],"\xC1"=>[18,"\xB7\x3D"],"\xC2"=>[77,"\xB7A"],"\xC3"=>[18,"\xB7A"],"\xC4"=>[77,"\xB7_"],"\xC5"=>[18,"\xB7_"],"\xC6"=>[77,"\xB7b"],"\xC7"=>[18,"\xB7b"],"\xC8"=>[77,"\xB7d"],"\xC9"=>[18,"\xB7d"],"\xCA"=>[77,"\xB7f"],"\xCB"=>[18,"\xB7f"],"\xCC"=>[77,"\xB7g"],"\xCD"=>[18,"\xB7g"],"\xCE"=>[77,"\xB7h"],"\xCF"=>[18,"\xB7h"],"\xD0"=>[77,"\xB7l"],"\xD1"=>[18,"\xB7l"],"\xD2"=>[77,"\xB7m"],"\xD3"=>[18,"\xB7m"],"\xD4"=>[77,"\xB7n"],"\xD5"=>[18,"\xB7n"],"\xD6"=>[77,"\xB7p"],"\xD7"=>[18,"\xB7p"],"\xD8"=>[77,"\xB7r"],"\xD9"=>[18,"\xB7r"],"\xDA"=>[77,"\xB7u"],"\xDB"=>[18,"\xB7u"],"\xDC"=>[0,"\xB7\x3A"],"\xDD"=>[0,"\xB7B"],"\xDE"=>[0,"\xB7C"],"\xDF"=>[0,"\xB7D"],"\xE0"=>[0,"\xB7E"],"\xE1"=>[0,"\xB7F"],"\xE2"=>[0,"\xB7G"],"\xE3"=>[0,"\xB7H"],"\xE4"=>[0,"\xB7I"],"\xE5"=>[0,"\xB7J"],"\xE6"=>[0,"\xB7K"],"\xE7"=>[0,"\xB7L"],"\xE8"=>[0,"\xB7M"],"\xE9"=>[0,"\xB7N"],"\xEA"=>[0,"\xB7O"],"\xEB"=>[0,"\xB7P"],"\xEC"=>[0,"\xB7Q"],"\xED"=>[0,"\xB7R"],"\xEE"=>[0,"\xB7S"],"\xEF"=>[0,"\xB7T"],"\xF0"=>[0,"\xB7U"],"\xF1"=>[0,"\xB7V"],"\xF2"=>[0,"\xB7W"],"\xF3"=>[0,"\xB7Y"],"\xF4"=>[0,"\xB7j"],"\xF5"=>[0,"\xB7k"],"\xF6"=>[0,"\xB7q"],"\xF7"=>[0,"\xB7v"],"\xF8"=>[0,"\xB7w"],"\xF9"=>[0,"\xB7x"],"\xFA"=>[0,"\xB7y"],"\xFB"=>[0,"\xB7z"],"\xFC"=>[82,"\xB7"],"\xFD"=>[87,"\xB7"],"\xFE"=>[130,"\xB7"],"\xFF"=>[9,"\xB7"],],["\x00"=>[94,"\xB80"],"\x01"=>[76,"\xB80"],"\x02"=>[104,"\xB80"],"\x03"=>[16,"\xB80"],"\x04"=>[94,"\xB81"],"\x05"=>[76,"\xB81"],"\x06"=>[104,"\xB81"],"\x07"=>[16,"\xB81"],"\x08"=>[94,"\xB82"],"\x09"=>[76,"\xB82"],"\x0A"=>[104,"\xB82"],"\x0B"=>[16,"\xB82"],"\x0C"=>[94,"\xB8a"],"\x0D"=>[76,"\xB8a"],"\x0E"=>[104,"\xB8a"],"\x0F"=>[16,"\xB8a"],"\x10"=>[94,"\xB8c"],"\x11"=>[76,"\xB8c"],"\x12"=>[104,"\xB8c"],"\x13"=>[16,"\xB8c"],"\x14"=>[94,"\xB8e"],"\x15"=>[76,"\xB8e"],"\x16"=>[104,"\xB8e"],"\x17"=>[16,"\xB8e"],"\x18"=>[94,"\xB8i"],"\x19"=>[76,"\xB8i"],"\x1A"=>[104,"\xB8i"],"\x1B"=>[16,"\xB8i"],"\x1C"=>[94,"\xB8o"],"\x1D"=>[76,"\xB8o"],"\x1E"=>[104,"\xB8o"],"\x1F"=>[16,"\xB8o"],"\x20"=>[94,"\xB8s"],"\x21"=>[76,"\xB8s"],"\x22"=>[104,"\xB8s"],"\x23"=>[16,"\xB8s"],"\x24"=>[94,"\xB8t"],"\x25"=>[76,"\xB8t"],"\x26"=>[104,"\xB8t"],"\x27"=>[16,"\xB8t"],"\x28"=>[77,"\xB8\x20"],"\x29"=>[18,"\xB8\x20"],"\x2A"=>[77,"\xB8\x25"],"\x2B"=>[18,"\xB8\x25"],"\x2C"=>[77,"\xB8-"],"-"=>[18,"\xB8-"],"."=>[77,"\xB8."],"\x2F"=>[18,"\xB8."],[77,"\xB8\x2F"],[18,"\xB8\x2F"],[77,"\xB83"],[18,"\xB83"],[77,"\xB84"],[18,"\xB84"],[77,"\xB85"],[18,"\xB85"],[77,"\xB86"],[18,"\xB86"],"\x3A"=>[77,"\xB87"],"\x3B"=>[18,"\xB87"],"\x3C"=>[77,"\xB88"],"\x3D"=>[18,"\xB88"],"\x3E"=>[77,"\xB89"],"\x3F"=>[18,"\xB89"],"\x40"=>[77,"\xB8\x3D"],"A"=>[18,"\xB8\x3D"],"B"=>[77,"\xB8A"],"C"=>[18,"\xB8A"],"D"=>[77,"\xB8_"],"E"=>[18,"\xB8_"],"F"=>[77,"\xB8b"],"G"=>[18,"\xB8b"],"H"=>[77,"\xB8d"],"I"=>[18,"\xB8d"],"J"=>[77,"\xB8f"],"K"=>[18,"\xB8f"],"L"=>[77,"\xB8g"],"M"=>[18,"\xB8g"],"N"=>[77,"\xB8h"],"O"=>[18,"\xB8h"],"P"=>[77,"\xB8l"],"Q"=>[18,"\xB8l"],"R"=>[77,"\xB8m"],"S"=>[18,"\xB8m"],"T"=>[77,"\xB8n"],"U"=>[18,"\xB8n"],"V"=>[77,"\xB8p"],"W"=>[18,"\xB8p"],"X"=>[77,"\xB8r"],"Y"=>[18,"\xB8r"],"Z"=>[77,"\xB8u"],"\x5B"=>[18,"\xB8u"],"\x5C"=>[0,"\xB8\x3A"],"\x5D"=>[0,"\xB8B"],"\x5E"=>[0,"\xB8C"],"_"=>[0,"\xB8D"],"\x60"=>[0,"\xB8E"],"a"=>[0,"\xB8F"],"b"=>[0,"\xB8G"],"c"=>[0,"\xB8H"],"d"=>[0,"\xB8I"],"e"=>[0,"\xB8J"],"f"=>[0,"\xB8K"],"g"=>[0,"\xB8L"],"h"=>[0,"\xB8M"],"i"=>[0,"\xB8N"],"j"=>[0,"\xB8O"],"k"=>[0,"\xB8P"],"l"=>[0,"\xB8Q"],"m"=>[0,"\xB8R"],"n"=>[0,"\xB8S"],"o"=>[0,"\xB8T"],"p"=>[0,"\xB8U"],"q"=>[0,"\xB8V"],"r"=>[0,"\xB8W"],"s"=>[0,"\xB8Y"],"t"=>[0,"\xB8j"],"u"=>[0,"\xB8k"],"v"=>[0,"\xB8q"],"w"=>[0,"\xB8v"],"x"=>[0,"\xB8w"],"y"=>[0,"\xB8x"],"z"=>[0,"\xB8y"],"\x7B"=>[0,"\xB8z"],"\x7C"=>[82,"\xB8"],"\x7D"=>[87,"\xB8"],"~"=>[130,"\xB8"],"\x7F"=>[9,"\xB8"],"\x80"=>[94,"\xC20"],"\x81"=>[76,"\xC20"],"\x82"=>[104,"\xC20"],"\x83"=>[16,"\xC20"],"\x84"=>[94,"\xC21"],"\x85"=>[76,"\xC21"],"\x86"=>[104,"\xC21"],"\x87"=>[16,"\xC21"],"\x88"=>[94,"\xC22"],"\x89"=>[76,"\xC22"],"\x8A"=>[104,"\xC22"],"\x8B"=>[16,"\xC22"],"\x8C"=>[94,"\xC2a"],"\x8D"=>[76,"\xC2a"],"\x8E"=>[104,"\xC2a"],"\x8F"=>[16,"\xC2a"],"\x90"=>[94,"\xC2c"],"\x91"=>[76,"\xC2c"],"\x92"=>[104,"\xC2c"],"\x93"=>[16,"\xC2c"],"\x94"=>[94,"\xC2e"],"\x95"=>[76,"\xC2e"],"\x96"=>[104,"\xC2e"],"\x97"=>[16,"\xC2e"],"\x98"=>[94,"\xC2i"],"\x99"=>[76,"\xC2i"],"\x9A"=>[104,"\xC2i"],"\x9B"=>[16,"\xC2i"],"\x9C"=>[94,"\xC2o"],"\x9D"=>[76,"\xC2o"],"\x9E"=>[104,"\xC2o"],"\x9F"=>[16,"\xC2o"],"\xA0"=>[94,"\xC2s"],"\xA1"=>[76,"\xC2s"],"\xA2"=>[104,"\xC2s"],"\xA3"=>[16,"\xC2s"],"\xA4"=>[94,"\xC2t"],"\xA5"=>[76,"\xC2t"],"\xA6"=>[104,"\xC2t"],"\xA7"=>[16,"\xC2t"],"\xA8"=>[77,"\xC2\x20"],"\xA9"=>[18,"\xC2\x20"],"\xAA"=>[77,"\xC2\x25"],"\xAB"=>[18,"\xC2\x25"],"\xAC"=>[77,"\xC2-"],"\xAD"=>[18,"\xC2-"],"\xAE"=>[77,"\xC2."],"\xAF"=>[18,"\xC2."],"\xB0"=>[77,"\xC2\x2F"],"\xB1"=>[18,"\xC2\x2F"],"\xB2"=>[77,"\xC23"],"\xB3"=>[18,"\xC23"],"\xB4"=>[77,"\xC24"],"\xB5"=>[18,"\xC24"],"\xB6"=>[77,"\xC25"],"\xB7"=>[18,"\xC25"],"\xB8"=>[77,"\xC26"],"\xB9"=>[18,"\xC26"],"\xBA"=>[77,"\xC27"],"\xBB"=>[18,"\xC27"],"\xBC"=>[77,"\xC28"],"\xBD"=>[18,"\xC28"],"\xBE"=>[77,"\xC29"],"\xBF"=>[18,"\xC29"],"\xC0"=>[77,"\xC2\x3D"],"\xC1"=>[18,"\xC2\x3D"],"\xC2"=>[77,"\xC2A"],"\xC3"=>[18,"\xC2A"],"\xC4"=>[77,"\xC2_"],"\xC5"=>[18,"\xC2_"],"\xC6"=>[77,"\xC2b"],"\xC7"=>[18,"\xC2b"],"\xC8"=>[77,"\xC2d"],"\xC9"=>[18,"\xC2d"],"\xCA"=>[77,"\xC2f"],"\xCB"=>[18,"\xC2f"],"\xCC"=>[77,"\xC2g"],"\xCD"=>[18,"\xC2g"],"\xCE"=>[77,"\xC2h"],"\xCF"=>[18,"\xC2h"],"\xD0"=>[77,"\xC2l"],"\xD1"=>[18,"\xC2l"],"\xD2"=>[77,"\xC2m"],"\xD3"=>[18,"\xC2m"],"\xD4"=>[77,"\xC2n"],"\xD5"=>[18,"\xC2n"],"\xD6"=>[77,"\xC2p"],"\xD7"=>[18,"\xC2p"],"\xD8"=>[77,"\xC2r"],"\xD9"=>[18,"\xC2r"],"\xDA"=>[77,"\xC2u"],"\xDB"=>[18,"\xC2u"],"\xDC"=>[0,"\xC2\x3A"],"\xDD"=>[0,"\xC2B"],"\xDE"=>[0,"\xC2C"],"\xDF"=>[0,"\xC2D"],"\xE0"=>[0,"\xC2E"],"\xE1"=>[0,"\xC2F"],"\xE2"=>[0,"\xC2G"],"\xE3"=>[0,"\xC2H"],"\xE4"=>[0,"\xC2I"],"\xE5"=>[0,"\xC2J"],"\xE6"=>[0,"\xC2K"],"\xE7"=>[0,"\xC2L"],"\xE8"=>[0,"\xC2M"],"\xE9"=>[0,"\xC2N"],"\xEA"=>[0,"\xC2O"],"\xEB"=>[0,"\xC2P"],"\xEC"=>[0,"\xC2Q"],"\xED"=>[0,"\xC2R"],"\xEE"=>[0,"\xC2S"],"\xEF"=>[0,"\xC2T"],"\xF0"=>[0,"\xC2U"],"\xF1"=>[0,"\xC2V"],"\xF2"=>[0,"\xC2W"],"\xF3"=>[0,"\xC2Y"],"\xF4"=>[0,"\xC2j"],"\xF5"=>[0,"\xC2k"],"\xF6"=>[0,"\xC2q"],"\xF7"=>[0,"\xC2v"],"\xF8"=>[0,"\xC2w"],"\xF9"=>[0,"\xC2x"],"\xFA"=>[0,"\xC2y"],"\xFB"=>[0,"\xC2z"],"\xFC"=>[82,"\xC2"],"\xFD"=>[87,"\xC2"],"\xFE"=>[130,"\xC2"],"\xFF"=>[9,"\xC2"],],["\x00"=>[94,"\xB90"],"\x01"=>[76,"\xB90"],"\x02"=>[104,"\xB90"],"\x03"=>[16,"\xB90"],"\x04"=>[94,"\xB91"],"\x05"=>[76,"\xB91"],"\x06"=>[104,"\xB91"],"\x07"=>[16,"\xB91"],"\x08"=>[94,"\xB92"],"\x09"=>[76,"\xB92"],"\x0A"=>[104,"\xB92"],"\x0B"=>[16,"\xB92"],"\x0C"=>[94,"\xB9a"],"\x0D"=>[76,"\xB9a"],"\x0E"=>[104,"\xB9a"],"\x0F"=>[16,"\xB9a"],"\x10"=>[94,"\xB9c"],"\x11"=>[76,"\xB9c"],"\x12"=>[104,"\xB9c"],"\x13"=>[16,"\xB9c"],"\x14"=>[94,"\xB9e"],"\x15"=>[76,"\xB9e"],"\x16"=>[104,"\xB9e"],"\x17"=>[16,"\xB9e"],"\x18"=>[94,"\xB9i"],"\x19"=>[76,"\xB9i"],"\x1A"=>[104,"\xB9i"],"\x1B"=>[16,"\xB9i"],"\x1C"=>[94,"\xB9o"],"\x1D"=>[76,"\xB9o"],"\x1E"=>[104,"\xB9o"],"\x1F"=>[16,"\xB9o"],"\x20"=>[94,"\xB9s"],"\x21"=>[76,"\xB9s"],"\x22"=>[104,"\xB9s"],"\x23"=>[16,"\xB9s"],"\x24"=>[94,"\xB9t"],"\x25"=>[76,"\xB9t"],"\x26"=>[104,"\xB9t"],"\x27"=>[16,"\xB9t"],"\x28"=>[77,"\xB9\x20"],"\x29"=>[18,"\xB9\x20"],"\x2A"=>[77,"\xB9\x25"],"\x2B"=>[18,"\xB9\x25"],"\x2C"=>[77,"\xB9-"],"-"=>[18,"\xB9-"],"."=>[77,"\xB9."],"\x2F"=>[18,"\xB9."],[77,"\xB9\x2F"],[18,"\xB9\x2F"],[77,"\xB93"],[18,"\xB93"],[77,"\xB94"],[18,"\xB94"],[77,"\xB95"],[18,"\xB95"],[77,"\xB96"],[18,"\xB96"],"\x3A"=>[77,"\xB97"],"\x3B"=>[18,"\xB97"],"\x3C"=>[77,"\xB98"],"\x3D"=>[18,"\xB98"],"\x3E"=>[77,"\xB99"],"\x3F"=>[18,"\xB99"],"\x40"=>[77,"\xB9\x3D"],"A"=>[18,"\xB9\x3D"],"B"=>[77,"\xB9A"],"C"=>[18,"\xB9A"],"D"=>[77,"\xB9_"],"E"=>[18,"\xB9_"],"F"=>[77,"\xB9b"],"G"=>[18,"\xB9b"],"H"=>[77,"\xB9d"],"I"=>[18,"\xB9d"],"J"=>[77,"\xB9f"],"K"=>[18,"\xB9f"],"L"=>[77,"\xB9g"],"M"=>[18,"\xB9g"],"N"=>[77,"\xB9h"],"O"=>[18,"\xB9h"],"P"=>[77,"\xB9l"],"Q"=>[18,"\xB9l"],"R"=>[77,"\xB9m"],"S"=>[18,"\xB9m"],"T"=>[77,"\xB9n"],"U"=>[18,"\xB9n"],"V"=>[77,"\xB9p"],"W"=>[18,"\xB9p"],"X"=>[77,"\xB9r"],"Y"=>[18,"\xB9r"],"Z"=>[77,"\xB9u"],"\x5B"=>[18,"\xB9u"],"\x5C"=>[0,"\xB9\x3A"],"\x5D"=>[0,"\xB9B"],"\x5E"=>[0,"\xB9C"],"_"=>[0,"\xB9D"],"\x60"=>[0,"\xB9E"],"a"=>[0,"\xB9F"],"b"=>[0,"\xB9G"],"c"=>[0,"\xB9H"],"d"=>[0,"\xB9I"],"e"=>[0,"\xB9J"],"f"=>[0,"\xB9K"],"g"=>[0,"\xB9L"],"h"=>[0,"\xB9M"],"i"=>[0,"\xB9N"],"j"=>[0,"\xB9O"],"k"=>[0,"\xB9P"],"l"=>[0,"\xB9Q"],"m"=>[0,"\xB9R"],"n"=>[0,"\xB9S"],"o"=>[0,"\xB9T"],"p"=>[0,"\xB9U"],"q"=>[0,"\xB9V"],"r"=>[0,"\xB9W"],"s"=>[0,"\xB9Y"],"t"=>[0,"\xB9j"],"u"=>[0,"\xB9k"],"v"=>[0,"\xB9q"],"w"=>[0,"\xB9v"],"x"=>[0,"\xB9w"],"y"=>[0,"\xB9x"],"z"=>[0,"\xB9y"],"\x7B"=>[0,"\xB9z"],"\x7C"=>[82,"\xB9"],"\x7D"=>[87,"\xB9"],"~"=>[130,"\xB9"],"\x7F"=>[9,"\xB9"],"\x80"=>[94,"\xBA0"],"\x81"=>[76,"\xBA0"],"\x82"=>[104,"\xBA0"],"\x83"=>[16,"\xBA0"],"\x84"=>[94,"\xBA1"],"\x85"=>[76,"\xBA1"],"\x86"=>[104,"\xBA1"],"\x87"=>[16,"\xBA1"],"\x88"=>[94,"\xBA2"],"\x89"=>[76,"\xBA2"],"\x8A"=>[104,"\xBA2"],"\x8B"=>[16,"\xBA2"],"\x8C"=>[94,"\xBAa"],"\x8D"=>[76,"\xBAa"],"\x8E"=>[104,"\xBAa"],"\x8F"=>[16,"\xBAa"],"\x90"=>[94,"\xBAc"],"\x91"=>[76,"\xBAc"],"\x92"=>[104,"\xBAc"],"\x93"=>[16,"\xBAc"],"\x94"=>[94,"\xBAe"],"\x95"=>[76,"\xBAe"],"\x96"=>[104,"\xBAe"],"\x97"=>[16,"\xBAe"],"\x98"=>[94,"\xBAi"],"\x99"=>[76,"\xBAi"],"\x9A"=>[104,"\xBAi"],"\x9B"=>[16,"\xBAi"],"\x9C"=>[94,"\xBAo"],"\x9D"=>[76,"\xBAo"],"\x9E"=>[104,"\xBAo"],"\x9F"=>[16,"\xBAo"],"\xA0"=>[94,"\xBAs"],"\xA1"=>[76,"\xBAs"],"\xA2"=>[104,"\xBAs"],"\xA3"=>[16,"\xBAs"],"\xA4"=>[94,"\xBAt"],"\xA5"=>[76,"\xBAt"],"\xA6"=>[104,"\xBAt"],"\xA7"=>[16,"\xBAt"],"\xA8"=>[77,"\xBA\x20"],"\xA9"=>[18,"\xBA\x20"],"\xAA"=>[77,"\xBA\x25"],"\xAB"=>[18,"\xBA\x25"],"\xAC"=>[77,"\xBA-"],"\xAD"=>[18,"\xBA-"],"\xAE"=>[77,"\xBA."],"\xAF"=>[18,"\xBA."],"\xB0"=>[77,"\xBA\x2F"],"\xB1"=>[18,"\xBA\x2F"],"\xB2"=>[77,"\xBA3"],"\xB3"=>[18,"\xBA3"],"\xB4"=>[77,"\xBA4"],"\xB5"=>[18,"\xBA4"],"\xB6"=>[77,"\xBA5"],"\xB7"=>[18,"\xBA5"],"\xB8"=>[77,"\xBA6"],"\xB9"=>[18,"\xBA6"],"\xBA"=>[77,"\xBA7"],"\xBB"=>[18,"\xBA7"],"\xBC"=>[77,"\xBA8"],"\xBD"=>[18,"\xBA8"],"\xBE"=>[77,"\xBA9"],"\xBF"=>[18,"\xBA9"],"\xC0"=>[77,"\xBA\x3D"],"\xC1"=>[18,"\xBA\x3D"],"\xC2"=>[77,"\xBAA"],"\xC3"=>[18,"\xBAA"],"\xC4"=>[77,"\xBA_"],"\xC5"=>[18,"\xBA_"],"\xC6"=>[77,"\xBAb"],"\xC7"=>[18,"\xBAb"],"\xC8"=>[77,"\xBAd"],"\xC9"=>[18,"\xBAd"],"\xCA"=>[77,"\xBAf"],"\xCB"=>[18,"\xBAf"],"\xCC"=>[77,"\xBAg"],"\xCD"=>[18,"\xBAg"],"\xCE"=>[77,"\xBAh"],"\xCF"=>[18,"\xBAh"],"\xD0"=>[77,"\xBAl"],"\xD1"=>[18,"\xBAl"],"\xD2"=>[77,"\xBAm"],"\xD3"=>[18,"\xBAm"],"\xD4"=>[77,"\xBAn"],"\xD5"=>[18,"\xBAn"],"\xD6"=>[77,"\xBAp"],"\xD7"=>[18,"\xBAp"],"\xD8"=>[77,"\xBAr"],"\xD9"=>[18,"\xBAr"],"\xDA"=>[77,"\xBAu"],"\xDB"=>[18,"\xBAu"],"\xDC"=>[0,"\xBA\x3A"],"\xDD"=>[0,"\xBAB"],"\xDE"=>[0,"\xBAC"],"\xDF"=>[0,"\xBAD"],"\xE0"=>[0,"\xBAE"],"\xE1"=>[0,"\xBAF"],"\xE2"=>[0,"\xBAG"],"\xE3"=>[0,"\xBAH"],"\xE4"=>[0,"\xBAI"],"\xE5"=>[0,"\xBAJ"],"\xE6"=>[0,"\xBAK"],"\xE7"=>[0,"\xBAL"],"\xE8"=>[0,"\xBAM"],"\xE9"=>[0,"\xBAN"],"\xEA"=>[0,"\xBAO"],"\xEB"=>[0,"\xBAP"],"\xEC"=>[0,"\xBAQ"],"\xED"=>[0,"\xBAR"],"\xEE"=>[0,"\xBAS"],"\xEF"=>[0,"\xBAT"],"\xF0"=>[0,"\xBAU"],"\xF1"=>[0,"\xBAV"],"\xF2"=>[0,"\xBAW"],"\xF3"=>[0,"\xBAY"],"\xF4"=>[0,"\xBAj"],"\xF5"=>[0,"\xBAk"],"\xF6"=>[0,"\xBAq"],"\xF7"=>[0,"\xBAv"],"\xF8"=>[0,"\xBAw"],"\xF9"=>[0,"\xBAx"],"\xFA"=>[0,"\xBAy"],"\xFB"=>[0,"\xBAz"],"\xFC"=>[82,"\xBA"],"\xFD"=>[87,"\xBA"],"\xFE"=>[130,"\xBA"],"\xFF"=>[9,"\xBA"],],["\x00"=>[94,"\xBB0"],"\x01"=>[76,"\xBB0"],"\x02"=>[104,"\xBB0"],"\x03"=>[16,"\xBB0"],"\x04"=>[94,"\xBB1"],"\x05"=>[76,"\xBB1"],"\x06"=>[104,"\xBB1"],"\x07"=>[16,"\xBB1"],"\x08"=>[94,"\xBB2"],"\x09"=>[76,"\xBB2"],"\x0A"=>[104,"\xBB2"],"\x0B"=>[16,"\xBB2"],"\x0C"=>[94,"\xBBa"],"\x0D"=>[76,"\xBBa"],"\x0E"=>[104,"\xBBa"],"\x0F"=>[16,"\xBBa"],"\x10"=>[94,"\xBBc"],"\x11"=>[76,"\xBBc"],"\x12"=>[104,"\xBBc"],"\x13"=>[16,"\xBBc"],"\x14"=>[94,"\xBBe"],"\x15"=>[76,"\xBBe"],"\x16"=>[104,"\xBBe"],"\x17"=>[16,"\xBBe"],"\x18"=>[94,"\xBBi"],"\x19"=>[76,"\xBBi"],"\x1A"=>[104,"\xBBi"],"\x1B"=>[16,"\xBBi"],"\x1C"=>[94,"\xBBo"],"\x1D"=>[76,"\xBBo"],"\x1E"=>[104,"\xBBo"],"\x1F"=>[16,"\xBBo"],"\x20"=>[94,"\xBBs"],"\x21"=>[76,"\xBBs"],"\x22"=>[104,"\xBBs"],"\x23"=>[16,"\xBBs"],"\x24"=>[94,"\xBBt"],"\x25"=>[76,"\xBBt"],"\x26"=>[104,"\xBBt"],"\x27"=>[16,"\xBBt"],"\x28"=>[77,"\xBB\x20"],"\x29"=>[18,"\xBB\x20"],"\x2A"=>[77,"\xBB\x25"],"\x2B"=>[18,"\xBB\x25"],"\x2C"=>[77,"\xBB-"],"-"=>[18,"\xBB-"],"."=>[77,"\xBB."],"\x2F"=>[18,"\xBB."],[77,"\xBB\x2F"],[18,"\xBB\x2F"],[77,"\xBB3"],[18,"\xBB3"],[77,"\xBB4"],[18,"\xBB4"],[77,"\xBB5"],[18,"\xBB5"],[77,"\xBB6"],[18,"\xBB6"],"\x3A"=>[77,"\xBB7"],"\x3B"=>[18,"\xBB7"],"\x3C"=>[77,"\xBB8"],"\x3D"=>[18,"\xBB8"],"\x3E"=>[77,"\xBB9"],"\x3F"=>[18,"\xBB9"],"\x40"=>[77,"\xBB\x3D"],"A"=>[18,"\xBB\x3D"],"B"=>[77,"\xBBA"],"C"=>[18,"\xBBA"],"D"=>[77,"\xBB_"],"E"=>[18,"\xBB_"],"F"=>[77,"\xBBb"],"G"=>[18,"\xBBb"],"H"=>[77,"\xBBd"],"I"=>[18,"\xBBd"],"J"=>[77,"\xBBf"],"K"=>[18,"\xBBf"],"L"=>[77,"\xBBg"],"M"=>[18,"\xBBg"],"N"=>[77,"\xBBh"],"O"=>[18,"\xBBh"],"P"=>[77,"\xBBl"],"Q"=>[18,"\xBBl"],"R"=>[77,"\xBBm"],"S"=>[18,"\xBBm"],"T"=>[77,"\xBBn"],"U"=>[18,"\xBBn"],"V"=>[77,"\xBBp"],"W"=>[18,"\xBBp"],"X"=>[77,"\xBBr"],"Y"=>[18,"\xBBr"],"Z"=>[77,"\xBBu"],"\x5B"=>[18,"\xBBu"],"\x5C"=>[0,"\xBB\x3A"],"\x5D"=>[0,"\xBBB"],"\x5E"=>[0,"\xBBC"],"_"=>[0,"\xBBD"],"\x60"=>[0,"\xBBE"],"a"=>[0,"\xBBF"],"b"=>[0,"\xBBG"],"c"=>[0,"\xBBH"],"d"=>[0,"\xBBI"],"e"=>[0,"\xBBJ"],"f"=>[0,"\xBBK"],"g"=>[0,"\xBBL"],"h"=>[0,"\xBBM"],"i"=>[0,"\xBBN"],"j"=>[0,"\xBBO"],"k"=>[0,"\xBBP"],"l"=>[0,"\xBBQ"],"m"=>[0,"\xBBR"],"n"=>[0,"\xBBS"],"o"=>[0,"\xBBT"],"p"=>[0,"\xBBU"],"q"=>[0,"\xBBV"],"r"=>[0,"\xBBW"],"s"=>[0,"\xBBY"],"t"=>[0,"\xBBj"],"u"=>[0,"\xBBk"],"v"=>[0,"\xBBq"],"w"=>[0,"\xBBv"],"x"=>[0,"\xBBw"],"y"=>[0,"\xBBx"],"z"=>[0,"\xBBy"],"\x7B"=>[0,"\xBBz"],"\x7C"=>[82,"\xBB"],"\x7D"=>[87,"\xBB"],"~"=>[130,"\xBB"],"\x7F"=>[9,"\xBB"],"\x80"=>[94,"\xBD0"],"\x81"=>[76,"\xBD0"],"\x82"=>[104,"\xBD0"],"\x83"=>[16,"\xBD0"],"\x84"=>[94,"\xBD1"],"\x85"=>[76,"\xBD1"],"\x86"=>[104,"\xBD1"],"\x87"=>[16,"\xBD1"],"\x88"=>[94,"\xBD2"],"\x89"=>[76,"\xBD2"],"\x8A"=>[104,"\xBD2"],"\x8B"=>[16,"\xBD2"],"\x8C"=>[94,"\xBDa"],"\x8D"=>[76,"\xBDa"],"\x8E"=>[104,"\xBDa"],"\x8F"=>[16,"\xBDa"],"\x90"=>[94,"\xBDc"],"\x91"=>[76,"\xBDc"],"\x92"=>[104,"\xBDc"],"\x93"=>[16,"\xBDc"],"\x94"=>[94,"\xBDe"],"\x95"=>[76,"\xBDe"],"\x96"=>[104,"\xBDe"],"\x97"=>[16,"\xBDe"],"\x98"=>[94,"\xBDi"],"\x99"=>[76,"\xBDi"],"\x9A"=>[104,"\xBDi"],"\x9B"=>[16,"\xBDi"],"\x9C"=>[94,"\xBDo"],"\x9D"=>[76,"\xBDo"],"\x9E"=>[104,"\xBDo"],"\x9F"=>[16,"\xBDo"],"\xA0"=>[94,"\xBDs"],"\xA1"=>[76,"\xBDs"],"\xA2"=>[104,"\xBDs"],"\xA3"=>[16,"\xBDs"],"\xA4"=>[94,"\xBDt"],"\xA5"=>[76,"\xBDt"],"\xA6"=>[104,"\xBDt"],"\xA7"=>[16,"\xBDt"],"\xA8"=>[77,"\xBD\x20"],"\xA9"=>[18,"\xBD\x20"],"\xAA"=>[77,"\xBD\x25"],"\xAB"=>[18,"\xBD\x25"],"\xAC"=>[77,"\xBD-"],"\xAD"=>[18,"\xBD-"],"\xAE"=>[77,"\xBD."],"\xAF"=>[18,"\xBD."],"\xB0"=>[77,"\xBD\x2F"],"\xB1"=>[18,"\xBD\x2F"],"\xB2"=>[77,"\xBD3"],"\xB3"=>[18,"\xBD3"],"\xB4"=>[77,"\xBD4"],"\xB5"=>[18,"\xBD4"],"\xB6"=>[77,"\xBD5"],"\xB7"=>[18,"\xBD5"],"\xB8"=>[77,"\xBD6"],"\xB9"=>[18,"\xBD6"],"\xBA"=>[77,"\xBD7"],"\xBB"=>[18,"\xBD7"],"\xBC"=>[77,"\xBD8"],"\xBD"=>[18,"\xBD8"],"\xBE"=>[77,"\xBD9"],"\xBF"=>[18,"\xBD9"],"\xC0"=>[77,"\xBD\x3D"],"\xC1"=>[18,"\xBD\x3D"],"\xC2"=>[77,"\xBDA"],"\xC3"=>[18,"\xBDA"],"\xC4"=>[77,"\xBD_"],"\xC5"=>[18,"\xBD_"],"\xC6"=>[77,"\xBDb"],"\xC7"=>[18,"\xBDb"],"\xC8"=>[77,"\xBDd"],"\xC9"=>[18,"\xBDd"],"\xCA"=>[77,"\xBDf"],"\xCB"=>[18,"\xBDf"],"\xCC"=>[77,"\xBDg"],"\xCD"=>[18,"\xBDg"],"\xCE"=>[77,"\xBDh"],"\xCF"=>[18,"\xBDh"],"\xD0"=>[77,"\xBDl"],"\xD1"=>[18,"\xBDl"],"\xD2"=>[77,"\xBDm"],"\xD3"=>[18,"\xBDm"],"\xD4"=>[77,"\xBDn"],"\xD5"=>[18,"\xBDn"],"\xD6"=>[77,"\xBDp"],"\xD7"=>[18,"\xBDp"],"\xD8"=>[77,"\xBDr"],"\xD9"=>[18,"\xBDr"],"\xDA"=>[77,"\xBDu"],"\xDB"=>[18,"\xBDu"],"\xDC"=>[0,"\xBD\x3A"],"\xDD"=>[0,"\xBDB"],"\xDE"=>[0,"\xBDC"],"\xDF"=>[0,"\xBDD"],"\xE0"=>[0,"\xBDE"],"\xE1"=>[0,"\xBDF"],"\xE2"=>[0,"\xBDG"],"\xE3"=>[0,"\xBDH"],"\xE4"=>[0,"\xBDI"],"\xE5"=>[0,"\xBDJ"],"\xE6"=>[0,"\xBDK"],"\xE7"=>[0,"\xBDL"],"\xE8"=>[0,"\xBDM"],"\xE9"=>[0,"\xBDN"],"\xEA"=>[0,"\xBDO"],"\xEB"=>[0,"\xBDP"],"\xEC"=>[0,"\xBDQ"],"\xED"=>[0,"\xBDR"],"\xEE"=>[0,"\xBDS"],"\xEF"=>[0,"\xBDT"],"\xF0"=>[0,"\xBDU"],"\xF1"=>[0,"\xBDV"],"\xF2"=>[0,"\xBDW"],"\xF3"=>[0,"\xBDY"],"\xF4"=>[0,"\xBDj"],"\xF5"=>[0,"\xBDk"],"\xF6"=>[0,"\xBDq"],"\xF7"=>[0,"\xBDv"],"\xF8"=>[0,"\xBDw"],"\xF9"=>[0,"\xBDx"],"\xFA"=>[0,"\xBDy"],"\xFB"=>[0,"\xBDz"],"\xFC"=>[82,"\xBD"],"\xFD"=>[87,"\xBD"],"\xFE"=>[130,"\xBD"],"\xFF"=>[9,"\xBD"],],["\x00"=>[77,"\xBB0"],"\x01"=>[18,"\xBB0"],"\x02"=>[77,"\xBB1"],"\x03"=>[18,"\xBB1"],"\x04"=>[77,"\xBB2"],"\x05"=>[18,"\xBB2"],"\x06"=>[77,"\xBBa"],"\x07"=>[18,"\xBBa"],"\x08"=>[77,"\xBBc"],"\x09"=>[18,"\xBBc"],"\x0A"=>[77,"\xBBe"],"\x0B"=>[18,"\xBBe"],"\x0C"=>[77,"\xBBi"],"\x0D"=>[18,"\xBBi"],"\x0E"=>[77,"\xBBo"],"\x0F"=>[18,"\xBBo"],"\x10"=>[77,"\xBBs"],"\x11"=>[18,"\xBBs"],"\x12"=>[77,"\xBBt"],"\x13"=>[18,"\xBBt"],"\x14"=>[0,"\xBB\x20"],"\x15"=>[0,"\xBB\x25"],"\x16"=>[0,"\xBB-"],"\x17"=>[0,"\xBB."],"\x18"=>[0,"\xBB\x2F"],"\x19"=>[0,"\xBB3"],"\x1A"=>[0,"\xBB4"],"\x1B"=>[0,"\xBB5"],"\x1C"=>[0,"\xBB6"],"\x1D"=>[0,"\xBB7"],"\x1E"=>[0,"\xBB8"],"\x1F"=>[0,"\xBB9"],"\x20"=>[0,"\xBB\x3D"],"\x21"=>[0,"\xBBA"],"\x22"=>[0,"\xBB_"],"\x23"=>[0,"\xBBb"],"\x24"=>[0,"\xBBd"],"\x25"=>[0,"\xBBf"],"\x26"=>[0,"\xBBg"],"\x27"=>[0,"\xBBh"],"\x28"=>[0,"\xBBl"],"\x29"=>[0,"\xBBm"],"\x2A"=>[0,"\xBBn"],"\x2B"=>[0,"\xBBp"],"\x2C"=>[0,"\xBBr"],"-"=>[0,"\xBBu"],"."=>[100,"\xBB"],"\x2F"=>[110,"\xBB"],[111,"\xBB"],[115,"\xBB"],[116,"\xBB"],[118,"\xBB"],[119,"\xBB"],[122,"\xBB"],[123,"\xBB"],[125,"\xBB"],[126,"\xBB"],[129,"\xBB"],"\x3A"=>[143,"\xBB"],"\x3B"=>[148,"\xBB"],"\x3C"=>[151,"\xBB"],"\x3D"=>[153,"\xBB"],"\x3E"=>[83,"\xBB"],"\x3F"=>[10,"\xBB"],"\x40"=>[77,"\xBD0"],"A"=>[18,"\xBD0"],"B"=>[77,"\xBD1"],"C"=>[18,"\xBD1"],"D"=>[77,"\xBD2"],"E"=>[18,"\xBD2"],"F"=>[77,"\xBDa"],"G"=>[18,"\xBDa"],"H"=>[77,"\xBDc"],"I"=>[18,"\xBDc"],"J"=>[77,"\xBDe"],"K"=>[18,"\xBDe"],"L"=>[77,"\xBDi"],"M"=>[18,"\xBDi"],"N"=>[77,"\xBDo"],"O"=>[18,"\xBDo"],"P"=>[77,"\xBDs"],"Q"=>[18,"\xBDs"],"R"=>[77,"\xBDt"],"S"=>[18,"\xBDt"],"T"=>[0,"\xBD\x20"],"U"=>[0,"\xBD\x25"],"V"=>[0,"\xBD-"],"W"=>[0,"\xBD."],"X"=>[0,"\xBD\x2F"],"Y"=>[0,"\xBD3"],"Z"=>[0,"\xBD4"],"\x5B"=>[0,"\xBD5"],"\x5C"=>[0,"\xBD6"],"\x5D"=>[0,"\xBD7"],"\x5E"=>[0,"\xBD8"],"_"=>[0,"\xBD9"],"\x60"=>[0,"\xBD\x3D"],"a"=>[0,"\xBDA"],"b"=>[0,"\xBD_"],"c"=>[0,"\xBDb"],"d"=>[0,"\xBDd"],"e"=>[0,"\xBDf"],"f"=>[0,"\xBDg"],"g"=>[0,"\xBDh"],"h"=>[0,"\xBDl"],"i"=>[0,"\xBDm"],"j"=>[0,"\xBDn"],"k"=>[0,"\xBDp"],"l"=>[0,"\xBDr"],"m"=>[0,"\xBDu"],"n"=>[100,"\xBD"],"o"=>[110,"\xBD"],"p"=>[111,"\xBD"],"q"=>[115,"\xBD"],"r"=>[116,"\xBD"],"s"=>[118,"\xBD"],"t"=>[119,"\xBD"],"u"=>[122,"\xBD"],"v"=>[123,"\xBD"],"w"=>[125,"\xBD"],"x"=>[126,"\xBD"],"y"=>[129,"\xBD"],"z"=>[143,"\xBD"],"\x7B"=>[148,"\xBD"],"\x7C"=>[151,"\xBD"],"\x7D"=>[153,"\xBD"],"~"=>[83,"\xBD"],"\x7F"=>[10,"\xBD"],"\x80"=>[77,"\xBE0"],"\x81"=>[18,"\xBE0"],"\x82"=>[77,"\xBE1"],"\x83"=>[18,"\xBE1"],"\x84"=>[77,"\xBE2"],"\x85"=>[18,"\xBE2"],"\x86"=>[77,"\xBEa"],"\x87"=>[18,"\xBEa"],"\x88"=>[77,"\xBEc"],"\x89"=>[18,"\xBEc"],"\x8A"=>[77,"\xBEe"],"\x8B"=>[18,"\xBEe"],"\x8C"=>[77,"\xBEi"],"\x8D"=>[18,"\xBEi"],"\x8E"=>[77,"\xBEo"],"\x8F"=>[18,"\xBEo"],"\x90"=>[77,"\xBEs"],"\x91"=>[18,"\xBEs"],"\x92"=>[77,"\xBEt"],"\x93"=>[18,"\xBEt"],"\x94"=>[0,"\xBE\x20"],"\x95"=>[0,"\xBE\x25"],"\x96"=>[0,"\xBE-"],"\x97"=>[0,"\xBE."],"\x98"=>[0,"\xBE\x2F"],"\x99"=>[0,"\xBE3"],"\x9A"=>[0,"\xBE4"],"\x9B"=>[0,"\xBE5"],"\x9C"=>[0,"\xBE6"],"\x9D"=>[0,"\xBE7"],"\x9E"=>[0,"\xBE8"],"\x9F"=>[0,"\xBE9"],"\xA0"=>[0,"\xBE\x3D"],"\xA1"=>[0,"\xBEA"],"\xA2"=>[0,"\xBE_"],"\xA3"=>[0,"\xBEb"],"\xA4"=>[0,"\xBEd"],"\xA5"=>[0,"\xBEf"],"\xA6"=>[0,"\xBEg"],"\xA7"=>[0,"\xBEh"],"\xA8"=>[0,"\xBEl"],"\xA9"=>[0,"\xBEm"],"\xAA"=>[0,"\xBEn"],"\xAB"=>[0,"\xBEp"],"\xAC"=>[0,"\xBEr"],"\xAD"=>[0,"\xBEu"],"\xAE"=>[100,"\xBE"],"\xAF"=>[110,"\xBE"],"\xB0"=>[111,"\xBE"],"\xB1"=>[115,"\xBE"],"\xB2"=>[116,"\xBE"],"\xB3"=>[118,"\xBE"],"\xB4"=>[119,"\xBE"],"\xB5"=>[122,"\xBE"],"\xB6"=>[123,"\xBE"],"\xB7"=>[125,"\xBE"],"\xB8"=>[126,"\xBE"],"\xB9"=>[129,"\xBE"],"\xBA"=>[143,"\xBE"],"\xBB"=>[148,"\xBE"],"\xBC"=>[151,"\xBE"],"\xBD"=>[153,"\xBE"],"\xBE"=>[83,"\xBE"],"\xBF"=>[10,"\xBE"],"\xC0"=>[77,"\xC40"],"\xC1"=>[18,"\xC40"],"\xC2"=>[77,"\xC41"],"\xC3"=>[18,"\xC41"],"\xC4"=>[77,"\xC42"],"\xC5"=>[18,"\xC42"],"\xC6"=>[77,"\xC4a"],"\xC7"=>[18,"\xC4a"],"\xC8"=>[77,"\xC4c"],"\xC9"=>[18,"\xC4c"],"\xCA"=>[77,"\xC4e"],"\xCB"=>[18,"\xC4e"],"\xCC"=>[77,"\xC4i"],"\xCD"=>[18,"\xC4i"],"\xCE"=>[77,"\xC4o"],"\xCF"=>[18,"\xC4o"],"\xD0"=>[77,"\xC4s"],"\xD1"=>[18,"\xC4s"],"\xD2"=>[77,"\xC4t"],"\xD3"=>[18,"\xC4t"],"\xD4"=>[0,"\xC4\x20"],"\xD5"=>[0,"\xC4\x25"],"\xD6"=>[0,"\xC4-"],"\xD7"=>[0,"\xC4."],"\xD8"=>[0,"\xC4\x2F"],"\xD9"=>[0,"\xC43"],"\xDA"=>[0,"\xC44"],"\xDB"=>[0,"\xC45"],"\xDC"=>[0,"\xC46"],"\xDD"=>[0,"\xC47"],"\xDE"=>[0,"\xC48"],"\xDF"=>[0,"\xC49"],"\xE0"=>[0,"\xC4\x3D"],"\xE1"=>[0,"\xC4A"],"\xE2"=>[0,"\xC4_"],"\xE3"=>[0,"\xC4b"],"\xE4"=>[0,"\xC4d"],"\xE5"=>[0,"\xC4f"],"\xE6"=>[0,"\xC4g"],"\xE7"=>[0,"\xC4h"],"\xE8"=>[0,"\xC4l"],"\xE9"=>[0,"\xC4m"],"\xEA"=>[0,"\xC4n"],"\xEB"=>[0,"\xC4p"],"\xEC"=>[0,"\xC4r"],"\xED"=>[0,"\xC4u"],"\xEE"=>[100,"\xC4"],"\xEF"=>[110,"\xC4"],"\xF0"=>[111,"\xC4"],"\xF1"=>[115,"\xC4"],"\xF2"=>[116,"\xC4"],"\xF3"=>[118,"\xC4"],"\xF4"=>[119,"\xC4"],"\xF5"=>[122,"\xC4"],"\xF6"=>[123,"\xC4"],"\xF7"=>[125,"\xC4"],"\xF8"=>[126,"\xC4"],"\xF9"=>[129,"\xC4"],"\xFA"=>[143,"\xC4"],"\xFB"=>[148,"\xC4"],"\xFC"=>[151,"\xC4"],"\xFD"=>[153,"\xC4"],"\xFE"=>[83,"\xC4"],"\xFF"=>[10,"\xC4"],],["\x00"=>[94,"\xBC0"],"\x01"=>[76,"\xBC0"],"\x02"=>[104,"\xBC0"],"\x03"=>[16,"\xBC0"],"\x04"=>[94,"\xBC1"],"\x05"=>[76,"\xBC1"],"\x06"=>[104,"\xBC1"],"\x07"=>[16,"\xBC1"],"\x08"=>[94,"\xBC2"],"\x09"=>[76,"\xBC2"],"\x0A"=>[104,"\xBC2"],"\x0B"=>[16,"\xBC2"],"\x0C"=>[94,"\xBCa"],"\x0D"=>[76,"\xBCa"],"\x0E"=>[104,"\xBCa"],"\x0F"=>[16,"\xBCa"],"\x10"=>[94,"\xBCc"],"\x11"=>[76,"\xBCc"],"\x12"=>[104,"\xBCc"],"\x13"=>[16,"\xBCc"],"\x14"=>[94,"\xBCe"],"\x15"=>[76,"\xBCe"],"\x16"=>[104,"\xBCe"],"\x17"=>[16,"\xBCe"],"\x18"=>[94,"\xBCi"],"\x19"=>[76,"\xBCi"],"\x1A"=>[104,"\xBCi"],"\x1B"=>[16,"\xBCi"],"\x1C"=>[94,"\xBCo"],"\x1D"=>[76,"\xBCo"],"\x1E"=>[104,"\xBCo"],"\x1F"=>[16,"\xBCo"],"\x20"=>[94,"\xBCs"],"\x21"=>[76,"\xBCs"],"\x22"=>[104,"\xBCs"],"\x23"=>[16,"\xBCs"],"\x24"=>[94,"\xBCt"],"\x25"=>[76,"\xBCt"],"\x26"=>[104,"\xBCt"],"\x27"=>[16,"\xBCt"],"\x28"=>[77,"\xBC\x20"],"\x29"=>[18,"\xBC\x20"],"\x2A"=>[77,"\xBC\x25"],"\x2B"=>[18,"\xBC\x25"],"\x2C"=>[77,"\xBC-"],"-"=>[18,"\xBC-"],"."=>[77,"\xBC."],"\x2F"=>[18,"\xBC."],[77,"\xBC\x2F"],[18,"\xBC\x2F"],[77,"\xBC3"],[18,"\xBC3"],[77,"\xBC4"],[18,"\xBC4"],[77,"\xBC5"],[18,"\xBC5"],[77,"\xBC6"],[18,"\xBC6"],"\x3A"=>[77,"\xBC7"],"\x3B"=>[18,"\xBC7"],"\x3C"=>[77,"\xBC8"],"\x3D"=>[18,"\xBC8"],"\x3E"=>[77,"\xBC9"],"\x3F"=>[18,"\xBC9"],"\x40"=>[77,"\xBC\x3D"],"A"=>[18,"\xBC\x3D"],"B"=>[77,"\xBCA"],"C"=>[18,"\xBCA"],"D"=>[77,"\xBC_"],"E"=>[18,"\xBC_"],"F"=>[77,"\xBCb"],"G"=>[18,"\xBCb"],"H"=>[77,"\xBCd"],"I"=>[18,"\xBCd"],"J"=>[77,"\xBCf"],"K"=>[18,"\xBCf"],"L"=>[77,"\xBCg"],"M"=>[18,"\xBCg"],"N"=>[77,"\xBCh"],"O"=>[18,"\xBCh"],"P"=>[77,"\xBCl"],"Q"=>[18,"\xBCl"],"R"=>[77,"\xBCm"],"S"=>[18,"\xBCm"],"T"=>[77,"\xBCn"],"U"=>[18,"\xBCn"],"V"=>[77,"\xBCp"],"W"=>[18,"\xBCp"],"X"=>[77,"\xBCr"],"Y"=>[18,"\xBCr"],"Z"=>[77,"\xBCu"],"\x5B"=>[18,"\xBCu"],"\x5C"=>[0,"\xBC\x3A"],"\x5D"=>[0,"\xBCB"],"\x5E"=>[0,"\xBCC"],"_"=>[0,"\xBCD"],"\x60"=>[0,"\xBCE"],"a"=>[0,"\xBCF"],"b"=>[0,"\xBCG"],"c"=>[0,"\xBCH"],"d"=>[0,"\xBCI"],"e"=>[0,"\xBCJ"],"f"=>[0,"\xBCK"],"g"=>[0,"\xBCL"],"h"=>[0,"\xBCM"],"i"=>[0,"\xBCN"],"j"=>[0,"\xBCO"],"k"=>[0,"\xBCP"],"l"=>[0,"\xBCQ"],"m"=>[0,"\xBCR"],"n"=>[0,"\xBCS"],"o"=>[0,"\xBCT"],"p"=>[0,"\xBCU"],"q"=>[0,"\xBCV"],"r"=>[0,"\xBCW"],"s"=>[0,"\xBCY"],"t"=>[0,"\xBCj"],"u"=>[0,"\xBCk"],"v"=>[0,"\xBCq"],"w"=>[0,"\xBCv"],"x"=>[0,"\xBCw"],"y"=>[0,"\xBCx"],"z"=>[0,"\xBCy"],"\x7B"=>[0,"\xBCz"],"\x7C"=>[82,"\xBC"],"\x7D"=>[87,"\xBC"],"~"=>[130,"\xBC"],"\x7F"=>[9,"\xBC"],"\x80"=>[94,"\xBF0"],"\x81"=>[76,"\xBF0"],"\x82"=>[104,"\xBF0"],"\x83"=>[16,"\xBF0"],"\x84"=>[94,"\xBF1"],"\x85"=>[76,"\xBF1"],"\x86"=>[104,"\xBF1"],"\x87"=>[16,"\xBF1"],"\x88"=>[94,"\xBF2"],"\x89"=>[76,"\xBF2"],"\x8A"=>[104,"\xBF2"],"\x8B"=>[16,"\xBF2"],"\x8C"=>[94,"\xBFa"],"\x8D"=>[76,"\xBFa"],"\x8E"=>[104,"\xBFa"],"\x8F"=>[16,"\xBFa"],"\x90"=>[94,"\xBFc"],"\x91"=>[76,"\xBFc"],"\x92"=>[104,"\xBFc"],"\x93"=>[16,"\xBFc"],"\x94"=>[94,"\xBFe"],"\x95"=>[76,"\xBFe"],"\x96"=>[104,"\xBFe"],"\x97"=>[16,"\xBFe"],"\x98"=>[94,"\xBFi"],"\x99"=>[76,"\xBFi"],"\x9A"=>[104,"\xBFi"],"\x9B"=>[16,"\xBFi"],"\x9C"=>[94,"\xBFo"],"\x9D"=>[76,"\xBFo"],"\x9E"=>[104,"\xBFo"],"\x9F"=>[16,"\xBFo"],"\xA0"=>[94,"\xBFs"],"\xA1"=>[76,"\xBFs"],"\xA2"=>[104,"\xBFs"],"\xA3"=>[16,"\xBFs"],"\xA4"=>[94,"\xBFt"],"\xA5"=>[76,"\xBFt"],"\xA6"=>[104,"\xBFt"],"\xA7"=>[16,"\xBFt"],"\xA8"=>[77,"\xBF\x20"],"\xA9"=>[18,"\xBF\x20"],"\xAA"=>[77,"\xBF\x25"],"\xAB"=>[18,"\xBF\x25"],"\xAC"=>[77,"\xBF-"],"\xAD"=>[18,"\xBF-"],"\xAE"=>[77,"\xBF."],"\xAF"=>[18,"\xBF."],"\xB0"=>[77,"\xBF\x2F"],"\xB1"=>[18,"\xBF\x2F"],"\xB2"=>[77,"\xBF3"],"\xB3"=>[18,"\xBF3"],"\xB4"=>[77,"\xBF4"],"\xB5"=>[18,"\xBF4"],"\xB6"=>[77,"\xBF5"],"\xB7"=>[18,"\xBF5"],"\xB8"=>[77,"\xBF6"],"\xB9"=>[18,"\xBF6"],"\xBA"=>[77,"\xBF7"],"\xBB"=>[18,"\xBF7"],"\xBC"=>[77,"\xBF8"],"\xBD"=>[18,"\xBF8"],"\xBE"=>[77,"\xBF9"],"\xBF"=>[18,"\xBF9"],"\xC0"=>[77,"\xBF\x3D"],"\xC1"=>[18,"\xBF\x3D"],"\xC2"=>[77,"\xBFA"],"\xC3"=>[18,"\xBFA"],"\xC4"=>[77,"\xBF_"],"\xC5"=>[18,"\xBF_"],"\xC6"=>[77,"\xBFb"],"\xC7"=>[18,"\xBFb"],"\xC8"=>[77,"\xBFd"],"\xC9"=>[18,"\xBFd"],"\xCA"=>[77,"\xBFf"],"\xCB"=>[18,"\xBFf"],"\xCC"=>[77,"\xBFg"],"\xCD"=>[18,"\xBFg"],"\xCE"=>[77,"\xBFh"],"\xCF"=>[18,"\xBFh"],"\xD0"=>[77,"\xBFl"],"\xD1"=>[18,"\xBFl"],"\xD2"=>[77,"\xBFm"],"\xD3"=>[18,"\xBFm"],"\xD4"=>[77,"\xBFn"],"\xD5"=>[18,"\xBFn"],"\xD6"=>[77,"\xBFp"],"\xD7"=>[18,"\xBFp"],"\xD8"=>[77,"\xBFr"],"\xD9"=>[18,"\xBFr"],"\xDA"=>[77,"\xBFu"],"\xDB"=>[18,"\xBFu"],"\xDC"=>[0,"\xBF\x3A"],"\xDD"=>[0,"\xBFB"],"\xDE"=>[0,"\xBFC"],"\xDF"=>[0,"\xBFD"],"\xE0"=>[0,"\xBFE"],"\xE1"=>[0,"\xBFF"],"\xE2"=>[0,"\xBFG"],"\xE3"=>[0,"\xBFH"],"\xE4"=>[0,"\xBFI"],"\xE5"=>[0,"\xBFJ"],"\xE6"=>[0,"\xBFK"],"\xE7"=>[0,"\xBFL"],"\xE8"=>[0,"\xBFM"],"\xE9"=>[0,"\xBFN"],"\xEA"=>[0,"\xBFO"],"\xEB"=>[0,"\xBFP"],"\xEC"=>[0,"\xBFQ"],"\xED"=>[0,"\xBFR"],"\xEE"=>[0,"\xBFS"],"\xEF"=>[0,"\xBFT"],"\xF0"=>[0,"\xBFU"],"\xF1"=>[0,"\xBFV"],"\xF2"=>[0,"\xBFW"],"\xF3"=>[0,"\xBFY"],"\xF4"=>[0,"\xBFj"],"\xF5"=>[0,"\xBFk"],"\xF6"=>[0,"\xBFq"],"\xF7"=>[0,"\xBFv"],"\xF8"=>[0,"\xBFw"],"\xF9"=>[0,"\xBFx"],"\xFA"=>[0,"\xBFy"],"\xFB"=>[0,"\xBFz"],"\xFC"=>[82,"\xBF"],"\xFD"=>[87,"\xBF"],"\xFE"=>[130,"\xBF"],"\xFF"=>[9,"\xBF"],],["\x00"=>[77,"\xBC0"],"\x01"=>[18,"\xBC0"],"\x02"=>[77,"\xBC1"],"\x03"=>[18,"\xBC1"],"\x04"=>[77,"\xBC2"],"\x05"=>[18,"\xBC2"],"\x06"=>[77,"\xBCa"],"\x07"=>[18,"\xBCa"],"\x08"=>[77,"\xBCc"],"\x09"=>[18,"\xBCc"],"\x0A"=>[77,"\xBCe"],"\x0B"=>[18,"\xBCe"],"\x0C"=>[77,"\xBCi"],"\x0D"=>[18,"\xBCi"],"\x0E"=>[77,"\xBCo"],"\x0F"=>[18,"\xBCo"],"\x10"=>[77,"\xBCs"],"\x11"=>[18,"\xBCs"],"\x12"=>[77,"\xBCt"],"\x13"=>[18,"\xBCt"],"\x14"=>[0,"\xBC\x20"],"\x15"=>[0,"\xBC\x25"],"\x16"=>[0,"\xBC-"],"\x17"=>[0,"\xBC."],"\x18"=>[0,"\xBC\x2F"],"\x19"=>[0,"\xBC3"],"\x1A"=>[0,"\xBC4"],"\x1B"=>[0,"\xBC5"],"\x1C"=>[0,"\xBC6"],"\x1D"=>[0,"\xBC7"],"\x1E"=>[0,"\xBC8"],"\x1F"=>[0,"\xBC9"],"\x20"=>[0,"\xBC\x3D"],"\x21"=>[0,"\xBCA"],"\x22"=>[0,"\xBC_"],"\x23"=>[0,"\xBCb"],"\x24"=>[0,"\xBCd"],"\x25"=>[0,"\xBCf"],"\x26"=>[0,"\xBCg"],"\x27"=>[0,"\xBCh"],"\x28"=>[0,"\xBCl"],"\x29"=>[0,"\xBCm"],"\x2A"=>[0,"\xBCn"],"\x2B"=>[0,"\xBCp"],"\x2C"=>[0,"\xBCr"],"-"=>[0,"\xBCu"],"."=>[100,"\xBC"],"\x2F"=>[110,"\xBC"],[111,"\xBC"],[115,"\xBC"],[116,"\xBC"],[118,"\xBC"],[119,"\xBC"],[122,"\xBC"],[123,"\xBC"],[125,"\xBC"],[126,"\xBC"],[129,"\xBC"],"\x3A"=>[143,"\xBC"],"\x3B"=>[148,"\xBC"],"\x3C"=>[151,"\xBC"],"\x3D"=>[153,"\xBC"],"\x3E"=>[83,"\xBC"],"\x3F"=>[10,"\xBC"],"\x40"=>[77,"\xBF0"],"A"=>[18,"\xBF0"],"B"=>[77,"\xBF1"],"C"=>[18,"\xBF1"],"D"=>[77,"\xBF2"],"E"=>[18,"\xBF2"],"F"=>[77,"\xBFa"],"G"=>[18,"\xBFa"],"H"=>[77,"\xBFc"],"I"=>[18,"\xBFc"],"J"=>[77,"\xBFe"],"K"=>[18,"\xBFe"],"L"=>[77,"\xBFi"],"M"=>[18,"\xBFi"],"N"=>[77,"\xBFo"],"O"=>[18,"\xBFo"],"P"=>[77,"\xBFs"],"Q"=>[18,"\xBFs"],"R"=>[77,"\xBFt"],"S"=>[18,"\xBFt"],"T"=>[0,"\xBF\x20"],"U"=>[0,"\xBF\x25"],"V"=>[0,"\xBF-"],"W"=>[0,"\xBF."],"X"=>[0,"\xBF\x2F"],"Y"=>[0,"\xBF3"],"Z"=>[0,"\xBF4"],"\x5B"=>[0,"\xBF5"],"\x5C"=>[0,"\xBF6"],"\x5D"=>[0,"\xBF7"],"\x5E"=>[0,"\xBF8"],"_"=>[0,"\xBF9"],"\x60"=>[0,"\xBF\x3D"],"a"=>[0,"\xBFA"],"b"=>[0,"\xBF_"],"c"=>[0,"\xBFb"],"d"=>[0,"\xBFd"],"e"=>[0,"\xBFf"],"f"=>[0,"\xBFg"],"g"=>[0,"\xBFh"],"h"=>[0,"\xBFl"],"i"=>[0,"\xBFm"],"j"=>[0,"\xBFn"],"k"=>[0,"\xBFp"],"l"=>[0,"\xBFr"],"m"=>[0,"\xBFu"],"n"=>[100,"\xBF"],"o"=>[110,"\xBF"],"p"=>[111,"\xBF"],"q"=>[115,"\xBF"],"r"=>[116,"\xBF"],"s"=>[118,"\xBF"],"t"=>[119,"\xBF"],"u"=>[122,"\xBF"],"v"=>[123,"\xBF"],"w"=>[125,"\xBF"],"x"=>[126,"\xBF"],"y"=>[129,"\xBF"],"z"=>[143,"\xBF"],"\x7B"=>[148,"\xBF"],"\x7C"=>[151,"\xBF"],"\x7D"=>[153,"\xBF"],"~"=>[83,"\xBF"],"\x7F"=>[10,"\xBF"],"\x80"=>[77,"\xC50"],"\x81"=>[18,"\xC50"],"\x82"=>[77,"\xC51"],"\x83"=>[18,"\xC51"],"\x84"=>[77,"\xC52"],"\x85"=>[18,"\xC52"],"\x86"=>[77,"\xC5a"],"\x87"=>[18,"\xC5a"],"\x88"=>[77,"\xC5c"],"\x89"=>[18,"\xC5c"],"\x8A"=>[77,"\xC5e"],"\x8B"=>[18,"\xC5e"],"\x8C"=>[77,"\xC5i"],"\x8D"=>[18,"\xC5i"],"\x8E"=>[77,"\xC5o"],"\x8F"=>[18,"\xC5o"],"\x90"=>[77,"\xC5s"],"\x91"=>[18,"\xC5s"],"\x92"=>[77,"\xC5t"],"\x93"=>[18,"\xC5t"],"\x94"=>[0,"\xC5\x20"],"\x95"=>[0,"\xC5\x25"],"\x96"=>[0,"\xC5-"],"\x97"=>[0,"\xC5."],"\x98"=>[0,"\xC5\x2F"],"\x99"=>[0,"\xC53"],"\x9A"=>[0,"\xC54"],"\x9B"=>[0,"\xC55"],"\x9C"=>[0,"\xC56"],"\x9D"=>[0,"\xC57"],"\x9E"=>[0,"\xC58"],"\x9F"=>[0,"\xC59"],"\xA0"=>[0,"\xC5\x3D"],"\xA1"=>[0,"\xC5A"],"\xA2"=>[0,"\xC5_"],"\xA3"=>[0,"\xC5b"],"\xA4"=>[0,"\xC5d"],"\xA5"=>[0,"\xC5f"],"\xA6"=>[0,"\xC5g"],"\xA7"=>[0,"\xC5h"],"\xA8"=>[0,"\xC5l"],"\xA9"=>[0,"\xC5m"],"\xAA"=>[0,"\xC5n"],"\xAB"=>[0,"\xC5p"],"\xAC"=>[0,"\xC5r"],"\xAD"=>[0,"\xC5u"],"\xAE"=>[100,"\xC5"],"\xAF"=>[110,"\xC5"],"\xB0"=>[111,"\xC5"],"\xB1"=>[115,"\xC5"],"\xB2"=>[116,"\xC5"],"\xB3"=>[118,"\xC5"],"\xB4"=>[119,"\xC5"],"\xB5"=>[122,"\xC5"],"\xB6"=>[123,"\xC5"],"\xB7"=>[125,"\xC5"],"\xB8"=>[126,"\xC5"],"\xB9"=>[129,"\xC5"],"\xBA"=>[143,"\xC5"],"\xBB"=>[148,"\xC5"],"\xBC"=>[151,"\xC5"],"\xBD"=>[153,"\xC5"],"\xBE"=>[83,"\xC5"],"\xBF"=>[10,"\xC5"],"\xC0"=>[77,"\xE70"],"\xC1"=>[18,"\xE70"],"\xC2"=>[77,"\xE71"],"\xC3"=>[18,"\xE71"],"\xC4"=>[77,"\xE72"],"\xC5"=>[18,"\xE72"],"\xC6"=>[77,"\xE7a"],"\xC7"=>[18,"\xE7a"],"\xC8"=>[77,"\xE7c"],"\xC9"=>[18,"\xE7c"],"\xCA"=>[77,"\xE7e"],"\xCB"=>[18,"\xE7e"],"\xCC"=>[77,"\xE7i"],"\xCD"=>[18,"\xE7i"],"\xCE"=>[77,"\xE7o"],"\xCF"=>[18,"\xE7o"],"\xD0"=>[77,"\xE7s"],"\xD1"=>[18,"\xE7s"],"\xD2"=>[77,"\xE7t"],"\xD3"=>[18,"\xE7t"],"\xD4"=>[0,"\xE7\x20"],"\xD5"=>[0,"\xE7\x25"],"\xD6"=>[0,"\xE7-"],"\xD7"=>[0,"\xE7."],"\xD8"=>[0,"\xE7\x2F"],"\xD9"=>[0,"\xE73"],"\xDA"=>[0,"\xE74"],"\xDB"=>[0,"\xE75"],"\xDC"=>[0,"\xE76"],"\xDD"=>[0,"\xE77"],"\xDE"=>[0,"\xE78"],"\xDF"=>[0,"\xE79"],"\xE0"=>[0,"\xE7\x3D"],"\xE1"=>[0,"\xE7A"],"\xE2"=>[0,"\xE7_"],"\xE3"=>[0,"\xE7b"],"\xE4"=>[0,"\xE7d"],"\xE5"=>[0,"\xE7f"],"\xE6"=>[0,"\xE7g"],"\xE7"=>[0,"\xE7h"],"\xE8"=>[0,"\xE7l"],"\xE9"=>[0,"\xE7m"],"\xEA"=>[0,"\xE7n"],"\xEB"=>[0,"\xE7p"],"\xEC"=>[0,"\xE7r"],"\xED"=>[0,"\xE7u"],"\xEE"=>[100,"\xE7"],"\xEF"=>[110,"\xE7"],"\xF0"=>[111,"\xE7"],"\xF1"=>[115,"\xE7"],"\xF2"=>[116,"\xE7"],"\xF3"=>[118,"\xE7"],"\xF4"=>[119,"\xE7"],"\xF5"=>[122,"\xE7"],"\xF6"=>[123,"\xE7"],"\xF7"=>[125,"\xE7"],"\xF8"=>[126,"\xE7"],"\xF9"=>[129,"\xE7"],"\xFA"=>[143,"\xE7"],"\xFB"=>[148,"\xE7"],"\xFC"=>[151,"\xE7"],"\xFD"=>[153,"\xE7"],"\xFE"=>[83,"\xE7"],"\xFF"=>[10,"\xE7"],],["\x00"=>[94,"\xBE0"],"\x01"=>[76,"\xBE0"],"\x02"=>[104,"\xBE0"],"\x03"=>[16,"\xBE0"],"\x04"=>[94,"\xBE1"],"\x05"=>[76,"\xBE1"],"\x06"=>[104,"\xBE1"],"\x07"=>[16,"\xBE1"],"\x08"=>[94,"\xBE2"],"\x09"=>[76,"\xBE2"],"\x0A"=>[104,"\xBE2"],"\x0B"=>[16,"\xBE2"],"\x0C"=>[94,"\xBEa"],"\x0D"=>[76,"\xBEa"],"\x0E"=>[104,"\xBEa"],"\x0F"=>[16,"\xBEa"],"\x10"=>[94,"\xBEc"],"\x11"=>[76,"\xBEc"],"\x12"=>[104,"\xBEc"],"\x13"=>[16,"\xBEc"],"\x14"=>[94,"\xBEe"],"\x15"=>[76,"\xBEe"],"\x16"=>[104,"\xBEe"],"\x17"=>[16,"\xBEe"],"\x18"=>[94,"\xBEi"],"\x19"=>[76,"\xBEi"],"\x1A"=>[104,"\xBEi"],"\x1B"=>[16,"\xBEi"],"\x1C"=>[94,"\xBEo"],"\x1D"=>[76,"\xBEo"],"\x1E"=>[104,"\xBEo"],"\x1F"=>[16,"\xBEo"],"\x20"=>[94,"\xBEs"],"\x21"=>[76,"\xBEs"],"\x22"=>[104,"\xBEs"],"\x23"=>[16,"\xBEs"],"\x24"=>[94,"\xBEt"],"\x25"=>[76,"\xBEt"],"\x26"=>[104,"\xBEt"],"\x27"=>[16,"\xBEt"],"\x28"=>[77,"\xBE\x20"],"\x29"=>[18,"\xBE\x20"],"\x2A"=>[77,"\xBE\x25"],"\x2B"=>[18,"\xBE\x25"],"\x2C"=>[77,"\xBE-"],"-"=>[18,"\xBE-"],"."=>[77,"\xBE."],"\x2F"=>[18,"\xBE."],[77,"\xBE\x2F"],[18,"\xBE\x2F"],[77,"\xBE3"],[18,"\xBE3"],[77,"\xBE4"],[18,"\xBE4"],[77,"\xBE5"],[18,"\xBE5"],[77,"\xBE6"],[18,"\xBE6"],"\x3A"=>[77,"\xBE7"],"\x3B"=>[18,"\xBE7"],"\x3C"=>[77,"\xBE8"],"\x3D"=>[18,"\xBE8"],"\x3E"=>[77,"\xBE9"],"\x3F"=>[18,"\xBE9"],"\x40"=>[77,"\xBE\x3D"],"A"=>[18,"\xBE\x3D"],"B"=>[77,"\xBEA"],"C"=>[18,"\xBEA"],"D"=>[77,"\xBE_"],"E"=>[18,"\xBE_"],"F"=>[77,"\xBEb"],"G"=>[18,"\xBEb"],"H"=>[77,"\xBEd"],"I"=>[18,"\xBEd"],"J"=>[77,"\xBEf"],"K"=>[18,"\xBEf"],"L"=>[77,"\xBEg"],"M"=>[18,"\xBEg"],"N"=>[77,"\xBEh"],"O"=>[18,"\xBEh"],"P"=>[77,"\xBEl"],"Q"=>[18,"\xBEl"],"R"=>[77,"\xBEm"],"S"=>[18,"\xBEm"],"T"=>[77,"\xBEn"],"U"=>[18,"\xBEn"],"V"=>[77,"\xBEp"],"W"=>[18,"\xBEp"],"X"=>[77,"\xBEr"],"Y"=>[18,"\xBEr"],"Z"=>[77,"\xBEu"],"\x5B"=>[18,"\xBEu"],"\x5C"=>[0,"\xBE\x3A"],"\x5D"=>[0,"\xBEB"],"\x5E"=>[0,"\xBEC"],"_"=>[0,"\xBED"],"\x60"=>[0,"\xBEE"],"a"=>[0,"\xBEF"],"b"=>[0,"\xBEG"],"c"=>[0,"\xBEH"],"d"=>[0,"\xBEI"],"e"=>[0,"\xBEJ"],"f"=>[0,"\xBEK"],"g"=>[0,"\xBEL"],"h"=>[0,"\xBEM"],"i"=>[0,"\xBEN"],"j"=>[0,"\xBEO"],"k"=>[0,"\xBEP"],"l"=>[0,"\xBEQ"],"m"=>[0,"\xBER"],"n"=>[0,"\xBES"],"o"=>[0,"\xBET"],"p"=>[0,"\xBEU"],"q"=>[0,"\xBEV"],"r"=>[0,"\xBEW"],"s"=>[0,"\xBEY"],"t"=>[0,"\xBEj"],"u"=>[0,"\xBEk"],"v"=>[0,"\xBEq"],"w"=>[0,"\xBEv"],"x"=>[0,"\xBEw"],"y"=>[0,"\xBEx"],"z"=>[0,"\xBEy"],"\x7B"=>[0,"\xBEz"],"\x7C"=>[82,"\xBE"],"\x7D"=>[87,"\xBE"],"~"=>[130,"\xBE"],"\x7F"=>[9,"\xBE"],"\x80"=>[94,"\xC40"],"\x81"=>[76,"\xC40"],"\x82"=>[104,"\xC40"],"\x83"=>[16,"\xC40"],"\x84"=>[94,"\xC41"],"\x85"=>[76,"\xC41"],"\x86"=>[104,"\xC41"],"\x87"=>[16,"\xC41"],"\x88"=>[94,"\xC42"],"\x89"=>[76,"\xC42"],"\x8A"=>[104,"\xC42"],"\x8B"=>[16,"\xC42"],"\x8C"=>[94,"\xC4a"],"\x8D"=>[76,"\xC4a"],"\x8E"=>[104,"\xC4a"],"\x8F"=>[16,"\xC4a"],"\x90"=>[94,"\xC4c"],"\x91"=>[76,"\xC4c"],"\x92"=>[104,"\xC4c"],"\x93"=>[16,"\xC4c"],"\x94"=>[94,"\xC4e"],"\x95"=>[76,"\xC4e"],"\x96"=>[104,"\xC4e"],"\x97"=>[16,"\xC4e"],"\x98"=>[94,"\xC4i"],"\x99"=>[76,"\xC4i"],"\x9A"=>[104,"\xC4i"],"\x9B"=>[16,"\xC4i"],"\x9C"=>[94,"\xC4o"],"\x9D"=>[76,"\xC4o"],"\x9E"=>[104,"\xC4o"],"\x9F"=>[16,"\xC4o"],"\xA0"=>[94,"\xC4s"],"\xA1"=>[76,"\xC4s"],"\xA2"=>[104,"\xC4s"],"\xA3"=>[16,"\xC4s"],"\xA4"=>[94,"\xC4t"],"\xA5"=>[76,"\xC4t"],"\xA6"=>[104,"\xC4t"],"\xA7"=>[16,"\xC4t"],"\xA8"=>[77,"\xC4\x20"],"\xA9"=>[18,"\xC4\x20"],"\xAA"=>[77,"\xC4\x25"],"\xAB"=>[18,"\xC4\x25"],"\xAC"=>[77,"\xC4-"],"\xAD"=>[18,"\xC4-"],"\xAE"=>[77,"\xC4."],"\xAF"=>[18,"\xC4."],"\xB0"=>[77,"\xC4\x2F"],"\xB1"=>[18,"\xC4\x2F"],"\xB2"=>[77,"\xC43"],"\xB3"=>[18,"\xC43"],"\xB4"=>[77,"\xC44"],"\xB5"=>[18,"\xC44"],"\xB6"=>[77,"\xC45"],"\xB7"=>[18,"\xC45"],"\xB8"=>[77,"\xC46"],"\xB9"=>[18,"\xC46"],"\xBA"=>[77,"\xC47"],"\xBB"=>[18,"\xC47"],"\xBC"=>[77,"\xC48"],"\xBD"=>[18,"\xC48"],"\xBE"=>[77,"\xC49"],"\xBF"=>[18,"\xC49"],"\xC0"=>[77,"\xC4\x3D"],"\xC1"=>[18,"\xC4\x3D"],"\xC2"=>[77,"\xC4A"],"\xC3"=>[18,"\xC4A"],"\xC4"=>[77,"\xC4_"],"\xC5"=>[18,"\xC4_"],"\xC6"=>[77,"\xC4b"],"\xC7"=>[18,"\xC4b"],"\xC8"=>[77,"\xC4d"],"\xC9"=>[18,"\xC4d"],"\xCA"=>[77,"\xC4f"],"\xCB"=>[18,"\xC4f"],"\xCC"=>[77,"\xC4g"],"\xCD"=>[18,"\xC4g"],"\xCE"=>[77,"\xC4h"],"\xCF"=>[18,"\xC4h"],"\xD0"=>[77,"\xC4l"],"\xD1"=>[18,"\xC4l"],"\xD2"=>[77,"\xC4m"],"\xD3"=>[18,"\xC4m"],"\xD4"=>[77,"\xC4n"],"\xD5"=>[18,"\xC4n"],"\xD6"=>[77,"\xC4p"],"\xD7"=>[18,"\xC4p"],"\xD8"=>[77,"\xC4r"],"\xD9"=>[18,"\xC4r"],"\xDA"=>[77,"\xC4u"],"\xDB"=>[18,"\xC4u"],"\xDC"=>[0,"\xC4\x3A"],"\xDD"=>[0,"\xC4B"],"\xDE"=>[0,"\xC4C"],"\xDF"=>[0,"\xC4D"],"\xE0"=>[0,"\xC4E"],"\xE1"=>[0,"\xC4F"],"\xE2"=>[0,"\xC4G"],"\xE3"=>[0,"\xC4H"],"\xE4"=>[0,"\xC4I"],"\xE5"=>[0,"\xC4J"],"\xE6"=>[0,"\xC4K"],"\xE7"=>[0,"\xC4L"],"\xE8"=>[0,"\xC4M"],"\xE9"=>[0,"\xC4N"],"\xEA"=>[0,"\xC4O"],"\xEB"=>[0,"\xC4P"],"\xEC"=>[0,"\xC4Q"],"\xED"=>[0,"\xC4R"],"\xEE"=>[0,"\xC4S"],"\xEF"=>[0,"\xC4T"],"\xF0"=>[0,"\xC4U"],"\xF1"=>[0,"\xC4V"],"\xF2"=>[0,"\xC4W"],"\xF3"=>[0,"\xC4Y"],"\xF4"=>[0,"\xC4j"],"\xF5"=>[0,"\xC4k"],"\xF6"=>[0,"\xC4q"],"\xF7"=>[0,"\xC4v"],"\xF8"=>[0,"\xC4w"],"\xF9"=>[0,"\xC4x"],"\xFA"=>[0,"\xC4y"],"\xFB"=>[0,"\xC4z"],"\xFC"=>[82,"\xC4"],"\xFD"=>[87,"\xC4"],"\xFE"=>[130,"\xC4"],"\xFF"=>[9,"\xC4"],],["\x00"=>[77,"\xC00"],"\x01"=>[18,"\xC00"],"\x02"=>[77,"\xC01"],"\x03"=>[18,"\xC01"],"\x04"=>[77,"\xC02"],"\x05"=>[18,"\xC02"],"\x06"=>[77,"\xC0a"],"\x07"=>[18,"\xC0a"],"\x08"=>[77,"\xC0c"],"\x09"=>[18,"\xC0c"],"\x0A"=>[77,"\xC0e"],"\x0B"=>[18,"\xC0e"],"\x0C"=>[77,"\xC0i"],"\x0D"=>[18,"\xC0i"],"\x0E"=>[77,"\xC0o"],"\x0F"=>[18,"\xC0o"],"\x10"=>[77,"\xC0s"],"\x11"=>[18,"\xC0s"],"\x12"=>[77,"\xC0t"],"\x13"=>[18,"\xC0t"],"\x14"=>[0,"\xC0\x20"],"\x15"=>[0,"\xC0\x25"],"\x16"=>[0,"\xC0-"],"\x17"=>[0,"\xC0."],"\x18"=>[0,"\xC0\x2F"],"\x19"=>[0,"\xC03"],"\x1A"=>[0,"\xC04"],"\x1B"=>[0,"\xC05"],"\x1C"=>[0,"\xC06"],"\x1D"=>[0,"\xC07"],"\x1E"=>[0,"\xC08"],"\x1F"=>[0,"\xC09"],"\x20"=>[0,"\xC0\x3D"],"\x21"=>[0,"\xC0A"],"\x22"=>[0,"\xC0_"],"\x23"=>[0,"\xC0b"],"\x24"=>[0,"\xC0d"],"\x25"=>[0,"\xC0f"],"\x26"=>[0,"\xC0g"],"\x27"=>[0,"\xC0h"],"\x28"=>[0,"\xC0l"],"\x29"=>[0,"\xC0m"],"\x2A"=>[0,"\xC0n"],"\x2B"=>[0,"\xC0p"],"\x2C"=>[0,"\xC0r"],"-"=>[0,"\xC0u"],"."=>[100,"\xC0"],"\x2F"=>[110,"\xC0"],[111,"\xC0"],[115,"\xC0"],[116,"\xC0"],[118,"\xC0"],[119,"\xC0"],[122,"\xC0"],[123,"\xC0"],[125,"\xC0"],[126,"\xC0"],[129,"\xC0"],"\x3A"=>[143,"\xC0"],"\x3B"=>[148,"\xC0"],"\x3C"=>[151,"\xC0"],"\x3D"=>[153,"\xC0"],"\x3E"=>[83,"\xC0"],"\x3F"=>[10,"\xC0"],"\x40"=>[77,"\xC10"],"A"=>[18,"\xC10"],"B"=>[77,"\xC11"],"C"=>[18,"\xC11"],"D"=>[77,"\xC12"],"E"=>[18,"\xC12"],"F"=>[77,"\xC1a"],"G"=>[18,"\xC1a"],"H"=>[77,"\xC1c"],"I"=>[18,"\xC1c"],"J"=>[77,"\xC1e"],"K"=>[18,"\xC1e"],"L"=>[77,"\xC1i"],"M"=>[18,"\xC1i"],"N"=>[77,"\xC1o"],"O"=>[18,"\xC1o"],"P"=>[77,"\xC1s"],"Q"=>[18,"\xC1s"],"R"=>[77,"\xC1t"],"S"=>[18,"\xC1t"],"T"=>[0,"\xC1\x20"],"U"=>[0,"\xC1\x25"],"V"=>[0,"\xC1-"],"W"=>[0,"\xC1."],"X"=>[0,"\xC1\x2F"],"Y"=>[0,"\xC13"],"Z"=>[0,"\xC14"],"\x5B"=>[0,"\xC15"],"\x5C"=>[0,"\xC16"],"\x5D"=>[0,"\xC17"],"\x5E"=>[0,"\xC18"],"_"=>[0,"\xC19"],"\x60"=>[0,"\xC1\x3D"],"a"=>[0,"\xC1A"],"b"=>[0,"\xC1_"],"c"=>[0,"\xC1b"],"d"=>[0,"\xC1d"],"e"=>[0,"\xC1f"],"f"=>[0,"\xC1g"],"g"=>[0,"\xC1h"],"h"=>[0,"\xC1l"],"i"=>[0,"\xC1m"],"j"=>[0,"\xC1n"],"k"=>[0,"\xC1p"],"l"=>[0,"\xC1r"],"m"=>[0,"\xC1u"],"n"=>[100,"\xC1"],"o"=>[110,"\xC1"],"p"=>[111,"\xC1"],"q"=>[115,"\xC1"],"r"=>[116,"\xC1"],"s"=>[118,"\xC1"],"t"=>[119,"\xC1"],"u"=>[122,"\xC1"],"v"=>[123,"\xC1"],"w"=>[125,"\xC1"],"x"=>[126,"\xC1"],"y"=>[129,"\xC1"],"z"=>[143,"\xC1"],"\x7B"=>[148,"\xC1"],"\x7C"=>[151,"\xC1"],"\x7D"=>[153,"\xC1"],"~"=>[83,"\xC1"],"\x7F"=>[10,"\xC1"],"\x80"=>[77,"\xC80"],"\x81"=>[18,"\xC80"],"\x82"=>[77,"\xC81"],"\x83"=>[18,"\xC81"],"\x84"=>[77,"\xC82"],"\x85"=>[18,"\xC82"],"\x86"=>[77,"\xC8a"],"\x87"=>[18,"\xC8a"],"\x88"=>[77,"\xC8c"],"\x89"=>[18,"\xC8c"],"\x8A"=>[77,"\xC8e"],"\x8B"=>[18,"\xC8e"],"\x8C"=>[77,"\xC8i"],"\x8D"=>[18,"\xC8i"],"\x8E"=>[77,"\xC8o"],"\x8F"=>[18,"\xC8o"],"\x90"=>[77,"\xC8s"],"\x91"=>[18,"\xC8s"],"\x92"=>[77,"\xC8t"],"\x93"=>[18,"\xC8t"],"\x94"=>[0,"\xC8\x20"],"\x95"=>[0,"\xC8\x25"],"\x96"=>[0,"\xC8-"],"\x97"=>[0,"\xC8."],"\x98"=>[0,"\xC8\x2F"],"\x99"=>[0,"\xC83"],"\x9A"=>[0,"\xC84"],"\x9B"=>[0,"\xC85"],"\x9C"=>[0,"\xC86"],"\x9D"=>[0,"\xC87"],"\x9E"=>[0,"\xC88"],"\x9F"=>[0,"\xC89"],"\xA0"=>[0,"\xC8\x3D"],"\xA1"=>[0,"\xC8A"],"\xA2"=>[0,"\xC8_"],"\xA3"=>[0,"\xC8b"],"\xA4"=>[0,"\xC8d"],"\xA5"=>[0,"\xC8f"],"\xA6"=>[0,"\xC8g"],"\xA7"=>[0,"\xC8h"],"\xA8"=>[0,"\xC8l"],"\xA9"=>[0,"\xC8m"],"\xAA"=>[0,"\xC8n"],"\xAB"=>[0,"\xC8p"],"\xAC"=>[0,"\xC8r"],"\xAD"=>[0,"\xC8u"],"\xAE"=>[100,"\xC8"],"\xAF"=>[110,"\xC8"],"\xB0"=>[111,"\xC8"],"\xB1"=>[115,"\xC8"],"\xB2"=>[116,"\xC8"],"\xB3"=>[118,"\xC8"],"\xB4"=>[119,"\xC8"],"\xB5"=>[122,"\xC8"],"\xB6"=>[123,"\xC8"],"\xB7"=>[125,"\xC8"],"\xB8"=>[126,"\xC8"],"\xB9"=>[129,"\xC8"],"\xBA"=>[143,"\xC8"],"\xBB"=>[148,"\xC8"],"\xBC"=>[151,"\xC8"],"\xBD"=>[153,"\xC8"],"\xBE"=>[83,"\xC8"],"\xBF"=>[10,"\xC8"],"\xC0"=>[77,"\xC90"],"\xC1"=>[18,"\xC90"],"\xC2"=>[77,"\xC91"],"\xC3"=>[18,"\xC91"],"\xC4"=>[77,"\xC92"],"\xC5"=>[18,"\xC92"],"\xC6"=>[77,"\xC9a"],"\xC7"=>[18,"\xC9a"],"\xC8"=>[77,"\xC9c"],"\xC9"=>[18,"\xC9c"],"\xCA"=>[77,"\xC9e"],"\xCB"=>[18,"\xC9e"],"\xCC"=>[77,"\xC9i"],"\xCD"=>[18,"\xC9i"],"\xCE"=>[77,"\xC9o"],"\xCF"=>[18,"\xC9o"],"\xD0"=>[77,"\xC9s"],"\xD1"=>[18,"\xC9s"],"\xD2"=>[77,"\xC9t"],"\xD3"=>[18,"\xC9t"],"\xD4"=>[0,"\xC9\x20"],"\xD5"=>[0,"\xC9\x25"],"\xD6"=>[0,"\xC9-"],"\xD7"=>[0,"\xC9."],"\xD8"=>[0,"\xC9\x2F"],"\xD9"=>[0,"\xC93"],"\xDA"=>[0,"\xC94"],"\xDB"=>[0,"\xC95"],"\xDC"=>[0,"\xC96"],"\xDD"=>[0,"\xC97"],"\xDE"=>[0,"\xC98"],"\xDF"=>[0,"\xC99"],"\xE0"=>[0,"\xC9\x3D"],"\xE1"=>[0,"\xC9A"],"\xE2"=>[0,"\xC9_"],"\xE3"=>[0,"\xC9b"],"\xE4"=>[0,"\xC9d"],"\xE5"=>[0,"\xC9f"],"\xE6"=>[0,"\xC9g"],"\xE7"=>[0,"\xC9h"],"\xE8"=>[0,"\xC9l"],"\xE9"=>[0,"\xC9m"],"\xEA"=>[0,"\xC9n"],"\xEB"=>[0,"\xC9p"],"\xEC"=>[0,"\xC9r"],"\xED"=>[0,"\xC9u"],"\xEE"=>[100,"\xC9"],"\xEF"=>[110,"\xC9"],"\xF0"=>[111,"\xC9"],"\xF1"=>[115,"\xC9"],"\xF2"=>[116,"\xC9"],"\xF3"=>[118,"\xC9"],"\xF4"=>[119,"\xC9"],"\xF5"=>[122,"\xC9"],"\xF6"=>[123,"\xC9"],"\xF7"=>[125,"\xC9"],"\xF8"=>[126,"\xC9"],"\xF9"=>[129,"\xC9"],"\xFA"=>[143,"\xC9"],"\xFB"=>[148,"\xC9"],"\xFC"=>[151,"\xC9"],"\xFD"=>[153,"\xC9"],"\xFE"=>[83,"\xC9"],"\xFF"=>[10,"\xC9"],],["\x00"=>[0,"\xC00"],"\x01"=>[0,"\xC01"],"\x02"=>[0,"\xC02"],"\x03"=>[0,"\xC0a"],"\x04"=>[0,"\xC0c"],"\x05"=>[0,"\xC0e"],"\x06"=>[0,"\xC0i"],"\x07"=>[0,"\xC0o"],"\x08"=>[0,"\xC0s"],"\x09"=>[0,"\xC0t"],"\x0A"=>[73,"\xC0"],"\x0B"=>[88,"\xC0"],"\x0C"=>[89,"\xC0"],"\x0D"=>[96,"\xC0"],"\x0E"=>[97,"\xC0"],"\x0F"=>[99,"\xC0"],"\x10"=>[106,"\xC0"],"\x11"=>[136,"\xC0"],"\x12"=>[139,"\xC0"],"\x13"=>[141,"\xC0"],"\x14"=>[145,"\xC0"],"\x15"=>[147,"\xC0"],"\x16"=>[149,"\xC0"],"\x17"=>[101,"\xC0"],"\x18"=>[112,"\xC0"],"\x19"=>[117,"\xC0"],"\x1A"=>[120,"\xC0"],"\x1B"=>[124,"\xC0"],"\x1C"=>[127,"\xC0"],"\x1D"=>[144,"\xC0"],"\x1E"=>[152,"\xC0"],"\x1F"=>[11,"\xC0"],"\x20"=>[0,"\xC10"],"\x21"=>[0,"\xC11"],"\x22"=>[0,"\xC12"],"\x23"=>[0,"\xC1a"],"\x24"=>[0,"\xC1c"],"\x25"=>[0,"\xC1e"],"\x26"=>[0,"\xC1i"],"\x27"=>[0,"\xC1o"],"\x28"=>[0,"\xC1s"],"\x29"=>[0,"\xC1t"],"\x2A"=>[73,"\xC1"],"\x2B"=>[88,"\xC1"],"\x2C"=>[89,"\xC1"],"-"=>[96,"\xC1"],"."=>[97,"\xC1"],"\x2F"=>[99,"\xC1"],[106,"\xC1"],[136,"\xC1"],[139,"\xC1"],[141,"\xC1"],[145,"\xC1"],[147,"\xC1"],[149,"\xC1"],[101,"\xC1"],[112,"\xC1"],[117,"\xC1"],"\x3A"=>[120,"\xC1"],"\x3B"=>[124,"\xC1"],"\x3C"=>[127,"\xC1"],"\x3D"=>[144,"\xC1"],"\x3E"=>[152,"\xC1"],"\x3F"=>[11,"\xC1"],"\x40"=>[0,"\xC80"],"A"=>[0,"\xC81"],"B"=>[0,"\xC82"],"C"=>[0,"\xC8a"],"D"=>[0,"\xC8c"],"E"=>[0,"\xC8e"],"F"=>[0,"\xC8i"],"G"=>[0,"\xC8o"],"H"=>[0,"\xC8s"],"I"=>[0,"\xC8t"],"J"=>[73,"\xC8"],"K"=>[88,"\xC8"],"L"=>[89,"\xC8"],"M"=>[96,"\xC8"],"N"=>[97,"\xC8"],"O"=>[99,"\xC8"],"P"=>[106,"\xC8"],"Q"=>[136,"\xC8"],"R"=>[139,"\xC8"],"S"=>[141,"\xC8"],"T"=>[145,"\xC8"],"U"=>[147,"\xC8"],"V"=>[149,"\xC8"],"W"=>[101,"\xC8"],"X"=>[112,"\xC8"],"Y"=>[117,"\xC8"],"Z"=>[120,"\xC8"],"\x5B"=>[124,"\xC8"],"\x5C"=>[127,"\xC8"],"\x5D"=>[144,"\xC8"],"\x5E"=>[152,"\xC8"],"_"=>[11,"\xC8"],"\x60"=>[0,"\xC90"],"a"=>[0,"\xC91"],"b"=>[0,"\xC92"],"c"=>[0,"\xC9a"],"d"=>[0,"\xC9c"],"e"=>[0,"\xC9e"],"f"=>[0,"\xC9i"],"g"=>[0,"\xC9o"],"h"=>[0,"\xC9s"],"i"=>[0,"\xC9t"],"j"=>[73,"\xC9"],"k"=>[88,"\xC9"],"l"=>[89,"\xC9"],"m"=>[96,"\xC9"],"n"=>[97,"\xC9"],"o"=>[99,"\xC9"],"p"=>[106,"\xC9"],"q"=>[136,"\xC9"],"r"=>[139,"\xC9"],"s"=>[141,"\xC9"],"t"=>[145,"\xC9"],"u"=>[147,"\xC9"],"v"=>[149,"\xC9"],"w"=>[101,"\xC9"],"x"=>[112,"\xC9"],"y"=>[117,"\xC9"],"z"=>[120,"\xC9"],"\x7B"=>[124,"\xC9"],"\x7C"=>[127,"\xC9"],"\x7D"=>[144,"\xC9"],"~"=>[152,"\xC9"],"\x7F"=>[11,"\xC9"],"\x80"=>[0,"\xCA0"],"\x81"=>[0,"\xCA1"],"\x82"=>[0,"\xCA2"],"\x83"=>[0,"\xCAa"],"\x84"=>[0,"\xCAc"],"\x85"=>[0,"\xCAe"],"\x86"=>[0,"\xCAi"],"\x87"=>[0,"\xCAo"],"\x88"=>[0,"\xCAs"],"\x89"=>[0,"\xCAt"],"\x8A"=>[73,"\xCA"],"\x8B"=>[88,"\xCA"],"\x8C"=>[89,"\xCA"],"\x8D"=>[96,"\xCA"],"\x8E"=>[97,"\xCA"],"\x8F"=>[99,"\xCA"],"\x90"=>[106,"\xCA"],"\x91"=>[136,"\xCA"],"\x92"=>[139,"\xCA"],"\x93"=>[141,"\xCA"],"\x94"=>[145,"\xCA"],"\x95"=>[147,"\xCA"],"\x96"=>[149,"\xCA"],"\x97"=>[101,"\xCA"],"\x98"=>[112,"\xCA"],"\x99"=>[117,"\xCA"],"\x9A"=>[120,"\xCA"],"\x9B"=>[124,"\xCA"],"\x9C"=>[127,"\xCA"],"\x9D"=>[144,"\xCA"],"\x9E"=>[152,"\xCA"],"\x9F"=>[11,"\xCA"],"\xA0"=>[0,"\xCD0"],"\xA1"=>[0,"\xCD1"],"\xA2"=>[0,"\xCD2"],"\xA3"=>[0,"\xCDa"],"\xA4"=>[0,"\xCDc"],"\xA5"=>[0,"\xCDe"],"\xA6"=>[0,"\xCDi"],"\xA7"=>[0,"\xCDo"],"\xA8"=>[0,"\xCDs"],"\xA9"=>[0,"\xCDt"],"\xAA"=>[73,"\xCD"],"\xAB"=>[88,"\xCD"],"\xAC"=>[89,"\xCD"],"\xAD"=>[96,"\xCD"],"\xAE"=>[97,"\xCD"],"\xAF"=>[99,"\xCD"],"\xB0"=>[106,"\xCD"],"\xB1"=>[136,"\xCD"],"\xB2"=>[139,"\xCD"],"\xB3"=>[141,"\xCD"],"\xB4"=>[145,"\xCD"],"\xB5"=>[147,"\xCD"],"\xB6"=>[149,"\xCD"],"\xB7"=>[101,"\xCD"],"\xB8"=>[112,"\xCD"],"\xB9"=>[117,"\xCD"],"\xBA"=>[120,"\xCD"],"\xBB"=>[124,"\xCD"],"\xBC"=>[127,"\xCD"],"\xBD"=>[144,"\xCD"],"\xBE"=>[152,"\xCD"],"\xBF"=>[11,"\xCD"],"\xC0"=>[0,"\xD20"],"\xC1"=>[0,"\xD21"],"\xC2"=>[0,"\xD22"],"\xC3"=>[0,"\xD2a"],"\xC4"=>[0,"\xD2c"],"\xC5"=>[0,"\xD2e"],"\xC6"=>[0,"\xD2i"],"\xC7"=>[0,"\xD2o"],"\xC8"=>[0,"\xD2s"],"\xC9"=>[0,"\xD2t"],"\xCA"=>[73,"\xD2"],"\xCB"=>[88,"\xD2"],"\xCC"=>[89,"\xD2"],"\xCD"=>[96,"\xD2"],"\xCE"=>[97,"\xD2"],"\xCF"=>[99,"\xD2"],"\xD0"=>[106,"\xD2"],"\xD1"=>[136,"\xD2"],"\xD2"=>[139,"\xD2"],"\xD3"=>[141,"\xD2"],"\xD4"=>[145,"\xD2"],"\xD5"=>[147,"\xD2"],"\xD6"=>[149,"\xD2"],"\xD7"=>[101,"\xD2"],"\xD8"=>[112,"\xD2"],"\xD9"=>[117,"\xD2"],"\xDA"=>[120,"\xD2"],"\xDB"=>[124,"\xD2"],"\xDC"=>[127,"\xD2"],"\xDD"=>[144,"\xD2"],"\xDE"=>[152,"\xD2"],"\xDF"=>[11,"\xD2"],"\xE0"=>[0,"\xD50"],"\xE1"=>[0,"\xD51"],"\xE2"=>[0,"\xD52"],"\xE3"=>[0,"\xD5a"],"\xE4"=>[0,"\xD5c"],"\xE5"=>[0,"\xD5e"],"\xE6"=>[0,"\xD5i"],"\xE7"=>[0,"\xD5o"],"\xE8"=>[0,"\xD5s"],"\xE9"=>[0,"\xD5t"],"\xEA"=>[73,"\xD5"],"\xEB"=>[88,"\xD5"],"\xEC"=>[89,"\xD5"],"\xED"=>[96,"\xD5"],"\xEE"=>[97,"\xD5"],"\xEF"=>[99,"\xD5"],"\xF0"=>[106,"\xD5"],"\xF1"=>[136,"\xD5"],"\xF2"=>[139,"\xD5"],"\xF3"=>[141,"\xD5"],"\xF4"=>[145,"\xD5"],"\xF5"=>[147,"\xD5"],"\xF6"=>[149,"\xD5"],"\xF7"=>[101,"\xD5"],"\xF8"=>[112,"\xD5"],"\xF9"=>[117,"\xD5"],"\xFA"=>[120,"\xD5"],"\xFB"=>[124,"\xD5"],"\xFC"=>[127,"\xD5"],"\xFD"=>[144,"\xD5"],"\xFE"=>[152,"\xD5"],"\xFF"=>[11,"\xD5"],],["\x00"=>[92,"\xC0"],"\x01"=>[95,"\xC0"],"\x02"=>[137,"\xC0"],"\x03"=>[142,"\xC0"],"\x04"=>[150,"\xC0"],"\x05"=>[74,"\xC0"],"\x06"=>[90,"\xC0"],"\x07"=>[98,"\xC0"],"\x08"=>[107,"\xC0"],"\x09"=>[140,"\xC0"],"\x0A"=>[146,"\xC0"],"\x0B"=>[102,"\xC0"],"\x0C"=>[113,"\xC0"],"\x0D"=>[121,"\xC0"],"\x0E"=>[128,"\xC0"],"\x0F"=>[12,"\xC0"],"\x10"=>[92,"\xC1"],"\x11"=>[95,"\xC1"],"\x12"=>[137,"\xC1"],"\x13"=>[142,"\xC1"],"\x14"=>[150,"\xC1"],"\x15"=>[74,"\xC1"],"\x16"=>[90,"\xC1"],"\x17"=>[98,"\xC1"],"\x18"=>[107,"\xC1"],"\x19"=>[140,"\xC1"],"\x1A"=>[146,"\xC1"],"\x1B"=>[102,"\xC1"],"\x1C"=>[113,"\xC1"],"\x1D"=>[121,"\xC1"],"\x1E"=>[128,"\xC1"],"\x1F"=>[12,"\xC1"],"\x20"=>[92,"\xC8"],"\x21"=>[95,"\xC8"],"\x22"=>[137,"\xC8"],"\x23"=>[142,"\xC8"],"\x24"=>[150,"\xC8"],"\x25"=>[74,"\xC8"],"\x26"=>[90,"\xC8"],"\x27"=>[98,"\xC8"],"\x28"=>[107,"\xC8"],"\x29"=>[140,"\xC8"],"\x2A"=>[146,"\xC8"],"\x2B"=>[102,"\xC8"],"\x2C"=>[113,"\xC8"],"-"=>[121,"\xC8"],"."=>[128,"\xC8"],"\x2F"=>[12,"\xC8"],[92,"\xC9"],[95,"\xC9"],[137,"\xC9"],[142,"\xC9"],[150,"\xC9"],[74,"\xC9"],[90,"\xC9"],[98,"\xC9"],[107,"\xC9"],[140,"\xC9"],"\x3A"=>[146,"\xC9"],"\x3B"=>[102,"\xC9"],"\x3C"=>[113,"\xC9"],"\x3D"=>[121,"\xC9"],"\x3E"=>[128,"\xC9"],"\x3F"=>[12,"\xC9"],"\x40"=>[92,"\xCA"],"A"=>[95,"\xCA"],"B"=>[137,"\xCA"],"C"=>[142,"\xCA"],"D"=>[150,"\xCA"],"E"=>[74,"\xCA"],"F"=>[90,"\xCA"],"G"=>[98,"\xCA"],"H"=>[107,"\xCA"],"I"=>[140,"\xCA"],"J"=>[146,"\xCA"],"K"=>[102,"\xCA"],"L"=>[113,"\xCA"],"M"=>[121,"\xCA"],"N"=>[128,"\xCA"],"O"=>[12,"\xCA"],"P"=>[92,"\xCD"],"Q"=>[95,"\xCD"],"R"=>[137,"\xCD"],"S"=>[142,"\xCD"],"T"=>[150,"\xCD"],"U"=>[74,"\xCD"],"V"=>[90,"\xCD"],"W"=>[98,"\xCD"],"X"=>[107,"\xCD"],"Y"=>[140,"\xCD"],"Z"=>[146,"\xCD"],"\x5B"=>[102,"\xCD"],"\x5C"=>[113,"\xCD"],"\x5D"=>[121,"\xCD"],"\x5E"=>[128,"\xCD"],"_"=>[12,"\xCD"],"\x60"=>[92,"\xD2"],"a"=>[95,"\xD2"],"b"=>[137,"\xD2"],"c"=>[142,"\xD2"],"d"=>[150,"\xD2"],"e"=>[74,"\xD2"],"f"=>[90,"\xD2"],"g"=>[98,"\xD2"],"h"=>[107,"\xD2"],"i"=>[140,"\xD2"],"j"=>[146,"\xD2"],"k"=>[102,"\xD2"],"l"=>[113,"\xD2"],"m"=>[121,"\xD2"],"n"=>[128,"\xD2"],"o"=>[12,"\xD2"],"p"=>[92,"\xD5"],"q"=>[95,"\xD5"],"r"=>[137,"\xD5"],"s"=>[142,"\xD5"],"t"=>[150,"\xD5"],"u"=>[74,"\xD5"],"v"=>[90,"\xD5"],"w"=>[98,"\xD5"],"x"=>[107,"\xD5"],"y"=>[140,"\xD5"],"z"=>[146,"\xD5"],"\x7B"=>[102,"\xD5"],"\x7C"=>[113,"\xD5"],"\x7D"=>[121,"\xD5"],"~"=>[128,"\xD5"],"\x7F"=>[12,"\xD5"],"\x80"=>[92,"\xDA"],"\x81"=>[95,"\xDA"],"\x82"=>[137,"\xDA"],"\x83"=>[142,"\xDA"],"\x84"=>[150,"\xDA"],"\x85"=>[74,"\xDA"],"\x86"=>[90,"\xDA"],"\x87"=>[98,"\xDA"],"\x88"=>[107,"\xDA"],"\x89"=>[140,"\xDA"],"\x8A"=>[146,"\xDA"],"\x8B"=>[102,"\xDA"],"\x8C"=>[113,"\xDA"],"\x8D"=>[121,"\xDA"],"\x8E"=>[128,"\xDA"],"\x8F"=>[12,"\xDA"],"\x90"=>[92,"\xDB"],"\x91"=>[95,"\xDB"],"\x92"=>[137,"\xDB"],"\x93"=>[142,"\xDB"],"\x94"=>[150,"\xDB"],"\x95"=>[74,"\xDB"],"\x96"=>[90,"\xDB"],"\x97"=>[98,"\xDB"],"\x98"=>[107,"\xDB"],"\x99"=>[140,"\xDB"],"\x9A"=>[146,"\xDB"],"\x9B"=>[102,"\xDB"],"\x9C"=>[113,"\xDB"],"\x9D"=>[121,"\xDB"],"\x9E"=>[128,"\xDB"],"\x9F"=>[12,"\xDB"],"\xA0"=>[92,"\xEE"],"\xA1"=>[95,"\xEE"],"\xA2"=>[137,"\xEE"],"\xA3"=>[142,"\xEE"],"\xA4"=>[150,"\xEE"],"\xA5"=>[74,"\xEE"],"\xA6"=>[90,"\xEE"],"\xA7"=>[98,"\xEE"],"\xA8"=>[107,"\xEE"],"\xA9"=>[140,"\xEE"],"\xAA"=>[146,"\xEE"],"\xAB"=>[102,"\xEE"],"\xAC"=>[113,"\xEE"],"\xAD"=>[121,"\xEE"],"\xAE"=>[128,"\xEE"],"\xAF"=>[12,"\xEE"],"\xB0"=>[92,"\xF0"],"\xB1"=>[95,"\xF0"],"\xB2"=>[137,"\xF0"],"\xB3"=>[142,"\xF0"],"\xB4"=>[150,"\xF0"],"\xB5"=>[74,"\xF0"],"\xB6"=>[90,"\xF0"],"\xB7"=>[98,"\xF0"],"\xB8"=>[107,"\xF0"],"\xB9"=>[140,"\xF0"],"\xBA"=>[146,"\xF0"],"\xBB"=>[102,"\xF0"],"\xBC"=>[113,"\xF0"],"\xBD"=>[121,"\xF0"],"\xBE"=>[128,"\xF0"],"\xBF"=>[12,"\xF0"],"\xC0"=>[92,"\xF2"],"\xC1"=>[95,"\xF2"],"\xC2"=>[137,"\xF2"],"\xC3"=>[142,"\xF2"],"\xC4"=>[150,"\xF2"],"\xC5"=>[74,"\xF2"],"\xC6"=>[90,"\xF2"],"\xC7"=>[98,"\xF2"],"\xC8"=>[107,"\xF2"],"\xC9"=>[140,"\xF2"],"\xCA"=>[146,"\xF2"],"\xCB"=>[102,"\xF2"],"\xCC"=>[113,"\xF2"],"\xCD"=>[121,"\xF2"],"\xCE"=>[128,"\xF2"],"\xCF"=>[12,"\xF2"],"\xD0"=>[92,"\xF3"],"\xD1"=>[95,"\xF3"],"\xD2"=>[137,"\xF3"],"\xD3"=>[142,"\xF3"],"\xD4"=>[150,"\xF3"],"\xD5"=>[74,"\xF3"],"\xD6"=>[90,"\xF3"],"\xD7"=>[98,"\xF3"],"\xD8"=>[107,"\xF3"],"\xD9"=>[140,"\xF3"],"\xDA"=>[146,"\xF3"],"\xDB"=>[102,"\xF3"],"\xDC"=>[113,"\xF3"],"\xDD"=>[121,"\xF3"],"\xDE"=>[128,"\xF3"],"\xDF"=>[12,"\xF3"],"\xE0"=>[92,"\xFF"],"\xE1"=>[95,"\xFF"],"\xE2"=>[137,"\xFF"],"\xE3"=>[142,"\xFF"],"\xE4"=>[150,"\xFF"],"\xE5"=>[74,"\xFF"],"\xE6"=>[90,"\xFF"],"\xE7"=>[98,"\xFF"],"\xE8"=>[107,"\xFF"],"\xE9"=>[140,"\xFF"],"\xEA"=>[146,"\xFF"],"\xEB"=>[102,"\xFF"],"\xEC"=>[113,"\xFF"],"\xED"=>[121,"\xFF"],"\xEE"=>[128,"\xFF"],"\xEF"=>[12,"\xFF"],"\xF0"=>[93,"\xCB"],"\xF1"=>[138,"\xCB"],"\xF2"=>[75,"\xCB"],"\xF3"=>[91,"\xCB"],"\xF4"=>[108,"\xCB"],"\xF5"=>[103,"\xCB"],"\xF6"=>[114,"\xCB"],"\xF7"=>[14,"\xCB"],"\xF8"=>[93,"\xCC"],"\xF9"=>[138,"\xCC"],"\xFA"=>[75,"\xCC"],"\xFB"=>[91,"\xCC"],"\xFC"=>[108,"\xCC"],"\xFD"=>[103,"\xCC"],"\xFE"=>[114,"\xCC"],"\xFF"=>[14,"\xCC"],],["\x00"=>[94,"\xC00"],"\x01"=>[76,"\xC00"],"\x02"=>[104,"\xC00"],"\x03"=>[16,"\xC00"],"\x04"=>[94,"\xC01"],"\x05"=>[76,"\xC01"],"\x06"=>[104,"\xC01"],"\x07"=>[16,"\xC01"],"\x08"=>[94,"\xC02"],"\x09"=>[76,"\xC02"],"\x0A"=>[104,"\xC02"],"\x0B"=>[16,"\xC02"],"\x0C"=>[94,"\xC0a"],"\x0D"=>[76,"\xC0a"],"\x0E"=>[104,"\xC0a"],"\x0F"=>[16,"\xC0a"],"\x10"=>[94,"\xC0c"],"\x11"=>[76,"\xC0c"],"\x12"=>[104,"\xC0c"],"\x13"=>[16,"\xC0c"],"\x14"=>[94,"\xC0e"],"\x15"=>[76,"\xC0e"],"\x16"=>[104,"\xC0e"],"\x17"=>[16,"\xC0e"],"\x18"=>[94,"\xC0i"],"\x19"=>[76,"\xC0i"],"\x1A"=>[104,"\xC0i"],"\x1B"=>[16,"\xC0i"],"\x1C"=>[94,"\xC0o"],"\x1D"=>[76,"\xC0o"],"\x1E"=>[104,"\xC0o"],"\x1F"=>[16,"\xC0o"],"\x20"=>[94,"\xC0s"],"\x21"=>[76,"\xC0s"],"\x22"=>[104,"\xC0s"],"\x23"=>[16,"\xC0s"],"\x24"=>[94,"\xC0t"],"\x25"=>[76,"\xC0t"],"\x26"=>[104,"\xC0t"],"\x27"=>[16,"\xC0t"],"\x28"=>[77,"\xC0\x20"],"\x29"=>[18,"\xC0\x20"],"\x2A"=>[77,"\xC0\x25"],"\x2B"=>[18,"\xC0\x25"],"\x2C"=>[77,"\xC0-"],"-"=>[18,"\xC0-"],"."=>[77,"\xC0."],"\x2F"=>[18,"\xC0."],[77,"\xC0\x2F"],[18,"\xC0\x2F"],[77,"\xC03"],[18,"\xC03"],[77,"\xC04"],[18,"\xC04"],[77,"\xC05"],[18,"\xC05"],[77,"\xC06"],[18,"\xC06"],"\x3A"=>[77,"\xC07"],"\x3B"=>[18,"\xC07"],"\x3C"=>[77,"\xC08"],"\x3D"=>[18,"\xC08"],"\x3E"=>[77,"\xC09"],"\x3F"=>[18,"\xC09"],"\x40"=>[77,"\xC0\x3D"],"A"=>[18,"\xC0\x3D"],"B"=>[77,"\xC0A"],"C"=>[18,"\xC0A"],"D"=>[77,"\xC0_"],"E"=>[18,"\xC0_"],"F"=>[77,"\xC0b"],"G"=>[18,"\xC0b"],"H"=>[77,"\xC0d"],"I"=>[18,"\xC0d"],"J"=>[77,"\xC0f"],"K"=>[18,"\xC0f"],"L"=>[77,"\xC0g"],"M"=>[18,"\xC0g"],"N"=>[77,"\xC0h"],"O"=>[18,"\xC0h"],"P"=>[77,"\xC0l"],"Q"=>[18,"\xC0l"],"R"=>[77,"\xC0m"],"S"=>[18,"\xC0m"],"T"=>[77,"\xC0n"],"U"=>[18,"\xC0n"],"V"=>[77,"\xC0p"],"W"=>[18,"\xC0p"],"X"=>[77,"\xC0r"],"Y"=>[18,"\xC0r"],"Z"=>[77,"\xC0u"],"\x5B"=>[18,"\xC0u"],"\x5C"=>[0,"\xC0\x3A"],"\x5D"=>[0,"\xC0B"],"\x5E"=>[0,"\xC0C"],"_"=>[0,"\xC0D"],"\x60"=>[0,"\xC0E"],"a"=>[0,"\xC0F"],"b"=>[0,"\xC0G"],"c"=>[0,"\xC0H"],"d"=>[0,"\xC0I"],"e"=>[0,"\xC0J"],"f"=>[0,"\xC0K"],"g"=>[0,"\xC0L"],"h"=>[0,"\xC0M"],"i"=>[0,"\xC0N"],"j"=>[0,"\xC0O"],"k"=>[0,"\xC0P"],"l"=>[0,"\xC0Q"],"m"=>[0,"\xC0R"],"n"=>[0,"\xC0S"],"o"=>[0,"\xC0T"],"p"=>[0,"\xC0U"],"q"=>[0,"\xC0V"],"r"=>[0,"\xC0W"],"s"=>[0,"\xC0Y"],"t"=>[0,"\xC0j"],"u"=>[0,"\xC0k"],"v"=>[0,"\xC0q"],"w"=>[0,"\xC0v"],"x"=>[0,"\xC0w"],"y"=>[0,"\xC0x"],"z"=>[0,"\xC0y"],"\x7B"=>[0,"\xC0z"],"\x7C"=>[82,"\xC0"],"\x7D"=>[87,"\xC0"],"~"=>[130,"\xC0"],"\x7F"=>[9,"\xC0"],"\x80"=>[94,"\xC10"],"\x81"=>[76,"\xC10"],"\x82"=>[104,"\xC10"],"\x83"=>[16,"\xC10"],"\x84"=>[94,"\xC11"],"\x85"=>[76,"\xC11"],"\x86"=>[104,"\xC11"],"\x87"=>[16,"\xC11"],"\x88"=>[94,"\xC12"],"\x89"=>[76,"\xC12"],"\x8A"=>[104,"\xC12"],"\x8B"=>[16,"\xC12"],"\x8C"=>[94,"\xC1a"],"\x8D"=>[76,"\xC1a"],"\x8E"=>[104,"\xC1a"],"\x8F"=>[16,"\xC1a"],"\x90"=>[94,"\xC1c"],"\x91"=>[76,"\xC1c"],"\x92"=>[104,"\xC1c"],"\x93"=>[16,"\xC1c"],"\x94"=>[94,"\xC1e"],"\x95"=>[76,"\xC1e"],"\x96"=>[104,"\xC1e"],"\x97"=>[16,"\xC1e"],"\x98"=>[94,"\xC1i"],"\x99"=>[76,"\xC1i"],"\x9A"=>[104,"\xC1i"],"\x9B"=>[16,"\xC1i"],"\x9C"=>[94,"\xC1o"],"\x9D"=>[76,"\xC1o"],"\x9E"=>[104,"\xC1o"],"\x9F"=>[16,"\xC1o"],"\xA0"=>[94,"\xC1s"],"\xA1"=>[76,"\xC1s"],"\xA2"=>[104,"\xC1s"],"\xA3"=>[16,"\xC1s"],"\xA4"=>[94,"\xC1t"],"\xA5"=>[76,"\xC1t"],"\xA6"=>[104,"\xC1t"],"\xA7"=>[16,"\xC1t"],"\xA8"=>[77,"\xC1\x20"],"\xA9"=>[18,"\xC1\x20"],"\xAA"=>[77,"\xC1\x25"],"\xAB"=>[18,"\xC1\x25"],"\xAC"=>[77,"\xC1-"],"\xAD"=>[18,"\xC1-"],"\xAE"=>[77,"\xC1."],"\xAF"=>[18,"\xC1."],"\xB0"=>[77,"\xC1\x2F"],"\xB1"=>[18,"\xC1\x2F"],"\xB2"=>[77,"\xC13"],"\xB3"=>[18,"\xC13"],"\xB4"=>[77,"\xC14"],"\xB5"=>[18,"\xC14"],"\xB6"=>[77,"\xC15"],"\xB7"=>[18,"\xC15"],"\xB8"=>[77,"\xC16"],"\xB9"=>[18,"\xC16"],"\xBA"=>[77,"\xC17"],"\xBB"=>[18,"\xC17"],"\xBC"=>[77,"\xC18"],"\xBD"=>[18,"\xC18"],"\xBE"=>[77,"\xC19"],"\xBF"=>[18,"\xC19"],"\xC0"=>[77,"\xC1\x3D"],"\xC1"=>[18,"\xC1\x3D"],"\xC2"=>[77,"\xC1A"],"\xC3"=>[18,"\xC1A"],"\xC4"=>[77,"\xC1_"],"\xC5"=>[18,"\xC1_"],"\xC6"=>[77,"\xC1b"],"\xC7"=>[18,"\xC1b"],"\xC8"=>[77,"\xC1d"],"\xC9"=>[18,"\xC1d"],"\xCA"=>[77,"\xC1f"],"\xCB"=>[18,"\xC1f"],"\xCC"=>[77,"\xC1g"],"\xCD"=>[18,"\xC1g"],"\xCE"=>[77,"\xC1h"],"\xCF"=>[18,"\xC1h"],"\xD0"=>[77,"\xC1l"],"\xD1"=>[18,"\xC1l"],"\xD2"=>[77,"\xC1m"],"\xD3"=>[18,"\xC1m"],"\xD4"=>[77,"\xC1n"],"\xD5"=>[18,"\xC1n"],"\xD6"=>[77,"\xC1p"],"\xD7"=>[18,"\xC1p"],"\xD8"=>[77,"\xC1r"],"\xD9"=>[18,"\xC1r"],"\xDA"=>[77,"\xC1u"],"\xDB"=>[18,"\xC1u"],"\xDC"=>[0,"\xC1\x3A"],"\xDD"=>[0,"\xC1B"],"\xDE"=>[0,"\xC1C"],"\xDF"=>[0,"\xC1D"],"\xE0"=>[0,"\xC1E"],"\xE1"=>[0,"\xC1F"],"\xE2"=>[0,"\xC1G"],"\xE3"=>[0,"\xC1H"],"\xE4"=>[0,"\xC1I"],"\xE5"=>[0,"\xC1J"],"\xE6"=>[0,"\xC1K"],"\xE7"=>[0,"\xC1L"],"\xE8"=>[0,"\xC1M"],"\xE9"=>[0,"\xC1N"],"\xEA"=>[0,"\xC1O"],"\xEB"=>[0,"\xC1P"],"\xEC"=>[0,"\xC1Q"],"\xED"=>[0,"\xC1R"],"\xEE"=>[0,"\xC1S"],"\xEF"=>[0,"\xC1T"],"\xF0"=>[0,"\xC1U"],"\xF1"=>[0,"\xC1V"],"\xF2"=>[0,"\xC1W"],"\xF3"=>[0,"\xC1Y"],"\xF4"=>[0,"\xC1j"],"\xF5"=>[0,"\xC1k"],"\xF6"=>[0,"\xC1q"],"\xF7"=>[0,"\xC1v"],"\xF8"=>[0,"\xC1w"],"\xF9"=>[0,"\xC1x"],"\xFA"=>[0,"\xC1y"],"\xFB"=>[0,"\xC1z"],"\xFC"=>[82,"\xC1"],"\xFD"=>[87,"\xC1"],"\xFE"=>[130,"\xC1"],"\xFF"=>[9,"\xC1"],],["\x00"=>[94,"\xC50"],"\x01"=>[76,"\xC50"],"\x02"=>[104,"\xC50"],"\x03"=>[16,"\xC50"],"\x04"=>[94,"\xC51"],"\x05"=>[76,"\xC51"],"\x06"=>[104,"\xC51"],"\x07"=>[16,"\xC51"],"\x08"=>[94,"\xC52"],"\x09"=>[76,"\xC52"],"\x0A"=>[104,"\xC52"],"\x0B"=>[16,"\xC52"],"\x0C"=>[94,"\xC5a"],"\x0D"=>[76,"\xC5a"],"\x0E"=>[104,"\xC5a"],"\x0F"=>[16,"\xC5a"],"\x10"=>[94,"\xC5c"],"\x11"=>[76,"\xC5c"],"\x12"=>[104,"\xC5c"],"\x13"=>[16,"\xC5c"],"\x14"=>[94,"\xC5e"],"\x15"=>[76,"\xC5e"],"\x16"=>[104,"\xC5e"],"\x17"=>[16,"\xC5e"],"\x18"=>[94,"\xC5i"],"\x19"=>[76,"\xC5i"],"\x1A"=>[104,"\xC5i"],"\x1B"=>[16,"\xC5i"],"\x1C"=>[94,"\xC5o"],"\x1D"=>[76,"\xC5o"],"\x1E"=>[104,"\xC5o"],"\x1F"=>[16,"\xC5o"],"\x20"=>[94,"\xC5s"],"\x21"=>[76,"\xC5s"],"\x22"=>[104,"\xC5s"],"\x23"=>[16,"\xC5s"],"\x24"=>[94,"\xC5t"],"\x25"=>[76,"\xC5t"],"\x26"=>[104,"\xC5t"],"\x27"=>[16,"\xC5t"],"\x28"=>[77,"\xC5\x20"],"\x29"=>[18,"\xC5\x20"],"\x2A"=>[77,"\xC5\x25"],"\x2B"=>[18,"\xC5\x25"],"\x2C"=>[77,"\xC5-"],"-"=>[18,"\xC5-"],"."=>[77,"\xC5."],"\x2F"=>[18,"\xC5."],[77,"\xC5\x2F"],[18,"\xC5\x2F"],[77,"\xC53"],[18,"\xC53"],[77,"\xC54"],[18,"\xC54"],[77,"\xC55"],[18,"\xC55"],[77,"\xC56"],[18,"\xC56"],"\x3A"=>[77,"\xC57"],"\x3B"=>[18,"\xC57"],"\x3C"=>[77,"\xC58"],"\x3D"=>[18,"\xC58"],"\x3E"=>[77,"\xC59"],"\x3F"=>[18,"\xC59"],"\x40"=>[77,"\xC5\x3D"],"A"=>[18,"\xC5\x3D"],"B"=>[77,"\xC5A"],"C"=>[18,"\xC5A"],"D"=>[77,"\xC5_"],"E"=>[18,"\xC5_"],"F"=>[77,"\xC5b"],"G"=>[18,"\xC5b"],"H"=>[77,"\xC5d"],"I"=>[18,"\xC5d"],"J"=>[77,"\xC5f"],"K"=>[18,"\xC5f"],"L"=>[77,"\xC5g"],"M"=>[18,"\xC5g"],"N"=>[77,"\xC5h"],"O"=>[18,"\xC5h"],"P"=>[77,"\xC5l"],"Q"=>[18,"\xC5l"],"R"=>[77,"\xC5m"],"S"=>[18,"\xC5m"],"T"=>[77,"\xC5n"],"U"=>[18,"\xC5n"],"V"=>[77,"\xC5p"],"W"=>[18,"\xC5p"],"X"=>[77,"\xC5r"],"Y"=>[18,"\xC5r"],"Z"=>[77,"\xC5u"],"\x5B"=>[18,"\xC5u"],"\x5C"=>[0,"\xC5\x3A"],"\x5D"=>[0,"\xC5B"],"\x5E"=>[0,"\xC5C"],"_"=>[0,"\xC5D"],"\x60"=>[0,"\xC5E"],"a"=>[0,"\xC5F"],"b"=>[0,"\xC5G"],"c"=>[0,"\xC5H"],"d"=>[0,"\xC5I"],"e"=>[0,"\xC5J"],"f"=>[0,"\xC5K"],"g"=>[0,"\xC5L"],"h"=>[0,"\xC5M"],"i"=>[0,"\xC5N"],"j"=>[0,"\xC5O"],"k"=>[0,"\xC5P"],"l"=>[0,"\xC5Q"],"m"=>[0,"\xC5R"],"n"=>[0,"\xC5S"],"o"=>[0,"\xC5T"],"p"=>[0,"\xC5U"],"q"=>[0,"\xC5V"],"r"=>[0,"\xC5W"],"s"=>[0,"\xC5Y"],"t"=>[0,"\xC5j"],"u"=>[0,"\xC5k"],"v"=>[0,"\xC5q"],"w"=>[0,"\xC5v"],"x"=>[0,"\xC5w"],"y"=>[0,"\xC5x"],"z"=>[0,"\xC5y"],"\x7B"=>[0,"\xC5z"],"\x7C"=>[82,"\xC5"],"\x7D"=>[87,"\xC5"],"~"=>[130,"\xC5"],"\x7F"=>[9,"\xC5"],"\x80"=>[94,"\xE70"],"\x81"=>[76,"\xE70"],"\x82"=>[104,"\xE70"],"\x83"=>[16,"\xE70"],"\x84"=>[94,"\xE71"],"\x85"=>[76,"\xE71"],"\x86"=>[104,"\xE71"],"\x87"=>[16,"\xE71"],"\x88"=>[94,"\xE72"],"\x89"=>[76,"\xE72"],"\x8A"=>[104,"\xE72"],"\x8B"=>[16,"\xE72"],"\x8C"=>[94,"\xE7a"],"\x8D"=>[76,"\xE7a"],"\x8E"=>[104,"\xE7a"],"\x8F"=>[16,"\xE7a"],"\x90"=>[94,"\xE7c"],"\x91"=>[76,"\xE7c"],"\x92"=>[104,"\xE7c"],"\x93"=>[16,"\xE7c"],"\x94"=>[94,"\xE7e"],"\x95"=>[76,"\xE7e"],"\x96"=>[104,"\xE7e"],"\x97"=>[16,"\xE7e"],"\x98"=>[94,"\xE7i"],"\x99"=>[76,"\xE7i"],"\x9A"=>[104,"\xE7i"],"\x9B"=>[16,"\xE7i"],"\x9C"=>[94,"\xE7o"],"\x9D"=>[76,"\xE7o"],"\x9E"=>[104,"\xE7o"],"\x9F"=>[16,"\xE7o"],"\xA0"=>[94,"\xE7s"],"\xA1"=>[76,"\xE7s"],"\xA2"=>[104,"\xE7s"],"\xA3"=>[16,"\xE7s"],"\xA4"=>[94,"\xE7t"],"\xA5"=>[76,"\xE7t"],"\xA6"=>[104,"\xE7t"],"\xA7"=>[16,"\xE7t"],"\xA8"=>[77,"\xE7\x20"],"\xA9"=>[18,"\xE7\x20"],"\xAA"=>[77,"\xE7\x25"],"\xAB"=>[18,"\xE7\x25"],"\xAC"=>[77,"\xE7-"],"\xAD"=>[18,"\xE7-"],"\xAE"=>[77,"\xE7."],"\xAF"=>[18,"\xE7."],"\xB0"=>[77,"\xE7\x2F"],"\xB1"=>[18,"\xE7\x2F"],"\xB2"=>[77,"\xE73"],"\xB3"=>[18,"\xE73"],"\xB4"=>[77,"\xE74"],"\xB5"=>[18,"\xE74"],"\xB6"=>[77,"\xE75"],"\xB7"=>[18,"\xE75"],"\xB8"=>[77,"\xE76"],"\xB9"=>[18,"\xE76"],"\xBA"=>[77,"\xE77"],"\xBB"=>[18,"\xE77"],"\xBC"=>[77,"\xE78"],"\xBD"=>[18,"\xE78"],"\xBE"=>[77,"\xE79"],"\xBF"=>[18,"\xE79"],"\xC0"=>[77,"\xE7\x3D"],"\xC1"=>[18,"\xE7\x3D"],"\xC2"=>[77,"\xE7A"],"\xC3"=>[18,"\xE7A"],"\xC4"=>[77,"\xE7_"],"\xC5"=>[18,"\xE7_"],"\xC6"=>[77,"\xE7b"],"\xC7"=>[18,"\xE7b"],"\xC8"=>[77,"\xE7d"],"\xC9"=>[18,"\xE7d"],"\xCA"=>[77,"\xE7f"],"\xCB"=>[18,"\xE7f"],"\xCC"=>[77,"\xE7g"],"\xCD"=>[18,"\xE7g"],"\xCE"=>[77,"\xE7h"],"\xCF"=>[18,"\xE7h"],"\xD0"=>[77,"\xE7l"],"\xD1"=>[18,"\xE7l"],"\xD2"=>[77,"\xE7m"],"\xD3"=>[18,"\xE7m"],"\xD4"=>[77,"\xE7n"],"\xD5"=>[18,"\xE7n"],"\xD6"=>[77,"\xE7p"],"\xD7"=>[18,"\xE7p"],"\xD8"=>[77,"\xE7r"],"\xD9"=>[18,"\xE7r"],"\xDA"=>[77,"\xE7u"],"\xDB"=>[18,"\xE7u"],"\xDC"=>[0,"\xE7\x3A"],"\xDD"=>[0,"\xE7B"],"\xDE"=>[0,"\xE7C"],"\xDF"=>[0,"\xE7D"],"\xE0"=>[0,"\xE7E"],"\xE1"=>[0,"\xE7F"],"\xE2"=>[0,"\xE7G"],"\xE3"=>[0,"\xE7H"],"\xE4"=>[0,"\xE7I"],"\xE5"=>[0,"\xE7J"],"\xE6"=>[0,"\xE7K"],"\xE7"=>[0,"\xE7L"],"\xE8"=>[0,"\xE7M"],"\xE9"=>[0,"\xE7N"],"\xEA"=>[0,"\xE7O"],"\xEB"=>[0,"\xE7P"],"\xEC"=>[0,"\xE7Q"],"\xED"=>[0,"\xE7R"],"\xEE"=>[0,"\xE7S"],"\xEF"=>[0,"\xE7T"],"\xF0"=>[0,"\xE7U"],"\xF1"=>[0,"\xE7V"],"\xF2"=>[0,"\xE7W"],"\xF3"=>[0,"\xE7Y"],"\xF4"=>[0,"\xE7j"],"\xF5"=>[0,"\xE7k"],"\xF6"=>[0,"\xE7q"],"\xF7"=>[0,"\xE7v"],"\xF8"=>[0,"\xE7w"],"\xF9"=>[0,"\xE7x"],"\xFA"=>[0,"\xE7y"],"\xFB"=>[0,"\xE7z"],"\xFC"=>[82,"\xE7"],"\xFD"=>[87,"\xE7"],"\xFE"=>[130,"\xE7"],"\xFF"=>[9,"\xE7"],],["\x00"=>[94,"\xC60"],"\x01"=>[76,"\xC60"],"\x02"=>[104,"\xC60"],"\x03"=>[16,"\xC60"],"\x04"=>[94,"\xC61"],"\x05"=>[76,"\xC61"],"\x06"=>[104,"\xC61"],"\x07"=>[16,"\xC61"],"\x08"=>[94,"\xC62"],"\x09"=>[76,"\xC62"],"\x0A"=>[104,"\xC62"],"\x0B"=>[16,"\xC62"],"\x0C"=>[94,"\xC6a"],"\x0D"=>[76,"\xC6a"],"\x0E"=>[104,"\xC6a"],"\x0F"=>[16,"\xC6a"],"\x10"=>[94,"\xC6c"],"\x11"=>[76,"\xC6c"],"\x12"=>[104,"\xC6c"],"\x13"=>[16,"\xC6c"],"\x14"=>[94,"\xC6e"],"\x15"=>[76,"\xC6e"],"\x16"=>[104,"\xC6e"],"\x17"=>[16,"\xC6e"],"\x18"=>[94,"\xC6i"],"\x19"=>[76,"\xC6i"],"\x1A"=>[104,"\xC6i"],"\x1B"=>[16,"\xC6i"],"\x1C"=>[94,"\xC6o"],"\x1D"=>[76,"\xC6o"],"\x1E"=>[104,"\xC6o"],"\x1F"=>[16,"\xC6o"],"\x20"=>[94,"\xC6s"],"\x21"=>[76,"\xC6s"],"\x22"=>[104,"\xC6s"],"\x23"=>[16,"\xC6s"],"\x24"=>[94,"\xC6t"],"\x25"=>[76,"\xC6t"],"\x26"=>[104,"\xC6t"],"\x27"=>[16,"\xC6t"],"\x28"=>[77,"\xC6\x20"],"\x29"=>[18,"\xC6\x20"],"\x2A"=>[77,"\xC6\x25"],"\x2B"=>[18,"\xC6\x25"],"\x2C"=>[77,"\xC6-"],"-"=>[18,"\xC6-"],"."=>[77,"\xC6."],"\x2F"=>[18,"\xC6."],[77,"\xC6\x2F"],[18,"\xC6\x2F"],[77,"\xC63"],[18,"\xC63"],[77,"\xC64"],[18,"\xC64"],[77,"\xC65"],[18,"\xC65"],[77,"\xC66"],[18,"\xC66"],"\x3A"=>[77,"\xC67"],"\x3B"=>[18,"\xC67"],"\x3C"=>[77,"\xC68"],"\x3D"=>[18,"\xC68"],"\x3E"=>[77,"\xC69"],"\x3F"=>[18,"\xC69"],"\x40"=>[77,"\xC6\x3D"],"A"=>[18,"\xC6\x3D"],"B"=>[77,"\xC6A"],"C"=>[18,"\xC6A"],"D"=>[77,"\xC6_"],"E"=>[18,"\xC6_"],"F"=>[77,"\xC6b"],"G"=>[18,"\xC6b"],"H"=>[77,"\xC6d"],"I"=>[18,"\xC6d"],"J"=>[77,"\xC6f"],"K"=>[18,"\xC6f"],"L"=>[77,"\xC6g"],"M"=>[18,"\xC6g"],"N"=>[77,"\xC6h"],"O"=>[18,"\xC6h"],"P"=>[77,"\xC6l"],"Q"=>[18,"\xC6l"],"R"=>[77,"\xC6m"],"S"=>[18,"\xC6m"],"T"=>[77,"\xC6n"],"U"=>[18,"\xC6n"],"V"=>[77,"\xC6p"],"W"=>[18,"\xC6p"],"X"=>[77,"\xC6r"],"Y"=>[18,"\xC6r"],"Z"=>[77,"\xC6u"],"\x5B"=>[18,"\xC6u"],"\x5C"=>[0,"\xC6\x3A"],"\x5D"=>[0,"\xC6B"],"\x5E"=>[0,"\xC6C"],"_"=>[0,"\xC6D"],"\x60"=>[0,"\xC6E"],"a"=>[0,"\xC6F"],"b"=>[0,"\xC6G"],"c"=>[0,"\xC6H"],"d"=>[0,"\xC6I"],"e"=>[0,"\xC6J"],"f"=>[0,"\xC6K"],"g"=>[0,"\xC6L"],"h"=>[0,"\xC6M"],"i"=>[0,"\xC6N"],"j"=>[0,"\xC6O"],"k"=>[0,"\xC6P"],"l"=>[0,"\xC6Q"],"m"=>[0,"\xC6R"],"n"=>[0,"\xC6S"],"o"=>[0,"\xC6T"],"p"=>[0,"\xC6U"],"q"=>[0,"\xC6V"],"r"=>[0,"\xC6W"],"s"=>[0,"\xC6Y"],"t"=>[0,"\xC6j"],"u"=>[0,"\xC6k"],"v"=>[0,"\xC6q"],"w"=>[0,"\xC6v"],"x"=>[0,"\xC6w"],"y"=>[0,"\xC6x"],"z"=>[0,"\xC6y"],"\x7B"=>[0,"\xC6z"],"\x7C"=>[82,"\xC6"],"\x7D"=>[87,"\xC6"],"~"=>[130,"\xC6"],"\x7F"=>[9,"\xC6"],"\x80"=>[94,"\xE40"],"\x81"=>[76,"\xE40"],"\x82"=>[104,"\xE40"],"\x83"=>[16,"\xE40"],"\x84"=>[94,"\xE41"],"\x85"=>[76,"\xE41"],"\x86"=>[104,"\xE41"],"\x87"=>[16,"\xE41"],"\x88"=>[94,"\xE42"],"\x89"=>[76,"\xE42"],"\x8A"=>[104,"\xE42"],"\x8B"=>[16,"\xE42"],"\x8C"=>[94,"\xE4a"],"\x8D"=>[76,"\xE4a"],"\x8E"=>[104,"\xE4a"],"\x8F"=>[16,"\xE4a"],"\x90"=>[94,"\xE4c"],"\x91"=>[76,"\xE4c"],"\x92"=>[104,"\xE4c"],"\x93"=>[16,"\xE4c"],"\x94"=>[94,"\xE4e"],"\x95"=>[76,"\xE4e"],"\x96"=>[104,"\xE4e"],"\x97"=>[16,"\xE4e"],"\x98"=>[94,"\xE4i"],"\x99"=>[76,"\xE4i"],"\x9A"=>[104,"\xE4i"],"\x9B"=>[16,"\xE4i"],"\x9C"=>[94,"\xE4o"],"\x9D"=>[76,"\xE4o"],"\x9E"=>[104,"\xE4o"],"\x9F"=>[16,"\xE4o"],"\xA0"=>[94,"\xE4s"],"\xA1"=>[76,"\xE4s"],"\xA2"=>[104,"\xE4s"],"\xA3"=>[16,"\xE4s"],"\xA4"=>[94,"\xE4t"],"\xA5"=>[76,"\xE4t"],"\xA6"=>[104,"\xE4t"],"\xA7"=>[16,"\xE4t"],"\xA8"=>[77,"\xE4\x20"],"\xA9"=>[18,"\xE4\x20"],"\xAA"=>[77,"\xE4\x25"],"\xAB"=>[18,"\xE4\x25"],"\xAC"=>[77,"\xE4-"],"\xAD"=>[18,"\xE4-"],"\xAE"=>[77,"\xE4."],"\xAF"=>[18,"\xE4."],"\xB0"=>[77,"\xE4\x2F"],"\xB1"=>[18,"\xE4\x2F"],"\xB2"=>[77,"\xE43"],"\xB3"=>[18,"\xE43"],"\xB4"=>[77,"\xE44"],"\xB5"=>[18,"\xE44"],"\xB6"=>[77,"\xE45"],"\xB7"=>[18,"\xE45"],"\xB8"=>[77,"\xE46"],"\xB9"=>[18,"\xE46"],"\xBA"=>[77,"\xE47"],"\xBB"=>[18,"\xE47"],"\xBC"=>[77,"\xE48"],"\xBD"=>[18,"\xE48"],"\xBE"=>[77,"\xE49"],"\xBF"=>[18,"\xE49"],"\xC0"=>[77,"\xE4\x3D"],"\xC1"=>[18,"\xE4\x3D"],"\xC2"=>[77,"\xE4A"],"\xC3"=>[18,"\xE4A"],"\xC4"=>[77,"\xE4_"],"\xC5"=>[18,"\xE4_"],"\xC6"=>[77,"\xE4b"],"\xC7"=>[18,"\xE4b"],"\xC8"=>[77,"\xE4d"],"\xC9"=>[18,"\xE4d"],"\xCA"=>[77,"\xE4f"],"\xCB"=>[18,"\xE4f"],"\xCC"=>[77,"\xE4g"],"\xCD"=>[18,"\xE4g"],"\xCE"=>[77,"\xE4h"],"\xCF"=>[18,"\xE4h"],"\xD0"=>[77,"\xE4l"],"\xD1"=>[18,"\xE4l"],"\xD2"=>[77,"\xE4m"],"\xD3"=>[18,"\xE4m"],"\xD4"=>[77,"\xE4n"],"\xD5"=>[18,"\xE4n"],"\xD6"=>[77,"\xE4p"],"\xD7"=>[18,"\xE4p"],"\xD8"=>[77,"\xE4r"],"\xD9"=>[18,"\xE4r"],"\xDA"=>[77,"\xE4u"],"\xDB"=>[18,"\xE4u"],"\xDC"=>[0,"\xE4\x3A"],"\xDD"=>[0,"\xE4B"],"\xDE"=>[0,"\xE4C"],"\xDF"=>[0,"\xE4D"],"\xE0"=>[0,"\xE4E"],"\xE1"=>[0,"\xE4F"],"\xE2"=>[0,"\xE4G"],"\xE3"=>[0,"\xE4H"],"\xE4"=>[0,"\xE4I"],"\xE5"=>[0,"\xE4J"],"\xE6"=>[0,"\xE4K"],"\xE7"=>[0,"\xE4L"],"\xE8"=>[0,"\xE4M"],"\xE9"=>[0,"\xE4N"],"\xEA"=>[0,"\xE4O"],"\xEB"=>[0,"\xE4P"],"\xEC"=>[0,"\xE4Q"],"\xED"=>[0,"\xE4R"],"\xEE"=>[0,"\xE4S"],"\xEF"=>[0,"\xE4T"],"\xF0"=>[0,"\xE4U"],"\xF1"=>[0,"\xE4V"],"\xF2"=>[0,"\xE4W"],"\xF3"=>[0,"\xE4Y"],"\xF4"=>[0,"\xE4j"],"\xF5"=>[0,"\xE4k"],"\xF6"=>[0,"\xE4q"],"\xF7"=>[0,"\xE4v"],"\xF8"=>[0,"\xE4w"],"\xF9"=>[0,"\xE4x"],"\xFA"=>[0,"\xE4y"],"\xFB"=>[0,"\xE4z"],"\xFC"=>[82,"\xE4"],"\xFD"=>[87,"\xE4"],"\xFE"=>[130,"\xE4"],"\xFF"=>[9,"\xE4"],],["\x00"=>[77,"\xC60"],"\x01"=>[18,"\xC60"],"\x02"=>[77,"\xC61"],"\x03"=>[18,"\xC61"],"\x04"=>[77,"\xC62"],"\x05"=>[18,"\xC62"],"\x06"=>[77,"\xC6a"],"\x07"=>[18,"\xC6a"],"\x08"=>[77,"\xC6c"],"\x09"=>[18,"\xC6c"],"\x0A"=>[77,"\xC6e"],"\x0B"=>[18,"\xC6e"],"\x0C"=>[77,"\xC6i"],"\x0D"=>[18,"\xC6i"],"\x0E"=>[77,"\xC6o"],"\x0F"=>[18,"\xC6o"],"\x10"=>[77,"\xC6s"],"\x11"=>[18,"\xC6s"],"\x12"=>[77,"\xC6t"],"\x13"=>[18,"\xC6t"],"\x14"=>[0,"\xC6\x20"],"\x15"=>[0,"\xC6\x25"],"\x16"=>[0,"\xC6-"],"\x17"=>[0,"\xC6."],"\x18"=>[0,"\xC6\x2F"],"\x19"=>[0,"\xC63"],"\x1A"=>[0,"\xC64"],"\x1B"=>[0,"\xC65"],"\x1C"=>[0,"\xC66"],"\x1D"=>[0,"\xC67"],"\x1E"=>[0,"\xC68"],"\x1F"=>[0,"\xC69"],"\x20"=>[0,"\xC6\x3D"],"\x21"=>[0,"\xC6A"],"\x22"=>[0,"\xC6_"],"\x23"=>[0,"\xC6b"],"\x24"=>[0,"\xC6d"],"\x25"=>[0,"\xC6f"],"\x26"=>[0,"\xC6g"],"\x27"=>[0,"\xC6h"],"\x28"=>[0,"\xC6l"],"\x29"=>[0,"\xC6m"],"\x2A"=>[0,"\xC6n"],"\x2B"=>[0,"\xC6p"],"\x2C"=>[0,"\xC6r"],"-"=>[0,"\xC6u"],"."=>[100,"\xC6"],"\x2F"=>[110,"\xC6"],[111,"\xC6"],[115,"\xC6"],[116,"\xC6"],[118,"\xC6"],[119,"\xC6"],[122,"\xC6"],[123,"\xC6"],[125,"\xC6"],[126,"\xC6"],[129,"\xC6"],"\x3A"=>[143,"\xC6"],"\x3B"=>[148,"\xC6"],"\x3C"=>[151,"\xC6"],"\x3D"=>[153,"\xC6"],"\x3E"=>[83,"\xC6"],"\x3F"=>[10,"\xC6"],"\x40"=>[77,"\xE40"],"A"=>[18,"\xE40"],"B"=>[77,"\xE41"],"C"=>[18,"\xE41"],"D"=>[77,"\xE42"],"E"=>[18,"\xE42"],"F"=>[77,"\xE4a"],"G"=>[18,"\xE4a"],"H"=>[77,"\xE4c"],"I"=>[18,"\xE4c"],"J"=>[77,"\xE4e"],"K"=>[18,"\xE4e"],"L"=>[77,"\xE4i"],"M"=>[18,"\xE4i"],"N"=>[77,"\xE4o"],"O"=>[18,"\xE4o"],"P"=>[77,"\xE4s"],"Q"=>[18,"\xE4s"],"R"=>[77,"\xE4t"],"S"=>[18,"\xE4t"],"T"=>[0,"\xE4\x20"],"U"=>[0,"\xE4\x25"],"V"=>[0,"\xE4-"],"W"=>[0,"\xE4."],"X"=>[0,"\xE4\x2F"],"Y"=>[0,"\xE43"],"Z"=>[0,"\xE44"],"\x5B"=>[0,"\xE45"],"\x5C"=>[0,"\xE46"],"\x5D"=>[0,"\xE47"],"\x5E"=>[0,"\xE48"],"_"=>[0,"\xE49"],"\x60"=>[0,"\xE4\x3D"],"a"=>[0,"\xE4A"],"b"=>[0,"\xE4_"],"c"=>[0,"\xE4b"],"d"=>[0,"\xE4d"],"e"=>[0,"\xE4f"],"f"=>[0,"\xE4g"],"g"=>[0,"\xE4h"],"h"=>[0,"\xE4l"],"i"=>[0,"\xE4m"],"j"=>[0,"\xE4n"],"k"=>[0,"\xE4p"],"l"=>[0,"\xE4r"],"m"=>[0,"\xE4u"],"n"=>[100,"\xE4"],"o"=>[110,"\xE4"],"p"=>[111,"\xE4"],"q"=>[115,"\xE4"],"r"=>[116,"\xE4"],"s"=>[118,"\xE4"],"t"=>[119,"\xE4"],"u"=>[122,"\xE4"],"v"=>[123,"\xE4"],"w"=>[125,"\xE4"],"x"=>[126,"\xE4"],"y"=>[129,"\xE4"],"z"=>[143,"\xE4"],"\x7B"=>[148,"\xE4"],"\x7C"=>[151,"\xE4"],"\x7D"=>[153,"\xE4"],"~"=>[83,"\xE4"],"\x7F"=>[10,"\xE4"],"\x80"=>[77,"\xE80"],"\x81"=>[18,"\xE80"],"\x82"=>[77,"\xE81"],"\x83"=>[18,"\xE81"],"\x84"=>[77,"\xE82"],"\x85"=>[18,"\xE82"],"\x86"=>[77,"\xE8a"],"\x87"=>[18,"\xE8a"],"\x88"=>[77,"\xE8c"],"\x89"=>[18,"\xE8c"],"\x8A"=>[77,"\xE8e"],"\x8B"=>[18,"\xE8e"],"\x8C"=>[77,"\xE8i"],"\x8D"=>[18,"\xE8i"],"\x8E"=>[77,"\xE8o"],"\x8F"=>[18,"\xE8o"],"\x90"=>[77,"\xE8s"],"\x91"=>[18,"\xE8s"],"\x92"=>[77,"\xE8t"],"\x93"=>[18,"\xE8t"],"\x94"=>[0,"\xE8\x20"],"\x95"=>[0,"\xE8\x25"],"\x96"=>[0,"\xE8-"],"\x97"=>[0,"\xE8."],"\x98"=>[0,"\xE8\x2F"],"\x99"=>[0,"\xE83"],"\x9A"=>[0,"\xE84"],"\x9B"=>[0,"\xE85"],"\x9C"=>[0,"\xE86"],"\x9D"=>[0,"\xE87"],"\x9E"=>[0,"\xE88"],"\x9F"=>[0,"\xE89"],"\xA0"=>[0,"\xE8\x3D"],"\xA1"=>[0,"\xE8A"],"\xA2"=>[0,"\xE8_"],"\xA3"=>[0,"\xE8b"],"\xA4"=>[0,"\xE8d"],"\xA5"=>[0,"\xE8f"],"\xA6"=>[0,"\xE8g"],"\xA7"=>[0,"\xE8h"],"\xA8"=>[0,"\xE8l"],"\xA9"=>[0,"\xE8m"],"\xAA"=>[0,"\xE8n"],"\xAB"=>[0,"\xE8p"],"\xAC"=>[0,"\xE8r"],"\xAD"=>[0,"\xE8u"],"\xAE"=>[100,"\xE8"],"\xAF"=>[110,"\xE8"],"\xB0"=>[111,"\xE8"],"\xB1"=>[115,"\xE8"],"\xB2"=>[116,"\xE8"],"\xB3"=>[118,"\xE8"],"\xB4"=>[119,"\xE8"],"\xB5"=>[122,"\xE8"],"\xB6"=>[123,"\xE8"],"\xB7"=>[125,"\xE8"],"\xB8"=>[126,"\xE8"],"\xB9"=>[129,"\xE8"],"\xBA"=>[143,"\xE8"],"\xBB"=>[148,"\xE8"],"\xBC"=>[151,"\xE8"],"\xBD"=>[153,"\xE8"],"\xBE"=>[83,"\xE8"],"\xBF"=>[10,"\xE8"],"\xC0"=>[77,"\xE90"],"\xC1"=>[18,"\xE90"],"\xC2"=>[77,"\xE91"],"\xC3"=>[18,"\xE91"],"\xC4"=>[77,"\xE92"],"\xC5"=>[18,"\xE92"],"\xC6"=>[77,"\xE9a"],"\xC7"=>[18,"\xE9a"],"\xC8"=>[77,"\xE9c"],"\xC9"=>[18,"\xE9c"],"\xCA"=>[77,"\xE9e"],"\xCB"=>[18,"\xE9e"],"\xCC"=>[77,"\xE9i"],"\xCD"=>[18,"\xE9i"],"\xCE"=>[77,"\xE9o"],"\xCF"=>[18,"\xE9o"],"\xD0"=>[77,"\xE9s"],"\xD1"=>[18,"\xE9s"],"\xD2"=>[77,"\xE9t"],"\xD3"=>[18,"\xE9t"],"\xD4"=>[0,"\xE9\x20"],"\xD5"=>[0,"\xE9\x25"],"\xD6"=>[0,"\xE9-"],"\xD7"=>[0,"\xE9."],"\xD8"=>[0,"\xE9\x2F"],"\xD9"=>[0,"\xE93"],"\xDA"=>[0,"\xE94"],"\xDB"=>[0,"\xE95"],"\xDC"=>[0,"\xE96"],"\xDD"=>[0,"\xE97"],"\xDE"=>[0,"\xE98"],"\xDF"=>[0,"\xE99"],"\xE0"=>[0,"\xE9\x3D"],"\xE1"=>[0,"\xE9A"],"\xE2"=>[0,"\xE9_"],"\xE3"=>[0,"\xE9b"],"\xE4"=>[0,"\xE9d"],"\xE5"=>[0,"\xE9f"],"\xE6"=>[0,"\xE9g"],"\xE7"=>[0,"\xE9h"],"\xE8"=>[0,"\xE9l"],"\xE9"=>[0,"\xE9m"],"\xEA"=>[0,"\xE9n"],"\xEB"=>[0,"\xE9p"],"\xEC"=>[0,"\xE9r"],"\xED"=>[0,"\xE9u"],"\xEE"=>[100,"\xE9"],"\xEF"=>[110,"\xE9"],"\xF0"=>[111,"\xE9"],"\xF1"=>[115,"\xE9"],"\xF2"=>[116,"\xE9"],"\xF3"=>[118,"\xE9"],"\xF4"=>[119,"\xE9"],"\xF5"=>[122,"\xE9"],"\xF6"=>[123,"\xE9"],"\xF7"=>[125,"\xE9"],"\xF8"=>[126,"\xE9"],"\xF9"=>[129,"\xE9"],"\xFA"=>[143,"\xE9"],"\xFB"=>[148,"\xE9"],"\xFC"=>[151,"\xE9"],"\xFD"=>[153,"\xE9"],"\xFE"=>[83,"\xE9"],"\xFF"=>[10,"\xE9"],],["\x00"=>[94,"\xC70"],"\x01"=>[76,"\xC70"],"\x02"=>[104,"\xC70"],"\x03"=>[16,"\xC70"],"\x04"=>[94,"\xC71"],"\x05"=>[76,"\xC71"],"\x06"=>[104,"\xC71"],"\x07"=>[16,"\xC71"],"\x08"=>[94,"\xC72"],"\x09"=>[76,"\xC72"],"\x0A"=>[104,"\xC72"],"\x0B"=>[16,"\xC72"],"\x0C"=>[94,"\xC7a"],"\x0D"=>[76,"\xC7a"],"\x0E"=>[104,"\xC7a"],"\x0F"=>[16,"\xC7a"],"\x10"=>[94,"\xC7c"],"\x11"=>[76,"\xC7c"],"\x12"=>[104,"\xC7c"],"\x13"=>[16,"\xC7c"],"\x14"=>[94,"\xC7e"],"\x15"=>[76,"\xC7e"],"\x16"=>[104,"\xC7e"],"\x17"=>[16,"\xC7e"],"\x18"=>[94,"\xC7i"],"\x19"=>[76,"\xC7i"],"\x1A"=>[104,"\xC7i"],"\x1B"=>[16,"\xC7i"],"\x1C"=>[94,"\xC7o"],"\x1D"=>[76,"\xC7o"],"\x1E"=>[104,"\xC7o"],"\x1F"=>[16,"\xC7o"],"\x20"=>[94,"\xC7s"],"\x21"=>[76,"\xC7s"],"\x22"=>[104,"\xC7s"],"\x23"=>[16,"\xC7s"],"\x24"=>[94,"\xC7t"],"\x25"=>[76,"\xC7t"],"\x26"=>[104,"\xC7t"],"\x27"=>[16,"\xC7t"],"\x28"=>[77,"\xC7\x20"],"\x29"=>[18,"\xC7\x20"],"\x2A"=>[77,"\xC7\x25"],"\x2B"=>[18,"\xC7\x25"],"\x2C"=>[77,"\xC7-"],"-"=>[18,"\xC7-"],"."=>[77,"\xC7."],"\x2F"=>[18,"\xC7."],[77,"\xC7\x2F"],[18,"\xC7\x2F"],[77,"\xC73"],[18,"\xC73"],[77,"\xC74"],[18,"\xC74"],[77,"\xC75"],[18,"\xC75"],[77,"\xC76"],[18,"\xC76"],"\x3A"=>[77,"\xC77"],"\x3B"=>[18,"\xC77"],"\x3C"=>[77,"\xC78"],"\x3D"=>[18,"\xC78"],"\x3E"=>[77,"\xC79"],"\x3F"=>[18,"\xC79"],"\x40"=>[77,"\xC7\x3D"],"A"=>[18,"\xC7\x3D"],"B"=>[77,"\xC7A"],"C"=>[18,"\xC7A"],"D"=>[77,"\xC7_"],"E"=>[18,"\xC7_"],"F"=>[77,"\xC7b"],"G"=>[18,"\xC7b"],"H"=>[77,"\xC7d"],"I"=>[18,"\xC7d"],"J"=>[77,"\xC7f"],"K"=>[18,"\xC7f"],"L"=>[77,"\xC7g"],"M"=>[18,"\xC7g"],"N"=>[77,"\xC7h"],"O"=>[18,"\xC7h"],"P"=>[77,"\xC7l"],"Q"=>[18,"\xC7l"],"R"=>[77,"\xC7m"],"S"=>[18,"\xC7m"],"T"=>[77,"\xC7n"],"U"=>[18,"\xC7n"],"V"=>[77,"\xC7p"],"W"=>[18,"\xC7p"],"X"=>[77,"\xC7r"],"Y"=>[18,"\xC7r"],"Z"=>[77,"\xC7u"],"\x5B"=>[18,"\xC7u"],"\x5C"=>[0,"\xC7\x3A"],"\x5D"=>[0,"\xC7B"],"\x5E"=>[0,"\xC7C"],"_"=>[0,"\xC7D"],"\x60"=>[0,"\xC7E"],"a"=>[0,"\xC7F"],"b"=>[0,"\xC7G"],"c"=>[0,"\xC7H"],"d"=>[0,"\xC7I"],"e"=>[0,"\xC7J"],"f"=>[0,"\xC7K"],"g"=>[0,"\xC7L"],"h"=>[0,"\xC7M"],"i"=>[0,"\xC7N"],"j"=>[0,"\xC7O"],"k"=>[0,"\xC7P"],"l"=>[0,"\xC7Q"],"m"=>[0,"\xC7R"],"n"=>[0,"\xC7S"],"o"=>[0,"\xC7T"],"p"=>[0,"\xC7U"],"q"=>[0,"\xC7V"],"r"=>[0,"\xC7W"],"s"=>[0,"\xC7Y"],"t"=>[0,"\xC7j"],"u"=>[0,"\xC7k"],"v"=>[0,"\xC7q"],"w"=>[0,"\xC7v"],"x"=>[0,"\xC7w"],"y"=>[0,"\xC7x"],"z"=>[0,"\xC7y"],"\x7B"=>[0,"\xC7z"],"\x7C"=>[82,"\xC7"],"\x7D"=>[87,"\xC7"],"~"=>[130,"\xC7"],"\x7F"=>[9,"\xC7"],"\x80"=>[94,"\xCF0"],"\x81"=>[76,"\xCF0"],"\x82"=>[104,"\xCF0"],"\x83"=>[16,"\xCF0"],"\x84"=>[94,"\xCF1"],"\x85"=>[76,"\xCF1"],"\x86"=>[104,"\xCF1"],"\x87"=>[16,"\xCF1"],"\x88"=>[94,"\xCF2"],"\x89"=>[76,"\xCF2"],"\x8A"=>[104,"\xCF2"],"\x8B"=>[16,"\xCF2"],"\x8C"=>[94,"\xCFa"],"\x8D"=>[76,"\xCFa"],"\x8E"=>[104,"\xCFa"],"\x8F"=>[16,"\xCFa"],"\x90"=>[94,"\xCFc"],"\x91"=>[76,"\xCFc"],"\x92"=>[104,"\xCFc"],"\x93"=>[16,"\xCFc"],"\x94"=>[94,"\xCFe"],"\x95"=>[76,"\xCFe"],"\x96"=>[104,"\xCFe"],"\x97"=>[16,"\xCFe"],"\x98"=>[94,"\xCFi"],"\x99"=>[76,"\xCFi"],"\x9A"=>[104,"\xCFi"],"\x9B"=>[16,"\xCFi"],"\x9C"=>[94,"\xCFo"],"\x9D"=>[76,"\xCFo"],"\x9E"=>[104,"\xCFo"],"\x9F"=>[16,"\xCFo"],"\xA0"=>[94,"\xCFs"],"\xA1"=>[76,"\xCFs"],"\xA2"=>[104,"\xCFs"],"\xA3"=>[16,"\xCFs"],"\xA4"=>[94,"\xCFt"],"\xA5"=>[76,"\xCFt"],"\xA6"=>[104,"\xCFt"],"\xA7"=>[16,"\xCFt"],"\xA8"=>[77,"\xCF\x20"],"\xA9"=>[18,"\xCF\x20"],"\xAA"=>[77,"\xCF\x25"],"\xAB"=>[18,"\xCF\x25"],"\xAC"=>[77,"\xCF-"],"\xAD"=>[18,"\xCF-"],"\xAE"=>[77,"\xCF."],"\xAF"=>[18,"\xCF."],"\xB0"=>[77,"\xCF\x2F"],"\xB1"=>[18,"\xCF\x2F"],"\xB2"=>[77,"\xCF3"],"\xB3"=>[18,"\xCF3"],"\xB4"=>[77,"\xCF4"],"\xB5"=>[18,"\xCF4"],"\xB6"=>[77,"\xCF5"],"\xB7"=>[18,"\xCF5"],"\xB8"=>[77,"\xCF6"],"\xB9"=>[18,"\xCF6"],"\xBA"=>[77,"\xCF7"],"\xBB"=>[18,"\xCF7"],"\xBC"=>[77,"\xCF8"],"\xBD"=>[18,"\xCF8"],"\xBE"=>[77,"\xCF9"],"\xBF"=>[18,"\xCF9"],"\xC0"=>[77,"\xCF\x3D"],"\xC1"=>[18,"\xCF\x3D"],"\xC2"=>[77,"\xCFA"],"\xC3"=>[18,"\xCFA"],"\xC4"=>[77,"\xCF_"],"\xC5"=>[18,"\xCF_"],"\xC6"=>[77,"\xCFb"],"\xC7"=>[18,"\xCFb"],"\xC8"=>[77,"\xCFd"],"\xC9"=>[18,"\xCFd"],"\xCA"=>[77,"\xCFf"],"\xCB"=>[18,"\xCFf"],"\xCC"=>[77,"\xCFg"],"\xCD"=>[18,"\xCFg"],"\xCE"=>[77,"\xCFh"],"\xCF"=>[18,"\xCFh"],"\xD0"=>[77,"\xCFl"],"\xD1"=>[18,"\xCFl"],"\xD2"=>[77,"\xCFm"],"\xD3"=>[18,"\xCFm"],"\xD4"=>[77,"\xCFn"],"\xD5"=>[18,"\xCFn"],"\xD6"=>[77,"\xCFp"],"\xD7"=>[18,"\xCFp"],"\xD8"=>[77,"\xCFr"],"\xD9"=>[18,"\xCFr"],"\xDA"=>[77,"\xCFu"],"\xDB"=>[18,"\xCFu"],"\xDC"=>[0,"\xCF\x3A"],"\xDD"=>[0,"\xCFB"],"\xDE"=>[0,"\xCFC"],"\xDF"=>[0,"\xCFD"],"\xE0"=>[0,"\xCFE"],"\xE1"=>[0,"\xCFF"],"\xE2"=>[0,"\xCFG"],"\xE3"=>[0,"\xCFH"],"\xE4"=>[0,"\xCFI"],"\xE5"=>[0,"\xCFJ"],"\xE6"=>[0,"\xCFK"],"\xE7"=>[0,"\xCFL"],"\xE8"=>[0,"\xCFM"],"\xE9"=>[0,"\xCFN"],"\xEA"=>[0,"\xCFO"],"\xEB"=>[0,"\xCFP"],"\xEC"=>[0,"\xCFQ"],"\xED"=>[0,"\xCFR"],"\xEE"=>[0,"\xCFS"],"\xEF"=>[0,"\xCFT"],"\xF0"=>[0,"\xCFU"],"\xF1"=>[0,"\xCFV"],"\xF2"=>[0,"\xCFW"],"\xF3"=>[0,"\xCFY"],"\xF4"=>[0,"\xCFj"],"\xF5"=>[0,"\xCFk"],"\xF6"=>[0,"\xCFq"],"\xF7"=>[0,"\xCFv"],"\xF8"=>[0,"\xCFw"],"\xF9"=>[0,"\xCFx"],"\xFA"=>[0,"\xCFy"],"\xFB"=>[0,"\xCFz"],"\xFC"=>[82,"\xCF"],"\xFD"=>[87,"\xCF"],"\xFE"=>[130,"\xCF"],"\xFF"=>[9,"\xCF"],],["\x00"=>[77,"\xC70"],"\x01"=>[18,"\xC70"],"\x02"=>[77,"\xC71"],"\x03"=>[18,"\xC71"],"\x04"=>[77,"\xC72"],"\x05"=>[18,"\xC72"],"\x06"=>[77,"\xC7a"],"\x07"=>[18,"\xC7a"],"\x08"=>[77,"\xC7c"],"\x09"=>[18,"\xC7c"],"\x0A"=>[77,"\xC7e"],"\x0B"=>[18,"\xC7e"],"\x0C"=>[77,"\xC7i"],"\x0D"=>[18,"\xC7i"],"\x0E"=>[77,"\xC7o"],"\x0F"=>[18,"\xC7o"],"\x10"=>[77,"\xC7s"],"\x11"=>[18,"\xC7s"],"\x12"=>[77,"\xC7t"],"\x13"=>[18,"\xC7t"],"\x14"=>[0,"\xC7\x20"],"\x15"=>[0,"\xC7\x25"],"\x16"=>[0,"\xC7-"],"\x17"=>[0,"\xC7."],"\x18"=>[0,"\xC7\x2F"],"\x19"=>[0,"\xC73"],"\x1A"=>[0,"\xC74"],"\x1B"=>[0,"\xC75"],"\x1C"=>[0,"\xC76"],"\x1D"=>[0,"\xC77"],"\x1E"=>[0,"\xC78"],"\x1F"=>[0,"\xC79"],"\x20"=>[0,"\xC7\x3D"],"\x21"=>[0,"\xC7A"],"\x22"=>[0,"\xC7_"],"\x23"=>[0,"\xC7b"],"\x24"=>[0,"\xC7d"],"\x25"=>[0,"\xC7f"],"\x26"=>[0,"\xC7g"],"\x27"=>[0,"\xC7h"],"\x28"=>[0,"\xC7l"],"\x29"=>[0,"\xC7m"],"\x2A"=>[0,"\xC7n"],"\x2B"=>[0,"\xC7p"],"\x2C"=>[0,"\xC7r"],"-"=>[0,"\xC7u"],"."=>[100,"\xC7"],"\x2F"=>[110,"\xC7"],[111,"\xC7"],[115,"\xC7"],[116,"\xC7"],[118,"\xC7"],[119,"\xC7"],[122,"\xC7"],[123,"\xC7"],[125,"\xC7"],[126,"\xC7"],[129,"\xC7"],"\x3A"=>[143,"\xC7"],"\x3B"=>[148,"\xC7"],"\x3C"=>[151,"\xC7"],"\x3D"=>[153,"\xC7"],"\x3E"=>[83,"\xC7"],"\x3F"=>[10,"\xC7"],"\x40"=>[77,"\xCF0"],"A"=>[18,"\xCF0"],"B"=>[77,"\xCF1"],"C"=>[18,"\xCF1"],"D"=>[77,"\xCF2"],"E"=>[18,"\xCF2"],"F"=>[77,"\xCFa"],"G"=>[18,"\xCFa"],"H"=>[77,"\xCFc"],"I"=>[18,"\xCFc"],"J"=>[77,"\xCFe"],"K"=>[18,"\xCFe"],"L"=>[77,"\xCFi"],"M"=>[18,"\xCFi"],"N"=>[77,"\xCFo"],"O"=>[18,"\xCFo"],"P"=>[77,"\xCFs"],"Q"=>[18,"\xCFs"],"R"=>[77,"\xCFt"],"S"=>[18,"\xCFt"],"T"=>[0,"\xCF\x20"],"U"=>[0,"\xCF\x25"],"V"=>[0,"\xCF-"],"W"=>[0,"\xCF."],"X"=>[0,"\xCF\x2F"],"Y"=>[0,"\xCF3"],"Z"=>[0,"\xCF4"],"\x5B"=>[0,"\xCF5"],"\x5C"=>[0,"\xCF6"],"\x5D"=>[0,"\xCF7"],"\x5E"=>[0,"\xCF8"],"_"=>[0,"\xCF9"],"\x60"=>[0,"\xCF\x3D"],"a"=>[0,"\xCFA"],"b"=>[0,"\xCF_"],"c"=>[0,"\xCFb"],"d"=>[0,"\xCFd"],"e"=>[0,"\xCFf"],"f"=>[0,"\xCFg"],"g"=>[0,"\xCFh"],"h"=>[0,"\xCFl"],"i"=>[0,"\xCFm"],"j"=>[0,"\xCFn"],"k"=>[0,"\xCFp"],"l"=>[0,"\xCFr"],"m"=>[0,"\xCFu"],"n"=>[100,"\xCF"],"o"=>[110,"\xCF"],"p"=>[111,"\xCF"],"q"=>[115,"\xCF"],"r"=>[116,"\xCF"],"s"=>[118,"\xCF"],"t"=>[119,"\xCF"],"u"=>[122,"\xCF"],"v"=>[123,"\xCF"],"w"=>[125,"\xCF"],"x"=>[126,"\xCF"],"y"=>[129,"\xCF"],"z"=>[143,"\xCF"],"\x7B"=>[148,"\xCF"],"\x7C"=>[151,"\xCF"],"\x7D"=>[153,"\xCF"],"~"=>[83,"\xCF"],"\x7F"=>[10,"\xCF"],"\x80"=>[77,"\xEA0"],"\x81"=>[18,"\xEA0"],"\x82"=>[77,"\xEA1"],"\x83"=>[18,"\xEA1"],"\x84"=>[77,"\xEA2"],"\x85"=>[18,"\xEA2"],"\x86"=>[77,"\xEAa"],"\x87"=>[18,"\xEAa"],"\x88"=>[77,"\xEAc"],"\x89"=>[18,"\xEAc"],"\x8A"=>[77,"\xEAe"],"\x8B"=>[18,"\xEAe"],"\x8C"=>[77,"\xEAi"],"\x8D"=>[18,"\xEAi"],"\x8E"=>[77,"\xEAo"],"\x8F"=>[18,"\xEAo"],"\x90"=>[77,"\xEAs"],"\x91"=>[18,"\xEAs"],"\x92"=>[77,"\xEAt"],"\x93"=>[18,"\xEAt"],"\x94"=>[0,"\xEA\x20"],"\x95"=>[0,"\xEA\x25"],"\x96"=>[0,"\xEA-"],"\x97"=>[0,"\xEA."],"\x98"=>[0,"\xEA\x2F"],"\x99"=>[0,"\xEA3"],"\x9A"=>[0,"\xEA4"],"\x9B"=>[0,"\xEA5"],"\x9C"=>[0,"\xEA6"],"\x9D"=>[0,"\xEA7"],"\x9E"=>[0,"\xEA8"],"\x9F"=>[0,"\xEA9"],"\xA0"=>[0,"\xEA\x3D"],"\xA1"=>[0,"\xEAA"],"\xA2"=>[0,"\xEA_"],"\xA3"=>[0,"\xEAb"],"\xA4"=>[0,"\xEAd"],"\xA5"=>[0,"\xEAf"],"\xA6"=>[0,"\xEAg"],"\xA7"=>[0,"\xEAh"],"\xA8"=>[0,"\xEAl"],"\xA9"=>[0,"\xEAm"],"\xAA"=>[0,"\xEAn"],"\xAB"=>[0,"\xEAp"],"\xAC"=>[0,"\xEAr"],"\xAD"=>[0,"\xEAu"],"\xAE"=>[100,"\xEA"],"\xAF"=>[110,"\xEA"],"\xB0"=>[111,"\xEA"],"\xB1"=>[115,"\xEA"],"\xB2"=>[116,"\xEA"],"\xB3"=>[118,"\xEA"],"\xB4"=>[119,"\xEA"],"\xB5"=>[122,"\xEA"],"\xB6"=>[123,"\xEA"],"\xB7"=>[125,"\xEA"],"\xB8"=>[126,"\xEA"],"\xB9"=>[129,"\xEA"],"\xBA"=>[143,"\xEA"],"\xBB"=>[148,"\xEA"],"\xBC"=>[151,"\xEA"],"\xBD"=>[153,"\xEA"],"\xBE"=>[83,"\xEA"],"\xBF"=>[10,"\xEA"],"\xC0"=>[77,"\xEB0"],"\xC1"=>[18,"\xEB0"],"\xC2"=>[77,"\xEB1"],"\xC3"=>[18,"\xEB1"],"\xC4"=>[77,"\xEB2"],"\xC5"=>[18,"\xEB2"],"\xC6"=>[77,"\xEBa"],"\xC7"=>[18,"\xEBa"],"\xC8"=>[77,"\xEBc"],"\xC9"=>[18,"\xEBc"],"\xCA"=>[77,"\xEBe"],"\xCB"=>[18,"\xEBe"],"\xCC"=>[77,"\xEBi"],"\xCD"=>[18,"\xEBi"],"\xCE"=>[77,"\xEBo"],"\xCF"=>[18,"\xEBo"],"\xD0"=>[77,"\xEBs"],"\xD1"=>[18,"\xEBs"],"\xD2"=>[77,"\xEBt"],"\xD3"=>[18,"\xEBt"],"\xD4"=>[0,"\xEB\x20"],"\xD5"=>[0,"\xEB\x25"],"\xD6"=>[0,"\xEB-"],"\xD7"=>[0,"\xEB."],"\xD8"=>[0,"\xEB\x2F"],"\xD9"=>[0,"\xEB3"],"\xDA"=>[0,"\xEB4"],"\xDB"=>[0,"\xEB5"],"\xDC"=>[0,"\xEB6"],"\xDD"=>[0,"\xEB7"],"\xDE"=>[0,"\xEB8"],"\xDF"=>[0,"\xEB9"],"\xE0"=>[0,"\xEB\x3D"],"\xE1"=>[0,"\xEBA"],"\xE2"=>[0,"\xEB_"],"\xE3"=>[0,"\xEBb"],"\xE4"=>[0,"\xEBd"],"\xE5"=>[0,"\xEBf"],"\xE6"=>[0,"\xEBg"],"\xE7"=>[0,"\xEBh"],"\xE8"=>[0,"\xEBl"],"\xE9"=>[0,"\xEBm"],"\xEA"=>[0,"\xEBn"],"\xEB"=>[0,"\xEBp"],"\xEC"=>[0,"\xEBr"],"\xED"=>[0,"\xEBu"],"\xEE"=>[100,"\xEB"],"\xEF"=>[110,"\xEB"],"\xF0"=>[111,"\xEB"],"\xF1"=>[115,"\xEB"],"\xF2"=>[116,"\xEB"],"\xF3"=>[118,"\xEB"],"\xF4"=>[119,"\xEB"],"\xF5"=>[122,"\xEB"],"\xF6"=>[123,"\xEB"],"\xF7"=>[125,"\xEB"],"\xF8"=>[126,"\xEB"],"\xF9"=>[129,"\xEB"],"\xFA"=>[143,"\xEB"],"\xFB"=>[148,"\xEB"],"\xFC"=>[151,"\xEB"],"\xFD"=>[153,"\xEB"],"\xFE"=>[83,"\xEB"],"\xFF"=>[10,"\xEB"],],["\x00"=>[77,"\xEC0"],"\x01"=>[18,"\xEC0"],"\x02"=>[77,"\xEC1"],"\x03"=>[18,"\xEC1"],"\x04"=>[77,"\xEC2"],"\x05"=>[18,"\xEC2"],"\x06"=>[77,"\xECa"],"\x07"=>[18,"\xECa"],"\x08"=>[77,"\xECc"],"\x09"=>[18,"\xECc"],"\x0A"=>[77,"\xECe"],"\x0B"=>[18,"\xECe"],"\x0C"=>[77,"\xECi"],"\x0D"=>[18,"\xECi"],"\x0E"=>[77,"\xECo"],"\x0F"=>[18,"\xECo"],"\x10"=>[77,"\xECs"],"\x11"=>[18,"\xECs"],"\x12"=>[77,"\xECt"],"\x13"=>[18,"\xECt"],"\x14"=>[0,"\xEC\x20"],"\x15"=>[0,"\xEC\x25"],"\x16"=>[0,"\xEC-"],"\x17"=>[0,"\xEC."],"\x18"=>[0,"\xEC\x2F"],"\x19"=>[0,"\xEC3"],"\x1A"=>[0,"\xEC4"],"\x1B"=>[0,"\xEC5"],"\x1C"=>[0,"\xEC6"],"\x1D"=>[0,"\xEC7"],"\x1E"=>[0,"\xEC8"],"\x1F"=>[0,"\xEC9"],"\x20"=>[0,"\xEC\x3D"],"\x21"=>[0,"\xECA"],"\x22"=>[0,"\xEC_"],"\x23"=>[0,"\xECb"],"\x24"=>[0,"\xECd"],"\x25"=>[0,"\xECf"],"\x26"=>[0,"\xECg"],"\x27"=>[0,"\xECh"],"\x28"=>[0,"\xECl"],"\x29"=>[0,"\xECm"],"\x2A"=>[0,"\xECn"],"\x2B"=>[0,"\xECp"],"\x2C"=>[0,"\xECr"],"-"=>[0,"\xECu"],"."=>[100,"\xEC"],"\x2F"=>[110,"\xEC"],[111,"\xEC"],[115,"\xEC"],[116,"\xEC"],[118,"\xEC"],[119,"\xEC"],[122,"\xEC"],[123,"\xEC"],[125,"\xEC"],[126,"\xEC"],[129,"\xEC"],"\x3A"=>[143,"\xEC"],"\x3B"=>[148,"\xEC"],"\x3C"=>[151,"\xEC"],"\x3D"=>[153,"\xEC"],"\x3E"=>[83,"\xEC"],"\x3F"=>[10,"\xEC"],"\x40"=>[77,"\xED0"],"A"=>[18,"\xED0"],"B"=>[77,"\xED1"],"C"=>[18,"\xED1"],"D"=>[77,"\xED2"],"E"=>[18,"\xED2"],"F"=>[77,"\xEDa"],"G"=>[18,"\xEDa"],"H"=>[77,"\xEDc"],"I"=>[18,"\xEDc"],"J"=>[77,"\xEDe"],"K"=>[18,"\xEDe"],"L"=>[77,"\xEDi"],"M"=>[18,"\xEDi"],"N"=>[77,"\xEDo"],"O"=>[18,"\xEDo"],"P"=>[77,"\xEDs"],"Q"=>[18,"\xEDs"],"R"=>[77,"\xEDt"],"S"=>[18,"\xEDt"],"T"=>[0,"\xED\x20"],"U"=>[0,"\xED\x25"],"V"=>[0,"\xED-"],"W"=>[0,"\xED."],"X"=>[0,"\xED\x2F"],"Y"=>[0,"\xED3"],"Z"=>[0,"\xED4"],"\x5B"=>[0,"\xED5"],"\x5C"=>[0,"\xED6"],"\x5D"=>[0,"\xED7"],"\x5E"=>[0,"\xED8"],"_"=>[0,"\xED9"],"\x60"=>[0,"\xED\x3D"],"a"=>[0,"\xEDA"],"b"=>[0,"\xED_"],"c"=>[0,"\xEDb"],"d"=>[0,"\xEDd"],"e"=>[0,"\xEDf"],"f"=>[0,"\xEDg"],"g"=>[0,"\xEDh"],"h"=>[0,"\xEDl"],"i"=>[0,"\xEDm"],"j"=>[0,"\xEDn"],"k"=>[0,"\xEDp"],"l"=>[0,"\xEDr"],"m"=>[0,"\xEDu"],"n"=>[100,"\xED"],"o"=>[110,"\xED"],"p"=>[111,"\xED"],"q"=>[115,"\xED"],"r"=>[116,"\xED"],"s"=>[118,"\xED"],"t"=>[119,"\xED"],"u"=>[122,"\xED"],"v"=>[123,"\xED"],"w"=>[125,"\xED"],"x"=>[126,"\xED"],"y"=>[129,"\xED"],"z"=>[143,"\xED"],"\x7B"=>[148,"\xED"],"\x7C"=>[151,"\xED"],"\x7D"=>[153,"\xED"],"~"=>[83,"\xED"],"\x7F"=>[10,"\xED"],"\x80"=>[0,"\xC70"],"\x81"=>[0,"\xC71"],"\x82"=>[0,"\xC72"],"\x83"=>[0,"\xC7a"],"\x84"=>[0,"\xC7c"],"\x85"=>[0,"\xC7e"],"\x86"=>[0,"\xC7i"],"\x87"=>[0,"\xC7o"],"\x88"=>[0,"\xC7s"],"\x89"=>[0,"\xC7t"],"\x8A"=>[73,"\xC7"],"\x8B"=>[88,"\xC7"],"\x8C"=>[89,"\xC7"],"\x8D"=>[96,"\xC7"],"\x8E"=>[97,"\xC7"],"\x8F"=>[99,"\xC7"],"\x90"=>[106,"\xC7"],"\x91"=>[136,"\xC7"],"\x92"=>[139,"\xC7"],"\x93"=>[141,"\xC7"],"\x94"=>[145,"\xC7"],"\x95"=>[147,"\xC7"],"\x96"=>[149,"\xC7"],"\x97"=>[101,"\xC7"],"\x98"=>[112,"\xC7"],"\x99"=>[117,"\xC7"],"\x9A"=>[120,"\xC7"],"\x9B"=>[124,"\xC7"],"\x9C"=>[127,"\xC7"],"\x9D"=>[144,"\xC7"],"\x9E"=>[152,"\xC7"],"\x9F"=>[11,"\xC7"],"\xA0"=>[0,"\xCF0"],"\xA1"=>[0,"\xCF1"],"\xA2"=>[0,"\xCF2"],"\xA3"=>[0,"\xCFa"],"\xA4"=>[0,"\xCFc"],"\xA5"=>[0,"\xCFe"],"\xA6"=>[0,"\xCFi"],"\xA7"=>[0,"\xCFo"],"\xA8"=>[0,"\xCFs"],"\xA9"=>[0,"\xCFt"],"\xAA"=>[73,"\xCF"],"\xAB"=>[88,"\xCF"],"\xAC"=>[89,"\xCF"],"\xAD"=>[96,"\xCF"],"\xAE"=>[97,"\xCF"],"\xAF"=>[99,"\xCF"],"\xB0"=>[106,"\xCF"],"\xB1"=>[136,"\xCF"],"\xB2"=>[139,"\xCF"],"\xB3"=>[141,"\xCF"],"\xB4"=>[145,"\xCF"],"\xB5"=>[147,"\xCF"],"\xB6"=>[149,"\xCF"],"\xB7"=>[101,"\xCF"],"\xB8"=>[112,"\xCF"],"\xB9"=>[117,"\xCF"],"\xBA"=>[120,"\xCF"],"\xBB"=>[124,"\xCF"],"\xBC"=>[127,"\xCF"],"\xBD"=>[144,"\xCF"],"\xBE"=>[152,"\xCF"],"\xBF"=>[11,"\xCF"],"\xC0"=>[0,"\xEA0"],"\xC1"=>[0,"\xEA1"],"\xC2"=>[0,"\xEA2"],"\xC3"=>[0,"\xEAa"],"\xC4"=>[0,"\xEAc"],"\xC5"=>[0,"\xEAe"],"\xC6"=>[0,"\xEAi"],"\xC7"=>[0,"\xEAo"],"\xC8"=>[0,"\xEAs"],"\xC9"=>[0,"\xEAt"],"\xCA"=>[73,"\xEA"],"\xCB"=>[88,"\xEA"],"\xCC"=>[89,"\xEA"],"\xCD"=>[96,"\xEA"],"\xCE"=>[97,"\xEA"],"\xCF"=>[99,"\xEA"],"\xD0"=>[106,"\xEA"],"\xD1"=>[136,"\xEA"],"\xD2"=>[139,"\xEA"],"\xD3"=>[141,"\xEA"],"\xD4"=>[145,"\xEA"],"\xD5"=>[147,"\xEA"],"\xD6"=>[149,"\xEA"],"\xD7"=>[101,"\xEA"],"\xD8"=>[112,"\xEA"],"\xD9"=>[117,"\xEA"],"\xDA"=>[120,"\xEA"],"\xDB"=>[124,"\xEA"],"\xDC"=>[127,"\xEA"],"\xDD"=>[144,"\xEA"],"\xDE"=>[152,"\xEA"],"\xDF"=>[11,"\xEA"],"\xE0"=>[0,"\xEB0"],"\xE1"=>[0,"\xEB1"],"\xE2"=>[0,"\xEB2"],"\xE3"=>[0,"\xEBa"],"\xE4"=>[0,"\xEBc"],"\xE5"=>[0,"\xEBe"],"\xE6"=>[0,"\xEBi"],"\xE7"=>[0,"\xEBo"],"\xE8"=>[0,"\xEBs"],"\xE9"=>[0,"\xEBt"],"\xEA"=>[73,"\xEB"],"\xEB"=>[88,"\xEB"],"\xEC"=>[89,"\xEB"],"\xED"=>[96,"\xEB"],"\xEE"=>[97,"\xEB"],"\xEF"=>[99,"\xEB"],"\xF0"=>[106,"\xEB"],"\xF1"=>[136,"\xEB"],"\xF2"=>[139,"\xEB"],"\xF3"=>[141,"\xEB"],"\xF4"=>[145,"\xEB"],"\xF5"=>[147,"\xEB"],"\xF6"=>[149,"\xEB"],"\xF7"=>[101,"\xEB"],"\xF8"=>[112,"\xEB"],"\xF9"=>[117,"\xEB"],"\xFA"=>[120,"\xEB"],"\xFB"=>[124,"\xEB"],"\xFC"=>[127,"\xEB"],"\xFD"=>[144,"\xEB"],"\xFE"=>[152,"\xEB"],"\xFF"=>[11,"\xEB"],],["\x00"=>[94,"\xC80"],"\x01"=>[76,"\xC80"],"\x02"=>[104,"\xC80"],"\x03"=>[16,"\xC80"],"\x04"=>[94,"\xC81"],"\x05"=>[76,"\xC81"],"\x06"=>[104,"\xC81"],"\x07"=>[16,"\xC81"],"\x08"=>[94,"\xC82"],"\x09"=>[76,"\xC82"],"\x0A"=>[104,"\xC82"],"\x0B"=>[16,"\xC82"],"\x0C"=>[94,"\xC8a"],"\x0D"=>[76,"\xC8a"],"\x0E"=>[104,"\xC8a"],"\x0F"=>[16,"\xC8a"],"\x10"=>[94,"\xC8c"],"\x11"=>[76,"\xC8c"],"\x12"=>[104,"\xC8c"],"\x13"=>[16,"\xC8c"],"\x14"=>[94,"\xC8e"],"\x15"=>[76,"\xC8e"],"\x16"=>[104,"\xC8e"],"\x17"=>[16,"\xC8e"],"\x18"=>[94,"\xC8i"],"\x19"=>[76,"\xC8i"],"\x1A"=>[104,"\xC8i"],"\x1B"=>[16,"\xC8i"],"\x1C"=>[94,"\xC8o"],"\x1D"=>[76,"\xC8o"],"\x1E"=>[104,"\xC8o"],"\x1F"=>[16,"\xC8o"],"\x20"=>[94,"\xC8s"],"\x21"=>[76,"\xC8s"],"\x22"=>[104,"\xC8s"],"\x23"=>[16,"\xC8s"],"\x24"=>[94,"\xC8t"],"\x25"=>[76,"\xC8t"],"\x26"=>[104,"\xC8t"],"\x27"=>[16,"\xC8t"],"\x28"=>[77,"\xC8\x20"],"\x29"=>[18,"\xC8\x20"],"\x2A"=>[77,"\xC8\x25"],"\x2B"=>[18,"\xC8\x25"],"\x2C"=>[77,"\xC8-"],"-"=>[18,"\xC8-"],"."=>[77,"\xC8."],"\x2F"=>[18,"\xC8."],[77,"\xC8\x2F"],[18,"\xC8\x2F"],[77,"\xC83"],[18,"\xC83"],[77,"\xC84"],[18,"\xC84"],[77,"\xC85"],[18,"\xC85"],[77,"\xC86"],[18,"\xC86"],"\x3A"=>[77,"\xC87"],"\x3B"=>[18,"\xC87"],"\x3C"=>[77,"\xC88"],"\x3D"=>[18,"\xC88"],"\x3E"=>[77,"\xC89"],"\x3F"=>[18,"\xC89"],"\x40"=>[77,"\xC8\x3D"],"A"=>[18,"\xC8\x3D"],"B"=>[77,"\xC8A"],"C"=>[18,"\xC8A"],"D"=>[77,"\xC8_"],"E"=>[18,"\xC8_"],"F"=>[77,"\xC8b"],"G"=>[18,"\xC8b"],"H"=>[77,"\xC8d"],"I"=>[18,"\xC8d"],"J"=>[77,"\xC8f"],"K"=>[18,"\xC8f"],"L"=>[77,"\xC8g"],"M"=>[18,"\xC8g"],"N"=>[77,"\xC8h"],"O"=>[18,"\xC8h"],"P"=>[77,"\xC8l"],"Q"=>[18,"\xC8l"],"R"=>[77,"\xC8m"],"S"=>[18,"\xC8m"],"T"=>[77,"\xC8n"],"U"=>[18,"\xC8n"],"V"=>[77,"\xC8p"],"W"=>[18,"\xC8p"],"X"=>[77,"\xC8r"],"Y"=>[18,"\xC8r"],"Z"=>[77,"\xC8u"],"\x5B"=>[18,"\xC8u"],"\x5C"=>[0,"\xC8\x3A"],"\x5D"=>[0,"\xC8B"],"\x5E"=>[0,"\xC8C"],"_"=>[0,"\xC8D"],"\x60"=>[0,"\xC8E"],"a"=>[0,"\xC8F"],"b"=>[0,"\xC8G"],"c"=>[0,"\xC8H"],"d"=>[0,"\xC8I"],"e"=>[0,"\xC8J"],"f"=>[0,"\xC8K"],"g"=>[0,"\xC8L"],"h"=>[0,"\xC8M"],"i"=>[0,"\xC8N"],"j"=>[0,"\xC8O"],"k"=>[0,"\xC8P"],"l"=>[0,"\xC8Q"],"m"=>[0,"\xC8R"],"n"=>[0,"\xC8S"],"o"=>[0,"\xC8T"],"p"=>[0,"\xC8U"],"q"=>[0,"\xC8V"],"r"=>[0,"\xC8W"],"s"=>[0,"\xC8Y"],"t"=>[0,"\xC8j"],"u"=>[0,"\xC8k"],"v"=>[0,"\xC8q"],"w"=>[0,"\xC8v"],"x"=>[0,"\xC8w"],"y"=>[0,"\xC8x"],"z"=>[0,"\xC8y"],"\x7B"=>[0,"\xC8z"],"\x7C"=>[82,"\xC8"],"\x7D"=>[87,"\xC8"],"~"=>[130,"\xC8"],"\x7F"=>[9,"\xC8"],"\x80"=>[94,"\xC90"],"\x81"=>[76,"\xC90"],"\x82"=>[104,"\xC90"],"\x83"=>[16,"\xC90"],"\x84"=>[94,"\xC91"],"\x85"=>[76,"\xC91"],"\x86"=>[104,"\xC91"],"\x87"=>[16,"\xC91"],"\x88"=>[94,"\xC92"],"\x89"=>[76,"\xC92"],"\x8A"=>[104,"\xC92"],"\x8B"=>[16,"\xC92"],"\x8C"=>[94,"\xC9a"],"\x8D"=>[76,"\xC9a"],"\x8E"=>[104,"\xC9a"],"\x8F"=>[16,"\xC9a"],"\x90"=>[94,"\xC9c"],"\x91"=>[76,"\xC9c"],"\x92"=>[104,"\xC9c"],"\x93"=>[16,"\xC9c"],"\x94"=>[94,"\xC9e"],"\x95"=>[76,"\xC9e"],"\x96"=>[104,"\xC9e"],"\x97"=>[16,"\xC9e"],"\x98"=>[94,"\xC9i"],"\x99"=>[76,"\xC9i"],"\x9A"=>[104,"\xC9i"],"\x9B"=>[16,"\xC9i"],"\x9C"=>[94,"\xC9o"],"\x9D"=>[76,"\xC9o"],"\x9E"=>[104,"\xC9o"],"\x9F"=>[16,"\xC9o"],"\xA0"=>[94,"\xC9s"],"\xA1"=>[76,"\xC9s"],"\xA2"=>[104,"\xC9s"],"\xA3"=>[16,"\xC9s"],"\xA4"=>[94,"\xC9t"],"\xA5"=>[76,"\xC9t"],"\xA6"=>[104,"\xC9t"],"\xA7"=>[16,"\xC9t"],"\xA8"=>[77,"\xC9\x20"],"\xA9"=>[18,"\xC9\x20"],"\xAA"=>[77,"\xC9\x25"],"\xAB"=>[18,"\xC9\x25"],"\xAC"=>[77,"\xC9-"],"\xAD"=>[18,"\xC9-"],"\xAE"=>[77,"\xC9."],"\xAF"=>[18,"\xC9."],"\xB0"=>[77,"\xC9\x2F"],"\xB1"=>[18,"\xC9\x2F"],"\xB2"=>[77,"\xC93"],"\xB3"=>[18,"\xC93"],"\xB4"=>[77,"\xC94"],"\xB5"=>[18,"\xC94"],"\xB6"=>[77,"\xC95"],"\xB7"=>[18,"\xC95"],"\xB8"=>[77,"\xC96"],"\xB9"=>[18,"\xC96"],"\xBA"=>[77,"\xC97"],"\xBB"=>[18,"\xC97"],"\xBC"=>[77,"\xC98"],"\xBD"=>[18,"\xC98"],"\xBE"=>[77,"\xC99"],"\xBF"=>[18,"\xC99"],"\xC0"=>[77,"\xC9\x3D"],"\xC1"=>[18,"\xC9\x3D"],"\xC2"=>[77,"\xC9A"],"\xC3"=>[18,"\xC9A"],"\xC4"=>[77,"\xC9_"],"\xC5"=>[18,"\xC9_"],"\xC6"=>[77,"\xC9b"],"\xC7"=>[18,"\xC9b"],"\xC8"=>[77,"\xC9d"],"\xC9"=>[18,"\xC9d"],"\xCA"=>[77,"\xC9f"],"\xCB"=>[18,"\xC9f"],"\xCC"=>[77,"\xC9g"],"\xCD"=>[18,"\xC9g"],"\xCE"=>[77,"\xC9h"],"\xCF"=>[18,"\xC9h"],"\xD0"=>[77,"\xC9l"],"\xD1"=>[18,"\xC9l"],"\xD2"=>[77,"\xC9m"],"\xD3"=>[18,"\xC9m"],"\xD4"=>[77,"\xC9n"],"\xD5"=>[18,"\xC9n"],"\xD6"=>[77,"\xC9p"],"\xD7"=>[18,"\xC9p"],"\xD8"=>[77,"\xC9r"],"\xD9"=>[18,"\xC9r"],"\xDA"=>[77,"\xC9u"],"\xDB"=>[18,"\xC9u"],"\xDC"=>[0,"\xC9\x3A"],"\xDD"=>[0,"\xC9B"],"\xDE"=>[0,"\xC9C"],"\xDF"=>[0,"\xC9D"],"\xE0"=>[0,"\xC9E"],"\xE1"=>[0,"\xC9F"],"\xE2"=>[0,"\xC9G"],"\xE3"=>[0,"\xC9H"],"\xE4"=>[0,"\xC9I"],"\xE5"=>[0,"\xC9J"],"\xE6"=>[0,"\xC9K"],"\xE7"=>[0,"\xC9L"],"\xE8"=>[0,"\xC9M"],"\xE9"=>[0,"\xC9N"],"\xEA"=>[0,"\xC9O"],"\xEB"=>[0,"\xC9P"],"\xEC"=>[0,"\xC9Q"],"\xED"=>[0,"\xC9R"],"\xEE"=>[0,"\xC9S"],"\xEF"=>[0,"\xC9T"],"\xF0"=>[0,"\xC9U"],"\xF1"=>[0,"\xC9V"],"\xF2"=>[0,"\xC9W"],"\xF3"=>[0,"\xC9Y"],"\xF4"=>[0,"\xC9j"],"\xF5"=>[0,"\xC9k"],"\xF6"=>[0,"\xC9q"],"\xF7"=>[0,"\xC9v"],"\xF8"=>[0,"\xC9w"],"\xF9"=>[0,"\xC9x"],"\xFA"=>[0,"\xC9y"],"\xFB"=>[0,"\xC9z"],"\xFC"=>[82,"\xC9"],"\xFD"=>[87,"\xC9"],"\xFE"=>[130,"\xC9"],"\xFF"=>[9,"\xC9"],],["\x00"=>[77,"\xCA0"],"\x01"=>[18,"\xCA0"],"\x02"=>[77,"\xCA1"],"\x03"=>[18,"\xCA1"],"\x04"=>[77,"\xCA2"],"\x05"=>[18,"\xCA2"],"\x06"=>[77,"\xCAa"],"\x07"=>[18,"\xCAa"],"\x08"=>[77,"\xCAc"],"\x09"=>[18,"\xCAc"],"\x0A"=>[77,"\xCAe"],"\x0B"=>[18,"\xCAe"],"\x0C"=>[77,"\xCAi"],"\x0D"=>[18,"\xCAi"],"\x0E"=>[77,"\xCAo"],"\x0F"=>[18,"\xCAo"],"\x10"=>[77,"\xCAs"],"\x11"=>[18,"\xCAs"],"\x12"=>[77,"\xCAt"],"\x13"=>[18,"\xCAt"],"\x14"=>[0,"\xCA\x20"],"\x15"=>[0,"\xCA\x25"],"\x16"=>[0,"\xCA-"],"\x17"=>[0,"\xCA."],"\x18"=>[0,"\xCA\x2F"],"\x19"=>[0,"\xCA3"],"\x1A"=>[0,"\xCA4"],"\x1B"=>[0,"\xCA5"],"\x1C"=>[0,"\xCA6"],"\x1D"=>[0,"\xCA7"],"\x1E"=>[0,"\xCA8"],"\x1F"=>[0,"\xCA9"],"\x20"=>[0,"\xCA\x3D"],"\x21"=>[0,"\xCAA"],"\x22"=>[0,"\xCA_"],"\x23"=>[0,"\xCAb"],"\x24"=>[0,"\xCAd"],"\x25"=>[0,"\xCAf"],"\x26"=>[0,"\xCAg"],"\x27"=>[0,"\xCAh"],"\x28"=>[0,"\xCAl"],"\x29"=>[0,"\xCAm"],"\x2A"=>[0,"\xCAn"],"\x2B"=>[0,"\xCAp"],"\x2C"=>[0,"\xCAr"],"-"=>[0,"\xCAu"],"."=>[100,"\xCA"],"\x2F"=>[110,"\xCA"],[111,"\xCA"],[115,"\xCA"],[116,"\xCA"],[118,"\xCA"],[119,"\xCA"],[122,"\xCA"],[123,"\xCA"],[125,"\xCA"],[126,"\xCA"],[129,"\xCA"],"\x3A"=>[143,"\xCA"],"\x3B"=>[148,"\xCA"],"\x3C"=>[151,"\xCA"],"\x3D"=>[153,"\xCA"],"\x3E"=>[83,"\xCA"],"\x3F"=>[10,"\xCA"],"\x40"=>[77,"\xCD0"],"A"=>[18,"\xCD0"],"B"=>[77,"\xCD1"],"C"=>[18,"\xCD1"],"D"=>[77,"\xCD2"],"E"=>[18,"\xCD2"],"F"=>[77,"\xCDa"],"G"=>[18,"\xCDa"],"H"=>[77,"\xCDc"],"I"=>[18,"\xCDc"],"J"=>[77,"\xCDe"],"K"=>[18,"\xCDe"],"L"=>[77,"\xCDi"],"M"=>[18,"\xCDi"],"N"=>[77,"\xCDo"],"O"=>[18,"\xCDo"],"P"=>[77,"\xCDs"],"Q"=>[18,"\xCDs"],"R"=>[77,"\xCDt"],"S"=>[18,"\xCDt"],"T"=>[0,"\xCD\x20"],"U"=>[0,"\xCD\x25"],"V"=>[0,"\xCD-"],"W"=>[0,"\xCD."],"X"=>[0,"\xCD\x2F"],"Y"=>[0,"\xCD3"],"Z"=>[0,"\xCD4"],"\x5B"=>[0,"\xCD5"],"\x5C"=>[0,"\xCD6"],"\x5D"=>[0,"\xCD7"],"\x5E"=>[0,"\xCD8"],"_"=>[0,"\xCD9"],"\x60"=>[0,"\xCD\x3D"],"a"=>[0,"\xCDA"],"b"=>[0,"\xCD_"],"c"=>[0,"\xCDb"],"d"=>[0,"\xCDd"],"e"=>[0,"\xCDf"],"f"=>[0,"\xCDg"],"g"=>[0,"\xCDh"],"h"=>[0,"\xCDl"],"i"=>[0,"\xCDm"],"j"=>[0,"\xCDn"],"k"=>[0,"\xCDp"],"l"=>[0,"\xCDr"],"m"=>[0,"\xCDu"],"n"=>[100,"\xCD"],"o"=>[110,"\xCD"],"p"=>[111,"\xCD"],"q"=>[115,"\xCD"],"r"=>[116,"\xCD"],"s"=>[118,"\xCD"],"t"=>[119,"\xCD"],"u"=>[122,"\xCD"],"v"=>[123,"\xCD"],"w"=>[125,"\xCD"],"x"=>[126,"\xCD"],"y"=>[129,"\xCD"],"z"=>[143,"\xCD"],"\x7B"=>[148,"\xCD"],"\x7C"=>[151,"\xCD"],"\x7D"=>[153,"\xCD"],"~"=>[83,"\xCD"],"\x7F"=>[10,"\xCD"],"\x80"=>[77,"\xD20"],"\x81"=>[18,"\xD20"],"\x82"=>[77,"\xD21"],"\x83"=>[18,"\xD21"],"\x84"=>[77,"\xD22"],"\x85"=>[18,"\xD22"],"\x86"=>[77,"\xD2a"],"\x87"=>[18,"\xD2a"],"\x88"=>[77,"\xD2c"],"\x89"=>[18,"\xD2c"],"\x8A"=>[77,"\xD2e"],"\x8B"=>[18,"\xD2e"],"\x8C"=>[77,"\xD2i"],"\x8D"=>[18,"\xD2i"],"\x8E"=>[77,"\xD2o"],"\x8F"=>[18,"\xD2o"],"\x90"=>[77,"\xD2s"],"\x91"=>[18,"\xD2s"],"\x92"=>[77,"\xD2t"],"\x93"=>[18,"\xD2t"],"\x94"=>[0,"\xD2\x20"],"\x95"=>[0,"\xD2\x25"],"\x96"=>[0,"\xD2-"],"\x97"=>[0,"\xD2."],"\x98"=>[0,"\xD2\x2F"],"\x99"=>[0,"\xD23"],"\x9A"=>[0,"\xD24"],"\x9B"=>[0,"\xD25"],"\x9C"=>[0,"\xD26"],"\x9D"=>[0,"\xD27"],"\x9E"=>[0,"\xD28"],"\x9F"=>[0,"\xD29"],"\xA0"=>[0,"\xD2\x3D"],"\xA1"=>[0,"\xD2A"],"\xA2"=>[0,"\xD2_"],"\xA3"=>[0,"\xD2b"],"\xA4"=>[0,"\xD2d"],"\xA5"=>[0,"\xD2f"],"\xA6"=>[0,"\xD2g"],"\xA7"=>[0,"\xD2h"],"\xA8"=>[0,"\xD2l"],"\xA9"=>[0,"\xD2m"],"\xAA"=>[0,"\xD2n"],"\xAB"=>[0,"\xD2p"],"\xAC"=>[0,"\xD2r"],"\xAD"=>[0,"\xD2u"],"\xAE"=>[100,"\xD2"],"\xAF"=>[110,"\xD2"],"\xB0"=>[111,"\xD2"],"\xB1"=>[115,"\xD2"],"\xB2"=>[116,"\xD2"],"\xB3"=>[118,"\xD2"],"\xB4"=>[119,"\xD2"],"\xB5"=>[122,"\xD2"],"\xB6"=>[123,"\xD2"],"\xB7"=>[125,"\xD2"],"\xB8"=>[126,"\xD2"],"\xB9"=>[129,"\xD2"],"\xBA"=>[143,"\xD2"],"\xBB"=>[148,"\xD2"],"\xBC"=>[151,"\xD2"],"\xBD"=>[153,"\xD2"],"\xBE"=>[83,"\xD2"],"\xBF"=>[10,"\xD2"],"\xC0"=>[77,"\xD50"],"\xC1"=>[18,"\xD50"],"\xC2"=>[77,"\xD51"],"\xC3"=>[18,"\xD51"],"\xC4"=>[77,"\xD52"],"\xC5"=>[18,"\xD52"],"\xC6"=>[77,"\xD5a"],"\xC7"=>[18,"\xD5a"],"\xC8"=>[77,"\xD5c"],"\xC9"=>[18,"\xD5c"],"\xCA"=>[77,"\xD5e"],"\xCB"=>[18,"\xD5e"],"\xCC"=>[77,"\xD5i"],"\xCD"=>[18,"\xD5i"],"\xCE"=>[77,"\xD5o"],"\xCF"=>[18,"\xD5o"],"\xD0"=>[77,"\xD5s"],"\xD1"=>[18,"\xD5s"],"\xD2"=>[77,"\xD5t"],"\xD3"=>[18,"\xD5t"],"\xD4"=>[0,"\xD5\x20"],"\xD5"=>[0,"\xD5\x25"],"\xD6"=>[0,"\xD5-"],"\xD7"=>[0,"\xD5."],"\xD8"=>[0,"\xD5\x2F"],"\xD9"=>[0,"\xD53"],"\xDA"=>[0,"\xD54"],"\xDB"=>[0,"\xD55"],"\xDC"=>[0,"\xD56"],"\xDD"=>[0,"\xD57"],"\xDE"=>[0,"\xD58"],"\xDF"=>[0,"\xD59"],"\xE0"=>[0,"\xD5\x3D"],"\xE1"=>[0,"\xD5A"],"\xE2"=>[0,"\xD5_"],"\xE3"=>[0,"\xD5b"],"\xE4"=>[0,"\xD5d"],"\xE5"=>[0,"\xD5f"],"\xE6"=>[0,"\xD5g"],"\xE7"=>[0,"\xD5h"],"\xE8"=>[0,"\xD5l"],"\xE9"=>[0,"\xD5m"],"\xEA"=>[0,"\xD5n"],"\xEB"=>[0,"\xD5p"],"\xEC"=>[0,"\xD5r"],"\xED"=>[0,"\xD5u"],"\xEE"=>[100,"\xD5"],"\xEF"=>[110,"\xD5"],"\xF0"=>[111,"\xD5"],"\xF1"=>[115,"\xD5"],"\xF2"=>[116,"\xD5"],"\xF3"=>[118,"\xD5"],"\xF4"=>[119,"\xD5"],"\xF5"=>[122,"\xD5"],"\xF6"=>[123,"\xD5"],"\xF7"=>[125,"\xD5"],"\xF8"=>[126,"\xD5"],"\xF9"=>[129,"\xD5"],"\xFA"=>[143,"\xD5"],"\xFB"=>[148,"\xD5"],"\xFC"=>[151,"\xD5"],"\xFD"=>[153,"\xD5"],"\xFE"=>[83,"\xD5"],"\xFF"=>[10,"\xD5"],],["\x00"=>[94,"\xCA0"],"\x01"=>[76,"\xCA0"],"\x02"=>[104,"\xCA0"],"\x03"=>[16,"\xCA0"],"\x04"=>[94,"\xCA1"],"\x05"=>[76,"\xCA1"],"\x06"=>[104,"\xCA1"],"\x07"=>[16,"\xCA1"],"\x08"=>[94,"\xCA2"],"\x09"=>[76,"\xCA2"],"\x0A"=>[104,"\xCA2"],"\x0B"=>[16,"\xCA2"],"\x0C"=>[94,"\xCAa"],"\x0D"=>[76,"\xCAa"],"\x0E"=>[104,"\xCAa"],"\x0F"=>[16,"\xCAa"],"\x10"=>[94,"\xCAc"],"\x11"=>[76,"\xCAc"],"\x12"=>[104,"\xCAc"],"\x13"=>[16,"\xCAc"],"\x14"=>[94,"\xCAe"],"\x15"=>[76,"\xCAe"],"\x16"=>[104,"\xCAe"],"\x17"=>[16,"\xCAe"],"\x18"=>[94,"\xCAi"],"\x19"=>[76,"\xCAi"],"\x1A"=>[104,"\xCAi"],"\x1B"=>[16,"\xCAi"],"\x1C"=>[94,"\xCAo"],"\x1D"=>[76,"\xCAo"],"\x1E"=>[104,"\xCAo"],"\x1F"=>[16,"\xCAo"],"\x20"=>[94,"\xCAs"],"\x21"=>[76,"\xCAs"],"\x22"=>[104,"\xCAs"],"\x23"=>[16,"\xCAs"],"\x24"=>[94,"\xCAt"],"\x25"=>[76,"\xCAt"],"\x26"=>[104,"\xCAt"],"\x27"=>[16,"\xCAt"],"\x28"=>[77,"\xCA\x20"],"\x29"=>[18,"\xCA\x20"],"\x2A"=>[77,"\xCA\x25"],"\x2B"=>[18,"\xCA\x25"],"\x2C"=>[77,"\xCA-"],"-"=>[18,"\xCA-"],"."=>[77,"\xCA."],"\x2F"=>[18,"\xCA."],[77,"\xCA\x2F"],[18,"\xCA\x2F"],[77,"\xCA3"],[18,"\xCA3"],[77,"\xCA4"],[18,"\xCA4"],[77,"\xCA5"],[18,"\xCA5"],[77,"\xCA6"],[18,"\xCA6"],"\x3A"=>[77,"\xCA7"],"\x3B"=>[18,"\xCA7"],"\x3C"=>[77,"\xCA8"],"\x3D"=>[18,"\xCA8"],"\x3E"=>[77,"\xCA9"],"\x3F"=>[18,"\xCA9"],"\x40"=>[77,"\xCA\x3D"],"A"=>[18,"\xCA\x3D"],"B"=>[77,"\xCAA"],"C"=>[18,"\xCAA"],"D"=>[77,"\xCA_"],"E"=>[18,"\xCA_"],"F"=>[77,"\xCAb"],"G"=>[18,"\xCAb"],"H"=>[77,"\xCAd"],"I"=>[18,"\xCAd"],"J"=>[77,"\xCAf"],"K"=>[18,"\xCAf"],"L"=>[77,"\xCAg"],"M"=>[18,"\xCAg"],"N"=>[77,"\xCAh"],"O"=>[18,"\xCAh"],"P"=>[77,"\xCAl"],"Q"=>[18,"\xCAl"],"R"=>[77,"\xCAm"],"S"=>[18,"\xCAm"],"T"=>[77,"\xCAn"],"U"=>[18,"\xCAn"],"V"=>[77,"\xCAp"],"W"=>[18,"\xCAp"],"X"=>[77,"\xCAr"],"Y"=>[18,"\xCAr"],"Z"=>[77,"\xCAu"],"\x5B"=>[18,"\xCAu"],"\x5C"=>[0,"\xCA\x3A"],"\x5D"=>[0,"\xCAB"],"\x5E"=>[0,"\xCAC"],"_"=>[0,"\xCAD"],"\x60"=>[0,"\xCAE"],"a"=>[0,"\xCAF"],"b"=>[0,"\xCAG"],"c"=>[0,"\xCAH"],"d"=>[0,"\xCAI"],"e"=>[0,"\xCAJ"],"f"=>[0,"\xCAK"],"g"=>[0,"\xCAL"],"h"=>[0,"\xCAM"],"i"=>[0,"\xCAN"],"j"=>[0,"\xCAO"],"k"=>[0,"\xCAP"],"l"=>[0,"\xCAQ"],"m"=>[0,"\xCAR"],"n"=>[0,"\xCAS"],"o"=>[0,"\xCAT"],"p"=>[0,"\xCAU"],"q"=>[0,"\xCAV"],"r"=>[0,"\xCAW"],"s"=>[0,"\xCAY"],"t"=>[0,"\xCAj"],"u"=>[0,"\xCAk"],"v"=>[0,"\xCAq"],"w"=>[0,"\xCAv"],"x"=>[0,"\xCAw"],"y"=>[0,"\xCAx"],"z"=>[0,"\xCAy"],"\x7B"=>[0,"\xCAz"],"\x7C"=>[82,"\xCA"],"\x7D"=>[87,"\xCA"],"~"=>[130,"\xCA"],"\x7F"=>[9,"\xCA"],"\x80"=>[94,"\xCD0"],"\x81"=>[76,"\xCD0"],"\x82"=>[104,"\xCD0"],"\x83"=>[16,"\xCD0"],"\x84"=>[94,"\xCD1"],"\x85"=>[76,"\xCD1"],"\x86"=>[104,"\xCD1"],"\x87"=>[16,"\xCD1"],"\x88"=>[94,"\xCD2"],"\x89"=>[76,"\xCD2"],"\x8A"=>[104,"\xCD2"],"\x8B"=>[16,"\xCD2"],"\x8C"=>[94,"\xCDa"],"\x8D"=>[76,"\xCDa"],"\x8E"=>[104,"\xCDa"],"\x8F"=>[16,"\xCDa"],"\x90"=>[94,"\xCDc"],"\x91"=>[76,"\xCDc"],"\x92"=>[104,"\xCDc"],"\x93"=>[16,"\xCDc"],"\x94"=>[94,"\xCDe"],"\x95"=>[76,"\xCDe"],"\x96"=>[104,"\xCDe"],"\x97"=>[16,"\xCDe"],"\x98"=>[94,"\xCDi"],"\x99"=>[76,"\xCDi"],"\x9A"=>[104,"\xCDi"],"\x9B"=>[16,"\xCDi"],"\x9C"=>[94,"\xCDo"],"\x9D"=>[76,"\xCDo"],"\x9E"=>[104,"\xCDo"],"\x9F"=>[16,"\xCDo"],"\xA0"=>[94,"\xCDs"],"\xA1"=>[76,"\xCDs"],"\xA2"=>[104,"\xCDs"],"\xA3"=>[16,"\xCDs"],"\xA4"=>[94,"\xCDt"],"\xA5"=>[76,"\xCDt"],"\xA6"=>[104,"\xCDt"],"\xA7"=>[16,"\xCDt"],"\xA8"=>[77,"\xCD\x20"],"\xA9"=>[18,"\xCD\x20"],"\xAA"=>[77,"\xCD\x25"],"\xAB"=>[18,"\xCD\x25"],"\xAC"=>[77,"\xCD-"],"\xAD"=>[18,"\xCD-"],"\xAE"=>[77,"\xCD."],"\xAF"=>[18,"\xCD."],"\xB0"=>[77,"\xCD\x2F"],"\xB1"=>[18,"\xCD\x2F"],"\xB2"=>[77,"\xCD3"],"\xB3"=>[18,"\xCD3"],"\xB4"=>[77,"\xCD4"],"\xB5"=>[18,"\xCD4"],"\xB6"=>[77,"\xCD5"],"\xB7"=>[18,"\xCD5"],"\xB8"=>[77,"\xCD6"],"\xB9"=>[18,"\xCD6"],"\xBA"=>[77,"\xCD7"],"\xBB"=>[18,"\xCD7"],"\xBC"=>[77,"\xCD8"],"\xBD"=>[18,"\xCD8"],"\xBE"=>[77,"\xCD9"],"\xBF"=>[18,"\xCD9"],"\xC0"=>[77,"\xCD\x3D"],"\xC1"=>[18,"\xCD\x3D"],"\xC2"=>[77,"\xCDA"],"\xC3"=>[18,"\xCDA"],"\xC4"=>[77,"\xCD_"],"\xC5"=>[18,"\xCD_"],"\xC6"=>[77,"\xCDb"],"\xC7"=>[18,"\xCDb"],"\xC8"=>[77,"\xCDd"],"\xC9"=>[18,"\xCDd"],"\xCA"=>[77,"\xCDf"],"\xCB"=>[18,"\xCDf"],"\xCC"=>[77,"\xCDg"],"\xCD"=>[18,"\xCDg"],"\xCE"=>[77,"\xCDh"],"\xCF"=>[18,"\xCDh"],"\xD0"=>[77,"\xCDl"],"\xD1"=>[18,"\xCDl"],"\xD2"=>[77,"\xCDm"],"\xD3"=>[18,"\xCDm"],"\xD4"=>[77,"\xCDn"],"\xD5"=>[18,"\xCDn"],"\xD6"=>[77,"\xCDp"],"\xD7"=>[18,"\xCDp"],"\xD8"=>[77,"\xCDr"],"\xD9"=>[18,"\xCDr"],"\xDA"=>[77,"\xCDu"],"\xDB"=>[18,"\xCDu"],"\xDC"=>[0,"\xCD\x3A"],"\xDD"=>[0,"\xCDB"],"\xDE"=>[0,"\xCDC"],"\xDF"=>[0,"\xCDD"],"\xE0"=>[0,"\xCDE"],"\xE1"=>[0,"\xCDF"],"\xE2"=>[0,"\xCDG"],"\xE3"=>[0,"\xCDH"],"\xE4"=>[0,"\xCDI"],"\xE5"=>[0,"\xCDJ"],"\xE6"=>[0,"\xCDK"],"\xE7"=>[0,"\xCDL"],"\xE8"=>[0,"\xCDM"],"\xE9"=>[0,"\xCDN"],"\xEA"=>[0,"\xCDO"],"\xEB"=>[0,"\xCDP"],"\xEC"=>[0,"\xCDQ"],"\xED"=>[0,"\xCDR"],"\xEE"=>[0,"\xCDS"],"\xEF"=>[0,"\xCDT"],"\xF0"=>[0,"\xCDU"],"\xF1"=>[0,"\xCDV"],"\xF2"=>[0,"\xCDW"],"\xF3"=>[0,"\xCDY"],"\xF4"=>[0,"\xCDj"],"\xF5"=>[0,"\xCDk"],"\xF6"=>[0,"\xCDq"],"\xF7"=>[0,"\xCDv"],"\xF8"=>[0,"\xCDw"],"\xF9"=>[0,"\xCDx"],"\xFA"=>[0,"\xCDy"],"\xFB"=>[0,"\xCDz"],"\xFC"=>[82,"\xCD"],"\xFD"=>[87,"\xCD"],"\xFE"=>[130,"\xCD"],"\xFF"=>[9,"\xCD"],],["\x00"=>[77,"\xF20"],"\x01"=>[18,"\xF20"],"\x02"=>[77,"\xF21"],"\x03"=>[18,"\xF21"],"\x04"=>[77,"\xF22"],"\x05"=>[18,"\xF22"],"\x06"=>[77,"\xF2a"],"\x07"=>[18,"\xF2a"],"\x08"=>[77,"\xF2c"],"\x09"=>[18,"\xF2c"],"\x0A"=>[77,"\xF2e"],"\x0B"=>[18,"\xF2e"],"\x0C"=>[77,"\xF2i"],"\x0D"=>[18,"\xF2i"],"\x0E"=>[77,"\xF2o"],"\x0F"=>[18,"\xF2o"],"\x10"=>[77,"\xF2s"],"\x11"=>[18,"\xF2s"],"\x12"=>[77,"\xF2t"],"\x13"=>[18,"\xF2t"],"\x14"=>[0,"\xF2\x20"],"\x15"=>[0,"\xF2\x25"],"\x16"=>[0,"\xF2-"],"\x17"=>[0,"\xF2."],"\x18"=>[0,"\xF2\x2F"],"\x19"=>[0,"\xF23"],"\x1A"=>[0,"\xF24"],"\x1B"=>[0,"\xF25"],"\x1C"=>[0,"\xF26"],"\x1D"=>[0,"\xF27"],"\x1E"=>[0,"\xF28"],"\x1F"=>[0,"\xF29"],"\x20"=>[0,"\xF2\x3D"],"\x21"=>[0,"\xF2A"],"\x22"=>[0,"\xF2_"],"\x23"=>[0,"\xF2b"],"\x24"=>[0,"\xF2d"],"\x25"=>[0,"\xF2f"],"\x26"=>[0,"\xF2g"],"\x27"=>[0,"\xF2h"],"\x28"=>[0,"\xF2l"],"\x29"=>[0,"\xF2m"],"\x2A"=>[0,"\xF2n"],"\x2B"=>[0,"\xF2p"],"\x2C"=>[0,"\xF2r"],"-"=>[0,"\xF2u"],"."=>[100,"\xF2"],"\x2F"=>[110,"\xF2"],[111,"\xF2"],[115,"\xF2"],[116,"\xF2"],[118,"\xF2"],[119,"\xF2"],[122,"\xF2"],[123,"\xF2"],[125,"\xF2"],[126,"\xF2"],[129,"\xF2"],"\x3A"=>[143,"\xF2"],"\x3B"=>[148,"\xF2"],"\x3C"=>[151,"\xF2"],"\x3D"=>[153,"\xF2"],"\x3E"=>[83,"\xF2"],"\x3F"=>[10,"\xF2"],"\x40"=>[77,"\xF30"],"A"=>[18,"\xF30"],"B"=>[77,"\xF31"],"C"=>[18,"\xF31"],"D"=>[77,"\xF32"],"E"=>[18,"\xF32"],"F"=>[77,"\xF3a"],"G"=>[18,"\xF3a"],"H"=>[77,"\xF3c"],"I"=>[18,"\xF3c"],"J"=>[77,"\xF3e"],"K"=>[18,"\xF3e"],"L"=>[77,"\xF3i"],"M"=>[18,"\xF3i"],"N"=>[77,"\xF3o"],"O"=>[18,"\xF3o"],"P"=>[77,"\xF3s"],"Q"=>[18,"\xF3s"],"R"=>[77,"\xF3t"],"S"=>[18,"\xF3t"],"T"=>[0,"\xF3\x20"],"U"=>[0,"\xF3\x25"],"V"=>[0,"\xF3-"],"W"=>[0,"\xF3."],"X"=>[0,"\xF3\x2F"],"Y"=>[0,"\xF33"],"Z"=>[0,"\xF34"],"\x5B"=>[0,"\xF35"],"\x5C"=>[0,"\xF36"],"\x5D"=>[0,"\xF37"],"\x5E"=>[0,"\xF38"],"_"=>[0,"\xF39"],"\x60"=>[0,"\xF3\x3D"],"a"=>[0,"\xF3A"],"b"=>[0,"\xF3_"],"c"=>[0,"\xF3b"],"d"=>[0,"\xF3d"],"e"=>[0,"\xF3f"],"f"=>[0,"\xF3g"],"g"=>[0,"\xF3h"],"h"=>[0,"\xF3l"],"i"=>[0,"\xF3m"],"j"=>[0,"\xF3n"],"k"=>[0,"\xF3p"],"l"=>[0,"\xF3r"],"m"=>[0,"\xF3u"],"n"=>[100,"\xF3"],"o"=>[110,"\xF3"],"p"=>[111,"\xF3"],"q"=>[115,"\xF3"],"r"=>[116,"\xF3"],"s"=>[118,"\xF3"],"t"=>[119,"\xF3"],"u"=>[122,"\xF3"],"v"=>[123,"\xF3"],"w"=>[125,"\xF3"],"x"=>[126,"\xF3"],"y"=>[129,"\xF3"],"z"=>[143,"\xF3"],"\x7B"=>[148,"\xF3"],"\x7C"=>[151,"\xF3"],"\x7D"=>[153,"\xF3"],"~"=>[83,"\xF3"],"\x7F"=>[10,"\xF3"],"\x80"=>[77,"\xFF0"],"\x81"=>[18,"\xFF0"],"\x82"=>[77,"\xFF1"],"\x83"=>[18,"\xFF1"],"\x84"=>[77,"\xFF2"],"\x85"=>[18,"\xFF2"],"\x86"=>[77,"\xFFa"],"\x87"=>[18,"\xFFa"],"\x88"=>[77,"\xFFc"],"\x89"=>[18,"\xFFc"],"\x8A"=>[77,"\xFFe"],"\x8B"=>[18,"\xFFe"],"\x8C"=>[77,"\xFFi"],"\x8D"=>[18,"\xFFi"],"\x8E"=>[77,"\xFFo"],"\x8F"=>[18,"\xFFo"],"\x90"=>[77,"\xFFs"],"\x91"=>[18,"\xFFs"],"\x92"=>[77,"\xFFt"],"\x93"=>[18,"\xFFt"],"\x94"=>[0,"\xFF\x20"],"\x95"=>[0,"\xFF\x25"],"\x96"=>[0,"\xFF-"],"\x97"=>[0,"\xFF."],"\x98"=>[0,"\xFF\x2F"],"\x99"=>[0,"\xFF3"],"\x9A"=>[0,"\xFF4"],"\x9B"=>[0,"\xFF5"],"\x9C"=>[0,"\xFF6"],"\x9D"=>[0,"\xFF7"],"\x9E"=>[0,"\xFF8"],"\x9F"=>[0,"\xFF9"],"\xA0"=>[0,"\xFF\x3D"],"\xA1"=>[0,"\xFFA"],"\xA2"=>[0,"\xFF_"],"\xA3"=>[0,"\xFFb"],"\xA4"=>[0,"\xFFd"],"\xA5"=>[0,"\xFFf"],"\xA6"=>[0,"\xFFg"],"\xA7"=>[0,"\xFFh"],"\xA8"=>[0,"\xFFl"],"\xA9"=>[0,"\xFFm"],"\xAA"=>[0,"\xFFn"],"\xAB"=>[0,"\xFFp"],"\xAC"=>[0,"\xFFr"],"\xAD"=>[0,"\xFFu"],"\xAE"=>[100,"\xFF"],"\xAF"=>[110,"\xFF"],"\xB0"=>[111,"\xFF"],"\xB1"=>[115,"\xFF"],"\xB2"=>[116,"\xFF"],"\xB3"=>[118,"\xFF"],"\xB4"=>[119,"\xFF"],"\xB5"=>[122,"\xFF"],"\xB6"=>[123,"\xFF"],"\xB7"=>[125,"\xFF"],"\xB8"=>[126,"\xFF"],"\xB9"=>[129,"\xFF"],"\xBA"=>[143,"\xFF"],"\xBB"=>[148,"\xFF"],"\xBC"=>[151,"\xFF"],"\xBD"=>[153,"\xFF"],"\xBE"=>[83,"\xFF"],"\xBF"=>[10,"\xFF"],"\xC0"=>[0,"\xCB0"],"\xC1"=>[0,"\xCB1"],"\xC2"=>[0,"\xCB2"],"\xC3"=>[0,"\xCBa"],"\xC4"=>[0,"\xCBc"],"\xC5"=>[0,"\xCBe"],"\xC6"=>[0,"\xCBi"],"\xC7"=>[0,"\xCBo"],"\xC8"=>[0,"\xCBs"],"\xC9"=>[0,"\xCBt"],"\xCA"=>[73,"\xCB"],"\xCB"=>[88,"\xCB"],"\xCC"=>[89,"\xCB"],"\xCD"=>[96,"\xCB"],"\xCE"=>[97,"\xCB"],"\xCF"=>[99,"\xCB"],"\xD0"=>[106,"\xCB"],"\xD1"=>[136,"\xCB"],"\xD2"=>[139,"\xCB"],"\xD3"=>[141,"\xCB"],"\xD4"=>[145,"\xCB"],"\xD5"=>[147,"\xCB"],"\xD6"=>[149,"\xCB"],"\xD7"=>[101,"\xCB"],"\xD8"=>[112,"\xCB"],"\xD9"=>[117,"\xCB"],"\xDA"=>[120,"\xCB"],"\xDB"=>[124,"\xCB"],"\xDC"=>[127,"\xCB"],"\xDD"=>[144,"\xCB"],"\xDE"=>[152,"\xCB"],"\xDF"=>[11,"\xCB"],"\xE0"=>[0,"\xCC0"],"\xE1"=>[0,"\xCC1"],"\xE2"=>[0,"\xCC2"],"\xE3"=>[0,"\xCCa"],"\xE4"=>[0,"\xCCc"],"\xE5"=>[0,"\xCCe"],"\xE6"=>[0,"\xCCi"],"\xE7"=>[0,"\xCCo"],"\xE8"=>[0,"\xCCs"],"\xE9"=>[0,"\xCCt"],"\xEA"=>[73,"\xCC"],"\xEB"=>[88,"\xCC"],"\xEC"=>[89,"\xCC"],"\xED"=>[96,"\xCC"],"\xEE"=>[97,"\xCC"],"\xEF"=>[99,"\xCC"],"\xF0"=>[106,"\xCC"],"\xF1"=>[136,"\xCC"],"\xF2"=>[139,"\xCC"],"\xF3"=>[141,"\xCC"],"\xF4"=>[145,"\xCC"],"\xF5"=>[147,"\xCC"],"\xF6"=>[149,"\xCC"],"\xF7"=>[101,"\xCC"],"\xF8"=>[112,"\xCC"],"\xF9"=>[117,"\xCC"],"\xFA"=>[120,"\xCC"],"\xFB"=>[124,"\xCC"],"\xFC"=>[127,"\xCC"],"\xFD"=>[144,"\xCC"],"\xFE"=>[152,"\xCC"],"\xFF"=>[11,"\xCC"],],["\x00"=>[0,"\xDA0"],"\x01"=>[0,"\xDA1"],"\x02"=>[0,"\xDA2"],"\x03"=>[0,"\xDAa"],"\x04"=>[0,"\xDAc"],"\x05"=>[0,"\xDAe"],"\x06"=>[0,"\xDAi"],"\x07"=>[0,"\xDAo"],"\x08"=>[0,"\xDAs"],"\x09"=>[0,"\xDAt"],"\x0A"=>[73,"\xDA"],"\x0B"=>[88,"\xDA"],"\x0C"=>[89,"\xDA"],"\x0D"=>[96,"\xDA"],"\x0E"=>[97,"\xDA"],"\x0F"=>[99,"\xDA"],"\x10"=>[106,"\xDA"],"\x11"=>[136,"\xDA"],"\x12"=>[139,"\xDA"],"\x13"=>[141,"\xDA"],"\x14"=>[145,"\xDA"],"\x15"=>[147,"\xDA"],"\x16"=>[149,"\xDA"],"\x17"=>[101,"\xDA"],"\x18"=>[112,"\xDA"],"\x19"=>[117,"\xDA"],"\x1A"=>[120,"\xDA"],"\x1B"=>[124,"\xDA"],"\x1C"=>[127,"\xDA"],"\x1D"=>[144,"\xDA"],"\x1E"=>[152,"\xDA"],"\x1F"=>[11,"\xDA"],"\x20"=>[0,"\xDB0"],"\x21"=>[0,"\xDB1"],"\x22"=>[0,"\xDB2"],"\x23"=>[0,"\xDBa"],"\x24"=>[0,"\xDBc"],"\x25"=>[0,"\xDBe"],"\x26"=>[0,"\xDBi"],"\x27"=>[0,"\xDBo"],"\x28"=>[0,"\xDBs"],"\x29"=>[0,"\xDBt"],"\x2A"=>[73,"\xDB"],"\x2B"=>[88,"\xDB"],"\x2C"=>[89,"\xDB"],"-"=>[96,"\xDB"],"."=>[97,"\xDB"],"\x2F"=>[99,"\xDB"],[106,"\xDB"],[136,"\xDB"],[139,"\xDB"],[141,"\xDB"],[145,"\xDB"],[147,"\xDB"],[149,"\xDB"],[101,"\xDB"],[112,"\xDB"],[117,"\xDB"],"\x3A"=>[120,"\xDB"],"\x3B"=>[124,"\xDB"],"\x3C"=>[127,"\xDB"],"\x3D"=>[144,"\xDB"],"\x3E"=>[152,"\xDB"],"\x3F"=>[11,"\xDB"],"\x40"=>[0,"\xEE0"],"A"=>[0,"\xEE1"],"B"=>[0,"\xEE2"],"C"=>[0,"\xEEa"],"D"=>[0,"\xEEc"],"E"=>[0,"\xEEe"],"F"=>[0,"\xEEi"],"G"=>[0,"\xEEo"],"H"=>[0,"\xEEs"],"I"=>[0,"\xEEt"],"J"=>[73,"\xEE"],"K"=>[88,"\xEE"],"L"=>[89,"\xEE"],"M"=>[96,"\xEE"],"N"=>[97,"\xEE"],"O"=>[99,"\xEE"],"P"=>[106,"\xEE"],"Q"=>[136,"\xEE"],"R"=>[139,"\xEE"],"S"=>[141,"\xEE"],"T"=>[145,"\xEE"],"U"=>[147,"\xEE"],"V"=>[149,"\xEE"],"W"=>[101,"\xEE"],"X"=>[112,"\xEE"],"Y"=>[117,"\xEE"],"Z"=>[120,"\xEE"],"\x5B"=>[124,"\xEE"],"\x5C"=>[127,"\xEE"],"\x5D"=>[144,"\xEE"],"\x5E"=>[152,"\xEE"],"_"=>[11,"\xEE"],"\x60"=>[0,"\xF00"],"a"=>[0,"\xF01"],"b"=>[0,"\xF02"],"c"=>[0,"\xF0a"],"d"=>[0,"\xF0c"],"e"=>[0,"\xF0e"],"f"=>[0,"\xF0i"],"g"=>[0,"\xF0o"],"h"=>[0,"\xF0s"],"i"=>[0,"\xF0t"],"j"=>[73,"\xF0"],"k"=>[88,"\xF0"],"l"=>[89,"\xF0"],"m"=>[96,"\xF0"],"n"=>[97,"\xF0"],"o"=>[99,"\xF0"],"p"=>[106,"\xF0"],"q"=>[136,"\xF0"],"r"=>[139,"\xF0"],"s"=>[141,"\xF0"],"t"=>[145,"\xF0"],"u"=>[147,"\xF0"],"v"=>[149,"\xF0"],"w"=>[101,"\xF0"],"x"=>[112,"\xF0"],"y"=>[117,"\xF0"],"z"=>[120,"\xF0"],"\x7B"=>[124,"\xF0"],"\x7C"=>[127,"\xF0"],"\x7D"=>[144,"\xF0"],"~"=>[152,"\xF0"],"\x7F"=>[11,"\xF0"],"\x80"=>[0,"\xF20"],"\x81"=>[0,"\xF21"],"\x82"=>[0,"\xF22"],"\x83"=>[0,"\xF2a"],"\x84"=>[0,"\xF2c"],"\x85"=>[0,"\xF2e"],"\x86"=>[0,"\xF2i"],"\x87"=>[0,"\xF2o"],"\x88"=>[0,"\xF2s"],"\x89"=>[0,"\xF2t"],"\x8A"=>[73,"\xF2"],"\x8B"=>[88,"\xF2"],"\x8C"=>[89,"\xF2"],"\x8D"=>[96,"\xF2"],"\x8E"=>[97,"\xF2"],"\x8F"=>[99,"\xF2"],"\x90"=>[106,"\xF2"],"\x91"=>[136,"\xF2"],"\x92"=>[139,"\xF2"],"\x93"=>[141,"\xF2"],"\x94"=>[145,"\xF2"],"\x95"=>[147,"\xF2"],"\x96"=>[149,"\xF2"],"\x97"=>[101,"\xF2"],"\x98"=>[112,"\xF2"],"\x99"=>[117,"\xF2"],"\x9A"=>[120,"\xF2"],"\x9B"=>[124,"\xF2"],"\x9C"=>[127,"\xF2"],"\x9D"=>[144,"\xF2"],"\x9E"=>[152,"\xF2"],"\x9F"=>[11,"\xF2"],"\xA0"=>[0,"\xF30"],"\xA1"=>[0,"\xF31"],"\xA2"=>[0,"\xF32"],"\xA3"=>[0,"\xF3a"],"\xA4"=>[0,"\xF3c"],"\xA5"=>[0,"\xF3e"],"\xA6"=>[0,"\xF3i"],"\xA7"=>[0,"\xF3o"],"\xA8"=>[0,"\xF3s"],"\xA9"=>[0,"\xF3t"],"\xAA"=>[73,"\xF3"],"\xAB"=>[88,"\xF3"],"\xAC"=>[89,"\xF3"],"\xAD"=>[96,"\xF3"],"\xAE"=>[97,"\xF3"],"\xAF"=>[99,"\xF3"],"\xB0"=>[106,"\xF3"],"\xB1"=>[136,"\xF3"],"\xB2"=>[139,"\xF3"],"\xB3"=>[141,"\xF3"],"\xB4"=>[145,"\xF3"],"\xB5"=>[147,"\xF3"],"\xB6"=>[149,"\xF3"],"\xB7"=>[101,"\xF3"],"\xB8"=>[112,"\xF3"],"\xB9"=>[117,"\xF3"],"\xBA"=>[120,"\xF3"],"\xBB"=>[124,"\xF3"],"\xBC"=>[127,"\xF3"],"\xBD"=>[144,"\xF3"],"\xBE"=>[152,"\xF3"],"\xBF"=>[11,"\xF3"],"\xC0"=>[0,"\xFF0"],"\xC1"=>[0,"\xFF1"],"\xC2"=>[0,"\xFF2"],"\xC3"=>[0,"\xFFa"],"\xC4"=>[0,"\xFFc"],"\xC5"=>[0,"\xFFe"],"\xC6"=>[0,"\xFFi"],"\xC7"=>[0,"\xFFo"],"\xC8"=>[0,"\xFFs"],"\xC9"=>[0,"\xFFt"],"\xCA"=>[73,"\xFF"],"\xCB"=>[88,"\xFF"],"\xCC"=>[89,"\xFF"],"\xCD"=>[96,"\xFF"],"\xCE"=>[97,"\xFF"],"\xCF"=>[99,"\xFF"],"\xD0"=>[106,"\xFF"],"\xD1"=>[136,"\xFF"],"\xD2"=>[139,"\xFF"],"\xD3"=>[141,"\xFF"],"\xD4"=>[145,"\xFF"],"\xD5"=>[147,"\xFF"],"\xD6"=>[149,"\xFF"],"\xD7"=>[101,"\xFF"],"\xD8"=>[112,"\xFF"],"\xD9"=>[117,"\xFF"],"\xDA"=>[120,"\xFF"],"\xDB"=>[124,"\xFF"],"\xDC"=>[127,"\xFF"],"\xDD"=>[144,"\xFF"],"\xDE"=>[152,"\xFF"],"\xDF"=>[11,"\xFF"],"\xE0"=>[92,"\xCB"],"\xE1"=>[95,"\xCB"],"\xE2"=>[137,"\xCB"],"\xE3"=>[142,"\xCB"],"\xE4"=>[150,"\xCB"],"\xE5"=>[74,"\xCB"],"\xE6"=>[90,"\xCB"],"\xE7"=>[98,"\xCB"],"\xE8"=>[107,"\xCB"],"\xE9"=>[140,"\xCB"],"\xEA"=>[146,"\xCB"],"\xEB"=>[102,"\xCB"],"\xEC"=>[113,"\xCB"],"\xED"=>[121,"\xCB"],"\xEE"=>[128,"\xCB"],"\xEF"=>[12,"\xCB"],"\xF0"=>[92,"\xCC"],"\xF1"=>[95,"\xCC"],"\xF2"=>[137,"\xCC"],"\xF3"=>[142,"\xCC"],"\xF4"=>[150,"\xCC"],"\xF5"=>[74,"\xCC"],"\xF6"=>[90,"\xCC"],"\xF7"=>[98,"\xCC"],"\xF8"=>[107,"\xCC"],"\xF9"=>[140,"\xCC"],"\xFA"=>[146,"\xCC"],"\xFB"=>[102,"\xCC"],"\xFC"=>[113,"\xCC"],"\xFD"=>[121,"\xCC"],"\xFE"=>[128,"\xCC"],"\xFF"=>[12,"\xCC"],],["\x00"=>[94,"\xCB0"],"\x01"=>[76,"\xCB0"],"\x02"=>[104,"\xCB0"],"\x03"=>[16,"\xCB0"],"\x04"=>[94,"\xCB1"],"\x05"=>[76,"\xCB1"],"\x06"=>[104,"\xCB1"],"\x07"=>[16,"\xCB1"],"\x08"=>[94,"\xCB2"],"\x09"=>[76,"\xCB2"],"\x0A"=>[104,"\xCB2"],"\x0B"=>[16,"\xCB2"],"\x0C"=>[94,"\xCBa"],"\x0D"=>[76,"\xCBa"],"\x0E"=>[104,"\xCBa"],"\x0F"=>[16,"\xCBa"],"\x10"=>[94,"\xCBc"],"\x11"=>[76,"\xCBc"],"\x12"=>[104,"\xCBc"],"\x13"=>[16,"\xCBc"],"\x14"=>[94,"\xCBe"],"\x15"=>[76,"\xCBe"],"\x16"=>[104,"\xCBe"],"\x17"=>[16,"\xCBe"],"\x18"=>[94,"\xCBi"],"\x19"=>[76,"\xCBi"],"\x1A"=>[104,"\xCBi"],"\x1B"=>[16,"\xCBi"],"\x1C"=>[94,"\xCBo"],"\x1D"=>[76,"\xCBo"],"\x1E"=>[104,"\xCBo"],"\x1F"=>[16,"\xCBo"],"\x20"=>[94,"\xCBs"],"\x21"=>[76,"\xCBs"],"\x22"=>[104,"\xCBs"],"\x23"=>[16,"\xCBs"],"\x24"=>[94,"\xCBt"],"\x25"=>[76,"\xCBt"],"\x26"=>[104,"\xCBt"],"\x27"=>[16,"\xCBt"],"\x28"=>[77,"\xCB\x20"],"\x29"=>[18,"\xCB\x20"],"\x2A"=>[77,"\xCB\x25"],"\x2B"=>[18,"\xCB\x25"],"\x2C"=>[77,"\xCB-"],"-"=>[18,"\xCB-"],"."=>[77,"\xCB."],"\x2F"=>[18,"\xCB."],[77,"\xCB\x2F"],[18,"\xCB\x2F"],[77,"\xCB3"],[18,"\xCB3"],[77,"\xCB4"],[18,"\xCB4"],[77,"\xCB5"],[18,"\xCB5"],[77,"\xCB6"],[18,"\xCB6"],"\x3A"=>[77,"\xCB7"],"\x3B"=>[18,"\xCB7"],"\x3C"=>[77,"\xCB8"],"\x3D"=>[18,"\xCB8"],"\x3E"=>[77,"\xCB9"],"\x3F"=>[18,"\xCB9"],"\x40"=>[77,"\xCB\x3D"],"A"=>[18,"\xCB\x3D"],"B"=>[77,"\xCBA"],"C"=>[18,"\xCBA"],"D"=>[77,"\xCB_"],"E"=>[18,"\xCB_"],"F"=>[77,"\xCBb"],"G"=>[18,"\xCBb"],"H"=>[77,"\xCBd"],"I"=>[18,"\xCBd"],"J"=>[77,"\xCBf"],"K"=>[18,"\xCBf"],"L"=>[77,"\xCBg"],"M"=>[18,"\xCBg"],"N"=>[77,"\xCBh"],"O"=>[18,"\xCBh"],"P"=>[77,"\xCBl"],"Q"=>[18,"\xCBl"],"R"=>[77,"\xCBm"],"S"=>[18,"\xCBm"],"T"=>[77,"\xCBn"],"U"=>[18,"\xCBn"],"V"=>[77,"\xCBp"],"W"=>[18,"\xCBp"],"X"=>[77,"\xCBr"],"Y"=>[18,"\xCBr"],"Z"=>[77,"\xCBu"],"\x5B"=>[18,"\xCBu"],"\x5C"=>[0,"\xCB\x3A"],"\x5D"=>[0,"\xCBB"],"\x5E"=>[0,"\xCBC"],"_"=>[0,"\xCBD"],"\x60"=>[0,"\xCBE"],"a"=>[0,"\xCBF"],"b"=>[0,"\xCBG"],"c"=>[0,"\xCBH"],"d"=>[0,"\xCBI"],"e"=>[0,"\xCBJ"],"f"=>[0,"\xCBK"],"g"=>[0,"\xCBL"],"h"=>[0,"\xCBM"],"i"=>[0,"\xCBN"],"j"=>[0,"\xCBO"],"k"=>[0,"\xCBP"],"l"=>[0,"\xCBQ"],"m"=>[0,"\xCBR"],"n"=>[0,"\xCBS"],"o"=>[0,"\xCBT"],"p"=>[0,"\xCBU"],"q"=>[0,"\xCBV"],"r"=>[0,"\xCBW"],"s"=>[0,"\xCBY"],"t"=>[0,"\xCBj"],"u"=>[0,"\xCBk"],"v"=>[0,"\xCBq"],"w"=>[0,"\xCBv"],"x"=>[0,"\xCBw"],"y"=>[0,"\xCBx"],"z"=>[0,"\xCBy"],"\x7B"=>[0,"\xCBz"],"\x7C"=>[82,"\xCB"],"\x7D"=>[87,"\xCB"],"~"=>[130,"\xCB"],"\x7F"=>[9,"\xCB"],"\x80"=>[94,"\xCC0"],"\x81"=>[76,"\xCC0"],"\x82"=>[104,"\xCC0"],"\x83"=>[16,"\xCC0"],"\x84"=>[94,"\xCC1"],"\x85"=>[76,"\xCC1"],"\x86"=>[104,"\xCC1"],"\x87"=>[16,"\xCC1"],"\x88"=>[94,"\xCC2"],"\x89"=>[76,"\xCC2"],"\x8A"=>[104,"\xCC2"],"\x8B"=>[16,"\xCC2"],"\x8C"=>[94,"\xCCa"],"\x8D"=>[76,"\xCCa"],"\x8E"=>[104,"\xCCa"],"\x8F"=>[16,"\xCCa"],"\x90"=>[94,"\xCCc"],"\x91"=>[76,"\xCCc"],"\x92"=>[104,"\xCCc"],"\x93"=>[16,"\xCCc"],"\x94"=>[94,"\xCCe"],"\x95"=>[76,"\xCCe"],"\x96"=>[104,"\xCCe"],"\x97"=>[16,"\xCCe"],"\x98"=>[94,"\xCCi"],"\x99"=>[76,"\xCCi"],"\x9A"=>[104,"\xCCi"],"\x9B"=>[16,"\xCCi"],"\x9C"=>[94,"\xCCo"],"\x9D"=>[76,"\xCCo"],"\x9E"=>[104,"\xCCo"],"\x9F"=>[16,"\xCCo"],"\xA0"=>[94,"\xCCs"],"\xA1"=>[76,"\xCCs"],"\xA2"=>[104,"\xCCs"],"\xA3"=>[16,"\xCCs"],"\xA4"=>[94,"\xCCt"],"\xA5"=>[76,"\xCCt"],"\xA6"=>[104,"\xCCt"],"\xA7"=>[16,"\xCCt"],"\xA8"=>[77,"\xCC\x20"],"\xA9"=>[18,"\xCC\x20"],"\xAA"=>[77,"\xCC\x25"],"\xAB"=>[18,"\xCC\x25"],"\xAC"=>[77,"\xCC-"],"\xAD"=>[18,"\xCC-"],"\xAE"=>[77,"\xCC."],"\xAF"=>[18,"\xCC."],"\xB0"=>[77,"\xCC\x2F"],"\xB1"=>[18,"\xCC\x2F"],"\xB2"=>[77,"\xCC3"],"\xB3"=>[18,"\xCC3"],"\xB4"=>[77,"\xCC4"],"\xB5"=>[18,"\xCC4"],"\xB6"=>[77,"\xCC5"],"\xB7"=>[18,"\xCC5"],"\xB8"=>[77,"\xCC6"],"\xB9"=>[18,"\xCC6"],"\xBA"=>[77,"\xCC7"],"\xBB"=>[18,"\xCC7"],"\xBC"=>[77,"\xCC8"],"\xBD"=>[18,"\xCC8"],"\xBE"=>[77,"\xCC9"],"\xBF"=>[18,"\xCC9"],"\xC0"=>[77,"\xCC\x3D"],"\xC1"=>[18,"\xCC\x3D"],"\xC2"=>[77,"\xCCA"],"\xC3"=>[18,"\xCCA"],"\xC4"=>[77,"\xCC_"],"\xC5"=>[18,"\xCC_"],"\xC6"=>[77,"\xCCb"],"\xC7"=>[18,"\xCCb"],"\xC8"=>[77,"\xCCd"],"\xC9"=>[18,"\xCCd"],"\xCA"=>[77,"\xCCf"],"\xCB"=>[18,"\xCCf"],"\xCC"=>[77,"\xCCg"],"\xCD"=>[18,"\xCCg"],"\xCE"=>[77,"\xCCh"],"\xCF"=>[18,"\xCCh"],"\xD0"=>[77,"\xCCl"],"\xD1"=>[18,"\xCCl"],"\xD2"=>[77,"\xCCm"],"\xD3"=>[18,"\xCCm"],"\xD4"=>[77,"\xCCn"],"\xD5"=>[18,"\xCCn"],"\xD6"=>[77,"\xCCp"],"\xD7"=>[18,"\xCCp"],"\xD8"=>[77,"\xCCr"],"\xD9"=>[18,"\xCCr"],"\xDA"=>[77,"\xCCu"],"\xDB"=>[18,"\xCCu"],"\xDC"=>[0,"\xCC\x3A"],"\xDD"=>[0,"\xCCB"],"\xDE"=>[0,"\xCCC"],"\xDF"=>[0,"\xCCD"],"\xE0"=>[0,"\xCCE"],"\xE1"=>[0,"\xCCF"],"\xE2"=>[0,"\xCCG"],"\xE3"=>[0,"\xCCH"],"\xE4"=>[0,"\xCCI"],"\xE5"=>[0,"\xCCJ"],"\xE6"=>[0,"\xCCK"],"\xE7"=>[0,"\xCCL"],"\xE8"=>[0,"\xCCM"],"\xE9"=>[0,"\xCCN"],"\xEA"=>[0,"\xCCO"],"\xEB"=>[0,"\xCCP"],"\xEC"=>[0,"\xCCQ"],"\xED"=>[0,"\xCCR"],"\xEE"=>[0,"\xCCS"],"\xEF"=>[0,"\xCCT"],"\xF0"=>[0,"\xCCU"],"\xF1"=>[0,"\xCCV"],"\xF2"=>[0,"\xCCW"],"\xF3"=>[0,"\xCCY"],"\xF4"=>[0,"\xCCj"],"\xF5"=>[0,"\xCCk"],"\xF6"=>[0,"\xCCq"],"\xF7"=>[0,"\xCCv"],"\xF8"=>[0,"\xCCw"],"\xF9"=>[0,"\xCCx"],"\xFA"=>[0,"\xCCy"],"\xFB"=>[0,"\xCCz"],"\xFC"=>[82,"\xCC"],"\xFD"=>[87,"\xCC"],"\xFE"=>[130,"\xCC"],"\xFF"=>[9,"\xCC"],],["\x00"=>[94,"\xFF0"],"\x01"=>[76,"\xFF0"],"\x02"=>[104,"\xFF0"],"\x03"=>[16,"\xFF0"],"\x04"=>[94,"\xFF1"],"\x05"=>[76,"\xFF1"],"\x06"=>[104,"\xFF1"],"\x07"=>[16,"\xFF1"],"\x08"=>[94,"\xFF2"],"\x09"=>[76,"\xFF2"],"\x0A"=>[104,"\xFF2"],"\x0B"=>[16,"\xFF2"],"\x0C"=>[94,"\xFFa"],"\x0D"=>[76,"\xFFa"],"\x0E"=>[104,"\xFFa"],"\x0F"=>[16,"\xFFa"],"\x10"=>[94,"\xFFc"],"\x11"=>[76,"\xFFc"],"\x12"=>[104,"\xFFc"],"\x13"=>[16,"\xFFc"],"\x14"=>[94,"\xFFe"],"\x15"=>[76,"\xFFe"],"\x16"=>[104,"\xFFe"],"\x17"=>[16,"\xFFe"],"\x18"=>[94,"\xFFi"],"\x19"=>[76,"\xFFi"],"\x1A"=>[104,"\xFFi"],"\x1B"=>[16,"\xFFi"],"\x1C"=>[94,"\xFFo"],"\x1D"=>[76,"\xFFo"],"\x1E"=>[104,"\xFFo"],"\x1F"=>[16,"\xFFo"],"\x20"=>[94,"\xFFs"],"\x21"=>[76,"\xFFs"],"\x22"=>[104,"\xFFs"],"\x23"=>[16,"\xFFs"],"\x24"=>[94,"\xFFt"],"\x25"=>[76,"\xFFt"],"\x26"=>[104,"\xFFt"],"\x27"=>[16,"\xFFt"],"\x28"=>[77,"\xFF\x20"],"\x29"=>[18,"\xFF\x20"],"\x2A"=>[77,"\xFF\x25"],"\x2B"=>[18,"\xFF\x25"],"\x2C"=>[77,"\xFF-"],"-"=>[18,"\xFF-"],"."=>[77,"\xFF."],"\x2F"=>[18,"\xFF."],[77,"\xFF\x2F"],[18,"\xFF\x2F"],[77,"\xFF3"],[18,"\xFF3"],[77,"\xFF4"],[18,"\xFF4"],[77,"\xFF5"],[18,"\xFF5"],[77,"\xFF6"],[18,"\xFF6"],"\x3A"=>[77,"\xFF7"],"\x3B"=>[18,"\xFF7"],"\x3C"=>[77,"\xFF8"],"\x3D"=>[18,"\xFF8"],"\x3E"=>[77,"\xFF9"],"\x3F"=>[18,"\xFF9"],"\x40"=>[77,"\xFF\x3D"],"A"=>[18,"\xFF\x3D"],"B"=>[77,"\xFFA"],"C"=>[18,"\xFFA"],"D"=>[77,"\xFF_"],"E"=>[18,"\xFF_"],"F"=>[77,"\xFFb"],"G"=>[18,"\xFFb"],"H"=>[77,"\xFFd"],"I"=>[18,"\xFFd"],"J"=>[77,"\xFFf"],"K"=>[18,"\xFFf"],"L"=>[77,"\xFFg"],"M"=>[18,"\xFFg"],"N"=>[77,"\xFFh"],"O"=>[18,"\xFFh"],"P"=>[77,"\xFFl"],"Q"=>[18,"\xFFl"],"R"=>[77,"\xFFm"],"S"=>[18,"\xFFm"],"T"=>[77,"\xFFn"],"U"=>[18,"\xFFn"],"V"=>[77,"\xFFp"],"W"=>[18,"\xFFp"],"X"=>[77,"\xFFr"],"Y"=>[18,"\xFFr"],"Z"=>[77,"\xFFu"],"\x5B"=>[18,"\xFFu"],"\x5C"=>[0,"\xFF\x3A"],"\x5D"=>[0,"\xFFB"],"\x5E"=>[0,"\xFFC"],"_"=>[0,"\xFFD"],"\x60"=>[0,"\xFFE"],"a"=>[0,"\xFFF"],"b"=>[0,"\xFFG"],"c"=>[0,"\xFFH"],"d"=>[0,"\xFFI"],"e"=>[0,"\xFFJ"],"f"=>[0,"\xFFK"],"g"=>[0,"\xFFL"],"h"=>[0,"\xFFM"],"i"=>[0,"\xFFN"],"j"=>[0,"\xFFO"],"k"=>[0,"\xFFP"],"l"=>[0,"\xFFQ"],"m"=>[0,"\xFFR"],"n"=>[0,"\xFFS"],"o"=>[0,"\xFFT"],"p"=>[0,"\xFFU"],"q"=>[0,"\xFFV"],"r"=>[0,"\xFFW"],"s"=>[0,"\xFFY"],"t"=>[0,"\xFFj"],"u"=>[0,"\xFFk"],"v"=>[0,"\xFFq"],"w"=>[0,"\xFFv"],"x"=>[0,"\xFFw"],"y"=>[0,"\xFFx"],"z"=>[0,"\xFFy"],"\x7B"=>[0,"\xFFz"],"\x7C"=>[82,"\xFF"],"\x7D"=>[87,"\xFF"],"~"=>[130,"\xFF"],"\x7F"=>[9,"\xFF"],"\x80"=>[77,"\xCB0"],"\x81"=>[18,"\xCB0"],"\x82"=>[77,"\xCB1"],"\x83"=>[18,"\xCB1"],"\x84"=>[77,"\xCB2"],"\x85"=>[18,"\xCB2"],"\x86"=>[77,"\xCBa"],"\x87"=>[18,"\xCBa"],"\x88"=>[77,"\xCBc"],"\x89"=>[18,"\xCBc"],"\x8A"=>[77,"\xCBe"],"\x8B"=>[18,"\xCBe"],"\x8C"=>[77,"\xCBi"],"\x8D"=>[18,"\xCBi"],"\x8E"=>[77,"\xCBo"],"\x8F"=>[18,"\xCBo"],"\x90"=>[77,"\xCBs"],"\x91"=>[18,"\xCBs"],"\x92"=>[77,"\xCBt"],"\x93"=>[18,"\xCBt"],"\x94"=>[0,"\xCB\x20"],"\x95"=>[0,"\xCB\x25"],"\x96"=>[0,"\xCB-"],"\x97"=>[0,"\xCB."],"\x98"=>[0,"\xCB\x2F"],"\x99"=>[0,"\xCB3"],"\x9A"=>[0,"\xCB4"],"\x9B"=>[0,"\xCB5"],"\x9C"=>[0,"\xCB6"],"\x9D"=>[0,"\xCB7"],"\x9E"=>[0,"\xCB8"],"\x9F"=>[0,"\xCB9"],"\xA0"=>[0,"\xCB\x3D"],"\xA1"=>[0,"\xCBA"],"\xA2"=>[0,"\xCB_"],"\xA3"=>[0,"\xCBb"],"\xA4"=>[0,"\xCBd"],"\xA5"=>[0,"\xCBf"],"\xA6"=>[0,"\xCBg"],"\xA7"=>[0,"\xCBh"],"\xA8"=>[0,"\xCBl"],"\xA9"=>[0,"\xCBm"],"\xAA"=>[0,"\xCBn"],"\xAB"=>[0,"\xCBp"],"\xAC"=>[0,"\xCBr"],"\xAD"=>[0,"\xCBu"],"\xAE"=>[100,"\xCB"],"\xAF"=>[110,"\xCB"],"\xB0"=>[111,"\xCB"],"\xB1"=>[115,"\xCB"],"\xB2"=>[116,"\xCB"],"\xB3"=>[118,"\xCB"],"\xB4"=>[119,"\xCB"],"\xB5"=>[122,"\xCB"],"\xB6"=>[123,"\xCB"],"\xB7"=>[125,"\xCB"],"\xB8"=>[126,"\xCB"],"\xB9"=>[129,"\xCB"],"\xBA"=>[143,"\xCB"],"\xBB"=>[148,"\xCB"],"\xBC"=>[151,"\xCB"],"\xBD"=>[153,"\xCB"],"\xBE"=>[83,"\xCB"],"\xBF"=>[10,"\xCB"],"\xC0"=>[77,"\xCC0"],"\xC1"=>[18,"\xCC0"],"\xC2"=>[77,"\xCC1"],"\xC3"=>[18,"\xCC1"],"\xC4"=>[77,"\xCC2"],"\xC5"=>[18,"\xCC2"],"\xC6"=>[77,"\xCCa"],"\xC7"=>[18,"\xCCa"],"\xC8"=>[77,"\xCCc"],"\xC9"=>[18,"\xCCc"],"\xCA"=>[77,"\xCCe"],"\xCB"=>[18,"\xCCe"],"\xCC"=>[77,"\xCCi"],"\xCD"=>[18,"\xCCi"],"\xCE"=>[77,"\xCCo"],"\xCF"=>[18,"\xCCo"],"\xD0"=>[77,"\xCCs"],"\xD1"=>[18,"\xCCs"],"\xD2"=>[77,"\xCCt"],"\xD3"=>[18,"\xCCt"],"\xD4"=>[0,"\xCC\x20"],"\xD5"=>[0,"\xCC\x25"],"\xD6"=>[0,"\xCC-"],"\xD7"=>[0,"\xCC."],"\xD8"=>[0,"\xCC\x2F"],"\xD9"=>[0,"\xCC3"],"\xDA"=>[0,"\xCC4"],"\xDB"=>[0,"\xCC5"],"\xDC"=>[0,"\xCC6"],"\xDD"=>[0,"\xCC7"],"\xDE"=>[0,"\xCC8"],"\xDF"=>[0,"\xCC9"],"\xE0"=>[0,"\xCC\x3D"],"\xE1"=>[0,"\xCCA"],"\xE2"=>[0,"\xCC_"],"\xE3"=>[0,"\xCCb"],"\xE4"=>[0,"\xCCd"],"\xE5"=>[0,"\xCCf"],"\xE6"=>[0,"\xCCg"],"\xE7"=>[0,"\xCCh"],"\xE8"=>[0,"\xCCl"],"\xE9"=>[0,"\xCCm"],"\xEA"=>[0,"\xCCn"],"\xEB"=>[0,"\xCCp"],"\xEC"=>[0,"\xCCr"],"\xED"=>[0,"\xCCu"],"\xEE"=>[100,"\xCC"],"\xEF"=>[110,"\xCC"],"\xF0"=>[111,"\xCC"],"\xF1"=>[115,"\xCC"],"\xF2"=>[116,"\xCC"],"\xF3"=>[118,"\xCC"],"\xF4"=>[119,"\xCC"],"\xF5"=>[122,"\xCC"],"\xF6"=>[123,"\xCC"],"\xF7"=>[125,"\xCC"],"\xF8"=>[126,"\xCC"],"\xF9"=>[129,"\xCC"],"\xFA"=>[143,"\xCC"],"\xFB"=>[148,"\xCC"],"\xFC"=>[151,"\xCC"],"\xFD"=>[153,"\xCC"],"\xFE"=>[83,"\xCC"],"\xFF"=>[10,"\xCC"],],["\x00"=>[94,"\xD20"],"\x01"=>[76,"\xD20"],"\x02"=>[104,"\xD20"],"\x03"=>[16,"\xD20"],"\x04"=>[94,"\xD21"],"\x05"=>[76,"\xD21"],"\x06"=>[104,"\xD21"],"\x07"=>[16,"\xD21"],"\x08"=>[94,"\xD22"],"\x09"=>[76,"\xD22"],"\x0A"=>[104,"\xD22"],"\x0B"=>[16,"\xD22"],"\x0C"=>[94,"\xD2a"],"\x0D"=>[76,"\xD2a"],"\x0E"=>[104,"\xD2a"],"\x0F"=>[16,"\xD2a"],"\x10"=>[94,"\xD2c"],"\x11"=>[76,"\xD2c"],"\x12"=>[104,"\xD2c"],"\x13"=>[16,"\xD2c"],"\x14"=>[94,"\xD2e"],"\x15"=>[76,"\xD2e"],"\x16"=>[104,"\xD2e"],"\x17"=>[16,"\xD2e"],"\x18"=>[94,"\xD2i"],"\x19"=>[76,"\xD2i"],"\x1A"=>[104,"\xD2i"],"\x1B"=>[16,"\xD2i"],"\x1C"=>[94,"\xD2o"],"\x1D"=>[76,"\xD2o"],"\x1E"=>[104,"\xD2o"],"\x1F"=>[16,"\xD2o"],"\x20"=>[94,"\xD2s"],"\x21"=>[76,"\xD2s"],"\x22"=>[104,"\xD2s"],"\x23"=>[16,"\xD2s"],"\x24"=>[94,"\xD2t"],"\x25"=>[76,"\xD2t"],"\x26"=>[104,"\xD2t"],"\x27"=>[16,"\xD2t"],"\x28"=>[77,"\xD2\x20"],"\x29"=>[18,"\xD2\x20"],"\x2A"=>[77,"\xD2\x25"],"\x2B"=>[18,"\xD2\x25"],"\x2C"=>[77,"\xD2-"],"-"=>[18,"\xD2-"],"."=>[77,"\xD2."],"\x2F"=>[18,"\xD2."],[77,"\xD2\x2F"],[18,"\xD2\x2F"],[77,"\xD23"],[18,"\xD23"],[77,"\xD24"],[18,"\xD24"],[77,"\xD25"],[18,"\xD25"],[77,"\xD26"],[18,"\xD26"],"\x3A"=>[77,"\xD27"],"\x3B"=>[18,"\xD27"],"\x3C"=>[77,"\xD28"],"\x3D"=>[18,"\xD28"],"\x3E"=>[77,"\xD29"],"\x3F"=>[18,"\xD29"],"\x40"=>[77,"\xD2\x3D"],"A"=>[18,"\xD2\x3D"],"B"=>[77,"\xD2A"],"C"=>[18,"\xD2A"],"D"=>[77,"\xD2_"],"E"=>[18,"\xD2_"],"F"=>[77,"\xD2b"],"G"=>[18,"\xD2b"],"H"=>[77,"\xD2d"],"I"=>[18,"\xD2d"],"J"=>[77,"\xD2f"],"K"=>[18,"\xD2f"],"L"=>[77,"\xD2g"],"M"=>[18,"\xD2g"],"N"=>[77,"\xD2h"],"O"=>[18,"\xD2h"],"P"=>[77,"\xD2l"],"Q"=>[18,"\xD2l"],"R"=>[77,"\xD2m"],"S"=>[18,"\xD2m"],"T"=>[77,"\xD2n"],"U"=>[18,"\xD2n"],"V"=>[77,"\xD2p"],"W"=>[18,"\xD2p"],"X"=>[77,"\xD2r"],"Y"=>[18,"\xD2r"],"Z"=>[77,"\xD2u"],"\x5B"=>[18,"\xD2u"],"\x5C"=>[0,"\xD2\x3A"],"\x5D"=>[0,"\xD2B"],"\x5E"=>[0,"\xD2C"],"_"=>[0,"\xD2D"],"\x60"=>[0,"\xD2E"],"a"=>[0,"\xD2F"],"b"=>[0,"\xD2G"],"c"=>[0,"\xD2H"],"d"=>[0,"\xD2I"],"e"=>[0,"\xD2J"],"f"=>[0,"\xD2K"],"g"=>[0,"\xD2L"],"h"=>[0,"\xD2M"],"i"=>[0,"\xD2N"],"j"=>[0,"\xD2O"],"k"=>[0,"\xD2P"],"l"=>[0,"\xD2Q"],"m"=>[0,"\xD2R"],"n"=>[0,"\xD2S"],"o"=>[0,"\xD2T"],"p"=>[0,"\xD2U"],"q"=>[0,"\xD2V"],"r"=>[0,"\xD2W"],"s"=>[0,"\xD2Y"],"t"=>[0,"\xD2j"],"u"=>[0,"\xD2k"],"v"=>[0,"\xD2q"],"w"=>[0,"\xD2v"],"x"=>[0,"\xD2w"],"y"=>[0,"\xD2x"],"z"=>[0,"\xD2y"],"\x7B"=>[0,"\xD2z"],"\x7C"=>[82,"\xD2"],"\x7D"=>[87,"\xD2"],"~"=>[130,"\xD2"],"\x7F"=>[9,"\xD2"],"\x80"=>[94,"\xD50"],"\x81"=>[76,"\xD50"],"\x82"=>[104,"\xD50"],"\x83"=>[16,"\xD50"],"\x84"=>[94,"\xD51"],"\x85"=>[76,"\xD51"],"\x86"=>[104,"\xD51"],"\x87"=>[16,"\xD51"],"\x88"=>[94,"\xD52"],"\x89"=>[76,"\xD52"],"\x8A"=>[104,"\xD52"],"\x8B"=>[16,"\xD52"],"\x8C"=>[94,"\xD5a"],"\x8D"=>[76,"\xD5a"],"\x8E"=>[104,"\xD5a"],"\x8F"=>[16,"\xD5a"],"\x90"=>[94,"\xD5c"],"\x91"=>[76,"\xD5c"],"\x92"=>[104,"\xD5c"],"\x93"=>[16,"\xD5c"],"\x94"=>[94,"\xD5e"],"\x95"=>[76,"\xD5e"],"\x96"=>[104,"\xD5e"],"\x97"=>[16,"\xD5e"],"\x98"=>[94,"\xD5i"],"\x99"=>[76,"\xD5i"],"\x9A"=>[104,"\xD5i"],"\x9B"=>[16,"\xD5i"],"\x9C"=>[94,"\xD5o"],"\x9D"=>[76,"\xD5o"],"\x9E"=>[104,"\xD5o"],"\x9F"=>[16,"\xD5o"],"\xA0"=>[94,"\xD5s"],"\xA1"=>[76,"\xD5s"],"\xA2"=>[104,"\xD5s"],"\xA3"=>[16,"\xD5s"],"\xA4"=>[94,"\xD5t"],"\xA5"=>[76,"\xD5t"],"\xA6"=>[104,"\xD5t"],"\xA7"=>[16,"\xD5t"],"\xA8"=>[77,"\xD5\x20"],"\xA9"=>[18,"\xD5\x20"],"\xAA"=>[77,"\xD5\x25"],"\xAB"=>[18,"\xD5\x25"],"\xAC"=>[77,"\xD5-"],"\xAD"=>[18,"\xD5-"],"\xAE"=>[77,"\xD5."],"\xAF"=>[18,"\xD5."],"\xB0"=>[77,"\xD5\x2F"],"\xB1"=>[18,"\xD5\x2F"],"\xB2"=>[77,"\xD53"],"\xB3"=>[18,"\xD53"],"\xB4"=>[77,"\xD54"],"\xB5"=>[18,"\xD54"],"\xB6"=>[77,"\xD55"],"\xB7"=>[18,"\xD55"],"\xB8"=>[77,"\xD56"],"\xB9"=>[18,"\xD56"],"\xBA"=>[77,"\xD57"],"\xBB"=>[18,"\xD57"],"\xBC"=>[77,"\xD58"],"\xBD"=>[18,"\xD58"],"\xBE"=>[77,"\xD59"],"\xBF"=>[18,"\xD59"],"\xC0"=>[77,"\xD5\x3D"],"\xC1"=>[18,"\xD5\x3D"],"\xC2"=>[77,"\xD5A"],"\xC3"=>[18,"\xD5A"],"\xC4"=>[77,"\xD5_"],"\xC5"=>[18,"\xD5_"],"\xC6"=>[77,"\xD5b"],"\xC7"=>[18,"\xD5b"],"\xC8"=>[77,"\xD5d"],"\xC9"=>[18,"\xD5d"],"\xCA"=>[77,"\xD5f"],"\xCB"=>[18,"\xD5f"],"\xCC"=>[77,"\xD5g"],"\xCD"=>[18,"\xD5g"],"\xCE"=>[77,"\xD5h"],"\xCF"=>[18,"\xD5h"],"\xD0"=>[77,"\xD5l"],"\xD1"=>[18,"\xD5l"],"\xD2"=>[77,"\xD5m"],"\xD3"=>[18,"\xD5m"],"\xD4"=>[77,"\xD5n"],"\xD5"=>[18,"\xD5n"],"\xD6"=>[77,"\xD5p"],"\xD7"=>[18,"\xD5p"],"\xD8"=>[77,"\xD5r"],"\xD9"=>[18,"\xD5r"],"\xDA"=>[77,"\xD5u"],"\xDB"=>[18,"\xD5u"],"\xDC"=>[0,"\xD5\x3A"],"\xDD"=>[0,"\xD5B"],"\xDE"=>[0,"\xD5C"],"\xDF"=>[0,"\xD5D"],"\xE0"=>[0,"\xD5E"],"\xE1"=>[0,"\xD5F"],"\xE2"=>[0,"\xD5G"],"\xE3"=>[0,"\xD5H"],"\xE4"=>[0,"\xD5I"],"\xE5"=>[0,"\xD5J"],"\xE6"=>[0,"\xD5K"],"\xE7"=>[0,"\xD5L"],"\xE8"=>[0,"\xD5M"],"\xE9"=>[0,"\xD5N"],"\xEA"=>[0,"\xD5O"],"\xEB"=>[0,"\xD5P"],"\xEC"=>[0,"\xD5Q"],"\xED"=>[0,"\xD5R"],"\xEE"=>[0,"\xD5S"],"\xEF"=>[0,"\xD5T"],"\xF0"=>[0,"\xD5U"],"\xF1"=>[0,"\xD5V"],"\xF2"=>[0,"\xD5W"],"\xF3"=>[0,"\xD5Y"],"\xF4"=>[0,"\xD5j"],"\xF5"=>[0,"\xD5k"],"\xF6"=>[0,"\xD5q"],"\xF7"=>[0,"\xD5v"],"\xF8"=>[0,"\xD5w"],"\xF9"=>[0,"\xD5x"],"\xFA"=>[0,"\xD5y"],"\xFB"=>[0,"\xD5z"],"\xFC"=>[82,"\xD5"],"\xFD"=>[87,"\xD5"],"\xFE"=>[130,"\xD5"],"\xFF"=>[9,"\xD5"],],["\x00"=>[0,"\xD30"],"\x01"=>[0,"\xD31"],"\x02"=>[0,"\xD32"],"\x03"=>[0,"\xD3a"],"\x04"=>[0,"\xD3c"],"\x05"=>[0,"\xD3e"],"\x06"=>[0,"\xD3i"],"\x07"=>[0,"\xD3o"],"\x08"=>[0,"\xD3s"],"\x09"=>[0,"\xD3t"],"\x0A"=>[73,"\xD3"],"\x0B"=>[88,"\xD3"],"\x0C"=>[89,"\xD3"],"\x0D"=>[96,"\xD3"],"\x0E"=>[97,"\xD3"],"\x0F"=>[99,"\xD3"],"\x10"=>[106,"\xD3"],"\x11"=>[136,"\xD3"],"\x12"=>[139,"\xD3"],"\x13"=>[141,"\xD3"],"\x14"=>[145,"\xD3"],"\x15"=>[147,"\xD3"],"\x16"=>[149,"\xD3"],"\x17"=>[101,"\xD3"],"\x18"=>[112,"\xD3"],"\x19"=>[117,"\xD3"],"\x1A"=>[120,"\xD3"],"\x1B"=>[124,"\xD3"],"\x1C"=>[127,"\xD3"],"\x1D"=>[144,"\xD3"],"\x1E"=>[152,"\xD3"],"\x1F"=>[11,"\xD3"],"\x20"=>[0,"\xD40"],"\x21"=>[0,"\xD41"],"\x22"=>[0,"\xD42"],"\x23"=>[0,"\xD4a"],"\x24"=>[0,"\xD4c"],"\x25"=>[0,"\xD4e"],"\x26"=>[0,"\xD4i"],"\x27"=>[0,"\xD4o"],"\x28"=>[0,"\xD4s"],"\x29"=>[0,"\xD4t"],"\x2A"=>[73,"\xD4"],"\x2B"=>[88,"\xD4"],"\x2C"=>[89,"\xD4"],"-"=>[96,"\xD4"],"."=>[97,"\xD4"],"\x2F"=>[99,"\xD4"],[106,"\xD4"],[136,"\xD4"],[139,"\xD4"],[141,"\xD4"],[145,"\xD4"],[147,"\xD4"],[149,"\xD4"],[101,"\xD4"],[112,"\xD4"],[117,"\xD4"],"\x3A"=>[120,"\xD4"],"\x3B"=>[124,"\xD4"],"\x3C"=>[127,"\xD4"],"\x3D"=>[144,"\xD4"],"\x3E"=>[152,"\xD4"],"\x3F"=>[11,"\xD4"],"\x40"=>[0,"\xD60"],"A"=>[0,"\xD61"],"B"=>[0,"\xD62"],"C"=>[0,"\xD6a"],"D"=>[0,"\xD6c"],"E"=>[0,"\xD6e"],"F"=>[0,"\xD6i"],"G"=>[0,"\xD6o"],"H"=>[0,"\xD6s"],"I"=>[0,"\xD6t"],"J"=>[73,"\xD6"],"K"=>[88,"\xD6"],"L"=>[89,"\xD6"],"M"=>[96,"\xD6"],"N"=>[97,"\xD6"],"O"=>[99,"\xD6"],"P"=>[106,"\xD6"],"Q"=>[136,"\xD6"],"R"=>[139,"\xD6"],"S"=>[141,"\xD6"],"T"=>[145,"\xD6"],"U"=>[147,"\xD6"],"V"=>[149,"\xD6"],"W"=>[101,"\xD6"],"X"=>[112,"\xD6"],"Y"=>[117,"\xD6"],"Z"=>[120,"\xD6"],"\x5B"=>[124,"\xD6"],"\x5C"=>[127,"\xD6"],"\x5D"=>[144,"\xD6"],"\x5E"=>[152,"\xD6"],"_"=>[11,"\xD6"],"\x60"=>[0,"\xDD0"],"a"=>[0,"\xDD1"],"b"=>[0,"\xDD2"],"c"=>[0,"\xDDa"],"d"=>[0,"\xDDc"],"e"=>[0,"\xDDe"],"f"=>[0,"\xDDi"],"g"=>[0,"\xDDo"],"h"=>[0,"\xDDs"],"i"=>[0,"\xDDt"],"j"=>[73,"\xDD"],"k"=>[88,"\xDD"],"l"=>[89,"\xDD"],"m"=>[96,"\xDD"],"n"=>[97,"\xDD"],"o"=>[99,"\xDD"],"p"=>[106,"\xDD"],"q"=>[136,"\xDD"],"r"=>[139,"\xDD"],"s"=>[141,"\xDD"],"t"=>[145,"\xDD"],"u"=>[147,"\xDD"],"v"=>[149,"\xDD"],"w"=>[101,"\xDD"],"x"=>[112,"\xDD"],"y"=>[117,"\xDD"],"z"=>[120,"\xDD"],"\x7B"=>[124,"\xDD"],"\x7C"=>[127,"\xDD"],"\x7D"=>[144,"\xDD"],"~"=>[152,"\xDD"],"\x7F"=>[11,"\xDD"],"\x80"=>[0,"\xDE0"],"\x81"=>[0,"\xDE1"],"\x82"=>[0,"\xDE2"],"\x83"=>[0,"\xDEa"],"\x84"=>[0,"\xDEc"],"\x85"=>[0,"\xDEe"],"\x86"=>[0,"\xDEi"],"\x87"=>[0,"\xDEo"],"\x88"=>[0,"\xDEs"],"\x89"=>[0,"\xDEt"],"\x8A"=>[73,"\xDE"],"\x8B"=>[88,"\xDE"],"\x8C"=>[89,"\xDE"],"\x8D"=>[96,"\xDE"],"\x8E"=>[97,"\xDE"],"\x8F"=>[99,"\xDE"],"\x90"=>[106,"\xDE"],"\x91"=>[136,"\xDE"],"\x92"=>[139,"\xDE"],"\x93"=>[141,"\xDE"],"\x94"=>[145,"\xDE"],"\x95"=>[147,"\xDE"],"\x96"=>[149,"\xDE"],"\x97"=>[101,"\xDE"],"\x98"=>[112,"\xDE"],"\x99"=>[117,"\xDE"],"\x9A"=>[120,"\xDE"],"\x9B"=>[124,"\xDE"],"\x9C"=>[127,"\xDE"],"\x9D"=>[144,"\xDE"],"\x9E"=>[152,"\xDE"],"\x9F"=>[11,"\xDE"],"\xA0"=>[0,"\xDF0"],"\xA1"=>[0,"\xDF1"],"\xA2"=>[0,"\xDF2"],"\xA3"=>[0,"\xDFa"],"\xA4"=>[0,"\xDFc"],"\xA5"=>[0,"\xDFe"],"\xA6"=>[0,"\xDFi"],"\xA7"=>[0,"\xDFo"],"\xA8"=>[0,"\xDFs"],"\xA9"=>[0,"\xDFt"],"\xAA"=>[73,"\xDF"],"\xAB"=>[88,"\xDF"],"\xAC"=>[89,"\xDF"],"\xAD"=>[96,"\xDF"],"\xAE"=>[97,"\xDF"],"\xAF"=>[99,"\xDF"],"\xB0"=>[106,"\xDF"],"\xB1"=>[136,"\xDF"],"\xB2"=>[139,"\xDF"],"\xB3"=>[141,"\xDF"],"\xB4"=>[145,"\xDF"],"\xB5"=>[147,"\xDF"],"\xB6"=>[149,"\xDF"],"\xB7"=>[101,"\xDF"],"\xB8"=>[112,"\xDF"],"\xB9"=>[117,"\xDF"],"\xBA"=>[120,"\xDF"],"\xBB"=>[124,"\xDF"],"\xBC"=>[127,"\xDF"],"\xBD"=>[144,"\xDF"],"\xBE"=>[152,"\xDF"],"\xBF"=>[11,"\xDF"],"\xC0"=>[0,"\xF10"],"\xC1"=>[0,"\xF11"],"\xC2"=>[0,"\xF12"],"\xC3"=>[0,"\xF1a"],"\xC4"=>[0,"\xF1c"],"\xC5"=>[0,"\xF1e"],"\xC6"=>[0,"\xF1i"],"\xC7"=>[0,"\xF1o"],"\xC8"=>[0,"\xF1s"],"\xC9"=>[0,"\xF1t"],"\xCA"=>[73,"\xF1"],"\xCB"=>[88,"\xF1"],"\xCC"=>[89,"\xF1"],"\xCD"=>[96,"\xF1"],"\xCE"=>[97,"\xF1"],"\xCF"=>[99,"\xF1"],"\xD0"=>[106,"\xF1"],"\xD1"=>[136,"\xF1"],"\xD2"=>[139,"\xF1"],"\xD3"=>[141,"\xF1"],"\xD4"=>[145,"\xF1"],"\xD5"=>[147,"\xF1"],"\xD6"=>[149,"\xF1"],"\xD7"=>[101,"\xF1"],"\xD8"=>[112,"\xF1"],"\xD9"=>[117,"\xF1"],"\xDA"=>[120,"\xF1"],"\xDB"=>[124,"\xF1"],"\xDC"=>[127,"\xF1"],"\xDD"=>[144,"\xF1"],"\xDE"=>[152,"\xF1"],"\xDF"=>[11,"\xF1"],"\xE0"=>[0,"\xF40"],"\xE1"=>[0,"\xF41"],"\xE2"=>[0,"\xF42"],"\xE3"=>[0,"\xF4a"],"\xE4"=>[0,"\xF4c"],"\xE5"=>[0,"\xF4e"],"\xE6"=>[0,"\xF4i"],"\xE7"=>[0,"\xF4o"],"\xE8"=>[0,"\xF4s"],"\xE9"=>[0,"\xF4t"],"\xEA"=>[73,"\xF4"],"\xEB"=>[88,"\xF4"],"\xEC"=>[89,"\xF4"],"\xED"=>[96,"\xF4"],"\xEE"=>[97,"\xF4"],"\xEF"=>[99,"\xF4"],"\xF0"=>[106,"\xF4"],"\xF1"=>[136,"\xF4"],"\xF2"=>[139,"\xF4"],"\xF3"=>[141,"\xF4"],"\xF4"=>[145,"\xF4"],"\xF5"=>[147,"\xF4"],"\xF6"=>[149,"\xF4"],"\xF7"=>[101,"\xF4"],"\xF8"=>[112,"\xF4"],"\xF9"=>[117,"\xF4"],"\xFA"=>[120,"\xF4"],"\xFB"=>[124,"\xF4"],"\xFC"=>[127,"\xF4"],"\xFD"=>[144,"\xF4"],"\xFE"=>[152,"\xF4"],"\xFF"=>[11,"\xF4"],],["\x00"=>[92,"\xD3"],"\x01"=>[95,"\xD3"],"\x02"=>[137,"\xD3"],"\x03"=>[142,"\xD3"],"\x04"=>[150,"\xD3"],"\x05"=>[74,"\xD3"],"\x06"=>[90,"\xD3"],"\x07"=>[98,"\xD3"],"\x08"=>[107,"\xD3"],"\x09"=>[140,"\xD3"],"\x0A"=>[146,"\xD3"],"\x0B"=>[102,"\xD3"],"\x0C"=>[113,"\xD3"],"\x0D"=>[121,"\xD3"],"\x0E"=>[128,"\xD3"],"\x0F"=>[12,"\xD3"],"\x10"=>[92,"\xD4"],"\x11"=>[95,"\xD4"],"\x12"=>[137,"\xD4"],"\x13"=>[142,"\xD4"],"\x14"=>[150,"\xD4"],"\x15"=>[74,"\xD4"],"\x16"=>[90,"\xD4"],"\x17"=>[98,"\xD4"],"\x18"=>[107,"\xD4"],"\x19"=>[140,"\xD4"],"\x1A"=>[146,"\xD4"],"\x1B"=>[102,"\xD4"],"\x1C"=>[113,"\xD4"],"\x1D"=>[121,"\xD4"],"\x1E"=>[128,"\xD4"],"\x1F"=>[12,"\xD4"],"\x20"=>[92,"\xD6"],"\x21"=>[95,"\xD6"],"\x22"=>[137,"\xD6"],"\x23"=>[142,"\xD6"],"\x24"=>[150,"\xD6"],"\x25"=>[74,"\xD6"],"\x26"=>[90,"\xD6"],"\x27"=>[98,"\xD6"],"\x28"=>[107,"\xD6"],"\x29"=>[140,"\xD6"],"\x2A"=>[146,"\xD6"],"\x2B"=>[102,"\xD6"],"\x2C"=>[113,"\xD6"],"-"=>[121,"\xD6"],"."=>[128,"\xD6"],"\x2F"=>[12,"\xD6"],[92,"\xDD"],[95,"\xDD"],[137,"\xDD"],[142,"\xDD"],[150,"\xDD"],[74,"\xDD"],[90,"\xDD"],[98,"\xDD"],[107,"\xDD"],[140,"\xDD"],"\x3A"=>[146,"\xDD"],"\x3B"=>[102,"\xDD"],"\x3C"=>[113,"\xDD"],"\x3D"=>[121,"\xDD"],"\x3E"=>[128,"\xDD"],"\x3F"=>[12,"\xDD"],"\x40"=>[92,"\xDE"],"A"=>[95,"\xDE"],"B"=>[137,"\xDE"],"C"=>[142,"\xDE"],"D"=>[150,"\xDE"],"E"=>[74,"\xDE"],"F"=>[90,"\xDE"],"G"=>[98,"\xDE"],"H"=>[107,"\xDE"],"I"=>[140,"\xDE"],"J"=>[146,"\xDE"],"K"=>[102,"\xDE"],"L"=>[113,"\xDE"],"M"=>[121,"\xDE"],"N"=>[128,"\xDE"],"O"=>[12,"\xDE"],"P"=>[92,"\xDF"],"Q"=>[95,"\xDF"],"R"=>[137,"\xDF"],"S"=>[142,"\xDF"],"T"=>[150,"\xDF"],"U"=>[74,"\xDF"],"V"=>[90,"\xDF"],"W"=>[98,"\xDF"],"X"=>[107,"\xDF"],"Y"=>[140,"\xDF"],"Z"=>[146,"\xDF"],"\x5B"=>[102,"\xDF"],"\x5C"=>[113,"\xDF"],"\x5D"=>[121,"\xDF"],"\x5E"=>[128,"\xDF"],"_"=>[12,"\xDF"],"\x60"=>[92,"\xF1"],"a"=>[95,"\xF1"],"b"=>[137,"\xF1"],"c"=>[142,"\xF1"],"d"=>[150,"\xF1"],"e"=>[74,"\xF1"],"f"=>[90,"\xF1"],"g"=>[98,"\xF1"],"h"=>[107,"\xF1"],"i"=>[140,"\xF1"],"j"=>[146,"\xF1"],"k"=>[102,"\xF1"],"l"=>[113,"\xF1"],"m"=>[121,"\xF1"],"n"=>[128,"\xF1"],"o"=>[12,"\xF1"],"p"=>[92,"\xF4"],"q"=>[95,"\xF4"],"r"=>[137,"\xF4"],"s"=>[142,"\xF4"],"t"=>[150,"\xF4"],"u"=>[74,"\xF4"],"v"=>[90,"\xF4"],"w"=>[98,"\xF4"],"x"=>[107,"\xF4"],"y"=>[140,"\xF4"],"z"=>[146,"\xF4"],"\x7B"=>[102,"\xF4"],"\x7C"=>[113,"\xF4"],"\x7D"=>[121,"\xF4"],"~"=>[128,"\xF4"],"\x7F"=>[12,"\xF4"],"\x80"=>[92,"\xF5"],"\x81"=>[95,"\xF5"],"\x82"=>[137,"\xF5"],"\x83"=>[142,"\xF5"],"\x84"=>[150,"\xF5"],"\x85"=>[74,"\xF5"],"\x86"=>[90,"\xF5"],"\x87"=>[98,"\xF5"],"\x88"=>[107,"\xF5"],"\x89"=>[140,"\xF5"],"\x8A"=>[146,"\xF5"],"\x8B"=>[102,"\xF5"],"\x8C"=>[113,"\xF5"],"\x8D"=>[121,"\xF5"],"\x8E"=>[128,"\xF5"],"\x8F"=>[12,"\xF5"],"\x90"=>[92,"\xF6"],"\x91"=>[95,"\xF6"],"\x92"=>[137,"\xF6"],"\x93"=>[142,"\xF6"],"\x94"=>[150,"\xF6"],"\x95"=>[74,"\xF6"],"\x96"=>[90,"\xF6"],"\x97"=>[98,"\xF6"],"\x98"=>[107,"\xF6"],"\x99"=>[140,"\xF6"],"\x9A"=>[146,"\xF6"],"\x9B"=>[102,"\xF6"],"\x9C"=>[113,"\xF6"],"\x9D"=>[121,"\xF6"],"\x9E"=>[128,"\xF6"],"\x9F"=>[12,"\xF6"],"\xA0"=>[92,"\xF7"],"\xA1"=>[95,"\xF7"],"\xA2"=>[137,"\xF7"],"\xA3"=>[142,"\xF7"],"\xA4"=>[150,"\xF7"],"\xA5"=>[74,"\xF7"],"\xA6"=>[90,"\xF7"],"\xA7"=>[98,"\xF7"],"\xA8"=>[107,"\xF7"],"\xA9"=>[140,"\xF7"],"\xAA"=>[146,"\xF7"],"\xAB"=>[102,"\xF7"],"\xAC"=>[113,"\xF7"],"\xAD"=>[121,"\xF7"],"\xAE"=>[128,"\xF7"],"\xAF"=>[12,"\xF7"],"\xB0"=>[92,"\xF8"],"\xB1"=>[95,"\xF8"],"\xB2"=>[137,"\xF8"],"\xB3"=>[142,"\xF8"],"\xB4"=>[150,"\xF8"],"\xB5"=>[74,"\xF8"],"\xB6"=>[90,"\xF8"],"\xB7"=>[98,"\xF8"],"\xB8"=>[107,"\xF8"],"\xB9"=>[140,"\xF8"],"\xBA"=>[146,"\xF8"],"\xBB"=>[102,"\xF8"],"\xBC"=>[113,"\xF8"],"\xBD"=>[121,"\xF8"],"\xBE"=>[128,"\xF8"],"\xBF"=>[12,"\xF8"],"\xC0"=>[92,"\xFA"],"\xC1"=>[95,"\xFA"],"\xC2"=>[137,"\xFA"],"\xC3"=>[142,"\xFA"],"\xC4"=>[150,"\xFA"],"\xC5"=>[74,"\xFA"],"\xC6"=>[90,"\xFA"],"\xC7"=>[98,"\xFA"],"\xC8"=>[107,"\xFA"],"\xC9"=>[140,"\xFA"],"\xCA"=>[146,"\xFA"],"\xCB"=>[102,"\xFA"],"\xCC"=>[113,"\xFA"],"\xCD"=>[121,"\xFA"],"\xCE"=>[128,"\xFA"],"\xCF"=>[12,"\xFA"],"\xD0"=>[92,"\xFB"],"\xD1"=>[95,"\xFB"],"\xD2"=>[137,"\xFB"],"\xD3"=>[142,"\xFB"],"\xD4"=>[150,"\xFB"],"\xD5"=>[74,"\xFB"],"\xD6"=>[90,"\xFB"],"\xD7"=>[98,"\xFB"],"\xD8"=>[107,"\xFB"],"\xD9"=>[140,"\xFB"],"\xDA"=>[146,"\xFB"],"\xDB"=>[102,"\xFB"],"\xDC"=>[113,"\xFB"],"\xDD"=>[121,"\xFB"],"\xDE"=>[128,"\xFB"],"\xDF"=>[12,"\xFB"],"\xE0"=>[92,"\xFC"],"\xE1"=>[95,"\xFC"],"\xE2"=>[137,"\xFC"],"\xE3"=>[142,"\xFC"],"\xE4"=>[150,"\xFC"],"\xE5"=>[74,"\xFC"],"\xE6"=>[90,"\xFC"],"\xE7"=>[98,"\xFC"],"\xE8"=>[107,"\xFC"],"\xE9"=>[140,"\xFC"],"\xEA"=>[146,"\xFC"],"\xEB"=>[102,"\xFC"],"\xEC"=>[113,"\xFC"],"\xED"=>[121,"\xFC"],"\xEE"=>[128,"\xFC"],"\xEF"=>[12,"\xFC"],"\xF0"=>[92,"\xFD"],"\xF1"=>[95,"\xFD"],"\xF2"=>[137,"\xFD"],"\xF3"=>[142,"\xFD"],"\xF4"=>[150,"\xFD"],"\xF5"=>[74,"\xFD"],"\xF6"=>[90,"\xFD"],"\xF7"=>[98,"\xFD"],"\xF8"=>[107,"\xFD"],"\xF9"=>[140,"\xFD"],"\xFA"=>[146,"\xFD"],"\xFB"=>[102,"\xFD"],"\xFC"=>[113,"\xFD"],"\xFD"=>[121,"\xFD"],"\xFE"=>[128,"\xFD"],"\xFF"=>[12,"\xFD"],],["\x00"=>[94,"\xD30"],"\x01"=>[76,"\xD30"],"\x02"=>[104,"\xD30"],"\x03"=>[16,"\xD30"],"\x04"=>[94,"\xD31"],"\x05"=>[76,"\xD31"],"\x06"=>[104,"\xD31"],"\x07"=>[16,"\xD31"],"\x08"=>[94,"\xD32"],"\x09"=>[76,"\xD32"],"\x0A"=>[104,"\xD32"],"\x0B"=>[16,"\xD32"],"\x0C"=>[94,"\xD3a"],"\x0D"=>[76,"\xD3a"],"\x0E"=>[104,"\xD3a"],"\x0F"=>[16,"\xD3a"],"\x10"=>[94,"\xD3c"],"\x11"=>[76,"\xD3c"],"\x12"=>[104,"\xD3c"],"\x13"=>[16,"\xD3c"],"\x14"=>[94,"\xD3e"],"\x15"=>[76,"\xD3e"],"\x16"=>[104,"\xD3e"],"\x17"=>[16,"\xD3e"],"\x18"=>[94,"\xD3i"],"\x19"=>[76,"\xD3i"],"\x1A"=>[104,"\xD3i"],"\x1B"=>[16,"\xD3i"],"\x1C"=>[94,"\xD3o"],"\x1D"=>[76,"\xD3o"],"\x1E"=>[104,"\xD3o"],"\x1F"=>[16,"\xD3o"],"\x20"=>[94,"\xD3s"],"\x21"=>[76,"\xD3s"],"\x22"=>[104,"\xD3s"],"\x23"=>[16,"\xD3s"],"\x24"=>[94,"\xD3t"],"\x25"=>[76,"\xD3t"],"\x26"=>[104,"\xD3t"],"\x27"=>[16,"\xD3t"],"\x28"=>[77,"\xD3\x20"],"\x29"=>[18,"\xD3\x20"],"\x2A"=>[77,"\xD3\x25"],"\x2B"=>[18,"\xD3\x25"],"\x2C"=>[77,"\xD3-"],"-"=>[18,"\xD3-"],"."=>[77,"\xD3."],"\x2F"=>[18,"\xD3."],[77,"\xD3\x2F"],[18,"\xD3\x2F"],[77,"\xD33"],[18,"\xD33"],[77,"\xD34"],[18,"\xD34"],[77,"\xD35"],[18,"\xD35"],[77,"\xD36"],[18,"\xD36"],"\x3A"=>[77,"\xD37"],"\x3B"=>[18,"\xD37"],"\x3C"=>[77,"\xD38"],"\x3D"=>[18,"\xD38"],"\x3E"=>[77,"\xD39"],"\x3F"=>[18,"\xD39"],"\x40"=>[77,"\xD3\x3D"],"A"=>[18,"\xD3\x3D"],"B"=>[77,"\xD3A"],"C"=>[18,"\xD3A"],"D"=>[77,"\xD3_"],"E"=>[18,"\xD3_"],"F"=>[77,"\xD3b"],"G"=>[18,"\xD3b"],"H"=>[77,"\xD3d"],"I"=>[18,"\xD3d"],"J"=>[77,"\xD3f"],"K"=>[18,"\xD3f"],"L"=>[77,"\xD3g"],"M"=>[18,"\xD3g"],"N"=>[77,"\xD3h"],"O"=>[18,"\xD3h"],"P"=>[77,"\xD3l"],"Q"=>[18,"\xD3l"],"R"=>[77,"\xD3m"],"S"=>[18,"\xD3m"],"T"=>[77,"\xD3n"],"U"=>[18,"\xD3n"],"V"=>[77,"\xD3p"],"W"=>[18,"\xD3p"],"X"=>[77,"\xD3r"],"Y"=>[18,"\xD3r"],"Z"=>[77,"\xD3u"],"\x5B"=>[18,"\xD3u"],"\x5C"=>[0,"\xD3\x3A"],"\x5D"=>[0,"\xD3B"],"\x5E"=>[0,"\xD3C"],"_"=>[0,"\xD3D"],"\x60"=>[0,"\xD3E"],"a"=>[0,"\xD3F"],"b"=>[0,"\xD3G"],"c"=>[0,"\xD3H"],"d"=>[0,"\xD3I"],"e"=>[0,"\xD3J"],"f"=>[0,"\xD3K"],"g"=>[0,"\xD3L"],"h"=>[0,"\xD3M"],"i"=>[0,"\xD3N"],"j"=>[0,"\xD3O"],"k"=>[0,"\xD3P"],"l"=>[0,"\xD3Q"],"m"=>[0,"\xD3R"],"n"=>[0,"\xD3S"],"o"=>[0,"\xD3T"],"p"=>[0,"\xD3U"],"q"=>[0,"\xD3V"],"r"=>[0,"\xD3W"],"s"=>[0,"\xD3Y"],"t"=>[0,"\xD3j"],"u"=>[0,"\xD3k"],"v"=>[0,"\xD3q"],"w"=>[0,"\xD3v"],"x"=>[0,"\xD3w"],"y"=>[0,"\xD3x"],"z"=>[0,"\xD3y"],"\x7B"=>[0,"\xD3z"],"\x7C"=>[82,"\xD3"],"\x7D"=>[87,"\xD3"],"~"=>[130,"\xD3"],"\x7F"=>[9,"\xD3"],"\x80"=>[94,"\xD40"],"\x81"=>[76,"\xD40"],"\x82"=>[104,"\xD40"],"\x83"=>[16,"\xD40"],"\x84"=>[94,"\xD41"],"\x85"=>[76,"\xD41"],"\x86"=>[104,"\xD41"],"\x87"=>[16,"\xD41"],"\x88"=>[94,"\xD42"],"\x89"=>[76,"\xD42"],"\x8A"=>[104,"\xD42"],"\x8B"=>[16,"\xD42"],"\x8C"=>[94,"\xD4a"],"\x8D"=>[76,"\xD4a"],"\x8E"=>[104,"\xD4a"],"\x8F"=>[16,"\xD4a"],"\x90"=>[94,"\xD4c"],"\x91"=>[76,"\xD4c"],"\x92"=>[104,"\xD4c"],"\x93"=>[16,"\xD4c"],"\x94"=>[94,"\xD4e"],"\x95"=>[76,"\xD4e"],"\x96"=>[104,"\xD4e"],"\x97"=>[16,"\xD4e"],"\x98"=>[94,"\xD4i"],"\x99"=>[76,"\xD4i"],"\x9A"=>[104,"\xD4i"],"\x9B"=>[16,"\xD4i"],"\x9C"=>[94,"\xD4o"],"\x9D"=>[76,"\xD4o"],"\x9E"=>[104,"\xD4o"],"\x9F"=>[16,"\xD4o"],"\xA0"=>[94,"\xD4s"],"\xA1"=>[76,"\xD4s"],"\xA2"=>[104,"\xD4s"],"\xA3"=>[16,"\xD4s"],"\xA4"=>[94,"\xD4t"],"\xA5"=>[76,"\xD4t"],"\xA6"=>[104,"\xD4t"],"\xA7"=>[16,"\xD4t"],"\xA8"=>[77,"\xD4\x20"],"\xA9"=>[18,"\xD4\x20"],"\xAA"=>[77,"\xD4\x25"],"\xAB"=>[18,"\xD4\x25"],"\xAC"=>[77,"\xD4-"],"\xAD"=>[18,"\xD4-"],"\xAE"=>[77,"\xD4."],"\xAF"=>[18,"\xD4."],"\xB0"=>[77,"\xD4\x2F"],"\xB1"=>[18,"\xD4\x2F"],"\xB2"=>[77,"\xD43"],"\xB3"=>[18,"\xD43"],"\xB4"=>[77,"\xD44"],"\xB5"=>[18,"\xD44"],"\xB6"=>[77,"\xD45"],"\xB7"=>[18,"\xD45"],"\xB8"=>[77,"\xD46"],"\xB9"=>[18,"\xD46"],"\xBA"=>[77,"\xD47"],"\xBB"=>[18,"\xD47"],"\xBC"=>[77,"\xD48"],"\xBD"=>[18,"\xD48"],"\xBE"=>[77,"\xD49"],"\xBF"=>[18,"\xD49"],"\xC0"=>[77,"\xD4\x3D"],"\xC1"=>[18,"\xD4\x3D"],"\xC2"=>[77,"\xD4A"],"\xC3"=>[18,"\xD4A"],"\xC4"=>[77,"\xD4_"],"\xC5"=>[18,"\xD4_"],"\xC6"=>[77,"\xD4b"],"\xC7"=>[18,"\xD4b"],"\xC8"=>[77,"\xD4d"],"\xC9"=>[18,"\xD4d"],"\xCA"=>[77,"\xD4f"],"\xCB"=>[18,"\xD4f"],"\xCC"=>[77,"\xD4g"],"\xCD"=>[18,"\xD4g"],"\xCE"=>[77,"\xD4h"],"\xCF"=>[18,"\xD4h"],"\xD0"=>[77,"\xD4l"],"\xD1"=>[18,"\xD4l"],"\xD2"=>[77,"\xD4m"],"\xD3"=>[18,"\xD4m"],"\xD4"=>[77,"\xD4n"],"\xD5"=>[18,"\xD4n"],"\xD6"=>[77,"\xD4p"],"\xD7"=>[18,"\xD4p"],"\xD8"=>[77,"\xD4r"],"\xD9"=>[18,"\xD4r"],"\xDA"=>[77,"\xD4u"],"\xDB"=>[18,"\xD4u"],"\xDC"=>[0,"\xD4\x3A"],"\xDD"=>[0,"\xD4B"],"\xDE"=>[0,"\xD4C"],"\xDF"=>[0,"\xD4D"],"\xE0"=>[0,"\xD4E"],"\xE1"=>[0,"\xD4F"],"\xE2"=>[0,"\xD4G"],"\xE3"=>[0,"\xD4H"],"\xE4"=>[0,"\xD4I"],"\xE5"=>[0,"\xD4J"],"\xE6"=>[0,"\xD4K"],"\xE7"=>[0,"\xD4L"],"\xE8"=>[0,"\xD4M"],"\xE9"=>[0,"\xD4N"],"\xEA"=>[0,"\xD4O"],"\xEB"=>[0,"\xD4P"],"\xEC"=>[0,"\xD4Q"],"\xED"=>[0,"\xD4R"],"\xEE"=>[0,"\xD4S"],"\xEF"=>[0,"\xD4T"],"\xF0"=>[0,"\xD4U"],"\xF1"=>[0,"\xD4V"],"\xF2"=>[0,"\xD4W"],"\xF3"=>[0,"\xD4Y"],"\xF4"=>[0,"\xD4j"],"\xF5"=>[0,"\xD4k"],"\xF6"=>[0,"\xD4q"],"\xF7"=>[0,"\xD4v"],"\xF8"=>[0,"\xD4w"],"\xF9"=>[0,"\xD4x"],"\xFA"=>[0,"\xD4y"],"\xFB"=>[0,"\xD4z"],"\xFC"=>[82,"\xD4"],"\xFD"=>[87,"\xD4"],"\xFE"=>[130,"\xD4"],"\xFF"=>[9,"\xD4"],],["\x00"=>[77,"\xD30"],"\x01"=>[18,"\xD30"],"\x02"=>[77,"\xD31"],"\x03"=>[18,"\xD31"],"\x04"=>[77,"\xD32"],"\x05"=>[18,"\xD32"],"\x06"=>[77,"\xD3a"],"\x07"=>[18,"\xD3a"],"\x08"=>[77,"\xD3c"],"\x09"=>[18,"\xD3c"],"\x0A"=>[77,"\xD3e"],"\x0B"=>[18,"\xD3e"],"\x0C"=>[77,"\xD3i"],"\x0D"=>[18,"\xD3i"],"\x0E"=>[77,"\xD3o"],"\x0F"=>[18,"\xD3o"],"\x10"=>[77,"\xD3s"],"\x11"=>[18,"\xD3s"],"\x12"=>[77,"\xD3t"],"\x13"=>[18,"\xD3t"],"\x14"=>[0,"\xD3\x20"],"\x15"=>[0,"\xD3\x25"],"\x16"=>[0,"\xD3-"],"\x17"=>[0,"\xD3."],"\x18"=>[0,"\xD3\x2F"],"\x19"=>[0,"\xD33"],"\x1A"=>[0,"\xD34"],"\x1B"=>[0,"\xD35"],"\x1C"=>[0,"\xD36"],"\x1D"=>[0,"\xD37"],"\x1E"=>[0,"\xD38"],"\x1F"=>[0,"\xD39"],"\x20"=>[0,"\xD3\x3D"],"\x21"=>[0,"\xD3A"],"\x22"=>[0,"\xD3_"],"\x23"=>[0,"\xD3b"],"\x24"=>[0,"\xD3d"],"\x25"=>[0,"\xD3f"],"\x26"=>[0,"\xD3g"],"\x27"=>[0,"\xD3h"],"\x28"=>[0,"\xD3l"],"\x29"=>[0,"\xD3m"],"\x2A"=>[0,"\xD3n"],"\x2B"=>[0,"\xD3p"],"\x2C"=>[0,"\xD3r"],"-"=>[0,"\xD3u"],"."=>[100,"\xD3"],"\x2F"=>[110,"\xD3"],[111,"\xD3"],[115,"\xD3"],[116,"\xD3"],[118,"\xD3"],[119,"\xD3"],[122,"\xD3"],[123,"\xD3"],[125,"\xD3"],[126,"\xD3"],[129,"\xD3"],"\x3A"=>[143,"\xD3"],"\x3B"=>[148,"\xD3"],"\x3C"=>[151,"\xD3"],"\x3D"=>[153,"\xD3"],"\x3E"=>[83,"\xD3"],"\x3F"=>[10,"\xD3"],"\x40"=>[77,"\xD40"],"A"=>[18,"\xD40"],"B"=>[77,"\xD41"],"C"=>[18,"\xD41"],"D"=>[77,"\xD42"],"E"=>[18,"\xD42"],"F"=>[77,"\xD4a"],"G"=>[18,"\xD4a"],"H"=>[77,"\xD4c"],"I"=>[18,"\xD4c"],"J"=>[77,"\xD4e"],"K"=>[18,"\xD4e"],"L"=>[77,"\xD4i"],"M"=>[18,"\xD4i"],"N"=>[77,"\xD4o"],"O"=>[18,"\xD4o"],"P"=>[77,"\xD4s"],"Q"=>[18,"\xD4s"],"R"=>[77,"\xD4t"],"S"=>[18,"\xD4t"],"T"=>[0,"\xD4\x20"],"U"=>[0,"\xD4\x25"],"V"=>[0,"\xD4-"],"W"=>[0,"\xD4."],"X"=>[0,"\xD4\x2F"],"Y"=>[0,"\xD43"],"Z"=>[0,"\xD44"],"\x5B"=>[0,"\xD45"],"\x5C"=>[0,"\xD46"],"\x5D"=>[0,"\xD47"],"\x5E"=>[0,"\xD48"],"_"=>[0,"\xD49"],"\x60"=>[0,"\xD4\x3D"],"a"=>[0,"\xD4A"],"b"=>[0,"\xD4_"],"c"=>[0,"\xD4b"],"d"=>[0,"\xD4d"],"e"=>[0,"\xD4f"],"f"=>[0,"\xD4g"],"g"=>[0,"\xD4h"],"h"=>[0,"\xD4l"],"i"=>[0,"\xD4m"],"j"=>[0,"\xD4n"],"k"=>[0,"\xD4p"],"l"=>[0,"\xD4r"],"m"=>[0,"\xD4u"],"n"=>[100,"\xD4"],"o"=>[110,"\xD4"],"p"=>[111,"\xD4"],"q"=>[115,"\xD4"],"r"=>[116,"\xD4"],"s"=>[118,"\xD4"],"t"=>[119,"\xD4"],"u"=>[122,"\xD4"],"v"=>[123,"\xD4"],"w"=>[125,"\xD4"],"x"=>[126,"\xD4"],"y"=>[129,"\xD4"],"z"=>[143,"\xD4"],"\x7B"=>[148,"\xD4"],"\x7C"=>[151,"\xD4"],"\x7D"=>[153,"\xD4"],"~"=>[83,"\xD4"],"\x7F"=>[10,"\xD4"],"\x80"=>[77,"\xD60"],"\x81"=>[18,"\xD60"],"\x82"=>[77,"\xD61"],"\x83"=>[18,"\xD61"],"\x84"=>[77,"\xD62"],"\x85"=>[18,"\xD62"],"\x86"=>[77,"\xD6a"],"\x87"=>[18,"\xD6a"],"\x88"=>[77,"\xD6c"],"\x89"=>[18,"\xD6c"],"\x8A"=>[77,"\xD6e"],"\x8B"=>[18,"\xD6e"],"\x8C"=>[77,"\xD6i"],"\x8D"=>[18,"\xD6i"],"\x8E"=>[77,"\xD6o"],"\x8F"=>[18,"\xD6o"],"\x90"=>[77,"\xD6s"],"\x91"=>[18,"\xD6s"],"\x92"=>[77,"\xD6t"],"\x93"=>[18,"\xD6t"],"\x94"=>[0,"\xD6\x20"],"\x95"=>[0,"\xD6\x25"],"\x96"=>[0,"\xD6-"],"\x97"=>[0,"\xD6."],"\x98"=>[0,"\xD6\x2F"],"\x99"=>[0,"\xD63"],"\x9A"=>[0,"\xD64"],"\x9B"=>[0,"\xD65"],"\x9C"=>[0,"\xD66"],"\x9D"=>[0,"\xD67"],"\x9E"=>[0,"\xD68"],"\x9F"=>[0,"\xD69"],"\xA0"=>[0,"\xD6\x3D"],"\xA1"=>[0,"\xD6A"],"\xA2"=>[0,"\xD6_"],"\xA3"=>[0,"\xD6b"],"\xA4"=>[0,"\xD6d"],"\xA5"=>[0,"\xD6f"],"\xA6"=>[0,"\xD6g"],"\xA7"=>[0,"\xD6h"],"\xA8"=>[0,"\xD6l"],"\xA9"=>[0,"\xD6m"],"\xAA"=>[0,"\xD6n"],"\xAB"=>[0,"\xD6p"],"\xAC"=>[0,"\xD6r"],"\xAD"=>[0,"\xD6u"],"\xAE"=>[100,"\xD6"],"\xAF"=>[110,"\xD6"],"\xB0"=>[111,"\xD6"],"\xB1"=>[115,"\xD6"],"\xB2"=>[116,"\xD6"],"\xB3"=>[118,"\xD6"],"\xB4"=>[119,"\xD6"],"\xB5"=>[122,"\xD6"],"\xB6"=>[123,"\xD6"],"\xB7"=>[125,"\xD6"],"\xB8"=>[126,"\xD6"],"\xB9"=>[129,"\xD6"],"\xBA"=>[143,"\xD6"],"\xBB"=>[148,"\xD6"],"\xBC"=>[151,"\xD6"],"\xBD"=>[153,"\xD6"],"\xBE"=>[83,"\xD6"],"\xBF"=>[10,"\xD6"],"\xC0"=>[77,"\xDD0"],"\xC1"=>[18,"\xDD0"],"\xC2"=>[77,"\xDD1"],"\xC3"=>[18,"\xDD1"],"\xC4"=>[77,"\xDD2"],"\xC5"=>[18,"\xDD2"],"\xC6"=>[77,"\xDDa"],"\xC7"=>[18,"\xDDa"],"\xC8"=>[77,"\xDDc"],"\xC9"=>[18,"\xDDc"],"\xCA"=>[77,"\xDDe"],"\xCB"=>[18,"\xDDe"],"\xCC"=>[77,"\xDDi"],"\xCD"=>[18,"\xDDi"],"\xCE"=>[77,"\xDDo"],"\xCF"=>[18,"\xDDo"],"\xD0"=>[77,"\xDDs"],"\xD1"=>[18,"\xDDs"],"\xD2"=>[77,"\xDDt"],"\xD3"=>[18,"\xDDt"],"\xD4"=>[0,"\xDD\x20"],"\xD5"=>[0,"\xDD\x25"],"\xD6"=>[0,"\xDD-"],"\xD7"=>[0,"\xDD."],"\xD8"=>[0,"\xDD\x2F"],"\xD9"=>[0,"\xDD3"],"\xDA"=>[0,"\xDD4"],"\xDB"=>[0,"\xDD5"],"\xDC"=>[0,"\xDD6"],"\xDD"=>[0,"\xDD7"],"\xDE"=>[0,"\xDD8"],"\xDF"=>[0,"\xDD9"],"\xE0"=>[0,"\xDD\x3D"],"\xE1"=>[0,"\xDDA"],"\xE2"=>[0,"\xDD_"],"\xE3"=>[0,"\xDDb"],"\xE4"=>[0,"\xDDd"],"\xE5"=>[0,"\xDDf"],"\xE6"=>[0,"\xDDg"],"\xE7"=>[0,"\xDDh"],"\xE8"=>[0,"\xDDl"],"\xE9"=>[0,"\xDDm"],"\xEA"=>[0,"\xDDn"],"\xEB"=>[0,"\xDDp"],"\xEC"=>[0,"\xDDr"],"\xED"=>[0,"\xDDu"],"\xEE"=>[100,"\xDD"],"\xEF"=>[110,"\xDD"],"\xF0"=>[111,"\xDD"],"\xF1"=>[115,"\xDD"],"\xF2"=>[116,"\xDD"],"\xF3"=>[118,"\xDD"],"\xF4"=>[119,"\xDD"],"\xF5"=>[122,"\xDD"],"\xF6"=>[123,"\xDD"],"\xF7"=>[125,"\xDD"],"\xF8"=>[126,"\xDD"],"\xF9"=>[129,"\xDD"],"\xFA"=>[143,"\xDD"],"\xFB"=>[148,"\xDD"],"\xFC"=>[151,"\xDD"],"\xFD"=>[153,"\xDD"],"\xFE"=>[83,"\xDD"],"\xFF"=>[10,"\xDD"],],["\x00"=>[94,"\xD60"],"\x01"=>[76,"\xD60"],"\x02"=>[104,"\xD60"],"\x03"=>[16,"\xD60"],"\x04"=>[94,"\xD61"],"\x05"=>[76,"\xD61"],"\x06"=>[104,"\xD61"],"\x07"=>[16,"\xD61"],"\x08"=>[94,"\xD62"],"\x09"=>[76,"\xD62"],"\x0A"=>[104,"\xD62"],"\x0B"=>[16,"\xD62"],"\x0C"=>[94,"\xD6a"],"\x0D"=>[76,"\xD6a"],"\x0E"=>[104,"\xD6a"],"\x0F"=>[16,"\xD6a"],"\x10"=>[94,"\xD6c"],"\x11"=>[76,"\xD6c"],"\x12"=>[104,"\xD6c"],"\x13"=>[16,"\xD6c"],"\x14"=>[94,"\xD6e"],"\x15"=>[76,"\xD6e"],"\x16"=>[104,"\xD6e"],"\x17"=>[16,"\xD6e"],"\x18"=>[94,"\xD6i"],"\x19"=>[76,"\xD6i"],"\x1A"=>[104,"\xD6i"],"\x1B"=>[16,"\xD6i"],"\x1C"=>[94,"\xD6o"],"\x1D"=>[76,"\xD6o"],"\x1E"=>[104,"\xD6o"],"\x1F"=>[16,"\xD6o"],"\x20"=>[94,"\xD6s"],"\x21"=>[76,"\xD6s"],"\x22"=>[104,"\xD6s"],"\x23"=>[16,"\xD6s"],"\x24"=>[94,"\xD6t"],"\x25"=>[76,"\xD6t"],"\x26"=>[104,"\xD6t"],"\x27"=>[16,"\xD6t"],"\x28"=>[77,"\xD6\x20"],"\x29"=>[18,"\xD6\x20"],"\x2A"=>[77,"\xD6\x25"],"\x2B"=>[18,"\xD6\x25"],"\x2C"=>[77,"\xD6-"],"-"=>[18,"\xD6-"],"."=>[77,"\xD6."],"\x2F"=>[18,"\xD6."],[77,"\xD6\x2F"],[18,"\xD6\x2F"],[77,"\xD63"],[18,"\xD63"],[77,"\xD64"],[18,"\xD64"],[77,"\xD65"],[18,"\xD65"],[77,"\xD66"],[18,"\xD66"],"\x3A"=>[77,"\xD67"],"\x3B"=>[18,"\xD67"],"\x3C"=>[77,"\xD68"],"\x3D"=>[18,"\xD68"],"\x3E"=>[77,"\xD69"],"\x3F"=>[18,"\xD69"],"\x40"=>[77,"\xD6\x3D"],"A"=>[18,"\xD6\x3D"],"B"=>[77,"\xD6A"],"C"=>[18,"\xD6A"],"D"=>[77,"\xD6_"],"E"=>[18,"\xD6_"],"F"=>[77,"\xD6b"],"G"=>[18,"\xD6b"],"H"=>[77,"\xD6d"],"I"=>[18,"\xD6d"],"J"=>[77,"\xD6f"],"K"=>[18,"\xD6f"],"L"=>[77,"\xD6g"],"M"=>[18,"\xD6g"],"N"=>[77,"\xD6h"],"O"=>[18,"\xD6h"],"P"=>[77,"\xD6l"],"Q"=>[18,"\xD6l"],"R"=>[77,"\xD6m"],"S"=>[18,"\xD6m"],"T"=>[77,"\xD6n"],"U"=>[18,"\xD6n"],"V"=>[77,"\xD6p"],"W"=>[18,"\xD6p"],"X"=>[77,"\xD6r"],"Y"=>[18,"\xD6r"],"Z"=>[77,"\xD6u"],"\x5B"=>[18,"\xD6u"],"\x5C"=>[0,"\xD6\x3A"],"\x5D"=>[0,"\xD6B"],"\x5E"=>[0,"\xD6C"],"_"=>[0,"\xD6D"],"\x60"=>[0,"\xD6E"],"a"=>[0,"\xD6F"],"b"=>[0,"\xD6G"],"c"=>[0,"\xD6H"],"d"=>[0,"\xD6I"],"e"=>[0,"\xD6J"],"f"=>[0,"\xD6K"],"g"=>[0,"\xD6L"],"h"=>[0,"\xD6M"],"i"=>[0,"\xD6N"],"j"=>[0,"\xD6O"],"k"=>[0,"\xD6P"],"l"=>[0,"\xD6Q"],"m"=>[0,"\xD6R"],"n"=>[0,"\xD6S"],"o"=>[0,"\xD6T"],"p"=>[0,"\xD6U"],"q"=>[0,"\xD6V"],"r"=>[0,"\xD6W"],"s"=>[0,"\xD6Y"],"t"=>[0,"\xD6j"],"u"=>[0,"\xD6k"],"v"=>[0,"\xD6q"],"w"=>[0,"\xD6v"],"x"=>[0,"\xD6w"],"y"=>[0,"\xD6x"],"z"=>[0,"\xD6y"],"\x7B"=>[0,"\xD6z"],"\x7C"=>[82,"\xD6"],"\x7D"=>[87,"\xD6"],"~"=>[130,"\xD6"],"\x7F"=>[9,"\xD6"],"\x80"=>[94,"\xDD0"],"\x81"=>[76,"\xDD0"],"\x82"=>[104,"\xDD0"],"\x83"=>[16,"\xDD0"],"\x84"=>[94,"\xDD1"],"\x85"=>[76,"\xDD1"],"\x86"=>[104,"\xDD1"],"\x87"=>[16,"\xDD1"],"\x88"=>[94,"\xDD2"],"\x89"=>[76,"\xDD2"],"\x8A"=>[104,"\xDD2"],"\x8B"=>[16,"\xDD2"],"\x8C"=>[94,"\xDDa"],"\x8D"=>[76,"\xDDa"],"\x8E"=>[104,"\xDDa"],"\x8F"=>[16,"\xDDa"],"\x90"=>[94,"\xDDc"],"\x91"=>[76,"\xDDc"],"\x92"=>[104,"\xDDc"],"\x93"=>[16,"\xDDc"],"\x94"=>[94,"\xDDe"],"\x95"=>[76,"\xDDe"],"\x96"=>[104,"\xDDe"],"\x97"=>[16,"\xDDe"],"\x98"=>[94,"\xDDi"],"\x99"=>[76,"\xDDi"],"\x9A"=>[104,"\xDDi"],"\x9B"=>[16,"\xDDi"],"\x9C"=>[94,"\xDDo"],"\x9D"=>[76,"\xDDo"],"\x9E"=>[104,"\xDDo"],"\x9F"=>[16,"\xDDo"],"\xA0"=>[94,"\xDDs"],"\xA1"=>[76,"\xDDs"],"\xA2"=>[104,"\xDDs"],"\xA3"=>[16,"\xDDs"],"\xA4"=>[94,"\xDDt"],"\xA5"=>[76,"\xDDt"],"\xA6"=>[104,"\xDDt"],"\xA7"=>[16,"\xDDt"],"\xA8"=>[77,"\xDD\x20"],"\xA9"=>[18,"\xDD\x20"],"\xAA"=>[77,"\xDD\x25"],"\xAB"=>[18,"\xDD\x25"],"\xAC"=>[77,"\xDD-"],"\xAD"=>[18,"\xDD-"],"\xAE"=>[77,"\xDD."],"\xAF"=>[18,"\xDD."],"\xB0"=>[77,"\xDD\x2F"],"\xB1"=>[18,"\xDD\x2F"],"\xB2"=>[77,"\xDD3"],"\xB3"=>[18,"\xDD3"],"\xB4"=>[77,"\xDD4"],"\xB5"=>[18,"\xDD4"],"\xB6"=>[77,"\xDD5"],"\xB7"=>[18,"\xDD5"],"\xB8"=>[77,"\xDD6"],"\xB9"=>[18,"\xDD6"],"\xBA"=>[77,"\xDD7"],"\xBB"=>[18,"\xDD7"],"\xBC"=>[77,"\xDD8"],"\xBD"=>[18,"\xDD8"],"\xBE"=>[77,"\xDD9"],"\xBF"=>[18,"\xDD9"],"\xC0"=>[77,"\xDD\x3D"],"\xC1"=>[18,"\xDD\x3D"],"\xC2"=>[77,"\xDDA"],"\xC3"=>[18,"\xDDA"],"\xC4"=>[77,"\xDD_"],"\xC5"=>[18,"\xDD_"],"\xC6"=>[77,"\xDDb"],"\xC7"=>[18,"\xDDb"],"\xC8"=>[77,"\xDDd"],"\xC9"=>[18,"\xDDd"],"\xCA"=>[77,"\xDDf"],"\xCB"=>[18,"\xDDf"],"\xCC"=>[77,"\xDDg"],"\xCD"=>[18,"\xDDg"],"\xCE"=>[77,"\xDDh"],"\xCF"=>[18,"\xDDh"],"\xD0"=>[77,"\xDDl"],"\xD1"=>[18,"\xDDl"],"\xD2"=>[77,"\xDDm"],"\xD3"=>[18,"\xDDm"],"\xD4"=>[77,"\xDDn"],"\xD5"=>[18,"\xDDn"],"\xD6"=>[77,"\xDDp"],"\xD7"=>[18,"\xDDp"],"\xD8"=>[77,"\xDDr"],"\xD9"=>[18,"\xDDr"],"\xDA"=>[77,"\xDDu"],"\xDB"=>[18,"\xDDu"],"\xDC"=>[0,"\xDD\x3A"],"\xDD"=>[0,"\xDDB"],"\xDE"=>[0,"\xDDC"],"\xDF"=>[0,"\xDDD"],"\xE0"=>[0,"\xDDE"],"\xE1"=>[0,"\xDDF"],"\xE2"=>[0,"\xDDG"],"\xE3"=>[0,"\xDDH"],"\xE4"=>[0,"\xDDI"],"\xE5"=>[0,"\xDDJ"],"\xE6"=>[0,"\xDDK"],"\xE7"=>[0,"\xDDL"],"\xE8"=>[0,"\xDDM"],"\xE9"=>[0,"\xDDN"],"\xEA"=>[0,"\xDDO"],"\xEB"=>[0,"\xDDP"],"\xEC"=>[0,"\xDDQ"],"\xED"=>[0,"\xDDR"],"\xEE"=>[0,"\xDDS"],"\xEF"=>[0,"\xDDT"],"\xF0"=>[0,"\xDDU"],"\xF1"=>[0,"\xDDV"],"\xF2"=>[0,"\xDDW"],"\xF3"=>[0,"\xDDY"],"\xF4"=>[0,"\xDDj"],"\xF5"=>[0,"\xDDk"],"\xF6"=>[0,"\xDDq"],"\xF7"=>[0,"\xDDv"],"\xF8"=>[0,"\xDDw"],"\xF9"=>[0,"\xDDx"],"\xFA"=>[0,"\xDDy"],"\xFB"=>[0,"\xDDz"],"\xFC"=>[82,"\xDD"],"\xFD"=>[87,"\xDD"],"\xFE"=>[130,"\xDD"],"\xFF"=>[9,"\xDD"],],["\x00"=>[94,"\xD70"],"\x01"=>[76,"\xD70"],"\x02"=>[104,"\xD70"],"\x03"=>[16,"\xD70"],"\x04"=>[94,"\xD71"],"\x05"=>[76,"\xD71"],"\x06"=>[104,"\xD71"],"\x07"=>[16,"\xD71"],"\x08"=>[94,"\xD72"],"\x09"=>[76,"\xD72"],"\x0A"=>[104,"\xD72"],"\x0B"=>[16,"\xD72"],"\x0C"=>[94,"\xD7a"],"\x0D"=>[76,"\xD7a"],"\x0E"=>[104,"\xD7a"],"\x0F"=>[16,"\xD7a"],"\x10"=>[94,"\xD7c"],"\x11"=>[76,"\xD7c"],"\x12"=>[104,"\xD7c"],"\x13"=>[16,"\xD7c"],"\x14"=>[94,"\xD7e"],"\x15"=>[76,"\xD7e"],"\x16"=>[104,"\xD7e"],"\x17"=>[16,"\xD7e"],"\x18"=>[94,"\xD7i"],"\x19"=>[76,"\xD7i"],"\x1A"=>[104,"\xD7i"],"\x1B"=>[16,"\xD7i"],"\x1C"=>[94,"\xD7o"],"\x1D"=>[76,"\xD7o"],"\x1E"=>[104,"\xD7o"],"\x1F"=>[16,"\xD7o"],"\x20"=>[94,"\xD7s"],"\x21"=>[76,"\xD7s"],"\x22"=>[104,"\xD7s"],"\x23"=>[16,"\xD7s"],"\x24"=>[94,"\xD7t"],"\x25"=>[76,"\xD7t"],"\x26"=>[104,"\xD7t"],"\x27"=>[16,"\xD7t"],"\x28"=>[77,"\xD7\x20"],"\x29"=>[18,"\xD7\x20"],"\x2A"=>[77,"\xD7\x25"],"\x2B"=>[18,"\xD7\x25"],"\x2C"=>[77,"\xD7-"],"-"=>[18,"\xD7-"],"."=>[77,"\xD7."],"\x2F"=>[18,"\xD7."],[77,"\xD7\x2F"],[18,"\xD7\x2F"],[77,"\xD73"],[18,"\xD73"],[77,"\xD74"],[18,"\xD74"],[77,"\xD75"],[18,"\xD75"],[77,"\xD76"],[18,"\xD76"],"\x3A"=>[77,"\xD77"],"\x3B"=>[18,"\xD77"],"\x3C"=>[77,"\xD78"],"\x3D"=>[18,"\xD78"],"\x3E"=>[77,"\xD79"],"\x3F"=>[18,"\xD79"],"\x40"=>[77,"\xD7\x3D"],"A"=>[18,"\xD7\x3D"],"B"=>[77,"\xD7A"],"C"=>[18,"\xD7A"],"D"=>[77,"\xD7_"],"E"=>[18,"\xD7_"],"F"=>[77,"\xD7b"],"G"=>[18,"\xD7b"],"H"=>[77,"\xD7d"],"I"=>[18,"\xD7d"],"J"=>[77,"\xD7f"],"K"=>[18,"\xD7f"],"L"=>[77,"\xD7g"],"M"=>[18,"\xD7g"],"N"=>[77,"\xD7h"],"O"=>[18,"\xD7h"],"P"=>[77,"\xD7l"],"Q"=>[18,"\xD7l"],"R"=>[77,"\xD7m"],"S"=>[18,"\xD7m"],"T"=>[77,"\xD7n"],"U"=>[18,"\xD7n"],"V"=>[77,"\xD7p"],"W"=>[18,"\xD7p"],"X"=>[77,"\xD7r"],"Y"=>[18,"\xD7r"],"Z"=>[77,"\xD7u"],"\x5B"=>[18,"\xD7u"],"\x5C"=>[0,"\xD7\x3A"],"\x5D"=>[0,"\xD7B"],"\x5E"=>[0,"\xD7C"],"_"=>[0,"\xD7D"],"\x60"=>[0,"\xD7E"],"a"=>[0,"\xD7F"],"b"=>[0,"\xD7G"],"c"=>[0,"\xD7H"],"d"=>[0,"\xD7I"],"e"=>[0,"\xD7J"],"f"=>[0,"\xD7K"],"g"=>[0,"\xD7L"],"h"=>[0,"\xD7M"],"i"=>[0,"\xD7N"],"j"=>[0,"\xD7O"],"k"=>[0,"\xD7P"],"l"=>[0,"\xD7Q"],"m"=>[0,"\xD7R"],"n"=>[0,"\xD7S"],"o"=>[0,"\xD7T"],"p"=>[0,"\xD7U"],"q"=>[0,"\xD7V"],"r"=>[0,"\xD7W"],"s"=>[0,"\xD7Y"],"t"=>[0,"\xD7j"],"u"=>[0,"\xD7k"],"v"=>[0,"\xD7q"],"w"=>[0,"\xD7v"],"x"=>[0,"\xD7w"],"y"=>[0,"\xD7x"],"z"=>[0,"\xD7y"],"\x7B"=>[0,"\xD7z"],"\x7C"=>[82,"\xD7"],"\x7D"=>[87,"\xD7"],"~"=>[130,"\xD7"],"\x7F"=>[9,"\xD7"],"\x80"=>[94,"\xE10"],"\x81"=>[76,"\xE10"],"\x82"=>[104,"\xE10"],"\x83"=>[16,"\xE10"],"\x84"=>[94,"\xE11"],"\x85"=>[76,"\xE11"],"\x86"=>[104,"\xE11"],"\x87"=>[16,"\xE11"],"\x88"=>[94,"\xE12"],"\x89"=>[76,"\xE12"],"\x8A"=>[104,"\xE12"],"\x8B"=>[16,"\xE12"],"\x8C"=>[94,"\xE1a"],"\x8D"=>[76,"\xE1a"],"\x8E"=>[104,"\xE1a"],"\x8F"=>[16,"\xE1a"],"\x90"=>[94,"\xE1c"],"\x91"=>[76,"\xE1c"],"\x92"=>[104,"\xE1c"],"\x93"=>[16,"\xE1c"],"\x94"=>[94,"\xE1e"],"\x95"=>[76,"\xE1e"],"\x96"=>[104,"\xE1e"],"\x97"=>[16,"\xE1e"],"\x98"=>[94,"\xE1i"],"\x99"=>[76,"\xE1i"],"\x9A"=>[104,"\xE1i"],"\x9B"=>[16,"\xE1i"],"\x9C"=>[94,"\xE1o"],"\x9D"=>[76,"\xE1o"],"\x9E"=>[104,"\xE1o"],"\x9F"=>[16,"\xE1o"],"\xA0"=>[94,"\xE1s"],"\xA1"=>[76,"\xE1s"],"\xA2"=>[104,"\xE1s"],"\xA3"=>[16,"\xE1s"],"\xA4"=>[94,"\xE1t"],"\xA5"=>[76,"\xE1t"],"\xA6"=>[104,"\xE1t"],"\xA7"=>[16,"\xE1t"],"\xA8"=>[77,"\xE1\x20"],"\xA9"=>[18,"\xE1\x20"],"\xAA"=>[77,"\xE1\x25"],"\xAB"=>[18,"\xE1\x25"],"\xAC"=>[77,"\xE1-"],"\xAD"=>[18,"\xE1-"],"\xAE"=>[77,"\xE1."],"\xAF"=>[18,"\xE1."],"\xB0"=>[77,"\xE1\x2F"],"\xB1"=>[18,"\xE1\x2F"],"\xB2"=>[77,"\xE13"],"\xB3"=>[18,"\xE13"],"\xB4"=>[77,"\xE14"],"\xB5"=>[18,"\xE14"],"\xB6"=>[77,"\xE15"],"\xB7"=>[18,"\xE15"],"\xB8"=>[77,"\xE16"],"\xB9"=>[18,"\xE16"],"\xBA"=>[77,"\xE17"],"\xBB"=>[18,"\xE17"],"\xBC"=>[77,"\xE18"],"\xBD"=>[18,"\xE18"],"\xBE"=>[77,"\xE19"],"\xBF"=>[18,"\xE19"],"\xC0"=>[77,"\xE1\x3D"],"\xC1"=>[18,"\xE1\x3D"],"\xC2"=>[77,"\xE1A"],"\xC3"=>[18,"\xE1A"],"\xC4"=>[77,"\xE1_"],"\xC5"=>[18,"\xE1_"],"\xC6"=>[77,"\xE1b"],"\xC7"=>[18,"\xE1b"],"\xC8"=>[77,"\xE1d"],"\xC9"=>[18,"\xE1d"],"\xCA"=>[77,"\xE1f"],"\xCB"=>[18,"\xE1f"],"\xCC"=>[77,"\xE1g"],"\xCD"=>[18,"\xE1g"],"\xCE"=>[77,"\xE1h"],"\xCF"=>[18,"\xE1h"],"\xD0"=>[77,"\xE1l"],"\xD1"=>[18,"\xE1l"],"\xD2"=>[77,"\xE1m"],"\xD3"=>[18,"\xE1m"],"\xD4"=>[77,"\xE1n"],"\xD5"=>[18,"\xE1n"],"\xD6"=>[77,"\xE1p"],"\xD7"=>[18,"\xE1p"],"\xD8"=>[77,"\xE1r"],"\xD9"=>[18,"\xE1r"],"\xDA"=>[77,"\xE1u"],"\xDB"=>[18,"\xE1u"],"\xDC"=>[0,"\xE1\x3A"],"\xDD"=>[0,"\xE1B"],"\xDE"=>[0,"\xE1C"],"\xDF"=>[0,"\xE1D"],"\xE0"=>[0,"\xE1E"],"\xE1"=>[0,"\xE1F"],"\xE2"=>[0,"\xE1G"],"\xE3"=>[0,"\xE1H"],"\xE4"=>[0,"\xE1I"],"\xE5"=>[0,"\xE1J"],"\xE6"=>[0,"\xE1K"],"\xE7"=>[0,"\xE1L"],"\xE8"=>[0,"\xE1M"],"\xE9"=>[0,"\xE1N"],"\xEA"=>[0,"\xE1O"],"\xEB"=>[0,"\xE1P"],"\xEC"=>[0,"\xE1Q"],"\xED"=>[0,"\xE1R"],"\xEE"=>[0,"\xE1S"],"\xEF"=>[0,"\xE1T"],"\xF0"=>[0,"\xE1U"],"\xF1"=>[0,"\xE1V"],"\xF2"=>[0,"\xE1W"],"\xF3"=>[0,"\xE1Y"],"\xF4"=>[0,"\xE1j"],"\xF5"=>[0,"\xE1k"],"\xF6"=>[0,"\xE1q"],"\xF7"=>[0,"\xE1v"],"\xF8"=>[0,"\xE1w"],"\xF9"=>[0,"\xE1x"],"\xFA"=>[0,"\xE1y"],"\xFB"=>[0,"\xE1z"],"\xFC"=>[82,"\xE1"],"\xFD"=>[87,"\xE1"],"\xFE"=>[130,"\xE1"],"\xFF"=>[9,"\xE1"],],["\x00"=>[94,"\xD80"],"\x01"=>[76,"\xD80"],"\x02"=>[104,"\xD80"],"\x03"=>[16,"\xD80"],"\x04"=>[94,"\xD81"],"\x05"=>[76,"\xD81"],"\x06"=>[104,"\xD81"],"\x07"=>[16,"\xD81"],"\x08"=>[94,"\xD82"],"\x09"=>[76,"\xD82"],"\x0A"=>[104,"\xD82"],"\x0B"=>[16,"\xD82"],"\x0C"=>[94,"\xD8a"],"\x0D"=>[76,"\xD8a"],"\x0E"=>[104,"\xD8a"],"\x0F"=>[16,"\xD8a"],"\x10"=>[94,"\xD8c"],"\x11"=>[76,"\xD8c"],"\x12"=>[104,"\xD8c"],"\x13"=>[16,"\xD8c"],"\x14"=>[94,"\xD8e"],"\x15"=>[76,"\xD8e"],"\x16"=>[104,"\xD8e"],"\x17"=>[16,"\xD8e"],"\x18"=>[94,"\xD8i"],"\x19"=>[76,"\xD8i"],"\x1A"=>[104,"\xD8i"],"\x1B"=>[16,"\xD8i"],"\x1C"=>[94,"\xD8o"],"\x1D"=>[76,"\xD8o"],"\x1E"=>[104,"\xD8o"],"\x1F"=>[16,"\xD8o"],"\x20"=>[94,"\xD8s"],"\x21"=>[76,"\xD8s"],"\x22"=>[104,"\xD8s"],"\x23"=>[16,"\xD8s"],"\x24"=>[94,"\xD8t"],"\x25"=>[76,"\xD8t"],"\x26"=>[104,"\xD8t"],"\x27"=>[16,"\xD8t"],"\x28"=>[77,"\xD8\x20"],"\x29"=>[18,"\xD8\x20"],"\x2A"=>[77,"\xD8\x25"],"\x2B"=>[18,"\xD8\x25"],"\x2C"=>[77,"\xD8-"],"-"=>[18,"\xD8-"],"."=>[77,"\xD8."],"\x2F"=>[18,"\xD8."],[77,"\xD8\x2F"],[18,"\xD8\x2F"],[77,"\xD83"],[18,"\xD83"],[77,"\xD84"],[18,"\xD84"],[77,"\xD85"],[18,"\xD85"],[77,"\xD86"],[18,"\xD86"],"\x3A"=>[77,"\xD87"],"\x3B"=>[18,"\xD87"],"\x3C"=>[77,"\xD88"],"\x3D"=>[18,"\xD88"],"\x3E"=>[77,"\xD89"],"\x3F"=>[18,"\xD89"],"\x40"=>[77,"\xD8\x3D"],"A"=>[18,"\xD8\x3D"],"B"=>[77,"\xD8A"],"C"=>[18,"\xD8A"],"D"=>[77,"\xD8_"],"E"=>[18,"\xD8_"],"F"=>[77,"\xD8b"],"G"=>[18,"\xD8b"],"H"=>[77,"\xD8d"],"I"=>[18,"\xD8d"],"J"=>[77,"\xD8f"],"K"=>[18,"\xD8f"],"L"=>[77,"\xD8g"],"M"=>[18,"\xD8g"],"N"=>[77,"\xD8h"],"O"=>[18,"\xD8h"],"P"=>[77,"\xD8l"],"Q"=>[18,"\xD8l"],"R"=>[77,"\xD8m"],"S"=>[18,"\xD8m"],"T"=>[77,"\xD8n"],"U"=>[18,"\xD8n"],"V"=>[77,"\xD8p"],"W"=>[18,"\xD8p"],"X"=>[77,"\xD8r"],"Y"=>[18,"\xD8r"],"Z"=>[77,"\xD8u"],"\x5B"=>[18,"\xD8u"],"\x5C"=>[0,"\xD8\x3A"],"\x5D"=>[0,"\xD8B"],"\x5E"=>[0,"\xD8C"],"_"=>[0,"\xD8D"],"\x60"=>[0,"\xD8E"],"a"=>[0,"\xD8F"],"b"=>[0,"\xD8G"],"c"=>[0,"\xD8H"],"d"=>[0,"\xD8I"],"e"=>[0,"\xD8J"],"f"=>[0,"\xD8K"],"g"=>[0,"\xD8L"],"h"=>[0,"\xD8M"],"i"=>[0,"\xD8N"],"j"=>[0,"\xD8O"],"k"=>[0,"\xD8P"],"l"=>[0,"\xD8Q"],"m"=>[0,"\xD8R"],"n"=>[0,"\xD8S"],"o"=>[0,"\xD8T"],"p"=>[0,"\xD8U"],"q"=>[0,"\xD8V"],"r"=>[0,"\xD8W"],"s"=>[0,"\xD8Y"],"t"=>[0,"\xD8j"],"u"=>[0,"\xD8k"],"v"=>[0,"\xD8q"],"w"=>[0,"\xD8v"],"x"=>[0,"\xD8w"],"y"=>[0,"\xD8x"],"z"=>[0,"\xD8y"],"\x7B"=>[0,"\xD8z"],"\x7C"=>[82,"\xD8"],"\x7D"=>[87,"\xD8"],"~"=>[130,"\xD8"],"\x7F"=>[9,"\xD8"],"\x80"=>[94,"\xD90"],"\x81"=>[76,"\xD90"],"\x82"=>[104,"\xD90"],"\x83"=>[16,"\xD90"],"\x84"=>[94,"\xD91"],"\x85"=>[76,"\xD91"],"\x86"=>[104,"\xD91"],"\x87"=>[16,"\xD91"],"\x88"=>[94,"\xD92"],"\x89"=>[76,"\xD92"],"\x8A"=>[104,"\xD92"],"\x8B"=>[16,"\xD92"],"\x8C"=>[94,"\xD9a"],"\x8D"=>[76,"\xD9a"],"\x8E"=>[104,"\xD9a"],"\x8F"=>[16,"\xD9a"],"\x90"=>[94,"\xD9c"],"\x91"=>[76,"\xD9c"],"\x92"=>[104,"\xD9c"],"\x93"=>[16,"\xD9c"],"\x94"=>[94,"\xD9e"],"\x95"=>[76,"\xD9e"],"\x96"=>[104,"\xD9e"],"\x97"=>[16,"\xD9e"],"\x98"=>[94,"\xD9i"],"\x99"=>[76,"\xD9i"],"\x9A"=>[104,"\xD9i"],"\x9B"=>[16,"\xD9i"],"\x9C"=>[94,"\xD9o"],"\x9D"=>[76,"\xD9o"],"\x9E"=>[104,"\xD9o"],"\x9F"=>[16,"\xD9o"],"\xA0"=>[94,"\xD9s"],"\xA1"=>[76,"\xD9s"],"\xA2"=>[104,"\xD9s"],"\xA3"=>[16,"\xD9s"],"\xA4"=>[94,"\xD9t"],"\xA5"=>[76,"\xD9t"],"\xA6"=>[104,"\xD9t"],"\xA7"=>[16,"\xD9t"],"\xA8"=>[77,"\xD9\x20"],"\xA9"=>[18,"\xD9\x20"],"\xAA"=>[77,"\xD9\x25"],"\xAB"=>[18,"\xD9\x25"],"\xAC"=>[77,"\xD9-"],"\xAD"=>[18,"\xD9-"],"\xAE"=>[77,"\xD9."],"\xAF"=>[18,"\xD9."],"\xB0"=>[77,"\xD9\x2F"],"\xB1"=>[18,"\xD9\x2F"],"\xB2"=>[77,"\xD93"],"\xB3"=>[18,"\xD93"],"\xB4"=>[77,"\xD94"],"\xB5"=>[18,"\xD94"],"\xB6"=>[77,"\xD95"],"\xB7"=>[18,"\xD95"],"\xB8"=>[77,"\xD96"],"\xB9"=>[18,"\xD96"],"\xBA"=>[77,"\xD97"],"\xBB"=>[18,"\xD97"],"\xBC"=>[77,"\xD98"],"\xBD"=>[18,"\xD98"],"\xBE"=>[77,"\xD99"],"\xBF"=>[18,"\xD99"],"\xC0"=>[77,"\xD9\x3D"],"\xC1"=>[18,"\xD9\x3D"],"\xC2"=>[77,"\xD9A"],"\xC3"=>[18,"\xD9A"],"\xC4"=>[77,"\xD9_"],"\xC5"=>[18,"\xD9_"],"\xC6"=>[77,"\xD9b"],"\xC7"=>[18,"\xD9b"],"\xC8"=>[77,"\xD9d"],"\xC9"=>[18,"\xD9d"],"\xCA"=>[77,"\xD9f"],"\xCB"=>[18,"\xD9f"],"\xCC"=>[77,"\xD9g"],"\xCD"=>[18,"\xD9g"],"\xCE"=>[77,"\xD9h"],"\xCF"=>[18,"\xD9h"],"\xD0"=>[77,"\xD9l"],"\xD1"=>[18,"\xD9l"],"\xD2"=>[77,"\xD9m"],"\xD3"=>[18,"\xD9m"],"\xD4"=>[77,"\xD9n"],"\xD5"=>[18,"\xD9n"],"\xD6"=>[77,"\xD9p"],"\xD7"=>[18,"\xD9p"],"\xD8"=>[77,"\xD9r"],"\xD9"=>[18,"\xD9r"],"\xDA"=>[77,"\xD9u"],"\xDB"=>[18,"\xD9u"],"\xDC"=>[0,"\xD9\x3A"],"\xDD"=>[0,"\xD9B"],"\xDE"=>[0,"\xD9C"],"\xDF"=>[0,"\xD9D"],"\xE0"=>[0,"\xD9E"],"\xE1"=>[0,"\xD9F"],"\xE2"=>[0,"\xD9G"],"\xE3"=>[0,"\xD9H"],"\xE4"=>[0,"\xD9I"],"\xE5"=>[0,"\xD9J"],"\xE6"=>[0,"\xD9K"],"\xE7"=>[0,"\xD9L"],"\xE8"=>[0,"\xD9M"],"\xE9"=>[0,"\xD9N"],"\xEA"=>[0,"\xD9O"],"\xEB"=>[0,"\xD9P"],"\xEC"=>[0,"\xD9Q"],"\xED"=>[0,"\xD9R"],"\xEE"=>[0,"\xD9S"],"\xEF"=>[0,"\xD9T"],"\xF0"=>[0,"\xD9U"],"\xF1"=>[0,"\xD9V"],"\xF2"=>[0,"\xD9W"],"\xF3"=>[0,"\xD9Y"],"\xF4"=>[0,"\xD9j"],"\xF5"=>[0,"\xD9k"],"\xF6"=>[0,"\xD9q"],"\xF7"=>[0,"\xD9v"],"\xF8"=>[0,"\xD9w"],"\xF9"=>[0,"\xD9x"],"\xFA"=>[0,"\xD9y"],"\xFB"=>[0,"\xD9z"],"\xFC"=>[82,"\xD9"],"\xFD"=>[87,"\xD9"],"\xFE"=>[130,"\xD9"],"\xFF"=>[9,"\xD9"],],["\x00"=>[77,"\xD80"],"\x01"=>[18,"\xD80"],"\x02"=>[77,"\xD81"],"\x03"=>[18,"\xD81"],"\x04"=>[77,"\xD82"],"\x05"=>[18,"\xD82"],"\x06"=>[77,"\xD8a"],"\x07"=>[18,"\xD8a"],"\x08"=>[77,"\xD8c"],"\x09"=>[18,"\xD8c"],"\x0A"=>[77,"\xD8e"],"\x0B"=>[18,"\xD8e"],"\x0C"=>[77,"\xD8i"],"\x0D"=>[18,"\xD8i"],"\x0E"=>[77,"\xD8o"],"\x0F"=>[18,"\xD8o"],"\x10"=>[77,"\xD8s"],"\x11"=>[18,"\xD8s"],"\x12"=>[77,"\xD8t"],"\x13"=>[18,"\xD8t"],"\x14"=>[0,"\xD8\x20"],"\x15"=>[0,"\xD8\x25"],"\x16"=>[0,"\xD8-"],"\x17"=>[0,"\xD8."],"\x18"=>[0,"\xD8\x2F"],"\x19"=>[0,"\xD83"],"\x1A"=>[0,"\xD84"],"\x1B"=>[0,"\xD85"],"\x1C"=>[0,"\xD86"],"\x1D"=>[0,"\xD87"],"\x1E"=>[0,"\xD88"],"\x1F"=>[0,"\xD89"],"\x20"=>[0,"\xD8\x3D"],"\x21"=>[0,"\xD8A"],"\x22"=>[0,"\xD8_"],"\x23"=>[0,"\xD8b"],"\x24"=>[0,"\xD8d"],"\x25"=>[0,"\xD8f"],"\x26"=>[0,"\xD8g"],"\x27"=>[0,"\xD8h"],"\x28"=>[0,"\xD8l"],"\x29"=>[0,"\xD8m"],"\x2A"=>[0,"\xD8n"],"\x2B"=>[0,"\xD8p"],"\x2C"=>[0,"\xD8r"],"-"=>[0,"\xD8u"],"."=>[100,"\xD8"],"\x2F"=>[110,"\xD8"],[111,"\xD8"],[115,"\xD8"],[116,"\xD8"],[118,"\xD8"],[119,"\xD8"],[122,"\xD8"],[123,"\xD8"],[125,"\xD8"],[126,"\xD8"],[129,"\xD8"],"\x3A"=>[143,"\xD8"],"\x3B"=>[148,"\xD8"],"\x3C"=>[151,"\xD8"],"\x3D"=>[153,"\xD8"],"\x3E"=>[83,"\xD8"],"\x3F"=>[10,"\xD8"],"\x40"=>[77,"\xD90"],"A"=>[18,"\xD90"],"B"=>[77,"\xD91"],"C"=>[18,"\xD91"],"D"=>[77,"\xD92"],"E"=>[18,"\xD92"],"F"=>[77,"\xD9a"],"G"=>[18,"\xD9a"],"H"=>[77,"\xD9c"],"I"=>[18,"\xD9c"],"J"=>[77,"\xD9e"],"K"=>[18,"\xD9e"],"L"=>[77,"\xD9i"],"M"=>[18,"\xD9i"],"N"=>[77,"\xD9o"],"O"=>[18,"\xD9o"],"P"=>[77,"\xD9s"],"Q"=>[18,"\xD9s"],"R"=>[77,"\xD9t"],"S"=>[18,"\xD9t"],"T"=>[0,"\xD9\x20"],"U"=>[0,"\xD9\x25"],"V"=>[0,"\xD9-"],"W"=>[0,"\xD9."],"X"=>[0,"\xD9\x2F"],"Y"=>[0,"\xD93"],"Z"=>[0,"\xD94"],"\x5B"=>[0,"\xD95"],"\x5C"=>[0,"\xD96"],"\x5D"=>[0,"\xD97"],"\x5E"=>[0,"\xD98"],"_"=>[0,"\xD99"],"\x60"=>[0,"\xD9\x3D"],"a"=>[0,"\xD9A"],"b"=>[0,"\xD9_"],"c"=>[0,"\xD9b"],"d"=>[0,"\xD9d"],"e"=>[0,"\xD9f"],"f"=>[0,"\xD9g"],"g"=>[0,"\xD9h"],"h"=>[0,"\xD9l"],"i"=>[0,"\xD9m"],"j"=>[0,"\xD9n"],"k"=>[0,"\xD9p"],"l"=>[0,"\xD9r"],"m"=>[0,"\xD9u"],"n"=>[100,"\xD9"],"o"=>[110,"\xD9"],"p"=>[111,"\xD9"],"q"=>[115,"\xD9"],"r"=>[116,"\xD9"],"s"=>[118,"\xD9"],"t"=>[119,"\xD9"],"u"=>[122,"\xD9"],"v"=>[123,"\xD9"],"w"=>[125,"\xD9"],"x"=>[126,"\xD9"],"y"=>[129,"\xD9"],"z"=>[143,"\xD9"],"\x7B"=>[148,"\xD9"],"\x7C"=>[151,"\xD9"],"\x7D"=>[153,"\xD9"],"~"=>[83,"\xD9"],"\x7F"=>[10,"\xD9"],"\x80"=>[77,"\xE30"],"\x81"=>[18,"\xE30"],"\x82"=>[77,"\xE31"],"\x83"=>[18,"\xE31"],"\x84"=>[77,"\xE32"],"\x85"=>[18,"\xE32"],"\x86"=>[77,"\xE3a"],"\x87"=>[18,"\xE3a"],"\x88"=>[77,"\xE3c"],"\x89"=>[18,"\xE3c"],"\x8A"=>[77,"\xE3e"],"\x8B"=>[18,"\xE3e"],"\x8C"=>[77,"\xE3i"],"\x8D"=>[18,"\xE3i"],"\x8E"=>[77,"\xE3o"],"\x8F"=>[18,"\xE3o"],"\x90"=>[77,"\xE3s"],"\x91"=>[18,"\xE3s"],"\x92"=>[77,"\xE3t"],"\x93"=>[18,"\xE3t"],"\x94"=>[0,"\xE3\x20"],"\x95"=>[0,"\xE3\x25"],"\x96"=>[0,"\xE3-"],"\x97"=>[0,"\xE3."],"\x98"=>[0,"\xE3\x2F"],"\x99"=>[0,"\xE33"],"\x9A"=>[0,"\xE34"],"\x9B"=>[0,"\xE35"],"\x9C"=>[0,"\xE36"],"\x9D"=>[0,"\xE37"],"\x9E"=>[0,"\xE38"],"\x9F"=>[0,"\xE39"],"\xA0"=>[0,"\xE3\x3D"],"\xA1"=>[0,"\xE3A"],"\xA2"=>[0,"\xE3_"],"\xA3"=>[0,"\xE3b"],"\xA4"=>[0,"\xE3d"],"\xA5"=>[0,"\xE3f"],"\xA6"=>[0,"\xE3g"],"\xA7"=>[0,"\xE3h"],"\xA8"=>[0,"\xE3l"],"\xA9"=>[0,"\xE3m"],"\xAA"=>[0,"\xE3n"],"\xAB"=>[0,"\xE3p"],"\xAC"=>[0,"\xE3r"],"\xAD"=>[0,"\xE3u"],"\xAE"=>[100,"\xE3"],"\xAF"=>[110,"\xE3"],"\xB0"=>[111,"\xE3"],"\xB1"=>[115,"\xE3"],"\xB2"=>[116,"\xE3"],"\xB3"=>[118,"\xE3"],"\xB4"=>[119,"\xE3"],"\xB5"=>[122,"\xE3"],"\xB6"=>[123,"\xE3"],"\xB7"=>[125,"\xE3"],"\xB8"=>[126,"\xE3"],"\xB9"=>[129,"\xE3"],"\xBA"=>[143,"\xE3"],"\xBB"=>[148,"\xE3"],"\xBC"=>[151,"\xE3"],"\xBD"=>[153,"\xE3"],"\xBE"=>[83,"\xE3"],"\xBF"=>[10,"\xE3"],"\xC0"=>[77,"\xE50"],"\xC1"=>[18,"\xE50"],"\xC2"=>[77,"\xE51"],"\xC3"=>[18,"\xE51"],"\xC4"=>[77,"\xE52"],"\xC5"=>[18,"\xE52"],"\xC6"=>[77,"\xE5a"],"\xC7"=>[18,"\xE5a"],"\xC8"=>[77,"\xE5c"],"\xC9"=>[18,"\xE5c"],"\xCA"=>[77,"\xE5e"],"\xCB"=>[18,"\xE5e"],"\xCC"=>[77,"\xE5i"],"\xCD"=>[18,"\xE5i"],"\xCE"=>[77,"\xE5o"],"\xCF"=>[18,"\xE5o"],"\xD0"=>[77,"\xE5s"],"\xD1"=>[18,"\xE5s"],"\xD2"=>[77,"\xE5t"],"\xD3"=>[18,"\xE5t"],"\xD4"=>[0,"\xE5\x20"],"\xD5"=>[0,"\xE5\x25"],"\xD6"=>[0,"\xE5-"],"\xD7"=>[0,"\xE5."],"\xD8"=>[0,"\xE5\x2F"],"\xD9"=>[0,"\xE53"],"\xDA"=>[0,"\xE54"],"\xDB"=>[0,"\xE55"],"\xDC"=>[0,"\xE56"],"\xDD"=>[0,"\xE57"],"\xDE"=>[0,"\xE58"],"\xDF"=>[0,"\xE59"],"\xE0"=>[0,"\xE5\x3D"],"\xE1"=>[0,"\xE5A"],"\xE2"=>[0,"\xE5_"],"\xE3"=>[0,"\xE5b"],"\xE4"=>[0,"\xE5d"],"\xE5"=>[0,"\xE5f"],"\xE6"=>[0,"\xE5g"],"\xE7"=>[0,"\xE5h"],"\xE8"=>[0,"\xE5l"],"\xE9"=>[0,"\xE5m"],"\xEA"=>[0,"\xE5n"],"\xEB"=>[0,"\xE5p"],"\xEC"=>[0,"\xE5r"],"\xED"=>[0,"\xE5u"],"\xEE"=>[100,"\xE5"],"\xEF"=>[110,"\xE5"],"\xF0"=>[111,"\xE5"],"\xF1"=>[115,"\xE5"],"\xF2"=>[116,"\xE5"],"\xF3"=>[118,"\xE5"],"\xF4"=>[119,"\xE5"],"\xF5"=>[122,"\xE5"],"\xF6"=>[123,"\xE5"],"\xF7"=>[125,"\xE5"],"\xF8"=>[126,"\xE5"],"\xF9"=>[129,"\xE5"],"\xFA"=>[143,"\xE5"],"\xFB"=>[148,"\xE5"],"\xFC"=>[151,"\xE5"],"\xFD"=>[153,"\xE5"],"\xFE"=>[83,"\xE5"],"\xFF"=>[10,"\xE5"],],["\x00"=>[77,"\xDA0"],"\x01"=>[18,"\xDA0"],"\x02"=>[77,"\xDA1"],"\x03"=>[18,"\xDA1"],"\x04"=>[77,"\xDA2"],"\x05"=>[18,"\xDA2"],"\x06"=>[77,"\xDAa"],"\x07"=>[18,"\xDAa"],"\x08"=>[77,"\xDAc"],"\x09"=>[18,"\xDAc"],"\x0A"=>[77,"\xDAe"],"\x0B"=>[18,"\xDAe"],"\x0C"=>[77,"\xDAi"],"\x0D"=>[18,"\xDAi"],"\x0E"=>[77,"\xDAo"],"\x0F"=>[18,"\xDAo"],"\x10"=>[77,"\xDAs"],"\x11"=>[18,"\xDAs"],"\x12"=>[77,"\xDAt"],"\x13"=>[18,"\xDAt"],"\x14"=>[0,"\xDA\x20"],"\x15"=>[0,"\xDA\x25"],"\x16"=>[0,"\xDA-"],"\x17"=>[0,"\xDA."],"\x18"=>[0,"\xDA\x2F"],"\x19"=>[0,"\xDA3"],"\x1A"=>[0,"\xDA4"],"\x1B"=>[0,"\xDA5"],"\x1C"=>[0,"\xDA6"],"\x1D"=>[0,"\xDA7"],"\x1E"=>[0,"\xDA8"],"\x1F"=>[0,"\xDA9"],"\x20"=>[0,"\xDA\x3D"],"\x21"=>[0,"\xDAA"],"\x22"=>[0,"\xDA_"],"\x23"=>[0,"\xDAb"],"\x24"=>[0,"\xDAd"],"\x25"=>[0,"\xDAf"],"\x26"=>[0,"\xDAg"],"\x27"=>[0,"\xDAh"],"\x28"=>[0,"\xDAl"],"\x29"=>[0,"\xDAm"],"\x2A"=>[0,"\xDAn"],"\x2B"=>[0,"\xDAp"],"\x2C"=>[0,"\xDAr"],"-"=>[0,"\xDAu"],"."=>[100,"\xDA"],"\x2F"=>[110,"\xDA"],[111,"\xDA"],[115,"\xDA"],[116,"\xDA"],[118,"\xDA"],[119,"\xDA"],[122,"\xDA"],[123,"\xDA"],[125,"\xDA"],[126,"\xDA"],[129,"\xDA"],"\x3A"=>[143,"\xDA"],"\x3B"=>[148,"\xDA"],"\x3C"=>[151,"\xDA"],"\x3D"=>[153,"\xDA"],"\x3E"=>[83,"\xDA"],"\x3F"=>[10,"\xDA"],"\x40"=>[77,"\xDB0"],"A"=>[18,"\xDB0"],"B"=>[77,"\xDB1"],"C"=>[18,"\xDB1"],"D"=>[77,"\xDB2"],"E"=>[18,"\xDB2"],"F"=>[77,"\xDBa"],"G"=>[18,"\xDBa"],"H"=>[77,"\xDBc"],"I"=>[18,"\xDBc"],"J"=>[77,"\xDBe"],"K"=>[18,"\xDBe"],"L"=>[77,"\xDBi"],"M"=>[18,"\xDBi"],"N"=>[77,"\xDBo"],"O"=>[18,"\xDBo"],"P"=>[77,"\xDBs"],"Q"=>[18,"\xDBs"],"R"=>[77,"\xDBt"],"S"=>[18,"\xDBt"],"T"=>[0,"\xDB\x20"],"U"=>[0,"\xDB\x25"],"V"=>[0,"\xDB-"],"W"=>[0,"\xDB."],"X"=>[0,"\xDB\x2F"],"Y"=>[0,"\xDB3"],"Z"=>[0,"\xDB4"],"\x5B"=>[0,"\xDB5"],"\x5C"=>[0,"\xDB6"],"\x5D"=>[0,"\xDB7"],"\x5E"=>[0,"\xDB8"],"_"=>[0,"\xDB9"],"\x60"=>[0,"\xDB\x3D"],"a"=>[0,"\xDBA"],"b"=>[0,"\xDB_"],"c"=>[0,"\xDBb"],"d"=>[0,"\xDBd"],"e"=>[0,"\xDBf"],"f"=>[0,"\xDBg"],"g"=>[0,"\xDBh"],"h"=>[0,"\xDBl"],"i"=>[0,"\xDBm"],"j"=>[0,"\xDBn"],"k"=>[0,"\xDBp"],"l"=>[0,"\xDBr"],"m"=>[0,"\xDBu"],"n"=>[100,"\xDB"],"o"=>[110,"\xDB"],"p"=>[111,"\xDB"],"q"=>[115,"\xDB"],"r"=>[116,"\xDB"],"s"=>[118,"\xDB"],"t"=>[119,"\xDB"],"u"=>[122,"\xDB"],"v"=>[123,"\xDB"],"w"=>[125,"\xDB"],"x"=>[126,"\xDB"],"y"=>[129,"\xDB"],"z"=>[143,"\xDB"],"\x7B"=>[148,"\xDB"],"\x7C"=>[151,"\xDB"],"\x7D"=>[153,"\xDB"],"~"=>[83,"\xDB"],"\x7F"=>[10,"\xDB"],"\x80"=>[77,"\xEE0"],"\x81"=>[18,"\xEE0"],"\x82"=>[77,"\xEE1"],"\x83"=>[18,"\xEE1"],"\x84"=>[77,"\xEE2"],"\x85"=>[18,"\xEE2"],"\x86"=>[77,"\xEEa"],"\x87"=>[18,"\xEEa"],"\x88"=>[77,"\xEEc"],"\x89"=>[18,"\xEEc"],"\x8A"=>[77,"\xEEe"],"\x8B"=>[18,"\xEEe"],"\x8C"=>[77,"\xEEi"],"\x8D"=>[18,"\xEEi"],"\x8E"=>[77,"\xEEo"],"\x8F"=>[18,"\xEEo"],"\x90"=>[77,"\xEEs"],"\x91"=>[18,"\xEEs"],"\x92"=>[77,"\xEEt"],"\x93"=>[18,"\xEEt"],"\x94"=>[0,"\xEE\x20"],"\x95"=>[0,"\xEE\x25"],"\x96"=>[0,"\xEE-"],"\x97"=>[0,"\xEE."],"\x98"=>[0,"\xEE\x2F"],"\x99"=>[0,"\xEE3"],"\x9A"=>[0,"\xEE4"],"\x9B"=>[0,"\xEE5"],"\x9C"=>[0,"\xEE6"],"\x9D"=>[0,"\xEE7"],"\x9E"=>[0,"\xEE8"],"\x9F"=>[0,"\xEE9"],"\xA0"=>[0,"\xEE\x3D"],"\xA1"=>[0,"\xEEA"],"\xA2"=>[0,"\xEE_"],"\xA3"=>[0,"\xEEb"],"\xA4"=>[0,"\xEEd"],"\xA5"=>[0,"\xEEf"],"\xA6"=>[0,"\xEEg"],"\xA7"=>[0,"\xEEh"],"\xA8"=>[0,"\xEEl"],"\xA9"=>[0,"\xEEm"],"\xAA"=>[0,"\xEEn"],"\xAB"=>[0,"\xEEp"],"\xAC"=>[0,"\xEEr"],"\xAD"=>[0,"\xEEu"],"\xAE"=>[100,"\xEE"],"\xAF"=>[110,"\xEE"],"\xB0"=>[111,"\xEE"],"\xB1"=>[115,"\xEE"],"\xB2"=>[116,"\xEE"],"\xB3"=>[118,"\xEE"],"\xB4"=>[119,"\xEE"],"\xB5"=>[122,"\xEE"],"\xB6"=>[123,"\xEE"],"\xB7"=>[125,"\xEE"],"\xB8"=>[126,"\xEE"],"\xB9"=>[129,"\xEE"],"\xBA"=>[143,"\xEE"],"\xBB"=>[148,"\xEE"],"\xBC"=>[151,"\xEE"],"\xBD"=>[153,"\xEE"],"\xBE"=>[83,"\xEE"],"\xBF"=>[10,"\xEE"],"\xC0"=>[77,"\xF00"],"\xC1"=>[18,"\xF00"],"\xC2"=>[77,"\xF01"],"\xC3"=>[18,"\xF01"],"\xC4"=>[77,"\xF02"],"\xC5"=>[18,"\xF02"],"\xC6"=>[77,"\xF0a"],"\xC7"=>[18,"\xF0a"],"\xC8"=>[77,"\xF0c"],"\xC9"=>[18,"\xF0c"],"\xCA"=>[77,"\xF0e"],"\xCB"=>[18,"\xF0e"],"\xCC"=>[77,"\xF0i"],"\xCD"=>[18,"\xF0i"],"\xCE"=>[77,"\xF0o"],"\xCF"=>[18,"\xF0o"],"\xD0"=>[77,"\xF0s"],"\xD1"=>[18,"\xF0s"],"\xD2"=>[77,"\xF0t"],"\xD3"=>[18,"\xF0t"],"\xD4"=>[0,"\xF0\x20"],"\xD5"=>[0,"\xF0\x25"],"\xD6"=>[0,"\xF0-"],"\xD7"=>[0,"\xF0."],"\xD8"=>[0,"\xF0\x2F"],"\xD9"=>[0,"\xF03"],"\xDA"=>[0,"\xF04"],"\xDB"=>[0,"\xF05"],"\xDC"=>[0,"\xF06"],"\xDD"=>[0,"\xF07"],"\xDE"=>[0,"\xF08"],"\xDF"=>[0,"\xF09"],"\xE0"=>[0,"\xF0\x3D"],"\xE1"=>[0,"\xF0A"],"\xE2"=>[0,"\xF0_"],"\xE3"=>[0,"\xF0b"],"\xE4"=>[0,"\xF0d"],"\xE5"=>[0,"\xF0f"],"\xE6"=>[0,"\xF0g"],"\xE7"=>[0,"\xF0h"],"\xE8"=>[0,"\xF0l"],"\xE9"=>[0,"\xF0m"],"\xEA"=>[0,"\xF0n"],"\xEB"=>[0,"\xF0p"],"\xEC"=>[0,"\xF0r"],"\xED"=>[0,"\xF0u"],"\xEE"=>[100,"\xF0"],"\xEF"=>[110,"\xF0"],"\xF0"=>[111,"\xF0"],"\xF1"=>[115,"\xF0"],"\xF2"=>[116,"\xF0"],"\xF3"=>[118,"\xF0"],"\xF4"=>[119,"\xF0"],"\xF5"=>[122,"\xF0"],"\xF6"=>[123,"\xF0"],"\xF7"=>[125,"\xF0"],"\xF8"=>[126,"\xF0"],"\xF9"=>[129,"\xF0"],"\xFA"=>[143,"\xF0"],"\xFB"=>[148,"\xF0"],"\xFC"=>[151,"\xF0"],"\xFD"=>[153,"\xF0"],"\xFE"=>[83,"\xF0"],"\xFF"=>[10,"\xF0"],],["\x00"=>[94,"\xDA0"],"\x01"=>[76,"\xDA0"],"\x02"=>[104,"\xDA0"],"\x03"=>[16,"\xDA0"],"\x04"=>[94,"\xDA1"],"\x05"=>[76,"\xDA1"],"\x06"=>[104,"\xDA1"],"\x07"=>[16,"\xDA1"],"\x08"=>[94,"\xDA2"],"\x09"=>[76,"\xDA2"],"\x0A"=>[104,"\xDA2"],"\x0B"=>[16,"\xDA2"],"\x0C"=>[94,"\xDAa"],"\x0D"=>[76,"\xDAa"],"\x0E"=>[104,"\xDAa"],"\x0F"=>[16,"\xDAa"],"\x10"=>[94,"\xDAc"],"\x11"=>[76,"\xDAc"],"\x12"=>[104,"\xDAc"],"\x13"=>[16,"\xDAc"],"\x14"=>[94,"\xDAe"],"\x15"=>[76,"\xDAe"],"\x16"=>[104,"\xDAe"],"\x17"=>[16,"\xDAe"],"\x18"=>[94,"\xDAi"],"\x19"=>[76,"\xDAi"],"\x1A"=>[104,"\xDAi"],"\x1B"=>[16,"\xDAi"],"\x1C"=>[94,"\xDAo"],"\x1D"=>[76,"\xDAo"],"\x1E"=>[104,"\xDAo"],"\x1F"=>[16,"\xDAo"],"\x20"=>[94,"\xDAs"],"\x21"=>[76,"\xDAs"],"\x22"=>[104,"\xDAs"],"\x23"=>[16,"\xDAs"],"\x24"=>[94,"\xDAt"],"\x25"=>[76,"\xDAt"],"\x26"=>[104,"\xDAt"],"\x27"=>[16,"\xDAt"],"\x28"=>[77,"\xDA\x20"],"\x29"=>[18,"\xDA\x20"],"\x2A"=>[77,"\xDA\x25"],"\x2B"=>[18,"\xDA\x25"],"\x2C"=>[77,"\xDA-"],"-"=>[18,"\xDA-"],"."=>[77,"\xDA."],"\x2F"=>[18,"\xDA."],[77,"\xDA\x2F"],[18,"\xDA\x2F"],[77,"\xDA3"],[18,"\xDA3"],[77,"\xDA4"],[18,"\xDA4"],[77,"\xDA5"],[18,"\xDA5"],[77,"\xDA6"],[18,"\xDA6"],"\x3A"=>[77,"\xDA7"],"\x3B"=>[18,"\xDA7"],"\x3C"=>[77,"\xDA8"],"\x3D"=>[18,"\xDA8"],"\x3E"=>[77,"\xDA9"],"\x3F"=>[18,"\xDA9"],"\x40"=>[77,"\xDA\x3D"],"A"=>[18,"\xDA\x3D"],"B"=>[77,"\xDAA"],"C"=>[18,"\xDAA"],"D"=>[77,"\xDA_"],"E"=>[18,"\xDA_"],"F"=>[77,"\xDAb"],"G"=>[18,"\xDAb"],"H"=>[77,"\xDAd"],"I"=>[18,"\xDAd"],"J"=>[77,"\xDAf"],"K"=>[18,"\xDAf"],"L"=>[77,"\xDAg"],"M"=>[18,"\xDAg"],"N"=>[77,"\xDAh"],"O"=>[18,"\xDAh"],"P"=>[77,"\xDAl"],"Q"=>[18,"\xDAl"],"R"=>[77,"\xDAm"],"S"=>[18,"\xDAm"],"T"=>[77,"\xDAn"],"U"=>[18,"\xDAn"],"V"=>[77,"\xDAp"],"W"=>[18,"\xDAp"],"X"=>[77,"\xDAr"],"Y"=>[18,"\xDAr"],"Z"=>[77,"\xDAu"],"\x5B"=>[18,"\xDAu"],"\x5C"=>[0,"\xDA\x3A"],"\x5D"=>[0,"\xDAB"],"\x5E"=>[0,"\xDAC"],"_"=>[0,"\xDAD"],"\x60"=>[0,"\xDAE"],"a"=>[0,"\xDAF"],"b"=>[0,"\xDAG"],"c"=>[0,"\xDAH"],"d"=>[0,"\xDAI"],"e"=>[0,"\xDAJ"],"f"=>[0,"\xDAK"],"g"=>[0,"\xDAL"],"h"=>[0,"\xDAM"],"i"=>[0,"\xDAN"],"j"=>[0,"\xDAO"],"k"=>[0,"\xDAP"],"l"=>[0,"\xDAQ"],"m"=>[0,"\xDAR"],"n"=>[0,"\xDAS"],"o"=>[0,"\xDAT"],"p"=>[0,"\xDAU"],"q"=>[0,"\xDAV"],"r"=>[0,"\xDAW"],"s"=>[0,"\xDAY"],"t"=>[0,"\xDAj"],"u"=>[0,"\xDAk"],"v"=>[0,"\xDAq"],"w"=>[0,"\xDAv"],"x"=>[0,"\xDAw"],"y"=>[0,"\xDAx"],"z"=>[0,"\xDAy"],"\x7B"=>[0,"\xDAz"],"\x7C"=>[82,"\xDA"],"\x7D"=>[87,"\xDA"],"~"=>[130,"\xDA"],"\x7F"=>[9,"\xDA"],"\x80"=>[94,"\xDB0"],"\x81"=>[76,"\xDB0"],"\x82"=>[104,"\xDB0"],"\x83"=>[16,"\xDB0"],"\x84"=>[94,"\xDB1"],"\x85"=>[76,"\xDB1"],"\x86"=>[104,"\xDB1"],"\x87"=>[16,"\xDB1"],"\x88"=>[94,"\xDB2"],"\x89"=>[76,"\xDB2"],"\x8A"=>[104,"\xDB2"],"\x8B"=>[16,"\xDB2"],"\x8C"=>[94,"\xDBa"],"\x8D"=>[76,"\xDBa"],"\x8E"=>[104,"\xDBa"],"\x8F"=>[16,"\xDBa"],"\x90"=>[94,"\xDBc"],"\x91"=>[76,"\xDBc"],"\x92"=>[104,"\xDBc"],"\x93"=>[16,"\xDBc"],"\x94"=>[94,"\xDBe"],"\x95"=>[76,"\xDBe"],"\x96"=>[104,"\xDBe"],"\x97"=>[16,"\xDBe"],"\x98"=>[94,"\xDBi"],"\x99"=>[76,"\xDBi"],"\x9A"=>[104,"\xDBi"],"\x9B"=>[16,"\xDBi"],"\x9C"=>[94,"\xDBo"],"\x9D"=>[76,"\xDBo"],"\x9E"=>[104,"\xDBo"],"\x9F"=>[16,"\xDBo"],"\xA0"=>[94,"\xDBs"],"\xA1"=>[76,"\xDBs"],"\xA2"=>[104,"\xDBs"],"\xA3"=>[16,"\xDBs"],"\xA4"=>[94,"\xDBt"],"\xA5"=>[76,"\xDBt"],"\xA6"=>[104,"\xDBt"],"\xA7"=>[16,"\xDBt"],"\xA8"=>[77,"\xDB\x20"],"\xA9"=>[18,"\xDB\x20"],"\xAA"=>[77,"\xDB\x25"],"\xAB"=>[18,"\xDB\x25"],"\xAC"=>[77,"\xDB-"],"\xAD"=>[18,"\xDB-"],"\xAE"=>[77,"\xDB."],"\xAF"=>[18,"\xDB."],"\xB0"=>[77,"\xDB\x2F"],"\xB1"=>[18,"\xDB\x2F"],"\xB2"=>[77,"\xDB3"],"\xB3"=>[18,"\xDB3"],"\xB4"=>[77,"\xDB4"],"\xB5"=>[18,"\xDB4"],"\xB6"=>[77,"\xDB5"],"\xB7"=>[18,"\xDB5"],"\xB8"=>[77,"\xDB6"],"\xB9"=>[18,"\xDB6"],"\xBA"=>[77,"\xDB7"],"\xBB"=>[18,"\xDB7"],"\xBC"=>[77,"\xDB8"],"\xBD"=>[18,"\xDB8"],"\xBE"=>[77,"\xDB9"],"\xBF"=>[18,"\xDB9"],"\xC0"=>[77,"\xDB\x3D"],"\xC1"=>[18,"\xDB\x3D"],"\xC2"=>[77,"\xDBA"],"\xC3"=>[18,"\xDBA"],"\xC4"=>[77,"\xDB_"],"\xC5"=>[18,"\xDB_"],"\xC6"=>[77,"\xDBb"],"\xC7"=>[18,"\xDBb"],"\xC8"=>[77,"\xDBd"],"\xC9"=>[18,"\xDBd"],"\xCA"=>[77,"\xDBf"],"\xCB"=>[18,"\xDBf"],"\xCC"=>[77,"\xDBg"],"\xCD"=>[18,"\xDBg"],"\xCE"=>[77,"\xDBh"],"\xCF"=>[18,"\xDBh"],"\xD0"=>[77,"\xDBl"],"\xD1"=>[18,"\xDBl"],"\xD2"=>[77,"\xDBm"],"\xD3"=>[18,"\xDBm"],"\xD4"=>[77,"\xDBn"],"\xD5"=>[18,"\xDBn"],"\xD6"=>[77,"\xDBp"],"\xD7"=>[18,"\xDBp"],"\xD8"=>[77,"\xDBr"],"\xD9"=>[18,"\xDBr"],"\xDA"=>[77,"\xDBu"],"\xDB"=>[18,"\xDBu"],"\xDC"=>[0,"\xDB\x3A"],"\xDD"=>[0,"\xDBB"],"\xDE"=>[0,"\xDBC"],"\xDF"=>[0,"\xDBD"],"\xE0"=>[0,"\xDBE"],"\xE1"=>[0,"\xDBF"],"\xE2"=>[0,"\xDBG"],"\xE3"=>[0,"\xDBH"],"\xE4"=>[0,"\xDBI"],"\xE5"=>[0,"\xDBJ"],"\xE6"=>[0,"\xDBK"],"\xE7"=>[0,"\xDBL"],"\xE8"=>[0,"\xDBM"],"\xE9"=>[0,"\xDBN"],"\xEA"=>[0,"\xDBO"],"\xEB"=>[0,"\xDBP"],"\xEC"=>[0,"\xDBQ"],"\xED"=>[0,"\xDBR"],"\xEE"=>[0,"\xDBS"],"\xEF"=>[0,"\xDBT"],"\xF0"=>[0,"\xDBU"],"\xF1"=>[0,"\xDBV"],"\xF2"=>[0,"\xDBW"],"\xF3"=>[0,"\xDBY"],"\xF4"=>[0,"\xDBj"],"\xF5"=>[0,"\xDBk"],"\xF6"=>[0,"\xDBq"],"\xF7"=>[0,"\xDBv"],"\xF8"=>[0,"\xDBw"],"\xF9"=>[0,"\xDBx"],"\xFA"=>[0,"\xDBy"],"\xFB"=>[0,"\xDBz"],"\xFC"=>[82,"\xDB"],"\xFD"=>[87,"\xDB"],"\xFE"=>[130,"\xDB"],"\xFF"=>[9,"\xDB"],],["\x00"=>[94,"\xDE0"],"\x01"=>[76,"\xDE0"],"\x02"=>[104,"\xDE0"],"\x03"=>[16,"\xDE0"],"\x04"=>[94,"\xDE1"],"\x05"=>[76,"\xDE1"],"\x06"=>[104,"\xDE1"],"\x07"=>[16,"\xDE1"],"\x08"=>[94,"\xDE2"],"\x09"=>[76,"\xDE2"],"\x0A"=>[104,"\xDE2"],"\x0B"=>[16,"\xDE2"],"\x0C"=>[94,"\xDEa"],"\x0D"=>[76,"\xDEa"],"\x0E"=>[104,"\xDEa"],"\x0F"=>[16,"\xDEa"],"\x10"=>[94,"\xDEc"],"\x11"=>[76,"\xDEc"],"\x12"=>[104,"\xDEc"],"\x13"=>[16,"\xDEc"],"\x14"=>[94,"\xDEe"],"\x15"=>[76,"\xDEe"],"\x16"=>[104,"\xDEe"],"\x17"=>[16,"\xDEe"],"\x18"=>[94,"\xDEi"],"\x19"=>[76,"\xDEi"],"\x1A"=>[104,"\xDEi"],"\x1B"=>[16,"\xDEi"],"\x1C"=>[94,"\xDEo"],"\x1D"=>[76,"\xDEo"],"\x1E"=>[104,"\xDEo"],"\x1F"=>[16,"\xDEo"],"\x20"=>[94,"\xDEs"],"\x21"=>[76,"\xDEs"],"\x22"=>[104,"\xDEs"],"\x23"=>[16,"\xDEs"],"\x24"=>[94,"\xDEt"],"\x25"=>[76,"\xDEt"],"\x26"=>[104,"\xDEt"],"\x27"=>[16,"\xDEt"],"\x28"=>[77,"\xDE\x20"],"\x29"=>[18,"\xDE\x20"],"\x2A"=>[77,"\xDE\x25"],"\x2B"=>[18,"\xDE\x25"],"\x2C"=>[77,"\xDE-"],"-"=>[18,"\xDE-"],"."=>[77,"\xDE."],"\x2F"=>[18,"\xDE."],[77,"\xDE\x2F"],[18,"\xDE\x2F"],[77,"\xDE3"],[18,"\xDE3"],[77,"\xDE4"],[18,"\xDE4"],[77,"\xDE5"],[18,"\xDE5"],[77,"\xDE6"],[18,"\xDE6"],"\x3A"=>[77,"\xDE7"],"\x3B"=>[18,"\xDE7"],"\x3C"=>[77,"\xDE8"],"\x3D"=>[18,"\xDE8"],"\x3E"=>[77,"\xDE9"],"\x3F"=>[18,"\xDE9"],"\x40"=>[77,"\xDE\x3D"],"A"=>[18,"\xDE\x3D"],"B"=>[77,"\xDEA"],"C"=>[18,"\xDEA"],"D"=>[77,"\xDE_"],"E"=>[18,"\xDE_"],"F"=>[77,"\xDEb"],"G"=>[18,"\xDEb"],"H"=>[77,"\xDEd"],"I"=>[18,"\xDEd"],"J"=>[77,"\xDEf"],"K"=>[18,"\xDEf"],"L"=>[77,"\xDEg"],"M"=>[18,"\xDEg"],"N"=>[77,"\xDEh"],"O"=>[18,"\xDEh"],"P"=>[77,"\xDEl"],"Q"=>[18,"\xDEl"],"R"=>[77,"\xDEm"],"S"=>[18,"\xDEm"],"T"=>[77,"\xDEn"],"U"=>[18,"\xDEn"],"V"=>[77,"\xDEp"],"W"=>[18,"\xDEp"],"X"=>[77,"\xDEr"],"Y"=>[18,"\xDEr"],"Z"=>[77,"\xDEu"],"\x5B"=>[18,"\xDEu"],"\x5C"=>[0,"\xDE\x3A"],"\x5D"=>[0,"\xDEB"],"\x5E"=>[0,"\xDEC"],"_"=>[0,"\xDED"],"\x60"=>[0,"\xDEE"],"a"=>[0,"\xDEF"],"b"=>[0,"\xDEG"],"c"=>[0,"\xDEH"],"d"=>[0,"\xDEI"],"e"=>[0,"\xDEJ"],"f"=>[0,"\xDEK"],"g"=>[0,"\xDEL"],"h"=>[0,"\xDEM"],"i"=>[0,"\xDEN"],"j"=>[0,"\xDEO"],"k"=>[0,"\xDEP"],"l"=>[0,"\xDEQ"],"m"=>[0,"\xDER"],"n"=>[0,"\xDES"],"o"=>[0,"\xDET"],"p"=>[0,"\xDEU"],"q"=>[0,"\xDEV"],"r"=>[0,"\xDEW"],"s"=>[0,"\xDEY"],"t"=>[0,"\xDEj"],"u"=>[0,"\xDEk"],"v"=>[0,"\xDEq"],"w"=>[0,"\xDEv"],"x"=>[0,"\xDEw"],"y"=>[0,"\xDEx"],"z"=>[0,"\xDEy"],"\x7B"=>[0,"\xDEz"],"\x7C"=>[82,"\xDE"],"\x7D"=>[87,"\xDE"],"~"=>[130,"\xDE"],"\x7F"=>[9,"\xDE"],"\x80"=>[94,"\xDF0"],"\x81"=>[76,"\xDF0"],"\x82"=>[104,"\xDF0"],"\x83"=>[16,"\xDF0"],"\x84"=>[94,"\xDF1"],"\x85"=>[76,"\xDF1"],"\x86"=>[104,"\xDF1"],"\x87"=>[16,"\xDF1"],"\x88"=>[94,"\xDF2"],"\x89"=>[76,"\xDF2"],"\x8A"=>[104,"\xDF2"],"\x8B"=>[16,"\xDF2"],"\x8C"=>[94,"\xDFa"],"\x8D"=>[76,"\xDFa"],"\x8E"=>[104,"\xDFa"],"\x8F"=>[16,"\xDFa"],"\x90"=>[94,"\xDFc"],"\x91"=>[76,"\xDFc"],"\x92"=>[104,"\xDFc"],"\x93"=>[16,"\xDFc"],"\x94"=>[94,"\xDFe"],"\x95"=>[76,"\xDFe"],"\x96"=>[104,"\xDFe"],"\x97"=>[16,"\xDFe"],"\x98"=>[94,"\xDFi"],"\x99"=>[76,"\xDFi"],"\x9A"=>[104,"\xDFi"],"\x9B"=>[16,"\xDFi"],"\x9C"=>[94,"\xDFo"],"\x9D"=>[76,"\xDFo"],"\x9E"=>[104,"\xDFo"],"\x9F"=>[16,"\xDFo"],"\xA0"=>[94,"\xDFs"],"\xA1"=>[76,"\xDFs"],"\xA2"=>[104,"\xDFs"],"\xA3"=>[16,"\xDFs"],"\xA4"=>[94,"\xDFt"],"\xA5"=>[76,"\xDFt"],"\xA6"=>[104,"\xDFt"],"\xA7"=>[16,"\xDFt"],"\xA8"=>[77,"\xDF\x20"],"\xA9"=>[18,"\xDF\x20"],"\xAA"=>[77,"\xDF\x25"],"\xAB"=>[18,"\xDF\x25"],"\xAC"=>[77,"\xDF-"],"\xAD"=>[18,"\xDF-"],"\xAE"=>[77,"\xDF."],"\xAF"=>[18,"\xDF."],"\xB0"=>[77,"\xDF\x2F"],"\xB1"=>[18,"\xDF\x2F"],"\xB2"=>[77,"\xDF3"],"\xB3"=>[18,"\xDF3"],"\xB4"=>[77,"\xDF4"],"\xB5"=>[18,"\xDF4"],"\xB6"=>[77,"\xDF5"],"\xB7"=>[18,"\xDF5"],"\xB8"=>[77,"\xDF6"],"\xB9"=>[18,"\xDF6"],"\xBA"=>[77,"\xDF7"],"\xBB"=>[18,"\xDF7"],"\xBC"=>[77,"\xDF8"],"\xBD"=>[18,"\xDF8"],"\xBE"=>[77,"\xDF9"],"\xBF"=>[18,"\xDF9"],"\xC0"=>[77,"\xDF\x3D"],"\xC1"=>[18,"\xDF\x3D"],"\xC2"=>[77,"\xDFA"],"\xC3"=>[18,"\xDFA"],"\xC4"=>[77,"\xDF_"],"\xC5"=>[18,"\xDF_"],"\xC6"=>[77,"\xDFb"],"\xC7"=>[18,"\xDFb"],"\xC8"=>[77,"\xDFd"],"\xC9"=>[18,"\xDFd"],"\xCA"=>[77,"\xDFf"],"\xCB"=>[18,"\xDFf"],"\xCC"=>[77,"\xDFg"],"\xCD"=>[18,"\xDFg"],"\xCE"=>[77,"\xDFh"],"\xCF"=>[18,"\xDFh"],"\xD0"=>[77,"\xDFl"],"\xD1"=>[18,"\xDFl"],"\xD2"=>[77,"\xDFm"],"\xD3"=>[18,"\xDFm"],"\xD4"=>[77,"\xDFn"],"\xD5"=>[18,"\xDFn"],"\xD6"=>[77,"\xDFp"],"\xD7"=>[18,"\xDFp"],"\xD8"=>[77,"\xDFr"],"\xD9"=>[18,"\xDFr"],"\xDA"=>[77,"\xDFu"],"\xDB"=>[18,"\xDFu"],"\xDC"=>[0,"\xDF\x3A"],"\xDD"=>[0,"\xDFB"],"\xDE"=>[0,"\xDFC"],"\xDF"=>[0,"\xDFD"],"\xE0"=>[0,"\xDFE"],"\xE1"=>[0,"\xDFF"],"\xE2"=>[0,"\xDFG"],"\xE3"=>[0,"\xDFH"],"\xE4"=>[0,"\xDFI"],"\xE5"=>[0,"\xDFJ"],"\xE6"=>[0,"\xDFK"],"\xE7"=>[0,"\xDFL"],"\xE8"=>[0,"\xDFM"],"\xE9"=>[0,"\xDFN"],"\xEA"=>[0,"\xDFO"],"\xEB"=>[0,"\xDFP"],"\xEC"=>[0,"\xDFQ"],"\xED"=>[0,"\xDFR"],"\xEE"=>[0,"\xDFS"],"\xEF"=>[0,"\xDFT"],"\xF0"=>[0,"\xDFU"],"\xF1"=>[0,"\xDFV"],"\xF2"=>[0,"\xDFW"],"\xF3"=>[0,"\xDFY"],"\xF4"=>[0,"\xDFj"],"\xF5"=>[0,"\xDFk"],"\xF6"=>[0,"\xDFq"],"\xF7"=>[0,"\xDFv"],"\xF8"=>[0,"\xDFw"],"\xF9"=>[0,"\xDFx"],"\xFA"=>[0,"\xDFy"],"\xFB"=>[0,"\xDFz"],"\xFC"=>[82,"\xDF"],"\xFD"=>[87,"\xDF"],"\xFE"=>[130,"\xDF"],"\xFF"=>[9,"\xDF"],],["\x00"=>[77,"\xDE0"],"\x01"=>[18,"\xDE0"],"\x02"=>[77,"\xDE1"],"\x03"=>[18,"\xDE1"],"\x04"=>[77,"\xDE2"],"\x05"=>[18,"\xDE2"],"\x06"=>[77,"\xDEa"],"\x07"=>[18,"\xDEa"],"\x08"=>[77,"\xDEc"],"\x09"=>[18,"\xDEc"],"\x0A"=>[77,"\xDEe"],"\x0B"=>[18,"\xDEe"],"\x0C"=>[77,"\xDEi"],"\x0D"=>[18,"\xDEi"],"\x0E"=>[77,"\xDEo"],"\x0F"=>[18,"\xDEo"],"\x10"=>[77,"\xDEs"],"\x11"=>[18,"\xDEs"],"\x12"=>[77,"\xDEt"],"\x13"=>[18,"\xDEt"],"\x14"=>[0,"\xDE\x20"],"\x15"=>[0,"\xDE\x25"],"\x16"=>[0,"\xDE-"],"\x17"=>[0,"\xDE."],"\x18"=>[0,"\xDE\x2F"],"\x19"=>[0,"\xDE3"],"\x1A"=>[0,"\xDE4"],"\x1B"=>[0,"\xDE5"],"\x1C"=>[0,"\xDE6"],"\x1D"=>[0,"\xDE7"],"\x1E"=>[0,"\xDE8"],"\x1F"=>[0,"\xDE9"],"\x20"=>[0,"\xDE\x3D"],"\x21"=>[0,"\xDEA"],"\x22"=>[0,"\xDE_"],"\x23"=>[0,"\xDEb"],"\x24"=>[0,"\xDEd"],"\x25"=>[0,"\xDEf"],"\x26"=>[0,"\xDEg"],"\x27"=>[0,"\xDEh"],"\x28"=>[0,"\xDEl"],"\x29"=>[0,"\xDEm"],"\x2A"=>[0,"\xDEn"],"\x2B"=>[0,"\xDEp"],"\x2C"=>[0,"\xDEr"],"-"=>[0,"\xDEu"],"."=>[100,"\xDE"],"\x2F"=>[110,"\xDE"],[111,"\xDE"],[115,"\xDE"],[116,"\xDE"],[118,"\xDE"],[119,"\xDE"],[122,"\xDE"],[123,"\xDE"],[125,"\xDE"],[126,"\xDE"],[129,"\xDE"],"\x3A"=>[143,"\xDE"],"\x3B"=>[148,"\xDE"],"\x3C"=>[151,"\xDE"],"\x3D"=>[153,"\xDE"],"\x3E"=>[83,"\xDE"],"\x3F"=>[10,"\xDE"],"\x40"=>[77,"\xDF0"],"A"=>[18,"\xDF0"],"B"=>[77,"\xDF1"],"C"=>[18,"\xDF1"],"D"=>[77,"\xDF2"],"E"=>[18,"\xDF2"],"F"=>[77,"\xDFa"],"G"=>[18,"\xDFa"],"H"=>[77,"\xDFc"],"I"=>[18,"\xDFc"],"J"=>[77,"\xDFe"],"K"=>[18,"\xDFe"],"L"=>[77,"\xDFi"],"M"=>[18,"\xDFi"],"N"=>[77,"\xDFo"],"O"=>[18,"\xDFo"],"P"=>[77,"\xDFs"],"Q"=>[18,"\xDFs"],"R"=>[77,"\xDFt"],"S"=>[18,"\xDFt"],"T"=>[0,"\xDF\x20"],"U"=>[0,"\xDF\x25"],"V"=>[0,"\xDF-"],"W"=>[0,"\xDF."],"X"=>[0,"\xDF\x2F"],"Y"=>[0,"\xDF3"],"Z"=>[0,"\xDF4"],"\x5B"=>[0,"\xDF5"],"\x5C"=>[0,"\xDF6"],"\x5D"=>[0,"\xDF7"],"\x5E"=>[0,"\xDF8"],"_"=>[0,"\xDF9"],"\x60"=>[0,"\xDF\x3D"],"a"=>[0,"\xDFA"],"b"=>[0,"\xDF_"],"c"=>[0,"\xDFb"],"d"=>[0,"\xDFd"],"e"=>[0,"\xDFf"],"f"=>[0,"\xDFg"],"g"=>[0,"\xDFh"],"h"=>[0,"\xDFl"],"i"=>[0,"\xDFm"],"j"=>[0,"\xDFn"],"k"=>[0,"\xDFp"],"l"=>[0,"\xDFr"],"m"=>[0,"\xDFu"],"n"=>[100,"\xDF"],"o"=>[110,"\xDF"],"p"=>[111,"\xDF"],"q"=>[115,"\xDF"],"r"=>[116,"\xDF"],"s"=>[118,"\xDF"],"t"=>[119,"\xDF"],"u"=>[122,"\xDF"],"v"=>[123,"\xDF"],"w"=>[125,"\xDF"],"x"=>[126,"\xDF"],"y"=>[129,"\xDF"],"z"=>[143,"\xDF"],"\x7B"=>[148,"\xDF"],"\x7C"=>[151,"\xDF"],"\x7D"=>[153,"\xDF"],"~"=>[83,"\xDF"],"\x7F"=>[10,"\xDF"],"\x80"=>[77,"\xF10"],"\x81"=>[18,"\xF10"],"\x82"=>[77,"\xF11"],"\x83"=>[18,"\xF11"],"\x84"=>[77,"\xF12"],"\x85"=>[18,"\xF12"],"\x86"=>[77,"\xF1a"],"\x87"=>[18,"\xF1a"],"\x88"=>[77,"\xF1c"],"\x89"=>[18,"\xF1c"],"\x8A"=>[77,"\xF1e"],"\x8B"=>[18,"\xF1e"],"\x8C"=>[77,"\xF1i"],"\x8D"=>[18,"\xF1i"],"\x8E"=>[77,"\xF1o"],"\x8F"=>[18,"\xF1o"],"\x90"=>[77,"\xF1s"],"\x91"=>[18,"\xF1s"],"\x92"=>[77,"\xF1t"],"\x93"=>[18,"\xF1t"],"\x94"=>[0,"\xF1\x20"],"\x95"=>[0,"\xF1\x25"],"\x96"=>[0,"\xF1-"],"\x97"=>[0,"\xF1."],"\x98"=>[0,"\xF1\x2F"],"\x99"=>[0,"\xF13"],"\x9A"=>[0,"\xF14"],"\x9B"=>[0,"\xF15"],"\x9C"=>[0,"\xF16"],"\x9D"=>[0,"\xF17"],"\x9E"=>[0,"\xF18"],"\x9F"=>[0,"\xF19"],"\xA0"=>[0,"\xF1\x3D"],"\xA1"=>[0,"\xF1A"],"\xA2"=>[0,"\xF1_"],"\xA3"=>[0,"\xF1b"],"\xA4"=>[0,"\xF1d"],"\xA5"=>[0,"\xF1f"],"\xA6"=>[0,"\xF1g"],"\xA7"=>[0,"\xF1h"],"\xA8"=>[0,"\xF1l"],"\xA9"=>[0,"\xF1m"],"\xAA"=>[0,"\xF1n"],"\xAB"=>[0,"\xF1p"],"\xAC"=>[0,"\xF1r"],"\xAD"=>[0,"\xF1u"],"\xAE"=>[100,"\xF1"],"\xAF"=>[110,"\xF1"],"\xB0"=>[111,"\xF1"],"\xB1"=>[115,"\xF1"],"\xB2"=>[116,"\xF1"],"\xB3"=>[118,"\xF1"],"\xB4"=>[119,"\xF1"],"\xB5"=>[122,"\xF1"],"\xB6"=>[123,"\xF1"],"\xB7"=>[125,"\xF1"],"\xB8"=>[126,"\xF1"],"\xB9"=>[129,"\xF1"],"\xBA"=>[143,"\xF1"],"\xBB"=>[148,"\xF1"],"\xBC"=>[151,"\xF1"],"\xBD"=>[153,"\xF1"],"\xBE"=>[83,"\xF1"],"\xBF"=>[10,"\xF1"],"\xC0"=>[77,"\xF40"],"\xC1"=>[18,"\xF40"],"\xC2"=>[77,"\xF41"],"\xC3"=>[18,"\xF41"],"\xC4"=>[77,"\xF42"],"\xC5"=>[18,"\xF42"],"\xC6"=>[77,"\xF4a"],"\xC7"=>[18,"\xF4a"],"\xC8"=>[77,"\xF4c"],"\xC9"=>[18,"\xF4c"],"\xCA"=>[77,"\xF4e"],"\xCB"=>[18,"\xF4e"],"\xCC"=>[77,"\xF4i"],"\xCD"=>[18,"\xF4i"],"\xCE"=>[77,"\xF4o"],"\xCF"=>[18,"\xF4o"],"\xD0"=>[77,"\xF4s"],"\xD1"=>[18,"\xF4s"],"\xD2"=>[77,"\xF4t"],"\xD3"=>[18,"\xF4t"],"\xD4"=>[0,"\xF4\x20"],"\xD5"=>[0,"\xF4\x25"],"\xD6"=>[0,"\xF4-"],"\xD7"=>[0,"\xF4."],"\xD8"=>[0,"\xF4\x2F"],"\xD9"=>[0,"\xF43"],"\xDA"=>[0,"\xF44"],"\xDB"=>[0,"\xF45"],"\xDC"=>[0,"\xF46"],"\xDD"=>[0,"\xF47"],"\xDE"=>[0,"\xF48"],"\xDF"=>[0,"\xF49"],"\xE0"=>[0,"\xF4\x3D"],"\xE1"=>[0,"\xF4A"],"\xE2"=>[0,"\xF4_"],"\xE3"=>[0,"\xF4b"],"\xE4"=>[0,"\xF4d"],"\xE5"=>[0,"\xF4f"],"\xE6"=>[0,"\xF4g"],"\xE7"=>[0,"\xF4h"],"\xE8"=>[0,"\xF4l"],"\xE9"=>[0,"\xF4m"],"\xEA"=>[0,"\xF4n"],"\xEB"=>[0,"\xF4p"],"\xEC"=>[0,"\xF4r"],"\xED"=>[0,"\xF4u"],"\xEE"=>[100,"\xF4"],"\xEF"=>[110,"\xF4"],"\xF0"=>[111,"\xF4"],"\xF1"=>[115,"\xF4"],"\xF2"=>[116,"\xF4"],"\xF3"=>[118,"\xF4"],"\xF4"=>[119,"\xF4"],"\xF5"=>[122,"\xF4"],"\xF6"=>[123,"\xF4"],"\xF7"=>[125,"\xF4"],"\xF8"=>[126,"\xF4"],"\xF9"=>[129,"\xF4"],"\xFA"=>[143,"\xF4"],"\xFB"=>[148,"\xF4"],"\xFC"=>[151,"\xF4"],"\xFD"=>[153,"\xF4"],"\xFE"=>[83,"\xF4"],"\xFF"=>[10,"\xF4"],],["\x00"=>[94,"\xE00"],"\x01"=>[76,"\xE00"],"\x02"=>[104,"\xE00"],"\x03"=>[16,"\xE00"],"\x04"=>[94,"\xE01"],"\x05"=>[76,"\xE01"],"\x06"=>[104,"\xE01"],"\x07"=>[16,"\xE01"],"\x08"=>[94,"\xE02"],"\x09"=>[76,"\xE02"],"\x0A"=>[104,"\xE02"],"\x0B"=>[16,"\xE02"],"\x0C"=>[94,"\xE0a"],"\x0D"=>[76,"\xE0a"],"\x0E"=>[104,"\xE0a"],"\x0F"=>[16,"\xE0a"],"\x10"=>[94,"\xE0c"],"\x11"=>[76,"\xE0c"],"\x12"=>[104,"\xE0c"],"\x13"=>[16,"\xE0c"],"\x14"=>[94,"\xE0e"],"\x15"=>[76,"\xE0e"],"\x16"=>[104,"\xE0e"],"\x17"=>[16,"\xE0e"],"\x18"=>[94,"\xE0i"],"\x19"=>[76,"\xE0i"],"\x1A"=>[104,"\xE0i"],"\x1B"=>[16,"\xE0i"],"\x1C"=>[94,"\xE0o"],"\x1D"=>[76,"\xE0o"],"\x1E"=>[104,"\xE0o"],"\x1F"=>[16,"\xE0o"],"\x20"=>[94,"\xE0s"],"\x21"=>[76,"\xE0s"],"\x22"=>[104,"\xE0s"],"\x23"=>[16,"\xE0s"],"\x24"=>[94,"\xE0t"],"\x25"=>[76,"\xE0t"],"\x26"=>[104,"\xE0t"],"\x27"=>[16,"\xE0t"],"\x28"=>[77,"\xE0\x20"],"\x29"=>[18,"\xE0\x20"],"\x2A"=>[77,"\xE0\x25"],"\x2B"=>[18,"\xE0\x25"],"\x2C"=>[77,"\xE0-"],"-"=>[18,"\xE0-"],"."=>[77,"\xE0."],"\x2F"=>[18,"\xE0."],[77,"\xE0\x2F"],[18,"\xE0\x2F"],[77,"\xE03"],[18,"\xE03"],[77,"\xE04"],[18,"\xE04"],[77,"\xE05"],[18,"\xE05"],[77,"\xE06"],[18,"\xE06"],"\x3A"=>[77,"\xE07"],"\x3B"=>[18,"\xE07"],"\x3C"=>[77,"\xE08"],"\x3D"=>[18,"\xE08"],"\x3E"=>[77,"\xE09"],"\x3F"=>[18,"\xE09"],"\x40"=>[77,"\xE0\x3D"],"A"=>[18,"\xE0\x3D"],"B"=>[77,"\xE0A"],"C"=>[18,"\xE0A"],"D"=>[77,"\xE0_"],"E"=>[18,"\xE0_"],"F"=>[77,"\xE0b"],"G"=>[18,"\xE0b"],"H"=>[77,"\xE0d"],"I"=>[18,"\xE0d"],"J"=>[77,"\xE0f"],"K"=>[18,"\xE0f"],"L"=>[77,"\xE0g"],"M"=>[18,"\xE0g"],"N"=>[77,"\xE0h"],"O"=>[18,"\xE0h"],"P"=>[77,"\xE0l"],"Q"=>[18,"\xE0l"],"R"=>[77,"\xE0m"],"S"=>[18,"\xE0m"],"T"=>[77,"\xE0n"],"U"=>[18,"\xE0n"],"V"=>[77,"\xE0p"],"W"=>[18,"\xE0p"],"X"=>[77,"\xE0r"],"Y"=>[18,"\xE0r"],"Z"=>[77,"\xE0u"],"\x5B"=>[18,"\xE0u"],"\x5C"=>[0,"\xE0\x3A"],"\x5D"=>[0,"\xE0B"],"\x5E"=>[0,"\xE0C"],"_"=>[0,"\xE0D"],"\x60"=>[0,"\xE0E"],"a"=>[0,"\xE0F"],"b"=>[0,"\xE0G"],"c"=>[0,"\xE0H"],"d"=>[0,"\xE0I"],"e"=>[0,"\xE0J"],"f"=>[0,"\xE0K"],"g"=>[0,"\xE0L"],"h"=>[0,"\xE0M"],"i"=>[0,"\xE0N"],"j"=>[0,"\xE0O"],"k"=>[0,"\xE0P"],"l"=>[0,"\xE0Q"],"m"=>[0,"\xE0R"],"n"=>[0,"\xE0S"],"o"=>[0,"\xE0T"],"p"=>[0,"\xE0U"],"q"=>[0,"\xE0V"],"r"=>[0,"\xE0W"],"s"=>[0,"\xE0Y"],"t"=>[0,"\xE0j"],"u"=>[0,"\xE0k"],"v"=>[0,"\xE0q"],"w"=>[0,"\xE0v"],"x"=>[0,"\xE0w"],"y"=>[0,"\xE0x"],"z"=>[0,"\xE0y"],"\x7B"=>[0,"\xE0z"],"\x7C"=>[82,"\xE0"],"\x7D"=>[87,"\xE0"],"~"=>[130,"\xE0"],"\x7F"=>[9,"\xE0"],"\x80"=>[94,"\xE20"],"\x81"=>[76,"\xE20"],"\x82"=>[104,"\xE20"],"\x83"=>[16,"\xE20"],"\x84"=>[94,"\xE21"],"\x85"=>[76,"\xE21"],"\x86"=>[104,"\xE21"],"\x87"=>[16,"\xE21"],"\x88"=>[94,"\xE22"],"\x89"=>[76,"\xE22"],"\x8A"=>[104,"\xE22"],"\x8B"=>[16,"\xE22"],"\x8C"=>[94,"\xE2a"],"\x8D"=>[76,"\xE2a"],"\x8E"=>[104,"\xE2a"],"\x8F"=>[16,"\xE2a"],"\x90"=>[94,"\xE2c"],"\x91"=>[76,"\xE2c"],"\x92"=>[104,"\xE2c"],"\x93"=>[16,"\xE2c"],"\x94"=>[94,"\xE2e"],"\x95"=>[76,"\xE2e"],"\x96"=>[104,"\xE2e"],"\x97"=>[16,"\xE2e"],"\x98"=>[94,"\xE2i"],"\x99"=>[76,"\xE2i"],"\x9A"=>[104,"\xE2i"],"\x9B"=>[16,"\xE2i"],"\x9C"=>[94,"\xE2o"],"\x9D"=>[76,"\xE2o"],"\x9E"=>[104,"\xE2o"],"\x9F"=>[16,"\xE2o"],"\xA0"=>[94,"\xE2s"],"\xA1"=>[76,"\xE2s"],"\xA2"=>[104,"\xE2s"],"\xA3"=>[16,"\xE2s"],"\xA4"=>[94,"\xE2t"],"\xA5"=>[76,"\xE2t"],"\xA6"=>[104,"\xE2t"],"\xA7"=>[16,"\xE2t"],"\xA8"=>[77,"\xE2\x20"],"\xA9"=>[18,"\xE2\x20"],"\xAA"=>[77,"\xE2\x25"],"\xAB"=>[18,"\xE2\x25"],"\xAC"=>[77,"\xE2-"],"\xAD"=>[18,"\xE2-"],"\xAE"=>[77,"\xE2."],"\xAF"=>[18,"\xE2."],"\xB0"=>[77,"\xE2\x2F"],"\xB1"=>[18,"\xE2\x2F"],"\xB2"=>[77,"\xE23"],"\xB3"=>[18,"\xE23"],"\xB4"=>[77,"\xE24"],"\xB5"=>[18,"\xE24"],"\xB6"=>[77,"\xE25"],"\xB7"=>[18,"\xE25"],"\xB8"=>[77,"\xE26"],"\xB9"=>[18,"\xE26"],"\xBA"=>[77,"\xE27"],"\xBB"=>[18,"\xE27"],"\xBC"=>[77,"\xE28"],"\xBD"=>[18,"\xE28"],"\xBE"=>[77,"\xE29"],"\xBF"=>[18,"\xE29"],"\xC0"=>[77,"\xE2\x3D"],"\xC1"=>[18,"\xE2\x3D"],"\xC2"=>[77,"\xE2A"],"\xC3"=>[18,"\xE2A"],"\xC4"=>[77,"\xE2_"],"\xC5"=>[18,"\xE2_"],"\xC6"=>[77,"\xE2b"],"\xC7"=>[18,"\xE2b"],"\xC8"=>[77,"\xE2d"],"\xC9"=>[18,"\xE2d"],"\xCA"=>[77,"\xE2f"],"\xCB"=>[18,"\xE2f"],"\xCC"=>[77,"\xE2g"],"\xCD"=>[18,"\xE2g"],"\xCE"=>[77,"\xE2h"],"\xCF"=>[18,"\xE2h"],"\xD0"=>[77,"\xE2l"],"\xD1"=>[18,"\xE2l"],"\xD2"=>[77,"\xE2m"],"\xD3"=>[18,"\xE2m"],"\xD4"=>[77,"\xE2n"],"\xD5"=>[18,"\xE2n"],"\xD6"=>[77,"\xE2p"],"\xD7"=>[18,"\xE2p"],"\xD8"=>[77,"\xE2r"],"\xD9"=>[18,"\xE2r"],"\xDA"=>[77,"\xE2u"],"\xDB"=>[18,"\xE2u"],"\xDC"=>[0,"\xE2\x3A"],"\xDD"=>[0,"\xE2B"],"\xDE"=>[0,"\xE2C"],"\xDF"=>[0,"\xE2D"],"\xE0"=>[0,"\xE2E"],"\xE1"=>[0,"\xE2F"],"\xE2"=>[0,"\xE2G"],"\xE3"=>[0,"\xE2H"],"\xE4"=>[0,"\xE2I"],"\xE5"=>[0,"\xE2J"],"\xE6"=>[0,"\xE2K"],"\xE7"=>[0,"\xE2L"],"\xE8"=>[0,"\xE2M"],"\xE9"=>[0,"\xE2N"],"\xEA"=>[0,"\xE2O"],"\xEB"=>[0,"\xE2P"],"\xEC"=>[0,"\xE2Q"],"\xED"=>[0,"\xE2R"],"\xEE"=>[0,"\xE2S"],"\xEF"=>[0,"\xE2T"],"\xF0"=>[0,"\xE2U"],"\xF1"=>[0,"\xE2V"],"\xF2"=>[0,"\xE2W"],"\xF3"=>[0,"\xE2Y"],"\xF4"=>[0,"\xE2j"],"\xF5"=>[0,"\xE2k"],"\xF6"=>[0,"\xE2q"],"\xF7"=>[0,"\xE2v"],"\xF8"=>[0,"\xE2w"],"\xF9"=>[0,"\xE2x"],"\xFA"=>[0,"\xE2y"],"\xFB"=>[0,"\xE2z"],"\xFC"=>[82,"\xE2"],"\xFD"=>[87,"\xE2"],"\xFE"=>[130,"\xE2"],"\xFF"=>[9,"\xE2"],],["\x00"=>[94,"\xE30"],"\x01"=>[76,"\xE30"],"\x02"=>[104,"\xE30"],"\x03"=>[16,"\xE30"],"\x04"=>[94,"\xE31"],"\x05"=>[76,"\xE31"],"\x06"=>[104,"\xE31"],"\x07"=>[16,"\xE31"],"\x08"=>[94,"\xE32"],"\x09"=>[76,"\xE32"],"\x0A"=>[104,"\xE32"],"\x0B"=>[16,"\xE32"],"\x0C"=>[94,"\xE3a"],"\x0D"=>[76,"\xE3a"],"\x0E"=>[104,"\xE3a"],"\x0F"=>[16,"\xE3a"],"\x10"=>[94,"\xE3c"],"\x11"=>[76,"\xE3c"],"\x12"=>[104,"\xE3c"],"\x13"=>[16,"\xE3c"],"\x14"=>[94,"\xE3e"],"\x15"=>[76,"\xE3e"],"\x16"=>[104,"\xE3e"],"\x17"=>[16,"\xE3e"],"\x18"=>[94,"\xE3i"],"\x19"=>[76,"\xE3i"],"\x1A"=>[104,"\xE3i"],"\x1B"=>[16,"\xE3i"],"\x1C"=>[94,"\xE3o"],"\x1D"=>[76,"\xE3o"],"\x1E"=>[104,"\xE3o"],"\x1F"=>[16,"\xE3o"],"\x20"=>[94,"\xE3s"],"\x21"=>[76,"\xE3s"],"\x22"=>[104,"\xE3s"],"\x23"=>[16,"\xE3s"],"\x24"=>[94,"\xE3t"],"\x25"=>[76,"\xE3t"],"\x26"=>[104,"\xE3t"],"\x27"=>[16,"\xE3t"],"\x28"=>[77,"\xE3\x20"],"\x29"=>[18,"\xE3\x20"],"\x2A"=>[77,"\xE3\x25"],"\x2B"=>[18,"\xE3\x25"],"\x2C"=>[77,"\xE3-"],"-"=>[18,"\xE3-"],"."=>[77,"\xE3."],"\x2F"=>[18,"\xE3."],[77,"\xE3\x2F"],[18,"\xE3\x2F"],[77,"\xE33"],[18,"\xE33"],[77,"\xE34"],[18,"\xE34"],[77,"\xE35"],[18,"\xE35"],[77,"\xE36"],[18,"\xE36"],"\x3A"=>[77,"\xE37"],"\x3B"=>[18,"\xE37"],"\x3C"=>[77,"\xE38"],"\x3D"=>[18,"\xE38"],"\x3E"=>[77,"\xE39"],"\x3F"=>[18,"\xE39"],"\x40"=>[77,"\xE3\x3D"],"A"=>[18,"\xE3\x3D"],"B"=>[77,"\xE3A"],"C"=>[18,"\xE3A"],"D"=>[77,"\xE3_"],"E"=>[18,"\xE3_"],"F"=>[77,"\xE3b"],"G"=>[18,"\xE3b"],"H"=>[77,"\xE3d"],"I"=>[18,"\xE3d"],"J"=>[77,"\xE3f"],"K"=>[18,"\xE3f"],"L"=>[77,"\xE3g"],"M"=>[18,"\xE3g"],"N"=>[77,"\xE3h"],"O"=>[18,"\xE3h"],"P"=>[77,"\xE3l"],"Q"=>[18,"\xE3l"],"R"=>[77,"\xE3m"],"S"=>[18,"\xE3m"],"T"=>[77,"\xE3n"],"U"=>[18,"\xE3n"],"V"=>[77,"\xE3p"],"W"=>[18,"\xE3p"],"X"=>[77,"\xE3r"],"Y"=>[18,"\xE3r"],"Z"=>[77,"\xE3u"],"\x5B"=>[18,"\xE3u"],"\x5C"=>[0,"\xE3\x3A"],"\x5D"=>[0,"\xE3B"],"\x5E"=>[0,"\xE3C"],"_"=>[0,"\xE3D"],"\x60"=>[0,"\xE3E"],"a"=>[0,"\xE3F"],"b"=>[0,"\xE3G"],"c"=>[0,"\xE3H"],"d"=>[0,"\xE3I"],"e"=>[0,"\xE3J"],"f"=>[0,"\xE3K"],"g"=>[0,"\xE3L"],"h"=>[0,"\xE3M"],"i"=>[0,"\xE3N"],"j"=>[0,"\xE3O"],"k"=>[0,"\xE3P"],"l"=>[0,"\xE3Q"],"m"=>[0,"\xE3R"],"n"=>[0,"\xE3S"],"o"=>[0,"\xE3T"],"p"=>[0,"\xE3U"],"q"=>[0,"\xE3V"],"r"=>[0,"\xE3W"],"s"=>[0,"\xE3Y"],"t"=>[0,"\xE3j"],"u"=>[0,"\xE3k"],"v"=>[0,"\xE3q"],"w"=>[0,"\xE3v"],"x"=>[0,"\xE3w"],"y"=>[0,"\xE3x"],"z"=>[0,"\xE3y"],"\x7B"=>[0,"\xE3z"],"\x7C"=>[82,"\xE3"],"\x7D"=>[87,"\xE3"],"~"=>[130,"\xE3"],"\x7F"=>[9,"\xE3"],"\x80"=>[94,"\xE50"],"\x81"=>[76,"\xE50"],"\x82"=>[104,"\xE50"],"\x83"=>[16,"\xE50"],"\x84"=>[94,"\xE51"],"\x85"=>[76,"\xE51"],"\x86"=>[104,"\xE51"],"\x87"=>[16,"\xE51"],"\x88"=>[94,"\xE52"],"\x89"=>[76,"\xE52"],"\x8A"=>[104,"\xE52"],"\x8B"=>[16,"\xE52"],"\x8C"=>[94,"\xE5a"],"\x8D"=>[76,"\xE5a"],"\x8E"=>[104,"\xE5a"],"\x8F"=>[16,"\xE5a"],"\x90"=>[94,"\xE5c"],"\x91"=>[76,"\xE5c"],"\x92"=>[104,"\xE5c"],"\x93"=>[16,"\xE5c"],"\x94"=>[94,"\xE5e"],"\x95"=>[76,"\xE5e"],"\x96"=>[104,"\xE5e"],"\x97"=>[16,"\xE5e"],"\x98"=>[94,"\xE5i"],"\x99"=>[76,"\xE5i"],"\x9A"=>[104,"\xE5i"],"\x9B"=>[16,"\xE5i"],"\x9C"=>[94,"\xE5o"],"\x9D"=>[76,"\xE5o"],"\x9E"=>[104,"\xE5o"],"\x9F"=>[16,"\xE5o"],"\xA0"=>[94,"\xE5s"],"\xA1"=>[76,"\xE5s"],"\xA2"=>[104,"\xE5s"],"\xA3"=>[16,"\xE5s"],"\xA4"=>[94,"\xE5t"],"\xA5"=>[76,"\xE5t"],"\xA6"=>[104,"\xE5t"],"\xA7"=>[16,"\xE5t"],"\xA8"=>[77,"\xE5\x20"],"\xA9"=>[18,"\xE5\x20"],"\xAA"=>[77,"\xE5\x25"],"\xAB"=>[18,"\xE5\x25"],"\xAC"=>[77,"\xE5-"],"\xAD"=>[18,"\xE5-"],"\xAE"=>[77,"\xE5."],"\xAF"=>[18,"\xE5."],"\xB0"=>[77,"\xE5\x2F"],"\xB1"=>[18,"\xE5\x2F"],"\xB2"=>[77,"\xE53"],"\xB3"=>[18,"\xE53"],"\xB4"=>[77,"\xE54"],"\xB5"=>[18,"\xE54"],"\xB6"=>[77,"\xE55"],"\xB7"=>[18,"\xE55"],"\xB8"=>[77,"\xE56"],"\xB9"=>[18,"\xE56"],"\xBA"=>[77,"\xE57"],"\xBB"=>[18,"\xE57"],"\xBC"=>[77,"\xE58"],"\xBD"=>[18,"\xE58"],"\xBE"=>[77,"\xE59"],"\xBF"=>[18,"\xE59"],"\xC0"=>[77,"\xE5\x3D"],"\xC1"=>[18,"\xE5\x3D"],"\xC2"=>[77,"\xE5A"],"\xC3"=>[18,"\xE5A"],"\xC4"=>[77,"\xE5_"],"\xC5"=>[18,"\xE5_"],"\xC6"=>[77,"\xE5b"],"\xC7"=>[18,"\xE5b"],"\xC8"=>[77,"\xE5d"],"\xC9"=>[18,"\xE5d"],"\xCA"=>[77,"\xE5f"],"\xCB"=>[18,"\xE5f"],"\xCC"=>[77,"\xE5g"],"\xCD"=>[18,"\xE5g"],"\xCE"=>[77,"\xE5h"],"\xCF"=>[18,"\xE5h"],"\xD0"=>[77,"\xE5l"],"\xD1"=>[18,"\xE5l"],"\xD2"=>[77,"\xE5m"],"\xD3"=>[18,"\xE5m"],"\xD4"=>[77,"\xE5n"],"\xD5"=>[18,"\xE5n"],"\xD6"=>[77,"\xE5p"],"\xD7"=>[18,"\xE5p"],"\xD8"=>[77,"\xE5r"],"\xD9"=>[18,"\xE5r"],"\xDA"=>[77,"\xE5u"],"\xDB"=>[18,"\xE5u"],"\xDC"=>[0,"\xE5\x3A"],"\xDD"=>[0,"\xE5B"],"\xDE"=>[0,"\xE5C"],"\xDF"=>[0,"\xE5D"],"\xE0"=>[0,"\xE5E"],"\xE1"=>[0,"\xE5F"],"\xE2"=>[0,"\xE5G"],"\xE3"=>[0,"\xE5H"],"\xE4"=>[0,"\xE5I"],"\xE5"=>[0,"\xE5J"],"\xE6"=>[0,"\xE5K"],"\xE7"=>[0,"\xE5L"],"\xE8"=>[0,"\xE5M"],"\xE9"=>[0,"\xE5N"],"\xEA"=>[0,"\xE5O"],"\xEB"=>[0,"\xE5P"],"\xEC"=>[0,"\xE5Q"],"\xED"=>[0,"\xE5R"],"\xEE"=>[0,"\xE5S"],"\xEF"=>[0,"\xE5T"],"\xF0"=>[0,"\xE5U"],"\xF1"=>[0,"\xE5V"],"\xF2"=>[0,"\xE5W"],"\xF3"=>[0,"\xE5Y"],"\xF4"=>[0,"\xE5j"],"\xF5"=>[0,"\xE5k"],"\xF6"=>[0,"\xE5q"],"\xF7"=>[0,"\xE5v"],"\xF8"=>[0,"\xE5w"],"\xF9"=>[0,"\xE5x"],"\xFA"=>[0,"\xE5y"],"\xFB"=>[0,"\xE5z"],"\xFC"=>[82,"\xE5"],"\xFD"=>[87,"\xE5"],"\xFE"=>[130,"\xE5"],"\xFF"=>[9,"\xE5"],],["\x00"=>[94,"\xE80"],"\x01"=>[76,"\xE80"],"\x02"=>[104,"\xE80"],"\x03"=>[16,"\xE80"],"\x04"=>[94,"\xE81"],"\x05"=>[76,"\xE81"],"\x06"=>[104,"\xE81"],"\x07"=>[16,"\xE81"],"\x08"=>[94,"\xE82"],"\x09"=>[76,"\xE82"],"\x0A"=>[104,"\xE82"],"\x0B"=>[16,"\xE82"],"\x0C"=>[94,"\xE8a"],"\x0D"=>[76,"\xE8a"],"\x0E"=>[104,"\xE8a"],"\x0F"=>[16,"\xE8a"],"\x10"=>[94,"\xE8c"],"\x11"=>[76,"\xE8c"],"\x12"=>[104,"\xE8c"],"\x13"=>[16,"\xE8c"],"\x14"=>[94,"\xE8e"],"\x15"=>[76,"\xE8e"],"\x16"=>[104,"\xE8e"],"\x17"=>[16,"\xE8e"],"\x18"=>[94,"\xE8i"],"\x19"=>[76,"\xE8i"],"\x1A"=>[104,"\xE8i"],"\x1B"=>[16,"\xE8i"],"\x1C"=>[94,"\xE8o"],"\x1D"=>[76,"\xE8o"],"\x1E"=>[104,"\xE8o"],"\x1F"=>[16,"\xE8o"],"\x20"=>[94,"\xE8s"],"\x21"=>[76,"\xE8s"],"\x22"=>[104,"\xE8s"],"\x23"=>[16,"\xE8s"],"\x24"=>[94,"\xE8t"],"\x25"=>[76,"\xE8t"],"\x26"=>[104,"\xE8t"],"\x27"=>[16,"\xE8t"],"\x28"=>[77,"\xE8\x20"],"\x29"=>[18,"\xE8\x20"],"\x2A"=>[77,"\xE8\x25"],"\x2B"=>[18,"\xE8\x25"],"\x2C"=>[77,"\xE8-"],"-"=>[18,"\xE8-"],"."=>[77,"\xE8."],"\x2F"=>[18,"\xE8."],[77,"\xE8\x2F"],[18,"\xE8\x2F"],[77,"\xE83"],[18,"\xE83"],[77,"\xE84"],[18,"\xE84"],[77,"\xE85"],[18,"\xE85"],[77,"\xE86"],[18,"\xE86"],"\x3A"=>[77,"\xE87"],"\x3B"=>[18,"\xE87"],"\x3C"=>[77,"\xE88"],"\x3D"=>[18,"\xE88"],"\x3E"=>[77,"\xE89"],"\x3F"=>[18,"\xE89"],"\x40"=>[77,"\xE8\x3D"],"A"=>[18,"\xE8\x3D"],"B"=>[77,"\xE8A"],"C"=>[18,"\xE8A"],"D"=>[77,"\xE8_"],"E"=>[18,"\xE8_"],"F"=>[77,"\xE8b"],"G"=>[18,"\xE8b"],"H"=>[77,"\xE8d"],"I"=>[18,"\xE8d"],"J"=>[77,"\xE8f"],"K"=>[18,"\xE8f"],"L"=>[77,"\xE8g"],"M"=>[18,"\xE8g"],"N"=>[77,"\xE8h"],"O"=>[18,"\xE8h"],"P"=>[77,"\xE8l"],"Q"=>[18,"\xE8l"],"R"=>[77,"\xE8m"],"S"=>[18,"\xE8m"],"T"=>[77,"\xE8n"],"U"=>[18,"\xE8n"],"V"=>[77,"\xE8p"],"W"=>[18,"\xE8p"],"X"=>[77,"\xE8r"],"Y"=>[18,"\xE8r"],"Z"=>[77,"\xE8u"],"\x5B"=>[18,"\xE8u"],"\x5C"=>[0,"\xE8\x3A"],"\x5D"=>[0,"\xE8B"],"\x5E"=>[0,"\xE8C"],"_"=>[0,"\xE8D"],"\x60"=>[0,"\xE8E"],"a"=>[0,"\xE8F"],"b"=>[0,"\xE8G"],"c"=>[0,"\xE8H"],"d"=>[0,"\xE8I"],"e"=>[0,"\xE8J"],"f"=>[0,"\xE8K"],"g"=>[0,"\xE8L"],"h"=>[0,"\xE8M"],"i"=>[0,"\xE8N"],"j"=>[0,"\xE8O"],"k"=>[0,"\xE8P"],"l"=>[0,"\xE8Q"],"m"=>[0,"\xE8R"],"n"=>[0,"\xE8S"],"o"=>[0,"\xE8T"],"p"=>[0,"\xE8U"],"q"=>[0,"\xE8V"],"r"=>[0,"\xE8W"],"s"=>[0,"\xE8Y"],"t"=>[0,"\xE8j"],"u"=>[0,"\xE8k"],"v"=>[0,"\xE8q"],"w"=>[0,"\xE8v"],"x"=>[0,"\xE8w"],"y"=>[0,"\xE8x"],"z"=>[0,"\xE8y"],"\x7B"=>[0,"\xE8z"],"\x7C"=>[82,"\xE8"],"\x7D"=>[87,"\xE8"],"~"=>[130,"\xE8"],"\x7F"=>[9,"\xE8"],"\x80"=>[94,"\xE90"],"\x81"=>[76,"\xE90"],"\x82"=>[104,"\xE90"],"\x83"=>[16,"\xE90"],"\x84"=>[94,"\xE91"],"\x85"=>[76,"\xE91"],"\x86"=>[104,"\xE91"],"\x87"=>[16,"\xE91"],"\x88"=>[94,"\xE92"],"\x89"=>[76,"\xE92"],"\x8A"=>[104,"\xE92"],"\x8B"=>[16,"\xE92"],"\x8C"=>[94,"\xE9a"],"\x8D"=>[76,"\xE9a"],"\x8E"=>[104,"\xE9a"],"\x8F"=>[16,"\xE9a"],"\x90"=>[94,"\xE9c"],"\x91"=>[76,"\xE9c"],"\x92"=>[104,"\xE9c"],"\x93"=>[16,"\xE9c"],"\x94"=>[94,"\xE9e"],"\x95"=>[76,"\xE9e"],"\x96"=>[104,"\xE9e"],"\x97"=>[16,"\xE9e"],"\x98"=>[94,"\xE9i"],"\x99"=>[76,"\xE9i"],"\x9A"=>[104,"\xE9i"],"\x9B"=>[16,"\xE9i"],"\x9C"=>[94,"\xE9o"],"\x9D"=>[76,"\xE9o"],"\x9E"=>[104,"\xE9o"],"\x9F"=>[16,"\xE9o"],"\xA0"=>[94,"\xE9s"],"\xA1"=>[76,"\xE9s"],"\xA2"=>[104,"\xE9s"],"\xA3"=>[16,"\xE9s"],"\xA4"=>[94,"\xE9t"],"\xA5"=>[76,"\xE9t"],"\xA6"=>[104,"\xE9t"],"\xA7"=>[16,"\xE9t"],"\xA8"=>[77,"\xE9\x20"],"\xA9"=>[18,"\xE9\x20"],"\xAA"=>[77,"\xE9\x25"],"\xAB"=>[18,"\xE9\x25"],"\xAC"=>[77,"\xE9-"],"\xAD"=>[18,"\xE9-"],"\xAE"=>[77,"\xE9."],"\xAF"=>[18,"\xE9."],"\xB0"=>[77,"\xE9\x2F"],"\xB1"=>[18,"\xE9\x2F"],"\xB2"=>[77,"\xE93"],"\xB3"=>[18,"\xE93"],"\xB4"=>[77,"\xE94"],"\xB5"=>[18,"\xE94"],"\xB6"=>[77,"\xE95"],"\xB7"=>[18,"\xE95"],"\xB8"=>[77,"\xE96"],"\xB9"=>[18,"\xE96"],"\xBA"=>[77,"\xE97"],"\xBB"=>[18,"\xE97"],"\xBC"=>[77,"\xE98"],"\xBD"=>[18,"\xE98"],"\xBE"=>[77,"\xE99"],"\xBF"=>[18,"\xE99"],"\xC0"=>[77,"\xE9\x3D"],"\xC1"=>[18,"\xE9\x3D"],"\xC2"=>[77,"\xE9A"],"\xC3"=>[18,"\xE9A"],"\xC4"=>[77,"\xE9_"],"\xC5"=>[18,"\xE9_"],"\xC6"=>[77,"\xE9b"],"\xC7"=>[18,"\xE9b"],"\xC8"=>[77,"\xE9d"],"\xC9"=>[18,"\xE9d"],"\xCA"=>[77,"\xE9f"],"\xCB"=>[18,"\xE9f"],"\xCC"=>[77,"\xE9g"],"\xCD"=>[18,"\xE9g"],"\xCE"=>[77,"\xE9h"],"\xCF"=>[18,"\xE9h"],"\xD0"=>[77,"\xE9l"],"\xD1"=>[18,"\xE9l"],"\xD2"=>[77,"\xE9m"],"\xD3"=>[18,"\xE9m"],"\xD4"=>[77,"\xE9n"],"\xD5"=>[18,"\xE9n"],"\xD6"=>[77,"\xE9p"],"\xD7"=>[18,"\xE9p"],"\xD8"=>[77,"\xE9r"],"\xD9"=>[18,"\xE9r"],"\xDA"=>[77,"\xE9u"],"\xDB"=>[18,"\xE9u"],"\xDC"=>[0,"\xE9\x3A"],"\xDD"=>[0,"\xE9B"],"\xDE"=>[0,"\xE9C"],"\xDF"=>[0,"\xE9D"],"\xE0"=>[0,"\xE9E"],"\xE1"=>[0,"\xE9F"],"\xE2"=>[0,"\xE9G"],"\xE3"=>[0,"\xE9H"],"\xE4"=>[0,"\xE9I"],"\xE5"=>[0,"\xE9J"],"\xE6"=>[0,"\xE9K"],"\xE7"=>[0,"\xE9L"],"\xE8"=>[0,"\xE9M"],"\xE9"=>[0,"\xE9N"],"\xEA"=>[0,"\xE9O"],"\xEB"=>[0,"\xE9P"],"\xEC"=>[0,"\xE9Q"],"\xED"=>[0,"\xE9R"],"\xEE"=>[0,"\xE9S"],"\xEF"=>[0,"\xE9T"],"\xF0"=>[0,"\xE9U"],"\xF1"=>[0,"\xE9V"],"\xF2"=>[0,"\xE9W"],"\xF3"=>[0,"\xE9Y"],"\xF4"=>[0,"\xE9j"],"\xF5"=>[0,"\xE9k"],"\xF6"=>[0,"\xE9q"],"\xF7"=>[0,"\xE9v"],"\xF8"=>[0,"\xE9w"],"\xF9"=>[0,"\xE9x"],"\xFA"=>[0,"\xE9y"],"\xFB"=>[0,"\xE9z"],"\xFC"=>[82,"\xE9"],"\xFD"=>[87,"\xE9"],"\xFE"=>[130,"\xE9"],"\xFF"=>[9,"\xE9"],],["\x00"=>[94,"\xEA0"],"\x01"=>[76,"\xEA0"],"\x02"=>[104,"\xEA0"],"\x03"=>[16,"\xEA0"],"\x04"=>[94,"\xEA1"],"\x05"=>[76,"\xEA1"],"\x06"=>[104,"\xEA1"],"\x07"=>[16,"\xEA1"],"\x08"=>[94,"\xEA2"],"\x09"=>[76,"\xEA2"],"\x0A"=>[104,"\xEA2"],"\x0B"=>[16,"\xEA2"],"\x0C"=>[94,"\xEAa"],"\x0D"=>[76,"\xEAa"],"\x0E"=>[104,"\xEAa"],"\x0F"=>[16,"\xEAa"],"\x10"=>[94,"\xEAc"],"\x11"=>[76,"\xEAc"],"\x12"=>[104,"\xEAc"],"\x13"=>[16,"\xEAc"],"\x14"=>[94,"\xEAe"],"\x15"=>[76,"\xEAe"],"\x16"=>[104,"\xEAe"],"\x17"=>[16,"\xEAe"],"\x18"=>[94,"\xEAi"],"\x19"=>[76,"\xEAi"],"\x1A"=>[104,"\xEAi"],"\x1B"=>[16,"\xEAi"],"\x1C"=>[94,"\xEAo"],"\x1D"=>[76,"\xEAo"],"\x1E"=>[104,"\xEAo"],"\x1F"=>[16,"\xEAo"],"\x20"=>[94,"\xEAs"],"\x21"=>[76,"\xEAs"],"\x22"=>[104,"\xEAs"],"\x23"=>[16,"\xEAs"],"\x24"=>[94,"\xEAt"],"\x25"=>[76,"\xEAt"],"\x26"=>[104,"\xEAt"],"\x27"=>[16,"\xEAt"],"\x28"=>[77,"\xEA\x20"],"\x29"=>[18,"\xEA\x20"],"\x2A"=>[77,"\xEA\x25"],"\x2B"=>[18,"\xEA\x25"],"\x2C"=>[77,"\xEA-"],"-"=>[18,"\xEA-"],"."=>[77,"\xEA."],"\x2F"=>[18,"\xEA."],[77,"\xEA\x2F"],[18,"\xEA\x2F"],[77,"\xEA3"],[18,"\xEA3"],[77,"\xEA4"],[18,"\xEA4"],[77,"\xEA5"],[18,"\xEA5"],[77,"\xEA6"],[18,"\xEA6"],"\x3A"=>[77,"\xEA7"],"\x3B"=>[18,"\xEA7"],"\x3C"=>[77,"\xEA8"],"\x3D"=>[18,"\xEA8"],"\x3E"=>[77,"\xEA9"],"\x3F"=>[18,"\xEA9"],"\x40"=>[77,"\xEA\x3D"],"A"=>[18,"\xEA\x3D"],"B"=>[77,"\xEAA"],"C"=>[18,"\xEAA"],"D"=>[77,"\xEA_"],"E"=>[18,"\xEA_"],"F"=>[77,"\xEAb"],"G"=>[18,"\xEAb"],"H"=>[77,"\xEAd"],"I"=>[18,"\xEAd"],"J"=>[77,"\xEAf"],"K"=>[18,"\xEAf"],"L"=>[77,"\xEAg"],"M"=>[18,"\xEAg"],"N"=>[77,"\xEAh"],"O"=>[18,"\xEAh"],"P"=>[77,"\xEAl"],"Q"=>[18,"\xEAl"],"R"=>[77,"\xEAm"],"S"=>[18,"\xEAm"],"T"=>[77,"\xEAn"],"U"=>[18,"\xEAn"],"V"=>[77,"\xEAp"],"W"=>[18,"\xEAp"],"X"=>[77,"\xEAr"],"Y"=>[18,"\xEAr"],"Z"=>[77,"\xEAu"],"\x5B"=>[18,"\xEAu"],"\x5C"=>[0,"\xEA\x3A"],"\x5D"=>[0,"\xEAB"],"\x5E"=>[0,"\xEAC"],"_"=>[0,"\xEAD"],"\x60"=>[0,"\xEAE"],"a"=>[0,"\xEAF"],"b"=>[0,"\xEAG"],"c"=>[0,"\xEAH"],"d"=>[0,"\xEAI"],"e"=>[0,"\xEAJ"],"f"=>[0,"\xEAK"],"g"=>[0,"\xEAL"],"h"=>[0,"\xEAM"],"i"=>[0,"\xEAN"],"j"=>[0,"\xEAO"],"k"=>[0,"\xEAP"],"l"=>[0,"\xEAQ"],"m"=>[0,"\xEAR"],"n"=>[0,"\xEAS"],"o"=>[0,"\xEAT"],"p"=>[0,"\xEAU"],"q"=>[0,"\xEAV"],"r"=>[0,"\xEAW"],"s"=>[0,"\xEAY"],"t"=>[0,"\xEAj"],"u"=>[0,"\xEAk"],"v"=>[0,"\xEAq"],"w"=>[0,"\xEAv"],"x"=>[0,"\xEAw"],"y"=>[0,"\xEAx"],"z"=>[0,"\xEAy"],"\x7B"=>[0,"\xEAz"],"\x7C"=>[82,"\xEA"],"\x7D"=>[87,"\xEA"],"~"=>[130,"\xEA"],"\x7F"=>[9,"\xEA"],"\x80"=>[94,"\xEB0"],"\x81"=>[76,"\xEB0"],"\x82"=>[104,"\xEB0"],"\x83"=>[16,"\xEB0"],"\x84"=>[94,"\xEB1"],"\x85"=>[76,"\xEB1"],"\x86"=>[104,"\xEB1"],"\x87"=>[16,"\xEB1"],"\x88"=>[94,"\xEB2"],"\x89"=>[76,"\xEB2"],"\x8A"=>[104,"\xEB2"],"\x8B"=>[16,"\xEB2"],"\x8C"=>[94,"\xEBa"],"\x8D"=>[76,"\xEBa"],"\x8E"=>[104,"\xEBa"],"\x8F"=>[16,"\xEBa"],"\x90"=>[94,"\xEBc"],"\x91"=>[76,"\xEBc"],"\x92"=>[104,"\xEBc"],"\x93"=>[16,"\xEBc"],"\x94"=>[94,"\xEBe"],"\x95"=>[76,"\xEBe"],"\x96"=>[104,"\xEBe"],"\x97"=>[16,"\xEBe"],"\x98"=>[94,"\xEBi"],"\x99"=>[76,"\xEBi"],"\x9A"=>[104,"\xEBi"],"\x9B"=>[16,"\xEBi"],"\x9C"=>[94,"\xEBo"],"\x9D"=>[76,"\xEBo"],"\x9E"=>[104,"\xEBo"],"\x9F"=>[16,"\xEBo"],"\xA0"=>[94,"\xEBs"],"\xA1"=>[76,"\xEBs"],"\xA2"=>[104,"\xEBs"],"\xA3"=>[16,"\xEBs"],"\xA4"=>[94,"\xEBt"],"\xA5"=>[76,"\xEBt"],"\xA6"=>[104,"\xEBt"],"\xA7"=>[16,"\xEBt"],"\xA8"=>[77,"\xEB\x20"],"\xA9"=>[18,"\xEB\x20"],"\xAA"=>[77,"\xEB\x25"],"\xAB"=>[18,"\xEB\x25"],"\xAC"=>[77,"\xEB-"],"\xAD"=>[18,"\xEB-"],"\xAE"=>[77,"\xEB."],"\xAF"=>[18,"\xEB."],"\xB0"=>[77,"\xEB\x2F"],"\xB1"=>[18,"\xEB\x2F"],"\xB2"=>[77,"\xEB3"],"\xB3"=>[18,"\xEB3"],"\xB4"=>[77,"\xEB4"],"\xB5"=>[18,"\xEB4"],"\xB6"=>[77,"\xEB5"],"\xB7"=>[18,"\xEB5"],"\xB8"=>[77,"\xEB6"],"\xB9"=>[18,"\xEB6"],"\xBA"=>[77,"\xEB7"],"\xBB"=>[18,"\xEB7"],"\xBC"=>[77,"\xEB8"],"\xBD"=>[18,"\xEB8"],"\xBE"=>[77,"\xEB9"],"\xBF"=>[18,"\xEB9"],"\xC0"=>[77,"\xEB\x3D"],"\xC1"=>[18,"\xEB\x3D"],"\xC2"=>[77,"\xEBA"],"\xC3"=>[18,"\xEBA"],"\xC4"=>[77,"\xEB_"],"\xC5"=>[18,"\xEB_"],"\xC6"=>[77,"\xEBb"],"\xC7"=>[18,"\xEBb"],"\xC8"=>[77,"\xEBd"],"\xC9"=>[18,"\xEBd"],"\xCA"=>[77,"\xEBf"],"\xCB"=>[18,"\xEBf"],"\xCC"=>[77,"\xEBg"],"\xCD"=>[18,"\xEBg"],"\xCE"=>[77,"\xEBh"],"\xCF"=>[18,"\xEBh"],"\xD0"=>[77,"\xEBl"],"\xD1"=>[18,"\xEBl"],"\xD2"=>[77,"\xEBm"],"\xD3"=>[18,"\xEBm"],"\xD4"=>[77,"\xEBn"],"\xD5"=>[18,"\xEBn"],"\xD6"=>[77,"\xEBp"],"\xD7"=>[18,"\xEBp"],"\xD8"=>[77,"\xEBr"],"\xD9"=>[18,"\xEBr"],"\xDA"=>[77,"\xEBu"],"\xDB"=>[18,"\xEBu"],"\xDC"=>[0,"\xEB\x3A"],"\xDD"=>[0,"\xEBB"],"\xDE"=>[0,"\xEBC"],"\xDF"=>[0,"\xEBD"],"\xE0"=>[0,"\xEBE"],"\xE1"=>[0,"\xEBF"],"\xE2"=>[0,"\xEBG"],"\xE3"=>[0,"\xEBH"],"\xE4"=>[0,"\xEBI"],"\xE5"=>[0,"\xEBJ"],"\xE6"=>[0,"\xEBK"],"\xE7"=>[0,"\xEBL"],"\xE8"=>[0,"\xEBM"],"\xE9"=>[0,"\xEBN"],"\xEA"=>[0,"\xEBO"],"\xEB"=>[0,"\xEBP"],"\xEC"=>[0,"\xEBQ"],"\xED"=>[0,"\xEBR"],"\xEE"=>[0,"\xEBS"],"\xEF"=>[0,"\xEBT"],"\xF0"=>[0,"\xEBU"],"\xF1"=>[0,"\xEBV"],"\xF2"=>[0,"\xEBW"],"\xF3"=>[0,"\xEBY"],"\xF4"=>[0,"\xEBj"],"\xF5"=>[0,"\xEBk"],"\xF6"=>[0,"\xEBq"],"\xF7"=>[0,"\xEBv"],"\xF8"=>[0,"\xEBw"],"\xF9"=>[0,"\xEBx"],"\xFA"=>[0,"\xEBy"],"\xFB"=>[0,"\xEBz"],"\xFC"=>[82,"\xEB"],"\xFD"=>[87,"\xEB"],"\xFE"=>[130,"\xEB"],"\xFF"=>[9,"\xEB"],],["\x00"=>[94,"\xEC0"],"\x01"=>[76,"\xEC0"],"\x02"=>[104,"\xEC0"],"\x03"=>[16,"\xEC0"],"\x04"=>[94,"\xEC1"],"\x05"=>[76,"\xEC1"],"\x06"=>[104,"\xEC1"],"\x07"=>[16,"\xEC1"],"\x08"=>[94,"\xEC2"],"\x09"=>[76,"\xEC2"],"\x0A"=>[104,"\xEC2"],"\x0B"=>[16,"\xEC2"],"\x0C"=>[94,"\xECa"],"\x0D"=>[76,"\xECa"],"\x0E"=>[104,"\xECa"],"\x0F"=>[16,"\xECa"],"\x10"=>[94,"\xECc"],"\x11"=>[76,"\xECc"],"\x12"=>[104,"\xECc"],"\x13"=>[16,"\xECc"],"\x14"=>[94,"\xECe"],"\x15"=>[76,"\xECe"],"\x16"=>[104,"\xECe"],"\x17"=>[16,"\xECe"],"\x18"=>[94,"\xECi"],"\x19"=>[76,"\xECi"],"\x1A"=>[104,"\xECi"],"\x1B"=>[16,"\xECi"],"\x1C"=>[94,"\xECo"],"\x1D"=>[76,"\xECo"],"\x1E"=>[104,"\xECo"],"\x1F"=>[16,"\xECo"],"\x20"=>[94,"\xECs"],"\x21"=>[76,"\xECs"],"\x22"=>[104,"\xECs"],"\x23"=>[16,"\xECs"],"\x24"=>[94,"\xECt"],"\x25"=>[76,"\xECt"],"\x26"=>[104,"\xECt"],"\x27"=>[16,"\xECt"],"\x28"=>[77,"\xEC\x20"],"\x29"=>[18,"\xEC\x20"],"\x2A"=>[77,"\xEC\x25"],"\x2B"=>[18,"\xEC\x25"],"\x2C"=>[77,"\xEC-"],"-"=>[18,"\xEC-"],"."=>[77,"\xEC."],"\x2F"=>[18,"\xEC."],[77,"\xEC\x2F"],[18,"\xEC\x2F"],[77,"\xEC3"],[18,"\xEC3"],[77,"\xEC4"],[18,"\xEC4"],[77,"\xEC5"],[18,"\xEC5"],[77,"\xEC6"],[18,"\xEC6"],"\x3A"=>[77,"\xEC7"],"\x3B"=>[18,"\xEC7"],"\x3C"=>[77,"\xEC8"],"\x3D"=>[18,"\xEC8"],"\x3E"=>[77,"\xEC9"],"\x3F"=>[18,"\xEC9"],"\x40"=>[77,"\xEC\x3D"],"A"=>[18,"\xEC\x3D"],"B"=>[77,"\xECA"],"C"=>[18,"\xECA"],"D"=>[77,"\xEC_"],"E"=>[18,"\xEC_"],"F"=>[77,"\xECb"],"G"=>[18,"\xECb"],"H"=>[77,"\xECd"],"I"=>[18,"\xECd"],"J"=>[77,"\xECf"],"K"=>[18,"\xECf"],"L"=>[77,"\xECg"],"M"=>[18,"\xECg"],"N"=>[77,"\xECh"],"O"=>[18,"\xECh"],"P"=>[77,"\xECl"],"Q"=>[18,"\xECl"],"R"=>[77,"\xECm"],"S"=>[18,"\xECm"],"T"=>[77,"\xECn"],"U"=>[18,"\xECn"],"V"=>[77,"\xECp"],"W"=>[18,"\xECp"],"X"=>[77,"\xECr"],"Y"=>[18,"\xECr"],"Z"=>[77,"\xECu"],"\x5B"=>[18,"\xECu"],"\x5C"=>[0,"\xEC\x3A"],"\x5D"=>[0,"\xECB"],"\x5E"=>[0,"\xECC"],"_"=>[0,"\xECD"],"\x60"=>[0,"\xECE"],"a"=>[0,"\xECF"],"b"=>[0,"\xECG"],"c"=>[0,"\xECH"],"d"=>[0,"\xECI"],"e"=>[0,"\xECJ"],"f"=>[0,"\xECK"],"g"=>[0,"\xECL"],"h"=>[0,"\xECM"],"i"=>[0,"\xECN"],"j"=>[0,"\xECO"],"k"=>[0,"\xECP"],"l"=>[0,"\xECQ"],"m"=>[0,"\xECR"],"n"=>[0,"\xECS"],"o"=>[0,"\xECT"],"p"=>[0,"\xECU"],"q"=>[0,"\xECV"],"r"=>[0,"\xECW"],"s"=>[0,"\xECY"],"t"=>[0,"\xECj"],"u"=>[0,"\xECk"],"v"=>[0,"\xECq"],"w"=>[0,"\xECv"],"x"=>[0,"\xECw"],"y"=>[0,"\xECx"],"z"=>[0,"\xECy"],"\x7B"=>[0,"\xECz"],"\x7C"=>[82,"\xEC"],"\x7D"=>[87,"\xEC"],"~"=>[130,"\xEC"],"\x7F"=>[9,"\xEC"],"\x80"=>[94,"\xED0"],"\x81"=>[76,"\xED0"],"\x82"=>[104,"\xED0"],"\x83"=>[16,"\xED0"],"\x84"=>[94,"\xED1"],"\x85"=>[76,"\xED1"],"\x86"=>[104,"\xED1"],"\x87"=>[16,"\xED1"],"\x88"=>[94,"\xED2"],"\x89"=>[76,"\xED2"],"\x8A"=>[104,"\xED2"],"\x8B"=>[16,"\xED2"],"\x8C"=>[94,"\xEDa"],"\x8D"=>[76,"\xEDa"],"\x8E"=>[104,"\xEDa"],"\x8F"=>[16,"\xEDa"],"\x90"=>[94,"\xEDc"],"\x91"=>[76,"\xEDc"],"\x92"=>[104,"\xEDc"],"\x93"=>[16,"\xEDc"],"\x94"=>[94,"\xEDe"],"\x95"=>[76,"\xEDe"],"\x96"=>[104,"\xEDe"],"\x97"=>[16,"\xEDe"],"\x98"=>[94,"\xEDi"],"\x99"=>[76,"\xEDi"],"\x9A"=>[104,"\xEDi"],"\x9B"=>[16,"\xEDi"],"\x9C"=>[94,"\xEDo"],"\x9D"=>[76,"\xEDo"],"\x9E"=>[104,"\xEDo"],"\x9F"=>[16,"\xEDo"],"\xA0"=>[94,"\xEDs"],"\xA1"=>[76,"\xEDs"],"\xA2"=>[104,"\xEDs"],"\xA3"=>[16,"\xEDs"],"\xA4"=>[94,"\xEDt"],"\xA5"=>[76,"\xEDt"],"\xA6"=>[104,"\xEDt"],"\xA7"=>[16,"\xEDt"],"\xA8"=>[77,"\xED\x20"],"\xA9"=>[18,"\xED\x20"],"\xAA"=>[77,"\xED\x25"],"\xAB"=>[18,"\xED\x25"],"\xAC"=>[77,"\xED-"],"\xAD"=>[18,"\xED-"],"\xAE"=>[77,"\xED."],"\xAF"=>[18,"\xED."],"\xB0"=>[77,"\xED\x2F"],"\xB1"=>[18,"\xED\x2F"],"\xB2"=>[77,"\xED3"],"\xB3"=>[18,"\xED3"],"\xB4"=>[77,"\xED4"],"\xB5"=>[18,"\xED4"],"\xB6"=>[77,"\xED5"],"\xB7"=>[18,"\xED5"],"\xB8"=>[77,"\xED6"],"\xB9"=>[18,"\xED6"],"\xBA"=>[77,"\xED7"],"\xBB"=>[18,"\xED7"],"\xBC"=>[77,"\xED8"],"\xBD"=>[18,"\xED8"],"\xBE"=>[77,"\xED9"],"\xBF"=>[18,"\xED9"],"\xC0"=>[77,"\xED\x3D"],"\xC1"=>[18,"\xED\x3D"],"\xC2"=>[77,"\xEDA"],"\xC3"=>[18,"\xEDA"],"\xC4"=>[77,"\xED_"],"\xC5"=>[18,"\xED_"],"\xC6"=>[77,"\xEDb"],"\xC7"=>[18,"\xEDb"],"\xC8"=>[77,"\xEDd"],"\xC9"=>[18,"\xEDd"],"\xCA"=>[77,"\xEDf"],"\xCB"=>[18,"\xEDf"],"\xCC"=>[77,"\xEDg"],"\xCD"=>[18,"\xEDg"],"\xCE"=>[77,"\xEDh"],"\xCF"=>[18,"\xEDh"],"\xD0"=>[77,"\xEDl"],"\xD1"=>[18,"\xEDl"],"\xD2"=>[77,"\xEDm"],"\xD3"=>[18,"\xEDm"],"\xD4"=>[77,"\xEDn"],"\xD5"=>[18,"\xEDn"],"\xD6"=>[77,"\xEDp"],"\xD7"=>[18,"\xEDp"],"\xD8"=>[77,"\xEDr"],"\xD9"=>[18,"\xEDr"],"\xDA"=>[77,"\xEDu"],"\xDB"=>[18,"\xEDu"],"\xDC"=>[0,"\xED\x3A"],"\xDD"=>[0,"\xEDB"],"\xDE"=>[0,"\xEDC"],"\xDF"=>[0,"\xEDD"],"\xE0"=>[0,"\xEDE"],"\xE1"=>[0,"\xEDF"],"\xE2"=>[0,"\xEDG"],"\xE3"=>[0,"\xEDH"],"\xE4"=>[0,"\xEDI"],"\xE5"=>[0,"\xEDJ"],"\xE6"=>[0,"\xEDK"],"\xE7"=>[0,"\xEDL"],"\xE8"=>[0,"\xEDM"],"\xE9"=>[0,"\xEDN"],"\xEA"=>[0,"\xEDO"],"\xEB"=>[0,"\xEDP"],"\xEC"=>[0,"\xEDQ"],"\xED"=>[0,"\xEDR"],"\xEE"=>[0,"\xEDS"],"\xEF"=>[0,"\xEDT"],"\xF0"=>[0,"\xEDU"],"\xF1"=>[0,"\xEDV"],"\xF2"=>[0,"\xEDW"],"\xF3"=>[0,"\xEDY"],"\xF4"=>[0,"\xEDj"],"\xF5"=>[0,"\xEDk"],"\xF6"=>[0,"\xEDq"],"\xF7"=>[0,"\xEDv"],"\xF8"=>[0,"\xEDw"],"\xF9"=>[0,"\xEDx"],"\xFA"=>[0,"\xEDy"],"\xFB"=>[0,"\xEDz"],"\xFC"=>[82,"\xED"],"\xFD"=>[87,"\xED"],"\xFE"=>[130,"\xED"],"\xFF"=>[9,"\xED"],],["\x00"=>[94,"\xEE0"],"\x01"=>[76,"\xEE0"],"\x02"=>[104,"\xEE0"],"\x03"=>[16,"\xEE0"],"\x04"=>[94,"\xEE1"],"\x05"=>[76,"\xEE1"],"\x06"=>[104,"\xEE1"],"\x07"=>[16,"\xEE1"],"\x08"=>[94,"\xEE2"],"\x09"=>[76,"\xEE2"],"\x0A"=>[104,"\xEE2"],"\x0B"=>[16,"\xEE2"],"\x0C"=>[94,"\xEEa"],"\x0D"=>[76,"\xEEa"],"\x0E"=>[104,"\xEEa"],"\x0F"=>[16,"\xEEa"],"\x10"=>[94,"\xEEc"],"\x11"=>[76,"\xEEc"],"\x12"=>[104,"\xEEc"],"\x13"=>[16,"\xEEc"],"\x14"=>[94,"\xEEe"],"\x15"=>[76,"\xEEe"],"\x16"=>[104,"\xEEe"],"\x17"=>[16,"\xEEe"],"\x18"=>[94,"\xEEi"],"\x19"=>[76,"\xEEi"],"\x1A"=>[104,"\xEEi"],"\x1B"=>[16,"\xEEi"],"\x1C"=>[94,"\xEEo"],"\x1D"=>[76,"\xEEo"],"\x1E"=>[104,"\xEEo"],"\x1F"=>[16,"\xEEo"],"\x20"=>[94,"\xEEs"],"\x21"=>[76,"\xEEs"],"\x22"=>[104,"\xEEs"],"\x23"=>[16,"\xEEs"],"\x24"=>[94,"\xEEt"],"\x25"=>[76,"\xEEt"],"\x26"=>[104,"\xEEt"],"\x27"=>[16,"\xEEt"],"\x28"=>[77,"\xEE\x20"],"\x29"=>[18,"\xEE\x20"],"\x2A"=>[77,"\xEE\x25"],"\x2B"=>[18,"\xEE\x25"],"\x2C"=>[77,"\xEE-"],"-"=>[18,"\xEE-"],"."=>[77,"\xEE."],"\x2F"=>[18,"\xEE."],[77,"\xEE\x2F"],[18,"\xEE\x2F"],[77,"\xEE3"],[18,"\xEE3"],[77,"\xEE4"],[18,"\xEE4"],[77,"\xEE5"],[18,"\xEE5"],[77,"\xEE6"],[18,"\xEE6"],"\x3A"=>[77,"\xEE7"],"\x3B"=>[18,"\xEE7"],"\x3C"=>[77,"\xEE8"],"\x3D"=>[18,"\xEE8"],"\x3E"=>[77,"\xEE9"],"\x3F"=>[18,"\xEE9"],"\x40"=>[77,"\xEE\x3D"],"A"=>[18,"\xEE\x3D"],"B"=>[77,"\xEEA"],"C"=>[18,"\xEEA"],"D"=>[77,"\xEE_"],"E"=>[18,"\xEE_"],"F"=>[77,"\xEEb"],"G"=>[18,"\xEEb"],"H"=>[77,"\xEEd"],"I"=>[18,"\xEEd"],"J"=>[77,"\xEEf"],"K"=>[18,"\xEEf"],"L"=>[77,"\xEEg"],"M"=>[18,"\xEEg"],"N"=>[77,"\xEEh"],"O"=>[18,"\xEEh"],"P"=>[77,"\xEEl"],"Q"=>[18,"\xEEl"],"R"=>[77,"\xEEm"],"S"=>[18,"\xEEm"],"T"=>[77,"\xEEn"],"U"=>[18,"\xEEn"],"V"=>[77,"\xEEp"],"W"=>[18,"\xEEp"],"X"=>[77,"\xEEr"],"Y"=>[18,"\xEEr"],"Z"=>[77,"\xEEu"],"\x5B"=>[18,"\xEEu"],"\x5C"=>[0,"\xEE\x3A"],"\x5D"=>[0,"\xEEB"],"\x5E"=>[0,"\xEEC"],"_"=>[0,"\xEED"],"\x60"=>[0,"\xEEE"],"a"=>[0,"\xEEF"],"b"=>[0,"\xEEG"],"c"=>[0,"\xEEH"],"d"=>[0,"\xEEI"],"e"=>[0,"\xEEJ"],"f"=>[0,"\xEEK"],"g"=>[0,"\xEEL"],"h"=>[0,"\xEEM"],"i"=>[0,"\xEEN"],"j"=>[0,"\xEEO"],"k"=>[0,"\xEEP"],"l"=>[0,"\xEEQ"],"m"=>[0,"\xEER"],"n"=>[0,"\xEES"],"o"=>[0,"\xEET"],"p"=>[0,"\xEEU"],"q"=>[0,"\xEEV"],"r"=>[0,"\xEEW"],"s"=>[0,"\xEEY"],"t"=>[0,"\xEEj"],"u"=>[0,"\xEEk"],"v"=>[0,"\xEEq"],"w"=>[0,"\xEEv"],"x"=>[0,"\xEEw"],"y"=>[0,"\xEEx"],"z"=>[0,"\xEEy"],"\x7B"=>[0,"\xEEz"],"\x7C"=>[82,"\xEE"],"\x7D"=>[87,"\xEE"],"~"=>[130,"\xEE"],"\x7F"=>[9,"\xEE"],"\x80"=>[94,"\xF00"],"\x81"=>[76,"\xF00"],"\x82"=>[104,"\xF00"],"\x83"=>[16,"\xF00"],"\x84"=>[94,"\xF01"],"\x85"=>[76,"\xF01"],"\x86"=>[104,"\xF01"],"\x87"=>[16,"\xF01"],"\x88"=>[94,"\xF02"],"\x89"=>[76,"\xF02"],"\x8A"=>[104,"\xF02"],"\x8B"=>[16,"\xF02"],"\x8C"=>[94,"\xF0a"],"\x8D"=>[76,"\xF0a"],"\x8E"=>[104,"\xF0a"],"\x8F"=>[16,"\xF0a"],"\x90"=>[94,"\xF0c"],"\x91"=>[76,"\xF0c"],"\x92"=>[104,"\xF0c"],"\x93"=>[16,"\xF0c"],"\x94"=>[94,"\xF0e"],"\x95"=>[76,"\xF0e"],"\x96"=>[104,"\xF0e"],"\x97"=>[16,"\xF0e"],"\x98"=>[94,"\xF0i"],"\x99"=>[76,"\xF0i"],"\x9A"=>[104,"\xF0i"],"\x9B"=>[16,"\xF0i"],"\x9C"=>[94,"\xF0o"],"\x9D"=>[76,"\xF0o"],"\x9E"=>[104,"\xF0o"],"\x9F"=>[16,"\xF0o"],"\xA0"=>[94,"\xF0s"],"\xA1"=>[76,"\xF0s"],"\xA2"=>[104,"\xF0s"],"\xA3"=>[16,"\xF0s"],"\xA4"=>[94,"\xF0t"],"\xA5"=>[76,"\xF0t"],"\xA6"=>[104,"\xF0t"],"\xA7"=>[16,"\xF0t"],"\xA8"=>[77,"\xF0\x20"],"\xA9"=>[18,"\xF0\x20"],"\xAA"=>[77,"\xF0\x25"],"\xAB"=>[18,"\xF0\x25"],"\xAC"=>[77,"\xF0-"],"\xAD"=>[18,"\xF0-"],"\xAE"=>[77,"\xF0."],"\xAF"=>[18,"\xF0."],"\xB0"=>[77,"\xF0\x2F"],"\xB1"=>[18,"\xF0\x2F"],"\xB2"=>[77,"\xF03"],"\xB3"=>[18,"\xF03"],"\xB4"=>[77,"\xF04"],"\xB5"=>[18,"\xF04"],"\xB6"=>[77,"\xF05"],"\xB7"=>[18,"\xF05"],"\xB8"=>[77,"\xF06"],"\xB9"=>[18,"\xF06"],"\xBA"=>[77,"\xF07"],"\xBB"=>[18,"\xF07"],"\xBC"=>[77,"\xF08"],"\xBD"=>[18,"\xF08"],"\xBE"=>[77,"\xF09"],"\xBF"=>[18,"\xF09"],"\xC0"=>[77,"\xF0\x3D"],"\xC1"=>[18,"\xF0\x3D"],"\xC2"=>[77,"\xF0A"],"\xC3"=>[18,"\xF0A"],"\xC4"=>[77,"\xF0_"],"\xC5"=>[18,"\xF0_"],"\xC6"=>[77,"\xF0b"],"\xC7"=>[18,"\xF0b"],"\xC8"=>[77,"\xF0d"],"\xC9"=>[18,"\xF0d"],"\xCA"=>[77,"\xF0f"],"\xCB"=>[18,"\xF0f"],"\xCC"=>[77,"\xF0g"],"\xCD"=>[18,"\xF0g"],"\xCE"=>[77,"\xF0h"],"\xCF"=>[18,"\xF0h"],"\xD0"=>[77,"\xF0l"],"\xD1"=>[18,"\xF0l"],"\xD2"=>[77,"\xF0m"],"\xD3"=>[18,"\xF0m"],"\xD4"=>[77,"\xF0n"],"\xD5"=>[18,"\xF0n"],"\xD6"=>[77,"\xF0p"],"\xD7"=>[18,"\xF0p"],"\xD8"=>[77,"\xF0r"],"\xD9"=>[18,"\xF0r"],"\xDA"=>[77,"\xF0u"],"\xDB"=>[18,"\xF0u"],"\xDC"=>[0,"\xF0\x3A"],"\xDD"=>[0,"\xF0B"],"\xDE"=>[0,"\xF0C"],"\xDF"=>[0,"\xF0D"],"\xE0"=>[0,"\xF0E"],"\xE1"=>[0,"\xF0F"],"\xE2"=>[0,"\xF0G"],"\xE3"=>[0,"\xF0H"],"\xE4"=>[0,"\xF0I"],"\xE5"=>[0,"\xF0J"],"\xE6"=>[0,"\xF0K"],"\xE7"=>[0,"\xF0L"],"\xE8"=>[0,"\xF0M"],"\xE9"=>[0,"\xF0N"],"\xEA"=>[0,"\xF0O"],"\xEB"=>[0,"\xF0P"],"\xEC"=>[0,"\xF0Q"],"\xED"=>[0,"\xF0R"],"\xEE"=>[0,"\xF0S"],"\xEF"=>[0,"\xF0T"],"\xF0"=>[0,"\xF0U"],"\xF1"=>[0,"\xF0V"],"\xF2"=>[0,"\xF0W"],"\xF3"=>[0,"\xF0Y"],"\xF4"=>[0,"\xF0j"],"\xF5"=>[0,"\xF0k"],"\xF6"=>[0,"\xF0q"],"\xF7"=>[0,"\xF0v"],"\xF8"=>[0,"\xF0w"],"\xF9"=>[0,"\xF0x"],"\xFA"=>[0,"\xF0y"],"\xFB"=>[0,"\xF0z"],"\xFC"=>[82,"\xF0"],"\xFD"=>[87,"\xF0"],"\xFE"=>[130,"\xF0"],"\xFF"=>[9,"\xF0"],],["\x00"=>[94,"\xF10"],"\x01"=>[76,"\xF10"],"\x02"=>[104,"\xF10"],"\x03"=>[16,"\xF10"],"\x04"=>[94,"\xF11"],"\x05"=>[76,"\xF11"],"\x06"=>[104,"\xF11"],"\x07"=>[16,"\xF11"],"\x08"=>[94,"\xF12"],"\x09"=>[76,"\xF12"],"\x0A"=>[104,"\xF12"],"\x0B"=>[16,"\xF12"],"\x0C"=>[94,"\xF1a"],"\x0D"=>[76,"\xF1a"],"\x0E"=>[104,"\xF1a"],"\x0F"=>[16,"\xF1a"],"\x10"=>[94,"\xF1c"],"\x11"=>[76,"\xF1c"],"\x12"=>[104,"\xF1c"],"\x13"=>[16,"\xF1c"],"\x14"=>[94,"\xF1e"],"\x15"=>[76,"\xF1e"],"\x16"=>[104,"\xF1e"],"\x17"=>[16,"\xF1e"],"\x18"=>[94,"\xF1i"],"\x19"=>[76,"\xF1i"],"\x1A"=>[104,"\xF1i"],"\x1B"=>[16,"\xF1i"],"\x1C"=>[94,"\xF1o"],"\x1D"=>[76,"\xF1o"],"\x1E"=>[104,"\xF1o"],"\x1F"=>[16,"\xF1o"],"\x20"=>[94,"\xF1s"],"\x21"=>[76,"\xF1s"],"\x22"=>[104,"\xF1s"],"\x23"=>[16,"\xF1s"],"\x24"=>[94,"\xF1t"],"\x25"=>[76,"\xF1t"],"\x26"=>[104,"\xF1t"],"\x27"=>[16,"\xF1t"],"\x28"=>[77,"\xF1\x20"],"\x29"=>[18,"\xF1\x20"],"\x2A"=>[77,"\xF1\x25"],"\x2B"=>[18,"\xF1\x25"],"\x2C"=>[77,"\xF1-"],"-"=>[18,"\xF1-"],"."=>[77,"\xF1."],"\x2F"=>[18,"\xF1."],[77,"\xF1\x2F"],[18,"\xF1\x2F"],[77,"\xF13"],[18,"\xF13"],[77,"\xF14"],[18,"\xF14"],[77,"\xF15"],[18,"\xF15"],[77,"\xF16"],[18,"\xF16"],"\x3A"=>[77,"\xF17"],"\x3B"=>[18,"\xF17"],"\x3C"=>[77,"\xF18"],"\x3D"=>[18,"\xF18"],"\x3E"=>[77,"\xF19"],"\x3F"=>[18,"\xF19"],"\x40"=>[77,"\xF1\x3D"],"A"=>[18,"\xF1\x3D"],"B"=>[77,"\xF1A"],"C"=>[18,"\xF1A"],"D"=>[77,"\xF1_"],"E"=>[18,"\xF1_"],"F"=>[77,"\xF1b"],"G"=>[18,"\xF1b"],"H"=>[77,"\xF1d"],"I"=>[18,"\xF1d"],"J"=>[77,"\xF1f"],"K"=>[18,"\xF1f"],"L"=>[77,"\xF1g"],"M"=>[18,"\xF1g"],"N"=>[77,"\xF1h"],"O"=>[18,"\xF1h"],"P"=>[77,"\xF1l"],"Q"=>[18,"\xF1l"],"R"=>[77,"\xF1m"],"S"=>[18,"\xF1m"],"T"=>[77,"\xF1n"],"U"=>[18,"\xF1n"],"V"=>[77,"\xF1p"],"W"=>[18,"\xF1p"],"X"=>[77,"\xF1r"],"Y"=>[18,"\xF1r"],"Z"=>[77,"\xF1u"],"\x5B"=>[18,"\xF1u"],"\x5C"=>[0,"\xF1\x3A"],"\x5D"=>[0,"\xF1B"],"\x5E"=>[0,"\xF1C"],"_"=>[0,"\xF1D"],"\x60"=>[0,"\xF1E"],"a"=>[0,"\xF1F"],"b"=>[0,"\xF1G"],"c"=>[0,"\xF1H"],"d"=>[0,"\xF1I"],"e"=>[0,"\xF1J"],"f"=>[0,"\xF1K"],"g"=>[0,"\xF1L"],"h"=>[0,"\xF1M"],"i"=>[0,"\xF1N"],"j"=>[0,"\xF1O"],"k"=>[0,"\xF1P"],"l"=>[0,"\xF1Q"],"m"=>[0,"\xF1R"],"n"=>[0,"\xF1S"],"o"=>[0,"\xF1T"],"p"=>[0,"\xF1U"],"q"=>[0,"\xF1V"],"r"=>[0,"\xF1W"],"s"=>[0,"\xF1Y"],"t"=>[0,"\xF1j"],"u"=>[0,"\xF1k"],"v"=>[0,"\xF1q"],"w"=>[0,"\xF1v"],"x"=>[0,"\xF1w"],"y"=>[0,"\xF1x"],"z"=>[0,"\xF1y"],"\x7B"=>[0,"\xF1z"],"\x7C"=>[82,"\xF1"],"\x7D"=>[87,"\xF1"],"~"=>[130,"\xF1"],"\x7F"=>[9,"\xF1"],"\x80"=>[94,"\xF40"],"\x81"=>[76,"\xF40"],"\x82"=>[104,"\xF40"],"\x83"=>[16,"\xF40"],"\x84"=>[94,"\xF41"],"\x85"=>[76,"\xF41"],"\x86"=>[104,"\xF41"],"\x87"=>[16,"\xF41"],"\x88"=>[94,"\xF42"],"\x89"=>[76,"\xF42"],"\x8A"=>[104,"\xF42"],"\x8B"=>[16,"\xF42"],"\x8C"=>[94,"\xF4a"],"\x8D"=>[76,"\xF4a"],"\x8E"=>[104,"\xF4a"],"\x8F"=>[16,"\xF4a"],"\x90"=>[94,"\xF4c"],"\x91"=>[76,"\xF4c"],"\x92"=>[104,"\xF4c"],"\x93"=>[16,"\xF4c"],"\x94"=>[94,"\xF4e"],"\x95"=>[76,"\xF4e"],"\x96"=>[104,"\xF4e"],"\x97"=>[16,"\xF4e"],"\x98"=>[94,"\xF4i"],"\x99"=>[76,"\xF4i"],"\x9A"=>[104,"\xF4i"],"\x9B"=>[16,"\xF4i"],"\x9C"=>[94,"\xF4o"],"\x9D"=>[76,"\xF4o"],"\x9E"=>[104,"\xF4o"],"\x9F"=>[16,"\xF4o"],"\xA0"=>[94,"\xF4s"],"\xA1"=>[76,"\xF4s"],"\xA2"=>[104,"\xF4s"],"\xA3"=>[16,"\xF4s"],"\xA4"=>[94,"\xF4t"],"\xA5"=>[76,"\xF4t"],"\xA6"=>[104,"\xF4t"],"\xA7"=>[16,"\xF4t"],"\xA8"=>[77,"\xF4\x20"],"\xA9"=>[18,"\xF4\x20"],"\xAA"=>[77,"\xF4\x25"],"\xAB"=>[18,"\xF4\x25"],"\xAC"=>[77,"\xF4-"],"\xAD"=>[18,"\xF4-"],"\xAE"=>[77,"\xF4."],"\xAF"=>[18,"\xF4."],"\xB0"=>[77,"\xF4\x2F"],"\xB1"=>[18,"\xF4\x2F"],"\xB2"=>[77,"\xF43"],"\xB3"=>[18,"\xF43"],"\xB4"=>[77,"\xF44"],"\xB5"=>[18,"\xF44"],"\xB6"=>[77,"\xF45"],"\xB7"=>[18,"\xF45"],"\xB8"=>[77,"\xF46"],"\xB9"=>[18,"\xF46"],"\xBA"=>[77,"\xF47"],"\xBB"=>[18,"\xF47"],"\xBC"=>[77,"\xF48"],"\xBD"=>[18,"\xF48"],"\xBE"=>[77,"\xF49"],"\xBF"=>[18,"\xF49"],"\xC0"=>[77,"\xF4\x3D"],"\xC1"=>[18,"\xF4\x3D"],"\xC2"=>[77,"\xF4A"],"\xC3"=>[18,"\xF4A"],"\xC4"=>[77,"\xF4_"],"\xC5"=>[18,"\xF4_"],"\xC6"=>[77,"\xF4b"],"\xC7"=>[18,"\xF4b"],"\xC8"=>[77,"\xF4d"],"\xC9"=>[18,"\xF4d"],"\xCA"=>[77,"\xF4f"],"\xCB"=>[18,"\xF4f"],"\xCC"=>[77,"\xF4g"],"\xCD"=>[18,"\xF4g"],"\xCE"=>[77,"\xF4h"],"\xCF"=>[18,"\xF4h"],"\xD0"=>[77,"\xF4l"],"\xD1"=>[18,"\xF4l"],"\xD2"=>[77,"\xF4m"],"\xD3"=>[18,"\xF4m"],"\xD4"=>[77,"\xF4n"],"\xD5"=>[18,"\xF4n"],"\xD6"=>[77,"\xF4p"],"\xD7"=>[18,"\xF4p"],"\xD8"=>[77,"\xF4r"],"\xD9"=>[18,"\xF4r"],"\xDA"=>[77,"\xF4u"],"\xDB"=>[18,"\xF4u"],"\xDC"=>[0,"\xF4\x3A"],"\xDD"=>[0,"\xF4B"],"\xDE"=>[0,"\xF4C"],"\xDF"=>[0,"\xF4D"],"\xE0"=>[0,"\xF4E"],"\xE1"=>[0,"\xF4F"],"\xE2"=>[0,"\xF4G"],"\xE3"=>[0,"\xF4H"],"\xE4"=>[0,"\xF4I"],"\xE5"=>[0,"\xF4J"],"\xE6"=>[0,"\xF4K"],"\xE7"=>[0,"\xF4L"],"\xE8"=>[0,"\xF4M"],"\xE9"=>[0,"\xF4N"],"\xEA"=>[0,"\xF4O"],"\xEB"=>[0,"\xF4P"],"\xEC"=>[0,"\xF4Q"],"\xED"=>[0,"\xF4R"],"\xEE"=>[0,"\xF4S"],"\xEF"=>[0,"\xF4T"],"\xF0"=>[0,"\xF4U"],"\xF1"=>[0,"\xF4V"],"\xF2"=>[0,"\xF4W"],"\xF3"=>[0,"\xF4Y"],"\xF4"=>[0,"\xF4j"],"\xF5"=>[0,"\xF4k"],"\xF6"=>[0,"\xF4q"],"\xF7"=>[0,"\xF4v"],"\xF8"=>[0,"\xF4w"],"\xF9"=>[0,"\xF4x"],"\xFA"=>[0,"\xF4y"],"\xFB"=>[0,"\xF4z"],"\xFC"=>[82,"\xF4"],"\xFD"=>[87,"\xF4"],"\xFE"=>[130,"\xF4"],"\xFF"=>[9,"\xF4"],],["\x00"=>[94,"\xF20"],"\x01"=>[76,"\xF20"],"\x02"=>[104,"\xF20"],"\x03"=>[16,"\xF20"],"\x04"=>[94,"\xF21"],"\x05"=>[76,"\xF21"],"\x06"=>[104,"\xF21"],"\x07"=>[16,"\xF21"],"\x08"=>[94,"\xF22"],"\x09"=>[76,"\xF22"],"\x0A"=>[104,"\xF22"],"\x0B"=>[16,"\xF22"],"\x0C"=>[94,"\xF2a"],"\x0D"=>[76,"\xF2a"],"\x0E"=>[104,"\xF2a"],"\x0F"=>[16,"\xF2a"],"\x10"=>[94,"\xF2c"],"\x11"=>[76,"\xF2c"],"\x12"=>[104,"\xF2c"],"\x13"=>[16,"\xF2c"],"\x14"=>[94,"\xF2e"],"\x15"=>[76,"\xF2e"],"\x16"=>[104,"\xF2e"],"\x17"=>[16,"\xF2e"],"\x18"=>[94,"\xF2i"],"\x19"=>[76,"\xF2i"],"\x1A"=>[104,"\xF2i"],"\x1B"=>[16,"\xF2i"],"\x1C"=>[94,"\xF2o"],"\x1D"=>[76,"\xF2o"],"\x1E"=>[104,"\xF2o"],"\x1F"=>[16,"\xF2o"],"\x20"=>[94,"\xF2s"],"\x21"=>[76,"\xF2s"],"\x22"=>[104,"\xF2s"],"\x23"=>[16,"\xF2s"],"\x24"=>[94,"\xF2t"],"\x25"=>[76,"\xF2t"],"\x26"=>[104,"\xF2t"],"\x27"=>[16,"\xF2t"],"\x28"=>[77,"\xF2\x20"],"\x29"=>[18,"\xF2\x20"],"\x2A"=>[77,"\xF2\x25"],"\x2B"=>[18,"\xF2\x25"],"\x2C"=>[77,"\xF2-"],"-"=>[18,"\xF2-"],"."=>[77,"\xF2."],"\x2F"=>[18,"\xF2."],[77,"\xF2\x2F"],[18,"\xF2\x2F"],[77,"\xF23"],[18,"\xF23"],[77,"\xF24"],[18,"\xF24"],[77,"\xF25"],[18,"\xF25"],[77,"\xF26"],[18,"\xF26"],"\x3A"=>[77,"\xF27"],"\x3B"=>[18,"\xF27"],"\x3C"=>[77,"\xF28"],"\x3D"=>[18,"\xF28"],"\x3E"=>[77,"\xF29"],"\x3F"=>[18,"\xF29"],"\x40"=>[77,"\xF2\x3D"],"A"=>[18,"\xF2\x3D"],"B"=>[77,"\xF2A"],"C"=>[18,"\xF2A"],"D"=>[77,"\xF2_"],"E"=>[18,"\xF2_"],"F"=>[77,"\xF2b"],"G"=>[18,"\xF2b"],"H"=>[77,"\xF2d"],"I"=>[18,"\xF2d"],"J"=>[77,"\xF2f"],"K"=>[18,"\xF2f"],"L"=>[77,"\xF2g"],"M"=>[18,"\xF2g"],"N"=>[77,"\xF2h"],"O"=>[18,"\xF2h"],"P"=>[77,"\xF2l"],"Q"=>[18,"\xF2l"],"R"=>[77,"\xF2m"],"S"=>[18,"\xF2m"],"T"=>[77,"\xF2n"],"U"=>[18,"\xF2n"],"V"=>[77,"\xF2p"],"W"=>[18,"\xF2p"],"X"=>[77,"\xF2r"],"Y"=>[18,"\xF2r"],"Z"=>[77,"\xF2u"],"\x5B"=>[18,"\xF2u"],"\x5C"=>[0,"\xF2\x3A"],"\x5D"=>[0,"\xF2B"],"\x5E"=>[0,"\xF2C"],"_"=>[0,"\xF2D"],"\x60"=>[0,"\xF2E"],"a"=>[0,"\xF2F"],"b"=>[0,"\xF2G"],"c"=>[0,"\xF2H"],"d"=>[0,"\xF2I"],"e"=>[0,"\xF2J"],"f"=>[0,"\xF2K"],"g"=>[0,"\xF2L"],"h"=>[0,"\xF2M"],"i"=>[0,"\xF2N"],"j"=>[0,"\xF2O"],"k"=>[0,"\xF2P"],"l"=>[0,"\xF2Q"],"m"=>[0,"\xF2R"],"n"=>[0,"\xF2S"],"o"=>[0,"\xF2T"],"p"=>[0,"\xF2U"],"q"=>[0,"\xF2V"],"r"=>[0,"\xF2W"],"s"=>[0,"\xF2Y"],"t"=>[0,"\xF2j"],"u"=>[0,"\xF2k"],"v"=>[0,"\xF2q"],"w"=>[0,"\xF2v"],"x"=>[0,"\xF2w"],"y"=>[0,"\xF2x"],"z"=>[0,"\xF2y"],"\x7B"=>[0,"\xF2z"],"\x7C"=>[82,"\xF2"],"\x7D"=>[87,"\xF2"],"~"=>[130,"\xF2"],"\x7F"=>[9,"\xF2"],"\x80"=>[94,"\xF30"],"\x81"=>[76,"\xF30"],"\x82"=>[104,"\xF30"],"\x83"=>[16,"\xF30"],"\x84"=>[94,"\xF31"],"\x85"=>[76,"\xF31"],"\x86"=>[104,"\xF31"],"\x87"=>[16,"\xF31"],"\x88"=>[94,"\xF32"],"\x89"=>[76,"\xF32"],"\x8A"=>[104,"\xF32"],"\x8B"=>[16,"\xF32"],"\x8C"=>[94,"\xF3a"],"\x8D"=>[76,"\xF3a"],"\x8E"=>[104,"\xF3a"],"\x8F"=>[16,"\xF3a"],"\x90"=>[94,"\xF3c"],"\x91"=>[76,"\xF3c"],"\x92"=>[104,"\xF3c"],"\x93"=>[16,"\xF3c"],"\x94"=>[94,"\xF3e"],"\x95"=>[76,"\xF3e"],"\x96"=>[104,"\xF3e"],"\x97"=>[16,"\xF3e"],"\x98"=>[94,"\xF3i"],"\x99"=>[76,"\xF3i"],"\x9A"=>[104,"\xF3i"],"\x9B"=>[16,"\xF3i"],"\x9C"=>[94,"\xF3o"],"\x9D"=>[76,"\xF3o"],"\x9E"=>[104,"\xF3o"],"\x9F"=>[16,"\xF3o"],"\xA0"=>[94,"\xF3s"],"\xA1"=>[76,"\xF3s"],"\xA2"=>[104,"\xF3s"],"\xA3"=>[16,"\xF3s"],"\xA4"=>[94,"\xF3t"],"\xA5"=>[76,"\xF3t"],"\xA6"=>[104,"\xF3t"],"\xA7"=>[16,"\xF3t"],"\xA8"=>[77,"\xF3\x20"],"\xA9"=>[18,"\xF3\x20"],"\xAA"=>[77,"\xF3\x25"],"\xAB"=>[18,"\xF3\x25"],"\xAC"=>[77,"\xF3-"],"\xAD"=>[18,"\xF3-"],"\xAE"=>[77,"\xF3."],"\xAF"=>[18,"\xF3."],"\xB0"=>[77,"\xF3\x2F"],"\xB1"=>[18,"\xF3\x2F"],"\xB2"=>[77,"\xF33"],"\xB3"=>[18,"\xF33"],"\xB4"=>[77,"\xF34"],"\xB5"=>[18,"\xF34"],"\xB6"=>[77,"\xF35"],"\xB7"=>[18,"\xF35"],"\xB8"=>[77,"\xF36"],"\xB9"=>[18,"\xF36"],"\xBA"=>[77,"\xF37"],"\xBB"=>[18,"\xF37"],"\xBC"=>[77,"\xF38"],"\xBD"=>[18,"\xF38"],"\xBE"=>[77,"\xF39"],"\xBF"=>[18,"\xF39"],"\xC0"=>[77,"\xF3\x3D"],"\xC1"=>[18,"\xF3\x3D"],"\xC2"=>[77,"\xF3A"],"\xC3"=>[18,"\xF3A"],"\xC4"=>[77,"\xF3_"],"\xC5"=>[18,"\xF3_"],"\xC6"=>[77,"\xF3b"],"\xC7"=>[18,"\xF3b"],"\xC8"=>[77,"\xF3d"],"\xC9"=>[18,"\xF3d"],"\xCA"=>[77,"\xF3f"],"\xCB"=>[18,"\xF3f"],"\xCC"=>[77,"\xF3g"],"\xCD"=>[18,"\xF3g"],"\xCE"=>[77,"\xF3h"],"\xCF"=>[18,"\xF3h"],"\xD0"=>[77,"\xF3l"],"\xD1"=>[18,"\xF3l"],"\xD2"=>[77,"\xF3m"],"\xD3"=>[18,"\xF3m"],"\xD4"=>[77,"\xF3n"],"\xD5"=>[18,"\xF3n"],"\xD6"=>[77,"\xF3p"],"\xD7"=>[18,"\xF3p"],"\xD8"=>[77,"\xF3r"],"\xD9"=>[18,"\xF3r"],"\xDA"=>[77,"\xF3u"],"\xDB"=>[18,"\xF3u"],"\xDC"=>[0,"\xF3\x3A"],"\xDD"=>[0,"\xF3B"],"\xDE"=>[0,"\xF3C"],"\xDF"=>[0,"\xF3D"],"\xE0"=>[0,"\xF3E"],"\xE1"=>[0,"\xF3F"],"\xE2"=>[0,"\xF3G"],"\xE3"=>[0,"\xF3H"],"\xE4"=>[0,"\xF3I"],"\xE5"=>[0,"\xF3J"],"\xE6"=>[0,"\xF3K"],"\xE7"=>[0,"\xF3L"],"\xE8"=>[0,"\xF3M"],"\xE9"=>[0,"\xF3N"],"\xEA"=>[0,"\xF3O"],"\xEB"=>[0,"\xF3P"],"\xEC"=>[0,"\xF3Q"],"\xED"=>[0,"\xF3R"],"\xEE"=>[0,"\xF3S"],"\xEF"=>[0,"\xF3T"],"\xF0"=>[0,"\xF3U"],"\xF1"=>[0,"\xF3V"],"\xF2"=>[0,"\xF3W"],"\xF3"=>[0,"\xF3Y"],"\xF4"=>[0,"\xF3j"],"\xF5"=>[0,"\xF3k"],"\xF6"=>[0,"\xF3q"],"\xF7"=>[0,"\xF3v"],"\xF8"=>[0,"\xF3w"],"\xF9"=>[0,"\xF3x"],"\xFA"=>[0,"\xF3y"],"\xFB"=>[0,"\xF3z"],"\xFC"=>[82,"\xF3"],"\xFD"=>[87,"\xF3"],"\xFE"=>[130,"\xF3"],"\xFF"=>[9,"\xF3"],],["\x00"=>[0,"\xF50"],"\x01"=>[0,"\xF51"],"\x02"=>[0,"\xF52"],"\x03"=>[0,"\xF5a"],"\x04"=>[0,"\xF5c"],"\x05"=>[0,"\xF5e"],"\x06"=>[0,"\xF5i"],"\x07"=>[0,"\xF5o"],"\x08"=>[0,"\xF5s"],"\x09"=>[0,"\xF5t"],"\x0A"=>[73,"\xF5"],"\x0B"=>[88,"\xF5"],"\x0C"=>[89,"\xF5"],"\x0D"=>[96,"\xF5"],"\x0E"=>[97,"\xF5"],"\x0F"=>[99,"\xF5"],"\x10"=>[106,"\xF5"],"\x11"=>[136,"\xF5"],"\x12"=>[139,"\xF5"],"\x13"=>[141,"\xF5"],"\x14"=>[145,"\xF5"],"\x15"=>[147,"\xF5"],"\x16"=>[149,"\xF5"],"\x17"=>[101,"\xF5"],"\x18"=>[112,"\xF5"],"\x19"=>[117,"\xF5"],"\x1A"=>[120,"\xF5"],"\x1B"=>[124,"\xF5"],"\x1C"=>[127,"\xF5"],"\x1D"=>[144,"\xF5"],"\x1E"=>[152,"\xF5"],"\x1F"=>[11,"\xF5"],"\x20"=>[0,"\xF60"],"\x21"=>[0,"\xF61"],"\x22"=>[0,"\xF62"],"\x23"=>[0,"\xF6a"],"\x24"=>[0,"\xF6c"],"\x25"=>[0,"\xF6e"],"\x26"=>[0,"\xF6i"],"\x27"=>[0,"\xF6o"],"\x28"=>[0,"\xF6s"],"\x29"=>[0,"\xF6t"],"\x2A"=>[73,"\xF6"],"\x2B"=>[88,"\xF6"],"\x2C"=>[89,"\xF6"],"-"=>[96,"\xF6"],"."=>[97,"\xF6"],"\x2F"=>[99,"\xF6"],[106,"\xF6"],[136,"\xF6"],[139,"\xF6"],[141,"\xF6"],[145,"\xF6"],[147,"\xF6"],[149,"\xF6"],[101,"\xF6"],[112,"\xF6"],[117,"\xF6"],"\x3A"=>[120,"\xF6"],"\x3B"=>[124,"\xF6"],"\x3C"=>[127,"\xF6"],"\x3D"=>[144,"\xF6"],"\x3E"=>[152,"\xF6"],"\x3F"=>[11,"\xF6"],"\x40"=>[0,"\xF70"],"A"=>[0,"\xF71"],"B"=>[0,"\xF72"],"C"=>[0,"\xF7a"],"D"=>[0,"\xF7c"],"E"=>[0,"\xF7e"],"F"=>[0,"\xF7i"],"G"=>[0,"\xF7o"],"H"=>[0,"\xF7s"],"I"=>[0,"\xF7t"],"J"=>[73,"\xF7"],"K"=>[88,"\xF7"],"L"=>[89,"\xF7"],"M"=>[96,"\xF7"],"N"=>[97,"\xF7"],"O"=>[99,"\xF7"],"P"=>[106,"\xF7"],"Q"=>[136,"\xF7"],"R"=>[139,"\xF7"],"S"=>[141,"\xF7"],"T"=>[145,"\xF7"],"U"=>[147,"\xF7"],"V"=>[149,"\xF7"],"W"=>[101,"\xF7"],"X"=>[112,"\xF7"],"Y"=>[117,"\xF7"],"Z"=>[120,"\xF7"],"\x5B"=>[124,"\xF7"],"\x5C"=>[127,"\xF7"],"\x5D"=>[144,"\xF7"],"\x5E"=>[152,"\xF7"],"_"=>[11,"\xF7"],"\x60"=>[0,"\xF80"],"a"=>[0,"\xF81"],"b"=>[0,"\xF82"],"c"=>[0,"\xF8a"],"d"=>[0,"\xF8c"],"e"=>[0,"\xF8e"],"f"=>[0,"\xF8i"],"g"=>[0,"\xF8o"],"h"=>[0,"\xF8s"],"i"=>[0,"\xF8t"],"j"=>[73,"\xF8"],"k"=>[88,"\xF8"],"l"=>[89,"\xF8"],"m"=>[96,"\xF8"],"n"=>[97,"\xF8"],"o"=>[99,"\xF8"],"p"=>[106,"\xF8"],"q"=>[136,"\xF8"],"r"=>[139,"\xF8"],"s"=>[141,"\xF8"],"t"=>[145,"\xF8"],"u"=>[147,"\xF8"],"v"=>[149,"\xF8"],"w"=>[101,"\xF8"],"x"=>[112,"\xF8"],"y"=>[117,"\xF8"],"z"=>[120,"\xF8"],"\x7B"=>[124,"\xF8"],"\x7C"=>[127,"\xF8"],"\x7D"=>[144,"\xF8"],"~"=>[152,"\xF8"],"\x7F"=>[11,"\xF8"],"\x80"=>[0,"\xFA0"],"\x81"=>[0,"\xFA1"],"\x82"=>[0,"\xFA2"],"\x83"=>[0,"\xFAa"],"\x84"=>[0,"\xFAc"],"\x85"=>[0,"\xFAe"],"\x86"=>[0,"\xFAi"],"\x87"=>[0,"\xFAo"],"\x88"=>[0,"\xFAs"],"\x89"=>[0,"\xFAt"],"\x8A"=>[73,"\xFA"],"\x8B"=>[88,"\xFA"],"\x8C"=>[89,"\xFA"],"\x8D"=>[96,"\xFA"],"\x8E"=>[97,"\xFA"],"\x8F"=>[99,"\xFA"],"\x90"=>[106,"\xFA"],"\x91"=>[136,"\xFA"],"\x92"=>[139,"\xFA"],"\x93"=>[141,"\xFA"],"\x94"=>[145,"\xFA"],"\x95"=>[147,"\xFA"],"\x96"=>[149,"\xFA"],"\x97"=>[101,"\xFA"],"\x98"=>[112,"\xFA"],"\x99"=>[117,"\xFA"],"\x9A"=>[120,"\xFA"],"\x9B"=>[124,"\xFA"],"\x9C"=>[127,"\xFA"],"\x9D"=>[144,"\xFA"],"\x9E"=>[152,"\xFA"],"\x9F"=>[11,"\xFA"],"\xA0"=>[0,"\xFB0"],"\xA1"=>[0,"\xFB1"],"\xA2"=>[0,"\xFB2"],"\xA3"=>[0,"\xFBa"],"\xA4"=>[0,"\xFBc"],"\xA5"=>[0,"\xFBe"],"\xA6"=>[0,"\xFBi"],"\xA7"=>[0,"\xFBo"],"\xA8"=>[0,"\xFBs"],"\xA9"=>[0,"\xFBt"],"\xAA"=>[73,"\xFB"],"\xAB"=>[88,"\xFB"],"\xAC"=>[89,"\xFB"],"\xAD"=>[96,"\xFB"],"\xAE"=>[97,"\xFB"],"\xAF"=>[99,"\xFB"],"\xB0"=>[106,"\xFB"],"\xB1"=>[136,"\xFB"],"\xB2"=>[139,"\xFB"],"\xB3"=>[141,"\xFB"],"\xB4"=>[145,"\xFB"],"\xB5"=>[147,"\xFB"],"\xB6"=>[149,"\xFB"],"\xB7"=>[101,"\xFB"],"\xB8"=>[112,"\xFB"],"\xB9"=>[117,"\xFB"],"\xBA"=>[120,"\xFB"],"\xBB"=>[124,"\xFB"],"\xBC"=>[127,"\xFB"],"\xBD"=>[144,"\xFB"],"\xBE"=>[152,"\xFB"],"\xBF"=>[11,"\xFB"],"\xC0"=>[0,"\xFC0"],"\xC1"=>[0,"\xFC1"],"\xC2"=>[0,"\xFC2"],"\xC3"=>[0,"\xFCa"],"\xC4"=>[0,"\xFCc"],"\xC5"=>[0,"\xFCe"],"\xC6"=>[0,"\xFCi"],"\xC7"=>[0,"\xFCo"],"\xC8"=>[0,"\xFCs"],"\xC9"=>[0,"\xFCt"],"\xCA"=>[73,"\xFC"],"\xCB"=>[88,"\xFC"],"\xCC"=>[89,"\xFC"],"\xCD"=>[96,"\xFC"],"\xCE"=>[97,"\xFC"],"\xCF"=>[99,"\xFC"],"\xD0"=>[106,"\xFC"],"\xD1"=>[136,"\xFC"],"\xD2"=>[139,"\xFC"],"\xD3"=>[141,"\xFC"],"\xD4"=>[145,"\xFC"],"\xD5"=>[147,"\xFC"],"\xD6"=>[149,"\xFC"],"\xD7"=>[101,"\xFC"],"\xD8"=>[112,"\xFC"],"\xD9"=>[117,"\xFC"],"\xDA"=>[120,"\xFC"],"\xDB"=>[124,"\xFC"],"\xDC"=>[127,"\xFC"],"\xDD"=>[144,"\xFC"],"\xDE"=>[152,"\xFC"],"\xDF"=>[11,"\xFC"],"\xE0"=>[0,"\xFD0"],"\xE1"=>[0,"\xFD1"],"\xE2"=>[0,"\xFD2"],"\xE3"=>[0,"\xFDa"],"\xE4"=>[0,"\xFDc"],"\xE5"=>[0,"\xFDe"],"\xE6"=>[0,"\xFDi"],"\xE7"=>[0,"\xFDo"],"\xE8"=>[0,"\xFDs"],"\xE9"=>[0,"\xFDt"],"\xEA"=>[73,"\xFD"],"\xEB"=>[88,"\xFD"],"\xEC"=>[89,"\xFD"],"\xED"=>[96,"\xFD"],"\xEE"=>[97,"\xFD"],"\xEF"=>[99,"\xFD"],"\xF0"=>[106,"\xFD"],"\xF1"=>[136,"\xFD"],"\xF2"=>[139,"\xFD"],"\xF3"=>[141,"\xFD"],"\xF4"=>[145,"\xFD"],"\xF5"=>[147,"\xFD"],"\xF6"=>[149,"\xFD"],"\xF7"=>[101,"\xFD"],"\xF8"=>[112,"\xFD"],"\xF9"=>[117,"\xFD"],"\xFA"=>[120,"\xFD"],"\xFB"=>[124,"\xFD"],"\xFC"=>[127,"\xFD"],"\xFD"=>[144,"\xFD"],"\xFE"=>[152,"\xFD"],"\xFF"=>[11,"\xFD"],],["\x00"=>[94,"\xF50"],"\x01"=>[76,"\xF50"],"\x02"=>[104,"\xF50"],"\x03"=>[16,"\xF50"],"\x04"=>[94,"\xF51"],"\x05"=>[76,"\xF51"],"\x06"=>[104,"\xF51"],"\x07"=>[16,"\xF51"],"\x08"=>[94,"\xF52"],"\x09"=>[76,"\xF52"],"\x0A"=>[104,"\xF52"],"\x0B"=>[16,"\xF52"],"\x0C"=>[94,"\xF5a"],"\x0D"=>[76,"\xF5a"],"\x0E"=>[104,"\xF5a"],"\x0F"=>[16,"\xF5a"],"\x10"=>[94,"\xF5c"],"\x11"=>[76,"\xF5c"],"\x12"=>[104,"\xF5c"],"\x13"=>[16,"\xF5c"],"\x14"=>[94,"\xF5e"],"\x15"=>[76,"\xF5e"],"\x16"=>[104,"\xF5e"],"\x17"=>[16,"\xF5e"],"\x18"=>[94,"\xF5i"],"\x19"=>[76,"\xF5i"],"\x1A"=>[104,"\xF5i"],"\x1B"=>[16,"\xF5i"],"\x1C"=>[94,"\xF5o"],"\x1D"=>[76,"\xF5o"],"\x1E"=>[104,"\xF5o"],"\x1F"=>[16,"\xF5o"],"\x20"=>[94,"\xF5s"],"\x21"=>[76,"\xF5s"],"\x22"=>[104,"\xF5s"],"\x23"=>[16,"\xF5s"],"\x24"=>[94,"\xF5t"],"\x25"=>[76,"\xF5t"],"\x26"=>[104,"\xF5t"],"\x27"=>[16,"\xF5t"],"\x28"=>[77,"\xF5\x20"],"\x29"=>[18,"\xF5\x20"],"\x2A"=>[77,"\xF5\x25"],"\x2B"=>[18,"\xF5\x25"],"\x2C"=>[77,"\xF5-"],"-"=>[18,"\xF5-"],"."=>[77,"\xF5."],"\x2F"=>[18,"\xF5."],[77,"\xF5\x2F"],[18,"\xF5\x2F"],[77,"\xF53"],[18,"\xF53"],[77,"\xF54"],[18,"\xF54"],[77,"\xF55"],[18,"\xF55"],[77,"\xF56"],[18,"\xF56"],"\x3A"=>[77,"\xF57"],"\x3B"=>[18,"\xF57"],"\x3C"=>[77,"\xF58"],"\x3D"=>[18,"\xF58"],"\x3E"=>[77,"\xF59"],"\x3F"=>[18,"\xF59"],"\x40"=>[77,"\xF5\x3D"],"A"=>[18,"\xF5\x3D"],"B"=>[77,"\xF5A"],"C"=>[18,"\xF5A"],"D"=>[77,"\xF5_"],"E"=>[18,"\xF5_"],"F"=>[77,"\xF5b"],"G"=>[18,"\xF5b"],"H"=>[77,"\xF5d"],"I"=>[18,"\xF5d"],"J"=>[77,"\xF5f"],"K"=>[18,"\xF5f"],"L"=>[77,"\xF5g"],"M"=>[18,"\xF5g"],"N"=>[77,"\xF5h"],"O"=>[18,"\xF5h"],"P"=>[77,"\xF5l"],"Q"=>[18,"\xF5l"],"R"=>[77,"\xF5m"],"S"=>[18,"\xF5m"],"T"=>[77,"\xF5n"],"U"=>[18,"\xF5n"],"V"=>[77,"\xF5p"],"W"=>[18,"\xF5p"],"X"=>[77,"\xF5r"],"Y"=>[18,"\xF5r"],"Z"=>[77,"\xF5u"],"\x5B"=>[18,"\xF5u"],"\x5C"=>[0,"\xF5\x3A"],"\x5D"=>[0,"\xF5B"],"\x5E"=>[0,"\xF5C"],"_"=>[0,"\xF5D"],"\x60"=>[0,"\xF5E"],"a"=>[0,"\xF5F"],"b"=>[0,"\xF5G"],"c"=>[0,"\xF5H"],"d"=>[0,"\xF5I"],"e"=>[0,"\xF5J"],"f"=>[0,"\xF5K"],"g"=>[0,"\xF5L"],"h"=>[0,"\xF5M"],"i"=>[0,"\xF5N"],"j"=>[0,"\xF5O"],"k"=>[0,"\xF5P"],"l"=>[0,"\xF5Q"],"m"=>[0,"\xF5R"],"n"=>[0,"\xF5S"],"o"=>[0,"\xF5T"],"p"=>[0,"\xF5U"],"q"=>[0,"\xF5V"],"r"=>[0,"\xF5W"],"s"=>[0,"\xF5Y"],"t"=>[0,"\xF5j"],"u"=>[0,"\xF5k"],"v"=>[0,"\xF5q"],"w"=>[0,"\xF5v"],"x"=>[0,"\xF5w"],"y"=>[0,"\xF5x"],"z"=>[0,"\xF5y"],"\x7B"=>[0,"\xF5z"],"\x7C"=>[82,"\xF5"],"\x7D"=>[87,"\xF5"],"~"=>[130,"\xF5"],"\x7F"=>[9,"\xF5"],"\x80"=>[94,"\xF60"],"\x81"=>[76,"\xF60"],"\x82"=>[104,"\xF60"],"\x83"=>[16,"\xF60"],"\x84"=>[94,"\xF61"],"\x85"=>[76,"\xF61"],"\x86"=>[104,"\xF61"],"\x87"=>[16,"\xF61"],"\x88"=>[94,"\xF62"],"\x89"=>[76,"\xF62"],"\x8A"=>[104,"\xF62"],"\x8B"=>[16,"\xF62"],"\x8C"=>[94,"\xF6a"],"\x8D"=>[76,"\xF6a"],"\x8E"=>[104,"\xF6a"],"\x8F"=>[16,"\xF6a"],"\x90"=>[94,"\xF6c"],"\x91"=>[76,"\xF6c"],"\x92"=>[104,"\xF6c"],"\x93"=>[16,"\xF6c"],"\x94"=>[94,"\xF6e"],"\x95"=>[76,"\xF6e"],"\x96"=>[104,"\xF6e"],"\x97"=>[16,"\xF6e"],"\x98"=>[94,"\xF6i"],"\x99"=>[76,"\xF6i"],"\x9A"=>[104,"\xF6i"],"\x9B"=>[16,"\xF6i"],"\x9C"=>[94,"\xF6o"],"\x9D"=>[76,"\xF6o"],"\x9E"=>[104,"\xF6o"],"\x9F"=>[16,"\xF6o"],"\xA0"=>[94,"\xF6s"],"\xA1"=>[76,"\xF6s"],"\xA2"=>[104,"\xF6s"],"\xA3"=>[16,"\xF6s"],"\xA4"=>[94,"\xF6t"],"\xA5"=>[76,"\xF6t"],"\xA6"=>[104,"\xF6t"],"\xA7"=>[16,"\xF6t"],"\xA8"=>[77,"\xF6\x20"],"\xA9"=>[18,"\xF6\x20"],"\xAA"=>[77,"\xF6\x25"],"\xAB"=>[18,"\xF6\x25"],"\xAC"=>[77,"\xF6-"],"\xAD"=>[18,"\xF6-"],"\xAE"=>[77,"\xF6."],"\xAF"=>[18,"\xF6."],"\xB0"=>[77,"\xF6\x2F"],"\xB1"=>[18,"\xF6\x2F"],"\xB2"=>[77,"\xF63"],"\xB3"=>[18,"\xF63"],"\xB4"=>[77,"\xF64"],"\xB5"=>[18,"\xF64"],"\xB6"=>[77,"\xF65"],"\xB7"=>[18,"\xF65"],"\xB8"=>[77,"\xF66"],"\xB9"=>[18,"\xF66"],"\xBA"=>[77,"\xF67"],"\xBB"=>[18,"\xF67"],"\xBC"=>[77,"\xF68"],"\xBD"=>[18,"\xF68"],"\xBE"=>[77,"\xF69"],"\xBF"=>[18,"\xF69"],"\xC0"=>[77,"\xF6\x3D"],"\xC1"=>[18,"\xF6\x3D"],"\xC2"=>[77,"\xF6A"],"\xC3"=>[18,"\xF6A"],"\xC4"=>[77,"\xF6_"],"\xC5"=>[18,"\xF6_"],"\xC6"=>[77,"\xF6b"],"\xC7"=>[18,"\xF6b"],"\xC8"=>[77,"\xF6d"],"\xC9"=>[18,"\xF6d"],"\xCA"=>[77,"\xF6f"],"\xCB"=>[18,"\xF6f"],"\xCC"=>[77,"\xF6g"],"\xCD"=>[18,"\xF6g"],"\xCE"=>[77,"\xF6h"],"\xCF"=>[18,"\xF6h"],"\xD0"=>[77,"\xF6l"],"\xD1"=>[18,"\xF6l"],"\xD2"=>[77,"\xF6m"],"\xD3"=>[18,"\xF6m"],"\xD4"=>[77,"\xF6n"],"\xD5"=>[18,"\xF6n"],"\xD6"=>[77,"\xF6p"],"\xD7"=>[18,"\xF6p"],"\xD8"=>[77,"\xF6r"],"\xD9"=>[18,"\xF6r"],"\xDA"=>[77,"\xF6u"],"\xDB"=>[18,"\xF6u"],"\xDC"=>[0,"\xF6\x3A"],"\xDD"=>[0,"\xF6B"],"\xDE"=>[0,"\xF6C"],"\xDF"=>[0,"\xF6D"],"\xE0"=>[0,"\xF6E"],"\xE1"=>[0,"\xF6F"],"\xE2"=>[0,"\xF6G"],"\xE3"=>[0,"\xF6H"],"\xE4"=>[0,"\xF6I"],"\xE5"=>[0,"\xF6J"],"\xE6"=>[0,"\xF6K"],"\xE7"=>[0,"\xF6L"],"\xE8"=>[0,"\xF6M"],"\xE9"=>[0,"\xF6N"],"\xEA"=>[0,"\xF6O"],"\xEB"=>[0,"\xF6P"],"\xEC"=>[0,"\xF6Q"],"\xED"=>[0,"\xF6R"],"\xEE"=>[0,"\xF6S"],"\xEF"=>[0,"\xF6T"],"\xF0"=>[0,"\xF6U"],"\xF1"=>[0,"\xF6V"],"\xF2"=>[0,"\xF6W"],"\xF3"=>[0,"\xF6Y"],"\xF4"=>[0,"\xF6j"],"\xF5"=>[0,"\xF6k"],"\xF6"=>[0,"\xF6q"],"\xF7"=>[0,"\xF6v"],"\xF8"=>[0,"\xF6w"],"\xF9"=>[0,"\xF6x"],"\xFA"=>[0,"\xF6y"],"\xFB"=>[0,"\xF6z"],"\xFC"=>[82,"\xF6"],"\xFD"=>[87,"\xF6"],"\xFE"=>[130,"\xF6"],"\xFF"=>[9,"\xF6"],],["\x00"=>[77,"\xF50"],"\x01"=>[18,"\xF50"],"\x02"=>[77,"\xF51"],"\x03"=>[18,"\xF51"],"\x04"=>[77,"\xF52"],"\x05"=>[18,"\xF52"],"\x06"=>[77,"\xF5a"],"\x07"=>[18,"\xF5a"],"\x08"=>[77,"\xF5c"],"\x09"=>[18,"\xF5c"],"\x0A"=>[77,"\xF5e"],"\x0B"=>[18,"\xF5e"],"\x0C"=>[77,"\xF5i"],"\x0D"=>[18,"\xF5i"],"\x0E"=>[77,"\xF5o"],"\x0F"=>[18,"\xF5o"],"\x10"=>[77,"\xF5s"],"\x11"=>[18,"\xF5s"],"\x12"=>[77,"\xF5t"],"\x13"=>[18,"\xF5t"],"\x14"=>[0,"\xF5\x20"],"\x15"=>[0,"\xF5\x25"],"\x16"=>[0,"\xF5-"],"\x17"=>[0,"\xF5."],"\x18"=>[0,"\xF5\x2F"],"\x19"=>[0,"\xF53"],"\x1A"=>[0,"\xF54"],"\x1B"=>[0,"\xF55"],"\x1C"=>[0,"\xF56"],"\x1D"=>[0,"\xF57"],"\x1E"=>[0,"\xF58"],"\x1F"=>[0,"\xF59"],"\x20"=>[0,"\xF5\x3D"],"\x21"=>[0,"\xF5A"],"\x22"=>[0,"\xF5_"],"\x23"=>[0,"\xF5b"],"\x24"=>[0,"\xF5d"],"\x25"=>[0,"\xF5f"],"\x26"=>[0,"\xF5g"],"\x27"=>[0,"\xF5h"],"\x28"=>[0,"\xF5l"],"\x29"=>[0,"\xF5m"],"\x2A"=>[0,"\xF5n"],"\x2B"=>[0,"\xF5p"],"\x2C"=>[0,"\xF5r"],"-"=>[0,"\xF5u"],"."=>[100,"\xF5"],"\x2F"=>[110,"\xF5"],[111,"\xF5"],[115,"\xF5"],[116,"\xF5"],[118,"\xF5"],[119,"\xF5"],[122,"\xF5"],[123,"\xF5"],[125,"\xF5"],[126,"\xF5"],[129,"\xF5"],"\x3A"=>[143,"\xF5"],"\x3B"=>[148,"\xF5"],"\x3C"=>[151,"\xF5"],"\x3D"=>[153,"\xF5"],"\x3E"=>[83,"\xF5"],"\x3F"=>[10,"\xF5"],"\x40"=>[77,"\xF60"],"A"=>[18,"\xF60"],"B"=>[77,"\xF61"],"C"=>[18,"\xF61"],"D"=>[77,"\xF62"],"E"=>[18,"\xF62"],"F"=>[77,"\xF6a"],"G"=>[18,"\xF6a"],"H"=>[77,"\xF6c"],"I"=>[18,"\xF6c"],"J"=>[77,"\xF6e"],"K"=>[18,"\xF6e"],"L"=>[77,"\xF6i"],"M"=>[18,"\xF6i"],"N"=>[77,"\xF6o"],"O"=>[18,"\xF6o"],"P"=>[77,"\xF6s"],"Q"=>[18,"\xF6s"],"R"=>[77,"\xF6t"],"S"=>[18,"\xF6t"],"T"=>[0,"\xF6\x20"],"U"=>[0,"\xF6\x25"],"V"=>[0,"\xF6-"],"W"=>[0,"\xF6."],"X"=>[0,"\xF6\x2F"],"Y"=>[0,"\xF63"],"Z"=>[0,"\xF64"],"\x5B"=>[0,"\xF65"],"\x5C"=>[0,"\xF66"],"\x5D"=>[0,"\xF67"],"\x5E"=>[0,"\xF68"],"_"=>[0,"\xF69"],"\x60"=>[0,"\xF6\x3D"],"a"=>[0,"\xF6A"],"b"=>[0,"\xF6_"],"c"=>[0,"\xF6b"],"d"=>[0,"\xF6d"],"e"=>[0,"\xF6f"],"f"=>[0,"\xF6g"],"g"=>[0,"\xF6h"],"h"=>[0,"\xF6l"],"i"=>[0,"\xF6m"],"j"=>[0,"\xF6n"],"k"=>[0,"\xF6p"],"l"=>[0,"\xF6r"],"m"=>[0,"\xF6u"],"n"=>[100,"\xF6"],"o"=>[110,"\xF6"],"p"=>[111,"\xF6"],"q"=>[115,"\xF6"],"r"=>[116,"\xF6"],"s"=>[118,"\xF6"],"t"=>[119,"\xF6"],"u"=>[122,"\xF6"],"v"=>[123,"\xF6"],"w"=>[125,"\xF6"],"x"=>[126,"\xF6"],"y"=>[129,"\xF6"],"z"=>[143,"\xF6"],"\x7B"=>[148,"\xF6"],"\x7C"=>[151,"\xF6"],"\x7D"=>[153,"\xF6"],"~"=>[83,"\xF6"],"\x7F"=>[10,"\xF6"],"\x80"=>[77,"\xF70"],"\x81"=>[18,"\xF70"],"\x82"=>[77,"\xF71"],"\x83"=>[18,"\xF71"],"\x84"=>[77,"\xF72"],"\x85"=>[18,"\xF72"],"\x86"=>[77,"\xF7a"],"\x87"=>[18,"\xF7a"],"\x88"=>[77,"\xF7c"],"\x89"=>[18,"\xF7c"],"\x8A"=>[77,"\xF7e"],"\x8B"=>[18,"\xF7e"],"\x8C"=>[77,"\xF7i"],"\x8D"=>[18,"\xF7i"],"\x8E"=>[77,"\xF7o"],"\x8F"=>[18,"\xF7o"],"\x90"=>[77,"\xF7s"],"\x91"=>[18,"\xF7s"],"\x92"=>[77,"\xF7t"],"\x93"=>[18,"\xF7t"],"\x94"=>[0,"\xF7\x20"],"\x95"=>[0,"\xF7\x25"],"\x96"=>[0,"\xF7-"],"\x97"=>[0,"\xF7."],"\x98"=>[0,"\xF7\x2F"],"\x99"=>[0,"\xF73"],"\x9A"=>[0,"\xF74"],"\x9B"=>[0,"\xF75"],"\x9C"=>[0,"\xF76"],"\x9D"=>[0,"\xF77"],"\x9E"=>[0,"\xF78"],"\x9F"=>[0,"\xF79"],"\xA0"=>[0,"\xF7\x3D"],"\xA1"=>[0,"\xF7A"],"\xA2"=>[0,"\xF7_"],"\xA3"=>[0,"\xF7b"],"\xA4"=>[0,"\xF7d"],"\xA5"=>[0,"\xF7f"],"\xA6"=>[0,"\xF7g"],"\xA7"=>[0,"\xF7h"],"\xA8"=>[0,"\xF7l"],"\xA9"=>[0,"\xF7m"],"\xAA"=>[0,"\xF7n"],"\xAB"=>[0,"\xF7p"],"\xAC"=>[0,"\xF7r"],"\xAD"=>[0,"\xF7u"],"\xAE"=>[100,"\xF7"],"\xAF"=>[110,"\xF7"],"\xB0"=>[111,"\xF7"],"\xB1"=>[115,"\xF7"],"\xB2"=>[116,"\xF7"],"\xB3"=>[118,"\xF7"],"\xB4"=>[119,"\xF7"],"\xB5"=>[122,"\xF7"],"\xB6"=>[123,"\xF7"],"\xB7"=>[125,"\xF7"],"\xB8"=>[126,"\xF7"],"\xB9"=>[129,"\xF7"],"\xBA"=>[143,"\xF7"],"\xBB"=>[148,"\xF7"],"\xBC"=>[151,"\xF7"],"\xBD"=>[153,"\xF7"],"\xBE"=>[83,"\xF7"],"\xBF"=>[10,"\xF7"],"\xC0"=>[77,"\xF80"],"\xC1"=>[18,"\xF80"],"\xC2"=>[77,"\xF81"],"\xC3"=>[18,"\xF81"],"\xC4"=>[77,"\xF82"],"\xC5"=>[18,"\xF82"],"\xC6"=>[77,"\xF8a"],"\xC7"=>[18,"\xF8a"],"\xC8"=>[77,"\xF8c"],"\xC9"=>[18,"\xF8c"],"\xCA"=>[77,"\xF8e"],"\xCB"=>[18,"\xF8e"],"\xCC"=>[77,"\xF8i"],"\xCD"=>[18,"\xF8i"],"\xCE"=>[77,"\xF8o"],"\xCF"=>[18,"\xF8o"],"\xD0"=>[77,"\xF8s"],"\xD1"=>[18,"\xF8s"],"\xD2"=>[77,"\xF8t"],"\xD3"=>[18,"\xF8t"],"\xD4"=>[0,"\xF8\x20"],"\xD5"=>[0,"\xF8\x25"],"\xD6"=>[0,"\xF8-"],"\xD7"=>[0,"\xF8."],"\xD8"=>[0,"\xF8\x2F"],"\xD9"=>[0,"\xF83"],"\xDA"=>[0,"\xF84"],"\xDB"=>[0,"\xF85"],"\xDC"=>[0,"\xF86"],"\xDD"=>[0,"\xF87"],"\xDE"=>[0,"\xF88"],"\xDF"=>[0,"\xF89"],"\xE0"=>[0,"\xF8\x3D"],"\xE1"=>[0,"\xF8A"],"\xE2"=>[0,"\xF8_"],"\xE3"=>[0,"\xF8b"],"\xE4"=>[0,"\xF8d"],"\xE5"=>[0,"\xF8f"],"\xE6"=>[0,"\xF8g"],"\xE7"=>[0,"\xF8h"],"\xE8"=>[0,"\xF8l"],"\xE9"=>[0,"\xF8m"],"\xEA"=>[0,"\xF8n"],"\xEB"=>[0,"\xF8p"],"\xEC"=>[0,"\xF8r"],"\xED"=>[0,"\xF8u"],"\xEE"=>[100,"\xF8"],"\xEF"=>[110,"\xF8"],"\xF0"=>[111,"\xF8"],"\xF1"=>[115,"\xF8"],"\xF2"=>[116,"\xF8"],"\xF3"=>[118,"\xF8"],"\xF4"=>[119,"\xF8"],"\xF5"=>[122,"\xF8"],"\xF6"=>[123,"\xF8"],"\xF7"=>[125,"\xF8"],"\xF8"=>[126,"\xF8"],"\xF9"=>[129,"\xF8"],"\xFA"=>[143,"\xF8"],"\xFB"=>[148,"\xF8"],"\xFC"=>[151,"\xF8"],"\xFD"=>[153,"\xF8"],"\xFE"=>[83,"\xF8"],"\xFF"=>[10,"\xF8"],],["\x00"=>[94,"\xF70"],"\x01"=>[76,"\xF70"],"\x02"=>[104,"\xF70"],"\x03"=>[16,"\xF70"],"\x04"=>[94,"\xF71"],"\x05"=>[76,"\xF71"],"\x06"=>[104,"\xF71"],"\x07"=>[16,"\xF71"],"\x08"=>[94,"\xF72"],"\x09"=>[76,"\xF72"],"\x0A"=>[104,"\xF72"],"\x0B"=>[16,"\xF72"],"\x0C"=>[94,"\xF7a"],"\x0D"=>[76,"\xF7a"],"\x0E"=>[104,"\xF7a"],"\x0F"=>[16,"\xF7a"],"\x10"=>[94,"\xF7c"],"\x11"=>[76,"\xF7c"],"\x12"=>[104,"\xF7c"],"\x13"=>[16,"\xF7c"],"\x14"=>[94,"\xF7e"],"\x15"=>[76,"\xF7e"],"\x16"=>[104,"\xF7e"],"\x17"=>[16,"\xF7e"],"\x18"=>[94,"\xF7i"],"\x19"=>[76,"\xF7i"],"\x1A"=>[104,"\xF7i"],"\x1B"=>[16,"\xF7i"],"\x1C"=>[94,"\xF7o"],"\x1D"=>[76,"\xF7o"],"\x1E"=>[104,"\xF7o"],"\x1F"=>[16,"\xF7o"],"\x20"=>[94,"\xF7s"],"\x21"=>[76,"\xF7s"],"\x22"=>[104,"\xF7s"],"\x23"=>[16,"\xF7s"],"\x24"=>[94,"\xF7t"],"\x25"=>[76,"\xF7t"],"\x26"=>[104,"\xF7t"],"\x27"=>[16,"\xF7t"],"\x28"=>[77,"\xF7\x20"],"\x29"=>[18,"\xF7\x20"],"\x2A"=>[77,"\xF7\x25"],"\x2B"=>[18,"\xF7\x25"],"\x2C"=>[77,"\xF7-"],"-"=>[18,"\xF7-"],"."=>[77,"\xF7."],"\x2F"=>[18,"\xF7."],[77,"\xF7\x2F"],[18,"\xF7\x2F"],[77,"\xF73"],[18,"\xF73"],[77,"\xF74"],[18,"\xF74"],[77,"\xF75"],[18,"\xF75"],[77,"\xF76"],[18,"\xF76"],"\x3A"=>[77,"\xF77"],"\x3B"=>[18,"\xF77"],"\x3C"=>[77,"\xF78"],"\x3D"=>[18,"\xF78"],"\x3E"=>[77,"\xF79"],"\x3F"=>[18,"\xF79"],"\x40"=>[77,"\xF7\x3D"],"A"=>[18,"\xF7\x3D"],"B"=>[77,"\xF7A"],"C"=>[18,"\xF7A"],"D"=>[77,"\xF7_"],"E"=>[18,"\xF7_"],"F"=>[77,"\xF7b"],"G"=>[18,"\xF7b"],"H"=>[77,"\xF7d"],"I"=>[18,"\xF7d"],"J"=>[77,"\xF7f"],"K"=>[18,"\xF7f"],"L"=>[77,"\xF7g"],"M"=>[18,"\xF7g"],"N"=>[77,"\xF7h"],"O"=>[18,"\xF7h"],"P"=>[77,"\xF7l"],"Q"=>[18,"\xF7l"],"R"=>[77,"\xF7m"],"S"=>[18,"\xF7m"],"T"=>[77,"\xF7n"],"U"=>[18,"\xF7n"],"V"=>[77,"\xF7p"],"W"=>[18,"\xF7p"],"X"=>[77,"\xF7r"],"Y"=>[18,"\xF7r"],"Z"=>[77,"\xF7u"],"\x5B"=>[18,"\xF7u"],"\x5C"=>[0,"\xF7\x3A"],"\x5D"=>[0,"\xF7B"],"\x5E"=>[0,"\xF7C"],"_"=>[0,"\xF7D"],"\x60"=>[0,"\xF7E"],"a"=>[0,"\xF7F"],"b"=>[0,"\xF7G"],"c"=>[0,"\xF7H"],"d"=>[0,"\xF7I"],"e"=>[0,"\xF7J"],"f"=>[0,"\xF7K"],"g"=>[0,"\xF7L"],"h"=>[0,"\xF7M"],"i"=>[0,"\xF7N"],"j"=>[0,"\xF7O"],"k"=>[0,"\xF7P"],"l"=>[0,"\xF7Q"],"m"=>[0,"\xF7R"],"n"=>[0,"\xF7S"],"o"=>[0,"\xF7T"],"p"=>[0,"\xF7U"],"q"=>[0,"\xF7V"],"r"=>[0,"\xF7W"],"s"=>[0,"\xF7Y"],"t"=>[0,"\xF7j"],"u"=>[0,"\xF7k"],"v"=>[0,"\xF7q"],"w"=>[0,"\xF7v"],"x"=>[0,"\xF7w"],"y"=>[0,"\xF7x"],"z"=>[0,"\xF7y"],"\x7B"=>[0,"\xF7z"],"\x7C"=>[82,"\xF7"],"\x7D"=>[87,"\xF7"],"~"=>[130,"\xF7"],"\x7F"=>[9,"\xF7"],"\x80"=>[94,"\xF80"],"\x81"=>[76,"\xF80"],"\x82"=>[104,"\xF80"],"\x83"=>[16,"\xF80"],"\x84"=>[94,"\xF81"],"\x85"=>[76,"\xF81"],"\x86"=>[104,"\xF81"],"\x87"=>[16,"\xF81"],"\x88"=>[94,"\xF82"],"\x89"=>[76,"\xF82"],"\x8A"=>[104,"\xF82"],"\x8B"=>[16,"\xF82"],"\x8C"=>[94,"\xF8a"],"\x8D"=>[76,"\xF8a"],"\x8E"=>[104,"\xF8a"],"\x8F"=>[16,"\xF8a"],"\x90"=>[94,"\xF8c"],"\x91"=>[76,"\xF8c"],"\x92"=>[104,"\xF8c"],"\x93"=>[16,"\xF8c"],"\x94"=>[94,"\xF8e"],"\x95"=>[76,"\xF8e"],"\x96"=>[104,"\xF8e"],"\x97"=>[16,"\xF8e"],"\x98"=>[94,"\xF8i"],"\x99"=>[76,"\xF8i"],"\x9A"=>[104,"\xF8i"],"\x9B"=>[16,"\xF8i"],"\x9C"=>[94,"\xF8o"],"\x9D"=>[76,"\xF8o"],"\x9E"=>[104,"\xF8o"],"\x9F"=>[16,"\xF8o"],"\xA0"=>[94,"\xF8s"],"\xA1"=>[76,"\xF8s"],"\xA2"=>[104,"\xF8s"],"\xA3"=>[16,"\xF8s"],"\xA4"=>[94,"\xF8t"],"\xA5"=>[76,"\xF8t"],"\xA6"=>[104,"\xF8t"],"\xA7"=>[16,"\xF8t"],"\xA8"=>[77,"\xF8\x20"],"\xA9"=>[18,"\xF8\x20"],"\xAA"=>[77,"\xF8\x25"],"\xAB"=>[18,"\xF8\x25"],"\xAC"=>[77,"\xF8-"],"\xAD"=>[18,"\xF8-"],"\xAE"=>[77,"\xF8."],"\xAF"=>[18,"\xF8."],"\xB0"=>[77,"\xF8\x2F"],"\xB1"=>[18,"\xF8\x2F"],"\xB2"=>[77,"\xF83"],"\xB3"=>[18,"\xF83"],"\xB4"=>[77,"\xF84"],"\xB5"=>[18,"\xF84"],"\xB6"=>[77,"\xF85"],"\xB7"=>[18,"\xF85"],"\xB8"=>[77,"\xF86"],"\xB9"=>[18,"\xF86"],"\xBA"=>[77,"\xF87"],"\xBB"=>[18,"\xF87"],"\xBC"=>[77,"\xF88"],"\xBD"=>[18,"\xF88"],"\xBE"=>[77,"\xF89"],"\xBF"=>[18,"\xF89"],"\xC0"=>[77,"\xF8\x3D"],"\xC1"=>[18,"\xF8\x3D"],"\xC2"=>[77,"\xF8A"],"\xC3"=>[18,"\xF8A"],"\xC4"=>[77,"\xF8_"],"\xC5"=>[18,"\xF8_"],"\xC6"=>[77,"\xF8b"],"\xC7"=>[18,"\xF8b"],"\xC8"=>[77,"\xF8d"],"\xC9"=>[18,"\xF8d"],"\xCA"=>[77,"\xF8f"],"\xCB"=>[18,"\xF8f"],"\xCC"=>[77,"\xF8g"],"\xCD"=>[18,"\xF8g"],"\xCE"=>[77,"\xF8h"],"\xCF"=>[18,"\xF8h"],"\xD0"=>[77,"\xF8l"],"\xD1"=>[18,"\xF8l"],"\xD2"=>[77,"\xF8m"],"\xD3"=>[18,"\xF8m"],"\xD4"=>[77,"\xF8n"],"\xD5"=>[18,"\xF8n"],"\xD6"=>[77,"\xF8p"],"\xD7"=>[18,"\xF8p"],"\xD8"=>[77,"\xF8r"],"\xD9"=>[18,"\xF8r"],"\xDA"=>[77,"\xF8u"],"\xDB"=>[18,"\xF8u"],"\xDC"=>[0,"\xF8\x3A"],"\xDD"=>[0,"\xF8B"],"\xDE"=>[0,"\xF8C"],"\xDF"=>[0,"\xF8D"],"\xE0"=>[0,"\xF8E"],"\xE1"=>[0,"\xF8F"],"\xE2"=>[0,"\xF8G"],"\xE3"=>[0,"\xF8H"],"\xE4"=>[0,"\xF8I"],"\xE5"=>[0,"\xF8J"],"\xE6"=>[0,"\xF8K"],"\xE7"=>[0,"\xF8L"],"\xE8"=>[0,"\xF8M"],"\xE9"=>[0,"\xF8N"],"\xEA"=>[0,"\xF8O"],"\xEB"=>[0,"\xF8P"],"\xEC"=>[0,"\xF8Q"],"\xED"=>[0,"\xF8R"],"\xEE"=>[0,"\xF8S"],"\xEF"=>[0,"\xF8T"],"\xF0"=>[0,"\xF8U"],"\xF1"=>[0,"\xF8V"],"\xF2"=>[0,"\xF8W"],"\xF3"=>[0,"\xF8Y"],"\xF4"=>[0,"\xF8j"],"\xF5"=>[0,"\xF8k"],"\xF6"=>[0,"\xF8q"],"\xF7"=>[0,"\xF8v"],"\xF8"=>[0,"\xF8w"],"\xF9"=>[0,"\xF8x"],"\xFA"=>[0,"\xF8y"],"\xFB"=>[0,"\xF8z"],"\xFC"=>[82,"\xF8"],"\xFD"=>[87,"\xF8"],"\xFE"=>[130,"\xF8"],"\xFF"=>[9,"\xF8"],],["\x00"=>[94,"\xFA0"],"\x01"=>[76,"\xFA0"],"\x02"=>[104,"\xFA0"],"\x03"=>[16,"\xFA0"],"\x04"=>[94,"\xFA1"],"\x05"=>[76,"\xFA1"],"\x06"=>[104,"\xFA1"],"\x07"=>[16,"\xFA1"],"\x08"=>[94,"\xFA2"],"\x09"=>[76,"\xFA2"],"\x0A"=>[104,"\xFA2"],"\x0B"=>[16,"\xFA2"],"\x0C"=>[94,"\xFAa"],"\x0D"=>[76,"\xFAa"],"\x0E"=>[104,"\xFAa"],"\x0F"=>[16,"\xFAa"],"\x10"=>[94,"\xFAc"],"\x11"=>[76,"\xFAc"],"\x12"=>[104,"\xFAc"],"\x13"=>[16,"\xFAc"],"\x14"=>[94,"\xFAe"],"\x15"=>[76,"\xFAe"],"\x16"=>[104,"\xFAe"],"\x17"=>[16,"\xFAe"],"\x18"=>[94,"\xFAi"],"\x19"=>[76,"\xFAi"],"\x1A"=>[104,"\xFAi"],"\x1B"=>[16,"\xFAi"],"\x1C"=>[94,"\xFAo"],"\x1D"=>[76,"\xFAo"],"\x1E"=>[104,"\xFAo"],"\x1F"=>[16,"\xFAo"],"\x20"=>[94,"\xFAs"],"\x21"=>[76,"\xFAs"],"\x22"=>[104,"\xFAs"],"\x23"=>[16,"\xFAs"],"\x24"=>[94,"\xFAt"],"\x25"=>[76,"\xFAt"],"\x26"=>[104,"\xFAt"],"\x27"=>[16,"\xFAt"],"\x28"=>[77,"\xFA\x20"],"\x29"=>[18,"\xFA\x20"],"\x2A"=>[77,"\xFA\x25"],"\x2B"=>[18,"\xFA\x25"],"\x2C"=>[77,"\xFA-"],"-"=>[18,"\xFA-"],"."=>[77,"\xFA."],"\x2F"=>[18,"\xFA."],[77,"\xFA\x2F"],[18,"\xFA\x2F"],[77,"\xFA3"],[18,"\xFA3"],[77,"\xFA4"],[18,"\xFA4"],[77,"\xFA5"],[18,"\xFA5"],[77,"\xFA6"],[18,"\xFA6"],"\x3A"=>[77,"\xFA7"],"\x3B"=>[18,"\xFA7"],"\x3C"=>[77,"\xFA8"],"\x3D"=>[18,"\xFA8"],"\x3E"=>[77,"\xFA9"],"\x3F"=>[18,"\xFA9"],"\x40"=>[77,"\xFA\x3D"],"A"=>[18,"\xFA\x3D"],"B"=>[77,"\xFAA"],"C"=>[18,"\xFAA"],"D"=>[77,"\xFA_"],"E"=>[18,"\xFA_"],"F"=>[77,"\xFAb"],"G"=>[18,"\xFAb"],"H"=>[77,"\xFAd"],"I"=>[18,"\xFAd"],"J"=>[77,"\xFAf"],"K"=>[18,"\xFAf"],"L"=>[77,"\xFAg"],"M"=>[18,"\xFAg"],"N"=>[77,"\xFAh"],"O"=>[18,"\xFAh"],"P"=>[77,"\xFAl"],"Q"=>[18,"\xFAl"],"R"=>[77,"\xFAm"],"S"=>[18,"\xFAm"],"T"=>[77,"\xFAn"],"U"=>[18,"\xFAn"],"V"=>[77,"\xFAp"],"W"=>[18,"\xFAp"],"X"=>[77,"\xFAr"],"Y"=>[18,"\xFAr"],"Z"=>[77,"\xFAu"],"\x5B"=>[18,"\xFAu"],"\x5C"=>[0,"\xFA\x3A"],"\x5D"=>[0,"\xFAB"],"\x5E"=>[0,"\xFAC"],"_"=>[0,"\xFAD"],"\x60"=>[0,"\xFAE"],"a"=>[0,"\xFAF"],"b"=>[0,"\xFAG"],"c"=>[0,"\xFAH"],"d"=>[0,"\xFAI"],"e"=>[0,"\xFAJ"],"f"=>[0,"\xFAK"],"g"=>[0,"\xFAL"],"h"=>[0,"\xFAM"],"i"=>[0,"\xFAN"],"j"=>[0,"\xFAO"],"k"=>[0,"\xFAP"],"l"=>[0,"\xFAQ"],"m"=>[0,"\xFAR"],"n"=>[0,"\xFAS"],"o"=>[0,"\xFAT"],"p"=>[0,"\xFAU"],"q"=>[0,"\xFAV"],"r"=>[0,"\xFAW"],"s"=>[0,"\xFAY"],"t"=>[0,"\xFAj"],"u"=>[0,"\xFAk"],"v"=>[0,"\xFAq"],"w"=>[0,"\xFAv"],"x"=>[0,"\xFAw"],"y"=>[0,"\xFAx"],"z"=>[0,"\xFAy"],"\x7B"=>[0,"\xFAz"],"\x7C"=>[82,"\xFA"],"\x7D"=>[87,"\xFA"],"~"=>[130,"\xFA"],"\x7F"=>[9,"\xFA"],"\x80"=>[94,"\xFB0"],"\x81"=>[76,"\xFB0"],"\x82"=>[104,"\xFB0"],"\x83"=>[16,"\xFB0"],"\x84"=>[94,"\xFB1"],"\x85"=>[76,"\xFB1"],"\x86"=>[104,"\xFB1"],"\x87"=>[16,"\xFB1"],"\x88"=>[94,"\xFB2"],"\x89"=>[76,"\xFB2"],"\x8A"=>[104,"\xFB2"],"\x8B"=>[16,"\xFB2"],"\x8C"=>[94,"\xFBa"],"\x8D"=>[76,"\xFBa"],"\x8E"=>[104,"\xFBa"],"\x8F"=>[16,"\xFBa"],"\x90"=>[94,"\xFBc"],"\x91"=>[76,"\xFBc"],"\x92"=>[104,"\xFBc"],"\x93"=>[16,"\xFBc"],"\x94"=>[94,"\xFBe"],"\x95"=>[76,"\xFBe"],"\x96"=>[104,"\xFBe"],"\x97"=>[16,"\xFBe"],"\x98"=>[94,"\xFBi"],"\x99"=>[76,"\xFBi"],"\x9A"=>[104,"\xFBi"],"\x9B"=>[16,"\xFBi"],"\x9C"=>[94,"\xFBo"],"\x9D"=>[76,"\xFBo"],"\x9E"=>[104,"\xFBo"],"\x9F"=>[16,"\xFBo"],"\xA0"=>[94,"\xFBs"],"\xA1"=>[76,"\xFBs"],"\xA2"=>[104,"\xFBs"],"\xA3"=>[16,"\xFBs"],"\xA4"=>[94,"\xFBt"],"\xA5"=>[76,"\xFBt"],"\xA6"=>[104,"\xFBt"],"\xA7"=>[16,"\xFBt"],"\xA8"=>[77,"\xFB\x20"],"\xA9"=>[18,"\xFB\x20"],"\xAA"=>[77,"\xFB\x25"],"\xAB"=>[18,"\xFB\x25"],"\xAC"=>[77,"\xFB-"],"\xAD"=>[18,"\xFB-"],"\xAE"=>[77,"\xFB."],"\xAF"=>[18,"\xFB."],"\xB0"=>[77,"\xFB\x2F"],"\xB1"=>[18,"\xFB\x2F"],"\xB2"=>[77,"\xFB3"],"\xB3"=>[18,"\xFB3"],"\xB4"=>[77,"\xFB4"],"\xB5"=>[18,"\xFB4"],"\xB6"=>[77,"\xFB5"],"\xB7"=>[18,"\xFB5"],"\xB8"=>[77,"\xFB6"],"\xB9"=>[18,"\xFB6"],"\xBA"=>[77,"\xFB7"],"\xBB"=>[18,"\xFB7"],"\xBC"=>[77,"\xFB8"],"\xBD"=>[18,"\xFB8"],"\xBE"=>[77,"\xFB9"],"\xBF"=>[18,"\xFB9"],"\xC0"=>[77,"\xFB\x3D"],"\xC1"=>[18,"\xFB\x3D"],"\xC2"=>[77,"\xFBA"],"\xC3"=>[18,"\xFBA"],"\xC4"=>[77,"\xFB_"],"\xC5"=>[18,"\xFB_"],"\xC6"=>[77,"\xFBb"],"\xC7"=>[18,"\xFBb"],"\xC8"=>[77,"\xFBd"],"\xC9"=>[18,"\xFBd"],"\xCA"=>[77,"\xFBf"],"\xCB"=>[18,"\xFBf"],"\xCC"=>[77,"\xFBg"],"\xCD"=>[18,"\xFBg"],"\xCE"=>[77,"\xFBh"],"\xCF"=>[18,"\xFBh"],"\xD0"=>[77,"\xFBl"],"\xD1"=>[18,"\xFBl"],"\xD2"=>[77,"\xFBm"],"\xD3"=>[18,"\xFBm"],"\xD4"=>[77,"\xFBn"],"\xD5"=>[18,"\xFBn"],"\xD6"=>[77,"\xFBp"],"\xD7"=>[18,"\xFBp"],"\xD8"=>[77,"\xFBr"],"\xD9"=>[18,"\xFBr"],"\xDA"=>[77,"\xFBu"],"\xDB"=>[18,"\xFBu"],"\xDC"=>[0,"\xFB\x3A"],"\xDD"=>[0,"\xFBB"],"\xDE"=>[0,"\xFBC"],"\xDF"=>[0,"\xFBD"],"\xE0"=>[0,"\xFBE"],"\xE1"=>[0,"\xFBF"],"\xE2"=>[0,"\xFBG"],"\xE3"=>[0,"\xFBH"],"\xE4"=>[0,"\xFBI"],"\xE5"=>[0,"\xFBJ"],"\xE6"=>[0,"\xFBK"],"\xE7"=>[0,"\xFBL"],"\xE8"=>[0,"\xFBM"],"\xE9"=>[0,"\xFBN"],"\xEA"=>[0,"\xFBO"],"\xEB"=>[0,"\xFBP"],"\xEC"=>[0,"\xFBQ"],"\xED"=>[0,"\xFBR"],"\xEE"=>[0,"\xFBS"],"\xEF"=>[0,"\xFBT"],"\xF0"=>[0,"\xFBU"],"\xF1"=>[0,"\xFBV"],"\xF2"=>[0,"\xFBW"],"\xF3"=>[0,"\xFBY"],"\xF4"=>[0,"\xFBj"],"\xF5"=>[0,"\xFBk"],"\xF6"=>[0,"\xFBq"],"\xF7"=>[0,"\xFBv"],"\xF8"=>[0,"\xFBw"],"\xF9"=>[0,"\xFBx"],"\xFA"=>[0,"\xFBy"],"\xFB"=>[0,"\xFBz"],"\xFC"=>[82,"\xFB"],"\xFD"=>[87,"\xFB"],"\xFE"=>[130,"\xFB"],"\xFF"=>[9,"\xFB"],],["\x00"=>[77,"\xFA0"],"\x01"=>[18,"\xFA0"],"\x02"=>[77,"\xFA1"],"\x03"=>[18,"\xFA1"],"\x04"=>[77,"\xFA2"],"\x05"=>[18,"\xFA2"],"\x06"=>[77,"\xFAa"],"\x07"=>[18,"\xFAa"],"\x08"=>[77,"\xFAc"],"\x09"=>[18,"\xFAc"],"\x0A"=>[77,"\xFAe"],"\x0B"=>[18,"\xFAe"],"\x0C"=>[77,"\xFAi"],"\x0D"=>[18,"\xFAi"],"\x0E"=>[77,"\xFAo"],"\x0F"=>[18,"\xFAo"],"\x10"=>[77,"\xFAs"],"\x11"=>[18,"\xFAs"],"\x12"=>[77,"\xFAt"],"\x13"=>[18,"\xFAt"],"\x14"=>[0,"\xFA\x20"],"\x15"=>[0,"\xFA\x25"],"\x16"=>[0,"\xFA-"],"\x17"=>[0,"\xFA."],"\x18"=>[0,"\xFA\x2F"],"\x19"=>[0,"\xFA3"],"\x1A"=>[0,"\xFA4"],"\x1B"=>[0,"\xFA5"],"\x1C"=>[0,"\xFA6"],"\x1D"=>[0,"\xFA7"],"\x1E"=>[0,"\xFA8"],"\x1F"=>[0,"\xFA9"],"\x20"=>[0,"\xFA\x3D"],"\x21"=>[0,"\xFAA"],"\x22"=>[0,"\xFA_"],"\x23"=>[0,"\xFAb"],"\x24"=>[0,"\xFAd"],"\x25"=>[0,"\xFAf"],"\x26"=>[0,"\xFAg"],"\x27"=>[0,"\xFAh"],"\x28"=>[0,"\xFAl"],"\x29"=>[0,"\xFAm"],"\x2A"=>[0,"\xFAn"],"\x2B"=>[0,"\xFAp"],"\x2C"=>[0,"\xFAr"],"-"=>[0,"\xFAu"],"."=>[100,"\xFA"],"\x2F"=>[110,"\xFA"],[111,"\xFA"],[115,"\xFA"],[116,"\xFA"],[118,"\xFA"],[119,"\xFA"],[122,"\xFA"],[123,"\xFA"],[125,"\xFA"],[126,"\xFA"],[129,"\xFA"],"\x3A"=>[143,"\xFA"],"\x3B"=>[148,"\xFA"],"\x3C"=>[151,"\xFA"],"\x3D"=>[153,"\xFA"],"\x3E"=>[83,"\xFA"],"\x3F"=>[10,"\xFA"],"\x40"=>[77,"\xFB0"],"A"=>[18,"\xFB0"],"B"=>[77,"\xFB1"],"C"=>[18,"\xFB1"],"D"=>[77,"\xFB2"],"E"=>[18,"\xFB2"],"F"=>[77,"\xFBa"],"G"=>[18,"\xFBa"],"H"=>[77,"\xFBc"],"I"=>[18,"\xFBc"],"J"=>[77,"\xFBe"],"K"=>[18,"\xFBe"],"L"=>[77,"\xFBi"],"M"=>[18,"\xFBi"],"N"=>[77,"\xFBo"],"O"=>[18,"\xFBo"],"P"=>[77,"\xFBs"],"Q"=>[18,"\xFBs"],"R"=>[77,"\xFBt"],"S"=>[18,"\xFBt"],"T"=>[0,"\xFB\x20"],"U"=>[0,"\xFB\x25"],"V"=>[0,"\xFB-"],"W"=>[0,"\xFB."],"X"=>[0,"\xFB\x2F"],"Y"=>[0,"\xFB3"],"Z"=>[0,"\xFB4"],"\x5B"=>[0,"\xFB5"],"\x5C"=>[0,"\xFB6"],"\x5D"=>[0,"\xFB7"],"\x5E"=>[0,"\xFB8"],"_"=>[0,"\xFB9"],"\x60"=>[0,"\xFB\x3D"],"a"=>[0,"\xFBA"],"b"=>[0,"\xFB_"],"c"=>[0,"\xFBb"],"d"=>[0,"\xFBd"],"e"=>[0,"\xFBf"],"f"=>[0,"\xFBg"],"g"=>[0,"\xFBh"],"h"=>[0,"\xFBl"],"i"=>[0,"\xFBm"],"j"=>[0,"\xFBn"],"k"=>[0,"\xFBp"],"l"=>[0,"\xFBr"],"m"=>[0,"\xFBu"],"n"=>[100,"\xFB"],"o"=>[110,"\xFB"],"p"=>[111,"\xFB"],"q"=>[115,"\xFB"],"r"=>[116,"\xFB"],"s"=>[118,"\xFB"],"t"=>[119,"\xFB"],"u"=>[122,"\xFB"],"v"=>[123,"\xFB"],"w"=>[125,"\xFB"],"x"=>[126,"\xFB"],"y"=>[129,"\xFB"],"z"=>[143,"\xFB"],"\x7B"=>[148,"\xFB"],"\x7C"=>[151,"\xFB"],"\x7D"=>[153,"\xFB"],"~"=>[83,"\xFB"],"\x7F"=>[10,"\xFB"],"\x80"=>[77,"\xFC0"],"\x81"=>[18,"\xFC0"],"\x82"=>[77,"\xFC1"],"\x83"=>[18,"\xFC1"],"\x84"=>[77,"\xFC2"],"\x85"=>[18,"\xFC2"],"\x86"=>[77,"\xFCa"],"\x87"=>[18,"\xFCa"],"\x88"=>[77,"\xFCc"],"\x89"=>[18,"\xFCc"],"\x8A"=>[77,"\xFCe"],"\x8B"=>[18,"\xFCe"],"\x8C"=>[77,"\xFCi"],"\x8D"=>[18,"\xFCi"],"\x8E"=>[77,"\xFCo"],"\x8F"=>[18,"\xFCo"],"\x90"=>[77,"\xFCs"],"\x91"=>[18,"\xFCs"],"\x92"=>[77,"\xFCt"],"\x93"=>[18,"\xFCt"],"\x94"=>[0,"\xFC\x20"],"\x95"=>[0,"\xFC\x25"],"\x96"=>[0,"\xFC-"],"\x97"=>[0,"\xFC."],"\x98"=>[0,"\xFC\x2F"],"\x99"=>[0,"\xFC3"],"\x9A"=>[0,"\xFC4"],"\x9B"=>[0,"\xFC5"],"\x9C"=>[0,"\xFC6"],"\x9D"=>[0,"\xFC7"],"\x9E"=>[0,"\xFC8"],"\x9F"=>[0,"\xFC9"],"\xA0"=>[0,"\xFC\x3D"],"\xA1"=>[0,"\xFCA"],"\xA2"=>[0,"\xFC_"],"\xA3"=>[0,"\xFCb"],"\xA4"=>[0,"\xFCd"],"\xA5"=>[0,"\xFCf"],"\xA6"=>[0,"\xFCg"],"\xA7"=>[0,"\xFCh"],"\xA8"=>[0,"\xFCl"],"\xA9"=>[0,"\xFCm"],"\xAA"=>[0,"\xFCn"],"\xAB"=>[0,"\xFCp"],"\xAC"=>[0,"\xFCr"],"\xAD"=>[0,"\xFCu"],"\xAE"=>[100,"\xFC"],"\xAF"=>[110,"\xFC"],"\xB0"=>[111,"\xFC"],"\xB1"=>[115,"\xFC"],"\xB2"=>[116,"\xFC"],"\xB3"=>[118,"\xFC"],"\xB4"=>[119,"\xFC"],"\xB5"=>[122,"\xFC"],"\xB6"=>[123,"\xFC"],"\xB7"=>[125,"\xFC"],"\xB8"=>[126,"\xFC"],"\xB9"=>[129,"\xFC"],"\xBA"=>[143,"\xFC"],"\xBB"=>[148,"\xFC"],"\xBC"=>[151,"\xFC"],"\xBD"=>[153,"\xFC"],"\xBE"=>[83,"\xFC"],"\xBF"=>[10,"\xFC"],"\xC0"=>[77,"\xFD0"],"\xC1"=>[18,"\xFD0"],"\xC2"=>[77,"\xFD1"],"\xC3"=>[18,"\xFD1"],"\xC4"=>[77,"\xFD2"],"\xC5"=>[18,"\xFD2"],"\xC6"=>[77,"\xFDa"],"\xC7"=>[18,"\xFDa"],"\xC8"=>[77,"\xFDc"],"\xC9"=>[18,"\xFDc"],"\xCA"=>[77,"\xFDe"],"\xCB"=>[18,"\xFDe"],"\xCC"=>[77,"\xFDi"],"\xCD"=>[18,"\xFDi"],"\xCE"=>[77,"\xFDo"],"\xCF"=>[18,"\xFDo"],"\xD0"=>[77,"\xFDs"],"\xD1"=>[18,"\xFDs"],"\xD2"=>[77,"\xFDt"],"\xD3"=>[18,"\xFDt"],"\xD4"=>[0,"\xFD\x20"],"\xD5"=>[0,"\xFD\x25"],"\xD6"=>[0,"\xFD-"],"\xD7"=>[0,"\xFD."],"\xD8"=>[0,"\xFD\x2F"],"\xD9"=>[0,"\xFD3"],"\xDA"=>[0,"\xFD4"],"\xDB"=>[0,"\xFD5"],"\xDC"=>[0,"\xFD6"],"\xDD"=>[0,"\xFD7"],"\xDE"=>[0,"\xFD8"],"\xDF"=>[0,"\xFD9"],"\xE0"=>[0,"\xFD\x3D"],"\xE1"=>[0,"\xFDA"],"\xE2"=>[0,"\xFD_"],"\xE3"=>[0,"\xFDb"],"\xE4"=>[0,"\xFDd"],"\xE5"=>[0,"\xFDf"],"\xE6"=>[0,"\xFDg"],"\xE7"=>[0,"\xFDh"],"\xE8"=>[0,"\xFDl"],"\xE9"=>[0,"\xFDm"],"\xEA"=>[0,"\xFDn"],"\xEB"=>[0,"\xFDp"],"\xEC"=>[0,"\xFDr"],"\xED"=>[0,"\xFDu"],"\xEE"=>[100,"\xFD"],"\xEF"=>[110,"\xFD"],"\xF0"=>[111,"\xFD"],"\xF1"=>[115,"\xFD"],"\xF2"=>[116,"\xFD"],"\xF3"=>[118,"\xFD"],"\xF4"=>[119,"\xFD"],"\xF5"=>[122,"\xFD"],"\xF6"=>[123,"\xFD"],"\xF7"=>[125,"\xFD"],"\xF8"=>[126,"\xFD"],"\xF9"=>[129,"\xFD"],"\xFA"=>[143,"\xFD"],"\xFB"=>[148,"\xFD"],"\xFC"=>[151,"\xFD"],"\xFD"=>[153,"\xFD"],"\xFE"=>[83,"\xFD"],"\xFF"=>[10,"\xFD"],],["\x00"=>[94,"\xFC0"],"\x01"=>[76,"\xFC0"],"\x02"=>[104,"\xFC0"],"\x03"=>[16,"\xFC0"],"\x04"=>[94,"\xFC1"],"\x05"=>[76,"\xFC1"],"\x06"=>[104,"\xFC1"],"\x07"=>[16,"\xFC1"],"\x08"=>[94,"\xFC2"],"\x09"=>[76,"\xFC2"],"\x0A"=>[104,"\xFC2"],"\x0B"=>[16,"\xFC2"],"\x0C"=>[94,"\xFCa"],"\x0D"=>[76,"\xFCa"],"\x0E"=>[104,"\xFCa"],"\x0F"=>[16,"\xFCa"],"\x10"=>[94,"\xFCc"],"\x11"=>[76,"\xFCc"],"\x12"=>[104,"\xFCc"],"\x13"=>[16,"\xFCc"],"\x14"=>[94,"\xFCe"],"\x15"=>[76,"\xFCe"],"\x16"=>[104,"\xFCe"],"\x17"=>[16,"\xFCe"],"\x18"=>[94,"\xFCi"],"\x19"=>[76,"\xFCi"],"\x1A"=>[104,"\xFCi"],"\x1B"=>[16,"\xFCi"],"\x1C"=>[94,"\xFCo"],"\x1D"=>[76,"\xFCo"],"\x1E"=>[104,"\xFCo"],"\x1F"=>[16,"\xFCo"],"\x20"=>[94,"\xFCs"],"\x21"=>[76,"\xFCs"],"\x22"=>[104,"\xFCs"],"\x23"=>[16,"\xFCs"],"\x24"=>[94,"\xFCt"],"\x25"=>[76,"\xFCt"],"\x26"=>[104,"\xFCt"],"\x27"=>[16,"\xFCt"],"\x28"=>[77,"\xFC\x20"],"\x29"=>[18,"\xFC\x20"],"\x2A"=>[77,"\xFC\x25"],"\x2B"=>[18,"\xFC\x25"],"\x2C"=>[77,"\xFC-"],"-"=>[18,"\xFC-"],"."=>[77,"\xFC."],"\x2F"=>[18,"\xFC."],[77,"\xFC\x2F"],[18,"\xFC\x2F"],[77,"\xFC3"],[18,"\xFC3"],[77,"\xFC4"],[18,"\xFC4"],[77,"\xFC5"],[18,"\xFC5"],[77,"\xFC6"],[18,"\xFC6"],"\x3A"=>[77,"\xFC7"],"\x3B"=>[18,"\xFC7"],"\x3C"=>[77,"\xFC8"],"\x3D"=>[18,"\xFC8"],"\x3E"=>[77,"\xFC9"],"\x3F"=>[18,"\xFC9"],"\x40"=>[77,"\xFC\x3D"],"A"=>[18,"\xFC\x3D"],"B"=>[77,"\xFCA"],"C"=>[18,"\xFCA"],"D"=>[77,"\xFC_"],"E"=>[18,"\xFC_"],"F"=>[77,"\xFCb"],"G"=>[18,"\xFCb"],"H"=>[77,"\xFCd"],"I"=>[18,"\xFCd"],"J"=>[77,"\xFCf"],"K"=>[18,"\xFCf"],"L"=>[77,"\xFCg"],"M"=>[18,"\xFCg"],"N"=>[77,"\xFCh"],"O"=>[18,"\xFCh"],"P"=>[77,"\xFCl"],"Q"=>[18,"\xFCl"],"R"=>[77,"\xFCm"],"S"=>[18,"\xFCm"],"T"=>[77,"\xFCn"],"U"=>[18,"\xFCn"],"V"=>[77,"\xFCp"],"W"=>[18,"\xFCp"],"X"=>[77,"\xFCr"],"Y"=>[18,"\xFCr"],"Z"=>[77,"\xFCu"],"\x5B"=>[18,"\xFCu"],"\x5C"=>[0,"\xFC\x3A"],"\x5D"=>[0,"\xFCB"],"\x5E"=>[0,"\xFCC"],"_"=>[0,"\xFCD"],"\x60"=>[0,"\xFCE"],"a"=>[0,"\xFCF"],"b"=>[0,"\xFCG"],"c"=>[0,"\xFCH"],"d"=>[0,"\xFCI"],"e"=>[0,"\xFCJ"],"f"=>[0,"\xFCK"],"g"=>[0,"\xFCL"],"h"=>[0,"\xFCM"],"i"=>[0,"\xFCN"],"j"=>[0,"\xFCO"],"k"=>[0,"\xFCP"],"l"=>[0,"\xFCQ"],"m"=>[0,"\xFCR"],"n"=>[0,"\xFCS"],"o"=>[0,"\xFCT"],"p"=>[0,"\xFCU"],"q"=>[0,"\xFCV"],"r"=>[0,"\xFCW"],"s"=>[0,"\xFCY"],"t"=>[0,"\xFCj"],"u"=>[0,"\xFCk"],"v"=>[0,"\xFCq"],"w"=>[0,"\xFCv"],"x"=>[0,"\xFCw"],"y"=>[0,"\xFCx"],"z"=>[0,"\xFCy"],"\x7B"=>[0,"\xFCz"],"\x7C"=>[82,"\xFC"],"\x7D"=>[87,"\xFC"],"~"=>[130,"\xFC"],"\x7F"=>[9,"\xFC"],"\x80"=>[94,"\xFD0"],"\x81"=>[76,"\xFD0"],"\x82"=>[104,"\xFD0"],"\x83"=>[16,"\xFD0"],"\x84"=>[94,"\xFD1"],"\x85"=>[76,"\xFD1"],"\x86"=>[104,"\xFD1"],"\x87"=>[16,"\xFD1"],"\x88"=>[94,"\xFD2"],"\x89"=>[76,"\xFD2"],"\x8A"=>[104,"\xFD2"],"\x8B"=>[16,"\xFD2"],"\x8C"=>[94,"\xFDa"],"\x8D"=>[76,"\xFDa"],"\x8E"=>[104,"\xFDa"],"\x8F"=>[16,"\xFDa"],"\x90"=>[94,"\xFDc"],"\x91"=>[76,"\xFDc"],"\x92"=>[104,"\xFDc"],"\x93"=>[16,"\xFDc"],"\x94"=>[94,"\xFDe"],"\x95"=>[76,"\xFDe"],"\x96"=>[104,"\xFDe"],"\x97"=>[16,"\xFDe"],"\x98"=>[94,"\xFDi"],"\x99"=>[76,"\xFDi"],"\x9A"=>[104,"\xFDi"],"\x9B"=>[16,"\xFDi"],"\x9C"=>[94,"\xFDo"],"\x9D"=>[76,"\xFDo"],"\x9E"=>[104,"\xFDo"],"\x9F"=>[16,"\xFDo"],"\xA0"=>[94,"\xFDs"],"\xA1"=>[76,"\xFDs"],"\xA2"=>[104,"\xFDs"],"\xA3"=>[16,"\xFDs"],"\xA4"=>[94,"\xFDt"],"\xA5"=>[76,"\xFDt"],"\xA6"=>[104,"\xFDt"],"\xA7"=>[16,"\xFDt"],"\xA8"=>[77,"\xFD\x20"],"\xA9"=>[18,"\xFD\x20"],"\xAA"=>[77,"\xFD\x25"],"\xAB"=>[18,"\xFD\x25"],"\xAC"=>[77,"\xFD-"],"\xAD"=>[18,"\xFD-"],"\xAE"=>[77,"\xFD."],"\xAF"=>[18,"\xFD."],"\xB0"=>[77,"\xFD\x2F"],"\xB1"=>[18,"\xFD\x2F"],"\xB2"=>[77,"\xFD3"],"\xB3"=>[18,"\xFD3"],"\xB4"=>[77,"\xFD4"],"\xB5"=>[18,"\xFD4"],"\xB6"=>[77,"\xFD5"],"\xB7"=>[18,"\xFD5"],"\xB8"=>[77,"\xFD6"],"\xB9"=>[18,"\xFD6"],"\xBA"=>[77,"\xFD7"],"\xBB"=>[18,"\xFD7"],"\xBC"=>[77,"\xFD8"],"\xBD"=>[18,"\xFD8"],"\xBE"=>[77,"\xFD9"],"\xBF"=>[18,"\xFD9"],"\xC0"=>[77,"\xFD\x3D"],"\xC1"=>[18,"\xFD\x3D"],"\xC2"=>[77,"\xFDA"],"\xC3"=>[18,"\xFDA"],"\xC4"=>[77,"\xFD_"],"\xC5"=>[18,"\xFD_"],"\xC6"=>[77,"\xFDb"],"\xC7"=>[18,"\xFDb"],"\xC8"=>[77,"\xFDd"],"\xC9"=>[18,"\xFDd"],"\xCA"=>[77,"\xFDf"],"\xCB"=>[18,"\xFDf"],"\xCC"=>[77,"\xFDg"],"\xCD"=>[18,"\xFDg"],"\xCE"=>[77,"\xFDh"],"\xCF"=>[18,"\xFDh"],"\xD0"=>[77,"\xFDl"],"\xD1"=>[18,"\xFDl"],"\xD2"=>[77,"\xFDm"],"\xD3"=>[18,"\xFDm"],"\xD4"=>[77,"\xFDn"],"\xD5"=>[18,"\xFDn"],"\xD6"=>[77,"\xFDp"],"\xD7"=>[18,"\xFDp"],"\xD8"=>[77,"\xFDr"],"\xD9"=>[18,"\xFDr"],"\xDA"=>[77,"\xFDu"],"\xDB"=>[18,"\xFDu"],"\xDC"=>[0,"\xFD\x3A"],"\xDD"=>[0,"\xFDB"],"\xDE"=>[0,"\xFDC"],"\xDF"=>[0,"\xFDD"],"\xE0"=>[0,"\xFDE"],"\xE1"=>[0,"\xFDF"],"\xE2"=>[0,"\xFDG"],"\xE3"=>[0,"\xFDH"],"\xE4"=>[0,"\xFDI"],"\xE5"=>[0,"\xFDJ"],"\xE6"=>[0,"\xFDK"],"\xE7"=>[0,"\xFDL"],"\xE8"=>[0,"\xFDM"],"\xE9"=>[0,"\xFDN"],"\xEA"=>[0,"\xFDO"],"\xEB"=>[0,"\xFDP"],"\xEC"=>[0,"\xFDQ"],"\xED"=>[0,"\xFDR"],"\xEE"=>[0,"\xFDS"],"\xEF"=>[0,"\xFDT"],"\xF0"=>[0,"\xFDU"],"\xF1"=>[0,"\xFDV"],"\xF2"=>[0,"\xFDW"],"\xF3"=>[0,"\xFDY"],"\xF4"=>[0,"\xFDj"],"\xF5"=>[0,"\xFDk"],"\xF6"=>[0,"\xFDq"],"\xF7"=>[0,"\xFDv"],"\xF8"=>[0,"\xFDw"],"\xF9"=>[0,"\xFDx"],"\xFA"=>[0,"\xFDy"],"\xFB"=>[0,"\xFDz"],"\xFC"=>[82,"\xFD"],"\xFD"=>[87,"\xFD"],"\xFE"=>[130,"\xFD"],"\xFF"=>[9,"\xFD"],],];
<?php declare(strict_types=1);

namespace Amp\Http\Internal;

use Amp\Http\HPack;
use Amp\Http\HPackException;
use FFI;

/**
 * @internal
 * @psalm-import-type HeaderArray from HPack
 */
final class HPackNghttp2
{
    private const FLAG_NO_INDEX = 0x01;
    private const FLAG_NO_COPY_NAME = 0x02;
    private const FLAG_NO_COPY_VALUE = 0x04;
    private const FLAG_NO_COPY = self::FLAG_NO_COPY_NAME | self::FLAG_NO_COPY_VALUE;
    private const FLAG_NO_COPY_SENSITIVE = self::FLAG_NO_COPY | self::FLAG_NO_INDEX;
    private const SENSITIVE_HEADERS = [
        'authorization' => self::FLAG_NO_COPY_SENSITIVE,
        'cookie' => self::FLAG_NO_COPY_SENSITIVE,
        'proxy-authorization' => self::FLAG_NO_COPY_SENSITIVE,
        'set-cookie' => self::FLAG_NO_COPY_SENSITIVE,
    ];

    private static $ffi;
    private static $deflatePtrType;
    private static $inflatePtrType;
    private static $nvType;
    private static $nvSize;
    private static $charType;
    private static $uint8Type;
    private static $uint8PtrType;
    private static $decodeNv;
    private static $decodeNvPtr;
    private static $decodeFlags;
    private static $decodeFlagsPtr;
    private static $supported;

    public static function isSupported(): bool
    {
        if (isset(self::$supported)) {
            return self::$supported;
        }

        if (!\extension_loaded('ffi')) {
            return self::$supported = false;
        }

        if (!\class_exists(FFI::class)) {
            return self::$supported = false;
        }

        try {
            self::init();

            return self::$supported = true;
        } catch (\Throwable $e) {
            return self::$supported = false;
        }
    }

    private static function init(): void
    {
        if (self::$ffi !== null) {
            return;
        }

        $header = \file_get_contents(__DIR__ . '/amp-hpack.h');

        $files = ['libnghttp2.so.14', 'libnghttp2.so', 'libnghttp2.dylib', '/opt/homebrew/lib/libnghttp2.dylib'];
        $error = null;

        foreach ($files as $file) {
            try {
                self::$ffi = FFI::cdef($header, $file);
                $error = null;
                break;
            } catch (\Throwable $exception) {
                $error = $error ?? $exception;
            }
        }

        if ($error) {
            throw $error;
        }

        self::$deflatePtrType = self::$ffi->type('nghttp2_hd_deflater*');
        self::$inflatePtrType = self::$ffi->type('nghttp2_hd_inflater*');
        self::$nvType = self::$ffi->type('nghttp2_nv');
        self::$nvSize = FFI::sizeof(self::$nvType);
        self::$charType = self::$ffi->type('char');
        self::$uint8Type = self::$ffi->type('uint8_t');
        self::$uint8PtrType = self::$ffi->type('uint8_t*');

        self::$decodeNv = self::$ffi->new(self::$nvType);
        self::$decodeNvPtr = FFI::addr(self::$decodeNv);

        self::$decodeFlags = self::$ffi->new('int');
        self::$decodeFlagsPtr = FFI::addr(self::$decodeFlags);
    }

    private static function createBufferFromString(string $value): ?FFI\CData
    {
        $length = \strlen($value);
        if (!$length) {
            return null;
        }

        $buffer = self::$ffi->new(FFI::arrayType(self::$uint8Type, [$length]));
        FFI::memcpy($buffer, $value, $length);

        return $buffer;
    }

    private $deflatePtr;
    private $inflatePtr;

    /**
     * @param int $maxSize Upper limit on table size.
     */
    public function __construct(int $maxSize = 4096)
    {
        self::init();

        $this->deflatePtr = self::$ffi->new(self::$deflatePtrType);
        $this->inflatePtr = self::$ffi->new(self::$inflatePtrType);

        $return = self::$ffi->nghttp2_hd_deflate_new(FFI::addr($this->deflatePtr), $maxSize);
        if ($return !== 0) {
            throw new \RuntimeException('Failed to init deflate context');
        }

        $return = self::$ffi->nghttp2_hd_inflate_new(FFI::addr($this->inflatePtr));
        if ($return !== 0) {
            throw new \RuntimeException('Failed to init inflate context');
        }
    }

    /**
     * @param string $input Encoded headers.
     * @param int $maxSize Maximum length of the decoded header string.
     *
     * @return string[][]|null Returns null if decoding fails or if $maxSize is exceeded.
     */
    public function decode(string $input, int $maxSize): ?array
    {
        $ffi = self::$ffi;
        $pair = self::$decodeNv;
        $pairPtr = self::$decodeNvPtr;
        $flags = self::$decodeFlags;
        $flagsPtr = self::$decodeFlagsPtr;
        $inflate = $this->inflatePtr;

        $size = 0;

        $bufferLength = \strlen($input);
        $buffer = self::createBufferFromString($input);
        if ($buffer === null) {
            return [];
        }

        $offset = 0;
        $bufferPtr = $ffi->cast(self::$uint8PtrType, $buffer);

        $headers = [];

        while (true) {
            $read = $ffi->nghttp2_hd_inflate_hd2($inflate, $pairPtr, $flagsPtr, $bufferPtr, $bufferLength - $offset, 1);

            if ($read < 0) {
                return null;
            }

            $offset += $read;
            $bufferPtr += $read;

            $cFlags = $flags->cdata;
            if ($cFlags & 0x02) { // NGHTTP2_HD_INFLATE_EMIT
                $nameLength = $pair->namelen;
                $valueLength = $pair->valuelen;

                $headers[] = [
                    FFI::string($pair->name, $nameLength),
                    FFI::string($pair->value, $valueLength),
                ];

                $size += $nameLength + $valueLength;

                if ($size > $maxSize) {
                    return null;
                }
            }

            if ($cFlags & 0x01) { // NGHTTP2_HD_INFLATE_FINAL
                $ffi->nghttp2_hd_inflate_end_headers($inflate);

                FFI::memset($pair, 0, self::$nvSize);

                return $headers;
            }

            if ($read === 0 || $offset > $bufferLength) {
                return null;
            }
        }

        return null;
    }

    /**
     * @param HeaderArray $headers
     *
     * @return string Encoded headers.
     */
    public function encode(array $headers): string
    {
        $ffi = self::$ffi;

        // To keep memory buffers
        $buffers = [];

        $headerCount = \count($headers);
        $current = 0;

        $pairs = $ffi->new(FFI::arrayType(self::$nvType, [$headerCount]));

        foreach ($headers as $index => [$name, $value]) {
            \assert($index === $current);

            $name = (string) $name;
            $value = (string) $value;

            $pair = $pairs[$current];

            $nameBuffer = self::createBufferFromString($name);
            $valueBuffer = self::createBufferFromString($value);

            $pair->name = $ffi->cast(self::$uint8PtrType, $nameBuffer);
            $pair->namelen = \strlen($name);

            $pair->value = $ffi->cast(self::$uint8PtrType, $valueBuffer);
            $pair->valuelen = \strlen($value);

            $pair->flags = self::SENSITIVE_HEADERS[$name] ?? self::FLAG_NO_COPY;

            $buffers[] = $nameBuffer;
            $buffers[] = $valueBuffer;

            $current++;
        }

        $bufferLength = $ffi->nghttp2_hd_deflate_bound($this->deflatePtr, $pairs, $headerCount);
        $buffer = $ffi->new(FFI::arrayType(self::$uint8Type, [$bufferLength]));

        $bufferLength = $ffi->nghttp2_hd_deflate_hd($this->deflatePtr, $buffer, $bufferLength, $pairs, $headerCount);

        if ($bufferLength < 0) {
            throw new HPackException('Failed to compress headers using nghttp2');
        }

        return FFI::string($buffer, $bufferLength);
    }
}
<?php declare(strict_types=1);

namespace Amp\Http\Internal;

use Amp\Http\HPack;
use Amp\Http\HPackException;

/**
 * @internal
 * @psalm-import-type HeaderArray from HPack
 */
final class HPackNative
{
    private const HUFFMAN_CODE = [
        /* 0x00 */ 0x1ff8, 0x7fffd8, 0xfffffe2, 0xfffffe3, 0xfffffe4, 0xfffffe5, 0xfffffe6, 0xfffffe7,
        /* 0x08 */ 0xfffffe8, 0xffffea, 0x3ffffffc, 0xfffffe9, 0xfffffea, 0x3ffffffd, 0xfffffeb, 0xfffffec,
        /* 0x10 */ 0xfffffed, 0xfffffee, 0xfffffef, 0xffffff0, 0xffffff1, 0xffffff2, 0x3ffffffe, 0xffffff3,
        /* 0x18 */ 0xffffff4, 0xffffff5, 0xffffff6, 0xffffff7, 0xffffff8, 0xffffff9, 0xffffffa, 0xffffffb,
        /* 0x20 */ 0x14, 0x3f8, 0x3f9, 0xffa, 0x1ff9, 0x15, 0xf8, 0x7fa,
        /* 0x28 */ 0x3fa, 0x3fb, 0xf9, 0x7fb, 0xfa, 0x16, 0x17, 0x18,
        /* 0x30 */ 0x0, 0x1, 0x2, 0x19, 0x1a, 0x1b, 0x1c, 0x1d,
        /* 0x38 */ 0x1e, 0x1f, 0x5c, 0xfb, 0x7ffc, 0x20, 0xffb, 0x3fc,
        /* 0x40 */ 0x1ffa, 0x21, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62,
        /* 0x48 */ 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a,
        /* 0x50 */ 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72,
        /* 0x58 */ 0xfc, 0x73, 0xfd, 0x1ffb, 0x7fff0, 0x1ffc, 0x3ffc, 0x22,
        /* 0x60 */ 0x7ffd, 0x3, 0x23, 0x4, 0x24, 0x5, 0x25, 0x26,
        /* 0x68 */ 0x27, 0x6, 0x74, 0x75, 0x28, 0x29, 0x2a, 0x7,
        /* 0x70 */ 0x2b, 0x76, 0x2c, 0x8, 0x9, 0x2d, 0x77, 0x78,
        /* 0x78 */ 0x79, 0x7a, 0x7b, 0x7ffe, 0x7fc, 0x3ffd, 0x1ffd, 0xffffffc,
        /* 0x80 */ 0xfffe6, 0x3fffd2, 0xfffe7, 0xfffe8, 0x3fffd3, 0x3fffd4, 0x3fffd5, 0x7fffd9,
        /* 0x88 */ 0x3fffd6, 0x7fffda, 0x7fffdb, 0x7fffdc, 0x7fffdd, 0x7fffde, 0xffffeb, 0x7fffdf,
        /* 0x90 */ 0xffffec, 0xffffed, 0x3fffd7, 0x7fffe0, 0xffffee, 0x7fffe1, 0x7fffe2, 0x7fffe3,
        /* 0x98 */ 0x7fffe4, 0x1fffdc, 0x3fffd8, 0x7fffe5, 0x3fffd9, 0x7fffe6, 0x7fffe7, 0xffffef,
        /* 0xA0 */ 0x3fffda, 0x1fffdd, 0xfffe9, 0x3fffdb, 0x3fffdc, 0x7fffe8, 0x7fffe9, 0x1fffde,
        /* 0xA8 */ 0x7fffea, 0x3fffdd, 0x3fffde, 0xfffff0, 0x1fffdf, 0x3fffdf, 0x7fffeb, 0x7fffec,
        /* 0xB0 */ 0x1fffe0, 0x1fffe1, 0x3fffe0, 0x1fffe2, 0x7fffed, 0x3fffe1, 0x7fffee, 0x7fffef,
        /* 0xB8 */ 0xfffea, 0x3fffe2, 0x3fffe3, 0x3fffe4, 0x7ffff0, 0x3fffe5, 0x3fffe6, 0x7ffff1,
        /* 0xC0 */ 0x3ffffe0, 0x3ffffe1, 0xfffeb, 0x7fff1, 0x3fffe7, 0x7ffff2, 0x3fffe8, 0x1ffffec,
        /* 0xC8 */ 0x3ffffe2, 0x3ffffe3, 0x3ffffe4, 0x7ffffde, 0x7ffffdf, 0x3ffffe5, 0xfffff1, 0x1ffffed,
        /* 0xD0 */ 0x7fff2, 0x1fffe3, 0x3ffffe6, 0x7ffffe0, 0x7ffffe1, 0x3ffffe7, 0x7ffffe2, 0xfffff2,
        /* 0xD8 */ 0x1fffe4, 0x1fffe5, 0x3ffffe8, 0x3ffffe9, 0xffffffd, 0x7ffffe3, 0x7ffffe4, 0x7ffffe5,
        /* 0xE0 */ 0xfffec, 0xfffff3, 0xfffed, 0x1fffe6, 0x3fffe9, 0x1fffe7, 0x1fffe8, 0x7ffff3,
        /* 0xE8 */ 0x3fffea, 0x3fffeb, 0x1ffffee, 0x1ffffef, 0xfffff4, 0xfffff5, 0x3ffffea, 0x7ffff4,
        /* 0xF0 */ 0x3ffffeb, 0x7ffffe6, 0x3ffffec, 0x3ffffed, 0x7ffffe7, 0x7ffffe8, 0x7ffffe9, 0x7ffffea,
        /* 0xF8 */ 0x7ffffeb, 0xffffffe, 0x7ffffec, 0x7ffffed, 0x7ffffee, 0x7ffffef, 0x7fffff0, 0x3ffffee,
        /* end! */ 0x3fffffff
    ];

    private const HUFFMAN_CODE_LENGTHS = [
        /* 0x00 */ 13, 23, 28, 28, 28, 28, 28, 28,
        /* 0x08 */ 28, 24, 30, 28, 28, 30, 28, 28,
        /* 0x10 */ 28, 28, 28, 28, 28, 28, 30, 28,
        /* 0x18 */ 28, 28, 28, 28, 28, 28, 28, 28,
        /* 0x20 */ 6, 10, 10, 12, 13, 6, 8, 11,
        /* 0x28 */ 10, 10, 8, 11, 8, 6, 6, 6,
        /* 0x30 */ 5, 5, 5, 6, 6, 6, 6, 6,
        /* 0x38 */ 6, 6, 7, 8, 15, 6, 12, 10,
        /* 0x40 */ 13, 6, 7, 7, 7, 7, 7, 7,
        /* 0x48 */ 7, 7, 7, 7, 7, 7, 7, 7,
        /* 0x50 */ 7, 7, 7, 7, 7, 7, 7, 7,
        /* 0x58 */ 8, 7, 8, 13, 19, 13, 14, 6,
        /* 0x60 */ 15, 5, 6, 5, 6, 5, 6, 6,
        /* 0x68 */ 6, 5, 7, 7, 6, 6, 6, 5,
        /* 0x70 */ 6, 7, 6, 5, 5, 6, 7, 7,
        /* 0x78 */ 7, 7, 7, 15, 11, 14, 13, 28,
        /* 0x80 */ 20, 22, 20, 20, 22, 22, 22, 23,
        /* 0x88 */ 22, 23, 23, 23, 23, 23, 24, 23,
        /* 0x90 */ 24, 24, 22, 23, 24, 23, 23, 23,
        /* 0x98 */ 23, 21, 22, 23, 22, 23, 23, 24,
        /* 0xA0 */ 22, 21, 20, 22, 22, 23, 23, 21,
        /* 0xA8 */ 23, 22, 22, 24, 21, 22, 23, 23,
        /* 0xB0 */ 21, 21, 22, 21, 23, 22, 23, 23,
        /* 0xB8 */ 20, 22, 22, 22, 23, 22, 22, 23,
        /* 0xC0 */ 26, 26, 20, 19, 22, 23, 22, 25,
        /* 0xC8 */ 26, 26, 26, 27, 27, 26, 24, 25,
        /* 0xD0 */ 19, 21, 26, 27, 27, 26, 27, 24,
        /* 0xD8 */ 21, 21, 26, 26, 28, 27, 27, 27,
        /* 0xE0 */ 20, 24, 20, 21, 22, 21, 21, 23,
        /* 0xE8 */ 22, 22, 25, 25, 24, 24, 26, 23,
        /* 0xF0 */ 26, 27, 26, 26, 27, 27, 27, 27,
        /* 0xF8 */ 27, 28, 27, 27, 27, 27, 27, 26,
        /* end! */ 30
    ];

    private const DEFAULT_COMPRESSION_THRESHOLD = 1024;
    private const DEFAULT_MAX_SIZE = 4096;

    private static $huffmanLookup;
    private static $huffmanCodes;
    private static $huffmanLengths;

    private static $indexMap = [];

    /** @var string[][] */
    private $headers = [];

    /** @var int */
    private $hardMaxSize = self::DEFAULT_MAX_SIZE;

    /** @var int Max table size. */
    private $currentMaxSize = self::DEFAULT_MAX_SIZE;

    /** @var int Current table size. */
    private $size = 0;

    /** Called via bindTo(), see end of file */
    private static function init() /* : void */
    {
        self::$huffmanLookup = self::huffmanLookupInit();
        self::$huffmanCodes = self::huffmanCodesInit();
        self::$huffmanLengths = self::huffmanLengthsInit();

        foreach (\array_column(self::TABLE, 0) as $index => $name) {
            if (isset(self::$indexMap[$name])) {
                continue;
            }

            self::$indexMap[$name] = $index + 1;
        }
    }

    // (micro-)optimized decode
    private static function huffmanLookupInit(): array
    {
        if (('cli' !== \PHP_SAPI && 'phpdbg' !== \PHP_SAPI) || \filter_var(\ini_get('opcache.enable_cli'), \FILTER_VALIDATE_BOOLEAN)) {
            return require __DIR__ . '/huffman-lookup.php';
        }

        \gc_disable();
        $encodingAccess = [];
        $terminals = [];
        $index = 7;

        foreach (self::HUFFMAN_CODE as $chr => $bits) {
            $len = self::HUFFMAN_CODE_LENGTHS[$chr];

            for ($bit = 0; $bit < 8; $bit++) {
                $offlen = $len + $bit;
                $next = $bit;

                for ($byte = ($offlen - 1) >> 3; $byte > 0; $byte--) {
                    $cur = \str_pad(\decbin(($bits >> ($byte * 8 - ((0x30 - $offlen) & 7))) & 0xFF), 8, "0", STR_PAD_LEFT);
                    if (($encodingAccess[$next][$cur][0] ?? 0) !== 0) {
                        $next = $encodingAccess[$next][$cur][0];
                    } else {
                        $encodingAccess[$next][$cur] = [++$index, null];
                        $next = $index;
                    }
                }

                $key = \str_pad(
                    \decbin($bits & ((1 << ((($offlen - 1) & 7) + 1)) - 1)),
                    (($offlen - 1) & 7) + 1,
                    "0",
                    STR_PAD_LEFT
                );
                $encodingAccess[$next][$key] = [null, $chr > 0xFF ? "" : \chr($chr)];

                if ($offlen & 7) {
                    $terminals[$offlen & 7][] = [$key, $next];
                } else {
                    $encodingAccess[$next][$key][0] = 0;
                }
            }
        }

        $memoize = [];
        for ($off = 7; $off > 0; $off--) {
            foreach ($terminals[$off] as [$key, $next]) {
                if ($encodingAccess[$next][$key][0] === null) {
                    foreach ($encodingAccess[$off] as $chr => $cur) {
                        $encodingAccess[$next][($memoize[$key] ?? $memoize[$key] = \str_pad($key, 8, "0", STR_PAD_RIGHT)) | $chr] =
                            [$cur[0], $encodingAccess[$next][$key][1] != "" ? $encodingAccess[$next][$key][1] . $cur[1] : ""];
                    }

                    unset($encodingAccess[$next][$key]);
                }
            }
        }

        $memoize = [];
        for ($off = 7; $off > 0; $off--) {
            foreach ($terminals[$off] as [$key, $next]) {
                foreach ($encodingAccess[$next] as $k => $v) {
                    if (\strlen((string) $k) !== 1) {
                        $encodingAccess[$next][$memoize[$k] ?? $memoize[$k] = \chr(\bindec((string) $k))] = $v;
                        unset($encodingAccess[$next][$k]);
                    }
                }
            }

            unset($encodingAccess[$off]);
        }

        \gc_enable();

        return $encodingAccess;
    }

    /**
     *
     * @return string|null Returns null if decoding fails.
     */
    public static function huffmanDecode(string $input) /* : ?string */
    {
        $huffmanLookup = self::$huffmanLookup;
        $lookup = 0;
        $lengths = self::$huffmanLengths;
        $length = \strlen($input);
        $out = \str_repeat("\0", (int) \floor($length / 5 * 8 + 1)); // max length

        // Fail if EOS symbol is found.
        if (\strpos($input, "\x3f\xff\xff\xff") !== false) {
            return null;
        }

        for ($bitCount = $off = $i = 0; $i < $length; $i++) {
            [$lookup, $chr] = $huffmanLookup[$lookup][$input[$i]];

            if ($chr === null) {
                continue;
            }

            if ($chr === "") {
                return null;
            }

            $out[$off++] = $chr[0];
            $bitCount += $lengths[$chr[0]];

            if (isset($chr[1])) {
                $out[$off++] = $chr[1];
                $bitCount += $lengths[$chr[1]];
            }
        }

        // Padding longer than 7-bits
        if ($i && $chr === null) {
            return null;
        }

        // Check for 0's in padding
        if ($bitCount & 7) {
            $mask = 0xff >> ($bitCount & 7);
            if ((\ord($input[$i - 1]) & $mask) !== $mask) {
                return null;
            }
        }

        return \substr($out, 0, $off);
    }

    private static function huffmanCodesInit(): array
    {
        if (('cli' !== \PHP_SAPI && 'phpdbg' !== \PHP_SAPI) || \filter_var(\ini_get('opcache.enable_cli'), \FILTER_VALIDATE_BOOLEAN)) {
            return require __DIR__ . '/huffman-codes.php';
        }

        $lookup = [];

        for ($chr = 0; $chr <= 0xFF; $chr++) {
            $bits = self::HUFFMAN_CODE[$chr];
            $length = self::HUFFMAN_CODE_LENGTHS[$chr];

            for ($bit = 0; $bit < 8; $bit++) {
                $bytes = ($length + $bit - 1) >> 3;
                $codes = [];

                for ($byte = $bytes; $byte >= 0; $byte--) {
                    $codes[] = \chr(
                        (
                            $byte
                                ? $bits >> ($length - ($bytes - $byte + 1) * 8 + $bit)
                                : ($bits << ((0x30 - $length - $bit) & 7))
                        ) & 0xFF
                    );
                }

                $lookup[$bit][\chr($chr)] = $codes;
            }
        }

        return $lookup;
    }

    private static function huffmanLengthsInit(): array
    {
        $lengths = [];

        for ($chr = 0; $chr <= 0xFF; $chr++) {
            $lengths[\chr($chr)] = self::HUFFMAN_CODE_LENGTHS[$chr];
        }

        return $lengths;
    }

    public static function huffmanEncode(string $input): string
    {
        $codes = self::$huffmanCodes;
        $lengths = self::$huffmanLengths;

        $length = \strlen($input);
        $out = \str_repeat("\0", $length * 5 + 1); // max length

        for ($bitCount = $i = 0; $i < $length; $i++) {
            $chr = $input[$i];
            $byte = $bitCount >> 3;

            foreach ($codes[$bitCount & 7][$chr] as $bits) {
                // Note: |= can't be used with strings in PHP
                $out[$byte] = $out[$byte] | $bits;
                $byte++;
            }

            $bitCount += $lengths[$chr];
        }

        if ($bitCount & 7) {
            // Note: |= can't be used with strings in PHP
            $out[$byte - 1] = $out[$byte - 1] | \chr(0xFF >> ($bitCount & 7));
        }

        return $i ? \substr($out, 0, $byte) : '';
    }

    /** @see RFC 7541 Appendix A */
    const LAST_INDEX = 61;
    const TABLE = [ // starts at 1
        [":authority", ""],
        [":method", "GET"],
        [":method", "POST"],
        [":path", "/"],
        [":path", "/index.html"],
        [":scheme", "http"],
        [":scheme", "https"],
        [":status", "200"],
        [":status", "204"],
        [":status", "206"],
        [":status", "304"],
        [":status", "400"],
        [":status", "404"],
        [":status", "500"],
        ["accept-charset", ""],
        ["accept-encoding", "gzip, deflate"],
        ["accept-language", ""],
        ["accept-ranges", ""],
        ["accept", ""],
        ["access-control-allow-origin", ""],
        ["age", ""],
        ["allow", ""],
        ["authorization", ""],
        ["cache-control", ""],
        ["content-disposition", ""],
        ["content-encoding", ""],
        ["content-language", ""],
        ["content-length", ""],
        ["content-location", ""],
        ["content-range", ""],
        ["content-type", ""],
        ["cookie", ""],
        ["date", ""],
        ["etag", ""],
        ["expect", ""],
        ["expires", ""],
        ["from", ""],
        ["host", ""],
        ["if-match", ""],
        ["if-modified-since", ""],
        ["if-none-match", ""],
        ["if-range", ""],
        ["if-unmodified-since", ""],
        ["last-modified", ""],
        ["link", ""],
        ["location", ""],
        ["max-forwards", ""],
        ["proxy-authenticate", ""],
        ["proxy-authorization", ""],
        ["range", ""],
        ["referer", ""],
        ["refresh", ""],
        ["retry-after", ""],
        ["server", ""],
        ["set-cookie", ""],
        ["strict-transport-security", ""],
        ["transfer-encoding", ""],
        ["user-agent", ""],
        ["vary", ""],
        ["via", ""],
        ["www-authenticate", ""]
    ];

    private static function decodeDynamicInteger(string $input, int &$off): int
    {
        if (!isset($input[$off])) {
            throw new HPackException('Invalid input data, too short for dynamic integer');
        }

        $c = \ord($input[$off++]);
        $int = $c & 0x7f;
        $i = 0;

        while ($c & 0x80) {
            if (!isset($input[$off])) {
                return -0x80;
            }

            $c = \ord($input[$off++]);
            $int += ($c & 0x7f) << (++$i * 7);

            if ($int > 2147483647) {
                throw new HPackException('Invalid integer, too large');
            }
        }

        return $int;
    }

    /**
     * @param int $maxSize Upper limit on table size.
     */
    public function __construct(int $maxSize = self::DEFAULT_MAX_SIZE)
    {
        $this->hardMaxSize = $maxSize;
    }

    /**
     * Sets the upper limit on table size. Dynamic table updates requesting a size above this size will result in a
     * decoding error (i.e., returning null from decode()).
     *
     */
    public function setTableSizeLimit(int $maxSize) /* : void */
    {
        $this->hardMaxSize = $maxSize;
    }

    /**
     * Resizes the table to the given size, removing old entries as per section 4.4 if necessary.
     *
     */
    public function resizeTable(?int $size = null) /* : void */
    {
        if ($size !== null) {
            $this->currentMaxSize = \max(0, \min($size, $this->hardMaxSize));
        }

        while ($this->size > $this->currentMaxSize) {
            [$name, $value] = \array_pop($this->headers);
            $this->size -= 32 + \strlen($name) + \strlen($value);
        }
    }

    /**
     * @param string $input Encoded headers.
     * @param int $maxSize Maximum length of the decoded header string.
     *
     * @return string[][]|null Returns null if decoding fails or if $maxSize is exceeded.
     */
    public function decode(string $input, int $maxSize) /* : ?array */
    {
        $headers = [];
        $off = 0;
        $inputLength = \strlen($input);
        $size = 0;

        try {
            // dynamic $table as per 2.3.2
            while ($off < $inputLength) {
                $index = \ord($input[$off++]);

                if ($index & 0x80) {
                    // range check
                    if ($index <= self::LAST_INDEX + 0x80) {
                        if ($index === 0x80) {
                            return null;
                        }

                        [$name, $value] = $headers[] = self::TABLE[$index - 0x81];
                    } else {
                        if ($index == 0xff) {
                            $index = self::decodeDynamicInteger($input, $off) + 0xff;
                        }

                        $index -= 0x81 + self::LAST_INDEX;
                        if (!isset($this->headers[$index])) {
                            return null;
                        }

                        [$name, $value] = $headers[] = $this->headers[$index];
                    }
                } elseif (($index & 0x60) !== 0x20) { // (($index & 0x40) || !($index & 0x20)): bit 4: never index is ignored
                    $dynamic = (bool) ($index & 0x40);

                    if ($index & ($dynamic ? 0x3f : 0x0f)) { // separate length
                        if ($dynamic) {
                            if ($index === 0x7f) {
                                $index = self::decodeDynamicInteger($input, $off) + 0x3f;
                            } else {
                                $index &= 0x3f;
                            }
                        } else {
                            $index &= 0x0f;
                            if ($index === 0x0f) {
                                $index = self::decodeDynamicInteger($input, $off) + 0x0f;
                            }
                        }

                        if ($index < 0) {
                            return null;
                        }

                        if ($index <= self::LAST_INDEX) {
                            $header = self::TABLE[$index - 1];
                        } elseif (!isset($this->headers[$index - 1 - self::LAST_INDEX])) {
                            return null;
                        } else {
                            $header = $this->headers[$index - 1 - self::LAST_INDEX];
                        }
                    } else {
                        if ($off >= $inputLength) {
                            return null;
                        }

                        $length = \ord($input[$off++]);
                        $huffman = $length & 0x80;
                        $length &= 0x7f;

                        if ($length === 0x7f) {
                            $length = self::decodeDynamicInteger($input, $off) + 0x7f;
                        }

                        if ($inputLength - $off < $length || $length <= 0) {
                            return null;
                        }

                        if ($huffman) {
                            $header = [self::huffmanDecode(\substr($input, $off, $length))];
                            if ($header[0] === null) {
                                return null;
                            }
                        } else {
                            $header = [\substr($input, $off, $length)];
                        }

                        $off += $length;
                    }

                    if ($off >= $inputLength) {
                        return null;
                    }

                    $length = \ord($input[$off++]);
                    $huffman = $length & 0x80;
                    $length &= 0x7f;

                    if ($length === 0x7f) {
                        $length = self::decodeDynamicInteger($input, $off) + 0x7f;
                    }

                    if ($inputLength - $off < $length || $length < 0) {
                        return null;
                    }

                    if ($huffman) {
                        $header[1] = self::huffmanDecode(\substr($input, $off, $length));
                        if ($header[1] === null) {
                            return null;
                        }
                    } else {
                        $header[1] = \substr($input, $off, $length);
                    }

                    $off += $length;

                    if ($dynamic) {
                        \array_unshift($this->headers, $header);
                        $this->size += 32 + \strlen($header[0]) + \strlen($header[1]);
                        if ($this->currentMaxSize < $this->size) {
                            $this->resizeTable();
                        }
                    }

                    [$name, $value] = $headers[] = $header;
                } else { // if ($index & 0x20) {
                    if ($off >= $inputLength) {
                        return null; // Dynamic table size update must not be the last entry in header block.
                    }

                    $index &= 0x1f;
                    if ($index === 0x1f) {
                        $index = self::decodeDynamicInteger($input, $off) + 0x1f;
                    }

                    if ($index > $this->hardMaxSize) {
                        return null;
                    }

                    $this->resizeTable($index);

                    continue;
                }

                $size += \strlen($name) + \strlen($value);

                if ($size > $maxSize) {
                    return null;
                }
            }
        } catch (HPackException $e) {
            return null;
        }

        return $headers;
    }

    private static function encodeDynamicInteger(int $int): string
    {
        $out = "";
        for ($i = 0; ($int >> $i) >= 0x80; $i += 7) {
            $out .= \chr(0x80 | (($int >> $i) & 0x7f));
        }
        return $out . \chr($int >> $i);
    }

    /**
     * @param HeaderArray $headers
     * @param int $compressionThreshold Compress strings whose length is at least the number of bytes given.
     *
     */
    public function encode(array $headers, int $compressionThreshold = self::DEFAULT_COMPRESSION_THRESHOLD): string
    {
        // @TODO implementation is deliberately primitive... [doesn't use any dynamic table...]
        $output = "";

        foreach ($headers as [$name, $value]) {
            $name = (string) $name;
            $value = (string) $value;

            if (isset(self::$indexMap[$name])) {
                $index = self::$indexMap[$name];
                if ($index < 0x10) {
                    $output .= \chr($index);
                } else {
                    $output .= "\x0f" . \chr($index - 0x0f);
                }
            } else {
                $output .= "\0" . $this->encodeString($name, $compressionThreshold);
            }

            $output .= $this->encodeString($value, $compressionThreshold);
        }

        return $output;
    }

    private function encodeString(string $value, int $compressionThreshold): string
    {
        $prefix = "\0";
        if (\strlen($value) >= $compressionThreshold) {
            $value = self::huffmanEncode($value);
            $prefix = "\x80";
        }

        if (\strlen($value) < 0x7f) {
            return ($prefix | \chr(\strlen($value))) . $value;
        }

        return ($prefix | "\x7f") . self::encodeDynamicInteger(\strlen($value) - 0x7f) . $value;
    }
}

(function () {
    static::init();
})->bindTo(null, HPackNative::class)();

#define FFI_SCOPE "amphp-hpack-nghttp2"
#define FFI_LIB "libnghttp2.so"

typedef struct nghttp2_hd_deflater nghttp2_hd_deflater;

typedef struct nghttp2_hd_inflater nghttp2_hd_inflater;

typedef struct {
  uint8_t *name;
  uint8_t *value;

  size_t namelen;
  size_t valuelen;

  uint8_t flags;
} nghttp2_nv;

int nghttp2_hd_deflate_new(nghttp2_hd_deflater **deflater_ptr, size_t deflate_hd_table_bufsize_max);

ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, uint8_t *buf, size_t buflen, const nghttp2_nv *nva, size_t nvlen);

size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater, const nghttp2_nv *nva, size_t nvlen);

int nghttp2_hd_inflate_new(nghttp2_hd_inflater **inflater_ptr);

ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, int *inflate_flags, const uint8_t *in, size_t inlen, int in_final);

int nghttp2_hd_inflate_end_headers(nghttp2_hd_inflater *inflater);
{
    "name": "amphp/hpack",
    "homepage": "https://github.com/amphp/hpack",
    "description": "HTTP/2 HPack implementation.",
    "keywords": [
        "http-2",
        "hpack",
        "headers"
    ],
    "license": "MIT",
    "authors": [
        {
            "name": "Daniel Lowrey",
            "email": "rdlowrey@php.net"
        },
        {
            "name": "Bob Weinand"
        },
        {
            "name": "Niklas Keller",
            "email": "me@kelunik.com"
        },
        {
            "name": "Aaron Piotrowski",
            "email": "aaron@trowski.com"
        }
    ],
    "require": {
        "php": ">=7.1"
    },
    "require-dev": {
        "amphp/php-cs-fixer-config": "^2",
        "http2jp/hpack-test-case": "^1",
        "phpunit/phpunit": "^7 | ^8 | ^9",
        "nikic/php-fuzzer": "^0.0.11"
    },
    "autoload": {
        "psr-4": {
            "Amp\\Http\\": "src"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Amp\\Http\\": "test"
        }
    },
    "extra": {
        "branch-alias": {
            "dev-master": "3.x-dev"
        }
    },
    "repositories": [
        {
            "type": "package",
            "package": {
                "name": "http2jp/hpack-test-case",
                "version": "1.0",
                "source": {
                    "url": "https://github.com/http2jp/hpack-test-case",
                    "type": "git",
                    "reference": "origin/master"
                }
            }
        }
    ]
}
<?php

/*
 * Generic example for establishing a connection
 */

require 'support/bootstrap.php';

use Amp\Mysql;

$config = Mysql\MysqlConfig::fromAuthority(DB_HOST, DB_USER, DB_PASS, DB_NAME);

/* use an alternative charset... Default is utf8mb4_general_ci */
$config = $config->withCharset("ascii", "ascii_general_ci");

$db = Mysql\connect($config);

echo "Character set changed\n";

/* optional, as connection will automatically close when destructed. */
$db->close();
<?php

require 'support/bootstrap.php';
require 'support/generic-table.php';

use Amp\Mysql\MysqlConfig;
use Amp\Mysql\MysqlConnectionPool;

$db = new MysqlConnectionPool(MysqlConfig::fromAuthority(DB_HOST, DB_USER, DB_PASS, DB_NAME));

/* create same table than in 3-generic-with-yield.php */
createGenericTable($db);

$transaction = $db->beginTransaction();

$transaction->execute("INSERT INTO tmp VALUES (?, ? * 2)", [6, 6]);

$result = $transaction->execute("SELECT * FROM tmp WHERE a >= ?", [5]); // Two rows should be returned.

foreach ($result as $row) {
    \var_dump($row);
}

$transaction->rollback();

// Run same query again, should only return a single row since the other was rolled back.
$result = $db->execute("SELECT * FROM tmp WHERE a >= ?", [5]);

foreach ($result as $row) {
    \var_dump($row);
}

$db->close();
<?php

require __DIR__ . '/../../vendor/autoload.php';

//define('MYSQL_DEBUG', true);

/*
 * This file is not in VCS - create it if you want to set
 * the DB credentials without having them commited to VCS.
 */
@include_once __DIR__ . "/../../mysql-config.php";

if (!\defined('DB_HOST') || !\defined('DB_USER') || !\defined('DB_PASS') || !\defined('DB_NAME')) {
    print "We couldn't find a mysql-config.php." . PHP_EOL;

    ask_whether_config_should_be_created:
    print "Do you want to create a configuration to run the examples? (yes/no): ";

    $answer = \trim(\fgets(STDIN));

    if ($answer === "no") {
        print "Can't run any examples without valid database credentials." . PHP_EOL;
        exit(1);
    } elseif ($answer === "yes") {
        print "Database host: ";
        $host = \var_export(\trim(\fgets(STDIN)), true);

        print "Database user: ";
        $user = \var_export(\trim(\fgets(STDIN)), true);

        print "Database password: ";
        $pass = \var_export(\trim(\fgets(STDIN)), true);

        print "Database name: ";
        $name = \var_export(\trim(\fgets(STDIN)), true);

        $config = <<<CONFIG
<?php

const DB_HOST = $host;
const DB_USER = $user;
const DB_PASS = $pass;
const DB_NAME = $name;

CONFIG;

        \file_put_contents(__DIR__ . "/../../mysql-config.php", $config);
        require __DIR__ . "/../../mysql-config.php";

        print "Successfully created configuration, running example now." . PHP_EOL;
        print "You can find the config in " . \realpath(__DIR__ . "/../../mysql-config.php") . PHP_EOL;
        print \str_repeat("-", 80) . PHP_EOL;
    } else {
        goto ask_whether_config_should_be_created;
    }
}
<?php

use Amp\Future;
use Amp\Sql\Link;
use function Amp\async;

/* Create table and fill in a few rows for examples; for comments see 3-generic-with-yield.php */
function createGenericTable(Link $db): void
{
    $db->query("DROP TABLE IF EXISTS tmp");

    $db->query("CREATE TABLE tmp (a INT(10), b INT(10))");

    $statement = $db->prepare("INSERT INTO tmp (a, b) VALUES (?, ? * 2)");

    $futures = [];
    foreach (\range(1, 5) as $num) {
        $futures[] = async(fn () => $statement->execute([$num, $num]));
    }

    Future\await($futures);
}
<?php

require 'support/bootstrap.php';
require 'support/generic-table.php';

use Amp\Future;
use Amp\Mysql\MysqlConfig;
use Amp\Mysql\MysqlConnectionPool;
use Amp\Mysql\MysqlResult;
use function Amp\async;

$db = new MysqlConnectionPool(MysqlConfig::fromAuthority(DB_HOST, DB_USER, DB_PASS, DB_NAME));

/* create same table than in 3-generic-with-yield.php */
createGenericTable($db);

$future = [];

$future[] = async(fn () => $db->query("SELECT a * b FROM tmp"));
$future[] = async(fn () => $db->execute("SELECT POW(a, ?) AS power FROM tmp", [2]));

/**
 * @var MysqlResult $result1
 * @var MysqlResult $result2
 */
[$result1, $result2] = Future\await($future); // Both queries execute simultaneously. Wait for both to finish here.

print "Query 1 Results:" . PHP_EOL;
foreach ($result1 as $row) {
    \var_dump($row);
}

print  PHP_EOL . "Query 2 Results:" . PHP_EOL;
foreach ($result2 as $row) {
    \var_dump($row);
}

$db->query("DROP TABLE tmp");

$db->close();
<?php

require 'support/bootstrap.php';

use Amp\Future;
use Amp\Mysql\MysqlConfig;
use Amp\Mysql\MysqlConnectionPool;
use function Amp\async;

$db = new MysqlConnectionPool(MysqlConfig::fromAuthority(DB_HOST, DB_USER, DB_PASS, DB_NAME));

$db->query("DROP TABLE IF EXISTS tmp");

/* Create table and insert a few rows */
/* we need to wait until table is finished, so that we can insert. */
$db->query("CREATE TABLE IF NOT EXISTS tmp (a INT(10), b INT(10))");

print "Table successfully created." . PHP_EOL;

$statement = $db->prepare("INSERT INTO tmp (a, b) VALUES (?, ? * 2)");

$future = [];
foreach (\range(1, 5) as $num) {
    $future[] = async(fn () => $statement->execute([$num, $num]));
}

/* wait until everything is inserted */
$results = Future\await($future);

print "Insertion successful (if it wasn't, an exception would have been thrown by now)" . PHP_EOL;

$result = $db->query("SELECT a, b FROM tmp");

foreach ($result as $row) {
    var_dump($row);
}

$db->query("DROP TABLE tmp");

$db->close();
<?php

require 'support/bootstrap.php';
require 'support/generic-table.php';

use Amp\Mysql\MysqlConfig;
use Amp\Mysql\MysqlConnectionPool;

$db = new MysqlConnectionPool(MysqlConfig::fromAuthority(DB_HOST, DB_USER, DB_PASS, DB_NAME));

/* create same table than in 3-generic-with-yield.php */
createGenericTable($db);

/* multi statements are enabled by default, but generally stored procedures also might return multiple resultsets anyway */
$result = $db->query("SELECT a + b FROM tmp; SELECT a - b FROM tmp;");

$i = 0;
do {
    print PHP_EOL . "Query " . ++$i . " Results:" . PHP_EOL;
    foreach ($result as $row) {
        \var_dump($row);
    }
} while ($result = $result->getNextResult()); // Advances to the next result set.

$db->query("DROP TABLE tmp");

$db->close();
<?php

require 'support/bootstrap.php';

use Amp\Mysql\MysqlConfig;
use Amp\Mysql\MysqlConnectionPool;

$db = new MysqlConnectionPool(MysqlConfig::fromAuthority(DB_HOST, DB_USER, DB_PASS, DB_NAME));

$result = $db->query("SELECT 1 AS value");

foreach ($result as $row) {
    \var_dump($row['value']);
}

$db->close();
<?php

$config = new Amp\CodeStyle\Config;
$config->getFinder()
    ->in(__DIR__ . '/src')
    ->in(__DIR__ . '/test');

$config->setCacheFile(__DIR__ . '/.php_cs.cache');

return $config;
{
    "$schema": "vendor/phpbench/phpbench/phpbench.schema.json",
    "runner.bootstrap": "vendor/autoload.php",
    "runner.path": "benchmarks"
}
<?php

namespace Amp\Mysql\Bench;

use Amp\Future;
use Amp\Mysql\MysqlConnectionPool;
use Amp\Mysql\MysqlLink;
use Amp\Mysql\SocketMysqlConnector;
use Amp\Mysql\MysqlConnection;
use Amp\Mysql\MysqlConfig;
use PhpBench\Attributes\AfterMethods;
use PhpBench\Attributes\BeforeMethods;
use PhpBench\Attributes\Iterations;
use PhpBench\Attributes\Revs;
use PhpBench\Attributes\OutputTimeUnit;
use PhpBench\Attributes\Warmup;
use function Amp\async;

#[
    BeforeMethods('init'),
    AfterMethods('cleanup'),
    Iterations(1),
    Revs(100),
    Warmup(1),
    OutputTimeUnit('milliseconds', precision: 5),
]
class QueryBench extends AbstractBench
{
    protected MysqlConnectionPool $connectionPool;

    protected MysqlConnection $connection;

    protected \PDO $pdoConnection;

    protected int $maxQueries = 100;

    /** @var int */
    protected int $poolLimit = 10;

    public function init(): void
    {
        $config = MysqlConfig::fromAuthority($this->host, $this->user, $this->password);
        $connector = new SocketMysqlConnector;

        $this->connectionPool = new MysqlConnectionPool(
            config: $config,
            maxConnections: $this->poolLimit,
            connector: $connector,
        );

        $this->connection = $connector->connect($config);

        $this->pdoConnection = new \PDO("mysql:host=$this->host", $this->user, $this->password);
        $this->pdoConnection->setAttribute(\PDO::ATTR_EMULATE_PREPARES, false);
    }

    public function cleanup(): void
    {
        $this->connectionPool->close();
        $this->connection->close();
    }

    public function benchPdoQueries(): void
    {
        $statement = $this->pdoConnection->prepare("SELECT ?");

        foreach (\range(1, $this->maxQueries) as $i) {
            $statement->execute([$i]);
            $statement->fetch(\PDO::FETCH_ASSOC);
        }
    }

    public function benchSequentialQueries(): void
    {
        $statement = $this->connection->prepare("SELECT ?");

        foreach (\range(1, $this->maxQueries) as $i) {
            \iterator_to_array($statement->execute([$i]));
        }
    }

    public function benchConcurrentQueriesUsingSingleConnection(): void
    {
        $this->runConcurrentQueries($this->connection);
    }

    public function benchConcurrentQueriesUsingConnectionPool(): void
    {
        $this->runConcurrentQueries($this->connectionPool);
    }

    private function runConcurrentQueries(MysqlLink $link): void
    {
        $statement = $link->prepare("SELECT ?");

        Future\await(\array_map(
            fn (int $i) => async(fn () => \iterator_to_array($statement->execute([$i]))),
            \range(1, $this->maxQueries),
        ));
    }
}
<?php

namespace Amp\Mysql\Bench;

/**
 * docker run --rm -ti -e MYSQL_ROOT_PASSWORD=secret -p 10101:3306 mysql:latest --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
 */
class AbstractBench
{
    protected string $host = 'localhost:10101';

    protected string $user = 'root';

    protected string $password = 'secret';
}
<?php

namespace Amp\Mysql\Test;

const DB_HOST = 'localhost:3306';
const DB_USER = 'root';
const DB_PASS = 'root';

initialize(new \mysqli(DB_HOST, DB_USER, DB_PASS));
<?php declare(strict_types=1);

namespace Amp\Mysql;

use Amp\Sql\SqlConnection;

/**
 * @extends SqlConnection<MysqlConfig, MysqlResult, MysqlStatement, MysqlTransaction>
 */
interface MysqlConnection extends MysqlLink, SqlConnection
{
    /**
     * @return MysqlConfig Config object specific to this library.
     */
    #[\Override]
    public function getConfig(): MysqlConfig;
}
<?php declare(strict_types=1);

namespace Amp\Mysql;

use Amp\Cancellation;
use Amp\DeferredFuture;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Socket\SocketConnector;
use Amp\Socket\SocketException;
use Amp\Sql\SqlException;
use Amp\Sql\SqlTransactionIsolation;
use Amp\Sql\SqlTransactionIsolationLevel;
use Revolt\EventLoop;

final class SocketMysqlConnection implements MysqlConnection
{
    use ForbidCloning;
    use ForbidSerialization;

    private SqlTransactionIsolation $transactionIsolation = SqlTransactionIsolationLevel::Committed;

    private ?DeferredFuture $busy = null;

    /** @var \Closure():void Function used to release connection after a transaction has completed. */
    private readonly \Closure $release;

    public static function connect(
        SocketConnector $connector,
        MysqlConfig $config,
        ?Cancellation $cancellation = null,
    ): self {
        try {
            $socket = $connector->connect($config->getConnectionString(), $config->getConnectContext(), $cancellation);
        } catch (SocketException $exception) {
            throw new SqlException(
                'Connecting to the MySQL server failed: ' . $exception->getMessage(),
                previous: $exception,
            );
        }

        $processor = new Internal\ConnectionProcessor($socket, $config);
        $processor->connect($cancellation);
        return new self($processor);
    }

    private function __construct(private readonly Internal\ConnectionProcessor $processor)
    {
        $busy = &$this->busy;
        $this->release = static function () use (&$busy): void {
            $busy?->complete();
            $busy = null;
        };
    }

    #[\Override]
    public function getConfig(): MysqlConfig
    {
        return $this->processor->getConfig();
    }

    #[\Override]
    public function getTransactionIsolation(): SqlTransactionIsolation
    {
        return $this->transactionIsolation;
    }

    #[\Override]
    public function setTransactionIsolation(SqlTransactionIsolation $isolation): void
    {
        $this->transactionIsolation = $isolation;
    }

    /**
     * @return bool False if the connection has been closed.
     */
    #[\Override]
    public function isClosed(): bool
    {
        return $this->processor->isClosed();
    }

    /**
     * @return int Timestamp of the last time this connection was used.
     */
    #[\Override]
    public function getLastUsedAt(): int
    {
        return $this->processor->getLastUsedAt();
    }

    public function useCharacterSet(string $charset, string $collate): void
    {
        $this->processor->useCharacterSet($charset, $collate)->await();
    }

    #[\Override]
    public function close(): void
    {
        // Send close command if connection is not already in a closed or closing state
        if (!$this->processor->isClosed()) {
            $this->processor->sendClose()->await();
        }
    }

    #[\Override]
    public function onClose(\Closure $onClose): void
    {
        $this->processor->onClose($onClose);
    }

    public function useDatabase(string $database): void
    {
        $this->processor->useDatabase($database)->await();
    }

    #[\Override]
    public function query(string $sql): MysqlResult
    {
        while ($this->busy) {
            $this->busy->getFuture()->await();
        }

        return $this->processor->query($sql)->await();
    }

    #[\Override]
    public function beginTransaction(): MysqlTransaction
    {
        while ($this->busy) {
            $this->busy->getFuture()->await();
        }

        $this->busy = $deferred = new DeferredFuture();

        $sql = \sprintf(
            "SET SESSION TRANSACTION ISOLATION LEVEL %s; START TRANSACTION",
            $this->transactionIsolation->toSql(),
        );

        try {
            $this->processor->query($sql)->await();
        } catch (\Throwable $exception) {
            $this->busy = null;
            $deferred->complete();
            throw $exception;
        }

        $executor = new Internal\MysqlNestableExecutor($this->processor);
        return new Internal\MysqlConnectionTransaction($executor, $this->release, $this->transactionIsolation);
    }

    public function ping(): void
    {
        $this->processor->ping()->await();
    }

    #[\Override]
    public function prepare(string $sql): MysqlStatement
    {
        while ($this->busy) {
            $this->busy->getFuture()->await();
        }

        return $this->processor->prepare($sql)->await();
    }

    #[\Override]
    public function execute(string $sql, array $params = []): MysqlResult
    {
        $statement = $this->prepare($sql);
        return $statement->execute($params);
    }

    public function __destruct()
    {
        $processor = $this->processor;
        EventLoop::queue(static fn () => $processor->unreference());
    }
}
<?php declare(strict_types=1);

namespace Amp\Mysql;

use Amp\Sql\SqlException;

/** @see 14.6.4.1.1.1 Column Type */
enum MysqlDataType: int
{
    case Decimal = 0x00;
    case Tiny = 0x01;
    case Short = 0x02;
    case Long = 0x03;
    case Float = 0x04;
    case Double = 0x05;
    case Null = 0x06;
    case Timestamp = 0x07;
    case LongLong = 0x08;
    case Int24 = 0x09;
    case Date = 0x0a;
    case Time = 0x0b;
    case Datetime = 0x0c;
    case Year = 0x0d;
    case NewDate = 0x0e; // Internal, not used in protocol, see Date
    case Varchar = 0x0f;
    case Bit = 0x10;
    case Timestamp2 = 0x11; // Internal, not used in protocol, see Timestamp
    case Datetime2 = 0x12; // Internal, not used in protocol, see DateTime
    case Time2 = 0x13; // Internal, not used in protocol, see Time
    case Json = 0xf5;
    case NewDecimal = 0xf6;
    case Enum = 0xf7;
    case Set = 0xf8;
    case TinyBlob = 0xf9;
    case MediumBlob = 0xfa;
    case LongBlob = 0xfb;
    case Blob = 0xfc;
    case VarString = 0xfd;
    case String = 0xfe;
    case Geometry = 0xff;

    /**
     * @see 14.7.3 Binary Protocol Value
     *
     * @param int<0, max> $offset
     *
     * @throws SqlException
     */
    public function decodeBinary(string $bytes, int &$offset = 0, int $flags = 0): int|float|string|null
    {
        $unsigned = $flags & 0x20;

        switch ($this) {
            case self::String:
            case self::Varchar:
            case self::VarString:
            case self::Enum:
            case self::Set:
            case self::LongBlob:
            case self::MediumBlob:
            case self::Blob:
            case self::TinyBlob:
            case self::Geometry:
            case self::Bit:
            case self::Decimal:
            case self::NewDecimal:
            case self::Json:
                return self::decodeString($bytes, $offset);

            case self::LongLong:
                return $unsigned
                    ? self::decodeUnsigned64($bytes, $offset)
                    : self::decodeInt64($bytes, $offset);

            case self::Long:
            case self::Int24:
                return $unsigned
                    ? self::decodeUnsigned32($bytes, $offset)
                    : self::decodeInt32($bytes, $offset);

            case self::Short:
            case self::Year:
                return $unsigned
                    ? self::decodeUnsigned16($bytes, $offset)
                    : self::decodeInt16($bytes, $offset);

            case self::Tiny:
                return $unsigned
                    ? self::decodeUnsigned8($bytes, $offset)
                    : self::decodeInt8($bytes, $offset);

            case self::Double:
                $offset += 8;
                return \unpack("e", $bytes, $offset - 8)[1];

            case self::Float:
                $offset += 4;
                return \unpack("g", $bytes, $offset - 4)[1];

            case self::Date:
            case self::Datetime:
            case self::Timestamp:
                return $this->decodeDateTime($bytes, $offset);

            case self::Time:
                return $this->decodeTime($bytes, $offset);

            case self::Null:
                return null;

            default:
                throw new SqlException("Invalid type for Binary Protocol: 0x" . \dechex($this->value));
        }
    }

    /**
     * @param int<0, max> $offset
     *
     * @throws SqlException
     */
    public function decodeText(string $bytes, int &$offset = 0, int $flags = 0): int|float|string
    {
        $length = self::decodeUnsigned($bytes, $offset);
        $offset += $length;
        $data = \substr($bytes, $offset - $length, $length);

        switch ($this) {
            case self::LongLong:
                if ($flags & 0x20) {
                    return $data; // Return UNSIGNED BIGINT as a string.
                }
                // no break

            case self::Long:
                if (\PHP_INT_SIZE < 8) {
                    return $data; // Return BIGINT and UNSIGNED INT as string on 32-bit.
                }
                // no break

            case self::Int24:
            case self::Short:
            case self::Tiny:
            case self::Year:
                return (int) $data;

            case self::Double:
            case self::Float:
                return (float) $data;

            default:
                return $data;
        }
    }

    /**
     * @param int<0, max> $offset
     */
    private function decodeDateTime(string $bytes, int &$offset): string
    {
        $year = $month = $day = $hour = $minute = $second = $microsecond = 0;

        switch ($length = self::decodeUnsigned8($bytes, $offset)) {
            case 11:
                $position = $offset + 7;
                $microsecond = self::decodeUnsigned32($bytes, $position);
                // no break

            case 7:
                $position = $offset + 4;
                $hour = self::decodeUnsigned8($bytes, $position);
                $minute = self::decodeUnsigned8($bytes, $position);
                $second = self::decodeUnsigned8($bytes, $position);
                // no break

            case 4:
                $position = $offset;
                $year = self::decodeUnsigned16($bytes, $position);
                $month = self::decodeUnsigned8($bytes, $position);
                $day = self::decodeUnsigned8($bytes, $position);
                // no break

            case 0:
                break;

            default:
                throw new SqlException("Unexpected string length for datetime in binary protocol: $length");
        }

        $offset += $length;

        $result = \sprintf('%04d-%02d-%02d', $year, $month, $day);
        if ($this === self::Date) {
            return $result;
        }

        $result .= \sprintf(' %02d:%02d:%02d', $hour, $minute, $second);
        if ($microsecond) {
            $result .= \sprintf('.%06d', $microsecond);
        }

        return $result;
    }

    /**
     * @param int<0, max> $offset
     */
    private function decodeTime(string $bytes, int &$offset): string
    {
        $negative = $day = $hour = $minute = $second = $microsecond = 0;

        switch ($length = self::decodeUnsigned8($bytes, $offset)) {
            case 12:
                $position = $offset + 8;
                $microsecond = self::decodeUnsigned32($bytes, $position);
                // no break

            case 8:
                $position = $offset;
                $negative = self::decodeUnsigned8($bytes, $position);
                $day = self::decodeUnsigned32($bytes, $position);
                $hour = self::decodeUnsigned8($bytes, $position);
                $minute = self::decodeUnsigned8($bytes, $position);
                $second = self::decodeUnsigned8($bytes, $position);
                // no break

            case 0:
                break;

            default:
                throw new SqlException("Unexpected string length for time in binary protocol: $length");
        }

        $offset += $length;

        $hour += $day * 24;

        $result = \sprintf('%s%02d:%02d:%02d', ($negative ? "-" : ""), $hour, $minute, $second);
        if ($microsecond) {
            $result .= \sprintf('.%06d', $microsecond);
        }

        return $result;
    }

    /**
     * @param int<0, max> $offset
     */
    public static function decodeNullTerminatedString(string $bytes, int &$offset = 0): string
    {
        $length = \strpos($bytes, "\0", $offset);
        if ($length === false) {
            throw new \ValueError('Null not found in string');
        }

        $length -= $offset;
        $result = \substr($bytes, $offset, $length);
        $offset += $length + 1;
        \assert($offset >= 0);

        return $result;
    }

    /**
     * @param int<0, max> $offset
     *
     * @throws SqlException
     */
    public static function decodeString(string $bytes, int &$offset = 0): string
    {
        $length = self::decodeUnsigned($bytes, $offset);
        $offset += $length;
        return \substr($bytes, $offset - $length, $length);
    }

    /**
     * @param int<0, max> $offset
     *
     * @return int<0, max>
     */
    public static function decodeUnsigned(string $bytes, int &$offset = 0): int
    {
        $int = self::decodeUnsigned8($bytes, $offset);
        if ($int < 0xfb) {
            return $int;
        }

        return match ($int) {
            0xfc => self::decodeUnsigned16($bytes, $offset),
            0xfd => self::decodeUnsigned24($bytes, $offset),
            0xfe => self::decodeUnsigned64($bytes, $offset),
            // If this happens connection is borked...
            default => throw new SqlException("$int is not in ranges [0x00, 0xfa] or [0xfc, 0xfe]"),
        };
    }

    /**
     * @param int<0, max> $offset
     * @param int<0, max> $length
     */
    public static function decodeIntByLength(string $bytes, int $length, int &$offset = 0): int
    {
        $int = 0;
        while ($length) {
            $int = ($int << 8) + \ord($bytes[--$length + $offset]);
        }

        $offset += $length;

        return $int;
    }

    /**
     * @param int<0, max> $offset
     */
    public static function decodeInt8(string $bytes, int &$offset = 0): int
    {
        $int = \ord($bytes[$offset++]);
        if ($int < (1 << 7)) {
            return $int;
        }
        $shift = \PHP_INT_SIZE * 8 - 8;
        return $int << $shift >> $shift;
    }

    /**
     * @param int<0, max> $offset
     *
     * @return int<0, max>
     */
    public static function decodeUnsigned8(string $bytes, int &$offset = 0): int
    {
        $result = \ord($bytes[$offset++]);
        \assert($result >= 0);
        return $result;
    }

    /**
     * @param int<0, max> $offset
     */
    public static function decodeInt16(string $bytes, int &$offset = 0): int
    {
        $int = \unpack("v", $bytes, $offset)[1];
        $offset += 2;
        if ($int < (1 << 15)) {
            return $int;
        }
        $shift = \PHP_INT_SIZE * 8 - 16;
        return $int << $shift >> $shift;
    }

    /**
     * @param int<0, max> $offset
     *
     * @return int<0, max>
     */
    public static function decodeUnsigned16(string $bytes, int &$offset = 0): int
    {
        $offset += 2;
        return \unpack("v", $bytes, $offset - 2)[1];
    }

    /**
     * @param int<0, max> $offset
     */
    public static function decodeInt24(string $bytes, int &$offset = 0): int
    {
        $int = \unpack("V", \substr($bytes, $offset, 3) . "\x00")[1];
        $offset += 3;
        if ($int < (1 << 23)) {
            return $int;
        }
        $shift = \PHP_INT_SIZE * 8 - 24;
        return $int << $shift >> $shift;
    }

    /**
     * @param int<0, max> $offset
     *
     * @return int<0, max>
     */
    public static function decodeUnsigned24(string $bytes, int &$offset = 0): int
    {
        $result = \unpack("V", \substr($bytes, $offset, 3) . "\x00")[1];
        $offset += 3;
        return $result;
    }

    /**
     * @param int<0, max> $offset
     */
    public static function decodeInt32(string $bytes, int &$offset = 0): int
    {
        $offset += 4;

        if (\PHP_INT_SIZE > 4) {
            $int = \unpack("V", $bytes, $offset - 4)[1];
            if ($int < (1 << 31)) {
                return $int;
            }
            return $int << 32 >> 32;
        }

        return \unpack("V", $bytes, $offset - 4)[1];
    }

    /**
     * @param int<0, max> $offset
     *
     * @return int<0, max>
     */
    public static function decodeUnsigned32(string $bytes, int &$offset = 0): int
    {
        $result = \unpack("V", $bytes, $offset)[1];
        $offset += 4;

        \assert($result >= 0);

        return $result;
    }

    /**
     * @param int<0, max> $offset
     *
     * @return int<0, max>|string
     */
    public static function decodeUnsigned32WithGmp(string $bytes, int &$offset = 0): int|string
    {
        $offset += 4;

        if (\PHP_INT_SIZE > 4) {
            return \unpack("V", $bytes, $offset - 4)[1];
        }

        if (!\extension_loaded("gmp")) {
            throw new \Error('The GMP extension is required for UNSIGNED INT fields on 32-bit systems');
        }

        return \gmp_strval(\gmp_import(\substr($bytes, $offset - 4, 4), 1, \GMP_LSW_FIRST));
    }

    /**
     * @param int<0, max> $offset
     */
    public static function decodeInt64(string $bytes, int &$offset = 0): int
    {
        if (\PHP_INT_SIZE > 4) {
            $offset += 8;
            return \unpack("P", $bytes, $offset - 8)[1];
        }

        throw new \Error('64-bit integers are not supported by 32-bit builds of PHP');
    }

    public static function decodeInt64WithGmp(string $bytes, int &$offset = 0): int|string
    {
        $offset += 8;

        if (\PHP_INT_SIZE > 4) {
            return \unpack("P", $bytes, $offset - 8)[1];
        }

        if (!\extension_loaded("gmp")) {
            throw new \Error('The GMP extension is required for BIGINT fields on 32-bit systems');
        }

        return \gmp_strval(\gmp_import(\substr($bytes, $offset - 8, 8), 1, \GMP_LSW_FIRST));
    }

    /**
     * @param int<0, max> $offset
     *
     * @return int<0, max>
     */
    public static function decodeUnsigned64(string $bytes, int &$offset = 0): int
    {
        if (\PHP_INT_SIZE <= 4) {
            throw new \Error('64-bit integers are not supported by 32-bit builds of PHP');
        }

        $result = \unpack("P", $bytes, $offset)[1];
        $offset += 8;

        \assert($result >= 0);

        return $result;
    }

    /**
     * @param int<0, max> $offset
     */
    public static function decodeUnsigned64WithGmp(string $bytes, int &$offset = 0): string
    {
        $offset += 8;

        if (!\extension_loaded("gmp")) {
            throw new \Error('The GMP extension is required for UNSIGNED BIGINT fields');
        }

        return \gmp_strval(\gmp_import(\substr($bytes, $offset - 8, 8), 1, \GMP_LSW_FIRST));
    }

    public static function encodeInt(int $int): string
    {
        if ($int < 0xfb) {
            return self::encodeInt8($int);
        }

        if ($int < (1 << 16)) {
            return "\xfc" . self::encodeInt16($int);
        }

        if ($int < (1 << 24)) {
            return "\xfd" . self::encodeInt24($int);
        }

        return "\xfe" . self::encodeInt64($int);
    }

    public static function encodeInt8(int $int): string
    {
        return \pack("C", $int);
    }

    public static function encodeInt16(int $int): string
    {
        return \pack("v", $int);
    }

    public static function encodeInt24(int $int): string
    {
        return \substr(\pack("V", $int), 0, 3);
    }

    public static function encodeInt32(int $int): string
    {
        return \pack("V", $int);
    }

    public static function encodeInt64(int $int): string
    {
        return \pack("VV", $int & 0xffffffff, $int >> 32);
    }
}
<?php declare(strict_types=1);

namespace Amp\Mysql;

use Amp\Sql\SqlStatement;

/**
 * @extends SqlStatement<MysqlResult>
 */
interface MysqlStatement extends SqlStatement
{
    #[\Override]
    public function execute(array $params = []): MysqlResult;

    /**
     * @param int|string $paramId Parameter ID or name.
     * @param string $data Data to bind to parameter.
     *
     * @throws \Error If $paramId does not exist.
     */
    public function bind(int|string $paramId, string $data): void;

    /**
     * @return list<MysqlColumnDefinition>
     */
    public function getColumnDefinitions(): array;

    /**
     * @return list<MysqlColumnDefinition>
     */
    public function getParameterDefinitions(): array;

    /**
     * Reset statement to state just after preparing.
     */
    public function reset(): void;
}
<?php declare(strict_types=1);

namespace Amp\Mysql;

use Amp\Sql\SqlTransaction;

/**
 * @extends SqlTransaction<MysqlResult, MysqlStatement, MysqlTransaction>
 */
interface MysqlTransaction extends MysqlLink, SqlTransaction
{
}
<?php declare(strict_types=1);

namespace Amp\Mysql;

use Amp\Sql\Common\SqlCommonConnectionPool;
use Amp\Sql\SqlConnector;
use Amp\Sql\SqlResult;
use Amp\Sql\SqlTransaction;
use Amp\Sql\SqlTransactionIsolation;
use Amp\Sql\SqlTransactionIsolationLevel;

/**
 * @extends SqlCommonConnectionPool<MysqlConfig, MysqlResult, MysqlStatement, MysqlTransaction, MysqlConnection>
 */
final class MysqlConnectionPool extends SqlCommonConnectionPool implements MysqlConnection
{
    use Internal\MysqlStatementCreationDelegate;

    /**
     * @param positive-int $maxConnections
     * @param positive-int $idleTimeout
     * @param SqlConnector<MysqlConfig, MysqlConnection>|null $connector
     */
    public function __construct(
        MysqlConfig $config,
        int $maxConnections = self::DEFAULT_MAX_CONNECTIONS,
        int $idleTimeout = self::DEFAULT_IDLE_TIMEOUT,
        ?SqlConnector $connector = null,
        SqlTransactionIsolation $transactionIsolation = SqlTransactionIsolationLevel::Committed,
    ) {
        parent::__construct(
            config: $config,
            connector: $connector ?? mysqlConnector(),
            maxConnections: $maxConnections,
            idleTimeout: $idleTimeout,
            transactionIsolation: $transactionIsolation,
        );
    }

    #[\Override]
    protected function createResult(SqlResult $result, \Closure $release): MysqlResult
    {
        \assert($result instanceof MysqlResult);
        return new Internal\MysqlPooledResult($result, $release);
    }

    #[\Override]
    protected function createStatementPool(string $sql, \Closure $prepare): MysqlStatement
    {
        return new Internal\MysqlStatementPool($this, $sql, $prepare);
    }

    #[\Override]
    protected function createTransaction(SqlTransaction $transaction, \Closure $release): MysqlTransaction
    {
        return new Internal\MysqlPooledTransaction($transaction, $release);
    }

    /**
     * Changes return type to this library's configuration type.
     */
    #[\Override]
    public function getConfig(): MysqlConfig
    {
        return parent::getConfig();
    }

    /**
     * Changes return type to this library's Result type.
     */
    #[\Override]
    public function query(string $sql): MysqlResult
    {
        return parent::query($sql);
    }

    /**
     * Changes return type to this library's Statement type.
     */
    #[\Override]
    public function prepare(string $sql): MysqlStatement
    {
        return parent::prepare($sql);
    }

    /**
     * Changes return type to this library's Result type.
     */
    #[\Override]
    public function execute(string $sql, array $params = []): MysqlResult
    {
        return parent::execute($sql, $params);
    }

    /**
     * Changes return type to this library's Transaction type.
     */
    #[\Override]
    public function beginTransaction(): MysqlTransaction
    {
        return parent::beginTransaction();
    }
}
<?php declare(strict_types=1);

namespace Amp\Mysql;

use Amp\Cancellation;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Socket;
use Amp\Sql\SqlConfig;
use Amp\Sql\SqlConnector;

/**
 * @implements SqlConnector<MysqlConfig, MysqlConnection>
 */
final class SocketMysqlConnector implements SqlConnector
{
    use ForbidCloning;
    use ForbidSerialization;

    public function __construct(private readonly ?Socket\SocketConnector $connector = null)
    {
    }

    #[\Override]
    public function connect(SqlConfig $config, ?Cancellation $cancellation = null): MysqlConnection
    {
        if (!$config instanceof MysqlConfig) {
            throw new \TypeError(\sprintf("Must provide an instance of %s to MySQL connectors", MysqlConfig::class));
        }

        $connector = $this->connector ?? Socket\socketConnector();

        return SocketMysqlConnection::connect($connector, $config, $cancellation);
    }
}
<?php declare(strict_types=1);

namespace Amp\Mysql;

use Amp\Sql\SqlExecutor;

/**
 * @extends SqlExecutor<MysqlResult, MysqlStatement>
 */
interface MysqlExecutor extends SqlExecutor
{
    /**
     * @return MysqlResult Result object specific to this library.
     */
    #[\Override]
    public function query(string $sql): MysqlResult;

    /**
     * @return MysqlStatement Statement object specific to this library.
     */
    #[\Override]
    public function prepare(string $sql): MysqlStatement;

    /**
     * @return MysqlResult Result object specific to this library.
     */
    #[\Override]
    public function execute(string $sql, array $params = []): MysqlResult;
}
<?php declare(strict_types=1);

namespace Amp\Mysql;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;

final class MysqlColumnDefinition
{
    use ForbidCloning;
    use ForbidSerialization;

    /**
     * @param non-empty-string $table
     * @param non-empty-string $name
     * @param int<0, max> $length
     * @param int<0, max> $flags
     * @param int<0, max> $decimals
     * @param non-empty-string|null $originalTable
     * @param non-empty-string|null $originalName
     * @param int<0, max>|null $charset
     * @param non-empty-string|null $catalog
     * @param non-empty-string|null $schema
     */
    public function __construct(
        private readonly string $table,
        private readonly string $name,
        private readonly int $length,
        private readonly MysqlDataType $type,
        private readonly int $flags,
        private readonly int $decimals,
        private readonly string $defaults = '',
        private readonly ?string $originalTable = null,
        private readonly ?string $originalName = null,
        private readonly ?int $charset = null,
        private readonly ?string $catalog = null,
        private readonly ?string $schema = null,
    ) {
    }

    public function getTable(): string
    {
        return $this->table;
    }

    /**
     * @return non-empty-string
     */
    public function getName(): string
    {
        return $this->name;
    }

    /**
     * @return int<0, max>
     */
    public function getLength(): int
    {
        return $this->length;
    }

    public function getType(): MysqlDataType
    {
        return $this->type;
    }

    /**
     * @return int<0, max>
     */
    public function getFlags(): int
    {
        return $this->flags;
    }

    /**
     * @return int<0, max>
     */
    public function getDecimals(): int
    {
        return $this->decimals;
    }

    public function getDefaults(): string
    {
        return $this->defaults;
    }

    /**
     * @return non-empty-string|null
     */
    public function getOriginalTable(): ?string
    {
        return $this->originalTable;
    }

    /**
     * @return non-empty-string|null
     */
    public function getOriginalName(): ?string
    {
        return $this->originalName;
    }

    /**
     * @return int<0, max>|null
     */
    public function getCharset(): ?int
    {
        return $this->charset;
    }

    /**
     * @return non-empty-string|null
     */
    public function getCatalog(): ?string
    {
        return $this->catalog;
    }

    /**
     * @return non-empty-string|null
     */
    public function getSchema(): ?string
    {
        return $this->schema;
    }
}
<?php declare(strict_types=1);

namespace Amp\Mysql;

use Amp\Sql\SqlResult;

/**
 * @psalm-type TFieldType = int|float|string|null
 * @psalm-type TRowType = array<string, TFieldType>
 *
 * @extends SqlResult<TFieldType>
 */
interface MysqlResult extends SqlResult
{
    /**
     * Changes return type to this library's Result type.
     */
    #[\Override]
    public function getNextResult(): ?self;

    /**
     * @return int|null Insert ID of the last auto increment row if applicable to the result or null if no ID
     *                  is available.
     */
    public function getLastInsertId(): ?int;

    /**
     * @return list<MysqlColumnDefinition>|null
     */
    public function getColumnDefinitions(): ?array;
}
<?php declare(strict_types=1);

namespace Amp\Mysql\Internal;

use Amp\DeferredFuture;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Mysql\MysqlResult;
use Amp\Mysql\MysqlStatement;
use Amp\Sql\SqlConnectionException;
use Revolt\EventLoop;

/** @internal */
final class MysqlConnectionStatement implements MysqlStatement
{
    use ForbidCloning;
    use ForbidSerialization;

    private readonly int $totalParamCount;
    private readonly int $positionalParamCount;

    private array $named = [];

    /** @var array<string> */
    private array $prebound = [];

    private ?ConnectionProcessor $processor;

    private int $lastUsedAt;

    private readonly DeferredFuture $onClose;

    public function __construct(
        ConnectionProcessor $processor,
        private readonly string $query,
        private readonly int $statementId,
        private readonly array $byNamed,
        private readonly MysqlResultProxy $result
    ) {
        $this->processor = $processor;
        $this->totalParamCount = $this->result->columnsToFetch;

        $this->onClose = new DeferredFuture();

        $positionalParamCount = $this->totalParamCount;
        foreach ($this->byNamed as $name => $ids) {
            foreach ($ids as $id) {
                $this->named[$id] = $name;
                $positionalParamCount--;
            }
        }

        $this->positionalParamCount = $positionalParamCount;

        $this->lastUsedAt = \time();
    }

    private function getProcessor(): ConnectionProcessor
    {
        if ($this->processor === null) {
            throw new \Error("The statement has been closed");
        }

        if ($this->processor->isClosed()) {
            throw new SqlConnectionException("Connection went away");
        }

        return $this->processor;
    }

    #[\Override]
    public function isClosed(): bool
    {
        return !$this->processor || $this->processor->isClosed();
    }

    #[\Override]
    public function close(): void
    {
        if ($this->processor) {
            self::shutdown($this->processor, $this->statementId, $this->onClose);
            $this->processor = null;
        }
    }

    #[\Override]
    public function onClose(\Closure $onClose): void
    {
        $this->onClose->getFuture()->finally($onClose);
    }

    #[\Override]
    public function bind(int|string $paramId, string $data): void
    {
        if (\is_int($paramId)) {
            if ($paramId >= $this->positionalParamCount || $paramId < 0) {
                throw new \Error("Parameter $paramId is not defined for this prepared statement");
            }
            $i = $paramId;
        } else {
            if (!isset($this->byNamed[$paramId])) {
                throw new \Error("Named parameter :$paramId is not defined for this prepared statement");
            }
            $array = $this->byNamed[$paramId];
            $i = \reset($array);
        }

        do {
            $realId = -1;
            while (isset($this->named[++$realId]) || $i-- > 0) {
                if (!\is_numeric($paramId) && isset($this->named[$realId]) && $this->named[$realId] === $paramId) {
                    break;
                }
            }

            $this->getProcessor()->bindParam($this->statementId, $realId, $data);
        } while (isset($array) && $i = \next($array));

        $prior = $this->prebound[$paramId] ?? '';
        $this->prebound[$paramId] = $prior . $data;
    }

    #[\Override]
    public function execute(array $params = []): MysqlResult
    {
        $this->lastUsedAt = \time();

        $prebound = $args = [];
        for ($unnamed = $i = 0; $i < $this->totalParamCount; $i++) {
            if (isset($this->named[$i])) {
                $name = $this->named[$i];
                if (\array_key_exists($name, $params)) {
                    $args[$i] = $params[$name];
                } elseif (!\array_key_exists($name, $this->prebound)) {
                    throw new \Error("Named parameter '$name' missing for executing prepared statement");
                } else {
                    $prebound[$i] = $this->prebound[$name];
                }
            } elseif (\array_key_exists($unnamed, $params)) {
                $args[$i] = $params[$unnamed];
                $unnamed++;
            } elseif (!\array_key_exists($unnamed, $this->prebound)) {
                throw new \Error("Parameter $unnamed missing for executing prepared statement");
            } else {
                $prebound[$i] = $this->prebound[$unnamed++];
            }
        }

        return $this->getProcessor()
            ->execute($this->statementId, $this->query, $this->result->params, $prebound, $args)
            ->await();
    }

    #[\Override]
    public function getQuery(): string
    {
        return $this->query;
    }

    #[\Override]
    public function reset(): void
    {
        $this->getProcessor()
            ->resetStmt($this->statementId)
            ->await();
    }

    #[\Override]
    public function getColumnDefinitions(): array
    {
        return $this->result->getColumnDefinitions();
    }

    #[\Override]
    public function getParameterDefinitions(): array
    {
        return $this->result->getParameterDefinitions();
    }

    #[\Override]
    public function getLastUsedAt(): int
    {
        return $this->lastUsedAt;
    }

    public function __destruct()
    {
        if ($this->processor) {
            EventLoop::queue(self::shutdown(...), $this->processor, $this->statementId, $this->onClose);
        }
    }

    private static function shutdown(ConnectionProcessor $processor, int $stmtId, DeferredFuture $onClose): void
    {
        try {
            $processor->closeStmt($stmtId);
            $processor->unreference();
        } finally {
            $onClose->complete();
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\Mysql\Internal;

use Amp\Mysql\MysqlResult;
use Amp\Mysql\MysqlStatement;
use Amp\Sql\Common\SqlPooledStatement;
use Amp\Sql\SqlResult;

/**
 * @internal
 * @extends SqlPooledStatement<MysqlResult, MysqlStatement>
 */
final class MysqlPooledStatement extends SqlPooledStatement implements MysqlStatement
{
    /**
     * @param \Closure():void $release
     * @param (\Closure():void)|null $awaitBusyResource
     */
    public function __construct(
        private readonly MysqlStatement $statement,
        \Closure $release,
        ?\Closure $awaitBusyResource = null,
    ) {
        parent::__construct($statement, $release, $awaitBusyResource);
    }

    #[\Override]
    protected function createResult(SqlResult $result, \Closure $release): MysqlResult
    {
        \assert($result instanceof MysqlResult);
        return new MysqlPooledResult($result, $release);
    }

    /**
     * Changes return type to this library's Result type.
     */
    #[\Override]
    public function execute(array $params = []): MysqlResult
    {
        return parent::execute($params);
    }

    #[\Override]
    public function bind(int|string $paramId, string $data): void
    {
        $this->statement->bind($paramId, $data);
    }

    #[\Override]
    public function getColumnDefinitions(): array
    {
        return $this->statement->getColumnDefinitions();
    }

    #[\Override]
    public function getParameterDefinitions(): array
    {
        return $this->statement->getParameterDefinitions();
    }

    #[\Override]
    public function reset(): void
    {
        $this->statement->reset();
    }
}
<?php declare(strict_types=1);

namespace Amp\Mysql\Internal;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Mysql\MysqlExecutor;
use Amp\Mysql\MysqlResult;
use Amp\Mysql\MysqlStatement;
use Amp\Sql\Common\SqlNestableTransactionExecutor;

/**
 * @internal
 * @implements SqlNestableTransactionExecutor<MysqlResult, MysqlStatement>
 */
final class MysqlNestableExecutor implements MysqlExecutor, SqlNestableTransactionExecutor
{
    use ForbidCloning;
    use ForbidSerialization;

    public function __construct(
        private readonly ConnectionProcessor $processor,
    ) {
    }

    #[\Override]
    public function isClosed(): bool
    {
        return $this->processor->isClosed();
    }

    /**
     * @return int Timestamp of the last time this connection was used.
     */
    #[\Override]
    public function getLastUsedAt(): int
    {
        return $this->processor->getLastUsedAt();
    }

    #[\Override]
    public function close(): void
    {
        // Send close command if connection is not already in a closed or closing state
        if (!$this->processor->isClosed()) {
            $this->processor->sendClose()->await();
        }
    }

    #[\Override]
    public function onClose(\Closure $onClose): void
    {
        $this->processor->onClose($onClose);
    }

    #[\Override]
    public function query(string $sql): MysqlResult
    {
        return $this->processor->query($sql)->await();
    }

    #[\Override]
    public function prepare(string $sql): MysqlStatement
    {
        return $this->processor->prepare($sql)->await();
    }

    #[\Override]
    public function execute(string $sql, array $params = []): MysqlResult
    {
        $statement = $this->prepare($sql);
        return $statement->execute($params);
    }

    #[\Override]
    public function commit(): void
    {
        $this->query("COMMIT");
    }

    #[\Override]
    public function rollback(): void
    {
        $this->query("ROLLBACK");
    }

    #[\Override]
    public function createSavepoint(string $identifier): void
    {
        $this->query(\sprintf("SAVEPOINT `%s`", $identifier));
    }

    #[\Override]
    public function rollbackTo(string $identifier): void
    {
        $this->query(\sprintf("ROLLBACK TO `%s`", $identifier));
    }

    #[\Override]
    public function releaseSavepoint(string $identifier): void
    {
        $this->query(\sprintf("RELEASE SAVEPOINT `%s`", $identifier));
    }
}
<?php declare(strict_types=1);

namespace Amp\Mysql\Internal;

enum MysqlResultProxyState
{
    case Initial;
    case Fetched;
    case Complete;
}
<?php declare(strict_types=1);

namespace Amp\Mysql\Internal;

use Amp\Future;
use Amp\Mysql\MysqlResult;
use Amp\Sql\Common\SqlCommandResult;

/**
 * @internal
 * @psalm-import-type TFieldType from MysqlResult
 * @extends SqlCommandResult<TFieldType, MysqlResult>
 */
final class MysqlCommandResult extends SqlCommandResult implements MysqlResult
{
    private ?int $lastInsertId;

    public function __construct(int $affectedRows, int $lastInsertId)
    {
        /** @var Future<MysqlResult|null> $future Explicit declaration for Psalm. */
        $future = Future::complete();

        parent::__construct($affectedRows, $future);
        $this->lastInsertId = $lastInsertId ?: null; // Convert 0 to null
    }

    /**
     * Changes return type to this library's Result type.
     */
    #[\Override]
    public function getNextResult(): ?MysqlResult
    {
        return parent::getNextResult();
    }

    /**
     * @return int|null Insert ID of the last auto increment row or null if not applicable to the query.
     */
    #[\Override]
    public function getLastInsertId(): ?int
    {
        return $this->lastInsertId;
    }

    /**
     * @return null Always returns null as command results do not have a field list.
     */
    #[\Override]
    public function getColumnDefinitions(): ?array
    {
        return null; // Command results do not have a field list.
    }
}
<?php declare(strict_types=1);

namespace Amp\Mysql\Internal;

use Amp\Cache\AtomicCache;
use Amp\Cache\LocalCache;
use Amp\Sync\LocalKeyedMutex;

/** @internal */
final class PublicKeyCache
{
    private static ?AtomicCache $cache = null;

    public static function loadKey(string $pem): \OpenSSLAsymmetricKey
    {
        self::$cache ??= new AtomicCache(new LocalCache(32), new LocalKeyedMutex());

        return self::$cache->computeIfAbsent($pem, fn () => \openssl_pkey_get_public($pem));
    }

    /**
     * @psalm-suppress UnusedConstructor Private constructor to prevent instantiation.
     */
    private function __construct()
    {
    }
}
<?php declare(strict_types=1);

namespace Amp\Mysql\Internal;

use Amp\Mysql\MysqlResult;
use Amp\Mysql\MysqlStatement;
use Amp\Mysql\MysqlTransaction;
use Amp\Sql\SqlResult;

/** @internal */
trait MysqlTransactionDelegate
{
    #[\Override]
    protected function createResult(SqlResult $result, \Closure $release): MysqlResult
    {
        \assert($result instanceof MysqlResult);
        return new MysqlPooledResult($result, $release);
    }

    /**
     * Changes return type to this library's Result type.
     */
    #[\Override]
    public function query(string $sql): MysqlResult
    {
        return parent::query($sql);
    }

    /**
     * Changes return type to this library's Statement type.
     */
    #[\Override]
    public function prepare(string $sql): MysqlStatement
    {
        return parent::prepare($sql);
    }

    /**
     * Changes return type to this library's Result type.
     */
    #[\Override]
    public function execute(string $sql, array $params = []): MysqlResult
    {
        return parent::execute($sql, $params);
    }

    /**
     * Changes return type to this library's Transaction type.
     */
    #[\Override]
    public function beginTransaction(): MysqlTransaction
    {
        return parent::beginTransaction();
    }
}
<?php declare(strict_types=1);

namespace Amp\Mysql\Internal;

use Amp\Mysql\MysqlResult;
use Amp\Mysql\MysqlStatement;
use Amp\Mysql\MysqlTransaction;
use Amp\Sql\Common\SqlNestableTransactionExecutor;
use Amp\Sql\Common\SqlNestedTransaction;
use Amp\Sql\SqlTransaction;

/**
 * @internal
 * @extends SqlNestedTransaction<MysqlResult, MysqlStatement, MysqlTransaction, MysqlNestableExecutor>
 */
final class MysqlNestedTransaction extends SqlNestedTransaction implements MysqlTransaction
{
    use MysqlTransactionDelegate;

    /**
     * @param non-empty-string $identifier
     * @param \Closure():void $release
     */
    public function __construct(
        MysqlTransaction $transaction,
        MysqlNestableExecutor $executor,
        string $identifier,
        \Closure $release,
    ) {
        parent::__construct($transaction, $executor, $identifier, $release);
    }

    #[\Override]
    protected function createNestedTransaction(
        SqlTransaction $transaction,
        SqlNestableTransactionExecutor $executor,
        string $identifier,
        \Closure $release,
    ): MysqlTransaction {
        return new self($transaction, $executor, $identifier, $release);
    }
}
<?php declare(strict_types=1);

namespace Amp\Mysql\Internal;

/**
 * @internal
 *
 * @see 13.1.3.1.1 Session State Information
 */
enum SessionStateType: int
{
    case SystemVariables = 0x00;
    case Schema = 0x01;
    case StateChange = 0x02;
    case Gtids = 0x03;
    case TransactionCharacteristics = 0x04;
    case TransactionState = 0x05;
}
<?php declare(strict_types=1);

namespace Amp\Mysql\Internal;

use Amp\Mysql\MysqlResult;
use Amp\Sql\Common\SqlPooledResult;
use Amp\Sql\SqlResult;

/**
 * @internal
 * @psalm-import-type TFieldType from MysqlResult
 * @extends SqlPooledResult<TFieldType, MysqlResult>
 */
final class MysqlPooledResult extends SqlPooledResult implements MysqlResult
{
    private readonly MysqlResult $result;

    /**
     * @param \Closure():void $release
     */
    public function __construct(MysqlResult $result, \Closure $release)
    {
        parent::__construct($result, $release);
        $this->result = $result;
    }

    #[\Override]
    protected static function newInstanceFrom(SqlResult $result, \Closure $release): self
    {
        \assert($result instanceof MysqlResult);
        return new self($result, $release);
    }

    #[\Override]
    public function getNextResult(): ?MysqlResult
    {
        return parent::getNextResult();
    }

    #[\Override]
    public function getLastInsertId(): ?int
    {
        return $this->result->getLastInsertId();
    }

    #[\Override]
    public function getColumnDefinitions(): ?array
    {
        return $this->result->getColumnDefinitions();
    }
}
<?php declare(strict_types=1);

namespace Amp\Mysql\Internal;

use Amp\Mysql\MysqlStatement;
use Amp\Sql\SqlStatement;

/** @internal */
trait MysqlStatementCreationDelegate
{
    #[\Override]
    protected function createStatement(
        SqlStatement $statement,
        \Closure $release,
        ?\Closure $awaitBusyResource = null,
    ): MysqlStatement {
        \assert($statement instanceof MysqlStatement);
        return new MysqlPooledStatement($statement, $release, $awaitBusyResource);
    }
}
<?php declare(strict_types=1);

namespace Amp\Mysql\Internal;

use Amp\Mysql\MysqlResult;
use Amp\Mysql\MysqlStatement;
use Amp\Mysql\MysqlTransaction;
use Amp\Sql\Common\SqlConnectionTransaction;
use Amp\Sql\Common\SqlNestableTransactionExecutor;
use Amp\Sql\SqlTransaction;

/**
 * @internal
 * @extends SqlConnectionTransaction<MysqlResult, MysqlStatement, MysqlTransaction, MysqlNestableExecutor>
 */
final class MysqlConnectionTransaction extends SqlConnectionTransaction implements MysqlTransaction
{
    use MysqlStatementCreationDelegate;
    use MysqlTransactionDelegate;

    #[\Override]
    protected function createNestedTransaction(
        SqlTransaction $transaction,
        SqlNestableTransactionExecutor $executor,
        string $identifier,
        \Closure $release,
    ): MysqlTransaction {
        \assert($transaction instanceof MysqlTransaction);
        return new MysqlNestedTransaction($transaction, $executor, $identifier, $release);
    }
}
<?php declare(strict_types=1);

namespace Amp\Mysql\Internal;

use Amp\Mysql\MysqlResult;
use Amp\Mysql\MysqlStatement;
use Amp\Mysql\MysqlTransaction;
use Amp\Sql\Common\SqlPooledTransaction;
use Amp\Sql\SqlTransaction;

/**
 * @internal
 * @extends SqlPooledTransaction<MysqlResult, MysqlStatement, MysqlTransaction>
 */
final class MysqlPooledTransaction extends SqlPooledTransaction implements MysqlTransaction
{
    use MysqlStatementCreationDelegate;
    use MysqlTransactionDelegate;

    /**
     * @param \Closure():void $release
     */
    public function __construct(MysqlTransaction $transaction, \Closure $release)
    {
        parent::__construct($transaction, $release);
    }

    #[\Override]
    protected function createTransaction(SqlTransaction $transaction, \Closure $release): MysqlTransaction
    {
        \assert($transaction instanceof MysqlTransaction);
        return new MysqlPooledTransaction($transaction, $release);
    }
}
<?php declare(strict_types=1);

namespace Amp\Mysql\Internal;

/** @internal */
final class MysqlConnectionMetadata
{
    public int $affectedRows = 0;
    public int $insertId = 0;
    public int $statusFlags = 0;
    public ?int $warnings = null;
    public ?string $statusInfo = null;
    public array $sessionState = [];
    public ?string $errorMsg = null;
    public ?int $errorCode = null;
    public ?string $errorState = null; // begins with "#"

    public string $serverVersion = '';

    public int $charset = 0;
}
<?php declare(strict_types=1);

namespace Amp\Mysql\Internal;

use Amp\DeferredFuture;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Future;
use Amp\Mysql\MysqlColumnDefinition;
use Amp\Mysql\MysqlResult;
use Revolt\EventLoop;
use function Amp\async;

/**
 * @internal
 * @psalm-import-type TRowType from MysqlResult
 * @implements \IteratorAggregate<int, TRowType>
 */
final class MysqlConnectionResult implements MysqlResult, \IteratorAggregate
{
    use ForbidCloning;
    use ForbidSerialization;

    private readonly MysqlResultProxy $result;

    private readonly \Generator $generator;

    private ?Future $nextResult = null;

    public function __construct(MysqlResultProxy $result)
    {
        $this->result = $result;
        $this->generator = self::iterate($result);
    }

    private static function iterate(MysqlResultProxy $result): \Generator
    {
        static $mapper;

        $columnNames = \array_map(
            $mapper ??= static fn (MysqlColumnDefinition $cd) => $cd->getName(),
            $result->getColumnDefinitions(),
        );

        foreach ($result->rowIterator as $row) {
            yield \array_combine($columnNames, $row);
        }
    }

    public function __destruct()
    {
        EventLoop::queue(self::dispose(...), $this->generator);
    }

    private static function dispose(\Generator $generator): void
    {
        try {
            // Discard remaining rows in the result set.
            while ($generator->valid()) {
                $generator->next();
            }
        } catch (\Throwable) {
            // Ignore errors while discarding result.
        }
    }

    #[\Override]
    public function getIterator(): \Traversable
    {
        // Using a Generator to keep a reference to $this.
        yield from $this->generator;
    }

    #[\Override]
    public function fetchRow(): ?array
    {
        if (!$this->generator->valid()) {
            return null;
        }

        $current = $this->generator->current();
        $this->generator->next();
        return $current;
    }

    #[\Override]
    public function getNextResult(): ?MysqlResult
    {
        $this->nextResult ??= async(function (): ?MysqlResult {
            self::dispose($this->generator);

            $deferred = $this->result->next ??= new DeferredFuture;
            $result = $deferred->getFuture()->await();

            if ($result instanceof MysqlResultProxy) {
                return new self($result);
            }

            return $result; // Instance of CommandResult or null.
        });

        return $this->nextResult->await();
    }

    #[\Override]
    public function getRowCount(): ?int
    {
        return $this->result->affectedRows;
    }

    #[\Override]
    public function getColumnCount(): int
    {
        return $this->result->columnCount;
    }

    #[\Override]
    public function getLastInsertId(): ?int
    {
        return $this->result->insertId;
    }

    #[\Override]
    public function getColumnDefinitions(): ?array
    {
        return $this->result->getColumnDefinitions();
    }
}
<?php declare(strict_types=1);

namespace Amp\Mysql\Internal;

use Amp\Mysql\MysqlDataType;

/** @internal */
final class MysqlEncodedValue
{
    public static function fromValue(mixed $param): self
    {
        switch (\get_debug_type($param)) {
            case "string":
                return new self(MysqlDataType::VarString, MysqlDataType::encodeInt(\strlen($param)) . $param);

            case "int":
                if ($param >= -(1 << 7) && $param < (1 << 7)) {
                    return new self(MysqlDataType::Tiny, MysqlDataType::encodeInt8($param));
                }

                if ($param >= -(1 << 15) && $param < (1 << 15)) {
                    return new self(MysqlDataType::Short, MysqlDataType::encodeInt16($param));
                }

                if ($param >= -(1 << 31) && $param < (1 << 31)) {
                    return new self(MysqlDataType::Long, MysqlDataType::encodeInt32($param));
                }

                return new self(MysqlDataType::LongLong, MysqlDataType::encodeInt64($param));

            case "float":
                return new self(MysqlDataType::Double, \pack("e", $param));

            case "bool":
                $encoded = $param ? "\x01" : "\0";
                return new self(MysqlDataType::Tiny, $encoded);

            case "null":
                return new self(MysqlDataType::Null, "");

            default:
                if ($param instanceof \BackedEnum) {
                    return self::fromValue($param->value);
                }

                if ($param instanceof \Stringable) {
                    return self::fromValue((string) $param);
                }

                throw new \TypeError("Unexpected type for query parameter: " . \get_debug_type($param));
        }
    }

    public static function forTargetType(MysqlDataType $targetType, mixed $data): self
    {
        if ($data === null) {
            return new self(MysqlDataType::Null, "");
        }

        if ($data instanceof \Stringable) {
            $data = (string) $data;
        }

        if (!\is_string($data)) {
            throw new \TypeError(\sprintf(
                "Expected string or null for %s column data, got %s",
                $targetType->name,
                \get_debug_type($data),
            ));
        }

        return new self($targetType, MysqlDataType::encodeInt(\strlen($data)) . $data);
    }

    private function __construct(
        private readonly MysqlDataType $type,
        private readonly string $bytes,
    ) {
    }

    public function getType(): MysqlDataType
    {
        return $this->type;
    }

    public function getBytes(): string
    {
        return $this->bytes;
    }
}
<?php declare(strict_types=1);

namespace Amp\Mysql\Internal;

/** @internal */
enum ConnectionState
{
    case Unconnected;
    case Connecting;
    case Established;
    case Ready;
    case Closing;
    case Closed;
}
<?php declare(strict_types=1);

namespace Amp\Mysql\Internal;

use Amp\ByteStream\ResourceStream;
use Amp\Cancellation;
use Amp\DeferredFuture;
use Amp\File;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Future;
use Amp\Mysql\MysqlColumnDefinition;
use Amp\Mysql\MysqlConfig;
use Amp\Mysql\MysqlDataType;
use Amp\Mysql\MysqlResult;
use Amp\Parser\Parser;
use Amp\Socket\Socket;
use Amp\Sql\SqlConnectionException;
use Amp\Sql\SqlException;
use Amp\Sql\SqlQueryError;
use Amp\Sql\SqlTransientResource;
use Revolt\EventLoop;

/* @TODO
 * 14.2.3 Auth switch request??
 * 14.2.4 COM_CHANGE_USER
 */

/**
 * @internal
 * @see https://dev.mysql.com/doc/dev/mysql-server/latest/PAGE_PROTOCOL.html Protocol documentation.
 */
class ConnectionProcessor implements SqlTransientResource
{
    use ForbidCloning;
    use ForbidSerialization;

    private const COMPRESSION_MINIMUM_LENGTH = 860;
    private const MAX_PACKET_LENGTH = 0xffffff;

    const STATEMENT_PARAM_REGEX = <<<'REGEX'
        [
            # Skip all quoted groups.
            (['"])(?:\\(?:\\|\1)|(?!\1).)*+\1(*SKIP)(*FAIL)
            |
            # Unnamed parameters.
            (?<unnamed>
                # Match all question marks except those surrounded by "operator"-class characters on either side.
                (?<!(?<operators>[-+\\*/<>~!@#%^&|`?]))
                \?
                (?!\g<operators>|=)
                |
                :\?
            )
            |
            # Named parameters.
            (?<!:):(?<named>[a-zA-Z_][a-zA-Z0-9_]*)
        ]msxS
        REGEX;

    private Parser $parser;

    private int $seqId = -1;
    private int $compressionId = -1;

    private readonly Socket $socket;

    private ?string $query = null;
    private array $named = [];

    private ?\Closure $parseCallback = null;
    private ?\Closure $packetCallback = null;

    private MysqlConfig $config;

    private readonly MysqlConnectionMetadata $metadata;

    /** @var \SplQueue<DeferredFuture> */
    private readonly \SplQueue $deferreds;

    /** @var \SplQueue<\Closure():void> */
    private readonly \SplQueue $onReady;

    private ?MysqlResultProxy $result = null;

    private int $lastUsedAt;

    private int $connectionId = 0;
    private string $authPluginData = '';
    private int $capabilities = 0;
    private int $serverCapabilities = 0;
    private string $authPluginName = '';
    private int $refcount = 1;

    private ConnectionState $connectionState = ConnectionState::Unconnected;

    private ?DeferredFuture $paused = null;

    private const MAX_PACKET_SIZE = 0xffffff;
    private const MAX_UNCOMPRESSED_BUFLEN = 0xfffffb;

    private const CLIENT_LONG_FLAG = 0x00000004;
    private const CLIENT_CONNECT_WITH_DB = 0x00000008;
    private const CLIENT_COMPRESS = 0x00000020;
    private const CLIENT_LOCAL_INFILE = 0x00000080;
    private const CLIENT_PROTOCOL_41 = 0x00000200;
    private const CLIENT_SSL = 0x00000800;
    private const CLIENT_TRANSACTIONS = 0x00002000;
    private const CLIENT_SECURE_CONNECTION = 0x00008000;
    private const CLIENT_MULTI_STATEMENTS = 0x00010000;
    private const CLIENT_MULTI_RESULTS = 0x00020000;
    private const CLIENT_PS_MULTI_RESULTS = 0x00040000;
    private const CLIENT_PLUGIN_AUTH = 0x00080000;
    private const CLIENT_CONNECT_ATTRS = 0x00100000;
    private const CLIENT_SESSION_TRACK = 0x00800000;
    private const CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA = 0x00200000;
    private const CLIENT_DEPRECATE_EOF = 0x01000000;

    /* @see 14.1.3.4 Status Flags */
    private const SERVER_STATUS_IN_TRANS = 0x0001; // a transaction is active
    private const SERVER_STATUS_AUTOCOMMIT = 0x0002; // auto-commit is enabled
    private const SERVER_MORE_RESULTS_EXISTS = 0x0008;
    private const SERVER_STATUS_NO_GOOD_INDEX_USED = 0x0010;
    private const SERVER_STATUS_NO_INDEX_USED = 0x0020;
    private const SERVER_STATUS_CURSOR_EXISTS = 0x0040; // Used by Binary Protocol Resultset to signal that COM_STMT_FETCH has to be used to fetch the row-data.
    private const SERVER_STATUS_LAST_ROW_SENT = 0x0080;
    private const SERVER_STATUS_DB_DROPPED = 0x0100;
    private const SERVER_STATUS_NO_BACKSLASH_ESCAPES = 0x0200;
    private const SERVER_STATUS_METADATA_CHANGED = 0x0400;
    private const SERVER_QUERY_WAS_SLOW = 0x0800;
    private const SERVER_PS_OUT_PARAMS = 0x1000;
    private const SERVER_STATUS_IN_TRANS_READONLY = 0x2000; // in a read-only transaction
    private const SERVER_SESSION_STATE_CHANGED = 0x4000; // connection state information has changed

    private const OK_PACKET = 0x00;
    private const EXTRA_AUTH_PACKET = 0x01;
    private const LOCAL_INFILE_REQUEST = 0xfb;
    private const AUTH_SWITCH_PACKET = 0xfe;
    private const EOF_PACKET = 0xfe;
    private const ERR_PACKET = 0xff;

    public function __construct(Socket $socket, MysqlConfig $config)
    {
        $this->socket = $socket;
        $this->metadata = new MysqlConnectionMetadata();
        $this->config = $config;
        $this->lastUsedAt = \time();

        $this->deferreds = new \SplQueue();
        $this->onReady = new \SplQueue();

        $this->parser = new Parser($this->parseMysql());
    }

    #[\Override]
    public function isClosed(): bool
    {
        return match ($this->connectionState) {
            ConnectionState::Closing, ConnectionState::Closed => true,
            default => false,
        };
    }

    #[\Override]
    public function onClose(\Closure $onClose): void
    {
        $this->socket->onClose($onClose);
    }

    public function isReady(): bool
    {
        return $this->connectionState === ConnectionState::Ready;
    }

    public function unreference(): void
    {
        if (--$this->refcount || $this->isClosed()) {
            return;
        }

        $this->sendClose()->ignore();
    }

    private function ready(): void
    {
        if (!$this->deferreds->isEmpty()) {
            return;
        }

        if (!$this->onReady->isEmpty()) {
            $this->onReady->shift()();
            return;
        }

        $this->resetIds();
        if ($this->socket instanceof ResourceStream) {
            $this->socket->unreference();
        }
    }

    private function enqueueDeferred(DeferredFuture $deferred): void
    {
        \assert(!$this->socket->isClosed(), "The connection has been closed");
        $this->deferreds->push($deferred);
        if ($this->socket instanceof ResourceStream) {
            $this->socket->reference();
        }
    }

    public function connect(?Cancellation $cancellation = null): void
    {
        \assert(
            $this->connectionState === ConnectionState::Unconnected,
            self::class . "::connect() must not be called twice",
        );

        $this->connectionState = ConnectionState::Connecting;

        $this->enqueueDeferred($deferred = new DeferredFuture()); // Will be resolved in sendHandshake().

        $id = $cancellation?->subscribe($this->close(...));

        EventLoop::queue($this->read(...));

        $future = $deferred->getFuture();
        if ($id !== null) {
            $future = $future->finally(static fn () => $cancellation?->unsubscribe($id));
        }

        // if a charset is specified, we need to set before any query
        if ($this->config->getCharset() !== MysqlConfig::DEFAULT_CHARSET
            || $this->config->getCollation() !== MysqlConfig::DEFAULT_COLLATE
        ) {
            $future = $future->map(function (): void {
                $charset = $this->config->getCharset();
                $collate = $this->config->getCollation();

                $this->query("SET NAMES '$charset'" . ($collate === "" ? "" : " COLLATE '$collate'"))->await();
            });
        }

        if ($this->config->getSqlMode() !== null) {
            $future = $future->map(function (): void {
                $sqlMode = $this->config->getSqlMode();
                $this->query("SET SESSION sql_mode='$sqlMode'")->await();
            });
        }

        $future->await();
    }

    /**
     * @psalm-suppress UnusedVariable
     */
    private function read(): void
    {
        try {
            while (($bytes = $this->socket->read()) !== null) {
                \assert($this->writeDebugData($bytes, 'in'));

                $this->lastUsedAt = \time();

                $this->parser->push($bytes);
                $bytes = null; // Free last data read.

                $this->paused?->getFuture()->await(); // Pause next read if negotiating TLS.
            }
        } catch (\Throwable $exception) {
            // $exception used as previous exception below.
        } finally {
            $this->free($exception ?? null);
        }
    }

    private function dequeueDeferred(): DeferredFuture
    {
        \assert(!$this->deferreds->isEmpty(), 'Pending deferred not found when shifting from pending queue');
        return $this->deferreds->shift();
    }

    /**
     * @param \Closure():void $callback
     */
    private function appendTask(\Closure $callback): void
    {
        if ($this->packetCallback
            || $this->parseCallback
            || !$this->onReady->isEmpty()
            || !$this->deferreds->isEmpty()
            || $this->connectionState !== ConnectionState::Ready
        ) {
            $this->onReady->push($callback);
        } else {
            $callback();
        }
    }

    public function getMetadata(): MysqlConnectionMetadata
    {
        return clone $this->metadata;
    }

    public function getConfig(): MysqlConfig
    {
        return $this->config;
    }

    public function getConnectionId(): int
    {
        return $this->connectionId;
    }

    #[\Override]
    public function getLastUsedAt(): int
    {
        return $this->lastUsedAt;
    }

    protected function startCommand(\Closure $callback): Future
    {
        if ($this->isClosed()) {
            throw new \Error("The connection has been closed");
        }

        $deferred = new DeferredFuture;
        $this->appendTask(function () use ($callback, $deferred) {
            $this->seqId = $this->compressionId = -1;
            $this->enqueueDeferred($deferred);
            $callback();
        });
        return $deferred->getFuture();
    }

    public function useCharacterSet(string $charset, string $collate): Future
    {
        if ($collate === "" && false !== $offset = \strpos($charset, "_")) {
            $collate = $charset;
            $charset = \substr($collate, 0, $offset);
        }

        $query = "SET NAMES '$charset'" . ($collate === "" ? "" : " COLLATE '$collate'");
        $future = $this->query($query);

        $this->config = $this->config->withCharset($charset, $collate);

        return $future;
    }

    /** @see 14.6.3 COM_INIT_DB */
    public function useDatabase(string $database): Future
    {
        return $this->startCommand(function () use ($database): void {
            /** @psalm-suppress PropertyTypeCoercion */
            $this->config = $this->config->withDatabase($database);
            $this->write("\x02$database");
        });
    }

    /**
     * @see 14.6.4 COM_QUERY
     *
     * @return Future<MysqlResult>
     */
    public function query(string $query): Future
    {
        return $this->startCommand(function () use ($query): void {
            $this->query = $query;
            $this->parseCallback = $this->handleQuery(...);
            $this->write("\x03$query");
        });
    }

    /**
     * @see 14.7.4 COM_STMT_PREPARE
     *
     * @return Future<MysqlConnectionStatement>
     */
    public function prepare(string $query): Future
    {
        return $this->startCommand(function () use ($query): void {
            $this->query = $query;
            $this->parseCallback = $this->handlePrepare(...);

            $query = \preg_replace_callback(self::STATEMENT_PARAM_REGEX, function (array $m): string {
                static $index = 0;
                if (isset($m['named'])) {
                    $this->named[$m['named']][] = $index;
                }
                $index++;
                return "?";
            }, $query);

            $this->write("\x16$query");
        });
    }

    /** @see 14.6.18 COM_CHANGE_USER */
    /* @TODO broken, my test server doesn't support that command, can't test now
    public function changeUser($user, $pass, $db = null) {
        return $this->startCommand(function() use ($user, $pass, $db) {
            $this->config->user = $user;
            $this->config->pass = $pass;
            $this->config->db = $db;
            $payload = "\x11";

            $payload .= "$user\0";
            $auth = $this->secureAuth($this->config->pass, $this->authPluginData);
            if ($this->capabilities & self::CLIENT_SECURE_CONNECTION) {
                $payload .= ord($auth) . $auth;
            } else {
                $payload .= "$auth\0";
            }
            $payload .= "$db\0";

            $this->write($payload);
            $this->parseCallback = [$this, "authSwitchRequest"];
        });
    }
    */

    /** @see 14.6.15 COM_PING */
    public function ping(): Future
    {
        return $this->startCommand(fn () => $this->write("\x0e"));
    }

    /** @see 14.6.19 COM_RESET_CONNECTION */
    public function resetConnection(): Future
    {
        return $this->startCommand(fn () => $this->write("\x1f"));
    }

    /** @see 14.7.5 COM_STMT_SEND_LONG_DATA */
    public function bindParam(int $stmtId, int $paramId, string $data): void
    {
        $payload = ["\x18"];
        $payload[] = MysqlDataType::encodeInt32($stmtId);
        $payload[] = MysqlDataType::encodeInt16($paramId);
        $payload[] = $data;
        $this->appendTask(function () use ($payload): void {
            $this->resetIds();
            $this->write(\implode($payload));
            $this->ready();
        });
    }

    /** @see 14.7.6 COM_STMT_EXECUTE
     * prebound params: null-bit set, type MYSQL_TYPE_LONG_BLOB, no value
     * $params is by-ref, because the actual result object might not yet have been filled completely with data upon
     * call of this method ...
     *
     * @param list<MysqlColumnDefinition> $params
     * @param array<int, string> $prebound
     * @param array<int, mixed> $data
     */
    public function execute(int $stmtId, string $query, array $params, array $prebound, array $data = []): Future
    {
        $deferred = new DeferredFuture;
        $this->appendTask(function () use ($stmtId, $query, $params, $prebound, $data, $deferred): void {
            $payload = ["\x17"];
            $payload[] = MysqlDataType::encodeInt32($stmtId);
            $payload[] = \chr(0); // cursor flag // @TODO cursor types?!
            $payload[] = MysqlDataType::encodeInt32(1);

            $paramCount = \count($params);
            $bound = (!empty($data) || !empty($prebound)) ? 1 : 0;
            $types = [];
            $values = [];

            if ($paramCount) {
                $args = $data + \array_fill(0, $paramCount, null);
                \ksort($args);
                $args = \array_slice($args, 0, $paramCount);
                $paramList = \str_repeat("\0", ($paramCount + 7) >> 3);
                foreach ($args as $paramId => $param) {
                    if ($param === null) {
                        $offset = ($paramId >> 3);
                        $paramList[$offset] = $paramList[$offset] | \chr(1 << ($paramId & 0x7));
                    } else {
                        $bound = 1;
                    }

                    $paramType = $params[$paramId]->getType();

                    if (isset($prebound[$paramId])) {
                        $preboundType = match ($paramType) {
                            MysqlDataType::TinyBlob,
                            MysqlDataType::Blob,
                            MysqlDataType::MediumBlob,
                            MysqlDataType::LongBlob => MysqlDataType::LongBlob,
                            default => MysqlDataType::VarString,
                        };

                        $types[] = MysqlDataType::encodeInt16($preboundType->value);

                        continue;
                    }

                    $encodedValue = match ($paramType) {
                        MysqlDataType::TinyBlob,
                        MysqlDataType::Blob,
                        MysqlDataType::MediumBlob,
                        MysqlDataType::LongBlob => MysqlEncodedValue::forTargetType(MysqlDataType::LongBlob, $param),
                        MysqlDataType::Json => MysqlEncodedValue::forTargetType(MysqlDataType::Json, $param),
                        default => MysqlEncodedValue::fromValue($param),
                    };

                    $types[] = MysqlDataType::encodeInt16($encodedValue->getType()->value);
                    $values[] = $encodedValue->getBytes();
                }

                $payload[] = $paramList;
                $payload[] = \chr($bound);
                if ($bound) {
                    $payload[] = \implode($types);
                    $payload[] = \implode($values);
                }
            }

            $this->query = $query;

            $this->resetIds();
            $this->enqueueDeferred($deferred);
            $this->write(\implode($payload));
            // apparently LOAD DATA LOCAL INFILE requests are not supported via prepared statements
            $this->packetCallback = $this->handleExecute(...);
        });
        return $deferred->getFuture(); // do not use $this->startCommand(), that might unexpectedly reset the seqId!
    }

    /** @see 14.7.7 COM_STMT_CLOSE */
    public function closeStmt(int $stmtId): void
    {
        $payload = "\x19" . MysqlDataType::encodeInt32($stmtId);
        $this->appendTask(function () use ($payload): void {
            if ($this->connectionState === ConnectionState::Ready) {
                $this->resetIds();
                $this->write($payload);
                $this->resetIds(); // does not expect a reply - must be reset immediately
            }
            $this->ready();
        });
    }

    /** @see 14.6.5 COM_FIELD_LIST */
    public function listFields(string $table, string $like = "%"): Future
    {
        return $this->startCommand(function () use ($table, $like): void {
            $this->write("\x04$table\0$like");
            $this->parseCallback = $this->handleFieldList(...);
        });
    }

    public function listAllFields(string $table, string $like = "%"): Future
    {
        $map = function (?array $array) use (&$map): array {
            static $columns = [];

            if ($array === null) {
                return $columns;
            }

            [$columns[], $future] = $array;
            return $future->map($map)->await();
        };

        return $this->listFields($table, $like)->map($map);
    }

    /** @see 14.6.6 COM_CREATE_DB */
    public function createDatabase(string $db): Future
    {
        return $this->startCommand(fn () => $this->write("\x05$db"));
    }

    /** @see 14.6.7 COM_DROP_DB */
    public function dropDatabase(string $db): Future
    {
        return $this->startCommand(fn () => $this->write("\x06$db"));
    }

    /**
     * @see 14.6.8 COM_REFRESH
     */
    public function refresh(int $subcommand): Future
    {
        return $this->startCommand(fn () => $this->write("\x07" . \chr($subcommand)));
    }

    /** @see 14.6.9 COM_SHUTDOWN */
    public function shutdown(): Future
    {
        /* SHUTDOWN_DEFAULT / SHUTDOWN_WAIT_ALL_BUFFERS, only one in use */
        return $this->startCommand(fn () => $this->write("\x08\x00"));
    }

    /** @see 14.6.10 COM_STATISTICS */
    public function statistics(): Future
    {
        return $this->startCommand(function (): void {
            $this->write("\x09");
            $this->parseCallback = $this->readStatistics(...);
        });
    }

    /** @see 14.6.11 COM_PROCESS_INFO */
    public function processInfo(): Future
    {
        return $this->startCommand(function (): void {
            $this->write("\x0a");
            $this->query("SHOW PROCESSLIST");
        });
    }

    /** @see 14.6.13 COM_PROCESS_KILL */
    public function killProcess(int $process): Future
    {
        return $this->startCommand(fn () => $this->write("\x0c" . MysqlDataType::encodeInt32($process)));
    }

    /** @see 14.6.14 COM_DEBUG */
    public function debugStdout(): Future
    {
        return $this->startCommand(fn () => $this->write("\x0d"));
    }

    /** @see 14.7.8 COM_STMT_RESET */
    public function resetStmt(int $stmtId): Future
    {
        $payload = "\x1a" . MysqlDataType::encodeInt32($stmtId);
        $deferred = new DeferredFuture;
        $this->appendTask(function () use ($payload, $deferred): void {
            $this->resetIds();
            $this->enqueueDeferred($deferred);
            $this->write($payload);
        });
        return $deferred->getFuture();
    }

    /** @see 14.8.4 COM_STMT_FETCH */
    public function fetchStmt(int $stmtId): Future
    {
        $payload = "\x1c" . MysqlDataType::encodeInt32($stmtId) . MysqlDataType::encodeInt32(1);
        $deferred = new DeferredFuture;
        $this->appendTask(function () use ($payload, $deferred): void {
            $this->resetIds();
            $this->enqueueDeferred($deferred);
            $this->write($payload);
        });
        return $deferred->getFuture();
    }

    private function established(): void
    {
        // @TODO flags to use?
        $this->capabilities |= self::CLIENT_SESSION_TRACK
            | self::CLIENT_TRANSACTIONS
            | self::CLIENT_PROTOCOL_41
            | self::CLIENT_SECURE_CONNECTION
            | self::CLIENT_MULTI_RESULTS
            | self::CLIENT_PS_MULTI_RESULTS
            | self::CLIENT_MULTI_STATEMENTS
            | self::CLIENT_PLUGIN_AUTH
            | self::CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA;

        if (\extension_loaded("zlib") && $this->config->isCompressionEnabled()) {
            $this->capabilities |= self::CLIENT_COMPRESS;
        }

        if ($this->config->isLocalInfileEnabled()) {
            $this->capabilities |=  self::CLIENT_LOCAL_INFILE;
        }
    }

    /** @see 14.1.3.2 ERR-Packet */
    private function handleError(string $packet): void
    {
        $offset = 1;

        $this->metadata->errorCode = MysqlDataType::decodeUnsigned16($packet, $offset);

        $connecting = $this->connectionState === ConnectionState::Connecting
            || $this->connectionState === ConnectionState::Established;

        if ($this->capabilities & self::CLIENT_PROTOCOL_41 && !$connecting) {
            $this->metadata->errorState = \substr($packet, $offset, 6);
            $offset += 6;
        }

        $this->metadata->errorMsg = \substr($packet, $offset);

        $this->parseCallback = null;

        if ($connecting) {
            // connection failure
            $this->free(new SqlConnectionException(\sprintf(
                'Could not connect to %s: %s',
                $this->config->getConnectionString(),
                $this->metadata->errorMsg,
            )));
            return;
        }

        if ($this->result === null && $this->deferreds->isEmpty()) {
            // connection killed without pending query or active result
            $this->free(new SqlConnectionException('Connection closed after receiving an unexpected error packet'));
            return;
        }

        $deferred = $this->result ?? $this->dequeueDeferred();

        // normal error
        $exception = new SqlQueryError(\sprintf(
            'MySQL error (%d): %s %s',
            $this->metadata->errorCode,
            $this->metadata->errorState ?? 'Unknown state',
            $this->metadata->errorMsg,
        ), $this->query ?? '');

        $this->result = null;
        $this->query = null;
        $this->named = [];

        $deferred->error($exception);

        $this->ready();
    }

    /**
     * @see 14.1.3.1 OK-Packet
     *
     * @psalm-suppress TypeDoesNotContainType Psalm seems to be having trouble with enums.
     */
    private function parseOk(string $packet): void
    {
        $offset = 1;

        $this->metadata->affectedRows = MysqlDataType::decodeUnsigned($packet, $offset);
        $this->metadata->insertId = MysqlDataType::decodeUnsigned($packet, $offset);

        if ($this->capabilities & (self::CLIENT_PROTOCOL_41 | self::CLIENT_TRANSACTIONS)) {
            $this->metadata->statusFlags = MysqlDataType::decodeUnsigned16($packet, $offset);
            $this->metadata->warnings = MysqlDataType::decodeUnsigned16($packet, $offset);
        }

        if (!($this->capabilities & self::CLIENT_SESSION_TRACK)) {
            $this->metadata->statusInfo = \substr($packet, $offset);
            return;
        }

        // Even though it seems required according to 14.1.3.1, there is no length encoded string,
        // i.e. no trailing NULL byte ....???
        if (\strlen($packet) <= $offset) {
            $this->metadata->statusInfo = "";
            return;
        }

        $this->metadata->statusInfo = MysqlDataType::decodeString($packet, $offset);

        if (!($this->metadata->statusFlags & self::SERVER_SESSION_STATE_CHANGED)) {
            return;
        }

        $sessionState = MysqlDataType::decodeString($packet, $offset);

        while (\strlen($packet) > $offset) {
            $data = MysqlDataType::decodeString($sessionState, $offset);
            $type = MysqlDataType::decodeUnsigned8($sessionState, $offset);

            switch (SessionStateType::tryFrom($type)) {
                case SessionStateType::SystemVariables:
                    $var = MysqlDataType::decodeString($data, $offset);
                    $this->metadata->sessionState[$type][$var] = MysqlDataType::decodeString($data, $offset);
                    break;

                case SessionStateType::Schema:
                case SessionStateType::StateChange:
                case SessionStateType::Gtids:
                case SessionStateType::TransactionCharacteristics:
                case SessionStateType::TransactionState:
                    $this->metadata->sessionState[$type] = MysqlDataType::decodeString($data, $offset);
                    break;

                default:
                    throw new \Error("$type is not a valid mysql session state type");
            }
        }
    }

    private function handleOk(string $packet): void
    {
        $this->parseOk($packet);
        $this->dequeueDeferred()->complete(
            new MysqlCommandResult($this->metadata->affectedRows, $this->metadata->insertId),
        );
        $this->ready();
    }

    /** @see 14.1.3.3 EOF-Packet */
    private function parseEof(string $packet): void
    {
        $offset = 1;
        if ($this->capabilities & self::CLIENT_PROTOCOL_41) {
            $this->metadata->warnings = MysqlDataType::decodeUnsigned16($packet, $offset);
            $this->metadata->statusFlags = MysqlDataType::decodeUnsigned16($packet, $offset);
        }
    }

    private function handleEof(string $packet): void
    {
        $this->parseEof($packet);
        $exception = new SqlException($this->metadata->errorMsg ?? 'Unknown error', $this->metadata->errorCode ?? 0);
        $this->dequeueDeferred()->error($exception);
        $this->ready();
    }

    /** @see 14.2.5 Connection Phase Packets */
    private function handleHandshake(string $packet): void
    {
        $offset = 1;

        $protocol = \ord($packet[0]);

        if ($protocol === self::ERR_PACKET) {
            $this->handleError($packet);
            return;
        }

        if ($protocol !== 0x0a) {
            throw new SqlConnectionException("Unsupported protocol version ".\ord($packet[0])." (Expected: 10)");
        }

        $this->metadata->serverVersion = MysqlDataType::decodeNullTerminatedString($packet, $offset);

        $this->connectionId = MysqlDataType::decodeUnsigned32($packet, $offset);

        $this->authPluginData = \substr($packet, $offset, 8);
        $offset += 8;

        $offset += 1; // filler byte

        $this->serverCapabilities = MysqlDataType::decodeUnsigned16($packet, $offset);

        if (\strlen($packet) > $offset) {
            $this->metadata->charset = MysqlDataType::decodeUnsigned8($packet, $offset);
            $this->metadata->statusFlags = MysqlDataType::decodeUnsigned16($packet, $offset);
            $this->serverCapabilities += MysqlDataType::decodeUnsigned16($packet, $offset) << 16;

            $authPluginDataLen = $this->serverCapabilities & self::CLIENT_PLUGIN_AUTH
                ? MysqlDataType::decodeUnsigned8($packet, $offset)
                : 0;

            if ($this->serverCapabilities & self::CLIENT_SECURE_CONNECTION) {
                $offset += 10;

                $strlen = \max(13, $authPluginDataLen - 8);
                $this->authPluginData .= \substr($packet, $offset, $strlen);
                $offset += $strlen;

                if ($this->serverCapabilities & self::CLIENT_PLUGIN_AUTH) {
                    $this->authPluginName = MysqlDataType::decodeNullTerminatedString($packet, $offset);
                }
            }
        }

        $this->sendHandshake();
    }

    /** @see 14.2.5 Connection Phase Packets */
    private function handleAuthSwitch(string $packet): void
    {
        $offset = 1;
        $this->authPluginName = MysqlDataType::decodeNullTerminatedString($packet, $offset);
        $this->authPluginData = \substr($packet, $offset);
        $this->sendAuthSwitchResponse();
    }

    private function sendAuthSwitchResponse(): void
    {
        $this->write($this->getAuthData());
    }

    /** @see 14.6.4.1.2 LOCAL INFILE Request */
    private function handleLocalInfileRequest(string $packet): void
    {
        EventLoop::queue(function () use ($packet): void {
            try {
                $filePath = \substr($packet, 1);
                if (!\function_exists("Amp\\File\\openFile")) {
                    throw new \Error("amphp/file must be installed for LOCAL INFILE queries");
                }

                $fileHandle = File\openFile($filePath, 'r');

                while (null !== ($chunk = $fileHandle->read())) {
                    $this->write($chunk);
                }
                $this->write("");
            } catch (\Throwable $e) {
                $this->dequeueDeferred()->error(new SqlConnectionException("Failed to transfer a file to the server", 0, $e));
            }
        });
    }

    /** @see 14.6.4.1.1 Text Resultset */
    private function handleQuery(string $packet): void
    {
        switch (\ord($packet[0])) {
            case self::OK_PACKET:
                $this->parseOk($packet);

                if ($this->metadata->statusFlags & self::SERVER_MORE_RESULTS_EXISTS) {
                    $this->result = new MysqlResultProxy(
                        affectedRows: $this->metadata->affectedRows,
                        insertId: $this->metadata->insertId
                    );
                    $this->result->markDefinitionsFetched();
                    $this->dequeueDeferred()->complete(new MysqlConnectionResult($this->result));
                    $this->successfulResultFetch();
                } else {
                    $this->parseCallback = null;
                    $this->dequeueDeferred()->complete(new MysqlCommandResult(
                        $this->metadata->affectedRows,
                        $this->metadata->insertId
                    ));
                    $this->ready();
                }
                return;
            case self::LOCAL_INFILE_REQUEST:
                if ($this->config->isLocalInfileEnabled()) {
                    $this->handleLocalInfileRequest($packet);
                } else {
                    $this->dequeueDeferred()->error(new SqlConnectionException("Unexpected LOCAL_INFILE_REQUEST packet"));
                }
                return;
            case self::ERR_PACKET:
                $this->handleError($packet);
                return;
        }

        $this->parseCallback = $this->handleTextColumnDefinition(...);
        $this->result = new MysqlResultProxy(MysqlDataType::decodeUnsigned($packet));
        $this->dequeueDeferred()->complete(new MysqlConnectionResult($this->result));
    }

    /** @see 14.7.1 Binary Protocol Resultset */
    private function handleExecute(string $packet): void
    {
        $this->parseCallback = $this->handleBinaryColumnDefinition(...);
        $this->result = new MysqlResultProxy(\ord($packet[0]));
        $this->dequeueDeferred()->complete(new MysqlConnectionResult($this->result));
    }

    private function handleFieldList(string $packet): void
    {
        if (\ord($packet[0]) === self::ERR_PACKET) {
            $this->parseCallback = null;
            $this->handleError($packet);
        } elseif (\ord($packet[0]) === self::EOF_PACKET) {
            $this->parseCallback = null;
            $this->parseEof($packet);
            $this->dequeueDeferred()->complete();
            $this->ready();
        } else {
            $this->enqueueDeferred($deferred = new DeferredFuture);
            $this->dequeueDeferred()->complete([$this->parseColumnDefinition($packet), $deferred]);
        }
    }

    private function handleTextColumnDefinition(string $packet): void
    {
        $this->handleColumnDefinition($packet, $this->handleTextResultSetRow(...));
    }

    private function handleBinaryColumnDefinition(string $packet): void
    {
        $this->handleColumnDefinition($packet, $this->handleBinaryResultSetRow(...));
    }

    private function handleColumnDefinition(string $packet, \Closure $parseCallback): void
    {
        \assert($this->result !== null, 'Connection result was in invalid state');

        if (!$this->result->columnsToFetch--) {
            $this->result->markDefinitionsFetched();
            if (\ord($packet[0]) === self::ERR_PACKET) {
                $this->parseCallback = null;
                $this->handleError($packet);
            } else {
                $cb = $this->parseCallback = $parseCallback;
                if ($this->capabilities & self::CLIENT_DEPRECATE_EOF) {
                    $cb($packet);
                } else {
                    $this->parseEof($packet);
                    // we don't need the EOF packet, skip!
                }
            }
            return;
        }

        $this->result->columns[] = $this->parseColumnDefinition($packet);
    }

    private function prepareParams(string $packet): void
    {
        \assert($this->result !== null, 'Connection result was in invalid state');

        if (!$this->result->columnsToFetch--) {
            $this->result->columnsToFetch = $this->result->columnCount;
            if (!$this->result->columnsToFetch) {
                $this->prepareFields($packet);
            } else {
                $this->parseCallback = $this->prepareFields(...);
            }
            return;
        }

        $this->result->params[] = $this->parseColumnDefinition($packet);
    }

    private function prepareFields(string $packet): void
    {
        \assert($this->result !== null, 'Connection result was in invalid state');

        if (!$this->result->columnsToFetch--) {
            $this->parseCallback = null;
            $this->query = null;
            $result = $this->result;
            $this->result = null;
            $this->ready();
            $result->markDefinitionsFetched();

            return;
        }

        $this->result->columns[] = $this->parseColumnDefinition($packet);
    }

    /** @see 14.6.4.1.1.2 Column Defintion */
    private function parseColumnDefinition(string $packet): MysqlColumnDefinition
    {
        $offset = 0;
        $column = [];

        if ($this->capabilities & self::CLIENT_PROTOCOL_41) {
            $column["catalog"] = MysqlDataType::decodeString($packet, $offset);
            $column["schema"] = MysqlDataType::decodeString($packet, $offset);
            $column["table"] = MysqlDataType::decodeString($packet, $offset);
            $column["originalTable"] = MysqlDataType::decodeString($packet, $offset);
            $column["name"] = MysqlDataType::decodeString($packet, $offset);
            $column["originalName"] = MysqlDataType::decodeString($packet, $offset);
            $fixLength = MysqlDataType::decodeUnsigned($packet, $offset);

            $column["charset"] = MysqlDataType::decodeUnsigned16($packet, $offset);
            $column["length"] = MysqlDataType::decodeUnsigned32($packet, $offset);
            $column["type"] = MysqlDataType::from(MysqlDataType::decodeUnsigned8($packet, $offset));
            $column["flags"] = MysqlDataType::decodeUnsigned16($packet, $offset);
            $column["decimals"] = MysqlDataType::decodeUnsigned8($packet, $offset);

            $offset += $fixLength;
        } else {
            $column["table"] = MysqlDataType::decodeString($packet, $offset);
            $column["name"] = MysqlDataType::decodeString($packet, $offset);

            $columnLength = MysqlDataType::decodeUnsigned($packet, $offset);
            $column["length"] = MysqlDataType::decodeIntByLength($packet, $columnLength, $offset);

            $typeLength = MysqlDataType::decodeUnsigned($packet, $offset);
            $column["type"] = MysqlDataType::from(MysqlDataType::decodeIntByLength($packet, $typeLength, $offset));

            $flagLength = $this->capabilities & self::CLIENT_LONG_FLAG
                ? MysqlDataType::decodeUnsigned($packet, $offset)
                : MysqlDataType::decodeUnsigned8($packet, $offset);

            if ($flagLength > 2) {
                $column["flags"] = MysqlDataType::decodeUnsigned16($packet, $offset);
            } else {
                $column["flags"] = MysqlDataType::decodeUnsigned8($packet, $offset);
            }

            $column["decimals"] = MysqlDataType::decodeUnsigned8($packet, $offset);
        }

        if ($offset < \strlen($packet)) {
            $column["defaults"] = MysqlDataType::decodeString($packet, $offset);
        }

        /** @psalm-suppress InvalidScalarArgument, ArgumentTypeCoercion */
        return new MysqlColumnDefinition(...$column);
    }

    private function successfulResultFetch(): void
    {
        \assert($this->result !== null, 'Connection result was in invalid state');

        $deferred = $this->result->next ??= new DeferredFuture();

        if ($this->metadata->statusFlags & self::SERVER_MORE_RESULTS_EXISTS) {
            $this->parseCallback = $this->handleQuery(...);
            $this->enqueueDeferred($deferred);
        } else {
            $this->parseCallback = null;
            $this->query = null;
            $deferred->complete();
            $this->ready();
        }

        $this->result->complete();
        $this->result = null;
    }

    /** @see 14.6.4.1.1.3 Resultset Row */
    private function handleTextResultSetRow(string $packet): void
    {
        $packetType = \ord($packet[0]);
        if ($packetType === self::EOF_PACKET) {
            if ($this->capabilities & self::CLIENT_DEPRECATE_EOF) {
                $this->parseOk($packet);
            } else {
                $this->parseEof($packet);
            }
            $this->successfulResultFetch();
            return;
        }

        if ($packetType === self::ERR_PACKET) {
            $this->handleError($packet);
            return;
        }

        \assert($this->result !== null, 'Connection result was in invalid state');

        $offset = 0;
        $fields = [];
        for ($i = 0; $offset < \strlen($packet); ++$i) {
            if (\ord($packet[$offset]) === 0xfb) {
                $fields[] = null;
                $offset += 1;
            } else {
                $column = $this->result->columns[$i] ?? throw new \RuntimeException("Definition missing for column $i");
                $fields[] = $column->getType()->decodeText($packet, $offset, $column->getFlags());
            }
        }

        $this->result->pushRow($fields);
    }

    /** @see 14.7.2 Binary Protocol Resultset Row */
    private function handleBinaryResultSetRow(string $packet): void
    {
        $packetType = \ord($packet[0]);
        if ($packetType === self::EOF_PACKET) {
            $this->parseEof($packet);
            $this->successfulResultFetch();
            return;
        }

        if ($packetType === self::ERR_PACKET) {
            $this->handleError($packet);
            return;
        }

        \assert($this->result !== null, 'Connection result was in invalid state');

        $offset = 1; // skip first byte
        $offset += ($this->result->columnCount + 9) >> 3;
        $fields = [];

        for ($i = 0; $i < $this->result->columnCount; $i++) {
            if (\ord($packet[1 + (($i + 2) >> 3)]) & (1 << (($i + 2) % 8))) {
                $fields[] = null;
                continue;
            }

            $column = $this->result->columns[$i] ?? throw new \RuntimeException("Definition missing for column $i");
            \assert($offset >= 0 && $offset < \strlen($packet));
            $fields[] = $column->getType()->decodeBinary($packet, $offset, $column->getFlags());
        }

        $this->result->pushRow($fields);
    }

    /** @see 14.7.4.1 COM_STMT_PREPARE Response */
    private function handlePrepare(string $packet): void
    {
        switch (\ord($packet[0])) {
            case self::OK_PACKET:
                break;
            case self::ERR_PACKET:
                $this->handleError($packet);
                return;
            default:
                throw new SqlConnectionException("Unexpected value for first byte of COM_STMT_PREPARE Response");
        }

        $offset = 1;

        $stmtId = MysqlDataType::decodeUnsigned32($packet, $offset);
        $columns = MysqlDataType::decodeUnsigned16($packet, $offset);
        $params = MysqlDataType::decodeUnsigned16($packet, $offset);

        $offset += 1; // filler

        $this->metadata->warnings = MysqlDataType::decodeUnsigned16($packet, $offset);

        $this->result = new MysqlResultProxy($columns, $params);
        $this->refcount++;
        \assert($this->query !== null, 'Invalid value for connection query');
        $this->dequeueDeferred()->complete(new MysqlConnectionStatement($this, $this->query, $stmtId, $this->named, $this->result));
        $this->named = [];
        if ($params) {
            $this->parseCallback = $this->prepareParams(...);
        } else {
            $this->prepareParams($packet);
        }
    }

    private function readStatistics(string $packet): void
    {
        $this->dequeueDeferred()->complete($packet);
        $this->parseCallback = null;
        $this->ready();
    }

    /** @see 14.6.2 COM_QUIT */
    public function sendClose(): Future
    {
        return $this->startCommand(function (): void {
            $this->write("\x01");
            $this->connectionState = ConnectionState::Closing;
        });
    }

    #[\Override]
    public function close(): void
    {
        $this->free();
    }

    private function free(?\Throwable $exception = null): void
    {
        if ($this->connectionState === ConnectionState::Closing) {
            \assert(!$this->deferreds->isEmpty(), 'Closing deferred not found in array when in closing state');
            $this->deferreds->pop()->complete();
        }

        $this->connectionState = ConnectionState::Closed;

        $this->socket->close();
        $this->parser->cancel();

        if (!$this->deferreds->isEmpty() || $this->result) {
            if (!$exception instanceof SqlConnectionException) {
                $exception = new SqlConnectionException("Connection closed unexpectedly", 0, $exception ?? null);
            }

            while (!$this->deferreds->isEmpty()) {
                $this->deferreds->shift()->error($exception);
            }

            $this->result?->error($exception);
            $this->result = null;
        }
    }

    private function resetIds(): void
    {
        $this->seqId = $this->compressionId = -1;
    }

    private function write(string $packet): void
    {
        \assert(!$this->socket->isClosed(), 'The connection was closed during a call to write');

        while (\strlen($packet) >= self::MAX_PACKET_LENGTH) {
            $this->sendPacket(\substr($packet, 0, self::MAX_PACKET_LENGTH));
            $packet = \substr($packet, self::MAX_PACKET_LENGTH);
        }

        $this->sendPacket($packet);
    }

    /**
     * @codeCoverageIgnore
     */
    private function writeDebugData(string $packet, string $label): bool
    {
        if (\defined("MYSQL_DEBUG")) {
            \fwrite(STDERR, "$label: ");
            for ($i = 0; $i < \min(\strlen($packet), 200); $i++) {
                \fwrite(STDERR, \dechex(\ord($packet[$i])) . " ");
            }
            $r = \range("\0", "\x1f");
            unset($r[10], $r[9]);
            \fwrite(STDERR, "len: ".\strlen($packet)."\n");
            \fwrite(STDERR, \str_replace($r, ".", \substr($packet, 0, 200))."\n");
        }

        return true;
    }

    /**
     * @see 14.1.2 MySQL Packets
     */
    private function sendPacket(string $out): void
    {
        $packet = MysqlDataType::encodeInt32(\strlen($out) | (++$this->seqId << 24)) . $out;

        \assert($this->writeDebugData($packet, 'out'));

        if (($this->capabilities & self::CLIENT_COMPRESS) && $this->connectionState === ConnectionState::Ready) {
            $packet = $this->compressPacket($packet);
        }

        $this->socket->write($packet);
    }

    /**
     * @see 14.4 Compression
     */
    private function compressPacket(string $packet): string
    {
        $length = \strlen($packet);
        if ($length < self::COMPRESSION_MINIMUM_LENGTH) {
            return $this->makeCompressedPacket(0, $packet);
        }

        $deflated = \zlib_encode($packet, \ZLIB_ENCODING_DEFLATE);
        if ($deflated === false) {
            throw new SqlConnectionException("Failed to compress packet");
        }

        if ($length < \strlen($deflated)) {
            return $this->makeCompressedPacket(0, $packet);
        }

        return $this->makeCompressedPacket($length, $deflated);
    }

    private function makeCompressedPacket(int $uncompressed, string $packet): string
    {
        return MysqlDataType::encodeInt32(\strlen($packet) | (++$this->compressionId << 24))
            . MysqlDataType::encodeInt24($uncompressed) . $packet;
    }

    /**
     * @see 14.4 Compression
     *
     * @return \Generator<int, int, string, void>
     *
     * @psalm-suppress InvalidReturnType Psalm confuses this function to have a return of never
     */
    private function parseCompression(Parser $parser): \Generator
    {
        while (true) {
            $buffer = yield 7;
            $offset = 0;

            $length = MysqlDataType::decodeUnsigned24($buffer, $offset);
            $this->compressionId = MysqlDataType::decodeUnsigned8($buffer, $offset);
            $uncompressed = MysqlDataType::decodeUnsigned24($buffer, $offset);

            if ($length > 0) {
                $buffer = yield $length;

                if ($uncompressed !== 0) {
                    $buffer = \zlib_decode($buffer, $uncompressed);
                    if ($buffer === false) {
                        throw new \RuntimeException('Decompression failed');
                    }
                }

                $parser->push($buffer);
            }
        }
    }

    /**
     * @see 14.1.2 MySQL Packet
     * @see 14.1.3 Generic Response Packets
     *
     * @return \Generator<int, int, string, void>
     */
    private function parseMysql(): \Generator
    {
        while (true) {
            $packet = '';

            do {
                $buffer = yield 4;
                $offset = 0;

                $length = MysqlDataType::decodeUnsigned24($buffer, $offset);
                $this->seqId = MysqlDataType::decodeUnsigned8($buffer, $offset);

                if ($length > 0) {
                    $packet .= yield $length;
                }
            } while ($length === self::MAX_PACKET_LENGTH);

            if ($packet !== '') {
                $this->parsePayload($packet);
            }
        }
    }

    private function parsePayload(string $packet): void
    {
        if ($this->connectionState === ConnectionState::Connecting) {
            $this->established();
            $this->connectionState = ConnectionState::Established;
            $this->handleHandshake($packet);
            return;
        }

        if ($this->connectionState === ConnectionState::Established) {
            switch (\ord($packet[0])) {
                case self::OK_PACKET:
                    if ($this->capabilities & self::CLIENT_COMPRESS) {
                        $this->parser = new Parser($this->parseCompression($this->parser));
                    }
                    $this->connectionState = ConnectionState::Ready;
                    $this->handleOk($packet);
                    break;
                case self::ERR_PACKET:
                    $this->handleError($packet);
                    break;
                case self::AUTH_SWITCH_PACKET:
                    $this->handleAuthSwitch($packet);
                    break;
                case self::EXTRA_AUTH_PACKET:
                    /** @see 14.2.5 Connection Phase Packets (AuthMoreData) */
                    switch ($this->authPluginName) {
                        case "sha256_password":
                            $key = \substr($packet, 1);
                            $this->config = $this->config->withKey($key);
                            $this->sendHandshake();
                            break;
                        case "caching_sha2_password":
                            switch (\ord(\substr($packet, 1, 1))) {
                                case 3: // success
                                    return; // expecting OK afterwards
                                case 4: // fast auth failure
                                    /* unix domain socket, information not trivially available from $this->socket */
                                    if ($this->capabilities & self::CLIENT_SSL || $this->config->getHost()[0] === "/") {
                                        $this->write($this->config->getPassword() . "\0");
                                    } else {
                                        $this->write("\x02");
                                    }
                                    break;
                                case 0x2d: // certificate
                                    $pubkey = \substr($packet, 1);
                                    $this->write($this->sha256Auth($this->config->getPassword() ?? '', $this->authPluginData, $pubkey));
                                    break;
                            }
                            break;
                        default:
                            throw new SqlConnectionException(
                                "Unexpected EXTRA_AUTH_PACKET in authentication phase for method {$this->authPluginName}"
                            );
                    }
                    break;
            }
            return;
        }

        if ($this->parseCallback) {
            ($this->parseCallback)($packet);
            return;
        }

        $cb = $this->packetCallback;
        $this->packetCallback = null;
        switch (\ord($packet[0])) {
            case self::OK_PACKET:
                $this->handleOk($packet);
                break;
            case self::ERR_PACKET:
                $this->handleError($packet);
                break;
            case self::EOF_PACKET:
                if (\strlen($packet) < 6) {
                    $this->handleEof($packet);
                    break;
                }
                // no break
            default:
                if (!$cb) {
                    throw new SqlConnectionException("Unexpected packet type: " . \ord($packet[0]));
                }

                $cb($packet);
        }
    }

    private function secureAuth(string $pass, string $scramble): string
    {
        $hash = \sha1($pass, true);
        return $hash ^ \sha1(\substr($scramble, 0, 20) . \sha1($hash, true), true);
    }

    private function sha256Auth(string $pass, string $scramble, string $key): string
    {
        \openssl_public_encrypt(
            "$pass\0" ^ \str_repeat($scramble, (int) \ceil(\strlen($pass) / \strlen($scramble))),
            $auth,
            PublicKeyCache::loadKey($key),
            \OPENSSL_PKCS1_OAEP_PADDING,
        );

        return $auth;
    }

    public static function sha2Auth(string $pass, string $scramble): string
    {
        $digestStage1 = \hash("sha256", $pass, true);
        $digestStage2 = \hash("sha256", $digestStage1, true);
        $scrambleStage1 = \hash("sha256", $digestStage2 . \substr($scramble, 0, 20), true);
        return $digestStage1 ^ $scrambleStage1;
    }

    /**
     * @psalm-suppress UnusedMethod Retaining unused method for potential future use.
     */
    private function authSwitchRequest(string $packet): void
    {
        $this->parseCallback = null;
        switch (\ord($packet[0])) {
            case self::EOF_PACKET:
                if (\strlen($packet) === 1) {
                    break;
                }
                $length = (int) \strpos($packet, "\0");
                //$pluginName = \substr($packet, 0, $length); // @TODO mysql_native_pass only now...
                $authPluginData = \substr($packet, $length + 1);
                $this->write($this->secureAuth($this->config->getPassword() ?? '', $authPluginData));
                break;
            case self::ERR_PACKET:
                $this->handleError($packet);
                return;
            default:
                throw new SqlConnectionException("AuthSwitchRequest: Expecting 0xfe (or ERR_Packet), got 0x".\dechex(\ord($packet[0])));
        }
    }

    /**
     * @see 14.2.5 Connection Phase Packets
     * @see 14.3 Authentication Method
     */
    private function sendHandshake(): void
    {
        if ($this->config->getDatabase() !== null) {
            $this->capabilities |= self::CLIENT_CONNECT_WITH_DB;
        }

        if ($this->config->getConnectContext()->getTlsContext() !== null) {
            $this->capabilities |= self::CLIENT_SSL;
        }

        $this->capabilities &= $this->serverCapabilities;

        $tlsEnabled = false;

        do {
            $payload = [];
            $payload[] = \pack("V", $this->capabilities);
            $payload[] = \pack("V", self::MAX_PACKET_LENGTH);
            $payload[] = \chr(MysqlConfig::BIN_CHARSET);
            $payload[] = \str_repeat("\0", 23); // reserved

            if ($tlsEnabled || !($this->capabilities & self::CLIENT_SSL)) {
                break;
            }

            $paused = $this->paused = new DeferredFuture;

            try {
                $this->write(\implode($payload));
                $this->socket->setupTls();
                $tlsEnabled = true;
            } catch (\Throwable $e) {
                $this->free($e);
                return;
            } finally {
                $paused->complete();
                $this->paused = null;
            }
        } while (true);

        $payload[] = $this->config->getUser()."\0";

        $auth = $this->getAuthData();
        if ($this->capabilities & self::CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA) {
            $payload[] = MysqlDataType::encodeInt(\strlen($auth));
            $payload[] = $auth;
        } elseif ($this->capabilities & self::CLIENT_SECURE_CONNECTION) {
            $payload[] = \chr(\strlen($auth));
            $payload[] = $auth;
        } else {
            $payload[] = "$auth\0";
        }

        if ($this->capabilities & self::CLIENT_CONNECT_WITH_DB) {
            $payload[] = "{$this->config->getDatabase()}\0";
        }

        if ($this->capabilities & self::CLIENT_PLUGIN_AUTH) {
            $payload[] = "{$this->authPluginName}\0";
        }

        if ($this->capabilities & self::CLIENT_CONNECT_ATTRS) {
            // connection attributes?! 5.6.6+ only!
        }

        $this->write(\implode($payload));
    }

    private function getAuthData(): string
    {
        $password = $this->config->getPassword() ?? "";

        if ($this->config->getPassword() == "") {
            return "";
        }

        if ($this->capabilities & self::CLIENT_PLUGIN_AUTH) {
            switch ($this->authPluginName) {
                case "mysql_native_password":
                    return $this->secureAuth($password, $this->authPluginData);
                case "mysql_clear_password":
                    return $password;
                case "sha256_password":
                    $key = $this->config->getKey();
                    if ($key !== '') {
                        return $this->sha256Auth($password, $this->authPluginData, $key);
                    }
                    return "\x01";
                case "caching_sha2_password":
                    return self::sha2Auth($password, $this->authPluginData);
                case "mysql_old_password":
                    throw new SqlConnectionException(
                        "mysql_old_password is outdated and insecure. Intentionally not implemented!"
                    );
                default:
                    throw new SqlConnectionException(
                        "Invalid (or unimplemented?) auth method requested by server: {$this->authPluginName}"
                    );
            }
        }

        return $this->secureAuth($password, $this->authPluginData);
    }
}
<?php declare(strict_types=1);

namespace Amp\Mysql\Internal;

use Amp\Mysql\MysqlConfig;
use Amp\Mysql\MysqlResult;
use Amp\Mysql\MysqlStatement;
use Amp\Mysql\MysqlTransaction;
use Amp\Sql\Common\SqlStatementPool;
use Amp\Sql\SqlResult;
use Amp\Sql\SqlStatement;

/**
 * @internal
 * @extends SqlStatementPool<MysqlConfig, MysqlResult, MysqlStatement, MysqlTransaction>
 */
final class MysqlStatementPool extends SqlStatementPool implements MysqlStatement
{
    private array $params = [];

    #[\Override]
    protected function pop(): MysqlStatement
    {
        $statement = parent::pop();

        try {
            \assert($statement instanceof MysqlStatement);

            foreach ($this->params as $paramId => $data) {
                $statement->bind($paramId, $data);
            }
        } catch (\Throwable $exception) {
            $this->push($statement);
            throw $exception;
        }

        return $statement;
    }

    #[\Override]
    protected function push(SqlStatement $statement): void
    {
        if ($statement->isClosed()) {
            return;
        }

        $statement->reset();
        parent::push($statement);
    }

    #[\Override]
    protected function createResult(SqlResult $result, \Closure $release): MysqlResult
    {
        if (!$result instanceof MysqlResult) {
            throw new \TypeError('Result object must be an instance of ' . MysqlResult::class);
        }

        return new MysqlPooledResult($result, $release);
    }

    #[\Override]
    public function execute(array $params = []): MysqlResult
    {
        return parent::execute($params);
    }

    #[\Override]
    public function bind(int|string $paramId, string $data): void
    {
        $prior = $this->params[$paramId] ?? '';
        $this->params[$paramId] = $prior . $data;
    }

    #[\Override]
    public function reset(): void
    {
        $this->params = [];
    }

    #[\Override]
    public function getColumnDefinitions(): array
    {
        $statement = parent::pop();
        $columns = $statement->getColumnDefinitions();
        parent::push($statement);
        return $columns;
    }

    #[\Override]
    public function getParameterDefinitions(): array
    {
        $statement = parent::pop();
        $parameters = $statement->getParameterDefinitions();
        parent::push($statement);
        return $parameters;
    }
}
<?php declare(strict_types=1);

namespace Amp\Mysql\Internal;

use Amp\DeferredFuture;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Mysql\MysqlColumnDefinition;
use Amp\Mysql\MysqlResult;
use Amp\Pipeline\ConcurrentIterator;
use Amp\Pipeline\Queue;

/**
 * @internal
 * @psalm-import-type TFieldType from MysqlResult
 */
final class MysqlResultProxy
{
    use ForbidCloning;
    use ForbidSerialization;

    public int $columnsToFetch = 0;

    /** @var list<MysqlColumnDefinition> */
    public array $columns = [];

    /** @var list<MysqlColumnDefinition> */
    public array $params = [];

    /** @var Queue<list<TFieldType>> */
    private readonly Queue $rowQueue;

    /** @var ConcurrentIterator<list<TFieldType>> */
    public readonly ConcurrentIterator $rowIterator;

    private ?DeferredFuture $columnDeferred = null;

    private MysqlResultProxyState $state = MysqlResultProxyState::Initial;

    /** @var DeferredFuture<MysqlResult|null>|null */
    public ?DeferredFuture $next = null;

    public function __construct(
        public readonly int $columnCount = 0,
        ?int $columnsToFetch = null,
        public readonly ?int $affectedRows = null,
        public readonly ?int $insertId = null,
    ) {
        $this->rowQueue = new Queue();
        $this->rowIterator = $this->rowQueue->iterate();

        $this->columnsToFetch = $columnsToFetch ?? $this->columnCount;
    }

    /**
     * @return list<MysqlColumnDefinition>
     */
    public function getColumnDefinitions(): array
    {
        if ($this->state === MysqlResultProxyState::Initial) {
            $this->columnDeferred ??= new DeferredFuture();
            $this->columnDeferred->getFuture()->await();
        }

        return $this->columns;
    }

    /**
     * @return list<MysqlColumnDefinition>
     */
    public function getParameterDefinitions(): array
    {
        if ($this->state === MysqlResultProxyState::Initial) {
            $this->columnDeferred ??= new DeferredFuture();
            $this->columnDeferred->getFuture()->await();
        }

        return $this->params;
    }

    public function markDefinitionsFetched(): void
    {
        \assert($this->state === MysqlResultProxyState::Initial, 'Result proxy in invalid state');

        $this->state = MysqlResultProxyState::Fetched;
        $this->columnDeferred?->complete();
        $this->columnDeferred = null;
    }

    /**
     * @param list<TFieldType> $row
     */
    public function pushRow(array $row): void
    {
        \assert($this->state === MysqlResultProxyState::Fetched, 'Result proxy in invalid state');

        $this->rowQueue->push($row);
    }

    public function complete(): void
    {
        \assert($this->state === MysqlResultProxyState::Fetched, 'Result proxy in invalid state');

        $this->state = MysqlResultProxyState::Complete;

        if (!$this->rowQueue->isComplete()) {
            $this->rowQueue->complete();
        }
    }

    public function error(\Throwable $e): void
    {
        if ($this->state === MysqlResultProxyState::Complete) {
            return;
        }

        $this->state = MysqlResultProxyState::Complete;

        if (!$this->rowQueue->isComplete()) {
            $this->rowQueue->error($e);
        }

        $this->columnDeferred?->error($e);
        $this->columnDeferred = null;
    }
}
<?php declare(strict_types=1);

namespace Amp\Mysql;

use Amp\Sql\SqlLink;

/**
 * @extends SqlLink<MysqlResult, MysqlStatement, MysqlTransaction>
 */
interface MysqlLink extends MysqlExecutor, SqlLink
{
    /**
     * @return MysqlTransaction Transaction object specific to this library.
     */
    #[\Override]
    public function beginTransaction(): MysqlTransaction;
}
<?php declare(strict_types=1);

namespace Amp\Mysql;

use Amp\Socket\ConnectContext;
use Amp\Sql\SqlConfig;

final class MysqlConfig extends SqlConfig
{
    public const DEFAULT_PORT = 3306;
    public const BIN_CHARSET = 255; // utf8mb4_0900_ai_ci

    public const KEY_MAP = [
        ...parent::KEY_MAP,
        'compress' => 'compression',
        'useCompression' => 'compression',
        'cs' => 'charset',
        'localInfile' => 'local-infile',
    ];

    public const DEFAULT_CHARSET = "utf8mb4";
    public const DEFAULT_COLLATE = "utf8mb4_0900_ai_ci";

    private ConnectContext $context;

    public static function fromString(string $connectionString, ?ConnectContext $context = null): self
    {
        $parts = self::parseConnectionString($connectionString, self::KEY_MAP);

        if (!isset($parts['host'])) {
            throw new \Error('Host must be provided in connection string');
        }

        return new self(
            host: $parts['host'],
            port: (int) ($parts['port'] ?? self::DEFAULT_PORT),
            user: $parts['user'] ?? null,
            password: $parts['password'] ?? null,
            database: $parts['db'] ?? null,
            context: $context,
            charset: $parts['charset'] ?? self::DEFAULT_CHARSET,
            collate: $parts['collate'] ?? self::DEFAULT_COLLATE,
            useCompression: ($parts['compression'] ?? '') === 'on',
            useLocalInfile: ($parts['local-infile'] ?? '') === 'on'
        );
    }

    public static function fromAuthority(
        string $authority,
        string $user,
        string $password,
        ?string $database = null,
        ?ConnectContext $context = null,
    ): self {
        [$host, $port] = \explode(':', $authority, 2) + ['', (string) self::DEFAULT_PORT];

        return new self(
            host: $host,
            port: (int) $port,
            user: $user,
            password: $password,
            database: $database,
            context: $context,
        );
    }

    /**
     * @param string $key Private key to use for sha256_password auth method
     */
    public function __construct(
        string $host,
        int $port = self::DEFAULT_PORT,
        ?string $user = null,
        ?string $password = null,
        ?string $database = null,
        ?ConnectContext $context = null,
        private string $charset = self::DEFAULT_CHARSET,
        private string $collate = self::DEFAULT_COLLATE,
        private ?string $sqlMode = null,
        private bool $useCompression = false,
        private string $key = '',
        private bool $useLocalInfile = false,
    ) {
        parent::__construct($host, $port, $user, $password, $database);

        $this->context = $context ?? new ConnectContext();
    }

    public function getConnectionString(): string
    {
        return $this->getHost()[0] == "/"
            ? 'unix://' . $this->getHost()
            : 'tcp://' . $this->getHost() . ':' . $this->getPort();
    }

    public function isCompressionEnabled(): bool
    {
        return $this->useCompression;
    }

    public function withCompression(): self
    {
        $new = clone $this;
        $new->useCompression = true;
        return $new;
    }

    public function withoutCompression(): self
    {
        $new = clone $this;
        $new->useCompression = false;
        return $new;
    }

    public function isLocalInfileEnabled(): bool
    {
        return $this->useLocalInfile;
    }

    public function withLocalInfile(): self
    {
        $new = clone $this;
        $new->useLocalInfile = true;
        return $new;
    }

    public function withoutLocalInfile(): self
    {
        $new = clone $this;
        $new->useLocalInfile = false;
        return $new;
    }

    public function getConnectContext(): ConnectContext
    {
        return $this->context;
    }

    public function withConnectContext(ConnectContext $context): self
    {
        $new = clone $this;
        $new->context = $context;
        return $new;
    }

    public function getCharset(): string
    {
        return $this->charset;
    }

    public function getCollation(): string
    {
        return $this->collate;
    }

    public function withCharset(string $charset, string $collate): self
    {
        $new = clone $this;
        $new->charset = $charset;
        $new->collate = $collate;
        return $new;
    }

    public function getSqlMode(): ?string
    {
        return $this->sqlMode;
    }

    /**
     * Set the Server SQL Modes at connection time.
     * @see https://dev.mysql.com/doc/refman/8.0/en/sql-mode.html
     */
    public function withSqlMode(?string $sqlMode): self
    {
        $new = clone $this;
        $new->sqlMode = $sqlMode;
        return $new;
    }

    public function getKey(): string
    {
        return $this->key;
    }

    public function withKey(string $key): self
    {
        $new = clone $this;
        $new->key = $key;
        return $new;
    }
}
<?php declare(strict_types=1);

namespace Amp\Mysql;

use Amp\Cancellation;
use Amp\Sql\Common\RetrySqlConnector;
use Amp\Sql\SqlConnector;
use Amp\Sql\SqlException;
use Revolt\EventLoop;

/**
 * @param SqlConnector<MysqlConfig, MysqlConnection>|null $connector
 *
 * @return SqlConnector<MysqlConfig, MysqlConnection>
 */
function mysqlConnector(?SqlConnector $connector = null): SqlConnector
{
    static $map;
    $map ??= new \WeakMap();
    $driver = EventLoop::getDriver();

    if ($connector) {
        return $map[$driver] = $connector;
    }

    /**
     * @psalm-suppress InvalidArgument
     * @var SqlConnector<MysqlConfig, MysqlConnection>
     */
    return $map[$driver] ??= new RetrySqlConnector(new SocketMysqlConnector());
}

/**
 * Create a connection using the global Connector instance.
 *
 * @throws SqlException If connecting fails.
 * @throws \Error If the connection string does not contain a host, user, and password.
 */
function connect(MysqlConfig $config, ?Cancellation $cancellation = null): MysqlConnection
{
    return mysqlConnector()->connect($config, $cancellation);
}
{
    "name": "amphp/mysql",
    "description": "Asynchronous MySQL client for PHP based on Amp.",
    "license": "MIT",
    "authors": [
        {
            "name": "Bob Weinand",
            "email": "bobwei9@hotmail.com"
        },
        {
            "name": "Aaron Piotrowski",
            "email": "aaron@trowski.com"
        }
    ],
    "require": {
        "php": ">=8.1",
        "amphp/amp": "^3",
        "amphp/parser": "^1.1",
        "amphp/pipeline": "^1",
        "amphp/socket": "^2.2",
        "amphp/sql": "^2",
        "amphp/sql-common": "^2"
    },
    "require-dev": {
        "ext-mysqli": "*",
        "ext-openssl": "*",
        "amphp/process": "^2",
        "amphp/file": "^3 | ^4",
        "phpunit/phpunit": "^9",
        "amphp/phpunit-util": "^3",
        "amphp/php-cs-fixer-config": "^2",
        "phpbench/phpbench": "^1.2.6",
        "psalm/phar": "6.16.1"
    },
    "autoload": {
        "psr-4": {
            "Amp\\Mysql\\": "src"
        },
        "files": [
            "src/functions.php"
        ]
    },
    "autoload-dev": {
        "psr-4": {
            "Amp\\Mysql\\Test\\": "test",
            "Amp\\Mysql\\Bench\\": "benchmarks"
        },
        "files": [
            "test/initialize.php"
        ]
    },
    "scripts": {
        "check": [
            "@cs",
            "@test"
        ],
        "cs": "php-cs-fixer fix -v --diff --dry-run",
        "cs-fix": "php-cs-fixer fix -v --diff",
        "test": "@php -dzend.assertions=1 -dassert.exception=1 ./vendor/bin/phpunit"
    }
}
<?php

$config = new Amp\CodeStyle\Config;
$config->getFinder()
    ->in(__DIR__ . '/examples')
    ->in(__DIR__ . '/src')
    ->in(__DIR__ . '/test');

$config->setCacheFile(__DIR__ . '/.php_cs.cache');

return $config;
{
    "symbol-whitelist": [
        "null",
        "true",
        "false",
        "static",
        "self",
        "parent",
        "array",
        "string",
        "int",
        "float",
        "bool",
        "iterable",
        "callable",
        "mixed",
        "void",
        "object",
        "MSG_EAGAIN",
        "MSG_ENOMSG",
        "msg_get_queue",
        "MSG_IPC_NOWAIT",
        "msg_queue_exists",
        "msg_receive",
        "msg_remove_queue",
        "msg_send",
        "msg_set_queue",
        "msg_stat_queue",
        "Threaded",
        "transform",
        "SysvMessageQueue",
        "Shmop",
        "shmop_delete",
        "shmop_open",
        "shmop_read",
        "shmop_size",
        "shmop_write",
        "random_int"
    ],
    "php-core-extensions": [
        "Core",
        "date",
        "pcre",
        "Phar",
        "Reflection",
        "SPL",
        "standard"
    ]
}
<?php declare(strict_types=1);

namespace Amp\Sync;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;

final class StaticKeyMutex implements Mutex
{
    use ForbidCloning;
    use ForbidSerialization;

    public function __construct(
        private readonly KeyedMutex $mutex,
        private readonly string $key,
    ) {
    }

    public function acquire(): Lock
    {
        return $this->mutex->acquire($this->key);
    }
}
<?php declare(strict_types=1);

namespace Amp\Sync;

/**
 * @template T
 *
 * A parcel object for sharing data across execution contexts.
 *
 * A parcel is an object that stores a value in a safe way that can be shared
 * between different threads or processes. Different handles to the same parcel
 * will access the same data, and a parcel handle itself is serializable and
 * can be transported to other execution contexts.
 *
 * Wrapping and unwrapping values in the parcel are not atomic. To prevent race
 * conditions and guarantee safety, you should use the provided synchronization
 * methods to acquire a lock for exclusive access to the parcel first before
 * accessing the contained value.
 */
interface Parcel
{
    /**
     * Invokes a callback while maintaining a lock on the parcel. The current value of the parcel is provided as
     * the first argument to the callback function. The return value of the callback is stored as the new value
     * of the parcel.
     *
     * @template R of T
     *
     * @param \Closure(T):R $closure The closure to invoke when a lock is obtained on the parcel. The parcel
     * value is given as the single argument to the closure. The return value is stored as the new parcel value.
     *
     * @return R The value of the parcel after the closure was invoked.
     */
    public function synchronized(\Closure $closure): mixed;

    /**
     * @return T The value inside the parcel.
     */
    public function unwrap(): mixed;
}
<?php declare(strict_types=1);

namespace Amp\Sync;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Serialization\NativeSerializer;
use Amp\Serialization\SerializationException;
use Amp\Serialization\Serializer;

/**
 * A container object for sharing a value across contexts.
 *
 * A shared object is a container that stores an object inside shared memory.
 * The object can be accessed and mutated by any thread or process.
 * Create a new parcel using {@see self::create()} within the owner process.
 *
 * The shared object itself is not serializable and therefore cannot be sent
 * to another process or thread. Send the integer returned from {@see self::getKey()}
 * to the other process and open the parcel using {@see self::use()}.
 *
 * Because each shared object uses its own shared memory segment, it is much
 * more efficient to store a larger object containing many values inside a
 * single shared container than to use many small shared containers.
 * The {@see Serializer} interface provided to this object provides a variety
 * of strategies for storing data in shared memory and can be used to serialize
 * data for consumption by programs written in languages other than PHP.
 *
 * @see http://php.net/manual/en/book.shmop.php The shared memory extension.
 * @see http://man7.org/linux/man-pages/man2/shmctl.2.html How shared memory works on Linux.
 * @see https://msdn.microsoft.com/en-us/library/ms810613.aspx How shared memory works on Windows.
 *
 * @template T
 * @template-implements Parcel<T>
 */
final class SharedMemoryParcel implements Parcel
{
    use ForbidCloning;
    use ForbidSerialization;

    /** @var int The byte offset to the start of the object data in memory. */
    private const MEM_DATA_OFFSET = 7;

    private const MAX_ID = 0x7fffffff;

    // A list of valid states the object can be in.
    private const STATE_UNALLOCATED = 0;
    private const STATE_ALLOCATED = 1;
    private const STATE_MOVED = 2;

    private static int $nextId = 0;

    /**
     * @param Mutex $mutex Mutex to control access to the shared memory. Recommended: Single lock {@see PosixSemaphore}
     *     wrapped in an instance of {@see SemaphoreMutex}.
     * @param int $size The initial size in bytes of the shared memory segment. It will automatically be
     *     expanded as necessary.
     * @param int $permissions Permissions to access the semaphore. Use file permission format specified as 0xxx.
     *
     * @throws ParcelException
     * @throws SyncException
     * @throws \Error If the size or permissions are invalid.
     */
    public static function create(
        Mutex $mutex,
        mixed $value,
        int $size = 8192,
        int $permissions = 0600,
        ?Serializer $serializer = null
    ): self {
        if ($size <= 0 || $size > 1 << 27) {
            throw new \ValueError('The memory size must be greater than 0 and less than 128 MB');
        }

        if ($permissions <= 0 || $permissions > 0777) {
            throw new \ValueError('Invalid permissions');
        }

        $parcel = new self(0, $mutex, $serializer);
        $parcel->init($value, $size, $permissions);
        return $parcel;
    }

    /**
     * @param int $key Use {@see getKey()} on the creating process and send this key to another process.
     *
     * @throws ParcelException
     */
    public static function use(Mutex $mutex, int $key, ?Serializer $serializer = null): self
    {
        $parcel = new self($key, $mutex, $serializer);
        $parcel->open();
        return $parcel;
    }

    /** @var \Shmop An open handle to the shared memory segment. */
    private ?\Shmop $handle = null;

    private int $initializer = 0;

    private readonly Serializer $serializer;

    /**
     * @param int $key The shared memory segment key.
     * @param Mutex $mutex A mutex for synchronizing on the parcel.
     */
    private function __construct(
        private int $key,
        private readonly Mutex $mutex,
        ?Serializer $serializer = null
    ) {
        if (!\extension_loaded("shmop")) {
            throw new \Error(__CLASS__ . " requires the shmop extension");
        }

        $this->serializer = $serializer ?? new NativeSerializer;
    }

    public function getKey(): int
    {
        return $this->key;
    }

    public function unwrap(): mixed
    {
        $lock = $this->mutex->acquire();

        try {
            return $this->getValue();
        } finally {
            $lock->release();
        }
    }

    public function synchronized(\Closure $closure): mixed
    {
        $lock = $this->mutex->acquire();

        try {
            $result = $closure($this->getValue());
            $this->wrap($result);
        } finally {
            $lock->release();
        }

        return $result;
    }

    /**
     * Frees the shared object from memory.
     *
     * The memory containing the shared value will be invalidated. When all
     * process disconnect from the object, the shared memory block will be
     * destroyed by the OS.
     */
    public function __destruct()
    {
        if ($this->initializer === 0 || $this->initializer !== \getmypid()) {
            return;
        }

        if ($this->isFreed()) {
            return;
        }

        // Request the block to be deleted, then close our local handle.
        $this->deleteSegment();
        $this->handle = null;
    }

    /**
     * @throws ParcelException
     * @throws \Error If the size or permissions are invalid.
     */
    private function init(mixed $value, int $size = 8192, int $permissions = 0600): void
    {
        $this->initializer = \getmypid();

        $lock = $this->mutex->acquire();

        try {
            [$this->key, $this->handle] = self::createSegment($permissions, $size + self::MEM_DATA_OFFSET);
            $this->writeSegment(self::generateHeader(self::STATE_ALLOCATED, 0, $permissions));
            $this->wrap($value);
        } finally {
            $lock->release();
        }
    }

    private function open(): void
    {
        \set_error_handler(static function (int $errno, string $errstr): never {
            throw new ParcelException('Failed to open shared memory block: ' . $errstr, $errno);
        });

        try {
            /** @psalm-suppress InvalidPropertyAssignmentValue Psalm needs to be updated for ext-shmop using objects. */
            $this->handle = \shmop_open($this->key, 'w', 0, 0);
        } finally {
            \restore_error_handler();
        }
    }

    /**
     * Checks if the object has been freed.
     *
     * Note that this does not check if the object has been destroyed; it only
     * checks if this handle has freed its reference to the object.
     *
     * @return bool True if the object is freed, otherwise false.
     */
    private function isFreed(): bool
    {
        // If we are still connected to the memory segment, check if it has
        // been invalidated.
        if ($this->handle !== null) {
            ['state' => $state] = $this->readHeader();
            return $state !== self::STATE_ALLOCATED;
        }

        return true;
    }

    /**
     * @throws ParcelException
     * @throws SerializationException
     */
    private function getValue(): mixed
    {
        if ($this->isFreed()) {
            throw new ParcelException('The object has already been freed');
        }

        ['state' => $state, 'size' => $size] = $this->readHeader();

        // Make sure the header is in a valid state and format.
        if ($state !== self::STATE_ALLOCATED || $size <= 0) {
            throw new ParcelException('Shared object memory is corrupt');
        }

        // Read the actual value data from memory and unserialize it.
        $data = $this->readSegment(self::MEM_DATA_OFFSET, $size);
        return $this->serializer->unserialize($data);
    }

    /**
     * If the value requires more memory to store than currently allocated, a
     * new shared memory segment will be allocated with a larger size to store
     * the value in. The previous memory segment will be cleaned up and marked
     * for deletion. Other processes and threads will be notified of the new
     * memory segment on the next read attempt. Once all running processes and
     * threads disconnect from the old segment, it will be freed by the OS.
     */
    private function wrap(mixed $value): void
    {
        if ($this->isFreed()) {
            throw new ParcelException('The object has already been freed');
        }

        ['permissions' => $permissions] = $this->readHeader();

        $serialized = $this->serializer->serialize($value);
        $size = \strlen($serialized);

        /* If we run out of space, we need to allocate a new shared memory
           segment that is larger than the current one. To coordinate with other
           processes, we will leave a message in the old segment that the segment
           has moved and along with the new key. The old segment will be discarded
           automatically after all other processes notice the change and close
           the old handle.
        */
        /** @psalm-suppress InvalidArgument Psalm needs to be updated for ext-shmop using objects. */
        if (\shmop_size($this->handle) < $size + self::MEM_DATA_OFFSET) {
            [$key, $handle] = self::createSegment($permissions, $size * 2);

            $this->writeSegment(self::generateHeader(self::STATE_MOVED, $key, 0));
            $this->deleteSegment();

            $this->key = $key;
            $this->handle = $handle;
        }

        // Rewrite the header and the serialized value to memory.
        $this->writeSegment(self::generateHeader(self::STATE_ALLOCATED, $size, $permissions) . $serialized);
    }

    /**
     * Reads and returns the data header at the current memory segment. If the memory has been moved, this method
     * updates the current memory segment handle, handling any moves made on the data.
     *
     * @return array{state: int, size: int, permissions: int} An associative array of header data.
     *
     * @throws ParcelException
     */
    private function readHeader(): array
    {
        // Read from the memory block and handle moved blocks until we find the
        // correct block.
        while (true) {
            $data = $this->readSegment(0, self::MEM_DATA_OFFSET);
            $header = \unpack('Cstate/Lsize/Spermissions', $data);

            // If the state is STATE_MOVED, the memory is stale and has been moved
            // to a new location. Move handle and try to read again.
            if ($header['state'] !== self::STATE_MOVED) {
                return $header;
            }

            $this->key = $header['size'];
            $this->open();
        }
    }

    /**
     * @param int $state An object state.
     * @param int $size The size of the stored data, or other value.
     * @param int $permissions The permissions mask on the memory segment.
     */
    private static function generateHeader(int $state, int $size, int $permissions): string
    {
        return \pack('CLS', $state, $size, $permissions);
    }

    /**
     * Opens a shared memory handle.
     *
     * @param int $permissions Process permissions on the shared memory.
     * @param int $size The size to crate the shared memory in bytes.
     *
     * @return array{int, \Shmop}
     *
     * @throws ParcelException
     *
     * @psalm-suppress InvalidReturnType
     */
    private static function createSegment(int $permissions, int $size): array
    {
        if (!self::$nextId) {
            self::$nextId = \random_int(1, self::MAX_ID);
        }

        \set_error_handler(static function (int $errno, string $errstr): bool {
            if (\str_contains($errstr, 'Unable to attach or create shared memory segment')) {
                return true;
            }

            throw new ParcelException('Failed to create shared memory block: ' . $errstr, $errno);
        });

        try {
            do {
                $id = self::$nextId;

                if ($handle = \shmop_open($id, 'n', $permissions, $size)) {
                    /** @psalm-suppress InvalidReturnStatement Psalm needs to be updated for ext-shmop using objects. */
                    return [$id, $handle];
                }

                self::$nextId = self::$nextId % self::MAX_ID + 1;
            } while (true);
        } finally {
            \restore_error_handler();
        }
    }

    /**
     * Reads binary data from shared memory.
     *
     * @param int $offset The offset to read from.
     * @param int $size The number of bytes to read.
     *
     * @return string The binary data at the given offset.
     *
     * @throws ParcelException
     */
    private function readSegment(int $offset, int $size): string
    {
        \assert($this->handle !== null);

        try {
            return \shmop_read($this->handle, $offset, $size);
        } catch (\ValueError $error) {
            throw new ParcelException(
                'Failed to read from shared memory block: ' . ($error->getMessage() ?? 'unknown error')
            );
        }
    }

    /**
     * Writes binary data to shared memory.
     *
     * @param string $data The binary data to write.
     *
     * @throws ParcelException
     */
    private function writeSegment(string $data): void
    {
        \assert($this->handle !== null);

        try {
            \shmop_write($this->handle, $data, 0);
        } catch (\ValueError $error) {
            throw new ParcelException(
                'Failed to write to shared memory block: ' . ($error->getMessage() ?? 'unknown error')
            );
        }
    }

    /**
     * Requests the shared memory segment to be deleted.
     *
     * @throws ParcelException
     */
    private function deleteSegment(): void
    {
        \assert($this->handle !== null);

        /** @psalm-suppress InvalidArgument Psalm needs to be updated for ext-shmop using objects. */
        if (!\shmop_delete($this->handle)) {
            $error = \error_get_last();
            throw new ParcelException(
                'Failed to discard shared memory block' . ($error['message'] ?? 'unknown error')
            );
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\Sync;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;

final class SemaphoreMutex implements Mutex
{
    use ForbidCloning;
    use ForbidSerialization;

    private bool $locked = false;

    /**
     * @param Semaphore $semaphore A semaphore with a single lock.
     */
    public function __construct(
        private readonly Semaphore $semaphore
    ) {
    }

    /** {@inheritdoc} */
    public function acquire(): Lock
    {
        $lock = $this->semaphore->acquire();

        if ($this->locked) {
            throw new \Error("Cannot use a semaphore with more than a single lock");
        }

        $this->locked = true;

        return new Lock(function () use ($lock): void {
            $this->locked = false;
            $lock->release();
        });
    }
}
<?php declare(strict_types=1);

namespace Amp\Sync;

/**
 * A counting semaphore.
 *
 * Objects that implement this interface should guarantee that all operations are atomic. Implementations do not have to
 * guarantee that acquiring a lock is first-come, first serve.
 */
interface Semaphore
{
    /**
     * Acquires a lock on the semaphore. Semaphores may have one or more locks.
     *
     * @return Lock Returns with a lock object once a lock is obtained. May fail with a SyncException if an
     *     error occurs when attempting to obtain the lock (e.g. a shared memory segment closed).
     */
    public function acquire(): Lock;
}
<?php declare(strict_types=1);

namespace Amp\Sync;

use Amp\Cancellation;
use Amp\Closable;
use Amp\Serialization\SerializationException;

/**
 * Interface for sending messages between execution contexts, such as two coroutines or two processes.
 *
 * @template-covariant TReceive
 * @template TSend
 */
interface Channel extends Closable
{
    /**
     * @param Cancellation|null $cancellation Cancels waiting for the next value. Note the next value is not discarded
     * if the operation is cancelled, rather it will be returned from the next call to this method.
     *
     * @return TReceive Data received.
     *
     * @throws ChannelException If receiving from the channel fails or the channel closed.
     * @throws SerializationException If the underlying transport mechanism uses serialization and fails.
     */
    public function receive(?Cancellation $cancellation = null): mixed;

    /**
     * @param TSend $data
     *
     * @throws ChannelException If sending on the channel fails or the channel is already closed.
     * @throws SerializationException If the underlying transport mechanism uses serialization and fails.
     */
    public function send(mixed $data): void;
}
<?php declare(strict_types=1);

namespace Amp\Sync;

/**
 * A synchronization primitive that can be used for mutual exclusion across contexts based on keys.
 *
 * Objects that implement this interface should guarantee that all operations are atomic. Implementations do not have to
 * guarantee that acquiring a lock is first-come, first serve.
 */
interface KeyedMutex extends KeyedSemaphore
{
    /**
     * Acquires a lock on the mutex.
     *
     * @param string $key Lock key
     *
     * @return Lock Returns a lock object with an ID of 0. May fail with a SyncException if an
     *     error occurs when attempting to obtain the lock (e.g. a shared memory segment closed).
     */
    public function acquire(string $key): Lock;
}
<?php declare(strict_types=1);

namespace Amp\Sync;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Revolt\EventLoop;

/**
 * When a locked is released from this semaphore, it does not become available to be acquired again until
 * the given lock-period has elapsed. This is useful when a number of operations or requests must be
 * limited to a particular quantity within a certain time period.
 */
final class RateLimitingSemaphore implements Semaphore
{
    use ForbidCloning;
    use ForbidSerialization;

    /** @var \SplQueue<string> List of event-loop delay callback IDs. */
    private readonly \SplQueue $timers;

    private int $waitingCount = 0;

    /**
     * @param float $lockPeriod Time after which a lock is released from the semaphore after being initially
     * released by the consumer.
     */
    public function __construct(
        private readonly Semaphore $semaphore,
        private readonly float $lockPeriod,
    ) {
        if ($lockPeriod <= 0) {
            throw new \ValueError('The lock period must be greater than 0, got ' . $lockPeriod);
        }

        $this->timers = new \SplQueue();
    }

    public function acquire(): Lock
    {
        ++$this->waitingCount;

        if (!$this->timers->isEmpty()) {
            EventLoop::reference($this->timers->bottom());
        }

        $lock = $this->semaphore->acquire();

        if (!--$this->waitingCount && !$this->timers->isEmpty()) {
            EventLoop::unreference($this->timers->bottom());
        }

        return new Lock(fn () => $this->release($lock));
    }

    private function release(Lock $lock): void
    {
        $timer = EventLoop::delay(
            $this->lockPeriod,
            function () use ($lock): void {
                \assert(!$this->timers->isEmpty());

                $this->timers->shift();
                if ($this->waitingCount && !$this->timers->isEmpty()) {
                    EventLoop::reference($this->timers->bottom());
                }

                $lock->release();
            },
        );

        if (!$this->waitingCount || !$this->timers->isEmpty()) {
            EventLoop::unreference($timer);
        }

        $this->timers->push($timer);
    }
}
<?php declare(strict_types=1);

namespace Amp\Sync;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;

final class StaticKeySemaphore implements Mutex
{
    use ForbidCloning;
    use ForbidSerialization;

    public function __construct(
        private readonly KeyedSemaphore $semaphore,
        private readonly string $key,
    ) {
    }

    public function acquire(): Lock
    {
        return $this->semaphore->acquire($this->key);
    }
}
<?php declare(strict_types=1);

namespace Amp\Sync;

/**
 * A synchronization primitive that can be used for mutual exclusion across contexts.
 *
 * Objects that implement this interface should guarantee that all operations are atomic. Implementations do not have to
 * guarantee that acquiring a lock is first-come, first serve.
 */
interface Mutex extends Semaphore
{
    /**
     * Acquires a lock on the mutex. A mutex has only a single lock available.
     *
     * @return Lock Returns with a lock object once a lock is obtained. May fail with a SyncException if an
     *     error occurs when attempting to obtain the lock (e.g. a shared memory segment closed).
     */
    public function acquire(): Lock;
}
<?php declare(strict_types=1);

namespace Amp\Sync;

/**
 * Uses a binary tree stored in an array to implement a heap.
 *
 * @template T of int|string
 */
final class PriorityQueue
{
    /** @var array<int, object{key: T, priority: int}> */
    private array $data = [];

    /** @var array<T, int> */
    private array $pointers = [];

    /**
     * Inserts the key into the queue with the given priority or updates the priority if the key
     * already exists in the queue.
     *
     * Time complexity: O(log(n)).
     *
     * @param T $key
     */
    public function insert(int|string $key, int $priority): void
    {
        if (isset($this->pointers[$key])) {
            $node = $this->pointers[$key];
            $entry = $this->data[$node];

            $previous = $entry->priority;
            $entry->priority = $priority;

            // Nothing to be done if priorities are equal.
            if ($previous < $priority) {
                $this->heapifyDown($node);
            } elseif ($previous > $priority) {
                $this->heapifyUp($node);
            }

            return;
        }

        $entry = new class($key, $priority) {
            public function __construct(
                public readonly int|string $key,
                public int $priority,
            ) {
            }
        };

        $node = \count($this->data);
        $this->data[$node] = $entry;
        $this->pointers[$key] = $node;

        $this->heapifyUp($node);
    }

    /**
     * Removes the given key from the queue.
     *
     * Time complexity: O(log(n)).
     *
     * @param T $key
     */
    public function remove(int|string $key): void
    {
        if (!isset($this->pointers[$key])) {
            return;
        }

        $this->removeAndRebuild($this->pointers[$key]);
    }

    /**
     * Deletes and returns the data at the top of the queue if the priority is less than the priority given.
     *
     * Time complexity: O(log(n)).
     *
     * @param int $priority Extract data with a priority less than the given priority.
     *
     * @return T|null
     */
    public function extract(int $priority = \PHP_INT_MAX): int|string|null
    {
        $data = $this->data[0] ?? null;
        if ($data === null || $data->priority > $priority) {
            return null;
        }

        $this->removeAndRebuild(0);

        return $data->key;
    }

    /**
     * Returns the data at top of the heap or null if empty. Time complexity: O(1).
     *
     * @return T|null
     */
    public function peekData(): int|string|null
    {
        return ($this->data[0] ?? null)?->key;
    }

    /**
     * Returns the priority at top of the heap or null if empty. Time complexity: O(1).
     */
    public function peekPriority(): ?int
    {
        return ($this->data[0] ?? null)?->priority;
    }

    public function isEmpty(): bool
    {
        return empty($this->data);
    }

    /**
     * @param int $node Rebuild the data array from the given node upward.
     */
    private function heapifyUp(int $node): void
    {
        $entry = $this->data[$node];
        while ($node !== 0 && $entry->priority < $this->data[$parent = ($node - 1) >> 1]->priority) {
            $this->swap($node, $parent);
            $node = $parent;
        }
    }

    /**
     * @param int $node Rebuild the data array from the given node downward.
     */
    private function heapifyDown(int $node): void
    {
        $length = \count($this->data);
        while (($child = ($node << 1) + 1) < $length) {
            if ($this->data[$child]->priority < $this->data[$node]->priority
                && ($child + 1 >= $length || $this->data[$child]->priority < $this->data[$child + 1]->priority)
            ) {
                // Left child is less than parent and right child.
                $swap = $child;
            } elseif ($child + 1 < $length && $this->data[$child + 1]->priority < $this->data[$node]->priority) {
                // Right child is less than parent and left child.
                $swap = $child + 1;
            } else { // Left and right child are greater than parent.
                break;
            }

            $this->swap($node, $swap);
            $node = $swap;
        }
    }

    private function swap(int $left, int $right): void
    {
        $temp = $this->data[$left];

        $this->data[$left] = $this->data[$right];
        $this->pointers[$this->data[$right]->key] = $left;

        $this->data[$right] = $temp;
        $this->pointers[$temp->key] = $right;
    }

    /**
     * @param int $node Remove the given node and then rebuild the data array.
     */
    private function removeAndRebuild(int $node): void
    {
        $length = \count($this->data) - 1;
        $id = $this->data[$node]->key;
        $left = $this->data[$node] = $this->data[$length];
        $this->pointers[$left->key] = $node;
        unset($this->data[$length], $this->pointers[$id]);

        if ($node < $length) { // don't need to do anything if we removed the last element
            $parent = ($node - 1) >> 1;
            if ($parent >= 0 && $this->data[$node]->priority < $this->data[$parent]->priority) {
                $this->heapifyUp($node);
            } else {
                $this->heapifyDown($node);
            }
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\Sync;

/**
 * A counting semaphore based on keys.
 *
 * Objects that implement this interface should guarantee that all operations are atomic. Implementations do not have to
 * guarantee that acquiring a lock is first-come, first serve.
 */
interface KeyedSemaphore
{
    /**
     * Acquires a lock on the semaphore.
     *
     * @param string $key Lock key
     *
     * @return Lock Returns an integer keyed lock object once a lock is obtained. Identifiers returned by the
     *    locks should be 0-indexed. Releasing an identifier MUST make that same identifier available. May fail with
     *    a SyncException if an error occurs when attempting to obtain the lock (e.g. a shared memory segment closed).
     */
    public function acquire(string $key): Lock;
}
<?php declare(strict_types=1);

namespace Amp\Sync;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use function Amp\delay;

/**
 * A non-blocking, inter-process POSIX semaphore.
 *
 * Uses a POSIX message queue to store a queue of permits in a lock-free data structure. This semaphore implementation
 * is preferred over other implementations when available, as it provides the best performance.
 *
 * Not compatible with Windows.
 */
final class PosixSemaphore implements Semaphore
{
    use ForbidCloning;
    use ForbidSerialization;

    private const LATENCY_TIMEOUT = 0.01;
    private const MAX_ID = 0x7fffffff;

    private static int $nextId = 0;

    private readonly \Closure $errorHandler;

    /**
     * Creates a new semaphore with a given ID and number of locks.
     *
     * @param int $maxLocks The maximum number of locks that can be acquired from the semaphore.
     * @param int $permissions Permissions to access the semaphore. Use file permission format specified as 0xxx.
     *
     * @throws SyncException If the semaphore could not be created due to an internal error.
     */
    public static function create(int $maxLocks, int $permissions = 0600): self
    {
        if ($maxLocks < 1) {
            throw new \Error('Number of locks must be greater than 0, got ' . $maxLocks);
        }

        \set_error_handler(static function (int $errno, string $errstr): bool {
            if (!\str_contains($errstr, 'No space left on device') && \str_contains($errstr, 'Failed for key')) {
                return true;
            }

            throw new SyncException('Failed to create semaphore: ' . $errstr, $errno);
        });

        try {
            do {
                do {
                    $id = self::getNextId();
                } while (\msg_queue_exists($id));

                if ($queue = \msg_get_queue($id, $permissions)) {
                    $semaphore = new self($queue, $id, \getmypid());

                    // Fill the semaphore with locks.
                    while (--$maxLocks >= 0) {
                        $semaphore->release();
                    }

                    return $semaphore;
                }
            } while (true);
        } finally {
            \restore_error_handler();
        }
    }

    private static function getNextId(): int
    {
        if (self::$nextId === 0) {
            return self::$nextId = \random_int(1, self::MAX_ID);
        }

        return self::$nextId = self::$nextId % self::MAX_ID + 1;
    }

    /**
     * @param int $key Use {@see getKey()} on the creating process and send this key to another process.
     */
    public static function use(int $key): self
    {
        if (!\msg_queue_exists($key)) {
            throw new SyncException('No semaphore with that ID found');
        }

        $queue = \msg_get_queue($key);

        if (!$queue) {
            throw new SyncException('Failed to open the semaphore.');
        }

        return new self($queue, $key, 0);
    }

    /**
     * @param int $initializer PID of the process that created the semaphore or 0 if the semaphore was only opened
     *      in this process.
     *
     * @throws \Error If the sysvmsg extension is not loaded.
     */
    private function __construct(
        private readonly \SysvMessageQueue $queue,
        private readonly int $key,
        private readonly int $initializer,
    ) {
        if (!\extension_loaded("sysvmsg")) {
            throw new \Error(__CLASS__ . " requires the sysvmsg extension.");
        }

        $this->errorHandler = static fn () => true;
    }

    public function getKey(): int
    {
        return $this->key;
    }

    /**
     * Gets the access permissions of the semaphore.
     *
     * @return int A permissions mode.
     */
    public function getPermissions(): int
    {
        $stat = \msg_stat_queue($this->queue);
        return $stat['msg_perm.mode'];
    }

    /**
     * Sets the access permissions of the semaphore.
     *
     * The current user must have access to the semaphore in order to change the permissions.
     *
     * @param int $mode A permissions mode to set.
     *
     * @throws SyncException If the operation failed.
     */
    public function setPermissions(int $mode): void
    {
        if (!\msg_set_queue($this->queue, ['msg_perm.mode' => $mode])) {
            throw new SyncException('Failed to change the semaphore permissions.');
        }
    }

    public function acquire(): Lock
    {
        do {
            // Attempt to acquire a lock from the semaphore.
            \set_error_handler($this->errorHandler);

            try {
                if (\msg_receive($this->queue, 0, $type, 1, $message, false, \MSG_IPC_NOWAIT, $errno)) {
                    // A free lock was found, so resolve with a lock object that can
                    // be used to release the lock.
                    return new Lock($this->release(...));
                }
            } finally {
                \restore_error_handler();
            }

            // Check for unusual errors.
            if ($errno !== \MSG_ENOMSG) {
                throw new SyncException(\sprintf('Failed to acquire a lock; errno: %d', $errno));
            }

            delay(self::LATENCY_TIMEOUT);
        } while (true);
    }

    /**
     * Removes the semaphore if it still exists.
     *
     * @throws SyncException If the operation failed.
     */
    public function __destruct()
    {
        if ($this->initializer === 0 || $this->initializer !== \getmypid()) {
            return;
        }

        if (!\msg_queue_exists($this->key)) {
            return;
        }

        \msg_remove_queue($this->queue);
    }

    /**
     * Releases a lock from the semaphore.
     *
     * @throws SyncException If the operation failed.
     */
    private function release(): void
    {
        // Send in non-blocking mode. If the call fails because the queue is full,
        // then the number of locks configured is too large.
        \set_error_handler($this->errorHandler);

        try {
            if (!\msg_send($this->queue, 1, "\0", false, false, $errno)) {
                if ($errno === \MSG_EAGAIN) {
                    throw new SyncException('The semaphore size is larger than the system allows.');
                }

                throw new SyncException('Failed to release the lock.');
            }
        } finally {
            \restore_error_handler();
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\Sync;

use SplObjectStorage;
use function Amp\async;
use function Amp\Future\awaitAll;

/**
 * A handle on an acquired lock from a synchronization object.
 *
 * This object is not thread-safe; after acquiring a lock from a mutex or semaphore, the lock should reside in the same
 * thread or process until it is released.
 */
final class Lock
{
    private static ?\Fiber $testFiber = null;

    private static ?\SplObjectStorage $pendingOperations = null;

    /** @var null|\Closure():void The function to be called on release or null if the lock has been released. */
    private ?\Closure $release;

    /**
     * Creates a new lock permit object.
     *
     * @param \Closure():void $release A function to be called upon release.
     */
    public function __construct(\Closure $release)
    {
        $this->release = $release;
    }

    private static function setupPendingOperations(): SplObjectStorage
    {
        $pending = new SplObjectStorage();

        \register_shutdown_function(static function () use ($pending): void {
            while ($pending->count() > 0) {
                awaitAll($pending);
            }
        });

        return $pending;
    }

    /**
     * Checks if the lock has already been released.
     *
     * @return bool True if the lock has already been released, otherwise false.
     */
    public function isReleased(): bool
    {
        return $this->release === null;
    }

    /**
     * Releases the lock. No-op if the lock has already been released.
     */
    public function release(): void
    {
        if ($this->release === null) {
            return;
        }

        // Invoke the releaser function given to us by the synchronization source to release the lock.
        $release = $this->release;
        $this->release = null;

        if ($this->isForceClosed()) {
            $future = async($release);

            $pending = self::$pendingOperations ??= self::setupPendingOperations();
            $pending->attach($future);
            $future->finally(fn () => $pending->detach($future));
        } else {
            $release();
        }
    }

    /**
     * Releases the lock when there are no more references to it.
     */
    public function __destruct()
    {
        if ($this->release) {
            async($this->release);
            $this->release = null;
        }
    }

    private function isForceClosed(): bool
    {
        $fiber = self::$testFiber ??= new \Fiber(function () {
            while (true) {
                \Fiber::suspend();
            }
        });

        try {
            if ($fiber->isStarted()) {
                $fiber->resume();
            } else {
                $fiber->start();
            }

            return false;
        } catch (\FiberError) {
            return true;
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\Sync;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;

final class LocalKeyedMutex implements KeyedMutex
{
    use ForbidCloning;
    use ForbidSerialization;

    private readonly LocalKeyedSemaphore $semaphore;

    public function __construct()
    {
        $this->semaphore = new LocalKeyedSemaphore(1);
    }

    public function acquire(string $key): Lock
    {
        return $this->semaphore->acquire($key);
    }
}
<?php declare(strict_types=1);

namespace Amp\Sync;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;

/**
 * @template T
 * @template-implements Parcel<T>
 */
final class LocalParcel implements Parcel
{
    use ForbidCloning;
    use ForbidSerialization;

    /**
     * @param T $value
     */
    public function __construct(
        private readonly Mutex $mutex,
        private mixed $value,
    ) {
    }

    public function synchronized(\Closure $closure): mixed
    {
        $lock = $this->mutex->acquire();

        try {
            $this->value = $closure($this->value);
        } finally {
            $lock->release();
        }

        return $this->value;
    }

    public function unwrap(): mixed
    {
        return $this->value;
    }
}
<?php declare(strict_types=1);

namespace Amp\Sync\Internal;

use Amp\Cancellation;
use Amp\DeferredFuture;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Pipeline\ConcurrentIterator;
use Amp\Pipeline\Queue;
use Amp\Sync\Channel;
use Amp\Sync\ChannelException;

/**
 * Creates a Channel from a ConcurrentIterator and Queue. The ConcurrentIterator emits data to be received on the
 * channel (data emitted on the ConcurrentIterator will be returned from calls to {@see Channel::receive()}).
 * The Queue will receive data that sent on the channel (data passed to {@see Channel::send()} will be passed to
 * {@see Queue::push()}).
 *
 * @template-covariant TReceive
 * @template TSend
 * @implements Channel<TReceive, TSend>
 *
 * @internal
 */
final class ConcurrentIteratorChannel implements Channel
{
    use ForbidCloning;
    use ForbidSerialization;

    private readonly DeferredFuture $onClose;

    /**
     * @param ConcurrentIterator<TReceive> $receive
     * @param Queue<TSend> $send
     */
    public function __construct(
        private readonly ConcurrentIterator $receive,
        private readonly Queue $send,
    ) {
        $this->onClose = new DeferredFuture();
    }

    public function __destruct()
    {
        $this->close();
    }

    public function isClosed(): bool
    {
        return $this->send->isComplete();
    }

    public function close(): void
    {
        if (!$this->send->isComplete()) {
            $this->send->complete();
        }

        $this->receive->dispose();

        if (!$this->onClose->isComplete()) {
            $this->onClose->complete();
        }
    }

    public function onClose(\Closure $onClose): void
    {
        $this->onClose->getFuture()->finally($onClose);
    }

    public function receive(?Cancellation $cancellation = null): mixed
    {
        if (!$this->receive->continue($cancellation)) {
            $this->close();
            throw new ChannelException("The channel closed while waiting to receive the next value");
        }

        return $this->receive->getValue();
    }

    public function send(mixed $data): void
    {
        if ($data === null) {
            throw new ChannelException("Cannot send null on a channel");
        }

        if ($this->send->isComplete()) {
            throw new ChannelException("Cannot send on a closed channel");
        }

        $this->send->push($data);
    }
}
<?php declare(strict_types=1);

namespace Amp\Sync;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;

final class PrefixedKeyedSemaphore implements KeyedSemaphore
{
    use ForbidCloning;
    use ForbidSerialization;

    public function __construct(
        private readonly KeyedSemaphore $semaphore,
        private readonly string $prefix
    ) {
    }

    public function acquire(string $key): Lock
    {
        return $this->semaphore->acquire($this->prefix . $key);
    }
}
<?php declare(strict_types=1);

namespace Amp\Sync;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;

final class LocalMutex implements Mutex
{
    use ForbidCloning;
    use ForbidSerialization;

    private readonly LocalSemaphore $semaphore;

    public function __construct()
    {
        $this->semaphore = new LocalSemaphore(1);
    }

    public function acquire(): Lock
    {
        return $this->semaphore->acquire();
    }
}
<?php declare(strict_types=1);

namespace Amp\Sync;

class ChannelException extends \Exception
{
}
<?php declare(strict_types=1);

namespace Amp\Sync;

use Amp\Cancellation;
use Amp\DeferredFuture;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;

/**
 * A barrier is a synchronization primitive.
 *
 * The barrier is initialized with a certain count, which can be increased and decreased until it reaches zero.
 *
 * A count of one can be used to block multiple coroutines until a certain condition is met.
 *
 * A count of N can be used to await multiple coroutines doing an action to complete.
 *
 * **Example**
 *
 * ```php
 * $barrier = new Amp\Sync\Barrier(2);
 * $barrier->arrive();
 * $barrier->arrive(); // Barrier::await() returns immediately now
 *
 * $barrier->await();
 * ```
 */
final class Barrier
{
    use ForbidCloning;
    use ForbidSerialization;

    private int $count;

    private readonly DeferredFuture $completion;

    /**
     * @param positive-int $count
     */
    public function __construct(int $count)
    {
        /** @psalm-suppress TypeDoesNotContainType */
        if ($count < 1) {
            throw new \Error('Count must be positive, got ' . $count);
        }

        $this->count = $count;
        $this->completion = new DeferredFuture;
    }

    public function getCount(): int
    {
        return $this->count;
    }

    /**
     * @param positive-int $count
     */
    public function arrive(int $count = 1): void
    {
        /** @psalm-suppress TypeDoesNotContainType */
        if ($count < 1) {
            throw new \Error('Count must be at least 1, got ' . $count);
        }

        if ($count > $this->count) {
            throw new \Error('Count cannot be greater than remaining count: ' . $count . ' > ' . $this->count);
        }

        $this->count -= $count;

        if ($this->count === 0) {
            $this->completion->complete();
        }
    }

    /**
     * @param positive-int $count
     */
    public function register(int $count = 1): void
    {
        /** @psalm-suppress TypeDoesNotContainType */
        if ($count < 1) {
            throw new \Error('Count must be at least 1, got ' . $count);
        }

        if ($this->count === 0) {
            throw new \Error('Can\'t increase count, because the barrier already broke');
        }

        $this->count += $count;
    }

    public function await(?Cancellation $cancellation = null): void
    {
        $this->completion->getFuture()->await($cancellation);
    }
}
<?php declare(strict_types=1);

namespace Amp\Sync;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;

final class LocalKeyedSemaphore implements KeyedSemaphore
{
    use ForbidCloning;
    use ForbidSerialization;

    /** @var LocalSemaphore[] */
    private array $semaphore = [];

    /** @var int[] */
    private array $locks = [];

    /**
     * @param positive-int $maxLocks
     */
    public function __construct(
        private readonly int $maxLocks,
    ) {
        /** @psalm-suppress TypeDoesNotContainType */
        if ($maxLocks < 1) {
            throw new \ValueError('The number of locks must be greater than 0, got ' . $maxLocks);
        }
    }

    public function acquire(string $key): Lock
    {
        if (!isset($this->semaphore[$key])) {
            $this->semaphore[$key] = new LocalSemaphore($this->maxLocks);
            $this->locks[$key] = 0;
        }

        $this->locks[$key]++;

        $lock = $this->semaphore[$key]->acquire();

        return new Lock(function () use ($lock, $key): void {
            if (--$this->locks[$key] === 0) {
                unset($this->semaphore[$key], $this->locks[$key]);
            }

            $lock->release();
        });
    }
}
<?php declare(strict_types=1);

namespace Amp\Sync;

class ParcelException extends \Exception
{
}
<?php declare(strict_types=1);

namespace Amp\Sync;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;

final class PrefixedKeyedMutex implements KeyedMutex
{
    use ForbidCloning;
    use ForbidSerialization;

    public function __construct(
        private readonly KeyedMutex $mutex,
        private readonly string $prefix
    ) {
    }

    public function acquire(string $key): Lock
    {
        return $this->mutex->acquire($this->prefix . $key);
    }
}
<?php declare(strict_types=1);

namespace Amp\Sync;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Revolt\EventLoop;
use Revolt\EventLoop\Suspension;

final class LocalSemaphore implements Semaphore
{
    use ForbidCloning;
    use ForbidSerialization;

    private int $locks = 0;

    /** @var \SplQueue<Suspension> */
    private readonly \SplQueue $waiting;

    /**
     * @param positive-int $maxLocks
     */
    public function __construct(private readonly int $maxLocks)
    {
        /** @psalm-suppress TypeDoesNotContainType */
        if ($maxLocks < 1) {
            throw new \ValueError('The number of locks must be greater than 0, got ' . $maxLocks);
        }

        $this->waiting = new \SplQueue();
    }

    public function acquire(): Lock
    {
        if ($this->locks < $this->maxLocks) {
            ++$this->locks;
            return $this->createLock();
        }

        $this->waiting->enqueue($suspension = EventLoop::getSuspension());

        return $suspension->suspend();
    }

    private function release(): void
    {
        if (!$this->waiting->isEmpty()) {
            $suspension = $this->waiting->dequeue();
            $suspension->resume($this->createLock());

            return;
        }

        --$this->locks;
    }

    private function createLock(): Lock
    {
        return new Lock($this->release(...));
    }
}
<?php declare(strict_types=1);

namespace Amp\Sync;

use Amp\Pipeline\Queue;
use Amp\Sync\Internal\ConcurrentIteratorChannel;
use Revolt\EventLoop\FiberLocal;

/**
 * Invokes the given Closure while maintaining a lock from the provided mutex.
 *
 * The lock is automatically released after the Closure returns.
 *
 * @template T
 *
 * @param \Closure(mixed...):T $synchronized
 *
 * @return T The return value of the Closure.
 */
function synchronized(Semaphore $semaphore, \Closure $synchronized, mixed ...$args): mixed
{
    static $reentry;
    $reentry ??= new FiberLocal(fn () => new \WeakMap());

    /** @var \WeakMap<Semaphore, bool> $existingLocks */
    $existingLocks = $reentry->get();
    if ($existingLocks[$semaphore] ?? false) {
        return $synchronized(...$args);
    }

    $lock = $semaphore->acquire();
    $existingLocks[$semaphore] = true;

    try {
        return $synchronized(...$args);
    } finally {
        unset($existingLocks[$semaphore]);
        $lock->release();
    }
}

/**
 * @param int $bufferSize Number of channel items to buffer in memory before back-pressure is applied.
 * @return array{Channel, Channel}
 */
function createChannelPair(int $bufferSize = 0): array
{
    $west = new Queue($bufferSize);
    $east = new Queue($bufferSize);

    return [
        new ConcurrentIteratorChannel($west->iterate(), $east),
        new ConcurrentIteratorChannel($east->iterate(), $west),
    ];
}
<?php declare(strict_types=1);

namespace Amp\Sync;

class SyncException extends \Exception
{
}
{
    "name": "amphp/sync",
    "description": "Non-blocking synchronization primitives for PHP based on Amp and Revolt.",
    "keywords": [
        "asynchronous",
        "async",
        "mutex",
        "semaphore",
        "synchronization"
    ],
    "homepage": "https://github.com/amphp/sync",
    "license": "MIT",
    "authors": [
        {
            "name": "Aaron Piotrowski",
            "email": "aaron@trowski.com"
        },
        {
            "name": "Niklas Keller",
            "email": "me@kelunik.com"
        },
        {
            "name": "Stephen Coakley",
            "email": "me@stephencoakley.com"
        }
    ],
    "require": {
        "php": ">=8.1",
        "amphp/amp": "^3",
        "amphp/pipeline": "^1",
        "amphp/serialization": "^1",
        "revolt/event-loop": "^1 || ^0.2"
    },
    "require-dev": {
        "phpunit/phpunit": "^9",
        "amphp/phpunit-util": "^3",
        "amphp/php-cs-fixer-config": "^2",
        "psalm/phar": "5.23"
    },
    "autoload": {
        "psr-4": {
            "Amp\\Sync\\": "src"
        },
        "files": [
            "src/functions.php"
        ]
    },
    "autoload-dev": {
        "psr-4": {
            "Amp\\Sync\\": "test"
        }
    }
}
<?php

$config = new Amp\CodeStyle\Config();
$config->getFinder()
    ->in(__DIR__ . '/examples')
    ->in(__DIR__ . '/src')
    ->in(__DIR__ . '/test');

$config->setCacheFile(__DIR__ . '/.php_cs.cache');

return $config;
{
    "symbol-whitelist": [
        "null",
        "true",
        "false",
        "static",
        "self",
        "parent",
        "array",
        "string",
        "int",
        "float",
        "bool",
        "iterable",
        "callable",
        "mixed",
        "void",
        "object",
        "buffer",
        "split",
        "splitLines",
        "deflate_add",
        "deflate_init",
        "inflate_add",
        "inflate_init",
        "ZLIB_FINISH",
        "ZLIB_SYNC_FLUSH",
        "json_decode",
        "JSON_THROW_ON_ERROR"
    ],
    "php-core-extensions": [
        "Core",
        "date",
        "pcre",
        "Phar",
        "Reflection",
        "SPL",
        "standard"
    ]
}
<?php declare(strict_types=1);

namespace Amp\ByteStream;

trait ReadableStreamIteratorAggregate
{
    /** @see ReadableStream::read() */
    abstract public function read(): ?string;

    /**
     * @return \Traversable<int, string>
     */
    public function getIterator(): \Traversable
    {
        while (($chunk = $this->read()) !== null) {
            yield $chunk;
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\ByteStream;

use Amp\DeferredFuture;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Revolt\EventLoop;
use Revolt\EventLoop\Suspension;

/**
 * Output stream abstraction for PHP's stream resources.
 */
final class WritableResourceStream implements WritableStream, ResourceStream
{
    use ForbidCloning;
    use ForbidSerialization;

    private const LARGE_CHUNK_SIZE = 128 * 1024;

    /** @var resource|null */
    private $resource;

    private string $callbackId;

    /** @var \SplQueue<array{string, Suspension|null}> */
    private readonly \SplQueue $writes;

    private bool $writable = true;

    /** @var positive-int|null */
    private ?int $chunkSize = null;

    /** @var \Closure():bool */
    private readonly \Closure $errorHandler;

    private readonly DeferredFuture $onClose;

    /**
     * @param resource $stream Stream resource.
     * @param positive-int|null $chunkSize Chunk size per `fwrite()` operation.
     */
    public function __construct($stream, ?int $chunkSize = null)
    {
        if (!\is_resource($stream) || \get_resource_type($stream) !== 'stream') {
            throw new \Error("Expected a valid stream");
        }

        $meta = \stream_get_meta_data($stream);

        if (\str_contains($meta["mode"], "r") && !\str_contains($meta["mode"], "+")) {
            throw new \Error("Expected a writable stream");
        }

        /** @psalm-suppress TypeDoesNotContainType */
        if ($chunkSize !== null && $chunkSize <= 0) {
            throw new \ValueError('The chunk length must be a positive integer');
        }

        $this->onClose = $onClose = new DeferredFuture;

        \stream_set_blocking($stream, false);
        \stream_set_write_buffer($stream, 0);

        // Ignore any errors raised while this handler is set. Errors will be checked through return values.
        $this->errorHandler = static fn () => true;

        $this->resource = $stream;
        $this->chunkSize = &$chunkSize;

        $writes = $this->writes = new \SplQueue;
        $writable = &$this->writable;
        $resource = &$this->resource;

        $this->callbackId = EventLoop::disable(EventLoop::onWritable(
            $this->resource,
            static function ($callbackId) use (
                $writes,
                &$chunkSize,
                &$writable,
                &$resource,
                $onClose,
            ): void {
                $firstWrite = true;

                try {
                    while (!$writes->isEmpty()) {
                        /** @var Suspension|null $suspension */
                        [$data, $suspension] = $writes->shift();
                        $length = \strlen($data);

                        if ($length === 0) {
                            $suspension?->resume();
                            continue;
                        }

                        if (!$writable) {
                            $suspension?->resume(static fn () => throw new ClosedException("The stream was closed"));
                            continue;
                        }

                        /** @psalm-suppress TypeDoesNotContainType */
                        if (!\is_resource($resource)) {
                            $writable = false;
                            $suspension?->resume(static fn () => throw new ClosedException("The stream was closed by the peer"));
                            continue;
                        }

                        // Using error handler to verify that writing zero bytes was not due an error.
                        // @see https://github.com/reactphp/stream/pull/150
                        $errorCode = 0;
                        $errorMessage = 'Unknown error';
                        \set_error_handler(static function (int $errno, string $message) use (&$errorCode, &$errorMessage): bool {
                            $errorCode = $errno;
                            $errorMessage = $message;

                            return true;
                        });

                        try {
                            // Customer error handler needed since fwrite() emits E_WARNING if the pipe is broken or the buffer is full.
                            // Use conditional, because PHP doesn't like getting null passed
                            if ($chunkSize) {
                                $written = \fwrite($resource, $data, $chunkSize);
                            } else {
                                $written = \fwrite($resource, $data);
                            }
                        } finally {
                            \restore_error_handler();
                        }

                        $written = (int) $written; // Cast potential false to 0.

                        // Broken pipes between processes on macOS/FreeBSD do not detect EOF properly.
                        // fwrite() may write zero bytes on subsequent calls due to the buffer filling again.
                        /** @psalm-suppress TypeDoesNotContainType $errorCode may be set by error handler. */
                        if ($written === 0 && $errorCode !== 0 && $firstWrite) {
                            $writable = false;
                            $suspension?->resume(static fn () => throw new StreamException(
                                \sprintf('Failed to write to stream (%d): %s', $errorCode, $errorMessage)
                            ));

                            continue;
                        }

                        if ($length > $written) {
                            $data = \substr($data, $written);
                            $writes->unshift([$data, $suspension]);
                            return;
                        }

                        $suspension?->resume();
                        $firstWrite = false;
                    }
                } finally {
                    /** @psalm-suppress RedundantCondition */
                    if (!$writable && \is_resource($resource)) {
                        $meta = \stream_get_meta_data($resource);
                        if (\str_contains($meta["mode"], "+")) {
                            \stream_socket_shutdown($resource, \STREAM_SHUT_WR);
                        } else {
                            \fclose($resource);
                        }
                        $resource = null;
                    }

                    if ($writes->isEmpty()) {
                        if ($writable) {
                            EventLoop::disable($callbackId);
                        } else {
                            EventLoop::cancel($callbackId);

                            if (!$onClose->isComplete()) {
                                $onClose->complete();
                            }
                        }
                    }
                }
            }
        ));
    }

    /**
     * Writes data to the stream.
     *
     * @param string $bytes Bytes to write.
     *
     * @throws ClosedException If the stream has already been closed.
     */
    public function write(string $bytes): void
    {
        if (!$this->writable) {
            throw new ClosedException("The stream is not writable");
        }

        $length = \strlen($bytes);
        $written = 0;

        if ($this->writes->isEmpty()) {
            if ($length === 0) {
                return;
            }

            if (!\is_resource($this->resource)) {
                throw new ClosedException("The stream was closed by the peer");
            }

            \set_error_handler($this->errorHandler);

            try {
                // Error reporting suppressed since fwrite() emits E_WARNING if the pipe is broken or the buffer is full.
                // Use conditional, because PHP doesn't like getting null passed.
                if ($this->chunkSize) {
                    $written = \fwrite($this->resource, $bytes, $this->chunkSize);
                } else {
                    $written = \fwrite($this->resource, $bytes);
                }
            } finally {
                \restore_error_handler();
            }

            $written = (int) $written; // Cast potential false to 0.

            if ($length === $written) {
                return;
            }

            if ($written > 0) {
                $bytes = \substr($bytes, $written);
            }
        }

        if ($length - $written > self::LARGE_CHUNK_SIZE) {
            $chunks = \str_split($bytes, self::LARGE_CHUNK_SIZE);

            /** @var string $data */
            $bytes = \array_pop($chunks);

            foreach ($chunks as $chunk) {
                $this->writes->push([$chunk, null]);
            }
        }

        EventLoop::enable($this->callbackId);
        $this->writes->push([$bytes, $suspension = EventLoop::getSuspension()]);

        if ($closure = $suspension->suspend()) {
            $closure();
        }
    }

    /**
     * Closes the stream after all pending writes have been completed. Optionally writes a final data chunk before.
     */
    public function end(): void
    {
        $this->writable = false;

        if ($this->writes->isEmpty()) {
            $this->close();
        }
    }

    public function isWritable(): bool
    {
        return $this->writable;
    }

    /**
     * Closes the stream forcefully. Multiple `close()` calls are ignored.
     */
    public function close(): void
    {
        if (\is_resource($this->resource) && \get_resource_type($this->resource) === 'stream') {
            // Error suppression, as resource might already be closed
            $meta = \stream_get_meta_data($this->resource);

            if (\str_contains($meta["mode"], "+")) {
                \stream_socket_shutdown($this->resource, \STREAM_SHUT_WR);
            } else {
                /** @psalm-suppress InvalidPropertyAssignmentValue psalm reports this as closed-resource */
                \fclose($this->resource);
            }
        }

        $this->free();
    }

    public function isClosed(): bool
    {
        return $this->resource === null;
    }

    public function onClose(\Closure $onClose): void
    {
        $this->onClose->getFuture()->finally($onClose);
    }

    /**
     * @return resource|object|null Stream resource or null if end() has been called or the stream closed.
     */
    public function getResource()
    {
        return $this->resource;
    }

    /**
     * @param positive-int $chunkSize
     */
    public function setChunkSize(int $chunkSize): void
    {
        /** @psalm-suppress TypeDoesNotContainType */
        if ($chunkSize <= 0) {
            throw new \ValueError('The chunk length must be a positive integer');
        }

        $this->chunkSize = $chunkSize;
    }

    public function __destruct()
    {
        $this->free();
    }

    /**
     * References the writable watcher, so the loop keeps running in case there's a pending write.
     *
     * @see EventLoop::reference()
     */
    public function reference(): void
    {
        if (!$this->resource) {
            return;
        }

        EventLoop::reference($this->callbackId);
    }

    /**
     * Unreferences the writable watcher, so the loop doesn't keep running even if there are pending writes.
     *
     * @see EventLoop::unreference()
     */
    public function unreference(): void
    {
        if (!$this->resource) {
            return;
        }

        EventLoop::unreference($this->callbackId);
    }

    /**
     * Nulls reference to resource, marks stream unwritable, and fails any pending write.
     */
    private function free(): void
    {
        if ($this->resource === null) {
            return;
        }

        $this->resource = null;
        $this->writable = false;

        if (!$this->writes->isEmpty()) {
            $exception = new ClosedException("The socket was closed before writing completed");
            do {
                /** @var Suspension|null $suspension */
                [, $suspension] = $this->writes->shift();
                $suspension?->throw($exception);
            } while (!$this->writes->isEmpty());
        }

        EventLoop::cancel($this->callbackId);

        if (!$this->onClose->isComplete()) {
            $this->onClose->complete();
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\ByteStream;

use Amp\Cancellation;
use Amp\CancelledException;
use Amp\DeferredFuture;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Revolt\EventLoop;
use Revolt\EventLoop\Suspension;

/**
 * Readable stream abstraction for PHP's stream resources.
 *
 * @implements \IteratorAggregate<int, string>
 */
final class ReadableResourceStream implements ReadableStream, ResourceStream, \IteratorAggregate
{
    use ReadableStreamIteratorAggregate;
    use ForbidCloning;
    use ForbidSerialization;

    public const DEFAULT_CHUNK_SIZE = 8192;

    /** @var \Closure():bool */
    private static \Closure $errorHandler;

    /** @var resource|null */
    private $resource;

    private string $callbackId;

    private ?Suspension $suspension = null;

    private bool $readable = true;

    private int $chunkSize;

    private readonly bool $useSingleRead;

    private int $defaultChunkSize;

    /** @var \Closure(CancelledException):void */
    private readonly \Closure $cancel;

    private readonly DeferredFuture $onClose;

    private int $continuousReads = 0;

    /** @var \Closure():void */
    private readonly \Closure $resumeSuspension;

    /** @var \Closure():void */
    private readonly \Closure $resetContinuousReads;

    /**
     * @param resource $stream Stream resource.
     * @param positive-int $chunkSize Default chunk size per read operation.
     *
     * @throws \Error If an invalid stream or parameter has been passed.
     */
    public function __construct($stream, int $chunkSize = self::DEFAULT_CHUNK_SIZE)
    {
        if (!\is_resource($stream) || \get_resource_type($stream) !== 'stream') {
            throw new \Error("Expected a valid stream");
        }

        $meta = \stream_get_meta_data($stream);
        $this->useSingleRead = $useSingleRead = $meta["stream_type"] === "udp_socket" || $meta["stream_type"] === "STDIO";

        if (!\str_contains($meta["mode"], "r") && !\str_contains($meta["mode"], "+")) {
            throw new \Error("Expected a readable stream");
        }

        /** @psalm-suppress TypeDoesNotContainType */
        if ($chunkSize <= 0) {
            throw new \ValueError('The chunk length must be a positive integer');
        }

        $this->onClose = $onClose = new DeferredFuture;

        \stream_set_blocking($stream, false);
        \stream_set_read_buffer($stream, 0);

        // Ignore any errors raised while this handler is set. Errors will be checked through return values.
        /** @psalm-suppress RedundantPropertyInitializationCheck */
        self::$errorHandler ??= static fn () => true;

        $this->resource = &$stream;
        $this->defaultChunkSize = $this->chunkSize = &$chunkSize;

        $suspension = &$this->suspension;
        $readable = &$this->readable;

        $this->callbackId = EventLoop::disable(EventLoop::onReadable($this->resource, static function ($callbackId) use (
            &$suspension,
            &$readable,
            &$stream,
            &$chunkSize,
            $useSingleRead,
            $onClose,
        ): void {
            \assert($stream !== null, 'Watcher invoked with null stream');

            \set_error_handler(self::$errorHandler);

            try {
                if ($useSingleRead) {
                    $data = \fread($stream, $chunkSize);
                } else {
                    $data = \stream_get_contents($stream, $chunkSize);
                }
            } finally {
                \restore_error_handler();
            }

            \assert(
                $data !== false,
                "Trying to read from a previously fclose()'d resource. Do NOT manually fclose() resources the loop still has a reference to."
            );

            if ($data === '' && \feof($stream)) {
                $readable = false;
                $stream = null;
                $data = null; // Stream closed, resolve read with null.

                EventLoop::cancel($callbackId);

                if (!$onClose->isComplete()) {
                    $onClose->complete();
                }
            } else {
                EventLoop::disable($callbackId);
            }

            \assert($suspension instanceof Suspension);

            $suspension->resume($data);
            $suspension = null;
        }));

        $callbackId = &$this->callbackId;
        $this->cancel = static function (CancelledException $exception) use (&$suspension, $callbackId): void {
            $suspension?->throw($exception);
            $suspension = null;

            EventLoop::disable($callbackId);
        };

        $this->resumeSuspension = static function () use (&$suspension): void {
            $suspension?->resume();
            $suspension = null;
        };

        $continuousReads = &$this->continuousReads;
        $this->resetContinuousReads = static function () use (&$continuousReads): void {
            $continuousReads = 0;
        };
    }

    /**
     * @param positive-int|null $limit
     */
    public function read(?Cancellation $cancellation = null, ?int $limit = null): ?string
    {
        $limit ??= $this->defaultChunkSize;

        if ($limit <= 0) {
            throw new \ValueError('The length limit must be a positive integer, got ' . $limit);
        }

        if ($this->suspension !== null) {
            throw new PendingReadError;
        }

        if (!$this->readable) {
            return null; // Return null on closed stream.
        }

        \assert($this->resource !== null);

        \set_error_handler(self::$errorHandler);

        try {
            // Attempt a direct read because PHP may buffer data, e.g. in TLS buffers.
            if ($this->useSingleRead) {
                $data = \fread($this->resource, $limit);
            } else {
                $data = \stream_get_contents($this->resource, $limit);
            }
        } finally {
            \restore_error_handler();
        }

        \assert(
            $data !== false,
            "Trying to read from a previously fclose()'d resource. Do NOT manually fclose() resources the loop still has a reference to."
        );

        if ($data === '') {
            if (\feof($this->resource)) {
                $this->free();

                return null;
            }

            $this->chunkSize = $limit;
            EventLoop::enable($this->callbackId);
            $this->suspension = EventLoop::getSuspension();

            $id = $cancellation?->subscribe($this->cancel);

            try {
                return $this->suspension->suspend();
            } finally {
                /** @psalm-suppress PossiblyNullArgument If $cancellation is not null, $id will not be null. */
                $cancellation?->unsubscribe($id);
            }
        }

        if ($this->continuousReads > 10) {
            // Use a deferred suspension so other events are not starved by a stream that always has data available.
            $this->suspension = EventLoop::getSuspension();
            EventLoop::defer($this->resumeSuspension);
            $this->suspension->suspend();
        } elseif ($this->continuousReads++ === 0) {
            EventLoop::defer($this->resetContinuousReads);
        }

        return $data;
    }

    public function isReadable(): bool
    {
        return $this->readable;
    }

    /**
     * Closes the stream forcefully. Multiple `close()` calls are ignored.
     */
    public function close(): void
    {
        if (\is_resource($this->resource) && \get_resource_type($this->resource) === 'stream') {
            $meta = \stream_get_meta_data($this->resource);

            if (\str_contains($meta["mode"], "+")) {
                \stream_socket_shutdown($this->resource, \STREAM_SHUT_RD);
            } else {
                /** @psalm-suppress InvalidPropertyAssignmentValue */
                \fclose($this->resource);
            }
        }

        $this->suspension?->resume();
        $this->suspension = null;

        $this->free();
    }

    public function isClosed(): bool
    {
        return $this->resource === null;
    }

    public function onClose(\Closure $onClose): void
    {
        $this->onClose->getFuture()->finally($onClose);
    }

    /**
     * @return resource|object|null The stream resource or null if the stream has closed.
     */
    public function getResource()
    {
        return $this->resource;
    }

    /**
     * @param positive-int $chunkSize
     */
    public function setChunkSize(int $chunkSize): void
    {
        /** @psalm-suppress TypeDoesNotContainType */
        if ($chunkSize <= 0) {
            throw new \ValueError('The chunk length must be a positive integer');
        }

        $this->defaultChunkSize = $chunkSize;
    }

    /**
     * References the readable watcher, so the loop keeps running in case there's an active read.
     *
     * @see EventLoop::reference()
     */
    public function reference(): void
    {
        if (!$this->resource) {
            return;
        }

        EventLoop::reference($this->callbackId);
    }

    /**
     * Unreferences the readable watcher, so the loop doesn't keep running even if there are active reads.
     *
     * @see EventLoop::unreference()
     */
    public function unreference(): void
    {
        if (!$this->resource) {
            return;
        }

        EventLoop::unreference($this->callbackId);
    }

    public function __destruct()
    {
        if ($this->resource !== null) {
            $this->free();
        }
    }

    /**
     * Nulls reference to resource, marks stream unreadable, and succeeds any pending read with null.
     */
    private function free(): void
    {
        $this->readable = false;
        $this->resource = null;

        EventLoop::cancel($this->callbackId);

        if (!$this->onClose->isComplete()) {
            $this->onClose->complete();
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\ByteStream;

use Amp\Cancellation;
use Amp\DeferredFuture;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;

/**
 * ReadableStream with a single already known data chunk.
 *
 * @implements \IteratorAggregate<int, string>
 */
final class ReadableBuffer implements ReadableStream, \IteratorAggregate
{
    use ReadableStreamIteratorAggregate;
    use ForbidCloning;
    use ForbidSerialization;

    private ?string $contents;

    private readonly DeferredFuture $onClose;

    /**
     * @param string|null $contents Data chunk or `null` for no data chunk.
     */
    public function __construct(?string $contents = null)
    {
        $this->contents = $contents === '' ? null : $contents;
        $this->onClose = new DeferredFuture;

        if ($this->contents === null) {
            $this->close();
        }
    }

    public function read(?Cancellation $cancellation = null): ?string
    {
        $contents = $this->contents;
        $this->close();

        return $contents;
    }

    public function isReadable(): bool
    {
        return $this->contents !== null;
    }

    public function close(): void
    {
        $this->contents = null;
        if (!$this->onClose->isComplete()) {
            $this->onClose->complete();
        }
    }

    public function isClosed(): bool
    {
        return !$this->isReadable();
    }

    public function onClose(\Closure $onClose): void
    {
        $this->onClose->getFuture()->finally($onClose);
    }
}
<?php declare(strict_types=1);

namespace Amp\ByteStream;

use Amp\Cancellation;
use Amp\CancelledException;
use Amp\DeferredFuture;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Pipeline\ConcurrentIterator;
use Amp\Pipeline\DisposedException;
use Amp\Pipeline\Pipeline;

/**
 * Creates a stream from an iterable emitting strings. If the iterable throws an exception, the exception will
 * be thrown from {@see read()} and {@see buffer()}. Consider wrapping any exceptions in {@see StreamException}
 * if you do not wish for another type of exception to be thrown from the stream.
 *
 * @implements \IteratorAggregate<int, string>
 */
final class ReadableIterableStream implements ReadableStream, \IteratorAggregate
{
    use ReadableStreamIteratorAggregate;
    use ForbidCloning;
    use ForbidSerialization;

    /** @var ConcurrentIterator<string>|null */
    private ?ConcurrentIterator $iterator;

    private ?\Throwable $exception = null;

    private bool $pending = false;

    private readonly DeferredFuture $onClose;

    /**
     * @param iterable<mixed, string> $iterable
     */
    public function __construct(iterable $iterable)
    {
        $this->iterator = $iterable instanceof ConcurrentIterator
            ? $iterable
            : Pipeline::fromIterable($iterable)->getIterator();

        $this->onClose = new DeferredFuture;
    }

    public function read(?Cancellation $cancellation = null): ?string
    {
        if ($this->exception) {
            throw $this->exception;
        }

        if ($this->pending) {
            throw new PendingReadError;
        }

        if ($this->iterator === null) {
            return null;
        }

        $this->pending = true;

        try {
            if (!$this->iterator->continue($cancellation)) {
                $this->iterator = null;
                return null;
            }

            $chunk = $this->iterator->getValue();

            if (!\is_string($chunk)) {
                throw new StreamException(\sprintf(
                    "Unexpected iterable value of type %s, expected string",
                    \get_debug_type($chunk)
                ));
            }

            return $chunk;
        } catch (\Throwable $exception) {
            if ($exception instanceof CancelledException && $cancellation?->isRequested()) {
                throw $exception; // Read cancelled, stream did not fail.
            }

            if ($exception instanceof DisposedException) {
                $exception = new ClosedException('Stream manually closed', previous: $exception);
            }

            throw $this->exception = $exception;
        } finally {
            $this->pending = false;
        }
    }

    public function isReadable(): bool
    {
        return $this->iterator !== null;
    }

    public function close(): void
    {
        $this->iterator?->dispose();
        $this->iterator = null;

        if (!$this->onClose->isComplete()) {
            $this->onClose->complete();
        }
    }

    public function isClosed(): bool
    {
        return !$this->isReadable();
    }

    public function onClose(\Closure $onClose): void
    {
        $this->onClose->getFuture()->finally($onClose);
    }
}
<?php declare(strict_types=1);

namespace Amp\ByteStream;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;

/**
 * Create a local stream where data written to the pipe is immediately available on the pipe.
 *
 * Primarily useful for testing.
 */
final class Pipe
{
    use ForbidCloning;
    use ForbidSerialization;

    private readonly WritableStream $sink;

    private readonly ReadableStream $source;

    public function __construct(int $bufferSize)
    {
        $this->sink = new WritableIterableStream($bufferSize);
        $this->source = new ReadableIterableStream($this->sink->getIterator());
    }

    /**
     * @return ReadableStream Data written to the WritableStream returned by {@see getSink()} will be readable
     * on this stream.
     */
    public function getSource(): ReadableStream
    {
        return $this->source;
    }

    /**
     * @return WritableStream Data written to this stream will be readable by the stream returned from
     * {@see getSource()}.
     */
    public function getSink(): WritableStream
    {
        return $this->sink;
    }
}
<?php declare(strict_types=1);

namespace Amp\ByteStream;

final class ClosedException extends StreamException
{
}
<?php declare(strict_types=1);

namespace Amp\ByteStream;

use Amp\Cancellation;
use Amp\DeferredFuture;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;

/**
 * Creates a buffered message from a ReadableStream.
 *
 * The message can be consumed in chunks using the read() API, or it may be buffered and accessed in its entirety by
 * calling buffer(). Once buffering is requested through buffer(), the stream cannot be read in chunks.
 *
 * @implements \IteratorAggregate<int, string>
 */
final class Payload implements ReadableStream, \IteratorAggregate, \Stringable
{
    use ReadableStreamIteratorAggregate;
    use ForbidCloning;
    use ForbidSerialization;

    private const MODE_STREAM = 1;
    private const MODE_BUFFER = 2;

    private ReadableStream|string|null $stream;

    private int $mode = 0;

    private readonly DeferredFuture $onClose;

    public function __construct(ReadableStream|string $stream)
    {
        $this->stream = match (true) {
            $stream instanceof ReadableBuffer => $stream->read(),
            default => $stream,
        };

        $this->onClose = new DeferredFuture;

        if ($this->stream === null) {
            $this->close();
        }
    }

    public function __destruct()
    {
        if ($this->stream instanceof ReadableStream) {
            $this->stream->close();
        }
    }

    final public function read(?Cancellation $cancellation = null): ?string
    {
        if ($this->mode === self::MODE_BUFFER) {
            throw new \Error('Can\'t stream payload after calling buffer()');
        }

        $this->mode = self::MODE_STREAM;

        if ($this->stream instanceof ReadableStream) {
            return $this->stream->read($cancellation);
        }

        $chunk = $this->stream;
        $this->stream = null;

        return $chunk;
    }

    final public function isReadable(): bool
    {
        return $this->stream instanceof ReadableStream
            ? $this->stream->isReadable()
            : $this->stream !== null;
    }

    /**
     * Buffers the entire message.
     *
     * @param int $limit Only buffer up to the given number of bytes, throwing {@see BufferException} if exceeded.
     *
     * @return string The entire message contents.
     *
     * @throws StreamException
     * @throws BufferException
     */
    final public function buffer(?Cancellation $cancellation = null, int $limit = \PHP_INT_MAX): string
    {
        if ($this->mode === self::MODE_STREAM) {
            throw new \Error('Can\'t buffer payload after calling read()');
        }

        if ($this->mode === self::MODE_BUFFER) {
            throw new \Error('Can\'t buffer() a payload more than once');
        }

        $this->mode = self::MODE_BUFFER;

        if ($this->stream instanceof ReadableStream) {
            return buffer($this->stream, $cancellation, $limit);
        }

        $payload = $this->stream ?? '';
        $this->stream = null;

        return $payload;
    }

    public function close(): void
    {
        if ($this->stream instanceof ReadableStream) {
            $this->stream->close();
        }

        if (!$this->onClose->isComplete()) {
            $this->onClose->complete();
        }
    }

    public function isClosed(): bool
    {
        return !$this->isReadable();
    }

    public function onClose(\Closure $onClose): void
    {
        $this->onClose->getFuture()->finally($onClose);
    }

    /**
     * Buffers entire stream before returning. Use {@see self::buffer()} to optionally provide a {@see Cancellation}
     * and/or length limit.
     *
     * @throws BufferException|StreamException
     */
    public function __toString(): string
    {
        return $this->buffer();
    }
}
<?php declare(strict_types=1);

namespace Amp\ByteStream;

use Amp\ByteStream\Internal\ChannelParser;
use Amp\Cancellation;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Serialization\Serializer;
use Amp\Sync\Channel;
use Amp\Sync\ChannelException;
use Amp\Sync\LocalMutex;
use Amp\Sync\Mutex;
use function Amp\async;

/**
 * An asynchronous channel for sending data between threads and processes.
 *
 * Supports full duplex read and write.
 *
 * @template TReceive
 * @template TSend
 * @template-implements Channel<TReceive, TSend>
 */
final class StreamChannel implements Channel
{
    use ForbidCloning;
    use ForbidSerialization;

    private readonly ChannelParser $parser;

    /** @var \SplQueue<TReceive> */
    private readonly \SplQueue $received;

    private readonly Mutex $readMutex;

    /**
     * Creates a new channel from the given stream objects. Note that $read and $write can be the same object.
     */
    public function __construct(
        private readonly ReadableStream $read,
        private readonly WritableStream $write,
        ?Serializer $serializer = null,
    ) {
        $this->received = new \SplQueue();
        $this->readMutex = new LocalMutex();
        $this->parser = new ChannelParser($this->received->push(...), $serializer);
    }

    public function __destruct()
    {
        $this->close();
    }

    /**
     * Closes the read and write resource streams.
     */
    public function close(): void
    {
        $this->read->close();
        $this->write->close();
    }

    public function send(mixed $data): void
    {
        $data = $this->parser->encode($data);

        try {
            $this->write->write($data);
        } catch (\Throwable $exception) {
            throw new ChannelException("Sending on the channel failed. Did the context die?", 0, $exception);
        }
    }

    public function receive(?Cancellation $cancellation = null): mixed
    {
        $cancellation?->throwIfRequested();

        $lock = $this->readMutex->acquire();

        try {
            while ($this->received->isEmpty()) {
                try {
                    $chunk = $this->read->read($cancellation);
                } catch (StreamException $exception) {
                    throw new ChannelException(
                        "Reading from the channel failed. Did the context die?",
                        0,
                        $exception,
                    );
                }

                if ($chunk === null) {
                    throw new ChannelException("The channel closed while waiting to receive the next value");
                }

                $this->parser->push($chunk);
            }

            return $this->received->shift();
        } finally {
            async($lock->release(...));
        }
    }

    public function isClosed(): bool
    {
        return $this->read->isClosed() || $this->write->isClosed();
    }

    public function onClose(\Closure $onClose): void
    {
        $this->read->onClose($onClose);
    }
}
<?php declare(strict_types=1);

namespace Amp\ByteStream;

interface ResourceStream
{
    /**
     * References the underlying watcher, so the loop keeps running in case there's an active stream operation.
     *
     * @see EventLoop::reference()
     */
    public function reference(): void;

    /**
     * Unreferences the underlying watcher, so the loop doesn't keep running even if there are active stream operations.
     *
     * @see EventLoop::unreference()
     */
    public function unreference(): void;

    /**
     * @return resource|object|null Stream resource (or object if PHP switches to object-based streams).
     */
    public function getResource();
}
<?php declare(strict_types=1);

namespace Amp\ByteStream\Base64;

use Amp\ByteStream\ReadableStream;
use Amp\ByteStream\ReadableStreamIteratorAggregate;
use Amp\ByteStream\StreamException;
use Amp\Cancellation;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;

/**
 * @implements \IteratorAggregate<int, string>
 */
final class Base64DecodingReadableStream implements ReadableStream, \IteratorAggregate
{
    use ReadableStreamIteratorAggregate;
    use ForbidCloning;
    use ForbidSerialization;

    private string $buffer = '';

    public function __construct(
        private readonly ReadableStream $source,
    ) {
    }

    public function read(?Cancellation $cancellation = null): ?string
    {
        if ($this->source->isClosed()) {
            throw new StreamException('Failed to read stream chunk due to invalid base64 data');
        }

        $chunk = $this->source->read($cancellation);
        if ($chunk === null) {
            if ($this->buffer === '') {
                return null;
            }

            $chunk = \base64_decode($this->buffer, true);
            $this->buffer = '';

            if ($chunk === false) {
                $this->source->close();
                throw new StreamException('Failed to read stream chunk due to invalid base64 data');
            }

            return $chunk;
        }

        $this->buffer .= $chunk;

        $length = \strlen($this->buffer);
        $chunk = \base64_decode(\substr($this->buffer, 0, $length - $length % 4), true);

        if ($chunk === false) {
            $this->source->close();
            $this->buffer = '';

            throw new StreamException('Failed to read stream chunk due to invalid base64 data');
        }

        $this->buffer = \substr($this->buffer, $length - $length % 4);

        return $chunk;
    }

    public function isReadable(): bool
    {
        return $this->source->isReadable();
    }

    public function close(): void
    {
        $this->source->close();
    }

    public function isClosed(): bool
    {
        return $this->source->isClosed();
    }

    public function onClose(\Closure $onClose): void
    {
        $this->source->onClose($onClose);
    }
}
<?php declare(strict_types=1);

namespace Amp\ByteStream\Base64;

use Amp\ByteStream\ReadableStream;
use Amp\ByteStream\ReadableStreamIteratorAggregate;
use Amp\Cancellation;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;

/**
 * @implements \IteratorAggregate<int, string>
 */
final class Base64EncodingReadableStream implements ReadableStream, \IteratorAggregate
{
    use ReadableStreamIteratorAggregate;
    use ForbidCloning;
    use ForbidSerialization;

    private ?string $buffer = '';

    public function __construct(
        private readonly ReadableStream $source,
    ) {
    }

    public function read(?Cancellation $cancellation = null): ?string
    {
        $chunk = $this->source->read($cancellation);
        if ($chunk === null) {
            if ($this->buffer === null) {
                return null;
            }

            $chunk = \base64_encode($this->buffer);
            $this->buffer = null;

            return $chunk;
        }

        $this->buffer .= $chunk;

        $length = \strlen($this->buffer);
        $chunk = \base64_encode(\substr($this->buffer, 0, $length - $length % 3));
        $this->buffer = \substr($this->buffer, $length - $length % 3);

        return $chunk;
    }

    public function isReadable(): bool
    {
        return $this->source->isReadable();
    }

    public function close(): void
    {
        $this->source->close();
    }

    public function isClosed(): bool
    {
        return $this->source->isClosed();
    }

    public function onClose(\Closure $onClose): void
    {
        $this->source->onClose($onClose);
    }
}
<?php declare(strict_types=1);

namespace Amp\ByteStream\Base64;

use Amp\ByteStream\StreamException;
use Amp\ByteStream\WritableStream;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;

final class Base64DecodingWritableStream implements WritableStream
{
    use ForbidCloning;
    use ForbidSerialization;

    private string $buffer = '';

    private int $offset = 0;

    public function __construct(
        private readonly WritableStream $destination,
    ) {
    }

    public function write(string $bytes): void
    {
        $this->buffer .= $bytes;

        $length = \strlen($this->buffer);
        $chunk = \base64_decode(\substr($this->buffer, 0, $length - $length % 4), true);
        if ($chunk === false) {
            throw new StreamException('Invalid base64 near offset ' . $this->offset);
        }

        $this->offset += $length - $length % 4;
        $this->buffer = \substr($this->buffer, $length - $length % 4);

        $this->destination->write($chunk);
    }

    public function end(): void
    {
        $this->offset += \strlen($this->buffer);

        $chunk = \base64_decode($this->buffer, true);
        if ($chunk === false) {
            throw new StreamException('Invalid base64 near offset ' . $this->offset);
        }

        $this->buffer = '';

        $this->destination->write($chunk);
        $this->destination->end();
    }

    public function isWritable(): bool
    {
        return $this->destination->isWritable();
    }

    public function close(): void
    {
        $this->destination->close();
    }

    public function isClosed(): bool
    {
        return $this->destination->isClosed();
    }

    public function onClose(\Closure $onClose): void
    {
        $this->destination->onClose($onClose);
    }
}
<?php declare(strict_types=1);

namespace Amp\ByteStream\Base64;

use Amp\ByteStream\WritableStream;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;

final class Base64EncodingWritableStream implements WritableStream
{
    use ForbidCloning;
    use ForbidSerialization;

    private string $buffer = '';

    public function __construct(
        private readonly WritableStream $destination,
    ) {
    }

    public function write(string $bytes): void
    {
        $this->buffer .= $bytes;

        $length = \strlen($this->buffer);
        $chunk = \base64_encode(\substr($this->buffer, 0, $length - $length % 3));
        $this->buffer = \substr($this->buffer, $length - $length % 3);

        $this->destination->write($chunk);
    }

    public function end(): void
    {
        $chunk = \base64_encode($this->buffer);
        $this->buffer = '';

        $this->destination->write($chunk);
        $this->destination->end();
    }

    public function isWritable(): bool
    {
        return $this->destination->isWritable();
    }

    public function close(): void
    {
        $this->destination->close();
    }

    public function isClosed(): bool
    {
        return $this->destination->isClosed();
    }

    public function onClose(\Closure $onClose): void
    {
        $this->destination->onClose($onClose);
    }
}
<?php declare(strict_types=1);

namespace Amp\ByteStream;

use Amp\Cancellation;
use Amp\Closable;

/**
 * A `ReadableStream` allows reading byte streams in chunks.
 *
 * **Example**
 *
 * ```php
 * function readAll(ReadableStream $source): string {
 *     $buffer = "";
 *
 *     while (null !== $chunk = $source->read()) {
 *         $buffer .= $chunk;
 *     }
 *
 *     return $buffer;
 * }
 * ```
 *
 * @extends \Traversable<int, string>
 */
interface ReadableStream extends Closable, \Traversable
{
    /**
     * Reads data from the stream.
     *
     * @param Cancellation|null $cancellation Cancel the read operation. The state in which the stream will be after
     * a cancelled operation is implementation dependent.
     *
     * @return string|null Returns a string when new data is available or {@code null} if the stream has closed.
     *
     * @throws PendingReadError Thrown if another read operation is still pending.
     * @throws StreamException If the stream contains invalid data, e.g. invalid compression
     */
    public function read(?Cancellation $cancellation = null): ?string;

    /**
     * @return bool A stream may become unreadable if the underlying source is closed or lost.
     */
    public function isReadable(): bool;
}
<?php declare(strict_types=1);

namespace Amp\ByteStream;

final class BufferException extends StreamException
{
    public function __construct(
        private readonly string $buffer,
        string $message,
        int $code = 0,
        ?\Throwable $previous = null
    ) {
        parent::__construct($message, $code, $previous);
    }

    /**
     * @return string The buffered string when the buffer limit was exceeded. Note that the length of this string
     * may exceed the set limit.
     */
    public function getBuffer(): string
    {
        return $this->buffer;
    }
}
<?php declare(strict_types=1);

namespace Amp\ByteStream;

use Amp\Cancellation;
use Amp\DeferredFuture;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;

/**
 * @implements \IteratorAggregate<int, string>
 */
final class ReadableStreamChain implements ReadableStream, \IteratorAggregate
{
    use ReadableStreamIteratorAggregate;
    use ForbidCloning;
    use ForbidSerialization;

    /** @var ReadableStream[] */
    private array $sources;

    private bool $reading = false;

    private readonly DeferredFuture $onClose;

    public function __construct(ReadableStream ...$sources)
    {
        $this->sources = $sources;
        $this->onClose = new DeferredFuture;

        if (empty($this->sources)) {
            $this->close();
        }
    }

    public function read(?Cancellation $cancellation = null): ?string
    {
        if ($this->reading) {
            throw new PendingReadError;
        }

        if (!$this->sources) {
            return null;
        }

        $this->reading = true;

        try {
            while ($this->sources) {
                $chunk = $this->sources[0]->read($cancellation);
                if ($chunk === null) {
                    \array_shift($this->sources);
                    continue;
                }

                return $chunk;
            }

            return null;
        } finally {
            $this->reading = false;
        }
    }

    public function isReadable(): bool
    {
        return !empty($this->sources);
    }

    public function close(): void
    {
        $sources = $this->sources;
        $this->sources = [];

        foreach ($sources as $source) {
            $source->close();
        }

        if (!$this->onClose->isComplete()) {
            $this->onClose->complete();
        }
    }

    public function isClosed(): bool
    {
        return !$this->isReadable();
    }

    public function onClose(\Closure $onClose): void
    {
        $this->onClose->getFuture()->finally($onClose);
    }
}
<?php declare(strict_types=1);

namespace Amp\ByteStream;

use Amp\Cancellation;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;

final class BufferedReader
{
    use ForbidCloning;
    use ForbidSerialization;

    private string $buffer = '';

    private bool $pending = false;

    public function __construct(
        private ReadableStream $stream,
    ) {
    }

    /**
     * @template TString as string|null
     *
     * @param \Closure():TString $read
     */
    private function guard(\Closure $read): mixed
    {
        if ($this->pending) {
            throw new PendingReadError();
        }

        $this->pending = true;

        try {
            return $read();
        } finally {
            $this->pending = false;
        }
    }

    public function isReadable(): bool
    {
        return $this->stream->isReadable() || $this->buffer !== '';
    }

    public function drain(): string
    {
        return $this->guard(function (): string {
            $buffer = $this->buffer;
            $this->buffer = '';
            return $buffer;
        });
    }

    /**
     * @throws StreamException If the implementation of {@see ReadableStream::read()} of the instance given the
     * constructor can throw.
     *
     * @see ReadableStream::read() Identical to this method, returning data from the internal buffer first.
     */
    public function read(?Cancellation $cancellation = null): ?string
    {
        return $this->guard(function () use ($cancellation): ?string {
            if ($this->buffer !== '') {
                $buffer = $this->buffer;
                $this->buffer = '';
                return $buffer;
            }

            return $this->stream->read($cancellation);
        });
    }

    /**
     * @param positive-int $length The number of bytes to read from the stream.
     *
     * @throws StreamException If the implementation of {@see ReadableStream::read()} of the instance given the
     * constructor can throw.
     * @throws BufferException If the stream closes before the given number of bytes are read.
     */
    public function readLength(int $length, ?Cancellation $cancellation = null): string
    {
        /** @psalm-suppress TypeDoesNotContainType */
        if ($length <= 0) {
            throw new \ValueError('The number of bytes to read must be a positive integer');
        }

        return $this->guard(function () use ($length, $cancellation): string {
            while (\strlen($this->buffer) < $length) {
                $chunk = $this->stream->read($cancellation);
                if ($chunk === null) {
                    $buffer = $this->buffer;
                    $this->buffer = '';
                    throw new BufferException(
                        $buffer,
                        'The stream closed before the given number of bytes were read',
                    );
                }
                $this->buffer .= $chunk;
            }

            $buffer = \substr($this->buffer, 0, $length);
            $this->buffer = \substr($this->buffer, $length);
            return $buffer;
        });
    }

    /**
     * @param non-empty-string $delimiter Read from the stream until the given delimiter is found in the stream, at
     * which point all bytes up to the delimiter will be returned (but not the delimiter).
     * @param positive-int $limit
     *
     * @throws StreamException If the implementation of {@see ReadableStream::read()} of the instance given the
     * constructor can throw.
     * @throws BufferException If the stream closes before the delimiter is found in the stream or if the buffer
     * exceeds $limit.
     */
    public function readUntil(string $delimiter, ?Cancellation $cancellation = null, int $limit = \PHP_INT_MAX): string
    {
        $length = \strlen($delimiter);

        if (!$length) {
            throw new \ValueError('The delimiter must be a non-empty string');
        }

        return $this->guard(function () use ($delimiter, $length, $cancellation, $limit): string {
            $position = 0;
            while (($position = \strpos($this->buffer, $delimiter, $position)) === false) {
                $chunk = $this->stream->read($cancellation);
                if ($chunk === null) {
                    $buffer = $this->buffer;
                    $this->buffer = '';
                    throw new BufferException(
                        $buffer,
                        'The stream closed before the delimiter was found in the stream',
                    );
                }

                $position = \max(\strlen($this->buffer) - $length + 1, 0);
                $this->buffer .= $chunk;

                if (\strlen($this->buffer) > $limit) {
                    $buffer = $this->buffer;
                    $this->buffer = '';
                    throw new BufferException($buffer, "Max length of $limit bytes exceeded");
                }
            }

            $buffer = \substr($this->buffer, 0, $position);
            $this->buffer = \substr($this->buffer, $position + $length);
            return $buffer;
        });
    }

    /**
     * @see buffer()
     *
     * @param positive-int $limit
     *
     * @throws BufferException If the $limit given is exceeded.
     * @throws StreamException If the implementation of {@see ReadableStream::read()} of the instance given the
     * constructor can throw.
     */
    public function buffer(?Cancellation $cancellation = null, int $limit = \PHP_INT_MAX): string
    {
        return $this->guard(function () use ($cancellation, $limit): string {
            $length = \strlen($this->buffer);
            $chunks = $this->buffer === '' ? [] : [$this->buffer];
            $this->buffer = '';

            while (null !== $chunk = $this->stream->read($cancellation)) {
                $chunks[] = $chunk;
                $length += \strlen($chunk);
                if ($length > $limit) {
                    throw new BufferException(\implode($chunks), "Max length of $limit bytes exceeded");
                }

                unset($chunk); // free memory
            }

            return \implode($chunks);
        });
    }
}
<?php declare(strict_types=1);

namespace Amp\ByteStream;

use Amp\Closable;

/**
 * A `WritableStream` allows writing data in chunks. Writers can wait on the returned promises to feel the backpressure.
 */
interface WritableStream extends Closable
{
    /**
     * Writes data to the stream.
     *
     * @param string $bytes Bytes to write.
     *
     * @throws ClosedException If the stream has already been closed.
     * @throws StreamException If writing to the stream fails.
     */
    public function write(string $bytes): void;

    /**
     * Marks the stream as no longer writable.
     *
     * Note that this is not the same as forcefully closing the stream. This method waits for all pending writes to
     * complete before closing the stream. Socket streams implementing this interface should only close the writable
     * side of the stream.
     *
     * @throws ClosedException If the stream has already been closed.
     * @throws StreamException If writing to the stream fails.
     */
    public function end(): void;

    /**
     * @return bool A stream may no longer be writable if it is closed or ended using {@see end()}.
     */
    public function isWritable(): bool;
}
<?php declare(strict_types=1);

namespace Amp\ByteStream;

use Amp\DeferredFuture;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Future;
use Revolt\EventLoop;
use Revolt\EventLoop\Suspension;

/**
 * This class provides a tool for efficiently writing to a stream asynchronously. A single fiber is used for all
 * writes to the stream, while each write returns a {@see Future} instead of waiting for each write to complete before
 * returning control to the caller.
 */
final class AsyncWriter
{
    use ForbidCloning;
    use ForbidSerialization;

    private ?WritableStream $destination;

    /** @var \SplQueue<array{DeferredFuture, string|null}> */
    private readonly \SplQueue $writeQueue;

    /** @var Suspension<bool>|null */
    private ?Suspension $suspension = null;

    public function __construct(WritableStream $destination)
    {
        $this->destination = $destination;
        $this->writeQueue = $writeQueue = new \SplQueue;

        $suspension = &$this->suspension;
        EventLoop::queue(static function () use ($writeQueue, $destination, &$suspension): void {
            while ($destination->isWritable()) {
                if ($writeQueue->isEmpty()) {
                    $suspension = EventLoop::getSuspension();
                    if (!$suspension->suspend()) {
                        return;
                    }
                }

                self::dequeue($writeQueue, $destination);
            }
        });
    }

    private static function dequeue(\SplQueue $writeQueue, WritableStream $destination): void
    {
        while (!$writeQueue->isEmpty()) {
            /**
             * @var DeferredFuture $deferredFuture
             * @var string|null $bytes
             */
            [$deferredFuture, $bytes] = $writeQueue->dequeue();

            try {
                if ($bytes !== null) {
                    $destination->write($bytes);
                } else {
                    $destination->end();
                }

                $deferredFuture->complete();
            } catch (\Throwable $exception) {
                $deferredFuture->error($exception);
                while (!$writeQueue->isEmpty()) {
                    [$deferredFuture] = $writeQueue->dequeue();
                    $deferredFuture->error($exception);
                }
                return;
            }
        }
    }

    public function __destruct()
    {
        $this->destination = null;
        $this->suspension?->resume(false);
        $this->suspension = null;
    }

    /**
     * Queues a chunk of data to be written to the stream, returning a {@see Future} that is completed once the data
     * has been written to the stream or errors if it cannot be written to the stream.
     *
     * @return Future<never>
     */
    public function write(string $bytes): Future
    {
        return $this->send($bytes);
    }

    /**
     * Closes the underlying WritableStream once all queued data has been written.
     *
     * @return Future<void>
     */
    public function end(): Future
    {
        return $this->send(null);
    }

    private function send(?string $bytes): Future
    {
        if (!$this->isWritable()) {
            return Future::error(new ClosedException('The destination stream is no longer writable'));
        }

        if ($bytes === null) {
            $this->destination = null;
        }

        $deferredFuture = new DeferredFuture();
        $this->writeQueue->enqueue([$deferredFuture, $bytes]);
        $this->suspension?->resume(true);
        $this->suspension = null;

        return $deferredFuture->getFuture();
    }

    public function isWritable(): bool
    {
        return (bool) $this->destination?->isWritable();
    }
}
<?php declare(strict_types=1);
/** @noinspection PhpComposerExtensionStubsInspection */

namespace Amp\ByteStream\Compression;

use Amp\ByteStream\ClosedException;
use Amp\ByteStream\StreamException;
use Amp\ByteStream\WritableStream;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;

/**
 * Allows decompression of output streams using Zlib.
 */
final class DecompressingWritableStream implements WritableStream
{
    use ForbidCloning;
    use ForbidSerialization;

    private ?\InflateContext $inflateContext;

    /**
     * @param WritableStream $destination Output stream to write the decompressed data to.
     * @param int $encoding Compression encoding to use, see `inflate_init()`.
     * @param array $options Compression options to use, see `inflate_init()`.
     *
     * @see http://php.net/manual/en/function.inflate-init.php
     */
    public function __construct(
        private readonly WritableStream $destination,
        private readonly int $encoding,
        private readonly array $options = []
    ) {
        \set_error_handler(function ($errno, $message) {
            $this->close();

            throw new \Error("Failed initializing inflate context: $message");
        });

        try {
            /** @psalm-suppress InvalidPropertyAssignmentValue */
            $this->inflateContext = \inflate_init($encoding, $options);
        } finally {
            \restore_error_handler();
        }
    }

    public function write(string $bytes): void
    {
        if ($this->inflateContext === null) {
            throw new ClosedException("The stream has already been closed");
        }

        /** @psalm-suppress InvalidArgument */
        $decompressed = \inflate_add($this->inflateContext, $bytes, \ZLIB_SYNC_FLUSH);

        if ($decompressed === false) {
            $this->close();

            throw new StreamException("Failed adding data to inflate context");
        }

        $this->destination->write($decompressed);
    }

    public function end(): void
    {
        if ($this->inflateContext === null) {
            throw new ClosedException("The stream has already been closed");
        }

        /** @psalm-suppress InvalidArgument */
        $decompressed = \inflate_add($this->inflateContext, '', \ZLIB_FINISH);

        if ($decompressed === false) {
            $this->close();

            throw new StreamException("Failed adding data to inflate context");
        }

        $this->inflateContext = null;

        $this->destination->write($decompressed);
        $this->destination->end();
    }

    public function isWritable(): bool
    {
        return $this->inflateContext !== null && $this->destination->isWritable();
    }

    /**
     * Gets the used compression encoding.
     *
     * @return int Encoding specified on construction time.
     */
    public function getEncoding(): int
    {
        return $this->encoding;
    }

    /**
     * Gets the used compression options.
     *
     * @return array Options array passed on construction time.
     */
    public function getOptions(): array
    {
        return $this->options;
    }

    public function close(): void
    {
        $this->destination->close();
    }

    public function isClosed(): bool
    {
        return $this->inflateContext === null || $this->destination->isClosed();
    }

    public function onClose(\Closure $onClose): void
    {
        $this->destination->onClose($onClose);
    }
}
<?php declare(strict_types=1);
/** @noinspection PhpComposerExtensionStubsInspection */

namespace Amp\ByteStream\Compression;

use Amp\ByteStream\ReadableStream;
use Amp\ByteStream\ReadableStreamIteratorAggregate;
use Amp\ByteStream\StreamException;
use Amp\Cancellation;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;

/**
 * Allows decompression of input streams using Zlib.
 *
 * @implements \IteratorAggregate<int, string>
 */
final class DecompressingReadableStream implements ReadableStream, \IteratorAggregate
{
    use ReadableStreamIteratorAggregate;
    use ForbidCloning;
    use ForbidSerialization;

    private ?\InflateContext $inflateContext;

    /**
     * @param ReadableStream $source Input stream to read compressed data from.
     * @param int $encoding Compression algorithm used, see `inflate_init()`.
     * @param array $options Algorithm options, see `inflate_init()`.
     *
     * @see http://php.net/manual/en/function.inflate-init.php
     */
    public function __construct(
        private readonly ReadableStream $source,
        private readonly int $encoding,
        private readonly array $options = [],
    ) {
        \set_error_handler(function ($errno, $message) {
            $this->close();

            throw new \Error("Failed initializing inflate context: $message");
        });

        try {
            /** @psalm-suppress InvalidPropertyAssignmentValue */
            $this->inflateContext = \inflate_init($encoding, $options);
        } finally {
            \restore_error_handler();
        }
    }

    public function close(): void
    {
        $this->source->close();
        $this->inflateContext = null;
    }

    public function read(?Cancellation $cancellation = null): ?string
    {
        if ($this->inflateContext === null) {
            return null;
        }

        $data = $this->source->read($cancellation);

        // Needs a double guard, as stream might have been closed while reading
        /** @psalm-suppress TypeDoesNotContainNull */
        if ($this->inflateContext === null) {
            return null;
        }

        if ($data === null) {
            /** @psalm-suppress InvalidArgument */
            $decompressed = @\inflate_add($this->inflateContext, "", \ZLIB_FINISH);

            if ($decompressed === false) {
                $this->close();

                throw new StreamException("Failed adding data to inflate context");
            }

            $this->close();

            return $decompressed;
        }

        /** @psalm-suppress InvalidArgument */
        $decompressed = @\inflate_add($this->inflateContext, $data, \ZLIB_SYNC_FLUSH);

        if ($decompressed === false) {
            $this->close();

            throw new StreamException("Failed adding data to inflate context");
        }

        return $decompressed;
    }

    public function isReadable(): bool
    {
        return $this->inflateContext !== null && $this->source->isReadable();
    }

    /**
     * Gets the used compression encoding.
     *
     * @return int Encoding specified on construction time.
     */
    public function getEncoding(): int
    {
        return $this->encoding;
    }

    /**
     * Gets the used compression options.
     *
     * @return array Options array passed on construction time.
     */
    public function getOptions(): array
    {
        return $this->options;
    }

    public function isClosed(): bool
    {
        return $this->inflateContext === null || $this->source->isClosed();
    }

    public function onClose(\Closure $onClose): void
    {
        $this->source->onClose($onClose);
    }
}
<?php declare(strict_types=1);
/** @noinspection PhpComposerExtensionStubsInspection */

namespace Amp\ByteStream\Compression;

use Amp\ByteStream\ClosedException;
use Amp\ByteStream\StreamException;
use Amp\ByteStream\WritableStream;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;

/**
 * Allows compression of output streams using Zlib.
 */
final class CompressingWritableStream implements WritableStream
{
    use ForbidCloning;
    use ForbidSerialization;

    private ?\DeflateContext $deflateContext;

    /**
     * @param WritableStream $destination Output stream to write the compressed data to.
     * @param int $encoding Compression encoding to use, see `deflate_init()`.
     * @param array $options Compression options to use, see `deflate_init()`.
     *
     * @see http://php.net/manual/en/function.deflate-init.php
     */
    public function __construct(
        private readonly WritableStream $destination,
        private readonly int $encoding,
        private readonly array $options = []
    ) {
        \set_error_handler(function ($errno, $message) {
            $this->close();

            throw new \Error("Failed initializing deflate context: $message");
        });

        try {
            /** @psalm-suppress InvalidPropertyAssignmentValue */
            $this->deflateContext = \deflate_init($encoding, $options);
        } finally {
            \restore_error_handler();
        }
    }

    public function close(): void
    {
        $this->destination->close();
    }

    public function end(): void
    {
        if ($this->deflateContext === null) {
            throw new ClosedException("The stream has already been closed");
        }

        \set_error_handler(function ($errno, $message) {
            $this->close();

            throw new StreamException("Failed adding data to deflate context: $message");
        });

        try {
            /** @psalm-suppress InvalidArgument */
            $compressed = \deflate_add($this->deflateContext, '', \ZLIB_FINISH);
        } finally {
            \restore_error_handler();
        }

        if ($compressed === false) {
            $this->close();

            throw new StreamException("Failed adding data to deflate context");
        }

        $this->deflateContext = null;

        $this->destination->write($compressed);
        $this->destination->end();
    }

    public function write(string $bytes): void
    {
        if ($this->deflateContext === null) {
            throw new ClosedException("The stream has already been closed");
        }

        \set_error_handler(function ($errno, $message) {
            $this->close();

            throw new StreamException("Failed adding data to deflate context: $message");
        });

        try {
            /** @psalm-suppress InvalidArgument */
            $compressed = \deflate_add($this->deflateContext, $bytes, \ZLIB_SYNC_FLUSH);
        } finally {
            \restore_error_handler();
        }

        if ($compressed === false) {
            $this->close();

            throw new StreamException("Failed adding data to deflate context");
        }

        $this->destination->write($compressed);
    }

    public function isWritable(): bool
    {
        return $this->deflateContext !== null && $this->destination->isWritable();
    }

    /**
     * Gets the used compression encoding.
     *
     * @return int Encoding specified on construction time.
     */
    public function getEncoding(): int
    {
        return $this->encoding;
    }

    /**
     * Gets the used compression options.
     *
     * @return array Options array passed on construction time.
     */
    public function getOptions(): array
    {
        return $this->options;
    }

    public function isClosed(): bool
    {
        return $this->deflateContext === null || $this->destination->isClosed();
    }

    public function onClose(\Closure $onClose): void
    {
        $this->destination->onClose($onClose);
    }
}
<?php declare(strict_types=1);
/** @noinspection PhpComposerExtensionStubsInspection */

namespace Amp\ByteStream\Compression;

use Amp\ByteStream\ReadableStream;
use Amp\ByteStream\ReadableStreamIteratorAggregate;
use Amp\ByteStream\StreamException;
use Amp\Cancellation;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;

/**
 * Allows compression of input streams using Zlib.
 *
 * @implements \IteratorAggregate<int, string>
 */
final class CompressingReadableStream implements ReadableStream, \IteratorAggregate
{
    use ReadableStreamIteratorAggregate;
    use ForbidCloning;
    use ForbidSerialization;

    private ?\DeflateContext $deflateContext;

    /**
     * @param ReadableStream $source Input stream to read data from.
     * @param int $encoding Compression algorithm used, see `deflate_init()`.
     * @param array $options Algorithm options, see `deflate_init()`.
     *
     * @see http://php.net/manual/en/function.deflate-init.php
     */
    public function __construct(
        private readonly ReadableStream $source,
        private readonly int $encoding,
        private readonly array $options = [],
    ) {
        \set_error_handler(function ($errno, $message) {
            $this->close();

            throw new \Error("Failed initializing deflate context: $message");
        });

        try {
            /** @psalm-suppress InvalidPropertyAssignmentValue */
            $this->deflateContext = \deflate_init($encoding, $options);
        } finally {
            \restore_error_handler();
        }
    }

    public function close(): void
    {
        $this->source->close();
        $this->deflateContext = null;
    }

    public function read(?Cancellation $cancellation = null): ?string
    {
        if ($this->deflateContext === null) {
            return null;
        }

        $data = $this->source->read($cancellation);

        // Needs a double guard, as stream might have been closed while reading
        /** @psalm-suppress TypeDoesNotContainNull */
        if ($this->deflateContext === null) {
            return null;
        }

        \set_error_handler(function ($errno, $message) {
            $this->close();

            throw new StreamException("Failed adding data to deflate context: $message");
        });

        try {
            if ($data === null) {
                /** @psalm-suppress InvalidArgument */
                $compressed = \deflate_add($this->deflateContext, "", \ZLIB_FINISH);

                $this->close();
            } else {
                /** @psalm-suppress InvalidArgument */
                $compressed = \deflate_add($this->deflateContext, $data, \ZLIB_SYNC_FLUSH);
            }
        } finally {
            \restore_error_handler();
        }

        if ($compressed === false) {
            $this->close();

            throw new StreamException("Failed adding data to deflate context");
        }

        return $compressed;
    }

    public function isReadable(): bool
    {
        return $this->deflateContext !== null && $this->source->isReadable();
    }

    /**
     * Gets the used compression encoding.
     *
     * @return int Encoding specified on construction time.
     */
    public function getEncoding(): int
    {
        return $this->encoding;
    }

    /**
     * Gets the used compression options.
     *
     * @return array Options array passed on construction time.
     */
    public function getOptions(): array
    {
        return $this->options;
    }

    public function isClosed(): bool
    {
        return $this->deflateContext === null || $this->source->isClosed();
    }

    public function onClose(\Closure $onClose): void
    {
        $this->source->onClose($onClose);
    }
}
<?php declare(strict_types=1);

namespace Amp\ByteStream\Internal;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Parser\Parser;
use Amp\Serialization\NativeSerializer;
use Amp\Serialization\SerializationException;
use Amp\Serialization\Serializer;
use Amp\Sync\ChannelException;
use function Amp\Serialization\encodeUnprintableChars;

/** @internal */
final class ChannelParser extends Parser
{
    use ForbidCloning;
    use ForbidSerialization;

    private const HEADER_LENGTH = 5;

    /**
     * @param \Closure(mixed):void $push
     *
     * @throws ChannelException
     * @throws SerializationException
     */
    private static function parser(\Closure $push, Serializer $serializer): \Generator
    {
        while (true) {
            /** @var string $header */
            $header = yield self::HEADER_LENGTH;
            ['prefix' => $prefix, 'length' => $length] = \unpack("Cprefix/Llength", $header);

            if ($prefix !== 0) {
                $data = $header . yield;
                throw new ChannelException("Invalid packet received: " . encodeUnprintableChars($data));
            }

            $data = $serializer->unserialize(yield $length);

            try {
                $push($data);
            } catch (\Throwable $exception) {
                throw new ChannelException(
                    "Invoking the parser callback failed: " . $exception->getMessage(),
                    0,
                    $exception,
                );
            }
        }
    }

    private readonly Serializer $serializer;

    /**
     * @param \Closure(mixed):void $onMessage Closure invoked when data is parsed.
     */
    public function __construct(\Closure $onMessage, ?Serializer $serializer = null)
    {
        $this->serializer = $serializer ?? new NativeSerializer;
        parent::__construct(self::parser($onMessage, $this->serializer));
    }

    /**
     * @param mixed $data Data to encode to send over a channel.
     *
     * @return string Encoded data that can be parsed by this class.
     *
     * @throws SerializationException
     */
    public function encode(mixed $data): string
    {
        $data = $this->serializer->serialize($data);
        return \pack("CL", 0, \strlen($data)) . $data;
    }
}
<?php declare(strict_types=1);

namespace Amp\ByteStream\Internal;

use Amp\ByteStream\ReadableResourceStream;
use Amp\ByteStream\WritableResourceStream;

/**
 * @internal
 * @param resource $resource Stream resource.
 */
function tryToCreateReadableStreamFromResource($resource): ReadableResourceStream
{
    if (\is_resource($resource) && \get_resource_type($resource) === 'stream') {
        return new ReadableResourceStream($resource);
    }

    $stream = new ReadableResourceStream(\fopen('php://memory', 'rb'));
    $stream->close();

    return $stream;
}

/**
 * @internal
 * @param resource $resource Stream resource.
 */
function tryToCreateWritableStreamFromResource($resource): WritableResourceStream
{
    if (\is_resource($resource) && \get_resource_type($resource) === 'stream') {
        return new WritableResourceStream($resource);
    }

    $stream = new WritableResourceStream(\fopen('php://memory', 'wb'));
    $stream->close();

    return $stream;
}
<?php declare(strict_types=1);

namespace Amp\ByteStream;

use Amp\DeferredFuture;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Pipeline\Queue;

/**
 * @template-implements \IteratorAggregate<int, string>
 */
final class WritableIterableStream implements WritableStream, \IteratorAggregate
{
    use ForbidCloning;
    use ForbidSerialization;

    private readonly Queue $queue;

    /** @var \Traversable<int, string> */
    private readonly iterable $iterable;

    private int $bufferSize;

    private readonly DeferredFuture $onClose;

    public function __construct(int $bufferSize)
    {
        $this->queue = new Queue;
        $this->iterable = $this->queue->iterate();
        $this->bufferSize = $bufferSize;
        $this->onClose = new DeferredFuture;
    }

    public function __destruct()
    {
        $this->close();
    }

    public function close(): void
    {
        if (!$this->queue->isComplete()) {
            $this->queue->complete();
        }

        if ($this->onClose->isComplete()) {
            $this->onClose->complete();
        }
    }

    public function isClosed(): bool
    {
        return !$this->isWritable();
    }

    public function onClose(\Closure $onClose): void
    {
        $this->onClose->getFuture()->finally($onClose);
    }

    public function write(string $bytes): void
    {
        if ($this->queue->isComplete() || $this->queue->isDisposed()) {
            throw new ClosedException('The stream is no longer writable');
        }

        $length = \strlen($bytes);
        $this->bufferSize -= $length;

        $future = $this->queue->pushAsync($bytes)->finally(fn () => $this->bufferSize += $length);

        if ($this->bufferSize < 0) {
            $future->await();
        } else {
            $future->ignore();
        }
    }

    public function end(): void
    {
        if (!$this->queue->isComplete()) {
            $this->queue->complete();
        }
    }

    public function isWritable(): bool
    {
        return !$this->queue->isComplete() && !$this->queue->isDisposed();
    }

    public function getIterator(): \Traversable
    {
        return $this->iterable;
    }
}
<?php declare(strict_types=1);

namespace Amp\ByteStream;

/**
 * Thrown in case a second read operation is attempted while another read operation is still pending.
 */
final class PendingReadError extends \Error
{
    public function __construct(
        string $message = "The previous read operation must complete before read can be called again",
        int $code = 0,
        ?\Throwable $previous = null
    ) {
        parent::__construct($message, $code, $previous);
    }
}
<?php declare(strict_types=1);

namespace Amp\ByteStream;

use Amp\DeferredFuture;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;

final class WritableBuffer implements WritableStream
{
    use ForbidCloning;
    use ForbidSerialization;

    private readonly DeferredFuture $deferredFuture;

    private string $contents = '';

    private bool $closed = false;

    public function __construct()
    {
        $this->deferredFuture = new DeferredFuture;
    }

    public function write(string $bytes): void
    {
        if ($this->closed) {
            throw new ClosedException("The stream has already been closed");
        }

        $this->contents .= $bytes;
    }

    public function end(): void
    {
        if ($this->closed) {
            throw new ClosedException("The stream has already been closed");
        }

        $this->close();
    }

    public function isWritable(): bool
    {
        return !$this->closed;
    }

    public function buffer(): string
    {
        return $this->deferredFuture->getFuture()->await();
    }

    public function close(): void
    {
        if ($this->closed) {
            return;
        }

        $this->closed = true;

        $this->deferredFuture->complete($this->contents);
        $this->contents = '';
    }

    public function isClosed(): bool
    {
        return $this->closed;
    }

    public function onClose(\Closure $onClose): void
    {
        $this->deferredFuture->getFuture()->finally($onClose);
    }
}
<?php declare(strict_types=1);

namespace Amp\ByteStream;

class StreamException extends \Exception
{
}
<?php declare(strict_types=1);

namespace Amp\ByteStream;

use Amp\Cancellation;
use Revolt\EventLoop;

// @codeCoverageIgnoreStart
if (\strlen('â€¦') !== 3) {
    throw new \Error(
        'The mbstring.func_overload ini setting is enabled. It must be disabled to use amphp/byte-stream.'
    );
} // @codeCoverageIgnoreEnd

/** @psalm-suppress PossiblyInvalidArgument */
if (!\defined('STDOUT')) {
    \define('STDOUT', \fopen('php://stdout', 'wb'));
}

/** @psalm-suppress PossiblyInvalidArgument */
if (!\defined('STDERR')) {
    \define('STDERR', \fopen('php://stderr', 'wb'));
}

/**
 * @return int The number of bytes written to the destination.
 */
function pipe(ReadableStream $source, WritableStream $destination, ?Cancellation $cancellation = null): int
{
    $written = 0;

    while (($chunk = $source->read($cancellation)) !== null) {
        $written += \strlen($chunk);
        $destination->write($chunk);
        unset($chunk); // free memory
    }

    return $written;
}

/**
 * @param int $limit Only buffer up to the given number of bytes, throwing {@see BufferException} if exceeded.
 *
 * @return string Entire contents of the InputStream.
 *
 * @throws BufferException Thrown if the maximum number of bytes is exceeded.
 */
function buffer(ReadableStream $source, ?Cancellation $cancellation = null, int $limit = \PHP_INT_MAX): string
{
    $chunks = [];
    $length = 0;

    while (null !== $chunk = $source->read($cancellation)) {
        $chunks[] = $chunk;
        $length += \strlen($chunk);
        if ($length > $limit) {
            throw new BufferException(\implode($chunks), "Buffer length limit of $limit bytes exceeded");
        }
    }

    return \implode($chunks);
}

/**
 * The php://input buffer stream for the process associated with the currently active event loop.
 */
function getInputBufferStream(): ReadableResourceStream
{
    static $map;

    $map ??= new \WeakMap();

    return $map[EventLoop::getDriver()] ??= new ReadableResourceStream(\fopen('php://input', 'rb'));
}

/**
 * The php://output buffer stream for the process associated with the currently active event loop.
 */
function getOutputBufferStream(): WritableResourceStream
{
    static $map;

    $map ??= new \WeakMap();

    return $map[EventLoop::getDriver()] ??= new WritableResourceStream(\fopen('php://output', 'wb'));
}

/**
 * The STDIN stream for the process associated with the currently active event loop.
 */
function getStdin(): ReadableResourceStream
{
    static $map;

    $map ??= new \WeakMap();

    return $map[EventLoop::getDriver()] ??= Internal\tryToCreateReadableStreamFromResource(\STDIN);
}

/**
 * The STDOUT stream for the process associated with the currently active event loop.
 */
function getStdout(): WritableResourceStream
{
    static $map;

    $map ??= new \WeakMap();

    return $map[EventLoop::getDriver()] ??= Internal\tryToCreateWritableStreamFromResource(\STDOUT);
}

/**
 * The STDERR stream for the process associated with the currently active event loop.
 */
function getStderr(): WritableResourceStream
{
    static $map;

    $map ??= new \WeakMap();

    return $map[EventLoop::getDriver()] ??= Internal\tryToCreateWritableStreamFromResource(\STDERR);
}

/**
 * Splits the stream into chunks based on a delimiter.
 *
 * @param non-empty-string $delimiter
 *
 * @return \Traversable<int, string>
 */
function split(ReadableStream $source, string $delimiter, ?Cancellation $cancellation = null): \Traversable
{
    $buffer = '';

    while (null !== $chunk = $source->read($cancellation)) {
        $buffer .= $chunk;

        $split = \explode($delimiter, $buffer);
        $buffer = \array_pop($split);

        yield from $split;
    }

    if ($buffer !== '') {
        yield $buffer;
    }
}

/**
 * Splits the stream into lines.
 *
 * @return \Traversable<int, string>
 */
function splitLines(ReadableStream $source, ?Cancellation $cancellation = null): \Traversable
{
    foreach (split($source, "\n", $cancellation) as $line) {
        yield \rtrim($line, "\r");
    }
}

/**
 * @param int<1, 2147483647> $depth
 *
 * @return \Traversable<int, mixed> Traversable of decoded JSON values
 *
 * @throws \JsonException If JSON parsing fails
 */
function parseLineDelimitedJson(
    ReadableStream $source,
    bool $associative = false,
    int $depth = 512,
    int $flags = 0,
    ?Cancellation $cancellation = null
): \Traversable {
    foreach (splitLines($source, $cancellation) as $line) {
        $line = \trim($line);

        if ($line === '') {
            continue;
        }

        yield \json_decode($line, $associative, $depth, $flags | \JSON_THROW_ON_ERROR);
    }
}
{
    "name": "amphp/byte-stream",
    "homepage": "https://amphp.org/byte-stream",
    "description": "A stream abstraction to make working with non-blocking I/O simple.",
    "support": {
        "issues": "https://github.com/amphp/byte-stream/issues"
    },
    "keywords": [
        "stream",
        "async",
        "non-blocking",
        "amp",
        "amphp",
        "io"
    ],
    "license": "MIT",
    "authors": [
        {
            "name": "Aaron Piotrowski",
            "email": "aaron@trowski.com"
        },
        {
            "name": "Niklas Keller",
            "email": "me@kelunik.com"
        }
    ],
    "require": {
        "php": ">=8.1",
        "amphp/amp": "^3",
        "amphp/pipeline": "^1",
        "amphp/parser": "^1.1",
        "amphp/serialization": "^1",
        "amphp/sync": "^2",
        "revolt/event-loop": "^1 || ^0.2.3"
    },
    "require-dev": {
        "amphp/phpunit-util": "^3",
        "phpunit/phpunit": "^9",
        "amphp/php-cs-fixer-config": "^2",
        "psalm/phar": "5.22.1"
    },
    "autoload": {
        "psr-4": {
            "Amp\\ByteStream\\": "src"
        },
        "files": [
            "src/functions.php",
            "src/Internal/functions.php"
        ]
    },
    "autoload-dev": {
        "psr-4": {
            "Amp\\ByteStream\\": "test"
        }
    }
}
<?php

$config = new Amp\CodeStyle\Config;
$config->getFinder()
    ->in(__DIR__ . '/src')
    ->in(__DIR__ . '/test');

$config->setCacheFile(__DIR__ . '/.php_cs.cache');

return $config;
<?php declare(strict_types=1);

namespace Amp\Serialization;

final class JsonSerializer implements Serializer
{
    private bool $associative;
    private int $encodeOptions;
    private int $decodeOptions;
    private int $depth;

    /**
     * Creates a JSON serializer that decodes objects to associative arrays.
     *
     * @param int $encodeOptions {@see \json_encode()} options parameter.
     * @param int $decodeOptions {@see \json_decode()} options parameter.
     * @param int $depth Maximum recursion depth.
     */
    public static function withAssociativeArrays(int $encodeOptions = 0, int $decodeOptions = 0, int $depth = 512): self
    {
        return new self(true, $encodeOptions, $decodeOptions, $depth);
    }

    /**
     * Creates a JSON serializer that decodes objects to instances of stdClass.
     *
     * @param int $encodeOptions {@see \json_encode()} options parameter.
     * @param int $decodeOptions {@see \json_decode()} options parameter.
     * @param int $depth Maximum recursion depth.
     */
    public static function withObjects(int $encodeOptions = 0, int $decodeOptions = 0, int $depth = 512): self
    {
        return new self(false, $encodeOptions, $decodeOptions, $depth);
    }

    private function __construct(bool $associative, int $encodeOptions = 0, int $decodeOptions = 0, int $depth = 512)
    {
        $this->associative = $associative;
        $this->depth = $depth;

        // We always want to throw on errors.
        $this->encodeOptions = $encodeOptions | \JSON_THROW_ON_ERROR;
        $this->decodeOptions = $decodeOptions | \JSON_THROW_ON_ERROR;
    }

    /**
     * @psalm-suppress InvalidFalsableReturnType $this->encodeOptions always contains JSON_THROW_ON_ERROR.
     */
    #[\Override]
    public function serialize($data): string
    {
        try {
            /** @psalm-suppress ArgumentTypeCoercion, FalsableReturnStatement */
            return \json_encode($data, $this->encodeOptions, $this->depth);
        } catch (\JsonException $e) {
            throw new SerializationException($e->getMessage(), $e->getCode(), $e);
        }
    }

    #[\Override]
    public function unserialize(string $data)
    {
        try {
            /** @psalm-suppress ArgumentTypeCoercion, FalsableReturnStatement */
            return \json_decode($data, $this->associative, $this->depth, $this->decodeOptions);
        } catch (\JsonException $e) {
            throw new SerializationException($e->getMessage(), $e->getCode(), $e);
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\Serialization;

interface Serializer
{
    /**
     * @param mixed $data Serializable PHP value.
     *
     * @throws SerializationException
     */
    public function serialize($data): string;

    /**
     * @return mixed The unserialized data.
     *
     * @throws SerializationException
     */
    public function unserialize(string $data);
}
<?php declare(strict_types=1);

namespace Amp\Serialization;

/**
 * @psalm-suppress ClassMustBeFinal
 */
class SerializationException extends \Exception
{
}
<?php declare(strict_types=1);

namespace Amp\Serialization;

final class PassthroughSerializer implements Serializer
{
    #[\Override]
    public function serialize($data): string
    {
        if (!\is_string($data)) {
            throw new SerializationException('Serializer implementation only allows strings');
        }

        return $data;
    }

    #[\Override]
    public function unserialize(string $data): string
    {
        return $data;
    }
}
<?php declare(strict_types=1);

namespace Amp\Serialization;

final class NativeSerializer implements Serializer
{
    /** @var list<class-string>|null */
    private ?array $allowedClasses;

    /**
     * @param list<class-string>|null $allowedClasses List of allowed class names to be unserialized.
     *  Null for any class.
     */
    public function __construct(?array $allowedClasses = null)
    {
        $this->allowedClasses = $allowedClasses;
    }

    #[\Override]
    public function serialize($data): string
    {
        try {
            return \serialize($data);
        } catch (\Throwable $exception) {
            throw new SerializationException(
                \sprintf('The given data could not be serialized: %s', $exception->getMessage()),
                0,
                $exception
            );
        }
    }

    #[\Override]
    public function unserialize(string $data)
    {
        try {
            $result = \unserialize($data, ['allowed_classes' => $this->allowedClasses ?? true]);

            if ($result === false && $data !== \serialize(false)) {
                throw new SerializationException(
                    'Invalid data provided to unserialize: ' . encodeUnprintableChars($data)
                );
            }
        } catch (\Throwable $exception) {
            throw new SerializationException('Exception thrown when unserializing data', 0, $exception);
        }

        return $result;
    }
}
<?php declare(strict_types=1);

namespace Amp\Serialization;

final class CompressingSerializer implements Serializer
{
    private const FLAG_COMPRESSED = 1;
    private const COMPRESSION_THRESHOLD = 256;

    private Serializer $serializer;

    /** @var \Closure():true */
    private \Closure $errorHandler;

    public function __construct(Serializer $serializer)
    {
        $this->serializer = $serializer;
        $this->errorHandler = static fn () => true;
    }

    #[\Override]
    public function serialize($data): string
    {
        $serializedData = $this->serializer->serialize($data);

        $flags = 0;

        if (\strlen($serializedData) > self::COMPRESSION_THRESHOLD) {
            \set_error_handler($this->errorHandler);
            try {
                $serializedData = \gzdeflate($serializedData, 1);
            } finally {
                \restore_error_handler();
            }

            if ($serializedData === false) {
                $error = \error_get_last();
                throw new SerializationException('Could not compress data: ' . ($error['message'] ?? 'unknown error'));
            }

            $flags |= self::FLAG_COMPRESSED;
        }

        return \chr($flags & 0xff) . $serializedData;
    }

    #[\Override]
    public function unserialize(string $data)
    {
        if ($data === '') {
            throw new SerializationException('Empty string provided');
        }

        $firstByte = \ord($data[0]);
        $data = \substr($data, 1);
        \assert(\is_string($data));

        if ($firstByte & self::FLAG_COMPRESSED) {
            \set_error_handler($this->errorHandler);
            try {
                $data = \gzinflate($data);
            } finally {
                \restore_error_handler();
            }
        }

        if ($data === false) {
            $error = \error_get_last();
            throw new SerializationException('Could not decompress data: ' . ($error['message'] ?? 'unknown error'));
        }

        return $this->serializer->unserialize($data);
    }
}
<?php declare(strict_types=1);

namespace Amp\Serialization;

/**
 * @param string $data Binary data.
 *
 * @return string Unprintable characters encoded as \x##.
 */
function encodeUnprintableChars(string $data): string
{
    $result = \preg_replace_callback("/[^\x20-\x7e]/", function (array $matches): string {
        return "\\x" . \dechex(\ord($matches[0]));
    }, $data);

    // For Psalm.
    \assert(\is_string($result));

    return $result;
}
{
    "name": "amphp/serialization",
    "description": "Serialization tools for IPC and data storage in PHP.",
    "keywords": [
        "asynchronous",
        "async",
        "serialization",
        "serialize"
    ],
    "homepage": "https://github.com/amphp/serialization",
    "license": "MIT",
    "authors": [
        {
            "name": "Aaron Piotrowski",
            "email": "aaron@trowski.com"
        },
        {
            "name": "Niklas Keller",
            "email": "me@kelunik.com"
        }
    ],
    "require": {
        "php": ">=7.4"
    },
    "require-dev": {
        "ext-json": "*",
        "ext-zlib": "*",
        "phpunit/phpunit": "^9",
        "amphp/php-cs-fixer-config": "^2",
        "psalm/phar": "6.16.1"
    },
    "autoload": {
        "psr-4": {
            "Amp\\Serialization\\": "src"
        },
        "files": [
            "src/functions.php"
        ]
    },
    "autoload-dev": {
        "psr-4": {
            "Amp\\Serialization\\Test\\": "test"
        }
    }
}
<?php declare(strict_types=1);

namespace Amp;

use Revolt\EventLoop;

final class CompositeCancellation implements Cancellation
{
    use ForbidCloning;
    use ForbidSerialization;

    /** @var array<int, array{Cancellation, string}> */
    private array $cancellations = [];

    private string $nextId = "a";

    /** @var array<string, \Closure(CancelledException): void> */
    private array $callbacks = [];

    private ?CancelledException $exception = null;

    public function __construct(Cancellation ...$cancellations)
    {
        $thatException = &$this->exception;
        $thatCallbacks = &$this->callbacks;
        $thatCancellations = &$this->cancellations;
        $onCancel = static function (CancelledException $exception) use (
            &$thatException,
            &$thatCallbacks,
            &$thatCancellations,
        ): void {
            if ($thatException) {
                return;
            }

            $thatException = $exception;

            foreach ($thatCancellations as [$cancellation, $id]) {
                $cancellation->unsubscribe($id);
            }

            $thatCancellations = [];

            foreach ($thatCallbacks as $callback) {
                EventLoop::queue($callback, $exception);
            }

            $thatCallbacks = [];
        };

        foreach ($cancellations as $cancellation) {
            $id = $cancellation->subscribe($onCancel);
            $this->cancellations[] = [$cancellation, $id];
        }
    }

    public function __destruct()
    {
        foreach ($this->cancellations as [$cancellation, $id]) {
            $cancellation->unsubscribe($id);
        }

        // The reference created in the constructor causes this property to persist beyond the life of this object,
        // so explicitly removing references will speed up garbage collection.
        $this->cancellations = [];
    }

    #[\Override]
    public function subscribe(\Closure $callback): string
    {
        $id = $this->nextId;
        \PHP_VERSION_ID >= 80300 ? $this->nextId = \str_increment($this->nextId) : ++$this->nextId;

        if ($this->exception) {
            EventLoop::queue($callback, $this->exception);
        } else {
            $this->callbacks[$id] = $callback;
        }

        return $id;
    }

    #[\Override]
    public function unsubscribe(string $id): void
    {
        unset($this->callbacks[$id]);
    }

    #[\Override]
    public function isRequested(): bool
    {
        if ($this->exception) {
            return true;
        }

        // Check each cancellation directly, as the callback setting $this->exception is invoked asynchronously.
        foreach ($this->cancellations as [$cancellation]) {
            if ($cancellation->isRequested()) {
                return true;
            }
        }

        return false;
    }

    #[\Override]
    public function throwIfRequested(): void
    {
        if ($this->exception) {
            throw $this->exception;
        }

        // Check each cancellation directly, as the callback setting $this->exception is invoked asynchronously.
        foreach ($this->cancellations as [$cancellation]) {
            $cancellation->throwIfRequested();
        }
    }
}
<?php declare(strict_types=1);

namespace Amp;

trait ForbidSerialization
{
    final public function __serialize(): never
    {
        throw new \Error(__CLASS__ . ' does not support serialization');
    }

    /**
     * @psalm-suppress UnusedParam $data required for __unserialize() prototype.
     */
    final public function __unserialize(array $data): never
    {
        throw new \Error(__CLASS__ . ' does not support deserialization');
    }
}
<?php declare(strict_types=1);

namespace Amp;

/**
 * A deferred cancellation provides a mechanism to cancel operations dynamically.
 *
 * Cancellation of operation works by creating a deferred cancellation and passing the corresponding cancellation when
 * starting the operation. To cancel the operation, invoke `DeferredCancellation::cancel()`.
 *
 * Any operation can decide what to do on a cancellation request, it has "don't care" semantics. An operation SHOULD be
 * aborted, but MAY continue. Example: A DNS client might continue to receive and cache the response, as the query has
 * been sent anyway. An HTTP client would usually close a connection, but might not do so in case a response is close to
 * be fully received to reuse the connection.
 *
 * **Example**
 *
 * ```php
 * $deferredCancellation = new DeferredCancellation;
 * $cancellation = $deferredCancellation->getCancellation();
 *
 * $response = $httpClient->request("https://example.com/pipeline", $cancellation);
 * $responseBody = $response->getBody();
 *
 * while (null !== $chunk = $response->read()) {
 *     // consume $chunk
 *
 *     if ($noLongerInterested) {
 *         $deferredCancellation->cancel();
 *         break;
 *     }
 * }
 * ```
 *
 * @see Cancellation
 * @see CancelledException
 */
final class DeferredCancellation
{
    use ForbidCloning;
    use ForbidSerialization;

    private readonly Internal\Cancellable $source;

    private readonly Cancellation $cancellation;

    public function __construct()
    {
        $this->source = new Internal\Cancellable();
        $this->cancellation = new Internal\WrappedCancellation($this->source);
    }

    public function __destruct()
    {
        $this->source->cancel();
    }

    public function getCancellation(): Cancellation
    {
        return $this->cancellation;
    }

    public function isCancelled(): bool
    {
        return $this->source->isRequested();
    }

    /**
     * @param \Throwable|null $previous Exception to be used as the previous exception to CancelledException.
     */
    public function cancel(?\Throwable $previous = null): void
    {
        $this->source->cancel($previous);
    }
}
<?php declare(strict_types=1);

namespace Amp;

use Amp\Internal\FutureIterator;
use Amp\Internal\FutureState;
use Revolt\EventLoop;

/**
 * @template-covariant T
 */
final class Future
{
    use ForbidCloning;
    use ForbidSerialization;

    /**
     * Iterate over the given futures in completion order.
     *
     * @template Tk of array-key
     * @template Tv
     *
     * @param iterable<Tk, Future<Tv>> $futures
     * @param Cancellation|null $cancellation Optional cancellation.
     *
     * @return iterable<Tk, Future<Tv>>
     */
    public static function iterate(iterable $futures, ?Cancellation $cancellation = null): iterable
    {
        $iterator = new FutureIterator($cancellation);

        // Directly iterate in case of an array, because there can't be suspensions during iteration
        if (\is_array($futures)) {
            foreach ($futures as $key => $future) {
                if (!$future instanceof self) {
                    throw new \TypeError('Array must only contain instances of ' . self::class);
                }
                $iterator->enqueue($future->state, $key, $future);
            }
            $iterator->complete();
        } else {
            // Hold the iterator through a weak reference so the coroutine below stops consuming the iterable once the
            // consumer of the returned generator no longer references it.
            $ref = \WeakReference::create($iterator);

            // Use separate fiber for iteration over non-array, because not all items might be immediately available
            // while other futures are already completed.
            EventLoop::queue(static function () use ($futures, $ref): void {
                $enqueue = static function (Future $future, int|string $key) use ($ref): bool {
                    $iterator = $ref->get();
                    $iterator?->enqueue($future->state, $key, $future);

                    return $iterator !== null;
                };

                try {
                    foreach ($futures as $key => $future) {
                        if (!$future instanceof self) {
                            throw new \TypeError('Iterable must only provide instances of ' . self::class);
                        }

                        if (!$enqueue($future, $key)) {
                            return;
                        }
                    }

                    $ref->get()?->complete();
                } catch (\Throwable $exception) {
                    $ref->get()?->error($exception);
                }
            });
        }

        while ($item = $iterator->consume()) {
            [$key, $future] = $item;
            yield $key => $future;
        }
    }

    /**
     * @template Tv
     *
     * @param Tv $value
     *
     * @return Future<Tv>
     */
    public static function complete(mixed $value = null): self
    {
        $state = new FutureState();
        $state->complete($value);

        return new self($state);
    }

    /**
     * @return Future<never>
     */
    public static function error(\Throwable $throwable): self
    {
        /** @var FutureState<never> $state */
        $state = new FutureState();
        $state->error($throwable);

        return new self($state);
    }

    /** @var FutureState<T> */
    private readonly FutureState $state;

    /**
     * @param FutureState<T> $state
     *
     * @internal Use {@see DeferredFuture} or {@see async()} to create and resolve a Future.
     */
    public function __construct(FutureState $state)
    {
        $this->state = $state;
    }

    /**
     * @return bool True if the operation has completed.
     */
    public function isComplete(): bool
    {
        return $this->state->isComplete();
    }

    /**
     * Do not forward unhandled errors to the event loop handler.
     *
     * @return Future<T>
     */
    public function ignore(): self
    {
        $this->state->ignore();

        return $this;
    }

    /**
     * Attaches a callback that is invoked if this future completes. The returned future is completed with the return
     * value of the callback, or errors with an exception thrown from the callback.
     *
     * @psalm-suppress InvalidTemplateParam
     *
     * @template Tr
     *
     * @param \Closure(T):Tr $map
     *
     * @return Future<Tr>
     */
    public function map(\Closure $map): self
    {
        $state = new FutureState();

        $this->state->subscribe(static function (?\Throwable $error, mixed $value) use ($state, $map): void {
            if ($error) {
                $state->error($error);
                return;
            }

            try {
                /** @var T $value */
                $state->complete($map($value));
            } catch (\Throwable $exception) {
                $state->error($exception);
            }
        });

        return new self($state);
    }

    /**
     * Attaches a callback that is invoked if this future errors. The returned future is completed with the return
     * value of the callback, or errors with an exception thrown from the callback.
     *
     * @template Tr
     *
     * @param \Closure(\Throwable):Tr $catch
     *
     * @return Future<Tr>
     */
    public function catch(\Closure $catch): self
    {
        $state = new FutureState();

        $this->state->subscribe(static function (?\Throwable $error, mixed $value) use ($state, $catch): void {
            if (!$error) {
                $state->complete($value);
                return;
            }

            try {
                $state->complete($catch($error));
            } catch (\Throwable $exception) {
                $state->error($exception);
            }
        });

        return new self($state);
    }

    /**
     * Attaches a callback that is always invoked when the future is completed. The returned future resolves with the
     * same value as this future once the callback has finished execution. If the callback throws, the returned future
     * will error with the thrown exception.
     *
     * @param \Closure():void $finally
     *
     * @return Future<T>
     */
    public function finally(\Closure $finally): self
    {
        $state = new FutureState();

        $this->state->subscribe(static function (?\Throwable $error, mixed $value) use ($state, $finally): void {
            try {
                $finally();

                if ($error) {
                    $state->error($error);
                } else {
                    $state->complete($value);
                }
            } catch (\Throwable $exception) {
                $state->error($exception);
            }
        });

        return new self($state);
    }

    /**
     * Awaits the operation to complete.
     *
     * Throws an exception if the operation fails.
     *
     * @return T
     */
    public function await(?Cancellation $cancellation = null): mixed
    {
        $suspension = EventLoop::getSuspension();

        $callbackId = $this->state->subscribe(static function (?\Throwable $error, mixed $value) use (
            $suspension,
        ): void {
            if ($error) {
                $suspension->throw($error);
            } else {
                $suspension->resume($value);
            }
        });

        $state = $this->state;
        $cancellationId = $cancellation?->subscribe(static function (\Throwable $reason) use (
            $callbackId,
            $suspension,
            $state,
        ): void {
            $state->unsubscribe($callbackId);
            if (!$state->isComplete()) { // Resume has already been scheduled if complete.
                $suspension->throw($reason);
            }
        });

        try {
            return $suspension->suspend();
        } finally {
            /** @psalm-suppress PossiblyNullArgument $cancellationId will not be null if $cancellation is not null. */
            $cancellation?->unsubscribe($cancellationId);
        }
    }
}
<?php declare(strict_types=1);

namespace Amp;

use Revolt\EventLoop;

/**
 * This object invokes the given callback within a new coroutine every $interval seconds until either the
 * {@see self::disable()} method is called or the object is destroyed.
 */
final class Interval
{
    use ForbidCloning;
    use ForbidSerialization;

    private readonly string $callbackId;

    /**
     * @param float $interval Invoke the function every $interval seconds.
     * @param \Closure():void $closure Use {@see weakClosure()} to avoid a circular reference if storing this object
     *      as a property of another object.
     * @param bool $reference If false, unreference the underlying event-loop callback.
     */
    public function __construct(float $interval, \Closure $closure, bool $reference = true)
    {
        $this->callbackId = EventLoop::repeat($interval, $closure);

        if (!$reference) {
            EventLoop::unreference($this->callbackId);
        }
    }

    public function __destruct()
    {
        EventLoop::cancel($this->callbackId);
    }

    /**
     * @return bool True if the internal event-loop callback is referenced.
     */
    public function isReferenced(): bool
    {
        return EventLoop::isReferenced($this->callbackId);
    }

    /**
     * References the internal event-loop callback, keeping the loop running while the repeat loop is enabled.
     *
     * @return $this
     */
    public function reference(): self
    {
        EventLoop::reference($this->callbackId);

        return $this;
    }

    /**
     * Unreferences the internal event-loop callback, allowing the loop to stop while the repeat loop is enabled.
     *
     * @return $this
     */
    public function unreference(): self
    {
        EventLoop::unreference($this->callbackId);

        return $this;
    }

    /**
     * @return bool True if the repeating timer is enabled.
     */
    public function isEnabled(): bool
    {
        return EventLoop::isEnabled($this->callbackId);
    }

    /**
     * Restart the repeating timer if previously stopped with {@see self::disable()}.
     *
     * @return $this
     */
    public function enable(): self
    {
        EventLoop::enable($this->callbackId);

        return $this;
    }

    /**
     * Stop the repeating timer. Restart it with {@see self::enable()}.
     *
     * @return $this
     */
    public function disable(): self
    {
        EventLoop::disable($this->callbackId);

        return $this;
    }
}
<?php declare(strict_types=1);

namespace Amp;

/**
 * A NullCancellation can be used to avoid conditionals to check whether a cancellation has been provided.
 *
 * Instead of writing
 *
 * ```php
 * if ($cancellation) {
 *     $cancellation->throwIfRequested();
 * }
 * ```
 *
 * potentially multiple times, it allows writing
 *
 * ```php
 * $cancellation = $cancellation ?? new NullCancellation;
 *
 * // ...
 *
 * $cancellation->throwIfRequested();
 * ```
 *
 * instead.
 */
final class NullCancellation implements Cancellation
{
    use ForbidCloning;
    use ForbidSerialization;

    #[\Override]
    public function subscribe(\Closure $callback): string
    {
        return "null-cancellation";
    }

    #[\Override]
    public function unsubscribe(string $id): void
    {
        // nothing to do
    }

    #[\Override]
    public function isRequested(): bool
    {
        return false;
    }

    #[\Override]
    public function throwIfRequested(): void
    {
        // nothing to do
    }
}
<?php declare(strict_types=1);

namespace Amp;

/**
 * Will be thrown in case an operation is cancelled.
 *
 * @see Cancellation
 * @see DeferredCancellation
 */
class CancelledException extends \Exception
{
    public function __construct(?\Throwable $previous = null)
    {
        parent::__construct("The operation was cancelled", 0, $previous);
    }
}
<?php declare(strict_types=1);

namespace Amp;

/**
 * Used as the previous exception to {@see CancelledException} when a {@see TimeoutCancellation} expires.
 *
 * @see TimeoutCancellation
 */
class TimeoutException extends \Exception
{
    /**
     * @param string $message Exception message.
     */
    public function __construct(string $message = "Operation timed out")
    {
        parent::__construct($message);
    }
}
<?php declare(strict_types=1);

namespace Amp\Future;

/**
 * Will be thrown to the event loop error handler in case a future exception is not handled.
 */
final class UnhandledFutureError extends \Error
{
    public function __construct(\Throwable $previous, ?string $origin = null)
    {
        $message = 'Unhandled future: ' . $previous::class . ': "' . $previous->getMessage()
            . '"; Await the Future with Future::await() before the future is destroyed or use '
            . 'Future::ignore() to suppress this exception.';

        if ($origin !== null) {
            $message .= ' The future has been created at ' . $origin;
        } else {
            $message .= ' Enable assertions and set AMP_DEBUG=true in the process environment to track its origin.';
        }

        parent::__construct($message, 0, $previous);
    }
}
<?php declare(strict_types=1);

namespace Amp\Future;

use Amp\Cancellation;
use Amp\CompositeException;
use Amp\CompositeLengthException;
use Amp\Future;

/**
 * Unwraps the first completed future.
 *
 * If you want the first future completed without an error, use {@see awaitAny()} instead.
 *
 * @template T
 *
 * @param iterable<Future<T>> $futures
 * @param Cancellation|null $cancellation Optional cancellation.
 *
 * @return T
 *
 * @throws CompositeLengthException If {@code $futures} is empty.
 */
function awaitFirst(iterable $futures, ?Cancellation $cancellation = null): mixed
{
    foreach (Future::iterate($futures, $cancellation) as $first) {
        return $first->await();
    }

    throw new CompositeLengthException('Argument #1 ($futures) is empty');
}

/**
 * Awaits the first successfully completed future, ignoring errors.
 *
 * If you want the first future completed, successful or not, use {@see awaitFirst()} instead.
 *
 * @template Tk of array-key
 * @template Tv
 *
 * @param iterable<Tk, Future<Tv>> $futures
 * @param Cancellation|null $cancellation Optional cancellation.
 *
 * @return Tv
 *
 * @throws CompositeException If all futures errored.
 * @throws CompositeLengthException If {@code $futures} is empty.
 */
function awaitAny(iterable $futures, ?Cancellation $cancellation = null): mixed
{
    $result = awaitAnyN(1, $futures, $cancellation);
    return $result[\array_key_first($result)];
}

/**
 * Awaits the first N successfully completed futures, ignoring errors.
 *
 * @template Tk of array-key
 * @template Tv
 *
 * @param positive-int $count
 * @param iterable<Tk, Future<Tv>> $futures
 * @param Cancellation|null $cancellation Optional cancellation.
 *
 * @return non-empty-array<Tk, Tv>
 *
 * @throws CompositeException If too many futures errored.
 * @throws CompositeLengthException If {@code $futures} is empty.
 */
function awaitAnyN(int $count, iterable $futures, ?Cancellation $cancellation = null): array
{
    if ($count <= 0) {
        throw new \ValueError('Argument #1 ($count) must be greater than 0, got ' . $count);
    }

    $values = [];
    $errors = [];

    foreach (Future::iterate($futures, $cancellation) as $index => $future) {
        try {
            $values[$index] = $future->await();
            if (\count($values) === $count) {
                return $values;
            }
        } catch (\Throwable $throwable) {
            $errors[$index] = $throwable;
        }
    }

    if (\count($values) + \count($errors) < $count) {
        throw new CompositeLengthException('Argument #2 ($futures) contains too few futures to satisfy the required count of ' . $count);
    }

    /**
     * @var non-empty-array<Tk, \Throwable> $errors
     */
    throw new CompositeException($errors);
}

/**
 * Awaits all futures to complete or error.
 *
 * This awaits all futures without aborting on first error (unlike {@see await()}).
 *
 * @template Tk of array-key
 * @template Tv
 *
 * @param iterable<Tk, Future<Tv>> $futures
 * @param Cancellation|null $cancellation Optional cancellation.
 *
 * @return array{array<Tk, \Throwable>, array<Tk, Tv>}
 */
function awaitAll(iterable $futures, ?Cancellation $cancellation = null): array
{
    $values = [];
    $errors = [];

    foreach (Future::iterate($futures, $cancellation) as $index => $future) {
        try {
            $values[$index] = $future->await();
        } catch (\Throwable $throwable) {
            $errors[$index] = $throwable;
        }
    }

    return [$errors, $values];
}

/**
 * Awaits all futures to complete or aborts if any errors.
 *
 * The returned array keys will be in the order the futures resolved, not in the order given by the iterable.
 * Sort the array after completion if necessary.
 *
 * This is equivalent to awaiting all futures in a loop, except that it aborts as soon as one of the futures errors
 * instead of relying on the order in the iterable and awaiting the futures sequentially.
 *
 * @template Tk of array-key
 * @template Tv
 *
 * @param iterable<Tk, Future<Tv>> $futures
 * @param Cancellation|null $cancellation Optional cancellation.
 *
 * @return array<Tk, Tv> Unwrapped values with the order preserved.
 */
function await(iterable $futures, ?Cancellation $cancellation = null): array
{
    $values = [];

    // Future::iterate() to throw the first error based on completion order instead of argument order
    foreach (Future::iterate($futures, $cancellation) as $index => $future) {
        $values[$index] = $future->await();
    }

    /** @var array<Tk, Tv> */
    return $values;
}
<?php declare(strict_types=1);

namespace Amp;

/**
 * @template T
 */
final class DeferredFuture
{
    use ForbidCloning;
    use ForbidSerialization;

    /** @var Internal\FutureState<T> */
    private readonly Internal\FutureState $state;

    /** @var Future<T> */
    private readonly Future $future;

    public function __construct()
    {
        $this->state = new Internal\FutureState();
        $this->future = new Future($this->state);
    }

    /**
     * Completes the operation with a result value.
     *
     * @param T $value Result of the operation.
     */
    public function complete(mixed $value = null): void
    {
        $this->state->complete($value);
    }

    /**
     * Marks the operation as failed.
     *
     * @param \Throwable $throwable Throwable to indicate the error.
     */
    public function error(\Throwable $throwable): void
    {
        $this->state->error($throwable);
    }

    /**
     * @return bool True if the operation has completed.
     */
    public function isComplete(): bool
    {
        return $this->state->isComplete();
    }

    /**
     * @return Future<T> The future associated with this Deferred.
     */
    public function getFuture(): Future
    {
        return $this->future;
    }
}
<?php declare(strict_types=1);

namespace Amp;

/**
 * Used as the previous exception to {@see CancelledException} when a {@see SignalCancellation} is triggered.
 *
 * @see SignalCancellation
 */
class SignalException extends \Exception
{
    /**
     * @param string $message Exception message.
     */
    public function __construct(string $message = "Operation cancelled by signal")
    {
        parent::__construct($message);
    }
}
<?php declare(strict_types=1);

namespace Amp;

final class CompositeException extends \Exception
{
    /**
     * @var non-empty-array<array-key, \Throwable>
     */
    private array $reasons;

    /**
     * @param non-empty-array<array-key, \Throwable> $reasons Array of exceptions.
     * @param string|null $message Exception message, defaults to message generated from passed exceptions.
     *
     * @psalm-assert non-empty-array<array-key, \Throwable> $reasons
     */
    public function __construct(array $reasons, ?string $message = null)
    {
        parent::__construct($message ?? $this->generateMessage($reasons));

        $this->reasons = $reasons;
    }

    /**
     * @return non-empty-array<array-key, \Throwable>
     */
    public function getReasons(): array
    {
        return $this->reasons;
    }

    /**
     * @param non-empty-array<array-key, \Throwable> $reasons
     */
    private function generateMessage(array $reasons): string
    {
        $message = \sprintf(
            'Multiple exceptions encountered (%d); use "%s::getReasons()" to retrieve the array of exceptions thrown:',
            \count($reasons),
            self::class
        );

        foreach ($reasons as $reason) {
            $message .= \PHP_EOL . \PHP_EOL . \get_class($reason);

            if ($reason->getMessage() !== '') {
                $message .= ': ' . $reason->getMessage();
            }
        }

        return $message;
    }
}
<?php declare(strict_types=1);

namespace Amp;

/**
 * Cancellations are simple objects that allow registering handlers to subscribe to cancellation requests.
 */
interface Cancellation
{
    /**
     * Subscribes a new handler to be invoked on a cancellation request.
     *
     * This handler might be invoked immediately in case the cancellation has already been requested. Any unhandled
     * exceptions will be thrown into the event loop.
     *
     * @param \Closure(CancelledException) $callback Callback to be invoked on a cancellation request. Will receive a
     * `CancelledException` as first argument that may be used to fail the operation.
     *
     * @return string Identifier that can be used to cancel the subscription.
     */
    public function subscribe(\Closure $callback): string;

    /**
     * Unsubscribes a previously registered handler.
     *
     * The handler will no longer be called as long as this method isn't invoked from a subscribed callback.
     */
    public function unsubscribe(string $id): void;

    /**
     * Returns whether cancellation has been requested yet.
     */
    public function isRequested(): bool;

    /**
     * Throws the `CancelledException` if cancellation has been requested, otherwise does nothing.
     *
     * @throws CancelledException
     */
    public function throwIfRequested(): void;
}
<?php declare(strict_types=1);

namespace Amp;

use Revolt\EventLoop;

/**
 * A SignalCancellation automatically requests cancellation when a given signal is received.
 */
final class SignalCancellation implements Cancellation
{
    use ForbidCloning;
    use ForbidSerialization;

    /** @var list<string> */
    private readonly array $callbackIds;

    private readonly Cancellation $cancellation;

    /**
     * @param int|int[] $signals Signal number or array of signal numbers.
     * @param string $message Message for SignalException. Default is "Operation cancelled by signal".
     */
    public function __construct(int|array $signals, string $message = "Operation cancelled by signal")
    {
        if (\is_int($signals)) {
            $signals = [$signals];
        }

        $this->cancellation = $source = new Internal\Cancellable();

        \assert((bool) ($trace = \debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS)));

        $callbackIds = [];

        $callback = static function () use (&$callbackIds, &$trace, $source, $message): void {
            foreach ($callbackIds as $callbackId) {
                EventLoop::cancel($callbackId);
            }

            if ($trace ?? false) {
                $message .= \sprintf("\r\n%s was created here: %s", self::class, Internal\formatStacktrace($trace));
            } else {
                $message .= \sprintf(" (Enable assertions for a backtrace of the %s creation)", self::class);
            }

            $source->cancel(new SignalException($message));
        };

        foreach ($signals as $signal) {
            $callbackIds[] = EventLoop::unreference(EventLoop::onSignal($signal, $callback));
        }

        $this->callbackIds = $callbackIds;
    }

    /**
     * Cancels the delay watcher.
     */
    public function __destruct()
    {
        foreach ($this->callbackIds as $watcher) {
            EventLoop::cancel($watcher);
        }
    }

    #[\Override]
    public function subscribe(\Closure $callback): string
    {
        return $this->cancellation->subscribe($callback);
    }

    #[\Override]
    public function unsubscribe(string $id): void
    {
        $this->cancellation->unsubscribe($id);
    }

    #[\Override]
    public function isRequested(): bool
    {
        return $this->cancellation->isRequested();
    }

    #[\Override]
    public function throwIfRequested(): void
    {
        $this->cancellation->throwIfRequested();
    }
}
<?php declare(strict_types=1);

namespace Amp\Internal;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Future;
use Amp\Future\UnhandledFutureError;
use Revolt\EventLoop;

/**
 * @internal
 *
 * @template T
 */
final class FutureState
{
    use ForbidCloning;
    use ForbidSerialization;

    // Static so they can be used as array keys
    private static string $nextId = 'a';

    private bool $complete = false;

    private bool $handled = false;

    /**
     * @var array<string, \Closure(?\Throwable, ?T, string): void>
     */
    private array $callbacks = [];

    /**
     * @var T|null
     */
    private mixed $result = null;

    private ?\Throwable $throwable = null;

    private ?string $origin = null;

    public function __construct()
    {
        \assert((function () {
            if (isDebugEnabled()) {
                $trace = \debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS);
                \array_shift($trace); // remove current closure
                $this->origin = formatStacktrace($trace);
            }

            return true;
        })());
    }

    public function __destruct()
    {
        if ($this->throwable && !$this->handled) {
            $throwable = new UnhandledFutureError($this->throwable, $this->origin);
            EventLoop::queue(static fn () => throw $throwable);
        }
    }

    /**
     * Registers a callback to be notified once the operation is complete or errored.
     *
     * The callback is invoked directly from the event loop context, so suspension within the callback is not possible.
     *
     * @param \Closure(?\Throwable, ?T, string): void $callback Callback invoked on error / successful completion of
     * the future.
     *
     * @return string Identifier that can be used to cancel interest for this future.
     */
    public function subscribe(\Closure $callback): string
    {
        $id = self::$nextId;
        \PHP_VERSION_ID >= 80300 ? self::$nextId = \str_increment(self::$nextId) : ++self::$nextId;

        $this->handled = true; // Even if unsubscribed later, consider the future handled.

        if ($this->complete) {
            EventLoop::queue($callback, $this->throwable, $this->result, $id);
        } else {
            $this->callbacks[$id] = $callback;
        }

        return $id;
    }

    /**
     * Cancels a subscription.
     *
     * Cancellations are advisory only. The callback might still be called if it is already queued for execution.
     *
     * @param string $id Identifier returned from subscribe()
     */
    public function unsubscribe(string $id): void
    {
        unset($this->callbacks[$id]);
    }

    /**
     * Completes the operation with a result value.
     *
     * @param T $result Result of the operation.
     */
    public function complete(mixed $result): void
    {
        if ($this->complete) {
            throw new \Error('Operation is no longer pending');
        }

        if ($result instanceof Future) {
            throw new \Error('Cannot complete with an instance of ' . Future::class);
        }

        $this->result = $result;
        $this->invokeCallbacks();
    }

    /**
     * Marks the operation as failed.
     *
     * @param \Throwable $throwable Throwable to indicate the error.
     */
    public function error(\Throwable $throwable): void
    {
        if ($this->complete) {
            throw new \Error('Operation is no longer pending');
        }

        $this->throwable = $throwable;
        $this->invokeCallbacks();
    }

    /**
     * @return bool True if the operation has completed.
     */
    public function isComplete(): bool
    {
        return $this->complete;
    }

    /**
     * Suppress the exception thrown to the loop error handler if and operation error is not handled by a callback.
     */
    public function ignore(): void
    {
        $this->handled = true;
    }

    private function invokeCallbacks(): void
    {
        $this->complete = true;

        foreach ($this->callbacks as $id => $callback) {
            EventLoop::queue($callback, $this->throwable, $this->result, $id);
        }

        $this->callbacks = [];
    }
}
<?php declare(strict_types=1);

namespace Amp\Internal;

use Amp\Cancellation;
use Amp\CancelledException;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Revolt\EventLoop;

/**
 * Cancellation with public cancellation method.
 *
 * @internal
 */
final class Cancellable implements Cancellation
{
    use ForbidCloning;
    use ForbidSerialization;

    private string $nextId = "a";

    /** @var \Closure[] */
    private array $callbacks = [];

    private ?CancelledException $exception = null;

    private ?\Throwable $previous = null;

    private bool $requested = false;

    public function cancel(?\Throwable $previous = null): void
    {
        if ($this->requested) {
            return;
        }

        $this->requested = true;
        $this->previous = $previous;

        $callbacks = $this->callbacks;
        $this->callbacks = [];

        if (empty($callbacks)) {
            return;
        }

        $exception = $this->getException();

        foreach ($callbacks as $callback) {
            EventLoop::queue($callback, $exception);
        }
    }

    private function getException(): CancelledException
    {
        return $this->exception ??= new CancelledException($this->previous);
    }

    #[\Override]
    public function subscribe(\Closure $callback): string
    {
        $id = $this->nextId;
        \PHP_VERSION_ID >= 80300 ? $this->nextId = \str_increment($this->nextId) : ++$this->nextId;

        if ($this->requested) {
            $exception = $this->getException();
            EventLoop::queue($callback, $exception);
        } else {
            $this->callbacks[$id] = $callback;
        }

        return $id;
    }

    #[\Override]
    public function unsubscribe(string $id): void
    {
        unset($this->callbacks[$id]);
    }

    #[\Override]
    public function isRequested(): bool
    {
        return $this->requested;
    }

    #[\Override]
    public function throwIfRequested(): void
    {
        if ($this->requested) {
            throw $this->getException();
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\Internal;

use Amp\Cancellation;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Future;
use Amp\NullCancellation;
use Revolt\EventLoop;

/**
 * @template Tk of array-key
 * @template Tv
 *
 * @internal
 */
final class FutureIterator
{
    use ForbidCloning;
    use ForbidSerialization;

    /**
     * @var FutureIteratorQueue<Tk, Tv>
     */
    private readonly FutureIteratorQueue $queue;

    private readonly Cancellation $cancellation;

    private readonly string $cancellationId;

    /**
     * @var Future<null>|Future<never>|null
     */
    private ?Future $complete = null;

    public function __construct(?Cancellation $cancellation = null)
    {
        $this->queue = $queue = new FutureIteratorQueue();
        $this->cancellation = $cancellation ?? new NullCancellation();
        $this->cancellationId = $this->cancellation->subscribe(static function (\Throwable $reason) use ($queue): void {
            if ($queue->suspension) {
                $queue->suspension->throw($reason);
                $queue->suspension = null;
            }
        });
    }

    /**
     * @param FutureState<Tv> $state
     * @param Tk $key
     * @param Future<Tv> $future
     */
    public function enqueue(FutureState $state, int|string $key, Future $future): void
    {
        if ($this->complete) {
            throw new \Error('Iterator has already been marked as complete');
        }

        $queue = $this->queue; // Using separate object to avoid a circular reference.

        /**
         * @param Tv|null $result
         */
        $handler = static function (?\Throwable $error, mixed $result, string $id) use (
            $key,
            $future,
            $queue
        ): void {
            unset($queue->pending[$id]);

            if ($queue->suspension) {
                $queue->suspension->resume([$key, $future]);
                $queue->suspension = null;
                return;
            }

            $queue->items[] = [$key, $future];
        };

        $id = $state->subscribe($handler);

        $queue->pending[$id] = $state;
    }

    public function complete(): void
    {
        if ($this->complete) {
            throw new \Error('Iterator has already been marked as complete');
        }

        $this->complete = Future::complete();

        if (!$this->queue->pending && $this->queue->suspension) {
            $this->queue->suspension->resume();
            $this->queue->suspension = null;
        }
    }

    public function error(\Throwable $exception): void
    {
        if ($this->complete) {
            throw new \Error('Iterator has already been marked as complete');
        }

        $this->complete = Future::error($exception);

        if (!$this->queue->pending && $this->queue->suspension) {
            $this->queue->suspension->throw($exception);
            $this->queue->suspension = null;
        }
    }

    /**
     * @return array{Tk, Future<Tv>}|null
     */
    public function consume(): ?array
    {
        if ($this->queue->suspension) {
            throw new \Error('Concurrent consume() operations are not supported');
        }

        if (!$this->queue->items) {
            if ($this->complete && !$this->queue->pending) {
                return $this->complete->await();
            }

            $this->cancellation->throwIfRequested();

            $this->queue->suspension = EventLoop::getSuspension();

            /** @var array{Tk, Future<Tv>}|null */
            return $this->queue->suspension->suspend();
        }

        $key = \array_key_first($this->queue->items);
        $item = $this->queue->items[$key];

        unset($this->queue->items[$key]);

        return $item;
    }

    public function __destruct()
    {
        $this->cancellation->unsubscribe($this->cancellationId);
        foreach ($this->queue->pending as $id => $state) {
            $state->unsubscribe($id);
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\Internal;

use Amp\Future;
use Revolt\EventLoop\Suspension;

/**
 * @template Tk of array-key
 * @template Tv
 *
 * @internal
 */
final class FutureIteratorQueue
{
    /** @var array<array{Tk, Future<Tv>}> */
    public array $items = [];

    /** @var array<string, FutureState<Tv>> */
    public array $pending = [];

    /** @var Suspension<array{Tk, Future<Tv>}|null>|null */
    public ?Suspension $suspension = null;
}
<?php declare(strict_types=1);

namespace Amp\Internal;

use Amp\Cancellation;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;

/**
 * @internal
 */
final class WrappedCancellation implements Cancellation
{
    use ForbidCloning;
    use ForbidSerialization;

    public function __construct(
        private readonly Cancellation $cancellation
    ) {
    }

    #[\Override]
    public function subscribe(\Closure $callback): string
    {
        return $this->cancellation->subscribe($callback);
    }

    #[\Override]
    public function unsubscribe(string $id): void
    {
        $this->cancellation->unsubscribe($id);
    }

    #[\Override]
    public function isRequested(): bool
    {
        return $this->cancellation->isRequested();
    }

    #[\Override]
    public function throwIfRequested(): void
    {
        $this->cancellation->throwIfRequested();
    }
}
<?php declare(strict_types=1);

namespace Amp\Internal;

/**
 * Formats a stacktrace obtained via `debug_backtrace()`.
 *
 * @param list<array{
 *     args?:list<mixed>,
 *     class?: class-string,
 *     file?: string,
 *     function: string,
 *     line?: int,
 *     object?: object,
 *     type?: string,
 * }> $trace Output of `debug_backtrace()`.
 *
 * @return string Formatted stacktrace.
 *
 * @codeCoverageIgnore
 * @internal
 */
function formatStacktrace(array $trace): string
{
    return \implode("\n", \array_map(static function (array $e, int $i): string {
        $line = "#{$i} ";

        if (isset($e["file"], $e['line'])) {
            $line .= "{$e['file']}:{$e['line']} ";
        }

        if (isset($e["class"], $e["type"])) {
            $line .= $e["class"] . $e["type"];
        }

        return $line . $e["function"] . "()";
    }, $trace, \array_keys($trace)));
}

/**
 * @return bool True if the environment variable AMP_DEBUG is set to a value other than "0", "false", or "off" or
 *      if the constant AMP_DEBUG is defined and set to a truthy value.
 * @internal
 */
function isDebugEnabled(): bool
{
    /** @psalm-suppress RiskyTruthyFalsyComparison */
    return match (\getenv("AMP_DEBUG") ?: "0") {
        "0", "false", "off" => false,
        default => true,
    } || (\defined("AMP_DEBUG") && \AMP_DEBUG);
}
<?php declare(strict_types=1);

namespace Amp;

use Revolt\EventLoop;

/**
 * A TimeoutCancellation automatically requests cancellation after the timeout has elapsed.
 */
final class TimeoutCancellation implements Cancellation
{
    use ForbidCloning;
    use ForbidSerialization;

    private readonly string $callbackId;

    private readonly Cancellation $cancellation;

    /**
     * @param float $timeout Seconds until cancellation is requested.
     * @param string $message Message for TimeoutException. Default is "Operation timed out".
     */
    public function __construct(float $timeout, string $message = "Operation timed out")
    {
        $this->cancellation = $source = new Internal\Cancellable();

        \assert((bool) ($trace = \debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS)));

        $this->callbackId = EventLoop::delay($timeout, static function () use (&$trace, $source, $message): void {
            if ($trace ?? false) {
                $message .= \sprintf("\r\n%s was created here: %s", self::class, Internal\formatStacktrace($trace));
            } else {
                $message .= \sprintf(" (Enable assertions for a backtrace of the %s creation)", self::class);
            }

            $source->cancel(new TimeoutException($message));
        });

        EventLoop::unreference($this->callbackId);
    }

    /**
     * Cancels the delay watcher.
     */
    public function __destruct()
    {
        EventLoop::cancel($this->callbackId);
    }

    #[\Override]
    public function subscribe(\Closure $callback): string
    {
        return $this->cancellation->subscribe($callback);
    }

    #[\Override]
    public function unsubscribe(string $id): void
    {
        $this->cancellation->unsubscribe($id);
    }

    #[\Override]
    public function isRequested(): bool
    {
        return $this->cancellation->isRequested();
    }

    #[\Override]
    public function throwIfRequested(): void
    {
        $this->cancellation->throwIfRequested();
    }
}
<?php declare(strict_types=1);

namespace Amp;

trait ForbidCloning
{
    final protected function __clone()
    {
        throw new \Error(__CLASS__ . ' does not support cloning');
    }
}
<?php declare(strict_types=1);

namespace Amp;

interface Closable
{
    /**
     * Closes the resource, marking it as unusable.
     * Whether pending operations are aborted or not is implementation dependent.
     */
    public function close(): void;

    /**
     * Returns whether this resource has been closed.
     *
     * @return bool `true` if closed, otherwise `false`.
     */
    public function isClosed(): bool;

    /**
     * Registers a callback that is invoked when this resource is closed.
     *
     * @param \Closure():void $onClose
     */
    public function onClose(\Closure $onClose): void;
}
<?php declare(strict_types=1);

namespace Amp;

final class CompositeLengthException extends \Exception
{
    public function __construct(string $message)
    {
        parent::__construct($message);
    }
}
<?php declare(strict_types=1);

namespace Amp;

use Revolt\EventLoop;
use Revolt\EventLoop\UnsupportedFeatureException;

/**
 * Creates a new fiber to execute the given closure asynchronously. A Future is returned which is completed with the
 * return value of the passed closure or will fail if the closure throws an exception.
 *
 * @template T
 *
 * @param \Closure(...):T $closure
 * @param mixed ...$args Arguments forwarded to the closure when starting the fiber.
 *
 * @return Future<T>
 */
function async(\Closure $closure, mixed ...$args): Future
{
    static $run = null;

    $run ??= static function (Internal\FutureState $state, \Closure $closure, array $args): void {
        $s = $state;
        $c = $closure;

        /* Null function arguments so an exception thrown from the closure does not contain the FutureState object
         * in the stack trace, which would create a circular reference, preventing immediate garbage collection */
        $state = $closure = null;

        try {
            // Clear $args to allow garbage collection of arguments during fiber execution
            $s->complete($c(...$args, ...($args = [])));
        } catch (\Throwable $exception) {
            $s->error($exception);
        }
    };

    $state = new Internal\FutureState();

    EventLoop::queue($run, $state, $closure, $args);

    return new Future($state);
}

/**
 * Returns the current time relative to an arbitrary point in time.
 *
 * @return float Time in seconds.
 */
function now(): float
{
    /** @psalm-suppress InvalidOperand */
    return (float) \hrtime(true) / 1_000_000_000;
}

/**
 * Non-blocking sleep for the specified number of seconds.
 *
 * @param float $timeout Number of seconds to wait.
 * @param bool $reference If false, unreference the underlying watcher.
 * @param Cancellation|null $cancellation Cancel waiting if cancellation is requested.
 */
function delay(float $timeout, bool $reference = true, ?Cancellation $cancellation = null): void
{
    $suspension = EventLoop::getSuspension();
    $callbackId = EventLoop::delay($timeout, static fn () => $suspension->resume());
    $cancellationId = $cancellation?->subscribe(
        static fn (CancelledException $exception) => $suspension->throw($exception)
    );

    if (!$reference) {
        EventLoop::unreference($callbackId);
    }

    try {
        $suspension->suspend();
    } finally {
        EventLoop::cancel($callbackId);

        /** @psalm-suppress PossiblyNullArgument $cancellationId will not be null if $cancellation is not null. */
        $cancellation?->unsubscribe($cancellationId);
    }
}

/**
 * Wait for signal(s) in a non-blocking way.
 *
 * @param int|int[] $signals Signal number or array of signal numbers.
 * @param bool $reference If false, unreference the underlying watcher.
 * @param Cancellation|null $cancellation Cancel waiting if cancellation is requested.
 *
 * @return int Caught signal number.
 * @throws UnsupportedFeatureException
 */
function trapSignal(int|array $signals, bool $reference = true, ?Cancellation $cancellation = null): int
{
    $suspension = EventLoop::getSuspension();
    $callback = static fn (string $watcher, int $signal) => $suspension->resume($signal);
    $id = $cancellation?->subscribe(static fn (CancelledException $exception) => $suspension->throw($exception));

    $callbackIds = [];

    if (\is_int($signals)) {
        $signals = [$signals];
    }

    foreach ($signals as $signo) {
        $callbackIds[] = $callbackId = EventLoop::onSignal($signo, $callback);
        if (!$reference) {
            EventLoop::unreference($callbackId);
        }
    }

    try {
        return $suspension->suspend();
    } finally {
        foreach ($callbackIds as $callbackId) {
            EventLoop::cancel($callbackId);
        }

        /** @psalm-suppress PossiblyNullArgument $id will not be null if $cancellation is not null. */
        $cancellation?->unsubscribe($id);
    }
}

/**
 * Returns a Closure that maintains a weak reference to any $this object held by the Closure (a weak-Closure).
 * This allows a class to hold a self-referencing Closure without creating a circular reference that would
 * prevent or delay automatic garbage collection.
 * Invoking the returned Closure after the object is destroyed will throw an instance of Error.
 *
 * @template TClosure of \Closure
 *
 * @param TClosure $closure
 *
 * @return TClosure
 */
function weakClosure(\Closure $closure): \Closure
{
    $reflection = new \ReflectionFunction($closure);

    $that = $reflection->getClosureThis();
    if (!$that) {
        return $closure;
    }

    $reference = \WeakReference::create($that);

    // For internal classes use \Closure::bindTo() without scope.
    $scope = $reflection->getClosureScopeClass();
    $useBindTo = !$scope || $that::class !== $scope->name || $scope->isInternal();

    $methodName = $reflection->getShortName();
    if (!\str_starts_with($methodName, '{closure')) {
        // Closure from first-class callable or \Closure::fromCallable(), declare an anonymous closure to rebind.
        /** @psalm-suppress InvalidScope Closure is bound before being invoked. */
        $closure = fn (mixed ...$args): mixed => $this->{$methodName}(...$args);
        if ($useBindTo && $scope) {
            $closure = $closure->bindTo(null, $scope->name);
        }
    } else {
        // Rebind to remove reference to $that
        $closure = $closure->bindTo($reference);
    }

    if (!$closure) {
        throw new \RuntimeException('Unable to rebind closure scoped to ' . ($scope?->name ?? $that::class));
    }

    /** @var TClosure */
    return static function (mixed ...$args) use ($reference, $closure, $useBindTo): mixed {
        $that = $reference->get();
        if (!$that) {
            throw new \Error('Weakened closure invoked after referenced object destroyed');
        }

        if ($useBindTo) {
            $closure = $closure->bindTo($that);

            if (!$closure) {
                throw new \RuntimeException('Unable to rebind function to object of type ' . $that::class);
            }

            return $closure(...$args);
        }

        return $closure->call($that, ...$args);
    };
}
{
    "name": "amphp/amp",
    "homepage": "https://amphp.org/amp",
    "description": "A non-blocking concurrency framework for PHP applications.",
    "keywords": [
        "async",
        "asynchronous",
        "concurrency",
        "promise",
        "awaitable",
        "future",
        "non-blocking",
        "event",
        "event-loop"
    ],
    "license": "MIT",
    "authors": [
        {
            "name": "Aaron Piotrowski",
            "email": "aaron@trowski.com"
        },
        {
            "name": "Bob Weinand",
            "email": "bobwei9@hotmail.com"
        },
        {
            "name": "Niklas Keller",
            "email": "me@kelunik.com"
        },
        {
            "name": "Daniel Lowrey",
            "email": "rdlowrey@php.net"
        }
    ],
    "require": {
        "php": ">=8.1",
        "revolt/event-loop": "^1 || ^0.2"
    },
    "require-dev": {
        "amphp/php-cs-fixer-config": "^2",
        "phpunit/phpunit": "^9",
        "psalm/phar": "6.16.1"
    },
    "autoload": {
        "psr-4": {
            "Amp\\": "src"
        },
        "files": [
            "src/functions.php",
            "src/Future/functions.php",
            "src/Internal/functions.php"
        ]
    },
    "autoload-dev": {
        "psr-4": {
            "Amp\\": "test"
        }
    },
    "support": {
        "issues": "https://github.com/amphp/amp/issues"
    },
    "scripts": {
        "test": "@php -dzend.assertions=1 -dassert.exception=1 ./vendor/bin/phpunit",
        "code-style": "@php ./vendor/bin/php-cs-fixer fix"
    }
}
<?php

$config = new Amp\CodeStyle\Config();

$config->getFinder()
    ->in(__DIR__ . '/src')
    ->in(__DIR__ . '/test');

$config->setCacheFile(__DIR__ . '/.php_cs.cache');

return $config;
{
    "symbol-whitelist": [
        "null",
        "true",
        "false",
        "static",
        "self",
        "parent",
        "array",
        "string",
        "int",
        "float",
        "bool",
        "iterable",
        "callable",
        "mixed",
        "void",
        "object"
    ],
    "php-core-extensions": [
        "Core",
        "date",
        "pcre",
        "Phar",
        "Reflection",
        "SPL",
        "standard"
    ]
}
<?php declare(strict_types=1);

namespace Amp\Cache;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Sync\KeyedMutex;
use Amp\Sync\Lock;

/**
 * @template TValue
 */
final class AtomicCache
{
    use ForbidCloning;
    use ForbidSerialization;

    /**
     * @param Cache<TValue> $cache
     */
    public function __construct(
        private readonly Cache $cache,
        private readonly KeyedMutex $mutex,
    ) {
    }

    /**
     * Obtains the lock for the given key, then invokes the {@code $compute} callback with the current cached value
     * (which may be {@code null} if the key did not exist in the cache). The value returned from the callback is stored
     * in the cache and returned from this method.
     *
     * @param \Closure(string, TValue|null):TValue $compute Receives $key and $value as parameters.
     * @param int|null $ttl Timeout in seconds. The default {@code null} $ttl value indicates no timeout.
     *
     * @return TValue
     *
     * @throws CacheException If the $create callback throws an exception while generating the value.
     */
    public function compute(string $key, \Closure $compute, ?int $ttl = null): mixed
    {
        $lock = $this->lock($key);

        try {
            $value = $this->cache->get($key);

            return $this->create($compute, $key, $value, $ttl);
        } finally {
            $lock->release();
        }
    }

    /**
     * Attempts to get the value for the given key. If the key is not found, the key is locked, the $compute callback
     * is invoked with the key as the first parameter. The value returned from the callback is stored in the cache and
     * returned from this method.
     *
     * @param string $key Cache key.
     * @param \Closure(string, null):TValue $compute Receives $key as parameter.
     * @param int|null $ttl Timeout in seconds. The default `null` $ttl value indicates no timeout.
     *
     * @return TValue
     *
     * @throws CacheException If the $compute callback throws an exception while generating the value.
     */
    public function computeIfAbsent(string $key, \Closure $compute, ?int $ttl = null): mixed
    {
        $value = $this->cache->get($key);

        if ($value !== null) {
            return $value;
        }

        $lock = $this->lock($key);

        try {
            // Attempt to get the value again, since it may have been set while obtaining the lock.
            return $this->cache->get($key) ?? $this->create($compute, $key, null, $ttl);
        } finally {
            $lock->release();
        }
    }

    /**
     * Attempts to get the value for the given key. If the key exists, the key is locked, the $compute callback
     * is invoked with the key as the first parameter and the current key value as the second parameter. The value
     * returned from the callback is stored in the cache and returned from this method.
     *
     * @param string $key Cache key.
     * @param \Closure(string, TValue):TValue $compute Receives $key and $value as parameters.
     * @param int|null $ttl Timeout in seconds. The default {@code null} $ttl value indicates no timeout.
     *
     * @return TValue
     *
     * @throws CacheException If the $create callback throws an exception while generating the value.
     */
    public function computeIfPresent(string $key, \Closure $compute, ?int $ttl = null): mixed
    {
        $value = $this->cache->get($key);

        if ($value === null) {
            return null;
        }

        $lock = $this->lock($key);

        try {
            // Attempt to get the value again, since it may have been set while obtaining the lock.
            $value = $this->cache->get($key);

            if ($value === null) {
                return null;
            }

            return $this->create($compute, $key, $value, $ttl);
        } finally {
            $lock->release();
        }
    }

    /**
     * The lock is obtained for the key before setting the value.
     *
     * @param string $key Cache key.
     * @param TValue $value Value to cache.
     * @param int|null $ttl Timeout in seconds. The default `null` $ttl value indicates no timeout.
     *
     * @throws CacheException
     *
     * @see SerializedCache::set()
     */
    public function set(string $key, mixed $value, ?int $ttl = null): void
    {
        $lock = $this->lock($key);

        try {
            $this->cache->set($key, $value, $ttl);
        } finally {
            $lock->release();
        }
    }

    /**
     * Returns the cached value for the key or the given default value if the key does not exist.
     *
     * @template TDefault
     *
     * @param string $key Cache key.
     * @param TDefault $default Default value returned if the key does not exist. Null by default.
     *
     * @return TValue|TDefault Resolved with null iff $default is null.
     *
     * @throws CacheException
     */
    public function get(string $key, mixed $default = null): mixed
    {
        $value = $this->cache->get($key);

        if ($value === null) {
            return $default;
        }

        return $value;
    }

    /**
     * The lock is obtained for the key before deleting the key.
     *
     * @throws CacheException
     */
    public function delete(string $key): ?bool
    {
        $lock = $this->lock($key);

        try {
            return $this->cache->delete($key);
        } finally {
            $lock->release();
        }
    }

    private function lock(string $key): Lock
    {
        try {
            return $this->mutex->acquire($key);
        } catch (\Throwable $exception) {
            throw new CacheException(
                \sprintf('Exception thrown when obtaining the lock for key "%s"', $key),
                0,
                $exception
            );
        }
    }

    /**
     * @param TValue|null $value
     *
     * @throws CacheException
     */
    private function create(\Closure $compute, string $key, mixed $value, ?int $ttl): mixed
    {
        try {
            $value = $compute($key, $value);
        } catch (\Throwable $exception) {
            throw new CacheException(
                \sprintf('Exception thrown while creating the value for key "%s"', $key),
                0,
                $exception
            );
        }

        $this->cache->set($key, $value, $ttl);

        return $value;
    }
}
<?php declare(strict_types=1);

namespace Amp\Cache;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;

/**
 * Cache implementation that just ignores all operations and always resolves to `null`.
 *
 * @template TValue
 * @implements Cache<TValue>
 */
final class NullCache implements Cache
{
    use ForbidCloning;
    use ForbidSerialization;

    public function get(string $key): mixed
    {
        return null;
    }

    public function set(string $key, mixed $value, ?int $ttl = null): void
    {
        // Nothing to do.
    }

    public function delete(string $key): bool
    {
        return false;
    }
}
<?php declare(strict_types=1);

namespace Amp\Cache;

interface StringCache
{
    /**
     * Gets a value associated with the given key.
     *
     * If the specified key doesn't exist implementations MUST return {@code null}.
     *
     * @param $key string Cache key.
     *
     * @return string|null Returns the cached value, or {@code null} if it doesn't exist
     *
     * @throws CacheException On failure to determine the cached value
     */
    public function get(string $key): ?string;

    /**
     * Sets a value associated with the given key. Overrides existing values (if they exist).
     *
     * TTL values less than 0 MUST throw an \Error.
     *
     * @param $key string Cache key.
     * @param $value string Value to cache.
     * @param $ttl int Timeout in seconds >= 0. The default {@code null} $ttl value indicates no timeout.
     *
     * @throws CacheException On failure to store the cached value
     */
    public function set(string $key, string $value, ?int $ttl = null): void;

    /**
     * Deletes a value associated with the given key if it exists.
     *
     * Implementations SHOULD return boolean {@code true} or {@code false} to indicate whether the specified key
     * existed
     * at the time the delete operation was requested. If such information is not available, the implementation MUST
     * return {@code null}.
     *
     * Implementations MUST NOT error for non-existent keys.
     *
     * @param $key string Cache key.
     *
     * @return bool|null Returns {@code true} / {@code false} to indicate whether the key existed, or {@code null} if
     *     that information is not available.
     *
     * @throws CacheException On failure to delete the cached value
     */
    public function delete(string $key): ?bool;
}
<?php declare(strict_types=1);

namespace Amp\Cache;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;

/**
 * @template TValue
 * @implements Cache<TValue>
 */
final class PrefixCache implements Cache
{
    use ForbidCloning;
    use ForbidSerialization;

    public function __construct(
        private readonly Cache $cache,
        private readonly string $keyPrefix,
    ) {
    }

    /**
     * Gets the specified key prefix.
     */
    public function getKeyPrefix(): string
    {
        return $this->keyPrefix;
    }

    public function get(string $key): mixed
    {
        return $this->cache->get($this->keyPrefix . $key);
    }

    public function set(string $key, mixed $value, ?int $ttl = null): void
    {
        $this->cache->set($this->keyPrefix . $key, $value, $ttl);
    }

    public function delete(string $key): ?bool
    {
        return $this->cache->delete($this->keyPrefix . $key);
    }
}
<?php declare(strict_types=1);

namespace Amp\Cache;

/**
 * @template TValue
 */
interface Cache
{
    /**
     * Gets a value associated with the given key.
     *
     * If the specified key doesn't exist implementations MUST return {@code null}.
     *
     * @param $key string Cache key.
     *
     * @return TValue|null Returns the cached value, or {@code null} if it doesn't exist
     *
     * @throws CacheException On failure to determine the cached value
     */
    public function get(string $key): mixed;

    /**
     * Sets a value associated with the given key. Overrides existing values (if they exist).
     *
     * TTL values less than 0 MUST throw an \Error.
     *
     * @param $key string Cache key.
     * @param $value TValue Value to cache.
     * @param $ttl int Timeout in seconds >= 0. The default {@code null} $ttl value indicates no timeout.
     *
     * @throws CacheException On failure to store the cached value
     */
    public function set(string $key, mixed $value, ?int $ttl = null): void;

    /**
     * Deletes a value associated with the given key if it exists.
     *
     * Implementations SHOULD return boolean {@code true} or {@code false} to indicate whether the specified key
     * existed
     * at the time the delete operation was requested. If such information is not available, the implementation MUST
     * return {@code null}.
     *
     * Implementations MUST NOT error for non-existent keys.
     *
     * @param $key string Cache key.
     *
     * @return bool|null Returns {@code true} / {@code false} to indicate whether the key existed, or {@code null} if
     *     that information is not available.
     *
     * @throws CacheException On failure to delete the cached value
     */
    public function delete(string $key): ?bool;
}
<?php declare(strict_types=1);

namespace Amp\Cache;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;

final class StringCacheAdapter implements StringCache
{
    use ForbidCloning;
    use ForbidSerialization;

    public function __construct(private readonly Cache $cache)
    {
    }

    public function get(string $key): ?string
    {
        $value = $this->cache->get($key);

        if ($value !== null && !\is_string($value)) {
            throw new CacheException(
                'Received unexpected type from ' . \get_class($this->cache) . ': ' . \get_debug_type($value)
            );
        }

        return $value;
    }

    public function set(string $key, string $value, ?int $ttl = null): void
    {
        $this->cache->set($key, $value, $ttl);
    }

    public function delete(string $key): ?bool
    {
        return $this->cache->delete($key);
    }
}
<?php declare(strict_types=1);

namespace Amp\Cache;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Revolt\EventLoop;

/**
 * A cache which stores data in an in-memory (local) array.
 * This class may be used as a least-recently-used (LRU) cache of a given size.
 * Iterating over the cache will iterate from least-recently-used to most-recently-used.
 *
 * @template TValue
 * @implements Cache<TValue>
 * @implements \IteratorAggregate<string, TValue>
 */
final class LocalCache implements Cache, \Countable, \IteratorAggregate
{
    use ForbidCloning;
    use ForbidSerialization;

    private readonly object $state;

    private readonly string $gcCallbackId;

    /** @var int<1, max>|null */
    private readonly ?int $sizeLimit;

    /**
     * @param int<1, max>|null $sizeLimit The maximum size of cache array (number of elements). NULL for unlimited size.
     * @param float $gcInterval The frequency in seconds at which expired cache entries should be garbage collected.
     */
    public function __construct(?int $sizeLimit = null, float $gcInterval = 5)
    {
        if ($sizeLimit !== null && $sizeLimit < 1) {
            throw new \Error('Invalid sizeLimit, must be > 0: ' . $sizeLimit);
        }

        // By using a separate state object we're able to use `__destruct()` for garbage collection of both this
        // instance and the event loop callback. Otherwise, this object could only be collected when the garbage
        // collection callback was cancelled at the event loop layer.
        $this->state = $state = new class {
            public array $cache = [];

            /** @var array<string, int> */
            public array $cacheTimeouts = [];

            public bool $isSortNeeded = false;

            public function collectGarbage(): void
            {
                $now = \time();

                if ($this->isSortNeeded) {
                    \asort($this->cacheTimeouts);
                    $this->isSortNeeded = false;
                }

                foreach ($this->cacheTimeouts as $key => $expiry) {
                    if ($now <= $expiry) {
                        break;
                    }

                    unset(
                        $this->cache[$key],
                        $this->cacheTimeouts[$key]
                    );
                }
            }
        };

        $this->gcCallbackId = EventLoop::repeat($gcInterval, $state->collectGarbage(...));
        $this->sizeLimit = $sizeLimit;

        EventLoop::unreference($this->gcCallbackId);
    }

    public function __destruct()
    {
        $this->state->cache = [];
        $this->state->cacheTimeouts = [];

        EventLoop::cancel($this->gcCallbackId);
    }

    public function get(string $key): mixed
    {
        if (!isset($this->state->cache[$key])) {
            return null;
        }

        $value = $this->state->cache[$key];
        unset($this->state->cache[$key]);

        if (isset($this->state->cacheTimeouts[$key]) && \time() > $this->state->cacheTimeouts[$key]) {
            unset($this->state->cacheTimeouts[$key]);

            return null;
        }

        $this->state->cache[$key] = $value;

        return $value;
    }

    public function set(string $key, mixed $value, ?int $ttl = null): void
    {
        if ($value === null) {
            throw new CacheException('Cannot store NULL in ' . self::class);
        }

        if ($ttl === null) {
            unset($this->state->cacheTimeouts[$key]);
        } elseif ($ttl >= 0) {
            $expiry = \time() + $ttl;
            $this->state->cacheTimeouts[$key] = $expiry;
            $this->state->isSortNeeded = true;
        } else {
            throw new \Error("Invalid cache TTL ({$ttl}; integer >= 0 or null required");
        }

        unset($this->state->cache[$key]);
        if (\count($this->state->cache) === $this->sizeLimit) {
            /** @var array-key $keyToEvict */
            $keyToEvict = \array_key_first($this->state->cache);
            unset($this->state->cache[$keyToEvict]);
        }

        $this->state->cache[$key] = $value;
    }

    public function delete(string $key): bool
    {
        $exists = isset($this->state->cache[$key]);

        unset(
            $this->state->cache[$key],
            $this->state->cacheTimeouts[$key]
        );

        return $exists;
    }

    public function count(): int
    {
        return \count($this->state->cache);
    }

    public function getIterator(): \Traversable
    {
        foreach ($this->state->cache as $key => $value) {
            yield (string) $key => $value;
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\Cache;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Serialization\SerializationException;
use Amp\Serialization\Serializer;

/**
 * @template TValue
 * @implements Cache<TValue>
 */
final class SerializedCache implements Cache
{
    use ForbidCloning;
    use ForbidSerialization;

    public function __construct(
        private readonly StringCache $cache,
        private readonly Serializer $serializer,
    ) {
    }

    /**
     * Fetch a value from the cache and unserialize it.
     *
     * @param $key string Cache key.
     *
     * @return TValue Returns the cached value or {@code null} if it doesn't exist.
     *
     * @throws CacheException
     * @throws SerializationException
     *
     * @see StringCache::get()
     */
    public function get(string $key): mixed
    {
        $data = $this->cache->get($key);
        if ($data === null) {
            return null;
        }

        return $this->serializer->unserialize($data);
    }

    /**
     * Serializes a value and stores its serialization to the cache.
     *
     * @param string $key Cache key.
     * @param TValue $value Value to cache.
     * @param int|null $ttl Timeout in seconds. The default {@code null} $ttl value indicates no timeout. Values less
     *     than 0 MUST throw an \Error.
     *
     * @throws CacheException
     * @throws SerializationException
     *
     * @see StringCache::set()
     */
    public function set(string $key, mixed $value, ?int $ttl = null): void
    {
        if ($value === null) {
            throw new CacheException('Cannot store NULL in ' . self::class);
        }

        $value = $this->serializer->serialize($value);

        $this->cache->set($key, $value, $ttl);
    }

    /**
     * Deletes a value associated with the given key if it exists.
     *
     * @param $key string Cache key.
     *
     * @return bool|null Returns {@code true} / {@code false} to indicate whether the key existed, or {@code null} if
     *     that information is not available.
     *
     * @throws CacheException
     *
     * @see StringCache::delete()
     */
    public function delete(string $key): ?bool
    {
        return $this->cache->delete($key);
    }
}
<?php declare(strict_types=1);

namespace Amp\Cache;

/**
 * MUST be thrown in case a cache operation fails.
 */
class CacheException extends \Exception
{
}
{
    "name": "amphp/cache",
    "homepage": "https://amphp.org/cache",
    "description": "A fiber-aware cache API based on Amp and Revolt.",
    "license": "MIT",
    "support": {
        "issues": "https://github.com/amphp/cache/issues"
    },
    "authors": [
        {
            "name": "Niklas Keller",
            "email": "me@kelunik.com"
        },
        {
            "name": "Aaron Piotrowski",
            "email": "aaron@trowski.com"
        },
        {
            "name": "Daniel Lowrey",
            "email": "rdlowrey@php.net"
        }
    ],
    "require": {
        "php": ">=8.1",
        "amphp/amp": "^3",
        "amphp/serialization": "^1",
        "amphp/sync": "^2",
        "revolt/event-loop": "^1 || ^0.2"
    },
    "require-dev": {
        "amphp/phpunit-util": "^3",
        "phpunit/phpunit": "^9",
        "amphp/php-cs-fixer-config": "^2",
        "psalm/phar": "^5.4"
    },
    "autoload": {
        "psr-4": {
            "Amp\\Cache\\": "src"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Amp\\Cache\\Test\\": "test"
        }
    }
}
#!/usr/bin/env php
<?php declare(strict_types=1);

require dirname(__DIR__) . '/vendor/autoload.php';

use Amp\Postgres\PostgresByteA;
use Amp\Postgres\PostgresConfig;
use Amp\Postgres\PostgresConnectionPool;

$config = PostgresConfig::fromString('host=localhost user=postgres');
$pool = new PostgresConnectionPool($config);

$pool->query('DROP TABLE IF EXISTS test');

$transaction = $pool->beginTransaction();

$transaction->query('CREATE TABLE test (value BYTEA)');

$statement = $transaction->prepare('INSERT INTO test VALUES (?)');

$statement->execute([new PostgresByteA($a = random_bytes(10))]);
$statement->execute([new PostgresByteA($b = random_bytes(10))]);
$statement->execute([new PostgresByteA($c = random_bytes(10))]);

$result = $transaction->execute('SELECT * FROM test WHERE value = :value', ['value' => new PostgresByteA($a)]);

foreach ($result as $row) {
    assert($row['value'] === $a);
    var_dump(bin2hex($row['value']));
}

$transaction->rollback();
#!/usr/bin/env php
<?php declare(strict_types=1);

require dirname(__DIR__) . '/vendor/autoload.php';

use Amp\Future;
use Amp\Postgres\PostgresConfig;
use Amp\Postgres\PostgresConnectionPool;
use Amp\Postgres\PostgresListener;
use Amp\Postgres\PostgresNotification;
use function Amp\async;
use function Amp\delay;

$config = PostgresConfig::fromString('host=localhost user=postgres');
$pool = new PostgresConnectionPool($config);

$channel1 = "test1";
$channel2 = "test2";

$listener1 = $pool->listen($channel1);

printf("Listening on channel '%s'\n", $listener1->getChannel());

$listener2 = $pool->listen($channel2);

printf("Listening on channel '%s'\n", $listener2->getChannel());

async(function () use ($pool, $listener1, $listener2, $channel1, $channel2): void {
    $pool->notify($channel1, "Data 1.1");

    delay(0.5);

    $pool->notify($channel2, "Data 2.1");

    delay(0.5);

    $pool->notify($channel2, "Data 2.2");

    delay(0.5);

    printf("Unlistening from channel '%s'\n", $listener2->getChannel());
    $listener2->unlisten();

    delay(0.5);

    $pool->notify($channel1, "Data 1.2");

    delay(0.5);

    printf("Unlistening from channel '%s'\n", $listener1->getChannel());
    $listener1->unlisten();
});

$consumer = function (PostgresListener $listener): void {
    /** @var PostgresNotification $notification */
    foreach ($listener as $notification) {
        printf(
            "Received notification from PID %d on channel '%s' with payload: %s\n",
            $notification->pid,
            $notification->channel,
            $notification->payload,
        );
    }
};

$future1 = async($consumer, $listener1);
$future2 = async($consumer, $listener2);

Future\await([$future1, $future2]);

$pool->close();
#!/usr/bin/env php
<?php declare(strict_types=1);

require dirname(__DIR__) . '/vendor/autoload.php';

use Amp\Postgres\PostgresConfig;
use Amp\Postgres\PostgresConnectionPool;
use function Amp\async;
use function Amp\delay;

$config = PostgresConfig::fromString('host=localhost user=postgres');
$pool = new PostgresConnectionPool($config);

$channel = "test";

$listener = $pool->listen($channel);

printf("Listening on channel '%s'\n", $listener->getChannel());

async(function () use ($pool, $channel, $listener): void {
    delay(1);

    $pool->notify($channel, "Data 1"); // Send first notification.

    delay(1);

    $pool->notify($channel, "Data 2"); // Send second notification.

    delay(1);

    $listener->unlisten();
});

foreach ($listener as $notification) {
    printf(
        "Received notification from PID %d on channel '%s' with payload: %s\n",
        $notification->pid,
        $notification->channel,
        $notification->payload,
    );
}

$pool->close();
#!/usr/bin/env php
<?php declare(strict_types=1);

require dirname(__DIR__) . '/vendor/autoload.php';

use Amp\Postgres\PostgresConfig;
use Amp\Postgres\PostgresConnectionPool;

$config = PostgresConfig::fromString('host=localhost user=postgres');
$pool = new PostgresConnectionPool($config);

$pool->query('DROP TABLE IF EXISTS test');

$transaction = $pool->beginTransaction();

$transaction->query('CREATE TABLE test (domain VARCHAR(63), tld VARCHAR(63), PRIMARY KEY (domain, tld))');

$statement = $transaction->prepare('INSERT INTO test VALUES (?, ?)');

$statement->execute(['amphp', 'org']);
$statement->execute(['google', 'com']);
$statement->execute(['github', 'com']);

$result = $transaction->execute('SELECT * FROM test WHERE tld = :tld', ['tld' => 'com']);

$format = "%-20s | %-10s\n";
printf($format, 'TLD', 'Domain');
foreach ($result as $row) {
    printf($format, $row['domain'], $row['tld']);
}

$transaction->rollback();

$pool->close();
#!/usr/bin/env php
<?php declare(strict_types=1);

require dirname(__DIR__) . '/vendor/autoload.php';

use Amp\Postgres;

$config = Postgres\PostgresConfig::fromString('host=localhost user=postgres');

$connection = Postgres\connect($config);

$result = $connection->query('SHOW ALL');

foreach ($result as $row) {
    printf("%-35s = %s (%s)\n", $row['name'], $row['setting'], $row['description']);
}
<?php

$config = new Amp\CodeStyle\Config;
$config->getFinder()
    ->in(__DIR__ . '/examples')
    ->in(__DIR__ . '/src')
    ->in(__DIR__ . '/test');

$config->setCacheFile(__DIR__ . '/.php_cs.cache');

return $config;
<?php declare(strict_types=1);

namespace Amp\Postgres;

use Amp\Cancellation;
use Amp\DeferredFuture;
use Amp\Sql\SqlConnectionException;
use pq;
use Revolt\EventLoop;

final class PqConnection extends Internal\PostgresHandleConnection implements PostgresConnection
{
    private readonly Internal\PqHandle $handle;

    #[\Override]
    public static function connect(PostgresConfig $config, ?Cancellation $cancellation = null): self
    {
        try {
            $connection = new pq\Connection($config->getConnectionString(), pq\Connection::ASYNC);
        } catch (pq\Exception $exception) {
            throw new SqlConnectionException("Could not connect to PostgreSQL server", 0, $exception);
        }

        $connection->nonblocking = true;
        $connection->unbuffered = true;

        $deferred = new DeferredFuture();

        /** @psalm-suppress UndefinedVariable $poll is defined below. */
        $callback = static function () use (&$poll, &$await, $connection, $config, $deferred): void {
            switch ($result = $connection->poll()) {
                case pq\Connection::POLLING_READING:
                case pq\Connection::POLLING_WRITING:
                    return; // Connection still reading or writing, so return and leave callback enabled.

                case pq\Connection::POLLING_FAILED:
                    $deferred->error(new SqlConnectionException($connection->errorMessage));
                    break;

                case pq\Connection::POLLING_OK:
                    $deferred->complete(new self($connection, $config));
                    break;

                default:
                    $deferred->error(new SqlConnectionException('Unexpected connection status value: ' . $result));
                    break;
            }

            EventLoop::disable($poll);
            EventLoop::disable($await);
        };

        $poll = EventLoop::onReadable($connection->socket, $callback);
        $await = EventLoop::onWritable($connection->socket, $callback);

        try {
            return $deferred->getFuture()->await($cancellation);
        } finally {
            EventLoop::cancel($poll);
            EventLoop::cancel($await);
        }
    }

    protected function __construct(pq\Connection $handle, PostgresConfig $config)
    {
        $this->handle = new Internal\PqHandle($handle, $config);
        parent::__construct($this->handle);
    }

    /**
     * @return bool True if result sets are buffered in memory, false if unbuffered.
     */
    public function isBufferingResults(): bool
    {
        return $this->handle->isBufferingResults();
    }

    /**
     * Sets result sets to be fully buffered in local memory.
     */
    public function shouldBufferResults(): void
    {
        $this->handle->shouldBufferResults();
    }

    /**
     * Sets result sets to be streamed from the database server.
     */
    public function shouldNotBufferResults(): void
    {
        $this->handle->shouldNotBufferResults();
    }
}
<?php declare(strict_types=1);

namespace Amp\Postgres;

use Amp\Sql\SqlResult;

/**
 * Recursive template types currently not supported, list<mixed> should be list<TFieldType>.
 * @psalm-type TFieldType = list<mixed>|scalar|null
 * @psalm-type TRowType = array<string, TFieldType>
 * @extends SqlResult<TFieldType>
 */
interface PostgresResult extends SqlResult
{
    /**
     * Changes return type to this library's Result type.
     */
    #[\Override]
    public function getNextResult(): ?self;
}
<?php declare(strict_types=1);

namespace Amp\Postgres;

final class PostgresArray
{
    public function __construct(
        private readonly array $data,
        private readonly string $delimiter = ',',
    ) {
    }

    /**
     * Encodes the array for use in a query. Note that the value returned must still be quoted using
     * {@see PostgresExecutor::quoteLiteral()} if inserting directly into a query string.
     */
    public function encode(PostgresExecutor $executor): string
    {
        return Internal\encodeArray($executor, $this->data, $this->delimiter);
    }
}
<?php declare(strict_types=1);

namespace Amp\Postgres;

use Amp\Future;
use Amp\Sql\Common\SqlCommonConnectionPool;
use Amp\Sql\SqlConnector;
use Amp\Sql\SqlResult;
use Amp\Sql\SqlStatement;
use Amp\Sql\SqlTransaction;
use Amp\Sql\SqlTransactionIsolation;
use Amp\Sql\SqlTransactionIsolationLevel;
use function Amp\async;

/**
 * @extends SqlCommonConnectionPool<PostgresConfig, PostgresResult, PostgresStatement, PostgresTransaction, PostgresConnection>
 */
final class PostgresConnectionPool extends SqlCommonConnectionPool implements PostgresConnection
{
    /** @var Future<PostgresConnection>|null Connection used for notification listening. */
    private Future|null $listeningConnection = null;

    /** @var int Number of listeners on listening connection. */
    private int $listenerCount = 0;

    /**
     * @param positive-int $maxConnections
     * @param positive-int $idleTimeout
     * @param bool $resetConnections True to automatically execute DISCARD ALL on a connection before use.
     * @param SqlConnector<PostgresConfig, PostgresConnection>|null $connector
     */
    public function __construct(
        PostgresConfig $config,
        int $maxConnections = self::DEFAULT_MAX_CONNECTIONS,
        int $idleTimeout = self::DEFAULT_IDLE_TIMEOUT,
        private readonly bool $resetConnections = true,
        ?SqlConnector $connector = null,
        SqlTransactionIsolation $transactionIsolation = SqlTransactionIsolationLevel::Committed,
    ) {
        parent::__construct(
            config: $config,
            connector: $connector ?? postgresConnector(),
            maxConnections: $maxConnections,
            idleTimeout: $idleTimeout,
            transactionIsolation: $transactionIsolation,
        );
    }

    /**
     * @param \Closure():void $release
     */
    #[\Override]
    protected function createStatement(SqlStatement $statement, \Closure $release): PostgresStatement
    {
        \assert($statement instanceof PostgresStatement);
        return new Internal\PostgresPooledStatement($statement, $release);
    }

    #[\Override]
    protected function createResult(SqlResult $result, \Closure $release): PostgresResult
    {
        \assert($result instanceof PostgresResult);
        return new Internal\PostgresPooledResult($result, $release);
    }

    #[\Override]
    protected function createStatementPool(string $sql, \Closure $prepare): PostgresStatement
    {
        return new Internal\PostgresStatementPool($this, $sql, $prepare);
    }

    #[\Override]
    protected function createTransaction(SqlTransaction $transaction, \Closure $release): PostgresTransaction
    {
        \assert($transaction instanceof PostgresTransaction);
        return new Internal\PostgresPooledTransaction($transaction, $release);
    }

    #[\Override]
    protected function pop(): PostgresConnection
    {
        $connection = parent::pop();

        if ($this->resetConnections) {
            $connection->query("DISCARD ALL");
        }

        return $connection;
    }

    /**
     * Changes return type to this library's Result type.
     */
    #[\Override]
    public function query(string $sql): PostgresResult
    {
        return parent::query($sql);
    }

    /**
     * Changes return type to this library's Statement type.
     */
    #[\Override]
    public function prepare(string $sql): PostgresStatement
    {
        return parent::prepare($sql);
    }

    /**
     * Changes return type to this library's Result type.
     */
    #[\Override]
    public function execute(string $sql, array $params = []): PostgresResult
    {
        return parent::execute($sql, $params);
    }

    /**
     * Changes return type to this library's Transaction type.
     */
    #[\Override]
    public function beginTransaction(): PostgresTransaction
    {
        return parent::beginTransaction();
    }

    /**
     * Changes return type to this library's configuration type.
     */
    #[\Override]
    public function getConfig(): PostgresConfig
    {
        return parent::getConfig();
    }

    #[\Override]
    public function notify(string $channel, string $payload = ""): PostgresResult
    {
        $connection = $this->pop();

        try {
            $result = $connection->notify($channel, $payload);
        } finally {
            $this->push($connection);
        }

        return $result;
    }

    #[\Override]
    public function listen(string $channel): PostgresListener
    {
        $this->listeningConnection ??= async($this->pop(...));

        $connection = $this->listeningConnection->await();

        ++$this->listenerCount;

        try {
            $listener = $connection->listen($channel);
        } catch (\Throwable $exception) {
            if (--$this->listenerCount === 0) {
                $this->push($connection);
            }
            throw $exception;
        }

        return new Internal\PostgresPooledListener($listener, function () use ($connection): void {
            if (--$this->listenerCount === 0) {
                $this->push($connection);
            }
        });
    }

    #[\Override]
    public function quoteLiteral(string $data): string
    {
        $connection = $this->pop();

        try {
            return $connection->quoteLiteral($data);
        } finally {
            $this->push($connection);
        }
    }

    #[\Override]
    public function quoteIdentifier(string $name): string
    {
        $connection = $this->pop();

        try {
            return $connection->quoteIdentifier($name);
        } finally {
            $this->push($connection);
        }
    }

    #[\Override]
    public function escapeByteA(string $data): string
    {
        $connection = $this->pop();

        try {
            return $connection->escapeByteA($data);
        } finally {
            $this->push($connection);
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\Postgres;

use Amp\Sql\SqlConnectionException;
use Amp\Sql\SqlException;
use Amp\Sql\SqlExecutor;

/**
 * @extends SqlExecutor<PostgresResult, PostgresStatement>
 */
interface PostgresExecutor extends SqlExecutor
{
    /**
     * @return PostgresResult Result object specific to this library.
     */
    #[\Override]
    public function query(string $sql): PostgresResult;

    /**
     * @return PostgresStatement Statement object specific to this library.
     */
    #[\Override]
    public function prepare(string $sql): PostgresStatement;

    /**
     * @return PostgresResult Result object specific to this library.
     */
    #[\Override]
    public function execute(string $sql, array $params = []): PostgresResult;

    /**
     * @param non-empty-string $channel Channel name.
     * @param string $payload Notification payload.
     *
     * @throws SqlException If the operation fails due to unexpected condition.
     * @throws SqlConnectionException If the connection to the database is lost.
     */
    public function notify(string $channel, string $payload = ""): PostgresResult;

    /**
     * Quotes (escapes) the given string for use as a string literal in a query. This method wraps the
     * string in single quotes, so additional quotes should not be added in the query.
     *
     * @param string $data Unquoted data.
     *
     * @return string Quoted string literal.
     *
     * @throws \Error If the connection to the database has been closed.
     */
    public function quoteLiteral(string $data): string;

    /**
     * Quotes (escapes) the given string for use as an identifier in a query.
     *
     * @param string $name Unquoted identifier.
     *
     * @return string Quoted identifier.
     *
     * @throws \Error If the connection to the database has been closed.
     */
    public function quoteIdentifier(string $name): string;

    /**
     * Escapes a binary string to be used as BYTEA data.
     */
    public function escapeByteA(string $data): string;
}
<?php declare(strict_types=1);

namespace Amp\Postgres;

use Amp\Sql\SqlConnection;
use Amp\Sql\SqlConnectionException;
use Amp\Sql\SqlException;
use Amp\Sql\SqlQueryError;

/**
 * @extends SqlConnection<PostgresConfig, PostgresResult, PostgresStatement, PostgresTransaction>
 */
interface PostgresConnection extends PostgresLink, SqlConnection
{
    /**
     * @return PostgresConfig Config object specific to this library.
     */
    #[\Override]
    public function getConfig(): PostgresConfig;

    /**
     * @param non-empty-string $channel Channel name.
     *
     * @throws SqlException If the operation fails due to unexpected condition.
     * @throws SqlConnectionException If the connection to the database is lost.
     * @throws SqlQueryError If the operation fails due to an error in the query (such as a syntax error).
     */
    public function listen(string $channel): PostgresListener;
}
<?php declare(strict_types=1);

namespace Amp\Postgres;

use Amp\Sql\SqlException;

final class PostgresParseException extends SqlException
{
    public function __construct(string $message = '')
    {
        $message = "Parse error while splitting array" . (($message === '') ? '' : ": " . $message);
        parent::__construct($message);
    }
}
<?php declare(strict_types=1);

namespace Amp\Postgres;

use Amp\Sql\SqlQueryError;

class PostgresQueryError extends SqlQueryError
{
    /**
     * @param array<non-empty-string, scalar|null> $diagnostics
     */
    public function __construct(
        string $message,
        private readonly array $diagnostics,
        string $query,
        ?\Throwable $previous = null,
    ) {
        parent::__construct($message, $query, $previous);
    }

    /**
     * @return array<non-empty-string, scalar|null>
     */
    public function getDiagnostics(): array
    {
        return $this->diagnostics;
    }
}
<?php declare(strict_types=1);

namespace Amp\Postgres;

use Amp\Sql\SqlConfig;

final class PostgresConfig extends SqlConfig
{
    public const DEFAULT_PORT = 5432;

    public const SSL_MODES = [
        'disable',
        'allow',
        'prefer',
        'require',
        'verify-ca',
        'verify-full',
    ];

    public const KEY_MAP = [
        ...parent::KEY_MAP,
        'ssl_mode' => 'sslmode',
        'sslMode' => 'sslmode',
        'applicationName' => 'application_name',
        'options' => 'options',
    ];

    private ?string $connectionString = null;

    public static function fromString(string $connectionString): self
    {
        $parts = self::parseConnectionString($connectionString, self::KEY_MAP);

        if (!isset($parts["host"])) {
            throw new \Error("Host must be provided in connection string");
        }

        return new self(
            $parts["host"],
            (int) ($parts["port"] ?? self::DEFAULT_PORT),
            $parts["user"] ?? null,
            $parts["password"] ?? null,
            $parts["db"] ?? null,
            $parts["application_name"] ?? null,
            $parts["sslmode"] ?? null,
            $parts["options"] ?? null,
        );
    }

    public function __construct(
        string $host,
        int $port = self::DEFAULT_PORT,
        ?string $user = null,
        ?string $password = null,
        ?string $database = null,
        private ?string $applicationName = null,
        private ?string $sslMode = null,
        private ?string $options = null,
    ) {
        self::assertValidSslMode($sslMode);

        parent::__construct($host, $port, $user, $password, $database);
    }

    public function __clone()
    {
        $this->connectionString = null;
    }

    public function getSslMode(): ?string
    {
        return $this->sslMode;
    }

    private static function assertValidSslMode(?string $mode): void
    {
        if ($mode === null) {
            return;
        }

        if (!\in_array($mode, self::SSL_MODES, true)) {
            throw new \Error('Invalid SSL mode, must be one of: ' . \implode(', ', self::SSL_MODES));
        }
    }

    public function withSslMode(string $mode): self
    {
        self::assertValidSslMode($mode);

        $new = clone $this;
        $new->sslMode = $mode;
        return $new;
    }

    public function withoutSslMode(): self
    {
        $new = clone $this;
        $new->sslMode = null;
        return $new;
    }

    public function getApplicationName(): ?string
    {
        return $this->applicationName;
    }

    public function withApplicationName(string $name): self
    {
        $new = clone $this;
        $new->applicationName = $name;
        return $new;
    }

    public function withoutApplicationName(): self
    {
        $new = clone $this;
        $new->applicationName = null;
        return $new;
    }

    public function getOptions(): ?string
    {
        return $this->options;
    }

    public function withOptions(string $options): self
    {
        $new = clone $this;
        $new->options = $options;
        return $new;
    }

    public function withoutOptions(): self
    {
        $new = clone $this;
        $new->options = null;
        return $new;
    }

    /**
     * @return string Connection string used with ext-pgsql and pecl-pq.
     */
    public function getConnectionString(): string
    {
        if ($this->connectionString !== null) {
            return $this->connectionString;
        }

        $chunks = [
            "host=" . $this->getHost(),
            "port=" . $this->getPort(),
        ];

        $user = $this->getUser();
        if ($user !== null) {
            $chunks[] = \sprintf("user='%s'", \addslashes($user));
        }

        $password = $this->getPassword();
        if ($password !== null) {
            $chunks[] = \sprintf("password='%s'", \addslashes($password));
        }

        $database = $this->getDatabase();
        if ($database !== null) {
            $chunks[] = \sprintf("dbname='%s'", \addslashes($database));
        }

        if ($this->sslMode !== null) {
            $chunks[] = "sslmode=" . $this->sslMode;
        }

        if ($this->applicationName !== null) {
            $chunks[] = \sprintf("application_name='%s'", \addslashes($this->applicationName));
        }

        if ($this->options !== null) {
            $chunks[] = \sprintf("options='%s'", \addslashes($this->options));
        }

        return $this->connectionString = \implode(" ", $chunks);
    }
}
<?php declare(strict_types=1);

namespace Amp\Postgres;

use Amp\Cancellation;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Sql\SqlConfig;
use Amp\Sql\SqlConnector;
use Amp\Sql\SqlException;

/**
 * @implements SqlConnector<PostgresConfig, PostgresConnection>
 */
final class DefaultPostgresConnector implements SqlConnector
{
    use ForbidCloning;
    use ForbidSerialization;

    /**
     * @throws SqlException If connecting fails.
     *
     * @throws \Error If neither ext-pgsql nor pecl-pq is loaded.
     */
    #[\Override]
    public function connect(SqlConfig $config, ?Cancellation $cancellation = null): PostgresConnection
    {
        if (!$config instanceof PostgresConfig) {
            throw new \TypeError(\sprintf("Must provide an instance of %s to Postgres connectors", PostgresConfig::class));
        }

        if (\extension_loaded("pq")) {
            return PqConnection::connect($config, $cancellation);
        }

        if (\extension_loaded("pgsql")) {
            return PgSqlConnection::connect($config, $cancellation);
        }

        throw new \Error("amphp/postgres requires either pecl-pq or ext-pgsql");
    }
}
<?php declare(strict_types=1);

namespace Amp\Postgres\Internal;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Postgres\PostgresListener;
use Amp\Postgres\PostgresNotification;
use function Amp\async;

/**
 * @internal
 * @implements \IteratorAggregate<int, PostgresNotification>
 */
final class PostgresConnectionListener implements PostgresListener, \IteratorAggregate
{
    use ForbidCloning;
    use ForbidSerialization;

    /** @var null|\Closure(non-empty-string):void */
    private ?\Closure $unlisten;

    /**
     * @param \Traversable<int, PostgresNotification> $source Traversable of notifications on the channel.
     * @param non-empty-string $channel Channel name.
     * @param \Closure(non-empty-string):void $unlisten Function invoked to stop listening on the channel.
     */
    public function __construct(
        private readonly \Traversable $source,
        private readonly string $channel,
        \Closure $unlisten,
    ) {
        $this->unlisten = $unlisten;
    }

    public function __destruct()
    {
        if ($this->unlisten) {
            async($this->unlisten, $this->channel);
            $this->unlisten = null;
        }
    }

    #[\Override]
    public function getIterator(): \Traversable
    {
        // Using a Generator to keep a reference to $this.
        yield from $this->source;
    }

    #[\Override]
    public function getChannel(): string
    {
        return $this->channel;
    }

    #[\Override]
    public function isListening(): bool
    {
        return $this->unlisten !== null;
    }

    /**
     * Unlistens from the channel. No more values will be emitted from this listener.
     *
     * @throws \Error If this method was previously invoked.
     */
    #[\Override]
    public function unlisten(): void
    {
        if (!$this->unlisten) {
            return;
        }

        $unlisten = $this->unlisten;
        $this->unlisten = null;

        $unlisten($this->channel);
    }
}
<?php declare(strict_types=1);

namespace Amp\Postgres\Internal;

use Amp\DeferredFuture;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Pipeline\Queue;
use Amp\Postgres\PostgresConfig;
use Amp\Sql\SqlConnectionException;
use Revolt\EventLoop;

/**
 * @internal
 */
abstract class AbstractHandle implements PostgresHandle
{
    use ForbidCloning;
    use ForbidSerialization;

    protected ?DeferredFuture $pendingOperation = null;

    /** @var array<non-empty-string, Queue> */
    protected array $listeners = [];

    protected int $lastUsedAt = 0;

    public function __construct(
        private readonly PostgresConfig $config,
        protected readonly string $poll,
        protected readonly string $await,
        private readonly DeferredFuture $onClose,
    ) {
        $this->lastUsedAt = \time();
    }

    public function __destruct()
    {
        if (!$this->isClosed()) {
            $this->close();
        }
    }

    #[\Override]
    public function getConfig(): PostgresConfig
    {
        return $this->config;
    }

    #[\Override]
    public function getLastUsedAt(): int
    {
        return $this->lastUsedAt;
    }

    #[\Override]
    public function onClose(\Closure $onClose): void
    {
        $this->onClose->getFuture()->finally($onClose);
    }

    #[\Override]
    public function close(): void
    {
        self::shutdown($this->listeners, $this->pendingOperation, $this->onClose);

        EventLoop::cancel($this->poll);
        EventLoop::cancel($this->await);
    }

    /**
     * @param array<non-empty-string, Queue> $listeners
     */
    protected static function shutdown(
        array &$listeners,
        ?DeferredFuture &$pendingOperation,
        DeferredFuture $onClose,
        ?\Throwable $exception = null,
    ): void {
        if (!empty($listeners)) {
            $exception ??= new SqlConnectionException("The connection was closed");
            foreach ($listeners as $listener) {
                $listener->error($exception);
            }
            $listeners = [];
        }

        $pendingOperation?->error($exception ?? new SqlConnectionException("The connection was closed"));
        $pendingOperation = null;

        if (!$onClose->isComplete()) {
            $onClose->complete();
        }
    }

    protected function encodeParam(mixed $value): string|int|float|null
    {
        return encodeParam($this, $value);
    }

    #[\Override]
    public function commit(): void
    {
        $this->query("COMMIT");
    }

    #[\Override]
    public function rollback(): void
    {
        $this->query("ROLLBACK");
    }

    #[\Override]
    public function createSavepoint(string $identifier): void
    {
        $this->query("SAVEPOINT " . $this->quoteIdentifier($identifier));
    }

    #[\Override]
    public function rollbackTo(string $identifier): void
    {
        $this->query("ROLLBACK TO " . $this->quoteIdentifier($identifier));
    }

    #[\Override]
    public function releaseSavepoint(string $identifier): void
    {
        $this->query("RELEASE SAVEPOINT " . $this->quoteIdentifier($identifier));
    }
}
<?php declare(strict_types=1);

namespace Amp\Postgres\Internal;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Future;
use Amp\Postgres\PostgresResult;

/**
 * @internal
 * @psalm-import-type TRowType from PostgresResult
 * @implements \IteratorAggregate<int, TRowType>
 */
final class PgSqlResultSet implements PostgresResult, \IteratorAggregate
{
    use ForbidCloning;
    use ForbidSerialization;

    private readonly \Iterator $iterator;

    private readonly int $rowCount;

    private readonly int $columnCount;

    /**
     * @param array<int, PgSqlType> $types
     * @param Future<PostgresResult|null> $nextResult
     */
    public function __construct(
        \PgSql\Result $handle,
        array $types,
        private readonly Future $nextResult,
    ) {
        $this->rowCount = \pg_num_rows($handle);
        $this->columnCount = \pg_num_fields($handle);

        $this->iterator = PgSqlResultIterator::iterate($handle, $types);
    }

    #[\Override]
    public function fetchRow(): ?array
    {
        if (!$this->iterator->valid()) {
            return null;
        }

        $current = $this->iterator->current();
        $this->iterator->next();
        return $current;
    }

    #[\Override]
    public function getIterator(): \Traversable
    {
        return $this->iterator;
    }

    #[\Override]
    public function getNextResult(): ?PostgresResult
    {
        return $this->nextResult->await();
    }

    /**
     * @return int Number of rows returned.
     */
    #[\Override]
    public function getRowCount(): int
    {
        return $this->rowCount;
    }

    /**
     * @return int Number of columns returned.
     */
    #[\Override]
    public function getColumnCount(): int
    {
        return $this->columnCount;
    }
}
<?php declare(strict_types=1);

namespace Amp\Postgres\Internal;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Postgres\PostgresParseException;
use Amp\Postgres\PostgresResult;
use Amp\Sql\SqlException;

/**
 * @internal
 * @psalm-import-type TRowType from PostgresResult
 */
final class PgSqlResultIterator
{
    use ForbidCloning;
    use ForbidSerialization;

    /**
     * @param array<int, PgSqlType> $types
     *
     * @return \Iterator<int, TRowType>
     */
    public static function iterate(\PgSql\Result $handle, array $types): \Iterator
    {
        return (new self($handle, $types))->getIterator();
    }

    /**
     * @param array<int, PgSqlType> $types
     */
    private function __construct(
        private readonly \PgSql\Result $handle,
        private readonly array $types,
    ) {
    }

    private function getIterator(): \Iterator
    {
        $fieldNames = [];
        $fieldTypes = [];
        $numFields = \pg_num_fields($this->handle);
        for ($i = 0; $i < $numFields; ++$i) {
            $fieldNames[] = \pg_field_name($this->handle, $i);
            $fieldTypes[] = \pg_field_type_oid($this->handle, $i);
        }

        $position = 0;

        try {
            while (++$position <= \pg_num_rows($this->handle)) {
                /** @var list<string|null>|false $result */
                $result = \pg_fetch_array($this->handle, mode: \PGSQL_NUM);

                if ($result === false) {
                    throw new SqlException(\pg_result_error($this->handle) ?: 'Unknown result error');
                }

                /** @var list<int> $fieldTypes */
                yield \array_combine($fieldNames, \array_map($this->cast(...), $fieldTypes, $result));
            }
        } finally {
            /** @psalm-suppress UnusedFunctionCall */
            \pg_free_result($this->handle);
        }
    }

    /**
     * @see https://github.com/postgres/postgres/blob/REL_14_STABLE/src/include/catalog/pg_type.dat for OID types.
     * @see https://www.postgresql.org/docs/14/catalog-pg-type.html for pg_type catalog docs.
     *
     * @return list<mixed>|bool|int|float|string|null
     *
     * @throws PostgresParseException
     */
    private function cast(int $oid, ?string $value): array|bool|int|float|string|null
    {
        if ($value === null) {
            return null;
        }

        $type = $this->types[$oid] ?? PgSqlType::getDefaultType();

        return match ($type->category) {
            'A' => match ($type->name) { // Array
                'int2vector', 'oidvector' => $value, // Deprecated array types
                default => ArrayParser::parse(
                    $value,
                    fn (string $data) => $this->cast($type->element, $data),
                    $type->delimiter,
                ),
            },
            'B' => match ($value) { // Boolean
                't' => true,
                'f' => false,
                default => throw new PostgresParseException('Unexpected value for boolean field: ' . $value),
            },
            'N' => match ($type->name) { // Numeric
                'float4', 'float8' => (float) $value,
                'int2', 'int4', 'oid' => (int) $value,
                'int8' => \PHP_INT_SIZE >= 8 ? (int) $value : $value, // String on 32-bit systems
                default => $value, // Return a string for all other numeric types
            },
            'U' => match ($type->name) {
                'bytea' => \pg_unescape_bytea($value),
                default => $value,
            },
            default => $value, // Return a string for all other types
        };
    }
}
<?php declare(strict_types=1);

namespace Amp\Postgres\Internal;

use Amp\Postgres\PostgresResult;
use Amp\Sql\Common\SqlPooledResult;
use Amp\Sql\SqlResult;

/**
 * @internal
 * @psalm-import-type TFieldType from PostgresResult
 * @extends SqlPooledResult<TFieldType, PostgresResult>
 */
final class PostgresPooledResult extends SqlPooledResult implements PostgresResult
{
    #[\Override]
    protected static function newInstanceFrom(SqlResult $result, \Closure $release): self
    {
        \assert($result instanceof PostgresResult);
        return new self($result, $release);
    }

    #[\Override]
    public function getNextResult(): ?PostgresResult
    {
        return parent::getNextResult();
    }
}
<?php declare(strict_types=1);

namespace Amp\Postgres\Internal;

use Amp\Postgres\PostgresExecutor;
use Amp\Postgres\PostgresResult;
use Amp\Postgres\PostgresStatement;
use Amp\Postgres\PostgresTransaction;
use Amp\Sql\Common\SqlNestableTransactionExecutor;
use Amp\Sql\Common\SqlNestedTransaction;
use Amp\Sql\SqlTransaction;

/**
 * @internal
 * @extends SqlNestedTransaction<PostgresResult, PostgresStatement, PostgresTransaction, PostgresHandle>
 */
final class PostgresNestedTransaction extends SqlNestedTransaction implements PostgresTransaction
{
    use PostgresTransactionDelegate;

    /**
     * @param non-empty-string $identifier
     * @param \Closure():void $release
     */
    public function __construct(
        private readonly PostgresTransaction $transaction,
        PostgresHandle $handle,
        string $identifier,
        \Closure $release,
    ) {
        parent::__construct($transaction, $handle, $identifier, $release);
    }

    #[\Override]
    protected function getExecutor(): PostgresExecutor
    {
        return $this->transaction;
    }

    #[\Override]
    protected function createNestedTransaction(
        SqlTransaction $transaction,
        SqlNestableTransactionExecutor $executor,
        string $identifier,
        \Closure $release,
    ): PostgresTransaction {
        return new self($transaction, $executor, $identifier, $release);
    }

    #[\Override]
    public function prepare(string $sql): PostgresStatement
    {
        $statement = parent::prepare($sql);

        // Defer statement deallocation until parent is committed or rolled back.
        $this->transaction->onClose(static fn () => $statement);

        return $statement;
    }
}
<?php declare(strict_types=1);

namespace Amp\Postgres\Internal;

/** @internal */
final class PgSqlType
{
    private static ?self $default = null;

    public function __construct(
        public readonly string $category,
        public readonly string $name,
        public readonly string $delimiter,
        public readonly int $element,
    ) {
    }

    public static function getDefaultType(): self
    {
        return self::$default ??= new self('S', 'text', ',', 0);
    }
}
<?php declare(strict_types=1);

namespace Amp\Postgres\Internal;

use Amp\Postgres\PostgresExecutor;
use Amp\Postgres\PostgresResult;
use Amp\Postgres\PostgresStatement;
use Amp\Postgres\PostgresTransaction;
use Amp\Sql\Common\SqlPooledTransaction;
use Amp\Sql\SqlTransaction;

/**
 * @internal
 * @extends SqlPooledTransaction<PostgresResult, PostgresStatement, PostgresTransaction>
 */
final class PostgresPooledTransaction extends SqlPooledTransaction implements PostgresTransaction
{
    use PostgresTransactionDelegate;

    /**
     * @param \Closure():void $release
     */
    public function __construct(private readonly PostgresTransaction $transaction, \Closure $release)
    {
        parent::__construct($transaction, $release);
    }

    #[\Override]
    protected function getExecutor(): PostgresExecutor
    {
        return $this->transaction;
    }

    #[\Override]
    protected function createTransaction(SqlTransaction $transaction, \Closure $release): PostgresTransaction
    {
        \assert($transaction instanceof PostgresTransaction);
        return new PostgresPooledTransaction($transaction, $release);
    }
}
<?php declare(strict_types=1);

namespace Amp\Postgres\Internal;

use Amp\DeferredFuture;
use Amp\Future;
use Amp\Pipeline\Queue;
use Amp\Postgres\PostgresConfig;
use Amp\Postgres\PostgresListener;
use Amp\Postgres\PostgresNotification;
use Amp\Postgres\PostgresQueryError;
use Amp\Postgres\PostgresResult;
use Amp\Postgres\PostgresStatement;
use Amp\Sql\SqlConnectionException;
use Amp\Sql\SqlException;
use Amp\Sql\SqlQueryError;
use Revolt\EventLoop;
use function Amp\async;

/**
 * @internal
 *
 * @psalm-type PgSqlTypeMap = array<int, PgSqlType> Map of OID to corresponding PgSqlType.
 */
final class PgSqlHandle extends AbstractHandle
{
    private const TYPE_QUERY = <<<SQL
        SELECT t.oid, t.typcategory, t.typname, t.typdelim, t.typelem
        FROM pg_catalog.pg_type t JOIN pg_catalog.pg_namespace n ON t.typnamespace=n.oid
        WHERE t.typisdefined AND n.nspname IN ('pg_catalog', 'public') ORDER BY t.oid
        SQL;

    private const DIAGNOSTIC_CODES = [
        \PGSQL_DIAG_SEVERITY => "severity",
        \PGSQL_DIAG_SQLSTATE => "sqlstate",
        \PGSQL_DIAG_MESSAGE_PRIMARY => "message_primary",
        \PGSQL_DIAG_MESSAGE_DETAIL => "message_detail",
        \PGSQL_DIAG_MESSAGE_HINT => "message_hint",
        \PGSQL_DIAG_STATEMENT_POSITION => "statement_position",
        \PGSQL_DIAG_INTERNAL_POSITION => "internal_position",
        \PGSQL_DIAG_INTERNAL_QUERY => "internal_query",
        \PGSQL_DIAG_CONTEXT => "context",
        \PGSQL_DIAG_SOURCE_FILE => "source_file",
        \PGSQL_DIAG_SOURCE_LINE => "source_line",
        \PGSQL_DIAG_SOURCE_FUNCTION => "source_function",
    ];

    /** @var array<string, Future<PgSqlTypeMap>> */
    private static array $typeCache;

    private static ?\Closure $errorHandler = null;

    /** @var \PgSql\Connection PostgreSQL connection handle. */
    private ?\PgSql\Connection $handle;

    /** @var PgSqlTypeMap|null */
    private ?array $types = null;

    /** @var array<non-empty-string, StatementStorage<string>> */
    private array $statements = [];

    /**
     * @param \PgSql\Connection $connection PostgreSQL connection handle.
     * @param resource $socket PostgreSQL connection stream socket.
     * @param string $id Connection identifier for determining which cached type table to use.
     */
    public function __construct(
        \PgSql\Connection $connection,
        $socket,
        private readonly string $id,
        PostgresConfig $config,
    ) {
        $this->handle = $connection;

        $connection = &$this->handle;
        $lastUsedAt = &$this->lastUsedAt;
        $deferred = &$this->pendingOperation;
        $listeners = &$this->listeners;
        $onClose = new DeferredFuture();

        $poll = EventLoop::onReadable($socket, static function (string $watcher) use (
            &$deferred,
            &$lastUsedAt,
            &$listeners,
            &$connection,
            $onClose,
        ): void {
            if (!$connection) {
                EventLoop::disable($watcher);
                return;
            }

            $lastUsedAt = \time();

            \set_error_handler(self::getErrorHandler());

            try {
                if (\pg_connection_status($connection) !== \PGSQL_CONNECTION_OK) {
                    throw new SqlConnectionException("The connection closed during the operation");
                }

                if (!\pg_consume_input($connection)) {
                    throw new SqlConnectionException(\pg_last_error($connection));
                }

                while ($result = \pg_get_notify($connection, \PGSQL_ASSOC)) {
                    $channel = $result["message"];

                    if (!isset($listeners[$channel])) {
                        continue;
                    }

                    $notification = new PostgresNotification($channel, $result["pid"], $result["payload"]);
                    $listeners[$channel]->pushAsync($notification)->ignore();
                }

                if ($deferred === null) {
                    return; // No active query, only notification listeners.
                }

                if (\pg_connection_busy($connection)) {
                    return;
                }

                $deferred->complete(\pg_get_result($connection));
                $deferred = null;

                if (empty($listeners)) {
                    EventLoop::unreference($watcher);
                }
            } catch (SqlConnectionException $exception) {
                $connection = null; // Marks connection as dead.
                EventLoop::disable($watcher);

                self::shutdown($listeners, $deferred, $onClose, $exception);
            } finally {
                \restore_error_handler();
            }
        });

        $await = EventLoop::onWritable($socket, static function (string $watcher) use (
            &$deferred,
            &$listeners,
            &$connection,
            $onClose,
        ): void {
            if (!$connection) {
                EventLoop::disable($watcher);
                return;
            }

            \set_error_handler(self::getErrorHandler());

            try {
                $flush = \pg_flush($connection);
                if ($flush === 0) {
                    return; // Not finished sending data, listen again.
                }

                EventLoop::disable($watcher);

                if ($flush === false) {
                    throw new SqlConnectionException(\pg_last_error($connection));
                }
            } catch (SqlConnectionException $exception) {
                $connection = null; // Marks connection as dead.
                EventLoop::disable($watcher);

                self::shutdown($listeners, $deferred, $onClose, $exception);
            } finally {
                \restore_error_handler();
            }
        });

        EventLoop::unreference($poll);
        EventLoop::disable($await);

        parent::__construct($config, $poll, $await, $onClose);
    }

    /**
     * @return Future<PgSqlTypeMap>
     */
    private function fetchTypes(): Future
    {
        if (isset(self::$typeCache[$this->id])) {
            return self::$typeCache[$this->id];
        }

        \assert($this->pendingOperation === null, 'Operation pending when fetching types!');

        if ($this->handle === null) {
            throw new \Error("The connection to the database has been closed");
        }

        $result = \pg_send_query($this->handle, self::TYPE_QUERY);
        if ($result === false) {
            $this->close();
            throw new SqlException(\pg_last_error($this->handle));
        }

        $this->pendingOperation = $queryDeferred = new DeferredFuture();
        $typesDeferred = new DeferredFuture();

        EventLoop::reference($this->poll);
        if ($result === 0) {
            EventLoop::enable($this->await);
        }

        EventLoop::queue(function () use ($queryDeferred, $typesDeferred): void {
            try {
                $result = $queryDeferred->getFuture()->await();
                if (\pg_result_status($result) !== \PGSQL_TUPLES_OK) {
                    throw new SqlException(\pg_result_error($result) ?: 'Unknown result error');
                }

                $types = [];
                while ($row = \pg_fetch_array($result, mode: \PGSQL_NUM)) {
                    [$oid, $category, $name, $delimiter, $element] = $row;

                    \assert(
                        \is_numeric($oid) && \is_numeric($element),
                        "OID and element type expected to be integers",
                    );
                    \assert( // For Psalm
                        \is_string($category) && \is_string($name) && \is_string($delimiter),
                        "Unexpected nulls in type catalog query results",
                    );

                    $types[(int) $oid] = new PgSqlType($category, $name, $delimiter, (int) $element);
                }

                $typesDeferred->complete($types);
            } catch (\Throwable $exception) {
                $this->close();
                $typesDeferred->error($exception);
                unset(self::$typeCache[$this->id]);
            }
        });

        return self::$typeCache[$this->id] = $typesDeferred->getFuture();
    }

    private static function getErrorHandler(): \Closure
    {
        return self::$errorHandler ??= static function (int $code, string $message): never {
            throw new SqlConnectionException($message, $code);
        };
    }

    #[\Override]
    public function close(): void
    {
        $this->handle = null;
        parent::close();
    }

    #[\Override]
    public function isClosed(): bool
    {
        return !$this->handle instanceof \PgSql\Connection;
    }

    /**
     * @param \Closure $function Function to execute.
     * @param mixed ...$args Arguments to pass to function.
     *
     * @throws SqlException
     */
    private function send(\Closure $function, mixed ...$args): mixed
    {
        $this->types ??= $this->fetchTypes()->await();

        while ($this->pendingOperation) {
            try {
                $this->pendingOperation->getFuture()->await();
            } catch (\Throwable) {
                // Ignore failure from another operation.
            }
        }

        if ($this->handle === null) {
            throw new SqlConnectionException("The connection to the database has been closed");
        }

        while ($result = \pg_get_result($this->handle)) {
            /** @psalm-suppress UnusedFunctionCall */
            \pg_free_result($result);
        }

        $result = $function($this->handle, ...$args);

        if ($result === false) {
            throw new SqlException(\pg_last_error($this->handle));
        }

        $this->pendingOperation = new DeferredFuture;

        EventLoop::reference($this->poll);
        if ($result === 0) {
            EventLoop::enable($this->await);
        }

        return $this->pendingOperation->getFuture()->await();
    }

    /**
     * @param \PgSql\Result $result PostgreSQL result resource.
     * @param string $sql Query SQL.
     *
     * @throws SqlException
     * @throws SqlQueryError
     */
    private function createResult(\PgSql\Result $result, string $sql): PostgresResult
    {
        if ($this->handle === null) {
            throw new \Error("The connection to the database has been closed");
        }

        \assert($this->types !== null, 'Expected type array to be populated before creating a result');

        switch ($status = \pg_result_status($result)) {
            case \PGSQL_EMPTY_QUERY:
                throw new SqlQueryError("Empty query string");

            case \PGSQL_COMMAND_OK:
                return new PostgresCommandResult(
                    \pg_affected_rows($result),
                    Future::complete($this->fetchNextResult($sql)),
                );

            case \PGSQL_TUPLES_OK:
                return new PgSqlResultSet($result, $this->types, Future::complete($this->fetchNextResult($sql)));

            case \PGSQL_NONFATAL_ERROR:
            case \PGSQL_FATAL_ERROR:
                $diagnostics = [];
                foreach (self::DIAGNOSTIC_CODES as $fieldCode => $description) {
                    $diagnostics[$description] = \pg_result_error_field($result, $fieldCode);
                }
                $message = \pg_result_error($result) ?: 'Unknown result error';
                \set_error_handler(self::getErrorHandler());
                try {
                    while (\pg_connection_busy($this->handle) && \pg_get_result($this->handle)) {
                        // Clear all outstanding result rows from the connection
                    }
                } finally {
                    \restore_error_handler();
                    throw new PostgresQueryError($message, $diagnostics, $sql);
                }

            case \PGSQL_BAD_RESPONSE:
                $this->close();
                throw new SqlException(\pg_result_error($result) ?: 'Unknown result error');

            default:
                // @codeCoverageIgnoreStart
                $this->close();
                throw new SqlException(\sprintf(
                    "Unknown result status: %d; error: %s",
                    $status,
                    \pg_result_error($result) ?: 'none',
                ));
                // @codeCoverageIgnoreEnd
        }
    }

    /**
     * @throws SqlException
     */
    private function fetchNextResult(string $sql): ?PostgresResult
    {
        if ($this->handle === null) {
            throw new \Error("The connection to the database has been closed");
        }

        if ($result = \pg_get_result($this->handle)) {
            return $this->createResult($result, $sql);
        }

        return null;
    }

    #[\Override]
    public function statementExecute(string $name, array $params): PostgresResult
    {
        \assert(isset($this->statements[$name]), "Named statement not found when executing");
        $result = $this->send(\pg_send_execute(...), $name, \array_map($this->encodeParam(...), $params));
        return $this->createResult($result, $this->statements[$name]->sql);
    }

    /**
     * @throws \Error
     */
    #[\Override]
    public function statementDeallocate(string $name): void
    {
        if ($this->isClosed()) {
            return; // Connection closed, no need to deallocate.
        }

        \assert(isset($this->statements[$name]), "Named statement not found when deallocating");

        $storage = $this->statements[$name];

        if (--$storage->refCount) {
            return;
        }

        $future = $storage->future;
        $storage->future = async(function () use ($future, $name): void {
            if (!$future->await()) {
                return; // Statement already deallocated.
            }

            $this->query(\sprintf("DEALLOCATE %s", $name));
            unset($this->statements[$name]);
        });
        $storage->future->ignore();
    }

    #[\Override]
    public function escapeByteA(string $data): string
    {
        if ($this->handle === null) {
            throw new \Error("The connection to the database has been closed");
        }

        return \pg_escape_bytea($this->handle, $data);
    }

    #[\Override]
    public function query(string $sql): PostgresResult
    {
        if ($this->handle === null) {
            throw new \Error("The connection to the database has been closed");
        }

        return $this->createResult($this->send(\pg_send_query(...), $sql), $sql);
    }

    #[\Override]
    public function execute(string $sql, array $params = []): PostgresResult
    {
        if ($this->handle === null) {
            throw new \Error("The connection to the database has been closed");
        }

        $sql = parseNamedParams($sql, $names);
        $params = replaceNamedParams($params, $names);

        $result = $this->send(
            \pg_send_query_params(...),
            $sql,
            \array_map($this->encodeParam(...), $params)
        );

        return $this->createResult($result, $sql);
    }

    #[\Override]
    public function prepare(string $sql): PostgresStatement
    {
        if ($this->handle === null) {
            throw new \Error("The connection to the database has been closed");
        }

        $modifiedSql = parseNamedParams($sql, $names);

        $name = self::STATEMENT_NAME_PREFIX . \sha1($modifiedSql);

        while (isset($this->statements[$name])) {
            $storage = $this->statements[$name];

            ++$storage->refCount;
            // Do not return promised prepared statement object, as the $names array may differ.
            $result = $storage->future->await();

            if ($result) { // Null returned if future was from deallocation.
                return new PostgresConnectionStatement($this, $name, $sql, $names);
            }
        }

        $future = async(function () use ($name, $modifiedSql, $sql): string {
            $result = $this->send(\pg_send_prepare(...), $name, $modifiedSql);

            switch ($status = \pg_result_status($result)) {
                case \PGSQL_COMMAND_OK:
                    return $name; // Statement created successfully.

                case \PGSQL_NONFATAL_ERROR:
                case \PGSQL_FATAL_ERROR:
                    $diagnostics = [];
                    foreach (self::DIAGNOSTIC_CODES as $fieldCode => $description) {
                        $diagnostics[$description] = \pg_result_error_field($result, $fieldCode);
                    }
                    throw new PostgresQueryError(
                        \pg_result_error($result) ?: 'Unknown result error',
                        $diagnostics,
                        $sql,
                    );

                case \PGSQL_BAD_RESPONSE:
                    throw new SqlException(\pg_result_error($result) ?: 'Unknown result error');

                default:
                    // @codeCoverageIgnoreStart
                    throw new SqlException(\sprintf(
                        "Unknown result status: %d; error: %s",
                        $status,
                        \pg_result_error($result) ?: 'Unknown result error',
                    ));
                    // @codeCoverageIgnoreEnd
            }
        });

        $storage = new StatementStorage($sql, $future);
        $this->statements[$name] = $storage;

        try {
            $storage->future->await();
        } catch (\Throwable $exception) {
            unset($this->statements[$name]);
            throw $exception;
        }

        return new PostgresConnectionStatement($this, $name, $sql, $names);
    }

    #[\Override]
    public function notify(string $channel, string $payload = ""): PostgresResult
    {
        if ($payload === "") {
            return $this->query(\sprintf("NOTIFY %s", $this->quoteIdentifier($channel)));
        }

        return $this->query(\sprintf("NOTIFY %s, %s", $this->quoteIdentifier($channel), $this->quoteLiteral($payload)));
    }

    #[\Override]
    public function listen(string $channel): PostgresListener
    {
        if (isset($this->listeners[$channel])) {
            throw new SqlQueryError(\sprintf("Already listening on channel '%s'", $channel));
        }

        $this->listeners[$channel] = $source = new Queue();

        try {
            $this->query(\sprintf("LISTEN %s", $this->quoteIdentifier($channel)));
        } catch (\Throwable $exception) {
            unset($this->listeners[$channel]);
            throw $exception;
        }

        EventLoop::enable($this->poll);
        return new PostgresConnectionListener($source->iterate(), $channel, $this->unlisten(...));
    }

    /**
     * @throws \Error
     */
    private function unlisten(string $channel): void
    {
        if (!isset($this->listeners[$channel])) {
            return;
        }

        $source = $this->listeners[$channel];
        unset($this->listeners[$channel]);

        if ($this->handle === null) {
            $source->complete();
            return; // Connection already closed.
        }

        try {
            $this->query(\sprintf("UNLISTEN %s", $this->quoteIdentifier($channel)));
            $source->complete();
        } catch (\Throwable $exception) {
            $source->error($exception);
        }
    }

    #[\Override]
    public function quoteLiteral(string $data): string
    {
        if ($this->handle === null) {
            throw new \Error("The connection to the database has been closed");
        }

        return \pg_escape_literal($this->handle, $data);
    }

    #[\Override]
    public function quoteIdentifier(string $name): string
    {
        if ($this->handle === null) {
            throw new \Error("The connection to the database has been closed");
        }

        return \pg_escape_identifier($this->handle, $name);
    }
}
<?php declare(strict_types=1);

namespace Amp\Postgres\Internal;

use Amp\Postgres\PostgresResult;
use Amp\Postgres\PostgresStatement;
use Amp\Sql\Common\SqlPooledStatement;
use Amp\Sql\SqlResult;

/**
 * @internal
 * @extends SqlPooledStatement<PostgresResult, PostgresStatement>
 */
final class PostgresPooledStatement extends SqlPooledStatement implements PostgresStatement
{
    #[\Override]
    protected function createResult(SqlResult $result, \Closure $release): PostgresResult
    {
        \assert($result instanceof PostgresResult);
        return new PostgresPooledResult($result, $release);
    }

    /**
     * Changes return type to this library's Result type.
     */
    #[\Override]
    public function execute(array $params = []): PostgresResult
    {
        return parent::execute($params);
    }
}
<?php declare(strict_types=1);

namespace Amp\Postgres\Internal;

use Amp\Postgres\PostgresResult;
use Amp\Sql\Common\SqlCommandResult;

/**
 * @internal
 * @psalm-import-type TFieldType from PostgresResult
 * @extends SqlCommandResult<TFieldType, PostgresResult>
 */
final class PostgresCommandResult extends SqlCommandResult implements PostgresResult
{
    /**
     * Changes return type to this library's Result type.
     */
    #[\Override]
    public function getNextResult(): ?PostgresResult
    {
        return parent::getNextResult();
    }
}
<?php declare(strict_types=1);

namespace Amp\Postgres\Internal;

use Amp\Postgres\PostgresConfig;
use Amp\Postgres\PostgresResult;
use Amp\Postgres\PostgresStatement;
use Amp\Postgres\PostgresTransaction;
use Amp\Sql\Common\SqlStatementPool as SqlStatementPool;
use Amp\Sql\SqlResult as SqlResult;

/**
 * @internal
 * @extends SqlStatementPool<PostgresConfig, PostgresResult, PostgresStatement, PostgresTransaction>
 */
final class PostgresStatementPool extends SqlStatementPool implements PostgresStatement
{
    #[\Override]
    protected function createResult(SqlResult $result, \Closure $release): PostgresResult
    {
        \assert($result instanceof PostgresResult);
        return new PostgresPooledResult($result, $release);
    }

    #[\Override]
    public function execute(array $params = []): PostgresResult
    {
        return parent::execute($params);
    }
}
<?php declare(strict_types=1);

namespace Amp\Postgres\Internal;

use Amp\Cancellation;
use Amp\DeferredFuture;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Postgres\Internal;
use Amp\Postgres\PostgresConfig;
use Amp\Postgres\PostgresConnection;
use Amp\Postgres\PostgresListener;
use Amp\Postgres\PostgresResult;
use Amp\Postgres\PostgresStatement;
use Amp\Postgres\PostgresTransaction;
use Amp\Sql\SqlConnectionException;
use Amp\Sql\SqlTransactionIsolation;
use Amp\Sql\SqlTransactionIsolationLevel;

/** @internal */
abstract class PostgresHandleConnection implements PostgresConnection
{
    use ForbidCloning;
    use ForbidSerialization;

    /** @var DeferredFuture|null Used to only allow one transaction at a time. */
    private ?DeferredFuture $busy = null;

    private SqlTransactionIsolation $transactionIsolation = SqlTransactionIsolationLevel::Committed;

    /**
     * @throws SqlConnectionException
     */
    abstract public static function connect(
        PostgresConfig $config,
        ?Cancellation $cancellation = null,
    ): self;

    protected function __construct(private readonly PostgresHandle $handle)
    {
    }

    #[\Override]
    final public function getConfig(): PostgresConfig
    {
        return $this->handle->getConfig();
    }

    #[\Override]
    final public function getLastUsedAt(): int
    {
        return $this->handle->getLastUsedAt();
    }

    #[\Override]
    final public function close(): void
    {
        $this->handle->close();
    }

    #[\Override]
    final public function isClosed(): bool
    {
        return $this->handle->isClosed();
    }

    #[\Override]
    final public function onClose(\Closure $onClose): void
    {
        $this->handle->onClose($onClose);
    }

    private function awaitPending(): void
    {
        while ($this->busy) {
            $this->busy->getFuture()->await();
        }
    }

    /**
     * Reserves the connection for a transaction.
     */
    private function reserve(): void
    {
        \assert($this->busy === null);
        $this->busy = new DeferredFuture;
    }

    /**
     * Releases the transaction lock.
     */
    private function release(): void
    {
        \assert($this->busy !== null);

        $this->busy->complete();
        $this->busy = null;
    }

    #[\Override]
    final public function query(string $sql): PostgresResult
    {
        $this->awaitPending();
        return $this->handle->query($sql);
    }

    #[\Override]
    final public function execute(string $sql, array $params = []): PostgresResult
    {
        $this->awaitPending();
        return $this->handle->execute($sql, $params);
    }

    #[\Override]
    final public function prepare(string $sql): PostgresStatement
    {
        $this->awaitPending();
        return $this->handle->prepare($sql);
    }

    #[\Override]
    final public function notify(string $channel, string $payload = ""): PostgresResult
    {
        $this->awaitPending();
        return $this->handle->notify($channel, $payload);
    }

    #[\Override]
    final public function listen(string $channel): PostgresListener
    {
        $this->awaitPending();
        return $this->handle->listen($channel);
    }

    #[\Override]
    final public function beginTransaction(): PostgresTransaction
    {
        $this->reserve();

        try {
            $this->handle->query("BEGIN TRANSACTION ISOLATION LEVEL " . $this->transactionIsolation->toSql());
        } catch (\Throwable $exception) {
            $this->release();
            throw $exception;
        }

        return new Internal\PostgresConnectionTransaction(
            $this->handle,
            $this->release(...),
            $this->transactionIsolation,
        );
    }

    #[\Override]
    final public function getTransactionIsolation(): SqlTransactionIsolation
    {
        return $this->transactionIsolation;
    }

    #[\Override]
    final public function setTransactionIsolation(SqlTransactionIsolation $isolation): void
    {
        $this->transactionIsolation = $isolation;
    }

    #[\Override]
    final public function quoteLiteral(string $data): string
    {
        return $this->handle->quoteLiteral($data);
    }

    #[\Override]
    final public function quoteIdentifier(string $name): string
    {
        return $this->handle->quoteIdentifier($name);
    }

    #[\Override]
    final public function escapeByteA(string $data): string
    {
        return $this->handle->escapeByteA($data);
    }
}
<?php declare(strict_types=1);

namespace Amp\Postgres\Internal;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Future;
use Amp\Postgres\PostgresResult;
use pq;
use Revolt\EventLoop;

/**
 * @internal
 * @psalm-import-type TRowType from PostgresResult
 * @implements \IteratorAggregate<int, TRowType>
 */
final class PqUnbufferedResultSet implements PostgresResult, \IteratorAggregate
{
    use ForbidCloning;
    use ForbidSerialization;

    private readonly \Generator $generator;

    private readonly int $columnCount;

    /**
     * @param \Closure():(\pq\Result|null) $fetch Function to fetch next result row.
     * @param \pq\Result $result Initial pq\Result result object.
     * @param Future<PostgresResult|null> $nextResult
     */
    public function __construct(
        \Closure $fetch,
        pq\Result $result,
        private readonly Future $nextResult,
    ) {
        $this->columnCount = $result->numCols;

        $this->generator = self::generate($fetch, $result);
    }

    private static function generate(\Closure $fetch, pq\Result $result): \Generator
    {
        do {
            $result->autoConvert = pq\Result::CONV_SCALAR | pq\Result::CONV_ARRAY | pq\Result::CONV_BYTEA;
            yield $result->fetchRow(pq\Result::FETCH_ASSOC);
            $result = $fetch();
        } while ($result instanceof pq\Result);
    }

    public function __destruct()
    {
        EventLoop::queue(self::dispose(...), $this->generator);
    }

    private static function dispose(\Generator $generator): void
    {
        try {
            // Discard remaining rows in the result set.
            while ($generator->valid()) {
                $generator->next();
            }
        } catch (\Throwable) {
            // Ignore errors while discarding result.
        }
    }

    #[\Override]
    public function fetchRow(): ?array
    {
        if (!$this->generator->valid()) {
            return null;
        }

        $current = $this->generator->current();
        $this->generator->next();
        return $current;
    }

    #[\Override]
    public function getIterator(): \Traversable
    {
        // Using a Generator to keep a reference to $this.
        yield from $this->generator;
    }

    #[\Override]
    public function getNextResult(): ?PostgresResult
    {
        self::dispose($this->generator);

        return $this->nextResult->await();
    }

    #[\Override]
    public function getRowCount(): ?int
    {
        return null; // Unbuffered result sets do not have a total row count.
    }

    #[\Override]
    public function getColumnCount(): int
    {
        return $this->columnCount;
    }
}
<?php declare(strict_types=1);

namespace Amp\Postgres\Internal;

use Amp\Postgres\PostgresExecutor;
use Amp\Postgres\PostgresResult;
use Amp\Postgres\PostgresStatement;
use Amp\Postgres\PostgresTransaction;
use Amp\Sql\SqlResult;
use Amp\Sql\SqlStatement;

/** @internal */
trait PostgresTransactionDelegate
{
    abstract protected function getExecutor(): PostgresExecutor;

    /**
     * @param \Closure():void $release
     */
    protected function createStatement(
        SqlStatement $statement,
        \Closure $release,
        ?\Closure $awaitBusyResource = null,
    ): PostgresStatement {
        \assert($statement instanceof PostgresStatement);
        return new PostgresPooledStatement($statement, $release, $awaitBusyResource);
    }

    /**
     * @param \Closure():void $release
     */
    protected function createResult(SqlResult $result, \Closure $release): PostgresResult
    {
        \assert($result instanceof PostgresResult);
        return new PostgresPooledResult($result, $release);
    }

    /**
     * Changes return type to this library's Result type.
     */
    public function query(string $sql): PostgresResult
    {
        return parent::query($sql);
    }

    /**
     * Changes return type to this library's Statement type.
     */
    public function prepare(string $sql): PostgresStatement
    {
        return parent::prepare($sql);
    }

    /**
     * Changes return type to this library's Result type.
     */
    public function execute(string $sql, array $params = []): PostgresResult
    {
        return parent::execute($sql, $params);
    }

    /**
     * Changes return type to this library's Transaction type.
     */
    public function beginTransaction(): PostgresTransaction
    {
        return parent::beginTransaction();
    }

    /**
     * @param non-empty-string $channel
     */
    public function notify(string $channel, string $payload = ""): PostgresResult
    {
        return $this->getExecutor()->notify($channel, $payload);
    }

    public function quoteLiteral(string $data): string
    {
        return $this->getExecutor()->quoteLiteral($data);
    }

    public function quoteIdentifier(string $name): string
    {
        return $this->getExecutor()->quoteIdentifier($name);
    }

    public function escapeByteA(string $data): string
    {
        return $this->getExecutor()->escapeByteA($data);
    }
}
<?php declare(strict_types=1);

namespace Amp\Postgres\Internal;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Postgres\PostgresParseException;

/**
 * @internal
 */
final class ArrayParser
{
    use ForbidCloning;
    use ForbidSerialization;

    private const WHITESPACE_CHARS = " \n\r\t\v\0";

    /**
     * @param string $data String representation of PostgresSQL array.
     * @param \Closure(string):mixed $cast Callback to cast parsed values.
     * @param string $delimiter Delimiter used to separate values.
     *
     * @return list<mixed> Parsed column data.
     *
     * @throws PostgresParseException
     */
    public static function parse(string $data, \Closure $cast, string $delimiter = ','): array
    {
        $parser = new self($data, $cast, $delimiter);
        $result = $parser->parseToArray();

        if (isset($parser->data[$parser->position])) {
            throw new PostgresParseException("Data left in buffer after parsing");
        }

        return $result;
    }

    /**
     * @param string $data String representation of PostgresSQL array.
     * @param \Closure(string):mixed $cast Callback to cast parsed values.
     * @param string $delimiter Delimiter used to separate values.
     */
    private function __construct(
        private readonly string $data,
        private readonly \Closure $cast,
        private readonly string $delimiter,
        private int $position = 0,
    ) {
    }

    /**
     * @return list<mixed> Parsed column data.
     *
     * @throws PostgresParseException
     */
    private function parseToArray(): array
    {
        $result = [];

        $this->position = $this->skipWhitespace($this->position);

        if (!isset($this->data[$this->position])) {
            throw new PostgresParseException("Unexpected end of data");
        }

        if ($this->data[$this->position] !== '{') {
            throw new PostgresParseException("Missing opening bracket");
        }

        $this->position = $this->skipWhitespace($this->position + 1);

        do {
            if (!isset($this->data[$this->position])) {
                throw new PostgresParseException("Unexpected end of data");
            }

            if ($this->data[$this->position] === '}') { // Empty array
                $this->position = $this->skipWhitespace($this->position + 1);
                break;
            }

            if ($this->data[$this->position] === '{') { // Array
                $parser = new self($this->data, $this->cast, $this->delimiter, $this->position);
                $result[] = $parser->parseToArray();
                $this->position = $parser->position;
                $delimiter = $this->moveToNextDelimiter($this->position);
                continue;
            }

            if ($this->data[$this->position] === '"') { // Quoted value
                ++$this->position;
                for ($position = $this->position; isset($this->data[$position]); ++$position) {
                    if ($this->data[$position] === '\\') {
                        ++$position; // Skip next character
                        continue;
                    }

                    if ($this->data[$position] === '"') {
                        break;
                    }
                }

                if (!isset($this->data[$position])) {
                    throw new PostgresParseException("Could not find matching quote in quoted value");
                }

                $entry = \stripslashes(\substr($this->data, $this->position, $position - $this->position));

                $delimiter = $this->moveToNextDelimiter($position + 1);
            } else { // Unquoted value
                $position = $this->position;
                while (isset($this->data[$position])
                    && $this->data[$position] !== $this->delimiter
                    && $this->data[$position] !== '}'
                ) {
                    ++$position;
                }

                $entry = \trim(\substr($this->data, $this->position, $position - $this->position));

                $delimiter = $this->moveToNextDelimiter($position);

                if (\strcasecmp($entry, "NULL") === 0) { // Literal NULL is always unquoted.
                    $result[] = null;
                    continue;
                }
            }

            $result[] = ($this->cast)($entry);
        } while ($delimiter !== '}');

        return $result;
    }

    /**
     * @param int $position Position to start search for delimiter.
     *
     * @return string First non-whitespace character after given position.
     *
     * @throws PostgresParseException
     */
    private function moveToNextDelimiter(int $position): string
    {
        $position = $this->skipWhitespace($position);

        if (!isset($this->data[$position])) {
            throw new PostgresParseException("Unexpected end of data");
        }

        $delimiter = $this->data[$position];

        if ($delimiter !== $this->delimiter && $delimiter !== '}') {
            throw new PostgresParseException("Invalid delimiter");
        }

        $this->position = $this->skipWhitespace($position + 1);

        return $delimiter;
    }

    private function skipWhitespace(int $position): int
    {
        while (isset($this->data[$position]) && \str_contains(self::WHITESPACE_CHARS, $this->data[$position])) {
            ++$position;
        }

        return $position;
    }
}
<?php declare(strict_types=1);

namespace Amp\Postgres\Internal;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Future;

/**
 * @template T
 *
 * @internal
 */
final class StatementStorage
{
    use ForbidCloning;
    use ForbidSerialization;

    public int $refCount = 1;

    /**
     * @param Future<T>|Future<void> $future
     */
    public function __construct(
        public readonly string $sql,
        public Future $future,
    ) {
    }
}
<?php declare(strict_types=1);

namespace Amp\Postgres\Internal;

use Amp\Postgres\PostgresConfig;
use Amp\Postgres\PostgresExecutor;
use Amp\Postgres\PostgresListener;
use Amp\Postgres\PostgresResult;
use Amp\Postgres\PostgresStatement;
use Amp\Sql\Common\SqlNestableTransactionExecutor;

/**
 * @internal
 * @extends SqlNestableTransactionExecutor<PostgresResult, PostgresStatement>
 */
interface PostgresHandle extends PostgresExecutor, SqlNestableTransactionExecutor
{
    public const STATEMENT_NAME_PREFIX = "amp_";

    public function getConfig(): PostgresConfig;

    /**
     * @param non-empty-string $channel
     */
    public function listen(string $channel): PostgresListener;

    /**
     * Execute the statement with the given name and parameters.
     *
     * @param list<mixed> $params List of statement parameters, indexed starting at 0.
     */
    public function statementExecute(string $name, array $params): PostgresResult;

    /**
     * Deallocate the statement with the given name.
     */
    public function statementDeallocate(string $name): void;
}
<?php declare(strict_types=1);

namespace Amp\Postgres\Internal;

use Amp\Postgres\PostgresExecutor;
use Amp\Postgres\PostgresResult;
use Amp\Postgres\PostgresStatement;
use Amp\Postgres\PostgresTransaction;
use Amp\Sql\Common\SqlConnectionTransaction;
use Amp\Sql\Common\SqlNestableTransactionExecutor;
use Amp\Sql\SqlTransaction;
use Amp\Sql\SqlTransactionIsolation;

/**
 * @internal
 * @extends SqlConnectionTransaction<PostgresResult, PostgresStatement, PostgresTransaction, PostgresHandle>
 */
final class PostgresConnectionTransaction extends SqlConnectionTransaction implements PostgresTransaction
{
    use PostgresTransactionDelegate;

    public function __construct(
        private readonly PostgresHandle $handle,
        \Closure $release,
        SqlTransactionIsolation $isolation
    ) {
        parent::__construct($handle, $release, $isolation);
    }

    #[\Override]
    protected function createNestedTransaction(
        SqlTransaction $transaction,
        SqlNestableTransactionExecutor $executor,
        string $identifier,
        \Closure $release,
    ): PostgresTransaction {
        \assert($executor instanceof PostgresHandle);
        return new PostgresNestedTransaction($this, $executor, $identifier, $release);
    }

    #[\Override]
    protected function getExecutor(): PostgresExecutor
    {
        return $this->handle;
    }
}
<?php declare(strict_types=1);

namespace Amp\Postgres\Internal;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Postgres\PostgresListener;
use Amp\Postgres\PostgresNotification;
use Revolt\EventLoop;

/**
 * @internal
 * @implements \IteratorAggregate<int, PostgresNotification>
 */
final class PostgresPooledListener implements PostgresListener, \IteratorAggregate
{
    use ForbidCloning;
    use ForbidSerialization;

    private readonly PostgresListener $listener;

    /** @var null|\Closure():void  */
    private ?\Closure $release;

    /**
     * @param \Closure():void $release
     */
    public function __construct(PostgresListener $listener, \Closure $release)
    {
        $this->listener = $listener;
        $this->release = $release;

        if (!$this->listener->isListening()) {
            ($this->release)();
            $this->release = null;
        }
    }

    public function __destruct()
    {
        if ($this->listener->isListening() && $this->release) {
            EventLoop::queue(self::dispose(...), $this->listener, $this->release);
            $this->release = null;
        }
    }

    /**
     * @param \Closure():void $release
     */
    private static function dispose(PostgresListener $listener, \Closure $release): void
    {
        try {
            $listener->unlisten();
        } finally {
            EventLoop::queue($release);
        }
    }

    #[\Override]
    public function getIterator(): \Traversable
    {
        // Using a Generator to keep a reference to $this.
        yield from $this->listener;
    }

    #[\Override]
    public function getChannel(): string
    {
        return $this->listener->getChannel();
    }

    #[\Override]
    public function isListening(): bool
    {
        return $this->listener->isListening();
    }

    #[\Override]
    public function unlisten(): void
    {
        if (!$this->release) {
            return;
        }

        $release = $this->release;
        $this->release = null;

        try {
            $this->listener->unlisten();
        } finally {
            EventLoop::queue($release);
        }
    }
}
<?php declare(strict_types=1);

namespace Amp\Postgres\Internal;

use Amp\DeferredFuture;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Postgres\PostgresResult;
use Amp\Postgres\PostgresStatement;
use Amp\Sql\SqlException;

/** @internal  */
final class PostgresConnectionStatement implements PostgresStatement
{
    use ForbidCloning;
    use ForbidSerialization;

    private int $lastUsedAt;

    private readonly DeferredFuture $onClose;

    /**
     * @param string $name Statement name.
     * @param string $sql Original prepared SQL query.
     * @param list<int|string> $params Parameter indices to parameter names.
     */
    public function __construct(
        private readonly PostgresHandle $handle,
        private readonly string $name,
        private readonly string $sql,
        private readonly array $params,
    ) {
        $this->lastUsedAt = \time();
        $this->onClose = new DeferredFuture();
        $this->onClose(static fn () => $handle->statementDeallocate($name));
    }

    public function __destruct()
    {
        $this->close();
    }

    #[\Override]
    public function isClosed(): bool
    {
        return $this->onClose->isComplete();
    }

    #[\Override]
    public function close(): void
    {
        if (!$this->onClose->isComplete()) {
            $this->onClose->complete();
        }
    }

    #[\Override]
    public function onClose(\Closure $onClose): void
    {
        $this->onClose->getFuture()->finally($onClose);
    }

    #[\Override]
    public function getQuery(): string
    {
        return $this->sql;
    }

    #[\Override]
    public function getLastUsedAt(): int
    {
        return $this->lastUsedAt;
    }

    #[\Override]
    public function execute(array $params = []): PostgresResult
    {
        if ($this->isClosed()) {
            throw new SqlException('The statement has been closed or the connection went away');
        }

        $this->lastUsedAt = \time();
        return $this->handle->statementExecute($this->name, replaceNamedParams($params, $this->params));
    }
}
<?php declare(strict_types=1);

namespace Amp\Postgres\Internal;

use Amp\Postgres\PostgresArray;
use Amp\Postgres\PostgresByteA;
use Amp\Postgres\PostgresExecutor;

/** @internal */
const STATEMENT_PARAM_REGEX = <<<'REGEX'
[
    # Skip all quoted groups.
    (['"])(?:\\(?:\\|\1)|(?!\1).)*+\1(*SKIP)(*FAIL)
    |
    # Unnamed parameters.
    (?<unnamed>
        \$(?<numbered>\d+)
        |
        # Match all question marks except those surrounded by "operator"-class characters on either side.
        (?<!(?<operators>[-+\\*/<>~!@#%^&|`?]))
        \?
        (?!\g<operators>|=)
        |
        :\?
    )
    |
    # Named parameters.
    (?<!:):(?<named>[a-zA-Z_][a-zA-Z0-9_]*)
]msxS
REGEX;

/**
 * @internal
 * @psalm-suppress InvalidNullableReturnType, NullableReturnStatement, ReferenceConstraintViolation
 *
 * @param string $sql SQL statement with named and unnamed placeholders.
 * @param-out list<int|string> $names Array of parameter positions mapped to names and/or indexed locations.
 *
 * @return string SQL statement with Postgres-style placeholders
 */
function parseNamedParams(string $sql, ?array &$names): string
{
    $names = [];
    return \preg_replace_callback(STATEMENT_PARAM_REGEX, function (array $matches) use (&$names): string {
        static $index = 0, $unnamed = 0, $numbered = 1;

        if (isset($matches['named'])) {
            $names[$index] = $matches['named'];
        } elseif (!isset($matches['numbered'])) {
            $names[$index] = $unnamed++;
        } else {
            if ($unnamed > 0) {
                throw new \Error("Cannot mix unnamed (? placeholders) with numbered parameters");
            }

            $position = (int) $matches['numbered'];
            if ($position <= 0 || $position > $numbered + 1) {
                throw new \Error("Numbered placeholders must be sequential starting at 1");
            }

            $numbered = \max($position, $numbered);
            $names[$index] = $position - 1;
        }

        return '$' . ++$index;
    }, $sql);
}

/**
 * @internal
 *
 * @param array $params User-provided array of statement parameters.
 * @param list<int|string> $names Array generated by the $names param of {@see parseNamedParams()}.
 *
 * @return list<mixed>
 *
 * @throws \Error If the $param array does not contain a key corresponding to a named parameter.
 */
function replaceNamedParams(array $params, array $names): array
{
    $values = [];
    foreach ($names as $name) {
        if (!\array_key_exists($name, $params)) {
            if (\is_int($name)) {
                $message = \sprintf("Value for unnamed parameter at position %s missing", $name);
            } else {
                $message = \sprintf("Value for named parameter '%s' missing", $name);
            }

            throw new \Error($message);
        }

        $values[] = $params[$name];
    }

    return $values;
}

/**
 * @internal
 *
 * Casts a PHP value to a representation that is understood by Postgres, including encoding arrays.
 */
function encodeParam(PostgresExecutor $executor, mixed $value): string|int|float|null
{
    return match (\gettype($value)) {
        "NULL", "integer", "double", "string" => $value,
        "boolean" => $value ? 't' : 'f',
        "array" => encodeArray($executor, $value, ','),
        "object" => match (true) {
            $value instanceof PostgresArray => $value->encode($executor),
            $value instanceof PostgresByteA => $executor->escapeByteA($value->getData()),
            $value instanceof \BackedEnum => $value->value,
            $value instanceof \Stringable => (string) $value,
            default => throw new \TypeError(
                "An object in parameter values must be a PostgresByteA, a BackedEnum, or implement Stringable; "
                . "got instance of " . \get_debug_type($value)
            ),
        },
        default => throw new \TypeError(\sprintf(
            "Invalid value type '%s' in parameter values",
            \get_debug_type($value),
        )),
    };
}

/**
 * @internal
 */
function encodeArray(PostgresExecutor $executor, array $array, string $delimiter): string
{
    return '{' . \implode($delimiter, \array_map(fn ($i) => encodeArrayItem($executor, $i), $array)) . '}';
}

/**
 * @internal
 *
 * Wraps string in double-quotes for inclusion in an array.
 */
function encodeArrayItem(PostgresExecutor $executor, mixed $value): mixed
{
    return match (\gettype($value)) {
        "NULL" => "NULL",
        "string" => '"' . \str_replace(['\\', '"'], ['\\\\', '\\"'], $value) . '"',
        "array", "boolean", "integer", "double" => encodeParam($executor, $value),
        "object" => match (true) {
            $value instanceof PostgresArray => encodeParam($executor, $value),
            default => encodeArrayItem($executor, encodeParam($executor, $value)),
        },
        default => throw new \TypeError(\sprintf(
            "Invalid value type '%s' in array",
            \get_debug_type($value),
        )),
    };
}
<?php declare(strict_types=1);

namespace Amp\Postgres\Internal;

use Amp\DeferredFuture;
use Amp\Future;
use Amp\Pipeline\Queue;
use Amp\Postgres\PostgresConfig;
use Amp\Postgres\PostgresListener;
use Amp\Postgres\PostgresNotification;
use Amp\Postgres\PostgresQueryError;
use Amp\Postgres\PostgresResult;
use Amp\Postgres\PostgresStatement;
use Amp\Sql\SqlConnectionException;
use Amp\Sql\SqlException;
use Amp\Sql\SqlQueryError;
use pq;
use Revolt\EventLoop;
use function Amp\async;

/** @internal  */
final class PqHandle extends AbstractHandle
{
    private ?pq\Connection $handle;

    private ?DeferredFuture $busy = null;

    /** @var array<non-empty-string, StatementStorage<pq\Statement>> */
    private array $statements = [];

    /**
     * @psalm-suppress UnusedVariable
     */
    public function __construct(pq\Connection $handle, PostgresConfig $config)
    {
        $this->handle = $handle;

        $connection = &$this->handle;
        $lastUsedAt = &$this->lastUsedAt;
        $deferred = &$this->pendingOperation;
        $listeners = &$this->listeners;
        $onClose = new DeferredFuture();

        $poll = EventLoop::onReadable($this->handle->socket, static function (string $watcher) use (
            &$deferred,
            &$lastUsedAt,
            &$listeners,
            &$connection,
            $onClose,
        ): void {
            $lastUsedAt = \time();

            try {
                if ($connection->status !== pq\Connection::OK) {
                    throw new SqlConnectionException("The connection closed during the operation");
                }

                if ($connection->poll() === pq\Connection::POLLING_FAILED) {
                    throw new SqlConnectionException($connection->errorMessage);
                }
            } catch (SqlConnectionException $exception) {
                $connection = null; // Marks connection as dead.
                EventLoop::disable($watcher);

                self::shutdown($listeners, $deferred, $onClose, $exception);

                return;
            }

            if ($deferred === null) {
                return; // No active query, only notification listeners.
            }

            if ($connection->busy) {
                return; // Not finished receiving data, poll again.
            }

            $deferred->complete($connection->getResult());
            $deferred = null;

            if (empty($listeners)) {
                EventLoop::unreference($watcher);
            }
        });

        $await = EventLoop::onWritable($this->handle->socket, static function (string $watcher) use (
            &$deferred,
            &$listeners,
            &$connection,
            $onClose,
        ): void {
            try {
                if (!$connection?->flush()) {
                    return; // Not finished sending data, continue polling for writability.
                }
            } catch (pq\Exception $exception) {
                $exception = new SqlConnectionException("Flushing the connection failed", 0, $exception);
                $connection = null; // Marks connection as dead.

                self::shutdown($listeners, $deferred, $onClose, $exception);
            }

            EventLoop::disable($watcher);
        });

        EventLoop::unreference($poll);
        EventLoop::disable($await);

        parent::__construct($config, $poll, $await, $onClose);
    }

    #[\Override]
    public function isClosed(): bool
    {
        return $this->handle === null;
    }

    #[\Override]
    public function close(): void
    {
        $this->handle = null;
        parent::close();
    }

    /**
     * @param string|null Query SQL or null if not related.
     * @param \Closure $method Method to execute.
     * @param mixed ...$args Arguments to pass to function.
     *
     * @throws SqlException
     */
    private function send(?string $sql, \Closure $method, mixed ...$args): mixed
    {
        while ($this->busy) {
            try {
                $this->busy->getFuture()->await();
            } catch (\Throwable) {
                // Ignore failure from another operation.
            }
        }

        if (!$this->handle) {
            throw new SqlConnectionException("The connection to the database has been closed");
        }

        try {
            $this->pendingOperation = $this->busy = new DeferredFuture;

            $handle = $method(...$args);

            EventLoop::reference($this->poll);
            if (!$this->handle->flush()) {
                EventLoop::enable($this->await);
            }

            $result = $this->pendingOperation->getFuture()->await();
        } catch (pq\Exception $exception) {
            throw new SqlException($this->handle->errorMessage, 0, $exception);
        } finally {
            $this->busy = null;
        }

        if (!$result instanceof pq\Result) {
            throw new SqlException("Unknown query result: " . \get_debug_type($result));
        }

        if ($handle instanceof pq\Statement) {
            switch ($result->status) {
                case pq\Result::COMMAND_OK:
                    return $handle; // Will be wrapped into a ConnectionStatement object.

                default:
                    $this->makeResult($result, $sql);
                    // The statement below *should* be unreachable: $this->makeResult() will throw.
                    throw new SqlException("Unexpected error preparing statement: " . $result->errorMessage);
            }
        }

        return $this->makeResult($result, $sql);
    }

    /**
     * @throws SqlException
     */
    private function makeResult(pq\Result $result, ?string $sql): PostgresResult
    {
        if (!$this->handle) {
            throw new SqlConnectionException("Connection closed");
        }

        switch ($result->status) {
            case pq\Result::EMPTY_QUERY:
                throw new SqlQueryError("Empty query string");

            case pq\Result::COMMAND_OK:
                return new PostgresCommandResult(
                    $result->affectedRows,
                    Future::complete($this->fetchNextResult($sql)),
                );

            case pq\Result::TUPLES_OK:
                return new PqBufferedResultSet($result, Future::complete($this->fetchNextResult($sql)));

            case pq\Result::SINGLE_TUPLE:
                $this->busy = new DeferredFuture;
                return new PqUnbufferedResultSet(
                    fn () => $this->fetch($sql),
                    $result,
                    $this->busy->getFuture()
                );

            case pq\Result::NONFATAL_ERROR:
            case pq\Result::FATAL_ERROR:
                while ($this->handle->busy && $this->handle->getResult()) {
                    // Clear all outstanding result rows from the connection
                }
                throw new PostgresQueryError($result->errorMessage, $result->diag, $sql ?? '');

            case pq\Result::BAD_RESPONSE:
                $this->close();
                throw new SqlException($result->errorMessage);

            default:
                $this->close();
                throw new SqlException("Unknown result status");
        }
    }

    /**
     * @throws SqlException
     */
    private function fetchNextResult(?string $sql): ?PostgresResult
    {
        if (!$this->handle) {
            throw new SqlConnectionException("Connection closed");
        }

        if (!$this->handle->busy && ($next = $this->handle->getResult()) instanceof pq\Result) {
            return $this->makeResult($next, $sql);
        }

        return null;
    }

    private function fetch(?string $sql): ?pq\Result
    {
        if (!$this->handle) {
            throw new SqlConnectionException("Connection closed");
        }

        if (!$this->handle->busy) { // Results buffered.
            $result = $this->handle->getResult();
        } else {
            $this->pendingOperation = new DeferredFuture;

            EventLoop::reference($this->poll);
            if (!$this->handle->flush()) {
                EventLoop::enable($this->await);
            }

            $result = $this->pendingOperation->getFuture()->await();
        }

        if (!$result) {
            throw new SqlConnectionException("Connection closed");
        }

        switch ($result->status) {
            case pq\Result::TUPLES_OK: // End of result set.
                $deferred = $this->busy;
                $this->busy = null;

                \assert($deferred !== null, 'Pending deferred was not set');

                try {
                    $deferred->complete($this->fetchNextResult($sql));
                } catch (\Throwable $exception) {
                    $deferred->error($exception);
                }

                return null;

            case pq\Result::SINGLE_TUPLE:
                return $result;

            default:
                $this->close();
                throw new SqlException($result->errorMessage);
        }
    }

    /**
     * Executes the named statement using the given parameters.
     *
     * @throws SqlException
     */
    #[\Override]
    public function statementExecute(string $name, array $params): PostgresResult
    {
        \assert(isset($this->statements[$name]), "Named statement not found when executing");

        $storage = $this->statements[$name];

        $statement = $storage->future->await();
        if (!$statement instanceof pq\Statement) {
            throw new SqlException('Statement unexpectedly closed before being executed');
        }

        return $this->send(
            $storage->sql,
            $statement->execAsync(...),
            \array_map($this->encodeParam(...), $params),
        );
    }

    /**
     * @throws SqlException
     */
    #[\Override]
    public function statementDeallocate(string $name): void
    {
        if (!$this->handle) {
            return; // Connection dead.
        }

        \assert(isset($this->statements[$name]), "Named statement not found when deallocating");

        $storage = $this->statements[$name];

        if (--$storage->refCount) {
            return;
        }

        $future = $storage->future;
        $storage->future = async(function () use ($future, $name): void {
            $statement = $future->await();
            if (!$statement instanceof pq\Statement) {
                return; // Statement already deallocated.
            }

            $this->send(null, $statement->deallocateAsync(...));
            unset($this->statements[$name]);
        });
    }

    #[\Override]
    public function escapeByteA(string $data): string
    {
        if (!$this->handle) {
            throw new \Error("The connection to the database has been closed");
        }

        return $this->handle->escapeBytea($data);
    }

    #[\Override]
    public function query(string $sql): PostgresResult
    {
        if (!$this->handle) {
            throw new \Error("The connection to the database has been closed");
        }

        return $this->send($sql, $this->handle->execAsync(...), $sql);
    }

    #[\Override]
    public function execute(string $sql, array $params = []): PostgresResult
    {
        if (!$this->handle) {
            throw new \Error("The connection to the database has been closed");
        }

        $sql = parseNamedParams($sql, $names);
        $params = replaceNamedParams($params, $names);

        return $this->send(
            $sql,
            $this->handle->execParamsAsync(...),
            $sql,
            \array_map($this->encodeParam(...), $params),
        );
    }

    #[\Override]
    public function prepare(string $sql): PostgresStatement
    {
        if (!$this->handle) {
            throw new \Error("The connection to the database has been closed");
        }

        $modifiedSql = parseNamedParams($sql, $names);

        $name = self::STATEMENT_NAME_PREFIX . \sha1($modifiedSql);

        while (isset($this->statements[$name])) {
            $storage = $this->statements[$name];

            ++$storage->refCount;
            // Do not return promised prepared statement object, as the $names array may differ.
            $result = $storage->future->await();

            if ($result) { // Null returned if future was from deallocation.
                return new PostgresConnectionStatement($this, $name, $sql, $names);
            }
        }

        $future = async(function () use ($sql, $name, $modifiedSql): pq\Statement {
            if (!$this->handle) {
                throw new \Error("The connection to the database has been closed");
            }

            return $this->send($sql, $this->handle->prepareAsync(...), $name, $modifiedSql);
        });

        $storage = new StatementStorage($sql, $future);
        $this->statements[$name] = $storage;

        try {
            $storage->future->await();
        } catch (\Throwable $exception) {
            unset($this->statements[$name]);
            throw $exception;
        }

        return new PostgresConnectionStatement($this, $name, $sql, $names);
    }

    #[\Override]
    public function notify(string $channel, string $payload = ""): PostgresResult
    {
        if (!$this->handle) {
            throw new \Error("The connection to the database has been closed");
        }

        return $this->send(null, $this->handle->notifyAsync(...), $channel, $payload);
    }

    #[\Override]
    public function listen(string $channel): PostgresListener
    {
        if (!$this->handle) {
            throw new \Error("The connection to the database has been closed");
        }

        if (isset($this->listeners[$channel])) {
            throw new SqlQueryError(\sprintf("Already listening on channel '%s'", $channel));
        }

        $this->listeners[$channel] = $source = new Queue();

        try {
            $this->send(
                null,
                $this->handle->listenAsync(...),
                $channel,
                /**
                 * @param non-empty-string $channel
                 * @param positive-int $pid
                 */
                static function (string $channel, string $message, int $pid) use ($source): void {
                    $notification = new PostgresNotification($channel, $pid, $message);
                    $source->pushAsync($notification)->ignore();
                }
            );
        } catch (\Throwable $exception) {
            unset($this->listeners[$channel]);
            throw $exception;
        }

        EventLoop::enable($this->poll);
        return new PostgresConnectionListener($source->iterate(), $channel, $this->unlisten(...));
    }

    /**
     * @throws \Error
     */
    private function unlisten(string $channel): void
    {
        if (!isset($this->listeners[$channel])) {
            return;
        }

        $source = $this->listeners[$channel];
        unset($this->listeners[$channel]);

        if (!$this->handle) {
            $source->complete();
            return; // Connection already closed.
        }

        try {
            $this->send(null, $this->handle->unlistenAsync(...), $channel);
            $source->complete();
        } catch (\Throwable $exception) {
            $source->error($exception);
        }
    }

    #[\Override]
    public function quoteLiteral(string $data): string
    {
        if (!$this->handle) {
            throw new \Error("The connection to the database has been closed");
        }

        return $this->handle->quote($data);
    }

    #[\Override]
    public function quoteIdentifier(string $name): string
    {
        if (!$this->handle) {
            throw new \Error("The connection to the database has been closed");
        }

        return $this->handle->quoteName($name);
    }

    /**
     * @return bool True if result sets are buffered in memory, false if unbuffered.
     */
    public function isBufferingResults(): bool
    {
        if (!$this->handle) {
            throw new \Error("The connection to the database has been closed");
        }

        return !$this->handle->unbuffered;
    }

    /**
     * Sets result sets to be fully buffered in local memory.
     */
    public function shouldBufferResults(): void
    {
        if (!$this->handle) {
            throw new \Error("The connection to the database has been closed");
        }

        $this->handle->unbuffered = false;
    }

    /**
     * Sets result sets to be streamed from the database server.
     */
    public function shouldNotBufferResults(): void
    {
        if (!$this->handle) {
            throw new \Error("The connection to the database has been closed");
        }

        $this->handle->unbuffered = true;
    }
}
<?php declare(strict_types=1);

namespace Amp\Postgres\Internal;

use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Future;
use Amp\Postgres\PostgresResult;
use pq;

/**
 * @internal
 * @psalm-import-type TRowType from PostgresResult
 * @implements \IteratorAggregate<int, TRowType>
 */
final class PqBufferedResultSet implements PostgresResult, \IteratorAggregate
{
    use ForbidCloning;
    use ForbidSerialization;

    private readonly \Generator $iterator;

    private readonly int $rowCount;

    private readonly int $columnCount;

    /**
     * @param Future<PostgresResult|null> $nextResult Promise for next result set.
     */
    public function __construct(
        pq\Result $result,
        private readonly Future $nextResult,
    ) {
        $this->rowCount = $result->numRows;
        $this->columnCount = $result->numCols;

        $this->iterator = self::generate($result);
    }

    private static function generate(pq\Result $result): \Generator
    {
        $position = 0;

        while (++$position <= $result->numRows) {
            $result->autoConvert = pq\Result::CONV_SCALAR | pq\Result::CONV_ARRAY | pq\Result::CONV_BYTEA;
            yield $result->fetchRow(pq\Result::FETCH_ASSOC);
        }
    }

    #[\Override]
    public function fetchRow(): ?array
    {
        if (!$this->iterator->valid()) {
            return null;
        }

        $current = $this->iterator->current();
        $this->iterator->next();
        return $current;
    }

    #[\Override]
    public function getIterator(): \Traversable
    {
        return $this->iterator;
    }

    #[\Override]
    public function getNextResult(): ?PostgresResult
    {
        return $this->nextResult->await();
    }

    #[\Override]
    public function getRowCount(): int
    {
        return $this->rowCount;
    }

    #[\Override]
    public function getColumnCount(): int
    {
        return $this->columnCount;
    }
}
<?php declare(strict_types=1);

namespace Amp\Postgres;

use Amp\Sql\SqlTransaction;

/**
 * Note that notifications sent during a transaction are not delivered until the transaction has been committed.
 *
 * @extends SqlTransaction<PostgresResult, PostgresStatement, PostgresTransaction>
 */
interface PostgresTransaction extends PostgresLink, SqlTransaction
{
}
<?php declare(strict_types=1);

namespace Amp\Postgres;

use Amp\Sql\SqlLink;

/**
 * @extends SqlLink<PostgresResult, PostgresStatement, PostgresTransaction>
 */
interface PostgresLink extends PostgresExecutor, SqlLink
{
    /**
     * @return PostgresTransaction Transaction object specific to this library.
     */
    #[\Override]
    public function beginTransaction(): PostgresTransaction;
}
<?php declare(strict_types=1);

namespace Amp\Postgres;

final class PostgresNotification
{
    /**
     * @param non-empty-string $channel Channel name.
     * @param positive-int $pid PID of message source.
     * @param string $payload Message payload.
     */
    public function __construct(
        public readonly string $channel,
        public readonly int $pid,
        public readonly string $payload,
    ) {
    }
}
<?php declare(strict_types=1);

namespace Amp\Postgres;

use Amp\Cancellation;
use Amp\Postgres\Internal\PostgresHandleConnection;
use Amp\Sql\Common\RetrySqlConnector;
use Amp\Sql\SqlConnector;
use Amp\Sql\SqlException;
use Revolt\EventLoop;

/**
 * @param SqlConnector<PostgresConfig, PostgresHandleConnection>|null $connector
 *
 * @return SqlConnector<PostgresConfig, PostgresHandleConnection>
 */
function postgresConnector(?SqlConnector $connector = null): SqlConnector
{
    static $map;
    $map ??= new \WeakMap();
    $driver = EventLoop::getDriver();

    if ($connector) {
        return $map[$driver] = $connector;
    }

    /**
     * @psalm-suppress InvalidArgument
     * @var SqlConnector<PostgresConfig, PostgresHandleConnection>
     */
    return $map[$driver] ??= new RetrySqlConnector(new DefaultPostgresConnector());
}

/**
 * Create a connection using the global Connector instance.
 *
 * @throws SqlException If connecting fails.
 *
 * @throws \Error If neither ext-pgsql or pecl-pq is loaded.
 */
function connect(PostgresConfig $config, ?Cancellation $cancellation = null): PostgresHandleConnection
{
    return postgresConnector()->connect($config, $cancellation);
}
<?php declare(strict_types=1);

namespace Amp\Postgres;

final class PostgresByteA
{
    public function __construct(
        private readonly string $data,
    ) {
    }

    public function getData(): string
    {
        return $this->data;
    }
}
<?php declare(strict_types=1);

namespace Amp\Postgres;

use Amp\Sql\SqlStatement;

/**
 * @extends SqlStatement<PostgresResult>
 */
interface PostgresStatement extends SqlStatement
{
}
<?php declare(strict_types=1);

namespace Amp\Postgres;

/**
 * @extends \Traversable<int, PostgresNotification>
 */
interface PostgresListener extends \Traversable
{
    /**
     * @return non-empty-string Channel name.
     */
    public function getChannel(): string;

    public function isListening(): bool;

    /**
     * Stops listening on the channel. No more values will be emitted from this listener.
     */
    public function unlisten(): void;
}
<?php declare(strict_types=1);

namespace Amp\Postgres;

use Amp\Cancellation;
use Amp\DeferredFuture;
use Amp\Sql\SqlConnectionException;
use Revolt\EventLoop;

final class PgSqlConnection extends Internal\PostgresHandleConnection implements PostgresConnection
{
    /**
     * @throws \Error If pecl-ev is used as a loop extension.
     */
    #[\Override]
    public static function connect(PostgresConfig $config, ?Cancellation $cancellation = null): self
    {
        // @codeCoverageIgnoreStart
        /** @psalm-suppress UndefinedClass */
        if (EventLoop::getDriver()->getHandle() instanceof \EvLoop) {
            throw new \Error('ext-pgsql is not compatible with pecl-ev; use pecl-pq or a different loop extension');
        } // @codeCoverageIgnoreEnd

        if (!$connection = \pg_connect($config->getConnectionString(), \PGSQL_CONNECT_ASYNC | \PGSQL_CONNECT_FORCE_NEW)) {
            throw new SqlConnectionException("Failed to create connection resource");
        }

        if (\pg_connection_status($connection) === \PGSQL_CONNECTION_BAD) {
            throw new SqlConnectionException(\pg_last_error($connection));
        }

        if (!$socket = \pg_socket($connection)) {
            throw new SqlConnectionException("Failed to access connection socket");
        }

        $hash = \sha1($config->getHost() . $config->getPort() . $config->getUser());

        $deferred = new DeferredFuture();

        /**
         * @psalm-suppress MissingClosureParamType $resource is a resource and cannot be inferred in this context.
         * @psalm-suppress UndefinedVariable $poll is defined below.
         */
        $callback = static function (string $callbackId, $resource) use (
            &$poll,
            &$await,
            $connection,
            $config,
            $deferred,
            $hash,
        ): void {
            switch ($result = \pg_connect_poll($connection)) {
                case \PGSQL_POLLING_READING:
                case \PGSQL_POLLING_WRITING:
                    return; // Connection still reading or writing, so return and leave callback enabled.

                case \PGSQL_POLLING_FAILED:
                    $deferred->error(new SqlConnectionException(\pg_last_error($connection)));
                    break;

                case \PGSQL_POLLING_OK:
                    $deferred->complete(new self($connection, $resource, $hash, $config));
                    break;

                default:
                    $deferred->error(new SqlConnectionException('Unexpected connection status value: ' . $result));
                    break;
            }

            EventLoop::disable($poll);
            EventLoop::disable($await);
        };

        $poll = EventLoop::onReadable($socket, $callback);
        $await = EventLoop::onWritable($socket, $callback);

        try {
            return $deferred->getFuture()->await($cancellation);
        } finally {
            EventLoop::cancel($poll);
            EventLoop::cancel($await);
        }
    }

    /**
     * @param \PgSql\Connection $handle PostgreSQL connection handle.
     * @param resource $socket PostgreSQL connection stream socket.
     * @param string $id Connection identifier for determining which cached type table to use.
     */
    protected function __construct(\PgSql\Connection $handle, $socket, string $id, PostgresConfig $config)
    {
        parent::__construct(new Internal\PgSqlHandle($handle, $socket, $id, $config));
    }
}
{
    "name": "amphp/postgres",
    "description": "Asynchronous PostgreSQL client for Amp.",
    "keywords": [
        "database",
        "db",
        "postgresql",
        "postgre",
        "pgsql",
        "asynchronous",
        "async"
    ],
    "homepage": "https://amphp.org",
    "license": "MIT",
    "authors": [
        {
            "name": "Aaron Piotrowski",
            "email": "aaron@trowski.com"
        }
    ],
    "require": {
        "php": ">=8.1",
        "amphp/amp": "^3",
        "amphp/pipeline": "^1",
        "amphp/sql": "^2",
        "amphp/sql-common": "^2"
    },
    "require-dev": {
        "ext-pgsql": "*",
        "ext-pq": "*",
        "amphp/phpunit-util": "^3",
        "phpunit/phpunit": "^9",
        "amphp/php-cs-fixer-config": "^2",
        "psalm/phar": "6.15.1"
    },
    "autoload": {
        "psr-4": {
            "Amp\\Postgres\\": "src"
        },
        "files": [
            "src/functions.php",
            "src/Internal/functions.php"
        ]
    },
    "autoload-dev": {
        "psr-4": {
            "Amp\\Postgres\\Test\\": "test"
        }
    },
    "scripts": {
        "check": [
            "@cs",
            "@test"
        ],
        "cs": "php-cs-fixer fix -v --diff --dry-run",
        "cs-fix": "php-cs-fixer fix -v --diff",
        "test": "@php -dzend.assertions=1 -dassert.exception=1 ./vendor/bin/phpunit --coverage-text"
    }
}
<?php

$config = new Amp\CodeStyle\Config;
$config->getFinder()
    ->in(__DIR__ . '/src')
    ->in(__DIR__ . '/test');

$config->setCacheFile(__DIR__ . '/.php_cs.cache');

return $config;
<?php declare(strict_types=1);

namespace Amp\Sql\Test;

use Amp\Sql\SqlQueryError;
use PHPUnit\Framework\TestCase;

class SqlQueryErrorTest extends TestCase
{
    /**
     * @test
     */
    public function testItPassesQueryAlong()
    {
        $error = new SqlQueryError('error', 'SELECT * FROM foo');

        self::assertSame('SELECT * FROM foo', $error->getQuery());
        self::assertStringStartsWith("Amp\Sql\SqlQueryError: error\nCurrent query was SELECT * FROM foo", (string) $error);
    }
}
<?php declare(strict_types=1);

namespace Amp\Sql\Test;

use Amp\Sql\SqlConfig;
use PHPUnit\Framework\TestCase;

class SqlConfigTest extends TestCase
{
    private function createConfigFromString(string $connectionString): SqlConfig
    {
        return new class($connectionString) extends SqlConfig {
            public function __construct(string $connectionString)
            {
                $parts = self::parseConnectionString($connectionString);

                parent::__construct(
                    host: $parts["host"] ?? '',
                    port: (int) ($parts["port"] ?? 0),
                    user: $parts["user"] ?? "",
                    password: $parts["password"] ?? "",
                    database: $parts["db"] ?? "",
                );
            }
        };
    }

    public function provideValidConnectionStrings(): array
    {
        return [
            'basic' => ["host=localhost port=5432 user=user pass=test db=test"],
            'alternative' => ["host=localhost;port=5432;user=user;password=test;db=test"],
            'port-in-host' => ["host=localhost:5432 user=user pass=test db=test"],
            'whitespace' => ["   host=localhost   port=5432   user=user   pass=test   db=test   "],
            'whitespace-after-semicolon' => ["host=localhost; port=5432; user=user; password=test; db=test; "],
            'quotes' => ['host="localhost" port=5432 user="user" pass="test" db="test"'],
            'alternative-with-whitespace' => ["host=localhost; port=5432; user=user; password=test; db=test;"],
        ];
    }

    /**
     * @dataProvider provideValidConnectionStrings
     */
    public function testValidStrings(string $connectionString): void
    {
        $config = $this->createConfigFromString($connectionString);

        self::assertSame("localhost", $config->getHost());
        self::assertSame(5432, $config->getPort());
        self::assertSame("user", $config->getUser());
        self::assertSame("test", $config->getPassword());
        self::assertSame("test", $config->getDatabase());
    }

    public function testQuoteInQuotedValue(): void
    {
        $config = $this->createConfigFromString(
            <<<'CS'
            host="local\"host:3306" database='test'
            CS
        );

        self::assertSame('local"host', $config->getHost());
        self::assertSame(3306, $config->getPort());
        self::assertSame('test', $config->getDatabase());
    }

    public function testEscapedSpace(): void
    {
        $config = $this->createConfigFromString(
            <<<'CS'
            host=localhost user=Test\ User database=Test\ Name
            CS
        );

        self::assertSame('localhost', $config->getHost());
        self::assertSame('Test User', $config->getUser());
        self::assertSame('Test Name', $config->getDatabase());
    }

    public function provideInvalidConnectionStrings(): array
    {
        return [
            'missing-value' => ['host='],
            'empty-value' => ['host=""'],
            'leading-characters' => ['test host=localhost'],
            'trailing-characters' => ['host=localhost test'],
            'invalid-whitespace' => ['host= localhost'],
            'duplicate-key' => ['host=localhost port=5432 port=5433']
        ];
    }

    /**
     * @dataProvider provideInvalidConnectionStrings
     */
    public function testInvalidStrings(string $connectionString): void
    {
        $this->expectException(\ValueError::class);
        $this->createConfigFromString($connectionString);
    }
}
<?php declare(strict_types=1);

namespace Amp\Sql;

/**
 * @template TResult of SqlResult
 * @template TStatement of SqlStatement<TResult>
 * @template TTransaction of SqlTransaction
 *
 * @extends SqlExecutor<TResult, TStatement>
 */
interface SqlLink extends SqlExecutor
{
    /**
     * Starts a transaction, returning an object where all queries are executed on a single connection.
     *
     * @return TTransaction
     */
    public function beginTransaction(): SqlTransaction;
}
<?php declare(strict_types=1);

namespace Amp\Sql;

class SqlConnectionException extends SqlException
{
}
<?php declare(strict_types=1);

namespace Amp\Sql;

interface SqlTransactionIsolation
{
    /**
     * @return string Human-readable label for the transaction isolation level.
     */
    public function getLabel(): string;

    /**
     * @return string SQL to be inserted as the transaction isolation level.
     */
    public function toSql(): string;
}
<?php declare(strict_types=1);

namespace Amp\Sql;

/**
 * @template TResult of SqlResult
 * @template TStatement of SqlStatement<TResult>
 * @template TTransaction of SqlTransaction
 *
 * @extends SqlLink<TResult, TStatement, TTransaction>
 */
interface SqlTransaction extends SqlLink
{
    public function getIsolation(): SqlTransactionIsolation;

    /**
     * @return bool True if the transaction is active, false if it has been committed or rolled back.
     */
    public function isActive(): bool;

    /**
     * @return string|null Nested transaction identifier or null if a top-level transaction.
     */
    public function getSavepointIdentifier(): ?string;

    /**
     * Commits the transaction and makes it inactive.
     *
     * @throws SqlTransactionError If the transaction has been committed or rolled back.
     */
    public function commit(): void;

    /**
     * Rolls back the transaction and makes it inactive.
     *
     * @throws SqlTransactionError If the transaction has been committed or rolled back.
     */
    public function rollback(): void;

    /**
     * Attaches a callback which is invoked when the entire transaction is committed. If this transaction
     * is a nested transaction, the callback will not be invoked until the top-level transaction is committed.
     *
     * @param \Closure():void $onCommit
     */
    public function onCommit(\Closure $onCommit): void;

    /**
     * Attaches a callback which is invoked when the transaction is rolled back. If in a nested transaction, the
     * callbacks may be invoked when rolling back to a savepoint or if the entire transaction is rolled back,
     * regardless of if the savepoint was released prior.
     *
     * @param \Closure():void $onRollback
     */
    public function onRollback(\Closure $onRollback): void;
}
<?php declare(strict_types=1);

namespace Amp\Sql;

enum SqlTransactionIsolationLevel implements SqlTransactionIsolation
{
    case Uncommitted;
    case Committed;
    case Repeatable;
    case Serializable;

    #[\Override]
    public function getLabel(): string
    {
        return match ($this) {
            self::Uncommitted => 'Uncommitted',
            self::Committed => 'Committed',
            self::Repeatable => 'Repeatable',
            self::Serializable => 'Serializable',
        };
    }

    #[\Override]
    public function toSql(): string
    {
        return match ($this) {
            self::Uncommitted => 'READ UNCOMMITTED',
            self::Committed => 'READ COMMITTED',
            self::Repeatable => 'REPEATABLE READ',
            self::Serializable => 'SERIALIZABLE',
        };
    }
}
<?php declare(strict_types=1);

namespace Amp\Sql;

class SqlQueryError extends \Error
{
    public function __construct(
        string $message,
        protected readonly string $query = "",
        ?\Throwable $previous = null,
    ) {
        parent::__construct($message, 0, $previous);
    }

    final public function getQuery(): string
    {
        return $this->query;
    }

    public function __toString(): string
    {
        if ($this->query === "") {
            return parent::__toString();
        }

        $msg = $this->message;
        $this->message .= "\nCurrent query was {$this->query}";
        $str = parent::__toString();
        $this->message = $msg;
        return $str;
    }
}
<?php declare(strict_types=1);

namespace Amp\Sql;

/**
 * @template TResult of SqlResult
 * @template TStatement of SqlStatement
 */
interface SqlExecutor extends SqlTransientResource
{
    /**
     * @param string $sql SQL query to execute.
     *
     * @return TResult
     *
     * @throws SqlException If the operation fails due to unexpected condition.
     * @throws SqlConnectionException If the connection to the database is lost.
     * @throws SqlQueryError If the operation fails due to an error in the query (such as a syntax error).
     */
    public function query(string $sql): SqlResult;

    /**
     * @param string $sql SQL query to prepare.
     *
     * @return TStatement
     *
     * @throws SqlException If the operation fails due to unexpected condition.
     * @throws SqlConnectionException If the connection to the database is lost.
     * @throws SqlQueryError If the operation fails due to an error in the query (such as a syntax error).
     */
    public function prepare(string $sql): SqlStatement;

    /**
     * @param string $sql SQL query to prepare and execute.
     * @param array<int, mixed>|array<string, mixed> $params Query parameters.
     *
     * @return TResult
     *
     * @throws SqlException If the operation fails due to unexpected condition.
     * @throws SqlConnectionException If the connection to the database is lost.
     * @throws SqlQueryError If the operation fails due to an error in the query (such as a syntax error).
     */
    public function execute(string $sql, array $params = []): SqlResult;
}
<?php declare(strict_types=1);

namespace Amp\Sql;

class SqlTransactionError extends \Error
{
}
<?php declare(strict_types=1);

namespace Amp\Sql;

use Amp\Closable;

interface SqlTransientResource extends Closable
{
    /**
     * Get the timestamp of the last usage of this resource.
     *
     * @return int Unix timestamp in seconds.
     */
    public function getLastUsedAt(): int;
}
<?php declare(strict_types=1);

namespace Amp\Sql;

/**
 * @template TResult of SqlResult
 */
interface SqlStatement extends SqlTransientResource
{
    /**
     * @return TResult
     */
    public function execute(array $params = []): SqlResult;

    /**
     * @return string The SQL string used to prepare the statement.
     */
    public function getQuery(): string;
}
<?php declare(strict_types=1);

namespace Amp\Sql;

abstract class SqlConfig
{
    public const KEY_MAP = [
        'hostname' => 'host',
        'username' => 'user',
        'pass' => 'password',
        'database' => 'db',
        'dbname' => 'db',
    ];

    private const KEY_VALUE_PAIR_REGEXP = <<<'REGEXP'
        [\G\s*(\w+)=((['"])(?:\\(?:\\|\3)|(?!\3).)*+\3|[^ '";](?:\\(?:\\| )|(?!\s+|;| ).)*+)(?:\s+|;|$)]
        REGEXP;

    private string $host;

    private int $port;

    private ?string $user;

    private ?string $password;

    private ?string $database;

    /**
     * Parses a connection string into an array of keys and values given.
     *
     * @param string $connectionString Connection string, e.g., "hostname=localhost username=sql password=default"
     * @param array<non-empty-string, non-empty-string> $keymap Map of alternative key names to canonical key names.
     *
     * @return array<non-empty-string, string>
     */
    protected static function parseConnectionString(string $connectionString, array $keymap = self::KEY_MAP): array
    {
        $values = [];
        $connectionString = \trim($connectionString);

        if ($connectionString === '') {
            throw new \ValueError("Empty connection string");
        }

        if (\preg_match_all(
            pattern: self::KEY_VALUE_PAIR_REGEXP,
            subject: $connectionString,
            matches: $matches,
            flags: \PREG_SET_ORDER | \PREG_UNMATCHED_AS_NULL,
        ) === false) {
            throw new \ValueError("Invalid connection string");
        }

        $offset = 0;
        foreach ($matches as [$pair, $key, $value, $quote]) {
            \assert($value !== null);

            if ($quote !== null) {
                $value = \stripslashes(\substr($value, 1, -1));

                if ($value === '') {
                    throw new \ValueError("Empty connection string value for key '{$key}'");
                }
            } else {
                $value = \str_replace('\\ ', ' ', $value);
            }

            \assert($key !== null && $key !== '');
            $key = $keymap[$key] ?? $key;
            if (\array_key_exists($key, $values)) {
                throw new \ValueError("Duplicate connection string key '{$key}'");
            }

            $values[$key] = $value;

            \assert($pair !== null);
            $offset += \strlen($pair);
        }

        if ($offset !== \strlen($connectionString)) {
            throw new \ValueError("Trailing characters in connection string");
        }

        if (\preg_match('[^(?<host>.+):(?<port>\d{1,5})$]', $values["host"] ?? "", $match)) {
            $values["host"] = $match["host"];
            $values["port"] = $match["port"];
        }

        return $values;
    }

    public function __construct(
        string $host,
        int $port,
        ?string $user = null,
        ?string $password = null,
        ?string $database = null
    ) {
        $this->host = $host;
        $this->port = $port;
        $this->user = $user;
        $this->password = $password;
        $this->database = $database;
    }

    final public function getHost(): string
    {
        return $this->host;
    }

    final public function withHost(string $host): static
    {
        $new = clone $this;
        $new->host = $host;
        return $new;
    }

    final public function getPort(): int
    {
        return $this->port;
    }

    final public function withPort(int $port): static
    {
        $new = clone $this;
        $new->port = $port;
        return $new;
    }

    final public function getUser(): ?string
    {
        return $this->user;
    }

    final public function withUser(?string $user = null): static
    {
        $new = clone $this;
        $new->user = $user;
        return $new;
    }

    final public function getPassword(): ?string
    {
        return $this->password;
    }

    final public function withPassword(?string $password = null): static
    {
        $new = clone $this;
        $new->password = $password;
        return $new;
    }

    final public function getDatabase(): ?string
    {
        return $this->database;
    }

    final public function withDatabase(?string $database = null): static
    {
        $new = clone $this;
        $new->database = $database;
        return $new;
    }
}
<?php declare(strict_types=1);

namespace Amp\Sql;

/**
 * @template TConfig of SqlConfig
 * @template TResult of SqlResult
 * @template TStatement of SqlStatement<TResult>
 * @template TTransaction of SqlTransaction
 *
 * @extends SqlLink<TResult, TStatement, TTransaction>
 */
interface SqlConnection extends SqlLink
{
    /**
     * @return TConfig The configuration used to create this connection.
     */
    public function getConfig(): SqlConfig;

    /**
     * @return SqlTransactionIsolation Current transaction isolation used when beginning transactions on this connection.
     */
    public function getTransactionIsolation(): SqlTransactionIsolation;

    /**
     * Sets the transaction isolation level for transactions began on this link.
     *
     * @see SqlLink::beginTransaction()
     */
    public function setTransactionIsolation(SqlTransactionIsolation $isolation): void;
}
<?php declare(strict_types=1);

namespace Amp\Sql;

class SqlException extends \Exception
{
}
<?php declare(strict_types=1);

namespace Amp\Sql;

use Amp\Cancellation;

/**
 * @template TConfig of SqlConfig
 * @template TConnection of SqlConnection
 */
interface SqlConnector
{
    /**
     * Returns a new database connection based on the given configuration.
     * Implementations may provide further parameters, such as a Cancellation.
     *
     * @param TConfig $config
     *
     * @return TConnection
     *
     * @throws SqlConnectionException
     */
    public function connect(SqlConfig $config, ?Cancellation $cancellation = null): SqlConnection;
}
<?php declare(strict_types=1);

namespace Amp\Sql;

/**
 * @template TConfig of SqlConfig
 * @template TResult of SqlResult
 * @template TStatement of SqlStatement<TResult>
 * @template TTransaction of SqlTransaction
 *
 * @extends SqlConnection<TConfig, TResult, TStatement, TTransaction>
 */
interface SqlConnectionPool extends SqlConnection
{
    /**
     * Gets a single connection from the pool to run a set of queries against a single connection.
     * Generally a transaction should be used instead of this method.
     *
     * @return SqlConnection<TConfig, TResult, TStatement, TTransaction>
     */
    public function extractConnection(): SqlConnection;

    /**
     * @return int Total number of active connections in the pool.
     */
    public function getConnectionCount(): int;

    /**
     * @return int Total number of idle connections in the pool.
     */
    public function getIdleConnectionCount(): int;

    /**
     * @return int Maximum number of connections this pool will create.
     */
    public function getConnectionLimit(): int;

    /**
     * @return int Number of seconds a connection may remain idle before it is automatically closed.
     */
    public function getIdleTimeout(): int;
}
<?php declare(strict_types=1);

namespace Amp\Sql;

/**
 * @template TFieldValue
 * @extends \Traversable<int, array<string, TFieldValue>>
 */
interface SqlResult extends \Traversable
{
    /**
     * Returns the next row in the result set or null if no rows remain. This method may be used as an alternative
     * to foreach iteration to obtain single rows from the result.
     *
     * @return array<string, TFieldValue>|null
     */
    public function fetchRow(): ?array;

    /**
     * Resolves with a new instance of Result if another result is available after this result. Resolves with null if
     * no further results are available.
     *
     * @return SqlResult<TFieldValue>|null
     */
    public function getNextResult(): ?self;

    /**
     * Returns the number of rows affected or returned by the query if applicable or null if the number of rows is
     * unknown or not applicable to the query.
     */
    public function getRowCount(): ?int;

    /**
     * Returns the number of columns returned by the query if applicable or null if the number of columns is
     * unknown or not applicable to the query.
     */
    public function getColumnCount(): ?int;
}
{
    "name": "amphp/sql",
    "description": "Asynchronous SQL client for Amp.",
    "keywords": [
        "database",
        "db",
        "sql",
        "asynchronous",
        "async"
    ],
    "homepage": "https://amphp.org",
    "license": "MIT",
    "require": {
        "php": ">=8.1",
        "amphp/amp": "^3"
    },
    "require-dev": {
        "amphp/php-cs-fixer-config": "^2",
        "phpunit/phpunit": "^9",
        "psalm/phar": "6.15.1"
    },
    "autoload": {
        "psr-4": {
            "Amp\\Sql\\": "src"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Amp\\Sql\\Test\\": "test"
        }
    },
    "scripts": {
        "check": [
            "@cs",
            "@test"
        ],
        "cs": "php-cs-fixer fix -v --diff --dry-run",
        "cs-fix": "php-cs-fixer fix -v --diff",
        "test": "phpunit --coverage-text"
    }
}
<?php

declare(strict_types=1);
/*
 * This file is part of the webmozart/assert package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Assert;

class InvalidArgumentException extends \InvalidArgumentException
{
}
<?php

declare(strict_types=1);

namespace Webmozart\Assert;

use Psalm\Internal\Analyzer\Statements\Expression\ExpressionIdentifier;
use Psalm\Plugin\EventHandler\AfterMethodCallAnalysisInterface;
use Psalm\Plugin\EventHandler\Event\AfterMethodCallAnalysisEvent;
use Psalm\Plugin\PluginEntryPointInterface;
use Psalm\PluginRegistrationSocket;
use SimpleXMLElement;

final class PsalmPlugin implements PluginEntryPointInterface, AfterMethodCallAnalysisInterface
{
    public function __invoke(PluginRegistrationSocket $registration, ?SimpleXMLElement $config = null): void
    {
        $registration->registerHooksFromClass(self::class);
    }

    public static function afterMethodCallAnalysis(AfterMethodCallAnalysisEvent $event): void
    {
        [$class, $method] = explode('::', $event->getAppearingMethodId());
        if ($class !== Assert::class) {
            return;
        }
        if (!isset(HasAssert::HAS_ASSERT[$method])) {
            return;
        }

        $firstArg = $event->getExpr()->getArgs()[0] ?? null;
        if ($firstArg === null) {
            return;
        }

        $varId = ExpressionIdentifier::getExtendedVarId(
            $firstArg->value,
            $event->getContext()->self,
            $event->getStatementsSource()
        );

        if ($varId === null || !isset($event->getContext()->vars_in_scope[$varId])) {
            return;
        }

        $candidateType = $event->getContext()->vars_in_scope[$varId];

        $event->setReturnTypeCandidate($candidateType);
    }
}
<?php

declare(strict_types=1);

/*
 * This file is part of the webmozart/assert package.
 *
 * (c) Bernhard Schussek <bschussek@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Webmozart\Assert;

use ArrayAccess;
use Closure;
use Countable;
use DateTime;
use DateTimeImmutable;
use ReflectionFunction;
use ReflectionProperty;
use Throwable;
use Traversable;

/**
 * Efficient assertions to validate the input/output of your methods.
 *
 * @since  1.0
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class Assert
{
    use Mixin;

    /**
     * @psalm-pure
     *
     * @psalm-assert string $value
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function string(mixed $value, string|callable $message = ''): string
    {
        if (!\is_string($value)) {
            $message = static::resolveMessage($message);

            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected a string. Got: %s',
                static::typeToString($value)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert non-empty-string $value
     * @param string|callable():string $message
     *
     * @return non-empty-string
     *
     * @throws InvalidArgumentException
     */
    public static function stringNotEmpty(mixed $value, string|callable $message = ''): string
    {
        static::string($value, $message);
        static::notSame($value, '', $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert int $value
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function integer(mixed $value, string|callable $message = ''): int
    {
        if (!\is_int($value)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected an integer. Got: %s',
                static::typeToString($value)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert numeric $value
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function integerish(mixed $value, string|callable $message = ''): int|float|string
    {
        if (!\is_numeric($value) || $value != (int) $value) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected an integerish value. Got: %s',
                static::typeToString($value)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert positive-int $value
     *
     * @param string|callable():string $message
     *
     * @return positive-int
     *
     * @throws InvalidArgumentException
     */
    public static function positiveInteger(mixed $value, string|callable $message = ''): int
    {
        static::integer($value, $message);

        if ($value < 1) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected a positive integer. Got: %s',
                static::valueToString($value)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     * @psalm-assert non-negative-int $value
     * @param string|callable():string $message
     *
     * @return non-negative-int
     *
     * @throws InvalidArgumentException
     */
    public static function notNegativeInteger(mixed $value, string|callable $message = ''): int
    {
        static::integer($value, $message);

        if ($value < 0) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected a non negative integer. Got: %s',
                static::valueToString($value)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     * @psalm-assert negative-int $value
     * @param string|callable():string $message
     *
     * @return negative-int
     *
     * @throws InvalidArgumentException
     */
    public static function negativeInteger(mixed $value, string|callable $message = ''): int
    {
        static::integer($value, $message);

        if ($value >= 0) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected a negative integer. Got: %s',
                static::valueToString($value)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert float $value
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function float(mixed $value, string|callable $message = ''): float
    {
        if (!\is_float($value)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected a float. Got: %s',
                static::typeToString($value)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert numeric $value
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function numeric(mixed $value, string|callable $message = ''): int|float|string
    {
        if (!\is_numeric($value)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected a numeric. Got: %s',
                static::typeToString($value)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert positive-int|0 $value
     *
     * @param string|callable():string $message
     *
     * @return positive-int|0
     *
     * @throws InvalidArgumentException
     */
    public static function natural(mixed $value, string|callable $message = ''): int
    {
        if (!\is_int($value) || $value < 0) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected a non-negative integer. Got: %s',
                static::valueToString($value)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert bool $value
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function boolean(mixed $value, string|callable $message = ''): bool
    {
        if (!\is_bool($value)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected a boolean. Got: %s',
                static::typeToString($value)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert scalar $value
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function scalar(mixed $value, string|callable $message = ''): int|bool|float|string
    {
        if (!\is_scalar($value)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected a scalar. Got: %s',
                static::typeToString($value)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert object $value
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function object(mixed $value, string|callable $message = ''): object
    {
        if (!\is_object($value)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected an object. Got: %s',
                static::typeToString($value)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert object|class-string $value
     *
     * @param string|callable():string $message
     *
     * @return object|class-string
     *
     * @throws InvalidArgumentException
     */
    public static function objectish(mixed $value, string|callable $message = ''): object|string
    {
        if (!\is_object($value) && !\is_string($value)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected an objectish value. Got: %s',
                static::typeToString($value)
            ));
        }

        if (\is_string($value) && !\class_exists($value)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected class to be defined. Got: %s',
                $value
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert resource $value
     *
     * @param string|callable():string $message
     *
     * @see https://www.php.net/manual/en/function.get-resource-type.php
     *
     * @return resource
     *
     * @throws InvalidArgumentException
     */
    public static function resource(mixed $value, ?string $type = null, string|callable $message = ''): mixed
    {
        if (!\is_resource($value)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected a resource. Got: %s',
                static::typeToString($value),
                $type // User supplied message might include the second placeholder.
            ));
        }

        if ($type && $type !== \get_resource_type($value)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected a resource of type %2$s. Got: %s',
                static::typeToString($value),
                $type
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert object $value
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function isInitialized(mixed $value, string $property, string|callable $message = ''): object
    {
        Assert::object($value);

        $reflectionProperty = new ReflectionProperty($value, $property);

        if (!$reflectionProperty->isInitialized($value)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected property %s to be initialized.',
                $property,
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert callable $value
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function isCallable(mixed $value, string|callable $message = ''): callable
    {
        if (!\is_callable($value)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected a callable. Got: %s',
                static::typeToString($value)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert array $value
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function isArray(mixed $value, string|callable $message = ''): array
    {
        if (!\is_array($value)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected an array. Got: %s',
                static::typeToString($value)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert array|ArrayAccess $value
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function isArrayAccessible(mixed $value, string|callable $message = ''): array|ArrayAccess
    {
        if (!\is_array($value) && !($value instanceof ArrayAccess)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected an array accessible. Got: %s',
                static::typeToString($value)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert countable $value
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function isCountable(mixed $value, string|callable $message = ''): array|Countable
    {
        if (!\is_array($value) && !($value instanceof Countable)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected a countable. Got: %s',
                static::typeToString($value)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable $value
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function isIterable(mixed $value, string|callable $message = ''): iterable
    {
        if (!\is_array($value) && !($value instanceof Traversable)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected an iterable. Got: %s',
                static::typeToString($value)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @template T of object
     *
     * @psalm-assert T $value
     *
     * @param string|callable():string $message
     * @param class-string<T> $class
     *
     * @return T
     *
     * @throws InvalidArgumentException
     */
    public static function isInstanceOf(mixed $value, mixed $class, string|callable $message = ''): object
    {
        static::string($class, 'Expected class as a string. Got: %s');

        if (!($value instanceof $class)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected an instance of %2$s. Got: %s',
                static::typeToString($value),
                $class
            ));
        }

        return $value;
    }

    /**
     * @template T of object
     *
     * @psalm-assert object $value
     *
     * @param string|callable():string $message
     * @param class-string<T> $class
     *
     * @throws InvalidArgumentException
     */
    public static function notInstanceOf(mixed $value, mixed $class, string|callable $message = ''): object
    {
        static::string($class, 'Expected class as a string. Got: %s');

        if (!\is_object($value) || $value instanceof $class) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected an instance other than %2$s. Got: %s',
                static::typeToString($value),
                $class
            ));
        }

        return $value;
    }

    /**
     * @template T of object
     *
     * @psalm-assert T $value
     *
     * @param iterable<class-string<T>> $classes
     * @param string|callable():string $message
     *
     * @return T
     *
     * @throws InvalidArgumentException
     */
    public static function isInstanceOfAny(mixed $value, mixed $classes, string|callable $message = ''): object
    {
        static::isIterable($classes);

        foreach ($classes as $class) {
            static::string($class, 'Expected class as a string. Got: %s');

            if ($value instanceof $class) {
                return $value;
            }
        }

        $message = self::resolveMessage($message);
        static::reportInvalidArgument(\sprintf(
            $message ?: 'Expected an instance of any of %2$s. Got: %s',
            static::typeToString($value),
            \implode(', ', \array_map(static::valueToString(...), \iterator_to_array($classes)))
        ));
    }

    /**
     * @template T
     *
     * @psalm-assert object|class-string $value
     *
     * @param T $value
     * @param string|callable():string $message
     *
     * @return T
     *
     * @throws InvalidArgumentException
     */
    public static function isNotInstanceOfAny(mixed $value, mixed $classes, string|callable $message = ''): mixed
    {
        static::isIterable($classes);

        foreach ($classes as $class) {
            static::string($class, 'Expected class as a string. Got: %s');

            if ($value instanceof $class) {
                $message = self::resolveMessage($message);
                static::reportInvalidArgument(\sprintf(
                    $message ?: 'Expected not an instance of %2$s. Got: %s',
                    static::typeToString($value),
                    \implode(', ', \array_map(static::valueToString(...), \iterator_to_array($classes)))
                ));
            }
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @template T of object
     *
     * @psalm-assert T|class-string<T> $value
     *
     * @param string|callable():string $message
     * @param class-string<T> $class
     *
     * @return T|class-string<T>
     *
     * @throws InvalidArgumentException
     */
    public static function isAOf(mixed $value, mixed $class, string|callable $message = ''): object|string
    {
        static::string($class, 'Expected class as a string. Got: %s');

        if (!\is_a($value, $class, \is_string($value))) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected an instance of this class or to this class among its parents "%2$s". Got: %s',
                static::valueToString($value),
                $class
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @template T
     *
     * @psalm-assert object|class-string $value
     *
     * @param T $value
     * @param string|callable():string $message
     *
     * @return object|class-string
     *
     * @throws InvalidArgumentException
     */
    public static function isNotA(mixed $value, mixed $class, string|callable $message = ''): object|string
    {
        static::objectish($value, $message);
        static::string($class, 'Expected class as a string. Got: %s');

        if (\is_a($value, $class, \is_string($value))) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected an instance of this class or to this class among its parents other than "%2$s". Got: %s',
                static::valueToString($value),
                $class
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert T $value
     *
     * @template T as object
     *
     * @param array<class-string<T>> $classes
     * @param string|callable():string $message
     *
     * @return T
     * @throws InvalidArgumentException
     */
    public static function isAnyOf(mixed $value, mixed $classes, string|callable $message = ''): object|string
    {
        static::objectish($value, $message);
        static::isIterable($classes);

        foreach ($classes as $class) {
            static::string($class, 'Expected class as a string. Got: %s');

            if (\is_a($value, $class, \is_string($value))) {
                return $value;
            }
        }

        $message = self::resolveMessage($message);
        static::reportInvalidArgument(\sprintf(
            $message ?: 'Expected an instance of any of this classes or any of those classes among their parents "%2$s". Got: %s',
            static::valueToString($value),
            \implode(', ', \iterator_to_array($classes))
        ));
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert empty $value
     *
     * @param string|callable():string $message
     *
     * @return empty
     *
     * @throws InvalidArgumentException
     */
    public static function isEmpty(mixed $value, string|callable $message = ''): mixed
    {
        if (!empty($value)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected an empty value. Got: %s',
                static::valueToString($value)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert !empty $value
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function notEmpty(mixed $value, string|callable $message = ''): mixed
    {
        if (empty($value)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected a non-empty value. Got: %s',
                static::valueToString($value)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert null $value
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function null(mixed $value, string|callable $message = ''): null
    {
        if (null !== $value) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected null. Got: %s',
                static::valueToString($value)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert !null $value
     *
     * @param string|callable():string $message
     *
     * @template T
     * @param T|null $value
     * @return T
     * @throws InvalidArgumentException
     */
    public static function notNull(mixed $value, string|callable $message = ''): mixed
    {
        if (null === $value) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(
                $message ?: 'Expected a value other than null.'
            );
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert true $value
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function true(mixed $value, string|callable $message = ''): true
    {
        if (true !== $value) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected a value to be true. Got: %s',
                static::valueToString($value)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert false $value
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function false(mixed $value, string|callable $message = ''): false
    {
        if (false !== $value) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected a value to be false. Got: %s',
                static::valueToString($value)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert !false $value
     *
     * @param string|callable():string $message
     *
     * @template T
     * @param T|false $value
     * @return T
     * @throws InvalidArgumentException
     */
    public static function notFalse(mixed $value, string|callable $message = ''): mixed
    {
        if (false === $value) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(
                $message ?: 'Expected a value other than false.'
            );
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     * @param string $value
     *
     * @throws InvalidArgumentException
     */
    public static function ip(mixed $value, string|callable $message = ''): string
    {
        static::string($value, $message);

        if (false === \filter_var($value, \FILTER_VALIDATE_IP)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected a value to be an IP. Got: %s',
                static::valueToString($value)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     * @param string $value
     *
     * @throws InvalidArgumentException
     */
    public static function ipv4(mixed $value, string|callable $message = ''): string
    {
        static::string($value, $message);

        if (false === \filter_var($value, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV4)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected a value to be an IPv4. Got: %s',
                static::valueToString($value)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     * @param string $value
     *
     * @throws InvalidArgumentException
     */
    public static function ipv6(mixed $value, string|callable $message = ''): string
    {
        static::string($value, $message);

        if (false === \filter_var($value, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected a value to be an IPv6. Got: %s',
                static::valueToString($value)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     * @param string $value
     *
     * @throws InvalidArgumentException
     */
    public static function email(mixed $value, string|callable $message = ''): string
    {
        static::string($value, $message);

        if (false === \filter_var($value, FILTER_VALIDATE_EMAIL, FILTER_FLAG_EMAIL_UNICODE)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected a value to be a valid e-mail address. Got: %s',
                static::valueToString($value)
            ));
        }

        return $value;
    }

    /**
     * Does non-strict comparisons on the items, so ['3', 3] will not pass the assertion.
     * Note: objects with identical properties are also considered equal.
     *
     * @psalm-assert array $values
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function uniqueValues(mixed $values, string|callable $message = ''): array
    {
        static::isArray($values);

        $allValues = \count($values);
        $uniqueValues = \count(\array_unique($values));

        if ($allValues !== $uniqueValues) {
            $difference = $allValues - $uniqueValues;

            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected an array of unique values, but %s of them %s duplicated',
                $difference,
                1 === $difference ? 'is' : 'are'
            ));
        }

        return $values;
    }

    /**
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function eq(mixed $value, mixed $expect, string|callable $message = ''): mixed
    {
        if ($expect != $value) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected a value equal to %2$s. Got: %s',
                static::valueToString($value),
                static::valueToString($expect)
            ));
        }

        return $value;
    }

    /**
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function notEq(mixed $value, mixed $expect, string|callable $message = ''): mixed
    {
        if ($expect == $value) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected a different value than %s.',
                static::valueToString($expect)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function same(mixed $value, mixed $expect, string|callable $message = ''): mixed
    {
        if ($expect !== $value) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected a value identical to %2$s. Got: %s',
                static::valueToString($value),
                static::valueToString($expect)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function notSame(mixed $value, mixed $expect, string|callable $message = ''): mixed
    {
        if ($expect === $value) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected a value not identical to %s.',
                static::valueToString($expect)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function greaterThan(mixed $value, mixed $limit, string|callable $message = ''): mixed
    {
        if ($value <= $limit) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected a value greater than %2$s. Got: %s',
                static::valueToString($value),
                static::valueToString($limit)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function greaterThanEq(mixed $value, mixed $limit, string|callable $message = ''): mixed
    {
        if ($value < $limit) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected a value greater than or equal to %2$s. Got: %s',
                static::valueToString($value),
                static::valueToString($limit)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function lessThan(mixed $value, mixed $limit, string|callable $message = ''): mixed
    {
        if ($value >= $limit) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected a value less than %2$s. Got: %s',
                static::valueToString($value),
                static::valueToString($limit)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function lessThanEq(mixed $value, mixed $limit, string|callable $message = ''): mixed
    {
        if ($value > $limit) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected a value less than or equal to %2$s. Got: %s',
                static::valueToString($value),
                static::valueToString($limit)
            ));
        }

        return $value;
    }

    /**
     * Inclusive range, so Assert::(3, 3, 5) passes.
     *
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function range(mixed $value, mixed $min, mixed $max, string|callable $message = ''): mixed
    {
        if ($value < $min || $value > $max) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected a value between %2$s and %3$s. Got: %s',
                static::valueToString($value),
                static::valueToString($min),
                static::valueToString($max)
            ));
        }

        return $value;
    }

    /**
     * A more human-readable alias of Assert::inArray().
     *
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function oneOf(mixed $value, mixed $values, string|callable $message = ''): mixed
    {
        static::inArray($value, $values, $message);

        return $value;
    }

    /**
     * Does strict comparison, so Assert::inArray(3, ['3']) does not pass the assertion.
     *
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function inArray(mixed $value, mixed $values, string|callable $message = ''): mixed
    {
        static::isArray($values);

        if (!\in_array($value, $values, true)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected one of: %2$s. Got: %s',
                static::valueToString($value),
                \implode(', ', \array_map(static::valueToString(...), $values))
            ));
        }

        return $value;
    }

    /**
     * A more human-readable alias of Assert::notInArray().
     *
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function notOneOf(mixed $value, mixed $values, string|callable $message = ''): mixed
    {
        static::notInArray($value, $values, $message);

        return $value;
    }

    /**
     * Check that a value is not present
     *
     * Does strict comparison, so Assert::notInArray(3, [1, 2, 3]) will not pass
     * the assertion, but Assert::notInArray(3, ['3']) will.
     *
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function notInArray(mixed $value, mixed $values, string|callable $message = ''): mixed
    {
        static::isArray($values);

        if (\in_array($value, $values, true)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: '%2$s was not expected to contain a value. Got: %s',
                static::valueToString($value),
                \implode(', ', \array_map(static::valueToString(...), $values))
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function contains(mixed $value, mixed $subString, string|callable $message = ''): string
    {
        static::string($value);
        static::string($subString);

        if (!\str_contains($value, $subString)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected a value to contain %2$s. Got: %s',
                static::valueToString($value),
                static::valueToString($subString)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function notContains(mixed $value, mixed $subString, string|callable $message = ''): string
    {
        static::string($value);
        static::string($subString);

        if (\str_contains($value, $subString)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: '%2$s was not expected to be contained in a value. Got: %s',
                static::valueToString($value),
                static::valueToString($subString)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function notWhitespaceOnly(mixed $value, string|callable $message = ''): string
    {
        static::string($value);

        if (\preg_match('/^\s*$/', $value)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected a non-whitespace string. Got: %s',
                static::valueToString($value)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function startsWith(mixed $value, mixed $prefix, string|callable $message = ''): string
    {
        static::string($value);
        static::string($prefix);

        if (!\str_starts_with($value, $prefix)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected a value to start with %2$s. Got: %s',
                static::valueToString($value),
                static::valueToString($prefix)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function notStartsWith(mixed $value, mixed $prefix, string|callable $message = ''): string
    {
        static::string($value);
        static::string($prefix);

        if (\str_starts_with($value, $prefix)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected a value not to start with %2$s. Got: %s',
                static::valueToString($value),
                static::valueToString($prefix)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function startsWithLetter(mixed $value, string|callable $message = ''): string
    {
        static::string($value);

        $valid = isset($value[0]);

        if ($valid) {
            $locale = \setlocale(LC_CTYPE, '0');
            \setlocale(LC_CTYPE, 'C');
            $valid = \ctype_alpha($value[0]);
            \setlocale(LC_CTYPE, $locale);
        }

        if (!$valid) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected a value to start with a letter. Got: %s',
                static::valueToString($value)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function endsWith(mixed $value, mixed $suffix, string|callable $message = ''): string
    {
        static::string($value);
        static::string($suffix);

        if (!\str_ends_with($value, $suffix)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected a value to end with %2$s. Got: %s',
                static::valueToString($value),
                static::valueToString($suffix)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function notEndsWith(mixed $value, mixed $suffix, string|callable $message = ''): string
    {
        static::string($value);
        static::string($suffix);

        if (\str_ends_with($value, $suffix)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected a value not to end with %2$s. Got: %s',
                static::valueToString($value),
                static::valueToString($suffix)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function regex(mixed $value, mixed $pattern, string|callable $message = ''): string
    {
        static::string($value);
        static::string($pattern);

        if (!\preg_match($pattern, $value)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'The value %s does not match the expected pattern.',
                static::valueToString($value)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function notRegex(mixed $value, mixed $pattern, string|callable $message = ''): string
    {
        static::string($value);
        static::string($pattern);

        if (\preg_match($pattern, $value, $matches, PREG_OFFSET_CAPTURE)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'The value %s matches the pattern %s (at offset %d).',
                static::valueToString($value),
                static::valueToString($pattern),
                $matches[0][1]
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function unicodeLetters(mixed $value, string|callable $message = ''): string
    {
        static::string($value, $message);

        if (!\preg_match('/^\p{L}+$/u', $value)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected a value to contain only Unicode letters. Got: %s',
                static::valueToString($value)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function alpha(mixed $value, string|callable $message = ''): string
    {
        static::string($value, $message);

        $locale = \setlocale(LC_CTYPE, '0');
        \setlocale(LC_CTYPE, 'C');
        $valid = !\ctype_alpha($value);
        \setlocale(LC_CTYPE, $locale);

        if ($valid) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected a value to contain only letters. Got: %s',
                static::valueToString($value)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function digits(mixed $value, string|callable $message = ''): string
    {
        static::string($value, $message);

        $locale = \setlocale(LC_CTYPE, '0');
        \setlocale(LC_CTYPE, 'C');
        $valid = !\ctype_digit($value);
        \setlocale(LC_CTYPE, $locale);

        if ($valid) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected a value to contain digits only. Got: %s',
                static::valueToString($value)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function alnum(mixed $value, string|callable $message = ''): string
    {
        static::string($value, $message);

        $locale = \setlocale(LC_CTYPE, '0');
        \setlocale(LC_CTYPE, 'C');
        $valid = !\ctype_alnum($value);
        \setlocale(LC_CTYPE, $locale);

        if ($valid) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected a value to contain letters and digits only. Got: %s',
                static::valueToString($value)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert lowercase-string $value
     *
     * @param string|callable():string $message
     *
     * @return lowercase-string
     *
     * @throws InvalidArgumentException
     */
    public static function lower(mixed $value, string|callable $message = ''): string
    {
        static::string($value, $message);

        $locale = \setlocale(LC_CTYPE, '0');
        \setlocale(LC_CTYPE, 'C');
        $valid = !\ctype_lower($value);
        \setlocale(LC_CTYPE, $locale);

        if ($valid) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected a value to contain lowercase characters only. Got: %s',
                static::valueToString($value)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert string $value
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function upper(mixed $value, string|callable $message = ''): string
    {
        static::string($value, $message);

        $locale = \setlocale(LC_CTYPE, '0');
        \setlocale(LC_CTYPE, 'C');
        $valid = !\ctype_upper($value);
        \setlocale(LC_CTYPE, $locale);

        if ($valid) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected a value to contain uppercase characters only. Got: %s',
                static::valueToString($value)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function length(mixed $value, mixed $length, string|callable $message = ''): string
    {
        static::string($value);
        static::integerish($length);

        if ($length !== static::strlen($value)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected a value to contain %2$s characters. Got: %s',
                static::valueToString($value),
                $length
            ));
        }

        return $value;
    }

    /**
     * Inclusive min.
     *
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function minLength(mixed $value, mixed $min, string|callable $message = ''): string
    {
        static::string($value);
        static::integerish($min);

        if (static::strlen($value) < $min) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected a value to contain at least %2$s characters. Got: %s',
                static::valueToString($value),
                $min
            ));
        }

        return $value;
    }

    /**
     * Inclusive max.
     *
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function maxLength(mixed $value, mixed $max, string|callable $message = ''): string
    {
        static::string($value);
        static::integerish($max);

        if (static::strlen($value) > $max) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected a value to contain at most %2$s characters. Got: %s',
                static::valueToString($value),
                $max
            ));
        }

        return $value;
    }

    /**
     * Inclusive, so Assert::lengthBetween('asd', 3, 5); passes the assertion.
     *
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function lengthBetween(mixed $value, mixed $min, mixed $max, string|callable $message = ''): string
    {
        static::string($value);
        static::integerish($min);
        static::integerish($max);

        $length = static::strlen($value);

        if ($length < $min || $length > $max) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected a value to contain between %2$s and %3$s characters. Got: %s',
                static::valueToString($value),
                $min,
                $max
            ));
        }

        return $value;
    }

    /**
     * Will also pass if $value is a directory, use Assert::file() instead if you need to be sure it is a file.
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function fileExists(mixed $value, string|callable $message = ''): string
    {
        static::string($value);

        if (!\file_exists($value)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'The path %s does not exist.',
                static::valueToString($value)
            ));
        }

        return $value;
    }

    /**
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function file(mixed $value, string|callable $message = ''): string
    {
        static::string($value);

        if (!\is_file($value)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'The path %s is not a file.',
                static::valueToString($value)
            ));
        }

        return $value;
    }

    /**
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function directory(mixed $value, string|callable $message = ''): string
    {
        static::string($value);

        if (!\is_dir($value)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'The path %s is not a directory.',
                static::valueToString($value)
            ));
        }

        return $value;
    }

    /**
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function readable(mixed $value, string|callable $message = ''): string
    {
        static::string($value);

        if (!\is_readable($value)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'The path %s is not readable.',
                static::valueToString($value)
            ));
        }

        return $value;
    }

    /**
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function writable(mixed $value, string|callable $message = ''): string
    {
        static::string($value);

        if (!\is_writable($value)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'The path %s is not writable.',
                static::valueToString($value)
            ));
        }

        return $value;
    }

    /**
     * @psalm-assert class-string $value
     *
     * @param string|callable():string $message
     *
     * @return class-string
     *
     * @throws InvalidArgumentException
     */
    public static function classExists(mixed $value, string|callable $message = ''): string
    {
        static::string($value);

        if (!\class_exists($value)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected an existing class name. Got: %s',
                static::valueToString($value)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @template ExpectedType of object
     *
     * @psalm-assert class-string<ExpectedType> $value
     *
     * @param class-string<ExpectedType> $class
     * @param string|callable():string $message
     *
     * @return class-string<ExpectedType>
     *
     * @throws InvalidArgumentException
     */
    public static function subclassOf(mixed $value, mixed $class, string|callable $message = ''): string
    {
        static::string($value);
        static::string($class);

        if (!\is_subclass_of($value, $class)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected a sub-class of %2$s. Got: %s',
                static::valueToString($value),
                static::valueToString($class)
            ));
        }

        return $value;
    }

    /**
     * @psalm-assert class-string $value
     *
     * @param string|callable():string $message
     *
     * @return class-string
     *
     * @throws InvalidArgumentException
     */
    public static function interfaceExists(mixed $value, string|callable $message = ''): string
    {
        static::string($value);

        if (!\interface_exists($value)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected an existing interface name. got %s',
                static::valueToString($value)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @template ExpectedType of object
     *
     * @psalm-assert class-string<ExpectedType>|ExpectedType $value
     *
     * @param class-string<ExpectedType> $interface
     * @param string|callable():string $message
     *
     * @return class-string<ExpectedType>|ExpectedType
     *
     * @throws InvalidArgumentException
     */
    public static function implementsInterface(mixed $value, mixed $interface, string|callable $message = ''): object|string
    {
        static::objectish($value);

        $implements = \class_implements($value);

        static::isArray($implements);

        if (!\in_array($interface, $implements, true)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected an implementation of %2$s. Got: %s',
                static::valueToString($value),
                static::valueToString($interface)
            ));
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @template T as class-string|object
     * @param T $classOrObject
     * @param string|callable():string $message
     *
     * @return T
     * @throws InvalidArgumentException
     */
    public static function propertyExists(mixed $classOrObject, mixed $property, string|callable $message = ''): object|string
    {
        static::objectish($classOrObject);

        if (!\property_exists($classOrObject, $property)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected the property %s to exist.',
                static::valueToString($property)
            ));
        }

        return $classOrObject;
    }

    /**
     * @psalm-pure
     *
     * @template T as class-string|object
     * @param T $classOrObject
     * @param string|callable():string $message
     *
     * @return T
     * @throws InvalidArgumentException
     */
    public static function propertyNotExists(mixed $classOrObject, mixed $property, string|callable $message = ''): mixed
    {
        if (!(\is_string($classOrObject) || \is_object($classOrObject)) || \property_exists($classOrObject, $property)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected the property %s to not exist.',
                static::valueToString($property)
            ));
        }

        return $classOrObject;
    }

    /**
     * @psalm-pure
     *
     * @template T as class-string|object
     * @param T $classOrObject
     * @param string|callable():string $message
     *
     * @return T
     * @throws InvalidArgumentException
     */
    public static function methodExists(mixed $classOrObject, mixed $method, string|callable $message = ''): object|string
    {
        static::objectish($classOrObject);

        if (!\method_exists($classOrObject, $method)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected the method %s to exist.',
                static::valueToString($method)
            ));
        }

        return $classOrObject;
    }

    /**
     * @psalm-pure
     *
     * @template T as class-string|object
     * @param T $classOrObject
     * @param string|callable():string $message
     *
     * @return T
     * @throws InvalidArgumentException
     */
    public static function methodNotExists(mixed $classOrObject, mixed $method, string|callable $message = ''): mixed
    {
        static::objectish($classOrObject);

        if (\method_exists($classOrObject, $method)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected the method %s to not exist.',
                static::valueToString($method)
            ));
        }

        return $classOrObject;
    }

    /**
     * @psalm-pure
     *
     * @param string|int $key
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function keyExists(mixed $array, string|int $key, string|callable $message = ''): array
    {
        static::isArray($array, $message);

        if (!(isset($array[$key]) || \array_key_exists($key, $array))) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected the key %s to exist.',
                static::valueToString($key)
            ));
        }

        return $array;
    }

    /**
     * @psalm-pure
     *
     * @param iterable<string|int> $keys
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function keysExist(mixed $array, mixed $keys, string|callable $message = ''): array
    {
        static::isArray($array, $message);
        static::isIterable($keys);

        $missing = [];

        foreach ($keys as $key) {
            static::validArrayKey($key, 'Expected an array key. Got: %s');

            if (!(isset($array[$key]) || \array_key_exists($key, $array))) {
                $missing[] = $key;
            }
        }

        if ([] !== $missing) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected the keys %s to exist.',
                \implode(', ', \array_map(static::valueToString(...), $missing))
            ));
        }

        return $array;
    }

    /**
     * @psalm-pure
     *
     * @param string|int $key
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function keyNotExists(mixed $array, string|int $key, string|callable $message = ''): array
    {
        static::isArray($array, $message);

        if (isset($array[$key]) || \array_key_exists($key, $array)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected the key %s to not exist.',
                static::valueToString($key)
            ));
        }

        return $array;
    }

    /**
     * Checks if a value is a valid array key (int or string).
     *
     * @psalm-pure
     *
     * @psalm-assert array-key $value
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function validArrayKey(mixed $value, string|callable $message = ''): string|int
    {
        if (!(\is_int($value) || \is_string($value))) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected string or integer. Got: %s',
                static::typeToString($value)
            ));
        }

        return $value;
    }

    /**
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function count(mixed $array, mixed $number, string|callable $message = ''): array|Countable
    {
        static::isCountable($array);
        static::integerish($number);

        static::eq(
            \count($array),
            $number,
            fn () => static::resolveMessage($message) ?: \sprintf(
                $message ?: 'Expected an array to contain %d elements. Got: %d.',
                $number,
                \count($array)
            )
        );

        return $array;
    }

    /**
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function minCount(mixed $array, mixed $min, string|callable $message = ''): array|Countable
    {
        static::isCountable($array);
        static::integerish($min);

        if (\count($array) < $min) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected an array to contain at least %2$d elements. Got: %d',
                \count($array),
                $min
            ));
        }

        return $array;
    }

    /**
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function maxCount(mixed $array, mixed $max, string|callable $message = ''): array|Countable
    {
        static::isCountable($array);
        static::integerish($max);

        if (\count($array) > $max) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected an array to contain at most %2$d elements. Got: %d',
                \count($array),
                $max
            ));
        }

        return $array;
    }

    /**
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function countBetween(mixed $array, mixed $min, mixed $max, string|callable $message = ''): array|Countable
    {
        static::isCountable($array);
        static::integerish($min);
        static::integerish($max);

        $count = \count($array);

        if ($count < $min || $count > $max) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(\sprintf(
                $message ?: 'Expected an array to contain between %2$d and %3$d elements. Got: %d',
                $count,
                $min,
                $max
            ));
        }

        return $array;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert list<mixed> $array
     *
     * @param string|callable():string $message
     *
     * @return list<mixed>
     *
     * @throws InvalidArgumentException
     */
    public static function isList(mixed $array, string|callable $message = ''): array
    {
        if (!\is_array($array) || !\array_is_list($array)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(
                $message ?: 'Expected list - non-associative array.'
            );
        }

        return $array;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert non-empty-list<mixed> $array
     *
     * @param string|callable():string $message
     *
     * @return non-empty-list<mixed>
     *
     * @throws InvalidArgumentException
     */
    public static function isNonEmptyList(mixed $array, string|callable $message = ''): array
    {
        static::isList($array, $message);
        static::notEmpty($array, $message);

        return $array;
    }

    /**
     * @psalm-pure
     *
     * @template T
     *
     * @psalm-assert array<string, T> $array
     *
     * @param mixed|array<array-key, T> $array
     * @param string|callable():string $message
     *
     * @return array<string, T>
     *
     * @throws InvalidArgumentException
     */
    public static function isMap(mixed $array, string|callable $message = ''): array
    {
        static::isArray($array, $message);

        if (\count($array) > 0 && \array_is_list($array)) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(
                $message ?: 'Expected map - associative array with string keys.'
            );
        }

        return $array;
    }

    /**
     * @param callable $callable
     * @param string|callable():string $message
     *
     * @return Closure|callable-string
     *
     * @throws InvalidArgumentException
     */
    public static function isStatic(mixed $callable, string|callable $message = ''): Closure|string
    {
        static::isCallable($callable, $message);

        $callable = static::callableToClosure($callable);

        $reflection = new ReflectionFunction($callable);

        if (!$reflection->isStatic()) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(
                $message ?: 'Closure is not static.'
            );
        }

        return $callable;
    }

    /**
     * @param callable $callable
     * @param string|callable():string $message
     *
     * @return Closure|callable-string
     *
     * @throws InvalidArgumentException
     */
    public static function notStatic(mixed $callable, string|callable $message = ''): Closure|string
    {
        static::isCallable($callable, $message);

        $callable = static::callableToClosure($callable);

        $reflection = new ReflectionFunction($callable);

        if ($reflection->isStatic()) {
            $message = self::resolveMessage($message);
            static::reportInvalidArgument(
                $message ?: 'Closure is not static.'
            );
        }

        return $callable;
    }

    /**
     * @psalm-pure
     *
     * @template T
     *
     * @psalm-assert array<string, T> $array
     * @psalm-assert !empty $array
     *
     * @param array<string, T> $array
     * @param string|callable():string $message
     *
     * @return non-empty-array<string, T>
     *
     * @throws InvalidArgumentException
     */
    public static function isNonEmptyMap(mixed $array, string|callable $message = ''): array
    {
        static::isMap($array, $message);
        static::notEmpty($array, $message);

        return $array;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @throws InvalidArgumentException
     */
    public static function uuid(mixed $value, string|callable $message = ''): string
    {
        static::string($value, $message);

        $uuid = '[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}';

        // URN form as specified by RFC 9562, e.g. "urn:uuid:ff6f8cb0-...".
        if (\str_starts_with($value, 'urn:uuid:') && \preg_match('/^urn:uuid:'.$uuid.'$/D', $value)) {
            return $value;
        }

        // "uuid:" prefix, optionally combined with the curly-braced form.
        if (\str_starts_with($value, 'uuid:') && \preg_match('/^uuid:(?:'.$uuid.'|\{'.$uuid.'\})$/D', $value)) {
            return $value;
        }

        // Curly-braced form; the braces must be a matching pair.
        if (\str_starts_with($value, '{') && \str_ends_with($value, '}') && \preg_match('/^\{'.$uuid.'\}$/D', $value)) {
            return $value;
        }

        // Plain form, including the nil UUID with all 128 bits set to zero.
        if (\preg_match('/^'.$uuid.'$/D', $value)) {
            return $value;
        }

        $message = self::resolveMessage($message);
        static::reportInvalidArgument(\sprintf(
            $message ?: 'Value %s is not a valid UUID.',
            static::valueToString($value)
        ));
    }

    /**
     * @template T as callable
     *
     * @param T $expression
     * @param string|callable():string $message
     * @param class-string<Throwable> $class
     *
     * @return T
     *
     * @throws InvalidArgumentException
     */
    public static function throws(mixed $expression, string $class = Throwable::class, string|callable $message = ''): callable
    {
        static::string($class);
        static::isCallable($expression);

        $actual = 'none';

        try {
            $expression();
        } catch (Throwable $e) {
            $actual = \get_class($e);
            if ($e instanceof $class) {
                return $expression;
            }
        }

        $message = self::resolveMessage($message);

        static::reportInvalidArgument($message ?: \sprintf(
            'Expected to throw "%s", got "%s"',
            $class,
            $actual
        ));
    }

    /**
     * @psalm-pure
     *
     * @return Closure|callable-string
     */
    protected static function callableToClosure(callable $callable): Closure|string
    {
        if (\is_string($callable) && \function_exists($callable)) {
            return $callable;
        }

        if ($callable instanceof Closure) {
            return $callable;
        }

        return $callable(...);
    }

    /**
     * @psalm-pure
     */
    protected static function valueToString(mixed $value): string
    {
        if (null === $value) {
            return 'null';
        }

        if (true === $value) {
            return 'true';
        }

        if (false === $value) {
            return 'false';
        }

        if (\is_array($value)) {
            return 'array';
        }

        if (\is_object($value)) {
            if (\method_exists($value, '__toString')) {
                return \get_class($value).': '.self::valueToString($value->__toString());
            }

            if ($value instanceof DateTime || $value instanceof DateTimeImmutable) {
                return \get_class($value).': '.self::valueToString($value->format('c'));
            }

            if (\enum_exists(\get_class($value))) {
                return \get_class($value).'::'.$value->name;
            }

            return \get_class($value);
        }

        if (\is_resource($value)) {
            return 'resource';
        }

        if (\is_string($value)) {
            return '"'.$value.'"';
        }

        return (string) $value;
    }

    /**
     * @psalm-pure
     */
    protected static function typeToString(mixed $value): string
    {
        return \is_object($value) ? \get_class($value) : \gettype($value);
    }

    protected static function strlen(string $value): int
    {
        if (!\function_exists('mb_detect_encoding')) {
            return \strlen($value);
        }

        if (false === $encoding = \mb_detect_encoding($value)) {
            return \strlen($value);
        }

        return \mb_strlen($value, $encoding);
    }

    /**
     * @psalm-pure this method is not supposed to perform side effects
     *
     * @throws InvalidArgumentException
     */
    protected static function reportInvalidArgument(string $message): never
    {
        throw new InvalidArgumentException($message);
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     */
    protected static function resolveMessage(string|callable $message): string
    {
        return \is_callable($message) ? $message() : $message;
    }

    private function __construct()
    {
    }
}
<?php

declare(strict_types=1);

namespace Webmozart\Assert;

/** @internal Used by the Psalm plugin */
final class HasAssert
{
    public const HAS_ASSERT = [
        'string' => true,
        'nullorstring' => true,
        'allstring' => true,
        'allnullorstring' => true,
        'stringnotempty' => true,
        'nullorstringnotempty' => true,
        'allstringnotempty' => true,
        'allnullorstringnotempty' => true,
        'integer' => true,
        'nullorinteger' => true,
        'allinteger' => true,
        'allnullorinteger' => true,
        'integerish' => true,
        'nullorintegerish' => true,
        'allintegerish' => true,
        'allnullorintegerish' => true,
        'positiveinteger' => true,
        'nullorpositiveinteger' => true,
        'allpositiveinteger' => true,
        'allnullorpositiveinteger' => true,
        'notnegativeinteger' => true,
        'nullornotnegativeinteger' => true,
        'allnotnegativeinteger' => true,
        'allnullornotnegativeinteger' => true,
        'negativeinteger' => true,
        'nullornegativeinteger' => true,
        'allnegativeinteger' => true,
        'allnullornegativeinteger' => true,
        'float' => true,
        'nullorfloat' => true,
        'allfloat' => true,
        'allnullorfloat' => true,
        'numeric' => true,
        'nullornumeric' => true,
        'allnumeric' => true,
        'allnullornumeric' => true,
        'natural' => true,
        'nullornatural' => true,
        'allnatural' => true,
        'allnullornatural' => true,
        'boolean' => true,
        'nullorboolean' => true,
        'allboolean' => true,
        'allnullorboolean' => true,
        'scalar' => true,
        'nullorscalar' => true,
        'allscalar' => true,
        'allnullorscalar' => true,
        'object' => true,
        'nullorobject' => true,
        'allobject' => true,
        'allnullorobject' => true,
        'objectish' => true,
        'nullorobjectish' => true,
        'allobjectish' => true,
        'allnullorobjectish' => true,
        'resource' => true,
        'nullorresource' => true,
        'allresource' => true,
        'allnullorresource' => true,
        'iscallable' => true,
        'nulloriscallable' => true,
        'alliscallable' => true,
        'allnulloriscallable' => true,
        'isarray' => true,
        'nullorisarray' => true,
        'allisarray' => true,
        'allnullorisarray' => true,
        'isarrayaccessible' => true,
        'nullorisarrayaccessible' => true,
        'allisarrayaccessible' => true,
        'allnullorisarrayaccessible' => true,
        'iscountable' => true,
        'nulloriscountable' => true,
        'alliscountable' => true,
        'allnulloriscountable' => true,
        'isiterable' => true,
        'nullorisiterable' => true,
        'allisiterable' => true,
        'allnullorisiterable' => true,
        'isinstanceof' => true,
        'nullorisinstanceof' => true,
        'allisinstanceof' => true,
        'allnullorisinstanceof' => true,
        'notinstanceof' => true,
        'allnullornotinstanceof' => true,
        'isinstanceofany' => true,
        'nullorisinstanceofany' => true,
        'allisinstanceofany' => true,
        'allnullorisinstanceofany' => true,
        'isnotinstanceofany' => true,
        'nullorisnotinstanceofany' => true,
        'allisnotinstanceofany' => true,
        'allnullorisnotinstanceofany' => true,
        'isaof' => true,
        'nullorisaof' => true,
        'allisaof' => true,
        'allnullorisaof' => true,
        'isnota' => true,
        'allnullorisnota' => true,
        'isanyof' => true,
        'nullorisanyof' => true,
        'allisanyof' => true,
        'allnullorisanyof' => true,
        'isempty' => true,
        'nullorisempty' => true,
        'allisempty' => true,
        'allnullorisempty' => true,
        'notempty' => true,
        'allnullornotempty' => true,
        'null' => true,
        'allnull' => true,
        'notnull' => true,
        'true' => true,
        'nullortrue' => true,
        'alltrue' => true,
        'allnullortrue' => true,
        'false' => true,
        'nullorfalse' => true,
        'allfalse' => true,
        'allnullorfalse' => true,
        'notfalse' => true,
        'allnullornotfalse' => true,
        'uniquevalues' => true,
        'nulloruniquevalues' => true,
        'alluniquevalues' => true,
        'allnulloruniquevalues' => true,
        'lower' => true,
        'nullorlower' => true,
        'alllower' => true,
        'allnullorlower' => true,
        'upper' => true,
        'allnullorupper' => true,
        'classexists' => true,
        'nullorclassexists' => true,
        'allclassexists' => true,
        'allnullorclassexists' => true,
        'subclassof' => true,
        'nullorsubclassof' => true,
        'allsubclassof' => true,
        'allnullorsubclassof' => true,
        'interfaceexists' => true,
        'nullorinterfaceexists' => true,
        'allinterfaceexists' => true,
        'allnullorinterfaceexists' => true,
        'implementsinterface' => true,
        'nullorimplementsinterface' => true,
        'allimplementsinterface' => true,
        'allnullorimplementsinterface' => true,
        'validarraykey' => true,
        'nullorvalidarraykey' => true,
        'allvalidarraykey' => true,
        'allnullorvalidarraykey' => true,
        'islist' => true,
        'nullorislist' => true,
        'allislist' => true,
        'allnullorislist' => true,
        'isnonemptylist' => true,
        'nullorisnonemptylist' => true,
        'allisnonemptylist' => true,
        'allnullorisnonemptylist' => true,
        'ismap' => true,
        'nullorismap' => true,
        'allismap' => true,
        'allnullorismap' => true,
        'isnonemptymap' => true,
        'allnullorisnonemptymap' => true,
    ];
}
<?php

declare(strict_types=1);

namespace Webmozart\Assert;

use ArrayAccess;
use Closure;
use Countable;
use Throwable;

/**
 * This trait provides nullOr*, all* and allNullOr* variants of assertion base methods.
 * Do not use this trait directly: it will change, and is not designed for reuse.
 */
trait Mixin
{
    /**
     * @psalm-pure
     *
     * @psalm-assert string|null $value
     *
     * @param string|callable():string $message
     *
     * @return string|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrString(mixed $value, callable|string $message = ''): ?string
    {
        null === $value || static::string($value, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<string> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<string>
     *
     * @throws InvalidArgumentException
     */
    public static function allString(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::string($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<string|null> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<string|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrString(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::string($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert non-empty-string|null $value
     *
     * @param string|callable():string $message
     *
     * @return non-empty-string|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrStringNotEmpty(mixed $value, callable|string $message = ''): ?string
    {
        null === $value || static::stringNotEmpty($value, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<non-empty-string> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<non-empty-string>
     *
     * @throws InvalidArgumentException
     */
    public static function allStringNotEmpty(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::stringNotEmpty($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<non-empty-string|null> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<non-empty-string|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrStringNotEmpty(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::stringNotEmpty($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert int|null $value
     *
     * @param string|callable():string $message
     *
     * @return int|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrInteger(mixed $value, callable|string $message = ''): ?int
    {
        null === $value || static::integer($value, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<int> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<int>
     *
     * @throws InvalidArgumentException
     */
    public static function allInteger(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::integer($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<int|null> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<int|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrInteger(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::integer($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert numeric|null $value
     *
     * @param string|callable():string $message
     *
     * @return numeric|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrIntegerish(mixed $value, callable|string $message = ''): string|int|float|null
    {
        null === $value || static::integerish($value, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<numeric> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<numeric>
     *
     * @throws InvalidArgumentException
     */
    public static function allIntegerish(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::integerish($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<numeric|null> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<numeric|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrIntegerish(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::integerish($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert positive-int|null $value
     *
     * @param string|callable():string $message
     *
     * @return positive-int|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrPositiveInteger(mixed $value, callable|string $message = ''): ?int
    {
        null === $value || static::positiveInteger($value, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<positive-int> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<positive-int>
     *
     * @throws InvalidArgumentException
     */
    public static function allPositiveInteger(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::positiveInteger($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<positive-int|null> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<positive-int|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrPositiveInteger(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::positiveInteger($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert non-negative-int|null $value
     *
     * @param string|callable():string $message
     *
     * @return non-negative-int|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrNotNegativeInteger(mixed $value, callable|string $message = ''): ?int
    {
        null === $value || static::notNegativeInteger($value, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<non-negative-int> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<non-negative-int>
     *
     * @throws InvalidArgumentException
     */
    public static function allNotNegativeInteger(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::notNegativeInteger($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<non-negative-int|null> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<non-negative-int|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrNotNegativeInteger(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::notNegativeInteger($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert negative-int|null $value
     *
     * @param string|callable():string $message
     *
     * @return negative-int|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrNegativeInteger(mixed $value, callable|string $message = ''): ?int
    {
        null === $value || static::negativeInteger($value, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<negative-int> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<negative-int>
     *
     * @throws InvalidArgumentException
     */
    public static function allNegativeInteger(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::negativeInteger($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<negative-int|null> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<negative-int|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrNegativeInteger(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::negativeInteger($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert float|null $value
     *
     * @param string|callable():string $message
     *
     * @return float|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrFloat(mixed $value, callable|string $message = ''): ?float
    {
        null === $value || static::float($value, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<float> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<float>
     *
     * @throws InvalidArgumentException
     */
    public static function allFloat(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::float($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<float|null> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<float|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrFloat(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::float($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert numeric|null $value
     *
     * @param string|callable():string $message
     *
     * @return numeric|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrNumeric(mixed $value, callable|string $message = ''): string|int|float|null
    {
        null === $value || static::numeric($value, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<numeric> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<numeric>
     *
     * @throws InvalidArgumentException
     */
    public static function allNumeric(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::numeric($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<numeric|null> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<numeric|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrNumeric(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::numeric($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert positive-int|0|null $value
     *
     * @param string|callable():string $message
     *
     * @return positive-int|0|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrNatural(mixed $value, callable|string $message = ''): ?int
    {
        null === $value || static::natural($value, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<positive-int|0> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<positive-int|0>
     *
     * @throws InvalidArgumentException
     */
    public static function allNatural(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::natural($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<positive-int|0|null> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<positive-int|0|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrNatural(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::natural($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert bool|null $value
     *
     * @param string|callable():string $message
     *
     * @return bool|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrBoolean(mixed $value, callable|string $message = ''): ?bool
    {
        null === $value || static::boolean($value, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<bool> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<bool>
     *
     * @throws InvalidArgumentException
     */
    public static function allBoolean(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::boolean($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<bool|null> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<bool|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrBoolean(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::boolean($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert scalar|null $value
     *
     * @param string|callable():string $message
     *
     * @return scalar|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrScalar(mixed $value, callable|string $message = ''): string|int|float|bool|null
    {
        null === $value || static::scalar($value, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<scalar> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<scalar>
     *
     * @throws InvalidArgumentException
     */
    public static function allScalar(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::scalar($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<scalar|null> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<scalar|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrScalar(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::scalar($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert object|null $value
     *
     * @param string|callable():string $message
     *
     * @return object|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrObject(mixed $value, callable|string $message = ''): ?object
    {
        null === $value || static::object($value, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<object> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<object>
     *
     * @throws InvalidArgumentException
     */
    public static function allObject(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::object($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<object|null> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<object|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrObject(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::object($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert object|class-string|null $value
     *
     * @param string|callable():string $message
     *
     * @return object|class-string|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrObjectish(mixed $value, callable|string $message = ''): object|string|null
    {
        null === $value || static::objectish($value, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<object|class-string> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<object|class-string>
     *
     * @throws InvalidArgumentException
     */
    public static function allObjectish(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::objectish($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<object|class-string|null> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<object|class-string|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrObjectish(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::objectish($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert resource|null $value
     *
     * @param string|callable():string $message
     *
     * @see https://www.php.net/manual/en/function.get-resource-type.php
     *
     * @return resource|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrResource(mixed $value, ?string $type = null, callable|string $message = ''): mixed
    {
        null === $value || static::resource($value, $type, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<resource> $value
     *
     * @param string|callable():string $message
     *
     * @see https://www.php.net/manual/en/function.get-resource-type.php
     *
     * @return iterable<resource>
     *
     * @throws InvalidArgumentException
     */
    public static function allResource(mixed $value, ?string $type = null, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::resource($entry, $type, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<resource|null> $value
     *
     * @param string|callable():string $message
     *
     * @see https://www.php.net/manual/en/function.get-resource-type.php
     *
     * @return iterable<resource|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrResource(mixed $value, ?string $type = null, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::resource($entry, $type, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert callable|null $value
     *
     * @param string|callable():string $message
     *
     * @return callable|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrIsCallable(mixed $value, callable|string $message = ''): ?callable
    {
        null === $value || static::isCallable($value, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<callable> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<callable>
     *
     * @throws InvalidArgumentException
     */
    public static function allIsCallable(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::isCallable($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<callable|null> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<callable|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrIsCallable(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::isCallable($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert array|null $value
     *
     * @param string|callable():string $message
     *
     * @return array|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrIsArray(mixed $value, callable|string $message = ''): ?array
    {
        null === $value || static::isArray($value, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<array> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<array>
     *
     * @throws InvalidArgumentException
     */
    public static function allIsArray(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::isArray($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<array|null> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<array|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrIsArray(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::isArray($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert array|ArrayAccess|null $value
     *
     * @param string|callable():string $message
     *
     * @return array|ArrayAccess|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrIsArrayAccessible(mixed $value, callable|string $message = ''): ArrayAccess|array|null
    {
        null === $value || static::isArrayAccessible($value, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<array|ArrayAccess> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<array|ArrayAccess>
     *
     * @throws InvalidArgumentException
     */
    public static function allIsArrayAccessible(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::isArrayAccessible($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<array|ArrayAccess|null> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<array|ArrayAccess|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrIsArrayAccessible(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::isArrayAccessible($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert countable|null $value
     *
     * @param string|callable():string $message
     *
     * @return countable|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrIsCountable(mixed $value, callable|string $message = ''): Countable|array|null
    {
        null === $value || static::isCountable($value, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<countable> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<countable>
     *
     * @throws InvalidArgumentException
     */
    public static function allIsCountable(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::isCountable($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<countable|null> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<countable|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrIsCountable(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::isCountable($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable|null $value
     *
     * @param string|callable():string $message
     *
     * @return iterable|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrIsIterable(mixed $value, callable|string $message = ''): ?iterable
    {
        null === $value || static::isIterable($value, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<iterable> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<iterable>
     *
     * @throws InvalidArgumentException
     */
    public static function allIsIterable(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::isIterable($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<iterable|null> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<iterable|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrIsIterable(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::isIterable($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @template T of object
     * @psalm-assert T|null $value
     *
     * @param class-string<T>          $class
     * @param string|callable():string $message
     *
     * @return T|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrIsInstanceOf(mixed $value, mixed $class, callable|string $message = ''): ?object
    {
        null === $value || static::isInstanceOf($value, $class, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @template T of object
     * @psalm-assert iterable<T> $value
     *
     * @param class-string<T>          $class
     * @param string|callable():string $message
     *
     * @return iterable<T>
     *
     * @throws InvalidArgumentException
     */
    public static function allIsInstanceOf(mixed $value, mixed $class, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::isInstanceOf($entry, $class, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @template T of object|null
     * @psalm-assert iterable<T> $value
     *
     * @param class-string<T>          $class
     * @param string|callable():string $message
     *
     * @return iterable<T>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrIsInstanceOf(mixed $value, mixed $class, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::isInstanceOf($entry, $class, $message);
        }

        return $value;
    }

    /**
     * @template T of object
     *
     * @param class-string<T>          $class
     * @param string|callable():string $message
     *
     * @return object|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrNotInstanceOf(mixed $value, mixed $class, callable|string $message = ''): ?object
    {
        null === $value || static::notInstanceOf($value, $class, $message);

        return $value;
    }

    /**
     * @template T of object
     *
     * @param class-string<T>          $class
     * @param string|callable():string $message
     *
     * @return iterable<object>
     *
     * @throws InvalidArgumentException
     */
    public static function allNotInstanceOf(mixed $value, mixed $class, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::notInstanceOf($entry, $class, $message);
        }

        return $value;
    }

    /**
     * @template T of object|null
     * @psalm-assert iterable<object|null> $value
     *
     * @param class-string<T>          $class
     * @param string|callable():string $message
     *
     * @return iterable<object|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrNotInstanceOf(mixed $value, mixed $class, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::notInstanceOf($entry, $class, $message);
        }

        return $value;
    }

    /**
     * @template T of object
     * @psalm-assert T|null $value
     *
     * @param iterable<class-string<T>> $classes
     * @param string|callable():string  $message
     *
     * @return T|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrIsInstanceOfAny(mixed $value, mixed $classes, callable|string $message = ''): ?object
    {
        null === $value || static::isInstanceOfAny($value, $classes, $message);

        return $value;
    }

    /**
     * @template T of object
     * @psalm-assert iterable<T> $value
     *
     * @param iterable<class-string<T>> $classes
     * @param string|callable():string  $message
     *
     * @return iterable<T>
     *
     * @throws InvalidArgumentException
     */
    public static function allIsInstanceOfAny(mixed $value, mixed $classes, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::isInstanceOfAny($entry, $classes, $message);
        }

        return $value;
    }

    /**
     * @template T of object|null
     * @psalm-assert iterable<T> $value
     *
     * @param iterable<class-string<T>> $classes
     * @param string|callable():string  $message
     *
     * @return iterable<T>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrIsInstanceOfAny(mixed $value, mixed $classes, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::isInstanceOfAny($entry, $classes, $message);
        }

        return $value;
    }

    /**
     * @template T
     * @psalm-assert object|class-string|null $value
     *
     * @param T|null                   $value
     * @param string|callable():string $message
     *
     * @return T|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrIsNotInstanceOfAny(mixed $value, mixed $classes, callable|string $message = ''): mixed
    {
        null === $value || static::isNotInstanceOfAny($value, $classes, $message);

        return $value;
    }

    /**
     * @template T
     * @psalm-assert iterable<object|class-string> $value
     *
     * @param iterable<T>              $value
     * @param string|callable():string $message
     *
     * @return iterable<T>
     *
     * @throws InvalidArgumentException
     */
    public static function allIsNotInstanceOfAny(mixed $value, mixed $classes, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::isNotInstanceOfAny($entry, $classes, $message);
        }

        return $value;
    }

    /**
     * @template T
     * @psalm-assert iterable<object|class-string|null> $value
     *
     * @param iterable<T>              $value
     * @param string|callable():string $message
     *
     * @return iterable<T>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrIsNotInstanceOfAny(mixed $value, mixed $classes, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::isNotInstanceOfAny($entry, $classes, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @template T of object
     * @psalm-assert T|class-string<T>|null $value
     *
     * @param class-string<T>          $class
     * @param string|callable():string $message
     *
     * @return T|class-string<T>|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrIsAOf(mixed $value, mixed $class, callable|string $message = ''): object|string|null
    {
        null === $value || static::isAOf($value, $class, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @template T of object
     * @psalm-assert iterable<T|class-string<T>> $value
     *
     * @param class-string<T>          $class
     * @param string|callable():string $message
     *
     * @return iterable<T|class-string<T>>
     *
     * @throws InvalidArgumentException
     */
    public static function allIsAOf(mixed $value, mixed $class, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::isAOf($entry, $class, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @template T of object|null
     * @psalm-assert iterable<T|class-string<T>|null> $value
     *
     * @param class-string<T>          $class
     * @param string|callable():string $message
     *
     * @return iterable<T|class-string<T>|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrIsAOf(mixed $value, mixed $class, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::isAOf($entry, $class, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @template T
     *
     * @param T|null                   $value
     * @param string|callable():string $message
     *
     * @return object|class-string|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrIsNotA(mixed $value, mixed $class, callable|string $message = ''): object|string|null
    {
        null === $value || static::isNotA($value, $class, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @template T
     *
     * @param iterable<T>              $value
     * @param string|callable():string $message
     *
     * @return iterable<object|class-string>
     *
     * @throws InvalidArgumentException
     */
    public static function allIsNotA(mixed $value, mixed $class, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::isNotA($entry, $class, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @template T
     * @psalm-assert iterable<object|class-string|null> $value
     *
     * @param iterable<T>              $value
     * @param string|callable():string $message
     *
     * @return iterable<object|class-string|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrIsNotA(mixed $value, mixed $class, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::isNotA($entry, $class, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert T|null $value
     *
     * @template T as object
     *
     * @param array<class-string<T>>   $classes
     * @param string|callable():string $message
     *
     * @return T|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrIsAnyOf(mixed $value, mixed $classes, callable|string $message = ''): object|string|null
    {
        null === $value || static::isAnyOf($value, $classes, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<T> $value
     *
     * @template T as object
     *
     * @param array<class-string<T>>   $classes
     * @param string|callable():string $message
     *
     * @return iterable<T>
     *
     * @throws InvalidArgumentException
     */
    public static function allIsAnyOf(mixed $value, mixed $classes, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::isAnyOf($entry, $classes, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<T> $value
     *
     * @template T as object|null
     *
     * @param array<class-string<T>>   $classes
     * @param string|callable():string $message
     *
     * @return iterable<T>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrIsAnyOf(mixed $value, mixed $classes, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::isAnyOf($entry, $classes, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert empty $value
     *
     * @param string|callable():string $message
     *
     * @return empty
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrIsEmpty(mixed $value, callable|string $message = ''): mixed
    {
        null === $value || static::isEmpty($value, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<empty> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<empty>
     *
     * @throws InvalidArgumentException
     */
    public static function allIsEmpty(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::isEmpty($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<empty|null> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<empty|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrIsEmpty(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::isEmpty($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return mixed
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrNotEmpty(mixed $value, callable|string $message = ''): mixed
    {
        null === $value || static::notEmpty($value, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable
     *
     * @throws InvalidArgumentException
     */
    public static function allNotEmpty(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::notEmpty($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<!empty|null> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<!empty|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrNotEmpty(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::notEmpty($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<null> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNull(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::null($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @template T
     *
     * @param iterable<T|null>         $value
     * @param string|callable():string $message
     *
     * @return iterable<T>
     *
     * @throws InvalidArgumentException
     */
    public static function allNotNull(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::notNull($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert true|null $value
     *
     * @param string|callable():string $message
     *
     * @return true|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrTrue(mixed $value, callable|string $message = ''): ?true
    {
        null === $value || static::true($value, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<true> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<true>
     *
     * @throws InvalidArgumentException
     */
    public static function allTrue(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::true($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<true|null> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<true|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrTrue(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::true($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert false|null $value
     *
     * @param string|callable():string $message
     *
     * @return false|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrFalse(mixed $value, callable|string $message = ''): ?false
    {
        null === $value || static::false($value, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<false> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<false>
     *
     * @throws InvalidArgumentException
     */
    public static function allFalse(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::false($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<false|null> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<false|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrFalse(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::false($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @template T
     *
     * @param T|false|null             $value
     * @param string|callable():string $message
     *
     * @return T|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrNotFalse(mixed $value, callable|string $message = ''): mixed
    {
        null === $value || static::notFalse($value, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @template T
     *
     * @param iterable<T|false>        $value
     * @param string|callable():string $message
     *
     * @return iterable<T>
     *
     * @throws InvalidArgumentException
     */
    public static function allNotFalse(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::notFalse($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<!false|null> $value
     *
     * @template T
     *
     * @param iterable<T|false|null>   $value
     * @param string|callable():string $message
     *
     * @return iterable<T>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrNotFalse(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::notFalse($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|null              $value
     * @param string|callable():string $message
     *
     * @return string|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrIp(mixed $value, callable|string $message = ''): ?string
    {
        null === $value || static::ip($value, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param iterable<string>         $value
     * @param string|callable():string $message
     *
     * @return iterable<string>
     *
     * @throws InvalidArgumentException
     */
    public static function allIp(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::ip($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param iterable<string|null>    $value
     * @param string|callable():string $message
     *
     * @return iterable<string|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrIp(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::ip($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|null              $value
     * @param string|callable():string $message
     *
     * @return string|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrIpv4(mixed $value, callable|string $message = ''): ?string
    {
        null === $value || static::ipv4($value, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param iterable<string>         $value
     * @param string|callable():string $message
     *
     * @return iterable<string>
     *
     * @throws InvalidArgumentException
     */
    public static function allIpv4(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::ipv4($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param iterable<string|null>    $value
     * @param string|callable():string $message
     *
     * @return iterable<string|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrIpv4(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::ipv4($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|null              $value
     * @param string|callable():string $message
     *
     * @return string|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrIpv6(mixed $value, callable|string $message = ''): ?string
    {
        null === $value || static::ipv6($value, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param iterable<string>         $value
     * @param string|callable():string $message
     *
     * @return iterable<string>
     *
     * @throws InvalidArgumentException
     */
    public static function allIpv6(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::ipv6($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param iterable<string|null>    $value
     * @param string|callable():string $message
     *
     * @return iterable<string|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrIpv6(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::ipv6($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|null              $value
     * @param string|callable():string $message
     *
     * @return string|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrEmail(mixed $value, callable|string $message = ''): ?string
    {
        null === $value || static::email($value, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param iterable<string>         $value
     * @param string|callable():string $message
     *
     * @return iterable<string>
     *
     * @throws InvalidArgumentException
     */
    public static function allEmail(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::email($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param iterable<string|null>    $value
     * @param string|callable():string $message
     *
     * @return iterable<string|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrEmail(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::email($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-assert array|null $values
     *
     * @param string|callable():string $message
     *
     * @return array|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrUniqueValues(mixed $values, callable|string $message = ''): ?array
    {
        null === $values || static::uniqueValues($values, $message);

        return $values;
    }

    /**
     * @psalm-assert iterable<array> $values
     *
     * @param string|callable():string $message
     *
     * @return iterable<array>
     *
     * @throws InvalidArgumentException
     */
    public static function allUniqueValues(mixed $values, callable|string $message = ''): iterable
    {
        static::isIterable($values);

        foreach ($values as $entry) {
            static::uniqueValues($entry, $message);
        }

        return $values;
    }

    /**
     * @psalm-assert iterable<array|null> $values
     *
     * @param string|callable():string $message
     *
     * @return iterable<array|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrUniqueValues(mixed $values, callable|string $message = ''): iterable
    {
        static::isIterable($values);

        foreach ($values as $entry) {
            null === $entry || static::uniqueValues($entry, $message);
        }

        return $values;
    }

    /**
     * @param string|callable():string $message
     *
     * @return mixed
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrEq(mixed $value, mixed $expect, callable|string $message = ''): mixed
    {
        null === $value || static::eq($value, $expect, $message);

        return $value;
    }

    /**
     * @param string|callable():string $message
     *
     * @return iterable
     *
     * @throws InvalidArgumentException
     */
    public static function allEq(mixed $value, mixed $expect, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::eq($entry, $expect, $message);
        }

        return $value;
    }

    /**
     * @param string|callable():string $message
     *
     * @return iterable
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrEq(mixed $value, mixed $expect, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::eq($entry, $expect, $message);
        }

        return $value;
    }

    /**
     * @param string|callable():string $message
     *
     * @return mixed
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrNotEq(mixed $value, mixed $expect, callable|string $message = ''): mixed
    {
        null === $value || static::notEq($value, $expect, $message);

        return $value;
    }

    /**
     * @param string|callable():string $message
     *
     * @return iterable
     *
     * @throws InvalidArgumentException
     */
    public static function allNotEq(mixed $value, mixed $expect, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::notEq($entry, $expect, $message);
        }

        return $value;
    }

    /**
     * @param string|callable():string $message
     *
     * @return iterable
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrNotEq(mixed $value, mixed $expect, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::notEq($entry, $expect, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return mixed
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrSame(mixed $value, mixed $expect, callable|string $message = ''): mixed
    {
        null === $value || static::same($value, $expect, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable
     *
     * @throws InvalidArgumentException
     */
    public static function allSame(mixed $value, mixed $expect, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::same($entry, $expect, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrSame(mixed $value, mixed $expect, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::same($entry, $expect, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return mixed
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrNotSame(mixed $value, mixed $expect, callable|string $message = ''): mixed
    {
        null === $value || static::notSame($value, $expect, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable
     *
     * @throws InvalidArgumentException
     */
    public static function allNotSame(mixed $value, mixed $expect, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::notSame($entry, $expect, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrNotSame(mixed $value, mixed $expect, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::notSame($entry, $expect, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return mixed
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrGreaterThan(mixed $value, mixed $limit, callable|string $message = ''): mixed
    {
        null === $value || static::greaterThan($value, $limit, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable
     *
     * @throws InvalidArgumentException
     */
    public static function allGreaterThan(mixed $value, mixed $limit, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::greaterThan($entry, $limit, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrGreaterThan(mixed $value, mixed $limit, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::greaterThan($entry, $limit, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return mixed
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrGreaterThanEq(mixed $value, mixed $limit, callable|string $message = ''): mixed
    {
        null === $value || static::greaterThanEq($value, $limit, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable
     *
     * @throws InvalidArgumentException
     */
    public static function allGreaterThanEq(mixed $value, mixed $limit, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::greaterThanEq($entry, $limit, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrGreaterThanEq(mixed $value, mixed $limit, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::greaterThanEq($entry, $limit, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return mixed
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrLessThan(mixed $value, mixed $limit, callable|string $message = ''): mixed
    {
        null === $value || static::lessThan($value, $limit, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable
     *
     * @throws InvalidArgumentException
     */
    public static function allLessThan(mixed $value, mixed $limit, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::lessThan($entry, $limit, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrLessThan(mixed $value, mixed $limit, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::lessThan($entry, $limit, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return mixed
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrLessThanEq(mixed $value, mixed $limit, callable|string $message = ''): mixed
    {
        null === $value || static::lessThanEq($value, $limit, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable
     *
     * @throws InvalidArgumentException
     */
    public static function allLessThanEq(mixed $value, mixed $limit, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::lessThanEq($entry, $limit, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrLessThanEq(mixed $value, mixed $limit, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::lessThanEq($entry, $limit, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return mixed
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrRange(mixed $value, mixed $min, mixed $max, callable|string $message = ''): mixed
    {
        null === $value || static::range($value, $min, $max, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable
     *
     * @throws InvalidArgumentException
     */
    public static function allRange(mixed $value, mixed $min, mixed $max, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::range($entry, $min, $max, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrRange(mixed $value, mixed $min, mixed $max, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::range($entry, $min, $max, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return mixed
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrOneOf(mixed $value, mixed $values, callable|string $message = ''): mixed
    {
        null === $value || static::oneOf($value, $values, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable
     *
     * @throws InvalidArgumentException
     */
    public static function allOneOf(mixed $value, mixed $values, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::oneOf($entry, $values, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrOneOf(mixed $value, mixed $values, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::oneOf($entry, $values, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return mixed
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrInArray(mixed $value, mixed $values, callable|string $message = ''): mixed
    {
        null === $value || static::inArray($value, $values, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable
     *
     * @throws InvalidArgumentException
     */
    public static function allInArray(mixed $value, mixed $values, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::inArray($entry, $values, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrInArray(mixed $value, mixed $values, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::inArray($entry, $values, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return mixed
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrNotOneOf(mixed $value, mixed $values, callable|string $message = ''): mixed
    {
        null === $value || static::notOneOf($value, $values, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable
     *
     * @throws InvalidArgumentException
     */
    public static function allNotOneOf(mixed $value, mixed $values, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::notOneOf($entry, $values, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrNotOneOf(mixed $value, mixed $values, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::notOneOf($entry, $values, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return mixed
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrNotInArray(mixed $value, mixed $values, callable|string $message = ''): mixed
    {
        null === $value || static::notInArray($value, $values, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable
     *
     * @throws InvalidArgumentException
     */
    public static function allNotInArray(mixed $value, mixed $values, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::notInArray($entry, $values, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrNotInArray(mixed $value, mixed $values, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::notInArray($entry, $values, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return string|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrContains(mixed $value, mixed $subString, callable|string $message = ''): ?string
    {
        null === $value || static::contains($value, $subString, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable<string>
     *
     * @throws InvalidArgumentException
     */
    public static function allContains(mixed $value, mixed $subString, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::contains($entry, $subString, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable<string|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrContains(mixed $value, mixed $subString, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::contains($entry, $subString, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return string|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrNotContains(mixed $value, mixed $subString, callable|string $message = ''): ?string
    {
        null === $value || static::notContains($value, $subString, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable<string>
     *
     * @throws InvalidArgumentException
     */
    public static function allNotContains(mixed $value, mixed $subString, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::notContains($entry, $subString, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable<string|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrNotContains(mixed $value, mixed $subString, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::notContains($entry, $subString, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return string|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrNotWhitespaceOnly(mixed $value, callable|string $message = ''): ?string
    {
        null === $value || static::notWhitespaceOnly($value, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable<string>
     *
     * @throws InvalidArgumentException
     */
    public static function allNotWhitespaceOnly(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::notWhitespaceOnly($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable<string|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrNotWhitespaceOnly(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::notWhitespaceOnly($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return string|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrStartsWith(mixed $value, mixed $prefix, callable|string $message = ''): ?string
    {
        null === $value || static::startsWith($value, $prefix, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable<string>
     *
     * @throws InvalidArgumentException
     */
    public static function allStartsWith(mixed $value, mixed $prefix, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::startsWith($entry, $prefix, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable<string|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrStartsWith(mixed $value, mixed $prefix, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::startsWith($entry, $prefix, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return string|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrNotStartsWith(mixed $value, mixed $prefix, callable|string $message = ''): ?string
    {
        null === $value || static::notStartsWith($value, $prefix, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable<string>
     *
     * @throws InvalidArgumentException
     */
    public static function allNotStartsWith(mixed $value, mixed $prefix, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::notStartsWith($entry, $prefix, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable<string|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrNotStartsWith(mixed $value, mixed $prefix, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::notStartsWith($entry, $prefix, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return string|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrStartsWithLetter(mixed $value, callable|string $message = ''): ?string
    {
        null === $value || static::startsWithLetter($value, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable<string>
     *
     * @throws InvalidArgumentException
     */
    public static function allStartsWithLetter(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::startsWithLetter($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable<string|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrStartsWithLetter(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::startsWithLetter($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return string|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrEndsWith(mixed $value, mixed $suffix, callable|string $message = ''): ?string
    {
        null === $value || static::endsWith($value, $suffix, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable<string>
     *
     * @throws InvalidArgumentException
     */
    public static function allEndsWith(mixed $value, mixed $suffix, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::endsWith($entry, $suffix, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable<string|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrEndsWith(mixed $value, mixed $suffix, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::endsWith($entry, $suffix, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return string|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrNotEndsWith(mixed $value, mixed $suffix, callable|string $message = ''): ?string
    {
        null === $value || static::notEndsWith($value, $suffix, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable<string>
     *
     * @throws InvalidArgumentException
     */
    public static function allNotEndsWith(mixed $value, mixed $suffix, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::notEndsWith($entry, $suffix, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable<string|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrNotEndsWith(mixed $value, mixed $suffix, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::notEndsWith($entry, $suffix, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return string|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrRegex(mixed $value, mixed $pattern, callable|string $message = ''): ?string
    {
        null === $value || static::regex($value, $pattern, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable<string>
     *
     * @throws InvalidArgumentException
     */
    public static function allRegex(mixed $value, mixed $pattern, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::regex($entry, $pattern, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable<string|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrRegex(mixed $value, mixed $pattern, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::regex($entry, $pattern, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return string|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrNotRegex(mixed $value, mixed $pattern, callable|string $message = ''): ?string
    {
        null === $value || static::notRegex($value, $pattern, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable<string>
     *
     * @throws InvalidArgumentException
     */
    public static function allNotRegex(mixed $value, mixed $pattern, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::notRegex($entry, $pattern, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable<string|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrNotRegex(mixed $value, mixed $pattern, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::notRegex($entry, $pattern, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return string|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrUnicodeLetters(mixed $value, callable|string $message = ''): ?string
    {
        null === $value || static::unicodeLetters($value, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable<string>
     *
     * @throws InvalidArgumentException
     */
    public static function allUnicodeLetters(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::unicodeLetters($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable<string|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrUnicodeLetters(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::unicodeLetters($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return string|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrAlpha(mixed $value, callable|string $message = ''): ?string
    {
        null === $value || static::alpha($value, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable<string>
     *
     * @throws InvalidArgumentException
     */
    public static function allAlpha(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::alpha($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable<string|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrAlpha(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::alpha($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return string|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrDigits(mixed $value, callable|string $message = ''): ?string
    {
        null === $value || static::digits($value, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable<string>
     *
     * @throws InvalidArgumentException
     */
    public static function allDigits(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::digits($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable<string|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrDigits(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::digits($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return string|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrAlnum(mixed $value, callable|string $message = ''): ?string
    {
        null === $value || static::alnum($value, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable<string>
     *
     * @throws InvalidArgumentException
     */
    public static function allAlnum(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::alnum($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable<string|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrAlnum(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::alnum($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert lowercase-string|null $value
     *
     * @param string|callable():string $message
     *
     * @return lowercase-string|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrLower(mixed $value, callable|string $message = ''): ?string
    {
        null === $value || static::lower($value, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<lowercase-string> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<lowercase-string>
     *
     * @throws InvalidArgumentException
     */
    public static function allLower(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::lower($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<lowercase-string|null> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<lowercase-string|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrLower(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::lower($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return string|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrUpper(mixed $value, callable|string $message = ''): ?string
    {
        null === $value || static::upper($value, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable<string>
     *
     * @throws InvalidArgumentException
     */
    public static function allUpper(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::upper($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<string|null> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<string|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrUpper(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::upper($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return string|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrLength(mixed $value, mixed $length, callable|string $message = ''): ?string
    {
        null === $value || static::length($value, $length, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable<string>
     *
     * @throws InvalidArgumentException
     */
    public static function allLength(mixed $value, mixed $length, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::length($entry, $length, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable<string|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrLength(mixed $value, mixed $length, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::length($entry, $length, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return string|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrMinLength(mixed $value, mixed $min, callable|string $message = ''): ?string
    {
        null === $value || static::minLength($value, $min, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable<string>
     *
     * @throws InvalidArgumentException
     */
    public static function allMinLength(mixed $value, mixed $min, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::minLength($entry, $min, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable<string|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrMinLength(mixed $value, mixed $min, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::minLength($entry, $min, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return string|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrMaxLength(mixed $value, mixed $max, callable|string $message = ''): ?string
    {
        null === $value || static::maxLength($value, $max, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable<string>
     *
     * @throws InvalidArgumentException
     */
    public static function allMaxLength(mixed $value, mixed $max, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::maxLength($entry, $max, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable<string|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrMaxLength(mixed $value, mixed $max, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::maxLength($entry, $max, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return string|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrLengthBetween(mixed $value, mixed $min, mixed $max, callable|string $message = ''): ?string
    {
        null === $value || static::lengthBetween($value, $min, $max, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable<string>
     *
     * @throws InvalidArgumentException
     */
    public static function allLengthBetween(mixed $value, mixed $min, mixed $max, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::lengthBetween($entry, $min, $max, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable<string|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrLengthBetween(mixed $value, mixed $min, mixed $max, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::lengthBetween($entry, $min, $max, $message);
        }

        return $value;
    }

    /**
     * @param string|callable():string $message
     *
     * @return string|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrFileExists(mixed $value, callable|string $message = ''): ?string
    {
        null === $value || static::fileExists($value, $message);

        return $value;
    }

    /**
     * @param string|callable():string $message
     *
     * @return iterable<string>
     *
     * @throws InvalidArgumentException
     */
    public static function allFileExists(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::fileExists($entry, $message);
        }

        return $value;
    }

    /**
     * @param string|callable():string $message
     *
     * @return iterable<string|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrFileExists(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::fileExists($entry, $message);
        }

        return $value;
    }

    /**
     * @param string|callable():string $message
     *
     * @return string|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrFile(mixed $value, callable|string $message = ''): ?string
    {
        null === $value || static::file($value, $message);

        return $value;
    }

    /**
     * @param string|callable():string $message
     *
     * @return iterable<string>
     *
     * @throws InvalidArgumentException
     */
    public static function allFile(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::file($entry, $message);
        }

        return $value;
    }

    /**
     * @param string|callable():string $message
     *
     * @return iterable<string|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrFile(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::file($entry, $message);
        }

        return $value;
    }

    /**
     * @param string|callable():string $message
     *
     * @return string|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrDirectory(mixed $value, callable|string $message = ''): ?string
    {
        null === $value || static::directory($value, $message);

        return $value;
    }

    /**
     * @param string|callable():string $message
     *
     * @return iterable<string>
     *
     * @throws InvalidArgumentException
     */
    public static function allDirectory(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::directory($entry, $message);
        }

        return $value;
    }

    /**
     * @param string|callable():string $message
     *
     * @return iterable<string|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrDirectory(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::directory($entry, $message);
        }

        return $value;
    }

    /**
     * @param string|callable():string $message
     *
     * @return string|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrReadable(mixed $value, callable|string $message = ''): ?string
    {
        null === $value || static::readable($value, $message);

        return $value;
    }

    /**
     * @param string|callable():string $message
     *
     * @return iterable<string>
     *
     * @throws InvalidArgumentException
     */
    public static function allReadable(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::readable($entry, $message);
        }

        return $value;
    }

    /**
     * @param string|callable():string $message
     *
     * @return iterable<string|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrReadable(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::readable($entry, $message);
        }

        return $value;
    }

    /**
     * @param string|callable():string $message
     *
     * @return string|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrWritable(mixed $value, callable|string $message = ''): ?string
    {
        null === $value || static::writable($value, $message);

        return $value;
    }

    /**
     * @param string|callable():string $message
     *
     * @return iterable<string>
     *
     * @throws InvalidArgumentException
     */
    public static function allWritable(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::writable($entry, $message);
        }

        return $value;
    }

    /**
     * @param string|callable():string $message
     *
     * @return iterable<string|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrWritable(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::writable($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-assert class-string|null $value
     *
     * @param string|callable():string $message
     *
     * @return class-string|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrClassExists(mixed $value, callable|string $message = ''): ?string
    {
        null === $value || static::classExists($value, $message);

        return $value;
    }

    /**
     * @psalm-assert iterable<class-string> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<class-string>
     *
     * @throws InvalidArgumentException
     */
    public static function allClassExists(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::classExists($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-assert iterable<class-string|null> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<class-string|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrClassExists(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::classExists($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @template ExpectedType of object
     * @psalm-assert class-string<ExpectedType>|null $value
     *
     * @param class-string<ExpectedType> $class
     * @param string|callable():string   $message
     *
     * @return class-string<ExpectedType>|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrSubclassOf(mixed $value, mixed $class, callable|string $message = ''): ?string
    {
        null === $value || static::subclassOf($value, $class, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @template ExpectedType of object
     * @psalm-assert iterable<class-string<ExpectedType>> $value
     *
     * @param class-string<ExpectedType> $class
     * @param string|callable():string   $message
     *
     * @return iterable<class-string<ExpectedType>>
     *
     * @throws InvalidArgumentException
     */
    public static function allSubclassOf(mixed $value, mixed $class, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::subclassOf($entry, $class, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @template ExpectedType of object|null
     * @psalm-assert iterable<class-string<ExpectedType>|null> $value
     *
     * @param class-string<ExpectedType> $class
     * @param string|callable():string   $message
     *
     * @return iterable<class-string<ExpectedType>|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrSubclassOf(mixed $value, mixed $class, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::subclassOf($entry, $class, $message);
        }

        return $value;
    }

    /**
     * @psalm-assert class-string|null $value
     *
     * @param string|callable():string $message
     *
     * @return class-string|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrInterfaceExists(mixed $value, callable|string $message = ''): ?string
    {
        null === $value || static::interfaceExists($value, $message);

        return $value;
    }

    /**
     * @psalm-assert iterable<class-string> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<class-string>
     *
     * @throws InvalidArgumentException
     */
    public static function allInterfaceExists(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::interfaceExists($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-assert iterable<class-string|null> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<class-string|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrInterfaceExists(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::interfaceExists($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @template ExpectedType of object
     * @psalm-assert class-string<ExpectedType>|ExpectedType|null $value
     *
     * @param class-string<ExpectedType> $interface
     * @param string|callable():string   $message
     *
     * @return class-string<ExpectedType>|ExpectedType|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrImplementsInterface(mixed $value, mixed $interface, callable|string $message = ''): object|string|null
    {
        null === $value || static::implementsInterface($value, $interface, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @template ExpectedType of object
     * @psalm-assert iterable<class-string<ExpectedType>|ExpectedType> $value
     *
     * @param class-string<ExpectedType> $interface
     * @param string|callable():string   $message
     *
     * @return iterable<class-string<ExpectedType>|ExpectedType>
     *
     * @throws InvalidArgumentException
     */
    public static function allImplementsInterface(mixed $value, mixed $interface, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::implementsInterface($entry, $interface, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @template ExpectedType of object|null
     * @psalm-assert iterable<class-string<ExpectedType>|ExpectedType|null> $value
     *
     * @param class-string<ExpectedType> $interface
     * @param string|callable():string   $message
     *
     * @return iterable<class-string<ExpectedType>|ExpectedType|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrImplementsInterface(mixed $value, mixed $interface, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::implementsInterface($entry, $interface, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @template T as class-string|object
     *
     * @param T|null                   $classOrObject
     * @param string|callable():string $message
     *
     * @return T|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrPropertyExists(mixed $classOrObject, mixed $property, callable|string $message = ''): object|string|null
    {
        null === $classOrObject || static::propertyExists($classOrObject, $property, $message);

        return $classOrObject;
    }

    /**
     * @psalm-pure
     *
     * @template T as class-string|object
     *
     * @param iterable<T>              $classOrObject
     * @param string|callable():string $message
     *
     * @return iterable<T>
     *
     * @throws InvalidArgumentException
     */
    public static function allPropertyExists(mixed $classOrObject, mixed $property, callable|string $message = ''): iterable
    {
        static::isIterable($classOrObject);

        foreach ($classOrObject as $entry) {
            static::propertyExists($entry, $property, $message);
        }

        return $classOrObject;
    }

    /**
     * @psalm-pure
     *
     * @template T as class-string|object|null
     *
     * @param iterable<T>              $classOrObject
     * @param string|callable():string $message
     *
     * @return iterable<T>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrPropertyExists(mixed $classOrObject, mixed $property, callable|string $message = ''): iterable
    {
        static::isIterable($classOrObject);

        foreach ($classOrObject as $entry) {
            null === $entry || static::propertyExists($entry, $property, $message);
        }

        return $classOrObject;
    }

    /**
     * @psalm-pure
     *
     * @template T as class-string|object
     *
     * @param T|null                   $classOrObject
     * @param string|callable():string $message
     *
     * @return T|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrPropertyNotExists(mixed $classOrObject, mixed $property, callable|string $message = ''): mixed
    {
        null === $classOrObject || static::propertyNotExists($classOrObject, $property, $message);

        return $classOrObject;
    }

    /**
     * @psalm-pure
     *
     * @template T as class-string|object
     *
     * @param iterable<T>              $classOrObject
     * @param string|callable():string $message
     *
     * @return iterable<T>
     *
     * @throws InvalidArgumentException
     */
    public static function allPropertyNotExists(mixed $classOrObject, mixed $property, callable|string $message = ''): iterable
    {
        static::isIterable($classOrObject);

        foreach ($classOrObject as $entry) {
            static::propertyNotExists($entry, $property, $message);
        }

        return $classOrObject;
    }

    /**
     * @psalm-pure
     *
     * @template T as class-string|object|null
     *
     * @param iterable<T>              $classOrObject
     * @param string|callable():string $message
     *
     * @return iterable<T>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrPropertyNotExists(mixed $classOrObject, mixed $property, callable|string $message = ''): iterable
    {
        static::isIterable($classOrObject);

        foreach ($classOrObject as $entry) {
            null === $entry || static::propertyNotExists($entry, $property, $message);
        }

        return $classOrObject;
    }

    /**
     * @psalm-pure
     *
     * @template T as class-string|object
     *
     * @param T|null                   $classOrObject
     * @param string|callable():string $message
     *
     * @return T|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrMethodExists(mixed $classOrObject, mixed $method, callable|string $message = ''): object|string|null
    {
        null === $classOrObject || static::methodExists($classOrObject, $method, $message);

        return $classOrObject;
    }

    /**
     * @psalm-pure
     *
     * @template T as class-string|object
     *
     * @param iterable<T>              $classOrObject
     * @param string|callable():string $message
     *
     * @return iterable<T>
     *
     * @throws InvalidArgumentException
     */
    public static function allMethodExists(mixed $classOrObject, mixed $method, callable|string $message = ''): iterable
    {
        static::isIterable($classOrObject);

        foreach ($classOrObject as $entry) {
            static::methodExists($entry, $method, $message);
        }

        return $classOrObject;
    }

    /**
     * @psalm-pure
     *
     * @template T as class-string|object|null
     *
     * @param iterable<T>              $classOrObject
     * @param string|callable():string $message
     *
     * @return iterable<T>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrMethodExists(mixed $classOrObject, mixed $method, callable|string $message = ''): iterable
    {
        static::isIterable($classOrObject);

        foreach ($classOrObject as $entry) {
            null === $entry || static::methodExists($entry, $method, $message);
        }

        return $classOrObject;
    }

    /**
     * @psalm-pure
     *
     * @template T as class-string|object
     *
     * @param T|null                   $classOrObject
     * @param string|callable():string $message
     *
     * @return T|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrMethodNotExists(mixed $classOrObject, mixed $method, callable|string $message = ''): mixed
    {
        null === $classOrObject || static::methodNotExists($classOrObject, $method, $message);

        return $classOrObject;
    }

    /**
     * @psalm-pure
     *
     * @template T as class-string|object
     *
     * @param iterable<T>              $classOrObject
     * @param string|callable():string $message
     *
     * @return iterable<T>
     *
     * @throws InvalidArgumentException
     */
    public static function allMethodNotExists(mixed $classOrObject, mixed $method, callable|string $message = ''): iterable
    {
        static::isIterable($classOrObject);

        foreach ($classOrObject as $entry) {
            static::methodNotExists($entry, $method, $message);
        }

        return $classOrObject;
    }

    /**
     * @psalm-pure
     *
     * @template T as class-string|object|null
     *
     * @param iterable<T>              $classOrObject
     * @param string|callable():string $message
     *
     * @return iterable<T>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrMethodNotExists(mixed $classOrObject, mixed $method, callable|string $message = ''): iterable
    {
        static::isIterable($classOrObject);

        foreach ($classOrObject as $entry) {
            null === $entry || static::methodNotExists($entry, $method, $message);
        }

        return $classOrObject;
    }

    /**
     * @psalm-pure
     *
     * @param string|int               $key
     * @param string|callable():string $message
     *
     * @return array|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrKeyExists(mixed $array, string|int $key, callable|string $message = ''): ?array
    {
        null === $array || static::keyExists($array, $key, $message);

        return $array;
    }

    /**
     * @psalm-pure
     *
     * @param string|int               $key
     * @param string|callable():string $message
     *
     * @return iterable<array>
     *
     * @throws InvalidArgumentException
     */
    public static function allKeyExists(mixed $array, string|int $key, callable|string $message = ''): iterable
    {
        static::isIterable($array);

        foreach ($array as $entry) {
            static::keyExists($entry, $key, $message);
        }

        return $array;
    }

    /**
     * @psalm-pure
     *
     * @param string|int               $key
     * @param string|callable():string $message
     *
     * @return iterable<array|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrKeyExists(mixed $array, string|int $key, callable|string $message = ''): iterable
    {
        static::isIterable($array);

        foreach ($array as $entry) {
            null === $entry || static::keyExists($entry, $key, $message);
        }

        return $array;
    }

    /**
     * @psalm-pure
     *
     * @param iterable<string|int>     $keys
     * @param string|callable():string $message
     *
     * @return array|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrKeysExist(mixed $array, mixed $keys, callable|string $message = ''): ?array
    {
        null === $array || static::keysExist($array, $keys, $message);

        return $array;
    }

    /**
     * @psalm-pure
     *
     * @param iterable<string|int>     $keys
     * @param string|callable():string $message
     *
     * @return iterable<array>
     *
     * @throws InvalidArgumentException
     */
    public static function allKeysExist(mixed $array, mixed $keys, callable|string $message = ''): iterable
    {
        static::isIterable($array);

        foreach ($array as $entry) {
            static::keysExist($entry, $keys, $message);
        }

        return $array;
    }

    /**
     * @psalm-pure
     *
     * @param iterable<string|int>     $keys
     * @param string|callable():string $message
     *
     * @return iterable<array|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrKeysExist(mixed $array, mixed $keys, callable|string $message = ''): iterable
    {
        static::isIterable($array);

        foreach ($array as $entry) {
            null === $entry || static::keysExist($entry, $keys, $message);
        }

        return $array;
    }

    /**
     * @psalm-pure
     *
     * @param string|int               $key
     * @param string|callable():string $message
     *
     * @return array|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrKeyNotExists(mixed $array, string|int $key, callable|string $message = ''): ?array
    {
        null === $array || static::keyNotExists($array, $key, $message);

        return $array;
    }

    /**
     * @psalm-pure
     *
     * @param string|int               $key
     * @param string|callable():string $message
     *
     * @return iterable<array>
     *
     * @throws InvalidArgumentException
     */
    public static function allKeyNotExists(mixed $array, string|int $key, callable|string $message = ''): iterable
    {
        static::isIterable($array);

        foreach ($array as $entry) {
            static::keyNotExists($entry, $key, $message);
        }

        return $array;
    }

    /**
     * @psalm-pure
     *
     * @param string|int               $key
     * @param string|callable():string $message
     *
     * @return iterable<array|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrKeyNotExists(mixed $array, string|int $key, callable|string $message = ''): iterable
    {
        static::isIterable($array);

        foreach ($array as $entry) {
            null === $entry || static::keyNotExists($entry, $key, $message);
        }

        return $array;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert array-key|null $value
     *
     * @param string|callable():string $message
     *
     * @return array-key|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrValidArrayKey(mixed $value, callable|string $message = ''): string|int|null
    {
        null === $value || static::validArrayKey($value, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<array-key> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<array-key>
     *
     * @throws InvalidArgumentException
     */
    public static function allValidArrayKey(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::validArrayKey($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<array-key|null> $value
     *
     * @param string|callable():string $message
     *
     * @return iterable<array-key|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrValidArrayKey(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::validArrayKey($entry, $message);
        }

        return $value;
    }

    /**
     * @param string|callable():string $message
     *
     * @return Countable|array|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrCount(mixed $array, mixed $number, callable|string $message = ''): Countable|array|null
    {
        null === $array || static::count($array, $number, $message);

        return $array;
    }

    /**
     * @param string|callable():string $message
     *
     * @return iterable<Countable|array>
     *
     * @throws InvalidArgumentException
     */
    public static function allCount(mixed $array, mixed $number, callable|string $message = ''): iterable
    {
        static::isIterable($array);

        foreach ($array as $entry) {
            static::count($entry, $number, $message);
        }

        return $array;
    }

    /**
     * @param string|callable():string $message
     *
     * @return iterable<Countable|array|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrCount(mixed $array, mixed $number, callable|string $message = ''): iterable
    {
        static::isIterable($array);

        foreach ($array as $entry) {
            null === $entry || static::count($entry, $number, $message);
        }

        return $array;
    }

    /**
     * @param string|callable():string $message
     *
     * @return Countable|array|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrMinCount(mixed $array, mixed $min, callable|string $message = ''): Countable|array|null
    {
        null === $array || static::minCount($array, $min, $message);

        return $array;
    }

    /**
     * @param string|callable():string $message
     *
     * @return iterable<Countable|array>
     *
     * @throws InvalidArgumentException
     */
    public static function allMinCount(mixed $array, mixed $min, callable|string $message = ''): iterable
    {
        static::isIterable($array);

        foreach ($array as $entry) {
            static::minCount($entry, $min, $message);
        }

        return $array;
    }

    /**
     * @param string|callable():string $message
     *
     * @return iterable<Countable|array|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrMinCount(mixed $array, mixed $min, callable|string $message = ''): iterable
    {
        static::isIterable($array);

        foreach ($array as $entry) {
            null === $entry || static::minCount($entry, $min, $message);
        }

        return $array;
    }

    /**
     * @param string|callable():string $message
     *
     * @return Countable|array|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrMaxCount(mixed $array, mixed $max, callable|string $message = ''): Countable|array|null
    {
        null === $array || static::maxCount($array, $max, $message);

        return $array;
    }

    /**
     * @param string|callable():string $message
     *
     * @return iterable<Countable|array>
     *
     * @throws InvalidArgumentException
     */
    public static function allMaxCount(mixed $array, mixed $max, callable|string $message = ''): iterable
    {
        static::isIterable($array);

        foreach ($array as $entry) {
            static::maxCount($entry, $max, $message);
        }

        return $array;
    }

    /**
     * @param string|callable():string $message
     *
     * @return iterable<Countable|array|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrMaxCount(mixed $array, mixed $max, callable|string $message = ''): iterable
    {
        static::isIterable($array);

        foreach ($array as $entry) {
            null === $entry || static::maxCount($entry, $max, $message);
        }

        return $array;
    }

    /**
     * @param string|callable():string $message
     *
     * @return Countable|array|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrCountBetween(mixed $array, mixed $min, mixed $max, callable|string $message = ''): Countable|array|null
    {
        null === $array || static::countBetween($array, $min, $max, $message);

        return $array;
    }

    /**
     * @param string|callable():string $message
     *
     * @return iterable<Countable|array>
     *
     * @throws InvalidArgumentException
     */
    public static function allCountBetween(mixed $array, mixed $min, mixed $max, callable|string $message = ''): iterable
    {
        static::isIterable($array);

        foreach ($array as $entry) {
            static::countBetween($entry, $min, $max, $message);
        }

        return $array;
    }

    /**
     * @param string|callable():string $message
     *
     * @return iterable<Countable|array|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrCountBetween(mixed $array, mixed $min, mixed $max, callable|string $message = ''): iterable
    {
        static::isIterable($array);

        foreach ($array as $entry) {
            null === $entry || static::countBetween($entry, $min, $max, $message);
        }

        return $array;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert list<mixed>|null $array
     *
     * @param string|callable():string $message
     *
     * @return list<mixed>|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrIsList(mixed $array, callable|string $message = ''): ?array
    {
        null === $array || static::isList($array, $message);

        return $array;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<list<mixed>> $array
     *
     * @param string|callable():string $message
     *
     * @return iterable<list<mixed>>
     *
     * @throws InvalidArgumentException
     */
    public static function allIsList(mixed $array, callable|string $message = ''): iterable
    {
        static::isIterable($array);

        foreach ($array as $entry) {
            static::isList($entry, $message);
        }

        return $array;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<list<mixed>|null> $array
     *
     * @param string|callable():string $message
     *
     * @return iterable<list<mixed>|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrIsList(mixed $array, callable|string $message = ''): iterable
    {
        static::isIterable($array);

        foreach ($array as $entry) {
            null === $entry || static::isList($entry, $message);
        }

        return $array;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert non-empty-list<mixed>|null $array
     *
     * @param string|callable():string $message
     *
     * @return non-empty-list<mixed>|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrIsNonEmptyList(mixed $array, callable|string $message = ''): ?array
    {
        null === $array || static::isNonEmptyList($array, $message);

        return $array;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<non-empty-list<mixed>> $array
     *
     * @param string|callable():string $message
     *
     * @return iterable<non-empty-list<mixed>>
     *
     * @throws InvalidArgumentException
     */
    public static function allIsNonEmptyList(mixed $array, callable|string $message = ''): iterable
    {
        static::isIterable($array);

        foreach ($array as $entry) {
            static::isNonEmptyList($entry, $message);
        }

        return $array;
    }

    /**
     * @psalm-pure
     *
     * @psalm-assert iterable<non-empty-list<mixed>|null> $array
     *
     * @param string|callable():string $message
     *
     * @return iterable<non-empty-list<mixed>|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrIsNonEmptyList(mixed $array, callable|string $message = ''): iterable
    {
        static::isIterable($array);

        foreach ($array as $entry) {
            null === $entry || static::isNonEmptyList($entry, $message);
        }

        return $array;
    }

    /**
     * @psalm-pure
     *
     * @template T
     * @psalm-assert array<string, T>|null $array
     *
     * @param mixed|array<array-key, T>|null $array
     * @param string|callable():string       $message
     *
     * @return array<string, T>|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrIsMap(mixed $array, callable|string $message = ''): ?array
    {
        null === $array || static::isMap($array, $message);

        return $array;
    }

    /**
     * @psalm-pure
     *
     * @template T
     * @psalm-assert iterable<array<string, T>> $array
     *
     * @param iterable<mixed|array<array-key, T>> $array
     * @param string|callable():string            $message
     *
     * @return iterable<array<string, T>>
     *
     * @throws InvalidArgumentException
     */
    public static function allIsMap(mixed $array, callable|string $message = ''): iterable
    {
        static::isIterable($array);

        foreach ($array as $entry) {
            static::isMap($entry, $message);
        }

        return $array;
    }

    /**
     * @psalm-pure
     *
     * @template T
     * @psalm-assert iterable<array<string, T>|null> $array
     *
     * @param iterable<mixed|array<array-key, T>|null> $array
     * @param string|callable():string                 $message
     *
     * @return iterable<array<string, T>|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrIsMap(mixed $array, callable|string $message = ''): iterable
    {
        static::isIterable($array);

        foreach ($array as $entry) {
            null === $entry || static::isMap($entry, $message);
        }

        return $array;
    }

    /**
     * @param callable|null            $callable
     * @param string|callable():string $message
     *
     * @return Closure|callable-string|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrIsStatic(mixed $callable, callable|string $message = ''): Closure|string|null
    {
        null === $callable || static::isStatic($callable, $message);

        return $callable;
    }

    /**
     * @param iterable<callable>       $callable
     * @param string|callable():string $message
     *
     * @return iterable<Closure|callable-string>
     *
     * @throws InvalidArgumentException
     */
    public static function allIsStatic(mixed $callable, callable|string $message = ''): iterable
    {
        static::isIterable($callable);

        foreach ($callable as $entry) {
            static::isStatic($entry, $message);
        }

        return $callable;
    }

    /**
     * @param iterable<callable|null>  $callable
     * @param string|callable():string $message
     *
     * @return iterable<Closure|callable-string|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrIsStatic(mixed $callable, callable|string $message = ''): iterable
    {
        static::isIterable($callable);

        foreach ($callable as $entry) {
            null === $entry || static::isStatic($entry, $message);
        }

        return $callable;
    }

    /**
     * @param callable|null            $callable
     * @param string|callable():string $message
     *
     * @return Closure|callable-string|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrNotStatic(mixed $callable, callable|string $message = ''): Closure|string|null
    {
        null === $callable || static::notStatic($callable, $message);

        return $callable;
    }

    /**
     * @param iterable<callable>       $callable
     * @param string|callable():string $message
     *
     * @return iterable<Closure|callable-string>
     *
     * @throws InvalidArgumentException
     */
    public static function allNotStatic(mixed $callable, callable|string $message = ''): iterable
    {
        static::isIterable($callable);

        foreach ($callable as $entry) {
            static::notStatic($entry, $message);
        }

        return $callable;
    }

    /**
     * @param iterable<callable|null>  $callable
     * @param string|callable():string $message
     *
     * @return iterable<Closure|callable-string|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrNotStatic(mixed $callable, callable|string $message = ''): iterable
    {
        static::isIterable($callable);

        foreach ($callable as $entry) {
            null === $entry || static::notStatic($entry, $message);
        }

        return $callable;
    }

    /**
     * @psalm-pure
     *
     * @template T
     *
     * @param array<string, T>|null    $array
     * @param string|callable():string $message
     *
     * @return non-empty-array<string, T>|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrIsNonEmptyMap(mixed $array, callable|string $message = ''): ?array
    {
        null === $array || static::isNonEmptyMap($array, $message);

        return $array;
    }

    /**
     * @psalm-pure
     *
     * @template T
     *
     * @param iterable<array<string, T>> $array
     * @param string|callable():string   $message
     *
     * @return iterable<non-empty-array<string, T>>
     *
     * @throws InvalidArgumentException
     */
    public static function allIsNonEmptyMap(mixed $array, callable|string $message = ''): iterable
    {
        static::isIterable($array);

        foreach ($array as $entry) {
            static::isNonEmptyMap($entry, $message);
        }

        return $array;
    }

    /**
     * @psalm-pure
     *
     * @template T
     * @psalm-assert iterable<array<string, T>|null> $array
     * @psalm-assert iterable<!empty|null> $array
     *
     * @param iterable<array<string, T>|null> $array
     * @param string|callable():string        $message
     *
     * @return iterable<non-empty-array<string, T>|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrIsNonEmptyMap(mixed $array, callable|string $message = ''): iterable
    {
        static::isIterable($array);

        foreach ($array as $entry) {
            null === $entry || static::isNonEmptyMap($entry, $message);
        }

        return $array;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return string|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrUuid(mixed $value, callable|string $message = ''): ?string
    {
        null === $value || static::uuid($value, $message);

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable<string>
     *
     * @throws InvalidArgumentException
     */
    public static function allUuid(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            static::uuid($entry, $message);
        }

        return $value;
    }

    /**
     * @psalm-pure
     *
     * @param string|callable():string $message
     *
     * @return iterable<string|null>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrUuid(mixed $value, callable|string $message = ''): iterable
    {
        static::isIterable($value);

        foreach ($value as $entry) {
            null === $entry || static::uuid($entry, $message);
        }

        return $value;
    }

    /**
     * @template T as callable
     *
     * @param T|null                   $expression
     * @param class-string<Throwable>  $class
     * @param string|callable():string $message
     *
     * @return T|null
     *
     * @throws InvalidArgumentException
     */
    public static function nullOrThrows(mixed $expression, string $class = 'Throwable', callable|string $message = ''): ?callable
    {
        null === $expression || static::throws($expression, $class, $message);

        return $expression;
    }

    /**
     * @template T as callable
     *
     * @param iterable<T>              $expression
     * @param class-string<Throwable>  $class
     * @param string|callable():string $message
     *
     * @return iterable<T>
     *
     * @throws InvalidArgumentException
     */
    public static function allThrows(mixed $expression, string $class = 'Throwable', callable|string $message = ''): iterable
    {
        static::isIterable($expression);

        foreach ($expression as $entry) {
            static::throws($entry, $class, $message);
        }

        return $expression;
    }

    /**
     * @template T as callable|null
     *
     * @param iterable<T>              $expression
     * @param class-string<Throwable>  $class
     * @param string|callable():string $message
     *
     * @return iterable<T>
     *
     * @throws InvalidArgumentException
     */
    public static function allNullOrThrows(mixed $expression, string $class = 'Throwable', callable|string $message = ''): iterable
    {
        static::isIterable($expression);

        foreach ($expression as $entry) {
            null === $entry || static::throws($entry, $class, $message);
        }

        return $expression;
    }
}
{
    "name": "webmozart/assert",
    "description": "Assertions to validate method input/output with nice error messages.",
    "license": "MIT",
    "keywords": [
        "assert",
        "check",
        "validate"
    ],
    "authors": [
        {
            "name": "Bernhard Schussek",
            "email": "bschussek@gmail.com"
        },
        {
            "name": "Woody Gilk",
            "email": "woody.gilk@gmail.com"
        }
    ],
    "require": {
        "php": "^8.2",
        "ext-ctype": "*",
        "ext-date": "*",
        "ext-filter": "*"
    },
    "suggest": {
        "ext-intl": "",
        "ext-simplexml": "",
        "ext-spl": ""
    },
    "autoload": {
        "psr-4": {
            "Webmozart\\Assert\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Webmozart\\Assert\\Bin\\": "bin/src",
            "Webmozart\\Assert\\Tests\\": "tests/"
        }
    },
    "extra": {
        "psalm": {
            "pluginClass": "Webmozart\\Assert\\PsalmPlugin"
        },
        "branch-alias": {
            "dev-feature/2-0": "2.0-dev",
            "dev-master": "2.0-dev"
        }
    },
    "scripts": {
        "install-tools": [
            "composer --working-dir=tools/php-cs-fixer install",
            "composer --working-dir=tools/phpunit install",
            "composer --working-dir=tools/psalm install",
            "composer --working-dir=tools/roave-bc-check install"
        ],
        "update-tools": [
            "composer --working-dir=tools/php-cs-fixer update",
            "composer --working-dir=tools/phpunit update",
            "composer --working-dir=tools/psalm update",
            "composer --working-dir=tools/roave-bc-check update"
        ],
        "generate-mixin": "php bin/generate.php",
        "bc-check": "./tools/roave-bc-check/vendor/bin/roave-backward-compatibility-check",
        "cs-check" : "./tools/php-cs-fixer/vendor/bin/php-cs-fixer check",
        "cs-fix": "./tools/php-cs-fixer/vendor/bin/php-cs-fixer fix",
        "static-analysis": "./tools/psalm/vendor/bin/psalm --threads=4 --root=$(pwd)",
        "test": "./tools/phpunit/vendor/bin/phpunit"
    }
}
<?php
declare(strict_types = 1);

namespace DASPRiD\Enum;

use DASPRiD\Enum\Exception\CloneNotSupportedException;
use DASPRiD\Enum\Exception\SerializeNotSupportedException;
use DASPRiD\Enum\Exception\UnserializeNotSupportedException;

final class NullValue
{
    /**
     * @var self
     */
    private static $instance;

    private function __construct()
    {
    }

    public static function instance() : self
    {
        return self::$instance ?: self::$instance = new self();
    }

    /**
     * Forbid cloning enums.
     *
     * @throws CloneNotSupportedException
     */
    final public function __clone()
    {
        throw new CloneNotSupportedException();
    }

    /**
     * Forbid serializing enums.
     *
     * @throws SerializeNotSupportedException
     */
    final public function __sleep() : array
    {
        throw new SerializeNotSupportedException();
    }

    /**
     * Forbid serializing enums.
     *
     * @throws SerializeNotSupportedException
     */
    final public function __serialize() : array
    {
        throw new SerializeNotSupportedException();
    }

    /**
     * Forbid unserializing enums.
     *
     * @throws UnserializeNotSupportedException
     */
    final public function __wakeup() : void
    {
        throw new UnserializeNotSupportedException();
    }

    /**
     * Forbid unserializing enums.
     *
     * @throws UnserializeNotSupportedException
     */
    final public function __unserialize($arg) : void
    {
        throw new UnserializeNotSupportedException();
    }
}
<?php
declare(strict_types = 1);

namespace DASPRiD\Enum\Exception;

use Throwable;

interface ExceptionInterface extends Throwable
{
}
<?php
declare(strict_types = 1);

namespace DASPRiD\Enum\Exception;

use Exception;

final class SerializeNotSupportedException extends Exception implements ExceptionInterface
{
}
<?php
declare(strict_types = 1);

namespace DASPRiD\Enum\Exception;

use Exception;

final class CloneNotSupportedException extends Exception implements ExceptionInterface
{
}
<?php
declare(strict_types = 1);

namespace DASPRiD\Enum\Exception;

use Exception;

final class UnserializeNotSupportedException extends Exception implements ExceptionInterface
{
}
<?php
declare(strict_types = 1);

namespace DASPRiD\Enum\Exception;

use Exception;

final class ExpectationException extends Exception implements ExceptionInterface
{
}
<?php
declare(strict_types = 1);

namespace DASPRiD\Enum\Exception;

use Exception;

final class MismatchException extends Exception implements ExceptionInterface
{
}
<?php
declare(strict_types = 1);

namespace DASPRiD\Enum\Exception;

use Exception;

final class IllegalArgumentException extends Exception implements ExceptionInterface
{
}
<?php
declare(strict_types = 1);

namespace DASPRiD\Enum;

use DASPRiD\Enum\Exception\CloneNotSupportedException;
use DASPRiD\Enum\Exception\IllegalArgumentException;
use DASPRiD\Enum\Exception\MismatchException;
use DASPRiD\Enum\Exception\SerializeNotSupportedException;
use DASPRiD\Enum\Exception\UnserializeNotSupportedException;
use ReflectionClass;

abstract class AbstractEnum
{
    /**
     * @var string
     */
    private $name;

    /**
     * @var int
     */
    private $ordinal;

    /**
     * @var array<string, array<string, static>>
     */
    private static $values = [];

    /**
     * @var array<string, bool>
     */
    private static $allValuesLoaded = [];

    /**
     * @var array<string, array>
     */
    private static $constants = [];

    /**
     * The constructor is private by default to avoid arbitrary enum creation.
     *
     * When creating your own constructor for a parameterized enum, make sure to declare it as protected, so that
     * the static methods are able to construct it. Avoid making it public, as that would allow creation of
     * non-singleton enum instances.
     */
    private function __construct()
    {
    }

    /**
     * Magic getter which forwards all calls to {@see self::valueOf()}.
     *
     * @return static
     */
    final public static function __callStatic(string $name, array $arguments) : self
    {
        return static::valueOf($name);
    }

    /**
     * Returns an enum with the specified name.
     *
     * The name must match exactly an identifier used to declare an enum in this type (extraneous whitespace characters
     * are not permitted).
     *
     * @return static
     * @throws IllegalArgumentException if the enum has no constant with the specified name
     */
    final public static function valueOf(string $name) : self
    {
        if (isset(self::$values[static::class][$name])) {
            return self::$values[static::class][$name];
        }

        $constants = self::constants();

        if (array_key_exists($name, $constants)) {
            return self::createValue($name, $constants[$name][0], $constants[$name][1]);
        }

        throw new IllegalArgumentException(sprintf('No enum constant %s::%s', static::class, $name));
    }

    /**
     * @return static
     */
    private static function createValue(string $name, int $ordinal, array $arguments) : self
    {
        $instance = new static(...$arguments);
        $instance->name = $name;
        $instance->ordinal = $ordinal;
        self::$values[static::class][$name] = $instance;
        return $instance;
    }

    /**
     * Obtains all possible types defined by this enum.
     *
     * @return static[]
     */
    final public static function values() : array
    {
        if (isset(self::$allValuesLoaded[static::class])) {
            return self::$values[static::class];
        }

        if (! isset(self::$values[static::class])) {
            self::$values[static::class] = [];
        }

        foreach (self::constants() as $name => $constant) {
            if (array_key_exists($name, self::$values[static::class])) {
                continue;
            }

            static::createValue($name, $constant[0], $constant[1]);
        }

        uasort(self::$values[static::class], function (self $a, self $b) {
            return $a->ordinal() <=> $b->ordinal();
        });

        self::$allValuesLoaded[static::class] = true;
        return self::$values[static::class];
    }

    private static function constants() : array
    {
        if (isset(self::$constants[static::class])) {
            return self::$constants[static::class];
        }

        self::$constants[static::class] = [];
        $reflectionClass = new ReflectionClass(static::class);
        $ordinal = -1;

        foreach ($reflectionClass->getReflectionConstants() as $reflectionConstant) {
            if (! $reflectionConstant->isProtected()) {
                continue;
            }

            $value = $reflectionConstant->getValue();

            self::$constants[static::class][$reflectionConstant->name] = [
                ++$ordinal,
                is_array($value) ? $value : []
            ];
        }

        return self::$constants[static::class];
    }

    /**
     * Returns the name of this enum constant, exactly as declared in its enum declaration.
     *
     * Most programmers should use the {@see self::__toString()} method in preference to this one, as the toString
     * method may return a more user-friendly name. This method is designed primarily for use in specialized situations
     * where correctness depends on getting the exact name, which will not vary from release to release.
     */
    final public function name() : string
    {
        return $this->name;
    }

    /**
     * Returns the ordinal of this enumeration constant (its position in its enum declaration, where the initial
     * constant is assigned an ordinal of zero).
     *
     * Most programmers will have no use for this method. It is designed for use by sophisticated enum-based data
     * structures.
     */
    final public function ordinal() : int
    {
        return $this->ordinal;
    }

    /**
     * Compares this enum with the specified object for order.
     *
     * Returns negative integer, zero or positive integer as this object is less than, equal to or greater than the
     * specified object.
     *
     * Enums are only comparable to other enums of the same type. The natural order implemented by this method is the
     * order in which the constants are declared.
     *
     * @throws MismatchException if the passed enum is not of the same type
     */
    final public function compareTo(self $other) : int
    {
        if (! $other instanceof static) {
            throw new MismatchException(sprintf(
                'The passed enum %s is not of the same type as %s',
                get_class($other),
                static::class
            ));
        }

        return $this->ordinal - $other->ordinal;
    }

    /**
     * Forbid cloning enums.
     *
     * @throws CloneNotSupportedException
     */
    final public function __clone()
    {
        throw new CloneNotSupportedException();
    }

    /**
     * Forbid serializing enums.
     *
     * @throws SerializeNotSupportedException
     */
    final public function __sleep() : array
    {
        throw new SerializeNotSupportedException();
    }

    /**
     * Forbid serializing enums.
     *
     * @throws SerializeNotSupportedException
     */
    final public function __serialize() : array
    {
        throw new SerializeNotSupportedException();
    }

    /**
     * Forbid unserializing enums.
     *
     * @throws UnserializeNotSupportedException
     */
    final public function __wakeup() : void
    {
        throw new UnserializeNotSupportedException();
    }

    /**
     * Forbid unserializing enums.
     *
     * @throws UnserializeNotSupportedException
     */
    final public function __unserialize($arg) : void
    {
        throw new UnserializeNotSupportedException();
    }

    /**
     * Turns the enum into a string representation.
     *
     * You may override this method to give a more user-friendly version.
     */
    public function __toString() : string
    {
        return $this->name;
    }
}
<?php
declare(strict_types = 1);

namespace DASPRiD\Enum;

use DASPRiD\Enum\Exception\ExpectationException;
use DASPRiD\Enum\Exception\IllegalArgumentException;
use IteratorAggregate;
use Serializable;
use Traversable;

/**
 * A specialized map implementation for use with enum type keys.
 *
 * All of the keys in an enum map must come from a single enum type that is specified, when the map is created. Enum
 * maps are represented internally as arrays. This representation is extremely compact and efficient.
 *
 * Enum maps are maintained in the natural order of their keys (the order in which the enum constants are declared).
 * This is reflected in the iterators returned by the collection views {@see self::getIterator()} and
 * {@see self::values()}.
 *
 * Iterators returned by the collection views are not consistent: They may or may not show the effects of modifications
 * to the map that occur while the iteration is in progress.
 */
final class EnumMap implements Serializable, IteratorAggregate
{
    /**
     * The class name of the key.
     *
     * @var string
     */
    private $keyType;

    /**
     * The type of the value.
     *
     * @var string
     */
    private $valueType;

    /**
     * @var bool
     */
    private $allowNullValues;

    /**
     * All of the constants comprising the enum, cached for performance.
     *
     * @var array<int, AbstractEnum>
     */
    private $keyUniverse;

    /**
     * Array representation of this map. The ith element is the value to which universe[i] is currently mapped, or null
     * if it isn't mapped to anything, or NullValue if it's mapped to null.
     *
     * @var array<int, mixed>
     */
    private $values;

    /**
     * @var int
     */
    private $size = 0;

    /**
     * Creates a new enum map.
     *
     * @param string $keyType the type of the keys, must extend AbstractEnum
     * @param string $valueType the type of the values
     * @param bool $allowNullValues whether to allow null values
     * @throws IllegalArgumentException when key type does not extend AbstractEnum
     */
    public function __construct(string $keyType, string $valueType, bool $allowNullValues)
    {
        if (! is_subclass_of($keyType, AbstractEnum::class)) {
            throw new IllegalArgumentException(sprintf(
                'Class %s does not extend %s',
                $keyType,
                AbstractEnum::class
            ));
        }

        $this->keyType = $keyType;
        $this->valueType = $valueType;
        $this->allowNullValues = $allowNullValues;
        $this->keyUniverse = $keyType::values();
        $this->values = array_fill(0, count($this->keyUniverse), null);
    }

    public function __serialize(): array
    {
        $values = [];

        foreach ($this->values as $ordinal => $value) {
            if (null === $value) {
                continue;
            }

            $values[$ordinal] = $this->unmaskNull($value);
        }

        return [
            'keyType' => $this->keyType,
            'valueType' => $this->valueType,
            'allowNullValues' => $this->allowNullValues,
            'values' => $values,
        ];
    }

    public function __unserialize(array $data): void
    {
        $this->unserialize(serialize($data));
    }

    /**
     * Checks whether the map types match the supplied ones.
     *
     * You should call this method when an EnumMap is passed to you and you want to ensure that it's made up of the
     * correct types.
     *
     * @throws ExpectationException when supplied key type mismatches local key type
     * @throws ExpectationException when supplied value type mismatches local value type
     * @throws ExpectationException when the supplied map allows null values, abut should not
     */
    public function expect(string $keyType, string $valueType, bool $allowNullValues) : void
    {
        if ($keyType !== $this->keyType) {
            throw new ExpectationException(sprintf(
                'Callee expected an EnumMap with key type %s, but got %s',
                $keyType,
                $this->keyType
            ));
        }

        if ($valueType !== $this->valueType) {
            throw new ExpectationException(sprintf(
                'Callee expected an EnumMap with value type %s, but got %s',
                $keyType,
                $this->keyType
            ));
        }

        if ($allowNullValues !== $this->allowNullValues) {
            throw new ExpectationException(sprintf(
                'Callee expected an EnumMap with nullable flag %s, but got %s',
                ($allowNullValues ? 'true' : 'false'),
                ($this->allowNullValues ? 'true' : 'false')
            ));
        }
    }

    /**
     * Returns the number of key-value mappings in this map.
     */
    public function size() : int
    {
        return $this->size;
    }

    /**
     * Returns true if this map maps one or more keys to the specified value.
     */
    public function containsValue($value) : bool
    {
        return in_array($this->maskNull($value), $this->values, true);
    }

    /**
     * Returns true if this map contains a mapping for the specified key.
     */
    public function containsKey(AbstractEnum $key) : bool
    {
        $this->checkKeyType($key);
        return null !== $this->values[$key->ordinal()];
    }

    /**
     * Returns the value to which the specified key is mapped, or null if this map contains no mapping for the key.
     *
     * More formally, if this map contains a mapping from a key to a value, then this method returns the value;
     * otherwise it returns null (there can be at most one such mapping).
     *
     * A return value of null does not necessarily indicate that the map contains no mapping for the key; it's also
     * possible that hte map explicitly maps the key to null. The {@see self::containsKey()} operation may be used to
     * distinguish these two cases.
     *
     * @return mixed
     */
    public function get(AbstractEnum $key)
    {
        $this->checkKeyType($key);
        return $this->unmaskNull($this->values[$key->ordinal()]);
    }

    /**
     * Associates the specified value with the specified key in this map.
     *
     * If the map previously contained a mapping for this key, the old value is replaced.
     *
     * @return mixed the previous value associated with the specified key, or null if there was no mapping for the key.
     *               (a null return can also indicate that the map previously associated null with the specified key.)
     * @throws IllegalArgumentException when the passed values does not match the internal value type
     */
    public function put(AbstractEnum $key, $value)
    {
        $this->checkKeyType($key);

        if (! $this->isValidValue($value)) {
            throw new IllegalArgumentException(sprintf('Value is not of type %s', $this->valueType));
        }

        $index = $key->ordinal();
        $oldValue = $this->values[$index];
        $this->values[$index] = $this->maskNull($value);

        if (null === $oldValue) {
            ++$this->size;
        }

        return $this->unmaskNull($oldValue);
    }

    /**
     * Removes the mapping for this key frm this map if present.
     *
     * @return mixed the previous value associated with the specified key, or null if there was no mapping for the key.
     *               (a null return can also indicate that the map previously associated null with the specified key.)
     */
    public function remove(AbstractEnum $key)
    {
        $this->checkKeyType($key);

        $index = $key->ordinal();
        $oldValue = $this->values[$index];
        $this->values[$index] = null;

        if (null !== $oldValue) {
            --$this->size;
        }

        return $this->unmaskNull($oldValue);
    }

    /**
     * Removes all mappings from this map.
     */
    public function clear() : void
    {
        $this->values = array_fill(0, count($this->keyUniverse), null);
        $this->size = 0;
    }

    /**
     * Compares the specified map with this map for quality.
     *
     * Returns true if the two maps represent the same mappings.
     */
    public function equals(self $other) : bool
    {
        if ($this === $other) {
            return true;
        }

        if ($this->size !== $other->size) {
            return false;
        }

        return $this->values === $other->values;
    }

    /**
     * Returns the values contained in this map.
     *
     * The array will contain the values in the order their corresponding keys appear in the map, which is their natural
     * order (the order in which the num constants are declared).
     */
    public function values() : array
    {
        return array_values(array_map(function ($value) {
            return $this->unmaskNull($value);
        }, array_filter($this->values, function ($value) : bool {
            return null !== $value;
        })));
    }

    public function serialize() : string
    {
        return serialize($this->__serialize());
    }

    public function unserialize($serialized) : void
    {
        $data = unserialize($serialized);
        $this->__construct($data['keyType'], $data['valueType'], $data['allowNullValues']);

        foreach ($this->keyUniverse as $key) {
            if (array_key_exists($key->ordinal(), $data['values'])) {
                $this->put($key, $data['values'][$key->ordinal()]);
            }
        }
    }

    public function getIterator() : Traversable
    {
        foreach ($this->keyUniverse as $key) {
            if (null === $this->values[$key->ordinal()]) {
                continue;
            }

            yield $key => $this->unmaskNull($this->values[$key->ordinal()]);
        }
    }

    private function maskNull($value)
    {
        if (null === $value) {
            return NullValue::instance();
        }

        return $value;
    }

    private function unmaskNull($value)
    {
        if ($value instanceof NullValue) {
            return null;
        }

        return $value;
    }

    /**
     * @throws IllegalArgumentException when the passed key does not match the internal key type
     */
    private function checkKeyType(AbstractEnum $key) : void
    {
        if (get_class($key) !== $this->keyType) {
            throw new IllegalArgumentException(sprintf(
                'Object of type %s is not the same type as %s',
                get_class($key),
                $this->keyType
            ));
        }
    }

    private function isValidValue($value) : bool
    {
        if (null === $value) {
            if ($this->allowNullValues) {
                return true;
            }

            return false;
        }

        switch ($this->valueType) {
            case 'mixed':
                return true;

            case 'bool':
            case 'boolean':
                return is_bool($value);

            case 'int':
            case 'integer':
                return is_int($value);

            case 'float':
            case 'double':
                return is_float($value);

            case 'string':
                return is_string($value);

            case 'object':
                return is_object($value);

            case 'array':
                return is_array($value);
        }

        return $value instanceof $this->valueType;
    }
}
{
    "name": "dasprid/enum",
    "description": "PHP 7.1 enum implementation",
    "license": "BSD-2-Clause",
    "authors": [
        {
            "name": "Ben Scholzen 'DASPRiD'",
            "email": "mail@dasprids.de",
            "homepage": "https://dasprids.de/",
            "role": "Developer"
        }
    ],
    "keywords": [
        "enum",
        "map"
    ],
    "require": {
        "php": ">=7.1 <9.0"
    },
    "require-dev": {
        "phpunit/phpunit": "^7 || ^8 || ^9 || ^10 || ^11",
        "squizlabs/php_codesniffer": "*"
    },
    "autoload": {
        "psr-4": {
            "DASPRiD\\Enum\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "DASPRiD\\EnumTest\\": "test/"
        }
    }
}
<?php

namespace Psr\Log;

class InvalidArgumentException extends \InvalidArgumentException
{
}
<?php

namespace Psr\Log;

/**
 * This Logger can be used to avoid conditional log calls.
 *
 * Logging should always be optional, and if no logger is provided to your
 * library creating a NullLogger instance to have something to throw logs at
 * is a good way to avoid littering your code with `if ($this->logger) { }`
 * blocks.
 */
class NullLogger extends AbstractLogger
{
    /**
     * Logs with an arbitrary level.
     *
     * @param mixed[] $context
     *
     * @throws \Psr\Log\InvalidArgumentException
     */
    public function log($level, string|\Stringable $message, array $context = []): void
    {
        // noop
    }
}
<?php

namespace Psr\Log;

/**
 * Describes log levels.
 */
class LogLevel
{
    const EMERGENCY = 'emergency';
    const ALERT     = 'alert';
    const CRITICAL  = 'critical';
    const ERROR     = 'error';
    const WARNING   = 'warning';
    const NOTICE    = 'notice';
    const INFO      = 'info';
    const DEBUG     = 'debug';
}
<?php

namespace Psr\Log;

/**
 * Describes a logger-aware instance.
 */
interface LoggerAwareInterface
{
    /**
     * Sets a logger instance on the object.
     */
    public function setLogger(LoggerInterface $logger): void;
}
<?php

namespace Psr\Log;

/**
 * This is a simple Logger trait that classes unable to extend AbstractLogger
 * (because they extend another class, etc) can include.
 *
 * It simply delegates all log-level-specific methods to the `log` method to
 * reduce boilerplate code that a simple Logger that does the same thing with
 * messages regardless of the error level has to implement.
 */
trait LoggerTrait
{
    /**
     * System is unusable.
     */
    public function emergency(string|\Stringable $message, array $context = []): void
    {
        $this->log(LogLevel::EMERGENCY, $message, $context);
    }

    /**
     * Action must be taken immediately.
     *
     * Example: Entire website down, database unavailable, etc. This should
     * trigger the SMS alerts and wake you up.
     */
    public function alert(string|\Stringable $message, array $context = []): void
    {
        $this->log(LogLevel::ALERT, $message, $context);
    }

    /**
     * Critical conditions.
     *
     * Example: Application component unavailable, unexpected exception.
     */
    public function critical(string|\Stringable $message, array $context = []): void
    {
        $this->log(LogLevel::CRITICAL, $message, $context);
    }

    /**
     * Runtime errors that do not require immediate action but should typically
     * be logged and monitored.
     */
    public function error(string|\Stringable $message, array $context = []): void
    {
        $this->log(LogLevel::ERROR, $message, $context);
    }

    /**
     * Exceptional occurrences that are not errors.
     *
     * Example: Use of deprecated APIs, poor use of an API, undesirable things
     * that are not necessarily wrong.
     */
    public function warning(string|\Stringable $message, array $context = []): void
    {
        $this->log(LogLevel::WARNING, $message, $context);
    }

    /**
     * Normal but significant events.
     */
    public function notice(string|\Stringable $message, array $context = []): void
    {
        $this->log(LogLevel::NOTICE, $message, $context);
    }

    /**
     * Interesting events.
     *
     * Example: User logs in, SQL logs.
     */
    public function info(string|\Stringable $message, array $context = []): void
    {
        $this->log(LogLevel::INFO, $message, $context);
    }

    /**
     * Detailed debug information.
     */
    public function debug(string|\Stringable $message, array $context = []): void
    {
        $this->log(LogLevel::DEBUG, $message, $context);
    }

    /**
     * Logs with an arbitrary level.
     *
     * @param mixed $level
     *
     * @throws \Psr\Log\InvalidArgumentException
     */
    abstract public function log($level, string|\Stringable $message, array $context = []): void;
}
<?php

namespace Psr\Log;

/**
 * Basic Implementation of LoggerAwareInterface.
 */
trait LoggerAwareTrait
{
    /**
     * The logger instance.
     */
    protected ?LoggerInterface $logger = null;

    /**
     * Sets a logger.
     */
    public function setLogger(LoggerInterface $logger): void
    {
        $this->logger = $logger;
    }
}
<?php

namespace Psr\Log;

/**
 * Describes a logger instance.
 *
 * The message MUST be a string or object implementing __toString().
 *
 * The message MAY contain placeholders in the form: {foo} where foo
 * will be replaced by the context data in key "foo".
 *
 * The context array can contain arbitrary data. The only assumption that
 * can be made by implementors is that if an Exception instance is given
 * to produce a stack trace, it MUST be in a key named "exception".
 *
 * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md
 * for the full interface specification.
 */
interface LoggerInterface
{
    /**
     * System is unusable.
     *
     * @param mixed[] $context
     */
    public function emergency(string|\Stringable $message, array $context = []): void;

    /**
     * Action must be taken immediately.
     *
     * Example: Entire website down, database unavailable, etc. This should
     * trigger the SMS alerts and wake you up.
     *
     * @param mixed[] $context
     */
    public function alert(string|\Stringable $message, array $context = []): void;

    /**
     * Critical conditions.
     *
     * Example: Application component unavailable, unexpected exception.
     *
     * @param mixed[] $context
     */
    public function critical(string|\Stringable $message, array $context = []): void;

    /**
     * Runtime errors that do not require immediate action but should typically
     * be logged and monitored.
     *
     * @param mixed[] $context
     */
    public function error(string|\Stringable $message, array $context = []): void;

    /**
     * Exceptional occurrences that are not errors.
     *
     * Example: Use of deprecated APIs, poor use of an API, undesirable things
     * that are not necessarily wrong.
     *
     * @param mixed[] $context
     */
    public function warning(string|\Stringable $message, array $context = []): void;

    /**
     * Normal but significant events.
     *
     * @param mixed[] $context
     */
    public function notice(string|\Stringable $message, array $context = []): void;

    /**
     * Interesting events.
     *
     * Example: User logs in, SQL logs.
     *
     * @param mixed[] $context
     */
    public function info(string|\Stringable $message, array $context = []): void;

    /**
     * Detailed debug information.
     *
     * @param mixed[] $context
     */
    public function debug(string|\Stringable $message, array $context = []): void;

    /**
     * Logs with an arbitrary level.
     *
     * @param mixed $level
     * @param mixed[] $context
     *
     * @throws \Psr\Log\InvalidArgumentException
     */
    public function log($level, string|\Stringable $message, array $context = []): void;
}
<?php

namespace Psr\Log;

/**
 * This is a simple Logger implementation that other Loggers can inherit from.
 *
 * It simply delegates all log-level-specific methods to the `log` method to
 * reduce boilerplate code that a simple Logger that does the same thing with
 * messages regardless of the error level has to implement.
 */
abstract class AbstractLogger implements LoggerInterface
{
    use LoggerTrait;
}
{
    "name": "psr/log",
    "description": "Common interface for logging libraries",
    "keywords": ["psr", "psr-3", "log"],
    "homepage": "https://github.com/php-fig/log",
    "license": "MIT",
    "authors": [
        {
            "name": "PHP-FIG",
            "homepage": "https://www.php-fig.org/"
        }
    ],
    "require": {
        "php": ">=8.0.0"
    },
    "autoload": {
        "psr-4": {
            "Psr\\Log\\": "src"
        }
    },
    "extra": {
        "branch-alias": {
            "dev-master": "3.x-dev"
        }
    }
}
<?php

namespace Psr\Http\Message;

interface UploadedFileFactoryInterface
{
    /**
     * Create a new uploaded file.
     *
     * If a size is not provided it will be determined by checking the size of
     * the file.
     *
     * @see http://php.net/manual/features.file-upload.post-method.php
     * @see http://php.net/manual/features.file-upload.errors.php
     *
     * @param StreamInterface $stream Underlying stream representing the
     *     uploaded file content.
     * @param int|null $size in bytes
     * @param int $error PHP file upload error
     * @param string|null $clientFilename Filename as provided by the client, if any.
     * @param string|null $clientMediaType Media type as provided by the client, if any.
     *
     * @return UploadedFileInterface
     *
     * @throws \InvalidArgumentException If the file resource is not readable.
     */
    public function createUploadedFile(
        StreamInterface $stream,
        ?int $size = null,
        int $error = \UPLOAD_ERR_OK,
        ?string $clientFilename = null,
        ?string $clientMediaType = null
    ): UploadedFileInterface;
}
<?php

namespace Psr\Http\Message;

interface StreamFactoryInterface
{
    /**
     * Create a new stream from a string.
     *
     * The stream SHOULD be created with a temporary resource.
     *
     * @param string $content String content with which to populate the stream.
     *
     * @return StreamInterface
     */
    public function createStream(string $content = ''): StreamInterface;

    /**
     * Create a stream from an existing file.
     *
     * The file MUST be opened using the given mode, which may be any mode
     * supported by the `fopen` function.
     *
     * The `$filename` MAY be any string supported by `fopen()`.
     *
     * @param string $filename Filename or stream URI to use as basis of stream.
     * @param string $mode Mode with which to open the underlying filename/stream.
     *
     * @return StreamInterface
     * @throws \RuntimeException If the file cannot be opened.
     * @throws \InvalidArgumentException If the mode is invalid.
     */
    public function createStreamFromFile(string $filename, string $mode = 'r'): StreamInterface;

    /**
     * Create a new stream from an existing resource.
     *
     * The stream MUST be readable and may be writable.
     *
     * @param resource $resource PHP resource to use as basis of stream.
     *
     * @return StreamInterface
     */
    public function createStreamFromResource($resource): StreamInterface;
}
<?php

namespace Psr\Http\Message;

interface UriFactoryInterface
{
    /**
     * Create a new URI.
     *
     * @param string $uri
     *
     * @return UriInterface
     *
     * @throws \InvalidArgumentException If the given URI cannot be parsed.
     */
    public function createUri(string $uri = ''): UriInterface;
}
<?php

namespace Psr\Http\Message;

interface ResponseFactoryInterface
{
    /**
     * Create a new response.
     *
     * @param int $code HTTP status code; defaults to 200
     * @param string $reasonPhrase Reason phrase to associate with status code
     *     in generated response; if none is provided implementations MAY use
     *     the defaults as suggested in the HTTP specification.
     *
     * @return ResponseInterface
     */
    public function createResponse(int $code = 200, string $reasonPhrase = ''): ResponseInterface;
}
<?php

namespace Psr\Http\Message;

interface RequestFactoryInterface
{
    /**
     * Create a new request.
     *
     * @param string $method The HTTP method associated with the request.
     * @param UriInterface|string $uri The URI associated with the request. If
     *     the value is a string, the factory MUST create a UriInterface
     *     instance based on it.
     *
     * @return RequestInterface
     */
    public function createRequest(string $method, $uri): RequestInterface;
}
<?php

namespace Psr\Http\Message;

interface ServerRequestFactoryInterface
{
    /**
     * Create a new server request.
     *
     * Note that server-params are taken precisely as given - no parsing/processing
     * of the given values is performed, and, in particular, no attempt is made to
     * determine the HTTP method or URI, which must be provided explicitly.
     *
     * @param string $method The HTTP method associated with the request.
     * @param UriInterface|string $uri The URI associated with the request. If
     *     the value is a string, the factory MUST create a UriInterface
     *     instance based on it.
     * @param array $serverParams Array of SAPI parameters with which to seed
     *     the generated request instance.
     *
     * @return ServerRequestInterface
     */
    public function createServerRequest(string $method, $uri, array $serverParams = []): ServerRequestInterface;
}
{
    "name": "psr/http-factory",
    "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories",
    "keywords": [
        "psr",
        "psr-7",
        "psr-17",
        "http",
        "factory",
        "message",
        "request",
        "response"
    ],
    "license": "MIT",
    "authors": [
        {
            "name": "PHP-FIG",
            "homepage": "https://www.php-fig.org/"
        }
    ],
    "support": {
        "source": "https://github.com/php-fig/http-factory"
    },
    "require": {
        "php": ">=7.1",
        "psr/http-message": "^1.0 || ^2.0"
    },
    "autoload": {
        "psr-4": {
            "Psr\\Http\\Message\\": "src/"
        }
    },
    "extra": {
        "branch-alias": {
            "dev-master": "1.0.x-dev"
        }
    }
}
<?php

namespace Psr\Http\Message;

/**
 * Representation of an outgoing, server-side response.
 *
 * Per the HTTP specification, this interface includes properties for
 * each of the following:
 *
 * - Protocol version
 * - Status code and reason phrase
 * - Headers
 * - Message body
 *
 * Responses are considered immutable; all methods that might change state MUST
 * be implemented such that they retain the internal state of the current
 * message and return an instance that contains the changed state.
 */
interface ResponseInterface extends MessageInterface
{
    /**
     * Gets the response status code.
     *
     * The status code is a 3-digit integer result code of the server's attempt
     * to understand and satisfy the request.
     *
     * @return int Status code.
     */
    public function getStatusCode(): int;

    /**
     * Return an instance with the specified status code and, optionally, reason phrase.
     *
     * If no reason phrase is specified, implementations MAY choose to default
     * to the RFC 7231 or IANA recommended reason phrase for the response's
     * status code.
     *
     * This method MUST be implemented in such a way as to retain the
     * immutability of the message, and MUST return an instance that has the
     * updated status and reason phrase.
     *
     * @link http://tools.ietf.org/html/rfc7231#section-6
     * @link http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
     * @param int $code The 3-digit integer result code to set.
     * @param string $reasonPhrase The reason phrase to use with the
     *     provided status code; if none is provided, implementations MAY
     *     use the defaults as suggested in the HTTP specification.
     * @return static
     * @throws \InvalidArgumentException For invalid status code arguments.
     */
    public function withStatus(int $code, string $reasonPhrase = ''): ResponseInterface;

    /**
     * Gets the response reason phrase associated with the status code.
     *
     * Because a reason phrase is not a required element in a response
     * status line, the reason phrase value MAY be null. Implementations MAY
     * choose to return the default RFC 7231 recommended reason phrase (or those
     * listed in the IANA HTTP Status Code Registry) for the response's
     * status code.
     *
     * @link http://tools.ietf.org/html/rfc7231#section-6
     * @link http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
     * @return string Reason phrase; must return an empty string if none present.
     */
    public function getReasonPhrase(): string;
}
<?php

namespace Psr\Http\Message;

/**
 * Describes a data stream.
 *
 * Typically, an instance will wrap a PHP stream; this interface provides
 * a wrapper around the most common operations, including serialization of
 * the entire stream to a string.
 */
interface StreamInterface
{
    /**
     * Reads all data from the stream into a string, from the beginning to end.
     *
     * This method MUST attempt to seek to the beginning of the stream before
     * reading data and read the stream until the end is reached.
     *
     * Warning: This could attempt to load a large amount of data into memory.
     *
     * This method MUST NOT raise an exception in order to conform with PHP's
     * string casting operations.
     *
     * @see http://php.net/manual/en/language.oop5.magic.php#object.tostring
     * @return string
     */
    public function __toString(): string;

    /**
     * Closes the stream and any underlying resources.
     *
     * @return void
     */
    public function close(): void;

    /**
     * Separates any underlying resources from the stream.
     *
     * After the stream has been detached, the stream is in an unusable state.
     *
     * @return resource|null Underlying PHP stream, if any
     */
    public function detach();

    /**
     * Get the size of the stream if known.
     *
     * @return int|null Returns the size in bytes if known, or null if unknown.
     */
    public function getSize(): ?int;

    /**
     * Returns the current position of the file read/write pointer
     *
     * @return int Position of the file pointer
     * @throws \RuntimeException on error.
     */
    public function tell(): int;

    /**
     * Returns true if the stream is at the end of the stream.
     *
     * @return bool
     */
    public function eof(): bool;

    /**
     * Returns whether or not the stream is seekable.
     *
     * @return bool
     */
    public function isSeekable(): bool;

    /**
     * Seek to a position in the stream.
     *
     * @link http://www.php.net/manual/en/function.fseek.php
     * @param int $offset Stream offset
     * @param int $whence Specifies how the cursor position will be calculated
     *     based on the seek offset. Valid values are identical to the built-in
     *     PHP $whence values for `fseek()`.  SEEK_SET: Set position equal to
     *     offset bytes SEEK_CUR: Set position to current location plus offset
     *     SEEK_END: Set position to end-of-stream plus offset.
     * @throws \RuntimeException on failure.
     */
    public function seek(int $offset, int $whence = SEEK_SET): void;

    /**
     * Seek to the beginning of the stream.
     *
     * If the stream is not seekable, this method will raise an exception;
     * otherwise, it will perform a seek(0).
     *
     * @see seek()
     * @link http://www.php.net/manual/en/function.fseek.php
     * @throws \RuntimeException on failure.
     */
    public function rewind(): void;

    /**
     * Returns whether or not the stream is writable.
     *
     * @return bool
     */
    public function isWritable(): bool;

    /**
     * Write data to the stream.
     *
     * @param string $string The string that is to be written.
     * @return int Returns the number of bytes written to the stream.
     * @throws \RuntimeException on failure.
     */
    public function write(string $string): int;

    /**
     * Returns whether or not the stream is readable.
     *
     * @return bool
     */
    public function isReadable(): bool;

    /**
     * Read data from the stream.
     *
     * @param int $length Read up to $length bytes from the object and return
     *     them. Fewer than $length bytes may be returned if underlying stream
     *     call returns fewer bytes.
     * @return string Returns the data read from the stream, or an empty string
     *     if no bytes are available.
     * @throws \RuntimeException if an error occurs.
     */
    public function read(int $length): string;

    /**
     * Returns the remaining contents in a string
     *
     * @return string
     * @throws \RuntimeException if unable to read or an error occurs while
     *     reading.
     */
    public function getContents(): string;

    /**
     * Get stream metadata as an associative array or retrieve a specific key.
     *
     * The keys returned are identical to the keys returned from PHP's
     * stream_get_meta_data() function.
     *
     * @link http://php.net/manual/en/function.stream-get-meta-data.php
     * @param string|null $key Specific metadata to retrieve.
     * @return array|mixed|null Returns an associative array if no key is
     *     provided. Returns a specific key value if a key is provided and the
     *     value is found, or null if the key is not found.
     */
    public function getMetadata(?string $key = null);
}
<?php

namespace Psr\Http\Message;

/**
 * HTTP messages consist of requests from a client to a server and responses
 * from a server to a client. This interface defines the methods common to
 * each.
 *
 * Messages are considered immutable; all methods that might change state MUST
 * be implemented such that they retain the internal state of the current
 * message and return an instance that contains the changed state.
 *
 * @link http://www.ietf.org/rfc/rfc7230.txt
 * @link http://www.ietf.org/rfc/rfc7231.txt
 */
interface MessageInterface
{
    /**
     * Retrieves the HTTP protocol version as a string.
     *
     * The string MUST contain only the HTTP version number (e.g., "1.1", "1.0").
     *
     * @return string HTTP protocol version.
     */
    public function getProtocolVersion(): string;

    /**
     * Return an instance with the specified HTTP protocol version.
     *
     * The version string MUST contain only the HTTP version number (e.g.,
     * "1.1", "1.0").
     *
     * This method MUST be implemented in such a way as to retain the
     * immutability of the message, and MUST return an instance that has the
     * new protocol version.
     *
     * @param string $version HTTP protocol version
     * @return static
     */
    public function withProtocolVersion(string $version): MessageInterface;

    /**
     * Retrieves all message header values.
     *
     * The keys represent the header name as it will be sent over the wire, and
     * each value is an array of strings associated with the header.
     *
     *     // Represent the headers as a string
     *     foreach ($message->getHeaders() as $name => $values) {
     *         echo $name . ": " . implode(", ", $values);
     *     }
     *
     *     // Emit headers iteratively:
     *     foreach ($message->getHeaders() as $name => $values) {
     *         foreach ($values as $value) {
     *             header(sprintf('%s: %s', $name, $value), false);
     *         }
     *     }
     *
     * While header names are not case-sensitive, getHeaders() will preserve the
     * exact case in which headers were originally specified.
     *
     * @return string[][] Returns an associative array of the message's headers. Each
     *     key MUST be a header name, and each value MUST be an array of strings
     *     for that header.
     */
    public function getHeaders(): array;

    /**
     * Checks if a header exists by the given case-insensitive name.
     *
     * @param string $name Case-insensitive header field name.
     * @return bool Returns true if any header names match the given header
     *     name using a case-insensitive string comparison. Returns false if
     *     no matching header name is found in the message.
     */
    public function hasHeader(string $name): bool;

    /**
     * Retrieves a message header value by the given case-insensitive name.
     *
     * This method returns an array of all the header values of the given
     * case-insensitive header name.
     *
     * If the header does not appear in the message, this method MUST return an
     * empty array.
     *
     * @param string $name Case-insensitive header field name.
     * @return string[] An array of string values as provided for the given
     *    header. If the header does not appear in the message, this method MUST
     *    return an empty array.
     */
    public function getHeader(string $name): array;

    /**
     * Retrieves a comma-separated string of the values for a single header.
     *
     * This method returns all of the header values of the given
     * case-insensitive header name as a string concatenated together using
     * a comma.
     *
     * NOTE: Not all header values may be appropriately represented using
     * comma concatenation. For such headers, use getHeader() instead
     * and supply your own delimiter when concatenating.
     *
     * If the header does not appear in the message, this method MUST return
     * an empty string.
     *
     * @param string $name Case-insensitive header field name.
     * @return string A string of values as provided for the given header
     *    concatenated together using a comma. If the header does not appear in
     *    the message, this method MUST return an empty string.
     */
    public function getHeaderLine(string $name): string;

    /**
     * Return an instance with the provided value replacing the specified header.
     *
     * While header names are case-insensitive, the casing of the header will
     * be preserved by this function, and returned from getHeaders().
     *
     * This method MUST be implemented in such a way as to retain the
     * immutability of the message, and MUST return an instance that has the
     * new and/or updated header and value.
     *
     * @param string $name Case-insensitive header field name.
     * @param string|string[] $value Header value(s).
     * @return static
     * @throws \InvalidArgumentException for invalid header names or values.
     */
    public function withHeader(string $name, $value): MessageInterface;

    /**
     * Return an instance with the specified header appended with the given value.
     *
     * Existing values for the specified header will be maintained. The new
     * value(s) will be appended to the existing list. If the header did not
     * exist previously, it will be added.
     *
     * This method MUST be implemented in such a way as to retain the
     * immutability of the message, and MUST return an instance that has the
     * new header and/or value.
     *
     * @param string $name Case-insensitive header field name to add.
     * @param string|string[] $value Header value(s).
     * @return static
     * @throws \InvalidArgumentException for invalid header names or values.
     */
    public function withAddedHeader(string $name, $value): MessageInterface;

    /**
     * Return an instance without the specified header.
     *
     * Header resolution MUST be done without case-sensitivity.
     *
     * This method MUST be implemented in such a way as to retain the
     * immutability of the message, and MUST return an instance that removes
     * the named header.
     *
     * @param string $name Case-insensitive header field name to remove.
     * @return static
     */
    public function withoutHeader(string $name): MessageInterface;

    /**
     * Gets the body of the message.
     *
     * @return StreamInterface Returns the body as a stream.
     */
    public function getBody(): StreamInterface;

    /**
     * Return an instance with the specified message body.
     *
     * The body MUST be a StreamInterface object.
     *
     * This method MUST be implemented in such a way as to retain the
     * immutability of the message, and MUST return a new instance that has the
     * new body stream.
     *
     * @param StreamInterface $body Body.
     * @return static
     * @throws \InvalidArgumentException When the body is not valid.
     */
    public function withBody(StreamInterface $body): MessageInterface;
}
<?php

namespace Psr\Http\Message;

/**
 * Value object representing a URI.
 *
 * This interface is meant to represent URIs according to RFC 3986 and to
 * provide methods for most common operations. Additional functionality for
 * working with URIs can be provided on top of the interface or externally.
 * Its primary use is for HTTP requests, but may also be used in other
 * contexts.
 *
 * Instances of this interface are considered immutable; all methods that
 * might change state MUST be implemented such that they retain the internal
 * state of the current instance and return an instance that contains the
 * changed state.
 *
 * Typically the Host header will be also be present in the request message.
 * For server-side requests, the scheme will typically be discoverable in the
 * server parameters.
 *
 * @link http://tools.ietf.org/html/rfc3986 (the URI specification)
 */
interface UriInterface
{
    /**
     * Retrieve the scheme component of the URI.
     *
     * If no scheme is present, this method MUST return an empty string.
     *
     * The value returned MUST be normalized to lowercase, per RFC 3986
     * Section 3.1.
     *
     * The trailing ":" character is not part of the scheme and MUST NOT be
     * added.
     *
     * @see https://tools.ietf.org/html/rfc3986#section-3.1
     * @return string The URI scheme.
     */
    public function getScheme(): string;

    /**
     * Retrieve the authority component of the URI.
     *
     * If no authority information is present, this method MUST return an empty
     * string.
     *
     * The authority syntax of the URI is:
     *
     * <pre>
     * [user-info@]host[:port]
     * </pre>
     *
     * If the port component is not set or is the standard port for the current
     * scheme, it SHOULD NOT be included.
     *
     * @see https://tools.ietf.org/html/rfc3986#section-3.2
     * @return string The URI authority, in "[user-info@]host[:port]" format.
     */
    public function getAuthority(): string;

    /**
     * Retrieve the user information component of the URI.
     *
     * If no user information is present, this method MUST return an empty
     * string.
     *
     * If a user is present in the URI, this will return that value;
     * additionally, if the password is also present, it will be appended to the
     * user value, with a colon (":") separating the values.
     *
     * The trailing "@" character is not part of the user information and MUST
     * NOT be added.
     *
     * @return string The URI user information, in "username[:password]" format.
     */
    public function getUserInfo(): string;

    /**
     * Retrieve the host component of the URI.
     *
     * If no host is present, this method MUST return an empty string.
     *
     * The value returned MUST be normalized to lowercase, per RFC 3986
     * Section 3.2.2.
     *
     * @see http://tools.ietf.org/html/rfc3986#section-3.2.2
     * @return string The URI host.
     */
    public function getHost(): string;

    /**
     * Retrieve the port component of the URI.
     *
     * If a port is present, and it is non-standard for the current scheme,
     * this method MUST return it as an integer. If the port is the standard port
     * used with the current scheme, this method SHOULD return null.
     *
     * If no port is present, and no scheme is present, this method MUST return
     * a null value.
     *
     * If no port is present, but a scheme is present, this method MAY return
     * the standard port for that scheme, but SHOULD return null.
     *
     * @return null|int The URI port.
     */
    public function getPort(): ?int;

    /**
     * Retrieve the path component of the URI.
     *
     * The path can either be empty or absolute (starting with a slash) or
     * rootless (not starting with a slash). Implementations MUST support all
     * three syntaxes.
     *
     * Normally, the empty path "" and absolute path "/" are considered equal as
     * defined in RFC 7230 Section 2.7.3. But this method MUST NOT automatically
     * do this normalization because in contexts with a trimmed base path, e.g.
     * the front controller, this difference becomes significant. It's the task
     * of the user to handle both "" and "/".
     *
     * The value returned MUST be percent-encoded, but MUST NOT double-encode
     * any characters. To determine what characters to encode, please refer to
     * RFC 3986, Sections 2 and 3.3.
     *
     * As an example, if the value should include a slash ("/") not intended as
     * delimiter between path segments, that value MUST be passed in encoded
     * form (e.g., "%2F") to the instance.
     *
     * @see https://tools.ietf.org/html/rfc3986#section-2
     * @see https://tools.ietf.org/html/rfc3986#section-3.3
     * @return string The URI path.
     */
    public function getPath(): string;

    /**
     * Retrieve the query string of the URI.
     *
     * If no query string is present, this method MUST return an empty string.
     *
     * The leading "?" character is not part of the query and MUST NOT be
     * added.
     *
     * The value returned MUST be percent-encoded, but MUST NOT double-encode
     * any characters. To determine what characters to encode, please refer to
     * RFC 3986, Sections 2 and 3.4.
     *
     * As an example, if a value in a key/value pair of the query string should
     * include an ampersand ("&") not intended as a delimiter between values,
     * that value MUST be passed in encoded form (e.g., "%26") to the instance.
     *
     * @see https://tools.ietf.org/html/rfc3986#section-2
     * @see https://tools.ietf.org/html/rfc3986#section-3.4
     * @return string The URI query string.
     */
    public function getQuery(): string;

    /**
     * Retrieve the fragment component of the URI.
     *
     * If no fragment is present, this method MUST return an empty string.
     *
     * The leading "#" character is not part of the fragment and MUST NOT be
     * added.
     *
     * The value returned MUST be percent-encoded, but MUST NOT double-encode
     * any characters. To determine what characters to encode, please refer to
     * RFC 3986, Sections 2 and 3.5.
     *
     * @see https://tools.ietf.org/html/rfc3986#section-2
     * @see https://tools.ietf.org/html/rfc3986#section-3.5
     * @return string The URI fragment.
     */
    public function getFragment(): string;

    /**
     * Return an instance with the specified scheme.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the specified scheme.
     *
     * Implementations MUST support the schemes "http" and "https" case
     * insensitively, and MAY accommodate other schemes if required.
     *
     * An empty scheme is equivalent to removing the scheme.
     *
     * @param string $scheme The scheme to use with the new instance.
     * @return static A new instance with the specified scheme.
     * @throws \InvalidArgumentException for invalid or unsupported schemes.
     */
    public function withScheme(string $scheme): UriInterface;

    /**
     * Return an instance with the specified user information.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the specified user information.
     *
     * Password is optional, but the user information MUST include the
     * user; an empty string for the user is equivalent to removing user
     * information.
     *
     * @param string $user The user name to use for authority.
     * @param null|string $password The password associated with $user.
     * @return static A new instance with the specified user information.
     */
    public function withUserInfo(string $user, ?string $password = null): UriInterface;

    /**
     * Return an instance with the specified host.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the specified host.
     *
     * An empty host value is equivalent to removing the host.
     *
     * @param string $host The hostname to use with the new instance.
     * @return static A new instance with the specified host.
     * @throws \InvalidArgumentException for invalid hostnames.
     */
    public function withHost(string $host): UriInterface;

    /**
     * Return an instance with the specified port.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the specified port.
     *
     * Implementations MUST raise an exception for ports outside the
     * established TCP and UDP port ranges.
     *
     * A null value provided for the port is equivalent to removing the port
     * information.
     *
     * @param null|int $port The port to use with the new instance; a null value
     *     removes the port information.
     * @return static A new instance with the specified port.
     * @throws \InvalidArgumentException for invalid ports.
     */
    public function withPort(?int $port): UriInterface;

    /**
     * Return an instance with the specified path.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the specified path.
     *
     * The path can either be empty or absolute (starting with a slash) or
     * rootless (not starting with a slash). Implementations MUST support all
     * three syntaxes.
     *
     * If the path is intended to be domain-relative rather than path relative then
     * it must begin with a slash ("/"). Paths not starting with a slash ("/")
     * are assumed to be relative to some base path known to the application or
     * consumer.
     *
     * Users can provide both encoded and decoded path characters.
     * Implementations ensure the correct encoding as outlined in getPath().
     *
     * @param string $path The path to use with the new instance.
     * @return static A new instance with the specified path.
     * @throws \InvalidArgumentException for invalid paths.
     */
    public function withPath(string $path): UriInterface;

    /**
     * Return an instance with the specified query string.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the specified query string.
     *
     * Users can provide both encoded and decoded query characters.
     * Implementations ensure the correct encoding as outlined in getQuery().
     *
     * An empty query string value is equivalent to removing the query string.
     *
     * @param string $query The query string to use with the new instance.
     * @return static A new instance with the specified query string.
     * @throws \InvalidArgumentException for invalid query strings.
     */
    public function withQuery(string $query): UriInterface;

    /**
     * Return an instance with the specified URI fragment.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the specified URI fragment.
     *
     * Users can provide both encoded and decoded fragment characters.
     * Implementations ensure the correct encoding as outlined in getFragment().
     *
     * An empty fragment value is equivalent to removing the fragment.
     *
     * @param string $fragment The fragment to use with the new instance.
     * @return static A new instance with the specified fragment.
     */
    public function withFragment(string $fragment): UriInterface;

    /**
     * Return the string representation as a URI reference.
     *
     * Depending on which components of the URI are present, the resulting
     * string is either a full URI or relative reference according to RFC 3986,
     * Section 4.1. The method concatenates the various components of the URI,
     * using the appropriate delimiters:
     *
     * - If a scheme is present, it MUST be suffixed by ":".
     * - If an authority is present, it MUST be prefixed by "//".
     * - The path can be concatenated without delimiters. But there are two
     *   cases where the path has to be adjusted to make the URI reference
     *   valid as PHP does not allow to throw an exception in __toString():
     *     - If the path is rootless and an authority is present, the path MUST
     *       be prefixed by "/".
     *     - If the path is starting with more than one "/" and no authority is
     *       present, the starting slashes MUST be reduced to one.
     * - If a query is present, it MUST be prefixed by "?".
     * - If a fragment is present, it MUST be prefixed by "#".
     *
     * @see http://tools.ietf.org/html/rfc3986#section-4.1
     * @return string
     */
    public function __toString(): string;
}
<?php

namespace Psr\Http\Message;

/**
 * Representation of an incoming, server-side HTTP request.
 *
 * Per the HTTP specification, this interface includes properties for
 * each of the following:
 *
 * - Protocol version
 * - HTTP method
 * - URI
 * - Headers
 * - Message body
 *
 * Additionally, it encapsulates all data as it has arrived to the
 * application from the CGI and/or PHP environment, including:
 *
 * - The values represented in $_SERVER.
 * - Any cookies provided (generally via $_COOKIE)
 * - Query string arguments (generally via $_GET, or as parsed via parse_str())
 * - Upload files, if any (as represented by $_FILES)
 * - Deserialized body parameters (generally from $_POST)
 *
 * $_SERVER values MUST be treated as immutable, as they represent application
 * state at the time of request; as such, no methods are provided to allow
 * modification of those values. The other values provide such methods, as they
 * can be restored from $_SERVER or the request body, and may need treatment
 * during the application (e.g., body parameters may be deserialized based on
 * content type).
 *
 * Additionally, this interface recognizes the utility of introspecting a
 * request to derive and match additional parameters (e.g., via URI path
 * matching, decrypting cookie values, deserializing non-form-encoded body
 * content, matching authorization headers to users, etc). These parameters
 * are stored in an "attributes" property.
 *
 * Requests are considered immutable; all methods that might change state MUST
 * be implemented such that they retain the internal state of the current
 * message and return an instance that contains the changed state.
 */
interface ServerRequestInterface extends RequestInterface
{
    /**
     * Retrieve server parameters.
     *
     * Retrieves data related to the incoming request environment,
     * typically derived from PHP's $_SERVER superglobal. The data IS NOT
     * REQUIRED to originate from $_SERVER.
     *
     * @return array
     */
    public function getServerParams(): array;

    /**
     * Retrieve cookies.
     *
     * Retrieves cookies sent by the client to the server.
     *
     * The data MUST be compatible with the structure of the $_COOKIE
     * superglobal.
     *
     * @return array
     */
    public function getCookieParams(): array;

    /**
     * Return an instance with the specified cookies.
     *
     * The data IS NOT REQUIRED to come from the $_COOKIE superglobal, but MUST
     * be compatible with the structure of $_COOKIE. Typically, this data will
     * be injected at instantiation.
     *
     * This method MUST NOT update the related Cookie header of the request
     * instance, nor related values in the server params.
     *
     * This method MUST be implemented in such a way as to retain the
     * immutability of the message, and MUST return an instance that has the
     * updated cookie values.
     *
     * @param array $cookies Array of key/value pairs representing cookies.
     * @return static
     */
    public function withCookieParams(array $cookies): ServerRequestInterface;

    /**
     * Retrieve query string arguments.
     *
     * Retrieves the deserialized query string arguments, if any.
     *
     * Note: the query params might not be in sync with the URI or server
     * params. If you need to ensure you are only getting the original
     * values, you may need to parse the query string from `getUri()->getQuery()`
     * or from the `QUERY_STRING` server param.
     *
     * @return array
     */
    public function getQueryParams(): array;

    /**
     * Return an instance with the specified query string arguments.
     *
     * These values SHOULD remain immutable over the course of the incoming
     * request. They MAY be injected during instantiation, such as from PHP's
     * $_GET superglobal, or MAY be derived from some other value such as the
     * URI. In cases where the arguments are parsed from the URI, the data
     * MUST be compatible with what PHP's parse_str() would return for
     * purposes of how duplicate query parameters are handled, and how nested
     * sets are handled.
     *
     * Setting query string arguments MUST NOT change the URI stored by the
     * request, nor the values in the server params.
     *
     * This method MUST be implemented in such a way as to retain the
     * immutability of the message, and MUST return an instance that has the
     * updated query string arguments.
     *
     * @param array $query Array of query string arguments, typically from
     *     $_GET.
     * @return static
     */
    public function withQueryParams(array $query): ServerRequestInterface;

    /**
     * Retrieve normalized file upload data.
     *
     * This method returns upload metadata in a normalized tree, with each leaf
     * an instance of Psr\Http\Message\UploadedFileInterface.
     *
     * These values MAY be prepared from $_FILES or the message body during
     * instantiation, or MAY be injected via withUploadedFiles().
     *
     * @return array An array tree of UploadedFileInterface instances; an empty
     *     array MUST be returned if no data is present.
     */
    public function getUploadedFiles(): array;

    /**
     * Create a new instance with the specified uploaded files.
     *
     * This method MUST be implemented in such a way as to retain the
     * immutability of the message, and MUST return an instance that has the
     * updated body parameters.
     *
     * @param array $uploadedFiles An array tree of UploadedFileInterface instances.
     * @return static
     * @throws \InvalidArgumentException if an invalid structure is provided.
     */
    public function withUploadedFiles(array $uploadedFiles): ServerRequestInterface;

    /**
     * Retrieve any parameters provided in the request body.
     *
     * If the request Content-Type is either application/x-www-form-urlencoded
     * or multipart/form-data, and the request method is POST, this method MUST
     * return the contents of $_POST.
     *
     * Otherwise, this method may return any results of deserializing
     * the request body content; as parsing returns structured content, the
     * potential types MUST be arrays or objects only. A null value indicates
     * the absence of body content.
     *
     * @return null|array|object The deserialized body parameters, if any.
     *     These will typically be an array or object.
     */
    public function getParsedBody();

    /**
     * Return an instance with the specified body parameters.
     *
     * These MAY be injected during instantiation.
     *
     * If the request Content-Type is either application/x-www-form-urlencoded
     * or multipart/form-data, and the request method is POST, use this method
     * ONLY to inject the contents of $_POST.
     *
     * The data IS NOT REQUIRED to come from $_POST, but MUST be the results of
     * deserializing the request body content. Deserialization/parsing returns
     * structured data, and, as such, this method ONLY accepts arrays or objects,
     * or a null value if nothing was available to parse.
     *
     * As an example, if content negotiation determines that the request data
     * is a JSON payload, this method could be used to create a request
     * instance with the deserialized parameters.
     *
     * This method MUST be implemented in such a way as to retain the
     * immutability of the message, and MUST return an instance that has the
     * updated body parameters.
     *
     * @param null|array|object $data The deserialized body data. This will
     *     typically be in an array or object.
     * @return static
     * @throws \InvalidArgumentException if an unsupported argument type is
     *     provided.
     */
    public function withParsedBody($data): ServerRequestInterface;

    /**
     * Retrieve attributes derived from the request.
     *
     * The request "attributes" may be used to allow injection of any
     * parameters derived from the request: e.g., the results of path
     * match operations; the results of decrypting cookies; the results of
     * deserializing non-form-encoded message bodies; etc. Attributes
     * will be application and request specific, and CAN be mutable.
     *
     * @return array Attributes derived from the request.
     */
    public function getAttributes(): array;

    /**
     * Retrieve a single derived request attribute.
     *
     * Retrieves a single derived request attribute as described in
     * getAttributes(). If the attribute has not been previously set, returns
     * the default value as provided.
     *
     * This method obviates the need for a hasAttribute() method, as it allows
     * specifying a default value to return if the attribute is not found.
     *
     * @see getAttributes()
     * @param string $name The attribute name.
     * @param mixed $default Default value to return if the attribute does not exist.
     * @return mixed
     */
    public function getAttribute(string $name, $default = null);

    /**
     * Return an instance with the specified derived request attribute.
     *
     * This method allows setting a single derived request attribute as
     * described in getAttributes().
     *
     * This method MUST be implemented in such a way as to retain the
     * immutability of the message, and MUST return an instance that has the
     * updated attribute.
     *
     * @see getAttributes()
     * @param string $name The attribute name.
     * @param mixed $value The value of the attribute.
     * @return static
     */
    public function withAttribute(string $name, $value): ServerRequestInterface;

    /**
     * Return an instance that removes the specified derived request attribute.
     *
     * This method allows removing a single derived request attribute as
     * described in getAttributes().
     *
     * This method MUST be implemented in such a way as to retain the
     * immutability of the message, and MUST return an instance that removes
     * the attribute.
     *
     * @see getAttributes()
     * @param string $name The attribute name.
     * @return static
     */
    public function withoutAttribute(string $name): ServerRequestInterface;
}
<?php

namespace Psr\Http\Message;

/**
 * Representation of an outgoing, client-side request.
 *
 * Per the HTTP specification, this interface includes properties for
 * each of the following:
 *
 * - Protocol version
 * - HTTP method
 * - URI
 * - Headers
 * - Message body
 *
 * During construction, implementations MUST attempt to set the Host header from
 * a provided URI if no Host header is provided.
 *
 * Requests are considered immutable; all methods that might change state MUST
 * be implemented such that they retain the internal state of the current
 * message and return an instance that contains the changed state.
 */
interface RequestInterface extends MessageInterface
{
    /**
     * Retrieves the message's request target.
     *
     * Retrieves the message's request-target either as it will appear (for
     * clients), as it appeared at request (for servers), or as it was
     * specified for the instance (see withRequestTarget()).
     *
     * In most cases, this will be the origin-form of the composed URI,
     * unless a value was provided to the concrete implementation (see
     * withRequestTarget() below).
     *
     * If no URI is available, and no request-target has been specifically
     * provided, this method MUST return the string "/".
     *
     * @return string
     */
    public function getRequestTarget(): string;

    /**
     * Return an instance with the specific request-target.
     *
     * If the request needs a non-origin-form request-target â€” e.g., for
     * specifying an absolute-form, authority-form, or asterisk-form â€”
     * this method may be used to create an instance with the specified
     * request-target, verbatim.
     *
     * This method MUST be implemented in such a way as to retain the
     * immutability of the message, and MUST return an instance that has the
     * changed request target.
     *
     * @link http://tools.ietf.org/html/rfc7230#section-5.3 (for the various
     *     request-target forms allowed in request messages)
     * @param string $requestTarget
     * @return static
     */
    public function withRequestTarget(string $requestTarget): RequestInterface;


    /**
     * Retrieves the HTTP method of the request.
     *
     * @return string Returns the request method.
     */
    public function getMethod(): string;

    /**
     * Return an instance with the provided HTTP method.
     *
     * While HTTP method names are typically all uppercase characters, HTTP
     * method names are case-sensitive and thus implementations SHOULD NOT
     * modify the given string.
     *
     * This method MUST be implemented in such a way as to retain the
     * immutability of the message, and MUST return an instance that has the
     * changed request method.
     *
     * @param string $method Case-sensitive method.
     * @return static
     * @throws \InvalidArgumentException for invalid HTTP methods.
     */
    public function withMethod(string $method): RequestInterface;

    /**
     * Retrieves the URI instance.
     *
     * This method MUST return a UriInterface instance.
     *
     * @link http://tools.ietf.org/html/rfc3986#section-4.3
     * @return UriInterface Returns a UriInterface instance
     *     representing the URI of the request.
     */
    public function getUri(): UriInterface;

    /**
     * Returns an instance with the provided URI.
     *
     * This method MUST update the Host header of the returned request by
     * default if the URI contains a host component. If the URI does not
     * contain a host component, any pre-existing Host header MUST be carried
     * over to the returned request.
     *
     * You can opt-in to preserving the original state of the Host header by
     * setting `$preserveHost` to `true`. When `$preserveHost` is set to
     * `true`, this method interacts with the Host header in the following ways:
     *
     * - If the Host header is missing or empty, and the new URI contains
     *   a host component, this method MUST update the Host header in the returned
     *   request.
     * - If the Host header is missing or empty, and the new URI does not contain a
     *   host component, this method MUST NOT update the Host header in the returned
     *   request.
     * - If a Host header is present and non-empty, this method MUST NOT update
     *   the Host header in the returned request.
     *
     * This method MUST be implemented in such a way as to retain the
     * immutability of the message, and MUST return an instance that has the
     * new UriInterface instance.
     *
     * @link http://tools.ietf.org/html/rfc3986#section-4.3
     * @param UriInterface $uri New request URI to use.
     * @param bool $preserveHost Preserve the original state of the Host header.
     * @return static
     */
    public function withUri(UriInterface $uri, bool $preserveHost = false): RequestInterface;
}
<?php

namespace Psr\Http\Message;

/**
 * Value object representing a file uploaded through an HTTP request.
 *
 * Instances of this interface are considered immutable; all methods that
 * might change state MUST be implemented such that they retain the internal
 * state of the current instance and return an instance that contains the
 * changed state.
 */
interface UploadedFileInterface
{
    /**
     * Retrieve a stream representing the uploaded file.
     *
     * This method MUST return a StreamInterface instance, representing the
     * uploaded file. The purpose of this method is to allow utilizing native PHP
     * stream functionality to manipulate the file upload, such as
     * stream_copy_to_stream() (though the result will need to be decorated in a
     * native PHP stream wrapper to work with such functions).
     *
     * If the moveTo() method has been called previously, this method MUST raise
     * an exception.
     *
     * @return StreamInterface Stream representation of the uploaded file.
     * @throws \RuntimeException in cases when no stream is available or can be
     *     created.
     */
    public function getStream(): StreamInterface;

    /**
     * Move the uploaded file to a new location.
     *
     * Use this method as an alternative to move_uploaded_file(). This method is
     * guaranteed to work in both SAPI and non-SAPI environments.
     * Implementations must determine which environment they are in, and use the
     * appropriate method (move_uploaded_file(), rename(), or a stream
     * operation) to perform the operation.
     *
     * $targetPath may be an absolute path, or a relative path. If it is a
     * relative path, resolution should be the same as used by PHP's rename()
     * function.
     *
     * The original file or stream MUST be removed on completion.
     *
     * If this method is called more than once, any subsequent calls MUST raise
     * an exception.
     *
     * When used in an SAPI environment where $_FILES is populated, when writing
     * files via moveTo(), is_uploaded_file() and move_uploaded_file() SHOULD be
     * used to ensure permissions and upload status are verified correctly.
     *
     * If you wish to move to a stream, use getStream(), as SAPI operations
     * cannot guarantee writing to stream destinations.
     *
     * @see http://php.net/is_uploaded_file
     * @see http://php.net/move_uploaded_file
     * @param string $targetPath Path to which to move the uploaded file.
     * @throws \InvalidArgumentException if the $targetPath specified is invalid.
     * @throws \RuntimeException on any error during the move operation, or on
     *     the second or subsequent call to the method.
     */
    public function moveTo(string $targetPath): void;
    
    /**
     * Retrieve the file size.
     *
     * Implementations SHOULD return the value stored in the "size" key of
     * the file in the $_FILES array if available, as PHP calculates this based
     * on the actual size transmitted.
     *
     * @return int|null The file size in bytes or null if unknown.
     */
    public function getSize(): ?int;
    
    /**
     * Retrieve the error associated with the uploaded file.
     *
     * The return value MUST be one of PHP's UPLOAD_ERR_XXX constants.
     *
     * If the file was uploaded successfully, this method MUST return
     * UPLOAD_ERR_OK.
     *
     * Implementations SHOULD return the value stored in the "error" key of
     * the file in the $_FILES array.
     *
     * @see http://php.net/manual/en/features.file-upload.errors.php
     * @return int One of PHP's UPLOAD_ERR_XXX constants.
     */
    public function getError(): int;
    
    /**
     * Retrieve the filename sent by the client.
     *
     * Do not trust the value returned by this method. A client could send
     * a malicious filename with the intention to corrupt or hack your
     * application.
     *
     * Implementations SHOULD return the value stored in the "name" key of
     * the file in the $_FILES array.
     *
     * @return string|null The filename sent by the client or null if none
     *     was provided.
     */
    public function getClientFilename(): ?string;
    
    /**
     * Retrieve the media type sent by the client.
     *
     * Do not trust the value returned by this method. A client could send
     * a malicious media type with the intention to corrupt or hack your
     * application.
     *
     * Implementations SHOULD return the value stored in the "type" key of
     * the file in the $_FILES array.
     *
     * @return string|null The media type sent by the client or null if none
     *     was provided.
     */
    public function getClientMediaType(): ?string;
}
{
    "name": "psr/http-message",
    "description": "Common interface for HTTP messages",
    "keywords": ["psr", "psr-7", "http", "http-message", "request", "response"],
    "homepage": "https://github.com/php-fig/http-message",
    "license": "MIT",
    "authors": [
        {
            "name": "PHP-FIG",
            "homepage": "https://www.php-fig.org/"
        }
    ],
    "require": {
        "php": "^7.2 || ^8.0"
    },
    "autoload": {
        "psr-4": {
            "Psr\\Http\\Message\\": "src/"
        }
    },
    "extra": {
        "branch-alias": {
            "dev-master": "2.0.x-dev"
        }
    }
}
{
    "name": "phpseclib/phpseclib",
    "type": "library",
    "description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.",
    "keywords": [
        "security",
        "crypto",
        "cryptography",
        "encryption",
        "signature",
        "signing",
        "rsa",
        "aes",
        "blowfish",
        "twofish",
        "ssh",
        "sftp",
        "x509",
        "x.509",
        "asn1",
        "asn.1",
        "BigInteger"
    ],
    "homepage": "https://phpseclib.com/",
    "license": "MIT",
    "authors": [
        {
            "name": "Jim Wigginton",
            "email": "terrafrost@php.net",
            "role": "Lead Developer"
        },
        {
            "name": "Patrick Monnerat",
            "email": "pm@datasphere.ch",
            "role": "Developer"
        },
        {
            "name": "Andreas Fischer",
            "email": "bantu@phpbb.com",
            "role": "Developer"
        },
        {
            "name": "Hans-JÃ¼rgen Petrich",
            "email": "petrich@tronic-media.com",
            "role": "Developer"
        },
        {
            "name": "Graham Campbell",
            "email": "graham@alt-three.com",
            "role": "Developer"
        },
        {
            "name": "Jack Worman",
            "email": "jack.worman@gmail.com",
            "role": "Developer",
            "homepage": "https://jackworman.com"
        }
    ],
    "require": {
        "php": ">=8.1",
        "paragonie/constant_time_encoding": "^2|^3",
        "symfony/polyfill-php82": "^1.26"
    },
    "require-dev": {
        "ext-xml": "*",
        "brianium/paratest": "^7.22",
        "phpunit/phpunit": "^13",
        "php-parallel-lint/php-parallel-lint": "^1.3",
        "squizlabs/php_codesniffer": "^3.7",
        "vimeo/psalm": "*"
    },
    "suggest": {
        "ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.",
        "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations.",
        "ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.",
        "ext-dom": "Install the DOM extension to load XML formatted public keys."
    },
    "autoload": {
        "files": [
            "phpseclib/bootstrap.php"
        ],
        "psr-4": {
            "phpseclib4\\": "phpseclib/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "phpseclib4\\Tests\\": "tests/"
        }
    },
    "config": {
        "sort-packages": true
    },
    "scripts": {
        "lint": "vendor/bin/parallel-lint --show-deprecated build phpseclib tests",
        "php_codesniffer": "vendor/bin/phpcs --standard=build/php_codesniffer.xml",
        "php_codesniffer-fix": "vendor/bin/phpcbf --standard=build/php_codesniffer.xml",
        "psalm": "vendor/bin/psalm --config=build/psalm.xml --no-cache --long-progress --threads=4",
        "psalm-set-baseline": "vendor/bin/psalm --config=build/psalm.xml --no-cache --long-progress --set-baseline=psalm_baseline.xml --threads=4",
        "test": "vendor/bin/paratest --verbose --configuration=tests/phpunit.xml --runner=WrapperRunner",
        "all-quality-tools": [
            "@lint",
            "@phpcs",
            "@psalm",
            "@test"
        ]
    }
}
<?php

declare(strict_types=1);

/**
 * Bootstrapping File for phpseclib
 *
 * composer isn't a requirement for phpseclib 2.0 but this file isn't really required
 * either. it's a bonus for those using composer but if you're not phpseclib will
 * still work
 *
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 */

if (extension_loaded('mbstring')) {
    // 2 - MB_OVERLOAD_STRING
    // mbstring.func_overload is deprecated in php 7.2 and removed in php 8.0.
    if (version_compare(PHP_VERSION, '8.0.0') < 0 && ini_get('mbstring.func_overload') & 2) {
        throw new UnexpectedValueException(
            'Overloading of string functions using mbstring.func_overload ' .
            'is not supported by phpseclib.'
        );
    }
}
<?php

/**
 * PKCS#8 Formatted DH Key Handler
 *
 * PHP version 8.1+
 *
 * Processes keys with the following headers:
 *
 * -----BEGIN ENCRYPTED PRIVATE KEY-----
 * -----BEGIN PRIVATE KEY-----
 * -----BEGIN PUBLIC KEY-----
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\DH\Formats\Keys;

use phpseclib4\Crypt\Common\Formats\Keys\PKCS8 as Progenitor;
use phpseclib4\Exception\{InvalidArgumentException, UnexpectedValueException};
use phpseclib4\File\ASN1;
use phpseclib4\File\ASN1\Maps;
use phpseclib4\Math\BigInteger;

/**
 * PKCS#8 Formatted DH Key Handler
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class PKCS8 extends Progenitor
{
    /**
     * OID Name
     *
     * @var string
     */
    public const OID_NAME = 'dhKeyAgreement';

    /**
     * OID Value
     *
     * @var string
     */
    public const OID_VALUE = '1.2.840.113549.1.3.1';

    /**
     * Break a public or private key down into its constituent components
     */
    public static function load(
        #[\SensitiveParameter] string $key,
        #[\SensitiveParameter] ?string $password = null
    ): array {
        $isPublic = str_contains($key, 'PUBLIC');

        $key = parent::load($key, $password);

        $type = isset($key['privateKey']) ? 'privateKey' : 'publicKey';

        switch (true) {
            case !$isPublic && $type == 'publicKey':
                throw new UnexpectedValueException('Human readable string claims non-public key but DER encoded string claims public key');
            case $isPublic && $type == 'privateKey':
                throw new UnexpectedValueException('Human readable string claims public key but DER encoded string claims private key');
        }

        $decoded = ASN1::decodeBER($key[$type . 'Algorithm']['parameters']->value);
        $components = ASN1::map($decoded, Maps\DHParameter::MAP)->toArray();

        $decoded = ASN1::decodeBER((string) $key[$type]);
        if (!$decoded['content'] instanceof BigInteger) {
            throw new UnexpectedValueException('Unable to decode BER of parameters');
        }
        $components[$type] = $decoded['content'];

        return $components;
    }

    /**
     * Convert a private key to the appropriate format.
     *
     * @psalm-suppress PossiblyUnusedParam
     */
    public static function savePrivateKey(
        BigInteger $prime,
        BigInteger $base,
        BigInteger $privateKey,
        BigInteger $publicKey,
        #[\SensitiveParameter] ?string $password = null,
        array $options = []
    ): string {
        $params = [
            'prime' => $prime,
            'base' => $base,
        ];
        $params = ASN1::encodeDER($params, Maps\DHParameter::MAP);
        $params = new ASN1\Element($params);
        $key = ASN1::encodeDER($privateKey, ['type' => ASN1::TYPE_INTEGER]);
        return self::wrapPrivateKey(
            key: $key,
            params: $params,
            password: $password,
            options: $options
        );
    }

    /**
     * Convert a public key to the appropriate format
     */
    public static function savePublicKey(BigInteger $prime, BigInteger $base, BigInteger $publicKey, array $options = []): string
    {
        $params = [
            'prime' => $prime,
            'base' => $base,
        ];
        $params = ASN1::encodeDER($params, Maps\DHParameter::MAP);
        $params = new ASN1\Element($params);
        $key = ASN1::encodeDER($publicKey, ['type' => ASN1::TYPE_INTEGER]);
        return self::wrapPublicKey(
            key: $key,
            params: $params,
            options: $options
        );
    }
}
<?php

/**
 * "PKCS1" Formatted EC Key Handler
 *
 * PHP version 8.1+
 *
 * Processes keys with the following headers:
 *
 * -----BEGIN DH PARAMETERS-----
 *
 * Technically, PKCS1 is for RSA keys, only, but we're using PKCS1 to describe
 * DSA, whose format isn't really formally described anywhere, so might as well
 * use it to describe this, too.
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2019-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\DH\Formats\Keys;

use phpseclib4\Crypt\Common\Formats\Keys\PKCS1 as Progenitor;
use phpseclib4\File\ASN1;
use phpseclib4\File\ASN1\Maps;
use phpseclib4\Math\BigInteger;

/**
 * "PKCS1" Formatted DH Key Handler
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class PKCS1 extends Progenitor
{
    /**
     * Break a public or private key down into its constituent components
     */
    public static function load(
        #[\SensitiveParameter] string $key,
        #[\SensitiveParameter] ?string $password = null
    ): array {
        $key = parent::loadHelper($key, $password);

        $decoded = ASN1::decodeBER($key);

        return ASN1::map($decoded, Maps\DHParameter::MAP)->toArray();
    }

    /**
     * Convert EC parameters to the appropriate format
     */
    public static function saveParameters(BigInteger $prime, BigInteger $base): string
    {
        $params = [
            'prime' => $prime,
            'base' => $base,
        ];
        $params = ASN1::encodeDER($params, Maps\DHParameter::MAP);

        return "-----BEGIN DH PARAMETERS-----\r\n" .
               chunk_split(base64_encode($params), 64) .
               "-----END DH PARAMETERS-----\r\n";
    }
}
<?php

/**
 * DH Public Key
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2019-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\DH;

use phpseclib4\Crypt\{Common, DH};
use phpseclib4\Math\BigInteger;

/**
 * DH Public Key
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
final class PublicKey extends DH
{
    use Common\Traits\Fingerprint;

    /**
     * Returns the public key
     */
    public function toString(string $type, array $options = []): string
    {
        $type = self::validatePlugin('Keys', $type, 'savePublicKey');

        return $type::savePublicKey($this->prime, $this->base, $this->publicKey, $options);
    }

    /**
     * Returns the public key as a BigInteger
     */
    public function toBigInteger(): BigInteger
    {
        return $this->publicKey;
    }
}
<?php

/**
 * DH Parameters
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2019-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\DH;

use phpseclib4\Crypt\DH;

/**
 * DH Parameters
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
final class Parameters extends DH
{
    /**
     * Returns the parameters
     */
    public function toString(string $type = 'PKCS1', array $options = []): string
    {
        $type = self::validatePlugin('Keys', 'PKCS1', 'saveParameters');

        return $type::saveParameters($this->prime, $this->base, $options);
    }
}
<?php

/**
 * DH Private Key
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2019-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\DH;

use phpseclib4\Crypt\{Common, DH};
use phpseclib4\Math\BigInteger;

/**
 * DH Private Key
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
final class PrivateKey extends DH
{
    use Common\Traits\PasswordProtected;

    /**
     * Private Key
     */
    protected BigInteger $privateKey;

    /**
     * Returns the public key
     */
    public function getPublicKey(): PublicKey
    {
        $type = self::validatePlugin('Keys', 'PKCS8', 'savePublicKey');

        $this->publicKey ??= $this->base->powMod($this->privateKey, $this->prime);

        $key = $type::savePublicKey($this->prime, $this->base, $this->publicKey);

        return DH::loadFormat('PKCS8', $key);
    }

    /**
     * Returns the private key as a string
     */
    public function toString(string $type, array $options = []): string
    {
        $type = self::validatePlugin('Keys', $type, 'savePrivateKey');

        $this->publicKey ??= $this->base->powMod($this->privateKey, $this->prime);

        return $type::savePrivateKey($this->prime, $this->base, $this->privateKey, $this->publicKey, $this->password, $options);
    }
}
<?php

/**
 * Pure-PHP implementation of RC4.
 *
 * Uses OpenSSL, if available/possible, and an internal implementation, otherwise
 *
 * PHP version 8.1+
 *
 * Useful resources are as follows:
 *
 *  - {@link http://www.mozilla.org/projects/security/pki/nss/draft-kaukonen-cipher-arcfour-03.txt ARCFOUR Algorithm}
 *  - {@link http://en.wikipedia.org/wiki/RC4 - Wikipedia: RC4}
 *
 * RC4 is also known as ARCFOUR or ARC4.  The reason is elaborated upon at Wikipedia.  This class is named RC4 and not
 * ARCFOUR or ARC4 because RC4 is how it is referred to in the SSH1 specification.
 *
 * Here's a short example of how to use this library:
 * <code>
 * <?php
 *    include 'vendor/autoload.php';
 *
 *    $rc4 = new \phpseclib4\Crypt\RC4();
 *
 *    $rc4->setKey('abcdefgh');
 *
 *    $size = 10 * 1024;
 *    $plaintext = '';
 *    for ($i = 0; $i < $size; $i++) {
 *        $plaintext.= 'a';
 *    }
 *
 *    echo $rc4->decrypt($rc4->encrypt($plaintext));
 * ?>
 * </code>
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2007-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt;

use phpseclib4\Crypt\Common\StreamCipher;
use phpseclib4\Exception\LengthException;

/**
 * Pure-PHP implementation of RC4.
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class RC4 extends StreamCipher
{
    /**
     * @see \phpseclib4\Crypt\RC4::_crypt()
     */
    public const ENCRYPT = 0;

    /**
     * @see \phpseclib4\Crypt\RC4::_crypt()
     */
    public const DECRYPT = 1;

    /**
     * Key Length (in bytes)
     *
     * @see \phpseclib4\Crypt\RC4::setKeyLength(
     */
    protected int $key_length = 128; // = 1024 bits

    /**
     * The Key Stream for decryption and encryption
     *
     * @see self::setKey()
     */
    private array $stream;

    /**
     * Test for engine validity
     *
     * This is mainly just a wrapper to set things up for \phpseclib4\Crypt\Common\SymmetricKey::isValidEngine()
     *
     * @see Common\SymmetricKey::__construct()
     */
    protected function isValidEngineHelper(int $engine): bool
    {
        if ($engine == self::ENGINE_OPENSSL) {
            if ($this->continuousBuffer) {
                return false;
            }
            // quoting https://www.openssl.org/news/openssl-3.0-notes.html, OpenSSL 3.0.1
            // "Moved all variations of the EVP ciphers CAST5, BF, IDEA, SEED, RC2, RC4, RC5, and DES to the legacy provider"
            // in theory openssl_get_cipher_methods() should catch this but, on GitHub Actions, at least, it does not
            if (defined('OPENSSL_VERSION_NUMBER') && OPENSSL_VERSION_NUMBER >= 0x30000010) {
                return false;
            }
            $this->cipher_name_openssl = 'rc4-40';
        }

        return parent::isValidEngineHelper($engine);
    }

    /**
     * Sets the key length
     *
     * Keys can be between 1 and 256 bytes long.
     *
     * @throws LengthException if the key length is invalid
     */
    public function setKeyLength(int $length): void
    {
        if ($length < 8 || $length > 2048) {
            throw new LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys between 1 and 256 bytes are supported');
        }

        $this->key_length = $length >> 3;

        parent::setKeyLength($length);
    }

    /**
     * Sets the key length
     *
     * Keys can be between 1 and 256 bytes long.
     */
    public function setKey(#[\SensitiveParameter] string $key): void
    {
        $length = strlen($key);
        if ($length < 1 || $length > 256) {
            throw new LengthException('Key size of ' . $length . ' bytes is not supported by RC4. Keys must be between 1 and 256 bytes long');
        }

        parent::setKey($key);
    }

    /**
     * Encrypts a message.
     *
     * @return string $ciphertext
     * @see Common\SymmetricKey::decrypt()
     * @see self::crypt()
     */
    public function encrypt(#[\SensitiveParameter] string $plaintext): string
    {
        if ($this->engine != self::ENGINE_INTERNAL) {
            return parent::encrypt($plaintext);
        }
        return $this->crypt($plaintext, self::ENCRYPT);
    }

    /**
     * Decrypts a message.
     *
     * $this->decrypt($this->encrypt($plaintext)) == $this->encrypt($this->encrypt($plaintext)).
     * At least if the continuous buffer is disabled.
     *
     * @return string $plaintext
     * @see Common\SymmetricKey::encrypt()
     * @see self::crypt()
     */
    public function decrypt(string $ciphertext): string
    {
        if ($this->engine != self::ENGINE_INTERNAL) {
            return parent::decrypt($ciphertext);
        }
        return $this->crypt($ciphertext, self::DECRYPT);
    }

    /**
     * Encrypts a block
     */
    protected function encryptBlock(string $in): string
    {
        // RC4 does not utilize this method
        return '';
    }

    /**
     * Decrypts a block
     */
    protected function decryptBlock(string $in): string
    {
        // RC4 does not utilize this method
        return '';
    }

    /**
     * Setup the key (expansion)
     *
     * @see Common\SymmetricKey::_setupKey()
     */
    protected function setupKey(): void
    {
        $key = $this->key;
        $keyLength = strlen($key);
        $keyStream = range(0, 255);
        $j = 0;
        for ($i = 0; $i < 256; $i++) {
            $j = ($j + $keyStream[$i] + ord($key[$i % $keyLength])) & 255;
            $temp = $keyStream[$i];
            $keyStream[$i] = $keyStream[$j];
            $keyStream[$j] = $temp;
        }

        $this->stream = [];
        $this->stream[self::DECRYPT] = $this->stream[self::ENCRYPT] = [
            0, // index $i
            0, // index $j
            $keyStream,
        ];
    }

    /**
     * Encrypts or decrypts a message.
     *
     * @return string $text
     * @see self::decrypt()
     * @see self::encrypt()
     */
    private function crypt(string $text, int $mode): string
    {
        if ($this->changed) {
            $this->setup();
        }

        $stream = &$this->stream[$mode];
        if ($this->continuousBuffer) {
            $i = &$stream[0];
            $j = &$stream[1];
            $keyStream = &$stream[2];
        } else {
            $i = $stream[0];
            $j = $stream[1];
            $keyStream = $stream[2];
        }

        $len = strlen($text);
        for ($k = 0; $k < $len; ++$k) {
            $i = ($i + 1) & 255;
            $ksi = $keyStream[$i];
            $j = ($j + $ksi) & 255;
            $ksj = $keyStream[$j];

            $keyStream[$i] = $ksj;
            $keyStream[$j] = $ksi;
            $text[$k] = $text[$k] ^ chr($keyStream[($ksj + $ksi) & 255]);
        }

        return $text;
    }
}
<?php

/**
 * Pure-PHP implementation of Salsa20.
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2019-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt;

use phpseclib4\Common\Functions\Strings;
use phpseclib4\Crypt\Common\StreamCipher;
use phpseclib4\Exception\{BadDecryptionException, InvalidStateException, LengthException};

/**
 * Pure-PHP implementation of Salsa20.
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class Salsa20 extends StreamCipher
{
    /**
     * Part 1 of the state
     */
    protected ?string $p1 = null;

    /**
     * Part 2 of the state
     */
    protected ?string $p2 = null;

    /**
     * Key Length (in bytes)
     */
    protected int $key_length = 32; // = 256 bits

    /**
     * @see \phpseclib4\Crypt\Salsa20::crypt()
     */
    public const ENCRYPT = 0;

    /**
     * @see \phpseclib4\Crypt\Salsa20::crypt()
     */
    public const DECRYPT = 1;

    /**
     * Encryption buffer for continuous mode
     */
    protected array $enbuffer;

    /**
     * Decryption buffer for continuous mode
     */
    protected array $debuffer;

    /**
     * Counter
     */
    protected int $counter = 0;

    /**
     * Using Generated Poly1305 Key
     */
    protected bool $usingGeneratedPoly1305Key = false;

    /**
     * Salsa20 uses a nonce
     */
    public function usesNonce(): bool
    {
        return true;
    }

    /**
     * Sets the key.
     */
    public function setKey(#[\SensitiveParameter] string $key): void
    {
        switch (strlen($key)) {
            case 16:
            case 32:
                break;
            default:
                throw new LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of sizes 16 or 32 are supported');
        }

        parent::setKey($key);
    }

    /**
     * Sets the nonce.
     */
    public function setNonce(string $nonce): void
    {
        if (strlen($nonce) != 8) {
            throw new LengthException('Nonce of size ' . strlen($nonce) . ' not supported by this algorithm. Only an 64-bit nonce is supported');
        }

        $this->nonce = $nonce;
        $this->changed = true;
        $this->setEngine();
    }

    /**
     * Sets the counter.
     */
    public function setCounter(int $counter): void
    {
        $this->counter = $counter;
        $this->setEngine();
    }

    /**
     * Creates a Poly1305 key using the method discussed in RFC8439
     *
     * See https://tools.ietf.org/html/rfc8439#section-2.6.1
     */
    protected function createPoly1305Key(): void
    {
        if (!isset($this->nonce)) {
            throw new InvalidStateException('No nonce has been defined - call setNonce() first');
        }

        if (!isset($this->key)) {
            throw new InvalidStateException('No key has been defined - call setKey() first');
        }

        $c = clone $this;
        $c->setCounter(0);
        $c->usePoly1305 = false;
        $block = $c->encrypt(str_repeat("\0", 256));
        $this->setPoly1305Key(substr($block, 0, 32));

        if ($this->counter == 0) {
            $this->counter++;
        }

        $this->usingGeneratedPoly1305Key = true;
    }

    /**
     * Setup the self::ENGINE_INTERNAL $engine
     *
     * (re)init, if necessary, the internal cipher $engine
     *
     * _setup() will be called each time if $changed === true
     * typically this happens when using one or more of following public methods:
     *
     * - setKey()
     *
     * - setNonce()
     *
     * - First run of encrypt() / decrypt() with no init-settings
     *
     * @see self::setKey()
     * @see self::setNonce()
     * @see self::disableContinuousBuffer()
     */
    protected function setup(): void
    {
        if (!$this->changed) {
            return;
        }

        $this->enbuffer = $this->debuffer = ['ciphertext' => '', 'counter' => $this->counter];

        $this->changed = $this->nonIVChanged = false;

        if (!isset($this->nonce)) {
            throw new InvalidStateException('No nonce has been defined - call setNonce() first');
        }

        if (!isset($this->key)) {
            throw new InvalidStateException('No key has been defined - call setKey() first');
        }

        if ($this->usePoly1305 && !isset($this->poly1305Key)) {
            $this->usingGeneratedPoly1305Key = true;
            $this->createPoly1305Key();
        }

        $key = $this->key;
        if (strlen($key) == 16) {
            $constant = 'expand 16-byte k';
            $key .= $key;
        } else {
            $constant = 'expand 32-byte k';
        }

        $this->p1 = substr($constant, 0, 4) .
                    substr($key, 0, 16) .
                    substr($constant, 4, 4) .
                    $this->nonce .
                    "\0\0\0\0";
        $this->p2 = substr($constant, 8, 4) .
                    substr($key, 16, 16) .
                    substr($constant, 12, 4);
    }

    /**
     * Setup the key (expansion)
     */
    protected function setupKey(): void
    {
        // Salsa20 does not utilize this method
    }

    /**
     * Encrypts a message.
     *
     * @return string $ciphertext
     * @see \phpseclib4\Crypt\Common\SymmetricKey::decrypt()
     * @see self::crypt()
     */
    public function encrypt(#[\SensitiveParameter] string $plaintext): string
    {
        $ciphertext = $this->crypt($plaintext, self::ENCRYPT);
        if (isset($this->poly1305Key)) {
            $this->newtag = $this->poly1305($ciphertext);
        }
        return $ciphertext;
    }

    /**
     * Decrypts a message.
     *
     * $this->decrypt($this->encrypt($plaintext)) == $this->encrypt($this->encrypt($plaintext)).
     * At least if the continuous buffer is disabled.
     *
     * @return string $plaintext
     * @see \phpseclib4\Crypt\Common\SymmetricKey::encrypt()
     * @see self::crypt()
     */
    public function decrypt(string $ciphertext): string
    {
        if (isset($this->poly1305Key)) {
            if (!isset($this->oldtag)) {
                throw new InvalidStateException('Authentication Tag has not been set - call setTag() first');
            }
            $newtag = $this->poly1305($ciphertext);
            if ($this->oldtag != substr($newtag, 0, strlen($this->oldtag))) {
                $this->oldtag = null;
                throw new BadDecryptionException('Derived authentication tag and supplied authentication tag do not match');
            }
            $this->oldtag = null;
        }

        return $this->crypt($ciphertext, self::DECRYPT);
    }

    /**
     * Encrypts a block
     */
    protected function encryptBlock(string $in): string
    {
        // Salsa20 does not utilize this method
        return '';
    }

    /**
     * Decrypts a block
     */
    protected function decryptBlock(string $in): string
    {
        // Salsa20 does not utilize this method
        return '';
    }

    /**
     * Encrypts or decrypts a message.
     *
     * @see self::decrypt()
     * @see self::encrypt()
     */
    private function crypt(string $text, int $mode): string
    {
        $this->setup();
        if (!$this->continuousBuffer) {
            if ($this->engine == self::ENGINE_OPENSSL) {
                $iv = pack('V', $this->counter) . $this->p2;
                return openssl_encrypt(
                    $text,
                    $this->cipher_name_openssl,
                    $this->key,
                    OPENSSL_RAW_DATA,
                    $iv
                );
            }
            $i = $this->counter;
            $blocks = str_split($text, 64);
            foreach ($blocks as &$block) {
                $block ^= static::salsa20($this->p1 . pack('V', $i++) . $this->p2);
            }
            unset($block);
            return implode('', $blocks);
        }

        if ($mode == self::ENCRYPT) {
            $buffer = &$this->enbuffer;
        } else {
            $buffer = &$this->debuffer;
        }
        if (!strlen($buffer['ciphertext'])) {
            $ciphertext = '';
        } else {
            $ciphertext = $text ^ Strings::shift($buffer['ciphertext'], strlen($text));
            $text = substr($text, strlen($ciphertext));
            if (!strlen($text)) {
                return $ciphertext;
            }
        }

        $overflow = strlen($text) % 64; // & 0x3F
        if ($overflow) {
            $text2 = Strings::pop($text, $overflow);
            if ($this->engine == self::ENGINE_OPENSSL) {
                $iv = pack('V', $buffer['counter']) . $this->p2;
                // at this point $text should be a multiple of 64
                $buffer['counter'] += (strlen($text) >> 6) + 1; // ie. divide by 64
                $encrypted = openssl_encrypt(
                    $text . str_repeat("\0", 64),
                    $this->cipher_name_openssl,
                    $this->key,
                    OPENSSL_RAW_DATA,
                    $iv
                );
                $temp = Strings::pop($encrypted, 64);
            } else {
                $blocks = str_split($text, 64);
                if (strlen($text)) {
                    foreach ($blocks as &$block) {
                        $block ^= static::salsa20($this->p1 . pack('V', $buffer['counter']++) . $this->p2);
                    }
                    unset($block);
                }
                $encrypted = implode('', $blocks);
                $temp = static::salsa20($this->p1 . pack('V', $buffer['counter']++) . $this->p2);
            }
            $ciphertext .= $encrypted . ($text2 ^ $temp);
            $buffer['ciphertext'] = substr($temp, $overflow);
        } elseif (!strlen($buffer['ciphertext'])) {
            if ($this->engine == self::ENGINE_OPENSSL) {
                $iv = pack('V', $buffer['counter']) . $this->p2;
                $buffer['counter'] += (strlen($text) >> 6);
                $ciphertext .= openssl_encrypt(
                    $text,
                    $this->cipher_name_openssl,
                    $this->key,
                    OPENSSL_RAW_DATA,
                    $iv
                );
            } else {
                $blocks = str_split($text, 64);
                foreach ($blocks as &$block) {
                    $block ^= static::salsa20($this->p1 . pack('V', $buffer['counter']++) . $this->p2);
                }
                unset($block);
                $ciphertext .= implode('', $blocks);
            }
        }

        return $ciphertext;
    }

    /**
     * Left Rotate
     */
    protected static function leftRotate(int $x, int $n): int
    {
        if (PHP_INT_SIZE == 8) {
            $r1 = $x << $n;
            $r1 &= 0xFFFFFFFF;
            $r2 = ($x & 0xFFFFFFFF) >> (32 - $n);
        } else {
            $x = self::safe_intval($x);
            $r1 = $x << $n;
            $r2 = $x >> (32 - $n);
            $r2 &= (1 << $n) - 1;
        }
        return $r1 | $r2;
    }

    /**
     * The quarterround function
     */
    protected static function quarterRound(int &$a, int &$b, int &$c, int &$d): void
    {
        $b ^= self::leftRotate($a + $d, 7);
        $c ^= self::leftRotate($b + $a, 9);
        $d ^= self::leftRotate($c + $b, 13);
        $a ^= self::leftRotate($d + $c, 18);
    }

    /**
     * The doubleround function
     */
    protected static function doubleRound(int &$x0, int &$x1, int &$x2, int &$x3, int &$x4, int &$x5, int &$x6, int &$x7, int &$x8, int &$x9, int &$x10, int &$x11, int &$x12, int &$x13, int &$x14, int &$x15): void
    {
        // columnRound
        static::quarterRound($x0, $x4, $x8, $x12);
        static::quarterRound($x5, $x9, $x13, $x1);
        static::quarterRound($x10, $x14, $x2, $x6);
        static::quarterRound($x15, $x3, $x7, $x11);
        // rowRound
        static::quarterRound($x0, $x1, $x2, $x3);
        static::quarterRound($x5, $x6, $x7, $x4);
        static::quarterRound($x10, $x11, $x8, $x9);
        static::quarterRound($x15, $x12, $x13, $x14);
    }

    /**
     * The Salsa20 hash function function
     */
    protected static function salsa20(string $x): string
    {
        $z = $x = unpack('V*', $x);
        for ($i = 0; $i < 10; $i++) {
            static::doubleRound($z[1], $z[2], $z[3], $z[4], $z[5], $z[6], $z[7], $z[8], $z[9], $z[10], $z[11], $z[12], $z[13], $z[14], $z[15], $z[16]);
        }

        for ($i = 1; $i <= 16; $i++) {
            $x[$i] = self::safe_intval($x[$i] + $z[$i]);
        }

        return pack('V*', ...$x);
    }

    /**
     * Calculates Poly1305 MAC
     *
     * @see self::decrypt()
     * @see self::encrypt()
     */
    protected function poly1305(string $text): string
    {
        if (!$this->usingGeneratedPoly1305Key) {
            return parent::poly1305($this->aad . $text);
        } else {
            /*
            sodium_crypto_aead_chacha20poly1305_encrypt does not calculate the poly1305 tag
            the same way sodium_crypto_aead_chacha20poly1305_ietf_encrypt does. you can see
            how the latter encrypts it in Salsa20::encrypt(). here's how the former encrypts
            it:

            $this->newtag = $this->poly1305(
                $this->aad .
                pack('V', strlen($this->aad)) . "\0\0\0\0" .
                $ciphertext .
                pack('V', strlen($ciphertext)) . "\0\0\0\0"
            );

            phpseclib opts to use the IETF construction, even when the nonce is 64-bits
            instead of 96-bits
            */
            return parent::poly1305(
                self::nullPad128($this->aad) .
                self::nullPad128($text) .
                pack('V', strlen($this->aad)) . "\0\0\0\0" .
                pack('V', strlen($text)) . "\0\0\0\0"
            );
        }
    }
}
<?php

/**
 * PublicKeyLoader
 *
 * Returns a PublicKey or PrivateKey object.
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2019-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt;

use phpseclib4\Crypt\Common\{AsymmetricKey, PrivateKey, PublicKey};
use phpseclib4\Exception\NoKeyLoadedException;
use phpseclib4\File\{ASN1, X509};

/**
 * PublicKeyLoader
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class PublicKeyLoader
{
    /**
     * Loads a public or private key
     *
     * @throws NoKeyLoadedException if key is not valid
     */
    public static function load(
        #[\SensitiveParameter] string|array $key,
        #[\SensitiveParameter] ?string $password = null
    ): AsymmetricKey {
        // use ASN1::EXCEPTIONS_EVERY_TIME here because without it a valid RSAPublicKey
        // will be recognized as an invalid RSAPrivateKey
        $reenable = ASN1::isBlobsOnBadDecodesEnabled();
        ASN1::disableBlobsOnBadDecodes();
        try {
            $key = EC::load($key, $password);
            if ($reenable) {
                ASN1::enableBlobsOnBadDecodes();
            }
            return $key;
        } catch (NoKeyLoadedException) {
        }

        try {
            $key = RSA::load($key, $password);
            if ($reenable) {
                ASN1::enableBlobsOnBadDecodes();
            }
            return $key;
        } catch (NoKeyLoadedException) {
        }

        try {
            $key = DSA::load($key, $password);
            if ($reenable) {
                ASN1::enableBlobsOnBadDecodes();
            }
            return $key;
        } catch (NoKeyLoadedException) {
        }

        try {
            $key = X509::load($key)->getPublicKey();
            if ($reenable) {
                ASN1::enableBlobsOnBadDecodes();
            }
            return $key;
        } catch (\Exception) {
        }

        if ($reenable) {
            ASN1::enableBlobsOnBadDecodes();
        }

        throw new NoKeyLoadedException('Unable to read key');
    }

    /**
     * Loads a private key
     */
    public static function loadPrivateKey(
        #[\SensitiveParameter] string|array $key,
        #[\SensitiveParameter] ?string $password = null
    ): PrivateKey {
        $key = self::load($key, $password);
        if (!$key instanceof PrivateKey) {
            throw new NoKeyLoadedException('The key that was loaded was not a private key');
        }
        return $key;
    }

    /**
     * Loads a public key
     */
    public static function loadPublicKey(string|array $key): PublicKey
    {
        $key = self::load($key);
        if (!$key instanceof PublicKey) {
            throw new NoKeyLoadedException('The key that was loaded was not a public key');
        }
        return $key;
    }

    /**
     * Loads parameters
     */
    public static function loadParameters(string $key): AsymmetricKey
    {
        $key = self::load($key);
        if ($key instanceof PrivateKey || $key instanceof PublicKey) {
            throw new NoKeyLoadedException('The key that was loaded was not a parameter');
        }
        return $key;
    }
}
<?php

/**
 * Pure-PHP FIPS 186-4 compliant implementation of DSA.
 *
 * PHP version 8.1+
 *
 * Here's an example of how to create signatures and verify signatures with this library:
 * <code>
 * <?php
 * include 'vendor/autoload.php';
 *
 * $private = \phpseclib4\Crypt\DSA::createKey();
 * $public = $private->getPublicKey();
 *
 * $plaintext = 'terrafrost';
 *
 * $signature = $private->sign($plaintext);
 *
 * echo $public->verify($plaintext, $signature) ? 'verified' : 'unverified';
 * ?>
 * </code>
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt;

use phpseclib4\Crypt\Common\AsymmetricKey;
use phpseclib4\Crypt\DSA\{Parameters, PrivateKey, PublicKey};
use phpseclib4\Exception\{BadConfigurationException, BadMethodCallException, InvalidArgumentException};
use phpseclib4\Math\BigInteger;

/**
 * Pure-PHP FIPS 186-4 compliant implementation of DSA.
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class DSA extends AsymmetricKey
{
    /**
     * Algorithm Name
     *
     * @var string
     */
    public const ALGORITHM = 'DSA';

    /**
     * DSA Prime P
     */
    protected BigInteger $p;

    /**
     * DSA Group Order q
     *
     * Prime divisor of p-1
     */
    protected BigInteger $q;

    /**
     * DSA Group Generator G
     */
    protected BigInteger $g;

    /**
     * DSA public key value y
     */
    protected BigInteger $y;

    /**
     * Signature Format
     */
    protected string $sigFormat;

    /**
     * Forced Engine
     *
     * @see parent::forceEngine()
     */
    protected static ?string $forcedEngine = null;

    /**
     * Signature Format (Short)
     */
    protected string $shortFormat = 'ASN1';

    /**
     * Create DSA parameters
     */
    public static function createParameters(int $L = 2048, int $N = 224): Parameters
    {
        self::initialize_static_variables();

        $class = new \ReflectionClass(static::class);
        if ($class->isFinal()) {
            throw new BadMethodCallException('createParameters() should not be called from final classes (' . static::class . ')');
        }

        switch (true) {
            case $N == 160:
            /*
              in FIPS 186-1 and 186-2 N was fixed at 160 whereas K had an upper bound of 1024.
              RFC 4253 (SSH Transport Layer Protocol) references FIPS 186-2 and as such most
              SSH DSA implementations only support keys with an N of 160.
              puttygen let's you set the size of L (but not the size of N) and uses 2048 as the
              default L value. that's not really compliant with any of the FIPS standards, however,
              for the purposes of maintaining compatibility with puttygen, we'll support it
            */
            //case ($L >= 512 || $L <= 1024) && (($L & 0x3F) == 0) && $N == 160:
            // FIPS 186-3 changed this as follows:
            //case $L == 1024 && $N == 160:
            case $L == 2048 && $N == 224:
            case $L == 2048 && $N == 256:
            case $L == 3072 && $N == 256:
                break;
            default:
                throw new InvalidArgumentException('Invalid values for N and L');
        }

        $two = new BigInteger(2);

        $q = BigInteger::randomPrime($N);
        $divisor = $q->multiply($two);

        do {
            $x = BigInteger::random($L);
            [, $c] = $x->divide($divisor);
            $p = $x->subtract($c->subtract(self::$one));
        } while ($p->getLength() != $L || !$p->isPrime());

        $p_1 = $p->subtract(self::$one);
        [$e] = $p_1->divide($q);

        // quoting http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf#page=50 ,
        // "h could be obtained from a random number generator or from a counter that
        //  changes after each use". PuTTY (sshdssg.c) starts h off at 1 and increments
        // it on each loop. wikipedia says "commonly h = 2 is used" so we'll just do that
        $h = clone $two;
        while (true) {
            $g = $h->powMod($e, $p);
            if (!$g->equals(self::$one)) {
                break;
            }
            $h = $h->add(self::$one);
        }

        $dsa = new Parameters();
        $dsa->p = $p;
        $dsa->q = $q;
        $dsa->g = $g;

        return $dsa;
    }

    /**
     * Create public / private key pair.
     *
     * This method is a bit polymorphic. It can take a DSA/Parameters object, L / N as two distinct parameters or
     * no parameters (at which point L and N will be generated with this method)
     *
     * Returns the private key, from which the publickey can be extracted
     */
    public static function createKey(int|Parameters ...$args): PrivateKey
    {
        self::initialize_static_variables();

        if (self::$forcedEngine == 'libsodium') {
            throw new BadConfigurationException('Engine ' . self::$forcedEngine . ' is forced but unsupported for DSA');
        }

        if (self::$forcedEngine == 'OpenSSL' && !defined('OPENSSL_KEYTYPE_DSA')) {
            throw new BadConfigurationException("Engine OpenSSL is forced but unsupported for DSA");
        }

        $class = new \ReflectionClass(static::class);
        if ($class->isFinal()) {
            throw new BadMethodCallException('createKey() should not be called from final classes (' . static::class . ')');
        }

        if (count($args) == 2 && is_int($args[0]) && is_int($args[1])) {
            $params = self::createParameters($args[0], $args[1]);
        } elseif (count($args) == 1 && $args[0] instanceof Parameters) {
            $params = $args[0];
        } elseif (!count($args)) {
            $params = self::createParameters();
        } else {
            throw new InvalidArgumentException('Valid parameters are either two integers (L and N), a single DSA object or no parameters at all.');
        }

        // at this point the only two supported values for self::$forcedEngine are OpenSSL, PHP and null
        // if it's either OpenSSL or null we'll use OpenSSL (if it's available)
        if (self::$forcedEngine !== 'PHP' && defined('OPENSSL_KEYTYPE_DSA')) {
            $config = [];
            if (self::$configFile) {
                $config['config'] = self::$configFile;
            }
            $dsa = openssl_pkey_new($config + [
                'private_key_type' => OPENSSL_KEYTYPE_DSA,
                'p' => $params->p,
                'q' => $params->q,
                'g' => $params->g,
            ]);
            if ($dsa && openssl_pkey_export($dsa, $privatekeystr, null, $config)) {
                // clear the buffer of error strings stemming from a minimalistic openssl.cnf
                // https://github.com/php/php-src/issues/11054 talks about other errors this'll pick up
                while (openssl_error_string() !== false) {
                }

                return DSA::load($privatekeystr)
                    ->withHash($params->hash->getHash())
                    ->withSignatureFormat($params->shortFormat);
            } elseif (isset(self::$forcedEngine)) {
                throw new BadConfigurationException('Engine OpenSSL is forced but unsupported for DSA');
            }
        }

        $private = new PrivateKey();
        $private->p = $params->p;
        $private->q = $params->q;
        $private->g = $params->g;

        $private->x = BigInteger::randomRange(self::$one, $private->q->subtract(self::$one));
        $private->y = $private->g->powMod($private->x, $private->p);

        //$public = clone $private;
        //unset($public->x);

        return $private
            ->withHash($params->hash->getHash())
            ->withSignatureFormat($params->shortFormat);
    }

    /**
     * OnLoad Handler
     */
    protected static function onLoad(array $components): Parameters|PrivateKey|PublicKey
    {
        if (!isset($components['x']) && !isset($components['y'])) {
            $new = new Parameters();
        } elseif (isset($components['x'])) {
            $new = new PrivateKey();
            $new->x = $components['x'];
        } else {
            $new = new PublicKey();
        }

        $new->p = $components['p'];
        $new->q = $components['q'];
        $new->g = $components['g'];

        if (isset($components['y'])) {
            $new->y = $components['y'];
        }

        return $new;
    }

    /**
     * Constructor
     *
     * PublicKey and PrivateKey objects can only be created from abstract RSA class
     */
    protected function __construct()
    {
        $this->sigFormat = self::validatePlugin('Signature', 'ASN1');

        parent::__construct();
    }

    /**
     * Returns the key size
     *
     * More specifically, this L (the length of DSA Prime P) and N (the length of DSA Group Order q)
     */
    public function getLength(): array
    {
        return ['L' => $this->p->getLength(), 'N' => $this->q->getLength()];
    }

    /**
     * Returns the parameters
     *
     * A public / private key is only returned if the currently loaded "key" contains an x or y
     * value.
     *
     * @see self::getPublicKey()
     */
    public function getParameters(): Parameters
    {
        $type = self::validatePlugin('Keys', 'PKCS1', 'saveParameters');

        $key = $type::saveParameters($this->p, $this->q, $this->g);
        return DSA::load($key, 'PKCS1')
            ->withHash($this->hash->getHash())
            ->withSignatureFormat($this->shortFormat);
    }

    /**
     * Determines the signature padding mode
     *
     * Valid values are: ASN1, SSH2, Raw
     */
    public function withSignatureFormat(string $format): static
    {
        $new = clone $this;
        $new->shortFormat = $format;
        $new->sigFormat = self::validatePlugin('Signature', $format);
        return $new;
    }

    /**
     * Returns the signature format currently being used
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function getSignatureFormat(): string
    {
        return $this->shortFormat;
    }
}
<?php

/**
 * Pure-PHP implementation of Triple DES.
 *
 * Uses OpenSSL, if available/possible, and an internal implementation, otherwise.
 *
 * Operates in the EDE3 mode (encrypt-decrypt-encrypt).
 *
 * PHP version 8.1+
 *
 * Here's a short example of how to use this library:
 * <code>
 * <?php
 *    include 'vendor/autoload.php';
 *
 *    $des = new \phpseclib4\Crypt\TripleDES('ctr');
 *
 *    $des->setKey('abcdefghijklmnopqrstuvwx');
 *
 *    $size = 10 * 1024;
 *    $plaintext = '';
 *    for ($i = 0; $i < $size; $i++) {
 *        $plaintext.= 'a';
 *    }
 *
 *    echo $des->decrypt($des->encrypt($plaintext));
 * ?>
 * </code>
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2007-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt;

use phpseclib4\Exception\LengthException;

/**
 * Pure-PHP implementation of Triple DES.
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class TripleDES extends DES
{
    /**
     * Encrypt / decrypt using inner chaining
     *
     * Inner chaining is used by SSH-1 and is generally considered to be less secure then outer chaining (self::MODE_CBC3).
     */
    public const MODE_3CBC = -2;

    /**
     * Encrypt / decrypt using outer chaining
     *
     * Outer chaining is used by SSH-2 and when the mode is set to \phpseclib4\Crypt\Common\BlockCipher::MODE_CBC.
     */
    public const MODE_CBC3 = self::MODE_CBC;

    /**
     * Key Length (in bytes)
     *
     * @see \phpseclib4\Crypt\TripleDES::setKeyLength()
     */
    protected int $key_length = 24;

    /**
     * max possible size of $key
     *
     * @see self::setKey()
     * @see DES::setKey()
     */
    protected int $key_length_max = 24;

    /**
     * Internal flag whether using self::MODE_3CBC or not
     */
    private bool $mode_3cbc = false;

    /**
     * The \phpseclib4\Crypt\DES objects
     *
     * Used only if $mode_3cbc === true
     */
    private array $des;

    /**
     * Default Constructor.
     *
     * $mode could be:
     *
     * - ecb
     *
     * - cbc
     *
     * - ctr
     *
     * - cfb
     *
     * - ofb
     *
     * - 3cbc
     *
     * - cbc3 (same as cbc)
     *
     * @see Common\SymmetricKey::__construct()
     * @see Crypt\DES::__construct()
     */
    public function __construct(string $mode)
    {
        switch (strtolower($mode)) {
            // In case of self::MODE_3CBC, we init as CRYPT_DES_MODE_CBC
            // and additional flag us internally as 3CBC
            case '3cbc':
                parent::__construct('cbc');
                $this->mode_3cbc = true;

                // This three $des'es will do the 3CBC work (if $key > 64bits)
                $this->des = [
                    new DES('cbc'),
                    new DES('cbc'),
                    new DES('cbc'),
                ];

                // we're going to be doing the padding, ourselves, so disable it in the \phpseclib4\Crypt\DES objects
                $this->des[0]->disablePadding();
                $this->des[1]->disablePadding();
                $this->des[2]->disablePadding();
                break;
            case 'cbc3':
                $mode = 'cbc';
                // no break
            // If not 3CBC, we init as usual
            default:
                parent::__construct($mode);
        }
    }

    /**
     * Test for engine validity
     *
     * This is mainly just a wrapper to set things up for \phpseclib4\Crypt\Common\SymmetricKey::isValidEngine()
     *
     * @see Common\SymmetricKey::__construct()
     */
    protected function isValidEngineHelper(int $engine): bool
    {
        if ($engine == self::ENGINE_OPENSSL) {
            $this->cipher_name_openssl_ecb = 'des-ede3';
            $mode = $this->openssl_translate_mode();
            $this->cipher_name_openssl = $mode == 'ecb' ? 'des-ede3' : 'des-ede3-' . $mode;
        }

        return parent::isValidEngineHelper($engine);
    }

    /**
     * Sets the initialization vector.
     *
     * SetIV is not required when \phpseclib4\Crypt\Common\SymmetricKey::MODE_ECB is being used.
     *
     * @see Common\SymmetricKey::setIV()
     */
    public function setIV(string $iv): void
    {
        parent::setIV($iv);
        if ($this->mode_3cbc) {
            $this->des[0]->setIV($iv);
            $this->des[1]->setIV($iv);
            $this->des[2]->setIV($iv);
        }
    }

    /**
     * Sets the key length.
     *
     * Valid key lengths are 128 and 192 bits.
     *
     * If you want to use a 64-bit key use DES.php
     *
     * @see Common\SymmetricKey:setKeyLength()
     */
    public function setKeyLength(int $length): void
    {
        switch ($length) {
            case 128:
            case 192:
                break;
            default:
                throw new LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys of sizes 128 or 192 bits are supported');
        }

        parent::setKeyLength($length);
    }

    /**
     * Sets the key.
     *
     * Triple DES can use 128-bit (eg. strlen($key) == 16) or 192-bit (eg. strlen($key) == 24) keys.
     *
     * DES also requires that every eighth bit be a parity bit, however, we'll ignore that.
     *
     * @see DES::setKey()
     * @see Common\SymmetricKey::setKey()
     */
    public function setKey(#[\SensitiveParameter] string $key): void
    {
        if (isset($this->explicit_key_length) && strlen($key) != $this->explicit_key_length) {
            throw new LengthException('Key length has already been set to ' . $this->explicit_key_length . ' bytes and this key is ' . strlen($key) . ' bytes');
        }

        switch (strlen($key)) {
            case 16:
                $key .= substr($key, 0, 8);
                break;
            case 24:
                break;
            default:
                throw new LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of sizes 16 or 24 are supported');
        }

        // copied from self::setKey()
        $this->key = $key;
        $this->key_length = strlen($key);
        $this->changed = $this->nonIVChanged = true;
        $this->setEngine();

        if ($this->mode_3cbc) {
            $this->des[0]->setKey(substr($key, 0, 8));
            $this->des[1]->setKey(substr($key, 8, 8));
            $this->des[2]->setKey(substr($key, 16, 8));
        }
    }

    /**
     * Encrypts a message.
     *
     * @see Common\SymmetricKey::encrypt()
     */
    public function encrypt(string $plaintext): string
    {
        // parent::en/decrypt() is able to do all the work for all modes and keylengths,
        // except for: self::MODE_3CBC (inner chaining CBC) with a key > 64bits

        // if the key is smaller then 8, do what we'd normally do
        if ($this->mode_3cbc && strlen($this->key) > 8) {
            return $this->des[2]->encrypt(
                $this->des[1]->decrypt(
                    $this->des[0]->encrypt(
                        $this->pad($plaintext)
                    )
                )
            );
        }

        return parent::encrypt($plaintext);
    }

    /**
     * Decrypts a message.
     *
     * @see Common\SymmetricKey::decrypt()
     */
    public function decrypt(string $ciphertext): string
    {
        if ($this->mode_3cbc && strlen($this->key) > 8) {
            return $this->unpad(
                $this->des[0]->decrypt(
                    $this->des[1]->encrypt(
                        $this->des[2]->decrypt(
                            str_pad($ciphertext, (strlen($ciphertext) + 7) & 0xFFFFFFF8, "\0")
                        )
                    )
                )
            );
        }

        return parent::decrypt($ciphertext);
    }

    /**
     * Treat consecutive "packets" as if they are a continuous buffer.
     *
     * Say you have a 16-byte plaintext $plaintext.  Using the default behavior, the two following code snippets
     * will yield different outputs:
     *
     * <code>
     *    echo $des->encrypt(substr($plaintext, 0, 8));
     *    echo $des->encrypt(substr($plaintext, 8, 8));
     * </code>
     * <code>
     *    echo $des->encrypt($plaintext);
     * </code>
     *
     * The solution is to enable the continuous buffer.  Although this will resolve the above discrepancy, it creates
     * another, as demonstrated with the following:
     *
     * <code>
     *    $des->encrypt(substr($plaintext, 0, 8));
     *    echo $des->decrypt($des->encrypt(substr($plaintext, 8, 8)));
     * </code>
     * <code>
     *    echo $des->decrypt($des->encrypt(substr($plaintext, 8, 8)));
     * </code>
     *
     * With the continuous buffer disabled, these would yield the same output.  With it enabled, they yield different
     * outputs.  The reason is due to the fact that the initialization vector's change after every encryption /
     * decryption round when the continuous buffer is enabled.  When it's disabled, they remain constant.
     *
     * Put another way, when the continuous buffer is enabled, the state of the \phpseclib4\Crypt\DES() object changes after each
     * encryption / decryption round, whereas otherwise, it'd remain constant.  For this reason, it's recommended that
     * continuous buffers not be used.  They do offer better security and are, in fact, sometimes required (SSH uses them),
     * however, they are also less intuitive and more likely to cause you problems.
     *
     * @see Common\SymmetricKey::enableContinuousBuffer()
     * @see self::disableContinuousBuffer()
     */
    public function enableContinuousBuffer(): void
    {
        parent::enableContinuousBuffer();
        if ($this->mode_3cbc) {
            $this->des[0]->enableContinuousBuffer();
            $this->des[1]->enableContinuousBuffer();
            $this->des[2]->enableContinuousBuffer();
        }
    }

    /**
     * Treat consecutive packets as if they are a discontinuous buffer.
     *
     * The default behavior.
     *
     * @see Common\SymmetricKey::disableContinuousBuffer()
     * @see self::enableContinuousBuffer()
     */
    public function disableContinuousBuffer(): void
    {
        parent::disableContinuousBuffer();
        if ($this->mode_3cbc) {
            $this->des[0]->disableContinuousBuffer();
            $this->des[1]->disableContinuousBuffer();
            $this->des[2]->disableContinuousBuffer();
        }
    }

    /**
     * Creates the key schedule
     *
     * @see DES::setupKey()
     * @see Common\SymmetricKey::setupKey()
     */
    protected function setupKey(): void
    {
        switch (true) {
            // if $key <= 64bits we configure our internal pure-php cipher engine
            // to act as regular [1]DES, not as 3DES. mcrypt.so::tripledes does the same.
            case strlen($this->key) <= 8:
                $this->des_rounds = 1;
                break;
            // otherwise, if $key > 64bits, we configure our engine to work as 3DES.
            default:
                $this->des_rounds = 3;

                // (only) if 3CBC is used we have, of course, to setup the $des[0-2] keys also separately.
                if ($this->mode_3cbc) {
                    $this->des[0]->setupKey();
                    $this->des[1]->setupKey();
                    $this->des[2]->setupKey();

                    // because $des[0-2] will, now, do all the work we can return here
                    // not need unnecessary stress parent::setupKey() with our, now unused, $key.
                    return;
                }
        }
        // setup our key
        parent::setupKey();
    }

    /**
     * Sets the internal crypt engine
     *
     * @see Common\SymmetricKey::setPreferredEngine()
     * @see Common\SymmetricKey::__construct()
     */
    public function setPreferredEngine(string $engine): void
    {
        if ($this->mode_3cbc) {
            $this->des[0]->setPreferredEngine($engine);
            $this->des[1]->setPreferredEngine($engine);
            $this->des[2]->setPreferredEngine($engine);
        }

        parent::setPreferredEngine($engine);
    }
}
<?php

/**
 * Pure-PHP implementation of Twofish.
 *
 * Uses an internal implementation.
 *
 * PHP version 8.1+
 *
 * Useful resources are as follows:
 *
 *  - {@link http://en.wikipedia.org/wiki/Twofish Wikipedia description of Twofish}
 *
 * Here's a short example of how to use this library:
 * <code>
 * <?php
 *    include 'vendor/autoload.php';
 *
 *    $twofish = new \phpseclib4\Crypt\Twofish('ctr');
 *
 *    $twofish->setKey('12345678901234567890123456789012');
 *
 *    $plaintext = str_repeat('a', 1024);
 *
 *    echo $twofish->decrypt($twofish->encrypt($plaintext));
 * ?>
 * </code>
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @author    Hans-Juergen Petrich <petrich@tronic-media.com>
 * @copyright 2013-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt;

use phpseclib4\Crypt\Common\BlockCipher;
use phpseclib4\Exception\LengthException;

/**
 * Pure-PHP implementation of Twofish.
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @author  Hans-Juergen Petrich <petrich@tronic-media.com>
 */
class Twofish extends BlockCipher
{
    /**
     * Q-Table
     *
     * @var int[]
     */
    private static array $q0 = [
        0xA9, 0x67, 0xB3, 0xE8, 0x04, 0xFD, 0xA3, 0x76,
        0x9A, 0x92, 0x80, 0x78, 0xE4, 0xDD, 0xD1, 0x38,
        0x0D, 0xC6, 0x35, 0x98, 0x18, 0xF7, 0xEC, 0x6C,
        0x43, 0x75, 0x37, 0x26, 0xFA, 0x13, 0x94, 0x48,
        0xF2, 0xD0, 0x8B, 0x30, 0x84, 0x54, 0xDF, 0x23,
        0x19, 0x5B, 0x3D, 0x59, 0xF3, 0xAE, 0xA2, 0x82,
        0x63, 0x01, 0x83, 0x2E, 0xD9, 0x51, 0x9B, 0x7C,
        0xA6, 0xEB, 0xA5, 0xBE, 0x16, 0x0C, 0xE3, 0x61,
        0xC0, 0x8C, 0x3A, 0xF5, 0x73, 0x2C, 0x25, 0x0B,
        0xBB, 0x4E, 0x89, 0x6B, 0x53, 0x6A, 0xB4, 0xF1,
        0xE1, 0xE6, 0xBD, 0x45, 0xE2, 0xF4, 0xB6, 0x66,
        0xCC, 0x95, 0x03, 0x56, 0xD4, 0x1C, 0x1E, 0xD7,
        0xFB, 0xC3, 0x8E, 0xB5, 0xE9, 0xCF, 0xBF, 0xBA,
        0xEA, 0x77, 0x39, 0xAF, 0x33, 0xC9, 0x62, 0x71,
        0x81, 0x79, 0x09, 0xAD, 0x24, 0xCD, 0xF9, 0xD8,
        0xE5, 0xC5, 0xB9, 0x4D, 0x44, 0x08, 0x86, 0xE7,
        0xA1, 0x1D, 0xAA, 0xED, 0x06, 0x70, 0xB2, 0xD2,
        0x41, 0x7B, 0xA0, 0x11, 0x31, 0xC2, 0x27, 0x90,
        0x20, 0xF6, 0x60, 0xFF, 0x96, 0x5C, 0xB1, 0xAB,
        0x9E, 0x9C, 0x52, 0x1B, 0x5F, 0x93, 0x0A, 0xEF,
        0x91, 0x85, 0x49, 0xEE, 0x2D, 0x4F, 0x8F, 0x3B,
        0x47, 0x87, 0x6D, 0x46, 0xD6, 0x3E, 0x69, 0x64,
        0x2A, 0xCE, 0xCB, 0x2F, 0xFC, 0x97, 0x05, 0x7A,
        0xAC, 0x7F, 0xD5, 0x1A, 0x4B, 0x0E, 0xA7, 0x5A,
        0x28, 0x14, 0x3F, 0x29, 0x88, 0x3C, 0x4C, 0x02,
        0xB8, 0xDA, 0xB0, 0x17, 0x55, 0x1F, 0x8A, 0x7D,
        0x57, 0xC7, 0x8D, 0x74, 0xB7, 0xC4, 0x9F, 0x72,
        0x7E, 0x15, 0x22, 0x12, 0x58, 0x07, 0x99, 0x34,
        0x6E, 0x50, 0xDE, 0x68, 0x65, 0xBC, 0xDB, 0xF8,
        0xC8, 0xA8, 0x2B, 0x40, 0xDC, 0xFE, 0x32, 0xA4,
        0xCA, 0x10, 0x21, 0xF0, 0xD3, 0x5D, 0x0F, 0x00,
        0x6F, 0x9D, 0x36, 0x42, 0x4A, 0x5E, 0xC1, 0xE0,
    ];

    /**
     * Q-Table
     *
     * @var int[]
     */
    private static array $q1 = [
        0x75, 0xF3, 0xC6, 0xF4, 0xDB, 0x7B, 0xFB, 0xC8,
        0x4A, 0xD3, 0xE6, 0x6B, 0x45, 0x7D, 0xE8, 0x4B,
        0xD6, 0x32, 0xD8, 0xFD, 0x37, 0x71, 0xF1, 0xE1,
        0x30, 0x0F, 0xF8, 0x1B, 0x87, 0xFA, 0x06, 0x3F,
        0x5E, 0xBA, 0xAE, 0x5B, 0x8A, 0x00, 0xBC, 0x9D,
        0x6D, 0xC1, 0xB1, 0x0E, 0x80, 0x5D, 0xD2, 0xD5,
        0xA0, 0x84, 0x07, 0x14, 0xB5, 0x90, 0x2C, 0xA3,
        0xB2, 0x73, 0x4C, 0x54, 0x92, 0x74, 0x36, 0x51,
        0x38, 0xB0, 0xBD, 0x5A, 0xFC, 0x60, 0x62, 0x96,
        0x6C, 0x42, 0xF7, 0x10, 0x7C, 0x28, 0x27, 0x8C,
        0x13, 0x95, 0x9C, 0xC7, 0x24, 0x46, 0x3B, 0x70,
        0xCA, 0xE3, 0x85, 0xCB, 0x11, 0xD0, 0x93, 0xB8,
        0xA6, 0x83, 0x20, 0xFF, 0x9F, 0x77, 0xC3, 0xCC,
        0x03, 0x6F, 0x08, 0xBF, 0x40, 0xE7, 0x2B, 0xE2,
        0x79, 0x0C, 0xAA, 0x82, 0x41, 0x3A, 0xEA, 0xB9,
        0xE4, 0x9A, 0xA4, 0x97, 0x7E, 0xDA, 0x7A, 0x17,
        0x66, 0x94, 0xA1, 0x1D, 0x3D, 0xF0, 0xDE, 0xB3,
        0x0B, 0x72, 0xA7, 0x1C, 0xEF, 0xD1, 0x53, 0x3E,
        0x8F, 0x33, 0x26, 0x5F, 0xEC, 0x76, 0x2A, 0x49,
        0x81, 0x88, 0xEE, 0x21, 0xC4, 0x1A, 0xEB, 0xD9,
        0xC5, 0x39, 0x99, 0xCD, 0xAD, 0x31, 0x8B, 0x01,
        0x18, 0x23, 0xDD, 0x1F, 0x4E, 0x2D, 0xF9, 0x48,
        0x4F, 0xF2, 0x65, 0x8E, 0x78, 0x5C, 0x58, 0x19,
        0x8D, 0xE5, 0x98, 0x57, 0x67, 0x7F, 0x05, 0x64,
        0xAF, 0x63, 0xB6, 0xFE, 0xF5, 0xB7, 0x3C, 0xA5,
        0xCE, 0xE9, 0x68, 0x44, 0xE0, 0x4D, 0x43, 0x69,
        0x29, 0x2E, 0xAC, 0x15, 0x59, 0xA8, 0x0A, 0x9E,
        0x6E, 0x47, 0xDF, 0x34, 0x35, 0x6A, 0xCF, 0xDC,
        0x22, 0xC9, 0xC0, 0x9B, 0x89, 0xD4, 0xED, 0xAB,
        0x12, 0xA2, 0x0D, 0x52, 0xBB, 0x02, 0x2F, 0xA9,
        0xD7, 0x61, 0x1E, 0xB4, 0x50, 0x04, 0xF6, 0xC2,
        0x16, 0x25, 0x86, 0x56, 0x55, 0x09, 0xBE, 0x91,
    ];

    /**
     * M-Table
     *
     * @var int[]
     */
    private static array $m0 = [
        0xBCBC3275, 0xECEC21F3, 0x202043C6, 0xB3B3C9F4, 0xDADA03DB, 0x02028B7B, 0xE2E22BFB, 0x9E9EFAC8,
        0xC9C9EC4A, 0xD4D409D3, 0x18186BE6, 0x1E1E9F6B, 0x98980E45, 0xB2B2387D, 0xA6A6D2E8, 0x2626B74B,
        0x3C3C57D6, 0x93938A32, 0x8282EED8, 0x525298FD, 0x7B7BD437, 0xBBBB3771, 0x5B5B97F1, 0x474783E1,
        0x24243C30, 0x5151E20F, 0xBABAC6F8, 0x4A4AF31B, 0xBFBF4887, 0x0D0D70FA, 0xB0B0B306, 0x7575DE3F,
        0xD2D2FD5E, 0x7D7D20BA, 0x666631AE, 0x3A3AA35B, 0x59591C8A, 0x00000000, 0xCDCD93BC, 0x1A1AE09D,
        0xAEAE2C6D, 0x7F7FABC1, 0x2B2BC7B1, 0xBEBEB90E, 0xE0E0A080, 0x8A8A105D, 0x3B3B52D2, 0x6464BAD5,
        0xD8D888A0, 0xE7E7A584, 0x5F5FE807, 0x1B1B1114, 0x2C2CC2B5, 0xFCFCB490, 0x3131272C, 0x808065A3,
        0x73732AB2, 0x0C0C8173, 0x79795F4C, 0x6B6B4154, 0x4B4B0292, 0x53536974, 0x94948F36, 0x83831F51,
        0x2A2A3638, 0xC4C49CB0, 0x2222C8BD, 0xD5D5F85A, 0xBDBDC3FC, 0x48487860, 0xFFFFCE62, 0x4C4C0796,
        0x4141776C, 0xC7C7E642, 0xEBEB24F7, 0x1C1C1410, 0x5D5D637C, 0x36362228, 0x6767C027, 0xE9E9AF8C,
        0x4444F913, 0x1414EA95, 0xF5F5BB9C, 0xCFCF18C7, 0x3F3F2D24, 0xC0C0E346, 0x7272DB3B, 0x54546C70,
        0x29294CCA, 0xF0F035E3, 0x0808FE85, 0xC6C617CB, 0xF3F34F11, 0x8C8CE4D0, 0xA4A45993, 0xCACA96B8,
        0x68683BA6, 0xB8B84D83, 0x38382820, 0xE5E52EFF, 0xADAD569F, 0x0B0B8477, 0xC8C81DC3, 0x9999FFCC,
        0x5858ED03, 0x19199A6F, 0x0E0E0A08, 0x95957EBF, 0x70705040, 0xF7F730E7, 0x6E6ECF2B, 0x1F1F6EE2,
        0xB5B53D79, 0x09090F0C, 0x616134AA, 0x57571682, 0x9F9F0B41, 0x9D9D803A, 0x111164EA, 0x2525CDB9,
        0xAFAFDDE4, 0x4545089A, 0xDFDF8DA4, 0xA3A35C97, 0xEAEAD57E, 0x353558DA, 0xEDEDD07A, 0x4343FC17,
        0xF8F8CB66, 0xFBFBB194, 0x3737D3A1, 0xFAFA401D, 0xC2C2683D, 0xB4B4CCF0, 0x32325DDE, 0x9C9C71B3,
        0x5656E70B, 0xE3E3DA72, 0x878760A7, 0x15151B1C, 0xF9F93AEF, 0x6363BFD1, 0x3434A953, 0x9A9A853E,
        0xB1B1428F, 0x7C7CD133, 0x88889B26, 0x3D3DA65F, 0xA1A1D7EC, 0xE4E4DF76, 0x8181942A, 0x91910149,
        0x0F0FFB81, 0xEEEEAA88, 0x161661EE, 0xD7D77321, 0x9797F5C4, 0xA5A5A81A, 0xFEFE3FEB, 0x6D6DB5D9,
        0x7878AEC5, 0xC5C56D39, 0x1D1DE599, 0x7676A4CD, 0x3E3EDCAD, 0xCBCB6731, 0xB6B6478B, 0xEFEF5B01,
        0x12121E18, 0x6060C523, 0x6A6AB0DD, 0x4D4DF61F, 0xCECEE94E, 0xDEDE7C2D, 0x55559DF9, 0x7E7E5A48,
        0x2121B24F, 0x03037AF2, 0xA0A02665, 0x5E5E198E, 0x5A5A6678, 0x65654B5C, 0x62624E58, 0xFDFD4519,
        0x0606F48D, 0x404086E5, 0xF2F2BE98, 0x3333AC57, 0x17179067, 0x05058E7F, 0xE8E85E05, 0x4F4F7D64,
        0x89896AAF, 0x10109563, 0x74742FB6, 0x0A0A75FE, 0x5C5C92F5, 0x9B9B74B7, 0x2D2D333C, 0x3030D6A5,
        0x2E2E49CE, 0x494989E9, 0x46467268, 0x77775544, 0xA8A8D8E0, 0x9696044D, 0x2828BD43, 0xA9A92969,
        0xD9D97929, 0x8686912E, 0xD1D187AC, 0xF4F44A15, 0x8D8D1559, 0xD6D682A8, 0xB9B9BC0A, 0x42420D9E,
        0xF6F6C16E, 0x2F2FB847, 0xDDDD06DF, 0x23233934, 0xCCCC6235, 0xF1F1C46A, 0xC1C112CF, 0x8585EBDC,
        0x8F8F9E22, 0x7171A1C9, 0x9090F0C0, 0xAAAA539B, 0x0101F189, 0x8B8BE1D4, 0x4E4E8CED, 0x8E8E6FAB,
        0xABABA212, 0x6F6F3EA2, 0xE6E6540D, 0xDBDBF252, 0x92927BBB, 0xB7B7B602, 0x6969CA2F, 0x3939D9A9,
        0xD3D30CD7, 0xA7A72361, 0xA2A2AD1E, 0xC3C399B4, 0x6C6C4450, 0x07070504, 0x04047FF6, 0x272746C2,
        0xACACA716, 0xD0D07625, 0x50501386, 0xDCDCF756, 0x84841A55, 0xE1E15109, 0x7A7A25BE, 0x1313EF91,
    ];

    /**
     * M-Table
     *
     * @var int[]
     */
    private static array $m1 = [
        0xA9D93939, 0x67901717, 0xB3719C9C, 0xE8D2A6A6, 0x04050707, 0xFD985252, 0xA3658080, 0x76DFE4E4,
        0x9A084545, 0x92024B4B, 0x80A0E0E0, 0x78665A5A, 0xE4DDAFAF, 0xDDB06A6A, 0xD1BF6363, 0x38362A2A,
        0x0D54E6E6, 0xC6432020, 0x3562CCCC, 0x98BEF2F2, 0x181E1212, 0xF724EBEB, 0xECD7A1A1, 0x6C774141,
        0x43BD2828, 0x7532BCBC, 0x37D47B7B, 0x269B8888, 0xFA700D0D, 0x13F94444, 0x94B1FBFB, 0x485A7E7E,
        0xF27A0303, 0xD0E48C8C, 0x8B47B6B6, 0x303C2424, 0x84A5E7E7, 0x54416B6B, 0xDF06DDDD, 0x23C56060,
        0x1945FDFD, 0x5BA33A3A, 0x3D68C2C2, 0x59158D8D, 0xF321ECEC, 0xAE316666, 0xA23E6F6F, 0x82165757,
        0x63951010, 0x015BEFEF, 0x834DB8B8, 0x2E918686, 0xD9B56D6D, 0x511F8383, 0x9B53AAAA, 0x7C635D5D,
        0xA63B6868, 0xEB3FFEFE, 0xA5D63030, 0xBE257A7A, 0x16A7ACAC, 0x0C0F0909, 0xE335F0F0, 0x6123A7A7,
        0xC0F09090, 0x8CAFE9E9, 0x3A809D9D, 0xF5925C5C, 0x73810C0C, 0x2C273131, 0x2576D0D0, 0x0BE75656,
        0xBB7B9292, 0x4EE9CECE, 0x89F10101, 0x6B9F1E1E, 0x53A93434, 0x6AC4F1F1, 0xB499C3C3, 0xF1975B5B,
        0xE1834747, 0xE66B1818, 0xBDC82222, 0x450E9898, 0xE26E1F1F, 0xF4C9B3B3, 0xB62F7474, 0x66CBF8F8,
        0xCCFF9999, 0x95EA1414, 0x03ED5858, 0x56F7DCDC, 0xD4E18B8B, 0x1C1B1515, 0x1EADA2A2, 0xD70CD3D3,
        0xFB2BE2E2, 0xC31DC8C8, 0x8E195E5E, 0xB5C22C2C, 0xE9894949, 0xCF12C1C1, 0xBF7E9595, 0xBA207D7D,
        0xEA641111, 0x77840B0B, 0x396DC5C5, 0xAF6A8989, 0x33D17C7C, 0xC9A17171, 0x62CEFFFF, 0x7137BBBB,
        0x81FB0F0F, 0x793DB5B5, 0x0951E1E1, 0xADDC3E3E, 0x242D3F3F, 0xCDA47676, 0xF99D5555, 0xD8EE8282,
        0xE5864040, 0xC5AE7878, 0xB9CD2525, 0x4D049696, 0x44557777, 0x080A0E0E, 0x86135050, 0xE730F7F7,
        0xA1D33737, 0x1D40FAFA, 0xAA346161, 0xED8C4E4E, 0x06B3B0B0, 0x706C5454, 0xB22A7373, 0xD2523B3B,
        0x410B9F9F, 0x7B8B0202, 0xA088D8D8, 0x114FF3F3, 0x3167CBCB, 0xC2462727, 0x27C06767, 0x90B4FCFC,
        0x20283838, 0xF67F0404, 0x60784848, 0xFF2EE5E5, 0x96074C4C, 0x5C4B6565, 0xB1C72B2B, 0xAB6F8E8E,
        0x9E0D4242, 0x9CBBF5F5, 0x52F2DBDB, 0x1BF34A4A, 0x5FA63D3D, 0x9359A4A4, 0x0ABCB9B9, 0xEF3AF9F9,
        0x91EF1313, 0x85FE0808, 0x49019191, 0xEE611616, 0x2D7CDEDE, 0x4FB22121, 0x8F42B1B1, 0x3BDB7272,
        0x47B82F2F, 0x8748BFBF, 0x6D2CAEAE, 0x46E3C0C0, 0xD6573C3C, 0x3E859A9A, 0x6929A9A9, 0x647D4F4F,
        0x2A948181, 0xCE492E2E, 0xCB17C6C6, 0x2FCA6969, 0xFCC3BDBD, 0x975CA3A3, 0x055EE8E8, 0x7AD0EDED,
        0xAC87D1D1, 0x7F8E0505, 0xD5BA6464, 0x1AA8A5A5, 0x4BB72626, 0x0EB9BEBE, 0xA7608787, 0x5AF8D5D5,
        0x28223636, 0x14111B1B, 0x3FDE7575, 0x2979D9D9, 0x88AAEEEE, 0x3C332D2D, 0x4C5F7979, 0x02B6B7B7,
        0xB896CACA, 0xDA583535, 0xB09CC4C4, 0x17FC4343, 0x551A8484, 0x1FF64D4D, 0x8A1C5959, 0x7D38B2B2,
        0x57AC3333, 0xC718CFCF, 0x8DF40606, 0x74695353, 0xB7749B9B, 0xC4F59797, 0x9F56ADAD, 0x72DAE3E3,
        0x7ED5EAEA, 0x154AF4F4, 0x229E8F8F, 0x12A2ABAB, 0x584E6262, 0x07E85F5F, 0x99E51D1D, 0x34392323,
        0x6EC1F6F6, 0x50446C6C, 0xDE5D3232, 0x68724646, 0x6526A0A0, 0xBC93CDCD, 0xDB03DADA, 0xF8C6BABA,
        0xC8FA9E9E, 0xA882D6D6, 0x2BCF6E6E, 0x40507070, 0xDCEB8585, 0xFE750A0A, 0x328A9393, 0xA48DDFDF,
        0xCA4C2929, 0x10141C1C, 0x2173D7D7, 0xF0CCB4B4, 0xD309D4D4, 0x5D108A8A, 0x0FE25151, 0x00000000,
        0x6F9A1919, 0x9DE01A1A, 0x368F9494, 0x42E6C7C7, 0x4AECC9C9, 0x5EFDD2D2, 0xC1AB7F7F, 0xE0D8A8A8,
    ];

    /**
     * M-Table
     *
     * @var int[]
     */
    private static array $m2 = [
        0xBC75BC32, 0xECF3EC21, 0x20C62043, 0xB3F4B3C9, 0xDADBDA03, 0x027B028B, 0xE2FBE22B, 0x9EC89EFA,
        0xC94AC9EC, 0xD4D3D409, 0x18E6186B, 0x1E6B1E9F, 0x9845980E, 0xB27DB238, 0xA6E8A6D2, 0x264B26B7,
        0x3CD63C57, 0x9332938A, 0x82D882EE, 0x52FD5298, 0x7B377BD4, 0xBB71BB37, 0x5BF15B97, 0x47E14783,
        0x2430243C, 0x510F51E2, 0xBAF8BAC6, 0x4A1B4AF3, 0xBF87BF48, 0x0DFA0D70, 0xB006B0B3, 0x753F75DE,
        0xD25ED2FD, 0x7DBA7D20, 0x66AE6631, 0x3A5B3AA3, 0x598A591C, 0x00000000, 0xCDBCCD93, 0x1A9D1AE0,
        0xAE6DAE2C, 0x7FC17FAB, 0x2BB12BC7, 0xBE0EBEB9, 0xE080E0A0, 0x8A5D8A10, 0x3BD23B52, 0x64D564BA,
        0xD8A0D888, 0xE784E7A5, 0x5F075FE8, 0x1B141B11, 0x2CB52CC2, 0xFC90FCB4, 0x312C3127, 0x80A38065,
        0x73B2732A, 0x0C730C81, 0x794C795F, 0x6B546B41, 0x4B924B02, 0x53745369, 0x9436948F, 0x8351831F,
        0x2A382A36, 0xC4B0C49C, 0x22BD22C8, 0xD55AD5F8, 0xBDFCBDC3, 0x48604878, 0xFF62FFCE, 0x4C964C07,
        0x416C4177, 0xC742C7E6, 0xEBF7EB24, 0x1C101C14, 0x5D7C5D63, 0x36283622, 0x672767C0, 0xE98CE9AF,
        0x441344F9, 0x149514EA, 0xF59CF5BB, 0xCFC7CF18, 0x3F243F2D, 0xC046C0E3, 0x723B72DB, 0x5470546C,
        0x29CA294C, 0xF0E3F035, 0x088508FE, 0xC6CBC617, 0xF311F34F, 0x8CD08CE4, 0xA493A459, 0xCAB8CA96,
        0x68A6683B, 0xB883B84D, 0x38203828, 0xE5FFE52E, 0xAD9FAD56, 0x0B770B84, 0xC8C3C81D, 0x99CC99FF,
        0x580358ED, 0x196F199A, 0x0E080E0A, 0x95BF957E, 0x70407050, 0xF7E7F730, 0x6E2B6ECF, 0x1FE21F6E,
        0xB579B53D, 0x090C090F, 0x61AA6134, 0x57825716, 0x9F419F0B, 0x9D3A9D80, 0x11EA1164, 0x25B925CD,
        0xAFE4AFDD, 0x459A4508, 0xDFA4DF8D, 0xA397A35C, 0xEA7EEAD5, 0x35DA3558, 0xED7AEDD0, 0x431743FC,
        0xF866F8CB, 0xFB94FBB1, 0x37A137D3, 0xFA1DFA40, 0xC23DC268, 0xB4F0B4CC, 0x32DE325D, 0x9CB39C71,
        0x560B56E7, 0xE372E3DA, 0x87A78760, 0x151C151B, 0xF9EFF93A, 0x63D163BF, 0x345334A9, 0x9A3E9A85,
        0xB18FB142, 0x7C337CD1, 0x8826889B, 0x3D5F3DA6, 0xA1ECA1D7, 0xE476E4DF, 0x812A8194, 0x91499101,
        0x0F810FFB, 0xEE88EEAA, 0x16EE1661, 0xD721D773, 0x97C497F5, 0xA51AA5A8, 0xFEEBFE3F, 0x6DD96DB5,
        0x78C578AE, 0xC539C56D, 0x1D991DE5, 0x76CD76A4, 0x3EAD3EDC, 0xCB31CB67, 0xB68BB647, 0xEF01EF5B,
        0x1218121E, 0x602360C5, 0x6ADD6AB0, 0x4D1F4DF6, 0xCE4ECEE9, 0xDE2DDE7C, 0x55F9559D, 0x7E487E5A,
        0x214F21B2, 0x03F2037A, 0xA065A026, 0x5E8E5E19, 0x5A785A66, 0x655C654B, 0x6258624E, 0xFD19FD45,
        0x068D06F4, 0x40E54086, 0xF298F2BE, 0x335733AC, 0x17671790, 0x057F058E, 0xE805E85E, 0x4F644F7D,
        0x89AF896A, 0x10631095, 0x74B6742F, 0x0AFE0A75, 0x5CF55C92, 0x9BB79B74, 0x2D3C2D33, 0x30A530D6,
        0x2ECE2E49, 0x49E94989, 0x46684672, 0x77447755, 0xA8E0A8D8, 0x964D9604, 0x284328BD, 0xA969A929,
        0xD929D979, 0x862E8691, 0xD1ACD187, 0xF415F44A, 0x8D598D15, 0xD6A8D682, 0xB90AB9BC, 0x429E420D,
        0xF66EF6C1, 0x2F472FB8, 0xDDDFDD06, 0x23342339, 0xCC35CC62, 0xF16AF1C4, 0xC1CFC112, 0x85DC85EB,
        0x8F228F9E, 0x71C971A1, 0x90C090F0, 0xAA9BAA53, 0x018901F1, 0x8BD48BE1, 0x4EED4E8C, 0x8EAB8E6F,
        0xAB12ABA2, 0x6FA26F3E, 0xE60DE654, 0xDB52DBF2, 0x92BB927B, 0xB702B7B6, 0x692F69CA, 0x39A939D9,
        0xD3D7D30C, 0xA761A723, 0xA21EA2AD, 0xC3B4C399, 0x6C506C44, 0x07040705, 0x04F6047F, 0x27C22746,
        0xAC16ACA7, 0xD025D076, 0x50865013, 0xDC56DCF7, 0x8455841A, 0xE109E151, 0x7ABE7A25, 0x139113EF,
    ];

    /**
     * M-Table
     *
     * @var int[]
     */
    private static array $m3 = [
        0xD939A9D9, 0x90176790, 0x719CB371, 0xD2A6E8D2, 0x05070405, 0x9852FD98, 0x6580A365, 0xDFE476DF,
        0x08459A08, 0x024B9202, 0xA0E080A0, 0x665A7866, 0xDDAFE4DD, 0xB06ADDB0, 0xBF63D1BF, 0x362A3836,
        0x54E60D54, 0x4320C643, 0x62CC3562, 0xBEF298BE, 0x1E12181E, 0x24EBF724, 0xD7A1ECD7, 0x77416C77,
        0xBD2843BD, 0x32BC7532, 0xD47B37D4, 0x9B88269B, 0x700DFA70, 0xF94413F9, 0xB1FB94B1, 0x5A7E485A,
        0x7A03F27A, 0xE48CD0E4, 0x47B68B47, 0x3C24303C, 0xA5E784A5, 0x416B5441, 0x06DDDF06, 0xC56023C5,
        0x45FD1945, 0xA33A5BA3, 0x68C23D68, 0x158D5915, 0x21ECF321, 0x3166AE31, 0x3E6FA23E, 0x16578216,
        0x95106395, 0x5BEF015B, 0x4DB8834D, 0x91862E91, 0xB56DD9B5, 0x1F83511F, 0x53AA9B53, 0x635D7C63,
        0x3B68A63B, 0x3FFEEB3F, 0xD630A5D6, 0x257ABE25, 0xA7AC16A7, 0x0F090C0F, 0x35F0E335, 0x23A76123,
        0xF090C0F0, 0xAFE98CAF, 0x809D3A80, 0x925CF592, 0x810C7381, 0x27312C27, 0x76D02576, 0xE7560BE7,
        0x7B92BB7B, 0xE9CE4EE9, 0xF10189F1, 0x9F1E6B9F, 0xA93453A9, 0xC4F16AC4, 0x99C3B499, 0x975BF197,
        0x8347E183, 0x6B18E66B, 0xC822BDC8, 0x0E98450E, 0x6E1FE26E, 0xC9B3F4C9, 0x2F74B62F, 0xCBF866CB,
        0xFF99CCFF, 0xEA1495EA, 0xED5803ED, 0xF7DC56F7, 0xE18BD4E1, 0x1B151C1B, 0xADA21EAD, 0x0CD3D70C,
        0x2BE2FB2B, 0x1DC8C31D, 0x195E8E19, 0xC22CB5C2, 0x8949E989, 0x12C1CF12, 0x7E95BF7E, 0x207DBA20,
        0x6411EA64, 0x840B7784, 0x6DC5396D, 0x6A89AF6A, 0xD17C33D1, 0xA171C9A1, 0xCEFF62CE, 0x37BB7137,
        0xFB0F81FB, 0x3DB5793D, 0x51E10951, 0xDC3EADDC, 0x2D3F242D, 0xA476CDA4, 0x9D55F99D, 0xEE82D8EE,
        0x8640E586, 0xAE78C5AE, 0xCD25B9CD, 0x04964D04, 0x55774455, 0x0A0E080A, 0x13508613, 0x30F7E730,
        0xD337A1D3, 0x40FA1D40, 0x3461AA34, 0x8C4EED8C, 0xB3B006B3, 0x6C54706C, 0x2A73B22A, 0x523BD252,
        0x0B9F410B, 0x8B027B8B, 0x88D8A088, 0x4FF3114F, 0x67CB3167, 0x4627C246, 0xC06727C0, 0xB4FC90B4,
        0x28382028, 0x7F04F67F, 0x78486078, 0x2EE5FF2E, 0x074C9607, 0x4B655C4B, 0xC72BB1C7, 0x6F8EAB6F,
        0x0D429E0D, 0xBBF59CBB, 0xF2DB52F2, 0xF34A1BF3, 0xA63D5FA6, 0x59A49359, 0xBCB90ABC, 0x3AF9EF3A,
        0xEF1391EF, 0xFE0885FE, 0x01914901, 0x6116EE61, 0x7CDE2D7C, 0xB2214FB2, 0x42B18F42, 0xDB723BDB,
        0xB82F47B8, 0x48BF8748, 0x2CAE6D2C, 0xE3C046E3, 0x573CD657, 0x859A3E85, 0x29A96929, 0x7D4F647D,
        0x94812A94, 0x492ECE49, 0x17C6CB17, 0xCA692FCA, 0xC3BDFCC3, 0x5CA3975C, 0x5EE8055E, 0xD0ED7AD0,
        0x87D1AC87, 0x8E057F8E, 0xBA64D5BA, 0xA8A51AA8, 0xB7264BB7, 0xB9BE0EB9, 0x6087A760, 0xF8D55AF8,
        0x22362822, 0x111B1411, 0xDE753FDE, 0x79D92979, 0xAAEE88AA, 0x332D3C33, 0x5F794C5F, 0xB6B702B6,
        0x96CAB896, 0x5835DA58, 0x9CC4B09C, 0xFC4317FC, 0x1A84551A, 0xF64D1FF6, 0x1C598A1C, 0x38B27D38,
        0xAC3357AC, 0x18CFC718, 0xF4068DF4, 0x69537469, 0x749BB774, 0xF597C4F5, 0x56AD9F56, 0xDAE372DA,
        0xD5EA7ED5, 0x4AF4154A, 0x9E8F229E, 0xA2AB12A2, 0x4E62584E, 0xE85F07E8, 0xE51D99E5, 0x39233439,
        0xC1F66EC1, 0x446C5044, 0x5D32DE5D, 0x72466872, 0x26A06526, 0x93CDBC93, 0x03DADB03, 0xC6BAF8C6,
        0xFA9EC8FA, 0x82D6A882, 0xCF6E2BCF, 0x50704050, 0xEB85DCEB, 0x750AFE75, 0x8A93328A, 0x8DDFA48D,
        0x4C29CA4C, 0x141C1014, 0x73D72173, 0xCCB4F0CC, 0x09D4D309, 0x108A5D10, 0xE2510FE2, 0x00000000,
        0x9A196F9A, 0xE01A9DE0, 0x8F94368F, 0xE6C742E6, 0xECC94AEC, 0xFDD25EFD, 0xAB7FC1AB, 0xD8A8E0D8,
    ];

    /**
     * The Key Schedule Array
     */
    private array $K = [];

    /**
     * The Key depended S-Table 0
     */
    private array $S0 = [];

    /**
     * The Key depended S-Table 1
     */
    private array $S1 = [];

    /**
     * The Key depended S-Table 2
     */
    private array $S2 = [];

    /**
     * The Key depended S-Table 3
     */
    private array $S3 = [];

    /**
     * Holds the last used key
     */
    private array $kl;

    /**
     * The Key Length (in bytes)
     *
     * @see self::setKeyLength()
     */
    protected int $key_length = 16;

    /**
     * Initialize Static Variables
     */
    protected static function initialize_static_variables(): void
    {
        if (is_float(self::$m3[0])) {
            self::$m0 = array_map(self::safe_intval(...), self::$m0);
            self::$m1 = array_map(self::safe_intval(...), self::$m1);
            self::$m2 = array_map(self::safe_intval(...), self::$m2);
            self::$m3 = array_map(self::safe_intval(...), self::$m3);
            self::$q0 = array_map(self::safe_intval(...), self::$q0);
            self::$q1 = array_map(self::safe_intval(...), self::$q1);
        }

        parent::initialize_static_variables();
    }

    /**
     * Sets the key length.
     *
     * Valid key lengths are 128, 192 or 256 bits
     */
    public function setKeyLength(int $length): void
    {
        switch ($length) {
            case 128:
            case 192:
            case 256:
                break;
            default:
                throw new LengthException('Key of size ' . $length . ' not supported by this algorithm. Only keys of sizes 16, 24 or 32 supported');
        }

        parent::setKeyLength($length);
    }

    /**
     * Sets the key.
     *
     * Rijndael supports five different key lengths
     *
     * @throws LengthException if the key length isn't supported
     * @see self::setKeyLength()
     */
    public function setKey(#[\SensitiveParameter] string $key): void
    {
        switch (strlen($key)) {
            case 16:
            case 24:
            case 32:
                break;
            default:
                throw new LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of sizes 16, 24 or 32 supported');
        }

        parent::setKey($key);
    }

    /**
     * Setup the key (expansion)
     *
     * @see Common\SymmetricKey::_setupKey()
     */
    protected function setupKey(): void
    {
        if (isset($this->kl['key']) && $this->key === $this->kl['key']) {
            // already expanded
            return;
        }
        $this->kl = ['key' => $this->key];

        /* Key expanding and generating the key-depended s-boxes */
        $le_longs = unpack('V*', $this->key);
        $key = unpack('C*', $this->key);
        $m0 = self::$m0;
        $m1 = self::$m1;
        $m2 = self::$m2;
        $m3 = self::$m3;
        $q0 = self::$q0;
        $q1 = self::$q1;

        $K = $S0 = $S1 = $S2 = $S3 = [];

        switch (strlen($this->key)) {
            case 16:
                [$s7, $s6, $s5, $s4] = $this->mdsrem($le_longs[1], $le_longs[2]);
                [$s3, $s2, $s1, $s0] = $this->mdsrem($le_longs[3], $le_longs[4]);
                for ($i = 0, $j = 1; $i < 40; $i += 2, $j += 2) {
                    $A = $m0[$q0[$q0[$i] ^ $key[ 9]] ^ $key[1]] ^
                         $m1[$q0[$q1[$i] ^ $key[10]] ^ $key[2]] ^
                         $m2[$q1[$q0[$i] ^ $key[11]] ^ $key[3]] ^
                         $m3[$q1[$q1[$i] ^ $key[12]] ^ $key[4]];
                    $B = $m0[$q0[$q0[$j] ^ $key[13]] ^ $key[5]] ^
                         $m1[$q0[$q1[$j] ^ $key[14]] ^ $key[6]] ^
                         $m2[$q1[$q0[$j] ^ $key[15]] ^ $key[7]] ^
                         $m3[$q1[$q1[$j] ^ $key[16]] ^ $key[8]];
                    $B = ($B << 8) | ($B >> 24 & 0xff);
                    $A = self::safe_intval($A + $B);
                    $K[] = $A;
                    $A = self::safe_intval($A + $B);
                    $K[] = ($A << 9 | $A >> 23 & 0x1ff);
                }
                for ($i = 0; $i < 256; ++$i) {
                    $S0[$i] = $m0[$q0[$q0[$i] ^ $s4] ^ $s0];
                    $S1[$i] = $m1[$q0[$q1[$i] ^ $s5] ^ $s1];
                    $S2[$i] = $m2[$q1[$q0[$i] ^ $s6] ^ $s2];
                    $S3[$i] = $m3[$q1[$q1[$i] ^ $s7] ^ $s3];
                }
                break;
            case 24:
                [$sb, $sa, $s9, $s8] = $this->mdsrem($le_longs[1], $le_longs[2]);
                [$s7, $s6, $s5, $s4] = $this->mdsrem($le_longs[3], $le_longs[4]);
                [$s3, $s2, $s1, $s0] = $this->mdsrem($le_longs[5], $le_longs[6]);
                for ($i = 0, $j = 1; $i < 40; $i += 2, $j += 2) {
                    $A = $m0[$q0[$q0[$q1[$i] ^ $key[17]] ^ $key[ 9]] ^ $key[1]] ^
                         $m1[$q0[$q1[$q1[$i] ^ $key[18]] ^ $key[10]] ^ $key[2]] ^
                         $m2[$q1[$q0[$q0[$i] ^ $key[19]] ^ $key[11]] ^ $key[3]] ^
                         $m3[$q1[$q1[$q0[$i] ^ $key[20]] ^ $key[12]] ^ $key[4]];
                    $B = $m0[$q0[$q0[$q1[$j] ^ $key[21]] ^ $key[13]] ^ $key[5]] ^
                         $m1[$q0[$q1[$q1[$j] ^ $key[22]] ^ $key[14]] ^ $key[6]] ^
                         $m2[$q1[$q0[$q0[$j] ^ $key[23]] ^ $key[15]] ^ $key[7]] ^
                         $m3[$q1[$q1[$q0[$j] ^ $key[24]] ^ $key[16]] ^ $key[8]];
                    $B = ($B << 8) | ($B >> 24 & 0xff);
                    $A = self::safe_intval($A + $B);
                    $K[] = $A;
                    $A = self::safe_intval($A + $B);
                    $K[] = ($A << 9 | $A >> 23 & 0x1ff);
                }
                for ($i = 0; $i < 256; ++$i) {
                    $S0[$i] = $m0[$q0[$q0[$q1[$i] ^ $s8] ^ $s4] ^ $s0];
                    $S1[$i] = $m1[$q0[$q1[$q1[$i] ^ $s9] ^ $s5] ^ $s1];
                    $S2[$i] = $m2[$q1[$q0[$q0[$i] ^ $sa] ^ $s6] ^ $s2];
                    $S3[$i] = $m3[$q1[$q1[$q0[$i] ^ $sb] ^ $s7] ^ $s3];
                }
                break;
            default: // 32
                [$sf, $se, $sd, $sc] = $this->mdsrem($le_longs[1], $le_longs[2]);
                [$sb, $sa, $s9, $s8] = $this->mdsrem($le_longs[3], $le_longs[4]);
                [$s7, $s6, $s5, $s4] = $this->mdsrem($le_longs[5], $le_longs[6]);
                [$s3, $s2, $s1, $s0] = $this->mdsrem($le_longs[7], $le_longs[8]);
                for ($i = 0, $j = 1; $i < 40; $i += 2, $j += 2) {
                    $A = $m0[$q0[$q0[$q1[$q1[$i] ^ $key[25]] ^ $key[17]] ^ $key[ 9]] ^ $key[1]] ^
                         $m1[$q0[$q1[$q1[$q0[$i] ^ $key[26]] ^ $key[18]] ^ $key[10]] ^ $key[2]] ^
                         $m2[$q1[$q0[$q0[$q0[$i] ^ $key[27]] ^ $key[19]] ^ $key[11]] ^ $key[3]] ^
                         $m3[$q1[$q1[$q0[$q1[$i] ^ $key[28]] ^ $key[20]] ^ $key[12]] ^ $key[4]];
                    $B = $m0[$q0[$q0[$q1[$q1[$j] ^ $key[29]] ^ $key[21]] ^ $key[13]] ^ $key[5]] ^
                         $m1[$q0[$q1[$q1[$q0[$j] ^ $key[30]] ^ $key[22]] ^ $key[14]] ^ $key[6]] ^
                         $m2[$q1[$q0[$q0[$q0[$j] ^ $key[31]] ^ $key[23]] ^ $key[15]] ^ $key[7]] ^
                         $m3[$q1[$q1[$q0[$q1[$j] ^ $key[32]] ^ $key[24]] ^ $key[16]] ^ $key[8]];
                    $B = ($B << 8) | ($B >> 24 & 0xff);
                    $A = self::safe_intval($A + $B);
                    $K[] = $A;
                    $A = self::safe_intval($A + $B);
                    $K[] = ($A << 9 | $A >> 23 & 0x1ff);
                }
                for ($i = 0; $i < 256; ++$i) {
                    $S0[$i] = $m0[$q0[$q0[$q1[$q1[$i] ^ $sc] ^ $s8] ^ $s4] ^ $s0];
                    $S1[$i] = $m1[$q0[$q1[$q1[$q0[$i] ^ $sd] ^ $s9] ^ $s5] ^ $s1];
                    $S2[$i] = $m2[$q1[$q0[$q0[$q0[$i] ^ $se] ^ $sa] ^ $s6] ^ $s2];
                    $S3[$i] = $m3[$q1[$q1[$q0[$q1[$i] ^ $sf] ^ $sb] ^ $s7] ^ $s3];
                }
        }

        $this->K  = $K;
        $this->S0 = $S0;
        $this->S1 = $S1;
        $this->S2 = $S2;
        $this->S3 = $S3;
    }

    /**
     * _mdsrem function using by the twofish cipher algorithm
     */
    private function mdsrem(int $A, int $B): array
    {
        // No gain by unrolling this loop.
        for ($i = 0; $i < 8; ++$i) {
            // Get most significant coefficient.
            $t = 0xff & ($B >> 24);

            // Shift the others up.
            $B = ($B << 8) | (0xff & ($A >> 24));
            $A <<= 8;

            $u = $t << 1;

            // Subtract the modular polynomial on overflow.
            if ($t & 0x80) {
                $u ^= 0x14d;
            }

            // Remove t * (a * x^2 + 1).
            $B ^= $t ^ ($u << 16);

            // Form u = a*t + t/a = t*(a + 1/a).
            $u ^= 0x7fffffff & ($t >> 1);

            // Add the modular polynomial on underflow.
            if ($t & 0x01) {
                $u ^= 0xa6 ;
            }

            // Remove t * (a + 1/a) * (x^3 + x).
            $B ^= ($u << 24) | ($u << 8);
        }

        return [
            0xff & $B >> 24,
            0xff & $B >> 16,
            0xff & $B >>  8,
            0xff & $B, ];
    }

    /**
     * Encrypts a block
     */
    protected function encryptBlock(string $in): string
    {
        $S0 = $this->S0;
        $S1 = $this->S1;
        $S2 = $this->S2;
        $S3 = $this->S3;
        $K  = $this->K;

        $in = unpack("V4", $in);
        $R0 = $K[0] ^ $in[1];
        $R1 = $K[1] ^ $in[2];
        $R2 = $K[2] ^ $in[3];
        $R3 = $K[3] ^ $in[4];

        $ki = 7;
        while ($ki < 39) {
            $t0 = $S0[ $R0        & 0xff] ^
                  $S1[($R0 >>  8) & 0xff] ^
                  $S2[($R0 >> 16) & 0xff] ^
                  $S3[($R0 >> 24) & 0xff];
            $t1 = $S0[($R1 >> 24) & 0xff] ^
                  $S1[ $R1        & 0xff] ^
                  $S2[($R1 >>  8) & 0xff] ^
                  $S3[($R1 >> 16) & 0xff];
            $R2 ^= self::safe_intval($t0 + $t1 + $K[++$ki]);
            $R2 = ($R2 >> 1 & 0x7fffffff) | ($R2 << 31);
            $R3 = ((($R3 >> 31) & 1) | ($R3 << 1)) ^ self::safe_intval($t0 + ($t1 << 1) + $K[++$ki]);

            $t0 = $S0[ $R2        & 0xff] ^
                  $S1[($R2 >>  8) & 0xff] ^
                  $S2[($R2 >> 16) & 0xff] ^
                  $S3[($R2 >> 24) & 0xff];
            $t1 = $S0[($R3 >> 24) & 0xff] ^
                  $S1[ $R3        & 0xff] ^
                  $S2[($R3 >>  8) & 0xff] ^
                  $S3[($R3 >> 16) & 0xff];
            $R0 ^= self::safe_intval($t0 + $t1 + $K[++$ki]);
            $R0 = ($R0 >> 1 & 0x7fffffff) | ($R0 << 31);
            $R1 = ((($R1 >> 31) & 1) | ($R1 << 1)) ^ self::safe_intval($t0 + ($t1 << 1) + $K[++$ki]);
        }

        return pack(
            "V4",
            $K[4] ^ $R2,
            $K[5] ^ $R3,
            $K[6] ^ $R0,
            $K[7] ^ $R1
        );
    }

    /**
     * Decrypts a block
     */
    protected function decryptBlock(string $in): string
    {
        $S0 = $this->S0;
        $S1 = $this->S1;
        $S2 = $this->S2;
        $S3 = $this->S3;
        $K  = $this->K;

        $in = unpack("V4", $in);
        $R0 = $K[4] ^ $in[1];
        $R1 = $K[5] ^ $in[2];
        $R2 = $K[6] ^ $in[3];
        $R3 = $K[7] ^ $in[4];

        $ki = 40;
        while ($ki > 8) {
            $t0 = $S0[$R0       & 0xff] ^
                  $S1[$R0 >>  8 & 0xff] ^
                  $S2[$R0 >> 16 & 0xff] ^
                  $S3[$R0 >> 24 & 0xff];
            $t1 = $S0[$R1 >> 24 & 0xff] ^
                  $S1[$R1       & 0xff] ^
                  $S2[$R1 >>  8 & 0xff] ^
                  $S3[$R1 >> 16 & 0xff];
            $R3 ^= self::safe_intval($t0 + ($t1 << 1) + $K[--$ki]);
            $R3 = $R3 >> 1 & 0x7fffffff | $R3 << 31;
            $R2 = ($R2 >> 31 & 0x1 | $R2 << 1) ^ self::safe_intval($t0 + $t1 + $K[--$ki]);

            $t0 = $S0[$R2       & 0xff] ^
                  $S1[$R2 >>  8 & 0xff] ^
                  $S2[$R2 >> 16 & 0xff] ^
                  $S3[$R2 >> 24 & 0xff];
            $t1 = $S0[$R3 >> 24 & 0xff] ^
                  $S1[$R3       & 0xff] ^
                  $S2[$R3 >>  8 & 0xff] ^
                  $S3[$R3 >> 16 & 0xff];
            $R1 ^= self::safe_intval($t0 + ($t1 << 1) + $K[--$ki]);
            $R1 = $R1 >> 1 & 0x7fffffff | $R1 << 31;
            $R0 = ($R0 >> 31 & 0x1 | $R0 << 1) ^ self::safe_intval($t0 + $t1 + $K[--$ki]);
        }

        return pack(
            "V4",
            $K[0] ^ $R2,
            $K[1] ^ $R3,
            $K[2] ^ $R0,
            $K[3] ^ $R1
        );
    }

    /**
     * Setup the performance-optimized function for de/encrypt()
     *
     * @see Common\SymmetricKey::_setupInlineCrypt()
     * @psalm-suppress PossiblyUnusedMethod
     */
    protected function setupInlineCrypt(): void
    {
        $K = $this->K;
        $init_crypt = '
            static $S0, $S1, $S2, $S3;
            if (!$S0) {
                for ($i = 0; $i < 256; ++$i) {
                    $S0[] = self::safe_intval($this->S0[$i]);
                    $S1[] = self::safe_intval($this->S1[$i]);
                    $S2[] = self::safe_intval($this->S2[$i]);
                    $S3[] = self::safe_intval($this->S3[$i]);
                }
            }
        ';

        $safeint = self::safe_intval_inline();

        // Generating encrypt code:
        $encrypt_block = '
            $in = unpack("V4", $in);
            $R0 = ' . $K[0] . ' ^ $in[1];
            $R1 = ' . $K[1] . ' ^ $in[2];
            $R2 = ' . $K[2] . ' ^ $in[3];
            $R3 = ' . $K[3] . ' ^ $in[4];
        ';
        for ($ki = 7, $i = 0; $i < 8; ++$i) {
            $encrypt_block .= '
                $t0 = $S0[ $R0        & 0xff] ^
                      $S1[($R0 >>  8) & 0xff] ^
                      $S2[($R0 >> 16) & 0xff] ^
                      $S3[($R0 >> 24) & 0xff];
                $t1 = $S0[($R1 >> 24) & 0xff] ^
                      $S1[ $R1        & 0xff] ^
                      $S2[($R1 >>  8) & 0xff] ^
                      $S3[($R1 >> 16) & 0xff];
                $R2^= ' . sprintf($safeint, '$t0 + $t1 + ' . $K[++$ki]) . ';
                $R2 = ($R2 >> 1 & 0x7fffffff) | ($R2 << 31);
                $R3 = ((($R3 >> 31) & 1) | ($R3 << 1)) ^ ' . sprintf($safeint, '($t0 + ($t1 << 1) + ' . $K[++$ki] . ')') . ';
                $t0 = $S0[ $R2        & 0xff] ^
                      $S1[($R2 >>  8) & 0xff] ^
                      $S2[($R2 >> 16) & 0xff] ^
                      $S3[($R2 >> 24) & 0xff];
                $t1 = $S0[($R3 >> 24) & 0xff] ^
                      $S1[ $R3        & 0xff] ^
                      $S2[($R3 >>  8) & 0xff] ^
                      $S3[($R3 >> 16) & 0xff];
                $R0^= ' . sprintf($safeint, '$t0 + $t1 + ' . $K[++$ki]) . ';
                $R0 = ($R0 >> 1 & 0x7fffffff) | ($R0 << 31);
                $R1 = ((($R1 >> 31) & 1) | ($R1 << 1)) ^ ' . sprintf($safeint, '($t0 + ($t1 << 1) + ' . $K[++$ki] . ')') . ';
            ';
        }
        $encrypt_block .= '
            $in = pack("V4", ' . $K[4] . ' ^ $R2,
                             ' . $K[5] . ' ^ $R3,
                             ' . $K[6] . ' ^ $R0,
                             ' . $K[7] . ' ^ $R1);
        ';

        // Generating decrypt code:
        $decrypt_block = '
            $in = unpack("V4", $in);
            $R0 = ' . $K[4] . ' ^ $in[1];
            $R1 = ' . $K[5] . ' ^ $in[2];
            $R2 = ' . $K[6] . ' ^ $in[3];
            $R3 = ' . $K[7] . ' ^ $in[4];
        ';
        for ($ki = 40, $i = 0; $i < 8; ++$i) {
            $decrypt_block .= '
                $t0 = $S0[$R0       & 0xff] ^
                      $S1[$R0 >>  8 & 0xff] ^
                      $S2[$R0 >> 16 & 0xff] ^
                      $S3[$R0 >> 24 & 0xff];
                $t1 = $S0[$R1 >> 24 & 0xff] ^
                      $S1[$R1       & 0xff] ^
                      $S2[$R1 >>  8 & 0xff] ^
                      $S3[$R1 >> 16 & 0xff];
                $R3^= ' . sprintf($safeint, '$t0 + ($t1 << 1) + ' . $K[--$ki]) . ';
                $R3 = $R3 >> 1 & 0x7fffffff | $R3 << 31;
                $R2 = ($R2 >> 31 & 0x1 | $R2 << 1) ^ ' . sprintf($safeint, '($t0 + $t1 + ' . $K[--$ki] . ')') . ';

                $t0 = $S0[$R2       & 0xff] ^
                      $S1[$R2 >>  8 & 0xff] ^
                      $S2[$R2 >> 16 & 0xff] ^
                      $S3[$R2 >> 24 & 0xff];
                $t1 = $S0[$R3 >> 24 & 0xff] ^
                      $S1[$R3       & 0xff] ^
                      $S2[$R3 >>  8 & 0xff] ^
                      $S3[$R3 >> 16 & 0xff];
                $R1^= ' . sprintf($safeint, '$t0 + ($t1 << 1) + ' . $K[--$ki]) . ';
                $R1 = $R1 >> 1 & 0x7fffffff | $R1 << 31;
                $R0 = ($R0 >> 31 & 0x1 | $R0 << 1) ^ ' . sprintf($safeint, '($t0 + $t1 + ' . $K[--$ki] . ')') . ';
            ';
        }
        $decrypt_block .= '
            $in = pack("V4", ' . $K[0] . ' ^ $R2,
                             ' . $K[1] . ' ^ $R3,
                             ' . $K[2] . ' ^ $R0,
                             ' . $K[3] . ' ^ $R1);
        ';

        $this->inline_crypt = $this->createInlineCryptFunction(
            [
               'init_crypt'    => $init_crypt,
               'init_encrypt'  => '',
               'init_decrypt'  => '',
               'encrypt_block' => $encrypt_block,
               'decrypt_block' => $decrypt_block,
            ]
        );
    }
}
<?php

/**
 * Pure-PHP implementation of ChaCha20.
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2019-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt;

use phpseclib4\Exception\{BadDecryptionException, InvalidStateException, LengthException};

/**
 * Pure-PHP implementation of ChaCha20.
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class ChaCha20 extends Salsa20
{
    /**
     * The OpenSSL specific name of the cipher
     */
    protected string $cipher_name_openssl = 'chacha20';

    /**
     * Test for engine validity
     *
     * This is mainly just a wrapper to set things up for \phpseclib4\Crypt\Common\SymmetricKey::isValidEngine()
     *
     * @see \phpseclib4\Crypt\Common\SymmetricKey::__construct()
     */
    protected function isValidEngineHelper(int $engine): bool
    {
        // neither libsodium or openssl let you set the key for poly1305 so, in-so-far as poly1305
        // is concerned, our options are fairly limited
        switch ($engine) {
            case self::ENGINE_LIBSODIUM:
                // PHP 7.2.0 (30 Nov 2017) added support for libsodium

                // we could probably make it so that if $this->counter == 0 then the first block would be done with either OpenSSL
                // or PHP and then subsequent blocks would then be done with libsodium but idk - it's not a high priority atm

                // we could also make it so that if $this->counter == 0 and $this->continuousBuffer then do the first string
                // with libsodium and subsequent strings with openssl or pure-PHP but again not a high priority
                return function_exists('sodium_crypto_aead_chacha20poly1305_ietf_encrypt') &&
                       $this->key_length == 32 &&
                       // if the poly1305 key is being auto-calculated and the counter is 0 we can use libsodium for both chacha20 and poly1305
                       // if the counter is 1 we can use libsodium for chacha20 but NOT for poly1305. for poly1305, phpseclib will use its own
                       // pure php implementation
                       (($this->usePoly1305 && !isset($this->poly1305Key) && $this->counter == 0) || $this->counter == 1) &&
                       !$this->continuousBuffer;
            case self::ENGINE_OPENSSL_AEAD:
                // PHP 8.2.0 (8 Dec 2022) "added AEAD support for the chacha20-poly1305 algorithm"
                return extension_loaded('openssl') &&
                    PHP_VERSION_ID >= 80200 &&
                    $this->key_length == 32 &&
                    // we're not doing the "|| $this->>counter == 1" bit that we do for openssl because, most likely,
                    // if you have chacha20-poly1305 as an available cipher you also have chacha20 and
                    // if the poly1305 result is going to be discarded, anyway, it'd just be faster to
                    // not even compute it at all
                    $this->usePoly1305 && !isset($this->poly1305Key) &&
                    $this->counter == 0 &&
                    // libsodium has sodium_crypto_aead_chacha20poly1305_encrypt() for 64-bit nonces
                    // and sodium_crypto_aead_chacha20poly1305_ietf_encrypt() for 96-bit nonces.
                    // openssl just does 96-bit nonces and that's it.
                    strlen($this->nonce ?? '') == 12 &&
                    !$this->continuousBuffer &&
                    in_array('chacha20-poly1305', openssl_get_cipher_methods());
            case self::ENGINE_OPENSSL:
                // OpenSSL 1.1.0 (released 25 Aug 2016) added support for chacha20.
                // PHP didn't support OpenSSL 1.1.0 until 7.0.19 (11 May 2017)

                // if you attempt to provide openssl with a 128 bit key (as opposed to a 256 bit key) openssl will null
                // pad the key to 256 bits and still use the expansion constant for 256-bit keys. the fact that
                // openssl treats the IV as both the counter and nonce, however, let's us use openssl in continuous mode
                // whereas libsodium does not
                if ($this->key_length != 32) {
                    return false;
                }
        }

        return parent::isValidEngineHelper($engine);
    }

    /**
     * Encrypts a message.
     *
     * @see \phpseclib4\Crypt\Common\SymmetricKey::decrypt()
     * @see self::crypt()
     */
    public function encrypt(string $plaintext): string
    {
        $this->setup();
        return match ($this->engine) {
            self::ENGINE_LIBSODIUM => $this->encrypt_with_libsodium($plaintext),
            self::ENGINE_OPENSSL_AEAD => $this->encrypt_with_openssl_aead($plaintext),
            default => parent::encrypt($plaintext)
        };
    }

    /**
     * Decrypts a message.
     *
     * $this->decrypt($this->encrypt($plaintext)) == $this->encrypt($this->encrypt($plaintext)).
     * At least if the continuous buffer is disabled.
     *
     * @see \phpseclib4\Crypt\Common\SymmetricKey::encrypt()
     * @see self::crypt()
     */
    public function decrypt(string $ciphertext): string
    {
        $this->setup();
        return match ($this->engine) {
            self::ENGINE_LIBSODIUM => $this->decrypt_with_libsodium($ciphertext),
            self::ENGINE_OPENSSL_AEAD => $this->decrypt_with_openssl_aead($ciphertext),
            default => parent::decrypt($ciphertext)
        };
    }

    /**
     * Encrypts a message with libsodium
     *
     * @see self::encrypt()
     */
    private function encrypt_with_libsodium(string $plaintext): string
    {
        $params = [$plaintext, $this->aad, $this->nonce, $this->key];
        $ciphertext = strlen($this->nonce) == 8 ?
            sodium_crypto_aead_chacha20poly1305_encrypt(...$params) :
            sodium_crypto_aead_chacha20poly1305_ietf_encrypt(...$params);
        if (!$this->usePoly1305) {
            return substr($ciphertext, 0, strlen($plaintext));
        }

        $newciphertext = substr($ciphertext, 0, strlen($plaintext));

        $this->newtag = $this->usingGeneratedPoly1305Key ?
            substr($ciphertext, strlen($plaintext)) :
            $this->poly1305($newciphertext);

        return $newciphertext;
    }

    private function encrypt_with_openssl_aead(string $plaintext): string
    {
        return openssl_encrypt($plaintext, 'chacha20-poly1305', $this->key, OPENSSL_RAW_DATA, $this->nonce, $this->newtag, $this->aad);
    }

    /**
     * Decrypts a message with libsodium
     *
     * @see self::decrypt()
     */
    private function decrypt_with_libsodium(string $ciphertext): string
    {
        $params = [$ciphertext, $this->aad, $this->nonce, $this->key];

        if (isset($this->poly1305Key)) {
            if (!isset($this->oldtag)) {
                throw new InvalidStateException('Authentication Tag has not been set - call setTag() first');
            }
            $params[0] .= $this->oldtag;
            if ($this->usingGeneratedPoly1305Key) {
                $plaintext = strlen($this->nonce) == 8 ?
                    sodium_crypto_aead_chacha20poly1305_decrypt(...$params) :
                    sodium_crypto_aead_chacha20poly1305_ietf_decrypt(...$params);
                $this->oldtag = null;
                if ($plaintext === false) {
                    throw new BadDecryptionException('Derived authentication tag and supplied authentication tag do not match');
                }
                return $plaintext;
            }
            $newtag = $this->poly1305($ciphertext);
            if ($this->oldtag != substr($newtag, 0, strlen($this->oldtag))) {
                $this->oldtag = null;
                throw new BadDecryptionException('Derived authentication tag and supplied authentication tag do not match');
            }
            $this->oldtag = null;
        }

        // we're calling encrypt vs decrypt here because decrypt will try to verify the tag and, if we're
        // at this point in the code, we don't have one. so like if you call decrypt an exception will be
        // thrown. consequently, we encrypt. since chacha20 is a stream cipher all encryption basically is
        // is XOR'ing the plaintext with the keystream. if you instead XOR the ciphertext with the keystream
        // you get the plaintext
        $plaintext = strlen($this->nonce) == 8 ?
            sodium_crypto_aead_chacha20poly1305_encrypt(...$params) :
            sodium_crypto_aead_chacha20poly1305_ietf_encrypt(...$params);

        return substr($plaintext, 0, strlen($ciphertext));
    }

    private function decrypt_with_openssl_aead(string $ciphertext): string
    {
        //assert($this->usingGeneratedPoly1305Key);
        if (!isset($this->oldtag)) {
            throw new InvalidStateException('Authentication Tag has not been set - call setTag() first');
        }
        $plaintext = openssl_decrypt($ciphertext, 'chacha20-poly1305', $this->key, OPENSSL_RAW_DATA, $this->nonce, $this->oldtag, $this->aad);
        if ($plaintext === false) {
            throw new BadDecryptionException('Derived authentication tag and supplied authentication tag do not match');
        }
        $this->oldtag = null;
        return $plaintext;

        // whereas in libsodium we worry about the !$this->usingGeneratedPoly1305Key case, in this case, we don't.
        // if you're not using a generated poly1305 key then the OpenSSL algorithm in question should be chacha20 -
        // not chacha20-poly1305. and chacha20 is taken care of by the "OpenSSL" engine - not the "OpenSSL (AEAD)"
        // engine
    }

    /**
     * Sets the nonce.
     */
    public function setNonce(string $nonce): void
    {
        /*
          from https://tools.ietf.org/html/rfc7539#page-7

          "Note also that the original ChaCha had a 64-bit nonce and 64-bit
           block count.  We have modified this here to be more consistent with
           recommendations in Section 3.2 of [RFC5116]."
         */
        switch (strlen($nonce)) {
            case 8:  // 64 bits
            case 12: // 96 bits
                break;
            default:
                throw new LengthException('Nonce of size ' . strlen($nonce) . ' not supported by this algorithm. Only 64-bit nonces or 96-bit nonces are supported');
        }

        $this->nonce = $nonce;
        $this->changed = true;
        $this->setEngine();
    }

    /**
     * Setup the self::ENGINE_INTERNAL $engine
     *
     * (re)init, if necessary, the internal cipher $engine
     *
     * _setup() will be called each time if $changed === true
     * typically this happens when using one or more of following public methods:
     *
     * - setKey()
     *
     * - setNonce()
     *
     * - First run of encrypt() / decrypt() with no init-settings
     *
     * @see self::setKey()
     * @see self::setNonce()
     * @see self::disableContinuousBuffer()
     */
    protected function setup(): void
    {
        if (!$this->changed) {
            return;
        }

        $this->enbuffer = $this->debuffer = ['ciphertext' => '', 'counter' => $this->counter];

        $this->changed = $this->nonIVChanged = false;

        if (!isset($this->nonce)) {
            throw new InvalidStateException('No nonce has been defined - call setNonce() first');
        }

        if (!isset($this->key)) {
            throw new InvalidStateException('No key has been defined - call setKey() first');
        }

        if ($this->usePoly1305 && !isset($this->poly1305Key)) {
            if ($this->engine == self::ENGINE_LIBSODIUM || $this->engine == self::ENGINE_OPENSSL_AEAD) {
                $this->usingGeneratedPoly1305Key = true;
                return;
            }
            $this->createPoly1305Key();
        }

        if ($this->engine != self::ENGINE_INTERNAL && $this->engine != self::ENGINE_OPENSSL) {
            return;
        }

        $key = $this->key;
        if (strlen($key) == 16) {
            $constant = 'expand 16-byte k';
            $key .= $key;
        } else {
            $constant = 'expand 32-byte k';
        }

        $this->p1 = $constant . $key;
        $this->p2 = $this->nonce;
        if (strlen($this->nonce) == 8) {
            $this->p2 = "\0\0\0\0" . $this->p2;
        }
    }

    /**
     * The quarterround function
     */
    protected static function quarterRound(int &$a, int &$b, int &$c, int &$d): void
    {
        // in https://datatracker.ietf.org/doc/html/rfc7539#section-2.1 the addition,
        // xor'ing and rotation are all on the same line so i'm keeping it on the same
        // line here as well
        // @codingStandardsIgnoreStart
        $a+= $b; $d = self::leftRotate(self::safe_intval($d) ^ self::safe_intval($a), 16);
        $c+= $d; $b = self::leftRotate(self::safe_intval($b) ^ self::safe_intval($c), 12);
        $a+= $b; $d = self::leftRotate(self::safe_intval($d) ^ self::safe_intval($a), 8);
        $c+= $d; $b = self::leftRotate(self::safe_intval($b) ^ self::safe_intval($c), 7);
        // @codingStandardsIgnoreEnd
    }

    /**
     * The doubleround function
     */
    protected static function doubleRound(int &$x0, int &$x1, int &$x2, int &$x3, int &$x4, int &$x5, int &$x6, int &$x7, int &$x8, int &$x9, int &$x10, int &$x11, int &$x12, int &$x13, int &$x14, int &$x15): void
    {
        // columnRound
        static::quarterRound($x0, $x4, $x8, $x12);
        static::quarterRound($x1, $x5, $x9, $x13);
        static::quarterRound($x2, $x6, $x10, $x14);
        static::quarterRound($x3, $x7, $x11, $x15);
        // rowRound
        static::quarterRound($x0, $x5, $x10, $x15);
        static::quarterRound($x1, $x6, $x11, $x12);
        static::quarterRound($x2, $x7, $x8, $x13);
        static::quarterRound($x3, $x4, $x9, $x14);
    }

    /**
     * The Salsa20 hash function function
     *
     * On my laptop this loop unrolled / function dereferenced version of parent::salsa20 encrypts 1mb of text in
     * 0.65s vs the 0.85s that it takes with the parent method.
     *
     * If we were free to assume that the host OS would always be 64-bits then the if condition in leftRotate could
     * be eliminated and we could knock this done to 0.60s.
     *
     * For comparison purposes, RC4 takes 0.16s and AES in CTR mode with the Eval engine takes 0.48s.
     * AES in CTR mode with the PHP engine takes 1.19s. Salsa20 / ChaCha20 do not benefit as much from the Eval
     * approach due to the fact that there are a lot less variables to de-reference, fewer loops to unroll, etc
     */
    protected static function salsa20(string $x): string
    {
        [, $x0, $x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10, $x11, $x12, $x13, $x14, $x15] = unpack('V*', $x);
        $z0 = $x0;
        $z1 = $x1;
        $z2 = $x2;
        $z3 = $x3;
        $z4 = $x4;
        $z5 = $x5;
        $z6 = $x6;
        $z7 = $x7;
        $z8 = $x8;
        $z9 = $x9;
        $z10 = $x10;
        $z11 = $x11;
        $z12 = $x12;
        $z13 = $x13;
        $z14 = $x14;
        $z15 = $x15;

        // @codingStandardsIgnoreStart
        // columnRound
        $x0 += $x4; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x0), 16);
        $x8 += $x12; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x8), 12);
        $x0 += $x4; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x0), 8);
        $x8 += $x12; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x8), 7);

        $x1 += $x5; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x1), 16);
        $x9 += $x13; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x9), 12);
        $x1 += $x5; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x1), 8);
        $x9 += $x13; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x9), 7);

        $x2 += $x6; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x2), 16);
        $x10 += $x14; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x10), 12);
        $x2 += $x6; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x2), 8);
        $x10 += $x14; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x10), 7);

        $x3 += $x7; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x3), 16);
        $x11 += $x15; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x11), 12);
        $x3 += $x7; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x3), 8);
        $x11 += $x15; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x11), 7);

        // rowRound
        $x0 += $x5; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x0), 16);
        $x10 += $x15; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x10), 12);
        $x0 += $x5; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x0), 8);
        $x10 += $x15; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x10), 7);

        $x1 += $x6; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x1), 16);
        $x11 += $x12; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x11), 12);
        $x1 += $x6; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x1), 8);
        $x11 += $x12; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x11), 7);

        $x2 += $x7; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x2), 16);
        $x8 += $x13; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x8), 12);
        $x2 += $x7; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x2), 8);
        $x8 += $x13; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x8), 7);

        $x3 += $x4; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x3), 16);
        $x9 += $x14; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x9), 12);
        $x3 += $x4; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x3), 8);
        $x9 += $x14; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x9), 7);

        // columnRound
        $x0 += $x4; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x0), 16);
        $x8 += $x12; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x8), 12);
        $x0 += $x4; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x0), 8);
        $x8 += $x12; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x8), 7);

        $x1 += $x5; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x1), 16);
        $x9 += $x13; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x9), 12);
        $x1 += $x5; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x1), 8);
        $x9 += $x13; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x9), 7);

        $x2 += $x6; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x2), 16);
        $x10 += $x14; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x10), 12);
        $x2 += $x6; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x2), 8);
        $x10 += $x14; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x10), 7);

        $x3 += $x7; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x3), 16);
        $x11 += $x15; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x11), 12);
        $x3 += $x7; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x3), 8);
        $x11 += $x15; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x11), 7);

        // rowRound
        $x0 += $x5; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x0), 16);
        $x10 += $x15; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x10), 12);
        $x0 += $x5; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x0), 8);
        $x10 += $x15; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x10), 7);

        $x1 += $x6; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x1), 16);
        $x11 += $x12; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x11), 12);
        $x1 += $x6; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x1), 8);
        $x11 += $x12; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x11), 7);

        $x2 += $x7; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x2), 16);
        $x8 += $x13; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x8), 12);
        $x2 += $x7; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x2), 8);
        $x8 += $x13; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x8), 7);

        $x3 += $x4; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x3), 16);
        $x9 += $x14; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x9), 12);
        $x3 += $x4; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x3), 8);
        $x9 += $x14; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x9), 7);

        // columnRound
        $x0 += $x4; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x0), 16);
        $x8 += $x12; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x8), 12);
        $x0 += $x4; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x0), 8);
        $x8 += $x12; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x8), 7);

        $x1 += $x5; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x1), 16);
        $x9 += $x13; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x9), 12);
        $x1 += $x5; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x1), 8);
        $x9 += $x13; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x9), 7);

        $x2 += $x6; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x2), 16);
        $x10 += $x14; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x10), 12);
        $x2 += $x6; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x2), 8);
        $x10 += $x14; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x10), 7);

        $x3 += $x7; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x3), 16);
        $x11 += $x15; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x11), 12);
        $x3 += $x7; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x3), 8);
        $x11 += $x15; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x11), 7);

        // rowRound
        $x0 += $x5; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x0), 16);
        $x10 += $x15; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x10), 12);
        $x0 += $x5; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x0), 8);
        $x10 += $x15; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x10), 7);

        $x1 += $x6; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x1), 16);
        $x11 += $x12; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x11), 12);
        $x1 += $x6; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x1), 8);
        $x11 += $x12; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x11), 7);

        $x2 += $x7; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x2), 16);
        $x8 += $x13; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x8), 12);
        $x2 += $x7; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x2), 8);
        $x8 += $x13; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x8), 7);

        $x3 += $x4; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x3), 16);
        $x9 += $x14; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x9), 12);
        $x3 += $x4; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x3), 8);
        $x9 += $x14; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x9), 7);

        // columnRound
        $x0 += $x4; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x0), 16);
        $x8 += $x12; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x8), 12);
        $x0 += $x4; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x0), 8);
        $x8 += $x12; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x8), 7);

        $x1 += $x5; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x1), 16);
        $x9 += $x13; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x9), 12);
        $x1 += $x5; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x1), 8);
        $x9 += $x13; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x9), 7);

        $x2 += $x6; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x2), 16);
        $x10 += $x14; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x10), 12);
        $x2 += $x6; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x2), 8);
        $x10 += $x14; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x10), 7);

        $x3 += $x7; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x3), 16);
        $x11 += $x15; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x11), 12);
        $x3 += $x7; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x3), 8);
        $x11 += $x15; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x11), 7);

        // rowRound
        $x0 += $x5; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x0), 16);
        $x10 += $x15; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x10), 12);
        $x0 += $x5; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x0), 8);
        $x10 += $x15; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x10), 7);

        $x1 += $x6; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x1), 16);
        $x11 += $x12; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x11), 12);
        $x1 += $x6; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x1), 8);
        $x11 += $x12; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x11), 7);

        $x2 += $x7; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x2), 16);
        $x8 += $x13; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x8), 12);
        $x2 += $x7; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x2), 8);
        $x8 += $x13; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x8), 7);

        $x3 += $x4; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x3), 16);
        $x9 += $x14; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x9), 12);
        $x3 += $x4; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x3), 8);
        $x9 += $x14; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x9), 7);

        // columnRound
        $x0 += $x4; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x0), 16);
        $x8 += $x12; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x8), 12);
        $x0 += $x4; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x0), 8);
        $x8 += $x12; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x8), 7);

        $x1 += $x5; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x1), 16);
        $x9 += $x13; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x9), 12);
        $x1 += $x5; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x1), 8);
        $x9 += $x13; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x9), 7);

        $x2 += $x6; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x2), 16);
        $x10 += $x14; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x10), 12);
        $x2 += $x6; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x2), 8);
        $x10 += $x14; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x10), 7);

        $x3 += $x7; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x3), 16);
        $x11 += $x15; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x11), 12);
        $x3 += $x7; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x3), 8);
        $x11 += $x15; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x11), 7);

        // rowRound
        $x0 += $x5; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x0), 16);
        $x10 += $x15; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x10), 12);
        $x0 += $x5; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x0), 8);
        $x10 += $x15; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x10), 7);

        $x1 += $x6; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x1), 16);
        $x11 += $x12; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x11), 12);
        $x1 += $x6; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x1), 8);
        $x11 += $x12; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x11), 7);

        $x2 += $x7; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x2), 16);
        $x8 += $x13; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x8), 12);
        $x2 += $x7; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x2), 8);
        $x8 += $x13; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x8), 7);

        $x3 += $x4; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x3), 16);
        $x9 += $x14; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x9), 12);
        $x3 += $x4; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x3), 8);
        $x9 += $x14; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x9), 7);

        // columnRound
        $x0 += $x4; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x0), 16);
        $x8 += $x12; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x8), 12);
        $x0 += $x4; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x0), 8);
        $x8 += $x12; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x8), 7);

        $x1 += $x5; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x1), 16);
        $x9 += $x13; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x9), 12);
        $x1 += $x5; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x1), 8);
        $x9 += $x13; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x9), 7);

        $x2 += $x6; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x2), 16);
        $x10 += $x14; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x10), 12);
        $x2 += $x6; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x2), 8);
        $x10 += $x14; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x10), 7);

        $x3 += $x7; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x3), 16);
        $x11 += $x15; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x11), 12);
        $x3 += $x7; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x3), 8);
        $x11 += $x15; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x11), 7);

        // rowRound
        $x0 += $x5; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x0), 16);
        $x10 += $x15; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x10), 12);
        $x0 += $x5; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x0), 8);
        $x10 += $x15; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x10), 7);

        $x1 += $x6; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x1), 16);
        $x11 += $x12; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x11), 12);
        $x1 += $x6; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x1), 8);
        $x11 += $x12; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x11), 7);

        $x2 += $x7; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x2), 16);
        $x8 += $x13; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x8), 12);
        $x2 += $x7; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x2), 8);
        $x8 += $x13; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x8), 7);

        $x3 += $x4; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x3), 16);
        $x9 += $x14; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x9), 12);
        $x3 += $x4; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x3), 8);
        $x9 += $x14; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x9), 7);

        // columnRound
        $x0 += $x4; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x0), 16);
        $x8 += $x12; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x8), 12);
        $x0 += $x4; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x0), 8);
        $x8 += $x12; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x8), 7);

        $x1 += $x5; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x1), 16);
        $x9 += $x13; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x9), 12);
        $x1 += $x5; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x1), 8);
        $x9 += $x13; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x9), 7);

        $x2 += $x6; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x2), 16);
        $x10 += $x14; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x10), 12);
        $x2 += $x6; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x2), 8);
        $x10 += $x14; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x10), 7);

        $x3 += $x7; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x3), 16);
        $x11 += $x15; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x11), 12);
        $x3 += $x7; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x3), 8);
        $x11 += $x15; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x11), 7);

        // rowRound
        $x0 += $x5; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x0), 16);
        $x10 += $x15; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x10), 12);
        $x0 += $x5; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x0), 8);
        $x10 += $x15; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x10), 7);

        $x1 += $x6; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x1), 16);
        $x11 += $x12; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x11), 12);
        $x1 += $x6; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x1), 8);
        $x11 += $x12; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x11), 7);

        $x2 += $x7; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x2), 16);
        $x8 += $x13; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x8), 12);
        $x2 += $x7; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x2), 8);
        $x8 += $x13; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x8), 7);

        $x3 += $x4; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x3), 16);
        $x9 += $x14; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x9), 12);
        $x3 += $x4; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x3), 8);
        $x9 += $x14; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x9), 7);

        // columnRound
        $x0 += $x4; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x0), 16);
        $x8 += $x12; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x8), 12);
        $x0 += $x4; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x0), 8);
        $x8 += $x12; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x8), 7);

        $x1 += $x5; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x1), 16);
        $x9 += $x13; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x9), 12);
        $x1 += $x5; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x1), 8);
        $x9 += $x13; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x9), 7);

        $x2 += $x6; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x2), 16);
        $x10 += $x14; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x10), 12);
        $x2 += $x6; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x2), 8);
        $x10 += $x14; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x10), 7);

        $x3 += $x7; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x3), 16);
        $x11 += $x15; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x11), 12);
        $x3 += $x7; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x3), 8);
        $x11 += $x15; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x11), 7);

        // rowRound
        $x0 += $x5; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x0), 16);
        $x10 += $x15; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x10), 12);
        $x0 += $x5; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x0), 8);
        $x10 += $x15; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x10), 7);

        $x1 += $x6; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x1), 16);
        $x11 += $x12; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x11), 12);
        $x1 += $x6; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x1), 8);
        $x11 += $x12; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x11), 7);

        $x2 += $x7; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x2), 16);
        $x8 += $x13; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x8), 12);
        $x2 += $x7; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x2), 8);
        $x8 += $x13; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x8), 7);

        $x3 += $x4; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x3), 16);
        $x9 += $x14; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x9), 12);
        $x3 += $x4; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x3), 8);
        $x9 += $x14; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x9), 7);

        // columnRound
        $x0 += $x4; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x0), 16);
        $x8 += $x12; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x8), 12);
        $x0 += $x4; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x0), 8);
        $x8 += $x12; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x8), 7);

        $x1 += $x5; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x1), 16);
        $x9 += $x13; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x9), 12);
        $x1 += $x5; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x1), 8);
        $x9 += $x13; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x9), 7);

        $x2 += $x6; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x2), 16);
        $x10 += $x14; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x10), 12);
        $x2 += $x6; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x2), 8);
        $x10 += $x14; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x10), 7);

        $x3 += $x7; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x3), 16);
        $x11 += $x15; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x11), 12);
        $x3 += $x7; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x3), 8);
        $x11 += $x15; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x11), 7);

        // rowRound
        $x0 += $x5; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x0), 16);
        $x10 += $x15; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x10), 12);
        $x0 += $x5; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x0), 8);
        $x10 += $x15; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x10), 7);

        $x1 += $x6; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x1), 16);
        $x11 += $x12; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x11), 12);
        $x1 += $x6; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x1), 8);
        $x11 += $x12; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x11), 7);

        $x2 += $x7; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x2), 16);
        $x8 += $x13; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x8), 12);
        $x2 += $x7; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x2), 8);
        $x8 += $x13; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x8), 7);

        $x3 += $x4; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x3), 16);
        $x9 += $x14; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x9), 12);
        $x3 += $x4; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x3), 8);
        $x9 += $x14; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x9), 7);

        // columnRound
        $x0 += $x4; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x0), 16);
        $x8 += $x12; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x8), 12);
        $x0 += $x4; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x0), 8);
        $x8 += $x12; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x8), 7);

        $x1 += $x5; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x1), 16);
        $x9 += $x13; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x9), 12);
        $x1 += $x5; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x1), 8);
        $x9 += $x13; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x9), 7);

        $x2 += $x6; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x2), 16);
        $x10 += $x14; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x10), 12);
        $x2 += $x6; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x2), 8);
        $x10 += $x14; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x10), 7);

        $x3 += $x7; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x3), 16);
        $x11 += $x15; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x11), 12);
        $x3 += $x7; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x3), 8);
        $x11 += $x15; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x11), 7);

        // rowRound
        $x0 += $x5; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x0), 16);
        $x10 += $x15; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x10), 12);
        $x0 += $x5; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x0), 8);
        $x10 += $x15; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x10), 7);

        $x1 += $x6; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x1), 16);
        $x11 += $x12; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x11), 12);
        $x1 += $x6; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x1), 8);
        $x11 += $x12; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x11), 7);

        $x2 += $x7; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x2), 16);
        $x8 += $x13; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x8), 12);
        $x2 += $x7; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x2), 8);
        $x8 += $x13; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x8), 7);

        $x3 += $x4; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x3), 16);
        $x9 += $x14; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x9), 12);
        $x3 += $x4; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x3), 8);
        $x9 += $x14; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x9), 7);
        // @codingStandardsIgnoreEnd

        $x0 += $z0;
        $x1 += $z1;
        $x2 += $z2;
        $x3 += $z3;
        $x4 += $z4;
        $x5 += $z5;
        $x6 += $z6;
        $x7 += $z7;
        $x8 += $z8;
        $x9 += $z9;
        $x10 += $z10;
        $x11 += $z11;
        $x12 += $z12;
        $x13 += $z13;
        $x14 += $z14;
        $x15 += $z15;

        return pack('V*', self::safe_intval($x0), self::safe_intval($x1), self::safe_intval($x2), self::safe_intval($x3), self::safe_intval($x4), self::safe_intval($x5), self::safe_intval($x6), self::safe_intval($x7), self::safe_intval($x8), self::safe_intval($x9), self::safe_intval($x10), self::safe_intval($x11), self::safe_intval($x12), self::safe_intval($x13), self::safe_intval($x14), self::safe_intval($x15));
    }
}
<?php

/**
 * Wrapper around hash() and hash_hmac() functions supporting truncated hashes
 * such as sha256-96.  Any hash algorithm returned by hash_algos() (and
 * truncated versions thereof) are supported.
 *
 * If {@link self::setKey() setKey()} is called, {@link self::hash() hash()} will
 * return the HMAC as opposed to the hash.
 *
 * Here's a short example of how to use this library:
 * <code>
 * <?php
 *    include 'vendor/autoload.php';
 *
 *    $hash = new \phpseclib4\Crypt\Hash('sha512');
 *
 *    $hash->setKey('abcdefg');
 *
 *    echo base64_encode($hash->hash('abcdefg'));
 * ?>
 * </code>
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2007-2026 Jim Wigginton
 * @author    Andreas Fischer <bantu@phpbb.com>
 * @copyright 2007-2026 Andreas Fischer
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt;

use phpseclib4\Exception\{
    BadMethodCallException,
    InvalidStateException,
    LengthException,
    UnexpectedValueException,
    UnsupportedAlgorithmException,
    UnsupportedValueException
};
use phpseclib4\Math\{BigInteger, PrimeField};

/**
 * @author  Jim Wigginton <terrafrost@php.net>
 * @author  Andreas Fischer <bantu@phpbb.com>
 */
class Hash
{
    use Common\Traits\PKCS12Helper;

    /**
     * Padding Types
     */
    public const PADDING_KECCAK = 1;

    /**
     * Padding Types
     */
    public const PADDING_SHA3 = 2;

    /**
     * Padding Types
     */
    public const PADDING_SHAKE = 3;

    /**
     * Padding Type
     *
     * Only used by SHA3
     */
    private int $paddingType = 0;

    /**
     * Hash Parameter
     *
     * @see self::setHash()
     */
    private string $hashParam;

    /**
     * Byte-length of hash output (Internal HMAC)
     *
     * @see self::setHash()
     */
    private int $length;

    /**
     * Hash Algorithm
     *
     * @see self::setHash()
     */
    private string|array $algo;

    /**
     * Key
     *
     * @see self::setKey()
     */
    private ?string $key = null;

    /**
     * Nonce
     *
     * @see self::setNonce()
     */
    private ?string $nonce = null;

    /**
     * Hash Parameters
     */
    private array $parameters = [];

    /**
     * Computed Key
     *
     * @see self::_computeKey()
     */
    private ?string $computedKey = null;

    /**
     * Outer XOR (Internal HMAC)
     *
     * Used only for sha512
     *
     * @see self::hash()
     */
    private string $opad;

    /**
     * Inner XOR (Internal HMAC)
     *
     * Used only for sha512
     *
     * @see self::hash()
     */
    private string $ipad;

    /**
     * Recompute AES Key
     *
     * Used only for umac
     *
     * @see self::hash()
     */
    private bool $recomputeAESKey;

    /**
     * umac cipher object
     *
     * @see self::hash()
     */
    private AES $c;

    /**
     * umac pad
     *
     * @see self::hash()
     */
    private string $pad;

    /**
     * Block Size
     */
    private int $blockSize;

    /**#@+
     * UMAC variables
     */
    private static PrimeField $factory36;
    private static PrimeField $factory64;
    private static PrimeField $factory128;
    private static PrimeField\Integer $offset64;
    private static PrimeField\Integer $offset128;
    private static PrimeField\Integer $marker64;
    private static PrimeField\Integer $marker128;
    private static PrimeField\Integer $maxwordrange64;
    private static PrimeField\Integer $maxwordrange128;
    /**#@-*/

    /**#@+
     * AES_CMAC variables
     */
    //private string $k1;
    //private string $k2;
    /**#@-*/

    /**
     * Default Constructor.
     */
    public function __construct(string $hash = 'sha256')
    {
        $this->setHash($hash);
    }

    /**
     * Sets the key for HMACs
     *
     * Keys can be of any length.
     */
    public function setKey(#[\SensitiveParameter] ?string $key = null): void
    {
        $this->key = $key;
        $this->computeKey();
        $this->recomputeAESKey = true;
    }

    /**
     * Sets the nonce for UMACs
     *
     * Keys can be of any length.
     */
    public function setNonce(?string $nonce = null): void
    {
        switch (true) {
            case !is_string($nonce):
            case strlen($nonce) > 0 && strlen($nonce) <= 16:
                $this->recomputeAESKey = true;
                $this->nonce = $nonce;
                return;
        }

        throw new LengthException('The nonce length must be between 1 and 16 bytes, inclusive');
    }

    /**
     * Pre-compute the key used by the HMAC
     *
     * Quoting http://tools.ietf.org/html/rfc2104#section-2, "Applications that use keys longer than B bytes
     * will first hash the key using H and then use the resultant L byte string as the actual key to HMAC."
     *
     * As documented in https://www.reddit.com/r/PHP/comments/9nct2l/symfonypolyfill_hash_pbkdf2_correct_fix_for/
     * when doing an HMAC multiple times it's faster to compute the hash once instead of computing it during
     * every call
     */
    private function computeKey(): void
    {
        if (!isset($this->key)) {
            $this->computedKey = null;
            return;
        }

        if (strlen($this->key) <= $this->getBlockLengthInBytes()) {
            $this->computedKey = $this->key;
            return;
        }

        $this->computedKey = is_array($this->algo) ?
            ($this->algo)($this->key) :
            hash($this->algo, $this->key, true);
    }

    /**
     * Gets the hash function.
     *
     * As set by the constructor or by the setHash() method.
     */
    public function getHash(): string
    {
        return $this->hashParam;
    }

    /**
     * Sets the hash function.
     */
    public function setHash(string $hash): void
    {
        $oldHash = $this->hashParam ?? null;
        $this->hashParam = $hash = strtolower(str_replace('/', '-', $hash));
        switch ($hash) {
            case 'umac-32':
            case 'umac-64':
            case 'umac-96':
            case 'umac-128':
                if ($oldHash != $this->hashParam) {
                    $this->recomputeAESKey = true;
                }
                $this->blockSize = 128;
                $this->length = abs((int) substr($hash, -3)) >> 3;
                $this->algo = 'umac';
                return;
            case 'aes_cmac':
                if ($oldHash != $this->hashParam) {
                    $this->recomputeAESKey = true;
                }
                $this->blockSize = 128;
                $this->length = 16;
                $this->algo = 'aes_cmac';
                return;
            case 'md2-96':
            case 'md5-96':
            case 'sha1-96':
            case 'sha224-96':
            case 'sha256-96':
            case 'sha384-96':
            case 'sha512-96':
            case 'sha512-224-96':
            case 'sha512-256-96':
                $hash = substr($hash, 0, -3);
                $this->length = 12; // 96 / 8 = 12
                break;
            case 'md2':
            case 'md5':
                $this->length = 16;
                break;
            case 'sha1':
                $this->length = 20;
                break;
            case 'sha224':
            case 'sha512-224':
            case 'sha3-224':
                $this->length = 28;
                break;
            case 'keccak256':
                $this->paddingType = self::PADDING_KECCAK;
                // no break
            case 'sha256':
            case 'sha512-256':
            case 'sha3-256':
                $this->length = 32;
                break;
            case 'sha384':
            case 'sha3-384':
                $this->length = 48;
                break;
            case 'sha512':
            case 'sha3-512':
                $this->length = 64;
                break;
            default:
                if (preg_match('#^(shake(?:128|256))-(\d+)$#', $hash, $matches)) {
                    $this->paddingType = self::PADDING_SHAKE;
                    $hash = $matches[1];
                    $this->length = $matches[2] >> 3;
                } else {
                    throw new UnsupportedAlgorithmException("$hash is not a supported algorithm");
                }
        }

        $this->blockSize = match ($hash) {
            'md2', 'md2-96' => 128,
            'md5-96', 'sha1-96', 'sha224-96', 'sha256-96', 'md5', 'sha1', 'sha224', 'sha256' => 512,
            'sha3-224' => 1152, // 1600 - 2*224
            'sha3-256', 'shake256', 'keccak256' => 1088, // 1600 - 2*256
            'sha3-384' => 832, // 1600 - 2*384
            'sha3-512' => 576, // 1600 - 2*512
            'shake128' => 1344, // 1600 - 2*128
            default => 1024
        };

        if (in_array(substr($hash, 0, 5), ['shake', 'kecca'])) {
            //preg_match('#(\d+)$#', $hash, $matches);
            //$this->parameters['capacity'] = 2 * $matches[1]; // 1600 - $this->blockSize
            //$this->parameters['rate'] = 1600 - $this->parameters['capacity']; // == $this->blockSize
            if (!$this->paddingType) {
                $this->paddingType = self::PADDING_SHA3;
            }
            $this->parameters = [
                'capacity' => 1600 - $this->blockSize,
                'rate' => $this->blockSize,
                'length' => $this->length,
                'padding' => $this->paddingType,
            ];
            $hash = ['phpseclib4\Crypt\Hash', PHP_INT_SIZE == 8 ? 'sha3_64' : 'sha3_32'];
        }

        if (is_array($hash)) {
            $b = $this->blockSize >> 3;
            $this->ipad = str_repeat(chr(0x36), $b);
            $this->opad = str_repeat(chr(0x5C), $b);
        }

        // PHP's built in hash function does sha3-256 but sha512/256 so we'll update those accordingly
        $this->algo = match ($hash) {
            'sha512-224' => 'sha512/224',
            'sha512-256' => 'sha512/256',
            default => $hash
        };

        $this->computeKey();
    }

    /**
     * KDF: Key-Derivation Function
     *
     * The key-derivation function generates pseudorandom bits used to key the hash functions.
     *
     * @param int $index a non-negative integer less than 2^64
     * @param int $numbytes a non-negative integer less than 2^64
     * @return string string of length numbytes bytes
     */
    private function kdf(int $index, int $numbytes): string
    {
        $this->c->setIV(pack('N4', 0, $index, 0, 1));

        return $this->c->encrypt(str_repeat("\0", $numbytes));
    }

    /**
     * PDF Algorithm
     *
     * @return string string of length taglen bytes.
     */
    private function pdf(): string
    {
        //$k = $this->key;
        $nonce = $this->nonce;
        $taglen = $this->length;

        //
        // Extract and zero low bit(s) of Nonce if needed
        //
        if ($taglen <= 8) {
            $last = strlen($nonce) - 1;
            $mask = $taglen == 4 ? "\3" : "\1";
            $index = $nonce[$last] & $mask;
            $nonce[$last] = $nonce[$last] ^ $index;
        }

        //
        // Make Nonce BLOCKLEN bytes by appending zeroes if needed
        //
        $nonce = str_pad($nonce, 16, "\0");

        //
        // Generate subkey, encipher and extract indexed substring
        //
        $kp = $this->kdf(0, 16);
        $c = new AES('ctr');
        $c->disablePadding();
        $c->setKey($kp);
        $c->setIV($nonce);
        $t = $c->encrypt("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");

        // we could use ord() but per https://paragonie.com/blog/2016/06/constant-time-encoding-boring-cryptography-rfc-4648-and-you
        // unpack() doesn't leak timing info
        return $taglen <= 8 ?
            substr($t, unpack('C', $index)[1] * $taglen, $taglen) :
            substr($t, 0, $taglen);
    }

    /**
     * UHASH Algorithm
     *
     * @param string $m string of length less than 2^67 bits.
     * @param int $taglen the integer 4, 8, 12 or 16.
     * @return string string of length taglen bytes.
     */
    private function uhash(string $m, int $taglen): string
    {
        //
        // One internal iteration per 4 bytes of output
        //
        $iters = $taglen >> 2;

        //
        // Define total key needed for all iterations using KDF.
        // L1Key reuses most key material between iterations.
        //
        //$L1Key  = $this->kdf(1, 1024 + ($iters - 1) * 16);
        $L1Key  = $this->kdf(1, (1024 + ($iters - 1)) * 16);
        $L2Key  = $this->kdf(2, $iters * 24);
        $L3Key1 = $this->kdf(3, $iters * 64);
        $L3Key2 = $this->kdf(4, $iters * 4);

        //
        // For each iteration, extract key and do three-layer hash.
        // If bytelength(M) <= 1024, then skip L2-HASH.
        //
        $y = '';
        for ($i = 0; $i < $iters; $i++) {
            $L1Key_i  = substr($L1Key, $i * 16, 1024);
            $L2Key_i  = substr($L2Key, $i * 24, 24);
            $L3Key1_i = substr($L3Key1, $i * 64, 64);
            $L3Key2_i = substr($L3Key2, $i * 4, 4);

            $a = self::L1Hash($L1Key_i, $m);
            $b = strlen($m) <= 1024 ? "\0\0\0\0\0\0\0\0$a" : self::L2Hash($L2Key_i, $a);
            $c = self::L3Hash($L3Key1_i, $L3Key2_i, $b);
            $y .= $c;
        }

        return $y;
    }

    /**
     * L1-HASH Algorithm
     *
     * The first-layer hash breaks the message into 1024-byte chunks and
     * hashes each with a function called NH.  Concatenating the results
     * forms a string, which is up to 128 times shorter than the original.
     *
     * @param string $k string of length 1024 bytes.
     * @param string $m string of length less than 2^67 bits.
     * @return string string of length (8 * ceil(bitlength(M)/8192)) bytes.
     */
    private static function L1Hash(string $k, string $m): string
    {
        //
        // Break M into 1024 byte chunks (final chunk may be shorter)
        //
        $m = str_split($m, 1024);

        //
        // For each chunk, except the last: endian-adjust, NH hash
        // and add bit-length.  Use results to build Y.
        //
        $length = 1024 * 8;
        $y = '';

        for ($i = 0; $i < count($m) - 1; $i++) {
            $m[$i] = pack('N*', ...unpack('V*', $m[$i])); // ENDIAN-SWAP
            $y .= PHP_INT_SIZE == 8 ?
                static::nh64($k, $m[$i], $length) :
                static::nh32($k, $m[$i], $length);
        }

        //
        // For the last chunk: pad to 32-byte boundary, endian-adjust,
        // NH hash and add bit-length.  Concatenate the result to Y.
        //
        $length = count($m) ? strlen($m[$i]) : 0;
        $pad = 32 - ($length % 32);
        $pad = max(32, $length + $pad % 32);
        $m[$i] = str_pad($m[$i] ?? '', $pad, "\0"); // zeropad
        $m[$i] = pack('N*', ...unpack('V*', $m[$i])); // ENDIAN-SWAP

        $y .= PHP_INT_SIZE == 8 ?
            static::nh64($k, $m[$i], $length * 8) :
            static::nh32($k, $m[$i], $length * 8);

        return $y;
    }

    /**
     * 32-bit safe 64-bit Multiply with 2x 32-bit ints
     *
     * @return string $x * $y
     */
    private static function mul32_64(int $x, int $y): string
    {
        // see mul64() for a more detailed explanation of how this works

        $x1 = ($x >> 16) & 0xFFFF;
        $x0 = $x & 0xFFFF;

        $y1 = ($y >> 16) & 0xFFFF;
        $y0 = $y & 0xFFFF;

        // the following 3x lines will possibly yield floats
        $z2 = $x1 * $y1;
        $z0 = $x0 * $y0;
        $z1 = $x1 * $y0 + $x0 * $y1;

        $a = intval(fmod($z0, 65536));
        $b = intval($z0 / 65536) + intval(fmod($z1, 65536));
        $c = intval($z1 / 65536) + intval(fmod($z2, 65536)) + intval($b / 65536);
        $b = intval(fmod($b, 65536));
        $d = intval($z2 / 65536) + intval($c / 65536);
        $c = intval(fmod($c, 65536));
        $d = intval(fmod($d, 65536));

        return pack('n4', $d, $c, $b, $a);
    }

    /**
     * 32-bit safe 64-bit Addition with 2x 64-bit strings
     *
     * @return string $x + $y
     */
    private static function add32_64(string $x, string $y): string
    {
        [, $x1, $x2, $x3, $x4] = unpack('n4', $x);
        [, $y1, $y2, $y3, $y4] = unpack('n4', $y);
        $a = $x4 + $y4;
        $b = $x3 + $y3 + ($a >> 16);
        $c = $x2 + $y2 + ($b >> 16);
        $d = $x1 + $y1 + ($c >> 16);
        return pack('n4', $d, $c, $b, $a);
    }

    /**
     * 32-bit safe 32-bit Addition with 2x 32-bit strings
     *
     * @return int $x + $y
     */
    private static function add32(int $x, int $y): int
    {
        // see add64() for a more detailed explanation of how this works

        $x1 = $x & 0xFFFF;
        $x2 = ($x >> 16) & 0xFFFF;
        $y1 = $y & 0xFFFF;
        $y2 = ($y >> 16) & 0xFFFF;

        $a = $x1 + $y1;
        $b = ($x2 + $y2 + ($a >> 16)) << 16;
        $a &= 0xFFFF;

        return $a | $b;
    }

    /**
     * NH Algorithm / 32-bit safe
     *
     * @param string $k string of length 1024 bytes.
     * @param string $m string with length divisible by 32 bytes.
     * @return string string of length 8 bytes.
     */
    private static function nh32(string $k, string $m, int $length): string
    {
        //
        // Break M and K into 4-byte chunks
        //
        $k = unpack('N*', $k);
        $m = unpack('N*', $m);
        $t = count($m);

        //
        // Perform NH hash on the chunks, pairing words for multiplication
        // which are 4 apart to accommodate vector-parallelism.
        //
        $i = 1;
        $y = "\0\0\0\0\0\0\0\0";
        while ($i <= $t) {
            $temp  = self::add32($m[$i], $k[$i]);
            $temp2 = self::add32($m[$i + 4], $k[$i + 4]);
            $y = self::add32_64($y, self::mul32_64($temp, $temp2));

            $temp  = self::add32($m[$i + 1], $k[$i + 1]);
            $temp2 = self::add32($m[$i + 5], $k[$i + 5]);
            $y = self::add32_64($y, self::mul32_64($temp, $temp2));

            $temp  = self::add32($m[$i + 2], $k[$i + 2]);
            $temp2 = self::add32($m[$i + 6], $k[$i + 6]);
            $y = self::add32_64($y, self::mul32_64($temp, $temp2));

            $temp  = self::add32($m[$i + 3], $k[$i + 3]);
            $temp2 = self::add32($m[$i + 7], $k[$i + 7]);
            $y = self::add32_64($y, self::mul32_64($temp, $temp2));

            $i += 8;
        }

        return self::add32_64($y, pack('N2', 0, $length));
    }

    /**
     * 64-bit Multiply with 2x 32-bit ints
     */
    private static function mul64(int $x, int $y): int
    {
        // since PHP doesn't implement unsigned integers we'll implement them with signed integers
        // to do this we'll use karatsuba multiplication

        $x1 = $x >> 16;
        $x0 = $x & 0xFFFF;

        $y1 = $y >> 16;
        $y0 = $y & 0xFFFF;

        $z2 = $x1 * $y1; // up to 32 bits long
        $z0 = $x0 * $y0; // up to 32 bits long
        $z1 = $x1 * $y0 + $x0 * $y1; // up to 33 bit long
        // normally karatsuba multiplication calculates $z1 thusly:
        //$z1 = ($x1 + $x0) * ($y0 + $y1) - $z2 - $z0;
        // the idea being to eliminate one extra multiplication. for arbitrary precision math that makes sense
        // but not for this purpose

        // at this point karatsuba would normally return this:
        //return ($z2 << 64) + ($z1 << 32) + $z0;
        // the problem is that the output could be out of range for signed 64-bit ints,
        // which would cause PHP to switch to floats, which would risk losing the lower few bits
        // as such we'll OR 4x 16-bit blocks together like so:
        /*
          ........  |  ........  |  ........  |  ........
          upper $z2 |  lower $z2 |  lower $z1 |  lower $z0
                    | +upper $z1 | +upper $z0 |
         +   $carry | +   $carry |            |
        */
        // technically upper $z1 is 17 bit - not 16 - but the most significant digit of that will
        // just get added to $carry

        $a = $z0 & 0xFFFF;
        $b = ($z0 >> 16) + ($z1 & 0xFFFF);
        $c = ($z1 >> 16) + ($z2 & 0xFFFF) + ($b >> 16);
        $b = ($b & 0xFFFF) << 16;
        $d = ($z2 >> 16) + ($c >> 16);
        $c = ($c & 0xFFFF) << 32;
        $d = ($d & 0xFFFF) << 48;

        return $a | $b | $c | $d;
    }

    /**
     * 64-bit Addition with 2x 64-bit ints
     */
    private static function add64(int $x, int $y): int
    {
        // doing $x + $y risks returning a result that's out of range for signed 64-bit ints
        // in that event PHP would convert the result to a float and precision would be lost
        // so we'll just add 2x 32-bit ints together like so:
        /*
           ........ | ........
           upper $x | lower $x
          +upper $y |+lower $y
          +  $carry |
        */
        $x1 = $x & 0xFFFFFFFF;
        $x2 = ($x >> 32) & 0xFFFFFFFF;
        $y1 = $y & 0xFFFFFFFF;
        $y2 = ($y >> 32) & 0xFFFFFFFF;

        $a = $x1 + $y1;
        $b = ($x2 + $y2 + ($a >> 32)) << 32;
        $a &= 0xFFFFFFFF;

        return $a | $b;
    }

    /**
     * NH Algorithm / 64-bit safe
     *
     * @param string $k string of length 1024 bytes.
     * @param string $m string with length divisible by 32 bytes.
     * @return string string of length 8 bytes.
     */
    private static function nh64(string $k, string $m, int $length): string
    {
        //
        // Break M and K into 4-byte chunks
        //
        $k = unpack('N*', $k);
        $m = unpack('N*', $m);
        $t = count($m);

        //
        // Perform NH hash on the chunks, pairing words for multiplication
        // which are 4 apart to accommodate vector-parallelism.
        //
        $i = 1;
        $y = 0;
        while ($i <= $t) {
            $temp  = ($m[$i] + $k[$i]) & 0xFFFFFFFF;
            $temp2 = ($m[$i + 4] + $k[$i + 4]) & 0xFFFFFFFF;
            $y = self::add64($y, self::mul64($temp, $temp2));

            $temp  = ($m[$i + 1] + $k[$i + 1]) & 0xFFFFFFFF;
            $temp2 = ($m[$i + 5] + $k[$i + 5]) & 0xFFFFFFFF;
            $y = self::add64($y, self::mul64($temp, $temp2));

            $temp  = ($m[$i + 2] + $k[$i + 2]) & 0xFFFFFFFF;
            $temp2 = ($m[$i + 6] + $k[$i + 6]) & 0xFFFFFFFF;
            $y = self::add64($y, self::mul64($temp, $temp2));

            $temp  = ($m[$i + 3] + $k[$i + 3]) & 0xFFFFFFFF;
            $temp2 = ($m[$i + 7] + $k[$i + 7]) & 0xFFFFFFFF;
            $y = self::add64($y, self::mul64($temp, $temp2));

            $i += 8;
        }

        return pack('J', self::add64($y, $length));
    }

    /**
     * L2-HASH: Second-Layer Hash
     *
     * The second-layer rehashes the L1-HASH output using a polynomial hash
     * called POLY.  If the L1-HASH output is long, then POLY is called once
     * on a prefix of the L1-HASH output and called using different settings
     * on the remainder.  (This two-step hashing of the L1-HASH output is
     * needed only if the message length is greater than 16 megabytes.)
     * Careful implementation of POLY is necessary to avoid a possible
     * timing attack (see Section 6.6 for more information).
     *
     * @param string $k string of length 24 bytes.
     * @param string $m string of length less than 2^64 bytes.
     * @return string string of length 16 bytes.
     */
    private static function L2Hash(string $k, string $m): string
    {
        //
        //  Extract keys and restrict to special key-sets
        //
        $k64 = $k & "\x01\xFF\xFF\xFF\x01\xFF\xFF\xFF";
        $k64 = new BigInteger($k64, 256);
        $k128 = substr($k, 8) & "\x01\xFF\xFF\xFF\x01\xFF\xFF\xFF\x01\xFF\xFF\xFF\x01\xFF\xFF\xFF";
        $k128 = new BigInteger($k128, 256);

        //
        // If M is no more than 2^17 bytes, hash under 64-bit prime,
        // otherwise, hash first 2^17 bytes under 64-bit prime and
        // remainder under 128-bit prime.
        //
        if (strlen($m) <= 0x20000) { // 2^14 64-bit words
            $y = self::poly(64, self::$maxwordrange64, $k64, $m);
        } else {
            $m_1 = substr($m, 0, 0x20000); // 1 << 17
            $m_2 = substr($m, 0x20000) . "\x80";
            $length = strlen($m_2);
            $pad = 16 - ($length % 16);
            $pad %= 16;
            $m_2 = str_pad($m_2, $length + $pad, "\0"); // zeropad
            $y = self::poly(64, self::$maxwordrange64, $k64, $m_1);
            $y = str_pad($y, 16, "\0", STR_PAD_LEFT);
            $y = self::poly(128, self::$maxwordrange128, $k128, $y . $m_2);
        }

        return str_pad($y, 16, "\0", STR_PAD_LEFT);
    }

    /**
     * POLY Algorithm
     *
     * @param int $wordbits the integer 64 or 128.
     * @param PrimeField\Integer $maxwordrange positive integer less than 2^wordbits.
     * @param BigInteger $k integer in the range 0 ... prime(wordbits) - 1.
     * @param string $m string with length divisible by (wordbits / 8) bytes.
     * @return string in the range 0 ... prime(wordbits) - 1.
     */
    private static function poly(int $wordbits, PrimeField\Integer $maxwordrange, BigInteger $k, string $m): string
    {
        //
        // Define constants used for fixing out-of-range words
        //
        $wordbytes = $wordbits >> 3;
        if ($wordbits == 128) {
            $factory = self::$factory128;
            $offset = self::$offset128;
            $marker = self::$marker128;
        } else {
            $factory = self::$factory64;
            $offset = self::$offset64;
            $marker = self::$marker64;
        }

        $k = $factory->newInteger($k);

        //
        // Break M into chunks of length wordbytes bytes
        //
        $m_i = str_split($m, $wordbytes);

        //
        // Each input word m is compared with maxwordrange.  If not smaller
        // then 'marker' and (m - offset), both in range, are hashed.
        //
        $y = $factory->newInteger(new BigInteger(1));
        foreach ($m_i as $m) {
            $m = $factory->newInteger(new BigInteger($m, 256));
            if ($m->compare($maxwordrange) >= 0) {
                $y = $k->multiply($y)->add($marker);
                $y = $k->multiply($y)->add($m->subtract($offset));
            } else {
                $y = $k->multiply($y)->add($m);
            }
        }

        return $y->toBytes();
    }

    /**
     * L3-HASH: Third-Layer Hash
     *
     * The output from L2-HASH is 16 bytes long.  This final hash function
     * hashes the 16-byte string to a fixed length of 4 bytes.
     *
     * @param string $k1 string of length 64 bytes.
     * @param string $k2 string of length 4 bytes.
     * @param string $m string of length 16 bytes.
     * @return string string of length 4 bytes.
     */
    private static function L3Hash(string $k1, string $k2, string $m): string
    {
        $factory = self::$factory36;

        $y = $factory->newInteger(new BigInteger());
        for ($i = 0; $i < 8; $i++) {
            $m_i = $factory->newInteger(new BigInteger(substr($m, 2 * $i, 2), 256));
            $k_i = $factory->newInteger(new BigInteger(substr($k1, 8 * $i, 8), 256));
            $y = $y->add($m_i->multiply($k_i));
        }
        $y = str_pad(substr($y->toBytes(), -4), 4, "\0", STR_PAD_LEFT);
        $y = $y ^ $k2;

        return $y;
    }

    /**
     * Compute the Hash / HMAC / UMAC.
     *
     * @param string|resource $text
     */
    public function hash(mixed $text): string
    {
        if (!is_string($text) && !is_resource($text)) {
            throw new UnexpectedValueException('$text must be either a string or a resource');
        }
        $algo = $this->algo;
        // https://www.rfc-editor.org/rfc/rfc4493.html
        // https://en.wikipedia.org/wiki/One-key_MAC
        if ($algo == 'aes_cmac') {
            if (is_resource($text)) {
                throw new UnexpectedValueException('aes_cmac only works with strings');
            }
            $constZero = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
            if ($this->recomputeAESKey) {
                if (!is_string($this->key)) {
                    throw new InvalidStateException('No key has been set');
                }
                if (strlen($this->key) != 16) {
                    throw new LengthException('Key must be 16 bytes long');
                }
                // Algorithm Generate_Subkey
                $constRb = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x87";
                $this->c = new AES('ecb');
                $this->c->setKey($this->key);
                $this->c->disablePadding();
                $l = $this->c->encrypt($constZero);
                $msb = ($l & "\x80") == "\x80";
                $l = new BigInteger($l, 256);
                $l->setPrecision(128);
                $l = $l->bitwise_leftShift(1)->toBytes();
                // make it constant time
                $k1 = $msb ? $l ^ $constRb : $l | $constZero;

                $msb = ($k1 & "\x80") == "\x80";
                $k2 = new BigInteger($k1, 256);
                $k2->setPrecision(128);
                $k2 = $k2->bitwise_leftShift(1)->toBytes();
                // make it constant time
                $k2 = $msb ? $k2 ^ $constRb : $k2 | $constZero;

                //$this->k1 = $k1;
                //$this->k2 = $k2;
            }

            $len = strlen($text);
            $const_Bsize = 16;
            $M = strlen($text) ? str_split($text, $const_Bsize) : [''];

            // Step 2
            $n = ceil($len / $const_Bsize);
            // Step 3
            if ($n == 0) {
                $n = 1;
                $flag = false;
            } else {
                $flag = $len % $const_Bsize == 0;
            }
            // Step 4
            $M_last = $flag ?
                $M[$n - 1] ^ $k1 :
                self::OMAC_padding($M[$n - 1], $const_Bsize) ^ $k2;
            // Step 5
            $x = $constZero;
            // Step 6
            $c = &$this->c;
            for ($i = 0; $i < $n - 1; $i++) {
                $y = $x ^ $M[$i];
                $x = $c->encrypt($y);
            }
            $y = $M_last ^ $x;
            return $c->encrypt($y);
        }
        if ($algo == 'umac') {
            if (is_resource($text)) {
                throw new UnexpectedValueException('umac only works with strings');
            }
            if ($this->recomputeAESKey) {
                if (!is_string($this->nonce)) {
                    throw new InvalidStateException('No nonce has been set');
                }
                if (!is_string($this->key)) {
                    throw new InvalidStateException('No key has been set');
                }
                if (strlen($this->key) != 16) {
                    throw new LengthException('Key must be 16 bytes long');
                }

                if (!isset(self::$maxwordrange64)) {
                    $one = new BigInteger(1);

                    $prime36 = new BigInteger("\x00\x00\x00\x0F\xFF\xFF\xFF\xFB", 256);
                    self::$factory36 = new PrimeField($prime36);

                    $prime64 = new BigInteger("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xC5", 256);
                    self::$factory64 = new PrimeField($prime64);

                    $prime128 = new BigInteger("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x61", 256);
                    self::$factory128 = new PrimeField($prime128);

                    $temp = new BigInteger("\1\0\0\0\0\0\0\0\0", 256);
                    self::$offset64 = self::$factory64->newInteger($temp->subtract($prime64));
                    $temp = new BigInteger("\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 256);
                    self::$offset128 = self::$factory128->newInteger($temp->subtract($prime128));

                    self::$marker64 = self::$factory64->newInteger($prime64->subtract($one));
                    self::$marker128 = self::$factory128->newInteger($prime128->subtract($one));

                    $maxwordrange64 = $one->bitwise_leftShift(64)->subtract($one->bitwise_leftShift(32));
                    self::$maxwordrange64 = self::$factory64->newInteger($maxwordrange64);

                    $maxwordrange128 = $one->bitwise_leftShift(128)->subtract($one->bitwise_leftShift(96));
                    self::$maxwordrange128 = self::$factory128->newInteger($maxwordrange128);
                }

                $this->c = new AES('ctr');
                $this->c->disablePadding();
                $this->c->setKey($this->key);

                $this->pad = $this->pdf();

                $this->recomputeAESKey = false;
            }

            $hashedmessage = $this->uhash($text, $this->length);
            return $hashedmessage ^ $this->pad;
        }

        if (is_array($algo)) {
            if (is_resource($algo)) {
                throw new UnsupportedValueException($this->hashParam . ' only works with strings');
            }
            if (empty($this->key) || !is_string($this->key)) {
                return substr($algo($text, ...array_values($this->parameters)), 0, $this->length);
            }

            // SHA3 HMACs are discussed at https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf#page=30

            $key    = str_pad($this->computedKey, $this->blockSize >> 3, chr(0));
            $temp   = $this->ipad ^ $key;
            $temp  .= $text;
            $temp   = substr($algo($temp, ...array_values($this->parameters)), 0, $this->length);
            $output = $this->opad ^ $key;
            $output .= $temp;
            $output = $algo($output, ...array_values($this->parameters));

            return substr($output, 0, $this->length);
        }

        if (is_resource($text)) {
            $pos = ftell($text);
            rewind($text);
            $ctx = !empty($this->key) || is_string($this->key) ?
                hash_init($algo, HASH_HMAC, $this->computedKey) :
                hash_init($algo);
            hash_update_stream($ctx, $text);
            fseek($text, $pos);
            $output = hash_final($ctx, true);
        } else {
            $output = !empty($this->key) || is_string($this->key) ?
                hash_hmac($algo, $text, $this->computedKey, true) :
                hash($algo, $text, true);
        }

        return strlen($output) > $this->length
            ? substr($output, 0, $this->length)
            : $output;
    }

    /**
     * Returns the hash length (in bits)
     */
    public function getLength(): int
    {
        return $this->length << 3;
    }

    /**
     * Returns the hash length (in bytes)
     */
    public function getLengthInBytes(): int
    {
        return $this->length;
    }

    /**
     * Returns the block length (in bits)
     */
    public function getBlockLength(): int
    {
        return $this->blockSize;
    }

    /**
     * Returns the block length (in bytes)
     */
    public function getBlockLengthInBytes(): int
    {
        return $this->blockSize >> 3;
    }

    /**
     * Pads SHA3 based on the mode
     */
    private static function sha3_pad(int $padLength, int $padType): string
    {
        switch ($padType) {
            case self::PADDING_KECCAK:
                $temp = chr(0x01) . str_repeat("\0", $padLength - 1);
                $temp[$padLength - 1] = $temp[$padLength - 1] | chr(0x80);
                return $temp;
            case self::PADDING_SHAKE:
                $temp = chr(0x1F) . str_repeat("\0", $padLength - 1);
                $temp[$padLength - 1] = $temp[$padLength - 1] | chr(0x80);
                return $temp;
            //case self::PADDING_SHA3:
            default:
                // from https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf#page=36
                return $padLength == 1 ? chr(0x86) : chr(0x06) . str_repeat("\0", $padLength - 2) . chr(0x80);
        }
    }

    /**
     * Pure-PHP 32-bit implementation of SHA3
     *
     * Whereas BigInteger.php's 32-bit engine works on PHP 64-bit this 32-bit implementation
     * of SHA3 will *not* work on PHP 64-bit. This is because this implementation
     * employees bitwise NOTs and bitwise left shifts. And the round constants only work
     * on 32-bit PHP. eg. dechex(-2147483648) returns 80000000 on 32-bit PHP and
     * FFFFFFFF80000000 on 64-bit PHP. Sure, we could do bitwise ANDs but that would slow
     * things down.
     *
     * SHA512 requires BigInteger to simulate 64-bit unsigned integers because SHA2 employees
     * addition whereas SHA3 just employees bitwise operators. PHP64 only supports signed
     * 64-bit integers, which complicates addition, whereas that limitation isn't an issue
     * for SHA3.
     *
     * In https://ws680.nist.gov/publication/get_pdf.cfm?pub_id=919061#page=16 KECCAK[C] is
     * defined as "the KECCAK instance with KECCAK-f[1600] as the underlying permutation and
     * capacity c". This is relevant because, altho the KECCAK standard defines a mode
     * (KECCAK-f[800]) designed for 32-bit machines that mode is incompatible with SHA3
     *
     * @psalm-suppress UnusedMethod
     */
    private static function sha3_32(string $p, int $c, int $r, int $d, int $padType): string
    {
        $block_size = $r >> 3;
        $padLength = $block_size - (strlen($p) % $block_size);
        $num_ints = $block_size >> 2;

        $p .= static::sha3_pad($padLength, $padType);

        $s = [
            [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0]],
            [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0]],
            [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0]],
            [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0]],
            [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0]],
        ];

        $p = str_split($p, $block_size);

        foreach ($p as $pi) {
            $pi = unpack('V*', $pi);
            $x = $y = 0;
            for ($i = 1; $i <= $num_ints; $i += 2) {
                $s[$x][$y][0] ^= $pi[$i + 1];
                $s[$x][$y][1] ^= $pi[$i];
                if (++$y == 5) {
                    $y = 0;
                    $x++;
                }
            }
            static::processSHA3Block32($s);
        }

        $z = '';
        $i = $j = 0;
        while (strlen($z) < $d) {
            $z .= pack('V2', $s[$i][$j][1], $s[$i][$j++][0]);
            if ($j == 5) {
                $j = 0;
                $i++;
                if ($i == 5) {
                    $i = 0;
                    static::processSHA3Block32($s);
                }
            }
        }

        return $z;
    }

    /**
     * 32-bit block processing method for SHA3
     */
    private static function processSHA3Block32(array &$s): void
    {
        static $rotationOffsets = [
            [ 0,  1, 62, 28, 27],
            [36, 44,  6, 55, 20],
            [ 3, 10, 43, 25, 39],
            [41, 45, 15, 21,  8],
            [18,  2, 61, 56, 14],
        ];

        // the standards give these constants in hexadecimal notation. it's tempting to want to use
        // that same notation, here, however, we can't, because 0x80000000, on PHP32, is a positive
        // float - not the negative int that we need to be in PHP32. so we use -2147483648 instead
        static $roundConstants = [
            [0, 1],
            [0, 32898],
            [-2147483648, 32906],
            [-2147483648, -2147450880],
            [0, 32907],
            [0, -2147483647],
            [-2147483648, -2147450751],
            [-2147483648, 32777],
            [0, 138],
            [0, 136],
            [0, -2147450871],
            [0, -2147483638],
            [0, -2147450741],
            [-2147483648, 139],
            [-2147483648, 32905],
            [-2147483648, 32771],
            [-2147483648, 32770],
            [-2147483648, 128],
            [0, 32778],
            [-2147483648, -2147483638],
            [-2147483648, -2147450751],
            [-2147483648, 32896],
            [0, -2147483647],
            [-2147483648, -2147450872],
        ];

        for ($round = 0; $round < 24; $round++) {
            // theta step
            $parity = $rotated = [];
            for ($i = 0; $i < 5; $i++) {
                $parity[] = [
                    $s[0][$i][0] ^ $s[1][$i][0] ^ $s[2][$i][0] ^ $s[3][$i][0] ^ $s[4][$i][0],
                    $s[0][$i][1] ^ $s[1][$i][1] ^ $s[2][$i][1] ^ $s[3][$i][1] ^ $s[4][$i][1],
                ];
                $rotated[] = static::rotateLeft32($parity[$i], 1);
            }

            $temp = [
                [$parity[4][0] ^ $rotated[1][0], $parity[4][1] ^ $rotated[1][1]],
                [$parity[0][0] ^ $rotated[2][0], $parity[0][1] ^ $rotated[2][1]],
                [$parity[1][0] ^ $rotated[3][0], $parity[1][1] ^ $rotated[3][1]],
                [$parity[2][0] ^ $rotated[4][0], $parity[2][1] ^ $rotated[4][1]],
                [$parity[3][0] ^ $rotated[0][0], $parity[3][1] ^ $rotated[0][1]],
            ];
            for ($i = 0; $i < 5; $i++) {
                for ($j = 0; $j < 5; $j++) {
                    $s[$i][$j][0] ^= $temp[$j][0];
                    $s[$i][$j][1] ^= $temp[$j][1];
                }
            }

            $st = $s;

            // rho and pi steps
            for ($i = 0; $i < 5; $i++) {
                for ($j = 0; $j < 5; $j++) {
                    $st[(2 * $i + 3 * $j) % 5][$j] = static::rotateLeft32($s[$j][$i], $rotationOffsets[$j][$i]);
                }
            }

            // chi step
            for ($i = 0; $i < 5; $i++) {
                $s[$i][0] = [
                    $st[$i][0][0] ^ (~$st[$i][1][0] & $st[$i][2][0]),
                    $st[$i][0][1] ^ (~$st[$i][1][1] & $st[$i][2][1]),
                ];
                $s[$i][1] = [
                    $st[$i][1][0] ^ (~$st[$i][2][0] & $st[$i][3][0]),
                    $st[$i][1][1] ^ (~$st[$i][2][1] & $st[$i][3][1]),
                ];
                $s[$i][2] = [
                    $st[$i][2][0] ^ (~$st[$i][3][0] & $st[$i][4][0]),
                    $st[$i][2][1] ^ (~$st[$i][3][1] & $st[$i][4][1]),
                ];
                $s[$i][3] = [
                    $st[$i][3][0] ^ (~$st[$i][4][0] & $st[$i][0][0]),
                    $st[$i][3][1] ^ (~$st[$i][4][1] & $st[$i][0][1]),
                ];
                $s[$i][4] = [
                    $st[$i][4][0] ^ (~$st[$i][0][0] & $st[$i][1][0]),
                    $st[$i][4][1] ^ (~$st[$i][0][1] & $st[$i][1][1]),
                ];
            }

            // iota step
            $s[0][0][0] ^= $roundConstants[$round][0];
            $s[0][0][1] ^= $roundConstants[$round][1];
        }
    }

    /**
     * Rotate 32-bit int
     */
    private static function rotateLeft32(array $x, int $shift): array
    {
        if ($shift < 32) {
            [$hi, $lo] = $x;
        } else {
            $shift -= 32;
            [$lo, $hi] = $x;
        }

        $mask = -1 ^ (-1 << $shift);
        return [
            ($hi << $shift) | (($lo >> (32 - $shift)) & $mask),
            ($lo << $shift) | (($hi >> (32 - $shift)) & $mask),
        ];
    }

    /**
     * Pure-PHP 64-bit implementation of SHA3
     *
     * @psalm-suppress UnusedMethod
     */
    private static function sha3_64(string $p, int $c, int $r, int $d, int $padType): string
    {
        $block_size = $r >> 3;
        $padLength = $block_size - (strlen($p) % $block_size);

        $p .= static::sha3_pad($padLength, $padType);

        $s = [
            [0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0],
        ];

        $p = str_split($p, $block_size);

        foreach ($p as $pi) {
            $pi = unpack('P*', $pi);
            $x = $y = 0;
            foreach ($pi as $subpi) {
                $s[$x][$y++] ^= $subpi;
                if ($y == 5) {
                    $y = 0;
                    $x++;
                }
            }
            static::processSHA3Block64($s);
        }

        $z = '';
        $i = $j = 0;
        while (strlen($z) < $d) {
            $z .= pack('P', $s[$i][$j++]);
            if ($j == 5) {
                $j = 0;
                $i++;
                if ($i == 5) {
                    $i = 0;
                    static::processSHA3Block64($s);
                }
            }
        }

        return $z;
    }

    /**
     * 64-bit block processing method for SHA3
     */
    private static function processSHA3Block64(array &$s): void
    {
        static $rotationOffsets = [
            [ 0,  1, 62, 28, 27],
            [36, 44,  6, 55, 20],
            [ 3, 10, 43, 25, 39],
            [41, 45, 15, 21,  8],
            [18,  2, 61, 56, 14],
        ];

        static $roundConstants = [
            1,
            32898,
            -9223372036854742902,
            -9223372034707259392,
            32907,
            2147483649,
            -9223372034707259263,
            -9223372036854743031,
            138,
            136,
            2147516425,
            2147483658,
            2147516555,
            -9223372036854775669,
            -9223372036854742903,
            -9223372036854743037,
            -9223372036854743038,
            -9223372036854775680,
            32778,
            -9223372034707292150,
            -9223372034707259263,
            -9223372036854742912,
            2147483649,
            -9223372034707259384,
        ];

        for ($round = 0; $round < 24; $round++) {
            // theta step
            $parity = [];
            for ($i = 0; $i < 5; $i++) {
                $parity[] = $s[0][$i] ^ $s[1][$i] ^ $s[2][$i] ^ $s[3][$i] ^ $s[4][$i];
            }
            $temp = [
                $parity[4] ^ static::rotateLeft64($parity[1], 1),
                $parity[0] ^ static::rotateLeft64($parity[2], 1),
                $parity[1] ^ static::rotateLeft64($parity[3], 1),
                $parity[2] ^ static::rotateLeft64($parity[4], 1),
                $parity[3] ^ static::rotateLeft64($parity[0], 1),
            ];
            for ($i = 0; $i < 5; $i++) {
                for ($j = 0; $j < 5; $j++) {
                    $s[$i][$j] ^= $temp[$j];
                }
            }

            $st = $s;

            // rho and pi steps
            for ($i = 0; $i < 5; $i++) {
                for ($j = 0; $j < 5; $j++) {
                    $st[(2 * $i + 3 * $j) % 5][$j] = static::rotateLeft64($s[$j][$i], $rotationOffsets[$j][$i]);
                }
            }

            // chi step
            for ($i = 0; $i < 5; $i++) {
                $s[$i] = [
                    $st[$i][0] ^ (~$st[$i][1] & $st[$i][2]),
                    $st[$i][1] ^ (~$st[$i][2] & $st[$i][3]),
                    $st[$i][2] ^ (~$st[$i][3] & $st[$i][4]),
                    $st[$i][3] ^ (~$st[$i][4] & $st[$i][0]),
                    $st[$i][4] ^ (~$st[$i][0] & $st[$i][1]),
                ];
            }

            // iota step
            $s[0][0] ^= $roundConstants[$round];
        }
    }

    /**
     * Rotate 64-bit int
     */
    private static function rotateLeft64(int $x, int $shift): int
    {
        $mask = -1 ^ (-1 << $shift);
        return ($x << $shift) | (($x >> (64 - $shift)) & $mask);
    }

    /**
     *  OMAC Padding
     *
     * @link https://www.rfc-editor.org/rfc/rfc4493.html#section-2.4
     */
    private static function OMAC_padding(string $m, int $length): string
    {
        $count = $length - strlen($m) - 1;
        return "$m\x80" . str_repeat("\0", $count);
    }

    /**
     *  __toString() magic method
     */
    public function __toString()
    {
        return $this->getHash();
    }

    // from https://www.rfc-editor.org/rfc/rfc7292#appendix-B.2
    // this is mostly the same as SymmetricKey::setPassword()'s implementation of pkcs12
    public function setPassword(
        #[\SensitiveParameter] string $password,
        string $salt,
        int $iterationCount
    ): void {
        if (!isset($this->blockSize)) {
            throw new BadMethodCallException($this->hashParam . ' cannot be used with the PKCS#12 KDF');
        }

        $password = "\0" . chunk_split($password, 1, "\0") . "\0";

        $v = $this->blockSize >> 3;
        $saltLength = strlen($salt);
        $passLength = strlen($password);

        $d = str_repeat("\3", $v);

        $s = $saltLength ? str_repeat($salt, (int) ceil($v / $saltLength)) : '';
        $s = substr($s, 0, $v);

        $p = $passLength ? str_repeat($password, (int) ceil($v / $passLength)) : '';
        $p = substr($p, 0, $v);

        $i = $s . $p;

        $key = self::pkcs12helper($this->length, $this, $i, $d, $iterationCount);
        $this->setKey($key);
    }
}
<?php

/**
 * Pure-PHP implementation of EC.
 *
 * PHP version 8.1+
 *
 * Here's an example of how to create signatures and verify signatures with this library:
 * <code>
 * <?php
 * include 'vendor/autoload.php';
 *
 * $private = \phpseclib4\Crypt\EC::createKey('secp256k1');
 * $public = $private->getPublicKey();
 *
 * $plaintext = 'terrafrost';
 *
 * $signature = $private->sign($plaintext);
 *
 * echo $public->verify($plaintext, $signature) ? 'verified' : 'unverified';
 * ?>
 * </code>
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt;

use phpseclib4\Crypt\Common\AsymmetricKey;
use phpseclib4\Crypt\EC\BaseCurves\{
    Binary as BinaryCurve,
    Montgomery as MontgomeryCurve,
    TwistedEdwards as TwistedEdwardsCurve
};
use phpseclib4\Crypt\EC\Curves\{Curve25519, Ed25519, Ed448};
use phpseclib4\Crypt\EC\Formats\Keys\PKCS8;
use phpseclib4\Crypt\EC\{Parameters, PrivateKey, PublicKey};
use phpseclib4\Exception\{
    BadConfigurationException,
    BadMethodCallException,
    InvalidStateException,
    LengthException,
    UnsupportedCurveException
};
use phpseclib4\File\ASN1;
use phpseclib4\File\ASN1\Maps\ECParameters;
use phpseclib4\Math\BigInteger;

/**
 * Pure-PHP implementation of EC.
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class EC extends AsymmetricKey
{
    /**
     * Algorithm Name
     *
     * @var string
     */
    public const ALGORITHM = 'EC';

    /**
     * Public Key QA
     *
     * @var object[]
     */
    protected array $QA;

    /**
     * Curve
     */
    protected $curve;

    /**
     * Signature Format (Short)
     */
    protected string $shortFormat = 'ASN1';

    /**
     * Curve Name
     */
    protected string $curveName;

    /**
     * Context
     */
    protected ?string $context = null;

    /**
     * Signature Format
     */
    protected string $sigFormat;

    /**
     * Forced Engine
     *
     * @see parent::forceEngine()
     */
    protected static ?string $forcedEngine = null;

    /**
     * Create public / private key pair.
     */
    public static function createKey(string $curve): PrivateKey
    {
        self::initialize_static_variables();

        $class = new \ReflectionClass(static::class);
        if ($class->isFinal()) {
            throw new BadMethodCallException('createKey() should not be called from final classes (' . static::class . ')');
        }

        $curveName = self::getCurveCase($curve);
        $curve = '\phpseclib4\Crypt\EC\Curves\\' . $curveName;

        if (!class_exists($curve)) {
            throw new UnsupportedCurveException('Named Curve of ' . $curveName . ' is not supported');
        }

        $reflect = new \ReflectionClass($curve);
        $curveName = $reflect->isFinal() ?
            $reflect->getParentClass()->getShortName() :
            $reflect->getShortName();
        $curveEngineName = self::getOpenSSLCurveName(strtolower($curveName));

        $providers = match ($curveName) {
            'Ed25519' => [
                    'libsodium' => function_exists('sodium_crypto_sign_keypair'),
                    // OPENSSL_KEYTYPE_ED25519 introduced in PHP 8.4.0
                    'OpenSSL'   => defined('OPENSSL_KEYTYPE_ED25519'),
                ],
            // OPENSSL_KEYTYPE_ED448 introduced in PHP 8.4.0
            'Ed448' => ['OpenSSL' => defined('OPENSSL_KEYTYPE_ED448')],
            'Curve25519' => [
                'libsodium' => function_exists('sodium_crypto_box_publickey_from_secretkey'),
                // OPENSSL_KEYTYPE_X25519 introduced in PHP 8.4.0
                'OpenSSL'   => defined('OPENSSL_KEYTYPE_X25519'),
            ],
            // OPENSSL_KEYTYPE_X448 introduced in PHP 8.4.0
            'Curve448' => ['OpenSSL' => defined('OPENSSL_KEYTYPE_X448')],
            // openssl_get_curve_names() was introduced in PHP 7.1.0
            // exclude curve25519 and curve448 from testing
            default => ['OpenSSL' => function_exists('openssl_get_curve_names') && substr($curveEngineName, 0, 5) != 'curve' && in_array($curveEngineName, openssl_get_curve_names())]
        };

        foreach ($providers as $engine => $isSupported) {
            // if an engine is being forced and the forced engine doesn't match $engine, skip it
            if (isset(self::$forcedEngine) && self::$forcedEngine !== $engine) {
                continue;
            }
            if ($isSupported) {
                $result = self::generateWithEngine($engine, $curveEngineName);
                if (isset($result)) {
                    return $result;
                }
            }
            if (self::$forcedEngine === $engine) {
                throw new BadConfigurationException("EC::createKey: Engine $engine is forced but unsupported for $curve");
            }
        }

        $privatekey = new PrivateKey();

        $curve = new $curve();
        if ($curve instanceof TwistedEdwardsCurve) {
            $arr = $curve->extractSecret(random_bytes($curve instanceof Ed448 ? 57 : 32));
            $privatekey->dA = $dA = $arr['dA'];
            $privatekey->secret = $arr['secret'];
        } else {
            $privatekey->dA = $dA = $curve->createRandomMultiplier();
        }
        $privatekey->curve = $curve;
        $privatekey->curveName = $curveName;
        $privatekey->QA = $curve->multiplyPoint($curve->getBasePoint(), $dA);

        //$publickey = clone $privatekey;
        //unset($publickey->dA);
        //unset($publickey->x);

        //$publickey->curveName = $curveName;

        if ($privatekey->curve instanceof TwistedEdwardsCurve) {
            return $privatekey->withHash($curve::HASH);
        }

        return $privatekey;
    }

    /**
     * Returns the actual case of the curve
     *
     * Useful for initializing the curve class
     */
    private static function getCurveCase(string $curveName): string
    {
        $curveName = strtolower($curveName);
        if (preg_match('#(?:^curve|^ed)\d+$#', $curveName)) {
            return ucfirst($curveName);
        }
        if (substr($curveName, 0, 10) == 'brainpoolp') {
            return 'brainpoolP' . substr($curveName, 10);
        }
        return $curveName;
    }

    /**
     * Return the OpenSSL name for a curve
     */
    private static function getOpenSSLCurveName(string $curve): string
    {
        return match ($curve) {
            'secp256r1' => 'prime256v1',
            'secp192r1' => 'prime192v1',
            default => $curve
        };
    }

    /**
     * Generate the key for a given curve / engine combo
     */
    private static function generateWithEngine(string $engine, string $curve): ?PrivateKey
    {
        if ($engine == 'libsodium') {
            if ($curve == 'ed25519') {
                $kp = sodium_crypto_sign_keypair();

                $privatekey = EC::loadPrivateKeyFormat('libsodium', sodium_crypto_sign_secretkey($kp));
                //$publickey = EC::loadFormat('libsodium', sodium_crypto_sign_publickey($kp));

                $privatekey->curveName = 'Ed25519';
                //$publickey->curveName = $curve;

                return $privatekey;
            } else { // $curve == 'curve25519
                $privatekey = new PrivateKey();
                $privatekey->curve = new Curve25519();
                $privatekey->curveName = 'Curve25519';
                $privatekey->dA = $privatekey->curve->createRandomMultiplier();
                $dA = str_pad($privatekey->dA->toBytes(), 32, "\0", STR_PAD_LEFT);
                //$r = pack('H*', '0900000000000000000000000000000000000000000000000000000000000000');
                //$QA = sodium_crypto_scalarmult($dA, $r);
                $QA = sodium_crypto_box_publickey_from_secretkey($dA);
                $privatekey->QA = [$privatekey->curve->convertInteger(new BigInteger(strrev($QA), 256))];
                return $privatekey;
            }
        }

        // at this point $engine == 'OpenSSL'

        $curveName = self::getCurveCase($curve);

        $config = [];
        if (self::$configFile) {
            $config['config'] = self::$configFile;
        }
        $params = $config;
        $params['private_key_type'] = match ($curve) {
            'ed25519' => OPENSSL_KEYTYPE_ED25519,
            'ed448' => OPENSSL_KEYTYPE_ED448,
            'curve25519' => OPENSSL_KEYTYPE_X25519,
            'curve448' => OPENSSL_KEYTYPE_X448,
            default => OPENSSL_KEYTYPE_EC
        };
        if ($params['private_key_type'] == OPENSSL_KEYTYPE_EC) {
            $params['curve_name'] = $curveName;
        }

        $key = openssl_pkey_new($params);
        if (!$key) {
            return null;
        }
        $privateKeyStr = '';
        if (!openssl_pkey_export($key, $privateKeyStr, null, $config)) {
            return null;
        }
        // clear the buffer of error strings
        while (openssl_error_string() !== false) {
        }
        // some versions of OpenSSL / PHP return PKCS1 keys, others return PKCS8 keys
        $privatekey = EC::loadPrivateKey($privateKeyStr);
        $privatekey->curveName = match ($curveName) {
            'prime256v1' => 'secp256r1',
            'prime192v1' => 'secp192r1',
            default => $curveName
        };
        return $privatekey;
    }

    /**
     * OnLoad Handler
     */
    protected static function onLoad(array $components): static
    {
        if (!isset($components['dA']) && !isset($components['QA'])) {
            $new = new Parameters();
            $new->curve = $components['curve'];
            return $new;
        }

        if (isset($components['dA'])) {
            $new = new PrivateKey();
            $new->dA = $components['dA'];
            $new->secret = $components['secret'];
        } else {
            $new = new PublicKey();
        }

        $new->curve = $components['curve'];
        $new->QA = $components['QA'];

        if ($new->curve instanceof TwistedEdwardsCurve) {
            return $new->withHash($components['curve']::HASH);
        }

        return $new;
    }

    /**
     * Constructor
     *
     * PublicKey and PrivateKey objects can only be created from abstract RSA class
     */
    protected function __construct()
    {
        $this->sigFormat = self::validatePlugin('Signature', 'ASN1');

        parent::__construct();
    }

    /**
     * Returns the curve
     *
     * Returns a string if it's a named curve, an array if not
     */
    public function getCurve(): string|array
    {
        if (isset($this->curveName)) {
            return $this->curveName;
        }

        if ($this->curve instanceof MontgomeryCurve) {
            $this->curveName = $this->curve instanceof Curve25519 ? 'Curve25519' : 'Curve448';
            return $this->curveName;
        }

        if ($this->curve instanceof TwistedEdwardsCurve) {
            $this->curveName = $this->curve instanceof Ed25519 ? 'Ed25519' : 'Ed448';
            return $this->curveName;
        }

        $params = $this->getParameters()->toString('PKCS8', ['namedCurve' => true]);
        $decoded = ASN1::extractBER($params);
        $decoded = ASN1::decodeBER($decoded);
        $decoded = ASN1::map($decoded, ECParameters::MAP)->toArray();
        if (isset($decoded['namedCurve'])) {
            $this->curveName = (string) $decoded['namedCurve'];
            return (string) $decoded['namedCurve'];
        }

        return $decoded;
    }

    public function isBinaryCurve(): bool
    {
        return $this->curve instanceof BinaryCurve;
    }

    /**
     * Returns the key size
     *
     * Quoting https://tools.ietf.org/html/rfc5656#section-2,
     *
     * "The size of a set of elliptic curve domain parameters on a prime
     *  curve is defined as the number of bits in the binary representation
     *  of the field order, commonly denoted by p.  Size on a
     *  characteristic-2 curve is defined as the number of bits in the binary
     *  representation of the field, commonly denoted by m.  A set of
     *  elliptic curve domain parameters defines a group of order n generated
     *  by a base point P"
     */
    public function getLength(): int
    {
        return $this->curve->getLength();
    }

    /**
     * Returns the public key coordinates as a string
     *
     * Used by ECDH
     */
    public function getEncodedCoordinates(): string
    {
        if ($this->curve instanceof MontgomeryCurve) {
            return strrev($this->QA[0]->toBytes(true));
        }
        if ($this->curve instanceof TwistedEdwardsCurve) {
            return $this->curve->encodePoint($this->QA);
        }
        return "\4" . $this->QA[0]->toBytes(true) . $this->QA[1]->toBytes(true);
    }

    // for Weierstrass curves, if only the x coordinate is present (as is the case after doing a round of ECDH)
    // then we'll guess at the y coordinate. there are only two possible y values and, atleast in-so-far as
    // multiplication is concerned, neither value affects the resultant x value
    public static function convertPointToPublicKey(
        string $curveName,
        #[\SensitiveParameter] string $secret,
        bool $toPublicKey = true
    ): PublicKey|string {
        $curveName = self::getCurveCase($curveName);
        $curve = '\phpseclib4\Crypt\EC\Curves\\' . $curveName;

        if (!class_exists($curve)) {
            throw new UnsupportedCurveException('Named Curve of ' . $curveName . ' is not supported');
        }

        $curve = new $curve();
        if (!$curve instanceof TwistedEdwardsCurve) {
            if ($curve instanceof MontgomeryCurve) {
                $secret = strrev($secret);
            } elseif ($curve->getLengthInBytes() == strlen($secret)) {
                $secret = "\3$secret";
            }
            if (!$toPublicKey) {
                return $secret;
            }
            $secret = "\0$secret";
        } elseif (!$toPublicKey) {
            return $secret;
        }
        $QA = PKCS8::extractPoint($secret, $curve);
        $key = PKCS8::savePublicKey($curve, $QA);
        return EC::loadFormat('PKCS8', $key);
    }

    /**
     * Returns the parameters
     *
     * @see self::getPublicKey()
     */
    public function getParameters(string $type = 'PKCS1'): Parameters
    {
        $type = self::validatePlugin('Keys', $type, 'saveParameters');

        $key = $type::saveParameters($this->curve);

        return EC::load($key, 'PKCS1')
            ->withHash($this->hash->getHash())
            ->withSignatureFormat($this->shortFormat);
    }

    /**
     * Determines the signature padding mode
     *
     * Valid values are: ASN1, IEEE, SSH2, Raw
     */
    public function withSignatureFormat(string $format): static
    {
        if ($this->curve instanceof MontgomeryCurve) {
            throw new BadMethodCallException('Montgomery Curves cannot be used to create signatures');
        }

        $new = clone $this;
        $new->shortFormat = $format;
        $new->sigFormat = self::validatePlugin('Signature', $format);
        return $new;
    }

    /**
     * Returns the signature format currently being used
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function getSignatureFormat(): string
    {
        return $this->shortFormat;
    }

    /**
     * Sets the context
     *
     * Used by Ed25519 / Ed448.
     *
     * @see self::verify()
     * @see self::sign()
     */
    public function withContext(?string $context = null): static
    {
        if (!$this->curve instanceof TwistedEdwardsCurve) {
            throw new BadMethodCallException('Only Ed25519 and Ed448 support contexts');
        }

        $new = clone $this;
        if (!isset($context)) {
            $new->context = null;
            return $new;
        }

        if (strlen($context) > 255) {
            throw new LengthException('The context is supposed to be, at most, 255 bytes long');
        }
        $new->context = $context;
        return $new;
    }

    /**
     * Returns the context
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function getContext(): string
    {
        if (!isset($this->context)) {
            throw new BadMethodCallException('No context has been set');
        }
        return $this->context;
    }

    /**
     * Determines which hashing function should be used
     */
    public function withHash(string $hash): static
    {
        if ($this->curve instanceof MontgomeryCurve) {
            throw new BadMethodCallException('Montgomery Curves cannot be used to create signatures');
        }
        if ($this->curve instanceof Ed25519 && $hash != 'sha512') {
            throw new BadMethodCallException('Ed25519 only supports sha512 as a hash');
        }
        if ($this->curve instanceof Ed448 && $hash != 'shake256-912') {
            throw new BadMethodCallException('Ed448 only supports shake256 with a length of 114 bytes');
        }

        return parent::withHash($hash);
    }
}
<?php

/**
 * Generic EC Key Parsing Helper functions
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Formats\Keys;

use phpseclib4\Common\Functions\Strings;
use phpseclib4\Crypt\EC;
use phpseclib4\Crypt\EC\BaseCurves\{
    Base as BaseCurve,
    Binary as BinaryCurve,
    Montgomery,
    Prime as PrimeCurve,
    TwistedEdwards as TwistedEdwardsCurve
};
use phpseclib4\Crypt\EC\Curves\Curve25519;
use phpseclib4\Exception\{UnexpectedValueException, UnsupportedCurveException, UnsupportedValueException, BadConfigurationException};
use phpseclib4\File\ASN1;
use phpseclib4\File\ASN1\Maps;
use phpseclib4\File\ASN1\OIDs\Curves;
use phpseclib4\Math\BigInteger;

/**
 * Generic EC Key Parsing Helper functions
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
trait Common
{
    /**
     * Use Named Curves
     */
    private static bool $useNamedCurves = true;

    private static bool $oidsLoaded = false;

    private static ?BaseCurve $implicitCurve;

    /**
     * Initialize static variables
     */
    private static function initialize_static_variables(): void
    {
        if (!self::$oidsLoaded) {
            ASN1::loadOIDs('Curves');
            ASN1::loadOIDs('EC');
            self::$oidsLoaded = true;
        }
    }

    /**
     * Explicitly set the curve
     *
     * If the key contains an implicit curve phpseclib needs the curve
     * to be explicitly provided
     */
    public static function setImplicitCurve(?BaseCurve $curve): void
    {
        self::$implicitCurve = $curve;
    }

    /**
     * Returns an instance of \phpseclib4\Crypt\EC\BaseCurves\Base based
     * on the curve parameters
     */
    protected static function loadCurveByParam(array $params): BaseCurve
    {
        if (count($params) > 1) {
            throw new UnexpectedValueException('No parameters are present');
        }
        if (isset($params['namedCurve'])) {
            $curve = '\phpseclib4\Crypt\EC\Curves\\' . $params['namedCurve'];
            if (!class_exists($curve)) {
                throw new UnsupportedCurveException('Named Curve of ' . $params['namedCurve'] . ' is not supported');
            }
            return new $curve();
        }
        if (isset($params['implicitCurve'])) {
            if (!isset(self::$implicitCurve)) {
                throw new UnexpectedValueException('Implicit curves can be provided by calling setImplicitCurve');
            }
            return self::$implicitCurve;
        }
        if (isset($params['specifiedCurve'])) {
            $data = $params['specifiedCurve'];
            switch ($data['fieldID']['fieldType']) {
                case 'prime-field':
                    $curve = new PrimeCurve();
                    $curve->setModulo($data['fieldID']['parameters']);
                    $curve->setCoefficients(
                        new BigInteger((string) $data['curve']['a'], 256),
                        new BigInteger((string) $data['curve']['b'], 256)
                    );
                    $point = self::extractPoint("\0" . $data['base'], $curve);
                    $curve->setBasePoint(...$point);
                    $curve->setOrder($data['order']);
                    return $curve;
                case 'characteristic-two-field':
                    $curve = new BinaryCurve();
                    $params = ASN1::decodeBER($data['fieldID']['parameters']->value);
                    $params = ASN1::map($params, Maps\Characteristic_two::MAP)->toArray();
                    $modulo = [(int) $params['m']->toString()];
                    switch ($params['basis']) {
                        case 'tpBasis':
                            $modulo[] = (int) $params['parameters']->toString();
                            break;
                        case 'ppBasis':
                            $temp = ASN1::decodeBER($params['parameters']->value);
                            $temp = ASN1::map($temp, Maps\Pentanomial::MAP)->toArray();
                            $modulo[] = (int) $temp['k3']->toString();
                            $modulo[] = (int) $temp['k2']->toString();
                            $modulo[] = (int) $temp['k1']->toString();
                    }
                    $modulo[] = 0;
                    $curve->setModulo(...$modulo);
                    //$len = ceil($modulo[0] / 8);
                    $curve->setCoefficients(
                        Strings::bin2hex((string) $data['curve']['a']),
                        Strings::bin2hex((string) $data['curve']['b'])
                    );
                    $point = self::extractPoint("\0" . $data['base'], $curve);
                    $curve->setBasePoint(...$point);
                    $curve->setOrder($data['order']);
                    return $curve;
                default:
                    throw new UnsupportedCurveException('Field Type of ' . $data['fieldID']['fieldType'] . ' is not supported');
            }
        }
        throw new UnexpectedValueException('No valid parameters are present');
    }

    /**
     * Extract points from a string
     *
     * Supports both compressed and uncompressed points
     */
    public static function extractPoint(string $str, BaseCurve $curve): array
    {
        if ($curve instanceof Montgomery) {
            return [new BigInteger($str, 256)];
        }
        if ($curve instanceof TwistedEdwardsCurve) {
            // first step of point deciding as discussed at the following URL's:
            // https://tools.ietf.org/html/rfc8032#section-5.1.3
            // https://tools.ietf.org/html/rfc8032#section-5.2.3
            $y = $str;
            $y = strrev($y);
            $sign = (bool) (ord($y[0]) & 0x80);
            $y[0] = $y[0] & chr(0x7F);
            $y = new BigInteger($y, 256);
            if ($y->compare($curve->getModulo()) >= 0) {
                throw new UnexpectedValueException('The Y coordinate should not be >= the modulo');
            }
            $point = $curve->recoverX($y, $sign);
            if (!$curve->verifyPoint($point)) {
                throw new UnexpectedValueException('Unable to verify that point exists on curve');
            }
            return $point;
        }

        // the first byte of a bit string represents the number of bits in the last byte that are to be ignored but,
        // currently, bit strings wanting a non-zero amount of bits trimmed are not supported
        if (($val = Strings::shift($str)) != "\0") {
            throw new UnexpectedValueException('extractPoint expects the first byte to be null - not ' . Strings::bin2hex($val));
        }
        if ($str == "\0") {
            return [];
        }

        $keylen = strlen($str);
        $order = $curve->getLengthInBytes();
        // point compression is being used
        if ($keylen == $order + 1) {
            return $curve->derivePoint($str);
        }

        // point compression is not being used
        if ($keylen == 2 * $order + 1) {
            preg_match("#(.)(.{{$order}})(.{{$order}})#s", $str, $matches);
            [, $w, $x, $y] = $matches;
            if ($w != "\4") {
                throw new UnexpectedValueException('The first byte of an uncompressed point should be 04 - not ' . Strings::bin2hex($val));
            }
            $point = [
                $curve->convertInteger(new BigInteger($x, 256)),
                $curve->convertInteger(new BigInteger($y, 256)),
            ];

            if (!$curve->verifyPoint($point)) {
                throw new UnexpectedValueException('Unable to verify that point exists on curve');
            }

            return $point;
        }

        throw new UnexpectedValueException('The string representation of the points is not of an appropriate length');
    }

    /**
     * Encode Parameters
     *
     * @todo Maybe at some point this could be moved to __toString() for each of the curves?
     */
    private static function encodeParameters(BaseCurve $curve, bool $returnArray = false, array $options = []): string|array
    {
        $useNamedCurves = $options['namedCurve'] ?? self::$useNamedCurves;

        $reflect = new \ReflectionClass($curve);
        $name = $reflect->getShortName();
        if ($useNamedCurves) {
            if (isset(Curves::OIDs[$name])) {
                if ($reflect->isFinal()) {
                    $reflect = $reflect->getParentClass();
                    $name = $reflect->getShortName();
                }
                return $returnArray ?
                    ['namedCurve' => $name] :
                    ASN1::encodeDER(['namedCurve' => $name], Maps\ECParameters::MAP);
            }
            foreach (new \DirectoryIterator(__DIR__ . '/../../Curves/') as $file) {
                if ($file->getExtension() != 'php') {
                    continue;
                }
                $testName = $file->getBasename('.php');
                $class = 'phpseclib4\Crypt\EC\Curves\\' . $testName;
                $reflect = new \ReflectionClass($class);
                if ($reflect->isFinal()) {
                    continue;
                }
                $candidate = new $class();
                switch ($name) {
                    case 'Prime':
                        if (!$candidate instanceof PrimeCurve) {
                            break;
                        }
                        if (!$candidate->getModulo()->equals($curve->getModulo())) {
                            break;
                        }
                        if ($candidate->getA()->toBytes() != $curve->getA()->toBytes()) {
                            break;
                        }
                        if ($candidate->getB()->toBytes() != $curve->getB()->toBytes()) {
                            break;
                        }

                        [$candidateX, $candidateY] = $candidate->getBasePoint();
                        [$curveX, $curveY] = $curve->getBasePoint();
                        if ($candidateX->toBytes() != $curveX->toBytes()) {
                            break;
                        }
                        if ($candidateY->toBytes() != $curveY->toBytes()) {
                            break;
                        }

                        return $returnArray ?
                            ['namedCurve' => $testName] :
                            ASN1::encodeDER(['namedCurve' => $testName], Maps\ECParameters::MAP);
                    case 'Binary':
                        if (!$candidate instanceof BinaryCurve) {
                            break;
                        }
                        if ($candidate->getModulo() != $curve->getModulo()) {
                            break;
                        }
                        if ($candidate->getA()->toBytes() != $curve->getA()->toBytes()) {
                            break;
                        }
                        if ($candidate->getB()->toBytes() != $curve->getB()->toBytes()) {
                            break;
                        }

                        [$candidateX, $candidateY] = $candidate->getBasePoint();
                        [$curveX, $curveY] = $curve->getBasePoint();
                        if ($candidateX->toBytes() != $curveX->toBytes()) {
                            break;
                        }
                        if ($candidateY->toBytes() != $curveY->toBytes()) {
                            break;
                        }

                        return $returnArray ?
                            ['namedCurve' => $testName] :
                            ASN1::encodeDER(['namedCurve' => $testName], Maps\ECParameters::MAP);
                }
            }
        }

        $order = $curve->getOrder();
        // we could try to calculate the order thusly:
        // https://crypto.stackexchange.com/a/27914/4520
        // https://en.wikipedia.org/wiki/Schoof%E2%80%93Elkies%E2%80%93Atkin_algorithm
        if (!$order) {
            throw new UnsupportedValueException('Specified Curves need the order to be specified');
        }
        $point = $curve->getBasePoint();
        $x = $point[0]->toBytes();
        $y = $point[1]->toBytes();

        if ($curve instanceof PrimeCurve) {
            /*
             * valid versions are:
             *
             * ecdpVer1:
             *   - neither the curve or the base point are generated verifiably randomly.
             * ecdpVer2:
             *   - curve and base point are generated verifiably at random and curve.seed is present
             * ecdpVer3:
             *   - base point is generated verifiably at random but curve is not. curve.seed is present
             */
            // other (optional) parameters can be calculated using the methods discused at
            // https://crypto.stackexchange.com/q/28947/4520
            $data = [
                'version' => 'ecdpVer1',
                'fieldID' => [
                    'fieldType' => 'prime-field',
                    'parameters' => $curve->getModulo(),
                ],
                'curve' => [
                    'a' => $curve->getA()->toBytes(),
                    'b' => $curve->getB()->toBytes(),
                ],
                'base' => "\4" . $x . $y,
                'order' => $order,
            ];

            return $returnArray ?
                ['specifiedCurve' => $data] :
                ASN1::encodeDER(['specifiedCurve' => $data], Maps\ECParameters::MAP);
        }
        if ($curve instanceof BinaryCurve) {
            $modulo = $curve->getModulo();
            $basis = count($modulo);
            $m = array_shift($modulo);
            array_pop($modulo); // the last parameter should always be 0
            //rsort($modulo);
            switch ($basis) {
                case 3:
                    $basis = 'tpBasis';
                    $modulo = new BigInteger($modulo[0]);
                    break;
                case 5:
                    $basis = 'ppBasis';
                    // these should be in strictly ascending order (hence the commented out rsort above)
                    $modulo = [
                        'k1' => new BigInteger($modulo[2]),
                        'k2' => new BigInteger($modulo[1]),
                        'k3' => new BigInteger($modulo[0]),
                    ];
                    $modulo = ASN1::encodeDER($modulo, Maps\Pentanomial::MAP);
                    $modulo = new ASN1\Element($modulo);
            }
            $params = ASN1::encodeDER([
                'm' => new BigInteger($m),
                'basis' => $basis,
                'parameters' => $modulo,
            ], Maps\Characteristic_two::MAP);
            $params = new ASN1\Element($params);
            $a = ltrim($curve->getA()->toBytes(), "\0");
            if (!strlen($a)) {
                $a = "\0";
            }
            $b = ltrim($curve->getB()->toBytes(), "\0");
            if (!strlen($b)) {
                $b = "\0";
            }
            $data = [
                'version' => 'ecdpVer1',
                'fieldID' => [
                    'fieldType' => 'characteristic-two-field',
                    'parameters' => $params,
                ],
                'curve' => [
                    'a' => $a,
                    'b' => $b,
                ],
                'base' => "\4" . $x . $y,
                'order' => $order,
            ];

            return $returnArray ?
                ['specifiedCurve' => $data] :
                ASN1::encodeDER(['specifiedCurve' => $data], Maps\ECParameters::MAP);
        }

        throw new UnsupportedCurveException('Curve cannot be serialized');
    }

    /**
     * @param array{curve: BaseCurve, dA: BigInteger} $components
     * @return array{\phpseclib4\Math\PrimeField\Integer}
     */
    private static function deriveMontgomeryPublicKey(array $components): array
    {
        $curve = $components['curve'];
        $dA = $components['dA'];
        $forcedEngine = EC::getForcedEngine();

        $useLibsodium = !isset($forcedEngine) && $curve instanceof Curve25519 && function_exists('sodium_crypto_box_publickey_from_secretkey');
        if ($forcedEngine === 'libsodium') {
            $useLibsodium = true;
            if (!$curve instanceof Curve25519) {
                throw new BadConfigurationException('Engine libsodium is forced but is not supported for Curve448');
            }
            if (!function_exists('sodium_crypto_box_publickey_from_secretkey')) {
                throw new BadConfigurationException('Engine libsodium is forced but not available');
            }
        }

        if ($useLibsodium) {
            //$r = pack('H*', '0900000000000000000000000000000000000000000000000000000000000000');
            //$QA = sodium_crypto_scalarmult($dA->toBytes(), $r);
            $QA = sodium_crypto_box_publickey_from_secretkey(str_pad($dA->toBytes(), 32, chr(0), STR_PAD_LEFT));
            return [$components['curve']->convertInteger(new BigInteger(strrev($QA), 256))];
        }

        $useOpenSSL = !isset($forcedEngine) && function_exists('openssl_pkey_get_private');
        if ($forcedEngine == 'OpenSSL') {
            $useOpenSSL = true;
            if (!function_exists('openssl_pkey_get_private')) {
                throw new BadConfigurationException('Engine OpenSSL is forced but is not available');
            }
        }

        if ($useOpenSSL) {
            $pem = PKCS8::savePrivateKey($dA, $curve, []);
            $res = openssl_pkey_get_private($pem);
            if ($res !== false && ($details = openssl_pkey_get_details($res)) !== false) {
                $index = $curve instanceof Curve25519 ? 'x25519' : 'x448';
                return isset($details[$index]['pub_key']) ?
                    [$curve->convertInteger(new BigInteger(strrev($details[$index]['pub_key']), 256))] :
                    PKCS8::load($details['key'])['QA'];
            } elseif ($forcedEngine == 'OpenSSL') {
                throw new BadConfigurationException('Engine OpenSSL is forced but was unable to derive the public key because of ' . openssl_error_string());
            }
        }

        return [$components['curve']->multiplyPoint($components['curve']->getBasePoint(), $components['dA'])[0]];
    }

    /**
     * Use Specified Curve
     *
     * A specified curve has all the coefficients, the base points, etc, explicitely included.
     * A specified curve is a more verbose way of representing a curve
     */
    public static function useSpecifiedCurve(): void
    {
        self::$useNamedCurves = false;
    }

    /**
     * Use Named Curve
     *
     * A named curve does not include any parameters. It is up to the EC parameters to
     * know what the coefficients, the base points, etc, are from the name of the curve.
     * A named curve is a more concise way of representing a curve
     */
    public static function useNamedCurve(): void
    {
        self::$useNamedCurves = true;
    }
}
<?php

/**
 * Montgomery Public Key Handler
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2019-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Formats\Keys;

use phpseclib4\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve;
use phpseclib4\Crypt\EC\Curves\{Curve25519, Curve448};
use phpseclib4\Exception\UnexpectedValueException;
use phpseclib4\Math\BigInteger;
use phpseclib4\Math\Common\FiniteField\Integer;

/**
 * Montgomery Public Key Handler
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class MontgomeryPublic
{
    /**
     * Is invisible flag
     */
    public const IS_INVISIBLE = true;

    /**
     * Break a public or private key down into its constituent components
     *
     * @psalm-suppress PossiblyUnusedParam
     */
    public static function load(
        #[\SensitiveParameter] string $key,
        #[\SensitiveParameter] ?string $password = null
    ): array {
        $curve = match (strlen($key)) {
            32 => new Curve25519(),
            56 => new Curve448(),
            default => throw new UnexpectedValueException('The only supported lengths are 32 and 56')
        };

        $components = ['curve' => $curve];
        $components['QA'] = [$components['curve']->convertInteger(new BigInteger(strrev($key), 256))];

        return $components;
    }

    /**
     * Convert an EC public key to the appropriate format
     *
     * @param Integer[] $publicKey
     * @psalm-suppress PossiblyUnusedParam
     */
    public static function savePublicKey(MontgomeryCurve $curve, array $publicKey, array $options = []): string
    {
        return strrev($publicKey[0]->toBytes());
    }
}
<?php

/**
 * PKCS#8 Formatted EC Key Handler
 *
 * PHP version 8.1+
 *
 * Processes keys with the following headers:
 *
 * -----BEGIN ENCRYPTED PRIVATE KEY-----
 * -----BEGIN PRIVATE KEY-----
 * -----BEGIN PUBLIC KEY-----
 *
 * Analogous to ssh-keygen's pkcs8 format (as specified by -m). Although PKCS8
 * is specific to private keys it's basically creating a DER-encoded wrapper
 * for keys. This just extends that same concept to public keys (much like ssh-keygen)
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Formats\Keys;

use phpseclib4\Crypt\Common\Formats\Keys\PKCS8 as Progenitor;
use phpseclib4\Crypt\EC\BaseCurves\{
    Base as BaseCurve,
    Montgomery as MontgomeryCurve,
    TwistedEdwards as TwistedEdwardsCurve
};
use phpseclib4\Crypt\EC\Curves\{Curve25519, Curve448, Ed25519, Ed448};
use phpseclib4\Exception\UnexpectedValueException;
use phpseclib4\File\ASN1;
use phpseclib4\File\ASN1\Maps;
use phpseclib4\Math\BigInteger;
use phpseclib4\Math\Common\FiniteField\Integer;

/**
 * PKCS#8 Formatted EC Key Handler
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class PKCS8 extends Progenitor
{
    use Common;

    /**
     * OID Name
     *
     * @var array
     */
    public const OID_NAME = ['id-ecPublicKey', 'id-Ed25519', 'id-Ed448', 'id-X25519', 'id-X448'];

    /**
     * OID Value
     *
     * @var array
     */
    public const OID_VALUE = ['1.2.840.10045.2.1', '1.3.101.112', '1.3.101.113', '1.3.101.110', '1.3.101.111'];

    /**
     * Break a public or private key down into its constituent components
     */
    public static function load(
        #[\SensitiveParameter] string $key,
        #[\SensitiveParameter] ?string $password = null
    ): array {
        // initialize_static_variables() is defined in both the trait and the parent class
        // when it's defined in two places it's the traits one that's called
        // the parent one is needed, as well, but the parent one is called by other methods
        // in the parent class as needed and in the context of the parent it's the parent
        // one that's called
        self::initialize_static_variables();

        $isPublic = match (true) {
            str_contains($key, 'PUBLIC') => true,
            str_contains($key, 'PRIVATE') => false,
            default => null
        };

        $key = parent::load($key, $password);

        $type = isset($key['privateKey']) ? 'privateKey' : 'publicKey';

        if (isset($isPublic)) {
            switch (true) {
                case !$isPublic && $type == 'publicKey':
                    throw new UnexpectedValueException('Human readable string claims non-public key but DER encoded string claims public key');
                case $isPublic && $type == 'privateKey':
                    throw new UnexpectedValueException('Human readable string claims public key but DER encoded string claims private key');
            }
        } else {
            $isPublic = $type == 'publicKey';
        }

        switch ($key[$type . 'Algorithm']['algorithm']) {
            case 'id-Ed25519':
            case 'id-Ed448':
                return self::loadEdDSA($key);
            case 'id-X25519':
            case 'id-X448':
                return self::loadECDH($key);
        }

        $decoded = ASN1::decodeBER((string) $key[$type . 'Algorithm']['parameters']);
        $params = ASN1::map($decoded, Maps\ECParameters::MAP)->toArray();

        $components = [];
        $components['curve'] = self::loadCurveByParam($params);

        if ($isPublic) {
            $components['QA'] = self::extractPoint("\0" . $key['publicKey'], $components['curve']);

            return $components;
        }

        $decoded = ASN1::decodeBER((string) $key['privateKey']);
        $key = ASN1::map($decoded, Maps\ECPrivateKey::MAP)->toArray();

        if (isset($key['parameters']) && $params != $key['parameters']) {
            throw new UnexpectedValueException('The PKCS8 parameter field does not match the private key parameter field');
        }

        $components['dA'] = new BigInteger((string) $key['privateKey'], 256);
        $components['curve']->rangeCheck($components['dA']);
        $components['QA'] = isset($key['publicKey']) ?
            self::extractPoint((string) $key['publicKey'], $components['curve']) :
            $components['curve']->multiplyPoint($components['curve']->getBasePoint(), $components['dA']);

        return $components;
    }

    /**
     * Break a public or private EdDSA key down into its constituent components
     */
    private static function loadEdDSA(array $key): array
    {
        $components = [];

        if (isset($key['privateKey'])) {
            $key['privateKey'] = (string) $key['privateKey'];
            $components['curve'] = $key['privateKeyAlgorithm']['algorithm'] == 'id-Ed25519' ? new Ed25519() : new Ed448();
            $expected = chr(ASN1::TYPE_OCTET_STRING) . ASN1::encodeLength($components['curve']::SIZE);
            if (substr($key['privateKey'], 0, 2) != $expected) {
                throw new UnexpectedValueException(
                    'The first two bytes of the ' .
                    $key['privateKeyAlgorithm']['algorithm'] .
                    ' private key field should be 0x' . bin2hex($expected)
                );
            }
            $arr = $components['curve']->extractSecret(substr($key['privateKey'], 2));
            $components['dA'] = $arr['dA'];
            $components['secret'] = $arr['secret'];
        }

        if (isset($key['publicKey'])) {
            if (!isset($components['curve'])) {
                $components['curve'] = $key['publicKeyAlgorithm']['algorithm'] == 'id-Ed25519' ? new Ed25519() : new Ed448();
            }

            $components['QA'] = self::extractPoint($key['publicKey'], $components['curve']);
        }

        if (isset($key['privateKey']) && !isset($components['QA'])) {
            $components['QA'] = $components['curve']->multiplyPoint($components['curve']->getBasePoint(), $components['dA']);
        }

        return $components;
    }

    private static function loadECDH(#[\SensitiveParameter] array $key): array
    {
        $components = [];

        if (isset($key['privateKey'])) {
            $components['curve'] = $key['privateKeyAlgorithm']['algorithm'] == 'id-X25519' ? new Curve25519() : new Curve448();
            $expected = chr(ASN1::TYPE_OCTET_STRING) . ASN1::encodeLength($components['curve']::SIZE);
            $privateKey = (string) $key['privateKey'];
            if (substr($privateKey, 0, 2) != $expected) {
                throw new UnexpectedValueException(
                    'The first two bytes of the ' .
                    $key['privateKeyAlgorithm']['algorithm'] .
                    ' private key field should be 0x' . bin2hex($expected)
                );
            }
            $components['dA'] = new BigInteger(substr($privateKey, 2), 256);
        }

        if (isset($key['publicKey'])) {
            if (!isset($components['curve'])) {
                $components['curve'] = $key['publicKeyAlgorithm']['algorithm'] == 'id-X25519' ? new Curve25519() : new Curve448();
            }

            $components['QA'] = [$components['curve']->convertInteger(new BigInteger(strrev($key['publicKey']), 256))];
        }

        if (isset($key['privateKey']) && !isset($components['QA'])) {
            /** @psalm-suppress InvalidArgument */
            $components['QA'] = self::deriveMontgomeryPublicKey($components);
        }

        return $components;
    }

    /**
     * Convert an EC public key to the appropriate format
     *
     * @param Integer[] $publicKey
     */
    public static function savePublicKey(BaseCurve $curve, array $publicKey, array $options = []): string
    {
        self::initialize_static_variables();
        if ($curve instanceof MontgomeryCurve) {
            return self::wrapPublicKey(
                key: str_pad(strrev($publicKey[0]->toBytes()), $curve::SIZE, "\0", STR_PAD_RIGHT),
                oid: $curve instanceof Curve25519 ? 'id-X25519' : 'id-X448',
                options: $options
            );
        }

        if ($curve instanceof TwistedEdwardsCurve) {
            return self::wrapPublicKey(
                key: $curve->encodePoint($publicKey),
                oid: $curve instanceof Ed25519 ? 'id-Ed25519' : 'id-Ed448',
                options: $options
            );
        }

        $params = new ASN1\Element(self::encodeParameters($curve, false, $options));

        $key = "\4" . $publicKey[0]->toBytes() . $publicKey[1]->toBytes();

        return self::wrapPublicKey(
            key: $key,
            params: $params,
            oid: 'id-ecPublicKey',
            options: $options
        );
    }

    /**
     * Convert a private key to the appropriate format.
     *
     * @param Integer[] $publicKey
     */
    public static function savePrivateKey(
        #[\SensitiveParameter] BigInteger $privateKey,
        BaseCurve $curve,
        array $publicKey,
        #[\SensitiveParameter] ?string $secret = null,
        #[\SensitiveParameter] ?string $password = null,
        array $options = []
    ): string {
        self::initialize_static_variables();

        if ($curve instanceof MontgomeryCurve) {
            return self::wrapPrivateKey(
                key: chr(ASN1::TYPE_OCTET_STRING) . ASN1::encodeLength($curve::SIZE) . str_pad($privateKey->toBytes(), $curve::SIZE, "\0", STR_PAD_LEFT),
                password: $password,
                oid: $curve instanceof Curve25519 ? 'id-X25519' : 'id-X448'
            );
        }

        if ($curve instanceof TwistedEdwardsCurve) {
            return self::wrapPrivateKey(
                key: "\x04" . chr(strlen($secret)) . $secret,
                password: $password,
                oid: $curve instanceof Ed25519 ? 'id-Ed25519' : 'id-Ed448',
                options: $options
            );
        }

        $publicKey = "\4" . $publicKey[0]->toBytes() . $publicKey[1]->toBytes();

        $params = new ASN1\Element(self::encodeParameters($curve, false, $options));

        $key = [
            'version' => 'ecPrivkeyVer1',
            'privateKey' => $privateKey->toBytes(),
            //'parameters' => $params,
            'publicKey' => "\0" . $publicKey,
        ];

        $key = ASN1::encodeDER($key, Maps\ECPrivateKey::MAP);

        return self::wrapPrivateKey(
            key: $key,
            params: $params,
            password: $password,
            oid: 'id-ecPublicKey',
            options: $options
        );
    }
}
<?php

/**
 * OpenSSH Formatted EC Key Handler
 *
 * PHP version 8.1+
 *
 * Place in $HOME/.ssh/authorized_keys
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Formats\Keys;

use phpseclib4\Common\Functions\Strings;
use phpseclib4\Crypt\Common\Formats\Keys\OpenSSH as Progenitor;
use phpseclib4\Crypt\EC\BaseCurves\Base as BaseCurve;
use phpseclib4\Crypt\EC\Curves\Ed25519;
use phpseclib4\Exception\{
    LengthException,
    UnexpectedValueException,
    UnsupportedCurveException,
    UnsupportedValueException
};
use phpseclib4\File\ASN1\OIDs\Curves;
use phpseclib4\Math\BigInteger;
use phpseclib4\Math\Common\FiniteField\Integer;

/**
 * OpenSSH Formatted EC Key Handler
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class OpenSSH extends Progenitor
{
    use Common;

    /**
     * Supported Key Types
     */
    protected static array $types = [
        'ecdsa-sha2-nistp256',
        'ecdsa-sha2-nistp384',
        'ecdsa-sha2-nistp521',
        'ssh-ed25519',
    ];

    /**
     * Break a public or private key down into its constituent components
     */
    public static function load(
        #[\SensitiveParameter] string $key,
        #[\SensitiveParameter] ?string $password = null
    ): array {
        $parsed = parent::load($key, $password);

        if (isset($parsed['paddedKey'])) {
            $paddedKey = $parsed['paddedKey'];
            [$type] = Strings::unpackSSH2('s', $paddedKey);
            if ($type != $parsed['type']) {
                throw new UnexpectedValueException("The public and private keys are not of the same type ($type vs $parsed[type])");
            }
            if ($type == 'ssh-ed25519') {
                [, $key, $comment] = Strings::unpackSSH2('sss', $paddedKey);
                $key = libsodium::load($key);
                $key['comment'] = $comment;
                return $key;
            }
            [$curveName, $publicKey, $privateKey, $comment] = Strings::unpackSSH2('ssis', $paddedKey);
            $curve = self::loadCurveByParam(['namedCurve' => $curveName]);
            $curve->rangeCheck($privateKey);
            return [
                'curve' => $curve,
                'dA' => $privateKey,
                'QA' => self::extractPoint("\0$publicKey", $curve),
                'comment' => $comment,
            ];
        }

        if ($parsed['type'] == 'ssh-ed25519') {
            if (Strings::shift($parsed['publicKey'], 4) != "\0\0\0\x20") {
                throw new UnexpectedValueException('Length of ssh-ed25519 key should be 32');
            }

            $curve = new Ed25519();
            $qa = self::extractPoint($parsed['publicKey'], $curve);
        } else {
            [$curveName, $publicKey] = Strings::unpackSSH2('ss', $parsed['publicKey']);
            $curveName = '\phpseclib4\Crypt\EC\Curves\\' . $curveName;
            $curve = new $curveName();

            $qa = self::extractPoint("\0" . $publicKey, $curve);
        }

        return [
            'curve' => $curve,
            'QA' => $qa,
            'comment' => $parsed['comment'],
        ];
    }

    /**
     * Returns the alias that corresponds to a curve
     */
    private static function getAlias(BaseCurve $curve): string
    {
        self::initialize_static_variables();

        $reflect = new \ReflectionClass($curve);
        $name = $reflect->getShortName();
        if (!isset(Curves::OIDs[$name])) {
            throw new UnsupportedCurveException($name . ' is not a curve that the OpenSSH plugin supports');
        }
        $oid = Curves::OIDs[$name];
        $aliases = array_filter(Curves::OIDs, fn ($v) => $v == $oid);
        $aliases = array_keys($aliases);

        for ($i = 0; $i < count($aliases); $i++) {
            if (in_array('ecdsa-sha2-' . $aliases[$i], self::$types)) {
                $alias = $aliases[$i];
                break;
            }
        }

        if (!isset($alias)) {
            throw new UnsupportedCurveException($name . ' is not a curve that the OpenSSH plugin supports');
        }

        return $alias;
    }

    /**
     * Convert an EC public key to the appropriate format
     *
     * @param Integer[] $publicKey
     */
    public static function savePublicKey(BaseCurve $curve, array $publicKey, array $options = []): string
    {
        $comment = $options['comment'] ?? self::$comment;

        if ($curve instanceof Ed25519) {
            $key = Strings::packSSH2('ss', 'ssh-ed25519', $curve->encodePoint($publicKey));

            if ($options['binary'] ?? self::$binary) {
                return $key;
            }

            $key = 'ssh-ed25519 ' . base64_encode($key) . ' ' . $comment;
            return $key;
        }

        $alias = self::getAlias($curve);

        $points = "\4" . $publicKey[0]->toBytes() . $publicKey[1]->toBytes();
        $key = Strings::packSSH2('sss', 'ecdsa-sha2-' . $alias, $alias, $points);

        if ($options['binary'] ?? self::$binary) {
            return $key;
        }

        $key = 'ecdsa-sha2-' . $alias . ' ' . base64_encode($key) . ' ' . $comment;

        return $key;
    }

    /**
     * Convert a private key to the appropriate format.
     *
     * @param Integer[] $publicKey
     */
    public static function savePrivateKey(
        #[\SensitiveParameter] BigInteger $privateKey,
        BaseCurve $curve,
        array $publicKey,
        #[\SensitiveParameter] ?string $secret = null,
        #[\SensitiveParameter] ?string $password = null,
        array $options = []
    ): string {
        if ($curve instanceof Ed25519) {
            if (!isset($secret)) {
                throw new UnsupportedValueException('Private Key does not have a secret set');
            }
            if (strlen($secret) != 32) {
                throw new LengthException('Private Key secret is not of the correct length');
            }

            $pubKey = $curve->encodePoint($publicKey);

            $publicKey = Strings::packSSH2('ss', 'ssh-ed25519', $pubKey);
            $privateKey = Strings::packSSH2('sss', 'ssh-ed25519', $pubKey, $secret . $pubKey);

            return self::wrapPrivateKey($publicKey, $privateKey, $password, $options);
        }

        $alias = self::getAlias($curve);

        $points = "\4" . $publicKey[0]->toBytes() . $publicKey[1]->toBytes();
        $publicKey = self::savePublicKey($curve, $publicKey, ['binary' => true]);

        $privateKey = Strings::packSSH2('sssi', 'ecdsa-sha2-' . $alias, $alias, $points, $privateKey);

        return self::wrapPrivateKey($publicKey, $privateKey, $password, $options);
    }
}
<?php

/**
 * XML Formatted EC Key Handler
 *
 * More info:
 *
 * https://www.w3.org/TR/xmldsig-core/#sec-ECKeyValue
 * http://en.wikipedia.org/wiki/XML_Signature
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Formats\Keys;

use phpseclib4\Common\Functions\Strings;
use phpseclib4\Crypt\EC\BaseCurves\{
    Base as BaseCurve,
    Montgomery as MontgomeryCurve,
    Prime as PrimeCurve,
    TwistedEdwards as TwistedEdwardsCurve
};
use phpseclib4\Exception\{
    BadConfigurationException,
    UnexpectedValueException,
    UnsupportedCurveException,
    UnsupportedValueException
};
use phpseclib4\File\ASN1\OIDs\Curves;
use phpseclib4\Math\BigInteger;
use phpseclib4\Math\Common\FiniteField\Integer;

/**
 * XML Formatted EC Key Handler
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class XML
{
    use Common;

    /**
     * Default namespace
     */
    private static string $namespace;

    /**
     * Flag for using RFC4050 syntax
     */
    private static bool $rfc4050 = false;

    /**
     * Break a public or private key down into its constituent components
     *
     * @psalm-suppress PossiblyUnusedParam
     */
    public static function load(
        #[\SensitiveParameter] string $key,
        #[\SensitiveParameter] ?string $password = null
    ): array {
        self::initialize_static_variables();

        if (!class_exists('DOMDocument')) {
            throw new BadConfigurationException('The dom extension is not setup correctly on this system');
        }

        $use_errors = libxml_use_internal_errors(true);

        if (substr($key, 0, 5) != '<?xml') {
            $key = '<xml>' . $key . '</xml>';
        }

        $temp = self::isolateNamespace($key, 'http://www.w3.org/2009/xmldsig11#');
        if ($temp) {
            $key = $temp;
        }

        $temp = self::isolateNamespace($key, 'http://www.w3.org/2001/04/xmldsig-more#');
        if ($temp) {
            $key = $temp;
        }

        $dom = new \DOMDocument();

        if (!$dom->loadXML($key)) {
            $e = libxml_get_last_error();
            $message = 'Error loading XML - ' . $e->message;
            libxml_use_internal_errors($use_errors);
            throw new UnexpectedValueException($message, $e->code);
        }
        $xpath = new \DOMXPath($dom);
        libxml_use_internal_errors($use_errors);
        $curve = self::loadCurveByParam($xpath);

        $pubkey = self::query($xpath, 'publickey', 'Public Key is not present');

        $QA = self::query($xpath, 'ecdsakeyvalue')->length ?
            self::extractPointRFC4050($xpath, $curve) :
            self::extractPoint("\0" . $pubkey, $curve);

        libxml_use_internal_errors($use_errors);

        return compact('curve', 'QA');
    }

    /**
     * Case-insensitive xpath query
     */
    private static function query(
        \DOMXPath $xpath,
        string $name,
        ?string $error = null,
        bool $decode = true
    ): \DOMNodeList|string {
        $query = '/';
        $names = explode('/', $name);
        foreach ($names as $name) {
            $query .= "/*[translate(local-name(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')='$name']";
        }
        $result = $xpath->query($query);
        if (!isset($error)) {
            return $result;
        }

        if (!$result->length) {
            throw new UnexpectedValueException($error);
        }
        return $decode ? self::decodeValue($result->item(0)->textContent) : $result->item(0)->textContent;
    }

    /**
     * Finds the first element in the relevant namespace, strips the namespacing and returns the XML for that element.
     */
    private static function isolateNamespace(string $xml, string $ns): ?string
    {
        $dom = new \DOMDocument();
        if (!$dom->loadXML($xml)) {
            return null;
        }
        $xpath = new \DOMXPath($dom);
        $nodes = $xpath->query("//*[namespace::*[.='$ns'] and not(../namespace::*[.='$ns'])]");
        if (!$nodes->length) {
            return null;
        }
        $node = $nodes->item(0);
        $ns_name = $node->lookupPrefix($ns);
        if ($ns_name) {
            $node->removeAttributeNS($ns, $ns_name);
        }
        return $dom->saveXML($node);
    }

    /**
     * Decodes the value
     */
    private static function decodeValue(string $value): string
    {
        return Strings::base64_decode(str_replace(["\r", "\n", ' ', "\t"], '', $value));
    }

    /**
     * Extract points from an XML document
     */
    private static function extractPointRFC4050(\DOMXPath $xpath, BaseCurve $curve): array
    {
        $x = self::query($xpath, 'publickey/x');
        $y = self::query($xpath, 'publickey/y');
        if (!$x->length || !$x->item(0)->hasAttribute('Value')) {
            throw new UnexpectedValueException('Public Key / X coordinate not found');
        }
        if (!$y->length || !$y->item(0)->hasAttribute('Value')) {
            throw new UnexpectedValueException('Public Key / Y coordinate not found');
        }
        $point = [
            $curve->convertInteger(new BigInteger($x->item(0)->getAttribute('Value'))),
            $curve->convertInteger(new BigInteger($y->item(0)->getAttribute('Value'))),
        ];
        if (!$curve->verifyPoint($point)) {
            throw new UnexpectedValueException('Unable to verify that point exists on curve');
        }
        return $point;
    }

    /**
     * Returns an instance of \phpseclib4\Crypt\EC\BaseCurves\Base based
     * on the curve parameters
     */
    private static function loadCurveByParam(\DOMXPath $xpath): BaseCurve
    {
        $namedCurve = self::query($xpath, 'namedcurve');
        if ($namedCurve->length == 1) {
            $oid = $namedCurve->item(0)->getAttribute('URN');
            $oid = preg_replace('#[^\d.]#', '', $oid);
            $name = array_search($oid, Curves::OIDs);
            if ($name === false) {
                throw new UnsupportedCurveException('Curve with OID of ' . $oid . ' is not supported');
            }

            $curve = '\phpseclib4\Crypt\EC\Curves\\' . $name;
            if (!class_exists($curve)) {
                throw new UnsupportedCurveException('Named Curve of ' . $name . ' is not supported');
            }
            return new $curve();
        }

        $params = self::query($xpath, 'explicitparams');
        if ($params->length) {
            return self::loadCurveByParamRFC4050($xpath);
        }

        self::query($xpath, 'ecparameters', 'No parameters are present');

        $fieldTypes = [
            'prime-field' => ['fieldid/prime/p'],
            'gnb' => ['fieldid/gnb/m'],
            'tnb' => ['fieldid/tnb/k'],
            'pnb' => ['fieldid/pnb/k1', 'fieldid/pnb/k2', 'fieldid/pnb/k3'],
            'unknown' => [],
        ];

        foreach ($fieldTypes as $type => $queries) {
            foreach ($queries as $query) {
                $result = self::query($xpath, $query);
                if (!$result->length) {
                    continue 2;
                }
                $param = preg_replace('#.*/#', '', $query);
                $$param = self::decodeValue($result->item(0)->textContent);
            }
            break;
        }

        $a = self::query($xpath, 'curve/a', 'A coefficient is not present');
        $b = self::query($xpath, 'curve/b', 'B coefficient is not present');
        $base = self::query($xpath, 'base', 'Base point is not present');
        $order = self::query($xpath, 'order', 'Order is not present');

        switch ($type) {
            case 'prime-field':
                $curve = new PrimeCurve();
                /** @psalm-suppress UndefinedVariable $p */
                $curve->setModulo(new BigInteger($p, 256));
                $curve->setCoefficients(
                    new BigInteger($a, 256),
                    new BigInteger($b, 256)
                );
                $point = self::extractPoint("\0" . $base, $curve);
                $curve->setBasePoint(...$point);
                $curve->setOrder(new BigInteger($order, 256));
                return $curve;
            case 'gnb':
            case 'tnb':
            case 'pnb':
            default:
                throw new UnsupportedCurveException('Field Type of ' . $type . ' is not supported');
        }
    }

    /**
     * Returns an instance of \phpseclib4\Crypt\EC\BaseCurves\Base based
     * on the curve parameters
     */
    private static function loadCurveByParamRFC4050(\DOMXPath $xpath): BaseCurve
    {
        $fieldTypes = [
            'prime-field' => ['primefieldparamstype/p'],
            'unknown' => [],
        ];

        foreach ($fieldTypes as $type => $queries) {
            foreach ($queries as $query) {
                $result = self::query($xpath, $query);
                if (!$result->length) {
                    continue 2;
                }
                $param = preg_replace('#.*/#', '', $query);
                $$param = $result->item(0)->textContent;
            }
            break;
        }

        $a = self::query($xpath, 'curveparamstype/a', 'A coefficient is not present', false);
        $b = self::query($xpath, 'curveparamstype/b', 'B coefficient is not present', false);
        $x = self::query($xpath, 'basepointparams/basepoint/ecpointtype/x', 'Base Point X is not present', false);
        $y = self::query($xpath, 'basepointparams/basepoint/ecpointtype/y', 'Base Point Y is not present', false);
        $order = self::query($xpath, 'order', 'Order is not present', false);

        switch ($type) {
            case 'prime-field':
                $curve = new PrimeCurve();

                /** @psalm-suppress UndefinedVariable $p */
                $p = str_replace(["\r", "\n", ' ', "\t"], '', $p);
                $curve->setModulo(new BigInteger($p));

                $a = str_replace(["\r", "\n", ' ', "\t"], '', $a);
                $b = str_replace(["\r", "\n", ' ', "\t"], '', $b);
                $curve->setCoefficients(
                    new BigInteger($a),
                    new BigInteger($b)
                );

                $x = str_replace(["\r", "\n", ' ', "\t"], '', $x);
                $y = str_replace(["\r", "\n", ' ', "\t"], '', $y);
                $curve->setBasePoint(
                    new BigInteger($x),
                    new BigInteger($y)
                );

                $order = str_replace(["\r", "\n", ' ', "\t"], '', $order);
                $curve->setOrder(new BigInteger($order));
                return $curve;
            default:
                throw new UnsupportedCurveException('Field Type of ' . $type . ' is not supported');
        }
    }

    /**
     * Sets the namespace. dsig11 is the most common one.
     *
     * Set to null to unset. Used only for creating public keys.
     */
    public static function setNamespace(string $namespace): void
    {
        self::$namespace = $namespace;
    }

    /**
     * Uses the XML syntax specified in https://tools.ietf.org/html/rfc4050
     */
    public static function enableRFC4050Syntax(): void
    {
        self::$rfc4050 = true;
    }

    /**
     * Uses the XML syntax specified in https://www.w3.org/TR/xmldsig-core/#sec-ECParameters
     */
    public static function disableRFC4050Syntax(): void
    {
        self::$rfc4050 = false;
    }

    /**
     * Convert a public key to the appropriate format
     *
     * @param Integer[] $publicKey
     */
    public static function savePublicKey(BaseCurve $curve, array $publicKey, array $options = []): string
    {
        self::initialize_static_variables();

        if ($curve instanceof TwistedEdwardsCurve || $curve instanceof MontgomeryCurve) {
            throw new UnsupportedCurveException('TwistedEdwards and Montgomery Curves are not supported');
        }

        if (empty(static::$namespace)) {
            $pre = $post = '';
        } else {
            $pre = static::$namespace . ':';
            $post = ':' . static::$namespace;
        }

        if (self::$rfc4050) {
            return '<' . $pre . 'ECDSAKeyValue xmlns' . $post . '="http://www.w3.org/2001/04/xmldsig-more#">' . "\r\n" .
                   self::encodeXMLParameters($curve, $pre, $options) . "\r\n" .
                   '<' . $pre . 'PublicKey>' . "\r\n" .
                   '<' . $pre . 'X Value="' . $publicKey[0] . '" />' . "\r\n" .
                   '<' . $pre . 'Y Value="' . $publicKey[1] . '" />' . "\r\n" .
                   '</' . $pre . 'PublicKey>' . "\r\n" .
                   '</' . $pre . 'ECDSAKeyValue>';
        }

        $publicKey = "\4" . $publicKey[0]->toBytes() . $publicKey[1]->toBytes();

        return '<' . $pre . 'ECDSAKeyValue xmlns' . $post . '="http://www.w3.org/2009/xmldsig11#">' . "\r\n" .
               self::encodeXMLParameters($curve, $pre, $options) . "\r\n" .
               '<' . $pre . 'PublicKey>' . Strings::base64_encode($publicKey) . '</' . $pre . 'PublicKey>' . "\r\n" .
               '</' . $pre . 'ECDSAKeyValue>';
    }

    /**
     * Encode Parameters
     */
    private static function encodeXMLParameters(BaseCurve $curve, string $pre, array $options = []): string
    {
        $result = self::encodeParameters($curve, true, $options);

        if (isset($result['namedCurve'])) {
            $namedCurve = '<' . $pre . 'NamedCurve URI="urn:oid:' . Curves::OIDs[$result['namedCurve']] . '" />';
            return self::$rfc4050 ?
                '<DomainParameters>' . str_replace('URI', 'URN', $namedCurve) . '</DomainParameters>' :
                $namedCurve;
        }

        if (self::$rfc4050) {
            $xml = '<' . $pre . 'ExplicitParams>' . "\r\n" .
                  '<' . $pre . 'FieldParams>' . "\r\n";
            $temp = $result['specifiedCurve'];
            switch ($temp['fieldID']['fieldType']) {
                case 'prime-field':
                    $xml .= '<' . $pre . 'PrimeFieldParamsType>' . "\r\n" .
                           '<' . $pre . 'P>' . $temp['fieldID']['parameters'] . '</' . $pre . 'P>' . "\r\n" .
                           '</' . $pre . 'PrimeFieldParamsType>' . "\r\n";
                    $a = $curve->getA();
                    $b = $curve->getB();
                    [$x, $y] = $curve->getBasePoint();
                    break;
                default:
                    throw new UnsupportedCurveException('Field Type of ' . $temp['fieldID']['fieldType'] . ' is not supported');
            }
            $xml .= '</' . $pre . 'FieldParams>' . "\r\n" .
                   '<' . $pre . 'CurveParamsType>' . "\r\n" .
                   '<' . $pre . 'A>' . $a . '</' . $pre . 'A>' . "\r\n" .
                   '<' . $pre . 'B>' . $b . '</' . $pre . 'B>' . "\r\n" .
                   '</' . $pre . 'CurveParamsType>' . "\r\n" .
                   '<' . $pre . 'BasePointParams>' . "\r\n" .
                   '<' . $pre . 'BasePoint>' . "\r\n" .
                   '<' . $pre . 'ECPointType>' . "\r\n" .
                   '<' . $pre . 'X>' . $x . '</' . $pre . 'X>' . "\r\n" .
                   '<' . $pre . 'Y>' . $y . '</' . $pre . 'Y>' . "\r\n" .
                   '</' . $pre . 'ECPointType>' . "\r\n" .
                   '</' . $pre . 'BasePoint>' . "\r\n" .
                   '<' . $pre . 'Order>' . $curve->getOrder() . '</' . $pre . 'Order>' . "\r\n" .
                   '</' . $pre . 'BasePointParams>' . "\r\n" .
                   '</' . $pre . 'ExplicitParams>' . "\r\n";

            return $xml;
        }

        if (isset($result['specifiedCurve'])) {
            $xml = '<' . $pre . 'ECParameters>' . "\r\n" .
                   '<' . $pre . 'FieldID>' . "\r\n";
            $temp = $result['specifiedCurve'];
            switch ($temp['fieldID']['fieldType']) {
                case 'prime-field':
                    $xml .= '<' . $pre . 'Prime>' . "\r\n" .
                           '<' . $pre . 'P>' . Strings::base64_encode($temp['fieldID']['parameters']->toBytes()) . '</' . $pre . 'P>' . "\r\n" .
                           '</' . $pre . 'Prime>' . "\r\n" ;
                    break;
                default:
                    throw new UnsupportedCurveException('Field Type of ' . $temp['fieldID']['fieldType'] . ' is not supported');
            }
            $xml .= '</' . $pre . 'FieldID>' . "\r\n" .
                   '<' . $pre . 'Curve>' . "\r\n" .
                   '<' . $pre . 'A>' . Strings::base64_encode($temp['curve']['a']) . '</' . $pre . 'A>' . "\r\n" .
                   '<' . $pre . 'B>' . Strings::base64_encode($temp['curve']['b']) . '</' . $pre . 'B>' . "\r\n" .
                   '</' . $pre . 'Curve>' . "\r\n" .
                   '<' . $pre . 'Base>' . Strings::base64_encode($temp['base']) . '</' . $pre . 'Base>' . "\r\n" .
                   '<' . $pre . 'Order>' . Strings::base64_encode($temp['order']) . '</' . $pre . 'Order>' . "\r\n" .
                   '</' . $pre . 'ECParameters>';
            return $xml;
        }

        throw new UnsupportedValueException('Only named and specified curves are supported.');
    }
}
<?php

/**
 * Montgomery Private Key Handler
 *
 * "Naked" Curve25519 private keys can pretty much be any sequence of random 32x bytes so unless
 * we have a "hidden" key handler pretty much every 32 byte string will be loaded as a curve25519
 * private key even if it probably isn't one by PublicKeyLoader.
 *
 * "Naked" Curve25519 public keys also a string of 32 bytes so distinguishing between a "naked"
 * curve25519 private key and a public key is nigh impossible, hence separate plugins for each
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2019-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Formats\Keys;

use phpseclib4\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve;
use phpseclib4\Crypt\EC\Curves\{Curve25519, Curve448};
use phpseclib4\Exception\{InvalidArgumentException, UnexpectedValueException};
use phpseclib4\Math\BigInteger;
use phpseclib4\Math\Common\FiniteField\Integer;

/**
 * Montgomery Curve Private Key Handler
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class MontgomeryPrivate
{
    use Common;

    /**
     * Is invisible flag
     */
    public const IS_INVISIBLE = true;

    /**
     * Break a public or private key down into its constituent components
     *
     * @psalm-suppress PossiblyUnusedParam
     */
    public static function load(
        #[\SensitiveParameter] string $key,
        #[\SensitiveParameter] ?string $password = null
    ): array {
        $curve = match (strlen($key)) {
            32 => new Curve25519(),
            56 => new Curve448(),
            default => throw new UnexpectedValueException('The only supported lengths are 32 and 56')
        };

        $components = [
            'curve' => $curve,
            'dA' => new BigInteger($key, 256)
        ];
        $components['QA'] = self::deriveMontgomeryPublicKey($components);

        return $components;
    }

    /**
     * Convert an EC public key to the appropriate format
     *
     * @param Integer[] $publicKey
     * @psalm-suppress PossiblyUnusedParam
     */
    public static function savePublicKey(MontgomeryCurve $curve, array $publicKey, array $options = []): string
    {
        return strrev($publicKey[0]->toBytes());
    }

    /**
     * Convert a private key to the appropriate format.
     *
     * @param Integer[] $publicKey
     * @psalm-suppress PossiblyUnusedParam
     */
    public static function savePrivateKey(
        #[\SensitiveParameter] BigInteger $privateKey,
        MontgomeryCurve $curve,
        array $publicKey,
        #[\SensitiveParameter] ?string $secret = null,
        #[\SensitiveParameter] ?string $password = null,
        array $options = []
    ): string {
        if (isset($password)) {
            throw new InvalidArgumentException('MontgomeryPrivate private keys do not support encryption');
        }

        return str_pad($privateKey->toBytes(), $curve::SIZE, "\0", STR_PAD_RIGHT);
    }
}
<?php

/**
 * "PKCS1" (RFC5915) Formatted EC Key Handler
 *
 * PHP version 8.1+
 *
 * Used by File/X509.php
 *
 * Processes keys with the following headers:
 *
 * -----BEGIN EC PRIVATE KEY-----
 * -----BEGIN EC PARAMETERS-----
 *
 * Technically, PKCS1 is for RSA keys, only, but we're using PKCS1 to describe
 * DSA, whose format isn't really formally described anywhere, so might as well
 * use it to describe this, too. PKCS1 is easier to remember than RFC5915, after
 * all. I suppose this could also be named IETF but idk
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Formats\Keys;

use phpseclib4\Common\Functions\Strings;
use phpseclib4\Crypt\Common\Formats\Keys\PKCS1 as Progenitor;
use phpseclib4\Crypt\EC\BaseCurves\{
    Base as BaseCurve,
    Montgomery as MontgomeryCurve,
    TwistedEdwards as TwistedEdwardsCurve
};
use phpseclib4\Exception\{UnexpectedValueException, UnsupportedCurveException};
use phpseclib4\File\ASN1;
use phpseclib4\File\ASN1\Maps;
use phpseclib4\Math\BigInteger;
use phpseclib4\Math\Common\FiniteField\Integer;

/**
 * "PKCS1" (RFC5915) Formatted EC Key Handler
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class PKCS1 extends Progenitor
{
    use Common;

    /**
     * Break a public or private key down into its constituent components
     */
    public static function load(
        #[\SensitiveParameter] string $key,
        #[\SensitiveParameter] ?string $password = null
    ): array {
        self::initialize_static_variables();

        if (str_contains($key, 'BEGIN EC PARAMETERS') && str_contains($key, 'BEGIN EC PRIVATE KEY')) {
            $components = [];

            preg_match('#-*BEGIN EC PRIVATE KEY-*[^-]*-*END EC PRIVATE KEY-*#s', $key, $matches);
            $decoded = parent::loadHelper($matches[0], $password);
            $decoded = ASN1::decodeBER($decoded);
            $ecPrivate = ASN1::map($decoded, Maps\ECPrivateKey::MAP)->toArray();

            if (isset($ecPrivate['parameters'])) {
                $components['curve'] = self::loadCurveByParam($ecPrivate['parameters']);
            }

            preg_match('#-*BEGIN EC PARAMETERS-*[^-]*-*END EC PARAMETERS-*#s', $key, $matches);
            $decoded = parent::loadHelper($matches[0], '');
            $decoded = ASN1::decodeBER($decoded);
            $ecParams = ASN1::map($decoded, Maps\ECParameters::MAP)->toArray();
            $ecParams = self::loadCurveByParam($ecParams);

            // comparing $ecParams and $components['curve'] directly won't work because they'll have different Math\Common\FiniteField classes
            // even if the modulo is the same
            if (isset($components['curve']) && self::encodeParameters($ecParams, false, []) != self::encodeParameters($components['curve'], false, [])) {
                throw new UnexpectedValueException('EC PARAMETERS does not correspond to EC PRIVATE KEY');
            }

            if (!isset($components['curve'])) {
                $components['curve'] = $ecParams;
            }

            $components['dA'] = new BigInteger((string) $ecPrivate['privateKey'], 256);
            $components['curve']->rangeCheck($components['dA']);
            $components['QA'] = isset($ecPrivate['publicKey']) ?
                self::extractPoint((string) $ecPrivate['publicKey'], $components['curve']) :
                $components['curve']->multiplyPoint($components['curve']->getBasePoint(), $components['dA']);

            return $components;
        }

        $key = parent::loadHelper($key, $password);
        $decoded = ASN1::decodeBER($key);

        try {
            $key = ASN1::map($decoded, Maps\ECParameters::MAP)->toArray();
        } catch (\Exception) {
            $key = null;
        }

        if (is_array($key)) {
            return ['curve' => self::loadCurveByParam($key)];
        }

        $key = ASN1::map($decoded, Maps\ECPrivateKey::MAP)->toArray();
        if (!isset($key['parameters'])) {
            throw new UnexpectedValueException('Key cannot be loaded without parameters');
        }

        $components = [];
        $components['curve'] = self::loadCurveByParam($key['parameters']);
        $components['dA'] = new BigInteger((string) $key['privateKey'], 256);
        $components['QA'] = isset($key['publicKey']) ?
            self::extractPoint((string) $key['publicKey'], $components['curve']) :
            $components['curve']->multiplyPoint($components['curve']->getBasePoint(), $components['dA']);

        return $components;
    }

    /**
     * Convert EC parameters to the appropriate format
     */
    public static function saveParameters(BaseCurve $curve, array $options = []): string
    {
        self::initialize_static_variables();

        if ($curve instanceof TwistedEdwardsCurve || $curve instanceof MontgomeryCurve) {
            throw new UnsupportedCurveException('TwistedEdwards and Montgomery Curves are not supported');
        }

        $key = self::encodeParameters($curve, false, $options);

        return "-----BEGIN EC PARAMETERS-----\r\n" .
               chunk_split(Strings::base64_encode($key), 64) .
               "-----END EC PARAMETERS-----\r\n";
    }

    /**
     * Convert a private key to the appropriate format.
?     *
     * @param Integer[] $publicKey
     * @psalm-suppress PossiblyUnusedParam
     */
    public static function savePrivateKey(
        #[\SensitiveParameter] BigInteger $privateKey,
        BaseCurve $curve,
        array $publicKey,
        #[\SensitiveParameter] ?string $secret = null,
        #[\SensitiveParameter] ?string $password = null,
        array $options = []
    ): string {
        self::initialize_static_variables();

        if ($curve instanceof TwistedEdwardsCurve || $curve instanceof MontgomeryCurve) {
            throw new UnsupportedCurveException('TwistedEdwards Curves are not supported');
        }

        $publicKey = "\4" . $publicKey[0]->toBytes() . $publicKey[1]->toBytes();

        $key = [
            'version' => 'ecPrivkeyVer1',
            'privateKey' => $privateKey->toBytes(),
            'parameters' => new ASN1\Element(self::encodeParameters($curve)),
            'publicKey' => "\0" . $publicKey,
        ];

        $key = ASN1::encodeDER($key, Maps\ECPrivateKey::MAP);

        return self::wrapPrivateKey($key, 'EC', $password, $options);
    }
}
<?php

/**
 * libsodium Key Handler
 *
 * Different NaCl implementations store the key differently.
 * https://blog.mozilla.org/warner/2011/11/29/ed25519-keys/ elaborates.
 * libsodium appears to use the same format as SUPERCOP.
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Formats\Keys;

use phpseclib4\Crypt\EC\Curves\Ed25519;
use phpseclib4\Exception\{
    InvalidArgumentException,
    LengthException,
    UnexpectedValueException,
    UnsupportedValueException
};
use phpseclib4\Math\BigInteger;
use phpseclib4\Math\Common\FiniteField\Integer;

/**
 * libsodium Key Handler
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class libsodium
{
    use Common;

    /**
     * Is invisible flag
     */
    public const IS_INVISIBLE = true;

    /**
     * Break a public or private key down into its constituent components
     *
     * @psalm-suppress PossiblyUnusedParam
     */
    public static function load(
        #[\SensitiveParameter] string $key,
        #[\SensitiveParameter] ?string $password = null
    ): array {
        switch (strlen($key)) {
            case 32:
                $public = $key;
                break;
            case 64:
                $private = substr($key, 0, 32);
                $public = substr($key, -32);
                break;
            case 96:
                $public = substr($key, -32);
                if (substr($key, 32, 32) != $public) {
                    throw new UnexpectedValueException('Keys with 96 bytes should have the 2nd and 3rd set of 32 bytes match');
                }
                $private = substr($key, 0, 32);
                break;
            default:
                throw new UnexpectedValueException('libsodium keys need to either be 32 bytes long, 64 bytes long or 96 bytes long');
        }

        $curve = new Ed25519();
        $components = ['curve' => $curve];
        if (isset($private)) {
            $arr = $curve->extractSecret($private);
            $components['dA'] = $arr['dA'];
            $components['secret'] = $arr['secret'];
        }
        $components['QA'] = isset($public) ?
            self::extractPoint($public, $curve) :
            $curve->multiplyPoint($curve->getBasePoint(), $components['dA']);

        return $components;
    }

    /**
     * Convert an EC public key to the appropriate format
     *
     * @param Integer[] $publicKey
     * @psalm-suppress PossiblyUnusedParam
     */
    public static function savePublicKey(Ed25519 $curve, array $publicKey, array $options = []): string
    {
        return $curve->encodePoint($publicKey);
    }

    /**
     * Convert a private key to the appropriate format.
     *
     * @param Integer[] $publicKey
     * @psalm-suppress PossiblyUnusedParam
     */
    public static function savePrivateKey(
        #[\SensitiveParameter] BigInteger $privateKey,
        Ed25519 $curve,
        array $publicKey,
        #[\SensitiveParameter] ?string $secret = null,
        #[\SensitiveParameter] ?string $password = null,
        array $options = []
    ): string {
        if (!isset($secret)) {
            throw new UnsupportedValueException('Private Key does not have a secret set');
        }
        if (strlen($secret) != 32) {
            throw new LengthException('Private Key secret is not of the correct length');
        }
        if (isset($password)) {
            throw new InvalidArgumentException('libsodium private keys do not support encryption');
        }
        return $secret . $curve->encodePoint($publicKey);
    }
}
<?php

/**
 * PuTTY Formatted EC Key Handler
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Formats\Keys;

use phpseclib4\Common\Functions\Strings;
use phpseclib4\Crypt\Common\Formats\Keys\PuTTY as Progenitor;
use phpseclib4\Crypt\EC\BaseCurves\{Base as BaseCurve, TwistedEdwards as TwistedEdwardsCurve};
use phpseclib4\Exception\UnexpectedValueException;
use phpseclib4\Math\BigInteger;
use phpseclib4\Math\Common\FiniteField;
use phpseclib4\Math\Common\FiniteField\Integer;

/**
 * PuTTY Formatted EC Key Handler
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class PuTTY extends Progenitor
{
    use Common;

    /**
     * Public Handler
     *
     * @var string
     */
    public const PUBLIC_HANDLER = OpenSSH::class;

    /**
     * Supported Key Types
     */
    protected static array $types = [
        'ecdsa-sha2-nistp256',
        'ecdsa-sha2-nistp384',
        'ecdsa-sha2-nistp521',
        'ssh-ed25519',
    ];

    /**
     * Break a public or private key down into its constituent components
     */
    public static function load(
        #[\SensitiveParameter] string $key,
        #[\SensitiveParameter] ?string $password
    ): array {
        $components = parent::load($key, $password);
        if (!isset($components['private'])) {
            return $components;
        }

        $private = $components['private'];

        $temp = Strings::base64_encode(Strings::packSSH2('s', $components['type']) . $components['public']);
        $components = OpenSSH::load($components['type'] . ' ' . $temp . ' ' . $components['comment']);

        if ($components['curve'] instanceof TwistedEdwardsCurve) {
            if (Strings::shift($private, 4) != "\0\0\0\x20") {
                throw new UnexpectedValueException('Length of ssh-ed25519 key should be 32');
            }
            $arr = $components['curve']->extractSecret($private);
            $components['dA'] = $arr['dA'];
            $components['secret'] = $arr['secret'];
        } else {
            [$components['dA']] = Strings::unpackSSH2('i', $private);
            $components['curve']->rangeCheck($components['dA']);
        }

        return $components;
    }

    /**
     * Convert a private key to the appropriate format.
     *
     * @param Integer[] $publicKey
     */
    public static function savePrivateKey(
        #[\SensitiveParameter] BigInteger $privateKey,
        BaseCurve $curve,
        array $publicKey,
        #[\SensitiveParameter] ?string $secret = null,
        #[\SensitiveParameter] ?string $password = null,
        array $options = []
    ): string {
        self::initialize_static_variables();

        $public = explode(' ', OpenSSH::savePublicKey($curve, $publicKey));
        $name = $public[0];
        $public = Strings::base64_decode($public[1]);
        [, $length] = unpack('N', Strings::shift($public, 4));
        Strings::shift($public, $length);

        // PuTTY pads private keys with a null byte per the following:
        // https://github.com/github/putty/blob/a3d14d77f566a41fc61dfdc5c2e0e384c9e6ae8b/sshecc.c#L1926
        if (!$curve instanceof TwistedEdwardsCurve) {
            $private = $privateKey->toBytes();
            if (!(strlen($privateKey->toBits()) & 7)) {
                $private = "\0$private";
            }
        }

        $private = $curve instanceof TwistedEdwardsCurve ?
            Strings::packSSH2('s', $secret) :
            Strings::packSSH2('s', $private);

        return self::wrapPrivateKey($public, $private, $name, $password, $options);
    }

    /**
     * Convert an EC public key to the appropriate format
     *
     * @param Integer[] $publicKey
     * @psalm-suppress PossiblyUnusedParam
     */
    public static function savePublicKey(BaseCurve $curve, array $publicKey, array $options = []): string
    {
        $public = explode(' ', OpenSSH::savePublicKey($curve, $publicKey));
        $type = $public[0];
        $public = Strings::base64_decode($public[1]);
        [, $length] = unpack('N', Strings::shift($public, 4));
        Strings::shift($public, $length);

        return self::wrapPublicKey($public, $type);
    }
}
<?php

/**
 * JSON Web Key (RFC7517 / RFC8037) Formatted EC Handler
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2022-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Formats\Keys;

use phpseclib4\Common\Functions\Strings;
use phpseclib4\Crypt\Common\Formats\Keys\JWK as Progenitor;
use phpseclib4\Crypt\EC\BaseCurves\{Base as BaseCurve, TwistedEdwards as TwistedEdwardsCurve};
use phpseclib4\Crypt\EC\Curves\{Ed25519, secp256k1, secp256r1, secp384r1, secp521r1};
use phpseclib4\Exception\{InvalidArgumentException, UnexpectedValueException, UnsupportedCurveException};
use phpseclib4\Math\BigInteger;

/**
 * JWK Formatted EC Handler
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class JWK extends Progenitor
{
    use Common;

    /**
     * Break a public or private key down into its constituent components
     *
     * @psalm-suppress PossiblyUnusedParam
     */
    public static function load(
        #[\SensitiveParameter] string $key,
        #[\SensitiveParameter] ?string $password = null
    ): array {
        $key = parent::loadHelper($key);

        switch ($key->kty) {
            case 'EC':
                switch ($key->crv) {
                    case 'P-256':
                    case 'P-384':
                    case 'P-521':
                    case 'secp256k1':
                        break;
                    default:
                        throw new UnsupportedCurveException('Only P-256, P-384, P-521 and secp256k1 curves are accepted (' . $key->crv . ' provided)');
                }
                break;
            case 'OKP':
                switch ($key->crv) {
                    case 'Ed25519':
                    case 'Ed448':
                        break;
                    default:
                        throw new UnsupportedCurveException('Only Ed25519 and Ed448 curves are accepted (' . $key->crv . ' provided)');
                }
                break;
            default:
                throw new UnexpectedValueException('Only EC and OKP JWK keys are supported');
        }

        $curve = '\phpseclib4\Crypt\EC\Curves\\' . str_replace('P-', 'nistp', $key->crv);
        $curve = new $curve();

        if ($curve instanceof TwistedEdwardsCurve) {
            $QA = self::extractPoint(Strings::base64url_decode($key->x), $curve);
            if (!isset($key->d)) {
                return compact('curve', 'QA');
            }
            $arr = $curve->extractSecret(Strings::base64url_decode($key->d));
            return compact('curve', 'QA') + $arr;
        }

        $QA = [
            $curve->convertInteger(new BigInteger(Strings::base64url_decode($key->x), 256)),
            $curve->convertInteger(new BigInteger(Strings::base64url_decode($key->y), 256)),
        ];

        if (!$curve->verifyPoint($QA)) {
            throw new UnexpectedValueException('Unable to verify that point exists on curve');
        }

        if (!isset($key->d)) {
            return compact('curve', 'QA');
        }

        $dA = new BigInteger(Strings::base64url_decode($key->d), 256);

        $curve->rangeCheck($dA);

        return compact('curve', 'dA', 'QA');
    }

    /**
     * Returns the alias that corresponds to a curve
     */
    private static function getAlias(BaseCurve $curve): string
    {
        switch (true) {
            case $curve instanceof secp256r1:
                return 'P-256';
            case $curve instanceof secp384r1:
                return 'P-384';
            case $curve instanceof secp521r1:
                return 'P-521';
            case $curve instanceof secp256k1:
                return 'secp256k1';
        }

        $reflect = new \ReflectionClass($curve);
        $curveName = $reflect->isFinal() ?
            $reflect->getParentClass()->getShortName() :
            $reflect->getShortName();
        throw new UnsupportedCurveException("$curveName is not a supported curve");
    }

    /**
     * Return the array superstructure for an EC public key
     *
     * @param \phpseclib4\Math\Common\FiniteField\Integer[] $publicKey
     */
    private static function savePublicKeyHelper(BaseCurve $curve, array $publicKey): array
    {
        if ($curve instanceof TwistedEdwardsCurve) {
            return [
                'kty' => 'OKP',
                'crv' => $curve instanceof Ed25519 ? 'Ed25519' : 'Ed448',
                'x' => Strings::base64url_encode($curve->encodePoint($publicKey)),
            ];
        }

        return [
            'kty' => 'EC',
            'crv' => self::getAlias($curve),
            'x' => Strings::base64url_encode($publicKey[0]->toBytes()),
            'y' => Strings::base64url_encode($publicKey[1]->toBytes()),
        ];
    }

    /**
     * Convert an EC public key to the appropriate format
     *
     * @param \phpseclib4\Math\Common\FiniteField\Integer[] $publicKey
     */
    public static function savePublicKey(BaseCurve $curve, array $publicKey, array $options = []): string
    {
        $key = self::savePublicKeyHelper($curve, $publicKey);

        return self::wrapKey($key, $options);
    }

    /**
     * Convert a private key to the appropriate format.
     *
     * @param \phpseclib4\Math\Common\FiniteField\Integer[] $publicKey
     */
    public static function savePrivateKey(
        #[\SensitiveParameter] BigInteger $privateKey,
        BaseCurve $curve,
        array $publicKey,
        #[\SensitiveParameter] ?string $secret = null,
        #[\SensitiveParameter] ?string $password = null,
        array $options = []
    ): string {
        if (isset($password)) {
            throw new InvalidArgumentException('JWK private keys do not support encryption');
        }

        $key = self::savePublicKeyHelper($curve, $publicKey);
        $key['d'] = $curve instanceof TwistedEdwardsCurve ? $secret : $privateKey->toBytes();
        $key['d'] = Strings::base64url_encode($key['d']);

        return self::wrapKey($key, $options);
    }
}
<?php

/**
 * IEEE P1363 Signature Handler
 *
 * PHP version 8.1+
 *
 * Handles signatures in the format described in
 * https://standards.ieee.org/ieee/1363/2049/ and
 * https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/sign#ecdsa
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Formats\Signature;

use phpseclib4\Exception\UnexpectedValueException;
use phpseclib4\Math\BigInteger;

/**
 * ASN1 Signature Handler
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class IEEE
{
    /**
     * Loads a signature
     */
    public static function load(string $sig): array
    {
        $len = strlen($sig);
        if ($len & 1) {
            throw new UnexpectedValueException('String length should be an even number');
        }

        $r = new BigInteger(substr($sig, 0, $len >> 1), 256);
        $s = new BigInteger(substr($sig, $len >> 1), 256);

        return compact('r', 's');
    }

    /**
     * Returns a signature in the appropriate format
     *
     * @psalm-suppress PossiblyUnusedParam
     */
    public static function save(BigInteger $r, BigInteger $s, string $curve, int $length): string
    {
        $r = $r->toBytes();
        $s = $s->toBytes();
        $length = (int) ceil($length / 8);
        return str_pad($r, $length, "\0", STR_PAD_LEFT) . str_pad($s, $length, "\0", STR_PAD_LEFT);
    }
}
<?php

/**
 * Raw EC Signature Handler
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Formats\Signature;

use phpseclib4\Crypt\Common\Formats\Signature\Raw as Progenitor;

/**
 * Raw DSA Signature Handler
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class Raw extends Progenitor
{
}
<?php

/**
 * ASN1 Signature Handler
 *
 * PHP version 8.1+
 *
 * Handles signatures in the format described in
 * https://tools.ietf.org/html/rfc3279#section-2.2.3
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Formats\Signature;

use phpseclib4\File\ASN1 as Encoder;
use phpseclib4\File\ASN1\Maps\EcdsaSigValue;
use phpseclib4\Math\BigInteger;

/**
 * ASN1 Signature Handler
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class ASN1
{
    /**
     * Loads a signature
     *
     * @return array
     */
    public static function load(string $sig): array
    {
        $decoded = Encoder::decodeBER($sig);
        $components = Encoder::map($decoded, EcdsaSigValue::MAP);

        return $components->toArray();
    }

    /**
     * Returns a signature in the appropriate format
     */
    public static function save(BigInteger $r, BigInteger $s): string
    {
        return Encoder::encodeDER(compact('r', 's'), EcdsaSigValue::MAP);
    }
}
<?php

/**
 * SSH2 Signature Handler
 *
 * PHP version 8.1+
 *
 * Handles signatures in the format used by SSH2
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Formats\Signature;

use phpseclib4\Common\Functions\Strings;
use phpseclib4\Exception\{UnexpectedValueException, UnsupportedCurveException};
use phpseclib4\Math\BigInteger;

/**
 * SSH2 Signature Handler
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class SSH2
{
    /**
     * Loads a signature
     */
    public static function load(string $sig): array
    {
        [$type, $blob] = Strings::unpackSSH2('ss', $sig);
        switch ($type) {
            // see https://tools.ietf.org/html/rfc5656#section-3.1.2
            case 'ecdsa-sha2-nistp256':
            case 'ecdsa-sha2-nistp384':
            case 'ecdsa-sha2-nistp521':
                break;
            default:
                throw new UnexpectedValueException("Expected something matching ecdsa-sha2-nistp{256,384,521}, $type found");
        }

        $result = Strings::unpackSSH2('ii', $blob);

        return [
            'r' => $result[0],
            's' => $result[1],
        ];
    }

    /**
     * Returns a signature in the appropriate format
     *
     * @return string
     */
    public static function save(BigInteger $r, BigInteger $s, string $curve): string
    {
        $curve = match ($curve) {
            'secp256r1' => 'nistp256',
            'secp384r1' => 'nistp384',
            'secp521r1' => 'nistp521',
            default => throw new UnsupportedCurveException("The only supported curves are secp256r1, secp384r1 and secp521r1 - $curve provided")
        };

        $blob = Strings::packSSH2('ii', $r, $s);

        return Strings::packSSH2('ss', 'ecdsa-sha2-' . $curve, $blob);
    }
}
<?php

/**
 * EC Public Key
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2019-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC;

use phpseclib4\Common\Functions\Strings;
use phpseclib4\Crypt\{Common, EC, Hash};
use phpseclib4\Crypt\EC\BaseCurves\{Montgomery as MontgomeryCurve, TwistedEdwards as TwistedEdwardsCurve};
use phpseclib4\Crypt\EC\Curves\{Ed25519, Ed448};
use phpseclib4\Crypt\EC\Formats\Keys\PKCS1;
use phpseclib4\Crypt\EC\Formats\Signature\ASN1 as ASN1Signature;
use phpseclib4\Exception\{BadConfigurationException, BadMethodCallException, UnexpectedValueException};
use phpseclib4\Math\BigInteger;

/**
 * EC Public Key
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
final class PublicKey extends EC implements Common\PublicKey
{
    use Common\Traits\Fingerprint;

    /**
     * Verify a signature
     *
     * @see self::verify()
     */
    public function verify(string $message, string|array $signature): bool
    {
        if ($this->curve instanceof MontgomeryCurve) {
            throw new BadMethodCallException('Montgomery Curves cannot be used to create signatures');
        }

        $shortFormat = $this->shortFormat;
        $format = $this->sigFormat;

        if ($shortFormat == 'Raw') {
            if (is_string($signature)) {
                throw new UnexpectedValueException('Raw signatures must be arrays');
            }
        } elseif (is_array($signature)) {
            throw new UnexpectedValueException('The only signature format that takes in arrays is the Raw format');
        }

        if (self::$forcedEngine === 'libsodium' && !$this->curve instanceof Ed25519) {
            throw new BadConfigurationException('Engine libsodium is only supported for Ed25519');
        }

        // at this point either self::$forcedEngine is NOT libsodium or the curve is Ed25519

        if ($this->curve instanceof Ed25519 && self::$forcedEngine !== 'PHP' && self::$forcedEngine !== 'OpenSSL') {
            if (self::$forcedEngine === 'libsodium') {
                if (!function_exists('sodium_crypto_sign_verify_detached')) {
                    throw new BadConfigurationException('Engine libsodium is forced but unsupported for Ed25519 / Ed448');
                }
                if (isset($this->context)) {
                    throw new BadConfigurationException('Engine libsodium is forced but unsupported for Ed25519ctx (context)');
                }
            }
            if (function_exists('sodium_crypto_sign_verify_detached') && !isset($this->context)) {
                if ($shortFormat == 'SSH2') {
                    [$type, $signature] = Strings::unpackSSH2('ss', $signature);
                    if ($type != 'ssh-ed25519') {
                        return false;
                    }
                }
                if (strlen($signature) != 64) {
                    return false;
                }

                return sodium_crypto_sign_verify_detached($signature, $message, $this->toString('libsodium'));
            }
        }

        // at this point self::$forcedEngine CAN'T be libsodium so we won't check for it henceforth

        if ($this->curve instanceof TwistedEdwardsCurve) {
            if ($shortFormat == 'SSH2') {
                [$type, $signature] = Strings::unpackSSH2('ss', $signature);
                if ($this->curve instanceof Ed25519 && $type != 'ssh-ed25519') {
                    return false;
                }
                if ($this->curve instanceof Ed448 && $type != 'ssh-ed448') {
                    return false;
                }
            }
            if (strlen($signature) != 2 * $this->curve::SIZE) {
                return false;
            }

            if (self::$forcedEngine !== 'PHP') {
                $keyTypeConstant = $this->curve instanceof Ed25519 ? 'OPENSSL_KEYTYPE_ED25519' : 'OPENSSL_KEYTYPE_ED448';
                if (self::$forcedEngine === 'OpenSSL') {
                    if (!defined($keyTypeConstant)) {
                        throw new BadConfigurationException('Engine OpenSSL is forced but unsupported for Ed25519 / Ed448');
                    }
                    // OpenSSL supports Ed25519/Ed448 but not Ed25519ctx (context), so skip if context is set
                    if (isset($this->context)) {
                        throw new BadConfigurationException('Engine OpenSSL is forced but unsupported for Ed25519 / Ed448 curves with context\'s');
                    }
                }
                if (defined($keyTypeConstant) && !isset($this->context)) {
                    // algorithm 0 is used because EdDSA has a built-in hash
                    $result = openssl_verify($message, $signature, $this->toString('PKCS8'), 0) === 1;
                    if ($result !== -1 && $result !== false) {
                        return (bool) $result;
                    }
                    if (self::$forcedEngine === 'OpenSSL') {
                        throw new BadConfigurationException('Engine OpenSSL is forced but was unable to create signature because of ' . openssl_error_string());
                    }
                }
            }

            $order = $this->curve->getOrder();

            $curve = $this->curve;
            if (strlen($signature) != 2 * $curve::SIZE) {
                return false;
            }

            $R = substr($signature, 0, $curve::SIZE);
            $S = substr($signature, $curve::SIZE);

            try {
                $R = PKCS1::extractPoint($R, $curve);
                $R = $this->curve->convertToInternal($R);
            } catch (\Exception) {
                return false;
            }

            $S = strrev($S);
            $S = new BigInteger($S, 256);

            if ($S->compare($order) >= 0) {
                return false;
            }

            $A = $curve->encodePoint($this->QA);

            if ($curve instanceof Ed25519) {
                $dom2 = !isset($this->context) ? '' :
                    'SigEd25519 no Ed25519 collisions' . "\0" . chr(strlen($this->context)) . $this->context;
            } else {
                $context = $this->context ?? '';
                $dom2 = 'SigEd448' . "\0" . chr(strlen($context)) . $context;
            }

            $hash = new Hash($curve::HASH);
            $k = $hash->hash($dom2 . substr($signature, 0, $curve::SIZE) . $A . $message);
            $k = strrev($k);
            $k = new BigInteger($k, 256);
            [, $k] = $k->divide($order);

            $qa = $curve->convertToInternal($this->QA);

            $lhs = $curve->multiplyPoint($curve->getBasePoint(), $S);
            $rhs = $curve->multiplyPoint($qa, $k);
            $rhs = $curve->addPoint($rhs, $R);
            $rhs = $curve->convertToAffine($rhs);

            return $lhs[0]->equals($rhs[0]) && $lhs[1]->equals($rhs[1]);
        }

        try {
            ['r' => $r, 's' => $s] = $format::load($signature);
        } catch (\Exception) {
            return false;
        }

        if (self::$forcedEngine === 'OpenSSL' && !function_exists('openssl_get_md_methods')) {
            throw new BadConfigurationException('Engine OpenSSL is forced but unsupported for ECDSA');
        }

        // at this point $forcedEngine is either PHP or null. either that OR openssl_get_md_methods() exists

        if (self::$forcedEngine !== 'PHP') {
            if (in_array($this->hash->getHash(), openssl_get_md_methods())) {
                $sig = $format != 'ASN1' ? ASN1Signature::save($r, $s) : $signature;

                $result = openssl_verify($message, $sig, $this->toString('PKCS8', ['namedCurve' => false]), $this->hash->getHash());

                if ($result !== -1 && $result !== false) {
                    return (bool) $result;
                }
                if (self::$forcedEngine === 'OpenSSL') {
                    throw new BadConfigurationException('Engine OpenSSL is forced but was unable to verify signature because of ' . openssl_error_string());
                }
            } elseif (self::$forcedEngine === 'OpenSSL') {
                throw new BadConfigurationException('Engine OpenSSL is forced but unsupported for ECDSA / ' . $this->hash->getHash());
            }
        }

        $order = $this->curve->getOrder();

        $n_1 = $order->subtract(self::$one);
        if (!$r->between(self::$one, $n_1) || !$s->between(self::$one, $n_1)) {
            return false;
        }

        $e = $this->hash->hash($message);
        $e = new BigInteger($e, 256);

        $Ln = $this->hash->getLength() - $order->getLength();
        $z = $Ln > 0 ? $e->bitwise_rightShift($Ln) : $e;

        $w = $s->modInverse($order);
        [, $u1] = $z->multiply($w)->divide($order);
        [, $u2] = $r->multiply($w)->divide($order);

        $u1 = $this->curve->convertInteger($u1);
        $u2 = $this->curve->convertInteger($u2);

        // multiplyPoint() always returns [$x, $y]; only $x is needed here
        [$x1, ] = $this->curve->multiplyAddPoints(
            [$this->curve->getBasePoint(), $this->QA],
            [$u1, $u2]
        );

        $x1 = $x1->toBigInteger();
        [, $x1] = $x1->divide($order);

        return $x1->equals($r);
    }

    /**
     * Returns the public key
     */
    public function toString(string $type, array $options = []): string
    {
        $type = self::validatePlugin('Keys', $type, 'savePublicKey');

        return $type::savePublicKey($this->curve, $this->QA, $options);
    }
}
<?php

/**
 * prime192v3
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Prime;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class prime192v3 extends Prime
{
    public function __construct()
    {
        $this->setModulo(new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF', 16));
        $this->setCoefficients(
            new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC', 16),
            new BigInteger('22123DC2395A05CAA7423DAECCC94760A7D462256BD56916', 16)
        );
        $this->setBasePoint(
            new BigInteger('7D29778100C65A1DA1783716588DCE2B8B4AEE8E228F1896', 16),
            new BigInteger('38A90F22637337334B49DCB66A6DC8F9978ACA7648A943B0', 16)
        );
        $this->setOrder(new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFF7A62D031C83F4294F640EC13', 16));
    }
}
<?php

/**
 * brainpoolP224t1
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Prime;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class brainpoolP224t1 extends Prime
{
    public function __construct()
    {
        $this->setModulo(new BigInteger('D7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FF', 16));
        $this->setCoefficients(
            new BigInteger('D7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FC', 16), // eg. -3
            new BigInteger('4B337D934104CD7BEF271BF60CED1ED20DA14C08B3BB64F18A60888D', 16)
        );
        $this->setBasePoint(
            new BigInteger('6AB1E344CE25FF3896424E7FFE14762ECB49F8928AC0C76029B4D580', 16),
            new BigInteger('0374E9F5143E568CD23F3F4D7C0D4B1E41C8CC0D1C6ABD5F1A46DB4C', 16)
        );
        $this->setOrder(new BigInteger('D7C134AA264366862A18302575D0FB98D116BC4B6DDEBCA3A5A7939F', 16));
    }
}
<?php

/**
 * nistb233
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

/** @psalm-api */
final class nistb233 extends sect233r1
{
}
<?php

/**
 * secp256r1
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Prime;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class secp256r1 extends Prime
{
    public function __construct()
    {
        $this->setModulo(new BigInteger('FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF', 16));
        $this->setCoefficients(
            new BigInteger('FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC', 16),
            new BigInteger('5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B', 16)
        );
        $this->setBasePoint(
            new BigInteger('6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296', 16),
            new BigInteger('4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5', 16)
        );
        $this->setOrder(new BigInteger('FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551', 16));
    }
}
<?php

/**
 * sect113r2
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Binary;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class sect113r2 extends Binary
{
    public function __construct()
    {
        $this->setModulo(113, 9, 0);
        $this->setCoefficients(
            '00689918DBEC7E5A0DD6DFC0AA55C7',
            '0095E9A9EC9B297BD4BF36E059184F'
        );
        $this->setBasePoint(
            '01A57A6A7B26CA5EF52FCDB8164797',
            '00B3ADC94ED1FE674C06E695BABA1D'
        );
        $this->setOrder(new BigInteger('010000000000000108789B2496AF93', 16));
    }
}
<?php

/**
 * prime192v1
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

/** @psalm-api */
final class prime192v1 extends secp192r1
{
}
<?php

/**
 * brainpoolP320t1
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Prime;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class brainpoolP320t1 extends Prime
{
    public function __construct()
    {
        $this->setModulo(new BigInteger('D35E472036BC4FB7E13C785ED201E065F98FCFA6F6F40DEF4F9' .
                                        '2B9EC7893EC28FCD412B1F1B32E27', 16));
        $this->setCoefficients(
            new BigInteger('D35E472036BC4FB7E13C785ED201E065F98FCFA6F6F40DEF4F92B9EC7893EC28' .
                           'FCD412B1F1B32E24', 16), // eg. -3
            new BigInteger('A7F561E038EB1ED560B3D147DB782013064C19F27ED27C6780AAF77FB8A547CE' .
                           'B5B4FEF422340353', 16)
        );
        $this->setBasePoint(
            new BigInteger('925BE9FB01AFC6FB4D3E7D4990010F813408AB106C4F09CB7EE07868CC136FFF' .
                           '3357F624A21BED52', 16),
            new BigInteger('63BA3A7A27483EBF6671DBEF7ABB30EBEE084E58A0B077AD42A5A0989D1EE71B' .
                           '1B9BC0455FB0D2C3', 16)
        );
        $this->setOrder(new BigInteger('D35E472036BC4FB7E13C785ED201E065F98FCFA5B68F12A32D4' .
                                       '82EC7EE8658E98691555B44C59311', 16));
    }
}
<?php

/**
 * prime192v2
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Prime;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class prime192v2 extends Prime
{
    public function __construct()
    {
        $this->setModulo(new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF', 16));
        $this->setCoefficients(
            new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC', 16),
            new BigInteger('CC22D6DFB95C6B25E49C0D6364A4E5980C393AA21668D953', 16)
        );
        $this->setBasePoint(
            new BigInteger('EEA2BAE7E1497842F2DE7769CFE9C989C072AD696F48034A', 16),
            new BigInteger('6574D11D69B6EC7A672BB82A083DF2F2B0847DE970B2DE15', 16)
        );
        $this->setOrder(new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFE5FB1A724DC80418648D8DD31', 16));
    }
}
<?php

/**
 * sect283k1
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

/** @psalm-api */
final class nistk283 extends sect283k1
{
}
<?php

/**
 * secp256k1
 *
 * This is the curve used in Bitcoin
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

//use phpseclib4\Crypt\EC\BaseCurves\Prime;
use phpseclib4\Crypt\EC\BaseCurves\KoblitzPrime;
use phpseclib4\Math\BigInteger;

//class secp256k1 extends Prime
/** @psalm-api */
class secp256k1 extends KoblitzPrime
{
    public function __construct()
    {
        $this->setModulo(new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F', 16));
        $this->setCoefficients(
            new BigInteger('0000000000000000000000000000000000000000000000000000000000000000', 16),
            new BigInteger('0000000000000000000000000000000000000000000000000000000000000007', 16)
        );
        $this->setOrder(new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141', 16));
        $this->setBasePoint(
            new BigInteger('79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798', 16),
            new BigInteger('483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8', 16)
        );

        $this->basis = [];
        $this->basis[] = [
            'a' => new BigInteger('3086D221A7D46BCDE86C90E49284EB15', -16),
            'b' => new BigInteger('FF1BBC8129FEF177D790AB8056F5401B3D', -16),
        ];
        $this->basis[] = [
            'a' => new BigInteger('114CA50F7A8E2F3F657C1108D9D44CFD8', -16),
            'b' => new BigInteger('3086D221A7D46BCDE86C90E49284EB15', -16),
        ];
        $this->beta = $this->factory->newInteger(new BigInteger('7AE96A2B657C07106E64479EAC3434E99CF0497512F58995C1396C28719501EE', -16));
    }
}
<?php

/**
 * brainpoolP320r1
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Prime;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class brainpoolP320r1 extends Prime
{
    public function __construct()
    {
        $this->setModulo(new BigInteger('D35E472036BC4FB7E13C785ED201E065F98FCFA6F6F40DEF4F9' .
                                        '2B9EC7893EC28FCD412B1F1B32E27', 16));
        $this->setCoefficients(
            new BigInteger('3EE30B568FBAB0F883CCEBD46D3F3BB8A2A73513F5EB79DA66190EB085FFA9F4' .
                           '92F375A97D860EB4', 16),
            new BigInteger('520883949DFDBC42D3AD198640688A6FE13F41349554B49ACC31DCCD88453981' .
                           '6F5EB4AC8FB1F1A6', 16)
        );
        $this->setBasePoint(
            new BigInteger('43BD7E9AFB53D8B85289BCC48EE5BFE6F20137D10A087EB6E7871E2A10A599C7' .
                           '10AF8D0D39E20611', 16),
            new BigInteger('14FDD05545EC1CC8AB4093247F77275E0743FFED117182EAA9C77877AAAC6AC7' .
                           'D35245D1692E8EE1', 16)
        );
        $this->setOrder(new BigInteger('D35E472036BC4FB7E13C785ED201E065F98FCFA5B68F12A32D4' .
                                       '82EC7EE8658E98691555B44C59311', 16));
    }
}
<?php

/**
 * sect571r1
 *
 * PHP version 8.1+
 *
 * @author    Jim Wiggint  on <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Binary;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class sect571r1 extends Binary
{
    public function __construct()
    {
        $this->setModulo(571, 10, 5, 2, 0);
        $this->setCoefficients(
            '000000000000000000000000000000000000000000000000000000000000000000000000' .
            '000000000000000000000000000000000000000000000000000000000000000000000001',
            '02F40E7E2221F295DE297117B7F3D62F5C6A97FFCB8CEFF1CD6BA8CE4A9A18AD84FFABBD' .
            '8EFA59332BE7AD6756A66E294AFD185A78FF12AA520E4DE739BACA0C7FFEFF7F2955727A'
        );
        $this->setBasePoint(
            '0303001D34B856296C16C0D40D3CD7750A93D1D2955FA80AA5F40FC8DB7B2ABDBDE53950' .
            'F4C0D293CDD711A35B67FB1499AE60038614F1394ABFA3B4C850D927E1E7769C8EEC2D19',
            '037BF27342DA639B6DCCFFFEB73D69D78C6C27A6009CBBCA1980F8533921E8A684423E43' .
            'BAB08A576291AF8F461BB2A8B3531D2F0485C19B16E2F1516E23DD3C1A4827AF1B8AC15B'
        );
        $this->setOrder(new BigInteger(
            '03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' .
            'E661CE18FF55987308059B186823851EC7DD9CA1161DE93D5174D66E8382E9BB2FE84E47',
            16
        ));
    }
}
<?php

/**
 * secp192r1
 *
 * This is the NIST P-192 curve
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Prime;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class secp192r1 extends Prime
{
    public function __construct()
    {
        $modulo = new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF', 16);
        $this->setModulo($modulo);

        // algorithm 2.27 from http://diamond.boisestate.edu/~liljanab/MATH308/GuideToECC.pdf#page=66
        /* in theory this should be faster than regular modular reductions save for one small issue.
           to convert to / from base-2**8 with BCMath you have to call bcmul() and bcdiv() a lot.
           to convert to / from base-2**8 with PHP64 you have to call base256_rshift() a lot.
           in short, converting to / from base-2**8 is pretty expensive and that expense is
           enough to offset whatever else might be gained by a simplified reduction algorithm.
           now, if PHP supported unsigned integers things might be different. no bit-shifting
           would be required for the PHP engine and it'd be a lot faster. but as is, BigInteger
           uses base-2**31 or base-2**26 depending on whether or not the system is a 32-bit
           or a 64-bit OS.
        */
        /*
        $m_length = $this->getLengthInBytes();
        $this->setReduction(function(BigInteger $c) use ($modulo, $m_length) {
            $cBytes = $c->toBytes();

            if (strlen($cBytes) > 2 * $m_length) {
                [], $r] = $c->divide($modulo);
                return $r;
            }

            $c = str_pad($cBytes, 48, "\0", STR_PAD_LEFT);
            $c = array_reverse(str_split($c, 8));

            $null = "\0\0\0\0\0\0\0\0";
            $s1 = new BigInteger($c[2] . $c[1] . $c[0], 256);
            $s2 = new BigInteger($null . $c[3] . $c[3], 256);
            $s3 = new BigInteger($c[4] . $c[4] . $null, 256);
            $s4 = new BigInteger($c[5] . $c[5] . $c[5], 256);

            $r = $s1->add($s2)->add($s3)->add($s4);
            while ($r->compare($modulo) >= 0) {
                $r = $r->subtract($modulo);
            }

            return $r;
        });
        */

        $this->setCoefficients(
            new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC', 16),
            new BigInteger('64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1', 16)
        );
        $this->setBasePoint(
            new BigInteger('188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012', 16),
            new BigInteger('07192B95FFC8DA78631011ED6B24CDD573F977A11E794811', 16)
        );
        $this->setOrder(new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831', 16));
    }
}
<?php

/**
 * nistk163
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

/** @psalm-api */
final class nistk163 extends sect163k1
{
}
<?php

/**
 * sect131r2
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Binary;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class sect131r2 extends Binary
{
    public function __construct()
    {
        $this->setModulo(131, 8, 3, 2, 0);
        $this->setCoefficients(
            '03E5A88919D7CAFCBF415F07C2176573B2',
            '04B8266A46C55657AC734CE38F018F2192'
        );
        $this->setBasePoint(
            '0356DCD8F2F95031AD652D23951BB366A8',
            '0648F06D867940A5366D9E265DE9EB240F'
        );
        $this->setOrder(new BigInteger('0400000000000000016954A233049BA98F', 16));
    }
}
<?php

/**
 * nistp192
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

/** @psalm-api */
final class nistp192 extends secp192r1
{
}
<?php

/**
 * nistb409
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

/** @psalm-api */
final class nistb409 extends sect409r1
{
}
<?php

/**
 * sect283k1
 *
 * PHP version 8.1+
 *
 * @author    Jim Wiggint  on <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Binary;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class sect283k1 extends Binary
{
    public function __construct()
    {
        $this->setModulo(283, 12, 7, 5, 0);
        $this->setCoefficients(
            '000000000000000000000000000000000000000000000000000000000000000000000000',
            '000000000000000000000000000000000000000000000000000000000000000000000001'
        );
        $this->setBasePoint(
            '0503213F78CA44883F1A3B8162F188E553CD265F23C1567A16876913B0C2AC2458492836',
            '01CCDA380F1C9E318D90F95D07E5426FE87E45C0E8184698E45962364E34116177DD2259'
        );
        $this->setOrder(new BigInteger('01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE9AE2ED07577265DFF7F94451E061E163C61', 16));
    }
}
<?php

/**
 * brainpoolP256t1
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Prime;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class brainpoolP256t1 extends Prime
{
    public function __construct()
    {
        $this->setModulo(new BigInteger('A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377', 16));
        $this->setCoefficients(
            new BigInteger('A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5374', 16), // eg. -3
            new BigInteger('662C61C430D84EA4FE66A7733D0B76B7BF93EBC4AF2F49256AE58101FEE92B04', 16)
        );
        $this->setBasePoint(
            new BigInteger('A3E8EB3CC1CFE7B7732213B23A656149AFA142C47AAFBC2B79A191562E1305F4', 16),
            new BigInteger('2D996C823439C56D7F7B22E14644417E69BCB6DE39D027001DABE8F35B25C9BE', 16)
        );
        $this->setOrder(new BigInteger('A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7', 16));
    }
}
<?php

/**
 * prime239v3
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Prime;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class prime239v3 extends Prime
{
    public function __construct()
    {
        $this->setModulo(new BigInteger('7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFF', 16));
        $this->setCoefficients(
            new BigInteger('7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFC', 16),
            new BigInteger('255705FA2A306654B1F4CB03D6A750A30C250102D4988717D9BA15AB6D3E', 16)
        );
        $this->setBasePoint(
            new BigInteger('6768AE8E18BB92CFCF005C949AA2C6D94853D0E660BBF854B1C9505FE95A', 16),
            new BigInteger('1607E6898F390C06BC1D552BAD226F3B6FCFE48B6E818499AF18E3ED6CF3', 16)
        );
        $this->setOrder(new BigInteger('7FFFFFFFFFFFFFFFFFFFFFFF7FFFFF975DEB41B3A6057C3C432146526551', 16));
    }
}
<?php

/**
 * brainpoolP512r1
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Prime;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class brainpoolP512r1 extends Prime
{
    public function __construct()
    {
        $this->setModulo(new BigInteger(
            'AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC' .
            '66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F3',
            16
        ));
        $this->setCoefficients(
            new BigInteger(
                '7830A3318B603B89E2327145AC234CC594CBDD8D3DF91610A83441CAEA9863BC2DED5D5AA82' .
                '53AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CA',
                16
            ),
            new BigInteger(
                '3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C' .
                '1AC4D77FC94CADC083E67984050B75EBAE5DD2809BD638016F723',
                16
            )
        );
        $this->setBasePoint(
            new BigInteger(
                '81AEE4BDD82ED9645A21322E9C4C6A9385ED9F70B5D916C1B43B62EEF4D0098EFF3B1F78E2D' .
                '0D48D50D1687B93B97D5F7C6D5047406A5E688B352209BCB9F822',
                16
            ),
            new BigInteger(
                '7DDE385D566332ECC0EABFA9CF7822FDF209F70024A57B1AA000C55B881F8111B2DCDE494A5' .
                'F485E5BCA4BD88A2763AED1CA2B2FA8F0540678CD1E0F3AD80892',
                16
            )
        );
        $this->setOrder(new BigInteger(
            'AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA70330870553E5C414CA' .
            '92619418661197FAC10471DB1D381085DDADDB58796829CA90069',
            16
        ));
    }
}
<?php

/**
 * Ed448
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\TwistedEdwards;
use phpseclib4\Crypt\Hash;
use phpseclib4\Exception\{InvalidStateException, UnexpectedValueException, UnsupportedValueException};
use phpseclib4\Math\BigInteger;
use phpseclib4\Math\PrimeField\Integer as PrimeInteger;

/** @psalm-api */
class Ed448 extends TwistedEdwards
{
    public const HASH = 'shake256-912';
    public const SIZE = 57;

    public function __construct()
    {
        // 2^448 - 2^224 - 1
        $this->setModulo(new BigInteger(
            'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE' .
            'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF',
            16
        ));
        $this->setCoefficients(
            new BigInteger(1),
            // -39081
            new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE' .
                           'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6756', 16)
        );
        $this->setBasePoint(
            new BigInteger('4F1970C66BED0DED221D15A622BF36DA9E146570470F1767EA6DE324' .
                           'A3D3A46412AE1AF72AB66511433B80E18B00938E2626A82BC70CC05E', 16),
            new BigInteger('693F46716EB6BC248876203756C9C7624BEA73736CA3984087789C1E' .
                           '05A0C2D73AD3FF1CE67C39C4FDBD132C4ED7C8AD9808795BF230FA14', 16)
        );
        $this->setOrder(new BigInteger(
            '3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' .
            '7CCA23E9C44EDB49AED63690216CC2728DC58F552378C292AB5844F3',
            16
        ));
    }

    /**
     * Recover X from Y
     *
     * Implements steps 2-4 at https://tools.ietf.org/html/rfc8032#section-5.2.3
     *
     * Used by EC\Keys\Common.php
     *
     * @return PrimeInteger[]
     */
    public function recoverX(BigInteger $y, bool $sign): array
    {
        $y = $this->factory->newInteger($y);

        $y2 = $y->multiply($y);
        $u = $y2->subtract($this->one);
        $v = $this->d->multiply($y2)->subtract($this->one);
        $x2 = $u->divide($v);
        if ($x2->equals($this->zero)) {
            if ($sign) {
                throw new UnexpectedValueException('Unable to recover X coordinate (x2 = 0)');
            }
            return [clone $this->zero, $y];
        }
        // find the square root
        $exp = $this->getModulo()->add(new BigInteger(1));
        $exp = $exp->bitwise_rightShift(2);
        $x = $x2->pow($exp);

        if (!$x->multiply($x)->subtract($x2)->equals($this->zero)) {
            throw new UnexpectedValueException('Unable to recover X coordinate');
        }
        if ($x->isOdd() != $sign) {
            $x = $x->negate();
        }

        return [$x, $y];
    }

    /**
     * Extract Secret Scalar
     *
     * Implements steps 1-3 at https://tools.ietf.org/html/rfc8032#section-5.2.5
     *
     * Used by the various key handlers
     */
    public function extractSecret(string $str): array
    {
        if (strlen($str) != 57) {
            throw new UnexpectedValueException('Private Key should be 57-bytes long');
        }
        // 1.  Hash the 57-byte private key using SHAKE256(x, 114), storing the
        //     digest in a 114-octet large buffer, denoted h.  Only the lower 57
        //     bytes are used for generating the public key.
        $hash = new Hash('shake256-912');
        $h = $hash->hash($str);
        $h = substr($h, 0, 57);
        // 2.  Prune the buffer: The two least significant bits of the first
        //     octet are cleared, all eight bits the last octet are cleared, and
        //     the highest bit of the second to last octet is set.
        $h[0] = $h[0] & chr(0xFC);
        $h = strrev($h);
        $h[0] = "\0";
        $h[1] = $h[1] | chr(0x80);
        // 3.  Interpret the buffer as the little-endian integer, forming a
        //     secret scalar s.
        $dA = new BigInteger($h, 256);

        return [
            'dA' => $dA,
            'secret' => $str,
        ];
    }

    /**
     * Encode a point as a string
     */
    public function encodePoint(array $point): string
    {
        [$x, $y] = $point;
        $y = "\0" . $y->toBytes();
        if ($x->isOdd()) {
            $y[0] = $y[0] | chr(0x80);
        }
        $y = strrev($y);

        return $y;
    }

    /**
     * Creates a random scalar multiplier
     */
    public function createRandomMultiplier(): BigInteger
    {
        return $this->extractSecret(random_bytes(57))['dA'];
    }

    /**
     * Converts an affine point to an extended homogeneous coordinate
     *
     * From https://tools.ietf.org/html/rfc8032#section-5.2.4 :
     *
     * A point (x,y) is represented in extended homogeneous coordinates (X, Y, Z, T),
     * with x = X/Z, y = Y/Z, x * y = T/Z.
     *
     * @return PrimeInteger[]
     */
    public function convertToInternal(array $p): array
    {
        if (empty($p)) {
            return [clone $this->zero, clone $this->one, clone $this->one];
        }

        if (isset($p[2])) {
            return $p;
        }

        $p[2] = clone $this->one;

        return $p;
    }

    /**
     * Doubles a point on a curve
     *
     * @return PrimeInteger[]
     */
    public function doublePoint(array $p): array
    {
        if (!isset($this->factory)) {
            throw new InvalidStateException('setModulo needs to be called before this method');
        }

        if (!count($p)) {
            return [];
        }

        if (!isset($p[2])) {
            throw new UnsupportedValueException('Affine coordinates need to be manually converted to "Jacobi" coordinates or vice versa');
        }

        // from https://tools.ietf.org/html/rfc8032#page-18

        [$x1, $y1, $z1] = $p;

        $b = $x1->add($y1);
        $b = $b->multiply($b);
        $c = $x1->multiply($x1);
        $d = $y1->multiply($y1);
        $e = $c->add($d);
        $h = $z1->multiply($z1);
        $j = $e->subtract($this->two->multiply($h));

        $x3 = $b->subtract($e)->multiply($j);
        $y3 = $c->subtract($d)->multiply($e);
        $z3 = $e->multiply($j);

        return [$x3, $y3, $z3];
    }

    /**
     * Adds two points on the curve
     *
     * @return PrimeInteger[]
     */
    public function addPoint(array $p, array $q): array
    {
        if (!isset($this->factory)) {
            throw new InvalidStateException('setModulo needs to be called before this method');
        }

        if (!count($p) || !count($q)) {
            if (count($q)) {
                return $q;
            }
            if (count($p)) {
                return $p;
            }
            return [];
        }

        if (!isset($p[2]) || !isset($q[2])) {
            throw new UnsupportedValueException('Affine coordinates need to be manually converted to "Jacobi" coordinates or vice versa');
        }

        if ($p[0]->equals($q[0])) {
            return !$p[1]->equals($q[1]) ? [] : $this->doublePoint($p);
        }

        // from https://tools.ietf.org/html/rfc8032#page-17

        [$x1, $y1, $z1] = $p;
        [$x2, $y2, $z2] = $q;

        $a = $z1->multiply($z2);
        $b = $a->multiply($a);
        $c = $x1->multiply($x2);
        $d = $y1->multiply($y2);
        $e = $this->d->multiply($c)->multiply($d);
        $f = $b->subtract($e);
        $g = $b->add($e);
        $h = $x1->add($y1)->multiply($x2->add($y2));

        $x3 = $a->multiply($f)->multiply($h->subtract($c)->subtract($d));
        $y3 = $a->multiply($g)->multiply($d->subtract($c));
        $z3 = $f->multiply($g);

        return [$x3, $y3, $z3];
    }
}
<?php

/**
 * secp112r2
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Prime;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class secp112r2 extends Prime
{
    public function __construct()
    {
        // same modulo as secp112r1
        $this->setModulo(new BigInteger('DB7C2ABF62E35E668076BEAD208B', 16));
        $this->setCoefficients(
            new BigInteger('6127C24C05F38A0AAAF65C0EF02C', 16),
            new BigInteger('51DEF1815DB5ED74FCC34C85D709', 16)
        );
        $this->setBasePoint(
            new BigInteger('4BA30AB5E892B4E1649DD0928643', 16),
            new BigInteger('ADCD46F5882E3747DEF36E956E97', 16)
        );
        $this->setOrder(new BigInteger('36DF0AAFD8B8D7597CA10520D04B', 16));
    }
}
<?php

/**
 * secp160k1
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\KoblitzPrime;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class secp160k1 extends KoblitzPrime
{
    public function __construct()
    {
        // same as secp160r2
        $this->setModulo(new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73', 16));
        $this->setCoefficients(
            new BigInteger('0000000000000000000000000000000000000000', 16),
            new BigInteger('0000000000000000000000000000000000000007', 16)
        );
        $this->setBasePoint(
            new BigInteger('3B4C382CE37AA192A4019E763036F4F5DD4D7EBB', 16),
            new BigInteger('938CF935318FDCED6BC28286531733C3F03C4FEE', 16)
        );
        $this->setOrder(new BigInteger('0100000000000000000001B8FA16DFAB9ACA16B6B3', 16));

        $this->basis = [];
        $this->basis[] = [
            'a' => new BigInteger('0096341F1138933BC2F505', -16),
            'b' => new BigInteger('FF6E9D0418C67BB8D5F562', -16),
        ];
        $this->basis[] = [
            'a' => new BigInteger('01BDCB3A09AAAABEAFF4A8', -16),
            'b' => new BigInteger('04D12329FF0EF498EA67', -16),
        ];
        $this->beta = $this->factory->newInteger(new BigInteger('645B7345A143464942CC46D7CF4D5D1E1E6CBB68', -16));
    }
}
<?php

/**
 * nistk233
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

/** @psalm-api */
final class nistk233 extends sect233k1
{
}
<?php

/**
 * brainpoolP160t1
 *
 * This curve is a twisted version of brainpoolP160r1 with A = -3. With brainpool,
 * the curves ending in r1 are the "regular" curves and the curves ending in "t1"
 * are the twisted version of the r1 curves. Per https://tools.ietf.org/html/rfc5639#page-7
 * you can convert a point on an r1 curve to a point on a t1 curve thusly:
 *
 *     F(x,y) := (x*Z^2, y*Z^3)
 *
 * The advantage of A = -3 is that some of the point doubling and point addition can be
 * slightly optimized. See http://hyperelliptic.org/EFD/g1p/auto-shortw-projective-3.html
 * vs http://hyperelliptic.org/EFD/g1p/auto-shortw-projective.html for example.
 *
 * phpseclib does not currently take advantage of this optimization opportunity
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Prime;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class brainpoolP160t1 extends Prime
{
    public function __construct()
    {
        $this->setModulo(new BigInteger('E95E4A5F737059DC60DFC7AD95B3D8139515620F', 16));
        $this->setCoefficients(
            new BigInteger('E95E4A5F737059DC60DFC7AD95B3D8139515620C', 16), // eg. -3
            new BigInteger('7A556B6DAE535B7B51ED2C4D7DAA7A0B5C55F380', 16)
        );
        $this->setBasePoint(
            new BigInteger('B199B13B9B34EFC1397E64BAEB05ACC265FF2378', 16),
            new BigInteger('ADD6718B7C7C1961F0991B842443772152C9E0AD', 16)
        );
        $this->setOrder(new BigInteger('E95E4A5F737059DC60DF5991D45029409E60FC09', 16));
    }
}
<?php

/**
 * prime239v1
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Prime;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class prime239v1 extends Prime
{
    public function __construct()
    {
        $this->setModulo(new BigInteger('7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFF', 16));
        $this->setCoefficients(
            new BigInteger('7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFC', 16),
            new BigInteger('6B016C3BDCF18941D0D654921475CA71A9DB2FB27D1D37796185C2942C0A', 16)
        );
        $this->setBasePoint(
            new BigInteger('0FFA963CDCA8816CCC33B8642BEDF905C3D358573D3F27FBBD3B3CB9AAAF', 16),
            new BigInteger('7DEBE8E4E90A5DAE6E4054CA530BA04654B36818CE226B39FCCB7B02F1AE', 16)
        );
        $this->setOrder(new BigInteger('7FFFFFFFFFFFFFFFFFFFFFFF7FFFFF9E5E9A9F5D9071FBD1522688909D0B', 16));
    }
}
<?php

/**
 * secp160r2
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Prime;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class secp160r2 extends Prime
{
    public function __construct()
    {
        // same as secp160k1
        $this->setModulo(new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73', 16));
        $this->setCoefficients(
            new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC70', 16),
            new BigInteger('B4E134D3FB59EB8BAB57274904664D5AF50388BA', 16)
        );
        $this->setBasePoint(
            new BigInteger('52DCB034293A117E1F4FF11B30F7199D3144CE6D', 16),
            new BigInteger('FEAFFEF2E331F296E071FA0DF9982CFEA7D43F2E', 16)
        );
        $this->setOrder(new BigInteger('0100000000000000000000351EE786A818F3A1A16B', 16));
    }
}
<?php

/**
 * brainpoolP512t1
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Prime;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class brainpoolP512t1 extends Prime
{
    public function __construct()
    {
        $this->setModulo(new BigInteger(
            'AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC' .
            '66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F3',
            16
        ));
        $this->setCoefficients(
            new BigInteger(
                'AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC' .
                '66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F0',
                16
            ), // eg. -3
            new BigInteger(
                '7CBBBCF9441CFAB76E1890E46884EAE321F70C0BCB4981527897504BEC3E36A62BCDFA23049' .
                '76540F6450085F2DAE145C22553B465763689180EA2571867423E',
                16
            )
        );
        $this->setBasePoint(
            new BigInteger(
                '640ECE5C12788717B9C1BA06CBC2A6FEBA85842458C56DDE9DB1758D39C0313D82BA51735CD' .
                'B3EA499AA77A7D6943A64F7A3F25FE26F06B51BAA2696FA9035DA',
                16
            ),
            new BigInteger(
                '5B534BD595F5AF0FA2C892376C84ACE1BB4E3019B71634C01131159CAE03CEE9D9932184BEE' .
                'F216BD71DF2DADF86A627306ECFF96DBB8BACE198B61E00F8B332',
                16
            )
        );
        $this->setOrder(new BigInteger(
            'AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA70330870553E5C414CA' .
            '92619418661197FAC10471DB1D381085DDADDB58796829CA90069',
            16
        ));
    }
}
<?php

/**
 * brainpoolP384r1
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Prime;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class brainpoolP384r1 extends Prime
{
    public function __construct()
    {
        $this->setModulo(new BigInteger(
            '8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901D1A7' .
            '1874700133107EC53',
            16
        ));
        $this->setCoefficients(
            new BigInteger(
                '7BC382C63D8C150C3C72080ACE05AFA0C2BEA28E4FB22787139165EFBA91F90F8AA5814A503' .
                'AD4EB04A8C7DD22CE2826',
                16
            ),
            new BigInteger(
                '4A8C7DD22CE28268B39B55416F0447C2FB77DE107DCD2A62E880EA53EEB62D57CB4390295DB' .
                'C9943AB78696FA504C11',
                16
            )
        );
        $this->setBasePoint(
            new BigInteger(
                '1D1C64F068CF45FFA2A63A81B7C13F6B8847A3E77EF14FE3DB7FCAFE0CBD10E8E826E03436D' .
                '646AAEF87B2E247D4AF1E',
                16
            ),
            new BigInteger(
                '8ABE1D7520F9C2A45CB1EB8E95CFD55262B70B29FEEC5864E19C054FF99129280E464621779' .
                '1811142820341263C5315',
                16
            )
        );
        $this->setOrder(new BigInteger(
            '8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B31F166E6CAC0425A7CF3AB6AF6B7FC31' .
            '03B883202E9046565',
            16
        ));
    }
}
<?php

/**
 * nistp256
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

/** @psalm-api */
final class nistp256 extends secp256r1
{
}
<?php

/**
 * brainpoolP192r1
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Prime;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class brainpoolP192r1 extends Prime
{
    public function __construct()
    {
        $this->setModulo(new BigInteger('C302F41D932A36CDA7A3463093D18DB78FCE476DE1A86297', 16));
        $this->setCoefficients(
            new BigInteger('6A91174076B1E0E19C39C031FE8685C1CAE040E5C69A28EF', 16),
            new BigInteger('469A28EF7C28CCA3DC721D044F4496BCCA7EF4146FBF25C9', 16)
        );
        $this->setBasePoint(
            new BigInteger('C0A0647EAAB6A48753B033C56CB0F0900A2F5C4853375FD6', 16),
            new BigInteger('14B690866ABD5BB88B5F4828C1490002E6773FA2FA299B8F', 16)
        );
        $this->setOrder(new BigInteger('C302F41D932A36CDA7A3462F9E9E916B5BE8F1029AC4ACC1', 16));
    }
}
<?php

/**
 * sect283r1
 *
 * PHP version 8.1+
 *
 * @author    Jim Wiggint  on <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Binary;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class sect283r1 extends Binary
{
    public function __construct()
    {
        $this->setModulo(283, 12, 7, 5, 0);
        $this->setCoefficients(
            '000000000000000000000000000000000000000000000000000000000000000000000001',
            '027B680AC8B8596DA5A4AF8A19A0303FCA97FD7645309FA2A581485AF6263E313B79A2F5'
        );
        $this->setBasePoint(
            '05F939258DB7DD90E1934F8C70B0DFEC2EED25B8557EAC9C80E2E198F8CDBECD86B12053',
            '03676854FE24141CB98FE6D4B20D02B4516FF702350EDDB0826779C813F0DF45BE8112F4'
        );
        $this->setOrder(new BigInteger('03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEF90399660FC938A90165B042A7CEFADB307', 16));
    }
}
<?php

/**
 * sect571k1
 *
 * PHP version 8.1+
 *
 * @author    Jim Wiggint  on <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Binary;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class sect571k1 extends Binary
{
    public function __construct()
    {
        $this->setModulo(571, 10, 5, 2, 0);
        $this->setCoefficients(
            '000000000000000000000000000000000000000000000000000000000000000000000000' .
            '000000000000000000000000000000000000000000000000000000000000000000000000',
            '000000000000000000000000000000000000000000000000000000000000000000000000' .
            '000000000000000000000000000000000000000000000000000000000000000000000001'
        );
        $this->setBasePoint(
            '026EB7A859923FBC82189631F8103FE4AC9CA2970012D5D46024804801841CA443709584' .
            '93B205E647DA304DB4CEB08CBBD1BA39494776FB988B47174DCA88C7E2945283A01C8972',
            '0349DC807F4FBF374F4AEADE3BCA95314DD58CEC9F307A54FFC61EFC006D8A2C9D4979C0' .
            'AC44AEA74FBEBBB9F772AEDCB620B01A7BA7AF1B320430C8591984F601CD4C143EF1C7A3'
        );
        $this->setOrder(new BigInteger(
            '020000000000000000000000000000000000000000000000000000000000000000000000' .
            '131850E1F19A63E4B391A8DB917F4138B630D84BE5D639381E91DEB45CFE778F637C1001',
            16
        ));
    }
}
<?php

/**
 * nistt571
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

/** @psalm-api */
final class nistt571 extends sect571k1
{
}
<?php

/**
 * secp112r1
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Prime;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class secp112r1 extends Prime
{
    public function __construct()
    {
        $this->setModulo(new BigInteger('DB7C2ABF62E35E668076BEAD208B', 16));
        $this->setCoefficients(
            new BigInteger('DB7C2ABF62E35E668076BEAD2088', 16),
            new BigInteger('659EF8BA043916EEDE8911702B22', 16)
        );
        $this->setBasePoint(
            new BigInteger('09487239995A5EE76B55F9C2F098', 16),
            new BigInteger('A89CE5AF8724C0A23E0E0FF77500', 16)
        );
        $this->setOrder(new BigInteger('DB7C2ABF62E35E7628DFAC6561C5', 16));
    }
}
<?php

/**
 * secp224r1
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Prime;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class secp224r1 extends Prime
{
    public function __construct()
    {
        $this->setModulo(new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001', 16));
        $this->setCoefficients(
            new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFE', 16),
            new BigInteger('B4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4', 16)
        );
        $this->setBasePoint(
            new BigInteger('B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21', 16),
            new BigInteger('BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34', 16)
        );
        $this->setOrder(new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D', 16));
    }
}
<?php

/**
 * Curve25519
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2019-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Montgomery;
use phpseclib4\Exception\UnexpectedValueException;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class Curve25519 extends Montgomery
{
    public const SIZE = 32;

    public function __construct()
    {
        // 2^255 - 19
        $this->setModulo(new BigInteger('7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED', 16));
        $this->a24 = $this->factory->newInteger(new BigInteger('121666'));
        $this->p = [$this->factory->newInteger(new BigInteger(9))];
        // 2^252 + 0x14def9dea2f79cd65812631a5cf5d3ed
        $this->setOrder(new BigInteger('1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED', 16));

        /*
        $this->setCoefficients(
            new BigInteger('486662'), // a
        );
        $this->setBasePoint(
            new BigInteger(9),
            new BigInteger('14781619447589544791020593568409986887264606134616475288964881837755586237401')
        );
        */
    }

    /**
     * Multiply a point on the curve by a scalar
     *
     * Modifies the scalar as described at https://tools.ietf.org/html/rfc7748#page-8
     */
    public function multiplyPoint(array $p, BigInteger $d): array
    {
        $d = $d->toBytes();
        $d = str_pad($d, 32, "\0", STR_PAD_LEFT);

        //$r = strrev(sodium_crypto_scalarmult($d, strrev($p[0]->toBytes())));
        //return [$this->factory->newInteger(new BigInteger($r, 256))];

        $d &= "\xF8" . str_repeat("\xFF", 30) . "\x7F";
        $d = strrev($d);
        $d |= "\x40";
        $d = new BigInteger($d, -256);

        return parent::multiplyPoint($p, $d);
    }

    /**
     * Creates a random scalar multiplier
     */
    public function createRandomMultiplier(): BigInteger
    {
        return BigInteger::random(256);
    }

    /**
     * Performs range check
     */
    public function rangeCheck(BigInteger $x): void
    {
        if ($x->getLength() > 256 || $x->isNegative()) {
            throw new UnexpectedValueException('x must be a positive integer less than 256 bytes in length');
        }
    }
}
<?php

/**
 * sect239k1
 *
 * PHP version 8.1+
 *
 * @author    Jim Wiggint  on <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Binary;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class sect239k1 extends Binary
{
    public function __construct()
    {
        $this->setModulo(239, 158, 0);
        $this->setCoefficients(
            '000000000000000000000000000000000000000000000000000000000000',
            '000000000000000000000000000000000000000000000000000000000001'
        );
        $this->setBasePoint(
            '29A0B6A887A983E9730988A68727A8B2D126C44CC2CC7B2A6555193035DC',
            '76310804F12E549BDB011C103089E73510ACB275FC312A5DC6B76553F0CA'
        );
        $this->setOrder(new BigInteger('2000000000000000000000000000005A79FEC67CB6E91F1C1DA800E478A5', 16));
    }
}
<?php

/**
 * sect193r2
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Binary;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class sect193r2 extends Binary
{
    public function __construct()
    {
        $this->setModulo(193, 15, 0);
        $this->setCoefficients(
            '0163F35A5137C2CE3EA6ED8667190B0BC43ECD69977702709B',
            '00C9BB9E8927D4D64C377E2AB2856A5B16E3EFB7F61D4316AE'
        );
        $this->setBasePoint(
            '00D9B67D192E0367C803F39E1A7E82CA14A651350AAE617E8F',
            '01CE94335607C304AC29E7DEFBD9CA01F596F927224CDECF6C'
        );
        $this->setOrder(new BigInteger('010000000000000000000000015AAB561B005413CCD4EE99D5', 16));
    }
}
<?php

/**
 * prime239v2
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Prime;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class prime239v2 extends Prime
{
    public function __construct()
    {
        $this->setModulo(new BigInteger('7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFF', 16));
        $this->setCoefficients(
            new BigInteger('7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFC', 16),
            new BigInteger('617FAB6832576CBBFED50D99F0249C3FEE58B94BA0038C7AE84C8C832F2C', 16)
        );
        $this->setBasePoint(
            new BigInteger('38AF09D98727705120C921BB5E9E26296A3CDCF2F35757A0EAFD87B830E7', 16),
            new BigInteger('5B0125E4DBEA0EC7206DA0FC01D9B081329FB555DE6EF460237DFF8BE4BA', 16)
        );
        $this->setOrder(new BigInteger('7FFFFFFFFFFFFFFFFFFFFFFF800000CFA7E8594377D414C03821BC582063', 16));
    }
}
<?php

/**
 * nistp384
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

/** @psalm-api */
final class nistp384 extends secp384r1
{
}
<?php

/**
 * sect113r1
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Binary;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class sect113r1 extends Binary
{
    public function __construct()
    {
        $this->setModulo(113, 9, 0);
        $this->setCoefficients(
            '003088250CA6E7C7FE649CE85820F7',
            '00E8BEE4D3E2260744188BE0E9C723'
        );
        $this->setBasePoint(
            '009D73616F35F4AB1407D73562C10F',
            '00A52830277958EE84D1315ED31886'
        );
        $this->setOrder(new BigInteger('0100000000000000D9CCEC8A39E56F', 16));
    }
}
<?php

/**
 * secp128r2
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Prime;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class secp128r2 extends Prime
{
    public function __construct()
    {
        // same as secp128r1
        $this->setModulo(new BigInteger('FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF', 16));
        $this->setCoefficients(
            new BigInteger('D6031998D1B3BBFEBF59CC9BBFF9AEE1', 16),
            new BigInteger('5EEEFCA380D02919DC2C6558BB6D8A5D', 16)
        );
        $this->setBasePoint(
            new BigInteger('7B6AA5D85E572983E6FB32A7CDEBC140', 16),
            new BigInteger('27B6916A894D3AEE7106FE805FC34B44', 16)
        );
        $this->setOrder(new BigInteger('3FFFFFFF7FFFFFFFBE0024720613B5A3', 16));
    }
}
<?php

/**
 * sect233r1
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Binary;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class sect233r1 extends Binary
{
    public function __construct()
    {
        $this->setModulo(233, 74, 0);
        $this->setCoefficients(
            '000000000000000000000000000000000000000000000000000000000001',
            '0066647EDE6C332C7F8C0923BB58213B333B20E9CE4281FE115F7D8F90AD'
        );
        $this->setBasePoint(
            '00FAC9DFCBAC8313BB2139F1BB755FEF65BC391F8B36F8F8EB7371FD558B',
            '01006A08A41903350678E58528BEBF8A0BEFF867A7CA36716F7E01F81052'
        );
        $this->setOrder(new BigInteger('01000000000000000000000000000013E974E72F8A6922031D2603CFE0D7', 16));
    }
}
<?php

/**
 * nistp224
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

/** @psalm-api */
final class nistp224 extends secp224r1
{
}
<?php

/**
 * secp160r1
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Prime;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class secp160r1 extends Prime
{
    public function __construct()
    {
        $this->setModulo(new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFF', 16));
        $this->setCoefficients(
            new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFC', 16),
            new BigInteger('1C97BEFC54BD7A8B65ACF89F81D4D4ADC565FA45', 16)
        );
        $this->setBasePoint(
            new BigInteger('4A96B5688EF573284664698968C38BB913CBFC82', 16),
            new BigInteger('23A628553168947D59DCC912042351377AC5FB32', 16)
        );
        $this->setOrder(new BigInteger('0100000000000000000001F4C8F927AED3CA752257', 16));
    }
}
<?php

/**
 * secp384r1
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Prime;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class secp384r1 extends Prime
{
    public function __construct()
    {
        $this->setModulo(new BigInteger(
            'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF',
            16
        ));
        $this->setCoefficients(
            new BigInteger(
                'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC',
                16
            ),
            new BigInteger(
                'B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF',
                16
            )
        );
        $this->setBasePoint(
            new BigInteger(
                'AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7',
                16
            ),
            new BigInteger(
                '3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F',
                16
            )
        );
        $this->setOrder(new BigInteger(
            'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973',
            16
        ));
    }
}
<?php

/**
 * Ed25519
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\TwistedEdwards;
use phpseclib4\Crypt\Hash;
use phpseclib4\Exception\{InvalidStateException, UnexpectedValueException, UnsupportedValueException};
use phpseclib4\Math\BigInteger;
use phpseclib4\Math\PrimeField\Integer as PrimeInteger;

/** @psalm-api */
class Ed25519 extends TwistedEdwards
{
    public const HASH = 'sha512';
    /*
      Per https://tools.ietf.org/html/rfc8032#page-6 EdDSA has several parameters, one of which is b:

      2.   An integer b with 2^(b-1) > p.  EdDSA public keys have exactly b
           bits, and EdDSA signatures have exactly 2*b bits.  b is
           recommended to be a multiple of 8, so public key and signature
           lengths are an integral number of octets.

      SIZE corresponds to b
    */
    public const SIZE = 32;

    public function __construct()
    {
        // 2^255 - 19
        $modulo = new BigInteger('7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED', 16);
        $this->setModulo($modulo);
        $this->setCoefficients(
            // -1
            new BigInteger('7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEC', 16), // a
            // -121665/121666
            new BigInteger('52036CEE2B6FFE738CC740797779E89800700A4D4141D8AB75EB4DCA135978A3', 16)  // d
        );
        $this->setBasePoint(
            new BigInteger('216936D3CD6E53FEC0A4E231FDD6DC5C692CC7609525A7B2C9562D608F25D51A', 16),
            new BigInteger('6666666666666666666666666666666666666666666666666666666666666658', 16)
        );
        $this->setOrder(new BigInteger('1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED', 16));
        // algorithm 14.47 from http://cacr.uwaterloo.ca/hac/about/chap14.pdf#page=16
        /*
        $this->setReduction(function(BigInteger $x) use ($modulo) {
            $parts = $x->bitwise_split(255);

            if (count($parts) > 2) {
                [, $r] = $x->divide($modulo);
                return $r;
            }

            $zero = new BigInteger();
            $c = new BigInteger(19);

            switch (count($parts)) {
                case 2:
                    [$qi, $ri] = $parts;
                    break;
                case 1:
                    $qi = $zero;
                    [$ri] = $parts;
                    break;
                case 0:
                    return $zero;
            }
            $r = $ri;

            while ($qi->compare($zero) > 0) {
                $temp = $qi->multiply($c)->bitwise_split(255);
                if (count($temp) == 2) {
                    [$qi, $ri] = $temp;
                } else {
                    $qi = $zero;
                    [$ri] = $temp;
                }
                $r = $r->add($ri);
            }

            while ($r->compare($modulo) > 0) {
                $r = $r->subtract($modulo);
            }
            return $r;
        });
        */
    }

    /**
     * Recover X from Y
     *
     * Implements steps 2-4 at https://tools.ietf.org/html/rfc8032#section-5.1.3
     *
     * Used by EC\Keys\Common.php
     *
     * @return PrimeInteger[]
     */
    public function recoverX(BigInteger $y, bool $sign): array
    {
        $y = $this->factory->newInteger($y);

        $y2 = $y->multiply($y);
        $u = $y2->subtract($this->one);
        $v = $this->d->multiply($y2)->add($this->one);
        $x2 = $u->divide($v);
        if ($x2->equals($this->zero)) {
            if ($sign) {
                throw new UnexpectedValueException('Unable to recover X coordinate (x2 = 0)');
            }
            return [clone $this->zero, $y];
        }
        // find the square root
        /* we don't do $x2->squareRoot() because, quoting from
           https://tools.ietf.org/html/rfc8032#section-5.1.1:

           "For point decoding or "decompression", square roots modulo p are
            needed.  They can be computed using the Tonelli-Shanks algorithm or
            the special case for p = 5 (mod 8).  To find a square root of a,
            first compute the candidate root x = a^((p+3)/8) (mod p)."
         */
        $exp = $this->getModulo()->add(new BigInteger(3));
        $exp = $exp->bitwise_rightShift(3);
        $x = $x2->pow($exp);

        // If v x^2 = -u (mod p), set x <-- x * 2^((p-1)/4), which is a square root.
        if (!$x->multiply($x)->subtract($x2)->equals($this->zero)) {
            $temp = $this->getModulo()->subtract(new BigInteger(1));
            $temp = $temp->bitwise_rightShift(2);
            $temp = $this->two->pow($temp);
            $x = $x->multiply($temp);
            if (!$x->multiply($x)->subtract($x2)->equals($this->zero)) {
                throw new UnexpectedValueException('Unable to recover X coordinate');
            }
        }
        if ($x->isOdd() != $sign) {
            $x = $x->negate();
        }

        return [$x, $y];
    }

    /**
     * Extract Secret Scalar
     *
     * Implements steps 1-3 at https://tools.ietf.org/html/rfc8032#section-5.1.5
     *
     * Used by the various key handlers
     */
    public function extractSecret(string $str): array
    {
        if (strlen($str) != 32) {
            throw new UnexpectedValueException('Private Key should be 32-bytes long');
        }
        // 1.  Hash the 32-byte private key using SHA-512, storing the digest in
        //     a 64-octet large buffer, denoted h.  Only the lower 32 bytes are
        //     used for generating the public key.
        $hash = new Hash('sha512');
        $h = $hash->hash($str);
        $h = substr($h, 0, 32);
        // 2.  Prune the buffer: The lowest three bits of the first octet are
        //     cleared, the highest bit of the last octet is cleared, and the
        //     second highest bit of the last octet is set.
        $h[0] = $h[0] & chr(0xF8);
        $h = strrev($h);
        $h[0] = ($h[0] & chr(0x3F)) | chr(0x40);
        // 3.  Interpret the buffer as the little-endian integer, forming a
        //     secret scalar s.
        $dA = new BigInteger($h, 256);

        return [
            'dA' => $dA,
            'secret' => $str,
        ];
    }

    /**
     * Encode a point as a string
     */
    public function encodePoint(array $point): string
    {
        [$x, $y] = $point;
        $y = $y->toBytes();
        $y[0] = $y[0] & chr(0x7F);
        if ($x->isOdd()) {
            $y[0] = $y[0] | chr(0x80);
        }
        $y = strrev($y);

        return $y;
    }

    /**
     * Creates a random scalar multiplier
     */
    public function createRandomMultiplier(): BigInteger
    {
        return $this->extractSecret(random_bytes(32))['dA'];
    }

    /**
     * Converts an affine point to an extended homogeneous coordinate
     *
     * From https://tools.ietf.org/html/rfc8032#section-5.1.4 :
     *
     * A point (x,y) is represented in extended homogeneous coordinates (X, Y, Z, T),
     * with x = X/Z, y = Y/Z, x * y = T/Z.
     *
     * @return PrimeInteger[]
     */
    public function convertToInternal(array $p): array
    {
        if (empty($p)) {
            return [clone $this->zero, clone $this->one, clone $this->one, clone $this->zero];
        }

        if (isset($p[2])) {
            return $p;
        }

        $p[2] = clone $this->one;
        $p[3] = $p[0]->multiply($p[1]);

        return $p;
    }

    /**
     * Doubles a point on a curve
     *
     * @return PrimeInteger[]
     */
    public function doublePoint(array $p): array
    {
        if (!isset($this->factory)) {
            throw new InvalidStateException('setModulo needs to be called before this method');
        }

        if (!count($p)) {
            return [];
        }

        if (!isset($p[2])) {
            throw new UnsupportedValueException('Affine coordinates need to be manually converted to "Jacobi" coordinates or vice versa');
        }

        // from https://tools.ietf.org/html/rfc8032#page-12
        [$x1, $y1, $z1] = $p; // $p has a fourth parameter as well - $t1

        $a = $x1->multiply($x1);
        $b = $y1->multiply($y1);
        $c = $this->two->multiply($z1)->multiply($z1);
        $h = $a->add($b);
        $temp = $x1->add($y1);
        $e = $h->subtract($temp->multiply($temp));
        $g = $a->subtract($b);
        $f = $c->add($g);

        $x3 = $e->multiply($f);
        $y3 = $g->multiply($h);
        $t3 = $e->multiply($h);
        $z3 = $f->multiply($g);

        return [$x3, $y3, $z3, $t3];
    }

    /**
     * Adds two points on the curve
     *
     * @return PrimeInteger[]
     */
    public function addPoint(array $p, array $q): array
    {
        if (!isset($this->factory)) {
            throw new InvalidStateException('setModulo needs to be called before this method');
        }

        if (!count($p) || !count($q)) {
            if (count($q)) {
                return $q;
            }
            if (count($p)) {
                return $p;
            }
            return [];
        }

        if (!isset($p[2]) || !isset($q[2])) {
            throw new UnsupportedValueException('Affine coordinates need to be manually converted to "Jacobi" coordinates or vice versa');
        }

        if ($p[0]->equals($q[0])) {
            return !$p[1]->equals($q[1]) ? [] : $this->doublePoint($p);
        }

        // from https://tools.ietf.org/html/rfc8032#page-12

        [$x1, $y1, $z1, $t1] = $p;
        [$x2, $y2, $z2, $t2] = $q;

        $a = $y1->subtract($x1)->multiply($y2->subtract($x2));
        $b = $y1->add($x1)->multiply($y2->add($x2));
        $c = $t1->multiply($this->two)->multiply($this->d)->multiply($t2);
        $d = $z1->multiply($this->two)->multiply($z2);
        $e = $b->subtract($a);
        $f = $d->subtract($c);
        $g = $d->add($c);
        $h = $b->add($a);

        $x3 = $e->multiply($f);
        $y3 = $g->multiply($h);
        $t3 = $e->multiply($h);
        $z3 = $f->multiply($g);

        return [$x3, $y3, $z3, $t3];
    }
}
<?php

/**
 * sect163k1
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Binary;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class sect163k1 extends Binary
{
    public function __construct()
    {
        $this->setModulo(163, 7, 6, 3, 0);
        $this->setCoefficients(
            '000000000000000000000000000000000000000001',
            '000000000000000000000000000000000000000001'
        );
        $this->setBasePoint(
            '02FE13C0537BBC11ACAA07D793DE4E6D5E5C94EEE8',
            '0289070FB05D38FF58321F2E800536D538CCDAA3D9'
        );
        $this->setOrder(new BigInteger('04000000000000000000020108A2E0CC0D99F8A5EF', 16));
    }
}
<?php

/**
 * prime256v1
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

/** @psalm-api */
final class prime256v1 extends secp256r1
{
}
<?php

/**
 * sect409k1
 *
 * PHP version 8.1+
 *
 * @author    Jim Wiggint  on <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Binary;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class sect409k1 extends Binary
{
    public function __construct()
    {
        $this->setModulo(409, 87, 0);
        $this->setCoefficients(
            '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
            '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001'
        );
        $this->setBasePoint(
            '0060F05F658F49C1AD3AB1890F7184210EFD0987E307C84C27ACCFB8F9F67CC2C460189EB5AAAA62EE222EB1B35540CFE9023746',
            '01E369050B7C4E42ACBA1DACBF04299C3460782F918EA427E6325165E9EA10E3DA5F6C42E9C55215AA9CA27A5863EC48D8E0286B'
        );
        $this->setOrder(new BigInteger(
            '7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE5F' .
            '83B2D4EA20400EC4557D5ED3E3E7CA5B4B5C83B8E01E5FCF',
            16
        ));
    }
}
<?php

/**
 * sect409r1
 *
 * PHP version 8.1+
 *
 * @author    Jim Wiggint  on <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Binary;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class sect409r1 extends Binary
{
    public function __construct()
    {
        $this->setModulo(409, 87, 0);
        $this->setCoefficients(
            '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001',
            '0021A5C2C8EE9FEB5C4B9A753B7B476B7FD6422EF1F3DD674761FA99D6AC27C8A9A197B272822F6CD57A55AA4F50AE317B13545F'
        );
        $this->setBasePoint(
            '015D4860D088DDB3496B0C6064756260441CDE4AF1771D4DB01FFE5B34E59703DC255A868A1180515603AEAB60794E54BB7996A7',
            '0061B1CFAB6BE5F32BBFA78324ED106A7636B9C5A7BD198D0158AA4F5488D08F38514F1FDF4B4F40D2181B3681C364BA0273C706'
        );
        $this->setOrder(new BigInteger(
            '010000000000000000000000000000000000000000000000000001E2' .
            'AAD6A612F33307BE5FA47C3C9E052F838164CD37D9A21173',
            16
        ));
    }
}
<?php

/**
 * nistp521
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

/** @psalm-api */
final class nistp521 extends secp521r1
{
}
<?php

/**
 * brainpoolP160r1
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Prime;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class brainpoolP160r1 extends Prime
{
    public function __construct()
    {
        $this->setModulo(new BigInteger('E95E4A5F737059DC60DFC7AD95B3D8139515620F', 16));
        $this->setCoefficients(
            new BigInteger('340E7BE2A280EB74E2BE61BADA745D97E8F7C300', 16),
            new BigInteger('1E589A8595423412134FAA2DBDEC95C8D8675E58', 16)
        );
        $this->setBasePoint(
            new BigInteger('BED5AF16EA3F6A4F62938C4631EB5AF7BDBCDBC3', 16),
            new BigInteger('1667CB477A1A8EC338F94741669C976316DA6321', 16)
        );
        $this->setOrder(new BigInteger('E95E4A5F737059DC60DF5991D45029409E60FC09', 16));
    }
}
<?php

/**
 * secp521r1
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Prime;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class secp521r1 extends Prime
{
    public function __construct()
    {
        $this->setModulo(new BigInteger('01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' .
                                        'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' .
                                        'FFFF', 16));
        $this->setCoefficients(
            new BigInteger('01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' .
                           'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' .
                           'FFFC', 16),
            new BigInteger('0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF1' .
                           '09E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B50' .
                           '3F00', 16)
        );
        $this->setBasePoint(
            new BigInteger('00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D' .
                           '3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5' .
                           'BD66', 16),
            new BigInteger('011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E' .
                           '662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD1' .
                           '6650', 16)
        );
        $this->setOrder(new BigInteger('01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' .
                                       'FFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E9138' .
                                       '6409', 16));
    }
}
<?php

/**
 * brainpoolP384t1
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Prime;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class brainpoolP384t1 extends Prime
{
    public function __construct()
    {
        $this->setModulo(new BigInteger(
            '8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901D1A7' .
            '1874700133107EC53',
            16
        ));
        $this->setCoefficients(
            new BigInteger(
                '8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901' .
                'D1A71874700133107EC50',
                16
            ), // eg. -3
            new BigInteger(
                '7F519EADA7BDA81BD826DBA647910F8C4B9346ED8CCDC64E4B1ABD11756DCE1D2074AA263B8' .
                '8805CED70355A33B471EE',
                16
            )
        );
        $this->setBasePoint(
            new BigInteger(
                '18DE98B02DB9A306F2AFCD7235F72A819B80AB12EBD653172476FECD462AABFFC4FF191B946' .
                'A5F54D8D0AA2F418808CC',
                16
            ),
            new BigInteger(
                '25AB056962D30651A114AFD2755AD336747F93475B7A1FCA3B88F2B6A208CCFE469408584DC' .
                '2B2912675BF5B9E582928',
                16
            )
        );
        $this->setOrder(new BigInteger(
            '8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B31F166E6CAC0425A7CF3AB6AF6B7FC31' .
            '03B883202E9046565',
            16
        ));
    }
}
<?php

/**
 * brainpoolP256r1
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Prime;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class brainpoolP256r1 extends Prime
{
    public function __construct()
    {
        $this->setModulo(new BigInteger('A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377', 16));
        $this->setCoefficients(
            new BigInteger('7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9', 16),
            new BigInteger('26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6', 16)
        );
        $this->setBasePoint(
            new BigInteger('8BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262', 16),
            new BigInteger('547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997', 16)
        );
        $this->setOrder(new BigInteger('A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7', 16));
    }
}
<?php

/**
 * sect193r1
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Binary;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class sect193r1 extends Binary
{
    public function __construct()
    {
        $this->setModulo(193, 15, 0);
        $this->setCoefficients(
            '0017858FEB7A98975169E171F77B4087DE098AC8A911DF7B01',
            '00FDFB49BFE6C3A89FACADAA7A1E5BBC7CC1C2E5D831478814'
        );
        $this->setBasePoint(
            '01F481BC5F0FF84A74AD6CDF6FDEF4BF6179625372D8C0C5E1',
            '0025E399F2903712CCF3EA9E3A1AD17FB0B3201B6AF7CE1B05'
        );
        $this->setOrder(new BigInteger('01000000000000000000000000C7F34A778F443ACC920EBA49', 16));
    }
}
<?php

/**
 * sect163r1
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Binary;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class sect163r1 extends Binary
{
    public function __construct()
    {
        $this->setModulo(163, 7, 6, 3, 0);
        $this->setCoefficients(
            '07B6882CAAEFA84F9554FF8428BD88E246D2782AE2',
            '0713612DCDDCB40AAB946BDA29CA91F73AF958AFD9'
        );
        $this->setBasePoint(
            '0369979697AB43897789566789567F787A7876A654',
            '00435EDB42EFAFB2989D51FEFCE3C80988F41FF883'
        );
        $this->setOrder(new BigInteger('03FFFFFFFFFFFFFFFFFFFF48AAB689C29CA710279B', 16));
    }
}
<?php

/**
 * sect131r1
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Binary;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class sect131r1 extends Binary
{
    public function __construct()
    {
        $this->setModulo(131, 8, 3, 2, 0);
        $this->setCoefficients(
            '07A11B09A76B562144418FF3FF8C2570B8',
            '0217C05610884B63B9C6C7291678F9D341'
        );
        $this->setBasePoint(
            '0081BAF91FDF9833C40F9C181343638399',
            '078C6E7EA38C001F73C8134B1B4EF9E150'
        );
        $this->setOrder(new BigInteger('0400000000000000023123953A9464B54D', 16));
    }
}
<?php

/**
 * sect233k1
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Binary;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class sect233k1 extends Binary
{
    public function __construct()
    {
        $this->setModulo(233, 74, 0);
        $this->setCoefficients(
            '000000000000000000000000000000000000000000000000000000000000',
            '000000000000000000000000000000000000000000000000000000000001'
        );
        $this->setBasePoint(
            '017232BA853A7E731AF129F22FF4149563A419C26BF50A4C9D6EEFAD6126',
            '01DB537DECE819B7F70F555A67C427A8CD9BF18AEB9B56E0C11056FAE6A3'
        );
        $this->setOrder(new BigInteger('8000000000000000000000000000069D5BB915BCD46EFB1AD5F173ABDF', 16));
    }
}
<?php

/**
 * sect163r2
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Binary;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class sect163r2 extends Binary
{
    public function __construct()
    {
        $this->setModulo(163, 7, 6, 3, 0);
        $this->setCoefficients(
            '000000000000000000000000000000000000000001',
            '020A601907B8C953CA1481EB10512F78744A3205FD'
        );
        $this->setBasePoint(
            '03F0EBA16286A2D57EA0991168D4994637E8343E36',
            '00D51FBC6C71A0094FA2CDD545B11C5C0C797324F1'
        );
        $this->setOrder(new BigInteger('040000000000000000000292FE77E70C12A4234C33', 16));
    }
}
<?php

/**
 * brainpoolP224r1
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Prime;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class brainpoolP224r1 extends Prime
{
    public function __construct()
    {
        $this->setModulo(new BigInteger('D7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FF', 16));
        $this->setCoefficients(
            new BigInteger('68A5E62CA9CE6C1C299803A6C1530B514E182AD8B0042A59CAD29F43', 16),
            new BigInteger('2580F63CCFE44138870713B1A92369E33E2135D266DBB372386C400B', 16)
        );
        $this->setBasePoint(
            new BigInteger('0D9029AD2C7E5CF4340823B2A87DC68C9E4CE3174C1E6EFDEE12C07D', 16),
            new BigInteger('58AA56F772C0726F24C6B89E4ECDAC24354B9E99CAA3F6D3761402CD', 16)
        );
        $this->setOrder(new BigInteger('D7C134AA264366862A18302575D0FB98D116BC4B6DDEBCA3A5A7939F', 16));
    }
}
<?php

/**
 * secp128r1
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Prime;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class secp128r1 extends Prime
{
    public function __construct()
    {
        $this->setModulo(new BigInteger('FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF', 16));
        $this->setCoefficients(
            new BigInteger('FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFC', 16),
            new BigInteger('E87579C11079F43DD824993C2CEE5ED3', 16)
        );
        $this->setBasePoint(
            new BigInteger('161FF7528B899B2D0C28607CA52C5B86', 16),
            new BigInteger('CF5AC8395BAFEB13C02DA292DDED7A83', 16)
        );
        $this->setOrder(new BigInteger('FFFFFFFE0000000075A30D1B9038A115', 16));
    }
}
<?php

/**
 * secp192k1
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\KoblitzPrime;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class secp192k1 extends KoblitzPrime
{
    public function __construct()
    {
        $this->setModulo(new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37', 16));
        $this->setCoefficients(
            new BigInteger('000000000000000000000000000000000000000000000000', 16),
            new BigInteger('000000000000000000000000000000000000000000000003', 16)
        );
        $this->setBasePoint(
            new BigInteger('DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D', 16),
            new BigInteger('9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D', 16)
        );
        $this->setOrder(new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D', 16));

        $this->basis = [];
        $this->basis[] = [
            'a' => new BigInteger('00B3FB3400DEC5C4ADCEB8655C', -16),
            'b' => new BigInteger('8EE96418CCF4CFC7124FDA0F', -16),
        ];
        $this->basis[] = [
            'a' => new BigInteger('01D90D03E8F096B9948B20F0A9', -16),
            'b' => new BigInteger('42E49819ABBA9474E1083F6B', -16),
        ];
        $this->beta = $this->factory->newInteger(new BigInteger('447A96E6C647963E2F7809FEAAB46947F34B0AA3CA0BBA74', -16));
    }
}
<?php

/**
 * brainpoolP192t1
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Prime;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class brainpoolP192t1 extends Prime
{
    public function __construct()
    {
        $this->setModulo(new BigInteger('C302F41D932A36CDA7A3463093D18DB78FCE476DE1A86297', 16));
        $this->setCoefficients(
            new BigInteger('C302F41D932A36CDA7A3463093D18DB78FCE476DE1A86294', 16), // eg. -3
            new BigInteger('13D56FFAEC78681E68F9DEB43B35BEC2FB68542E27897B79', 16)
        );
        $this->setBasePoint(
            new BigInteger('3AE9E58C82F63C30282E1FE7BBF43FA72C446AF6F4618129', 16),
            new BigInteger('097E2C5667C2223A902AB5CA449D0084B7E5B3DE7CCC01C9', 16)
        );
        $this->setOrder(new BigInteger('C302F41D932A36CDA7A3462F9E9E916B5BE8F1029AC4ACC1', 16));
    }
}
<?php

/**
 * secp224k1
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\KoblitzPrime;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class secp224k1 extends KoblitzPrime
{
    public function __construct()
    {
        $this->setModulo(new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFE56D', 16));
        $this->setCoefficients(
            new BigInteger('00000000000000000000000000000000000000000000000000000000', 16),
            new BigInteger('00000000000000000000000000000000000000000000000000000005', 16)
        );
        $this->setBasePoint(
            new BigInteger('A1455B334DF099DF30FC28A169A467E9E47075A90F7E650EB6B7A45C', 16),
            new BigInteger('7E089FED7FBA344282CAFBD6F7E319F7C0B0BD59E2CA4BDB556D61A5', 16)
        );
        $this->setOrder(new BigInteger('010000000000000000000000000001DCE8D2EC6184CAF0A971769FB1F7', 16));

        $this->basis = [];
        $this->basis[] = [
            'a' => new BigInteger('00B8ADF1378A6EB73409FA6C9C637D', -16),
            'b' => new BigInteger('94730F82B358A3776A826298FA6F', -16),
        ];
        $this->basis[] = [
            'a' => new BigInteger('01DCE8D2EC6184CAF0A972769FCC8B', -16),
            'b' => new BigInteger('4D2100BA3DC75AAB747CCF355DEC', -16),
        ];
        $this->beta = $this->factory->newInteger(new BigInteger('01F178FFA4B17C89E6F73AECE2AAD57AF4C0A748B63C830947B27E04', -16));
    }
}
<?php

/**
 * Curve448
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2019-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

use phpseclib4\Crypt\EC\BaseCurves\Montgomery;
use phpseclib4\Exception\UnexpectedValueException;
use phpseclib4\Math\BigInteger;

/** @psalm-api */
class Curve448 extends Montgomery
{
    public const SIZE = 56;

    public function __construct()
    {
        // 2^448 - 2^224 - 1
        $this->setModulo(new BigInteger(
            'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE' .
            'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF',
            16
        ));
        $this->a24 = $this->factory->newInteger(new BigInteger('39081'));
        $this->p = [$this->factory->newInteger(new BigInteger(5))];
        // 2^446 - 0x8335dc163bb124b65129c96fde933d8d723a70aadc873d6d54a7bb0d
        $this->setOrder(new BigInteger(
            '3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' .
            '7CCA23E9C44EDB49AED63690216CC2728DC58F552378C292AB5844F3',
            16
        ));

        /*
        $this->setCoefficients(
            new BigInteger('156326'), // a
        );
        $this->setBasePoint(
            new BigInteger(5),
            new BigInteger(
                '355293926785568175264127502063783334808976399387714271831880898' .
                '435169088786967410002932673765864550910142774147268105838985595290' .
                '606362')
        );
        */
    }

    /**
     * Multiply a point on the curve by a scalar
     *
     * Modifies the scalar as described at https://tools.ietf.org/html/rfc7748#page-8
     */
    public function multiplyPoint(array $p, BigInteger $d): array
    {
        $d = $d->toBytes();
        $d = str_pad($d, 56, "\0", STR_PAD_LEFT);

        //$r = strrev(sodium_crypto_scalarmult($d, strrev($p[0]->toBytes())));
        //return [$this->factory->newInteger(new BigInteger($r, 256))];

        $d[0] = $d[0] & "\xFC";
        $d = strrev($d);
        $d |= "\x80";
        $d = new BigInteger($d, 256);

        return parent::multiplyPoint($p, $d);
    }

    /**
     * Creates a random scalar multiplier
     */
    public function createRandomMultiplier(): BigInteger
    {
        return BigInteger::random(446);
    }

    /**
     * Performs range check
     */
    public function rangeCheck(BigInteger $x): void
    {
        if ($x->getLength() > 448 || $x->isNegative()) {
            throw new UnexpectedValueException('x must be a positive integer less than 446 bytes in length');
        }
    }
}
<?php

/**
 * nistk409
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\Curves;

/** @psalm-api */
final class nistk409 extends sect409k1
{
}
<?php

/**
 * Curve methods common to all curves
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\BaseCurves;

use phpseclib4\Exception\{InvalidStateException, UnexpectedValueException};
use phpseclib4\Math\BigInteger;
use phpseclib4\Math\Common\FiniteField;
use phpseclib4\Math\Common\FiniteField\Integer;

/**
 * Base
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class Base
{
    /**
     * The Order
     */
    protected BigInteger $order;

    /**
     * Returns a random integer
     *
     * @psalm-suppress UndefinedThisPropertyFetch
     */
    public function randomInteger(): Integer
    {
        return $this->factory->randomInteger();
    }

    /**
     * Converts a BigInteger to a \phpseclib4\Math\FiniteField\Integer integer
     *
     * @psalm-suppress UndefinedThisPropertyFetch
     */
    public function convertInteger(BigInteger $x): Integer
    {
        return $this->factory->newInteger($x);
    }

    /**
     * Returns the length, in bytes, of the modulo
     *
     * @psalm-suppress UndefinedThisPropertyFetch
     */
    public function getLengthInBytes(): int
    {
        return $this->factory->getLengthInBytes();
    }

    /**
     * Returns the length, in bits, of the modulo
     *
     * @psalm-suppress PossiblyUnusedMethod
     * @psalm-suppress UndefinedThisPropertyFetch
     */
    public function getLength(): int
    {
        return $this->factory->getLength();
    }

    /**
     * Multiply a point on the curve by a scalar
     *
     * Uses the montgomery ladder technique as described here:
     *
     * https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication#Montgomery_ladder
     * https://github.com/phpecc/phpecc/issues/16#issuecomment-59176772
     */
    public function multiplyPoint(array $p, BigInteger $d): array
    {
        $alreadyInternal = isset($p[2]);
        $r = $alreadyInternal ?
            [[], $p] :
            [[], $this->convertToInternal($p)];

        $d = $d->toBits();
        for ($i = 0; $i < strlen($d); $i++) {
            $d_i = (int) $d[$i];
            $r[1 - $d_i] = $this->addPoint($r[0], $r[1]);
            $r[$d_i] = $this->doublePoint($r[$d_i]);
        }

        return $alreadyInternal ? $r[0] : $this->convertToAffine($r[0]);
    }

    /**
     * Creates a random scalar multiplier
     */
    public function createRandomMultiplier(): BigInteger
    {
        static $one;
        if (!isset($one)) {
            $one = new BigInteger(1);
        }

        return BigInteger::randomRange($one, $this->order->subtract($one));
    }

    /**
     * Performs range check
     */
    public function rangeCheck(BigInteger $x): void
    {
        static $zero;
        if (!isset($zero)) {
            $zero = new BigInteger();
        }

        if (!isset($this->order)) {
            throw new InvalidStateException('setOrder needs to be called before this method');
        }
        if ($x->compare($this->order) > 0 || $x->compare($zero) <= 0) {
            throw new UnexpectedValueException('x must be between 1 and the order of the curve');
        }
    }

    /**
     * Sets the Order
     */
    public function setOrder(BigInteger $order): void
    {
        $this->order = $order;
    }

    /**
     * Returns the Order
     */
    public function getOrder(): BigInteger
    {
        return $this->order;
    }

    /**
     * Use a custom defined modular reduction function
     *
     * @psalm-suppress PossiblyUnusedMethod
     * @psalm-suppress UndefinedThisPropertyFetch
     */
    public function setReduction(\Closure $func): void
    {
        $this->factory->setReduction($func);
    }

    /**
     * Returns the affine point
     */
    public function convertToAffine(array $p): array
    {
        return $p;
    }

    /**
     * Converts an affine point to a jacobian coordinate
     */
    public function convertToInternal(array $p): array
    {
        return $p;
    }

    /**
     * Negates a point
     */
    public function negatePoint(array $p): array
    {
        $temp = [
            $p[0],
            $p[1]->negate(),
        ];
        if (isset($p[2])) {
            $temp[] = $p[2];
        }
        return $temp;
    }

    /**
     * Multiply and Add Points
     */
    public function multiplyAddPoints(array $points, array $scalars): array
    {
        $p1 = $this->convertToInternal($points[0]);
        $p2 = $this->convertToInternal($points[1]);
        $p1 = $this->multiplyPoint($p1, $scalars[0]);
        $p2 = $this->multiplyPoint($p2, $scalars[1]);
        $r = $this->addPoint($p1, $p2);
        return $this->convertToAffine($r);
    }
}
<?php

/**
 * Generalized Koblitz Curves over y^2 = x^3 + b.
 *
 * According to http://www.secg.org/SEC2-Ver-1.0.pdf Koblitz curves are over the GF(2**m)
 * finite field. Both the $a$ and $b$ coefficients are either 0 or 1. However, SEC2
 * generalizes the definition to include curves over GF(P) "which possess an efficiently
 * computable endomorphism".
 *
 * For these generalized Koblitz curves $b$ doesn't have to be 0 or 1. Whether or not $a$
 * has any restrictions on it is unclear, however, for all the GF(P) Koblitz curves defined
 * in SEC2 v1.0 $a$ is $0$ so all of the methods defined herein will assume that it is.
 *
 * I suppose we could rename the $b$ coefficient to $a$, however, the documentation refers
 * to $b$ so we'll just keep it.
 *
 * If a later version of SEC2 comes out wherein some $a$ values are non-zero we can create a
 * new method for those. eg. KoblitzA1Prime.php or something.
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\BaseCurves;

use phpseclib4\Math\{BigInteger, PrimeField};
use phpseclib4\Math\PrimeField\Integer as PrimeInteger;

/**
 * Curves over y^2 = x^3 + b
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class KoblitzPrime extends Prime
{
    /**
     * Basis
     *
     * @var list<array{a: BigInteger, b: BigInteger}>
     */
    public array $basis;

    /**
     * Beta
     */
    public PrimeInteger $beta;

    // don't overwrite setCoefficients() with one that only accepts one parameter so that
    // one might be able to switch between KoblitzPrime and Prime more easily (for benchmarking
    // purposes).

    /**
     * Multiply and Add Points
     *
     * Uses a efficiently computable endomorphism to achieve a slight speedup
     *
     * Adapted from:
     * https://github.com/indutny/elliptic/blob/725bd91/lib/elliptic/curve/short.js#L219
     *
     * @return PrimeInteger[]
     */
    public function multiplyAddPoints(array $points, array $scalars): array
    {
        static $one, $two;
        if (!isset($two)) {
            $two = new BigInteger(2);
            $one = new BigInteger(1);
        }

        if (!isset($this->beta)) {
            // get roots
            $inv = $this->one->divide($this->two)->negate();
            $s = $this->three->negate()->squareRoot()->multiply($inv);
            $betas = [
                $inv->add($s),
                $inv->subtract($s),
            ];
            $this->beta = $betas[0]->compare($betas[1]) < 0 ? $betas[0] : $betas[1];
        }

        if (!isset($this->basis)) {
            $factory = new PrimeField($this->order);
            $tempOne = $factory->newInteger($one);
            $tempTwo = $factory->newInteger($two);
            $tempThree = $factory->newInteger(new BigInteger(3));

            $inv = $tempOne->divide($tempTwo)->negate();
            $s = $tempThree->negate()->squareRoot()->multiply($inv);

            $lambdas = [
                $inv->add($s),
                $inv->subtract($s),
            ];

            $lhs = $this->multiplyPoint($this->p, $lambdas[0]->toBigInteger())[0];
            $rhs = $this->p[0]->multiply($this->beta);
            $lambda = $lhs->equals($rhs) ? $lambdas[0] : $lambdas[1];

            $this->basis = static::extendedGCD($lambda->toBigInteger(), $this->order);
        }

        $npoints = $nscalars = [];
        for ($i = 0; $i < count($points); $i++) {
            $p = $points[$i];
            $k = $scalars[$i]->toBigInteger();

            // begin split
            [$v1, $v2] = $this->basis;

            $c1 = $v2['b']->multiply($k);
            [$c1, $r] = $c1->divide($this->order);
            if ($this->order->compare($r->multiply($two)) <= 0) {
                $c1 = $c1->add($one);
            }

            $c2 = $v1['b']->negate()->multiply($k);
            [$c2, $r] = $c2->divide($this->order);
            if ($this->order->compare($r->multiply($two)) <= 0) {
                $c2 = $c2->add($one);
            }

            $p1 = $c1->multiply($v1['a']);
            $p2 = $c2->multiply($v2['a']);
            $q1 = $c1->multiply($v1['b']);
            $q2 = $c2->multiply($v2['b']);

            $k1 = $k->subtract($p1)->subtract($p2);
            $k2 = $q1->add($q2)->negate();
            // end split

            $beta = [
                $p[0]->multiply($this->beta),
                $p[1],
                clone $this->one,
            ];

            if (isset($p['naf'])) {
                $beta['naf'] = array_map(fn (array $p): array => [
                    $p[0]->multiply($this->beta),
                    $p[1],
                    clone $this->one
                ], $p['naf']);
                $beta['nafwidth'] = $p['nafwidth'];
            }

            if ($k1->isNegative()) {
                $k1 = $k1->negate();
                $p = $this->negatePoint($p);
            }

            if ($k2->isNegative()) {
                $k2 = $k2->negate();
                $beta = $this->negatePoint($beta);
            }

            $pos = 2 * $i;
            $npoints[$pos] = $p;
            $nscalars[$pos] = $this->factory->newInteger($k1);

            $pos++;
            $npoints[$pos] = $beta;
            $nscalars[$pos] = $this->factory->newInteger($k2);
        }

        return parent::multiplyAddPoints($npoints, $nscalars);
    }

    /**
     * Returns the numerator and denominator of the slope
     *
     * @return PrimeInteger[]
     */
    protected function doublePointHelper(array $p): array
    {
        $numerator = $this->three->multiply($p[0])->multiply($p[0]);
        $denominator = $this->two->multiply($p[1]);
        return [$numerator, $denominator];
    }

    /**
     * Doubles a jacobian coordinate on the curve
     *
     * See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l
     *
     * @return PrimeInteger[]
     */
    protected function jacobianDoublePoint(array $p): array
    {
        [$x1, $y1, $z1] = $p;
        $a = $x1->multiply($x1);
        $b = $y1->multiply($y1);
        $c = $b->multiply($b);
        $d = $x1->add($b);
        $d = $d->multiply($d)->subtract($a)->subtract($c)->multiply($this->two);
        $e = $this->three->multiply($a);
        $f = $e->multiply($e);
        $x3 = $f->subtract($this->two->multiply($d));
        $y3 = $e->multiply($d->subtract($x3))->subtract(
            $this->eight->multiply($c)
        );
        $z3 = $this->two->multiply($y1)->multiply($z1);
        return [$x3, $y3, $z3];
    }

    /**
     * Doubles a "fresh" jacobian coordinate on the curve
     *
     * See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-mdbl-2007-bl
     *
     * @return PrimeInteger[]
     */
    protected function jacobianDoublePointMixed(array $p): array
    {
        [$x1, $y1] = $p;
        $xx = $x1->multiply($x1);
        $yy = $y1->multiply($y1);
        $yyyy = $yy->multiply($yy);
        $s = $x1->add($yy);
        $s = $s->multiply($s)->subtract($xx)->subtract($yyyy)->multiply($this->two);
        $m = $this->three->multiply($xx);
        $t = $m->multiply($m)->subtract($this->two->multiply($s));
        $x3 = $t;
        $y3 = $s->subtract($t);
        $y3 = $m->multiply($y3)->subtract($this->eight->multiply($yyyy));
        $z3 = $this->two->multiply($y1);
        return [$x3, $y3, $z3];
    }

    /**
     * Tests whether or not the x / y values satisfy the equation
     */
    public function verifyPoint(array $p): bool
    {
        [$x, $y] = $p;
        $lhs = $y->multiply($y);
        $temp = $x->multiply($x)->multiply($x);
        $rhs = $temp->add($this->b);

        return $lhs->equals($rhs);
    }

    /**
     * Calculates the parameters needed from the Euclidean algorithm as discussed at
     * http://diamond.boisestate.edu/~liljanab/MATH308/GuideToECC.pdf#page=148
     *
     * @return list{
     *     array{a: BigInteger, b: BigInteger},
     *     array{a: BigInteger, b: BigInteger}
     * }
     */
    protected static function extendedGCD(BigInteger $u, BigInteger $v): array
    {
        $one = new BigInteger(1);
        $zero = new BigInteger();

        $a = clone $one;
        $b = clone $zero;
        $c = clone $zero;
        $d = clone $one;

        $stop = $v->bitwise_rightShift($v->getLength() >> 1);

        $a1 = clone $zero;
        $b1 = clone $zero;
        $a2 = clone $zero;
        $b2 = clone $zero;

        $postGreatestIndex = 0;

        while (!$v->equals($zero)) {
            [$q] = $u->divide($v);

            $temp = $u;
            $u = $v;
            $v = $temp->subtract($v->multiply($q));

            $temp = $a;
            $a = $c;
            $c = $temp->subtract($a->multiply($q));

            $temp = $b;
            $b = $d;
            $d = $temp->subtract($b->multiply($q));

            if ($v->compare($stop) > 0) {
                $a0 = $v;
                $b0 = $c;
            } else {
                $postGreatestIndex++;
            }

            if ($postGreatestIndex == 1) {
                $a1 = $v;
                $b1 = $c->negate();
            }

            if ($postGreatestIndex == 2) {
                $rhs = $a0->multiply($a0)->add($b0->multiply($b0));
                $lhs = $v->multiply($v)->add($b->multiply($b));
                if ($lhs->compare($rhs) <= 0) {
                    $a2 = $a0;
                    $b2 = $b0->negate();
                } else {
                    $a2 = $v;
                    $b2 = $c->negate();
                }

                break;
            }
        }

        return [
            ['a' => $a1, 'b' => $b1],
            ['a' => $a2, 'b' => $b2],
        ];
    }
}
<?php

/**
 * Curves over y^2 = x^3 + a*x + x
 *
 * Technically, a Montgomery curve has a coefficient for y^2 but for Curve25519 and Curve448 that
 * coefficient is 1.
 *
 * Curve25519 and Curve448 do not make use of the y coordinate, which makes it unsuitable for use
 * with ECDSA / EdDSA. A few other differences between Curve25519 and Ed25519 are discussed at
 * https://crypto.stackexchange.com/a/43058/4520
 *
 * More info:
 *
 * https://en.wikipedia.org/wiki/Montgomery_curve
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2019-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\BaseCurves;

use phpseclib4\Crypt\EC\Curves\Curve25519;
use phpseclib4\Exception\{InvalidStateException, UnsupportedValueException};
use phpseclib4\Math\{BigInteger, PrimeField};
use phpseclib4\Math\PrimeField\Integer as PrimeInteger;

/**
 * Curves over y^2 = x^3 + a*x + x
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class Montgomery extends Base
{
    public const SIZE = -1;

    /**
     * Prime Field Integer factory
     */
    protected PrimeField $factory;

    /**
     * Coefficient for x
     */
    protected PrimeInteger $a;

    /**
     * Constant used for point doubling
     */
    protected PrimeInteger $a24;

    /**
     * The Number Zero
     */
    protected PrimeInteger $zero;

    /**
     * The Number One
     */
    protected PrimeInteger $one;

    /**
     * Base Point
     */
    protected array $p;

    /**
     * The modulo
     *
     * @psalm-suppress PossiblyUnusedProperty
     */
    protected BigInteger $modulo;

    /**
     * The Order
     */
    protected BigInteger $order;

    /**
     * Sets the modulo
     */
    public function setModulo(BigInteger $modulo): void
    {
        $this->modulo = $modulo;
        $this->factory = new PrimeField($modulo);
        $this->zero = $this->factory->newInteger(new BigInteger());
        $this->one = $this->factory->newInteger(new BigInteger(1));
    }

    /**
     * Set coefficients a
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function setCoefficients(BigInteger $a): void
    {
        if (!isset($this->factory)) {
            throw new InvalidStateException('setModulo needs to be called before this method');
        }
        $this->a = $this->factory->newInteger($a);
        $two = $this->factory->newInteger(new BigInteger(2));
        $four = $this->factory->newInteger(new BigInteger(4));
        $this->a24 = $this->a->subtract($two)->divide($four);
    }

    /**
     * Set x and y coordinates for the base point
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function setBasePoint(BigInteger|PrimeInteger $x, BigInteger|PrimeInteger $y): void
    {
        if (!isset($this->factory)) {
            throw new InvalidStateException('setModulo needs to be called before this method');
        }
        $this->p = [
            $x instanceof BigInteger ? $this->factory->newInteger($x) : $x,
            $y instanceof BigInteger ? $this->factory->newInteger($y) : $y,
        ];
    }

    /**
     * Retrieve the base point as an array
     *
     * @return PrimeInteger[]
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function getBasePoint(): array
    {
        if (!isset($this->factory)) {
            throw new InvalidStateException('setModulo needs to be called before this method');
        }
        /*
        if (!isset($this->p)) {
            throw new InvalidStateException('setBasePoint needs to be called before this method');
        }
        */
        return $this->p;
    }

    /**
     * Doubles and adds a point on a curve
     *
     * See https://tools.ietf.org/html/draft-ietf-tls-curve25519-01#appendix-A.1.3
     *
     * @return PrimeInteger[][]
     */
    private function doubleAndAddPoint(array $p, array $q, PrimeInteger $x1): array
    {
        if (!isset($this->factory)) {
            throw new InvalidStateException('setModulo needs to be called before this method');
        }

        if (!count($p) || !count($q)) {
            return [];
        }

        if (!isset($p[1])) {
            throw new UnsupportedValueException('Affine coordinates need to be manually converted to XZ coordinates');
        }

        [$x2, $z2] = $p;
        [$x3, $z3] = $q;

        $a = $x2->add($z2);
        $aa = $a->multiply($a);
        $b = $x2->subtract($z2);
        $bb = $b->multiply($b);
        $e = $aa->subtract($bb);
        $c = $x3->add($z3);
        $d = $x3->subtract($z3);
        $da = $d->multiply($a);
        $cb = $c->multiply($b);
        $temp = $da->add($cb);
        $x5 = $temp->multiply($temp);
        $temp = $da->subtract($cb);
        $z5 = $x1->multiply($temp->multiply($temp));
        $x4 = $aa->multiply($bb);
        $temp = static::class == Curve25519::class ? $bb : $aa;
        $z4 = $e->multiply($temp->add($this->a24->multiply($e)));

        return [
            [$x4, $z4],
            [$x5, $z5],
        ];
    }

    /**
     * Multiply a point on the curve by a scalar
     *
     * Uses the montgomery ladder technique as described here:
     *
     * https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication#Montgomery_ladder
     * https://github.com/phpecc/phpecc/issues/16#issuecomment-59176772
     */
    public function multiplyPoint(array $p, BigInteger $d): array
    {
        $p1 = [$this->one, $this->zero];
        $alreadyInternal = isset($p[1]);
        $p2 = $this->convertToInternal($p);
        $x = $p[0];

        $r = $this->randomInteger();
        $p2 = [$p2[0]->multiply($r), $p2[1]->multiply($r)];

        $b = $d->toBits();
        $b = str_pad($b, 256, '0', STR_PAD_LEFT);
        for ($i = 0; $i < strlen($b); $i++) {
            $b_i = (int) $b[$i];
            if ($b_i) {
                [$p2, $p1] = $this->doubleAndAddPoint($p2, $p1, $x);
            } else {
                [$p1, $p2] = $this->doubleAndAddPoint($p1, $p2, $x);
            }
        }

        return $alreadyInternal ? $p1 : $this->convertToAffine($p1);
    }

    /**
     * Converts an affine point to an XZ coordinate
     *
     * From https://hyperelliptic.org/EFD/g1p/auto-montgom-xz.html
     *
     * XZ coordinates represent x y as X Z satsfying the following equations:
     *
     *   x=X/Z
     *
     * @return PrimeInteger[]
     */
    public function convertToInternal(array $p): array
    {
        if (empty($p)) {
            return [clone $this->zero, clone $this->one];
        }

        if (isset($p[1])) {
            return $p;
        }

        $p[1] = clone $this->one;

        return $p;
    }

    /**
     * Returns the affine point
     *
     * @return PrimeInteger[]
     */
    public function convertToAffine(array $p): array
    {
        if (!isset($p[1])) {
            return $p;
        }
        [$x, $z] = $p;
        if ($z->equals($this->zero)) {
            return [clone $this->zero];
        }
        return [$x->divide($z)];
    }
}
<?php

/**
 * Curves over y^2 + x*y = x^3 + a*x^2 + b
 *
 * These are curves used in SEC 2 over prime fields: http://www.secg.org/SEC2-Ver-1.0.pdf
 * The curve is a weierstrass curve with a[3] and a[2] set to 0.
 *
 * Uses Jacobian Coordinates for speed if able:
 *
 * https://en.wikipedia.org/wiki/Jacobian_curve
 * https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\BaseCurves;

use phpseclib4\Exception\{BadMethodCallException, InvalidStateException, UnsupportedValueException};
use phpseclib4\Math\BinaryField;
use phpseclib4\Math\BinaryField\Integer as BinaryInteger;

/**
 * Curves over y^2 + x*y = x^3 + a*x^2 + b
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class Binary extends Base
{
    /**
     * Binary Field Integer factory
     */
    protected BinaryField $factory;

    /**
     * Cofficient for x^1
     */
    protected BinaryInteger $a;

    /**
     * Cofficient for x^0
     */
    protected BinaryInteger $b;

    /**
     * Base Point
     *
     * @var BinaryInteger[]
     */
    protected array $p;

    /**
     * The number one over the specified finite field
     */
    protected BinaryInteger $one;

    /**
     * The modulo
     *
     * @var int[]
     */
    protected array $modulo;

    /**
     * Sets the modulo
     */
    public function setModulo(int ...$modulo): void
    {
        $this->modulo = $modulo;
        $this->factory = new BinaryField(...$modulo);

        $this->one = $this->factory->newInteger("\1");
    }

    /**
     * Set coefficients a and b
     */
    public function setCoefficients(string $a, string $b): void
    {
        if (!isset($this->factory)) {
            throw new InvalidStateException('setModulo needs to be called before this method');
        }
        $this->a = $this->factory->newInteger(pack('H*', $a));
        $this->b = $this->factory->newInteger(pack('H*', $b));
    }

    /**
     * Set x and y coordinates for the base point
     */
    public function setBasePoint(string|BinaryInteger $x, string|BinaryInteger $y): void
    {
        if (!isset($this->factory)) {
            throw new InvalidStateException('setModulo needs to be called before this method');
        }
        $this->p = [
            is_string($x) ? $this->factory->newInteger(pack('H*', $x)) : $x,
            is_string($y) ? $this->factory->newInteger(pack('H*', $y)) : $y,
        ];
    }

    /**
     * Retrieve the base point as an array
     *
     * @return BinaryInteger[]
     */
    public function getBasePoint(): array
    {
        if (!isset($this->factory)) {
            throw new InvalidStateException('setModulo needs to be called before this method');
        }
        /*
        if (!isset($this->p)) {
            throw new InvalidStateException('setBasePoint needs to be called before this method');
        }
        */
        return $this->p;
    }

    /**
     * Adds two points on the curve
     *
     * @return BinaryInteger[]
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function addPoint(array $p, array $q): array
    {
        if (!isset($this->factory)) {
            throw new InvalidStateException('setModulo needs to be called before this method');
        }

        if (!count($p) || !count($q)) {
            if (count($q)) {
                return $q;
            }
            if (count($p)) {
                return $p;
            }
            return [];
        }

        if (!isset($p[2]) || !isset($q[2])) {
            throw new UnsupportedValueException('Affine coordinates need to be manually converted to "Jacobi" coordinates or vice versa');
        }

        if ($p[0]->equals($q[0])) {
            return !$p[1]->equals($q[1]) ? [] : $this->doublePoint($p);
        }

        // formulas from http://hyperelliptic.org/EFD/g12o/auto-shortw-jacobian.html

        [$x1, $y1, $z1] = $p;
        [$x2, $y2, $z2] = $q;

        $o1 = $z1->multiply($z1);
        $b = $x2->multiply($o1);

        if ($z2->equals($this->one)) {
            $d = $y2->multiply($o1)->multiply($z1);
            $e = $x1->add($b);
            $f = $y1->add($d);
            $z3 = $e->multiply($z1);
            $h = $f->multiply($x2)->add($z3->multiply($y2));
            $i = $f->add($z3);
            $g = $z3->multiply($z3);
            $p1 = $this->a->multiply($g);
            $p2 = $f->multiply($i);
            $p3 = $e->multiply($e)->multiply($e);
            $x3 = $p1->add($p2)->add($p3);
            $y3 = $i->multiply($x3)->add($g->multiply($h));

            return [$x3, $y3, $z3];
        }

        $o2 = $z2->multiply($z2);
        $a = $x1->multiply($o2);
        $c = $y1->multiply($o2)->multiply($z2);
        $d = $y2->multiply($o1)->multiply($z1);
        $e = $a->add($b);
        $f = $c->add($d);
        $g = $e->multiply($z1);
        $h = $f->multiply($x2)->add($g->multiply($y2));
        $z3 = $g->multiply($z2);
        $i = $f->add($z3);
        $p1 = $this->a->multiply($z3->multiply($z3));
        $p2 = $f->multiply($i);
        $p3 = $e->multiply($e)->multiply($e);
        $x3 = $p1->add($p2)->add($p3);
        $y3 = $i->multiply($x3)->add($g->multiply($g)->multiply($h));

        return [$x3, $y3, $z3];
    }

    /**
     * Doubles a point on a curve
     *
     * @return BinaryInteger[]
     */
    public function doublePoint(array $p): array
    {
        if (!isset($this->factory)) {
            throw new InvalidStateException('setModulo needs to be called before this method');
        }

        if (!count($p)) {
            return [];
        }

        if (!isset($p[2])) {
            throw new UnsupportedValueException('Affine coordinates need to be manually converted to "Jacobi" coordinates or vice versa');
        }

        // formulas from http://hyperelliptic.org/EFD/g12o/auto-shortw-jacobian.html

        [$x1, $y1, $z1] = $p;

        $a = $x1->multiply($x1);
        $b = $a->multiply($a);

        if ($z1->equals($this->one)) {
            $x3 = $b->add($this->b);
            $z3 = clone $x1;
            $p1 = $a->add($y1)->add($z3)->multiply($this->b);
            $p2 = $a->add($y1)->multiply($b);
            $y3 = $p1->add($p2);

            return [$x3, $y3, $z3];
        }

        $c = $z1->multiply($z1);
        $d = $c->multiply($c);
        $x3 = $b->add($this->b->multiply($d->multiply($d)));
        $z3 = $x1->multiply($c);
        $p1 = $b->multiply($z3);
        $p2 = $a->add($y1->multiply($z1))->add($z3)->multiply($x3);
        $y3 = $p1->add($p2);

        return [$x3, $y3, $z3];
    }

    /**
     * Returns the X coordinate and the derived Y coordinate
     *
     * Not supported because it is covered by patents.
     * Quoting https://www.openssl.org/docs/man1.1.0/apps/ecparam.html ,
     *
     * "Due to patent issues the compressed option is disabled by default for binary curves
     *  and can be enabled by defining the preprocessor macro OPENSSL_EC_BIN_PT_COMP at
     *  compile time."
     *
     * @psalm-suppress PossiblyUnusedParam
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function derivePoint(string $m): array
    {
        throw new BadMethodCallException('Point compression on binary finite field elliptic curves is not supported');
    }

    /**
     * Tests whether or not the x / y values satisfy the equation
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function verifyPoint(array $p): bool
    {
        [$x, $y] = $p;
        $lhs = $y->multiply($y);
        $lhs = $lhs->add($x->multiply($y));
        $x2 = $x->multiply($x);
        $x3 = $x2->multiply($x);
        $rhs = $x3->add($this->a->multiply($x2))->add($this->b);

        return $lhs->equals($rhs);
    }

    /**
     * Returns the modulo
     */
    public function getModulo(): array
    {
        return $this->modulo;
    }

    /**
     * Returns the a coefficient
     */
    public function getA(): BinaryInteger
    {
        return $this->a;
    }

    /**
     * Returns the a coefficient
     */
    public function getB(): BinaryInteger
    {
        return $this->b;
    }

    /**
     * Returns the affine point
     *
     * A Jacobian Coordinate is of the form (x, y, z).
     * To convert a Jacobian Coordinate to an Affine Point
     * you do (x / z^2, y / z^3)
     *
     * @return BinaryInteger[]
     */
    public function convertToAffine(array $p): array
    {
        if (!isset($p[2])) {
            return $p;
        }
        [$x, $y, $z] = $p;
        $z = $this->one->divide($z);
        $z2 = $z->multiply($z);
        return [
            $x->multiply($z2),
            $y->multiply($z2)->multiply($z),
        ];
    }

    /**
     * Converts an affine point to a jacobian coordinate
     *
     * @return BinaryInteger[]
     */
    public function convertToInternal(array $p): array
    {
        if (isset($p[2])) {
            return $p;
        }

        $p[2] = clone $this->one;
        $p['fresh'] = true;
        return $p;
    }
}
<?php

/**
 * Curves over y^2 = x^3 + a*x + b
 *
 * These are curves used in SEC 2 over prime fields: http://www.secg.org/SEC2-Ver-1.0.pdf
 * The curve is a weierstrass curve with a[1], a[3] and a[2] set to 0.
 *
 * Uses Jacobian Coordinates for speed if able:
 *
 * https://en.wikipedia.org/wiki/Jacobian_curve
 * https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\BaseCurves;

use phpseclib4\Common\Functions\Strings;
use phpseclib4\Exception\{InvalidStateException, UnexpectedValueException, UnsupportedValueException};
use phpseclib4\Math\{BigInteger, PrimeField};
use phpseclib4\Math\PrimeField\Integer as PrimeInteger;

/**
 * Curves over y^2 = x^3 + a*x + b
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class Prime extends Base
{
    /**
     * Prime Field Integer factory
     */
    protected PrimeField $factory;

    /**
     * Coefficient for x^1
     */
    protected PrimeInteger $a;

    /**
     * Coefficient for x^0
     */
    protected PrimeInteger $b;

    /**
     * Base Point
     */
    protected array $p;

    /**
     * The number one over the specified finite field
     */
    protected PrimeInteger $one;

    /**
     * The number two over the specified finite field
     */
    protected PrimeInteger $two;

    /**
     * The number three over the specified finite field
     */
    protected PrimeInteger $three;

    /**
     * The number four over the specified finite field
     */
    protected PrimeInteger $four;

    /**
     * The number eight over the specified finite field
     */
    protected PrimeInteger $eight;

    /**
     * The modulo
     */
    protected BigInteger $modulo;

    /**
     * Sets the modulo
     */
    public function setModulo(BigInteger $modulo): void
    {
        $this->modulo = $modulo;
        $this->factory = new PrimeField($modulo);
        $this->two = $this->factory->newInteger(new BigInteger(2));
        $this->three = $this->factory->newInteger(new BigInteger(3));
        // used by jacobian coordinates
        $this->one = $this->factory->newInteger(new BigInteger(1));
        $this->four = $this->factory->newInteger(new BigInteger(4));
        $this->eight = $this->factory->newInteger(new BigInteger(8));
    }

    /**
     * Set coefficients a and b
     */
    public function setCoefficients(BigInteger $a, BigInteger $b): void
    {
        if (!isset($this->factory)) {
            throw new InvalidStateException('setModulo needs to be called before this method');
        }
        $this->a = $this->factory->newInteger($a);
        $this->b = $this->factory->newInteger($b);
    }

    /**
     * Set x and y coordinates for the base point
     */
    public function setBasePoint(BigInteger|PrimeInteger $x, BigInteger|PrimeInteger $y): void
    {
        if (!isset($this->factory)) {
            throw new InvalidStateException('setModulo needs to be called before this method');
        }
        $this->p = [
            $x instanceof BigInteger ? $this->factory->newInteger($x) : $x,
            $y instanceof BigInteger ? $this->factory->newInteger($y) : $y,
        ];
    }

    /**
     * Retrieve the base point as an array
     *
     * @return PrimeInteger[]
     */
    public function getBasePoint(): array
    {
        if (!isset($this->factory)) {
            throw new InvalidStateException('setModulo needs to be called before this method');
        }
        /*
        if (!isset($this->p)) {
            throw new InvalidStateException('setBasePoint needs to be called before this method');
        }
        */
        return $this->p;
    }

    /**
     * Adds two "fresh" jacobian form on the curve
     *
     * @return PrimeInteger[]
     */
    protected function jacobianAddPointMixedXY(array $p, array $q): array
    {
        [$u1, $s1] = $p;
        [$u2, $s2] = $q;
        if ($u1->equals($u2)) {
            return $s1->equals($s2) ? $this->doublePoint($p) : [];
        }
        $h = $u2->subtract($u1);
        $r = $s2->subtract($s1);
        $h2 = $h->multiply($h);
        $h3 = $h2->multiply($h);
        $v = $u1->multiply($h2);
        $x3 = $r->multiply($r)->subtract($h3)->subtract($v->multiply($this->two));
        $y3 = $r->multiply(
            $v->subtract($x3)
        )->subtract(
            $s1->multiply($h3)
        );
        return [$x3, $y3, $h];
    }

    /**
     * Adds one "fresh" jacobian form on the curve
     *
     * The second parameter should be the "fresh" one
     *
     * @return PrimeInteger[]
     */
    protected function jacobianAddPointMixedX(array $p, array $q): array
    {
        [$u1, $s1, $z1] = $p;
        [$x2, $y2] = $q;

        $z12 = $z1->multiply($z1);

        $u2 = $x2->multiply($z12);
        $s2 = $y2->multiply($z12->multiply($z1));
        if ($u1->equals($u2)) {
            if (!$s1->equals($s2)) {
                return [];
            } else {
                return $this->doublePoint($p);
            }
        }
        $h = $u2->subtract($u1);
        $r = $s2->subtract($s1);
        $h2 = $h->multiply($h);
        $h3 = $h2->multiply($h);
        $v = $u1->multiply($h2);
        $x3 = $r->multiply($r)->subtract($h3)->subtract($v->multiply($this->two));
        $y3 = $r->multiply(
            $v->subtract($x3)
        )->subtract(
            $s1->multiply($h3)
        );
        $z3 = $h->multiply($z1);
        return [$x3, $y3, $z3];
    }

    /**
     * Adds two jacobian coordinates on the curve
     *
     * @return PrimeInteger[]
     */
    protected function jacobianAddPoint(array $p, array $q): array
    {
        [$x1, $y1, $z1] = $p;
        [$x2, $y2, $z2] = $q;

        $z12 = $z1->multiply($z1);
        $z22 = $z2->multiply($z2);

        $u1 = $x1->multiply($z22);
        $u2 = $x2->multiply($z12);
        $s1 = $y1->multiply($z22->multiply($z2));
        $s2 = $y2->multiply($z12->multiply($z1));
        if ($u1->equals($u2)) {
            return $s1->equals($s2) ? $this->doublePoint($p) : [];
        }
        $h = $u2->subtract($u1);
        $r = $s2->subtract($s1);
        $h2 = $h->multiply($h);
        $h3 = $h2->multiply($h);
        $v = $u1->multiply($h2);
        $x3 = $r->multiply($r)->subtract($h3)->subtract($v->multiply($this->two));
        $y3 = $r->multiply(
            $v->subtract($x3)
        )->subtract(
            $s1->multiply($h3)
        );
        $z3 = $h->multiply($z1)->multiply($z2);
        return [$x3, $y3, $z3];
    }

    /**
     * Adds two points on the curve
     *
     * @return PrimeInteger[]
     */
    public function addPoint(array $p, array $q): array
    {
        if (!isset($this->factory)) {
            throw new InvalidStateException('setModulo needs to be called before this method');
        }

        if (!count($p) || !count($q)) {
            if (count($q)) {
                return $q;
            }
            if (count($p)) {
                return $p;
            }
            return [];
        }

        // use jacobian coordinates
        if (isset($p[2]) && isset($q[2])) {
            return match (true) {
                isset($p['fresh']) && isset($q['fresh']) => $this->jacobianAddPointMixedXY($p, $q),
                isset($p['fresh']) => $this->jacobianAddPointMixedX($q, $p),
                isset($q['fresh']) => $this->jacobianAddPointMixedX($p, $q),
                default => $this->jacobianAddPoint($p, $q)
            };
        }

        if (isset($p[2]) || isset($q[2])) {
            throw new UnsupportedValueException('Affine coordinates need to be manually converted to Jacobi coordinates or vice versa');
        }

        if ($p[0]->equals($q[0])) {
            if (!$p[1]->equals($q[1])) {
                return [];
            } else { // eg. doublePoint
                [$numerator, $denominator] = $this->doublePointHelper($p);
            }
        } else {
            $numerator = $q[1]->subtract($p[1]);
            $denominator = $q[0]->subtract($p[0]);
        }
        $slope = $numerator->divide($denominator);
        $x = $slope->multiply($slope)->subtract($p[0])->subtract($q[0]);
        $y = $slope->multiply($p[0]->subtract($x))->subtract($p[1]);

        return [$x, $y];
    }

    /**
     * Returns the numerator and denominator of the slope
     *
     * @return PrimeInteger[]
     */
    protected function doublePointHelper(array $p): array
    {
        $numerator = $this->three->multiply($p[0])->multiply($p[0])->add($this->a);
        $denominator = $this->two->multiply($p[1]);
        return [$numerator, $denominator];
    }

    /**
     * Doubles a jacobian coordinate on the curve
     *
     * @return PrimeInteger[]
     */
    protected function jacobianDoublePoint(array $p): array
    {
        [$x, $y, $z] = $p;
        $x2 = $x->multiply($x);
        $y2 = $y->multiply($y);
        $z2 = $z->multiply($z);
        $s = $this->four->multiply($x)->multiply($y2);
        $m1 = $this->three->multiply($x2);
        $m2 = $this->a->multiply($z2->multiply($z2));
        $m = $m1->add($m2);
        $x1 = $m->multiply($m)->subtract($this->two->multiply($s));
        $y1 = $m->multiply($s->subtract($x1))->subtract(
            $this->eight->multiply($y2->multiply($y2))
        );
        $z1 = $this->two->multiply($y)->multiply($z);
        return [$x1, $y1, $z1];
    }

    /**
     * Doubles a "fresh" jacobian coordinate on the curve
     *
     * @return PrimeInteger[]
     */
    protected function jacobianDoublePointMixed(array $p): array
    {
        [$x, $y] = $p;
        $x2 = $x->multiply($x);
        $y2 = $y->multiply($y);
        $s = $this->four->multiply($x)->multiply($y2);
        $m1 = $this->three->multiply($x2);
        $m = $m1->add($this->a);
        $x1 = $m->multiply($m)->subtract($this->two->multiply($s));
        $y1 = $m->multiply($s->subtract($x1))->subtract(
            $this->eight->multiply($y2->multiply($y2))
        );
        $z1 = $this->two->multiply($y);
        return [$x1, $y1, $z1];
    }

    /**
     * Doubles a point on a curve
     *
     * @return PrimeInteger[]
     */
    public function doublePoint(array $p): array
    {
        if (!isset($this->factory)) {
            throw new InvalidStateException('setModulo needs to be called before this method');
        }

        if (!count($p)) {
            return [];
        }

        // use jacobian coordinates
        if (isset($p[2])) {
            return isset($p['fresh']) ? $this->jacobianDoublePointMixed($p) : $this->jacobianDoublePoint($p);
        }

        [$numerator, $denominator] = $this->doublePointHelper($p);

        $slope = $numerator->divide($denominator);

        $x = $slope->multiply($slope)->subtract($p[0])->subtract($p[0]);
        $y = $slope->multiply($p[0]->subtract($x))->subtract($p[1]);

        return [$x, $y];
    }

    /**
     * Returns the X coordinate and the derived Y coordinate
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function derivePoint(string $m): array
    {
        $y = ord(Strings::shift($m));
        $x = new BigInteger($m, 256);
        $xp = $this->convertInteger($x);
        $ypn = match ($y) {
            2 => false,
            3 => true,
            default => throw new UnexpectedValueException("Coordinate not in recognized format (found $y; expected 2 or 3)")
        };
        $temp = $xp->multiply($this->a);
        $temp = $xp->multiply($xp)->multiply($xp)->add($temp);
        $temp = $temp->add($this->b);
        $b = $temp->squareRoot();
        if (!$b) {
            throw new UnexpectedValueException('Unable to derive Y coordinate');
        }
        $bn = $b->isOdd();
        $yp = $ypn == $bn ? $b : $b->negate();
        return [$xp, $yp];
    }

    /**
     * Tests whether or not the x / y values satisfy the equation
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function verifyPoint(array $p): bool
    {
        [$x, $y] = $p;
        $lhs = $y->multiply($y);
        $temp = $x->multiply($this->a);
        $temp = $x->multiply($x)->multiply($x)->add($temp);
        $rhs = $temp->add($this->b);

        return $lhs->equals($rhs);
    }

    /**
     * Returns the modulo
     */
    public function getModulo(): BigInteger
    {
        return $this->modulo;
    }

    /**
     * Returns the a coefficient
     */
    public function getA(): PrimeInteger
    {
        return $this->a;
    }

    /**
     * Returns the a coefficient
     */
    public function getB(): PrimeInteger
    {
        return $this->b;
    }

    /**
     * Multiply and Add Points
     *
     * Adapted from:
     * https://github.com/indutny/elliptic/blob/725bd91/lib/elliptic/curve/base.js#L125
     *
     * @return PrimeInteger[]
     */
    public function multiplyAddPoints(array $points, array $scalars): array
    {
        $length = count($points);

        foreach ($points as &$point) {
            $point = $this->convertToInternal($point);
        }

        $wnd = [$this->getNAFPoints($points[0], 7)];
        $wndWidth = [$points[0]['nafwidth'] ?? 7];
        for ($i = 1; $i < $length; $i++) {
            $wnd[] = $this->getNAFPoints($points[$i], 1);
            $wndWidth[] = $points[$i]['nafwidth'] ?? 1;
        }

        $naf = [];

        // comb all window NAFs

        $max = 0;
        for ($i = $length - 1; $i >= 1; $i -= 2) {
            $a = $i - 1;
            $b = $i;
            if ($wndWidth[$a] != 1 || $wndWidth[$b] != 1) {
                $naf[$a] = $scalars[$a]->getNAF($wndWidth[$a]);
                $naf[$b] = $scalars[$b]->getNAF($wndWidth[$b]);
                $max = max(count($naf[$a]), count($naf[$b]), $max);
                continue;
            }

            $comb = [
                $points[$a], // 1
                null,        // 3
                null,        // 5
                $points[$b],  // 7
            ];

            $comb[1] = $this->addPoint($points[$a], $points[$b]);
            $comb[2] = $this->addPoint($points[$a], $this->negatePoint($points[$b]));

            $index = [
                -3, /* -1 -1 */
                -1, /* -1  0 */
                -5, /* -1  1 */
                -7, /*  0 -1 */
                 0, /*  0 -1 */
                 7, /*  0  1 */
                 5, /*  1 -1 */
                 1, /*  1  0 */
                 3,  /*  1  1 */
            ];

            $jsf = self::getJSFPoints($scalars[$a], $scalars[$b]);

            $max = max(count($jsf[0]), $max);
            if ($max > 0) {
                $naf[$a] = array_fill(0, $max, 0);
                $naf[$b] = array_fill(0, $max, 0);
            } else {
                $naf[$a] = [];
                $naf[$b] = [];
            }

            for ($j = 0; $j < $max; $j++) {
                $ja = $jsf[0][$j] ?? 0;
                $jb = $jsf[1][$j] ?? 0;

                $naf[$a][$j] = $index[3 * ($ja + 1) + $jb + 1];
                $naf[$b][$j] = 0;
                $wnd[$a] = $comb;
            }
        }

        $acc = [];
        $temp = [0, 0, 0, 0];
        for ($i = $max; $i >= 0; $i--) {
            $k = 0;
            while ($i >= 0) {
                $zero = true;
                for ($j = 0; $j < $length; $j++) {
                    $temp[$j] = $naf[$j][$i] ?? 0;
                    if ($temp[$j] != 0) {
                        $zero = false;
                    }
                }
                if (!$zero) {
                    break;
                }
                $k++;
                $i--;
            }

            if ($i >= 0) {
                $k++;
            }
            while ($k--) {
                $acc = $this->doublePoint($acc);
            }

            if ($i < 0) {
                break;
            }

            for ($j = 0; $j < $length; $j++) {
                $z = $temp[$j];
                if ($z == 0) {
                    continue;
                }
                $p = $z > 0 ?
                    $wnd[$j][($z - 1) >> 1] :
                    $this->negatePoint($wnd[$j][(-$z - 1) >> 1]);
                $acc = $this->addPoint($acc, $p);
            }
        }

        return $this->convertToAffine($acc);
    }

    /**
     * Precomputes NAF points
     *
     * Adapted from:
     * https://github.com/indutny/elliptic/blob/725bd91/lib/elliptic/curve/base.js#L351
     *
     * @return list<array>
     */
    private function getNAFPoints(array $point, int $wnd): array
    {
        if (isset($point['naf'])) {
            return $point['naf'];
        }

        $res = [$point];
        $max = (1 << $wnd) - 1;
        $dbl = $max == 1 ? null : $this->doublePoint($point);
        for ($i = 1; $i < $max; $i++) {
            $res[] = $this->addPoint($res[$i - 1], $dbl);
        }

        $point['naf'] = $res;

        /*
        $str = '';
        foreach ($res as $re) {
            $re[0] = bin2hex($re[0]->toBytes());
            $re[1] = bin2hex($re[1]->toBytes());
            $str.= "            ['$re[0]', '$re[1]'],\r\n";
        }
        file_put_contents('temp.txt', $str);
        exit;
        */

        return $res;
    }

    /**
     * Precomputes points in Joint Sparse Form
     *
     * Adapted from:
     * https://github.com/indutny/elliptic/blob/725bd91/lib/elliptic/utils.js#L96
     */
    private static function getJSFPoints(PrimeInteger $k1, PrimeInteger $k2): array
    {
        static $three;
        if (!isset($three)) {
            $three = new BigInteger(3);
        }

        $jsf = [[], []];
        $k1 = $k1->toBigInteger();
        $k2 = $k2->toBigInteger();
        $d1 = 0;
        $d2 = 0;

        while ($k1->compare(new BigInteger(-$d1)) > 0 || $k2->compare(new BigInteger(-$d2)) > 0) {
            // first phase
            $m14 = $k1->testBit(0) + 2 * $k1->testBit(1);
            $m14 += $d1;
            $m14 &= 3;

            $m24 = $k2->testBit(0) + 2 * $k2->testBit(1);
            $m24 += $d2;
            $m24 &= 3;

            if ($m14 == 3) {
                $m14 = -1;
            }
            if ($m24 == 3) {
                $m24 = -1;
            }

            $u1 = 0;
            if ($m14 & 1) { // if $m14 is odd
                $m8 = $k1->testBit(0) + 2 * $k1->testBit(1) + 4 * $k1->testBit(2);
                $m8 += $d1;
                $m8 &= 7;
                $u1 = ($m8 == 3 || $m8 == 5) && $m24 == 2 ? -$m14 : $m14;
            }
            $jsf[0][] = $u1;

            $u2 = 0;
            if ($m24 & 1) { // if $m24 is odd
                $m8 = $k2->testBit(0) + 2 * $k2->testBit(1) + 4 * $k2->testBit(2);
                $m8 += $d2;
                $m8 &= 7;
                $u2 = ($m8 == 3 || $m8 == 5) && $m14 == 2 ? -$m24 : $m24;
            }
            $jsf[1][] = $u2;

            // second phase
            if (2 * $d1 == $u1 + 1) {
                $d1 = 1 - $d1;
            }
            if (2 * $d2 == $u2 + 1) {
                $d2 = 1 - $d2;
            }
            $k1 = $k1->bitwise_rightShift(1);
            $k2 = $k2->bitwise_rightShift(1);
        }

        return $jsf;
    }

    /**
     * Returns the affine point
     *
     * A Jacobian Coordinate is of the form (x, y, z).
     * To convert a Jacobian Coordinate to an Affine Point
     * you do (x / z^2, y / z^3)
     *
     * @return PrimeInteger[]
     */
    public function convertToAffine(array $p): array
    {
        if (!isset($p[2])) {
            return $p;
        }
        [$x, $y, $z] = $p;
        $z = $this->one->divide($z);
        $z2 = $z->multiply($z);
        return [
            $x->multiply($z2),
            $y->multiply($z2)->multiply($z),
        ];
    }

    /**
     * Converts an affine point to a jacobian coordinate
     *
     * @return PrimeInteger[]
     */
    public function convertToInternal(array $p): array
    {
        if (isset($p[2])) {
            return $p;
        }

        $p[2] = clone $this->one;
        $p['fresh'] = true;
        return $p;
    }
}
<?php

/**
 * Curves over a*x^2 + y^2 = 1 + d*x^2*y^2
 *
 * http://www.secg.org/SEC2-Ver-1.0.pdf provides for curves with custom parameters.
 * ie. the coefficients can be arbitrary set through specially formatted keys, etc.
 * As such, Prime.php is built very generically and it's not able to take full
 * advantage of curves with 0 coefficients to produce simplified point doubling,
 * point addition. Twisted Edwards curves, in contrast, do not have a way, currently,
 * to customize them. As such, we can omit the super generic stuff from this class
 * and let the named curves (Ed25519 and Ed448) define their own custom tailored
 * point addition and point doubling methods.
 *
 * More info:
 *
 * https://en.wikipedia.org/wiki/Twisted_Edwards_curve
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC\BaseCurves;

use phpseclib4\Exception\InvalidStateException;
use phpseclib4\Math\{BigInteger, PrimeField};
use phpseclib4\Math\PrimeField\Integer as PrimeInteger;

/**
 * Curves over a*x^2 + y^2 = 1 + d*x^2*y^2
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class TwistedEdwards extends Base
{
    public const HASH = '';
    public const SIZE = -1;

    /**
     * Prime Field Integer factory
     */
    protected PrimeField $factory;

    /**
     * The modulo
     */
    protected BigInteger $modulo;

    /**
     * Coefficient for x^2
     */
    protected PrimeInteger $a;

    /**
     * Coefficient for x^2*y^2
     */
    protected PrimeInteger $d;

    /**
     * Base Point
     */
    protected array $p;

    /**
     * The number zero over the specified finite field
     */
    protected PrimeInteger $zero;

    /**
     * The number one over the specified finite field
     */
    protected PrimeInteger $one;

    /**
     * The number two over the specified finite field
     */
    protected PrimeInteger $two;

    /**
     * Sets the modulo
     */
    public function setModulo(BigInteger $modulo): void
    {
        $this->modulo = $modulo;
        $this->factory = new PrimeField($modulo);
        $this->zero = $this->factory->newInteger(new BigInteger(0));
        $this->one = $this->factory->newInteger(new BigInteger(1));
        $this->two = $this->factory->newInteger(new BigInteger(2));
    }

    /**
     * Set coefficients a and b
     */
    public function setCoefficients(BigInteger $a, BigInteger $d): void
    {
        if (!isset($this->factory)) {
            throw new InvalidStateException('setModulo needs to be called before this method');
        }
        $this->a = $this->factory->newInteger($a);
        $this->d = $this->factory->newInteger($d);
    }

    /**
     * Set x and y coordinates for the base point
     */
    public function setBasePoint(BigInteger|PrimeInteger $x, BigInteger|PrimeInteger $y): void
    {
        if (!isset($this->factory)) {
            throw new InvalidStateException('setModulo needs to be called before this method');
        }
        $this->p = [
            $x instanceof BigInteger ? $this->factory->newInteger($x) : $x,
            $y instanceof BigInteger ? $this->factory->newInteger($y) : $y,
        ];
    }

    /**
     * Returns the a coefficient
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function getA(): PrimeInteger
    {
        return $this->a;
    }

    /**
     * Returns the d coefficient
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function getD(): PrimeInteger
    {
        return $this->d;
    }

    /**
     * Retrieve the base point as an array
     */
    public function getBasePoint(): array
    {
        if (!isset($this->factory)) {
            throw new InvalidStateException('setModulo needs to be called before this method');
        }
        /*
        if (!isset($this->p)) {
            throw new InvalidStateException('setBasePoint needs to be called before this method');
        }
        */
        return $this->p;
    }

    /**
     * Returns the affine point
     *
     * @return PrimeInteger[]
     */
    public function convertToAffine(array $p): array
    {
        if (!isset($p[2])) {
            return $p;
        }
        [$x, $y, $z] = $p;
        $z = $this->one->divide($z);
        return [
            $x->multiply($z),
            $y->multiply($z),
        ];
    }

    /**
     * Returns the modulo
     */
    public function getModulo(): BigInteger
    {
        return $this->modulo;
    }

    /**
     * Tests whether or not the x / y values satisfy the equation
     */
    public function verifyPoint(array $p): bool
    {
        [$x, $y] = $p;
        $x2 = $x->multiply($x);
        $y2 = $y->multiply($y);

        $lhs = $this->a->multiply($x2)->add($y2);
        $rhs = $this->d->multiply($x2)->multiply($y2)->add($this->one);

        return $lhs->equals($rhs);
    }
}
<?php

/**
 * EC Parameters
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2019-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC;

use phpseclib4\Crypt\EC;

/**
 * EC Parameters
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
final class Parameters extends EC
{
    /**
     * Returns the parameters
     *
     * @param array $options optional
     */
    public function toString(string $type = 'PKCS1', array $options = []): string
    {
        $type = self::validatePlugin('Keys', 'PKCS1', 'saveParameters');

        return $type::saveParameters($this->curve, $options);
    }
}
<?php

/**
 * EC Private Key
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2019-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\EC;

use phpseclib4\Common\Functions\Strings;
use phpseclib4\Crypt\{Common, EC, Hash};
use phpseclib4\Crypt\EC\BaseCurves\{Montgomery as MontgomeryCurve, TwistedEdwards as TwistedEdwardsCurve};
use phpseclib4\Crypt\EC\Curves\{Curve25519, Ed25519};
use phpseclib4\Crypt\EC\Formats\Keys\PKCS1;
use phpseclib4\Crypt\EC\Formats\Signature\ASN1 as ASN1Signature;
use phpseclib4\Exception\{
    BadConfigurationException,
    BadMethodCallException,
    UnexpectedValueException,
    UnsupportedValueException
};
use phpseclib4\File\Common\Signable;
use phpseclib4\File\CSR;
use phpseclib4\Math\BigInteger;

/**
 * EC Private Key
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
final class PrivateKey extends EC implements Common\PrivateKey
{
    use Common\Traits\PasswordProtected;

    /**
     * Private Key dA
     *
     * sign() converts this to a BigInteger so one might wonder why this is a FiniteFieldInteger instead of
     * a BigInteger. That's because a FiniteFieldInteger, when converted to a byte string, is null padded by
     * a certain amount whereas a BigInteger isn't.
     */
    protected BigInteger $dA;

    protected ?string $secret = null;

    /**
     * Curve Order
     *
     * Used for deterministic ECDSA
     *
     * @psalm-suppress PossiblyUnusedProperty
     */
    protected BigInteger $q;

    /**
     * Alias for the private key
     *
     * Used for deterministic ECDSA. AsymmetricKey expects $x. I don't like x because
     * with x you have x * the base point yielding an (x, y)-coordinate that is the
     * public key. But the x is different depending on which side of the equal sign
     * you're on. It's less ambiguous if you do dA * base point = (x, y)-coordinate.
     *
     * @psalm-suppress PossiblyUnusedProperty
     */
    protected BigInteger $x;

    /**
     * Multiplies an encoded point by the private key
     *
     * Used by ECDH
     */
    public function multiply(string $coordinates): string
    {
        if (self::$forcedEngine === 'OpenSSL') {
            throw new BadConfigurationException('Engine OpenSSL is not supported for the multiplication operation');
        }

        if (self::$forcedEngine === 'libsodium' && !$this->curve instanceof Curve25519) {
            throw new BadConfigurationException('Engine libsodium is only supported for Curve25519');
        }

        if ($this->curve instanceof Curve25519 && self::$forcedEngine !== 'PHP') {
            if (self::$forcedEngine === 'libsodium' && !function_exists('sodium_crypto_scalarmult')) {
                throw new BadConfigurationException('Engine libsodium is forced but unsupported for Curve25519');
            }
            if (function_exists('sodium_crypto_scalarmult')) {
                $dA = str_pad($this->dA->toBytes(), 32, "\0", STR_PAD_LEFT);
                try {
                    return sodium_crypto_scalarmult($dA, $coordinates);
                } catch (\SodiumException $e) {
                    if (self::$forcedEngine == 'libsodium') {
                        throw new BadConfigurationException('Engine libsodium is forced but was unable to perform multiplication because of ' . $e->getMessage());
                    }
                }
            }
        }

        if ($this->curve instanceof MontgomeryCurve) {
            $point = [$this->curve->convertInteger(new BigInteger(strrev($coordinates), 256))];
            $point = $this->curve->multiplyPoint($point, $this->dA);
            return strrev($point[0]->toBytes(true));
        }

        if (!$this->curve instanceof TwistedEdwardsCurve) {
            $coordinates = "\0$coordinates";
        }

        $point = PKCS1::extractPoint($coordinates, $this->curve);
        $point = $this->curve->multiplyPoint($point, $this->dA);
        if ($this->curve instanceof TwistedEdwardsCurve) {
            return $this->curve->encodePoint($point);
        }
        if (empty($point)) {
            throw new UnexpectedValueException('The infinity point is invalid');
        }
        return "\4" . $point[0]->toBytes(true) . $point[1]->toBytes(true);
    }

    /**
     * Create a signature
     *
     * @see self::verify()
     */
    public function sign(string|Signable $source): string|array
    {
        if ($this->curve instanceof MontgomeryCurve) {
            throw new BadMethodCallException('Montgomery Curves cannot be used to create signatures');
        }

        if ($source instanceof Signable) {
            $public = $this->getPublicKey();
            if ($source instanceof CSR && !$source->hasPublicKey()) {
                $source->setPublicKey($public);
            }
            $source->identifySignatureAlgorithm($this);
            $message = $source->getSignableSection();
        } else {
            $message = $source;
        }

        $dA = $this->dA;
        $order = $this->curve->getOrder();

        $shortFormat = $this->shortFormat;

        if (self::$forcedEngine === 'libsodium' && !$this->curve instanceof Ed25519) {
            throw new BadConfigurationException('Engine libsodium is only supported for Ed25519');
        }

        // at this point either self::$forcedEngine is NOT libsodium or the curve is Ed25519

        if ($this->curve instanceof Ed25519 && self::$forcedEngine !== 'PHP' && self::$forcedEngine !== 'OpenSSL') {
            if (self::$forcedEngine === 'libsodium') {
                if (!function_exists('sodium_crypto_sign_detached')) {
                    throw new BadConfigurationException('Engine libsodium is forced but unsupported for Ed25519 / Ed448');
                }
                if (isset($this->context)) {
                    throw new BadConfigurationException('Engine libsodium is forced but unsupported for Ed25519ctx (context)');
                }
            }
            if (function_exists('sodium_crypto_sign_detached') && !isset($this->context)) {
                $result = sodium_crypto_sign_detached($message, $this->withPassword()->toString('libsodium'));
                $signature = $shortFormat == 'SSH2' ? Strings::packSSH2('ss', 'ssh-' . strtolower($this->getCurve()), $result) : $result;
                if ($source instanceof Signable) {
                    $source->setSignature($signature);
                }
                return $signature;
            }
        }

        // at this point self::$forcedEngine CAN'T be libsodium so we won't check for it henceforth

        if ($this->curve instanceof TwistedEdwardsCurve) {
            if (self::$forcedEngine !== 'PHP') {
                $keyTypeConstant = $this->curve instanceof Ed25519 ? 'OPENSSL_KEYTYPE_ED25519' : 'OPENSSL_KEYTYPE_ED448';
                if (self::$forcedEngine === 'OpenSSL') {
                    if (!defined($keyTypeConstant)) {
                        throw new BadConfigurationException('Engine OpenSSL is forced but unsupported for Ed25519 / Ed448');
                    }
                    // OpenSSL supports Ed25519/Ed448 but not Ed25519ctx (context), so skip if context is set
                    if (isset($this->context)) {
                        throw new BadConfigurationException('Engine OpenSSL is forced but unsupported for Ed25519 / Ed448 curves with context\'s');
                    }
                }
                if (defined($keyTypeConstant) && !isset($this->context)) {
                    $result = '';
                    // algorithm 0 is used because EdDSA has a built-in hash
                    openssl_sign($message, $result, $this->withPassword()->toString('PKCS8'), 0);
                    if ($result) {
                        $signature = $shortFormat == 'SSH2'
                            ? Strings::packSSH2('ss', 'ssh-' . strtolower($this->getCurve()), $result)
                            : $result;
                        if ($source instanceof Signable) {
                            $source->setSignature($signature);
                        }
                        return $signature;
                    }
                    if (self::$forcedEngine === 'OpenSSL') {
                        throw new BadConfigurationException('Engine OpenSSL is forced but was unable to create signature because of ' . openssl_error_string());
                    }
                }
            }

            // contexts (Ed25519ctx) are supported but prehashing (Ed25519ph) is not.
            // quoting https://tools.ietf.org/html/rfc8032#section-8.5 ,
            // "The Ed25519ph and Ed448ph variants ... SHOULD NOT be used"
            $A = $this->curve->encodePoint($this->QA);
            $curve = $this->curve;
            $hash = new Hash($curve::HASH);

            $secret = substr($hash->hash($this->secret), $curve::SIZE);

            if ($curve instanceof Ed25519) {
                $dom = !isset($this->context) ? '' :
                    'SigEd25519 no Ed25519 collisions' . "\0" . chr(strlen($this->context)) . $this->context;
            } else {
                $context = $this->context ?? '';
                $dom = 'SigEd448' . "\0" . chr(strlen($context)) . $context;
            }
            // SHA-512(dom2(F, C) || prefix || PH(M))
            $r = $hash->hash($dom . $secret . $message);
            $r = strrev($r);
            $r = new BigInteger($r, 256);
            [, $r] = $r->divide($order);
            $R = $curve->multiplyPoint($curve->getBasePoint(), $r);
            $R = $curve->encodePoint($R);
            $k = $hash->hash($dom . $R . $A . $message);
            $k = strrev($k);
            $k = new BigInteger($k, 256);
            [, $k] = $k->divide($order);
            $S = $k->multiply($dA)->add($r);
            [, $S] = $S->divide($order);
            $S = str_pad(strrev($S->toBytes()), $curve::SIZE, "\0");
            $signature = $shortFormat == 'SSH2' ? Strings::packSSH2('ss', 'ssh-' . strtolower($this->getCurve()), $R . $S) : $R . $S;
            if ($source instanceof Signable) {
                $source->setSignature($signature);
            }
            return $signature;
        }

        if (self::$forcedEngine === 'OpenSSL' && !function_exists('openssl_get_md_methods')) {
            throw new BadConfigurationException('Engine OpenSSL is forced but unsupported for ECDSA');
        }

        // at this point $forcedEngine is either PHP or null. either that OR openssl_get_md_methods() exists

        if (self::$forcedEngine !== 'PHP') {
            if (in_array($this->hash->getHash(), openssl_get_md_methods())) {
                $signature = '';
                // altho PHP's OpenSSL bindings only supported EC key creation in PHP 7.1 they've long
                // supported signing / verification
                // we use specified curves to avoid issues with OpenSSL possibly not supporting a given named curve;
                // doing this may mean some curve-specific optimizations can't be used but idk if OpenSSL even
                // has curve-specific optimizations
                $result = openssl_sign($message, $signature, $this->withPassword()->toString('PKCS8', ['namedCurve' => false]), $this->hash->getHash());

                if ($result) {
                    if ($shortFormat == 'ASN1') {
                        if ($source instanceof Signable) {
                            $source->setSignature($signature);
                        }
                        return $signature;
                    }

                    ['r' => $r, 's' => $s] = ASN1Signature::load($signature);
                    $signature = $this->formatSignature($r, $s);

                    if ($source instanceof Signable) {
                        if (is_array($signature)) {
                            throw new UnsupportedValueException('The Raw signature format cannot be used with Signable objects');
                        }
                        $source->setSignature($signature);
                    }

                    return $signature;
                } elseif (self::$forcedEngine === 'OpenSSL') {
                    throw new BadConfigurationException('Engine OpenSSL is forced but was unable to create signature because of ' . openssl_error_string());
                }
            } elseif (self::$forcedEngine === 'OpenSSL') {
                throw new BadConfigurationException('Engine OpenSSL is forced but unsupported for ECDSA / ' . $this->hash->getHash());
            }
        }

        $e = $this->hash->hash($message);
        $e = new BigInteger($e, 256);

        $Ln = $this->hash->getLength() - $order->getLength();
        $z = $Ln > 0 ? $e->bitwise_rightShift($Ln) : $e;

        while (true) {
            $k = BigInteger::randomRange(self::$one, $order->subtract(self::$one));
            // multiplyPoint() always returns [$x, $y]; only $x is needed here
            [$x, ] = $this->curve->multiplyPoint($this->curve->getBasePoint(), $k);
            $x = $x->toBigInteger();
            [, $r] = $x->divide($order);
            if ($r->equals(self::$zero)) {
                continue;
            }
            $kinv = $k->modInverse($order);
            $temp = $z->add($dA->multiply($r));
            $temp = $kinv->multiply($temp);
            [, $s] = $temp->divide($order);
            if (!$s->equals(self::$zero)) {
                break;
            }
        }

        // the following is an RFC6979 compliant implementation of deterministic ECDSA
        // it's unused because it's mainly intended for use when a good CSPRNG isn't
        // available. if phpseclib's CSPRNG isn't good then even key generation is
        // suspect
        /*
        // if this were actually being used it'd probably be better if this lived in load() and createKey()
        $q = $this->curve->getOrder();
        $x = $this->dA->toBigInteger();

        $h1 = $this->hash->hash($message);
        $k = $this->computek($h1);
        [$x, $y] = $this->curve->multiplyPoint($this->curve->getBasePoint(), $k);
        $x = $x->toBigInteger();
        [, $r] = $x->divide($q);
        $kinv = $k->modInverse($q);
        $h1 = $this->bits2int($h1);
        $temp = $h1->add($dA->multiply($r));
        $temp = $kinv->multiply($temp);
        [, $s] = $temp->divide($q);
        */

        $signature = $this->formatSignature($r, $s);

        if ($source instanceof Signable) {
            if (is_array($signature)) {
                throw new UnsupportedValueException('The Raw signature format cannot be used with Signable objects');
            }
            $source->setSignature($signature);
        }

        return $signature;
    }

    /**
     * Returns the private key
     *
     * @param array $options optional
     */
    public function toString(string $type, array $options = []): string
    {
        $type = self::validatePlugin('Keys', $type, 'savePrivateKey');

        return $type::savePrivateKey($this->dA, $this->curve, $this->QA, $this->secret, $this->password, $options);
    }

    /**
     * Returns the public key
     *
     * @see self::getPrivateKey()
     */
    public function getPublicKey(): PublicKey
    {
        $format = 'PKCS8';
        if ($this->curve instanceof MontgomeryCurve) {
            $format = 'MontgomeryPublic';
        }

        $type = self::validatePlugin('Keys', $format, 'savePublicKey');

        $key = $type::savePublicKey($this->curve, $this->QA);
        $key = EC::loadFormat($format, $key);
        if ($this->curve instanceof MontgomeryCurve) {
            return $key;
        }
        $key = $key
            ->withHash($this->hash->getHash())
            ->withSignatureFormat($this->shortFormat);
        if ($this->curve instanceof TwistedEdwardsCurve) {
            $key = $key->withContext($this->context);
        }
        return $key;
    }

    /**
     * Returns a signature in the appropriate format
     */
    private function formatSignature(BigInteger $r, BigInteger $s): string|array
    {
        $format = $this->sigFormat;

        $temp = new \ReflectionMethod($format, 'save');
        $paramCount = $temp->getNumberOfRequiredParameters();

        return match ($paramCount) {
            2 => $format::save($r, $s),
            3 => $format::save($r, $s, $this->getCurve()),
            4 => $format::save($r, $s, $this->getCurve(), $this->getLength()),
            default => throw new UnexpectedValueException("$format::save() has $paramCount parameters - the only valid parameter counts are 2, 3 or 4")
        };
    }
}
<?php

/**
 * Pure-PHP implementation of RC2.
 *
 * Uses OpenSSL, if available/possible, and an internal implementation, otherwise
 *
 * PHP version 8.1+
 *
 * Useful resources are as follows:
 *
 *  - {@link http://tools.ietf.org/html/rfc2268}
 *
 * Here's a short example of how to use this library:
 * <code>
 * <?php
 *    include 'vendor/autoload.php';
 *
 *    $rc2 = new \phpseclib4\Crypt\RC2('ctr');
 *
 *    $rc2->setKey('abcdefgh');
 *
 *    $plaintext = str_repeat('a', 1024);
 *
 *    echo $rc2->decrypt($rc2->encrypt($plaintext));
 * ?>
 * </code>
 *
 * @author   Patrick Monnerat <pm@datasphere.ch>
 * @license  http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link     https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt;

use phpseclib4\Crypt\Common\BlockCipher;
use phpseclib4\Exception\LengthException;

/**
 * Pure-PHP implementation of RC2.
 */
class RC2 extends BlockCipher
{
    /**
     * Block Length of the cipher
     *
     * @see Common\SymmetricKey::block_size
     */
    protected int $block_size = 8;

    /**
     * The Original (unpadded) Key
     *
     * @see Common\SymmetricKey::key
     * @see self::setKey()
     * @see self::encrypt()
     * @see self::decrypt()
     */
    private string $orig_key;

    /**
     * Key Length (in bytes)
     *
     * @see \phpseclib4\Crypt\RC2::setKeyLength()
     */
    protected int $key_length = 16; // = 128 bits

    /**
     * The key length in bits.
     *
     * {@internal Should be in range [1..1024].}
     *
     * {@internal Changing this value after setting the key has no effect.}
     *
     * @see self::setKeyLength()
     * @see self::setKey()
     */
    private int $default_key_length = 1024;

    /**
     * The key length in bits.
     *
     * {@internal Should be in range [1..1024].}
     *
     * @see self::isValidEnine()
     * @see self::setKey()
     */
    private int $current_key_length;

    /**
     * The Key Schedule
     *
     * @see self::setupKey()
     */
    private array $keys;

    /**
     * Key expansion randomization table.
     * Twice the same 256-value sequence to save a modulus in key expansion.
     *
     * @see self::setKey()
     * @var int[]
     */
    private static array $pitable = [
        0xD9, 0x78, 0xF9, 0xC4, 0x19, 0xDD, 0xB5, 0xED,
        0x28, 0xE9, 0xFD, 0x79, 0x4A, 0xA0, 0xD8, 0x9D,
        0xC6, 0x7E, 0x37, 0x83, 0x2B, 0x76, 0x53, 0x8E,
        0x62, 0x4C, 0x64, 0x88, 0x44, 0x8B, 0xFB, 0xA2,
        0x17, 0x9A, 0x59, 0xF5, 0x87, 0xB3, 0x4F, 0x13,
        0x61, 0x45, 0x6D, 0x8D, 0x09, 0x81, 0x7D, 0x32,
        0xBD, 0x8F, 0x40, 0xEB, 0x86, 0xB7, 0x7B, 0x0B,
        0xF0, 0x95, 0x21, 0x22, 0x5C, 0x6B, 0x4E, 0x82,
        0x54, 0xD6, 0x65, 0x93, 0xCE, 0x60, 0xB2, 0x1C,
        0x73, 0x56, 0xC0, 0x14, 0xA7, 0x8C, 0xF1, 0xDC,
        0x12, 0x75, 0xCA, 0x1F, 0x3B, 0xBE, 0xE4, 0xD1,
        0x42, 0x3D, 0xD4, 0x30, 0xA3, 0x3C, 0xB6, 0x26,
        0x6F, 0xBF, 0x0E, 0xDA, 0x46, 0x69, 0x07, 0x57,
        0x27, 0xF2, 0x1D, 0x9B, 0xBC, 0x94, 0x43, 0x03,
        0xF8, 0x11, 0xC7, 0xF6, 0x90, 0xEF, 0x3E, 0xE7,
        0x06, 0xC3, 0xD5, 0x2F, 0xC8, 0x66, 0x1E, 0xD7,
        0x08, 0xE8, 0xEA, 0xDE, 0x80, 0x52, 0xEE, 0xF7,
        0x84, 0xAA, 0x72, 0xAC, 0x35, 0x4D, 0x6A, 0x2A,
        0x96, 0x1A, 0xD2, 0x71, 0x5A, 0x15, 0x49, 0x74,
        0x4B, 0x9F, 0xD0, 0x5E, 0x04, 0x18, 0xA4, 0xEC,
        0xC2, 0xE0, 0x41, 0x6E, 0x0F, 0x51, 0xCB, 0xCC,
        0x24, 0x91, 0xAF, 0x50, 0xA1, 0xF4, 0x70, 0x39,
        0x99, 0x7C, 0x3A, 0x85, 0x23, 0xB8, 0xB4, 0x7A,
        0xFC, 0x02, 0x36, 0x5B, 0x25, 0x55, 0x97, 0x31,
        0x2D, 0x5D, 0xFA, 0x98, 0xE3, 0x8A, 0x92, 0xAE,
        0x05, 0xDF, 0x29, 0x10, 0x67, 0x6C, 0xBA, 0xC9,
        0xD3, 0x00, 0xE6, 0xCF, 0xE1, 0x9E, 0xA8, 0x2C,
        0x63, 0x16, 0x01, 0x3F, 0x58, 0xE2, 0x89, 0xA9,
        0x0D, 0x38, 0x34, 0x1B, 0xAB, 0x33, 0xFF, 0xB0,
        0xBB, 0x48, 0x0C, 0x5F, 0xB9, 0xB1, 0xCD, 0x2E,
        0xC5, 0xF3, 0xDB, 0x47, 0xE5, 0xA5, 0x9C, 0x77,
        0x0A, 0xA6, 0x20, 0x68, 0xFE, 0x7F, 0xC1, 0xAD,
        0xD9, 0x78, 0xF9, 0xC4, 0x19, 0xDD, 0xB5, 0xED,
        0x28, 0xE9, 0xFD, 0x79, 0x4A, 0xA0, 0xD8, 0x9D,
        0xC6, 0x7E, 0x37, 0x83, 0x2B, 0x76, 0x53, 0x8E,
        0x62, 0x4C, 0x64, 0x88, 0x44, 0x8B, 0xFB, 0xA2,
        0x17, 0x9A, 0x59, 0xF5, 0x87, 0xB3, 0x4F, 0x13,
        0x61, 0x45, 0x6D, 0x8D, 0x09, 0x81, 0x7D, 0x32,
        0xBD, 0x8F, 0x40, 0xEB, 0x86, 0xB7, 0x7B, 0x0B,
        0xF0, 0x95, 0x21, 0x22, 0x5C, 0x6B, 0x4E, 0x82,
        0x54, 0xD6, 0x65, 0x93, 0xCE, 0x60, 0xB2, 0x1C,
        0x73, 0x56, 0xC0, 0x14, 0xA7, 0x8C, 0xF1, 0xDC,
        0x12, 0x75, 0xCA, 0x1F, 0x3B, 0xBE, 0xE4, 0xD1,
        0x42, 0x3D, 0xD4, 0x30, 0xA3, 0x3C, 0xB6, 0x26,
        0x6F, 0xBF, 0x0E, 0xDA, 0x46, 0x69, 0x07, 0x57,
        0x27, 0xF2, 0x1D, 0x9B, 0xBC, 0x94, 0x43, 0x03,
        0xF8, 0x11, 0xC7, 0xF6, 0x90, 0xEF, 0x3E, 0xE7,
        0x06, 0xC3, 0xD5, 0x2F, 0xC8, 0x66, 0x1E, 0xD7,
        0x08, 0xE8, 0xEA, 0xDE, 0x80, 0x52, 0xEE, 0xF7,
        0x84, 0xAA, 0x72, 0xAC, 0x35, 0x4D, 0x6A, 0x2A,
        0x96, 0x1A, 0xD2, 0x71, 0x5A, 0x15, 0x49, 0x74,
        0x4B, 0x9F, 0xD0, 0x5E, 0x04, 0x18, 0xA4, 0xEC,
        0xC2, 0xE0, 0x41, 0x6E, 0x0F, 0x51, 0xCB, 0xCC,
        0x24, 0x91, 0xAF, 0x50, 0xA1, 0xF4, 0x70, 0x39,
        0x99, 0x7C, 0x3A, 0x85, 0x23, 0xB8, 0xB4, 0x7A,
        0xFC, 0x02, 0x36, 0x5B, 0x25, 0x55, 0x97, 0x31,
        0x2D, 0x5D, 0xFA, 0x98, 0xE3, 0x8A, 0x92, 0xAE,
        0x05, 0xDF, 0x29, 0x10, 0x67, 0x6C, 0xBA, 0xC9,
        0xD3, 0x00, 0xE6, 0xCF, 0xE1, 0x9E, 0xA8, 0x2C,
        0x63, 0x16, 0x01, 0x3F, 0x58, 0xE2, 0x89, 0xA9,
        0x0D, 0x38, 0x34, 0x1B, 0xAB, 0x33, 0xFF, 0xB0,
        0xBB, 0x48, 0x0C, 0x5F, 0xB9, 0xB1, 0xCD, 0x2E,
        0xC5, 0xF3, 0xDB, 0x47, 0xE5, 0xA5, 0x9C, 0x77,
        0x0A, 0xA6, 0x20, 0x68, 0xFE, 0x7F, 0xC1, 0xAD,
    ];

    /**
     * Inverse key expansion randomization table.
     *
     * @see self::setKey()
     * @var int[]
     */
    private static array $invpitable = [
        0xD1, 0xDA, 0xB9, 0x6F, 0x9C, 0xC8, 0x78, 0x66,
        0x80, 0x2C, 0xF8, 0x37, 0xEA, 0xE0, 0x62, 0xA4,
        0xCB, 0x71, 0x50, 0x27, 0x4B, 0x95, 0xD9, 0x20,
        0x9D, 0x04, 0x91, 0xE3, 0x47, 0x6A, 0x7E, 0x53,
        0xFA, 0x3A, 0x3B, 0xB4, 0xA8, 0xBC, 0x5F, 0x68,
        0x08, 0xCA, 0x8F, 0x14, 0xD7, 0xC0, 0xEF, 0x7B,
        0x5B, 0xBF, 0x2F, 0xE5, 0xE2, 0x8C, 0xBA, 0x12,
        0xE1, 0xAF, 0xB2, 0x54, 0x5D, 0x59, 0x76, 0xDB,
        0x32, 0xA2, 0x58, 0x6E, 0x1C, 0x29, 0x64, 0xF3,
        0xE9, 0x96, 0x0C, 0x98, 0x19, 0x8D, 0x3E, 0x26,
        0xAB, 0xA5, 0x85, 0x16, 0x40, 0xBD, 0x49, 0x67,
        0xDC, 0x22, 0x94, 0xBB, 0x3C, 0xC1, 0x9B, 0xEB,
        0x45, 0x28, 0x18, 0xD8, 0x1A, 0x42, 0x7D, 0xCC,
        0xFB, 0x65, 0x8E, 0x3D, 0xCD, 0x2A, 0xA3, 0x60,
        0xAE, 0x93, 0x8A, 0x48, 0x97, 0x51, 0x15, 0xF7,
        0x01, 0x0B, 0xB7, 0x36, 0xB1, 0x2E, 0x11, 0xFD,
        0x84, 0x2D, 0x3F, 0x13, 0x88, 0xB3, 0x34, 0x24,
        0x1B, 0xDE, 0xC5, 0x1D, 0x4D, 0x2B, 0x17, 0x31,
        0x74, 0xA9, 0xC6, 0x43, 0x6D, 0x39, 0x90, 0xBE,
        0xC3, 0xB0, 0x21, 0x6B, 0xF6, 0x0F, 0xD5, 0x99,
        0x0D, 0xAC, 0x1F, 0x5C, 0x9E, 0xF5, 0xF9, 0x4C,
        0xD6, 0xDF, 0x89, 0xE4, 0x8B, 0xFF, 0xC7, 0xAA,
        0xE7, 0xED, 0x46, 0x25, 0xB6, 0x06, 0x5E, 0x35,
        0xB5, 0xEC, 0xCE, 0xE8, 0x6C, 0x30, 0x55, 0x61,
        0x4A, 0xFE, 0xA0, 0x79, 0x03, 0xF0, 0x10, 0x72,
        0x7C, 0xCF, 0x52, 0xA6, 0xA7, 0xEE, 0x44, 0xD3,
        0x9A, 0x57, 0x92, 0xD0, 0x5A, 0x7A, 0x41, 0x7F,
        0x0E, 0x00, 0x63, 0xF2, 0x4F, 0x05, 0x83, 0xC9,
        0xA1, 0xD4, 0xDD, 0xC4, 0x56, 0xF4, 0xD2, 0x77,
        0x81, 0x09, 0x82, 0x33, 0x9F, 0x07, 0x86, 0x75,
        0x38, 0x4E, 0x69, 0xF1, 0xAD, 0x23, 0x73, 0x87,
        0x70, 0x02, 0xC2, 0x1E, 0xB8, 0x0A, 0xFC, 0xE6,
    ];

    /**
     * Test for engine validity
     *
     * This is mainly just a wrapper to set things up for \phpseclib4\Crypt\Common\SymmetricKey::isValidEngine()
     *
     * @see Common\SymmetricKey::__construct()
     */
    protected function isValidEngineHelper(int $engine): bool
    {
        switch ($engine) {
            case self::ENGINE_OPENSSL:
                if ($this->current_key_length != 128 || strlen($this->orig_key) < 16) {
                    return false;
                }
                // quoting https://www.openssl.org/news/openssl-3.0-notes.html, OpenSSL 3.0.1
                // "Moved all variations of the EVP ciphers CAST5, BF, IDEA, SEED, RC2, RC4, RC5, and DES to the legacy provider"
                // in theory openssl_get_cipher_methods() should catch this but, on GitHub Actions, at least, it does not
                if (defined('OPENSSL_VERSION_NUMBER') && OPENSSL_VERSION_NUMBER >= 0x30000010) {
                    return false;
                }
                $this->cipher_name_openssl_ecb = 'rc2-ecb';
                $this->cipher_name_openssl = 'rc2-' . $this->openssl_translate_mode();
        }

        return parent::isValidEngineHelper($engine);
    }

    /**
     * Sets the key length.
     *
     * Valid key lengths are 8 to 1024.
     * Calling this function after setting the key has no effect until the next
     *  \phpseclib4\Crypt\RC2::setKey() call.
     *
     * @param int $length in bits
     * @throws LengthException if the key length isn't supported
     */
    public function setKeyLength(int $length): void
    {
        if ($length < 8 || $length > 1024) {
            throw new LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys between 1 and 1024 bits, inclusive, are supported');
        }

        $this->default_key_length = $this->current_key_length = $length;
        $this->explicit_key_length = $length >> 3;
    }

    /**
     * Returns the current key length
     */
    public function getKeyLength(): int
    {
        return $this->current_key_length;
    }

    /**
     * Sets the key.
     *
     * Keys can be of any length. RC2, itself, uses 8 to 1024 bit keys (eg.
     * strlen($key) <= 128), however, we only use the first 128 bytes if $key
     * has more then 128 bytes in it, and set $key to a single null byte if
     * it is empty.
     *
     * @throws LengthException if the key length isn't supported
     * @see Common\SymmetricKey::setKey()
     */
    public function setKey(#[\SensitiveParameter] string $key, ?int $t1 = null): void
    {
        $this->orig_key = $key;

        if ($t1 === null) {
            $t1 = $this->default_key_length;
        }

        if ($t1 < 1 || $t1 > 1024) {
            throw new LengthException('Key size of ' . $t1 . ' bits is not supported by this algorithm. Only keys between 1 and 1024 bits, inclusive, are supported');
        }

        $this->current_key_length = $t1;
        if (strlen($key) < 1 || strlen($key) > 128) {
            throw new LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of sizes between 8 and 1024 bits, inclusive, are supported');
        }

        $t = strlen($key);

        // The mcrypt RC2 implementation only supports effective key length
        // of 1024 bits. It is however possible to handle effective key
        // lengths in range 1..1024 by expanding the key and applying
        // inverse pitable mapping to the first byte before submitting it
        // to mcrypt.

        // Key expansion.
        $l = array_values(unpack('C*', $key));
        $t8 = ($t1 + 7) >> 3;
        $tm = 0xFF >> (8 * $t8 - $t1);

        // Expand key.
        $pitable = self::$pitable;
        for ($i = $t; $i < 128; $i++) {
            $l[$i] = $pitable[$l[$i - 1] + $l[$i - $t]];
        }
        $i = 128 - $t8;
        $l[$i] = $pitable[$l[$i] & $tm];
        while ($i--) {
            $l[$i] = $pitable[$l[$i + 1] ^ $l[$i + $t8]];
        }

        // Prepare the key for mcrypt.
        $l[0] = self::$invpitable[$l[0]];
        array_unshift($l, 'C*');

        $this->key = pack(...$l);
        $this->key_length = strlen($this->key);
        $this->changed = $this->nonIVChanged = true;
        $this->setEngine();
    }

    /**
     * Encrypts a message.
     *
     * Mostly a wrapper for \phpseclib4\Crypt\Common\SymmetricKey::encrypt, with some additional OpenSSL handling code
     *
     * @see self::decrypt()
     */
    public function encrypt(#[\SensitiveParameter] string $plaintext): string
    {
        if ($this->engine == self::ENGINE_OPENSSL) {
            $temp = $this->key;
            $this->key = $this->orig_key;
            $result = parent::encrypt($plaintext);
            $this->key = $temp;
            return $result;
        }

        return parent::encrypt($plaintext);
    }

    /**
     * Decrypts a message.
     *
     * Mostly a wrapper for \phpseclib4\Crypt\Common\SymmetricKey::decrypt, with some additional OpenSSL handling code
     *
     * @see self::encrypt()
     */
    public function decrypt(string $ciphertext): string
    {
        if ($this->engine == self::ENGINE_OPENSSL) {
            $temp = $this->key;
            $this->key = $this->orig_key;
            $result = parent::decrypt($ciphertext);
            $this->key = $temp;
            return $result;
        }

        return parent::decrypt($ciphertext);
    }

    /**
     * Encrypts a block
     *
     * @see Common\SymmetricKey::encryptBlock()
     * @see Common\SymmetricKey::encrypt()
     */
    protected function encryptBlock(string $in): string
    {
        [$r0, $r1, $r2, $r3] = array_values(unpack('v*', $in));
        $keys = $this->keys;
        $limit = 20;
        $actions = [$limit => 44, 44 => 64];
        $j = 0;

        for (;;) {
            // Mixing round.
            $r0 = (($r0 + $keys[$j++] + ((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF) << 1;
            $r0 |= $r0 >> 16;
            $r1 = (($r1 + $keys[$j++] + ((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF) << 2;
            $r1 |= $r1 >> 16;
            $r2 = (($r2 + $keys[$j++] + ((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF) << 3;
            $r2 |= $r2 >> 16;
            $r3 = (($r3 + $keys[$j++] + ((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF) << 5;
            $r3 |= $r3 >> 16;

            if ($j === $limit) {
                if ($limit === 64) {
                    break;
                }

                // Mashing round.
                $r0 += $keys[$r3 & 0x3F];
                $r1 += $keys[$r0 & 0x3F];
                $r2 += $keys[$r1 & 0x3F];
                $r3 += $keys[$r2 & 0x3F];
                $limit = $actions[$limit];
            }
        }

        return pack('vvvv', $r0, $r1, $r2, $r3);
    }

    /**
     * Decrypts a block
     *
     * @see Common\SymmetricKey::decryptBlock()
     * @see Common\SymmetricKey::decrypt()
     */
    protected function decryptBlock(string $in): string
    {
        [$r0, $r1, $r2, $r3] = array_values(unpack('v*', $in));
        $keys = $this->keys;
        $limit = 44;
        $actions = [$limit => 20, 20 => 0];
        $j = 64;

        for (;;) {
            // R-mixing round.
            $r3 = ($r3 | ($r3 << 16)) >> 5;
            $r3 = ($r3 - $keys[--$j] - ((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF;
            $r2 = ($r2 | ($r2 << 16)) >> 3;
            $r2 = ($r2 - $keys[--$j] - ((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF;
            $r1 = ($r1 | ($r1 << 16)) >> 2;
            $r1 = ($r1 - $keys[--$j] - ((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF;
            $r0 = ($r0 | ($r0 << 16)) >> 1;
            $r0 = ($r0 - $keys[--$j] - ((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF;

            if ($j === $limit) {
                if ($limit === 0) {
                    break;
                }

                // R-mashing round.
                $r3 = ($r3 - $keys[$r2 & 0x3F]) & 0xFFFF;
                $r2 = ($r2 - $keys[$r1 & 0x3F]) & 0xFFFF;
                $r1 = ($r1 - $keys[$r0 & 0x3F]) & 0xFFFF;
                $r0 = ($r0 - $keys[$r3 & 0x3F]) & 0xFFFF;
                $limit = $actions[$limit];
            }
        }

        return pack('vvvv', $r0, $r1, $r2, $r3);
    }

    /**
     * Creates the key schedule
     *
     * @see Common\SymmetricKey::setupKey()
     */
    protected function setupKey(): void
    {
        // Key has already been expanded in \phpseclib4\Crypt\RC2::setKey():
        // Only the first value must be altered.
        $l = unpack('Ca/Cb/v*', $this->key);
        array_unshift($l, self::$pitable[$l['a']] | ($l['b'] << 8));
        unset($l['a']);
        unset($l['b']);
        $this->keys = $l;
    }

    /**
     * Setup the performance-optimized function for de/encrypt()
     *
     * @see Common\SymmetricKey::setupInlineCrypt()
     * @psalm-suppress PossiblyUnusedMethod
     */
    protected function setupInlineCrypt(): void
    {
        // Init code for both, encrypt and decrypt.
        $init_crypt = '$keys = $this->keys;';

        $keys = $this->keys;

        // $in is the current 8 bytes block which has to be en/decrypt
        $encrypt_block = $decrypt_block = '
            $in = unpack("v4", $in);
            $r0 = $in[1];
            $r1 = $in[2];
            $r2 = $in[3];
            $r3 = $in[4];
        ';

        // Create code for encryption.
        $limit = 20;
        $actions = [$limit => 44, 44 => 64];
        $j = 0;

        while (true) {
            // Mixing round.
            $encrypt_block .= '
                $r0 = (($r0 + ' . $keys[$j++] . ' +
                       ((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF) << 1;
                $r0 |= $r0 >> 16;
                $r1 = (($r1 + ' . $keys[$j++] . ' +
                       ((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF) << 2;
                $r1 |= $r1 >> 16;
                $r2 = (($r2 + ' . $keys[$j++] . ' +
                       ((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF) << 3;
                $r2 |= $r2 >> 16;
                $r3 = (($r3 + ' . $keys[$j++] . ' +
                       ((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF) << 5;
                $r3 |= $r3 >> 16;';

            if ($j === $limit) {
                if ($limit === 64) {
                    break;
                }

                // Mashing round.
                $encrypt_block .= '
                    $r0 += $keys[$r3 & 0x3F];
                    $r1 += $keys[$r0 & 0x3F];
                    $r2 += $keys[$r1 & 0x3F];
                    $r3 += $keys[$r2 & 0x3F];';
                $limit = $actions[$limit];
            }
        }

        $encrypt_block .= '$in = pack("v4", $r0, $r1, $r2, $r3);';

        // Create code for decryption.
        $limit = 44;
        $actions = [$limit => 20, 20 => 0];
        $j = 64;

        while (true) {
            // R-mixing round.
            $decrypt_block .= '
                $r3 = ($r3 | ($r3 << 16)) >> 5;
                $r3 = ($r3 - ' . $keys[--$j] . ' -
                       ((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF;
                $r2 = ($r2 | ($r2 << 16)) >> 3;
                $r2 = ($r2 - ' . $keys[--$j] . ' -
                       ((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF;
                $r1 = ($r1 | ($r1 << 16)) >> 2;
                $r1 = ($r1 - ' . $keys[--$j] . ' -
                       ((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF;
                $r0 = ($r0 | ($r0 << 16)) >> 1;
                $r0 = ($r0 - ' . $keys[--$j] . ' -
                       ((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF;';

            if ($j === $limit) {
                if ($limit === 0) {
                    break;
                }

                // R-mashing round.
                $decrypt_block .= '
                    $r3 = ($r3 - $keys[$r2 & 0x3F]) & 0xFFFF;
                    $r2 = ($r2 - $keys[$r1 & 0x3F]) & 0xFFFF;
                    $r1 = ($r1 - $keys[$r0 & 0x3F]) & 0xFFFF;
                    $r0 = ($r0 - $keys[$r3 & 0x3F]) & 0xFFFF;';
                $limit = $actions[$limit];
            }
        }

        $decrypt_block .= '$in = pack("v4", $r0, $r1, $r2, $r3);';

        // Creates the inline-crypt function
        $this->inline_crypt = $this->createInlineCryptFunction(
            [
               'init_crypt'    => $init_crypt,
               'encrypt_block' => $encrypt_block,
               'decrypt_block' => $decrypt_block,
            ]
        );
    }
}
<?php

/**
 * Pure-PHP implementation of Blowfish.
 *
 * Uses an internal implementation.
 *
 * PHP version 8.1+
 *
 * Useful resources are as follows:
 *
 *  - {@link http://en.wikipedia.org/wiki/Blowfish_(cipher) Wikipedia description of Blowfish}
 *
 * # An overview of bcrypt vs Blowfish
 *
 * OpenSSH private keys use a customized version of bcrypt. Specifically, instead of
 * encrypting OrpheanBeholderScryDoubt 64 times OpenSSH's bcrypt variant encrypts
 * OxychromaticBlowfishSwatDynamite 64 times. so we can't use crypt().
 *
 * bcrypt is basically Blowfish but instead of performing the key expansion once it performs
 * the expansion 129 times for each round, with the first key expansion interleaving the salt
 * and password. This renders OpenSSL unusable and forces us to use a pure-PHP implementation
 * of blowfish.
 *
 * # phpseclib's three different _encryptBlock() implementations
 *
 * When using Blowfish as an encryption algorithm, _encryptBlock() is called 9 + 512 +
 * (the number of blocks in the plaintext) times.
 *
 * Each of the first 9 calls to _encryptBlock() modify the P-array. Each of the next 512
 * calls modify the S-boxes. The remaining _encryptBlock() calls operate on the plaintext to
 * produce the ciphertext. In the pure-PHP implementation of Blowfish these remaining
 * _encryptBlock() calls are highly optimized through the use of eval(). Among other things,
 * P-array lookups are eliminated by hard-coding the key-dependent P-array values, and thus we
 * have explained 2 of the 4 different _encryptBlock() implementations.
 *
 * With bcrypt things are a bit different. _encryptBlock() is called 1,079,296 times,
 * assuming 16 rounds (which is what OpenSSH's bcrypt defaults to). The eval()-optimized
 * _encryptBlock() isn't as beneficial because the P-array values are not constant. Well, they
 * are constant, but only for, at most, 777 _encryptBlock() calls, which is equivalent to ~6KB
 * of data. The average length of back to back _encryptBlock() calls with a fixed P-array is
 * 514.12, which is ~4KB of data. Creating an eval()-optimized _encryptBlock() has an upfront
 * cost, which is CPU dependent and is probably not going to be worth it for just ~4KB of
 * data. Conseqeuently, bcrypt does not benefit from the eval()-optimized _encryptBlock().
 *
 * The regular _encryptBlock() does unpack() and pack() on every call, as well, and that can
 * begin to add up after one million function calls.
 *
 * In theory, one might think that it might be beneficial to rewrite all block ciphers so
 * that, instead of passing strings to _encryptBlock(), you convert the string to an array of
 * integers and then pass successive subarrays of that array to _encryptBlock. This, however,
 * kills PHP's memory use. Like let's say you have a 1MB long string. After doing
 * $in = str_repeat('a', 1024 * 1024); PHP's memory utilization jumps up by ~1MB. After doing
 * $blocks = str_split($in, 4); it jumps up by an additional ~16MB. After
 * $blocks = array_map(fn($x) => unpack('N*', $x), $blocks); it jumps up by an additional
 * ~90MB, yielding a 106x increase in memory usage. Consequently, it bcrypt calls a different
 * _encryptBlock() then the regular Blowfish does. That said, the Blowfish _encryptBlock() is
 * basically just a thin wrapper around the bcrypt _encryptBlock(), so there's that.
 *
 * # phpseclib's three different _setupKey() implementations
 *
 * Every bcrypt round is the equivalent of encrypting 512KB of data. Since OpenSSH uses 16
 * rounds by default that's ~8MB of data that's essentially being encrypted whenever
 * you use bcrypt. That's a lot of data, however, bcrypt operates within tighter constraints
 * than regular Blowfish, so we can use that to our advantage. In particular, whereas Blowfish
 * supports variable length keys, in bcrypt, the initial "key" is the sha512 hash of the
 * password. sha512 hashes are 512 bits or 64 bytes long and thus the bcrypt keys are of a
 * fixed length whereas Blowfish keys are not of a fixed length.
 *
 * bcrypt actually has two different key expansion steps. The first one (expandstate) is
 * constantly XOR'ing every _encryptBlock() parameter against the salt prior _encryptBlock()'s
 * being called. The second one (expand0state) is more similar to Blowfish's _setupKey()
 * but it can still use the fixed length key optimization discussed above and can do away with
 * the pack() / unpack() calls.
 *
 * I suppose _setupKey() could be made to be a thin wrapper around expandstate() but idk it's
 * just a lot of work for very marginal benefits as _setupKey() is only called once for
 * regular Blowfish vs the 128 times it's called --per round-- with bcrypt.
 *
 * # blowfish + bcrypt in the same class
 *
 * Altho there's a lot of Blowfish code that bcrypt doesn't re-use, bcrypt does re-use the
 * initial S-boxes, the initial P-array and the int-only _encryptBlock() implementation.
 *
 * # Credit
 *
 * phpseclib's bcrypt implementation is based losely off of OpenSSH's implementation:
 *
 * https://github.com/openssh/openssh-portable/blob/master/openbsd-compat/bcrypt_pbkdf.c
 *
 * Here's a short example of how to use this library:
 * <code>
 * <?php
 *    include 'vendor/autoload.php';
 *
 *    $blowfish = new \phpseclib4\Crypt\Blowfish('ctr');
 *
 *    $blowfish->setKey('12345678901234567890123456789012');
 *
 *    $plaintext = str_repeat('a', 1024);
 *
 *    echo $blowfish->decrypt($blowfish->encrypt($plaintext));
 * ?>
 * </code>
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @author    Hans-Juergen Petrich <petrich@tronic-media.com>
 * @copyright 2013-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt;

use phpseclib4\Crypt\Common\BlockCipher;
use phpseclib4\Exception\{BadConfigurationException, LengthException};

/**
 * Pure-PHP implementation of Blowfish.
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @author  Hans-Juergen Petrich <petrich@tronic-media.com>
 */
class Blowfish extends BlockCipher
{
    /**
     * Block Length of the cipher
     *
     * @see Common\SymmetricKey::block_size
     */
    protected int $block_size = 8;

    /**
     * The fixed subkeys boxes ($sbox0 - $sbox3) with 256 entries each
     *
     * S-Box
     *
     * @var int[]
     */
    private static array $sbox = [
        0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99,
        0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e,
        0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
        0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e,
        0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440,
        0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
        0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677,
        0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032,
        0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
        0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0,
        0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98,
        0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
        0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d,
        0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7,
        0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
        0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09,
        0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb,
        0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
        0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82,
        0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573,
        0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
        0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8,
        0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0,
        0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
        0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1,
        0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9,
        0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
        0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af,
        0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5,
        0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
        0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915,
        0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a,

        0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266,
        0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e,
        0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
        0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1,
        0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8,
        0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
        0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7,
        0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331,
        0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
        0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87,
        0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2,
        0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
        0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509,
        0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3,
        0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
        0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960,
        0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28,
        0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
        0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf,
        0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e,
        0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
        0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281,
        0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696,
        0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
        0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0,
        0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250,
        0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
        0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061,
        0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e,
        0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
        0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340,
        0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7,

        0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068,
        0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840,
        0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
        0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb,
        0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6,
        0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
        0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb,
        0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b,
        0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
        0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc,
        0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564,
        0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
        0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728,
        0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e,
        0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
        0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b,
        0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb,
        0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
        0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9,
        0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe,
        0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
        0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61,
        0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9,
        0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
        0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633,
        0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169,
        0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
        0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62,
        0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76,
        0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
        0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c,
        0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0,

        0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe,
        0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4,
        0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
        0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22,
        0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6,
        0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
        0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51,
        0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c,
        0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
        0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd,
        0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319,
        0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
        0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32,
        0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166,
        0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
        0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47,
        0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d,
        0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
        0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd,
        0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7,
        0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
        0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525,
        0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442,
        0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
        0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d,
        0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299,
        0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
        0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a,
        0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b,
        0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
        0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9,
        0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6,
    ];

    /**
     * P-Array consists of 18 32-bit subkeys
     *
     * @var int[]
     */
    private static array $parray = [
        0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0,
        0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
        0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9, 0x8979fb1b,
    ];

    /**
     * The BCTX-working Array
     *
     * Holds the expanded key [p] and the key-depended s-boxes [sb]
     *
     * @var array{p: int[], sb: int[]}
     */
    private array $bctx;

    /**
     * Holds the last used key
     *
     * @var array{key: ?string}
     */
    private array $kl;

    /**
     * The Key Length (in bytes)
     * {@internal The max value is 256 / 8 = 32, the min value is 128 / 8 = 16.  Exists in conjunction with $Nk
     *    because the encryption / decryption / key schedule creation requires this number and not $key_length.  We could
     *    derive this from $key_length or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu
     *    of that, we'll just precompute it once.}
     *
     * @see Common\SymmetricKey::setKeyLength()
     */
    protected int $key_length = 16;

    /**
     * Sets the key length.
     *
     * Key lengths can be between 32 and 448 bits.
     */
    public function setKeyLength(int $length): void
    {
        if ($length < 32 || $length > 448) {
            throw new LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys of sizes between 32 and 448 bits are supported');
        }

        $this->key_length = $length >> 3;

        parent::setKeyLength($length);
    }

    /**
     * Test for engine validity
     *
     * This is mainly just a wrapper to set things up for \phpseclib4\Crypt\Common\SymmetricKey::isValidEngine()
     *
     * @see Common\SymmetricKey::isValidEngine()
     */
    protected function isValidEngineHelper(int $engine): bool
    {
        if ($engine == self::ENGINE_OPENSSL) {
            if ($this->key_length < 16) {
                return false;
            }
            // quoting https://www.openssl.org/news/openssl-3.0-notes.html, OpenSSL 3.0.1
            // "Moved all variations of the EVP ciphers CAST5, BF, IDEA, SEED, RC2, RC4, RC5, and DES to the legacy provider"
            // in theory openssl_get_cipher_methods() should catch this but, on GitHub Actions, at least, it does not
            if (defined('OPENSSL_VERSION_NUMBER') && OPENSSL_VERSION_NUMBER >= 0x30000010) {
                return false;
            }
            $this->cipher_name_openssl_ecb = 'bf-ecb';
            $this->cipher_name_openssl = 'bf-' . $this->openssl_translate_mode();
        }

        return parent::isValidEngineHelper($engine);
    }

    /**
     * Setup the key (expansion)
     *
     * @see Common\SymmetricKey::_setupKey()
     */
    protected function setupKey(): void
    {
        if (isset($this->kl['key']) && $this->key === $this->kl['key']) {
            // already expanded
            return;
        }
        $this->kl = ['key' => $this->key];

        /* key-expanding p[] and S-Box building sb[] */
        $this->bctx = [
            'p'  => [],
            'sb' => self::$sbox,
        ];

        // unpack binary string in unsigned chars
        $key  = array_values(unpack('C*', $this->key));
        $keyl = count($key);
        // with bcrypt $keyl will always be 16 (because the key is the sha512 of the key you provide)
        for ($j = 0, $i = 0; $i < 18; ++$i) {
            // xor P1 with the first 32-bits of the key, xor P2 with the second 32-bits ...
            for ($data = 0, $k = 0; $k < 4; ++$k) {
                $data = ($data << 8) | $key[$j];
                $j = ($j + 1) % $keyl;
            }
            $this->bctx['p'][] = self::$parray[$i] ^ intval($data);
        }

        // encrypt the zero-string, replace P1 and P2 with the encrypted data,
        // encrypt P3 and P4 with the new P1 and P2, do it with all P-array and subkeys
        $data = "\0\0\0\0\0\0\0\0";
        for ($i = 0; $i < 18; $i += 2) {
            [$l, $r] = array_values(unpack('N*', $data = $this->encryptBlock($data)));
            $this->bctx['p'][$i    ] = $l;
            $this->bctx['p'][$i + 1] = $r;
        }
        for ($i = 0; $i < 0x400; $i += 0x100) {
            for ($j = 0; $j < 256; $j += 2) {
                [$l, $r] = array_values(unpack('N*', $data = $this->encryptBlock($data)));
                $this->bctx['sb'][$i | $j] = $l;
                $this->bctx['sb'][$i | ($j + 1)] = $r;
            }
        }
    }

    /**
     * Initialize Static Variables
     */
    protected static function initialize_static_variables(): void
    {
        if (is_float(self::$sbox[0x200])) {
            self::$sbox = array_map(self::safe_intval(...), self::$sbox);
            self::$parray = array_map(self::safe_intval(...), self::$parray);
        }

        parent::initialize_static_variables();
    }

    /**
     * bcrypt
     */
    private static function bcrypt_hash(string $sha2pass, string $sha2salt): string
    {
        $p = self::$parray;
        $sbox = self::$sbox;

        $cdata = array_values(unpack('N*', 'OxychromaticBlowfishSwatDynamite'));
        $sha2pass = array_values(unpack('N*', $sha2pass));
        $sha2salt = array_values(unpack('N*', $sha2salt));

        self::expandstate($sha2salt, $sha2pass, $sbox, $p);
        for ($i = 0; $i < 64; $i++) {
            self::expand0state($sha2salt, $sbox, $p);
            self::expand0state($sha2pass, $sbox, $p);
        }

        for ($i = 0; $i < 64; $i++) {
            for ($j = 0; $j < 8; $j += 2) { // count($cdata) == 8
                [$cdata[$j], $cdata[$j + 1]] = self::encryptBlockHelperFast($cdata[$j], $cdata[$j + 1], $sbox, $p);
            }
        }

        return pack('V*', ...$cdata);
    }

    /**
     * Performs OpenSSH-style bcrypt
     */
    public static function bcrypt_pbkdf(
        #[\SensitiveParameter] string $pass,
        string $salt,
        int $keylen,
        int $rounds
    ): string {
        self::initialize_static_variables();

        if (PHP_INT_SIZE == 4) {
            throw new BadConfigurationException('bcrypt is far too slow to be practical on 32-bit versions of PHP');
        }

        $sha2pass = hash('sha512', $pass, true);
        $results = [];
        $count = 1;
        while (32 * count($results) < $keylen) {
            $countsalt = $salt . pack('N', $count++);
            $sha2salt = hash('sha512', $countsalt, true);
            $out = $tmpout = self::bcrypt_hash($sha2pass, $sha2salt);
            for ($i = 1; $i < $rounds; $i++) {
                $sha2salt = hash('sha512', $tmpout, true);
                $tmpout = self::bcrypt_hash($sha2pass, $sha2salt);
                $out ^= $tmpout;
            }
            $results[] = $out;
        }
        $output = '';
        for ($i = 0; $i < 32; $i++) {
            foreach ($results as $result) {
                $output .= $result[$i];
            }
        }
        return substr($output, 0, $keylen);
    }

    /**
     * Key expansion without salt
     *
     * @param int[] $key
     * @param int[] $sbox
     * @param int[] $p
     * @see self::_bcrypt_hash()
     */
    private static function expand0state(array $key, array &$sbox, array &$p): void
    {
        // expand0state is basically the same thing as this:
        //return self::expandstate(array_fill(0, 16, 0), $key);
        // but this separate function eliminates a bunch of XORs and array lookups

        $p = [
            $p[0] ^ $key[0],
            $p[1] ^ $key[1],
            $p[2] ^ $key[2],
            $p[3] ^ $key[3],
            $p[4] ^ $key[4],
            $p[5] ^ $key[5],
            $p[6] ^ $key[6],
            $p[7] ^ $key[7],
            $p[8] ^ $key[8],
            $p[9] ^ $key[9],
            $p[10] ^ $key[10],
            $p[11] ^ $key[11],
            $p[12] ^ $key[12],
            $p[13] ^ $key[13],
            $p[14] ^ $key[14],
            $p[15] ^ $key[15],
            $p[16] ^ $key[0],
            $p[17] ^ $key[1],
        ];

        [ $p[0],  $p[1]] = self::encryptBlockHelperFast(0, 0, $sbox, $p);
        [ $p[2],  $p[3]] = self::encryptBlockHelperFast($p[ 0], $p[ 1], $sbox, $p);
        [ $p[4],  $p[5]] = self::encryptBlockHelperFast($p[ 2], $p[ 3], $sbox, $p);
        [ $p[6],  $p[7]] = self::encryptBlockHelperFast($p[ 4], $p[ 5], $sbox, $p);
        [ $p[8],  $p[9]] = self::encryptBlockHelperFast($p[ 6], $p[ 7], $sbox, $p);
        [$p[10], $p[11]] = self::encryptBlockHelperFast($p[ 8], $p[ 9], $sbox, $p);
        [$p[12], $p[13]] = self::encryptBlockHelperFast($p[10], $p[11], $sbox, $p);
        [$p[14], $p[15]] = self::encryptBlockHelperFast($p[12], $p[13], $sbox, $p);
        [$p[16], $p[17]] = self::encryptBlockHelperFast($p[14], $p[15], $sbox, $p);

        [$sbox[0], $sbox[1]] = self::encryptBlockHelperFast($p[16], $p[17], $sbox, $p);
        for ($i = 2; $i < 1024; $i += 2) {
            [$sbox[$i], $sbox[$i + 1]] = self::encryptBlockHelperFast($sbox[$i - 2], $sbox[$i - 1], $sbox, $p);
        }
    }

    /**
     * Key expansion with salt
     *
     * @param int[] $data
     * @param int[] $key
     * @param int[] $sbox
     * @param int[] $p
     * @see self::_bcrypt_hash()
     */
    private static function expandstate(array $data, array $key, array &$sbox, array &$p): void
    {
        $p = [
            $p[0] ^ $key[0],
            $p[1] ^ $key[1],
            $p[2] ^ $key[2],
            $p[3] ^ $key[3],
            $p[4] ^ $key[4],
            $p[5] ^ $key[5],
            $p[6] ^ $key[6],
            $p[7] ^ $key[7],
            $p[8] ^ $key[8],
            $p[9] ^ $key[9],
            $p[10] ^ $key[10],
            $p[11] ^ $key[11],
            $p[12] ^ $key[12],
            $p[13] ^ $key[13],
            $p[14] ^ $key[14],
            $p[15] ^ $key[15],
            $p[16] ^ $key[0],
            $p[17] ^ $key[1],
        ];

        [ $p[0],  $p[1]] = self::encryptBlockHelperFast($data[0], $data[ 1], $sbox, $p);
        [ $p[2],  $p[3]] = self::encryptBlockHelperFast($data[ 2] ^ $p[ 0], $data[ 3] ^ $p[ 1], $sbox, $p);
        [ $p[4],  $p[5]] = self::encryptBlockHelperFast($data[ 4] ^ $p[ 2], $data[ 5] ^ $p[ 3], $sbox, $p);
        [ $p[6],  $p[7]] = self::encryptBlockHelperFast($data[ 6] ^ $p[ 4], $data[ 7] ^ $p[ 5], $sbox, $p);
        [ $p[8],  $p[9]] = self::encryptBlockHelperFast($data[ 8] ^ $p[ 6], $data[ 9] ^ $p[ 7], $sbox, $p);
        [$p[10], $p[11]] = self::encryptBlockHelperFast($data[10] ^ $p[ 8], $data[11] ^ $p[ 9], $sbox, $p);
        [$p[12], $p[13]] = self::encryptBlockHelperFast($data[12] ^ $p[10], $data[13] ^ $p[11], $sbox, $p);
        [$p[14], $p[15]] = self::encryptBlockHelperFast($data[14] ^ $p[12], $data[15] ^ $p[13], $sbox, $p);
        [$p[16], $p[17]] = self::encryptBlockHelperFast($data[ 0] ^ $p[14], $data[ 1] ^ $p[15], $sbox, $p);

        [$sbox[0], $sbox[1]] = self::encryptBlockHelperFast($data[2] ^ $p[16], $data[3] ^ $p[17], $sbox, $p);
        for ($i = 2, $j = 4; $i < 1024; $i += 2, $j = ($j + 2) % 16) { // instead of 16 maybe count($data) would be better?
            [$sbox[$i], $sbox[$i + 1]] = self::encryptBlockHelperFast($data[$j] ^ $sbox[$i - 2], $data[$j + 1] ^ $sbox[$i - 1], $sbox, $p);
        }
    }

    /**
     * Encrypts a block
     */
    protected function encryptBlock(string $in): string
    {
        $p = $this->bctx['p'];
        // extract($this->bctx['sb'], EXTR_PREFIX_ALL, 'sb'); // slower
        $sb = $this->bctx['sb'];

        $in = unpack('N*', $in);
        $l = $in[1];
        $r = $in[2];

        [$r, $l] = PHP_INT_SIZE == 4 ?
            self::encryptBlockHelperSlow($l, $r, $sb, $p) :
            self::encryptBlockHelperFast($l, $r, $sb, $p);

        return pack("N*", $r, $l);
    }

    /**
     * Fast helper function for block encryption
     *
     * @access private
     * @param int[] $sbox
     * @param int[] $p
     * @return int[]
     */
    private static function encryptBlockHelperFast(int $x0, int $x1, array $sbox, array $p): array
    {
        $x0 ^= $p[0];
        $x1 ^= ((($sbox[($x0 & 0xFF000000) >> 24] + $sbox[0x100 | (($x0 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x0 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x0 & 0xFF)]) ^ $p[1];
        $x0 ^= ((($sbox[($x1 & 0xFF000000) >> 24] + $sbox[0x100 | (($x1 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x1 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x1 & 0xFF)]) ^ $p[2];
        $x1 ^= ((($sbox[($x0 & 0xFF000000) >> 24] + $sbox[0x100 | (($x0 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x0 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x0 & 0xFF)]) ^ $p[3];
        $x0 ^= ((($sbox[($x1 & 0xFF000000) >> 24] + $sbox[0x100 | (($x1 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x1 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x1 & 0xFF)]) ^ $p[4];
        $x1 ^= ((($sbox[($x0 & 0xFF000000) >> 24] + $sbox[0x100 | (($x0 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x0 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x0 & 0xFF)]) ^ $p[5];
        $x0 ^= ((($sbox[($x1 & 0xFF000000) >> 24] + $sbox[0x100 | (($x1 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x1 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x1 & 0xFF)]) ^ $p[6];
        $x1 ^= ((($sbox[($x0 & 0xFF000000) >> 24] + $sbox[0x100 | (($x0 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x0 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x0 & 0xFF)]) ^ $p[7];
        $x0 ^= ((($sbox[($x1 & 0xFF000000) >> 24] + $sbox[0x100 | (($x1 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x1 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x1 & 0xFF)]) ^ $p[8];
        $x1 ^= ((($sbox[($x0 & 0xFF000000) >> 24] + $sbox[0x100 | (($x0 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x0 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x0 & 0xFF)]) ^ $p[9];
        $x0 ^= ((($sbox[($x1 & 0xFF000000) >> 24] + $sbox[0x100 | (($x1 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x1 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x1 & 0xFF)]) ^ $p[10];
        $x1 ^= ((($sbox[($x0 & 0xFF000000) >> 24] + $sbox[0x100 | (($x0 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x0 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x0 & 0xFF)]) ^ $p[11];
        $x0 ^= ((($sbox[($x1 & 0xFF000000) >> 24] + $sbox[0x100 | (($x1 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x1 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x1 & 0xFF)]) ^ $p[12];
        $x1 ^= ((($sbox[($x0 & 0xFF000000) >> 24] + $sbox[0x100 | (($x0 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x0 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x0 & 0xFF)]) ^ $p[13];
        $x0 ^= ((($sbox[($x1 & 0xFF000000) >> 24] + $sbox[0x100 | (($x1 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x1 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x1 & 0xFF)]) ^ $p[14];
        $x1 ^= ((($sbox[($x0 & 0xFF000000) >> 24] + $sbox[0x100 | (($x0 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x0 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x0 & 0xFF)]) ^ $p[15];
        $x0 ^= ((($sbox[($x1 & 0xFF000000) >> 24] + $sbox[0x100 | (($x1 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x1 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x1 & 0xFF)]) ^ $p[16];

        return [$x1 & 0xFFFFFFFF ^ $p[17], $x0 & 0xFFFFFFFF];
    }

    /**
     * Slow helper function for block encryption
     *
     * @param int[] $sbox
     * @param int[] $p
     * @return int[]
     */
    private static function encryptBlockHelperSlow(int $x0, int $x1, array $sbox, array $p): array
    {
        // -16777216 == intval(0xFF000000) on 32-bit PHP installs
        $x0 ^= $p[0];
        $x1 ^= self::safe_intval((self::safe_intval($sbox[(($x0 & -16777216) >> 24) & 0xFF] + $sbox[0x100 | (($x0 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x0 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x0 & 0xFF)]) ^ $p[1];
        $x0 ^= self::safe_intval((self::safe_intval($sbox[(($x1 & -16777216) >> 24) & 0xFF] + $sbox[0x100 | (($x1 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x1 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x1 & 0xFF)]) ^ $p[2];
        $x1 ^= self::safe_intval((self::safe_intval($sbox[(($x0 & -16777216) >> 24) & 0xFF] + $sbox[0x100 | (($x0 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x0 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x0 & 0xFF)]) ^ $p[3];
        $x0 ^= self::safe_intval((self::safe_intval($sbox[(($x1 & -16777216) >> 24) & 0xFF] + $sbox[0x100 | (($x1 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x1 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x1 & 0xFF)]) ^ $p[4];
        $x1 ^= self::safe_intval((self::safe_intval($sbox[(($x0 & -16777216) >> 24) & 0xFF] + $sbox[0x100 | (($x0 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x0 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x0 & 0xFF)]) ^ $p[5];
        $x0 ^= self::safe_intval((self::safe_intval($sbox[(($x1 & -16777216) >> 24) & 0xFF] + $sbox[0x100 | (($x1 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x1 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x1 & 0xFF)]) ^ $p[6];
        $x1 ^= self::safe_intval((self::safe_intval($sbox[(($x0 & -16777216) >> 24) & 0xFF] + $sbox[0x100 | (($x0 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x0 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x0 & 0xFF)]) ^ $p[7];
        $x0 ^= self::safe_intval((self::safe_intval($sbox[(($x1 & -16777216) >> 24) & 0xFF] + $sbox[0x100 | (($x1 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x1 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x1 & 0xFF)]) ^ $p[8];
        $x1 ^= self::safe_intval((self::safe_intval($sbox[(($x0 & -16777216) >> 24) & 0xFF] + $sbox[0x100 | (($x0 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x0 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x0 & 0xFF)]) ^ $p[9];
        $x0 ^= self::safe_intval((self::safe_intval($sbox[(($x1 & -16777216) >> 24) & 0xFF] + $sbox[0x100 | (($x1 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x1 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x1 & 0xFF)]) ^ $p[10];
        $x1 ^= self::safe_intval((self::safe_intval($sbox[(($x0 & -16777216) >> 24) & 0xFF] + $sbox[0x100 | (($x0 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x0 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x0 & 0xFF)]) ^ $p[11];
        $x0 ^= self::safe_intval((self::safe_intval($sbox[(($x1 & -16777216) >> 24) & 0xFF] + $sbox[0x100 | (($x1 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x1 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x1 & 0xFF)]) ^ $p[12];
        $x1 ^= self::safe_intval((self::safe_intval($sbox[(($x0 & -16777216) >> 24) & 0xFF] + $sbox[0x100 | (($x0 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x0 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x0 & 0xFF)]) ^ $p[13];
        $x0 ^= self::safe_intval((self::safe_intval($sbox[(($x1 & -16777216) >> 24) & 0xFF] + $sbox[0x100 | (($x1 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x1 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x1 & 0xFF)]) ^ $p[14];
        $x1 ^= self::safe_intval((self::safe_intval($sbox[(($x0 & -16777216) >> 24) & 0xFF] + $sbox[0x100 | (($x0 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x0 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x0 & 0xFF)]) ^ $p[15];
        $x0 ^= self::safe_intval((self::safe_intval($sbox[(($x1 & -16777216) >> 24) & 0xFF] + $sbox[0x100 | (($x1 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x1 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x1 & 0xFF)]) ^ $p[16];

        return [$x1 ^ $p[17], $x0];
    }

    /**
     * Decrypts a block
     */
    protected function decryptBlock(string $in): string
    {
        $p = $this->bctx['p'];
        $sb = $this->bctx['sb'];

        $in = unpack('N*', $in);
        $l = $in[1];
        $r = $in[2];

        for ($i = 17; $i > 2; $i -= 2) {
            $l ^= $p[$i];
            $r ^= self::safe_intval((self::safe_intval($sb[$l >> 24 & 0xff] + $sb[0x100 + ($l >> 16 & 0xff)]) ^
                  $sb[0x200 + ($l >>  8 & 0xff)]) +
                  $sb[0x300 + ($l       & 0xff)]);

            $r ^= $p[$i - 1];
            $l ^= self::safe_intval((self::safe_intval($sb[$r >> 24 & 0xff] + $sb[0x100 + ($r >> 16 & 0xff)]) ^
                  $sb[0x200 + ($r >>  8 & 0xff)]) +
                  $sb[0x300 + ($r       & 0xff)]);
        }
        return pack('N*', $r ^ $p[0], $l ^ $p[1]);
    }

    /**
     * Setup the performance-optimized function for de/encrypt()
     *
     * @see Common\SymmetricKey::setup()
     * @psalm-suppress PossiblyUnusedMethod
     */
    protected function setupInlineCrypt(): void
    {
        $p = $this->bctx['p'];
        $init_crypt = '
            static $sb;
            if (!$sb) {
                $sb = $this->bctx["sb"];
            }
        ';

        $safeint = self::safe_intval_inline();

        // Generating encrypt code:
        $encrypt_block = '
            $in = unpack("N*", $in);
            $l = $in[1];
            $r = $in[2];
        ';
        for ($i = 0; $i < 16; $i += 2) {
            $encrypt_block .= '
                $l^= ' . $p[$i] . ';
                $r^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb[$l >> 24 & 0xff] + $sb[0x100 + ($l >> 16 & 0xff)]') . ' ^
                      $sb[0x200 + ($l >>  8 & 0xff)]) +
                      $sb[0x300 + ($l       & 0xff)]') . ';

                $r^= ' . $p[$i + 1] . ';
                $l^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb[$r >> 24 & 0xff] + $sb[0x100 + ($r >> 16 & 0xff)]') . '  ^
                      $sb[0x200 + ($r >>  8 & 0xff)]) +
                      $sb[0x300 + ($r       & 0xff)]') . ';
            ';
        }
        $encrypt_block .= '
            $in = pack("N*",
                $r ^ ' . $p[17] . ',
                $l ^ ' . $p[16] . '
            );
        ';

        // Generating decrypt code:
        $decrypt_block = '
            $in = unpack("N*", $in);
            $l = $in[1];
            $r = $in[2];
        ';

        for ($i = 17; $i > 2; $i -= 2) {
            $decrypt_block .= '
                $l^= ' . $p[$i] . ';
                $r^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb[$l >> 24 & 0xff] + $sb[0x100 + ($l >> 16 & 0xff)]') . ' ^
                      $sb[0x200 + ($l >>  8 & 0xff)]) +
                      $sb[0x300 + ($l       & 0xff)]') . ';

                $r^= ' . $p[$i - 1] . ';
                $l^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb[$r >> 24 & 0xff] + $sb[0x100 + ($r >> 16 & 0xff)]') . ' ^
                      $sb[0x200 + ($r >>  8 & 0xff)]) +
                      $sb[0x300 + ($r       & 0xff)]') . ';
            ';
        }

        $decrypt_block .= '
            $in = pack("N*",
                $r ^ ' . $p[0] . ',
                $l ^ ' . $p[1] . '
            );
        ';

        $this->inline_crypt = $this->createInlineCryptFunction(
            [
               'init_crypt'    => $init_crypt,
               'init_encrypt'  => '',
               'init_decrypt'  => '',
               'encrypt_block' => $encrypt_block,
               'decrypt_block' => $decrypt_block,
            ]
        );
    }
}
<?php

/**
 * Pure-PHP implementation of Rijndael.
 *
 * Uses OpenSSL, if available/possible, and an internal implementation, otherwise
 *
 * PHP version 8.1+
 *
 * If {@link self::setBlockLength() setBlockLength()} isn't called, it'll be assumed to be 128 bits.  If
 * {@link self::setKeyLength() setKeyLength()} isn't called, it'll be calculated from
 * {@link self::setKey() setKey()}.  ie. if the key is 128-bits, the key length will be 128-bits.  If it's
 * 136-bits it'll be null-padded to 192-bits and 192 bits will be the key length until
 * {@link self::setKey() setKey()} is called, again, at which point, it'll be recalculated.
 *
 * Not all Rijndael implementations may support 160-bits or 224-bits as the block length / key length. AES, itself, only
 * supports block lengths of 128 and key lengths of 128, 192, and 256.
 * {@link http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=10 Rijndael-ammended.pdf#page=10} defines the
 * algorithm for block lengths of 192 and 256 but not for block lengths / key lengths of 160 and 224.  Indeed, 160 and 224
 * are first defined as valid key / block lengths in
 * {@link http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=44 Rijndael-ammended.pdf#page=44}:
 * Extensions: Other block and Cipher Key lengths.
 * Note: Use of 160/224-bit Keys must be explicitly set by setKeyLength(160) respectively setKeyLength(224).
 *
 * {@internal The variable names are the same as those in
 * {@link http://www.csrc.nist.gov/publications/fips/fips197/fips-197.pdf#page=10 fips-197.pdf#page=10}.}}
 *
 * Here's a short example of how to use this library:
 * <code>
 * <?php
 *    include 'vendor/autoload.php';
 *
 *    $rijndael = new \phpseclib4\Crypt\Rijndael('ctr');
 *
 *    $rijndael->setKey('abcdefghijklmnop');
 *
 *    $size = 10 * 1024;
 *    $plaintext = '';
 *    for ($i = 0; $i < $size; $i++) {
 *        $plaintext.= 'a';
 *    }
 *
 *    echo $rijndael->decrypt($rijndael->encrypt($plaintext));
 * ?>
 * </code>
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2009-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt;

use phpseclib4\Common\Functions\Strings;
use phpseclib4\Crypt\Common\BlockCipher;
use phpseclib4\Exception\{BadDecryptionException, InvalidStateException, LengthException};

/**
 * Pure-PHP implementation of Rijndael.
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class Rijndael extends BlockCipher
{
    /**
     * The Key Schedule
     *
     * @see self::setup()
     */
    private array $w;

    /**
     * The Inverse Key Schedule
     *
     * @see self::setup()
     */
    private array $dw;

    /**
     * The Block Length divided by 32
     *
     * {@internal The max value is 256 / 32 = 8, the min value is 128 / 32 = 4.  Exists in conjunction with $block_size
     *    because the encryption / decryption / key schedule creation requires this number and not $block_size.  We could
     *    derive this from $block_size or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu
     *    of that, we'll just precompute it once.}
     *
     * @see self::setBlockLength()
     */
    private int $Nb = 4;

    /**
     * The Key Length (in bytes)
     *
     * {@internal The max value is 256 / 8 = 32, the min value is 128 / 8 = 16.  Exists in conjunction with $Nk
     *    because the encryption / decryption / key schedule creation requires this number and not $key_length.  We could
     *    derive this from $key_length or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu
     *    of that, we'll just precompute it once.}
     *
     * @see self::setKeyLength()
     */
    protected int $key_length = 16;

    /**
     * The Key Length divided by 32
     *
     * @see self::setKeyLength()
     * @internal The max value is 256 / 32 = 8, the min value is 128 / 32 = 4
     */
    private int $Nk = 4;

    /**
     * The Number of Rounds
     *
     * {@internal The max value is 14, the min value is 10.}
     */
    private int $Nr;

    /**
     * Shift offsets
     */
    private array $c;

    /**
     * Holds the last used key- and block_size information
     */
    private array $kl;

    /**
     * Sets the key length.
     *
     * Valid key lengths are 128, 160, 192, 224, and 256.
     *
     * Note: phpseclib extends Rijndael (and AES) for using 160- and 224-bit keys but they are officially not defined
     *       and the most (if not all) implementations are not able using 160/224-bit keys but round/pad them up to
     *       192/256 bits as.
     *
     *       That said, if you want be compatible with other Rijndael and AES implementations,
     *       you should not setKeyLength(160) or setKeyLength(224).
     */
    public function setKeyLength(int $length): void
    {
        switch ($length) {
            case 128:
            case 160:
            case 192:
            case 224:
            case 256:
                $this->key_length = $length >> 3;
                break;
            default:
                throw new LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys of sizes 128, 160, 192, 224 or 256 bits are supported');
        }

        parent::setKeyLength($length);
    }

    /**
     * Sets the key.
     *
     * Rijndael supports five different key lengths
     *
     * @throws LengthException if the key length isn't supported
     * @see setKeyLength()
     */
    public function setKey(#[\SensitiveParameter] string $key): void
    {
        switch (strlen($key)) {
            case 16:
            case 20:
            case 24:
            case 28:
            case 32:
                break;
            default:
                throw new LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of sizes 16, 20, 24, 28 or 32 are supported');
        }

        parent::setKey($key);
    }

    /**
     * Sets the block length
     *
     * Valid block lengths are 128, 160, 192, 224, and 256.
     */
    public function setBlockLength(int $length): void
    {
        switch ($length) {
            case 128:
            case 160:
            case 192:
            case 224:
            case 256:
                break;
            default:
                throw new LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys of sizes 128, 160, 192, 224 or 256 bits are supported');
        }

        $this->Nb = $length >> 5;
        $this->block_size = $length >> 3;
        $this->changed = $this->nonIVChanged = true;
        $this->setEngine();
    }

    /**
     * Test for engine validity
     *
     * This is mainly just a wrapper to set things up for \phpseclib4\Crypt\Common\SymmetricKey::isValidEngine()
     *
     * @see \phpseclib4\Crypt\Common\SymmetricKey::__construct()
     */
    protected function isValidEngineHelper(int $engine): bool
    {
        switch ($engine) {
            case self::ENGINE_LIBSODIUM:
                return function_exists('sodium_crypto_aead_aes256gcm_is_available') &&
                       sodium_crypto_aead_aes256gcm_is_available() &&
                       $this->mode == self::MODE_GCM &&
                       $this->key_length == 32 &&
                       $this->nonce && strlen($this->nonce) == 12 &&
                       $this->block_size == 16;
            case self::ENGINE_OPENSSL_AEAD:
                if (!extension_loaded('openssl')) {
                    return false;
                }
                $methods = openssl_get_cipher_methods();
                return $this->mode == self::MODE_GCM &&
                       version_compare(PHP_VERSION, '7.1.0', '>=') &&
                       in_array('aes-' . $this->getKeyLength() . '-gcm', $methods) &&
                       $this->block_size == 16;
            case self::ENGINE_OPENSSL:
                if ($this->block_size != 16) {
                    return false;
                }
                $this->cipher_name_openssl_ecb = 'aes-' . ($this->key_length << 3) . '-ecb';
                $this->cipher_name_openssl = 'aes-' . ($this->key_length << 3) . '-' . $this->openssl_translate_mode();
                break;
        }

        return parent::isValidEngineHelper($engine);
    }

    /**
     * Encrypts a block
     */
    protected function encryptBlock(string $in): string
    {
        static $tables;
        if (empty($tables)) {
            $tables = &$this->getTables();
        }
        $t0   = $tables[0];
        $t1   = $tables[1];
        $t2   = $tables[2];
        $t3   = $tables[3];
        $sbox = $tables[4];

        $state = [];
        $words = unpack('N*', $in);

        $c = $this->c;
        $w = $this->w;
        $Nb = $this->Nb;
        $Nr = $this->Nr;

        // addRoundKey
        $wc = $Nb - 1;
        foreach ($words as $word) {
            $state[] = $word ^ $w[++$wc];
        }

        // fips-197.pdf#page=19, "Figure 5. Pseudo Code for the Cipher", states that this loop has four components -
        // subBytes, shiftRows, mixColumns, and addRoundKey. fips-197.pdf#page=30, "Implementation Suggestions Regarding
        // Various Platforms" suggests that performs enhanced implementations are described in Rijndael-ammended.pdf.
        // Rijndael-ammended.pdf#page=20, "Implementation aspects / 32-bit processor", discusses such an optimization.
        // Unfortunately, the description given there is not quite correct.  Per aes.spec.v316.pdf#page=19 [1],
        // equation (7.4.7) is supposed to use addition instead of subtraction, so we'll do that here, as well.

        // [1] http://fp.gladman.plus.com/cryptography_technology/rijndael/aes.spec.v316.pdf
        $temp = [];
        for ($round = 1; $round < $Nr; ++$round) {
            $i = 0; // $c[0] == 0
            $j = $c[1];
            $k = $c[2];
            $l = $c[3];

            while ($i < $Nb) {
                $temp[$i] = $t0[$state[$i] >> 24 & 0x000000FF] ^
                            $t1[$state[$j] >> 16 & 0x000000FF] ^
                            $t2[$state[$k] >>  8 & 0x000000FF] ^
                            $t3[$state[$l]       & 0x000000FF] ^
                            $w[++$wc];
                ++$i;
                $j = ($j + 1) % $Nb;
                $k = ($k + 1) % $Nb;
                $l = ($l + 1) % $Nb;
            }
            $state = $temp;
        }

        // subWord
        for ($i = 0; $i < $Nb; ++$i) {
            $state[$i] =   $sbox[$state[$i]       & 0x000000FF]        |
                          ($sbox[$state[$i] >>  8 & 0x000000FF] <<  8) |
                          ($sbox[$state[$i] >> 16 & 0x000000FF] << 16) |
                          ($sbox[$state[$i] >> 24 & 0x000000FF] << 24);
        }

        // shiftRows + addRoundKey
        $i = 0; // $c[0] == 0
        $j = $c[1];
        $k = $c[2];
        $l = $c[3];
        while ($i < $Nb) {
            $temp[$i] = ($state[$i] & (PHP_INT_SIZE == 8 ? 0xFF000000 : -16777216)) ^
                        ($state[$j] & 0x00FF0000) ^
                        ($state[$k] & 0x0000FF00) ^
                        ($state[$l] & 0x000000FF) ^
                         $w[$i];
            ++$i;
            $j = ($j + 1) % $Nb;
            $k = ($k + 1) % $Nb;
            $l = ($l + 1) % $Nb;
        }

        return pack('N*', ...$temp);
    }

    /**
     * Decrypts a block
     */
    protected function decryptBlock(string $in): string
    {
        static $invtables;
        if (empty($invtables)) {
            $invtables = &$this->getInvTables();
        }
        $dt0   = $invtables[0];
        $dt1   = $invtables[1];
        $dt2   = $invtables[2];
        $dt3   = $invtables[3];
        $isbox = $invtables[4];

        $state = [];
        $words = unpack('N*', $in);

        $c  = $this->c;
        $dw = $this->dw;
        $Nb = $this->Nb;
        $Nr = $this->Nr;

        // addRoundKey
        $wc = $Nb - 1;
        foreach ($words as $word) {
            $state[] = $word ^ $dw[++$wc];
        }

        $temp = [];
        for ($round = $Nr - 1; $round > 0; --$round) {
            $i = 0; // $c[0] == 0
            $j = $Nb - $c[1];
            $k = $Nb - $c[2];
            $l = $Nb - $c[3];

            while ($i < $Nb) {
                $temp[$i] = $dt0[$state[$i] >> 24 & 0x000000FF] ^
                            $dt1[$state[$j] >> 16 & 0x000000FF] ^
                            $dt2[$state[$k] >>  8 & 0x000000FF] ^
                            $dt3[$state[$l]       & 0x000000FF] ^
                            $dw[++$wc];
                ++$i;
                $j = ($j + 1) % $Nb;
                $k = ($k + 1) % $Nb;
                $l = ($l + 1) % $Nb;
            }
            $state = $temp;
        }

        // invShiftRows + invSubWord + addRoundKey
        $i = 0; // $c[0] == 0
        $j = $Nb - $c[1];
        $k = $Nb - $c[2];
        $l = $Nb - $c[3];

        while ($i < $Nb) {
            $word = ($state[$i] & (PHP_INT_SIZE == 8 ? 0xFF000000 : -16777216)) |
                    ($state[$j] & 0x00FF0000) |
                    ($state[$k] & 0x0000FF00) |
                    ($state[$l] & 0x000000FF);

            $temp[$i] = $dw[$i] ^ ($isbox[$word       & 0x000000FF]        |
                                  ($isbox[$word >>  8 & 0x000000FF] <<  8) |
                                  ($isbox[$word >> 16 & 0x000000FF] << 16) |
                                  ($isbox[$word >> 24 & 0x000000FF] << 24));
            ++$i;
            $j = ($j + 1) % $Nb;
            $k = ($k + 1) % $Nb;
            $l = ($l + 1) % $Nb;
        }

        return pack('N*', ...$temp);
    }

    /**
     * Setup the self::ENGINE_INTERNAL $engine
     *
     * (re)init, if necessary, the internal cipher $engine and flush all $buffers
     * Used (only) if $engine == self::ENGINE_INTERNAL
     *
     * _setup() will be called each time if $changed === true
     * typically this happens when using one or more of following public methods:
     *
     * - setKey()
     *
     * - setIV()
     *
     * - disableContinuousBuffer()
     *
     * - First run of encrypt() / decrypt() with no init-settings
     *
     * {@internal setup() is always called before en/decryption.}
     *
     * {@internal Could, but not must, extend by the child Crypt_* class}
     *
     * @see self::setKey()
     * @see self::setIV()
     * @see self::disableContinuousBuffer()
     */
    protected function setup(): void
    {
        if (!$this->changed) {
            return;
        }

        parent::setup();

        if (is_string($this->iv) && strlen($this->iv) != $this->block_size) {
            throw new LengthException('The IV length (' . strlen($this->iv) . ') does not match the block size (' . $this->block_size . ')');
        }
    }

    /**
     * Setup the key (expansion)
     *
     * @see \phpseclib4\Crypt\Common\SymmetricKey::setupKey()
     */
    protected function setupKey(): void
    {
        // Each number in $rcon is equal to the previous number multiplied by two in Rijndael's finite field.
        // See http://en.wikipedia.org/wiki/Finite_field_arithmetic#Multiplicative_inverse
        static $rcon;

        if (!isset($rcon)) {
            $rcon = [0,
                0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000,
                0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000,
                0x6C000000, 0xD8000000, 0xAB000000, 0x4D000000, 0x9A000000,
                0x2F000000, 0x5E000000, 0xBC000000, 0x63000000, 0xC6000000,
                0x97000000, 0x35000000, 0x6A000000, 0xD4000000, 0xB3000000,
                0x7D000000, 0xFA000000, 0xEF000000, 0xC5000000, 0x91000000,
            ];
            $rcon = array_map(self::safe_intval(...), $rcon);
        }

        if (isset($this->kl['key']) && $this->key === $this->kl['key'] && $this->key_length === $this->kl['key_length'] && $this->block_size === $this->kl['block_size']) {
            // already expanded
            return;
        }
        $this->kl = ['key' => $this->key, 'key_length' => $this->key_length, 'block_size' => $this->block_size];

        $this->Nk = $this->key_length >> 2;
        // see Rijndael-ammended.pdf#page=44
        $this->Nr = max($this->Nk, $this->Nb) + 6;

        // shift offsets for Nb = 5, 7 are defined in Rijndael-ammended.pdf#page=44,
        //     "Table 8: Shift offsets in Shiftrow for the alternative block lengths"
        // shift offsets for Nb = 4, 6, 8 are defined in Rijndael-ammended.pdf#page=14,
        //     "Table 2: Shift offsets for different block lengths"
        $this->c = match ($this->Nb) {
            4, 5, 6 => [0, 1, 2, 3],
            7 => [0, 1, 2, 4],
            8 => [0, 1, 3, 4]
        };

        $w = array_values(unpack('N*words', $this->key));

        $length = $this->Nb * ($this->Nr + 1);
        for ($i = $this->Nk; $i < $length; $i++) {
            $temp = $w[$i - 1];
            if ($i % $this->Nk == 0) {
                // according to <http://php.net/language.types.integer>, "the size of an integer is platform-dependent".
                // on a 32-bit machine, it's 32-bits, and on a 64-bit machine, it's 64-bits. on a 32-bit machine,
                // 0xFFFFFFFF << 8 == 0xFFFFFF00, but on a 64-bit machine, it equals 0xFFFFFFFF00. as such, doing 'and'
                // with 0xFFFFFFFF (or 0xFFFFFF00) on a 32-bit machine is unnecessary, but on a 64-bit machine, it is.
                $temp = PHP_INT_SIZE == 8 ? // rotWord
                    (($temp << 8) & 0xFFFFFF00) | (($temp >> 24) & 0x000000FF) :
                    ($temp << 8) | (($temp >> 24) & 0x000000FF);
                $temp = $this->subWord($temp) ^ $rcon[$i / $this->Nk];
            } elseif ($this->Nk > 6 && $i % $this->Nk == 4) {
                $temp = $this->subWord($temp);
            }
            $w[$i] = $w[$i - $this->Nk] ^ $temp;
        }

        // convert the key schedule from a vector of $Nb * ($Nr + 1) length to a matrix with $Nr + 1 rows and $Nb columns
        // and generate the inverse key schedule.  more specifically,
        // according to <http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=23> (section 5.3.3),
        // "The key expansion for the Inverse Cipher is defined as follows:
        //        1. Apply the Key Expansion.
        //        2. Apply InvMixColumn to all Round Keys except the first and the last one."
        // also, see fips-197.pdf#page=27, "5.3.5 Equivalent Inverse Cipher"
        [$dt0, $dt1, $dt2, $dt3] = $this->getInvTables();
        $temp = $this->w = $this->dw = [];
        for ($i = $row = $col = 0; $i < $length; $i++, $col++) {
            if ($col == $this->Nb) {
                if ($row == 0) {
                    $this->dw[0] = $this->w[0];
                } else {
                    // subWord + invMixColumn + invSubWord = invMixColumn
                    $j = 0;
                    while ($j < $this->Nb) {
                        $dw = $this->subWord($this->w[$row][$j]);
                        $temp[$j] = $dt0[$dw >> 24 & 0x000000FF] ^
                                    $dt1[$dw >> 16 & 0x000000FF] ^
                                    $dt2[$dw >>  8 & 0x000000FF] ^
                                    $dt3[$dw       & 0x000000FF];
                        $j++;
                    }
                    $this->dw[$row] = $temp;
                }

                /** @psalm-suppress LoopInvalidation */
                $col = 0;
                $row++;
            }
            $this->w[$row][$col] = $w[$i];
        }

        $this->dw[$row] = $this->w[$row];

        // Converting to 1-dim key arrays (both ascending)
        $this->dw = array_reverse($this->dw);
        $w  = array_pop($this->w);
        $dw = array_pop($this->dw);
        foreach ($this->w as $r => $wr) {
            foreach ($wr as $c => $wc) {
                $w[]  = $wc;
                $dw[] = $this->dw[$r][$c];
            }
        }
        $this->w  = $w;
        $this->dw = $dw;
    }

    /**
     * Performs S-Box substitutions
     */
    private function subWord(int $word): int
    {
        static $sbox;
        if (empty($sbox)) {
            [, , , , $sbox] = self::getTables();
        }

        return  $sbox[$word       & 0x000000FF]        |
               ($sbox[$word >>  8 & 0x000000FF] <<  8) |
               ($sbox[$word >> 16 & 0x000000FF] << 16) |
               ($sbox[$word >> 24 & 0x000000FF] << 24);
    }

    /**
     * Provides the mixColumns and sboxes tables
     *
     * @see self::encryptBlock()
     * @see self::setupInlineCrypt()
     * @see self::subWord()
     */
    protected function &getTables(): array
    {
        static $tables;
        if (empty($tables)) {
            // according to <http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=19> (section 5.2.1),
            // precomputed tables can be used in the mixColumns phase. in that example, they're assigned t0...t3, so
            // those are the names we'll use.
            $t3 = array_map(self::safe_intval(...), [
                // with array_map('intval', ...) we ensure we have only int's and not
                // some slower floats converted by php automatically on high values
                0x6363A5C6, 0x7C7C84F8, 0x777799EE, 0x7B7B8DF6, 0xF2F20DFF, 0x6B6BBDD6, 0x6F6FB1DE, 0xC5C55491,
                0x30305060, 0x01010302, 0x6767A9CE, 0x2B2B7D56, 0xFEFE19E7, 0xD7D762B5, 0xABABE64D, 0x76769AEC,
                0xCACA458F, 0x82829D1F, 0xC9C94089, 0x7D7D87FA, 0xFAFA15EF, 0x5959EBB2, 0x4747C98E, 0xF0F00BFB,
                0xADADEC41, 0xD4D467B3, 0xA2A2FD5F, 0xAFAFEA45, 0x9C9CBF23, 0xA4A4F753, 0x727296E4, 0xC0C05B9B,
                0xB7B7C275, 0xFDFD1CE1, 0x9393AE3D, 0x26266A4C, 0x36365A6C, 0x3F3F417E, 0xF7F702F5, 0xCCCC4F83,
                0x34345C68, 0xA5A5F451, 0xE5E534D1, 0xF1F108F9, 0x717193E2, 0xD8D873AB, 0x31315362, 0x15153F2A,
                0x04040C08, 0xC7C75295, 0x23236546, 0xC3C35E9D, 0x18182830, 0x9696A137, 0x05050F0A, 0x9A9AB52F,
                0x0707090E, 0x12123624, 0x80809B1B, 0xE2E23DDF, 0xEBEB26CD, 0x2727694E, 0xB2B2CD7F, 0x75759FEA,
                0x09091B12, 0x83839E1D, 0x2C2C7458, 0x1A1A2E34, 0x1B1B2D36, 0x6E6EB2DC, 0x5A5AEEB4, 0xA0A0FB5B,
                0x5252F6A4, 0x3B3B4D76, 0xD6D661B7, 0xB3B3CE7D, 0x29297B52, 0xE3E33EDD, 0x2F2F715E, 0x84849713,
                0x5353F5A6, 0xD1D168B9, 0x00000000, 0xEDED2CC1, 0x20206040, 0xFCFC1FE3, 0xB1B1C879, 0x5B5BEDB6,
                0x6A6ABED4, 0xCBCB468D, 0xBEBED967, 0x39394B72, 0x4A4ADE94, 0x4C4CD498, 0x5858E8B0, 0xCFCF4A85,
                0xD0D06BBB, 0xEFEF2AC5, 0xAAAAE54F, 0xFBFB16ED, 0x4343C586, 0x4D4DD79A, 0x33335566, 0x85859411,
                0x4545CF8A, 0xF9F910E9, 0x02020604, 0x7F7F81FE, 0x5050F0A0, 0x3C3C4478, 0x9F9FBA25, 0xA8A8E34B,
                0x5151F3A2, 0xA3A3FE5D, 0x4040C080, 0x8F8F8A05, 0x9292AD3F, 0x9D9DBC21, 0x38384870, 0xF5F504F1,
                0xBCBCDF63, 0xB6B6C177, 0xDADA75AF, 0x21216342, 0x10103020, 0xFFFF1AE5, 0xF3F30EFD, 0xD2D26DBF,
                0xCDCD4C81, 0x0C0C1418, 0x13133526, 0xECEC2FC3, 0x5F5FE1BE, 0x9797A235, 0x4444CC88, 0x1717392E,
                0xC4C45793, 0xA7A7F255, 0x7E7E82FC, 0x3D3D477A, 0x6464ACC8, 0x5D5DE7BA, 0x19192B32, 0x737395E6,
                0x6060A0C0, 0x81819819, 0x4F4FD19E, 0xDCDC7FA3, 0x22226644, 0x2A2A7E54, 0x9090AB3B, 0x8888830B,
                0x4646CA8C, 0xEEEE29C7, 0xB8B8D36B, 0x14143C28, 0xDEDE79A7, 0x5E5EE2BC, 0x0B0B1D16, 0xDBDB76AD,
                0xE0E03BDB, 0x32325664, 0x3A3A4E74, 0x0A0A1E14, 0x4949DB92, 0x06060A0C, 0x24246C48, 0x5C5CE4B8,
                0xC2C25D9F, 0xD3D36EBD, 0xACACEF43, 0x6262A6C4, 0x9191A839, 0x9595A431, 0xE4E437D3, 0x79798BF2,
                0xE7E732D5, 0xC8C8438B, 0x3737596E, 0x6D6DB7DA, 0x8D8D8C01, 0xD5D564B1, 0x4E4ED29C, 0xA9A9E049,
                0x6C6CB4D8, 0x5656FAAC, 0xF4F407F3, 0xEAEA25CF, 0x6565AFCA, 0x7A7A8EF4, 0xAEAEE947, 0x08081810,
                0xBABAD56F, 0x787888F0, 0x25256F4A, 0x2E2E725C, 0x1C1C2438, 0xA6A6F157, 0xB4B4C773, 0xC6C65197,
                0xE8E823CB, 0xDDDD7CA1, 0x74749CE8, 0x1F1F213E, 0x4B4BDD96, 0xBDBDDC61, 0x8B8B860D, 0x8A8A850F,
                0x707090E0, 0x3E3E427C, 0xB5B5C471, 0x6666AACC, 0x4848D890, 0x03030506, 0xF6F601F7, 0x0E0E121C,
                0x6161A3C2, 0x35355F6A, 0x5757F9AE, 0xB9B9D069, 0x86869117, 0xC1C15899, 0x1D1D273A, 0x9E9EB927,
                0xE1E138D9, 0xF8F813EB, 0x9898B32B, 0x11113322, 0x6969BBD2, 0xD9D970A9, 0x8E8E8907, 0x9494A733,
                0x9B9BB62D, 0x1E1E223C, 0x87879215, 0xE9E920C9, 0xCECE4987, 0x5555FFAA, 0x28287850, 0xDFDF7AA5,
                0x8C8C8F03, 0xA1A1F859, 0x89898009, 0x0D0D171A, 0xBFBFDA65, 0xE6E631D7, 0x4242C684, 0x6868B8D0,
                0x4141C382, 0x9999B029, 0x2D2D775A, 0x0F0F111E, 0xB0B0CB7B, 0x5454FCA8, 0xBBBBD66D, 0x16163A2C,
            ]);

            foreach ($t3 as $t3i) {
                $t0[] = (($t3i << 24) & self::safe_intval(0xFF000000)) | (($t3i >>  8) & 0x00FFFFFF);
                $t1[] = (($t3i << 16) & self::safe_intval(0xFFFF0000)) | (($t3i >> 16) & 0x0000FFFF);
                $t2[] = (($t3i <<  8) & self::safe_intval(0xFFFFFF00)) | (($t3i >> 24) & 0x000000FF);
            }

            $tables = [
                // The Precomputed mixColumns tables t0 - t3
                $t0,
                $t1,
                $t2,
                $t3,
                // The SubByte S-Box
                [
                    0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
                    0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
                    0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
                    0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
                    0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
                    0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
                    0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
                    0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
                    0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
                    0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
                    0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
                    0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
                    0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
                    0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
                    0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
                    0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16,
                ],
            ];
        }
        return $tables;
    }

    /**
     * Provides the inverse mixColumns and inverse sboxes tables
     *
     * @see self::decryptBlock()
     * @see self::setupInlineCrypt()
     * @see self::setupKey()
     * @return array &$tables
     */
    protected function &getInvTables(): array
    {
        static $tables;
        if (empty($tables)) {
            $dt3 = array_map(self::safe_intval(...), [
                0xF4A75051, 0x4165537E, 0x17A4C31A, 0x275E963A, 0xAB6BCB3B, 0x9D45F11F, 0xFA58ABAC, 0xE303934B,
                0x30FA5520, 0x766DF6AD, 0xCC769188, 0x024C25F5, 0xE5D7FC4F, 0x2ACBD7C5, 0x35448026, 0x62A38FB5,
                0xB15A49DE, 0xBA1B6725, 0xEA0E9845, 0xFEC0E15D, 0x2F7502C3, 0x4CF01281, 0x4697A38D, 0xD3F9C66B,
                0x8F5FE703, 0x929C9515, 0x6D7AEBBF, 0x5259DA95, 0xBE832DD4, 0x7421D358, 0xE0692949, 0xC9C8448E,
                0xC2896A75, 0x8E7978F4, 0x583E6B99, 0xB971DD27, 0xE14FB6BE, 0x88AD17F0, 0x20AC66C9, 0xCE3AB47D,
                0xDF4A1863, 0x1A3182E5, 0x51336097, 0x537F4562, 0x6477E0B1, 0x6BAE84BB, 0x81A01CFE, 0x082B94F9,
                0x48685870, 0x45FD198F, 0xDE6C8794, 0x7BF8B752, 0x73D323AB, 0x4B02E272, 0x1F8F57E3, 0x55AB2A66,
                0xEB2807B2, 0xB5C2032F, 0xC57B9A86, 0x3708A5D3, 0x2887F230, 0xBFA5B223, 0x036ABA02, 0x16825CED,
                0xCF1C2B8A, 0x79B492A7, 0x07F2F0F3, 0x69E2A14E, 0xDAF4CD65, 0x05BED506, 0x34621FD1, 0xA6FE8AC4,
                0x2E539D34, 0xF355A0A2, 0x8AE13205, 0xF6EB75A4, 0x83EC390B, 0x60EFAA40, 0x719F065E, 0x6E1051BD,
                0x218AF93E, 0xDD063D96, 0x3E05AEDD, 0xE6BD464D, 0x548DB591, 0xC45D0571, 0x06D46F04, 0x5015FF60,
                0x98FB2419, 0xBDE997D6, 0x4043CC89, 0xD99E7767, 0xE842BDB0, 0x898B8807, 0x195B38E7, 0xC8EEDB79,
                0x7C0A47A1, 0x420FE97C, 0x841EC9F8, 0x00000000, 0x80868309, 0x2BED4832, 0x1170AC1E, 0x5A724E6C,
                0x0EFFFBFD, 0x8538560F, 0xAED51E3D, 0x2D392736, 0x0FD9640A, 0x5CA62168, 0x5B54D19B, 0x362E3A24,
                0x0A67B10C, 0x57E70F93, 0xEE96D2B4, 0x9B919E1B, 0xC0C54F80, 0xDC20A261, 0x774B695A, 0x121A161C,
                0x93BA0AE2, 0xA02AE5C0, 0x22E0433C, 0x1B171D12, 0x090D0B0E, 0x8BC7ADF2, 0xB6A8B92D, 0x1EA9C814,
                0xF1198557, 0x75074CAF, 0x99DDBBEE, 0x7F60FDA3, 0x01269FF7, 0x72F5BC5C, 0x663BC544, 0xFB7E345B,
                0x4329768B, 0x23C6DCCB, 0xEDFC68B6, 0xE4F163B8, 0x31DCCAD7, 0x63851042, 0x97224013, 0xC6112084,
                0x4A247D85, 0xBB3DF8D2, 0xF93211AE, 0x29A16DC7, 0x9E2F4B1D, 0xB230F3DC, 0x8652EC0D, 0xC1E3D077,
                0xB3166C2B, 0x70B999A9, 0x9448FA11, 0xE9642247, 0xFC8CC4A8, 0xF03F1AA0, 0x7D2CD856, 0x3390EF22,
                0x494EC787, 0x38D1C1D9, 0xCAA2FE8C, 0xD40B3698, 0xF581CFA6, 0x7ADE28A5, 0xB78E26DA, 0xADBFA43F,
                0x3A9DE42C, 0x78920D50, 0x5FCC9B6A, 0x7E466254, 0x8D13C2F6, 0xD8B8E890, 0x39F75E2E, 0xC3AFF582,
                0x5D80BE9F, 0xD0937C69, 0xD52DA96F, 0x2512B3CF, 0xAC993BC8, 0x187DA710, 0x9C636EE8, 0x3BBB7BDB,
                0x267809CD, 0x5918F46E, 0x9AB701EC, 0x4F9AA883, 0x956E65E6, 0xFFE67EAA, 0xBCCF0821, 0x15E8E6EF,
                0xE79BD9BA, 0x6F36CE4A, 0x9F09D4EA, 0xB07CD629, 0xA4B2AF31, 0x3F23312A, 0xA59430C6, 0xA266C035,
                0x4EBC3774, 0x82CAA6FC, 0x90D0B0E0, 0xA7D81533, 0x04984AF1, 0xECDAF741, 0xCD500E7F, 0x91F62F17,
                0x4DD68D76, 0xEFB04D43, 0xAA4D54CC, 0x9604DFE4, 0xD1B5E39E, 0x6A881B4C, 0x2C1FB8C1, 0x65517F46,
                0x5EEA049D, 0x8C355D01, 0x877473FA, 0x0B412EFB, 0x671D5AB3, 0xDBD25292, 0x105633E9, 0xD647136D,
                0xD7618C9A, 0xA10C7A37, 0xF8148E59, 0x133C89EB, 0xA927EECE, 0x61C935B7, 0x1CE5EDE1, 0x47B13C7A,
                0xD2DF599C, 0xF2733F55, 0x14CE7918, 0xC737BF73, 0xF7CDEA53, 0xFDAA5B5F, 0x3D6F14DF, 0x44DB8678,
                0xAFF381CA, 0x68C43EB9, 0x24342C38, 0xA3405FC2, 0x1DC37216, 0xE2250CBC, 0x3C498B28, 0x0D9541FF,
                0xA8017139, 0x0CB3DE08, 0xB4E49CD8, 0x56C19064, 0xCB84617B, 0x32B670D5, 0x6C5C7448, 0xB85742D0,
            ]);

            if (PHP_INT_SIZE === 8) {
                foreach ($dt3 as $dt3i) {
                    $dt0[] = (($dt3i << 24) & 0xFF000000) | (($dt3i >>  8) & 0x00FFFFFF);
                    $dt1[] = (($dt3i << 16) & 0xFFFF0000) | (($dt3i >> 16) & 0x0000FFFF);
                    $dt2[] = (($dt3i <<  8) & 0xFFFFFF00) | (($dt3i >> 24) & 0x000000FF);
                };
            } else {
                foreach ($dt3 as $dt3i) {
                    $dt0[] = ($dt3i << 24) | (($dt3i >>  8) & 0x00FFFFFF);
                    $dt1[] = ($dt3i << 16) | (($dt3i >> 16) & 0x0000FFFF);
                    $dt2[] = ($dt3i <<  8) | (($dt3i >> 24) & 0x000000FF);
                };
            }

            $tables = [
                // The Precomputed inverse mixColumns tables dt0 - dt3
                $dt0,
                $dt1,
                $dt2,
                $dt3,
                // The inverse SubByte S-Box
                [
                    0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,
                    0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,
                    0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
                    0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,
                    0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,
                    0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
                    0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,
                    0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,
                    0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
                    0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,
                    0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,
                    0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
                    0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,
                    0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,
                    0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
                    0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D,
                ],
            ];
        }
        return $tables;
    }

    /**
     * Setup the performance-optimized function for de/encrypt()
     *
     * @see Common\SymmetricKey::setup()
     * @psalm-suppress PossiblyUnusedMethod
     */
    protected function setupInlineCrypt(): void
    {
        $w  = $this->w;
        $dw = $this->dw;
        $init_encrypt = '';
        $init_decrypt = '';

        $Nr = $this->Nr;
        $Nb = $this->Nb;
        $c  = $this->c;

        // Generating encrypt code:
        $init_encrypt .= '
            if (empty($tables)) {
                $tables = &$this->getTables();
            }
            $t0   = $tables[0];
            $t1   = $tables[1];
            $t2   = $tables[2];
            $t3   = $tables[3];
            $sbox = $tables[4];
        ';

        $s  = 'e';
        $e  = 's';
        $wc = $Nb - 1;

        // Preround: addRoundKey
        $encrypt_block = '$in = unpack("N*", $in);' . "\n";
        for ($i = 0; $i < $Nb; ++$i) {
            $encrypt_block .= '$s' . $i . ' = $in[' . ($i + 1) . '] ^ ' . $w[++$wc] . ";\n";
        }

        // Mainrounds: shiftRows + subWord + mixColumns + addRoundKey
        for ($round = 1; $round < $Nr; ++$round) {
            [$s, $e] = [$e, $s];
            for ($i = 0; $i < $Nb; ++$i) {
                $encrypt_block .=
                    '$' . $e . $i . ' =
                    $t0[($' . $s . $i                  . ' >> 24) & 0xff] ^
                    $t1[($' . $s . (($i + $c[1]) % $Nb) . ' >> 16) & 0xff] ^
                    $t2[($' . $s . (($i + $c[2]) % $Nb) . ' >>  8) & 0xff] ^
                    $t3[ $' . $s . (($i + $c[3]) % $Nb) . '        & 0xff] ^
                    ' . $w[++$wc] . ";\n";
            }
        }

        // Finalround: subWord + shiftRows + addRoundKey
        for ($i = 0; $i < $Nb; ++$i) {
            $encrypt_block .=
                '$' . $e . $i . ' =
                 $sbox[ $' . $e . $i . '        & 0xff]        |
                ($sbox[($' . $e . $i . ' >>  8) & 0xff] <<  8) |
                ($sbox[($' . $e . $i . ' >> 16) & 0xff] << 16) |
                ($sbox[($' . $e . $i . ' >> 24) & 0xff] << 24);' . "\n";
        }
        $encrypt_block .= '$in = pack("N*"' . "\n";
        for ($i = 0; $i < $Nb; ++$i) {
            $encrypt_block .= ',
                ($' . $e . $i                   . ' & ' . (PHP_INT_SIZE == 8 ? 0xFF000000 : -16777216) . ') ^
                ($' . $e . (($i + $c[1]) % $Nb) . ' &         0x00FF0000   ) ^
                ($' . $e . (($i + $c[2]) % $Nb) . ' &         0x0000FF00   ) ^
                ($' . $e . (($i + $c[3]) % $Nb) . ' &         0x000000FF   ) ^
                ' . $w[$i] . "\n";
        }
        $encrypt_block .= ');';

        // Generating decrypt code:
        $init_decrypt .= '
            if (empty($invtables)) {
                $invtables = &$this->getInvTables();
            }
            $dt0   = $invtables[0];
            $dt1   = $invtables[1];
            $dt2   = $invtables[2];
            $dt3   = $invtables[3];
            $isbox = $invtables[4];
        ';

        $s  = 'e';
        $e  = 's';
        $wc = $Nb - 1;

        // Preround: addRoundKey
        $decrypt_block = '$in = unpack("N*", $in);' . "\n";
        for ($i = 0; $i < $Nb; ++$i) {
            $decrypt_block .= '$s' . $i . ' = $in[' . ($i + 1) . '] ^ ' . $dw[++$wc] . ';' . "\n";
        }

        // Mainrounds: shiftRows + subWord + mixColumns + addRoundKey
        for ($round = 1; $round < $Nr; ++$round) {
            [$s, $e] = [$e, $s];
            for ($i = 0; $i < $Nb; ++$i) {
                $decrypt_block .=
                    '$' . $e . $i . ' =
                    $dt0[($' . $s . $i                        . ' >> 24) & 0xff] ^
                    $dt1[($' . $s . (($Nb + $i - $c[1]) % $Nb) . ' >> 16) & 0xff] ^
                    $dt2[($' . $s . (($Nb + $i - $c[2]) % $Nb) . ' >>  8) & 0xff] ^
                    $dt3[ $' . $s . (($Nb + $i - $c[3]) % $Nb) . '        & 0xff] ^
                    ' . $dw[++$wc] . ";\n";
            }
        }

        // Finalround: subWord + shiftRows + addRoundKey
        for ($i = 0; $i < $Nb; ++$i) {
            $decrypt_block .=
                '$' . $e . $i . ' =
                 $isbox[ $' . $e . $i . '        & 0xff]        |
                ($isbox[($' . $e . $i . ' >>  8) & 0xff] <<  8) |
                ($isbox[($' . $e . $i . ' >> 16) & 0xff] << 16) |
                ($isbox[($' . $e . $i . ' >> 24) & 0xff] << 24);' . "\n";
        }
        $decrypt_block .= '$in = pack("N*"' . "\n";
        for ($i = 0; $i < $Nb; ++$i) {
            $decrypt_block .= ',
                ($' . $e . $i .                         ' & ' . (PHP_INT_SIZE == 8 ? 0xFF000000 : -16777216) . ') ^
                ($' . $e . (($Nb + $i - $c[1]) % $Nb) . ' &         0x00FF0000   ) ^
                ($' . $e . (($Nb + $i - $c[2]) % $Nb) . ' &         0x0000FF00   ) ^
                ($' . $e . (($Nb + $i - $c[3]) % $Nb) . ' &         0x000000FF   ) ^
                ' . $dw[$i] . "\n";
        }
        $decrypt_block .= ');';

        $this->inline_crypt = $this->createInlineCryptFunction(
            [
               'init_crypt'    => 'static $tables; static $invtables;',
               'init_encrypt'  => $init_encrypt,
               'init_decrypt'  => $init_decrypt,
               'encrypt_block' => $encrypt_block,
               'decrypt_block' => $decrypt_block,
            ]
        );
    }

    /**
     * Encrypts a message.
     *
     * @see self::decrypt()
     * @see parent::encrypt()
     */
    public function encrypt(#[\SensitiveParameter] string $plaintext): string
    {
        $this->setup();

        switch ($this->engine) {
            case self::ENGINE_LIBSODIUM:
                $this->newtag = sodium_crypto_aead_aes256gcm_encrypt($plaintext, $this->aad, $this->nonce, $this->key);
                return Strings::shift($this->newtag, strlen($plaintext));
            case self::ENGINE_OPENSSL_AEAD:
                return openssl_encrypt(
                    $plaintext,
                    'aes-' . $this->getKeyLength() . '-gcm',
                    $this->key,
                    OPENSSL_RAW_DATA,
                    $this->nonce,
                    $this->newtag,
                    $this->aad
                );
        }

        return parent::encrypt($plaintext);
    }

    /**
     * Decrypts a message.
     *
     * @see self::encrypt()
     * @see parent::decrypt()
     */
    public function decrypt(string $ciphertext): string
    {
        $this->setup();

        switch ($this->engine) {
            case self::ENGINE_LIBSODIUM:
                if (!isset($this->oldtag)) {
                    throw new InvalidStateException('Authentication Tag has not been set - call setTag() first');
                }
                if (strlen($this->oldtag) != 16) {
                    break;
                }
                $plaintext = sodium_crypto_aead_aes256gcm_decrypt($ciphertext . $this->oldtag, $this->aad, $this->nonce, $this->key);
                if ($plaintext === false) {
                    $this->oldtag = null;
                    throw new BadDecryptionException('Error decrypting ciphertext with libsodium');
                }
                return $plaintext;
            case self::ENGINE_OPENSSL_AEAD:
                if (!isset($this->oldtag)) {
                    throw new InvalidStateException('Authentication Tag has not been set - call setTag() first');
                }
                $plaintext = openssl_decrypt(
                    $ciphertext,
                    'aes-' . $this->getKeyLength() . '-gcm',
                    $this->key,
                    OPENSSL_RAW_DATA,
                    $this->nonce,
                    $this->oldtag,
                    $this->aad
                );
                if ($plaintext === false) {
                    $this->oldtag = null;
                    throw new BadDecryptionException('Error decrypting ciphertext with OpenSSL');
                }
                return $plaintext;
        }

        return parent::decrypt($ciphertext);
    }
}
<?php

/**
 * Pure-PHP (EC)DH implementation
 *
 * PHP version 8.1+
 *
 * Here's an example of how to compute a shared secret with this library:
 * <code>
 * <?php
 * include 'vendor/autoload.php';
 *
 * $ourPrivate = \phpseclib4\Crypt\DH::createKey();
 * $secret = DH::computeSecret($ourPrivate, $theirPublic);
 *
 * ?>
 * </code>
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2019-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt;

use phpseclib4\Crypt\Common\AsymmetricKey;
use phpseclib4\Crypt\DH\{Parameters, PrivateKey, PublicKey};
use phpseclib4\Exception\{
    BadConfigurationException,
    BadMethodCallException,
    InvalidArgumentException,
    NoKeyLoadedException,
    UnsupportedAlgorithmException
};
use phpseclib4\Math\BigInteger;

/**
 * Pure-PHP (EC)DH implementation
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class DH extends AsymmetricKey
{
    /**
     * Algorithm Name
     *
     * @var string
     */
    public const ALGORITHM = 'DH';

    /**
     * DH prime
     */
    protected BigInteger $prime;

    /**
     * DH Base
     *
     * Prime divisor of p-1
     */
    protected BigInteger $base;

    /**
     * Public Key
     */
    protected BigInteger $publicKey;

    /**
     * Create DH parameters
     *
     * This method is a bit polymorphic. It can take any of the following:
     *  - two BigInteger's (prime and base)
     *  - an integer representing the size of the prime in bits (the base is assumed to be 2)
     *  - a string (eg. diffie-hellman-group14-sha1)
     */
    public static function createParameters(BigInteger|int|string ...$args): Parameters
    {
        $class = new \ReflectionClass(static::class);
        if ($class->isFinal()) {
            throw new BadMethodCallException('createParameters() should not be called from final classes (' . static::class . ')');
        }

        $params = new Parameters();
        if (count($args) == 2 && $args[0] instanceof BigInteger && $args[1] instanceof BigInteger) {
            //if (!$args[0]->isPrime()) {
            //    throw new InvalidArgumentException('The first parameter should be a prime number');
            //}
            $params->prime = $args[0];
            $params->base = $args[1];
            return $params;
        } elseif (count($args) == 1 && is_numeric($args[0])) {
            $params->prime = BigInteger::randomPrime((int) $args[0]);
            $params->base = new BigInteger(2);
            return $params;
        } elseif (count($args) != 1 || !is_string($args[0])) {
            throw new InvalidArgumentException('Valid parameters are either: two BigInteger\'s (prime and base), a single integer (the length of the prime; base is assumed to be 2) or a string');
        }
        $prime = match ($args[0]) {
            // see http://tools.ietf.org/html/rfc2409#section-6.2 and
            // http://tools.ietf.org/html/rfc2412, appendex E
            'diffie-hellman-group1-sha1' =>
                'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
                '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
                '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
                'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF',
            // see http://tools.ietf.org/html/rfc3526#section-3, 2048-bit MODP Group
            'diffie-hellman-group14-sha1', 'diffie-hellman-group14-sha256' =>
                'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
                '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
                '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
                'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' .
                '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' .
                '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' .
                'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' .
                '3995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF',
            // see https://tools.ietf.org/html/rfc3526#section-4, 3072-bit MODP Group
            'diffie-hellman-group15-sha512' =>
                'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
                '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
                '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
                'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' .
                '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' .
                '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' .
                'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' .
                '3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33' .
                'A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7' .
                'ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864' .
                'D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E2' .
                '08E24FA074E5AB3143DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF',
            // see https://tools.ietf.org/html/rfc3526#section-5, 4096-bit MODP Group
            'diffie-hellman-group16-sha512' =>
                'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
                '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
                '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
                'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' .
                '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' .
                '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' .
                'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' .
                '3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33' .
                'A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7' .
                'ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864' .
                'D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E2' .
                '08E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7' .
                '88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8' .
                'DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2' .
                '233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9' .
                '93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199FFFFFFFFFFFFFFFF',
            // see https://tools.ietf.org/html/rfc3526#section-6, 6144-bit MODP Group
            'diffie-hellman-group17-sha512' =>
                'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
                '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
                '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
                'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' .
                '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' .
                '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' .
                'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' .
                '3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33' .
                'A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7' .
                'ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864' .
                'D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E2' .
                '08E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7' .
                '88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8' .
                'DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2' .
                '233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9' .
                '93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026' .
                'C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AE' .
                'B06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B' .
                'DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92EC' .
                'F032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E' .
                '59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA' .
                'CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76' .
                'F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468' .
                '043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DCC4024FFFFFFFFFFFFFFFF',
            // see https://tools.ietf.org/html/rfc3526#section-7, 8192-bit MODP Group
            'diffie-hellman-group18-sha512' =>
                'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
                '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
                '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
                'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' .
                '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' .
                '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' .
                'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' .
                '3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33' .
                'A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7' .
                'ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864' .
                'D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E2' .
                '08E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7' .
                '88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8' .
                'DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2' .
                '233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9' .
                '93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026' .
                'C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AE' .
                'B06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B' .
                'DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92EC' .
                'F032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E' .
                '59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA' .
                'CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76' .
                'F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468' .
                '043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E4' .
                '38777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300741FA7BF8AFC47ED' .
                '2576F6936BA424663AAB639C5AE4F5683423B4742BF1C978238F16CBE39D652D' .
                'E3FDB8BEFC848AD922222E04A4037C0713EB57A81A23F0C73473FC646CEA306B' .
                '4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A6' .
                '6D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC50846851D' .
                'F9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F92' .
                '4009438B481C6CD7889A002ED5EE382BC9190DA6FC026E479558E4475677E9AA' .
                '9E3050E2765694DFC81F56E880B96E7160C980DD98EDD3DFFFFFFFFFFFFFFFFF',
            default => throw new UnsupportedAlgorithmException('Invalid named prime provided')
        };

        $params->prime = new BigInteger($prime, 16);
        $params->base = new BigInteger(2);

        return $params;
    }

    /**
     * Create public / private key pair.
     *
     * The rationale for the second parameter is described in http://tools.ietf.org/html/rfc4419#section-6.2 :
     *
     * "To increase the speed of the key exchange, both client and server may
     *  reduce the size of their private exponents.  It should be at least
     *  twice as long as the key material that is generated from the shared
     *  secret.  For more details, see the paper by van Oorschot and Wiener
     *  [VAN-OORSCHOT]."
     *
     * $length is in bits
     *
     * @param int $length optional
     */
    public static function createKey(Parameters $params, int $length = 0): PrivateKey
    {
        $class = new \ReflectionClass(static::class);
        if ($class->isFinal()) {
            throw new BadMethodCallException('createKey() should not be called from final classes (' . static::class . ')');
        }

        $one = new BigInteger(1);
        if ($length) {
            $max = $one->bitwise_leftShift($length);
            $max = $max->subtract($one);
        } else {
            $max = $params->prime->subtract($one);
        }

        $key = new PrivateKey();
        $key->prime = $params->prime;
        $key->base = $params->base;
        $key->privateKey = BigInteger::randomRange($one, $max);
        $key->publicKey = $key->base->powMod($key->privateKey, $key->prime);
        return $key;
    }

    /**
     * Compute Shared Secret
     */
    public static function computeSecret(
        #[\SensitiveParameter] PrivateKey|EC\PrivateKey $private,
        PublicKey|EC\PublicKey|BigInteger|string $public
    ): BigInteger|string {
        if ($private instanceof EC\PrivateKey) {
            $privateCurve = $private->getCurve();
            switch (true) {
                case $public instanceof EC\PublicKey:
                    if ($privateCurve !== $public->getCurve()) {
                        throw new InvalidArgumentException("The public key curve (" . $public->getCurve() . ") and private key curve ($privateCurve) need to match");
                    }
                    $orig = $public;
                    $public = $public->getEncodedCoordinates();
                    // no break
                case is_string($public):
                    $forcedEngine = EC::getForcedEngine();
                    if ($forcedEngine === 'libsodium' && $privateCurve !== 'Curve25519') {
                        throw new BadConfigurationException('Engine libsodium is forced but can only used with Curve25519 for ECDH');
                    }
                    if (!isset($forcedEngine) || $forcedEngine === 'OpenSSL') {
                        // PHP 7.3.0 introduced the openssl_pkey_derive() function
                        // openssl_dh_computee_key() has been around since PHP 5.3.0+ BUT it did not support ECDH
                        // until PHP 8.1.0 / OpenSSL 3.0.0
                        if ($forcedEngine === 'OpenSSL' && !function_exists('openssl_pkey_derive')) {
                            throw new BadConfigurationException('Engine OpenSSL is forced but unsupported for ECDH');
                        }
                        if (function_exists('openssl_pkey_derive')) {
                            $privateStr = (string) $private->withPassword();
                            $publicStr = (string) ($orig ?? EC::convertPointToPublicKey($private->getCurve(), $public));
                            $result = openssl_pkey_derive($publicStr, $privateStr);
                            if ($result) {
                                return $result;
                            }
                            if ($forcedEngine === 'OpenSSL') {
                                // i suppose we _could_ try openssl_dh_compute_key() at this point
                                // quoting https://www.php.net/openssl-dh-compute-key "ECDH is only supported as of PHP 8.1.0 and OpenSSL 3.0.0". ie.
                                // PHP_VERSION_ID >= 80100 && OPENSSL_VERSION_NUMBER >= 0x3000000f
                                // but i think that's overkill. if openssl_pkey_derive() doesn't work it seems doubtful to me that openssl_dh_compute_key() would
                                throw new BadConfigurationException('Engine OpenSSL is forced but was unable to perform ECDH because of ' . openssl_error_string());
                            }
                        }
                    }
                    $curveName = $private->getCurve();
                    $isMontgomeryCurve = $curveName == 'Curve25519' || $curveName == 'Curve448';
                    if (!$isMontgomeryCurve) {
                        $public = EC::convertPointToPublicKey($curveName, $public, false);
                    }
                    $point = $private->multiply($public);
                    if ($isMontgomeryCurve) {
                        /*
                        "Both MAY check, without leaking extra information about the value of K,
                         whether K is the all-zero value and abort if so"
                        -- https://datatracker.ietf.org/doc/html/rfc7748#section-6.1 (and #section-6.2)
                        */
                        $size = $curveName == 'Curve25519' ? 32 : 56;
                        // throw exception if hash_equals is false, otherwise, return $point
                        if (hash_equals(str_repeat("\0", $size), $point)) {
                            throw new \UnexpectedValueException('All-zero shared secret detected (points order is too small)');
                        }
                        return $point;
                    }
                    // according to https://www.secg.org/sec1-v2.pdf#page=33 only X is returned
                    $secret = substr($point, 1, (strlen($point) - 1) >> 1);
                    /*
                    if (($secret[0] & "\x80") === "\x80") {
                        $secret = "\0$secret";
                    }
                    */
                    return $secret;
                default:
                    throw new InvalidArgumentException('$public needs to be an instance of EC\PublicKey or a string (an encoded coordinate)');
            }
        }

        // at this point $private is an instanceof DH\PrivateKey

        switch (true) {
            case $public instanceof PublicKey:
                if (!$private->prime->equals($public->prime) || !$private->base->equals($public->base)) {
                    throw new InvalidArgumentException('The public and private key do not share the same prime and / or base numbers');
                }
                return $public->publicKey->powMod($private->privateKey, $private->prime)->toBytes(true);
            case is_string($public):
                $public = new BigInteger($public, -256);
            // no break
            case $public instanceof BigInteger:
                return $public->powMod($private->privateKey, $private->prime)->toBytes(true);
            default:
                throw new InvalidArgumentException('$public needs to be an instance of DH\PublicKey, a BigInteger or a string');
        }
    }

    /**
     * Load the key
     */
    public static function load(
        #[\SensitiveParameter] string|array $key,
        #[\SensitiveParameter] ?string $password = null
    ): static {
        try {
            return EC::load($key, $password);
        } catch (NoKeyLoadedException $e) {
        }

        return parent::load($key, $password);
    }

    /**
     * OnLoad Handler
     */
    protected static function onLoad(array $components): Parameters|PrivateKey|PublicKey
    {
        if (!isset($components['privateKey']) && !isset($components['publicKey'])) {
            $new = new Parameters();
        } elseif (isset($components['privateKey'])) {
            $new = new PrivateKey();
            $new->privateKey = $components['privateKey'];
        } else {
            $new = new PublicKey();
        }

        $new->prime = $components['prime'];
        $new->base = $components['base'];

        if (isset($components['publicKey'])) {
            $new->publicKey = $components['publicKey'];
        }

        return $new;
    }

    /**
     * Determines which hashing function should be used
     */
    public function withHash(string $hash): static
    {
        throw new BadMethodCallException('DH does not use a hash algorithm');
    }

    /**
     * Returns the hash algorithm currently being used
     */
    public function getHash(): Hash
    {
        throw new BadMethodCallException('DH does not use a hash algorithm');
    }

    /**
     * Returns the parameters
     *
     * A public / private key is only returned if the currently loaded "key" contains an x or y
     * value.
     *
     * @see self::getPublicKey()
     */
    public function getParameters(): Parameters
    {
        $type = DH::validatePlugin('Keys', 'PKCS1', 'saveParameters');

        $key = $type::saveParameters($this->prime, $this->base);
        return DH::load($key, 'PKCS1');
    }
}
<?php

/**
 * Base Class for all \phpseclib4\Crypt\* cipher classes
 *
 * PHP version 8.1+
 *
 * Internally for phpseclib developers:
 *  If you plan to add a new cipher class, please note following rules:
 *
 *  - The new \phpseclib4\Crypt\* cipher class should extend \phpseclib4\Crypt\Common\SymmetricKey
 *
 *  - Following methods are then required to be overridden/overloaded:
 *
 *    - encryptBlock()
 *
 *    - decryptBlock()
 *
 *    - setupKey()
 *
 *  - All other methods are optional to be overridden/overloaded
 *
 *  - Look at the source code of the current ciphers how they extend \phpseclib4\Crypt\Common\SymmetricKey
 *    and take one of them as a start up for the new cipher class.
 *
 *  - Please read all the other comments/notes/hints here also for each class var/method
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @author    Hans-Juergen Petrich <petrich@tronic-media.com>
 * @copyright 2013-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\Common;

use phpseclib4\Common\Functions\Strings;
use phpseclib4\Crypt\{Blowfish, Hash};
use phpseclib4\Exception\{
    BadDecryptionException,
    BadMethodCallException,
    InvalidArgumentException,
    InvalidModeException,
    InvalidStateException,
    LengthException,
    UnsupportedAlgorithmException
};
use phpseclib4\Math\{BigInteger, BinaryField, PrimeField};

/**
 * Base Class for all \phpseclib4\Crypt\* cipher classes
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @author  Hans-Juergen Petrich <petrich@tronic-media.com>
 */
abstract class SymmetricKey
{
    use Traits\PKCS12Helper;

    /**
     * Encrypt / decrypt using the Counter mode.
     *
     * Set to -1 since that's what Crypt/Random.php uses to index the CTR mode.
     *
     * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29
     * @see \phpseclib4\Crypt\Common\SymmetricKey::encrypt()
     * @see \phpseclib4\Crypt\Common\SymmetricKey::decrypt()
     */
    public const MODE_CTR = -1;
    /**
     * Encrypt / decrypt using the Electronic Code Book mode.
     *
     * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29
     * @see \phpseclib4\Crypt\Common\SymmetricKey::encrypt()
     * @see \phpseclib4\Crypt\Common\SymmetricKey::decrypt()
     */
    public const MODE_ECB = 1;
    /**
     * Encrypt / decrypt using the Code Book Chaining mode.
     *
     * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29
     * @see \phpseclib4\Crypt\Common\SymmetricKey::encrypt()
     * @see \phpseclib4\Crypt\Common\SymmetricKey::decrypt()
     */
    public const MODE_CBC = 2;
    /**
     * Encrypt / decrypt using the Cipher Feedback mode.
     *
     * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29
     * @see \phpseclib4\Crypt\Common\SymmetricKey::encrypt()
     * @see \phpseclib4\Crypt\Common\SymmetricKey::decrypt()
     */
    public const MODE_CFB = 3;
    /**
     * Encrypt / decrypt using the Cipher Feedback mode (8bit)
     *
     * @see \phpseclib4\Crypt\Common\SymmetricKey::encrypt()
     * @see \phpseclib4\Crypt\Common\SymmetricKey::decrypt()
     */
    public const MODE_CFB8 = 7;
    /**
     * Encrypt / decrypt using the Output Feedback mode (8bit)
     *
     * @see \phpseclib4\Crypt\Common\SymmetricKey::encrypt()
     * @see \phpseclib4\Crypt\Common\SymmetricKey::decrypt()
     */
    public const MODE_OFB8 = 8;
    /**
     * Encrypt / decrypt using the Output Feedback mode.
     *
     * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29
     * @see \phpseclib4\Crypt\Common\SymmetricKey::encrypt()
     * @see \phpseclib4\Crypt\Common\SymmetricKey::decrypt()
     */
    public const MODE_OFB = 4;
    /**
     * Encrypt / decrypt using Galois/Counter mode.
     *
     * @link https://en.wikipedia.org/wiki/Galois/Counter_Mode
     * @see \phpseclib4\Crypt\Common\SymmetricKey::encrypt()
     * @see \phpseclib4\Crypt\Common\SymmetricKey::decrypt()
     */
    public const MODE_GCM = 5;
    /**
     * Encrypt / decrypt using streaming mode.
     *
     * @see \phpseclib4\Crypt\Common\SymmetricKey::encrypt()
     * @see \phpseclib4\Crypt\Common\SymmetricKey::decrypt()
     */
    public const MODE_STREAM = 6;

    protected int $key_length = -1;

    /**
     * Mode Map
     *
     * @see \phpseclib4\Crypt\Common\SymmetricKey::__construct()
     */
    public const MODE_MAP = [
        'ctr'    => self::MODE_CTR,
        'ecb'    => self::MODE_ECB,
        'cbc'    => self::MODE_CBC,
        'cfb'    => self::MODE_CFB,
        'cfb8'   => self::MODE_CFB8,
        'ofb'    => self::MODE_OFB,
        'ofb8'   => self::MODE_OFB8,
        'gcm'    => self::MODE_GCM,
        'stream' => self::MODE_STREAM,
    ];

    /**
     * Base value for the internal implementation $engine switch
     *
     * @see \phpseclib4\Crypt\Common\SymmetricKey::__construct()
     */
    public const ENGINE_INTERNAL = 1;
    /**
     * Base value for the eval() implementation $engine switch
     *
     * @see \phpseclib4\Crypt\Common\SymmetricKey::__construct()
     */
    public const ENGINE_EVAL = 2;
    /**
     * Base value for the openssl implementation $engine switch
     *
     * @see \phpseclib4\Crypt\Common\SymmetricKey::__construct()
     */
    public const ENGINE_OPENSSL = 4;
    /**
     * Base value for the libsodium implementation $engine switch
     *
     * @see \phpseclib4\Crypt\Common\SymmetricKey::__construct()
     */
    public const ENGINE_LIBSODIUM = 5;
    /**
     * Base value for the openssl / gcm implementation $engine switch
     *
     * @see \phpseclib4\Crypt\Common\SymmetricKey::__construct()
     */
    public const ENGINE_OPENSSL_AEAD = 6;

    /**
     * Engine Reverse Map
     *
     * @see \phpseclib4\Crypt\Common\SymmetricKey::getEngine()
     */
    public const ENGINE_MAP = [
        self::ENGINE_INTERNAL     => 'PHP',
        self::ENGINE_EVAL         => 'Eval',
        self::ENGINE_OPENSSL      => 'OpenSSL',
        self::ENGINE_LIBSODIUM    => 'libsodium',
        self::ENGINE_OPENSSL_AEAD => 'OpenSSL (AEAD)',
    ];

    /**
     * The Encryption Mode
     *
     * @see self::__construct()
     */
    protected int $mode;

    /**
     * The Block Length of the block cipher
     */
    protected int $block_size = 16;

    /**
     * The Key
     *
     * @see self::setKey()
     */
    protected ?string $key = null;

    /**
     * HMAC Key
     *
     * @see self::setupGCM()
     */
    private ?string $hKey = null;

    /**
     * The Initialization Vector
     *
     * @see self::setIV()
     */
    protected ?string $iv = null;

    /**
     * A "sliding" Initialization Vector
     *
     * @see self::enableContinuousBuffer()
     * @see self::clearBuffers()
     */
    protected ?string $encryptIV;

    /**
     * A "sliding" Initialization Vector
     *
     * @see self::enableContinuousBuffer()
     * @see self::clearBuffers()
     */
    protected ?string $decryptIV;

    /**
     * Continuous Buffer status
     *
     * @see self::enableContinuousBuffer()
     */
    protected bool $continuousBuffer = false;

    /**
     * Encryption buffer for CTR, OFB and CFB modes
     *
     * @see self::encrypt()
     * @see self::clearBuffers()
     */
    protected array $enbuffer;

    /**
     * Decryption buffer for CTR, OFB and CFB modes
     *
     * @see self::decrypt()
     * @see self::clearBuffers()
     */
    protected array $debuffer;

    /**
     * Does internal cipher state need to be (re)initialized?
     *
     * @see self::setKey()
     * @see self::setIV()
     * @see self::disableContinuousBuffer()
     */
    protected bool $changed = true;

    /**
     * Does Eval engie need to be (re)initialized?
     *
     * @see self::setup()
     */
    protected bool $nonIVChanged = true;

    /**
     * Padding status
     *
     * @see self::enablePadding()
     */
    private bool $padding = true;

    /**
     * Is the mode one that is paddable?
     *
     * @see self::__construct()
     */
    private bool $paddable = false;

    /**
     * Holds which crypt engine internaly should be use,
     * which will be determined automatically on __construct()
     *
     * Currently available $engines are:
     * - self::ENGINE_LIBSODIUM    (very fast, php-extension: libsodium, extension_loaded('libsodium') required)
     * - self::ENGINE_OPENSSL_AEAD (very fast, php-extension: openssl, extension_loaded('openssl') required)
     * - self::ENGINE_OPENSSL      (very fast, php-extension: openssl, extension_loaded('openssl') required)
     * - self::ENGINE_EVAL         (medium, pure php-engine, no php-extension required)
     * - self::ENGINE_INTERNAL     (slower, pure php-engine, no php-extension required)
     *
     * @see self::setEngine()
     * @see self::encrypt()
     * @see self::decrypt()
     */
    protected int $engine;

    /**
     * Holds the preferred crypt engine
     *
     * @see self::setEngine()
     * @see self::setPreferredEngine()
     */
    private int $preferredEngine;

    /**
     * The openssl specific name of the cipher
     *
     * Only used if $engine == self::ENGINE_OPENSSL
     *
     * @link http://www.php.net/openssl-get-cipher-methods
     */
    protected string $cipher_name_openssl;

    /**
     * The openssl specific name of the cipher in ECB mode
     *
     * If OpenSSL does not support the mode we're trying to use (CTR)
     * it can still be emulated with ECB mode.
     *
     * @link http://www.php.net/openssl-get-cipher-methods
     */
    protected string $cipher_name_openssl_ecb;

    /**
     * The default salt used by setPassword()
     *
     * @see self::setPassword()
     */
    private string $password_default_salt = 'phpseclib/salt';

    /**
     * The name of the performance-optimized callback function
     *
     * Used by encrypt() / decrypt()
     * only if $engine == self::ENGINE_INTERNAL
     *
     * @see self::encrypt()
     * @see self::decrypt()
     * @see self::setupInlineCrypt()
     */
    protected \Closure $inline_crypt;

    /**
     * If OpenSSL can be used in ECB but not in CTR we can emulate CTR
     *
     * @see self::openssl_ctr_process()
     */
    private bool $openssl_emulate_ctr = false;

    /**
     * Has the key length explicitly been set or should it be derived from the key, itself?
     *
     * @see self::setKeyLength()
     */
    protected ?int $explicit_key_length;

    /**
     * Hash subkey for GHASH
     *
     * @see self::setupGCM()
     * @see self::ghash()
     */
    private BinaryField\Integer $h;

    /**
     * Additional authenticated data
     */
    protected string $aad = '';

    /**
     * Authentication Tag produced after a round of encryption
     */
    protected ?string $newtag = null;

    /**
     * Authentication Tag to be verified during decryption
     */
    protected ?string $oldtag = null;

    /**
     * GCM Binary Field
     *
     * @see self::__construct()
     * @see self::ghash()
     */
    private static BinaryField $gcmField;

    /**
     * Poly1305 Prime Field
     *
     * @see self::enablePoly1305()
     * @see self::poly1305()
     */
    private static PrimeField $poly1305Field;

    /**
     * Poly1305 Key
     *
     * @see self::setPoly1305Key()
     * @see self::poly1305()
     */
    protected string $poly1305Key;

    /**
     * Poly1305 Flag
     *
     * @see self::setPoly1305Key()
     * @see self::enablePoly1305()
     */
    protected bool $usePoly1305 = false;

    /**
     * The Original Initialization Vector
     *
     * GCM uses the nonce to build the IV but we want to be able to distinguish between nonce-derived
     * IV's and user-set IV's
     *
     * @see self::setIV()
     */
    private ?string $origIV = null;

    /**
     * Nonce
     *
     * Only used with GCM. We could re-use setIV() but nonce's can be of a different length and
     * toggling between GCM and other modes could be more complicated if we re-used setIV()
     *
     * @see self::setNonce()
     */
    protected ?string $nonce = null;

    private array $metadata = [];

    /**
     * Flag for using regular vs "safe" intval
     *
     * @see self::initialize_static_variables()
     */
    protected static bool $use_reg_intval;

    /**
     * Default Constructor.
     *
     * $mode could be:
     *
     * - ecb
     *
     * - cbc
     *
     * - ctr
     *
     * - cfb
     *
     * - cfb8
     *
     * - ofb
     *
     * - ofb8
     *
     * - gcm
     */
    public function __construct(string $mode)
    {
        $mode = strtolower($mode);
        // necessary because of 5.6 compatibility; we can't do isset(self::MODE_MAP[$mode]) in 5.6
        $map = self::MODE_MAP;
        if (!isset($map[$mode])) {
            throw new InvalidArgumentException('No valid mode has been specified');
        }

        $mode = self::MODE_MAP[$mode];

        // $mode dependent settings
        switch ($mode) {
            case self::MODE_ECB:
            case self::MODE_CBC:
                $this->paddable = true;
                break;
            case self::MODE_CTR:
            case self::MODE_CFB:
            case self::MODE_CFB8:
            case self::MODE_OFB:
            case self::MODE_OFB8:
            case self::MODE_STREAM:
                $this->paddable = false;
                break;
            case self::MODE_GCM:
                if ($this->block_size != 16) {
                    throw new InvalidModeException('GCM is only valid for block ciphers with a block size of 128 bits');
                }
                if (!isset(self::$gcmField)) {
                    self::$gcmField = new BinaryField(128, 7, 2, 1, 0);
                }
                $this->paddable = false;
                break;
            default:
                throw new InvalidModeException('No valid mode has been specified');
        }

        $this->mode = $mode;

        static::initialize_static_variables();
    }

    /**
     * Initialize static variables
     */
    protected static function initialize_static_variables(): void
    {
        self::$use_reg_intval = PHP_VERSION_ID < 80500 || PHP_INT_SIZE == 8;
    }

    /**
     * Sets the initialization vector.
     *
     * setIV() is not required when ecb or gcm modes are being used.
     *
     * {@internal Can be overwritten by a sub class, but does not have to be}
     */
    public function setIV(string $iv): void
    {
        if ($this->mode == self::MODE_ECB) {
            throw new BadMethodCallException('This mode does not require an IV.');
        }

        if ($this->mode == self::MODE_GCM) {
            throw new BadMethodCallException('Use setNonce instead');
        }

        if (!$this->usesIV()) {
            throw new BadMethodCallException('This algorithm does not use an IV.');
        }

        if (strlen($iv) != $this->block_size) {
            throw new LengthException('Received initialization vector of size ' . strlen($iv) . ', but size ' . $this->block_size . ' is required');
        }

        $this->iv = $this->origIV = $iv;
        $this->changed = true;
    }

    /**
     * Enables Poly1305 mode.
     *
     * Once enabled Poly1305 cannot be disabled.
     */
    public function enablePoly1305(): void
    {
        if ($this->mode == self::MODE_GCM) {
            throw new BadMethodCallException('Poly1305 cannot be used in GCM mode');
        }

        $this->usePoly1305 = true;
    }

    /**
     * Enables Poly1305 mode.
     *
     * Once enabled Poly1305 cannot be disabled. If $key is not passed then an attempt to call createPoly1305Key
     * will be made.
     */
    public function setPoly1305Key(#[\SensitiveParameter] ?string $key = null): void
    {
        if ($this->mode == self::MODE_GCM) {
            throw new BadMethodCallException('Poly1305 cannot be used in GCM mode');
        }

        if (!is_string($key) || strlen($key) != 32) {
            throw new LengthException('The Poly1305 key must be 32 bytes long (256 bits)');
        }

        if (!isset(self::$poly1305Field)) {
            // 2^130-5
            self::$poly1305Field = new PrimeField(new BigInteger('3fffffffffffffffffffffffffffffffb', 16));
        }

        $this->poly1305Key = $key;
        $this->usePoly1305 = true;
    }

    /**
     * Sets the nonce.
     *
     * setNonce() is only required when gcm is used
     */
    public function setNonce(string $nonce): void
    {
        if ($this->mode != self::MODE_GCM) {
            throw new BadMethodCallException('Nonces are only used in GCM mode.');
        }

        $this->nonce = $nonce;
        $this->setEngine();
    }

    /**
     * Sets additional authenticated data
     *
     * setAAD() is only used by gcm or in poly1305 mode
     */
    public function setAAD(string $aad): void
    {
        if ($this->mode != self::MODE_GCM && !$this->usePoly1305) {
            throw new BadMethodCallException('Additional authenticated data is only utilized in GCM mode or with Poly1305');
        }

        $this->aad = $aad;
    }

    /**
     * Returns whether or not the algorithm uses an IV
     */
    public function usesIV(): bool
    {
        return $this->mode != self::MODE_GCM && $this->mode != self::MODE_ECB;
    }

    /**
     * Returns whether or not the algorithm uses a nonce
     */
    public function usesNonce(): bool
    {
        return $this->mode == self::MODE_GCM;
    }

    /**
     * Returns the current key length in bits
     */
    public function getKeyLength(): int
    {
        return $this->key_length << 3;
    }

    /**
     * Returns the current key length in bytes
     */
    public function getKeyLengthInBytes(): int
    {
        return $this->key_length;
    }

    /**
     * Returns the current block length in bits
     */
    public function getBlockLength(): int
    {
        return $this->block_size << 3;
    }

    /**
     * Returns the current block length in bytes
     */
    public function getBlockLengthInBytes(): int
    {
        return $this->block_size;
    }

    /**
     * Sets the key length.
     *
     * Keys with explicitly set lengths need to be treated accordingly
     */
    public function setKeyLength(int $length): void
    {
        $this->explicit_key_length = $length >> 3;

        if (isset($this->key) && strlen($this->key) != $this->explicit_key_length) {
            throw new LengthException('Key has already been set and is not ' . $this->explicit_key_length . ' bytes long');
        }
    }

    /**
     * Sets the key.
     *
     * The min/max length(s) of the key depends on the cipher which is used.
     * If the key not fits the length(s) of the cipher it will paded with null bytes
     * up to the closest valid key length.  If the key is more than max length,
     * we trim the excess bits.
     *
     * If the key is not explicitly set, it'll be assumed to be all null bytes.
     *
     * {@internal Could, but not must, extend by the child Crypt_* class}
     */
    public function setKey(#[\SensitiveParameter] string $key): void
    {
        if (isset($this->explicit_key_length) && strlen($key) != $this->explicit_key_length) {
            throw new LengthException('Key length has already been set to ' . $this->explicit_key_length . ' bytes and this key is ' . strlen($key) . ' bytes');
        }

        $this->key = $key;
        $this->key_length = strlen($key);
        $this->setEngine();
    }

    /**
     * Sets the password.
     *
     * Depending on what $method is set to, setPassword()'s (optional) parameters are as follows:
     *     {@link http://en.wikipedia.org/wiki/PBKDF2 pbkdf2} or pbkdf1:
     *         $hash, $salt, $count, $dkLen
     *
     *         Where $hash (default = sha1) currently supports the following hashes: see: Crypt/Hash.php
     *     {@link https://en.wikipedia.org/wiki/Bcrypt bcypt}:
     *         $salt, $rounds, $keylen
     *
     *         This is a modified version of bcrypt used by OpenSSH.
     *
     * {@internal Could, but not must, extend by the child Crypt_* class}
     *
     * @see Crypt/Hash.php
     */
    public function setPassword(
        #[\SensitiveParameter] string $password,
        string $method = 'pbkdf2',
        int|string ...$func_args
    ): void {
        $key = '';

        $method = strtolower($method);
        switch ($method) {
            case 'bcrypt':
                if (!isset($func_args[2])) {
                    throw new InvalidArgumentException('A salt must be provided for bcrypt to work');
                }

                $salt = $func_args[0];

                $rounds = $func_args[1] ?? 16;
                $keylen = $func_args[2] ?? $this->key_length;

                $key = Blowfish::bcrypt_pbkdf($password, $salt, $keylen + $this->block_size, $rounds);

                $this->setKey(substr($key, 0, $keylen));
                $this->setIV(substr($key, $keylen));

                return;
            case 'pkcs12': // from https://tools.ietf.org/html/rfc7292#appendix-B.2
            case 'pbkdf1':
            case 'pbkdf2':
                // Hash function
                $hash = isset($func_args[0]) ? strtolower($func_args[0]) : 'sha1';
                $hashObj = new Hash();
                $hashObj->setHash($hash);

                // WPA and WPA2 use the SSID as the salt
                $salt = $func_args[1] ?? $this->password_default_salt;

                // RFC2898#section-4.2 uses 1,000 iterations by default
                // WPA and WPA2 use 4,096.
                $count = $func_args[2] ?? 1000;

                // Keylength
                if (isset($func_args[3])) {
                    if ($func_args[3] <= 0) {
                        throw new LengthException('Derived key length cannot be longer 0 or less');
                    }
                    $dkLen = $func_args[3];
                } else {
                    $key_length = $this->explicit_key_length ?? $this->key_length;
                    $dkLen = $method == 'pbkdf1' ? 2 * $key_length : $key_length;
                }

                switch (true) {
                    case $method == 'pkcs12':
                        /*
                         In this specification, however, all passwords are created from
                         BMPStrings with a NULL terminator.  This means that each character in
                         the original BMPString is encoded in 2 bytes in big-endian format
                         (most-significant byte first).  There are no Unicode byte order
                         marks.  The 2 bytes produced from the last character in the BMPString
                         are followed by 2 additional bytes with the value 0x00.

                         -- https://tools.ietf.org/html/rfc7292#appendix-B.1
                         */
                        $password = "\0" . chunk_split($password, 1, "\0") . "\0";

                        /*
                         This standard specifies 3 different values for the ID byte mentioned
                         above:

                         1.  If ID=1, then the pseudorandom bits being produced are to be used
                             as key material for performing encryption or decryption.

                         2.  If ID=2, then the pseudorandom bits being produced are to be used
                             as an IV (Initial Value) for encryption or decryption.

                         3.  If ID=3, then the pseudorandom bits being produced are to be used
                             as an integrity key for MACing.
                         */
                        // Construct a string, D (the "diversifier"), by concatenating v/8
                        // copies of ID.
                        $blockLength = $hashObj->getBlockLengthInBytes();
                        $d1 = str_repeat(chr(1), $blockLength);
                        $d2 = str_repeat(chr(2), $blockLength);
                        $s = '';
                        if (strlen($salt)) {
                            while (strlen($s) < $blockLength) {
                                $s .= $salt;
                            }
                        }
                        $s = substr($s, 0, $blockLength);

                        $p = '';
                        if (strlen($password)) {
                            while (strlen($p) < $blockLength) {
                                $p .= $password;
                            }
                        }
                        $p = substr($p, 0, $blockLength);

                        $i = $s . $p;

                        $this->setKey(self::pkcs12helper($dkLen, $hashObj, $i, $d1, $count));
                        if ($this->usesIV()) {
                            $this->setIV(self::pkcs12helper($this->block_size, $hashObj, $i, $d2, $count));
                        }

                        return;
                    case $method == 'pbkdf1':
                        if ($dkLen > $hashObj->getLengthInBytes()) {
                            throw new LengthException('Derived key length cannot be longer than the hash length');
                        }
                        $t = $password . $salt;
                        for ($i = 0; $i < $count; ++$i) {
                            $t = $hashObj->hash($t);
                        }
                        $key = substr($t, 0, $dkLen);

                        $this->setKey(substr($key, 0, $dkLen >> 1));
                        if ($this->usesIV()) {
                            $this->setIV(substr($key, $dkLen >> 1));
                        }

                        return;
                    case !in_array($hash, hash_algos()):
                        $i = 1;
                        $hashObj->setKey($password);
                        while (strlen($key) < $dkLen) {
                            $f = $u = $hashObj->hash($salt . pack('N', $i++));
                            for ($j = 2; $j <= $count; ++$j) {
                                $u = $hashObj->hash($u);
                                $f ^= $u;
                            }
                            $key .= $f;
                        }
                        $key = substr($key, 0, $dkLen);
                        break;
                    default:
                        $key = hash_pbkdf2($hash, $password, $salt, $count, $dkLen, true);
                }
                break;
            default:
                throw new UnsupportedAlgorithmException($method . ' is not a supported password hashing method');
        }

        $this->setKey($key);
    }

    /**
     * Encrypts a message.
     *
     * $plaintext will be padded with additional bytes such that it's length is a multiple of the block size. Other cipher
     * implementations may or may not pad in the same manner.  Other common approaches to padding and the reasons why it's
     * necessary are discussed in the following
     * URL:
     *
     * {@link http://www.di-mgt.com.au/cryptopad.html http://www.di-mgt.com.au/cryptopad.html}
     *
     * An alternative to padding is to, separately, send the length of the file.  This is what SSH, in fact, does.
     * strlen($plaintext) will still need to be a multiple of the block size, however, arbitrary values can be added to make it that
     * length.
     *
     * {@internal Could, but not must, extend by the child Crypt_* class}
     *
     * @see self::decrypt()
     */
    public function encrypt(#[\SensitiveParameter] string $plaintext): string
    {
        if ($this->paddable) {
            $plaintext = $this->pad($plaintext);
        }

        $this->setup();

        if ($this->mode == self::MODE_GCM) {
            $oldIV = $this->iv;
            Strings::increment_str($this->iv);
            $cipher = new static('ctr');
            $cipher->setKey($this->key);
            $cipher->setIV($this->iv);
            $ciphertext = $cipher->encrypt($plaintext);

            $s = $this->ghash(
                self::nullPad128($this->aad) .
                self::nullPad128($ciphertext) .
                self::len64($this->aad) .
                self::len64($ciphertext)
            );
            $cipher->encryptIV = $this->iv = $this->encryptIV = $this->decryptIV = $oldIV;
            $this->newtag = $cipher->encrypt($s);
            return $ciphertext;
        }

        if (isset($this->poly1305Key)) {
            $cipher = clone $this;
            unset($cipher->poly1305Key);
            $this->usePoly1305 = false;
            $ciphertext = $cipher->encrypt($plaintext);
            $this->newtag = $this->poly1305($ciphertext);
            return $ciphertext;
        }

        if ($this->engine === self::ENGINE_OPENSSL) {
            switch ($this->mode) {
                case self::MODE_STREAM:
                    return openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA);
                case self::MODE_ECB:
                    return openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING);
                case self::MODE_CBC:
                    $result = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $this->encryptIV);
                    if ($this->continuousBuffer) {
                        $this->encryptIV = substr($result, -$this->block_size);
                    }
                    return $result;
                case self::MODE_CTR:
                    return $this->openssl_ctr_process($plaintext, $this->encryptIV, $this->enbuffer);
                case self::MODE_CFB:
                    // cfb loosely routines inspired by openssl's:
                    // {@link http://cvs.openssl.org/fileview?f=openssl/crypto/modes/cfb128.c&v=1.3.2.2.2.1}
                    $ciphertext = '';
                    if ($this->continuousBuffer) {
                        $iv = &$this->encryptIV;
                        $pos = &$this->enbuffer['pos'];
                    } else {
                        $iv = $this->encryptIV;
                        $pos = 0;
                    }
                    $len = strlen($plaintext);
                    if ($pos) {
                        $orig_pos = $pos;
                        $max = $this->block_size - $pos;
                        if ($len >= $max) {
                            $i = $max;
                            $len -= $max;
                            $pos = 0;
                        } else {
                            $i = $len;
                            $pos += $len;
                            $len = 0;
                        }
                        // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $blocksize
                        $ciphertext = substr($iv, $orig_pos) ^ $plaintext;
                        $iv = substr_replace($iv, $ciphertext, $orig_pos, $i);
                        $plaintext = substr($plaintext, $i);
                    }

                    $overflow = $len % $this->block_size;

                    if ($overflow) {
                        $ciphertext .= openssl_encrypt(substr($plaintext, 0, -$overflow) . str_repeat("\0", $this->block_size), $this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA, $iv);
                        $iv = Strings::pop($ciphertext, $this->block_size);

                        $block = $iv ^ substr($plaintext, -$overflow);
                        $iv = substr_replace($iv, $block, 0, $overflow);
                        $ciphertext .= $block;
                        $pos = $overflow;
                    } elseif ($len) {
                        $ciphertext = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA, $iv);
                        $iv = substr($ciphertext, -$this->block_size);
                    }

                    return $ciphertext;
                case self::MODE_CFB8:
                    $ciphertext = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA, $this->encryptIV);
                    if ($this->continuousBuffer) {
                        if (($len = strlen($ciphertext)) >= $this->block_size) {
                            $this->encryptIV = substr($ciphertext, -$this->block_size);
                        } else {
                            $this->encryptIV = substr($this->encryptIV, $len - $this->block_size) . substr($ciphertext, -$len);
                        }
                    }
                    return $ciphertext;
                case self::MODE_OFB8:
                    $ciphertext = '';
                    $len = strlen($plaintext);
                    $iv = $this->encryptIV;

                    for ($i = 0; $i < $len; ++$i) {
                        $xor = openssl_encrypt($iv, $this->cipher_name_openssl_ecb, $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $this->decryptIV);
                        $ciphertext .= $plaintext[$i] ^ $xor;
                        /** @psalm-suppress InvalidArrayAccess */
                        $iv = substr($iv, 1) . $xor[0];
                    }

                    if ($this->continuousBuffer) {
                        $this->encryptIV = $iv;
                    }
                    return $ciphertext;
                case self::MODE_OFB:
                    return $this->openssl_ofb_process($plaintext, $this->encryptIV, $this->enbuffer);
            }
        }

        if ($this->engine === self::ENGINE_EVAL) {
            $inline = $this->inline_crypt;
            return $inline('encrypt', $plaintext);
        }

        $buffer = &$this->enbuffer;
        $block_size = $this->block_size;
        $ciphertext = '';
        switch ($this->mode) {
            case self::MODE_ECB:
                for ($i = 0; $i < strlen($plaintext); $i += $block_size) {
                    $ciphertext .= $this->encryptBlock(substr($plaintext, $i, $block_size));
                }
                break;
            case self::MODE_CBC:
                $xor = $this->encryptIV;
                for ($i = 0; $i < strlen($plaintext); $i += $block_size) {
                    $block = substr($plaintext, $i, $block_size);
                    $block = $this->encryptBlock($block ^ $xor);
                    $xor = $block;
                    $ciphertext .= $block;
                }
                if ($this->continuousBuffer) {
                    $this->encryptIV = $xor;
                }
                break;
            case self::MODE_CTR:
                $xor = $this->encryptIV;
                if (strlen($buffer['ciphertext'])) {
                    for ($i = 0; $i < strlen($plaintext); $i += $block_size) {
                        $block = substr($plaintext, $i, $block_size);
                        if (strlen($block) > strlen($buffer['ciphertext'])) {
                            $buffer['ciphertext'] .= $this->encryptBlock($xor);
                            Strings::increment_str($xor);
                        }
                        $key = Strings::shift($buffer['ciphertext'], $block_size);
                        $ciphertext .= $block ^ $key;
                    }
                } else {
                    for ($i = 0; $i < strlen($plaintext); $i += $block_size) {
                        $block = substr($plaintext, $i, $block_size);
                        $key = $this->encryptBlock($xor);
                        Strings::increment_str($xor);
                        $ciphertext .= $block ^ $key;
                    }
                }
                if ($this->continuousBuffer) {
                    $this->encryptIV = $xor;
                    if ($start = strlen($plaintext) % $block_size) {
                        $buffer['ciphertext'] = substr($key, $start) . $buffer['ciphertext'];
                    }
                }
                break;
            case self::MODE_CFB:
                // cfb loosely routines inspired by openssl's:
                // {@link http://cvs.openssl.org/fileview?f=openssl/crypto/modes/cfb128.c&v=1.3.2.2.2.1}
                if ($this->continuousBuffer) {
                    $iv = &$this->encryptIV;
                    $pos = &$buffer['pos'];
                } else {
                    $iv = $this->encryptIV;
                    $pos = 0;
                }
                $len = strlen($plaintext);
                $i = 0;
                if ($pos) {
                    $orig_pos = $pos;
                    $max = $block_size - $pos;
                    if ($len >= $max) {
                        $i = $max;
                        $len -= $max;
                        $pos = 0;
                    } else {
                        $i = $len;
                        $pos += $len;
                        $len = 0;
                    }
                    // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $blocksize
                    $ciphertext = substr($iv, $orig_pos) ^ $plaintext;
                    $iv = substr_replace($iv, $ciphertext, $orig_pos, $i);
                }
                while ($len >= $block_size) {
                    $iv = $this->encryptBlock($iv) ^ substr($plaintext, $i, $block_size);
                    $ciphertext .= $iv;
                    $len -= $block_size;
                    $i += $block_size;
                }
                if ($len) {
                    $iv = $this->encryptBlock($iv);
                    $block = $iv ^ substr($plaintext, $i);
                    $iv = substr_replace($iv, $block, 0, $len);
                    $ciphertext .= $block;
                    $pos = $len;
                }
                break;
            case self::MODE_CFB8:
                $ciphertext = '';
                $len = strlen($plaintext);
                $iv = $this->encryptIV;

                for ($i = 0; $i < $len; ++$i) {
                    $ciphertext .= ($c = $plaintext[$i] ^ $this->encryptBlock($iv));
                    $iv = substr($iv, 1) . $c;
                }

                if ($this->continuousBuffer) {
                    if ($len >= $block_size) {
                        $this->encryptIV = substr($ciphertext, -$block_size);
                    } else {
                        $this->encryptIV = substr($this->encryptIV, $len - $block_size) . substr($ciphertext, -$len);
                    }
                }
                break;
            case self::MODE_OFB8:
                $ciphertext = '';
                $len = strlen($plaintext);
                $iv = $this->encryptIV;

                for ($i = 0; $i < $len; ++$i) {
                    $xor = $this->encryptBlock($iv);
                    $ciphertext .= $plaintext[$i] ^ $xor;
                    $iv = substr($iv, 1) . $xor[0];
                }

                if ($this->continuousBuffer) {
                    $this->encryptIV = $iv;
                }
                break;
            case self::MODE_OFB:
                $xor = $this->encryptIV;
                if (strlen($buffer['xor'])) {
                    for ($i = 0; $i < strlen($plaintext); $i += $block_size) {
                        $block = substr($plaintext, $i, $block_size);
                        if (strlen($block) > strlen($buffer['xor'])) {
                            $xor = $this->encryptBlock($xor);
                            $buffer['xor'] .= $xor;
                        }
                        $key = Strings::shift($buffer['xor'], $block_size);
                        $ciphertext .= $block ^ $key;
                    }
                } else {
                    for ($i = 0; $i < strlen($plaintext); $i += $block_size) {
                        $xor = $this->encryptBlock($xor);
                        $ciphertext .= substr($plaintext, $i, $block_size) ^ $xor;
                    }
                    $key = $xor;
                }
                if ($this->continuousBuffer) {
                    $this->encryptIV = $xor;
                    if ($start = strlen($plaintext) % $block_size) {
                        $buffer['xor'] = substr($key, $start) . $buffer['xor'];
                    }
                }
                break;
            case self::MODE_STREAM:
                $ciphertext = $this->encryptBlock($plaintext);
                break;
        }

        return $ciphertext;
    }

    /**
     * Decrypts a message.
     *
     * If strlen($ciphertext) is not a multiple of the block size, null bytes will be added to the end of the string until
     * it is.
     *
     * {@internal Could, but not must, extend by the child Crypt_* class}
     *
     * @see self::encrypt()
     */
    public function decrypt(string $ciphertext): string
    {
        if ($this->paddable && strlen($ciphertext) % $this->block_size) {
            throw new BadDecryptionException('The ciphertext length (' . strlen($ciphertext) . ') needs to be a multiple of the block size (' . $this->block_size . ')');
        }
        $this->setup();

        if ($this->mode == self::MODE_GCM || isset($this->poly1305Key)) {
            if (!isset($this->oldtag)) {
                throw new InvalidStateException('Authentication Tag has not been set - call setTag() first');
            }

            if (isset($this->poly1305Key)) {
                $newtag = $this->poly1305($ciphertext);
            } else {
                $oldIV = $this->iv;
                Strings::increment_str($this->iv);
                $cipher = new static('ctr');
                $cipher->setKey($this->key);
                $cipher->setIV($this->iv);
                $plaintext = $cipher->decrypt($ciphertext);

                $s = $this->ghash(
                    self::nullPad128($this->aad) .
                    self::nullPad128($ciphertext) .
                    self::len64($this->aad) .
                    self::len64($ciphertext)
                );
                $cipher->encryptIV = $this->iv = $this->encryptIV = $this->decryptIV = $oldIV;
                $newtag = $cipher->encrypt($s);
            }
            if ($this->oldtag != substr($newtag, 0, strlen($newtag))) {
                $cipher = clone $this;
                unset($cipher->poly1305Key);
                $this->usePoly1305 = false;
                $plaintext = $cipher->decrypt($ciphertext);
                $this->oldtag = null;
                throw new BadDecryptionException('Derived authentication tag and supplied authentication tag do not match');
            }
            $this->oldtag = null;
            return $plaintext;
        }

        if ($this->engine === self::ENGINE_OPENSSL) {
            switch ($this->mode) {
                case self::MODE_STREAM:
                    $plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA);
                    break;
                case self::MODE_ECB:
                    $plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING);
                    break;
                case self::MODE_CBC:
                    $offset = $this->block_size;
                    $plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $this->decryptIV);
                    if ($this->continuousBuffer) {
                        $this->decryptIV = substr($ciphertext, -$offset, $this->block_size);
                    }
                    break;
                case self::MODE_CTR:
                    $plaintext = $this->openssl_ctr_process($ciphertext, $this->decryptIV, $this->debuffer);
                    break;
                case self::MODE_CFB:
                    // cfb loosely routines inspired by openssl's:
                    // {@link http://cvs.openssl.org/fileview?f=openssl/crypto/modes/cfb128.c&v=1.3.2.2.2.1}
                    $plaintext = '';
                    if ($this->continuousBuffer) {
                        $iv = &$this->decryptIV;
                        $pos = &$this->debuffer['pos'];
                    } else {
                        $iv = $this->decryptIV;
                        $pos = 0;
                    }
                    $len = strlen($ciphertext);
                    if ($pos) {
                        $orig_pos = $pos;
                        $max = $this->block_size - $pos;
                        if ($len >= $max) {
                            $i = $max;
                            $len -= $max;
                            $pos = 0;
                        } else {
                            $i = $len;
                            $pos += $len;
                            $len = 0;
                        }
                        // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $this->blocksize
                        $plaintext = substr($iv, $orig_pos) ^ $ciphertext;
                        $iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i);
                        $ciphertext = substr($ciphertext, $i);
                    }
                    $overflow = $len % $this->block_size;
                    if ($overflow) {
                        $plaintext .= openssl_decrypt(substr($ciphertext, 0, -$overflow), $this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA, $iv);
                        if ($len - $overflow) {
                            $iv = substr($ciphertext, -$overflow - $this->block_size, -$overflow);
                        }
                        $iv = openssl_encrypt(str_repeat("\0", $this->block_size), $this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA, $iv);
                        $plaintext .= $iv ^ substr($ciphertext, -$overflow);
                        $iv = substr_replace($iv, substr($ciphertext, -$overflow), 0, $overflow);
                        $pos = $overflow;
                    } elseif ($len) {
                        $plaintext .= openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA, $iv);
                        $iv = substr($ciphertext, -$this->block_size);
                    }
                    break;
                case self::MODE_CFB8:
                    $plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA, $this->decryptIV);
                    if ($this->continuousBuffer) {
                        if (($len = strlen($ciphertext)) >= $this->block_size) {
                            $this->decryptIV = substr($ciphertext, -$this->block_size);
                        } else {
                            $this->decryptIV = substr($this->decryptIV, $len - $this->block_size) . substr($ciphertext, -$len);
                        }
                    }
                    break;
                case self::MODE_OFB8:
                    $plaintext = '';
                    $len = strlen($ciphertext);
                    $iv = $this->decryptIV;

                    for ($i = 0; $i < $len; ++$i) {
                        $xor = openssl_encrypt($iv, $this->cipher_name_openssl_ecb, $this->key, OPENSSL_RAW_DATA, $this->decryptIV);
                        $plaintext .= $ciphertext[$i] ^ $xor;
                        /** @psalm-suppress InvalidArrayAccess */
                        $iv = substr($iv, 1) . $xor[0];
                    }

                    if ($this->continuousBuffer) {
                        $this->decryptIV = $iv;
                    }
                    break;
                case self::MODE_OFB:
                    $plaintext = $this->openssl_ofb_process($ciphertext, $this->decryptIV, $this->debuffer);
            }

            return $this->paddable ? $this->unpad($plaintext) : $plaintext;
        }

        if ($this->engine === self::ENGINE_EVAL) {
            $inline = $this->inline_crypt;
            return $inline('decrypt', $ciphertext);
        }

        $block_size = $this->block_size;

        $buffer = &$this->debuffer;
        $plaintext = '';
        switch ($this->mode) {
            case self::MODE_ECB:
                for ($i = 0; $i < strlen($ciphertext); $i += $block_size) {
                    $plaintext .= $this->decryptBlock(substr($ciphertext, $i, $block_size));
                }
                break;
            case self::MODE_CBC:
                $xor = $this->decryptIV;
                for ($i = 0; $i < strlen($ciphertext); $i += $block_size) {
                    $block = substr($ciphertext, $i, $block_size);
                    $plaintext .= $this->decryptBlock($block) ^ $xor;
                    $xor = $block;
                }
                if ($this->continuousBuffer) {
                    $this->decryptIV = $xor;
                }
                break;
            case self::MODE_CTR:
                $xor = $this->decryptIV;
                if (strlen($buffer['ciphertext'])) {
                    for ($i = 0; $i < strlen($ciphertext); $i += $block_size) {
                        $block = substr($ciphertext, $i, $block_size);
                        if (strlen($block) > strlen($buffer['ciphertext'])) {
                            $buffer['ciphertext'] .= $this->encryptBlock($xor);
                            Strings::increment_str($xor);
                        }
                        $key = Strings::shift($buffer['ciphertext'], $block_size);
                        $plaintext .= $block ^ $key;
                    }
                } else {
                    for ($i = 0; $i < strlen($ciphertext); $i += $block_size) {
                        $block = substr($ciphertext, $i, $block_size);
                        $key = $this->encryptBlock($xor);
                        Strings::increment_str($xor);
                        $plaintext .= $block ^ $key;
                    }
                }
                if ($this->continuousBuffer) {
                    $this->decryptIV = $xor;
                    if ($start = strlen($ciphertext) % $block_size) {
                        $buffer['ciphertext'] = substr($key, $start) . $buffer['ciphertext'];
                    }
                }
                break;
            case self::MODE_CFB:
                if ($this->continuousBuffer) {
                    $iv = &$this->decryptIV;
                    $pos = &$buffer['pos'];
                } else {
                    $iv = $this->decryptIV;
                    $pos = 0;
                }
                $len = strlen($ciphertext);
                $i = 0;
                if ($pos) {
                    $orig_pos = $pos;
                    $max = $block_size - $pos;
                    if ($len >= $max) {
                        $i = $max;
                        $len -= $max;
                        $pos = 0;
                    } else {
                        $i = $len;
                        $pos += $len;
                        $len = 0;
                    }
                    // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $blocksize
                    $plaintext = substr($iv, $orig_pos) ^ $ciphertext;
                    $iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i);
                }
                while ($len >= $block_size) {
                    $iv = $this->encryptBlock($iv);
                    $cb = substr($ciphertext, $i, $block_size);
                    $plaintext .= $iv ^ $cb;
                    $iv = $cb;
                    $len -= $block_size;
                    $i += $block_size;
                }
                if ($len) {
                    $iv = $this->encryptBlock($iv);
                    $plaintext .= $iv ^ substr($ciphertext, $i);
                    $iv = substr_replace($iv, substr($ciphertext, $i), 0, $len);
                    $pos = $len;
                }
                break;
            case self::MODE_CFB8:
                $plaintext = '';
                $len = strlen($ciphertext);
                $iv = $this->decryptIV;

                for ($i = 0; $i < $len; ++$i) {
                    $plaintext .= $ciphertext[$i] ^ $this->encryptBlock($iv);
                    $iv = substr($iv, 1) . $ciphertext[$i];
                }

                if ($this->continuousBuffer) {
                    if ($len >= $block_size) {
                        $this->decryptIV = substr($ciphertext, -$block_size);
                    } else {
                        $this->decryptIV = substr($this->decryptIV, $len - $block_size) . substr($ciphertext, -$len);
                    }
                }
                break;
            case self::MODE_OFB8:
                $plaintext = '';
                $len = strlen($ciphertext);
                $iv = $this->decryptIV;

                for ($i = 0; $i < $len; ++$i) {
                    $xor = $this->encryptBlock($iv);
                    $plaintext .= $ciphertext[$i] ^ $xor;
                    $iv = substr($iv, 1) . $xor[0];
                }

                if ($this->continuousBuffer) {
                    $this->decryptIV = $iv;
                }
                break;
            case self::MODE_OFB:
                $xor = $this->decryptIV;
                if (strlen($buffer['xor'])) {
                    for ($i = 0; $i < strlen($ciphertext); $i += $block_size) {
                        $block = substr($ciphertext, $i, $block_size);
                        if (strlen($block) > strlen($buffer['xor'])) {
                            $xor = $this->encryptBlock($xor);
                            $buffer['xor'] .= $xor;
                        }
                        $key = Strings::shift($buffer['xor'], $block_size);
                        $plaintext .= $block ^ $key;
                    }
                } else {
                    for ($i = 0; $i < strlen($ciphertext); $i += $block_size) {
                        $xor = $this->encryptBlock($xor);
                        $plaintext .= substr($ciphertext, $i, $block_size) ^ $xor;
                    }
                    $key = $xor;
                }
                if ($this->continuousBuffer) {
                    $this->decryptIV = $xor;
                    if ($start = strlen($ciphertext) % $block_size) {
                        $buffer['xor'] = substr($key, $start) . $buffer['xor'];
                    }
                }
                break;
            case self::MODE_STREAM:
                $plaintext = $this->decryptBlock($ciphertext);
                break;
        }
        return $this->paddable ? $this->unpad($plaintext) : $plaintext;
    }

    /**
     * Get the authentication tag
     *
     * Only used in GCM or Poly1305 mode
     *
     * @see self::encrypt()
     */
    public function getTag(int $length = 16): string
    {
        if ($this->mode != self::MODE_GCM && !$this->usePoly1305) {
            throw new BadMethodCallException('Authentication tags are only utilized in GCM mode or with Poly1305');
        }

        if (!isset($this->newtag)) {
            throw new BadMethodCallException('A tag can only be returned after a round of encryption has been performed');
        }

        // the tag is 128-bits. it can't be greater than 16 bytes because that's bigger than the tag is. if it
        // were 0 you might as well be doing CTR and less than 4 provides minimal security that could be trivially
        // easily brute forced.
        // see https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf#page=36
        // for more info
        if ($length < 4 || $length > 16) {
            throw new LengthException('The authentication tag must be between 4 and 16 bytes long');
        }

        return $length == 16 ?
            $this->newtag :
            substr($this->newtag, 0, $length);
    }

    /**
     * Sets the authentication tag
     *
     * Only used in GCM mode and only when decrypting data and only when decrypting data
     *
     * @see self::decrypt()
     */
    public function setTag(string $tag): void
    {
        if ($this->mode != self::MODE_GCM && !$this->usePoly1305) {
            throw new BadMethodCallException('Authentication tags are only utilized in GCM mode or with Poly1305');
        }

        $length = strlen($tag);
        if ($length < 4 || $length > 16) {
            throw new LengthException('The authentication tag must be between 4 and 16 bytes long');
        }
        $this->oldtag = $tag;
    }

    /**
     * OpenSSL CTR Processor
     *
     * PHP's OpenSSL bindings do not operate in continuous mode so we'll wrap around it. Since the keystream
     * for CTR is the same for both encrypting and decrypting this function is re-used by both SymmetricKey::encrypt()
     * and SymmetricKey::decrypt(). Also, OpenSSL doesn't implement CTR for all of it's symmetric ciphers so this
     * function will emulate CTR with ECB when necessary.
     *
     * @see self::encrypt()
     * @see self::decrypt()
     */
    private function openssl_ctr_process(string $plaintext, string &$encryptIV, array &$buffer): string
    {
        $ciphertext = '';

        $block_size = $this->block_size;
        $key = $this->key;

        if ($this->openssl_emulate_ctr) {
            $xor = $encryptIV;
            if (strlen($buffer['ciphertext'])) {
                for ($i = 0; $i < strlen($plaintext); $i += $block_size) {
                    $block = substr($plaintext, $i, $block_size);
                    if (strlen($block) > strlen($buffer['ciphertext'])) {
                        $buffer['ciphertext'] .= openssl_encrypt($xor, $this->cipher_name_openssl_ecb, $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING);
                    }
                    Strings::increment_str($xor);
                    $otp = Strings::shift($buffer['ciphertext'], $block_size);
                    $ciphertext .= $block ^ $otp;
                }
            } else {
                for ($i = 0; $i < strlen($plaintext); $i += $block_size) {
                    $block = substr($plaintext, $i, $block_size);
                    $otp = openssl_encrypt($xor, $this->cipher_name_openssl_ecb, $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING);
                    Strings::increment_str($xor);
                    $ciphertext .= $block ^ $otp;
                }
            }
            if ($this->continuousBuffer) {
                $encryptIV = $xor;
                if ($start = strlen($plaintext) % $block_size) {
                    $buffer['ciphertext'] = substr($key, $start) . $buffer['ciphertext'];
                }
            }

            return $ciphertext;
        }

        if (strlen($buffer['ciphertext'])) {
            $ciphertext = $plaintext ^ Strings::shift($buffer['ciphertext'], strlen($plaintext));
            $plaintext = substr($plaintext, strlen($ciphertext));

            if (!strlen($plaintext)) {
                return $ciphertext;
            }
        }

        $overflow = strlen($plaintext) % $block_size;
        if ($overflow) {
            $plaintext2 = Strings::pop($plaintext, $overflow); // ie. trim $plaintext to a multiple of $block_size and put rest of $plaintext in $plaintext2
            $encrypted = openssl_encrypt($plaintext . str_repeat("\0", $block_size), $this->cipher_name_openssl, $key, OPENSSL_RAW_DATA, $encryptIV);
            $temp = Strings::pop($encrypted, $block_size);
            $ciphertext .= $encrypted . ($plaintext2 ^ $temp);
            if ($this->continuousBuffer) {
                $buffer['ciphertext'] = substr($temp, $overflow);
                $encryptIV = $temp;
            }
        } elseif (!strlen($buffer['ciphertext'])) {
            $ciphertext .= openssl_encrypt($plaintext . str_repeat("\0", $block_size), $this->cipher_name_openssl, $key, OPENSSL_RAW_DATA, $encryptIV);
            $temp = Strings::pop($ciphertext, $block_size);
            if ($this->continuousBuffer) {
                $encryptIV = $temp;
            }
        }
        if ($this->continuousBuffer) {
            $encryptIV = openssl_decrypt($encryptIV, $this->cipher_name_openssl_ecb, $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING);
            if ($overflow) {
                Strings::increment_str($encryptIV);
            }
        }

        return $ciphertext;
    }

    /**
     * OpenSSL OFB Processor
     *
     * PHP's OpenSSL bindings do not operate in continuous mode so we'll wrap around it. Since the keystream
     * for OFB is the same for both encrypting and decrypting this function is re-used by both SymmetricKey::encrypt()
     * and SymmetricKey::decrypt().
     *
     * @see self::encrypt()
     * @see self::decrypt()
     */
    private function openssl_ofb_process(string $plaintext, string &$encryptIV, array &$buffer): string
    {
        if (strlen($buffer['xor'])) {
            $ciphertext = $plaintext ^ $buffer['xor'];
            $buffer['xor'] = substr($buffer['xor'], strlen($ciphertext));
            $plaintext = substr($plaintext, strlen($ciphertext));
        } else {
            $ciphertext = '';
        }

        $block_size = $this->block_size;

        $len = strlen($plaintext);
        $key = $this->key;
        $overflow = $len % $block_size;

        if (strlen($plaintext)) {
            if ($overflow) {
                $ciphertext .= openssl_encrypt(substr($plaintext, 0, -$overflow) . str_repeat("\0", $block_size), $this->cipher_name_openssl, $key, OPENSSL_RAW_DATA, $encryptIV);
                $xor = Strings::pop($ciphertext, $block_size);
                if ($this->continuousBuffer) {
                    $encryptIV = $xor;
                }
                $ciphertext .= Strings::shift($xor, $overflow) ^ substr($plaintext, -$overflow);
                if ($this->continuousBuffer) {
                    $buffer['xor'] = $xor;
                }
            } else {
                $ciphertext = openssl_encrypt($plaintext, $this->cipher_name_openssl, $key, OPENSSL_RAW_DATA, $encryptIV);
                if ($this->continuousBuffer) {
                    $encryptIV = substr($ciphertext, -$block_size) ^ substr($plaintext, -$block_size);
                }
            }
        }

        return $ciphertext;
    }

    /**
     * phpseclib <-> OpenSSL Mode Mapper
     *
     * May need to be overwritten by classes extending this one in some cases
     */
    protected function openssl_translate_mode(): ?string
    {
        return match ($this->mode) {
            self::MODE_ECB => 'ecb',
            self::MODE_CBC => 'cbc',
            self::MODE_CTR, self::MODE_GCM => 'ctr',
            self::MODE_CFB => 'cfb',
            self::MODE_CFB8 => 'cfb8',
            self::MODE_OFB => 'ufb',
            default => null
        };
    }

    /**
     * Pad "packets".
     *
     * Block ciphers working by encrypting between their specified [$this->]block_size at a time
     * If you ever need to encrypt or decrypt something that isn't of the proper length, it becomes necessary to
     * pad the input so that it is of the proper length.
     *
     * Padding is enabled by default.  Sometimes, however, it is undesirable to pad strings.  Such is the case in SSH,
     * where "packets" are padded with random bytes before being encrypted.  Unpad these packets and you risk stripping
     * away characters that shouldn't be stripped away. (SSH knows how many bytes are added because the length is
     * transmitted separately)
     *
     * @see self::disablePadding()
     */
    public function enablePadding(): void
    {
        $this->padding = true;
    }

    /**
     * Do not pad packets.
     *
     * @see self::enablePadding()
     */
    public function disablePadding(): void
    {
        $this->padding = false;
    }

    /**
     * Treat consecutive "packets" as if they are a continuous buffer.
     *
     * Say you have a 32-byte plaintext $plaintext.  Using the default behavior, the two following code snippets
     * will yield different outputs:
     *
     * <code>
     *    echo $rijndael->encrypt(substr($plaintext,  0, 16));
     *    echo $rijndael->encrypt(substr($plaintext, 16, 16));
     * </code>
     * <code>
     *    echo $rijndael->encrypt($plaintext);
     * </code>
     *
     * The solution is to enable the continuous buffer.  Although this will resolve the above discrepancy, it creates
     * another, as demonstrated with the following:
     *
     * <code>
     *    $rijndael->encrypt(substr($plaintext, 0, 16));
     *    echo $rijndael->decrypt($rijndael->encrypt(substr($plaintext, 16, 16)));
     * </code>
     * <code>
     *    echo $rijndael->decrypt($rijndael->encrypt(substr($plaintext, 16, 16)));
     * </code>
     *
     * With the continuous buffer disabled, these would yield the same output.  With it enabled, they yield different
     * outputs.  The reason is due to the fact that the initialization vector's change after every encryption /
     * decryption round when the continuous buffer is enabled.  When it's disabled, they remain constant.
     *
     * Put another way, when the continuous buffer is enabled, the state of the \phpseclib4\Crypt\*() object changes after each
     * encryption / decryption round, whereas otherwise, it'd remain constant.  For this reason, it's recommended that
     * continuous buffers not be used.  They do offer better security and are, in fact, sometimes required (SSH uses them),
     * however, they are also less intuitive and more likely to cause you problems.
     *
     * {@internal Could, but not must, extend by the child Crypt_* class}
     *
     * @see self::disableContinuousBuffer()
     */
    public function enableContinuousBuffer(): void
    {
        if ($this->mode == self::MODE_ECB) {
            return;
        }

        if ($this->mode == self::MODE_GCM) {
            throw new BadMethodCallException('This mode does not run in continuous mode');
        }

        $this->continuousBuffer = true;

        $this->setEngine();
    }

    /**
     * Treat consecutive packets as if they are a discontinuous buffer.
     *
     * The default behavior.
     *
     * {@internal Could, but not must, extend by the child Crypt_* class}
     *
     * @see self::enableContinuousBuffer()
     */
    public function disableContinuousBuffer(): void
    {
        if ($this->mode == self::MODE_ECB) {
            return;
        }
        if (!$this->continuousBuffer) {
            return;
        }

        $this->continuousBuffer = false;

        $this->setEngine();
    }

    /**
     * Test for engine validity
     *
     * @see self::__construct()
     */
    protected function isValidEngineHelper(int $engine): bool
    {
        switch ($engine) {
            case self::ENGINE_OPENSSL:
                $this->openssl_emulate_ctr = false;
                $result = isset($this->cipher_name_openssl) &&
                          extension_loaded('openssl');
                if (!$result) {
                    return false;
                }

                $methods = openssl_get_cipher_methods();
                if (in_array($this->cipher_name_openssl, $methods)) {
                    return true;
                }
                // not all of openssl's symmetric cipher's support ctr. for those
                // that don't we'll emulate it
                switch ($this->mode) {
                    case self::MODE_CTR:
                        if (in_array($this->cipher_name_openssl_ecb, $methods)) {
                            $this->openssl_emulate_ctr = true;
                            return true;
                        }
                }
                return false;
            case self::ENGINE_EVAL:
                return method_exists($this, 'setupInlineCrypt');
            case self::ENGINE_INTERNAL:
                return true;
        }

        return false;
    }

    /**
     * Test for engine validity
     *
     * @see self::__construct()
     */
    public function isValidEngine(string $engine): bool
    {
        static $reverseMap;
        if (!isset($reverseMap)) {
            $reverseMap = array_map('strtolower', self::ENGINE_MAP);
            $reverseMap = array_flip($reverseMap);
        }
        $engine = strtolower($engine);
        if (!isset($reverseMap[$engine])) {
            return false;
        }

        return $this->isValidEngineHelper($reverseMap[$engine]);
    }

    /**
     * Sets the preferred crypt engine
     *
     * Currently, $engine could be:
     *
     * - libsodium[very fast]
     *
     * - OpenSSL  [very fast]
     *
     * - Eval     [slow]
     *
     * - PHP      [slowest]
     *
     * If the preferred crypt engine is not available the fastest available one will be used
     *
     * @see self::__construct()
     */
    public function setPreferredEngine(string $engine): void
    {
        static $reverseMap;
        if (!isset($reverseMap)) {
            $reverseMap = array_map('strtolower', self::ENGINE_MAP);
            $reverseMap = array_flip($reverseMap);
        }
        $engine = is_string($engine) ? strtolower($engine) : '';
        $this->preferredEngine = $reverseMap[$engine] ?? self::ENGINE_LIBSODIUM;

        $this->setEngine();
    }

    /**
     * Returns the engine currently being utilized
     *
     * @see self::setEngine()
     */
    public function getEngine(): string
    {
        $this->setEngine();
        return self::ENGINE_MAP[$this->engine];
    }

    /**
     * Sets the engine as appropriate
     *
     * @see self::__construct()
     */
    protected function setEngine(): void
    {
        $this->engine = 0;

        $candidateEngines = [
            self::ENGINE_LIBSODIUM,
            self::ENGINE_OPENSSL_AEAD,
            self::ENGINE_OPENSSL,
            self::ENGINE_EVAL,
        ];
        if (isset($this->preferredEngine)) {
            $temp = [$this->preferredEngine];
            $candidateEngines = array_merge(
                $temp,
                array_diff($candidateEngines, $temp)
            );
        }
        foreach ($candidateEngines as $engine) {
            if ($this->isValidEngineHelper($engine)) {
                $this->engine = $engine;
                break;
            }
        }
        if (!$this->engine) {
            $this->engine = self::ENGINE_INTERNAL;
        }

        $this->changed = $this->nonIVChanged = true;
    }

    /**
     * Encrypts a block
     *
     * Note: Must be extended by the child \phpseclib4\Crypt\* class
     */
    abstract protected function encryptBlock(string $in): string;

    /**
     * Decrypts a block
     *
     * Note: Must be extended by the child \phpseclib4\Crypt\* class
     */
    abstract protected function decryptBlock(string $in): string;

    /**
     * Setup the key (expansion)
     *
     * Only used if $engine == self::ENGINE_INTERNAL
     *
     * Note: Must extend by the child \phpseclib4\Crypt\* class
     *
     * @see self::setup()
     */
    abstract protected function setupKey(): void;

    /**
     * Setup the self::ENGINE_INTERNAL $engine
     *
     * (re)init, if necessary, the internal cipher $engine and flush all $buffers
     * Used (only) if $engine == self::ENGINE_INTERNAL
     *
     * _setup() will be called each time if $changed === true
     * typically this happens when using one or more of following public methods:
     *
     * - setKey()
     *
     * - setIV()
     *
     * - disableContinuousBuffer()
     *
     * - First run of encrypt() / decrypt() with no init-settings
     *
     * {@internal setup() is always called before en/decryption.}
     *
     * {@internal Could, but not must, extend by the child Crypt_* class}
     *
     * @see self::setKey()
     * @see self::setIV()
     * @see self::disableContinuousBuffer()
     */
    protected function setup(): void
    {
        if (!$this->changed) {
            return;
        }

        $this->changed = false;

        if ($this->usePoly1305 && !isset($this->poly1305Key) && method_exists($this, 'createPoly1305Key')) {
            $this->createPoly1305Key();
        }

        $this->enbuffer = $this->debuffer = ['ciphertext' => '', 'xor' => '', 'pos' => 0];
        //$this->newtag = $this->oldtag = false;

        if ($this->usesNonce()) {
            if (!isset($this->nonce)) {
                throw new InvalidStateException('No nonce has been defined - call setNonce() first');
            }
            if ($this->mode == self::MODE_GCM && !in_array($this->engine, [self::ENGINE_LIBSODIUM, self::ENGINE_OPENSSL_AEAD])) {
                $this->setupGCM();
            }
        } else {
            $this->iv = $this->origIV;
        }

        if (!isset($this->iv) && !in_array($this->mode, [self::MODE_STREAM, self::MODE_ECB])) {
            if ($this->mode != self::MODE_GCM || !in_array($this->engine, [self::ENGINE_LIBSODIUM, self::ENGINE_OPENSSL_AEAD])) {
                throw new InvalidStateException('No IV has been defined - call setIV() first');
            }
        }

        if (!isset($this->key)) {
            throw new InvalidStateException('No key has been defined - call setKey() first');
        }

        $this->encryptIV = $this->decryptIV = $this->iv;

        switch ($this->engine) {
            case self::ENGINE_INTERNAL:
                $this->setupKey();
                break;
            case self::ENGINE_EVAL:
                if ($this->nonIVChanged) {
                    $this->setupKey();
                    $this->setupInlineCrypt();
                }
        }

        $this->nonIVChanged = false;
    }

    /**
     * Pads a string
     *
     * Pads a string using the RSA PKCS padding standards so that its length is a multiple of the blocksize.
     * $this->block_size - (strlen($text) % $this->block_size) bytes are added, each of which is equal to
     * chr($this->block_size - (strlen($text) % $this->block_size)
     *
     * If padding is disabled and $text is not a multiple of the blocksize, the string will be padded regardless
     * and padding will, hence forth, be enabled.
     *
     * @see self::unpad()
     */
    protected function pad(string $text): string
    {
        $length = strlen($text);

        if (!$this->padding) {
            if ($length % $this->block_size == 0) {
                return $text;
            } else {
                throw new LengthException("The plaintext's length ($length) is not a multiple of the block size ({$this->block_size}). Try enabling padding.");
            }
        }

        $pad = $this->block_size - ($length % $this->block_size);

        return str_pad($text, $length + $pad, chr($pad));
    }

    /**
     * Unpads a string.
     *
     * If padding is enabled and the reported padding length is invalid the encryption key will be assumed to be wrong
     * and false will be returned.
     *
     * @see self::pad()
     */
    protected function unpad(string $text): string
    {
        if (!$this->padding) {
            return $text;
        }

        $length = ord($text[-1]);

        if (!$length | ($length > $this->block_size)) {
            throw new BadDecryptionException("The ciphertext has an invalid padding length ($length) compared to the block size ({$this->block_size})");
        }

        return substr($text, 0, -$length);
    }

    /**
     * Setup the performance-optimized function for de/encrypt()
     *
     * Stores the created (or existing) callback function-name
     * in $this->inline_crypt
     *
     * Internally for phpseclib developers:
     *
     *     _setupInlineCrypt() would be called only if:
     *
     *     - $this->engine === self::ENGINE_EVAL
     *
     *     - each time on _setup(), after(!) _setupKey()
     *
     *
     *     This ensures that _setupInlineCrypt() has always a
     *     full ready2go initializated internal cipher $engine state
     *     where, for example, the keys already expanded,
     *     keys/block_size calculated and such.
     *
     *     It is, each time if called, the responsibility of _setupInlineCrypt():
     *
     *     - to set $this->inline_crypt to a valid and fully working callback function
     *       as a (faster) replacement for encrypt() / decrypt()
     *
     *     - NOT to create unlimited callback functions (for memory reasons!)
     *       no matter how often _setupInlineCrypt() would be called. At some
     *       point of amount they must be generic re-useable.
     *
     *     - the code of _setupInlineCrypt() it self,
     *       and the generated callback code,
     *       must be, in following order:
     *       - 100% safe
     *       - 100% compatible to encrypt()/decrypt()
     *       - using only php5+ features/lang-constructs/php-extensions if
     *         compatibility (down to php4) or fallback is provided
     *       - readable/maintainable/understandable/commented and... not-cryptic-styled-code :-)
     *       - >= 10% faster than encrypt()/decrypt() [which is, by the way,
     *         the reason for the existence of _setupInlineCrypt() :-)]
     *       - memory-nice
     *       - short (as good as possible)
     *
     * Note: - _setupInlineCrypt() is using _createInlineCryptFunction() to create the full callback function code.
     *       - In case of using inline crypting, _setupInlineCrypt() must extend by the child \phpseclib4\Crypt\* class.
     *       - The following variable names are reserved:
     *         - $_*  (all variable names prefixed with an underscore)
     *         - $self (object reference to it self. Do not use $this, but $self instead)
     *         - $in (the content of $in has to en/decrypt by the generated code)
     *       - The callback function should not use the 'return' statement, but en/decrypt'ing the content of $in only
     *
     * {@internal If a Crypt_* class providing inline crypting it must extend _setupInlineCrypt()}
     *
     * @see self::setup()
     * @see self::createInlineCryptFunction()
     * @see self::encrypt()
     * @see self::decrypt()
     */
    //protected function setupInlineCrypt();

    /**
     * Creates the performance-optimized function for en/decrypt()
     *
     * Internally for phpseclib developers:
     *
     *    _createInlineCryptFunction():
     *
     *    - merge the $cipher_code [setup'ed by _setupInlineCrypt()]
     *      with the current [$this->]mode of operation code
     *
     *    - create the $inline function, which called by encrypt() / decrypt()
     *      as its replacement to speed up the en/decryption operations.
     *
     *    - return the name of the created $inline callback function
     *
     *    - used to speed up en/decryption
     *
     *
     *
     *    The main reason why can speed up things [up to 50%] this way are:
     *
     *    - using variables more effective then regular.
     *      (ie no use of expensive arrays but integers $k_0, $k_1 ...
     *      or even, for example, the pure $key[] values hardcoded)
     *
     *    - avoiding 1000's of function calls of ie _encryptBlock()
     *      but inlining the crypt operations.
     *      in the mode of operation for() loop.
     *
     *    - full loop unroll the (sometimes key-dependent) rounds
     *      avoiding this way ++$i counters and runtime-if's etc...
     *
     *    The basic code architectur of the generated $inline en/decrypt()
     *    lambda function, in pseudo php, is:
     *
     *    <code>
     *    +----------------------------------------------------------------------------------------------+
     *    | callback $inline = create_function:                                                          |
     *    | lambda_function_0001_crypt_ECB($action, $text)                                               |
     *    | {                                                                                            |
     *    |     INSERT PHP CODE OF:                                                                      |
     *    |     $cipher_code['init_crypt'];                  // general init code.                       |
     *    |                                                  // ie: $sbox'es declarations used for       |
     *    |                                                  //     encrypt and decrypt'ing.             |
     *    |                                                                                              |
     *    |     switch ($action) {                                                                       |
     *    |         case 'encrypt':                                                                      |
     *    |             INSERT PHP CODE OF:                                                              |
     *    |             $cipher_code['init_encrypt'];       // encrypt sepcific init code.               |
     *    |                                                    ie: specified $key or $box                |
     *    |                                                        declarations for encrypt'ing.         |
     *    |                                                                                              |
     *    |             foreach ($ciphertext) {                                                          |
     *    |                 $in = $block_size of $ciphertext;                                            |
     *    |                                                                                              |
     *    |                 INSERT PHP CODE OF:                                                          |
     *    |                 $cipher_code['encrypt_block'];  // encrypt's (string) $in, which is always:  |
     *    |                                                 // strlen($in) == $this->block_size          |
     *    |                                                 // here comes the cipher algorithm in action |
     *    |                                                 // for encryption.                           |
     *    |                                                 // $cipher_code['encrypt_block'] has to      |
     *    |                                                 // encrypt the content of the $in variable   |
     *    |                                                                                              |
     *    |                 $plaintext .= $in;                                                           |
     *    |             }                                                                                |
     *    |             return $plaintext;                                                               |
     *    |                                                                                              |
     *    |         case 'decrypt':                                                                      |
     *    |             INSERT PHP CODE OF:                                                              |
     *    |             $cipher_code['init_decrypt'];       // decrypt sepcific init code                |
     *    |                                                    ie: specified $key or $box                |
     *    |                                                        declarations for decrypt'ing.         |
     *    |             foreach ($plaintext) {                                                           |
     *    |                 $in = $block_size of $plaintext;                                             |
     *    |                                                                                              |
     *    |                 INSERT PHP CODE OF:                                                          |
     *    |                 $cipher_code['decrypt_block'];  // decrypt's (string) $in, which is always   |
     *    |                                                 // strlen($in) == $this->block_size          |
     *    |                                                 // here comes the cipher algorithm in action |
     *    |                                                 // for decryption.                           |
     *    |                                                 // $cipher_code['decrypt_block'] has to      |
     *    |                                                 // decrypt the content of the $in variable   |
     *    |                 $ciphertext .= $in;                                                          |
     *    |             }                                                                                |
     *    |             return $ciphertext;                                                              |
     *    |     }                                                                                        |
     *    | }                                                                                            |
     *    +----------------------------------------------------------------------------------------------+
     *    </code>
     *
     *    See also the \phpseclib4\Crypt\*::_setupInlineCrypt()'s for
     *    productive inline $cipher_code's how they works.
     *
     *    Structure of:
     *    <code>
     *    $cipher_code = [
     *        'init_crypt'    => (string) '', // optional
     *        'init_encrypt'  => (string) '', // optional
     *        'init_decrypt'  => (string) '', // optional
     *        'encrypt_block' => (string) '', // required
     *        'decrypt_block' => (string) ''  // required
     *    ];
     *    </code>
     *
     * @see self::decrypt()
     * @see self::setupInlineCrypt()
     * @see self::encrypt()
     * @psalm-suppress PossiblyUnusedParam
     */
    protected function createInlineCryptFunction(array $cipher_code): \Closure
    {
        $block_size = $this->block_size;

        // optional
        $init_crypt    = $cipher_code['init_crypt']    ?? '';
        $init_encrypt  = $cipher_code['init_encrypt']  ?? '';
        $init_decrypt  = $cipher_code['init_decrypt']  ?? '';
        // required
        $encrypt_block = $cipher_code['encrypt_block'];
        $decrypt_block = $cipher_code['decrypt_block'];

        // Generating mode of operation inline code,
        // merged with the $cipher_code algorithm
        // for encrypt- and decryption.
        switch ($this->mode) {
            case self::MODE_ECB:
                $encrypt = $init_encrypt . '
                    $_ciphertext = "";
                    $_plaintext_len = strlen($_text);

                    for ($_i = 0; $_i < $_plaintext_len; $_i+= ' . $block_size . ') {
                        $in = substr($_text, $_i, ' . $block_size . ');
                        ' . $encrypt_block . '
                        $_ciphertext.= $in;
                    }

                    return $_ciphertext;
                    ';

                $decrypt = $init_decrypt . '
                    $_plaintext = "";
                    $_text = str_pad($_text, strlen($_text) + (' . $block_size . ' - strlen($_text) % ' . $block_size . ') % ' . $block_size . ', chr(0));
                    $_ciphertext_len = strlen($_text);

                    for ($_i = 0; $_i < $_ciphertext_len; $_i+= ' . $block_size . ') {
                        $in = substr($_text, $_i, ' . $block_size . ');
                        ' . $decrypt_block . '
                        $_plaintext.= $in;
                    }

                    return $this->unpad($_plaintext);
                    ';
                break;
            case self::MODE_CTR:
                $encrypt = $init_encrypt . '
                    $_ciphertext = "";
                    $_plaintext_len = strlen($_text);
                    $_xor = $this->encryptIV;
                    $_buffer = &$this->enbuffer;
                    if (strlen($_buffer["ciphertext"])) {
                        for ($_i = 0; $_i < $_plaintext_len; $_i+= ' . $block_size . ') {
                            $_block = substr($_text, $_i, ' . $block_size . ');
                            if (strlen($_block) > strlen($_buffer["ciphertext"])) {
                                $in = $_xor;
                                ' . $encrypt_block . '
                                \phpseclib4\Common\Functions\Strings::increment_str($_xor);
                                $_buffer["ciphertext"].= $in;
                            }
                            $_key = \phpseclib4\Common\Functions\Strings::shift($_buffer["ciphertext"], ' . $block_size . ');
                            $_ciphertext.= $_block ^ $_key;
                        }
                    } else {
                        for ($_i = 0; $_i < $_plaintext_len; $_i+= ' . $block_size . ') {
                            $_block = substr($_text, $_i, ' . $block_size . ');
                            $in = $_xor;
                            ' . $encrypt_block . '
                            \phpseclib4\Common\Functions\Strings::increment_str($_xor);
                            $_key = $in;
                            $_ciphertext.= $_block ^ $_key;
                        }
                    }
                    if ($this->continuousBuffer) {
                        $this->encryptIV = $_xor;
                        if ($_start = $_plaintext_len % ' . $block_size . ') {
                            $_buffer["ciphertext"] = substr($_key, $_start) . $_buffer["ciphertext"];
                        }
                    }

                    return $_ciphertext;
                ';

                $decrypt = $init_encrypt . '
                    $_plaintext = "";
                    $_ciphertext_len = strlen($_text);
                    $_xor = $this->decryptIV;
                    $_buffer = &$this->debuffer;

                    if (strlen($_buffer["ciphertext"])) {
                        for ($_i = 0; $_i < $_ciphertext_len; $_i+= ' . $block_size . ') {
                            $_block = substr($_text, $_i, ' . $block_size . ');
                            if (strlen($_block) > strlen($_buffer["ciphertext"])) {
                                $in = $_xor;
                                ' . $encrypt_block . '
                                \phpseclib4\Common\Functions\Strings::increment_str($_xor);
                                $_buffer["ciphertext"].= $in;
                            }
                            $_key = \phpseclib4\Common\Functions\Strings::shift($_buffer["ciphertext"], ' . $block_size . ');
                            $_plaintext.= $_block ^ $_key;
                        }
                    } else {
                        for ($_i = 0; $_i < $_ciphertext_len; $_i+= ' . $block_size . ') {
                            $_block = substr($_text, $_i, ' . $block_size . ');
                            $in = $_xor;
                            ' . $encrypt_block . '
                            \phpseclib4\Common\Functions\Strings::increment_str($_xor);
                            $_key = $in;
                            $_plaintext.= $_block ^ $_key;
                        }
                    }
                    if ($this->continuousBuffer) {
                        $this->decryptIV = $_xor;
                        if ($_start = $_ciphertext_len % ' . $block_size . ') {
                            $_buffer["ciphertext"] = substr($_key, $_start) . $_buffer["ciphertext"];
                        }
                    }

                    return $_plaintext;
                    ';
                break;
            case self::MODE_CFB:
                $encrypt = $init_encrypt . '
                    $_ciphertext = "";
                    $_buffer = &$this->enbuffer;

                    if ($this->continuousBuffer) {
                        $_iv = &$this->encryptIV;
                        $_pos = &$_buffer["pos"];
                    } else {
                        $_iv = $this->encryptIV;
                        $_pos = 0;
                    }
                    $_len = strlen($_text);
                    $_i = 0;
                    if ($_pos) {
                        $_orig_pos = $_pos;
                        $_max = ' . $block_size . ' - $_pos;
                        if ($_len >= $_max) {
                            $_i = $_max;
                            $_len-= $_max;
                            $_pos = 0;
                        } else {
                            $_i = $_len;
                            $_pos+= $_len;
                            $_len = 0;
                        }
                        $_ciphertext = substr($_iv, $_orig_pos) ^ $_text;
                        $_iv = substr_replace($_iv, $_ciphertext, $_orig_pos, $_i);
                    }
                    while ($_len >= ' . $block_size . ') {
                        $in = $_iv;
                        ' . $encrypt_block . ';
                        $_iv = $in ^ substr($_text, $_i, ' . $block_size . ');
                        $_ciphertext.= $_iv;
                        $_len-= ' . $block_size . ';
                        $_i+= ' . $block_size . ';
                    }
                    if ($_len) {
                        $in = $_iv;
                        ' . $encrypt_block . '
                        $_iv = $in;
                        $_block = $_iv ^ substr($_text, $_i);
                        $_iv = substr_replace($_iv, $_block, 0, $_len);
                        $_ciphertext.= $_block;
                        $_pos = $_len;
                    }
                    return $_ciphertext;
                ';

                $decrypt = $init_encrypt . '
                    $_plaintext = "";
                    $_buffer = &$this->debuffer;

                    if ($this->continuousBuffer) {
                        $_iv = &$this->decryptIV;
                        $_pos = &$_buffer["pos"];
                    } else {
                        $_iv = $this->decryptIV;
                        $_pos = 0;
                    }
                    $_len = strlen($_text);
                    $_i = 0;
                    if ($_pos) {
                        $_orig_pos = $_pos;
                        $_max = ' . $block_size . ' - $_pos;
                        if ($_len >= $_max) {
                            $_i = $_max;
                            $_len-= $_max;
                            $_pos = 0;
                        } else {
                            $_i = $_len;
                            $_pos+= $_len;
                            $_len = 0;
                        }
                        $_plaintext = substr($_iv, $_orig_pos) ^ $_text;
                        $_iv = substr_replace($_iv, substr($_text, 0, $_i), $_orig_pos, $_i);
                    }
                    while ($_len >= ' . $block_size . ') {
                        $in = $_iv;
                        ' . $encrypt_block . '
                        $_iv = $in;
                        $cb = substr($_text, $_i, ' . $block_size . ');
                        $_plaintext.= $_iv ^ $cb;
                        $_iv = $cb;
                        $_len-= ' . $block_size . ';
                        $_i+= ' . $block_size . ';
                    }
                    if ($_len) {
                        $in = $_iv;
                        ' . $encrypt_block . '
                        $_iv = $in;
                        $_plaintext.= $_iv ^ substr($_text, $_i);
                        $_iv = substr_replace($_iv, substr($_text, $_i), 0, $_len);
                        $_pos = $_len;
                    }

                    return $_plaintext;
                    ';
                break;
            case self::MODE_CFB8:
                $encrypt = $init_encrypt . '
                    $_ciphertext = "";
                    $_len = strlen($_text);
                    $_iv = $this->encryptIV;

                    for ($_i = 0; $_i < $_len; ++$_i) {
                        $in = $_iv;
                        ' . $encrypt_block . '
                        $_ciphertext .= ($_c = $_text[$_i] ^ $in);
                        $_iv = substr($_iv, 1) . $_c;
                    }

                    if ($this->continuousBuffer) {
                        if ($_len >= ' . $block_size . ') {
                            $this->encryptIV = substr($_ciphertext, -' . $block_size . ');
                        } else {
                            $this->encryptIV = substr($this->encryptIV, $_len - ' . $block_size . ') . substr($_ciphertext, -$_len);
                        }
                    }

                    return $_ciphertext;
                    ';
                $decrypt = $init_encrypt . '
                    $_plaintext = "";
                    $_len = strlen($_text);
                    $_iv = $this->decryptIV;

                    for ($_i = 0; $_i < $_len; ++$_i) {
                        $in = $_iv;
                        ' . $encrypt_block . '
                        $_plaintext .= $_text[$_i] ^ $in;
                        $_iv = substr($_iv, 1) . $_text[$_i];
                    }

                    if ($this->continuousBuffer) {
                        if ($_len >= ' . $block_size . ') {
                            $this->decryptIV = substr($_text, -' . $block_size . ');
                        } else {
                            $this->decryptIV = substr($this->decryptIV, $_len - ' . $block_size . ') . substr($_text, -$_len);
                        }
                    }

                    return $_plaintext;
                    ';
                break;
            case self::MODE_OFB8:
                $encrypt = $init_encrypt . '
                    $_ciphertext = "";
                    $_len = strlen($_text);
                    $_iv = $this->encryptIV;

                    for ($_i = 0; $_i < $_len; ++$_i) {
                        $in = $_iv;
                        ' . $encrypt_block . '
                        $_ciphertext.= $_text[$_i] ^ $in;
                        $_iv = substr($_iv, 1) . $in[0];
                    }

                    if ($this->continuousBuffer) {
                        $this->encryptIV = $_iv;
                    }

                    return $_ciphertext;
                    ';
                $decrypt = $init_encrypt . '
                    $_plaintext = "";
                    $_len = strlen($_text);
                    $_iv = $this->decryptIV;

                    for ($_i = 0; $_i < $_len; ++$_i) {
                        $in = $_iv;
                        ' . $encrypt_block . '
                        $_plaintext.= $_text[$_i] ^ $in;
                        $_iv = substr($_iv, 1) . $in[0];
                    }

                    if ($this->continuousBuffer) {
                        $this->decryptIV = $_iv;
                    }

                    return $_plaintext;
                    ';
                break;
            case self::MODE_OFB:
                $encrypt = $init_encrypt . '
                    $_ciphertext = "";
                    $_plaintext_len = strlen($_text);
                    $_xor = $this->encryptIV;
                    $_buffer = &$this->enbuffer;

                    if (strlen($_buffer["xor"])) {
                        for ($_i = 0; $_i < $_plaintext_len; $_i+= ' . $block_size . ') {
                            $_block = substr($_text, $_i, ' . $block_size . ');
                            if (strlen($_block) > strlen($_buffer["xor"])) {
                                $in = $_xor;
                                ' . $encrypt_block . '
                                $_xor = $in;
                                $_buffer["xor"].= $_xor;
                            }
                            $_key = \phpseclib4\Common\Functions\Strings::shift($_buffer["xor"], ' . $block_size . ');
                            $_ciphertext.= $_block ^ $_key;
                        }
                    } else {
                        for ($_i = 0; $_i < $_plaintext_len; $_i+= ' . $block_size . ') {
                            $in = $_xor;
                            ' . $encrypt_block . '
                            $_xor = $in;
                            $_ciphertext.= substr($_text, $_i, ' . $block_size . ') ^ $_xor;
                        }
                        $_key = $_xor;
                    }
                    if ($this->continuousBuffer) {
                        $this->encryptIV = $_xor;
                        if ($_start = $_plaintext_len % ' . $block_size . ') {
                             $_buffer["xor"] = substr($_key, $_start) . $_buffer["xor"];
                        }
                    }
                    return $_ciphertext;
                    ';

                $decrypt = $init_encrypt . '
                    $_plaintext = "";
                    $_ciphertext_len = strlen($_text);
                    $_xor = $this->decryptIV;
                    $_buffer = &$this->debuffer;

                    if (strlen($_buffer["xor"])) {
                        for ($_i = 0; $_i < $_ciphertext_len; $_i+= ' . $block_size . ') {
                            $_block = substr($_text, $_i, ' . $block_size . ');
                            if (strlen($_block) > strlen($_buffer["xor"])) {
                                $in = $_xor;
                                ' . $encrypt_block . '
                                $_xor = $in;
                                $_buffer["xor"].= $_xor;
                            }
                            $_key = \phpseclib4\Common\Functions\Strings::shift($_buffer["xor"], ' . $block_size . ');
                            $_plaintext.= $_block ^ $_key;
                        }
                    } else {
                        for ($_i = 0; $_i < $_ciphertext_len; $_i+= ' . $block_size . ') {
                            $in = $_xor;
                            ' . $encrypt_block . '
                            $_xor = $in;
                            $_plaintext.= substr($_text, $_i, ' . $block_size . ') ^ $_xor;
                        }
                        $_key = $_xor;
                    }
                    if ($this->continuousBuffer) {
                        $this->decryptIV = $_xor;
                        if ($_start = $_ciphertext_len % ' . $block_size . ') {
                             $_buffer["xor"] = substr($_key, $_start) . $_buffer["xor"];
                        }
                    }
                    return $_plaintext;
                    ';
                break;
            case self::MODE_STREAM:
                $encrypt = $init_encrypt . '
                    $_ciphertext = "";
                    ' . $encrypt_block . '
                    return $_ciphertext;
                    ';
                $decrypt = $init_decrypt . '
                    $_plaintext = "";
                    ' . $decrypt_block . '
                    return $_plaintext;
                    ';
                break;
            // case self::MODE_CBC:
            default:
                $encrypt = $init_encrypt . '
                    $_ciphertext = "";
                    $_plaintext_len = strlen($_text);

                    $in = $this->encryptIV;

                    for ($_i = 0; $_i < $_plaintext_len; $_i+= ' . $block_size . ') {
                        $in = substr($_text, $_i, ' . $block_size . ') ^ $in;
                        ' . $encrypt_block . '
                        $_ciphertext.= $in;
                    }

                    if ($this->continuousBuffer) {
                        $this->encryptIV = $in;
                    }

                    return $_ciphertext;
                    ';

                $decrypt = $init_decrypt . '
                    $_plaintext = "";
                    $_text = str_pad($_text, strlen($_text) + (' . $block_size . ' - strlen($_text) % ' . $block_size . ') % ' . $block_size . ', chr(0));
                    $_ciphertext_len = strlen($_text);

                    $_iv = $this->decryptIV;

                    for ($_i = 0; $_i < $_ciphertext_len; $_i+= ' . $block_size . ') {
                        $in = $_block = substr($_text, $_i, ' . $block_size . ');
                        ' . $decrypt_block . '
                        $_plaintext.= $in ^ $_iv;
                        $_iv = $_block;
                    }

                    if ($this->continuousBuffer) {
                        $this->decryptIV = $_iv;
                    }

                    return $this->unpad($_plaintext);
                    ';
        }

        // Before discrediting this, please read the following:
        // @see https://github.com/phpseclib/phpseclib/issues/1293
        // @see https://github.com/phpseclib/phpseclib/pull/1143

        /** @var \Closure $func */
        $func = eval(<<<PHP
            return function (string \$_action, string \$_text): string
            {
                {$init_crypt}
                if (\$_action === 'encrypt') {
                    {$encrypt}
                } else {
                    {$decrypt}
                }
            };
            PHP
        );

        return \Closure::bind($func, $this, static::class);
    }

    /**
     * Convert float to int
     *
     * On ARM CPUs converting floats to ints doesn't always work
     */
    protected static function safe_intval(int|float $x): int
    {
        if (is_int($x)) {
            return $x;
        }

        if (self::$use_reg_intval) {
            return intval($x);
        }

        return (int) (
            (fmod($x, 0x80000000) & 0x7FFFFFFF) |
            ((fmod(floor($x / 0x80000000), 2) & 1) << 31)
        );
    }

    /**
     * eval()'able string for in-line float to int
     */
    protected static function safe_intval_inline(): string
    {
        if (self::$use_reg_intval) {
            return PHP_INT_SIZE == 4 ? 'intval(%s)' : '%s';
        }

        $safeint = '(is_int($temp = %s) ? $temp : (fmod($temp, 0x80000000) & 0x7FFFFFFF) | ';
        return $safeint . '((fmod(floor($temp / 0x80000000), 2) & 1) << 31))';
    }

    /**
     * Sets up GCM parameters
     *
     * See steps 1-2 of https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf#page=23
     * for more info
     */
    private function setupGCM(): void
    {
        // don't keep on re-calculating $this->h
        if (!isset($this->h) || $this->hKey != $this->key) {
            $cipher = new static('ecb');
            $cipher->setKey($this->key);
            $cipher->disablePadding();

            $this->h = self::$gcmField->newInteger(
                Strings::switchEndianness($cipher->encrypt("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"))
            );
            $this->hKey = $this->key;
        }

        if (strlen($this->nonce) == 12) {
            $this->iv = $this->nonce . "\0\0\0\1";
        } else {
            $this->iv = $this->ghash(
                self::nullPad128($this->nonce) . str_repeat("\0", 8) . self::len64($this->nonce)
            );
        }
    }

    /**
     * Performs GHASH operation
     *
     * See https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf#page=20
     * for more info
     *
     * @see self::decrypt()
     * @see self::encrypt()
     */
    private function ghash(string $x): string
    {
        $h = $this->h;
        $y = ["\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"];
        $x = str_split($x, 16);
        $n = 0;
        // the switchEndianness calls are necessary because the multiplication algorithm in BinaryField/Integer
        // interprets strings as polynomials in big endian order whereas in GCM they're interpreted in little
        // endian order per https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf#page=19.
        // big endian order is what binary field elliptic curves use per http://www.secg.org/sec1-v2.pdf#page=18.

        // we could switchEndianness here instead of in the while loop but doing so in the while loop seems like it
        // might be slightly more performant
        //$x = Strings::switchEndianness($x);
        foreach ($x as $xn) {
            $xn = Strings::switchEndianness($xn);
            $t = $y[$n] ^ $xn;
            $temp = self::$gcmField->newInteger($t);
            $y[++$n] = $temp->multiply($h)->toBytes();
            $y[$n] = substr($y[$n], 1);
        }
        $y[$n] = Strings::switchEndianness($y[$n]);
        return $y[$n];
    }

    /**
     * Returns the bit length of a string in a packed format
     *
     * @see self::setupGCM()
     * @see self::decrypt()
     * @see self::encrypt()
     */
    private static function len64(string $str): string
    {
        return "\0\0\0\0" . pack('N', 8 * strlen($str));
    }

    /**
     * NULL pads a string to be a multiple of 128
     *
     * @see self::setupGCM()
     * @see self::decrypt()
     * @see self::encrypt()
     */
    protected static function nullPad128(string $str): string
    {
        $len = strlen($str);
        return $str . str_repeat("\0", 16 * ((int) ceil($len / 16)) - $len);
    }

    /**
     * Calculates Poly1305 MAC
     *
     * On my system ChaCha20, with libsodium, takes 0.5s. With this custom Poly1305 implementation
     * it takes 1.2s.
     *
     * @see self::decrypt()
     * @see self::encrypt()
     */
    protected function poly1305(string $text): string
    {
        $s = $this->poly1305Key; // strlen($this->poly1305Key) == 32
        $r = Strings::shift($s, 16);
        $r = strrev($r);
        $r &= "\x0f\xff\xff\xfc\x0f\xff\xff\xfc\x0f\xff\xff\xfc\x0f\xff\xff\xff";
        $s = strrev($s);

        $r = self::$poly1305Field->newInteger(new BigInteger($r, 256));
        $s = self::$poly1305Field->newInteger(new BigInteger($s, 256));
        $a = self::$poly1305Field->newInteger(new BigInteger());

        $blocks = str_split($text, 16);
        foreach ($blocks as $block) {
            $n = strrev($block . chr(1));
            $n = self::$poly1305Field->newInteger(new BigInteger($n, 256));
            $a = $a->add($n);
            $a = $a->multiply($r);
        }
        $r = $a->toBigInteger()->add($s->toBigInteger());
        $mask = "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF";
        return strrev($r->toBytes()) & $mask;
    }

    /**
     * Return the mode
     *
     * You can do $obj instanceof AES or whatever to get the cipher but you can't do that to get the mode
     */
    public function getMode(): string
    {
        return array_flip(self::MODE_MAP)[$this->mode];
    }

    /**
     * Is the continuous buffer enabled?
     */
    public function continuousBufferEnabled(): bool
    {
        return $this->continuousBuffer;
    }

    public function setMetaData(string $key, mixed $value): void
    {
        $this->metadata[$key] = $value;
    }

    public function getMetaData(string $key): mixed
    {
        return $this->metadata[$key];
    }

    public function hasMetaData(string $key): bool
    {
        return isset($this->metadata[$key]);
    }

    public function deleteMetaData(string $key): void
    {
        unset($this->metadata[$key]);
    }
}
<?php

/**
 * Fingerprint Trait for Public Keys
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2019-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\Common\Traits;

use phpseclib4\Crypt\Hash;
use phpseclib4\Exception\UnsupportedAlgorithmException;

/**
 * Fingerprint Trait for Private Keys
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
trait Fingerprint
{
    /**
     * Returns the public key's fingerprint
     *
     * The public key's fingerprint is returned, which is equivalent to running `ssh-keygen -lf rsa.pub`. If there is
     * no public key currently loaded, false is returned.
     * Example output (md5): "c1:b1:30:29:d7:b8:de:6c:97:77:10:d7:46:41:63:87" (as specified by RFC 4716)
     *
     * @param string $algorithm The hashing algorithm to be used. Valid options are 'md5' and 'sha256'. False is returned
     * for invalid values.
     */
    public function getFingerprint(string $algorithm = 'md5'): string
    {
        self::validatePlugin('Keys', 'OpenSSH', 'savePublicKey');
        $key = $this->toString('OpenSSH', ['binary' => true]);
        switch ($algorithm) {
            case 'sha256':
                $hash = new Hash('sha256');
                $base = base64_encode($hash->hash($key));
                return substr($base, 0, strlen($base) - 1);
            case 'md5':
                return substr(chunk_split(md5($key), 2, ':'), 0, -1);
            default:
                throw new UnsupportedAlgorithmException('The only two supported fingerprinting algorithms are sha256 and md5');
        }
    }
}
<?php

/**
 * Password Protected Trait for Private Keys
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2019-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\Common\Traits;

/**
 * Password Protected Trait for Private Keys
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
trait PasswordProtected
{
    protected ?string $password = null;

    /**
     * Sets the password
     *
     * Private keys can be encrypted with a password.  To unset the password, pass in the empty string or false.
     * Or rather, pass in $password such that empty($password) && !is_string($password) is true.
     *
     * @see self::createKey()
     * @see self::load()
     *
     * @return static
     */
    public function withPassword(#[\SensitiveParameter] ?string $password = null): self
    {
        $new = clone $this;
        $new->password = $password;
        return $new;
    }

    public function withoutPassword(): self
    {
        return $this->withPassword();
    }

    public function hasPassword(): bool
    {
        return isset($this->password);
    }
}
<?php

/**
 * PKCS12 PBKDF Helper for Symmetric Keys and MACs
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2025-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\Common\Traits;

use phpseclib4\Crypt\{AES, DES, RC2, RC4, TripleDES};
use phpseclib4\Crypt\Common\SymmetricKey;
use phpseclib4\Exception\UnsupportedAlgorithmException;
use phpseclib4\File\ASN1;
use phpseclib4\File\ASN1\{Constructed, Maps};
use phpseclib4\File\ASN1\Types\OctetString;

/**
 * PKCS12 PBKDF Helper for Symmetric Keys and MACs
 *
 * Used by PKCS8, PFX and CMS
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
trait ASN1AlgorithmIdentifier
{
    /**
     * Default encryption algorithm
     */
    private static string $defaultEncryptionAlgorithm = 'id-PBES2';

    /**
     * Default encryption scheme
     *
     * Only used when defaultEncryptionAlgorithm is id-PBES2
     */
    private static string $defaultEncryptionScheme = 'aes128-CBC-PAD';

    /**
     * Default PRF
     *
     * Only used when defaultEncryptionAlgorithm is id-PBES2
     */
    private static string $defaultPRF = 'id-hmacWithSHA256';

    /**
     * Default Iteration Count
     */
    private static int $defaultIterationCount = 2048;

    /**
     * Sets the default encryption algorithm
     */
    public static function setEncryptionAlgorithm(string $algo): void
    {
        self::$defaultEncryptionAlgorithm = $algo;
    }

    /**
     * Sets the default encryption algorithm for PBES2
     */
    public static function setEncryptionScheme(string $algo): void
    {
        self::$defaultEncryptionScheme = $algo;
    }

    /**
     * Sets the iteration count
     */
    public static function setIterationCount(int $count): void
    {
        self::$defaultIterationCount = $count;
    }

    /**
     * Sets the PRF for PBES2
     */
    public static function setPRF(string $algo): void
    {
        self::$defaultPRF = $algo;
    }

    /**
     * Returns a SymmetricKey object based on a PBES1 $algo
     *
     * @return SymmetricKey
     */
    private static function getPBES1EncryptionObject(string $algo): SymmetricKey
    {
        $origAlgo = $algo;
        $algo = preg_match('#^pbeWith(?:MD2|MD5|SHA1|SHA)And(.*?)-CBC$#', $algo, $matches) ?
            $matches[1] :
            substr($algo, 13); // strlen('pbeWithSHAAnd') == 13

        switch ($algo) {
            case 'DES':
                $cipher = new DES('cbc');
                break;
            case 'RC2':
                $cipher = new RC2('cbc');
                $cipher->setKeyLength(64);
                break;
            case '3-KeyTripleDES':
                $cipher = new TripleDES('cbc');
                break;
            case '2-KeyTripleDES':
                $cipher = new TripleDES('cbc');
                $cipher->setKeyLength(128);
                break;
            case '128BitRC2':
                $cipher = new RC2('cbc');
                $cipher->setKeyLength(128);
                break;
            case '40BitRC2':
                $cipher = new RC2('cbc');
                $cipher->setKeyLength(40);
                break;
            case '128BitRC4':
                $cipher = new RC4();
                $cipher->setKeyLength(128);
                break;
            case '40BitRC4':
                $cipher = new RC4();
                $cipher->setKeyLength(40);
                break;
            default:
                throw new UnsupportedAlgorithmException("$origAlgo is not a supported algorithm");
        }

        return $cipher;
    }

    /**
     * Returns a hash based on a PBES1 $algo
     */
    private static function getPBES1Hash(string $algo): string
    {
        if (preg_match('#^pbeWith(MD2|MD5|SHA1|SHA)And.*?-CBC$#', $algo, $matches)) {
            return $matches[1] == 'SHA' ? 'sha1' : $matches[1];
        }

        return 'sha1';
    }

    /**
     * Returns a KDF baesd on a PBES1 $algo
     */
    private static function getPBES1KDF(string $algo): string
    {
        switch ($algo) {
            case 'pbeWithMD2AndDES-CBC':
            case 'pbeWithMD2AndRC2-CBC':
            case 'pbeWithMD5AndDES-CBC':
            case 'pbeWithMD5AndRC2-CBC':
            case 'pbeWithSHA1AndDES-CBC':
            case 'pbeWithSHA1AndRC2-CBC':
                return 'pbkdf1';
        }

        return 'pkcs12';
    }

    /**
     * Returns a SymmetricKey object based on a PBES2 $algo
     */
    protected static function getPBES2EncryptionObject(string $algo): SymmetricKey
    {
        switch ($algo) {
            case 'desCBC':
                $cipher = new DES('cbc');
                break;
            case 'des-EDE3-CBC':
                $cipher = new TripleDES('cbc');
                break;
            case 'rc2CBC':
                $cipher = new RC2('cbc');
                // in theory this can be changed
                $cipher->setKeyLength(128);
                break;
            case 'rc5-CBC-PAD':
                throw new UnsupportedAlgorithmException('rc5-CBC-PAD is not supported for PBES2 PKCS#8 keys');
            case 'aes128-CBC-PAD':
            case 'aes192-CBC-PAD':
            case 'aes256-CBC-PAD':
                $cipher = new AES('cbc');
                $cipher->setKeyLength((int) substr($algo, 3, 3));
                break;
            default:
                throw new UnsupportedAlgorithmException("$algo is not supported");
        }

        return $cipher;
    }

    private static function getCryptoObjectFromAlgorithmIdentifier(
        array|Constructed $data,
        #[\SensitiveParameter] string $password
    ): SymmetricKey {
        $meta = [];
        $algorithm = (string) $data['algorithm'];
        switch ($algorithm) {
            // PBES1
            case 'pbeWithMD2AndDES-CBC':
            case 'pbeWithMD2AndRC2-CBC':
            case 'pbeWithMD5AndDES-CBC':
            case 'pbeWithMD5AndRC2-CBC':
            case 'pbeWithSHA1AndDES-CBC':
            case 'pbeWithSHA1AndRC2-CBC':
            case 'pbeWithSHAAnd3-KeyTripleDES-CBC':
            case 'pbeWithSHAAnd2-KeyTripleDES-CBC':
            case 'pbeWithSHAAnd128BitRC2-CBC':
            case 'pbeWithSHAAnd40BitRC2-CBC':
            case 'pbeWithSHAAnd128BitRC4':
            case 'pbeWithSHAAnd40BitRC4':
                $cipher = self::getPBES1EncryptionObject($algorithm);
                $hash = self::getPBES1Hash($algorithm);
                $kdf = self::getPBES1KDF($algorithm);

                $meta['meta']['algorithm'] = $algorithm;

                $temp = ASN1::decodeBER((string) $data['parameters']);
                extract(ASN1::map($temp, Maps\PBEParameter::MAP)->toArray());
                $iterationCount = (int) $iterationCount->toString();
                $cipher->setPassword($password, $kdf, $hash, "$salt", $iterationCount);
                $cipher->setMetaData('meta', $meta);
                return $cipher;
            case 'id-PBES2':
                $meta['meta']['algorithm'] = $algorithm;

                $temp = ASN1::decodeBER((string) $data['parameters']);
                $temp = ASN1::map($temp, Maps\PBES2params::MAP)->toArray();
                extract($temp);

                $cipher = self::getPBES2EncryptionObject((string) $encryptionScheme['algorithm']);
                $meta['meta']['cipher'] = $encryptionScheme['algorithm'];

                $temp = ASN1::decodeBER((string) $data['parameters']);
                $temp = ASN1::map($temp, Maps\PBES2params::MAP)->toArray();
                extract($temp);

                if (!$cipher instanceof RC2) {
                    $cipher->setIV((string) $encryptionScheme['parameters']);
                } else {
                    $temp = ASN1::decodeBER((string) $encryptionScheme['parameters']);
                    extract(ASN1::map($temp, Maps\RC2CBCParameter::MAP)->toArray());

                    $effectiveKeyLength = match ((int) $rc2ParametersVersion->toString()) {
                        160 => 40,
                        120 => 64,
                        58 => 128,
                        default => throw new UnsupportedAlgorithmException('The only effective key lengths that phpseclib supports for RC2 encrypted keys are 40, 64 an 128')
                    };
                    $cipher->setIV((string) $iv);
                    $cipher->setKeyLength($effectiveKeyLength);
                }

                $meta['meta']['keyDerivationFunc'] = $keyDerivationFunc['algorithm'];
                $cipher->setMetaData('meta', $meta);
                self::setupPBKDF2($keyDerivationFunc, $password, $cipher);
                return $cipher;
            //case 'id-PBMAC1':
            //    $temp = ASN1::decodeBER($data['parameters']);
            //    $value = ASN1::map($temp[0], Maps\PBMAC1params::MAP)->toArray();
            //    // since i can't find any implementation that does PBMAC1 it is unsupported
            default:
                throw new UnsupportedAlgorithmException('Only PBES1 and PBES2 PKCS#8 keys are supported.');
        }
    }

    protected static function setupPBKDF2(
        array|Constructed $keyDerivationFunc,
        #[\SensitiveParameter] string $password,
        SymmetricKey $cipher
    ): void {
        switch ($keyDerivationFunc['algorithm']) {
            case 'id-PBKDF2':
                $meta = $cipher->hasMetaData('meta') ? $cipher->getMetaData('meta') : [];
                $prf = ['algorithm' => 'id-hmacWithSHA1'];
                $temp = ASN1::decodeBER((string) $keyDerivationFunc['parameters']);
                $params = ASN1::map($temp, Maps\PBKDF2params::MAP)->toArray();
                extract($params);

                $meta['meta']['prf'] = $prf['algorithm'];
                $hash = str_replace('-', '/', substr((string) $prf['algorithm'], 11));
                $params = [
                    $password,
                    'pbkdf2',
                    $hash,
                    (string) $salt,
                    (int) $iterationCount->toString(),
                ];
                if (isset($keyLength)) {
                    $params[] = (int) $keyLength->toString();
                }
                $cipher->setPassword(...$params);
                $cipher->setMetaData('meta', $meta);
                break;
            default:
                throw new UnsupportedAlgorithmException('Only PBKDF2 is supported for PBES2 PKCS#8 keys');
        }
    }

    private static function getCryptoObjectFromParams(string $password, array $options): SymmetricKey
    {
        $salt = random_bytes(8);

        $iterationCount = $options['iterationCount'] ?? self::$defaultIterationCount;
        $encryptionAlgorithm = $options['encryptionAlgorithm'] ?? self::$defaultEncryptionAlgorithm;
        $encryptionScheme = $options['encryptionScheme'] ?? self::$defaultEncryptionScheme;
        $prf = $options['PRF'] ?? self::$defaultPRF;

        if ($encryptionAlgorithm == 'id-PBES2') {
            $crypto = self::getPBES2EncryptionObject($encryptionScheme);
            $hash = str_replace('-', '/', substr($prf, 11));
            $kdf = 'pbkdf2';
            $iv = random_bytes($crypto->getBlockLength() >> 3);

            $PBKDF2params = [
                'salt' => $salt,
                'iterationCount' => $iterationCount,
                'prf' => ['algorithm' => $prf, 'parameters' => null],
            ];
            $PBKDF2params = ASN1::encodeDER($PBKDF2params, Maps\PBKDF2params::MAP);

            if (!$crypto instanceof RC2) {
                $params = new OctetString($iv);
            } else {
                $params = [
                    'rc2ParametersVersion' => 58,
                    'iv' => $iv,
                ];
                $params = ASN1::encodeDER($params, Maps\RC2CBCParameter::MAP);
                $params = new ASN1\Element($params);
            }

            $params = [
                'keyDerivationFunc' => [
                    'algorithm' => 'id-PBKDF2',
                    'parameters' => new ASN1\Element($PBKDF2params),
                ],
                'encryptionScheme' => [
                    'algorithm' => $encryptionScheme,
                    'parameters' => $params,
                ],
            ];
            $params = ASN1::encodeDER($params, Maps\PBES2params::MAP);

            $crypto->setIV($iv);
        } else {
            $crypto = self::getPBES1EncryptionObject($encryptionAlgorithm);
            $hash = self::getPBES1Hash($encryptionAlgorithm);
            $kdf = self::getPBES1KDF($encryptionAlgorithm);

            $params = [
                'salt' => $salt,
                'iterationCount' => $iterationCount,
            ];
            $params = ASN1::encodeDER($params, Maps\PBEParameter::MAP);
        }
        $crypto->setMetaData('algorithmIdentifier', [
            'algorithm' => $encryptionAlgorithm,
            'parameters' => new ASN1\Element($params),
        ]);
        $crypto->setPassword($password, $kdf, $hash, $salt, $iterationCount);

        return $crypto;
    }
}
<?php

/**
 * PKCS12 PBKDF Helper for Symmetric Keys and MACs
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2025-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\Common\Traits;

use phpseclib4\Crypt\Hash;
use phpseclib4\Math\BigInteger;

/**
 * PKCS12 PBKDF Helper for Symmetric Keys and MACs
 *
 * Used by PKCS8, PFX and CMS
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
trait PKCS12Helper
{
    /**
     * PKCS#12 KDF Helper Function
     *
     * As discussed here:
     *
     * {@link https://tools.ietf.org/html/rfc7292#appendix-B}
     *
     * @return string $a
     * @see self::setPassword()
     */
    private static function pkcs12helper(int $n, Hash $hashObj, string $i, string $d, int $count): string
    {
        static $one;
        if (!isset($one)) {
            $one = new BigInteger(1);
        }

        $blockLength = $hashObj->getBlockLength() >> 3;

        $c = ceil($n / $hashObj->getLengthInBytes());
        $a = '';
        for ($j = 1; $j <= $c; $j++) {
            $ai = $d . $i;
            for ($k = 0; $k < $count; $k++) {
                $ai = $hashObj->hash($ai);
            }
            $b = '';
            while (strlen($b) < $blockLength) {
                $b .= $ai;
            }
            $b = substr($b, 0, $blockLength);
            $b = new BigInteger($b, 256);
            $newi = '';
            for ($k = 0; $k < strlen($i); $k += $blockLength) {
                $temp = substr($i, $k, $blockLength);
                $temp = new BigInteger($temp, 256);
                $temp->setPrecision($blockLength << 3);
                $temp = $temp->add($b);
                $temp = $temp->add($one);
                $newi .= $temp->toBytes(false);
            }
            $i = $newi;
            $a .= $ai;
        }

        return substr($a, 0, $n);
    }
}
<?php

/**
 * PKCS#8 Formatted Key Handler
 *
 * PHP version 8.1+
 *
 * Used by PHP's openssl_public_encrypt() and openssl's rsautl (when -pubin is set)
 *
 * Processes keys with the following headers:
 *
 * -----BEGIN ENCRYPTED PRIVATE KEY-----
 * -----BEGIN PRIVATE KEY-----
 * -----BEGIN PUBLIC KEY-----
 *
 * Analogous to ssh-keygen's pkcs8 format (as specified by -m). Although PKCS8
 * is specific to private keys it's basically creating a DER-encoded wrapper
 * for keys. This just extends that same concept to public keys (much like ssh-keygen)
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\Common\Formats\Keys;

use phpseclib4\Common\Functions\Strings;
use phpseclib4\Exception\{
    PasswordNeededException,
    UnexpectedValueException,
    UnsupportedAlgorithmException
};
use phpseclib4\File\ASN1;
use phpseclib4\File\ASN1\{Element, Maps};
use phpseclib4\File\ASN1\Types\{BaseType, ExplicitNull};

/**
 * PKCS#8 Formatted Key Handler
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class PKCS8 extends PKCS
{
    use \phpseclib4\Crypt\Common\Traits\ASN1AlgorithmIdentifier;

    /**
     * OID Name
     *
     * @var string|list<string>
     */
    public const OID_NAME = '';

    /**
     * OID Value
     *
     * @var string|list<string>
     */
    public const OID_VALUE = '';

    /**
     * OIDs loaded
     */
    private static bool $oidsLoaded = false;

    /**
     * Binary key flag
     */
    private static bool $binary = false;

    /** @var array<class-string, true> */
    private static array $childOIDsLoaded = [];

    /**
     * Initialize static variables
     */
    private static function initialize_static_variables(): void
    {
        if (!isset(self::$childOIDsLoaded[static::class])) {
            if (static::OID_NAME !== '') {
                ASN1::loadOIDs(
                    is_array(static::OID_NAME) ?
                        array_combine(static::OID_NAME, static::OID_VALUE) :
                        [(string) static::OID_NAME => static::OID_VALUE]
                );
            }
            self::$childOIDsLoaded[static::class] = true;
        }
        if (!self::$oidsLoaded) {
            ASN1::loadOIDs('PKCS8');
            self::$oidsLoaded = true;
        }
    }

    /**
     * Break a public or private key down into its constituent components
     */
    protected static function load(
        #[\SensitiveParameter] string $key,
        #[\SensitiveParameter] ?string $password = null
    ): array {
        $isPublic = str_contains($key, 'PUBLIC');
        $isPrivate = str_contains($key, 'PRIVATE');

        $decoded = self::preParse($key);

        $meta = [];

        try {
            $decrypted = ASN1::map($decoded, Maps\EncryptedPrivateKeyInfo::MAP)->toArray();
        } catch (\Exception $e) {
            $decrypted = false;
        }

        if (is_array($decrypted)) {
            if (!isset($password)) {
                throw new PasswordNeededException('Key is encrypted but no password has been provided');
            }
            $cipher = self::getCryptoObjectFromAlgorithmIdentifier($decrypted['encryptionAlgorithm'], $password);
            $meta = $cipher->getMetaData('meta');
            $key = $cipher->decrypt((string) $decrypted['encryptedData']);
            $decoded = ASN1::decodeBER($key);
        }

        try {
            $private = ASN1::map($decoded, Maps\OneAsymmetricKey::MAP)->toArray();
        } catch (\Exception $e) {
            $private = false;
        }
        if (is_array($private)) {
            if ($isPublic) {
                throw new UnexpectedValueException('Human readable string claims public key but DER encoded string claims private key');
            }

            if (isset($private['privateKeyAlgorithm']['parameters']) && !$private['privateKeyAlgorithm']['parameters'] instanceof ASN1\Element) {
                $private['privateKeyAlgorithm']['parameters'] = new ASN1\Element($private['privateKeyAlgorithm']['parameters']->getEncoded());
            }
            if (is_array(static::OID_NAME)) {
                if (!in_array($private['privateKeyAlgorithm']['algorithm'], static::OID_NAME)) {
                    throw new UnsupportedAlgorithmException($private['privateKeyAlgorithm']['algorithm'] . ' is not a supported key type');
                }
            } else {
                if ($private['privateKeyAlgorithm']['algorithm'] != static::OID_NAME) {
                    throw new UnsupportedAlgorithmException('Only ' . static::OID_NAME . ' keys are supported; this is a ' . $private['privateKeyAlgorithm']['algorithm'] . ' key');
                }
            }
            if (isset($private['publicKey'])) {
                $private['publicKey'] = (string) $private['publicKey'];
                if ($private['publicKey'][0] != "\0") {
                    throw new UnexpectedValueException('The first byte of the public key should be null - not ' . bin2hex($private['publicKey'][0]));
                }
                $private['publicKey'] = substr($private['publicKey'], 1);
            }
            return $private + $meta;
        }

        // EncryptedPrivateKeyInfo and PublicKeyInfo have largely identical "signatures". the only difference
        // is that the former has an octet string and the later has a bit string. the first byte of a bit
        // string represents the number of bits in the last byte that are to be ignored but, currently,
        // bit strings wanting a non-zero amount of bits trimmed are not supported
        try {
            $public = ASN1::map($decoded, Maps\PublicKeyInfo::MAP)->toArray();
        } catch (\Exception $e) {
            $public = false;
        }

        if (is_array($public)) {
            if ($isPrivate) {
                throw new UnexpectedValueException('Human readable string claims private key but DER encoded string claims public key');
            }

            if ("$public[publicKey]"[0] != "\0") {
                throw new UnexpectedValueException('The first byte of the public key should be null - not ' . bin2hex($public['publicKey'][0]));
            }
            if (is_array(static::OID_NAME)) {
                if (!in_array($public['publicKeyAlgorithm']['algorithm'], static::OID_NAME)) {
                    throw new UnsupportedAlgorithmException($public['publicKeyAlgorithm']['algorithm'] . ' is not a supported key type');
                }
            } else {
                if ($public['publicKeyAlgorithm']['algorithm'] != static::OID_NAME) {
                    throw new UnsupportedAlgorithmException('Only ' . static::OID_NAME . ' keys are supported; this is a ' . $public['publicKeyAlgorithm']['algorithm'] . ' key');
                }
            }
            if (isset($public['publicKeyAlgorithm']['parameters']) && !$public['publicKeyAlgorithm']['parameters'] instanceof ASN1\Element) {
                $public['publicKeyAlgorithm']['parameters'] = new ASN1\Element($public['publicKeyAlgorithm']['parameters']->getEncoded());
            }
            $public['publicKey'] = substr((string) $public['publicKey'], 1);
            return $public;
        }

        throw new UnexpectedValueException('Unable to parse using either OneAsymmetricKey or PublicKeyInfo ASN1 maps');
    }

    /**
     * Enable binary output (DER)
     */
    public static function enableBinaryOutput(): void
    {
        self::$binary = true;
    }

    /**
     * Disable binary output (ie. enable PEM)
     */
    public static function disableBinaryOutput(): void
    {
        self::$binary = false;
    }

    /**
     * Wrap a private key appropriately
     */
    protected static function wrapPrivateKey(
        #[\SensitiveParameter] string $key,
        Element|BaseType $params = new ExplicitNull(),
        #[\SensitiveParameter] ?string $password = null,
        ?string $oid = null,
        array $options = []
    ): string {
        self::initialize_static_variables();

        $key = [
            'version' => 'v1',
            'privateKeyAlgorithm' => [
                'algorithm' => is_string(static::OID_NAME) ? static::OID_NAME : $oid,
             ],
            'privateKey' => $key,
        ];
        if ($oid != 'id-Ed25519' && $oid != 'id-Ed448' && $oid != 'id-X25519' && $oid != 'id-X448') {
            $key['privateKeyAlgorithm']['parameters'] = $params;
        }
        //if (!empty($attr)) {
        //    $key['attributes'] = $attr;
        //}
        //if (!empty($publicKey)) {
        //    $key['version'] = 'v2';
        //    $key['publicKey'] = $publicKey;
        //}
        $key = ASN1::encodeDER($key, Maps\OneAsymmetricKey::MAP);
        if (isset($password)) {
            $crypto = self::getCryptoObjectFromParams($password, $options);
            $key = $crypto->encrypt($key);

            $key = [
                'encryptionAlgorithm' => $crypto->getMetaData('algorithmIdentifier'),
                'encryptedData' => $key,
            ];

            $key = ASN1::encodeDER($key, Maps\EncryptedPrivateKeyInfo::MAP);

            if ($options['binary'] ?? self::$binary) {
                return $key;
            }

            return "-----BEGIN ENCRYPTED PRIVATE KEY-----\r\n" .
                   chunk_split(Strings::base64_encode($key), 64) .
                   "-----END ENCRYPTED PRIVATE KEY-----";
        }

        if ($options['binary'] ?? self::$binary) {
            return $key;
        }

        return "-----BEGIN PRIVATE KEY-----\r\n" .
               chunk_split(Strings::base64_encode($key), 64) .
               "-----END PRIVATE KEY-----";
    }

    /**
     * Wrap a public key appropriately
     */
    protected static function wrapPublicKey(
        string $key,
        Element|BaseType $params = new ExplicitNull(),
        ?string $oid = null,
        array $options = []
    ): string {
        self::initialize_static_variables();

        $key = [
            'publicKeyAlgorithm' => [
                'algorithm' => is_string(static::OID_NAME) ? static::OID_NAME : $oid,
            ],
            'publicKey' => "\0" . $key,
        ];

        if ($oid != 'id-Ed25519' && $oid != 'id-Ed448' && $oid != 'id-X25519' && $oid != 'id-X448') {
            $key['publicKeyAlgorithm']['parameters'] = $params;
        }

        $key = ASN1::encodeDER($key, Maps\PublicKeyInfo::MAP);

        if ($options['binary'] ?? self::$binary) {
            return $key;
        }

        return "-----BEGIN PUBLIC KEY-----\r\n" .
               chunk_split(Strings::base64_encode($key), 64) .
               "-----END PUBLIC KEY-----";
    }

    /**
     * Perform some preliminary parsing of the key
     */
    private static function preParse(#[\SensitiveParameter] string $key): array
    {
        self::initialize_static_variables();

        if (self::$format != self::MODE_DER) {
            try {
                $decoded = ASN1::extractBER($key);
                $key = $decoded;
            } catch (\Exception $e) {
                if (self::$format == self::MODE_PEM) {
                    throw $e;
                }
            }
        }

        $decoded = ASN1::decodeBER($key);

        return $decoded;
    }

    /**
     * Returns the encryption parameters used by the key
     */
    public static function extractEncryptionAlgorithm(#[\SensitiveParameter] string $key): array
    {
        $decoded = self::preParse($key);

        $r = ASN1::map($decoded, Maps\EncryptedPrivateKeyInfo::MAP)->toArray();

        if ($r['encryptionAlgorithm']['algorithm'] == 'id-PBES2') {
            $decoded = ASN1::decodeBER((string) $r['encryptionAlgorithm']['parameters']);
            $r['encryptionAlgorithm']['parameters'] = ASN1::map($decoded, Maps\PBES2params::MAP)->toArray();

            $kdf = &$r['encryptionAlgorithm']['parameters']['keyDerivationFunc'];
            switch ($kdf['algorithm']) {
                case 'id-PBKDF2':
                    $decoded = ASN1::decodeBER((string) $kdf['parameters']);
                    $kdf['parameters'] = ASN1::map($decoded, Maps\PBKDF2params::MAP)->toArray();
            }
        }

        return $r['encryptionAlgorithm'];
    }
}
<?php

/**
 * PKCS Formatted Key Handler
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\Common\Formats\Keys;

/**
 * PKCS1 Formatted Key Handler
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class PKCS
{
    /**
     * Auto-detect the format
     */
    public const MODE_ANY = 0;
    /**
     * Require base64-encoded PEM's be supplied
     */
    public const MODE_PEM = 1;
    /**
     * Require raw DER's be supplied
     */
    public const MODE_DER = 2;
    /**#@-*/

    /**
     * Is the key a base-64 encoded PEM, DER or should it be auto-detected?
     */
    protected static int $format = self::MODE_ANY;

    /**
     * Require base64-encoded PEM's be supplied
     */
    public static function requirePEM(): void
    {
        self::$format = self::MODE_PEM;
    }

    /**
     * Require raw DER's be supplied
     */
    public static function requireDER(): void
    {
        self::$format = self::MODE_DER;
    }

    /**
     * Accept any format and auto detect the format
     *
     * This is the default setting
     */
    public static function requireAny(): void
    {
        self::$format = self::MODE_ANY;
    }
}
<?php

/**
 * OpenSSH Key Handler
 *
 * PHP version 8.1+
 *
 * Place in $HOME/.ssh/authorized_keys
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\Common\Formats\Keys;

use phpseclib4\Common\Functions\Strings;
use phpseclib4\Crypt\AES;
use phpseclib4\Exception\{
    BadDecryptionException,
    PasswordNeededException,
    UnexpectedValueException,
    UnsupportedAlgorithmException,
    UnsupportedValueException
};

/**
 * OpenSSH Formatted RSA Key Handler
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class OpenSSH
{
    /**
     * Default comment
     */
    protected static string $comment = 'phpseclib-generated-key';

    /**
     * Binary key flag
     */
    protected static bool $binary = false;

    protected static array $types = [];

    /**
     * Sets the default comment
     */
    public static function setComment(string $comment): void
    {
        self::$comment = str_replace(["\r", "\n"], '', $comment);
    }

    /**
     * Break a public or private key down into its constituent components
     *
     * $type can be either ssh-dss or ssh-rsa
     */
    public static function load(
        #[\SensitiveParameter] string $key,
        #[\SensitiveParameter] ?string $password = null
    ): array {
        // key format is described here:
        // https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.key?annotate=HEAD

        if (str_contains($key, 'BEGIN OPENSSH PRIVATE KEY')) {
            $key = preg_replace('#(?:^-.*?-[\r\n]*$)|\s#ms', '', $key);
            $key = Strings::base64_decode($key);
            $magic = Strings::shift($key, 15);
            if ($magic != "openssh-key-v1\0") {
                throw new UnexpectedValueException('Expected openssh-key-v1');
            }
            [$ciphername, $kdfname, $kdfoptions, $numKeys] = Strings::unpackSSH2('sssN', $key);
            if ($numKeys != 1) {
                // if we wanted to support multiple keys we could update PublicKeyLoader to preview what the # of keys
                // would be; it'd then call Common\Keys\OpenSSH.php::load() and get the paddedKey. it'd then pass
                // that to the appropriate key loading parser $numKey times or something
                throw new UnsupportedValueException('Although the OpenSSH private key format supports multiple keys phpseclib does not');
            }

            switch ($ciphername) {
                case 'none':
                    break;
                case 'aes256-ctr':
                    if ($kdfname != 'bcrypt') {
                        throw new UnsupportedAlgorithmException('Only the bcrypt kdf is supported (' . $kdfname . ' encountered)');
                    }
                    if (!isset($password)) {
                        throw new PasswordNeededException('Key is encrypted but no password has been provided');
                    }
                    [$salt, $rounds] = Strings::unpackSSH2('sN', $kdfoptions);
                    $crypto = new AES('ctr');
                    //$crypto->setKeyLength(256);
                    //$crypto->disablePadding();
                    $crypto->setPassword($password, 'bcrypt', $salt, $rounds, 32);
                    break;
                default:
                    throw new UnsupportedAlgorithmException('The only supported ciphers are: none, aes256-ctr (' . $ciphername . ' is being used)');
            }

            [$publicKey, $paddedKey] = Strings::unpackSSH2('ss', $key);
            [$type] = Strings::unpackSSH2('s', $publicKey);
            if (isset($crypto)) {
                $paddedKey = $crypto->decrypt($paddedKey);
            }
            [$checkint1, $checkint2] = Strings::unpackSSH2('NN', $paddedKey);
            // any leftover bytes in $paddedKey are for padding? but they should be sequential bytes. eg. 1, 2, 3, etc.
            if ($checkint1 != $checkint2) {
                if (isset($crypto)) {
                    throw new BadDecryptionException('Unable to decrypt key - please verify the password you are using');
                }
                throw new UnexpectedValueException("The two checkints do not match ($checkint1 vs. $checkint2)");
            }
            self::checkType($type);

            return compact('type', 'publicKey', 'paddedKey');
        }

        $parts = preg_split("#[\t ]+#", $key, 3);

        if (!isset($parts[1])) {
            $key = Strings::base64_decode($parts[0]);
            $comment = null;
        } else {
            $asciiType = $parts[0];
            self::checkType($parts[0]);
            $key = Strings::base64_decode($parts[1]);
            $comment = $parts[2] ?? null;
        }

        [$type] = Strings::unpackSSH2('s', $key);
        self::checkType($type);
        if (isset($asciiType) && $asciiType != $type) {
            throw new UnexpectedValueException('Two different types of keys are claimed: ' . $asciiType . ' and ' . $type);
        }
        if (strlen($key) <= 4) {
            throw new UnexpectedValueException('Key appears to be malformed');
        }

        $publicKey = $key;

        return compact('type', 'publicKey', 'comment');
    }

    /**
     * Enable binary output
     */
    public static function enableBinaryOutput(): void
    {
        self::$binary = true;
    }

    /**
     * Disable binary output (ie. enable printable output)
     *
     * Printable output is what goes into $HOME/.ssh/authorized_key.
     */
    public static function disableBinaryOutput(): void
    {
        self::$binary = false;
    }

    /**
     * Checks to see if the type is valid
     */
    private static function checkType(string $candidate): void
    {
        if (!in_array($candidate, static::$types)) {
            throw new UnexpectedValueException("The key type ($candidate) is not equal to: " . implode(',', static::$types));
        }
    }

    /**
     * Wrap a private key appropriately
     */
    protected static function wrapPrivateKey(
        string $publicKey,
        #[\SensitiveParameter] string $privateKey,
        #[\SensitiveParameter] ?string $password,
        array $options
    ): string {
        [, $checkint] = unpack('N', random_bytes(4));

        $comment = $options['comment'] ?? self::$comment;
        $paddedKey = Strings::packSSH2('NN', $checkint, $checkint) .
                     $privateKey .
                     Strings::packSSH2('s', $comment);

        $usesEncryption = isset($password);

        /*
           from http://tools.ietf.org/html/rfc4253#section-6 :

           Note that the length of the concatenation of 'packet_length',
           'padding_length', 'payload', and 'random padding' MUST be a multiple
           of the cipher block size or 8, whichever is larger.
         */
        $blockSize = $usesEncryption ? 16 : 8;
        $paddingLength = (($blockSize - 1) * strlen($paddedKey)) % $blockSize;
        for ($i = 1; $i <= $paddingLength; $i++) {
            $paddedKey .= chr($i);
        }
        if (!$usesEncryption) {
            $key = Strings::packSSH2('sssNss', 'none', 'none', '', 1, $publicKey, $paddedKey);
        } else {
            $rounds = $options['rounds'] ?? 16;
            $salt = random_bytes(16);
            $kdfoptions = Strings::packSSH2('sN', $salt, $rounds);
            $crypto = new AES('ctr');
            $crypto->setPassword($password, 'bcrypt', $salt, $rounds, 32);
            $paddedKey = $crypto->encrypt($paddedKey);
            $key = Strings::packSSH2('sssNss', 'aes256-ctr', 'bcrypt', $kdfoptions, 1, $publicKey, $paddedKey);
        }
        $key = "openssh-key-v1\0$key";

        return "-----BEGIN OPENSSH PRIVATE KEY-----\n" .
               chunk_split(Strings::base64_encode($key), 70, "\n") .
               "-----END OPENSSH PRIVATE KEY-----\n";
    }
}
<?php

/**
 * PKCS1 Formatted Key Handler
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\Common\Formats\Keys;

use phpseclib4\Common\Functions\Strings;
use phpseclib4\Crypt\{AES, DES, TripleDES};
use phpseclib4\Exception\{
    PasswordNeededException,
    UnsupportedAlgorithmException,
    UnsupportedValueException
};
use phpseclib4\File\ASN1;

/**
 * PKCS1 Formatted Key Handler
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class PKCS1 extends PKCS
{
    /**
     * Default encryption algorithm
     */
    private static string $defaultEncryptionAlgorithm = 'AES-128-CBC';

    /**
     * Sets the default encryption algorithm
     */
    public static function setEncryptionAlgorithm(string $algo): void
    {
        self::$defaultEncryptionAlgorithm = $algo;
    }

    /**
     * Returns the mode constant corresponding to the mode string
     */
    private static function getEncryptionMode(string $mode): string
    {
        switch ($mode) {
            case 'CBC':
            case 'ECB':
            case 'CFB':
            case 'OFB':
            case 'CTR':
                return $mode;
        }
        throw new UnsupportedValueException('Unsupported block cipher mode of operation');
    }

    /**
     * Returns a cipher object corresponding to a string
     */
    private static function getEncryptionObject(string $algo): AES|DES|TripleDES
    {
        $modes = '(CBC|ECB|CFB|OFB|CTR)';
        switch (true) {
            case preg_match("#^AES-(128|192|256)-$modes$#", $algo, $matches):
                $cipher = new AES(self::getEncryptionMode($matches[2]));
                $cipher->setKeyLength((int) $matches[1]);
                return $cipher;
            case preg_match("#^DES-EDE3-$modes$#", $algo, $matches):
                return new TripleDES(self::getEncryptionMode($matches[1]));
            case preg_match("#^DES-$modes$#", $algo, $matches):
                return new DES(self::getEncryptionMode($matches[1]));
            default:
                throw new UnsupportedAlgorithmException($algo . ' is not a supported algorithm');
        }
    }

    /**
     * Generate a symmetric key for PKCS#1 keys
     */
    private static function generateSymmetricKey(
        #[\SensitiveParameter] string $password,
        string $iv,
        int $length
    ): string {
        $symkey = '';
        $iv = substr($iv, 0, 8);
        while (strlen($symkey) < $length) {
            $symkey .= md5($symkey . $password . $iv, true);
        }
        return substr($symkey, 0, $length);
    }

    /**
     * Break a public or private key down into its constituent components
     */
    protected static function loadHelper(
        #[\SensitiveParameter] string $key,
        #[\SensitiveParameter] ?string $password = null
    ): string {
        /* Although PKCS#1 proposes a format that public and private keys can use, encrypting them is
           "outside the scope" of PKCS#1.  PKCS#1 then refers you to PKCS#12 and PKCS#15 if you're wanting to
           protect private keys, however, that's not what OpenSSL* does.  OpenSSL protects private keys by adding
           two new "fields" to the key - DEK-Info and Proc-Type.  These fields are discussed here:

           http://tools.ietf.org/html/rfc1421#section-4.6.1.1
           http://tools.ietf.org/html/rfc1421#section-4.6.1.3

           DES-EDE3-CBC as an algorithm, however, is not discussed anywhere, near as I can tell.
           DES-CBC and DES-EDE are discussed in RFC1423, however, DES-EDE3-CBC isn't, nor is its key derivation
           function.  As is, the definitive authority on this encoding scheme isn't the IETF but rather OpenSSL's
           own implementation.  ie. the implementation *is* the standard and any bugs that may exist in that
           implementation are part of the standard, as well.

           * OpenSSL is the de facto standard.  It's utilized by OpenSSH and other projects */
        if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) {
            if (!isset($password)) {
                throw new PasswordNeededException('Unable to perform decryption without some sort of password');
            }
            $iv = Strings::hex2bin(trim($matches[2]));
            // remove the Proc-Type / DEK-Info sections as they're no longer needed
            $key = preg_replace('#^(?:Proc-Type|DEK-Info): .*#m', '', $key);
            try {
                $ciphertext = ASN1::extractBER($key);
            } catch (\Exception $e) {
                $ciphertext = $key;
            }
            $crypto = self::getEncryptionObject($matches[1]);
            $crypto->setKey(self::generateSymmetricKey($password, $iv, $crypto->getKeyLength() >> 3));
            $crypto->setIV($iv);
            $key = $crypto->decrypt($ciphertext);
        } else {
            if (self::$format != self::MODE_DER) {
                try {
                    $decoded = ASN1::extractBER($key);
                    $key = $decoded;
                } catch (\Exception $e) {
                    if (self::$format == self::MODE_PEM) {
                        throw $e;
                    }
                }
            }
        }

        return $key;
    }

    /**
     * Wrap a private key appropriately
     */
    protected static function wrapPrivateKey(
        #[\SensitiveParameter] string $key,
        string $type,
        #[\SensitiveParameter] ?string $password,
        array $options = []
    ): string {
        if (!isset($password)) {
            return "-----BEGIN $type PRIVATE KEY-----\r\n" .
                   chunk_split(Strings::base64_encode($key), 64) .
                   "-----END $type PRIVATE KEY-----";
        }

        $encryptionAlgorithm = $options['encryptionAlgorithm'] ?? self::$defaultEncryptionAlgorithm;

        $cipher = self::getEncryptionObject($encryptionAlgorithm);
        $iv = random_bytes($cipher->getBlockLength() >> 3);
        $cipher->setKey(self::generateSymmetricKey($password, $iv, $cipher->getKeyLength() >> 3));
        $cipher->setIV($iv);
        $iv = strtoupper(Strings::bin2hex($iv));
        return "-----BEGIN $type PRIVATE KEY-----\r\n" .
               "Proc-Type: 4,ENCRYPTED\r\n" .
               "DEK-Info: " . $encryptionAlgorithm . ",$iv\r\n" .
               "\r\n" .
               chunk_split(Strings::base64_encode($cipher->encrypt($key)), 64) .
               "-----END $type PRIVATE KEY-----";
    }

    /**
     * Wrap a public key appropriately
     */
    protected static function wrapPublicKey(string $key, string $type): string
    {
        return "-----BEGIN $type PUBLIC KEY-----\r\n" .
               chunk_split(Strings::base64_encode($key), 64) .
               "-----END $type PUBLIC KEY-----";
    }
}
<?php

/**
 * PuTTY Formatted Key Handler
 *
 * See PuTTY's SSHPUBK.C and https://tartarus.org/~simon/putty-snapshots/htmldoc/AppendixC.html
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\Common\Formats\Keys;

use phpseclib4\Common\Functions\Strings;
use phpseclib4\Crypt\{AES, Hash};
use phpseclib4\Exception\{
    BadConfigurationException,
    PasswordNeededException,
    UnexpectedValueException,
    UnsupportedAlgorithmException,
    UnsupportedValueException
};

/**
 * PuTTY Formatted Key Handler
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class PuTTY
{
    /**
     * Public Handler
     *
     * @var string
     */
    public const PUBLIC_HANDLER = '';

    protected static array $types = [];

    /**
     * Default comment
     */
    private static string $comment = 'phpseclib-generated-key';

    /**
     * Default version
     */
    private static int $version = 2;

    /**
     * Sets the default comment
     */
    public static function setComment(string $comment): void
    {
        self::$comment = str_replace(["\r", "\n"], '', $comment);
    }

    /**
     * Sets the default version
     */
    public static function setVersion(int $version): void
    {
        self::$version = match ($version) {
            2, 3 => $version,
            default => throw new UnsupportedValueException('Only supported versions are 2 and 3')
        };
    }

    /**
     * Generate a symmetric key for PuTTY v2 keys
     */
    private static function generateV2Key(string $password, int $length): string
    {
        $symkey = '';
        $sequence = 0;
        while (strlen($symkey) < $length) {
            $temp = pack('Na*', $sequence++, $password);
            $symkey .= Strings::hex2bin(sha1($temp));
        }
        return substr($symkey, 0, $length);
    }

    /**
     * Generate a symmetric key for PuTTY v3 keys
     */
    private static function generateV3Key(
        #[\SensitiveParameter] string $password,
        string $flavour,
        int $memory,
        int $passes,
        string $salt
    ): array {
        if (!function_exists('sodium_crypto_pwhash')) {
            throw new BadConfigurationException('sodium_crypto_pwhash needs to exist for Argon2 password hasing');
        }

        $flavour = match ($flavour) {
            'Argon2i' => SODIUM_CRYPTO_PWHASH_ALG_ARGON2I13,
            'Argon2id' => SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13,
            default => throw new UnsupportedAlgorithmException('Only Argon2i and Argon2id are supported')
        };

        $length = 80; // keylen + ivlen + mac_keylen
        $temp = sodium_crypto_pwhash($length, $password, $salt, $passes, $memory << 10, $flavour);

        $symkey = substr($temp, 0, 32);
        $symiv = substr($temp, 32, 16);
        $hashkey = substr($temp, -32);

        return compact('symkey', 'symiv', 'hashkey');
    }

    /**
     * Break a public or private key down into its constituent components
     */
    public static function load(
        #[\SensitiveParameter] string $key,
        #[\SensitiveParameter] ?string $password
    ): array {

        if (str_contains($key, 'BEGIN SSH2 PUBLIC KEY')) {
            $lines = preg_split('#[\r\n]+#', $key);
            /** @psalm-suppress InvalidArrayOffset */
            switch (true) {
                case $lines[0] != '---- BEGIN SSH2 PUBLIC KEY ----':
                    throw new UnexpectedValueException('Key doesn\'t start with ---- BEGIN SSH2 PUBLIC KEY ----');
                case $lines[count($lines) - 1] != '---- END SSH2 PUBLIC KEY ----':
                    throw new UnexpectedValueException('Key doesn\'t end with ---- END SSH2 PUBLIC KEY ----');
            }
            $lines = array_splice($lines, 1, -1);
            $lines = array_map(fn ($line) => rtrim($line, "\r\n"), $lines);
            $data = $current = '';
            $values = [];
            $in_value = false;
            foreach ($lines as $line) {
                switch (true) {
                    case preg_match('#^(.*?): (.*)#', $line, $match):
                        $in_value = $line[-1] == '\\';
                        $current = strtolower($match[1]);
                        $values[$current] = $in_value ? substr($match[2], 0, -1) : $match[2];
                        break;
                    case $in_value:
                        $in_value = $line[-1] == '\\';
                        $values[$current] .= $in_value ? substr($line, 0, -1) : $line;
                        break;
                    default:
                        $data .= $line;
                }
            }

            $components = ([static::PUBLIC_HANDLER, 'load'])($data) + $values;
            $components['comment'] = str_replace(['\\\\', '\"'], ['\\', '"'], $values['comment']);

            return $components;
        }

        $components = [];

        $key = preg_split('#\r\n|\r|\n#', trim($key));
        if (Strings::shift($key[0], strlen('PuTTY-User-Key-File-')) != 'PuTTY-User-Key-File-') {
            throw new UnexpectedValueException('PuTTY private keys need to begin with "PuTTY-User-Key-File-"');
        }
        $version = (int) Strings::shift($key[0], 3); // should be either "2: " or "3: 0" prior to int casting
        if ($version != 2 && $version != 3) {
            throw new UnsupportedValueException('Only v2 and v3 PuTTY private keys are supported');
        }
        $components['type'] = $type = rtrim($key[0]);
        if (!in_array($type, static::$types)) {
            $error = count(static::$types) == 1 ?
                'Only ' . static::$types[0] . ' keys are supported. ' :
                '';
            throw new UnsupportedAlgorithmException($error . 'This is an unsupported ' . $type . ' key');
        }
        $encryption = trim(preg_replace('#Encryption: (.+)#', '$1', $key[1]));
        $components['comment'] = trim(preg_replace('#Comment: (.+)#', '$1', $key[2]));

        $publicLength = (int) trim(preg_replace('#Public-Lines: (\d+)#', '$1', $key[3]));
        $public = Strings::base64_decode(implode('', array_map('trim', array_slice($key, 4, $publicLength))));

        $source = Strings::packSSH2('ssss', $type, $encryption, $components['comment'], $public);

        ['length' => $length] = unpack('Nlength', Strings::shift($public, 4));
        $newtype = Strings::shift($public, $length);
        if ($newtype != $type) {
            throw new UnexpectedValueException('The binary type does not match the human readable type field');
        }

        $components['public'] = $public;

        $hashkey = match ($version) {
            3 => '',
            2 => 'putty-private-key-file-mac-key'
        };

        if ($encryption != 'none' && !isset($password)) {
            throw new PasswordNeededException('Unable to perform decryption without some sort of password');
        }

        $offset = $publicLength + 4;
        switch ($encryption) {
            case 'aes256-cbc':
                $crypto = new AES('cbc');
                switch ($version) {
                    case 3:
                        $flavour = trim(preg_replace('#Key-Derivation: (.*)#', '$1', $key[$offset++]));
                        $memory = trim(preg_replace('#Argon2-Memory: (\d+)#', '$1', $key[$offset++]));
                        $passes = trim(preg_replace('#Argon2-Passes: (\d+)#', '$1', $key[$offset++]));
                        $parallelism = trim(preg_replace('#Argon2-Parallelism: (\d+)#', '$1', $key[$offset++]));
                        $salt = Strings::hex2bin(trim(preg_replace('#Argon2-Salt: ([0-9a-f]+)#', '$1', $key[$offset++])));

                        if ($parallelism != 1) {
                            throw new UnsupportedValueException('Argon2 parallelism > 1 is not supported by libsodium');
                        }

                        [
                            'symkey' => $symkey,
                            'symiv' => $symiv,
                            'hashkey' => $hashkey
                        ] = self::generateV3Key($password, $flavour, (int) $memory, (int) $passes, $salt);

                        break;
                    case 2:
                        $symkey = self::generateV2Key($password, 32);
                        $symiv = str_repeat("\0", $crypto->getBlockLength() >> 3);
                        $hashkey .= $password;
                }
        }

        switch ($version) {
            case 3:
                $hash = new Hash('sha256');
                $hash->setKey($hashkey);
                break;
            case 2:
                $hash = new Hash('sha1');
                $hash->setKey(sha1($hashkey, true));
        }

        $privateLength = (int) trim(preg_replace('#Private-Lines: (\d+)#', '$1', $key[$offset++]));
        $private = Strings::base64_decode(implode('', array_map('trim', array_slice($key, $offset, $privateLength))));

        if ($encryption != 'none') {
            $crypto->setKey($symkey);
            $crypto->setIV($symiv);
            $crypto->disablePadding();
            $private = $crypto->decrypt($private);
        }

        $source .= Strings::packSSH2('s', $private);

        $hmac = trim(preg_replace('#Private-MAC: (.+)#', '$1', $key[$offset + $privateLength]));
        $hmac = Strings::hex2bin($hmac);

        if (!hash_equals($hash->hash($source), $hmac)) {
            throw new UnexpectedValueException('MAC validation error');
        }

        $components['private'] = $private;

        return $components;
    }

    /**
     * Wrap a private key appropriately
     */
    protected static function wrapPrivateKey(
        string $public,
        #[\SensitiveParameter] string $private,
        string $type,
        #[\SensitiveParameter] ?string $password,
        array $options = []
    ): string {
        $encryption = isset($password) ? 'aes256-cbc' : 'none';
        $comment = $options['comment'] ?? self::$comment;
        $version = $options['version'] ?? self::$version;

        $key = "PuTTY-User-Key-File-$version: $type\r\n";
        $key .= "Encryption: $encryption\r\n";
        $key .= "Comment: $comment\r\n";

        $public = Strings::packSSH2('s', $type) . $public;

        $source = Strings::packSSH2('ssss', $type, $encryption, $comment, $public);

        $public = Strings::base64_encode($public);
        $key .= "Public-Lines: " . ((strlen($public) + 63) >> 6) . "\r\n";
        $key .= chunk_split($public, 64);

        if (!isset($password)) {
            $source .= Strings::packSSH2('s', $private);
            switch ($version) {
                case 3:
                    $hash = new Hash('sha256');
                    $hash->setKey('');
                    break;
                case 2:
                    $hash = new Hash('sha1');
                    $hash->setKey(sha1('putty-private-key-file-mac-key', true));
            }
        } else {
            $private .= random_bytes(16 - (strlen($private) & 15));
            $source .= Strings::packSSH2('s', $private);
            $crypto = new AES('cbc');

            switch ($version) {
                case 3:
                    $salt = random_bytes(16);
                    $key .= "Key-Derivation: Argon2id\r\n";
                    $key .= "Argon2-Memory: 8192\r\n";
                    $key .= "Argon2-Passes: 13\r\n";
                    $key .= "Argon2-Parallelism: 1\r\n";
                    $key .= "Argon2-Salt: " . Strings::bin2hex($salt) . "\r\n";
                    [
                        'symkey' => $symkey,
                        'symiv' => $symiv,
                        'hashkey' => $hashkey
                    ] = self::generateV3Key($password, 'Argon2id', 8192, 13, $salt);

                    $hash = new Hash('sha256');
                    $hash->setKey($hashkey);

                    break;
                case 2:
                    $symkey = self::generateV2Key($password, 32);
                    $symiv = str_repeat("\0", $crypto->getBlockLength() >> 3);
                    $hashkey = 'putty-private-key-file-mac-key' . $password;

                    $hash = new Hash('sha1');
                    $hash->setKey(sha1($hashkey, true));
            }

            $crypto->setKey($symkey);
            $crypto->setIV($symiv);
            $crypto->disablePadding();
            $private = $crypto->encrypt($private);
        }

        $private = Strings::base64_encode($private);
        $key .= 'Private-Lines: ' . ((strlen($private) + 63) >> 6) . "\r\n";
        $key .= chunk_split($private, 64);
        $key .= 'Private-MAC: ' . Strings::bin2hex($hash->hash($source)) . "\r\n";

        return $key;
    }

    /**
     * Wrap a public key appropriately
     *
     * This is basically the format described in RFC 4716 (https://tools.ietf.org/html/rfc4716)
     */
    protected static function wrapPublicKey(string $key, string $type): string
    {
        $key = pack('Na*a*', strlen($type), $type, $key);
        $key = "---- BEGIN SSH2 PUBLIC KEY ----\r\n" .
               'Comment: "' . str_replace(['\\', '"'], ['\\\\', '\"'], self::$comment) . "\"\r\n" .
               chunk_split(Strings::base64_encode($key), 64) .
               '---- END SSH2 PUBLIC KEY ----';
        return $key;
    }
}
<?php

/**
 * JSON Web Key (RFC7517) Handler
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2022-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\Common\Formats\Keys;

use phpseclib4\Exception\{UnexpectedValueException, UnsupportedValueException};

/**
 * JSON Web Key Formatted Key Handler
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class JWK
{
    /**
     * Break a public or private key down into its constituent components
     */
    protected static function loadHelper(#[\SensitiveParameter] string $key): \stdClass
    {
        $key = preg_replace('#\s#', '', $key); // remove whitespace

        $key = json_decode($key, null, 512, JSON_THROW_ON_ERROR);

        if (isset($key->kty)) {
            return $key;
        }

        if (!isset($key->keys)) {
            throw new UnexpectedValueException('Invalid JWK: object has no property "keys"');
        }

        if (count($key->keys) != 1) {
            throw new UnsupportedValueException('Although the JWK key format supports multiple keys phpseclib does not');
        }

        return $key->keys[0];
    }

    /**
     * Wrap a key appropriately
     */
    protected static function wrapKey(array $key, array $options): string
    {
        return json_encode(['keys' => [$key + $options]]);
    }
}
<?php

/**
 * Raw Signature Handler
 *
 * PHP version 8.1+
 *
 * Handles signatures as arrays
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\Common\Formats\Signature;

use phpseclib4\Math\BigInteger;

/**
 * Raw Signature Handler
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class Raw
{
    /**
     * Loads a signature
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public static function load(array $sig): array
    {
        switch (true) {
            case !isset($sig['r']) || !isset($sig['s']):
            case !$sig['r'] instanceof BigInteger:
            case !$sig['s'] instanceof BigInteger:
                return [];
        }

        return [
            'r' => $sig['r'],
            's' => $sig['s'],
        ];
    }

    /**
     * Returns a signature in the appropriate format
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public static function save(BigInteger $r, BigInteger $s): array
    {
        return compact('r', 's');
    }
}
<?php

/**
 * Base Class for all block ciphers
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @author    Hans-Juergen Petrich <petrich@tronic-media.com>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\Common;

use phpseclib4\Exception\InvalidArgumentException;

/**
 * Base Class for all block cipher classes
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class BlockCipher extends SymmetricKey
{
    /**
     * Default Constructor.
     */
    public function __construct(string $mode)
    {
        parent::__construct($mode);

        if ($this->mode == self::MODE_STREAM) {
            throw new InvalidArgumentException('Block ciphers cannot be ran in stream mode');
        }
    }
}
<?php

/**
 * PublicKey interface
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2019-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\Common;

/**
 * PublicKey interface
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
interface PublicKey
{
    public function verify(string $message, string|array $signature): bool;
    //public function encrypt(string $plaintext): string;
    public function toString(string $type, array $options = []): string;
    public function getFingerprint(string $algorithm): string;
}
<?php

/**
 * Base Class for all asymmetric key ciphers
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\Common;

use phpseclib4\Crypt\Hash;
use phpseclib4\Exception\{
    BadConfigurationException,
    InvalidStateException,
    NoKeyLoadedException,
    PasswordNeededException ,
    UnsupportedValueException
};
use phpseclib4\Math\BigInteger;

/**
 * Base Class for all asymmetric cipher classes
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class AsymmetricKey
{
    /**
     * Precomputed Zero
     */
    protected static BigInteger $zero;

    /**
     * Precomputed One
     */
    protected static BigInteger $one;

    /**
     * Format of the loaded key
     */
    protected string $format;

    /**
     * Hash function
     */
    protected Hash $hash;

    /**
     * HMAC function
     */
    private Hash $hmac;

    /**
     * Supported plugins (lower case)
     *
     * @see self::initialize_static_variables()
     */
    private static array $plugins = [];

    /**
     * Invisible plugins
     *
     * @see self::initialize_static_variables()
     */
    private static array $invisiblePlugins = [];

    /**
     * Key Comment
     */
    private ?string $comment;

    /**
     * OpenSSL configuration file name.
     *
     * @see self::createKey()
     */
    protected static ?string $configFile;

    /**
     * Algorithm Name
     *
     * This really shouldn't be needed. The child classes that extend AsymmetricKey define it so
     * there's no need for this class to do so, however, if this class doesn't define it then psalm
     * will complain
     *
     * @see self::load()
     */
    public const ALGORITHM = '';
    protected static ?string $forcedEngine = null;

    abstract public function toString(string $type, array $options = []): string;
    abstract protected static function onLoad(array $components): mixed;

    /**
     * The constructor
     */
    protected function __construct()
    {
        self::initialize_static_variables();

        $this->hash = new Hash('sha256');
        $this->hmac = new Hash('sha256');
    }

    /**
     * Initialize static variables
     */
    protected static function initialize_static_variables(): void
    {
        if (!isset(self::$zero)) {
            self::$zero = new BigInteger(0);
            self::$one = new BigInteger(1);
        }

        if (!isset(self::$configFile)) {
            self::$configFile = dirname(__FILE__) . '/../../openssl.cnf';
        }

        self::loadPlugins('Keys');
        if (static::ALGORITHM != 'RSA' && static::ALGORITHM != 'DH') {
            self::loadPlugins('Signature');
        }
    }

    /**
     * Load the key
     */
    public static function load(
        #[\SensitiveParameter] string|array $key,
        #[\SensitiveParameter] ?string $password = null
    ): static {
        self::initialize_static_variables();

        $class = new \ReflectionClass(static::class);
        if ($class->isFinal()) {
            throw new InvalidStateException('load() should not be called from final classes (' . static::class . ')');
        }

        foreach (self::$plugins[static::ALGORITHM]['Keys'] as $format) {
            if (isset(self::$invisiblePlugins[static::ALGORITHM]) && in_array($format, self::$invisiblePlugins[static::ALGORITHM])) {
                continue;
            }
            try {
                $components = $format::load($key, $password);
                break;
            } catch (PasswordNeededException $e) {
                throw $e;
            } catch (\Throwable) {
            }
        }

        if (!isset($components)) {
            throw new NoKeyLoadedException('Unable to read key');
        }

        $components['format'] = $format;
        $components['secret'] ??= '';
        $comment = $components['comment'] ?? null;
        $new = static::onLoad($components);
        $new->format = $format;
        $new->comment = $comment;
        return $new instanceof PrivateKey ?
            $new->withPassword($password) :
            $new;
    }

    /**
     * Loads a private key
     */
    public static function loadPrivateKey(
        #[\SensitiveParameter] string|array $key,
        #[\SensitiveParameter] ?string $password = null
    ): PrivateKey {
        $key = self::load($key, $password);
        if (!$key instanceof PrivateKey) {
            throw new NoKeyLoadedException('The key that was loaded was not a private key');
        }
        return $key;
    }

    /**
     * Loads a public key
     */
    public static function loadPublicKey(string|array $key): PublicKey
    {
        try {
            $key = self::load($key);
        } catch (PasswordNeededException) {
            $key = null;
        }
        if (!$key instanceof PublicKey) {
            throw new NoKeyLoadedException('The key that was loaded was not a public key');
        }
        return $key;
    }

    /**
     * Loads parameters
     */
    public static function loadParameters(string $key): static
    {
        try {
            $key = self::load($key);
        } catch (PasswordNeededException) {
            $key = null;
        }
        if (!isset($key) || $key instanceof PublicKey || $key instanceof PrivateKey) {
            throw new NoKeyLoadedException('The key that was loaded was not a parameter');
        }
        return $key;
    }

    /**
     * Load the key, assuming a specific format
     */
    public static function loadFormat(
        string $type,
        #[\SensitiveParameter] string|array $key,
        #[\SensitiveParameter] ?string $password = null
    ): static {
        self::initialize_static_variables();

        $components = false;
        $format = strtolower($type);
        if (isset(self::$plugins[static::ALGORITHM]['Keys'][$format])) {
            $format = self::$plugins[static::ALGORITHM]['Keys'][$format];
            $components = $format::load($key, $password);
        }

        $components['format'] = $format;
        $components['secret'] ??= '';

        $new = static::onLoad($components);
        $new->format = $format;
        return $new instanceof PrivateKey ?
            $new->withPassword($password) :
            $new;
    }

    /**
     * Loads a private key
     */
    public static function loadPrivateKeyFormat(
        string $type,
        #[\SensitiveParameter] string|array $key,
        #[\SensitiveParameter] ?string $password = null
    ): PrivateKey {
        $key = self::loadFormat($type, $key, $password);
        if (!$key instanceof PrivateKey) {
            throw new NoKeyLoadedException('The key that was loaded was not a private key');
        }
        return $key;
    }

    /**
     * Loads a public key
     */
    public static function loadPublicKeyFormat(string|array $type, string $key): PublicKey
    {
        $key = self::loadFormat($type, $key);
        if (!$key instanceof PublicKey) {
            throw new NoKeyLoadedException('The key that was loaded was not a public key');
        }
        return $key;
    }

    /**
     * Loads parameters
     */
    public static function loadParametersFormat(string $type, string $key): static
    {
        $key = self::loadFormat($type, $key);
        if ($key instanceof PrivateKey || $key instanceof PublicKey) {
            throw new NoKeyLoadedException('The key that was loaded was not a parameter');
        }
        return $key;
    }

    /**
     * Validate Plugin
     */
    protected static function validatePlugin(string $format, string $type, ?string $method = null): string
    {
        $type = strtolower($type);
        if (!isset(self::$plugins[static::ALGORITHM][$format][$type])) {
            throw new UnsupportedValueException("$type is not a supported format");
        }
        $type = self::$plugins[static::ALGORITHM][$format][$type];
        if (isset($method) && !method_exists($type, $method)) {
            throw new UnsupportedValueException("$type does not implement $method");
        }

        return $type;
    }

    /**
     * Load Plugins
     */
    private static function loadPlugins(string $format): void
    {
        if (!isset(self::$plugins[static::ALGORITHM][$format])) {
            self::$plugins[static::ALGORITHM][$format] = [];
            foreach (new \DirectoryIterator(__DIR__ . '/../' . static::ALGORITHM . '/Formats/' . $format . '/') as $file) {
                if ($file->getExtension() != 'php') {
                    continue;
                }
                $name = $file->getBasename('.php');
                if ($name[0] == '.') {
                    continue;
                }
                $type = 'phpseclib4\Crypt\\' . static::ALGORITHM . '\\Formats\\' . $format . '\\' . $name;
                $reflect = new \ReflectionClass($type);
                if ($reflect->isTrait()) {
                    continue;
                }
                self::$plugins[static::ALGORITHM][$format][strtolower($name)] = $type;
                if ($reflect->hasConstant('IS_INVISIBLE')) {
                    self::$invisiblePlugins[static::ALGORITHM][] = $type;
                }
            }
        }
    }

    /**
     * Returns a list of supported formats.
     */
    public static function getSupportedKeyFormats(): array
    {
        self::initialize_static_variables();

        return self::$plugins[static::ALGORITHM]['Keys'];
    }

    /**
     * Sets the OpenSSL config file path
     *
     * Set to the empty string to use the default config file
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public static function setOpenSSLConfigPath(string $path): void
    {
        self::$configFile = $path;
    }

    /**
     * Add a fileformat plugin
     *
     * The plugin needs to either already be loaded or be auto-loadable.
     * Loading a plugin whose shortname overwrite an existing shortname will overwrite the old plugin.
     *
     * @see self::load()
     */
    public static function addFileFormat(string $fullname): void
    {
        self::initialize_static_variables();

        if (class_exists($fullname)) {
            $meta = new \ReflectionClass($fullname);
            $shortname = $meta->getShortName();
            self::$plugins[static::ALGORITHM]['Keys'][strtolower($shortname)] = $fullname;
            if ($meta->hasConstant('IS_INVISIBLE')) {
                self::$invisiblePlugins[static::ALGORITHM][] = strtolower($shortname);
            }
        }
    }

    /**
     * Returns the format of the loaded key.
     *
     * If the key that was loaded wasn't in a valid or if the key was auto-generated
     * with RSA::createKey() then this will throw an exception.
     *
     * @see self::load()
     */
    public function getLoadedFormat(): string
    {
        if (empty($this->format)) {
            throw new NoKeyLoadedException('This key was created with createKey - it was not loaded with load. Therefore there is no "loaded format"');
        }

        $meta = new \ReflectionClass($this->format);
        return $meta->getShortName();
    }

    /**
     * Returns the key's comment
     *
     * Not all key formats support comments. If you want to set a comment use toString()
     */
    public function getComment(): ?string
    {
        return $this->comment;
    }

    /**
     * Force engine (useful for unit testing)
     */
    public static function forceEngine(?string $engine = null): void
    {
        if (!isset($engine)) {
            static::$forcedEngine = null;
            return;
        }
        switch ($engine) {
            case 'PHP':
            case 'OpenSSL':
            case 'libsodium':
                static::$forcedEngine = $engine;
                break;
            default:
                throw new BadConfigurationException('Valid engines are null, PHP, OpenSSL or libsodium');
        }
    }

    public static function getForcedEngine(): ?string
    {
        return static::$forcedEngine;
    }

    /**
     * __toString() magic method
     */
    public function __toString(): string
    {
        return $this->toString('PKCS8');
    }

    /**
     * __debugInfo() magic method
     */
    public function __debugInfo(): array
    {
        return ['value' => "$this"];
    }

    /**
     * Determines which hashing function should be used
     */
    public function withHash(string $hash): static
    {
        $new = clone $this;

        $new->hash = new Hash($hash);
        $new->hmac = new Hash($hash);

        return $new;
    }

    /**
     * Returns the hash algorithm currently being used
     */
    public function getHash(): Hash
    {
        return clone $this->hash;
    }

    /**
     * Compute the pseudorandom k for signature generation,
     * using the process specified for deterministic DSA.
     *
     * Not currently used but the method exists in case we do
     * want to use it at some point
     *
     * @psalm-suppress PossiblyUnusedMethod
     * @psalm-suppress UndefinedThisPropertyFetch
     */
    protected function computek(string $h1): string
    {
        $v = str_repeat("\1", strlen($h1));

        $k = str_repeat("\0", strlen($h1));

        $x = $this->int2octets($this->x);
        $h1 = $this->bits2octets($h1);

        $this->hmac->setKey($k);
        $k = $this->hmac->hash($v . "\0" . $x . $h1);
        $this->hmac->setKey($k);
        $v = $this->hmac->hash($v);
        $k = $this->hmac->hash($v . "\1" . $x . $h1);
        $this->hmac->setKey($k);
        $v = $this->hmac->hash($v);

        $qlen = $this->q->getLengthInBytes();

        while (true) {
            $t = '';
            while (strlen($t) < $qlen) {
                $v = $this->hmac->hash($v);
                $t = $t . $v;
            }
            $k = $this->bits2int($t);

            if (!$k->equals(self::$zero) && $k->compare($this->q) < 0) {
                break;
            }
            $k = $this->hmac->hash($v . "\0");
            $this->hmac->setKey($k);
            $v = $this->hmac->hash($v);
        }

        return $k;
    }

    /**
     * Integer to Octet String
     *
     * @psalm-suppress UndefinedThisPropertyFetch
     */
    private function int2octets(BigInteger $v): string
    {
        $out = $v->toBytes();
        $rolen = $this->q->getLengthInBytes();
        if (strlen($out) < $rolen) {
            return str_pad($out, $rolen, "\0", STR_PAD_LEFT);
        } elseif (strlen($out) > $rolen) {
            return substr($out, -$rolen);
        } else {
            return $out;
        }
    }

    /**
     * Bit String to Integer
     *
     * @psalm-suppress UndefinedThisPropertyFetch
     */
    protected function bits2int(string $in): BigInteger
    {
        $v = new BigInteger($in, 256);
        $vlen = strlen($in) << 3;
        $qlen = $this->q->getLength();
        if ($vlen > $qlen) {
            return $v->bitwise_rightShift($vlen - $qlen);
        }
        return $v;
    }

    /**
     * Bit String to Octet String
     *
     * @psalm-suppress UndefinedThisPropertyFetch
     */
    private function bits2octets(string $in): string
    {
        $z1 = $this->bits2int($in);
        $z2 = $z1->subtract($this->q);
        return $z2->compare(self::$zero) < 0 ?
            $this->int2octets($z1) :
            $this->int2octets($z2);
    }
}
<?php

/**
 * PrivateKey interface
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2019-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\Common;

use phpseclib4\File\Common\Signable;

/**
 * PrivateKey interface
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
interface PrivateKey
{
    public function sign(string|Signable $source): string|array;
    //public function decrypt($ciphertext);
    public function getPublicKey(): PublicKey;
    public function toString(string $type, array $options = []): string;

    /**
     * @return static
     */
    public function withPassword(#[\SensitiveParameter] ?string $password = null): PrivateKey;
}
<?php

/**
 * Base Class for all stream ciphers
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @author    Hans-Juergen Petrich <petrich@tronic-media.com>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\Common;

/**
 * Base Class for all stream cipher classes
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class StreamCipher extends SymmetricKey
{
    /**
     * Block Length of the cipher
     *
     * Stream ciphers do not have a block size
     *
     * @see SymmetricKey::block_size
     */
    protected int $block_size = 0;

    /**
     * Default Constructor.
     *
     * @see \phpseclib4\Crypt\Common\SymmetricKey::__construct()
     */
    public function __construct()
    {
        parent::__construct('stream');
    }

    /**
     * Stream ciphers not use an IV
     */
    public function usesIV(): bool
    {
        return false;
    }
}
<?php

/**
 * Pure-PHP PKCS#1 (v2.1) compliant implementation of RSA.
 *
 * PHP version 8.1+
 *
 * Here's an example of how to encrypt and decrypt text with this library:
 * <code>
 * <?php
 * include 'vendor/autoload.php';
 *
 * $private = \phpseclib4\Crypt\RSA::createKey();
 * $public = $private->getPublicKey();
 *
 * $plaintext = 'terrafrost';
 *
 * $ciphertext = $public->encrypt($plaintext);
 *
 * echo $private->decrypt($ciphertext);
 * ?>
 * </code>
 *
 * Here's an example of how to create signatures and verify signatures with this library:
 * <code>
 * <?php
 * include 'vendor/autoload.php';
 *
 * $private = \phpseclib4\Crypt\RSA::createKey();
 * $public = $private->getPublicKey();
 *
 * $plaintext = 'terrafrost';
 *
 * $signature = $private->sign($plaintext);
 *
 * echo $public->verify($plaintext, $signature) ? 'verified' : 'unverified';
 * ?>
 * </code>
 *
 * One thing to consider when using this: so phpseclib uses PSS mode by default.
 * Technically, id-RSASSA-PSS has a different key format than rsaEncryption. So
 * should phpseclib save to the id-RSASSA-PSS format by default or the
 * rsaEncryption format? For stand-alone keys I figure rsaEncryption is better
 * because SSH doesn't use PSS and idk how many SSH servers would be able to
 * decode an id-RSASSA-PSS key. For X.509 certificates the id-RSASSA-PSS
 * format is used by default (unless you change it up to use PKCS1 instead)
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2009-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt;

use phpseclib4\Crypt\Common\AsymmetricKey;
use phpseclib4\Crypt\RSA\Formats\Keys\PSS;
use phpseclib4\Crypt\RSA\{PrivateKey, PublicKey};
use phpseclib4\Exception\{
    BadConfigurationException,
    BadMethodCallException,
    InvalidArgumentException,
    InvalidStateException,
    LengthException,
    UnsupportedAlgorithmException
};
use phpseclib4\Math\BigInteger;

/**
 * Pure-PHP PKCS#1 compliant implementation of RSA.
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class RSA extends AsymmetricKey
{
    /**
     * Algorithm Name
     *
     * @var string
     */
    public const ALGORITHM = 'RSA';

    /**
     * Use {@link http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding Optimal Asymmetric Encryption Padding}
     * (OAEP) for encryption / decryption.
     *
     * Uses sha256 by default
     *
     * @see self::setHash()
     * @see self::setMGFHash()
     * @see self::encrypt()
     * @see self::decrypt()
     */
    public const ENCRYPTION_OAEP = 1;

    /**
     * Use PKCS#1 padding.
     *
     * Although self::PADDING_OAEP / self::PADDING_PSS  offers more security, including PKCS#1 padding is necessary for purposes of backwards
     * compatibility with protocols (like SSH-1) written before OAEP's introduction.
     *
     * @see self::encrypt()
     * @see self::decrypt()
     */
    public const ENCRYPTION_PKCS1 = 2;

    /**
     * Do not use any padding
     *
     * Although this method is not recommended it can none-the-less sometimes be useful if you're trying to decrypt some legacy
     * stuff, if you're trying to diagnose why an encrypted message isn't decrypting, etc.
     *
     * @see self::encrypt()
     * @see self::decrypt()
     */
    public const ENCRYPTION_NONE = 4;

    /**
     * Use the Probabilistic Signature Scheme for signing
     *
     * Uses sha256 and 0 as the salt length
     *
     * @see self::setSaltLength()
     * @see self::setMGFHash()
     * @see self::setHash()
     * @see self::sign()
     * @see self::verify()
     * @see self::setHash()
     */
    public const SIGNATURE_PSS = 16;

    /**
     * Use PKCS#1 padding for signature verification
     *
     * @see self::sign()
     * @see self::verify()
     * @see self::setHash()
     */
    public const SIGNATURE_PKCS1 = 32;

    /**
     * Encryption padding mode
     */
    protected int $encryptionPadding = self::ENCRYPTION_OAEP;

    /**
     * Signature padding mode
     */
    protected int $signaturePadding = self::SIGNATURE_PSS;

    /**
     * Length of hash function output
     */
    protected int $hLen;

    /**
     * Length of salt
     */
    protected ?int $sLen = null;

    /**
     * Label
     */
    protected string $label = '';

    /**
     * Hash function for the Mask Generation Function
     */
    protected Hash $mgfHash;

    /**
     * Length of MGF hash function output
     */
    protected int $mgfHLen;

    /**
     * Modulus (ie. n)
     */
    protected BigInteger $modulus;

    /**
     * Modulus length
     */
    protected int $k;

    /**
     * Exponent (ie. e or d)
     */
    protected BigInteger $exponent;

    /**
     * Default public exponent
     *
     * @link http://en.wikipedia.org/wiki/65537_%28number%29
     */
    private static int $defaultExponent = 65537;

    /**
     * Enable Blinding?
     */
    protected static bool $enableBlinding = true;

    /**
     * Enable automatic salt length determination
     */
    protected static bool $autoSaltLength = true;

    /**
     * Smallest Prime
     *
     * Per <http://cseweb.ucsd.edu/~hovav/dist/survey.pdf#page=5>, this number ought not result in primes smaller
     * than 256 bits. As a consequence if the key you're trying to create is 1024 bits and you've set smallestPrime
     * to 384 bits then you're going to get a 384 bit prime and a 640 bit prime (384 + 1024 % 384). At least if
     * engine is set to self::ENGINE_INTERNAL. If Engine is set to self::ENGINE_OPENSSL then smallest Prime is
     * ignored (ie. multi-prime RSA support is more intended as a way to speed up RSA key generation when there's
     * a chance neither gmp nor OpenSSL are installed)
     */
    private static int $smallestPrime = 4096;

    /**
     * Public Exponent
     */
    protected BigInteger $publicExponent;

    /**
     * Forced Engine
     *
     * @see self::forceEngine()
     */
    protected static ?string $forcedEngine = null;

    /**
     * Sets the public exponent for key generation
     *
     * This will be 65537 unless changed.
     */
    public static function setExponent(int $val): void
    {
        self::$defaultExponent = $val;
    }

    /**
     * Sets the smallest prime number in bits. Used for key generation
     *
     * This will be 4096 unless changed.
     */
    public static function setSmallestPrime(int $val): void
    {
        self::$smallestPrime = $val;
    }

    /**
     * Create a private key
     *
     * The public key can be extracted from the private key
     */
    public static function createKey(int $bits = 2048): PrivateKey
    {
        self::initialize_static_variables();

        $class = new \ReflectionClass(static::class);
        if ($class->isFinal()) {
            throw new BadMethodCallException('createKey() should not be called from final classes (' . static::class . ')');
        }

        if (self::$forcedEngine == 'libsodium' || (self::$forcedEngine == 'OpenSSL' && !function_exists('openssl_pkey_new'))) {
            throw new BadConfigurationException('Engine ' . self::$forcedEngine . ' is forced but unsupported for RSA');
        }

        $regSize = $bits >> 1; // divide by two to see how many bits P and Q would be
        if ($regSize > self::$smallestPrime) {
            $num_primes = floor($bits / self::$smallestPrime);
            $regSize = self::$smallestPrime;
        } else {
            $num_primes = 2;
        }

        if ($num_primes == 2 && $bits >= 384 && self::$defaultExponent == 65537) {
            // at this point the only two supported values for self::$forcedEngine are OpenSSL, PHP and null
            // if it's either OpenSSL or null we'll use OpenSSL (if it's available)
            if (self::$forcedEngine !== 'PHP' && function_exists('openssl_pkey_new')) {
                $config = [];
                if (self::$configFile) {
                    $config['config'] = self::$configFile;
                }
                // OpenSSL uses 65537 as the exponent and requires RSA keys be 384 bits minimum
                $rsa = openssl_pkey_new(['private_key_bits' => $bits] + $config);
                if (!$rsa || !openssl_pkey_export($rsa, $privatekeystr, null, $config)) {
                    if (isset(self::$forcedEngine)) {
                        throw new BadConfigurationException('Engine OpenSSL is forced but produced an error - ' . openssl_error_string());
                    }
                } else {
                    // clear the buffer of error strings stemming from a minimalistic openssl.cnf
                    // https://github.com/php/php-src/issues/11054 talks about other errors this'll pick up
                    while (openssl_error_string() !== false) {
                    }

                    return RSA::load($privatekeystr);
                }
            }
        }

        $e = new BigInteger(self::$defaultExponent);

        do {
            $n = clone self::$one;
            $exponents = $coefficients = $primes = [];
            $lcm = [
                'top' => clone self::$one,
                'bottom' => false,
            ];

            for ($i = 1; $i <= $num_primes; $i++) {
                if ($i != $num_primes) {
                    $primes[$i] = BigInteger::randomPrime($regSize);
                } else {
                    ['min' => $min, 'max' => $max] = BigInteger::minMaxBits($bits);
                    [$min] = $min->divide($n);
                    $min = $min->add(self::$one);
                    [$max] = $max->divide($n);
                    $primes[$i] = BigInteger::randomRangePrime($min, $max);
                }

                // the first coefficient is calculated differently from the rest
                // ie. instead of being $primes[1]->modInverse($primes[2]), it's $primes[2]->modInverse($primes[1])
                if ($i > 2) {
                    $coefficients[$i] = $n->modInverse($primes[$i]);
                }

                $n = $n->multiply($primes[$i]);

                $temp = $primes[$i]->subtract(self::$one);

                // textbook RSA implementations use Euler's totient function instead of the least common multiple.
                // see http://en.wikipedia.org/wiki/Euler%27s_totient_function
                $lcm['top'] = $lcm['top']->multiply($temp);
                $lcm['bottom'] = $lcm['bottom'] === false ? $temp : $lcm['bottom']->gcd($temp);
            }

            [$temp] = $lcm['top']->divide($lcm['bottom']);
            $gcd = $temp->gcd($e);
        } while (!$gcd->equals(self::$one));

        $coefficients[2] = $primes[2]->modInverse($primes[1]);

        $d = $e->modInverse($temp);

        foreach ($primes as $i => $prime) {
            $temp = $prime->subtract(self::$one);
            $exponents[$i] = $e->modInverse($temp);
        }

        // from <http://tools.ietf.org/html/rfc3447#appendix-A.1.2>:
        // RSAPrivateKey ::= SEQUENCE {
        //     version           Version,
        //     modulus           INTEGER,  -- n
        //     publicExponent    INTEGER,  -- e
        //     privateExponent   INTEGER,  -- d
        //     prime1            INTEGER,  -- p
        //     prime2            INTEGER,  -- q
        //     exponent1         INTEGER,  -- d mod (p-1)
        //     exponent2         INTEGER,  -- d mod (q-1)
        //     coefficient       INTEGER,  -- (inverse of q) mod p
        //     otherPrimeInfos   OtherPrimeInfos OPTIONAL
        // }
        $privatekey = new PrivateKey();
        $privatekey->modulus = $n;
        $privatekey->k = $bits >> 3;
        $privatekey->publicExponent = $e;
        $privatekey->exponent = $d;
        /** @psalm-suppress InvalidPropertyAssignmentValue */
        $privatekey->primes = $primes;
        /** @psalm-suppress InvalidPropertyAssignmentValue */
        $privatekey->exponents = $exponents;
        /** @psalm-suppress InvalidPropertyAssignmentValue */
        $privatekey->coefficients = $coefficients;

        /*
        $publickey = new PublicKey;
        $publickey->modulus = $n;
        $publickey->k = $bits >> 3;
        $publickey->exponent = $e;
        $publickey->publicExponent = $e;
        $publickey->isPublic = true;
        */

        return $privatekey;
    }

    /**
     * OnLoad Handler
     */
    protected static function onLoad(array $components): static
    {
        $key = $components['isPublicKey'] ?
            new PublicKey() :
            new PrivateKey();

        $key->modulus = $components['modulus'];
        $key->publicExponent = $components['publicExponent'];
        $key->k = $key->modulus->getLengthInBytes();

        if ($key instanceof PublicKey || !isset($components['privateExponent'])) {
            $key->exponent = $key->publicExponent;
        } else {
            $key->privateExponent = $components['privateExponent'];
            $key->exponent = $key->privateExponent;
            $key->primes = $components['primes'];
            $key->exponents = $components['exponents'];
            $key->coefficients = $components['coefficients'];
        }

        if ($components['format'] == PSS::class) {
            // in the X509 world RSA keys are assumed to use PKCS1 padding by default. only if the key is
            // explicitly a PSS key is the use of PSS assumed. phpseclib does not work like this. phpseclib
            // uses PSS padding by default. it assumes the more secure method by default and altho it provides
            // for the less secure PKCS1 method you have to go out of your way to use it. this is consistent
            // with the latest trends in crypto. libsodium (NaCl) is actually a little more extreme in that
            // not only does it defaults to the most secure methods - it doesn't even let you choose less
            // secure methods
            //$key = $key->withPadding(self::SIGNATURE_PSS);
            if (isset($components['hash'])) {
                $key = $key->withHash($components['hash']);
            }
            if (isset($components['MGFHash'])) {
                $key = $key->withMGFHash($components['MGFHash']);
            }
            if (isset($components['saltLength'])) {
                $key = $key->withSaltLength($components['saltLength']);
            }
        }

        return $key;
    }

    /**
     * Constructor
     *
     * PublicKey and PrivateKey objects can only be created from abstract RSA class
     */
    protected function __construct()
    {
        parent::__construct();

        $this->hLen = $this->hash->getLengthInBytes();
        $this->mgfHash = new Hash('sha256');
        $this->mgfHLen = $this->mgfHash->getLengthInBytes();
    }

    /**
     * Integer-to-Octet-String primitive
     *
     * See {@link http://tools.ietf.org/html/rfc3447#section-4.1 RFC3447#section-4.1}.
     */
    protected function i2osp(BigInteger $x, int $xLen): string
    {
        $x = $x->toBytes();
        if (strlen($x) > $xLen) {
            throw new LengthException('Resultant string length out of range');
        }
        return str_pad($x, $xLen, chr(0), STR_PAD_LEFT);
    }

    /**
     * Octet-String-to-Integer primitive
     *
     * See {@link http://tools.ietf.org/html/rfc3447#section-4.2 RFC3447#section-4.2}.
     */
    protected function os2ip(string $x): BigInteger
    {
        return new BigInteger($x, 256);
    }

    /**
     * EMSA-PKCS1-V1_5-ENCODE
     *
     * See {@link http://tools.ietf.org/html/rfc3447#section-9.2 RFC3447#section-9.2}.
     */
    protected function emsa_pkcs1_v1_5_encode(string $m, int $emLen): string
    {
        $h = $this->hash->hash($m);

        // see http://tools.ietf.org/html/rfc3447#page-43
        $t = match ($this->hash->getHash()) {
            'md2' => "\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x02\x05\x00\x04\x10",
            'md5' => "\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05\x05\x00\x04\x10",
            'sha1' => "\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14",
            'sha256' => "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20",
            'sha384' => "\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x05\x00\x04\x30",
            'sha512' => "\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04\x40",
            // from https://www.emc.com/collateral/white-papers/h11300-pkcs-1v2-2-rsa-cryptography-standard-wp.pdf#page=40
            'sha224' => "\x30\x2d\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x04\x05\x00\x04\x1c",
            'sha512/224' => "\x30\x2d\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x05\x05\x00\x04\x1c",
            'sha512/256' => "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x06\x05\x00\x04\x20",
            // the following 3x algorithms are not specified in PKCS1 v2.2, however, some standards none-the-less do use them:
            // https://sk-eid.github.io/smart-id-documentation/rp-api/changes.html#_security_enhancements
            // the OIDs are from this URL:
            // https://csrc.nist.gov/projects/computer-security-objects-register/algorithm-registration#Hash
            'sha3/224' => "\x30\x2d\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x07\x05\x00\x04\x1c",
            'sha3/256' => "\x30\x2d\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x08\x05\x00\x04\x20",
            'sha3/384' => "\x30\x2d\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x09\x05\x00\x04\x30",
            'sha3/512' => "\x30\x2d\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x0A\x05\x00\x04\x40",
        };

        $t .= $h;
        $tLen = strlen($t);

        if ($emLen < $tLen + 11) {
            throw new LengthException('Intended encoded message length too short');
        }

        $ps = str_repeat(chr(0xFF), $emLen - $tLen - 3);

        $em = "\0\1$ps\0$t";

        return $em;
    }

    /**
     * EMSA-PKCS1-V1_5-ENCODE (without NULL)
     *
     * Quoting https://tools.ietf.org/html/rfc8017#page-65,
     *
     * "The parameters field associated with id-sha1, id-sha224, id-sha256,
     *  id-sha384, id-sha512, id-sha512/224, and id-sha512/256 should
     *  generally be omitted, but if present, it shall have a value of type
     *  NULL"
     */
    protected function emsa_pkcs1_v1_5_encode_without_null(string $m, int $emLen): string
    {
        $h = $this->hash->hash($m);

        $hashName = $this->hash->getHash();
        if ($hashName === 'md2' || $hashName === 'md5') {
            throw new UnsupportedAlgorithmException('md2 and md5 require NULLs');
        }

        // see http://tools.ietf.org/html/rfc3447#page-43
        $t = match ($hashName) {
            'sha1' => "\x30\x1f\x30\x07\x06\x05\x2b\x0e\x03\x02\x1a\x04\x14",
            'sha256' => "\x30\x2f\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x04\x20",
            'sha384' => "\x30\x3f\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x04\x30",
            'sha512' => "\x30\x4f\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x04\x40",
            // from https://www.emc.com/collateral/white-papers/h11300-pkcs-1v2-2-rsa-cryptography-standard-wp.pdf#page=40
            'sha224' => "\x30\x2b\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x04\x04\x1c",
            'sha512/224' => "\x30\x2b\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x05\x04\x1c",
            'sha512/256' => "\x30\x2f\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x06\x04\x20",
            // the following 3x algorithms are not specified in PKCS1 v2.2, however, some standards none-the-less do use them:
            // https://sk-eid.github.io/smart-id-documentation/rp-api/changes.html#_security_enhancements
            // the OIDs are from this URL:
            // https://csrc.nist.gov/projects/computer-security-objects-register/algorithm-registration#Hash
            'sha3/224' => "\x30\x2b\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x07\x04\x1c",
            'sha3/256' => "\x30\x2b\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x08\x04\x20",
            'sha3/384' => "\x30\x2b\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x09\x04\x30",
            'sha3/512' => "\x30\x2b\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x0A\x04\x40",
        };
        $t .= $h;
        $tLen = strlen($t);

        if ($emLen < $tLen + 11) {
            throw new LengthException('Intended encoded message length too short');
        }

        $ps = str_repeat(chr(0xFF), $emLen - $tLen - 3);

        $em = "\0\1$ps\0$t";

        return $em;
    }

    /**
     * MGF1
     *
     * See {@link http://tools.ietf.org/html/rfc3447#appendix-B.2.1 RFC3447#appendix-B.2.1}.
     */
    protected function mgf1(string $mgfSeed, int $maskLen): string
    {
        // if $maskLen would yield strings larger than 4GB, PKCS#1 suggests a "Mask too long" error be output.

        $t = '';
        $count = ceil($maskLen / $this->mgfHLen);
        for ($i = 0; $i < $count; $i++) {
            $c = pack('N', $i);
            $t .= $this->mgfHash->hash($mgfSeed . $c);
        }

        return substr($t, 0, $maskLen);
    }

    /**
     * Returns the key size
     *
     * More specifically, this returns the size of the modulo in bits.
     */
    public function getLength(): int
    {
        return !isset($this->modulus) ? 0 : $this->modulus->getLength();
    }

    /**
     * Determines which hashing function should be used
     *
     * Used with signature production / verification and (if the encryption mode is self::PADDING_OAEP) encryption and
     * decryption.
     */
    public function withHash(string $hash): static
    {
        $new = clone $this;

        // \phpseclib4\Crypt\Hash supports algorithms that PKCS#1 doesn't support. md5-96 and sha1-96, for example.
        switch (strtolower($hash)) {
            case 'md2':
            case 'md5':
            case 'sha1':
            case 'sha256':
            case 'sha384':
            case 'sha512':
            case 'sha224':
            case 'sha512/224':
            case 'sha512/256':
            case 'sha3/224':
            case 'sha3/256':
            case 'sha3/384':
            case 'sha3/512':
                $new->hash = new Hash($hash);
                break;
            default:
                throw new UnsupportedAlgorithmException(
                    "The only supported hash algorithms are: md2, md5, sha1, sha256, sha384, sha512, sha224, sha512/224, sha512/256, sha3/224, sha3/256, sha3/384, sha3/512 - $hash provided"
                );
        }
        $new->hLen = $new->hash->getLengthInBytes();

        return $new;
    }

    /**
     * Determines which hashing function should be used for the mask generation function
     *
     * The mask generation function is used by self::PADDING_OAEP and self::PADDING_PSS and although it's
     * best if Hash and MGFHash are set to the same thing this is not a requirement.
     */
    public function withMGFHash(string $hash): static
    {
        $new = clone $this;

        // \phpseclib4\Crypt\Hash supports algorithms that PKCS#1 doesn't support.  md5-96 and sha1-96, for example.
        switch (strtolower($hash)) {
            case 'md2':
            case 'md5':
            case 'sha1':
            case 'sha256':
            case 'sha384':
            case 'sha512':
            case 'sha224':
            case 'sha512/224':
            case 'sha512/256':
            case 'sha3/224':
            case 'sha3/256':
            case 'sha3/384':
            case 'sha3/512':
                $new->mgfHash = new Hash($hash);
                break;
            default:
                throw new UnsupportedAlgorithmException(
                    "The only supported hash algorithms are: md2, md5, sha1, sha256, sha384, sha512, sha224, sha512/224, sha512/256, sha3/224, sha3/256, sha3/384, sha3/512 - $hash provided"
                );
        }
        $new->mgfHLen = $new->mgfHash->getLengthInBytes();

        return $new;
    }

    /**
     * Returns the MGF hash algorithm currently being used
     */
    public function getMGFHash(): Hash
    {
        return clone $this->mgfHash;
    }

    /**
     * Determines the salt length
     *
     * Used by RSA::PADDING_PSS
     *
     * To quote from {@link http://tools.ietf.org/html/rfc3447#page-38 RFC3447#page-38}:
     *
     *    Typical salt lengths in octets are hLen (the length of the output
     *    of the hash function Hash) and 0.
     */
    public function withSaltLength(?int $sLen): static
    {
        $new = clone $this;
        $new->sLen = $sLen;
        return $new;
    }

    /**
     * Returns the salt length currently being used
     */
    public function getSaltLength(): int
    {
        return $this->sLen ?? $this->hLen;
    }

    /**
     * Determines the label
     *
     * Used by RSA::PADDING_OAEP
     *
     * To quote from {@link http://tools.ietf.org/html/rfc3447#page-17 RFC3447#page-17}:
     *
     *    Both the encryption and the decryption operations of RSAES-OAEP take
     *    the value of a label L as input.  In this version of PKCS #1, L is
     *    the empty string; other uses of the label are outside the scope of
     *    this document.
     */
    public function withLabel(string $label): static
    {
        $new = clone $this;
        $new->label = $label;
        return $new;
    }

    /**
     * Returns the label currently being used
     */
    public function getLabel(): string
    {
        return $this->label;
    }

    /**
     * Determines the padding modes
     *
     * Example: $key->withPadding(RSA::ENCRYPTION_PKCS1 | RSA::SIGNATURE_PKCS1);
     */
    public function withPadding(int $padding): static
    {
        $masks = [
            self::ENCRYPTION_OAEP,
            self::ENCRYPTION_PKCS1,
            self::ENCRYPTION_NONE,
        ];
        $encryptedCount = 0;
        $selected = 0;
        foreach ($masks as $mask) {
            if ($padding & $mask) {
                $selected = $mask;
                $encryptedCount++;
            }
        }
        if ($encryptedCount > 1) {
            throw new InvalidStateException('Multiple encryption padding modes have been selected; at most only one should be selected');
        }
        $encryptionPadding = $selected;

        $masks = [
            self::SIGNATURE_PSS,
            self::SIGNATURE_PKCS1,
        ];
        $signatureCount = 0;
        $selected = 0;
        foreach ($masks as $mask) {
            if ($padding & $mask) {
                $selected = $mask;
                $signatureCount++;
            }
        }
        if ($signatureCount > 1) {
            throw new InvalidStateException('Multiple signature padding modes have been selected; at most only one should be selected');
        }
        $signaturePadding = $selected;

        $new = clone $this;
        if ($encryptedCount) {
            $new->encryptionPadding = $encryptionPadding;
        }
        if ($signatureCount) {
            $new->signaturePadding = $signaturePadding;
        }
        return $new;
    }

    /**
     * Returns the padding currently being used
     */
    public function getPadding(): int
    {
        return $this->signaturePadding | $this->encryptionPadding;
    }

    /**
     * Enable RSA Blinding
     */
    public static function enableBlinding(): void
    {
        static::$enableBlinding = true;
    }

    /**
     * Disable RSA Blinding
     */
    public static function disableBlinding(): void
    {
        static::$enableBlinding = false;
    }

    public static function enableSaltLengthDiscovery(): void
    {
        static::$autoSaltLength = true;
    }

    public static function disableSaltLengthDiscovery(): void
    {
        static::$autoSaltLength = false;
    }

    /**
     * Handles OpenSSL encryption / decryption / signature creation / verification
     *
     * @template T of string
     * @param T $func
     * @return (T is 'openssl_verify' ? bool|null : string|null)
     */
    protected function handleOpenSSL(
        #[\SensitiveParameter] string $func,
        string $message,
        ?string $signature = null
    ): bool|null|string {
        $paddingType = match ($func) {
            'openssl_verify', 'openssl_sign' => 'signaturePadding',
            // 'openssl_public_encrypt', 'openssl_private_decrypt'
            default => 'encryptionPadding'
        };

        if (self::$forcedEngine === 'libsodium') {
            throw new BadConfigurationException('Engine libsodium is not supported for RSA');
        }

        if (self::$forcedEngine !== 'PHP') {
            if (self::$forcedEngine === 'OpenSSL' && !function_exists($func)) {
                throw new BadConfigurationException('Engine OpenSSL is forced but unavailable for RSA');
            }
            if ($this->$paddingType === self::SIGNATURE_PSS) {
                $create = $func === 'openssl_sign';
                $error = match (true) {
                    !defined('OPENSSL_PKCS1_PSS_PADDING') =>
                        'Engine OpenSSL is forced but PSS encryption requires PHP >= 8.5.0',
                    $this->hash->getHash() !== $this->mgfHash->getHash() =>
                        'Engine OpenSSL is forced but can\'t be used because the Hash and MGF Hash do not match',
                    $this->getSaltLength() !== $this->hLen =>
                        'Engine OpenSSL is forced but can\'t be used because the salt length doesn\'t match the hash length',
                    !$create && !static::$autoSaltLength =>
                        'Engine OpenSSL is forced but auto calculation of the salt length is disabled',
                    $create && $this->getLength() < 8 * (2 * $this->getSaltLength() + 2) =>
                        'Engine OpenSSL is forced but can\'t be used for PSS signing because the key is too small for OpenSSL to use the configured salt length',
                    $create && OPENSSL_VERSION_NUMBER < 0x30100000 =>
                        'Engine OpenSSL is forced but can\'t be used for PSS signing because OpenSSL < 3.1.0 defaults to the maximum salt length instead of the hash length',
                    default => null
                };
            }
            /*
            https://datatracker.ietf.org/doc/html/rfc4055#page-6 says the following:

               There are two possible encodings for the AlgorithmIdentifier
               parameters field associated with these object identifiers.  The two
               alternatives arise from the loss of the OPTIONAL associated with the
               algorithm identifier parameters when the 1988 syntax for
               AlgorithmIdentifier was translated into the 1997 syntax.  Later the
               OPTIONAL was recovered via a defect report, but by then many people
               thought that algorithm parameters were mandatory.  Because of this
               history some implementations encode parameters as a NULL element
               while others omit them entirely.  The correct encoding is to omit the
               parameters field; however, when RSASSA-PSS and RSAES-OAEP were
               defined, it was done using the NULL parameters rather than absent
               parameters.

               All implementations MUST accept both NULL and absent parameters as
               legal and equivalent encodings.

            OpenSSL does NOT accept both - it REQUIRES NULL be present. phpseclib, however,
            DOES accept both. at first, it didn't. at first, not knowing why some small number
            of PKCS1 signatures omitted NULL, i added the SIGNATURE_RELAXED_PKCS1 mode on
            2015-08-26. https://phpseclib.com/docs/rsa#rsasignature_relaxed_pkcs1 talks more
            about that mode. later, on 2021-04-05, there was CVE-2021-30130. consequently,
            the SIGNATURE_PKCS1 mode was updated to accept either NULL or non-NULL.

            because phpseclib accepts PKCS1 signatures that OpenSSL doesn't, OpenSSL isn't
            used for PKCS1. if the OpenSSL extension is installed then it'll be used to perform
            unpadded RSA (ie. modular exponentiation), however, the actual PKCS1 construction
            takes place in PHP code vs OpenSSL.

            see https://security.stackexchange.com/questions/110330/encoding-of-optional-null-in-der
            for an additional reference
            */
            if ($this->$paddingType === self::SIGNATURE_PKCS1 && $func === 'openssl_verify') {
                $error = 'Engine OpenSSL is forced but can\'t be used with PKCS1 signature verification because OpenSSL requires NULL be present whereas phpseclib doesn\'t';
            }
            if ($this->$paddingType === self::ENCRYPTION_OAEP) {
                $error = match (true) {
                    $this->hash->getHash() !== $this->mgfHash->getHash() =>
                        'Engine OpenSSL is forced but can\'t be used because the Hash and MGF Hash do not match',
                    $this->hash->getHash() !== 'sha1' && PHP_VERSION_ID < 80500 =>
                        'Engine OpenSSL is forced but non-sha1 hashes are only supported on PHP 8.5.0+',
                    strlen($this->label) =>
                        'Engine OpenSSL is forced but can\'t be used because the label is not the empty string',
                    default => null
                };
            }
            if (isset($error)) {
                if (self::$forcedEngine === 'OpenSSL') {
                    throw new BadConfigurationException($error);
                }
            } elseif ($paddingType === 'signaturePadding') {
                switch (true) {
                    case $this->signaturePadding === self::SIGNATURE_PSS && defined('OPENSSL_PKCS1_PSS_PADDING'):
                    case $this->signaturePadding !== self::SIGNATURE_PSS && function_exists($func):
                        $key = $this instanceof PrivateKey ?
                            $this->withPassword()->toString('PKCS8') :
                            $this->toString('PKCS8');
                        if ($func === 'openssl_sign' && str_contains($key, 'PUBLIC')) {
                            if (self::$forcedEngine === 'OpenSSL') {
                                throw new BadConfigurationException('Engine OpenSSL is forced but cannot be used because the private key does not have the prime components within it');
                            }
                            break;
                        }
                        $hash = $this->hash->getHash();

                        $result = $this->signaturePadding === self::SIGNATURE_PSS ?
                            $func($message, $signature, $key, $hash, OPENSSL_PKCS1_PSS_PADDING) :
                            $func($message, $signature, $key, $hash);

                        if ($func === 'openssl_verify') {
                            if ($result === -1 || $result === false) {
                                throw new BadConfigurationException('Engine OpenSSL is forced but was unable to verify signature because of ' . openssl_error_string());
                            }
                            return (bool) $result;
                        }
                        if ($result) {
                            return $signature;
                        }
                        if (self::$forcedEngine === 'OpenSSL') {
                            throw new BadConfigurationException('Engine OpenSSL is forced but was unable to create signature because of ' . openssl_error_string());
                        }
                }
            } else {
                if ($this->encryptionPadding !== self::ENCRYPTION_OAEP || PHP_VERSION_ID >= 80500) {
                    $key = $this instanceof PrivateKey ?
                        $this->withPassword()->toString('PKCS8') :
                        $this->toString('PKCS8');
                    if ($func === 'openssl_private_decrypt' && str_contains($key, 'PUBLIC')) {
                        if ($this->encryptionPadding === self::ENCRYPTION_OAEP) {
                            if (self::$forcedEngine === 'OpenSSL') {
                                throw new BadConfigurationException('Engine OpenSSL is forced but cannot be used because openssl_public_decrypt() doesn\'t have a hash parameter like openssl_private_decrypt() does');
                            }
                            return null;
                        }
                        $func = 'openssl_public_decrypt';
                    }
                    if ($this->encryptionPadding === self::ENCRYPTION_PKCS1 && OPENSSL_VERSION_NUMBER >= 0x30200000) {
                        // quoting https://docs.openssl.org/3.4/man3/RSA_public_encrypt/#return-values :
                        //
                        // "Since version 3.2.0, the default provider in OpenSSL does not return an error when padding checks fail.
                        //  Instead it generates a random message"
                        //
                        // the idea is that even a perfect implementation of PKCS1 padding can be used to conduct a Bleichenbacher
                        // padding oracle attack.
                        //
                        // so like if $rsa->decrypt() doesn't throw an exception it's liable to run additional code that'll take
                        // longer to run than it'd take if an exception would be thrown and in theory, with PKCS1, in particular,
                        // you can use that fact to guess at successive bits of the private key until you've figured it out. it's
                        // why you should use OAEP padding vs PKCS1 padding BUT if you need PKCS1 padding for interoperability then
                        // you're stuck with it.
                        //
                        // with the OpenSSL 3.2.0+ behavior they're making it harder to do the attack by making it harder to use PKCS1
                        // in the real world. this isn't a design philosophy i agree with. like phpseclib lets you DES encryption. you
                        // shouldn't use DES encryption but if you need to you need to and phpseclib isn't here to judge. that's a big
                        // difference between phpseclib and stuff like libsodium.
                        if (self::$forcedEngine === 'OpenSSL') {
                            throw new BadConfigurationException('Engine OpenSSL is forced but cannot be used to decrypt PKCS1 encrypted strings with OpenSSL 3.2.0+');
                        }
                        return null;
                    }
                    $hash = $this->hash->getHash();
                    $output = '';
                    switch ($this->encryptionPadding) {
                        case self::ENCRYPTION_NONE:
                        case self::ENCRYPTION_PKCS1:
                            $padding = $this->encryptionPadding === self::ENCRYPTION_NONE ? OPENSSL_NO_PADDING : OPENSSL_PKCS1_PADDING;
                            $result = $func($message, $output, $key, $padding);
                            break;
                        //case self::ENCRYPTION_OAEP:
                        default:
                            $result = $func($message, $output, $key, OPENSSL_PKCS1_OAEP_PADDING, $hash);
                    }
                    if ($result) {
                        return $output;
                    }
                }
            }
            return null;
        }

        return null;
    }

    /**
     * Returns the public or private key as a string
     */
    public function toString(string $type, array $options = []): string
    {
        $type = self::validatePlugin(
            'Keys',
            $type,
            empty($this->primes) ? 'savePublicKey' : 'savePrivateKey'
        );

        if ($type == PSS::class) {
            if ($this->signaturePadding == self::SIGNATURE_PSS) {
                $options += [
                    'hash' => $this->hash->getHash(),
                    'MGFHash' => $this->mgfHash->getHash(),
                    'saltLength' => $this->getSaltLength(),
                ];
            } else {
                throw new InvalidArgumentException('The PSS format can only be used when the signature method has been explicitly set to PSS');
            }
        }

        if ($this instanceof PrivateKey && count($this->primes ?? [])) {
            return $type::savePrivateKey($this->modulus, $this->publicExponent, $this->exponent, $this->primes, $this->exponents, $this->coefficients, $this->password, $options);
        }

        return $type::savePublicKey($this->modulus, $this->exponent, $options);

        /*
        $key = $type::savePrivateKey($this->modulus, $th is->publicExponent, $this->exponent, $this->primes, $this->exponents, $this->coefficients, $this->password, $options);
        if ($key !== false || count($this->primes) == 2) {
            return $key;
        }

        $nSize = $this->getSize() >> 1;

        $primes = [1 => clone self::$one, clone self::$one];
        $i = 1;
        foreach ($this->primes as $prime) {
            $primes[$i] = $primes[$i]->multiply($prime);
            if ($primes[$i]->getLength() >= $nSize) {
                $i++;
            }
        }

        $exponents = [];
        $coefficients = [2 => $primes[2]->modInverse($primes[1])];

        foreach ($primes as $i => $prime) {
            $temp = $prime->subtract(self::$one);
            $exponents[$i] = $this->modulus->modInverse($temp);
        }

        return $type::savePrivateKey($this->modulus, $this->publicExponent, $this->exponent, $primes, $exponents, $coefficients, $this->password, $options);
        */
    }

    public function toArray(): array
    {
        if ($this instanceof PrivateKey && count($this->primes ?? [])) {
            return [
                'e' => clone $this->publicExponent,
                'n' => clone $this->modulus,
                'd' => clone $this->exponent,
                'primes' => array_map(fn (BigInteger $var): BigInteger => clone $var, $this->primes),
                'exponents' => array_map(fn (BigInteger $var): BigInteger => clone $var, $this->exponents),
                'coefficients' => array_map(fn (BigInteger $var): BigInteger => clone $var, $this->coefficients),
            ];
        }
        return [
            'e' => clone $this->publicExponent,
            'n' => clone $this->modulus,
        ];
    }
}
<?php

/**
 * PKCS#8 Formatted RSA-PSS Key Handler
 *
 * PHP version 8.1+
 *
 * Used by PHP's openssl_public_encrypt() and openssl's rsautl (when -pubin is set)
 *
 * Processes keys with the following headers:
 *
 * -----BEGIN ENCRYPTED PRIVATE KEY-----
 * -----BEGIN PRIVATE KEY-----
 * -----BEGIN PUBLIC KEY-----
 *
 * Analogous to "openssl genpkey -algorithm rsa-pss".
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2019-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\RSA\Formats\Keys;

use phpseclib4\Crypt\Common\Formats\Keys\PKCS8 as Progenitor;
use phpseclib4\File\ASN1;
use phpseclib4\File\ASN1\{Element, Maps};
use phpseclib4\Math\BigInteger;

/**
 * PKCS#8 Formatted RSA-PSS Key Handler
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class PSS extends Progenitor
{
    /**
     * OID Name
     *
     * @var string
     */
    public const OID_NAME = 'id-RSASSA-PSS';

    /**
     * OID Value
     *
     * @var string
     */
    public const OID_VALUE = '1.2.840.113549.1.1.10';

    /**
     * OIDs loaded
     */
    private static bool $oidsLoaded = false;

    /**
     * Initialize static variables
     */
    private static function initialize_static_variables(): void
    {
        if (!self::$oidsLoaded) {
            ASN1::loadOIDs('Hashes');
            self::$oidsLoaded = true;
        }
    }

    /**
     * Break a public or private key down into its constituent components
     */
    public static function load(
        #[\SensitiveParameter] string $key,
        #[\SensitiveParameter] ?string $password = null
    ): array {
        self::initialize_static_variables();

        $components = match (true) {
            str_contains($key, 'PUBLIC') => ['isPublicKey' => true],
            str_contains($key, 'PRIVATE') => ['isPublicKey' => false],
            default => []
        };

        $key = parent::load($key, $password);

        if (isset($key['privateKey'])) {
            if (!isset($components['isPublicKey'])) {
                $components['isPublicKey'] = false;
            }
            $type = 'private';
        } else {
            if (!isset($components['isPublicKey'])) {
                $components['isPublicKey'] = true;
            }
            $type = 'public';
        }

        $result = $components + PKCS1::load((string) $key[$type . 'Key']);

        if (isset($key[$type . 'KeyAlgorithm']['parameters'])) {
            $decoded = ASN1::decodeBER((string) $key[$type . 'KeyAlgorithm']['parameters']);
            $params = ASN1::map($decoded, Maps\RSASSA_PSS_params::MAP);
        } else {
            /** @var array<string, mixed> $params */
            $params = [];
        }

        if (isset($params['maskGenAlgorithm']['parameters'])) {
            $decoded = ASN1::decodeBER((string) $params['maskGenAlgorithm']['parameters']);
            $params['maskGenAlgorithm']['parameters'] = ASN1::map($decoded, Maps\HashAlgorithm::MAP);
        } else {
            $params['maskGenAlgorithm'] = [
                'algorithm' => 'id-mgf1',
                'parameters' => ['algorithm' => 'id-sha1'],
            ];
        }

        if (!isset($params['hashAlgorithm']['algorithm'])) {
            $params['hashAlgorithm']['algorithm'] = 'id-sha1';
        }

        $result['hash'] = str_replace('id-', '', (string) $params['hashAlgorithm']['algorithm']);
        $result['MGFHash'] = str_replace('id-', '', (string) $params['maskGenAlgorithm']['parameters']['algorithm']);
        if (isset($params['saltLength'])) {
            $result['saltLength'] = (int) "$params[saltLength]";
        }

        if (isset($key['meta'])) {
            $result['meta'] = $key['meta'];
        }

        return $result;
    }

    /**
     * Convert a private key to the appropriate format.
     */
    public static function savePrivateKey(
        BigInteger $n,
        BigInteger $e,
        #[\SensitiveParameter] BigInteger $d,
        #[\SensitiveParameter] array $primes,
        #[\SensitiveParameter] array $exponents,
        #[\SensitiveParameter] array $coefficients,
        #[\SensitiveParameter] ?string $password = null,
        array $options = []
    ): string {
        self::initialize_static_variables();

        $key = PKCS1::savePrivateKey($n, $e, $d, $primes, $exponents, $coefficients);
        $key = ASN1::extractBER($key);
        $params = self::savePSSParams($options);
        return self::wrapPrivateKey(
            key: $key,
            params: $params,
            password: $password,
            options: $options
        );
    }

    /**
     * Convert a public key to the appropriate format
     */
    public static function savePublicKey(BigInteger $n, BigInteger $e, array $options = []): string
    {
        self::initialize_static_variables();

        $key = PKCS1::savePublicKey($n, $e);
        $key = ASN1::extractBER($key);
        $params = self::savePSSParams($options);
        return self::wrapPublicKey(
            key: $key,
            params: $params,
            options: $options
        );
    }

    /**
     * Encodes PSS parameters
     */
    public static function savePSSParams(array $options): Element
    {
        /*
         The trailerField field is an integer.  It provides
         compatibility with IEEE Std 1363a-2004 [P1363A].  The value
         MUST be 1, which represents the trailer field with hexadecimal
         value 0xBC.  Other trailer fields, including the trailer field
         composed of HashID concatenated with 0xCC that is specified in
         IEEE Std 1363a, are not supported.  Implementations that
         perform signature generation MUST omit the trailerField field,
         indicating that the default trailer field value was used.
         Implementations that perform signature validation MUST
         recognize both a present trailerField field with value 1 and an
         absent trailerField field.

         source: https://tools.ietf.org/html/rfc4055#page-9
        */
        $params = [
            'trailerField' => new BigInteger(1),
        ];
        if (isset($options['hash'])) {
            $params['hashAlgorithm']['algorithm'] = 'id-' . $options['hash'];
        }
        if (isset($options['MGFHash'])) {
            $temp = ['algorithm' => 'id-' . $options['MGFHash']];
            $temp = ASN1::encodeDER($temp, Maps\HashAlgorithm::MAP);
            $params['maskGenAlgorithm'] = [
                'algorithm' => 'id-mgf1',
                'parameters' => new ASN1\Element($temp),
            ];
        }
        if (isset($options['saltLength'])) {
            $params['saltLength'] = new BigInteger($options['saltLength']);
        }

        return new ASN1\Element(ASN1::encodeDER($params, Maps\RSASSA_PSS_params::MAP));
    }
}
<?php

/**
 * Microsoft BLOB Formatted RSA Key Handler
 *
 * More info:
 *
 * https://msdn.microsoft.com/en-us/library/windows/desktop/aa375601(v=vs.85).aspx
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\RSA\Formats\Keys;

use phpseclib4\Common\Functions\Strings;
use phpseclib4\Exception\{InvalidArgumentException, UnexpectedValueException};
use phpseclib4\Math\BigInteger;

/**
 * Microsoft BLOB Formatted RSA Key Handler
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class MSBLOB
{
    /**
     * Public/Private Key Pair
     */
    private const PRIVATEKEYBLOB = 0x7;
    /**
     * Public Key
     */
    private const PUBLICKEYBLOB = 0x6;
    /**
     * Public Key
     */
    private const PUBLICKEYBLOBEX = 0xA;
    /**
     * RSA public key exchange algorithm
     */
    private const CALG_RSA_KEYX = 0x0000A400;
    /**
     * RSA public key exchange algorithm
     */
    private const CALG_RSA_SIGN = 0x00002400;
    /**
     * Public Key
     */
    private const RSA1 = 0x31415352;
    /**
     * Private Key
     */
    private const RSA2 = 0x32415352;

    /**
     * Break a public or private key down into its constituent components
     *
     * @psalm-suppress PossiblyUnusedParam
     */
    public static function load(
        #[\SensitiveParameter] string $key,
        #[\SensitiveParameter] ?string $password = null
    ): array {
        $key = Strings::base64_decode($key);
        if (strlen($key) < 20) {
            throw new UnexpectedValueException('Key appears to be malformed');
        }

        // PUBLICKEYSTRUC  publickeystruc
        // https://msdn.microsoft.com/en-us/library/windows/desktop/aa387453(v=vs.85).aspx
        [
            'type' => $type,
            //'version' => $version,
            //'reserved' => $reserved,
            'algo' => $algo
        ] = unpack('atype/aversion/vreserved/Valgo', Strings::shift($key, 8));
        $publickey = match (ord($type)) {
            self::PUBLICKEYBLOB, self::PUBLICKEYBLOBEX => true,
            self::PRIVATEKEYBLOB => false,
            default => throw new UnexpectedValueException('Key appears to be malformed')
        };

        $components = ['isPublicKey' => $publickey];

        // https://msdn.microsoft.com/en-us/library/windows/desktop/aa375549(v=vs.85).aspx
        switch ($algo) {
            case self::CALG_RSA_KEYX:
            case self::CALG_RSA_SIGN:
                break;
            default:
                throw new UnexpectedValueException('Key appears to be malformed');
        }

        // RSAPUBKEY rsapubkey
        // https://msdn.microsoft.com/en-us/library/windows/desktop/aa387685(v=vs.85).aspx
        // could do V for pubexp but that's unsigned 32-bit whereas some PHP installs only do signed 32-bit
        [
            'magic' => $magic,
            'bitlen' => $bitlen,
            'pubexp' => $pubexp
        ] = unpack('Vmagic/Vbitlen/a4pubexp', Strings::shift($key, 12));
        switch ($magic) {
            case self::RSA2:
                $components['isPublicKey'] = false;
                // no break
            case self::RSA1:
                break;
            default:
                throw new UnexpectedValueException('Key appears to be malformed');
        }

        $baseLength = $bitlen / 16;
        if (strlen($key) != 2 * $baseLength && strlen($key) != 9 * $baseLength) {
            throw new UnexpectedValueException('Key appears to be malformed');
        }

        $components[$components['isPublicKey'] ? 'publicExponent' : 'privateExponent'] = new BigInteger(strrev($pubexp), 256);
        // BYTE modulus[rsapubkey.bitlen/8]
        $components['modulus'] = new BigInteger(strrev(Strings::shift($key, $bitlen / 8)), 256);

        if ($publickey) {
            return $components;
        }

        $components['isPublicKey'] = false;

        // BYTE prime1[rsapubkey.bitlen/16]
        $components['primes'] = [1 => new BigInteger(strrev(Strings::shift($key, $bitlen / 16)), 256)];
        // BYTE prime2[rsapubkey.bitlen/16]
        $components['primes'][] = new BigInteger(strrev(Strings::shift($key, $bitlen / 16)), 256);
        // BYTE exponent1[rsapubkey.bitlen/16]
        $components['exponents'] = [1 => new BigInteger(strrev(Strings::shift($key, $bitlen / 16)), 256)];
        // BYTE exponent2[rsapubkey.bitlen/16]
        $components['exponents'][] = new BigInteger(strrev(Strings::shift($key, $bitlen / 16)), 256);
        // BYTE coefficient[rsapubkey.bitlen/16]
        $components['coefficients'] = [2 => new BigInteger(strrev(Strings::shift($key, $bitlen / 16)), 256)];
        if (isset($components['privateExponent'])) {
            $components['publicExponent'] = $components['privateExponent'];
        }
        // BYTE privateExponent[rsapubkey.bitlen/8]
        $components['privateExponent'] = new BigInteger(strrev(Strings::shift($key, $bitlen / 8)), 256);

        return $components;
    }

    /**
     * Convert a private key to the appropriate format.
     *
     * @psalm-suppress PossiblyUnusedParam
     */
    public static function savePrivateKey(
        BigInteger $n,
        BigInteger $e,
        #[\SensitiveParameter] BigInteger $d,
        #[\SensitiveParameter] array $primes,
        #[\SensitiveParameter] array $exponents,
        #[\SensitiveParameter] array $coefficients,
        #[\SensitiveParameter] ?string $password = null,
        array $options = []
    ): string {
        if (count($primes) != 2) {
            throw new InvalidArgumentException('MSBLOB does not support multi-prime RSA keys');
        }

        if (isset($password)) {
            throw new InvalidArgumentException('MSBLOB private keys do not support encryption');
        }

        $n = strrev($n->toBytes());
        $e = str_pad(strrev($e->toBytes()), 4, "\0");
        $key = pack('aavV', chr(self::PRIVATEKEYBLOB), chr(2), 0, self::CALG_RSA_KEYX);
        $key .= pack('VVa*', self::RSA2, 8 * strlen($n), $e);
        $key .= $n;
        $key .= strrev($primes[1]->toBytes());
        $key .= strrev($primes[2]->toBytes());
        $key .= strrev($exponents[1]->toBytes());
        $key .= strrev($exponents[2]->toBytes());
        $key .= strrev($coefficients[2]->toBytes());
        $key .= strrev($d->toBytes());

        return Strings::base64_encode($key);
    }

    /**
     * Convert a public key to the appropriate format
     *
     * @psalm-suppress PossiblyUnusedParam
     */
    public static function savePublicKey(BigInteger $n, BigInteger $e, array $options = []): string
    {
        $n = strrev($n->toBytes());
        $e = str_pad(strrev($e->toBytes()), 4, "\0");
        $key = pack('aavV', chr(self::PUBLICKEYBLOB), chr(2), 0, self::CALG_RSA_KEYX);
        $key .= pack('VVa*', self::RSA1, 8 * strlen($n), $e);
        $key .= $n;

        return Strings::base64_encode($key);
    }
}
<?php

/**
 * PKCS#8 Formatted RSA Key Handler
 *
 * PHP version 8.1+
 *
 * Used by PHP's openssl_public_encrypt() and openssl's rsautl (when -pubin is set)
 *
 * Processes keys with the following headers:
 *
 * -----BEGIN ENCRYPTED PRIVATE KEY-----
 * -----BEGIN PRIVATE KEY-----
 * -----BEGIN PUBLIC KEY-----
 *
 * Analogous to ssh-keygen's pkcs8 format (as specified by -m). Although PKCS8
 * is specific to private keys it's basically creating a DER-encoded wrapper
 * for keys. This just extends that same concept to public keys (much like ssh-keygen)
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\RSA\Formats\Keys;

use phpseclib4\Crypt\Common\Formats\Keys\PKCS8 as Progenitor;
use phpseclib4\File\ASN1;
use phpseclib4\Math\BigInteger;

/**
 * PKCS#8 Formatted RSA Key Handler
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class PKCS8 extends Progenitor
{
    /**
     * OID Name
     *
     * @var string
     */
    public const OID_NAME = 'rsaEncryption';

    /**
     * OID Value
     *
     * @var string
     */
    public const OID_VALUE = '1.2.840.113549.1.1.1';

    /**
     * Break a public or private key down into its constituent components
     */
    public static function load(
        #[\SensitiveParameter] string $key,
        #[\SensitiveParameter] ?string $password = null
    ): array {
        $components = match (true) {
            str_contains($key, 'PUBLIC') => ['isPublicKey' => true],
            str_contains($key, 'PRIVATE') => ['isPrivateKey' => false],
            default => null
        };

        $key = parent::load($key, $password);

        if (isset($key['privateKey'])) {
            $components['isPublicKey'] ??= false;
            $type = 'private';
        } else {
            $components['isPublicKey'] ??= true;
            $type = 'public';
        }

        $result = $components + PKCS1::load((string) $key[$type . 'Key']);

        if (isset($key['meta'])) {
            $result['meta'] = $key['meta'];
        }

        return $result;
    }

    /**
     * Convert a private key to the appropriate format.
     */
    public static function savePrivateKey(
        BigInteger $n,
        BigInteger $e,
        #[\SensitiveParameter] BigInteger $d,
        #[\SensitiveParameter] array $primes,
        #[\SensitiveParameter] array $exponents,
        #[\SensitiveParameter] array $coefficients,
        #[\SensitiveParameter] ?string $password = null,
        array $options = []
    ): string {
        $key = PKCS1::savePrivateKey($n, $e, $d, $primes, $exponents, $coefficients);
        $key = ASN1::extractBER($key);
        return self::wrapPrivateKey(
            key: $key,
            password: $password,
            options: $options
        );
    }

    /**
     * Convert a public key to the appropriate format
     */
    public static function savePublicKey(BigInteger $n, BigInteger $e, array $options = []): string
    {
        $key = PKCS1::savePublicKey($n, $e);
        $key = ASN1::extractBER($key);
        return self::wrapPublicKey(
            key: $key,
            options: $options
        );
    }
}
<?php

/**
 * Raw RSA Key Handler
 *
 * PHP version 8.1+
 *
 * An array containing two \phpseclib4\Math\BigInteger objects.
 *
 * The exponent can be indexed with any of the following:
 *
 * 0, e, exponent, publicExponent
 *
 * The modulus can be indexed with any of the following:
 *
 * 1, n, modulo, modulus
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\RSA\Formats\Keys;

use phpseclib4\Exception\UnexpectedValueException;
use phpseclib4\Math\BigInteger;

/**
 * Raw RSA Key Handler
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class Raw
{
    /**
     * Break a public or private key down into its constituent components
     *
     * @psalm-suppress PossiblyUnusedParam
     */
    public static function load(
        #[\SensitiveParameter] array $key,
        #[\SensitiveParameter] ?string $password = null
    ): array {
        $key = array_change_key_case($key, CASE_LOWER);

        $components = ['isPublicKey' => false];

        foreach (['e', 'exponent', 'publicexponent', 0, 'privateexponent', 'd'] as $index) {
            if (isset($key[$index])) {
                $components['publicExponent'] = $key[$index];
                break;
            }
        }

        foreach (['n', 'modulo', 'modulus', 1] as $index) {
            if (isset($key[$index])) {
                $components['modulus'] = $key[$index];
                break;
            }
        }

        if (!isset($components['publicExponent']) || !isset($components['modulus'])) {
            throw new UnexpectedValueException('Modulus / exponent not present');
        }

        if (isset($key['primes'])) {
            $components['primes'] = $key['primes'];
        } elseif (isset($key['p']) && isset($key['q'])) {
            $indices = [
                ['p', 'q'],
                ['prime1', 'prime2'],
            ];
            foreach ($indices as $index) {
                [$i0, $i1] = $index;
                if (isset($key[$i0]) && isset($key[$i1])) {
                    $components['primes'] = [1 => $key[$i0], $key[$i1]];
                }
            }
        }

        if (isset($key['exponents'])) {
            $components['exponents'] = $key['exponents'];
        } else {
            $indices = [
                ['dp', 'dq'],
                ['exponent1', 'exponent2'],
            ];
            foreach ($indices as $index) {
                [$i0, $i1] = $index;
                if (isset($key[$i0]) && isset($key[$i1])) {
                    $components['exponents'] = [1 => $key[$i0], $key[$i1]];
                }
            }
        }

        if (isset($key['coefficients'])) {
            $components['coefficients'] = $key['coefficients'];
        } else {
            foreach (['inverseq', 'q\'', 'coefficient'] as $index) {
                if (isset($key[$index])) {
                    $components['coefficients'] = [2 => $key[$index]];
                }
            }
        }

        if (!isset($components['primes'])) {
            $components['isPublicKey'] = true;
            return $components;
        }

        if (!isset($components['exponents'])) {
            $one = new BigInteger(1);
            $temp = $components['primes'][1]->subtract($one);
            $exponents = [1 => $components['publicExponent']->modInverse($temp)];
            $temp = $components['primes'][2]->subtract($one);
            $exponents[] = $components['publicExponent']->modInverse($temp);
            $components['exponents'] = $exponents;
        }

        if (!isset($components['coefficients'])) {
            $components['coefficients'] = [2 => $components['primes'][2]->modInverse($components['primes'][1])];
        }

        foreach (['privateexponent', 'd'] as $index) {
            if (isset($key[$index])) {
                $components['privateExponent'] = $key[$index];
                break;
            }
        }

        return $components;
    }
}
<?php

/**
 * OpenSSH Formatted RSA Key Handler
 *
 * PHP version 8.1+
 *
 * Place in $HOME/.ssh/authorized_keys
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\RSA\Formats\Keys;

use phpseclib4\Common\Functions\Strings;
use phpseclib4\Crypt\Common\Formats\Keys\OpenSSH as Progenitor;
use phpseclib4\Exception\UnexpectedValueException;
use phpseclib4\Math\BigInteger;

/**
 * OpenSSH Formatted RSA Key Handler
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class OpenSSH extends Progenitor
{
    /**
     * Supported Key Types
     */
    protected static array $types = ['ssh-rsa'];

    /**
     * Break a public or private key down into its constituent components
     */
    public static function load(
        #[\SensitiveParameter] string $key,
        #[\SensitiveParameter] ?string $password = null
    ): array {
        static $one;
        if (!isset($one)) {
            $one = new BigInteger(1);
        }

        $parsed = parent::load($key, $password);

        if (isset($parsed['paddedKey'])) {
            [$type] = Strings::unpackSSH2('s', $parsed['paddedKey']);
            if ($type != $parsed['type']) {
                throw new UnexpectedValueException("The public and private keys are not of the same type ($type vs $parsed[type])");
            }

            $primes = $coefficients = [];

            [
                $modulus,
                $publicExponent,
                $privateExponent,
                $coefficients[2],
                $primes[1],
                $primes[2],
                $comment,
            ] = Strings::unpackSSH2('i6s', $parsed['paddedKey']);

            $temp = $primes[1]->subtract($one);
            $exponents = [1 => $publicExponent->modInverse($temp)];
            $temp = $primes[2]->subtract($one);
            $exponents[] = $publicExponent->modInverse($temp);

            $isPublicKey = false;

            return compact('publicExponent', 'modulus', 'privateExponent', 'primes', 'coefficients', 'exponents', 'comment', 'isPublicKey');
        }

        [$publicExponent, $modulus] = Strings::unpackSSH2('ii', $parsed['publicKey']);

        return [
            'isPublicKey' => true,
            'modulus' => $modulus,
            'publicExponent' => $publicExponent,
            'comment' => $parsed['comment'],
        ];
    }

    /**
     * Convert a public key to the appropriate format
     */
    public static function savePublicKey(BigInteger $n, BigInteger $e, array $options = []): string
    {
        $RSAPublicKey = Strings::packSSH2('sii', 'ssh-rsa', $e, $n);

        if ($options['binary'] ?? self::$binary) {
            return $RSAPublicKey;
        }

        $comment = $options['comment'] ?? self::$comment;
        $RSAPublicKey = 'ssh-rsa ' . base64_encode($RSAPublicKey) . ' ' . $comment;

        return $RSAPublicKey;
    }

    /**
     * Convert a private key to the appropriate format.
     *
     * @psalm-suppress PossiblyUnusedParam
     */
    public static function savePrivateKey(
        BigInteger $n,
        BigInteger $e,
        #[\SensitiveParameter] BigInteger $d,
        #[\SensitiveParameter] array $primes,
        #[\SensitiveParameter] array $exponents,
        #[\SensitiveParameter] array $coefficients,
        #[\SensitiveParameter] ?string $password = null,
        array $options = []
    ): string {
        $publicKey = self::savePublicKey($n, $e, ['binary' => true]);
        $privateKey = Strings::packSSH2('si6', 'ssh-rsa', $n, $e, $d, $coefficients[2], $primes[1], $primes[2]);

        return self::wrapPrivateKey($publicKey, $privateKey, $password, $options);
    }
}
<?php

/**
 * XML Formatted RSA Key Handler
 *
 * More info:
 *
 * http://www.w3.org/TR/xmldsig-core/#sec-RSAKeyValue
 * http://www.w3.org/TR/xkms2/#XKMS_2_0_Paragraph_269
 * http://en.wikipedia.org/wiki/XML_Signature
 * http://en.wikipedia.org/wiki/XKMS
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\RSA\Formats\Keys;

use phpseclib4\Common\Functions\Strings;
use phpseclib4\Exception\{BadConfigurationException, InvalidArgumentException, UnexpectedValueException};
use phpseclib4\Math\BigInteger;

/**
 * XML Formatted RSA Key Handler
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class XML
{
    /**
     * Break a public or private key down into its constituent components
     *
     * @psalm-suppress PossiblyUnusedParam
     */
    public static function load(
        #[\SensitiveParameter] string $key,
        #[\SensitiveParameter] ?string $password = null
    ): array {
        if (!class_exists('DOMDocument')) {
            throw new BadConfigurationException('The dom extension is not setup correctly on this system');
        }

        $components = [
            'isPublicKey' => false,
            'primes' => [],
            'exponents' => [],
            'coefficients' => [],
        ];

        $use_errors = libxml_use_internal_errors(true);

        $dom = new \DOMDocument();
        if (substr($key, 0, 5) != '<?xml') {
            $key = '<xml>' . $key . '</xml>';
        }
        if (!$dom->loadXML($key)) {
            $e = libxml_get_last_error();
            $message = 'Error loading XML - ' . $e->message;
            libxml_use_internal_errors($use_errors);
            throw new UnexpectedValueException($message, $e->code);
        }
        $xpath = new \DOMXPath($dom);
        $keys = ['modulus', 'exponent', 'p', 'q', 'dp', 'dq', 'inverseq', 'd'];
        foreach ($keys as $key) {
            // $dom->getElementsByTagName($key) is case-sensitive
            $temp = $xpath->query("//*[translate(local-name(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')='$key']");
            if (!$temp->length) {
                continue;
            }
            $value = new BigInteger(Strings::base64_decode($temp->item(0)->nodeValue), 256);
            switch ($key) {
                case 'modulus':
                    $components['modulus'] = $value;
                    break;
                case 'exponent':
                    $components['publicExponent'] = $value;
                    break;
                case 'p':
                    $components['primes'][1] = $value;
                    break;
                case 'q':
                    $components['primes'][2] = $value;
                    break;
                case 'dp':
                    $components['exponents'][1] = $value;
                    break;
                case 'dq':
                    $components['exponents'][2] = $value;
                    break;
                case 'inverseq':
                    $components['coefficients'][2] = $value;
                    break;
                case 'd':
                    $components['privateExponent'] = $value;
            }
        }

        libxml_use_internal_errors($use_errors);

        foreach ($components as $key => $value) {
            if (is_array($value) && !count($value)) {
                unset($components[$key]);
            }
        }

        if (isset($components['modulus']) && isset($components['publicExponent'])) {
            if (count($components) == 3) {
                $components['isPublicKey'] = true;
            }
            return $components;
        }

        throw new UnexpectedValueException('Modulus / exponent not present');
    }

    /**
     * Convert a private key to the appropriate format.
     *
     * @psalm-suppress PossiblyUnusedParam
     */
    public static function savePrivateKey(
        BigInteger $n,
        BigInteger $e,
        #[\SensitiveParameter] BigInteger $d,
        #[\SensitiveParameter] array $primes,
        #[\SensitiveParameter] array $exponents,
        #[\SensitiveParameter] array $coefficients,
        #[\SensitiveParameter] ?string $password = null,
        array $options = []
    ): string {
        if (count($primes) != 2) {
            throw new InvalidArgumentException('XML does not support multi-prime RSA keys');
        }

        if (isset($password)) {
            throw new InvalidArgumentException('XML private keys do not support encryption');
        }

        return "<RSAKeyPair>\r\n" .
               '  <Modulus>' . Strings::base64_encode($n->toBytes()) . "</Modulus>\r\n" .
               '  <Exponent>' . Strings::base64_encode($e->toBytes()) . "</Exponent>\r\n" .
               '  <P>' . Strings::base64_encode($primes[1]->toBytes()) . "</P>\r\n" .
               '  <Q>' . Strings::base64_encode($primes[2]->toBytes()) . "</Q>\r\n" .
               '  <DP>' . Strings::base64_encode($exponents[1]->toBytes()) . "</DP>\r\n" .
               '  <DQ>' . Strings::base64_encode($exponents[2]->toBytes()) . "</DQ>\r\n" .
               '  <InverseQ>' . Strings::base64_encode($coefficients[2]->toBytes()) . "</InverseQ>\r\n" .
               '  <D>' . Strings::base64_encode($d->toBytes()) . "</D>\r\n" .
               '</RSAKeyPair>';
    }

    /**
     * Convert a public key to the appropriate format
     *
     * @psalm-suppress PossiblyUnusedParam
     */
    public static function savePublicKey(BigInteger $n, BigInteger $e, array $options = []): string
    {
        return "<RSAKeyValue>\r\n" .
               '  <Modulus>' . Strings::base64_encode($n->toBytes()) . "</Modulus>\r\n" .
               '  <Exponent>' . Strings::base64_encode($e->toBytes()) . "</Exponent>\r\n" .
               '</RSAKeyValue>';
    }
}
<?php

/**
 * PKCS#1 Formatted RSA Key Handler
 *
 * PHP version 8.1+
 *
 * Used by File/X509.php
 *
 * Processes keys with the following headers:
 *
 * -----BEGIN RSA PRIVATE KEY-----
 * -----BEGIN RSA PUBLIC KEY-----
 *
 * Analogous to ssh-keygen's pem format (as specified by -m)
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\RSA\Formats\Keys;

use phpseclib4\Crypt\Common\Formats\Keys\PKCS1 as Progenitor;
use phpseclib4\File\ASN1;
use phpseclib4\File\ASN1\Maps;
use phpseclib4\Math\BigInteger;

/**
 * PKCS#1 Formatted RSA Key Handler
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class PKCS1 extends Progenitor
{
    /**
     * Break a public or private key down into its constituent components
     */
    public static function load(
        #[\SensitiveParameter] string $key,
        #[\SensitiveParameter] ?string $password = null
    ): array {
        $components = match (true) {
            str_contains($key, 'PUBLIC') => ['isPublicKey' => true],
            str_contains($key, 'PRIVATE') => ['isPrivateKey' => false],
            default => []
        };

        $key = parent::loadHelper($key, $password);

        $decoded = ASN1::decodeBER($key);

        try {
            $key = ASN1::map($decoded, Maps\RSAPrivateKey::MAP)->toArray();
        } catch (\Exception) {
            $key = null;
        }
        if (is_array($key)) {
            $components += [
                'modulus' => $key['modulus'],
                'publicExponent' => $key['publicExponent'],
                'privateExponent' => $key['privateExponent'],
                'primes' => [1 => $key['prime1'], $key['prime2']],
                'exponents' => [1 => $key['exponent1'], $key['exponent2']],
                'coefficients' => [2 => $key['coefficient']],
            ];
            if ($key['version'] == 'multi') {
                foreach ($key['otherPrimeInfos'] as $primeInfo) {
                    $components['primes'][] = $primeInfo['prime'];
                    $components['exponents'][] = $primeInfo['exponent'];
                    $components['coefficients'][] = $primeInfo['coefficient'];
                }
            }
            if (!isset($components['isPublicKey'])) {
                $components['isPublicKey'] = false;
            }
            return $components;
        }

        $key = ASN1::map($decoded, Maps\RSAPublicKey::MAP)->toArray();

        if (!isset($components['isPublicKey'])) {
            $components['isPublicKey'] = true;
        }

        $components = $components + $key;
        foreach ($components as &$val) {
            if ($val instanceof BigInteger) {
                $val = self::makePositive($val);
            }
            if (is_array($val)) {
                foreach ($val as &$subval) {
                    if ($subval instanceof BigInteger) {
                        $subval = self::makePositive($subval);
                    }
                }
            }
        }

        return $components + $key;
    }

    /**
     * Convert a private key to the appropriate format.
     */
    public static function savePrivateKey(
        BigInteger $n,
        BigInteger $e,
        #[\SensitiveParameter] BigInteger $d,
        #[\SensitiveParameter] array $primes,
        #[\SensitiveParameter] array $exponents,
        #[\SensitiveParameter] array $coefficients,
        #[\SensitiveParameter] ?string $password = null,
        array $options = []
    ): string {
        $num_primes = count($primes);
        $key = [
            'version' => $num_primes == 2 ? 'two-prime' : 'multi',
            'modulus' => $n,
            'publicExponent' => $e,
            'privateExponent' => $d,
            'prime1' => $primes[1],
            'prime2' => $primes[2],
            'exponent1' => $exponents[1],
            'exponent2' => $exponents[2],
            'coefficient' => $coefficients[2],
        ];
        for ($i = 3; $i <= $num_primes; $i++) {
            $key['otherPrimeInfos'][] = [
                'prime' => $primes[$i],
                'exponent' => $exponents[$i],
                'coefficient' => $coefficients[$i],
            ];
        }

        $key = ASN1::encodeDER($key, Maps\RSAPrivateKey::MAP);

        return self::wrapPrivateKey($key, 'RSA', $password, $options);
    }

    /**
     * Convert a public key to the appropriate format
     *
     * @psalm-suppress PossiblyUnusedParam
     */
    public static function savePublicKey(BigInteger $n, BigInteger $e, array $options = []): string
    {
        $key = [
            'modulus' => $n,
            'publicExponent' => $e,
        ];

        $key = ASN1::encodeDER($key, Maps\RSAPublicKey::MAP);

        return self::wrapPublicKey($key, 'RSA');
    }

    /**
     * Negative numbers make no sense in RSA so convert them to positiveAdd commentMore actions
     */
    private static function makePositive(BigInteger $x): BigInteger
    {
        return $x->isNegative() ?
            new BigInteger($x->toBytes(true), 256) :
            $x;
    }
}
<?php

/**
 * PuTTY Formatted RSA Key Handler
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\RSA\Formats\Keys;

use phpseclib4\Common\Functions\Strings;
use phpseclib4\Crypt\Common\Formats\Keys\PuTTY as Progenitor;
use phpseclib4\Exception\InvalidArgumentException;
use phpseclib4\Math\BigInteger;

/**
 * PuTTY Formatted RSA Key Handler
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class PuTTY extends Progenitor
{
    /**
     * Public Handler
     *
     * @var string
     */
    public const PUBLIC_HANDLER = OpenSSH::class;

    /**
     * Algorithm Identifier
     */
    protected static array $types = ['ssh-rsa'];

    /**
     * Break a public or private key down into its constituent components
     */
    public static function load(
        #[\SensitiveParameter] string $key,
        #[\SensitiveParameter] ?string $password
    ): array {
        static $one;
        if (!isset($one)) {
            $one = new BigInteger(1);
        }

        $components = parent::load($key, $password);
        if (!isset($components['private'])) {
            return $components;
        }
        [
            //'type' => $type,
            'comment' => $comment,
            'public' => $public,
            'private' => $private
        ] = $components;
        unset($components['public'], $components['private']);

        $isPublicKey = false;

        $primes = $coefficients = [];
        [$publicExponent, $modulus] = Strings::unpackSSH2('ii', $public);
        [$privateExponent, $primes[1], $primes[2], $coefficients[2]] = Strings::unpackSSH2('iiii', $private);

        $temp = $primes[1]->subtract($one);
        $exponents = [1 => $publicExponent->modInverse($temp)];
        $temp = $primes[2]->subtract($one);
        $exponents[] = $publicExponent->modInverse($temp);

        return compact('publicExponent', 'modulus', 'privateExponent', 'primes', 'coefficients', 'exponents', 'comment', 'isPublicKey');
    }

    /**
     * Convert a private key to the appropriate format.
     *
     * @psalm-suppress PossiblyUnusedParam
     */
    public static function savePrivateKey(
        BigInteger $n,
        BigInteger $e,
        #[\SensitiveParameter] BigInteger $d,
        #[\SensitiveParameter] array $primes,
        #[\SensitiveParameter] array $exponents,
        #[\SensitiveParameter] array $coefficients,
        #[\SensitiveParameter] ?string $password = null,
        array $options = []
    ): string {
        if (count($primes) != 2) {
            throw new InvalidArgumentException('PuTTY does not support multi-prime RSA keys');
        }

        $public =  Strings::packSSH2('ii', $e, $n);
        $private = Strings::packSSH2('iiii', $d, $primes[1], $primes[2], $coefficients[2]);

        return self::wrapPrivateKey($public, $private, 'ssh-rsa', $password, $options);
    }

    /**
     * Convert a public key to the appropriate format
     *
     * @psalm-suppress PossiblyUnusedParam
     */
    public static function savePublicKey(BigInteger $n, BigInteger $e, array $options = []): string
    {
        return self::wrapPublicKey(Strings::packSSH2('ii', $e, $n), 'ssh-rsa');
    }
}
<?php

/**
 * JSON Web Key (RFC7517) Formatted RSA Handler
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2022-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\RSA\Formats\Keys;

use phpseclib4\Common\Functions\Strings;
use phpseclib4\Crypt\Common\Formats\Keys\JWK as Progenitor;
use phpseclib4\Exception\{InvalidArgumentException, UnexpectedValueException};
use phpseclib4\Math\BigInteger;

/**
 * JWK Formatted RSA Handler
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class JWK extends Progenitor
{
    /**
     * Break a public or private key down into its constituent components
     *
     * @psalm-suppress PossiblyUnusedParam
     */
    public static function load(
        #[\SensitiveParameter] string $key,
        #[\SensitiveParameter] ?string $password = null
    ): array {
        $key = parent::loadHelper($key);

        if ($key->kty != 'RSA') {
            throw new UnexpectedValueException('Only RSA JWK keys are supported');
        }

        $count = $publicCount = 0;
        $vars = ['n', 'e', 'd', 'p', 'q', 'dp', 'dq', 'qi'];
        foreach ($vars as $var) {
            if (!isset($key->$var) || !is_string($key->$var)) {
                continue;
            }
            $count++;
            $value = new BigInteger(Strings::base64url_decode($key->$var), 256);
            switch ($var) {
                case 'n':
                    $publicCount++;
                    $components['modulus'] = $value;
                    break;
                case 'e':
                    $publicCount++;
                    $components['publicExponent'] = $value;
                    break;
                case 'd':
                    $components['privateExponent'] = $value;
                    break;
                case 'p':
                    $components['primes'][1] = $value;
                    break;
                case 'q':
                    $components['primes'][2] = $value;
                    break;
                case 'dp':
                    $components['exponents'][1] = $value;
                    break;
                case 'dq':
                    $components['exponents'][2] = $value;
                    break;
                case 'qi':
                    $components['coefficients'][2] = $value;
            }
        }

        if ($count == count($vars)) {
            return $components + ['isPublicKey' => false];
        }

        if ($count == 2 && $publicCount == 2) {
            return $components + ['isPublicKey' => true];
        }

        throw new UnexpectedValueException('Key does not have an appropriate number of RSA parameters');
    }

    /**
     * Convert a private key to the appropriate format.
     */
    public static function savePrivateKey(
        BigInteger $n,
        BigInteger $e,
        #[\SensitiveParameter] BigInteger $d,
        #[\SensitiveParameter] array $primes,
        #[\SensitiveParameter] array $exponents,
        #[\SensitiveParameter] array $coefficients,
        #[\SensitiveParameter] ?string $password = null,
        array $options = []
    ): string {
        if (isset($password)) {
            throw new InvalidArgumentException('JWK private keys do not support encryption');
        }

        if (count($primes) != 2) {
            throw new InvalidArgumentException('JWK does not support multi-prime RSA keys');
        }

        $key = [
            'kty' => 'RSA',
            'n' => Strings::base64url_encode($n->toBytes()),
            'e' => Strings::base64url_encode($e->toBytes()),
            'd' => Strings::base64url_encode($d->toBytes()),
            'p' => Strings::base64url_encode($primes[1]->toBytes()),
            'q' => Strings::base64url_encode($primes[2]->toBytes()),
            'dp' => Strings::base64url_encode($exponents[1]->toBytes()),
            'dq' => Strings::base64url_encode($exponents[2]->toBytes()),
            'qi' => Strings::base64url_encode($coefficients[2]->toBytes()),
        ];

        return self::wrapKey($key, $options);
    }

    /**
     * Convert a public key to the appropriate format
     */
    public static function savePublicKey(BigInteger $n, BigInteger $e, array $options = []): string
    {
        $key = [
            'kty' => 'RSA',
            'n' => Strings::base64url_encode($n->toBytes()),
            'e' => Strings::base64url_encode($e->toBytes()),
        ];

        return self::wrapKey($key, $options);
    }
}
<?php

/**
 * RSA Public Key
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2019-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\RSA;

use phpseclib4\Crypt\{Common, RSA, RSA\PrivateKey};
use phpseclib4\Exception\{
    BadConfigurationException,
    KeyConstraintException,
    LengthException,
    UnexpectedValueException,
    UnsupportedAlgorithmException
};
use phpseclib4\Math\BigInteger;

/**
 * RSA Public Key
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
final class PublicKey extends RSA implements Common\PublicKey
{
    use Common\Traits\Fingerprint;

    /**
     * Exponentiate
     */
    private function exponentiate(BigInteger $x): BigInteger
    {
        return $x->modPow($this->exponent, $this->modulus);
    }

    /**
     * RSAVP1
     *
     * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.2 RFC3447#section-5.2.2}.
     */
    private function rsavp1(BigInteger $s): BigInteger
    {
        if ($s->compare(self::$zero) < 0 || $s->compare($this->modulus) > 0) {
            throw new LengthException('Signature representative out of range');
        }
        return $this->exponentiate($s);
    }

    /**
     * RSASSA-PKCS1-V1_5-VERIFY
     *
     * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.2 RFC3447#section-8.2.2}.
     */
    private function rsassa_pkcs1_v1_5_verify(string $m, string $s): bool
    {
        // Length checking

        if (strlen($s) != $this->k) {
            return false;
        }

        // RSA verification

        try {
            $s = $this->os2ip($s);
            $m2 = $this->rsavp1($s);
            $em = $this->i2osp($m2, $this->k);
        } catch (\Exception) {
            return false;
        }

        // EMSA-PKCS1-v1_5 encoding

        $exception = false;

        // If the encoding operation outputs "intended encoded message length too short," output "RSA modulus
        // too short" and stop.
        try {
            $em2 = $this->emsa_pkcs1_v1_5_encode($m, $this->k);
            $r1 = hash_equals($em, $em2);
        } catch (LengthException $e) {
            $exception = true;
        }

        try {
            $em3 = $this->emsa_pkcs1_v1_5_encode_without_null($m, $this->k);
            $r2 = hash_equals($em, $em3);
        } catch (LengthException $e) {
            $exception = true;
        } catch (UnsupportedAlgorithmException $e) {
            $r2 = false;
        }

        if ($exception) {
            throw new KeyConstraintException('RSA modulus too short');
        }

        // Compare
        return boolval($r1 | $r2);
    }

    /**
     * EMSA-PSS-VERIFY
     *
     * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.2 RFC3447#section-9.1.2}.
     */
    private function emsa_pss_verify(string $m, string $em, int $emBits): bool
    {
        // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
        // be output.

        $emLen = ($emBits + 7) >> 3; // ie. ceil($emBits / 8);
        $sLen = static::$autoSaltLength ? 0 : ($this->sLen ?? $this->hLen);

        $mHash = $this->hash->hash($m);
        if ($emLen < $this->hLen + $sLen + 2) {
            return false;
        }

        if ($em[-1] != chr(0xBC)) {
            return false;
        }

        $maskedDB = substr($em, 0, -$this->hLen - 1);
        $h = substr($em, -$this->hLen - 1, $this->hLen);
        $temp = chr(256 - (1 << ($emBits & 7)));
        if ((~$maskedDB[0] & $temp) != $temp) {
            return false;
        }
        $dbMask = $this->mgf1($h, $emLen - $this->hLen - 1);
        $db = $maskedDB ^ $dbMask;
        $db[0] = ~chr(256 - (1 << ($emBits & 7))) & $db[0];

        // PS is a run of zero bytes terminated by a single 0x01
        $psLen = strspn($db, "\0");
        if ($psLen == strlen($db) || $db[$psLen] != chr(0x01)) {
            return false;
        }
        if (!static::$autoSaltLength && $psLen != $emLen - $this->hLen - $sLen - 2) {
            return false;
        }

        $salt = substr($db, $psLen + 1); // should be $sLen long
        $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
        $h2 = $this->hash->hash($m2);
        return hash_equals($h, $h2);
    }

    /**
     * RSASSA-PSS-VERIFY
     *
     * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.2 RFC3447#section-8.1.2}.
     */
    private function rsassa_pss_verify(string $m, string $s): bool
    {
        // Length checking

        if (strlen($s) != $this->k) {
            return false;
        }

        // RSA verification

        $modBits = strlen($this->modulus->toBits());

        try {
            $s2 = $this->os2ip($s);
            $m2 = $this->rsavp1($s2);
            $em = $this->i2osp($m2, $this->k);
        } catch (\Exception) {
            return false;
        }

        // EMSA-PSS verification

        return $this->emsa_pss_verify($m, $em, $modBits - 1);
    }

    /**
     * Verifies a signature
     *
     * @see self::sign()
     */
    public function verify(string $message, string|array $signature): bool
    {
        if (is_array($signature)) {
            throw new UnexpectedValueException('$message must be a string, not an array');
        }

        $result = $this->handleOpenSSL('openssl_verify', $message, $signature);
        if ($result !== null) {
            return $result;
        }

        return match ($this->signaturePadding) {
            self::SIGNATURE_PKCS1 => $this->rsassa_pkcs1_v1_5_verify($message, $signature),
            self::SIGNATURE_PSS => $this->rsassa_pss_verify($message, $signature)
        };
    }

    /**
     * RSAES-PKCS1-V1_5-ENCRYPT
     *
     * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.1 RFC3447#section-7.2.1}.
     */
    private function rsaes_pkcs1_v1_5_encrypt(string $m): string
    {
        $mLen = strlen($m);

        // Length checking

        if ($mLen > $this->k - 11) {
            throw new KeyConstraintException('Message too long');
        }

        // EME-PKCS1-v1_5 encoding

        $psLen = $this->k - $mLen - 3;
        $ps = '';
        while (strlen($ps) != $psLen) {
            /** @psalm-suppress InvalidArgument */
            $temp = random_bytes($psLen - strlen($ps));
            $temp = str_replace("\x00", '', $temp);
            $ps .= $temp;
        }
        $type = 2;
        $em = chr(0) . chr($type) . $ps . chr(0) . $m;

        // RSA encryption
        $m = $this->os2ip($em);
        $c = $this->rsaep($m);
        $c = $this->i2osp($c, $this->k);

        // Output the ciphertext C

        return $c;
    }

    /**
     * RSAES-OAEP-ENCRYPT
     *
     * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.1 RFC3447#section-7.1.1} and
     * {http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding OAES}.
     */
    private function rsaes_oaep_encrypt(string $m): string
    {
        $mLen = strlen($m);

        // Length checking

        // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
        // be output.

        if ($mLen > $this->k - 2 * $this->hLen - 2) {
            throw new KeyConstraintException('Message too long');
        }

        // EME-OAEP encoding

        $lHash = $this->hash->hash($this->label);
        $ps = str_repeat(chr(0), $this->k - $mLen - 2 * $this->hLen - 2);
        $db = $lHash . $ps . chr(1) . $m;
        $seed = random_bytes($this->hLen);
        $dbMask = $this->mgf1($seed, $this->k - $this->hLen - 1);
        $maskedDB = $db ^ $dbMask;
        $seedMask = $this->mgf1($maskedDB, $this->hLen);
        $maskedSeed = $seed ^ $seedMask;
        $em = chr(0) . $maskedSeed . $maskedDB;

        // RSA encryption

        $m = $this->os2ip($em);
        $c = $this->rsaep($m);
        $c = $this->i2osp($c, $this->k);

        // Output the ciphertext C

        return $c;
    }

    /**
     * RSAEP
     *
     * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.1}.
     */
    private function rsaep(BigInteger $m): BigInteger
    {
        if ($m->compare(self::$zero) < 0 || $m->compare($this->modulus) > 0) {
            throw new LengthException('Message representative out of range');
        }
        return $this->exponentiate($m);
    }

    /**
     * Raw Encryption / Decryption
     *
     * Doesn't use padding and is not recommended.
     */
    private function raw_encrypt(string $m): string
    {
        if (strlen($m) > $this->k) {
            throw new KeyConstraintException('Message too long');
        }

        $temp = $this->os2ip($m);
        $temp = $this->rsaep($temp);
        return  $this->i2osp($temp, $this->k);
    }

    /**
     * Encryption
     *
     * Both self::PADDING_OAEP and self::PADDING_PKCS1 both place limits on how long $plaintext can be.
     * If $plaintext exceeds those limits it will be broken up so that it does and the resultant ciphertext's will
     * be concatenated together.
     *
     * @see self::decrypt()
     */
    public function encrypt(#[\SensitiveParameter] string $plaintext): string
    {
        $result = $this->handleOpenSSL('openssl_public_encrypt', $plaintext);
        if ($result !== null) {
            return $result;
        }

        return match ($this->encryptionPadding) {
            self::ENCRYPTION_NONE => $this->raw_encrypt($plaintext),
            self::ENCRYPTION_PKCS1 => $this->rsaes_pkcs1_v1_5_encrypt($plaintext),
            // self::ENCRYPTION_OAEP
            default => $this->rsaes_oaep_encrypt($plaintext)
        };
    }

    /**
     * Converts a public key to a private key
     */
    public function asPrivateKey(): PrivateKey
    {
        $new = new PrivateKey();
        $new->exponent = $this->exponent;
        $new->modulus = $this->modulus;
        $new->k = $this->k;
        $new->format = $this->format;
        return $new
            ->withHash($this->hash->getHash())
            ->withMGFHash($this->mgfHash->getHash())
            ->withSaltLength($this->sLen)
            ->withLabel($this->label)
            ->withPadding($this->signaturePadding | $this->encryptionPadding);
    }
}
<?php

/**
 * RSA Private Key
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2019-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\RSA;

use phpseclib4\Crypt\{Common, RSA};
use phpseclib4\Exception\{BadDecryptionException, KeyConstraintException, LengthException};
use phpseclib4\File\Common\Signable;
use phpseclib4\File\CSR;
use phpseclib4\Math\BigInteger;

/**
 * RSA Private Key
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
final class PrivateKey extends RSA implements Common\PrivateKey
{
    use Common\Traits\PasswordProtected;

    /**
     * Primes for Chinese Remainder Theorem (ie. p and q)
     *
     * @var BigInteger[]
     */
    protected array $primes;

    /**
     * Exponents for Chinese Remainder Theorem (ie. dP and dQ)
     *
     * @var BigInteger[]
     */
    protected array $exponents;

    /**
     * Coefficients for Chinese Remainder Theorem (ie. qInv)
     *
     * @var BigInteger[]
     */
    protected array $coefficients;

    /**
     * Private Exponent
     */
    protected BigInteger $privateExponent;

    /**
     * RSADP
     *
     * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.2 RFC3447#section-5.1.2}.
     */
    private function rsadp(BigInteger $c): BigInteger
    {
        if ($c->compare(self::$zero) < 0 || $c->compare($this->modulus) > 0) {
            throw new LengthException('Ciphertext representative out of range');
        }
        return $this->exponentiate($c);
    }

    /**
     * RSASP1
     *
     * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.1 RFC3447#section-5.2.1}.
     */
    private function rsasp1(BigInteger $m): BigInteger
    {
        if ($m->compare(self::$zero) < 0 || $m->compare($this->modulus) > 0) {
            throw new LengthException('Signature representative out of range');
        }
        return $this->exponentiate($m);
    }

    /**
     * Exponentiate
     */
    protected function exponentiate(BigInteger $x): BigInteger
    {
        switch (true) {
            case empty($this->primes):
            case $this->primes[1]->equals(self::$zero):
            case empty($this->coefficients):
            case $this->coefficients[2]->equals(self::$zero):
            case empty($this->exponents):
            case $this->exponents[1]->equals(self::$zero):
                return $x->modPow($this->exponent, $this->modulus);
        }

        $num_primes = count($this->primes);

        if (!static::$enableBlinding) {
            $m_i = [
                1 => $x->modPow($this->exponents[1], $this->primes[1]),
                2 => $x->modPow($this->exponents[2], $this->primes[2]),
            ];
            $h = $m_i[1]->subtract($m_i[2]);
            $h = $h->multiply($this->coefficients[2]);
            [, $h] = $h->divide($this->primes[1]);
            $m = $m_i[2]->add($h->multiply($this->primes[2]));

            $r = $this->primes[1];
            for ($i = 3; $i <= $num_primes; $i++) {
                $m_i = $x->modPow($this->exponents[$i], $this->primes[$i]);

                $r = $r->multiply($this->primes[$i - 1]);

                $h = $m_i->subtract($m);
                $h = $h->multiply($this->coefficients[$i]);
                [, $h] = $h->divide($this->primes[$i]);

                $m = $m->add($r->multiply($h));
            }
        } else {
            $smallest = $this->primes[1];
            for ($i = 2; $i <= $num_primes; $i++) {
                if ($smallest->compare($this->primes[$i]) > 0) {
                    $smallest = $this->primes[$i];
                }
            }

            $r = BigInteger::randomRange(self::$one, $smallest->subtract(self::$one));

            $m_i = [
                1 => $this->blind($x, $r, 1),
                2 => $this->blind($x, $r, 2),
            ];
            $h = $m_i[1]->subtract($m_i[2]);
            $h = $h->multiply($this->coefficients[2]);
            [, $h] = $h->divide($this->primes[1]);
            $m = $m_i[2]->add($h->multiply($this->primes[2]));

            $r = $this->primes[1];
            for ($i = 3; $i <= $num_primes; $i++) {
                $m_i = $this->blind($x, $r, $i);

                $r = $r->multiply($this->primes[$i - 1]);

                $h = $m_i->subtract($m);
                $h = $h->multiply($this->coefficients[$i]);
                [, $h] = $h->divide($this->primes[$i]);

                $m = $m->add($r->multiply($h));
            }
        }

        return $m;
    }

    /**
     * Performs RSA Blinding
     *
     * Protects against timing attacks by employing RSA Blinding.
     * Returns $x->modPow($this->exponents[$i], $this->primes[$i])
     */
    private function blind(BigInteger $x, BigInteger $r, int $i): BigInteger
    {
        $x = $x->multiply($r->modPow($this->publicExponent, $this->primes[$i]));
        $x = $x->modPow($this->exponents[$i], $this->primes[$i]);

        $r = $r->modInverse($this->primes[$i]);
        $x = $x->multiply($r);
        [, $x] = $x->divide($this->primes[$i]);

        return $x;
    }

    /**
     * EMSA-PSS-ENCODE
     *
     * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.1 RFC3447#section-9.1.1}.
     */
    private function emsa_pss_encode(string $m, int $emBits): string
    {
        // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
        // be output.

        $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8)
        $sLen = $this->sLen ?? $this->hLen;

        $mHash = $this->hash->hash($m);
        if ($emLen < $this->hLen + $sLen + 2) {
            throw new LengthException('RSA modulus too short');
        }

        $salt = $sLen ? random_bytes($sLen) : '';
        $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
        $h = $this->hash->hash($m2);
        $ps = str_repeat(chr(0), $emLen - $sLen - $this->hLen - 2);
        $db = $ps . chr(1) . $salt;
        $dbMask = $this->mgf1($h, $emLen - $this->hLen - 1); // ie. stlren($db)
        $maskedDB = $db ^ $dbMask;
        $maskedDB[0] = ~chr(256 - (1 << ($emBits & 7))) & $maskedDB[0];
        $em = $maskedDB . $h . chr(0xBC);

        return $em;
    }

    /**
     * RSASSA-PSS-SIGN
     *
     * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.1 RFC3447#section-8.1.1}.
     */
    private function rsassa_pss_sign(string $m): string
    {
        // EMSA-PSS encoding

        $em = $this->emsa_pss_encode($m, 8 * $this->k - 1);

        // RSA signature

        $m = $this->os2ip($em);
        $s = $this->rsasp1($m);
        $s = $this->i2osp($s, $this->k);

        // Output the signature S

        return $s;
    }

    /**
     * RSASSA-PKCS1-V1_5-SIGN
     *
     * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.1 RFC3447#section-8.2.1}.
     */
    private function rsassa_pkcs1_v1_5_sign(string $m): string
    {
        // EMSA-PKCS1-v1_5 encoding

        // If the encoding operation outputs "intended encoded message length too short," output "RSA modulus
        // too short" and stop.
        try {
            $em = $this->emsa_pkcs1_v1_5_encode($m, $this->k);
        } catch (LengthException $e) {
            throw new LengthException('RSA modulus too short');
        }

        // RSA signature

        $m = $this->os2ip($em);
        $s = $this->rsasp1($m);
        $s = $this->i2osp($s, $this->k);

        // Output the signature S

        return $s;
    }

    /**
     * Create a signature
     *
     * @see self::verify()
     */
    public function sign(string|Signable $source): string|array
    {
        if ($source instanceof Signable) {
            $public = $this->getPublicKey();
            if ($source instanceof CSR && !$source->hasPublicKey()) {
                $source->setPublicKey($public);
            }
            $source->identifySignatureAlgorithm($this);
            $message = $source->getSignableSection();
        } else {
            $message = $source;
        }

        $signature = $this->handleOpenSSL('openssl_sign', $message);
        if ($signature !== null) {
            if ($source instanceof Signable) {
                $source->setSignature($signature);
            }
            return $signature;
        }

        $signature = match ($this->signaturePadding) {
            self::SIGNATURE_PKCS1 => $this->rsassa_pkcs1_v1_5_sign($message),
            self::SIGNATURE_PSS => $this->rsassa_pss_sign($message)
        };

        if ($source instanceof Signable) {
            $source->setSignature($signature);
        }
        return $signature;
    }

    /**
     * RSAES-PKCS1-V1_5-DECRYPT
     *
     * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.2 RFC3447#section-7.2.2}.
     */
    private function rsaes_pkcs1_v1_5_decrypt(string $c): string
    {
        // Length checking

        if (strlen($c) != $this->k) { // or if k < 11
            throw new KeyConstraintException('Ciphertext representative too long');
        }

        // RSA decryption

        $c = $this->os2ip($c);
        $m = $this->rsadp($c);
        $em = $this->i2osp($m, $this->k);

        // EME-PKCS1-v1_5 decoding

        if ((ord($em[0]) != 0) | (ord($em[1]) > 2)) {
            throw new BadDecryptionException('Decryption error');
        }

        $ps = substr($em, 2, strpos($em, chr(0), 2) - 2);
        $m = substr($em, strlen($ps) + 3);

        if (strlen($ps) < 8) {
            throw new BadDecryptionException('Decryption error');
        }

        // Output M

        return $m;
    }

    /**
     * RSAES-OAEP-DECRYPT
     *
     * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.2 RFC3447#section-7.1.2}.  The fact that the error
     * messages aren't distinguishable from one another hinders debugging, but, to quote from RFC3447#section-7.1.2:
     *
     *    Note.  Care must be taken to ensure that an opponent cannot
     *    distinguish the different error conditions in Step 3.g, whether by
     *    error message or timing, or, more generally, learn partial
     *    information about the encoded message EM.  Otherwise an opponent may
     *    be able to obtain useful information about the decryption of the
     *    ciphertext C, leading to a chosen-ciphertext attack such as the one
     *    observed by Manger [36].
     */
    private function rsaes_oaep_decrypt(string $c): string
    {
        // Length checking

        // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
        // be output.

        if (strlen($c) != $this->k || $this->k < 2 * $this->hLen + 2) {
            throw new KeyConstraintException('Ciphertext representative too long');
        }

        // RSA decryption

        $c = $this->os2ip($c);
        $m = $this->rsadp($c);
        $em = $this->i2osp($m, $this->k);

        // EME-OAEP decoding

        $lHash = $this->hash->hash($this->label);
        //$y = ord($em[0]);
        $maskedSeed = substr($em, 1, $this->hLen);
        $maskedDB = substr($em, $this->hLen + 1);
        $seedMask = $this->mgf1($maskedDB, $this->hLen);
        $seed = $maskedSeed ^ $seedMask;
        $dbMask = $this->mgf1($seed, $this->k - $this->hLen - 1);
        $db = $maskedDB ^ $dbMask;
        $lHash2 = substr($db, 0, $this->hLen);
        $m = substr($db, $this->hLen);
        $hashesMatch = hash_equals($lHash, $lHash2);
        $leadingZeros = 1;
        $patternMatch = 0;
        $offset = 0;
        for ($i = 0; $i < strlen($m); $i++) {
            $patternMatch |= $leadingZeros & ($m[$i] === "\1");
            $leadingZeros &= $m[$i] === "\0";
            $offset += $patternMatch ? 0 : 1;
        }

        // we do | instead of || to avoid https://en.wikipedia.org/wiki/Short-circuit_evaluation
        // to protect against timing attacks
        if (!$hashesMatch | !$patternMatch) {
            throw new BadDecryptionException('Decryption error');
        }

        // Output the message M

        return substr($m, $offset + 1);
    }

    /**
     * Raw Encryption / Decryption
     *
     * Doesn't use padding and is not recommended.
     */
    private function raw_encrypt(string $m): string
    {
        if (strlen($m) > $this->k) {
            throw new KeyConstraintException('Ciphertext representative too long');
        }

        $temp = $this->os2ip($m);
        $temp = $this->rsadp($temp);
        return  $this->i2osp($temp, $this->k);
    }

    /**
     * Decryption
     *
     * @see self::encrypt()
     */
    public function decrypt(string $ciphertext): string
    {
        $result = $this->handleOpenSSL('openssl_private_decrypt', $ciphertext);
        if ($result !== null) {
            return $result;
        }

        return match ($this->encryptionPadding) {
            self::ENCRYPTION_NONE => $this->raw_encrypt($ciphertext),
            self::ENCRYPTION_PKCS1 => $this->rsaes_pkcs1_v1_5_decrypt($ciphertext),
            // self::ENCRYPTION_OAEP
            default => $this->rsaes_oaep_decrypt($ciphertext)
        };
    }

    /**
     * Returns the public key
     */
    public function getPublicKey(): PublicKey
    {
        $type = self::validatePlugin('Keys', 'PKCS8', 'savePublicKey');
        if (empty($this->modulus) || empty($this->publicExponent)) {
            throw new KeyConstraintException('Public key components not found');
        }

        $key = $type::savePublicKey($this->modulus, $this->publicExponent);
        return RSA::loadFormat('PKCS8', $key)
            ->withHash($this->hash->getHash())
            ->withMGFHash($this->mgfHash->getHash())
            ->withSaltLength($this->sLen)
            ->withLabel($this->label)
            ->withPadding($this->signaturePadding | $this->encryptionPadding);
    }
}
<?php

/**
 * Pure-PHP implementation of AES.
 *
 * Uses OpenSSL, if available/possible, and an internal implementation, otherwise
 *
 * PHP version 8.1+
 *
 * NOTE: Since AES.php is (for compatibility and phpseclib-historical reasons) virtually
 * just a wrapper to Rijndael.php you may consider using Rijndael.php instead of
 * to save one include_once().
 *
 * If {@link self::setKeyLength() setKeyLength()} isn't called, it'll be calculated from
 * {@link self::setKey() setKey()}.  ie. if the key is 128-bits, the key length will be 128-bits.  If it's 136-bits
 * it'll be null-padded to 192-bits and 192 bits will be the key length until {@link self::setKey() setKey()}
 * is called, again, at which point, it'll be recalculated.
 *
 * Since \phpseclib4\Crypt\AES extends \phpseclib4\Crypt\Rijndael, some functions are available to be called that, in the context of AES, don't
 * make a whole lot of sense.  {@link self::setBlockLength() setBlockLength()}, for instance.  Calling that function,
 * however possible, won't do anything (AES has a fixed block length whereas Rijndael has a variable one).
 *
 * Here's a short example of how to use this library:
 * <code>
 * <?php
 *    include 'vendor/autoload.php';
 *
 *    $aes = new \phpseclib4\Crypt\AES('ctr');
 *
 *    $aes->setKey('abcdefghijklmnop');
 *
 *    $size = 10 * 1024;
 *    $plaintext = '';
 *    for ($i = 0; $i < $size; $i++) {
 *        $plaintext.= 'a';
 *    }
 *
 *    echo $aes->decrypt($aes->encrypt($plaintext));
 * ?>
 * </code>
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2009-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt;

use phpseclib4\Exception\{BadMethodCallException, LengthException};

/**
 * Pure-PHP implementation of AES.
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class AES extends Rijndael
{
    /**
     * Dummy function
     *
     * Since \phpseclib4\Crypt\AES extends \phpseclib4\Crypt\Rijndael, this function is, technically, available, but it doesn't do anything.
     *
     * @see \phpseclib4\Crypt\Rijndael::setBlockLength()
     */
    public function setBlockLength(int $length): void
    {
        throw new BadMethodCallException('The block length cannot be set for AES.');
    }

    /**
     * Sets the key length
     *
     * Valid key lengths are 128, 192, and 256.  Set the link to bool(false) to disable a fixed key length
     *
     * @see \phpseclib4\Crypt\Rijndael:setKeyLength()
     */
    public function setKeyLength(int $length): void
    {
        switch ($length) {
            case 128:
            case 192:
            case 256:
                break;
            default:
                throw new LengthException('Key of size ' . $length . ' not supported by this algorithm. Only keys of sizes 128, 192 or 256 supported');
        }
        parent::setKeyLength($length);
    }

    /**
     * Sets the key.
     *
     * Rijndael supports five different key lengths, AES only supports three.
     *
     * @see \phpseclib4\Crypt\Rijndael:setKey()
     * @see setKeyLength()
     */
    public function setKey(#[\SensitiveParameter] string $key): void
    {
        switch (strlen($key)) {
            case 16:
            case 24:
            case 32:
                break;
            default:
                throw new LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of sizes 16, 24 or 32 supported');
        }

        parent::setKey($key);
    }
}
<?php

/**
 * PKCS#8 Formatted DSA Key Handler
 *
 * PHP version 8.1+
 *
 * Processes keys with the following headers:
 *
 * -----BEGIN ENCRYPTED PRIVATE KEY-----
 * -----BEGIN PRIVATE KEY-----
 * -----BEGIN PUBLIC KEY-----
 *
 * Analogous to ssh-keygen's pkcs8 format (as specified by -m). Although PKCS8
 * is specific to private keys it's basically creating a DER-encoded wrapper
 * for keys. This just extends that same concept to public keys (much like ssh-keygen)
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\DSA\Formats\Keys;

use phpseclib4\Crypt\Common\Formats\Keys\PKCS8 as Progenitor;
use phpseclib4\Exception\UnexpectedValueException;
use phpseclib4\File\ASN1;
use phpseclib4\File\ASN1\Maps;
use phpseclib4\Math\BigInteger;

/**
 * PKCS#8 Formatted DSA Key Handler
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class PKCS8 extends Progenitor
{
    /**
     * OID Name
     *
     * @var string
     */
    public const OID_NAME = 'id-dsa';

    /**
     * OID Value
     *
     * @var string
     */
    public const OID_VALUE = '1.2.840.10040.4.1';

    /**
     * Break a public or private key down into its constituent components
     */
    public static function load(
        #[\SensitiveParameter] string $key,
        #[\SensitiveParameter] ?string $password = null
    ): array {
        if (str_contains($key, 'PUBLIC')) {
            $isPublic = true;
        } elseif (str_contains($key, 'PRIVATE')) {
            $isPublic = false;
        }

        $key = parent::load($key, $password);

        $type = isset($key['privateKey']) ? 'privateKey' : 'publicKey';

        if (isset($isPublic)) {
            switch (true) {
                case !$isPublic && $type == 'publicKey':
                    throw new UnexpectedValueException('Human readable string claims non-public key but DER encoded string claims public key');
                case $isPublic && $type == 'privateKey':
                    throw new UnexpectedValueException('Human readable string claims public key but DER encoded string claims private key');
            }
        }

        $decoded = ASN1::decodeBER((string) $key[$type . 'Algorithm']['parameters']);
        $components = ASN1::map($decoded, Maps\DSAParams::MAP)->toArray();
        $decoded = ASN1::decodeBER((string) $key[$type]);

        $var = $type == 'privateKey' ? 'x' : 'y';
        $components[$var] = ASN1::map($decoded, Maps\DSAPublicKey::MAP);
        if (!$components[$var] instanceof BigInteger) {
            throw new UnexpectedValueException('Unable to perform ASN1 mapping');
        }

        if (isset($key['meta'])) {
            $components['meta'] = $key['meta'];
        }

        return $components;
    }

    /**
     * Convert a private key to the appropriate format.
     *
     * @psalm-suppress PossiblyUnusedParam
     */
    public static function savePrivateKey(
        BigInteger $p,
        BigInteger $q,
        BigInteger $g,
        BigInteger $y,
        #[\SensitiveParameter] BigInteger $x,
        #[\SensitiveParameter] ?string $password = null,
        array $options = []
    ): string {
        $params = [
            'p' => $p,
            'q' => $q,
            'g' => $g,
        ];
        $params = ASN1::encodeDER($params, Maps\DSAParams::MAP);
        $params = new ASN1\Element($params);
        $key = ASN1::encodeDER($x, Maps\DSAPublicKey::MAP);
        return self::wrapPrivateKey(
            key: $key,
            params: $params,
            password: $password,
            options: $options
        );
    }

    /**
     * Convert a public key to the appropriate format
     */
    public static function savePublicKey(
        BigInteger $p,
        BigInteger $q,
        BigInteger $g,
        BigInteger $y,
        array $options = []
    ): string {
        $params = [
            'p' => $p,
            'q' => $q,
            'g' => $g,
        ];
        $params = ASN1::encodeDER($params, Maps\DSAParams::MAP);
        $params = new ASN1\Element($params);
        $key = ASN1::encodeDER($y, Maps\DSAPublicKey::MAP);
        return self::wrapPublicKey(
            key: $key,
            params: $params,
            options: $options
        );
    }
}
<?php

/**
 * Raw DSA Key Handler
 *
 * PHP version 8.1+
 *
 * Reads and creates arrays as DSA keys
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\DSA\Formats\Keys;

use phpseclib4\Exception\UnexpectedValueException;
use phpseclib4\Math\BigInteger;

/**
 * Raw DSA Key Handler
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class Raw
{
    /**
     * Break a public or private key down into its constituent components
     *
     * @psalm-suppress PossiblyUnusedParam
     */
    public static function load(
        #[\SensitiveParameter] array $key,
        #[\SensitiveParameter] ?string $password = null
    ): array {
        switch (true) {
            case !isset($key['p']) || !isset($key['q']) || !isset($key['g']):
            case !$key['p'] instanceof BigInteger:
            case !$key['q'] instanceof BigInteger:
            case !$key['g'] instanceof BigInteger:
            case !isset($key['x']) && !isset($key['y']):
            case isset($key['x']) && !$key['x'] instanceof BigInteger:
            case isset($key['y']) && !$key['y'] instanceof BigInteger:
                throw new UnexpectedValueException('Key appears to be malformed');
        }

        $options = ['p' => 1, 'q' => 1, 'g' => 1, 'x' => 1, 'y' => 1];

        return array_intersect_key($key, $options);
    }
}
<?php

/**
 * OpenSSH Formatted DSA Key Handler
 *
 * PHP version 8.1+
 *
 * Place in $HOME/.ssh/authorized_keys
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\DSA\Formats\Keys;

use phpseclib4\Common\Functions\Strings;
use phpseclib4\Crypt\Common\Formats\Keys\OpenSSH as Progenitor;
use phpseclib4\Exception\{LengthException, UnexpectedValueException};
use phpseclib4\Math\BigInteger;

/**
 * OpenSSH Formatted DSA Key Handler
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class OpenSSH extends Progenitor
{
    /**
     * Supported Key Types
     */
    protected static array $types = ['ssh-dss'];

    /**
     * Break a public or private key down into its constituent components
     */
    public static function load(
        #[\SensitiveParameter] string $key,
        #[\SensitiveParameter] ?string $password = null
    ): array {
        $parsed = parent::load($key, $password);

        if (isset($parsed['paddedKey'])) {
            [$type] = Strings::unpackSSH2('s', $parsed['paddedKey']);
            if ($type != $parsed['type']) {
                throw new UnexpectedValueException("The public and private keys are not of the same type ($type vs $parsed[type])");
            }

            [$p, $q, $g, $y, $x, $comment] = Strings::unpackSSH2('i5s', $parsed['paddedKey']);

            return compact('p', 'q', 'g', 'y', 'x', 'comment');
        }

        [$p, $q, $g, $y] = Strings::unpackSSH2('iiii', $parsed['publicKey']);

        $comment = $parsed['comment'];

        return compact('p', 'q', 'g', 'y', 'comment');
    }

    /**
     * Convert a public key to the appropriate format
     */
    public static function savePublicKey(
        BigInteger $p,
        BigInteger $q,
        BigInteger $g,
        BigInteger $y,
        array $options = []
    ): string {
        if ($q->getLength() != 160) {
            throw new LengthException('SSH only supports keys with an N (length of Group Order q) of 160');
        }

        // from <http://tools.ietf.org/html/rfc4253#page-15>:
        // string    "ssh-dss"
        // mpint     p
        // mpint     q
        // mpint     g
        // mpint     y
        $DSAPublicKey = Strings::packSSH2('siiii', 'ssh-dss', $p, $q, $g, $y);

        if ($options['binary'] ?? self::$binary) {
            return $DSAPublicKey;
        }

        $comment = $options['comment'] ?? self::$comment;
        $DSAPublicKey = 'ssh-dss ' . base64_encode($DSAPublicKey) . ' ' . $comment;

        return $DSAPublicKey;
    }

    /**
     * Convert a private key to the appropriate format.
     */
    public static function savePrivateKey(
        BigInteger $p,
        BigInteger $q,
        BigInteger $g,
        BigInteger $y,
        #[\SensitiveParameter] BigInteger $x,
        #[\SensitiveParameter] ?string $password = null,
        array $options = []
    ): string {
        $publicKey = self::savePublicKey($p, $q, $g, $y, ['binary' => true]);
        $privateKey = Strings::packSSH2('si5', 'ssh-dss', $p, $q, $g, $y, $x);

        return self::wrapPrivateKey($publicKey, $privateKey, $password, $options);
    }
}
<?php

/**
 * XML Formatted DSA Key Handler
 *
 * While XKMS defines a private key format for RSA it does not do so for DSA. Quoting that standard:
 *
 * "[XKMS] does not specify private key parameters for the DSA signature algorithm since the algorithm only
 *  supports signature modes and so the application of server generated keys and key recovery is of limited
 *  value"
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\DSA\Formats\Keys;

use phpseclib4\Common\Functions\Strings;
use phpseclib4\Exception\{BadConfigurationException, UnexpectedValueException};
use phpseclib4\Math\BigInteger;

/**
 * XML Formatted DSA Key Handler
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class XML
{
    /**
     * Break a public or private key down into its constituent components
     *
     * @psalm-suppress PossiblyUnusedParam
     */
    public static function load(
        #[\SensitiveParameter] string $key,
        #[\SensitiveParameter] ?string $password = null
    ): array {
        if (!class_exists('DOMDocument')) {
            throw new BadConfigurationException('The dom extension is not setup correctly on this system');
        }

        $use_errors = libxml_use_internal_errors(true);

        $dom = new \DOMDocument();
        if (substr($key, 0, 5) != '<?xml') {
            $key = '<xml>' . $key . '</xml>';
        }
        if (!$dom->loadXML($key)) {
            $e = libxml_get_last_error();
            $message = 'Error loading XML - ' . $e->message;
            libxml_use_internal_errors($use_errors);
            throw new UnexpectedValueException($message, $e->code);
        }
        $xpath = new \DOMXPath($dom);
        $keys = ['p', 'q', 'g', 'y', 'j', 'seed', 'pgencounter'];
        $components = [];
        foreach ($keys as $key) {
            // $dom->getElementsByTagName($key) is case-sensitive
            $temp = $xpath->query("//*[translate(local-name(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')='$key']");
            if (!$temp->length) {
                continue;
            }
            $value = new BigInteger(Strings::base64_decode($temp->item(0)->nodeValue), 256);
            switch ($key) {
                case 'p': // a prime modulus meeting the [DSS] requirements
                    // Parameters P, Q, and G can be public and common to a group of users. They might be known
                    // from application context. As such, they are optional but P and Q must either both appear
                    // or both be absent
                    $components['p'] = $value;
                    break;
                case 'q': // an integer in the range 2**159 < Q < 2**160 which is a prime divisor of P-1
                    $components['q'] = $value;
                    break;
                case 'g': // an integer with certain properties with respect to P and Q
                    $components['g'] = $value;
                    break;
                case 'y': // G**X mod P (where X is part of the private key and not made public)
                    $components['y'] = $value;
                    // the remaining options do not do anything
                    // no break
                case 'j': // (P - 1) / Q
                    // Parameter J is available for inclusion solely for efficiency as it is calculatable from
                    // P and Q
                case 'seed': // a DSA prime generation seed
                    // Parameters seed and pgenCounter are used in the DSA prime number generation algorithm
                    // specified in [DSS]. As such, they are optional but must either both be present or both
                    // be absent
                case 'pgencounter': // a DSA prime generation counter
            }
        }

        libxml_use_internal_errors($use_errors);

        if (!isset($components['y'])) {
            throw new UnexpectedValueException('Key is missing y component');
        }

        switch (true) {
            case !isset($components['p']):
            case !isset($components['q']):
            case !isset($components['g']):
                return ['y' => $components['y']];
        }

        return $components;
    }

    /**
     * Convert a public key to the appropriate format
     *
     * See https://www.w3.org/TR/xmldsig-core/#sec-DSAKeyValue
     *
     * @psalm-suppress PossiblyUnusedParam
     */
    public static function savePublicKey(
        BigInteger $p,
        BigInteger $q,
        BigInteger $g,
        BigInteger $y,
        array $options = []
    ): string {
        return "<DSAKeyValue>\r\n" .
               '  <P>' . Strings::base64_encode($p->toBytes()) . "</P>\r\n" .
               '  <Q>' . Strings::base64_encode($q->toBytes()) . "</Q>\r\n" .
               '  <G>' . Strings::base64_encode($g->toBytes()) . "</G>\r\n" .
               '  <Y>' . Strings::base64_encode($y->toBytes()) . "</Y>\r\n" .
               '</DSAKeyValue>';
    }
}
<?php

/**
 * PKCS#1 Formatted DSA Key Handler
 *
 * PHP version 8.1+
 *
 * Used by File/X509.php
 *
 * Processes keys with the following headers:
 *
 * -----BEGIN DSA PRIVATE KEY-----
 * -----BEGIN DSA PUBLIC KEY-----
 * -----BEGIN DSA PARAMETERS-----
 *
 * Analogous to ssh-keygen's pem format (as specified by -m)
 *
 * Also, technically, PKCS1 decribes RSA but I am not aware of a formal specification for DSA.
 * The DSA private key format seems to have been adapted from the RSA private key format so
 * we're just re-using that as the name.
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\DSA\Formats\Keys;

use phpseclib4\Common\Functions\Strings;
use phpseclib4\Crypt\Common\Formats\Keys\PKCS1 as Progenitor;
use phpseclib4\Exception\{UnexpectedValueException, UnsupportedValueException};
use phpseclib4\File\ASN1;
use phpseclib4\File\ASN1\Maps;
use phpseclib4\Math\BigInteger;

/**
 * PKCS#1 Formatted DSA Key Handler
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class PKCS1 extends Progenitor
{
    /**
     * Break a public or private key down into its constituent components
     */
    public static function load(
        #[\SensitiveParameter] string $key,
        #[\SensitiveParameter] ?string $password = null
    ): array {
        $key = parent::loadHelper($key, $password);

        $decoded = ASN1::decodeBER($key);

        try {
            return ASN1::map($decoded, Maps\DSAParams::MAP)->toArray();
        } catch (\Exception) {
        }

        try {
            $key = ASN1::map($decoded, Maps\DSAPrivateKey::MAP)->toArray();
            if ($key['version']->toString() !== '0') {
                throw new UnsupportedValueException('DSA Private Keys only work when the version number is 0');
            }
            return $key;
        } catch (\Exception) {
        }

        // PKCS1 DSA public keys are not supported by phpseclib since they can't be used to do
        // anything on their own. in order to verify a signature with DSA you need p, q, g and y.
        // a PKCS1 DSA public key only has y. to verify a signature with a PKCS1 DSA public key
        // you'd also need to load a PKCS1 DSA parameters file separately. like you'd need to
        // load two files instead of just one. there's no other key format that phpseclib supports
        // that has that requirement so building it in for PKCS1 DSA public keys seems excessive.
        //
        // the whole thing would be rather like an RSA public key having the modulo live in
        // a separate file than the exponent.
        //
        // this isn't an issue for PKCS8 DSA public keys because those keys have the parameters
        // included. eg. \phpseclib4\File\ASN1\Maps\SubjectPublicKeyInfo has "algorithm" and
        // "subjectPublicKey" and "algorithm", in turn, has "algorithm" and "parameters". y
        // is saved as "subjectPublicKey" and p, q and g are saved as "parameters".
        //
        // furthermore, the following ASN1::map() doesn't return a Constructed object
        // with Maps\DSAPublicKey::MAP - it returns a BigInteger object that doesn't
        // even have the toArray() method, anyway

        try {
            if (ASN1::map($decoded, Maps\DSAPublicKey::MAP) instanceof BigInteger) {
                throw new UnsupportedValueException('Key appears to be a DSAPublicKey, which is unsupported');
            }
        } catch (\Exception) {
        }

        throw new UnexpectedValueException('Unable to perform ASN1 mapping');
    }

    /**
     * Convert DSA parameters to the appropriate format
     */
    public static function saveParameters(BigInteger $p, BigInteger $q, BigInteger $g): string
    {
        $key = [
            'p' => $p,
            'q' => $q,
            'g' => $g,
        ];

        $key = ASN1::encodeDER($key, Maps\DSAParams::MAP);

        return "-----BEGIN DSA PARAMETERS-----\r\n" .
               chunk_split(Strings::base64_encode($key), 64) .
               "-----END DSA PARAMETERS-----\r\n";
    }

    /**
     * Convert a private key to the appropriate format.
     */
    public static function savePrivateKey(
        BigInteger $p,
        BigInteger $q,
        BigInteger $g,
        BigInteger $y,
        #[\SensitiveParameter] BigInteger $x,
        #[\SensitiveParameter] ?string $password = null,
        array $options = []
    ): string {
        $key = [
            'version' => 0,
            'p' => $p,
            'q' => $q,
            'g' => $g,
            'y' => $y,
            'x' => $x,
        ];

        $key = ASN1::encodeDER($key, Maps\DSAPrivateKey::MAP);

        return self::wrapPrivateKey($key, 'DSA', $password, $options);
    }

    /**
     * Convert a public key to the appropriate format
     *
     * @psalm-suppress PossiblyUnusedParam
     */
    public static function savePublicKey(
        BigInteger $p,
        BigInteger $q,
        BigInteger $g,
        BigInteger $y,
        array $options = []
    ): string {
        $key = ASN1::encodeDER($y, Maps\DSAPublicKey::MAP);

        return self::wrapPublicKey($key, 'DSA');
    }
}
<?php

/**
 * PuTTY Formatted DSA Key Handler
 *
 * puttygen does not generate DSA keys with an N of anything other than 160, however,
 * it can still load them and convert them. PuTTY will load them, too, but SSH servers
 * won't accept them. Since PuTTY formatted keys are primarily used with SSH this makes
 * keys with N > 160 kinda useless, hence this handlers not supporting such keys.
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\DSA\Formats\Keys;

use phpseclib4\Common\Functions\Strings;
use phpseclib4\Crypt\Common\Formats\Keys\PuTTY as Progenitor;
use phpseclib4\Exception\{LengthException, UnexpectedValueException};
use phpseclib4\Math\BigInteger;

/**
 * PuTTY Formatted DSA Key Handler
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class PuTTY extends Progenitor
{
    /**
     * Public Handler
     *
     * @var string
     */
    public const PUBLIC_HANDLER = OpenSSH::class;

    /**
     * Algorithm Identifier
     */
    protected static array $types = ['ssh-dss'];

    /**
     * Break a public or private key down into its constituent components
     */
    public static function load(
        #[\SensitiveParameter] string $key,
        #[\SensitiveParameter] ?string $password
    ): array {
        $components = parent::load($key, $password);
        if (!isset($components['private'])) {
            return $components;
        }
        [
            //'type' => $type,
            'comment' => $comment,
            'public' => $public,
            'private' => $private
        ] = $components;
        unset($components['public'], $components['private']);

        [$p, $q, $g, $y] = Strings::unpackSSH2('iiii', $public);
        [$x] = Strings::unpackSSH2('i', $private);

        return compact('p', 'q', 'g', 'y', 'x', 'comment');
    }

    /**
     * Convert a private key to the appropriate format.
     */
    public static function savePrivateKey(
        BigInteger $p,
        BigInteger $q,
        BigInteger $g,
        BigInteger $y,
        #[\SensitiveParameter] BigInteger $x,
        #[\SensitiveParameter] ?string $password = null,
        array $options = []
    ): string {
        if ($q->getLength() != 160) {
            throw new LengthException('SSH only supports keys with an N (length of Group Order q) of 160');
        }

        $public = Strings::packSSH2('iiii', $p, $q, $g, $y);
        $private = Strings::packSSH2('i', $x);

        return self::wrapPrivateKey($public, $private, 'ssh-dss', $password, $options);
    }

    /**
     * Convert a public key to the appropriate format
     */
    public static function savePublicKey(
        BigInteger $p,
        BigInteger $q,
        BigInteger $g,
        BigInteger $y
    ): string {
        if ($q->getLength() != 160) {
            throw new LengthException('SSH only supports keys with an N (length of Group Order q) of 160');
        }

        return self::wrapPublicKey(Strings::packSSH2('iiii', $p, $q, $g, $y), 'ssh-dss');
    }
}
<?php

/**
 * Raw DSA Signature Handler
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\DSA\Formats\Signature;

use phpseclib4\Crypt\Common\Formats\Signature\Raw as Progenitor;

/**
 * Raw DSA Signature Handler
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class Raw extends Progenitor
{
}
<?php

/**
 * ASN1 Signature Handler
 *
 * PHP version 8.1+
 *
 * Handles signatures in the format described in
 * https://tools.ietf.org/html/rfc3279#section-2.2.2
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\DSA\Formats\Signature;

use phpseclib4\File\ASN1 as Encoder;
use phpseclib4\File\ASN1\Maps;
use phpseclib4\Math\BigInteger;

/**
 * ASN1 Signature Handler
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class ASN1
{
    /**
     * Loads a signature
     */
    public static function load(string $sig): array
    {
        $decoded = Encoder::decodeBER($sig);
        $components = Encoder::map($decoded, Maps\DssSigValue::MAP);

        return $components->toArray();
    }

    /**
     * Returns a signature in the appropriate format
     */
    public static function save(BigInteger $r, BigInteger $s): string
    {
        return Encoder::encodeDER(compact('r', 's'), Maps\DssSigValue::MAP);
    }
}
<?php

/**
 * SSH2 Signature Handler
 *
 * PHP version 8.1+
 *
 * Handles signatures in the format used by SSH2
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\DSA\Formats\Signature;

use phpseclib4\Common\Functions\Strings;
use phpseclib4\Exception\{LengthException, UnexpectedValueException};
use phpseclib4\Math\BigInteger;

/**
 * SSH2 Signature Handler
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class SSH2
{
    /**
     * Loads a signature
     */
    public static function load(string $sig): array
    {
        $result = Strings::unpackSSH2('ss', $sig);
        [$type, $blob] = $result;
        if ($type != 'ssh-dss' || strlen($blob) != 40) {
            throw new UnexpectedValueException('Both R and S must be less than or equal to 20 bytes in length');
        }

        return [
            'r' => new BigInteger(substr($blob, 0, 20), 256),
            's' => new BigInteger(substr($blob, 20), 256),
        ];
    }

    /**
     * Returns a signature in the appropriate format
     */
    public static function save(BigInteger $r, BigInteger $s): string
    {
        if ($r->getLength() > 160 || $s->getLength() > 160) {
            throw new LengthException('Both R and S must be less than or equal to 20 bytes in length');
        }
        return Strings::packSSH2(
            'ss',
            'ssh-dss',
            str_pad($r->toBytes(), 20, "\0", STR_PAD_LEFT) .
            str_pad($s->toBytes(), 20, "\0", STR_PAD_LEFT)
        );
    }
}
<?php

/**
 * DSA Public Key
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2019-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\DSA;

use phpseclib4\Crypt\{Common, DSA};
use phpseclib4\Crypt\DSA\Formats\Signature\ASN1 as ASN1Signature;
use phpseclib4\Exception\{BadConfigurationException, UnexpectedValueException};

/**
 * DSA Public Key
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
final class PublicKey extends DSA implements Common\PublicKey
{
    use Common\Traits\Fingerprint;

    /**
     * Verify a signature
     *
     * @see self::verify()
     */
    public function verify(string $message, string|array $signature): bool
    {
        if (self::$forcedEngine === 'libsodium') {
            throw new BadConfigurationException('Engine libsodium is forced but unsupported for DSA');
        }

        if (self::$forcedEngine === 'OpenSSL' && !function_exists('openssl_get_md_methods')) {
            throw new BadConfigurationException('Engine OpenSSL is forced but unsupported for DSA');
        }

        $shortFormat = $this->shortFormat;
        $format = $this->sigFormat;

        if ($shortFormat == 'Raw') {
            if (is_string($signature)) {
                throw new UnexpectedValueException('Raw signatures must be arrays');
            }
        } elseif (is_array($signature)) {
            throw new UnexpectedValueException('The only signature format that takes in arrays is the Raw format');
        }

        try {
            ['r' => $r, 's' => $s] = $format::load($signature);
        } catch (\Exception) {
            return false;
        }

        if (function_exists('openssl_get_md_methods') && self::$forcedEngine !== 'PHP') {
            if (in_array($this->hash->getHash(), openssl_get_md_methods())) {
                $sig = $format != 'ASN1' ? ASN1Signature::save($r, $s) : $signature;

                $result = openssl_verify($message, $sig, $this->toString('PKCS8'), $this->hash->getHash());

                if ($result != -1) {
                    return (bool) $result;
                }
            } elseif (self::$forcedEngine === 'OpenSSL') {
                throw new BadConfigurationException('Engine OpenSSL is forced but unsupported for DSA / ' . $this->hash->getHash());
            }
        }

        $q_1 = $this->q->subtract(self::$one);
        if (!$r->between(self::$one, $q_1) || !$s->between(self::$one, $q_1)) {
            return false;
        }

        $w = $s->modInverse($this->q);
        $h = $this->hash->hash($message);
        $h = $this->bits2int($h);
        [, $u1] = $h->multiply($w)->divide($this->q);
        [, $u2] = $r->multiply($w)->divide($this->q);
        $v1 = $this->g->powMod($u1, $this->p);
        $v2 = $this->y->powMod($u2, $this->p);
        [, $v] = $v1->multiply($v2)->divide($this->p);
        [, $v] = $v->divide($this->q);

        return $v->equals($r);
    }

    /**
     * Returns the public key
     */
    public function toString(string $type, array $options = []): string
    {
        $type = self::validatePlugin('Keys', $type, 'savePublicKey');

        return $type::savePublicKey($this->p, $this->q, $this->g, $this->y, $options);
    }

    public function toArray(): array
    {
        return [
            'p' => clone $this->p,
            'q' => clone $this->q,
            'g' => clone $this->g,
            'y' => clone $this->y,
        ];
    }
}
<?php

/**
 * DSA Parameters
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2019-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\DSA;

use phpseclib4\Crypt\DSA;

/**
 * DSA Parameters
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
final class Parameters extends DSA
{
    /**
     * Returns the parameters
     */
    public function toString(string $type = 'PKCS1', array $options = []): string
    {
        $type = self::validatePlugin('Keys', 'PKCS1', 'saveParameters');

        return $type::saveParameters($this->p, $this->q, $this->g, $options);
    }
}
<?php

/**
 * DSA Private Key
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2019-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt\DSA;

use phpseclib4\Crypt\{Common, DSA};
use phpseclib4\Crypt\DSA\Formats\Signature\ASN1 as ASN1Signature;
use phpseclib4\Exception\{BadConfigurationException, UnsupportedValueException};
use phpseclib4\File\Common\Signable;
use phpseclib4\File\CSR;
use phpseclib4\Math\BigInteger;

/**
 * DSA Private Key
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
final class PrivateKey extends DSA implements Common\PrivateKey
{
    use Common\Traits\PasswordProtected;

    /**
     * DSA secret exponent x
     */
    protected BigInteger $x;

    /**
     * Returns the public key
     *
     * If you do "openssl rsa -in private.rsa -pubout -outform PEM" you get a PKCS8 formatted key
     * that contains a publicKeyAlgorithm AlgorithmIdentifier and a publicKey BIT STRING.
     * An AlgorithmIdentifier contains an OID and a parameters field. With RSA public keys this
     * parameters field is NULL. With DSA PKCS8 public keys it is not - it contains the p, q and g
     * variables. The publicKey BIT STRING contains, simply, the y variable. This can be verified
     * by getting a DSA PKCS8 public key:
     *
     * "openssl dsa -in private.dsa -pubout -outform PEM"
     *
     * ie. just swap out rsa with dsa in the rsa command above.
     *
     * A PKCS1 public key corresponds to the publicKey portion of the PKCS8 key. In the case of RSA
     * the publicKey portion /is/ the key. In the case of DSA it is not. You cannot verify a signature
     * without the parameters and the PKCS1 DSA public key format does not include the parameters.
     *
     * @see self::getPrivateKey()
     */
    public function getPublicKey(): PublicKey
    {
        $type = self::validatePlugin('Keys', 'PKCS8', 'savePublicKey');

        $this->y ??= $this->g->powMod($this->x, $this->p);

        $key = $type::savePublicKey($this->p, $this->q, $this->g, $this->y);

        return DSA::loadFormat('PKCS8', $key)
            ->withHash($this->hash->getHash())
            ->withSignatureFormat($this->shortFormat);
    }

    /**
     * Create a signature
     *
     * @see self::verify()
     */
    public function sign(string|Signable $source): string|array
    {
        $format = $this->sigFormat;

        if (self::$forcedEngine === 'libsodium') {
            throw new BadConfigurationException('Engine libsodium is forced but unsupported for DSA');
        }

        if (self::$forcedEngine === 'OpenSSL' && !function_exists('openssl_get_md_methods')) {
            throw new BadConfigurationException('Engine OpenSSL is forced but unsupported for DSA');
        }

        if ($source instanceof Signable) {
            $public = $this->getPublicKey();
            if ($source instanceof CSR && !$source->hasPublicKey()) {
                $source->setPublicKey($public);
            }
            $source->identifySignatureAlgorithm($this);
            $message = $source->getSignableSection();
        } else {
            $message = $source;
        }

        if (function_exists('openssl_get_md_methods') && self::$forcedEngine !== 'PHP') {
            if (in_array($this->hash->getHash(), openssl_get_md_methods())) {
                $signature = '';
                $result = openssl_sign($message, $signature, $this->withPassword()->toString('PKCS8'), $this->hash->getHash());

                if ($result) {
                    if ($this->shortFormat == 'ASN1') {
                        if ($source instanceof Signable) {
                            $source->setSignature($signature);
                        }
                        return $signature;
                    }

                    $loaded = ASN1Signature::load($signature);
                    $r = $loaded['r'];
                    $s = $loaded['s'];

                    if ($source instanceof Signable) {
                        if (is_array($signature)) {
                            throw new UnsupportedValueException('The Raw signature format cannot be used with Signable objects');
                        }
                        $source->setSignature($signature);
                    }

                    return $format::save($r, $s);
                } elseif (self::$forcedEngine === 'OpenSSL') {
                    throw new BadConfigurationException('Engine OpenSSL is forced but was unable to create signature because of ' . openssl_error_string());
                }
            } elseif (self::$forcedEngine === 'OpenSSL') {
                throw new BadConfigurationException('Engine OpenSSL is forced but unsupported for DSA / ' . $this->hash->getHash());
            }
        }

        $h = $this->hash->hash($message);
        $h = $this->bits2int($h);

        while (true) {
            $k = BigInteger::randomRange(self::$one, $this->q->subtract(self::$one));
            $r = $this->g->powMod($k, $this->p);
            [, $r] = $r->divide($this->q);
            if ($r->equals(self::$zero)) {
                continue;
            }
            $kinv = $k->modInverse($this->q);
            $temp = $h->add($this->x->multiply($r));
            $temp = $kinv->multiply($temp);
            [, $s] = $temp->divide($this->q);
            if (!$s->equals(self::$zero)) {
                break;
            }
        }

        // the following is an RFC6979 compliant implementation of deterministic DSA
        // it's unused because it's mainly intended for use when a good CSPRNG isn't
        // available. if phpseclib's CSPRNG isn't good then even key generation is
        // suspect
        /*
        $h1 = $this->hash->hash($message);
        $k = $this->computek($h1);
        $r = $this->g->powMod($k, $this->p);
        [, $r] = $r->divide($this->q);
        $kinv = $k->modInverse($this->q);
        $h1 = $this->bits2int($h1);
        $temp = $h1->add($this->x->multiply($r));
        $temp = $kinv->multiply($temp);
        [, $s] = $temp->divide($this->q);
        */

        $signature = $format::save($r, $s);
        if ($source instanceof Signable) {
            $source->setSignature($signature);
        }
        return $signature;
    }

    /**
     * Returns the private key as a string
     */
    public function toString(string $type, array $options = []): string
    {
        $type = self::validatePlugin('Keys', $type, 'savePrivateKey');

        $this->y ??= $this->g->powMod($this->x, $this->p);

        return $type::savePrivateKey($this->p, $this->q, $this->g, $this->y, $this->x, $this->password, $options);
    }

    public function toArray(): array
    {
        $this->y ??= $this->g->powMod($this->x, $this->p);

        return [
            'p' => clone $this->p,
            'q' => clone $this->q,
            'g' => clone $this->g,
            'y' => clone $this->y,
            'x' => clone $this->x,
        ];
    }
}
<?php

/**
 * Pure-PHP implementation of DES.
 *
 * Uses OpenSSL, if available/possible, and an internal implementation, otherwise
 *
 * PHP version 8.1+
 *
 * Useful resources are as follows:
 *
 *  - {@link http://en.wikipedia.org/wiki/DES_supplementary_material Wikipedia: DES supplementary material}
 *  - {@link http://www.itl.nist.gov/fipspubs/fip46-2.htm FIPS 46-2 - (DES), Data Encryption Standard}
 *  - {@link http://www.cs.eku.edu/faculty/styer/460/Encrypt/JS-DES.html JavaScript DES Example}
 *
 * Here's a short example of how to use this library:
 * <code>
 * <?php
 *    include 'vendor/autoload.php';
 *
 *    $des = new \phpseclib4\Crypt\DES('ctr');
 *
 *    $des->setKey('abcdefgh');
 *
 *    $size = 10 * 1024;
 *    $plaintext = '';
 *    for ($i = 0; $i < $size; $i++) {
 *        $plaintext.= 'a';
 *    }
 *
 *    echo $des->decrypt($des->encrypt($plaintext));
 * ?>
 * </code>
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2007-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Crypt;

use phpseclib4\Crypt\Common\BlockCipher;
use phpseclib4\Exception\LengthException;

/**
 * Pure-PHP implementation of DES.
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class DES extends BlockCipher
{
    /**
     * Contains $keys[self::ENCRYPT]
     *
     * @see \phpseclib4\Crypt\DES::setupKey()
     * @see \phpseclib4\Crypt\DES::processBlock()
     */
    public const ENCRYPT = 0;
    /**
     * Contains $keys[self::DECRYPT]
     *
     * @see \phpseclib4\Crypt\DES::setupKey()
     * @see \phpseclib4\Crypt\DES::processBlock()
     */
    public const DECRYPT = 1;

    /**
     * Block Length of the cipher
     *
     * @see Common\SymmetricKey::block_size
     */
    protected int $block_size = 8;

    /**
     * Key Length (in bytes)
     *
     * @see Common\SymmetricKey::setKeyLength()
     */
    protected int $key_length = 8;

    /**
     * Switch for DES/3DES encryption
     *
     * Used only if $engine == self::ENGINE_INTERNAL
     *
     * @see self::setupKey()
     * @see self::processBlock()
     */
    protected int $des_rounds = 1;

    /**
     * max possible size of $key
     *
     * @see self::setKey()
     */
    protected int $key_length_max = 8;

    /**
     * The Key Schedule
     *
     * @see self::setupKey()
     */
    private array $keys;

    /**
     * Key Cache "key"
     *
     * @see self::setupKey()
     */
    private array $kl;

    /**
     * Shuffle table.
     *
     * For each byte value index, the entry holds an 8-byte string
     * with each byte containing all bits in the same state as the
     * corresponding bit in the index value.
     *
     * @see self::processBlock()
     * @see self::setupKey()
     * @var string[]
     */
    protected static array $shuffle = [
        "\x00\x00\x00\x00\x00\x00\x00\x00", "\x00\x00\x00\x00\x00\x00\x00\xFF",
        "\x00\x00\x00\x00\x00\x00\xFF\x00", "\x00\x00\x00\x00\x00\x00\xFF\xFF",
        "\x00\x00\x00\x00\x00\xFF\x00\x00", "\x00\x00\x00\x00\x00\xFF\x00\xFF",
        "\x00\x00\x00\x00\x00\xFF\xFF\x00", "\x00\x00\x00\x00\x00\xFF\xFF\xFF",
        "\x00\x00\x00\x00\xFF\x00\x00\x00", "\x00\x00\x00\x00\xFF\x00\x00\xFF",
        "\x00\x00\x00\x00\xFF\x00\xFF\x00", "\x00\x00\x00\x00\xFF\x00\xFF\xFF",
        "\x00\x00\x00\x00\xFF\xFF\x00\x00", "\x00\x00\x00\x00\xFF\xFF\x00\xFF",
        "\x00\x00\x00\x00\xFF\xFF\xFF\x00", "\x00\x00\x00\x00\xFF\xFF\xFF\xFF",
        "\x00\x00\x00\xFF\x00\x00\x00\x00", "\x00\x00\x00\xFF\x00\x00\x00\xFF",
        "\x00\x00\x00\xFF\x00\x00\xFF\x00", "\x00\x00\x00\xFF\x00\x00\xFF\xFF",
        "\x00\x00\x00\xFF\x00\xFF\x00\x00", "\x00\x00\x00\xFF\x00\xFF\x00\xFF",
        "\x00\x00\x00\xFF\x00\xFF\xFF\x00", "\x00\x00\x00\xFF\x00\xFF\xFF\xFF",
        "\x00\x00\x00\xFF\xFF\x00\x00\x00", "\x00\x00\x00\xFF\xFF\x00\x00\xFF",
        "\x00\x00\x00\xFF\xFF\x00\xFF\x00", "\x00\x00\x00\xFF\xFF\x00\xFF\xFF",
        "\x00\x00\x00\xFF\xFF\xFF\x00\x00", "\x00\x00\x00\xFF\xFF\xFF\x00\xFF",
        "\x00\x00\x00\xFF\xFF\xFF\xFF\x00", "\x00\x00\x00\xFF\xFF\xFF\xFF\xFF",
        "\x00\x00\xFF\x00\x00\x00\x00\x00", "\x00\x00\xFF\x00\x00\x00\x00\xFF",
        "\x00\x00\xFF\x00\x00\x00\xFF\x00", "\x00\x00\xFF\x00\x00\x00\xFF\xFF",
        "\x00\x00\xFF\x00\x00\xFF\x00\x00", "\x00\x00\xFF\x00\x00\xFF\x00\xFF",
        "\x00\x00\xFF\x00\x00\xFF\xFF\x00", "\x00\x00\xFF\x00\x00\xFF\xFF\xFF",
        "\x00\x00\xFF\x00\xFF\x00\x00\x00", "\x00\x00\xFF\x00\xFF\x00\x00\xFF",
        "\x00\x00\xFF\x00\xFF\x00\xFF\x00", "\x00\x00\xFF\x00\xFF\x00\xFF\xFF",
        "\x00\x00\xFF\x00\xFF\xFF\x00\x00", "\x00\x00\xFF\x00\xFF\xFF\x00\xFF",
        "\x00\x00\xFF\x00\xFF\xFF\xFF\x00", "\x00\x00\xFF\x00\xFF\xFF\xFF\xFF",
        "\x00\x00\xFF\xFF\x00\x00\x00\x00", "\x00\x00\xFF\xFF\x00\x00\x00\xFF",
        "\x00\x00\xFF\xFF\x00\x00\xFF\x00", "\x00\x00\xFF\xFF\x00\x00\xFF\xFF",
        "\x00\x00\xFF\xFF\x00\xFF\x00\x00", "\x00\x00\xFF\xFF\x00\xFF\x00\xFF",
        "\x00\x00\xFF\xFF\x00\xFF\xFF\x00", "\x00\x00\xFF\xFF\x00\xFF\xFF\xFF",
        "\x00\x00\xFF\xFF\xFF\x00\x00\x00", "\x00\x00\xFF\xFF\xFF\x00\x00\xFF",
        "\x00\x00\xFF\xFF\xFF\x00\xFF\x00", "\x00\x00\xFF\xFF\xFF\x00\xFF\xFF",
        "\x00\x00\xFF\xFF\xFF\xFF\x00\x00", "\x00\x00\xFF\xFF\xFF\xFF\x00\xFF",
        "\x00\x00\xFF\xFF\xFF\xFF\xFF\x00", "\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF",
        "\x00\xFF\x00\x00\x00\x00\x00\x00", "\x00\xFF\x00\x00\x00\x00\x00\xFF",
        "\x00\xFF\x00\x00\x00\x00\xFF\x00", "\x00\xFF\x00\x00\x00\x00\xFF\xFF",
        "\x00\xFF\x00\x00\x00\xFF\x00\x00", "\x00\xFF\x00\x00\x00\xFF\x00\xFF",
        "\x00\xFF\x00\x00\x00\xFF\xFF\x00", "\x00\xFF\x00\x00\x00\xFF\xFF\xFF",
        "\x00\xFF\x00\x00\xFF\x00\x00\x00", "\x00\xFF\x00\x00\xFF\x00\x00\xFF",
        "\x00\xFF\x00\x00\xFF\x00\xFF\x00", "\x00\xFF\x00\x00\xFF\x00\xFF\xFF",
        "\x00\xFF\x00\x00\xFF\xFF\x00\x00", "\x00\xFF\x00\x00\xFF\xFF\x00\xFF",
        "\x00\xFF\x00\x00\xFF\xFF\xFF\x00", "\x00\xFF\x00\x00\xFF\xFF\xFF\xFF",
        "\x00\xFF\x00\xFF\x00\x00\x00\x00", "\x00\xFF\x00\xFF\x00\x00\x00\xFF",
        "\x00\xFF\x00\xFF\x00\x00\xFF\x00", "\x00\xFF\x00\xFF\x00\x00\xFF\xFF",
        "\x00\xFF\x00\xFF\x00\xFF\x00\x00", "\x00\xFF\x00\xFF\x00\xFF\x00\xFF",
        "\x00\xFF\x00\xFF\x00\xFF\xFF\x00", "\x00\xFF\x00\xFF\x00\xFF\xFF\xFF",
        "\x00\xFF\x00\xFF\xFF\x00\x00\x00", "\x00\xFF\x00\xFF\xFF\x00\x00\xFF",
        "\x00\xFF\x00\xFF\xFF\x00\xFF\x00", "\x00\xFF\x00\xFF\xFF\x00\xFF\xFF",
        "\x00\xFF\x00\xFF\xFF\xFF\x00\x00", "\x00\xFF\x00\xFF\xFF\xFF\x00\xFF",
        "\x00\xFF\x00\xFF\xFF\xFF\xFF\x00", "\x00\xFF\x00\xFF\xFF\xFF\xFF\xFF",
        "\x00\xFF\xFF\x00\x00\x00\x00\x00", "\x00\xFF\xFF\x00\x00\x00\x00\xFF",
        "\x00\xFF\xFF\x00\x00\x00\xFF\x00", "\x00\xFF\xFF\x00\x00\x00\xFF\xFF",
        "\x00\xFF\xFF\x00\x00\xFF\x00\x00", "\x00\xFF\xFF\x00\x00\xFF\x00\xFF",
        "\x00\xFF\xFF\x00\x00\xFF\xFF\x00", "\x00\xFF\xFF\x00\x00\xFF\xFF\xFF",
        "\x00\xFF\xFF\x00\xFF\x00\x00\x00", "\x00\xFF\xFF\x00\xFF\x00\x00\xFF",
        "\x00\xFF\xFF\x00\xFF\x00\xFF\x00", "\x00\xFF\xFF\x00\xFF\x00\xFF\xFF",
        "\x00\xFF\xFF\x00\xFF\xFF\x00\x00", "\x00\xFF\xFF\x00\xFF\xFF\x00\xFF",
        "\x00\xFF\xFF\x00\xFF\xFF\xFF\x00", "\x00\xFF\xFF\x00\xFF\xFF\xFF\xFF",
        "\x00\xFF\xFF\xFF\x00\x00\x00\x00", "\x00\xFF\xFF\xFF\x00\x00\x00\xFF",
        "\x00\xFF\xFF\xFF\x00\x00\xFF\x00", "\x00\xFF\xFF\xFF\x00\x00\xFF\xFF",
        "\x00\xFF\xFF\xFF\x00\xFF\x00\x00", "\x00\xFF\xFF\xFF\x00\xFF\x00\xFF",
        "\x00\xFF\xFF\xFF\x00\xFF\xFF\x00", "\x00\xFF\xFF\xFF\x00\xFF\xFF\xFF",
        "\x00\xFF\xFF\xFF\xFF\x00\x00\x00", "\x00\xFF\xFF\xFF\xFF\x00\x00\xFF",
        "\x00\xFF\xFF\xFF\xFF\x00\xFF\x00", "\x00\xFF\xFF\xFF\xFF\x00\xFF\xFF",
        "\x00\xFF\xFF\xFF\xFF\xFF\x00\x00", "\x00\xFF\xFF\xFF\xFF\xFF\x00\xFF",
        "\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00", "\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF",
        "\xFF\x00\x00\x00\x00\x00\x00\x00", "\xFF\x00\x00\x00\x00\x00\x00\xFF",
        "\xFF\x00\x00\x00\x00\x00\xFF\x00", "\xFF\x00\x00\x00\x00\x00\xFF\xFF",
        "\xFF\x00\x00\x00\x00\xFF\x00\x00", "\xFF\x00\x00\x00\x00\xFF\x00\xFF",
        "\xFF\x00\x00\x00\x00\xFF\xFF\x00", "\xFF\x00\x00\x00\x00\xFF\xFF\xFF",
        "\xFF\x00\x00\x00\xFF\x00\x00\x00", "\xFF\x00\x00\x00\xFF\x00\x00\xFF",
        "\xFF\x00\x00\x00\xFF\x00\xFF\x00", "\xFF\x00\x00\x00\xFF\x00\xFF\xFF",
        "\xFF\x00\x00\x00\xFF\xFF\x00\x00", "\xFF\x00\x00\x00\xFF\xFF\x00\xFF",
        "\xFF\x00\x00\x00\xFF\xFF\xFF\x00", "\xFF\x00\x00\x00\xFF\xFF\xFF\xFF",
        "\xFF\x00\x00\xFF\x00\x00\x00\x00", "\xFF\x00\x00\xFF\x00\x00\x00\xFF",
        "\xFF\x00\x00\xFF\x00\x00\xFF\x00", "\xFF\x00\x00\xFF\x00\x00\xFF\xFF",
        "\xFF\x00\x00\xFF\x00\xFF\x00\x00", "\xFF\x00\x00\xFF\x00\xFF\x00\xFF",
        "\xFF\x00\x00\xFF\x00\xFF\xFF\x00", "\xFF\x00\x00\xFF\x00\xFF\xFF\xFF",
        "\xFF\x00\x00\xFF\xFF\x00\x00\x00", "\xFF\x00\x00\xFF\xFF\x00\x00\xFF",
        "\xFF\x00\x00\xFF\xFF\x00\xFF\x00", "\xFF\x00\x00\xFF\xFF\x00\xFF\xFF",
        "\xFF\x00\x00\xFF\xFF\xFF\x00\x00", "\xFF\x00\x00\xFF\xFF\xFF\x00\xFF",
        "\xFF\x00\x00\xFF\xFF\xFF\xFF\x00", "\xFF\x00\x00\xFF\xFF\xFF\xFF\xFF",
        "\xFF\x00\xFF\x00\x00\x00\x00\x00", "\xFF\x00\xFF\x00\x00\x00\x00\xFF",
        "\xFF\x00\xFF\x00\x00\x00\xFF\x00", "\xFF\x00\xFF\x00\x00\x00\xFF\xFF",
        "\xFF\x00\xFF\x00\x00\xFF\x00\x00", "\xFF\x00\xFF\x00\x00\xFF\x00\xFF",
        "\xFF\x00\xFF\x00\x00\xFF\xFF\x00", "\xFF\x00\xFF\x00\x00\xFF\xFF\xFF",
        "\xFF\x00\xFF\x00\xFF\x00\x00\x00", "\xFF\x00\xFF\x00\xFF\x00\x00\xFF",
        "\xFF\x00\xFF\x00\xFF\x00\xFF\x00", "\xFF\x00\xFF\x00\xFF\x00\xFF\xFF",
        "\xFF\x00\xFF\x00\xFF\xFF\x00\x00", "\xFF\x00\xFF\x00\xFF\xFF\x00\xFF",
        "\xFF\x00\xFF\x00\xFF\xFF\xFF\x00", "\xFF\x00\xFF\x00\xFF\xFF\xFF\xFF",
        "\xFF\x00\xFF\xFF\x00\x00\x00\x00", "\xFF\x00\xFF\xFF\x00\x00\x00\xFF",
        "\xFF\x00\xFF\xFF\x00\x00\xFF\x00", "\xFF\x00\xFF\xFF\x00\x00\xFF\xFF",
        "\xFF\x00\xFF\xFF\x00\xFF\x00\x00", "\xFF\x00\xFF\xFF\x00\xFF\x00\xFF",
        "\xFF\x00\xFF\xFF\x00\xFF\xFF\x00", "\xFF\x00\xFF\xFF\x00\xFF\xFF\xFF",
        "\xFF\x00\xFF\xFF\xFF\x00\x00\x00", "\xFF\x00\xFF\xFF\xFF\x00\x00\xFF",
        "\xFF\x00\xFF\xFF\xFF\x00\xFF\x00", "\xFF\x00\xFF\xFF\xFF\x00\xFF\xFF",
        "\xFF\x00\xFF\xFF\xFF\xFF\x00\x00", "\xFF\x00\xFF\xFF\xFF\xFF\x00\xFF",
        "\xFF\x00\xFF\xFF\xFF\xFF\xFF\x00", "\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF",
        "\xFF\xFF\x00\x00\x00\x00\x00\x00", "\xFF\xFF\x00\x00\x00\x00\x00\xFF",
        "\xFF\xFF\x00\x00\x00\x00\xFF\x00", "\xFF\xFF\x00\x00\x00\x00\xFF\xFF",
        "\xFF\xFF\x00\x00\x00\xFF\x00\x00", "\xFF\xFF\x00\x00\x00\xFF\x00\xFF",
        "\xFF\xFF\x00\x00\x00\xFF\xFF\x00", "\xFF\xFF\x00\x00\x00\xFF\xFF\xFF",
        "\xFF\xFF\x00\x00\xFF\x00\x00\x00", "\xFF\xFF\x00\x00\xFF\x00\x00\xFF",
        "\xFF\xFF\x00\x00\xFF\x00\xFF\x00", "\xFF\xFF\x00\x00\xFF\x00\xFF\xFF",
        "\xFF\xFF\x00\x00\xFF\xFF\x00\x00", "\xFF\xFF\x00\x00\xFF\xFF\x00\xFF",
        "\xFF\xFF\x00\x00\xFF\xFF\xFF\x00", "\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF",
        "\xFF\xFF\x00\xFF\x00\x00\x00\x00", "\xFF\xFF\x00\xFF\x00\x00\x00\xFF",
        "\xFF\xFF\x00\xFF\x00\x00\xFF\x00", "\xFF\xFF\x00\xFF\x00\x00\xFF\xFF",
        "\xFF\xFF\x00\xFF\x00\xFF\x00\x00", "\xFF\xFF\x00\xFF\x00\xFF\x00\xFF",
        "\xFF\xFF\x00\xFF\x00\xFF\xFF\x00", "\xFF\xFF\x00\xFF\x00\xFF\xFF\xFF",
        "\xFF\xFF\x00\xFF\xFF\x00\x00\x00", "\xFF\xFF\x00\xFF\xFF\x00\x00\xFF",
        "\xFF\xFF\x00\xFF\xFF\x00\xFF\x00", "\xFF\xFF\x00\xFF\xFF\x00\xFF\xFF",
        "\xFF\xFF\x00\xFF\xFF\xFF\x00\x00", "\xFF\xFF\x00\xFF\xFF\xFF\x00\xFF",
        "\xFF\xFF\x00\xFF\xFF\xFF\xFF\x00", "\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF",
        "\xFF\xFF\xFF\x00\x00\x00\x00\x00", "\xFF\xFF\xFF\x00\x00\x00\x00\xFF",
        "\xFF\xFF\xFF\x00\x00\x00\xFF\x00", "\xFF\xFF\xFF\x00\x00\x00\xFF\xFF",
        "\xFF\xFF\xFF\x00\x00\xFF\x00\x00", "\xFF\xFF\xFF\x00\x00\xFF\x00\xFF",
        "\xFF\xFF\xFF\x00\x00\xFF\xFF\x00", "\xFF\xFF\xFF\x00\x00\xFF\xFF\xFF",
        "\xFF\xFF\xFF\x00\xFF\x00\x00\x00", "\xFF\xFF\xFF\x00\xFF\x00\x00\xFF",
        "\xFF\xFF\xFF\x00\xFF\x00\xFF\x00", "\xFF\xFF\xFF\x00\xFF\x00\xFF\xFF",
        "\xFF\xFF\xFF\x00\xFF\xFF\x00\x00", "\xFF\xFF\xFF\x00\xFF\xFF\x00\xFF",
        "\xFF\xFF\xFF\x00\xFF\xFF\xFF\x00", "\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF",
        "\xFF\xFF\xFF\xFF\x00\x00\x00\x00", "\xFF\xFF\xFF\xFF\x00\x00\x00\xFF",
        "\xFF\xFF\xFF\xFF\x00\x00\xFF\x00", "\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF",
        "\xFF\xFF\xFF\xFF\x00\xFF\x00\x00", "\xFF\xFF\xFF\xFF\x00\xFF\x00\xFF",
        "\xFF\xFF\xFF\xFF\x00\xFF\xFF\x00", "\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF",
        "\xFF\xFF\xFF\xFF\xFF\x00\x00\x00", "\xFF\xFF\xFF\xFF\xFF\x00\x00\xFF",
        "\xFF\xFF\xFF\xFF\xFF\x00\xFF\x00", "\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF",
        "\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00", "\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF",
        "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00", "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF",
    ];

    /**
     * IP mapping helper table.
     *
     * Indexing this table with each source byte performs the initial bit permutation.
     *
     * @var int[]
     */
    protected static array $ipmap = [
        0x00, 0x10, 0x01, 0x11, 0x20, 0x30, 0x21, 0x31,
        0x02, 0x12, 0x03, 0x13, 0x22, 0x32, 0x23, 0x33,
        0x40, 0x50, 0x41, 0x51, 0x60, 0x70, 0x61, 0x71,
        0x42, 0x52, 0x43, 0x53, 0x62, 0x72, 0x63, 0x73,
        0x04, 0x14, 0x05, 0x15, 0x24, 0x34, 0x25, 0x35,
        0x06, 0x16, 0x07, 0x17, 0x26, 0x36, 0x27, 0x37,
        0x44, 0x54, 0x45, 0x55, 0x64, 0x74, 0x65, 0x75,
        0x46, 0x56, 0x47, 0x57, 0x66, 0x76, 0x67, 0x77,
        0x80, 0x90, 0x81, 0x91, 0xA0, 0xB0, 0xA1, 0xB1,
        0x82, 0x92, 0x83, 0x93, 0xA2, 0xB2, 0xA3, 0xB3,
        0xC0, 0xD0, 0xC1, 0xD1, 0xE0, 0xF0, 0xE1, 0xF1,
        0xC2, 0xD2, 0xC3, 0xD3, 0xE2, 0xF2, 0xE3, 0xF3,
        0x84, 0x94, 0x85, 0x95, 0xA4, 0xB4, 0xA5, 0xB5,
        0x86, 0x96, 0x87, 0x97, 0xA6, 0xB6, 0xA7, 0xB7,
        0xC4, 0xD4, 0xC5, 0xD5, 0xE4, 0xF4, 0xE5, 0xF5,
        0xC6, 0xD6, 0xC7, 0xD7, 0xE6, 0xF6, 0xE7, 0xF7,
        0x08, 0x18, 0x09, 0x19, 0x28, 0x38, 0x29, 0x39,
        0x0A, 0x1A, 0x0B, 0x1B, 0x2A, 0x3A, 0x2B, 0x3B,
        0x48, 0x58, 0x49, 0x59, 0x68, 0x78, 0x69, 0x79,
        0x4A, 0x5A, 0x4B, 0x5B, 0x6A, 0x7A, 0x6B, 0x7B,
        0x0C, 0x1C, 0x0D, 0x1D, 0x2C, 0x3C, 0x2D, 0x3D,
        0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F,
        0x4C, 0x5C, 0x4D, 0x5D, 0x6C, 0x7C, 0x6D, 0x7D,
        0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F,
        0x88, 0x98, 0x89, 0x99, 0xA8, 0xB8, 0xA9, 0xB9,
        0x8A, 0x9A, 0x8B, 0x9B, 0xAA, 0xBA, 0xAB, 0xBB,
        0xC8, 0xD8, 0xC9, 0xD9, 0xE8, 0xF8, 0xE9, 0xF9,
        0xCA, 0xDA, 0xCB, 0xDB, 0xEA, 0xFA, 0xEB, 0xFB,
        0x8C, 0x9C, 0x8D, 0x9D, 0xAC, 0xBC, 0xAD, 0xBD,
        0x8E, 0x9E, 0x8F, 0x9F, 0xAE, 0xBE, 0xAF, 0xBF,
        0xCC, 0xDC, 0xCD, 0xDD, 0xEC, 0xFC, 0xED, 0xFD,
        0xCE, 0xDE, 0xCF, 0xDF, 0xEE, 0xFE, 0xEF, 0xFF,
    ];

    /**
     * Inverse IP mapping helper table.
     * Indexing this table with a byte value reverses the bit order.
     *
     * @var int[]
     */
    protected static array $invipmap = [
        0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0,
        0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
        0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
        0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
        0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4,
        0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
        0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC,
        0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
        0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
        0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
        0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA,
        0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
        0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6,
        0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
        0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,
        0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
        0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1,
        0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
        0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9,
        0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
        0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5,
        0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
        0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED,
        0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
        0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3,
        0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
        0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
        0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
        0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7,
        0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
        0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF,
        0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF,
    ];

    /**
     * Pre-permuted S-box1
     *
     * Each box ($sbox1-$sbox8) has been vectorized, then each value pre-permuted using the
     * P table: concatenation can then be replaced by exclusive ORs.
     *
     * @var int[]
     */
    protected static array $sbox1 = [
        0x00808200, 0x00000000, 0x00008000, 0x00808202,
        0x00808002, 0x00008202, 0x00000002, 0x00008000,
        0x00000200, 0x00808200, 0x00808202, 0x00000200,
        0x00800202, 0x00808002, 0x00800000, 0x00000002,
        0x00000202, 0x00800200, 0x00800200, 0x00008200,
        0x00008200, 0x00808000, 0x00808000, 0x00800202,
        0x00008002, 0x00800002, 0x00800002, 0x00008002,
        0x00000000, 0x00000202, 0x00008202, 0x00800000,
        0x00008000, 0x00808202, 0x00000002, 0x00808000,
        0x00808200, 0x00800000, 0x00800000, 0x00000200,
        0x00808002, 0x00008000, 0x00008200, 0x00800002,
        0x00000200, 0x00000002, 0x00800202, 0x00008202,
        0x00808202, 0x00008002, 0x00808000, 0x00800202,
        0x00800002, 0x00000202, 0x00008202, 0x00808200,
        0x00000202, 0x00800200, 0x00800200, 0x00000000,
        0x00008002, 0x00008200, 0x00000000, 0x00808002,
    ];

    /**
     * Pre-permuted S-box2
     *
     * @var int[]
     */
    protected static array $sbox2 = [
        0x40084010, 0x40004000, 0x00004000, 0x00084010,
        0x00080000, 0x00000010, 0x40080010, 0x40004010,
        0x40000010, 0x40084010, 0x40084000, 0x40000000,
        0x40004000, 0x00080000, 0x00000010, 0x40080010,
        0x00084000, 0x00080010, 0x40004010, 0x00000000,
        0x40000000, 0x00004000, 0x00084010, 0x40080000,
        0x00080010, 0x40000010, 0x00000000, 0x00084000,
        0x00004010, 0x40084000, 0x40080000, 0x00004010,
        0x00000000, 0x00084010, 0x40080010, 0x00080000,
        0x40004010, 0x40080000, 0x40084000, 0x00004000,
        0x40080000, 0x40004000, 0x00000010, 0x40084010,
        0x00084010, 0x00000010, 0x00004000, 0x40000000,
        0x00004010, 0x40084000, 0x00080000, 0x40000010,
        0x00080010, 0x40004010, 0x40000010, 0x00080010,
        0x00084000, 0x00000000, 0x40004000, 0x00004010,
        0x40000000, 0x40080010, 0x40084010, 0x00084000,
    ];

    /**
     * Pre-permuted S-box3
     *
     * @var int[]
     */
    protected static array $sbox3 = [
        0x00000104, 0x04010100, 0x00000000, 0x04010004,
        0x04000100, 0x00000000, 0x00010104, 0x04000100,
        0x00010004, 0x04000004, 0x04000004, 0x00010000,
        0x04010104, 0x00010004, 0x04010000, 0x00000104,
        0x04000000, 0x00000004, 0x04010100, 0x00000100,
        0x00010100, 0x04010000, 0x04010004, 0x00010104,
        0x04000104, 0x00010100, 0x00010000, 0x04000104,
        0x00000004, 0x04010104, 0x00000100, 0x04000000,
        0x04010100, 0x04000000, 0x00010004, 0x00000104,
        0x00010000, 0x04010100, 0x04000100, 0x00000000,
        0x00000100, 0x00010004, 0x04010104, 0x04000100,
        0x04000004, 0x00000100, 0x00000000, 0x04010004,
        0x04000104, 0x00010000, 0x04000000, 0x04010104,
        0x00000004, 0x00010104, 0x00010100, 0x04000004,
        0x04010000, 0x04000104, 0x00000104, 0x04010000,
        0x00010104, 0x00000004, 0x04010004, 0x00010100,
    ];

    /**
     * Pre-permuted S-box4
     *
     * @var int[]
     */
    protected static array $sbox4 = [
        0x80401000, 0x80001040, 0x80001040, 0x00000040,
        0x00401040, 0x80400040, 0x80400000, 0x80001000,
        0x00000000, 0x00401000, 0x00401000, 0x80401040,
        0x80000040, 0x00000000, 0x00400040, 0x80400000,
        0x80000000, 0x00001000, 0x00400000, 0x80401000,
        0x00000040, 0x00400000, 0x80001000, 0x00001040,
        0x80400040, 0x80000000, 0x00001040, 0x00400040,
        0x00001000, 0x00401040, 0x80401040, 0x80000040,
        0x00400040, 0x80400000, 0x00401000, 0x80401040,
        0x80000040, 0x00000000, 0x00000000, 0x00401000,
        0x00001040, 0x00400040, 0x80400040, 0x80000000,
        0x80401000, 0x80001040, 0x80001040, 0x00000040,
        0x80401040, 0x80000040, 0x80000000, 0x00001000,
        0x80400000, 0x80001000, 0x00401040, 0x80400040,
        0x80001000, 0x00001040, 0x00400000, 0x80401000,
        0x00000040, 0x00400000, 0x00001000, 0x00401040,
    ];

    /**
     * Pre-permuted S-box5
     *
     * @var array
     */
    protected static $sbox5 = [
        0x00000080, 0x01040080, 0x01040000, 0x21000080,
        0x00040000, 0x00000080, 0x20000000, 0x01040000,
        0x20040080, 0x00040000, 0x01000080, 0x20040080,
        0x21000080, 0x21040000, 0x00040080, 0x20000000,
        0x01000000, 0x20040000, 0x20040000, 0x00000000,
        0x20000080, 0x21040080, 0x21040080, 0x01000080,
        0x21040000, 0x20000080, 0x00000000, 0x21000000,
        0x01040080, 0x01000000, 0x21000000, 0x00040080,
        0x00040000, 0x21000080, 0x00000080, 0x01000000,
        0x20000000, 0x01040000, 0x21000080, 0x20040080,
        0x01000080, 0x20000000, 0x21040000, 0x01040080,
        0x20040080, 0x00000080, 0x01000000, 0x21040000,
        0x21040080, 0x00040080, 0x21000000, 0x21040080,
        0x01040000, 0x00000000, 0x20040000, 0x21000000,
        0x00040080, 0x01000080, 0x20000080, 0x00040000,
        0x00000000, 0x20040000, 0x01040080, 0x20000080,
    ];

    /**
     * Pre-permuted S-box6
     *
     * @var int[]
     */
    protected static array $sbox6 = [
        0x10000008, 0x10200000, 0x00002000, 0x10202008,
        0x10200000, 0x00000008, 0x10202008, 0x00200000,
        0x10002000, 0x00202008, 0x00200000, 0x10000008,
        0x00200008, 0x10002000, 0x10000000, 0x00002008,
        0x00000000, 0x00200008, 0x10002008, 0x00002000,
        0x00202000, 0x10002008, 0x00000008, 0x10200008,
        0x10200008, 0x00000000, 0x00202008, 0x10202000,
        0x00002008, 0x00202000, 0x10202000, 0x10000000,
        0x10002000, 0x00000008, 0x10200008, 0x00202000,
        0x10202008, 0x00200000, 0x00002008, 0x10000008,
        0x00200000, 0x10002000, 0x10000000, 0x00002008,
        0x10000008, 0x10202008, 0x00202000, 0x10200000,
        0x00202008, 0x10202000, 0x00000000, 0x10200008,
        0x00000008, 0x00002000, 0x10200000, 0x00202008,
        0x00002000, 0x00200008, 0x10002008, 0x00000000,
        0x10202000, 0x10000000, 0x00200008, 0x10002008,
    ];

    /**
     * Pre-permuted S-box7
     *
     * @var int[]
     */
    protected static array $sbox7 = [
        0x00100000, 0x02100001, 0x02000401, 0x00000000,
        0x00000400, 0x02000401, 0x00100401, 0x02100400,
        0x02100401, 0x00100000, 0x00000000, 0x02000001,
        0x00000001, 0x02000000, 0x02100001, 0x00000401,
        0x02000400, 0x00100401, 0x00100001, 0x02000400,
        0x02000001, 0x02100000, 0x02100400, 0x00100001,
        0x02100000, 0x00000400, 0x00000401, 0x02100401,
        0x00100400, 0x00000001, 0x02000000, 0x00100400,
        0x02000000, 0x00100400, 0x00100000, 0x02000401,
        0x02000401, 0x02100001, 0x02100001, 0x00000001,
        0x00100001, 0x02000000, 0x02000400, 0x00100000,
        0x02100400, 0x00000401, 0x00100401, 0x02100400,
        0x00000401, 0x02000001, 0x02100401, 0x02100000,
        0x00100400, 0x00000000, 0x00000001, 0x02100401,
        0x00000000, 0x00100401, 0x02100000, 0x00000400,
        0x02000001, 0x02000400, 0x00000400, 0x00100001,
    ];

    /**
     * Pre-permuted S-box8
     *
     * @var int[]
     */
    protected static array $sbox8 = [
        0x08000820, 0x00000800, 0x00020000, 0x08020820,
        0x08000000, 0x08000820, 0x00000020, 0x08000000,
        0x00020020, 0x08020000, 0x08020820, 0x00020800,
        0x08020800, 0x00020820, 0x00000800, 0x00000020,
        0x08020000, 0x08000020, 0x08000800, 0x00000820,
        0x00020800, 0x00020020, 0x08020020, 0x08020800,
        0x00000820, 0x00000000, 0x00000000, 0x08020020,
        0x08000020, 0x08000800, 0x00020820, 0x00020000,
        0x00020820, 0x00020000, 0x08020800, 0x00000800,
        0x00000020, 0x08020020, 0x00000800, 0x00020820,
        0x08000800, 0x00000020, 0x08000020, 0x08020000,
        0x08020020, 0x08000000, 0x00020000, 0x08000820,
        0x00000000, 0x08020820, 0x00020020, 0x08000020,
        0x08020000, 0x08000800, 0x08000820, 0x00000000,
        0x08020820, 0x00020800, 0x00020800, 0x00000820,
        0x00000820, 0x00020020, 0x08000000, 0x08020800,
    ];

    /**
     * Test for engine validity
     *
     * This is mainly just a wrapper to set things up for \phpseclib4\Crypt\Common\SymmetricKey::isValidEngine()
     *
     * @see Common\SymmetricKey::isValidEngine()
     */
    protected function isValidEngineHelper(int $engine): bool
    {
        if ($this->key_length_max == 8) {
            if ($engine == self::ENGINE_OPENSSL) {
                // quoting https://www.openssl.org/news/openssl-3.0-notes.html, OpenSSL 3.0.1
                // "Moved all variations of the EVP ciphers CAST5, BF, IDEA, SEED, RC2, RC4, RC5, and DES to the legacy provider"
                // in theory openssl_get_cipher_methods() should catch this but, on GitHub Actions, at least, it does not
                if (defined('OPENSSL_VERSION_NUMBER') && OPENSSL_VERSION_NUMBER >= 0x30000010) {
                    return false;
                }
                $this->cipher_name_openssl_ecb = 'des-ecb';
                $this->cipher_name_openssl = 'des-' . $this->openssl_translate_mode();
            }
        }

        return parent::isValidEngineHelper($engine);
    }

    /**
     * Sets the key.
     *
     * Keys must be 64-bits long or 8 bytes long.
     *
     * DES also requires that every eighth bit be a parity bit, however, we'll ignore that.
     *
     * @see Common\SymmetricKey::setKey()
     */
    public function setKey(#[\SensitiveParameter] string $key): void
    {
        if (!($this instanceof TripleDES) && strlen($key) != 8) {
            throw new LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of size 8 are supported');
        }

        // Sets the key
        parent::setKey($key);
    }

    /**
     * Encrypts a block
     *
     * @see self::encrypt()
     * @see Common\SymmetricKey::encryptBlock()
     * @see Common\SymmetricKey::encrypt()
     */
    protected function encryptBlock(string $in): string
    {
        return $this->processBlock($in, self::ENCRYPT);
    }

    /**
     * Decrypts a block
     *
     * @see self::decrypt()
     * @see Common\SymmetricKey::decryptBlock()
     * @see Common\SymmetricKey::decrypt()
     */
    protected function decryptBlock(string $in): string
    {
        return $this->processBlock($in, self::DECRYPT);
    }

    /**
     * Encrypts or decrypts a 64-bit block
     *
     * $mode should be either self::ENCRYPT or self::DECRYPT.  See
     * {@link http://en.wikipedia.org/wiki/Image:Feistel.png Feistel.png} to get a general
     * idea of what this function does.
     *
     * @see self::decryptBlock()
     * @see self::encryptBlock()
     */
    private function processBlock(string $block, int $mode): string
    {
        static $sbox1, $sbox2, $sbox3, $sbox4, $sbox5, $sbox6, $sbox7, $sbox8, $shuffleip, $shuffleinvip;
        if (!$sbox1) {
            $sbox1 = array_map(self::safe_intval(...), self::$sbox1);
            $sbox2 = array_map(self::safe_intval(...), self::$sbox2);
            $sbox3 = array_map(self::safe_intval(...), self::$sbox3);
            $sbox4 = array_map(self::safe_intval(...), self::$sbox4);
            $sbox5 = array_map(self::safe_intval(...), self::$sbox5);
            $sbox6 = array_map(self::safe_intval(...), self::$sbox6);
            $sbox7 = array_map(self::safe_intval(...), self::$sbox7);
            $sbox8 = array_map(self::safe_intval(...), self::$sbox8);
            /* Merge $shuffle with $[inv]ipmap */
            for ($i = 0; $i < 256; ++$i) {
                $shuffleip[]    =  self::$shuffle[self::$ipmap[$i]];
                $shuffleinvip[] =  self::$shuffle[self::$invipmap[$i]];
            }
        }

        $keys  = $this->keys[$mode];
        $ki    = -1;

        // Do the initial IP permutation.
        $t = unpack('Nl/Nr', $block);
        [$l, $r] = [$t['l'], $t['r']];
        $block = ($shuffleip[ $r        & 0xFF] & "\x80\x80\x80\x80\x80\x80\x80\x80") |
                 ($shuffleip[($r >>  8) & 0xFF] & "\x40\x40\x40\x40\x40\x40\x40\x40") |
                 ($shuffleip[($r >> 16) & 0xFF] & "\x20\x20\x20\x20\x20\x20\x20\x20") |
                 ($shuffleip[($r >> 24) & 0xFF] & "\x10\x10\x10\x10\x10\x10\x10\x10") |
                 ($shuffleip[ $l        & 0xFF] & "\x08\x08\x08\x08\x08\x08\x08\x08") |
                 ($shuffleip[($l >>  8) & 0xFF] & "\x04\x04\x04\x04\x04\x04\x04\x04") |
                 ($shuffleip[($l >> 16) & 0xFF] & "\x02\x02\x02\x02\x02\x02\x02\x02") |
                 ($shuffleip[($l >> 24) & 0xFF] & "\x01\x01\x01\x01\x01\x01\x01\x01");

        // Extract L0 and R0.
        $t = unpack('Nl/Nr', $block);
        [$l, $r] = [$t['l'], $t['r']];

        for ($des_round = 0; $des_round < $this->des_rounds; ++$des_round) {
            // Perform the 16 steps.
            for ($i = 0; $i < 16; $i++) {
                // start of "the Feistel (F) function" - see the following URL:
                // http://en.wikipedia.org/wiki/Image:Data_Encryption_Standard_InfoBox_Diagram.png
                // Merge key schedule.
                $b1 = (($r >>  3) & 0x1FFFFFFF) ^ ($r << 29) ^ $keys[++$ki];
                $b2 = (($r >> 31) & 0x00000001) ^ ($r <<  1) ^ $keys[++$ki];

                // S-box indexing.
                $t = $sbox1[($b1 >> 24) & 0x3F] ^ $sbox2[($b2 >> 24) & 0x3F] ^
                     $sbox3[($b1 >> 16) & 0x3F] ^ $sbox4[($b2 >> 16) & 0x3F] ^
                     $sbox5[($b1 >>  8) & 0x3F] ^ $sbox6[($b2 >>  8) & 0x3F] ^
                     $sbox7[ $b1        & 0x3F] ^ $sbox8[ $b2        & 0x3F] ^ $l;
                // end of "the Feistel (F) function"

                $l = $r;
                $r = $t;
            }

            // Last step should not permute L & R.
            $t = $l;
            $l = $r;
            $r = $t;
        }

        // Perform the inverse IP permutation.
        return ($shuffleinvip[($r >> 24) & 0xFF] & "\x80\x80\x80\x80\x80\x80\x80\x80") |
               ($shuffleinvip[($l >> 24) & 0xFF] & "\x40\x40\x40\x40\x40\x40\x40\x40") |
               ($shuffleinvip[($r >> 16) & 0xFF] & "\x20\x20\x20\x20\x20\x20\x20\x20") |
               ($shuffleinvip[($l >> 16) & 0xFF] & "\x10\x10\x10\x10\x10\x10\x10\x10") |
               ($shuffleinvip[($r >>  8) & 0xFF] & "\x08\x08\x08\x08\x08\x08\x08\x08") |
               ($shuffleinvip[($l >>  8) & 0xFF] & "\x04\x04\x04\x04\x04\x04\x04\x04") |
               ($shuffleinvip[ $r        & 0xFF] & "\x02\x02\x02\x02\x02\x02\x02\x02") |
               ($shuffleinvip[ $l        & 0xFF] & "\x01\x01\x01\x01\x01\x01\x01\x01");
    }

    /**
     * Creates the key schedule
     *
     * @see Common\SymmetricKey::setupKey()
     */
    protected function setupKey(): void
    {
        if (isset($this->kl['key']) && $this->key === $this->kl['key'] && $this->des_rounds === $this->kl['des_rounds']) {
            // already expanded
            return;
        }
        $this->kl = ['key' => $this->key, 'des_rounds' => $this->des_rounds];

        static $shifts = [ // number of key bits shifted per round
            1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1,
        ];

        static $pc1map = [
            0x00, 0x00, 0x08, 0x08, 0x04, 0x04, 0x0C, 0x0C,
            0x02, 0x02, 0x0A, 0x0A, 0x06, 0x06, 0x0E, 0x0E,
            0x10, 0x10, 0x18, 0x18, 0x14, 0x14, 0x1C, 0x1C,
            0x12, 0x12, 0x1A, 0x1A, 0x16, 0x16, 0x1E, 0x1E,
            0x20, 0x20, 0x28, 0x28, 0x24, 0x24, 0x2C, 0x2C,
            0x22, 0x22, 0x2A, 0x2A, 0x26, 0x26, 0x2E, 0x2E,
            0x30, 0x30, 0x38, 0x38, 0x34, 0x34, 0x3C, 0x3C,
            0x32, 0x32, 0x3A, 0x3A, 0x36, 0x36, 0x3E, 0x3E,
            0x40, 0x40, 0x48, 0x48, 0x44, 0x44, 0x4C, 0x4C,
            0x42, 0x42, 0x4A, 0x4A, 0x46, 0x46, 0x4E, 0x4E,
            0x50, 0x50, 0x58, 0x58, 0x54, 0x54, 0x5C, 0x5C,
            0x52, 0x52, 0x5A, 0x5A, 0x56, 0x56, 0x5E, 0x5E,
            0x60, 0x60, 0x68, 0x68, 0x64, 0x64, 0x6C, 0x6C,
            0x62, 0x62, 0x6A, 0x6A, 0x66, 0x66, 0x6E, 0x6E,
            0x70, 0x70, 0x78, 0x78, 0x74, 0x74, 0x7C, 0x7C,
            0x72, 0x72, 0x7A, 0x7A, 0x76, 0x76, 0x7E, 0x7E,
            0x80, 0x80, 0x88, 0x88, 0x84, 0x84, 0x8C, 0x8C,
            0x82, 0x82, 0x8A, 0x8A, 0x86, 0x86, 0x8E, 0x8E,
            0x90, 0x90, 0x98, 0x98, 0x94, 0x94, 0x9C, 0x9C,
            0x92, 0x92, 0x9A, 0x9A, 0x96, 0x96, 0x9E, 0x9E,
            0xA0, 0xA0, 0xA8, 0xA8, 0xA4, 0xA4, 0xAC, 0xAC,
            0xA2, 0xA2, 0xAA, 0xAA, 0xA6, 0xA6, 0xAE, 0xAE,
            0xB0, 0xB0, 0xB8, 0xB8, 0xB4, 0xB4, 0xBC, 0xBC,
            0xB2, 0xB2, 0xBA, 0xBA, 0xB6, 0xB6, 0xBE, 0xBE,
            0xC0, 0xC0, 0xC8, 0xC8, 0xC4, 0xC4, 0xCC, 0xCC,
            0xC2, 0xC2, 0xCA, 0xCA, 0xC6, 0xC6, 0xCE, 0xCE,
            0xD0, 0xD0, 0xD8, 0xD8, 0xD4, 0xD4, 0xDC, 0xDC,
            0xD2, 0xD2, 0xDA, 0xDA, 0xD6, 0xD6, 0xDE, 0xDE,
            0xE0, 0xE0, 0xE8, 0xE8, 0xE4, 0xE4, 0xEC, 0xEC,
            0xE2, 0xE2, 0xEA, 0xEA, 0xE6, 0xE6, 0xEE, 0xEE,
            0xF0, 0xF0, 0xF8, 0xF8, 0xF4, 0xF4, 0xFC, 0xFC,
            0xF2, 0xF2, 0xFA, 0xFA, 0xF6, 0xF6, 0xFE, 0xFE,
        ];

        // Mapping tables for the PC-2 transformation.
        static $pc2mapc1 = [
            0x00000000, 0x00000400, 0x00200000, 0x00200400,
            0x00000001, 0x00000401, 0x00200001, 0x00200401,
            0x02000000, 0x02000400, 0x02200000, 0x02200400,
            0x02000001, 0x02000401, 0x02200001, 0x02200401,
        ];
        static $pc2mapc2 = [
            0x00000000, 0x00000800, 0x08000000, 0x08000800,
            0x00010000, 0x00010800, 0x08010000, 0x08010800,
            0x00000000, 0x00000800, 0x08000000, 0x08000800,
            0x00010000, 0x00010800, 0x08010000, 0x08010800,
            0x00000100, 0x00000900, 0x08000100, 0x08000900,
            0x00010100, 0x00010900, 0x08010100, 0x08010900,
            0x00000100, 0x00000900, 0x08000100, 0x08000900,
            0x00010100, 0x00010900, 0x08010100, 0x08010900,
            0x00000010, 0x00000810, 0x08000010, 0x08000810,
            0x00010010, 0x00010810, 0x08010010, 0x08010810,
            0x00000010, 0x00000810, 0x08000010, 0x08000810,
            0x00010010, 0x00010810, 0x08010010, 0x08010810,
            0x00000110, 0x00000910, 0x08000110, 0x08000910,
            0x00010110, 0x00010910, 0x08010110, 0x08010910,
            0x00000110, 0x00000910, 0x08000110, 0x08000910,
            0x00010110, 0x00010910, 0x08010110, 0x08010910,
            0x00040000, 0x00040800, 0x08040000, 0x08040800,
            0x00050000, 0x00050800, 0x08050000, 0x08050800,
            0x00040000, 0x00040800, 0x08040000, 0x08040800,
            0x00050000, 0x00050800, 0x08050000, 0x08050800,
            0x00040100, 0x00040900, 0x08040100, 0x08040900,
            0x00050100, 0x00050900, 0x08050100, 0x08050900,
            0x00040100, 0x00040900, 0x08040100, 0x08040900,
            0x00050100, 0x00050900, 0x08050100, 0x08050900,
            0x00040010, 0x00040810, 0x08040010, 0x08040810,
            0x00050010, 0x00050810, 0x08050010, 0x08050810,
            0x00040010, 0x00040810, 0x08040010, 0x08040810,
            0x00050010, 0x00050810, 0x08050010, 0x08050810,
            0x00040110, 0x00040910, 0x08040110, 0x08040910,
            0x00050110, 0x00050910, 0x08050110, 0x08050910,
            0x00040110, 0x00040910, 0x08040110, 0x08040910,
            0x00050110, 0x00050910, 0x08050110, 0x08050910,
            0x01000000, 0x01000800, 0x09000000, 0x09000800,
            0x01010000, 0x01010800, 0x09010000, 0x09010800,
            0x01000000, 0x01000800, 0x09000000, 0x09000800,
            0x01010000, 0x01010800, 0x09010000, 0x09010800,
            0x01000100, 0x01000900, 0x09000100, 0x09000900,
            0x01010100, 0x01010900, 0x09010100, 0x09010900,
            0x01000100, 0x01000900, 0x09000100, 0x09000900,
            0x01010100, 0x01010900, 0x09010100, 0x09010900,
            0x01000010, 0x01000810, 0x09000010, 0x09000810,
            0x01010010, 0x01010810, 0x09010010, 0x09010810,
            0x01000010, 0x01000810, 0x09000010, 0x09000810,
            0x01010010, 0x01010810, 0x09010010, 0x09010810,
            0x01000110, 0x01000910, 0x09000110, 0x09000910,
            0x01010110, 0x01010910, 0x09010110, 0x09010910,
            0x01000110, 0x01000910, 0x09000110, 0x09000910,
            0x01010110, 0x01010910, 0x09010110, 0x09010910,
            0x01040000, 0x01040800, 0x09040000, 0x09040800,
            0x01050000, 0x01050800, 0x09050000, 0x09050800,
            0x01040000, 0x01040800, 0x09040000, 0x09040800,
            0x01050000, 0x01050800, 0x09050000, 0x09050800,
            0x01040100, 0x01040900, 0x09040100, 0x09040900,
            0x01050100, 0x01050900, 0x09050100, 0x09050900,
            0x01040100, 0x01040900, 0x09040100, 0x09040900,
            0x01050100, 0x01050900, 0x09050100, 0x09050900,
            0x01040010, 0x01040810, 0x09040010, 0x09040810,
            0x01050010, 0x01050810, 0x09050010, 0x09050810,
            0x01040010, 0x01040810, 0x09040010, 0x09040810,
            0x01050010, 0x01050810, 0x09050010, 0x09050810,
            0x01040110, 0x01040910, 0x09040110, 0x09040910,
            0x01050110, 0x01050910, 0x09050110, 0x09050910,
            0x01040110, 0x01040910, 0x09040110, 0x09040910,
            0x01050110, 0x01050910, 0x09050110, 0x09050910,
        ];
        static $pc2mapc3 = [
            0x00000000, 0x00000004, 0x00001000, 0x00001004,
            0x00000000, 0x00000004, 0x00001000, 0x00001004,
            0x10000000, 0x10000004, 0x10001000, 0x10001004,
            0x10000000, 0x10000004, 0x10001000, 0x10001004,
            0x00000020, 0x00000024, 0x00001020, 0x00001024,
            0x00000020, 0x00000024, 0x00001020, 0x00001024,
            0x10000020, 0x10000024, 0x10001020, 0x10001024,
            0x10000020, 0x10000024, 0x10001020, 0x10001024,
            0x00080000, 0x00080004, 0x00081000, 0x00081004,
            0x00080000, 0x00080004, 0x00081000, 0x00081004,
            0x10080000, 0x10080004, 0x10081000, 0x10081004,
            0x10080000, 0x10080004, 0x10081000, 0x10081004,
            0x00080020, 0x00080024, 0x00081020, 0x00081024,
            0x00080020, 0x00080024, 0x00081020, 0x00081024,
            0x10080020, 0x10080024, 0x10081020, 0x10081024,
            0x10080020, 0x10080024, 0x10081020, 0x10081024,
            0x20000000, 0x20000004, 0x20001000, 0x20001004,
            0x20000000, 0x20000004, 0x20001000, 0x20001004,
            0x30000000, 0x30000004, 0x30001000, 0x30001004,
            0x30000000, 0x30000004, 0x30001000, 0x30001004,
            0x20000020, 0x20000024, 0x20001020, 0x20001024,
            0x20000020, 0x20000024, 0x20001020, 0x20001024,
            0x30000020, 0x30000024, 0x30001020, 0x30001024,
            0x30000020, 0x30000024, 0x30001020, 0x30001024,
            0x20080000, 0x20080004, 0x20081000, 0x20081004,
            0x20080000, 0x20080004, 0x20081000, 0x20081004,
            0x30080000, 0x30080004, 0x30081000, 0x30081004,
            0x30080000, 0x30080004, 0x30081000, 0x30081004,
            0x20080020, 0x20080024, 0x20081020, 0x20081024,
            0x20080020, 0x20080024, 0x20081020, 0x20081024,
            0x30080020, 0x30080024, 0x30081020, 0x30081024,
            0x30080020, 0x30080024, 0x30081020, 0x30081024,
            0x00000002, 0x00000006, 0x00001002, 0x00001006,
            0x00000002, 0x00000006, 0x00001002, 0x00001006,
            0x10000002, 0x10000006, 0x10001002, 0x10001006,
            0x10000002, 0x10000006, 0x10001002, 0x10001006,
            0x00000022, 0x00000026, 0x00001022, 0x00001026,
            0x00000022, 0x00000026, 0x00001022, 0x00001026,
            0x10000022, 0x10000026, 0x10001022, 0x10001026,
            0x10000022, 0x10000026, 0x10001022, 0x10001026,
            0x00080002, 0x00080006, 0x00081002, 0x00081006,
            0x00080002, 0x00080006, 0x00081002, 0x00081006,
            0x10080002, 0x10080006, 0x10081002, 0x10081006,
            0x10080002, 0x10080006, 0x10081002, 0x10081006,
            0x00080022, 0x00080026, 0x00081022, 0x00081026,
            0x00080022, 0x00080026, 0x00081022, 0x00081026,
            0x10080022, 0x10080026, 0x10081022, 0x10081026,
            0x10080022, 0x10080026, 0x10081022, 0x10081026,
            0x20000002, 0x20000006, 0x20001002, 0x20001006,
            0x20000002, 0x20000006, 0x20001002, 0x20001006,
            0x30000002, 0x30000006, 0x30001002, 0x30001006,
            0x30000002, 0x30000006, 0x30001002, 0x30001006,
            0x20000022, 0x20000026, 0x20001022, 0x20001026,
            0x20000022, 0x20000026, 0x20001022, 0x20001026,
            0x30000022, 0x30000026, 0x30001022, 0x30001026,
            0x30000022, 0x30000026, 0x30001022, 0x30001026,
            0x20080002, 0x20080006, 0x20081002, 0x20081006,
            0x20080002, 0x20080006, 0x20081002, 0x20081006,
            0x30080002, 0x30080006, 0x30081002, 0x30081006,
            0x30080002, 0x30080006, 0x30081002, 0x30081006,
            0x20080022, 0x20080026, 0x20081022, 0x20081026,
            0x20080022, 0x20080026, 0x20081022, 0x20081026,
            0x30080022, 0x30080026, 0x30081022, 0x30081026,
            0x30080022, 0x30080026, 0x30081022, 0x30081026,
        ];
        static $pc2mapc4 = [
            0x00000000, 0x00100000, 0x00000008, 0x00100008,
            0x00000200, 0x00100200, 0x00000208, 0x00100208,
            0x00000000, 0x00100000, 0x00000008, 0x00100008,
            0x00000200, 0x00100200, 0x00000208, 0x00100208,
            0x04000000, 0x04100000, 0x04000008, 0x04100008,
            0x04000200, 0x04100200, 0x04000208, 0x04100208,
            0x04000000, 0x04100000, 0x04000008, 0x04100008,
            0x04000200, 0x04100200, 0x04000208, 0x04100208,
            0x00002000, 0x00102000, 0x00002008, 0x00102008,
            0x00002200, 0x00102200, 0x00002208, 0x00102208,
            0x00002000, 0x00102000, 0x00002008, 0x00102008,
            0x00002200, 0x00102200, 0x00002208, 0x00102208,
            0x04002000, 0x04102000, 0x04002008, 0x04102008,
            0x04002200, 0x04102200, 0x04002208, 0x04102208,
            0x04002000, 0x04102000, 0x04002008, 0x04102008,
            0x04002200, 0x04102200, 0x04002208, 0x04102208,
            0x00000000, 0x00100000, 0x00000008, 0x00100008,
            0x00000200, 0x00100200, 0x00000208, 0x00100208,
            0x00000000, 0x00100000, 0x00000008, 0x00100008,
            0x00000200, 0x00100200, 0x00000208, 0x00100208,
            0x04000000, 0x04100000, 0x04000008, 0x04100008,
            0x04000200, 0x04100200, 0x04000208, 0x04100208,
            0x04000000, 0x04100000, 0x04000008, 0x04100008,
            0x04000200, 0x04100200, 0x04000208, 0x04100208,
            0x00002000, 0x00102000, 0x00002008, 0x00102008,
            0x00002200, 0x00102200, 0x00002208, 0x00102208,
            0x00002000, 0x00102000, 0x00002008, 0x00102008,
            0x00002200, 0x00102200, 0x00002208, 0x00102208,
            0x04002000, 0x04102000, 0x04002008, 0x04102008,
            0x04002200, 0x04102200, 0x04002208, 0x04102208,
            0x04002000, 0x04102000, 0x04002008, 0x04102008,
            0x04002200, 0x04102200, 0x04002208, 0x04102208,
            0x00020000, 0x00120000, 0x00020008, 0x00120008,
            0x00020200, 0x00120200, 0x00020208, 0x00120208,
            0x00020000, 0x00120000, 0x00020008, 0x00120008,
            0x00020200, 0x00120200, 0x00020208, 0x00120208,
            0x04020000, 0x04120000, 0x04020008, 0x04120008,
            0x04020200, 0x04120200, 0x04020208, 0x04120208,
            0x04020000, 0x04120000, 0x04020008, 0x04120008,
            0x04020200, 0x04120200, 0x04020208, 0x04120208,
            0x00022000, 0x00122000, 0x00022008, 0x00122008,
            0x00022200, 0x00122200, 0x00022208, 0x00122208,
            0x00022000, 0x00122000, 0x00022008, 0x00122008,
            0x00022200, 0x00122200, 0x00022208, 0x00122208,
            0x04022000, 0x04122000, 0x04022008, 0x04122008,
            0x04022200, 0x04122200, 0x04022208, 0x04122208,
            0x04022000, 0x04122000, 0x04022008, 0x04122008,
            0x04022200, 0x04122200, 0x04022208, 0x04122208,
            0x00020000, 0x00120000, 0x00020008, 0x00120008,
            0x00020200, 0x00120200, 0x00020208, 0x00120208,
            0x00020000, 0x00120000, 0x00020008, 0x00120008,
            0x00020200, 0x00120200, 0x00020208, 0x00120208,
            0x04020000, 0x04120000, 0x04020008, 0x04120008,
            0x04020200, 0x04120200, 0x04020208, 0x04120208,
            0x04020000, 0x04120000, 0x04020008, 0x04120008,
            0x04020200, 0x04120200, 0x04020208, 0x04120208,
            0x00022000, 0x00122000, 0x00022008, 0x00122008,
            0x00022200, 0x00122200, 0x00022208, 0x00122208,
            0x00022000, 0x00122000, 0x00022008, 0x00122008,
            0x00022200, 0x00122200, 0x00022208, 0x00122208,
            0x04022000, 0x04122000, 0x04022008, 0x04122008,
            0x04022200, 0x04122200, 0x04022208, 0x04122208,
            0x04022000, 0x04122000, 0x04022008, 0x04122008,
            0x04022200, 0x04122200, 0x04022208, 0x04122208,
        ];
        static $pc2mapd1 = [
            0x00000000, 0x00000001, 0x08000000, 0x08000001,
            0x00200000, 0x00200001, 0x08200000, 0x08200001,
            0x00000002, 0x00000003, 0x08000002, 0x08000003,
            0x00200002, 0x00200003, 0x08200002, 0x08200003,
        ];
        static $pc2mapd2 = [
            0x00000000, 0x00100000, 0x00000800, 0x00100800,
            0x00000000, 0x00100000, 0x00000800, 0x00100800,
            0x04000000, 0x04100000, 0x04000800, 0x04100800,
            0x04000000, 0x04100000, 0x04000800, 0x04100800,
            0x00000004, 0x00100004, 0x00000804, 0x00100804,
            0x00000004, 0x00100004, 0x00000804, 0x00100804,
            0x04000004, 0x04100004, 0x04000804, 0x04100804,
            0x04000004, 0x04100004, 0x04000804, 0x04100804,
            0x00000000, 0x00100000, 0x00000800, 0x00100800,
            0x00000000, 0x00100000, 0x00000800, 0x00100800,
            0x04000000, 0x04100000, 0x04000800, 0x04100800,
            0x04000000, 0x04100000, 0x04000800, 0x04100800,
            0x00000004, 0x00100004, 0x00000804, 0x00100804,
            0x00000004, 0x00100004, 0x00000804, 0x00100804,
            0x04000004, 0x04100004, 0x04000804, 0x04100804,
            0x04000004, 0x04100004, 0x04000804, 0x04100804,
            0x00000200, 0x00100200, 0x00000A00, 0x00100A00,
            0x00000200, 0x00100200, 0x00000A00, 0x00100A00,
            0x04000200, 0x04100200, 0x04000A00, 0x04100A00,
            0x04000200, 0x04100200, 0x04000A00, 0x04100A00,
            0x00000204, 0x00100204, 0x00000A04, 0x00100A04,
            0x00000204, 0x00100204, 0x00000A04, 0x00100A04,
            0x04000204, 0x04100204, 0x04000A04, 0x04100A04,
            0x04000204, 0x04100204, 0x04000A04, 0x04100A04,
            0x00000200, 0x00100200, 0x00000A00, 0x00100A00,
            0x00000200, 0x00100200, 0x00000A00, 0x00100A00,
            0x04000200, 0x04100200, 0x04000A00, 0x04100A00,
            0x04000200, 0x04100200, 0x04000A00, 0x04100A00,
            0x00000204, 0x00100204, 0x00000A04, 0x00100A04,
            0x00000204, 0x00100204, 0x00000A04, 0x00100A04,
            0x04000204, 0x04100204, 0x04000A04, 0x04100A04,
            0x04000204, 0x04100204, 0x04000A04, 0x04100A04,
            0x00020000, 0x00120000, 0x00020800, 0x00120800,
            0x00020000, 0x00120000, 0x00020800, 0x00120800,
            0x04020000, 0x04120000, 0x04020800, 0x04120800,
            0x04020000, 0x04120000, 0x04020800, 0x04120800,
            0x00020004, 0x00120004, 0x00020804, 0x00120804,
            0x00020004, 0x00120004, 0x00020804, 0x00120804,
            0x04020004, 0x04120004, 0x04020804, 0x04120804,
            0x04020004, 0x04120004, 0x04020804, 0x04120804,
            0x00020000, 0x00120000, 0x00020800, 0x00120800,
            0x00020000, 0x00120000, 0x00020800, 0x00120800,
            0x04020000, 0x04120000, 0x04020800, 0x04120800,
            0x04020000, 0x04120000, 0x04020800, 0x04120800,
            0x00020004, 0x00120004, 0x00020804, 0x00120804,
            0x00020004, 0x00120004, 0x00020804, 0x00120804,
            0x04020004, 0x04120004, 0x04020804, 0x04120804,
            0x04020004, 0x04120004, 0x04020804, 0x04120804,
            0x00020200, 0x00120200, 0x00020A00, 0x00120A00,
            0x00020200, 0x00120200, 0x00020A00, 0x00120A00,
            0x04020200, 0x04120200, 0x04020A00, 0x04120A00,
            0x04020200, 0x04120200, 0x04020A00, 0x04120A00,
            0x00020204, 0x00120204, 0x00020A04, 0x00120A04,
            0x00020204, 0x00120204, 0x00020A04, 0x00120A04,
            0x04020204, 0x04120204, 0x04020A04, 0x04120A04,
            0x04020204, 0x04120204, 0x04020A04, 0x04120A04,
            0x00020200, 0x00120200, 0x00020A00, 0x00120A00,
            0x00020200, 0x00120200, 0x00020A00, 0x00120A00,
            0x04020200, 0x04120200, 0x04020A00, 0x04120A00,
            0x04020200, 0x04120200, 0x04020A00, 0x04120A00,
            0x00020204, 0x00120204, 0x00020A04, 0x00120A04,
            0x00020204, 0x00120204, 0x00020A04, 0x00120A04,
            0x04020204, 0x04120204, 0x04020A04, 0x04120A04,
            0x04020204, 0x04120204, 0x04020A04, 0x04120A04,
        ];
        static $pc2mapd3 = [
            0x00000000, 0x00010000, 0x02000000, 0x02010000,
            0x00000020, 0x00010020, 0x02000020, 0x02010020,
            0x00040000, 0x00050000, 0x02040000, 0x02050000,
            0x00040020, 0x00050020, 0x02040020, 0x02050020,
            0x00002000, 0x00012000, 0x02002000, 0x02012000,
            0x00002020, 0x00012020, 0x02002020, 0x02012020,
            0x00042000, 0x00052000, 0x02042000, 0x02052000,
            0x00042020, 0x00052020, 0x02042020, 0x02052020,
            0x00000000, 0x00010000, 0x02000000, 0x02010000,
            0x00000020, 0x00010020, 0x02000020, 0x02010020,
            0x00040000, 0x00050000, 0x02040000, 0x02050000,
            0x00040020, 0x00050020, 0x02040020, 0x02050020,
            0x00002000, 0x00012000, 0x02002000, 0x02012000,
            0x00002020, 0x00012020, 0x02002020, 0x02012020,
            0x00042000, 0x00052000, 0x02042000, 0x02052000,
            0x00042020, 0x00052020, 0x02042020, 0x02052020,
            0x00000010, 0x00010010, 0x02000010, 0x02010010,
            0x00000030, 0x00010030, 0x02000030, 0x02010030,
            0x00040010, 0x00050010, 0x02040010, 0x02050010,
            0x00040030, 0x00050030, 0x02040030, 0x02050030,
            0x00002010, 0x00012010, 0x02002010, 0x02012010,
            0x00002030, 0x00012030, 0x02002030, 0x02012030,
            0x00042010, 0x00052010, 0x02042010, 0x02052010,
            0x00042030, 0x00052030, 0x02042030, 0x02052030,
            0x00000010, 0x00010010, 0x02000010, 0x02010010,
            0x00000030, 0x00010030, 0x02000030, 0x02010030,
            0x00040010, 0x00050010, 0x02040010, 0x02050010,
            0x00040030, 0x00050030, 0x02040030, 0x02050030,
            0x00002010, 0x00012010, 0x02002010, 0x02012010,
            0x00002030, 0x00012030, 0x02002030, 0x02012030,
            0x00042010, 0x00052010, 0x02042010, 0x02052010,
            0x00042030, 0x00052030, 0x02042030, 0x02052030,
            0x20000000, 0x20010000, 0x22000000, 0x22010000,
            0x20000020, 0x20010020, 0x22000020, 0x22010020,
            0x20040000, 0x20050000, 0x22040000, 0x22050000,
            0x20040020, 0x20050020, 0x22040020, 0x22050020,
            0x20002000, 0x20012000, 0x22002000, 0x22012000,
            0x20002020, 0x20012020, 0x22002020, 0x22012020,
            0x20042000, 0x20052000, 0x22042000, 0x22052000,
            0x20042020, 0x20052020, 0x22042020, 0x22052020,
            0x20000000, 0x20010000, 0x22000000, 0x22010000,
            0x20000020, 0x20010020, 0x22000020, 0x22010020,
            0x20040000, 0x20050000, 0x22040000, 0x22050000,
            0x20040020, 0x20050020, 0x22040020, 0x22050020,
            0x20002000, 0x20012000, 0x22002000, 0x22012000,
            0x20002020, 0x20012020, 0x22002020, 0x22012020,
            0x20042000, 0x20052000, 0x22042000, 0x22052000,
            0x20042020, 0x20052020, 0x22042020, 0x22052020,
            0x20000010, 0x20010010, 0x22000010, 0x22010010,
            0x20000030, 0x20010030, 0x22000030, 0x22010030,
            0x20040010, 0x20050010, 0x22040010, 0x22050010,
            0x20040030, 0x20050030, 0x22040030, 0x22050030,
            0x20002010, 0x20012010, 0x22002010, 0x22012010,
            0x20002030, 0x20012030, 0x22002030, 0x22012030,
            0x20042010, 0x20052010, 0x22042010, 0x22052010,
            0x20042030, 0x20052030, 0x22042030, 0x22052030,
            0x20000010, 0x20010010, 0x22000010, 0x22010010,
            0x20000030, 0x20010030, 0x22000030, 0x22010030,
            0x20040010, 0x20050010, 0x22040010, 0x22050010,
            0x20040030, 0x20050030, 0x22040030, 0x22050030,
            0x20002010, 0x20012010, 0x22002010, 0x22012010,
            0x20002030, 0x20012030, 0x22002030, 0x22012030,
            0x20042010, 0x20052010, 0x22042010, 0x22052010,
            0x20042030, 0x20052030, 0x22042030, 0x22052030,
        ];
        static $pc2mapd4 = [
            0x00000000, 0x00000400, 0x01000000, 0x01000400,
            0x00000000, 0x00000400, 0x01000000, 0x01000400,
            0x00000100, 0x00000500, 0x01000100, 0x01000500,
            0x00000100, 0x00000500, 0x01000100, 0x01000500,
            0x10000000, 0x10000400, 0x11000000, 0x11000400,
            0x10000000, 0x10000400, 0x11000000, 0x11000400,
            0x10000100, 0x10000500, 0x11000100, 0x11000500,
            0x10000100, 0x10000500, 0x11000100, 0x11000500,
            0x00080000, 0x00080400, 0x01080000, 0x01080400,
            0x00080000, 0x00080400, 0x01080000, 0x01080400,
            0x00080100, 0x00080500, 0x01080100, 0x01080500,
            0x00080100, 0x00080500, 0x01080100, 0x01080500,
            0x10080000, 0x10080400, 0x11080000, 0x11080400,
            0x10080000, 0x10080400, 0x11080000, 0x11080400,
            0x10080100, 0x10080500, 0x11080100, 0x11080500,
            0x10080100, 0x10080500, 0x11080100, 0x11080500,
            0x00000008, 0x00000408, 0x01000008, 0x01000408,
            0x00000008, 0x00000408, 0x01000008, 0x01000408,
            0x00000108, 0x00000508, 0x01000108, 0x01000508,
            0x00000108, 0x00000508, 0x01000108, 0x01000508,
            0x10000008, 0x10000408, 0x11000008, 0x11000408,
            0x10000008, 0x10000408, 0x11000008, 0x11000408,
            0x10000108, 0x10000508, 0x11000108, 0x11000508,
            0x10000108, 0x10000508, 0x11000108, 0x11000508,
            0x00080008, 0x00080408, 0x01080008, 0x01080408,
            0x00080008, 0x00080408, 0x01080008, 0x01080408,
            0x00080108, 0x00080508, 0x01080108, 0x01080508,
            0x00080108, 0x00080508, 0x01080108, 0x01080508,
            0x10080008, 0x10080408, 0x11080008, 0x11080408,
            0x10080008, 0x10080408, 0x11080008, 0x11080408,
            0x10080108, 0x10080508, 0x11080108, 0x11080508,
            0x10080108, 0x10080508, 0x11080108, 0x11080508,
            0x00001000, 0x00001400, 0x01001000, 0x01001400,
            0x00001000, 0x00001400, 0x01001000, 0x01001400,
            0x00001100, 0x00001500, 0x01001100, 0x01001500,
            0x00001100, 0x00001500, 0x01001100, 0x01001500,
            0x10001000, 0x10001400, 0x11001000, 0x11001400,
            0x10001000, 0x10001400, 0x11001000, 0x11001400,
            0x10001100, 0x10001500, 0x11001100, 0x11001500,
            0x10001100, 0x10001500, 0x11001100, 0x11001500,
            0x00081000, 0x00081400, 0x01081000, 0x01081400,
            0x00081000, 0x00081400, 0x01081000, 0x01081400,
            0x00081100, 0x00081500, 0x01081100, 0x01081500,
            0x00081100, 0x00081500, 0x01081100, 0x01081500,
            0x10081000, 0x10081400, 0x11081000, 0x11081400,
            0x10081000, 0x10081400, 0x11081000, 0x11081400,
            0x10081100, 0x10081500, 0x11081100, 0x11081500,
            0x10081100, 0x10081500, 0x11081100, 0x11081500,
            0x00001008, 0x00001408, 0x01001008, 0x01001408,
            0x00001008, 0x00001408, 0x01001008, 0x01001408,
            0x00001108, 0x00001508, 0x01001108, 0x01001508,
            0x00001108, 0x00001508, 0x01001108, 0x01001508,
            0x10001008, 0x10001408, 0x11001008, 0x11001408,
            0x10001008, 0x10001408, 0x11001008, 0x11001408,
            0x10001108, 0x10001508, 0x11001108, 0x11001508,
            0x10001108, 0x10001508, 0x11001108, 0x11001508,
            0x00081008, 0x00081408, 0x01081008, 0x01081408,
            0x00081008, 0x00081408, 0x01081008, 0x01081408,
            0x00081108, 0x00081508, 0x01081108, 0x01081508,
            0x00081108, 0x00081508, 0x01081108, 0x01081508,
            0x10081008, 0x10081408, 0x11081008, 0x11081408,
            0x10081008, 0x10081408, 0x11081008, 0x11081408,
            0x10081108, 0x10081508, 0x11081108, 0x11081508,
            0x10081108, 0x10081508, 0x11081108, 0x11081508,
        ];

        $keys = [];
        for ($des_round = 0; $des_round < $this->des_rounds; ++$des_round) {
            // pad the key and remove extra characters as appropriate.
            $key = str_pad(substr($this->key, $des_round * 8, 8), 8, "\0");

            // Perform the PC/1 transformation and compute C and D.
            $t = unpack('Nl/Nr', $key);
            [$l, $r] = [$t['l'], $t['r']];
            $key = (self::$shuffle[$pc1map[ $r        & 0xFF]] & "\x80\x80\x80\x80\x80\x80\x80\x00") |
                   (self::$shuffle[$pc1map[($r >>  8) & 0xFF]] & "\x40\x40\x40\x40\x40\x40\x40\x00") |
                   (self::$shuffle[$pc1map[($r >> 16) & 0xFF]] & "\x20\x20\x20\x20\x20\x20\x20\x00") |
                   (self::$shuffle[$pc1map[($r >> 24) & 0xFF]] & "\x10\x10\x10\x10\x10\x10\x10\x00") |
                   (self::$shuffle[$pc1map[ $l        & 0xFF]] & "\x08\x08\x08\x08\x08\x08\x08\x00") |
                   (self::$shuffle[$pc1map[($l >>  8) & 0xFF]] & "\x04\x04\x04\x04\x04\x04\x04\x00") |
                   (self::$shuffle[$pc1map[($l >> 16) & 0xFF]] & "\x02\x02\x02\x02\x02\x02\x02\x00") |
                   (self::$shuffle[$pc1map[($l >> 24) & 0xFF]] & "\x01\x01\x01\x01\x01\x01\x01\x00");
            $key = unpack('Nc/Nd', $key);
            $c = ($key['c'] >> 4) & 0x0FFFFFFF;
            $d = (($key['d'] >> 4) & 0x0FFFFFF0) | ($key['c'] & 0x0F);

            $keys[$des_round] = [
                self::ENCRYPT => [],
                self::DECRYPT => array_fill(0, 32, 0),
            ];
            for ($i = 0, $ki = 31; $i < 16; ++$i, $ki -= 2) {
                $c <<= $shifts[$i];
                $c = ($c | ($c >> 28)) & 0x0FFFFFFF;
                $d <<= $shifts[$i];
                $d = ($d | ($d >> 28)) & 0x0FFFFFFF;

                // Perform the PC-2 transformation.
                $cp = $pc2mapc1[ $c >> 24        ] | $pc2mapc2[($c >> 16) & 0xFF] |
                      $pc2mapc3[($c >>  8) & 0xFF] | $pc2mapc4[ $c        & 0xFF];
                $dp = $pc2mapd1[ $d >> 24        ] | $pc2mapd2[($d >> 16) & 0xFF] |
                      $pc2mapd3[($d >>  8) & 0xFF] | $pc2mapd4[ $d        & 0xFF];

                // Reorder: odd bytes/even bytes. Push the result in key schedule.
                $val1 = ($cp        & self::safe_intval(0xFF000000)) | (($cp <<  8) & 0x00FF0000) |
                        (($dp >> 16) & 0x0000FF00) | (($dp >>  8) & 0x000000FF);
                $val2 = (($cp <<  8) & self::safe_intval(0xFF000000)) | (($cp << 16) & 0x00FF0000) |
                        (($dp >>  8) & 0x0000FF00) | ($dp        & 0x000000FF);
                $keys[$des_round][self::ENCRYPT][       ] = $val1;
                $keys[$des_round][self::DECRYPT][$ki - 1] = $val1;
                $keys[$des_round][self::ENCRYPT][       ] = $val2;
                $keys[$des_round][self::DECRYPT][$ki    ] = $val2;
            }
        }

        switch ($this->des_rounds) {
            case 3: // 3DES keys
                $this->keys = [
                    self::ENCRYPT => array_merge(
                        $keys[0][self::ENCRYPT],
                        $keys[1][self::DECRYPT],
                        $keys[2][self::ENCRYPT]
                    ),
                    self::DECRYPT => array_merge(
                        $keys[2][self::DECRYPT],
                        $keys[1][self::ENCRYPT],
                        $keys[0][self::DECRYPT]
                    ),
                ];
                break;
            // case 1: // DES keys
            default:
                $this->keys = [
                    self::ENCRYPT => $keys[0][self::ENCRYPT],
                    self::DECRYPT => $keys[0][self::DECRYPT],
                ];
        }
    }

    /**
     * Setup the performance-optimized function for de/encrypt()
     *
     * @see Common\SymmetricKey::setupInlineCrypt()
     * @psalm-suppress PossiblyUnusedMethod
     */
    protected function setupInlineCrypt(): void
    {
        // Engine configuration for:
        // -  DES ($des_rounds == 1) or
        // - 3DES ($des_rounds == 3)
        $des_rounds = $this->des_rounds;

        $init_crypt = 'static $sbox1, $sbox2, $sbox3, $sbox4, $sbox5, $sbox6, $sbox7, $sbox8, $shuffleip, $shuffleinvip;
            if (!$sbox1) {
                $sbox1 = array_map(self::safe_intval(...), self::$sbox1);
                $sbox2 = array_map(self::safe_intval(...), self::$sbox2);
                $sbox3 = array_map(self::safe_intval(...), self::$sbox3);
                $sbox4 = array_map(self::safe_intval(...), self::$sbox4);
                $sbox5 = array_map(self::safe_intval(...), self::$sbox5);
                $sbox6 = array_map(self::safe_intval(...), self::$sbox6);
                $sbox7 = array_map(self::safe_intval(...), self::$sbox7);
                $sbox8 = array_map(self::safe_intval(...), self::$sbox8);'
                /* Merge $shuffle with $[inv]ipmap */ . '
                for ($i = 0; $i < 256; ++$i) {
                    $shuffleip[]    =  self::$shuffle[self::$ipmap[$i]];
                    $shuffleinvip[] =  self::$shuffle[self::$invipmap[$i]];
                }
            }
        ';

        $k = [
            self::ENCRYPT => $this->keys[self::ENCRYPT],
            self::DECRYPT => $this->keys[self::DECRYPT],
        ];
        $init_encrypt = '';
        $init_decrypt = '';

        // Creating code for en- and decryption.
        $crypt_block = [];
        foreach ([self::ENCRYPT, self::DECRYPT] as $c) {
            /* Do the initial IP permutation. */
            $crypt_block[$c] = '
                $in = unpack("N*", $in);
                $l  = $in[1];
                $r  = $in[2];
                $in = unpack("N*",
                    ($shuffleip[ $r        & 0xFF] & "\x80\x80\x80\x80\x80\x80\x80\x80") |
                    ($shuffleip[($r >>  8) & 0xFF] & "\x40\x40\x40\x40\x40\x40\x40\x40") |
                    ($shuffleip[($r >> 16) & 0xFF] & "\x20\x20\x20\x20\x20\x20\x20\x20") |
                    ($shuffleip[($r >> 24) & 0xFF] & "\x10\x10\x10\x10\x10\x10\x10\x10") |
                    ($shuffleip[ $l        & 0xFF] & "\x08\x08\x08\x08\x08\x08\x08\x08") |
                    ($shuffleip[($l >>  8) & 0xFF] & "\x04\x04\x04\x04\x04\x04\x04\x04") |
                    ($shuffleip[($l >> 16) & 0xFF] & "\x02\x02\x02\x02\x02\x02\x02\x02") |
                    ($shuffleip[($l >> 24) & 0xFF] & "\x01\x01\x01\x01\x01\x01\x01\x01")
                );
                ' . /* Extract L0 and R0 */ '
                $l = $in[1];
                $r = $in[2];
            ';

            $l = '$l';
            $r = '$r';

            // Perform DES or 3DES.
            for ($ki = -1, $des_round = 0; $des_round < $des_rounds; ++$des_round) {
                // Perform the 16 steps.
                for ($i = 0; $i < 16; ++$i) {
                    // start of "the Feistel (F) function" - see the following URL:
                    // http://en.wikipedia.org/wiki/Image:Data_Encryption_Standard_InfoBox_Diagram.png
                    // Merge key schedule.
                    $crypt_block[$c] .= '
                        $b1 = ((' . $r . ' >>  3) & 0x1FFFFFFF)  ^ (' . $r . ' << 29) ^ ' . $k[$c][++$ki] . ';
                        $b2 = ((' . $r . ' >> 31) & 0x00000001)  ^ (' . $r . ' <<  1) ^ ' . $k[$c][++$ki] . ';' .
                        /* S-box indexing. */
                        $l . ' = $sbox1[($b1 >> 24) & 0x3F] ^ $sbox2[($b2 >> 24) & 0x3F] ^
                                 $sbox3[($b1 >> 16) & 0x3F] ^ $sbox4[($b2 >> 16) & 0x3F] ^
                                 $sbox5[($b1 >>  8) & 0x3F] ^ $sbox6[($b2 >>  8) & 0x3F] ^
                                 $sbox7[ $b1        & 0x3F] ^ $sbox8[ $b2        & 0x3F] ^ ' . $l . ';
                    ';
                    // end of "the Feistel (F) function"

                    // swap L & R
                    [$l, $r] = [$r, $l];
                }
                [$l, $r] = [$r, $l];
            }

            // Perform the inverse IP permutation.
            $crypt_block[$c] .= '$in =
                ($shuffleinvip[($l >> 24) & 0xFF] & "\x80\x80\x80\x80\x80\x80\x80\x80") |
                ($shuffleinvip[($r >> 24) & 0xFF] & "\x40\x40\x40\x40\x40\x40\x40\x40") |
                ($shuffleinvip[($l >> 16) & 0xFF] & "\x20\x20\x20\x20\x20\x20\x20\x20") |
                ($shuffleinvip[($r >> 16) & 0xFF] & "\x10\x10\x10\x10\x10\x10\x10\x10") |
                ($shuffleinvip[($l >>  8) & 0xFF] & "\x08\x08\x08\x08\x08\x08\x08\x08") |
                ($shuffleinvip[($r >>  8) & 0xFF] & "\x04\x04\x04\x04\x04\x04\x04\x04") |
                ($shuffleinvip[ $l        & 0xFF] & "\x02\x02\x02\x02\x02\x02\x02\x02") |
                ($shuffleinvip[ $r        & 0xFF] & "\x01\x01\x01\x01\x01\x01\x01\x01");
            ';
        }

        // Creates the inline-crypt function
        $this->inline_crypt = $this->createInlineCryptFunction(
            [
               'init_crypt'    => $init_crypt,
               'init_encrypt'  => $init_encrypt,
               'init_decrypt'  => $init_decrypt,
               'encrypt_block' => $crypt_block[self::ENCRYPT],
               'decrypt_block' => $crypt_block[self::DECRYPT],
            ]
        );
    }
}
<?php

/**
 * ServiceUnavailableException
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Exception;

/**
 * ServiceUnavailableException
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class ServiceUnavailableException extends UnsupportedValueException
{
}
<?php

/**
 * UnexpectedValueException
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2022-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Exception;

/**
 * UnexpectedValueException
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class UnexpectedValueException extends \RuntimeException implements BaseException
{
}
<?php

declare(strict_types=1);

namespace phpseclib4\Exception;

class InvalidArgumentException extends InvalidStateException
{
}
<?php

/**
 * SSHChannelExitSignalException
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Exception;

/**
 * SSHChannelExitSignalException
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-suppress PossiblyUnusedProperty
 */
class SSHChannelExitSignalException extends UnexpectedValueException
{
    public string $partialOutput;

    public function __construct(
        public readonly string $signalName,
        public readonly bool $coreDumped,
        public readonly string $errorMessage
    ) {
        $message = 'Channel closed by server due to ';
        $message .= ctype_upper($signalName) ? 'SIG' . $signalName : 'signal ' . $signalName;
        if ($coreDumped) {
            $message .= ' (core dumped)';
        }
        if (strlen($errorMessage)) {
            $message .= ': ' . $errorMessage;
        }
        parent::__construct($message);
    }
}
<?php

/**
 * KeyConstraintException
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Exception;

/**
 * KeyConstraintException
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class KeyConstraintException extends UnsupportedValueException
{
}
<?php

/**
 * NoKeyLoadedException
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Exception;

/**
 * NoKeyLoadedException
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class NoKeyLoadedException extends UnexpectedValueException
{
}
<?php

declare(strict_types=1);

namespace phpseclib4\Exception;

class UnsupportedValueException extends \RuntimeException implements BaseException
{
}
<?php

/**
 * UnsupportedCurveException
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Exception;

/**
 * UnsupportedCurveException
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class UnsupportedCurveException extends UnsupportedValueException
{
}
<?php

/**
 * EOCException
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Exception;

/**
 * EOCException
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class EOCException extends UnexpectedValueException
{
}
<?php

/**
 * ResourceLimitException
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Exception;

/**
 * ResourceLimitException
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class ResourceLimitException extends UnsupportedValueException
{
}
<?php

/**
 * InvalidStateException
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Exception;

/**
 * InvalidStateException
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class InvalidStateException extends \RuntimeException implements BaseException
{
}
<?php

declare(strict_types=1);

namespace phpseclib4\Exception;

/**
 * Indicates an absent or malformed packet length header
 */
class InvalidPacketLengthException extends ConnectionClosedException
{
}
<?php

/**
 * ExcessivelyDeepDataException
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Exception;

/**
 * ExcessivelyDeepDataException
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class ExcessivelyDeepDataException extends ResourceLimitException
{
}
<?php

/**
 * InvalidModeException
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Exception;

/**
 * InvalidModeException
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class InvalidModeException extends InvalidStateException
{
}
<?php

/**
 * EncodedDataUnavailableException
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Exception;

/**
 * EncodedDataUnavailableException
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class EncodedDataUnavailableException extends UnexpectedValueException
{
}
<?php

declare(strict_types=1);

namespace phpseclib4\Exception;

/**
 * Indicates a timeout awaiting server response
 */
class TimeoutException extends \RuntimeException implements BaseException
{
}
<?php

/**
 * UnexpectedSFTPPacketException
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Exception;

/**
 * UnexpectedSFTPPacketException
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class UnexpectedSFTPPacketException extends UnexpectedValueException
{
}
<?php

/**
 * NoSupportedAlgorithmsException
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Exception;

/**
 * NoSupportedAlgorithmsException
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class NoSupportedAlgorithmsException extends UnsupportedValueException
{
}
<?php

/**
 * UnsupportedAlgorithmException
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Exception;

/**
 * UnsupportedAlgorithmException
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class UnsupportedAlgorithmException extends UnsupportedValueException
{
}
<?php

/**
 * BadConfigurationException
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Exception;

/**
 * BadConfigurationException
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class BadConfigurationException extends \RuntimeException implements BaseException
{
}
<?php

/**
 * UnexpectedSSHMessageException
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Exception;

/**
 * UnexpectedSSHMessageException
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class UnexpectedSSHMessageException extends UnexpectedValueException
{
}
<?php

declare(strict_types=1);

namespace phpseclib4\Exception;

class BadMethodCallException extends InvalidStateException
{
}
<?php

/**
 * BadDecryptionException
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Exception;

/**
 * BadDecryptionException
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class BadDecryptionException extends UnexpectedValueException
{
}
<?php

/**
 * CharacterConversionException
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Exception;

/**
 * CharacterConversionException
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class CharacterConversionException extends UnexpectedValueException
{
}
<?php

declare(strict_types=1);

namespace phpseclib4\Exception;

class LengthException extends UnsupportedValueException
{
}
<?php

/**
 * FileSystemException
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Exception;

/**
 * FileSystemException
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class FileSystemException extends UnexpectedValueException
{
}
<?php

/**
 * PasswordNeededException
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Exception;

/**
 * PasswordNeededException
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class PasswordNeededException extends UnsupportedValueException
{
}
<?php

declare(strict_types=1);

namespace phpseclib4\Exception;

/**
 * Used to mark exceptions originating from this library.
 */
interface BaseException
{
}
<?php

/**
 * ConnectionClosedException
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Exception;

/**
 * ConnectionClosedException
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class ConnectionClosedException extends UnexpectedValueException
{
}
<?php

/**
 * Common String Functions
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Common\Functions;

use ParagonIE\ConstantTime\{Base64, Base64UrlSafe, Hex};
use phpseclib4\Exception\{InvalidArgumentException, UnexpectedValueException, UnsupportedValueException};
use phpseclib4\Math\BigInteger;
use phpseclib4\Math\Common\FiniteField;

/**
 * Common String Functions
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class Strings
{
    /**
     * String Shift
     *
     * Inspired by array_shift
     */
    public static function shift(string &$string, int $index = 1): string
    {
        $substr = substr($string, 0, $index);
        $string = substr($string, $index);
        return $substr;
    }

    /**
     * String Pop
     *
     * Inspired by array_pop
     */
    public static function pop(string &$string, int $index = 1): string
    {
        $substr = substr($string, -$index);
        $string = substr($string, 0, -$index);
        return $substr;
    }

    /**
     * Parse SSH2-style string
     *
     * Returns either an array or a boolean if $data is malformed.
     *
     * Valid characters for $format are as follows:
     *
     * C = byte
     * b = boolean (true/false)
     * N = uint32
     * Q = uint64
     * s = string
     * i = mpint
     * L = name-list
     */
    public static function unpackSSH2(string $format, string &$data): array
    {
        $format = self::formatPack($format);
        $result = [];
        for ($i = 0; $i < strlen($format); $i++) {
            switch ($format[$i]) {
                case 'C':
                case 'b':
                    if (!strlen($data)) {
                        throw new UnexpectedValueException('At least one byte needs to be present for successful C / b decodes');
                    }
                    break;
                case 'N':
                case 'i':
                case 's':
                case 'L':
                    if (strlen($data) < 4) {
                        throw new UnexpectedValueException('At least four byte needs to be present for successful N / i / s / L decodes');
                    }
                    break;
                case 'Q':
                    if (strlen($data) < 8) {
                        throw new UnexpectedValueException('At least eight byte needs to be present for successful N / i / s / L decodes');
                    }
                    break;
                default:
                    throw new UnsupportedValueException('$format contains an invalid character');
            }
            switch ($format[$i]) {
                case 'C':
                    $result[] = ord(self::shift($data));
                    continue 2;
                case 'b':
                    $result[] = ord(self::shift($data)) != 0;
                    continue 2;
                case 'N':
                    [, $temp] = unpack('N', self::shift($data, 4));
                    $result[] = $temp;
                    continue 2;
                case 'Q':
                    // pack() added support for Q in PHP 5.6.3 and PHP 5.6 is phpseclib 3's minimum version
                    // so in theory we could support this BUT, "64-bit format codes are not available for
                    // 32-bit versions" and phpseclib works on 32-bit installs. on 32-bit installs
                    // 64-bit floats can be used to get larger numbers then 32-bit signed ints would allow
                    // for. sure, you're not gonna get the full precision of 64-bit numbers but just because
                    // you need > 32-bit precision doesn't mean you need the full 64-bit precision
                    ['upper' => $upper, 'lower' => $lower] = unpack('Nupper/Nlower', self::shift($data, 8));
                    $temp = $upper ? 4294967296 * $upper : 0;
                    $temp += $lower < 0 ? ($lower & 0x7FFFFFFFF) + 0x80000000 : $lower;
                    // $temp = hexdec(bin2hex(self::shift($data, 8)));
                    $result[] = $temp;
                    continue 2;
            }
            [, $length] = unpack('N', self::shift($data, 4));
            if (strlen($data) < $length) {
                throw new UnexpectedValueException("$length bytes needed; " . strlen($data) . ' bytes available');
            }
            $temp = self::shift($data, $length);
            switch ($format[$i]) {
                case 'i':
                    $result[] = new BigInteger($temp, -256);
                    break;
                case 's':
                    $result[] = $temp;
                    break;
                case 'L':
                    $result[] = explode(',', $temp);
            }
        }

        return $result;
    }

    /**
     * Create SSH2-style string
     */
    public static function packSSH2(string $format, string|int|float|array|bool|BigInteger|FiniteField\Integer ...$elements): string
    {
        $format = self::formatPack($format);
        if (strlen($format) != count($elements)) {
            throw new InvalidArgumentException('There must be as many $element arguments as there are characters in the $format string');
        }
        $result = '';
        for ($i = 0; $i < strlen($format); $i++) {
            $element = $elements[$i];
            switch ($format[$i]) {
                case 'C':
                    if (!is_int($element)) {
                        throw new UnsupportedValueException('Bytes must be represented as an integer between 0 and 255, inclusive.');
                    }
                    $result .= pack('C', $element);
                    break;
                case 'b':
                    if (!is_bool($element)) {
                        throw new UnsupportedValueException('A boolean parameter was expected.');
                    }
                    $result .= $element ? "\1" : "\0";
                    break;
                case 'Q':
                    if (!is_int($element) && !is_float($element)) {
                        throw new UnsupportedValueException('An integer was expected.');
                    }
                    // 4294967296 == 1 << 32
                    $result .= pack('NN', $element / 4294967296, $element);
                    break;
                case 'N':
                    if (is_float($element)) {
                        $element = (int) $element;
                    }
                    if (!is_int($element)) {
                        throw new UnsupportedValueException('An integer was expected.');
                    }
                    $result .= pack('N', $element);
                    break;
                case 's':
                    if (!is_string($element)) {
                        throw new UnsupportedValueException('A string was expected.');
                    }
                    $result .= pack('Na*', strlen($element), $element);
                    break;
                case 'i':
                    if (!$element instanceof BigInteger && !$element instanceof FiniteField\Integer) {
                        throw new UnsupportedValueException('A phpseclib4\Math\BigInteger or phpseclib4\Math\Common\FiniteField\Integer object was expected.');
                    }
                    $element = $element->toBytes(true);
                    $result .= pack('Na*', strlen($element), $element);
                    break;
                case 'L':
                    if (!is_array($element)) {
                        throw new UnsupportedValueException('An array was expected.');
                    }
                    $element = implode(',', $element);
                    $result .= pack('Na*', strlen($element), $element);
                    break;
                default:
                    throw new UnsupportedValueException('$format contains an invalid character');
            }
        }
        return $result;
    }

    /**
     * Expand a pack string
     *
     * Converts C5 to CCCCC, for example.
     */
    private static function formatPack(string $format): string
    {
        $parts = preg_split('#(\d+)#', $format, -1, PREG_SPLIT_DELIM_CAPTURE);
        $format = '';
        for ($i = 1; $i < count($parts); $i += 2) {
            $format .= substr($parts[$i - 1], 0, -1) . str_repeat($parts[$i - 1][-1], (int) $parts[$i]);
        }
        $format .= $parts[$i - 1];

        return $format;
    }

    /**
     * Convert binary data into bits
     *
     * bin2hex / hex2bin refer to base-256 encoded data as binary, whilst
     * decbin / bindec refer to base-2 encoded data as binary. For the purposes
     * of this function, bin refers to base-256 encoded data whilst bits refers
     * to base-2 encoded data
     */
    public static function bits2bin(string $x): string
    {
        /*
        // the pure-PHP approach is faster than the GMP approach
        if (function_exists('gmp_export')) {
             return strlen($x) ? gmp_export(gmp_init($x, 2)) : gmp_init(0);
        }
        */

        if (preg_match('#[^01]#', $x)) {
            throw new UnexpectedValueException('The only valid characters are 0 and 1');
        }

        $length = strlen($x);
        if (!$length) {
            return '';
        }
        $block_size = PHP_INT_SIZE << 3;
        $pad = $block_size - ($length % $block_size);
        if ($pad != $block_size) {
            $x = str_repeat('0', $pad) . $x;
        }

        $parts = str_split($x, $block_size);
        $str = '';
        foreach ($parts as $part) {
            $xor = $part[0] == '1' ? PHP_INT_MIN : 0;
            $part[0] = '0';
            $str .= pack(
                PHP_INT_SIZE == 4 ? 'N' : 'J',
                $xor ^ eval('return 0b' . $part . ';')
            );
        }
        return ltrim($str, "\0");
    }

    /**
     * Convert bits to binary data
     */
    public static function bin2bits(string $x, bool $trim = true): string
    {
        /*
        // the pure-PHP approach is slower than the GMP approach BUT
        // i want to the pure-PHP version to be easily unit tested as well
        if (function_exists('gmp_import')) {
            return gmp_strval(gmp_import($x), 2);
        }
        */

        $len = strlen($x);
        $mod = $len % PHP_INT_SIZE;
        if ($mod) {
            $x = str_pad($x, $len + PHP_INT_SIZE - $mod, "\0", STR_PAD_LEFT);
        }

        $bits = '';
        if (PHP_INT_SIZE == 4) {
            $digits = unpack('N*', $x);
            foreach ($digits as $digit) {
                $bits .= sprintf('%032b', $digit);
            }
        } else {
            $digits = unpack('J*', $x);
            foreach ($digits as $digit) {
                $bits .= sprintf('%064b', $digit);
            }
        }

        return $trim ? ltrim($bits, '0') : $bits;
    }

    /**
     * Switch Endianness Bit Order
     */
    public static function switchEndianness(string $x): string
    {
        $r = '';
        for ($i = strlen($x) - 1; $i >= 0; $i--) {
            $b = ord($x[$i]);
            if (PHP_INT_SIZE === 8) {
                // 3 operations
                // from http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith64BitsDiv
                $r .= chr((($b * 0x0202020202) & 0x010884422010) % 1023);
            } else {
                // 7 operations
                // from http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith32Bits
                $p1 = ($b * 0x0802) & 0x22110;
                $p2 = ($b * 0x8020) & 0x88440;
                $temp = ($p1 | $p2) * 0x10101;
                if (is_float($temp)) {
                    $temp = (int) fmod($temp, 0x7FFFFFFF);
                }
                $r .= chr(($temp >> 16) & 0xFF);
            }
        }
        return $r;
    }

    /**
     * Increment the current string
     */
    public static function increment_str(string &$var): void
    {
        if (function_exists('sodium_increment')) {
            $var = strrev($var);
            sodium_increment($var);
            $var = strrev($var);
            return;
        }

        for ($i = 4; $i <= strlen($var); $i += 4) {
            $temp = substr($var, -$i, 4);
            switch ($temp) {
                case "\xFF\xFF\xFF\xFF":
                    $var = substr_replace($var, "\x00\x00\x00\x00", -$i, 4);
                    break;
                case "\x7F\xFF\xFF\xFF":
                    $var = substr_replace($var, "\x80\x00\x00\x00", -$i, 4);
                    return;
                default:
                    $temp = unpack('Nnum', $temp);
                    $var = substr_replace($var, pack('N', $temp['num'] + 1), -$i, 4);
                    return;
            }
        }

        $remainder = strlen($var) % 4;

        if ($remainder == 0) {
            return;
        }

        $temp = unpack('Nnum', str_pad(substr($var, 0, $remainder), 4, "\0", STR_PAD_LEFT));
        $temp = substr(pack('N', $temp['num'] + 1), -$remainder);
        $var = substr_replace($var, $temp, 0, $remainder);
    }

    /**
     * Find whether the type of a variable is string (or could be converted to one)
     *
     * @psalm-assert-if-true string|\Stringable $var
     */
    public static function is_stringable(mixed $var): bool
    {
        return is_string($var) || (is_object($var) && method_exists($var, '__toString'));
    }

    /**
     * Constant Time Base64-decoding
     *
     * ParagoneIE\ConstantTime doesn't use libsodium if it's available so we'll do so
     * ourselves. see https://github.com/paragonie/constant_time_encoding/issues/39
     */
    public static function base64_decode(string $data): string
    {
        return function_exists('sodium_base642bin') ?
            sodium_base642bin($data, SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING, '=') :
            Base64::decode($data);
    }

    /**
     * Constant Time Base64-decoding (URL safe)
     */
    public static function base64url_decode(string $data): string
    {
        // return self::base64_decode(str_replace(['-', '_'], ['+', '/'], $data));

        return function_exists('sodium_base642bin') ?
            sodium_base642bin($data, SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING, '=') :
            Base64UrlSafe::decode($data);
    }

    /**
     * Constant Time Base64-encoding
     */
    public static function base64_encode(string $data): string
    {
        return function_exists('sodium_bin2base64') ?
            sodium_bin2base64($data, SODIUM_BASE64_VARIANT_ORIGINAL) :
            Base64::encode($data);
    }

    /**
     * Constant Time Base64-encoding (URL safe)
     */
    public static function base64url_encode(string $data): string
    {
        // return str_replace(['+', '/'], ['-', '_'], self::base64_encode($data));

        return function_exists('sodium_bin2base64') ?
            sodium_bin2base64($data, SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING) :
            Base64UrlSafe::encode($data);
    }

    /**
     * Constant Time Hex Decoder
     */
    public static function hex2bin(string $data): string
    {
        return function_exists('sodium_hex2bin') ?
            sodium_hex2bin($data) :
            Hex::decode($data);
    }

    /**
     * Constant Time Hex Encoder
     */
    public static function bin2hex(string $data): string
    {
        return function_exists('sodium_bin2hex') ?
            sodium_bin2hex($data) :
            Hex::encode($data);
    }
}
<?php

/**
 * Common Array Functions
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2025-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Common\Functions;

use phpseclib4\Exception\{InvalidStateException, UnexpectedValueException};

/**
 * Common Array Functions
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class Arrays
{
    /**
     * Get a reference to a subarray
     *
     * This is kinda like Laravel's Arr::set() / Arr::get() method with their dot notation
     * except that / is used as the component separator instead
     */
    public static function &subArray(array|\ArrayAccess|null &$root, string $path, bool $create = false): mixed
    {
        $false = null;

        if (!isset($root)) {
            // if you do "return false" you'll get this error:
            // Notice: Only variable references should be returned by reference
            return $false;
        }

        foreach (explode('/', $path) as $i) {
            if (!isset($root)) {
                return $false;
            }

            if (!is_array($root) && !$root instanceof \ArrayAccess) {
                return $false;
            }

            if (!isset($root[$i])) {
                if (!$create) {
                    return $false;
                }

                $root[$i] = [];
            }

            $root = &$root[$i];
        }

        return $root;
    }

    public static function &subArrayWithWildcards(array|\ArrayAccess &$root, string $path, bool $create = false): mixed
    {
        $parts = explode('/', $path);
        foreach ($parts as $k => $i) {
            if (!isset($root)) {
                $loc = implode('/', array_slice($parts, 0, $k));
                throw new UnexpectedValueException("Unable to find node for $loc");
            }

            if (!is_array($root) && !$root instanceof \ArrayAccess) {
                $loc = implode('/', array_slice($parts, 0, $k));
                throw new UnexpectedValueException("$loc isn't an array or an instance of ArrayAccess");
            }

            if ($i == '*') {
                $path = implode('/', array_slice($parts, $k + 1));
                foreach ($root as $key => $val) {
                    if (empty($path)) {
                        return $val;
                    } else {
                        $val = &self::subArrayWithWildcards($root[$key], $path, $create);
                        return $val;
                    }
                }
                $loc = implode('/', array_slice($parts, 0, $k));
                throw new UnexpectedValueException("$loc wasn't found");
            }

            if (!isset($root[$i])) {
                if (!$create) {
                    $loc = implode('/', array_slice($parts, 0, $k));
                    throw new UnexpectedValueException("$loc wasn't found and the create flag wasn't set");
                }

                $root[$i] = [];
            }

            $root = &$root[$i];
        }

        return $root;
    }

    public static function subArrayMapWithWildcards(array|\ArrayAccess &$root, string $path, \Closure $func): void
    {
        $parts = explode('/', $path);
        foreach ($parts as $k => $i) {
            if (!is_array($root) && !$root instanceof \ArrayAccess) {
                return;
            }

            if ($i == '*') {
                $path = implode('/', array_slice($parts, $k + 1));
                foreach ($root as $key => $val) {
                    if (empty($path)) {
                        $root[$key] = $func($val);
                    } elseif (is_array($root[$key]) || $root[$key] instanceof \ArrayAccess) {
                        self::subArrayMapWithWildcards($root[$key], $path, $func);
                    }
                }
                return;
            }

            if (!isset($root[$i])) {
                return;
            }

            if ($k == count($parts) - 1) {
                $root[$i] = $func($root[$i]);
                return;
            }

            $root = &$root[$i];
        }

        throw new InvalidStateException('Reached supposedly unreachable section of code');
    }
}
<?php

/**
 * Common File Functions
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Common\Functions;

use phpseclib4\Exception\FileSystemException;

/**
 * Common File Functions
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class Files
{
    /**
     * Safely open a file
     *
     * @return resource
     */
    public static function open(string $filename, string $mode)
    {
        set_error_handler(function ($errno, $errstr) use ($filename): void {
            throw new FileSystemException("Failed to open '$filename': $errstr");
        });

        try {
            $fp = @fopen($filename, $mode);
            if ($fp === false) {
                throw new FileSystemException("fopen returned false for $filename");
            }
            return $fp;
        } finally {
            restore_error_handler();
        }
    }
}
<?php

declare(strict_types=1);

namespace phpseclib4\Common;

use phpseclib4\Exception\InvalidArgumentException;

/**
 * @internal
 */
trait ConstantUtilityTrait
{
    /** @var string[]|null */
    private static ?array $valueToConstantNameMap = null;

    public static function findConstantNameByValue(string|int $value): ?string
    {
        if (!self::$valueToConstantNameMap) {
            $reflectionClass = new \ReflectionClass(static::class);
            $constantNameToValueMap = $reflectionClass->getConstants();
            self::$valueToConstantNameMap = array_flip($constantNameToValueMap);
        }
        if (isset(self::$valueToConstantNameMap[$value])) {
            return self::$valueToConstantNameMap[$value];
        }
        return null;
    }

    public static function getConstantNameByValue(string|int $value): string
    {
        $constantName = static::findConstantNameByValue($value);
        if ($constantName === null) {
            throw new InvalidArgumentException(sprintf('"%s" does not have constant with value "%s".', static::class, $value));
        }
        return $constantName;
    }
}
<?php

/**
 * Pure-PHP PFX (PKCS#12) Parser
 *
 * The PFX RFC is quite terrible, as discussed in https://www.cs.auckland.ac.nz/~pgut001/pubs/pfx.html
 *
 * PHP version 8.1+
 *
 * Encode and decode PFX files.
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2025-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File;

use phpseclib4\Crypt\Common\PrivateKey;
use phpseclib4\Crypt\{Common\AsymmetricKey, Hash, PublicKeyLoader};
use phpseclib4\Exception\{
    InvalidArgumentException,
    PasswordNeededException,
    UnexpectedValueException,
    UnsupportedAlgorithmException
};
use phpseclib4\File\ASN1\{Constructed, Element, Maps};
use phpseclib4\File\ASN1\Types\{BaseString, OctetString};
use phpseclib4\File\Common\Signable;

/**
 * Pure-PHP PFX (PKCS#12) Parser
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @implements \ArrayAccess<string, mixed>
 * @implements \Iterator<string, mixed>
 */
class PFX implements \ArrayAccess, \Countable, \Iterator
{
    use \phpseclib4\Crypt\Common\Traits\ASN1AlgorithmIdentifier;

    public Constructed|array $pfx = [
        'version' => 'v3',
        'authSafe' => [
            'contentType' => 'id-data',
            'content' => []
        ]
    ];
    private ?string $password = '';

    // same as OpenSSL
    // setting this to null will mean that a MAC isn't included
    private static ?string $defaultHashAlgorithm = 'sha256';
    // same as OpenSSL
    // https://www.rfc-editor.org/rfc/rfc7292#page-23 says this:
    // "The iteration count is recommended to be 1024 or more."
    //private static int $defaultIterationCount = 2048;
    // differs from OpenSSL, which defaults to 8, regardless of the hash
    // https://www.rfc-editor.org/rfc/rfc7292#page-23, however, states this:
    // "Ideally, the salt is as long as the output of the hash function being
    //  used and consists of completely random bits."
    private static int $defaultSaltLength = 32;

    public function __construct()
    {
        ASN1::loadOIDs('CMS');
        ASN1::loadOIDs('PFX');
    }

    public static function load(
        #[\SensitiveParameter] string|array|Constructed $pfx,
        #[\SensitiveParameter] ?string $password = null
    ): self {
        $temp = new self();
        try {
            $temp->pfx = is_string($pfx) ? self::loadString($pfx, $password) : $pfx;
        } catch (PasswordNeededException $e) {
            if (isset($password)) {
                throw $e;
            }
            $temp->pfx = self::loadString($pfx, $password = '');
        }
        $temp->password = $password;
        return $temp;
    }

    private static function loadString(
        #[\SensitiveParameter] string $pfx,
        #[\SensitiveParameter] ?string $password
    ): Constructed {
        ASN1::disableCacheInvalidation();

        $decoded = ASN1::decodeBER($pfx);
        $pfx = ASN1::map($decoded, ASN1\Maps\PFX::MAP);

        $decoded = ASN1::decodeBER($pfx['authSafe']['content']->value);
        $cms = ASN1::map($decoded, ASN1\Maps\AuthenticatedSafe::MAP);

        foreach ($cms as $key => $content) {
            switch ($content['contentType']) {
                case 'id-data': // id-data from CMS specs
                    $decoded = ASN1::decodeBER((string) $content['content']);
                    $cms[$key]['content'] = ASN1::map($decoded, ASN1\Maps\SafeContents::MAP);
                    /** @psalm-suppress UndefinedPropertyAssignment */
                    $cms[$key]['content']->parent = $cms[$key];
                    foreach ($cms[$key]['content'] as $subkey => $value) {
                        try {
                            $cms[$key]['content'][$subkey]['bagValue'] = match ("$value[bagId]") {
                                'PKCS8ShroudedKeyBag' => PublicKeyLoader::load("$value[bagValue]", $password),
                                'KeyBag' => PublicKeyLoader::load("$value[bagValue]"),
                                'CertBag' => self::handleCertBag("$value[bagValue]"),
                                default => $value['bagValue']
                            };
                        } catch (\Exception $e) {
                        }
                    }
                    break;
                case 'id-encryptedData':
                    if (!isset($password)) {
                        throw new PasswordNeededException('Encrypted data was found, however, no password has been provided');
                    }
                    $decoded = ASN1::decodeBER((string) $content['content']);
                    $content = ASN1::map($decoded, ASN1\Maps\CMSEncryptedData::MAP);
                    $cipher = self::getCryptoObjectFromAlgorithmIdentifier($content['encryptedContentInfo']['contentEncryptionAlgorithm'], $password);
                    $content = $cipher->decrypt((string) $content['encryptedContentInfo']['encryptedContent']);
                    $decoded = ASN1::decodeBER($content);
                    $contents = ASN1::map($decoded, ASN1\Maps\SafeContents::MAP);

                    foreach ($contents as $content) {
                        if ($content['bagId'] == 'CertBag') {
                            $content['bagValue'] = self::handleCertBag($content['bagValue']->value);
                        }
                    }
                    $cms[$key]['content'] = $contents;
            }
        }

        $pfx['authSafe']['content'] = $cms;
        /** @psalm-suppress UndefinedPropertyAssignment */
        $pfx['authSafe']['content']->parent = $pfx['authSafe'];

        ASN1::enableCacheInvalidation();

        return $pfx;
    }

    /**
     * Sets the default hash algorithm
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public static function setHashAlgorithm(string $algo): void
    {
        switch (strtolower($algo)) {
            case 'md2':
            case 'md5':
            case 'sha1':
            case 'sha224':
            case 'sha256':
            case 'sha384':
            case 'sha512':
            case 'sha512/224':
            case 'sha512/256':
            case 'sha3/224':
            case 'sha3/256':
            case 'sha3/384':
            case 'sha3/512':
                break;
            default:
                throw new UnsupportedAlgorithmException("$algo is an unsupported hash algorithm");
        }
        self::$defaultHashAlgorithm = $algo;
    }

    /**
     * Sets the iteration count
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public static function setIterationCount(int $count): void
    {
        self::$defaultIterationCount = $count;
    }

    /**
     * Sets the salt length
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public static function setSaltLength(int $length): void
    {
        self::$defaultSaltLength = $length;
    }

    // PrivateKey objects are designed to be immutable hence their use of withPassword()
    // PFX is *not* designed to be immutable
    public function setPassword(#[\SensitiveParameter] ?string $password = null): void
    {
        if (!isset($password)) {
            $this->removePassword();
            return;
        }
        if (isset($this->password)) { // there already is a password; simply changing the password is sufficient
            foreach ($this->pfx['authSafe']['content'] as $key => $value) {
                $value = &$this->pfx['authSafe']['content'][$key];
                switch ($value['contentType']) {
                    case 'id-data':
                        foreach ($value['content'] as $subkey => $subvalue) {
                            $subvalue = &$value['content'][$subkey];
                            if ($subvalue['bagId'] == 'PKCS8ShroudedKeyBag') {
                                $subvalue['bagValue'] = $subvalue['bagValue']->withPassword($password);
                            }
                            unset($subvalue);
                        }
                }
                unset($value);
            }
        } else { // there is no password but there will be; CertBag's need to be moved around
            $certs = $friendlyNames = $localKeyIDs = [];
            foreach ($this->pfx['authSafe']['content'] as $key => $value) {
                $value = &$this->pfx['authSafe']['content'][$key];
                if ($value['contentType'] == 'id-encryptedData') {
                    throw new UnexpectedValueException('Found id-encryptedData in an unencrypted PFX file');
                }
                if ($value['contentType'] != 'id-data') {
                    throw new UnexpectedValueException("Found $value[contentType], expected id-data");
                }
                $numCertBags = 0;
                foreach ($value['content'] as $subkey => $subvalue) {
                    $subvalue = &$value['content'][$subkey];
                    switch ($subvalue['bagId']) {
                        case 'PKCS8ShroudedKeyBag':
                            throw new UnexpectedValueException('Found PKCS8ShroudedKeyBag in an unencrypted PFX file');
                        case 'KeyBag':
                            $subvalue['bagId'] = 'PKCS8ShroudedKeyBag';
                            $subvalue['bagValue'] = $subvalue['bagValue']->withPassword($password);
                            break;
                        case 'CertBag':
                            $numCertBags++;
                    }
                    unset($subvalue);
                }
                if ($numCertBags == count($value['content'])) {
                    $value['contentType'] = 'id-encryptedData';
                    continue;
                }
                foreach ($value['content'] as $subkey => $subvalue) {
                    $subvalue = $value['content'][$subkey];
                    if ($subvalue['bagId'] == 'CertBag') {
                        if (isset($subvalue['bagAttributes'])) {
                            foreach ($subvalue['bagAttributes'] as $attr) {
                                switch ($attr['type']) {
                                    case 'pkcs-9-at-localKeyId':
                                        $var = 'localKeyIDs';
                                        break;
                                    case 'pkcs-9-at-friendlyName':
                                        $var = 'friendlyNames';
                                        break;
                                    default:
                                        continue 2;
                                }
                                foreach ($attr['value'] as $attrValue) {
                                    $$var[count($certs)][] = $attrValue;
                                }
                            }
                        }
                        $certs[] = $subvalue['bagValue']['certValue'];
                        unset($value['content'][$subkey]);
                    }
                }
                if ($numCertBags) {
                    if ($value['content'] instanceof Constructed) {
                        $value['content']->rekey();
                    } else {
                        $value['content'] = array_values($value['content']);
                    }
                }
                unset($value);
            }
            foreach ($certs as $i => $cert) {
                $this->add($cert, friendlyName: $friendlyNames[$i] ?? null, localKeyID: $localKeyIDs[$i] ?? null);
            }
        }
        $this->password = $password;
    }

    public function removePassword(): void
    {
        if (!isset($this->password)) {
            return;
        }

        $this->password = null;

        foreach ($this->pfx['authSafe']['content'] as $key => $value) {
            $value = &$this->pfx['authSafe']['content'][$key];
            switch ($value['contentType']) {
                case 'id-data':
                    foreach ($value['content'] as $subkey => $subvalue) {
                        $subvalue = &$value['content'][$subkey];
                        if ($subvalue['bagId'] == 'PKCS8ShroudedKeyBag') {
                            $subvalue['bagId'] = 'KeyBag';
                            $subvalue['bagValue'] = $subvalue['bagValue']->withoutPassword();
                        }
                        unset($subvalue);
                    }
                    break;
                case 'id-encryptedData':
                    $value['contentType'] = 'id-data';
            }
            unset($value);
        }
        unset($this->pfx['macData']);
    }

    /**
     * @var string|BaseString|string[]|BaseString[]|null $friendlyName
     * @var string|BaseString|string[]|BaseString[]|null $localKeyID
     */
    public function add(
        #[\SensitiveParameter] X509|PrivateKey $obj,
        string|BaseString|array|null $friendlyName = null,
        string|BaseString|array|null $localKeyID = null
    ): void {
        $extra = [];
        if (isset($friendlyName) || isset($localKeyID)) {
            if (isset($friendlyName)) {
                if (!is_array($friendlyName)) {
                    $value = [$friendlyName];
                } else {
                    foreach ($friendlyName as $temp) {
                        if (!is_string($temp) && !$temp instanceof BaseString) {
                            throw new UnexpectedValueException('Arrays must contain either strings or instances of BaseString');
                        }
                    }
                    $value = $friendlyName;
                }
                if (count($value)) {
                    $extra['bagAttributes'][] = [
                        'type' => 'pkcs-9-at-friendlyName',
                        'value' => $value,
                    ];
                }
            }
            if (isset($localKeyID)) {
                if (!is_array($localKeyID)) {
                    $value = [$localKeyID];
                } else {
                    foreach ($localKeyID as $temp) {
                        if (!is_string($temp) && !$temp instanceof BaseString) {
                            throw new UnexpectedValueException('Arrays must contain either strings or instances of BaseString');
                        }
                    }
                    $value = $localKeyID;
                }
                if (count($value)) {
                    $extra['bagAttributes'][] = [
                        'type' => 'pkcs-9-at-localKeyId',
                        'value' => $value,
                    ];
                }
            }
        }
        if ($obj instanceof PrivateKey) {
            if (isset($this->password)) {
                $obj = [
                    'bagId' => 'PKCS8ShroudedKeyBag',
                    'bagValue' => $obj->withPassword($this->password),
                ] + $extra;
            } else {
                $obj = [
                    'bagId' => 'KeyBag',
                    'bagValue' => $obj->withoutPassword(),
                ] + $extra;
            }
            $this->pfx['authSafe']['content'][] = [
                'contentType' => 'id-data',
                'content' => [$obj]
            ];
        } else {
            $obj = [
                'bagId' => 'CertBag',
                'bagValue' => [
                    'certId' => 'x509Certificate',
                    'certValue' => $obj,
                ]
            ] + $extra;
            $this->pfx['authSafe']['content'][] = [
                'contentType' => isset($this->password) ? 'id-encryptedData' : 'id-data',
                'content' => [$obj]
            ];
        }
    }

    public function toString(array $options = []): string
    {
        foreach ($this->pfx['authSafe']['content'] as $key => $value) {
            $value = &$this->pfx['authSafe']['content'][$key];
            switch ($value['contentType']) {
                case 'id-data':
                    foreach ($value['content'] as $subkey => $subvalue) {
                        $subvalue = &$value['content'][$subkey];
                        switch ($subvalue['bagId']) {
                            case 'CertBag':
                                $subvalue['bagValue']['certValue'] = new OctetString($subvalue['bagValue']['certValue']->getEncoded());
                                $subvalue['bagValue'] = new Element(ASN1::encodeDER($subvalue['bagValue'], Maps\CertBag::MAP));
                                break;
                            case 'KeyBag':
                            case 'PKCS8ShroudedKeyBag':
                                $subvalue['bagValue'] = new Element($subvalue['bagValue']->toString('PKCS8', ['binary' => true]));
                        }
                        $subvalue = new Element(ASN1::encodeDER($subvalue, Maps\SafeBag::MAP));
                        unset($subvalue);
                    }
                    $value['content'] = new OctetString(ASN1::encodeDER($value['content'], Maps\AuthenticatedSafe::MAP));
                    break;
                case 'id-encryptedData':
                    $crypt = self::getCryptoObjectFromParams($this->password, $options);
                    foreach ($value['content'] as $subkey => $subvalue) {
                        $subvalue = &$value['content'][$subkey];
                        if ($subvalue['bagId'] != 'CertBag') {
                            break;
                        }
                        $subvalue['bagValue']['certValue'] = new OctetString($subvalue['bagValue']['certValue']->getEncoded());
                        $subvalue['bagValue'] = new Element(ASN1::encodeDER($subvalue['bagValue'], Maps\CertBag::MAP));
                        $subvalue = new Element(ASN1::encodeDER($subvalue, Maps\SafeBag::MAP));
                        unset($subvalue);
                    }
                    $value['content'] = ASN1::encodeDER($value['content'], Maps\SafeContents::MAP);
                    $value['content'] = new OctetString($crypt->encrypt($value['content']));
                    $value['content'] = [
                        'version' => 'v0',
                        'encryptedContentInfo' => [
                            'contentType' => 'id-data',
                            'contentEncryptionAlgorithm' => $crypt->getMetaData('algorithmIdentifier'),
                            'encryptedContent' => $value['content'],
                        ],
                    ];
                    $value['content'] = new Element(ASN1::encodeDER($value['content'], Maps\CMSEncryptedData::MAP));
            }
            unset($value);
        }

        $this->pfx['authSafe']['content'] = new OctetString(ASN1::encodeDER($this->pfx['authSafe']['content'], Maps\AuthenticatedSafe::MAP));

        $hashAlgorithm = $options['hashAlgorithm'] ?? self::$defaultHashAlgorithm;
        $saltLength = $options['saltLength'] ?? self::$defaultSaltLength;
        $iterationCount = $options['iterationCount'] ?? self::$defaultIterationCount;
        if ($hashAlgorithm && isset($this->password)) {
            $salt = random_bytes($saltLength);
            $hash = new Hash($hashAlgorithm);
            $hash->setPassword($this->password, $salt, $iterationCount);
            $mac = $hash->hash($this->pfx['authSafe']['content']->value);
            $this->pfx['macData'] = [
                'mac' => [
                    'digestAlgorithm' => [
                        'algorithm' => 'id-' . $hashAlgorithm,
                        'parameters' => null,
                    ],
                    'digest' => $mac,
                ],
                'macSalt' => $salt,
                'iterations' => $iterationCount,
            ];
        }

        $output = ASN1::encodeDER($this->pfx, Maps\PFX::MAP);

        $temp = self::load($output, $this->password);
        $this->pfx = $temp->pfx;

        return $output;
    }

    public function toArray(): array
    {
        $this->compile();
        return $this->pfx instanceof Constructed ? $this->pfx->toArray() : $this->pfx;
    }

    private static function handleCertBag(string $value): Constructed
    {
        $decoded = ASN1::decodeBER($value);
        $result = ASN1::map($decoded, ASN1\Maps\CertBag::MAP);
        if ($result['certId'] == 'x509Certificate') {
            $result['certValue'] = X509::load((string) $result['certValue']);
        }
        return $result;
    }

    public function __toString(): string
    {
        return $this->toString();
    }

    public function &offsetGet(mixed $offset): mixed
    {
        $this->compile();
        /** @psalm-suppress NonVariableReferenceReturn */
        return $this->pfx[$offset];
    }

    public function offsetExists(mixed $offset): bool
    {
        return isset($this->pfx[$offset]);
    }

    public function offsetSet(mixed $offset, mixed $value): void
    {
        $this->pfx[$offset] = $value;
    }

    public function offsetUnset(mixed $offset): void
    {
        unset($this->pfx[$offset]);
    }

    public function count(): int
    {
        return is_array($this->pfx) ? count($this->pfx) : $this->pfx->count();
    }

    public function rewind(): void
    {
        $this->compile();
        $this->pfx->rewind();
    }

    public function current(): mixed
    {
        $this->compile();
        return $this->pfx->current();
    }

    public function key(): mixed
    {
        $this->compile();
        return $this->pfx->key();
    }

    public function next(): void
    {
        $this->compile();
        $this->pfx->next();
    }

    public function valid(): bool
    {
        $this->compile();
        return $this->pfx->valid();
    }

    public function keys(): array
    {
        return $this->pfx instanceof Constructed ? $this->pfx->keys() : array_keys($this->pfx);
    }

    public function __debugInfo(): array
    {
        $this->compile();
        return $this->pfx->__debugInfo();
    }

    public function getEncoded(): string
    {
        $this->compile();
        return $this->toString();
    }

    private function compile(): void
    {
        if (!$this->pfx instanceof Constructed) {
            $temp = self::load("$this", $this->password);
            $this->pfx = $temp->pfx;
        }
        if ($this->pfx->hasEncoded()) {
            return;
        }
        "$this";
    }

    private function getNames(string $type): array
    {
        $names = [];

        foreach ($this->pfx['authSafe']['content'] as $key => $value) {
            $value = $this->pfx['authSafe']['content'][$key];
            foreach ($value['content'] as $subkey => $subvalue) {
                $subvalue = $value['content'][$subkey];
                if (isset($subvalue['bagAttributes'])) {
                    foreach ($subvalue['bagAttributes'] as $attr) {
                        if ($attr['type'] == $type) {
                            foreach ($attr['value'] as $subattr) {
                                $names[] = $subattr;
                            }
                        }
                    }
                }
            }
        }

        return array_values(array_unique($names));
    }

    public function getFriendlyNames(): array
    {
        return $this->getNames('pkcs-9-at-friendlyName');
    }

    public function getLocalKeyIDs(): array
    {
        return $this->getNames('pkcs-9-at-localKeyId');
    }

    private function pluck(?string $type = null, string|BaseString|null $search = null): array
    {
        $objects = [];

        foreach ($this->pfx['authSafe']['content'] as $key => $value) {
            $value = $this->pfx['authSafe']['content'][$key];
            foreach ($value['content'] as $subkey => $subvalue) {
                if (isset($type)) {
                    if (!isset($subvalue['bagAttributes'])) {
                        continue;
                    }
                    $matchFound = false;
                    foreach ($subvalue['bagAttributes'] as $attr) {
                        if ($attr['type'] == $type) {
                            foreach ($attr['value'] as $subattr) {
                                if (is_string($search) || $search::class == $subattr::class) {
                                    if ($subattr instanceof BaseString) {
                                        if ($subattr->isConvertable()) {
                                            $subattr = $subattr->toUTF8String();
                                        }
                                    }
                                    if ("$subattr" == "$search") {
                                        $matchFound = true;
                                    }
                                }
                            }
                        }
                    }
                    if (!$matchFound) {
                        continue;
                    }
                }
                $subvalue = $value['content'][$subkey];
                switch ($subvalue['bagId']) {
                    case 'KeyBag':
                    case 'PKCS8ShroudedKeyBag':
                        $objects[] = $subvalue['bagValue'];
                        break;
                    case 'CertBag':
                        $objects[] = $subvalue['bagValue']['certValue'];
                }
            }
        }

        return $objects;
    }

    public function getAll(): array
    {
        return $this->pluck();
    }

    public function getCertificates(): array
    {
        $arr = $this->getAll();
        $x509 = [];
        foreach ($arr as $el) {
            if ($el instanceof X509) {
                $x509[] = $el;
            }
        }
        return $x509;
    }

    public function getPrivateKeys(): array
    {
        $arr = $this->getAll();
        $keys = [];
        foreach ($arr as $el) {
            if ($el instanceof PrivateKey) {
                $keys[] = $el;
            }
        }
        return $keys;
    }

    public function pluckByFriendlyName(string|BaseString $value): array
    {
        return $this->pluck('pkcs-9-at-friendlyName', $value);
    }

    public function pluckByLocalKeyID(string|BaseString $value): array
    {
        return $this->pluck('pkcs-9-at-localKeyId', $value);
    }

    public function pfxFromFriendlyName(string|BaseString $value): self
    {
        $temp = new self();
        $temp->password = $this->password;
        foreach ($this->pluckByFriendlyName($value) as $object) {
            $temp->add($object);
        }
        return $temp;
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function pfxFromLocalKeyID(string|BaseString $value): self
    {
        $temp = new self();
        $temp->password = $this->password;
        foreach ($this->pluckByLocalKeyID($value) as $object) {
            $temp->add($object);
        }
        return $temp;
    }

    public function sign(string|Signable $source): string
    {
        $objects = $this->getAll();
        $message = 'Signatures can only be performed if there are *exactly* one private key and one matching X509 cert OR if there is just one private key, no more no less';
        if (count($objects) > 2 || !count($objects)) {
            throw new InvalidArgumentException("$message - x1");
        }
        if (count($objects) == 1) {
            $private = $objects[0];
            if (!$private instanceof PrivateKey) {
                throw new InvalidArgumentException("$message - x2");
            }
        } else {
            switch (true) {
                case $objects[0] instanceof PrivateKey && $objects[1] instanceof X509:
                    $private = $objects[0];
                    $public = $objects[1];
                    break;
                case $objects[0] instanceof X509 && $objects[1] instanceof PrivateKey:
                    $public = $objects[0];
                    $private = $objects[1];
                    break;
                default:
                    throw new InvalidArgumentException("$message - x3");
            }
            $publicKey = (string) $public->getPublicKey();
            $privateKey = $private->getPublicKey()->toString('PKCS8');
            if ($publicKey != $privateKey) {
                throw new InvalidArgumentException("$message - x4");
            }
        }

        if (isset($public)) {
            $source->copySigningX509Attributes($public);
        }
        /** @var string $signature */
        $signature = $private->sign($source);

        return $signature;
    }
}
<?php

/**
 * Pure-PHP CRL Parser
 *
 * PHP version 8.1+
 *
 * Encode and decode Certificate Revocation Lists (CRL).
 *
 * The extensions are from {@link http://tools.ietf.org/html/rfc5280 RFC5280} and
 * {@link http://web.archive.org/web/19961027104704/http://www3.netscape.com/eng/security/cert-exts.html Netscape Certificate Extensions}.
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2025-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File;

use phpseclib4\Common\Functions\{Arrays, Strings};
use phpseclib4\Crypt\Common\PrivateKey;
use phpseclib4\Exception\UnexpectedValueException;
use phpseclib4\File\ASN1\{Constructed, Element, Maps};
use phpseclib4\File\ASN1\Maps\CRLReason;
use phpseclib4\File\ASN1\Types\{BitString, OctetString};
use phpseclib4\File\Common\Signable;
use phpseclib4\Math\BigInteger;

/**
 * Pure-PHP CRL Parser
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @implements \ArrayAccess<string, mixed>
 * @implements \Iterator<string, mixed>
 */
class CRL implements \ArrayAccess, \Countable, \Iterator, Signable
{
    use \phpseclib4\File\Common\Traits\Extension;
    use \phpseclib4\File\Common\Traits\DN;
    use \phpseclib4\File\Common\Traits\ASN1Signature;

    /**
     * Return internal array representation
     *
     * @see \phpseclib4\File\X509::getDN()
     */
    public const DN_ARRAY = 0;
    /**
     * Return string
     *
     * @see \phpseclib4\File\X509::getDN()
     */
    public const DN_STRING = 1;
    /**
     * Return ASN.1 name string
     *
     * @see \phpseclib4\File\X509::getDN()
     */
    public const DN_ASN1 = 2;
    /**
     * Return OpenSSL compatible array
     *
     * @see \phpseclib4\File\X509::getDN()
     */
    public const DN_OPENSSL = 3;
    /**
     * Return canonical ASN.1 RDNs string
     *
     * @see \phpseclib4\File\X509::getDN()
     */
    public const DN_CANON = 4;
    /**
     * Return name hash for file indexing
     *
     * @see \phpseclib4\File\X509::getDN()
     */
    public const DN_HASH = 5;

    private Constructed|array $crl;
    private bool $hideFullDecode = false;

    /**
     * Binary key flag
     */
    private static bool $binary = false;

    public function __construct()
    {
        ASN1::loadOIDs('X509');

        $date = new \DateTimeImmutable('now', new \DateTimeZone(@date_default_timezone_get()));

        $this->crl = [
            'tbsCertList' => [
                'version' => 'v2',
                'signature' => ['algorithm' => '0.0'],
                'issuer' => ['rdnSequence' => []],
                'thisUpdate' => ['utcTime' => $date->format('Y-m-d H:i:s')],
            ],
            'signatureAlgorithm' => [
                'algorithm' => '0.0',
            ],
            'signature' => "\0",
        ];
    }

    public static function load(string|array|Constructed $crl, int $mode = ASN1::FORMAT_AUTO_DETECT): CRL
    {
        $new = new self();
        $new->crl = is_string($crl) ? self::loadString($crl, $mode) : $crl;
        return $new;
    }

    private static function loadString(string $crl, int $mode): Constructed
    {
        if ($mode != ASN1::FORMAT_DER) {
            $newcrl = ASN1::extractBER($crl);
            if ($mode == ASN1::FORMAT_PEM && $crl == $newcrl) {
                throw new UnexpectedValueException('Unable to decode PEM');
            }
            $crl = $newcrl;
        }

        $decoded = ASN1::decodeBER($crl);

        $rules = [];
        $rules['tbsCertList']['issuer']['rdnSequence']['*']['*'] = self::mapInDNs(...);
        $rules['tbsCertList']['crlExtensions']['*'] = self::mapInExtensions(...);
        $rules['tbsCertList']['revokedCertificates']['*']['crlEntryExtensions']['*'] = self::mapInExtensions(...);

        return ASN1::map($decoded, Maps\CertificateList::MAP, $rules);
    }

    public function __debugInfo(): array
    {
        if ($this->hideFullDecode) {
            $this->hideFullDecode = false;
            return ['value' => (string) $this];
        }
        $this->compile();
        return $this->crl->__debugInfo();
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function keys(): array
    {
        return $this->crl instanceof Constructed ? $this->crl->keys() : array_keys($this->crl);
    }

    public function &offsetGet(mixed $offset): mixed
    {
        $this->compile();
        /** @psalm-suppress NonVariableReferenceReturn */
        return $this->crl[$offset];
    }

    public function offsetExists(mixed $offset): bool
    {
        return isset($this->crl[$offset]);
    }

    public function offsetSet(mixed $offset, mixed $value): void
    {
        $this->crl[$offset] = $value;
    }

    public function offsetUnset(mixed $offset): void
    {
        unset($this->crl[$offset]);
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function getDNProps(string $propName): array
    {
        return $this->getIssuerDNProps($propName);
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function hasDNProp(string $propName): bool
    {
        return $this->hasIssuerNProp($propName);
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function getDN(int $format = self::DN_STRING): array|string
    {
        return $this->getIssuerDN($format);
    }

    public function setDN(array|string|Element $props): void
    {
        $this->setIssuerDN($props);
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function resetDN(): void
    {
        self::resetIssuerDN();
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function hasIssuerDNProp(string $propName): bool
    {
        return static::hasDNPropsInternal($this->crl['tbsCertList']['issuer'], $propName);
    }

    public function getIssuerDNProps(string $propName): array
    {
        return self::retrieveDNProps($this->crl['tbsCertList']['issuer'], $propName);
    }

    public function getIssuerDN(int $format = self::DN_STRING): array|string
    {
        return self::formatDN($this->crl['tbsCertList']['issuer'], $format);
    }

    public function setIssuerDN(array|string|Element $props): void
    {
        self::setDNInternal($this->crl['tbsCertList']['issuer'], $props);
    }

    public function resetIssuerDN(): void
    {
        self::setDNInternal($this->crl['tbsCertList']['issuer'], []);
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function isRevoked(BigInteger|X509 $sn): bool
    {
        $idx = $this->getRevokedIndex($sn);
        return isset($idx);
    }

    public function getRevokedInfo(BigInteger|X509 $sn): ?array
    {
        $this->compile();
        $idx = $this->getRevokedIndex($sn);
        return isset($idx) ? $this->crl['tbsCertList']['revokedCertificates'][$idx]->toArray() : null;
    }

    public function getRevokedIndex(BigInteger|X509 $sn): ?int
    {
        if ($sn instanceof X509) {
            $sn = $sn['tbsCertificate']['serialNumber'];
        }
        $list = &$this->crl['tbsCertList']['revokedCertificates'];
        $total = count($list);
        for ($i = 0; $i < $total; $i++) {
            if ($list[$i]['userCertificate']->equals($sn)) {
                unset($list[$i]->decoded);
                return $i;
            }
            unset($list[$i]->decoded);
        }
        return null;
    }

    public function getRevokedAsArray(): array
    {
        $result = [];
        $list = &$this->crl['tbsCertList']['revokedCertificates'];
        $total = count($list);
        for ($i = 0; $i < $total; $i++) {
            $temp = ['revocationDate' => (string) $list[$i]['revocationDate']];
            if (isset($list[$i]['crlEntryExtensions'])) {
                foreach ($list[$i]['crlEntryExtensions'] as $entry) {
                    if ($entry['extnId'] == 'id-ce-cRLReasons') {
                        $temp['reason'] = (string) $entry['extnValue'];
                        break;
                    }
                }
            }
            $hex = $list[$i]['userCertificate']->toHex();
            if (!isset($result[$hex])) {
                $result[$hex] = $temp;
            } else {
                if (isset($result[$hex]['revocationDate'])) {
                    $result[$hex] = [$result[$hex]];
                }
                $result[$hex][] = $temp;
            }
            unset($list[$i]->decoded);
        }
        return $result;
    }

    public function numRevoked(): int
    {
        return count($this->crl['tbsCertList']['revokedCertificates']);
    }

    public function getRevokedByIndex(int $idx): ?array
    {
        $this->compile();
        return $this->crl['tbsCertList']['revokedCertificates'][$idx]?->toArray() ?? null;
    }

    public function revoke(BigInteger|X509 $cert, ?string $reason = null, \DateTimeInterface|string|null $date = null): void
    {
        static $validReasons;
        if (!isset($validReasons)) {
            $validReasons = [];
            foreach (self::listValidRevocationReasons() as $subreason) {
                $validReasons[strtolower($subreason)] = $subreason;
            }
        }
        $temp = [];
        $temp['userCertificate'] = $cert instanceof X509 ? $cert['tbsCertificate']['serialNumber'] : $cert;
        $temp['revocationDate'] = ASN1::formatTime($date ?? 'now');
        if (isset($reason)) {
            $lower = strtolower($reason);
            if (!isset($validReasons[$lower])) {
                throw new UnexpectedValueException('Invalid reason presented - call CRL::listValidRevocationReasons() to see a list of valid reasons');
            }
            $temp['crlEntryExtensions'][] = [
                'extnId' => 'id-ce-cRLReasons',
                'critical' => false,
                'extnValue' => $validReasons[$lower],
            ];
        }

        $this->crl['tbsCertList']['revokedCertificates'][] = $temp;
    }

    // returns true if the cert you're trying to revoke is in the cert
    // and false if it's not
    /** @psalm-suppress PossiblyUnusedMethod */
    public function unrevoke(BigInteger|X509 $cert): bool
    {
        $idx = $this->getRevokedIndex($cert);
        if (!isset($idx)) {
            return false;
        }
        unset($this->crl['tbsCertList']['revokedCertificates'][$idx]);
        return true;
    }

    /**
     * Alias for setThisDate
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function setLastDate(\DateTimeInterface|string $date): void
    {
        $this->setThisDate($date);
    }

    /**
     * Set this date
     */
    public function setThisDate(\DateTimeInterface|string $date): void
    {
        $this->crl['tbsCertList']['thisUpdate'] = ASN1::formatTime($date);
    }

    /**
     * Set next date
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function setNextDate(\DateTimeInterface|string $date): void
    {
        $this->crl['tbsCertList']['nextUpdate'] = ASN1::formatTime($date);
    }

    public static function listValidRevocationReasons(): array
    {
        return array_values(CRLReason::MAP['mapping']);
    }

    public function toArray(bool $convertPrimitives = false): array
    {
        $this->compile();
        return $this->crl instanceof Constructed ? $this->crl->toArray($convertPrimitives) : $this->crl;
    }

    /**
     * Enable binary output (DER)
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public static function enableBinaryOutput(): void
    {
        self::$binary = true;
    }

    /**
     * Disable binary output (ie. enable PEM)
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public static function disableBinaryOutput(): void
    {
        self::$binary = false;
    }

    public function toString(array $options = []): string
    {
        $this->mapOutDNs();
        $this->mapOutExtensions();

        $crl = ASN1::encodeDER($this->crl, Maps\CertificateList::MAP);

        if ($options['binary'] ?? self::$binary) {
            return $crl;
        }

        return "-----BEGIN X509 CRL-----\r\n" . chunk_split(Strings::base64_encode($crl), 64) . '-----END X509 CRL-----';
    }

    public function __toString(): string
    {
        return $this->toString();
    }

    private function mapOutDNs(): void
    {
        $dns = &Arrays::subArray($this->crl, 'tbsCertList/issuer/rdnSequence');
        if (!$dns) {
            return;
        }

        self::mapOutDNsInner($dns);
    }

    private function mapOutExtensions(): void
    {
        $extensions = &Arrays::subArray($this->crl, 'tbsCertList/crlExtensions');
        if ($extensions) {
            self::mapOutExtensionsHelper($extensions);
        }
        $rclist = &Arrays::subArray($this->crl, 'tbsCertList/revokedCertificates');
        if ($rclist) {
            foreach ($rclist as $i => $extension) {
                $extension = &Arrays::subArray($rclist, "$i/crlEntryExtensions");
                if ($extension) {
                    self::mapOutExtensionsHelper($extension);
                }
                unset($extension);
            }
        }
    }

    private function compile(): void
    {
        if (!$this->crl instanceof Constructed) {
            $temp = self::load("$this");
            $this->crl = $temp->crl;
        }
        if ($this->crl->hasEncoded()) {
            return;
        }
        $temp = self::load("$this");
        $this->crl = $temp->crl;
    }

    public function getSignableSection(): string
    {
        $this->compile();
        return $this->crl['tbsCertList']->getEncoded();
    }

    public function setSignature(string $signature): void
    {
        $this->crl['signature'] = new BitString("\0$signature");
    }

    /**
     * Identify signature algorithm from private key
     *
     * @throws \phpseclib4\Exception\UnsupportedAlgorithmException if the algorithm is unsupported
     */
    public function identifySignatureAlgorithm(PrivateKey $key): void
    {
        $algorithm = self::identifySignatureAlgorithmHelper($key);
        $this->crl['tbsCertList']['signature'] = $algorithm;
        $this->crl['signatureAlgorithm'] = $algorithm;
    }

    public function copySigningX509Attributes(X509 $x509): void
    {
        $this->setIssuerDN($x509->getSubjectDN(X509::DN_ARRAY));
        $subjectKeyIdentifier = $x509->getExtension('id-ce-subjectKeyIdentifier');
        if (isset($subjectKeyIdentifier)) {
            $this->setAuthorityKeyIdentifier($subjectKeyIdentifier['extnValue']);
        }
    }

    public function setAuthorityKeyIdentifier(string|OctetString $value): void
    {
        if (is_string($value)) {
            $value = new OctetString($value);
        }
        $this->setExtension('id-ce-authorityKeyIdentifier', [
            //'authorityCertIssuer' => [
            //    [
            //        'directoryName' => $issuer['tbsCertificate']['subject']
            //    ]
            //],
            //'authorityCertSerialNumber' => $issuer['tbsCertificate']['serialNumber'],
            'keyIdentifier' => $value,
        ]);
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function listExtensions(): array
    {
        if (!isset($this->crl['tbsCertList']['crlExtensions'])) {
            return [];
        }
        $exts = [];
        foreach ($this->crl['tbsCertList']['crlExtensions'] as $ext) {
            $exts[] = (string) $ext['extnId'];
        }
        return $exts;
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function listRevokedExtensions(BigInteger|X509 $cert): array
    {
        $r = $this->getRevokedInfo($cert);
        if (!isset($r) || !isset($r['crlEntryExtensions'])) {
            return [];
        }
        $exts = [];
        foreach ($r['crlEntryExtensions'] as $ext) {
            $exts[] = (string) $ext['extnId'];
        }
        return $exts;
    }

    // returns the first instance of an extension even if there are multiple instances
    // or null if said extension isn't present
    public function getExtension(string $name): ?array
    {
        $this->compile();
        if (!isset($this->crl['tbsCertList']['crlExtensions'])) {
            return null;
        }
        foreach ($this->crl['tbsCertList']['crlExtensions'] as $ext) {
            if (self::extensionMatch($name, $ext['extnId'])) {
                return [
                    'extnId' => $name,
                    'extnValue' => $ext['extnValue'],
                    'critical' => is_bool($ext['critical']) ? $ext['critical'] : $ext['critical']->value
                ];
            }
        }
        return null;
    }

    // returns the first instance of an extension even if there are multiple instances
    // or null if said extension isn't present
    public function getRevokedExtension(BigInteger|X509 $cert, string $name): ?array
    {
        $this->compile();
        $r = $this->getRevokedInfo($cert);
        if (!isset($r) || !isset($r['crlEntryExtensions'])) {
            return null;
        }
        foreach ($r['crlEntryExtensions'] as $ext) {
            if (self::extensionMatch($name, $ext['extnId'])) {
                return [
                    'extnId' => $ext['extnId'],
                    'extnValue' => $ext['extnValue'],
                    'critical' => is_bool($ext['critical']) ? $ext['critical'] : $ext['critical']->value
                ];
            }
        }
        return null;
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function hasExtension(string $name): bool
    {
        if (!isset($this->crl['tbsCertificate']['crlExtensions'])) {
            return false;
        }
        foreach ($this->crl['tbsCertificate']['crlExtensions'] as $ext) {
            if ("$ext[extnId]" == $name) {
                return true;
            }
        }
        return false;
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function hasRevokedExtension(BigInteger|X509 $cert, string $name): bool
    {
        $r = $this->getRevokedInfo($cert);
        if (!isset($r) || !isset($r['crlEntryExtensions'])) {
            return false;
        }
        foreach ($r['crlEntryExtensions'] as $ext) {
            if ("$ext[extnId]" == $name) {
                return true;
            }
        }
        return false;
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function removeExtension(string $name): void
    {
        if (!isset($this->crl['tbsCertList']['crlExtensions'])) {
            return;
        }
        foreach ($this->crl['tbsCertList']['crlExtensions'] as $i => $ext) {
            if ("$ext[extnId]" == $name) {
                unset($this->crl['tbsCertList']['crlExtensions'][$i]);
            }
        }
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function removeRevokedExtension(BigInteger|X509 $cert, string $name): void
    {
        $idx = $this->getRevokedIndex($cert);
        if (!isset($idx)) {
            return;
        }
        $revoked = &$this->crl['tbsCertList']['revokedCertificates'][$idx];
        if (!isset($revoked['crlEntryExtensions'])) {
            return;
        }
        foreach ($revoked['crlEntryExtensions'] as $i => $ext) {
            if ("$ext[extnId]" == $name) {
                unset($revoked['crlEntryExtensions'][$i]);
            }
        }
    }

    // $value could be array|Constructed|Element|BaseType|int|float|bool|string|null
    // and by array that means any of the non-array types in any combination. eg.
    // [Constructed, int] would be sufficient as would [array, BaseType]
    public function setExtension(string $name, mixed $value, ?bool $critical = null): void
    {
        $origCritical = $critical;
        if (!isset($critical)) {
            $critical = self::getExtensionCriticalValue($name);
        }
        if (isset($this->crl['tbsCertList']['crlExtensions'])) {
            foreach ($this->crl['tbsCertList']['crlExtensions'] as $i => $ext) {
                $ext = &$this->crl['tbsCertList']['crlExtensions'][$i];
                if ("$ext[extnId]" == $name) {
                    $ext['extnValue'] = $value;
                    if (isset($origCritical)) {
                        $ext['critical'] = $origCritical;
                    }
                    return;
                }
                unset($ext);
            }
        } else {
            $this->crl['tbsCertList']['crlExtensions'] = [];
        }

        $this->crl['tbsCertList']['crlExtensions'][] = [
            'extnId' => $name,
            'critical' => $critical,
            'extnValue' => $value,
        ];
    }

    // $value could be array|Constructed|Element|BaseType|int|float|bool|string|null
    // and by array that means any of the non-array types in any combination. eg.
    // [Constructed, int] would be sufficient as would [array, BaseType]
    // if $cert doesn't exist in the CRL this will *not* add it. if you want to revoke
    // a new cert call revoke().
    // if an extension is present multiple times only the first instance will be updated
    /** @psalm-suppress PossiblyUnusedMethod */
    public function setRevokedExtension(BigInteger|X509 $cert, string $name, mixed $value, ?bool $critical = null): void
    {
        $idx = $this->getRevokedIndex($cert);
        if (!isset($idx)) {
            return;
        }
        $origCritical = $critical;
        if (!isset($critical)) {
            $critical = self::getExtensionCriticalValue($name);
        }
        $revoked = &$this->crl['tbsCertList']['revokedCertificates'][$idx];
        if (!isset($revoked['crlEntryExtensions'])) {
            $revoked['crlEntryExtensions'][] = [
                'extnId' => $name,
                'critical' => $critical,
                'extnValue' => $value,
            ];
            return;
        }
        foreach ($revoked['crlEntryExtensions'] as $i => $ext) {
            $ext = &$revoked['crlEntryExtensions'][$i];
            if ("$ext[extnId]" == $name) {
                $ext['extnValue'] = $value;
                if (isset($origCritical)) {
                    $ext['critical'] = $origCritical;
                }
                return;
            }
        }
        unset($ext);
        $revoked['crlEntryExtensions'][] = [
            'extnId' => $name,
            'critical' => $critical,
            'extnValue' => $value,
        ];
    }

    public function count(): int
    {
        return is_array($this->crl) ? count($this->crl) : $this->crl->count();
    }

    public function rewind(): void
    {
        $this->compile();
        $this->crl->rewind();
    }

    public function current(): mixed
    {
        $this->compile();
        return $this->crl->current();
    }

    public function key(): mixed
    {
        $this->compile();
        return $this->crl->key();
    }

    public function next(): void
    {
        $this->compile();
        $this->crl->next();
    }

    public function valid(): bool
    {
        $this->compile();
        return $this->crl->valid();
    }

    /**
     * Validate a signature
     *
     * Returns true if the signature is verified, false if it is not correct or on error
     *
     * The behavior of this function is inspired by {@link http://php.net/openssl-verify openssl_verify}.
     */
    public function validateSignature(): bool
    {
        $CAs = X509::getCAs();
        foreach ($CAs as $ca) {
            if ($ca->isIssuerOf($this)) {
                $signingCert = $ca;
                break;
            }
        }
        if (!isset($signingCert)) {
            return false;
        }
        $this->compile();
        return self::validateSignatureHelper(
            $signingCert->getPublicKey(),
            $this->crl['signatureAlgorithm']['algorithm'],
            $this->crl['signature'],
            $this->crl['tbsCertList']->getEncoded()
        );
    }

    public function getEncoded(): string
    {
        $this->compile();
        return $this->crl->getEncoded();
    }

    public function enableHideFullDecode(): void
    {
        $this->hideFullDecode = true;
    }
}
<?php

/**
 * Pure-PHP SPKAC Parser
 *
 * PHP version 8.1+
 *
 * Encode and decode SPKACs.
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2025-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File;

use phpseclib4\Common\Functions\Strings;
use phpseclib4\Crypt\Common\{PrivateKey, PublicKey};
use phpseclib4\Crypt\{PublicKeyLoader, RSA};
use phpseclib4\Exception\{NoKeyLoadedException, UnexpectedValueException};
use phpseclib4\File\ASN1\{Constructed, Element, Maps};
use phpseclib4\File\ASN1\Types\BitString;
use phpseclib4\File\Common\Signable;

/**
 * Pure-PHP SPKAC Parser
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @implements \ArrayAccess<string, mixed>
 * @implements \Iterator<string, mixed>
 */
class SPKAC implements \ArrayAccess, \Countable, \Iterator, Signable
{
    use \phpseclib4\File\Common\Traits\ASN1Signature;

    private Constructed|array $spkac = [
        'publicKeyAndChallenge' => [
            'spki' => [
                'algorithm' => ['algorithm' => '0.0'],
                'subjectPublicKey' => "\0",
            ],
            // quoting <https://developer.mozilla.org/en-US/docs/Web/HTML/Element/keygen>,
            // "A challenge string that is submitted along with the public key. Defaults to an empty string if not specified."
            // both Firefox and OpenSSL ("openssl spkac -key private.key") behave this way
            // we could alternatively do this instead if we ignored the specs:
            // random_bytes(8) & str_repeat("\x7F", 8)
            'challenge' => '',
        ],
        'signatureAlgorithm' => [
            'algorithm' => '0.0',
        ],
        'signature' => "\0",
    ];

    /**
     * Binary key flag
     */
    private static bool $binary = false;

    public function __construct(?PublicKey $public = null)
    {
        ASN1::loadOIDs('X509');

        if ($public instanceof PublicKey) {
            $this->setPublicKey($public);
        }
    }

    /**
     * Enable binary output (DER)
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public static function enableBinaryOutput(): void
    {
        self::$binary = true;
    }

    /**
     * Disable binary output (ie. enable PEM)
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public static function disableBinaryOutput(): void
    {
        self::$binary = false;
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function hasPublicKey(): bool
    {
        return $this->spkac['publicKeyAndChallenge']['spki'] instanceof PublicKey;
    }

    public function setPublicKey(PublicKey $publicKey): void
    {
        $this->spkac['publicKeyAndChallenge']['spki'] = $publicKey;
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function removePublicKey(): void
    {
        $this->spkac['publicKeyAndChallenge']['spki'] = [
            'algorithm' => ['algorithm' => '0.0'],
            'subjectPublicKey' => "\0",
        ];
    }

    public static function load(string|array|Constructed $spkac, int $mode = ASN1::FORMAT_AUTO_DETECT): SPKAC
    {
        $new = new self();
        $new->spkac = is_string($spkac) ? self::loadString($spkac, $mode) : $spkac;
        return $new;
    }

    private static function loadString(string $spkac, int $mode): Constructed
    {
        if ($mode != ASN1::FORMAT_DER) {
            $newspkac = ASN1::extractBER($spkac);
            if ($mode == ASN1::FORMAT_PEM && $spkac == $newspkac) {
                throw new UnexpectedValueException('Unable to decode PEM');
            }
            $spkac = $newspkac;
        }

        // see http://www.w3.org/html/wg/drafts/html/master/forms.html#signedpublickeyandchallenge

        // OpenSSL produces SPKAC's that are preceded by the string SPKAC=
        $temp = preg_replace('#(?:SPKAC=)|[ \r\n\\\]#', '', $spkac);
        $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? Strings::base64_decode($temp) : false;
        if ($temp != false) {
            $spkac = $temp;
        }

        $decoded = ASN1::decodeBER($spkac);

        $rules = [];
        $rules['publicKeyAndChallenge']['spki'] = function (Constructed &$spkac) {
            try {
                $spkac = PublicKeyLoader::load($spkac->getEncoded());
            } catch (NoKeyLoadedException $e) {
            }
        };

        return ASN1::map($decoded, Maps\SignedPublicKeyAndChallenge::MAP, $rules);
    }

    /**
     * Set challenge
     */
    public function setChallenge(string $challenge): void
    {
        $this->spkac['publicKeyAndChallenge']['challenge'] = $challenge & str_repeat("\x7F", strlen($challenge));
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function getChallenge(): string
    {
        $this->compile();
        return $this->spkac['publicKeyAndChallenge']['challenge']->value;
    }

    public function toString(array $options = []): string
    {
        $publicKey = $this->spkac['publicKeyAndChallenge']['spki'] ;
        if ($publicKey instanceof PublicKey) {
            $origKey = $publicKey;
            $this->spkac['publicKeyAndChallenge']['spki']  = new Element($publicKey->toString('PKCS8', ['binary' => true]));
        }

        $spkac = ASN1::encodeDER($this->spkac, Maps\SignedPublicKeyAndChallenge::MAP);
        if (isset($origKey)) {
            $this->spkac['publicKeyAndChallenge']['spki'] = $origKey;
        }

        if ($options['binary'] ?? self::$binary) {
            return $spkac;
        }

        return 'SPKAC=' . Strings::base64_encode($spkac);
    }

    public function __toString(): string
    {
        return $this->toString();
    }

    public function getSignableSection(): string
    {
        $this->compile();
        return $this->spkac['publicKeyAndChallenge']->getEncoded();
    }

    public function setSignature(string $signature): void
    {
        $this->spkac['signature'] = new BitString("\0$signature");
    }

    /**
     * Identify signature algorithm from private key
     *
     * @throws \phpseclib4\Exception\UnsupportedAlgorithmException if the algorithm is unsupported
     */
    public function identifySignatureAlgorithm(PrivateKey $key): void
    {
        $algorithm = self::identifySignatureAlgorithmHelper($key);
        $this->spkac['publicKeyAndChallenge']['signature'] = $algorithm;
        $this->spkac['signatureAlgorithm'] = $algorithm;
    }

    public function copySigningX509Attributes(X509 $x509): void
    {
    }

    private function compile(): void
    {
        if (!$this->spkac instanceof Constructed) {
            $temp = self::load("$this");
            $this->spkac = $temp->spkac;
        }
        if ($this->spkac->hasEncoded()) {
            return;
        }
        $temp = self::load("$this");
        $this->spkac = $temp->spkac;
    }

    public function __debugInfo(): array
    {
        $this->compile();
        return $this->spkac->__debugInfo();
    }

    public function getPublicKey(): PublicKey
    {
        if (!$this->spkac['publicKeyAndChallenge']['spki'] instanceof PublicKey) {
            throw new UnexpectedValueException('Unable to decode spki');
        }

        $publicKey = $this->spkac['publicKeyAndChallenge']['spki'];
        if ($publicKey instanceof RSA && $publicKey->getLoadedFormat() == 'PKCS8') {
            return $publicKey->withPadding(RSA::SIGNATURE_PKCS1);
        }
        return $publicKey;
    }

    public function toArray(bool $convertPrimitives = false): array
    {
        $this->compile();
        return $this->spkac instanceof Constructed ? $this->spkac->toArray($convertPrimitives) : $this->spkac;
    }

    /**
     * Validate a signature
     *
     * Returns true if the signature is verified, false if it is not correct or on error
     *
     * The behavior of this function is inspired by {@link http://php.net/openssl-verify openssl_verify}.
     */
    public function validateSignature(): bool
    {
        return self::validateSignatureHelper(
            $this->getPublicKey(),
            $this->spkac['signatureAlgorithm']['algorithm'],
            $this->spkac['signature'],
            $this->spkac['publicKeyAndChallenge']->getEncoded()
        );
    }

    public function count(): int
    {
        return is_array($this->spkac) ? count($this->spkac) : $this->spkac->count();
    }

    public function rewind(): void
    {
        $this->compile();
        $this->spkac->rewind();
    }

    public function current(): mixed
    {
        $this->compile();
        return $this->spkac->current();
    }

    public function key(): mixed
    {
        $this->compile();
        return $this->spkac->key();
    }

    public function next(): void
    {
        $this->compile();
        $this->spkac->next();
    }

    public function valid(): bool
    {
        $this->compile();
        return $this->spkac->valid();
    }

    public function &offsetGet(mixed $offset): mixed
    {
        $this->compile();
        /** @psalm-suppress NonVariableReferenceReturn */
        return $this->spkac[$offset];
    }

    public function offsetExists(mixed $offset): bool
    {
        return isset($this->spkac[$offset]);
    }

    public function offsetSet(mixed $offset, mixed $value): void
    {
        $this->spkac[$offset] = $value;
    }

    public function offsetUnset(mixed $offset): void
    {
        unset($this->spkac[$offset]);
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function keys(): array
    {
        return $this->spkac instanceof Constructed ? $this->spkac->keys() : array_keys($this->spkac);
    }
}
<?php

/**
 * Pure-PHP CMS / EncryptedData Parser
 *
 * PHP version 8.1+
 *
 * Encode and decode CMS / CompressedData files.
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\CMS;

use phpseclib4\Common\Functions\Strings;
use phpseclib4\Crypt\{AES, EC, RSA, TripleDES};
use phpseclib4\Exception\{
    BadDecryptionException,
    BadMethodCallException,
    InvalidStateException,
    LengthException,
    UnexpectedValueException
};
use phpseclib4\File\ASN1\{Constructed, Element, Maps};
use phpseclib4\File\ASN1\Types\{Choice, OID, OctetString};
use phpseclib4\File\{ASN1, CMS, CRL, X509};
use phpseclib4\File\CMS\EnvelopedData\{
    DerivableKey,
    KEKRecipient,
    KeyAgreeRecipient,
    KeyTransRecipient,
    PasswordRecipient,
    Recipient,
    SearchableKey
};

/**
 * Pure-PHP CMS / EncryptedData Parser
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @implements \ArrayAccess<string, mixed>
 * @implements \Iterator<string, mixed>
 */
class EncryptedData implements \ArrayAccess, \Countable, \Iterator
{
    use \phpseclib4\File\Common\Traits\KeyDerivation;

    private Constructed|array $cms;
    public string $cek; // content encryption key

    public function __construct(
        #[\SensitiveParameter] string $data,
        string $encryptionAlgorithm = 'aes128-CBC-PAD',
        #[\SensitiveParameter] ?string $key = null
    ) {
        $cipher = self::getPBES2EncryptionObject($encryptionAlgorithm);
        $keyLength = $cipher->getKeyLength() >> 3;
        if (isset($key) && strlen($key) != $keyLength) {
            throw new LengthException('key is ' . strlen($key) . " bytes long; it should be $keyLength bytes long");
        }
        $this->cek = $key ?? random_bytes($keyLength);
        $cipher->setKey($this->cek);
        $iv = random_bytes($cipher->getBlockLengthInBytes());
        $cipher->setIV($iv);
        $encrypted = $cipher->encrypt($data);
        $this->cms = [
            'contentType' => 'id-encryptedData',
            'content' => [
                // "If unprotectedAttrs is present, then the version MUST be 2.  If unprotectedAttrs is
                //  absent, then version MUST be 0."
                // phpseclib doesn't (currently) support unprotectedAttrs so version is hard coded as v0
                'version' => 'v0',
                'encryptedContentInfo' => [
                    'contentType' => 'id-data',
                    'contentEncryptionAlgorithm' => [
                        'algorithm' => $encryptionAlgorithm,
                        'parameters' => new OctetString($iv),
                    ],
                    'encryptedContent' => $encrypted,
                ]
            ]
        ];
    }

    public static function load(string|array|Constructed $encoded): self
    {
        $r = new \ReflectionClass(__CLASS__);
        $cms = $r->newInstanceWithoutConstructor();
        $cms->cms = is_string($encoded) ? self::loadString($encoded) : $encoded;

        if (!isset($cms->cms['content']['recipientInfos'])) {
            return $cms;
        }

        ASN1::disableCacheInvalidation();
        foreach ($cms->cms['content']['recipientInfos'] as $i => $recipient) {
            $recipient = &$cms->cms['content']['recipientInfos'][$i];
            $key = $recipient->index;
            if ($recipient instanceof Choice) {
                $encoded = chr(ASN1::TYPE_SEQUENCE | 0x20) . substr($recipient->value->getEncoded(), 1);
                $recipient[$key] = match ($key) {
                    'ktri' => KeyTransRecipient::load($encoded),
                    'kari' => KeyAgreeRecipient::load($encoded),
                    'kekri' => KEKRecipient::load($encoded),
                    'pwri' => PasswordRecipient::load($encoded),
                    //'ori' => OtherRecipient::load($recipient),
                    default => $recipient->value,
                };
                if (!$recipient[$key] instanceof Constructed) {
                    $recipient[$key]->parent = $recipient;
                    $recipient[$key]->depth = $recipient->depth + 1;
                    $recipient[$key]->key = $key;
                }
            } elseif (is_array($recipient)) {
                $key = array_key_first($recipient);
                $value = $recipient[$key];
                $recipient[$key] = match ($key) {
                    'ktri' => new KeyTransRecipient($value),
                    'kari' => new KeyAgreeRecipient($value),
                    'kekri' => new KEKRecipient($value),
                    'pwri' => new PasswordRecipient($value),
                    default => $value,
                };
            }
            if (!$recipient[$key] instanceof Constructed) {
                $recipient[$key]->cms = $cms;
                if ($recipient[$key] instanceof KeyAgreeRecipient) {
                    $encryptedKeys = &$recipient[$key]['recipientEncryptedKeys'];
                    for ($i = 0; $i < count($encryptedKeys); $i++) {
                        $encryptedKeys[$i]->cms = $cms;
                        $encryptedKeys[$i]->recipient = $recipient[$key];
                    }
                    unset($encryptedKeys);
                }
            }
            unset($recipient);
        }
        ASN1::enableCacheInvalidation();

        return $cms;
    }

    private static function loadString(string $encoded): Constructed
    {
        $decoded = ASN1::decodeBER($encoded);
        $cms = ASN1::map($decoded, Maps\ContentInfo::MAP);
        $decoded = ASN1::decodeBER($cms['content']->value);
        if ($cms['contentType'] == 'id-envelopedData') {
            $rules = [];
            $rules['originatorInfo']['certs'] = [CMS::class, 'mapInCerts'];
            $rules['originatorInfo']['crls'] = [CMS::class, 'mapInCRLs'];
            ASN1::disableCacheInvalidation();
            $cms['content'] = ASN1::map($decoded, Maps\EnvelopedData::MAP, $rules);
            ASN1::enableCacheInvalidation();
        } else {
            $cms['content'] = ASN1::map($decoded, Maps\EncryptedDataCMS::MAP);
        }
        $cms['content']->parent = $cms;
        $cms['content']->key = 'content';
        return $cms;
    }

    public function withKey(#[\SensitiveParameter] string $key): self
    {
        $this->cek = $key;
        return $this;
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function getAlgorithm(): string
    {
        return (string) $this->cms['content']['encryptedContentInfo']['contentEncryptionAlgorithm']['algorithm'];
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function getKey(): string
    {
        return $this->cek;
    }

    public function getKeyLength(): int
    {
        $cea = ASN1::decodeBER((string) $this->cms['content']['encryptedContentInfo']['contentEncryptionAlgorithm']);
        $cea = ASN1::map($cea, ASN1\Maps\AlgorithmIdentifier::MAP);
        $contentCipher = self::getPBES2EncryptionObject((string) $cea['algorithm']);
        return $contentCipher->getKeyLength();
    }

    public function getKeyLengthInBytes(): int
    {
        return $this->getKeyLength() >> 3;
    }

    public function &offsetGet(mixed $offset): mixed
    {
        $this->compile();
        /** @psalm-suppress NonVariableReferenceReturn */
        return $this->cms[$offset];
    }

    public function offsetExists(mixed $offset): bool
    {
        return isset($this->cms[$offset]);
    }

    public function offsetSet(mixed $offset, mixed $value): void
    {
        $this->cms[$offset] = $value;
    }

    public function offsetUnset(mixed $offset): void
    {
        unset($this->cms[$offset]);
    }

    public function count(): int
    {
        return is_array($this->cms) ? count($this->cms) : $this->cms->count();
    }

    public function rewind(): void
    {
        $this->compile();
        $this->cms->rewind();
    }

    public function current(): mixed
    {
        $this->compile();
        return $this->cms->current();
    }

    public function key(): mixed
    {
        $this->compile();
        return $this->cms->key();
    }

    public function next(): void
    {
        $this->compile();
        $this->cms->next();
    }

    public function valid(): bool
    {
        $this->compile();
        return $this->cms->valid();
    }

    public function toString(array $options = []): string
    {
        if (isset($this->cms['content']['recipientInfos']) && count($this->cms['content']['recipientInfos'])) {
            $map = Maps\EnvelopedData::MAP;
            $this->cms['contentType'] = new OID('id-envelopedData');
        } else {
            $map = Maps\EncryptedDataCMS::MAP;
            $this->cms['contentType'] = new OID('id-encryptedData');
        }
        if ($this->cms instanceof Constructed) {
            ASN1::encodeDER($this->cms['content'], $map);
            $cms = ASN1::encodeDER($this->cms, Maps\ContentInfo::MAP);
        } else {
            $temp = [
                'contentType' => $this->cms['contentType'],
                'content' => new Element(ASN1::encodeDER($this->cms['content'], $map)),
            ];
            $cms = ASN1::encodeDER($temp, Maps\ContentInfo::MAP);
            $this->cms = self::load($cms)->cms;
        }

        if ($options['binary'] ?? CMS::$binary) {
            return $cms;
        }

        return "-----BEGIN CMS-----\r\n" . chunk_split(Strings::base64_encode($cms), 64) . '-----END CMS-----';
    }

    public function __toString(): string
    {
        return $this->toString();
    }

    public function compile(): void
    {
        if (!$this->cms instanceof Constructed) {
            $temp = self::load($this->toString(['binary' => true]));
            $this->cms = $temp->cms;
            return;
        }

        if ($this->cms->hasEncoded()) {
            return;
        }
        $temp = self::load($this->toString(['binary' => true]))->cms['content'];
        $content = &$this->cms['content'];
        foreach ($temp as $key => $val) {
            if ($key == 'recipientInfos' && !is_array($content['recipientInfos'])) {
                continue;
            }
            $content[$key] = $val;
        }
    }

    public function __debugInfo(): array
    {
        $this->compile();
        return $this->cms->__debugInfo();
    }

    public function toArray(bool $convertPrimitives = false): array
    {
        $this->compile();
        return $this->cms instanceof Constructed ? $this->cms->toArray($convertPrimitives) : $this->cms;
    }

    private function calculateVersion(): void
    {
        // based on https://www.rfc-editor.org/rfc/rfc5652#page-19
        $this->compile();
        if (isset($this->cms['content']['originatorInfo'])) {
            foreach ($this->cms['content']['originatorInfo']['certs'] as $cert) {
                if (isset($cert['other'])) {
                    $this->cms['content']['version'] = 'v4';
                    return;
                }
            }
            foreach ($this->cms['content']['originatorInfo']['crls'] as $crl) {
                if (isset($crl['other'])) {
                    $this->cms['content']['version'] = 'v4';
                    return;
                }
            }
            foreach ($this->cms['content']['originatorInfo']['certs'] as $cert) {
                if (isset($cert['v2AttrCert'])) {
                    $this->cms['content']['version'] = 'v3';
                    return;
                }
            }
        }
        foreach ($this->cms['content']['recipientInfos'] as $recipient) {
            if (isset($recipient['pwri']) || isset($recipient['ori'])) {
                $this->cms['content']['version'] = 'v3';
                return;
            }
        }
        if (isset($this->cms['content']['originatorInfo']) || isset($this->cms['content']['unprotectedAttrs'])) {
            $this->cms['content']['version'] = 'v2';
            return;
        }
        foreach ($this->cms['content']['recipientInfos'] as $recipient) {
            if ($recipient->value['version'] != 'v0') {
                $this->cms['content']['version'] = 'v2';
                return;
            }
        }
        $this->cms['content']['version'] = 'v0';
    }

    /** @return DerivableKey[] */
    public function getRecipients(): array
    {
        $this->compile();
        $result = [];
        foreach ($this->cms['content']['recipientInfos'] as $recipient) {
            switch ($recipient->index) {
                case 'pwri':
                case 'kekri':
                case 'ktri':
                    $result[] = $recipient->value;
                    break;
                case 'kari':
                    foreach ($recipient->value->getEncryptedKeys() as $key) {
                        $result[] = $key;
                    }
            }
        }
        return $result;
    }

    // returns all SearchableKeys matching $keyIdentifier
    /** @return SearchableKey[] */
    public function findRecipients(string|X509 $keyIdentifier): array
    {
        $this->compile();
        $recipients = [];
        if (is_string($keyIdentifier)) {
            foreach ($this->cms['content']['recipientInfos'] as $recipient) {
                if (isset($recipient['kekri']) && $recipient['kekri']['kekid']['keyIdentifier'] == $keyIdentifier) {
                    $recipients[] = $recipient['kekri'];
                }
            }
            return $recipients;
        }
        foreach ($this->cms['content']['recipientInfos'] as $recipient) {
            switch ($recipient->index) {
                case 'ktri':
                    if ($recipient->value->matchesX509($keyIdentifier)) {
                        $recipients[] = $recipient['ktri'];
                    }
                    break;
                case 'kari':
                    foreach ($recipient->value->getEncryptedKeys() as $key) {
                        if ($key->matchesX509($keyIdentifier)) {
                            $recipients[] = $key;
                        }
                    }
            }
        }
        return $recipients;
    }

    // return the first SearchableKey matching $keyIdentifier or null if none are found
    /** @psalm-suppress PossiblyUnusedMethod */
    public function findRecipient(string|X509 $keyIdentifier): ?SearchableKey
    {
        $recipients = $this->findRecipients($keyIdentifier);
        return count($recipients) ? $recipients[0] : null;
    }

    public function deriveFromKey(#[\SensitiveParameter] string|EC\PrivateKey|RSA\PrivateKey $key): self
    {
        $this->compile();
        foreach ($this->cms['content']['recipientInfos'] as $recipient) {
            switch (true) {
                case isset($recipient['kekri']) && is_string($key):
                    try {
                        $recipient['kekri']->withKey($key);
                        return $this;
                    } catch (\Exception) {
                    }
                    break;
                case isset($recipient['ktri']) && $key instanceof RSA\PrivateKey:
                    try {
                        $recipient['ktri']->withKey($key);
                        return $this;
                    } catch (\Exception) {
                    }
                    break;
                case isset($recipient['kari']) && $key instanceof EC\PrivateKey:
                    foreach ($recipient['kari']->getEncryptedKeys() as $subkey) {
                        try {
                            $subkey->withKey($key);
                            return $this;
                        } catch (\Exception) {
                        }
                    }
            }
        }
        throw new BadDecryptionException('Unable to perform decryption with key');
    }

    public function deriveFromPassword(#[\SensitiveParameter] string $password): self
    {
        $this->compile();
        foreach ($this->cms['content']['recipientInfos'] as $recipient) {
            if (isset($recipient['pwri'])) {
                try {
                    $recipient['pwri']->withPassword($password);
                    return $this;
                } catch (\Exception) {
                }
            }
        }
        throw new BadDecryptionException('Unable to perform decryption with password');
    }

    public function createNewRecipientFromPassword(#[\SensitiveParameter] string $password, string $encryptionAlgorithm = 'aes128-CBC-PAD'): PasswordRecipient
    {
        // to decrypt with openssl cli do this:
        // openssl cms -decrypt -inform PEM -in enveloped.pem -out plaintext.txt -pwri_password 'password'

        if (!isset($this->cek)) {
            throw new InvalidStateException('A content encryption key is unavailable');
        }
        $params = [
            'encryptionAlgorithm' => 'id-PBES2',
            'encryptionScheme' => $encryptionAlgorithm,
        ];
        $keyCipher = self::getCryptoObjectFromParams($password, $params);
        $keyCipher->disablePadding();
        $keyCipher->enableContinuousBuffer();
        $params = $keyCipher->getMetaData('algorithmIdentifier')['parameters'];
        $decoded = ASN1::decodeBER($params->value);
        $params = ASN1::map($decoded, Maps\PBES2params::MAP);
        $kdf = $params['keyDerivationFunc'];
        $kea = ['algorithm' => 'id-alg-PWRI-KEK', 'parameters' => $params['encryptionScheme']];

        // see https://datatracker.ietf.org/doc/html/rfc3211
        $keyCheck = ~substr($this->cek, 0, 3);
        $contentCipher = self::getPBES2EncryptionObject((string) $this->cms['content']['encryptedContentInfo']['contentEncryptionAlgorithm']['algorithm']);
        $length = max(
            $keyCipher->getBlockLengthInBytes(),
            2 * $contentCipher->getBlockLengthInBytes()
        ) - strlen($this->cek) - 4;
        if ($length < 0) {
            throw new LengthException('The content encryption key should be the same length or shorter than the key encryption key');
        }
        $padding = $length ? random_bytes($length) : '';
        $cekBlock = chr(strlen($this->cek)) . $keyCheck . $this->cek . $padding;

        $encryptedKey = $keyCipher->encrypt($cekBlock);
        $encryptedKey = $keyCipher->encrypt($encryptedKey);

        $recipient = [
            'version' => 'v0', // Always set to 0
            // "If [keyDerivationAlgorithm] is absent, the key-encryption key is supplied from an external
            //  source, for example a hardware crypto token such as a smart card."
            'keyDerivationAlgorithm' => $kdf,
            'keyEncryptionAlgorithm' => $kea,
            'encryptedKey' => $encryptedKey,
        ];
        $recipient = new PasswordRecipient($recipient);

        $this->placeRecipient($recipient, 'pwri');
        $this->calculateVersion();

        return $recipient;
    }

    public function createNewRecipientFromKeyWithIdentifier(
        #[\SensitiveParameter] string $key,
        string $identifier,
        ?\DateTimeInterface $date = null
    ): KEKRecipient {
        // to decrypt with openssl cli do this:
        // openssl cms -decrypt -in enveloped.pem -secretkey <hex-kek> -secretkeyid <hex-key-id> -out plaintext.txt

        if (!isset($this->cek)) {
            throw new InvalidStateException('A content encryption key is unavailable');
        }

        $keyLength = strlen($key);
        if (!in_array($keyLength, [16, 24, 32])) {
            throw new LengthException('Key length must be 16, 24, 32');
        }

        $kekid = ['keyIdentifier' => $identifier];
        if (isset($date)) {
            $kekid['date'] = $date;
        }

        $recipient = [
            'version' => 'v4', // always set to 4
            'kekid' => $kekid,
            'keyEncryptionAlgorithm' => ['algorithm' => 'id-aes' . ($keyLength << 3) . '-wrap'],
            'encryptedKey' => self::wrapAES($key, $this->cek),
        ];
        $recipient = new KEKRecipient($recipient);

        $this->placeRecipient($recipient, 'kekri');
        $this->calculateVersion();

        return $recipient;
    }

    /** @psalm-suppress PossiblyUnusedReturnValue */
    public function createNewRecipientFromX509(X509 $x509, int $type = CMS::ISSUER_AND_DN): KeyTransRecipient|KeyAgreeRecipient
    {
        $publicKey = $x509->getPublicKey();
        if (!$publicKey instanceof RSA && !$publicKey instanceof EC) {
            throw new UnexpectedValueException('Public key must be RSA or EC');
        }
        if ($publicKey instanceof RSA) {
            // to decrypt with openssl do this:
            // openssl cms -decrypt -inform PEM -in enveloped.pem -out plaintext.txt -recip recipient.crt -inkey recipient.key"

            if (!($publicKey->getPadding() & RSA::ENCRYPTION_OAEP)) {
                $algorithm = ['algorithm' => 'rsaEncryption', 'parameters' => new ASN1\Types\ExplicitNull()];
            } else {
                $hash = (string) $publicKey->getHash();
                if (substr($hash, 0, 3) == 'sha') {
                    $hash = "id-$hash";
                }
                $mgfHash = (string) $publicKey->getMGFHash();
                if (substr($mgfHash, 0, 3) == 'sha') {
                    $mgfHash = "id-$mgfHash";
                }
                $algorithm = [
                    'algorithm' => 'id-RSAES-OAEP',
                    'parameters' => [
                        'hashAlgorithm' => ['algorithm' => $hash],
                        'maskGenAlgorithm' => [
                            'algorithm' => 'id-mgf1',
                            'parameters' => ['algorithm' => $mgfHash],
                        ],
                        'pSourceAlgorithm' => ['algorithm' => 'id-pSpecified', 'parameters' => new OctetString($publicKey->getLabel())],
                    ]
                ];
                $mgf = &$algorithm['parameters']['maskGenAlgorithm']['parameters'];
                $mgf = new Element(ASN1::encodeDER($mgf, Maps\AlgorithmIdentifier::MAP));
                $algorithm['parameters'] = new Element(ASN1::encodeDER($algorithm['parameters'], Maps\RSAES_OAEP_params::MAP));
            }
            $recipient = [
                // version is the syntax version number.  If the RecipientIdentifier
                // is the CHOICE issuerAndSerialNumber, then the version MUST be 0.
                // If the RecipientIdentifier is subjectKeyIdentifier, then the
                // version MUST be 2.
                'version' => $type == CMS::ISSUER_AND_DN ? 'v0' : 'v2',
                'rid' => CMS::createSIDRID($x509, $type),
                'keyEncryptionAlgorithm' => $algorithm,
                'encryptedKey' => $publicKey->encrypt($this->cek),
            ];
            $recipient = new KeyTransRecipient($recipient);
            $this->placeRecipient($recipient, 'ktri');
        } else {
            $privateKey = EC::createKey($publicKey->getCurve());
            /** @psalm-suppress InvalidArgument */
            $secret = \phpseclib4\Crypt\DH::computeSecret($privateKey, $publicKey);
            $parameters = new ASN1\Element(ASN1::encodeDER(['algorithm' => 'id-aes128-wrap'], Maps\AlgorithmIdentifier::MAP));
            $sharedInfo = [
                'keyInfo' => $parameters,
                'suppPubInfo' => pack('N', 128)
            ];
            $sharedInfo = ASN1::encodeDER($sharedInfo, Maps\ECCCMSSharedInfo::MAP);
            $hash = $publicKey->getHash();
            $kek = self::ANSIX963KDF($secret, 16, $sharedInfo, $hash);
            $recipient = [
                'version' => 'v3', // always set to v3
                'originator' => ['originatorKey' => new Element($privateKey->getPublicKey()->toString('PKCS8', ['binary' => true]))],
                'keyEncryptionAlgorithm' => ['algorithm' => 'dhSinglePass-stdDH-' . $hash . 'kdf-scheme', 'parameters' => $parameters],
                'recipientEncryptedKeys' => [['rid' => CMS::createSIDRID($x509, $type), 'encryptedKey' => self::wrapAES($kek, $this->cek)]],
            ];
            $recipient = new KeyAgreeRecipient($recipient);
            $this->placeRecipient($recipient, 'kari');
        }

        $this->calculateVersion();

        return $recipient;
    }

    /** @psalm-suppress InvalidPropertyFetch */
    private function placeRecipient(Recipient $recipient, string $type): void
    {
        if (isset($this->cms['content']['recipientInfos'])) {
            $this->compile();
        } else {
            $this->cms['content']['recipientInfos'][] = ['ori' => ['oriType' => '0.0', 'oriValue' => '']];
            $this->compile();
            unset($this->cms['content']['recipientInfos'][0]);
        }

        $recipients = &$this->cms['content']['recipientInfos'];
        $idx = count($recipients);
        $recipient->compile();
        $recipients[$idx] = new Choice($type, $recipient);
        $recipient->cms = $this;
        $recipients[$idx]->parent = $recipients;
        $recipients[$idx]->key = $idx;
        $recipients[$idx]->depth = $recipients->depth;
        $recipient->parent = $recipients[$idx];
        $recipient->depth = $recipients[$idx]->depth + 1;
        $recipients[$idx][$type] = $recipient;
    }

    public function wrapAES(#[\SensitiveParameter] string $kek, #[\SensitiveParameter] string $cek): string
    {
        $p = str_split($cek, 8);
        $n = count($p);
        // from https://datatracker.ietf.org/doc/html/rfc3394.html#section-2.2.3.1
        $iv = "\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6";

        $aes = new AES('ecb');
        $aes->setKey($kek);
        $aes->disablePadding();

        // 1) Initialize variables.
        $a = $iv;
        $r = $p;

        // 2) Calculate intermediate values.
        for ($j = 0; $j <= 5; $j++) {
            for ($i = 0; $i < $n; $i++) {
                $b = $aes->encrypt($a . $r[$i]);
                $t = pack('J', $n * $j + $i + 1);
                $a = substr($b, 0, 8) ^ $t;
                $r[$i] = substr($b, 8);
            }
        }

        // 3) Output the results.
        return $a . implode('', $r);
    }

    // from https://www.rfc-editor.org/rfc/rfc3217#section-3.1
    /** @psalm-suppress PossiblyUnusedMethod */
    public static function wrapDES(#[\SensitiveParameter] string $kek, #[\SensitiveParameter] string $cek): string
    {
        $icv = substr(sha1($cek, true), 0, 8);
        $cekicv = $cek . $icv;
        $iv = random_bytes(8);
        $cipher = new TripleDES('cbc');
        $cipher->disablePadding();
        $cipher->setKey($kek);
        $cipher->setIV($iv);
        $temp1 = $cipher->encrypt($cekicv);
        $temp2 = $iv . $temp1;
        $temp3 = strrev($temp2);
        $cipher->setIV("\x4a\xdd\xa2\x2c\x79\xe8\x21\x05");
        return $cipher->encrypt($temp3);
    }

    // from https://www.secg.org/sec1-v2.pdf#page=38
    // $keydatalen should be the bytelength
    public static function ANSIX963KDF(string $z, int $keydatalen, string $sharedinfo, \phpseclib4\Crypt\Hash $hash): string
    {
        $counter = "\0\0\0\1";
        $hashlen = $hash->getLengthInBytes();
        $k = '';
        for ($i = 1; $i <= ceil($keydatalen / $hashlen); $i++) {
            $k .= $hash->hash($z . $counter . $sharedinfo);
            Strings::increment_str($counter);
        }
        return substr($k, 0, $keydatalen);
    }

    public function addCertificate(X509 $cert): void
    {
        if (!isset($this->cms['content']['recipientInfos'])) {
            throw new BadMethodCallException('Certificates can only be added if recipients exist');
        }
        $this->cms['content']['originatorInfo']['certs'][] = ['certificate' => $cert];
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function addCRL(CRL $crl): void
    {
        if (!isset($this->cms['content']['recipientInfos'])) {
            throw new BadMethodCallException('CRLs can only be added if recipients exist');
        }
        $this->cms['content']['originatorInfo']['crls'][] = $crl;
    }

    public function getCertificates(): array
    {
        // EncryptedData does not have anywhere to add certs - just EnvelopedData
        if (!isset($this->cms['content']['recipientInfos'])) {
            return [];
        }
        $certs = [];
        foreach ($this->cms['content']['originatorInfo']['certs'] as $cert) {
            switch (true) {
                // standard X509 cert
                case isset($cert['certificate']): // && $cert['certificate'] instanceof X509:
                    $certs[] = $cert['certificate'];
                //    break;
                // extended certificates are basically wrappers around regular X509 certs with unsigned attributes
                // living alongside the cert. this was intended for pre-v3 X509 certs where extensions were not
                // included
                //case isset($cert['extendedCertificate']): // obsolete
                //    if ($this->isSignedBy($cert['extendedCertificate']['certificate'])) {
                //        $signingCert = $cert['extendedCertificiate']['certificate'];
                //    }
                //    break;
                //case isset($cert['v1AttrCert']): // obsolete
                //    ['v1AttrCert']['acInfo'] = $AttributeCertificateInfoV1 ?
                //case isset($cert['v2AttrCert']):
                //    ['v2AttrCert']['acInfo'] = $AttributeCertificateInfo ?
                //case isset($cert['other']):
                //    ['other']['otherCert'] = ???
                //    continue 2;
            }
        }
        return $certs;
    }
}
<?php

/**
 * Pure-PHP CMS / SignedData Parser
 *
 * PHP version 8.1+
 *
 * Encode and decode CMS / SignedData files.
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\CMS;

use phpseclib4\Common\Functions\Strings;
use phpseclib4\Crypt\Common\PrivateKey;
use phpseclib4\Crypt\Hash;
use phpseclib4\Exception\{BadMethodCallException, UnexpectedValueException, UnsupportedAlgorithmException};
use phpseclib4\File\ASN1\{Constructed, Element, Maps};
use phpseclib4\File\ASN1\Types\{OID, OctetString};
use phpseclib4\File\{ASN1, CMS, CRL, X509};
use phpseclib4\File\CMS\SignedData\Signer;
use phpseclib4\File\Common\Signable;

/**
 * Pure-PHP CMS / SignedData Parser
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @implements \ArrayAccess<string, mixed>
 * @implements \Iterator<string, mixed>
 */
class SignedData implements \ArrayAccess, \Countable, \Iterator, Signable
{
    use \phpseclib4\File\Common\Traits\ASN1Signature;
    use \phpseclib4\File\Common\Traits\Extension; // pretty much just for extensionMatch()

    public Constructed|array $cms;
    private ?Signer $tempSigner = null;
    /*
     * @var resource
     */
    public $fp;

    /**
     * @param string|resource $data
     */
    public function __construct(mixed $data)
    {
        if (!is_string($data) && !is_resource($data)) {
            throw new UnexpectedValueException('$data must be a string or resource');
        }
        $this->cms = [
            'contentType' => 'id-signedData',
            'content' => [
                'version' => 'v1',
                'digestAlgorithms' => [],
                'encapContentInfo' => [
                    'eContentType' => 'id-data',
                ],
                //'certificates' => [],
                //'crls' => [],
                'signerInfos' => []
            ]
        ];
        if (is_string($data)) {
            $this->cms['content']['encapContentInfo']['eContent'] = $data;
        } else {
            $this->fp = $data;
        }
    }

    // CMS::load() takes care of the PEM / DER encoding toggling
    // if you want to load an array or Constructed as a SignedData instance you'll
    // need to call CMS\SignedData::load()
    public static function load(string|array|Constructed $encoded): self
    {
        $r = new \ReflectionClass(__CLASS__);
        $cms = $r->newInstanceWithoutConstructor();
        $cms->cms = is_string($encoded) ? self::loadString($encoded) : $encoded;
        foreach ($cms['content']['signerInfos'] as $i => $signerInfo) {
            if (is_array($signerInfo)) {
                $cms['content']['signerInfos'][$i] = new Signer($signerInfo);
            }
            $signerInfo = &$cms['content']['signerInfos'][$i];
            $signerInfo->cms = $cms;
            $signerInfo->parent = $cms['content']['signerInfos'];
            $signerInfo->depth = $cms['content']['signerInfos']->depth + 1;
            $signerInfo->key = $i;
            $signerInfo->signer->parent = $cms['content']['signerInfos'];
            $signerInfo->signer->depth = $signerInfo->depth;
            $signerInfo->signer->key = $i;
            unset($signerInfo);
        }
        return $cms;
    }

    private static function loadString(string $encoded): Constructed
    {
        $decoded = ASN1::decodeBER($encoded);
        $cms = ASN1::map($decoded, Maps\ContentInfo::MAP);
        ASN1::disableCacheInvalidation();
        $rules = [];
        $rules['certificates'] = self::mapInCerts(...);
        $rules['crls'] = self::mapInCRLs(...);
        $rules['signerInfos'] = self::mapInSigners(...);
        $rules['encapContentInfo'] = self::mapInEncapContentInfo(...);
        $decoded = ASN1::decodeBER($cms['content']->value);
        $cms['content'] = ASN1::map($decoded, Maps\SignedData::MAP, $rules);
        $cms['content']->parent = $cms;
        $cms['content']->key = 'content';
        ASN1::enableCacheInvalidation();
        return $cms;
    }

    public function __debugInfo(): array
    {
        $this->compile();
        return $this->cms->__debugInfo();
    }

    private function compile(): void
    {
        if (!$this->cms instanceof Constructed) {
            $temp = self::load($this->toString(['binary' => true]));
            $this->cms = $temp->cms;
            return;
        }
        if ($this->cms->hasEncoded()) {
            return;
        }
        $temp = self::load($this->toString(['binary' => true]))['content'];
        $content = &$this->cms['content'];
        foreach ($temp as $key => $val) {
            if ($key == 'signerInfos') {
                continue;
            }
            $content[$key] = $val;
        }
    }

    public static function mapInEncapContentInfo(Constructed $eContent): void
    {
        ASN1::disableCacheInvalidation();
        if (self::extensionMatch('id-ct-TSTInfo', $eContent['eContentType'])) {
            $temp = ASN1::decodeBER((string) $eContent['eContent']);
            $eContent['eContent'] = ASN1::map($temp, Maps\TSTInfo::MAP);
        }
        ASN1::enableCacheInvalidation();
    }

    // this NEEDS to be public so that Constructed.php can call it
    public static function mapInCerts(Constructed $certs): void
    {
        ASN1::disableCacheInvalidation();
        for ($i = 0; $i < count($certs); $i++) {
            if ($certs[$i]->index != 'certificate') {
                continue;
            }
            $certs[$i]['certificate'] = X509::load((string) $certs[$i]->getEncoded());
        }
        ASN1::enableCacheInvalidation();
    }

    public static function mapInCRLs(Constructed $crls): void
    {
        ASN1::disableCacheInvalidation();
        for ($i = 0; $i < count($crls); $i++) {
            if (!isset($crls[$i]['crl'])) {
                continue;
            }
            $crls[$i] = CRL::load((string) $crls[$i]->getEncoded());
        }
        ASN1::enableCacheInvalidation();
    }

    public static function mapInSigners(Constructed $signers): void
    {
        ASN1::disableCacheInvalidation();
        for ($i = 0; $i < count($signers); $i++) {
            $signers[$i] = Signer::load((string) $signers[$i]->getEncoded());
        }
        ASN1::enableCacheInvalidation();
    }

    public function toString(array $options = []): string
    {
        if ($this->cms instanceof Constructed) {
            ASN1::encodeDER($this->cms['content'], Maps\SignedData::MAP);
            $cms = ASN1::encodeDER($this->cms, Maps\ContentInfo::MAP);
        } else {
            $temp = [
                'contentType' => $this->cms['contentType'], // 99% of the time this'll be 'id-signedData'
                'content' => new Element(ASN1::encodeDER($this->cms['content'], Maps\SignedData::MAP)),
            ];
            $cms = ASN1::encodeDER($temp, Maps\ContentInfo::MAP);
            $this->cms = self::load($cms)->cms;
        }

        if ($options['binary'] ?? CMS::$binary) {
            return $cms;
        }

        return "-----BEGIN CMS-----\r\n" . chunk_split(Strings::base64_encode($cms), 64) . '-----END CMS-----';
    }

    public function getSignableSection(): string
    {
        return $this->tempSigner->getSignableSection();
    }

    public function setSignature(string $signature): void
    {
        $this->tempSigner->setSignature($signature);
        unset($this->tempSigner);
    }

    /**
     * Identify signature algorithm from private key
     *
     * @throws UnsupportedAlgorithmException if the algorithm is unsupported
     */
    public function identifySignatureAlgorithm(PrivateKey $key): void
    {
        if (!isset($this->tempSigner)) {
            $x509 = new X509($key->getPublicKey());
            $x509->setExtension('id-ce-keyUsage', ['digitalSignature']);
            $key->sign($x509);
            $this->tempSigner = $this->addESSSigner($x509);
        }
        $this->tempSigner->identifySignatureAlgorithm($key);
    }

    public function copySigningX509Attributes(X509 $x509): void
    {
        $this->tempSigner = $this->addESSSigner($x509);
    }

    private function calculateVersion(): void
    {
        // based on https://www.rfc-editor.org/rfc/rfc5652#page-10
        $this->compile();
        $final = 1;
        foreach ($this->cms['content']['certificates'] as $cert) {
            $temp = match ($cert->index) {
                'other' => 5,
                'v2AttrCert' => 4,
                'v1AttrCert' => 3,
                default => 1,
            };
            if ($temp > $final) {
                $final = $temp;
            }
        }

        foreach ($this->cms['content']['crls'] as $crl) {
            $temp = match ($crl->index) {
                'other' => 5,
                'default' => 1,
            };
            if ($temp > $final) {
                $final = $temp;
            }
        }
        foreach ($this->cms['content']['signerInfos'] as $signer) {
            $temp = match ($signer['version']) {
                'v3' => 3,
                default => 1,
            };
            if ($temp > $final) {
                $final = $temp;
            }
        }
        $temp = self::extensionMatch('id-data', $this->cms['content']['encapContentInfo']['eContentType']) ? 1 : 3;
        if ($temp > $final) {
            $final = $temp;
        }
        $this->cms['content']['version'] = "v$final";
    }

    public function calculateFileHash(string $hash)
    {
        $hash = new Hash($hash);
        if (isset($this->cms['content']['encapContentInfo']['eContent'])) {
            return $hash->hash((string) $this->cms['content']['encapContentInfo']['eContent']);
        } elseif (isset($this->fp)) {
            return $hash->hash($this->fp);
        } else {
            throw new BadMethodCallException('There is nothing to hash');
        }
    }

    private function createSignedAttrSkeleton(X509 $x509, int $type): array
    {
        $this->cms['content']['certificates'][] = ['certificate' => $x509];

        $sid = CMS::createSIDRID($x509, $type);
        // version is the syntax version number.  If the SignerIdentifier is
        // the CHOICE issuerAndSerialNumber, then the version MUST be 1.  If
        // the SignerIdentifier is subjectKeyIdentifier, then the version
        // MUST be 3.
        $version = $type == CMS::ISSUER_AND_DN ? 'v1' : 'v3';

        $signingTime = new \DateTimeImmutable('now', new \DateTimeZone(@date_default_timezone_get()));
        $hash = $this->calculateFileHash('sha256');
        $contentType = (string) $this->cms['content']['encapContentInfo']['eContentType'];
        return [
            'version' => $version,
            'sid' => $sid,
            'digestAlgorithm' => ['algorithm' => 'id-sha256'],
            /*
            If [signedAttrs] is present, it MUST contain, at a minimum, the following two attributes:

            A content-type attribute having as its value the content type
            of the EncapsulatedContentInfo value being signed.  Section
            11.1 defines the content-type attribute.  However, the
            content-type attribute MUST NOT be used as part of a
            countersignature unsigned attribute as defined in Section 11.4.

            A message-digest attribute, having as its value the message
            digest of the content.  Section 11.2 defines the message-digest
            attribute.
            */
            'signedAttrs' => [
                ['type' => 'id-contentType', 'value' => [new OID($contentType)]],
                ['type' => 'id-messageDigest', 'value' => [new OctetString($hash)]],
                // the following aren't necessary but OpenSSL always includes them so i shall as well
                ['type' => 'id-signingTime', 'value' => ASN1::formatTime($signingTime)],
            ],
            'signatureAlgorithm' => ['algorithm' => '0.0'],
            'signature' => "\0",
        ];
    }

    public function recalculateHashAlgorithms(): void
    {
        $signers = $this->getSigners();
        $algos = [];
        foreach ($signers as $signer) {
            foreach ($algos as $algo) {
                if ($signer['digestAlgorithm']['algorithm'] === $algo['algorithm']) {
                    continue;
                }
            }
            $algos[] = $signer['digestAlgorithm'];
        }
        $this->cms['content']['digestAlgorithms'] = $algos;
    }

    private function createSigner(array $skeleton): Signer
    {
        $signer = new Signer($skeleton);
        // make Signer point to SignedData
        $signer->cms = $this;
        $signer->compile();
        $this->compile();
        $this->cms['content']['signerInfos'][] = $signer;
        // make Signer Constructed point to SignedData Constructed
        $signer->signer->parent = $this->cms['content']['signerInfos'];
        $signer->signer->key = count($this->cms['content']['signerInfos']) - 1;
        // [digestAlgorithms] is intended to list the message digest algorithms employed by all of the signers, in any
        // order, to facilitate one-pass signature verification. Implementations MAY fail to validate signatures that
        // use a digest algorithm that is not included in this set.
        $algorithms = $this->cms['content']['digestAlgorithms'];
        if ($algorithms instanceof Constructed) {
            $algorithms = $algorithms->toArray();
        }
        $list = array_column($algorithms, 'algorithm');
        if (!in_array('id-sha256', $list)) {
            $this->cms['content']['digestAlgorithms'][] = ['algorithm' => 'id-sha256'];
        }
        $this->calculateVersion();
        return $signer;
    }

    public function addESSSigner(X509 $x509, int $type = CMS::ISSUER_AND_DN): Signer
    {
        $skeleton = $this->createSignedAttrSkeleton($x509, $type);
        $hash = new Hash('sha256');
        $hash = $hash->hash($x509->getEncoded());
        $skeleton['signedAttrs'][] = [
            'type' => 'id-aa-signingCertificateV2',
            'value' => [[
                'certs' => [[
                    // hashAlgorithm is new to id-aa-signingCertificateV2 - in id-aa-signingCertificate it was *always* sha1
                    'hashAlgorithm' => ['algorithm' => 'id-sha256'],
                    'certHash' => new OctetString($hash),
                    'issuerSerial' => [
                        'issuer' => [['directoryName' => $x509['tbsCertificate']['issuer']]],
                        'serialNumber' => $x509['tbsCertificate']['serialNumber'],
                    ]
                ]]
            ]]
        ];
        return $this->createSigner($skeleton, $x509);
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function addNakedSigner(X509 $x509, int $type = CMS::ISSUER_AND_DN): Signer
    {
        // When [signedAttrs] is absent, the result is just the message digest of the [the encapContentInfo eContent OCTET STRING].
        // ...
        // [signedAttrs] is optional, but it MUST be present if the content type of the EncapsulatedContentInfo value being signed
        // is not id-data.
        $skeleton = $this->createSignedAttrSkeleton($x509, $type);
        unset($skeleton['signedAttrs']);
        return $this->createSigner($skeleton);
    }

    public function addSigner(X509 $x509, int $type = CMS::ISSUER_AND_DN): Signer
    {
        $skeleton = $this->createSignedAttrSkeleton($x509, $type);
        return $this->createSigner($skeleton);
    }

    public function addSignature(Signer $signer): void
    {
        $this->compile();
        $this->addCertificate(($signer->getCertificate()));
        $this->cms['content']['signerInfos'][] = $signer;
        $signer->parent = $this->cms['content']['signerInfos'];
        $signer->key = count($this->cms['content']['signerInfos']) - 1;
        /** @psalm-suppress InvalidPropertyFetch */
        $signer->depth = $this->cms['content']['signerInfos']->depth + 1;
    }

    public function __toString(): string
    {
        return $this->toString();
    }

    public function &offsetGet(mixed $offset): mixed
    {
        $this->compile();
        /** @psalm-suppress NonVariableReferenceReturn */
        return $this->cms[$offset];
    }

    public function offsetExists(mixed $offset): bool
    {
        return isset($this->cms[$offset]);
    }

    public function offsetSet(mixed $offset, mixed $value): void
    {
        $this->cms[$offset] = $value;
    }

    public function offsetUnset(mixed $offset): void
    {
        unset($this->cms[$offset]);
    }

    public function count(): int
    {
        return is_array($this->cms) ? count($this->cms) : $this->cms->count();
    }

    public function rewind(): void
    {
        $this->compile();
        $this->cms->rewind();
    }

    public function current(): mixed
    {
        $this->compile();
        return $this->cms->current();
    }

    public function key(): mixed
    {
        $this->compile();
        return $this->cms->key();
    }

    public function next(): void
    {
        $this->compile();
        $this->cms->next();
    }

    public function valid(): bool
    {
        $this->compile();
        return $this->cms->valid();
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function keys(): array
    {
        return $this->cms instanceof Constructed ? $this->cms->keys() : array_keys($this->cms);
    }

    public function toArray(bool $convertPrimitives = false): array
    {
        $this->compile();
        return $this->cms instanceof Constructed ? $this->cms->toArray($convertPrimitives) : $this->cms;
    }

    public function getSigners(): array
    {
        $this->compile();
        $signers = [];
        foreach ($this->cms['content']['signerInfos'] as $signer) {
            $signers[] = $signer;
        }
        return $signers;
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function findSigner(X509 $x509): ?Signer
    {
        $this->compile();
        foreach ($this->cms['content']['signerInfos'] as $signer) {
            if ($signer->matchesX509($x509)) {
                return $signer;
            }
        }
        return null;
    }

    /**
     * @param string|resource $data
     */
    public function attach(mixed $data): void
    {
        if (!is_string($data) && !is_resource($data)) {
            throw new UnexpectedValueException('$data should be either a resource or a string');
        }
        if (is_resource($data)) {
            $this->fp = $data;
        } else {
            $this->cms['content']['encapContentInfo']['eContent'] = $data;
        }
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function detach(): void
    {
        $this->fp = null;
        unset($this->cms['content']['encapContentInfo']['eContent']);
    }

    public function getCertificates(): array
    {
        $certs = [];
        foreach ($this->cms['content']['certificates'] as $cert) {
            switch (true) {
                // standard X509 cert
                case isset($cert['certificate']): // && $cert['certificate'] instanceof X509:
                    $certs[] = $cert['certificate'];
                //    break;
                // extended certificates are basically wrappers around regular X509 certs with unsigned attributes
                // living alongside the cert. this was intended for pre-v3 X509 certs where extensions were not
                // included
                //case isset($cert['extendedCertificate']): // obsolete
                //    if ($this->isSignedBy($cert['extendedCertificate']['certificate'])) {
                //        $signingCert = $cert['extendedCertificiate']['certificate'];
                //    }
                //    break;
                //case isset($cert['v1AttrCert']): // obsolete
                //    ['v1AttrCert']['acInfo'] = $AttributeCertificateInfoV1 ?
                //case isset($cert['v2AttrCert']):
                //    ['v2AttrCert']['acInfo'] = $AttributeCertificateInfo ?
                //case isset($cert['other']):
                //    ['other']['otherCert'] = ???
                //    continue 2;
            }
        }
        return $certs;
    }

    public function addCertificate(X509 $cert): void
    {
        $this->cms['content']['certificates'][] = ['certificate' => $cert];
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function addCRL(CRL $crl): void
    {
        $this->cms['content']['crls'][] = $crl;
    }

    /**
     * Validate a signature
     */
    public function validateSignature(bool $caonly = true): bool
    {
        $this->compile();

        $expected = count($this->cms['content']['signerInfos']);
        if (!$expected) {
            return false;
        }

        $matches = 0;
        foreach ($this->cms['content']['signerInfos'] as $signer) {
            if ($signer->validateSignature($caonly)) {
                $matches++;
            }
        }

        return $expected == $matches;
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function getEncoded(): string
    {
        $this->compile();
        return $this->cms->getEncoded();
    }

    public function hasEncoded(): bool
    {
        //$this->compile();
        return $this->cms->hasEncoded();
    }
}
<?php

/**
 * Pure-PHP CMS / SignedData Parser
 *
 * PHP version 8.1+
 *
 * Encode and decode CMS / SignedData / SignerInfo files.
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\CMS\SignedData;

use phpseclib4\Common\Functions\Arrays;
use phpseclib4\Crypt\Common\PrivateKey;
use phpseclib4\Crypt\Hash;
use phpseclib4\Exception\{UnexpectedValueException, UnsupportedAlgorithmException};
use phpseclib4\File\ASN1\{Constructed, Element, Maps};
use phpseclib4\File\ASN1\Types\{BaseType, BitString, OctetString};
use phpseclib4\File\{ASN1, X509};
use phpseclib4\File\CMS\SignedData;
use phpseclib4\File\Common\Signable;

/**
 * @author  Jim Wigginton <terrafrost@php.net>
 * @implements \ArrayAccess<string, mixed>
 * @implements \Iterator<string, mixed>
 */
class Signer implements \ArrayAccess, \Countable, \Iterator, Signable
{
    use \phpseclib4\File\Common\Traits\ASN1Signature;
    use \phpseclib4\File\Common\Traits\DN;
    use \phpseclib4\File\Common\Traits\Extension; // pretty much just for extensionMatch()

    /**
     * Return internal array representation
     *
     * @see \phpseclib4\File\X509::getDN()
     */
    public const DN_ARRAY = 0;
    /**
     * Return string
     *
     * @see \phpseclib4\File\X509::getDN()
     */
    public const DN_STRING = 1;
    /**
     * Return ASN.1 name string
     *
     * @see \phpseclib4\File\X509::getDN()
     */
    public const DN_ASN1 = 2;
    /**
     * Return OpenSSL compatible array
     *
     * @see \phpseclib4\File\X509::getDN()
     */
    public const DN_OPENSSL = 3;
    /**
     * Return canonical ASN.1 RDNs string
     *
     * @see \phpseclib4\File\X509::getDN()
     */
    public const DN_CANON = 4;
    /**
     * Return name hash for file indexing
     *
     * @see \phpseclib4\File\X509::getDN()
     */
    public const DN_HASH = 5;

    public Constructed|array|null $signer;
    public ?SignedData $cms = null;
    /** @psalm-suppress PossiblyUnusedProperty */
    public Constructed|null $parent;
    /** @psalm-suppress PossiblyUnusedProperty */
    public int $depth = 0;
    /** @psalm-suppress PossiblyUnusedProperty */
    public int|string $key;

    public function __construct(Constructed|array|null $signer = null)
    {
        $this->signer = $signer;
    }

    public static function load(string|array|Constructed $encoded): self
    {
        $signer = new self();
        $signer->signer = is_string($encoded) ? self::loadString($encoded) : $encoded;
        return $signer;
    }

    private static function loadString(string $encoded): Constructed
    {
        //ASN1::disableCacheInvalidation();
        $rules = [];
        $rules['signedAttrs']['*'] = self::mapInAttrs(...);
        $rules['unsignedAttrs']['*'] = self::mapInAttrs(...);
        $rules['sid']['issuerAndSerialNumber']['issuer']['rdnSequence']['*']['*'] = self::mapInDNs(...);
        $decoded = ASN1::decodeBER($encoded);
        $signer = ASN1::map($decoded, Maps\SignerInfo::MAP, $rules);
        //ASN1::enableCacheInvalidation();
        return $signer;
    }

    private function mapOutDNs(string $path): void
    {
        $dns = &Arrays::subArray($this->signer, $path);
        if (!$dns) {
            return;
        }
        self::mapOutDNsInner($dns);
    }

    public function toString(): string
    {
        $this->mapOutAttrs('signedAttrs', $this->signer);
        $this->mapOutAttrs('unsignedAttrs', $this->signer);
        $this->mapOutDNs('sid/issuerAndSerialNumber/issuer/rdnSequence');

        $signer = ASN1::encodeDER($this->signer, Maps\SignerInfo::MAP);

        return $signer;
    }

    public function __toString(): string
    {
        return $this->toString();
    }

    private static function getAttrMapping(string $attrType): array|null
    {
        return match ($attrType) {
            'id-aa-signingCertificate' => Maps\SigningCertificate::MAP,
            'id-aa-signingCertificateV2' => Maps\SigningCertificateV2::MAP,
            'id-aa-CMSAlgorithmProtection' => Maps\CMSAlgorithmProtection::MAP,
            'id-aa-timeStampToken' => Maps\TimeStampToken::MAP,
            'pkcs-9-at-smimeCapabilities' => Maps\SMIMECapabilities::MAP,
            default => null
        };
    }

    private static function mapOutAttrs(string $idx, array|Constructed|Element &$info): void
    {
        if ($info instanceof Element || !isset($info[$idx]) || $info[$idx] instanceof Element) {
            return;
        }
        $attrs = &$info[$idx];
        $keys = is_array($attrs) ? array_keys($attrs) : $attrs->keys();
        foreach ($keys as $i) {
            switch (true) {
                case $attrs[$i] instanceof Element:
                case $attrs[$i]['value'] instanceof Element:
                case $attrs[$i] instanceof Constructed && $attrs[$i]->hasEncoded():
                    continue 2;
            }

            $id = (string) $attrs[$i]['type'];
            $values = &$attrs[$i]['value'];
            $map = self::getAttrMapping($id);
            if (is_null($map)) {
                continue;
            }
            foreach ($values as $j => $value) {
                if ($id == 'id-aa-signingCertificate' || $id == 'id-aa-signingCertificateV2') {
                    $dns = &Arrays::subArrayWithWildcards($values[$j], 'certs/*/issuerSerial/issuer/*/directoryName/rdnSequence');
                    if ($dns) {
                        self::mapOutDNsInner($dns);
                        $value = $values[$j];
                    }
                }
                if ($value instanceof BaseType) {
                    if ($value instanceof Constructed) {
                        $value->invalidateCache();
                    }
                    ASN1::encodeDER($value, $map);
                } else {
                    $temp = ASN1::encodeDER($value, $map);
                    $values[$j] = ASN1::map(ASN1::decodeBER($temp), $map);
                    $values[$j]->enableForcedCache();
                }
            }
        }

        if ($attrs instanceof Constructed) {
            if (count($attrs) - 1 != $attrs->lastKey()) {
                $attrs->rekey();
            }
        } else {
            if (count($attrs) - 1 != array_key_last($attrs)) {
                $attrs = array_values($attrs);
            }
        }
    }

    /** @psalm-suppress UndefinedPropertyAssignment */
    public static function mapInAttrs(Constructed $attr): void
    {
        if (self::extensionMatch('id-aa-timeStampToken', $attr['type'])) {
            ASN1::disableCacheInvalidation();
            for ($i = 0; $i < count($attr['value']); $i++) {
                $attr['value'][$i] = SignedData::load((string) $attr['value'][$i]);
            }
            ASN1::enableCacheInvalidation();
            return;
        }
        $map = self::getAttrMapping("$attr[type]");
        if (is_null($map)) {
            return;
        }
        $rules = [];
        switch ($attr['type']) {
            case 'id-aa-signingCertificate':
            case 'id-aa-signingCertificateV2':
                $rules['certs']['*']['issuerSerial']['issuer']['*']['directoryName']['rdnSequence']['*']['*'] = self::mapInDNs(...);
        }
        ASN1::disableCacheInvalidation();
        for ($i = 0; $i < count($attr['value']); $i++) {
            $attr['value'][$i] = ASN1::map(ASN1::decodeBER($attr['value'][$i]->value), $map, $rules);
            $attr['value'][$i]->parent = $attr['value'];
            $attr['value'][$i]->key = $i;
            /** @psalm-suppress InvalidPropertyFetch */
            $attr['value'][$i]->depth = $attr['value']->depth + 1;
        }
        ASN1::enableCacheInvalidation();
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function listSignedAttrs(): array
    {
        $names = [];
        foreach ($this->signer['signedAttrs'] as $attr) {
            $names[] = (string) $attr['type'];
        }
        return $names;
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function hasSignedAttr(string $type): bool
    {
        return $this->getSignedAttr($type) !== null;
    }

    public function getSignedAttr(string $type): ?Constructed
    {
        $this->compile();
        foreach ($this->signer['signedAttrs'] as $attr) {
            if (self::extensionMatch($type, $attr['type'])) {
                return $attr['value'];
            }
        }
        return null;
    }

    public function setSignedAttr(string $type, mixed $value): void
    {
        $this->compile();
        for ($i = 0; $i < count($this->signer['signedAttrs']); $i++) {
            $attr = &$this->signer['signedAttrs'][$i];
            if (self::extensionMatch($type, $attr['type'])) {
                $attr['value'] = [$value];
                return;
            }
        }
        $this->signer['signedAttrs'][] = ['type' => $type, 'value' => [$value]];
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function listUnsignedAttrs(): array
    {
        $names = [];
        foreach ($this->signer['unsignedAttrs'] as $attr) {
            $names[] = (string) $attr['type'];
        }
        return $names;
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function hasUnsignedAttr(string $type): bool
    {
        return $this->getUnsignedAttr($type) !== null;
    }

    public function getUnsignedAttr(string $type): ?Constructed
    {
        $this->compile();
        foreach ($this->signer['unsignedAttrs'] as $attr) {
            if (self::extensionMatch($type, $attr['type'])) {
                return $attr['value'];
            }
        }
        return null;
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function setUnsignedAttr(string $type, mixed $value): void
    {
        $this->compile();
        for ($i = 0; $i < count($this->signer['unsignedAttrs']); $i++) {
            $attr = &$this->signer['unsignedAttrs'][$i];
            if (self::extensionMatch($type, $attr['type'])) {
                $attr['value'] = [$value];
                return;
            }
        }
        $this->signer['unsignedAttrs'][] = ['type' => $type, 'value' => [$value]];
    }

    public function matchesX509(X509 $x509): bool
    {
        /*
          The recipient MAY obtain the correct public key for the signer
          by any means, but the preferred method is from a certificate obtained
          from the SignedData certificates field.
        */
        $ESSCertID = $this->getSignedAttr('id-aa-signingCertificate');
        $ESSCertIDv2 = $this->getSignedAttr('id-aa-signingCertificateV2');
        if (isset($ESSCertIDv2) || isset($ESSCertID)) {
            if (isset($ESSCertIDv2)) {
                $expected = $ESSCertIDv2[0]['certs'][0];
                $hash = preg_replace('#^id-#', '', (string) $expected['hashAlgorithm']['algorithm']);
            } else {
                $expected = $ESSCertID[0]['certs'][0];
                $hash = 'sha1';
            }
            $expectedHash = (string) $expected['certHash'];
            // issuerSerial is OPTIONAL (RFC 2634 Â§ 5.4.1 / RFC 5035 Â§ 4); when it's absent the certHash
            // alone identifies the certificate
            if (isset($expected['issuerSerial'])) {
                $expectedCert = [
                    'issuerAndSerialNumber' => [
                        'issuer' => $expected['issuerSerial']['issuer'][0]['directoryName'],
                        'serialNumber' => $expected['issuerSerial']['serialNumber'],
                    ]
                ];
            }
            $hash = new Hash($hash);
        }
        if (isset($expected)) {
            $certHash = $hash->hash($x509->getEncoded());
            if (!hash_equals($expectedHash, $certHash)) {
                return false;
            }
            // uncomment the following out to look at EITHER the ESS attribute or $this->>signer['sid']
            //if (!isset($expectedCert) || $cert->isIssuerOf($expectedCert)) {
            //    return $cert;
            //}
            //continue;
            // "The digitalSignature bit is asserted when the subject public key
            //  is used for verifying digital signatures, other than signatures on
            //  certificates (bit 5) and CRLs (bit 6)"
            // -- https://datatracker.ietf.org/doc/html/rfc5280#page-30
            //
            // in spite of the above, a lot of EU certs use nonRepudiation, exclusively,
            // so we'll support that as well
            if (isset($expectedCert) && !$x509->isIssuerOf($expectedCert, ['digitalSignature', 'nonRepudiation'])) {
                return false;
            }
        }

        return $x509->isIssuerOf($this->signer['sid'], ['digitalSignature', 'nonRepudiation']);
    }

    public function getCertificate(): ?X509
    {
        foreach ($this->cms['content']['certificates'] as $cert) {
            switch (true) {
                // standard X509 cert
                case isset($cert['certificate']): // && $cert['certificate'] instanceof X509:
                    $cert = $cert['certificate'];
                    break;
                // extended certificates are basically wrappers around regular X509 certs with unsigned attributes
                // living alongside the cert. this was intended for pre-v3 X509 certs where extensions were not
                // included
                case isset($cert['extendedCertificate']): // obsolete
                    //$cert = $cert['extendedCertificiate']['certificate'];
                    //break;
                case isset($cert['v1AttrCert']): // obsolete
                    // ['v1AttrCert']['acInfo'] = $AttributeCertificateInfoV1 ?
                case isset($cert['v2AttrCert']):
                    // ['v2AttrCert']['acInfo'] = $AttributeCertificateInfo ?
                case isset($cert['other']):
                    // ['other']['otherCert'] = ???
                    continue 2;
            }
            if ($this->matchesX509($cert)) {
                return $cert;
            }
        }
        return null;
    }

    private function setHash(string $hash): void
    {
        $origHash = strtolower($hash);
        $hash = match ($hash) {
            'md2' => 'md2',
            'md4' => 'md4',
            'md5' => 'md5',
            'sha1' => 'id-sha1',
            'sha256' => 'id-sha256',
            'sha384' => 'id-sha384',
            'sha224' => 'id-sha224',
            'sha512/224' => 'id-sha512/224',
            'sha512/256' => 'id-sha512/256',
            'sha512' => 'id-sha512',
            default => null
        };
        if (!isset($hash)) {
            throw new UnsupportedAlgorithmException("$origHash is not a supported algorithm: try md2, md4, md5, sha1, sha224, sha256, sha384, sha512, sha512/224 or sha512/256 instead");
        }
        $hashObj = new Hash($origHash);
        $this->compile();
        $this->signer['digestAlgorithm'] = ['algorithm' => $hash];
        if (count($this->signer['signedAttrs'])) {
            $messageDigest = $this->cms->calculateFileHash($origHash);
            // explicitly setting the following to OctetString is necessary because AttributeValue is type ANY and
            // if ANY is encountered with a string ASN1::encodeDER will assume the type is UTF8_STRING - not OCTET_STRING.
            $this->setSignedAttr('id-messageDigest', new OctetString($messageDigest));
            for ($i = 0; $i < count($this->signer['signedAttrs']); $i++) {
                $attr = &$this->signer['signedAttrs'][$i];
                if (self::extensionMatch('id-aa-signingCertificateV2', $attr['type'])) {
                    $checkKeyUsage = X509::isCheckKeyUsageEnabled();
                    if ($checkKeyUsage) {
                        X509::ignoreKeyUsage();
                    }
                    $cert = $this->getCertificate();
                    if ($checkKeyUsage) {
                        X509::checkKeyUsage();
                    }
                    if (!isset($cert)) {
                        throw new UnexpectedValueException('Unable to find matching id-aa-signingCertificateV2 certificate');
                    }
                    $attr['value'][0]['certs'][0]['hashAlgorithm'] = ['algorithm' => $hash];
                    // explicitly setting the following to OctetString isn't needed as Hash is type OCTET_STRING, however,
                    // since we're explicitly setting it for id-messageDigest, above, we might as well be consistent
                    $attr['value'][0]['certs'][0]['certHash'] = new OctetString($hashObj->hash($cert->getEncoded()));
                }
            }
        }
        $this->cms->recalculateHashAlgorithms();
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function validateSignature(bool $caonly = true): bool
    {
        if (count($this->signer['signedAttrs'])) {
            $messageDigest = $this->cms->calculateFileHash(preg_replace('#^id-#', '', (string) $this->signer['digestAlgorithm']['algorithm']));
            $expectedDigest = $this->getSignedAttr('id-messageDigest');
            if (!isset($expectedDigest)) {
                return false;
            }
            if (!hash_equals($messageDigest, (string) $expectedDigest[0])) {
                return false;
            }
        }

        $signingCert = $this->getCertificate();
        if (!isset($signingCert)) {
            return false;
        }

        $algo = (string) $this->signer['signatureAlgorithm']['algorithm'];
        if ($algo == 'rsaEncryption') {
            $algo = match ((string) $this->signer['digestAlgorithm']['algorithm']) {
                'id-md2' => 'md2WithRSAEncryption',
                'id-md5' => 'md5WithRSAEncryption',
                'id-sha1' => 'sha1WithRSAEncryption',
                'id-sha224' => 'sha224WithRSAEncryption',
                'id-sha256' => 'sha256WithRSAEncryption',
                'id-sha384' => 'sha384WithRSAEncryption',
                'id-sha512' => 'sha512WithRSAEncryption',
                'id-sha512/224' => 'sha512-224WithRSAEncryption',
                'id-sha512/256' => 'sha512-256WithRSAEncryption',
                default => null
            };
            if (!isset($algo)) {
                throw new UnsupportedAlgorithmException($this->signer['digestAlgorithm']['algorithm'] . ' is not a supported digest algorithm');
            }
        }
        /*
        The IMPLICIT [0] tag in the signedAttrs is not used for the DER
        encoding, rather an EXPLICIT SET OF tag is used.  That is, the DER
        encoding of the EXPLICIT SET OF tag, rather than of the IMPLICIT [0]
        tag, MUST be included in the message digest calculation along with
        the length and content octets of the SignedAttributes value.
        */
        $signatureSubject = count($this->signer['signedAttrs']) ?
            chr(ASN1::TYPE_SET | 0x20) . substr((string) $this->signer['signedAttrs'], 1) :
            (string) $this->cms['content']['encapContentInfo']['eContent'];

        if ($caonly && !$signingCert->validateSignature()) {
            return false;
        }

        // for X509's, CRL's and CSR's signatures are BIT STRINGs. for CMS's they're OCTET STRINGs.
        return self::validateSignatureHelper(
            $signingCert->getPublicKey(),
            $algo,
            new BitString("\0" . $this->signer['signature']),
            $signatureSubject
        );
    }

    public function getSignableSection(): string
    {
        if (isset($this->signer['signedAttrs'])) {
            $this->compile();
            return chr(ASN1::TYPE_SET | 0x20) . substr((string) $this->signer['signedAttrs'], 1);
        } else {
            return (string) $this->cms['content']['encapContentInfo']['eContent'];
        }
    }

    public function setSignature(string $signature): void
    {
        $this->signer['signature'] = new OctetString("$signature");
    }

    /**
     * Identify signature algorithm from private key
     *
     * @throws UnsupportedAlgorithmException if the algorithm is unsupported
     */
    public function identifySignatureAlgorithm(PrivateKey $key): void
    {
        $algorithm = self::identifySignatureAlgorithmHelper($key);
        $this->signer['signatureAlgorithm'] = $algorithm;
        $this->setHash((string) $key->getHash());
    }

    public function copySigningX509Attributes(X509 $x509): void
    {
        $expected = $this->getCertificate();
        $addCert = true;
        if (isset($expected)) {
            $expected = (string) $expected;
            $total = 0;
            // we only want to replace the cert if there's only one signer using that cert
            foreach ($this->cms->getSigners() as $signer) {
                $found = (string) $signer->getCertificate();
                if ($found === $expected) {
                    $total++;
                }
            }
            if ($total === 1) {
                $addCert = false;
                for ($i = 0; $i < count($this->cms['content']['certificates']); $i++) {
                    switch (true) {
                        case isset($this->cms['content']['certificates'][$i]['certificate']):
                            $found = (string) $this->cms['content']['certificates'][$i]['certificate'];
                        //    break;
                        //case isset($this->cms['content']['certificates'][$i]['extendedCertificate']['certificate']):
                        //    $found = (string) $this->cms['content']['certificates'][$i]['extendedCertificate']['certificate'];
                    }
                    if ($found === $expected) {
                        $this->cms['content']['certificates'][$i]['certificate'] = $x509;
                    }
                }
            }
        }
        if ($addCert) {
            $this->cms->addCertificate($x509);
        }
        $sid = ['issuerAndSerialNumber' =>
            [
                'issuer' => $x509['tbsCertificate']['issuer'],
                'serialNumber' => $x509['tbsCertificate']['serialNumber'],
            ]
        ];
        $this->signer['sid'] = $sid;
        $this->signer['version'] = 'v1';
        for ($i = 0; $i < count($this->signer['signedAttrs']); $i++) {
            $attr = &$this->signer['signedAttrs'][$i];
            if (self::extensionMatch('id-aa-signingCertificateV2', $attr['type'])) {
                $hash = preg_replace('#^id-#', '', (string) $attr['value'][0]['certs'][0]['hashAlgorithm']['algorithm']);
                $hash = new Hash($hash);
                $attr['value'][0]['certs'][0]['certHash'] = new OctetString($hash->hash($x509->getEncoded()));
                // the following errors out and idk why
                //$attr['value'][0]['certs'][0]['issuerSerial']['issuer'][0]['directoryName'] = $x509['tbsCertificate']['issuer'];
                // consequently we do this:
                /** @psalm-suppress InvalidArrayOffset */
                $attr['value'][0]['certs'][0]['issuerSerial']['issuer'][0] = ['directoryName' => $x509['tbsCertificate']['issuer']];
                $attr['value'][0]['certs'][0]['issuerSerial']['serialNumber'] = $x509['tbsCertificate']['serialNumber'];
                break;
            }
        }
    }

    public function __debugInfo(): array
    {
        $this->compile();
        return $this->signer->__debugInfo();
    }

    public function compile(): void
    {
        if (!$this->signer instanceof Constructed) {
            $temp = self::load("$this");
            $this->signer = $temp->signer;
            return;
        }
        if ($this->signer->hasEncoded()) {
            return;
        }
        $oldParent = $this->signer->parent;
        $temp = self::load("$this");
        $this->signer = $temp->signer;
        $this->signer->parent = $oldParent;
    }

    public function &offsetGet(mixed $offset): mixed
    {
        $this->compile();
        /** @psalm-suppress NonVariableReferenceReturn */
        return $this->signer[$offset];
    }

    public function offsetExists(mixed $offset): bool
    {
        return isset($this->signer[$offset]);
    }

    public function offsetSet(mixed $offset, mixed $value): void
    {
        $this->signer[$offset] = $value;
    }

    public function offsetUnset(mixed $offset): void
    {
        unset($this->signer[$offset]);
    }

    public function count(): int
    {
        return is_array($this->signer) ? count($this->signer) : $this->signer->count();
    }

    public function rewind(): void
    {
        $this->compile();
        $this->signer->rewind();
    }

    public function current(): mixed
    {
        $this->compile();
        return $this->signer->current();
    }

    public function key(): mixed
    {
        $this->compile();
        return $this->signer->key();
    }

    public function next(): void
    {
        $this->compile();
        $this->signer->next();
    }

    public function valid(): bool
    {
        $this->compile();
        return $this->signer->valid();
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function keys(): array
    {
        return $this->signer instanceof Constructed ? $this->signer->keys() : array_keys($this->signer);
    }

    public function toArray(bool $convertPrimitives = false): array
    {
        $this->compile();
        return $this->signer instanceof Constructed ? $this->signer->toArray($convertPrimitives) : $this->signer;
    }

    public function getEncoded(): string
    {
        $this->compile();
        return $this->signer->getEncoded();
    }

    public function hasEncoded(): bool
    {
        //$this->compile();
        return $this->signer->hasEncoded();
    }
}
<?php

/**
 * Pure-PHP CMS / CompressedData Parser
 *
 * PHP version 8.1+
 *
 * Encode and decode CMS / CompressedData files.
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\CMS;

use phpseclib4\Common\Functions\Strings;
use phpseclib4\Exception\{BadConfigurationException, UnsupportedValueException};
use phpseclib4\File\ASN1\{Constructed, Element, Maps};
use phpseclib4\File\{ASN1, CMS};

/**
 * Pure-PHP CMS / CompressedData Parser
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @implements \ArrayAccess<string, mixed>
 * @implements \Iterator<string, mixed>
 */
class CompressedData implements \ArrayAccess, \Countable, \Iterator
{
    private Constructed|array $cms;
    private string $decompressed;

    /**
     * @param string $data
     */
    public function __construct(string $data)
    {
        if (!function_exists('zlib_encode')) {
            throw new BadConfigurationException('zlib_encode() is not available');
        }
        $this->decompressed = $data;
        $this->cms = [
            'contentType' => 'id-ct-compressedData',
            'content' => [
                'version' => 'v0',
                'compressionAlgorithm' => ['algorithm' => 'id-alg-zlibCompress'],
                'encapContentInfo' => [
                    'eContentType' => 'id-data',
                    'eContent' => zlib_encode($data, ZLIB_ENCODING_DEFLATE),
                ],
            ]
        ];
    }

    // CMS::load() takes care of the PEM / DER encoding toggling
    // if you want to load an array or Constructed as a SignedData instance you'll
    // need to call CMS\SignedData::load()
    public static function load(string|array|Constructed $encoded): self
    {
        $r = new \ReflectionClass(__CLASS__);
        $cms = $r->newInstanceWithoutConstructor();
        $cms->cms = is_string($encoded) ? self::loadString($encoded) : $encoded;
        return $cms;
    }

    private static function loadString(string $encoded): Constructed
    {
        $decoded = ASN1::decodeBER($encoded);
        $cms = ASN1::map($decoded, Maps\ContentInfo::MAP);
        $decoded = ASN1::decodeBER($cms['content']->value);
        $cms['content'] = ASN1::map($decoded, Maps\CompressedData::MAP);
        $cms['content']->parent = $cms;
        $cms['content']->key = 'content';
        return $cms;
    }

    public function &offsetGet(mixed $offset): mixed
    {
        $this->compile();
        /** @psalm-suppress NonVariableReferenceReturn */
        return $this->cms[$offset];
    }

    public function offsetExists(mixed $offset): bool
    {
        return isset($this->cms[$offset]);
    }

    public function offsetSet(mixed $offset, mixed $value): void
    {
        $this->cms[$offset] = $value;
    }

    public function offsetUnset(mixed $offset): void
    {
        unset($this->cms[$offset]);
    }

    public function count(): int
    {
        return is_array($this->cms) ? count($this->cms) : $this->cms->count();
    }

    public function rewind(): void
    {
        $this->compile();
        $this->cms->rewind();
    }

    public function current(): mixed
    {
        $this->compile();
        return $this->cms->current();
    }

    public function key(): mixed
    {
        $this->compile();
        return $this->cms->key();
    }

    public function next(): void
    {
        $this->compile();
        $this->cms->next();
    }

    public function valid(): bool
    {
        $this->compile();
        return $this->cms->valid();
    }

    public function toString(array $options = []): string
    {
        if ($this->cms instanceof Constructed) {
            ASN1::encodeDER($this->cms['content'], Maps\CompressedData::MAP);
            $cms = ASN1::encodeDER($this->cms, Maps\ContentInfo::MAP);
        } else {
            $temp = [
                'contentType' => $this->cms['contentType'], // 99% of the time this'll be 'id-compressedData'
                'content' => new Element(ASN1::encodeDER($this->cms['content'], Maps\CompressedData::MAP)),
            ];
            $cms = ASN1::encodeDER($temp, Maps\ContentInfo::MAP);
            $this->cms = self::load($cms)->cms;
        }

        if ($options['binary'] ?? CMS::$binary) {
            return $cms;
        }

        return "-----BEGIN CMS-----\r\n" . chunk_split(Strings::base64_encode($cms), 64) . '-----END CMS-----';
    }

    public function __toString(): string
    {
        return $this->toString();
    }

    public function compile(): void
    {
        if (!$this->cms instanceof Constructed || !$this->cms->hasEncoded()) {
            $temp = self::load($this->toString(['binary' => true]));
            $this->cms = $temp->cms;
        }
    }

    public function __debugInfo(): array
    {
        $this->compile();
        return $this->cms->__debugInfo();
    }

    public function getContent(): string
    {
        if (!isset($this->decompressed)) {
            if (!function_exists('zlib_decode')) {
                throw new BadConfigurationException('zlib_decode() is not available');
            }
            $result = zlib_decode((string) $this->cms['content']['encapContentInfo']['eContent']);
            if ($result === false) {
                throw new UnsupportedValueException('zlib_decode() returned false');
            }
            $this->decompressed = $result;
        }
        return $this->decompressed;
    }

    public function toArray(bool $convertPrimitives = false): array
    {
        $this->compile();
        return $this->cms instanceof Constructed ? $this->cms->toArray($convertPrimitives) : $this->cms;
    }
}
<?php

/**
 * Pure-PHP CMS / PasswordRecipient Parser
 *
 * PHP version 8.1+
 *
 * Encode and decode CMS / EnvelopedData / PasswordRecipient files.
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\CMS\EnvelopedData;

use phpseclib4\File\ASN1\Constructed;
use phpseclib4\File\ASN1\Types\Choice;
use phpseclib4\File\CMS\EncryptedData;

/**
 * @author  Jim Wigginton <terrafrost@php.net>
 * @implements \ArrayAccess<string, mixed>
 * @implements \Iterator<string, mixed>
 */
abstract class Recipient implements \ArrayAccess, \Countable, \Iterator
{
    use \phpseclib4\File\Common\Traits\KeyDerivation;

    public Constructed|array|null $recipient;
    public ?EncryptedData $cms = null;
    /** @psalm-suppress PossiblyUnusedProperty */
    public ?Choice $parent;
    /** @psalm-suppress PossiblyUnusedProperty */
    public int $depth = 0;
    /** @psalm-suppress PossiblyUnusedProperty */
    public int|string $key;

    public function __construct(Constructed|array|null $recipient = null)
    {
        $this->recipient = $recipient;
    }

    public static function load(string|array|Constructed $encoded): static
    {
        $recipient = new static();
        $recipient->recipient = is_string($encoded) ? static::loadString($encoded) : $encoded;
        return $recipient;
    }

    public function compile(): void
    {
        if (!$this->recipient instanceof Constructed) {
            $temp = self::load("$this");
            $this->recipient = $temp->recipient;
            return;
        }
        if ($this->recipient->hasEncoded()) {
            return;
        }
        $oldParent = $this->recipient->parent;
        $temp = self::load("$this");
        $this->recipient = $temp->recipient;
        $this->recipient->parent = $oldParent;
    }

    public function getEncoded(): string
    {
        $this->compile();
        return $this->recipient->getEncoded();
    }

    public function &offsetGet(mixed $offset): mixed
    {
        $this->compile();
        /** @psalm-suppress NonVariableReferenceReturn */
        return $this->recipient[$offset];
    }

    public function offsetExists(mixed $offset): bool
    {
        return isset($this->recipient[$offset]);
    }

    public function offsetSet(mixed $offset, mixed $value): void
    {
        $this->recipient[$offset] = $value;
    }

    public function offsetUnset(mixed $offset): void
    {
        unset($this->recipient[$offset]);
    }

    public function count(): int
    {
        return is_array($this->recipient) ? count($this->recipient) : $this->recipient->count();
    }

    public function rewind(): void
    {
        $this->compile();
        $this->recipient->rewind();
    }

    public function current(): mixed
    {
        $this->compile();
        return $this->recipient->current();
    }

    public function key(): mixed
    {
        $this->compile();
        return $this->recipient->key();
    }

    public function next(): void
    {
        $this->compile();
        $this->recipient->next();
    }

    public function valid(): bool
    {
        $this->compile();
        return $this->recipient->valid();
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function keys(): array
    {
        return $this->recipient instanceof Constructed ? $this->recipient->keys() : array_keys($this->recipient);
    }

    public function __toString(): string
    {
        return $this->toString();
    }

    public function __debugInfo(): array
    {
        $this->compile();
        return $this->recipient->__debugInfo();
    }

    public function toArray(bool $convertPrimitives = false): array
    {
        $this->compile();
        return $this->recipient instanceof Constructed ? $this->recipient->toArray($convertPrimitives) : $this->recipient;
    }
}
<?php

/**
 * Pure-PHP CMS / KeyAgreeRecipient Parser
 *
 * PHP version 8.1+
 *
 * Encode and decode CMS / EnvelopedData / KeyAgreeRecipient files.
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\CMS\EnvelopedData;

use phpseclib4\File\ASN1;
use phpseclib4\File\ASN1\{Constructed, Maps};

class KeyAgreeRecipient extends Recipient
{
    /** @psalm-suppress PossiblyUnusedMethod */
    protected static function loadString(string $encoded): Constructed
    {
        //ASN1::disableCacheInvalidation();
        $decoded = ASN1::decodeBER($encoded);
        $rules = [];
        $rules['keyEncryptionAlgorithm'] = self::mapInAlgoParams(...);
        $rules['recipientEncryptedKeys'] = self::mapInEncryptedKeys(...);
        $recipient = ASN1::map($decoded, Maps\KeyAgreeRecipientInfo::MAP, $rules);
        //ASN1::enableCacheInvalidation();
        return $recipient;
    }

    public static function mapInAlgoParams(Constructed $algorithm): void
    {
        ASN1::disableCacheInvalidation();
        $temp = ASN1::decodeBER($algorithm['parameters']->getEncoded());
        $algorithm['parameters'] = ASN1::map($temp, Maps\AlgorithmIdentifier::MAP);
        ASN1::enableCacheInvalidation();
    }

    public static function mapInEncryptedKeys(Constructed $keys): void
    {
        ASN1::disableCacheInvalidation();
        foreach ($keys as $i => $key) {
            $keys[$i] = new KeyAgreeRecipient\EncryptedKey($key);
            $keys[$i]->parent = $keys;
            $keys[$i]->key = $i;
            $keys[$i]->depth = $keys->depth + 1;
            //$keys[$i]->cms = $keys->cms;
            //$keys[$i]->recipient = $keys->recipient;
        }
        ASN1::enableCacheInvalidation();
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function toString(): string
    {
        $recipient = ASN1::encodeDER($this->recipient, Maps\KeyAgreeRecipientInfo::MAP);

        return $recipient;
    }

    public function compile(): void
    {
        if (!$this->recipient instanceof Constructed) {
            $temp = self::load("$this");
            $this->recipient = $temp->recipient;
            return;
        }
        if ($this->recipient->hasEncoded()) {
            return;
        }
        $temp = self::load("$this");
        foreach ($temp as $key => $val) {
            if ($key == 'recipientEncryptedKeys') {
                continue;
            }
            $this->recipient[$key] = $val;
        }
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function getEncryptedKeys(): array
    {
        $result = [];
        foreach ($this->recipient['recipientEncryptedKeys'] as $key) {
            $result[] = $key;
        }
        return $result;
    }
}
<?php

/**
 * Pure-PHP CMS / KEKRecipient Parser
 *
 * PHP version 8.1+
 *
 * Encode and decode CMS / EnvelopedData / KEKRecipient files.
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\CMS\EnvelopedData;

use phpseclib4\Exception\UnsupportedAlgorithmException;
use phpseclib4\File\ASN1;
use phpseclib4\File\ASN1\{Constructed, Maps};

class KEKRecipient extends Recipient implements DerivableKey
{
    /** @psalm-suppress PossiblyUnusedMethod */
    protected static function loadString(string $encoded): Constructed
    {
        //ASN1::disableCacheInvalidation();
        $decoded = ASN1::decodeBER($encoded);
        $recipient = ASN1::map($decoded, Maps\KEKRecipientInfo::MAP);
        //ASN1::enableCacheInvalidation();
        return $recipient;
    }

    public function withKey(#[\SensitiveParameter] string $key): self
    {
        $kek = $key;
        $encryptedKey = (string) $this->recipient['encryptedKey'];
        switch ($this->recipient['keyEncryptionAlgorithm']['algorithm']) {
            case 'id-aes128-wrap':
            case 'id-aes192-wrap':
            case 'id-aes256-wrap':
                // from https://datatracker.ietf.org/doc/html/rfc3394.html#section-2.2.3.1
                $iv = "\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6";
                $cek = self::unwrapAES($kek, $iv, $encryptedKey);
                break;
            case 'id-alg-CMS3DESwrap':
                $cek = self::unwrap3DES($kek, $encryptedKey);
                break;
            default:
                throw new UnsupportedAlgorithmException($this->recipient['keyEncryptionAlgorithm']['algorithm'] . ' is not a supported algorithm');
        }

        //if (!$this->cms instanceof Constructed) {
        $this->cms->cek = $cek;
        //}

        return $this;
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function toString(): string
    {
        $recipient = ASN1::encodeDER($this->recipient, Maps\KEKRecipientInfo::MAP);

        return $recipient;
    }
}
<?php

/**
 * DerivableKey interface
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\CMS\EnvelopedData;

/**
 * DerivableKey interface
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
interface DerivableKey
{
    public function decrypt(): string;
}
<?php

/**
 * Searchable interface
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\CMS\EnvelopedData;

/**
 * Searchable interface
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
interface SearchableKey
{
}
<?php

/**
 * Pure-PHP CMS / PasswordRecipient Parser
 *
 * PHP version 8.1+
 *
 * Encode and decode CMS / EnvelopedData / PasswordRecipient files.
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\CMS\EnvelopedData;

use phpseclib4\Exception\{UnexpectedValueException, UnsupportedAlgorithmException};
use phpseclib4\File\ASN1;
use phpseclib4\File\ASN1\{Constructed, Maps};

class PasswordRecipient extends Recipient implements DerivableKey, SearchableKey
{
    /** @psalm-suppress PossiblyUnusedMethod */
    protected static function loadString(string $encoded): Constructed
    {
        //ASN1::disableCacheInvalidation();
        $rules = [];
        $rules['keyEncryptionAlgorithm'] = $rules['keyDerivationAlgorithm'] = self::mapInAlgoParams(...);
        $decoded = ASN1::decodeBER($encoded);
        $recipient = ASN1::map($decoded, Maps\PasswordRecipientInfo::MAP, $rules);
        //ASN1::enableCacheInvalidation();
        return $recipient;
    }

    public static function mapInAlgoParams(Constructed $algorithm): void
    {
        $temp = ASN1::decodeBER($algorithm['parameters']->getEncoded());
        $temp = match ((string) $algorithm['algorithm']) {
            'id-alg-PWRI-KEK' => ASN1::map($temp, Maps\AlgorithmIdentifier::MAP),
            'id-PBKDF2' => ASN1::map($temp, Maps\PBKDF2params::MAP),
            default => null,
        };
        ASN1::disableCacheInvalidation();
        if (isset($temp)) {
            $algorithm['parameters'] = $temp;
        }
        ASN1::enableCacheInvalidation();
    }

    public function withPassword(#[\SensitiveParameter] string $password): self
    {
        if ($this->recipient['keyEncryptionAlgorithm']['algorithm'] != 'id-alg-PWRI-KEK') {
            throw new UnsupportedAlgorithmException(
                'id-alg-PWRI-KEK is the only supported keyEncryptionAlgorithm (' .
                $this->recipient['keyEncryptionAlgorithm']['algorithm'] . ' found)'
            );
        }

        // see https://datatracker.ietf.org/doc/html/rfc3211
        $keyCipher = self::getPBES2EncryptionObject((string) $this->recipient['keyEncryptionAlgorithm']['parameters']['algorithm']);
        $keyCipher->disablePadding();
        self::setupPBKDF2($this->recipient['keyDerivationAlgorithm'], $password, $keyCipher);

        $blockSize = $keyCipher->getBlockLengthInBytes();

        $encrypted = (string) $this->recipient['encryptedKey'];
        $keyCipher->setIV(substr($encrypted, 0, $blockSize));
        $result = $keyCipher->decrypt(substr($encrypted, $blockSize));
        $keyCipher->setIV(substr($result, -$keyCipher->getBlockLengthInBytes()));
        $result = $keyCipher->decrypt(substr($encrypted, 0, $blockSize)) . $result;
        $keyCipher->setIV((string) $this->recipient['keyEncryptionAlgorithm']['parameters']['parameters']);
        $result = $keyCipher->decrypt($result);

        $length = ord($result);
        $cek = substr($result, 4, $length);
        $keyCheck = ~substr($result, 1, 3);
        if (substr($cek, 0, 3) != $keyCheck) {
            throw new UnexpectedValueException('keyCheck failed');
        }

        //if (!$this->cms instanceof Constructed) {
        $this->cms->cek = $cek;
        //}

        return $this;
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function toString(): string
    {
        $recipient = ASN1::encodeDER($this->recipient, Maps\PasswordRecipientInfo::MAP);

        return $recipient;
    }
}
<?php

/**
 * Pure-PHP CMS / KeyTransRecipient Parser
 *
 * PHP version 8.1+
 *
 * Encode and decode CMS / EnvelopedData / KeyTransRecipient files.
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\CMS\EnvelopedData;

use phpseclib4\Common\Functions\Arrays;
use phpseclib4\Crypt\RSA;
use phpseclib4\Exception\UnsupportedAlgorithmException;
use phpseclib4\File\ASN1\{Constructed, Maps};
use phpseclib4\File\{ASN1, X509};

class KeyTransRecipient extends Recipient implements DerivableKey, SearchableKey
{
    /** @psalm-suppress PossiblyUnusedMethod */
    protected static function loadString(string $encoded): Constructed
    {
        //ASN1::disableCacheInvalidation();
        $decoded = ASN1::decodeBER($encoded);
        $rules = [];
        $rules['keyEncryptionAlgorithm'] = self::mapInAlgoParams(...);
        $recipient = ASN1::map($decoded, Maps\KeyTransRecipientInfo::MAP, $rules);
        //ASN1::enableCacheInvalidation();
        return $recipient;
    }

    public static function mapInAlgoParams(Constructed $algorithm): void
    {
        $temp = ASN1::decodeBER($algorithm['parameters']->getEncoded());
        $rules = [];
        switch ($algorithm['algorithm']) {
            case 'id-RSAES-OAEP':
                $rules['maskGenAlgorithm'] = self::mapInAlgoParams(...);
                $map = Maps\RSAES_OAEP_params::MAP;
                break;
            case 'id-mgf1':
                $map = Maps\MaskGenAlgorithm::MAP;
                break;
            default:
                return;
        }
        ASN1::disableCacheInvalidation();
        $algorithm['parameters'] = ASN1::map($temp, $map, $rules);
        ASN1::enableCacheInvalidation();
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function withKey(#[\SensitiveParameter] RSA\PrivateKey $key): self
    {
        $kek = &$key;

        $encryptedKey = (string) $this->recipient['encryptedKey'];
        if ($this->recipient['keyEncryptionAlgorithm']['algorithm'] == 'id-RSAES-OAEP') {
            $kek = $kek->withPadding(RSA::ENCRYPTION_OAEP);
            $algorithm = Arrays::subArray($this->recipient, 'keyEncryptionAlgorithm/parameters/hashAlgorithm')['algorithm'] ?? 'sha1';
            $kek = $kek->withHash(preg_replace('#^id-#', '', "$algorithm"));
            $algorithm = Arrays::subArray($this->recipient, 'keyEncryptionAlgorithm/parameters/maskGenAlgorithm')['algorithm'] ?? 'id-mgf1';
            if ($algorithm != 'id-mgf1') {
                throw new UnsupportedAlgorithmException("Unsupported maskGenAlgorithm ($algorithm) found - only id-mgf1 is supported");
            }
            $algorithm = Arrays::subArray($this->recipient, 'keyEncryptionAlgorithm/parameters/maskGenAlgorithm/parameters')['algorithm'] ?? 'sha1';
            $kek = $kek->withMGFHash(preg_replace('#^id-#', '', "$algorithm"));
            $algorithm = Arrays::subArray($this->recipient, 'keyEncryptionAlgorithm/parameters/pSourceAlgorithm')['algorithm'] ?? 'id-pSpecified';
            if ($algorithm != 'id-pSpecified') {
                throw new UnsupportedAlgorithmException("Unsupported maskGenAlgorithm ($algorithm) found - only id-mgf1 is supported");
            }
            $label = Arrays::subArray($this->recipient, 'keyEncryptionAlgorithm/parameters/pSourceAlgorithm')['parameters'] ?? '';
            $kek = $kek->withLabel("$label");
        }

        //if (!$this->cms instanceof Constructed) {
        $this->cms->cek = $kek->decrypt($encryptedKey);
        //}

        return $this;
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function matchesX509(X509 $x509): bool
    {
        return $x509->isIssuerOf($this->recipient['rid'], ['keyEncipherment']);
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function toString(): string
    {
        $recipient = ASN1::encodeDER($this->recipient, Maps\KeyTransRecipientInfo::MAP);

        return $recipient;
    }
}
<?php

/**
 * Pure-PHP CMS / KeyAgreeRecipient / EncryptedKey Parser
 *
 * PHP version 8.1+
 *
 * Encode and decode CMS / EnvelopedData / KeyAgreeRecipient / EncryptedKey files.
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\CMS\EnvelopedData\KeyAgreeRecipient;

use phpseclib4\Crypt\{EC, DH};
use phpseclib4\Exception\UnsupportedAlgorithmException;
use phpseclib4\File\ASN1\{Constructed, Maps};
use phpseclib4\File\{ASN1, X509};
use phpseclib4\File\CMS\EncryptedData;
use phpseclib4\File\CMS\EnvelopedData\{DerivableKey, KeyAgreeRecipient, SearchableKey};

/**
 * @author  Jim Wigginton <terrafrost@php.net>
 * @implements \ArrayAccess<string, mixed>
 * @implements \Iterator<string, mixed>
 */
class EncryptedKey implements DerivableKey, SearchableKey, \ArrayAccess, \Countable, \Iterator
{
    use \phpseclib4\File\Common\Traits\KeyDerivation;

    public Constructed|array $encryptedKey;
    public EncryptedData $cms;
    public KeyAgreeRecipient $recipient;
    /** @psalm-suppress PossiblyUnusedProperty */
    public ?Constructed $parent;
    /** @psalm-suppress PossiblyUnusedProperty */
    public int $depth = 0;
    /** @psalm-suppress PossiblyUnusedProperty */
    public int|string $key;

    public function __construct(Constructed|array $key)
    {
        $this->encryptedKey = $key;
    }

    public static function load(string|array|Constructed $encoded): static
    {
        $r = new \ReflectionClass(__CLASS__);
        $cms = $r->newInstanceWithoutConstructor();
        $cms->encryptedKey = is_string($encoded) ? static::loadString($encoded) : $encoded;
        return $cms;
    }

    protected static function loadString(string $encoded): Constructed
    {
        //ASN1::disableCacheInvalidation();
        $decoded = ASN1::decodeBER($encoded);
        $temp = ASN1::map($decoded, Maps\RecipientEncryptedKey::MAP);
        //ASN1::enableCacheInvalidation();
        return $temp;
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function withKey(#[\SensitiveParameter] EC\PrivateKey $key): self
    {
        $private = &$key;

        $originatorKey = $this->recipient['originator']['originatorKey'];
        // we do this because https://datatracker.ietf.org/doc/html/rfc5753#section-3.1.1 says this:
        /*
          The parameters associated with id-ecPublicKey MUST be absent, ECParameters, or NULL.  The
          parameters associated with id-ecPublicKey SHOULD be absent or ECParameters, and NULL is
          allowed to support legacy implementations.  The previous version of this document required
          NULL to be present.
        */
        // PublicKeyLoader::load() won't work if parameters isn't present. like it won't know what curve it is and thus
        // won't be able to load it
        $encoded = $private->getPublicKey()->toString('PKCS8', ['binary' => true]);
        $decoded = ASN1::decodeBER($encoded);
        $mapped = ASN1::map($decoded, Maps\SubjectPublicKeyInfo::MAP);
        $originatorKey['algorithm']['parameters'] = $mapped['algorithm']['parameters'];
        $encoded = ASN1::encodeDER($originatorKey, Maps\OriginatorPublicKey::MAP);
        /** @var EC\PublicKey $public */
        $public = EC::loadPublicKey($encoded);

        $secret = DH::computeSecret($private, $public);

        $dhAlgo = (string) $this->recipient['keyEncryptionAlgorithm']['algorithm'];
        // in standard DH you multiply the private and public key together to get the secret
        // in cofactor DH you multiply the above result by the cofactor as well
        // prime curves always have a cofactor of 1 so standard and cofactor DH yield the same results
        // binary curves have a cofactor of 2 or 4 so standard and cofactor DH yield different results
        if (str_starts_with($dhAlgo, 'dhSinglePass-cofactorDH-') && $private->isBinaryCurve()) {
            throw new UnsupportedAlgorithmException('Cofactor DH on binary curves is not supported');
        }
        if (str_starts_with($dhAlgo, 'mqvSinglePass-')) {
            throw new UnsupportedAlgorithmException('MQV DH is not supported');
        }
        $cekAlgo = (string) $this->recipient['keyEncryptionAlgorithm']['parameters']['algorithm'];
        $length = match ($cekAlgo) {
            'id-aes256-wrap' => 32,
            'id-aes192-wrap' => 24,
            'id-aes128-wrap' => 16,
            'id-alg-CMS3DESwrap' => 24,
            default => null
        };
        if (!isset($length)) {
            throw new UnsupportedAlgorithmException($this->recipient['keyEncryptionAlgorithm']['parameters']['algorithm'] . ' is not a supported algorithm');
        }
        $sharedInfo = [
            'keyInfo' => new ASN1\Element($this->recipient['keyEncryptionAlgorithm']['parameters']->getEncoded()),
            'suppPubInfo' => pack('N', $length << 3)
        ];
        // this can't be set via OpenSSL CLI
        if (isset($this->recipient['ukm'])) {
            $sharedInfo['entityUInfo'] = $this->recipient['ukm'];
        }
        $sharedInfo = ASN1::encodeDER($sharedInfo, Maps\ECCCMSSharedInfo::MAP);

        if (!preg_match('#DH-(sha\d+)kdf-scheme$#', (string) $this->recipient['keyEncryptionAlgorithm']['algorithm'], $matches)) {
            throw new UnsupportedAlgorithmException($this->recipient['keyEncryptionAlgorithm']['algorithm'] . ' is not a supported algorithm');
        }
        $hash = new \phpseclib4\Crypt\Hash($matches[1]);
        $kek = EncryptedData::ANSIX963KDF($secret, $length, $sharedInfo, $hash);

        $encryptedKey = (string) $this->encryptedKey['encryptedKey'];
        if ($cekAlgo == 'id-alg-CMS3DESwrap') {
            $cek = self::unwrapDES($kek, $encryptedKey);
        } else {
            $iv = "\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6";
            $cek = self::unwrapAES($kek, $iv, $encryptedKey);
        }

        //if (!$this->cms instanceof Constructed) {
        $this->cms->cek = $cek;
        //}

        return $this;
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function matchesX509(X509 $x509): bool
    {
        return $x509->isIssuerOf($this->encryptedKey['rid'], ['keyAgreement']);
    }

    public function compile(): void
    {
        if (!$this->encryptedKey instanceof Constructed) {
            $temp = self::load("$this");
            $this->encryptedKey = $temp->encryptedKey;
            return;
        }
        if ($this->encryptedKey->hasEncoded()) {
            return;
        }
        $oldParent = $this->encryptedKey->parent;
        $temp = self::load("$this");
        $this->encryptedKey = $temp->encryptedKey;
        $this->encryptedKey->parent = $oldParent;
    }

    public function getEncoded(): string
    {
        $this->compile();
        return $this->encryptedKey->getEncoded();
    }

    public function &offsetGet(mixed $offset): mixed
    {
        $this->compile();
        /** @psalm-suppress NonVariableReferenceReturn */
        return $this->encryptedKey[$offset];
    }

    public function offsetExists(mixed $offset): bool
    {
        return isset($this->encryptedKey[$offset]);
    }

    public function offsetSet(mixed $offset, mixed $value): void
    {
        $this->encryptedKey[$offset] = $value;
    }

    public function offsetUnset(mixed $offset): void
    {
        unset($this->encryptedKey[$offset]);
    }

    public function count(): int
    {
        return is_array($this->encryptedKey) ? count($this->encryptedKey) : $this->encryptedKey->count();
    }

    public function rewind(): void
    {
        $this->compile();
        $this->encryptedKey->rewind();
    }

    public function current(): mixed
    {
        $this->compile();
        return $this->encryptedKey->current();
    }

    public function key(): mixed
    {
        $this->compile();
        return $this->encryptedKey->key();
    }

    public function next(): void
    {
        $this->compile();
        $this->encryptedKey->next();
    }

    public function valid(): bool
    {
        $this->compile();
        return $this->encryptedKey->valid();
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function keys(): array
    {
        return $this->encryptedKey instanceof Constructed ? $this->encryptedKey->keys() : array_keys($this->encryptedKey);
    }

    public function __toString(): string
    {
        return $this->toString();
    }

    public function __debugInfo(): array
    {
        $this->compile();
        return $this->encryptedKey->__debugInfo();
    }

    public function toArray(bool $convertPrimitives = false): array
    {
        $this->compile();
        return $this->encryptedKey instanceof Constructed ? $this->encryptedKey->toArray($convertPrimitives) : $this->encryptedKey;
    }
}
<?php

/**
 * Pure-PHP CMS / DigestedData Parser
 *
 * PHP version 8.1+
 *
 * Encode and decode CMS / DigestedData files.
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\CMS;

use phpseclib4\Common\Functions\Strings;
use phpseclib4\Crypt\Hash;
use phpseclib4\File\ASN1\{Constructed, Element, Maps};
use phpseclib4\File\ASN1\Types\OctetString;
use phpseclib4\File\{ASN1, CMS};

/**
 * Pure-PHP CMS / DigestedData Parser
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @implements \ArrayAccess<string, mixed>
 * @implements \Iterator<string, mixed>
 */
class DigestedData implements \ArrayAccess, \Countable, \Iterator
{
    private Constructed|array $cms;

    /**
     * @param string $data
     */
    public function __construct(string $data, $hashAlgorithm = 'sha256')
    {
        ASN1::loadOIDs('Hashes');
        $hash = new Hash($hashAlgorithm);
        if (substr($hashAlgorithm, 0, 2) != 'md') {
            $hashAlgorithm = "id-$hashAlgorithm";
        }
        $this->cms = [
            'contentType' => 'id-digestedData',
            'content' => [
                // "If the encapsulated content type is id-data, then the value of version MUST be 0; however, if
                //  the encapsulated content type is other than id-data, then the value of version MUST be 2."
                'version' => 'v0',
                'digestAlgorithm' => ['algorithm' => $hashAlgorithm],
                'encapContentInfo' => [
                    'eContentType' => 'id-data',
                    'eContent' => new OctetString($data),
                ],
                'digest' => $hash->hash($data),
            ]
        ];
    }

    // CMS::load() takes care of the PEM / DER encoding toggling
    // if you want to load an array or Constructed as a SignedData instance you'll
    // need to call CMS\SignedData::load()
    public static function load(string|array|Constructed $encoded): self
    {
        $r = new \ReflectionClass(__CLASS__);
        $cms = $r->newInstanceWithoutConstructor();
        $cms->cms = is_string($encoded) ? self::loadString($encoded) : $encoded;
        return $cms;
    }

    private static function loadString(string $encoded): Constructed
    {
        $decoded = ASN1::decodeBER($encoded);
        $cms = ASN1::map($decoded, Maps\ContentInfo::MAP);
        $decoded = ASN1::decodeBER($cms['content']->value);
        $cms['content'] = ASN1::map($decoded, Maps\DigestedData::MAP);
        $cms['content']->parent = $cms;
        $cms['content']->key = 'content';
        return $cms;
    }

    public function &offsetGet(mixed $offset): mixed
    {
        $this->compile();
        /** @psalm-suppress NonVariableReferenceReturn */
        return $this->cms[$offset];
    }

    public function offsetExists(mixed $offset): bool
    {
        return isset($this->cms[$offset]);
    }

    public function offsetSet(mixed $offset, mixed $value): void
    {
        $this->cms[$offset] = $value;
    }

    public function offsetUnset(mixed $offset): void
    {
        unset($this->cms[$offset]);
    }

    public function count(): int
    {
        return is_array($this->cms) ? count($this->cms) : $this->cms->count();
    }

    public function rewind(): void
    {
        $this->compile();
        $this->cms->rewind();
    }

    public function current(): mixed
    {
        $this->compile();
        return $this->cms->current();
    }

    public function key(): mixed
    {
        $this->compile();
        return $this->cms->key();
    }

    public function next(): void
    {
        $this->compile();
        $this->cms->next();
    }

    public function valid(): bool
    {
        $this->compile();
        return $this->cms->valid();
    }

    public function toString(array $options = []): string
    {
        if ($this->cms instanceof Constructed) {
            ASN1::encodeDER($this->cms['content'], Maps\DigestedData::MAP);
            $cms = ASN1::encodeDER($this->cms, Maps\ContentInfo::MAP);
        } else {
            $temp = [
                'contentType' => $this->cms['contentType'], // 99% of the time this'll be 'id-digestedData'
                'content' => new Element(ASN1::encodeDER($this->cms['content'], Maps\DigestedData::MAP)),
            ];
            $cms = ASN1::encodeDER($temp, Maps\ContentInfo::MAP);
            $this->cms = self::load($cms)->cms;
        }

        if ($options['binary'] ?? CMS::$binary) {
            return $cms;
        }

        return "-----BEGIN CMS-----\r\n" . chunk_split(Strings::base64_encode($cms), 64) . '-----END CMS-----';
    }

    public function __toString(): string
    {
        return $this->toString();
    }

    public function compile(): void
    {
        if (!$this->cms instanceof Constructed || !$this->cms->hasEncoded()) {
            $temp = self::load($this->toString(['binary' => true]));
            $this->cms = $temp->cms;
        }
    }

    public function __debugInfo(): array
    {
        $this->compile();
        return $this->cms->__debugInfo();
    }

    public function validate(): bool
    {
        $hash = new Hash(str_replace('id-', '', (string) $this->cms['content']['digestAlgorithm']['algorithm']));
        $actual = $hash->hash((string) $this->cms['content']['encapContentInfo']['eContent']);
        $expected = (string) $this->cms['content']['digest'];
        return hash_equals($actual, $expected);
    }

    public function toArray(bool $convertPrimitives = false): array
    {
        $this->compile();
        return $this->cms instanceof Constructed ? $this->cms->toArray($convertPrimitives) : $this->cms;
    }
}
<?php

/**
 * DN Helper for misc ASN1 classes
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2025-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\Common\Traits;

use phpseclib4\Common\Functions\Strings;
use phpseclib4\Crypt\Hash;
use phpseclib4\Exception\{
    CharacterConversionException,
    InvalidArgumentException,
    UnexpectedValueException,
    UnsupportedValueException
};
use phpseclib4\File\ASN1;
use phpseclib4\File\ASN1\{Constructed, Element, Maps};
use phpseclib4\File\ASN1\Types\{BaseString, BaseType, Choice, OID, UTF8String};

//use phpseclib4\File\ASN1\Types\OctetString;
/**
 * Extension Helper for misc ASN1 classes
 *
 * Used by X509 and CRL
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
trait DN
{
    public static function mapInDNs(array|Constructed &$dn): void
    {
        ASN1::disableCacheInvalidation();

        if ("$dn[type]" == 'id-at-postalAddress') {
            $dn['value'] = ASN1::map(ASN1::decodeBER($dn['value']->getEncoded()), Maps\PostalAddress::MAP);
            // if we swapped out the above line for the following code then
            // $cert['tbsCertificate']['subject']['rdnSequence'][0][0]['value'] would be an array vs a BaseType
            // object and ->getEncoded() wouldn't work
            /*
            $temp = ASN1::map(ASN1::decodeBER($dn['value']->getEncoded()), Maps\PostalAddress::MAP);
            $dn['value'] = [];
            foreach ($temp as $key => $part) {
                $dn['value'][] = current($part->toArray());
            }
            */
        }

        ASN1::enableCacheInvalidation();
    }

    private static function mapOutDNsInner(array|Constructed|Element &$dns): void
    {
        if ($dns instanceof Element) {
            return;
        }
        $size = count($dns);
        for ($i = 0; $i < $size; $i++) {
            if (!isset($dns[$i]) || !is_countable($dns[$i])) {
                continue;
            }

            for ($j = 0; $j < count($dns[$i]); $j++) {
                if ($dns[$i][$j] instanceof Element) {
                    continue;
                }

                $type = $dns[$i][$j]['type'];
                $value = &$dns[$i][$j]['value'];

                if ($value instanceof Element) {
                    continue;
                }

                self::encodeDNS($type, $value);
                if (is_array($value)) {
                    // maybe we should create an UnableToMapException ?
                    throw new UnsupportedValueException("An unmappable array was encountered for $type");
                }
            }
        }
    }

    private static function encodeDNS(OID|string $type, BaseType|array|string &$value): void
    {
        if ($type == 'id-at-postalAddress') {
            if ($value instanceof BaseType) { // eg. if it's Constructed
                ASN1::encodeDER($value, Maps\PostalAddress::MAP);
            } else {
                foreach ($value as &$val) {
                    if ($val instanceof BaseType) {
                        $class = (new \ReflectionClass($val::class))->getShortName();
                        $key = match ($class) {
                            'TeletexString' => 'teletexString',
                            'PrintableString' => 'printableString',
                            'UniversalString' => 'universalString',
                            'UTF8String' => 'utf8String',
                            'BMPString' => 'bmpString',
                            default => null
                        };
                        if (!isset($key)) {
                            throw new UnsupportedValueException("$class is not a supported value for id-at-postalAddress");
                        }
                        $val = [$key => $val];
                    } elseif (is_string($val)) {
                        $val = ['utf8String' => new UTF8String($val)];
                    } elseif ($val instanceof Element) {
                        $val = ['utf8String' => $val];
                    } elseif (!is_array($val)) {
                        throw new UnexpectedValueException('Invalid value for id-at-postalAddress encountered');
                    }
                }
                $temp = ASN1::encodeDER($value, Maps\PostalAddress::MAP);
                // we do this so that a print_r on the X509 object after string conversion
                // will still show something human readable
                $value = ASN1::map(ASN1::decodeBER($temp), Maps\PostalAddress::MAP);
            }
            $value->enableForcedCache();
        }
    }

    /**
     * "Normalizes" a Distinguished Name property
     */
    private static function translateDNProp(string $propName): string
    {
        // allow raw OIDs through
        if (preg_match('#^\d+(?:\.\d+)+$#', $propName)) {
            return $propName;
        }

        switch (strtolower($propName)) {
            case 'jurisdictionofincorporationcountryname':
            case 'jurisdictioncountryname':
            case 'jurisdictionc':
                return 'jurisdictionOfIncorporationCountryName';
            case 'jurisdictionofincorporationstateorprovincename':
            case 'jurisdictionstateorprovincename':
            case 'jurisdictionst':
                return 'jurisdictionOfIncorporationStateOrProvinceName';
            case 'jurisdictionlocalityname':
            case 'jurisdictionl':
                return 'jurisdictionLocalityName';
            case 'id-at-businesscategory':
            case 'businesscategory':
                return 'id-at-businessCategory';
            case 'id-at-countryname':
            case 'countryname':
            case 'c':
                return 'id-at-countryName';
            case 'id-at-organizationname':
            case 'organizationname':
            case 'o':
                return 'id-at-organizationName';
            case 'id-at-dnqualifier':
            case 'dnqualifier':
                return 'id-at-dnQualifier';
            case 'id-at-commonname':
            case 'commonname':
            case 'cn':
                return 'id-at-commonName';
            case 'id-at-stateorprovincename':
            case 'stateorprovincename':
            case 'state':
            case 'province':
            case 'provincename':
            case 'st':
                return 'id-at-stateOrProvinceName';
            case 'id-at-localityname':
            case 'localityname':
            case 'l':
                return 'id-at-localityName';
            case 'id-at-emailaddress':
            case 'id-emailaddress':
            case 'emailaddress':
                return 'pkcs-9-at-emailAddress';
            case 'id-at-serialnumber':
            case 'serialnumber':
                return 'id-at-serialNumber';
            case 'id-at-postalcode':
            case 'postalcode':
                return 'id-at-postalCode';
            case 'id-at-streetaddress':
            case 'streetaddress':
                return 'id-at-streetAddress';
            case 'id-at-name':
            case 'name':
                return 'id-at-name';
            case 'id-at-givenname':
            case 'givenname':
            case 'gn':
                return 'id-at-givenName';
            case 'id-at-surname':
            case 'surname':
            case 'sn':
                return 'id-at-surname';
            case 'id-at-initials':
            case 'initials':
                return 'id-at-initials';
            case 'id-at-generationqualifier':
            case 'generationqualifier':
                return 'id-at-generationQualifier';
            case 'id-at-organizationalunitname':
            case 'organizationalunitname':
            case 'ou':
                return 'id-at-organizationalUnitName';
            case 'id-at-organizationidentifier':
            case 'organizationIdentifier':
                return 'id-at-organizationIdentifier';
            case 'id-at-pseudonym':
            case 'pseudonym':
                return 'id-at-pseudonym';
            case 'id-at-title':
            case 'title':
                return 'id-at-title';
            case 'id-at-description':
            case 'description':
                return 'id-at-description';
            case 'id-at-role':
            case 'role':
                return 'id-at-role';
            case 'id-at-uniqueidentifier':
            case 'uniqueidentifier':
            case 'x500uniqueidentifier':
                return 'id-at-uniqueIdentifier';
            case 'postaladdress':
            case 'id-at-postaladdress':
                return 'id-at-postalAddress';
            case 'dc':
            case 'domaincomponent':
            case 'id-domaincomponent':
                return 'id-domainComponent';
            default:
                throw new InvalidArgumentException("$propName is not a supported distinguished name attribute");
        }
    }

    // if you want to replace a DN prop you should do removeDNProp and then addDNProp
    // i think adding addDNProps (plural) would be good too.
    // so removeDNProps(), addDNProps(), addDNProp()
    private static function addDNPropsInternal(array|Choice &$dn, string $propName, string|BaseString|array|Element|Constructed $value): void
    {
        $propName = self::translateDNProp($propName);
        if ($propName == 'id-at-postalAddress' && is_string($value) && preg_match('/^#(?:[0-9A-Fa-f][0-9A-Fa-f])+$/', $value)) {
            $temp = [
                'type' => $propName,
                'value' => new Element(pack('H*', substr($value, 1)))
            ];
            self::mapInDNs($temp);
            $dn['rdnSequence'][] = [$temp];
            return;
        }

        $dn['rdnSequence'][] = [
            [
                'type' => $propName,
                'value' => $value
            ]
        ];
    }

    private static function hasDNPropsInternal(array|Choice $dn, string $propName): bool
    {
        $propName = self::translateDNProp($propName);

        $rdn = &$dn['rdnSequence'];
        $size = count($rdn);
        for ($i = 0; $i < $size; $i++) {
            if ($rdn[$i][0]['type'] == $propName) {
                return true;
            }
        }
        return false;
    }

    private static function removeDNPropsInternal(array|Choice &$dn, string $propName): void
    {
        $propName = self::translateDNProp($propName);

        $rdn = &$dn['rdnSequence'];
        $size = count($rdn);
        for ($i = 0; $i < $size; $i++) {
            if ($rdn[$i][0]['type'] == $propName) {
                unset($rdn[$i]);
            }
        }
    }

    private static function setDNInternal(array|Choice &$dn, array|string|Element $props): void
    {
        $dn['rdnSequence'] = [];

        if ($props instanceof Element) {
            $dn = $props;
            return;
        }

        if (empty($props)) {
            $props = ['rdnSequence' => []];
        }

        if (is_array($props)) {
            if (isset($props['rdnSequence'])) {
                $dn = $props;
                return;
            }

            // handles stuff generated by openssl_x509_parse()
            foreach ($props as $prop => $value) {
                if (!is_array($value)) {
                    self::addDNPropsInternal($dn, $prop, $value);
                } else {
                    foreach ($value as $val) {
                        self::addDNPropsInternal($dn, $prop, $val);
                    }
                }
            }
            return;
        }

        if (preg_match('#[^\x20-\x7E]#', $props)) {
            throw new UnexpectedValueException('Non-printable ASCII characters should not be present');
        }

        // openssl_x509_parse() with OpenSSL 3.0+ returns this format
        // this is the default format of OpenSSL's X509_get_subject_name() method
        //    per https://zakird.com/2013/10/13/certificate-parsing-with-openssl
        if ($props[0] == '/') {
            $props = preg_split('#(?<!\\\\)(?:\\\\\\\\)*/#', $props);
            array_shift($props);
            foreach ($props as $prop) {
                preg_match('#(^[a-zA-Z0-9\.]+)=(.*)#', $prop, $match);
                $propName = $match[1];
                $propValue = $match[2];
                $propValue = str_replace('\/', '/', $propValue);
                $callback = fn ($x) => chr(hexdec($x[1]));
                $propValue = preg_replace_callback('#\\\x([0-9A-Fa-f]{2})#', $callback, $propValue);
                $temp = [
                    'type' => self::translateDNProp($propName),
                    'value' => new Element($propValue)
                ];
                self::mapInDNs($temp);
                if (!$temp['value'] instanceof Element) {
                    $propValue = $temp['value'];
                }
                self::addDNPropsInternal($dn, $propName, $propValue);
            }
            return;
        }

        // the following is the format that OpenSSL's CLI implementation (3.0+) outputs
        $doubleQuoteStringPattern = '#"(?:(?:[^"\\\]|\\\.)*)"#';
        preg_match_all($doubleQuoteStringPattern, $props, $matches);
        $props = preg_replace($doubleQuoteStringPattern, "\n", $props);
        $props = explode(',', $props);
        foreach ($props as &$prop) {
            $prop = ltrim($prop);
            if (str_contains($prop, "\n")) {
                $match = array_shift($matches[0]);
                $prop = str_replace("\n", $match, $prop);
            }
        }
        unset($prop);

        foreach ($props as $prop) {
            if (!preg_match('#(^[a-zA-Z0-9\.]+)[ ]?=[ ]?(.*)#', $prop, $match)) {
                throw new UnexpectedValueException('DNs should be in either a /C=whatever/O=whatever format or a "C = whatever, O = whatever" format (without the double quotes)');
            }
            $propName = $match[1];
            $propValue = $match[2];

            if (preg_match('/^#(?:[0-9A-Fa-f][0-9A-Fa-f])+$/', $propValue)) {
                $temp = [
                    'type' => self::translateDNProp($propName),
                    'value' => new Element(pack('H*', substr($propValue, 1)))
                ];
                self::mapInDNs($temp);
                self::addDNPropsInternal($dn, $propName, $temp['value']);
                continue;
            }

            // remove double quotes if present
            if ($propValue[0] == '"') {
                $propValue = substr($propValue, 1, -1);
            }
            // restore non-printable ascii
            $callback = fn ($x) => chr(hexdec($x[1]));
            $propValue = preg_replace_callback('#\\\([0-9A-Fa-f]{2})#', $callback, $propValue);
            // replace \" with " and \\ with \
            $propValue = str_replace(['\"', '\\\\'], ['"', '\\'], $propValue);
            self::addDNPropsInternal($dn, $propName, $propValue);
        }
    }

    // this should prob be moved to an interface
    private static function formatDN(array|Choice $dn, int $format): array|string
    {
        switch ($format) {
            case self::DN_ARRAY:
                self::mapOutDNsInner($dn['rdnSequence']);
                $dn = ASN1::encodeDER($dn, Maps\Name::MAP);
                $dn = ASN1::decodeBER($dn);
                $rules = [];
                $rules['rdnSequence']['*']['*'] = self::mapInDNs(...);
                $dn = ASN1::map($dn, Maps\Name::MAP, $rules);
                return $dn->toArray();
            case self::DN_ASN1:
                self::mapOutDNsInner($dn['rdnSequence']);
                return ASN1::encodeDER($dn, Maps\Name::MAP);
            case self::DN_CANON:
                // No SEQUENCE around RDNs and all string values normalized as
                // trimmed lowercase UTF-8 with all spacing as one blank.
                // constructed RDNs (save for id-at-postalAddress) will not be canonicalized
                $result = '';
                foreach ($dn['rdnSequence'] as $rdn) {
                    foreach ($rdn as $i => $attr) {
                        $attr = &$rdn[$i];
                        if ($attr['value'] instanceof BaseString && $attr['value']->isConvertable()) {
                            try {
                                $attr['value'] = $attr['value']->toUTF8String();
                                $attr['value']->value = strtolower(preg_replace('/\s+/', ' ', $attr['value']->value));
                            } catch (CharacterConversionException) {
                            }
                        } elseif (is_array($attr['value']) || $attr['value'] instanceof Constructed) {
                            if ($attr['type'] == 'id-at-postalAddress') {
                                foreach ($attr['value'] as $key => $val) {
                                    $val = &$attr['value'][$key];
                                    if ($val instanceof Choice && !isset($val['utf8String']) && $val->value instanceof BaseString && $val->value->isConvertable()) {
                                        $val['utf8String'] = $val->value->toUTF8String();
                                    }
                                    if ($val instanceof BaseString && $val->isConvertable()) {
                                        $val = $val->toUTF8String();
                                    }
                                    unset($val);
                                }
                            }
                            self::encodeDNS($attr['type'], $attr['value']);
                        }
                    }
                    $result .= ASN1::encodeDER($rdn, Maps\RelativeDistinguishedName::MAP);
                }
                return $result;
            case self::DN_HASH:
                $dn = self::formatDN($dn, self::DN_CANON);
                $hash = new Hash('sha1');
                $hash = $hash->hash($dn);
                $hash = unpack('Vhash', $hash)['hash'];
                return strtolower(Strings::bin2hex(pack('N', $hash)));
        }

        // Default is to return a string.
        $start = true;
        $output = '';

        $result = [];

        foreach ($dn['rdnSequence'] as $field) {
            $prop = (string) $field[0]['type'];
            $value = $field[0]['value'];

            $desc = match ($prop) {
                'id-at-countryName' => 'C',
                'id-at-stateOrProvinceName' => 'ST',
                'id-at-organizationName' => 'O',
                'id-at-organizationalUnitName' => 'OU',
                'id-at-commonName' => 'CN',
                'id-at-localityName' => 'L',
                'id-at-surname' => 'SN',
                'id-at-givenName' => 'GN',
                'id-at-uniqueIdentifier' => 'x500UniqueIdentifier',
                'jurisdictionLocalityName' => 'jurisdictionL',
                'jurisdictionOfIncorporationStateOrProvinceName' => 'jurisdictionST',
                'jurisdictionOfIncorporationCountryName' => 'jurisdictionC',
                'id-domainComponent' => 'DC',
                default => preg_replace('#.+-([^-]+)$#', '$1', $prop)
            };

            if (!$start) {
                $output .= ', ';
            }
            if (is_array($value)) {
                self::encodeDNS($prop, $value);
                if (is_array($value)) {
                    // maybe we should create an UnableToMapException ?
                    throw new UnexpectedValueException("An unmappable array was encountered for $prop");
                }
            }
            $isConstructed = false;
            if ($value instanceof Constructed || $value instanceof Element) {
                $isConstructed = true;
                $value = '#' . strtoupper(bin2hex("$value"));
            }
            if ($value instanceof BaseString && $value->isConvertable()) {
                try {
                    $value = (string) $value->toUTF8String();
                } catch (CharacterConversionException) {
                }
            }

            $result[$desc] = isset($result[$desc]) ?
                array_merge((array) $result[$desc], [$value]) :
                $value;
            // replace \ with \\ and " with \"
            $value = str_replace(['\\', '"'], ['\\\\', '\"'], "$value");
            // replace non printable ascii
            $callback = fn ($x) => '\\' . strtoupper(bin2hex($x[0]));
            $value = preg_replace_callback('#[^\x20-\x7E]#', $callback, $value);
            switch (true) {
                // if there are spaces on either end or a comma in the middle then encapsulate with double quotes
                case preg_match('#^ +| +$|,#', $value):
                case preg_match('/^#./', $value) && !$isConstructed:
                    $value = '"' . $value . '"';
            }
            $output .= $desc . ' = ' . $value;
            $start = false;
        }

        return $format == self::DN_OPENSSL ? $result : $output;
    }

    /**
     * @return BaseType[]
     */
    private static function retrieveDNProps(array|Choice $dn, string $propName): array
    {
        $propName = self::translateDNProp($propName);
        $dn = $dn['rdnSequence'];
        $result = [];
        for ($i = 0; $i < count($dn); $i++) {
            if ($dn[$i][0]['type'] == $propName) {
                $v = $dn[$i][0]['value'];
                if (is_string($v)) {
                    $v = new UTF8String($v);
                } elseif (is_array($v)) {
                    self::encodeDNS($propName, $v);
                }
                $result[] = $v;
            }
        }

        return $result;
    }
}
<?php

/**
 * Extension Helper for misc ASN1 classes
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2025-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\Common\Traits;

use phpseclib4\Common\Functions\Arrays;
use phpseclib4\File\ASN1;
use phpseclib4\File\ASN1\{Constructed, Element, Maps};
use phpseclib4\File\ASN1\Types\{BaseType, Choice, OID, OctetString};

/**
 * Extension Helper for misc ASN1 classes
 *
 * Used by X509, CSR and CRL
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
trait Extension
{
    private static function getMapping(string $extnId): array|bool|null
    {
        if (isset(self::$extensions[$extnId])) {
            return self::$extensions[$extnId];
        }

        return match ($extnId) {
            'id-ce-keyUsage' => Maps\KeyUsage::MAP,
            'id-ce-basicConstraints' => Maps\BasicConstraints::MAP,
            'id-ce-subjectKeyIdentifier' => Maps\KeyIdentifier::MAP,
            'id-ce-cRLDistributionPoints' => Maps\CRLDistributionPoints::MAP,
            'id-ce-authorityKeyIdentifier' => Maps\AuthorityKeyIdentifier::MAP,
            'id-ce-certificatePolicies' => Maps\CertificatePolicies::MAP,
            'id-ce-extKeyUsage' => Maps\ExtKeyUsageSyntax::MAP,
            // id-ce = certificate extension
            // id-pe = private extension
            'id-pe-authorityInfoAccess' => Maps\AuthorityInfoAccessSyntax::MAP,
            'id-pe-subjectInfoAccess' => Maps\SubjectInfoAccessSyntax::MAP,
            'id-ce-subjectAltName' => Maps\SubjectAltName::MAP,
            'id-ce-privateKeyUsagePeriod' => Maps\PrivateKeyUsagePeriod::MAP,
            'id-ce-issuerAltName' => Maps\IssuerAltName::MAP,
            'id-ce-policyMappings' => Maps\PolicyMappings::MAP,
            'id-ce-nameConstraints' => Maps\NameConstraints::MAP,

            // from https://datatracker.ietf.org/doc/html/rfc3739
            'id-ce-subjectDirectoryAttributes' => Maps\SubjectDirectoryAttributes::MAP,
            'id-pe-qcStatements' => Maps\QCStatements::MAP,

            // from https://datatracker.ietf.org/doc/html/rfc9399
            'id-pe-logotype' => Maps\LogotypeExtn::MAP,

            'netscape-cert-type' => Maps\netscape_cert_type::MAP,
            'netscape-comment' => Maps\netscape_comment::MAP,
            'netscape-ca-policy-url' => Maps\netscape_ca_policy_url::MAP,

            // the following OIDs are unsupported but we don't want them to give notices when calling saveX509().

            'entrustVersInfo' => true,
            // http://support.microsoft.com/kb/287547
            '1.3.6.1.4.1.311.20.2' => true, // szOID_ENROLL_CERTTYPE_EXTENSION
            '1.3.6.1.4.1.311.21.1' => true, // szOID_CERTSRV_CA_VERSION
            // "SET Secure Electronic Transaction Specification"
            // http://www.maithean.com/docs/set_bk3.pdf
            '2.23.42.7.0' => true, // id-set-hashedRootKey
            // "Certificate Transparency"
            // https://tools.ietf.org/html/rfc6962
            '1.3.6.1.4.1.11129.2.4.2' => true,
            // "Qualified Certificate statements"
            // https://tools.ietf.org/html/rfc3739#section-3.2.6
            '1.3.6.1.5.5.7.1.3' => true,

            // CRL extensions
            // OpenSSL seems to allow these in X509 certs so i guess we will as well
            'id-ce-cRLNumber' => Maps\CRLNumber::MAP,
            'id-ce-deltaCRLIndicator' => Maps\CRLNumber::MAP,
            'id-ce-issuingDistributionPoint' => Maps\IssuingDistributionPoint::MAP,
            'id-ce-freshestCRL' => Maps\CRLDistributionPoints::MAP,
            'id-ce-cRLReasons' => Maps\CRLReason::MAP,
            'id-ce-invalidityDate' => Maps\InvalidityDate::MAP,
            'id-ce-certificateIssuer' => Maps\CertificateIssuer::MAP,
            'id-ce-holdInstructionCode' => Maps\HoldInstructionCode::MAP,

            default => null,
        };
    }

    private static function getPathToIPAddress(string $id): string
    {
        return match ($id) {
            'id-ce-subjectAltName' => '*',
            'id-ce-issuerAltName' => '*',
            'id-ce-authorityKeyIdentifier' => 'authorityCertIssuer/*',
            'id-pe-authorityInfoAccess' => '*/accessLocation',
            'id-ce-cRLDistributionPoints' => '*/cRLIssuer/*',
        };
    }

    // this NEEDS to be public so that Constructed.php can call it
    public static function mapInExtensions(Constructed $ext): void
    {
        ASN1::disableCacheInvalidation();

        $extnId = "$ext[extnId]";

        $map = self::getMapping($extnId);

        $rules = [];
        switch ($extnId) {
            case 'id-ce-certificatePolicies':
                $rules['*']['policyQualifiers']['*'] = function (Constructed $el): void {
                    if ("$el[policyQualifierId]" == 'id-qt-unotice' && $el['qualifier'] instanceof ASN1\Element) {
                        $el['qualifier'] = ASN1::map(ASN1::decodeBER($el['qualifier']->getEncoded()), Maps\UserNotice::MAP);
                    }
                    // in theory id-qt-cps maps to Maps\CPSuri::MAP but since id-qt-cps isn't a constructed type it will have
                    // already been decoded to a string by the time it gets back aroud to map() and we don't want to
                    // decode it again
                };
                break;
            // see https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.10
            case 'id-ce-nameConstraints':
                $rules['permittedSubtrees']['*']['base'] = $rules['excludedSubtrees']['*']['base'] = function (Choice $el): void {
                    if (isset($el['iPAddress'])) {
                        $ip = (string) $el['iPAddress'];
                        $size = strlen($ip) >> 1;
                        $mask = substr($ip, $size);
                        $ip = substr($ip, 0, $size);
                        $el['iPAddress'] = [inet_ntop($ip), inet_ntop($mask)];
                    }
                };
                break;
            // see https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.6
            case 'id-ce-subjectAltName':
            case 'id-ce-issuerAltName':
            case 'id-ce-authorityKeyIdentifier':
            case 'id-pe-authorityInfoAccess':
            case 'id-ce-cRLDistributionPoints':
                $path = self::getPathToIPAddress($extnId);
                $rule = &Arrays::subArray($rules, $path, true);
                // pretty much every $extnId requires Choice, save for id-pe-authorityInfoAccess,
                // which needs Constructed
                $rule = function (Choice|Constructed $el): void {
                    if (isset($el['iPAddress'])) {
                        $el['iPAddress'] = inet_ntop("$el[iPAddress]");
                    }
                };
                break;
            case 'id-pe-qcStatements':
                $rules['*'] = function (Constructed $el): void {
                    if ("$el[statementId]" == 'id-etsi-qcs-QcLimitValue' && $el['qualifier'] instanceof ASN1\Element) {
                        $el['statementInfo'] = ASN1::map(ASN1::decodeBER($el['statementInfo']->getEncoded()), Maps\QcEuLimitValue::MAP);
                    }
                };
        }

        if ($map === true) {
            $map = ['type' => ASN1::TYPE_ANY];
        }

        try {
            if (isset($map)) {
                $ext['extnValue'] = ASN1::map(ASN1::decodeBER((string) $ext['extnValue']), $map, $rules);
                if ($ext['extnValue'] instanceof Constructed) {
                    $ext['extnValue']->parent = $ext;
                    $ext['extnValue']->depth = $ext->depth + 1;
                    $ext['extnValue']->key = 'extnValue';
                }
            } else {
                $temp = ASN1::decodeBER((string) $ext['extnValue']);
                $ext['extnValue'] = is_array($temp) && $temp['content'] instanceof Constructed ?
                    new Element($ext['extnValue']->value) :
                    $temp;
            }
        } catch (\Exception $e) {
            $ext['extnValue'] = new Element($ext['extnValue']->value);
        }

        ASN1::enableCacheInvalidation();
    }

    private static function mapOutExtensionsHelper(array|Constructed|Element &$extensions): void
    {
        if ($extensions instanceof Element) {
            return;
        }
        $keys = is_array($extensions) ? array_keys($extensions) : $extensions->keys();
        foreach ($keys as $i) {
            switch (true) {
                case $extensions[$i] instanceof Element:
                case $extensions[$i]['extnValue'] instanceof Element:
                case $extensions[$i] instanceof Constructed && $extensions[$i]->hasEncoded():
                    continue 2;
            }

            $unparsedTest = is_array($extensions[$i]['extnValue']) && isset($extensions[$i]['extnValue']['content']);
            $unparsedTest = $unparsedTest && $extensions[$i]['extnValue']['content'] instanceof Constructed;
            $unparsedTest = $unparsedTest && !$extensions[$i]['extnValue']['content']->hasMapping();
            if ($unparsedTest) {
                $wrapping = chr(ASN1::TYPE_OCTET_STRING) . ASN1::encodeLength($extensions[$i]['extnValue']['content']->getEncodedLength());
                $extensions[$i]['extnValue']['content']->setWrapping($wrapping);
                continue;
            }

            $id = (string) $extensions[$i]['extnId'];
            $value = &$extensions[$i]['extnValue'];
            switch ($id) {
                case 'id-ce-subjectAltName':
                case 'id-ce-issuerAltName':
                case 'id-ce-authorityKeyIdentifier':
                case 'id-pe-authorityInfoAccess':
                case 'id-ce-cRLDistributionPoints':
                    $oldValue = $value instanceof Constructed ? $value->toArray() : $value;
                    $path = self::getPathToIPAddress($id);
                    Arrays::subArrayMapWithWildcards($value, $path, function (Choice|Element|array $val): Choice|Element|array {
                        if ($val instanceof Element || !isset($val['iPAddress'])) {
                            return $val;
                        }
                        return new Choice('iPAddress', new OctetString(inet_pton("$val[iPAddress]")));
                    });
                    break;
                case 'id-ce-nameConstraints':
                    $oldValue = $value instanceof Constructed ? $value->toArray() : $value;
                    $paths = ['permittedSubtrees/*/base', 'excludedSubtrees/*/base'];
                    foreach ($paths as $path) {
                        Arrays::subArrayMapWithWildcards($value, $path, function (Choice|Element|array $val): Choice|Element|array {
                            if ($val instanceof Element || !isset($val['iPAddress'])) {
                                return $val;
                            }
                            return new Choice('iPAddress', new OctetString(inet_pton($val['iPAddress'][0]) . inet_pton($val['iPAddress'][1])));
                        });
                    }
                    break;
                case 'id-ce-certificatePolicies':
                    $oldValue = $value instanceof Constructed ? $value->toArray() : $value;
                    $path = '*/policyQualifiers/*';
                    Arrays::subArrayMapWithWildcards($value, $path, function (Constructed|Element|array $val): Constructed|Element|array {
                        if ($val instanceof Element || "$val[policyQualifierId]" != 'id-qt-unotice') {
                            return $val;
                        }
                        if ($val instanceof BaseType) {
                            ASN1::encodeDER($val['qualifier'], Maps\UserNotice::MAP);
                        } else {
                            $temp = ASN1::encodeDER($val['qualifier'], Maps\UserNotice::MAP);
                            $val['qualifier'] = ASN1::map(ASN1::decodeBER($temp), Maps\UserNotice::MAP);
                        }
                        $val['qualifier']->enableForcedCache();
                        return $val;
                    });
                    break;
                case 'id-pe-qcStatements':
                    $oldValue = $value instanceof Constructed ? $value->toArray() : $value;
                    $path = '*';
                    /** @psalm-suppress InvalidReturnType */
                    Arrays::subArrayMapWithWildcards($value, $path, function (Choice|Element|array $val): Constructed|Element|array {
                        if ($val instanceof Element || "$val[statementId]" != 'id-etsi-qcs-QcLimitValue') {
                            /** @psalm-suppress InvalidReturnStatement */
                            return $val;
                        }
                        if ($val instanceof BaseType) {
                            ASN1::encodeDER($val['statementInfo'], Maps\QcEuLimitValue::MAP);
                        } else {
                            $temp = ASN1::encodeDER($val['statementInfo'], Maps\QcEuLimitValue::MAP);
                            $val['statementInfo'] = ASN1::map(ASN1::decodeBER($temp), Maps\QcEuLimitValue::MAP);
                        }
                        $val['statementInfo']->enableForcedCache();
                        /** @psalm-suppress InvalidReturnStatement */
                        return $val;
                    });
            }

            /* [extnValue] contains the DER encoding of an ASN.1 value
               corresponding to the extension type identified by extnID */
            $map = self::getMapping("$id");
            if ($map === true) {
                $map = ['type' => ASN1::TYPE_ANY];
            }
            if (!isset($map)) {
                //user_error($id . ' is not a currently supported extension');
                unset($extensions[$i]);
                continue;
            } else {
                if ($value instanceof BaseType) {
                    if ($value instanceof Constructed) {
                        $value->invalidateCache();
                    }
                    ASN1::encodeDER($value, $map);
                } else {
                    $temp = ASN1::encodeDER($value, $map);
                    $value = ASN1::map(ASN1::decodeBER($temp), $map);
                }
            }

            if (isset($oldValue) && $value instanceof Constructed) {
                $value->decoded = $oldValue;
            }

            $value->setWrapping(chr(ASN1::TYPE_OCTET_STRING) . ASN1::encodeLength($value->getEncodedLength()));
            $value->enableForcedCache();
        }

        if ($extensions instanceof Constructed) {
            if (count($extensions) - 1 != $extensions->lastKey()) {
                $extensions->rekey();
            }
        } else {
            if (count($extensions) - 1 != array_key_last($extensions)) {
                $extensions = array_values($extensions);
            }
        }
    }

    private static function getExtensionCriticalValue(string $name): bool
    {
        return match ($name) {
            'id-ce-keyUsage' => true,
            'id-ce-nameConstraints' => true,
            'id-ce-policyConstraints' => true,
            'id-ce-inhibitAnyPolicy' => true,

            'id-ce-authorityKeyIdentifier' => false,
            'id-ce-subjectKeyIdentifier' => false,
            'id-ce-policyMappings' => false,
            'id-ce-issuerAltName' => false,
            'id-ce-subjectDirectoryAttributes' => false,
            // "This extension MUST NOT be marked critical" - RFC 9399, section 4.1
            'id-pe-logotype' => false,
            'id-ce-cRLDistributionPoints' => false,
            'id-ce-freshestCRL' => false,
            'id-pe-authorityInfoAccess' => false,
            'id-pe-subjectInfoAccess' => false,

            'id-ce-cRLNumber' => false,
            'id-ce-cRLReasons' => false,
            'id-ce-invalidityDate' => false,
            'id-ce-certificateIssuer' => true,

            default => false
        };
    }

    private static function extensionMatch(string $search, OID|string $candidate): bool
    {
        if (is_string($candidate)) {
            $candidate = new OID($candidate);
        }
        if ($candidate->value == $search) {
            return true;
        }
        if (isset($candidate->name) && $candidate->name == $search) {
            return true;
        }
        return false;
    }
}
<?php

/**
 * Pure-PHP CMS / PasswordRecipient Parser
 *
 * PHP version 8
 *
 * Encode and decode CMS / EnvelopedData / PasswordRecipient files.
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\Common\Traits;

use phpseclib4\Crypt\{AES, TripleDES};
use phpseclib4\Exception\{InvalidStateException, LengthException, UnexpectedValueException};
use phpseclib4\File\ASN1;
use phpseclib4\File\CMS\EncryptedData;

trait KeyDerivation
{
    use \phpseclib4\Crypt\Common\Traits\ASN1AlgorithmIdentifier;

    // used by both KEKRecipient and KeyAgreeRecipient
    protected static function unwrapAES(#[\SensitiveParameter] string $key, #[\SensitiveParameter] string $iv, string $ciphertext): string
    {
        $c = str_split($ciphertext, 8);
        $n = count($c) - 1;

        $aes = new AES('ecb');
        $aes->setKey($key);
        $aes->disablePadding();

        // 1) Initialize variables.
        $r = $c;
        $a = array_shift($r);

        // 2) Compute intermediate values.
        for ($j = 5; $j >= 0; $j--) {
            for ($i = $n - 1; $i >= 0; $i--) {
                $t = pack('J', $n * $j + $i + 1);
                $b = $aes->decrypt(($a ^ $t) . $r[$i]);
                $a = substr($b, 0, 8);
                $r[$i] = substr($b, 8);
            }
        }

        if ($a != $iv) {
            throw new UnexpectedValueException('Error with unwrapping');
        }
        return implode('', $r);
    }

    // used by both KEKRecipient and KeyAgreeRecipient
    // from https://www.rfc-editor.org/rfc/rfc3217#section-3.2
    protected static function unwrap3DES(#[\SensitiveParameter] string $kek, #[\SensitiveParameter] string $wrapped): string
    {
        if (strlen($wrapped) != 40) {
            throw new LengthException('Wrapped key should be 40 octets in length');
        }
        $cipher = new TripleDES('cbc');
        $cipher->disablePadding();
        $cipher->setKey($kek);
        $cipher->setIV("\x4a\xdd\xa2\x2c\x79\xe8\x21\x05");
        $temp3 = $cipher->decrypt($wrapped);
        $temp2 = strrev($temp3);
        $iv = substr($temp2, 0, 8);
        $temp1 = substr($temp2, 8);
        //$cipher->setKey($kek);
        $cipher->setIV($iv);
        $cekicv = $cipher->decrypt($temp1);
        $cek = substr($cekicv, 0, 24);
        $icv = substr($cekicv, -8);
        if ($icv != substr(sha1($cek, true), 0, 8)) {
            throw new UnexpectedValueException('Checksum validation failed');
        }
        return $cek;
    }

    /** @psalm-suppress UndefinedPropertyFetch, InvalidPropertyFetch */
    public function decrypt(): string
    {
        if ($this instanceof EncryptedData) {
            $cek = &$this->cek;
            $cms = &$this;
        } else {
            $cek = &$this->cms->cek;
            $cms = &$this->cms;
        }
        if (!isset($cek)) {
            throw new InvalidStateException('Content encryption key not set');
        }

        $cea = ASN1::decodeBER((string) $cms['content']['encryptedContentInfo']['contentEncryptionAlgorithm']);
        $cea = ASN1::map($cea, ASN1\Maps\AlgorithmIdentifier::MAP);
        $contentCipher = self::getPBES2EncryptionObject((string) $cea['algorithm']);
        $contentCipher->setKey($cek);
        $contentCipher->setIV((string) $cea['parameters']);

        return $contentCipher->decrypt((string) $cms['content']['encryptedContentInfo']['encryptedContent']);
    }
}
<?php

/**
 * Common methods for signature algorithm identification
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2025-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\Common\Traits;

use phpseclib4\Crypt\Common\{PrivateKey, PublicKey};
use phpseclib4\Crypt\{DSA, EC, RSA};
use phpseclib4\Crypt\RSA\Formats\Keys\PSS;
use phpseclib4\Exception\UnsupportedAlgorithmException;
use phpseclib4\File\ASN1\Types\{BitString, ExplicitNull, OID};

/**
 * Common methods for signature algorithm identification
 *
 * Used by X509, CSR and CRL
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
trait ASN1Signature
{
    /**
     * Identify signature algorithm from private key
     *
     * @throws UnsupportedAlgorithmException if the algorithm is unsupported
     */
    private static function identifySignatureAlgorithmHelper(PrivateKey $key): array
    {
        $key = $key->getPublicKey();
        $hash = (string) $key->getHash();
        if ($key instanceof RSA) {
            if ($key->getPadding() & RSA::SIGNATURE_PSS) {
                $r = PSS::load($key->toString('PSS'));
                return [
                    'algorithm' => 'id-RSASSA-PSS',
                    'parameters' => PSS::savePSSParams($r)
                ];
            }
            switch ($hash) {
                case 'sha512/224':
                case 'sha512/256':
                    $hash = str_replace('/', '-', $hash);
                    // no break
                case 'md2':
                case 'md5':
                case 'sha1':
                case 'sha224':
                case 'sha256':
                case 'sha384':
                case 'sha512':
                    return [
                        'algorithm' => $hash . 'WithRSAEncryption',
                        'parameters' => new ExplicitNull(),
                    ];
            }
            throw new UnsupportedAlgorithmException('The only supported hash algorithms for RSA are: md2, md5, sha1, sha224, sha256, sha384, sha512');
        }

        if ($key instanceof DSA) {
            switch ($hash) {
                case 'sha1':
                case 'sha224':
                case 'sha256':
                    return ['algorithm' => 'id-dsa-with-' . $hash];
            }
            throw new UnsupportedAlgorithmException('The only supported hash algorithms for DSA are: sha1, sha224, sha256');
        }

        if ($key instanceof EC) {
            switch ($key->getCurve()) {
                case 'Ed25519':
                case 'Ed448':
                    return ['algorithm' => 'id-' . $key->getCurve()];
            }
            switch ($hash) {
                case 'sha1':
                case 'sha224':
                case 'sha256':
                case 'sha384':
                case 'sha512':
                    return ['algorithm' => 'ecdsa-with-' . strtoupper($hash)];
            }
            throw new UnsupportedAlgorithmException('The only supported hash algorithms for EC are: sha1, sha224, sha256, sha384, sha512');
        }

        throw new UnsupportedAlgorithmException('The only supported public key classes are: RSA, DSA, EC');
    }

    private static function validateSignatureHelper(PublicKey $key, OID|string $signatureAlgorithm, BitString $signature, string $signatureSubject)
    {
        $signatureAlgorithm = (string) $signatureAlgorithm;
        if ($key instanceof RSA) {
            switch ($signatureAlgorithm) {
                case 'id-RSASSA-PSS':
                    $key = $key->withPadding(RSA::SIGNATURE_PSS);
                    break;
                case 'md2WithRSAEncryption':
                case 'md5WithRSAEncryption':
                case 'sha1WithRSAEncryption':
                case 'sha224WithRSAEncryption':
                case 'sha256WithRSAEncryption':
                case 'sha384WithRSAEncryption':
                case 'sha512WithRSAEncryption':
                case 'sha512-224WithRSAEncryption':
                case 'sha512-256WithRSAEncryption':
                    $hash = preg_replace('#WithRSAEncryption$#', '', $signatureAlgorithm);
                    $hash = str_replace('-', '/', $hash);
                    $key = $key
                        ->withHash($hash)
                        ->withPadding(RSA::SIGNATURE_PKCS1);
                    break;
                default:
                    throw new UnsupportedAlgorithmException('Signature algorithm unsupported');
            }
        }

        if ($key instanceof DSA) {
            switch ($signatureAlgorithm) {
                case 'id-dsa-with-sha1':
                case 'id-dsa-with-sha224':
                case 'id-dsa-with-sha256':
                    $key = $key
                        ->withHash(preg_replace('#^id-dsa-with-#', '', strtolower($signatureAlgorithm)));
                    break;
                default:
                    throw new UnsupportedAlgorithmException('Signature algorithm unsupported');
            }
        }

        if ($key instanceof EC) {
            switch ($signatureAlgorithm) {
                case 'id-Ed25519':
                case 'id-Ed448':
                    break;
                case 'ecdsa-with-SHA1':
                case 'ecdsa-with-SHA224':
                case 'ecdsa-with-SHA256':
                case 'ecdsa-with-SHA384':
                case 'ecdsa-with-SHA512':
                    $key = $key
                        ->withHash(preg_replace('#^ecdsa-with-#', '', strtolower($signatureAlgorithm)));
                    break;
                default:
                    throw new UnsupportedAlgorithmException('Signature algorithm unsupported');
            }
        }

        return $key->verify($signatureSubject, substr("$signature", 1));
    }
}
<?php

/**
 * Signable interface
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2025-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\Common;

use phpseclib4\Crypt\Common\PrivateKey;
use phpseclib4\File\X509;

/**
 * Signable interface
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
interface Signable
{
    public function getSignableSection(): string;
    public function setSignature(string $signature): void;
    public function identifySignatureAlgorithm(PrivateKey $key): void;
    public function copySigningX509Attributes(X509 $x509): void;
}
<?php

/**
 * Pure-PHP ASN.1 Parser
 *
 * PHP version 8.1+
 *
 * ASN.1 provides the semantics for data encoded using various schemes.  The most commonly
 * utilized scheme is DER or the "Distinguished Encoding Rules".  PEM's are base64 encoded
 * DER blobs.
 *
 * \phpseclib4\File\ASN1 decodes and encodes DER formatted messages and places them in a semantic context.
 *
 * Uses the 1988 ASN.1 syntax.
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2012-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File;

use phpseclib4\Common\Functions\Strings;
use phpseclib4\Crypt\Common\PublicKey;
use phpseclib4\Exception\{
    BadConfigurationException,
    EOCException,
    EncodedDataUnavailableException,
    ResourceLimitException,
    UnexpectedValueException,
    UnsupportedValueException
};
use phpseclib4\File\ASN1\{Constructed, Element, MalformedData};
use phpseclib4\File\ASN1\Types\{
    BMPString,
    BaseType,
    BitString,
    Boolean,
    Choice,
    ExplicitNull,
    GeneralString,
    GeneralizedTime,
    GraphicString,
    IA5String,
    Integer,
    NumericString,
    OID,
    OctetString,
    PrintableString,
    TeletexString,
    UTCTime,
    UTF8String,
    VideotexString,
    VisibleString
};
use phpseclib4\File\CMS\EnvelopedData\KeyAgreeRecipient\EncryptedKey;
use phpseclib4\File\CMS\EnvelopedData\Recipient;
use phpseclib4\File\CMS\SignedData\Signer;
use phpseclib4\Math\BigInteger;

/**
 * Pure-PHP ASN.1 Parser
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class ASN1
{
    /**
     * Return internal array representation
     *
     * @see \phpseclib4\File\X509::getDN()
     */
    public const DN_ARRAY = 0;
    /**
     * Return string
     *
     * @see \phpseclib4\File\X509::getDN()
     */
    public const DN_STRING = 1;
    /**
     * Return ASN.1 name string
     *
     * @see \phpseclib4\File\X509::getDN()
     */
    public const DN_ASN1 = 2;
    /**
     * Return OpenSSL compatible array
     *
     * @see \phpseclib4\File\X509::getDN()
     */
    public const DN_OPENSSL = 3;
    /**
     * Return canonical ASN.1 RDNs string
     *
     * @see \phpseclib4\File\X509::getDN()
     */
    public const DN_CANON = 4;
    /**
     * Return name hash for file indexing
     *
     * @see \phpseclib4\File\X509::getDN()
     */
    public const DN_HASH = 5;

    // Tag Classes
    // http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=12
    public const CLASS_UNIVERSAL        = 0;
    public const CLASS_APPLICATION      = 1;
    public const CLASS_CONTEXT_SPECIFIC = 2;
    public const CLASS_PRIVATE          = 3;

    // Tag Classes
    // http://www.obj-sys.com/asn1tutorial/node124.html
    public const TYPE_BOOLEAN           = 1;
    public const TYPE_INTEGER           = 2;
    public const TYPE_BIT_STRING        = 3;
    public const TYPE_OCTET_STRING      = 4;
    public const TYPE_NULL              = 5;
    public const TYPE_OBJECT_IDENTIFIER = 6;
    //const TYPE_OBJECT_DESCRIPTOR = 7;
    //const TYPE_INSTANCE_OF       = 8; // EXTERNAL
    public const TYPE_REAL              = 9;
    public const TYPE_ENUMERATED        = 10;
    //const TYPE_EMBEDDED          = 11;
    public const TYPE_UTF8_STRING       = 12;
    //const TYPE_RELATIVE_OID      = 13;
    public const TYPE_SEQUENCE          = 16; // SEQUENCE OF
    public const TYPE_SET               = 17; // SET OF

    // More Tag Classes
    // http://www.obj-sys.com/asn1tutorial/node10.html
    public const TYPE_NUMERIC_STRING   = 18;
    public const TYPE_PRINTABLE_STRING = 19;
    public const TYPE_TELETEX_STRING   = 20; // T61String
    public const TYPE_VIDEOTEX_STRING  = 21;
    public const TYPE_IA5_STRING       = 22;
    public const TYPE_UTC_TIME         = 23;
    public const TYPE_GENERALIZED_TIME = 24;
    public const TYPE_GRAPHIC_STRING   = 25;
    public const TYPE_VISIBLE_STRING   = 26; // ISO646String
    public const TYPE_GENERAL_STRING   = 27;
    public const TYPE_UNIVERSAL_STRING = 28;
    //const TYPE_CHARACTER_STRING = 29;
    public const TYPE_BMP_STRING       = 30;

    // Tag Aliases
    // These tags are kinda place holders for other tags.
    public const TYPE_CHOICE = -1;
    public const TYPE_ANY    = -2;

    /**
     * Save as PEM
     *
     * ie. a base64-encoded PEM with a header and a footer
     */
    public const FORMAT_PEM = 0;
    /**
     * Save as DER
     */
    public const FORMAT_DER = 1;
    /**
     * Auto-detect the format
     *
     * Used only by the load*() functions
     */
    public const FORMAT_AUTO_DETECT = 3;

    public const EXCEPTIONS_EVERY_TIME = 0;
    public const BLOB_ON_BAD_ELEMENT = 1;
    public const BLOB_ON_INCOMPLETE_ELEMENT = 2;

    /**
     * ASN.1 object identifiers
     *
     * @var array
     * @link http://en.wikipedia.org/wiki/Object_identifier
     */
    private static array $oids = [];

    /**
     * ASN.1 object identifier reverse mapping
     */
    private static array $reverseOIDs = [];

    /**
     * Have all OIDs been loaded?
     */
    private static bool $allOIDsLoaded = false;

    /**
     * Current Location of most recent ASN.1 encode process
     *
     * Useful for debug purposes
     *
     * @see self::encode_der()
     */
    private static array $location;

    /**
     * Use Encoded Cache Flag
     *
     * Only Constructed objects or ASN1\Types\* objects can have an encoded cache
     *
     * @see self::encodeDER()
     */
    private static bool $useEncodedCache = true;

    private static ?bool $use64BitOIDHandling = null;

    /**
     * Cache Invalidation Flag
     *
     * Sometimes we want setting an offset in a Constructed object should invalidate the cache,
     * sometimes it shouldn't
     */
    private static bool $invalidateCache = true;

    private static bool $blobsOnBadDecodes = false;

    /*
     * Recursion Depth Limit
     *
     * OpenSSL uses 128. the OpenSSL error reads "BAD RECURSION DEPTH"
     */
    private static int $recursionDepth = 128;

    /**
     * Decode the tag
     *
     * Returns the class, whether or not the tag is primitive or constructed and the tag number
     */
    public static function decodeTag(string $encoded, int &$encoded_pos = 0): array
    {
        if (!isset($encoded[$encoded_pos])) {
            throw new UnexpectedValueException('Not enough bytes to decode tag 1');
        }

        $type = ord($encoded[$encoded_pos++]);
        $offset = 1;

        $constructed = ($type >> 5) & 1;

        $tag = $type & 0x1F;
        if ($tag == 0x1F) {
            $tag = 0;
            // process septets (since the eighth bit is ignored, it's not an octet)
            do {
                if (!isset($encoded[$encoded_pos])) {
                    throw new UnexpectedValueException('Not enough bytes to decode tag 2');
                }
                $temp = ord($encoded[$encoded_pos++]);
                $offset++;
                $loop = $temp >> 7;
                $tag <<= 7;
                $temp &= 0x7F;
                if ($offset == 2 && $temp == 0) {
                    throw new UnexpectedValueException('Bits 7 to 1 of the first subsequent octet shall not be all zero');
                }
                $tag |= $temp;
            } while ($loop);
        }

        $class = ($type >> 6) & 3;

        return compact('constructed', 'tag', 'class');
    }

    /**
     * Decode the length
     *
     * Returns null if the length is of the indefinite form
     */
    public static function decodeLength(string $encoded, int &$encoded_pos = 0): ?int
    {
        // Length, as discussed in paragraph 8.1.3 of X.690-0207.pdf#page=13
        if (!isset($encoded[$encoded_pos])) {
            throw new UnexpectedValueException('Not enough bytes to decode length');
        }
        $length = ord($encoded[$encoded_pos++]);
        if ($length == 0x80) { // indefinite length
            // "[A sender shall] use the indefinite form (see 8.1.3.6) if the encoding is constructed and is not all
            //  immediately available." -- paragraph 8.1.3.2.c
            return null;
        }

        if ($length & 0x80) { // definite length, long form
            // technically, the long form of the length can be represented by up to 126 octets (bytes), but we'll only
            // support it up to four.
            $length &= 0x7F;
            $temp = substr($encoded, $encoded_pos, $length);
            $encoded_pos += $length;
            [, $length] = unpack('N', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4));
        }

        return $length;
    }

    /**
     * Parse BER-encoding (Helper function)
     *
     * Sometimes we want to get the BER encoding of a particular tag.  $start lets us do that without having to reencode.
     * $encoded is passed by reference for the recursive calls done for self::TYPE_BIT_STRING and
     * self::TYPE_OCTET_STRING. In those cases, the indefinite length is used.
     *
     * $start is the position in the _original_ $encoded string
     * $encoded_pos is the position in the _current_ $encoded string (which may have been passed through substr)
     * in what's returned, length is not set if it's indefinite length. when it is set it does not include the header
     * length.
     *
     * the array that's returned always has the following keys:
     *
     * - start
     * - headerlength
     * - type
     * - content: may be a BaseType object or an Element object
     *
     * keys that *may* be present are:
     *
     * - length: if the definitive length is used. if this is not present you know the indefinite length is being used
     * - constant: if a constant is being used
     */
    public static function decodeBER(string $encoded, int $start = 0, int $encoded_pos = 0): array
    {
        $current = ['start' => $start];

        $old_encoded_pos = $encoded_pos;
        [
            'constructed' => $constructed,
            'tag' => $tag,
            'class' => $class
        ] = self::decodeTag($encoded, $encoded_pos);
        $length = self::decodeLength($encoded, $encoded_pos);
        $current['headerlength'] = $encoded_pos - $old_encoded_pos;
        if (isset($length)) {
            $current['length'] = $length;
        }

        $start += $current['headerlength'];

        if ($length > (strlen($encoded) - $encoded_pos)) {
            throw new UnexpectedValueException("Length ($length) exceeds number of available bytes (" . (strlen($encoded) - $encoded_pos) . ')');
        }
        $content = substr($encoded, $encoded_pos, $length);
        $headercontent = substr($encoded, $old_encoded_pos, $current['headerlength']);

        // at this point $length can be overwritten. it's only accurate for definite length things as is

        /* Class is UNIVERSAL, APPLICATION, PRIVATE, or CONTEXT-SPECIFIC. The UNIVERSAL class is restricted to the ASN.1
           built-in types. It defines an application-independent data type that must be distinguishable from all other
           data types. The other three classes are user defined. The APPLICATION class distinguishes data types that
           have a wide, scattered use within a particular presentation context. PRIVATE distinguishes data types within
           a particular organization or country. CONTEXT-SPECIFIC distinguishes members of a sequence or set, the
           alternatives of a CHOICE, or universally tagged set members. Only the class number appears in braces for this
           data type; the term CONTEXT-SPECIFIC does not appear.

             -- http://www.obj-sys.com/asn1tutorial/node12.html */
        switch ($class) {
            case self::CLASS_APPLICATION:
            case self::CLASS_PRIVATE:
            case self::CLASS_CONTEXT_SPECIFIC:
                return [
                    'start'    => $start,
                    'type'     => $class,
                    'constant' => $tag,
                    'content'  => $constructed ?
                        new Constructed(
                            $content,
                            $class,
                            $tag,
                            $start,
                            0,
                            $current['headerlength'],
                            $headercontent,
                        ) :
                        $content,
                    'length'   => $length,
                ] + $current;
        }

        $current += ['type' => $tag];

        if ($constructed) {
            switch ($tag) {
                // see the following URLs for why GENERALIZED_TIME and UTC_TIME are allowed to be constructed:
                // https://github.com/phpseclib/phpseclib/commit/511f55de3d1d504e4686f9d558a3c10709b413f8
                // https://github.com/phpseclib/phpseclib/issues/1388
                case ASN1::TYPE_GENERALIZED_TIME:
                case ASN1::TYPE_UTC_TIME:
                case ASN1::TYPE_BIT_STRING:
                case ASN1::TYPE_OCTET_STRING:
                case ASN1::TYPE_SEQUENCE:
                case ASN1::TYPE_SET:
                    break;
                default:
                    if (!self::$blobsOnBadDecodes) {
                        throw new UnexpectedValueException("Tag #$tag should not have the constructed bit set");
                    }
                    return $current + ['content' => new MalformedData($headercontent . $content)];
            }
            return $current + ['content' => new Constructed(
                $content,
                $class,
                $tag,
                $start,
                $encoded_pos,
                $current['headerlength'],
                $headercontent
            )];
        }

        // decode UNIVERSAL tags
        switch ($tag) {
            case self::TYPE_BOOLEAN:
                if (strlen($content) != 1) {
                    // paragraph 8.2.1
                    if (!self::$blobsOnBadDecodes) {
                        // paragraph 8.8.2
                        throw new UnexpectedValueException('The contents octets shall consist of a single octet for bit strings');
                    }
                    $current['content'] = new Element($headercontent . $content);
                    break;
                }
                $current['content'] = new Boolean((bool) ord($content[0]));
                break;
            case self::TYPE_INTEGER:
            case self::TYPE_ENUMERATED:
                $current['content'] = new Integer($content, -256);
                break;
            case self::TYPE_REAL: // not currently supported
                //throw new UnsupportedValueException('Real numbers are not supported');
                $current['content'] = new Element($headercontent . $content);
                break;
            case self::TYPE_BIT_STRING:
                // The initial octet shall encode, as an unsigned binary integer with bit 1 as the least significant bit,
                // the number of unused bits in the final subsequent octet. The number shall be in the range zero to
                // seven.
                $current['content'] = new BitString($content);
                break;
            case self::TYPE_OCTET_STRING:
                $current['content'] = new OctetString($content);
                break;
            case self::TYPE_NULL:
                if (strlen($content)) {
                    if (!self::$blobsOnBadDecodes) {
                        // paragraph 8.8.2
                        throw new UnexpectedValueException('The contents octets shall not contain any octets for nulls');
                    }
                    $current['content'] = new MalformedData($headercontent . $content);
                    break;
                }
                $current['content'] = new ExplicitNull();
                break;
            case self::TYPE_SEQUENCE:
            case self::TYPE_SET:
                if (!self::$blobsOnBadDecodes) {
                    throw new UnexpectedValueException('All SEQUENCE and SET tags should be constructed');
                }
                $current['content'] = new MalformedData($headercontent . $content);
                break;
            case self::TYPE_OBJECT_IDENTIFIER:
                try {
                    if (strlen($content) > 128) {
                        throw new ResourceLimitException('Object Identifier size is limited to 128 bytes');
                    }
                    if (ord($content[-1]) & 0x80) {
                        throw new UnexpectedValueException('OID is malformed');
                    }
                } catch (\Exception $e) {
                    if (!self::$blobsOnBadDecodes) {
                        throw $e;
                    }
                    $current['content'] = new MalformedData($headercontent . $content);
                    break;
                }
                $current['content'] = new OID(new Element($content));
                break;
            /* Each character string type shall be encoded as if it had been declared:
               [UNIVERSAL x] IMPLICIT OCTET STRING

                 -- X.690-0207.pdf#page=23 (paragraph 8.21.3)

               Per that, we're not going to do any validation.  If there are any illegal characters in the string,
               we don't really care */
            case self::TYPE_NUMERIC_STRING:
                $current['content'] = new NumericString($content);
                break;
            case self::TYPE_PRINTABLE_STRING:
                $current['content'] = new PrintableString($content);
                break;
            case self::TYPE_TELETEX_STRING:
                $current['content'] = new TeletexString($content);
                break;
            case self::TYPE_VIDEOTEX_STRING:
                $current['content'] = new VideotexString($content);
                break;
            case self::TYPE_VISIBLE_STRING:
                $current['content'] = new VisibleString($content);
                break;
            case self::TYPE_IA5_STRING:
                $current['content'] = new IA5String($content);
                break;
            case self::TYPE_GRAPHIC_STRING:
                $current['content'] = new GraphicString($content);
                break;
            case self::TYPE_GENERAL_STRING:
                $current['content'] = new GeneralString($content);
                break;
            case self::TYPE_UTF8_STRING:
                $current['content'] = new UTF8String($content);
                break;
            case self::TYPE_BMP_STRING:
                $current['content'] = new BMPString($content);
                break;
            case self::TYPE_UTC_TIME:
            case self::TYPE_GENERALIZED_TIME:
                try {
                    $current['content'] = self::decodeTime($content, $tag);
                } catch (UnexpectedValueException $e) {
                    $current['content'] = new Element($headercontent . $content);
                }
                break;
            default:
                if ($tag === 0 && $length === 0) {
                    throw new EOCException('End-of-content (indefinite form) tag encountered');
                }
                throw new UnexpectedValueException("An unknown tag ($tag) was encountered");
        }

        // ie. length is the length of the full TLV encoding - it's not just the length of the value
        $current += ['length' => $start - $current['start']];
        $current['content']->addMetadata([
            'rawheader' => $headercontent,
            'content' => $content,
        ] + $current);

        //$start += $length;

        return $current;
    }

    /**
     * ASN.1 Map for CHOICE type
     */
    private static function mapChoice(array $decoded, array $mapping, array $rules = []): Choice
    {
        foreach ($mapping['children'] as $key => $option) {
            switch (true) {
                case isset($option['constant']) && isset($decoded['constant']) && $option['constant'] == $decoded['constant']:
                case !isset($option['constant']) && $option['type'] == $decoded['type']:
                    return new Choice($key, self::map($decoded, $option, $rules[$key] ?? []));
            }
        }
        throw new UnexpectedValueException('No valid CHOICEs found');
    }

    /**
     * ASN.1 Map
     *
     * Provides an ASN.1 semantic mapping ($mapping) from a parsed BER-encoding to a human readable format.
     */
    public static function map(array $decoded, array $mapping, array $rules = []): Element|BaseType
    {
        //if (isset($mapping['decoder'])) {
        //    return $mapping['decoder']($decoded['content']);
        //}

        if ($decoded['content'] instanceof OID) {
            "$decoded[content]"; // force the OID to be decoded
        }

        if (isset($mapping['explicit'])) {
            if (!$decoded['content'] instanceof Constructed) {
                throw new UnexpectedValueException('Child is explicit but actual data is not constructed');
            }
            $decoded = ASN1::decodeBER($decoded['content']->getEncodedWithoutHeader());
        }

        if (isset($decoded['content']) && $decoded['content'] instanceof Constructed) {
            switch ($mapping['type']) {
                case self::TYPE_CHOICE:
                    return self::mapChoice($decoded, $mapping, $rules);
                case self::TYPE_ANY:
                    throw new EncodedDataUnavailableException('To construct an Element object the original raw encoding needs to be available and it is not');
            }
            $decoded['content']->linkMapping($mapping, $rules);
            if (isset($mapping['implicit'])) {
                $decoded['content']->replaceTag($mapping['type']);
            }
            return $decoded['content'];
        }

        if ($mapping['type'] == self::TYPE_ANY) {
            if (isset($decoded['constant'])) {
                throw new EncodedDataUnavailableException('To construct an Element object the original raw encoding needs to be available and it is not');
            }
            return $decoded['content'];
        }

        if ($mapping['type'] == self::TYPE_CHOICE) {
            return self::mapChoice($decoded, $mapping, $rules);
        }

        if (isset($mapping['implicit']) && !is_object($decoded['content'])) {
            $temp = chr($mapping['type']) . self::encodeLength(strlen($decoded['content'])) . $decoded['content'];
            $temp = self::decodeBER($temp);
            return isset($mapping['mapping']) ? self::applyMap($temp['content'], $mapping['mapping']) : $temp['content'];
        }

        if ($decoded['type'] == $mapping['type']) {
            return isset($mapping['mapping']) ? self::applyMap($decoded['content'], $mapping['mapping']) : $decoded['content'];
        }

        // if $decoded['type'] and $mapping['type'] are both strings, but different types of strings,
        // let it through
        switch (true) {
            case $decoded['type'] < 18: // self::TYPE_NUMERIC_STRING == 18
            case $decoded['type'] > 30: // self::TYPE_BMP_STRING == 30
            case $mapping['type'] < 18:
            case $mapping['type'] > 30:
                break;
            default:
                return $decoded['content'];
        }

        throw new UnexpectedValueException(
            'Unable to perform mapping - found ' .
            self::convertTypeConstantToString($decoded['type']) .
            ' - expected ' .
            self::convertTypeConstantToString($mapping['type'])
        );
    }

    private static function applyMap(Integer|BitString $content, array $mapping): Integer|BitString
    {
        switch ($content::class) {
            case Integer::class:
                $temp = $content->toString();
                if (strlen($temp) > 1) {
                    throw new UnsupportedValueException('Mapped integers > 255 are not supported');
                }
                $key = (int) $temp;
                if (isset($mapping[$key])) {
                    $content->mappedValue = $mapping[$key];
                }
                break;
            case BitString::class:
                $raw = (string) $content;
                $offset = ord($raw[0]);
                $size = (strlen($raw) - 1) * 8 - $offset;
                /*
                   From X.680-0207.pdf#page=46 (21.7):

                   "When a "NamedBitList" is used in defining a bitstring type ASN.1 encoding rules are free to add (or remove)
                    arbitrarily any trailing 0 bits to (or from) values that are being encoded or decoded. Application designers should
                    therefore ensure that different semantics are not associated with such values which differ only in the number of trailing
                    0 bits."
                */
                $bits = count($mapping) == $size ? [] : array_fill(0, count($mapping) - $size, false);
                for ($i = strlen($raw) - 1; $i > 0; $i--) {
                    $current = ord($raw[$i]);
                    for ($j = $offset; $j < 8; $j++) {
                        $bits[] = (bool) ($current & (1 << $j));
                    }
                    $offset = 0;
                }
                $values = [];
                $map = array_reverse($mapping);
                foreach ($map as $i => $value) {
                    if ($bits[$i]) {
                        $values[] = $value;
                    }
                }
                $content->mappedValue = $values;
        }
        return $content;
    }

    /**
     * Use Encoded Cache
     *
     * The encoded cache is only used when encoding ASN.1 stuff
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public static function useEncodedCache(): void
    {
        self::$useEncodedCache = true;
    }

    /**
     * Ignore Encoded Cache
     *
     * The encoded cache is only used when encoding ASN.1 stuff
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public static function ignoreEncodedCache(): void
    {
        self::$useEncodedCache = false;
    }

    /**
     * Disable Cache Invalidation
     *
     * X.509 extensions are stored as octet strings but phpseclib decodes those based on the extension name.
     * This re-assignment would normally invalidate the cache.
     */
    public static function disableCacheInvalidation(): void
    {
        self::$invalidateCache = false;
    }

    /**
     * Enable Cache Invalidation
     *
     * If you change (for example) the certificate start date the cache'd data should be invalidated for that and for
     * everything up the chain
     */
    public static function enableCacheInvalidation(): void
    {
        self::$invalidateCache = true;
    }

    public static function enableBlobsOnBadDecodes(): void
    {
        self::$blobsOnBadDecodes = true;
    }

    public static function disableBlobsOnBadDecodes(): void
    {
        self::$blobsOnBadDecodes = false;
    }

    public static function isBlobsOnBadDecodesEnabled(): bool
    {
        return self::$blobsOnBadDecodes;
    }

    /**
     * @see File\ASN1\Constructed::offsetSet
     */
    public static function invalidateCache(): bool
    {
        return self::$invalidateCache;
    }

    /**
     * ASN.1 Encode
     *
     * DER-encodes an ASN.1 semantic mapping ($mapping).  Some libraries would probably call this function
     * an ASN.1 compiler.
     *
     * $source can be Element|BaseType|BigInteger|string|array|int|float|bool|null
     * and by array that means any of the non-array types in any combination.
     */
    public static function encodeDER(mixed $source, array $mapping): string
    {
        self::$location = [];
        return self::encode_der($source, $mapping);
    }
    /**
     * ASN.1 Encode (Helper function)
     *
     * $source can be Element|BaseType|BigInteger|string|array|int|float|bool|null
     * and by array that means any of the non-array types in any combination.
     */
    private static function encode_der(mixed $source, array $mapping, string|int|null $idx = null): string
    {
        if ($source instanceof Element) {
            return $source->value;
        }
        if ($source instanceof X509 || $source instanceof CRL || $source instanceof Signer || $source instanceof Recipient || $source instanceof EncryptedKey) {
            return $source->getEncoded();
        }

        if (is_array($source) && isset($source['content']) && $source['content'] instanceof Constructed && !$source['content']->hasMapping()) {
            return $source['content']->hasWrapping() ? $source['content']->getEncodedWithWrapping() : $source['content']->getEncoded();
        }

        if ($source instanceof BaseType && (self::$useEncodedCache || $source->isCacheForced()) && $source->hasEncoded()) {
            return $source->hasWrapping() ? $source->getEncodedWithWrapping() : $source->getEncoded();
        }

        // do not encode (implicitly optional) fields with value set to default
        //if (isset($mapping['default']) && $source === $mapping['default']) {
        //    return '';
        //}
        if (isset($mapping['default'])) {
            switch (true) {
                case $source === $mapping['default']:
                case $mapping['type'] === ASN1::TYPE_BOOLEAN && $source === (new Boolean($mapping['default'])):
                    return '';
                case $mapping['type'] === ASN1::TYPE_INTEGER && $source instanceof Integer:
                    switch (true) {
                        case isset($mapping['mapping']) && $source->mappedValue === $mapping['default']:
                        case !isset($mapping['mapping']) && $source->toString() == $mapping['default']:
                            return '';
                    }
            }
        }

        if (isset($idx)) {
            self::$location[] = $idx;
        }

        $tag = $mapping['type'];
        switch ($tag) {
            case self::TYPE_SET:    // Children order is not important, thus process in sequence.
            case self::TYPE_SEQUENCE:
                if (!is_array($source) && !$source instanceof Constructed) {
                    throw new UnexpectedValueException(implode('/', self::$location) . ' is not an array or an instance of Constructed');
                }
                $tag |= 0x20; // set the constructed bit

                // ignore the min and max
                if (isset($mapping['min']) && isset($mapping['max'])) {
                    $value = [];
                    $child = $mapping['children'];
                    foreach ($source as $i => $content) {
                        $value[] = self::encode_der($content, $child, $i);
                    }
                    /* "The encodings of the component values of a set-of value shall appear in ascending order, the encodings being compared
                        as octet strings with the shorter components being padded at their trailing end with 0-octets.
                        NOTE - The padding octets are for comparison purposes only and do not appear in the encodings."

                       -- sec 11.6 of http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf  */
                    if ($mapping['type'] == self::TYPE_SET) {
                        sort($value);
                    }
                    $value = implode('', $value);
                    break;
                }

                $value = '';
                foreach ($mapping['children'] as $key => $child) {
                    switch (true) {
                        case is_array($source) && !array_key_exists($key, $source):
                        case !is_array($source) && !isset($source[$key]):
                            if (!isset($child['optional'])) {
                                throw new UnexpectedValueException(implode('/', array_merge(self::$location, [$key])) . ' is not present and is not optional');
                            }
                            continue 2;
                    }

                    $temp = self::encode_der($source[$key], $child, $key);

                    // An empty child encoding means it has been optimized out.
                    // Else we should have at least one tag byte.
                    if ($temp === '') {
                        continue;
                    }

                    // if isset($child['constant']) is true then isset($child['optional']) should be true as well
                    if (isset($child['constant'])) {
                        /*
                           From X.680-0207.pdf#page=58 (30.6):

                           "The tagging construction specifies explicit tagging if any of the following holds:
                            ...
                            c) the "Tag Type" alternative is used and the value of "TagDefault" for the module is IMPLICIT TAGS or
                            AUTOMATIC TAGS, but the type defined by "Type" is an untagged choice type, an untagged open type, or
                            an untagged "DummyReference" (see ITU-T Rec. X.683 | ISO/IEC 8824-4, 8.3)."
                         */
                        if (isset($child['explicit']) || $child['type'] == self::TYPE_CHOICE) {
                            if ($child['constant'] <= 30) {
                                $subtag = chr((self::CLASS_CONTEXT_SPECIFIC << 6) | 0x20 | $child['constant']);
                            } else {
                                $constant = $child['constant'];
                                $subtag = '';
                                while ($constant > 0) {
                                    $subtagvalue = $constant & 0x7F;
                                    $subtag = (chr(0x80 | $subtagvalue)) . $subtag;
                                    $constant = $constant >> 7;
                                }
                                $subtag[-1] = $subtag[-1] & chr(0x7F);
                                $subtag = chr((self::CLASS_CONTEXT_SPECIFIC << 6) | 0x20 | 0x1F) . $subtag;
                            }
                            $temp = $subtag . self::encodeLength(strlen($temp)) . $temp;
                        } else {
                            $subtag = chr((self::CLASS_CONTEXT_SPECIFIC << 6) | (ord($temp[0]) & 0x20) | $child['constant']);
                            $temp = $subtag . substr($temp, 1);
                        }
                    }
                    $value .= $temp;
                }

                break;
            case self::TYPE_CHOICE:
                foreach ($mapping['children'] as $key => $child) {
                    if (!isset($source[$key])) {
                        continue;
                    }
                    $temp = self::encode_der($source[$key], $child, $key);

                    // An empty child encoding means it has been optimized out.
                    // Else we should have at least one tag byte.
                    if ($temp === '') {
                        continue;
                    }

                    $tag = ord($temp[0]);

                    // if isset($child['constant']) is true then isset($child['optional']) should be true as well
                    if (isset($child['constant'])) {
                        if (isset($child['explicit']) || $child['type'] == self::TYPE_CHOICE) {
                            $subtag = chr((self::CLASS_CONTEXT_SPECIFIC << 6) | 0x20 | $child['constant']);
                            $temp = $subtag . self::encodeLength(strlen($temp)) . $temp;
                        } else {
                            $subtag = chr((self::CLASS_CONTEXT_SPECIFIC << 6) | (ord($temp[0]) & 0x20) | $child['constant']);
                            $temp = $subtag . substr($temp, 1);
                        }
                    }
                }

                if (!isset($temp)) {
                    $options = implode(',', array_keys($mapping['children']));
                    $actual = is_array($source) ? array_keys($source) : $source->keys();
                    $actual = implode(',', $actual);
                    throw new UnexpectedValueException(implode('/', self::$location) . " appears to contain a key that's not defined in the CHOICE (expected: $options) (actual: $actual)");
                }

                if (isset($idx)) {
                    array_pop(self::$location);
                }

                if ($temp && isset($mapping['cast'])) {
                    $temp[0] = chr(($mapping['class'] << 6) | ($tag & 0x20) | $mapping['cast']);
                }

                return $temp;
            case self::TYPE_INTEGER:
            case self::TYPE_ENUMERATED:
                if (!is_string($source) && !$source instanceof BigInteger && !is_numeric($source)) {
                    $message = implode('/', self::$location) . ' must be a string, a numeric (is_numeric), or an instance of ' .
                        'either phpseclib4\File\ASN1\Types\Integer or a phpseclib4\Math\BigInteger';
                    throw new UnexpectedValueException($message);
                }
                if (!isset($mapping['mapping'])) {
                    if (is_numeric($source)) {
                        $source = new BigInteger($source);
                    }
                    $value = $source->toBytes(true);
                } else {
                    if ($source instanceof Integer && isset($source->mappedValue)) {
                        $source = $source->mappedValue;
                    } elseif ($source instanceof BigInteger) {
                        throw new UnexpectedValueException('No mapped value provided for ' . implode('/', self::$location));
                    }
                    $value = array_search($source, $mapping['mapping']);
                    if ($value === false) {
                        throw new UnexpectedValueException("Unexpected value ($source) encountered for " . implode('/', self::$location) . '; expected any of ' . implode(',', $mapping['mapping']));
                    }
                    $value = new BigInteger($value);
                    $value = $value->toBytes(true);
                }
                if (!strlen($value)) {
                    $value = chr(0);
                }
                break;
            case self::TYPE_UTC_TIME:
            case self::TYPE_GENERALIZED_TIME:
                if (!is_string($source) && !$source instanceof \DateTimeInterface) {
                    $type = is_object($source) ? $source::class : gettype($source);
                    throw new UnexpectedValueException(implode('/', self::$location) . " should be either a string or an instance of DateTimeInterface - $type given");
                }
                if ($tag === self::TYPE_UTC_TIME) {
                    if ($source instanceof GeneralizedTime) {
                        throw new UnexpectedValueException(implode('/', self::$location) . ' has a GeneralizedTime object but is of type UTCTime');
                    }
                } else {
                    if ($source instanceof UTCTime) {
                        throw new UnexpectedValueException(implode('/', self::$location) . ' has a UTCTime object but is of type GeneralizedTime');
                    }
                }
                $format = $tag === self::TYPE_UTC_TIME ? 'y' : 'Y';
                $format .= 'mdHis';
                // if $source does _not_ include timezone information within it then assume that the timezone is GMT
                $date = is_string($source) ? new \DateTime($source, new \DateTimeZone('GMT')) : clone $source;
                // if $source _does_ include timezone information within it then convert the time to GMT
                $date->setTimezone(new \DateTimeZone('GMT'));
                $value = $date->format($format) . 'Z';
                break;
            case self::TYPE_BIT_STRING:
                if (isset($mapping['mapping'])) {
                    if ($source instanceof BitString) {
                        if (!isset($source->mappedValue)) {
                            $value = (string) $source;
                            break;
                        }
                        $source = $source->mappedValue;
                    }
                    if (!is_array($source)) {
                        if (!is_string($source)) {
                            $type = is_object($source) ? $source::class : gettype($source);
                            throw new UnexpectedValueException(implode('/', self::$location) . " should be either a string, an array or an instance of BitString - $type provided");
                        }
                        $value = $source;
                        break;
                    }
                    $bits = array_fill(0, count($mapping['mapping']), 0);
                    $size = 0;
                    for ($i = 0; $i < count($mapping['mapping']); $i++) {
                        if (in_array($mapping['mapping'][$i], $source)) {
                            $bits[$i] = 1;
                            $size = $i;
                        }
                    }

                    if (isset($mapping['min']) && $mapping['min'] >= 1 && $size < $mapping['min']) {
                        $size = $mapping['min'] - 1;
                    }

                    $offset = 8 - (($size + 1) & 7);
                    $offset = $offset !== 8 ? $offset : 0;

                    $value = chr($offset);

                    for ($i = $size + 1; $i < count($mapping['mapping']); $i++) {
                        unset($bits[$i]);
                    }

                    $bits = implode('', array_pad($bits, $size + $offset + 1, 0));
                    $bytes = explode(' ', rtrim(chunk_split($bits, 8, ' ')));
                    foreach ($bytes as $byte) {
                        $value .= chr(bindec($byte));
                    }

                    break;
                }
                if (!is_string($source) && !$source instanceof BitString) {
                    $type = is_object($source) ? $source::class : gettype($source);
                    throw new UnexpectedValueException(implode('/', self::$location) . " should be either a string or an instance of BitString - $type provided");
                }
                $value = (string) $source;
                break;
            case self::TYPE_OCTET_STRING:
                /* The initial octet shall encode, as an unsigned binary integer with bit 1 as the least significant bit,
                   the number of unused bits in the final subsequent octet. The number shall be in the range zero to seven.

                   -- http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=16 */
                if (!Strings::is_stringable($source)) {
                    $type = is_object($source) ? $source::class : gettype($source);
                    throw new UnexpectedValueException(implode('/', self::$location) . " (a $type) could not be converted to a string");
                }
                $value = (string) $source;
                break;
            case self::TYPE_OBJECT_IDENTIFIER:
                $value = self::encodeOID((string) $source);
                break;
            case self::TYPE_ANY:
                $loc = self::$location;
                if (isset($idx)) {
                    array_pop(self::$location);
                }

                switch (true) {
                    case $source instanceof BaseType && $source->hasTypeID():
                        return self::encode_der($source, ['type' => $source->getTypeID()] + $mapping, null);
                    case !isset($source):
                        return self::encode_der(null, ['type' => self::TYPE_NULL] + $mapping, null);
                    case is_int($source):
                    case $source instanceof BigInteger:
                        return self::encode_der($source, ['type' => self::TYPE_INTEGER] + $mapping, null);
                    case is_float($source):
                        return self::encode_der($source, ['type' => self::TYPE_REAL] + $mapping, null);
                    case is_bool($source):
                        return self::encode_der($source, ['type' => self::TYPE_BOOLEAN] + $mapping, null);
                    case is_string($source):
                        return self::encode_der($source, ['type' => self::TYPE_UTF8_STRING] + $mapping, null);
                }
                throw new UnexpectedValueException('Please choose a primitive type or create an ASN1Element for ' . implode('/', $loc));
            case self::TYPE_NULL:
                $value = '';
                break;
            case self::TYPE_NUMERIC_STRING:
            case self::TYPE_TELETEX_STRING:
            case self::TYPE_PRINTABLE_STRING:
            case self::TYPE_UNIVERSAL_STRING:
            case self::TYPE_UTF8_STRING:
            case self::TYPE_BMP_STRING:
            case self::TYPE_IA5_STRING:
            case self::TYPE_VISIBLE_STRING:
            case self::TYPE_VIDEOTEX_STRING:
            case self::TYPE_GRAPHIC_STRING:
            case self::TYPE_GENERAL_STRING:
                if ($source instanceof BaseType && $source->hasTypeID() && $source->getTypeID() != $tag) {
                    throw new UnexpectedValueException('Object type does not match the expected type for ' . implode('/', self::$location));
                }
                $value = (string) $source;
                break;
            case self::TYPE_BOOLEAN:
                if ($source instanceof Boolean) {
                    $source = $source->value;
                }
                if (!is_bool($source)) {
                    throw new UnexpectedValueException('Value is not a Boolean or a bool at ' . implode('/', self::$location));
                }
                $value = $source ? "\xFF" : "\x00";
                break;
            default:
                throw new UnexpectedValueException('Mapping provides no type definition for ' . implode('/', self::$location));
        }

        if (isset($idx)) {
            array_pop(self::$location);
        }

        if (isset($mapping['cast'])) {
            if (isset($mapping['explicit']) || $mapping['type'] == self::TYPE_CHOICE) {
                $value = chr($tag) . self::encodeLength(strlen($value)) . $value;
                $tag = ($mapping['class'] << 6) | 0x20 | $mapping['cast'];
            } else {
                $tag = ($mapping['class'] << 6) | (ord($temp[0]) & 0x20) | $mapping['cast'];
            }
        }

        $header = chr($tag) . self::encodeLength(strlen((string) $value));

        if ($source instanceof BaseType) {
            $source->setEncoded($header, $value);
        }

        return $header . $value;
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public static function enable64BitOIDHandling(): void
    {
        if (PHP_INT_SIZE === 4) {
            throw new BadConfigurationException('64-bit OID handling is unavailable on 32-bit PHP installs');
        }
        self::$use64BitOIDHandling = true;
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public static function disable64BitOIDHandling(): void
    {
        self::$use64BitOIDHandling = false;
    }

    /**
     * BER-decode the OID
     *
     * Called by _decode_ber()
     */
    public static function decodeOID(string $content): OID
    {
        // BigInteger's are used because of OIDs like 2.25.329800735698586629295641978511506172918
        // https://healthcaresecprivacy.blogspot.com/2011/02/creating-and-using-unique-id-uuid-oid.html elaborates.
        static $eighty;
        if (!$eighty) {
            $eighty = new BigInteger(80);
        }

        if (!isset(self::$use64BitOIDHandling)) {
            self::$use64BitOIDHandling = PHP_INT_SIZE === 8;
        }

        $oid = [];
        $pos = 0;
        $len = strlen($content);

        // see https://github.com/openjdk/jdk/blob/2deb318c9f047ec5a4b160d66a4b52f93688ec42/src/java.base/share/classes/sun/security/util/ObjectIdentifier.java#L55
        if ($len > 128) {
            throw new ResourceLimitException('Object Identifier size is limited to 128 bytes');
        }

        if (ord($content[-1]) & 0x80) {
            throw new UnexpectedValueException('OID is malformed');
        }

        $n = new BigInteger();
        $subn = $numBytes = 0;
        if (self::$use64BitOIDHandling) {
            $prefix = '';
            while ($pos < $len) {
                $temp = ord($content[$pos++]);
                $subn <<= 7;
                $subn |= ($temp & 0x7F);
                $numBytes++;
                $endByte = ~$temp & 0x80;
                if ($numBytes === PHP_INT_SIZE) {
                    // the idea here is that if we have 8 bytes then we have 8 bits that we need to throw away. those
                    // 8 bits form 1 byte and the remaining bits form 7 bytes. since we've already compacted the 64 bits
                    // down to 56 bits (7 bytes) we can just throw away the most significant byte, hence the substring.
                    // at this point, appending to the string we've been building up is basically the same as left
                    // shifting that string by 56 bits and then doing a bitwise OR with our new 56-bit integer

                    // this trick doesn't work on 32-bit systems because on a 32-bit system you'd have 4 bytes and 4 bits
                    // that need to be thrown away. the problem with that being that you can't simply throw away 4 bits
                    // by doing a substring.
                    $prefix .= substr(pack('J', $subn), 1);
                    $subn = $numBytes = 0;
                    if ($endByte) {
                        $oid[] = new BigInteger($prefix, 256);
                        $prefix = '';
                    }
                } elseif ($endByte) {
                    if (strlen($prefix)) {
                        $arc = new BigInteger($prefix, 256);
                        $arc = $arc->bitwise_leftShift($numBytes * 7);
                        $oid[] = $arc->bitwise_or(new BigInteger($subn));
                        $prefix = '';
                    } else {
                        $oid[] = new BigInteger($subn);
                    }
                    $subn = $numBytes = 0;
                }
            }
        } else {
            while ($pos < $len) {
                $temp = ord($content[$pos++]);
                $subn <<= 7;
                $subn |= ($temp & 0x7F);
                $numBytes++;
                $endByte = ~$temp & 0x80;
                if ($numBytes === PHP_INT_SIZE || $endByte) {
                    $n = $n->bitwise_leftShift($numBytes * 7);
                    $n = $n->bitwise_or(new BigInteger($subn));
                    $subn = $numBytes = 0;
                    if ($endByte) {
                        $oid[] = $n;
                        $n = new BigInteger();
                    }
                }
            }
        }
        $part1 = array_shift($oid);
        $first = floor(ord($content[0]) / 40);
        /*
          "This packing of the first two object identifier components recognizes that only three values are allocated from the root
           node, and at most 39 subsequent values from nodes reached by X = 0 and X = 1."

          -- https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=22
        */
        if ($first <= 2) { // ie. 0 <= ord($content[0]) < 120 (0x78)
            array_unshift($oid, ord($content[0]) % 40);
            array_unshift($oid, $first);
        } else {
            array_unshift($oid, $part1->subtract($eighty));
            array_unshift($oid, 2);
        }

        return new OID(implode('.', $oid));
    }

    /**
     * DER-encode the OID
     *
     * Called by _encode_der()
     */
    public static function encodeOID(string $source): string
    {
        static $mask, $zero, $forty;
        if (!$mask) {
            $mask = new BigInteger(0x7F);
            $zero = new BigInteger();
            $forty = new BigInteger(40);
        }

        if (!preg_match('#^\d+(?:\.\d+)+$#', $source)) {
            if (!isset(self::$reverseOIDs[$source])) {
                self::loadAllOIDs();
            }
            $oid = self::$reverseOIDs[$source] ?? false;
        } else {
            $oid = $source;
        }
        if ($oid === false) {
            throw new UnsupportedValueException("$source is an invalid OID");
        }

        $parts = explode('.', $oid);
        $part1 = array_shift($parts);
        $part2 = array_shift($parts);

        if ($part1 > 2) {
            throw new UnexpectedValueException('The first OID subidentifier should be between 0 and 2');
        }
        if ($part1 < 2 && $part2 > 39) {
            throw new UnexpectedValueException('The second OID subidentifier should not be larger than 39 unless the first subidentifier is 2');
        }

        $first = new BigInteger($part1);
        $first = $first->multiply($forty);
        $first = $first->add(new BigInteger($part2));

        array_unshift($parts, $first->toString());

        $value = '';
        foreach ($parts as $part) {
            if (!$part) {
                $temp = "\0";
            } else {
                $temp = '';
                $part = new BigInteger($part);
                while (!$part->equals($zero)) {
                    $submask = $part->bitwise_and($mask);
                    $submask->setPrecision(8);
                    $temp = (chr(0x80) | $submask->toBytes()) . $temp;
                    $part = $part->bitwise_rightShift(7);
                }
                $temp[-1] = $temp[-1] & chr(0x7F);
            }
            $value .= $temp;
        }

        return $value;
    }

    /**
     * BER-decode the time
     *
     * Called by _decode_ber() and in the case of implicit tags map().
     */
    private static function decodeTime(string $content, int $tag): UTCTime|GeneralizedTime
    {
        /* UTCTime:
           http://tools.ietf.org/html/rfc5280#section-4.1.2.5.1
           http://www.obj-sys.com/asn1tutorial/node15.html

           GeneralizedTime:
           http://tools.ietf.org/html/rfc5280#section-4.1.2.5.2
           http://www.obj-sys.com/asn1tutorial/node14.html */

        $format = 'YmdHis';

        if ($tag == self::TYPE_UTC_TIME) {
            // https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=28 says "the seconds
            // element shall always be present" but none-the-less I've seen X509 certs where it isn't and if the
            // browsers parse it phpseclib ought to too
            if (preg_match('#^(\d{10})(Z|[+-]\d{4})$#', $content, $matches)) {
                $content = $matches[1] . '00' . $matches[2];
            }
            $prefix = substr($content, 0, 2) >= 50 ? '19' : '20';
            $content = $prefix . $content;
        } elseif (str_contains($content, '.')) {
            $format .= '.u';
        }

        if ($content[-1] == 'Z') {
            $content = substr($content, 0, -1) . '+0000';
        }

        if (str_contains($content, '-') || str_contains($content, '+')) {
            $format .= 'O';
        }

        $result = $tag == self::TYPE_UTC_TIME ?
            UTCTime::createFromFormat($format, $content) :
            GeneralizedTime::createFromFormat($format, $content);

        if ($result === false) {
            throw new UnexpectedValueException('Unable to parse date time');
        }

        return $result;
    }

    /**
     * Load OIDs
     *
     * Load the relevant OIDs for a particular ASN.1 semantic mapping.
     * Previously loaded OIDs are retained.
     */
    public static function loadOIDs(array|string $oids): void
    {
        if (is_array($oids)) {
            self::$reverseOIDs += $oids;
            self::$oids = array_flip(self::$reverseOIDs);
            return;
        }
        $class = 'phpseclib4\File\ASN1\OIDs\\' . $oids;
        if (!class_exists($class)) {
            return;
        }
        self::$reverseOIDs += $class::OIDs;
        self::$oids = array_flip(self::$reverseOIDs);
    }

    /**
     * Load All OIDs
     */
    private static function loadAllOIDs(): void
    {
        if (self::$allOIDsLoaded) {
            return;
        }
        foreach (new \DirectoryIterator(__DIR__ . '/ASN1/OIDs/') as $file) {
            if ($file->getExtension() != 'php') {
                continue;
            }
            $name = $file->getBasename('.php');
            if ($name[0] == '.') {
                continue;
            }
            self::loadOIDs($name);
        }
    }

    /**
     * Extract raw BER from Base64 encoding
     */
    public static function extractBER(string $str): string
    {
        /* X.509 certs are assumed to be base64 encoded but sometimes they'll have additional things in them
         * above and beyond the ceritificate.
         * ie. some may have the following preceding the -----BEGIN CERTIFICATE----- line:
         *
         * Bag Attributes
         *     localKeyID: 01 00 00 00
         * subject=/O=organization/OU=org unit/CN=common name
         * issuer=/O=organization/CN=common name
         */
        if (strlen($str) > ini_get('pcre.backtrack_limit')) {
            $temp = $str;
        } else {
            $temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms', '', $str, 1);
            $temp = preg_replace('#-+END.*[\r\n ]*.*#ms', '', $temp, 1);
        }
        // remove new lines
        $temp = str_replace(["\r", "\n", ' '], '', $temp);
        // remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff
        $temp = preg_replace('#^-+[^-]+-+|-+[^-]+-+$#', '', $temp);
        return preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? Strings::base64_decode($temp) : $str;
    }

    /**
     * DER-encode the length
     *
     * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4.  See
     * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
     */
    public static function encodeLength(int $length): string
    {
        if ($length <= 0x7F) {
            return chr($length);
        }

        $temp = ltrim(pack('N', $length), chr(0));
        return pack('Ca*', 0x80 | strlen($temp), $temp);
    }

    /**
     * Returns the OID corresponding to a name
     *
     * What's returned in the associative array returned by loadX509() (or load*()) is either a name or an OID if
     * no OID to name mapping is available. The problem with this is that what may be an unmapped OID in one version
     * of phpseclib may not be unmapped in the next version, so apps that are looking at this OID may not be able
     * to work from version to version.
     *
     * This method will return the OID if a name is passed to it and if no mapping is avialable it'll assume that
     * what's being passed to it already is an OID and return that instead. A few examples.
     *
     * getOID('2.16.840.1.101.3.4.2.1') == '2.16.840.1.101.3.4.2.1'
     * getOID('id-sha256') == '2.16.840.1.101.3.4.2.1'
     * getOID('zzz') == 'zzz'
     */
    public static function getOIDFromName(string $name): string
    {
        if (!isset(self::$reverseOIDs[$name])) {
            self::loadAllOIDs();
        }
        return self::$reverseOIDs[$name] ?? $name;
    }

    /**
     * Returns the name corresponding to an OID
     *
     * This method will return the name if an OID is passed to it and if no mapping is avialable it'll assume that
     * what's being passed to it already is a name and will return that instead. A few examples.
     *
     * getOID('2.16.840.1.101.3.4.2.1') == 'id-sha256'
     * getOID('id-sha256') == 'id-sha256'
     * getOID('zzz') == 'zzz'
     */
    public static function getNameFromOID(string $oid): string
    {
        if (!isset(self::$oids[$oid])) {
            self::loadAllOIDs();
        }
        return self::$oids[$oid] ?? $oid;
    }

    /**
     * Extracts the V part of a TLV DER / BER encoded string
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public static function extractValue(string $encoded): string
    {
        $offset = 0;
        ASN1::decodeTag($encoded, $offset);
        $length = ASN1::decodeLength($encoded, $offset);
        return substr($encoded, $offset, $length);
    }

    public static function formatTime(\DateTimeInterface|string $date): array
    {
        if ($date instanceof GeneralizedTime) {
            return ['generalTime' => $date];
        }
        if ($date instanceof UTCTime) {
            return ['utcTime' => $date];
        }
        if ($date instanceof \DateTimeInterface) {
            return ['utcTime' => UTCTime::createFromInterface($date)];
        }
        $date = new UTCTime($date, new \DateTimeZone(@date_default_timezone_get()));
        return ['utcTime' => $date];
    }

    public static function convertTypeConstantToString(int $type): string
    {
        $result = match ($type) {
            1 => 'BOOLEAN',
            2 => 'INTEGER',
            3 => 'BIT STRING',
            4 => 'OCTET STRING',
            5 => 'NULL',
            6 => 'OBJECT IDENTIFIER',
            9 => 'REAL',
            10 => 'ENUMERATED',
            12 => 'UTF8 STRING',
            16 => 'SEQUENCE',
            17 => 'SET',
            18 => 'NUMERIC STRING',
            19 => 'TELETEX STRING',
            21 => 'VIDEOTEX STRING',
            22 => 'IA5 STRING',
            23 => 'UTC TIME',
            24 => 'GENERALIZED TIME',
            25 => 'GRAPHIC STRING',
            26 => 'VISIBLE STRING',
            27 => 'GENERAL STRING',
            28 => 'UNIVERSAL STRING',
            30 => 'BMP STRING',
            default => 'UNKNOWN'
        };
        return "$result ($type)";
    }

    public static function setRecursionDepth(int $depth): void
    {
        self::$recursionDepth = $depth;
    }

    public static function getRecursionDepth(): int
    {
        return self::$recursionDepth;
    }

    public static function convertToPrimitive(BaseType|PublicKey|X509|CRL $value): string|null|bool
    {
        return match ($value::class) {
            ExplicitNull::class => null,
            Boolean::class => $value->value,
            default => (string) $value
        };
    }

    /*
    public static function decodeIP(string $val): string|array
    {
        switch (strlen($val)) {
            // this is the format that should be used 99% of the time per
            // https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.6
            case 4:  // IPv4
            case 16: // IPv6
                return inet_ntop($val);
            // in theory the following should only be encountered name constraints per
            // https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.10
            // in practice there's not an easy way to know which is which
            case 8:  // IPv4
            case 32: // IPv6
                $size = strlen($val) >> 1;
                $mask = substr($val, $size);
                $ip = substr($val, 0, $size);
                return [inet_ntop($ip), inet_ntop($mask)];
        }
        throw new UnexpectedValueException('An invalid IP address was encountered');
    }
    */
}
<?php

/**
 * GeneralNames
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * GeneralNames
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class GeneralNames
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'min' => 1,
        'max' => -1,
        'children' => GeneralName::MAP,
    ];
}
<?php

/**
 * RecipientInfo
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * RecipientInfo
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class RecipientInfo
{
    public const MAP = [
        'type' => ASN1::TYPE_CHOICE,
        'children' => [
            'ktri' => [
                //'constant' => 0,
                'optional' => true,
                'implicit' => true,
            ] + KeyTransRecipientInfo::MAP,
            'kari' => [
                'constant' => 1,
                'optional' => true,
                'implicit' => true,
            ] + KeyAgreeRecipientInfo::MAP,
            'kekri' => [
                'constant' => 2,
                'optional' => true,
                'implicit' => true,
            ] + KEKRecipientInfo::MAP,
            'pwri' => [
                'constant' => 3,
                'optional' => true,
                'implicit' => true,
            ] + PasswordRecipientInfo::MAP,
            'ori' => [
                'constant' => 4,
                'optional' => true,
                'implicit' => true,
            ] + OtherRecipientInfo::MAP,
        ],
    ];
}
<?php

/**
 * ObjectDigestInfo
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * ObjectDigestInfo
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class ObjectDigestInfo
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'digestedObjectType' => [
                'type' => ASN1::TYPE_ENUMERATED,
                'children' => [
                    'publicKey',
                    'publicKeyCert',
                    'otherObjectTypes',
                ],
            ],
            'otherObjectTypeID' => [
                'type' => ASN1::TYPE_OBJECT_IDENTIFIER,
                'optiona' => true,
            ],
            'digestAlgorithm' => AlgorithmIdentifier::MAP,
            'objectDigest' => ['type' => ASN1::TYPE_BIT_STRING],
        ],
    ];
}
<?php

/**
 * LogotypeReference
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * LogotypeReference
 *
 * https://datatracker.ietf.org/doc/html/rfc9399#appendix-A.1
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class LogotypeReference
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'refStructHash' => [
                'type' => ASN1::TYPE_SEQUENCE,
                'min' => 1,
                'max' => -1,
                'children' => HashAlgAndValue::MAP,
            ],
            'refStructURI' => [
                'type' => ASN1::TYPE_SEQUENCE,
                'min' => 1,
                'max' => -1,
                'children' => ['type' => ASN1::TYPE_IA5_STRING],
            ],
        ],
    ];
}
<?php

/**
 * EncryptedData
 *
 * This is using the RFC5208 (PKCS#8) definition of EncryptedData:
 * https://datatracker.ietf.org/doc/html/rfc5208#section-6
 *
 * RFC5652 (CMS) has another definition of EncryptedData:
 * https://datatracker.ietf.org/doc/html/rfc5652#section-8
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * EncryptedData
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class EncryptedData
{
    public const MAP = ['type' => ASN1::TYPE_OCTET_STRING];
}
<?php

/**
 * AuthorityKeyIdentifier
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * AuthorityKeyIdentifier
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class AuthorityKeyIdentifier
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'keyIdentifier' => [
                'constant' => 0,
                'optional' => true,
                'implicit' => true,
            ] + KeyIdentifier::MAP,
            'authorityCertIssuer' => [
                'constant' => 1,
                'optional' => true,
                'implicit' => true,
            ] + GeneralNames::MAP,
            'authorityCertSerialNumber' => [
                'constant' => 2,
                'optional' => true,
                'implicit' => true,
            ] + CertificateSerialNumber::MAP,
        ],
    ];
}
<?php

/**
 * SafeBag
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * SafeBag
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class SecretBag
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'secretTypeId' => ['type' => ASN1::TYPE_OBJECT_IDENTIFIER],
            'secretValue' => [
                'constant' => 0,
                'optional' => true,
                'explicit' => true,
                'type' => ASN1::TYPE_ANY,
            ],
            /*
            'bagAttributes' => [
                'optional' => true,
                'implicit' => true,
                'type' => ASN1::TYPE_SET,
                'min' => 1,
                'max' => -1,
                'children' => PKCS12Attribute::MAP
            ],
            */
        ],
    ];
}
<?php

/**
 * EncryptedContentInfo
 *
 * This is using the RFC5652 (CMS) definition of EncryptedData:
 * https://datatracker.ietf.org/doc/html/rfc5652#section-8
 *
 * RFC5208 (PKCS#8) has another definition of EncryptedData:
 * https://datatracker.ietf.org/doc/html/rfc5208#section-6
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2025-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * EncryptedContentInfo
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class EncryptedContentInfo
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'contentType' => ContentType::MAP,
            'contentEncryptionAlgorithm' => ContentEncryptionAlgorithmIdentifier::MAP,
            'encryptedContent' => [
                'constant' => 0,
                'optional' => true,
                'implicit' => true
            ] + EncryptedContent::MAP,
        ]
    ];
}
<?php

/**
 * CompressionAlgorithmIdentifiers
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * CompressionAlgorithmIdentifiers
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class CompressionAlgorithmIdentifiers
{
    public const MAP = [
        'type' => ASN1::TYPE_SET,
        'min' => 1,
        'max' => -1,
        'children' => CompressionAlgorithmIdentifier::MAP
    ];
}
<?php

/**
 * CertificationRequest
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * CertificationRequest
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class CertificationRequest
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'certificationRequestInfo' => CertificationRequestInfo::MAP,
            'signatureAlgorithm' => AlgorithmIdentifier::MAP,
            'signature' => ['type' => ASN1::TYPE_BIT_STRING],
        ],
    ];
}
<?php

/**
 * QCStatement
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * QCStatement
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class QCStatement
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'statementId' => ['type' => ASN1::TYPE_OBJECT_IDENTIFIER],
            'statementInfo' => [
                'type' => ASN1::TYPE_ANY,
                'optional' => true,
            ],
        ],
    ];
}
<?php

/**
 * MessageAuthenticationCodeAlgorithm
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

/**
 * MessageAuthenticationCodeAlgorithm
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class MessageAuthenticationCodeAlgorithm extends AlgorithmIdentifier
{
}
<?php

/**
 * CertificatePolicies
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * CertificatePolicies
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class CertificatePolicies
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'min' => 1,
        'max' => -1,
        'children' => PolicyInformation::MAP,
    ];
}
<?php

/**
 * Attribute
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * Attribute
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class Attribute
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'type' => AttributeType::MAP,
            'value' => [
                'type' => ASN1::TYPE_SET,
                'min' => 1,
                'max' => -1,
                'children' => AttributeValue::MAP,
            ],
        ],
    ];
}
<?php

/**
 * OtherPrimeInfo
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * OtherPrimeInfo
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class OtherPrimeInfo
{
    // version must be multi if otherPrimeInfos present
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'prime' => ['type' => ASN1::TYPE_INTEGER],      // ri
            'exponent' => ['type' => ASN1::TYPE_INTEGER],   // di
            'coefficient' => ['type' => ASN1::TYPE_INTEGER], // ti
        ],
    ];
}
<?php

/**
 * IssuerSerial
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * IssuerSerial
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class IssuerSerial
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'issuer' => GeneralNames::MAP,
            'serialNumber' => CertificateSerialNumber::MAP,
        ],
    ];
}
<?php

/**
 * TSAPolicyId
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * TSAPolicyId
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class TSAPolicyId
{
    public const MAP = ['type' => ASN1::TYPE_OBJECT_IDENTIFIER];
}
<?php

/**
 * PBEParameter
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * PBEParameter
 *
 * from https://tools.ietf.org/html/rfc2898#appendix-A.3
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class PBEParameter
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'salt' => ['type' => ASN1::TYPE_OCTET_STRING],
            'iterationCount' => ['type' => ASN1::TYPE_INTEGER],
        ],
    ];
}
<?php

/**
 * SubjectPublicKeyInfo
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * SubjectPublicKeyInfo
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class SubjectPublicKeyInfo
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'algorithm' => AlgorithmIdentifier::MAP,
            'subjectPublicKey' => ['type' => ASN1::TYPE_BIT_STRING],
        ],
    ];
}
<?php

/**
 * PrivateKeyUsagePeriod
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * PrivateKeyUsagePeriod
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class PrivateKeyUsagePeriod
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'notBefore' => [
                'constant' => 0,
                'optional' => true,
                'implicit' => true,
                'type' => ASN1::TYPE_GENERALIZED_TIME, ],
            'notAfter' => [
                'constant' => 1,
                'optional' => true,
                'implicit' => true,
                'type' => ASN1::TYPE_GENERALIZED_TIME, ],
        ],
    ];
}
<?php

/**
 * EnvelopedData
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * EnvelopedData
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class EnvelopedData
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'version' => CMSVersion::MAP,
            'originatorInfo' => [
                'constant' => 0,
                'optional' => true,
                'implicit' => true,
            ] + OriginatorInfo::MAP,
            'recipientInfos' => RecipientInfos::MAP,
            'encryptedContentInfo' => EncryptedContentInfo::MAP,
            'unprotectedAttrs' => [
                'constant' => 1,
                'optional' => true,
                'implicit' => true,
            ] + UnprotectedAttributes::MAP,
        ],
    ];
}
<?php

/**
 * LogotypeAudioInfo
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * LogotypeAudioInfo
 *
 * fileSize is in octets, playTime in milliseconds, channels is 1 for mono,
 * 2 for stereo and 4 for quad. sampleRate is in samples per second and
 * language, when present, uses the syntax of RFC 5646.
 *
 * https://datatracker.ietf.org/doc/html/rfc9399#appendix-A.1
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class LogotypeAudioInfo
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'fileSize' => ['type' => ASN1::TYPE_INTEGER],
            'playTime' => ['type' => ASN1::TYPE_INTEGER],
            'channels' => ['type' => ASN1::TYPE_INTEGER],
            'sampleRate' => [
                'type' => ASN1::TYPE_INTEGER,
                'constant' => 3,
                'optional' => true,
                'implicit' => true,
            ],
            'language' => [
                'type' => ASN1::TYPE_IA5_STRING,
                'constant' => 4,
                'optional' => true,
                'implicit' => true,
            ],
        ],
    ];
}
<?php

/**
 * PFX
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * PFX
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class PFX
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'version' => [
                'type' => ASN1::TYPE_INTEGER,
                'mapping' => [3 => 'v3'],
            ],
            'authSafe' => ContentInfo::MAP,
            'macData' => [
                'optional' => true,
                'implicit' => true,
            ] + MacData::MAP,
        ],
    ];
}
<?php

/**
 * netscape_comment
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * netscape_comment
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class netscape_comment
{
    public const MAP = ['type' => ASN1::TYPE_IA5_STRING];
}
<?php

/**
 * ContentInfo
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * ContentInfo
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class ContentInfo
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'contentType' => ContentType::MAP,
            'content' => [
                'type' => ASN1::TYPE_ANY,
                'constant' => 0,
                'optional' => true,
                'explicit' => true,
            ],
        ],
    ];
}
<?php

/**
 * OrganizationalUnitNames
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * OrganizationalUnitNames
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class OrganizationalUnitNames
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'min' => 1,
        'max' => 4, // ub-organizational-units
        'children' => ['type' => ASN1::TYPE_PRINTABLE_STRING],
    ];
}
<?php

/**
 * LogotypeImageType
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * LogotypeImageType
 *
 * https://datatracker.ietf.org/doc/html/rfc9399#appendix-A.1
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class LogotypeImageType
{
    public const MAP = [
        'type' => ASN1::TYPE_INTEGER,
        'mapping' => ['grayScale', 'color'],
    ];
}
<?php

/**
 * SignedData
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * SignedData
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class SignedData
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'version' => CMSVersion::MAP,
            'digestAlgorithms' => DigestAlgorithmIdentifiers::MAP,
            'encapContentInfo' => EncapsulatedContentInfo::MAP,
            'certificates' => [
                'constant' => 0,
                'optional' => true,
                'implicit' => true,
            ] + CertificateSet::MAP,
            'crls' => [
                'constant' => 1,
                'optional' => true,
                'implicit' => true,
            ] + RevocationInfoChoices::MAP,
            'signerInfos' => SignerInfos::MAP,
        ],
    ];
}
<?php

/**
 * BasicConstraints
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * BasicConstraints
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class BasicConstraints
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'cA' => [
                'type' => ASN1::TYPE_BOOLEAN,
                'optional' => true,
                'default' => false,
            ],
            'pathLenConstraint' => [
                'type' => ASN1::TYPE_INTEGER,
                'optional' => true,
            ],
        ],
    ];
}
<?php

/**
 * BuiltInDomainDefinedAttributes
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * BuiltInDomainDefinedAttributes
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class BuiltInDomainDefinedAttributes
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'min' => 1,
        'max' => 4, // ub-domain-defined-attributes
        'children' => BuiltInDomainDefinedAttribute::MAP,
    ];
}
<?php

/**
 * NumericUserIdentifier
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * NumericUserIdentifier
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class NumericUserIdentifier
{
    public const MAP = ['type' => ASN1::TYPE_NUMERIC_STRING];
}
<?php

/**
 * EncryptedDataCMS
 *
 * EncryptedData is defined differently in different RFCs.
 * RFC5652 (CMS) defines it one way:
 *
 * https://datatracker.ietf.org/doc/html/rfc5652#page-30
 *
 * RFC5208 (PKCS8) defines it another way:
 *
 * https://datatracker.ietf.org/doc/html/rfc5208#page-4
 *
 * This class implements the RFC5652 EncryptedData definition.
 *
 * The only difference between RFC5652's EncryptedData and RFC5652's EnvelopedData
 * is that EnvelopedData has originatorInfo (optional) and recipientInfos (required)
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * EncryptedDataCMS
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class EncryptedDataCMS
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'version' => CMSVersion::MAP,
            'encryptedContentInfo' => EncryptedContentInfo::MAP,
            'unprotectedAttrs' => [
                'constant' => 1,
                'optional' => true,
                'implicit' => true,
            ] + UnprotectedAttributes::MAP,
        ],
    ];
}
<?php

/**
 * MaskGenAglorithm
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

/**
 * MaskGenAglorithm
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class MaskGenAlgorithm
{
    public const MAP = AlgorithmIdentifier::MAP;
}
<?php

/**
 * PBMAC1params
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * PBMAC1params
 *
 * from https://tools.ietf.org/html/rfc2898#appendix-A.3
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class PBMAC1params
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'keyDerivationFunc' => AlgorithmIdentifier::MAP,
            'messageAuthScheme' => AlgorithmIdentifier::MAP,
        ],
    ];
}
<?php

/**
 * MacData
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * MacData
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class MacData
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'mac' => DigestInfo::MAP,
            'macSalt' => ['type' => ASN1::TYPE_OCTET_STRING],
            'iterations' => [
                'optional' => true,
                'implicit' => true,
                'default' => 1,
                'type' => ASN1::TYPE_INTEGER,
            ],
        ],
    ];
}
<?php

/**
 * CertificateList
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * CertificateList
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class CertificateList
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'tbsCertList' => TBSCertList::MAP,
            'signatureAlgorithm' => AlgorithmIdentifier::MAP,
            'signature' => ['type' => ASN1::TYPE_BIT_STRING],
        ],
    ];
}
<?php

/**
 * netscape_cert_type
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * netscape_cert_type
 *
 * mapping is from <http://www.mozilla.org/projects/security/pki/nss/tech-notes/tn3.html>
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class netscape_cert_type
{
    public const MAP = [
        'type' => ASN1::TYPE_BIT_STRING,
        'mapping' => [
            'SSLClient',
            'SSLServer',
            'Email',
            'ObjectSigning',
            'Reserved',
            'SSLCA',
            'EmailCA',
            'ObjectSigningCA',
        ],
    ];
}
<?php

/**
 * CMSEncryptedData
 *
 * This is using the RFC5652 (CMS) definition of EncryptedData:
 * https://datatracker.ietf.org/doc/html/rfc5652#section-8
 *
 * RFC5208 (PKCS#8) has another definition of EncryptedData:
 * https://datatracker.ietf.org/doc/html/rfc5208#section-6
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * CMSEncryptedData
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class CMSEncryptedData
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'version' => CMSVersion::MAP,
            'encryptedContentInfo' => EncryptedContentInfo::MAP,
            'unprotectedAttrs' => [
                'constant' => 1,
                'optional' => true,
                'implicit' => true
            ] + UnprotectedAttributes::MAP,
        ]
    ];
}
<?php

/**
 * MQVuserKeyingMaterial
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * MQVuserKeyingMaterial
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class MQVuserKeyingMaterial
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'ephemeralPublicKey' => OriginatorPublicKey::MAP,
            'addedukm' => [
                'constant' => 0,
                'optional' => true,
                'explicit' => true,
            ] + UserKeyingMaterial::MAP,
        ],
    ];
}
<?php

/**
 * UnsignedAttributes
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * UnsignedAttributes
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class UnsignedAttributes
{
    public const MAP = [
        'type' => ASN1::TYPE_SET,
        'min' => 1,
        'max' => -1,
        'children' => Attribute::MAP,
    ];
}
<?php

/**
 * AccessDescription
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * AccessDescription
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class AccessDescription
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'accessMethod' => ['type' => ASN1::TYPE_OBJECT_IDENTIFIER],
            'accessLocation' => GeneralName::MAP,
        ],
    ];
}
<?php

/**
 * CertificateChoices
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * CertificateChoices
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class CertificateChoices
{
    public const MAP = [
        'type' => ASN1::TYPE_CHOICE,
        'children' => [
            'certificate' => Certificate::MAP,
            'extendedCertificate' => [
                'constant' => 0,
                'optional' => true,
                'implicit' => true,
            ] + ExtendedCertificate::MAP,
            'v1AttrCert' => [
                'constant' => 1,
                'optional' => true,
                'implicit' => true,
            ] + AttributeCertificateV1::MAP,
            'v2AttrCert' => [
                'constant' => 2,
                'optional' => true,
                'implicit' => true,
            ] + AttributeCertificateV2::MAP,
            'other' => [
                'constant' => 3,
                'optional' => true,
                'implicit' => true,
            ] + OtherCertificateFormat::MAP,
        ],
    ];
}
<?php

/**
 * RSAPublicKey
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * RSAPublicKey
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class RSAPublicKey
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'modulus' => ['type' => ASN1::TYPE_INTEGER],
            'publicExponent' => ['type' => ASN1::TYPE_INTEGER],
        ],
    ];
}
<?php

/**
 * Trinomial
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * Trinomial
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class Trinomial
{
    public const MAP = ['type' => ASN1::TYPE_INTEGER];
}
<?php

/**
 * KeyUsage
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * KeyUsage
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class KeyUsage
{
    public const MAP = [
        'type' => ASN1::TYPE_BIT_STRING,
        'mapping' => [
            'digitalSignature',
            'nonRepudiation',
            'keyEncipherment',
            'dataEncipherment',
            'keyAgreement',
            'keyCertSign',
            'cRLSign',
            'encipherOnly',
            'decipherOnly',
        ],
    ];
}
<?php

/**
 * CertificationRequestInfo
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * CertificationRequestInfo
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class CertificationRequestInfo
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'version' => [
                'type' => ASN1::TYPE_INTEGER,
                'mapping' => ['v1'],
            ],
            'subject' => Name::MAP,
            'subjectPKInfo' => SubjectPublicKeyInfo::MAP,
            'attributes' => [
                'constant' => 0,
                'optional' => true,
                'implicit' => true,
            ] + Attributes::MAP,
        ],
    ];
}
<?php

/**
 * RC2CBCParameter
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * RC2CBCParameter
 *
 * from https://tools.ietf.org/html/rfc2898#appendix-A.3
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class RC2CBCParameter
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'rc2ParametersVersion' => [
                'type' => ASN1::TYPE_INTEGER,
                'optional' => true,
            ],
            'iv' => ['type' => ASN1::TYPE_OCTET_STRING],
        ],
    ];
}
<?php

/**
 * DHParameter
 *
 * From: https://www.teletrust.de/fileadmin/files/oid/oid_pkcs-3v1-4.pdf#page=6
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * DHParameter
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class DHParameter
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'prime' => ['type' => ASN1::TYPE_INTEGER],
            'base' => ['type' => ASN1::TYPE_INTEGER],
            'privateValueLength' => [
                'type' => ASN1::TYPE_INTEGER,
                'optional' => true,
            ],
        ],
    ];
}
<?php

/**
 * PolicyQualifierId
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * PolicyQualifierId
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class PolicyQualifierId
{
    public const MAP = ['type' => ASN1::TYPE_OBJECT_IDENTIFIER];
}
<?php

/**
 * AttributeValue
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * AttributeValue
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class AttributeValue
{
    public const MAP = ['type' => ASN1::TYPE_ANY];
}
<?php

/**
 * SMIMECapabilities
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * SMIMECapabilities
 *
 * From https://datatracker.ietf.org/doc/html/rfc2985#section-5.6
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class SMIMECapabilities
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'min' => 0,
        'max' => -1,
        'children' => SMIMECapability::MAP,
    ];
}
<?php

/**
 * AttCertIssuer
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * AttCertIssuer
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class AttCertIssuer
{
    public const MAP = [
        'type' => ASN1::TYPE_CHOICE,
        'children' => [
            'v1Form' => GeneralNames::MAP,
            'v2Form' => [
                'constant' => 0,
                'optional' => true,
            ] + V2Form::MAP,
        ],
    ];
}
<?php

/**
 * TBSCertificate
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * TBSCertificate
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class TBSCertificate
{
    // assert($TBSCertificate['children']['signature'] == $Certificate['children']['signatureAlgorithm'])
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            // technically, default implies optional, but we'll define it as being optional, none-the-less, just to
            // reenforce that fact
            'version' => [
                'type' => ASN1::TYPE_INTEGER,
                'constant' => 0,
                'optional' => true,
                'explicit' => true,
                'mapping' => ['v1', 'v2', 'v3'],
                'default' => 'v1',
            ],
            'serialNumber' => CertificateSerialNumber::MAP,
            'signature' => AlgorithmIdentifier::MAP,
            'issuer' => Name::MAP,
            'validity' => Validity::MAP,
            'subject' => Name::MAP,
            'subjectPublicKeyInfo' => SubjectPublicKeyInfo::MAP,
            // implicit means that the T in the TLV structure is to be rewritten, regardless of the type
            'issuerUniqueID' => [
                'constant' => 1,
                'optional' => true,
                'implicit' => true,
            ] + UniqueIdentifier::MAP,
            'subjectUniqueID' => [
                'constant' => 2,
                'optional' => true,
                'implicit' => true,
            ] + UniqueIdentifier::MAP,
            // <http://tools.ietf.org/html/rfc2459#page-74> doesn't use the EXPLICIT keyword but if
            // it's not IMPLICIT, it's EXPLICIT
            'extensions' => [
                'constant' => 3,
                'optional' => true,
                'explicit' => true,
            ] + Extensions::MAP,
        ],
    ];
}
<?php

/**
 * Certificate
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * Certificate
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class Certificate
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'tbsCertificate' => TBSCertificate::MAP,
            'signatureAlgorithm' => AlgorithmIdentifier::MAP,
            'signature' => ['type' => ASN1::TYPE_BIT_STRING],
        ],
    ];
}
<?php

/**
 * ECPrivateKey
 *
 * From: https://tools.ietf.org/html/rfc5915
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * ECPrivateKey
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class ECPrivateKey
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'version' => [
                'type' => ASN1::TYPE_INTEGER,
                'mapping' => [1 => 'ecPrivkeyVer1'],
            ],
            'privateKey' => ['type' => ASN1::TYPE_OCTET_STRING],
            'parameters' => [
                'constant' => 0,
                'optional' => true,
                'explicit' => true,
            ] + ECParameters::MAP,
            'publicKey' => [
                'type' => ASN1::TYPE_BIT_STRING,
                'constant' => 1,
                'optional' => true,
                'explicit' => true,
            ],
        ],
    ];
}
<?php

/**
 * OtherRevocationInfoFormat
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * OtherRevocationInfoFormat
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class OtherRevocationInfoFormat
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'otherRevInfoFormat' => ['type' => ASN1::TYPE_OBJECT_IDENTIFIER],
            'otherRevInfo' => ['type' => ASN1::TYPE_ANY],
        ],
    ];
}
<?php

/**
 * Iso4217CurrencyCode
 *
 * From https://www.etsi.org/deliver/etsi_ts/101800_101899/101862/01.03.02_60/ts_101862v010302p.pdf
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * Iso4217CurrencyCode
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class Iso4217CurrencyCode
{
    public const MAP = [
        'type' => ASN1::TYPE_CHOICE,
        'children' => [
            // Alphabetic or numeric currency code as defined in ISO 4217
            // It is recommended that the Alphabetic form is used
            'alphabetic' => ['type' => ASN1::TYPE_PRINTABLE_STRING], // Recommended (SIZE 3)
            'numeric' => ['type' => ASN1::TYPE_INTEGER], // 1..999
        ],
    ];
}
<?php

/**
 * BuiltInDomainDefinedAttribute
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * BuiltInDomainDefinedAttribute
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class BuiltInDomainDefinedAttribute
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'type' => ['type' => ASN1::TYPE_PRINTABLE_STRING],
            'value' => ['type' => ASN1::TYPE_PRINTABLE_STRING],
        ],
    ];
}
<?php

/**
 * AttributeCertificateV1
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * AttributeCertificateV1
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class AttributeCertificateV1
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'acinfo' => AttributeCertificateInfoV1::MAP,
            'signatureAlgorithm' => AlgorithmIdentifier::MAP,
            'signature' => ['type' => ASN1::TYPE_BIT_STRING],
        ],
    ];
}
<?php

/**
 * UserNotice
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * UserNotice
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class UserNotice
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'noticeRef' => [
                'optional' => true,
                'implicit' => true,
            ] + NoticeReference::MAP,
            'explicitText' => [
                'optional' => true,
                'implicit' => true,
            ] + DisplayText::MAP,
        ],
    ];
}
<?php

/**
 * SafeBag
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * SafeBag
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class SafeBag
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'bagId' => ['type' => ASN1::TYPE_OBJECT_IDENTIFIER],
            'bagValue' => [
                'constant' => 0,
                'optional' => true,
                'explicit' => true,
                'type' => ASN1::TYPE_ANY,
            ],
            // https://www.rfc-editor.org/rfc/rfc7292#section-4.2 defines this as:
            // bagAttributes  SET OF PKCS12Attribute OPTIONAL
            // but PKCS12Attribute and Attribute are basically the same thing so let's just use that one instead
            'bagAttributes' => [
                'optional' => true,
                'implicit' => true,
                'type' => ASN1::TYPE_SET,
                'min' => 1,
                'max' => -1,
                'children' => Attribute::MAP
            ],
        ],
    ];
}
<?php

/**
 * CertificateSet
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * CertificateSet
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class CertificateSet
{
    public const MAP = [
        'type' => ASN1::TYPE_SET,
        'min' => 1,
        'max' => -1,
        'children' => CertificateChoices::MAP,
    ];
}
<?php

/**
 * SigningCertificate
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * SigningCertificate
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class SigningCertificate
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'certs' => [
                'type' => ASN1::TYPE_SEQUENCE,
                'min' => 1,
                'max' => -1,
                'children' => ESSCertID::MAP,
            ],
            'policies' => [
                'type' => ASN1::TYPE_SEQUENCE,
                'min' => 1,
                'max' => -1,
                'children' => PolicyInformation::MAP,
                'optional' => true,
            ],
        ],
    ];
}
<?php

/**
 * ESSCertID
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * ESSCertID
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class ESSCertID
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'certHash' => Hash::MAP, // sha1 hash of the entire cert
            'issuerSerial' => ['optional' => true] + IssuerSerial::MAP,
        ],
    ];
}
<?php

/**
 * QCStatements
 *
 * From http://tools.ietf.org/html/rfc3739
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * QCStatements
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class QCStatements
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'min' => 0,
        'max' => -1,
        'children' => QCStatement::MAP,
    ];
}
<?php

/**
 * ContentEncryptionAlgorithmIdentifier
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

/**
 * ContentEncryptionAlgorithmIdentifier
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class ContentEncryptionAlgorithmIdentifier extends AlgorithmIdentifier
{
}
<?php

/**
 * MessageImprint
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * MessageImprint
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class MessageImprint
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'hashAlgorithm' => AlgorithmIdentifier::MAP,
            'hashedMessage' => ['type' => ASN1::TYPE_OCTET_STRING],
        ],
    ];
}
<?php

/**
 * CertBag
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * CertBag
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class CertBag
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'certId' => ['type' => ASN1::TYPE_OBJECT_IDENTIFIER],
            'certValue' => [
                'constant' => 0,
                'optional' => true,
                'explicit' => true,
                'type' => ASN1::TYPE_ANY,
            ],
        ],
    ];
}
<?php

/**
 * V2Form
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * V2Form
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class V2Form
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'issuerName' => ['optional' => true] + GeneralNames::MAP,
            'baseCertificateID' => [
                'constant' => 0,
                'optional' => true,
            ] + IssuerSerial::MAP,
            'objectDigestInfo' => [
                'constant' => 1,
                'optional' => true,
            ] + ObjectDigestInfo::MAP,
        ],
    ];
}
<?php

/**
 * QcEuLimitValue
 *
 * From https://www.etsi.org/deliver/etsi_ts/101800_101899/101862/01.03.02_60/ts_101862v010302p.pdf
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

/**
 * QcEuLimitValue
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class QcEuLimitValue
{
    public const MAP = MonetaryValue::MAP;
}
<?php

/**
 * KeyTransRecipientInfo
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * KeyTransRecipientInfo
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class KeyTransRecipientInfo
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'version' => CMSVersion::MAP,
            'rid' => RecipientIdentifier::MAP,
            'keyEncryptionAlgorithm' => KeyEncryptionAlgorithmIdentifier::MAP,
            'encryptedKey' => EncryptedKey::MAP,
        ],
    ];
}
<?php

/**
 * KEKIdentifier
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * KEKIdentifier
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class KEKIdentifier
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'keyIdentifier' => ['type' => ASN1::TYPE_OCTET_STRING],
            'date' => [
                'type' => ASN1::TYPE_GENERALIZED_TIME,
                'optional' => true
            ],
            'other' => ['optional' => true] + OtherKeyAttribute::MAP,
        ],
    ];
}
<?php

/**
 * GeneralName
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * GeneralName
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class GeneralName
{
    public const MAP = [
        'type' => ASN1::TYPE_CHOICE,
        'children' => [
            'otherName' => [
                'constant' => 0,
                'optional' => true,
                'implicit' => true,
            ] + AnotherName::MAP,
            'rfc822Name' => [
                'type' => ASN1::TYPE_IA5_STRING,
                'constant' => 1,
                'optional' => true,
                'implicit' => true,
            ],
            'dNSName' => [
                'type' => ASN1::TYPE_IA5_STRING,
                'constant' => 2,
                'optional' => true,
                'implicit' => true,
            ],
            'x400Address' => [
                'constant' => 3,
                'optional' => true,
                'implicit' => true,
            ] + ORAddress::MAP,
            'directoryName' => [
                'constant' => 4,
                'optional' => true,
                'explicit' => true,
            ] + Name::MAP,
            'ediPartyName' => [
                'constant' => 5,
                'optional' => true,
                'implicit' => true,
            ] + EDIPartyName::MAP,
            'uniformResourceIdentifier' => [
                'type' => ASN1::TYPE_IA5_STRING,
                'constant' => 6,
                'optional' => true,
                'implicit' => true,
            ],
            'iPAddress' => [
                'type' => ASN1::TYPE_OCTET_STRING,
                'constant' => 7,
                'optional' => true,
                'implicit' => true,
                //'decoder' => '\\phpseclib4\\File\\ASN1::decodeIP',
            ],
            'registeredID' => [
                'type' => ASN1::TYPE_OBJECT_IDENTIFIER,
                'constant' => 8,
                'optional' => true,
                'implicit' => true,
            ],
        ],
    ];
}
<?php

/**
 * UniqueIdentifier
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * UniqueIdentifier
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class UniqueIdentifier
{
    public const MAP = ['type' => ASN1::TYPE_BIT_STRING];
}
<?php

/**
 * IssuerAltName
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

/**
 * IssuerAltName
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class IssuerAltName
{
    public const MAP = GeneralNames::MAP;
}
<?php

/**
 * DssSigValue
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * DssSigValue
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class DssSigValue
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'r' => ['type' => ASN1::TYPE_INTEGER],
            's' => ['type' => ASN1::TYPE_INTEGER],
        ],
    ];
}
<?php

/**
 * PolicyMappings
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * PolicyMappings
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class PolicyMappings
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'min' => 1,
        'max' => -1,
        'children' => [
            'type' => ASN1::TYPE_SEQUENCE,
            'children' => [
                'issuerDomainPolicy' => CertPolicyId::MAP,
                'subjectDomainPolicy' => CertPolicyId::MAP,
            ],
        ],
    ];
}
<?php

/**
 * RevocationInfoChoice
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * RevocationInfoChoice
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class RevocationInfoChoice
{
    public const MAP = [
        'type' => ASN1::TYPE_CHOICE,
        'children' => [
            'crl' => CertificateList::MAP,
            'other' => [
                'constant' => 1,
                'optional' => true,
                'implicit' => true,
            ] + OtherRevocationInfoFormat::MAP,
        ],
    ];
}
<?php

/**
 * SignedPublicKeyAndChallenge
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * SignedPublicKeyAndChallenge
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class SignedPublicKeyAndChallenge
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'publicKeyAndChallenge' => PublicKeyAndChallenge::MAP,
            'signatureAlgorithm' => AlgorithmIdentifier::MAP,
            'signature' => ['type' => ASN1::TYPE_BIT_STRING],
        ],
    ];
}
<?php

/**
 * PKCS9String
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * PKCS9String
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class PKCS9String
{
    public const MAP = [
        'type' => ASN1::TYPE_CHOICE,
        'children' => [
            'ia5String' => ['type' => ASN1::TYPE_IA5_STRING],
            'directoryString' => DirectoryString::MAP,
        ],
    ];
}
<?php

/**
 * DistributionPoint
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * DistributionPoint
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class DistributionPoint
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'distributionPoint' => [
                'constant' => 0,
                'optional' => true,
                'explicit' => true,
            ] + DistributionPointName::MAP,
            'reasons' => [
                'constant' => 1,
                'optional' => true,
                'implicit' => true,
            ] + ReasonFlags::MAP,
            'cRLIssuer' => [
                'constant' => 2,
                'optional' => true,
                'implicit' => true,
            ] + GeneralNames::MAP,
        ],
    ];
}
<?php

/**
 * SignerIdentifier
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * SignerIdentifier
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class SignerIdentifier
{
    public const MAP = [
        'type' => ASN1::TYPE_CHOICE,
        'children' => [
            'issuerAndSerialNumber' => IssuerAndSerialNumber::MAP,
            'subjectKeyIdentifier' => [
                'constant' => 0,
                'optional' => true,
                'explicit' => true,
            ] + SubjectKeyIdentifier::MAP,
        ],
    ];
}
<?php

/**
 * Hash
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * Hash
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class Hash
{
    public const MAP = ['type' => ASN1::TYPE_OCTET_STRING];
}
<?php

/**
 * KeyAgreeRecipientInfo
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * KeyAgreeRecipientInfo
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class KeyAgreeRecipientInfo
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'version' => CMSVersion::MAP,
            'originator' => [
                'constant' => 0,
                'optional' => true,
                'explicit' => true,
            ] + OriginatorIdentifierOrKey::MAP,
            'ukm' => [
                'constant' => 1,
                'optional' => true,
                'explicit' => true,
            ] + UserKeyingMaterial::MAP,
            'keyEncryptionAlgorithm' => KeyEncryptionAlgorithmIdentifier::MAP,
            'recipientEncryptedKeys' => RecipientEncryptedKeys::MAP,
        ],
    ];
}
<?php

/**
 * LogotypeExtn
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * LogotypeExtn
 *
 * issuerLogo and subjectLogo share the same definition, so only their context
 * tag tells them apart. This extension MUST NOT be marked critical.
 *
 * https://datatracker.ietf.org/doc/html/rfc9399#appendix-A.1
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class LogotypeExtn
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'communityLogos' => [
                'type' => ASN1::TYPE_SEQUENCE,
                'min' => 0,
                'max' => -1,
                'constant' => 0,
                'optional' => true,
                'explicit' => true,
                'children' => LogotypeInfo::MAP,
            ],
            'issuerLogo' => [
                'constant' => 1,
                'optional' => true,
                'explicit' => true,
            ] + LogotypeInfo::MAP,
            'subjectLogo' => [
                'constant' => 2,
                'optional' => true,
                'explicit' => true,
            ] + LogotypeInfo::MAP,
            'otherLogos' => [
                'type' => ASN1::TYPE_SEQUENCE,
                'min' => 0,
                'max' => -1,
                'constant' => 3,
                'optional' => true,
                'explicit' => true,
                'children' => OtherLogotypeInfo::MAP,
            ],
        ],
    ];
}
<?php

/**
 * Curve
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * Curve
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class Curve
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'a' => FieldElement::MAP,
            'b' => FieldElement::MAP,
            'seed' => [
                'type' => ASN1::TYPE_BIT_STRING,
                'optional' => true,
            ],
        ],
    ];
}
<?php

/**
 * EncryptedPrivateKeyInfo
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * EncryptedPrivateKeyInfo
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class EncryptedPrivateKeyInfo
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'encryptionAlgorithm' => AlgorithmIdentifier::MAP,
            'encryptedData' => EncryptedData::MAP,
        ],
    ];
}
<?php

/**
 * ExtendedCertificate
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * ExtendedCertificate
 *
 * mapping is from <ftp://ftp.rsasecurity.com/pub/pkcs/ascii/pkcs-6.asc>
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class ExtendedCertificate
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'extendedCertificateInfo' => ExtendedCertificateInfo::MAP,
            'signatureAlgorithm' => AlgorithmIdentifier::MAP,
            'attributes' => ['type' => ASN1::TYPE_BIT_STRING],
        ],
    ];
}
<?php

/**
 * Name
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * Name
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class Name
{
    public const MAP = [
        'type' => ASN1::TYPE_CHOICE,
        'children' => [
            'rdnSequence' => RDNSequence::MAP,
        ],
    ];
}
<?php

/**
 * DirectoryString
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * DirectoryString
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class DirectoryString
{
    public const MAP = [
        'type' => ASN1::TYPE_CHOICE,
        'children' => [
            'teletexString' => ['type' => ASN1::TYPE_TELETEX_STRING],
            'printableString' => ['type' => ASN1::TYPE_PRINTABLE_STRING],
            'universalString' => ['type' => ASN1::TYPE_UNIVERSAL_STRING],
            'utf8String' => ['type' => ASN1::TYPE_UTF8_STRING],
            'bmpString' => ['type' => ASN1::TYPE_BMP_STRING],
        ],
    ];
}
<?php

/**
 * OriginatorInfo
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * OriginatorInfo
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class OriginatorInfo
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'certs' => [
                'constant' => 0,
                'optional' => true,
                'implicit' => true,
            ] + CertificateSet::MAP,
            'crls' => [
                'constant' => 0,
                'optional' => true,
                'implicit' => true,
            ] + RevocationInfoChoices::MAP,
        ],
    ];
}
<?php

/**
 * CountryName
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * CountryName
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class CountryName
{
    public const MAP = [
        'type' => ASN1::TYPE_CHOICE,
        // if class isn't present it's assumed to be \phpseclib4\File\ASN1::CLASS_UNIVERSAL or
        // (if constant is present) \phpseclib4\File\ASN1::CLASS_CONTEXT_SPECIFIC
        'class' => ASN1::CLASS_APPLICATION,
        'cast' => 1,
        'children' => [
            'x121-dcc-code' => ['type' => ASN1::TYPE_NUMERIC_STRING],
            'iso-3166-alpha2-code' => ['type' => ASN1::TYPE_PRINTABLE_STRING],
        ],
    ];
}
<?php

/**
 * KeyEncryptionAlgorithmIdentifier
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

/**
 * KeyEncryptionAlgorithmIdentifier
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class KeyEncryptionAlgorithmIdentifier extends AlgorithmIdentifier
{
}
<?php

/**
 * CompressionAlgorithmIdentifier
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

/**
 * CompressionAlgorithmIdentifier
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class CompressionAlgorithmIdentifier extends AlgorithmIdentifier
{
}
<?php

/**
 * OrganizationName
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * OrganizationName
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class OrganizationName
{
    public const MAP = ['type' => ASN1::TYPE_PRINTABLE_STRING];
}
<?php

/**
 * Digest
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * Digest
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class Digest
{
    public const MAP = ['type' => ASN1::TYPE_OCTET_STRING];
}
<?php

/**
 * KeyIdentifier
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * KeyIdentifier
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class KeyIdentifier
{
    public const MAP = ['type' => ASN1::TYPE_OCTET_STRING];
}
<?php

/**
 * LogotypeData
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * LogotypeData
 *
 * https://datatracker.ietf.org/doc/html/rfc9399#appendix-A.1
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class LogotypeData
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'image' => [
                'type' => ASN1::TYPE_SEQUENCE,
                'min' => 0,
                'max' => -1,
                'optional' => true,
                'children' => LogotypeImage::MAP,
            ],
            'audio' => [
                'type' => ASN1::TYPE_SEQUENCE,
                'min' => 0,
                'max' => -1,
                'constant' => 1,
                'optional' => true,
                'implicit' => true,
                'children' => LogotypeAudio::MAP,
            ],
        ],
    ];
}
<?php

/**
 * NoticeReference
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * NoticeReference
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class NoticeReference
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'organization' => DisplayText::MAP,
            'noticeNumbers' => [
                'type' => ASN1::TYPE_SEQUENCE,
                'min' => 1,
                'max' => 200,
                'children' => ['type' => ASN1::TYPE_INTEGER],
            ],
        ],
    ];
}
<?php

/**
 * LogotypeImage
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * LogotypeImage
 *
 * https://datatracker.ietf.org/doc/html/rfc9399#appendix-A.1
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class LogotypeImage
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'imageDetails' => LogotypeDetails::MAP,
            'imageInfo' => ['optional' => true] + LogotypeImageInfo::MAP,
        ],
    ];
}
<?php

/**
 * PostalAddress
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * PostalAddress
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class PostalAddress
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'optional' => true,
        'min' => 1,
        'max' => -1,
        'children' => DirectoryString::MAP,
    ];
}
<?php

/**
 * PrivateDomainName
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * PrivateDomainName
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class PrivateDomainName
{
    public const MAP = [
        'type' => ASN1::TYPE_CHOICE,
        'children' => [
            'numeric' => ['type' => ASN1::TYPE_NUMERIC_STRING],
            'printable' => ['type' => ASN1::TYPE_PRINTABLE_STRING],
        ],
    ];
}
<?php

/**
 * SMIMECapability
 *
 * This is an exact copy of AlgorithmIdentifier, however, https://datatracker.ietf.org/doc/html/rfc2985#section-5.6
 * opts not to use AlgorithmIdentifier as the name so we shant, either
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

/**
 * SMIMECapability
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class SMIMECapability extends AlgorithmIdentifier
{
}
<?php

/**
 * CMSVersion
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * CMSVersion
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class CMSVersion
{
    public const MAP = [
        'type' => ASN1::TYPE_INTEGER,
        'mapping' => ['v0', 'v1', 'v2', 'v3', 'v4', 'v5'],
    ];
}
<?php

/**
 * PublicKeyAndChallenge
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * PublicKeyAndChallenge
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class PublicKeyAndChallenge
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'spki' => SubjectPublicKeyInfo::MAP,
            'challenge' => ['type' => ASN1::TYPE_IA5_STRING],
        ],
    ];
}
<?php

/**
 * AttCertVersion
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * AttCertVersion
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class AttCertVersionV1
{
    public const MAP = [
        'type' => ASN1::TYPE_INTEGER,
        'mapping' => ['v1'],
    ];
}
<?php

/**
 * RSAES_OAEP_params
 *
 * As defined in https://tools.ietf.org/html/rfc4055#section-3.1
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2019-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * RSAES_OAEP_params
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class RSAES_OAEP_params
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'hashAlgorithm' => [
                'constant' => 0,
                'optional' => true,
                'explicit' => true,
                //'default'  => 'sha1'
            ] + HashAlgorithm::MAP,
            'maskGenAlgorithm' => [
                'constant' => 1,
                'optional' => true,
                'explicit' => true,
                //'default'  => 'mgf1SHA1'
            ] + MaskGenAlgorithm::MAP,
            'pSourceAlgorithm' => [
                'constant' => 2,
                'optional' => true,
                'explicit' => true,
                //'default' => 'pSpecifiedEmpty,
            ] + PSourceAlgorithm::MAP,
        ],
    ];
}
<?php

/**
 * ExtKeyUsageSyntax
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * ExtKeyUsageSyntax
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class ExtKeyUsageSyntax
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'min' => 1,
        'max' => -1,
        'children' => KeyPurposeId::MAP,
    ];
}
<?php

/**
 * LogotypeInfo
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * LogotypeInfo
 *
 * direct embeds the logotype data, indirect references it by hash and URI.
 *
 * https://datatracker.ietf.org/doc/html/rfc9399#appendix-A.1
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class LogotypeInfo
{
    public const MAP = [
        'type' => ASN1::TYPE_CHOICE,
        'children' => [
            'direct' => [
                'constant' => 0,
                'optional' => true,
                'implicit' => true,
            ] + LogotypeData::MAP,
            'indirect' => [
                'constant' => 1,
                'optional' => true,
                'implicit' => true,
            ] + LogotypeReference::MAP,
        ],
    ];
}
<?php

/**
 * SubjectInfoAccessSyntax
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2019-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * SubjectInfoAccessSyntax
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class SubjectInfoAccessSyntax
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'min' => 1,
        'max' => -1,
        'children' => AccessDescription::MAP,
    ];
}
<?php

/**
 * ExtendedCertificateInfo
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * ExtendedCertificateInfo
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class ExtendedCertificateInfo
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'version' => ['type' => ASN1::TYPE_INTEGER],
            'certificate' => Certificate::MAP,
            'attributes' => Attributes::MAP,
        ],
    ];
}
<?php

/**
 * SigningCertificateV2
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * SigningCertificateV2
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class SigningCertificateV2
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'certs' => [
                'type' => ASN1::TYPE_SEQUENCE,
                'min' => 1,
                'max' => -1,
                'children' => ESSCertIDv2::MAP,
            ],
            'policies' => [
                'type' => ASN1::TYPE_SEQUENCE,
                'min' => 1,
                'max' => -1,
                'children' => PolicyInformation::MAP,
                'optional' => true,
            ],
        ],
    ];
}
<?php

/**
 * NetworkAddress
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * NetworkAddress
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class NetworkAddress
{
    public const MAP = ['type' => ASN1::TYPE_NUMERIC_STRING];
}
<?php

/**
 * EncryptedContent
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * EncryptedContent
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class EncryptedContent
{
    public const MAP = ['type' => ASN1::TYPE_OCTET_STRING];
}
<?php

/**
 * AttributeCertificateInfo
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * AttributeCertificateInfo
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class AttributeCertificateInfo
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'version' => [
                'optional' => true,
                'default' => 'v2',
            ] + AttCertVersion::MAP,
            'holder' => Holder::MAP,
            'issuer' => AttCertIssuer::MAP,
            'signature' => AlgorithmIdentifier::MAP,
            'serialNumber' => CertificateSerialNumber::MAP,
            'attrCertValidityPeriod' => AttCertValidityPeriod::MAP,
            'attributes' => [
                'type' => ASN1::TYPE_SEQUENCE,
                'min' => 0,
                'max' => -1,
                'children' => Attribute::MAP,
            ],
            'issuerUniqueID' => ['optional' => true] + UniqueIdentifier::MAP,
            'extensions' => ['optional' => true] + Extensions::MAP,
        ],
    ];
}
<?php

/**
 * GeneralSubtrees
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * GeneralSubtrees
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class GeneralSubtrees
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'min' => 1,
        'max' => -1,
        'children' => GeneralSubtree::MAP,
    ];
}
<?php

/**
 * UserKeyingMaterial
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * UserKeyingMaterial
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class UserKeyingMaterial
{
    public const MAP = ['type' => ASN1::TYPE_OCTET_STRING];
}
<?php

/**
 * PBKDF2params
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * PBKDF2params
 *
 * from https://tools.ietf.org/html/rfc2898#appendix-A.3
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class PBKDF2params
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            // technically, this is a CHOICE in RFC2898 but the other "choice" is, currently, more of a placeholder
            // in the RFC
            'salt' => ['type' => ASN1::TYPE_OCTET_STRING],
            'iterationCount' => ['type' => ASN1::TYPE_INTEGER],
            'keyLength' => [
                'type' => ASN1::TYPE_INTEGER,
                'optional' => true,
            ],
            'prf' => AlgorithmIdentifier::MAP + ['optional' => true],
        ],
    ];
}
<?php

/**
 * AdministrationDomainName
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * AdministrationDomainName
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class AdministrationDomainName
{
    public const MAP = [
        'type' => ASN1::TYPE_CHOICE,
        // if class isn't present it's assumed to be \phpseclib4\File\ASN1::CLASS_UNIVERSAL or
        // (if constant is present) \phpseclib4\File\ASN1::CLASS_CONTEXT_SPECIFIC
        'class' => ASN1::CLASS_APPLICATION,
        'cast' => 2,
        'children' => [
            'numeric' => ['type' => ASN1::TYPE_NUMERIC_STRING],
            'printable' => ['type' => ASN1::TYPE_PRINTABLE_STRING],
        ],
    ];
}
<?php

/**
 * OriginatorIdentifierOrKey
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * OriginatorIdentifierOrKey
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class OriginatorIdentifierOrKey
{
    public const MAP = [
        'type' => ASN1::TYPE_CHOICE,
        'children' => [
            'issuerAndSerialNumber' => IssuerAndSerialNumber::MAP,
            'subjectKeyIdentifier' => [
                'constant' => 0,
                'optional' => true,
                'implicit' => true,
            ] + SubjectKeyIdentifier::MAP,
            'originatorKey' => [
                'constant' => 1,
                'optional' => true,
                'implicit' => true,
            ] + OriginatorPublicKey::MAP,
        ],
    ];
}
<?php

/**
 * DisplayText
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * DisplayText
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class DisplayText
{
    public const MAP = [
        'type' => ASN1::TYPE_CHOICE,
        'children' => [
            'ia5String' => ['type' => ASN1::TYPE_IA5_STRING],
            'visibleString' => ['type' => ASN1::TYPE_VISIBLE_STRING],
            'bmpString' => ['type' => ASN1::TYPE_BMP_STRING],
            'utf8String' => ['type' => ASN1::TYPE_UTF8_STRING],
        ],
    ];
}
<?php

/**
 * AttributeType
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * AttributeType
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class AttributeType
{
    public const MAP = ['type' => ASN1::TYPE_OBJECT_IDENTIFIER];
}
<?php

/**
 * DSAPrivateKey
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * DSAPrivateKey
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class DSAPrivateKey
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'version' => ['type' => ASN1::TYPE_INTEGER],
            'p' => ['type' => ASN1::TYPE_INTEGER],
            'q' => ['type' => ASN1::TYPE_INTEGER],
            'g' => ['type' => ASN1::TYPE_INTEGER],
            'y' => ['type' => ASN1::TYPE_INTEGER],
            'x' => ['type' => ASN1::TYPE_INTEGER],
        ],
    ];
}
<?php

/**
 * OtherLogotypeInfo
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * OtherLogotypeInfo
 *
 * logotypeType is one of id-logo-loyalty, id-logo-background or
 * id-logo-certImage.
 *
 * https://datatracker.ietf.org/doc/html/rfc9399#appendix-A.1
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class OtherLogotypeInfo
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'logotypeType' => ['type' => ASN1::TYPE_OBJECT_IDENTIFIER],
            'info' => LogotypeInfo::MAP,
        ],
    ];
}
<?php

/**
 * PersonalName
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * PersonalName
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class PersonalName
{
    public const MAP = [
        'type' => ASN1::TYPE_SET,
        'children' => [
            'surname' => [
                'type' => ASN1::TYPE_PRINTABLE_STRING,
                'constant' => 0,
                'optional' => true,
                'implicit' => true,
            ],
            'given-name' => [
                'type' => ASN1::TYPE_PRINTABLE_STRING,
                'constant' => 1,
                'optional' => true,
                'implicit' => true,
            ],
            'initials' => [
                'type' => ASN1::TYPE_PRINTABLE_STRING,
                'constant' => 2,
                'optional' => true,
                'implicit' => true,
            ],
            'generation-qualifier' => [
                'type' => ASN1::TYPE_PRINTABLE_STRING,
                'constant' => 3,
                'optional' => true,
                'implicit' => true,
            ],
        ],
    ];
}
<?php

/**
 * RSAPrivateKey
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * RSAPrivateKey
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class RSAPrivateKey
{
    // version must be multi if otherPrimeInfos present
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'version' => [
                'type' => ASN1::TYPE_INTEGER,
                'mapping' => ['two-prime', 'multi'],
            ],
            'modulus' => ['type' => ASN1::TYPE_INTEGER],         // n
            'publicExponent' => ['type' => ASN1::TYPE_INTEGER],  // e
            'privateExponent' => ['type' => ASN1::TYPE_INTEGER], // d
            'prime1' => ['type' => ASN1::TYPE_INTEGER],          // p
            'prime2' => ['type' => ASN1::TYPE_INTEGER],          // q
            'exponent1' => ['type' => ASN1::TYPE_INTEGER],       // d mod (p-1)
            'exponent2' => ['type' => ASN1::TYPE_INTEGER],       // d mod (q-1)
            'coefficient' => ['type' => ASN1::TYPE_INTEGER],     // (inverse of q) mod p
            'otherPrimeInfos' => OtherPrimeInfos::MAP + ['optional' => true],
        ],
    ];
}
<?php

/**
 * PublicKey
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * PublicKey
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class PublicKey
{
    public const MAP = ['type' => ASN1::TYPE_BIT_STRING];
}
<?php

/**
 * OneAsymmetricKey
 *
 * See https://tools.ietf.org/html/rfc5958
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * OneAsymmetricKey
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class OneAsymmetricKey
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'version' => [
                'type' => ASN1::TYPE_INTEGER,
                'mapping' => ['v1', 'v2'],
            ],
            'privateKeyAlgorithm' => AlgorithmIdentifier::MAP,
            'privateKey' => PrivateKey::MAP,
            'attributes' => [
                'constant' => 0,
                'optional' => true,
                'implicit' => true,
            ] + Attributes::MAP,
            'publicKey' => [
                'constant' => 1,
                'optional' => true,
                'implicit' => true,
            ] + PublicKey::MAP,
        ],
    ];
}
<?php

/**
 * BuiltInStandardAttributes
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * BuiltInStandardAttributes
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class BuiltInStandardAttributes
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'country-name' => ['optional' => true] + CountryName::MAP,
            'administration-domain-name' => ['optional' => true] + AdministrationDomainName::MAP,
            'network-address' => [
                'constant' => 0,
                'optional' => true,
                'implicit' => true,
            ] + NetworkAddress::MAP,
            'terminal-identifier' => [
                'constant' => 1,
                'optional' => true,
                'implicit' => true,
            ] + TerminalIdentifier::MAP,
            'private-domain-name' => [
                'constant' => 2,
                'optional' => true,
                'explicit' => true,
            ] + PrivateDomainName::MAP,
            'organization-name' => [
                'constant' => 3,
                'optional' => true,
                'implicit' => true,
            ] + OrganizationName::MAP,
            'numeric-user-identifier' => [
                'constant' => 4,
                'optional' => true,
                'implicit' => true,
            ] + NumericUserIdentifier::MAP,
            'personal-name' => [
                'constant' => 5,
                'optional' => true,
                'implicit' => true,
            ] + PersonalName::MAP,
            'organizational-unit-names' => [
                'constant' => 6,
                'optional' => true,
                'implicit' => true,
            ] + OrganizationalUnitNames::MAP,
        ],
    ];
}
<?php

/**
 * Pentanomial
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * Pentanomial
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class Pentanomial
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'k1' => ['type' => ASN1::TYPE_INTEGER], // k1 > 0
            'k2' => ['type' => ASN1::TYPE_INTEGER], // k2 > k1
            'k3' => ['type' => ASN1::TYPE_INTEGER], // k3 > h2
        ],
    ];
}
<?php

/**
 * NameConstraints
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * NameConstraints
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class NameConstraints
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'permittedSubtrees' => [
                'constant' => 0,
                'optional' => true,
                'implicit' => true,
            ] + GeneralSubtrees::MAP,
            'excludedSubtrees' => [
                'constant' => 1,
                'optional' => true,
                'implicit' => true,
            ] + GeneralSubtrees::MAP,
        ],
    ];
}
<?php

/**
 * CertificateIssuer
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

/**
 * CertificateIssuer
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class CertificateIssuer
{
    public const MAP = GeneralNames::MAP;
}
<?php

/**
 * ECPoint
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * ECPoint
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class ECPoint
{
    public const MAP = ['type' => ASN1::TYPE_OCTET_STRING];
}
<?php

/**
 * ECCCMSSharedInfo
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * ECCCMSSharedInfo
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class ECCCMSSharedInfo
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'keyInfo' => AlgorithmIdentifier::MAP,
            'entityUInfo' => [
                'type' => ASN1::TYPE_OCTET_STRING,
                'constant' => 0,
                'optional' => true,
                'explicit' => true,
            ],
            'suppPubInfo' => [
                'type' => ASN1::TYPE_OCTET_STRING,
                'constant' => 2,
                'optional' => true,
                'explicit' => true,
            ],
        ],
    ];
}
<?php

/**
 * RevokedCertificate
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * RevokedCertificate
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class RevokedCertificate
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'userCertificate' => CertificateSerialNumber::MAP,
            'revocationDate' => Time::MAP,
            'crlEntryExtensions' => [
                'optional' => true,
            ] + Extensions::MAP,
        ],
    ];
}
<?php

/**
 * TimeStampToken
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * TimeStampToken
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class TimeStampToken
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'contentType' => ContentType::MAP,
            'content' => [
                'type' => ASN1::TYPE_ANY,
                'constant' => 0,
                'optional' => true,
                'explicit' => true,
            ],
        ],
    ];
}
<?php

/**
 * SignerInfo
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * SignerInfo
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class SignerInfos
{
    public const MAP = [
        'type' => ASN1::TYPE_SET,
        'min' => 1,
        'max' => -1,
        'children' => SignerInfo::MAP,
    ];
}
<?php

/**
 * ExtensionAttribute
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * ExtensionAttribute
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class ExtensionAttribute
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'extension-attribute-type' => [
                'type' => ASN1::TYPE_PRINTABLE_STRING,
                'constant' => 0,
                'optional' => true,
                'implicit' => true,
            ],
            'extension-attribute-value' => [
                'type' => ASN1::TYPE_ANY,
                'constant' => 1,
                'optional' => true,
                'explicit' => true,
            ],
        ],
    ];
}
<?php

/**
 * DigestAlgorithmIdentifiers
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * DigestAlgorithmIdentifiers
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class DigestAlgorithmIdentifiers extends AlgorithmIdentifier
{
    public const MAP = [
        'type' => ASN1::TYPE_SET,
        'min' => 1,
        'max' => -1,
        'children' => DigestAlgorithmIdentifier::MAP
    ];
}
<?php

/**
 * AttributeCertificateInfoV1
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2025-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * AttributeCertificateInfoV1
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class AttributeCertificateInfoV1
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'version' => [
                'optional' => true,
                'default' => 'v1',
            ] + AttCertVersionV1::MAP,
            'subject' => [
                'type' => ASN1::TYPE_CHOICE,
                'children' => [
                    'baseCertificateID' => [
                        'constant' => 0,
                        'optional' => true,
                    ] + IssuerSerial::MAP,
                    'subjectName' => [
                        'constant' => 1,
                        'optional' => true,
                    ] + GeneralNames::MAP,
                ],
            ],
            'issuer' => GeneralNames::MAP,
            'signature' => AlgorithmIdentifier::MAP,
            'serialNumber' => CertificateSerialNumber::MAP,
            'attCertValidityPeriod' => AttCertValidityPeriod::MAP,
            'attributes' => [
                'type' => ASN1::TYPE_SEQUENCE,
                'min' => 0,
                'max' => -1,
                'children' => Attribute::MAP,
            ],
            'issuerUniqueID' => ['optional' => true] + UniqueIdentifier::MAP,
            'extensions' => ['optional' => true] + Extensions::MAP,
        ],
    ];
}
<?php

/**
 * FieldElement
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * FieldElement
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class FieldElement
{
    public const MAP = ['type' => ASN1::TYPE_OCTET_STRING];
}
<?php

/**
 * LogotypeImageInfo
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * LogotypeImageInfo
 *
 * fileSize is in octets, xSize and ySize are in pixels; 0 means unspecified.
 * language, when present, uses the syntax of RFC 5646.
 *
 * https://datatracker.ietf.org/doc/html/rfc9399#appendix-A.1
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class LogotypeImageInfo
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'type' => [
                'constant' => 0,
                'optional' => true,
                'implicit' => true,
                'default' => 'color',
            ] + LogotypeImageType::MAP,
            'fileSize' => ['type' => ASN1::TYPE_INTEGER],
            'xSize' => ['type' => ASN1::TYPE_INTEGER],
            'ySize' => ['type' => ASN1::TYPE_INTEGER],
            'resolution' => ['optional' => true] + LogotypeImageResolution::MAP,
            'language' => [
                'type' => ASN1::TYPE_IA5_STRING,
                'constant' => 4,
                'optional' => true,
                'implicit' => true,
            ],
        ],
    ];
}
<?php

/**
 * AnotherName
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * AnotherName
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class AnotherName
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'type-id' => ['type' => ASN1::TYPE_OBJECT_IDENTIFIER],
            'value' => [
                'type' => ASN1::TYPE_ANY,
                'constant' => 0,
                'optional' => true,
                'explicit' => true,
            ],
        ],
    ];
}
<?php

/**
 * RSASSA_PSS_params
 *
 * As defined in https://tools.ietf.org/html/rfc4055#section-3.1
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2019-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * RSASSA_PSS_params
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class RSASSA_PSS_params
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'hashAlgorithm' => [
                'constant' => 0,
                'optional' => true,
                'explicit' => true,
                //'default'  => 'sha1Identifier'
            ] + HashAlgorithm::MAP,
            'maskGenAlgorithm' => [
                'constant' => 1,
                'optional' => true,
                'explicit' => true,
                //'default'  => 'mgf1SHA1Identifier'
            ] + MaskGenAlgorithm::MAP,
            'saltLength' => [
                'type' => ASN1::TYPE_INTEGER,
                'constant' => 2,
                'optional' => true,
                'explicit' => true,
                'default' => 20,
            ],
            'trailerField' => [
                'type' => ASN1::TYPE_INTEGER,
                'constant' => 3,
                'optional' => true,
                'explicit' => true,
                'default' => 1,
            ],
        ],
    ];
}
<?php

/**
 * HashAglorithm
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

/**
 * HashAglorithm
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class HashAlgorithm
{
    public const MAP = AlgorithmIdentifier::MAP;
}
<?php

/**
 * UnauthAttributes
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * UnauthAttributes
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class UnauthAttributes
{
    public const MAP = [
        'type' => ASN1::TYPE_SET,
        'min' => 1,
        'max' => -1,
        'children' => Attribute::MAP,
    ];
}
<?php

/**
 * BaseDistance
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * BaseDistance
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class BaseDistance
{
    public const MAP = ['type' => ASN1::TYPE_INTEGER];
}
<?php

/**
 * TSTInfo
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * TSTInfo
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class TSTInfo
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'version' => [
                'type' => ASN1::TYPE_INTEGER,
                'mapping' => ['v1'],
            ],
            'policy' => TSAPolicyId::MAP,
            'messageImprint' => MessageImprint::MAP,
            'serialNumber' => ['type' => ASN1::TYPE_INTEGER],
            'genTime' => ['type' => ASN1::TYPE_GENERALIZED_TIME],
            'accuracy' => ['optional' => true] + Accuracy::MAP,
            'ordering' => [
                'type' => ASN1::TYPE_BOOLEAN,
                'optional' => true,
                'default' => false,
            ],
            'nonce' => [
                'type' => ASN1::TYPE_INTEGER,
                'optional' => true,
                'default' => -1,
            ],
            'tsa' => [
                'constant' => 0,
                'optional' => true,
                'explicit' => true,
            ] + GeneralName::MAP,
            'extensions' => [
                'constant' => 1,
                'optional' => true,
                'implicit' => true,
            ] + Extensions::MAP,
        ],
    ];
}
<?php

/**
 * netscape_ca_policy_url
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * netscape_ca_policy_url
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class netscape_ca_policy_url
{
    public const MAP = ['type' => ASN1::TYPE_IA5_STRING];
}
<?php

/**
 * RecipientEncryptedKeys
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * RecipientEncryptedKeys
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class RecipientEncryptedKeys
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'min' => 1,
        'max' => -1,
        'children' => RecipientEncryptedKey::MAP,
    ];
}
<?php

/**
 * RecipientKeyIdentifier
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * RecipientKeyIdentifier
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class RecipientKeyIdentifier
{
    public const MAP = [
        'type' => ASN1::TYPE_CHOICE,
        'children' => [
            'subjectKeyIdentifier' => SubjectKeyIdentifier::MAP,
            'date' => [
                'type' => ASN1::TYPE_GENERALIZED_TIME,
                'optional' => true
            ],
            'other' => ['optional' => true] + OtherKeyAttribute::MAP,
        ],
    ];
}
<?php

/**
 * AttributeTypeAndValue
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * AttributeTypeAndValue
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class AttributeTypeAndValue
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'type' => AttributeType::MAP,
            'value' => AttributeValue::MAP,
        ],
    ];
}
<?php

/**
 * MessageAuthenticationCode
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * MessageAuthenticationCode
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class MessageAuthenticationCode
{
    public const MAP = ['type' => ASN1::TYPE_OCTET_STRING];
}
<?php

/**
 * LogotypeImageResolution
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * LogotypeImageResolution
 *
 * numBits is the resolution in bits, tableSize the number of colors or grey tones.
 *
 * https://datatracker.ietf.org/doc/html/rfc9399#appendix-A.1
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class LogotypeImageResolution
{
    public const MAP = [
        'type' => ASN1::TYPE_CHOICE,
        'children' => [
            'numBits' => [
                'type' => ASN1::TYPE_INTEGER,
                'constant' => 1,
                'optional' => true,
                'implicit' => true,
            ],
            'tableSize' => [
                'type' => ASN1::TYPE_INTEGER,
                'constant' => 2,
                'optional' => true,
                'implicit' => true,
            ],
        ],
    ];
}
<?php

/**
 * CompressedData
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * CompressedData
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class CompressedData
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'version' => CMSVersion::MAP,
            'compressionAlgorithm' => CompressionAlgorithmIdentifier::MAP,
            'encapContentInfo' => EncapsulatedContentInfo::MAP,
        ],
    ];
}
<?php

/**
 * DigestAlgorithmIdentifier
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

/**
 * DigestAlgorithmIdentifier
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class DigestAlgorithmIdentifier extends AlgorithmIdentifier
{
}
<?php

/**
 * HoldInstructionCode
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * HoldInstructionCode
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class HoldInstructionCode
{
    public const MAP = ['type' => ASN1::TYPE_OBJECT_IDENTIFIER];
}
<?php

/**
 * RevocationInfoChoices
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * RevocationInfoChoices
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class RevocationInfoChoices
{
    public const MAP = [
        'type' => ASN1::TYPE_SET,
        'min' => 1,
        'max' => -1,
        'children' => RevocationInfoChoice::MAP,
    ];
}
<?php

/**
 * KeyDerivationAlgorithmIdentifier
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

/**
 * KeyDerivationAlgorithmIdentifier
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class KeyDerivationAlgorithmIdentifier extends AlgorithmIdentifier
{
}
<?php

/**
 * PolicyInformation
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * PolicyInformation
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class PolicyInformation
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'policyIdentifier' => CertPolicyId::MAP,
            'policyQualifiers' => [
                'type' => ASN1::TYPE_SEQUENCE,
                'min' => 0,
                'max' => -1,
                'optional' => true,
                'children' => PolicyQualifierInfo::MAP,
            ],
        ],
    ];
}
<?php

/**
 * OtherPrimeInfos
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * OtherPrimeInfos
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class OtherPrimeInfos
{
    // version must be multi if otherPrimeInfos present
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'min' => 1,
        'max' => -1,
        'children' => OtherPrimeInfo::MAP,
    ];
}
<?php

/**
 * ReasonFlags
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * ReasonFlags
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class ReasonFlags
{
    public const MAP = [
        'type' => ASN1::TYPE_BIT_STRING,
        'mapping' => [
            'unused',
            'keyCompromise',
            'cACompromise',
            'affiliationChanged',
            'superseded',
            'cessationOfOperation',
            'certificateHold',
            'privilegeWithdrawn',
            'aACompromise',
        ],
    ];
}
<?php

/**
 * RecipientInfos
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * RecipientInfos
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class RecipientInfos
{
    public const MAP = [
        'type' => ASN1::TYPE_SET,
        'min' => 1,
        'max' => -1,
        'children' => RecipientInfo::MAP,
    ];
}
<?php

/**
 * AlgorithmIdentifier
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * AlgorithmIdentifier
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class AlgorithmIdentifier
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'algorithm' => ['type' => ASN1::TYPE_OBJECT_IDENTIFIER],
            'parameters' => [
                'type' => ASN1::TYPE_ANY,
                'optional' => true,
                'implicit' => true,
            ],
        ],
    ];
}
<?php

/**
 * ORAddress
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * ORAddress
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class ORAddress
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'built-in-standard-attributes' => BuiltInStandardAttributes::MAP,
            'built-in-domain-defined-attributes' => ['optional' => true] + BuiltInDomainDefinedAttributes::MAP,
            'extension-attributes' => ['optional' => true] + ExtensionAttributes::MAP,
        ],
    ];
}
<?php

/**
 * OtherKeyAttribute
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * OtherKeyAttribute
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class OtherKeyAttribute
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'keyAttrId' => ['type' => ASN1::TYPE_OBJECT_IDENTIFIER],
            'keyAttr' => [
                'type' => ASN1::TYPE_ANY,
                'optional' => true,
            ],
        ],
    ];
}
<?php

/**
 * Extension
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * Extension
 *
 * A certificate using system MUST reject the certificate if it encounters
 * a critical extension it does not recognize; however, a non-critical
 * extension may be ignored if it is not recognized.
 *
 * http://tools.ietf.org/html/rfc5280#section-4.2
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class Extension
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'extnId' => ['type' => ASN1::TYPE_OBJECT_IDENTIFIER],
            'critical' => [
                'type' => ASN1::TYPE_BOOLEAN,
                'optional' => true,
                'default' => false,
            ],
            'extnValue' => ['type' => ASN1::TYPE_OCTET_STRING],
        ],
    ];
}
<?php

/**
 * PasswordRecipientInfo
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * PasswordRecipientInfo
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class PasswordRecipientInfo
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'version' => CMSVersion::MAP,
            'keyDerivationAlgorithm' => [
                'constant' => 0,
                'optional' => true,
                'implicit' => true,
            ] + KeyDerivationAlgorithmIdentifier::MAP,
            'keyEncryptionAlgorithm' => KeyEncryptionAlgorithmIdentifier::MAP,
            'encryptedKey' => EncryptedKey::MAP,
        ],
    ];
}
<?php

/**
 * DSAParams
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * DSAParams
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class DSAParams
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'p' => ['type' => ASN1::TYPE_INTEGER],
            'q' => ['type' => ASN1::TYPE_INTEGER],
            'g' => ['type' => ASN1::TYPE_INTEGER],
        ],
    ];
}
<?php

/**
 * OriginatorPublicKey
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * OriginatorPublicKey
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class OriginatorPublicKey
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'algorithm' => AlgorithmIdentifier::MAP,
            'publicKey' => ['type' => ASN1::TYPE_BIT_STRING],
        ],
    ];
}
<?php

/**
 * LogotypeDetails
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * LogotypeDetails
 *
 * https://datatracker.ietf.org/doc/html/rfc9399#appendix-A.1
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class LogotypeDetails
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'mediaType' => ['type' => ASN1::TYPE_IA5_STRING],
            'logotypeHash' => [
                'type' => ASN1::TYPE_SEQUENCE,
                'min' => 1,
                'max' => -1,
                'children' => HashAlgAndValue::MAP,
            ],
            'logotypeURI' => [
                'type' => ASN1::TYPE_SEQUENCE,
                'min' => 1,
                'max' => -1,
                'children' => ['type' => ASN1::TYPE_IA5_STRING],
            ],
        ],
    ];
}
<?php

/**
 * HashAlgAndValue
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * HashAlgAndValue
 *
 * https://datatracker.ietf.org/doc/html/rfc9399#appendix-A.1
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class HashAlgAndValue
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'hashAlg' => AlgorithmIdentifier::MAP,
            'hashValue' => ['type' => ASN1::TYPE_OCTET_STRING],
        ],
    ];
}
<?php

/**
 * IssuerAndSerialNumber
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * IssuerAndSerialNumber
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class IssuerAndSerialNumber
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'issuer' => Name::MAP,
            'serialNumber' => CertificateSerialNumber::MAP,
        ],
    ];
}
<?php

/**
 * TBSCertList
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * TBSCertList
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class TBSCertList
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'version' => [
                'type' => ASN1::TYPE_INTEGER,
                'mapping' => ['v1', 'v2'],
                'optional' => true,
                'implicit' => true,
                'default' => 'v1',
            ],
            'signature' => AlgorithmIdentifier::MAP,
            'issuer' => Name::MAP,
            'thisUpdate' => Time::MAP,
            'nextUpdate' => [
                'optional' => true,
                'implicit' => true,
            ] + Time::MAP,
            'revokedCertificates' => [
                'type' => ASN1::TYPE_SEQUENCE,
                'optional' => true,
                'implicit' => true,
                'min' => 0,
                'max' => -1,
                'children' => RevokedCertificate::MAP,
            ],
            'crlExtensions' => [
                'constant' => 0,
                'optional' => true,
                'explicit' => true,
            ] + Extensions::MAP,
        ],
    ];
}
<?php

/**
 * RDNSequence
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * RDNSequence
 *
 * In practice, RDNs containing multiple name-value pairs (called "multivalued RDNs") are rare,
 * but they can be useful at times when either there is no unique attribute in the entry or you
 * want to ensure that the entry's DN contains some useful identifying information.
 *
 * - https://www.opends.org/wiki/page/DefinitionRelativeDistinguishedName
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class RDNSequence
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        // RDNSequence does not define a min or a max, which means it doesn't have one
        'min' => 0,
        'max' => -1,
        'children' => RelativeDistinguishedName::MAP,
    ];
}
<?php

/**
 * SignerInfo
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * SignerInfo
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class SignerInfo
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'version' => CMSVersion::MAP,
            'sid' => SignerIdentifier::MAP,
            'digestAlgorithm' => DigestAlgorithmIdentifier::MAP,
            'signedAttrs' => [
                'constant' => 0,
                'optional' => true,
                'implicit' => true,
            ] + SignedAttributes::MAP,
            'signatureAlgorithm' => SignatureAlgorithmIdentifier::MAP,
            'signature' => SignatureValue::MAP,
            'unsignedAttrs' => [
                'constant' => 1,
                'optional' => true,
                'implicit' => true,
            ] + UnsignedAttributes::MAP,
        ],
    ];
}
<?php

/**
 * LogotypeAudio
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * LogotypeAudio
 *
 * https://datatracker.ietf.org/doc/html/rfc9399#appendix-A.1
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class LogotypeAudio
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'audioDetails' => LogotypeDetails::MAP,
            'audioInfo' => ['optional' => true] + LogotypeAudioInfo::MAP,
        ],
    ];
}
<?php

/**
 * ExtensionAttributes
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * ExtensionAttributes
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class ExtensionAttributes
{
    public const MAP = [
        'type' => ASN1::TYPE_SET,
        'min' => 1,
        'max' => 256, // ub-extension-attributes
        'children' => ExtensionAttribute::MAP,
    ];
}
<?php

/**
 * AttCertVersion
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * AttCertVersion
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class AttCertVersion
{
    public const MAP = [
        'type' => ASN1::TYPE_INTEGER,
        'mapping' => ['v2'],
    ];
}
<?php

/**
 * CMSAlgorithmProtection
 *
 * From RFC6211
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * CMSAlgorithmProtection
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class CMSAlgorithmProtection
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'digestAlgorithm' => DigestAlgorithmIdentifier::MAP,
            'signatureAlgorithm' => [
                'constant' => 1,
                'optional' => true,
                'implicit' => true,
            ] + SignatureAlgorithmIdentifier::MAP,
            'macAlgorithm' => [
                'constant' => 2,
                'optional' => true,
                'implicit' => true,
            ] + MessageAuthenticationCodeAlgorithm::MAP,
        ],
    ];
}
<?php

/**
 * PKCS8ShroudedKeyBag
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

/**
 * PKCS8ShroudedKeyBag
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class PKCS8ShroudedKeyBag
{
    public const MAP = EncryptedPrivateKeyInfo::MAP;
}
<?php

/**
 * CertPolicyId
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * CertPolicyId
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class CertPolicyId
{
    public const MAP = ['type' => ASN1::TYPE_OBJECT_IDENTIFIER];
}
<?php

/**
 * ShakeOutputLen
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * ShakeOutputLen
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class ShakeOutputLen
{
    public const MAP = ['type' => ASN1::TYPE_INTEGER];
}
<?php

/**
 * Prime_p
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * Prime_p
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class Prime_p
{
    public const MAP = ['type' => ASN1::TYPE_INTEGER];
}
<?php

/**
 * DigestedData
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * DigestedData
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class DigestedData
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'version' => CMSVersion::MAP,
            'digestAlgorithm' => DigestAlgorithmIdentifier::MAP,
            'encapContentInfo' => EncapsulatedContentInfo::MAP,
            'digest' => Digest::MAP,
        ],
    ];
}
<?php

/**
 * RelativeDistinguishedName
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * RelativeDistinguishedName
 *
 * In practice, RDNs containing multiple name-value pairs (called "multivalued RDNs") are rare,
 * but they can be useful at times when either there is no unique attribute in the entry or you
 * want to ensure that the entry's DN contains some useful identifying information.
 *
 * - https://www.opends.org/wiki/page/DefinitionRelativeDistinguishedName
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class RelativeDistinguishedName
{
    public const MAP = [
        'type' => ASN1::TYPE_SET,
        'min' => 1,
        'max' => -1,
        'children' => AttributeTypeAndValue::MAP,
    ];
}
<?php

/**
 * PSourceAlgorithm
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

/**
 * PSourceAlgorithm
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class PSourceAlgorithm
{
    public const MAP = AlgorithmIdentifier::MAP;
}
<?php

/**
 * CertificateSerialNumber
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * CertificateSerialNumber
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class CertificateSerialNumber
{
    public const MAP = ['type' => ASN1::TYPE_INTEGER];
}
<?php

/**
 * Attributes
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * Attributes
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class Attributes
{
    public const MAP = [
        'type' => ASN1::TYPE_SET,
        'min' => 1,
        'max' => -1,
        'children' => Attribute::MAP,
    ];
}
<?php

/**
 * ContentType
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * ContentType
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class ContentType
{
    public const MAP = [
        'type' => ASN1::TYPE_OBJECT_IDENTIFIER,
    ];
}
<?php

/**
 * SubjectAltName
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

/**
 * SubjectAltName
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class SubjectAltName
{
    public const MAP = GeneralNames::MAP;
}
<?php

/**
 * MonetaryValue
 *
 * From https://www.etsi.org/deliver/etsi_ts/101800_101899/101862/01.03.02_60/ts_101862v010302p.pdf
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * MonetaryValue
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class MonetaryValue
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'currency' => Iso4217CurrencyCode::MAP,
            'amount' => ['type' => ASN1::TYPE_INTEGER],
            'exponent' => ['type' => ASN1::TYPE_INTEGER],
        ],
        // value = amount * 10^exponent
    ];
}
<?php

/**
 * AuthorityInfoAccessSyntax
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * AuthorityInfoAccessSyntax
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class AuthorityInfoAccessSyntax
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'min' => 1,
        'max' => -1,
        'children' => AccessDescription::MAP,
    ];
}
<?php

/**
 * UnprotectedAttributes
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * UnprotectedAttributes
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class UnprotectedAttributes
{
    public const MAP = [
        'type' => ASN1::TYPE_SET,
        'min' => 1,
        'max' => -1,
        'children' => Attribute::MAP,
    ];
}
<?php

/**
 * SpecifiedECDomain
 *
 * From: http://www.secg.org/sec1-v2.pdf#page=109
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * SpecifiedECDomain
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class SpecifiedECDomain
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'version' => [
                'type' => ASN1::TYPE_INTEGER,
                'mapping' => [1 => 'ecdpVer1', 'ecdpVer2', 'ecdpVer3'],
            ],
            'fieldID' => FieldID::MAP,
            'curve' => Curve::MAP,
            'base' => ECPoint::MAP,
            'order' => ['type' => ASN1::TYPE_INTEGER],
            'cofactor' => [
                'type' => ASN1::TYPE_INTEGER,
                'optional' => true,
            ],
            'hash' => ['optional' => true] + HashAlgorithm::MAP,
        ],
    ];
}
<?php

/**
 * TerminalIdentifier
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * TerminalIdentifier
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class TerminalIdentifier
{
    public const MAP = ['type' => ASN1::TYPE_PRINTABLE_STRING];
}
<?php

/**
 * KeyPurposeId
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * KeyPurposeId
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class KeyPurposeId
{
    public const MAP = ['type' => ASN1::TYPE_OBJECT_IDENTIFIER];
}
<?php

/**
 * SignatureValue
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * SignatureValue
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class SignatureValue
{
    public const MAP = ['type' => ASN1::TYPE_OCTET_STRING];
}
<?php

/**
 * GeneralSubtree
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * GeneralSubtree
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class GeneralSubtree
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'base' => GeneralName::MAP,
            'minimum' => [
                'constant' => 0,
                'optional' => true,
                'implicit' => true,
                'default' => '0',
            ] + BaseDistance::MAP,
            'maximum' => [
                'constant' => 1,
                'optional' => true,
                'implicit' => true,
            ] + BaseDistance::MAP,
        ],
    ];
}
<?php

/**
 * EcdsaSigValue
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * EcdsaSigValue
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class EcdsaSigValue
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'r' => ['type' => ASN1::TYPE_INTEGER],
            's' => ['type' => ASN1::TYPE_INTEGER],
        ],
    ];
}
<?php

/**
 * InvalidityDate
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * InvalidityDate
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class InvalidityDate
{
    public const MAP = ['type' => ASN1::TYPE_GENERALIZED_TIME];
}
<?php

/**
 * CRLDistributionPoints
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * CRLDistributionPoints
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class CRLDistributionPoints
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'min' => 1,
        'max' => -1,
        'children' => DistributionPoint::MAP,
    ];
}
<?php

/**
 * IssuingDistributionPoint
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * IssuingDistributionPoint
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class IssuingDistributionPoint
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'distributionPoint' => [
                'constant' => 0,
                'optional' => true,
                'explicit' => true,
            ] + DistributionPointName::MAP,
            'onlyContainsUserCerts' => [
                'type' => ASN1::TYPE_BOOLEAN,
                'constant' => 1,
                'optional' => true,
                'default' => false,
                'implicit' => true,
            ],
            'onlyContainsCACerts' => [
                'type' => ASN1::TYPE_BOOLEAN,
                'constant' => 2,
                'optional' => true,
                'default' => false,
                'implicit' => true,
            ],
            'onlySomeReasons' => [
                'constant' => 3,
                'optional' => true,
                'implicit' => true,
            ] + ReasonFlags::MAP,
            'indirectCRL' => [
                'type' => ASN1::TYPE_BOOLEAN,
                'constant' => 4,
                'optional' => true,
                'default' => false,
                'implicit' => true,
            ],
            'onlyContainsAttributeCerts' => [
                'type' => ASN1::TYPE_BOOLEAN,
                'constant' => 5,
                'optional' => true,
                'default' => false,
                'implicit' => true,
            ],
        ],
    ];
}
<?php

/**
 * Holder
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * Holder
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class Holder
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'baseCertificateID' => [
                'constant' => 0,
                'optional' => true,
            ] + IssuerSerial::MAP,
            'entityName' => [
                'constant' => 1,
                'optional' => true,
            ] + GeneralNames::MAP,
            'objectDigestInfo' => [
                'constant' => 2,
                'optional' => true,
            ] + ObjectDigestInfo::MAP,
        ],
    ];
}
<?php

/**
 * PublicKeyInfo
 *
 * PublicKeyInfo and SubjectPublicKeyInfo are pretty much the same. the only difference
 * is that in SubjectPublicKeyInfo (which actually is defined by an RFC - RFC5280) the
 * element names are algorithm and subjectPublicKey whereas in this one they're
 * publicKeyAlgorithm and publicKey. the publicKey bit, in particular, is relevant, because
 * publicKey is also an optional element in OneAsymmetricKey and it just makes life easier
 * to do isset($key['publicKey']) irrespective of which "map" was used to load the key.
 *
 * PublicKeyInfo::MAP is only used in PKCS8.php and it's children classes
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * PublicKeyInfo
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class PublicKeyInfo
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'publicKeyAlgorithm' => AlgorithmIdentifier::MAP,
            'publicKey' => ['type' => ASN1::TYPE_BIT_STRING],
        ],
    ];
}
<?php

/**
 * AuthEnvelopedData
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * AuthEnvelopedData
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class AuthEnvelopedData
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'version' => CMSVersion::MAP,
            'originatorInfo' => [
                'constant' => 0,
                'optional' => true,
                'implicit' => true,
            ] + OriginatorInfo::MAP,
            'recipientInfos' => RecipientInfos::MAP,
            'authEncryptedContentInfo' => EncryptedContentInfo::MAP,
            'authAttrs' => [
                'constant' => 1,
                'optional' => true,
                'implicit' => true,
            ] + AuthAttributes::MAP,
            'mac' => MessageAuthenticationCode::MAP,
            'unauthAttrs' => [
                'constant' => 2,
                'optional' => true,
                'implicit' => true,
            ] + UnauthAttributes::MAP,
        ],
    ];
}
<?php

/**
 * Accuracy
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * Accuracy
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class Accuracy
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'seconds' => ['type' => ASN1::TYPE_INTEGER],
            'millis' => [
                'type' => ASN1::TYPE_INTEGER,
                'constant' => 0,
                'optional' => true,
                'implicit' => true,
            ],
            'micros' => [
                'type' => ASN1::TYPE_INTEGER,
                'constant' => 1,
                'optional' => true,
                'implicit' => true,
            ],
        ],
    ];
}
<?php

/**
 * ECParameters
 *
 * From: https://tools.ietf.org/html/rfc5915
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * ECParameters
 *
 *  ECParameters ::= CHOICE {
 *    namedCurve         OBJECT IDENTIFIER
 *    -- implicitCurve   NULL
 *    -- specifiedCurve  SpecifiedECDomain
 *  }
 *    -- implicitCurve and specifiedCurve MUST NOT be used in PKIX.
 *    -- Details for SpecifiedECDomain can be found in [X9.62].
 *    -- Any future additions to this CHOICE should be coordinated
 *    -- with ANSI X9.
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class ECParameters
{
    public const MAP = [
        'type' => ASN1::TYPE_CHOICE,
        'children' => [
            'namedCurve' => ['type' => ASN1::TYPE_OBJECT_IDENTIFIER],
            'implicitCurve' => ['type' => ASN1::TYPE_NULL],
            'specifiedCurve' => SpecifiedECDomain::MAP,
        ],
    ];
}
<?php

/**
 * Characteristic_two
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * Characteristic_two
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class Characteristic_two
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'm' => ['type' => ASN1::TYPE_INTEGER], // field size 2**m
            'basis' => ['type' => ASN1::TYPE_OBJECT_IDENTIFIER],
            'parameters' => [
                'type' => ASN1::TYPE_ANY,
                'optional' => true,
            ],
        ],
    ];
}
<?php

/**
 * PolicyQualifierInfo
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * PolicyQualifierInfo
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class PolicyQualifierInfo
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'policyQualifierId' => PolicyQualifierId::MAP,
            'qualifier' => ['type' => ASN1::TYPE_ANY],
        ],
    ];
}
<?php

/**
 * KEKRecipientInfo
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * KEKRecipientInfo
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class KEKRecipientInfo
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'version' => CMSVersion::MAP,
            'kekid' => KEKIdentifier::MAP,
            'keyEncryptionAlgorithm' => KeyEncryptionAlgorithmIdentifier::MAP,
            'encryptedKey' => EncryptedKey::MAP,
        ],
    ];
}
<?php

/**
 * Extensions
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * Extensions
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class Extensions
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'min' => 1,
        // technically, it's MAX, but we'll assume anything < 0 is MAX
        'max' => -1,
        // if 'children' isn't an array then 'min' and 'max' must be defined
        'children' => Extension::MAP,
    ];
}
<?php

/**
 * PrivateKey
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * PrivateKey
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class PrivateKey
{
    public const MAP = ['type' => ASN1::TYPE_OCTET_STRING];
}
<?php

/**
 * ESSCertIDv2
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * ESSCertIDv2
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class ESSCertIDv2
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'hashAlgorithm' => [
                'optional' => true,
                'default' => ['algorithm' => 'id-sha256', 'parameters' => null],
            ] + AlgorithmIdentifier::MAP,
            'certHash' => Hash::MAP,
            'issuerSerial' => ['optional' => true] + IssuerSerial::MAP,
        ],
    ];
}
<?php

/**
 * EDIPartyName
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * EDIPartyName
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class EDIPartyName
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'nameAssigner' => [
                'constant' => 0,
                'optional' => true,
                'implicit' => true,
            ] + DirectoryString::MAP,
            // partyName is technically required but \phpseclib4\File\ASN1 doesn't currently support non-optional constants and
            // setting it to optional gets the job done in any event.
            'partyName' => [
                'constant' => 1,
                'optional' => true,
                'implicit' => true,
            ] + DirectoryString::MAP,
        ],
    ];
}
<?php

/**
 * EncapsulatedContentInfo
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * EncapsulatedContentInfo
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class EncapsulatedContentInfo
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'eContentType' => ContentType::MAP,
            'eContent' => [
                'type' => ASN1::TYPE_OCTET_STRING,
                'constant' => 0,
                'optional' => true,
                'explicit' => true,
            ],
        ],
    ];
}
<?php

/**
 * DSAPublicKey
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * DSAPublicKey
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class DSAPublicKey
{
    public const MAP = ['type' => ASN1::TYPE_INTEGER];
}
<?php

/**
 * SubjectDirectoryAttributes
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * SubjectDirectoryAttributes
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class SubjectDirectoryAttributes
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'min' => 1,
        'max' => -1,
        'children' => Attribute::MAP,
    ];
}
<?php

/**
 * AuthAttributes
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * AuthAttributes
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class AuthAttributes
{
    public const MAP = [
        'type' => ASN1::TYPE_SET,
        'min' => 1,
        'max' => -1,
        'children' => Attribute::MAP,
    ];
}
<?php

/**
 * OtherRecipientInfo
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * OtherRecipientInfo
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class OtherRecipientInfo
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'oriType' => ['type' => ASN1::TYPE_OBJECT_IDENTIFIER],
            'oriValue' => ['type' => ASN1::TYPE_ANY],
        ],
    ];
}
<?php

/**
 * OtherCertificateFormat
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * OtherCertificateFormat
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class OtherCertificateFormat
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'otherCertFormat' => ['type' => ASN1::TYPE_OBJECT_IDENTIFIER],
            'otherCert' => ['type' => ASN1::TYPE_ANY],
        ],
    ];
}
<?php

/**
 * SafeContents
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * SafeContents
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class SafeContents
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'min' => 1,
        'max' => -1,
        'children' => SafeBag::MAP,
    ];
}
<?php

/**
 * KeyAgreeRecipientIdentifier
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * KeyAgreeRecipientIdentifier
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class KeyAgreeRecipientIdentifier
{
    public const MAP = [
        'type' => ASN1::TYPE_CHOICE,
        'children' => [
            'issuerAndSerialNumber' => IssuerAndSerialNumber::MAP,
            'rKeyId' => [
                'constant' => 0,
                'optional' => true,
                'implicit' => true,
            ] + RecipientKeyIdentifier::MAP,
        ],
    ];
}
<?php

/**
 * AttributeCertificateV2
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * AttributeCertificateV2
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class AttributeCertificateV2
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'acinfo' => AttributeCertificateInfo::MAP,
            'signatureAlgorithm' => AlgorithmIdentifier::MAP,
            'signature' => ['type' => ASN1::TYPE_BIT_STRING],
        ],
    ];
}
<?php

/**
 * EncryptedKey
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * EncryptedKey
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class EncryptedKey
{
    public const MAP = ['type' => ASN1::TYPE_OCTET_STRING];
}
<?php

/**
 * PBES2params
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * PBES2params
 *
 * from https://tools.ietf.org/html/rfc2898#appendix-A.3
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class PBES2params
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'keyDerivationFunc' => AlgorithmIdentifier::MAP,
            'encryptionScheme' => AlgorithmIdentifier::MAP,
        ],
    ];
}
<?php

/**
 * Validity
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * Validity
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class Validity
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'notBefore' => Time::MAP,
            'notAfter' => Time::MAP,
        ],
    ];
}
<?php

/**
 * CRLNumber
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * CRLNumber
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class CRLNumber
{
    public const MAP = ['type' => ASN1::TYPE_INTEGER];
}
<?php

/**
 * DigestInfo
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * DigestInfo
 *
 * from https://tools.ietf.org/html/rfc2898#appendix-A.3
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class DigestInfo
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'digestAlgorithm' => AlgorithmIdentifier::MAP,
            'digest' => ['type' => ASN1::TYPE_OCTET_STRING],
        ],
    ];
}
<?php

/**
 * PrivateKeyInfo
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * PrivateKeyInfo
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class PrivateKeyInfo
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'version' => [
                'type' => ASN1::TYPE_INTEGER,
                'mapping' => ['v1'],
            ],
            'privateKeyAlgorithm' => AlgorithmIdentifier::MAP,
            'privateKey' => PrivateKey::MAP,
            'attributes' => [
                'constant' => 0,
                'optional' => true,
                'implicit' => true,
            ] + Attributes::MAP,
        ],
    ];
}
<?php

/**
 * FieldID
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * FieldID
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class FieldID
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'fieldType' => ['type' => ASN1::TYPE_OBJECT_IDENTIFIER],
            'parameters' => [
                'type' => ASN1::TYPE_ANY,
                'optional' => true,
            ],
        ],
    ];
}
<?php

/**
 * SignatureAlgorithmIdentifier
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

/**
 * SignatureAlgorithmIdentifier
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class SignatureAlgorithmIdentifier extends AlgorithmIdentifier
{
}
<?php

/**
 * DistributionPointName
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * DistributionPointName
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class DistributionPointName
{
    public const MAP = [
        'type' => ASN1::TYPE_CHOICE,
        'children' => [
            'fullName' => [
                'constant' => 0,
                'optional' => true,
                'implicit' => true,
            ] + GeneralNames::MAP,
            'nameRelativeToCRLIssuer' => [
                'constant' => 1,
                'optional' => true,
                'implicit' => true,
            ] + RelativeDistinguishedName::MAP,
        ],
    ];
}
<?php

/**
 * CRLReason
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * CRLReason
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class CRLReason
{
    public const MAP = [
        'type' => ASN1::TYPE_ENUMERATED,
        'mapping' => [
            'unspecified',
            'keyCompromise',
            'cACompromise',
            'affiliationChanged',
            'superseded',
            'cessationOfOperation',
            'certificateHold',
            // Value 7 is not used.
            8 => 'removeFromCRL',
            'privilegeWithdrawn',
            'aACompromise',
        ],
    ];
}
<?php

/**
 * RecipientIdentifier
 *
 * Basically the same thing as SignerIdentifier
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * RecipientIdentifier
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class RecipientIdentifier
{
    public const MAP = [
        'type' => ASN1::TYPE_CHOICE,
        'children' => [
            'issuerAndSerialNumber' => IssuerAndSerialNumber::MAP,
            'subjectKeyIdentifier' => [
                'constant' => 0,
                'optional' => true,
                'explicit' => true,
            ] + SubjectKeyIdentifier::MAP,
        ],
    ];
}
<?php

/**
 * SubjectKeyIdentifier
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * SubjectKeyIdentifier
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class SubjectKeyIdentifier
{
    public const MAP = ['type' => ASN1::TYPE_OCTET_STRING];
}
<?php

/**
 * AuthenticatedSafe
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * AuthenticatedSafe
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class AuthenticatedSafe
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'min' => 0,
        'max' => -1,
        'children' => ContentInfo::MAP,
    ];
}
<?php

/**
 * SignedAttributes
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * SignedAttributes
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class SignedAttributes
{
    public const MAP = [
        'type' => ASN1::TYPE_SET,
        'min' => 1,
        'max' => -1,
        'children' => Attribute::MAP,
    ];
}
<?php

/**
 * AttCertValidityPeriod
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * AttCertValidityPeriod
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class AttCertValidityPeriod
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'notBeforeTime' => ['type' => ASN1::TYPE_GENERALIZED_TIME],
            'notAfterTime' => ['type' => ASN1::TYPE_GENERALIZED_TIME],
        ],
    ];
}
<?php

/**
 * CPSuri
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * CPSuri
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class CPSuri
{
    public const MAP = ['type' => ASN1::TYPE_IA5_STRING];
}
<?php

/**
 * Time
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * Time
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class Time
{
    public const MAP = [
        'type' => ASN1::TYPE_CHOICE,
        'children' => [
            'utcTime' => ['type' => ASN1::TYPE_UTC_TIME],
            'generalTime' => ['type' => ASN1::TYPE_GENERALIZED_TIME],
        ],
    ];
}
<?php

/**
 * RecipientEncryptedKey
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Maps;

use phpseclib4\File\ASN1;

/**
 * RecipientEncryptedKey
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class RecipientEncryptedKey
{
    public const MAP = [
        'type' => ASN1::TYPE_SEQUENCE,
        'children' => [
            'rid' => KeyAgreeRecipientIdentifier::MAP,
            'encryptedKey' => EncryptedKey::MAP,
        ],
    ];
}
<?php

/**
 * ASN.1 Constructed Array Object
 *
 * Load the 'content' array key of constructed array objects on-demand.
 *
 * Does not implement ArrayObject. Altho this class implements all the same interfaces
 * that ArrayObject implements, ArrayObject adds additional functions that really
 * don't make a lot of sense in the context of phpseclib. eg. asort(), ksort(), etc.
 * offsetSet() kinda makes append() redundant as well.
 *
 * PHP version 8
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2025-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1;

use phpseclib4\Exception\{
    EOCException,
    EncodedDataUnavailableException,
    ExcessivelyDeepDataException,
    InvalidStateException,
    UnexpectedValueException
};
use phpseclib4\File\ASN1\Types\{BaseType, BitString, Boolean, Choice, Integer, OctetString};
use phpseclib4\File\{ASN1, CRL, X509};
use phpseclib4\File\CMS\EnvelopedData\KeyAgreeRecipient\EncryptedKey;
use phpseclib4\File\CMS\EnvelopedData\Recipient;
use phpseclib4\File\CMS\SignedData\Signer;

/**
 * ASN.1 Constructed Array Object
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @implements \ArrayAccess<int|string, mixed>
 * @implements \Iterator<int|string, mixed>
 */
class Constructed implements \ArrayAccess, \Countable, \Iterator, BaseType
{
    private ?array $mapping = null;
    public ?array $decoded = null;
    public Choice|Constructed|null $parent = null;
    public int $depth = 0;
    public int|string $key;
    public array $rules = [];
    private string $wrapping = '';
    private bool $forcedCache = false;

    public function __construct(
        private string $encoded,
        private int $class,
        private int $tag,
        private int $start,
        private int $encoded_pos,
        private int $headerlength,
        private string $rawheader
    ) {
    }

    // you'd want to do this if you have a non-universal tag.
    // like to identify if it's present in a SEQUENCE you need the original tag
    // but to decode it you need the tag in the mapping
    public function replaceTag(int $tag): void
    {
        $this->tag = $tag;
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function getTag(): int
    {
        return $this->tag;
    }

    public function linkMapping(array $mapping, array $rules = []): void
    {
        $this->mapping = $mapping;
        $this->rules = $rules;
    }

    public function offsetExists(mixed $offset): bool
    {
        // `__isset()` is for stuff like $r->offset whereas `offsetExists()` is for stuff like $r['offset']
        self::decodeCurrent();

        return isset($this->decoded[$offset]);
    }

    public function enableForcedCache(): void
    {
        $this->forcedCache = true;
    }

    public function disableForcedCache(): void
    {
        $this->forcedCache = false;
    }

    public function isCacheForced(): bool
    {
        return $this->forcedCache;
    }

    private function calculateIndefiniteLength(int &$offset, int $depth = 1): int
    {
        $origOffset = $offset;
        while (true) {
            ['tag' => $tag] = ASN1::decodeTag($this->encoded, $offset);
            $length = ASN1::decodeLength($this->encoded, $offset);
            if ($tag === 0 && $length === 0) {
                break;
            }
            if (!isset($length)) {
                self::incrementDepth($depth);
                $this->calculateIndefiniteLength($offset, $depth);
            } else {
                $offset += $length;
            }
        }

        return $offset - $origOffset;
    }

    public function &offsetGet(mixed $offset): mixed
    {
        if (!$this->mapping) {
            throw new InvalidStateException('map() has not been ran; if you want to get the array keys do ->toArray()');
        }

        self::decodeCurrent();

        if (!isset($this->decoded[$offset])) {
            //throw new UnexpectedValueException("The requested offset '$offset' was not found");
            //this is https://en.wikipedia.org/wiki/Autovivification
            $this->decoded[$offset] = [];
            if (ASN1::invalidateCache()) {
                $this->invalidateCache();
            }
        }

        if ($this->decoded[$offset] instanceof Constructed) {
            try {
                $this->decoded[$offset]->decodeCurrent();
            } catch (\Exception $e) {
                if (ASN1::isBlobsOnBadDecodesEnabled()) {
                    $temp = new MalformedData($this->decoded[$offset]->encoded);
                    return $temp;
                }
                throw $e;
            }
        }

        /** @psalm-suppress NonVariableReferenceReturn */
        return $this->decoded[$offset];
    }

    public function count(): int
    {
        if (!$this->mapping) {
            throw new InvalidStateException('map() has not been ran; if you want to get the array keys do ->toArray()');
        }

        self::decodeCurrent();

        return count($this->decoded);
    }

    public function reconstituteKeyHelper(): ?string
    {
        if (!isset($this->key)) {
            return null;
        }
        return !isset($this->parent) || !isset($this->parent->key) ? (string) $this->key : $this->parent->reconstituteKeyHelper() . '/' . $this->key;
    }

    private function reconstituteKey(): string
    {
        $key = $this->reconstituteKeyHelper();
        return isset($key) ? " ($key)" : '';
    }

    // $this->depth is normally set in either setSeqOuterLoop() or setSeqInnerLoop(), however,
    // constructed OCTET_STRINGs and BIT_STRINGs keep track of the depth via the $depth parameter
    // to decodeCurrent()
    private function decodeCurrent(?int $depth = null): void
    {
        if (isset($this->decoded)) {
            return;
        }

        $mapping = $this->mapping;
        $rules = $this->rules;

        $offset = 0;
        $decoded = [];

        if ($this->tag != $mapping['type']) {
            $key = $this->reconstituteKey();
            throw new UnexpectedValueException('Found ' . ASN1::convertTypeConstantToString($this->tag) . ' - expecting ' . ASN1::convertTypeConstantToString($mapping['type']) . $key);
        }

        /*
         * let's say you have a CMS with a 5,202,560 byte PDF embedded within it as a indefinite length OCTET STRING
         * that's in 5202x 1,000 byte chunks and 1x 560 byte chunk. without this code it takes 0.21s to load it.
         * with this chunk of code it takes 0.11s. at least on PHP 8.3
         *
         * the basic idea is that if you have something like this:
         *
         * OCTET STRING
         *   OCTET STRING
         *   OCTET STRING
         *   OCTET STRING
         *   ...
         *
         * that all the nested OCTET STRINGS save for the last one will match.
         *
         * if you have something like this otoh:
         *
         * OCTET STRING
         *   OCTET STRING
         *     OCTET STRING
         *       OCTET STRING
         *         ...
         *
         * then it should parse that using the existing logic
         */
        if ($this->tag == ASN1::TYPE_OCTET_STRING) {
            $sample = ASN1::decodeBER($this->encoded, $this->start + $offset, $offset);
            $taglength = chr(ASN1::TYPE_OCTET_STRING) . ASN1::encodeLength($sample['length']);
            $temp = preg_replace('#((?:' . $taglength . '.{' . $sample['length'] . '})*).*#s', '$1', $this->encoded);
            $this->encoded = substr($this->encoded, strlen($temp));
            $temp = preg_replace('#' . $taglength . '(.{' . $sample['length'] . '})#s', '$1', $temp);
            $result = $temp;
        }

        $content_len = strlen($this->encoded);
        while ($offset < $content_len) {
            try {
                $temp = ASN1::decodeBER($this->encoded, $this->start + $offset, $offset);
                $offset += $temp['headerlength'];

                $length = $temp['length'] ?? $this->calculateIndefiniteLength($offset);
                if (isset($temp['length'])) {
                    $offset += $length;
                } else {
                    $temp['actuallength'] = $length;
                    if ($temp['content'] instanceof Constructed) {
                        $temp['content']->encoded = substr($temp['content']->encoded, 0, $length);
                    }
                }
                $decoded[] = $temp;
            } catch (EOCException) {
                break;
            }
        }

        switch ($this->tag) {
            // implicit time tags should never be explicit but sometimes they none-the-less are.
            // the following URLs elaborate
            // https://github.com/phpseclib/phpseclib/commit/511f55de3d1d504e4686f9d558a3c10709b413f8
            // https://github.com/phpseclib/phpseclib/issues/1388
            case ASN1::TYPE_GENERALIZED_TIME:
            case ASN1::TYPE_UTC_TIME:
                if (!$this->parent instanceof Constructed) {
                    throw new InvalidStateException('The only parent to a constructed time tag should be Constructed');
                }
                $this->parent->decoded[$this->key] = ASN1::map($decoded[0], $mapping);
                $this->decoded = [];
                break;
            case ASN1::TYPE_BIT_STRING:
            case ASN1::TYPE_OCTET_STRING:
                if (!isset($result)) {
                    $result = '';
                }
                $excessivelyDeepData = false;
                foreach ($decoded as $content) {
                    if ($content['content'] instanceof Constructed) {
                        /** @var Choice */
                        $temp = ASN1::map($content, [
                            'type' => ASN1::TYPE_CHOICE,
                            'children' => [
                                'data' => ['type' => $this->tag]
                            ]
                        ]);
                        $temp->key = 0;
                        $temp['data']->depth = $depth ?? $this->depth;
                        try {
                            self::incrementDepth($temp['data']->depth);
                            $temp['data']->decodeCurrent($temp['data']->depth);
                        } catch (ExcessivelyDeepDataException $e) {
                            if (!ASN1::isBlobsOnBadDecodesEnabled() || isset($depth)) {
                                throw $e;
                            }
                            $excessivelyDeepData = true;
                        }
                        $result .= $temp['data'];
                        continue;
                    }
                    if ($content['type'] != $this->tag) {
                        $error = 'All subtags of a constructed string should be the same type as the constructed string itself';
                        $error = "$error (found " . ASN1::convertTypeConstantToString($content['type']) . '; expected ' . ASN1::convertTypeConstantToString($this->tag) . ')';
                        throw new UnexpectedValueException($error);
                    }
                    $result .= $content['content'];
                }
                if ($excessivelyDeepData) {
                    $value = new ExcessivelyDeepData($this->rawheader . $result);
                } elseif ($this->tag == ASN1::TYPE_BIT_STRING) {
                    $value = new BitString($result);
                } else {
                    $value = new OctetString($result);
                }
                if ($this->parent instanceof Constructed) {
                    $this->parent->decoded[$this->key] = $value;
                } else {
                    $this->parent['data'] = $value;
                }
                $this->decoded = [];
                break;
            case ASN1::TYPE_SEQUENCE:
                /** @var array<string, mixed> $children */
                $children = $mapping['children'];

                if (isset($mapping['min']) && isset($mapping['max'])) {
                    $this->decoded = $this->setSeqOuterLoop($decoded, $children);
                    break;
                }

                $map = [];
                $keys = array_keys($children);
                $j = 0;

                for ($i = 0; $i < count($decoded); $i++) {
                    $temp = $decoded[$i];
                    $tempClass = ASN1::CLASS_UNIVERSAL;
                    if (isset($temp['constant'])) {
                        $tempClass = $temp['type'];
                    }

                    while ($j < count($keys)) {
                        $key = $keys[$j++];
                        $child = $children[$key];
                        $candidate = $this->setSeqInnerLoop($child, $temp, $tempClass, $key);
                        if ($candidate) {
                            // Got the match: use it.
                            $map[$key] = $candidate;
                            if (isset($rules[$key]) && !$map[$key] instanceof Element) {
                                if (!is_callable($rules[$key])) {
                                    $map[$key]->rules = $rules[$key];
                                } else {
                                    $rules[$key]($map[$key]);
                                    $this->rules = [];
                                }
                            }
                            break;
                        // if there are more elements than we're expecting
                        } elseif ($j === count($keys)) {
                            if (ASN1::isBlobsOnBadDecodesEnabled()) {
                                break 2;
                            }
                            throw new UnexpectedValueException('Found data that doesn\'t map to any key (' . implode(', ', array_keys($map)) . ')');
                        } elseif (isset($child['default'])) {
                            $map[$key] = self::fillInDefault($child);
                        } elseif (!isset($child['optional'])) {
                            if (ASN1::isBlobsOnBadDecodesEnabled()) {
                                $map[$key] = new MalformedData((string) $temp['content']);
                                break;
                            }
                            throw new UnexpectedValueException("Unable to find a matching element for $key in the SEQUENCE");
                        }
                    }
                }

                // after we've found all the $decoded elements that match we need to look to see if any that should have been
                // included are missing and if so then if those missing ones are optional
                while ($j < count($keys)) {
                    $key = $keys[$j++];
                    $child = $children[$key];
                    if (isset($child['default'])) {
                        $map[$key] = self::fillInDefault($child);
                    } elseif (!isset($child['optional'])) {
                        if (ASN1::isBlobsOnBadDecodesEnabled()) {
                            break;
                        }
                        throw new UnexpectedValueException("Unable to find a matching element for $key in the SEQUENCE");
                    }
                }

                // if there were less elements than we were expecting
                if (!ASN1::isBlobsOnBadDecodesEnabled() && $j < count($decoded)) {
                    throw new UnexpectedValueException('There were ' . count($decoded) . ' elements found in the decoded data but we\'re only expecting ' . count($keys) . ' (' . implode(', ', array_keys($map)) . ')');
                }

                $this->decoded = $map;
                break;
            // the main diff between sets and sequences is that in a sequence the elements must appear in a specific order whereas
            // in a set they can appear in any order
            case ASN1::TYPE_SET:
                $children = $mapping['children'];

                if (isset($mapping['min']) && isset($mapping['max'])) {
                    $this->decoded = $this->setSeqOuterLoop($decoded, $children);
                    break;
                }

                $map = [];

                for ($i = 0; $i < count($decoded); $i++) {
                    $temp = $decoded[$i];
                    $tempClass = ASN1::CLASS_UNIVERSAL;
                    if (isset($temp['constant'])) {
                        $tempClass = $temp['type'];
                    }

                    foreach ($mapping['children'] as $key => $child) {
                        if (isset($map[$key])) {
                            continue;
                        }

                        $candidate = $this->setSeqInnerLoop($child, $temp, $tempClass, $key);
                        if (!$candidate) {
                            continue;
                        }

                        // Got the match: use it.
                        $map[$key] = $candidate;
                        if ($map[$key] instanceof Constructed && isset($rules[$key])) {
                            $map[$key]->rules = $rules[$key];
                        }
                        break;
                    }
                }

                foreach ($mapping['children'] as $key => $child) {
                    if (!isset($map[$key])) {
                        if (isset($child['default'])) {
                            $map[$key] = self::fillInDefault($child);
                        } elseif (!isset($child['optional'])) {
                            if (ASN1::isBlobsOnBadDecodesEnabled()) {
                                $map[$key] = new MalformedData($temp['content']);
                                break;
                            }
                            throw new UnexpectedValueException("Unable to find a matching element for $key in the SET");
                        }
                    }
                }

                $this->decoded = $map;
                $this->rules = $rules;
                break;
            default:
                throw new UnexpectedValueException('Unable to decode element (' . $this->tag . ') @ ' . $this->start);
        }
    }

    private static function fillInDefault(array $child): BaseType
    {
        if ($child['type'] == ASN1::TYPE_INTEGER && isset($child['mapping'])) {
            $temp = new Integer(array_search($child['default'], $child['mapping']));
            $temp->mappedValue = $child['default'];
            return $temp;
        }
        switch ($child['type']) {
            case ASN1::TYPE_BOOLEAN:
                return new Boolean($child['default']);
            case ASN1::TYPE_INTEGER:
                return new Integer($child['default']);
            case ASN1::TYPE_SEQUENCE:
                $encoded = ASN1::encodeDER($child['default'], array_diff_key($child, ['default' => 1]));
                $decoded = ASN1::decodeBER($encoded);
                $result = ASN1::map($decoded, $child);
                if ($result instanceof Element) {
                    throw new InvalidStateException('Maps shouldn\'t have default values that decode to an Element');
                }
                return $result;
        }
        //return $child['default'];
        throw new UnexpectedValueException('An unsupported default type was encountered');
    }

    private function setSeqOuterLoop(array $decoded, array $children): array
    {
        $rules = $this->rules;
        $map = [];

        foreach ($decoded as $key => $content) {
            try {
                $temp = ASN1::map($content, $children, $rules);
                if ($temp instanceof Constructed || $temp instanceof Choice) {
                    $temp->depth = $this->depth;
                    self::incrementDepth($temp->depth);
                    $temp->parent = $this;
                    $temp->key = $key;
                    if (isset($rules['*'])) {
                        if (!is_callable($rules['*'])) {
                            $temp->rules = $rules['*'];
                        } else {
                            $rules['*']($temp);
                            $this->rules = [];
                        }
                    }
                }
                $map[] = $temp;
            } catch (EncodedDataUnavailableException | ExcessivelyDeepDataException $e) {
                $data = substr($this->encoded, $content['start'] - $this->start, ($content['length'] ?? $content['actuallength']) + $content['headerlength']);
                $map[] = $e instanceof EncodedDataUnavailableException ? new Element($data) : new ExcessivelyDeepData($data);
            }
        }

        return $map;
    }

    private function setSeqInnerLoop(array $child, array $temp, int $tempClass, string $key): BaseType|Element|null
    {
        $rules = isset($this->rules[$key]) && is_array($this->rules[$key]) ? $this->rules[$key] : [];

        $maymatch = true;
        if ($child['type'] != ASN1::TYPE_CHOICE) {
            $childClass = ASN1::CLASS_UNIVERSAL;
            $constant = null;
            if (isset($child['class'])) {
                $childClass = $child['class'];
                $constant = $child['cast'];
            } elseif (isset($child['constant'])) {
                $childClass = ASN1::CLASS_CONTEXT_SPECIFIC;
                $constant = $child['constant'];
            }
            if (isset($constant) && isset($temp['constant'])) {
                // Can only match if constants and class match.
                $maymatch = $constant == $temp['constant'] && $childClass == $tempClass;
            } elseif (!isset($constant) && !isset($temp['constant'])) {
                // Can only match if no constant expected and type matches or is generic.
                $maymatch = !isset($child['constant']) && array_search($child['type'], [$temp['type'], ASN1::TYPE_ANY, ASN1::TYPE_CHOICE]) !== false;
            } else {
                $maymatch = false;
            }
        } elseif (isset($child['constant'])) {
            // a CHOICE that is itself tagged is identified by that tag. its alternatives
            // can't be used to tell it apart from a sibling with the same CHOICE definition
            // (eg. issuerLogo [1] / subjectLogo [2] in RFC 9399's LogotypeExtn).
            $maymatch = isset($temp['constant']) &&
                $child['constant'] == $temp['constant'] &&
                $tempClass == ASN1::CLASS_CONTEXT_SPECIFIC;
        }

        if ($maymatch) {
            // Attempt submapping.
            try {
                $candidate = ASN1::map($temp, $child, $rules);
                if ($candidate instanceof Constructed || $candidate instanceof Choice) {
                    $candidate->depth = $this->depth;
                    self::incrementDepth($candidate->depth);
                    $candidate->parent = $this;
                    $candidate->key = $key;
                }
            // altho the data is unavailable to ASN1 it _is_ available to Constructed
            } catch (EncodedDataUnavailableException | ExcessivelyDeepDataException $e) {
                if (!ASN1::isBlobsOnBadDecodesEnabled() && $e instanceof ExcessivelyDeepDataException) {
                    throw $e;
                }
                $data = substr($this->encoded, $temp['start'] - $this->start, ($temp['length'] ?? $temp['actuallength']) + $temp['headerlength']);
                return $e instanceof EncodedDataUnavailableException ? new Element($data) : new ExcessivelyDeepData($data);
            } catch (UnexpectedValueException) {
                $maymatch = false;
            }
        }

        return $maymatch ? $candidate : null;
    }

    public function invalidateCache(): void
    {
        $this->rawheader = '';
        $this->encoded = '';
        $this->wrapping = '';

        if ($this->parent) {
            $this->parent->invalidateCache();
        }
    }

    public function offsetSet(mixed $offset, mixed $value): void
    {
        if (!$this->mapping) {
            throw new InvalidStateException('map() has not been ran');
        }

        self::decodeCurrent();

        if (!isset($offset)) {
            // from <https://www.php.net/manual/en/language.types.array.php>:
            // "if no key is specified, the maximum of the existing int indices is taken,
            //  and the new key will be that maximum value plus 1 (but at least 0)"
            $keys = $this->keys();
            $max = count($keys) ? max($keys) : false;
            $offset = is_int($max) ? $max + 1 : 0;
            // also from the above URL:
            // "Note that the maximum integer key used for this need not currently exist in the array.
            //  It need only have existed in the array at some time since the last time the array was re-indexed."
            // (this functionality is not emulated and i've no intention to do so)
        }

        $this->decoded[$offset] = $value;

        if (ASN1::invalidateCache()) {
            $this->invalidateCache();
        }
    }

    public function offsetUnset(mixed $offset): void
    {
        if (!$this->mapping) {
            throw new InvalidStateException('map() has not been ran');
        }

        self::decodeCurrent();

        unset($this->decoded[$offset]);

        $this->invalidateCache();
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function firstKey(): mixed
    {
        if (!$this->mapping) {
            throw new InvalidStateException('map() has not been ran');
        }

        self::decodeCurrent();

        return array_key_first($this->decoded);
    }

    public function lastKey(): mixed
    {
        if (!$this->mapping) {
            throw new InvalidStateException('map() has not been ran');
        }

        self::decodeCurrent();

        return array_key_last($this->decoded);
    }

    public function rekey(): void
    {
        // this is mainly intended to turn a sparse numerically indexed array into a dense array

        if (!$this->mapping) {
            throw new InvalidStateException('map() has not been ran');
        }

        // no point in messing with $this->decoded if it hasn't been messed with
        if (!$this->decoded) {
            return;
        }

        $this->decoded = array_values($this->decoded);
    }

    public function __debugInfo(): array
    {
        if (!isset($this->mapping)) {
            // by default __debugInfo is recursive so if we want this to show the full
            // array all we gotta do is do "return $this->decoded;"
            $temp = [
                'encoded' => '...',
                'class' => $this->class,
                'tag' => $this->tag,
                'start' => $this->start,
                'encoded_pos' => $this->encoded_pos,
                'headerlength' => $this->headerlength
            ];
            return $temp;
        }

        self::decodeCurrent();

        foreach ($this->decoded as $key => $value) {
            if ($value instanceof Constructed && !isset($value->decoded)) {
                // if we rely on __debugInfo's built-in recursiveness then we wouldn't be able to
                // replace Constructed objects with OctetString or BitString objects
                try {
                    $value->__debugInfo();
                } catch (\Exception $e) {
                    if (ASN1::isBlobsOnBadDecodesEnabled()) {
                        $this->decoded[$key] = new MalformedData($value->encoded);
                    } else {
                        throw $e;
                    }
                }
            }
            // ideally for an X509 cert, unless __debugInfo were called _directly_ on it, we'd
            // render the X509 cert differently. eg. as the PEM vs the broken down PEM
            if ($value instanceof X509 || $value instanceof CRL) {
                $value->enableHideFullDecode();
            }
        }

        return $this->decoded;
    }

    public function hasTypeID(): bool
    {
        return false;
    }

    public function setWrapping(string $wrapping): void
    {
        $this->wrapping = $wrapping;
    }

    public function hasWrapping(): bool
    {
        return strlen($this->wrapping) !== 0;
    }

    public function getEncodedWithWrapping(): string
    {
        return $this->wrapping . $this->rawheader . $this->encoded;
    }

    public function hasEncoded(): bool
    {
        return strlen($this->rawheader) !== 0;
    }

    public function getEncoded(): string
    {
        return $this->rawheader . $this->encoded;
    }

    public function getEncodedLength(): int
    {
        return strlen($this->rawheader) + strlen($this->encoded);
    }

    public function setEncoded(string $header, string $encoded): void
    {
        $this->rawheader = $header;
        $this->encoded = $encoded;
    }

    public function getEncodedWithoutHeader(): string
    {
        return $this->encoded;
    }

    public function keys(): array
    {
        if (!$this->mapping) {
            throw new InvalidStateException('map() has not been ran; if you want to get the array keys do ->toArray()');
        }

        self::decodeCurrent();

        return array_keys($this->decoded);
    }

    public function rewind(): void
    {
        if (!$this->mapping) {
            throw new InvalidStateException('map() has not been ran; if you want to get the array keys do ->toArray()');
        }

        self::decodeCurrent();

        reset($this->decoded);
    }

    public function current(): mixed
    {
        if (!$this->mapping) {
            throw new InvalidStateException('map() has not been ran; if you want to get the array keys do ->toArray()');
        }

        self::decodeCurrent();

        return current($this->decoded);
    }

    public function key(): mixed
    {
        if (!$this->mapping) {
            throw new InvalidStateException('map() has not been ran; if you want to get the array keys do ->toArray()');
        }

        self::decodeCurrent();

        return key($this->decoded);
    }

    public function next(): void
    {
        if (!$this->mapping) {
            throw new InvalidStateException('map() has not been ran; if you want to get the array keys do ->toArray()');
        }

        self::decodeCurrent();

        next($this->decoded);
    }

    public function valid(): bool
    {
        if (!$this->mapping) {
            throw new InvalidStateException('map() has not been ran; if you want to get the array keys do ->toArray()');
        }

        self::decodeCurrent();

        $key = key($this->decoded);
        if (!isset($key)) {
            return false;
        }

        return isset($this->decoded[key($this->decoded)]);
    }

    public function hasMapping(): bool
    {
        return isset($this->mapping);
    }

    public function currentlyDecoded(): array|string
    {
        if (!$this->decoded) {
            return '...';
        }

        $output = [];
        foreach ($this->decoded as $key => $value) {
            if ($value instanceof Constructed) {
                $output[$key] = $value->decoded ? $value->currentlyDecoded() : '...';
            } else {
                $output[$key] = $value;
            }
        }
        return $output;
    }

    public function toArray(bool $convertPrimitives = false): array
    {
        if (!isset($this->mapping)) {
            throw new InvalidStateException('Cannot convert Constructed object to an array when no mapping has been provided');
        }

        self::decodeCurrent();
        $result = [];
        foreach ($this->decoded as $key => $value) {
            try {
                if ($value instanceof Constructed || $value instanceof Choice || $value instanceof Signer || $value instanceof Recipient || $value instanceof EncryptedKey) {
                    $value = $value->toArray($convertPrimitives);
                } elseif ($convertPrimitives) {
                    $value = ASN1::convertToPrimitive($value);
                }
                $result[$key] = $value;
            } catch (\Exception $e) {
                if (ASN1::isBlobsOnBadDecodesEnabled()) {
                    $value = match ($value::class) {
                        Choice::class => $value->value->encoded,
                        Constructed::class => $value->encoded,
                        default => throw new UnexpectedValueException($value::class . ' found - expecting Choice or Constructed')
                    };
                    $result[$key] = new MalformedData($value);
                    continue;
                }
                throw $e;
            }
        }

        return $result;
    }

    public function __toString(): string
    {
        if (!$this->hasEncoded()) {
            throw new UnexpectedValueException('Unable to convert constructed to string');
        }
        return $this->getEncoded();
    }

    private static function incrementDepth(int &$depth): void
    {
        $depth++;
        if ($depth == ASN1::getRecursionDepth()) {
            throw new ExcessivelyDeepDataException("Depth exceeds safe limits ($depth)");
        }
    }
}
<?php

/**
 * ASN.1 PFX (PKCS#12) OIDs
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2025-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\OIDs;

/**
 * ASN.1 PFX (PKCS#12) OIDs
 *
 * OIDs from RFC7292
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class PFX
{
    public const OIDs = [
        'KeyBag' => '1.2.840.113549.1.12.10.1.1',
        'PKCS8ShroudedKeyBag' => '1.2.840.113549.1.12.10.1.2',
        'CertBag' => '1.2.840.113549.1.12.10.1.3',
        'CRLBag' => '1.2.840.113549.1.12.10.1.4',
        'SecretBag' => '1.2.840.113549.1.12.10.1.5',
        'SafeContents' => '1.2.840.113549.1.12.10.1.6',
        'x509Certificate' => '1.2.840.113549.1.9.22.1',
        'pkcs-9-at-friendlyName' => '1.2.840.113549.1.9.20',
        'pkcs-9-at-localKeyId' => '1.2.840.113549.1.9.21',
        // https://www.rfc-editor.org/rfc/rfc7292#section-4.2.3 defines the following:
        //'x509Certificate' => '1.2.840.113549.1.9.0.1.1',
        //'sdsiCertificate' => '1.2.840.113549.1.9.0.1.2',
        // near as i can tell, however, nothing actually uses those OIDs
    ];
}
<?php

/**
 * ASN.1 EC OIDs
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2025-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\OIDs;

/**
 * EC OIDs
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class EC
{
    public const OIDs = [
        'prime-field' => '1.2.840.10045.1.1',
        'characteristic-two-field' => '1.2.840.10045.1.2',
        'characteristic-two-basis' => '1.2.840.10045.1.2.3',
        // per http://www.secg.org/SEC1-Ver-1.0.pdf#page=84, gnBasis "not used here"
        'gnBasis' => '1.2.840.10045.1.2.3.1', // NULL
        'tpBasis' => '1.2.840.10045.1.2.3.2', // Trinomial
        'ppBasis' => '1.2.840.10045.1.2.3.3',  // Pentanomial
    ];
}
<?php

/**
 * ASN.1 PKCS8 OIDs
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2025-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\OIDs;

/**
 * PKCS8 OIDs
 *
 * From https://tools.ietf.org/html/rfc2898
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class PKCS8
{
    public const OIDs = [
        // PBES1 encryption schemes
        'pbeWithMD2AndDES-CBC' => '1.2.840.113549.1.5.1',
        'pbeWithMD2AndRC2-CBC' => '1.2.840.113549.1.5.4',
        'pbeWithMD5AndDES-CBC' => '1.2.840.113549.1.5.3',
        'pbeWithMD5AndRC2-CBC' => '1.2.840.113549.1.5.6',
        'pbeWithSHA1AndDES-CBC' => '1.2.840.113549.1.5.10',
        'pbeWithSHA1AndRC2-CBC' => '1.2.840.113549.1.5.11',

        // from PKCS#12:
        // https://tools.ietf.org/html/rfc7292
        'pbeWithSHAAnd128BitRC4' => '1.2.840.113549.1.12.1.1',
        'pbeWithSHAAnd40BitRC4' => '1.2.840.113549.1.12.1.2',
        'pbeWithSHAAnd3-KeyTripleDES-CBC' => '1.2.840.113549.1.12.1.3',
        'pbeWithSHAAnd2-KeyTripleDES-CBC' => '1.2.840.113549.1.12.1.4',
        'pbeWithSHAAnd128BitRC2-CBC' => '1.2.840.113549.1.12.1.5',
        'pbeWithSHAAnd40BitRC2-CBC' => '1.2.840.113549.1.12.1.6',

        'id-PBKDF2' => '1.2.840.113549.1.5.12',
        'id-PBES2' => '1.2.840.113549.1.5.13',
        'id-PBMAC1' => '1.2.840.113549.1.5.14',

        // from PKCS#5 v2.1:
        // http://www.rsa.com/rsalabs/pkcs/files/h11302-wp-pkcs5v2-1-password-based-cryptography-standard.pdf
        'id-hmacWithSHA1' => '1.2.840.113549.2.7',
        'id-hmacWithSHA224' => '1.2.840.113549.2.8',
        'id-hmacWithSHA256' => '1.2.840.113549.2.9',
        'id-hmacWithSHA384' => '1.2.840.113549.2.10',
        'id-hmacWithSHA512' => '1.2.840.113549.2.11',
        'id-hmacWithSHA512-224' => '1.2.840.113549.2.12',
        'id-hmacWithSHA512-256' => '1.2.840.113549.2.13',

        'desCBC'       => '1.3.14.3.2.7',
        'des-EDE3-CBC' => '1.2.840.113549.3.7',
        'rc2CBC' => '1.2.840.113549.3.2',
        'rc5-CBC-PAD' => '1.2.840.113549.3.9',

        'aes128-CBC-PAD' => '2.16.840.1.101.3.4.1.2',
        'aes192-CBC-PAD' => '2.16.840.1.101.3.4.1.22',
        'aes256-CBC-PAD' => '2.16.840.1.101.3.4.1.42',
    ];
}
<?php

/**
 * ASN.1 PSS OIDs
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2025-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\OIDs;

/**
 * PSS OIDs
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class Hashes
{
    public const OIDs = [
        'md2' => '1.2.840.113549.2.2',
        'md4' => '1.2.840.113549.2.4',
        'md5' => '1.2.840.113549.2.5',
        'id-sha1' => '1.3.14.3.2.26',
        'id-sha256' => '2.16.840.1.101.3.4.2.1',
        'id-sha384' => '2.16.840.1.101.3.4.2.2',
        'id-sha512' => '2.16.840.1.101.3.4.2.3',
        // from PKCS1 v2.2
        'id-sha224' => '2.16.840.1.101.3.4.2.4',
        'id-sha512/224' => '2.16.840.1.101.3.4.2.5',
        'id-sha512/256' => '2.16.840.1.101.3.4.2.6',

        // from https://csrc.nist.gov/projects/computer-security-objects-register/algorithm-registration#Hash
        'id-sha3/224' => '2.16.840.1.101.3.4.2.7',
        'id-sha3/256' => '2.16.840.1.101.3.4.2.8',
        'id-sha3/384' => '2.16.840.1.101.3.4.2.9',
        'id-sha3/512' => '2.16.840.1.101.3.4.2.10',
        'id-shake128' => '2.16.840.1.101.3.4.2.11',
        // from RFC8419
        'id-shake256' => '2.16.840.1.101.3.4.2.12',
        'id-shake256-len' => '2.16.840.1.101.3.4.2.18',

        'id-mgf1' => '1.2.840.113549.1.1.8',
    ];
}
<?php

/**
 * ASN.1 CMS OIDs
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2025-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\OIDs;

/**
 * CMS OIDs
 *
 * OIDs from RFC5652
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class CMS
{
    public const OIDs = [
        'id-data' => '1.2.840.113549.1.7.1', // section 4
        'id-signedData' => '1.2.840.113549.1.7.2', // section 5
        'id-ct-compressedData' => '1.2.840.113549.1.9.16.1.9', // section 1.1
        'id-envelopedData' => '1.2.840.113549.1.7.3', // section 6
        'id-digestedData' => '1.2.840.113549.1.7.5', // section 7
        'id-encryptedData' => '1.2.840.113549.1.7.6', // section 8
        'id-ct-authData' => '1.2.840.113549.1.9.16.1.2', // section 9

        'id-contentType' => '1.2.840.113549.1.9.3', // section 11.1
        'id-messageDigest' => '1.2.840.113549.1.9.4', // section 11.2
        'id-signingTime' => '1.2.840.113549.1.9.5', // section 11.3
        'id-countersignature' => '1.2.840.113549.1.9.6', // section 11.3

        'pkcs-9-at-smimeCapabilities' => '1.2.840.113549.1.9.15', // https://tools.ietf.org/html/rfc2985

        'id-aa-signingCertificate' => '1.2.840.113549.1.9.16.2.12', // https://tools.ietf.org/html/rfc2634#section-5.4
        'id-aa-signingCertificateV2' => '1.2.840.113549.1.9.16.2.47', // https://tools.ietf.org/html/rfc5035#section-3

        'id-aa-contentIdentifier' => '1.2.840.113549.1.9.16.2.7',

        'id-alg-zlibCompress' => '1.2.840.113549.1.9.16.3.8', // from RFC3274

        // from https://www.etsi.org/deliver/etsi_en/319100_319199/31912201/01.01.00_30/en_31912201v010100v.pdf
        'id-aa-ets-mimeType' => '0.4.0.1733.2.1',
        'id-aa-ets-commitmentType' => '1.2.840.113549.1.9.16.2.16',
        'id-aa-ets-signerLocation' => '1.2.840.113549.1.9.16.2.17',
        'id-aa-ets-signerAttrV2' => '0.4.0.19122.1.1',
        'id-aa-ets-claimedSAML' => '0.4.0.19122.1.2',
        'id-aa-ets-contentTimestamp' => '1.2.840.113549.1.9.16.2.20',
        'id-aa-ets-sigPolicyId' => '1.2.840.113549.1.9.16.2.15',
        'id-spq-ets-uri' => '1.2.840.113549.1.9.16.5.2',
        'id-spq-ets-docspec' => '0.4.0.19122.2.1',
        'id-aa-ets-sigPolicyStore' => '0.4.0.19122.1.3',
        'id-aa-ets-archiveTimestampV3' => '0.4.0.1733.2.4',
        'id-aa-ets-certificateRefs' => '1.2.840.113549.1.9.16.2.21',
        'id-aa-ets-certValues' => '1.2.840.113549.1.9.16.2.23',
        'id-aa-ets-revocationRefs' => '1.2.840.113549.1.9.16.2.22',
        'id-aa-ets-revocationValues' => '1.2.840.113549.1.9.16.2.24',
        'id-aa-ets-attrCertificateRefs' => '1.2.840.113549.1.9.16.2.44',
        'id-aa-ets-attrRevocationRefs' => '1.2.840.1113549.1.9.16.2.45',
        'id-aa-ets-certCRLTimestamp' => '1.2.840.113549.1.9.16.2.26',
        'id-aa-ets-escTimeStamp' => '1.2.840.113549.1.9.16.2.25',

        // from https://datatracker.ietf.org/doc/html/rfc6211
        'id-aa-CMSAlgorithmProtection' => '1.2.840.113549.1.9.52',
        // from https://www.ietf.org/rfc/rfc3161.txt
        'id-aa-timeStampToken' => '1.2.840.113549.1.9.16.2.14',
        'id-ct-TSTInfo' => '1.2.840.113549.1.9.16.1.4',

        // from https://www.rfc-editor.org/rfc/rfc2634.html
        'id-aa-receiptRequest' => '1.2.840.113549.1.9.16.2.1',
        'id-ct-receipt' => '1.2.840.113549.1.9.16.1.1',
        'id-aa-contentHint' => '1.2.840.113549.1.9.16.2.4',
        'id-aa-msgSigDigest' => '1.2.840.113549.1.9.16.2.5',
        'id-aa-contentReference' => '1.2.840.113549.1.9.16.2.10',
        'id-aa-securityLabel' => '1.2.840.113549.1.9.16.2.2',
        'id-aa-equivalentLabels' => '1.2.840.113549.1.9.16.2.9',
        'id-aa-mlExpandHistory' => '1.2.840.113549.1.9.16.2.3',

        // from https://datatracker.ietf.org/doc/html/rfc3394.html
        'id-aes128-wrap' => '2.16.840.1.101.3.4.1.5',
        'id-aes192-wrap' => '2.16.840.1.101.3.4.1.25',
        'id-aes256-wrap' => '2.16.840.1.101.3.4.1.45',

        // from https://datatracker.ietf.org/doc/html/rfc5649
        'id-aes128-wrap-pad' => '2.16.840.1.101.3.4.1.8',
        'id-aes192-wrap-pad' => '2.16.840.1.101.3.4.1.28',
        'id-aes256-wrap-pad' => '2.16.840.1.101.3.4.1.48',

        // from https://datatracker.ietf.org/doc/html/rfc2630
        'id-alg-CMS3DESwrap' => '1.2.840.113549.1.9.16.3.6',
        'id-alg-CMSRC2wrap' => '1.2.840.113549.1.9.16.3.7',

        'id-alg-PWRI-KEK' => '1.2.840.113549.1.9.16.3.9',

        // https://datatracker.ietf.org/doc/html/rfc5753?utm_source=chatgpt.com#section-7.1.4
        'dhSinglePass-stdDH-sha1kdf-scheme' => '1.3.133.16.840.63.0.2', // x9-63-scheme
        'dhSinglePass-stdDH-sha224kdf-scheme' => '1.3.132.1.11.0', // secg-scheme
        'dhSinglePass-stdDH-sha256kdf-scheme' => '1.3.132.1.11.1',
        'dhSinglePass-stdDH-sha384kdf-scheme' => '1.3.132.1.11.2',
        'dhSinglePass-stdDH-sha512kdf-scheme' => '1.3.132.1.11.3',
        'dhSinglePass-cofactorDH-sha1kdf-scheme' => '1.3.133.16.840.63.0.3',
        'dhSinglePass-cofactorDH-sha224kdf-scheme' => '1.3.132.1.14.0',
        'dhSinglePass-cofactorDH-sha256kdf-scheme' => '1.3.132.1.14.1',
        'dhSinglePass-cofactorDH-sha384kdf-scheme' => '1.3.132.1.14.2',
        'dhSinglePass-cofactorDH-sha512kdf-scheme' => '1.3.132.1.14.3',
        'mqvSinglePass-sha1kdf-scheme' => '1.3.133.16.840.63.0.16',
        'mqvSinglePass-sha224kdf-scheme' => '1.3.132.1.15.0',
        'mqvSinglePass-sha256kdf-scheme' => '1.3.132.1.15.1',
        'mqvSinglePass-sha384kdf-scheme' => '1.3.132.1.15.2',
        'mqvSinglePass-sha512kdf-scheme' => '1.3.132.1.15.3',

        // from https://www.rfc-editor.org/rfc/rfc9629.html
        'id-ori-kem' => '1.2.840.113549.1.9.16.13.3',

        // from https://datatracker.ietf.org/doc/html/rfc8017#appendix-A.2.1
        'id-RSAES-OAEP' => '1.2.840.113549.1.1.7',
        'id-pSpecified' => '1.2.840.113549.1.1.9',

        // from https://datatracker.ietf.org/doc/html/rfc5083
        'id-ct-authEnvelopedData' => '1.2.840.113549.1.9.16.1.23',
        'id-aes128-CCM' => '2.16.840.1.101.3.4.1.7',
        'id-aes192-CCM' => '2.16.840.1.101.3.4.1.27',
        'id-aes256-CCM' => '2.16.840.1.101.3.4.1.47',
        'id-aes128-GCM' => '2.16.840.1.101.3.4.1.6',
        'id-aes192-GCM' => '2.16.840.1.101.3.4.1.26',
        'id-aes256-GCM' => '2.16.840.1.101.3.4.1.46',

        // from https://datatracker.ietf.org/doc/html/rfc8103
        'id-alg-AEADChaCha20Poly1305' => '1.2.840.113549.1.9.16.3.18',
    ];
}
<?php

/**
 * ASN.1 X.509 OIDs
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2025-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\OIDs;

/**
 * X.509 OIDs
 *
 * OIDs from RFC5280 and those RFCs mentioned in RFC5280#section-4.1.1.2
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class X509
{
    public const OIDs = [
        //'id-pkix' => '1.3.6.1.5.5.7',
        //'id-pe' => '1.3.6.1.5.5.7.1',
        //'id-qt' => '1.3.6.1.5.5.7.2',
        //'id-kp' => '1.3.6.1.5.5.7.3',
        //'id-ad' => '1.3.6.1.5.5.7.48',
        //'id-logo' => '1.3.6.1.5.5.7.20',
        'id-qt-cps' => '1.3.6.1.5.5.7.2.1',
        'id-qt-unotice' => '1.3.6.1.5.5.7.2.2',
        'id-ad-ocsp' => '1.3.6.1.5.5.7.48.1',
        'id-ad-caIssuers' => '1.3.6.1.5.5.7.48.2',
        'id-ad-timeStamping' => '1.3.6.1.5.5.7.48.3',
        'id-ad-caRepository' => '1.3.6.1.5.5.7.48.5',
        //'id-at' => '2.5.4',
        'id-at-name' => '2.5.4.41',
        'id-at-surname' => '2.5.4.4',
        'id-at-givenName' => '2.5.4.42',
        'id-at-initials' => '2.5.4.43',
        'id-at-generationQualifier' => '2.5.4.44',
        'id-at-commonName' => '2.5.4.3',
        'id-at-localityName' => '2.5.4.7',
        'id-at-stateOrProvinceName' => '2.5.4.8',
        'id-at-organizationName' => '2.5.4.10',
        'id-at-organizationalUnitName' => '2.5.4.11',
        'id-at-title' => '2.5.4.12',
        'id-at-description' => '2.5.4.13',
        'id-at-dnQualifier' => '2.5.4.46',
        'id-at-countryName' => '2.5.4.6',
        'id-at-serialNumber' => '2.5.4.5',
        'id-at-pseudonym' => '2.5.4.65',
        'id-at-postalCode' => '2.5.4.17',
        'id-at-streetAddress' => '2.5.4.9',
        'id-at-uniqueIdentifier' => '2.5.4.45',
        'id-at-role' => '2.5.4.72',
        'id-at-postalAddress' => '2.5.4.16',
        'id-at-organizationIdentifier' => '2.5.4.97',
        'jurisdictionOfIncorporationCountryName' => '1.3.6.1.4.1.311.60.2.1.3',
        'jurisdictionOfIncorporationStateOrProvinceName' => '1.3.6.1.4.1.311.60.2.1.2',
        'jurisdictionLocalityName' => '1.3.6.1.4.1.311.60.2.1.1',
        // Mark Certificate attributes, from the BIMI Group's Verified Mark Certificate
        // Requirements. the trademark attributes below succeed 1.3.6.1.4.1.311.60.2.1.4
        // through .7 of the arc above.
        // https://bimigroup.org/resources/VMC_Requirements_v1_4.pdf
        'trademarkOfficeName' => '1.3.6.1.4.1.53087.1.2',
        'trademarkCountryOrRegionName' => '1.3.6.1.4.1.53087.1.3',
        'trademarkRegistration' => '1.3.6.1.4.1.53087.1.4',
        'legalEntityIdentifier' => '1.3.6.1.4.1.53087.1.5',
        'wordMark' => '1.3.6.1.4.1.53087.1.6',
        'markType' => '1.3.6.1.4.1.53087.1.13',
        // used in place of the trademark attributes when the mark is established by
        // statute rather than by registration
        'statuteCountryName' => '1.3.6.1.4.1.53087.3.2',
        'statuteStateOrProvinceName' => '1.3.6.1.4.1.53087.3.3',
        'statuteLocalityName' => '1.3.6.1.4.1.53087.3.4',
        'statuteCitation' => '1.3.6.1.4.1.53087.3.5',
        'statuteURL' => '1.3.6.1.4.1.53087.3.6',
        // not a DN attribute but a certificate policy, asserted by every VMC in
        // certificatePolicies alongside the issuing CA's own policy
        'certificateGeneralPolicyIdentifier' => '1.3.6.1.4.1.53087.1.1',
        'id-at-businessCategory' => '2.5.4.15',
        'id-domainComponent' => '0.9.2342.19200300.100.1.25',
        // from RFC3039
        'id-pda-dateOfBirth' => '1.3.6.1.5.5.7.9.1',
        'id-pda-placeOfBirth' => '1.3.6.1.5.5.7.9.2',
        'id-pda-gender' => '1.3.6.1.5.5.7.9.3',
        'id-pda-countyOfCitizenship' => '1.3.6.1.5.5.7.9.4',
        'id-pda-countyOfResidence' => '1.3.6.1.5.5.7.9.5',

        //'pkcs-9' => '1.2.840.113549.1.9',
        'pkcs-9-at-emailAddress' => '1.2.840.113549.1.9.1',
        //'id-ce' => '2.5.29',
        'id-ce-authorityKeyIdentifier' => '2.5.29.35',
        'id-ce-subjectKeyIdentifier' => '2.5.29.14',
        'id-ce-keyUsage' => '2.5.29.15',
        'id-ce-privateKeyUsagePeriod' => '2.5.29.16',
        'id-ce-certificatePolicies' => '2.5.29.32',
        //'anyPolicy' => '2.5.29.32.0',

        'id-ce-policyMappings' => '2.5.29.33',

        'id-ce-subjectAltName' => '2.5.29.17',
        'id-ce-issuerAltName' => '2.5.29.18',
        'id-ce-subjectDirectoryAttributes' => '2.5.29.9',
        'id-ce-basicConstraints' => '2.5.29.19',
        'id-ce-nameConstraints' => '2.5.29.30',
        'id-ce-policyConstraints' => '2.5.29.36',
        'id-ce-cRLDistributionPoints' => '2.5.29.31',
        'id-ce-extKeyUsage' => '2.5.29.37',
        //'anyExtendedKeyUsage' => '2.5.29.37.0',
        'id-kp-serverAuth' => '1.3.6.1.5.5.7.3.1',
        'id-kp-clientAuth' => '1.3.6.1.5.5.7.3.2',
        'id-kp-codeSigning' => '1.3.6.1.5.5.7.3.3',
        'id-kp-emailProtection' => '1.3.6.1.5.5.7.3.4',
        'id-kp-timeStamping' => '1.3.6.1.5.5.7.3.8',
        'id-kp-OCSPSigning' => '1.3.6.1.5.5.7.3.9',
        'id-ce-inhibitAnyPolicy' => '2.5.29.54',
        'id-ce-freshestCRL' => '2.5.29.46',
        'id-pe-authorityInfoAccess' => '1.3.6.1.5.5.7.1.1',
        'id-pe-subjectInfoAccess' => '1.3.6.1.5.5.7.1.11',
        'id-ce-cRLNumber' => '2.5.29.20',
        'id-ce-issuingDistributionPoint' => '2.5.29.28',
        'id-ce-deltaCRLIndicator' => '2.5.29.27',
        'id-ce-cRLReasons' => '2.5.29.21',
        'id-ce-certificateIssuer' => '2.5.29.29',
        'id-ce-holdInstructionCode' => '2.5.29.23',
        //'holdInstruction' => '1.2.840.10040.2',
        'id-holdinstruction-none' => '1.2.840.10040.2.1',
        'id-holdinstruction-callissuer' => '1.2.840.10040.2.2',
        'id-holdinstruction-reject' => '1.2.840.10040.2.3',
        'id-ce-invalidityDate' => '2.5.29.24',
        'id-pe-qcStatements' => '1.3.6.1.5.5.7.1.3',

        // from http://www.etsi.org/deliver/etsi_ts/101800_101899/101862/01.03.01_60/ts_101862v010301p.pdf
        'id-etsi-qcs' => '0.4.0.1862.1',
        'id-etsi-qcs-QcCompliance' => '0.4.0.1862.1.1',
        'id-etsi-qcs-QcLimitValue' => '0.4.0.1862.1.2',
        'id-etsi-qcs-QcRetentionPeriod' => '0.4.0.1862.1.3',

        'id-etsi-qcs-QcSSCD' => '0.4.0.1862.1.4',

        'rsaEncryption' => '1.2.840.113549.1.1.1',
        'md2WithRSAEncryption' => '1.2.840.113549.1.1.2',
        'md5WithRSAEncryption' => '1.2.840.113549.1.1.4',
        'sha1WithRSAEncryption' => '1.2.840.113549.1.1.5',
        'sha224WithRSAEncryption' => '1.2.840.113549.1.1.14',
        'sha256WithRSAEncryption' => '1.2.840.113549.1.1.11',
        'sha384WithRSAEncryption' => '1.2.840.113549.1.1.12',
        'sha512WithRSAEncryption' => '1.2.840.113549.1.1.13',
        // from https://datatracker.ietf.org/doc/html/rfc8017#appendix-A.2.4
        'sha512-224WithRSAEncryption' => '1.2.840.113549.1.1.15',
        'sha512-256WithRSAEncryption' => '1.2.840.113549.1.1.16',

        'id-ecPublicKey' => '1.2.840.10045.2.1',
        'ecdsa-with-SHA1' => '1.2.840.10045.4.1',
        // from https://tools.ietf.org/html/rfc5758#section-3.2
        'ecdsa-with-SHA224' => '1.2.840.10045.4.3.1',
        'ecdsa-with-SHA256' => '1.2.840.10045.4.3.2',
        'ecdsa-with-SHA384' => '1.2.840.10045.4.3.3',
        'ecdsa-with-SHA512' => '1.2.840.10045.4.3.4',

        'id-dsa' => '1.2.840.10040.4.1',
        'id-dsa-with-sha1' => '1.2.840.10040.4.3',
        // from https://tools.ietf.org/html/rfc5758#section-3.1
        'id-dsa-with-sha224' => '2.16.840.1.101.3.4.3.1',
        'id-dsa-with-sha256' => '2.16.840.1.101.3.4.3.2',

        // from https://tools.ietf.org/html/rfc8410:
        'id-Ed25519' => '1.3.101.112',
        'id-Ed448' => '1.3.101.113',

        'id-RSASSA-PSS' => '1.2.840.113549.1.1.10',

        'dhKeyAgreement' => '1.2.840.113549.1.3.1',

        //'id-sha224' => '2.16.840.1.101.3.4.2.4',
        //'id-sha256' => '2.16.840.1.101.3.4.2.1',
        //'id-sha384' => '2.16.840.1.101.3.4.2.2',
        //'id-sha512' => '2.16.840.1.101.3.4.2.3',
        //'id-GostR3411-94-with-GostR3410-94' => '1.2.643.2.2.4',
        //'id-GostR3411-94-with-GostR3410-2001' => '1.2.643.2.2.3',
        //'id-GostR3410-2001' => '1.2.643.2.2.20',
        //'id-GostR3410-94' => '1.2.643.2.2.19',
        // Netscape Object Identifiers from "Netscape Certificate Extensions"
        'netscape' => '2.16.840.1.113730',
        'netscape-cert-extension' => '2.16.840.1.113730.1',
        'netscape-cert-type' => '2.16.840.1.113730.1.1',
        'netscape-comment' => '2.16.840.1.113730.1.13',
        'netscape-ca-policy-url' => '2.16.840.1.113730.1.8',
        // Logotypes, from https://datatracker.ietf.org/doc/html/rfc9399
        'id-pe-logotype' => '1.3.6.1.5.5.7.1.12',
        'id-logo-loyalty' => '1.3.6.1.5.5.7.20.1',
        'id-logo-background' => '1.3.6.1.5.5.7.20.2',
        'id-logo-certImage' => '1.3.6.1.5.5.7.20.3',
        // the following are X.509 extensions not supported by phpseclib
        'entrustVersInfo' => '1.2.840.113533.7.65.0',
        'verisignPrivate' => '2.16.840.1.113733.1.6.9',
        // for Certificate Signing Requests
        // see http://tools.ietf.org/html/rfc2985
        'pkcs-9-at-unstructuredName' => '1.2.840.113549.1.9.2', // PKCS #9 unstructured name
        'pkcs-9-at-challengePassword' => '1.2.840.113549.1.9.7', // Challenge password for certificate revocations
        'pkcs-9-at-extensionRequest' => '1.2.840.113549.1.9.14', // Certificate extension request
    ];
}
<?php

/**
 * ASN.1 EC OIDs
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2025-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\OIDs;

/**
 * EC OIDs
 *
 * the sec* curves are from the standards for efficient cryptography group
 * sect* curves are curves over binary finite fields
 * secp* curves are curves over prime finite fields
 * sec*r* curves are regular curves; sec*k* curves are koblitz curves
 * brainpool*r* curves are regular prime finite field curves
 * brainpool*t* curves are twisted versions of the brainpool*r* curves
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class Curves
{
    public const OIDs = [
        'prime192v1' => '1.2.840.10045.3.1.1', // J.5.1, example 1 (aka secp192r1)
        'prime192v2' => '1.2.840.10045.3.1.2', // J.5.1, example 2
        'prime192v3' => '1.2.840.10045.3.1.3', // J.5.1, example 3
        'prime239v1' => '1.2.840.10045.3.1.4', // J.5.2, example 1
        'prime239v2' => '1.2.840.10045.3.1.5', // J.5.2, example 2
        'prime239v3' => '1.2.840.10045.3.1.6', // J.5.2, example 3
        'prime256v1' => '1.2.840.10045.3.1.7', // J.5.3, example 1 (aka secp256r1)

        // https://tools.ietf.org/html/rfc5656#section-10
        'nistp256' => '1.2.840.10045.3.1.7', // aka secp256r1
        'nistp384' => '1.3.132.0.34', // aka secp384r1
        'nistp521' => '1.3.132.0.35', // aka secp521r1

        'nistk163' => '1.3.132.0.1', // aka sect163k1
        'nistp192' => '1.2.840.10045.3.1.1', // aka secp192r1
        'nistp224' => '1.3.132.0.33', // aka secp224r1
        'nistk233' => '1.3.132.0.26', // aka sect233k1
        'nistb233' => '1.3.132.0.27', // aka sect233r1
        'nistk283' => '1.3.132.0.16', // aka sect283k1
        'nistk409' => '1.3.132.0.36', // aka sect409k1
        'nistb409' => '1.3.132.0.37', // aka sect409r1
        'nistt571' => '1.3.132.0.38', // aka sect571k1

        // from https://tools.ietf.org/html/rfc5915
        'secp192r1' => '1.2.840.10045.3.1.1', // aka prime192v1
        'sect163k1' => '1.3.132.0.1',
        'sect163r2' => '1.3.132.0.15',
        'secp224r1' => '1.3.132.0.33',
        'sect233k1' => '1.3.132.0.26',
        'sect233r1' => '1.3.132.0.27',
        'secp256r1' => '1.2.840.10045.3.1.7', // aka prime256v1
        'sect283k1' => '1.3.132.0.16',
        'sect283r1' => '1.3.132.0.17',
        'secp384r1' => '1.3.132.0.34',
        'sect409k1' => '1.3.132.0.36',
        'sect409r1' => '1.3.132.0.37',
        'secp521r1' => '1.3.132.0.35',
        'sect571k1' => '1.3.132.0.38',
        'sect571r1' => '1.3.132.0.39',
        // from http://www.secg.org/SEC2-Ver-1.0.pdf
        'secp112r1' => '1.3.132.0.6',
        'secp112r2' => '1.3.132.0.7',
        'secp128r1' => '1.3.132.0.28',
        'secp128r2' => '1.3.132.0.29',
        'secp160k1' => '1.3.132.0.9',
        'secp160r1' => '1.3.132.0.8',
        'secp160r2' => '1.3.132.0.30',
        'secp192k1' => '1.3.132.0.31',
        'secp224k1' => '1.3.132.0.32',
        'secp256k1' => '1.3.132.0.10',

        'sect113r1' => '1.3.132.0.4',
        'sect113r2' => '1.3.132.0.5',
        'sect131r1' => '1.3.132.0.22',
        'sect131r2' => '1.3.132.0.23',
        'sect163r1' => '1.3.132.0.2',
        'sect193r1' => '1.3.132.0.24',
        'sect193r2' => '1.3.132.0.25',
        'sect239k1' => '1.3.132.0.3',

        // from http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.202.2977&rep=rep1&type=pdf#page=36
        /*
        'c2pnb163v1' => '1.2.840.10045.3.0.1', // J.4.1, example 1
        'c2pnb163v2' => '1.2.840.10045.3.0.2', // J.4.1, example 2
        'c2pnb163v3' => '1.2.840.10045.3.0.3', // J.4.1, example 3
        'c2pnb172w1' => '1.2.840.10045.3.0.4', // J.4.2, example 1
        'c2tnb191v1' => '1.2.840.10045.3.0.5', // J.4.3, example 1
        'c2tnb191v2' => '1.2.840.10045.3.0.6', // J.4.3, example 2
        'c2tnb191v3' => '1.2.840.10045.3.0.7', // J.4.3, example 3
        'c2onb191v4' => '1.2.840.10045.3.0.8', // J.4.3, example 4
        'c2onb191v5' => '1.2.840.10045.3.0.9', // J.4.3, example 5
        'c2pnb208w1' => '1.2.840.10045.3.0.10', // J.4.4, example 1
        'c2tnb239v1' => '1.2.840.10045.3.0.11', // J.4.5, example 1
        'c2tnb239v2' => '1.2.840.10045.3.0.12', // J.4.5, example 2
        'c2tnb239v3' => '1.2.840.10045.3.0.13', // J.4.5, example 3
        'c2onb239v4' => '1.2.840.10045.3.0.14', // J.4.5, example 4
        'c2onb239v5' => '1.2.840.10045.3.0.15', // J.4.5, example 5
        'c2pnb272w1' => '1.2.840.10045.3.0.16', // J.4.6, example 1
        'c2pnb304w1' => '1.2.840.10045.3.0.17', // J.4.7, example 1
        'c2tnb359v1' => '1.2.840.10045.3.0.18', // J.4.8, example 1
        'c2pnb368w1' => '1.2.840.10045.3.0.19', // J.4.9, example 1
        'c2tnb431r1' => '1.2.840.10045.3.0.20', // J.4.10, example 1
        */

        // http://www.ecc-brainpool.org/download/Domain-parameters.pdf
        // https://tools.ietf.org/html/rfc5639
        'brainpoolP160r1' => '1.3.36.3.3.2.8.1.1.1',
        'brainpoolP160t1' => '1.3.36.3.3.2.8.1.1.2',
        'brainpoolP192r1' => '1.3.36.3.3.2.8.1.1.3',
        'brainpoolP192t1' => '1.3.36.3.3.2.8.1.1.4',
        'brainpoolP224r1' => '1.3.36.3.3.2.8.1.1.5',
        'brainpoolP224t1' => '1.3.36.3.3.2.8.1.1.6',
        'brainpoolP256r1' => '1.3.36.3.3.2.8.1.1.7',
        'brainpoolP256t1' => '1.3.36.3.3.2.8.1.1.8',
        'brainpoolP320r1' => '1.3.36.3.3.2.8.1.1.9',
        'brainpoolP320t1' => '1.3.36.3.3.2.8.1.1.10',
        'brainpoolP384r1' => '1.3.36.3.3.2.8.1.1.11',
        'brainpoolP384t1' => '1.3.36.3.3.2.8.1.1.12',
        'brainpoolP512r1' => '1.3.36.3.3.2.8.1.1.13',
        'brainpoolP512t1' => '1.3.36.3.3.2.8.1.1.14',
    ];
}
<?php

/**
 * Integer
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2025-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Types;

use phpseclib4\Math\BigInteger;

/**
 * Integer
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class Integer extends BigInteger implements BaseType
{
    use Common;

    public const TYPE = 2;

    public string $mappedValue;

    public function __debugInfo(): array
    {
        return isset($this->mappedValue) ?
            ['value' => $this->mappedValue] :
            parent::__debugInfo();
    }

    public function __toString(): string
    {
        return $this->mappedValue ?? parent::__toString();
    }
}
<?php

/**
 * ASN.1 Generalized Time
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2025-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Types;

/**
 * ASN.1 Generalized Time
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class GeneralizedTime extends \DateTime implements BaseType
{
    use Common;

    public const TYPE = 24;

    public function __toString(): string
    {
        return $this->format('Y-m-d H:i:s');
    }
}
<?php

/**
 * ASN.1 Teletex String
 *
 * CCITT and T.101 character sets
 * https://en.wikipedia.org/wiki/Teletext_character_set
 *
 * "The character string type TeletexString is a superset of
 *  PrintableString.  TeletexString supports a fairly standard (ASCII-
 *  like) Latin character set: Latin characters with non-spacing accents
 *  and Japanese characters."
 * -- RFC5280, Appendix B. ASN.1 Notes
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2025-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Types;

/**
 * ASN.1 Teletex String
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class TeletexString extends BaseString
{
    public const TYPE = 20;
    protected const SIZE = 1;
}
<?php

/**
 * ASN.1 Universal String
 *
 * ISO10646 character set
 * https://en.wikipedia.org/wiki/Universal_Coded_Character_Set
 *
 * "The character string type UniversalString supports any of the
 *  characters allowed by [ISO10646].  ISO 10646 is the Universal
 *  multiple-octet coded Character Set (UCS)."
 * -- RFC5280, Appendix B. ASN.1 Notes
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2025-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Types;

/**
 * ASN.1 Universal String
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class UniversalString extends BaseString
{
    public const TYPE = 28;
    protected const SIZE = 4;
}
<?php

/**
 * ASN.1 IA5 String
 *
 * International ASCII characters (International Alphabet 5)
 * https://en.wikipedia.org/wiki/T.50_(standard)
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2025-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Types;

/**
 * ASN.1 IA5 String
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class IA5String extends BaseString
{
    public const TYPE = 22;
    protected const SIZE = 1;
}
<?php

/**
 * ASN.1 Base String
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2025-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Types;

use phpseclib4\Exception\{BadMethodCallException, CharacterConversionException};

/**
 * ASN.1 Base String
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class BaseString implements BaseType
{
    use Common;

    protected const SIZE = -1;

    public function __construct(public string $value)
    {
    }

    public function __toString(): string
    {
        return $this->value;
    }

    /**
     * String type conversion
     *
     * This is a lazy conversion, dealing only with character size.
     * No real conversion table is used.
     */
    private function convert(string $class): self
    {
        if (!$this->isConvertable()) {
            throw new BadMethodCallException('Unable to convert - ' . static::class . ' doesn\'t have a size constant associated with it');
        }
        //if (!defined("$class::SIZE")) {
        //    throw new BadMethodCallException("Unable to convert - $class doesn't have a size constant associated with it");
        //}

        $insize = static::SIZE;
        $outsize = $class::SIZE;
        $in = $this->value;

        // altho in theory the following could save some computational resources
        // doing so risks making phpseclib 3.0 give different output than phpseclib 4.0
        // when the underlying string type is malformed.
        // see, for example, the cert in the testPostalAddress() method. the id-at-organizationName
        // attribute for the subject DN isn't valid UTF-8 so converting UTF-8 to UTF-8 results in
        // an error whereas if you just omit the conversion process all together, as the following
        // code does, then no error is issued
        //if ($insize == $outsize) {
        //    return new $class($in);
        //}

        $inlength = strlen($in);
        $out = '';

        for ($i = 0; $i < $inlength;) {
            if ($inlength - $i < $insize) {
                throw new CharacterConversionException('Malformed string detected: Input string needs at least ' . ($insize - $inlength + $i) . ' more bytes');
            }

            // Get an input character as a 32-bit value.
            $c = ord($in[$i++]);
            switch (true) {
                case $insize == 4:
                    $c = ($c << 8) | ord($in[$i++]);
                    $c = ($c << 8) | ord($in[$i++]);
                    // no break
                case $insize == 2:
                    $c = ($c << 8) | ord($in[$i++]);
                    // no break
                case $insize == 1:
                    break;
                // only single byte UTF-8 characters have the first bit set to 1
                case ($c & 0x80) == 0x00:
                    break;
                case ($c & 0x40) == 0x00:
                    // "In a sequence of n octets, n>1, the initial octet has the n higher-order bits set to 1,
                    //  followed by a bit set to 0."
                    // -- https://datatracker.ietf.org/doc/html/rfc2279#section-2
                    throw new CharacterConversionException('Malformed UTF-8 string detected (AND with 0x40 != 0x00)');
                default:
                    $bit = 6;
                    do {
                        // $bit > 25 is because the most number of non-fixed bits one can have in a UTF-8 character is 31
                        // and 25 + 6 = 31
                        // $i >= $inlength is because we don't want to try to extract more characters than are actually in
                        // the input string
                        // (ord($in[$i]) & 0xC0) != 0x80 makes sure that the first two bits are 10 (as above)
                        // RFC2279#section-2 elaborates
                        if ($bit > 25 || $i >= $inlength || (ord($in[$i]) & 0xC0) != 0x80) {
                            throw new CharacterConversionException('Malformed UTF-8 string detected');
                        }
                        $c = ($c << 6) | (ord($in[$i++]) & 0x3F);
                        $bit += 5;
                        $mask = 1 << $bit;
                    } while ($c & $bit);
                    $c &= $mask - 1;
                    break;
            }

            // Convert and append the character to output string.
            $v = '';
            $origC = $c;
            switch (true) {
                case $outsize == 4:
                    $v .= chr($c & 0xFF);
                    $c >>= 8;
                    $v .= chr($c & 0xFF);
                    $c >>= 8;
                    // no break
                case $outsize == 2:
                    $v .= chr($c & 0xFF);
                    $c >>= 8;
                    // no break
                case $outsize == 1:
                    $v .= chr($c & 0xFF);
                    $c >>= 8;
                    if ($c) {
                        throw new CharacterConversionException('Character requiring ' . floor(log($origC, 2)) . ' bits found but only ' . ($outsize << 3) . ' bits are available per character in new format');
                    }
                    break;
                // 1 << 31 == 0x8000000. we do the former vs the latter because the latter doesn't work well on 32-bit PHP installs
                case ($c & (1 << 31)) != 0:
                    throw new CharacterConversionException('Character requiring 32 bits found but only 31 bits are available per character in new format');
                case $c >= 0x04000000:
                    $v .= chr(0x80 | ($c & 0x3F));
                    $c = ($c >> 6) | 0x04000000;
                    // no break
                case $c >= 0x00200000:
                    $v .= chr(0x80 | ($c & 0x3F));
                    $c = ($c >> 6) | 0x00200000;
                    // no break
                case $c >= 0x00010000:
                    $v .= chr(0x80 | ($c & 0x3F));
                    $c = ($c >> 6) | 0x00010000;
                    // no break
                case $c >= 0x00000800:
                    $v .= chr(0x80 | ($c & 0x3F));
                    $c = ($c >> 6) | 0x00000800;
                    // no break
                case $c >= 0x00000080:
                    $v .= chr(0x80 | ($c & 0x3F));
                    $c = ($c >> 6) | 0x000000C0;
                    // no break
                default:
                    $v .= chr($c);
            }
            $out .= strrev($v);
        }
        return new $class($out);
    }

    public function isConvertable(): bool
    {
        return defined('static::SIZE');
    }

    public function toUTF8String(): UTF8String
    {
        return $this->convert(UTF8String::class);
    }

    public function toBMPString(): BMPString
    {
        return $this->convert(BMPString::class);
    }

    public function toUniversalString(): UniversalString
    {
        return $this->convert(UniversalString::class);
    }

    public function toPrintableString(): PrintableString
    {
        return $this->convert(PrintableString::class);
    }

    public function toTeletexString(): TeletexString
    {
        return $this->convert(TeletexString::class);
    }

    public function toIA5String(): IA5String
    {
        return $this->convert(IA5String::class);
    }

    public function toVisibleString(): VisibleString
    {
        return $this->convert(VisibleString::class);
    }
}
<?php

/**
 * ASN.1 Numeric String
 *
 * 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, and SPACE
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Types;

/**
 * ASN.1 Numeric String
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class NumericString extends BaseString
{
    public const TYPE = 18;
}
<?php

/**
 * ASN.1 Visible String
 *
 * International ASCII printing character sets
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Types;

/**
 * ASN.1 Visible String
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class VisibleString extends BaseString
{
    public const TYPE = 26;
    protected const SIZE = 1;
}
<?php

/**
 * ASN.1 General String
 *
 * all registered graphic and character sets plus SPACE and DELETE
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Types;

/**
 * ASN.1 General String
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class GeneralString extends BaseString
{
    public const TYPE = 27;
}
<?php

/**
 * Generic ASN.1 Type Helper functions
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2025-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Types;

use phpseclib4\Exception\BadMethodCallException;

/**
 * Generic ASN.1 Type Helper functions
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
trait Common
{
    public array $metadata = [];

    public function addMetadata(array $metadata): void
    {
        $this->metadata = $metadata;
    }

    public function enableForcedCache(): void
    {
        $this->metadata['forcedCache'] = true;
    }

    public function disableForcedCache(): void
    {
        unset($this->metadata['forcedCache']);
    }

    public function isCacheForced(): bool
    {
        return isset($this->metadata['forcedCache']) && $this->metadata['forcedCache'];
    }

    public function setWrapping(string $wrapping): void
    {
        $this->metadata['wrapping'] = $wrapping;
    }

    public function hasWrapping(): bool
    {
        return !empty($this->metadata['wrapping']);
    }

    public function getEncodedWithWrapping(): string
    {
        return ($this->metadata['wrapping'] ?? '') . $this->metadata['rawheader'] . $this->metadata['content'];
    }

    public function hasEncoded(): bool
    {
        return isset($this->metadata['content'], $this->metadata['rawheader']);
    }

    public function getEncoded(): string
    {
        if (!isset($this->metadata['content'], $this->metadata['rawheader'])) {
            throw new BadMethodCallException('Encoded data is not available');
        }
        return $this->metadata['rawheader'] . $this->metadata['content'];
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function getEncodedWithoutHeader(): string
    {
        if (!isset($this->metadata['content'])) {
            throw new BadMethodCallException('Encoded data is not available');
        }
        return $this->metadata['content'];
    }

    public function getEncodedLength(): int
    {
        if (!isset($this->metadata['content'], $this->metadata['rawheader'])) {
            throw new BadMethodCallException('Encoded data is not available');
        }
        return strlen($this->metadata['rawheader']) + strlen($this->metadata['content']);
    }

    public function setEncoded(string $header, string $encoded): void
    {
        $this->metadata['rawheader'] = $header;
        $this->metadata['content'] = $encoded;
    }

    // doesn't work on UTCTime or GeneralizedTime per https://github.com/php/php-src/issues/11310
    public function __debugInfo(): array
    {
        $temp = get_object_vars($this);
        unset($temp['metadata']);
        return $temp;
    }

    public function hasTypeID(): bool
    {
        try {
            $this->getTypeID();
            return true;
        } catch (\Exception) {
            return false;
        }
    }

    /** @psalm-suppress PossiblyUnusedReturnValue, InvalidReturnType */
    public function getTypeID(): int
    {
        $reflection = new \ReflectionClassConstant(static::class, 'TYPE');
        return $reflection->getValue();
    }
}
<?php

/**
 * Boolean
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Types;

/**
 * Boolean
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class Boolean implements BaseType
{
    use Common;

    public const TYPE = 1;

    public function __construct(public bool $value)
    {
    }
}
<?php

/**
 * ASN.1 UTC Time
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Types;

/**
 * ASN.1 UTC Time
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class UTCTime extends \DateTime implements BaseType
{
    use Common;

    public const TYPE = 23;

    public function __toString(): string
    {
        return $this->format('Y-m-d H:i:s');
    }
}
<?php

/**
 * ASN.1 Octet String
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Types;

/**
 * ASN.1 Octet String
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class OctetString extends BaseString
{
    public const TYPE = 4;

    public function __debugInfo(): array
    {
        return ['value' => bin2hex($this->value)];
    }
}
<?php

/**
 * ASN.1 BMP String
 *
 * Basic Multilingual Plane of ISO/IEC/ITU 10646-1
 * https://en.wikipedia.org/wiki/Universal_Coded_Character_Set
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2025-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Types;

/**
 * ASN.1 BMP String
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class BMPString extends BaseString
{
    public const TYPE = 30;
    protected const SIZE = 2;
}
<?php

/**
 * ASN.1 Bit String
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2025-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Types;

use phpseclib4\Exception\InvalidStateException;

/**
 * ASN.1 Bit String
 *
 * The use of the \ArrayAccess, \Countable and \Iterator interfaces is due to stuff like
 * \phpseclib4\File\ASN1\Maps\KeyUsage
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @implements \ArrayAccess<int, mixed>
 * @implements \Iterator<int, mixed>
 */
class BitString extends BaseString implements \ArrayAccess, \Countable, \Iterator
{
    public array $mappedValue;
    public const TYPE = 3;

    private function preCheck(): void
    {
        if (!isset($this->mappedValue)) {
            throw new InvalidStateException('mappedValue needs to be set for this functionality to be used');
        }
    }

    public function contains(string $key): bool
    {
        $this->preCheck();
        return in_array($key, $this->mappedValue);
    }

    public function offsetExists(mixed $offset): bool
    {
        $this->preCheck();
        return isset($this->mappedValue[$offset]);
    }

    public function offsetGet(mixed $offset): mixed
    {
        $this->preCheck();
        return $this->mappedValue[$offset];
    }

    public function offsetSet(mixed $offset, mixed $value): void
    {
        $this->preCheck();
        $this->mappedValue[$offset] = $value;
    }

    public function offsetUnset(mixed $offset): void
    {
        $this->preCheck();
        unset($this->mappedValue[$offset]);
    }

    public function count(): int
    {
        $this->preCheck();
        return count($this->mappedValue);
    }

    public function current(): mixed
    {
        $this->preCheck();
        return current($this->mappedValue);
    }

    public function key(): mixed
    {
        $this->preCheck();
        return key($this->mappedValue);
    }

    public function next(): void
    {
        $this->preCheck();
        next($this->mappedValue);
    }

    public function rewind(): void
    {
        $this->preCheck();
        reset($this->mappedValue);
    }

    public function valid(): bool
    {
        $this->preCheck();
        return isset($this->mappedValue[key($this->mappedValue)]);
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function toArray(): array
    {
        $this->preCheck();
        return $this->mappedValue;
    }

    public function __debugInfo(): array
    {
        return $this->mappedValue ?? ['value' => bin2hex($this->value)];
    }
}
<?php

/**
 * ASN.1 Graphic String
 *
 * all registered G sets and SPACE
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Types;

/**
 * ASN.1 Graphic String
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class GraphicString extends BaseString
{
}
<?php

/**
 * ASN.1 UTF8 String
 *
 * any character from a recognized alphabet (including ASCII control characters)
 * https://en.wikipedia.org/wiki/UTF-8
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2025-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Types;

/**
 * ASN.1 UTF8 String
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class UTF8String extends BaseString
{
    public const TYPE = 12;
    protected const SIZE = 0;
}
<?php

/**
 * ASN.1 Printable String
 *
 * "The character string type PrintableString supports a very basic Latin
 *  character set: the lowercase letters 'a' through 'z', uppercase
 *  letters 'A' through 'Z', the digits '0' through '9', eleven special
 *  characters ' = ( ) + , - . / : ? and space.
 *
 *  Implementers should note that the at sign ('@') and underscore ('_')
 *  characters are not supported by the ASN.1 type PrintableString."
 * -- RFC5280, Appendix B. ASN.1 Notes
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2025-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Types;

/**
 * ASN.1 Printable String
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class PrintableString extends BaseString
{
    public const TYPE = 19;
    protected const SIZE = 1;
}
<?php

/**
 * Null
 *
 * Doing isset($a['key']) will return null if $a['key'] === null.
 * Sure, with an array, you could do array_key_exists(), instead, but
 * phpseclib is actually returning ArrayAccess objects - not arrays -
 * so that won't work.
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2025-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Types;

/**
 * Null
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class ExplicitNull implements BaseType
{
    use Common;

    public const TYPE = 5;
}
<?php

/**
 * OID (Object Identifier)
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2025-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Types;

use phpseclib4\File\ASN1;
use phpseclib4\File\ASN1\Element;

/**
 * OID (Object Identifier)
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class OID implements BaseType
{
    use Common;

    public const TYPE = 6;

    public string $name;

    /**
     * Constructor
     */
    public function __construct(public string|Element $value)
    {
        //$this->tryToSetName();
    }

    public function __debugInfo(): array
    {
        if ($this->value instanceof Element) {
            $this->value = ASN1::decodeOID($this->value->value)->value;
        }
        $output = ['value' => $this->value];
        $this->tryToSetName();
        if (isset($this->name)) {
            $output['name'] = $this->name;
        }
        return $output;
    }

    public function __toString(): string
    {
        if ($this->value instanceof Element) {
            $this->value = ASN1::decodeOID($this->value->value)->value;
        }
        $this->tryToSetName();
        return $this->name ?? $this->value;
    }

    private function tryToSetName(): void
    {
        if (!isset($this->name)) {
            if (preg_match('#^\d[\d\.]+\d$#', $this->value)) {
                $temp = ASN1::getNameFromOID($this->value);
                if ($temp != $this->value) {
                    $this->name = $temp;
                }
            } else {
                $temp = ASN1::getOIDFromName($this->value);
                if ($temp != $this->value) {
                    $this->name = $this->value;
                    $this->value = $temp;
                }
            }
        }
    }
}
<?php

/**
 * ASN.1 Choice
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2025-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Types;

use phpseclib4\Common\Functions\Strings;
use phpseclib4\Exception\{
    BadMethodCallException,
    InvalidArgumentException,
    UnexpectedValueException,
    UnsupportedValueException
};
use phpseclib4\File\ASN1\Constructed;
use phpseclib4\File\{ASN1, X509};
use phpseclib4\File\CMS\EnvelopedData\KeyAgreeRecipient\EncryptedKey;
use phpseclib4\File\CMS\EnvelopedData\Recipient;
use phpseclib4\File\CMS\SignedData\Signer;

/**
 * ASN.1 Choice
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @implements \ArrayAccess<string, mixed>
 * @implements \Iterator<string, mixed>
 */
class Choice implements \ArrayAccess, \Countable, \Iterator, BaseType
{
    public array $rules = [];
    public Choice|Constructed|null $parent = null;
    public int $depth = 0;
    public int|string $key;
    private bool $forcedCache = false;
    private bool $iteratorStart = true;

    /**
     * Constructor
     */
    public function __construct(public string $index, public mixed $value)
    {
    }

    public function __debugInfo(): array
    {
        if ($this->value instanceof X509) {
            $this->value->enableHideFullDecode();
        }
        if (isset($this->rules[$this->index]) && ($this->value instanceof Constructed || $this->value instanceof Choice)) {
            $this->value->rules = $this->rules[$this->index];
        }
        return [$this->index => $this->value];
    }

    public function enableForcedCache(): void
    {
        $this->forcedCache = true;
    }

    public function disableForcedCache(): void
    {
        $this->forcedCache = false;
    }

    public function isCacheForced(): bool
    {
        return $this->forcedCache;
    }

    public function setWrapping(string $wrapping): void
    {
        if ($this->value instanceof BaseType) {
            $this->value->setWrapping($wrapping);
        }
    }

    public function setEncoded(string $header, string $encoded): void
    {
        if ($this->value instanceof BaseType) {
            $this->value->setEncoded($header, $encoded);
        }
    }

    public function hasTypeID(): bool
    {
        return $this->value instanceof BaseType ? $this->value->hasTypeID() : false;
    }

    public function hasWrapping(): bool
    {
        return $this->value instanceof BaseType && $this->value->hasWrapping();
    }

    public function getEncodedWithWrapping(): string
    {
        return $this->value instanceof BaseType ? $this->value->getEncodedWithWrapping() : '';
    }

    public function hasEncoded(): bool
    {
        return $this->value instanceof BaseType && $this->value->hasEncoded();
    }

    public function getEncoded(): string
    {
        return $this->value instanceof BaseType ? $this->value->getEncoded() : '';
    }

    public function getEncodedLength(): int
    {
        if (!$this->value instanceof BaseType) {
            throw new UnsupportedValueException('Unable to get encoded length of non-Base type');
        }
        return $this->value->getEncodedLength();
    }

    public function offsetExists(mixed $offset): bool
    {
        return $offset == $this->index;
    }

    public function &offsetGet(mixed $offset): mixed
    {
        if ($offset != $this->index) {
            throw new UnexpectedValueException("The requested offset '$offset' was not found - did you mean '{$this->index}'?");
        }
        if (($this->value instanceof Constructed || $this->value instanceof Choice) && !$this->value->parent) {
            $this->value->parent = $this;
            if (isset($this->rules[$this->index])) {
                $this->value->rules = $this->rules[$offset];
            }
            if (isset($this->value->depth)) {
                $this->value->depth = $this->depth + 1;
                if (isset($this->key)) {
                    $this->value->key = $this->key;
                }
            }
        }
        return $this->value;
    }

    public function offsetSet(mixed $offset, mixed $value): void
    {
        if (!Strings::is_stringable($offset)) {
            throw new BadMethodCallException('Only offsets that can be cast to strings are supported');
        }

        $this->index = "$offset";
        $this->value = $value;

        if (ASN1::invalidateCache()) {
            $this->invalidateCache();
        }
    }

    public function offsetUnset(mixed $offset): void
    {
        if ($offset == $this->index) {
            unset($this->index, $this->value);
        }
    }

    public function __toString(): string
    {
        if (!Strings::is_stringable($this->value)) {
            $reflect = new \ReflectionClass($this->value);
            throw new InvalidArgumentException($reflect->getShortName() . ' isn\'t stringable');
        }
        return (string) $this->value;
    }

    public function count(): int
    {
        return 1;
    }

    public function toArray(bool $convertPrimitives = false): array
    {
        if ($this->value instanceof Constructed || $this->value instanceof Choice) {
            if (isset($this->rules[$this->index])) {
                $this->value->rules = $this->rules[$this->index];
            }
            return [$this->index => $this->value->toArray($convertPrimitives)];
        }
        if ($this->value instanceof Signer || $this->value instanceof Recipient || $this->value instanceof EncryptedKey) {
            return [$this->index => $this->value->toArray($convertPrimitives)];
        }
        return [$this->index => $convertPrimitives ? ASN1::convertToPrimitive($this->value) : $this->value];
    }

    public function reconstituteKeyHelper(): ?string
    {
        if (!isset($this->key)) {
            return null;
        }
        return !isset($this->parent) || !isset($this->parent->key) ? (string) $this->key : $this->parent->reconstituteKeyHelper() . '/' . $this->key;
    }

    public function invalidateCache(): void
    {
        if ($this->parent) {
            $this->parent->invalidateCache();
        }
    }

    public function rewind(): void
    {
        $this->iteratorStart = true;
    }

    public function current(): mixed
    {
        return $this->value;
    }

    public function key(): mixed
    {
        return $this->index;
    }

    public function next(): void
    {
        $this->iteratorStart = false;
    }

    public function valid(): bool
    {
        return $this->iteratorStart;
    }
}
<?php

/**
 * ASN.1 Base Type
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2025-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Types;

/**
 * ASN.1 Base Type
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
interface BaseType
{
    public function enableForcedCache(): void;
    /** @psalm-suppress PossiblyUnusedMethod */
    public function disableForcedCache(): void;
    public function isCacheForced(): bool;
    public function setWrapping(string $wrapping): void;
    public function hasWrapping(): bool;
    public function getEncodedWithWrapping(): string;
    public function hasEncoded(): bool;
    public function getEncoded(): string;
    public function getEncodedLength(): int;
    public function setEncoded(string $header, string $encoded): void;
    public function hasTypeID(): bool;
}
<?php

/**
 * ASN.1 Videotex String
 *
 * CCITT's T.100 and T.101 character sets
 * https://en.wikipedia.org/wiki/Videotex_character_set
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2016-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1\Types;

/**
 * ASN.1 Videotex String
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class VideotexString extends BaseString
{
    public const TYPE = 21;
}
<?php

/**
 * ASN.1 Raw Element
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2014-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1;

/**
 * ASN.1 Raw Element
 *
 * An ASN.1 ANY mapping will return an ASN1\Element object. Use of this object
 * will also bypass the normal encoding rules in ASN1::encodeDER()
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class Element
{
    /** @psalm-suppress PossiblyUnusedProperty */
    public array $metadata = [];

    /**
     * Constructor
     */
    public function __construct(public string $value)
    {
    }

    public function __debugInfo(): array
    {
        return ['value' => bin2hex($this->value)];
    }

    public function __toString(): string
    {
        return $this->value;
    }

    public function getEncoded(): string
    {
        return $this->value;
    }

    public function addMetadata(array $metadata): void
    {
        $this->metadata = $metadata;
    }
}
<?php

/**
 * ASN.1 Malformed Data Element
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1;

/**
 * ASN.1 Malformed Data Element
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class MalformedData extends Element
{
}
<?php

/**
 * ASN.1 Excessively Deep Data Element
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2015-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File\ASN1;

/**
 * ASN.1 Excessively Deep Data Element
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class ExcessivelyDeepData extends Element
{
}
<?php

/**
 * Pure-PHP CMS Parser
 *
 * PHP version 8.1+
 *
 * Encode and decode CMS files.
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File;

use phpseclib4\Exception\{UnexpectedValueException, UnsupportedValueException};
use phpseclib4\File\ASN1\Constructed;

/**
 * Pure-PHP CMS Parser
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class CMS
{
    public const ISSUER_AND_DN = 1;
    public const KEY_ID = 2;
    public static bool $binary = false;

    public static function load(string $cms, int $mode = ASN1::FORMAT_AUTO_DETECT): CMS\SignedData|CMS\CompressedData|CMS\EncryptedData|CMS\DigestedData
    {
        ASN1::loadOIDs('CMS');

        if ($mode != ASN1::FORMAT_DER) {
            $newcms = ASN1::extractBER($cms);
            if ($mode == ASN1::FORMAT_PEM && $cms == $newcms) {
                throw new UnexpectedValueException('Unable to decode PEM');
            }
            $cms = $newcms;
        }

        $pos = 0;
        $result = ASN1::decodeTag($cms, $pos);
        switch (true) {
            case !$result['constructed']:
            case $result['tag'] !== ASN1::TYPE_SEQUENCE:
            case $result['class'] !== ASN1::CLASS_UNIVERSAL:
                throw new UnexpectedValueException('Invalid tag found (expected a universal SEQUENCE tag; found ' . ASN1::convertTypeConstantToString($result['tag']) . ')');
        }
        ASN1::decodeLength($cms, $pos);
        $result = ASN1::decodeTag($cms, $pos);
        switch (true) {
            case $result['constructed']:
            case $result['tag'] !== ASN1::TYPE_OBJECT_IDENTIFIER:
            case $result['class'] !== ASN1::CLASS_UNIVERSAL:
                throw new UnexpectedValueException('Invalid tag found (expected a universal OID tag; found ' . ASN1::convertTypeConstantToString($result['tag']) . ')');
        }
        $length = ASN1::decodeLength($cms, $pos);
        $oid = substr($cms, $pos, $length);
        $contentType = (string) ASN1::decodeOID($oid);
        switch ($contentType) {
            case 'id-signedData':
                return CMS\SignedData::load($cms);
            case 'id-ct-compressedData':
                return CMS\CompressedData::load($cms);
            case 'id-envelopedData':
            case 'id-encryptedData':
                return CMS\EncryptedData::load($cms);
            case 'id-digestedData':
                ASN1::loadOIDs('Hashes');
                return CMS\DigestedData::load($cms);
        }
        throw new UnsupportedValueException("$contentType is not a supported OID");
    }

    public static function createSIDRID(X509 $x509, int $type): array
    {
        switch ($type) {
            case CMS::ISSUER_AND_DN:
                return ['issuerAndSerialNumber' =>
                    [
                        'issuer' => $x509['tbsCertificate']['issuer'],
                        'serialNumber' => $x509['tbsCertificate']['serialNumber'],
                    ]
                ];
            case CMS::KEY_ID:
                $keyID = $x509->getExtension('id-ce-subjectKeyIdentifier');
                if (!isset($keyID)) {
                    throw new UnexpectedValueException('id-ce-subjectKeyIdentifier is not present');
                }
                return ['subjectKeyIdentifier' => $keyID['extnValue']];
            default:
                throw new UnexpectedValueException('$type should be either CMS::ISSUER_AND_DN or CMS::KEY_ID');
        }
    }

    // this NEEDS to be public so that Constructed.php can call it
    /** @psalm-suppress PossiblyUnusedMethod */
    public static function mapInCerts(Constructed $certs): void
    {
        ASN1::disableCacheInvalidation();
        for ($i = 0; $i < count($certs); $i++) {
            if ($certs[$i]->index != 'certificate') {
                continue;
            }
            $certs[$i]['certificate'] = X509::load((string) $certs[$i]->getEncoded());
        }
        ASN1::enableCacheInvalidation();
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public static function mapInCRLs(Constructed $crls): void
    {
        ASN1::disableCacheInvalidation();
        for ($i = 0; $i < count($crls); $i++) {
            if (!isset($crls[$i]['crl'])) {
                continue;
            }
            $crls[$i] = CRL::load((string) $crls[$i]->getEncoded());
        }
        ASN1::enableCacheInvalidation();
    }

    /**
     * Enable binary output (DER)
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public static function enableBinaryOutput(): void
    {
        self::$binary = true;
    }

    /**
     * Disable binary output (ie. enable PEM)
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public static function disableBinaryOutput(): void
    {
        self::$binary = false;
    }
}
<?php

/**
 * Pure-PHP CSR Parser
 *
 * PHP version 8.1+
 *
 * Encode and decode Certificate Signing Requests (CSRs).
 *
 * The extensions are from {@link http://tools.ietf.org/html/rfc5280 RFC5280} and
 * {@link http://web.archive.org/web/19961027104704/http://www3.netscape.com/eng/security/cert-exts.html Netscape Certificate Extensions}.
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2025-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File;

use phpseclib4\Common\Functions\{Arrays, Strings};
use phpseclib4\Crypt\Common\{PrivateKey, PublicKey};
use phpseclib4\Crypt\{PublicKeyLoader, RSA};
use phpseclib4\Exception\{InvalidStateException, NoKeyLoadedException, UnexpectedValueException};
use phpseclib4\File\ASN1\{Constructed, Element, Maps};
use phpseclib4\File\ASN1\Types\{BaseType, BaseString, BitString, PrintableString, UTF8String};
use phpseclib4\File\Common\Signable;

/**
 * Pure-PHP CSR Parser
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @implements \ArrayAccess<string, mixed>
 * @implements \Iterator<string, mixed>
 */
class CSR implements \ArrayAccess, \Countable, \Iterator, Signable
{
    use \phpseclib4\File\Common\Traits\Extension;
    use \phpseclib4\File\Common\Traits\DN;
    use \phpseclib4\File\Common\Traits\ASN1Signature;

    /**
     * Return internal array representation
     *
     * @see \phpseclib4\File\X509::getDN()
     */
    public const DN_ARRAY = 0;
    /**
     * Return string
     *
     * @see \phpseclib4\File\X509::getDN()
     */
    public const DN_STRING = 1;
    /**
     * Return ASN.1 name string
     *
     * @see \phpseclib4\File\X509::getDN()
     */
    public const DN_ASN1 = 2;
    /**
     * Return OpenSSL compatible array
     *
     * @see \phpseclib4\File\X509::getDN()
     */
    public const DN_OPENSSL = 3;
    /**
     * Return canonical ASN.1 RDNs string
     *
     * @see \phpseclib4\File\X509::getDN()
     */
    public const DN_CANON = 4;
    /**
     * Return name hash for file indexing
     *
     * @see \phpseclib4\File\X509::getDN()
     */
    public const DN_HASH = 5;

    private Constructed|array $csr = [
        'certificationRequestInfo' => [
            'version' => 'v1',
            'subject' => ['rdnSequence' => []],
            'subjectPKInfo' => [
                'algorithm' => ['algorithm' => '0.0'],
                'subjectPublicKey' => "\0",
            ],
            'attributes' => [],
        ],
        'signatureAlgorithm' => [
            'algorithm' => '0.0',
        ],
        'signature' => "\0",
    ];

    private static array $extensions = [];

    /**
     * Binary key flag
     */
    private static bool $binary = false;

    public function __construct(PublicKey|X509|null $csr = null)
    {
        ASN1::loadOIDs('X509');

        if ($csr instanceof PublicKey) {
            $this->setPublicKey($csr);
        }

        if ($csr instanceof X509) {
            $this->setPublicKey($csr->getPublicKey());
            $this->setSubjectDN($csr->getSubjectDN());
            $exts = array_unique($csr->listExtensions());
            foreach ($exts as $name) {
                $ext = $csr->getExtension($name);
                $this->setExtension($name, $ext['extnValue'], $ext['critical']);
            }
        }
    }

    public static function load(string|array|Constructed $csr, int $mode = ASN1::FORMAT_AUTO_DETECT): CSR
    {
        $new = new self();
        $new->csr = is_string($csr) ? self::loadString($csr, $mode) : $csr;
        return $new;
    }

    private static function loadString(string $csr, int $mode): Constructed
    {
        if ($mode != ASN1::FORMAT_DER) {
            $newcsr = ASN1::extractBER($csr);
            if ($mode == ASN1::FORMAT_PEM && $csr == $newcsr) {
                throw new UnexpectedValueException('Unable to decode PEM');
            }
            $csr = $newcsr;
        }

        $decoded = ASN1::decodeBER($csr);

        $rules = [];
        $rules['certificationRequestInfo']['attributes']['*'] = self::mapInAttributes(...);
        $rules['certificationRequestInfo']['subject']['rdnSequence']['*']['*'] = self::mapInDNs(...);
        $rules['certificationRequestInfo']['subjectPKInfo'] = function (Constructed &$csr) {
            try {
                $csr = PublicKeyLoader::load($csr->getEncoded());
            } catch (NoKeyLoadedException) {
            }
        };

        return ASN1::map($decoded, Maps\CertificationRequest::MAP, $rules);
    }

    public function __debugInfo(): array
    {
        $this->compile();
        return $this->csr->__debugInfo();
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function keys(): array
    {
        return $this->csr instanceof Constructed ? $this->csr->keys() : array_keys($this->csr);
    }

    private function mapOutAttributes(): void
    {
        $attributes = &Arrays::subArray($this->csr, 'certificationRequestInfo/attributes');
        if (!$attributes) {
            return;
        }

        self::mapOutAttributesHelper($attributes);
    }

    private static function mapOutAttributesHelper(array|Constructed|Element &$attributes): void
    {
        if ($attributes instanceof Element) {
            return;
        }
        $keys = is_array($attributes) ? array_keys($attributes) : $attributes->keys();
        foreach ($keys as $i) {
            if ($attributes[$i] instanceof Element || $attributes[$i]['value'] instanceof Element) {
                continue;
            }

            $unparsedTest = is_array($attributes[$i]) && isset($attributes[$i]['value']);
            $unparsedTest = $unparsedTest && $attributes[$i]['value'] instanceof Constructed;
            $unparsedTest = $unparsedTest && !$attributes[$i]['value']->hasMapping();
            if ($unparsedTest) {
                $wrapping = chr(ASN1::TYPE_OCTET_STRING) . ASN1::encodeLength($attributes[$i]['value']->getEncodedLength());
                $attributes[$i]['value']->setWrapping($wrapping);
                continue;
            }

            $id = (string) $attributes[$i]['type'];
            $value = &$attributes[$i]['value'];

            if ($id == 'pkcs-9-at-extensionRequest') {
                foreach ($value as $i => $subvalue) {
                    self::mapOutExtensionsHelper($value[$i]);
                }
            }

            /* [extnValue] contains the DER encoding of an ASN.1 value
               corresponding to the extension type identified by extnID */
            $map = self::getAttrMapping("$id");
            if ($map === true) {
                $map = ['type' => ASN1::TYPE_ANY];
            }

            if ($map === false) {
                //user_error($id . ' is not a currently supported extension');
                unset($attributes[$i]);
                continue;
            } else {
                foreach ($value as $i => $subvalue) {
                    if ($value[$i] instanceof BaseType) {
                        ASN1::encodeDER($value[$i], $map);
                        $value[$i]->enableForcedCache();
                    } else {
                        $oldValue = $value[$i] instanceof Constructed ? $value[$i]->toArray() : $value[$i];
                        $temp = ASN1::encodeDER($value[$i], $map);
                        $value[$i] = ASN1::map(ASN1::decodeBER($temp), $map);
                        if ($value[$i] instanceof Constructed) {
                            $value[$i]->decoded = $oldValue;
                        }
                        $value[$i]->enableForcedCache();
                    }
                }
            }
        }

        if ($attributes instanceof Constructed) {
            if (count($attributes) - 1 != $attributes->lastKey()) {
                $attributes->rekey();
            }
        } else {
            if (count($attributes) - 1 != array_key_last($attributes)) {
                $attributes = array_values($attributes);
            }
        }
    }

    public function &offsetGet(mixed $offset): mixed
    {
        $this->compile();
        /** @psalm-suppress NonVariableReferenceReturn */
        return $this->csr[$offset];
    }

    public function offsetExists(mixed $offset): bool
    {
        return isset($this->csr[$offset]);
    }

    public function offsetSet(mixed $offset, mixed $value): void
    {
        $this->csr[$offset] = $value;
    }

    public function offsetUnset(mixed $offset): void
    {
        unset($this->csr[$offset]);
    }

    private static function getAttrMapping(string $id): bool|array
    {
        return match ($id) {
            'pkcs-9-at-unstructuredName' => Maps\PKCS9String::MAP,
            'pkcs-9-at-challengePassword' => Maps\DirectoryString::MAP,
            'pkcs-9-at-extensionRequest' => Maps\Extensions::MAP,

            default => false,
        };
    }

    public static function mapInAttributes(Constructed $attr): void
    {
        ASN1::disableCacheInvalidation();

        $id = "$attr[type]";
        $map = self::getAttrMapping($id);
        if ($map === false) {
            return;
        }
        $rule = [];
        if ($id == 'pkcs-9-at-extensionRequest') {
            $rule['*'] = self::mapInExtensions(...);
        }
        foreach ($attr['value'] as $key => $value) {
            $value = &$attr['value'][$key];
            $decoded = ASN1::decodeBER($value instanceof Element ? $value->value : $value->getEncodedWithWrapping());
            $value = ASN1::map($decoded, $map, $rule);
            if ($value instanceof Constructed && $attr['value'] instanceof Constructed) {
                $value->parent = $attr['value'];
                $value->depth = $attr['value']->depth + 1;
                $value->key = $key;
            }
            unset($value);
        }

        ASN1::enableCacheInvalidation();
    }

    public function getPublicKey(): PublicKey
    {
        if (!$this->csr['certificationRequestInfo']['subjectPKInfo'] instanceof PublicKey) {
            throw new UnexpectedValueException('Unable to decode subjectPKInfo');
        }
        $publicKey = $this->csr['certificationRequestInfo']['subjectPKInfo'];
        if ($publicKey instanceof RSA && $publicKey->getLoadedFormat() == 'PKCS8') {
            return $publicKey->withPadding(RSA::SIGNATURE_PKCS1);
        }
        return $publicKey;
    }

    public function hasPublicKey(): bool
    {
        return $this->csr['certificationRequestInfo']['subjectPKInfo'] instanceof PublicKey;
    }

    public function setPublicKey(PublicKey $publicKey): void
    {
        $this->csr['certificationRequestInfo']['subjectPKInfo'] = $publicKey;
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function removePublicKey(): void
    {
        $this->csr['certificationRequestInfo']['subjectPKInfo'] = [
            'algorithm' => ['algorithm' => '0.0'],
            'subjectPublicKey' => "\0",
        ];
    }

    public function toArray(bool $convertPrimitives = false): array
    {
        $this->compile();
        return $this->csr instanceof Constructed ? $this->csr->toArray($convertPrimitives) : $this->csr;
    }

    private function mapOutDNs(): void
    {
        $dns = &Arrays::subArray($this->csr, 'certificationRequestInfo/subject/rdnSequence');
        if (!$dns) {
            return;
        }

        self::mapOutDNsInner($dns);
    }

    /**
     * Enable binary output (DER)
     */
    public static function enableBinaryOutput(): void
    {
        self::$binary = true;
    }

    /**
     * Disable binary output (ie. enable PEM)
     */
    public static function disableBinaryOutput(): void
    {
        self::$binary = false;
    }

    public function toString(array $options = []): string
    {
        $publicKey = $this->csr['certificationRequestInfo']['subjectPKInfo'] ;
        if ($publicKey instanceof PublicKey) {
            $origKey = $publicKey;
            $this->csr['certificationRequestInfo']['subjectPKInfo']  = new Element($publicKey->toString('PKCS8', ['binary' => true]));
        }

        $this->mapOutDNs();
        $this->mapOutAttributes();

        $csr = ASN1::encodeDER($this->csr, Maps\CertificationRequest::MAP);
        if (isset($origKey)) {
            $this->csr['certificationRequestInfo']['subjectPKInfo'] = $origKey;
        }

        if ($options['binary'] ?? self::$binary) {
            return $csr;
        }

        return "-----BEGIN NEW CERTIFICATE REQUEST-----\r\n" . chunk_split(Strings::base64_encode($csr), 64) . '-----END NEW CERTIFICATE REQUEST-----';
    }

    public function __toString(): string
    {
        return $this->toString();
    }

    public function getSignableSection(): string
    {
        $this->compile();
        return $this->csr['certificationRequestInfo']->getEncoded();
    }

    public function setSignature(string $signature): void
    {
        $this->csr['signature'] = new BitString("\0$signature");
    }

    /**
     * Identify signature algorithm from private key
     */
    public function identifySignatureAlgorithm(PrivateKey $key): void
    {
        $algorithm = self::identifySignatureAlgorithmHelper($key);
        $this->csr['certificationRequestInfo']['signature'] = $algorithm;
        $this->csr['signatureAlgorithm'] = $algorithm;
    }

    public function copySigningX509Attributes(X509 $x509): void
    {
        // signing a CSR with a PFX doesn't quite make as much sense as signing an X509 or CRL does
        // but, regardless, this behavior is basically analgous to this:
        // $csr = new CSR($x509);
        // $private->sign($csr);
        $this->setSubjectDN($x509->getSubjectDN());
        $exts = array_unique($x509->listExtensions());
        foreach ($exts as $name) {
            $ext = $x509->getExtension($name);
            $this->setExtension($name, $ext['extnValue'], $ext['critical']);
        }
    }

    private function compile(): void
    {
        if (!$this->csr instanceof Constructed) {
            $temp = self::load("$this");
            $this->csr = $temp->csr;
        }
        if ($this->csr->hasEncoded()) {
            return;
        }
        $temp = self::load("$this");
        $this->csr = $temp->csr;
    }

    public function setSubjectDN(array|string|Element $props): void
    {
        self::setDNInternal($this->csr['certificationRequestInfo']['subject'], $props);
    }

    public function resetSubjectDN(): void
    {
        self::setDNInternal($this->csr['certificationRequestInfo']['subject'], []);
    }

    public function removeSubjectDNProps(string $propName): void
    {
        self::removeDNPropsInternal($this->csr['certificationRequestInfo']['subject'], $propName);
    }

    public function addSubjectDNProp(string $propName, string|BaseString|array|Element|Constructed $value): void
    {
        self::addDNPropsInternal($this->csr['certificationRequestInfo']['subject'], $propName, $value);
    }

    public function addSubjectDNProps(string $propName, array $values): void
    {
        foreach ($values as $value) {
            $this->addSubjectDNProp($propName, $value);
        }
    }

    public function hasSubjectDNProp(string $propName): bool
    {
        return self::hasDNPropsInternal($this->csr['certificationRequestInfo']['subject'], $propName);
    }

    public function getSubjectDNProps(string $propName): array
    {
        return self::retrieveDNProps($this->csr['certificationRequestInfo']['subject'], $propName);
    }

    public function getSubjectDN(int $format = self::DN_STRING): array|string
    {
        return self::formatDN($this->csr['certificationRequestInfo']['subject'], $format);
    }

    public function setDN(array|string|Element $props): void
    {
        self::setSubjectDN($props);
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function resetDN(): void
    {
        self::resetSubjectDN();
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function removeDNProps(string $propName): void
    {
        self::removeSubjectDNProps($propName);
    }

    public function addDNProp(string $propName, string|BaseString|array|Element|Constructed $value): void
    {
        self::addSubjectDNProp($propName, $value);
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function addDNProps(string $propName, array $values): void
    {
        self::addSubjectDNProps($propName, $values);
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function hasDNProp(string $propName): bool
    {
        return self::hasSubjectDNProp($propName);
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function getDNProps(string $propName): array
    {
        return self::getSubjectDNProps($propName);
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function getDN(int $format = self::DN_STRING): array|string
    {
        return self::getSubjectDN($format);
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function listAttributes(): array
    {
        if (!isset($this->csr['certificationRequestInfo']['attributes'])) {
            return [];
        }
        $attrs = [];
        foreach ($this->csr['certificationRequestInfo']['attributes'] as $attr) {
            $attrs[] = (string) $attr['type'];
        }
        return $attrs;
    }

    public function listExtensions(): array
    {
        if (!isset($this->csr['certificationRequestInfo']['attributes'])) {
            return [];
        }
        $exts = [];
        foreach ($this->csr['certificationRequestInfo']['attributes'] as $attr) {
            if ($attr['type'] == 'pkcs-9-at-extensionRequest') {
                foreach ($attr['value'] as $subattr) {
                    foreach ($subattr as $ext) {
                        $exts[] = (string) $ext['extnId'];
                    }
                }
            }
        }
        return $exts;
    }

    // returns the first instance of an attribute even if there are multiple instances
    // or null if said attribute isn't present
    public function getAttribute(string $name): BaseType|Constructed|null
    {
        $this->compile();
        if (!isset($this->csr['certificationRequestInfo']['attributes'])) {
            return null;
        }
        foreach ($this->csr['certificationRequestInfo']['attributes'] as $attr) {
            if (self::extensionMatch($name, $attr['extnId'])) {
                return $attr['value'];
            }
        }
        return null;
    }

    // returns the first instance of an extension even if there are multiple instances
    // or null if said attribute isn't present
    public function getExtension(string $name): ?array
    {
        $this->compile();
        if (!isset($this->csr['certificationRequestInfo']['attributes'])) {
            return null;
        }
        foreach ($this->csr['certificationRequestInfo']['attributes'] as $attr) {
            if ($attr['type'] == 'pkcs-9-at-extensionRequest') {
                foreach ($attr['value'] as $subattr) {
                    foreach ($subattr as $ext) {
                        if (self::extensionMatch($name, $ext['extnId'])) {
                            return [
                                'extnId' => $name,
                                'extnValue' => $ext['extnValue'],
                                'critical' => is_bool($ext['critical']) ? $ext['critical'] : $ext['critical']->value
                            ];
                        }
                    }
                }
            }
        }
        return null;
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function hasAttribute(string $name): bool
    {
        if (!isset($this->csr['certificationRequestInfo']['attributes'])) {
            return false;
        }
        foreach ($this->csr['certificationRequestInfo']['attributes'] as $attr) {
            if (self::extensionMatch($name, $attr['type'])) {
                return true;
            }
        }
        return false;
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function hasExtension(string $name): bool
    {
        if (!isset($this->csr['certificationRequestInfo']['attributes'])) {
            return false;
        }
        foreach ($this->csr['certificationRequestInfo']['attributes'] as $attr) {
            if ($attr['type'] == 'pkcs-9-at-extensionRequest') {
                foreach ($attr['value'] as $subattr) {
                    foreach ($subattr as $ext) {
                        if (self::extensionMatch($name, $ext['extnId'])) {
                            return true;
                        }
                    }
                }
            }
        }
        return false;
    }

    public function setAttribute(string $type, mixed $value): void
    {
        if (isset($this->csr['certificationRequestInfo']['attributes'])) {
            foreach ($this->csr['certificationRequestInfo']['attributes'] as $i => $attr) {
                $attr = &$this->csr['certificationRequestInfo']['attributes'][$i];
                if (self::extensionMatch($type, $attr['type'])) {
                    $attr['value'] = $value;
                    return;
                }
                unset($attr);
            }
        } else {
            $this->csr['certificationRequestInfo']['attributes'] = [];
        }

        $this->csr['certificationRequestInfo']['attributes'][] = [
            'type' => $type,
            'value' => $value,
        ];
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function removeAttribute(string $type): void
    {
        if (!isset($this->csr['certificationRequestInfo']['attributes'])) {
            return;
        }
        foreach ($this->csr['certificationRequestInfo']['attributes'] as $i => $attr) {
            if (self::extensionMatch($type, $attr['type'])) {
                unset($this->csr['certificationRequestInfo']['attributes'][$i]);
            }
        }
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function removeExtension(string $name): void
    {
        if (!isset($this->csr['certificationRequestInfo']['attributes'])) {
            return;
        }
        foreach ($this->csr['certificationRequestInfo']['attributes'] as $i => $attr) {
            if ("$attr[type]" == 'pkcs-9-at-extensionRequest') {
                foreach ($attr['value'] as $j => $subattr) {
                    foreach ($subattr as $k => $ext) {
                        if (self::extensionMatch($name, $ext['extnId'])) {
                            unset($this->csr['certificationRequestInfo']['attributes'][$i]['value'][$j][$k]);
                        }
                    }
                }
                unset($subattr);
            }
        }
    }

    public function setExtension(string $name, mixed $value, ?bool $critical = null): void
    {
        $origCritical = $critical;
        if (!isset($critical)) {
            $critical = self::getExtensionCriticalValue($name);
        }
        $extensionRequestTemplate = [
            'type' => 'pkcs-9-at-extensionRequest',
            'value' => [[[
                'extnId' => $name,
                'critical' => $critical,
                'extnValue' => $value,
            ]]]
        ];
        if (!isset($this->csr['certificationRequestInfo']['attributes'])) {
            $this->csr['certificationRequestInfo']['attributes'] = [$extensionRequestTemplate];
            return;
        }
        $lastExtensionRequestIdx = null;
        foreach ($this->csr['certificationRequestInfo']['attributes'] as $i => $attr) {
            if ($attr['type'] == 'pkcs-9-at-extensionRequest') {
                $lastExtensionRequestIdx = $i;
                foreach ($attr['value'] as $j => $subattr) {
                    foreach ($subattr as $k => $ext) {
                        $ext = &$this->csr['certificationRequestInfo']['attributes'][$i]['value'][$j][$k];
                        if (self::extensionMatch($name, $ext['extnId'])) {
                            $ext['extnValue'] = $value;
                            if (isset($origCritical)) {
                                $ext['critical'] = $origCritical;
                            }
                            return;
                        }
                        unset($ext);
                    }
                }
            }
        }
        if (isset($lastExtensionRequestIdx)) {
            $this->csr['certificationRequestInfo']['attributes'][$lastExtensionRequestIdx]['value'][0][] = $extensionRequestTemplate['value'][0][0];
        } else {
            $this->csr['certificationRequestInfo']['attributes'][] = $extensionRequestTemplate;
        }
    }

    /*
     * challenge passwords are mainly intended for CA's. like if you want to perform some operation
     * on the CA's website involving the X.509 cert that was created from the CSR that had the
     * challenge password you may be asked to provide said challenge password as an extra layer of
     * authentication
     */
    public function setChallengePassword(string|UTF8String|PrintableString $password): void
    {
        $value = is_string($password) || $password instanceof UTF8String ?
            ['utf8String' => $password] :
            ['printableString' => $password];
        $this->setAttribute('pkcs-9-at-challengePassword', [$value]);
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function getChallengePassword(): ?string
    {
        $attr = $this->getAttribute('pkcs-9-at-challengePassword');
        return $attr ? $attr[0]->value->value : null;
    }

    /**
     * Register the mapping for a custom/unsupported extension.
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public static function registerExtension(string $id, array $mapping): void
    {
        if (!is_bool(self::getMapping($id))) {
            throw new InvalidStateException(
                "Extension $id has already been defined with a different mapping."
            );
        }

        self::$extensions[$id] = $mapping;
    }

    /**
     * Register the mapping for a custom/unsupported extension.
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public static function getRegisteredExtension(string $id): ?array
    {
        return self::$extensions[$id] ?? null;
    }

    public function count(): int
    {
        return is_array($this->csr) ? count($this->csr) : $this->csr->count();
    }

    public function rewind(): void
    {
        $this->compile();
        $this->csr->rewind();
    }

    public function current(): mixed
    {
        $this->compile();
        return $this->csr->current();
    }

    public function key(): mixed
    {
        $this->compile();
        return $this->csr->key();
    }

    public function next(): void
    {
        $this->compile();
        $this->csr->next();
    }

    public function valid(): bool
    {
        $this->compile();
        return $this->csr->valid();
    }

    /**
     * Validate a signature
     *
     * Returns true if the signature is verified, false if it is not correct or on error
     *
     * The behavior of this function is inspired by {@link http://php.net/openssl-verify openssl_verify}.
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function validateSignature(): bool
    {
        return self::validateSignatureHelper(
            $this->getPublicKey(),
            $this->csr['signatureAlgorithm']['algorithm'],
            $this->csr['signature'],
            $this->csr['certificationRequestInfo']->getEncoded()
        );
    }
}
<?php

/**
 * Pure-PHP X.509 Parser
 *
 * PHP version 8.1+
 *
 * Encode and decode X.509 certificates.
 *
 * The extensions are from {@link http://tools.ietf.org/html/rfc5280 RFC5280} and
 * {@link http://web.archive.org/web/19961027104704/http://www3.netscape.com/eng/security/cert-exts.html Netscape Certificate Extensions}.
 *
 * Note that loading an X.509 certificate and resaving it may invalidate the signature.  The reason being that the signature is based on a
 * portion of the certificate that contains optional parameters with default values.  ie. if the parameter isn't there the default value is
 * used.  Problem is, if the parameter is there and it just so happens to have the default value there are two ways that that parameter can
 * be encoded.  It can be encoded explicitly or left out all together.  This would effect the signature value and thus may invalidate the
 * the certificate all together unless the certificate is re-signed.
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2012-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File;

use phpseclib4\Common\Functions\{Arrays, Strings};
use phpseclib4\Crypt\Common\{AsymmetricKey, PublicKey, PrivateKey};
use phpseclib4\Crypt\{EC, Hash, PublicKeyLoader, RSA};
use phpseclib4\Exception\{
    BadMethodCallException,
    InvalidArgumentException,
    InvalidStateException,
    NoKeyLoadedException,
    UnexpectedValueException
};
use phpseclib4\File\ASN1\{Constructed, Element, Maps};
use phpseclib4\File\ASN1\Types\{
    BaseString,
    BitString,
    Boolean,
    Choice,
    OID,
    OctetString
};
use phpseclib4\File\Common\Signable;
use phpseclib4\Math\BigInteger;

/**
 * Pure-PHP X.509 Parser
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @implements \ArrayAccess<string, mixed>
 * @implements \Iterator<string, mixed>
 */
class X509 implements \ArrayAccess, \Countable, \Iterator, Signable
{
    use \phpseclib4\File\Common\Traits\Extension;
    use \phpseclib4\File\Common\Traits\DN;
    use \phpseclib4\File\Common\Traits\ASN1Signature;

    /**
     * Return internal array representation
     *
     * @see \phpseclib4\File\X509::getDN()
     */
    public const DN_ARRAY = 0;
    /**
     * Return string
     *
     * @see \phpseclib4\File\X509::getDN()
     */
    public const DN_STRING = 1;
    /**
     * Return ASN.1 name string
     *
     * @see \phpseclib4\File\X509::getDN()
     */
    public const DN_ASN1 = 2;
    /**
     * Return OpenSSL compatible array
     *
     * @see \phpseclib4\File\X509::getDN()
     */
    public const DN_OPENSSL = 3;
    /**
     * Return canonical ASN.1 RDNs string
     *
     * @see \phpseclib4\File\X509::getDN()
     */
    public const DN_CANON = 4;
    /**
     * Return name hash for file indexing
     *
     * @see \phpseclib4\File\X509::getDN()
     */
    public const DN_HASH = 5;

    private Constructed|array $cert;
    private static bool $strictDNComparison = true;
    public ?self $issuer = null;
    private static \DateTimeInterface|string|null $targetValidationDate = 'now';
    private static bool $checkKeyUsage = true;
    private static bool $checkBasicConstraints = true;
    private int $caSeq;
    private bool $isCA = false;
    private bool $hideFullDecode = false;

    /**
     * Recursion Limit
     */
    private static int $recur_limit = 5;

    /**
     * The certificate authorities
     */
    private static array $CAs = [];

    /**
     * Binary key flag
     */
    private static bool $binary = false;

    private static array $extensions = [];

    private static \Closure $inCRLFunction;

    private static ?\Closure $urlFetchCallback = null;

    public function __construct(PublicKey|CSR|null $cert = null)
    {
        if (!isset(self::$inCRLFunction)) {
            // ideally CRLs, which can sometimes be several megabytes in size, would be cached for some period
            // of time, however, phpseclib, making no assumptions on the underlying filesystem, doesn't
            // really have a mechanism to do this. if you want to save CRLs to some sort of DB then you can
            // replace this function with one that'll cache to the DB and pull from the DB when appropriate
            /** @psalm-suppress UnusedClosureParam */
            self::$inCRLFunction = function (string $url, BigInteger $serial): bool {
                return false;
            };
        }

        if (!isset(self::$urlFetchCallback)) {
            /** @psalm-suppress UnusedClosureParam */
            self::$urlFetchCallback = function (string $host, string $ip, int $port, string $scheme): bool {
                return false;
            };
        }

        ASN1::loadOIDs('X509');

        $startDate = new \DateTimeImmutable('now', new \DateTimeZone(@date_default_timezone_get()));
        $endDate = new \DateTimeImmutable('+1 year', new \DateTimeZone(@date_default_timezone_get()));

        /*
          "The serial number MUST be a positive integer"
          "Conforming CAs MUST NOT use serialNumber values longer than 20 octets."
           -- https://tools.ietf.org/html/rfc5280#section-4.1.2.2

          for the integer to be positive the leading bit needs to be 0 hence the
          application of a bitmap
        */
        $serialNumber = new BigInteger(random_bytes(20) & ("\x7F" . str_repeat("\xFF", 19)), 256);

        $this->cert = [
            'tbsCertificate' => [
                'version' => 'v3',
                'serialNumber' => $serialNumber,
                'signature' => [
                    'algorithm' => '0.0',
                ],
                'issuer' => ['rdnSequence' => []],
                'validity' => [
                    'notBefore' => ['utcTime' => $startDate->format('Y-m-d H:i:s')],
                    'notAfter' => ['utcTime' => $endDate->format('Y-m-d H:i:s')],
                ],
                'subject' => ['rdnSequence' => []],
                'subjectPublicKeyInfo' => [
                    'algorithm' => ['algorithm' => '0.0'],
                    'subjectPublicKey' => "\0",
                ],
            ],
            'signatureAlgorithm' => [
                'algorithm' => '0.0',
            ],
            'signature' => "\0",
        ];

        if ($cert instanceof PublicKey) {
            $this->setPublicKey($cert);
        }

        if ($cert instanceof CSR) {
            $this->setPublicKey($cert->getPublicKey());
            $this->setSubjectDN($cert->getSubjectDN());
            $exts = array_unique($cert->listExtensions());
            foreach ($exts as $name) {
                $ext = $cert->getExtension($name);
                $this->setExtension($name, $ext['extnValue'], $ext['critical']);
            }
        }
    }

    // if you're going to pass an array or a Constructed object to this there's no real way to validate it so you may get random exceptions
    // $mode is only used if you're loading a string
    public static function load(string|array|Constructed $cert, int $mode = ASN1::FORMAT_AUTO_DETECT): X509
    {
        $x509 = new self();
        $x509->cert = is_string($cert) ? self::loadString($cert, $mode) : $cert;
        return $x509;
    }

    public static function addCA(string|array|Constructed|X509 $cert, int $mode = ASN1::FORMAT_AUTO_DETECT): void
    {
        $x509 = new self();
        switch (true) {
            case is_string($cert):
                $x509->cert = self::loadString($cert, $mode);
                break;
            case $cert instanceof X509:
                $x509->cert = $cert->cert;
                break;
            default:
                $x509->cert = $cert;
        }
        $x509->isCA = true;
        self::$CAs[] = $x509;
    }

    public static function clearCAStore(): void
    {
        self::$CAs = [];
    }

    public static function getCAs(): array
    {
        return self::$CAs;
    }

    // this is the default behavior
    public static function strictDNComparison(): void
    {
        self::$strictDNComparison = true;
    }

    // what if you have two strings that have the same value but are of different types? eg. UTF8String vs PrintableString.
    // should those match? with strict DN comparison they wouldn't. with loose DN comparison they would.
    public static function looseDNComparison(): void
    {
        self::$strictDNComparison = false;
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public static function isStrictDNComparisonEnabled(): bool
    {
        return self::$strictDNComparison;
    }

    public static function checkKeyUsage(): void
    {
        self::$checkKeyUsage = true;
    }

    public static function ignoreKeyUsage(): void
    {
        self::$checkKeyUsage = false;
    }

    public static function isCheckKeyUsageEnabled(): bool
    {
        return self::$checkKeyUsage;
    }

    public static function checkBasicConstraints(): void
    {
        self::$checkBasicConstraints = true;
    }

    public static function ignoreBasicConstraints(): void
    {
        self::$checkBasicConstraints = false;
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public static function isCheckBasicConstraintsEnabled(): bool
    {
        return self::$checkBasicConstraints;
    }

    /**
     * Enable binary output (DER)
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public static function enableBinaryOutput(): void
    {
        self::$binary = true;
    }

    /**
     * Disable binary output (ie. enable PEM)
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public static function disableBinaryOutput(): void
    {
        self::$binary = false;
    }

    private static function loadString(string $cert, int $mode): Constructed
    {
        if ($mode != ASN1::FORMAT_DER) {
            $newcert = ASN1::extractBER($cert);
            if ($mode == ASN1::FORMAT_PEM && $cert == $newcert) {
                throw new UnexpectedValueException('Unable to decode PEM');
            }
            $cert = $newcert;
        }

        $decoded = ASN1::decodeBER($cert);

        $rules = [];
        $rules['tbsCertificate']['extensions']['*'] = self::mapInExtensions(...);
        $rules['tbsCertificate']['subject']['rdnSequence']['*']['*'] = self::mapInDNs(...);
        $rules['tbsCertificate']['issuer']['rdnSequence']['*']['*'] = self::mapInDNs(...);
        $rules['tbsCertificate']['subjectPublicKeyInfo'] = function (Constructed &$key) {
            try {
                $key = PublicKeyLoader::load($key->getEncoded());
                if ($key instanceof RSA && $key->getLoadedFormat() == 'PKCS8') {
                    $key = $key->withPadding(RSA::SIGNATURE_PKCS1);
                }
            } catch (NoKeyLoadedException) {
            }
        };

        return ASN1::map($decoded, Maps\Certificate::MAP, $rules);
    }

    public function &offsetGet(mixed $offset): mixed
    {
        $this->compile();
        /** @psalm-suppress NonVariableReferenceReturn */
        return $this->cert[$offset];
    }

    public function offsetExists(mixed $offset): bool
    {
        return isset($this->cert[$offset]);
    }

    public function offsetSet(mixed $offset, mixed $value): void
    {
        $this->cert[$offset] = $value;
    }

    public function offsetUnset(mixed $offset): void
    {
        unset($this->cert[$offset]);
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function currentlyDecoded(): array|string
    {
        return $this->cert->currentlyDecoded();
    }

    public function __debugInfo(): array
    {
        if ($this->hideFullDecode) {
            $this->hideFullDecode = false;
            return ['value' => (string) $this];
        }
        $this->compile();
        return $this->cert->__debugInfo();
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function keys(): array
    {
        return $this->cert instanceof Constructed ? $this->cert->keys() : array_keys($this->cert);
    }

    private function mapOutDNs(string $path): void
    {
        $dns = &Arrays::subArray($this->cert, $path);

        if (!$dns) {
            return;
        }

        self::mapOutDNsInner($dns);
    }

    private function mapOutExtensions(): void
    {
        $extensions = &Arrays::subArray($this->cert, 'tbsCertificate/extensions');
        if (!$extensions || ($extensions instanceof Constructed && $extensions->hasEncoded())) {
            return;
        }

        self::mapOutExtensionsHelper($extensions);
    }

    public function toArray(bool $convertPrimitives = false): array
    {
        $this->compile();
        return $this->cert instanceof Constructed ? $this->cert->toArray($convertPrimitives) : $this->cert;
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function setPadding(int $padding): void
    {
        $publicKey = &$this->cert['tbsCertificate']['subjectPublicKeyInfo'];
        if (!$publicKey instanceof RSA) {
            throw new BadMethodCallException('Padding can only be set for RSA X509 certificates');
        }

        $publicKey = $publicKey->withPadding($padding);
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function setHash(string $hash): void
    {
        $publicKey = &$this->cert['tbsCertificate']['subjectPublicKeyInfo'];
        if (!$publicKey instanceof RSA && !$publicKey instanceof EC) {
            throw new BadMethodCallException('Hash can only be set for RSA or EC X509 certificates');
        }

        $publicKey = $publicKey->withHash($hash);
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function setMFGHash(string $hash): void
    {
        $publicKey = &$this->cert['tbsCertificate']['subjectPublicKeyInfo'];
        if (!$publicKey instanceof RSA) {
            throw new BadMethodCallException('MGF Hash can only be set for RSA X509 certificates');
        }

        $publicKey = $publicKey->withMFGHash($hash);
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function setLabel(string $label): void
    {
        $publicKey = &$this->cert['tbsCertificate']['subjectPublicKeyInfo'];
        if (!$publicKey instanceof RSA) {
            throw new BadMethodCallException('MGF Hash can only be set for RSA X509 certificates');
        }

        $publicKey = $publicKey->withLabel($label);
    }

    public function getPublicKey(): AsymmetricKey&PublicKey
    {
        if (!$this->cert['tbsCertificate']['subjectPublicKeyInfo'] instanceof PublicKey) {
            throw new UnexpectedValueException('Unable to decode subjectPublicKeyInfo');
        }

        /** @var AsymmetricKey&PublicKey $publicKey */
        $publicKey = $this->cert['tbsCertificate']['subjectPublicKeyInfo'];
        //if ($publicKey instanceof RSA && $publicKey->getLoadedFormat() == 'PKCS8') {
        //    return $publicKey->withPadding(RSA::SIGNATURE_PKCS1);
        //}
        return $publicKey;
    }

    public function setSubjectDN(array|string|Element $props): void
    {
        self::setDNInternal($this->cert['tbsCertificate']['subject'], $props);
    }

    public function setIssuerDN(array|string|Element $props): void
    {
        self::setDNInternal($this->cert['tbsCertificate']['issuer'], $props);
    }

    public function resetSubjectDN(): void
    {
        self::setDNInternal($this->cert['tbsCertificate']['subject'], []);
    }

    public function resetIssuerDN(): void
    {
        self::setDNInternal($this->cert['tbsCertificate']['issuer'], []);
    }

    public function removeIssuerDNProps(string $propName): void
    {
        self::removeDNPropsInternal($this->cert['tbsCertificate']['issuer'], $propName);
    }

    public function removeSubjectDNProps(string $propName): void
    {
        self::removeDNPropsInternal($this->cert['tbsCertificate']['subject'], $propName);
    }

    public function addIssuerDNProp(string $propName, string|BaseString|array|Element|Constructed $value): void
    {
        self::addDNPropsInternal($this->cert['tbsCertificate']['issuer'], $propName, $value);
    }

    public function addSubjectDNProp(string $propName, string|BaseString|array|Element|Constructed $value): void
    {
        self::addDNPropsInternal($this->cert['tbsCertificate']['subject'], $propName, $value);
    }

    public function addIssuerDNProps(string $propName, array $values): void
    {
        foreach ($values as $value) {
            $this->addIssuerDNProp($propName, $value);
        }
    }

    public function addSubjectDNProps(string $propName, array $values): void
    {
        foreach ($values as $value) {
            $this->addSubjectDNProp($propName, $value);
        }
    }

    public function hasSubjectDNProp(string $propName): bool
    {
        return self::hasDNPropsInternal($this->cert['tbsCertificate']['subject'], $propName);
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function hasIssuerDNProp(string $propName): bool
    {
        return self::hasDNPropsInternal($this->cert['tbsCertificate']['issuer'], $propName);
    }

    public function getSubjectDNProps(string $propName): array
    {
        return self::retrieveDNProps($this->cert['tbsCertificate']['subject'], $propName);
    }

    public function getIssuerDNProps(string $propName): array
    {
        return self::retrieveDNProps($this->cert['tbsCertificate']['issuer'], $propName);
    }

    public function getSubjectDN(int $format = self::DN_STRING): array|string
    {
        return self::formatDN($this->cert['tbsCertificate']['subject'], $format);
    }

    public function getIssuerDN(int $format = self::DN_STRING): array|string
    {
        return self::formatDN($this->cert['tbsCertificate']['issuer'], $format);
    }

    public function setPublicKey(PublicKey $publicKey): void
    {
        $this->cert['tbsCertificate']['subjectPublicKeyInfo'] = $publicKey;
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function hasPublicKey(): bool
    {
        return $this->cert['tbsCertificate']['subjectPublicKeyInfo'] instanceof PublicKey;
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function removePublicKey(): void
    {
        $this->cert['tbsCertificate']['subjectPublicKeyInfo'] = [
            'algorithm' => ['algorithm' => '0.0'],
            'subjectPublicKey' => "\0",
        ];
    }

    /**
     * Set certificate start date
     */
    public function setStartDate(\DateTimeInterface|string $date): void
    {
        $this->cert['tbsCertificate']['validity']['notBefore'] = ASN1::formatTime($date);
    }

    /**
     * Set certificate end date
     *
     * The CA/Browser Forum recommends that the end date not be more than 25 years out for a CA:
     * https://cabforum.org/working-groups/server/baseline-requirements/requirements/#71211-root-ca-validity
     */
    public function setEndDate(\DateTimeInterface|string $date): void
    {
        /*
          To indicate that a certificate has no well-defined expiration date,
          the notAfter SHOULD be assigned the GeneralizedTime value of
          99991231235959Z.

          -- http://tools.ietf.org/html/rfc5280#section-4.1.2.5
        */
        if (is_string($date) && strtolower($date) === 'lifetime') {
            $temp = '99991231235959Z';
            $temp = chr(ASN1::TYPE_GENERALIZED_TIME) . ASN1::encodeLength(strlen($temp)) . $temp;
            $date = ['utcTime' => new Element($temp)];
        } else {
            $date = ASN1::formatTime($date);
        }

        $this->cert['tbsCertificate']['validity']['notAfter'] = $date;
    }

    public function setSerialNumber(string|BigInteger $serial, int $base = 256): void
    {
        if (is_string($serial)) {
            $serial = new BigInteger($serial, $base);
        }
        if ($serial->isNegative()) {
            $serial = new BigInteger($serial->toBytes(true), 256);
        }
        $this->cert['tbsCertificate']['serialNumber'] = $serial;
    }

    public function count(): int
    {
        return is_array($this->cert) ? count($this->cert) : $this->cert->count();
    }

    public function rewind(): void
    {
        $this->compile();
        $this->cert->rewind();
    }

    public function current(): mixed
    {
        $this->compile();
        return $this->cert->current();
    }

    public function key(): mixed
    {
        $this->compile();
        return $this->cert->key();
    }

    public function next(): void
    {
        $this->compile();
        $this->cert->next();
    }

    public function valid(): bool
    {
        $this->compile();
        return $this->cert->valid();
    }

    private function compile(): void
    {
        if (!$this->cert instanceof Constructed) {
            $temp = self::load("$this");
            $this->cert = $temp->cert;
        }
        if ($this->cert->hasEncoded()) {
            return;
        }
        $temp = self::load("$this");
        $this->cert = $temp->cert;
    }

    public function toString(array $options = []): string
    {
        //if ($this->cert['tbsCertificate']['serialNumber']->isNegative()) {
        //    throw new UnexpectedValueException('The serial number of an X.509 certificate must be positive');
        //}

        $publicKey = $this->cert['tbsCertificate']['subjectPublicKeyInfo'];
        if ($publicKey instanceof PublicKey) {
            $origKey = $publicKey;
            $format = $publicKey instanceof RSA && ($publicKey->getPadding() & RSA::SIGNATURE_PSS) ? 'PSS' : 'PKCS8';
            $this->cert['tbsCertificate']['subjectPublicKeyInfo'] = new Element($publicKey->toString($format, ['binary' => true]));
        }

        $this->mapOutDNs('tbsCertificate/issuer/rdnSequence');
        $this->mapOutDNs('tbsCertificate/subject/rdnSequence');
        $this->mapOutExtensions();

        $cert = ASN1::encodeDER($this->cert, Maps\Certificate::MAP);

        if (isset($origKey)) {
            $this->cert['tbsCertificate']['subjectPublicKeyInfo'] = $origKey;
        }

        if ($options['binary'] ?? self::$binary) {
            return $cert;
        }

        return "-----BEGIN CERTIFICATE-----\r\n" . chunk_split(Strings::base64_encode($cert), 64) . '-----END CERTIFICATE-----';
    }

    public function __toString(): string
    {
        return $this->toString();
    }

    public function getEncoded(): string
    {
        $this->compile();
        return $this->cert->getEncoded();
    }

    public function setAuthorityKeyIdentifier(string|OctetString $value): void
    {
        if (is_string($value)) {
            $value = new OctetString($value);
        }
        $this->setExtension('id-ce-authorityKeyIdentifier', [
            //'authorityCertIssuer' => [
            //    [
            //        'directoryName' => $issuer['tbsCertificate']['subject']
            //    ]
            //],
            //'authorityCertSerialNumber' => $issuer['tbsCertificate']['serialNumber'],
            'keyIdentifier' => $value,
        ]);
    }

    public function setSubjectKeyIdentifier(string|OctetString $value): void
    {
        if (is_string($value)) {
            $value = new OctetString($value);
        }
        $this->setExtension('id-ce-subjectKeyIdentifier', $value);
    }

    /**
     * Compute a public key identifier.
     *
     * Although key identifiers may be set to any unique value, this function
     * computes key identifiers from public key according to the two
     * recommended methods (4.2.1.2 RFC 5280).
     *
     * @link https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.2
    */
    public function createSubjectKeyIdentifier(int $method = 1): string
    {
        if ($method != 1 && $method != 2) {
            throw new InvalidArgumentException('method must be either 1 or 2');
        }

        if (!$this->cert['tbsCertificate']['subjectPublicKeyInfo'] instanceof PublicKey) {
            throw new UnexpectedValueException('createSubjectKeyIdentifier only works if the public key is actually recognized by phpseclib as a public key');
        }

        $key = $this->cert['tbsCertificate']['subjectPublicKeyInfo']->toString('PKCS8', ['binary' => true]);
        $hash = new Hash('sha1');
        $hash = $hash->hash($key);

        if ($method == 2) {
            $hash = substr($hash, -8);
            $hash[0] = chr((ord($hash[0]) & 0x0F) | 0x40);
        }
        $this->setExtension('id-ce-subjectKeyIdentifier', $hash);

        return $hash;
    }

    public function listExtensions(): array
    {
        if (!isset($this->cert['tbsCertificate']['extensions'])) {
            return [];
        }
        $exts = [];
        foreach ($this->cert['tbsCertificate']['extensions'] as $ext) {
            $exts[] = (string) $ext['extnId'];
        }
        return $exts;
    }

    // returns the first instance of an extension even if there are multiple instances
    // or null if said extension isn't present
    public function getExtension(string $name): ?array
    {
        $this->compile();
        if (!isset($this->cert['tbsCertificate']['extensions'])) {
            return null;
        }
        foreach ($this->cert['tbsCertificate']['extensions'] as $ext) {
            if (self::extensionMatch($name, $ext['extnId'])) {
                return [
                    'extnId' => $name,
                    'extnValue' => $ext['extnValue'],
                    'critical' => is_bool($ext['critical']) ? $ext['critical'] : $ext['critical']->value
                ];
            }
        }
        return null;
    }

    public function hasExtension(string $name): bool
    {
        if (!isset($this->cert['tbsCertificate']['extensions'])) {
            return false;
        }
        foreach ($this->cert['tbsCertificate']['extensions'] as $ext) {
            if (!$ext['extnId'] instanceof OID) {
                $ext['extnId'] = new OID($ext['extnId']);
            }
            if (self::extensionMatch($name, $ext['extnId'])) {
                return true;
            }
        }
        return false;
    }

    // $value could be Element|BaseType|BigInteger|string|array|int|float|bool|null
    // and by array that means any of the non-array types in any combination. eg.
    // [Constructed, int] would be sufficient as would [array, BaseType]
    public function setExtension(string $name, mixed $value, ?bool $critical = null): void
    {
        $origCritical = $critical;
        if (!isset($critical)) {
            $critical = self::getExtensionCriticalValue($name);
        }
        if (isset($this->cert['tbsCertificate']['extensions'])) {
            foreach ($this->cert['tbsCertificate']['extensions'] as $i => $ext) {
                $ext = &$this->cert['tbsCertificate']['extensions'][$i];
                if (self::extensionMatch($name, $ext['extnId'])) {
                    $ext['extnValue'] = $value;
                    if (isset($origCritical)) {
                        $ext['critical'] = $origCritical;
                    }
                    return;
                }
                unset($ext);
            }
        } else {
            $this->cert['tbsCertificate']['extensions'] = [];
        }

        $this->cert['tbsCertificate']['extensions'][] = [
            'extnId' => $name,
            'critical' => $critical,
            'extnValue' => $value,
        ];
    }

    // remove all instances of a particular extension
    public function removeExtension(string $name): void
    {
        if (!isset($this->cert['tbsCertificate']['extensions'])) {
            return;
        }
        foreach ($this->cert['tbsCertificate']['extensions'] as $i => $ext) {
            if (self::extensionMatch($name, $ext['extnId'])) {
                unset($this->cert['tbsCertificate']['extensions'][$i]);
            }
        }
    }

    private function testForSelfSigned(): void
    {
        $oldKeyUsage = self::$checkKeyUsage;
        $oldBasicConstraints = self::$checkBasicConstraints;
        self::$checkKeyUsage = false;
        self::$checkBasicConstraints = false;
        if (!$this->isIssuerOf($this)) {
            self::$checkBasicConstraints = $oldBasicConstraints;
            self::$checkKeyUsage = $oldKeyUsage;
            throw new BadMethodCallException('This method is only available for self signed certificates');
        }
        self::$checkBasicConstraints = $oldBasicConstraints;
        self::$checkKeyUsage = $oldKeyUsage;
    }

    public function setDN(array|string|Element $props): void
    {
        $this->testForSelfSigned();

        $this->setSubjectDN($props);
        $this->setIssuerDN($props);
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function resetDN(): void
    {
        $this->resetSubjectDN();
        $this->resetIssuerDN();
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function removeDNProps(string $propName): void
    {
        $this->testForSelfSigned();

        $this->removeIssuerDNProps($propName);
        $this->removeSubjectDNProps($propName);
    }

    public function addDNProp(string $propName, string|BaseString|array|Element|Constructed $value): void
    {
        $this->testForSelfSigned();

        $this->addSubjectDNProp($propName, $value);
        $this->addIssuerDNProp($propName, $value);
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function addDNProps(string $propName, array $values): void
    {
        $this->testForSelfSigned();

        $this->addSubjectDNProps($propName, $values);
        $this->addIssuerDNProps($propName, $values);
    }

    public function getDNProps(string $propName): array
    {
        $this->testForSelfSigned();

        return $this->getSubjectDNProps($propName);
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function hasDNProp(string $propName): bool
    {
        $this->testForSelfSigned();

        return $this->hasSubjectDNProp($propName);
    }

    public function getDN(int $format = self::DN_STRING): array|string
    {
        $this->testForSelfSigned();

        return $this->getSubjectDN($format);
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function setKeyIdentifier(string|OctetString $value): void
    {
        $this->testForSelfSigned();

        $this->setAuthorityKeyIdentifier($value);
        $this->setSubjectKeyIdentifier($value);
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function createKeyIdentifier(int $method = 1): void
    {
        $this->testForSelfSigned();

        $this->setAuthorityKeyIdentifier($this->createSubjectKeyIdentifier($method));
    }

    /**
     * Set the domain name's which the cert is to be valid for
     *
     * These days browsers usually ignore the CN DN attribute, relying exclusively on the
     * subjectAltName extension, however, some tools still look at it
     */
    public function addDomains(string ...$domains): void
    {
        if (!$this->hasSubjectDNProp('id-at-commonName')) {
            $this->addSubjectDNProp('id-at-commonName', $domains[0]);
        }
        $ext = $this->getExtension('id-ce-subjectAltName')['extnValue'] ?? [];
        foreach ($domains as $domain) {
            $ext[] = ['dNSName' => $domain];
        }
        // "If subject naming information is present only in the subjectAltName extension
        //  (e.g., a key bound only to an email address or URI), then the subject
        //  name MUST be an empty sequence and the subjectAltName extension MUST
        //  be critical."
        // -- https://www.rfc-editor.org/rfc/rfc5280#page-24
        //
        // that said, in practice, it seems like browsers do *not* support certs with
        // empty DNs, regardless of whether or not the subjectAltName extension is marked
        // as critical
        $this->setExtension('id-ce-subjectAltName', $ext, false);
    }

    /**
     * Set the IP Addresses's which the cert is to be valid for
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function addIPAddresses(string ...$ipAddresses): void
    {
        $ext = $this->getExtension('id-ce-subjectAltName')['extnValue'] ?? [];
        foreach ($ipAddresses as $ipAddress) {
            $ext[] = ['iPAddress' => $ipAddress];
        }
        $this->setExtension('id-ce-subjectAltName', $ext, false);
    }

    /**
     * Turns the certificate into a certificate authority
     */
    public function makeCA(): void
    {
        $keyUsage = $this->getExtension('id-ce-keyUsage')['extnValue'] ?? [];
        if ($keyUsage instanceof BitString) {
            $keyUsage = $keyUsage->mappedValue;
        }
        $this->setExtension(
            'id-ce-keyUsage',
            array_values(array_unique(array_merge($keyUsage, ['cRLSign', 'keyCertSign', 'digitalSignature'])))
        );
        $basicConstraints = $this->getExtension('id-ce-basicConstraints')['extnValue'] ?? [];
        if ($basicConstraints instanceof Constructed) {
            $basicConstraints = $basicConstraints->toArray();
        }
        // "Conforming CAs MUST include this extension in all CA certificates
        //  that contain public keys used to validate digital signatures on
        //  certificates and MUST mark the extension as critical in such
        //  certificates.  This extension MAY appear as a critical or non-
        //  critical extension in CA certificates that contain public keys used
        //  exclusively for purposes other than validating digital signatures on
        //  certificates.  Such CA certificates include ones that contain public
        //  keys used exclusively for validating digital signatures on CRLs and
        //  ones that contain key management public keys used with certificate
        //  enrollment protocols.  This extension MAY appear as a critical or
        //  non-critical extension in end entity certificates."
        // -- https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.9
        $this->setExtension(
            'id-ce-basicConstraints',
            array_merge(['cA' => true], $basicConstraints),
            true
        );

        if (!$this->hasExtension('id-ce-subjectKeyIdentifier')) {
            $this->createSubjectKeyIdentifier();
        }
        if (!$this->hasExtension('id-ce-authorityKeyIdentifier')) {
            $this->setAuthorityKeyIdentifier($this->getExtension('id-ce-subjectKeyIdentifier')['extnValue']->value);
        }
    }

    /**
     * Register the mapping for a custom/unsupported extension.
     */
    public static function registerExtension(string $id, array $mapping): void
    {
        if (is_array(self::getMapping($id))) {
            throw new InvalidStateException(
                "Extension $id has already been defined with a different mapping."
            );
        }

        self::$extensions[$id] = $mapping;
    }

    /**
     * Returns the mapping for a registered extension or null if $id isn't registered
     */
    public static function getRegisteredExtension(string $id): ?array
    {
        return self::$extensions[$id] ?? null;
    }

    /**
     * Setting $date to null for this function will mean that no date validation takes place.
     * This is in contrast to what null means in the validateDate() function
     */
    public static function setTargetValidationDate(\DateTimeInterface|string|null $date = null): void
    {
        self::$targetValidationDate = $date;
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public static function getTargetValidationDate(): \DateTimeInterface|string|null
    {
        return self::$targetValidationDate;
    }

    public function getSignableSection(): string
    {
        $this->compile();
        return $this->cert['tbsCertificate']->getEncoded();
    }

    public function setSignature(string $signature): void
    {
        $this->cert['signature'] = new BitString("\0$signature");
    }

    /**
     * Identify signature algorithm from private key
     */
    public function identifySignatureAlgorithm(PrivateKey $key): void
    {
        $algorithm = self::identifySignatureAlgorithmHelper($key);
        $this->cert['tbsCertificate']['signature'] = $algorithm;
        $this->cert['signatureAlgorithm'] = $algorithm;
    }

    public function copySigningX509Attributes(X509 $x509): void
    {
        $this->setIssuerDN($x509->getSubjectDN(X509::DN_ARRAY));
        $subjectKeyIdentifier = $x509->getExtension('id-ce-subjectKeyIdentifier');
        if (isset($subjectKeyIdentifier)) {
            $this->setAuthorityKeyIdentifier($subjectKeyIdentifier['extnValue']);
        }
    }

    /**
     * Validate an X.509 certificate against a URL
     *
     * From RFC2818 "HTTP over TLS":
     *
     * Matching is performed using the matching rules specified by
     * [RFC2459].  If more than one identity of a given type is present in
     * the certificate (e.g., more than one dNSName name, a match in any one
     * of the set is considered acceptable.) Names may contain the wildcard
     * character * which is considered to match any single domain name
     * component or component fragment. E.g., *.a.com matches foo.a.com but
     * not bar.foo.a.com. f*.com matches foo.com but not bar.com.
     */
    public function validateURL(string $url): bool
    {
        $components = parse_url($url);
        if (!isset($components['host'])) {
            throw new UnexpectedValueException('Unable to parse URL');
        }

        if ($names = $this->getExtension('id-ce-subjectAltName')) {
            foreach ($names['extnValue'] as $name) {
                $key = $name->index;
                $value = (string) $name->value;
                $value = preg_quote($value);
                $value = str_replace('\*', '[^.]*', $value);
                switch ($key) {
                    case 'dNSName':
                        /* From RFC2818 "HTTP over TLS":

                           If a subjectAltName extension of type dNSName is present, that MUST
                           be used as the identity. Otherwise, the (most specific) Common Name
                           field in the Subject field of the certificate MUST be used. Although
                           the use of the Common Name is existing practice, it is deprecated and
                           Certification Authorities are encouraged to use the dNSName instead. */
                        if (preg_match('#^' . $value . '$#', $components['host'])) {
                            return true;
                        }
                        break;
                    case 'iPAddress':
                        /* From RFC2818 "HTTP over TLS":

                           In some cases, the URI is specified as an IP address rather than a
                           hostname. In this case, the iPAddress subjectAltName must be present
                           in the certificate and must exactly match the IP in the URI. */
                        if (preg_match('#(?:\d{1-3}\.){4}#', $components['host'] . '.') && preg_match('#^' . $value . '$#', $components['host'])) {
                            return true;
                        }
                }
            }
            return false;
        }

        if ($value = $this->getSubjectDNProps('id-at-commonName')) {
            $value = str_replace(['.', '*'], ['\.', '[^.]*'], $value[0]);
            return preg_match('#^' . $value . '$#', $components['host']) === 1;
        }

        return false;
    }

    /**
     * Validate a date
     *
     * If $date isn't defined it is assumed to be the current date.
     */
    private function validateDate(\DateTimeInterface|string|null $date = null): bool
    {
        if (!$date instanceof \DateTimeInterface) {
            $date = new \DateTimeImmutable($date ?? 'now', new \DateTimeZone(@date_default_timezone_get()));
        }

        $notBefore = $this->cert['tbsCertificate']['validity']['notBefore'];
        $notBefore = $notBefore['generalTime'] ?? $notBefore['utcTime'];

        $notAfter = $this->cert['tbsCertificate']['validity']['notAfter'];
        $notAfter = $notAfter['generalTime'] ?? $notAfter['utcTime'];

        $notBefore = new \DateTimeImmutable("$notBefore", new \DateTimeZone(@date_default_timezone_get()));
        $notAfter = new \DateTimeImmutable("$notAfter", new \DateTimeZone(@date_default_timezone_get()));

        return $date >= $notBefore && $date <= $notAfter;
    }

    /**
     * Sets the recursion limit
     *
     * When validating a signature it may be necessary to download intermediate certs from URI's.
     * An intermediate cert that linked to itself would result in an infinite loop so to prevent
     * that we set a recursion limit. A negative number means that there is no recursion limit.
     */
    public static function setRecurLimit(int $count): void
    {
        self::$recur_limit = $count;
    }

    /**
     * Validate a signature
     *
     * Returns true if the signature is verified, false if it is not correct or on error
     *
     * By default returns false for self-signed certs. Call validateSignature(false) to make this support
     * self-signed.
     *
     * The behavior of this function is inspired by {@link http://php.net/openssl-verify openssl_verify}.
     */
    public function validateSignature(bool $caonly = true): bool
    {
        return $this->validateSignatureCountable($caonly, 0) && $this->testPathLen();
    }

    /**
     * Tests whether or not the X509 cert issued the $subject
     *
     * If $subject is an array or an instance of Choice it should have _either_:
     *
     *  - issuerAndSerialNumber
     *  - subjectKeyIdentifier
     */
    public function isIssuerOf(self|CRL|Choice|array $subject, array $expectedKeyUsage = []): bool
    {
        if (self::$checkKeyUsage && !$this->isCA) {
            if (!$this->hasExtension('id-ce-keyUsage')) {
                return false;
            }
            switch (true) {
                case is_array($subject) || $subject instanceof Choice:
                    $expected = $expectedKeyUsage;
                    break;
                case $subject instanceof self:
                    $expected = ['keyCertSign'];
                    break;
                //case $subject instanceof CRL:
                default:
                    $expected = ['cRLSign'];
            }
            $ext = $this->getExtension('id-ce-keyUsage')['extnValue'];
            $found = false;
            foreach ($expected as $search) {
                switch (true) {
                    case $ext instanceof BitString && $ext->contains($search):
                    case is_array($ext) && in_array($search, $ext):
                        $found = true;
                }
            }
            if (!$found) {
                return false;
            }

            if (self::$checkBasicConstraints && $expected == 'keyCertSign' && !$this->isCA) {
                if (!$this->hasExtension('id-ce-basicConstraints')) {
                    return false;
                }
                $ext = $this->getExtension('id-ce-basicConstraints')['extnValue'];
                switch (true) {
                    case $ext['cA'] instanceof Boolean && !$ext['cA']->value:
                    case is_bool($ext['cA']) && !$ext['cA']:
                        return false;
                }
            }
        }

        if (is_array($subject) || $subject instanceof Choice) {
            if (isset($subject['issuerAndSerialNumber'])) {
                switch (true) {
                    case self::$strictDNComparison && self::formatDN($subject['issuerAndSerialNumber']['issuer'], self::DN_ASN1) === $this->getIssuerDN(self::DN_ASN1):
                    case !self::$strictDNComparison && self::formatDN($subject['issuerAndSerialNumber']['issuer'], self::DN_CANON) === $this->getIssuerDN(self::DN_CANON):
                        return $subject['issuerAndSerialNumber']['serialNumber']->equals($this->cert['tbsCertificate']['serialNumber']);
                }
                return false;
            }
            if (isset($subject['subjectKeyIdentifier'])) {
                $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier');
                return isset($subjectKeyID['subjectKeyIdentifier']) && $subject['subjectKeyIdentifier']->value === $subjectKeyID['extnValue']->value;
            }
            // it shouldn't be possible to get to this point
            return false;
        }

        switch (true) {
            case self::$strictDNComparison && $subject->getIssuerDN(self::DN_ASN1) === $this->getSubjectDN(self::DN_ASN1):
            case !self::$strictDNComparison && $subject->getIssuerDN(self::DN_CANON) === $this->getSubjectDN(self::DN_CANON):
                $authorityKey = $subject->getExtension('id-ce-authorityKeyIdentifier');
                $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier');
                switch (true) {
                    case !isset($authorityKey) || !isset($subjectKeyID):
                    case isset($authorityKey['extnValue']['keyIdentifier']) && $authorityKey['extnValue']['keyIdentifier']->value === $subjectKeyID['extnValue']->value:
                    //case !isset($authorityKey) && !isset($subjectKeyID):
                    //case isset($subjectKeyID) && isset($authorityKey) && isset($authorityKey['extnValue']['keyIdentifier']) && $authorityKey['extnValue']['keyIdentifier']->value === $subjectKeyID['extnValue']->value:
                        if (isset($authorityKey) && isset($authorityKey['extnValue']['authorityCertSerialNumber']) && !$authorityKey['extnValue']['authorityCertSerialNumber']->equals($this['tbsCertificate']['serialNumber'])) {
                            return false;
                        }
                        return true;
                }
        }
        return false;
    }

    // returns true if the X509 is NOT revoked and false if it is
    private function validateNonRevokedStatus(): bool
    {
        $crl = $this->getExtension('id-ce-cRLDistributionPoints');
        if ($crl) {
            $key = '*/distributionPoint/fullName/*/uniformResourceIdentifier';
            try {
                $url = (string) Arrays::subArrayWithWildcards($crl['extnValue'], $key);
                // self::$inCRLFunction should return true if the serial number IS in the CRL
                if ((self::$inCRLFunction)($url, $this->cert['tbsCertificate']['serialNumber'])) {
                    return false;
                }
            } catch (UnexpectedValueException) {
                // there's not a URI for us to get the CRL from so we just won't check it
            }
        }
        return true;
    }

    /**
     * Validate a signature
     *
     * Performs said validation whilst keeping track of how many times validation method is called
     */
    private function validateSignatureCountable(bool $caonly, int $count): bool
    {
        if ($count == self::$recur_limit) {
            return false;
        }

        foreach (self::$CAs as $ca) {
            // even if the cert is a self-signed one we still want to see if it's a CA;
            // if not, we'll conditionally return an error
            if ($ca->isIssuerOf($this)) {
                $signingCert = $ca;
                break;
            }
        }

        if (!isset($signingCert)) {
            if ($caonly) {
                return $this->testForIntermediate(true, $count) && $this->validateSignature(true);
            } else {
                try {
                    $this->testForSelfSigned();
                    $signingCert = $this;
                } catch (BadMethodCallException) {
                    return $this->testForIntermediate(true, $count) && $this->validateSignature(true);
                }
            }
        }

        if (!$this->validateNonRevokedStatus()) {
            return false;
        }

        $signatureResult = self::validateSignatureHelper(
            $signingCert->getPublicKey(),
            $this->cert['signatureAlgorithm']['algorithm'],
            $this->cert['signature'],
            $this->cert['tbsCertificate']->getEncoded()
        );
        $dateResult = isset(self::$targetValidationDate) ? $this->validateDate(self::$targetValidationDate) : true;
        $result = $signatureResult && $dateResult;
        if ($result) {
            $this->issuer = $signingCert;
            $this->issuer->caSeq ??= 0;
            $this->caSeq = $this->issuer->caSeq + 1;
        }
        return $result;
    }

    /**
     * Validates an intermediate cert as identified via authority info access extension
     *
     * See https://tools.ietf.org/html/rfc4325 for more info
     */
    private function testForIntermediate(bool $caonly, int $count): bool
    {
        $opts = $this->getExtension('id-pe-authorityInfoAccess');
        if (!isset($opts)) {
            return false;
        }
        foreach ($opts['extnValue'] as $opt) {
            if ($opt['accessMethod'] == 'id-ad-caIssuers') {
                // accessLocation is a GeneralName. GeneralName fields support stuff like email addresses, IP addresses, LDAP,
                // etc, but we're only supporting URI's. URI's and LDAP are the only thing https://tools.ietf.org/html/rfc4325
                // discusses
                if (isset($opt['accessLocation']['uniformResourceIdentifier'])) {
                    $url = (string) $opt['accessLocation']['uniformResourceIdentifier'];
                    break;
                }
            }
        }

        if (!isset($url)) {
            return false;
        }

        $cert = static::fetchURL($url);
        if (!isset($cert)) {
            return false;
        }

        /*
         "Conforming applications that support HTTP or FTP for accessing
          certificates MUST be able to accept .cer files and SHOULD be able
          to accept .p7c files." -- https://tools.ietf.org/html/rfc4325

         A .p7c file is 'a "certs-only" CMS message as specified in RFC 2797"'

         These are currently unsupported
        */
        $parent = self::load($cert);

        if (!$parent->validateSignatureCountable($caonly, ++$count)) {
            return false;
        }

        self::$CAs[] = $parent;

        return true;
    }

    /**
     * The first cert is the original CA cert and the last cert is the current cert
     * If the chain doesn't ultimately lead to a CA cert (or at least one that was imported
     * via ::addCA) then the array returned will be one long. Otherwise it'll be at least
     * two long.
     *
     * If you're trying to get the chain of a self signed cert that has a copy of itself
     * in the CA store then I guess you'll get a two element array back wherein the first
     * and last elements are the same.
     */
    public function getValidationChain(): array
    {
        if (!isset($this->issuer)) {
            $this->validateSignature();
            if (!isset($this->issuer)) {
                return [$this];
            }
        }
        $certs = [];
        $current = $this;
        while ($current->issuer) {
            array_unshift($certs, $current);
            if ($current->issuer === $current) {
                break;
            }
            $current = $current->issuer;
        }
        array_unshift($certs, $current);
        return $certs;
    }

    private function testPathLen(): bool
    {
        if (!self::$checkBasicConstraints) {
            return true;
        }
        $certs = $this->getValidationChain();
        for ($i = count($certs) - 1; $i > 0; $i--) {
            $cert = $certs[$i];
            $issuer = $cert->issuer;
            $ext = $issuer->getExtension('id-ce-basicConstraints');
            if (isset($ext['extnValue']['pathLenConstraint'])) {
                $pathLenConstraint = (int) ((string) $ext['extnValue']['pathLenConstraint']);
                if ($this->caSeq - $issuer->caSeq - 1 > $pathLenConstraint) {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * Fetches a URL
     *
     * The host is resolved once and the connection is pinned to that IP. A fetch
     * callback registered via setURLFetchCallback() judges the resolved IP and must
     * approve it for the fetch to proceed; with no callback registered, no fetch
     * happens at all. This is the secure default that replaces disableURLFetch().
     * Redirects are intentionally NOT followed, so a permitted host cannot bounce
     * the fetch to an ungated internal address.
     */
    private static function fetchURL(string $url): ?string
    {
        $parts = parse_url($url);
        if ($parts === false || !isset($parts['scheme'], $parts['host'])) {
            return null;
        }
        $host = $parts['host'];
        $port = $parts['port'] ?? 80;

        if (filter_var($host, FILTER_VALIDATE_IP)) {
            $ip = $host;
            if (preg_match('/^::ffff:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/i', $ip, $m)) {
                $ip = $m[1];
            }
        } else {
            $records = dns_get_record($host, DNS_A | DNS_AAAA);
            if (!$records) {
                return null;
            }
            $ip = $records[0]['ip'] ?? $records[0]['ipv6'] ?? null;
            if ($ip === null) {
                return null;
            }
        }

        if (!(static::$urlFetchCallback)($host, $ip, $port, $parts['scheme'])) {
            return null;
        }

        $target = strpos($ip, ':') !== false ? "[$ip]" : $ip;

        $data = '';
        switch ($parts['scheme']) {
            case 'http':
                $fsock = @fsockopen($target, $port);
                if (!$fsock) {
                    return null;
                }
                $path = $parts['path'] ?? '/';
                if (isset($parts['query'])) {
                    $path .= '?' . $parts['query'];
                }
                fputs($fsock, "GET $path HTTP/1.0\r\n");
                fputs($fsock, "Host: $host\r\n\r\n");
                $line = fgets($fsock, 1024);
                if ($line === false || strlen($line) < 3) {
                    return null;
                }
                if (!preg_match('#HTTP/1\.\d (\d{3})#', $line, $temp) || $temp[1] != '200') {
                    return null;
                }
                while (!feof($fsock) && fgets($fsock, 1024) != "\r\n") {
                }
                while (!feof($fsock)) {
                    $temp = fread($fsock, 1024);
                    if ($temp === false) {
                        return null;
                    }
                    $data .= $temp;
                }
                break;
            //case 'ftp':
            //case 'ldap':
            default:
                return null; // fail closed on unhandled schemes
        }

        return $data;
    }

    public static function setCRLLookupCallback(\Closure $func): void
    {
        self::$inCRLFunction = $func;
    }

    public static function setURLFetchCallback(\Closure $func): void
    {
        static::$urlFetchCallback = $func;
    }

    public function enableHideFullDecode(): void
    {
        $this->hideFullDecode = true;
    }
}
<?php

/**
 * Pure-PHP ANSI Decoder
 *
 * PHP version 8.1+
 *
 * If you call read() in \phpseclib4\Net\SSH2 you may get {@link http://en.wikipedia.org/wiki/ANSI_escape_code ANSI escape codes} back.
 * They'd look like chr(0x1B) . '[00m' or whatever (0x1B = ESC).  They tell a
 * {@link http://en.wikipedia.org/wiki/Terminal_emulator terminal emulator} how to format the characters, what
 * color to display them in, etc. \phpseclib4\File\ANSI is a {@link http://en.wikipedia.org/wiki/VT100 VT100} terminal emulator.
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2012-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\File;

/**
 * Pure-PHP ANSI Decoder
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
class ANSI
{
    /**
     * Max Width
     */
    private int $max_x;

    /**
     * Max Height
     */
    private int $max_y;

    /**
     * Max History
     */
    private int $max_history;

    /**
     * History
     */
    private array $history;

    /**
     * History Attributes
     */
    private array $history_attrs;

    /**
     * Current Column
     */
    private int $x;

    /**
     * Current Row
     */
    private int $y;

    /**
     * Old Column
     *
     * @psalm-suppress UnusedProperty
     */
    private int $old_x;

    /**
     * Old Row
     */
    private int $old_y;

    /**
     * An empty attribute cell
     */
    private \stdClass $base_attr_cell;

    /**
     * The current attribute cell
     */
    private \stdClass $attr_cell;

    /**
     * An empty attribute row
     */
    private array $attr_row;

    /**
     * The current screen text
     *
     * @var list<string>
     */
    private array $screen;

    /**
     * The current screen attributes
     */
    private array $attrs;

    /**
     * Current ANSI code
     */
    private string $ansi;

    /**
     * Tokenization
     */
    private array $tokenization;

    /**
     * Default Constructor.
     *
     * @return ANSI
     */
    public function __construct()
    {
        $attr_cell = new \stdClass();
        $attr_cell->bold = false;
        $attr_cell->underline = false;
        $attr_cell->blink = false;
        $attr_cell->background = 'black';
        $attr_cell->foreground = 'white';
        $attr_cell->reverse = false;
        $this->base_attr_cell = clone $attr_cell;
        $this->attr_cell = clone $attr_cell;

        $this->setHistory(200);
        $this->setDimensions(80, 24);
    }

    /**
     * Set terminal width and height
     *
     * Resets the screen as well
     */
    public function setDimensions(int $x, int $y): void
    {
        $this->max_x = $x - 1;
        $this->max_y = $y - 1;
        $this->x = $this->y = 0;
        $this->history = $this->history_attrs = [];
        $this->attr_row = array_fill(0, $this->max_x + 2, $this->base_attr_cell);
        $this->screen = array_fill(0, $this->max_y + 1, '');
        $this->attrs = array_fill(0, $this->max_y + 1, $this->attr_row);
        $this->ansi = '';
    }

    /**
     * Set the number of lines that should be logged past the terminal height
     */
    public function setHistory(int $history): void
    {
        $this->max_history = $history;
    }

    /**
     * Load a string
     */
    public function loadString(string $source): void
    {
        $this->setDimensions($this->max_x + 1, $this->max_y + 1);
        $this->appendString($source);
    }

    /**
     * Appdend a string
     */
    public function appendString(string $source): void
    {
        $this->tokenization = [''];
        for ($i = 0; $i < strlen($source); $i++) {
            if (strlen($this->ansi)) {
                $this->ansi .= $source[$i];
                $chr = ord($source[$i]);
                // http://en.wikipedia.org/wiki/ANSI_escape_code#Sequence_elements
                // single character CSI's not currently supported
                switch (true) {
                    case $this->ansi == "\x1B=":
                        $this->ansi = '';
                        continue 2;
                    case strlen($this->ansi) == 2 && $chr >= 64 && $chr <= 95 && $chr != ord('['):
                    case strlen($this->ansi) > 2 && $chr >= 64 && $chr <= 126:
                        break;
                    default:
                        continue 2;
                }
                $this->tokenization[] = $this->ansi;
                $this->tokenization[] = '';
                // http://ascii-table.com/ansi-escape-sequences-vt-100.php
                switch ($this->ansi) {
                    case "\x1B[H": // Move cursor to upper left corner
                        $this->old_x = $this->x;
                        $this->old_y = $this->y;
                        $this->x = $this->y = 0;
                        break;
                    case "\x1B[J": // Clear screen from cursor down
                        $this->history = array_merge($this->history, array_slice(array_splice($this->screen, $this->y + 1), 0, $this->old_y));
                        $this->screen = array_merge($this->screen, array_fill($this->y, $this->max_y, ''));

                        $this->history_attrs = array_merge($this->history_attrs, array_slice(array_splice($this->attrs, $this->y + 1), 0, $this->old_y));
                        $this->attrs = array_merge($this->attrs, array_fill($this->y, $this->max_y, $this->attr_row));

                        if (count($this->history) == $this->max_history) {
                            array_shift($this->history);
                            array_shift($this->history_attrs);
                        }
                        // no break
                    case "\x1B[K": // Clear screen from cursor right
                        $this->screen[$this->y] = substr($this->screen[$this->y], 0, $this->x);

                        array_splice($this->attrs[$this->y], $this->x + 1, $this->max_x - $this->x, array_fill($this->x, $this->max_x - ($this->x - 1), $this->base_attr_cell));
                        break;
                    case "\x1B[2K": // Clear entire line
                        $this->screen[$this->y] = str_repeat(' ', $this->x);
                        $this->attrs[$this->y] = $this->attr_row;
                        break;
                    case "\x1B[?1h": // set cursor key to application
                    case "\x1B[?25h": // show the cursor
                    case "\x1B(B": // set united states g0 character set
                        break;
                    case "\x1BE": // Move to next line
                        $this->newLine();
                        $this->x = 0;
                        break;
                    default:
                        switch (true) {
                            case preg_match('#\x1B\[(\d+)B#', $this->ansi, $match): // Move cursor down n lines
                                $this->old_y = $this->y;
                                $this->y += (int) $match[1];
                                break;
                            case preg_match('#\x1B\[(\d+);(\d+)H#', $this->ansi, $match): // Move cursor to screen location v,h
                                $this->old_x = $this->x;
                                $this->old_y = $this->y;
                                $this->x = $match[2] - 1;
                                $this->y = (int) $match[1] - 1;
                                break;
                            case preg_match('#\x1B\[(\d+)C#', $this->ansi, $match): // Move cursor right n lines
                                $this->old_x = $this->x;
                                $this->x += $match[1];
                                break;
                            case preg_match('#\x1B\[(\d+)D#', $this->ansi, $match): // Move cursor left n lines
                                $this->old_x = $this->x;
                                $this->x -= $match[1];
                                if ($this->x < 0) {
                                    $this->x = 0;
                                }
                                break;
                            case preg_match('#\x1B\[(\d+);(\d+)r#', $this->ansi, $match): // Set top and bottom lines of a window
                                break;
                            case preg_match('#\x1B\[(\d*(?:;\d*)*)m#', $this->ansi, $match): // character attributes
                                $attr_cell = &$this->attr_cell;
                                $mods = explode(';', $match[1]);
                                foreach ($mods as $mod) {
                                    switch ($mod) {
                                        case '':
                                        case '0': // Turn off character attributes
                                            $attr_cell = clone $this->base_attr_cell;
                                            break;
                                        case '1': // Turn bold mode on
                                            $attr_cell->bold = true;
                                            break;
                                        case '4': // Turn underline mode on
                                            $attr_cell->underline = true;
                                            break;
                                        case '5': // Turn blinking mode on
                                            $attr_cell->blink = true;
                                            break;
                                        case '7': // Turn reverse video on
                                            $attr_cell->reverse = !$attr_cell->reverse;
                                            $temp = $attr_cell->background;
                                            $attr_cell->background = $attr_cell->foreground;
                                            $attr_cell->foreground = $temp;
                                            break;
                                        default: // set colors
                                            //$front = $attr_cell->reverse ? &$attr_cell->background : &$attr_cell->foreground;
                                            $front = &$attr_cell->{ $attr_cell->reverse ? 'background' : 'foreground' };
                                            //$back = $attr_cell->reverse ? &$attr_cell->foreground : &$attr_cell->background;
                                            $back = &$attr_cell->{ $attr_cell->reverse ? 'foreground' : 'background' };
                                            switch ($mod) {
                                                // @codingStandardsIgnoreStart
                                                case '30': $front = 'black'; break;
                                                case '31': $front = 'red'; break;
                                                case '32': $front = 'green'; break;
                                                case '33': $front = 'yellow'; break;
                                                case '34': $front = 'blue'; break;
                                                case '35': $front = 'magenta'; break;
                                                case '36': $front = 'cyan'; break;
                                                case '37': $front = 'white'; break;

                                                case '40': $back = 'black'; break;
                                                case '41': $back = 'red'; break;
                                                case '42': $back = 'green'; break;
                                                case '43': $back = 'yellow'; break;
                                                case '44': $back = 'blue'; break;
                                                case '45': $back = 'magenta'; break;
                                                case '46': $back = 'cyan'; break;
                                                case '47': $back = 'white'; break;
                                                // @codingStandardsIgnoreEnd

                                                default:
                                                    //user_error('Unsupported attribute: ' . $mod);
                                                    $this->ansi = '';
                                                    break 2;
                                            }
                                    }
                                }
                                break;
                            default:
                                //user_error("{$this->ansi} is unsupported\r\n");
                        }
                }
                $this->ansi = '';
                continue;
            }

            $this->tokenization[count($this->tokenization) - 1] .= $source[$i];
            switch ($source[$i]) {
                case "\r":
                    $this->x = 0;
                    break;
                case "\n":
                    $this->newLine();
                    break;
                case "\x08": // backspace
                    if ($this->x) {
                        $this->x--;
                        $this->attrs[$this->y][$this->x] = clone $this->base_attr_cell;
                        $this->screen[$this->y] = substr_replace(
                            $this->screen[$this->y],
                            $source[$i],
                            $this->x,
                            1
                        );
                    }
                    break;
                case "\x0F": // shift
                    break;
                case "\x1B": // start ANSI escape code
                    $this->tokenization[count($this->tokenization) - 1] = substr($this->tokenization[count($this->tokenization) - 1], 0, -1);
                    //if (!strlen($this->tokenization[count($this->tokenization) - 1])) {
                    //    array_pop($this->tokenization);
                    //}
                    $this->ansi .= "\x1B";
                    break;
                default:
                    $this->attrs[$this->y][$this->x] = clone $this->attr_cell;
                    if ($this->x > strlen($this->screen[$this->y])) {
                        $this->screen[$this->y] = str_repeat(' ', $this->x);
                    }
                    $this->screen[$this->y] = substr_replace(
                        $this->screen[$this->y],
                        $source[$i],
                        $this->x,
                        1
                    );

                    if ($this->x > $this->max_x) {
                        $this->x = 0;
                        $this->newLine();
                    } else {
                        $this->x++;
                    }
            }
        }
    }

    /**
     * Add a new line
     *
     * Also update the $this->screen and $this->history buffers
     */
    private function newLine(): void
    {
        //if ($this->y < $this->max_y) {
        //    $this->y++;
        //}

        while ($this->y >= $this->max_y) {
            $this->history = array_merge($this->history, [array_shift($this->screen)]);
            $this->screen[] = '';

            $this->history_attrs = array_merge($this->history_attrs, [array_shift($this->attrs)]);
            $this->attrs[] = $this->attr_row;

            if (count($this->history) >= $this->max_history) {
                array_shift($this->history);
                array_shift($this->history_attrs);
            }

            $this->y--;
        }
        $this->y++;
    }

    /**
     * Returns the current coordinate without preformating
     */
    private function processCoordinate(\stdClass $last_attr, \stdClass $cur_attr, string $char): string
    {
        $output = '';

        if ($last_attr != $cur_attr) {
            $close = $open = '';
            if ($last_attr->foreground != $cur_attr->foreground) {
                if ($cur_attr->foreground != 'white') {
                    $open .= '<span style="color: ' . $cur_attr->foreground . '">';
                }
                if ($last_attr->foreground != 'white') {
                    $close = '</span>' . $close;
                }
            }
            if ($last_attr->background != $cur_attr->background) {
                if ($cur_attr->background != 'black') {
                    $open .= '<span style="background: ' . $cur_attr->background . '">';
                }
                if ($last_attr->background != 'black') {
                    $close = '</span>' . $close;
                }
            }
            if ($last_attr->bold != $cur_attr->bold) {
                if ($cur_attr->bold) {
                    $open .= '<b>';
                } else {
                    $close = '</b>' . $close;
                }
            }
            if ($last_attr->underline != $cur_attr->underline) {
                if ($cur_attr->underline) {
                    $open .= '<u>';
                } else {
                    $close = '</u>' . $close;
                }
            }
            if ($last_attr->blink != $cur_attr->blink) {
                if ($cur_attr->blink) {
                    $open .= '<blink>';
                } else {
                    $close = '</blink>' . $close;
                }
            }
            $output .= $close . $open;
        }

        $output .= htmlspecialchars($char);

        return $output;
    }

    /**
     * Returns the current screen without preformating
     */
    private function getScreenHelper(): string
    {
        $output = '';
        $last_attr = $this->base_attr_cell;
        for ($i = 0; $i <= $this->max_y; $i++) {
            for ($j = 0; $j <= $this->max_x; $j++) {
                $cur_attr = $this->attrs[$i][$j];
                $output .= $this->processCoordinate($last_attr, $cur_attr, $this->screen[$i][$j] ?? '');
                $last_attr = $this->attrs[$i][$j];
            }
            $output .= "\r\n";
        }
        $output = substr($output, 0, -2);
        // close any remaining open tags
        $output .= $this->processCoordinate($last_attr, $this->base_attr_cell, '');
        return rtrim($output);
    }

    /**
     * Returns the current screen
     */
    public function getScreen(): string
    {
        return '<pre width="' . ($this->max_x + 1) . '" style="color: white; background: black">' . $this->getScreenHelper() . '</pre>';
    }

    /**
     * Returns the current screen and the x previous lines
     */
    public function getHistory(): string
    {
        $scrollback = '';
        $last_attr = $this->base_attr_cell;
        for ($i = 0; $i < count($this->history); $i++) {
            for ($j = 0; $j <= $this->max_x + 1; $j++) {
                $cur_attr = $this->history_attrs[$i][$j];
                $scrollback .= $this->processCoordinate($last_attr, $cur_attr, $this->history[$i][$j] ?? '');
                $last_attr = $this->history_attrs[$i][$j];
            }
            $scrollback .= "\r\n";
        }
        $base_attr_cell = $this->base_attr_cell;
        $this->base_attr_cell = $last_attr;
        $scrollback .= $this->getScreen();
        $this->base_attr_cell = $base_attr_cell;

        return '<pre width="' . ($this->max_x + 1) . '" style="color: white; background: black">' . $scrollback . '</span></pre>';
    }
}
<?php

/**
 * Pure-PHP ssh-agent client.
 *
 * {@internal See http://api.libssh.org/rfc/PROTOCOL.agent}
 *
 * PHP version 8.1+
 *
 * Here are some examples of how to use this library:
 * <code>
 * <?php
 *    include 'vendor/autoload.php';
 *
 *    $agent = new \phpseclib4\System\SSH\Agent();
 *
 *    $ssh = new \phpseclib4\Net\SSH2('www.domain.tld');
 *    if (!$ssh->login('username', $agent)) {
 *        exit('Login Failed');
 *    }
 *
 *    echo $ssh->exec('pwd');
 *    echo $ssh->exec('ls -la');
 * ?>
 * </code>
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2014-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\System\SSH;

use phpseclib4\Common\Functions\Strings;
use phpseclib4\Crypt\Common\{AsymmetricKey, PublicKey};
use phpseclib4\Crypt\PublicKeyLoader;
use phpseclib4\Exception\{
    BadConfigurationException,
    ConnectionClosedException,
    ServiceUnavailableException,
    UnexpectedValueException
};
use phpseclib4\Net\SSH2;
use phpseclib4\System\SSH\Agent\Identity;

/**
 * Pure-PHP ssh-agent client identity factory
 *
 * requestIdentities() method pumps out \phpseclib4\System\SSH\Agent\Identity objects
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class Agent
{
    use Common\Traits\ReadBytes;

    // Message numbers

    // to request SSH1 keys you have to use SSH_AGENTC_REQUEST_RSA_IDENTITIES (1)
    public const SSH_AGENTC_REQUEST_IDENTITIES = 11;
    // this is the SSH2 response; the SSH1 response is SSH_AGENT_RSA_IDENTITIES_ANSWER (2).
    public const SSH_AGENT_IDENTITIES_ANSWER = 12;
    // the SSH1 request is SSH_AGENTC_RSA_CHALLENGE (3)
    public const SSH_AGENTC_SIGN_REQUEST = 13;
    // the SSH1 response is SSH_AGENT_RSA_RESPONSE (4)
    public const SSH_AGENT_SIGN_RESPONSE = 14;

    // Agent forwarding status

    // no forwarding requested and not active
    public const FORWARD_NONE = 0;
    // request agent forwarding when opportune
    public const FORWARD_REQUEST = 1;
    // forwarding has been request and is active
    public const FORWARD_ACTIVE = 2;

    /**
     * Unused
     */
    public const SSH_AGENT_FAILURE = 5;

    /**
     * Socket Resource
     *
     * @var resource
     */
    private $fsock;

    /**
     * Agent forwarding status
     */
    private int $forward_status = self::FORWARD_NONE;

    /**
     * Buffer for accumulating forwarded authentication
     * agent data arriving on SSH data channel destined
     * for agent unix socket
     */
    private string $socket_buffer = '';

    /**
     * Tracking the number of bytes we are expecting
     * to arrive for the agent socket on the SSH data
     * channel
     */
    private int $expected_bytes = 0;

    /**
     * Default Constructor
     */
    public function __construct(?string $address = null)
    {
        $address = $address ??
                   $_SERVER['SSH_AUTH_SOCK'] ??
                   $_ENV['SSH_AUTH_SOCK'] ??
                   throw new BadConfigurationException('SSH_AUTH_SOCK not found');

        if (in_array('unix', stream_get_transports())) {
            $this->fsock = fsockopen('unix://' . $address, 0, $errno, $errstr);
            if (!$this->fsock) {
                throw new ServiceUnavailableException("Unable to connect to ssh-agent (Error $errno: $errstr)");
            }
        } else {
            if (substr($address, 0, 9) != '\\\\.\\pipe\\' || str_contains(substr($address, 9), '\\')) {
                throw new UnexpectedValueException('Address is not formatted as a named pipe should be');
            }

            $this->fsock = fopen($address, 'r+b');
            if (!$this->fsock) {
                throw new ServiceUnavailableException('Unable to open address');
            }
        }
    }

    /**
     * Request Identities
     *
     * See "2.5.2 Requesting a list of protocol 2 keys"
     * Returns an array containing zero or more \phpseclib4\System\SSH\Agent\Identity objects
     */
    public function requestIdentities(): array
    {
        if (!$this->fsock) {
            return [];
        }

        $packet = pack('NC', 1, self::SSH_AGENTC_REQUEST_IDENTITIES);
        if (strlen($packet) != fwrite($this->fsock, $packet)) {
            throw new ConnectionClosedException('Connection closed while requesting identities');
        }

        $length = current(unpack('N', $this->readBytes(4)));
        $packet = $this->readBytes($length);

        [$type, $keyCount] = Strings::unpackSSH2('CN', $packet);
        if ($type != self::SSH_AGENT_IDENTITIES_ANSWER) {
            throw new UnexpectedValueException('Unable to request identities');
        }

        $identities = [];
        for ($i = 0; $i < $keyCount; $i++) {
            [$key_blob, $comment] = Strings::unpackSSH2('ss', $packet);
            $temp = $key_blob;
            [$key_type] = Strings::unpackSSH2('s', $temp);
            switch ($key_type) {
                case 'ssh-rsa':
                case 'ssh-dss':
                case 'ssh-ed25519':
                case 'ecdsa-sha2-nistp256':
                case 'ecdsa-sha2-nistp384':
                case 'ecdsa-sha2-nistp521':
                    $key = PublicKeyLoader::load($key_type . ' ' . base64_encode($key_blob));
            }
            // resources are passed by reference by default
            if (isset($key)) {
                $identity = (new Identity($this->fsock))
                    ->withPublicKey($key)
                    ->withPublicKeyBlob($key_blob)
                    ->withComment($comment);
                $identities[] = $identity;
                unset($key);
            }
        }

        return $identities;
    }

    /**
     * Returns the SSH Agent identity matching a given public key or null if no identity is found
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function findIdentityByPublicKey(PublicKey&AsymmetricKey $key): ?Identity
    {
        $identities = $this->requestIdentities();
        $key = (string) $key;
        foreach ($identities as $identity) {
            if (((string) $identity->getPublicKey()) == $key) {
                return $identity;
            }
        }

        return null;
    }

    /**
     * Signal that agent forwarding should
     * be requested when a channel is opened
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function startSSHForwarding(): void
    {
        if ($this->forward_status == self::FORWARD_NONE) {
            $this->forward_status = self::FORWARD_REQUEST;
        }
    }

    /**
     * Request agent forwarding of remote server
     *
     * @psalm-suppress UnusedReturnValue
     */
    private function request_forwarding(SSH2 $ssh): bool
    {
        if (!$ssh->requestAgentForwarding()) {
            return false;
        }

        $this->forward_status = self::FORWARD_ACTIVE;

        return true;
    }

    /**
     * On successful channel open
     *
     * This method is called upon successful channel
     * open to give the SSH Agent an opportunity
     * to take further action. i.e. request agent forwarding
     */
    public function registerChannelOpen(SSH2 $ssh): void
    {
        if ($this->forward_status == self::FORWARD_REQUEST) {
            $this->request_forwarding($ssh);
        }
    }

    /**
     * Forward data to SSH Agent and return data reply
     */
    public function forwardData(string $data): ?string
    {
        if ($this->expected_bytes > 0) {
            $this->socket_buffer .= $data;
            $this->expected_bytes -= strlen($data);
        } else {
            $agent_data_bytes = current(unpack('N', $data));
            $current_data_bytes = strlen($data);
            $this->socket_buffer = $data;
            if ($current_data_bytes != $agent_data_bytes + 4) {
                $this->expected_bytes = ($agent_data_bytes + 4) - $current_data_bytes;
                return null;
            }
        }

        if (strlen($this->socket_buffer) != fwrite($this->fsock, $this->socket_buffer)) {
            throw new ConnectionClosedException('Connection closed attempting to forward data to SSH agent');
        }

        $this->socket_buffer = '';
        $this->expected_bytes = 0;

        $agent_reply_bytes = current(unpack('N', $this->readBytes(4)));

        $agent_reply_data = $this->readBytes($agent_reply_bytes);
        $agent_reply_data = current(unpack('a*', $agent_reply_data));

        return pack('Na*', $agent_reply_bytes, $agent_reply_data);
    }
}
<?php

/**
 * Pure-PHP ssh-agent client.
 *
 * {@internal See http://api.libssh.org/rfc/PROTOCOL.agent}
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2014-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\System\SSH\Agent;

use phpseclib4\Common\Functions\Strings;
use phpseclib4\Crypt\Common\{PrivateKey, PublicKey};
use phpseclib4\Crypt\{DSA, EC, RSA};
use phpseclib4\Exception\{
    BadMethodCallException,
    ConnectionClosedException,
    UnexpectedValueException,
    UnsupportedAlgorithmException
};
use phpseclib4\File\Common\Signable;
use phpseclib4\File\CSR;
use phpseclib4\System\SSH\Agent;
use phpseclib4\System\SSH\Common\Traits\ReadBytes;

/**
 * Pure-PHP ssh-agent client identity object
 *
 * Instantiation should only be performed by \phpseclib4\System\SSH\Agent class.
 * This could be thought of as implementing an interface that phpseclib4\Crypt\RSA
 * implements. ie. maybe a Net_SSH_Auth_PublicKey interface or something.
 * The methods in this interface would be getPublicKey and sign since those are the
 * methods phpseclib looks for to perform public key authentication.
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class Identity implements PrivateKey
{
    use ReadBytes;

    // Signature Flags
    // See https://tools.ietf.org/html/draft-miller-ssh-agent-00#section-5.3
    public const SSH_AGENT_RSA2_256 = 2;
    public const SSH_AGENT_RSA2_512 = 4;

    /**
     * Key Object
     *
     * @see self::getPublicKey()
     */
    private PublicKey $key;

    /**
     * Key Blob
     *
     * @see self::sign()
     */
    private string $key_blob;

    /**
     * Socket Resource
     *
     * @var resource
     * @see self::sign()
     */
    private $fsock;

    /**
     * Signature flags
     *
     * @see self::sign()
     * @see self::setHash()
     */
    private int $flags = 0;

    /**
     * Comment
     */
    private ?string $comment;

    /**
     * Curve Aliases
     */
    private static array $curveAliases = [
        'secp256r1' => 'nistp256',
        'secp384r1' => 'nistp384',
        'secp521r1' => 'nistp521',
        'Ed25519' => 'Ed25519',
    ];

    /**
     * Default Constructor.
     *
     * @param resource $fsock
     */
    public function __construct($fsock)
    {
        $this->fsock = $fsock;
    }

    /**
     * Set Public Key
     *
     * Called by \phpseclib4\System\SSH\Agent::requestIdentities()
     */
    public function withPublicKey(PublicKey $key): Identity
    {
        if ($key instanceof EC) {
            if (is_array($key->getCurve()) || !isset(self::$curveAliases[$key->getCurve()])) {
                throw new UnsupportedAlgorithmException('The only supported curves are nistp256, nistp384, nistp512 and Ed25519');
            }
        }

        $new = clone $this;
        $new->key = $key;
        return $new;
    }

    /**
     * Set Public Key
     *
     * Called by \phpseclib4\System\SSH\Agent::requestIdentities(). The key blob could be extracted from $this->key
     * but this saves a small amount of computation.
     */
    public function withPublicKeyBlob(string $key_blob): Identity
    {
        $new = clone $this;
        $new->key_blob = $key_blob;
        return $new;
    }

    /**
     * Get Public Key
     *
     * Wrapper for $this->key->getPublicKey()
     */
    public function getPublicKey(): PublicKey
    {
        return $this->key;
    }

    /**
     * Sets the hash
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function withHash(string $hash): Identity
    {
        $new = clone $this;

        $hash = strtolower($hash);

        if ($this->key instanceof RSA) {
            $new->flags = match ($hash) {
                'sha1' => 0,
                'sha256' => self::SSH_AGENT_RSA2_256,
                'sha512' => self::SSH_AGENT_RSA2_512,
                default => throw new UnsupportedAlgorithmException('The only supported hashes for RSA are sha1, sha256 and sha512')
            };
        }
        if ($this->key instanceof EC) {
            $expectedHash = match ($this->key->getCurve()) {
                'secp256r1' => 'sha256',
                'secp384r1' => 'sha384',
                default => 'sha512' // eg. secp512r1 or Ed25519
            };
            if ($hash != $expectedHash) {
                throw new UnsupportedAlgorithmException('The only supported hash for ' . self::$curveAliases[$this->key->getCurve()] . ' is ' . $expectedHash);
            }
        }
        if ($this->key instanceof DSA) {
            if ($hash != 'sha1') {
                throw new UnsupportedAlgorithmException('The only supported hash for DSA is sha1');
            }
        }
        return $new;
    }

    /**
     * Sets the padding
     *
     * Only PKCS1 padding is supported
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function withPadding(int $padding): Identity
    {
        if (!$this->key instanceof RSA) {
            throw new UnsupportedAlgorithmException('Only RSA keys support padding');
        }
        if ($padding != RSA::SIGNATURE_PKCS1) {
            throw new UnsupportedAlgorithmException('ssh-agent can only create PKCS1 signatures');
        }
        return $this;
    }

    /**
     * Determines the signature padding mode
     *
     * Valid values are: ASN1, SSH2, Raw
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function withSignatureFormat(string $format): Identity
    {
        if ($this->key instanceof RSA) {
            throw new UnsupportedAlgorithmException('Only DSA and EC keys support signature format setting');
        }
        if ($format != 'SSH2') {
            throw new UnsupportedAlgorithmException('Only SSH2-formatted signatures are currently supported');
        }

        return $this;
    }

    /**
     * Returns the curve
     *
     * Returns a string if it's a named curve, an array if not
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function getCurve(): string|array
    {
        if (!$this->key instanceof EC) {
            throw new UnsupportedAlgorithmException('Only EC keys have curves');
        }

        return $this->key->getCurve();
    }

    /**
     * Create a signature
     *
     * See "2.6.2 Protocol 2 private key signature request"
     */
    public function sign(string|Signable $source): string
    {
        if ($source instanceof Signable) {
            if ($source instanceof CSR && !$source->hasPublicKey()) {
                $source->setPublicKey($this->getPublicKey());
            }
            $source->identifySignatureAlgorithm($this);
            $message = $source->getSignableSection();
        } else {
            $message = $source;
        }

        // the last parameter (currently 0) is for flags and ssh-agent only defines one flag (for ssh-dss): SSH_AGENT_OLD_SIGNATURE
        $packet = Strings::packSSH2(
            'CssN',
            Agent::SSH_AGENTC_SIGN_REQUEST,
            $this->key_blob,
            $message,
            $this->flags
        );
        $packet = Strings::packSSH2('s', $packet);
        if (strlen($packet) != fwrite($this->fsock, $packet)) {
            throw new ConnectionClosedException('Connection closed during signing');
        }

        $length = current(unpack('N', $this->readBytes(4)));
        $packet = $this->readBytes($length);

        [$type, $signature_blob] = Strings::unpackSSH2('Cs', $packet);
        if ($type != Agent::SSH_AGENT_SIGN_RESPONSE) {
            throw new UnexpectedValueException('Unable to retrieve signature');
        }

        if ($this->key instanceof RSA) {
            [$type, $signature_blob] = Strings::unpackSSH2('ss', $signature_blob);
            unset($type);
        }

        if ($source instanceof Signable) {
            $source->setSignature($signature_blob);
        }

        return $signature_blob;
    }

    /**
     * Returns the private key
     */
    public function toString(string $type, array $options = []): string
    {
        throw new BadMethodCallException('ssh-agent does not provide a mechanism to get the private key');
    }

    /**
     * Sets the password
     */
    public function withPassword(#[\SensitiveParameter] ?string $password = null): PrivateKey
    {
        throw new BadMethodCallException('ssh-agent does not provide a mechanism to get the private key');
    }

    /**
     * Sets the comment
     */
    public function withComment($comment = null): PrivateKey
    {
        $new = clone $this;
        $new->comment = $comment;
        return $new;
    }

    /**
     * Returns the comment
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function getComment(): ?string
    {
        return $this->comment;
    }
}
<?php

/**
 * ReadBytes trait
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2022-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\System\SSH\Common\Traits;

use phpseclib4\Exception\{ConnectionClosedException, UnexpectedValueException};

/**
 * ReadBytes trait
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
trait ReadBytes
{
    /**
     * Read data
     */
    public function readBytes(int $length): string
    {
        $temp = fread($this->fsock, $length);
        if ($temp === false) {
            throw new ConnectionClosedException('\fread() failed.');
        }
        if (strlen($temp) !== $length) {
            throw new UnexpectedValueException("Expected $length bytes; got " . strlen($temp));
        }
        return $temp;
    }
}
<?php

/**
 * Pure-PHP arbitrary precision integer arithmetic library.
 *
 * Supports base-2, base-10, base-16, and base-256 numbers.  Uses the GMP or BCMath extensions, if available,
 * and an internal implementation, otherwise.
 *
 * PHP version 8.1+
 *
 * Here's an example of how to use this library:
 * <code>
 * <?php
 *    $a = new \phpseclib4\Math\BigInteger(2);
 *    $b = new \phpseclib4\Math\BigInteger(3);
 *
 *    $c = $a->add($b);
 *
 *    echo $c->toString(); // outputs 5
 * ?>
 * </code>
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2007-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 */

declare(strict_types=1);

namespace phpseclib4\Math;

use phpseclib4\Exception\BadConfigurationException;
use phpseclib4\Math\BigInteger\Engines\Engine;

/**
 * Pure-PHP arbitrary precision integer arithmetic library. Supports base-2, base-10, base-16, and base-256
 * numbers.
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
class BigInteger implements \JsonSerializable
{
    /**
     * Main Engine
     *
     * @var class-string<Engine>
     */
    private static string $mainEngine;

    /**
     * Selected Engines
     *
     * @var list<string>
     */
    private static array $engines;

    /**
     * The actual BigInteger object
     */
    private Engine $value;

    /**
     * Sets engine type.
     *
     * Throws an exception if the type is invalid
     *
     * @param list<string> $modexps optional
     */
    public static function setEngine(string $main, array $modexps = ['DefaultEngine']): void
    {
        self::$engines = [];

        $fqmain = 'phpseclib4\\Math\\BigInteger\\Engines\\' . $main;
        if (!class_exists($fqmain) || !method_exists($fqmain, 'isValidEngine')) {
            throw new BadConfigurationException("$main is not a valid engine");
        }
        if (!$fqmain::isValidEngine()) {
            throw new BadConfigurationException("$main is not setup correctly on this system");
        }
        /** @var class-string<Engine> $fqmain */
        self::$mainEngine = $fqmain;

        $found = false;
        foreach ($modexps as $modexp) {
            try {
                $fqmain::setModExpEngine($modexp);
                $found = true;
                break;
            } catch (\Exception) {
            }
        }

        if (!$found) {
            throw new BadConfigurationException("No valid modular exponentiation engine found for $main");
        }

        self::$engines = [$main, $modexp];
    }

    /**
     * Returns the engine type
     *
     * @return string[]
     */
    public static function getEngine(): array
    {
        self::initialize_static_variables();

        return self::$engines;
    }

    /**
     * Initialize static variables
     */
    private static function initialize_static_variables(): void
    {
        if (!isset(self::$mainEngine)) {
            $engines = [
                ['GMP', ['DefaultEngine']],
                ['PHP64', ['OpenSSL']],
                ['BCMath64', ['OpenSSL']],
                ['BCMath', ['OpenSSL']],
                ['PHP32', ['OpenSSL']],
                ['PHP64', ['DefaultEngine']],
                ['PHP32', ['DefaultEngine']],
            ];
            // per https://phpseclib.com/docs/speed PHP 8.4.0+ _significantly_ sped up BCMath
            if (PHP_VERSION_ID >= 80400) {
                $engines[1][0] = 'BCMath64';
                $engines[2][0] = 'BCMath';
                $engines[3][0] = 'PHP64';
            }

            foreach ($engines as $engine) {
                try {
                    self::setEngine($engine[0], $engine[1]);
                    return;
                } catch (\Exception $e) {
                }
            }

            throw new BadConfigurationException('No valid BigInteger mode found. This is only possible when JIT is enabled on Windows and neither the GMP or BCMath extensions are available, so either disable JIT or install GMP / BCMath');
        }
    }

    /**
     * Converts base-2, base-10, base-16, and binary strings (base-256) to BigIntegers.
     *
     * If the second parameter - $base - is negative, then it will be assumed that the number's are encoded using
     * two's compliment.  The sole exception to this is -10, which is treated the same as 10 is.
     */
    public function __construct(string|int|Engine $x = 0, int $base = 10)
    {
        self::initialize_static_variables();

        if ($x instanceof self::$mainEngine) {
            $this->value = clone $x;
        } elseif ($x instanceof Engine) {
            $this->value = new self::$mainEngine("$x");
            $this->value->setPrecision($x->getPrecision());
        } else {
            $this->value = new self::$mainEngine($x, $base);
        }
    }

    /**
     * Converts a BigInteger to a base-10 number.
     */
    public function toString(): string
    {
        return $this->value->toString();
    }

    /**
     *  __toString() magic method
     */
    public function __toString(): string
    {
        return (string) $this->value;
    }

    /**
     *  __debugInfo() magic method
     *
     * Will be called, automatically, when print_r() or var_dump() are called
     */
    public function __debugInfo(): array
    {
        return $this->value->__debugInfo();
    }

    /**
     * Converts a BigInteger to a byte string (eg. base-256).
     */
    public function toBytes(bool $twos_compliment = false): string
    {
        return $this->value->toBytes($twos_compliment);
    }

    /**
     * Converts a BigInteger to a hex string (eg. base-16).
     */
    public function toHex(bool $twos_compliment = false): string
    {
        return $this->value->toHex($twos_compliment);
    }

    /**
     * Converts a BigInteger to a bit string (eg. base-2).
     *
     * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're
     * saved as two's compliment.
     */
    public function toBits(bool $twos_compliment = false): string
    {
        return $this->value->toBits($twos_compliment);
    }

    /**
     * Adds two BigIntegers.
     */
    public function add(BigInteger $y): BigInteger
    {
        return new static($this->value->add($y->value));
    }

    /**
     * Subtracts two BigIntegers.
     */
    public function subtract(BigInteger $y): BigInteger
    {
        return new static($this->value->subtract($y->value));
    }

    /**
     * Multiplies two BigIntegers
     */
    public function multiply(BigInteger $x): BigInteger
    {
        return new static($this->value->multiply($x->value));
    }

    /**
     * Divides two BigIntegers.
     *
     * Returns an array whose first element contains the quotient and whose second element contains the
     * "common residue".  If the remainder would be positive, the "common residue" and the remainder are the
     * same.  If the remainder would be negative, the "common residue" is equal to the sum of the remainder
     * and the divisor (basically, the "common residue" is the first positive modulo).
     *
     * Here's an example:
     * <code>
     * <?php
     *    $a = new \phpseclib4\Math\BigInteger('10');
     *    $b = new \phpseclib4\Math\BigInteger('20');
     *
     *    [$quotient, $remainder] = $a->divide($b);
     *
     *    echo $quotient->toString(); // outputs 0
     *    echo "\r\n";
     *    echo $remainder->toString(); // outputs 10
     * ?>
     * </code>
     *
     * @return BigInteger[]
     */
    public function divide(BigInteger $y): array
    {
        [$q, $r] = $this->value->divide($y->value);
        return [
            new static($q),
            new static($r),
        ];
    }

    /**
     * Calculates modular inverses.
     *
     * Say you have (30 mod 17 * x mod 17) mod 17 == 1.  x can be found using modular inverses.
     */
    public function modInverse(BigInteger $n): ?BigInteger
    {
        $value = $this->value->modInverse($n->value);
        return $value ? new static($value) : null;
    }

    /**
     * Calculates modular inverses.
     *
     * Say you have (30 mod 17 * x mod 17) mod 17 == 1.  x can be found using modular inverses.
     *
     * @return array{gcd: static, x: static, y: static}
     */
    public function extendedGCD(BigInteger $n): array
    {
        ['gcd' => $gcd, 'x' => $x, 'y' => $y] = $this->value->extendedGCD($n->value);
        return [
            'gcd' => new static($gcd),
            'x' => new static($x),
            'y' => new static($y),
        ];
    }

    /**
     * Calculates the greatest common divisor
     *
     * Say you have 693 and 609.  The GCD is 21.
     */
    public function gcd(BigInteger $n): BigInteger
    {
        return new static($this->value->gcd($n->value));
    }

    /**
     * Absolute value.
     */
    public function abs(): BigInteger
    {
        return new static($this->value->abs());
    }

    /**
     * Set Precision
     *
     * Some bitwise operations give different results depending on the precision being used.  Examples include left
     * shift, not, and rotates.
     */
    public function setPrecision(int $bits): void
    {
        $this->value->setPrecision($bits);
    }

    /**
     * Get Precision
     *
     * Returns the precision if it exists, -1 if it doesn't
     */
    public function getPrecision(): int
    {
        return $this->value->getPrecision();
    }

    /**
     *  __serialize() magic method
     *
     * @see self::__unserialize()
     */
    public function __serialize(): array
    {
        $result = ['hex' => $this->toHex(true)];
        if ($this->getPrecision() > 0) {
            $result['precision'] = $this->getPrecision();
        }
        return $result;
    }

    /**
     *  __unserialize() magic method
     *
     * @see self::__serialize()
     */
    public function __unserialize(array $data): void
    {
        $temp = new static($data['hex'], -16);
        $this->value = $temp->value;
        if (isset($data['precision']) && $data['precision'] > 0) {
            // recalculate $this->bitmask
            $this->setPrecision($data['precision']);
        }
    }

    /**
     * JSON Serialize
     *
     * Will be called, automatically, when json_encode() is called on a BigInteger object.
     *
     * @return array{hex: string, precision?: int]
     */
    public function jsonSerialize(): mixed
    {
        $result = ['hex' => $this->toHex(true)];
        if ($this->getPrecision() > 0) {
            $result['precision'] = $this->getPrecision();
        }
        return $result;
    }

    /**
     * Performs modular exponentiation.
     */
    public function powMod(BigInteger $e, BigInteger $n): BigInteger
    {
        return new static($this->value->powMod($e->value, $n->value));
    }

    /**
     * Performs modular exponentiation.
     */
    public function modPow(BigInteger $e, BigInteger $n): BigInteger
    {
        return new static($this->value->modPow($e->value, $n->value));
    }

    /**
     * Compares two numbers.
     *
     * Although one might think !$x->compare($y) means $x != $y, it, in fact, means the opposite.  The reason for this
     * is demonstrated thusly:
     *
     * $x  > $y: $x->compare($y)  > 0
     * $x  < $y: $x->compare($y)  < 0
     * $x == $y: $x->compare($y) == 0
     *
     * Note how the same comparison operator is used.  If you want to test for equality, use $x->equals($y).
     *
     * {@internal Could return $this->subtract($x), but that's not as fast as what we do do.}
     *
     * @return int in case < 0 if $this is less than $y; > 0 if $this is greater than $y, and 0 if they are equal.
     * @see self::equals()
     */
    public function compare(BigInteger $y): int
    {
        return $this->value->compare($y->value);
    }

    /**
     * Tests the equality of two numbers.
     *
     * If you need to see if one number is greater than or less than another number, use BigInteger::compare()
     */
    public function equals(BigInteger $x): bool
    {
        return $this->value->equals($x->value);
    }

    /**
     * Logical Not
     */
    public function bitwise_not(): BigInteger
    {
        return new static($this->value->bitwise_not());
    }

    /**
     * Logical And
     */
    public function bitwise_and(BigInteger $x): BigInteger
    {
        return new static($this->value->bitwise_and($x->value));
    }

    /**
     * Logical Or
     */
    public function bitwise_or(BigInteger $x): BigInteger
    {
        return new static($this->value->bitwise_or($x->value));
    }

    /**
     * Logical Exclusive Or
     */
    public function bitwise_xor(BigInteger $x): BigInteger
    {
        return new static($this->value->bitwise_xor($x->value));
    }

    /**
     * Logical Right Shift
     *
     * Shifts BigInteger's by $shift bits, effectively dividing by 2**$shift.
     */
    public function bitwise_rightShift(int $shift): BigInteger
    {
        return new static($this->value->bitwise_rightShift($shift));
    }

    /**
     * Logical Left Shift
     *
     * Shifts BigInteger's by $shift bits, effectively multiplying by 2**$shift.
     */
    public function bitwise_leftShift(int $shift): BigInteger
    {
        return new static($this->value->bitwise_leftShift($shift));
    }

    /**
     * Logical Left Rotate
     *
     * Instead of the top x bits being dropped they're appended to the shifted bit string.
     */
    public function bitwise_leftRotate(int $shift): BigInteger
    {
        return new static($this->value->bitwise_leftRotate($shift));
    }

    /**
     * Logical Right Rotate
     *
     * Instead of the bottom x bits being dropped they're prepended to the shifted bit string.
     */
    public function bitwise_rightRotate(int $shift): BigInteger
    {
        return new static($this->value->bitwise_rightRotate($shift));
    }

    /**
     * Returns the smallest and largest n-bit number
     *
     * @return BigInteger[]
     */
    public static function minMaxBits(int $bits): array
    {
        self::initialize_static_variables();

        $class = self::$mainEngine;
        ['min' => $min, 'max' => $max] = $class::minMaxBits($bits);
        return [
            'min' => new static($min),
            'max' => new static($max),
        ];
    }

    /**
     * Return the size of a BigInteger in bits
     */
    public function getLength(): int
    {
        return $this->value->getLength();
    }

    /**
     * Return the size of a BigInteger in bytes
     */
    public function getLengthInBytes(): int
    {
        return $this->value->getLengthInBytes();
    }

    /**
     * Generates a random number of a certain size
     *
     * Bit length is equal to $size
     */
    public static function random(int $size): BigInteger
    {
        self::initialize_static_variables();

        $class = self::$mainEngine;
        return new static($class::random($size));
    }

    /**
     * Generates a random prime number of a certain size
     *
     * Bit length is equal to $size
     */
    public static function randomPrime(int $size): BigInteger
    {
        self::initialize_static_variables();

        $class = self::$mainEngine;
        return new static($class::randomPrime($size));
    }

    /**
     * Generate a random prime number between a range
     *
     * If there's not a prime within the given range, false will be returned.
     */
    public static function randomRangePrime(BigInteger $min, BigInteger $max): ?BigInteger
    {
        $class = self::$mainEngine;
        $result = $class::randomRangePrime($min->value, $max->value);
        if ($result === null) {
            return null;
        }
        return new static($result);
    }

    /**
     * Generate a random number between a range
     *
     * Returns a random number between $min and $max where $min and $max
     * can be defined using one of the two methods:
     *
     * BigInteger::randomRange($min, $max)
     * BigInteger::randomRange($max, $min)
     */
    public static function randomRange(BigInteger $min, BigInteger $max): BigInteger
    {
        $class = self::$mainEngine;
        return new static($class::randomRange($min->value, $max->value));
    }

    /**
     * Checks a numer to see if it's prime
     *
     * Assuming the $t parameter is not set, this function has an error rate of 2**-80.  The main motivation for the
     * $t parameter is distributability.  BigInteger::randomPrime() can be distributed across multiple pageloads
     * on a website instead of just one.
     */
    public function isPrime(?int $t = null): bool
    {
        return $this->value->isPrime($t);
    }

    /**
     * Calculates the nth root of a biginteger.
     *
     * Returns the nth root of a positive biginteger, where n defaults to 2
     */
    public function root(int $n = 2): BigInteger
    {
        return new static($this->value->root($n));
    }

    /**
     * Performs exponentiation.
     */
    public function pow(BigInteger $n): BigInteger
    {
        return new static($this->value->pow($n->value));
    }

    /**
     * Return the minimum BigInteger between an arbitrary number of BigIntegers.
     */
    public static function min(BigInteger ...$nums): BigInteger
    {
        $class = self::$mainEngine;
        $nums = array_map(fn ($num) => $num->value, $nums);
        return new static($class::min(...$nums));
    }

    /**
     * Return the maximum BigInteger between an arbitrary number of BigIntegers.
     */
    public static function max(BigInteger ...$nums): BigInteger
    {
        $class = self::$mainEngine;
        $nums = array_map(fn ($num) => $num->value, $nums);
        return new static($class::max(...$nums));
    }

    /**
     * Tests BigInteger to see if it is between two integers, inclusive
     */
    public function between(BigInteger $min, BigInteger $max): bool
    {
        return $this->value->between($min->value, $max->value);
    }

    /**
     * Clone
     */
    public function __clone()
    {
        $this->value = clone $this->value;
    }

    /**
     * Is Odd?
     */
    public function isOdd(): bool
    {
        return $this->value->isOdd();
    }

    /**
     * Tests if a bit is set
     */
    public function testBit(int $x): bool
    {
        return $this->value->testBit($x);
    }

    /**
     * Is Negative?
     */
    public function isNegative(): bool
    {
        return $this->value->isNegative();
    }

    /**
     * Negate
     *
     * Given $k, returns -$k
     */
    public function negate(): BigInteger
    {
        return new static($this->value->negate());
    }

    /**
     * Scan for 1 and right shift by that amount
     *
     * ie. $s = gmp_scan1($n, 0) and $r = gmp_div_q($n, gmp_pow(gmp_init('2'), $s));
     */
    public static function scan1divide(BigInteger $r): int
    {
        $class = self::$mainEngine;
        return $class::scan1divide($r->value);
    }

    /**
     * Create Recurring Modulo Function
     *
     * Sometimes it may be desirable to do repeated modulos with the same number outside of
     * modular exponentiation
     */
    public function createRecurringModuloFunction(): \Closure
    {
        $func = $this->value->createRecurringModuloFunction();
        return fn (BigInteger $x) => new static($func($x->value));
    }

    /**
     * Bitwise Split
     *
     * Splits BigInteger's into chunks of $split bits
     *
     * @return BigInteger[]
     */
    public function bitwise_split(int $split): array
    {
        return array_map(fn ($val) => new static($val), $this->value->bitwise_split($split));
    }
}
<?php

/**
 * Binary Finite Fields
 *
 * Utilizes the factory design pattern
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 */

declare(strict_types=1);

namespace phpseclib4\Math;

use phpseclib4\Common\Functions\Strings;
use phpseclib4\Exception\ResourceLimitException;
use phpseclib4\Math\BinaryField\Integer;
use phpseclib4\Math\Common\FiniteField;

/**
 * Binary Finite Fields
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
class BinaryField extends FiniteField
{
    /**
     * Instance Counter
     */
    private static int $instanceCounter = 0;

    /**
     * Keeps track of current instance
     */
    protected int $instanceID;

    private BigInteger $randomMax;

    /**
     * Default constructor
     */
    public function __construct(int ...$indices)
    {
        $m = array_shift($indices);
        if ($m > 571) {
            /* sect571r1 and sect571k1 are the largest binary curves that https://www.secg.org/sec2-v2.pdf defines
               altho theoretically there may be legit reasons to use binary finite fields with larger degrees
               imposing a limit on the maximum size is both reasonable and precedented. in particular,
               http://tools.ietf.org/html/rfc4253#section-6.1 (The Secure Shell (SSH) Transport Layer Protocol) says
               "implementations SHOULD check that the packet length is reasonable in order for the implementation to
                avoid denial of service and/or buffer overflow attacks" */
            throw new ResourceLimitException('Degrees larger than 571 are not supported');
        }
        $val = str_repeat('0', $m) . '1';
        foreach ($indices as $index) {
            $val[$index] = '1';
        }
        $modulo = static::base2ToBase256(strrev($val));

        $mStart = 2 * $m - 2;
        $t = (int) ceil($m / 8);
        $finalMask = chr((1 << ($m % 8)) - 1);
        if ($finalMask == "\0") {
            $finalMask = "\xFF";
        }
        $bitLen = $mStart + 1;
        $pad = (int) ceil($bitLen / 8);
        $h = $bitLen & 7;
        $h = $h ? 8 - $h : 0;

        $r = rtrim(substr($val, 0, -1), '0');
        $u = [static::base2ToBase256(strrev($r))];
        for ($i = 1; $i < 8; $i++) {
            $u[] = static::base2ToBase256(strrev(str_repeat('0', $i) . $r));
        }

        // implements algorithm 2.40 (in section 2.3.5) in "Guide to Elliptic Curve Cryptography"
        // with W = 8
        $reduce = function (string $c) use ($u, $mStart, $m, $t, $finalMask, $pad, $h): string {
            $c = str_pad($c, $pad, "\0", STR_PAD_LEFT);
            for ($i = $mStart; $i >= $m;) {
                $g = $h >> 3;
                $mask = $h & 7;
                $mask = $mask ? 1 << (7 - $mask) : 0x80;
                for (; $mask > 0; $mask >>= 1, $i--, $h++) {
                    if (ord($c[$g]) & $mask) {
                        $temp = $i - $m;
                        $j = $temp >> 3;
                        $k = $temp & 7;
                        $t1 = $j ? substr($c, 0, -$j) : $c;
                        $length = strlen($t1);
                        if ($length) {
                            $t2 = str_pad($u[$k], $length, "\0", STR_PAD_LEFT);
                            $temp = $t1 ^ $t2;
                            $c = $j ? substr_replace($c, $temp, 0, $length) : $temp;
                        }
                    }
                }
            }
            $c = substr($c, -$t);
            if (strlen($c) == $t) {
                $c[0] = $c[0] & $finalMask;
            }
            return ltrim($c, "\0");
        };

        $this->instanceID = self::$instanceCounter++;
        Integer::setModulo($this->instanceID, $modulo);
        Integer::setRecurringModuloFunction($this->instanceID, $reduce);

        $this->randomMax = new BigInteger($modulo, 2);
    }

    /**
     * Returns an instance of a dynamically generated PrimeFieldInteger class
     */
    public function newInteger(BigInteger|string $num): Integer
    {
        return new Integer($this->instanceID, $num instanceof BigInteger ? $num->toBytes() : $num);
    }

    /**
     * Returns an integer on the finite field between one and the prime modulo
     */
    public function randomInteger(): Integer
    {
        static $one;
        if (!isset($one)) {
            $one = new BigInteger(1);
        }

        return new Integer($this->instanceID, BigInteger::randomRange($one, $this->randomMax)->toBytes());
    }

    /**
     * Returns the length of the modulo in bytes
     */
    public function getLengthInBytes(): int
    {
        return strlen(Integer::getModulo($this->instanceID));
    }

    /**
     * Returns the length of the modulo in bits
     */
    public function getLength(): int
    {
        return strlen(Integer::getModulo($this->instanceID)) << 3;
    }

    /**
     * Converts a base-2 string to a base-256 string
     */
    public static function base2ToBase256(string $x, ?int $size = null): string
    {
        $str = Strings::bits2bin($x);

        $pad = strlen($x) >> 3;
        if (strlen($x) & 3) {
            $pad++;
        }
        $str = str_pad($str, $pad, "\0", STR_PAD_LEFT);
        if (isset($size)) {
            $str = str_pad($str, $size, "\0", STR_PAD_LEFT);
        }

        return $str;
    }

    /**
     * Converts a base-256 string to a base-2 string
     */
    public static function base256ToBase2(string $x): string
    {
        if (function_exists('gmp_import')) {
            return gmp_strval(gmp_import($x), 2);
        }

        return Strings::bin2bits($x);
    }
}
<?php

/**
 * BCMath64 BigInteger Engine
 *
 * Unlike PHP32 and PHP64, BCMath and BCMath64 are both internally represented the same way.
 * What this means is that you can add an instance of BCMath to BCMath64 and vice versa.
 *
 * The main reason a BCMath64 class exists is because 64-bit integers can convert to and from
 * base-256 faster than 32-bit integers.
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2017-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Math\BigInteger\Engines;

use phpseclib4\Common\Functions\Strings;
use phpseclib4\Exception\BadConfigurationException;

/**
 * BCMath64 Engine.
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
class BCMath64 extends BCMath
{
    /**
     * Test for engine validity
     *
     * @see parent::__construct()
     */
    public static function isValidEngine(): bool
    {
        return PHP_INT_SIZE === 8 && extension_loaded('bcmath');
    }

    /**
     * Initialize a BCMath BigInteger Engine instance
     *
     * @see parent::__construct()
     */
    protected function initialize(int $base): void
    {
        if (abs($base) != 256) {
            parent::initialize($base);
            return;
        }

        // round $len to the nearest 8
        $len = (strlen($this->value) + 7) & ~7;

        $x = str_pad($this->value, $len, chr(0), STR_PAD_LEFT);

        $this->value = '0';
        for ($i = 0; $i < $len; $i += 8) {
            $this->value = bcmul($this->value, '18446744073709551616', 0); // 18446744073709551616 == 2**64
            $temp = sprintf('%u', unpack('J', substr($x, $i, 8))[1]);
            $this->value = bcadd($this->value, $temp, 0);
        }
        if ($this->is_negative) {
            $this->value = '-' . $this->value;
        }
    }

    /**
     * Converts a BigInteger to a byte string (eg. base-256).
     */
    public function toBytes(bool $twos_compliment = false): string
    {
        if ($twos_compliment) {
            return $this->toBytesHelper();
        }

        $value = '';
        $current = $this->value;

        if ($current[0] == '-') {
            $current = substr($current, 1);
        }

        while (bccomp($current, '0', 0) > 0) {
            // the following requires PHP 8.4+
            /*
            [$current, $remainder] = bcdivmod($current, '72057594037927936', 0);
            $temp = pack('J', $remainder);
            $value = substr($temp, 1) . $value;
            */
            $quotient = bcdiv($current, '72057594037927936', 0); // 72057594037927936 == 2**56
            $remainder = bcsub($current, bcmul($quotient, '72057594037927936', 0), 0);
            $temp = pack('J', $remainder);
            $value = substr($temp, 1) . $value;
            $current = $quotient;
        }

        return $this->precision > 0 ?
            substr(str_pad($value, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) :
            ltrim($value, chr(0));
    }
}
<?php

/**
 * PHP Barrett Modular Exponentiation Engine
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2017-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Math\BigInteger\Engines\PHP\Reductions;

use phpseclib4\Math\BigInteger\Engines\PHP;
use phpseclib4\Math\BigInteger\Engines\PHP\Base;

/**
 * PHP Barrett Modular Exponentiation Engine
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class Barrett extends Base
{
    /**
     * Barrett Modular Reduction
     *
     * See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=14 HAC 14.3.3} /
     * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=165 MPM 6.2.5} for more information.  Modified slightly,
     * so as not to require negative numbers (initially, this script didn't support negative numbers).
     *
     * Employs "folding", as described at
     * {@link http://www.cosic.esat.kuleuven.be/publications/thesis-149.pdf#page=66 thesis-149.pdf#page=66}.  To quote from
     * it, "the idea [behind folding] is to find a value x' such that x (mod m) = x' (mod m), with x' being smaller than x."
     *
     * Unfortunately, the "Barrett Reduction with Folding" algorithm described in thesis-149.pdf is not, as written, all that
     * usable on account of (1) its not using reasonable radix points as discussed in
     * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=162 MPM 6.2.2} and (2) the fact that, even with reasonable
     * radix points, it only works when there are an even number of digits in the denominator.  The reason for (2) is that
     * (x >> 1) + (x >> 1) != x / 2 + x / 2.  If x is even, they're the same, but if x is odd, they're not.  See the in-line
     * comments for details.
     *
     * @param class-string<PHP> $class
     */
    protected static function reduce(array $n, array $m, string $class): array
    {
        static $cache = [
            self::VARIABLE => [],
            self::DATA => [],
        ];

        $m_length = count($m);

        // if (self::compareHelper($n, $static::square($m)) >= 0) {
        if (count($n) > 2 * $m_length) {
            $lhs = new $class();
            $rhs = new $class();
            $lhs->value = $n;
            $rhs->value = $m;
            [, $temp] = $lhs->divide($rhs);
            return $temp->value;
        }

        // if (m.length >> 1) + 2 <= m.length then m is too small and n can't be reduced
        if ($m_length < 5) {
            return self::regularBarrett($n, $m, $class);
        }
        // n = 2 * m.length
        $correctionNeeded = false;
        if ($m_length & 1) {
            $correctionNeeded = true;
            array_unshift($n, 0);
            array_unshift($m, 0);
            $m_length++;
        }

        if (($key = array_search($m, $cache[self::VARIABLE])) === false) {
            $cache[self::VARIABLE][] = $m;

            $lhs = new $class();
            $lhs_value = &$lhs->value;
            $lhs_value = self::array_repeat(0, $m_length + ($m_length >> 1));
            $lhs_value[] = 1;
            $rhs = new $class();
            $rhs->value = $m;

            [$u, $m1] = $lhs->divide($rhs);
            $u = $u->value;
            $m1 = $m1->value;

            $cache[self::DATA][] = [
                'u' => $u, // m.length >> 1 (technically (m.length >> 1) + 1)
                'm1' => $m1, // m.length
            ];
        } else {
            [
                'u' => $u,
                'm1' => $m1
            ] = $cache[self::DATA][$key];
        }

        $cutoff = $m_length + ($m_length >> 1);
        $lsd = array_slice($n, 0, $cutoff); // m.length + (m.length >> 1)
        $msd = array_slice($n, $cutoff);    // m.length >> 1

        $lsd = self::trim($lsd);
        $temp = $class::multiplyHelper($msd, false, $m1, false); // m.length + (m.length >> 1)
        $n = $class::addHelper($lsd, false, $temp[self::VALUE], false); // m.length + (m.length >> 1) + 1 (so basically we're adding two same length numbers)
        //if ($m_length & 1) {
        //    return self::regularBarrett($n[self::VALUE], $m, $class);
        //}

        // (m.length + (m.length >> 1) + 1) - (m.length - 1) == (m.length >> 1) + 2
        $temp = array_slice($n[self::VALUE], $m_length - 1);
        // if even: ((m.length >> 1) + 2) + (m.length >> 1) == m.length + 2
        // if odd:  ((m.length >> 1) + 2) + (m.length >> 1) == (m.length - 1) + 2 == m.length + 1
        // note that these are upper bounds. let's say m.length is 2. then you'd be multiplying a
        // 3 digit number by a 1 digit number. if you're doing 999 * 9 (in base 10) the result will
        // be a 4 digit number. but if you're multiplying 111 * 1 then the result will be a 3 digit
        // number.
        $temp = $class::multiplyHelper($temp, false, $u, false);
        // if even: (m.length + 2) - ((m.length >> 1) + 1) = m.length - (m.length >> 1) + 1
        // if odd:  (m.length + 1) - ((m.length >> 1) + 1) = m.length - (m.length >> 1)
        $temp = array_slice($temp[self::VALUE], ($m_length >> 1) + 1);
        // if even: (m.length - (m.length >> 1) + 1) + m.length = 2 * m.length - (m.length >> 1) + 1
        // if odd:  (m.length - (m.length >> 1)) + m.length     = 2 * m.length - (m.length >> 1)
        $temp = $class::multiplyHelper($temp, false, $m, false);
        // at this point, if m had an odd number of digits, we'd (probably) be subtracting a 2 * m.length - (m.length >> 1)
        // digit number from a m.length + (m.length >> 1) + 1 digit number.  ie. there'd be an extra digit and the while loop
        // following this comment would loop a lot (hence our calling _regularBarrett() in that situation).
        $result = $class::subtractHelper($n[self::VALUE], false, $temp[self::VALUE], false);

        while (self::compareHelper($result[self::VALUE], $result[self::SIGN], $m, false) >= 0) {
            $result = $class::subtractHelper($result[self::VALUE], $result[self::SIGN], $m, false);
        }

        if ($correctionNeeded) {
            array_shift($result[self::VALUE]);
        }

        return $result[self::VALUE];
    }

    /**
     * (Regular) Barrett Modular Reduction
     *
     * For numbers with more than four digits BigInteger::_barrett() is faster.  The difference between that and this
     * is that this function does not fold the denominator into a smaller form.
     */
    private static function regularBarrett(array $x, array $n, string $class): array
    {
        static $cache = [
            self::VARIABLE => [],
            self::DATA => [],
        ];

        $n_length = count($n);

        if (count($x) > 2 * $n_length) {
            $lhs = new $class();
            $rhs = new $class();
            $lhs->value = $x;
            $rhs->value = $n;
            [, $temp] = $lhs->divide($rhs);
            return $temp->value;
        }

        if (($key = array_search($n, $cache[self::VARIABLE])) === false) {
            $key = count($cache[self::VARIABLE]);
            $cache[self::VARIABLE][] = $n;
            $lhs = new $class();
            $lhs_value = &$lhs->value;
            $lhs_value = self::array_repeat(0, 2 * $n_length);
            $lhs_value[] = 1;
            $rhs = new $class();
            $rhs->value = $n;
            [$temp, ] = $lhs->divide($rhs); // m.length
            $cache[self::DATA][] = $temp->value;
        }

        // 2 * m.length - (m.length - 1) = m.length + 1
        $temp = array_slice($x, $n_length - 1);
        // (m.length + 1) + m.length = 2 * m.length + 1
        $temp = $class::multiplyHelper($temp, false, $cache[self::DATA][$key], false);
        // (2 * m.length + 1) - (m.length - 1) = m.length + 2
        $temp = array_slice($temp[self::VALUE], $n_length + 1);

        // m.length + 1
        $result = array_slice($x, 0, $n_length + 1);
        // m.length + 1
        $temp = self::multiplyLower($temp, false, $n, false, $n_length + 1, $class);
        // $temp == array_slice($class::regularMultiply($temp, false, $n, false)->value, 0, $n_length + 1)

        if (self::compareHelper($result, false, $temp[self::VALUE], $temp[self::SIGN]) < 0) {
            $corrector_value = self::array_repeat(0, $n_length + 1);
            $corrector_value[count($corrector_value)] = 1;
            $result = $class::addHelper($result, false, $corrector_value, false);
            $result = $result[self::VALUE];
        }

        // at this point, we're subtracting a number with m.length + 1 digits from another number with m.length + 1 digits
        $result = $class::subtractHelper($result, false, $temp[self::VALUE], $temp[self::SIGN]);
        while (self::compareHelper($result[self::VALUE], $result[self::SIGN], $n, false) > 0) {
            $result = $class::subtractHelper($result[self::VALUE], $result[self::SIGN], $n, false);
        }

        return $result[self::VALUE];
    }

    /**
     * Performs long multiplication up to $stop digits
     *
     * If you're going to be doing array_slice($product->value, 0, $stop), some cycles can be saved.
     *
     * @see self::regularBarrett()
     */
    private static function multiplyLower(array $x_value, bool $x_negative, array $y_value, bool $y_negative, int $stop, string $class): array
    {
        $x_length = count($x_value);
        $y_length = count($y_value);

        if (!$x_length || !$y_length) { // a 0 is being multiplied
            return [
                self::VALUE => [],
                self::SIGN => false,
            ];
        }

        if ($x_length < $y_length) {
            $temp = $x_value;
            $x_value = $y_value;
            $y_value = $temp;

            $x_length = count($x_value);
            $y_length = count($y_value);
        }

        $product_value = self::array_repeat(0, $x_length + $y_length);

        // the following for loop could be removed if the for loop following it
        // (the one with nested for loops) initially set $i to 0, but
        // doing so would also make the result in one set of unnecessary adds,
        // since on the outermost loops first pass, $product->value[$k] is going
        // to always be 0

        $carry = 0;

        for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0, $k = $i
            $temp = $x_value[$j] * $y_value[0] + $carry; // $product_value[$k] == 0
            $carry = $class::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
            $product_value[$j] = (int) ($temp - $class::BASE_FULL * $carry);
        }

        if ($j < $stop) {
            $product_value[$j] = $carry;
        }

        // the above for loop is what the previous comment was talking about.  the
        // following for loop is the "one with nested for loops"

        for ($i = 1; $i < $y_length; ++$i) {
            $carry = 0;

            for ($j = 0, $k = $i; $j < $x_length && $k < $stop; ++$j, ++$k) {
                $temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry;
                $carry = $class::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
                $product_value[$k] = (int) ($temp - $class::BASE_FULL * $carry);
            }

            if ($k < $stop) {
                $product_value[$k] = $carry;
            }
        }

        return [
            self::VALUE => self::trim($product_value),
            self::SIGN => $x_negative != $y_negative,
        ];
    }
}
<?php

/**
 * PHP Montgomery Modular Exponentiation Engine with interleaved multiplication
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2017-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Math\BigInteger\Engines\PHP\Reductions;

use phpseclib4\Math\BigInteger\Engines\PHP;

/**
 * PHP Montgomery Modular Exponentiation Engine with interleaved multiplication
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class MontgomeryMult extends Montgomery
{
    /**
     * Montgomery Multiply
     *
     * Interleaves the montgomery reduction and long multiplication algorithms together as described in
     * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=13 HAC 14.36}
     *
     * @param class-string<PHP> $class
     * @see self::_prepMontgomery()
     * @see self::_montgomery()
     */
    public static function multiplyReduce(array $x, array $y, array $n, string $class): array
    {
        // the following code, although not callable, can be run independently of the above code
        // although the above code performed better in my benchmarks the following could might
        // perform better under different circumstances. in lieu of deleting it it's just been
        // made uncallable

        static $cache = [
            self::VARIABLE => [],
            self::DATA => [],
        ];

        if (($key = array_search($n, $cache[self::VARIABLE])) === false) {
            $key = count($cache[self::VARIABLE]);
            $cache[self::VARIABLE][] = $n;
            $cache[self::DATA][] = self::modInverse67108864($n, $class);
        }

        $size = max(count($x), count($y), count($n));
        $x = array_pad($x, $size, 0);
        $y = array_pad($y, $size, 0);
        $n = array_pad($n, $size, 0);
        $a = [self::VALUE => self::array_repeat(0, $size + 1)];
        for ($i = 0; $i < $size; ++$i) {
            $temp = $a[self::VALUE][0] + $x[$i] * $y[0];
            $temp = $temp - $class::BASE_FULL * ($class::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31));
            $temp = $temp * $cache[self::DATA][$key];
            $temp = $temp - $class::BASE_FULL * ($class::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31));
            $temp = $class::addHelper($class::regularMultiply([$x[$i]], $y), false, $class::regularMultiply([$temp], $n), false);
            $a = $class::addHelper($a[self::VALUE], false, $temp[self::VALUE], false);
            $a[self::VALUE] = array_slice($a[self::VALUE], 1);
        }
        if (self::compareHelper($a[self::VALUE], false, $n, false) >= 0) {
            $a = $class::subtractHelper($a[self::VALUE], false, $n, false);
        }
        return $a[self::VALUE];
    }
}
<?php

/**
 * PHP Power of Two Modular Exponentiation Engine
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2017-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Math\BigInteger\Engines\PHP\Reductions;

use phpseclib4\Math\BigInteger\Engines\PHP\Base;

/**
 * PHP Power Of Two Modular Exponentiation Engine
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class PowerOfTwo extends Base
{
    /**
     * Prepare a number for use in Montgomery Modular Reductions
     */
    protected static function prepareReduce(array $x, array $n, string $class): array
    {
        return self::reduce($x, $n, $class);
    }

    /**
     * Power Of Two Reduction
     */
    protected static function reduce(array $x, array $n, string $class): array
    {
        $lhs = new $class();
        $lhs->value = $x;
        $rhs = new $class();
        $rhs->value = $n;

        $temp = new $class();
        $temp->value = [1];

        $result = $lhs->bitwise_and($rhs->subtract($temp));
        return $result->value;
    }
}
<?php

/**
 * PHP Dynamic Barrett Modular Exponentiation Engine
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2017-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Math\BigInteger\Engines\PHP\Reductions;

use phpseclib4\Math\BigInteger\Engines\PHP;
use phpseclib4\Math\BigInteger\Engines\PHP\Base;

/**
 * PHP Dynamic Barrett Modular Exponentiation Engine
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class EvalBarrett extends Base
{
    /**
     * Custom Reduction Function
     *
     * @see self::generateCustomReduction
     */
    private static \Closure $custom_reduction;

    /**
     * Barrett Modular Reduction
     *
     * This calls a dynamically generated loop unrolled function that's specific to a given modulo.
     * Array lookups are avoided as are if statements testing for how many bits the host OS supports, etc.
     *
     * @psalm-suppress PossiblyUnusedParam
     */
    protected static function reduce(array $n, array $m, string $class): array
    {
        $inline = self::$custom_reduction;
        return $inline($n);
    }

    /**
     * Generate Custom Reduction
     *
     * @psalm-suppress PossiblyUnusedParam
     */
    protected static function generateCustomReduction(PHP $m, string $class): \Closure
    {
        $m_length = count($m->value);

        if ($m_length < 5) {
            $code = '
                $lhs = new ' . $class . '();
                $lhs->value = $x;
                $rhs = new ' . $class . '();
                $rhs->value = [' .
                implode(',', array_map(self::float2string(...), $m->value)) . '];
                [, $temp] = $lhs->divide($rhs);
                return $temp->value;
            ';
            eval('$func = function ($x) { ' . $code . '};');
            self::$custom_reduction = $func;
            //self::$custom_reduction = \Closure::bind($func, $m, $class);
            return $func;
        }

        $correctionNeeded = false;
        if ($m_length & 1) {
            $correctionNeeded = true;
            $m = clone $m;
            array_unshift($m->value, 0);
            $m_length++;
        }

        $lhs = new $class();
        $lhs_value = &$lhs->value;

        $lhs_value = self::array_repeat(0, $m_length + ($m_length >> 1));
        $lhs_value[] = 1;
        $rhs = new $class();

        [$u, $m1] = $lhs->divide($m);

        if ($class::BASE != 26) {
            $u = $u->value;
        } else {
            $lhs_value = self::array_repeat(0, 2 * $m_length);
            $lhs_value[] = 1;
            $rhs = new $class();

            [$u] = $lhs->divide($m);
            $u = $u->value;
        }

        $m = $m->value;
        $m1 = $m1->value;

        $cutoff = count($m) + (count($m) >> 1);

        $code = $correctionNeeded ?
            'array_unshift($n, 0);' :
            '';

        $code .= '
            if (count($n) > ' . (2 * count($m)) . ') {
                $lhs = new ' . $class . '();
                $rhs = new ' . $class . '();
                $lhs->value = $n;
                $rhs->value = [' .
                implode(',', array_map(self::float2string(...), $m)) . '];
                [, $temp] = $lhs->divide($rhs);
                return $temp->value;
            }

            $lsd = array_slice($n, 0, ' . $cutoff . ');
            $msd = array_slice($n, ' . $cutoff . ');';

        $code .= self::generateInlineTrim('msd');
        $code .= self::generateInlineMultiply('msd', $m1, 'temp', $class);
        $code .= self::generateInlineAdd('lsd', 'temp', 'n', $class);

        $code .= '$temp = array_slice($n, ' . (count($m) - 1) . ');';
        $code .= self::generateInlineMultiply('temp', $u, 'temp2', $class);
        $code .= self::generateInlineTrim('temp2');

        $code .= $class::BASE == 26 ?
            '$temp = array_slice($temp2, ' . (count($m) + 1) . ');' :
            '$temp = array_slice($temp2, ' . ((count($m) >> 1) + 1) . ');';
        $code .= self::generateInlineMultiply('temp', $m, 'temp2', $class);
        $code .= self::generateInlineTrim('temp2');

        /*
        if ($class::BASE == 26) {
            $code.= '$n = array_slice($n, 0, ' . (count($m) + 1) . ');
                     $temp2 = array_slice($temp2, 0, ' . (count($m) + 1) . ');';
        }
        */

        $code .= self::generateInlineSubtract2('n', 'temp2', 'temp', $class);

        $subcode = self::generateInlineSubtract1('temp', $m, 'temp2', $class);
        $subcode .= '$temp = $temp2;';

        $code .= self::generateInlineCompare($m, 'temp', $subcode);

        if ($correctionNeeded) {
            $code .= 'array_shift($temp);';
        }

        $code .= 'return $temp;';

        eval('$func = function ($n) { ' . $code . '};');

        self::$custom_reduction = $func;

        return $func;

        //self::$custom_reduction = \Closure::bind($func, $m, $class);
    }

    /**
     * Inline Trim
     *
     * Removes leading zeros
     */
    private static function generateInlineTrim(string $name): string
    {
        return '
            for ($i = count($' . $name . ') - 1; $i >= 0; --$i) {
                if ($' . $name . '[$i]) {
                    break;
                }
                unset($' . $name . '[$i]);
            }';
    }

    /**
     * Inline Multiply (unknown, known)
     */
    private static function generateInlineMultiply(string $input, array $arr, string $output, string $class): string
    {
        if (!count($arr)) {
            return 'return [];';
        }

        $regular = '
            $length = count($' . $input . ');
            if (!$length) {
                $' . $output . ' = [];
            }else{
            $' . $output . ' = array_fill(0, $length + ' . count($arr) . ', 0);
            $carry = 0;';

        for ($i = 0; $i < count($arr); $i++) {
            $regular .= '
                $subtemp = $' . $input . '[0] * ' . $arr[$i];
            $regular .= $i ? ' + $carry;' : ';';

            $regular .= '$carry = ';
            $regular .= $class::BASE === 26 ?
            'intval($subtemp / 0x4000000);' :
            '$subtemp >> 31;';
            $regular .=
            '$' . $output . '[' . $i . '] = ';
            if ($class::BASE === 26) {
                $regular .= '(int) (';
            }
            $regular .= '$subtemp - ' . $class::BASE_FULL . ' * $carry';
            $regular .= $class::BASE === 26 ? ');' : ';';
        }

        $regular .= '$' . $output . '[' . count($arr) . '] = $carry;';

        $regular .= '
            for ($i = 1; $i < $length; ++$i) {';

        for ($j = 0; $j < count($arr); $j++) {
            $regular .= $j ? '$k++;' : '$k = $i;';
            $regular .= '
                $subtemp = $' . $output . '[$k] + $' . $input . '[$i] * ' . $arr[$j];
            $regular .= $j ? ' + $carry;' : ';';

            $regular .= '$carry = ';
            $regular .= $class::BASE === 26 ?
                'intval($subtemp / 0x4000000);' :
                '$subtemp >> 31;';
            $regular .=
                '$' . $output . '[$k] = ';
            if ($class::BASE === 26) {
                $regular .= '(int) (';
            }
            $regular .= '$subtemp - ' . $class::BASE_FULL . ' * $carry';
            $regular .= $class::BASE === 26 ? ');' : ';';
        }

        $regular .= '$' . $output . '[++$k] = $carry; $carry = 0;';

        $regular .= '}}';

        //if (count($arr) < 2 * self::KARATSUBA_CUTOFF) {
        //}

        return $regular;
    }

    /**
     * Inline Addition
     */
    private static function generateInlineAdd(string $x, string $y, string $result, string $class): string
    {
        $code = '
            $length = max(count($' . $x . '), count($' . $y . '));
            $' . $result . ' = array_pad($' . $x . ', $length + 1, 0);
            $_' . $y . ' = array_pad($' . $y . ', $length, 0);
            $carry = 0;
            for ($i = 0, $j = 1; $j < $length; $i+=2, $j+=2) {
                $sum = ($' . $result . '[$j] + $_' . $y . '[$j]) * ' . $class::BASE_FULL . '
                           + $' . $result . '[$i] + $_' . $y . '[$i] +
                           $carry;
                $carry = $sum >= ' . self::float2string($class::MAX_DIGIT2) . ';
                $sum = $carry ? $sum - ' . self::float2string($class::MAX_DIGIT2) . ' : $sum;';

        $code .= $class::BASE === 26 ?
            '$upper = intval($sum / 0x4000000); $' . $result . '[$i] = (int) ($sum - ' . $class::BASE_FULL . ' * $upper);' :
            '$upper = $sum >> 31; $' . $result . '[$i] = $sum - ' . $class::BASE_FULL . ' * $upper;';
        $code .= '
                $' . $result . '[$j] = $upper;
            }
            if ($j == $length) {
                $sum = $' . $result . '[$i] + $_' . $y . '[$i] + $carry;
                $carry = $sum >= ' . self::float2string($class::BASE_FULL) . ';
                $' . $result . '[$i] = $carry ? $sum - ' . self::float2string($class::BASE_FULL) . ' : $sum;
                ++$i;
            }
            if ($carry) {
                for (; $' . $result . '[$i] == ' . $class::MAX_DIGIT . '; ++$i) {
                    $' . $result . '[$i] = 0;
                }
                ++$' . $result . '[$i];
            }';
        $code .= self::generateInlineTrim($result);

        return $code;
    }

    /**
     * Inline Subtraction 2
     *
     * For when $known is more digits than $unknown. This is the harder use case to optimize for.
     */
    private static function generateInlineSubtract2(string $known, string $unknown, string $result, string $class): string
    {
        $code = '
            $' . $result . ' = $' . $known . ';
            $carry = 0;
            $size = count($' . $unknown . ');
            for ($i = 0, $j = 1; $j < $size; $i+= 2, $j+= 2) {
                $sum = ($' . $known . '[$j] - $' . $unknown . '[$j]) * ' . $class::BASE_FULL . ' + $' . $known . '[$i]
                    - $' . $unknown . '[$i]
                    - $carry;
                $carry = $sum < 0;
                if ($carry) {
                    $sum+= ' . self::float2string($class::MAX_DIGIT2) . ';
                }
                $subtemp = ';
        $code .= $class::BASE === 26 ?
            'intval($sum / 0x4000000);' :
            '$sum >> 31;';
        $code .= '$' . $result . '[$i] = ';
        if ($class::BASE === 26) {
            $code .= '(int) (';
        }
        $code .= '$sum - ' . $class::BASE_FULL . ' * $subtemp';
        if ($class::BASE === 26) {
            $code .= ')';
        }
        $code .= ';
                $' . $result . '[$j] = $subtemp;
            }
            if ($j == $size) {
                $sum = $' . $known . '[$i] - $' . $unknown . '[$i] - $carry;
                $carry = $sum < 0;
                $' . $result . '[$i] = $carry ? $sum + ' . $class::BASE_FULL . ' : $sum;
                ++$i;
            }

            if ($carry) {
                for (; !$' . $result . '[$i]; ++$i) {
                    $' . $result . '[$i] = ' . $class::MAX_DIGIT . ';
                }
                --$' . $result . '[$i];
            }';

        $code .= self::generateInlineTrim($result);

        return $code;
    }

    /**
     * Inline Subtraction 1
     *
     * For when $unknown is more digits than $known. This is the easier use case to optimize for.
     */
    private static function generateInlineSubtract1(string $unknown, array $known, string $result, string $class): string
    {
        $code = '$' . $result . ' = $' . $unknown . ';';
        for ($i = 0, $j = 1; $j < count($known); $i += 2, $j += 2) {
            $code .= '$sum = $' . $unknown . '[' . $j . '] * ' . $class::BASE_FULL . ' + $' . $unknown . '[' . $i . '] - ';
            $code .= self::float2string($known[$j] * $class::BASE_FULL + $known[$i]);
            if ($i != 0) {
                $code .= ' - $carry';
            }

            $code .= ';
                if ($carry = $sum < 0) {
                    $sum+= ' . self::float2string($class::MAX_DIGIT2) . ';
                }
                $subtemp = ';
            $code .= $class::BASE === 26 ?
                'intval($sum / 0x4000000);' :
                '$sum >> 31;';
            $code .= '
                $' . $result . '[' . $i . '] = ';
            if ($class::BASE === 26) {
                $code .= ' (int) (';
            }
            $code .= '$sum - ' . $class::BASE_FULL . ' * $subtemp';
            if ($class::BASE === 26) {
                $code .= ')';
            }
            $code .= ';
                $' . $result . '[' . $j . '] = $subtemp;';
        }

        $code .= '$i = ' . $i . ';';

        if ($j == count($known)) {
            $code .= '
                $sum = $' . $unknown . '[' . $i . '] - ' . $known[$i] . ' - $carry;
                $carry = $sum < 0;
                $' . $result . '[' . $i . '] = $carry ? $sum + ' . $class::BASE_FULL . ' : $sum;
                ++$i;';
        }

        $code .= '
            if ($carry) {
                for (; !$' . $result . '[$i]; ++$i) {
                    $' . $result . '[$i] = ' . $class::MAX_DIGIT . ';
                }
                --$' . $result . '[$i];
            }';
        $code .= self::generateInlineTrim($result);

        return $code;
    }

    /**
     * Inline Comparison
     *
     * If $unknown >= $known then loop
     */
    private static function generateInlineCompare(array $known, string $unknown, string $subcode): string
    {
        $uniqid = uniqid();
        $code = 'loop_' . $uniqid . ':
            $clength = count($' . $unknown . ');
            switch (true) {
                case $clength < ' . count($known) . ':
                    goto end_' . $uniqid . ';
                case $clength > ' . count($known) . ':';
        for ($i = count($known) - 1; $i >= 0; $i--) {
            $code .= '
                case $' . $unknown . '[' . $i . '] > ' . $known[$i] . ':
                    goto subcode_' . $uniqid . ';
                case $' . $unknown . '[' . $i . '] < ' . $known[$i] . ':
                    goto end_' . $uniqid . ';';
        }
        $code .= '
                default:
                    // do subcode
            }

            subcode_' . $uniqid . ':' . $subcode . '
            goto loop_' . $uniqid . ';

            end_' . $uniqid . ':';

        return $code;
    }

    /**
     * Convert a float to a string
     *
     * If you do echo floatval(pow(2, 52)) you'll get 4.6116860184274E+18. It /can/ be displayed without a loss of
     * precision but displayed in this way there will be precision loss, hence the need for this method.
     */
    private static function float2string(int|float $num): string
    {
        if (!is_float($num)) {
            return (string) $num;
        }

        if ($num < 0) {
            return '-' . self::float2string(abs($num));
        }

        $temp = '';
        while ($num) {
            $temp = fmod($num, 10) . $temp;
            $num = floor($num / 10);
        }

        return $temp;
    }
}
<?php

/**
 * PHP Montgomery Modular Exponentiation Engine
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2017-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Math\BigInteger\Engines\PHP\Reductions;

use phpseclib4\Math\BigInteger\Engines\PHP\Montgomery as Progenitor;

/**
 * PHP Montgomery Modular Exponentiation Engine
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class Montgomery extends Progenitor
{
    /**
     * Prepare a number for use in Montgomery Modular Reductions
     */
    protected static function prepareReduce(array $x, array $n, string $class): array
    {
        $lhs = new $class();
        $lhs->value = array_merge(self::array_repeat(0, count($n)), $x);
        $rhs = new $class();
        $rhs->value = $n;

        [, $temp] = $lhs->divide($rhs);
        return $temp->value;
    }

    /**
     * Montgomery Multiply
     *
     * Interleaves the montgomery reduction and long multiplication algorithms together as described in
     * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=13 HAC 14.36}
     */
    protected static function reduce(array $x, array $n, string $class): array
    {
        static $cache = [
            self::VARIABLE => [],
            self::DATA => [],
        ];

        if (($key = array_search($n, $cache[self::VARIABLE])) === false) {
            $key = count($cache[self::VARIABLE]);
            $cache[self::VARIABLE][] = $x;
            $cache[self::DATA][] = self::modInverse67108864($n, $class);
        }

        $k = count($n);

        $result = [self::VALUE => $x];

        for ($i = 0; $i < $k; ++$i) {
            $temp = $result[self::VALUE][$i] * $cache[self::DATA][$key];
            $temp = $temp - $class::BASE_FULL * ($class::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31));
            $temp = $class::regularMultiply([$temp], $n);
            $temp = array_merge(self::array_repeat(0, $i), $temp);
            $result = $class::addHelper($result[self::VALUE], false, $temp, false);
        }

        $result[self::VALUE] = array_slice($result[self::VALUE], $k);

        if (self::compareHelper($result, false, $n, false) >= 0) {
            $result = $class::subtractHelper($result[self::VALUE], false, $n, false);
        }

        return $result[self::VALUE];
    }

    /**
     * Modular Inverse of a number mod 2**26 (eg. 67108864)
     *
     * Based off of the bnpInvDigit function implemented and justified in the following URL:
     *
     * {@link http://www-cs-students.stanford.edu/~tjw/jsbn/jsbn.js}
     *
     * The following URL provides more info:
     *
     * {@link http://groups.google.com/group/sci.crypt/msg/7a137205c1be7d85}
     *
     * As for why we do all the bitmasking...  strange things can happen when converting from floats to ints. For
     * instance, on some computers, var_dump((int) -4294967297) yields int(-1) and on others, it yields
     * int(-2147483648).  To avoid problems stemming from this, we use bitmasks to guarantee that ints aren't
     * auto-converted to floats.  The outermost bitmask is present because without it, there's no guarantee that
     * the "residue" returned would be the so-called "common residue".  We use fmod, in the last step, because the
     * maximum possible $x is 26 bits and the maximum $result is 16 bits.  Thus, we have to be able to handle up to
     * 40 bits, which only 64-bit floating points will support.
     *
     * Thanks to Pedro Gimeno Fortea for input!
     */
    protected static function modInverse67108864(array $x, string $class): int // 2**26 == 67,108,864
    {
        $x = -$x[0];
        $result = $x & 0x3; // x**-1 mod 2**2
        $result = ($result * (2 - $x * $result)) & 0xF; // x**-1 mod 2**4
        $result = ($result * (2 - ($x & 0xFF) * $result))  & 0xFF; // x**-1 mod 2**8
        $result = ($result * ((2 - ($x & 0xFFFF) * $result) & 0xFFFF)) & 0xFFFF; // x**-1 mod 2**16
        $result = $class::BASE == 26 ?
            fmod($result * (2 - fmod($x * $result, $class::BASE_FULL)), $class::BASE_FULL) : // x**-1 mod 2**26
            ($result * (2 - ($x * $result) % $class::BASE_FULL)) % $class::BASE_FULL;
        return $result & $class::MAX_DIGIT;
    }
}
<?php

/**
 * PHP Classic Modular Exponentiation Engine
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2017-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Math\BigInteger\Engines\PHP\Reductions;

use phpseclib4\Math\BigInteger\Engines\PHP\Base;

/**
 * PHP Classic Modular Exponentiation Engine
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class Classic extends Base
{
    /**
     * Regular Division
     */
    protected static function reduce(array $x, array $n, string $class): array
    {
        $lhs = new $class();
        $lhs->value = $x;
        $rhs = new $class();
        $rhs->value = $n;
        [, $temp] = $lhs->divide($rhs);
        return $temp->value;
    }
}
<?php

/**
 * PHP Modular Exponentiation Engine
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2017-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Math\BigInteger\Engines\PHP;

use phpseclib4\Math\BigInteger\Engines\PHP;

/**
 * PHP Modular Exponentiation Engine
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class Base extends PHP
{
    /**
     * Cache constants
     *
     * $cache[self::VARIABLE] tells us whether or not the cached data is still valid.
     */
    public const VARIABLE = 0;
    /**
     * $cache[self::DATA] contains the cached data.
     */
    public const DATA = 1;

    /**
     * Test for engine validity
     */
    public static function isValidEngine(): bool
    {
        return static::class != __CLASS__;
    }

    /**
     * Performs modular exponentiation.
     *
     * The most naive approach to modular exponentiation has very unreasonable requirements, and
     * and although the approach involving repeated squaring does vastly better, it, too, is impractical
     * for our purposes.  The reason being that division - by far the most complicated and time-consuming
     * of the basic operations (eg. +,-,*,/) - occurs multiple times within it.
     *
     * Modular reductions resolve this issue.  Although an individual modular reduction takes more time
     * then an individual division, when performed in succession (with the same modulo), they're a lot faster.
     *
     * The two most commonly used modular reductions are Barrett and Montgomery reduction.  Montgomery reduction,
     * although faster, only works when the gcd of the modulo and of the base being used is 1.  In RSA, when the
     * base is a power of two, the modulo - a product of two primes - is always going to have a gcd of 1 (because
     * the product of two odd numbers is odd), but what about when RSA isn't used?
     *
     * In contrast, Barrett reduction has no such constraint.  As such, some bigint implementations perform a
     * Barrett reduction after every operation in the modpow function.  Others perform Barrett reductions when the
     * modulo is even and Montgomery reductions when the modulo is odd.  BigInteger.java's modPow method, however,
     * uses a trick involving the Chinese Remainder Theorem to factor the even modulo into two numbers - one odd and
     * the other, a power of two - and recombine them, later.  This is the method that this modPow function uses.
     * {@link http://islab.oregonstate.edu/papers/j34monex.pdf Montgomery Reduction with Even Modulus} elaborates.
     */
    protected static function powModHelper(PHP $x, PHP $e, PHP $n, string $class): PHP
    {
        if (empty($e->value)) {
            $temp = new $class();
            $temp->value = [1];
            return $x->normalize($temp);
        }

        if ($e->value == [1]) {
            [, $temp] = $x->divide($n);
            return $x->normalize($temp);
        }

        if ($e->value == [2]) {
            $temp = new $class();
            $temp->value = $class::square($x->value);
            [, $temp] = $temp->divide($n);
            return $x->normalize($temp);
        }

        return $x->normalize(static::slidingWindow($x, $e, $n, $class));
    }

    /**
     * Modular reduction preparation
     *
     * @see self::slidingWindow()
     */
    protected static function prepareReduce(array $x, array $n, string $class): array
    {
        return static::reduce($x, $n, $class);
    }

    /**
     * Modular multiply
     *
     * @see self::slidingWindow()
     */
    protected static function multiplyReduce(array $x, array $y, array $n, string $class): array
    {
        $temp = $class::multiplyHelper($x, false, $y, false);
        return static::reduce($temp[self::VALUE], $n, $class);
    }

    /**
     * Modular square
     *
     * @see self::slidingWindow()
     */
    protected static function squareReduce(array $x, array $n, string $class): array
    {
        return static::reduce($class::square($x), $n, $class);
    }
}
<?php

/**
 * OpenSSL Modular Exponentiation Engine
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2017-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Math\BigInteger\Engines\PHP;

use phpseclib4\Math\BigInteger\Engines\OpenSSL as Progenitor;

/**
 * OpenSSL Modular Exponentiation Engine
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class OpenSSL extends Progenitor
{
}
<?php

/**
 * PHP Montgomery Modular Exponentiation Engine
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2017-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Math\BigInteger\Engines\PHP;

use phpseclib4\Exception\InvalidArgumentException;
use phpseclib4\Math\BigInteger\Engines\{Engine, PHP};
use phpseclib4\Math\BigInteger\Engines\PHP\Reductions\PowerOfTwo;

/**
 * PHP Montgomery Modular Exponentiation Engine
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class Montgomery extends Base
{
    /**
     * Test for engine validity
     */
    public static function isValidEngine(): bool
    {
        return static::class != __CLASS__;
    }

    /**
     * Performs modular exponentiation.
     *
     * @template T of PHP
     * @param class-string<T> $class
     * @return T
     */
    protected static function slidingWindow(Engine $x, Engine $e, Engine $n, string $class): PHP
    {
        if (!$x instanceof PHP || !$e instanceof PHP || !$n instanceof PHP) {
            throw new InvalidArgumentException('Montgomery exponentiation only works with PHP engines');
        }
        // is the modulo odd?
        if ($n->value[0] & 1) {
            return parent::slidingWindow($x, $e, $n, $class);
        }
        // if it's not, it's even

        // find the lowest set bit (eg. the max pow of 2 that divides $n)
        for ($i = 0; $i < count($n->value); ++$i) {
            if ($n->value[$i]) {
                $temp = decbin($n->value[$i]);
                $j = strlen($temp) - strrpos($temp, '1') - 1;
                $j += $class::BASE * $i;
                break;
            }
        }
        // at this point, 2^$j * $n/(2^$j) == $n

        $mod1 = clone $n;
        $mod1->rshift($j);
        $mod2 = new $class();
        $mod2->value = [1];
        $mod2->lshift($j);

        $part1 = $mod1->value != [1] ? parent::slidingWindow($x, $e, $mod1, $class) : new $class();
        $part2 = PowerOfTwo::slidingWindow($x, $e, $mod2, $class);

        $y1 = $mod2->modInverse($mod1);
        $y2 = $mod1->modInverse($mod2);

        $result = $part1->multiply($mod2);
        $result = $result->multiply($y1);

        $temp = $part2->multiply($mod1);
        $temp = $temp->multiply($y2);

        $result = $result->add($temp);
        [, $result] = $result->divide($n);

        return $result;
    }
}
<?php

/**
 * PHP Default Modular Exponentiation Engine
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2017-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Math\BigInteger\Engines\PHP;

use phpseclib4\Math\BigInteger\Engines\PHP\Reductions\EvalBarrett;

/**
 * PHP Default Modular Exponentiation Engine
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class DefaultEngine extends EvalBarrett
{
}
<?php

/**
 * Pure-PHP BigInteger Engine
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2017-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Math\BigInteger\Engines;

use phpseclib4\Common\Functions\Strings;
use phpseclib4\Exception\{BadConfigurationException, InvalidArgumentException};

/**
 * Pure-PHP Engine.
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class PHP extends Engine
{
    public const BASE = -1;
    public const BASE_FULL = -1;
    public const MAX_DIGIT = -1;
    public const MAX10 = -1;
    public const MAX10LEN = -1;
    public const MAX_DIGIT2 = -1;
    public const MSB = -1;

    /**#@+
     * Array constants
     *
     * Rather than create a thousands and thousands of new BigInteger objects in repeated function calls to add() and
     * multiply() or whatever, we'll just work directly on arrays, taking them in as parameters and returning them.
     *
     */
    /**
     * $result[self::VALUE] contains the value.
     */
    public const VALUE = 0;
    /**
     * $result[self::SIGN] contains the sign.
     */
    public const SIGN = 1;
    /**#@-*/

    /**
     * Karatsuba Cutoff
     *
     * At what point do we switch between Karatsuba multiplication and schoolbook long multiplication?
     */
    public const KARATSUBA_CUTOFF = 25;

    /**
     * Can Bitwise operations be done fast?
     *
     * @see parent::bitwise_leftRotate()
     * @see parent::bitwise_rightRotate()
     */
    public const FAST_BITWISE = true;

    /**
     * Engine Directory
     *
     * @see parent::setModExpEngine
     */
    public const ENGINE_DIR = 'PHP';

    /**
     * Holds the BigInteger's value
     */
    protected string|array $value;

    /**
     * Default constructor
     *
     * @param mixed $x integer Base-10 number or base-$base number if $base set.
     * @see parent::__construct()
     */
    public function __construct(int|string|\GMP $x = 0, int $base = 10)
    {
        if (!isset(static::$isValidEngine[static::class])) {
            static::$isValidEngine[static::class] = static::isValidEngine();
        }
        if (!static::$isValidEngine[static::class]) {
            throw new BadConfigurationException(static::class . ' is not setup correctly on this system');
        }

        $this->value = [];
        parent::__construct($x, $base);
    }

    /**
     * Initialize a PHP BigInteger Engine instance
     *
     * @see parent::__construct()
     */
    protected function initialize(int $base): void
    {
        switch (abs($base)) {
            case 16:
                $x = (strlen($this->value) & 1) ? '0' . $this->value : $this->value;
                $temp = new static(Strings::hex2bin($x), 256);
                $this->value = $temp->value;
                break;
            case 10:
                $temp = new static();

                $multiplier = new static();
                $multiplier->value = [static::MAX10];

                $x = $this->value;

                if ($x[0] == '-') {
                    $this->is_negative = true;
                    $x = substr($x, 1);
                }

                $x = str_pad(
                    $x,
                    strlen($x) + ((static::MAX10LEN - 1) * strlen($x)) % static::MAX10LEN,
                    '0',
                    STR_PAD_LEFT
                );
                while (strlen($x)) {
                    $temp = $temp->multiply($multiplier);
                    $temp = $temp->add(new static($this->int2bytes((int) substr($x, 0, static::MAX10LEN)), 256));
                    $x = substr($x, static::MAX10LEN);
                }

                $this->value = $temp->value;
        }
    }

    /**
     * Converts a BigInteger to a base-10 number.
     */
    public function toString(): string
    {
        if (!count($this->value)) {
            return '0';
        }

        $temp = clone $this;
        $temp->bitmask = null;
        $temp->is_negative = false;

        $divisor = new static();
        $divisor->value = [static::MAX10];
        $result = '';
        while (count($temp->value)) {
            [$temp, $mod] = $temp->divide($divisor);
            $result = str_pad(
                (string) ($mod->value[0] ?? ''),
                static::MAX10LEN,
                '0',
                STR_PAD_LEFT
            ) . $result;
        }
        $result = ltrim($result, '0');
        if (empty($result)) {
            $result = '0';
        }

        if ($this->is_negative) {
            $result = '-' . $result;
        }

        return $result;
    }

    /**
     * Converts a BigInteger to a byte string (eg. base-256).
     */
    public function toBytes(bool $twos_compliment = false): string
    {
        if ($twos_compliment) {
            return $this->toBytesHelper();
        }

        if (!count($this->value)) {
            return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
        }

        $result = $this->bitwise_small_split(8);
        $result = implode('', array_map('chr', $result));

        return $this->precision > 0 ?
            str_pad(
                substr($result, -(($this->precision + 7) >> 3)),
                ($this->precision + 7) >> 3,
                chr(0),
                STR_PAD_LEFT
            ) :
            $result;
    }

    /**
     * Performs addition.
     */
    protected static function addHelper(array $x_value, bool $x_negative, array $y_value, bool $y_negative): array
    {
        $x_size = count($x_value);
        $y_size = count($y_value);

        if ($x_size == 0) {
            return [
                self::VALUE => $y_value,
                self::SIGN => $y_negative,
            ];
        } elseif ($y_size == 0) {
            return [
                self::VALUE => $x_value,
                self::SIGN => $x_negative,
            ];
        }

        // subtract, if appropriate
        if ($x_negative != $y_negative) {
            if ($x_value == $y_value) {
                return [
                    self::VALUE => [],
                    self::SIGN => false,
                ];
            }

            $temp = self::subtractHelper($x_value, false, $y_value, false);
            $temp[self::SIGN] = self::compareHelper($x_value, false, $y_value, false) > 0 ?
                $x_negative : $y_negative;

            return $temp;
        }

        if ($x_size < $y_size) {
            $size = $x_size;
            $value = $y_value;
        } else {
            $size = $y_size;
            $value = $x_value;
        }

        $value[count($value)] = 0; // just in case the carry adds an extra digit

        $carry = 0;
        for ($i = 0, $j = 1; $j < $size; $i += 2, $j += 2) {
            //$sum = $x_value[$j] * static::BASE_FULL + $x_value[$i] + $y_value[$j] * static::BASE_FULL + $y_value[$i] + $carry;
            $sum = ($x_value[$j] + $y_value[$j]) * static::BASE_FULL + $x_value[$i] + $y_value[$i] + $carry;
            $carry = $sum >= static::MAX_DIGIT2; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1
            $sum = $carry ? $sum - static::MAX_DIGIT2 : $sum;

            $temp = static::BASE === 26 ? intval($sum / 0x4000000) : ($sum >> 31);

            $value[$i] = (int)($sum - static::BASE_FULL * $temp); // eg. a faster alternative to fmod($sum, 0x4000000)
            $value[$j] = $temp;
        }

        if ($j == $size) { // ie. if $y_size is odd
            $sum = $x_value[$i] + $y_value[$i] + $carry;
            $carry = $sum >= static::BASE_FULL;
            $value[$i] = $carry ? $sum - static::BASE_FULL : $sum;
            ++$i; // ie. let $i = $j since we've just done $value[$i]
        }

        if ($carry) {
            for (; $value[$i] == static::MAX_DIGIT; ++$i) {
                $value[$i] = 0;
            }
            ++$value[$i];
        }

        return [
            self::VALUE => self::trim($value),
            self::SIGN => $x_negative,
        ];
    }

    /**
     * Performs subtraction.
     */
    protected static function subtractHelper(array $x_value, bool $x_negative, array $y_value, bool $y_negative): array
    {
        $x_size = count($x_value);
        $y_size = count($y_value);

        if ($x_size == 0) {
            return [
                self::VALUE => $y_value,
                self::SIGN => !$y_negative,
            ];
        } elseif ($y_size == 0) {
            return [
                self::VALUE => $x_value,
                self::SIGN => $x_negative,
            ];
        }

        // add, if appropriate (ie. -$x - +$y or +$x - -$y)
        if ($x_negative != $y_negative) {
            $temp = self::addHelper($x_value, false, $y_value, false);
            $temp[self::SIGN] = $x_negative;

            return $temp;
        }

        $diff = self::compareHelper($x_value, $x_negative, $y_value, $y_negative);

        if (!$diff) {
            return [
                self::VALUE => [],
                self::SIGN => false,
            ];
        }

        // switch $x and $y around, if appropriate.
        if ((!$x_negative && $diff < 0) || ($x_negative && $diff > 0)) {
            $temp = $x_value;
            $x_value = $y_value;
            $y_value = $temp;

            $x_negative = !$x_negative;

            //$x_size = count($x_value);
            $y_size = count($y_value);
        }

        // at this point, $x_value should be at least as big as - if not bigger than - $y_value

        $carry = 0;
        for ($i = 0, $j = 1; $j < $y_size; $i += 2, $j += 2) {
            $sum = ($x_value[$j] - $y_value[$j]) * static::BASE_FULL + $x_value[$i] - $y_value[$i] - $carry;

            $carry = $sum < 0; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1
            $sum = $carry ? $sum + static::MAX_DIGIT2 : $sum;

            $temp = static::BASE === 26 ? intval($sum / 0x4000000) : ($sum >> 31);

            $x_value[$i] = (int)($sum - static::BASE_FULL * $temp);
            $x_value[$j] = $temp;
        }

        if ($j == $y_size) { // ie. if $y_size is odd
            $sum = $x_value[$i] - $y_value[$i] - $carry;
            $carry = $sum < 0;
            $x_value[$i] = $carry ? $sum + static::BASE_FULL : $sum;
            ++$i;
        }

        if ($carry) {
            for (; !$x_value[$i]; ++$i) {
                $x_value[$i] = static::MAX_DIGIT;
            }
            --$x_value[$i];
        }

        return [
            self::VALUE => self::trim($x_value),
            self::SIGN => $x_negative,
        ];
    }

    /**
     * Performs multiplication.
     */
    protected static function multiplyHelper(array $x_value, bool $x_negative, array $y_value, bool $y_negative): array
    {
        //if ( $x_value == $y_value ) {
        //    return [
        //        self::VALUE => self::square($x_value),
        //        self::SIGN => $x_sign != $y_value
        //    ];
        //}

        $x_length = count($x_value);
        $y_length = count($y_value);

        if (!$x_length || !$y_length) { // a 0 is being multiplied
            return [
                self::VALUE => [],
                self::SIGN => false,
            ];
        }

        return [
            self::VALUE => min($x_length, $y_length) < 2 * self::KARATSUBA_CUTOFF ?
                self::trim(self::regularMultiply($x_value, $y_value)) :
                self::trim(self::karatsuba($x_value, $y_value)),
            self::SIGN => $x_negative != $y_negative,
        ];
    }

    /**
     * Performs Karatsuba multiplication on two BigIntegers
     *
     * See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm Karatsuba algorithm} and
     * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=120 MPM 5.2.3}.
     */
    private static function karatsuba(array $x_value, array $y_value): array
    {
        $m = min(count($x_value) >> 1, count($y_value) >> 1);

        if ($m < self::KARATSUBA_CUTOFF) {
            return self::regularMultiply($x_value, $y_value);
        }

        $x1 = array_slice($x_value, $m);
        $x0 = array_slice($x_value, 0, $m);
        $y1 = array_slice($y_value, $m);
        $y0 = array_slice($y_value, 0, $m);

        $z2 = self::karatsuba($x1, $y1);
        $z0 = self::karatsuba($x0, $y0);

        $z1 = self::addHelper($x1, false, $x0, false);
        $temp = self::addHelper($y1, false, $y0, false);
        $z1 = self::karatsuba($z1[self::VALUE], $temp[self::VALUE]);
        $temp = self::addHelper($z2, false, $z0, false);
        $z1 = self::subtractHelper($z1, false, $temp[self::VALUE], false);

        $z2 = array_merge(array_fill(0, 2 * $m, 0), $z2);
        $z1[self::VALUE] = array_merge(array_fill(0, $m, 0), $z1[self::VALUE]);

        $xy = self::addHelper($z2, false, $z1[self::VALUE], $z1[self::SIGN]);
        $xy = self::addHelper($xy[self::VALUE], $xy[self::SIGN], $z0, false);

        return $xy[self::VALUE];
    }

    /**
     * Performs long multiplication on two BigIntegers
     *
     * Modeled after 'multiply' in MutableBigInteger.java.
     */
    protected static function regularMultiply(array $x_value, array $y_value): array
    {
        $x_length = count($x_value);
        $y_length = count($y_value);

        if (!$x_length || !$y_length) { // a 0 is being multiplied
            return [];
        }

        $product_value = self::array_repeat(0, $x_length + $y_length);

        // the following for loop could be removed if the for loop following it
        // (the one with nested for loops) initially set $i to 0, but
        // doing so would also make the result in one set of unnecessary adds,
        // since on the outermost loops first pass, $product->value[$k] is going
        // to always be 0

        $carry = 0;
        for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0
            $temp = $x_value[$j] * $y_value[0] + $carry; // $product_value[$k] == 0
            $carry = static::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
            $product_value[$j] = (int)($temp - static::BASE_FULL * $carry);
        }

        $product_value[$j] = $carry;

        // the above for loop is what the previous comment was talking about.  the
        // following for loop is the "one with nested for loops"
        for ($i = 1; $i < $y_length; ++$i) {
            $carry = 0;

            for ($j = 0, $k = $i; $j < $x_length; ++$j, ++$k) {
                $temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry;
                $carry = static::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
                $product_value[$k] = (int)($temp - static::BASE_FULL * $carry);
            }

            $product_value[$k] = $carry;
        }

        return $product_value;
    }

    /**
     * Divides two BigIntegers.
     *
     * Returns an array whose first element contains the quotient and whose second element contains the
     * "common residue".  If the remainder would be positive, the "common residue" and the remainder are the
     * same.  If the remainder would be negative, the "common residue" is equal to the sum of the remainder
     * and the divisor (basically, the "common residue" is the first positive modulo).
     *
     * @return array{static, static}
     * @internal This function is based off of
     *     {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=9 HAC 14.20}.
     */
    protected function divideHelper(PHP $y): array
    {
        if (count($y->value) == 1) {
            [$q, $r] = $this->divide_digit($this->value, $y->value[0]);
            $quotient = new static();
            $remainder = new static();
            $quotient->value = $q;
            // The common residue is the first positive modulo, so it is only the
            // negative remainders that need the divisor added. A remainder of 0 is
            // already the residue; adding the divisor would return the modulus
            // itself, which is never a valid residue.
            if ($this->is_negative && $r) {
                $r = $y->value[0] - $r;
            }
            $remainder->value = [$r];
            $quotient->is_negative = $this->is_negative != $y->is_negative;
            return [$this->normalize($quotient), $this->normalize($remainder)];
        }

        $x = clone $this;
        $y = clone $y;

        $x_sign = $x->is_negative;
        $y_sign = $y->is_negative;

        $x->is_negative = $y->is_negative = false;

        $diff = $x->compare($y);

        if (!$diff) {
            $temp = new static();
            $temp->value = [1];
            $temp->is_negative = $x_sign != $y_sign;
            return [$this->normalize($temp), $this->normalize(static::$zero[static::class])];
        }

        if ($diff < 0) {
            // if $x is negative, "add" $y.
            if ($x_sign) {
                $x = $y->subtract($x);
            }
            return [$this->normalize(static::$zero[static::class]), $this->normalize($x)];
        }

        // normalize $x and $y as described in HAC 14.23 / 14.24
        $msb = $y->value[count($y->value) - 1];
        for ($shift = 0; !($msb & static::MSB); ++$shift) {
            $msb <<= 1;
        }
        $x->lshift($shift);
        $y->lshift($shift);
        $y_value = &$y->value;

        $x_max = count($x->value) - 1;
        $y_max = count($y_value) - 1;

        $quotient = new static();
        $quotient_value = &$quotient->value;
        $quotient_value = self::array_repeat(0, $x_max - $y_max + 1);

        static $temp, $lhs, $rhs;
        if (!isset($temp)) {
            $temp = new static();
            $lhs = new static();
            $rhs = new static();
        }
        if (static::class != $temp::class) {
            $temp = new static();
            $lhs = new static();
            $rhs = new static();
        }
        $temp_value = &$temp->value;
        $rhs_value =  &$rhs->value;

        // $temp = $y << ($x_max - $y_max-1) in base 2**26
        $temp_value = array_merge(self::array_repeat(0, $x_max - $y_max), $y_value);

        while ($x->compare($temp) >= 0) {
            // calculate the "common residue"
            ++$quotient_value[$x_max - $y_max];
            $x = $x->subtract($temp);
            $x_max = count($x->value) - 1;
        }

        for ($i = $x_max; $i >= $y_max + 1; --$i) {
            $x_value = &$x->value;
            $x_window = [
                $x_value[$i] ?? 0,
                $x_value[$i - 1] ?? 0,
                $x_value[$i - 2] ?? 0,
            ];
            $y_window = [
                $y_value[$y_max],
                ($y_max > 0) ? $y_value[$y_max - 1] : 0,
            ];

            $q_index = $i - $y_max - 1;
            if ($x_window[0] == $y_window[0]) {
                $quotient_value[$q_index] = static::MAX_DIGIT;
            } else {
                $quotient_value[$q_index] = self::safe_divide(
                    $x_window[0] * static::BASE_FULL + $x_window[1],
                    $y_window[0]
                );
            }

            $temp_value = [$y_window[1], $y_window[0]];

            $lhs->value = [$quotient_value[$q_index]];
            $lhs = $lhs->multiply($temp);

            $rhs_value = [$x_window[2], $x_window[1], $x_window[0]];

            while ($lhs->compare($rhs) > 0) {
                --$quotient_value[$q_index];

                $lhs->value = [$quotient_value[$q_index]];
                $lhs = $lhs->multiply($temp);
            }

            $adjust = self::array_repeat(0, $q_index);
            $temp_value = [$quotient_value[$q_index]];
            $temp = $temp->multiply($y);
            $temp_value = &$temp->value;
            if (count($temp_value)) {
                $temp_value = array_merge($adjust, $temp_value);
            }

            $x = $x->subtract($temp);

            if ($x->compare(static::$zero[static::class]) < 0) {
                $temp_value = array_merge($adjust, $y_value);
                $x = $x->add($temp);

                --$quotient_value[$q_index];
            }

            //$x_max = count($x_value) - 1;
        }

        // unnormalize the remainder
        $x->rshift($shift);

        $quotient->is_negative = $x_sign != $y_sign;

        // calculate the "common residue", if appropriate. A remainder of 0 is
        // already the residue -- see divideHelper's single-digit branch.
        if ($x_sign && count($x->value)) {
            $y->rshift($shift);
            $x = $y->subtract($x);
        }

        return [$this->normalize($quotient), $this->normalize($x)];
    }

    /**
     * Divides a BigInteger by a regular integer
     *
     * abc / x = a00 / x + b0 / x + c / x
     */
    private static function divide_digit(array $dividend, int $divisor): array
    {
        $carry = 0;
        $result = [];

        for ($i = count($dividend) - 1; $i >= 0; --$i) {
            $temp = static::BASE_FULL * $carry + (int) $dividend[$i];
            $result[$i] = self::safe_divide($temp, $divisor);
            $carry = (int)($temp - $divisor * $result[$i]);
        }

        return [$result, $carry];
    }

    /**
     * Single digit division
     *
     * Even if int64 is being used the division operator will return a float64 value
     * if the dividend is not evenly divisible by the divisor. Since a float64 doesn't
     * have the precision of int64 this is a problem so, when int64 is being used,
     * we'll guarantee that the dividend is divisible by first subtracting the remainder.
     */
    private static function safe_divide(int $x, int $y): int
    {
        if (static::BASE === 26) {
            return intdiv($x, $y);
        }

        // static::BASE === 31
        /** @var int */
        return ($x - ($x % $y)) / $y;
    }

    /**
     * Convert an array / boolean to a PHP BigInteger object
     *
     * @return static
     */
    protected function convertToObj(array $arr): PHP
    {
        $result = new static();
        $result->value = $arr[self::VALUE];
        $result->is_negative = $arr[self::SIGN];

        return $this->normalize($result);
    }

    /**
     * Normalize
     *
     * Removes leading zeros and truncates (if necessary) to maintain the appropriate precision
     *
     * @return static
     */
    protected function normalize(PHP $result): PHP
    {
        $result->precision = $this->precision;
        $result->bitmask = $this->bitmask;

        $value = &$result->value;

        if (!count($value)) {
            $result->is_negative = false;
            return $result;
        }

        $value = static::trim($value);

        if (isset($result->bitmask->value)) {
            $length = min(count($value), count($result->bitmask->value));
            $value = array_slice($value, 0, $length);

            for ($i = 0; $i < $length; ++$i) {
                $value[$i] = $value[$i] & $result->bitmask->value[$i];
            }

            $value = static::trim($value);
        }

        return $result;
    }

    /**
     * Compares two numbers.
     *
     * @see static::compare()
     */
    protected static function compareHelper(array $x_value, bool $x_negative, array $y_value, bool $y_negative): int
    {
        if ($x_negative != $y_negative) {
            return (!$x_negative && $y_negative) ? 1 : -1;
        }

        $result = $x_negative ? -1 : 1;

        if (count($x_value) != count($y_value)) {
            return (count($x_value) > count($y_value)) ? $result : -$result;
        }
        $size = max(count($x_value), count($y_value));

        $x_value = array_pad($x_value, $size, 0);
        $y_value = array_pad($y_value, $size, 0);

        for ($i = count($x_value) - 1; $i >= 0; --$i) {
            if ($x_value[$i] != $y_value[$i]) {
                return ($x_value[$i] > $y_value[$i]) ? $result : -$result;
            }
        }

        return 0;
    }

    /**
     * Absolute value.
     */
    public function abs(): PHP
    {
        $temp = new static();
        $temp->value = $this->value;

        return $temp;
    }

    /**
     * Trim
     *
     * Removes leading zeros
     *
     * @param list<static> $value
     * @return list<static>
     */
    protected static function trim(array $value): array
    {
        for ($i = count($value) - 1; $i >= 0; --$i) {
            if ($value[$i]) {
                break;
            }
            unset($value[$i]);
        }

        return $value;
    }

    /**
     * Logical Right Shift
     *
     * Shifts BigInteger's by $shift bits, effectively dividing by 2**$shift.
     */
    public function bitwise_rightShift(int $shift): PHP
    {
        $temp = new static();

        // could just replace lshift with this, but then all lshift() calls would need to be rewritten
        // and I don't want to do that...
        $temp->value = $this->value;
        $temp->rshift($shift);

        return $this->normalize($temp);
    }

    /**
     * Logical Left Shift
     *
     * Shifts BigInteger's by $shift bits, effectively multiplying by 2**$shift.
     */
    public function bitwise_leftShift(int $shift): PHP
    {
        $temp = new static();
        // could just replace _rshift with this, but then all _lshift() calls would need to be rewritten
        // and I don't want to do that...
        $temp->value = $this->value;
        $temp->lshift($shift);

        return $this->normalize($temp);
    }

    /**
     * Converts 32-bit integers to bytes.
     */
    private static function int2bytes(int $x): string
    {
        return ltrim(pack('N', $x), chr(0));
    }

    /**
     * Array Repeat
     */
    protected static function array_repeat(int $input, int $multiplier): array
    {
        return $multiplier ? array_fill(0, $multiplier, $input) : [];
    }

    /**
     * Logical Left Shift
     *
     * Shifts BigInteger's by $shift bits.
     */
    protected function lshift(int $shift): void
    {
        if ($shift == 0) {
            return;
        }

        $num_digits = intdiv($shift, static::BASE);
        $shift %= static::BASE;
        $shift = 1 << $shift;

        $carry = 0;

        for ($i = 0; $i < count($this->value); ++$i) {
            $temp = $this->value[$i] * $shift + $carry;
            $carry = static::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
            $this->value[$i] = (int)($temp - $carry * static::BASE_FULL);
        }

        if ($carry) {
            $this->value[count($this->value)] = $carry;
        }

        while ($num_digits--) {
            array_unshift($this->value, 0);
        }
    }

    /**
     * Logical Right Shift
     *
     * Shifts BigInteger's by $shift bits.
     */
    protected function rshift(int $shift): void
    {
        if ($shift == 0) {
            return;
        }

        $num_digits = intdiv($shift, static::BASE);
        $shift %= static::BASE;
        $carry_shift = static::BASE - $shift;
        $carry_mask = (1 << $shift) - 1;

        if ($num_digits) {
            $this->value = array_slice($this->value, $num_digits);
        }

        $carry = 0;

        for ($i = count($this->value) - 1; $i >= 0; --$i) {
            $temp = $this->value[$i] >> $shift | $carry;
            $carry = ($this->value[$i] & $carry_mask) << $carry_shift;
            $this->value[$i] = $temp;
        }

        $this->value = static::trim($this->value);
    }

    /**
     * Performs modular exponentiation.
     */
    protected function powModInner(PHP $e, PHP $n): PHP
    {
        try {
            $class = static::$modexpEngine[static::class];
            return $class::powModHelper($this, $e, $n, static::class);
        } catch (\Exception) {
            return PHP\DefaultEngine::powModHelper($this, $e, $n, static::class);
        }
    }

    /**
     * Performs squaring
     *
     * @param list<static> $x
     * @return list<static>
     */
    protected static function square(array $x): array
    {
        return count($x) < 2 * self::KARATSUBA_CUTOFF ?
            self::trim(self::baseSquare($x)) :
            self::trim(self::karatsubaSquare($x));
    }

    /**
     * Performs traditional squaring on two BigIntegers
     *
     * Squaring can be done faster than multiplying a number by itself can be.  See
     * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=7 HAC 14.2.4} /
     * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=141 MPM 5.3} for more information.
     */
    protected static function baseSquare(array $value): array
    {
        if (empty($value)) {
            return [];
        }
        $square_value = self::array_repeat(0, 2 * count($value));

        for ($i = 0, $max_index = count($value) - 1; $i <= $max_index; ++$i) {
            $i2 = $i << 1;

            $temp = $square_value[$i2] + $value[$i] * $value[$i];
            $carry = static::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
            $square_value[$i2] = (int) ($temp - static::BASE_FULL * $carry);

            // note how we start from $i+1 instead of 0 as we do in multiplication.
            for ($j = $i + 1, $k = $i2 + 1; $j <= $max_index; ++$j, ++$k) {
                $temp = $square_value[$k] + 2 * $value[$j] * $value[$i] + $carry;
                $carry = static::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
                $square_value[$k] = (int) ($temp - static::BASE_FULL * $carry);
            }

            // the following line can yield values larger 2**15.  at this point, PHP should switch
            // over to floats.
            $square_value[$i + $max_index + 1] = $carry;
        }

        return $square_value;
    }

    /**
     * Performs Karatsuba "squaring" on two BigIntegers
     *
     * See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm Karatsuba algorithm} and
     * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=151 MPM 5.3.4}.
     */
    protected static function karatsubaSquare(array $value): array
    {
        $m = count($value) >> 1;

        if ($m < self::KARATSUBA_CUTOFF) {
            return self::baseSquare($value);
        }

        $x1 = array_slice($value, $m);
        $x0 = array_slice($value, 0, $m);

        $z2 = self::karatsubaSquare($x1);
        $z0 = self::karatsubaSquare($x0);

        $z1 = self::addHelper($x1, false, $x0, false);
        $z1 = self::karatsubaSquare($z1[self::VALUE]);
        $temp = self::addHelper($z2, false, $z0, false);
        $z1 = self::subtractHelper($z1, false, $temp[self::VALUE], false);

        $z2 = array_merge(array_fill(0, 2 * $m, 0), $z2);
        $z1[self::VALUE] = array_merge(array_fill(0, $m, 0), $z1[self::VALUE]);

        $xx = self::addHelper($z2, false, $z1[self::VALUE], $z1[self::SIGN]);
        $xx = self::addHelper($xx[self::VALUE], $xx[self::SIGN], $z0, false);

        return $xx[self::VALUE];
    }

    /**
     * Make the current number odd
     *
     * If the current number is odd it'll be unchanged.  If it's even, one will be added to it.
     *
     * @see self::randomPrime()
     */
    protected function make_odd(): void
    {
        $this->value[0] |= 1;
    }

    /**
     * Test the number against small primes.
     *
     * @see self::isPrime()
     */
    protected function testSmallPrimes(): bool
    {
        if ($this->value == [1]) {
            return false;
        }
        if ($this->value == [2]) {
            return true;
        }
        if (~$this->value[0] & 1) {
            return false;
        }

        $value = $this->value;
        foreach (static::PRIMES as $prime) {
            [, $r] = self::divide_digit($value, $prime);
            if (!$r) {
                return count($value) == 1 && $value[0] == $prime;
            }
        }

        return true;
    }

    /**
     * Scan for 1 and right shift by that amount
     *
     * ie. $s = gmp_scan1($n, 0) and $r = gmp_div_q($n, gmp_pow(gmp_init('2'), $s));
     *
     * @see self::isPrime()
     */
    public static function scan1divide(PHP $r): int
    {
        $r_value = &$r->value;
        for ($i = 0, $r_length = count($r_value); $i < $r_length; ++$i) {
            $temp = ~$r_value[$i] & static::MAX_DIGIT;
            for ($j = 1; ($temp >> $j) & 1; ++$j) {
            }
            if ($j <= static::BASE) {
                break;
            }
        }
        $s = static::BASE * $i + $j;
        $r->rshift($s);
        return $s;
    }

    /**
     * Performs exponentiation.
     */
    protected function powHelper(PHP $n): PHP
    {
        if ($n->compare(static::$zero[static::class]) == 0) {
            return new static(1);
        } // n^0 = 1

        $temp = clone $this;
        while (!$n->equals(static::$one[static::class])) {
            $temp = $temp->multiply($this);
            $n = $n->subtract(static::$one[static::class]);
        }

        return $temp;
    }

    /**
     * Is Odd?
     */
    public function isOdd(): bool
    {
        return (bool) ($this->value[0] & 1);
    }

    /**
     * Tests if a bit is set
     */
    public function testBit(int $x): bool
    {
        $digit = (int) floor($x / static::BASE);
        $bit = $x % static::BASE;

        if (!isset($this->value[$digit])) {
            return false;
        }

        return (bool) ($this->value[$digit] & (1 << $bit));
    }

    /**
     * Is Negative?
     */
    public function isNegative(): bool
    {
        return $this->is_negative;
    }

    /**
     * Negate
     *
     * Given $k, returns -$k
     *
     * @return static
     */
    public function negate(): PHP
    {
        $temp = clone $this;
        $temp->is_negative = !$temp->is_negative;

        return $temp;
    }

    /**
     * Bitwise Split
     *
     * Splits BigInteger's into chunks of $split bits
     *
     * @return list<static>
     */
    public function bitwise_split(int $split): array
    {
        if ($split < 1) {
            throw new InvalidArgumentException('Offset must be greater than 1');
        }

        $width = intdiv($split, static::BASE);
        if (!$width) {
            $arr = $this->bitwise_small_split($split);
            return array_map(function ($digit) {
                $temp = new static();
                $temp->value = $digit != 0 ? [$digit] : [];
                return $temp;
            }, $arr);
        }

        $vals = [];
        $val = $this->value;

        $i = $overflow = 0;
        $len = count($val);
        while ($i < $len) {
            if (!$overflow) {
                $digit = array_slice($val, $i, $width);
                $i += $width;
                $overflow = $split % static::BASE;
                if ($overflow) {
                    $mask = (1 << $overflow) - 1;
                    $temp = $val[$i] ?? 0;
                    $digit[] = $temp & $mask;
                }
            } else {
                $remaining = static::BASE - $overflow;
                $tempsplit = $split - $remaining;
                $tempwidth = intdiv($tempsplit, static::BASE + 1);
                $digit = array_slice($val, $i, $tempwidth);
                $i += $tempwidth;
                $tempoverflow = $tempsplit % static::BASE;
                if ($tempoverflow) {
                    $tempmask = (1 << $tempoverflow) - 1;
                    $temp = $val[$i] ?? 0;
                    $digit[] = $temp & $tempmask;
                }
                $newbits = 0;
                for ($j = count($digit) - 1; $j >= 0; $j--) {
                    $temp = $digit[$j] & $mask;
                    $digit[$j] = ($digit[$j] >> $overflow) | ($newbits << $remaining);
                    $newbits = $temp;
                }
                $overflow = $tempoverflow;
                $mask = $tempmask;
            }
            $temp = new static();
            $temp->value = static::trim($digit);
            $vals[] = $temp;
        }

        return array_reverse($vals);
    }

    /**
     * Bitwise Split where $split < static::BASE
     *
     * @return list<int>
     */
    private function bitwise_small_split(int $split): array
    {
        $vals = [];
        $val = $this->value;

        $mask = (1 << $split) - 1;

        $i = $overflow = 0;
        $len = count($val);
        $val[] = 0;
        $remaining = static::BASE;
        while ($i != $len) {
            $digit = $val[$i] & $mask;
            $val[$i] >>= $split;
            if (!$overflow) {
                $remaining -= $split;
                $overflow = $split <= $remaining ? 0 : $split - $remaining;

                if (!$remaining) {
                    $i++;
                    $remaining = static::BASE;
                    $overflow = 0;
                }
            } elseif (++$i != $len) {
                $tempmask = (1 << $overflow) - 1;
                $digit |= ($val[$i] & $tempmask) << $remaining;
                $val[$i] >>= $overflow;
                $remaining = static::BASE - $overflow;
                $overflow = $split <= $remaining ? 0 : $split - $remaining;
            }

            $vals[] = $digit;
        }

        if (!count($vals)) {
            return [];
        }

        // $vals is never all-zeros here: callers normalize first, and trim() strips
        // trailing zero-limbs, so the top limb is always non-zero. The count()-1 read
        // therefore can't underflow, but Psalm can't track that across the unset loop.
        /** @psalm-suppress InvalidArrayOffset */
        while ($vals[count($vals) - 1] == 0) {
            unset($vals[count($vals) - 1]);
        }

        return array_reverse($vals);
    }

    protected static function testJITOnWindows(): bool
    {
        // see https://github.com/php/php-src/issues/11917
        if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' && function_exists('opcache_get_status') && PHP_VERSION_ID < 80213 && !defined('PHPSECLIB_ALLOW_JIT')) {
            $status = opcache_get_status();
            if ($status && isset($status['jit']) && $status['jit']['enabled'] && $status['jit']['on']) {
                return true;
            }
        }
        return false;
    }

    /**
     * Return the size of a BigInteger in bits
     */
    public function getLength(): int
    {
        $max = count($this->value) - 1;
        return $max != -1 ?
            $max * static::BASE + intval(ceil(log($this->value[$max] + 1, 2))) :
            0;
    }
}
<?php

/**
 * Pure-PHP 64-bit BigInteger Engine
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2017-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Math\BigInteger\Engines;

/**
 * Pure-PHP 64-bit Engine.
 *
 * Uses 64-bit integers if int size is 8 bits
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
class PHP64 extends PHP
{
    // Constants used by PHP.php
    public const BASE = 31;
    public const BASE_FULL = 0x80000000;
    public const MAX_DIGIT = 0x7FFFFFFF;
    public const MSB = 0x40000000;

    /**
     * MAX10 in greatest MAX10LEN satisfying
     * MAX10 = 10**MAX10LEN <= 2**BASE.
     */
    public const MAX10 = 1000000000;

    /**
     * MAX10LEN in greatest MAX10LEN satisfying
     * MAX10 = 10**MAX10LEN <= 2**BASE.
     */
    public const MAX10LEN = 9;
    public const MAX_DIGIT2 = 4611686018427387904;

    /**
     * Initialize a PHP64 BigInteger Engine instance
     *
     * @see parent::initialize()
     */
    protected function initialize(int $base): void
    {
        if ($base != 256 && $base != -256) {
            parent::initialize($base);
            return;
        }

        $val = $this->value;
        $this->value = [];
        $vals = &$this->value;
        $i = strlen($val);
        if (!$i) {
            return;
        }

        while (true) {
            $i -= 4;
            if ($i < 0) {
                if ($i == -4) {
                    break;
                }
                $val = substr($val, 0, 4 + $i);
                $val = str_pad($val, 4, "\0", STR_PAD_LEFT);
                if ($val == "\0\0\0\0") {
                    break;
                }
                $i = 0;
            }
            [, $digit] = unpack('N', substr($val, $i, 4));
            $step = count($vals) & 7;
            if (!$step) {
                $digit &= static::MAX_DIGIT;
                $i++;
            } else {
                $shift = 8 - $step;
                $digit >>= $shift;
                $shift = 32 - $shift;
                $digit &= (1 << $shift) - 1;
                $temp = $i > 0 ? ord($val[$i - 1]) : 0;
                $digit |= ($temp << $shift) & 0x7F000000;
            }
            $vals[] = $digit;
        }
        while (end($vals) === 0) {
            array_pop($vals);
        }
        reset($vals);
    }

    /**
     * Test for engine validity
     *
     * @see parent::__construct()
     */
    public static function isValidEngine(): bool
    {
        return PHP_INT_SIZE >= 8 && !self::testJITOnWindows();
    }

    /**
     * Adds two BigIntegers.
     */
    public function add(PHP64 $y): PHP64
    {
        $temp = self::addHelper($this->value, $this->is_negative, $y->value, $y->is_negative);

        return $this->convertToObj($temp);
    }

    /**
     * Subtracts two BigIntegers.
     */
    public function subtract(PHP64 $y): PHP64
    {
        $temp = self::subtractHelper($this->value, $this->is_negative, $y->value, $y->is_negative);

        return $this->convertToObj($temp);
    }

    /**
     * Multiplies two BigIntegers.
     */
    public function multiply(PHP64 $y): PHP64
    {
        $temp = self::multiplyHelper($this->value, $this->is_negative, $y->value, $y->is_negative);

        return $this->convertToObj($temp);
    }

    /**
     * Divides two BigIntegers.
     *
     * Returns an array whose first element contains the quotient and whose second element contains the
     * "common residue".  If the remainder would be positive, the "common residue" and the remainder are the
     * same.  If the remainder would be negative, the "common residue" is equal to the sum of the remainder
     * and the divisor (basically, the "common residue" is the first positive modulo).
     *
     * @return array{PHP64, PHP64}
     */
    public function divide(PHP64 $y): array
    {
        return $this->divideHelper($y);
    }

    /**
     * Calculates modular inverses.
     *
     * Say you have (30 mod 17 * x mod 17) mod 17 == 1.  x can be found using modular inverses.
     */
    public function modInverse(PHP64 $n): ?PHP64
    {
        return $this->modInverseHelper($n);
    }

    /**
     * Calculates modular inverses.
     *
     * Say you have (30 mod 17 * x mod 17) mod 17 == 1.  x can be found using modular inverses.
     * @return PHP64[]
     */
    public function extendedGCD(PHP64 $n): array
    {
        return $this->extendedGCDHelper($n);
    }

    /**
     * Calculates the greatest common divisor
     *
     * Say you have 693 and 609.  The GCD is 21.
     */
    public function gcd(PHP64 $n): PHP64
    {
        return $this->extendedGCD($n)['gcd'];
    }

    /**
     * Logical And
     */
    public function bitwise_and(PHP64 $x): PHP64
    {
        return $this->bitwiseAndHelper($x);
    }

    /**
     * Logical Or
     */
    public function bitwise_or(PHP64 $x): PHP64
    {
        return $this->bitwiseOrHelper($x);
    }

    /**
     * Logical Exclusive Or
     */
    public function bitwise_xor(PHP64 $x): PHP64
    {
        return $this->bitwiseXorHelper($x);
    }

    /**
     * Compares two numbers.
     *
     * Although one might think !$x->compare($y) means $x != $y, it, in fact, means the opposite.  The reason for this is
     * demonstrated thusly:
     *
     * $x  > $y: $x->compare($y)  > 0
     * $x  < $y: $x->compare($y)  < 0
     * $x == $y: $x->compare($y) == 0
     *
     * Note how the same comparison operator is used.  If you want to test for equality, use $x->equals($y).
     *
     * {@internal Could return $this->subtract($x), but that's not as fast as what we do do.}
     *
     * @return int in case < 0 if $this is less than $y; > 0 if $this is greater than $y, and 0 if they are equal.
     * @see self::equals()
     */
    public function compare(PHP64 $y): int
    {
        return parent::compareHelper($this->value, $this->is_negative, $y->value, $y->is_negative);
    }

    /**
     * Tests the equality of two numbers.
     *
     * If you need to see if one number is greater than or less than another number, use BigInteger::compare()
     */
    public function equals(PHP64 $x): bool
    {
        return $this->value === $x->value && $this->is_negative == $x->is_negative;
    }

    /**
     * Performs modular exponentiation.
     */
    public function modPow(PHP64 $e, PHP64 $n): ?PHP64
    {
        return $this->powModOuter($e, $n);
    }

    /**
     * Performs modular exponentiation.
     *
     * Alias for modPow().
     */
    public function powMod(PHP64 $e, PHP64 $n): ?PHP64
    {
        return $this->powModOuter($e, $n);
    }

    /**
     * Generate a random prime number between a range
     *
     * If there's not a prime within the given range, false will be returned.
     */
    public static function randomRangePrime(PHP64 $min, PHP64 $max): ?PHP64
    {
        return self::randomRangePrimeOuter($min, $max);
    }

    /**
     * Generate a random number between a range
     *
     * Returns a random number between $min and $max where $min and $max
     * can be defined using one of the two methods:
     *
     * BigInteger::randomRange($min, $max)
     * BigInteger::randomRange($max, $min)
     */
    public static function randomRange(PHP64 $min, PHP64 $max): PHP64
    {
        return self::randomRangeHelper($min, $max);
    }

    /**
     * Performs exponentiation.
     */
    public function pow(PHP64 $n): PHP64
    {
        return $this->powHelper($n);
    }

    /**
     * Return the minimum BigInteger between an arbitrary number of BigIntegers.
     */
    public static function min(PHP64 ...$nums): PHP64
    {
        return self::minHelper($nums);
    }

    /**
     * Return the maximum BigInteger between an arbitrary number of BigIntegers.
     */
    public static function max(PHP64 ...$nums): PHP64
    {
        return self::maxHelper($nums);
    }

    /**
     * Tests BigInteger to see if it is between two integers, inclusive
     */
    public function between(PHP64 $min, PHP64 $max): bool
    {
        return $this->compare($min) >= 0 && $this->compare($max) <= 0;
    }
}
<?php

/**
 * GMP Modular Exponentiation Engine
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2017-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Math\BigInteger\Engines\GMP;

use phpseclib4\Math\BigInteger\Engines\GMP;

/**
 * GMP Modular Exponentiation Engine
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class DefaultEngine extends GMP
{
    /**
     * Performs modular exponentiation.
     */
    protected static function powModHelper(GMP $x, GMP $e, GMP $n): GMP
    {
        $temp = new GMP();
        $temp->value = gmp_powm($x->value, $e->value, $n->value);

        return $x->normalize($temp);
    }
}
<?php

/**
 * OpenSSL Modular Exponentiation Engine
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2017-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Math\BigInteger\Engines;

use phpseclib4\Crypt\RSA\Formats\Keys\PKCS8;
use phpseclib4\Exception\{ResourceLimitException, UnexpectedValueException};
use phpseclib4\Math\BigInteger;

/**
 * OpenSSL Modular Exponentiation Engine
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class OpenSSL
{
    /**
     * Test for engine validity
     */
    public static function isValidEngine(): bool
    {
        return extension_loaded('openssl') && static::class != __CLASS__;
    }

    /**
     * Performs modular exponentiation.
     */
    public static function powModHelper(Engine $x, Engine $e, Engine $n): Engine
    {
        if ($n->getLengthInBytes() < 31 || $n->getLengthInBytes() > 16384) {
            throw new ResourceLimitException('Only modulo between 31 and 16384 bits are accepted');
        }

        $key = PKCS8::savePublicKey(
            new BigInteger($n),
            new BigInteger($e)
        );

        $plaintext = str_pad($x->toBytes(), $n->getLengthInBytes(), "\0", STR_PAD_LEFT);

        // this is easily prone to failure. if the modulo is a multiple of 2 or 3 or whatever it
        // won't work and you'll get a "failure: error:0906D06C:PEM routines:PEM_read_bio:no start line"
        // error. i suppose, for even numbers, we could do what PHP\Montgomery.php does, but then what
        // about odd numbers divisible by 3, by 5, etc?
        if (!openssl_public_encrypt($plaintext, $result, $key, OPENSSL_NO_PADDING)) {
            throw new UnexpectedValueException(openssl_error_string());
        }

        $class = $x::class;
        return new $class($result, 256);
    }
}
<?php

/**
 * Base BigInteger Engine
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2017-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Math\BigInteger\Engines;

use phpseclib4\Common\Functions\Strings;
use phpseclib4\Exception\{BadConfigurationException, InvalidArgumentException, ResourceLimitException};

/**
 * Base Engine.
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class Engine implements \JsonSerializable
{
    public const ENGINE_DIR = '';
    public const FAST_BITWISE = false;

    /* final */ protected const PRIMES = [
        3,   5,   7,   11,  13,  17,  19,  23,  29,  31,  37,  41,  43,  47,  53,  59,
        61,  67,  71,  73,  79,  83,  89,  97,  101, 103, 107, 109, 113, 127, 131, 137,
        139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227,
        229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313,
        317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419,
        421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509,
        521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617,
        619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727,
        733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829,
        839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947,
        953, 967, 971, 977, 983, 991, 997,
    ];

    /**
     * BigInteger(0)
     *
     * @var array<class-string<static>, ?static>
     */
    protected static array $zero = [];

    /**
     * BigInteger(1)
     *
     * @var array<class-string<static>, static>
     */
    protected static array $one  = [];

    /**
     * BigInteger(2)
     *
     * @var array<class-string<static>, static>
     */
    protected static array $two = [];

    /**
     * Modular Exponentiation Engine
     *
     * @var array<class-string<static>, class-string<static>>
     */
    protected static array $modexpEngine;

    /**
     * Engine Validity Flag
     *
     * @var array<class-string<static>, bool>
     */
    protected static array $isValidEngine;

    /**
     * Holds the BigInteger's sign
     */
    protected bool $is_negative = false;

    /**
     * Precision
     *
     * @see static::setPrecision()
     */
    protected int $precision = -1;

    /**
     * Precision Bitmask
     *
     * @see static::setPrecision()
     * @var ?static
     */
    protected ?Engine $bitmask = null;

    /**
     * Default constructor
     *
     * $base is ignored if $x is an int or an instance of \GMP
     *
     * @param int|numeric-string|\GMP $x integer Base-10 number or base-$base number if $base set.
     */
    public function __construct(int|string|\GMP $x = '0', int $base = 10)
    {
        if (!array_key_exists(static::class, static::$zero)) {
            static::$zero[static::class] = null; // Placeholder to prevent infinite loop.
            static::$zero[static::class] = new static(0);
            static::$one[static::class] = new static(1);
            static::$two[static::class] = new static(2);
        }

        if (is_int($x) || $x instanceof \GMP) {
            $temp = new static("$x");
            $this->value = $temp->value;
            $this->is_negative = $temp->is_negative;
            return;
        }

        // '0' counts as empty() but when the base is 256 '0' is equal to ord('0') or 48
        // '0' is the only value like this per http://php.net/empty
        if (empty($x) && (abs($base) != 256 || $x !== '0')) {
            return;
        }

        switch ($base) {
            case -256:
            case 256:
                if ($base == -256 && (ord($x[0]) & 0x80)) {
                    $this->value = ~$x;
                    $this->is_negative = true;
                } else {
                    $this->value = $x;
                    $this->is_negative = false;
                }

                $this->initialize($base);

                if ($this->is_negative) {
                    $temp = $this->add(new static('-1'));
                    $this->value = $temp->value;
                }
                break;
            case -16:
            case 16:
                if ($base > 0 && $x[0] == '-') {
                    $this->is_negative = true;
                    $x = substr($x, 1);
                }

                $x = preg_replace('#^(?:0x)?([A-Fa-f0-9]*).*#s', '$1', $x);

                $is_negative = false;
                if ($base < 0 && hexdec($x[0]) >= 8) {
                    $this->is_negative = $is_negative = true;
                    $x = Strings::bin2hex(~Strings::hex2bin($x));
                }

                $this->value = $x;
                $this->initialize($base);

                if ($is_negative) {
                    $temp = $this->add(new static('-1'));
                    $this->value = $temp->value;
                }
                break;
            case -10:
            case 10:
                // (?<!^)(?:-).*: find any -'s that aren't at the beginning and then any characters that follow that
                // (?<=^|-)0*: find any 0's that are preceded by the start of the string or by a - (ie. octals)
                // [^-0-9].*: find any non-numeric characters and then any characters that follow that
                $this->value = preg_replace('#(?<!^)-.*|(?<=^|-)0*|[^-0-9].*#s', '', (string) $x);
                if (!strlen($this->value) || $this->value == '-') {
                    $this->value = '0';
                }
                $this->initialize($base);
                break;
            case -2:
            case 2:
                if ($base > 0 && $x[0] == '-') {
                    $this->is_negative = true;
                    $x = substr($x, 1);
                }

                $x = preg_replace('#^([01]*).*#s', '$1', $x);

                $temp = new static(Strings::bits2bin($x), 128 * $base); // ie. either -16 or +16
                $this->value = $temp->value;
                if ($temp->is_negative) {
                    $this->is_negative = true;
                }

                break;
            default:
                // base not supported, so we'll let $this == 0
        }
    }

    /**
     * Sets engine type.
     *
     * Throws an exception if the type is invalid
     */
    public static function setModExpEngine(string $engine): void
    {
        $fqengine = '\\phpseclib4\\Math\\BigInteger\\Engines\\' . static::ENGINE_DIR . '\\' . $engine;
        if (!class_exists($fqengine) || !method_exists($fqengine, 'isValidEngine')) {
            throw new BadConfigurationException("$engine is not a valid engine");
        }
        if (!$fqengine::isValidEngine()) {
            throw new BadConfigurationException("$engine is not setup correctly on this system");
        }
        static::$modexpEngine[static::class] = $fqengine;
    }

    /**
     * Converts a BigInteger to a byte string (eg. base-256).
     *
     * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're
     * saved as two's compliment.
     */
    protected function toBytesHelper(): string
    {
        $comparison = $this->compare(new static());
        if ($comparison == 0) {
            return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
        }

        $temp = $comparison < 0 ? $this->add(new static(1)) : $this;
        $bytes = $temp->toBytes();

        if (!strlen($bytes)) { // eg. if the number we're trying to convert is -1
            $bytes = chr(0);
        }

        if (ord($bytes[0]) & 0x80) {
            $bytes = chr(0) . $bytes;
        }

        return $comparison < 0 ? ~$bytes : $bytes;
    }

    /**
     * Converts a BigInteger to a hex string (eg. base-16).
     */
    public function toHex(bool $twos_compliment = false): string
    {
        return Strings::bin2hex($this->toBytes($twos_compliment));
    }

    /**
     * Converts a BigInteger to a bit string (eg. base-2).
     *
     * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're
     * saved as two's compliment.
     */
    public function toBits(bool $twos_compliment = false): string
    {
        $hex = $this->toBytes($twos_compliment);
        $bits = Strings::bin2bits($hex);

        $result = $this->precision > 0 ? substr($bits, -$this->precision) : ltrim($bits, '0');

        if ($twos_compliment && $this->compare(new static()) > 0 && $this->precision <= 0) {
            return '0' . $result;
        }

        return $result;
    }

    /**
     * Calculates modular inverses.
     *
     * Say you have (30 mod 17 * x mod 17) mod 17 == 1.  x can be found using modular inverses.
     *
     * {@internal See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=21 HAC 14.64} for more information.}
     */
    protected function modInverseHelper(Engine $n): ?Engine
    {
        // $x mod -$n == $x mod $n.
        $n = $n->abs();

        if ($this->compare(static::$zero[static::class]) < 0) {
            $temp = $this->abs();
            $temp = $temp->modInverse($n);
            return $this->normalize($n->subtract($temp));
        }

        ['gcd' => $gcd, 'x' => $x] = $this->extendedGCD($n);

        if (!$gcd->equals(static::$one[static::class])) {
            return null;
        }

        $x = $x->compare(static::$zero[static::class]) < 0 ? $x->add($n) : $x;

        return $this->compare(static::$zero[static::class]) < 0 ? $this->normalize($n->subtract($x)) : $this->normalize($x);
    }

    /**
     *  __serialize() magic method
     *
     * __sleep / __wakeup were depreciated in PHP 8.5
     * Will be called, automatically, when serialize() is called on a Math_BigInteger object.
     *
     * @see self::__unserialize()
     */
    public function __serialize(): array
    {
        $result = ['hex' => $this->toHex(true)];
        if ($this->precision > 0) {
            $result['precision'] = $this->precision;
        }
        return $result;
    }

    /**
     *  __unserialize() magic method
     *
     * __sleep / __wakeup were depreciated in PHP 8.5
     * Will be called, automatically, when unserialize() is called on a Math_BigInteger object.
     *
     * @see self::__serialize()
     */
    public function __unserialize(array $data): void
    {
        $temp = new static($data['hex'], -16);
        $this->value = $temp->value;
        $this->is_negative = $temp->is_negative;
        if (isset($data['precision']) && $data['precision'] > 0) {
            // recalculate $this->bitmask
            $this->setPrecision($data['precision']);
        }
    }

    /**
     * JSON Serialize
     *
     * Will be called, automatically, when json_encode() is called on a BigInteger object.
     *
     * @return array{hex: string, precision?: int]
     */
    public function jsonSerialize(): mixed
    {
        $result = ['hex' => $this->toHex(true)];
        if ($this->precision > 0) {
            $result['precision'] = $this->precision;
        }
        return $result;
    }

    /**
     * Converts a BigInteger to a base-10 number.
     */
    public function __toString(): string
    {
        return $this->toString();
    }

    /**
     *  __debugInfo() magic method
     *
     * Will be called, automatically, when print_r() or var_dump() are called
     */
    public function __debugInfo(): array
    {
        $result = [
            'value' => '0x' . $this->toHex(true),
            'engine' => basename(static::class),
        ];
        return $this->precision > 0 ? $result + ['precision' => $this->precision] : $result;
    }

    /**
     * Set Precision
     *
     * Some bitwise operations give different results depending on the precision being used.  Examples include left
     * shift, not, and rotates.
     */
    public function setPrecision(int $bits): void
    {
        if ($bits < 1) {
            $this->precision = -1;
            $this->bitmask = null;

            return;
        }
        $this->precision = $bits;
        $this->bitmask = static::setBitmask($bits);

        $temp = $this->normalize($this);
        $this->value = $temp->value;
    }

    /**
     * Get Precision
     *
     * Returns the precision if it exists, -1 if it doesn't
     */
    public function getPrecision(): int
    {
        return $this->precision;
    }

    /**
     * Set Bitmask
     *
     * @return static
     * @see self::setPrecision()
     */
    protected static function setBitmask(int $bits): Engine
    {
        return new static(chr((1 << ($bits & 0x7)) - 1) . str_repeat(chr(0xFF), $bits >> 3), 256);
    }

    /**
     * Logical Not
     */
    public function bitwise_not(): Engine
    {
        // calculuate "not" without regard to $this->precision
        // (will always result in a smaller number.  ie. ~1 isn't 1111 1110 - it's 0)
        $temp = $this->toBytes();
        if ($temp == '') {
            return $this->normalize(static::$zero[static::class]);
        }
        $pre_msb = decbin(ord($temp[0]));
        $temp = ~$temp;
        $msb = decbin(ord($temp[0]));
        if (strlen($msb) == 8) {
            $msb = substr($msb, strpos($msb, '0'));
        }
        $temp[0] = chr(bindec($msb));

        // see if we need to add extra leading 1's
        $current_bits = strlen($pre_msb) + 8 * strlen($temp) - 8;
        $new_bits = $this->precision - $current_bits;
        if ($new_bits <= 0) {
            return $this->normalize(new static($temp, 256));
        }

        // generate as many leading 1's as we need to.
        $leading_ones = chr((1 << ($new_bits & 0x7)) - 1) . str_repeat(chr(0xFF), $new_bits >> 3);

        self::base256_lshift($leading_ones, $current_bits);

        $temp = str_pad($temp, strlen($leading_ones), chr(0), STR_PAD_LEFT);

        return $this->normalize(new static($leading_ones | $temp, 256));
    }

    /**
     * Logical Left Shift
     *
     * Shifts binary strings $shift bits, essentially multiplying by 2**$shift.
     */
    protected static function base256_lshift(string &$x, int $shift): void
    {
        if ($shift == 0) {
            return;
        }

        $num_bytes = $shift >> 3; // eg. floor($shift/8)
        $shift &= 7; // eg. $shift % 8

        $carry = 0;
        for ($i = strlen($x) - 1; $i >= 0; --$i) {
            $temp = (ord($x[$i]) << $shift) | $carry;
            $x[$i] = chr($temp & 0xFF);
            $carry = $temp >> 8;
        }
        $carry = ($carry != 0) ? chr($carry) : '';
        $x = $carry . $x . str_repeat(chr(0), $num_bytes);
    }

    /**
     * Logical Left Rotate
     *
     * Instead of the top x bits being dropped they're appended to the shifted bit string.
     */
    public function bitwise_leftRotate(int $shift): Engine
    {
        $bits = $this->toBytes();

        if ($this->precision > 0) {
            $precision = $this->precision;
            if (static::FAST_BITWISE) {
                $mask = $this->bitmask->toBytes();
            } else {
                $mask = $this->bitmask->subtract(new static(1));
                $mask = $mask->toBytes();
            }
        } else {
            $temp = ord($bits[0]);
            for ($i = 0; $temp >> $i; ++$i) {
            }
            $precision = 8 * strlen($bits) - 8 + $i;
            $mask = chr((1 << ($precision & 0x7)) - 1) . str_repeat(chr(0xFF), $precision >> 3);
        }

        if ($shift < 0) {
            $shift += $precision;
        }
        $shift %= $precision;

        if (!$shift) {
            return clone $this;
        }

        $left = $this->bitwise_leftShift($shift);
        $left = $left->bitwise_and(new static($mask, 256));
        $right = $this->bitwise_rightShift($precision - $shift);
        $result = static::FAST_BITWISE ? $left->bitwise_or($right) : $left->add($right);
        return $this->normalize($result);
    }

    /**
     * Logical Right Rotate
     *
     * Instead of the bottom x bits being dropped they're prepended to the shifted bit string.
     */
    public function bitwise_rightRotate(int $shift): Engine
    {
        return $this->bitwise_leftRotate(-$shift);
    }

    /**
     * Returns the smallest and largest n-bit number
     *
     * @return array{min: static, max: static}
     */
    public static function minMaxBits(int $bits): array
    {
        $bytes = $bits >> 3;
        $min = str_repeat(chr(0), $bytes);
        $max = str_repeat(chr(0xFF), $bytes);
        $msb = $bits & 7;
        if ($msb) {
            $min = chr(1 << ($msb - 1)) . $min;
            $max = chr((1 << $msb) - 1) . $max;
        } else {
            $min[0] = chr(0x80);
        }
        return [
            'min' => new static($min, 256),
            'max' => new static($max, 256),
        ];
    }

    /**
     * Return the size of a BigInteger in bits
     */
    public function getLength(): int
    {
        return strlen($this->toBits());
    }

    /**
     * Return the size of a BigInteger in bytes
     */
    public function getLengthInBytes(): int
    {
        return (int) ceil($this->getLength() / 8);
    }

    /**
     * Performs some pre-processing for powMod
     */
    protected function powModOuter(Engine $e, Engine $n): ?Engine
    {
        $n = isset($this->bitmask) && $this->bitmask->compare($n) < 0 ? $this->bitmask : $n->abs();

        if ($e->compare(new static()) < 0) {
            $e = $e->abs();

            $temp = $this->modInverse($n);
            if ($temp === null) {
                return null;
            }

            return $this->normalize($temp->powModInner($e, $n));
        }

        if ($this->compare($n) > 0 || $this->isNegative()) {
            [, $temp] = $this->divide($n);
            return $temp->powModInner($e, $n);
        }

        return $this->powModInner($e, $n);
    }

    /**
     * Sliding Window k-ary Modular Exponentiation
     *
     * Based on {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=27 HAC 14.85} /
     * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=210 MPM 7.7}.  In a departure from those algorithims,
     * however, this function performs a modular reduction after every multiplication and squaring operation.
     * As such, this function has the same preconditions that the reductions being used do.
     *
     * @template T of Engine
     * @param class-string<T> $class
     * @return T
     */
    protected static function slidingWindow(Engine $x, Engine $e, Engine $n, string $class): Engine
    {
        static $window_ranges = [7, 25, 81, 241, 673, 1793]; // from BigInteger.java's oddModPow function
        //static $window_ranges = [0, 7, 36, 140, 450, 1303, 3529]; // from MPM 7.3.1

        $e_bits = $e->toBits();
        $e_length = strlen($e_bits);

        // calculate the appropriate window size.
        // $window_size == 3 if $window_ranges is between 25 and 81, for example.
        for ($i = 0, $window_size = 1; $i < count($window_ranges) && $e_length > $window_ranges[$i]; ++$window_size, ++$i) {
        }

        $n_value = $n->value;

        if (method_exists(static::class, 'generateCustomReduction')) {
            static::generateCustomReduction($n, $class);
        }

        // precompute $this^0 through $this^$window_size
        $powers = [];
        $powers[1] = static::prepareReduce($x->value, $n_value, $class);
        $powers[2] = static::squareReduce($powers[1], $n_value, $class);

        // we do every other number since substr($e_bits, $i, $j+1) (see below) is supposed to end
        // in a 1.  ie. it's supposed to be odd.
        $temp = 1 << ($window_size - 1);
        for ($i = 1; $i < $temp; ++$i) {
            $i2 = $i << 1;
            $powers[$i2 + 1] = static::multiplyReduce($powers[$i2 - 1], $powers[2], $n_value, $class);
        }

        $result = new $class(1);
        $result = static::prepareReduce($result->value, $n_value, $class);

        for ($i = 0; $i < $e_length;) {
            if (!$e_bits[$i]) {
                $result = static::squareReduce($result, $n_value, $class);
                ++$i;
            } else {
                for ($j = $window_size - 1; $j > 0; --$j) {
                    if (!empty($e_bits[$i + $j])) {
                        break;
                    }
                }

                // eg. the length of substr($e_bits, $i, $j + 1)
                for ($k = 0; $k <= $j; ++$k) {
                    $result = static::squareReduce($result, $n_value, $class);
                }

                $result = static::multiplyReduce($result, $powers[bindec(substr($e_bits, $i, $j + 1))], $n_value, $class);

                $i += $j + 1;
            }
        }

        $temp = new $class();
        /** @psalm-suppress UndefinedPropertyAssignment */
        $temp->value = static::reduce($result, $n_value, $class);

        return $temp;
    }

    /**
     * Generates a random number of a certain size
     *
     * Bit length is equal to $size
     */
    public static function random(int $size): Engine
    {
        ['min' => $min, 'max' => $max] = static::minMaxBits($size);
        return static::randomRange($min, $max);
    }

    /**
     * Generates a random prime number of a certain size
     *
     * Bit length is equal to $size
     */
    public static function randomPrime(int $size): Engine
    {
        ['min' => $min, 'max' => $max] = static::minMaxBits($size);
        return static::randomRangePrime($min, $max);
    }

    /**
     * Performs some pre-processing for randomRangePrime
     */
    protected static function randomRangePrimeOuter(Engine $min, Engine $max): ?Engine
    {
        $compare = $max->compare($min);

        if (!$compare) {
            return $min->isPrime() ? $min : null;
        } elseif ($compare < 0) {
            // if $min is bigger then $max, swap $min and $max
            $temp = $max;
            $max = $min;
            $min = $temp;
        }

        $length = $max->getLength();
        if ($length > 8196) {
            throw new ResourceLimitException("Generation of random prime numbers larger than 8196 has been disabled ($length)");
        }

        $x = static::randomRange($min, $max);

        return static::randomRangePrimeInner($x, $min, $max);
    }

    /**
     * Generate a random number between a range
     *
     * Returns a random number between $min and $max where $min and $max
     * can be defined using one of the two methods:
     *
     * BigInteger::randomRange($min, $max)
     * BigInteger::randomRange($max, $min)
     */
    protected static function randomRangeHelper(Engine $min, Engine $max): Engine
    {
        $compare = $max->compare($min);

        if (!$compare) {
            return $min;
        } elseif ($compare < 0) {
            // if $min is bigger then $max, swap $min and $max
            $temp = $max;
            $max = $min;
            $min = $temp;
        }

        if (!isset(static::$one[static::class])) {
            static::$one[static::class] = new static(1);
        }

        $max = $max->subtract($min->subtract(static::$one[static::class]));

        $size = strlen(ltrim($max->toBytes(), chr(0)));

        /*
            doing $random % $max doesn't work because some numbers will be more likely to occur than others.
            eg. if $max is 140 and $random's max is 255 then that'd mean both $random = 5 and $random = 145
            would produce 5 whereas the only value of random that could produce 139 would be 139. ie.
            not all numbers would be equally likely. some would be more likely than others.

            creating a whole new random number until you find one that is within the range doesn't work
            because, for sufficiently small ranges, the likelihood that you'd get a number within that range
            would be pretty small. eg. with $random's max being 255 and if your $max being 1 the probability
            would be pretty high that $random would be greater than $max.

            phpseclib works around this using the technique described here:

            http://crypto.stackexchange.com/questions/5708/creating-a-small-number-from-a-cryptographically-secure-random-string
        */
        $random_max = new static(chr(1) . str_repeat("\0", $size), 256);
        $random = new static($size ? random_bytes($size) : '', 256);

        [$max_multiple] = $random_max->divide($max);
        $max_multiple = $max_multiple->multiply($max);

        while ($random->compare($max_multiple) >= 0) {
            $random = $random->subtract($max_multiple);
            $random_max = $random_max->subtract($max_multiple);
            $random = $random->bitwise_leftShift(8);
            $random = $random->add(new static(random_bytes(1), 256));
            $random_max = $random_max->bitwise_leftShift(8);
            [$max_multiple] = $random_max->divide($max);
            $max_multiple = $max_multiple->multiply($max);
        }
        [, $random] = $random->divide($max);

        return $random->add($min);
    }

    /**
     * Performs some post-processing for randomRangePrime
     */
    protected static function randomRangePrimeInner(Engine $x, Engine $min, Engine $max): ?Engine
    {
        if (!isset(static::$two[static::class])) {
            static::$two[static::class] = new static('2');
        }

        $x->make_odd();
        if ($x->compare($max) > 0) {
            // if $x > $max then $max is even and if $min == $max then no prime number exists between the specified range
            if ($min->equals($max)) {
                return null;
            }
            $x = clone $min;
            $x->make_odd();
        }

        $initial_x = clone $x;

        while (true) {
            if ($x->isPrime()) {
                return $x;
            }

            $x = $x->add(static::$two[static::class]);

            if ($x->compare($max) > 0) {
                $x = clone $min;
                if ($x->equals(static::$two[static::class])) {
                    return $x;
                }
                $x->make_odd();
            }

            if ($x->equals($initial_x)) {
                return null;
            }
        }
    }

    /**
     * Sets the $t parameter for primality testing
     */
    protected function setupIsPrime(): int
    {
        $length = $this->getLengthInBytes();

        // see HAC 4.49 "Note (controlling the error probability)"
        return match (true) {
            $length >= 163 =>  2, // floor(1300 / 8)
            $length >= 106 =>  3, // floor( 850 / 8)
            $length >= 81  =>  4, // floor( 650 / 8)
            $length >= 68  =>  5, // floor( 550 / 8)
            $length >= 56  =>  6, // floor( 450 / 8)
            $length >= 50  =>  7, // floor( 400 / 8)
            $length >= 43  =>  8, // floor( 350 / 8)
            $length >= 37  =>  9, // floor( 300 / 8)
            $length >= 31  => 12, // floor( 250 / 8)
            $length >= 25  => 15, // floor( 200 / 8)
            $length >= 18  => 18, // floor( 150 / 8)
            default        => 27,
        };
    }

    /**
     * Tests Primality
     *
     * Uses the {@link http://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test Miller-Rabin primality test}.
     * See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap4.pdf#page=8 HAC 4.24} for more info.
     */
    protected function testPrimality(int $t): bool
    {
        if (!$this->testSmallPrimes()) {
            return false;
        }

        $n   = clone $this;
        $n_1 = $n->subtract(static::$one[static::class]);
        $n_2 = $n->subtract(static::$two[static::class]);

        $r = clone $n_1;
        $s = static::scan1divide($r);

        for ($i = 0; $i < $t; ++$i) {
            $a = static::randomRange(static::$two[static::class], $n_2);
            $y = $a->modPow($r, $n);

            if (!$y->equals(static::$one[static::class]) && !$y->equals($n_1)) {
                for ($j = 1; $j < $s && !$y->equals($n_1); ++$j) {
                    $y = $y->modPow(static::$two[static::class], $n);
                    if ($y->equals(static::$one[static::class])) {
                        return false;
                    }
                }

                if (!$y->equals($n_1)) {
                    return false;
                }
            }
        }

        return true;
    }

    /**
     * Checks a numer to see if it's prime
     *
     * Assuming the $t parameter is not set, this function has an error rate of 2**-80.  The main motivation for the
     * $t parameter is distributability.  BigInteger::randomPrime() can be distributed across multiple pageloads
     * on a website instead of just one.
     */
    public function isPrime(?int $t = null): bool
    {
        // OpenSSL limits RSA keys to 16384 bits. The length of an RSA key is equal to the length of the modulo, which is
        // produced by multiplying the primes p and q by one another. The largest number two 8196 bit primes can produce is
        // a 16384 bit number so, basically, 8196 bit primes are the largest OpenSSL will generate and if that's the largest
        // that it'll generate it also stands to reason that that's the largest you'll be able to test primality on
        $length = $this->getLength();
        if ($length > 8196) {
            throw new ResourceLimitException("Primality testing is not supported for numbers larger than 8196 bits ($length)");
        }

        if (!isset($t)) {
            $t = $this->setupIsPrime();
        }
        return $this->testPrimality($t);
    }

    /**
     * Performs a few preliminary checks on root
     */
    protected function rootHelper(int $n): Engine
    {
        if ($n < 1) {
            return clone static::$zero[static::class];
        } // we want positive exponents
        if ($this->compare(static::$one[static::class]) < 0) {
            return clone static::$zero[static::class];
        } // we want positive numbers
        if ($this->compare(static::$two[static::class]) < 0) {
            return clone static::$one[static::class];
        } // n-th root of 1 or 2 is 1

        return $this->rootInner($n);
    }

    /**
     * Calculates the nth root of a biginteger.
     *
     * Returns the nth root of a positive biginteger, where n defaults to 2
     *
     * {@internal This function is based off of {@link http://mathforum.org/library/drmath/view/52605.html this page} and {@link http://stackoverflow.com/questions/11242920/calculating-nth-root-with-bcmath-in-php this stackoverflow question}.}
     */
    protected function rootInner(int $n): Engine
    {
        $n = new static($n);

        // g is our guess number
        $g = static::$two[static::class];
        // while (g^n < num) g=g*2
        while ($g->pow($n)->compare($this) < 0) {
            $g = $g->multiply(static::$two[static::class]);
        }
        // if (g^n==num) num is a power of 2, we're lucky, end of job
        // == 0 bccomp(bcpow($g, $n), $n->value)==0
        if ($g->pow($n)->equals($this) > 0) {
            $root = $g;
            return $this->normalize($root);
        }

        // if we're here num wasn't a power of 2 :(
        $og = $g; // og means original guess and here is our upper bound
        $g = $g->divide(static::$two[static::class])[0]; // g is set to be our lower bound
        $step = $og->subtract($g)->divide(static::$two[static::class])[0]; // step is the half of upper bound - lower bound
        $g = $g->add($step); // we start at lower bound + step , basically in the middle of our interval

        // while step>1

        while ($step->compare(static::$one[static::class]) == 1) {
            $guess = $g->pow($n);
            $step = $step->divide(static::$two[static::class])[0];
            $comp = $guess->compare($this); // compare our guess with real number
            switch ($comp) {
                case -1: // if guess is lower we add the new step
                    $g = $g->add($step);
                    break;
                case 1: // if guess is higher we sub the new step
                    $g = $g->subtract($step);
                    break;
                case 0: // if guess is exactly the num we're done, we return the value
                    break 2;
            }
        }

        if ($comp == 1) {
            $g = $g->subtract($step);
        }

        // whatever happened, g is the closest guess we can make so return it
        $root = $g;

        return $this->normalize($root);
    }

    /**
     * Calculates the nth root of a biginteger.
     */
    public function root(int $n = 2): Engine
    {
        return $this->rootHelper($n);
    }

    /**
     * Return the minimum BigInteger between an arbitrary number of BigIntegers.
     */
    protected static function minHelper(array $nums): Engine
    {
        if (count($nums) == 1) {
            return $nums[0];
        }
        $min = $nums[0];
        for ($i = 1; $i < count($nums); $i++) {
            $min = $min->compare($nums[$i]) > 0 ? $nums[$i] : $min;
        }
        return $min;
    }

    /**
     * Return the minimum BigInteger between an arbitrary number of BigIntegers.
     */
    protected static function maxHelper(array $nums): Engine
    {
        if (count($nums) == 1) {
            return $nums[0];
        }
        $max = $nums[0];
        for ($i = 1; $i < count($nums); $i++) {
            $max = $max->compare($nums[$i]) < 0 ? $nums[$i] : $max;
        }
        return $max;
    }

    /**
     * Create Recurring Modulo Function
     *
     * Sometimes it may be desirable to do repeated modulos with the same number outside of
     * modular exponentiation
     */
    public function createRecurringModuloFunction(): \Closure
    {
        $class = static::class;

        $fqengine = !method_exists(static::$modexpEngine[static::class], 'reduce') ?
            '\\phpseclib4\\Math\\BigInteger\\Engines\\' . static::ENGINE_DIR . '\\DefaultEngine' :
            static::$modexpEngine[static::class];
        if (method_exists($fqengine, 'generateCustomReduction')) {
            $func = $fqengine::generateCustomReduction($this, static::class);
            return eval('return function(' . static::class . ' $x) use ($func, $class) {
                $r = new $class();
                $r->value = $func($x->value);
                return $r;
            };');
        }
        /** @psalm-suppress UndefinedThisPropertyFetch */
        $n = $this->value;
        return eval('return function(' . static::class . ' $x) use ($n, $fqengine, $class) {
            $r = new $class();
            $r->value = $fqengine::reduce($x->value, $n, $class);
            return $r;
        };');
    }

    /**
     * Calculates the greatest common divisor and Bezout's identity.
     *
     * @return array{gcd: Engine, x: Engine, y: Engine}
     */
    protected function extendedGCDHelper(Engine $n): array
    {
        $u = clone $this;
        $v = clone $n;

        $one = new static(1);
        $zero = new static();

        $a = clone $one;
        $b = clone $zero;
        $c = clone $zero;
        $d = clone $one;

        while (!$v->equals($zero)) {
            [$q] = $u->divide($v);

            $temp = $u;
            $u = $v;
            $v = $temp->subtract($v->multiply($q));

            $temp = $a;
            $a = $c;
            $c = $temp->subtract($a->multiply($q));

            $temp = $b;
            $b = $d;
            $d = $temp->subtract($b->multiply($q));
        }

        return [
            'gcd' => $u,
            'x' => $a,
            'y' => $b,
        ];
    }

    /**
     * Bitwise Split
     *
     * Splits BigInteger's into chunks of $split bits
     *
     * @return Engine[]
     */
    public function bitwise_split(int $split): array
    {
        if ($split < 1) {
            throw new InvalidArgumentException('Offset must be greater than 1');
        }

        $mask = static::$one[static::class]->bitwise_leftShift($split)->subtract(static::$one[static::class]);

        $num = clone $this;

        $vals = [];
        while (!$num->equals(static::$zero[static::class])) {
            $vals[] = $num->bitwise_and($mask);
            $num = $num->bitwise_rightShift($split);
        }

        return array_reverse($vals);
    }

    /**
     * Logical And
     */
    protected function bitwiseAndHelper(Engine $x): Engine
    {
        $left = $this->toBytes(true);
        $right = $x->toBytes(true);

        $length = max(strlen($left), strlen($right));

        $left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
        $right = str_pad($right, $length, chr(0), STR_PAD_LEFT);

        return $this->normalize(new static($left & $right, -256));
    }

    /**
     * Logical Or
     */
    protected function bitwiseOrHelper(Engine $x): Engine
    {
        $left = $this->toBytes(true);
        $right = $x->toBytes(true);

        $length = max(strlen($left), strlen($right));

        $left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
        $right = str_pad($right, $length, chr(0), STR_PAD_LEFT);

        return $this->normalize(new static($left | $right, -256));
    }

    /**
     * Logical Exclusive Or
     */
    protected function bitwiseXorHelper(Engine $x): Engine
    {
        $left = $this->toBytes(true);
        $right = $x->toBytes(true);

        $length = max(strlen($left), strlen($right));


        $left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
        $right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
        return $this->normalize(new static($left ^ $right, -256));
    }
}
<?php

/**
 * BCMath BigInteger Engine
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2017-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Math\BigInteger\Engines;

use phpseclib4\Common\Functions\Strings;
use phpseclib4\Exception\BadConfigurationException;

/**
 * BCMath Engine.
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
class BCMath extends Engine
{
    /**
     * Can Bitwise operations be done fast?
     *
     * @see parent::bitwise_leftRotate()
     * @see parent::bitwise_rightRotate()
     */
    public const FAST_BITWISE = false;

    /**
     * Engine Directory
     *
     * @see parent::setModExpEngine
     */
    public const ENGINE_DIR = 'BCMath';

    /**
     * Holds the BigInteger's value
     */
    protected string $value;

    /**
     * Test for engine validity
     *
     * @see parent::__construct()
     */
    public static function isValidEngine(): bool
    {
        return extension_loaded('bcmath');
    }

    /**
     * Default constructor
     *
     * @param mixed $x integer Base-10 number or base-$base number if $base set.
     * @see parent::__construct()
     */
    public function __construct(int|string|\GMP $x = 0, int $base = 10)
    {
        if (!isset(static::$isValidEngine[static::class])) {
            static::$isValidEngine[static::class] = self::isValidEngine();
        }
        if (!static::$isValidEngine[static::class]) {
            throw new BadConfigurationException('BCMath is not setup correctly on this system');
        }

        $this->value = '0';

        parent::__construct($x, $base);
    }

    /**
     * Initialize a BCMath BigInteger Engine instance
     *
     * @see parent::__construct()
     */
    protected function initialize(int $base): void
    {
        switch (abs($base)) {
            case 256:
                // round $len to the nearest 4
                $len = (strlen($this->value) + 3) & ~3;

                $x = str_pad($this->value, $len, chr(0), STR_PAD_LEFT);

                $this->value = '0';
                for ($i = 0; $i < $len; $i += 4) {
                    $this->value = bcmul($this->value, '4294967296', 0); // 4294967296 == 2**32
                    $temp = sprintf('%u', unpack('N', substr($x, $i, 4))[1]);
                    $this->value = bcadd($this->value, $temp, 0);
                }

                if ($this->is_negative) {
                    $this->value = '-' . $this->value;
                }
                break;
            case 16:
                $x = (strlen($this->value) & 1) ? '0' . $this->value : $this->value;
                $temp = new static(Strings::hex2bin($x), 256);
                $this->value = $this->is_negative ? '-' . $temp->value : $temp->value;
                $this->is_negative = false;
                break;
            case 10:
                // explicitly casting $x to a string is necessary, here, since doing $x[0] on -1 yields different
                // results then doing it on '-1' does (modInverse does $x[0])
                $this->value = $this->value === '-' ? '0' : (string) $this->value;
        }
    }

    /**
     * Converts a BigInteger to a base-10 number.
     */
    public function toString(): string
    {
        if ($this->value === '0') {
            return '0';
        }

        return ltrim($this->value, '0');
    }

    /**
     * Converts a BigInteger to a byte string (eg. base-256).
     */
    public function toBytes(bool $twos_compliment = false): string
    {
        if ($twos_compliment) {
            return $this->toBytesHelper();
        }

        $value = '';
        $current = $this->value;

        if ($current[0] == '-') {
            $current = substr($current, 1);
        }

        while (bccomp($current, '0', 0) > 0) {
            $quotient = bcdiv($current, '4294967296', 0);
            $remainder = bcsub($current, bcmul($quotient, '4294967296', 0), 0);
            $temp = (int) ($remainder / 256); // ie. >> 8
            $value = chr($temp >> 16) . // ie. $remainder >> 24
                chr(($temp >> 8) & 0xFF) . // ie. $remainder >> 16
                chr($temp & 0xFF) . // i. $remainder >> 8
                //chr((int) ($remainder - 256 * $temp)) .
                chr((int) fmod(floatval($remainder), 256)) .
                $value;
            $current = $quotient;
        }

        return $this->precision > 0 ?
            substr(str_pad($value, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) :
            ltrim($value, chr(0));
    }

    /**
     * Adds two BigIntegers.
     */
    public function add(BCMath $y): static
    {
        $temp = new static();
        $temp->value = bcadd($this->value, $y->value, 0);

        return $this->normalize($temp);
    }

    /**
     * Subtracts two BigIntegers.
     */
    public function subtract(BCMath $y): static
    {
        $temp = new static();
        $temp->value = bcsub($this->value, $y->value, 0);

        return $this->normalize($temp);
    }

    /**
     * Multiplies two BigIntegers.
     */
    public function multiply(BCMath $x): static
    {
        $temp = new static();
        $temp->value = bcmul($this->value, $x->value, 0);

        return $this->normalize($temp);
    }

    /**
     * Divides two BigIntegers.
     *
     * Returns an array whose first element contains the quotient and whose second element contains the
     * "common residue".  If the remainder would be positive, the "common residue" and the remainder are the
     * same.  If the remainder would be negative, the "common residue" is equal to the sum of the remainder
     * and the divisor (basically, the "common residue" is the first positive modulo).
     *
     * @return array{static, static}
     */
    public function divide(BCMath $y): array
    {
        $quotient = new static();
        $remainder = new static();

        $quotient->value = bcdiv($this->value, $y->value, 0);
        $remainder->value = bcmod($this->value, $y->value, 0);

        if ($remainder->value[0] == '-') {
            $remainder->value = bcadd($remainder->value, $y->value[0] == '-' ? substr($y->value, 1) : $y->value, 0);
        }

        return [$this->normalize($quotient), $this->normalize($remainder)];
    }

    /**
     * Calculates modular inverses.
     *
     * Say you have (30 mod 17 * x mod 17) mod 17 == 1.  x can be found using modular inverses.
     */
    public function modInverse(BCMath $n): ?BCMath
    {
        return $this->modInverseHelper($n);
    }

    /**
     * Calculates the greatest common divisor and Bezout's identity.
     *
     * Say you have 693 and 609.  The GCD is 21.  Bezout's identity states that there exist integers x and y such that
     * 693*x + 609*y == 21.  In point of fact, there are actually an infinite number of x and y combinations and which
     * combination is returned is dependent upon which mode is in use.  See
     * {@link http://en.wikipedia.org/wiki/B%C3%A9zout%27s_identity Bezout's identity - Wikipedia} for more information.
     *
     * @return array{gcd: static, x: static, y: static}
     */
    public function extendedGCD(BCMath $n): array
    {
        // it might be faster to use the binary xGCD algorithim here, as well, but (1) that algorithim works
        // best when the base is a power of 2 and (2) i don't think it'd make much difference, anyway.  as is,
        // the basic extended euclidean algorithim is what we're using.

        $u = $this->value;
        $v = $n->value;

        $a = '1';
        $b = '0';
        $c = '0';
        $d = '1';

        while (bccomp($v, '0', 0) != 0) {
            $q = bcdiv($u, $v, 0);

            $temp = $u;
            $u = $v;
            $v = bcsub($temp, bcmul($v, $q, 0), 0);

            $temp = $a;
            $a = $c;
            $c = bcsub($temp, bcmul($a, $q, 0), 0);

            $temp = $b;
            $b = $d;
            $d = bcsub($temp, bcmul($b, $q, 0), 0);
        }

        return [
            'gcd' => $this->normalize(new static($u)),
            'x' => $this->normalize(new static($a)),
            'y' => $this->normalize(new static($b)),
        ];
    }

    /**
     * Calculates the greatest common divisor
     *
     * Say you have 693 and 609.  The GCD is 21.
     */
    public function gcd(BCMath $n): static
    {
        ['gcd' => $gcd] = $this->extendedGCD($n);
        return $gcd;
    }

    /**
     * Absolute value.
     */
    public function abs(): static
    {
        $temp = new static();
        $temp->value = strlen($this->value) && $this->value[0] == '-' ?
            substr($this->value, 1) :
            $this->value;

        return $temp;
    }

    /**
     * Logical And
     */
    public function bitwise_and(BCMath $x): static
    {
        return $this->bitwiseAndHelper($x);
    }

    /**
     * Logical Or
     */
    public function bitwise_or(BCMath $x): static
    {
        return $this->bitwiseOrHelper($x);
    }

    /**
     * Logical Exclusive Or
     */
    public function bitwise_xor(BCMath $x): static
    {
        return $this->bitwiseXorHelper($x);
    }

    /**
     * Logical Right Shift
     *
     * Shifts BigInteger's by $shift bits, effectively dividing by 2**$shift.
     */
    public function bitwise_rightShift(int $shift): static
    {
        $temp = new static();
        $temp->value = bcdiv($this->value, bcpow('2', (string)$shift, 0), 0);

        return $this->normalize($temp);
    }

    /**
     * Logical Left Shift
     *
     * Shifts BigInteger's by $shift bits, effectively multiplying by 2**$shift.
     */
    public function bitwise_leftShift(int $shift): static
    {
        $temp = new static();
        $temp->value = bcmul($this->value, bcpow('2', (string) $shift, 0), 0);

        return $this->normalize($temp);
    }

    /**
     * Compares two numbers.
     *
     * Although one might think !$x->compare($y) means $x != $y, it, in fact, means the opposite.  The reason for this
     * is demonstrated thusly:
     *
     * $x  > $y: $x->compare($y)  > 0
     * $x  < $y: $x->compare($y)  < 0
     * $x == $y: $x->compare($y) == 0
     *
     * Note how the same comparison operator is used.  If you want to test for equality, use $x->equals($y).
     *
     * {@internal Could return $this->subtract($x), but that's not as fast as what we do do.}
     *
     * @return int in case < 0 if $this is less than $y; > 0 if $this is greater than $y, and 0 if they are equal.
     * @see self::equals()
     */
    public function compare(BCMath $y): int
    {
        return bccomp($this->value, $y->value, 0);
    }

    /**
     * Tests the equality of two numbers.
     *
     * If you need to see if one number is greater than or less than another number, use BigInteger::compare()
     */
    public function equals(BCMath $x): bool
    {
        return $this->value == $x->value;
    }

    /**
     * Performs modular exponentiation.
     */
    public function modPow(BCMath $e, BCMath $n): ?BCMath
    {
        return $this->powModOuter($e, $n);
    }

    /**
     * Performs modular exponentiation.
     *
     * Alias for modPow().
     */
    public function powMod(BCMath $e, BCMath $n): ?BCMath
    {
        return $this->powModOuter($e, $n);
    }

    /**
     * Performs modular exponentiation.
     */
    protected function powModInner(BCMath $e, BCMath $n): static
    {
        try {
            $class = static::$modexpEngine[static::class];
            return $class::powModHelper($this, $e, $n, static::class);
        } catch (\Exception) {
            return BCMath\DefaultEngine::powModHelper($this, $e, $n, static::class);
        }
    }

    /**
     * Normalize
     *
     * Removes leading zeros and truncates (if necessary) to maintain the appropriate precision
     */
    protected function normalize(BCMath $result): static
    {
        $result->precision = $this->precision;
        $result->bitmask = $this->bitmask;

        if (isset($result->bitmask)) {
            $result->value = bcmod($result->value, $result->bitmask->value, 0);
        }

        return $result;
    }

    /**
     * Generate a random prime number between a range
     *
     * If there's not a prime within the given range, null will be returned.
     */
    public static function randomRangePrime(BCMath $min, BCMath $max): ?BCMath
    {
        return self::randomRangePrimeOuter($min, $max);
    }

    /**
     * Generate a random number between a range
     *
     * Returns a random number between $min and $max where $min and $max
     * can be defined using one of the two methods:
     *
     * BigInteger::randomRange($min, $max)
     * BigInteger::randomRange($max, $min)
     */
    public static function randomRange(BCMath $min, BCMath $max): static
    {
        return self::randomRangeHelper($min, $max);
    }

    /**
     * Make the current number odd
     *
     * If the current number is odd it'll be unchanged.  If it's even, one will be added to it.
     *
     * @see self::randomPrime()
     */
    protected function make_odd(): void
    {
        if (!$this->isOdd()) {
            $this->value = bcadd($this->value, '1', 0);
        }
    }

    /**
     * Test the number against small primes.
     *
     * @see self::isPrime()
     */
    protected function testSmallPrimes(): bool
    {
        if ($this->value === '1') {
            return false;
        }
        if ($this->value === '2') {
            return true;
        }
        if ($this->value[-1] % 2 == 0) {
            return false;
        }

        foreach (self::PRIMES as $prime) {
            $r = bcmod($this->value, (string) $prime, 0);
            if ($r == '0') {
                return $this->value == $prime;
            }
        }

        return true;
    }

    /**
     * Scan for 1 and right shift by that amount
     *
     * ie. $s = gmp_scan1($n, 0) and $r = gmp_div_q($n, gmp_pow(gmp_init('2'), $s));
     *
     * @see self::isPrime()
     */
    public static function scan1divide(BCMath $r): int
    {
        $r_value = &$r->value;
        $s = 0;
        // if $n was 1, $r would be 0 and this would be an infinite loop, hence our $this->equals(static::$one[static::class]) check earlier
        while ($r_value[-1] % 2 == 0) {
            $r_value = bcdiv($r_value, '2', 0);
            ++$s;
        }

        return $s;
    }

    /**
     * Performs exponentiation.
     */
    public function pow(BCMath $n): static
    {
        $temp = new static();
        $temp->value = bcpow($this->value, $n->value, 0);

        return $this->normalize($temp);
    }

    /**
     * Return the minimum BigInteger between an arbitrary number of BigIntegers.
     */
    public static function min(BCMath ...$nums): static
    {
        return self::minHelper($nums);
    }

    /**
     * Return the maximum BigInteger between an arbitrary number of BigIntegers.
     */
    public static function max(BCMath ...$nums): static
    {
        return self::maxHelper($nums);
    }

    /**
     * Tests BigInteger to see if it is between two integers, inclusive
     */
    public function between(BCMath $min, BCMath $max): bool
    {
        return $this->compare($min) >= 0 && $this->compare($max) <= 0;
    }

    /**
     * Set Bitmask
     *
     * @see self::setPrecision()
     */
    protected static function setBitmask(int $bits): static
    {
        $temp = parent::setBitmask($bits);
        return $temp->add(static::$one[static::class]);
    }

    /**
     * Is Odd?
     */
    public function isOdd(): bool
    {
        return $this->value[-1] % 2 == 1;
    }

    /**
     * Tests if a bit is set
     */
    public function testBit($x): bool
    {
        $divisor = bcpow('2', (string) ($x + 1), 0);
        return bccomp(
            bcmod($this->value, $divisor, 0),
            bcpow('2', "$x", 0),
            0
        ) >= 0;
    }

    /**
     * Is Negative?
     */
    public function isNegative(): bool
    {
        return strlen($this->value) && $this->value[0] == '-';
    }

    /**
     * Negate
     *
     * Given $k, returns -$k
     */
    public function negate(): static
    {
        $temp = clone $this;

        if (!strlen($temp->value)) {
            return $temp;
        }

        $temp->value = $temp->value[0] == '-' ?
            substr($this->value, 1) :
            '-' . $this->value;

        return $temp;
    }
}
<?php

/**
 * BCMath Barrett Modular Exponentiation Engine
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2017-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Math\BigInteger\Engines\BCMath\Reductions;

use phpseclib4\Math\BigInteger\Engines\BCMath\Base;

/**
 * PHP Barrett Modular Exponentiation Engine
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class Barrett extends Base
{
    /**
     * Cache constants
     *
     * $cache[self::VARIABLE] tells us whether or not the cached data is still valid.
     */
    public const VARIABLE = 0;
    /**
     * $cache[self::DATA] contains the cached data.
     */
    public const DATA = 1;

    /**
     * Barrett Modular Reduction
     *
     * See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=14 HAC 14.3.3} /
     * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=165 MPM 6.2.5} for more information.  Modified slightly,
     * so as not to require negative numbers (initially, this script didn't support negative numbers).
     *
     * Employs "folding", as described at
     * {@link http://www.cosic.esat.kuleuven.be/publications/thesis-149.pdf#page=66 thesis-149.pdf#page=66}.  To quote from
     * it, "the idea [behind folding] is to find a value x' such that x (mod m) = x' (mod m), with x' being smaller than x."
     *
     * Unfortunately, the "Barrett Reduction with Folding" algorithm described in thesis-149.pdf is not, as written, all that
     * usable on account of (1) its not using reasonable radix points as discussed in
     * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=162 MPM 6.2.2} and (2) the fact that, even with reasonable
     * radix points, it only works when there are an even number of digits in the denominator.  The reason for (2) is that
     * (x >> 1) + (x >> 1) != x / 2 + x / 2.  If x is even, they're the same, but if x is odd, they're not.  See the in-line
     * comments for details.
     */
    protected static function reduce(string $n, string $m): string
    {
        static $cache = [
            self::VARIABLE => [],
            self::DATA => [],
        ];

        $m_length = strlen($m);

        if (strlen($n) > 2 * $m_length) {
            return bcmod($n, $m, 0);
        }

        // if (m.length >> 1) + 2 <= m.length then m is too small and n can't be reduced
        if ($m_length < 5) {
            return self::regularBarrett($n, $m);
        }
        // n = 2 * m.length
        $correctionNeeded = false;
        if ($m_length & 1) {
            $correctionNeeded = true;
            $n .= '0';
            $m .= '0';
            $m_length++;
        }

        if (($key = array_search($m, $cache[self::VARIABLE])) === false) {
            $cache[self::VARIABLE][] = $m;

            $lhs = '1' . str_repeat('0', $m_length + ($m_length >> 1));
            $u = bcdiv($lhs, $m, 0);
            $m1 = bcsub($lhs, bcmul($u, $m, 0), 0);

            $cache[self::DATA][] = [
                'u' => $u, // m.length >> 1 (technically (m.length >> 1) + 1)
                'm1' => $m1, // m.length
            ];
        } else {
            [
                'u' => $u,
                'm1' => $m1
            ] = $cache[self::DATA][$key];
        }

        $cutoff = $m_length + ($m_length >> 1);

        $lsd = substr($n, -$cutoff);
        $msd = substr($n, 0, -$cutoff);

        $temp = bcmul($msd, $m1, 0); // m.length + (m.length >> 1)
        $n = bcadd($lsd, $temp, 0); // m.length + (m.length >> 1) + 1 (so basically we're adding two same length numbers)
        //if ($m_length & 1) {
        //    return self::regularBarrett($n, $m);
        //}

        // (m.length + (m.length >> 1) + 1) - (m.length - 1) == (m.length >> 1) + 2
        $temp = substr($n, 0, -$m_length + 1);
        // if even: ((m.length >> 1) + 2) + (m.length >> 1) == m.length + 2
        // if odd:  ((m.length >> 1) + 2) + (m.length >> 1) == (m.length - 1) + 2 == m.length + 1
        $temp = bcmul($temp, $u, 0);
        // if even: (m.length + 2) - ((m.length >> 1) + 1) = m.length - (m.length >> 1) + 1
        // if odd:  (m.length + 1) - ((m.length >> 1) + 1) = m.length - (m.length >> 1)
        $temp = substr($temp, 0, -($m_length >> 1) - 1);
        // if even: (m.length - (m.length >> 1) + 1) + m.length = 2 * m.length - (m.length >> 1) + 1
        // if odd:  (m.length - (m.length >> 1)) + m.length     = 2 * m.length - (m.length >> 1)
        $temp = bcmul($temp, $m, 0);

        // at this point, if m had an odd number of digits, we'd be subtracting a 2 * m.length - (m.length >> 1) digit
        // number from a m.length + (m.length >> 1) + 1 digit number.  ie. there'd be an extra digit and the while loop
        // following this comment would loop a lot (hence our calling _regularBarrett() in that situation).

        $result = bcsub($n, $temp, 0);

        //if (bccomp($result, '0') < 0) {
        if ($result[0] == '-') {
            $temp = '1' . str_repeat('0', $m_length + 1);
            $result = bcadd($result, $temp, 0);
        }

        while (bccomp($result, $m, 0) >= 0) {
            $result = bcsub($result, $m, 0);
        }

        return $correctionNeeded && $result != '0' ? substr($result, 0, -1) : $result;
    }

    /**
     * (Regular) Barrett Modular Reduction
     *
     * For numbers with more than four digits BigInteger::_barrett() is faster.  The difference between that and this
     * is that this function does not fold the denominator into a smaller form.
     */
    private static function regularBarrett(string $x, string $n): string
    {
        static $cache = [
            self::VARIABLE => [],
            self::DATA => [],
        ];

        $n_length = strlen($n);

        if (strlen($x) > 2 * $n_length) {
            return bcmod($x, $n, 0);
        }

        if (($key = array_search($n, $cache[self::VARIABLE])) === false) {
            $key = count($cache[self::VARIABLE]);
            $cache[self::VARIABLE][] = $n;
            $lhs = '1' . str_repeat('0', 2 * $n_length);
            $cache[self::DATA][] = bcdiv($lhs, $n, 0);
        }

        $temp = substr($x, 0, -$n_length + 1);
        $temp = bcmul($temp, $cache[self::DATA][$key], 0);
        $temp = substr($temp, 0, -$n_length - 1);

        $r1 = substr($x, -$n_length - 1);
        $r2 = substr(bcmul($temp, $n, 0), -$n_length - 1);
        $result = bcsub($r1, $r2);

        //if (bccomp($result, '0') < 0) {
        if ($result[0] == '-') {
            $q = '1' . str_repeat('0', $n_length + 1);
            $result = bcadd($result, $q, 0);
        }

        while (bccomp($result, $n, 0) >= 0) {
            $result = bcsub($result, $n, 0);
        }

        return $result;
    }
}
<?php

/**
 * BCMath Dynamic Barrett Modular Exponentiation Engine
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2017-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Math\BigInteger\Engines\BCMath\Reductions;

use phpseclib4\Math\BigInteger\Engines\BCMath;
use phpseclib4\Math\BigInteger\Engines\BCMath\Base;

/**
 * PHP Barrett Modular Exponentiation Engine
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class EvalBarrett extends Base
{
    /**
     * Custom Reduction Function
     *
     * @see self::generateCustomReduction
     */
    private static \Closure $custom_reduction;

    /**
     * Barrett Modular Reduction
     *
     * This calls a dynamically generated loop unrolled function that's specific to a given modulo.
     * Array lookups are avoided as are if statements testing for how many bits the host OS supports, etc.
     */
    protected static function reduce(string $n, string $m): string
    {
        $inline = self::$custom_reduction;
        return $inline($n);
    }

    /**
     * Generate Custom Reduction
     */
    protected static function generateCustomReduction(BCMath $m, string $class): \Closure
    {
        $m = $m->value;
        $m_length = strlen($m);

        if ($m_length < 5) {
            $code = 'return bcmod($x, $n, 0);';
            eval('$func = function ($n) { ' . $code . '};');
            self::$custom_reduction = $func;
            return $func;
        }

        $lhs = '1' . str_repeat('0', $m_length + ($m_length >> 1));
        $u = bcdiv($lhs, $m, 0);
        $m1 = bcsub($lhs, bcmul($u, $m, 0), 0);

        $cutoff = $m_length + ($m_length >> 1);

        $m = "'$m'";
        $u = "'$u'";
        $m1 = "'$m1'";

        $code = '
            $lsd = substr($n, -' . $cutoff . ');
            $msd = substr($n, 0, -' . $cutoff . ');

            $temp = bcmul($msd, ' . $m1 . ', 0);
            $n = bcadd($lsd, $temp, 0);

            $temp = substr($n, 0, ' . (-$m_length + 1) . ');
            $temp = bcmul($temp, ' . $u . ', 0);
            $temp = substr($temp, 0, ' . (-($m_length >> 1) - 1) . ');
            $temp = bcmul($temp, ' . $m . ', 0);

            $result = bcsub($n, $temp, 0);

            if ($result[0] == \'-\') {
                $temp = \'1' . str_repeat('0', $m_length + 1) . '\';
                $result = bcadd($result, $temp, 0);
            }

            while (bccomp($result, ' . $m . ') >= 0) {
                $result = bcsub($result, ' . $m . ', 0);
            }

            return $result;';

        eval('$func = function ($n) { ' . $code . '};');

        self::$custom_reduction = $func;

        return $func;
    }
}
<?php

/**
 * Modular Exponentiation Engine
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2017-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Math\BigInteger\Engines\BCMath;

use phpseclib4\Math\BigInteger\Engines\BCMath;

/**
 * Sliding Window Exponentiation Engine
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class Base extends BCMath
{
    /**
     * Cache constants
     *
     * $cache[self::VARIABLE] tells us whether or not the cached data is still valid.
     */
    public const VARIABLE = 0;
    /**
     * $cache[self::DATA] contains the cached data.
     */
    public const DATA = 1;

    /**
     * Test for engine validity
     */
    public static function isValidEngine(): bool
    {
        return static::class != __CLASS__;
    }

    /**
     * Performs modular exponentiation.
     */
    protected static function powModHelper(BCMath $x, BCMath $e, BCMath $n, string $class): BCMath
    {
        if (empty($e->value)) {
            $temp = new $class();
            $temp->value = '1';
            return $x->normalize($temp);
        }

        return $x->normalize(static::slidingWindow($x, $e, $n, $class));
    }

    /**
     * Modular reduction preparation
     *
     * @see self::slidingWindow()
     */
    protected static function prepareReduce(string $x, string $n, string $class): string
    {
        return static::reduce($x, $n);
    }

    /**
     * Modular multiply
     *
     * @see self::slidingWindow()
     */
    protected static function multiplyReduce(string $x, string $y, string $n, string $class): string
    {
        return static::reduce(bcmul($x, $y, 0), $n);
    }

    /**
     * Modular square
     *
     * @see self::slidingWindow()
     */
    protected static function squareReduce(string $x, string $n, string $class): string
    {
        return static::reduce(bcmul($x, $x, 0), $n);
    }
}
<?php

/**
 * OpenSSL Modular Exponentiation Engine
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2017-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Math\BigInteger\Engines\BCMath;

use phpseclib4\Math\BigInteger\Engines\OpenSSL as Progenitor;

/**
 * OpenSSL Modular Exponentiation Engine
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class OpenSSL extends Progenitor
{
}
<?php

/**
 * Built-In BCMath Modular Exponentiation Engine
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2017-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Math\BigInteger\Engines\BCMath;

use phpseclib4\Math\BigInteger\Engines\BCMath;

/**
 * Built-In BCMath Modular Exponentiation Engine
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
abstract class BuiltIn extends BCMath
{
    /**
     * Performs modular exponentiation.
     */
    protected static function powModHelper(BCMath $x, BCMath $e, BCMath $n): BCMath
    {
        $temp = new BCMath();
        $temp->value = bcpowmod($x->value, $e->value, $n->value, 0);

        return $x->normalize($temp);
    }
}
<?php

/**
 * BCMath Default Modular Exponentiation Engine
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2017-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Math\BigInteger\Engines\BCMath;

use phpseclib4\Math\BigInteger\Engines\BCMath\Reductions\Barrett;

/**
 * PHP Default Modular Exponentiation Engine
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class DefaultEngine extends Barrett
{
}
<?php

/**
 * GMP BigInteger Engine
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2017-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Math\BigInteger\Engines;

use phpseclib4\Exception\BadConfigurationException;

/**
 * GMP Engine.
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
class GMP extends Engine
{
    /**
     * Can Bitwise operations be done fast?
     *
     * @see parent::bitwise_leftRotate()
     * @see parent::bitwise_rightRotate()
     */
    public const FAST_BITWISE = true;

    /**
     * Engine Directory
     *
     * @see parent::setModExpEngine
     */
    public const ENGINE_DIR = 'GMP';

    /**
     * Holds the BigInteger's value
     */
    protected string|\GMP $value;

    /**
     * Test for engine validity
     *
     * @see parent::__construct()
     */
    public static function isValidEngine(): bool
    {
        return extension_loaded('gmp');
    }

    /**
     * Default constructor
     *
     * @param int|string|\GMP $x integer Base-10 number or base-$base number if $base set.
     * @see parent::__construct()
     */
    public function __construct(int|string|\GMP $x = 0, int $base = 10)
    {
        if (!isset(static::$isValidEngine[static::class])) {
            static::$isValidEngine[static::class] = self::isValidEngine();
        }
        if (!static::$isValidEngine[static::class]) {
            throw new BadConfigurationException('GMP is not setup correctly on this system');
        }

        if ($x instanceof \GMP) {
            $this->value = $x;
            return;
        }

        $this->value = gmp_init(0);

        parent::__construct($x, $base);
    }

    /**
     * Initialize a GMP BigInteger Engine instance
     *
     * @see parent::__construct()
     */
    protected function initialize(int $base): void
    {
        switch (abs($base)) {
            case 256:
                $this->value = gmp_import($this->value);
                if ($this->is_negative) {
                    $this->value = -$this->value;
                }
                break;
            case 16:
                $temp = $this->is_negative ? '-0x' . $this->value : '0x' . $this->value;
                $this->value = gmp_init($temp);
                break;
            case 10:
                $this->value = gmp_init($this->value ?? '0');
        }
    }

    /**
     * Converts a BigInteger to a base-10 number.
     */
    public function toString(): string
    {
        return (string) $this->value;
    }

    /**
     * Converts a BigInteger to a bit string (eg. base-2).
     *
     * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're
     * saved as two's compliment.
     */
    public function toBits(bool $twos_compliment = false): string
    {
        $hex = $this->toHex($twos_compliment);
        $bits = strlen($hex) ? gmp_strval(gmp_init($hex, 16), 2) : '';

        if ($this->precision > 0) {
            $bits = substr($bits, -$this->precision);
        }

        if ($twos_compliment && $this->compare(new static()) > 0 && $this->precision <= 0) {
            return '0' . $bits;
        }

        return $bits;
    }

    /**
     * Converts a BigInteger to a byte string (eg. base-256).
     */
    public function toBytes(bool $twos_compliment = false): string
    {
        if ($twos_compliment) {
            return $this->toBytesHelper();
        }

        if (gmp_cmp($this->value, gmp_init(0)) == 0) {
            return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
        }

        $temp = gmp_export($this->value);

        return $this->precision > 0 ?
            substr(str_pad($temp, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) :
            ltrim($temp, chr(0));
    }

    /**
     * Adds two BigIntegers.
     */
    public function add(GMP $y): GMP
    {
        $temp = new self();
        $temp->value = $this->value + $y->value;

        return $this->normalize($temp);
    }

    /**
     * Subtracts two BigIntegers.
     */
    public function subtract(GMP $y): GMP
    {
        $temp = new self();
        $temp->value = $this->value - $y->value;

        return $this->normalize($temp);
    }

    /**
     * Multiplies two BigIntegers.
     */
    public function multiply(GMP $x): GMP
    {
        $temp = new self();
        $temp->value = $this->value * $x->value;

        return $this->normalize($temp);
    }

    /**
     * Divides two BigIntegers.
     *
     * Returns an array whose first element contains the quotient and whose second element contains the
     * "common residue".  If the remainder would be positive, the "common residue" and the remainder are the
     * same.  If the remainder would be negative, the "common residue" is equal to the sum of the remainder
     * and the divisor (basically, the "common residue" is the first positive modulo).
     *
     * @return array{GMP, GMP}
     */
    public function divide(GMP $y): array
    {
        $quotient = new self();
        $remainder = new self();

        [$quotient->value, $remainder->value] = gmp_div_qr($this->value, $y->value);

        if (gmp_sign($remainder->value) < 0) {
            $remainder->value = $remainder->value + gmp_abs($y->value);
        }

        return [$this->normalize($quotient), $this->normalize($remainder)];
    }

    /**
     * Compares two numbers.
     *
     * Although one might think !$x->compare($y) means $x != $y, it, in fact, means the opposite.  The reason for this
     * is demonstrated thusly:
     *
     * $x  > $y: $x->compare($y)  > 0
     * $x  < $y: $x->compare($y)  < 0
     * $x == $y: $x->compare($y) == 0
     *
     * Note how the same comparison operator is used.  If you want to test for equality, use $x->equals($y).
     *
     * {@internal Could return $this->subtract($x), but that's not as fast as what we do do.}
     *
     * @return int in case < 0 if $this is less than $y; > 0 if $this is greater than $y, and 0 if they are equal.
     * @see self::equals()
     */
    public function compare(GMP $y): int
    {
        return $this->value <=> $y->value;
    }

    /**
     * Tests the equality of two numbers.
     *
     * If you need to see if one number is greater than or less than another number, use BigInteger::compare()
     */
    public function equals(GMP $x): bool
    {
        return $this->value == $x->value;
    }

    /**
     * Calculates modular inverses.
     *
     * Say you have (30 mod 17 * x mod 17) mod 17 == 1.  x can be found using modular inverses.
     */
    public function modInverse(GMP $n): ?GMP
    {
        $result = gmp_invert($this->value, $n->value);
        if ($result === false) {
            return null;
        }
        $temp = new self();
        $temp->value = $result;

        return $this->normalize($temp);
    }

    /**
     * Calculates the greatest common divisor and Bezout's identity.
     *
     * Say you have 693 and 609.  The GCD is 21.  Bezout's identity states that there exist integers x and y such that
     * 693*x + 609*y == 21.  In point of fact, there are actually an infinite number of x and y combinations and which
     * combination is returned is dependent upon which mode is in use.  See
     * {@link http://en.wikipedia.org/wiki/B%C3%A9zout%27s_identity Bezout's identity - Wikipedia} for more information.
     *
     * @return GMP[]
     */
    public function extendedGCD(GMP $n): array
    {
        [
            'g' => $g,
            's' => $s,
            't' => $t
        ] = gmp_gcdext($this->value, $n->value);

        return [
            'gcd' => $this->normalize(new self($g)),
            'x' => $this->normalize(new self($s)),
            'y' => $this->normalize(new self($t)),
        ];
    }

    /**
     * Calculates the greatest common divisor
     *
     * Say you have 693 and 609.  The GCD is 21.
     */
    public function gcd(GMP $n): GMP
    {
        $r = gmp_gcd($this->value, $n->value);
        return $this->normalize(new self($r));
    }

    /**
     * Absolute value.
     */
    public function abs(): GMP
    {
        $temp = new self();
        $temp->value = gmp_abs($this->value);

        return $temp;
    }

    /**
     * Logical And
     */
    public function bitwise_and(GMP $x): GMP
    {
        $temp = new self();
        $temp->value = $this->value & $x->value;

        return $this->normalize($temp);
    }

    /**
     * Logical Or
     */
    public function bitwise_or(GMP $x): GMP
    {
        $temp = new self();
        $temp->value = $this->value | $x->value;

        return $this->normalize($temp);
    }

    /**
     * Logical Exclusive Or
     */
    public function bitwise_xor(GMP $x): GMP
    {
        $temp = new self();
        $temp->value = $this->value ^ $x->value;

        return $this->normalize($temp);
    }

    /**
     * Logical Right Shift
     *
     * Shifts BigInteger's by $shift bits, effectively dividing by 2**$shift.
     */
    public function bitwise_rightShift(int $shift): GMP
    {
        // 0xFFFFFFFF >> 2 == -1 (on 32-bit systems)
        // gmp_init('0xFFFFFFFF') >> 2 == gmp_init('0x3FFFFFFF')

        $temp = new self();
        $temp->value = $this->value >> $shift;

        return $this->normalize($temp);
    }

    /**
     * Logical Left Shift
     *
     * Shifts BigInteger's by $shift bits, effectively multiplying by 2**$shift.
     */
    public function bitwise_leftShift(int $shift): GMP
    {
        $temp = new self();
        $temp->value = $this->value << $shift;

        return $this->normalize($temp);
    }

    /**
     * Performs modular exponentiation.
     */
    public function modPow(GMP $e, GMP $n): ?GMP
    {
        return $this->powModOuter($e, $n);
    }

    /**
     * Performs modular exponentiation.
     *
     * Alias for modPow().
     */
    public function powMod(GMP $e, GMP $n): ?GMP
    {
        return $this->powModOuter($e, $n);
    }

    /**
     * Performs modular exponentiation.
     */
    protected function powModInner(GMP $e, GMP $n): GMP
    {
        $class = static::$modexpEngine[static::class];
        return $class::powModHelper($this, $e, $n);
    }

    /**
     * Normalize
     *
     * Removes leading zeros and truncates (if necessary) to maintain the appropriate precision
     */
    protected function normalize(GMP $result): GMP
    {
        $result->precision = $this->precision;
        $result->bitmask = $this->bitmask;

        if (isset($result->bitmask)) {
            $flip = $result->value < 0;
            if ($flip) {
                $result->value = -$result->value;
            }
            $result->value = $result->value & $result->bitmask->value;
            if ($flip) {
                $result->value = -$result->value;
            }
        }

        return $result;
    }

    /**
     * Performs some post-processing for randomRangePrime
     *
     * @psalm-suppress UndefinedPropertyFetch
     */
    protected static function randomRangePrimeInner(Engine $x, Engine $min, Engine $max): ?GMP
    {
        $p = gmp_nextprime($x->value);

        if ($p <= $max->value) {
            return new self($p);
        }

        if ($min->value != $x->value) {
            $x = new self($x->value - 1);
        }

        return self::randomRangePrime($min, $x);
    }

    /**
     * Generate a random prime number between a range
     *
     * If there's not a prime within the given range, false will be returned.
     */
    public static function randomRangePrime(GMP $min, GMP $max): ?GMP
    {
        return self::randomRangePrimeOuter($min, $max);
    }

    /**
     * Generate a random number between a range
     *
     * Returns a random number between $min and $max where $min and $max
     * can be defined using one of the two methods:
     *
     * BigInteger::randomRange($min, $max)
     * BigInteger::randomRange($max, $min)
     */
    public static function randomRange(GMP $min, GMP $max): GMP
    {
        return self::randomRangeHelper($min, $max);
    }

    /**
     * Make the current number odd
     *
     * If the current number is odd it'll be unchanged.  If it's even, one will be added to it.
     *
     * @see self::randomPrime()
     */
    protected function make_odd(): void
    {
        gmp_setbit($this->value, 0);
    }

    /**
     * Tests Primality
     */
    protected function testPrimality(int $t): bool
    {
        return gmp_prob_prime($this->value, $t) != 0;
    }

    /**
     * Calculates the nth root of a biginteger.
     *
     * Returns the nth root of a positive biginteger, where n defaults to 2
     */
    protected function rootInner(int $n): GMP
    {
        $root = new self();
        $root->value = gmp_root($this->value, $n);
        return $this->normalize($root);
    }

    /**
     * Performs exponentiation.
     */
    public function pow(GMP $n): GMP
    {
        $temp = new self();
        $temp->value = $this->value ** $n->value;

        return $this->normalize($temp);
    }

    /**
     * Return the minimum BigInteger between an arbitrary number of BigIntegers.
     */
    public static function min(GMP ...$nums): GMP
    {
        return self::minHelper($nums);
    }

    /**
     * Return the maximum BigInteger between an arbitrary number of BigIntegers.
     */
    public static function max(GMP ...$nums): GMP
    {
        return self::maxHelper($nums);
    }

    /**
     * Tests BigInteger to see if it is between two integers, inclusive
     */
    public function between(GMP $min, GMP $max): bool
    {
        return $this->compare($min) >= 0 && $this->compare($max) <= 0;
    }

    /**
     * Create Recurring Modulo Function
     *
     * Sometimes it may be desirable to do repeated modulos with the same number outside of
     * modular exponentiation
     */
    public function createRecurringModuloFunction(): \Closure
    {
        $temp = $this->value;
        return fn (GMP $x): GMP => new GMP($x->value % $temp);
    }

    /**
     * Scan for 1 and right shift by that amount
     *
     * ie. $s = gmp_scan1($n, 0) and $r = gmp_div_q($n, gmp_pow(gmp_init('2'), $s));
     */
    public static function scan1divide(GMP $r): int
    {
        $s = gmp_scan1($r->value, 0);
        $r->value >>= $s;
        return $s;
    }

    /**
     * Is Odd?
     */
    public function isOdd(): bool
    {
        return gmp_testbit($this->value, 0);
    }

    /**
     * Tests if a bit is set
     */
    public function testBit($x): bool
    {
        return gmp_testbit($this->value, $x);
    }

    /**
     * Is Negative?
     */
    public function isNegative(): bool
    {
        return gmp_sign($this->value) == -1;
    }

    /**
     * Negate
     *
     * Given $k, returns -$k
     */
    public function negate(): GMP
    {
        $temp = clone $this;
        $temp->value = -$this->value;

        return $temp;
    }
}
<?php

/**
 * Pure-PHP 32-bit BigInteger Engine
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2017-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Math\BigInteger\Engines;

/**
 * Pure-PHP 32-bit Engine.
 *
 * Uses 64-bit floats if int size is 4 bits
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
class PHP32 extends PHP
{
    // Constants used by PHP.php
    public const BASE = 26;
    public const BASE_FULL = 0x4000000;
    public const MAX_DIGIT = 0x3FFFFFF;
    public const MSB = 0x2000000;

    /**
     * MAX10 in greatest MAX10LEN satisfying
     * MAX10 = 10**MAX10LEN <= 2**BASE.
     */
    public const MAX10 = 10000000;

    /**
     * MAX10LEN in greatest MAX10LEN satisfying
     * MAX10 = 10**MAX10LEN <= 2**BASE.
     */
    public const MAX10LEN = 7;
    public const MAX_DIGIT2 = 4503599627370496;

    /**
     * Initialize a PHP32 BigInteger Engine instance
     *
     * @see parent::initialize()
     */
    protected function initialize(int $base): void
    {
        if ($base != 256 && $base != -256) {
            parent::initialize($base);
            return;
        }

        $val = $this->value;
        $this->value = [];
        $vals = &$this->value;
        $i = strlen($val);
        if (!$i) {
            return;
        }

        while (true) {
            $i -= 4;
            if ($i < 0) {
                if ($i == -4) {
                    break;
                }
                $val = substr($val, 0, 4 + $i);
                $val = str_pad($val, 4, "\0", STR_PAD_LEFT);
                if ($val == "\0\0\0\0") {
                    break;
                }
                $i = 0;
            }
            [, $digit] = unpack('N', substr($val, $i, 4));
            if ($digit < 0) {
                $digit += 0xFFFFFFFF + 1;
            }
            $step = count($vals) & 3;
            if ($step) {
                $digit = (int) floor($digit / 2 ** (2 * $step));
            }
            if ($step != 3) {
                $digit = (int) fmod($digit, static::BASE_FULL);
                $i++;
            }
            $vals[] = $digit;
        }
        while (end($vals) === 0) {
            array_pop($vals);
        }
        reset($vals);
    }

    /**
     * Test for engine validity
     *
     * @see parent::__construct()
     */
    public static function isValidEngine(): bool
    {
        return PHP_INT_SIZE >= 4 && !self::testJITOnWindows();
    }

    /**
     * Adds two BigIntegers.
     */
    public function add(PHP32 $y): PHP32
    {
        $temp = self::addHelper($this->value, $this->is_negative, $y->value, $y->is_negative);

        return $this->convertToObj($temp);
    }

    /**
     * Subtracts two BigIntegers.
     */
    public function subtract(PHP32 $y): PHP32
    {
        $temp = self::subtractHelper($this->value, $this->is_negative, $y->value, $y->is_negative);

        return $this->convertToObj($temp);
    }

    /**
     * Multiplies two BigIntegers.
     */
    public function multiply(PHP32 $y): PHP32
    {
        $temp = self::multiplyHelper($this->value, $this->is_negative, $y->value, $y->is_negative);

        return $this->convertToObj($temp);
    }

    /**
     * Divides two BigIntegers.
     *
     * Returns an array whose first element contains the quotient and whose second element contains the
     * "common residue".  If the remainder would be positive, the "common residue" and the remainder are the
     * same.  If the remainder would be negative, the "common residue" is equal to the sum of the remainder
     * and the divisor (basically, the "common residue" is the first positive modulo).
     *
     * @return array{PHP32, PHP32}
     */
    public function divide(PHP32 $y): array
    {
        return $this->divideHelper($y);
    }

    /**
     * Calculates modular inverses.
     *
     * Say you have (30 mod 17 * x mod 17) mod 17 == 1.  x can be found using modular inverses.
     */
    public function modInverse(PHP32 $n): ?PHP32
    {
        return $this->modInverseHelper($n);
    }

    /**
     * Calculates modular inverses.
     *
     * Say you have (30 mod 17 * x mod 17) mod 17 == 1.  x can be found using modular inverses.
     * @return PHP32[]
     */
    public function extendedGCD(PHP32 $n): array
    {
        return $this->extendedGCDHelper($n);
    }

    /**
     * Calculates the greatest common divisor
     *
     * Say you have 693 and 609.  The GCD is 21.
     */
    public function gcd(PHP32 $n): PHP32
    {
        return $this->extendedGCD($n)['gcd'];
    }

    /**
     * Logical And
     */
    public function bitwise_and(PHP32 $x): PHP32
    {
        return $this->bitwiseAndHelper($x);
    }

    /**
     * Logical Or
     */
    public function bitwise_or(PHP32 $x): PHP32
    {
        return $this->bitwiseOrHelper($x);
    }

    /**
     * Logical Exclusive Or
     */
    public function bitwise_xor(PHP32 $x): PHP32
    {
        return $this->bitwiseXorHelper($x);
    }

    /**
     * Compares two numbers.
     *
     * Although one might think !$x->compare($y) means $x != $y, it, in fact, means the opposite.  The reason for this is
     * demonstrated thusly:
     *
     * $x  > $y: $x->compare($y)  > 0
     * $x  < $y: $x->compare($y)  < 0
     * $x == $y: $x->compare($y) == 0
     *
     * Note how the same comparison operator is used.  If you want to test for equality, use $x->equals($y).
     *
     * {@internal Could return $this->subtract($x), but that's not as fast as what we do do.}
     *
     * @return int in case < 0 if $this is less than $y; > 0 if $this is greater than $y, and 0 if they are equal.
     * @see self::equals()
     */
    public function compare(PHP32 $y): int
    {
        return $this->compareHelper($this->value, $this->is_negative, $y->value, $y->is_negative);
    }

    /**
     * Tests the equality of two numbers.
     *
     * If you need to see if one number is greater than or less than another number, use BigInteger::compare()
     */
    public function equals(PHP32 $x): bool
    {
        return $this->value === $x->value && $this->is_negative == $x->is_negative;
    }

    /**
     * Performs modular exponentiation.
     */
    public function modPow(PHP32 $e, PHP32 $n): ?PHP32
    {
        return $this->powModOuter($e, $n);
    }

    /**
     * Performs modular exponentiation.
     *
     * Alias for modPow().
     */
    public function powMod(PHP32 $e, PHP32 $n): ?PHP32
    {
        return $this->powModOuter($e, $n);
    }

    /**
     * Generate a random prime number between a range
     *
     * If there's not a prime within the given range, false will be returned.
     */
    public static function randomRangePrime(PHP32 $min, PHP32 $max): ?PHP32
    {
        return self::randomRangePrimeOuter($min, $max);
    }

    /**
     * Generate a random number between a range
     *
     * Returns a random number between $min and $max where $min and $max
     * can be defined using one of the two methods:
     *
     * BigInteger::randomRange($min, $max)
     * BigInteger::randomRange($max, $min)
     */
    public static function randomRange(PHP32 $min, PHP32 $max): PHP32
    {
        return self::randomRangeHelper($min, $max);
    }

    /**
     * Performs exponentiation.
     */
    public function pow(PHP32 $n): PHP32
    {
        return $this->powHelper($n);
    }

    /**
     * Return the minimum BigInteger between an arbitrary number of BigIntegers.
     */
    public static function min(PHP32 ...$nums): PHP32
    {
        return self::minHelper($nums);
    }

    /**
     * Return the maximum BigInteger between an arbitrary number of BigIntegers.
     */
    public static function max(PHP32 ...$nums): PHP32
    {
        return self::maxHelper($nums);
    }

    /**
     * Tests BigInteger to see if it is between two integers, inclusive
     */
    public function between(PHP32 $min, PHP32 $max): bool
    {
        return $this->compare($min) >= 0 && $this->compare($max) <= 0;
    }
}
<?php

declare(strict_types=1);

/**
 * Prime Finite Fields
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 */

namespace phpseclib4\Math\PrimeField;

use phpseclib4\Common\Functions\Strings;
use phpseclib4\Exception\UnexpectedValueException;
use phpseclib4\Math\BigInteger;
use phpseclib4\Math\Common\FiniteField\Integer as Base;

/**
 * Prime Finite Fields
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
class Integer extends Base
{
    /**
     * Holds the PrimeField's value
     */
    protected BigInteger $value;

    /**
     * Keeps track of current instance
     */
    protected int $instanceID;

    /**
     * Holds the PrimeField's modulo
     *
     * @var array<int, BigInteger>
     */
    protected static array $modulo;

    /**
     * Holds a pre-generated function to perform modulo reductions
     *
     * @var array<int, callable(BigInteger):BigInteger>
     */
    protected static array $reduce;

    /**
     * Zero
     *
     * @var BigInteger[]
     */
    protected static array $zero;

    /**
     * One
     *
     * @var BigInteger[]
     */
    protected static array $one;

    /**
     * Two
     *
     * @var BigInteger[]
     */
    protected static array $two;

    /**
     * Constant Time Mask
     *
     * @var array<int, list{BigInteger, BigInteger}>
     */
    protected static $mask;

    /**
     * Default constructor
     */
    public function __construct(int $instanceID, ?BigInteger $num = null)
    {
        $this->instanceID = $instanceID;
        if (!isset($num)) {
            $this->value = clone static::$zero[$instanceID];
        } else {
            $reduce = static::$reduce[$instanceID];
            $this->value = $reduce($num);
        }
    }

    /**
     * Set the modulo for a given instance
     */
    public static function setModulo(int $instanceID, BigInteger $modulo): void
    {
        static::$modulo[$instanceID] = $modulo;
        $one = new BigInteger(1);
        static::$mask[$instanceID] = [
            new BigInteger(0),
            $one->bitwise_leftShift($modulo->getLength())->subtract($one)
        ];
    }

    /**
     * Set the modulo for a given instance
     */
    public static function setRecurringModuloFunction(int $instanceID, callable $function): void
    {
        static::$reduce[$instanceID] = $function;
        if (!isset(static::$zero[$instanceID])) {
            static::$zero[$instanceID] = new BigInteger();
        }
    }

    /**
     * Delete the modulo for a given instance
     */
    public static function cleanupCache(int $instanceID): void
    {
        unset(
            static::$modulo[$instanceID],
            static::$reduce[$instanceID],
            static::$zero[$instanceID],
            static::$one[$instanceID],
            static::$two[$instanceID],
            static::$mask[$instanceID]
        );
    }

    /**
     * Returns the modulo
     */
    public static function getModulo(int $instanceID): BigInteger
    {
        return static::$modulo[$instanceID];
    }

    /**
     * Tests a parameter to see if it's of the right instance
     *
     * Throws an exception if the incorrect class is being utilized
     */
    public static function checkInstance(self $x, self $y): void
    {
        if ($x->instanceID != $y->instanceID) {
            throw new UnexpectedValueException('The instances of the two PrimeField\Integer objects do not match');
        }
    }

    /**
     * Tests the equality of two numbers.
     */
    public function equals(self $x): bool
    {
        static::checkInstance($this, $x);

        return $this->value->equals($x->value);
    }

    /**
     * Compares two numbers.
     */
    public function compare(self $x): int
    {
        static::checkInstance($this, $x);

        return $this->value->compare($x->value);
    }

    /**
     * Conditionally add the modulus, without branching on the value.
     *
     * $diff must be in (-modulo, modulo). Returns $diff + modulo when $diff is
     * negative, $diff otherwise.
     */
    private function conditionalAddModulo(BigInteger $diff): self
    {
        $mask = static::$mask[$this->instanceID][(int) $diff->isNegative()];

        $temp = new static($this->instanceID);
        $temp->value = $diff->add(
            static::$modulo[$this->instanceID]->bitwise_and($mask)
        );
        return $temp;
    }

    /**
     * Adds two PrimeFieldIntegers.
     */
    public function add(self $x): self
    {
        static::checkInstance($this, $x);

        // $this->value + $x->value is in [0, 2m), so subtracting once lands in [-m, m)
        return $this->conditionalAddModulo(
            $this->value->add($x->value)->subtract(static::$modulo[$this->instanceID])
        );
    }

    /**
     * Subtracts two PrimeFieldIntegers.
     */
    public function subtract(self $x): self
    {
        static::checkInstance($this, $x);

        // already in (-m, m)
        return $this->conditionalAddModulo($this->value->subtract($x->value));
    }

    /**
     * Multiplies two PrimeFieldIntegers.
     */
    public function multiply(self $x): self
    {
        static::checkInstance($this, $x);

        return new static($this->instanceID, $this->value->multiply($x->value));
    }

    /**
     * Divides two PrimeFieldIntegers.
     */
    public function divide(self $x): self
    {
        static::checkInstance($this, $x);

        $denominator = $x->value->modInverse(static::$modulo[$this->instanceID]);
        return new static($this->instanceID, $this->value->multiply($denominator));
    }

    /**
     * Performs power operation on a PrimeFieldInteger.
     */
    public function pow(BigInteger $x): self
    {
        $temp = new static($this->instanceID);
        $temp->value = $this->value->powMod($x, static::$modulo[$this->instanceID]);

        return $temp;
    }

    /**
     * Calculates the square root
     *
     * @link https://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm
     */
    public function squareRoot(): ?self
    {
        if (!isset(static::$one[$this->instanceID])) {
            static::$one[$this->instanceID] = new BigInteger(1);
            static::$two[$this->instanceID] = new BigInteger(2);
        }
        $one = &static::$one[$this->instanceID];
        $two = &static::$two[$this->instanceID];
        $modulo = &static::$modulo[$this->instanceID];
        $reduce = &static::$reduce[$this->instanceID];

        $p_1 = $modulo->subtract($one);
        $q = clone $p_1;
        $s = BigInteger::scan1divide($q);
        [$pow] = $p_1->divide($two);
        for ($z = $one; !$z->equals($modulo); $z = $z->add($one)) {
            $temp = $z->powMod($pow, $modulo);
            if ($temp->equals($p_1)) {
                break;
            }
        }

        $m = new BigInteger($s);
        $c = $z->powMod($q, $modulo);
        $t = $this->value->powMod($q, $modulo);
        [$temp] = $q->add($one)->divide($two);
        $r = $this->value->powMod($temp, $modulo);

        while (!$t->equals($one)) {
            for ($i = clone $one; $i->compare($m) < 0; $i = $i->add($one)) {
                if ($t->powMod($two->pow($i), $modulo)->equals($one)) {
                    break;
                }
            }

            if ($i->compare($m) == 0) {
                return null;
            }
            $b = $c->powMod($two->pow($m->subtract($i)->subtract($one)), $modulo);
            $m = $i;
            $c = $reduce($b->multiply($b));
            $t = $reduce($t->multiply($c));
            $r = $reduce($r->multiply($b));
        }

        return new static($this->instanceID, $r);
    }

    /**
     * Is Odd?
     */
    public function isOdd(): bool
    {
        return $this->value->isOdd();
    }

    /**
     * Negate
     *
     * A negative number can be written as 0-12. With modulos, 0 is the same thing as the modulo
     * so 0-12 is the same thing as modulo-12
     */
    public function negate(): self
    {
        return new static($this->instanceID, static::$modulo[$this->instanceID]->subtract($this->value));
    }

    /**
     * Converts an Integer to a byte string (eg. base-256).
     */
    public function toBytes(): string
    {
        if (isset(static::$modulo[$this->instanceID])) {
            $length = static::$modulo[$this->instanceID]->getLengthInBytes();
            return str_pad($this->value->toBytes(), $length, "\0", STR_PAD_LEFT);
        }
        return $this->value->toBytes();
    }

    /**
     * Converts an Integer to a hex string (eg. base-16).
     */
    public function toHex(): string
    {
        return Strings::bin2hex($this->toBytes());
    }

    /**
     * Converts an Integer to a bit string (eg. base-2).
     */
    public function toBits(): string
    {
        // return $this->value->toBits();
        static $length;
        if (!isset($length)) {
            $length = static::$modulo[$this->instanceID]->getLength();
        }

        return str_pad($this->value->toBits(), $length, '0', STR_PAD_LEFT);
    }

    /**
     * Returns the w-ary non-adjacent form (wNAF)
     *
     * @param int $w optional
     * @return array<int, int>
     */
    public function getNAF(int $w = 1): array
    {
        $w++;

        $zero = &static::$zero[$this->instanceID];

        $mask = new BigInteger((1 << $w) - 1);
        $sub = new BigInteger(1 << $w);
        //$sub = new BigInteger(1 << ($w - 1));
        $d = $this->toBigInteger();
        $d_i = [];

        $i = 0;
        while ($d->compare($zero) > 0) {
            if ($d->isOdd()) {
                // start mods

                $bigInteger = $d->testBit($w - 1) ?
                    $d->bitwise_and($mask)->subtract($sub) :
                    //$sub->subtract($d->bitwise_and($mask)) :
                    $d->bitwise_and($mask);
                // end mods
                $d = $d->subtract($bigInteger);
                $d_i[$i] = (int) $bigInteger->toString();
            } else {
                $d_i[$i] = 0;
            }
            $shift = !$d->equals($zero) && $d->bitwise_and($mask)->equals($zero) ? $w : 1; // $w or $w + 1?
            $d = $d->bitwise_rightShift($shift);
            while (--$shift > 0) {
                $d_i[++$i] = 0;
            }
            $i++;
        }

        return $d_i;
    }

    /**
     * Converts an Integer to a BigInteger
     */
    public function toBigInteger(): BigInteger
    {
        return clone $this->value;
    }

    /**
     *  __toString() magic method
     */
    public function __toString(): string
    {
        return (string) $this->value;
    }

    /**
     *  __debugInfo() magic method
     */
    public function __debugInfo(): array
    {
        return ['value' => $this->toHex()];
    }
}
<?php

declare(strict_types=1);

/**
 * Finite Fields Base Class
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2017-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 */

namespace phpseclib4\Math\Common;

/**
 * Finite Fields
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class FiniteField
{
}
<?php

declare(strict_types=1);

/**
 * Finite Field Integer Base Class
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2017-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 */

namespace phpseclib4\Math\Common\FiniteField;

/**
 * Finite Field Integer
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
abstract class Integer implements \JsonSerializable
{
    /**
     * JSON Serialize
     *
     * Will be called, automatically, when json_encode() is called on a BigInteger object.
     *
     * PHP Serialize isn't supported because unserializing would require the factory be
     * serialized as well and that just sounds like too much
     *
     * @return array{hex: string}
     */
    public function jsonSerialize(): mixed
    {
        return ['hex' => $this->toHex(true)];
    }

    /**
     * Converts an Integer to a hex string (eg. base-16).
     */
    abstract public function toHex(): string;
}
<?php

/**
 * Prime Finite Fields
 *
 * Utilizes the factory design pattern
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Math;

use phpseclib4\Exception\InvalidArgumentException;
use phpseclib4\Math\Common\FiniteField;
use phpseclib4\Math\PrimeField\Integer;

/**
 * Prime Finite Fields
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
class PrimeField extends FiniteField
{
    /**
     * Instance Counter
     */
    private static int $instanceCounter = 0;

    /**
     * Keeps track of current instance
     */
    protected int $instanceID;

    /**
     * Default constructor
     */
    public function __construct(BigInteger $modulo)
    {
        if (!$modulo->isPrime()) {
            throw new InvalidArgumentException('PrimeField requires a prime number be passed to the constructor');
        }

        $this->instanceID = self::$instanceCounter++;
        Integer::setModulo($this->instanceID, $modulo);
        Integer::setRecurringModuloFunction($this->instanceID, $modulo->createRecurringModuloFunction());
    }

    /**
     * Use a custom defined modular reduction function
     */
    public function setReduction(\Closure $func): void
    {
        Integer::setRecurringModuloFunction($this->instanceID, $func->bindTo($this, $this));
    }

    /**
     * Returns an instance of a dynamically generated PrimeFieldInteger class
     */
    public function newInteger(BigInteger $num): Integer
    {
        return new Integer($this->instanceID, $num);
    }

    /**
     * Returns an integer on the finite field between one and the prime modulo
     */
    public function randomInteger(): Integer
    {
        static $one;
        if (!isset($one)) {
            $one = new BigInteger(1);
        }

        return new Integer($this->instanceID, BigInteger::randomRange($one, Integer::getModulo($this->instanceID)));
    }

    /**
     * Returns the length of the modulo in bytes
     */
    public function getLengthInBytes(): int
    {
        return Integer::getModulo($this->instanceID)->getLengthInBytes();
    }

    /**
     * Returns the length of the modulo in bits
     */
    public function getLength(): int
    {
        return Integer::getModulo($this->instanceID)->getLength();
    }

    /**
     *  Destructor
     */
    public function __destruct()
    {
        Integer::cleanupCache($this->instanceID);
    }
}
<?php

/**
 * Binary Finite Fields
 *
 * In a binary finite field numbers are actually polynomial equations. If you
 * represent the number as a sequence of bits you get a sequence of 1's or 0's.
 * These 1's or 0's represent the coefficients of the x**n, where n is the
 * location of the given bit. When you add numbers over a binary finite field
 * the result should have a coefficient of 1 or 0 as well. Hence addition
 * and subtraction become the same operation as XOR.
 * eg. 1 + 1 + 1 == 3 % 2 == 1 or 0 - 1 == -1 % 2 == 1
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2018-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 */

declare(strict_types=1);

namespace phpseclib4\Math\BinaryField;

use phpseclib4\Common\Functions\Strings;
use phpseclib4\Exception\InvalidArgumentException;
use phpseclib4\Math\{BigInteger, BinaryField};
use phpseclib4\Math\Common\FiniteField\Integer as Base;

/**
 * Binary Finite Fields
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 * @psalm-api
 */
class Integer extends Base
{
    /**
     * Holds the BinaryField's value
     */
    protected string $value;

    /**
     * Keeps track of current instance
     */
    protected int $instanceID;

    /**
     * Holds the PrimeField's modulo
     *
     * @var array<int, string>
     */
    protected static array $modulo;

    /**
     * Holds a pre-generated function to perform modulo reductions
     *
     * @var callable[]
     */
    protected static array $reduce;

    /**
     * Default constructor
     */
    public function __construct($instanceID, $num = '')
    {
        $this->instanceID = $instanceID;
        if (!strlen($num)) {
            $this->value = '';
        } else {
            $reduce = static::$reduce[$instanceID];
            $this->value = $reduce($num);
        }
    }

    /**
     * Set the modulo for a given instance
     */
    public static function setModulo(int $instanceID, string $modulo): void
    {
        static::$modulo[$instanceID] = $modulo;
    }

    /**
     * Set the modulo for a given instance
     */
    public static function setRecurringModuloFunction($instanceID, callable $function): void
    {
        static::$reduce[$instanceID] = $function;
    }

    /**
     * Tests a parameter to see if it's of the right instance
     *
     * Throws an exception if the incorrect class is being utilized
     */
    private static function checkInstance(self $x, self $y): void
    {
        if ($x->instanceID != $y->instanceID) {
            throw new InvalidArgumentException('The instances of the two BinaryField\Integer objects do not match');
        }
    }

    /**
     * Tests the equality of two numbers.
     */
    public function equals(self $x): bool
    {
        static::checkInstance($this, $x);

        return $this->value == $x->value;
    }

    /**
     * Compares two numbers.
     */
    public function compare(self $x): int
    {
        static::checkInstance($this, $x);

        $a = $this->value;
        $b = $x->value;

        $length = max(strlen($a), strlen($b));

        $a = str_pad($a, $length, "\0", STR_PAD_LEFT);
        $b = str_pad($b, $length, "\0", STR_PAD_LEFT);

        return strcmp($a, $b);
    }

    /**
     * Returns the degree of the polynomial
     */
    private static function deg(string $x): int
    {
        $x = ltrim($x, "\0");
        $xbit = decbin(ord($x[0]));
        $xlen = $xbit == '0' ? 0 : strlen($xbit);
        $len = strlen($x);
        if (!$len) {
            return -1;
        }
        return 8 * strlen($x) - 9 + $xlen;
    }

    /**
     * Perform polynomial division
     *
     * @return string[]
     * @link https://en.wikipedia.org/wiki/Polynomial_greatest_common_divisor#Euclidean_division
     */
    private static function polynomialDivide(string $x, string $y): array
    {
        // in wikipedia's description of the algorithm, lc() is the leading coefficient. over a binary field that's
        // always going to be 1.

        $q = chr(0);
        $d = static::deg($y);
        $r = $x;
        while (($degr = static::deg($r)) >= $d) {
            $s = '1' . str_repeat('0', $degr - $d);
            $s = BinaryField::base2ToBase256($s);
            $length = max(strlen($s), strlen($q));
            $q = !isset($q) ? $s :
                str_pad($q, $length, "\0", STR_PAD_LEFT) ^
                str_pad($s, $length, "\0", STR_PAD_LEFT);
            $s = static::polynomialMultiply($s, $y);
            $length = max(strlen($r), strlen($s));
            $r = str_pad($r, $length, "\0", STR_PAD_LEFT) ^
                 str_pad($s, $length, "\0", STR_PAD_LEFT);
        }

        return [ltrim($q, "\0"), ltrim($r, "\0")];
    }

    /**
     * Perform polynomial multiplation in the traditional way
     *
     * @link https://en.wikipedia.org/wiki/Finite_field_arithmetic#Multiplication
     */
    private static function regularPolynomialMultiply(string $x, string $y): string
    {
        $precomputed = [ltrim($x, "\0")];
        $x = strrev(BinaryField::base256ToBase2($x));
        $y = strrev(BinaryField::base256ToBase2($y));
        if (strlen($x) == strlen($y)) {
            $length = strlen($x);
        } else {
            $length = max(strlen($x), strlen($y));
            $x = str_pad($x, $length, '0');
            $y = str_pad($y, $length, '0');
        }
        $result = str_repeat('0', 2 * $length - 1);
        $result = BinaryField::base2ToBase256($result);
        $size = strlen($result);
        $x = strrev($x);

        // precompute left shift 1 through 7
        for ($i = 1; $i < 8; $i++) {
            $precomputed[$i] = BinaryField::base2ToBase256($x . str_repeat('0', $i));
        }
        for ($i = 0; $i < strlen($y); $i++) {
            if ($y[$i] == '1') {
                $temp = $precomputed[$i & 7] . str_repeat("\0", $i >> 3);
                $result ^= str_pad($temp, $size, "\0", STR_PAD_LEFT);
            }
        }

        return $result;
    }

    /**
     * Perform polynomial multiplation
     *
     * Uses karatsuba multiplication to reduce x-bit multiplications to a series of 32-bit multiplications
     *
     * @link https://en.wikipedia.org/wiki/Karatsuba_algorithm
     */
    private static function polynomialMultiply(string $x, string $y): string
    {
        if (strlen($x) == strlen($y)) {
            $length = strlen($x);
        } else {
            $length = max(strlen($x), strlen($y));
            $x = str_pad($x, $length, "\0", STR_PAD_LEFT);
            $y = str_pad($y, $length, "\0", STR_PAD_LEFT);
        }

        switch (true) {
            case PHP_INT_SIZE == 8 && $length <= 4:
                return $length != 4 ?
                    self::subMultiply(str_pad($x, 4, "\0", STR_PAD_LEFT), str_pad($y, 4, "\0", STR_PAD_LEFT)) :
                    self::subMultiply($x, $y);
            case PHP_INT_SIZE == 4 || $length > 32:
                return self::regularPolynomialMultiply($x, $y);
        }

        $m = $length >> 1;

        $x1 = substr($x, 0, -$m);
        $x0 = substr($x, -$m);
        $y1 = substr($y, 0, -$m);
        $y0 = substr($y, -$m);

        $z2 = self::polynomialMultiply($x1, $y1);
        $z0 = self::polynomialMultiply($x0, $y0);
        $z1 = self::polynomialMultiply(
            self::subAdd2($x1, $x0),
            self::subAdd2($y1, $y0)
        );

        $z1 = self::subAdd3($z1, $z2, $z0);

        $xy = self::subAdd3(
            $z2 . str_repeat("\0", 2 * $m),
            $z1 . str_repeat("\0", $m),
            $z0
        );

        return ltrim($xy, "\0");
    }

    /**
     * Perform polynomial multiplication on 2x 32-bit numbers, returning
     * a 64-bit number
     *
     * @link https://www.bearssl.org/constanttime.html#ghash-for-gcm
     */
    private static function subMultiply(string $x, string $y): string
    {
        $x = unpack('N', $x)[1];
        $y = unpack('N', $y)[1];

        $x0 = $x & 0x11111111;
        $x1 = $x & 0x22222222;
        $x2 = $x & 0x44444444;
        $x3 = $x & 0x88888888;

        $y0 = $y & 0x11111111;
        $y1 = $y & 0x22222222;
        $y2 = $y & 0x44444444;
        $y3 = $y & 0x88888888;

        $z0 = ($x0 * $y0) ^ ($x1 * $y3) ^ ($x2 * $y2) ^ ($x3 * $y1);
        $z1 = ($x0 * $y1) ^ ($x1 * $y0) ^ ($x2 * $y3) ^ ($x3 * $y2);
        $z2 = ($x0 * $y2) ^ ($x1 * $y1) ^ ($x2 * $y0) ^ ($x3 * $y3);
        $z3 = ($x0 * $y3) ^ ($x1 * $y2) ^ ($x2 * $y1) ^ ($x3 * $y0);

        $z0 &= 0x1111111111111111;
        $z1 &= 0x2222222222222222;
        $z2 &= 0x4444444444444444;
        $z3 &= -8608480567731124088; // 0x8888888888888888 gets interpreted as a float

        $z = $z0 | $z1 | $z2 | $z3;

        return pack('J', $z);
    }

    /**
     * Adds two numbers
     */
    private static function subAdd2(string $x, string $y): string
    {
        $length = max(strlen($x), strlen($y));
        $x = str_pad($x, $length, "\0", STR_PAD_LEFT);
        $y = str_pad($y, $length, "\0", STR_PAD_LEFT);
        return $x ^ $y;
    }

    /**
     * Adds three numbers
     */
    private static function subAdd3(string $x, string $y, $z): string
    {
        $length = max(strlen($x), strlen($y), strlen($z));
        $x = str_pad($x, $length, "\0", STR_PAD_LEFT);
        $y = str_pad($y, $length, "\0", STR_PAD_LEFT);
        $z = str_pad($z, $length, "\0", STR_PAD_LEFT);
        return $x ^ $y ^ $z;
    }

    /**
     * Adds two BinaryFieldIntegers.
     */
    public function add(self $y): self
    {
        static::checkInstance($this, $y);

        $length = strlen(static::$modulo[$this->instanceID]);

        $x = str_pad($this->value, $length, "\0", STR_PAD_LEFT);
        $y = str_pad($y->value, $length, "\0", STR_PAD_LEFT);

        return new static($this->instanceID, $x ^ $y);
    }

    /**
     * Subtracts two BinaryFieldIntegers.
     */
    public function subtract(self $x): self
    {
        return $this->add($x);
    }

    /**
     * Multiplies two BinaryFieldIntegers.
     */
    public function multiply(self $y): self
    {
        static::checkInstance($this, $y);

        return new static($this->instanceID, static::polynomialMultiply($this->value, $y->value));
    }

    /**
     * Returns the modular inverse of a BinaryFieldInteger
     */
    public function modInverse(): self
    {
        $remainder0 = static::$modulo[$this->instanceID];
        $remainder1 = $this->value;

        if ($remainder1 == '') {
            return new static($this->instanceID);
        }

        $aux0 = "\0";
        $aux1 = "\1";
        while ($remainder1 != "\1") {
            [$q, $r] = static::polynomialDivide($remainder0, $remainder1);
            $remainder0 = $remainder1;
            $remainder1 = $r;
            // the auxiliary in row n is given by the sum of the auxiliary in
            // row n-2 and the product of the quotient and the auxiliary in row
            // n-1
            $temp = static::polynomialMultiply($aux1, $q);
            $aux = str_pad($aux0, strlen($temp), "\0", STR_PAD_LEFT) ^
                   str_pad($temp, strlen($aux0), "\0", STR_PAD_LEFT);
            $aux0 = $aux1;
            $aux1 = $aux;
        }

        $temp = new static($this->instanceID);
        $temp->value = ltrim($aux1, "\0");
        return $temp;
    }

    /**
     * Divides two PrimeFieldIntegers.
     */
    public function divide(self $x): self
    {
        static::checkInstance($this, $x);

        $x = $x->modInverse();
        return $this->multiply($x);
    }

    /**
     * Negate
     *
     * A negative number can be written as 0-12. With modulos, 0 is the same thing as the modulo
     * so 0-12 is the same thing as modulo-12
     */
    public function negate(): self
    {
        $x = str_pad($this->value, strlen(static::$modulo[$this->instanceID]), "\0", STR_PAD_LEFT);

        return new static($this->instanceID, $x ^ static::$modulo[$this->instanceID]);
    }

    /**
     * Returns the modulo
     */
    public static function getModulo(int $instanceID): string
    {
        return static::$modulo[$instanceID];
    }

    /**
     * Converts an Integer to a byte string (eg. base-256).
     */
    public function toBytes(): string
    {
        return str_pad($this->value, strlen(static::$modulo[$this->instanceID]), "\0", STR_PAD_LEFT);
    }

    /**
     * Converts an Integer to a hex string (eg. base-16).
     */
    public function toHex(): string
    {
        return Strings::bin2hex($this->toBytes());
    }

    /**
     * Converts an Integer to a bit string (eg. base-2).
     */
    public function toBits(): string
    {
        //return str_pad(BinaryField::base256ToBase2($this->value), strlen(static::$modulo[$this->instanceID]), '0', STR_PAD_LEFT);
        return BinaryField::base256ToBase2($this->value);
    }

    /**
     * Converts an Integer to a BigInteger
     *
     * @return string
     */
    public function toBigInteger(): BigInteger
    {
        return new BigInteger($this->value, 256);
    }

    /**
     *  __toString() magic method
     */
    public function __toString(): string
    {
        return (string) $this->toBigInteger();
    }

    /**
     *  __debugInfo() magic method
     */
    public function __debugInfo(): array
    {
        return ['value' => $this->toHex()];
    }
}
<?php

declare(strict_types=1);

namespace phpseclib4\Net\SSH2;

/**
 * @internal
 */
abstract class TerminalMode
{
    public const TTY_OP_END = 0;
}
<?php

declare(strict_types=1);

namespace phpseclib4\Net\SSH2;

use phpseclib4\Common\ConstantUtilityTrait;

/**
 * @internal
 */
abstract class MessageType
{
    use ConstantUtilityTrait;

    public const DISCONNECT = 1;
    public const IGNORE = 2;
    public const UNIMPLEMENTED = 3;
    public const DEBUG = 4;
    public const SERVICE_REQUEST = 5;
    public const SERVICE_ACCEPT = 6;
    public const EXT_INFO = 7;
    public const NEWCOMPRESS = 8;

    public const KEXINIT = 20;
    public const NEWKEYS = 21;

    // KEXDH_INIT and KEXDH_REPLY are not defined RFC4250 but are referenced in RFC4253
    public const KEXDH_INIT = 30;
    public const KEXDH_REPLY = 31;

    public const USERAUTH_REQUEST = 50;
    public const USERAUTH_FAILURE = 51;
    public const USERAUTH_SUCCESS = 52;
    public const USERAUTH_BANNER = 53;

    public const USERAUTH_INFO_REQUEST = 60;
    public const USERAUTH_INFO_RESPONSE = 61;

    public const GLOBAL_REQUEST = 80;
    public const REQUEST_SUCCESS = 81;
    public const REQUEST_FAILURE = 82;

    public const CHANNEL_OPEN = 90;
    public const CHANNEL_OPEN_CONFIRMATION = 91;
    public const CHANNEL_OPEN_FAILURE = 92;
    public const CHANNEL_WINDOW_ADJUST = 93;
    public const CHANNEL_DATA = 94;
    public const CHANNEL_EXTENDED_DATA = 95;
    public const CHANNEL_EOF = 96;
    public const CHANNEL_CLOSE = 97;
    public const CHANNEL_REQUEST = 98;
    public const CHANNEL_SUCCESS = 99;
    public const CHANNEL_FAILURE = 100;
}
<?php

declare(strict_types=1);

namespace phpseclib4\Net\SSH2;

/**
 * @internal
 */
abstract class MessageTypeExtra
{
    // RFC 4419 - diffie-hellman-group-exchange-sha{1,256}
    public const KEXDH_GEX_REQUEST_OLD = 30;
    public const KEXDH_GEX_GROUP = 31;
    public const KEXDH_GEX_INIT = 32;
    public const KEXDH_GEX_REPLY = 33;
    public const KEXDH_GEX_REQUEST = 34;

    // RFC 5656 - Elliptic Curves (for curve25519-sha256@libssh.org)
    public const KEX_ECDH_INIT = 30;
    public const KEX_ECDH_REPLY = 31;

    public const USERAUTH_PASSWD_CHANGEREQ = 60;

    public const USERAUTH_PK_OK = 60;
}
<?php

declare(strict_types=1);

namespace phpseclib4\Net\SSH2;

use phpseclib4\Common\ConstantUtilityTrait;

/**
 * @internal
 */
abstract class DisconnectReason
{
    use ConstantUtilityTrait;

    public const HOST_NOT_ALLOWED_TO_CONNECT = 1;
    public const PROTOCOL_ERROR = 2;
    public const KEY_EXCHANGE_FAILED = 3;
    public const RESERVED = 4;
    public const MAC_ERROR = 5;
    public const COMPRESSION_ERROR = 6;
    public const SERVICE_NOT_AVAILABLE = 7;
    public const PROTOCOL_VERSION_NOT_SUPPORTED = 8;
    public const HOST_KEY_NOT_VERIFIABLE = 9;
    public const CONNECTION_LOST = 10;
    public const BY_APPLICATION = 11;
    public const TOO_MANY_CONNECTIONS = 12;
    public const AUTH_CANCELLED_BY_USER = 13;
    public const NO_MORE_AUTH_METHODS_AVAILABLE = 14;
    public const ILLEGAL_USER_NAME = 15;
}
<?php

declare(strict_types=1);

namespace phpseclib4\Net\SSH2;

/**
 * @internal
 */
abstract class ChannelConnectionFailureReason
{
    public const ADMINISTRATIVELY_PROHIBITED = 1;
    public const CONNECT_FAILED = 2;
    public const UNKNOWN_CHANNEL_TYPE = 3;
    public const RESOURCE_SHORTAGE = 4;
}
<?php

/**
 * Pure-PHP implementation of SCP.
 *
 * PHP version 8.1+
 *
 * The API for this library is modeled after the API from PHP's {@link http://php.net/book.ftp FTP extension}.
 *
 * Here's a short example of how to use this library:
 * <code>
 * <?php
 *    include 'vendor/autoload.php';
 *
 *    $scp = new \phpseclib4\Net\SCP('www.domain.tld');
 *    if (!$scp->login('username', 'password')) {
 *        exit('Login Failed');
 *    }
 *
 *    echo $scp->exec('pwd') . "\r\n";
 *    $scp->put('filename.ext', 'hello, world!');
 *    echo $scp->exec('ls -latr');
 * ?>
 * </code>
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2013-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

namespace phpseclib4\Net;

use phpseclib4\Common\Functions\Files;
use phpseclib4\Exception\{
    FileSystemException,
    InvalidArgumentException,
    InvalidStateException,
    SSHChannelExitSignalException,
    TimeoutException,
    UnexpectedSSHMessageException,
    UnexpectedValueException
};

/**
 * Pure-PHP implementations of SCP.
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class SCP extends SSH2
{
    /**
     * Reads data from a local file.
     *
     * @see \phpseclib4\Net\SCP::put()
     */
    public const SOURCE_LOCAL_FILE = 1;
    /**
     * Reads data from a string.
     *
     * @see \phpseclib4\Net\SCP::put()
     */
    // this value isn't really used anymore but i'm keeping it reserved for historical reasons
    public const SOURCE_STRING = 2;
    /**
     * SCP.php doesn't support SOURCE_CALLBACK because, with that one, we don't know the size, in advance
     */
    //const SOURCE_CALLBACK = 16;

    /**
     * Uploads a file to the SCP server.
     *
     * By default, \phpseclib\Net\SCP::put() does not read from the local filesystem.  $data is dumped directly into $remote_file.
     * So, for example, if you set $data to 'filename.ext' and then do \phpseclib\Net\SCP::get(), you will get a file, twelve bytes
     * long, containing 'filename.ext' as its contents.
     *
     * Setting $mode to self::SOURCE_LOCAL_FILE will change the above behavior.  With self::SOURCE_LOCAL_FILE, $remote_file will
     * contain as many bytes as filename.ext does on your local filesystem.  If your filename.ext is 1MB then that is how
     * large $remote_file will be, as well.
     *
     * Currently, only binary mode is supported.  As such, if the line endings need to be adjusted, you will need to take
     * care of that, yourself.
     *
     * @param string|resource $data
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function put(
        string $remote_file,
        #[\SensitiveParameter] mixed $data,
        int $mode = self::SOURCE_STRING,
        ?\Closure $callback = null
    ): void {
        if (!$this->isAuthenticated()) {
            throw new InvalidStateException('Unable to upload file. Not connected.');
        }

        if (empty($remote_file)) {
            throw new InvalidArgumentException('Remote Filename cannot be blank');
        }

        $this->initExec('scp -t ' . escapeshellarg($remote_file)); // -t = to
        $this->get_scp_response();

        $packet_size = $this->packet_size_client_to_server[self::CHANNEL_EXEC] - 4;

        $remote_file = basename($remote_file);

        switch (true) {
            case is_resource($data):
                $mode = $mode & ~self::SOURCE_LOCAL_FILE;
                $info = stream_get_meta_data($data);
                if (isset($info['wrapper_type']) && $info['wrapper_type'] == 'PHP' && $info['stream_type'] == 'Input') {
                    $fp = fopen('php://memory', 'w+');
                    stream_copy_to_stream($data, $fp);
                    rewind($fp);
                } else {
                    $fp = $data;
                }
                break;
            case $mode & self::SOURCE_LOCAL_FILE:
                if (!is_file($data)) {
                    throw new FileSystemException("$data is not a valid file");
                }
                try {
                    $fp = Files::open($data, 'rb');
                } catch (FileSystemException $e) {
                    $this->close_channel(self::CHANNEL_EXEC, true);
                    throw $e;
                }
        }

        if (isset($fp)) {
            $stat = fstat($fp);
            $size = !empty($stat) ? $stat['size'] : 0;
        } else {
            $size = strlen($data);
        }

        $size = $size < 0 ? ($size & 0x7FFFFFFF) + 0x80000000 : $size;

        $temp = 'C0644 ' . $size . ' ' . $remote_file . "\n";
        $this->send_channel_packet(self::CHANNEL_EXEC, $temp);
        $this->get_scp_response();

        $sent = 0;
        while ($sent < $size) {
            $temp = $mode & self::SOURCE_STRING ? substr($data, $sent, $packet_size) : fread($fp, $packet_size);
            $this->send_channel_packet(self::CHANNEL_EXEC, $temp);
            $sent += strlen($temp);

            if (isset($callback)) {
                $callback($sent);
            }
        }
        $this->close_channel(self::CHANNEL_EXEC, true);

        if ($mode != self::SOURCE_STRING) {
            fclose($fp);
        }
    }

    /**
     * Downloads a file from the SCP server.
     *
     * Returns a string containing the contents of $remote_file if $local_file is left undefined or a boolean false if
     * the operation was unsuccessful.  If $local_file is defined, returns true or false depending on the success of the
     * operation
     *
     * @param string|resource|null $local_file
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function get(string $remote_file, mixed $local_file = null, ?\Closure $progressCallback = null): ?string
    {
        if (!$this->isAuthenticated()) {
            throw new InvalidStateException('Unable to download file. Not connected.');
        }

        $this->initExec('scp -f ' . escapeshellarg($remote_file)); // -f = from

        $this->send_channel_packet(self::CHANNEL_EXEC, chr(0));
        $info = $this->get_scp_response();

        $this->send_channel_packet(self::CHANNEL_EXEC, chr(0));

        if (!preg_match('#(?<perms>[^ ]+) (?<size>\d+) (?<name>.+)#', rtrim($info), $info)) {
            $this->close_channel(self::CHANNEL_EXEC, true);
            throw new UnexpectedSSHMessageException('Response did not meet expected format');
        }

        $fclose_check = false;
        if (is_resource($local_file)) {
            $fp = $local_file;
        } elseif (isset($local_file)) {
            try {
                $fp = Files::open($local_file, 'wb');
            } catch (FileSystemException $e) {
                $this->close_channel(self::CHANNEL_EXEC, true);
                throw $e;
            }
            $fclose_check = true;
            $content = null;
        } else {
            $content = '';
        }

        $size = 0;
        while (true) {
            try {
                $data = $this->get_scp_response(false);
            } catch (SSHChannelExitSignalException $e) {
                if (is_null($local_file)) {
                    $e->partialOutput = $content;
                }
                throw $e;
            }
            // SCP usually seems to split stuff out into 16k chunks
            $length = strlen($data);
            $size += $length;
            $end = $size > $info['size'];
            if ($end) {
                $diff = $size - $info['size'];
                $offset = (int) ($length - $diff);
                if ($data[$offset] === chr(0)) {
                    $data = substr($data, 0, -$diff);
                } else {
                    $type = $data[$offset] === chr(1) ? 'warning' : 'error';
                    $this->close_channel(self::CHANNEL_EXEC, true);
                    throw new FileSystemException("Received a $type from server: " . substr($data, 1));
                }
            }

            if (is_null($local_file)) {
                $content .= $data;
            } else {
                fputs($fp, $data);
            }

            if (isset($progressCallback)) {
                $progressCallback($size);
            }

            if ($end) {
                break;
            }
        }

        $this->close_channel(self::CHANNEL_EXEC, true);

        if ($fclose_check) {
            fclose($fp);
        }

        // if $content isn't set that means a file was written to
        return $content;
    }

    private function get_scp_response(bool $check_for_null = true): string
    {
        $response = $this->get_channel_packet(self::CHANNEL_EXEC, true);
        if (is_bool($response)) {
            throw $this->is_timeout ?
                new TimeoutException('SCP get() timed out') :
                new UnexpectedValueException('Error reading additional SCP data');
        }
        // per https://goteleport.com/blog/scp-familiar-simple-insecure-slow/ non-zero responses mean there are errors
        if ($check_for_null && in_array($response[0], ["\1", "\2"])) {
            $type = $response[0] === chr(1) ? 'warning' : 'error';
            $this->close_channel(self::CHANNEL_EXEC, true);
            throw new FileSystemException("Received a $type from server: " . substr($response, 1));
        }
        return $response;
    }
}
<?php

/**
 * Pure-PHP implementation of SFTP.
 *
 * PHP version 8.1+
 *
 * Supports SFTPv2/3/4/5/6. Defaults to v3.
 *
 * The API for this library is modeled after the API from PHP's {@link http://php.net/book.ftp FTP extension}.
 *
 * Here's a short example of how to use this library:
 * <code>
 * <?php
 *    include 'vendor/autoload.php';
 *
 *    $sftp = new \phpseclib4\Net\SFTP('www.domain.tld');
 *    if (!$sftp->login('username', 'password')) {
 *        exit('Login Failed');
 *    }
 *
 *    echo $sftp->pwd() . "\r\n";
 *    $sftp->put('filename.ext', 'hello, world!');
 *    print_r($sftp->nlist());
 * ?>
 * </code>
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2009-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Net;

use phpseclib4\Common\Functions\{Files, Strings};
use phpseclib4\Exception\{
    ConnectionClosedException,
    FileSystemException,
    InvalidArgumentException,
    InvalidModeException,
    InvalidStateException,
    ServiceUnavailableException,
    SSHChannelExitSignalException,
    TimeoutException,
    UnexpectedSFTPPacketException,
    UnexpectedValueException,
    UnsupportedValueException
};
use phpseclib4\Net\SFTP\{
    Attribute,
    FileType,
    OpenFlag,
    OpenFlag5,
    PacketType as SFTPPacketType,
    StatusCode
};
use phpseclib4\Net\SSH2\MessageType as SSH2MessageType;

/**
 * Pure-PHP implementations of SFTP.
 *
 * @author Jim Wigginton <terrafrost@php.net>
 */
class SFTP extends SSH2
{
    /**
     * SFTP channel constant
     *
     * \phpseclib4\Net\SSH2::exec() uses 0 and \phpseclib4\Net\SSH2::read() / \phpseclib4\Net\SSH2::write() use 1.
     *
     * @see \phpseclib4\Net\SSH2::send_channel_packet()
     * @see \phpseclib4\Net\SSH2::get_channel_packet()
     */
    public const CHANNEL = 0x100;

    /**
     * Reads data from a local file.
     *
     * @see \phpseclib4\Net\SFTP::put()
     */
    public const SOURCE_LOCAL_FILE = 1;
    /**
     * Reads data from a string.
     *
     * @see \phpseclib4\Net\SFTP::put()
     */
    // this value isn't really used anymore but i'm keeping it reserved for historical reasons
    public const SOURCE_STRING = 2;
    /**
     * Reads data from callback:
     * function callback($length) returns string to proceed, null for EOF
     *
     * @see \phpseclib4\Net\SFTP::put()
     */
    public const SOURCE_CALLBACK = 16;
    /**
     * Resumes an upload
     *
     * @see \phpseclib4\Net\SFTP::put()
     */
    public const RESUME = 4;
    /**
     * Append a local file to an already existing remote file
     *
     * @see \phpseclib4\Net\SFTP::put()
     */
    public const RESUME_START = 8;

    /**
     * The Request ID
     *
     * The request ID exists in the off chance that a packet is sent out-of-order.  Of course, this library doesn't support
     * concurrent actions, so it's somewhat academic, here.
     *
     * @see self::_send_sftp_packet()
     */
    private bool $use_request_id = false;

    /**
     * The Packet Type
     *
     * The request ID exists in the off chance that a packet is sent out-of-order.  Of course, this library doesn't support
     * concurrent actions, so it's somewhat academic, here.
     *
     * @see self::_get_sftp_packet()
     */
    private int $packet_type = -1;

    /**
     * Packet Buffer
     *
     * @see self::_get_sftp_packet()
     */
    private string $packet_buffer = '';

    /**
     * Extensions supported by the server
     *
     * @see self::_initChannel()
     */
    private array $extensions = [];

    /**
     * Server SFTP version
     *
     * @see self::_initChannel()
     */
    private int $version;

    /**
     * Default Server SFTP version
     *
     * @see self::_initChannel()
     */
    private int $defaultVersion;

    /**
     * Preferred SFTP version
     *
     * @see self::_initChannel()
     */
    private int $preferredVersion = 3;

    /**
     * Current working directory
     *
     * bool(false) means we need to initiate the connection
     * bool(true) means we don't have a string value but no connection initialization is needed
     * otherwise it's a string
     *
     * @see self::realpath()
     * @see self::chdir()
     */
    private string|bool $pwd = false;

    /**
     * Packet Type Log
     *
     * @see self::getLog()
     */
    private array $packet_type_log = [];

    /**
     * Packet Log
     *
     * @see self::getLog()
     */
    private array $packet_log = [];

    /**
     * Real-time log file pointer
     *
     * @see self::_append_log()
     * @var resource|closed-resource
     */
    private mixed $realtime_log_file;

    /**
     * Real-time log file size
     *
     * @see self::_append_log()
     */
    private ?int $realtime_log_size;

    /**
     * Real-time log file wrap boolean
     *
     * @see self::_append_log()
     */
    private ?bool $realtime_log_wrap;

    /**
     * Current log size
     *
     * Should never exceed self::LOG_MAX_SIZE
     */
    private ?int $log_size;

    /**
     * Error information
     *
     * @see self::getSFTPErrors()
     * @see self::getLastSFTPError()
     */
    private array $sftp_errors = [];

    /**
     * Stat Cache
     *
     * Rather than always having to open a directory and close it immediately there after to see if a file is a directory
     * we'll cache the results.
     *
     * @see self::_update_stat_cache()
     * @see self::_remove_from_stat_cache()
     * @see self::_query_stat_cache()
     */
    private array $stat_cache = [];

    /**
     * Max SFTP Packet Size
     *
     * @see self::__construct()
     * @see self::get()
     */
    private int $max_sftp_packet;

    /**
     * Stat Cache Flag
     *
     * @see self::disableStatCache()
     * @see self::enableStatCache()
     */
    private bool $use_stat_cache = true;

    /**
     * Sort Options
     *
     * @see self::_comparator()
     * @see self::setListOrder()
     */
    private array $sortOptions = [];

    /**
     * Canonicalization Flag
     *
     * Determines whether or not paths should be canonicalized before being
     * passed on to the remote server.
     *
     * @see self::enablePathCanonicalization()
     * @see self::disablePathCanonicalization()
     * @see self::realpath()
     */
    private bool $canonicalize_paths = true;

    /**
     * Request Buffers
     *
     * @see self::_get_sftp_packet()
     */
    private array $requestBuffer = [];

    /**
     * Preserve timestamps on file downloads / uploads
     *
     * @see self::get()
     * @see self::put()
     */
    private bool $preserveTime = false;

    /**
     * Arbitrary Length Packets Flag
     *
     * Determines whether or not packets of any length should be allowed,
     * in cases where the server chooses the packet length (such as
     * directory listings). By default, packets are only allowed to be
     * 256 * 1024 bytes (SFTP_MAX_MSG_LENGTH from OpenSSH's sftp-common.h)
     *
     * @see self::enableArbitraryLengthPackets()
     * @see self::_get_sftp_packet()
     */
    private bool $allow_arbitrary_length_packets = false;

    /**
     * Was the last packet due to the channels being closed or not?
     *
     * @see self::get()
     * @see self::get_sftp_packet()
     */
    private bool $channel_close = false;

    /**
     * Has the SFTP channel been partially negotiated?
     */
    private bool $partial_init = false;

    private int $queueSize = 32;
    private int $uploadQueueSize = 1024;

    private string $lastErrorResponse;

    /**
     * Default Constructor.
     *
     * Connects to an SFTP server
     *
     * @param string|resource $host
     */
    public function __construct(mixed $host, int $port = 22, int $timeout = 10)
    {
        parent::__construct($host, $port, $timeout);

        $this->max_sftp_packet = 1 << 15;

        if (defined('NET_SFTP_QUEUE_SIZE')) {
            $this->queueSize = NET_SFTP_QUEUE_SIZE;
        }
        if (defined('NET_SFTP_UPLOAD_QUEUE_SIZE')) {
            $this->uploadQueueSize = NET_SFTP_UPLOAD_QUEUE_SIZE;
        }
    }

    /**
     * Check a few things before SFTP functions are called
     */
    private function precheck(): void
    {
        if (!($this->bitmap & SSH2::MASK_LOGIN)) {
            throw new InvalidStateException("Function should not be called before you've logged in");
        }

        if ($this->pwd === false) {
            $this->init_sftp_connection();
        }
    }

    /**
     * Partially initialize an SFTP connection
     */
    private function partial_init_sftp_connection(): void
    {
        $response = $this->open_channel(self::CHANNEL, true);
        if ($response === true && $this->isTimeout()) {
            throw new TimeoutException('Attempt to (re)connect timed out');
        }

        $packet = Strings::packSSH2(
            'CNsbs',
            SSH2MessageType::CHANNEL_REQUEST,
            $this->server_channels[self::CHANNEL],
            'subsystem',
            true,
            'sftp'
        );
        $this->send_binary_packet($packet);

        $this->channel_status[self::CHANNEL] = SSH2MessageType::CHANNEL_REQUEST;

        $response = $this->get_channel_packet(self::CHANNEL, true);
        if ($response === false) {
            // from PuTTY's psftp.exe
            $command = "test -x /usr/lib/sftp-server && exec /usr/lib/sftp-server\n" .
                "test -x /usr/local/lib/sftp-server && exec /usr/local/lib/sftp-server\n" .
                "exec sftp-server";
            // we don't do $this->exec($command, false) because exec() operates on a different channel and plus the SSH_MSG_CHANNEL_OPEN that exec() does
            // is redundant
            $packet = Strings::packSSH2(
                'CNsCs',
                SSH2MessageType::CHANNEL_REQUEST,
                $this->server_channels[self::CHANNEL],
                'exec',
                1,
                $command
            );
            $this->send_binary_packet($packet);

            $this->channel_status[self::CHANNEL] = SSH2MessageType::CHANNEL_REQUEST;

            $response = $this->get_channel_packet(self::CHANNEL, true);
            if ($response === false) {
                throw new UnexpectedValueException('Received failure message when trying to open SFTP channel');
            }
        } elseif ($response === true && $this->isTimeout()) {
            throw new TimeoutException('Attempt to (re)connect timed out');
        }

        $this->channel_status[self::CHANNEL] = SSH2MessageType::CHANNEL_DATA;
        $this->send_sftp_packet(SFTPPacketType::INIT, "\0\0\0\3");

        $response = $this->get_sftp_packet_or_error([SFTPPacketType::VERSION]);

        $this->use_request_id = true;

        [$this->defaultVersion] = Strings::unpackSSH2('N', $response);
        while (!empty($response)) {
            [$key, $value] = Strings::unpackSSH2('ss', $response);
            $this->extensions[$key] = $value;
        }

        $this->partial_init = true;
    }

    /**
     * (Re)initializes the SFTP channel
     */
    private function init_sftp_connection(): void
    {
        if (!$this->partial_init) {
            $this->partial_init_sftp_connection();
        }

        /*
         A Note on SFTPv4/5/6 support:
         <http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-5.1> states the following:

         "If the client wishes to interoperate with servers that support noncontiguous version
          numbers it SHOULD send '3'"

         Given that the server only sends its version number after the client has already done so, the above
         seems to be suggesting that v3 should be the default version.  This makes sense given that v3 is the
         most popular.

         <http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-5.5> states the following;

         "If the server did not send the "versions" extension, or the version-from-list was not included, the
          server MAY send a status response describing the failure, but MUST then close the channel without
          processing any further requests."

         So what do you do if you have a client whose initial SSH_FXP_INIT packet says it implements v3 and
         a server whose initial SSH_FXP_VERSION reply says it implements v4 and only v4?  If it only implements
         v4, the "versions" extension is likely not going to have been sent so version re-negotiation as discussed
         in draft-ietf-secsh-filexfer-13 would be quite impossible.  As such, what \phpseclib4\Net\SFTP would do is close the
         channel and reopen it with a new and updated SSH_FXP_INIT packet.
        */
        $this->version = $this->defaultVersion;
        if (isset($this->extensions['versions']) && (!$this->preferredVersion || $this->preferredVersion != $this->version)) {
            $versions = explode(',', $this->extensions['versions']);
            $supported = [6, 5, 4];
            if ($this->preferredVersion) {
                $supported = array_diff($supported, [$this->preferredVersion]);
                array_unshift($supported, $this->preferredVersion);
            }
            foreach ($supported as $ver) {
                if (in_array($ver, $versions)) {
                    if ($ver === $this->version) {
                        break;
                    }
                    $this->version = (int) $ver;
                    $packet = Strings::packSSH2('ss', 'version-select', "$ver");
                    $this->send_sftp_packet(SFTPPacketType::EXTENDED, $packet);
                    $response = $this->get_sftp_packet_or_error([SFTPPacketType::STATUS]);
                    [$status] = Strings::unpackSSH2('N', $response);
                    if ($status != StatusCode::OK) {
                        // we can't do $this->throwStatusError($response, $status); because that throws
                        // FileSystemException
                        throw new UnexpectedValueException(
                            'Expected SSH_FX_OK. Got ' . StatusCode::getConstantNameByValue($status)
                        );
                    }
                    break;
                }
            }
        }

        /*
         SFTPv4+ defines a 'newline' extension.  SFTPv3 seems to have unofficial support for it via 'newline@vandyke.com',
         however, I'm not sure what 'newline@vandyke.com' is supposed to do (the fact that it's unofficial means that it's
         not in the official SFTPv3 specs) and 'newline@vandyke.com' / 'newline' are likely not drop-in substitutes for
         one another due to the fact that 'newline' comes with a SSH_FXF_TEXT bitmask whereas it seems unlikely that
         'newline@vandyke.com' would.
        */
        /*
        if (isset($this->extensions['newline@vandyke.com'])) {
            $this->extensions['newline'] = $this->extensions['newline@vandyke.com'];
            unset($this->extensions['newline@vandyke.com']);
        }
        */
        if ($this->version < 2 || $this->version > 6) {
            throw new UnsupportedValueException('The only SFTP versions phpseclib supports are >= 2 and <= 6 - not ' . $this->version);
        }

        $this->pwd = true;
        try {
            $this->pwd = $this->realpath('.');
        } catch (FileSystemException) {
            $this->pwd = false;
        } catch (UnexpectedSFTPPacketException $e) {
            if (!$this->canonicalize_paths) {
                throw $e;
            }
            $this->canonicalize_paths = false;
            $this->reset_sftp();
            $this->init_sftp_connection();
            return;
        }

        $this->update_stat_cache($this->pwd, []);
    }

    /**
     * Disable the stat cache
     */
    public function disableStatCache(): void
    {
        $this->use_stat_cache = false;
    }

    /**
     * Enable the stat cache
     */
    public function enableStatCache(): void
    {
        $this->use_stat_cache = true;
    }

    /**
     * Clear the stat cache
     */
    public function clearStatCache(): void
    {
        $this->stat_cache = [];
    }

    /**
     * Enable path canonicalization
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function enablePathCanonicalization(): void
    {
        $this->canonicalize_paths = true;
    }

    /**
     * Disable path canonicalization
     *
     * If this is enabled then $sftp->pwd() will not return the canonicalized absolute path
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function disablePathCanonicalization(): void
    {
        $this->canonicalize_paths = false;
    }

    /**
     * Enable arbitrary length packets
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function enableArbitraryLengthPackets(): void
    {
        $this->allow_arbitrary_length_packets = true;
    }

    /**
     * Disable arbitrary length packets
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function disableArbitraryLengthPackets(): void
    {
        $this->allow_arbitrary_length_packets = false;
    }

    /**
     * Returns the current directory name
     *
     * @psalm-suppress InvalidReturnType, InvalidReturnStatement
     */
    public function pwd(): string
    {
        $this->precheck();

        return $this->pwd;
    }

    private function throwStatusError(string $response, int $status = -1): FileSystemException
    {
        if ($status == -1) {
            [$status] = Strings::unpackSSH2('N', $response);
        }

        $this->lastErrorResponse = $response;

        $error = StatusCode::getConstantNameByValue($status);

        if ($this->version > 2) {
            [$message] = Strings::unpackSSH2('s', $response);
            $output = "Error from server (SSH_FX_$error)";
            if (strlen($message)) {
                $output .= ": $message";
            }
            return new FileSystemException($output, $status);
        }
        return new FileSystemException("Error from server ($error)", $status);
    }

    /**
     * Logs errors
     */
    private function logError(string $response, int $status, string $filename): void
    {
        $error = 'SSH_FX_' . StatusCode::getConstantNameByValue($status);

        if ($this->version > 2) {
            [$message] = Strings::unpackSSH2('s', $response);
            $this->sftp_errors[] = isset($filename) ? "$filename ($error): $message" : "$error: $message";
        } else {
            $this->sftp_errors[] = isset($filename) ? "$filename ($error)" : $error;
        }
    }

    /**
     * Canonicalize the Server-Side Path Name
     *
     * SFTP doesn't provide a mechanism by which the current working directory can be changed, so we'll emulate it.  Returns
     * the absolute (canonicalized) path.
     *
     * If canonicalize_paths has been disabled using disablePathCanonicalization(), $path is returned as-is.
     *
     * @see    self::chdir()
     * @see    self::disablePathCanonicalization()
     */
    public function realpath(string $path): string
    {
        $this->precheck();

        $path = (string) $path;

        if (!$this->canonicalize_paths) {
            if ($this->pwd === true) {
                return '.';
            }
            if (!strlen($path) || $path[0] != '/') {
                $path = $this->pwd . '/' . $path;
            }
            $parts = explode('/', $path);
            $afterPWD = $beforePWD = [];
            foreach ($parts as $part) {
                switch ($part) {
                    //case '': // some SFTP servers /require/ double /'s. see https://github.com/phpseclib/phpseclib/pull/1137
                    case '.':
                        break;
                    case '..':
                        if (!empty($afterPWD)) {
                            array_pop($afterPWD);
                        } else {
                            $beforePWD[] = '..';
                        }
                        break;
                    default:
                        $afterPWD[] = $part;
                }
            }
            $beforePWD = count($beforePWD) ? implode('/', $beforePWD) : '.';
            return $beforePWD . '/' . implode('/', $afterPWD);
        }

        if ($this->pwd === true) {
            // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.9
            $this->send_sftp_packet(SFTPPacketType::REALPATH, Strings::packSSH2('s', $path));
            $response = $this->get_sftp_packet_or_error([SFTPPacketType::NAME, SFTPPacketType::STATUS]);
            // although SSH_FXP_NAME is implemented differently in SFTPv3 than it is in SFTPv4+, the following
            // should work on all SFTP versions since the only part of the SSH_FXP_NAME packet the following looks
            // at is the first part and that part is defined the same in SFTP versions 3 through 6.
            [, $filename] = Strings::unpackSSH2('Ns', $response);
            return $filename;
        }

        if (!strlen($path) || $path[0] != '/') {
            $path = $this->pwd . '/' . $path;
        }

        $path = explode('/', $path);
        $new = [];
        foreach ($path as $dir) {
            if (!strlen($dir)) {
                continue;
            }
            switch ($dir) {
                case '..':
                    array_pop($new);
                // no break
                case '.':
                    break;
                default:
                    $new[] = $dir;
            }
        }

        return '/' . implode('/', $new);
    }

    /**
     * Changes the current directory
     */
    public function chdir(string $dir): void
    {
        $this->precheck();

        $dir = (string) $dir;

        // assume current dir if $dir is empty
        if ($dir === '') {
            $dir = './';
            // suffix a slash if needed
        } elseif ($dir[-1] != '/') {
            $dir .= '/';
        }

        $dir = $this->realpath($dir);

        // confirm that $dir is, in fact, a valid directory
        if ($this->use_stat_cache && is_array($this->query_stat_cache($dir))) {
            $this->pwd = $dir;
            return;
        }

        // we could do a stat on the alleged $dir to see if it's a directory but that doesn't tell us
        // the currently logged in user has the appropriate permissions or not. maybe you could see if
        // the file's uid / gid match the currently logged in user's uid / gid but how there's no easy
        // way to get those with SFTP

        $this->send_sftp_packet(SFTPPacketType::OPENDIR, Strings::packSSH2('s', $dir));
        // see \phpseclib4\Net\SFTP::nlist() for a more thorough explanation of the following
        $response = $this->get_sftp_packet_or_error([SFTPPacketType::HANDLE, SFTPPacketType::STATUS]);
        $handle = substr($response, 4);
        $this->close_handle($handle);

        $this->update_stat_cache($dir, []);

        $this->pwd = $dir;
    }

    /**
     * Returns a list of files in the given directory
     */
    public function nlist(string $dir = '.', bool $recursive = false): array
    {
        return $this->nlist_helper($dir, $recursive, '');
    }

    /**
     * Helper method for nlist
     */
    private function nlist_helper(string $dir, bool $recursive, string $relativeDir): array
    {
        $files = $this->readlist($dir, false);

        if (!$recursive) {
            return $files;
        }

        $result = [];
        foreach ($files as $value) {
            if ($value == '.' || $value == '..') {
                $result[] = $relativeDir . $value;
                continue;
            }

            $path = $this->realpath($dir . '/' . $value);
            if (is_array($this->query_stat_cache($path))) {
                try {
                    $temp = $this->nlist_helper($dir . '/' . $value, true, $relativeDir . $value . '/');
                } catch (FileSystemException $e) {
                    $this->logError($this->lastErrorResponse, $e->getCode(), "OPENDIR $path");
                    $temp = [];
                }

                $result = array_merge($result, $temp);
            } else {
                $result[] = $relativeDir . $value;
            }
        }

        return $result;
    }

    /**
     * Returns a detailed list of files in the given directory
     *
     * @param ?\Closure(string, string, array):void $onFile Callback invoked for each file found. The parameters are: directory, filename, attributes.
     */
    public function rawlist(string $dir = '.', bool $recursive = false, ?\Closure $onFile = null): array
    {
        try {
            $files = $this->readlist($dir, true, $onFile);
        } catch (FileSystemException $e) {
            if (!$recursive) {
                throw $e;
            }
            $files = [];
            $this->logError($this->lastErrorResponse, $e->getCode(), "OPENDIR $dir");
        }
        if (!$recursive) {
            return $files;
        }

        static $depth = 0;

        foreach ($files as $key => $value) {
            if ($depth != 0 && $key == '..') {
                unset($files[$key]);
                continue;
            }
            $is_directory = false;
            if ($key != '.' && $key != '..') {
                if ($this->use_stat_cache) {
                    $is_directory = is_array($this->query_stat_cache($this->realpath($dir . '/' . $key)));
                } else {
                    $stat = $this->lstat($dir . '/' . $key);
                    $is_directory = $stat && $stat['type'] === FileType::DIRECTORY;
                }
            }

            if ($is_directory) {
                $depth++;
                $files[$key] = $this->rawlist($dir . '/' . $key, true, $onFile);
                $depth--;
            } else {
                $files[$key] = (object) $value;
            }
        }

        return $files;
    }

    /**
     * Reads a list, be it detailed or not, of files in the given directory
     *
     * @param null|\Closure(string, string, array):void $onFile Callback invoked for each file found. The parameters are: directory, filename, attributes.
     */
    private function readlist(string $dir, bool $raw = true, ?\Closure $onFile = null): array
    {
        $this->precheck();

        $dir = $this->realpath($dir . '/');

        // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.2
        $this->send_sftp_packet(SFTPPacketType::OPENDIR, Strings::packSSH2('s', $dir));
        $response = $this->get_sftp_packet_or_error([SFTPPacketType::HANDLE, SFTPPacketType::STATUS]);
        // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.2
        // since 'handle' is the last field in the SSH_FXP_HANDLE packet, we'll just remove the first four bytes that
        // represent the length of the string and leave it at that
        $handle = substr($response, 4);

        $this->update_stat_cache($dir, []);

        $contents = [];
        while (true) {
            // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.2.2
            // why multiple SSH_FXP_READDIR packets would be sent when the response to a single one can span arbitrarily many
            // SSH_MSG_CHANNEL_DATA messages is not known to me.
            $this->send_sftp_packet(SFTPPacketType::READDIR, Strings::packSSH2('s', $handle));
            $response = $this->get_sftp_packet_or_error([SFTPPacketType::NAME, SFTPPacketType::STATUS], StatusCode::EOF);
            if ($this->packet_type == SFTPPacketType::STATUS) {
                break;
            }
            [$count] = Strings::unpackSSH2('N', $response);
            for ($i = 0; $i < $count; $i++) {
                [$shortname] = Strings::unpackSSH2('s', $response);
                // SFTPv4 "removed the long filename from the names structure-- it can now be
                //         built from information available in the attrs structure."
                if ($this->version < 4) {
                    [$longname] = Strings::unpackSSH2('s', $response);
                }
                $attributes = self::parseAttributes($this->version, $response);
                if (!isset($attributes['type']) && $this->version < 4) {
                    $fileType = self::parseLongname($longname);
                    if ($fileType) {
                        $attributes['type'] = $fileType;
                    }
                }
                $contents[$shortname] = $attributes + ['filename' => $shortname];

                if (isset($attributes['type']) && $attributes['type'] == FileType::DIRECTORY && ($shortname != '.' && $shortname != '..')) {
                    $this->update_stat_cache($dir . '/' . $shortname, []);
                } else {
                    $temp = $shortname == '..' ?
                        $this->realpath($dir . '/..') . '/.' :
                        $dir . '/' . $shortname;
                    $this->update_stat_cache($temp, (object) ['lstat' => $attributes]);
                }
                // SFTPv6 has an optional boolean end-of-list field, but we'll ignore that, since the
                // final SSH_FXP_STATUS packet should tell us that, already.

                if ($onFile) {
                    $onFile($dir, $shortname, $attributes);
                }
            }
        }

        $this->close_handle($handle);

        if (count($this->sortOptions)) {
            uasort($contents, $this->comparator(...));
        }

        return $raw ? $contents : array_map('strval', array_keys($contents));
    }

    /**
     * Compares two rawlist entries using parameters set by setListOrder()
     *
     * Intended for use with uasort()
     */
    private function comparator(array $a, array $b): int
    {
        switch (true) {
            case $a['filename'] === '.' || $b['filename'] === '.':
                if ($a['filename'] === $b['filename']) {
                    return 0;
                }
                return $a['filename'] === '.' ? -1 : 1;
            case $a['filename'] === '..' || $b['filename'] === '..':
                if ($a['filename'] === $b['filename']) {
                    return 0;
                }
                return $a['filename'] === '..' ? -1 : 1;
            case isset($a['type']) && $a['type'] === FileType::DIRECTORY:
                if (!isset($b['type'])) {
                    return 1;
                }
                if ($b['type'] !== $a['type']) {
                    return -1;
                }
                break;
            case isset($b['type']) && $b['type'] === FileType::DIRECTORY:
                return 1;
        }
        foreach ($this->sortOptions as $sort => $order) {
            if (!isset($a[$sort]) || !isset($b[$sort])) {
                if (isset($a[$sort])) {
                    return -1;
                }
                if (isset($b[$sort])) {
                    return 1;
                }
                return 0;
            }
            switch ($sort) {
                case 'filename':
                    $result = strcasecmp($a['filename'], $b['filename']);
                    if ($result) {
                        return $order === SORT_DESC ? -$result : $result;
                    }
                    break;
                case 'mode':
                    $a[$sort] &= 0o7777;
                    $b[$sort] &= 0o7777;
                // no break
                default:
                    if ($a[$sort] === $b[$sort]) {
                        break;
                    }
                    return $order === SORT_ASC ? $a[$sort] - $b[$sort] : $b[$sort] - $a[$sort];
            }
        }
        return 0;
    }

    /**
     * Defines how nlist() and rawlist() will be sorted - if at all.
     *
     * If sorting is enabled directories and files will be sorted independently with
     * directories appearing before files in the resultant array that is returned.
     *
     * Any parameter returned by stat is a valid sort parameter for this function.
     * Filename comparisons are case insensitive.
     *
     * Examples:
     *
     * $sftp->setListOrder('filename', SORT_ASC);
     * $sftp->setListOrder('size', SORT_DESC, 'filename', SORT_ASC);
     * $sftp->setListOrder(true);
     *    Separates directories from files but doesn't do any sorting beyond that
     * $sftp->setListOrder();
     *    Don't do any sort of sorting
     */
    public function setListOrder(string|int ...$args): void
    {
        $this->sortOptions = [];
        if (empty($args)) {
            return;
        }
        $len = count($args) & 0x7FFFFFFE;
        for ($i = 0; $i < $len; $i += 2) {
            $this->sortOptions[$args[$i]] = $args[$i + 1];
        }
        if (!count($this->sortOptions)) {
            $this->sortOptions = ['bogus' => true];
        }
    }

    /**
     * Save files / directories to cache
     */
    private function update_stat_cache(string $path, object|array $value): void
    {
        if ($this->use_stat_cache === false) {
            return;
        }

        // preg_replace('#^/|/(?=/)|/$#', '', $dir) == str_replace('//', '/', trim($path, '/'))
        $dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $path));

        $temp = &$this->stat_cache;
        $max = count($dirs) - 1;
        foreach ($dirs as $i => $dir) {
            // if $temp is an object that means one of two things.
            //  1. a file was deleted and changed to a directory behind phpseclib's back
            //  2. it's a symlink. when lstat is done it's unclear what it's a symlink to
            if (is_object($temp)) {
                $temp = [];
            }
            if (!isset($temp[$dir])) {
                $temp[$dir] = [];
            }
            if ($i === $max) {
                if (is_object($temp[$dir]) && is_object($value)) {
                    if (!isset($value->stat) && isset($temp[$dir]->stat)) {
                        $value->stat = $temp[$dir]->stat;
                    }
                    if (!isset($value->lstat) && isset($temp[$dir]->lstat)) {
                        $value->lstat = $temp[$dir]->lstat;
                    }
                }
                $temp[$dir] = $value;
                break;
            }
            $temp = &$temp[$dir];
        }
    }

    /**
     * Remove files / directories from cache
     */
    private function remove_from_stat_cache(string $path): void
    {
        $dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $path));

        $temp = &$this->stat_cache;
        $max = count($dirs) - 1;
        foreach ($dirs as $i => $dir) {
            if (!is_array($temp)) {
                return;
            }
            if ($i === $max) {
                unset($temp[$dir]);
                return;
            }
            if (!isset($temp[$dir])) {
                return;
            }
            $temp = &$temp[$dir];
        }
    }

    /**
     * Checks cache for path
     *
     * Returns object for files, array for directories and null if it's neither
     */
    private function query_stat_cache(string $path): object|array|null
    {
        $dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $path));

        $temp = &$this->stat_cache;
        foreach ($dirs as $dir) {
            if (!is_array($temp)) {
                return null;
            }
            if (!isset($temp[$dir])) {
                return null;
            }
            $temp = &$temp[$dir];
        }
        return $temp;
    }

    /**
     * Returns general information about a file.
     *
     * Returns an array on success and false otherwise.
     */
    public function stat(string $filename): array
    {
        $this->precheck();

        $filename = $this->realpath($filename);

        if ($this->use_stat_cache) {
            $result = $this->query_stat_cache($filename);
            if (is_array($result) && isset($result['.']) && isset($result['.']->stat)) {
                return $result['.']->stat;
            }
            if (is_object($result) && isset($result->stat)) {
                return $result->stat;
            }
        }

        try {
            $stat = $this->stat_helper($filename, SFTPPacketType::STAT);
        } catch (\Exception $e) {
            $this->remove_from_stat_cache($filename);
            throw $e;
        }
        if (isset($stat['type'])) {
            if ($stat['type'] == FileType::DIRECTORY) {
                $filename .= '/.';
            }
            $this->update_stat_cache($filename, (object) ['stat' => $stat]);
            return $stat;
        }

        $pwd = $this->pwd;
        $stat['type'] = $this->chdir($filename) ?
            FileType::DIRECTORY :
            FileType::REGULAR;
        $this->pwd = $pwd;

        if ($stat['type'] == FileType::DIRECTORY) {
            $filename .= '/.';
        }
        $this->update_stat_cache($filename, (object) ['stat' => $stat]);

        return $stat;
    }

    /**
     * Returns general information about a file or symbolic link.
     *
     * Returns an array on success and false otherwise.
     */
    public function lstat(string $filename): array
    {
        $this->precheck();

        $filename = $this->realpath($filename);

        if ($this->use_stat_cache) {
            $result = $this->query_stat_cache($filename);
            if (is_array($result) && isset($result['.']) && isset($result['.']->lstat)) {
                return $result['.']->lstat;
            }
            if (is_object($result) && isset($result->lstat)) {
                return $result->lstat;
            }
        }

        try {
            $lstat = $this->stat_helper($filename, SFTPPacketType::LSTAT);
        } catch (\Exception $e) {
            $this->remove_from_stat_cache($filename);
            throw $e;
        }
        if (isset($lstat['type'])) {
            if ($lstat['type'] == FileType::DIRECTORY) {
                $filename .= '/.';
            }
            $this->update_stat_cache($filename, (object) ['lstat' => $lstat]);
            return $lstat;
        }

        $stat = $this->stat_helper($filename, SFTPPacketType::STAT);

        if ($lstat != $stat) {
            $lstat = array_merge($lstat, ['type' => FileType::SYMLINK]);
            $this->update_stat_cache($filename, (object) ['lstat' => $lstat]);
            return $stat;
        }

        $pwd = $this->pwd;
        $lstat['type'] = $this->chdir($filename) ?
            FileType::DIRECTORY :
            FileType::REGULAR;
        $this->pwd = $pwd;

        if ($lstat['type'] == FileType::DIRECTORY) {
            $filename .= '/.';
        }
        $this->update_stat_cache($filename, (object) ['lstat' => $lstat]);

        return $lstat;
    }

    /**
     * Returns general information about a file or symbolic link
     *
     * Determines information without calling \phpseclib4\Net\SFTP::realpath().
     * The second parameter can be either SFTPPacketType::STAT or SFTPPacketType::LSTAT.
     */
    private function stat_helper(string $filename, int $type): array
    {
        // SFTPv4+ adds an additional 32-bit integer field - flags - to the following:
        $packet = Strings::packSSH2('s', $filename);
        $this->send_sftp_packet($type, $packet);
        $response = $this->get_sftp_packet_or_error([SFTPPacketType::ATTRS, SFTPPacketType::STATUS]);
        return self::parseAttributes($this->version, $response);
    }

    /**
     * Truncates a file to a given length
     */
    public function truncate(string $filename, int $new_size): void
    {
        $this->precheck();

        $attr = Strings::packSSH2('NQ', Attribute::SIZE, $new_size);

        $this->setstat($filename, $attr, false);
    }

    /**
     * Sets access and modification time of file.
     *
     * If the file does not exist, it will be created.
     */
    public function touch(string $filename, ?int $time = null, ?int $atime = null): void
    {
        $this->precheck();

        $filename = $this->realpath($filename);

        if (!isset($time)) {
            $time = time();
        }
        if (!isset($atime)) {
            $atime = $time;
        }

        $attr = $this->version < 4 ?
            pack('N3', Attribute::ACCESSTIME, $atime, $time) :
            Strings::packSSH2('NQ2', Attribute::ACCESSTIME | Attribute::MODIFYTIME, $atime, $time);

        $packet = Strings::packSSH2('s', $filename);
        $packet .= $this->version >= 5 ?
            pack('N2', 0, OpenFlag5::OPEN_EXISTING) :
            pack('N', OpenFlag::WRITE | OpenFlag::CREATE | OpenFlag::EXCL);
        $packet .= $attr;

        $this->send_sftp_packet(SFTPPacketType::OPEN, $packet);
        $response = $this->get_sftp_packet_or_error([SFTPPacketType::HANDLE, SFTPPacketType::STATUS]);
        $this->close_handle(substr($response, 4));

        $this->setstat($filename, $attr, false);
    }

    /**
     * Changes file or directory owner
     *
     * $uid should be an int for SFTPv3 and a string for SFTPv4+. Ideally the string
     * would be of the form "user@dns_domain" but it does not need to be.
     * `$sftp->getSupportedVersions()['version']` will return the specific version
     * that's being used.
     */
    public function chown(string $filename, int|string $uid, bool $recursive = false): void
    {
        $this->precheck();

        /*
         quoting <https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.5>,

         "To avoid a representation that is tied to a particular underlying
          implementation at the client or server, the use of UTF-8 strings has
          been chosen.  The string should be of the form "user@dns_domain".
          This will allow for a client and server that do not use the same
          local representation the ability to translate to a common syntax that
          can be interpreted by both.  In the case where there is no
          translation available to the client or server, the attribute value
          must be constructed without the "@"."

         phpseclib _could_ auto append the dns_domain to $uid BUT what if it shouldn't
         have one? phpseclib would have no way of knowing so rather than guess phpseclib
         will just use whatever value the user provided
        */

        $attr = $this->version < 4 ?
            // quoting <http://www.kernel.org/doc/man-pages/online/pages/man2/chown.2.html>,
            // "if the owner or group is specified as -1, then that ID is not changed"
            pack('N3', Attribute::UIDGID, $uid, -1) :
            // quoting <https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.5>,
            // "If either the owner or group field is zero length, the field should be
            //  considered absent, and no change should be made to that specific field
            //  during a modification operation"
            Strings::packSSH2('Nss', Attribute::OWNERGROUP, $uid, '');

        $this->setstat($filename, $attr, $recursive);
    }

    /**
     * Changes file or directory group
     *
     * $gid should be an int for SFTPv3 and a string for SFTPv4+. Ideally the string
     * would be of the form "user@dns_domain" but it does not need to be.
     * `$sftp->getSupportedVersions()['version']` will return the specific version
     * that's being used.
     *
     * Returns true on success or false on error.
     */
    public function chgrp(string $filename, int|string $gid, bool $recursive = false): void
    {
        $this->precheck();

        $attr = $this->version < 4 ?
            pack('N3', Attribute::UIDGID, -1, $gid) :
            Strings::packSSH2('Nss', Attribute::OWNERGROUP, '', $gid);

        $this->setstat($filename, $attr, $recursive);
    }

    /**
     * Set permissions on a file
     */
    public function chmod(string $filename, int $mode, bool $recursive = false): void
    {
        $this->precheck();

        $attr = pack('N2', Attribute::PERMISSIONS, $mode & 0o7777);
        $this->setstat($filename, $attr, $recursive);
    }

    /**
     * Sets information about a file
     */
    private function setstat(string $filename, string $attr, bool $recursive): void
    {
        $filename = $this->realpath($filename);

        $this->remove_from_stat_cache($filename);

        if ($recursive) {
            $files = [];
            $this->setstat_recursive($filename, $attr, $files);
            $this->read_put_responses_files($files);
            return;
        }

        $packet = Strings::packSSH2('s', $filename);
        $packet .= $this->version >= 4 ?
            pack('a*Ca*', substr($attr, 0, 4), FileType::UNKNOWN, substr($attr, 4)) :
            $attr;
        $this->send_sftp_packet(SFTPPacketType::SETSTAT, $packet);

        /*
         "Because some systems must use separate system calls to set various attributes, it is possible that a failure
          response will be returned, but yet some of the attributes may be have been successfully modified.  If possible,
          servers SHOULD avoid this situation; however, clients MUST be aware that this is possible."

          -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.6
        */
        $this->get_sftp_packet_or_error([SFTPPacketType::STATUS], StatusCode::OK);
    }

    /**
     * Recursively sets information on directories on the SFTP server
     *
     * Minimizes directory lookups and SSH_FXP_STATUS requests for speed.
     */
    private function setstat_recursive(string $path, string $attr, array &$files): void
    {
        $this->read_put_responses_files($files);

        try {
            // try to open $path as a directory
            $entries = $this->readlist($path, true);
        } catch (FileSystemException $e) {
            $this->logError($this->lastErrorResponse, $e->getCode(), "OPENDIR $path");
            // if it can't be opened assume it's a file
            try {
                $this->setstat($path, $attr, false);
            } catch (FileSystemException $e) {
                $this->logError($this->lastErrorResponse, $e->getCode(), "SETSTAT $path");
            }
            return;
        }

        // normally $entries would have at least . and .. but it might not if the directories
        // permissions didn't allow reading
        if (empty($entries)) {
            throw new UnexpectedValueException('Directory has zero entries - it should have at least . and ..');
        }

        unset($entries['.'], $entries['..']);
        foreach ($entries as $filename => $props) {
            if (!isset($props['type'])) {
                throw new UnexpectedValueException('Unable to determine the "type');
            }

            $temp = $path . '/' . $filename;
            if ($props['type'] == FileType::DIRECTORY) {
                $this->setstat_recursive($temp, $attr, $files);
            } else {
                $packet = Strings::packSSH2('s', $temp);
                $packet .= $this->version >= 4 ?
                    pack('Ca*', FileType::UNKNOWN, $attr) :
                    $attr;
                $this->send_sftp_packet(SFTPPacketType::SETSTAT, $packet);

                $files[] = $filename;
                if (count($files) >= $this->queueSize) {
                    $this->read_put_responses_files($files);
                }
            }
        }

        $packet = Strings::packSSH2('s', $path);
        $packet .= $this->version >= 4 ?
            pack('Ca*', FileType::UNKNOWN, $attr) :
            $attr;
        $this->send_sftp_packet(SFTPPacketType::SETSTAT, $packet);

        $files[] = $filename;
        if (count($files) >= $this->queueSize) {
            $this->read_put_responses_files($files);
        }
    }

    /**
     * Return the target of a symbolic link
     */
    public function readlink(string $link): string
    {
        $this->precheck();

        $link = $this->realpath($link);

        $this->send_sftp_packet(SFTPPacketType::READLINK, Strings::packSSH2('s', $link));
        $response = $this->get_sftp_packet_or_error([SFTPPacketType::NAME, SFTPPacketType::STATUS]);

        [$count] = Strings::unpackSSH2('N', $response);
        if (!$count) {
            throw new FileSystemException('The file in question is not a symlink');
        }

        [$filename] = Strings::unpackSSH2('s', $response);

        return $filename;
    }

    /**
     * Create a symlink
     *
     * symlink() creates a symbolic link to the existing $target with the specified name $link.
     */
    public function symlink(string $target, string $link): void
    {
        $this->precheck();

        //$target = $this->realpath($target);
        $link = $this->realpath($link);

        /* quoting https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-09#section-12.1 :

           Changed the SYMLINK packet to be LINK and give it the ability to
           create hard links.  Also change it's packet number because many
           implementation implemented SYMLINK with the arguments reversed.
           Hopefully the new argument names make it clear which way is which.
        */
        if ($this->version == 6) {
            $type = SFTPPacketType::LINK;
            $packet = Strings::packSSH2('ssC', $link, $target, 1);
        } else {
            $type = SFTPPacketType::SYMLINK;
            /* quoting http://bxr.su/OpenBSD/usr.bin/ssh/PROTOCOL#347 :

               3.1. sftp: Reversal of arguments to SSH_FXP_SYMLINK

               When OpenSSH's sftp-server was implemented, the order of the arguments
               to the SSH_FXP_SYMLINK method was inadvertently reversed. Unfortunately,
               the reversal was not noticed until the server was widely deployed. Since
               fixing this to follow the specification would cause incompatibility, the
               current order was retained. For correct operation, clients should send
               SSH_FXP_SYMLINK as follows:

                   uint32      id
                   string      targetpath
                   string      linkpath */
            $packet = substr($this->server_identifier, 0, 15) == 'SSH-2.0-OpenSSH' ?
                Strings::packSSH2('ss', $target, $link) :
                Strings::packSSH2('ss', $link, $target);
        }
        $this->send_sftp_packet($type, $packet);
        $this->get_sftp_packet_or_error([SFTPPacketType::STATUS], StatusCode::OK);
    }

    /**
     * Creates a directory.
     */
    public function mkdir(string $dir, int $mode = -1, bool $recursive = false): void
    {
        $this->precheck();

        $dir = $this->realpath($dir);
        // if the fully expanded directory is already a directory then trying to
        // create it anew would result in an exception being thrown. at least if
        // it weren't for the following line
        if ($this->is_dir($dir)) {
            return;
        }

        if ($recursive) {
            $dirs = explode('/', preg_replace('#/(?=/)|/$#', '', $dir));
            if (empty($dirs[0])) {
                array_shift($dirs);
                $dirs[0] = '/' . $dirs[0];
            }
            for ($i = 0; $i < count($dirs); $i++) {
                $temp = array_slice($dirs, 0, $i + 1);
                $temp = implode('/', $temp);
                try {
                    $this->mkdir_helper($temp, $mode);
                } catch (\Exception $e) {
                    // if we're trying to create /home/user/path/to/whatever and /home/user already exists,
                    // exceptions will be thrown, but that doesn't mean we can't try successive directories
                    // until we succeed
                    if ($i === count($dirs) - 1) {
                        throw $e;
                    }
                }
            }
            return;
        }

        $this->mkdir_helper($dir, $mode);
    }

    /**
     * Helper function for directory creation
     */
    private function mkdir_helper(string $dir, int $mode): void
    {
        // send SSH_FXP_MKDIR without any attributes (that's what the \0\0\0\0 is doing)
        $this->send_sftp_packet(SFTPPacketType::MKDIR, Strings::packSSH2('s', $dir) . "\0\0\0\0");
        $this->get_sftp_packet_or_error([SFTPPacketType::STATUS], StatusCode::OK);

        if ($mode !== -1) {
            $this->chmod($dir, $mode);
        }
    }

    /**
     * Removes a directory.
     */
    public function rmdir(string $dir): void
    {
        $this->precheck();

        $dir = $this->realpath($dir);

        $this->send_sftp_packet(SFTPPacketType::RMDIR, Strings::packSSH2('s', $dir));
        $this->get_sftp_packet_or_error([SFTPPacketType::STATUS], StatusCode::OK);

        $this->remove_from_stat_cache($dir);
        // the following will do a soft delete, which would be useful if you deleted a file
        // and then tried to do a stat on the deleted file. the above, in contrast, does
        // a hard delete
        //$this->update_stat_cache($dir, null);
    }

    /**
     * Uploads a file to the SFTP server.
     *
     * By default, \phpseclib4\Net\SFTP::put() does not read from the local filesystem.  $data is dumped directly into $remote_file.
     * So, for example, if you set $data to 'filename.ext' and then do \phpseclib4\Net\SFTP::get(), you will get a file, twelve bytes
     * long, containing 'filename.ext' as its contents.
     *
     * Setting $mode to self::SOURCE_LOCAL_FILE will change the above behavior.  With self::SOURCE_LOCAL_FILE, $remote_file will
     * contain as many bytes as filename.ext does on your local filesystem.  If your filename.ext is 1MB then that is how
     * large $remote_file will be, as well.
     *
     * Setting $mode to self::SOURCE_CALLBACK will use $data as callback function, which gets only one parameter -- number
     * of bytes to return, and returns a string if there is some data or null if there is no more data
     *
     * If $data is a resource then it'll be used as a resource instead.
     *
     * Currently, only binary mode is supported.  As such, if the line endings need to be adjusted, you will need to take
     * care of that, yourself.
     *
     * $mode can take an additional two parameters - self::RESUME and self::RESUME_START. These are bitwise AND'd with
     * $mode. So if you want to resume upload of a 300mb file on the local file system you'd set $mode to the following:
     *
     * self::SOURCE_LOCAL_FILE | self::RESUME
     *
     * If you wanted to simply append the full contents of a local file to the full contents of a remote file you'd replace
     * self::RESUME with self::RESUME_START.
     *
     * If $mode & (self::RESUME | self::RESUME_START) then self::RESUME_START will be assumed.
     *
     * $start and $local_start give you more fine grained control over this process and take precident over self::RESUME
     * when they're non-negative. ie. $start could let you write at the end of a file (like self::RESUME) or in the middle
     * of one. $local_start could let you start your reading from the end of a file (like self::RESUME_START) or in the
     * middle of one.
     *
     * Setting $local_start to > 0 or $mode | self::RESUME_START doesn't do anything unless $mode | self::SOURCE_LOCAL_FILE.
     *
     * {@internal ASCII mode for SFTPv4/5/6 can be supported by adding a new function - \phpseclib4\Net\SFTP::setMode().}
     *
     * @param  resource|string|\Closure $data
     */
    public function put(
        string $remote_file,
        #[\SensitiveParameter] mixed $data,
        int $mode = self::SOURCE_STRING,
        int $start = -1,
        int $local_start = -1,
        ?\Closure $progressCallback = null
    ): void {
        $this->precheck();

        $remote_file = $this->realpath($remote_file);

        $this->remove_from_stat_cache($remote_file);

        if ($this->version >= 5) {
            $flags = OpenFlag5::OPEN_OR_CREATE;
        } else {
            $flags = OpenFlag::WRITE | OpenFlag::CREATE;
            // according to the SFTP specs, OpenFlag::APPEND should "force all writes to append data at the end of the file."
            // in practice, it doesn't seem to do that.
            //$flags|= ($mode & self::RESUME) ? OpenFlag::APPEND : OpenFlag::TRUNCATE;
        }

        if ($start >= 0) {
            $offset = $start;
        } elseif ($mode & (self::RESUME | self::RESUME_START)) {
            // if OpenFlag::APPEND worked as it should _size() wouldn't need to be called
            $stat = $this->stat($remote_file);
            $offset = $stat !== false && $stat['size'] ? $stat['size'] : 0;
        } else {
            $offset = 0;
            if ($this->version >= 5) {
                $flags = OpenFlag5::CREATE_TRUNCATE;
            } else {
                $flags |= OpenFlag::TRUNCATE;
            }
        }

        $this->remove_from_stat_cache($remote_file);

        $packet = Strings::packSSH2('s', $remote_file);
        $packet .= $this->version >= 5 ?
            pack('N3', 0, $flags, 0) :
            pack('N2', $flags, 0);
        $this->send_sftp_packet(SFTPPacketType::OPEN, $packet);
        $response = $this->get_sftp_packet_or_error([SFTPPacketType::HANDLE, SFTPPacketType::STATUS]);
        $handle = substr($response, 4);

        // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.2.3
        $dataCallback = false;
        switch (true) {
            case $mode & self::SOURCE_CALLBACK:
                if (!$data instanceof \Closure) {
                    throw new InvalidArgumentException("\$data should be a Closure if you specify SOURCE_CALLBACK flag");
                }
                $dataCallback = $data;
                // do nothing
                break;
            case is_resource($data):
                $mode = $mode & ~self::SOURCE_LOCAL_FILE;
                $info = stream_get_meta_data($data);
                if (isset($info['wrapper_type']) && $info['wrapper_type'] == 'PHP' && $info['stream_type'] == 'Input') {
                    $fp = fopen('php://memory', 'w+');
                    stream_copy_to_stream($data, $fp);
                    rewind($fp);
                } else {
                    $fp = $data;
                }
                break;
            case $mode & self::SOURCE_LOCAL_FILE:
                if (!is_file($data)) {
                    throw new FileSystemException("$data is not a valid file");
                }
                $fp = Files::open($data, 'rb');
        }

        if (isset($fp)) {
            $stat = fstat($fp);
            $size = !empty($stat) ? $stat['size'] : 0;

            if ($local_start >= 0) {
                fseek($fp, $local_start);
                $size -= $local_start;
            } elseif ($mode & self::RESUME) {
                fseek($fp, $offset);
                $size -= $offset;
            }
        } elseif ($dataCallback) {
            $size = 0;
        } else {
            $size = strlen($data);
        }

        $sent = 0;
        $size = $size < 0 ? ($size & 0x7FFFFFFF) + 0x80000000 : $size;

        $sftp_packet_size = $this->max_sftp_packet;
        // make the SFTP packet be exactly the SFTP packet size by including the bytes in the PacketType::WRITE packets "header"
        $sftp_packet_size -= strlen($handle) + 25;
        $i = $j = 0;
        while ($dataCallback || ($size === 0 || $sent < $size)) {
            if ($dataCallback) {
                $temp = $dataCallback($sftp_packet_size);
                if (is_null($temp)) {
                    break;
                }
            } else {
                $temp = isset($fp) ? fread($fp, $sftp_packet_size) : substr($data, $sent, $sftp_packet_size);
                if ($temp === false || $temp === '') {
                    break;
                }
            }

            $subtemp = $offset + $sent;
            $packet = pack('Na*N3a*', strlen($handle), $handle, $subtemp / 4294967296, $subtemp, strlen($temp), $temp);
            try {
                $this->send_sftp_packet(SFTPPacketType::WRITE, $packet, $j);
            } catch (\Exception $e) {
                if ($mode & self::SOURCE_LOCAL_FILE) {
                    fclose($fp);
                }
                throw $e;
            }
            $sent += strlen($temp);
            if ($progressCallback) {
                $progressCallback($sent);
            }

            $i++;
            $j++;
            if ($i == $this->uploadQueueSize) {
                $this->read_put_responses_count($i);
            }
        }

        try {
            $this->read_put_responses_count($i);
        } catch (\Exception $e) {
            throw $e;
        } finally {
            if (($mode & self::SOURCE_LOCAL_FILE) && isset($fp) && is_resource($fp)) {
                fclose($fp);
            }
            $this->close_handle($handle);
        }

        if (($mode & SFTP::SOURCE_LOCAL_FILE) && $this->preserveTime) {
            $stat = stat($data);
            $attr = $this->version < 4 ?
                pack('N3', Attribute::ACCESSTIME, $stat['atime'], $stat['mtime']) :
                Strings::packSSH2('NQ2', Attribute::ACCESSTIME | Attribute::MODIFYTIME, $stat['atime'], $stat['mtime']);
            $this->setstat($remote_file, $attr, false);
        }
    }

    /**
     * Reads multiple successive SSH_FXP_WRITE responses
     *
     * Sending an SSH_FXP_WRITE packet and immediately reading its response isn't as efficient as blindly sending out $i
     * SSH_FXP_WRITEs, in succession, and then reading $i responses.
     */
    private function read_responses(int $i, ?array $files = null): void
    {
        $size = $files === null ? 0 : count($files);
        while ($i-- > 0) {
            $response = $this->get_sftp_packet();
            if ($this->packet_type != SFTPPacketType::STATUS) {
                throw new UnexpectedValueException(
                    'Expected SSH_FXP_STATUS. Got packet type: SSH_FXP_' .
                    SFTPPacketType::getConstantNameByValue($this->packet_type)
                );
            }
            [$status] = Strings::unpackSSH2('N', $response);
            if ($status != StatusCode::OK) {
                if ($files === null) {
                    throw $this->throwStatusError($response, $status);
                }
                $this->logError($response, $status, $files[$size - 1 - $i] ?? '');
            }
        }
    }

    private function read_put_responses_files(array &$files): void
    {
        $this->read_responses(count($files), $files);
        $files = [];
    }

    private function read_put_responses_count(int &$count): void
    {
        $this->read_responses($count);
        $count = 0;
    }

    /**
     * Close handle
     */
    private function close_handle(string $handle): void
    {
        $this->send_sftp_packet(SFTPPacketType::CLOSE, pack('Na*', strlen($handle), $handle));
        // "The client MUST release all resources associated with the handle regardless of the status."
        //  -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.3
        $this->get_sftp_packet_or_error([SFTPPacketType::STATUS], StatusCode::OK);
    }

    /**
     * Downloads a file from the SFTP server.
     *
     * Returns a string containing the contents of $remote_file if $local_file is left undefined.
     * If $local_file is defined, writes the file locally and returns null.
     *
     * $offset and $length can be used to download files in chunks.
     *
     * @param string|null|resource|\Closure $local_file
     */
    public function get(string $remote_file, mixed $local_file = null, int $offset = 0, int $length = -1, ?\Closure $progressCallback = null): ?string
    {
        $this->precheck();

        $remote_file = $this->realpath($remote_file);

        $packet = Strings::packSSH2('s', $remote_file);
        $packet .= $this->version >= 5 ?
            pack('N3', 0, OpenFlag5::OPEN_EXISTING, 0) :
            pack('N2', OpenFlag::READ, 0);
        $this->send_sftp_packet(SFTPPacketType::OPEN, $packet);

        $response = $this->get_sftp_packet_or_error([SFTPPacketType::HANDLE, SFTPPacketType::STATUS]);
        $handle = substr($response, 4);

        if (is_resource($local_file)) {
            $fp = $local_file;
            //$stat = fstat($fp);
            //$res_offset = $stat['size'];
        } else {
            //$res_offset = 0;
            if (is_string($local_file)) {
                $fp = Files::open($local_file, 'wb');
            } else {
                $content = '';
            }
        }

        $fclose_check = isset($local_file) && (!$local_file instanceof \Closure) && !is_resource($local_file);

        $start = $offset;
        $read = 0;
        while (true) {
            $i = 0;

            while ($i < $this->queueSize && ($length < 0 || $read < $length)) {
                $tempoffset = $start + $read;

                $packet_size = $length > 0 ? min($this->max_sftp_packet, $length - $read) : $this->max_sftp_packet;

                $packet = Strings::packSSH2('sN3', $handle, $tempoffset / 4294967296, $tempoffset, $packet_size);
                try {
                    $this->send_sftp_packet(SFTPPacketType::READ, $packet, $i);
                } catch (\Exception $e) {
                    if ($fclose_check) {
                        fclose($fp);
                    }
                    throw $e;
                }
                unset($packet);
                $read += $packet_size;
                $i++;
            }

            if (!$i) {
                break;
            }

            $packets_sent = $i - 1;

            $clear_responses = false;
            while ($i > 0) {
                $i--;

                if ($clear_responses) {
                    $this->get_sftp_packet($packets_sent - $i);
                    continue;
                } else {
                    try {
                        $response = $this->get_sftp_packet($packets_sent - $i);
                    } catch (SSHChannelExitSignalException $e) {
                        if (!isset($local_file)) {
                            $e->partialOutput = $content;
                        }
                        throw $e;
                    }
                }

                switch ($this->packet_type) {
                    case SFTPPacketType::DATA:
                        $temp = substr($response, 4);
                        $offset += strlen($temp);
                        if (!isset($local_file)) {
                            $content .= $temp;
                        } elseif (is_callable($local_file)) {
                            $local_file($temp);
                        } else {
                            fwrite($fp, $temp);
                        }
                        if ($progressCallback) {
                            $progressCallback($offset);
                        }
                        unset($temp);
                        break;
                    case SFTPPacketType::STATUS:
                        // could, in theory, return false if !strlen($content) but we'll hold off for the time being
                        //$this->logError($response);
                        $clear_responses = true; // don't break out of the loop yet, so we can read the remaining responses
                        break;
                    default:
                        if ($fclose_check) {
                            fclose($fp);
                        }
                        if ($this->channel_close) {
                            $this->partial_init = false;
                            $this->init_sftp_connection();
                            throw new ConnectionClosedException('Connection closed while downloading file');
                        } else {
                            try {
                                $type = 'SSH_FXP_' . SFTPPacketType::getConstantNameByValue($this->packet_type);
                            } catch (InvalidArgumentException) {
                                $type = '# ' . $this->packet_type;
                            }
                            throw new UnexpectedSFTPPacketException(
                                'Expected SSH_FXP_DATA or SSH_FXP_STATUS. ' .
                                "Got packet type: $type"
                            );
                        }
                }
                unset($response);
            }

            if ($clear_responses) {
                break;
            }
        }

        if ($fclose_check) {
            fclose($fp);

            if ($this->preserveTime) {
                $stat = $this->stat($remote_file);
                touch($local_file, $stat['mtime'], $stat['atime']);
            }
        }

        $this->close_handle($handle);

        // if $content isn't set that means a file was written to
        return $content ?? null;
    }

    /**
     * Deletes a file on the SFTP server.
     */
    public function delete(string $path, bool $recursive = true): void
    {
        $this->precheck();

        if (!strlen($path)) {
            throw new InvalidArgumentException('strlen($path) should be > 0');
        }

        $path = $this->realpath($path);

        // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3
        $this->send_sftp_packet(SFTPPacketType::REMOVE, pack('Na*', strlen($path), $path));

        try {
            $this->get_sftp_packet_or_error([SFTPPacketType::STATUS], StatusCode::OK);
        } catch (FileSystemException $e) {
            if ($recursive) {
                // if you're trying to delete a directory you'll have 1x entry in getSFTPErrors() as it'll
                // first try to delete it as a file. if the file you're deleting doesn't exist you'll have
                // 2x entries - the first one being for your attempt to delete the "file" as a directory
                // and the second attempting to delete the "file" as a file
                $this->logError($this->lastErrorResponse, $e->getCode(), "OPEN $path");
            } else {
                throw $e;
            }

            $files = [];
            $this->delete_recursive($path, $files);
            $this->read_put_responses_files($files);
            return;
        }

        $this->remove_from_stat_cache($path);
    }

    /**
     * Recursively deletes directories on the SFTP server
     *
     * Minimizes directory lookups and SSH_FXP_STATUS requests for speed.
     */
    private function delete_recursive(string $path, array &$files): void
    {
        $this->read_put_responses_files($files);
        try {
            $entries = $this->readlist($path, true);
        } catch (FileSystemException $e) {
            $this->logError($this->lastErrorResponse, $e->getCode(), "OPENDIR $path");
            if ($e->getCode() == StatusCode::NO_SUCH_FILE) {
                return;
            }
            // it's possible that the permissions just don't allow reading. in this scenario
            // default to an empty list of files
            $entries = [];
        }

        unset($entries['.'], $entries['..']);
        foreach ($entries as $filename => $props) {
            if (!isset($props['type'])) {
                throw new UnexpectedValueException('Unable to determine resource type');
            }

            $temp = $path . '/' . $filename;
            if ($props['type'] == FileType::DIRECTORY) {
                $this->delete_recursive($temp, $files);
            } else {
                $this->send_sftp_packet(SFTPPacketType::REMOVE, Strings::packSSH2('s', $temp));
                $this->remove_from_stat_cache($temp);

                $files[] = "REMOVE $temp";
                if (count($files) >= $this->queueSize) {
                    $this->read_put_responses_files($files);
                }
            }
        }

        $this->send_sftp_packet(SFTPPacketType::RMDIR, Strings::packSSH2('s', $path));
        $this->remove_from_stat_cache($path);

        $files[] = "RMDIR $path";
        if (count($files) >= $this->queueSize) {
            $this->read_put_responses_files($files);
        }
    }

    /**
     * Checks whether a file or directory exists
     */
    public function file_exists(string $path): bool
    {
        if ($this->use_stat_cache) {
            $path = $this->realpath($path);

            $result = $this->query_stat_cache($path);

            if (isset($result)) {
                // return true if $result is an array or if it's an stdClass object
                return $result !== false;
            }
        }

        try {
            $this->stat($path);
            return true;
        } catch (FileSystemException $e) {
            return false;
        }
    }

    /**
     * Tells whether the filename is a directory
     */
    public function is_dir(string $path): bool
    {
        try {
            return $this->get_stat_cache_prop($path, 'type') === FileType::DIRECTORY;
        } catch (FileSystemException $e) {
            return false;
        }
    }

    /**
     * Tells whether the filename is a regular file
     */
    public function is_file(string $path): bool
    {
        try {
            return $this->get_stat_cache_prop($path, 'type') === FileType::REGULAR;
        } catch (FileSystemException $e) {
            return false;
        }
    }

    /**
     * Tells whether the filename is a symbolic link
     */
    public function is_link(string $path): bool
    {
        try {
            return $this->get_lstat_cache_prop($path, 'type') === FileType::SYMLINK;
        } catch (FileSystemException $e) {
            return false;
        }
    }

    /**
     * Tells whether a file exists and is readable
     */
    public function is_readable(string $path): bool
    {
        $this->precheck();

        $packet = Strings::packSSH2('sNN', $this->realpath($path), OpenFlag::READ, 0);
        $this->send_sftp_packet(SFTPPacketType::OPEN, $packet);

        try {
            $response = $this->get_sftp_packet_or_error([SFTPPacketType::HANDLE, SFTPPacketType::STATUS]);
            $handle = substr($response, 4);
            $this->close_handle($handle);
            return true;
        } catch (FileSystemException $e) {
            $this->logError($this->lastErrorResponse, $e->getCode(), "OPEN $path");
            return false;
        }
    }

    /**
     * Tells whether the filename is writable
     */
    public function is_writable(string $path): bool
    {
        $this->precheck();

        $packet = Strings::packSSH2('sNN', $this->realpath($path), OpenFlag::WRITE, 0);
        $this->send_sftp_packet(SFTPPacketType::OPEN, $packet);

        try {
            $response = $this->get_sftp_packet_or_error([SFTPPacketType::HANDLE, SFTPPacketType::STATUS]);
            $handle = substr($response, 4);
            $this->close_handle($handle);
            return true;
        } catch (FileSystemException $e) {
            $this->logError($this->lastErrorResponse, $e->getCode(), "OPEN $path");
            return false;
        }
    }

    /**
     * Tells whether the filename is writeable
     *
     * Alias of is_writable
     */
    public function is_writeable(string $path): bool
    {
        return $this->is_writable($path);
    }

    /**
     * Gets last access time of file
     *
     * @psalm-suppress PossiblyUnusedMethod, InvalidReturnStatement, InvalidReturnType
     */
    public function fileatime(string $path): int|float
    {
        return $this->get_stat_cache_prop($path, 'atime');
    }

    /**
     * Gets file modification time
     *
     * @psalm-suppress PossiblyUnusedMethod, InvalidReturnStatement, InvalidReturnType
     */
    public function filemtime(string $path): int|float
    {
        return $this->get_stat_cache_prop($path, 'mtime');
    }

    /**
     * Gets file permissions
     *
     * @psalm-suppress InvalidReturnStatement, InvalidReturnType
     */
    public function fileperms(string $path): int
    {
        return $this->get_stat_cache_prop($path, 'mode');
    }

    /**
     * Gets file owner
     *
     * @psalm-suppress PossiblyUnusedMethod, InvalidReturnStatement, InvalidReturnType
     */
    public function fileowner(string $path): int|string
    {
        $this->precheck();

        return $this->get_stat_cache_prop($path, $this->version > 3 ? 'owner' : 'uid');
    }

    /**
     * Gets file group
     *
     * @psalm-suppress PossiblyUnusedMethod, InvalidReturnStatement, InvalidReturnType
     */
    public function filegroup(string $path): int|string
    {
        $this->precheck();

        return $this->get_stat_cache_prop($path, $this->version > 3 ? 'group' : 'gid');
    }

    /**
     * Recursively go through rawlist() output to get the total filesize
     */
    private static function recursiveFilesize(array $files): int|float
    {
        $size = 0;
        foreach ($files as $name => $file) {
            if ($name == '.' || $name == '..') {
                continue;
            }
            $size += is_array($file) ?
                self::recursiveFilesize($file) :
                $file->size;
        }
        return $size;
    }

    /**
     * Gets file size
     *
     * @psalm-suppress InvalidReturnStatement, InvalidReturnType
     */
    public function filesize(string $path, bool $recursive = false): int|float
    {
        return !$recursive || $this->filetype($path) != 'dir' ?
            $this->get_stat_cache_prop($path, 'size') :
            self::recursiveFilesize($this->rawlist($path, true));
    }

    /**
     * Gets file type
     */
    public function filetype(string $path): string
    {
        $type = $this->get_stat_cache_prop($path, 'type');

        return match ($type) {
            FileType::BLOCK_DEVICE => 'block',
            FileType::CHAR_DEVICE => 'char',
            FileType::DIRECTORY => 'dir',
            FileType::FIFO => 'fifo',
            FileType::REGULAR => 'link',
            default => throw new UnexpectedValueException("Unable to map file type ($type) to a known type")
        };
    }

    /**
     * Return a stat property
     *
     * Uses cache if appropriate.
     */
    private function get_stat_cache_prop(string $path, string $prop): int|float|string
    {
        return $this->get_xstat_cache_prop($path, $prop, 'stat');
    }

    /**
     * Return an lstat property
     *
     * Uses cache if appropriate.
     */
    private function get_lstat_cache_prop(string $path, string $prop): int|float|string
    {
        return $this->get_xstat_cache_prop($path, $prop, 'lstat');
    }

    /**
     * Return a stat or lstat properity
     *
     * Uses cache if appropriate.
     */
    private function get_xstat_cache_prop(string $path, string $prop, string $type): int|string
    {
        $this->precheck();

        if ($this->use_stat_cache) {
            $path = $this->realpath($path);

            $result = $this->query_stat_cache($path);

            if (is_object($result) && isset($result->$type)) {
                return $result->{$type}[$prop];
            }
        }

        $result = $this->$type($path);

        if (!isset($result[$prop])) {
            throw new UnsupportedValueException("Property $prop is not available for $path");
        }

        return $result[$prop];
    }

    /**
     * Renames a file or a directory on the SFTP server.
     *
     * If the file already exists this will return false
     */
    public function rename(string $oldname, string $newname): void
    {
        $this->precheck();

        $oldname = $this->realpath($oldname);
        $newname = $this->realpath($newname);

        // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3
        $packet = Strings::packSSH2('ss', $oldname, $newname);
        if ($this->version >= 5) {
            /* quoting https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-05#section-6.5 ,

               'flags' is 0 or a combination of:

                   SSH_FXP_RENAME_OVERWRITE  0x00000001
                   SSH_FXP_RENAME_ATOMIC     0x00000002
                   SSH_FXP_RENAME_NATIVE     0x00000004

               (none of these are currently supported) */
            $packet .= "\0\0\0\0";
        }
        $this->send_sftp_packet(SFTPPacketType::RENAME, $packet);
        $this->get_sftp_packet_or_error([SFTPPacketType::STATUS], StatusCode::OK);

        // don't move the stat cache entry over since this operation could very well change the
        // atime and mtime attributes
        //$this->update_stat_cache($newname, $this->query_stat_cache($oldname));
        $this->remove_from_stat_cache($oldname);
        $this->remove_from_stat_cache($newname);
    }

    /**
     * Parse Time
     *
     * See '7.7.  Times' of draft-ietf-secsh-filexfer-13 for more info.
     */
    private static function parseTime(string $key, int $flags, string &$response): array
    {
        $attr = [];
        [$attr[$key]] = Strings::unpackSSH2('Q', $response);
        if ($flags & Attribute::SUBSECOND_TIMES) {
            [$attr[$key . '-nseconds']] = Strings::unpackSSH2('N', $response);
        }
        return $attr;
    }

    /**
     * Parse Attributes
     *
     * See '7.  File Attributes' of draft-ietf-secsh-filexfer-13 for more info.
     *
     * @psalm-suppress UndefinedConstant
     */
    protected static function parseAttributes(int $version, string &$response): array
    {
        $attr = [];

        if ($version >= 4) {
            [$flags, $attr['type']] = Strings::unpackSSH2('NC', $response);
        } else {
            [$flags] = Strings::unpackSSH2('N', $response);
        }

        foreach (Attribute::getConstants() as $key) {
            switch ($flags & $key) {
                case Attribute::UIDGID:
                    if ($version > 3) {
                        continue 2;
                    }
                    break;
                case Attribute::CREATETIME:
                case Attribute::MODIFYTIME:
                case Attribute::ACL:
                case Attribute::OWNERGROUP:
                case Attribute::SUBSECOND_TIMES:
                    if ($version < 4) {
                        continue 2;
                    }
                    break;
                case Attribute::BITS:
                    if ($version < 5) {
                        continue 2;
                    }
                    break;
                case Attribute::ALLOCATION_SIZE:
                case Attribute::TEXT_HINT:
                case Attribute::MIME_TYPE:
                case Attribute::LINK_COUNT:
                case Attribute::UNTRANSLATED_NAME:
                case Attribute::CTIME:
                    if ($version < 6) {
                        continue 2;
                    }
            }
            switch ($flags & $key) {
                case Attribute::SIZE: // 0x00000001
                    // The size attribute is defined as an unsigned 64-bit integer.
                    // The following will use floats on 32-bit platforms, if necessary.
                    // As can be seen in the BigInteger class, floats are generally
                    // IEEE 754 binary64 "double precision" on such platforms and
                    // as such can represent integers of at least 2^50 without loss
                    // of precision. Interpreted in filesize, 2^50 bytes = 1024 TiB.
                    [$attr['size']] = Strings::unpackSSH2('Q', $response);
                    break;
                case Attribute::UIDGID: // 0x00000002 (SFTPv3 only)
                    [$attr['uid'], $attr['gid']] = Strings::unpackSSH2('NN', $response);
                    break;
                case Attribute::PERMISSIONS: // 0x00000004
                    [$attr['mode']] = Strings::unpackSSH2('N', $response);
                    $fileType = self::parseMode($attr['mode']);
                    if ($version < 4 && isset($fileType)) {
                        $attr += ['type' => $fileType];
                    }
                    break;
                case Attribute::ACCESSTIME: // 0x00000008
                    if ($version >= 4) {
                        $attr += self::parseTime('atime', $flags, $response);
                        break;
                    }
                    [$attr['atime'], $attr['mtime']] = Strings::unpackSSH2('NN', $response);
                    break;
                case Attribute::CREATETIME: // 0x00000010 (SFTPv4+)
                    $attr += self::parseTime('createtime', $flags, $response);
                    break;
                case Attribute::MODIFYTIME: // 0x00000020
                    $attr += self::parseTime('mtime', $flags, $response);
                    break;
                case Attribute::ACL: // 0x00000040
                    // access control list
                    // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-04#section-5.7
                    // currently unsupported
                    /*
                    [$count] = Strings::unpackSSH2('N', $response);
                    for ($i = 0; $i < $count; $i++) {
                        [$type, $flag, $mask, $who] = Strings::unpackSSH2('N3s', $result);
                    }
                    */
                    break;
                case Attribute::OWNERGROUP: // 0x00000080
                    [$attr['owner'], $attr['group']] = Strings::unpackSSH2('ss', $response);
                    break;
                case Attribute::SUBSECOND_TIMES: // 0x00000100
                    break;
                case Attribute::BITS: // 0x00000200 (SFTPv5+)
                    // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-05#section-5.8
                    // currently unsupported
                    // tells if you file is:
                    // readonly, system, hidden, case insensitive, archive, encrypted, compressed, sparse
                    // append only, immutable, sync
                    /*
                    [$attrib_bits, $attrib_bits_valid] = Strings::unpackSSH2('N2', $response);
                    */
                    // if we were actually gonna implement the above it ought to be
                    // $attr['attrib-bits'] and $attr['attrib-bits-valid']
                    // eg. - instead of _
                    break;
                case Attribute::ALLOCATION_SIZE: // 0x00000400 (SFTPv6+)
                    // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.4
                    // represents the number of bytes that the file consumes on the disk. will
                    // usually be larger than the 'size' field
                    [$attr['allocation-size']] = Strings::unpackSSH2('Q', $response);
                    break;
                case Attribute::TEXT_HINT: // 0x00000800
                    // https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.10
                    // currently unsupported
                    // tells if file is "known text", "guessed text", "known binary", "guessed binary"
                    /*
                    [$text_hint] = Strings::unpackSSH2('C', $response);
                    */
                    // the above should be $attr['text-hint']
                    break;
                case Attribute::MIME_TYPE: // 0x00001000
                    // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.11
                    [$attr['mime-type']] = Strings::unpackSSH2('s', $response);
                    break;
                case Attribute::LINK_COUNT: // 0x00002000
                    // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.12
                    [$attr['link-count']] = Strings::unpackSSH2('N', $response);
                    break;
                case Attribute::UNTRANSLATED_NAME: // 0x00004000
                    // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.13
                    [$attr['untranslated-name']] = Strings::unpackSSH2('s', $response);
                    break;
                case Attribute::CTIME:  // 0x00008000
                    // 'ctime' contains the last time the file attributes were changed.  The
                    // exact meaning of this field depends on the server.
                    $attr += self::parseTime('ctime', $flags, $response);
                    break;
                case Attribute::EXTENDED: // 0x80000000
                    [$count] = Strings::unpackSSH2('N', $response);
                    for ($i = 0; $i < $count; $i++) {
                        [$key, $value] = Strings::unpackSSH2('ss', $response);
                        $attr[$key] = $value;
                    }
            }
        }
        return $attr;
    }

    /**
     * Attempt to identify the file type
     *
     * Quoting the SFTP RFC, "Implementations MUST NOT send bits that are not defined" but they seem to anyway
     */
    private static function parseMode(int $mode): ?int
    {
        // values come from http://lxr.free-electrons.com/source/include/uapi/linux/stat.h#L12
        // see, also, http://linux.die.net/man/2/stat
        return match ($mode & 0o170000) {// ie. 1111 0000 0000 0000
            0 => null, // no file type specified - figure out the file type using alternative means
            0o040000 => FileType::DIRECTORY,
            0o100000 => FileType::REGULAR,
            0o120000 => FileType::SYMLINK,
            // new types introduced in SFTPv5+
            // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-05#section-5.2
            0o010000 => FileType::FIFO, // named pipe (fifo)
            0o020000 => FileType::CHAR_DEVICE, // character special
            0o060000 => FileType::BLOCK_DEVICE, // block special
            0o140000 => FileType::SOCKET, // socket
            // "SPECIAL should be used for files that are of
            //  a known type which cannot be expressed in the protocol"
            0o160000 => FileType::SPECIAL, // whiteout
            default => FileType::UNKNOWN
        };
    }

    /**
     * Parse Longname
     *
     * SFTPv3 doesn't provide any easy way of identifying a file type.  You could try to open
     * a file as a directory and see if an error is returned or you could try to parse the
     * SFTPv3-specific longname field of the SSH_FXP_NAME packet.  That's what this function does.
     * The result is returned using the
     * {@link http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2 SFTPv4 type constants}.
     *
     * If the longname is in an unrecognized format bool(false) is returned.
     */
    private static function parseLongname(string $longname): ?int
    {
        // http://en.wikipedia.org/wiki/Unix_file_types
        // http://en.wikipedia.org/wiki/Filesystem_permissions#Notation_of_traditional_Unix_permissions
        if (preg_match('#^[^/]([r-][w-][xstST-]){3}#', $longname)) {
            return match ($longname[0]) {
                '-' => FileType::REGULAR,
                'd' => FileType::DIRECTORY,
                'l' => FileType::SYMLINK,
                default => FileType::SPECIAL
            };
        }

        return null;
    }

    /**
     * Sends SFTP Packets
     *
     * See '6. General Packet Format' of draft-ietf-secsh-filexfer-13 for more info.
     *
     * @see self::_get_sftp_packet()
     * @see self::send_channel_packet()
     */
    private function send_sftp_packet(int $type, #[\SensitiveParameter] string $data, int $request_id = 1): void
    {
        // in SSH2.php the timeout is cumulative per function call. eg. exec() will
        // timeout after 10s. but for SFTP.php it's cumulative per packet
        $this->curTimeout = $this->timeout;
        $this->is_timeout = false;

        $packet = $this->use_request_id ?
            pack('NCNa*', strlen($data) + 5, $type, $request_id, $data) :
            pack('NCa*', strlen($data) + 1, $type, $data);

        $start = microtime(true);
        $this->send_channel_packet(self::CHANNEL, $packet);
        $stop = microtime(true);
        if (defined('NET_SFTP_LOGGING')) {
            $packet_type = '-> SSH_FXP_' . SFTPPacketType::getConstantNameByValue($type) .
                ' (' . round($stop - $start, 4) . 's)';
            $this->append_log($packet_type, $data);
        }
    }

    /**
     * Resets the SFTP channel for re-use
     */
    private function reset_sftp(): void
    {
        $this->use_request_id = false;
        $this->pwd = false;
        $this->requestBuffer = [];
        $this->partial_init = false;
    }

    /**
     * Resets a connection for re-use
     */
    protected function reset_connection(): void
    {
        parent::reset_connection();
        $this->reset_sftp();
    }

    /**
     * Receives SFTP Packets
     *
     * See '6. General Packet Format' of draft-ietf-secsh-filexfer-13 for more info.
     *
     * Incidentally, the number of SSH_MSG_CHANNEL_DATA messages has no bearing on the number of SFTP packets present.
     * There can be one SSH_MSG_CHANNEL_DATA messages containing two SFTP packets or there can be two SSH_MSG_CHANNEL_DATA
     * messages containing one SFTP packet.
     *
     * By the time this function is called the channel_status is SSH2MessageType::CHANNEL_DATA.
     *
     * @see    self::_send_sftp_packet()
     */
    private function get_sftp_packet(?int $request_id = null): string
    {
        $this->channel_close = false;

        if (isset($request_id) && isset($this->requestBuffer[$request_id])) {
            $this->packet_type = $this->requestBuffer[$request_id]['packet_type'];
            $temp = $this->requestBuffer[$request_id]['packet'];
            unset($this->requestBuffer[$request_id]);
            return $temp;
        }

        // in SSH2.php the timeout is cumulative per function call. eg. exec() will
        // timeout after 10s. but for SFTP.php it's cumulative per packet
        $this->curTimeout = $this->timeout;
        $this->is_timeout = false;

        $start = microtime(true);

        // SFTP packet length
        while (strlen($this->packet_buffer) < 4) {
            $temp = $this->get_channel_packet(self::CHANNEL, true);
            if ($temp === true) {
                if ($this->channel_status[self::CHANNEL] === SSH2MessageType::CHANNEL_CLOSE) {
                    $this->channel_close = true;
                }
                $this->packet_type = -1;
                $this->packet_buffer = '';
                throw new UnexpectedValueException('Error reading next SFTP packet');
            }
            $this->packet_buffer .= $temp;
        }
        if (strlen($this->packet_buffer) < 4) {
            throw new UnexpectedValueException('Packet is too small');
        }
        ['length' => $length] = unpack('Nlength', Strings::shift($this->packet_buffer, 4));

        $tempLength = $length;
        $tempLength -= strlen($this->packet_buffer);

        // 256 * 1024 is what SFTP_MAX_MSG_LENGTH is set to in OpenSSH's sftp-common.h
        if (!$this->allow_arbitrary_length_packets && !$this->use_request_id && $tempLength > 256 * 1024) {
            throw new UnexpectedValueException('Invalid Size');
        }

        // SFTP packet type and data payload
        while ($tempLength > 0) {
            $temp = $this->get_channel_packet(self::CHANNEL, true);
            if ($temp === true) {
                if ($this->channel_status[self::CHANNEL] === SSH2MessageType::CHANNEL_CLOSE) {
                    $this->channel_close = true;
                }
                $this->packet_type = -1;
                $this->packet_buffer = '';
                throw new UnexpectedValueException('Error reading next SFTP packet');
            }
            $this->packet_buffer .= $temp;
            $tempLength -= strlen($temp);
        }

        $stop = microtime(true);

        $this->packet_type = ord(Strings::shift($this->packet_buffer));

        if ($this->use_request_id) {
            ['packet_id' => $packet_id] = unpack('Npacket_id', Strings::shift($this->packet_buffer, 4)); // remove the request id
            $length -= 5; // account for the request id and the packet type
        } else {
            $length -= 1; // account for the packet type
        }

        $packet = Strings::shift($this->packet_buffer, $length);

        if (defined('NET_SFTP_LOGGING')) {
            $packet_type = '<- SSH_FXP_' . SFTPPacketType::getConstantNameByValue($this->packet_type) .
                ' (' . round($stop - $start, 4) . 's)';
            $this->append_log($packet_type, $packet);
        }

        if (isset($request_id) && $this->use_request_id && $packet_id != $request_id) {
            $this->requestBuffer[$packet_id] = [
                'packet_type' => $this->packet_type,
                'packet' => $packet,
            ];
            return $this->get_sftp_packet($request_id);
        }

        return $packet;
    }

    /**
     * Logs data packets
     *
     * Makes sure that only the last 1MB worth of packets will be logged
     */
    private function append_log(string $message_number, string $message): void
    {
        if (!defined('NET_SFTP_LOGGING')) {
            throw new InvalidModeException('Logging is not enabled');
        }
        $this->append_log_helper(
            NET_SFTP_LOGGING,
            $message_number,
            $message,
            $this->packet_type_log,
            $this->packet_log,
            $this->log_size,
            $this->realtime_log_file,
            $this->realtime_log_wrap,
            $this->realtime_log_size
        );
    }

    /**
     * Returns a log of the packets that have been sent and received.
     *
     * Returns a string if PacketType::LOGGING == self::LOG_COMPLEX, an array if PacketType::LOGGING == self::LOG_SIMPLE and false if !defined('NET_SFTP_LOGGING')
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function getSFTPLog(): array|string|null
    {
        if (!defined('NET_SFTP_LOGGING')) {
            return null;
        }

        return match (NET_SFTP_LOGGING) {
            self::LOG_COMPLEX => $this->format_log($this->packet_log, $this->packet_type_log),
            default => $this->packet_type_log
        };
    }

    /**
     * Returns all errors on the SFTP layer and resets internal error array
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function getErrors(): array
    {
        $copy = $this->sftp_errors;
        $this->sftp_errors = [];
        return $copy;
    }

    /**
     * Get supported SFTP versions
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function getSupportedVersions(): array
    {
        if (!($this->bitmap & SSH2::MASK_LOGIN)) {
            throw new InvalidStateException("Function should not be called before you've logged in");
        }

        if (!$this->partial_init) {
            $this->partial_init_sftp_connection();
        }

        $temp = ['version' => $this->defaultVersion];
        if (isset($this->extensions['versions'])) {
            $temp['extensions'] = $this->extensions['versions'];
        }
        return $temp;
    }

    /**
     * Get supported SFTP extensions
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function getSupportedExtensions(): array
    {
        if (!($this->bitmap & SSH2::MASK_LOGIN)) {
            throw new InvalidStateException("Function should not be called before you've logged in");
        }

        if (!$this->partial_init) {
            $this->partial_init_sftp_connection();
        }

        return $this->extensions;
    }

    /**
     * Get supported SFTP versions
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function getNegotiatedVersion(): int
    {
        $this->precheck();

        return $this->version;
    }

    /**
     * Set preferred version
     *
     * If you're preferred version isn't supported then the highest supported
     * version of SFTP will be utilized. Set to null or false or int(0) to
     * unset the preferred version
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function setPreferredVersion(int $version): void
    {
        $this->preferredVersion = $version;
    }

    /**
     * Disconnect
     *
     * @return false
     */
    protected function disconnect_helper(int $reason): bool
    {
        $this->pwd = false;
        return parent::disconnect_helper($reason);
    }

    /**
     * Enable Date Preservation
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function enableDatePreservation(): void
    {
        $this->preserveTime = true;
    }

    /**
     * Disable Date Preservation
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function disableDatePreservation(): void
    {
        $this->preserveTime = false;
    }

    /**
     * Copy
     *
     * This method (currently) only works if the copy-data extension is available
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function copy(string $oldname, string $newname): void
    {
        $this->precheck();

        $oldname = $this->realpath($oldname);
        $newname = $this->realpath($newname);

        if (!isset($this->extensions['copy-data']) || $this->extensions['copy-data'] !== '1') {
            throw new ServiceUnavailableException(
                "Extension 'copy-data' is not supported by the server. " .
                "Call getSupportedVersions() to see a list of supported extension"
            );
        }

        $size = $this->filesize($oldname);

        $packet = Strings::packSSH2('s', $oldname);
        $packet .= $this->version >= 5 ?
            pack('N3', 0, OpenFlag5::OPEN_EXISTING, 0) :
            pack('N2', OpenFlag::READ, 0);
        $this->send_sftp_packet(SFTPPacketType::OPEN, $packet);
        $response = $this->get_sftp_packet_or_error([SFTPPacketType::HANDLE, SFTPPacketType::STATUS]);
        $oldhandle = substr($response, 4);

        $flags = $this->version >= 5 ?
            OpenFlag5::OPEN_OR_CREATE :
            OpenFlag::WRITE | OpenFlag::CREATE;

        $packet = Strings::packSSH2('s', $newname);
        $packet .= $this->version >= 5 ?
            pack('N3', 0, $flags, 0) :
            pack('N2', $flags, 0);
        $this->send_sftp_packet(SFTPPacketType::OPEN, $packet);

        $response = $this->get_sftp_packet_or_error([SFTPPacketType::HANDLE, SFTPPacketType::STATUS]);
        $newhandle = substr($response, 4);

        $packet = Strings::packSSH2('ssQQsQ', 'copy-data', $oldhandle, 0, $size, $newhandle, 0);
        $this->send_sftp_packet(SFTPPacketType::EXTENDED, $packet);

        try {
            $this->get_sftp_packet_or_error([SFTPPacketType::STATUS], StatusCode::OK);
        } catch (FileSystemException $e) {
            throw $e;
        } finally {
            $this->close_handle($oldhandle);
            $this->close_handle($newhandle);
        }
    }

    /**
     * POSIX Rename
     *
     * Where rename() fails "if there already exists a file with the name specified by newpath"
     * (draft-ietf-secsh-filexfer-02#section-6.5), posix_rename() overwrites the existing file in an atomic fashion.
     * ie. "there is no observable instant in time where the name does not refer to either the old or the new file"
     * (draft-ietf-secsh-filexfer-13#page-39).
     */
    public function posix_rename(string $oldname, string $newname): void
    {
        $this->precheck();

        $oldname = $this->realpath($oldname);
        $newname = $this->realpath($newname);

        if ($this->version >= 5) {
            $packet = Strings::packSSH2('ssN', $oldname, $newname, 2); // 2 = SSH_FXP_RENAME_ATOMIC
            $this->send_sftp_packet(SFTPPacketType::RENAME, $packet);
        } elseif (isset($this->extensions['posix-rename@openssh.com']) && $this->extensions['posix-rename@openssh.com'] === '1') {
            $packet = Strings::packSSH2('sss', 'posix-rename@openssh.com', $oldname, $newname);
            $this->send_sftp_packet(SFTPPacketType::EXTENDED, $packet);
        } else {
            throw new ServiceUnavailableException(
                "Extension 'posix-rename@openssh.com' is not supported by the server. " .
                "Call getSupportedVersions() to see a list of supported extension"
            );
        }

        $this->get_sftp_packet_or_error([SFTPPacketType::STATUS], StatusCode::OK);

        // don't move the stat cache entry over since this operation could very well change the
        // atime and mtime attributes
        //$this->update_stat_cache($newname, $this->query_stat_cache($oldname));
        $this->remove_from_stat_cache($oldname);
        $this->remove_from_stat_cache($newname);
    }

    /**
     * Returns general information about a file system.
     *
     * The function statvfs() returns information about a mounted filesystem.
     * @see https://man7.org/linux/man-pages/man3/statvfs.3.html
     *
     * @return array{
     *     bsize:   int|float,
     *     frsize:  int|float,
     *     blocks:  int|float,
     *     bfree:   int|float,
     *     bavail:  int|float,
     *     files:   int|float,
     *     ffree:   int|float,
     *     favail:  int|float,
     *     fsid:    int|float,
     *     flag:    int|float,
     *     namemax: int|float
     * }
     */
    public function statvfs(string $path): array
    {
        $this->precheck();

        if (!isset($this->extensions['statvfs@openssh.com']) || $this->extensions['statvfs@openssh.com'] !== '2') {
            throw new ServiceUnavailableException(
                "Extension 'statvfs@openssh.com' is not supported by the server. " .
                "Call getSupportedVersions() to see a list of supported extension"
            );
        }

        $realpath = $this->realpath($path);

        $packet = Strings::packSSH2('ss', 'statvfs@openssh.com', $realpath);
        $this->send_sftp_packet(SFTPPacketType::EXTENDED, $packet);

        $response = $this->get_sftp_packet_or_error([SFTPPacketType::EXTENDED_REPLY]);

        /**
         * These requests return a SSH_FXP_STATUS reply on failure. On success they
         * return the following SSH_FXP_EXTENDED_REPLY reply:
         *
         * uint32        id
         * uint64        f_bsize     file system block size
         * uint64        f_frsize     fundamental fs block size
         * uint64        f_blocks     number of blocks (unit f_frsize)
         * uint64        f_bfree      free blocks in file system
         * uint64        f_bavail     free blocks for non-root
         * uint64        f_files      total file inodes
         * uint64        f_ffree      free file inodes
         * uint64        f_favail     free file inodes for to non-root
         * uint64        f_fsid       file system id
         * uint64        f_flag       bit mask of f_flag values
         * uint64        f_namemax    maximum filename length
         */

        /** @var list<int|float> $v */
        $v = Strings::unpackSSH2('QQQQQQQQQQQ', $response);
        return [
            'bsize'   => $v[0],
            'frsize'  => $v[1],
            'blocks'  => $v[2],
            'bfree'   => $v[3],
            'bavail'  => $v[4],
            'files'   => $v[5],
            'ffree'   => $v[6],
            'favail'  => $v[7],
            'fsid'    => $v[8],
            'flag'    => $v[9],
            'namemax' => $v[10],
        ];
    }

    /** @psalm-suppress PossiblyUnusedMethod */
    public function hardlink(string $oldpath, string $newpath): void
    {
        $this->precheck();

        if (!isset($this->extensions['hardlink@openssh.com']) || $this->extensions['hardlink@openssh.com'] !== '1') {
            throw new ServiceUnavailableException(
                "Extension 'hardlink@openssh.com' is not supported by the server. " .
                "Call getSupportedVersions() to see a list of supported extension"
            );
        }

        $oldpath = $this->realpath($oldpath);
        $newpath = $this->realpath($newpath);

        $packet = Strings::packSSH2('sss', 'hardlink@openssh.com', $oldpath, $newpath);
        $this->send_sftp_packet(SFTPPacketType::EXTENDED, $packet);
        $this->get_sftp_packet_or_error([SFTPPacketType::STATUS], StatusCode::OK);

        // this operation will change the ctime and link-count attributes
        // which could be cached depending on sftp version
        $this->remove_from_stat_cache($oldpath);
        // hardlink creation should fail if $newpath exists;
        // removing it from the cache anyway, just to be sure
        $this->remove_from_stat_cache($newpath);
    }

    /**
     * @param int[] $packet_types
     */
    private function get_sftp_packet_or_error(array $packet_types, ?int $expectedStatus = null): string
    {
        $packet = $this->get_sftp_packet();
        if (count($packet_types) > 0 && !in_array($this->packet_type, $packet_types)) {
            if ($this->is_timeout) {
                throw new TimeoutException('SFTP packet timed out');
            }
            $expected = [];
            foreach ($packet_types as $packet_type) {
                $expected[] = 'SSH_FXP_' . SFTPPacketType::getConstantNameByValue($packet_type);
            }
            $expected = implode(' or ', $expected);
            try {
                $type = 'SSH_FXP_' . SFTPPacketType::getConstantNameByValue($this->packet_type);
            } catch (InvalidArgumentException) {
                $type = '# ' . $this->packet_type;
            }
            throw new UnexpectedSFTPPacketException("Expected $expected. Got packet type: $type");
        }
        if ($this->packet_type == SFTPPacketType::STATUS) {
            if (is_null($expectedStatus)) {
                throw $this->throwStatusError($packet);
            }
            /**
             * @var int $status
             */
            [$status] = Strings::unpackSSH2('N', $packet);
            if ($status != $expectedStatus) {
                throw $this->throwStatusError($packet, $status);
            }
        }
        return $packet;
    }
}
<?php

/**
 * Pure-PHP implementation of SSHv2.
 *
 * PHP version 8.1+
 *
 * Here are some examples of how to use this library:
 * <code>
 * <?php
 *    include 'vendor/autoload.php';
 *
 *    $ssh = new \phpseclib4\Net\SSH2('www.domain.tld');
 *    if (!$ssh->login('username', 'password')) {
 *        exit('Login Failed');
 *    }
 *
 *    echo $ssh->exec('pwd');
 *    echo $ssh->exec('ls -la');
 * ?>
 * </code>
 *
 * <code>
 * <?php
 *    include 'vendor/autoload.php';
 *
 *    $key = \phpseclib4\Crypt\PublicKeyLoader::load('...', '(optional) password');
 *
 *    $ssh = new \phpseclib4\Net\SSH2('www.domain.tld');
 *    if (!$ssh->login('username', $key)) {
 *        exit('Login Failed');
 *    }
 *
 *    echo $ssh->read('username@username:~$');
 *    $ssh->write("ls -la\n");
 *    echo $ssh->read('username@username:~$');
 * ?>
 * </code>
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2007-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Net;

use phpseclib4\Common\Functions\Strings;
use phpseclib4\Crypt\{
    Blowfish,
    ChaCha20,
    DH,
    DSA,
    EC,
    Hash,
    RC4,
    RSA,
    Rijndael,
    TripleDES,
    Twofish
};
use phpseclib4\Crypt\Common\{PrivateKey, SymmetricKey};
use phpseclib4\Exception\{
    ConnectionClosedException,
    InvalidArgumentException,
    InvalidModeException,
    InvalidPacketLengthException,
    InvalidStateException,
    LengthException,
    NoSupportedAlgorithmsException,
    ServiceUnavailableException,
    SSHChannelExitSignalException,
    TimeoutException,
    UnexpectedSSHMessageException,
    UnexpectedValueException,
    UnsupportedAlgorithmException,
    UnsupportedCurveException,
    UnsupportedValueException
};
use phpseclib4\Math\BigInteger;
use phpseclib4\Net\SSH2\{
    ChannelConnectionFailureReason,
    DisconnectReason,
    MessageType,
    MessageTypeExtra,
    TerminalMode
};
use phpseclib4\System\SSH\Agent;

/**
 * Pure-PHP implementation of SSHv2.
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class SSH2
{
    /**#@+
     * Compression Types
     *
     */
    /**
     * No compression
     */
    public const NET_SSH2_COMPRESSION_NONE = 1;
    /**
     * zlib compression
     */
    public const NET_SSH2_COMPRESSION_ZLIB = 2;
    /**
     * zlib@openssh.com
     */
    public const NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH = 3;
    /**#@-*/

    // Execution Bitmap Masks
    public const MASK_CONSTRUCTOR   = 0x00000001;
    public const MASK_CONNECTED     = 0x00000002;
    public const MASK_LOGIN_REQ     = 0x00000004;
    public const MASK_LOGIN         = 0x00000008;
    public const MASK_SHELL         = 0x00000010;
    public const MASK_DISCONNECT    = 0x00000020;

    /*
     * Channel constants
     *
     * RFC4254 refers not to client and server channels but rather to sender and recipient channels.  we don't refer
     * to them in that way because RFC4254 toggles the meaning. the client sends a SSH_MSG_CHANNEL_OPEN message with
     * a sender channel and the server sends a SSH_MSG_CHANNEL_OPEN_CONFIRMATION in response, with a sender and a
     * recipient channel.  at first glance, you might conclude that SSH_MSG_CHANNEL_OPEN_CONFIRMATION's sender channel
     * would be the same thing as SSH_MSG_CHANNEL_OPEN's sender channel, but it's not, per this snippet:
     *     The 'recipient channel' is the channel number given in the original
     *     open request, and 'sender channel' is the channel number allocated by
     *     the other side.
     *
     * @see \phpseclib4\Net\SSH2::send_channel_packet()
     * @see \phpseclib4\Net\SSH2::get_channel_packet()
     */
    public const CHANNEL_EXEC          = 1; // PuTTy uses 0x100
    public const CHANNEL_SHELL         = 2;
    public const CHANNEL_SUBSYSTEM     = 3;
    public const CHANNEL_AGENT_FORWARD = 4;
    public const CHANNEL_KEEP_ALIVE    = 5;

    /**
     * Returns the message numbers
     *
     * @see \phpseclib4\Net\SSH2::getLog()
     */
    public const LOG_SIMPLE = 1;
    /**
     * Returns the message content
     *
     * @see \phpseclib4\Net\SSH2::getLog()
     */
    public const LOG_COMPLEX = 2;
    /**
     * Outputs the content real-time
     */
    public const LOG_REALTIME = 3;
    /**
     * Dumps the content real-time to a file
     */
    public const LOG_REALTIME_FILE = 4;
    /**
     * Outputs the message numbers real-time
     */
    public const LOG_SIMPLE_REALTIME = 5;
    /*
     * Dumps the message numbers real-time
     */
    public const LOG_REALTIME_SIMPLE = 5;
    /**
     * Make sure that the log never gets larger than this
     *
     * @see \phpseclib4\Net\SSH2::getLog()
     */
    public const LOG_MAX_SIZE = 1048576; // 1024 * 1024

    /**
     * Returns when a string matching $expect exactly is found
     *
     * @see \phpseclib4\Net\SSH2::read()
     */
    public const READ_SIMPLE = 1;
    /**
     * Returns when a string matching the regular expression $expect is found
     *
     * @see \phpseclib4\Net\SSH2::read()
     */
    public const READ_REGEX = 2;
    /**
     * Returns whenever a data packet is received.
     *
     * Some data packets may only contain a single character so it may be necessary
     * to call read() multiple times when using this option
     *
     * @see \phpseclib4\Net\SSH2::read()
     */
    public const READ_NEXT = 3;

    /**
     * The SSH identifier
     */
    private string $identifier;

    /**
     * The Socket Object
     *
     * @var resource|closed-resource|null
     */
    public mixed $fsock = null;

    /**
     * Execution Bitmap
     *
     * The bits that are set represent functions that have been called already.  This is used to determine
     * if a requisite function has been successfully executed.  If not, an error should be thrown.
     */
    protected int $bitmap = 0;

    /**
     * Error information
     *
     * @see self::getDebugMessages()
     */
    private array $debugMessages = [];

    /**
     * Server Identifier
     *
     * @see self::getServerIdentification()
     */
    protected string $server_identifier;

    /**
     * Raw Server Identifier
     *
     * @see self::getRawServerIdentification()
     */
    protected string $raw_server_identifier;

    /**
     * Key Exchange Algorithms
     *
     * @see self::getKexAlgorithims()
     */
    private array $kex_algorithms;

    /**
     * Key Exchange Algorithm
     *
     * @see self::getMethodsNegotiated()
     */
    private ?string $kex_algorithm;

    /**
     * Minimum Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods
     *
     * @see self::_key_exchange()
     */
    private int $kex_dh_group_size_min = 1536;

    /**
     * Preferred Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods
     *
     * @see self::_key_exchange()
     */
    private int $kex_dh_group_size_preferred = 2048;

    /**
     * Maximum Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods
     *
     * @see self::_key_exchange()
     */
    private int $kex_dh_group_size_max = 4096;

    /**
     * Server Host Key Algorithms
     *
     * @see self::getServerHostKeyAlgorithms()
     */
    private array $server_host_key_algorithms;

    /**
     * Supported Private Key Algorithms
     *
     * In theory this should be the same as the Server Host Key Algorithms but, in practice,
     * some servers (eg. Azure) will support rsa-sha2-512 as a server host key algorithm but
     * not a private key algorithm
     *
     * @see self::privatekey_login()
     */
    private array $supported_private_key_algorithms;

    /**
     * Encryption Algorithms: Client to Server
     *
     * @see self::getEncryptionAlgorithmsClient2Server()
     */
    private array $encryption_algorithms_client_to_server;

    /**
     * Encryption Algorithms: Server to Client
     *
     * @see self::getEncryptionAlgorithmsServer2Client()
     */
    private array $encryption_algorithms_server_to_client;

    /**
     * MAC Algorithms: Client to Server
     *
     * @see self::getMACAlgorithmsClient2Server()
     */
    private array $mac_algorithms_client_to_server;

    /**
     * MAC Algorithms: Server to Client
     *
     * @see self::getMACAlgorithmsServer2Client()
     */
    private array $mac_algorithms_server_to_client;

    /**
     * Compression Algorithms: Client to Server
     *
     * @see self::getCompressionAlgorithmsClient2Server()
     */
    private array $compression_algorithms_client_to_server;

    /**
     * Compression Algorithms: Server to Client
     *
     * @see self::getCompressionAlgorithmsServer2Client()
     */
    private array $compression_algorithms_server_to_client;

    /**
     * Languages: Server to Client
     */
    private array $languages_server_to_client;

    /**
     * Languages: Client to Server
     */
    private array $languages_client_to_server;

    /**
     * Preferred Algorithms
     *
     * @see self::setPreferredAlgorithms()
     */
    private array $preferred = [];

    /**
     * Block Size for Server to Client Encryption
     *
     * "Note that the length of the concatenation of 'packet_length',
     *  'padding_length', 'payload', and 'random padding' MUST be a multiple
     *  of the cipher block size or 8, whichever is larger.  This constraint
     *  MUST be enforced, even when using stream ciphers."
     *
     *  -- http://tools.ietf.org/html/rfc4253#section-6
     *
     * @see self::__construct()
     * @see self::send_binary_packet()
     */
    private int $encrypt_block_size = 8;

    /**
     * Block Size for Client to Server Encryption
     *
     * @see self::__construct()
     * @see self::get_binary_packet()
     */
    private int $decrypt_block_size = 8;

    /**
     * Server to Client Encryption Object
     *
     * @see self::get_binary_packet()
     */
    private ?SymmetricKey $decrypt = null;

    /**
     * Decryption Algorithm Name
     */
    private string $decryptName;

    /**
     * Decryption Invocation Counter
     *
     * Used by GCM
     */
    private ?string $decryptInvocationCounter;

    /**
     * Fixed Part of Nonce
     *
     * Used by GCM
     */
    private ?string $decryptFixedPart;

    /**
     * Server to Client Length Encryption Object
     *
     * @see self::get_binary_packet()
     */
    private SymmetricKey $lengthDecrypt;

    /**
     * Client to Server Encryption Object
     *
     * @see self::send_binary_packet()
     */
    private ?SymmetricKey $encrypt = null;

    /**
     * Encryption Algorithm Name
     */
    private string $encryptName;

    /**
     * Encryption Invocation Counter
     *
     * Used by GCM
     */
    private string $encryptInvocationCounter;

    /**
     * Fixed Part of Nonce
     *
     * Used by GCM
     */
    private string $encryptFixedPart;

    /**
     * Client to Server Length Encryption Object
     *
     * @see self::send_binary_packet()
     */
    private SymmetricKey $lengthEncrypt;

    /**
     * Client to Server HMAC Object
     *
     * @see self::send_binary_packet()
     */
    private Hash|\stdClass|null $hmac_create = null;

    /**
     * Client to Server HMAC Name
     */
    private string $hmac_create_name;

    /**
     * Client to Server ETM
     */
    private bool $hmac_create_etm = false;

    /**
     * Server to Client HMAC Object
     *
     * @see self::get_binary_packet()
     */
    private Hash|\stdClass|null $hmac_check = null;

    /**
     * Server to Client HMAC Name
     */
    private string $hmac_check_name;

    /**
     * Server to Client ETM
     */
    private bool $hmac_check_etm;

    /**
     * Size of server to client HMAC
     *
     * We need to know how big the HMAC will be for the server to client direction so that we know how many bytes to read.
     * For the client to server side, the HMAC object will make the HMAC as long as it needs to be.  All we need to do is
     * append it.
     *
     * @see self::get_binary_packet()
     */
    private ?int $hmac_size = null;

    /**
     * Server Public Host Key
     *
     * @see self::getServerPublicHostKey()
     */
    private string $server_public_host_key;

    /**
     * Session identifier
     *
     * "The exchange hash H from the first key exchange is additionally
     *  used as the session identifier, which is a unique identifier for
     *  this connection."
     *
     *  -- http://tools.ietf.org/html/rfc4253#section-7.2
     *
     * @see self::key_exchange()
     */
    private ?string $session_id = null;

    /**
     * Exchange hash
     *
     * The current exchange hash
     *
     * @see self::key_exchange()
     */
    private string $exchange_hash;

    /**
     * Send Sequence Number
     *
     * See 'Section 6.4.  Data Integrity' of rfc4253 for more info.
     *
     * @see self::send_binary_packet()
     */
    private int $send_seq_no = 0;

    /**
     * Get Sequence Number
     *
     * See 'Section 6.4.  Data Integrity' of rfc4253 for more info.
     *
     * @see self::get_binary_packet()
     */
    private int $get_seq_no = 0;

    /**
     * Server Channels
     *
     * Maps client channels to server channels
     *
     * @see self::get_channel_packet()
     * @see self::exec()
     */
    protected array $server_channels = [];

    /**
     * Channel Read Buffers
     *
     * If a client requests a packet from one channel but receives two packets from another those packets should
     * be placed in a buffer
     *
     * @see self::get_channel_packet()
     * @see self::exec()
     */
    private array $channel_buffers = [];

    /**
     * Channel Write Buffers
     *
     * If a client sends a packet and receives a timeout error mid-transmission, buffer the data written so it
     * can be de-duplicated upon resuming write
     *
     * @see self::send_channel_packet()
     */
    private array $channel_buffers_write = [];

    /**
     * Channel Status
     *
     * Contains the type of the last sent message
     *
     * @see self::get_channel_packet()
     */
    protected array $channel_status = [];

    /**
     * Exit Signal Exceptions
     *
     * @see self::get_channel_packet()
     */
    protected array $exit_signal_exceptions = [];

    /**
     * The identifier of the interactive channel which was opened most recently
     *
     * @see self::getInteractiveChannelId()
     */
    private int $channel_id_last_interactive = 0;

    /**
     * Packet Size
     *
     * Maximum packet size indexed by channel
     *
     * @see self::send_channel_packet()
     */
    protected array $packet_size_client_to_server = [];

    /**
     * Message Number Log
     *
     * @see self::getLog()
     */
    private array $message_number_log = [];

    /**
     * Message Log
     *
     * @see self::getLog()
     */
    private array $message_log = [];

    /**
     * The Window Size
     *
     * Bytes the other party can send before it must wait for the window to be adjusted (0x7FFFFFFF = 2GB)
     *
     * @see self::send_channel_packet()
     * @see self::exec()
     */
    protected int $window_size = 0x7FFFFFFF;

    /**
     * What we resize the window to
     *
     * When PuTTY resizes the window it doesn't add an additional 0x7FFFFFFF bytes - it adds 0x40000000 bytes.
     * Some SFTP clients (GoAnywhere) don't support adding 0x7FFFFFFF to the window size after the fact so
     * we'll just do what PuTTY does
     *
     * @see self::_send_channel_packet()
     * @see self::exec()
     */
    private int $window_resize = 0x40000000;

    /**
     * Window size, server to client
     *
     * Window size indexed by channel
     *
     * @see self::send_channel_packet()
     */
    protected array $window_size_server_to_client = [];

    /**
     * Window size, client to server
     *
     * Window size indexed by channel
     *
     * @see self::get_channel_packet()
     */
    private array $window_size_client_to_server = [];

    /**
     * Server signature
     *
     * Verified against $this->session_id
     *
     * @see self::getServerPublicHostKey()
     */
    private string $signature = '';

    /**
     * Server signature format
     *
     * ssh-rsa or ssh-dss.
     *
     * @see self::getServerPublicHostKey()
     */
    private string $signature_format = '';

    /**
     * Interactive Buffer
     *
     * @see self::read()
     */
    private string $interactiveBuffer = '';

    /**
     * Current log size
     *
     * Should never exceed self::LOG_MAX_SIZE
     *
     * @see self::_send_binary_packet()
     * @see self::_get_binary_packet()
     */
    private int $log_size = 0;

    /**
     * Timeout
     *
     * Is overwritten by the constructor BUT if you're mocking the object
     * the constructor may be bypassed, hence our setting it to -1 initially
     *
     * @see SSH2::setTimeout()
     */
    protected int $timeout = -1;

    /**
     * Current Timeout
     *
     * @see SSH2::get_channel_packet()
     */
    protected int|float|null $curTimeout = null;

    /**
     * Keep Alive Interval
     *
     * @see self::setKeepAlive()
     */
    private ?int $keepAlive = null;

    /**
     * Real-time log file pointer
     *
     * @see self::_append_log()
     * @var resource|closed-resource
     */
    private $realtime_log_file;

    /**
     * Real-time log file size
     *
     * @see self::_append_log()
     */
    private int $realtime_log_size = 0;

    /**
     * Has the signature been validated?
     *
     * @see self::getServerPublicHostKey()
     */
    private bool $signature_validated = false;

    /**
     * Real-time log file wrap boolean
     *
     * @see self::_append_log()
     */
    private bool $realtime_log_wrap = false;

    /**
     * Flag to suppress stderr from output
     *
     * @see self::enableQuietMode()
     */
    private bool $quiet_mode = false;

    /**
     * Time of last read/write network activity
     */
    private ?float $last_packet = null;

    /**
     * Exit status returned from ssh if any
     */
    private int $exit_status;

    /**
     * Flag to request a PTY when using exec()
     *
     * @see self::enablePTY()
     */
    private bool $request_pty = false;

    /**
     * Contents of stdError
     */
    private string $stdErrorLog;

    /**
     * The Last Interactive Response
     *
     * @see self::_keyboard_interactive_process()
     */
    private string $last_interactive_response = '';

    /**
     * Keyboard Interactive Request / Responses
     *
     * @see self::_keyboard_interactive_process()
     */
    private array $keyboard_requests_responses = [];

    /**
     * Banner Message
     *
     * Quoting from the RFC, "in some jurisdictions, sending a warning message before
     * authentication may be relevant for getting legal protection."
     *
     * @see self::_filter()
     * @see self::getBannerMessage()
     */
    private string $banner_message = '';

    /**
     * Did read() timeout or return normally?
     *
     * @see self::isTimeout()
     */
    protected bool $is_timeout = false;

    /**
     * Log Boundary
     *
     * @see self::_format_log()
     */
    private string $log_boundary = ':';

    /**
     * Log Long Width
     *
     * @see self::_format_log()
     */
    private int $log_long_width = 65;

    /**
     * Log Short Width
     *
     * @see self::_format_log()
     */
    private int $log_short_width = 16;

    /**
     * Hostname
     *
     * @see self::__construct()
     * @see self::_connect()
     */
    private string $host;

    /**
     * Port Number
     *
     * @see self::__construct()
     * @see self::connect()
     */
    private int $port;

    /**
     * Number of columns for terminal window size
     *
     * @see self::getWindowColumns()
     * @see self::setWindowColumns()
     * @see self::setWindowSize()
     */
    private int $windowColumns = 80;

    /**
     * Number of columns for terminal window size
     *
     * @see self::getWindowRows()
     * @see self::setWindowRows()
     * @see self::setWindowSize()
     */
    private int $windowRows = 24;

    /**
     * Crypto Engine
     *
     * @see self::setCryptoEngine()
     * @see self::_key_exchange()
     */
    private static ?string $crypto_engine = null;

    /**
     * A System_SSH_Agent for use in the SSH2 Agent Forwarding scenario
     */
    private Agent $agent;

    /**
     * Connection storage to replicate ssh2 extension functionality:
     * {@link http://php.net/manual/en/wrappers.ssh2.php#refsect1-wrappers.ssh2-examples}
     *
     * @var array<string, \WeakReference<SSH2>>
     */
    private static array $connections;

    /**
     * Send the identification string first?
     */
    private bool $send_id_string_first = true;

    /**
     * Send the key exchange initiation packet first?
     */
    private bool $send_kex_first = true;

    /**
     * Some versions of OpenSSH incorrectly calculate the key size
     */
    private bool $bad_key_size_fix = false;

    /**
     * Should we try to re-connect to re-establish keys?
     */
    private bool $login_credentials_finalized = false;

    /**
     * Binary Packet Buffer
     */
    private ?object $binary_packet_buffer = null;

    /**
     * Authentication Credentials
     */
    protected array $auth = [];

    /**
     * Terminal
     */
    private string $term = 'vt100';

    /**
     * The authentication methods that may productively continue authentication.
     *
     * @see https://tools.ietf.org/html/rfc4252#section-5.1
     */
    private ?array $auth_methods_to_continue = null;

    /**
     * Compression method
     */
    private int $compress = self::NET_SSH2_COMPRESSION_NONE;

    /**
     * Decompression method
     */
    private int $decompress = self::NET_SSH2_COMPRESSION_NONE;

    /**
     * Compression context
     */
    private \DeflateContext $compress_context;

    /**
     * Decompression context
     */
    private \InflateContext $decompress_context;

    /**
     * Regenerate Compression Context
     */
    private bool $regenerate_compression_context = false;

    /**
     * Regenerate Decompression Context
     */
    private bool $regenerate_decompression_context = false;

    /**
     * Smart multi-factor authentication flag
     */
    private bool $smartMFA = true;

    /**
     * How many channels are currently opened
     */
    private int $channelCount = 0;

    /**
     * Does the server support multiple channels? If not then error out
     * when multiple channels are attempted to be opened
     */
    private bool $errorOnMultipleChannels;

    /**
     * Bytes Transferred Since Last Key Exchange
     *
     * Includes outbound and inbound totals
     */
    private int $bytesTransferredSinceLastKEX = 0;

    /**
     * After how many transferred byte should phpseclib initiate a key re-exchange?
     */
    private int $doKeyReexchangeAfterXBytes = 1024 * 1024 * 1024;

    /**
     * Has a key re-exchange been initialized?
     */
    private bool $keyExchangeInProgress = false;

    /**
     * KEX Buffer
     *
     * If we're in the middle of a key exchange we want to buffer any additional packets we get until
     * the key exchange is over
     *
     * @see self::_get_binary_packet()
     * @see self::_key_exchange()
     * @see self::exec()
     */
    private array $kex_buffer = [];

    /**
     * Strict KEX Flag
     *
     * If kex-strict-s-v00@openssh.com is present in the first KEX packet it need not
     * be present in subsequent packet
     *
     * @see self::_key_exchange()
     * @see self::exec()
     */
    private bool $strict_kex_flag = false;

    /**
     * Default Constructor.
     *
     * $host can either be a string, representing the host, or a stream resource.
     * If $host is a stream resource then $port doesn't do anything, altho $timeout
     * still will be used
     *
     * @see self::login()
     * @param string|resource $host
     */
    public function __construct(mixed $host, int $port = 22, int $timeout = 10)
    {
        self::$connections[$this->getResourceId()] = \WeakReference::create($this);

        $this->timeout = $timeout;

        if (is_resource($host)) {
            $this->fsock = $host;
            return;
        }

        if (Strings::is_stringable($host)) {
            $this->host = (string) $host;
            $this->port = $port;
        }
    }

    /**
     * Set Crypto Engine Mode
     *
     * Possible $engine values:
     * OpenSSL, Eval, PHP
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public static function setCryptoEngine(string $engine): void
    {
        self::$crypto_engine = $engine;
    }

    /**
     * Send Identification String First
     *
     * https://tools.ietf.org/html/rfc4253#section-4.2 says "when the connection has been established,
     * both sides MUST send an identification string". It does not say which side sends it first. In
     * theory it shouldn't matter but it is a fact of life that some SSH servers are simply buggy
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function sendIdentificationStringFirst(): void
    {
        $this->send_id_string_first = true;
    }

    /**
     * Send Identification String Last
     *
     * https://tools.ietf.org/html/rfc4253#section-4.2 says "when the connection has been established,
     * both sides MUST send an identification string". It does not say which side sends it first. In
     * theory it shouldn't matter but it is a fact of life that some SSH servers are simply buggy
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function sendIdentificationStringLast(): void
    {
        $this->send_id_string_first = false;
    }

    /**
     * Send SSH_MSG_KEXINIT First
     *
     * https://tools.ietf.org/html/rfc4253#section-7.1 says "key exchange begins by each sending
     * sending the [SSH_MSG_KEXINIT] packet". It does not say which side sends it first. In theory
     * it shouldn't matter but it is a fact of life that some SSH servers are simply buggy
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function sendKEXINITFirst(): void
    {
        $this->send_kex_first = true;
    }

    /**
     * Send SSH_MSG_KEXINIT Last
     *
     * https://tools.ietf.org/html/rfc4253#section-7.1 says "key exchange begins by each sending
     * sending the [SSH_MSG_KEXINIT] packet". It does not say which side sends it first. In theory
     * it shouldn't matter but it is a fact of life that some SSH servers are simply buggy
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function sendKEXINITLast(): void
    {
        $this->send_kex_first = false;
    }

    /**
     * stream_select wrapper
     *
     * Quoting https://stackoverflow.com/a/14262151/569976,
     * "The general approach to `EINTR` is to simply handle the error and retry the operation again"
     *
     * This wrapper does that loop
     */
    private static function stream_select(?array &$read, ?array &$write, ?array &$except, ?int $seconds, ?int $microseconds = null): int|false
    {
        $remaining = $seconds + $microseconds / 1000000;
        $start = microtime(true);
        while (true) {
            $result = @stream_select($read, $write, $except, $seconds, $microseconds);
            if ($result !== false) {
                return $result;
            }
            $elapsed = microtime(true) - $start;
            $seconds = (int) ($remaining - floor($elapsed));
            $microseconds = (int) (1000000 * ($remaining - $seconds));
            if ($elapsed >= $remaining) {
                return false;
            }
        }
    }

    /**
     * Connect to an SSHv2 server
     */
    private function connect(): void
    {
        if ($this->bitmap & self::MASK_CONSTRUCTOR) {
            return;
        }

        $this->bitmap |= self::MASK_CONSTRUCTOR;

        $this->curTimeout = $this->timeout;

        if (!is_resource($this->fsock)) {
            $start = microtime(true);
            // with stream_select a timeout of 0 means that no timeout takes place;
            // with fsockopen a timeout of 0 means that you instantly timeout
            // to resolve this incompatibility a timeout of 100,000 will be used for fsockopen if timeout is 0
            $this->fsock = @fsockopen($this->host, $this->port, $errno, $errstr, $this->curTimeout == 0 ? 100000 : $this->curTimeout);
            if (!$this->fsock) {
                $host = $this->host . ':' . $this->port;
                throw new ServiceUnavailableException(rtrim("Cannot connect to $host. Error $errno. $errstr"));
            }
            $elapsed = microtime(true) - $start;

            if ($this->curTimeout) {
                $this->curTimeout -= $elapsed;
                if ($this->curTimeout < 0) {
                    throw new TimeoutException('Connection timed out whilst attempting to open socket connection');
                }
            }

            if (defined('NET_SSH2_LOGGING')) {
                $this->append_log('(fsockopen took ' . round($elapsed, 4) . 's)', '');
            }
        }

        $this->identifier = $this->generate_identifier();

        if ($this->send_id_string_first) {
            $start = microtime(true);
            fwrite($this->fsock, $this->identifier . "\r\n");
            $elapsed = round(microtime(true) - $start, 4);
            if (defined('NET_SSH2_LOGGING')) {
                $this->append_log("-> (network: $elapsed)", $this->identifier . "\r\n");
            }
        }

        /* According to the SSH2 specs,

          "The server MAY send other lines of data before sending the version
           string.  Each line SHOULD be terminated by a Carriage Return and Line
           Feed.  Such lines MUST NOT begin with "SSH-", and SHOULD be encoded
           in ISO-10646 UTF-8 [RFC3629] (language is not specified).  Clients
           MUST be able to process such lines." */
        $data = '';
        $totalElapsed = 0;
        while (!feof($this->fsock) && !preg_match('#(.*)^(SSH-(\d\.\d+).*)#ms', $data, $matches)) {
            $line = '';
            while (true) {
                if ($this->curTimeout) {
                    if ($this->curTimeout < 0) {
                        throw new TimeoutException('Connection timed out whilst receiving server identification string');
                    }
                    $read = [$this->fsock];
                    $write = $except = null;
                    $start = microtime(true);
                    $sec = (int) floor($this->curTimeout);
                    $usec = (int) (1000000 * ($this->curTimeout - $sec));
                    if (static::stream_select($read, $write, $except, $sec, $usec) === false) {
                        throw new TimeoutException('Connection timed out whilst receiving server identification string');
                    }
                    $elapsed = microtime(true) - $start;
                    $totalElapsed += $elapsed;
                    $this->curTimeout -= $elapsed;
                }

                $temp = stream_get_line($this->fsock, 255, "\n");
                if ($temp === false) {
                    throw new UnexpectedValueException('Error reading SSH identification string; are you sure you\'re connecting to an SSH server?');
                }

                $line .= $temp;
                if (strlen($temp) == 255) {
                    continue;
                }

                $line .= "\n";

                break;
            }

            $data .= $line;
        }

        if (defined('NET_SSH2_LOGGING')) {
            $this->append_log('<- (network: ' . round($totalElapsed, 4) . ')', $data);
        }

        if (feof($this->fsock)) {
            $this->bitmap = 0;
            throw new ConnectionClosedException('Connection closed by server; are you sure you\'re connected to an SSH server?');
        }

        // earlier the SSH specs were quoted.
        // "The server MAY send other lines of data before sending the version string." they said.
        // the implication of this is that the lines of data before the server string are *not* a part of it
        // getting this right is important because the correct server identifier needs to be fed into the
        // exchange hash for the shared keys to be calculated correctly
        $this->raw_server_identifier = $data;
        $data = explode("\r\n", trim($data, "\r\n"));
        $this->server_identifier = $data[count($data) - 1];

        if (version_compare($matches[3], '1.99', '<')) {
            $this->bitmap = 0;
            throw new UnsupportedValueException("Cannot connect to SSH $matches[3] servers");
        }

        // Ubuntu's OpenSSH from 5.8 to 6.9 didn't work with multiple channels. see
        // https://bugs.launchpad.net/ubuntu/+source/openssh/+bug/1334916 for more info.
        // https://lists.ubuntu.com/archives/oneiric-changes/2011-July/005772.html discusses
        // when consolekit was incorporated.
        // https://marc.info/?l=openssh-unix-dev&m=163409903417589&w=2 discusses some of the
        // issues with how Ubuntu incorporated consolekit
        $pattern = '#^SSH-2\.0-OpenSSH_([\d.]+)[^ ]* Ubuntu-.*$#';
        $match = preg_match($pattern, $this->server_identifier, $matches);
        $match = $match && version_compare('5.8', $matches[1], '<=');
        $match = $match && version_compare('6.9', $matches[1], '>=');
        $this->errorOnMultipleChannels = $match;

        if (!$this->send_id_string_first) {
            $start = microtime(true);
            fwrite($this->fsock, $this->identifier . "\r\n");
            $elapsed = round(microtime(true) - $start, 4);
            if (defined('NET_SSH2_LOGGING')) {
                $this->append_log("-> (network: $elapsed)", $this->identifier . "\r\n");
            }
        }

        $this->last_packet = microtime(true);

        if (!$this->send_kex_first) {
            $response = $this->get_binary_packet_or_close(MessageType::KEXINIT);
            $this->key_exchange($response);
        }

        if ($this->send_kex_first) {
            $this->key_exchange();
        }

        $this->bitmap |= self::MASK_CONNECTED;
    }

    /**
     * Generates the SSH identifier
     *
     * You should overwrite this method in your own class if you want to use another identifier
     */
    private function generate_identifier(): string
    {
        $identifier = 'SSH-2.0-phpseclib_4.0';

        $ext = [];
        if (extension_loaded('sodium')) {
            $ext[] = 'libsodium';
        }

        if (extension_loaded('openssl')) {
            $ext[] = 'openssl';
        }

        if (extension_loaded('gmp')) {
            $ext[] = 'gmp';
        } elseif (extension_loaded('bcmath')) {
            $ext[] = 'bcmath';
        }

        if (!empty($ext)) {
            $identifier .= ' (' . implode(', ', $ext) . ')';
        }

        return $identifier;
    }

    /**
     * Key Exchange
     */
    private function key_exchange(?string $kexinit_payload_server = null): bool
    {
        $this->bytesTransferredSinceLastKEX = 0;

        $preferred = $this->preferred;
        // for the initial key exchange $send_kex is true (no key re-exchange has been started)
        // for phpseclib initiated key exchanges $send_kex is false
        $send_kex = !$this->keyExchangeInProgress;
        $this->keyExchangeInProgress = true;

        $kex_algorithms = $preferred['kex'] ??
            SSH2::getSupportedKEXAlgorithms();
        $server_host_key_algorithms = $preferred['hostkey'] ??
            SSH2::getSupportedHostKeyAlgorithms();
        $s2c_encryption_algorithms = $preferred['server_to_client']['crypt'] ??
            SSH2::getSupportedEncryptionAlgorithms();
        $c2s_encryption_algorithms = $preferred['client_to_server']['crypt'] ??
            SSH2::getSupportedEncryptionAlgorithms();
        $s2c_mac_algorithms = $preferred['server_to_client']['mac'] ??
            SSH2::getSupportedMACAlgorithms();
        $c2s_mac_algorithms = $preferred['client_to_server']['mac'] ??
            SSH2::getSupportedMACAlgorithms();
        $s2c_compression_algorithms = $preferred['server_to_client']['comp'] ??
            SSH2::getSupportedCompressionAlgorithms();
        $c2s_compression_algorithms = $preferred['client_to_server']['comp'] ??
            SSH2::getSupportedCompressionAlgorithms();

        $kex_algorithms = array_merge($kex_algorithms, ['ext-info-c', 'kex-strict-c-v00@openssh.com']);

        // some SSH servers have buggy implementations of some of the above algorithms
        switch (true) {
            case $this->server_identifier == 'SSH-2.0-SSHD':
            case substr($this->server_identifier, 0, 13) == 'SSH-2.0-DLINK':
                if (!isset($preferred['server_to_client']['mac'])) {
                    $s2c_mac_algorithms = array_values(array_diff(
                        $s2c_mac_algorithms,
                        ['hmac-sha1-96', 'hmac-md5-96']
                    ));
                }
                if (!isset($preferred['client_to_server']['mac'])) {
                    $c2s_mac_algorithms = array_values(array_diff(
                        $c2s_mac_algorithms,
                        ['hmac-sha1-96', 'hmac-md5-96']
                    ));
                }
                break;
            case substr($this->server_identifier, 0, 24) == 'SSH-2.0-TurboFTP_SERVER_':
                if (!isset($preferred['server_to_client']['crypt'])) {
                    $s2c_encryption_algorithms = array_values(array_diff(
                        $s2c_encryption_algorithms,
                        ['aes128-gcm@openssh.com', 'aes256-gcm@openssh.com']
                    ));
                }
                if (!isset($preferred['client_to_server']['crypt'])) {
                    $c2s_encryption_algorithms = array_values(array_diff(
                        $c2s_encryption_algorithms,
                        ['aes128-gcm@openssh.com', 'aes256-gcm@openssh.com']
                    ));
                }
        }

        $client_cookie = random_bytes(16);

        $kexinit_payload_client = pack('Ca*', MessageType::KEXINIT, $client_cookie);
        $kexinit_payload_client .= Strings::packSSH2(
            'L10bN',
            $kex_algorithms,
            $server_host_key_algorithms,
            $c2s_encryption_algorithms,
            $s2c_encryption_algorithms,
            $c2s_mac_algorithms,
            $s2c_mac_algorithms,
            $c2s_compression_algorithms,
            $s2c_compression_algorithms,
            [], // language, client to server
            [], // language, server to client
            false, // first_kex_packet_follows
            0 // reserved for future extension
        );

        if (!isset($kexinit_payload_server) && $send_kex) {
            $this->send_binary_packet($kexinit_payload_client);

            while (true) {
                $kexinit_payload_server = $this->get_binary_packet();
                switch (ord($kexinit_payload_server[0])) {
                    case MessageType::KEXINIT:
                        break 2;
                    case MessageType::DISCONNECT:
                        $this->handleDisconnect($kexinit_payload_server);
                }
                $this->kex_buffer[] = $kexinit_payload_server;
            }

            $send_kex = false;
        }

        $response = $kexinit_payload_server;
        Strings::shift($response, 1); // skip past the message number (it should be SSH_MSG_KEXINIT)
        Strings::shift($response, 16); // skip past the server cookie

        [
            $this->kex_algorithms,
            $this->server_host_key_algorithms,
            $this->encryption_algorithms_client_to_server,
            $this->encryption_algorithms_server_to_client,
            $this->mac_algorithms_client_to_server,
            $this->mac_algorithms_server_to_client,
            $this->compression_algorithms_client_to_server,
            $this->compression_algorithms_server_to_client,
            $this->languages_client_to_server,
            $this->languages_server_to_client,
            //$first_kex_packet_follows
        ] = Strings::unpackSSH2('L10C', $response);
        if (in_array('kex-strict-s-v00@openssh.com', $this->kex_algorithms)) {
            if (!isset($this->session_id)) {
                // [kex-strict-s-v00@openssh.com is] only valid in the initial SSH2_MSG_KEXINIT and MUST be ignored
                // if [it is] present in subsequent SSH2_MSG_KEXINIT packets
                $this->strict_kex_flag = true;
                if (count($this->kex_buffer)) {
                    throw new UnexpectedValueException('Possible Terrapin Attack detected');
                }
            }
        }

        $this->supported_private_key_algorithms = $this->server_host_key_algorithms;

        if ($send_kex) {
            $this->send_binary_packet($kexinit_payload_client);
        }

        // we need to decide upon the symmetric encryption algorithms before we do the diffie-hellman key exchange

        // we don't initialize any crypto-objects, yet - we do that, later. for now, we need the lengths to make the
        // diffie-hellman key exchange as fast as possible
        $decrypt = self::array_intersect_first($s2c_encryption_algorithms, $this->encryption_algorithms_server_to_client);
        if (!$decrypt || ($decryptKeyLength = $this->encryption_algorithm_to_key_size($decrypt)) === null) {
            $this->disconnect_helper(DisconnectReason::KEY_EXCHANGE_FAILED);
            throw new NoSupportedAlgorithmsException('No compatible server to client encryption algorithms found');
        }

        $encrypt = self::array_intersect_first($c2s_encryption_algorithms, $this->encryption_algorithms_client_to_server);
        if (!$encrypt || ($encryptKeyLength = $this->encryption_algorithm_to_key_size($encrypt)) === null) {
            $this->disconnect_helper(DisconnectReason::KEY_EXCHANGE_FAILED);
            throw new NoSupportedAlgorithmsException('No compatible client to server encryption algorithms found');
        }

        // through diffie-hellman key exchange a symmetric key is obtained
        $this->kex_algorithm = self::array_intersect_first($kex_algorithms, $this->kex_algorithms);
        if (!$this->kex_algorithm) {
            $this->disconnect_helper(DisconnectReason::KEY_EXCHANGE_FAILED);
            throw new NoSupportedAlgorithmsException('No compatible key exchange algorithms found');
        }

        $server_host_key_algorithm = self::array_intersect_first($server_host_key_algorithms, $this->server_host_key_algorithms);
        if (!$server_host_key_algorithm) {
            $this->disconnect_helper(DisconnectReason::KEY_EXCHANGE_FAILED);
            throw new NoSupportedAlgorithmsException('No compatible server host key algorithms found');
        }

        $mac_algorithm_out = self::array_intersect_first($c2s_mac_algorithms, $this->mac_algorithms_client_to_server);
        if (!$mac_algorithm_out) {
            $this->disconnect_helper(DisconnectReason::KEY_EXCHANGE_FAILED);
            throw new NoSupportedAlgorithmsException('No compatible client to server message authentication algorithms found');
        }

        $mac_algorithm_in = self::array_intersect_first($s2c_mac_algorithms, $this->mac_algorithms_server_to_client);
        if (!$mac_algorithm_in) {
            $this->disconnect_helper(DisconnectReason::KEY_EXCHANGE_FAILED);
            throw new NoSupportedAlgorithmsException('No compatible server to client message authentication algorithms found');
        }

        $compression_map = [
            'none' => self::NET_SSH2_COMPRESSION_NONE,
            'zlib' => self::NET_SSH2_COMPRESSION_ZLIB,
            'zlib@openssh.com' => self::NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH,
        ];

        $compression_algorithm_in = self::array_intersect_first($s2c_compression_algorithms, $this->compression_algorithms_server_to_client);
        if (!$compression_algorithm_in) {
            $this->disconnect_helper(DisconnectReason::KEY_EXCHANGE_FAILED);
            throw new NoSupportedAlgorithmsException('No compatible server to client compression algorithms found');
        }
        $this->decompress = $compression_map[$compression_algorithm_in];

        $compression_algorithm_out = self::array_intersect_first($c2s_compression_algorithms, $this->compression_algorithms_client_to_server);
        if (!$compression_algorithm_out) {
            $this->disconnect_helper(DisconnectReason::KEY_EXCHANGE_FAILED);
            throw new NoSupportedAlgorithmsException('No compatible client to server compression algorithms found');
        }
        $this->compress = $compression_map[$compression_algorithm_out];

        $kexHash = match ($this->kex_algorithm) {
            'diffie-hellman-group15-sha512',
            'diffie-hellman-group16-sha512',
            'diffie-hellman-group17-sha512',
            'diffie-hellman-group18-sha512',
            'ecdh-sha2-nistp521'
                => new Hash('sha512'),
            'ecdh-sha2-nistp384'
                => new Hash('sha384'),
            'diffie-hellman-group-exchange-sha256',
            'diffie-hellman-group14-sha256',
            'ecdh-sha2-nistp256',
            'curve25519-sha256@libssh.org',
            'curve25519-sha256'
                => new Hash('sha256'),
            default
                => new Hash('sha1')
        };

        // Only relevant in diffie-hellman-group-exchange-sha{1,256}, otherwise empty.

        $exchange_hash_rfc4419 = '';

        if (str_starts_with($this->kex_algorithm, 'curve25519-sha256') || str_starts_with($this->kex_algorithm, 'ecdh-sha2-nistp')) {
            $curve = str_starts_with($this->kex_algorithm, 'curve25519-sha256') ?
                'Curve25519' :
                substr($this->kex_algorithm, 10);
            $ourPrivate = EC::createKey($curve);
            $ourPublicBytes = $ourPrivate->getPublicKey()->getEncodedCoordinates();
            $clientKexInitMessage = MessageTypeExtra::KEX_ECDH_INIT;
            $serverKexReplyMessage = MessageTypeExtra::KEX_ECDH_REPLY;
        } else {
            if (str_starts_with($this->kex_algorithm, 'diffie-hellman-group-exchange')) {
                $dh_group_sizes_packed = pack(
                    'NNN',
                    $this->kex_dh_group_size_min,
                    $this->kex_dh_group_size_preferred,
                    $this->kex_dh_group_size_max
                );
                $packet = pack(
                    'Ca*',
                    MessageTypeExtra::KEXDH_GEX_REQUEST,
                    $dh_group_sizes_packed
                );
                $this->send_binary_packet($packet);
                $this->updateLogHistory('UNKNOWN (34)', 'SSH_MSG_KEXDH_GEX_REQUEST');

                $response = $this->get_binary_packet_or_close(MessageTypeExtra::KEXDH_GEX_GROUP);
                [, $primeBytes, $gBytes] = Strings::unpackSSH2('Css', $response);
                $this->updateLogHistory('UNKNOWN (31)', 'SSH_MSG_KEXDH_GEX_GROUP');

                $prime = new BigInteger($primeBytes, -256);
                $g = new BigInteger($gBytes, -256);

                $exchange_hash_rfc4419 = $dh_group_sizes_packed . Strings::packSSH2(
                    'ss',
                    $primeBytes,
                    $gBytes
                );

                $params = DH::createParameters($prime, $g);
                $clientKexInitMessage = MessageTypeExtra::KEXDH_GEX_INIT;
                $serverKexReplyMessage = MessageTypeExtra::KEXDH_GEX_REPLY;
            } else {
                $params = DH::createParameters($this->kex_algorithm);
                $clientKexInitMessage = MessageType::KEXDH_INIT;
                $serverKexReplyMessage = MessageType::KEXDH_REPLY;
            }

            $keyLength = min($kexHash->getLengthInBytes(), max($encryptKeyLength, $decryptKeyLength));

            $ourPrivate = DH::createKey($params, 16 * $keyLength); // 2 * 8 * $keyLength
            $ourPublic = $ourPrivate->getPublicKey()->toBigInteger();
            $ourPublicBytes = $ourPublic->toBytes(true);
        }

        $data = pack('CNa*', $clientKexInitMessage, strlen($ourPublicBytes), $ourPublicBytes);

        $this->send_binary_packet($data);

        switch ($clientKexInitMessage) {
            case MessageTypeExtra::KEX_ECDH_INIT:
                $this->updateLogHistory('SSH_MSG_KEXDH_INIT', 'SSH_MSG_KEX_ECDH_INIT');
                break;
            case MessageTypeExtra::KEXDH_GEX_INIT:
                $this->updateLogHistory('UNKNOWN (32)', 'SSH_MSG_KEXDH_GEX_INIT');
        }

        $response = $this->get_binary_packet_or_close($serverKexReplyMessage);

        [
            , // $type (which was already checked with get_binary_packet_or_close()
            $server_public_host_key,
            $theirPublicBytes,
            $this->signature
        ] = Strings::unpackSSH2('Csss', $response);

        switch ($serverKexReplyMessage) {
            case MessageTypeExtra::KEX_ECDH_REPLY:
                $this->updateLogHistory('SSH_MSG_KEXDH_REPLY', 'SSH_MSG_KEX_ECDH_REPLY');
                break;
            case MessageTypeExtra::KEXDH_GEX_REPLY:
                $this->updateLogHistory('UNKNOWN (33)', 'SSH_MSG_KEXDH_GEX_REPLY');
        }

        $this->server_public_host_key = $server_public_host_key;
        [$public_key_format] = Strings::unpackSSH2('s', $server_public_host_key);
        if (strlen($this->signature) < 4) {
            throw new LengthException('The signature needs at least four bytes');
        }
        $temp = unpack('Nlength', substr($this->signature, 0, 4));
        $this->signature_format = substr($this->signature, 4, $temp['length']);

        $keyBytes = DH::computeSecret($ourPrivate, $theirPublicBytes);
        /*
          "Unnecessary leading bytes with the value 0 or 255 MUST NOT be
           included.  The value zero MUST be stored as a string with zero
           bytes of data."
          -- https://www.rfc-editor.org/info/rfc4251/#section-5 (mpint section)
        */
        $keyBytes = ltrim($keyBytes, "\0");
        if (strlen($keyBytes) && ($keyBytes[0] & "\x80") === "\x80") {
            $keyBytes = "\0$keyBytes";
        }

        $this->exchange_hash = Strings::packSSH2(
            's5',
            $this->identifier,
            $this->server_identifier,
            $kexinit_payload_client,
            $kexinit_payload_server,
            $this->server_public_host_key
        );
        $this->exchange_hash .= $exchange_hash_rfc4419;
        $this->exchange_hash .= Strings::packSSH2(
            's3',
            $ourPublicBytes,
            $theirPublicBytes,
            $keyBytes
        );

        $this->exchange_hash = $kexHash->hash($this->exchange_hash);
        $this->session_id ??= $this->exchange_hash;

        $expected_key_format = match ($server_host_key_algorithm) {
            'rsa-sha2-256', 'rsa-sha2-512' => 'ssh-rsa',
            default => $server_host_key_algorithm
        };

        if ($public_key_format != $expected_key_format || $this->signature_format != $server_host_key_algorithm) {
            switch (true) {
                case $this->signature_format == $server_host_key_algorithm:
                case $server_host_key_algorithm != 'rsa-sha2-256' && $server_host_key_algorithm != 'rsa-sha2-512':
                case $this->signature_format != 'ssh-rsa':
                    $this->disconnect_helper(DisconnectReason::HOST_KEY_NOT_VERIFIABLE);
                    throw new UnexpectedValueException('Server Host Key Algorithm Mismatch (' . $this->signature_format . ' vs ' . $server_host_key_algorithm . ')');
            }
        }

        $packet = pack('C', MessageType::NEWKEYS);
        $this->send_binary_packet($packet);
        $this->get_binary_packet_or_close(MessageType::NEWKEYS);

        $this->keyExchangeInProgress = false;

        if ($this->strict_kex_flag) {
            $this->get_seq_no = $this->send_seq_no = 0;
        }

        $keyBytes = pack('Na*', strlen($keyBytes), $keyBytes);

        $this->encrypt = self::encryption_algorithm_to_crypt_instance($encrypt);
        if ($this->encrypt) {
            if (self::$crypto_engine) {
                $this->encrypt->setPreferredEngine(self::$crypto_engine);
            }
            if ($this->encrypt->getBlockLengthInBytes()) {
                $this->encrypt_block_size = $this->encrypt->getBlockLengthInBytes();
            }
            $this->encrypt->disablePadding();

            if ($this->encrypt->usesIV()) {
                $iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'A' . $this->session_id);
                while ($this->encrypt_block_size > strlen($iv)) {
                    $iv .= $kexHash->hash($keyBytes . $this->exchange_hash . $iv);
                }
                $this->encrypt->setIV(substr($iv, 0, $this->encrypt_block_size));
            }

            switch ($encrypt) {
                case 'aes128-gcm@openssh.com':
                case 'aes256-gcm@openssh.com':
                    $nonce = $kexHash->hash($keyBytes . $this->exchange_hash . 'A' . $this->session_id);
                    $this->encryptFixedPart = substr($nonce, 0, 4);
                    $this->encryptInvocationCounter = substr($nonce, 4, 8);
                    // no break
                case 'chacha20-poly1305@openssh.com':
                    break;
                default:
                    $this->encrypt->enableContinuousBuffer();
            }

            $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'C' . $this->session_id);
            while ($encryptKeyLength > strlen($key)) {
                $key .= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
            }
            switch ($encrypt) {
                case 'chacha20-poly1305@openssh.com':
                    $encryptKeyLength = 32;
                    $this->lengthEncrypt = self::encryption_algorithm_to_crypt_instance($encrypt);
                    $this->lengthEncrypt->setKey(substr($key, 32, 32));
            }
            $this->encrypt->setKey(substr($key, 0, $encryptKeyLength));
            $this->encryptName = $encrypt;
        }

        $this->decrypt = self::encryption_algorithm_to_crypt_instance($decrypt);
        if ($this->decrypt) {
            if (self::$crypto_engine) {
                $this->decrypt->setPreferredEngine(self::$crypto_engine);
            }
            if ($this->decrypt->getBlockLengthInBytes()) {
                $this->decrypt_block_size = $this->decrypt->getBlockLengthInBytes();
            }
            $this->decrypt->disablePadding();

            if ($this->decrypt->usesIV()) {
                $iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'B' . $this->session_id);
                while ($this->decrypt_block_size > strlen($iv)) {
                    $iv .= $kexHash->hash($keyBytes . $this->exchange_hash . $iv);
                }
                $this->decrypt->setIV(substr($iv, 0, $this->decrypt_block_size));
            }

            switch ($decrypt) {
                case 'aes128-gcm@openssh.com':
                case 'aes256-gcm@openssh.com':
                    // see https://tools.ietf.org/html/rfc5647#section-7.1
                    $nonce = $kexHash->hash($keyBytes . $this->exchange_hash . 'B' . $this->session_id);
                    $this->decryptFixedPart = substr($nonce, 0, 4);
                    $this->decryptInvocationCounter = substr($nonce, 4, 8);
                    // no break
                case 'chacha20-poly1305@openssh.com':
                    break;
                default:
                    $this->decrypt->enableContinuousBuffer();
            }

            $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'D' . $this->session_id);
            while ($decryptKeyLength > strlen($key)) {
                $key .= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
            }
            switch ($decrypt) {
                case 'chacha20-poly1305@openssh.com':
                    $decryptKeyLength = 32;
                    $this->lengthDecrypt = self::encryption_algorithm_to_crypt_instance($decrypt);
                    $this->lengthDecrypt->setKey(substr($key, 32, 32));
            }
            $this->decrypt->setKey(substr($key, 0, $decryptKeyLength));
            $this->decryptName = $decrypt;
        }

        /* The "arcfour128" algorithm is the RC4 cipher, as described in
           [SCHNEIER], using a 128-bit key.  The first 1536 bytes of keystream
           generated by the cipher MUST be discarded, and the first byte of the
           first encrypted packet MUST be encrypted using the 1537th byte of
           keystream.

           -- http://tools.ietf.org/html/rfc4345#section-4 */
        if ($encrypt == 'arcfour128' || $encrypt == 'arcfour256') {
            $this->encrypt->encrypt(str_repeat("\0", 1536));
        }
        if ($decrypt == 'arcfour128' || $decrypt == 'arcfour256') {
            $this->decrypt->decrypt(str_repeat("\0", 1536));
        }

        if (!$this->encrypt->usesNonce()) {
            [$this->hmac_create, $createKeyLength] = self::mac_algorithm_to_hash_instance($mac_algorithm_out);
        } else {
            $this->hmac_create = new \stdClass();
            $this->hmac_create_name = $mac_algorithm_out;
            //$mac_algorithm_out = 'none';
            $createKeyLength = 0;
        }

        if ($this->hmac_create instanceof Hash) {
            $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'E' . $this->session_id);
            while ($createKeyLength > strlen($key)) {
                $key .= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
            }
            $this->hmac_create->setKey(substr($key, 0, $createKeyLength));
            $this->hmac_create_name = $mac_algorithm_out;
            $this->hmac_create_etm = (bool) preg_match('#-etm@openssh\.com$#', $mac_algorithm_out);
        }

        if (!$this->decrypt->usesNonce()) {
            [$this->hmac_check, $checkKeyLength] = self::mac_algorithm_to_hash_instance($mac_algorithm_in);
            $this->hmac_size = $this->hmac_check->getLengthInBytes();
        } else {
            $this->hmac_check = new \stdClass();
            $this->hmac_check_name = $mac_algorithm_in;
            //$mac_algorithm_in = 'none';
            $checkKeyLength = 0;
            $this->hmac_size = 0;
        }

        if ($this->hmac_check instanceof Hash) {
            $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'F' . $this->session_id);
            while ($checkKeyLength > strlen($key)) {
                $key .= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
            }
            $this->hmac_check->setKey(substr($key, 0, $checkKeyLength));
            $this->hmac_check_name = $mac_algorithm_in;
            $this->hmac_check_etm = (bool) preg_match('#-etm@openssh\.com$#', $mac_algorithm_in);
        }

        $this->regenerate_compression_context = $this->regenerate_decompression_context = true;

        return true;
    }

    /**
     * Maps an encryption algorithm name to the number of key bytes.
     *
     * @param string $algorithm Name of the encryption algorithm
     * @return int|null Number of bytes as an integer or null for unknown
     */
    private function encryption_algorithm_to_key_size(string $algorithm): int
    {
        if ($this->bad_key_size_fix && self::bad_algorithm_candidate($algorithm)) {
            return 16;
        }

        return match ($algorithm) {
            'none'
                => 0,
            'aes128-gcm@openssh.com',
            'aes128-cbc',
            'aes128-ctr',
            'arcfour',
            'arcfour128',
            'blowfish-cbc',
            'blowfish-ctr',
            'twofish128-cbc',
            'twofish128-ctr'
                => 16,
            '3des-cbc',
            '3des-ctr',
            'aes192-cbc',
            'aes192-ctr',
            'twofish192-cbc',
            'twofish192-ctr'
                => 24,
            'aes256-gcm@openssh.com',
            'aes256-cbc',
            'aes256-ctr',
            'arcfour256',
            'twofish-cbc',
            'twofish256-cbc',
            'twofish256-ctr'
                => 32,
            'chacha20-poly1305@openssh.com'
                => 64
        };
    }

    /**
     * Maps an encryption algorithm name to an instance of a subclass of
     * \phpseclib4\Crypt\Common\SymmetricKey.
     */
    private static function encryption_algorithm_to_crypt_instance(string $algorithm): ?SymmetricKey
    {
        return match ($algorithm) {
            '3des-cbc' => new TripleDES('cbc'),
            '3des-ctr' => new TripleDES('cbc'),
            'aes256-cbc', 'aes192-cbc', 'aes128-cbc' => new Rijndael('cbc'),
            'aes256-ctr', 'aes192-ctr', 'aes128-ctr' => new Rijndael('ctr'),
            'blowfish-cbc' => new Blowfish('cbc'),
            'blowfish-ctr' => new Blowfish('ctr'),
            'twofish128-cbc', 'twofish192-cbc', 'twofish256-cbc', 'twofish-cbc' => new Twofish('cbc'),
            'twofish128-ctr', 'twofish192-ctr', 'twofish256-ctr' => new Twofish('ctr'),
            'arcfour', 'arcfour128', 'arcfour256' => new RC4(),
            'aes128-gcm@openssh.com', 'aes256-gcm@openssh.com' => new Rijndael('gcm'),
            'chacha20-poly1305@openssh.com' => new ChaCha20(),
            default => null
        };
    }

    /**
     * Maps a hash algorithm name to an instance of a subclass of
     * \phpseclib4\Crypt\Hash.
     *
     * @return array{Hash, int}|null
     */
    private static function mac_algorithm_to_hash_instance(string $algorithm): ?array
    {
        return match ($algorithm) {
            'umac-64@openssh.com', 'umac-64-etm@openssh.com' => [new Hash('umac-64'), 16],
            'umac-128@openssh.com', 'umac-128-etm@openssh.com' => [new Hash('umac-128'), 16],
            'hmac-sha2-512', 'hmac-sha2-512-etm@openssh.com' => [new Hash('sha512'), 64],
            'hmac-sha2-256', 'hmac-sha2-256-etm@openssh.com' => [new Hash('sha256'), 32],
            'hmac-sha1', 'hmac-sha1-etm@openssh.com' => [new Hash('sha1'), 20],
            'hmac-sha1-96' => [new Hash('sha1-96'), 20],
            'hmac-md5' => [new Hash('md5'), 16],
            'hmac-md5-96' => [new Hash('md5-96'), 16],
            default => null
        };
    }

    /**
     * Tests whether or not proposed algorithm has a potential for issues
     *
     * @link https://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/ssh2-aesctr-openssh.html
     * @link https://bugzilla.mindrot.org/show_bug.cgi?id=1291
     * @param string $algorithm Name of the encryption algorithm
     */
    private static function bad_algorithm_candidate(string $algorithm): bool
    {
        return match ($algorithm) {
            'arcfour256', 'aes192-ctr', 'aes256-ctr' => true,
            default => false
        };
    }

    /**
     * Login
     *
     * The $password parameter can be a plaintext password, a \phpseclib4\Crypt\RSA|EC|DSA object, a \phpseclib4\System\SSH\Agent object or an array
     *
     * @see self::_login()
     */
    public function login(string $username, #[\SensitiveParameter] string|PrivateKey|array|Agent ...$args): bool
    {
        if (!($this->bitmap & self::MASK_CONSTRUCTOR)) {
            $this->connect();
        }

        if (!$this->login_credentials_finalized) {
            $this->auth[] = [$username, ...$args];
        }

        // try logging with 'none' as an authentication method first since that's what
        // PuTTY does
        if (
            substr($this->server_identifier ?: '', 0, 15) !== 'SSH-2.0-CoreFTP' &&
            $this->auth_methods_to_continue === null
        ) {
            if ($this->sublogin($username)) {
                return true;
            }
            if (!count($args)) {
                return false;
            }
        }
        return $this->sublogin($username, ...$args);
    }

    /**
     * Login Helper
     *
     * @see self::_login_helper()
     */
    protected function sublogin(string $username, #[\SensitiveParameter] string|PrivateKey|array|Agent ...$args): bool
    {
        if (empty($args)) {
            return $this->login_helper($username);
        }

        while (count($args)) {
            if (!$this->auth_methods_to_continue || !$this->smartMFA) {
                $newargs = $args;
                $args = [];
            } else {
                $newargs = [];
                foreach ($this->auth_methods_to_continue as $method) {
                    switch ($method) {
                        case 'publickey':
                            foreach ($args as $key => $arg) {
                                if ($arg instanceof PrivateKey || $arg instanceof Agent) {
                                    $newargs[] = $arg;
                                    unset($args[$key]);
                                    break;
                                }
                            }
                            break;
                        case 'keyboard-interactive':
                            $hasArray = $hasString = false;
                            foreach ($args as $arg) {
                                if ($hasArray || is_array($arg)) {
                                    $hasArray = true;
                                    break;
                                }
                                if ($hasString || is_string($arg)) {
                                    $hasString = true;
                                    break;
                                }
                            }
                            if ($hasArray && $hasString) {
                                foreach ($args as $arg) {
                                    if (is_array($arg)) {
                                        $newargs[] = $arg;
                                        break 2;
                                    }
                                }
                            }
                            // no break
                        case 'password':
                            foreach ($args as $key => $arg) {
                                $newargs[] = $arg;
                                unset($args[$key]);
                                break;
                            }
                    }
                }
            }

            if (!count($newargs)) {
                return $this->login_helper($username);
            }

            foreach ($newargs as $arg) {
                if ($this->login_helper($username, $arg)) {
                    $this->login_credentials_finalized = true;
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * Login Helper
     *
     * {@internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis}
     *           by sending dummy SSH_MSG_IGNORE messages.}
     */
    private function login_helper(
        string $username,
        #[\SensitiveParameter] string|PrivateKey|array|Agent|null $password = null
    ): bool {
        if (!($this->bitmap & self::MASK_LOGIN_REQ)) {
            $packet = Strings::packSSH2('Cs', MessageType::SERVICE_REQUEST, 'ssh-userauth');
            $this->send_binary_packet($packet);

            try {
                $response = $this->get_binary_packet_or_close(MessageType::SERVICE_ACCEPT);
            } catch (InvalidPacketLengthException $e) {
                // the first opportunity to encounter the "bad key size" error
                if (!$this->bad_key_size_fix && isset($this->decryptName) && self::bad_algorithm_candidate($this->decryptName)) {
                    // bad_key_size_fix is only ever re-assigned to true here
                    // retry the connection with that new setting but we'll
                    // only try it once.
                    $this->bad_key_size_fix = true;
                    return $this->reconnect();
                }
                throw $e;
            }

            [, $service] = Strings::unpackSSH2('Cs', $response);

            if ($service != 'ssh-userauth') {
                $this->disconnect_helper(DisconnectReason::PROTOCOL_ERROR);
                throw new UnexpectedSSHMessageException('Expected SSH_MSG_SERVICE_ACCEPT');
            }
            $this->bitmap |= self::MASK_LOGIN_REQ;
        }

        if (strlen($this->last_interactive_response)) {
            return !is_string($password) && !is_array($password) ? false : $this->keyboard_interactive_process($password);
        }

        if ($password instanceof PrivateKey) {
            return $this->privatekey_login($username, $password);
        }

        if ($password instanceof Agent) {
            return $this->ssh_agent_login($username, $password);
        }

        if (is_array($password)) {
            if ($this->keyboard_interactive_login($username, $password)) {
                $this->bitmap |= self::MASK_LOGIN;
                return true;
            }
            return false;
        }

        if (!isset($password)) {
            $packet = Strings::packSSH2(
                'Cs3',
                MessageType::USERAUTH_REQUEST,
                $username,
                'ssh-connection',
                'none'
            );

            $this->send_binary_packet($packet);

            $response = $this->get_binary_packet_or_close();

            [$type] = Strings::unpackSSH2('C', $response);
            switch ($type) {
                case MessageType::USERAUTH_SUCCESS:
                    $this->bitmap |= self::MASK_LOGIN;
                    return true;
                case MessageType::USERAUTH_FAILURE:
                    [$auth_methods] = Strings::unpackSSH2('L', $response);
                    $this->auth_methods_to_continue = $auth_methods;
                    // no break
                default:
                    return false;
            }
        }

        $packet = Strings::packSSH2(
            'Cs3bs',
            MessageType::USERAUTH_REQUEST,
            $username,
            'ssh-connection',
            'password',
            false,
            $password
        );

        // remove the username and password from the logged packet
        if (!defined('NET_SSH2_LOGGING')) {
            $logged = null;
        } else {
            $logged = Strings::packSSH2(
                'Cs3bs',
                MessageType::USERAUTH_REQUEST,
                $username,
                'ssh-connection',
                'password',
                false,
                'password'
            );
        }

        $this->send_binary_packet($packet, $logged);

        $response = $this->get_binary_packet_or_close();
        [$type] = Strings::unpackSSH2('C', $response);

        switch ($type) {
            case MessageTypeExtra::USERAUTH_PASSWD_CHANGEREQ: // in theory, the password can be changed
                $this->updateLogHistory('SSH_MSG_USERAUTH_INFO_REQUEST', 'SSH_MSG_USERAUTH_PASSWD_CHANGEREQ');

                [$message] = Strings::unpackSSH2('s', $response);
                $this->disconnect_helper(DisconnectReason::AUTH_CANCELLED_BY_USER);
                throw new ServiceUnavailableException("The server is wanting you to change the password ($message)");
            case MessageType::USERAUTH_FAILURE:
                // can we use keyboard-interactive authentication?  if not then either the login is bad or the server employees
                // multi-factor authentication
                [$auth_methods, $partial_success] = Strings::unpackSSH2('Lb', $response);
                $this->auth_methods_to_continue = $auth_methods;
                if (!$partial_success && in_array('keyboard-interactive', $auth_methods)) {
                    if ($this->keyboard_interactive_login($username, $password)) {
                        $this->bitmap |= self::MASK_LOGIN;
                        return true;
                    }
                    return false;
                }
                return false;
            case MessageType::USERAUTH_SUCCESS:
                $this->bitmap |= self::MASK_LOGIN;
                return true;
        }

        return false;
    }

    /**
     * Login via keyboard-interactive authentication
     *
     * See {@link http://tools.ietf.org/html/rfc4256 RFC4256} for details.  This is not a full-featured keyboard-interactive authenticator.
     */
    private function keyboard_interactive_login(string $username, #[\SensitiveParameter] string|array $password): bool
    {
        $packet = Strings::packSSH2(
            'Cs5',
            MessageType::USERAUTH_REQUEST,
            $username,
            'ssh-connection',
            'keyboard-interactive',
            '', // language tag
            '' // submethods
        );
        $this->send_binary_packet($packet);

        return $this->keyboard_interactive_process($password);
    }

    /**
     * Handle the keyboard-interactive requests / responses.
     */
    private function keyboard_interactive_process(string|array ...$responses): bool
    {
        if (strlen($this->last_interactive_response)) {
            $response = $this->last_interactive_response;
        } else {
            $orig = $response = $this->get_binary_packet_or_close();
        }

        [$type] = Strings::unpackSSH2('C', $response);
        switch ($type) {
            case MessageType::USERAUTH_INFO_REQUEST:
                [
                    , // name; may be empty
                    , // instruction; may be empty
                    , // language tag; may be empty
                    $num_prompts
                ] = Strings::unpackSSH2('s3N', $response);

                for ($i = 0; $i < count($responses); $i++) {
                    if (is_array($responses[$i])) {
                        foreach ($responses[$i] as $key => $value) {
                            $this->keyboard_requests_responses[$key] = $value;
                        }
                        unset($responses[$i]);
                    }
                }
                $responses = array_values($responses);

                if (isset($this->keyboard_requests_responses)) {
                    for ($i = 0; $i < $num_prompts; $i++) {
                        [
                            $prompt, // prompt - ie. "Password: "; must not be empty
                            // echo
                        ] = Strings::unpackSSH2('sC', $response);
                        foreach ($this->keyboard_requests_responses as $key => $value) {
                            if (substr($prompt, 0, strlen($key)) == $key) {
                                $responses[] = $value;
                                break;
                            }
                        }
                    }
                }

                // see http://tools.ietf.org/html/rfc4256#section-3.2
                if (strlen($this->last_interactive_response)) {
                    $this->last_interactive_response = '';
                } else {
                    $this->updateLogHistory('UNKNOWN (60)', 'SSH_MSG_USERAUTH_INFO_REQUEST');
                }

                if (!count($responses) && $num_prompts) {
                    $this->last_interactive_response = $orig;
                    return false;
                }

                /*
                   After obtaining the requested information from the user, the client
                   MUST respond with an SSH_MSG_USERAUTH_INFO_RESPONSE message.
                */
                // see http://tools.ietf.org/html/rfc4256#section-3.4
                $packet = $logged = pack('CN', MessageType::USERAUTH_INFO_RESPONSE, count($responses));
                for ($i = 0; $i < count($responses); $i++) {
                    $packet .= Strings::packSSH2('s', $responses[$i]);
                    $logged .= Strings::packSSH2('s', 'dummy-answer');
                }

                $this->send_binary_packet($packet, $logged);

                $this->updateLogHistory('UNKNOWN (61)', 'SSH_MSG_USERAUTH_INFO_RESPONSE');

                /*
                   After receiving the response, the server MUST send either an
                   SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, or another
                   SSH_MSG_USERAUTH_INFO_REQUEST message.
                */
                // maybe phpseclib should force close the connection after x request / responses?  unless something like that is done
                // there could be an infinite loop of request / responses.
                return $this->keyboard_interactive_process();
            case MessageType::USERAUTH_SUCCESS:
                return true;
            case MessageType::USERAUTH_FAILURE:
                [$auth_methods] = Strings::unpackSSH2('L', $response);
                $this->auth_methods_to_continue = $auth_methods;
                return false;
        }

        return false;
    }

    /**
     * Login with an ssh-agent provided key
     */
    private function ssh_agent_login(string $username, Agent $agent): bool
    {
        $this->agent = $agent;
        $keys = $agent->requestIdentities();
        $orig_algorithms = $this->supported_private_key_algorithms;
        foreach ($keys as $key) {
            if ($this->privatekey_login($username, $key)) {
                return true;
            }
            $this->supported_private_key_algorithms = $orig_algorithms;
        }

        return false;
    }

    /**
     * Login with an RSA private key
     *
     * {@internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis}
     *           by sending dummy SSH_MSG_IGNORE messages.}
     */
    private function privatekey_login(string $username, #[\SensitiveParameter] PrivateKey $privatekey): bool
    {
        $publickey = $privatekey->getPublicKey();

        if ($publickey instanceof RSA) {
            $privatekey = $privatekey->withPadding(RSA::SIGNATURE_PKCS1);
            $algos = ['rsa-sha2-256', 'rsa-sha2-512', 'ssh-rsa'];
            if (isset($this->preferred['hostkey'])) {
                $algos = array_intersect($algos, $this->preferred['hostkey']);
            }
            $algo = self::array_intersect_first($algos, $this->supported_private_key_algorithms);
            switch ($algo) {
                case 'rsa-sha2-512':
                    $hash = 'sha512';
                    $signatureType = 'rsa-sha2-512';
                    break;
                case 'rsa-sha2-256':
                    $hash = 'sha256';
                    $signatureType = 'rsa-sha2-256';
                    break;
                //case 'ssh-rsa':
                default:
                    $hash = 'sha1';
                    $signatureType = 'ssh-rsa';
            }
        } elseif ($publickey instanceof EC) {
            $privatekey = $privatekey->withSignatureFormat('SSH2');
            $curveName = $privatekey->getCurve();
            switch ($curveName) {
                case 'Ed25519':
                    $hash = 'sha512';
                    $signatureType = 'ssh-ed25519';
                    break;
                case 'secp256r1': // nistp256
                    $hash = 'sha256';
                    $signatureType = 'ecdsa-sha2-nistp256';
                    break;
                case 'secp384r1': // nistp384
                    $hash = 'sha384';
                    $signatureType = 'ecdsa-sha2-nistp384';
                    break;
                case 'secp521r1': // nistp521
                    $hash = 'sha512';
                    $signatureType = 'ecdsa-sha2-nistp521';
                    break;
                default:
                    if (is_array($curveName)) {
                        throw new UnsupportedCurveException('Specified Curves are not supported by SSH2');
                    }
                    throw new UnsupportedCurveException('Named Curve of ' . $curveName . ' is not supported by phpseclib4\'s SSH2 implementation');
            }
        } elseif ($publickey instanceof DSA) {
            $privatekey = $privatekey->withSignatureFormat('SSH2');
            $hash = 'sha1';
            $signatureType = 'ssh-dss';
        } else {
            throw new UnsupportedAlgorithmException('Please use either an RSA key, an EC one or a DSA key');
        }

        $publickeyStr = $publickey->toString('OpenSSH', ['binary' => true]);

        $part1 = Strings::packSSH2(
            'Csss',
            MessageType::USERAUTH_REQUEST,
            $username,
            'ssh-connection',
            'publickey'
        );
        $part2 = Strings::packSSH2('ss', $signatureType, $publickeyStr);

        $packet = $part1 . chr(0) . $part2;
        $this->send_binary_packet($packet);

        $response = $this->get_binary_packet_or_close(
            MessageType::USERAUTH_SUCCESS,
            MessageType::USERAUTH_FAILURE,
            MessageTypeExtra::USERAUTH_PK_OK
        );

        [$type] = Strings::unpackSSH2('C', $response);
        switch ($type) {
            case MessageType::USERAUTH_FAILURE:
                [$auth_methods] = Strings::unpackSSH2('L', $response);
                if (in_array('publickey', $auth_methods) && substr($signatureType, 0, 9) == 'rsa-sha2-') {
                    $this->supported_private_key_algorithms = array_diff($this->supported_private_key_algorithms, ['rsa-sha2-256', 'rsa-sha2-512']);
                    return $this->privatekey_login($username, $privatekey);
                }
                $this->auth_methods_to_continue = $auth_methods;
                return false;
            case MessageTypeExtra::USERAUTH_PK_OK:
                // we'll just take it on faith that the public key blob and the public key algorithm name are as
                // they should be
                $this->updateLogHistory('SSH_MSG_USERAUTH_INFO_REQUEST', 'SSH_MSG_USERAUTH_PK_OK');
                break;
            case MessageType::USERAUTH_SUCCESS:
                $this->bitmap |= self::MASK_LOGIN;
                return true;
        }

        $packet = $part1 . chr(1) . $part2;
        $privatekey = $privatekey->withHash($hash);
        $signature = $privatekey->sign(Strings::packSSH2('s', $this->session_id) . $packet);
        if ($publickey instanceof RSA) {
            $signature = Strings::packSSH2('ss', $signatureType, $signature);
        }
        $packet .= Strings::packSSH2('s', $signature);

        $this->send_binary_packet($packet);

        $response = $this->get_binary_packet_or_close(
            MessageType::USERAUTH_SUCCESS,
            MessageType::USERAUTH_FAILURE
        );

        [$type] = Strings::unpackSSH2('C', $response);
        switch ($type) {
            case MessageType::USERAUTH_SUCCESS:
                $this->bitmap |= self::MASK_LOGIN;
                return true;
            //case MessageType::USERAUTH_FAILURE:
            default:
                // either the login is bad or the server employs multi-factor authentication
                [$auth_methods] = Strings::unpackSSH2('L', $response);
                $this->auth_methods_to_continue = $auth_methods;
                return false;
        }
    }

    /**
     * Return the currently configured timeout
     */
    public function getTimeout(): int
    {
        return $this->timeout;
    }

    /**
     * Set Timeout
     *
     * $ssh->exec('ping 127.0.0.1'); on a Linux host will never return and will run indefinitely.  setTimeout() makes it so it'll timeout.
     * Setting $timeout to false or 0 will revert to the default socket timeout.
     */
    public function setTimeout(int $timeout): void
    {
        $this->timeout = $this->curTimeout = $timeout;
    }

    /**
     * Set Keep Alive
     *
     * Sends an SSH2_MSG_IGNORE message every x seconds, if x is a positive non-zero number.
     */
    public function setKeepAlive(int $interval): void
    {
        $this->keepAlive = $interval;
    }

    /**
     * Get the output from stdError
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function getStdError(): string
    {
        return $this->stdErrorLog;
    }

    protected function initExec(string $command): void
    {
        $this->curTimeout = $this->timeout;
        $this->is_timeout = false;
        $this->stdErrorLog = '';

        //if ($this->isPTYOpen()) {
        //    throw new InvalidStateException('If you want to run multiple exec()\'s you will need to disable (and re-enable if appropriate) a PTY for each one.');
        //}

        $this->open_channel(self::CHANNEL_EXEC);

        if ($this->request_pty === true) {
            $terminal_modes = pack('C', TerminalMode::TTY_OP_END);
            $packet = Strings::packSSH2(
                'CNsCsN4s',
                MessageType::CHANNEL_REQUEST,
                $this->server_channels[self::CHANNEL_EXEC],
                'pty-req',
                1,
                $this->term,
                $this->windowColumns,
                $this->windowRows,
                0,
                0,
                $terminal_modes
            );

            $this->send_binary_packet($packet);

            $this->channel_status[self::CHANNEL_EXEC] = MessageType::CHANNEL_REQUEST;
            if (!$this->get_channel_packet(self::CHANNEL_EXEC)) {
                $this->disconnect_helper(DisconnectReason::BY_APPLICATION);
                throw new UnexpectedValueException('Unable to request pseudo-terminal');
            }
        }

        // sending a pty-req SSH_MSG_CHANNEL_REQUEST message is unnecessary and, in fact, in most cases, slows things
        // down.  the one place where it might be desirable is if you're doing something like \phpseclib4\Net\SSH2::exec('ping localhost &').
        // with a pty-req SSH_MSG_CHANNEL_REQUEST, exec() will return immediately and the ping process will then
        // then immediately terminate.  without such a request exec() will loop indefinitely.  the ping process won't end but
        // neither will your script.

        // although, in theory, the size of SSH_MSG_CHANNEL_REQUEST could exceed the maximum packet size established by
        // SSH_MSG_CHANNEL_OPEN_CONFIRMATION, RFC4254#section-5.1 states that the "maximum packet size" refers to the
        // "maximum size of an individual data packet". ie. SSH_MSG_CHANNEL_DATA.  RFC4254#section-5.2 corroborates.
        $packet = Strings::packSSH2(
            'CNsCs',
            MessageType::CHANNEL_REQUEST,
            $this->server_channels[self::CHANNEL_EXEC],
            'exec',
            1,
            $command
        );
        $this->send_binary_packet($packet);

        $this->channel_status[self::CHANNEL_EXEC] = MessageType::CHANNEL_REQUEST;

        if (!$this->get_channel_packet(self::CHANNEL_EXEC)) {
            $this->disconnect_helper(DisconnectReason::BY_APPLICATION);
            throw new UnexpectedValueException('Unable to execute command');
        }

        $this->channel_status[self::CHANNEL_EXEC] = MessageType::CHANNEL_DATA;
    }

    /**
     * Execute Command
     *
     * If $callback is set to false then \phpseclib4\Net\SSH2::get_channel_packet(self::CHANNEL_EXEC) will need to be called manually.
     * In all likelihood, this is not a feature you want to be taking advantage of (SCP.php uses it)
     *
     * @psalm-return ($callback is callable ? null : string)
     */
    public function exec(string $command, ?\Closure $callback = null): ?string
    {
        if (!$this->isAuthenticated()) {
            throw new InvalidStateException('Please login before attempting to call exec()');
        }

        $this->initExec($command);

        if ($this->request_pty === true) {
            $this->channel_id_last_interactive = self::CHANNEL_EXEC;
            return null;
        }

        $output = '';
        while (true) {
            try {
                $temp = $this->get_channel_packet(self::CHANNEL_EXEC);
            } catch (SSHChannelExitSignalException $e) {
                $e->partialOutput = $output;
                throw $e;
            }
            if ($temp === true) {
                return $callback ? null : $output;
            }
            if ($callback) {
                if ($callback($temp) === true) {
                    $this->close_channel(self::CHANNEL_EXEC);
                    return null;
                }
            } else {
                $output .= $temp;
            }
        }
    }

    /**
     * How many channels are currently open?
     */
    public function getOpenChannelCount(): int
    {
        return $this->channelCount;
    }

    /**
     * Opens a channel
     *
     * @psalm-suppress InvalidReturnType
     */
    protected function open_channel(int $channel, bool $skip_extended = false): bool
    {
        if (isset($this->channel_status[$channel])) {
            throw new InvalidStateException('Please close the channel (' . $channel . ') before trying to open it again');
        }

        $this->channelCount++;

        if ($this->channelCount > 1 && $this->errorOnMultipleChannels) {
            throw new UnsupportedValueException("Ubuntu's OpenSSH from 5.8 to 6.9 doesn't work with multiple channels");
        }

        // RFC4254 defines the (client) window size as "bytes the other party can send before it must wait for the window to
        // be adjusted".  0x7FFFFFFF is, at 2GB, the max size.  technically, it should probably be decremented, but,
        // honestly, if you're transferring more than 2GB, you probably shouldn't be using phpseclib, anyway.
        // see http://tools.ietf.org/html/rfc4254#section-5.2 for more info
        $this->window_size_server_to_client[$channel] = $this->window_size;
        // 0x8000 is the maximum max packet size, per http://tools.ietf.org/html/rfc4253#section-6.1, although since PuTTy
        // uses 0x4000, that's what will be used here, as well.
        $packet_size = 0x4000;

        $packet = Strings::packSSH2(
            'CsN3',
            MessageType::CHANNEL_OPEN,
            'session',
            $channel,
            $this->window_size_server_to_client[$channel],
            $packet_size
        );

        $this->send_binary_packet($packet);

        $this->channel_status[$channel] = MessageType::CHANNEL_OPEN;

        /** @psalm-suppress InvalidReturnStatement */
        return $this->get_channel_packet($channel, $skip_extended);
    }

    /**
     * Creates an interactive shell
     *
     * Returns bool(true) if the shell was opened.
     * Returns bool(false) if the shell was already open.
     *
     * @see self::isShellOpen()
     * @see self::read()
     * @see self::write()
     */
    public function openShell(): void
    {
        if (!$this->isAuthenticated()) {
            throw new InvalidStateException('Operation disallowed prior to login()');
        }

        $this->open_channel(self::CHANNEL_SHELL);

        $terminal_modes = pack('C', TerminalMode::TTY_OP_END);
        $packet = Strings::packSSH2(
            'CNsbsN4s',
            MessageType::CHANNEL_REQUEST,
            $this->server_channels[self::CHANNEL_SHELL],
            'pty-req',
            true, // want reply
            $this->term,
            $this->windowColumns,
            $this->windowRows,
            0,
            0,
            $terminal_modes
        );

        $this->send_binary_packet($packet);

        $this->channel_status[self::CHANNEL_SHELL] = MessageType::CHANNEL_REQUEST;

        if (!$this->get_channel_packet(self::CHANNEL_SHELL)) {
            throw new UnexpectedValueException('Unable to request pty');
        }

        $packet = Strings::packSSH2(
            'CNsb',
            MessageType::CHANNEL_REQUEST,
            $this->server_channels[self::CHANNEL_SHELL],
            'shell',
            true // want reply
        );
        $this->send_binary_packet($packet);

        $response = $this->get_channel_packet(self::CHANNEL_SHELL);
        if ($response === false) {
            throw new UnexpectedValueException('Unable to request shell');
        }

        $this->channel_status[self::CHANNEL_SHELL] = MessageType::CHANNEL_DATA;

        $this->channel_id_last_interactive = self::CHANNEL_SHELL;

        $this->bitmap |= self::MASK_SHELL;
    }

    /**
     * Return the channel to be used with read(), write(), and reset(), if none were specified
     * @deprecated for lack of transparency in intended channel target, to be potentially replaced
     *             with method which guarantees open-ness of all yielded channels and throws
     *             error for multiple open channels
     * @see self::read()
     * @see self::write()
     */
    private function get_interactive_channel(): int
    {
        switch (true) {
            case $this->is_channel_status_data(self::CHANNEL_SUBSYSTEM):
                return self::CHANNEL_SUBSYSTEM;
            case $this->is_channel_status_data(self::CHANNEL_EXEC):
                return self::CHANNEL_EXEC;
            default:
                return self::CHANNEL_SHELL;
        }
    }

    /**
     * Indicates the DATA status on the given channel
     */
    private function is_channel_status_data(int $channel): bool
    {
        return isset($this->channel_status[$channel]) && $this->channel_status[$channel] == MessageType::CHANNEL_DATA;
    }

    /**
     * Return an available open channel
     */
    private function get_open_channel(): ?int
    {
        $channel = self::CHANNEL_EXEC;
        do {
            if (isset($this->channel_status[$channel]) && $this->channel_status[$channel] == MessageType::CHANNEL_OPEN) {
                return $channel;
            }
        } while ($channel++ < self::CHANNEL_SUBSYSTEM);

        return null;
    }

    /**
     * Request agent forwarding of remote server
     */
    public function requestAgentForwarding(): bool
    {
        $request_channel = $this->get_open_channel();
        if ($request_channel === null) {
            return false;
        }

        $packet = Strings::packSSH2(
            'CNsC',
            MessageType::CHANNEL_REQUEST,
            $this->server_channels[$request_channel],
            'auth-agent-req@openssh.com',
            1
        );

        $this->channel_status[$request_channel] = MessageType::CHANNEL_REQUEST;

        $this->send_binary_packet($packet);

        if (!$this->get_channel_packet($request_channel)) {
            return false;
        }

        $this->channel_status[$request_channel] = MessageType::CHANNEL_OPEN;

        return true;
    }

    /**
     * Returns the output of an interactive shell
     *
     * Returns when there's a match for $expect, which can take the form of a string literal or,
     * if $mode == self::READ_REGEX, a regular expression.
     *
     * If not specifying a channel, an open interactive channel will be selected, or, if there are
     * no open channels, an interactive shell will be created. If there are multiple open
     * interactive channels, a legacy behavior will apply in which channel selection prioritizes
     * an active subsystem, the exec pty, and, lastly, the shell. If using multiple interactive
     * channels, callers are discouraged from relying on this legacy behavior and should specify
     * the intended channel.
     *
     * @see self::write()
     */
    public function read(string $expect = '', int $mode = self::READ_SIMPLE, ?int $channel = null): string|bool
    {
        if (!$this->isAuthenticated()) {
            throw new InvalidStateException('Operation disallowed prior to login()');
        }

        $this->curTimeout = $this->timeout;
        $this->is_timeout = false;

        if ($channel === null) {
            $channel = $this->get_interactive_channel();
        }

        if (!$this->is_channel_status_data($channel) && empty($this->channel_buffers[$channel])) {
            if ($channel != self::CHANNEL_SHELL) {
                throw new InvalidStateException('Data is not available on channel');
            }
            $this->openShell();
        }

        if ($mode == self::READ_NEXT) {
            return $this->get_channel_packet($channel);
        }

        $match = $expect;
        while (true) {
            if ($mode == self::READ_REGEX) {
                preg_match($expect, substr($this->interactiveBuffer, -1024), $matches);
                $match = $matches[0] ?? '';
            }
            $pos = strlen($match) ? strpos($this->interactiveBuffer, $match) : false;
            if ($pos !== false) {
                return Strings::shift($this->interactiveBuffer, $pos + strlen($match));
            }
            try {
                $response = $this->get_channel_packet($channel);
            } catch (SSHChannelExitSignalException $e) {
                $e->partialOutput = $this->interactiveBuffer;
                $this->interactiveBuffer = '';
                throw $e;
            }
            if ($response === true) {
                return Strings::shift($this->interactiveBuffer, strlen($this->interactiveBuffer));
            }

            $this->interactiveBuffer .= $response;
        }
    }

    /**
     * Inputs a command into an interactive shell.
     *
     * If not specifying a channel, an open interactive channel will be selected, or, if there are
     * no open channels, an interactive shell will be created. If there are multiple open
     * interactive channels, a legacy behavior will apply in which channel selection prioritizes
     * an active subsystem, the exec pty, and, lastly, the shell. If using multiple interactive
     * channels, callers are discouraged from relying on this legacy behavior and should specify
     * the intended channel.
     *
     * @param int|null $channel Channel id returned by self::getInteractiveChannelId()
     * @see SSH2::read()
     */
    public function write(string $cmd, ?int $channel = null): void
    {
        if (!$this->isAuthenticated()) {
            throw new InvalidStateException('Operation disallowed prior to login()');
        }

        if ($channel === null) {
            $channel = $this->get_interactive_channel();
        }

        if (!$this->is_channel_status_data($channel)) {
            if ($channel != self::CHANNEL_SHELL) {
                throw new UnexpectedValueException('Data is not available on channel');
            }
            $this->openShell();
        }

        $this->curTimeout = $this->timeout;
        $this->is_timeout = false;
        $this->send_channel_packet($channel, $cmd);
    }

    /**
     * Start a subsystem.
     *
     * Right now only one subsystem at a time is supported. To support multiple subsystem's stopSubsystem() could accept
     * a string that contained the name of the subsystem, but at that point, only one subsystem of each type could be opened.
     * To support multiple subsystem's of the same name maybe it'd be best if startSubsystem() generated a new channel id and
     * returns that and then that that was passed into stopSubsystem() but that'll be saved for a future date and implemented
     * if there's sufficient demand for such a feature.
     *
     * @see self::stopSubsystem()
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function startSubsystem(string $subsystem): bool
    {
        $this->open_channel(self::CHANNEL_SUBSYSTEM);

        $packet = Strings::packSSH2(
            'CNsCs',
            MessageType::CHANNEL_REQUEST,
            $this->server_channels[self::CHANNEL_SUBSYSTEM],
            'subsystem',
            1,
            $subsystem
        );
        $this->send_binary_packet($packet);

        $this->channel_status[self::CHANNEL_SUBSYSTEM] = MessageType::CHANNEL_REQUEST;

        if (!$this->get_channel_packet(self::CHANNEL_SUBSYSTEM)) {
            return false;
        }

        $this->channel_status[self::CHANNEL_SUBSYSTEM] = MessageType::CHANNEL_DATA;

        $this->channel_id_last_interactive = self::CHANNEL_SUBSYSTEM;

        return true;
    }

    /**
     * Stops a subsystem.
     *
     * @see self::startSubsystem()
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function stopSubsystem(): void
    {
        if ($this->isInteractiveChannelOpen(self::CHANNEL_SUBSYSTEM)) {
            $this->close_channel(self::CHANNEL_SUBSYSTEM);
        }
    }

    /**
     * Closes a channel
     *
     * If read() timed out you might want to just close the channel and have it auto-restart on the next read() call
     *
     * If not specifying a channel, an open interactive channel will be selected. If there are
     * multiple open interactive channels, a legacy behavior will apply in which channel selection
     * prioritizes an active subsystem, the exec pty, and, lastly, the shell. If using multiple
     * interactive channels, callers are discouraged from relying on this legacy behavior and
     * should specify the intended channel.
     *
     * @param int|null $channel Channel id returned by self::getInteractiveChannelId()
     */
    public function reset(?int $channel = null): void
    {
        if ($channel === null) {
            $channel = $this->get_interactive_channel();
        }
        if ($this->isInteractiveChannelOpen($channel)) {
            $this->close_channel($channel);
        }
    }

    /**
     * Send EOF on a channel
     *
     * Sends an EOF to the stream; this is typically used to close standard
     * input, while keeping output and error alive.
     *
     * @param int|null $channel Channel id returned by self::getInteractiveChannelId()
     * @return void
     */
    public function sendEOF(?int $channel = null): void
    {
        if ($channel === null) {
            $channel = $this->get_interactive_channel();
        }

        $excludeStatuses = [MessageType::CHANNEL_EOF, MessageType::CHANNEL_CLOSE];
        if (isset($this->channel_status[$channel]) && !in_array($this->channel_status[$channel], $excludeStatuses)) {
            $this->send_binary_packet(pack('CN', MessageType::CHANNEL_EOF, $this->server_channels[$channel]));
        }
    }

    /**
     * Is timeout?
     *
     * Did exec() or read() return because they timed out or because they encountered the end?
     */
    public function isTimeout(): bool
    {
        return $this->is_timeout;
    }

    /**
     * Disconnect
     */
    public function disconnect(): void
    {
        $this->disconnect_helper(DisconnectReason::BY_APPLICATION);
        if (isset($this->realtime_log_file) && is_resource($this->realtime_log_file)) {
            fclose($this->realtime_log_file);
        }
        unset(self::$connections[$this->getResourceId()]);
    }

    /**
     * Destructor.
     *
     * Will be called, automatically, if you're supporting just PHP5.  If you're supporting PHP4, you'll need to call
     * disconnect().
     */
    public function __destruct()
    {
        $this->disconnect();
    }

    /**
     * Is the connection still active?
     *
     * $level has 3x possible values:
     * 0 (default): phpseclib takes a passive approach to see if the connection is still active by calling feof()
     *    on the socket
     * 1: phpseclib takes an active approach to see if the connection is still active by sending an SSH_MSG_IGNORE
     *    packet that doesn't require a response
     * 2: phpseclib takes an active approach to see if the connection is still active by sending an SSH_MSG_CHANNEL_OPEN
     *    packet and imediately trying to close that channel. some routers, in particular, however, will only let you
     *    open one channel, so this approach could yield false positives
     */
    public function isConnected(int $level = 0): bool
    {
        if ($level < 0 || $level > 2) {
            throw new InvalidArgumentException('$level must be 0, 1 or 2');
        }

        if ($level == 0) {
            return ($this->bitmap & self::MASK_CONNECTED) && is_resource($this->fsock) && !feof($this->fsock);
        }
        try {
            if ($level == 1) {
                $this->send_binary_packet(pack('CN', MessageType::IGNORE, 0));
            } else {
                $this->open_channel(self::CHANNEL_KEEP_ALIVE);
                $this->close_channel(self::CHANNEL_KEEP_ALIVE);
            }
            return true;
        } catch (\Exception) {
            return false;
        }
    }

    /**
     * Have you successfully been logged in?
     */
    public function isAuthenticated(): bool
    {
        return (bool) ($this->bitmap & self::MASK_LOGIN);
    }

    /**
     * Is the interactive shell active?
     */
    public function isShellOpen(): bool
    {
        return $this->isInteractiveChannelOpen(self::CHANNEL_SHELL);
    }

    /**
     * Is the exec pty active?
     */
    public function isPTYOpen(): bool
    {
        return $this->isInteractiveChannelOpen(self::CHANNEL_EXEC);
    }

    /**
     * Is the given interactive channel active?
     *
     * @param int $channel Channel id returned by self::getInteractiveChannelId()
     */
    public function isInteractiveChannelOpen(int $channel): bool
    {
        return $this->isAuthenticated() && $this->is_channel_status_data($channel);
    }

    /**
     * Returns a channel identifier, presently of the last interactive channel opened, regardless of current status.
     * Returns 0 if no interactive channel has been opened.
     *
     * @see self::isInteractiveChannelOpen()
     */
    public function getInteractiveChannelId(): int
    {
        return $this->channel_id_last_interactive;
    }

    /**
     * Pings a server connection, or tries to reconnect if the connection has gone down
     *
     * Inspired by http://php.net/manual/en/mysqli.ping.php
     */
    public function ping(): bool
    {
        if (!$this->isAuthenticated()) {
            if (!empty($this->auth)) {
                return $this->reconnect();
            }
            return false;
        }

        try {
            $this->open_channel(self::CHANNEL_KEEP_ALIVE);
        } catch (InvalidStateException) {
            return $this->reconnect();
        }

        $this->close_channel(self::CHANNEL_KEEP_ALIVE);
        return true;
    }

    /**
     * In situ reconnect method
     *
     * @return boolean
     */
    private function reconnect(): bool
    {
        $this->disconnect_helper(DisconnectReason::BY_APPLICATION);
        $this->connect();
        foreach ($this->auth as $auth) {
            $result = $this->login(...$auth);
        }
        return $result;
    }

    /**
     * Resets a connection for re-use
     */
    protected function reset_connection(): void
    {
        if (is_resource($this->fsock) && get_resource_type($this->fsock) === 'stream') {
            fclose($this->fsock);
        }
        $this->fsock = null;
        $this->bitmap = 0;
        $this->binary_packet_buffer = null;
        $this->decrypt = $this->encrypt = null;
        $this->decrypt_block_size = $this->encrypt_block_size = 8;
        $this->hmac_check = $this->hmac_create = null;
        $this->hmac_size = null;
        $this->session_id = null;
        $this->get_seq_no = $this->send_seq_no = 0;
        $this->channel_status = [];
        $this->channel_id_last_interactive = 0;
        $this->channel_buffers = [];
        $this->channel_buffers_write = [];
        $this->exit_signal_exceptions = [];
    }

    /**
     * @return int[] second and microsecond stream timeout options based on user-requested timeout and keep-alive, or the default socket timeout by default, which mirrors PHP socket streams.
     */
    private function get_stream_timeout(): array
    {
        $sec = (int) ini_get('default_socket_timeout');
        $usec = 0;
        if ($this->curTimeout > 0) {
            $sec = (int) floor($this->curTimeout);
            $usec = (int) (1000000 * ($this->curTimeout - $sec));
        }
        if ($this->keepAlive > 0) {
            $elapsed = microtime(true) - $this->last_packet;
            $timeout = max($this->keepAlive - $elapsed, 0);
            if (!$this->curTimeout || $timeout < $this->curTimeout) {
                $sec = (int) floor($timeout);
                $usec = (int) (1000000 * ($timeout - $sec));
            }
        }
        return [$sec, $usec];
    }

    /**
     * Retrieves the next packet with added timeout and type handling
     */
    private function get_binary_packet_or_close(int ...$message_types): string
    {
        try {
            $packet = $this->get_binary_packet();
            if (count($message_types) > 0 && !in_array(ord($packet[0]), $message_types)) {
                $this->disconnect_helper(DisconnectReason::PROTOCOL_ERROR);
                foreach ($message_types as &$type) {
                    try {
                        $type = 'SSH_MSG_' . MessageType::getConstantNameByValue($type);
                    } catch (InvalidArgumentException) {
                        $type = "# $type";
                    }
                }
                unset($type);
                $type = ord($packet[0]);
                try {
                    $type = 'SSH_MSG_' . MessageType::getConstantNameByValue($type);
                } catch (InvalidArgumentException) {
                    $type = "# $type";
                }
                throw new UnexpectedSSHMessageException('Bad message type. Expected: '
                    . implode(', ', $message_types) . ". Got: $type");
            }
            return $packet;
        } catch (TimeoutException) {
            $this->disconnect_helper(DisconnectReason::BY_APPLICATION);
            throw new TimeoutException('Connection closed due to timeout');
        }
    }

    /**
     * Gets Binary Packets
     *
     * See '6. Binary Packet Protocol' of rfc4253 for more info.
     *
     * @see self::_send_binary_packet()
     * @throws TimeoutException If user requested timeout was reached while waiting for next packet
     * @throws ConnectionClosedException If an error has occurred preventing read of the next packet
     */
    private function get_binary_packet(): string
    {
        if (!is_resource($this->fsock)) {
            throw new InvalidArgumentException('fsock is not a resource.');
        }
        if (!$this->keyExchangeInProgress && count($this->kex_buffer)) {
            return $this->filter(array_shift($this->kex_buffer));
        }
        if ($this->binary_packet_buffer == null) {
            // buffer the packet to permit continued reads across timeouts
            $this->binary_packet_buffer = (object) [
                'read_time' => 0, // the time to read the packet from the socket
                'raw' => '', // the raw payload read from the socket
                'plain' => '', // the packet in plain text, excluding packet_length header
                'packet_length' => null, // the packet_length value pulled from the payload
                'size' => $this->decrypt_block_size, // the total size of this packet to be read from the socket
                                                     // initialize to read single block until packet_length is available
            ];
        }
        $packet = $this->binary_packet_buffer;
        while (strlen($packet->raw) < $packet->size) {
            if (feof($this->fsock)) {
                $this->disconnect_helper(DisconnectReason::CONNECTION_LOST);
                throw new ConnectionClosedException('Connection closed by server');
            }
            if ($this->curTimeout < 0) {
                $this->is_timeout = true;
                throw new TimeoutException('Timed out waiting for server');
            }
            $this->send_keep_alive();

            [$sec, $usec] = $this->get_stream_timeout();
            stream_set_timeout($this->fsock, $sec, $usec);
            $start = microtime(true);
            $raw = stream_get_contents($this->fsock, $packet->size - strlen($packet->raw));
            $elapsed = microtime(true) - $start;
            $packet->read_time += $elapsed;
            if ($this->curTimeout > 0) {
                $this->curTimeout -= $elapsed;
            }
            if ($raw === false) {
                $this->disconnect_helper(DisconnectReason::CONNECTION_LOST);
                throw new ConnectionClosedException('Connection closed by server');
            } elseif (!strlen($raw)) {
                continue;
            }
            $packet->raw .= $raw;
            if (!$packet->packet_length) {
                $this->get_binary_packet_size($packet);
            }
        }

        if (strlen($packet->raw) != $packet->size) {
            throw new UnexpectedValueException('Size of packet was not expected length');
        }
        // destroy buffer as packet represents the entire payload and should be processed in full
        $this->binary_packet_buffer = null;
        // copy the raw payload, so as not to destroy original
        $raw = $packet->raw;
        if ($this->hmac_check instanceof Hash) {
            $hmac = Strings::pop($raw, $this->hmac_size);
        }
        $packet_length_header_size = 4;
        if ($this->decrypt) {
            switch ($this->decryptName) {
                case 'aes128-gcm@openssh.com':
                case 'aes256-gcm@openssh.com':
                    $this->decrypt->setNonce(
                        $this->decryptFixedPart .
                        $this->decryptInvocationCounter
                    );
                    Strings::increment_str($this->decryptInvocationCounter);
                    $this->decrypt->setAAD(Strings::shift($raw, $packet_length_header_size));
                    $this->decrypt->setTag(Strings::pop($raw, $this->decrypt_block_size));
                    $packet->plain = $this->decrypt->decrypt($raw);
                    break;
                case 'chacha20-poly1305@openssh.com':
                    // This should be impossible, but we are checking anyway to narrow the type for Psalm.
                    if (!($this->decrypt instanceof ChaCha20)) {
                        throw new InvalidStateException('$this->decrypt is not a ' . ChaCha20::class);
                    }
                    $this->decrypt->setNonce(pack('N2', 0, $this->get_seq_no));
                    $this->decrypt->setCounter(0);
                    // this is the same approach that's implemented in Salsa20::createPoly1305Key()
                    // but we don't want to use the same AEAD construction that RFC8439 describes
                    // for ChaCha20-Poly1305 so we won't rely on it (see Salsa20::poly1305())
                    $this->decrypt->setPoly1305Key(
                        $this->decrypt->encrypt(str_repeat("\0", 32))
                    );
                    $this->decrypt->setAAD(Strings::shift($raw, $packet_length_header_size));
                    $this->decrypt->setCounter(1);
                    $this->decrypt->setTag(Strings::pop($raw, 16));
                    $packet->plain = $this->decrypt->decrypt($raw);
                    break;
                default:
                    if (!$this->hmac_check instanceof Hash || !$this->hmac_check_etm) {
                        // first block was already decrypted for contained packet_length header
                        Strings::shift($raw, $this->decrypt_block_size);
                        if (strlen($raw) > 0) {
                            $packet->plain .= $this->decrypt->decrypt($raw);
                        }
                    } else {
                        Strings::shift($raw, $packet_length_header_size);
                        $packet->plain = $this->decrypt->decrypt($raw);
                    }
                    break;
            }
        } else {
            Strings::shift($raw, $packet_length_header_size);
            $packet->plain = $raw;
        }
        if ($this->hmac_check instanceof Hash) {
            $reconstructed = !$this->hmac_check_etm ?
                pack('Na*', $packet->packet_length, $packet->plain) :
                substr($packet->raw, 0, -$this->hmac_size);
            if (($this->hmac_check->getHash() & "\xFF\xFF\xFF\xFF") == 'umac') {
                $this->hmac_check->setNonce("\0\0\0\0" . pack('N', $this->get_seq_no));
                if (!hash_equals($hmac, $this->hmac_check->hash($reconstructed))) {
                    $this->disconnect_helper(DisconnectReason::MAC_ERROR);
                    throw new UnexpectedValueException('Invalid UMAC');
                }
            } else {
                if (!hash_equals($hmac, $this->hmac_check->hash(pack('Na*', $this->get_seq_no, $reconstructed)))) {
                    $this->disconnect_helper(DisconnectReason::MAC_ERROR);
                    throw new UnexpectedValueException('Invalid HMAC');
                }
            }
        }

        $payload = $packet->plain;
        ['padding_length' => $padding_length] = unpack('Cpadding_length', Strings::shift($payload, 1));
        if ($padding_length > 0) {
            Strings::pop($payload, $padding_length);
        }

        if (!$this->keyExchangeInProgress) {
            $this->bytesTransferredSinceLastKEX += $packet->packet_length + $padding_length + 5;
        }

        if (empty($payload)) {
            $this->disconnect_helper(DisconnectReason::PROTOCOL_ERROR);
            throw new UnexpectedValueException('Plaintext is too short');
        }

        switch ($this->decompress) {
            case self::NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH:
                if (!$this->isAuthenticated()) {
                    break;
                }
                // no break
            case self::NET_SSH2_COMPRESSION_ZLIB:
                if ($this->regenerate_decompression_context) {
                    $this->regenerate_decompression_context = false;

                    $cmf = ord($payload[0]);
                    $cm = $cmf & 0x0F;
                    if ($cm != 8) { // deflate
                        throw new UnsupportedAlgorithmException("Only CM = 8 ('deflate') is supported ($cm)");
                    }
                    $cinfo = ($cmf & 0xF0) >> 4;
                    if ($cinfo > 7) {
                        throw new UnexpectedValueException("CINFO above 7 is not allowed ($cinfo)");
                    }
                    //$windowSize = 1 << ($cinfo + 8);

                    $flg = ord($payload[1]);
                    //$fcheck = $flg & 0x0F;

                    if ((($cmf << 8) | $flg) % 31) {
                        throw new UnexpectedValueException('fcheck failed');
                    }
                    //$fdict = boolval($flg & 0x20);
                    //$flevel = ($flg & 0xC0) >> 6;

                    $this->decompress_context = inflate_init(ZLIB_ENCODING_RAW, ['window' => $cinfo + 8]);
                    $payload = substr($payload, 2);
                }
                if ($this->decompress_context) {
                    $payload = inflate_add($this->decompress_context, $payload, ZLIB_PARTIAL_FLUSH);
                }
        }

        $this->get_seq_no++;

        if (defined('NET_SSH2_LOGGING')) {
            $current = microtime(true);
            /** @psalm-suppress InvalidArrayAccess */
            $message_number = sprintf(
                '<- %s (since last: %s, network: %ss)',
                ($constantName = MessageType::findConstantNameByValue($value = ord($payload[0])))
                    ? "SSH_MSG_$constantName"
                    : "UNKNOWN ($value)",
                round($current - $this->last_packet, 4),
                round($packet->read_time, 4)
            );
            $this->append_log($message_number, $payload);
        }
        $this->last_packet = microtime(true);

        if ($this->bytesTransferredSinceLastKEX > $this->doKeyReexchangeAfterXBytes) {
            $this->key_exchange();
        }

        return $this->filter($payload);
    }

    /**
     * @param object $packet The packet object being constructed, passed by reference
     *        The size, packet_length, and plain properties of this object may be modified in processing
     */
    private function get_binary_packet_size(object &$packet): void
    {
        $packet_length_header_size = 4;
        if (strlen($packet->raw) < $packet_length_header_size) {
            return;
        }
        $added_validation_length = 0; // indicates when the packet length header is included when validating packet length against block size
        if ($this->decrypt) {
            switch ($this->decryptName) {
                case 'aes128-gcm@openssh.com':
                case 'aes256-gcm@openssh.com':
                    ['packet_length' => $packet_length] = unpack('Npacket_length', substr($packet->raw, 0, $packet_length_header_size));
                    $packet->size = $packet_length_header_size + $packet_length + $this->decrypt_block_size; // expect tag
                    break;
                case 'chacha20-poly1305@openssh.com':
                    $this->lengthDecrypt->setNonce(pack('N2', 0, $this->get_seq_no));
                    $packet_length_header = $this->lengthDecrypt->decrypt(substr($packet->raw, 0, $packet_length_header_size));
                    ['packet_length' => $packet_length] = unpack('Npacket_length', $packet_length_header);
                    $packet->size = $packet_length_header_size + $packet_length + 16; // expect tag
                    break;
                default:
                    if (!$this->hmac_check instanceof Hash || !$this->hmac_check_etm) {
                        if (strlen($packet->raw) < $this->decrypt_block_size) {
                            return;
                        }
                        $packet->plain = $this->decrypt->decrypt(substr($packet->raw, 0, $this->decrypt_block_size));
                        ['packet_length' => $packet_length] = unpack('Npacket_length', Strings::shift($packet->plain, $packet_length_header_size));
                        $packet->size = $packet_length_header_size + $packet_length;
                        $added_validation_length = $packet_length_header_size;
                    } else {
                        ['packet_length' => $packet_length] = unpack('Npacket_length', substr($packet->raw, 0, $packet_length_header_size));
                        $packet->size = $packet_length_header_size + $packet_length;
                    }
                    break;
            }
        } else {
            ['packet_length' => $packet_length] = unpack('Npacket_length', substr($packet->raw, 0, $packet_length_header_size));
            $packet->size = $packet_length_header_size + $packet_length;
            $added_validation_length = $packet_length_header_size;
        }
        // quoting <http://tools.ietf.org/html/rfc4253#section-6.1>,
        // "implementations SHOULD check that the packet length is reasonable"
        // PuTTY uses 0x9000 as the actual max packet size and so to shall we
        if (
            $packet_length <= 0 || $packet_length > 0x9000
            || ($packet_length + $added_validation_length) % $this->decrypt_block_size != 0
        ) {
            $this->disconnect_helper(DisconnectReason::PROTOCOL_ERROR);
            throw new InvalidPacketLengthException('Invalid packet length');
        }
        if ($this->hmac_check instanceof Hash) {
            $packet->size += $this->hmac_size;
        }
        $packet->packet_length = $packet_length;
    }

    /**
     * Handle Disconnect
     *
     * Because some binary packets need to be ignored...
     *
     * @see self::filter()
     * @see self::key_exchange()
     */
    private function handleDisconnect($payload): void
    {
        Strings::shift($payload, 1);
        [$reason_code, $message] = Strings::unpackSSH2('Ns', $payload);
        $this->debugMessages[] = 'DISCONNECT: ' .
            DisconnectReason::findConstantNameByValue($reason_code) .
            "\r\n$message";
        $this->disconnect_helper(DisconnectReason::CONNECTION_LOST);
        throw new ConnectionClosedException('Connection closed by server');
    }

    /**
     * Filter Binary Packets
     *
     * Because some binary packets need to be ignored...
     *
     * @see self::_get_binary_packet()
     */
    private function filter(string $payload): string
    {
        if (ord($payload[0]) == MessageType::DISCONNECT) {
            $this->handleDisconnect($payload);
        }

        if (!isset($this->session_id) && $this->keyExchangeInProgress) {
            return $payload;
        }

        switch (ord($payload[0])) {
            case MessageType::IGNORE:
                $payload = $this->get_binary_packet();
                break;
            case MessageType::DEBUG:
                Strings::shift($payload, 2); // second byte is "always_display"
                [$message] = Strings::unpackSSH2('s', $payload);
                $this->debugMessages[] = "DEBUG: $message";
                $payload = $this->get_binary_packet();
                break;
            case MessageType::UNIMPLEMENTED:
                break;
            case MessageType::KEXINIT:
                // this is here for server key re-exchanges after the initial key exchange
                if (!$this->keyExchangeInProgress && isset($this->session_id)) {
                    if (!$this->key_exchange($payload)) {
                        $this->disconnect_helper(DisconnectReason::KEY_EXCHANGE_FAILED);
                        throw new ConnectionClosedException('Key exchange failed');
                    }
                    $payload = $this->get_binary_packet();
                }
                break;
            case MessageType::EXT_INFO:
                Strings::shift($payload, 1);
                [$nr_extensions] = Strings::unpackSSH2('N', $payload);
                for ($i = 0; $i < $nr_extensions; $i++) {
                    [$extension_name, $extension_value] = Strings::unpackSSH2('ss', $payload);
                    if ($extension_name == 'server-sig-algs') {
                        $this->supported_private_key_algorithms = explode(',', $extension_value);
                    }
                }
                $payload = $this->get_binary_packet();
        }

        /*
           Once a party has sent a SSH_MSG_KEXINIT message for key exchange or
           re-exchange, until it has sent a SSH_MSG_NEWKEYS message (Section
           7.3), it MUST NOT send any messages other than:

           o  Transport layer generic messages (1 to 19) (but
              SSH_MSG_SERVICE_REQUEST and SSH_MSG_SERVICE_ACCEPT MUST NOT be
              sent);

           o  Algorithm negotiation messages (20 to 29) (but further
              SSH_MSG_KEXINIT messages MUST NOT be sent);

           o  Specific key exchange method messages (30 to 49).

           -- https://www.rfc-editor.org/rfc/rfc4253#section-7.1
        */
        if ($this->keyExchangeInProgress) {
            return $payload;
        }

        // see http://tools.ietf.org/html/rfc4252#section-5.4; only called when the encryption has been activated and when we haven't already logged in
        if (($this->bitmap & self::MASK_CONNECTED) && !$this->isAuthenticated() && ord($payload[0]) == MessageType::USERAUTH_BANNER) {
            Strings::shift($payload, 1);
            [$this->banner_message] = Strings::unpackSSH2('s', $payload);
            $payload = $this->get_binary_packet();
        }

        // only called when we've already logged in
        if (($this->bitmap & self::MASK_CONNECTED) && $this->isAuthenticated()) {
            switch (ord($payload[0])) {
                case MessageType::CHANNEL_REQUEST:
                    if (strlen($payload) == 31) {
                        [
                            'channel' => $channel,
                            'length' => $length
                        ] = unpack('cpacket_type/Nchannel/Nlength', $payload);
                        if (substr($payload, 9, $length) == 'keepalive@openssh.com' && isset($this->server_channels[$channel])) {
                            if (ord(substr($payload, 9 + $length))) { // want reply
                                $this->send_binary_packet(pack('CN', MessageType::CHANNEL_SUCCESS, $this->server_channels[$channel]));
                            }
                            $payload = $this->get_binary_packet();
                        }
                    }
                    break;
                case MessageType::GLOBAL_REQUEST: // see http://tools.ietf.org/html/rfc4254#section-4
                    Strings::shift($payload, 1);
                    [$request_name, $want_reply] = Strings::unpackSSH2('sb', $payload);
                    $this->debugMessages[] = "GLOBAL_REQUEST ($request_name) made by the server and REQUEST_FAILURE sent in response";
                    if ($want_reply) {
                        $this->send_binary_packet(pack('C', MessageType::REQUEST_FAILURE));
                    }
                    $payload = $this->get_binary_packet();
                    break;
                case MessageType::CHANNEL_OPEN: // see http://tools.ietf.org/html/rfc4254#section-5.1
                    Strings::shift($payload, 1);
                    [$data, $server_channel] = Strings::unpackSSH2('sN', $payload);
                    switch ($data) {
                        case 'auth-agent':
                        case 'auth-agent@openssh.com':
                            if (isset($this->agent)) {
                                $new_channel = self::CHANNEL_AGENT_FORWARD;

                                [
                                    $remote_window_size,
                                    $remote_maximum_packet_size
                                ] = Strings::unpackSSH2('NN', $payload);

                                $this->packet_size_client_to_server[$new_channel] = $remote_window_size;
                                $this->window_size_server_to_client[$new_channel] = $remote_maximum_packet_size;
                                $this->window_size_client_to_server[$new_channel] = $this->window_size;

                                $packet_size = 0x4000;

                                $packet = pack(
                                    'CN4',
                                    MessageType::CHANNEL_OPEN_CONFIRMATION,
                                    $server_channel,
                                    $new_channel,
                                    $packet_size,
                                    $packet_size
                                );

                                $this->server_channels[$new_channel] = $server_channel;
                                $this->channel_status[$new_channel] = MessageType::CHANNEL_OPEN_CONFIRMATION;
                                $this->send_binary_packet($packet);
                            }
                            break;
                        default:
                            $packet = Strings::packSSH2(
                                'CN2ss',
                                MessageType::CHANNEL_OPEN_FAILURE,
                                $server_channel,
                                ChannelConnectionFailureReason::ADMINISTRATIVELY_PROHIBITED,
                                '', // description
                                '' // language tag
                            );

                            $this->send_binary_packet($packet);
                    }

                    $payload = $this->get_binary_packet();
            }
        }

        return $payload;
    }

    /**
     * Enable Quiet Mode
     *
     * Suppress stderr from output
     */
    public function enableQuietMode(): void
    {
        $this->quiet_mode = true;
    }

    /**
     * Disable Quiet Mode
     *
     * Show stderr in output
     */
    public function disableQuietMode(): void
    {
        $this->quiet_mode = false;
    }

    /**
     * Returns whether Quiet Mode is enabled or not
     *
     * @see self::enableQuietMode()
     * @see self::disableQuietMode()
     */
    public function isQuietModeEnabled(): bool
    {
        return $this->quiet_mode;
    }

    /**
     * Enable request-pty when using exec()
     */
    public function enablePTY(): void
    {
        $this->request_pty = true;
    }

    /**
     * Disable request-pty when using exec()
     */
    public function disablePTY(): void
    {
        if ($this->isPTYOpen()) {
            $this->close_channel(self::CHANNEL_EXEC);
        }
        $this->request_pty = false;
    }

    /**
     * Returns whether request-pty is enabled or not
     *
     * @see self::enablePTY()
     * @see self::disablePTY()
     */
    public function isPTYEnabled(): bool
    {
        return $this->request_pty;
    }

    /**
     * Gets channel data
     *
     * Returns the data as a string. bool(true) is returned if:
     *
     * - the server closes the channel
     * - if the connection times out
     * - if a window adjust packet is received on the given negated client channel
     * - if the channel status is CHANNEL_OPEN and the response was CHANNEL_OPEN_CONFIRMATION
     * - if the channel status is CHANNEL_REQUEST and the response was CHANNEL_SUCCESS
     * - if the channel status is CHANNEL_CLOSE and the response was CHANNEL_CLOSE
     *
     * bool(false) is returned if:
     *
     * - if the channel status is CHANNEL_REQUEST and the response was CHANNEL_FAILURE
     *
     * @param int $client_channel Specifies the channel to return data for, and data received
     *        on other channels is buffered. The respective negative value of a channel is
     *        also supported for the case that the caller is awaiting adjustment of the data
     *        window, and where data received on that respective channel is also buffered.
     */
    protected function get_channel_packet(int $client_channel, bool $skip_extended = false): string|bool
    {
        if (!empty($this->channel_buffers[$client_channel])) {
            switch ($this->channel_status[$client_channel] ?? null) {
                case MessageType::CHANNEL_REQUEST:
                    foreach ($this->channel_buffers[$client_channel] as $i => $packet) {
                        switch (ord($packet[0])) {
                            case MessageType::CHANNEL_SUCCESS:
                            case MessageType::CHANNEL_FAILURE:
                                unset($this->channel_buffers[$client_channel][$i]);
                                return substr($packet, 1);
                        }
                    }
                    break;
                default:
                    return substr(array_shift($this->channel_buffers[$client_channel]), 1);
            }
        }

        if (isset($this->exit_signal_exceptions[$client_channel])) {
            $e = $this->exit_signal_exceptions[$client_channel];
            unset($this->exit_signal_exceptions[$client_channel]);
            throw $e;
        }

        while (true) {
            try {
                $response = $this->get_binary_packet();
            } catch (TimeoutException) {
                return true;
            }
            [$type] = Strings::unpackSSH2('C', $response);
            if (strlen($response) >= 4) {
                [$channel] = Strings::unpackSSH2('N', $response);
            }

            // will not be setup yet on incoming channel open request
            if (isset($channel) && isset($this->channel_status[$channel]) && isset($this->window_size_server_to_client[$channel])) {
                $this->window_size_server_to_client[$channel] -= strlen($response);

                // resize the window, if appropriate
                if ($this->window_size_server_to_client[$channel] < 0) {
                // PuTTY does something more analogous to the following:
                //if ($this->window_size_server_to_client[$channel] < 0x3FFFFFFF) {
                    $packet = pack('CNN', MessageType::CHANNEL_WINDOW_ADJUST, $this->server_channels[$channel], $this->window_resize);
                    $this->send_binary_packet($packet);
                    $this->window_size_server_to_client[$channel] += $this->window_resize;
                }

                switch ($type) {
                    case MessageType::CHANNEL_WINDOW_ADJUST:
                        [$window_size] = Strings::unpackSSH2('N', $response);
                        $this->window_size_client_to_server[$channel] += $window_size;
                        if ($channel == -$client_channel) {
                            return true;
                        }

                        continue 2;
                    case MessageType::CHANNEL_EXTENDED_DATA:
                        /*
                        if ($client_channel == self::CHANNEL_EXEC) {
                            $this->send_channel_packet($client_channel, chr(0));
                        }
                        */
                        // currently, there's only one possible value for $data_type_code: NET_SSH2_EXTENDED_DATA_STDERR
                        [$data_type_code, $data] = Strings::unpackSSH2('Ns', $response);
                        unset($data_type_code); // suppress psalm error

                        $this->stdErrorLog .= $data;
                        if ($skip_extended || $this->quiet_mode) {
                            continue 2;
                        }
                        if ($client_channel == $channel && $this->channel_status[$channel] == MessageType::CHANNEL_DATA) {
                            return $data;
                        }
                        $this->channel_buffers[$channel][] = chr($type) . $data;

                        continue 2;
                    case MessageType::CHANNEL_REQUEST:
                        if (!isset($this->channel_status[$channel])) {
                            continue 2;
                        }
                        [$value] = Strings::unpackSSH2('s', $response);
                        switch ($value) {
                            case 'exit-signal':
                                [
                                    , // FALSE
                                    $signal_name,
                                    $core_dumped,
                                    $error_message
                                ] = Strings::unpackSSH2('bsbs', $response);
                                $e = new SSHChannelExitSignalException($signal_name, $core_dumped, $error_message);
                                $oldStatus = $this->channel_status[$channel];
                                if (isset($this->channel_status[$channel]) && $oldStatus != MessageType::CHANNEL_CLOSE) {
                                    if ($oldStatus != MessageType::CHANNEL_EOF) {
                                        $this->send_binary_packet(pack('CN', MessageType::CHANNEL_EOF, $this->server_channels[$channel]));
                                    }
                                    $this->send_binary_packet(pack('CN', MessageType::CHANNEL_CLOSE, $this->server_channels[$channel]));

                                    $this->channel_status[$channel] = MessageType::CHANNEL_CLOSE;
                                }
                                if ($oldStatus != MessageType::CHANNEL_CLOSE) {
                                    if ($client_channel == $channel) {
                                        throw $e;
                                    }
                                    $this->exit_signal_exceptions[$channel] = $e;
                                }
                                continue 3;
                            case 'exit-status':
                                [, $this->exit_status] = Strings::unpackSSH2('CN', $response);

                                // "The client MAY ignore these messages."
                                // -- http://tools.ietf.org/html/rfc4254#section-6.10

                                continue 3;
                            default:
                                [$want_reply] = Strings::unpackSSH2('b', $response);
                                if ($want_reply) {
                                    // "If the request is not recognized or is not supported for the channel,
                                    //  SSH_MSG_CHANNEL_FAILURE is returned."
                                    // -- https://datatracker.ietf.org/doc/html/rfc4254#page-10
                                    $this->send_binary_packet(pack('CN', MessageType::CHANNEL_FAILURE, $this->server_channels[$channel]));
                                }
                                continue 3;
                        }
                }

                switch ($this->channel_status[$channel]) {
                    case MessageType::CHANNEL_OPEN:
                        switch ($type) {
                            case MessageType::CHANNEL_OPEN_CONFIRMATION:
                                [
                                    $this->server_channels[$channel],
                                    $window_size,
                                    $this->packet_size_client_to_server[$channel]
                                ] = Strings::unpackSSH2('NNN', $response);

                                if ($window_size < 0) {
                                    $window_size &= 0x7FFFFFFF;
                                    $window_size += 0x80000000;
                                }
                                $this->window_size_client_to_server[$channel] = $window_size;
                                $result = $client_channel == $channel ? true : $this->get_channel_packet($client_channel, $skip_extended);
                                $this->on_channel_open();
                                return $result;
                            case MessageType::CHANNEL_OPEN_FAILURE:
                                $this->disconnect_helper(DisconnectReason::BY_APPLICATION);
                                throw new ConnectionClosedException('Unable to open channel');
                            default:
                                if ($client_channel == $channel) {
                                    $this->disconnect_helper(DisconnectReason::BY_APPLICATION);
                                    throw new ConnectionClosedException('Unexpected response to open request');
                                }
                                return $this->get_channel_packet($client_channel, $skip_extended);
                        }
                        /** @psalm-suppress UnevaluatedCode */
                        break;
                    case MessageType::CHANNEL_REQUEST:
                        switch ($type) {
                            case MessageType::CHANNEL_SUCCESS:
                                return true;
                            case MessageType::CHANNEL_FAILURE:
                                return false;
                            case MessageType::CHANNEL_DATA:
                                [$data] = Strings::unpackSSH2('s', $response);
                                $this->channel_buffers[$channel][] = chr($type) . $data;
                                return $this->get_channel_packet($client_channel, $skip_extended);
                            default:
                                $this->disconnect_helper(DisconnectReason::BY_APPLICATION);
                                throw new ConnectionClosedException('Unable to fulfill channel request');
                        }
                        // no break
                    case MessageType::CHANNEL_CLOSE:
                        if ($client_channel == $channel && $type == MessageType::CHANNEL_CLOSE) {
                            return true;
                        }
                        return $this->get_channel_packet($client_channel, $skip_extended);
                }
            }

            // ie. $this->channel_status[$channel] == MessageType::CHANNEL_DATA

            switch ($type) {
                case MessageType::CHANNEL_DATA:
                    /*
                    if ($channel == self::CHANNEL_EXEC) {
                        // SCP requires null packets, such as this, be sent.  further, in the case of the ssh.com SSH server
                        // this actually seems to make things twice as fast.  more to the point, the message right after
                        // SSH_MSG_CHANNEL_DATA (usually SSH_MSG_IGNORE) won't block for as long as it would have otherwise.
                        // in OpenSSH it slows things down but only by a couple thousandths of a second.
                        $this->send_channel_packet($channel, chr(0));
                    }
                    */
                    [$data] = Strings::unpackSSH2('s', $response);

                    if ($channel == self::CHANNEL_AGENT_FORWARD) {
                        $agent_response = $this->agent->forwardData($data);
                        if (isset($agent_response)) {
                            $this->send_channel_packet($channel, $agent_response);
                        }
                        break;
                    }

                    if ($client_channel == $channel) {
                        return $data;
                    }
                    $this->channel_buffers[$channel][] = chr($type) . $data;
                    break;
                case MessageType::CHANNEL_CLOSE:
                    $this->curTimeout = 5;

                    $this->close_channel_bitmap($channel);

                    if ($this->channel_status[$channel] != MessageType::CHANNEL_CLOSE) {
                        $this->send_binary_packet(pack('CN', MessageType::CHANNEL_CLOSE, $this->server_channels[$channel]));
                    }

                    unset($this->channel_status[$channel]);
                    $this->channelCount--;

                    if ($client_channel == $channel) {
                        return true;
                    }
                    // no break
                case MessageType::CHANNEL_EOF:
                    break;
                default:
                    $this->disconnect_helper(DisconnectReason::BY_APPLICATION);
                    try {
                        $type = 'SSH_MSG_' . MessageType::getConstantNameByValue($type);
                    } catch (InvalidArgumentException) {
                    }
                    throw new UnexpectedSSHMessageException("Error reading channel data ($type)");
            }
        }
    }

    /**
     * Sends Binary Packets
     *
     * See '6. Binary Packet Protocol' of rfc4253 for more info.
     *
     * @see self::_get_binary_packet()
     */
    protected function send_binary_packet(
        #[\SensitiveParameter] string $data,
        ?string $logged = null
    ): void {
        if (!is_resource($this->fsock) || feof($this->fsock)) {
            $this->disconnect_helper(DisconnectReason::CONNECTION_LOST);
            throw new ConnectionClosedException('Connection closed prematurely');
        }

        if (!isset($logged)) {
            $logged = $data;
        }

        switch ($this->compress) {
            case self::NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH:
                if (!$this->isAuthenticated()) {
                    break;
                }
                // no break
            case self::NET_SSH2_COMPRESSION_ZLIB:
                if (!$this->regenerate_compression_context) {
                    $header = '';
                } else {
                    $this->regenerate_compression_context = false;
                    $this->compress_context = deflate_init(ZLIB_ENCODING_RAW, ['window' => 15]);
                    $header = "\x78\x9C";
                }
                if ($this->compress_context) {
                    $data = $header . deflate_add($this->compress_context, $data, ZLIB_PARTIAL_FLUSH);
                }
        }

        // 4 (packet length) + 1 (padding length) + 4 (minimal padding amount) == 9
        $packet_length = strlen($data) + 9;
        if ($this->encrypt && $this->encrypt->usesNonce()) {
            $packet_length -= 4;
        }
        // round up to the nearest $this->encrypt_block_size
        $packet_length += (($this->encrypt_block_size - 1) * $packet_length) % $this->encrypt_block_size;
        // subtracting strlen($data) is obvious - subtracting 5 is necessary because of packet_length and padding_length
        $padding_length = $packet_length - strlen($data) - 5;
        switch (true) {
            case $this->encrypt && $this->encrypt->usesNonce():
            case $this->hmac_create instanceof Hash && $this->hmac_create_etm:
                $padding_length += 4;
                $packet_length += 4;
        }

        // SSH binary packet padding is always 4-255 bytes; the block-rounding above
        // guarantees $padding_length >= 4. Psalm can't track that through the modular
        // arithmetic, so it sees int<min,max>.
        /** @psalm-suppress InvalidArgument */
        $padding = random_bytes($padding_length);

        // we subtract 4 from packet_length because the packet_length field isn't supposed to include itself
        $packet = pack('NCa*', $packet_length - 4, $padding_length, $data . $padding);

        $hmac = '';
        if ($this->hmac_create instanceof Hash && !$this->hmac_create_etm) {
            if (($this->hmac_create->getHash() & "\xFF\xFF\xFF\xFF") == 'umac') {
                $this->hmac_create->setNonce("\0\0\0\0" . pack('N', $this->send_seq_no));
                $hmac = $this->hmac_create->hash($packet);
            } else {
                $hmac = $this->hmac_create->hash(pack('Na*', $this->send_seq_no, $packet));
            }
        }

        if ($this->encrypt) {
            switch ($this->encryptName) {
                case 'aes128-gcm@openssh.com':
                case 'aes256-gcm@openssh.com':
                    $this->encrypt->setNonce(
                        $this->encryptFixedPart .
                        $this->encryptInvocationCounter
                    );
                    Strings::increment_str($this->encryptInvocationCounter);
                    $this->encrypt->setAAD($temp = ($packet & "\xFF\xFF\xFF\xFF"));
                    $packet = $temp . $this->encrypt->encrypt(substr($packet, 4));
                    break;
                case 'chacha20-poly1305@openssh.com':
                    // This should be impossible, but we are checking anyway to narrow the type for Psalm.
                    if (!($this->encrypt instanceof ChaCha20)) {
                        throw new InvalidStateException('$this->encrypt is not an instance of ' . ChaCha20::class);
                    }

                    $nonce = pack('N2', 0, $this->send_seq_no);

                    $this->encrypt->setNonce($nonce);
                    $this->lengthEncrypt->setNonce($nonce);

                    $length = $this->lengthEncrypt->encrypt($packet & "\xFF\xFF\xFF\xFF");

                    $this->encrypt->setCounter(0);
                    // this is the same approach that's implemented in Salsa20::createPoly1305Key()
                    // but we don't want to use the same AEAD construction that RFC8439 describes
                    // for ChaCha20-Poly1305 so we won't rely on it (see Salsa20::poly1305())
                    $this->encrypt->setPoly1305Key(
                        $this->encrypt->encrypt(str_repeat("\0", 32))
                    );
                    $this->encrypt->setAAD($length);
                    $this->encrypt->setCounter(1);
                    $packet = $length . $this->encrypt->encrypt(substr($packet, 4));
                    break;
                default:
                    $packet = $this->hmac_create instanceof Hash && $this->hmac_create_etm ?
                        ($packet & "\xFF\xFF\xFF\xFF") . $this->encrypt->encrypt(substr($packet, 4)) :
                        $this->encrypt->encrypt($packet);
            }
        }

        if ($this->hmac_create instanceof Hash && $this->hmac_create_etm) {
            if (($this->hmac_create->getHash() & "\xFF\xFF\xFF\xFF") == 'umac') {
                $this->hmac_create->setNonce("\0\0\0\0" . pack('N', $this->send_seq_no));
                $hmac = $this->hmac_create->hash($packet);
            } else {
                $hmac = $this->hmac_create->hash(pack('Na*', $this->send_seq_no, $packet));
            }
        }

        $this->send_seq_no++;

        $packet .= $this->encrypt && $this->encrypt->usesNonce() ? $this->encrypt->getTag() : $hmac;

        if (!$this->keyExchangeInProgress) {
            $this->bytesTransferredSinceLastKEX += strlen($packet);
        }

        $start = microtime(true);
        $sent = @fwrite($this->fsock, $packet);
        $stop = microtime(true);

        if (defined('NET_SSH2_LOGGING')) {
            $current = microtime(true);
            $message_number = sprintf(
                '-> %s (since last: %s, network: %ss)',
                ($constantName = MessageType::findConstantNameByValue($value = ord($logged[0]), false))
                    ? "SSH_MSG_$constantName"
                    : "UNKNOWN ($value)",
                round($current - $this->last_packet, 4),
                round($stop - $start, 4)
            );
            $this->append_log($message_number, $logged);
        }
        $this->last_packet = microtime(true);

        if (strlen($packet) != $sent) {
            $this->disconnect_helper(DisconnectReason::BY_APPLICATION);
            $message = $sent === false ?
                'Unable to write ' . strlen($packet) . ' bytes' :
                "Only $sent of " . strlen($packet) . " bytes were sent";
            throw new ConnectionClosedException($message);
        }

        if ($this->bytesTransferredSinceLastKEX > $this->doKeyReexchangeAfterXBytes) {
            $this->key_exchange();
        }
    }

    /**
     * Sends a keep-alive message, if keep-alive is enabled and interval is met
     */
    private function send_keep_alive(): void
    {
        if ($this->bitmap & self::MASK_CONNECTED) {
            $elapsed = microtime(true) - $this->last_packet;
            if ($this->keepAlive > 0 && $elapsed >= $this->keepAlive) {
                $this->send_binary_packet(pack('CN', MessageType::IGNORE, 0));
            }
        }
    }

    /**
     * Logs data packets
     *
     * Makes sure that only the last 1MB worth of packets will be logged
     */
    private function append_log(string $message_number, string $message): void
    {
        if (!defined('NET_SSH2_LOGGING')) {
            throw new InvalidModeException('Logging is not enabled');
        }
        $this->append_log_helper(
            NET_SSH2_LOGGING,
            $message_number,
            $message,
            $this->message_number_log,
            $this->message_log,
            $this->log_size,
            $this->realtime_log_file,
            $this->realtime_log_wrap,
            $this->realtime_log_size
        );
    }

    /**
     * Logs data packet helper
     *
     * @param resource &$realtime_log_file
     */
    protected function append_log_helper(int $constant, string $message_number, string $message, array &$message_number_log, array &$message_log, ?int &$log_size, mixed &$realtime_log_file, ?bool &$realtime_log_wrap, ?int &$realtime_log_size): void
    {
        // remove the byte identifying the message type from all but the first two messages (ie. the identification strings)
        if (!in_array(substr($message_number, 0, 4), ['<- (', '-> (']) && strlen($message_number) > 2 && substr($message_number, 3, 8) != 'SSH_FXP_') {
            Strings::shift($message);
        }

        switch ($constant) {
            // useful for benchmarks
            case self::LOG_SIMPLE:
                $message_number_log[] = $message_number;
                break;
            case self::LOG_SIMPLE_REALTIME:
            //case self::LOG_REALTIME_SIMPLE:
                echo $message_number;
                echo PHP_SAPI == 'cli' ? "\r\n" : '<br>';
                @flush();
                @ob_flush();
                break;
            // the most useful log for SSH2
            case self::LOG_COMPLEX:
                $message_number_log[] = $message_number;
                $log_size += strlen($message);
                $message_log[] = $message;
                while ($log_size > self::LOG_MAX_SIZE) {
                    $log_size -= strlen(array_shift($message_log));
                    array_shift($message_number_log);
                }
                break;
            // dump the output out realtime; packets may be interspersed with non packets,
            // passwords won't be filtered out and select other packets may not be correctly
            // identified
            case self::LOG_REALTIME:
                switch (PHP_SAPI) {
                    case 'cli':
                        $start = $stop = "\r\n";
                        break;
                    default:
                        $start = '<pre>';
                        $stop = '</pre>';
                }
                echo $start . $this->format_log([$message], [$message_number]) . $stop;
                @flush();
                @ob_flush();
                break;
            // basically the same thing as self::LOG_REALTIME with the caveat that NET_SSH2_LOG_REALTIME_FILENAME
            // needs to be defined and that the resultant log file will be capped out at self::LOG_MAX_SIZE.
            // the earliest part of the log file is denoted by the first <<< START >>> and is not going to necessarily
            // at the beginning of the file
            case self::LOG_REALTIME_FILE:
                if (!isset($realtime_log_file)) {
                    if (!defined('NET_SSH2_LOG_REALTIME_FILENAME')) {
                        throw new InvalidModeException('NET_SSH2_LOG_REALTIME_FILENAME must be defined');
                    }
                    // PHP doesn't seem to like using constants in fopen()
                    $filename = NET_SSH2_LOG_REALTIME_FILENAME;
                    $fp = fopen($filename, 'w');
                    $realtime_log_file = $fp;
                }
                if (!is_resource($realtime_log_file)) {
                    break;
                }
                $entry = $this->format_log([$message], [$message_number]);
                if ($realtime_log_wrap) {
                    $temp = "<<< START >>>\r\n";
                    $entry .= $temp;
                    fseek($realtime_log_file, ftell($realtime_log_file) - strlen($temp));
                }
                $realtime_log_size += strlen($entry);
                if ($realtime_log_size > self::LOG_MAX_SIZE) {
                    fseek($realtime_log_file, 0);
                    $realtime_log_size = strlen($entry);
                    $realtime_log_wrap = true;
                }
                fwrite($realtime_log_file, $entry);
        }
    }

    /**
     * Sends channel data
     *
     * Spans multiple SSH_MSG_CHANNEL_DATAs if appropriate
     */
    protected function send_channel_packet(
        int $client_channel,
        #[\SensitiveParameter] string $data
    ): void {
        if (
            isset($this->channel_buffers_write[$client_channel])
            && str_starts_with($data, $this->channel_buffers_write[$client_channel])
        ) {
            // if buffer holds identical initial data content, resume send from the unmatched data portion
            $data = substr($data, strlen($this->channel_buffers_write[$client_channel]));
        } else {
            $this->channel_buffers_write[$client_channel] = '';
        }
        while (strlen($data)) {
            if (!$this->window_size_client_to_server[$client_channel]) {
                // using an invalid channel will let the buffers be built up for the valid channels
                $this->get_channel_packet(-$client_channel);
                if ($this->isTimeout()) {
                    throw new TimeoutException('Timed out waiting for server');
                } elseif (!$this->window_size_client_to_server[$client_channel]) {
                    throw new UnexpectedValueException('Data window was not adjusted');
                }
            }

            /* The maximum amount of data allowed is determined by the maximum
               packet size for the channel, and the current window size, whichever
               is smaller.
                 -- http://tools.ietf.org/html/rfc4254#section-5.2 */
            $max_size = min(
                $this->packet_size_client_to_server[$client_channel],
                $this->window_size_client_to_server[$client_channel]
            );

            $temp = Strings::shift($data, $max_size);
            $packet = Strings::packSSH2(
                'CNs',
                MessageType::CHANNEL_DATA,
                $this->server_channels[$client_channel],
                $temp
            );
            $this->window_size_client_to_server[$client_channel] -= strlen($temp);
            $this->send_binary_packet($packet);
            $this->channel_buffers_write[$client_channel] .= $temp;
        }
        unset($this->channel_buffers_write[$client_channel]);
    }

    /**
     * Closes and flushes a channel
     *
     * \phpseclib4\Net\SSH2 doesn't properly close most channels.  For exec() channels are normally closed by the server
     * and for SFTP channels are presumably closed when the client disconnects.  This functions is intended
     * for SCP more than anything.
     */
    protected function close_channel(int $client_channel): void
    {
        // see http://tools.ietf.org/html/rfc4254#section-5.3

        if ($this->channel_status[$client_channel] != MessageType::CHANNEL_EOF) {
            $this->send_binary_packet(pack('CN', MessageType::CHANNEL_EOF, $this->server_channels[$client_channel]));
        }
        $this->send_binary_packet(pack('CN', MessageType::CHANNEL_CLOSE, $this->server_channels[$client_channel]));

        $this->channel_status[$client_channel] = MessageType::CHANNEL_CLOSE;

        $this->channelCount--;

        $this->curTimeout = 5;
        while (!is_bool($this->get_channel_packet($client_channel))) {
        }

        unset($this->channel_status[$client_channel]);

        $this->close_channel_bitmap($client_channel);
    }

    /**
     * Maintains execution state bitmap in response to channel closure
     */
    private function close_channel_bitmap(int $client_channel): void
    {
        switch ($client_channel) {
            case self::CHANNEL_SHELL:
                // Shell status has been maintained in the bitmap for backwards
                //  compatibility sake, but can be removed going forward
                if ($this->bitmap & self::MASK_SHELL) {
                    $this->bitmap &= ~self::MASK_SHELL;
                }
                break;
        }
    }

    /**
     * Disconnect
     *
     * @return false
     */
    protected function disconnect_helper(int $reason): bool
    {
        if ($this->bitmap & self::MASK_DISCONNECT) {
            // Disregard subsequent disconnect requests
            return false;
        }
        $this->bitmap |= self::MASK_DISCONNECT;
        if ($this->isConnected()) {
            $data = Strings::packSSH2('CNss', MessageType::DISCONNECT, $reason, '', '');
            try {
                $this->send_binary_packet($data);
            } catch (\Exception) {
            }
        }

        $this->reset_connection();

        return false;
    }

    /**
     * Returns a log of the packets that have been sent and received.
     *
     * Returns a string if NET_SSH2_LOGGING == self::LOG_COMPLEX, an array if NET_SSH2_LOGGING == self::LOG_SIMPLE and false if !defined('NET_SSH2_LOGGING')
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function getLog(): array|string|null
    {
        if (!defined('NET_SSH2_LOGGING')) {
            return null;
        }

        switch (NET_SSH2_LOGGING) {
            case self::LOG_SIMPLE:
                return $this->message_number_log;
            case self::LOG_COMPLEX:
                $log = $this->format_log($this->message_log, $this->message_number_log);
                return PHP_SAPI == 'cli' ? $log : '<pre>' . $log . '</pre>';
            default:
                return null;
        }
    }

    /**
     * Formats a log for printing
     */
    protected function format_log(array $message_log, array $message_number_log): string
    {
        $output = '';
        for ($i = 0; $i < count($message_log); $i++) {
            $output .= $message_number_log[$i];
            $current_log = $message_log[$i];
            $j = 0;
            if (strlen($current_log)) {
                $output .= "\r\n";
            }
            do {
                if (strlen($current_log)) {
                    $output .= str_pad(dechex($j), 7, '0', STR_PAD_LEFT) . '0  ';
                }
                $fragment = Strings::shift($current_log, $this->log_short_width);
                $hex = substr(preg_replace_callback('#.#s', fn ($matches) => $this->log_boundary . str_pad(dechex(ord($matches[0])), 2, '0', STR_PAD_LEFT), $fragment), strlen($this->log_boundary));
                // replace non ASCII printable characters with dots
                // http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters
                // also replace < with a . since < messes up the output on web browsers
                $raw = preg_replace('#[^\x20-\x7E]|<#', '.', $fragment);
                $output .= str_pad($hex, $this->log_long_width - $this->log_short_width, ' ') . $raw . "\r\n";
                $j++;
            } while (strlen($current_log));
            $output .= "\r\n";
        }

        return $output;
    }

    /**
     * Get Debug Messages
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function getDebugMessages(): array
    {
        return $this->debugMessages;
    }

    /**
     * Helper function for agent->on_channel_open()
     *
     * Used when channels are created to inform agent
     * of said channel opening. Must be called after
     * channel open confirmation received
     */
    private function on_channel_open(): void
    {
        if (isset($this->agent)) {
            $this->agent->registerChannelOpen($this);
        }
    }

    private static function array_intersect_first(array $array1, array $array2): ?string
    {
        foreach ($array1 as $value) {
            if (in_array($value, $array2)) {
                return $value;
            }
        }
        return null;
    }

    /**
     * Return the server identification.
     */
    public function getServerIdentification(): string
    {
        $this->connect();

        return $this->server_identifier;
    }

    /**
     * Return the raw server identification.
     *
     * Quoting the SSH2 specs, "the server MAY send other lines of data before sending the version
     * string." The implication is that lines preceeeding the version string are *not* part of the
     * version string. getServerIdentification() returns the server string. This function returns
     * the server string and the lines preceeding it.
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function getRawServerIdentification(): string
    {
        $this->connect();

        return $this->raw_server_identifier;
    }

    /**
     * Returns a list of algorithms the server supports
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function getServerAlgorithms(): array
    {
        $this->connect();

        return [
            'kex' => $this->kex_algorithms,
            'hostkey' => $this->server_host_key_algorithms,
            'client_to_server' => [
                'crypt' => $this->encryption_algorithms_client_to_server,
                'mac' => $this->mac_algorithms_client_to_server,
                'comp' => $this->compression_algorithms_client_to_server,
                'lang' => $this->languages_client_to_server,
            ],
            'server_to_client' => [
                'crypt' => $this->encryption_algorithms_server_to_client,
                'mac' => $this->mac_algorithms_server_to_client,
                'comp' => $this->compression_algorithms_server_to_client,
                'lang' => $this->languages_server_to_client,
            ],
        ];
    }

    /**
     * Returns a list of KEX algorithms that phpseclib supports
     */
    public static function getSupportedKEXAlgorithms(): array
    {
        $kex_algorithms = [
            // Elliptic Curve Diffie-Hellman Key Agreement (ECDH) using
            // Curve25519. See doc/curve25519-sha256@libssh.org.txt in the
            // libssh repository for more information.
            'curve25519-sha256',
            'curve25519-sha256@libssh.org',

            'ecdh-sha2-nistp256', // RFC 5656
            'ecdh-sha2-nistp384', // RFC 5656
            'ecdh-sha2-nistp521', // RFC 5656

            'diffie-hellman-group-exchange-sha256',// RFC 4419
            'diffie-hellman-group-exchange-sha1',  // RFC 4419

            // Diffie-Hellman Key Agreement (DH) using integer modulo prime
            // groups.
            'diffie-hellman-group14-sha256',
            'diffie-hellman-group14-sha1', // REQUIRED
            'diffie-hellman-group15-sha512',
            'diffie-hellman-group16-sha512',
            'diffie-hellman-group17-sha512',
            'diffie-hellman-group18-sha512',

            'diffie-hellman-group1-sha1', // REQUIRED
        ];

        return $kex_algorithms;
    }

    /**
     * Returns a list of host key algorithms that phpseclib supports
     */
    public static function getSupportedHostKeyAlgorithms(): array
    {
        return [
            'ssh-ed25519', // https://tools.ietf.org/html/draft-ietf-curdle-ssh-ed25519-02
            'ecdsa-sha2-nistp256', // RFC 5656
            'ecdsa-sha2-nistp384', // RFC 5656
            'ecdsa-sha2-nistp521', // RFC 5656
            'rsa-sha2-256', // RFC 8332
            'rsa-sha2-512', // RFC 8332
            'ssh-rsa', // RECOMMENDED  sign   Raw RSA Key
            'ssh-dss',  // REQUIRED     sign   Raw DSS Key
        ];
    }

    /**
     * Returns a list of symmetric key algorithms that phpseclib supports
     */
    public static function getSupportedEncryptionAlgorithms(): array
    {
        $algos = [
            // from <https://tools.ietf.org/html/rfc5647>:
            'aes128-gcm@openssh.com',
            'aes256-gcm@openssh.com',

            // from <http://tools.ietf.org/html/rfc4345#section-4>:
            'arcfour256',
            'arcfour128',

            //'arcfour',      // OPTIONAL          the ARCFOUR stream cipher with a 128-bit key

            // CTR modes from <http://tools.ietf.org/html/rfc4344#section-4>:
            'aes128-ctr',     // RECOMMENDED       AES (Rijndael) in SDCTR mode, with 128-bit key
            'aes192-ctr',     // RECOMMENDED       AES with 192-bit key
            'aes256-ctr',     // RECOMMENDED       AES with 256-bit key

            // from <https://github.com/openssh/openssh-portable/blob/001aa55/PROTOCOL.chacha20poly1305>:
            // one of the big benefits of chacha20-poly1305 is speed. the problem is...
            // libsodium doesn't generate the poly1305 keys in the way ssh does and openssl's PHP bindings don't even
            // seem to support poly1305 currently. so even if libsodium or openssl are being used for the chacha20
            // part, pure-PHP has to be used for the poly1305 part and that's gonna cause a big slow down.
            // speed-wise it winds up being faster to use AES (when openssl is available) and some HMAC
            // (which is always gonna be super fast to compute thanks to the hash extension, which
            // "is bundled and compiled into PHP by default")
            'chacha20-poly1305@openssh.com',

            'twofish128-ctr', // OPTIONAL          Twofish in SDCTR mode, with 128-bit key
            'twofish192-ctr', // OPTIONAL          Twofish with 192-bit key
            'twofish256-ctr', // OPTIONAL          Twofish with 256-bit key

            'aes128-cbc',     // RECOMMENDED       AES with a 128-bit key
            'aes192-cbc',     // OPTIONAL          AES with a 192-bit key
            'aes256-cbc',     // OPTIONAL          AES in CBC mode, with a 256-bit key

            'twofish128-cbc', // OPTIONAL          Twofish with a 128-bit key
            'twofish192-cbc', // OPTIONAL          Twofish with a 192-bit key
            'twofish256-cbc',
            'twofish-cbc',    // OPTIONAL          alias for "twofish256-cbc"
                              //                   (this is being retained for historical reasons)

            'blowfish-ctr',   // OPTIONAL          Blowfish in SDCTR mode

            'blowfish-cbc',   // OPTIONAL          Blowfish in CBC mode

            '3des-ctr',       // RECOMMENDED       Three-key 3DES in SDCTR mode

            '3des-cbc',       // REQUIRED          three-key 3DES in CBC mode

             //'none'           // OPTIONAL          no encryption; NOT RECOMMENDED
        ];

        if (self::$crypto_engine) {
            $engines = [self::$crypto_engine];
        } else {
            $engines = [
                'libsodium',
                'OpenSSL (AEAD)',
                'OpenSSL',
                'Eval',
                'PHP',
            ];
        }

        $ciphers = [];

        foreach ($engines as $engine) {
            foreach ($algos as $algo) {
                $obj = self::encryption_algorithm_to_crypt_instance($algo);
                if ($obj instanceof Rijndael) {
                    $obj->setKeyLength((int) preg_replace('#[^\d]#', '', $algo));
                }
                switch ($algo) {
                    // Eval engines do not exist for ChaCha20 or RC4 because they would not benefit from one.
                    // to benefit from an Eval engine they'd need to loop a variable amount of times, they'd
                    // need to do table lookups (eg. sbox subsitutions). ChaCha20 doesn't do either because
                    // it's a so-called ARX cipher, meaning that the only operations it does are add (A), rotate (R)
                    // and XOR (X). RC4 does do table lookups but being a stream cipher it works differently than
                    // block ciphers. with RC4 you XOR the plaintext against a keystream and the keystream changes
                    // as you encrypt stuff. the only table lookups are made against this keystream and thus table
                    // lookups are kinda unavoidable. with AES and DES, however, the table lookups that are done
                    // are done against substitution boxes (sboxes), which are invariant.

                    // OpenSSL can't be used as an engine, either, because OpenSSL doesn't support continuous buffers
                    // as SSH2 uses and altho you can emulate a continuous buffer with block ciphers you can't do so
                    // with stream ciphers. As for ChaCha20...  for the ChaCha20 part OpenSSL could prob be used but
                    // the big slow down isn't with ChaCha20 - it's with Poly1305. SSH constructs the key for that
                    // differently than how OpenSSL does it (OpenSSL does it as the RFC describes, SSH doesn't).

                    // libsodium can't be used because it doesn't support RC4 and it doesn't construct the Poly1305
                    // keys in the same way that SSH does

                    // mcrypt could prob be used for RC4 but mcrypt hasn't been included in PHP core for yearss
                    case 'chacha20-poly1305@openssh.com':
                    case 'arcfour128':
                    case 'arcfour256':
                        if ($engine != 'PHP') {
                            continue 2;
                        }
                        break;
                    case 'aes128-gcm@openssh.com':
                    case 'aes256-gcm@openssh.com':
                        if ($engine == 'OpenSSL') {
                            continue 2;
                        }
                        $obj->setNonce('dummydummydu');
                }
                if ($obj->isValidEngine($engine)) {
                    $algos = array_diff($algos, [$algo]);
                    $ciphers[] = $algo;
                }
            }
        }

        return $ciphers;
    }

    /**
     * Returns a list of MAC algorithms that phpseclib supports
     */
    public static function getSupportedMACAlgorithms(): array
    {
        return [
            'hmac-sha2-256-etm@openssh.com',
            'hmac-sha2-512-etm@openssh.com',
            'hmac-sha1-etm@openssh.com',

            // from <http://www.ietf.org/rfc/rfc6668.txt>:
            'hmac-sha2-256',// RECOMMENDED     HMAC-SHA256 (digest length = key length = 32)
            'hmac-sha2-512',// OPTIONAL        HMAC-SHA512 (digest length = key length = 64)

            'hmac-sha1-96', // RECOMMENDED     first 96 bits of HMAC-SHA1 (digest length = 12, key length = 20)
            'hmac-sha1',    // REQUIRED        HMAC-SHA1 (digest length = key length = 20)
            'hmac-md5-96',  // OPTIONAL        first 96 bits of HMAC-MD5 (digest length = 12, key length = 16)
            'hmac-md5',     // OPTIONAL        HMAC-MD5 (digest length = key length = 16)

            'umac-64-etm@openssh.com',
            'umac-128-etm@openssh.com',

            // from <https://tools.ietf.org/html/draft-miller-secsh-umac-01>:
            'umac-64@openssh.com',
            'umac-128@openssh.com',

            //'none'          // OPTIONAL        no MAC; NOT RECOMMENDED
        ];
    }

    /**
     * Returns a list of compression algorithms that phpseclib supports
     */
    public static function getSupportedCompressionAlgorithms(): array
    {
        $algos = ['none']; // REQUIRED        no compression
        if (function_exists('deflate_init')) {
            $algos[] = 'zlib@openssh.com'; // https://datatracker.ietf.org/doc/html/draft-miller-secsh-compression-delayed
            $algos[] = 'zlib';
        }
        return $algos;
    }

    /**
     * Return list of negotiated algorithms
     *
     * Uses the same format as https://www.php.net/ssh2-methods-negotiated
     */
    public function getAlgorithmsNegotiated(): array
    {
        $this->connect();

        $compression_map = [
            self::NET_SSH2_COMPRESSION_NONE => 'none',
            self::NET_SSH2_COMPRESSION_ZLIB => 'zlib',
            self::NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH => 'zlib@openssh.com',
        ];

        return [
            'kex' => $this->kex_algorithm,
            'hostkey' => $this->signature_format,
            'client_to_server' => [
                'crypt' => $this->encryptName,
                'mac' => $this->hmac_create_name,
                'comp' => $compression_map[$this->compress],
            ],
            'server_to_client' => [
                'crypt' => $this->decryptName,
                'mac' => $this->hmac_check_name,
                'comp' => $compression_map[$this->decompress],
            ],
        ];
    }

    /**
     * Force multiple channels (even if phpseclib has decided to disable them)
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function forceMultipleChannels(): void
    {
        $this->errorOnMultipleChannels = false;
    }

    /**
     * Allows you to set the terminal
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function setTerminal(string $term): void
    {
        $this->term = $term;
    }

    /**
     * Accepts an associative array with up to four parameters as described at
     * <https://www.php.net/manual/en/function.ssh2-connect.php>
     */
    public function setPreferredAlgorithms(array $methods): void
    {
        $keys = ['client_to_server', 'server_to_client'];

        if (isset($methods['kex']) && is_string($methods['kex'])) {
            $methods['kex'] = explode(',', $methods['kex']);
        }

        if (isset($methods['hostkey']) && is_string($methods['hostkey'])) {
            $methods['hostkey'] = explode(',', $methods['hostkey']);
        }

        foreach ($keys as $key) {
            if (isset($methods[$key])) {
                $a = &$methods[$key];
                if (isset($a['crypt']) && is_string($a['crypt'])) {
                    $a['crypt'] = explode(',', $a['crypt']);
                }
                if (isset($a['comp']) && is_string($a['comp'])) {
                    $a['comp'] = explode(',', $a['comp']);
                }
                if (isset($a['mac']) && is_string($a['mac'])) {
                    $a['mac'] = explode(',', $a['mac']);
                }
            }
        }

        $preferred = $methods;

        if (isset($preferred['kex'])) {
            $preferred['kex'] = array_intersect(
                $preferred['kex'],
                static::getSupportedKEXAlgorithms()
            );
        }

        if (isset($preferred['hostkey'])) {
            $preferred['hostkey'] = array_intersect(
                $preferred['hostkey'],
                static::getSupportedHostKeyAlgorithms()
            );
        }

        foreach ($keys as $key) {
            if (isset($preferred[$key])) {
                $a = &$preferred[$key];
                if (isset($a['crypt'])) {
                    $a['crypt'] = array_intersect(
                        $a['crypt'],
                        static::getSupportedEncryptionAlgorithms()
                    );
                }
                if (isset($a['comp'])) {
                    $a['comp'] = array_intersect(
                        $a['comp'],
                        static::getSupportedCompressionAlgorithms()
                    );
                }
                if (isset($a['mac'])) {
                    $a['mac'] = array_intersect(
                        $a['mac'],
                        static::getSupportedMACAlgorithms()
                    );
                }
            }
        }

        $keys = [
            'kex',
            'hostkey',
            'client_to_server/crypt',
            'client_to_server/comp',
            'client_to_server/mac',
            'server_to_client/crypt',
            'server_to_client/comp',
            'server_to_client/mac',
        ];
        foreach ($keys as $key) {
            $p = $preferred;
            $m = $methods;

            $subkeys = explode('/', $key);
            foreach ($subkeys as $subkey) {
                if (!isset($p[$subkey])) {
                    continue 2;
                }
                $p = $p[$subkey];
                $m = $m[$subkey];
            }

            if (count($p) != count($m)) {
                $diff = array_diff($m, $p);
                $msg = count($diff) == 1 ?
                    ' is not a supported algorithm' :
                    ' are not supported algorithms';
                throw new UnsupportedAlgorithmException(implode(', ', $diff) . $msg);
            }
        }

        $this->preferred = $preferred;
    }

    /**
     * Returns the banner message.
     *
     * Quoting from the RFC, "in some jurisdictions, sending a warning message before
     * authentication may be relevant for getting legal protection."
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function getBannerMessage(): string
    {
        return $this->banner_message;
    }

    /**
     * Returns the server public host key.
     *
     * Caching this the first time you connect to a server and checking the result on subsequent connections
     * is recommended.  Returns false if the server signature is not signed correctly with the public host key.
     */
    public function getServerPublicHostKey(): ?string
    {
        if (!($this->bitmap & self::MASK_CONSTRUCTOR)) {
            $this->connect();
        }

        $signature = $this->signature;
        $server_public_host_key = base64_encode($this->server_public_host_key);

        if ($this->signature_validated) {
            return $this->bitmap ?
                $this->signature_format . ' ' . $server_public_host_key :
                null;
        }

        $this->signature_validated = true;

        switch ($this->signature_format) {
            case 'ssh-ed25519':
            case 'ecdsa-sha2-nistp256':
            case 'ecdsa-sha2-nistp384':
            case 'ecdsa-sha2-nistp521':
                $key = EC::loadFormat('OpenSSH', $server_public_host_key)
                    ->withSignatureFormat('SSH2');
                $hash = match ($this->signature_format) {
                    'ssh-ed25519' => 'sha512',
                    'ecdsa-sha2-nistp256' => 'sha256',
                    'ecdsa-sha2-nistp384' => 'sha384',
                    'ecdsa-sha2-nistp521' => 'sha512'
                };
                $key = $key->withHash($hash);
                break;
            case 'ssh-dss':
                $key = DSA::loadFormat('OpenSSH', $server_public_host_key)
                    ->withSignatureFormat('SSH2')
                    ->withHash('sha1');
                break;
            case 'ssh-rsa':
            case 'rsa-sha2-256':
            case 'rsa-sha2-512':
                // could be ssh-rsa, rsa-sha2-256, rsa-sha2-512
                // we don't check here because we already checked in key_exchange
                // some signatures have the type embedded within the message and some don't
                [, $signature] = Strings::unpackSSH2('ss', $signature);

                $key = RSA::loadFormat('OpenSSH', $server_public_host_key)
                    ->withPadding(RSA::SIGNATURE_PKCS1);
                $hash = match ($this->signature_format) {
                    'rsa-sha2-512' => 'sha512',
                    'rsa-sha2-256' => 'sha256',
                    default => 'sha1'
                };
                $key = $key->withHash($hash);
                break;
            default:
                $this->disconnect_helper(DisconnectReason::HOST_KEY_NOT_VERIFIABLE);
                throw new NoSupportedAlgorithmsException('Unsupported signature format');
        }

        if (!$key->verify($this->exchange_hash, $signature)) {
            $this->disconnect_helper(DisconnectReason::HOST_KEY_NOT_VERIFIABLE);
            return null;
        };

        return $this->signature_format . ' ' . $server_public_host_key;
    }

    /**
     * Returns the exit status of an SSH command or false
     */
    public function getExitStatus(): ?int
    {
        if (!isset($this->exit_status)) {
            return null;
        }
        return $this->exit_status;
    }

    /**
     * Returns the number of columns for the terminal window size.
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function getWindowColumns(): int
    {
        return $this->windowColumns;
    }

    /**
     * Returns the number of rows for the terminal window size.
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function getWindowRows(): int
    {
        return $this->windowRows;
    }

    /**
     * Sets the number of columns for the terminal window size.
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function setWindowColumns(int $value): void
    {
        $this->windowColumns = $value;
    }

    /**
     * Sets the number of rows for the terminal window size.
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function setWindowRows(int $value): void
    {
        $this->windowRows = $value;
    }

    /**
     * Sets the number of columns and rows for the terminal window size.
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function setWindowSize(int $columns = 80, int $rows = 24): void
    {
        $this->windowColumns = $columns;
        $this->windowRows = $rows;
    }

    /**
     * To String Magic Method
     */
    public function __toString(): string
    {
        return $this->getResourceId();
    }

    /**
     * Get Resource ID
     *
     * We use {} because that symbols should not be in URL according to
     * {@link http://tools.ietf.org/html/rfc3986#section-2 RFC}.
     * It will safe us from any conflicts, because otherwise regexp will
     * match all alphanumeric domains.
     */
    public function getResourceId(): string
    {
        return '{' . spl_object_hash($this) . '}';
    }

    public static function getConnectionByResourceId(string $id): ?SSH2
    {
        if (array_key_exists($id, self::$connections)) {
            /**
             * @psalm-ignore-var
             * @var SSH2|null $ssh2
             */
            $ssh2 =  self::$connections[$id]->get();
            return $ssh2;
        }
        return null;
    }

    /**
     * Return all excising connections
     *
     * @return array<string, SSH2>
     */
    public static function getConnections(): array
    {
        $temp = [];
        foreach (self::$connections as $key => $ref) {
            $conn = $ref->get();
            if (isset($conn)) {
                $temp[$key] = $conn;
            }
        }
        return $temp;
    }

    /**
     * Update packet types in log history
     */
    private function updateLogHistory(string $old, string $new): void
    {
        if (defined('NET_SSH2_LOGGING') && NET_SSH2_LOGGING == self::LOG_COMPLEX) {
            $this->message_number_log[count($this->message_number_log) - 1] = str_replace(
                $old,
                $new,
                $this->message_number_log[count($this->message_number_log) - 1]
            );
        }
    }

    /**
     * Return the list of authentication methods that may productively continue authentication.
     *
     * @see https://tools.ietf.org/html/rfc4252#section-5.1
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function getAuthMethodsToContinue(): ?array
    {
        return $this->auth_methods_to_continue;
    }

    /**
     * Enables "smart" multi-factor authentication (MFA)
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function enableSmartMFA(): void
    {
        $this->smartMFA = true;
    }

    /**
     * Disables "smart" multi-factor authentication (MFA)
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function disableSmartMFA(): void
    {
        $this->smartMFA = false;
    }

    /**
     * How many bytes until the next key re-exchange?
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function setBytesUntilKeyReexchange(int $bytes): void
    {
        $this->doKeyReexchangeAfterXBytes = $bytes;
    }
}
<?php

declare(strict_types=1);

namespace phpseclib4\Net\SFTP;

/**
 * http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-7.1
 * the order, in this case, matters quite a lot - see \phpseclib4\Net\SFTP::_parseAttributes() to understand why
 *
 * @internal
 */
abstract class Attribute
{
    public const SIZE = 0x00000001;
    public const UIDGID = 0x00000002;          // defined in SFTPv3, removed in SFTPv4+
    public const OWNERGROUP = 0x00000080;      // defined in SFTPv4+
    public const PERMISSIONS = 0x00000004;
    public const ACCESSTIME = 0x00000008;
    public const CREATETIME = 0x00000010;      // SFTPv4+
    public const MODIFYTIME = 0x00000020;
    public const ACL = 0x00000040;
    public const SUBSECOND_TIMES = 0x00000100;
    public const BITS = 0x00000200;            // SFTPv5+
    public const ALLOCATION_SIZE = 0x00000400; // SFTPv6+
    public const TEXT_HINT = 0x00000800;
    public const MIME_TYPE = 0x00001000;
    public const LINK_COUNT = 0x00002000;
    public const UNTRANSLATED_NAME = 0x00004000;
    public const CTIME = 0x00008000;
    // 0x80000000 will yield a floating point on 32-bit systems and converting floating points to integers
    // yields inconsistent behavior depending on how php is compiled.  so we left shift -1 (which, in
    // two's compliment, consists of all 1 bits) by 31.  on 64-bit systems this'll yield 0xFFFFFFFF80000000.
    // that's not a problem, however, and 'anded' and a 32-bit number, as all the leading 1 bits are ignored.
    public const EXTENDED = PHP_INT_SIZE == 4 ? (-1 << 31) : 0x80000000;

    /**
     */
    public static function getConstants(): array
    {
        $reflectionClass = new \ReflectionClass(static::class);
        return $reflectionClass->getConstants();
    }
}
<?php

declare(strict_types=1);

namespace phpseclib4\Net\SFTP;

/**
 * SFTPv5+ changed the flags up: https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-8.1.1.3
 *
 * @internal
 */
abstract class OpenFlag5
{
    // when SSH_FXF_ACCESS_DISPOSITION is a 3 bit field that controls how the file is opened
    public const CREATE_NEW = 0x00000000;
    public const CREATE_TRUNCATE = 0x00000001;
    public const OPEN_EXISTING = 0x00000002;
    public const OPEN_OR_CREATE = 0x00000003;
    public const TRUNCATE_EXISTING = 0x00000004;
    // the rest of the flags are not supported
    public const APPEND_DATA = 0x00000008; // "the offset field of SS_FXP_WRITE requests is ignored"
    public const APPEND_DATA_ATOMIC = 0x00000010;
    public const TEXT_MODE = 0x00000020;
    public const BLOCK_READ = 0x00000040;
    public const BLOCK_WRITE = 0x00000080;
    public const BLOCK_DELETE = 0x00000100;
    public const BLOCK_ADVISORY = 0x00000200;
    public const NOFOLLOW = 0x00000400;
    public const DELETE_ON_CLOSE = 0x00000800;
    public const ACCESS_AUDIT_ALARM_INFO = 0x00001000;
    public const ACCESS_BACKUP = 0x00002000;
    public const BACKUP_STREAM = 0x00004000;
    public const OVERRIDE_OWNER = 0x00008000;
}
<?php

declare(strict_types=1);

namespace phpseclib4\Net\SFTP;

use phpseclib4\Common\ConstantUtilityTrait;

/**
 * @internal
 */
abstract class StatusCode
{
    use ConstantUtilityTrait;

    public const OK = 0;
    public const EOF = 1;
    public const NO_SUCH_FILE = 2;
    public const PERMISSION_DENIED = 3;
    public const FAILURE = 4;
    public const BAD_MESSAGE = 5;
    public const NO_CONNECTION = 6;
    public const CONNECTION_LOST = 7;
    public const OP_UNSUPPORTED = 8;
    // the following were added in SFTPv4
    public const INVALID_HANDLE = 9;
    public const NO_SUCH_PATH = 10;
    public const FILE_ALREADY_EXISTS = 11;
    public const WRITE_PROTECT = 12;
    // the following were added in SFTPv5
    public const NO_MEDIA = 13;
    public const NO_SPACE_ON_FILESYSTEM = 14;
    public const QUOTA_EXCEEDED = 15;
    public const UNKNOWN_PRINCIPAL = 16;
    public const LOCK_CONFLICT = 17;
    // the following were added in SFTPv6 (draft-ietf-secsh-filexfer-06)
    public const DIR_NOT_EMPTY = 18;
    public const NOT_A_DIRECTORY = 19;
    public const INVALID_FILENAME = 20;
    public const LINK_LOOP = 21;
    // the following were added in SFTPv6 (draft-ietf-secsh-filexfer-07)
    public const CANNOT_DELETE = 22;
    public const INVALID_PARAMETER = 23;
    public const FILE_IS_A_DIRECTORY = 24;
    public const BYTE_RANGE_LOCK_CONFLICT = 25;
    public const BYTE_RANGE_LOCK_REFUSED = 26;
    public const DELETE_PENDING = 27;
    // the following were added in SFTPv6 (draft-ietf-secsh-filexfer-08)
    public const FILE_CORRUPT = 28;
    // the following were added in SFTPv6 (draft-ietf-secsh-filexfer-10)
    public const OWNER_INVALID = 29;
    public const GROUP_INVALID = 30;
    // the following were added in SFTPv6 (draft-ietf-secsh-filexfer-13)
    public const NO_MATCHING_BYTE_RANGE_LOCK = 31;
}
<?php

declare(strict_types=1);

namespace phpseclib4\Net\SFTP;

/**
 * http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.3
 * the flag definitions change somewhat in SFTPv5+.  if SFTPv5+ support is added to this library, maybe name
 * the array for that $this->open5_flags and similarly alter the constant names.
 *
 * @internal
 */
abstract class OpenFlag
{
    public const READ = 0x00000001;
    public const WRITE = 0x00000002;
    public const APPEND = 0x00000004;
    public const CREATE = 0x00000008;
    public const TRUNCATE = 0x00000010;
    public const EXCL = 0x00000020;
    public const TEXT = 0x00000040; // defined in SFTPv4
}
<?php

declare(strict_types=1);

namespace phpseclib4\Net\SFTP;

/**
 * http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2
 * see \phpseclib4\Net\SFTP::_parseLongname() for an explanation
 *
 * @internal
 */
abstract class FileType
{
    public const REGULAR = 1;
    public const DIRECTORY = 2;
    public const SYMLINK = 3;
    public const SPECIAL = 4;
    public const UNKNOWN = 5;
    // the following types were first defined for use in SFTPv5+
    // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-05#section-5.2
    public const SOCKET = 6;
    public const CHAR_DEVICE = 7;
    public const BLOCK_DEVICE = 8;
    public const FIFO = 9;
}
<?php

declare(strict_types=1);

namespace phpseclib4\Net\SFTP;

use phpseclib4\Common\ConstantUtilityTrait;

/**
 * @internal
 */
abstract class PacketType
{
    use ConstantUtilityTrait;

    public const INIT = 1;
    public const VERSION = 2;
    public const OPEN = 3;
    public const CLOSE = 4;
    public const READ = 5;
    public const WRITE = 6;
    public const LSTAT = 7;
    public const SETSTAT = 9;
    public const FSETSTAT = 10;
    public const OPENDIR = 11;
    public const READDIR = 12;
    public const REMOVE = 13;
    public const MKDIR = 14;
    public const RMDIR = 15;
    public const REALPATH = 16;
    public const STAT = 17;
    public const RENAME = 18;
    public const READLINK = 19;
    public const SYMLINK = 20;
    public const LINK = 21;

    public const STATUS = 101;
    public const HANDLE = 102;
    public const DATA = 103;
    public const NAME = 104;
    public const ATTRS = 105;

    public const EXTENDED = 200;
    public const EXTENDED_REPLY = 201;
}
<?php

/**
 * SFTP Stream Wrapper
 *
 * Creates an sftp:// protocol handler that can be used with, for example, fopen(), dir(), etc.
 *
 * PHP version 8.1+
 *
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2013-2026 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      https://phpseclib.com/
 */

declare(strict_types=1);

namespace phpseclib4\Net\SFTP;

use phpseclib4\Crypt\Common\PrivateKey;
use phpseclib4\Exception\FileSystemException;
use phpseclib4\Net\{SFTP, SSH2};
use phpseclib4\Net\SSH2\MessageType as SSH2MessageType;

/**
 * SFTP Stream Wrapper
 *
 * @author  Jim Wigginton <terrafrost@php.net>
 */
class Stream
{
    /**
     * SFTP instances
     *
     * Rather than re-create the connection we re-use instances if possible
     */
    public static array $instances;

    /**
     * SFTP instance
     */
    private SFTP $sftp;

    /**
     * Path
     */
    private string $path;

    /**
     * Mode
     */
    private string $mode;

    /**
     * Position
     */
    private int $pos;

    /**
     * Size
     */
    private ?int $size;

    /**
     * Directory entries
     */
    private array $entries;

    /**
     * EOF flag
     */
    private bool $eof;

    /**
     * Context resource
     *
     * Technically this needs to be publicly accessible so PHP can set it directly
     *
     * @var resource
     */
    public $context;

    /**
     * Notification callback function
     *
     * @var callable
     */
    private \Closure|string|array $notification;

    /**
     * Registers this class as a URL wrapper.
     *
     * @param string $protocol The wrapper name to be registered.
     * @return bool True on success, false otherwise.
     */
    public static function register(string $protocol = 'sftp'): bool
    {
        if (in_array($protocol, stream_get_wrappers(), true)) {
            return false;
        }
        return stream_wrapper_register($protocol, get_called_class());
    }

    /**
     * The Constructor
     *
     * @psalm-suppress PossiblyUnusedMethod
     */
    public function __construct()
    {
        if (defined('NET_SFTP_STREAM_LOGGING')) {
            echo "__construct()\r\n";
        }
    }

    /**
     * Path Parser
     *
     * Extract a path from a URI and actually connect to an SSH server if appropriate
     *
     * If "notification" is set as a context parameter the message code for successful login is
     * SSHMsg::USERAUTH_SUCCESS. For a failed login it's SSHMsg::USERAUTH_FAILURE.
     */
    protected function parse_path(string $path): ?string
    {
        $orig = $path;
        $url = parse_url($path) + ['port' => 22];

        /**
         * @var string $scheme
         * @var string $host
         * @var int $port
         * @var string $user
         * @var string $pass
         * @var string $query
         * @var string $fragment
         */
        $keys = ['scheme', 'host', 'port', 'user', 'pass', 'path', 'query', 'fragment'];
        foreach ($keys as $key) {
            if (isset($url[$key])) {
                $$key = $url[$key];
            }
        }

        if (isset($query)) {
            $path .= '?' . $query;
        } elseif (preg_match('/(\?|\?#)$/', $orig)) {
            $path .= '?';
        }
        if (isset($fragment)) {
            $path .= '#' . $fragment;
        } elseif ($orig[-1] == '#') {
            $path .= '#';
        }

        if (!isset($host)) {
            return null;
        }

        if (isset($this->context)) {
            $context = stream_context_get_params($this->context);
            if (isset($context['notification']) && is_callable($context['notification'])) {
                $this->notification = $context['notification'];
            }
        }

        if (preg_match('/^{[a-z0-9]+}$/i', $host)) {
            $host = SSH2::getConnectionByResourceId($host);
            if ($host === null) {
                return null;
            }
            $this->sftp = $host;
        } else {
            $context = isset($this->context) ?
                stream_context_get_options($this->context) :
                stream_context_get_options(stream_context_get_default());
            if (isset($context[$scheme]['session'])) {
                $sftp = $context[$scheme]['session'];
            }
            if (isset($context[$scheme]['sftp'])) {
                $sftp = $context[$scheme]['sftp'];
            }
            if (isset($sftp) && $sftp instanceof SFTP) {
                $this->sftp = $sftp;
                return $path;
            }
            if (isset($context[$scheme]['username'])) {
                $user = $context[$scheme]['username'];
            }
            if (isset($context[$scheme]['password'])) {
                $pass = $context[$scheme]['password'];
            }
            if (isset($context[$scheme]['privkey']) && $context[$scheme]['privkey'] instanceof PrivateKey) {
                $pass = $context[$scheme]['privkey'];
            }

            if (!isset($user) || !isset($pass)) {
                return null;
            }

            // casting $pass to a string is necessary in the event that it's a \phpseclib4\Crypt\RSA object
            if (isset(self::$instances[$host][$port][$user][(string) $pass])) {
                $this->sftp = self::$instances[$host][$port][$user][(string) $pass];
            } else {
                $this->sftp = new SFTP($host, $port);
                $this->sftp->disableStatCache();
                if (isset($this->notification) && is_callable($this->notification)) {
                    /* if !is_callable($this->notification) we could do this:

                       user_error('fopen(): failed to call user notifier', E_USER_WARNING);

                       the ftp wrapper gives errors like that when the notifier isn't callable.
                       i've opted not to do that, however, since the ftp wrapper gives the line
                       on which the fopen occurred as the line number - not the line that the
                       user_error is on.
                    */
                    call_user_func($this->notification, STREAM_NOTIFY_CONNECT, STREAM_NOTIFY_SEVERITY_INFO, '', 0, 0, 0);
                    call_user_func($this->notification, STREAM_NOTIFY_AUTH_REQUIRED, STREAM_NOTIFY_SEVERITY_INFO, '', 0, 0, 0);
                    if (!$this->sftp->login($user, $pass)) {
                        call_user_func($this->notification, STREAM_NOTIFY_AUTH_RESULT, STREAM_NOTIFY_SEVERITY_ERR, 'Login Failure', SSH2MessageType::USERAUTH_FAILURE, 0, 0);
                        return null;
                    }
                    call_user_func($this->notification, STREAM_NOTIFY_AUTH_RESULT, STREAM_NOTIFY_SEVERITY_INFO, 'Login Success', SSH2MessageType::USERAUTH_SUCCESS, 0, 0);
                } else {
                    if (!$this->sftp->login($user, $pass)) {
                        return null;
                    }
                }
                self::$instances[$host][$port][$user][(string) $pass] = $this->sftp;
            }
        }

        return $path;
    }

    /**
     * Opens file or URL
     */
    private function _stream_open(string $path, string $mode): bool
    {
        $path = $this->parse_path($path);

        if (!isset($path)) {
            return false;
        }
        $this->path = $path;

        try {
            $this->size = $this->sftp->filesize($path);
        } catch (\Exception) {
            $this->size = null;
        }
        $this->mode = preg_replace('#[bt]$#', '', $mode);
        $this->eof = false;

        if (!isset($this->size)) {
            if ($this->mode[0] == 'r') {
                return false;
            } else {
                try {
                    $this->sftp->touch($path);
                } catch (\Exception $e) {
                    if (isset($this->notification) && is_callable($this->notification)) {
                        call_user_func($this->notification, STREAM_NOTIFY_FAILURE, STREAM_NOTIFY_SEVERITY_ERR, $e->getMessage(), $e->getCode(), 0, 0);
                    }
                    return false;
                }
                $this->size = 0;
            }
        } else {
            switch ($this->mode[0]) {
                case 'x':
                    return false;
                case 'w':
                    try {
                        $this->sftp->truncate($path, 0);
                    } catch (\Exception $e) {
                        if (isset($this->notification) && is_callable($this->notification)) {
                            call_user_func($this->notification, STREAM_NOTIFY_FAILURE, STREAM_NOTIFY_SEVERITY_ERR, $e->getMessage(), $e->getCode(), 0, 0);
                        }
                        return false;
                    }
                    $this->size = 0;
            }
        }

        $this->pos = $this->mode[0] != 'a' ? 0 : $this->size;

        return true;
    }

    /**
     * Read from stream
     *
     * @return string|false
     */
    private function _stream_read(int $count): string|bool
    {
        switch ($this->mode) {
            case 'w':
            case 'a':
            case 'x':
            case 'c':
                return false;
        }

        // commented out because some files - eg. /dev/urandom - will say their size is 0 when in fact it's kinda infinite
        //if ($this->pos >= $this->size) {
        //    $this->eof = true;
        //    return false;
        //}

        try {
            $result = $this->sftp->get($this->path, null, $this->pos, $count);
        } catch (\Exception $e) {
            if (isset($this->notification) && is_callable($this->notification)) {
                call_user_func($this->notification, STREAM_NOTIFY_FAILURE, STREAM_NOTIFY_SEVERITY_ERR, $e->getMessage(), $e->getCode(), 0, 0);
            }
            return false;
        }
        if (isset($this->notification) && is_callable($this->notification)) {
            // seems that PHP calls stream_read in 8k chunks
            call_user_func($this->notification, STREAM_NOTIFY_PROGRESS, STREAM_NOTIFY_SEVERITY_INFO, '', 0, strlen($result), $this->size);
        }

        $this->pos += strlen($result);

        return $result;
    }

    /**
     * Write to stream
     *
     * @return int|false
     */
    private function _stream_write(string $data): int|bool
    {
        switch ($this->mode) {
            case 'r':
                return false;
        }

        try {
            $this->sftp->put($this->path, $data, SFTP::SOURCE_STRING, $this->pos);
        } catch (\Exception $e) {
            if (isset($this->notification) && is_callable($this->notification)) {
                call_user_func($this->notification, STREAM_NOTIFY_FAILURE, STREAM_NOTIFY_SEVERITY_ERR, $e->getMessage(), $e->getCode(), 0, 0);
            }
            return false;
        }
        if (isset($this->notification) && is_callable($this->notification)) {
            // seems that PHP splits up strings into 8k blocks before calling stream_write
            call_user_func($this->notification, STREAM_NOTIFY_PROGRESS, STREAM_NOTIFY_SEVERITY_INFO, '', 0, strlen($data), strlen($data));
        }

        $this->pos += strlen($data);
        if ($this->pos > $this->size) {
            $this->size = $this->pos;
        }
        $this->eof = false;
        return strlen($data);
    }

    /**
     * Retrieve the current position of a stream
     */
    private function _stream_tell(): int
    {
        return $this->pos;
    }

    /**
     * Tests for end-of-file on a file pointer
     *
     * In my testing there are four classes functions that normally effect the pointer:
     * fseek, fputs  / fwrite, fgets / fread and ftruncate.
     *
     * Only fgets / fread, however, results in feof() returning true. do fputs($fp, 'aaa') on a blank file and feof()
     * will return false. do fread($fp, 1) and feof() will then return true. do fseek($fp, 10) on ablank file and feof()
     * will return false. do fread($fp, 1) and feof() will then return true.
     */
    private function _stream_eof(): bool
    {
        return $this->eof;
    }

    /**
     * Seeks to specific location in a stream
     */
    private function _stream_seek(int $offset, int $whence): bool
    {
        switch ($whence) {
            case SEEK_SET:
                if ($offset < 0) {
                    return false;
                }
                break;
            case SEEK_CUR:
                $offset += $this->pos;
                break;
            case SEEK_END:
                $offset += $this->size;
        }

        $this->pos = $offset;
        $this->eof = false;
        return true;
    }

    /**
     * Change stream options
     */
    private function _stream_metadata(string $path, int $option, int|string|array $var): bool
    {
        $path = $this->parse_path($path);
        if (!isset($path)) {
            return false;
        }

        try {
            switch ($option) {
                case STREAM_META_TOUCH:
                    $time = $var[0] ?? null;
                    $atime = $var[1] ?? null;
                    $this->sftp->touch($path, $time, $atime);
                    return true;
                case STREAM_META_OWNER_NAME:
                    if ($this->getNegotiatedVersion() >= 4) {
                        $this->sftp->chown($path, $var);
                        return true;
                    }
                    return false;
                case STREAM_META_GROUP_NAME:
                    if ($this->getNegotiatedVersion() >= 4) {
                        $this->sftp->chgrp($path, $var);
                        return true;
                    }
                    return false;
                case STREAM_META_OWNER:
                    if ($this->getNegotiatedVersion() < 4) {
                        $this->sftp->chown($path, $var);
                        return true;
                    }
                    return false;
                case STREAM_META_GROUP:
                    if ($this->getNegotiatedVersion() < 4) {
                        $this->sftp->chgrp($path, $var);
                        return true;
                    }
                    return false;
                case STREAM_META_ACCESS:
                    $this->sftp->chmod($path, $var);
                    return true;
                default:
                    return false;
            }
        } catch (\Exception $e) {
            if (isset($this->notification) && is_callable($this->notification)) {
                call_user_func($this->notification, STREAM_NOTIFY_FAILURE, STREAM_NOTIFY_SEVERITY_ERR, $e->getMessage(), $e->getCode(), 0, 0);
            }
            return false;
        }
    }

    /**
     * Retrieve the underlying resource
     *
     * @return resource|false
     */
    private function _stream_cast(int $cast_as)
    {
        return is_resource($this->sftp->fsock) ? $this->sftp->fsock : false;
    }

    /**
     * Advisory file locking
     */
    private function _stream_lock(int $operation): bool
    {
        return false;
    }

    /**
     * Renames a file or directory
     *
     * Attempts to rename oldname to newname, moving it between directories if necessary.
     * If newname exists, it will be overwritten.  This is a departure from what \phpseclib4\Net\SFTP
     * does.
     */
    private function _rename(string $path_from, string $path_to): bool
    {
        $path1 = parse_url($path_from);
        $path2 = parse_url($path_to);
        unset($path1['path'], $path2['path']);
        if ($path1 != $path2) {
            return false;
        }

        $path_from = $this->parse_path($path_from);
        $path_to = parse_url($path_to);
        if (!isset($path_from)) {
            return false;
        }

        $path_to = $path_to['path'];
        // "It is an error if there already exists a file with the name specified by newpath."
        //  -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-6.5
        try {
            $this->sftp->rename($path_from, $path_to);
        } catch (FileSystemException $e) {
            if (!$this->sftp->file_exists($path_to)) {
                if (isset($this->notification) && is_callable($this->notification)) {
                    call_user_func($this->notification, STREAM_NOTIFY_FAILURE, STREAM_NOTIFY_SEVERITY_ERR, $e->getMessage(), $e->getCode(), 0, 0);
                }
                return false;
            }
            try {
                $this->sftp->delete($path_to, true);
                $this->sftp->rename($path_from, $path_to);
            } catch (FileSystemException $e) {
                if (isset($this->notification) && is_callable($this->notification)) {
                    call_user_func($this->notification, STREAM_NOTIFY_FAILURE, STREAM_NOTIFY_SEVERITY_ERR, $e->getMessage(), $e->getCode(), 0, 0);
                }
                return false;
            }
        }

        return true;
    }

    /**
     * Open directory handle
     *
     * nlist() is the best that this function is realistically going to be able to do. When an SFTP client sends
     * a SSH_FXP_READDIR packet you don't generally get info on just one file but on multiple files. Quoting
     * the SFTP specs:
     *
     *    The SSH_FXP_NAME response has the following format:
     *
     *        uint32     id
     *        uint32     count
     *        repeats count times:
     *                string     filename
     *                string     longname
     *                ATTRS      attrs
     */
    private function _dir_opendir(string $path, int $options): bool
    {
        $path = $this->parse_path($path);
        if (!isset($path)) {
            return false;
        }
        $this->pos = 0;
        try {
            $this->entries = $this->sftp->nlist($path);
        } catch (FileSystemException $e) {
            if (isset($this->notification) && is_callable($this->notification)) {
                call_user_func($this->notification, STREAM_NOTIFY_FAILURE, STREAM_NOTIFY_SEVERITY_ERR, $e->getMessage(), $e->getCode(), 0, 0);
            }
            return false;
        }
        return true;
    }

    /**
     * Read entry from directory handle
     */
    private function _dir_readdir()
    {
        if (isset($this->entries[$this->pos])) {
            return $this->entries[$this->pos++];
        }
        return false;
    }

    /**
     * Rewind directory handle
     */
    private function _dir_rewinddir(): bool
    {
        $this->pos = 0;
        return true;
    }

    /**
     * Close directory handle
     */
    private function _dir_closedir(): bool
    {
        return true;
    }

    /**
     * Create a directory
     *
     * Only valid $options is STREAM_MKDIR_RECURSIVE
     */
    private function _mkdir(string $path, int $mode, int $options): bool
    {
        $path = $this->parse_path($path);
        if (!isset($path)) {
            return false;
        }

        try {
            $this->sftp->mkdir($path, $mode, boolval($options & STREAM_MKDIR_RECURSIVE));
            return true;
        } catch (FileSystemException $e) {
            if (isset($this->notification) && is_callable($this->notification)) {
                call_user_func($this->notification, STREAM_NOTIFY_FAILURE, STREAM_NOTIFY_SEVERITY_ERR, $e->getMessage(), $e->getCode(), 0, 0);
            }
            return false;
        }
    }

    /**
     * Removes a directory
     *
     * Only valid $options is STREAM_MKDIR_RECURSIVE per <http://php.net/streamwrapper.rmdir>, however,
     * <http://php.net/rmdir>  does not have a $recursive parameter as mkdir() does so I don't know how
     * STREAM_MKDIR_RECURSIVE is supposed to be set. Also, when I try it out with rmdir() I get 8 as
     * $options. What does 8 correspond to?
     */
    private function _rmdir(string $path, int $options): bool
    {
        $path = $this->parse_path($path);
        if (!isset($path)) {
            return false;
        }

        try {
            $this->sftp->rmdir($path);
            return true;
        } catch (FileSystemException $e) {
            if (isset($this->notification) && is_callable($this->notification)) {
                call_user_func($this->notification, STREAM_NOTIFY_FAILURE, STREAM_NOTIFY_SEVERITY_ERR, $e->getMessage(), $e->getCode(), 0, 0);
            }
            return false;
        }
    }

    /**
     * Flushes the output
     *
     * See <http://php.net/fflush>. Always returns true because \phpseclib4\Net\SFTP doesn't cache stuff before writing
     */
    private function _stream_flush(): bool
    {
        return true;
    }

    /**
     * Retrieve information about a file resource
     */
    private function _stream_stat(): array|bool
    {
        try {
            return $this->sftp->stat($this->path);
        } catch (FileSystemException $e) {
            if (isset($this->notification) && is_callable($this->notification)) {
                call_user_func($this->notification, STREAM_NOTIFY_FAILURE, STREAM_NOTIFY_SEVERITY_ERR, $e->getMessage(), $e->getCode(), 0, 0);
            }
            return false;
        }
    }

    /**
     * Delete a file
     */
    private function _unlink(string $path): bool
    {
        $path = $this->parse_path($path);
        if (!isset($path)) {
            return false;
        }

        try {
            $this->sftp->delete($path, false);
            return true;
        } catch (FileSystemException $e) {
            if (isset($this->notification) && is_callable($this->notification)) {
                call_user_func($this->notification, STREAM_NOTIFY_FAILURE, STREAM_NOTIFY_SEVERITY_ERR, $e->getMessage(), $e->getCode(), 0, 0);
            }
            return false;
        }
    }

    /**
     * Retrieve information about a file
     *
     * Ignores the STREAM_URL_STAT_QUIET flag because the entirety of \phpseclib4\Net\SFTP\Stream is quiet by default
     * might be worthwhile to reconstruct bits 12-16 (ie. the file type) if mode doesn't have them but we'll
     * cross that bridge when and if it's reached
     */
    private function _url_stat(string $path, int $flags): array|bool
    {
        $path = $this->parse_path($path);
        if (!isset($path)) {
            return false;
        }

        try {
            return $flags & STREAM_URL_STAT_LINK ? $this->sftp->lstat($path) : $this->sftp->stat($path);
        } catch (FileSystemException $e) {
            if (isset($this->notification) && is_callable($this->notification)) {
                call_user_func($this->notification, STREAM_NOTIFY_FAILURE, STREAM_NOTIFY_SEVERITY_ERR, $e->getMessage(), $e->getCode(), 0, 0);
            }
            return false;
        }
    }

    /**
     * Truncate stream
     */
    private function _stream_truncate(int $new_size): bool
    {
        try {
            $this->sftp->truncate($this->path, $new_size);
            $this->eof = false;
            $this->size = $new_size;
            return true;
        } catch (FileSystemException $e) {
            if (isset($this->notification) && is_callable($this->notification)) {
                call_user_func($this->notification, STREAM_NOTIFY_FAILURE, STREAM_NOTIFY_SEVERITY_ERR, $e->getMessage(), $e->getCode(), 0, 0);
            }
            return false;
        }
    }

    /**
     * Change stream options
     *
     * STREAM_OPTION_WRITE_BUFFER isn't supported for the same reason stream_flush isn't.
     * The other two aren't supported because of limitations in \phpseclib4\Net\SFTP.
     */
    private function _stream_set_option(int $option, int $arg1, int $arg2): bool
    {
        return false;
    }

    /**
     * Close a resource
     */
    private function _stream_close(): void
    {
    }

    /**
     * __call Magic Method
     *
     * When you're utilizing an SFTP stream you're not calling the methods in this class directly - PHP is calling them for you.
     * Which kinda begs the question... what methods is PHP calling and what parameters is it passing to them? This function
     * lets you figure that out.
     *
     * If NET_SFTP_STREAM_LOGGING is defined all calls will be output on the screen and then (regardless of whether or not
     * NET_SFTP_STREAM_LOGGING is enabled) the parameters will be passed through to the appropriate method.
     */
    public function __call(string $name, array $arguments)
    {
        if (defined('NET_SFTP_STREAM_LOGGING')) {
            echo $name . '(';
            $last = count($arguments) - 1;
            foreach ($arguments as $i => $argument) {
                var_export($argument);
                if ($i != $last) {
                    echo ',';
                }
            }
            echo ")\r\n";
        }
        $name = '_' . $name;
        if (!method_exists($this, $name)) {
            return false;
        }
        return $this->$name(...$arguments);
    }
}
<?php

// autoload.php @generated by Composer

if (PHP_VERSION_ID < 50600) {
    if (!headers_sent()) {
        header('HTTP/1.1 500 Internal Server Error');
    }
    $err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
    if (!ini_get('display_errors')) {
        if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
            fwrite(STDERR, $err);
        } elseif (!headers_sent()) {
            echo $err;
        }
    }
    throw new RuntimeException($err);
}

require_once __DIR__ . '/composer/autoload_real.php';

return ComposerAutoloaderInitfaa67bc9fee81c220dc566b8c299dea4::getLoader();
<?php

// autoload_real.php @generated by Composer

class ComposerAutoloaderInitfaa67bc9fee81c220dc566b8c299dea4
{
    private static $loader;

    public static function loadClassLoader($class)
    {
        if ('Composer\Autoload\ClassLoader' === $class) {
            require __DIR__ . '/ClassLoader.php';
        }
    }

    /**
     * @return \Composer\Autoload\ClassLoader
     */
    public static function getLoader()
    {
        if (null !== self::$loader) {
            return self::$loader;
        }

        require __DIR__ . '/platform_check.php';

        spl_autoload_register(array('ComposerAutoloaderInitfaa67bc9fee81c220dc566b8c299dea4', 'loadClassLoader'), true, true);
        self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
        spl_autoload_unregister(array('ComposerAutoloaderInitfaa67bc9fee81c220dc566b8c299dea4', 'loadClassLoader'));

        require __DIR__ . '/autoload_static.php';
        call_user_func(\Composer\Autoload\ComposerStaticInitfaa67bc9fee81c220dc566b8c299dea4::getInitializer($loader));

        $loader->register(true);

        $filesToLoad = \Composer\Autoload\ComposerStaticInitfaa67bc9fee81c220dc566b8c299dea4::$files;
        $requireFile = \Closure::bind(static function ($fileIdentifier, $file) {
            if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
                $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;

                require $file;
            }
        }, null, null);
        foreach ($filesToLoad as $fileIdentifier => $file) {
            $requireFile($fileIdentifier, $file);
        }

        return $loader;
    }
}
<?php

// autoload_classmap.php @generated by Composer

$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);

return array(
    'AllowDynamicProperties' => $vendorDir . '/symfony/polyfill-php82/Resources/stubs/AllowDynamicProperties.php',
    'Amp\\ByteStream\\AsyncWriter' => $vendorDir . '/amphp/byte-stream/src/AsyncWriter.php',
    'Amp\\ByteStream\\Base64\\Base64DecodingReadableStream' => $vendorDir . '/amphp/byte-stream/src/Base64/Base64DecodingReadableStream.php',
    'Amp\\ByteStream\\Base64\\Base64DecodingWritableStream' => $vendorDir . '/amphp/byte-stream/src/Base64/Base64DecodingWritableStream.php',
    'Amp\\ByteStream\\Base64\\Base64EncodingReadableStream' => $vendorDir . '/amphp/byte-stream/src/Base64/Base64EncodingReadableStream.php',
    'Amp\\ByteStream\\Base64\\Base64EncodingWritableStream' => $vendorDir . '/amphp/byte-stream/src/Base64/Base64EncodingWritableStream.php',
    'Amp\\ByteStream\\BufferException' => $vendorDir . '/amphp/byte-stream/src/BufferException.php',
    'Amp\\ByteStream\\BufferedReader' => $vendorDir . '/amphp/byte-stream/src/BufferedReader.php',
    'Amp\\ByteStream\\ClosedException' => $vendorDir . '/amphp/byte-stream/src/ClosedException.php',
    'Amp\\ByteStream\\Compression\\CompressingReadableStream' => $vendorDir . '/amphp/byte-stream/src/Compression/CompressingReadableStream.php',
    'Amp\\ByteStream\\Compression\\CompressingWritableStream' => $vendorDir . '/amphp/byte-stream/src/Compression/CompressingWritableStream.php',
    'Amp\\ByteStream\\Compression\\DecompressingReadableStream' => $vendorDir . '/amphp/byte-stream/src/Compression/DecompressingReadableStream.php',
    'Amp\\ByteStream\\Compression\\DecompressingWritableStream' => $vendorDir . '/amphp/byte-stream/src/Compression/DecompressingWritableStream.php',
    'Amp\\ByteStream\\Internal\\ChannelParser' => $vendorDir . '/amphp/byte-stream/src/Internal/ChannelParser.php',
    'Amp\\ByteStream\\Payload' => $vendorDir . '/amphp/byte-stream/src/Payload.php',
    'Amp\\ByteStream\\PendingReadError' => $vendorDir . '/amphp/byte-stream/src/PendingReadError.php',
    'Amp\\ByteStream\\Pipe' => $vendorDir . '/amphp/byte-stream/src/Pipe.php',
    'Amp\\ByteStream\\ReadableBuffer' => $vendorDir . '/amphp/byte-stream/src/ReadableBuffer.php',
    'Amp\\ByteStream\\ReadableIterableStream' => $vendorDir . '/amphp/byte-stream/src/ReadableIterableStream.php',
    'Amp\\ByteStream\\ReadableResourceStream' => $vendorDir . '/amphp/byte-stream/src/ReadableResourceStream.php',
    'Amp\\ByteStream\\ReadableStream' => $vendorDir . '/amphp/byte-stream/src/ReadableStream.php',
    'Amp\\ByteStream\\ReadableStreamChain' => $vendorDir . '/amphp/byte-stream/src/ReadableStreamChain.php',
    'Amp\\ByteStream\\ReadableStreamIteratorAggregate' => $vendorDir . '/amphp/byte-stream/src/ReadableStreamIteratorAggregate.php',
    'Amp\\ByteStream\\ResourceStream' => $vendorDir . '/amphp/byte-stream/src/ResourceStream.php',
    'Amp\\ByteStream\\StreamChannel' => $vendorDir . '/amphp/byte-stream/src/StreamChannel.php',
    'Amp\\ByteStream\\StreamException' => $vendorDir . '/amphp/byte-stream/src/StreamException.php',
    'Amp\\ByteStream\\WritableBuffer' => $vendorDir . '/amphp/byte-stream/src/WritableBuffer.php',
    'Amp\\ByteStream\\WritableIterableStream' => $vendorDir . '/amphp/byte-stream/src/WritableIterableStream.php',
    'Amp\\ByteStream\\WritableResourceStream' => $vendorDir . '/amphp/byte-stream/src/WritableResourceStream.php',
    'Amp\\ByteStream\\WritableStream' => $vendorDir . '/amphp/byte-stream/src/WritableStream.php',
    'Amp\\Cache\\AtomicCache' => $vendorDir . '/amphp/cache/src/AtomicCache.php',
    'Amp\\Cache\\Cache' => $vendorDir . '/amphp/cache/src/Cache.php',
    'Amp\\Cache\\CacheException' => $vendorDir . '/amphp/cache/src/CacheException.php',
    'Amp\\Cache\\LocalCache' => $vendorDir . '/amphp/cache/src/LocalCache.php',
    'Amp\\Cache\\NullCache' => $vendorDir . '/amphp/cache/src/NullCache.php',
    'Amp\\Cache\\PrefixCache' => $vendorDir . '/amphp/cache/src/PrefixCache.php',
    'Amp\\Cache\\SerializedCache' => $vendorDir . '/amphp/cache/src/SerializedCache.php',
    'Amp\\Cache\\StringCache' => $vendorDir . '/amphp/cache/src/StringCache.php',
    'Amp\\Cache\\StringCacheAdapter' => $vendorDir . '/amphp/cache/src/StringCacheAdapter.php',
    'Amp\\Cancellation' => $vendorDir . '/amphp/amp/src/Cancellation.php',
    'Amp\\CancelledException' => $vendorDir . '/amphp/amp/src/CancelledException.php',
    'Amp\\Closable' => $vendorDir . '/amphp/amp/src/Closable.php',
    'Amp\\CompositeCancellation' => $vendorDir . '/amphp/amp/src/CompositeCancellation.php',
    'Amp\\CompositeException' => $vendorDir . '/amphp/amp/src/CompositeException.php',
    'Amp\\CompositeLengthException' => $vendorDir . '/amphp/amp/src/CompositeLengthException.php',
    'Amp\\DeferredCancellation' => $vendorDir . '/amphp/amp/src/DeferredCancellation.php',
    'Amp\\DeferredFuture' => $vendorDir . '/amphp/amp/src/DeferredFuture.php',
    'Amp\\Dns\\BlockingFallbackDnsResolver' => $vendorDir . '/amphp/dns/src/BlockingFallbackDnsResolver.php',
    'Amp\\Dns\\DnsConfig' => $vendorDir . '/amphp/dns/src/DnsConfig.php',
    'Amp\\Dns\\DnsConfigException' => $vendorDir . '/amphp/dns/src/DnsConfigException.php',
    'Amp\\Dns\\DnsConfigLoader' => $vendorDir . '/amphp/dns/src/DnsConfigLoader.php',
    'Amp\\Dns\\DnsException' => $vendorDir . '/amphp/dns/src/DnsException.php',
    'Amp\\Dns\\DnsRecord' => $vendorDir . '/amphp/dns/src/DnsRecord.php',
    'Amp\\Dns\\DnsResolver' => $vendorDir . '/amphp/dns/src/DnsResolver.php',
    'Amp\\Dns\\DnsTimeoutException' => $vendorDir . '/amphp/dns/src/DnsTimeoutException.php',
    'Amp\\Dns\\HostLoader' => $vendorDir . '/amphp/dns/src/HostLoader.php',
    'Amp\\Dns\\Internal\\Socket' => $vendorDir . '/amphp/dns/src/Internal/Socket.php',
    'Amp\\Dns\\Internal\\TcpSocket' => $vendorDir . '/amphp/dns/src/Internal/TcpSocket.php',
    'Amp\\Dns\\Internal\\UdpSocket' => $vendorDir . '/amphp/dns/src/Internal/UdpSocket.php',
    'Amp\\Dns\\InvalidNameException' => $vendorDir . '/amphp/dns/src/InvalidNameException.php',
    'Amp\\Dns\\MissingDnsRecordException' => $vendorDir . '/amphp/dns/src/MissingDnsRecordException.php',
    'Amp\\Dns\\Rfc1035StubDnsResolver' => $vendorDir . '/amphp/dns/src/Rfc1035StubDnsResolver.php',
    'Amp\\Dns\\StaticDnsConfigLoader' => $vendorDir . '/amphp/dns/src/StaticDnsConfigLoader.php',
    'Amp\\Dns\\UnixDnsConfigLoader' => $vendorDir . '/amphp/dns/src/UnixDnsConfigLoader.php',
    'Amp\\Dns\\WindowsDnsConfigLoader' => $vendorDir . '/amphp/dns/src/WindowsDnsConfigLoader.php',
    'Amp\\DoH\\DoHConfig' => $vendorDir . '/danog/dns-over-https/lib/DoHConfig.php',
    'Amp\\DoH\\DoHException' => $vendorDir . '/danog/dns-over-https/lib/DoHException.php',
    'Amp\\DoH\\DoHNameserver' => $vendorDir . '/danog/dns-over-https/lib/DoHNameserver.php',
    'Amp\\DoH\\DoHNameserverType' => $vendorDir . '/danog/dns-over-https/lib/DoHNameserverType.php',
    'Amp\\DoH\\Rfc8484StubDoHResolver' => $vendorDir . '/danog/dns-over-https/lib/Rfc8484StubDoHResolver.php',
    'Amp\\File\\Driver\\BlockingFile' => $vendorDir . '/amphp/file/src/Driver/BlockingFile.php',
    'Amp\\File\\Driver\\BlockingFilesystemDriver' => $vendorDir . '/amphp/file/src/Driver/BlockingFilesystemDriver.php',
    'Amp\\File\\Driver\\EioFile' => $vendorDir . '/amphp/file/src/Driver/EioFile.php',
    'Amp\\File\\Driver\\EioFilesystemDriver' => $vendorDir . '/amphp/file/src/Driver/EioFilesystemDriver.php',
    'Amp\\File\\Driver\\ParallelFile' => $vendorDir . '/amphp/file/src/Driver/ParallelFile.php',
    'Amp\\File\\Driver\\ParallelFilesystemDriver' => $vendorDir . '/amphp/file/src/Driver/ParallelFilesystemDriver.php',
    'Amp\\File\\Driver\\StatusCachingFile' => $vendorDir . '/amphp/file/src/Driver/StatusCachingFile.php',
    'Amp\\File\\Driver\\StatusCachingFilesystemDriver' => $vendorDir . '/amphp/file/src/Driver/StatusCachingFilesystemDriver.php',
    'Amp\\File\\Driver\\UvFile' => $vendorDir . '/amphp/file/src/Driver/UvFile.php',
    'Amp\\File\\Driver\\UvFilesystemDriver' => $vendorDir . '/amphp/file/src/Driver/UvFilesystemDriver.php',
    'Amp\\File\\File' => $vendorDir . '/amphp/file/src/File.php',
    'Amp\\File\\FileCache' => $vendorDir . '/amphp/file/src/FileCache.php',
    'Amp\\File\\FileMutex' => $vendorDir . '/amphp/file/src/FileMutex.php',
    'Amp\\File\\Filesystem' => $vendorDir . '/amphp/file/src/Filesystem.php',
    'Amp\\File\\FilesystemDriver' => $vendorDir . '/amphp/file/src/FilesystemDriver.php',
    'Amp\\File\\FilesystemException' => $vendorDir . '/amphp/file/src/FilesystemException.php',
    'Amp\\File\\Internal\\Cache' => $vendorDir . '/amphp/file/src/Internal/Cache.php',
    'Amp\\File\\Internal\\EioPoll' => $vendorDir . '/amphp/file/src/Internal/EioPoll.php',
    'Amp\\File\\Internal\\FileTask' => $vendorDir . '/amphp/file/src/Internal/FileTask.php',
    'Amp\\File\\Internal\\FileWorker' => $vendorDir . '/amphp/file/src/Internal/FileWorker.php',
    'Amp\\File\\Internal\\QueuedWritesFile' => $vendorDir . '/amphp/file/src/Internal/QueuedWritesFile.php',
    'Amp\\File\\Internal\\UvPoll' => $vendorDir . '/amphp/file/src/Internal/UvPoll.php',
    'Amp\\File\\KeyedFileMutex' => $vendorDir . '/amphp/file/src/KeyedFileMutex.php',
    'Amp\\File\\PendingOperationError' => $vendorDir . '/amphp/file/src/PendingOperationError.php',
    'Amp\\File\\Whence' => $vendorDir . '/amphp/file/src/Whence.php',
    'Amp\\ForbidCloning' => $vendorDir . '/amphp/amp/src/ForbidCloning.php',
    'Amp\\ForbidSerialization' => $vendorDir . '/amphp/amp/src/ForbidSerialization.php',
    'Amp\\Future' => $vendorDir . '/amphp/amp/src/Future.php',
    'Amp\\Future\\UnhandledFutureError' => $vendorDir . '/amphp/amp/src/Future/UnhandledFutureError.php',
    'Amp\\Http\\Client\\ApplicationInterceptor' => $vendorDir . '/amphp/http-client/src/ApplicationInterceptor.php',
    'Amp\\Http\\Client\\BufferedContent' => $vendorDir . '/amphp/http-client/src/BufferedContent.php',
    'Amp\\Http\\Client\\Connection\\Connection' => $vendorDir . '/amphp/http-client/src/Connection/Connection.php',
    'Amp\\Http\\Client\\Connection\\ConnectionFactory' => $vendorDir . '/amphp/http-client/src/Connection/ConnectionFactory.php',
    'Amp\\Http\\Client\\Connection\\ConnectionLimitingPool' => $vendorDir . '/amphp/http-client/src/Connection/ConnectionLimitingPool.php',
    'Amp\\Http\\Client\\Connection\\ConnectionPool' => $vendorDir . '/amphp/http-client/src/Connection/ConnectionPool.php',
    'Amp\\Http\\Client\\Connection\\DefaultConnectionFactory' => $vendorDir . '/amphp/http-client/src/Connection/DefaultConnectionFactory.php',
    'Amp\\Http\\Client\\Connection\\Http1Connection' => $vendorDir . '/amphp/http-client/src/Connection/Http1Connection.php',
    'Amp\\Http\\Client\\Connection\\Http2Connection' => $vendorDir . '/amphp/http-client/src/Connection/Http2Connection.php',
    'Amp\\Http\\Client\\Connection\\HttpStream' => $vendorDir . '/amphp/http-client/src/Connection/HttpStream.php',
    'Amp\\Http\\Client\\Connection\\InterceptedStream' => $vendorDir . '/amphp/http-client/src/Connection/InterceptedStream.php',
    'Amp\\Http\\Client\\Connection\\Internal\\Http1Parser' => $vendorDir . '/amphp/http-client/src/Connection/Internal/Http1Parser.php',
    'Amp\\Http\\Client\\Connection\\Internal\\Http2ConnectionProcessor' => $vendorDir . '/amphp/http-client/src/Connection/Internal/Http2ConnectionProcessor.php',
    'Amp\\Http\\Client\\Connection\\Internal\\Http2Stream' => $vendorDir . '/amphp/http-client/src/Connection/Internal/Http2Stream.php',
    'Amp\\Http\\Client\\Connection\\Internal\\RequestNormalizer' => $vendorDir . '/amphp/http-client/src/Connection/Internal/RequestNormalizer.php',
    'Amp\\Http\\Client\\Connection\\Stream' => $vendorDir . '/amphp/http-client/src/Connection/Stream.php',
    'Amp\\Http\\Client\\Connection\\StreamLimitingPool' => $vendorDir . '/amphp/http-client/src/Connection/StreamLimitingPool.php',
    'Amp\\Http\\Client\\Connection\\UnlimitedConnectionPool' => $vendorDir . '/amphp/http-client/src/Connection/UnlimitedConnectionPool.php',
    'Amp\\Http\\Client\\Connection\\UpgradedSocket' => $vendorDir . '/amphp/http-client/src/Connection/UpgradedSocket.php',
    'Amp\\Http\\Client\\Cookie\\CookieInterceptor' => $vendorDir . '/amphp/http-client-cookies/src/CookieInterceptor.php',
    'Amp\\Http\\Client\\Cookie\\CookieJar' => $vendorDir . '/amphp/http-client-cookies/src/CookieJar.php',
    'Amp\\Http\\Client\\Cookie\\FileCookieJar' => $vendorDir . '/amphp/http-client-cookies/src/FileCookieJar.php',
    'Amp\\Http\\Client\\Cookie\\Internal\\PublicSuffixList' => $vendorDir . '/amphp/http-client-cookies/src/Internal/PublicSuffixList.php',
    'Amp\\Http\\Client\\Cookie\\LocalCookieJar' => $vendorDir . '/amphp/http-client-cookies/src/LocalCookieJar.php',
    'Amp\\Http\\Client\\Cookie\\NullCookieJar' => $vendorDir . '/amphp/http-client-cookies/src/NullCookieJar.php',
    'Amp\\Http\\Client\\DelegateHttpClient' => $vendorDir . '/amphp/http-client/src/DelegateHttpClient.php',
    'Amp\\Http\\Client\\EventListener' => $vendorDir . '/amphp/http-client/src/EventListener.php',
    'Amp\\Http\\Client\\EventListener\\LogHttpArchive' => $vendorDir . '/amphp/http-client/src/EventListener/LogHttpArchive.php',
    'Amp\\Http\\Client\\Form' => $vendorDir . '/amphp/http-client/src/Form.php',
    'Amp\\Http\\Client\\HttpClient' => $vendorDir . '/amphp/http-client/src/HttpClient.php',
    'Amp\\Http\\Client\\HttpClientBuilder' => $vendorDir . '/amphp/http-client/src/HttpClientBuilder.php',
    'Amp\\Http\\Client\\HttpContent' => $vendorDir . '/amphp/http-client/src/HttpContent.php',
    'Amp\\Http\\Client\\HttpException' => $vendorDir . '/amphp/http-client/src/HttpException.php',
    'Amp\\Http\\Client\\InterceptedHttpClient' => $vendorDir . '/amphp/http-client/src/InterceptedHttpClient.php',
    'Amp\\Http\\Client\\Interceptor\\AddRequestHeader' => $vendorDir . '/amphp/http-client/src/Interceptor/AddRequestHeader.php',
    'Amp\\Http\\Client\\Interceptor\\AddResponseHeader' => $vendorDir . '/amphp/http-client/src/Interceptor/AddResponseHeader.php',
    'Amp\\Http\\Client\\Interceptor\\DecompressResponse' => $vendorDir . '/amphp/http-client/src/Interceptor/DecompressResponse.php',
    'Amp\\Http\\Client\\Interceptor\\FollowRedirects' => $vendorDir . '/amphp/http-client/src/Interceptor/FollowRedirects.php',
    'Amp\\Http\\Client\\Interceptor\\ForbidUriUserInfo' => $vendorDir . '/amphp/http-client/src/Interceptor/ForbidUriUserInfo.php',
    'Amp\\Http\\Client\\Interceptor\\MatchOrigin' => $vendorDir . '/amphp/http-client/src/Interceptor/MatchOrigin.php',
    'Amp\\Http\\Client\\Interceptor\\ModifyRequest' => $vendorDir . '/amphp/http-client/src/Interceptor/ModifyRequest.php',
    'Amp\\Http\\Client\\Interceptor\\ModifyResponse' => $vendorDir . '/amphp/http-client/src/Interceptor/ModifyResponse.php',
    'Amp\\Http\\Client\\Interceptor\\RemoveRequestHeader' => $vendorDir . '/amphp/http-client/src/Interceptor/RemoveRequestHeader.php',
    'Amp\\Http\\Client\\Interceptor\\RemoveResponseHeader' => $vendorDir . '/amphp/http-client/src/Interceptor/RemoveResponseHeader.php',
    'Amp\\Http\\Client\\Interceptor\\ResolveBaseUri' => $vendorDir . '/amphp/http-client/src/Interceptor/ResolveBaseUri.php',
    'Amp\\Http\\Client\\Interceptor\\RetryRequests' => $vendorDir . '/amphp/http-client/src/Interceptor/RetryRequests.php',
    'Amp\\Http\\Client\\Interceptor\\SetRequestHeader' => $vendorDir . '/amphp/http-client/src/Interceptor/SetRequestHeader.php',
    'Amp\\Http\\Client\\Interceptor\\SetRequestHeaderIfUnset' => $vendorDir . '/amphp/http-client/src/Interceptor/SetRequestHeaderIfUnset.php',
    'Amp\\Http\\Client\\Interceptor\\SetRequestTimeout' => $vendorDir . '/amphp/http-client/src/Interceptor/SetRequestTimeout.php',
    'Amp\\Http\\Client\\Interceptor\\SetResponseHeader' => $vendorDir . '/amphp/http-client/src/Interceptor/SetResponseHeader.php',
    'Amp\\Http\\Client\\Interceptor\\SetResponseHeaderIfUnset' => $vendorDir . '/amphp/http-client/src/Interceptor/SetResponseHeaderIfUnset.php',
    'Amp\\Http\\Client\\Interceptor\\TooManyRedirectsException' => $vendorDir . '/amphp/http-client/src/Interceptor/TooManyRedirectsException.php',
    'Amp\\Http\\Client\\Internal\\EventInvoker' => $vendorDir . '/amphp/http-client/src/Internal/EventInvoker.php',
    'Amp\\Http\\Client\\Internal\\FormField' => $vendorDir . '/amphp/http-client/src/Internal/FormField.php',
    'Amp\\Http\\Client\\Internal\\HarAttributes' => $vendorDir . '/amphp/http-client/src/Internal/HarAttributes.php',
    'Amp\\Http\\Client\\Internal\\Phase' => $vendorDir . '/amphp/http-client/src/Internal/Phase.php',
    'Amp\\Http\\Client\\Internal\\ResponseBodyStream' => $vendorDir . '/amphp/http-client/src/Internal/ResponseBodyStream.php',
    'Amp\\Http\\Client\\Internal\\SizeLimitingReadableStream' => $vendorDir . '/amphp/http-client/src/Internal/SizeLimitingReadableStream.php',
    'Amp\\Http\\Client\\InvalidRequestException' => $vendorDir . '/amphp/http-client/src/InvalidRequestException.php',
    'Amp\\Http\\Client\\MissingAttributeError' => $vendorDir . '/amphp/http-client/src/MissingAttributeError.php',
    'Amp\\Http\\Client\\NetworkInterceptor' => $vendorDir . '/amphp/http-client/src/NetworkInterceptor.php',
    'Amp\\Http\\Client\\ParseException' => $vendorDir . '/amphp/http-client/src/ParseException.php',
    'Amp\\Http\\Client\\PooledHttpClient' => $vendorDir . '/amphp/http-client/src/PooledHttpClient.php',
    'Amp\\Http\\Client\\Request' => $vendorDir . '/amphp/http-client/src/Request.php',
    'Amp\\Http\\Client\\Response' => $vendorDir . '/amphp/http-client/src/Response.php',
    'Amp\\Http\\Client\\SocketException' => $vendorDir . '/amphp/http-client/src/SocketException.php',
    'Amp\\Http\\Client\\StreamedContent' => $vendorDir . '/amphp/http-client/src/StreamedContent.php',
    'Amp\\Http\\Client\\TimeoutException' => $vendorDir . '/amphp/http-client/src/TimeoutException.php',
    'Amp\\Http\\Client\\TlsException' => $vendorDir . '/amphp/http-client/src/TlsException.php',
    'Amp\\Http\\Client\\Trailers' => $vendorDir . '/amphp/http-client/src/Trailers.php',
    'Amp\\Http\\Cookie\\CookieAttributes' => $vendorDir . '/amphp/http/src/Cookie/CookieAttributes.php',
    'Amp\\Http\\Cookie\\InvalidCookieException' => $vendorDir . '/amphp/http/src/Cookie/InvalidCookieException.php',
    'Amp\\Http\\Cookie\\RequestCookie' => $vendorDir . '/amphp/http/src/Cookie/RequestCookie.php',
    'Amp\\Http\\Cookie\\ResponseCookie' => $vendorDir . '/amphp/http/src/Cookie/ResponseCookie.php',
    'Amp\\Http\\HPack' => $vendorDir . '/amphp/hpack/src/HPack.php',
    'Amp\\Http\\HPackException' => $vendorDir . '/amphp/hpack/src/HPackException.php',
    'Amp\\Http\\Http1\\Rfc7230' => $vendorDir . '/amphp/http/src/Http1/Rfc7230.php',
    'Amp\\Http\\Http2\\Http2ConnectionException' => $vendorDir . '/amphp/http/src/Http2/Http2ConnectionException.php',
    'Amp\\Http\\Http2\\Http2Parser' => $vendorDir . '/amphp/http/src/Http2/Http2Parser.php',
    'Amp\\Http\\Http2\\Http2Processor' => $vendorDir . '/amphp/http/src/Http2/Http2Processor.php',
    'Amp\\Http\\Http2\\Http2StreamException' => $vendorDir . '/amphp/http/src/Http2/Http2StreamException.php',
    'Amp\\Http\\HttpMessage' => $vendorDir . '/amphp/http/src/HttpMessage.php',
    'Amp\\Http\\HttpRequest' => $vendorDir . '/amphp/http/src/HttpRequest.php',
    'Amp\\Http\\HttpResponse' => $vendorDir . '/amphp/http/src/HttpResponse.php',
    'Amp\\Http\\HttpStatus' => $vendorDir . '/amphp/http/src/HttpStatus.php',
    'Amp\\Http\\Internal\\HPackNative' => $vendorDir . '/amphp/hpack/src/Internal/HPackNative.php',
    'Amp\\Http\\Internal\\HPackNghttp2' => $vendorDir . '/amphp/hpack/src/Internal/HPackNghttp2.php',
    'Amp\\Http\\InvalidHeaderException' => $vendorDir . '/amphp/http/src/InvalidHeaderException.php',
    'Amp\\Http\\Server\\ClientException' => $vendorDir . '/amphp/http-server/src/ClientException.php',
    'Amp\\Http\\Server\\DefaultErrorHandler' => $vendorDir . '/amphp/http-server/src/DefaultErrorHandler.php',
    'Amp\\Http\\Server\\DefaultExceptionHandler' => $vendorDir . '/amphp/http-server/src/DefaultExceptionHandler.php',
    'Amp\\Http\\Server\\Driver\\Client' => $vendorDir . '/amphp/http-server/src/Driver/Client.php',
    'Amp\\Http\\Server\\Driver\\ClientFactory' => $vendorDir . '/amphp/http-server/src/Driver/ClientFactory.php',
    'Amp\\Http\\Server\\Driver\\ConnectionLimitingClientFactory' => $vendorDir . '/amphp/http-server/src/Driver/ConnectionLimitingClientFactory.php',
    'Amp\\Http\\Server\\Driver\\ConnectionLimitingServerSocket' => $vendorDir . '/amphp/http-server/src/Driver/ConnectionLimitingServerSocket.php',
    'Amp\\Http\\Server\\Driver\\ConnectionLimitingServerSocketFactory' => $vendorDir . '/amphp/http-server/src/Driver/ConnectionLimitingServerSocketFactory.php',
    'Amp\\Http\\Server\\Driver\\DefaultHttpDriverFactory' => $vendorDir . '/amphp/http-server/src/Driver/DefaultHttpDriverFactory.php',
    'Amp\\Http\\Server\\Driver\\Http1Driver' => $vendorDir . '/amphp/http-server/src/Driver/Http1Driver.php',
    'Amp\\Http\\Server\\Driver\\Http2Driver' => $vendorDir . '/amphp/http-server/src/Driver/Http2Driver.php',
    'Amp\\Http\\Server\\Driver\\HttpDriver' => $vendorDir . '/amphp/http-server/src/Driver/HttpDriver.php',
    'Amp\\Http\\Server\\Driver\\HttpDriverFactory' => $vendorDir . '/amphp/http-server/src/Driver/HttpDriverFactory.php',
    'Amp\\Http\\Server\\Driver\\Internal\\AbstractHttpDriver' => $vendorDir . '/amphp/http-server/src/Driver/Internal/AbstractHttpDriver.php',
    'Amp\\Http\\Server\\Driver\\Internal\\Http2Stream' => $vendorDir . '/amphp/http-server/src/Driver/Internal/Http2Stream.php',
    'Amp\\Http\\Server\\Driver\\Internal\\HttpDriverErrorHandler' => $vendorDir . '/amphp/http-server/src/Driver/Internal/HttpDriverErrorHandler.php',
    'Amp\\Http\\Server\\Driver\\Internal\\StreamTimeoutTracker' => $vendorDir . '/amphp/http-server/src/Driver/Internal/StreamTimeoutTracker.php',
    'Amp\\Http\\Server\\Driver\\Internal\\TimeoutQueue' => $vendorDir . '/amphp/http-server/src/Driver/Internal/TimeoutQueue.php',
    'Amp\\Http\\Server\\Driver\\LoggingSocketClientFactory' => $vendorDir . '/amphp/http-server/src/Driver/LoggingSocketClientFactory.php',
    'Amp\\Http\\Server\\Driver\\SocketClient' => $vendorDir . '/amphp/http-server/src/Driver/SocketClient.php',
    'Amp\\Http\\Server\\Driver\\SocketClientFactory' => $vendorDir . '/amphp/http-server/src/Driver/SocketClientFactory.php',
    'Amp\\Http\\Server\\Driver\\UpgradedSocket' => $vendorDir . '/amphp/http-server/src/Driver/UpgradedSocket.php',
    'Amp\\Http\\Server\\ErrorHandler' => $vendorDir . '/amphp/http-server/src/ErrorHandler.php',
    'Amp\\Http\\Server\\ExceptionHandler' => $vendorDir . '/amphp/http-server/src/ExceptionHandler.php',
    'Amp\\Http\\Server\\HttpErrorException' => $vendorDir . '/amphp/http-server/src/HttpErrorException.php',
    'Amp\\Http\\Server\\HttpServer' => $vendorDir . '/amphp/http-server/src/HttpServer.php',
    'Amp\\Http\\Server\\HttpServerStatus' => $vendorDir . '/amphp/http-server/src/HttpServerStatus.php',
    'Amp\\Http\\Server\\Middleware' => $vendorDir . '/amphp/http-server/src/Middleware.php',
    'Amp\\Http\\Server\\Middleware\\AccessLoggerMiddleware' => $vendorDir . '/amphp/http-server/src/Middleware/AccessLoggerMiddleware.php',
    'Amp\\Http\\Server\\Middleware\\AllowedMethodsMiddleware' => $vendorDir . '/amphp/http-server/src/Middleware/AllowedMethodsMiddleware.php',
    'Amp\\Http\\Server\\Middleware\\ClosureMiddleware' => $vendorDir . '/amphp/http-server/src/Middleware/ClosureMiddleware.php',
    'Amp\\Http\\Server\\Middleware\\CompressionMiddleware' => $vendorDir . '/amphp/http-server/src/Middleware/CompressionMiddleware.php',
    'Amp\\Http\\Server\\Middleware\\ConcurrencyLimitingMiddleware' => $vendorDir . '/amphp/http-server/src/Middleware/ConcurrencyLimitingMiddleware.php',
    'Amp\\Http\\Server\\Middleware\\ExceptionHandlerMiddleware' => $vendorDir . '/amphp/http-server/src/Middleware/ExceptionHandlerMiddleware.php',
    'Amp\\Http\\Server\\Middleware\\Forwarded' => $vendorDir . '/amphp/http-server/src/Middleware/Forwarded.php',
    'Amp\\Http\\Server\\Middleware\\ForwardedHeaderType' => $vendorDir . '/amphp/http-server/src/Middleware/ForwardedHeaderType.php',
    'Amp\\Http\\Server\\Middleware\\ForwardedMiddleware' => $vendorDir . '/amphp/http-server/src/Middleware/ForwardedMiddleware.php',
    'Amp\\Http\\Server\\Middleware\\Internal\\MiddlewareRequestHandler' => $vendorDir . '/amphp/http-server/src/Middleware/Internal/MiddlewareRequestHandler.php',
    'Amp\\Http\\Server\\MissingAttributeError' => $vendorDir . '/amphp/http-server/src/MissingAttributeError.php',
    'Amp\\Http\\Server\\Push' => $vendorDir . '/amphp/http-server/src/Push.php',
    'Amp\\Http\\Server\\Request' => $vendorDir . '/amphp/http-server/src/Request.php',
    'Amp\\Http\\Server\\RequestBody' => $vendorDir . '/amphp/http-server/src/RequestBody.php',
    'Amp\\Http\\Server\\RequestHandler' => $vendorDir . '/amphp/http-server/src/RequestHandler.php',
    'Amp\\Http\\Server\\RequestHandler\\ClosureRequestHandler' => $vendorDir . '/amphp/http-server/src/RequestHandler/ClosureRequestHandler.php',
    'Amp\\Http\\Server\\Response' => $vendorDir . '/amphp/http-server/src/Response.php',
    'Amp\\Http\\Server\\SocketHttpServer' => $vendorDir . '/amphp/http-server/src/SocketHttpServer.php',
    'Amp\\Http\\Server\\Trailers' => $vendorDir . '/amphp/http-server/src/Trailers.php',
    'Amp\\Internal\\Cancellable' => $vendorDir . '/amphp/amp/src/Internal/Cancellable.php',
    'Amp\\Internal\\FutureIterator' => $vendorDir . '/amphp/amp/src/Internal/FutureIterator.php',
    'Amp\\Internal\\FutureIteratorQueue' => $vendorDir . '/amphp/amp/src/Internal/FutureIteratorQueue.php',
    'Amp\\Internal\\FutureState' => $vendorDir . '/amphp/amp/src/Internal/FutureState.php',
    'Amp\\Internal\\WrappedCancellation' => $vendorDir . '/amphp/amp/src/Internal/WrappedCancellation.php',
    'Amp\\Interval' => $vendorDir . '/amphp/amp/src/Interval.php',
    'Amp\\Ipc\\IpcServer' => $vendorDir . '/danog/ipc/lib/IpcServer.php',
    'Amp\\Ipc\\IpcServerException' => $vendorDir . '/danog/ipc/lib/IpcServerException.php',
    'Amp\\Ipc\\PendingAcceptError' => $vendorDir . '/danog/ipc/lib/PendingAcceptError.php',
    'Amp\\Ipc\\Sync\\Channel' => $vendorDir . '/danog/ipc/lib/Sync/Channel.php',
    'Amp\\Ipc\\Sync\\ChannelCloseReq' => $vendorDir . '/danog/ipc/lib/Sync/ChannelCloseReq.php',
    'Amp\\Ipc\\Sync\\ChannelException' => $vendorDir . '/danog/ipc/lib/Sync/ChannelException.php',
    'Amp\\Ipc\\Sync\\ChannelInitAck' => $vendorDir . '/danog/ipc/lib/Sync/ChannelInitAck.php',
    'Amp\\Ipc\\Sync\\ChannelParser' => $vendorDir . '/danog/ipc/lib/Sync/ChannelParser.php',
    'Amp\\Ipc\\Sync\\ChannelledSocket' => $vendorDir . '/danog/ipc/lib/Sync/ChannelledSocket.php',
    'Amp\\Ipc\\Sync\\PanicError' => $vendorDir . '/danog/ipc/lib/Sync/PanicError.php',
    'Amp\\Ipc\\Sync\\SerializationException' => $vendorDir . '/danog/ipc/lib/Sync/SerializationException.php',
    'Amp\\Ipc\\Sync\\SynchronizationError' => $vendorDir . '/danog/ipc/lib/Sync/SynchronizationError.php',
    'Amp\\Log\\ConsoleFormatter' => $vendorDir . '/amphp/log/src/ConsoleFormatter.php',
    'Amp\\Log\\StreamHandler' => $vendorDir . '/amphp/log/src/StreamHandler.php',
    'Amp\\Mysql\\Internal\\ConnectionProcessor' => $vendorDir . '/amphp/mysql/src/Internal/ConnectionProcessor.php',
    'Amp\\Mysql\\Internal\\ConnectionState' => $vendorDir . '/amphp/mysql/src/Internal/ConnectionState.php',
    'Amp\\Mysql\\Internal\\MysqlCommandResult' => $vendorDir . '/amphp/mysql/src/Internal/MysqlCommandResult.php',
    'Amp\\Mysql\\Internal\\MysqlConnectionMetadata' => $vendorDir . '/amphp/mysql/src/Internal/MysqlConnectionMetadata.php',
    'Amp\\Mysql\\Internal\\MysqlConnectionResult' => $vendorDir . '/amphp/mysql/src/Internal/MysqlConnectionResult.php',
    'Amp\\Mysql\\Internal\\MysqlConnectionStatement' => $vendorDir . '/amphp/mysql/src/Internal/MysqlConnectionStatement.php',
    'Amp\\Mysql\\Internal\\MysqlConnectionTransaction' => $vendorDir . '/amphp/mysql/src/Internal/MysqlConnectionTransaction.php',
    'Amp\\Mysql\\Internal\\MysqlEncodedValue' => $vendorDir . '/amphp/mysql/src/Internal/MysqlEncodedValue.php',
    'Amp\\Mysql\\Internal\\MysqlNestableExecutor' => $vendorDir . '/amphp/mysql/src/Internal/MysqlNestableExecutor.php',
    'Amp\\Mysql\\Internal\\MysqlNestedTransaction' => $vendorDir . '/amphp/mysql/src/Internal/MysqlNestedTransaction.php',
    'Amp\\Mysql\\Internal\\MysqlPooledResult' => $vendorDir . '/amphp/mysql/src/Internal/MysqlPooledResult.php',
    'Amp\\Mysql\\Internal\\MysqlPooledStatement' => $vendorDir . '/amphp/mysql/src/Internal/MysqlPooledStatement.php',
    'Amp\\Mysql\\Internal\\MysqlPooledTransaction' => $vendorDir . '/amphp/mysql/src/Internal/MysqlPooledTransaction.php',
    'Amp\\Mysql\\Internal\\MysqlResultProxy' => $vendorDir . '/amphp/mysql/src/Internal/MysqlResultProxy.php',
    'Amp\\Mysql\\Internal\\MysqlResultProxyState' => $vendorDir . '/amphp/mysql/src/Internal/MysqlResultProxyState.php',
    'Amp\\Mysql\\Internal\\MysqlStatementCreationDelegate' => $vendorDir . '/amphp/mysql/src/Internal/MysqlStatementCreationDelegate.php',
    'Amp\\Mysql\\Internal\\MysqlStatementPool' => $vendorDir . '/amphp/mysql/src/Internal/MysqlStatementPool.php',
    'Amp\\Mysql\\Internal\\MysqlTransactionDelegate' => $vendorDir . '/amphp/mysql/src/Internal/MysqlTransactionDelegate.php',
    'Amp\\Mysql\\Internal\\PublicKeyCache' => $vendorDir . '/amphp/mysql/src/Internal/PublicKeyCache.php',
    'Amp\\Mysql\\Internal\\SessionStateType' => $vendorDir . '/amphp/mysql/src/Internal/SessionStateType.php',
    'Amp\\Mysql\\MysqlColumnDefinition' => $vendorDir . '/amphp/mysql/src/MysqlColumnDefinition.php',
    'Amp\\Mysql\\MysqlConfig' => $vendorDir . '/amphp/mysql/src/MysqlConfig.php',
    'Amp\\Mysql\\MysqlConnection' => $vendorDir . '/amphp/mysql/src/MysqlConnection.php',
    'Amp\\Mysql\\MysqlConnectionPool' => $vendorDir . '/amphp/mysql/src/MysqlConnectionPool.php',
    'Amp\\Mysql\\MysqlDataType' => $vendorDir . '/amphp/mysql/src/MysqlDataType.php',
    'Amp\\Mysql\\MysqlExecutor' => $vendorDir . '/amphp/mysql/src/MysqlExecutor.php',
    'Amp\\Mysql\\MysqlLink' => $vendorDir . '/amphp/mysql/src/MysqlLink.php',
    'Amp\\Mysql\\MysqlResult' => $vendorDir . '/amphp/mysql/src/MysqlResult.php',
    'Amp\\Mysql\\MysqlStatement' => $vendorDir . '/amphp/mysql/src/MysqlStatement.php',
    'Amp\\Mysql\\MysqlTransaction' => $vendorDir . '/amphp/mysql/src/MysqlTransaction.php',
    'Amp\\Mysql\\SocketMysqlConnection' => $vendorDir . '/amphp/mysql/src/SocketMysqlConnection.php',
    'Amp\\Mysql\\SocketMysqlConnector' => $vendorDir . '/amphp/mysql/src/SocketMysqlConnector.php',
    'Amp\\NullCancellation' => $vendorDir . '/amphp/amp/src/NullCancellation.php',
    'Amp\\Parallel\\Context\\Context' => $vendorDir . '/amphp/parallel/src/Context/Context.php',
    'Amp\\Parallel\\Context\\ContextException' => $vendorDir . '/amphp/parallel/src/Context/ContextException.php',
    'Amp\\Parallel\\Context\\ContextFactory' => $vendorDir . '/amphp/parallel/src/Context/ContextFactory.php',
    'Amp\\Parallel\\Context\\ContextPanicError' => $vendorDir . '/amphp/parallel/src/Context/ContextPanicError.php',
    'Amp\\Parallel\\Context\\DefaultContextFactory' => $vendorDir . '/amphp/parallel/src/Context/DefaultContextFactory.php',
    'Amp\\Parallel\\Context\\ForkContext' => $vendorDir . '/amphp/parallel/src/Context/ForkContext.php',
    'Amp\\Parallel\\Context\\ForkContextFactory' => $vendorDir . '/amphp/parallel/src/Context/ForkContextFactory.php',
    'Amp\\Parallel\\Context\\Internal\\AbstractContext' => $vendorDir . '/amphp/parallel/src/Context/Internal/AbstractContext.php',
    'Amp\\Parallel\\Context\\Internal\\ContextChannel' => $vendorDir . '/amphp/parallel/src/Context/Internal/ContextChannel.php',
    'Amp\\Parallel\\Context\\Internal\\ContextException' => $vendorDir . '/amphp/parallel/src/Context/Internal/ContextException.php',
    'Amp\\Parallel\\Context\\Internal\\ContextMessage' => $vendorDir . '/amphp/parallel/src/Context/Internal/ContextMessage.php',
    'Amp\\Parallel\\Context\\Internal\\ExitFailure' => $vendorDir . '/amphp/parallel/src/Context/Internal/ExitFailure.php',
    'Amp\\Parallel\\Context\\Internal\\ExitResult' => $vendorDir . '/amphp/parallel/src/Context/Internal/ExitResult.php',
    'Amp\\Parallel\\Context\\Internal\\ExitSuccess' => $vendorDir . '/amphp/parallel/src/Context/Internal/ExitSuccess.php',
    'Amp\\Parallel\\Context\\Internal\\ParallelHub' => $vendorDir . '/amphp/parallel/src/Context/Internal/ParallelHub.php',
    'Amp\\Parallel\\Context\\ProcessContext' => $vendorDir . '/amphp/parallel/src/Context/ProcessContext.php',
    'Amp\\Parallel\\Context\\ProcessContextFactory' => $vendorDir . '/amphp/parallel/src/Context/ProcessContextFactory.php',
    'Amp\\Parallel\\Context\\StatusError' => $vendorDir . '/amphp/parallel/src/Context/StatusError.php',
    'Amp\\Parallel\\Context\\ThreadContext' => $vendorDir . '/amphp/parallel/src/Context/ThreadContext.php',
    'Amp\\Parallel\\Context\\ThreadContextFactory' => $vendorDir . '/amphp/parallel/src/Context/ThreadContextFactory.php',
    'Amp\\Parallel\\Ipc\\IpcHub' => $vendorDir . '/amphp/parallel/src/Ipc/IpcHub.php',
    'Amp\\Parallel\\Ipc\\LocalIpcHub' => $vendorDir . '/amphp/parallel/src/Ipc/LocalIpcHub.php',
    'Amp\\Parallel\\Ipc\\SocketIpcHub' => $vendorDir . '/amphp/parallel/src/Ipc/SocketIpcHub.php',
    'Amp\\Parallel\\Worker\\ContextWorkerFactory' => $vendorDir . '/amphp/parallel/src/Worker/ContextWorkerFactory.php',
    'Amp\\Parallel\\Worker\\ContextWorkerPool' => $vendorDir . '/amphp/parallel/src/Worker/ContextWorkerPool.php',
    'Amp\\Parallel\\Worker\\DelegatingWorkerPool' => $vendorDir . '/amphp/parallel/src/Worker/DelegatingWorkerPool.php',
    'Amp\\Parallel\\Worker\\Execution' => $vendorDir . '/amphp/parallel/src/Worker/Execution.php',
    'Amp\\Parallel\\Worker\\Internal\\ContextWorker' => $vendorDir . '/amphp/parallel/src/Worker/Internal/ContextWorker.php',
    'Amp\\Parallel\\Worker\\Internal\\JobCancellation' => $vendorDir . '/amphp/parallel/src/Worker/Internal/JobCancellation.php',
    'Amp\\Parallel\\Worker\\Internal\\JobChannel' => $vendorDir . '/amphp/parallel/src/Worker/Internal/JobChannel.php',
    'Amp\\Parallel\\Worker\\Internal\\JobMessage' => $vendorDir . '/amphp/parallel/src/Worker/Internal/JobMessage.php',
    'Amp\\Parallel\\Worker\\Internal\\JobPacket' => $vendorDir . '/amphp/parallel/src/Worker/Internal/JobPacket.php',
    'Amp\\Parallel\\Worker\\Internal\\PooledWorker' => $vendorDir . '/amphp/parallel/src/Worker/Internal/PooledWorker.php',
    'Amp\\Parallel\\Worker\\Internal\\TaskCancelled' => $vendorDir . '/amphp/parallel/src/Worker/Internal/TaskCancelled.php',
    'Amp\\Parallel\\Worker\\Internal\\TaskExceptionType' => $vendorDir . '/amphp/parallel/src/Worker/Internal/TaskExceptionType.php',
    'Amp\\Parallel\\Worker\\Internal\\TaskFailure' => $vendorDir . '/amphp/parallel/src/Worker/Internal/TaskFailure.php',
    'Amp\\Parallel\\Worker\\Internal\\TaskResult' => $vendorDir . '/amphp/parallel/src/Worker/Internal/TaskResult.php',
    'Amp\\Parallel\\Worker\\Internal\\TaskSubmission' => $vendorDir . '/amphp/parallel/src/Worker/Internal/TaskSubmission.php',
    'Amp\\Parallel\\Worker\\Internal\\TaskSuccess' => $vendorDir . '/amphp/parallel/src/Worker/Internal/TaskSuccess.php',
    'Amp\\Parallel\\Worker\\LimitedWorkerPool' => $vendorDir . '/amphp/parallel/src/Worker/LimitedWorkerPool.php',
    'Amp\\Parallel\\Worker\\Task' => $vendorDir . '/amphp/parallel/src/Worker/Task.php',
    'Amp\\Parallel\\Worker\\TaskCancelledException' => $vendorDir . '/amphp/parallel/src/Worker/TaskCancelledException.php',
    'Amp\\Parallel\\Worker\\TaskFailureError' => $vendorDir . '/amphp/parallel/src/Worker/TaskFailureError.php',
    'Amp\\Parallel\\Worker\\TaskFailureException' => $vendorDir . '/amphp/parallel/src/Worker/TaskFailureException.php',
    'Amp\\Parallel\\Worker\\TaskFailureThrowable' => $vendorDir . '/amphp/parallel/src/Worker/TaskFailureThrowable.php',
    'Amp\\Parallel\\Worker\\Worker' => $vendorDir . '/amphp/parallel/src/Worker/Worker.php',
    'Amp\\Parallel\\Worker\\WorkerException' => $vendorDir . '/amphp/parallel/src/Worker/WorkerException.php',
    'Amp\\Parallel\\Worker\\WorkerFactory' => $vendorDir . '/amphp/parallel/src/Worker/WorkerFactory.php',
    'Amp\\Parallel\\Worker\\WorkerPool' => $vendorDir . '/amphp/parallel/src/Worker/WorkerPool.php',
    'Amp\\Parser\\InvalidDelimiterError' => $vendorDir . '/amphp/parser/src/InvalidDelimiterError.php',
    'Amp\\Parser\\Parser' => $vendorDir . '/amphp/parser/src/Parser.php',
    'Amp\\Pipeline\\ConcurrentIterator' => $vendorDir . '/amphp/pipeline/src/ConcurrentIterator.php',
    'Amp\\Pipeline\\DisposedException' => $vendorDir . '/amphp/pipeline/src/DisposedException.php',
    'Amp\\Pipeline\\Internal\\ConcurrentArrayIterator' => $vendorDir . '/amphp/pipeline/src/Internal/ConcurrentArrayIterator.php',
    'Amp\\Pipeline\\Internal\\ConcurrentChainedIterator' => $vendorDir . '/amphp/pipeline/src/Internal/ConcurrentChainedIterator.php',
    'Amp\\Pipeline\\Internal\\ConcurrentClosureIterator' => $vendorDir . '/amphp/pipeline/src/Internal/ConcurrentClosureIterator.php',
    'Amp\\Pipeline\\Internal\\ConcurrentFlatMapIterator' => $vendorDir . '/amphp/pipeline/src/Internal/ConcurrentFlatMapIterator.php',
    'Amp\\Pipeline\\Internal\\ConcurrentIterableIterator' => $vendorDir . '/amphp/pipeline/src/Internal/ConcurrentIterableIterator.php',
    'Amp\\Pipeline\\Internal\\ConcurrentMergedIterator' => $vendorDir . '/amphp/pipeline/src/Internal/ConcurrentMergedIterator.php',
    'Amp\\Pipeline\\Internal\\ConcurrentQueueIterator' => $vendorDir . '/amphp/pipeline/src/Internal/ConcurrentQueueIterator.php',
    'Amp\\Pipeline\\Internal\\FlatMapOperation' => $vendorDir . '/amphp/pipeline/src/Internal/FlatMapOperation.php',
    'Amp\\Pipeline\\Internal\\IntermediateOperation' => $vendorDir . '/amphp/pipeline/src/Internal/IntermediateOperation.php',
    'Amp\\Pipeline\\Internal\\QueueState' => $vendorDir . '/amphp/pipeline/src/Internal/QueueState.php',
    'Amp\\Pipeline\\Internal\\Sequence' => $vendorDir . '/amphp/pipeline/src/Internal/Sequence.php',
    'Amp\\Pipeline\\Internal\\SortOperation' => $vendorDir . '/amphp/pipeline/src/Internal/SortOperation.php',
    'Amp\\Pipeline\\Pipeline' => $vendorDir . '/amphp/pipeline/src/Pipeline.php',
    'Amp\\Pipeline\\Queue' => $vendorDir . '/amphp/pipeline/src/Queue.php',
    'Amp\\Postgres\\DefaultPostgresConnector' => $vendorDir . '/amphp/postgres/src/DefaultPostgresConnector.php',
    'Amp\\Postgres\\Internal\\AbstractHandle' => $vendorDir . '/amphp/postgres/src/Internal/AbstractHandle.php',
    'Amp\\Postgres\\Internal\\ArrayParser' => $vendorDir . '/amphp/postgres/src/Internal/ArrayParser.php',
    'Amp\\Postgres\\Internal\\PgSqlHandle' => $vendorDir . '/amphp/postgres/src/Internal/PgSqlHandle.php',
    'Amp\\Postgres\\Internal\\PgSqlResultIterator' => $vendorDir . '/amphp/postgres/src/Internal/PgSqlResultIterator.php',
    'Amp\\Postgres\\Internal\\PgSqlResultSet' => $vendorDir . '/amphp/postgres/src/Internal/PgSqlResultSet.php',
    'Amp\\Postgres\\Internal\\PgSqlType' => $vendorDir . '/amphp/postgres/src/Internal/PgSqlType.php',
    'Amp\\Postgres\\Internal\\PostgresCommandResult' => $vendorDir . '/amphp/postgres/src/Internal/PostgresCommandResult.php',
    'Amp\\Postgres\\Internal\\PostgresConnectionListener' => $vendorDir . '/amphp/postgres/src/Internal/PostgresConnectionListener.php',
    'Amp\\Postgres\\Internal\\PostgresConnectionStatement' => $vendorDir . '/amphp/postgres/src/Internal/PostgresConnectionStatement.php',
    'Amp\\Postgres\\Internal\\PostgresConnectionTransaction' => $vendorDir . '/amphp/postgres/src/Internal/PostgresConnectionTransaction.php',
    'Amp\\Postgres\\Internal\\PostgresHandle' => $vendorDir . '/amphp/postgres/src/Internal/PostgresHandle.php',
    'Amp\\Postgres\\Internal\\PostgresHandleConnection' => $vendorDir . '/amphp/postgres/src/Internal/PostgresHandleConnection.php',
    'Amp\\Postgres\\Internal\\PostgresNestedTransaction' => $vendorDir . '/amphp/postgres/src/Internal/PostgresNestedTransaction.php',
    'Amp\\Postgres\\Internal\\PostgresPooledListener' => $vendorDir . '/amphp/postgres/src/Internal/PostgresPooledListener.php',
    'Amp\\Postgres\\Internal\\PostgresPooledResult' => $vendorDir . '/amphp/postgres/src/Internal/PostgresPooledResult.php',
    'Amp\\Postgres\\Internal\\PostgresPooledStatement' => $vendorDir . '/amphp/postgres/src/Internal/PostgresPooledStatement.php',
    'Amp\\Postgres\\Internal\\PostgresPooledTransaction' => $vendorDir . '/amphp/postgres/src/Internal/PostgresPooledTransaction.php',
    'Amp\\Postgres\\Internal\\PostgresStatementPool' => $vendorDir . '/amphp/postgres/src/Internal/PostgresStatementPool.php',
    'Amp\\Postgres\\Internal\\PostgresTransactionDelegate' => $vendorDir . '/amphp/postgres/src/Internal/PostgresTransactionDelegate.php',
    'Amp\\Postgres\\Internal\\PqBufferedResultSet' => $vendorDir . '/amphp/postgres/src/Internal/PqBufferedResultSet.php',
    'Amp\\Postgres\\Internal\\PqHandle' => $vendorDir . '/amphp/postgres/src/Internal/PqHandle.php',
    'Amp\\Postgres\\Internal\\PqUnbufferedResultSet' => $vendorDir . '/amphp/postgres/src/Internal/PqUnbufferedResultSet.php',
    'Amp\\Postgres\\Internal\\StatementStorage' => $vendorDir . '/amphp/postgres/src/Internal/StatementStorage.php',
    'Amp\\Postgres\\PgSqlConnection' => $vendorDir . '/amphp/postgres/src/PgSqlConnection.php',
    'Amp\\Postgres\\PostgresArray' => $vendorDir . '/amphp/postgres/src/PostgresArray.php',
    'Amp\\Postgres\\PostgresByteA' => $vendorDir . '/amphp/postgres/src/PostgresByteA.php',
    'Amp\\Postgres\\PostgresConfig' => $vendorDir . '/amphp/postgres/src/PostgresConfig.php',
    'Amp\\Postgres\\PostgresConnection' => $vendorDir . '/amphp/postgres/src/PostgresConnection.php',
    'Amp\\Postgres\\PostgresConnectionPool' => $vendorDir . '/amphp/postgres/src/PostgresConnectionPool.php',
    'Amp\\Postgres\\PostgresExecutor' => $vendorDir . '/amphp/postgres/src/PostgresExecutor.php',
    'Amp\\Postgres\\PostgresLink' => $vendorDir . '/amphp/postgres/src/PostgresLink.php',
    'Amp\\Postgres\\PostgresListener' => $vendorDir . '/amphp/postgres/src/PostgresListener.php',
    'Amp\\Postgres\\PostgresNotification' => $vendorDir . '/amphp/postgres/src/PostgresNotification.php',
    'Amp\\Postgres\\PostgresParseException' => $vendorDir . '/amphp/postgres/src/PostgresParseException.php',
    'Amp\\Postgres\\PostgresQueryError' => $vendorDir . '/amphp/postgres/src/PostgresQueryError.php',
    'Amp\\Postgres\\PostgresResult' => $vendorDir . '/amphp/postgres/src/PostgresResult.php',
    'Amp\\Postgres\\PostgresStatement' => $vendorDir . '/amphp/postgres/src/PostgresStatement.php',
    'Amp\\Postgres\\PostgresTransaction' => $vendorDir . '/amphp/postgres/src/PostgresTransaction.php',
    'Amp\\Postgres\\PqConnection' => $vendorDir . '/amphp/postgres/src/PqConnection.php',
    'Amp\\Process\\Internal\\Posix\\PosixHandle' => $vendorDir . '/amphp/process/src/Internal/Posix/PosixHandle.php',
    'Amp\\Process\\Internal\\Posix\\PosixRunner' => $vendorDir . '/amphp/process/src/Internal/Posix/PosixRunner.php',
    'Amp\\Process\\Internal\\ProcHolder' => $vendorDir . '/amphp/process/src/Internal/ProcHolder.php',
    'Amp\\Process\\Internal\\ProcessContext' => $vendorDir . '/amphp/process/src/Internal/ProcessContext.php',
    'Amp\\Process\\Internal\\ProcessHandle' => $vendorDir . '/amphp/process/src/Internal/ProcessHandle.php',
    'Amp\\Process\\Internal\\ProcessRunner' => $vendorDir . '/amphp/process/src/Internal/ProcessRunner.php',
    'Amp\\Process\\Internal\\ProcessStatus' => $vendorDir . '/amphp/process/src/Internal/ProcessStatus.php',
    'Amp\\Process\\Internal\\ProcessStreams' => $vendorDir . '/amphp/process/src/Internal/ProcessStreams.php',
    'Amp\\Process\\Internal\\Windows\\HandshakeException' => $vendorDir . '/amphp/process/src/Internal/Windows/HandshakeException.php',
    'Amp\\Process\\Internal\\Windows\\HandshakeStatus' => $vendorDir . '/amphp/process/src/Internal/Windows/HandshakeStatus.php',
    'Amp\\Process\\Internal\\Windows\\SignalCode' => $vendorDir . '/amphp/process/src/Internal/Windows/SignalCode.php',
    'Amp\\Process\\Internal\\Windows\\SocketConnector' => $vendorDir . '/amphp/process/src/Internal/Windows/SocketConnector.php',
    'Amp\\Process\\Internal\\Windows\\WindowsHandle' => $vendorDir . '/amphp/process/src/Internal/Windows/WindowsHandle.php',
    'Amp\\Process\\Internal\\Windows\\WindowsRunner' => $vendorDir . '/amphp/process/src/Internal/Windows/WindowsRunner.php',
    'Amp\\Process\\Process' => $vendorDir . '/amphp/process/src/Process.php',
    'Amp\\Process\\ProcessException' => $vendorDir . '/amphp/process/src/ProcessException.php',
    'Amp\\Redis\\Command\\Boundary\\LexBoundary' => $vendorDir . '/amphp/redis/src/Command/Boundary/LexBoundary.php',
    'Amp\\Redis\\Command\\Boundary\\ScoreBoundary' => $vendorDir . '/amphp/redis/src/Command/Boundary/ScoreBoundary.php',
    'Amp\\Redis\\Command\\Option\\RangeOptions' => $vendorDir . '/amphp/redis/src/Command/Option/RangeOptions.php',
    'Amp\\Redis\\Command\\Option\\SetOptions' => $vendorDir . '/amphp/redis/src/Command/Option/SetOptions.php',
    'Amp\\Redis\\Command\\Option\\SortOptions' => $vendorDir . '/amphp/redis/src/Command/Option/SortOptions.php',
    'Amp\\Redis\\Command\\RedisHyperLogLog' => $vendorDir . '/amphp/redis/src/Command/RedisHyperLogLog.php',
    'Amp\\Redis\\Command\\RedisList' => $vendorDir . '/amphp/redis/src/Command/RedisList.php',
    'Amp\\Redis\\Command\\RedisMap' => $vendorDir . '/amphp/redis/src/Command/RedisMap.php',
    'Amp\\Redis\\Command\\RedisSet' => $vendorDir . '/amphp/redis/src/Command/RedisSet.php',
    'Amp\\Redis\\Command\\RedisSortedSet' => $vendorDir . '/amphp/redis/src/Command/RedisSortedSet.php',
    'Amp\\Redis\\Connection\\Authenticator' => $vendorDir . '/amphp/redis/src/Connection/Authenticator.php',
    'Amp\\Redis\\Connection\\DatabaseSelector' => $vendorDir . '/amphp/redis/src/Connection/DatabaseSelector.php',
    'Amp\\Redis\\Connection\\ReconnectingRedisLink' => $vendorDir . '/amphp/redis/src/Connection/ReconnectingRedisLink.php',
    'Amp\\Redis\\Connection\\RedisConnection' => $vendorDir . '/amphp/redis/src/Connection/RedisConnection.php',
    'Amp\\Redis\\Connection\\RedisConnectionException' => $vendorDir . '/amphp/redis/src/Connection/RedisConnectionException.php',
    'Amp\\Redis\\Connection\\RedisConnector' => $vendorDir . '/amphp/redis/src/Connection/RedisConnector.php',
    'Amp\\Redis\\Connection\\RedisLink' => $vendorDir . '/amphp/redis/src/Connection/RedisLink.php',
    'Amp\\Redis\\Connection\\SocketRedisConnection' => $vendorDir . '/amphp/redis/src/Connection/SocketRedisConnection.php',
    'Amp\\Redis\\Connection\\SocketRedisConnector' => $vendorDir . '/amphp/redis/src/Connection/SocketRedisConnector.php',
    'Amp\\Redis\\Protocol\\ProtocolException' => $vendorDir . '/amphp/redis/src/Protocol/ProtocolException.php',
    'Amp\\Redis\\Protocol\\QueryException' => $vendorDir . '/amphp/redis/src/Protocol/QueryException.php',
    'Amp\\Redis\\Protocol\\RedisError' => $vendorDir . '/amphp/redis/src/Protocol/RedisError.php',
    'Amp\\Redis\\Protocol\\RedisResponse' => $vendorDir . '/amphp/redis/src/Protocol/RedisResponse.php',
    'Amp\\Redis\\Protocol\\RedisValue' => $vendorDir . '/amphp/redis/src/Protocol/RedisValue.php',
    'Amp\\Redis\\Protocol\\RespParser' => $vendorDir . '/amphp/redis/src/Protocol/RespParser.php',
    'Amp\\Redis\\RedisCache' => $vendorDir . '/amphp/redis/src/RedisCache.php',
    'Amp\\Redis\\RedisClient' => $vendorDir . '/amphp/redis/src/RedisClient.php',
    'Amp\\Redis\\RedisConfig' => $vendorDir . '/amphp/redis/src/RedisConfig.php',
    'Amp\\Redis\\RedisException' => $vendorDir . '/amphp/redis/src/RedisException.php',
    'Amp\\Redis\\RedisSubscriber' => $vendorDir . '/amphp/redis/src/RedisSubscriber.php',
    'Amp\\Redis\\RedisSubscription' => $vendorDir . '/amphp/redis/src/RedisSubscription.php',
    'Amp\\Redis\\Sync\\RedisMutex' => $vendorDir . '/amphp/redis/src/Sync/RedisMutex.php',
    'Amp\\Redis\\Sync\\RedisMutexException' => $vendorDir . '/amphp/redis/src/Sync/RedisMutexException.php',
    'Amp\\Redis\\Sync\\RedisMutexOptions' => $vendorDir . '/amphp/redis/src/Sync/RedisMutexOptions.php',
    'Amp\\Redis\\Sync\\RedisParcel' => $vendorDir . '/amphp/redis/src/Sync/RedisParcel.php',
    'Amp\\Serialization\\CompressingSerializer' => $vendorDir . '/amphp/serialization/src/CompressingSerializer.php',
    'Amp\\Serialization\\JsonSerializer' => $vendorDir . '/amphp/serialization/src/JsonSerializer.php',
    'Amp\\Serialization\\NativeSerializer' => $vendorDir . '/amphp/serialization/src/NativeSerializer.php',
    'Amp\\Serialization\\PassthroughSerializer' => $vendorDir . '/amphp/serialization/src/PassthroughSerializer.php',
    'Amp\\Serialization\\SerializationException' => $vendorDir . '/amphp/serialization/src/SerializationException.php',
    'Amp\\Serialization\\Serializer' => $vendorDir . '/amphp/serialization/src/Serializer.php',
    'Amp\\SignalCancellation' => $vendorDir . '/amphp/amp/src/SignalCancellation.php',
    'Amp\\SignalException' => $vendorDir . '/amphp/amp/src/SignalException.php',
    'Amp\\Socket\\BindContext' => $vendorDir . '/amphp/socket/src/BindContext.php',
    'Amp\\Socket\\Certificate' => $vendorDir . '/amphp/socket/src/Certificate.php',
    'Amp\\Socket\\CidrMatcher' => $vendorDir . '/amphp/socket/src/CidrMatcher.php',
    'Amp\\Socket\\ClientTlsContext' => $vendorDir . '/amphp/socket/src/ClientTlsContext.php',
    'Amp\\Socket\\ConnectContext' => $vendorDir . '/amphp/socket/src/ConnectContext.php',
    'Amp\\Socket\\ConnectException' => $vendorDir . '/amphp/socket/src/ConnectException.php',
    'Amp\\Socket\\DnsSocketConnector' => $vendorDir . '/amphp/socket/src/DnsSocketConnector.php',
    'Amp\\Socket\\InternetAddress' => $vendorDir . '/amphp/socket/src/InternetAddress.php',
    'Amp\\Socket\\InternetAddressVersion' => $vendorDir . '/amphp/socket/src/InternetAddressVersion.php',
    'Amp\\Socket\\PendingAcceptError' => $vendorDir . '/amphp/socket/src/PendingAcceptError.php',
    'Amp\\Socket\\PendingReceiveError' => $vendorDir . '/amphp/socket/src/PendingReceiveError.php',
    'Amp\\Socket\\ResourceServerSocket' => $vendorDir . '/amphp/socket/src/ResourceServerSocket.php',
    'Amp\\Socket\\ResourceServerSocketFactory' => $vendorDir . '/amphp/socket/src/ResourceServerSocketFactory.php',
    'Amp\\Socket\\ResourceSocket' => $vendorDir . '/amphp/socket/src/ResourceSocket.php',
    'Amp\\Socket\\ResourceUdpSocket' => $vendorDir . '/amphp/socket/src/ResourceUdpSocket.php',
    'Amp\\Socket\\RetrySocketConnector' => $vendorDir . '/amphp/socket/src/RetrySocketConnector.php',
    'Amp\\Socket\\ServerSocket' => $vendorDir . '/amphp/socket/src/ServerSocket.php',
    'Amp\\Socket\\ServerSocketFactory' => $vendorDir . '/amphp/socket/src/ServerSocketFactory.php',
    'Amp\\Socket\\ServerTlsContext' => $vendorDir . '/amphp/socket/src/ServerTlsContext.php',
    'Amp\\Socket\\Socket' => $vendorDir . '/amphp/socket/src/Socket.php',
    'Amp\\Socket\\SocketAddress' => $vendorDir . '/amphp/socket/src/SocketAddress.php',
    'Amp\\Socket\\SocketAddressType' => $vendorDir . '/amphp/socket/src/SocketAddressType.php',
    'Amp\\Socket\\SocketConnector' => $vendorDir . '/amphp/socket/src/SocketConnector.php',
    'Amp\\Socket\\SocketException' => $vendorDir . '/amphp/socket/src/SocketException.php',
    'Amp\\Socket\\SocketPool' => $vendorDir . '/amphp/socket/src/SocketPool.php',
    'Amp\\Socket\\Socks5SocketConnector' => $vendorDir . '/amphp/socket/src/Socks5SocketConnector.php',
    'Amp\\Socket\\StaticSocketConnector' => $vendorDir . '/amphp/socket/src/StaticSocketConnector.php',
    'Amp\\Socket\\TlsException' => $vendorDir . '/amphp/socket/src/TlsException.php',
    'Amp\\Socket\\TlsInfo' => $vendorDir . '/amphp/socket/src/TlsInfo.php',
    'Amp\\Socket\\TlsState' => $vendorDir . '/amphp/socket/src/TlsState.php',
    'Amp\\Socket\\UdpSocket' => $vendorDir . '/amphp/socket/src/UdpSocket.php',
    'Amp\\Socket\\UnixAddress' => $vendorDir . '/amphp/socket/src/UnixAddress.php',
    'Amp\\Socket\\UnlimitedSocketPool' => $vendorDir . '/amphp/socket/src/UnlimitedSocketPool.php',
    'Amp\\Sql\\Common\\RetrySqlConnector' => $vendorDir . '/amphp/sql-common/src/RetrySqlConnector.php',
    'Amp\\Sql\\Common\\SqlCommandResult' => $vendorDir . '/amphp/sql-common/src/SqlCommandResult.php',
    'Amp\\Sql\\Common\\SqlCommonConnectionPool' => $vendorDir . '/amphp/sql-common/src/SqlCommonConnectionPool.php',
    'Amp\\Sql\\Common\\SqlConnectionTransaction' => $vendorDir . '/amphp/sql-common/src/SqlConnectionTransaction.php',
    'Amp\\Sql\\Common\\SqlNestableTransactionExecutor' => $vendorDir . '/amphp/sql-common/src/SqlNestableTransactionExecutor.php',
    'Amp\\Sql\\Common\\SqlNestedTransaction' => $vendorDir . '/amphp/sql-common/src/SqlNestedTransaction.php',
    'Amp\\Sql\\Common\\SqlPooledResult' => $vendorDir . '/amphp/sql-common/src/SqlPooledResult.php',
    'Amp\\Sql\\Common\\SqlPooledStatement' => $vendorDir . '/amphp/sql-common/src/SqlPooledStatement.php',
    'Amp\\Sql\\Common\\SqlPooledTransaction' => $vendorDir . '/amphp/sql-common/src/SqlPooledTransaction.php',
    'Amp\\Sql\\Common\\SqlStatementPool' => $vendorDir . '/amphp/sql-common/src/SqlStatementPool.php',
    'Amp\\Sql\\SqlConfig' => $vendorDir . '/amphp/sql/src/SqlConfig.php',
    'Amp\\Sql\\SqlConnection' => $vendorDir . '/amphp/sql/src/SqlConnection.php',
    'Amp\\Sql\\SqlConnectionException' => $vendorDir . '/amphp/sql/src/SqlConnectionException.php',
    'Amp\\Sql\\SqlConnectionPool' => $vendorDir . '/amphp/sql/src/SqlConnectionPool.php',
    'Amp\\Sql\\SqlConnector' => $vendorDir . '/amphp/sql/src/SqlConnector.php',
    'Amp\\Sql\\SqlException' => $vendorDir . '/amphp/sql/src/SqlException.php',
    'Amp\\Sql\\SqlExecutor' => $vendorDir . '/amphp/sql/src/SqlExecutor.php',
    'Amp\\Sql\\SqlLink' => $vendorDir . '/amphp/sql/src/SqlLink.php',
    'Amp\\Sql\\SqlQueryError' => $vendorDir . '/amphp/sql/src/SqlQueryError.php',
    'Amp\\Sql\\SqlResult' => $vendorDir . '/amphp/sql/src/SqlResult.php',
    'Amp\\Sql\\SqlStatement' => $vendorDir . '/amphp/sql/src/SqlStatement.php',
    'Amp\\Sql\\SqlTransaction' => $vendorDir . '/amphp/sql/src/SqlTransaction.php',
    'Amp\\Sql\\SqlTransactionError' => $vendorDir . '/amphp/sql/src/SqlTransactionError.php',
    'Amp\\Sql\\SqlTransactionIsolation' => $vendorDir . '/amphp/sql/src/SqlTransactionIsolation.php',
    'Amp\\Sql\\SqlTransactionIsolationLevel' => $vendorDir . '/amphp/sql/src/SqlTransactionIsolationLevel.php',
    'Amp\\Sql\\SqlTransientResource' => $vendorDir . '/amphp/sql/src/SqlTransientResource.php',
    'Amp\\Sync\\Barrier' => $vendorDir . '/amphp/sync/src/Barrier.php',
    'Amp\\Sync\\Channel' => $vendorDir . '/amphp/sync/src/Channel.php',
    'Amp\\Sync\\ChannelException' => $vendorDir . '/amphp/sync/src/ChannelException.php',
    'Amp\\Sync\\Internal\\ConcurrentIteratorChannel' => $vendorDir . '/amphp/sync/src/Internal/ConcurrentIteratorChannel.php',
    'Amp\\Sync\\KeyedMutex' => $vendorDir . '/amphp/sync/src/KeyedMutex.php',
    'Amp\\Sync\\KeyedSemaphore' => $vendorDir . '/amphp/sync/src/KeyedSemaphore.php',
    'Amp\\Sync\\LocalKeyedMutex' => $vendorDir . '/amphp/sync/src/LocalKeyedMutex.php',
    'Amp\\Sync\\LocalKeyedSemaphore' => $vendorDir . '/amphp/sync/src/LocalKeyedSemaphore.php',
    'Amp\\Sync\\LocalMutex' => $vendorDir . '/amphp/sync/src/LocalMutex.php',
    'Amp\\Sync\\LocalParcel' => $vendorDir . '/amphp/sync/src/LocalParcel.php',
    'Amp\\Sync\\LocalSemaphore' => $vendorDir . '/amphp/sync/src/LocalSemaphore.php',
    'Amp\\Sync\\Lock' => $vendorDir . '/amphp/sync/src/Lock.php',
    'Amp\\Sync\\Mutex' => $vendorDir . '/amphp/sync/src/Mutex.php',
    'Amp\\Sync\\Parcel' => $vendorDir . '/amphp/sync/src/Parcel.php',
    'Amp\\Sync\\ParcelException' => $vendorDir . '/amphp/sync/src/ParcelException.php',
    'Amp\\Sync\\PosixSemaphore' => $vendorDir . '/amphp/sync/src/PosixSemaphore.php',
    'Amp\\Sync\\PrefixedKeyedMutex' => $vendorDir . '/amphp/sync/src/PrefixedKeyedMutex.php',
    'Amp\\Sync\\PrefixedKeyedSemaphore' => $vendorDir . '/amphp/sync/src/PrefixedKeyedSemaphore.php',
    'Amp\\Sync\\PriorityQueue' => $vendorDir . '/amphp/sync/src/PriorityQueue.php',
    'Amp\\Sync\\RateLimitingSemaphore' => $vendorDir . '/amphp/sync/src/RateLimitingSemaphore.php',
    'Amp\\Sync\\Semaphore' => $vendorDir . '/amphp/sync/src/Semaphore.php',
    'Amp\\Sync\\SemaphoreMutex' => $vendorDir . '/amphp/sync/src/SemaphoreMutex.php',
    'Amp\\Sync\\SharedMemoryParcel' => $vendorDir . '/amphp/sync/src/SharedMemoryParcel.php',
    'Amp\\Sync\\StaticKeyMutex' => $vendorDir . '/amphp/sync/src/StaticKeyMutex.php',
    'Amp\\Sync\\StaticKeySemaphore' => $vendorDir . '/amphp/sync/src/StaticKeySemaphore.php',
    'Amp\\Sync\\SyncException' => $vendorDir . '/amphp/sync/src/SyncException.php',
    'Amp\\TimeoutCancellation' => $vendorDir . '/amphp/amp/src/TimeoutCancellation.php',
    'Amp\\TimeoutException' => $vendorDir . '/amphp/amp/src/TimeoutException.php',
    'Amp\\Websocket\\Client\\Rfc6455Connection' => $vendorDir . '/amphp/websocket-client/src/Rfc6455Connection.php',
    'Amp\\Websocket\\Client\\Rfc6455ConnectionFactory' => $vendorDir . '/amphp/websocket-client/src/Rfc6455ConnectionFactory.php',
    'Amp\\Websocket\\Client\\Rfc6455Connector' => $vendorDir . '/amphp/websocket-client/src/Rfc6455Connector.php',
    'Amp\\Websocket\\Client\\WebsocketConnectException' => $vendorDir . '/amphp/websocket-client/src/WebsocketConnectException.php',
    'Amp\\Websocket\\Client\\WebsocketConnection' => $vendorDir . '/amphp/websocket-client/src/WebsocketConnection.php',
    'Amp\\Websocket\\Client\\WebsocketConnectionFactory' => $vendorDir . '/amphp/websocket-client/src/WebsocketConnectionFactory.php',
    'Amp\\Websocket\\Client\\WebsocketConnector' => $vendorDir . '/amphp/websocket-client/src/WebsocketConnector.php',
    'Amp\\Websocket\\Client\\WebsocketHandshake' => $vendorDir . '/amphp/websocket-client/src/WebsocketHandshake.php',
    'Amp\\Websocket\\Compression\\Rfc7692Compression' => $vendorDir . '/amphp/websocket/src/Compression/Rfc7692Compression.php',
    'Amp\\Websocket\\Compression\\Rfc7692CompressionFactory' => $vendorDir . '/amphp/websocket/src/Compression/Rfc7692CompressionFactory.php',
    'Amp\\Websocket\\Compression\\WebsocketCompressionContext' => $vendorDir . '/amphp/websocket/src/Compression/WebsocketCompressionContext.php',
    'Amp\\Websocket\\Compression\\WebsocketCompressionContextFactory' => $vendorDir . '/amphp/websocket/src/Compression/WebsocketCompressionContextFactory.php',
    'Amp\\Websocket\\ConstantRateLimit' => $vendorDir . '/amphp/websocket/src/ConstantRateLimit.php',
    'Amp\\Websocket\\Internal\\Rfc6455FrameHandler' => $vendorDir . '/amphp/websocket/src/Internal/Rfc6455FrameHandler.php',
    'Amp\\Websocket\\Internal\\WebsocketClientMetadata' => $vendorDir . '/amphp/websocket/src/Internal/WebsocketClientMetadata.php',
    'Amp\\Websocket\\Parser\\Rfc6455FrameCompiler' => $vendorDir . '/amphp/websocket/src/Parser/Rfc6455FrameCompiler.php',
    'Amp\\Websocket\\Parser\\Rfc6455FrameCompilerFactory' => $vendorDir . '/amphp/websocket/src/Parser/Rfc6455FrameCompilerFactory.php',
    'Amp\\Websocket\\Parser\\Rfc6455Parser' => $vendorDir . '/amphp/websocket/src/Parser/Rfc6455Parser.php',
    'Amp\\Websocket\\Parser\\Rfc6455ParserFactory' => $vendorDir . '/amphp/websocket/src/Parser/Rfc6455ParserFactory.php',
    'Amp\\Websocket\\Parser\\WebsocketFrameCompiler' => $vendorDir . '/amphp/websocket/src/Parser/WebsocketFrameCompiler.php',
    'Amp\\Websocket\\Parser\\WebsocketFrameCompilerFactory' => $vendorDir . '/amphp/websocket/src/Parser/WebsocketFrameCompilerFactory.php',
    'Amp\\Websocket\\Parser\\WebsocketFrameHandler' => $vendorDir . '/amphp/websocket/src/Parser/WebsocketFrameHandler.php',
    'Amp\\Websocket\\Parser\\WebsocketFrameType' => $vendorDir . '/amphp/websocket/src/Parser/WebsocketFrameType.php',
    'Amp\\Websocket\\Parser\\WebsocketParser' => $vendorDir . '/amphp/websocket/src/Parser/WebsocketParser.php',
    'Amp\\Websocket\\Parser\\WebsocketParserException' => $vendorDir . '/amphp/websocket/src/Parser/WebsocketParserException.php',
    'Amp\\Websocket\\Parser\\WebsocketParserFactory' => $vendorDir . '/amphp/websocket/src/Parser/WebsocketParserFactory.php',
    'Amp\\Websocket\\PeriodicHeartbeatQueue' => $vendorDir . '/amphp/websocket/src/PeriodicHeartbeatQueue.php',
    'Amp\\Websocket\\Rfc6455Client' => $vendorDir . '/amphp/websocket/src/Rfc6455Client.php',
    'Amp\\Websocket\\WebsocketClient' => $vendorDir . '/amphp/websocket/src/WebsocketClient.php',
    'Amp\\Websocket\\WebsocketCloseCode' => $vendorDir . '/amphp/websocket/src/WebsocketCloseCode.php',
    'Amp\\Websocket\\WebsocketCloseInfo' => $vendorDir . '/amphp/websocket/src/WebsocketCloseInfo.php',
    'Amp\\Websocket\\WebsocketClosedException' => $vendorDir . '/amphp/websocket/src/WebsocketClosedException.php',
    'Amp\\Websocket\\WebsocketCount' => $vendorDir . '/amphp/websocket/src/WebsocketCount.php',
    'Amp\\Websocket\\WebsocketException' => $vendorDir . '/amphp/websocket/src/WebsocketException.php',
    'Amp\\Websocket\\WebsocketHeartbeatQueue' => $vendorDir . '/amphp/websocket/src/WebsocketHeartbeatQueue.php',
    'Amp\\Websocket\\WebsocketMessage' => $vendorDir . '/amphp/websocket/src/WebsocketMessage.php',
    'Amp\\Websocket\\WebsocketRateLimit' => $vendorDir . '/amphp/websocket/src/WebsocketRateLimit.php',
    'Amp\\Websocket\\WebsocketTimestamp' => $vendorDir . '/amphp/websocket/src/WebsocketTimestamp.php',
    'BaconQrCode\\Common\\BitArray' => $vendorDir . '/bacon/bacon-qr-code/src/Common/BitArray.php',
    'BaconQrCode\\Common\\BitMatrix' => $vendorDir . '/bacon/bacon-qr-code/src/Common/BitMatrix.php',
    'BaconQrCode\\Common\\BitUtils' => $vendorDir . '/bacon/bacon-qr-code/src/Common/BitUtils.php',
    'BaconQrCode\\Common\\CharacterSetEci' => $vendorDir . '/bacon/bacon-qr-code/src/Common/CharacterSetEci.php',
    'BaconQrCode\\Common\\EcBlock' => $vendorDir . '/bacon/bacon-qr-code/src/Common/EcBlock.php',
    'BaconQrCode\\Common\\EcBlocks' => $vendorDir . '/bacon/bacon-qr-code/src/Common/EcBlocks.php',
    'BaconQrCode\\Common\\ErrorCorrectionLevel' => $vendorDir . '/bacon/bacon-qr-code/src/Common/ErrorCorrectionLevel.php',
    'BaconQrCode\\Common\\FormatInformation' => $vendorDir . '/bacon/bacon-qr-code/src/Common/FormatInformation.php',
    'BaconQrCode\\Common\\Mode' => $vendorDir . '/bacon/bacon-qr-code/src/Common/Mode.php',
    'BaconQrCode\\Common\\ReedSolomonCodec' => $vendorDir . '/bacon/bacon-qr-code/src/Common/ReedSolomonCodec.php',
    'BaconQrCode\\Common\\Version' => $vendorDir . '/bacon/bacon-qr-code/src/Common/Version.php',
    'BaconQrCode\\Encoder\\BlockPair' => $vendorDir . '/bacon/bacon-qr-code/src/Encoder/BlockPair.php',
    'BaconQrCode\\Encoder\\ByteMatrix' => $vendorDir . '/bacon/bacon-qr-code/src/Encoder/ByteMatrix.php',
    'BaconQrCode\\Encoder\\Encoder' => $vendorDir . '/bacon/bacon-qr-code/src/Encoder/Encoder.php',
    'BaconQrCode\\Encoder\\MaskUtil' => $vendorDir . '/bacon/bacon-qr-code/src/Encoder/MaskUtil.php',
    'BaconQrCode\\Encoder\\MatrixUtil' => $vendorDir . '/bacon/bacon-qr-code/src/Encoder/MatrixUtil.php',
    'BaconQrCode\\Encoder\\QrCode' => $vendorDir . '/bacon/bacon-qr-code/src/Encoder/QrCode.php',
    'BaconQrCode\\Exception\\ExceptionInterface' => $vendorDir . '/bacon/bacon-qr-code/src/Exception/ExceptionInterface.php',
    'BaconQrCode\\Exception\\InvalidArgumentException' => $vendorDir . '/bacon/bacon-qr-code/src/Exception/InvalidArgumentException.php',
    'BaconQrCode\\Exception\\OutOfBoundsException' => $vendorDir . '/bacon/bacon-qr-code/src/Exception/OutOfBoundsException.php',
    'BaconQrCode\\Exception\\RuntimeException' => $vendorDir . '/bacon/bacon-qr-code/src/Exception/RuntimeException.php',
    'BaconQrCode\\Exception\\UnexpectedValueException' => $vendorDir . '/bacon/bacon-qr-code/src/Exception/UnexpectedValueException.php',
    'BaconQrCode\\Exception\\WriterException' => $vendorDir . '/bacon/bacon-qr-code/src/Exception/WriterException.php',
    'BaconQrCode\\Renderer\\Color\\Alpha' => $vendorDir . '/bacon/bacon-qr-code/src/Renderer/Color/Alpha.php',
    'BaconQrCode\\Renderer\\Color\\Cmyk' => $vendorDir . '/bacon/bacon-qr-code/src/Renderer/Color/Cmyk.php',
    'BaconQrCode\\Renderer\\Color\\ColorInterface' => $vendorDir . '/bacon/bacon-qr-code/src/Renderer/Color/ColorInterface.php',
    'BaconQrCode\\Renderer\\Color\\Gray' => $vendorDir . '/bacon/bacon-qr-code/src/Renderer/Color/Gray.php',
    'BaconQrCode\\Renderer\\Color\\Rgb' => $vendorDir . '/bacon/bacon-qr-code/src/Renderer/Color/Rgb.php',
    'BaconQrCode\\Renderer\\Eye\\CompositeEye' => $vendorDir . '/bacon/bacon-qr-code/src/Renderer/Eye/CompositeEye.php',
    'BaconQrCode\\Renderer\\Eye\\EyeInterface' => $vendorDir . '/bacon/bacon-qr-code/src/Renderer/Eye/EyeInterface.php',
    'BaconQrCode\\Renderer\\Eye\\ModuleEye' => $vendorDir . '/bacon/bacon-qr-code/src/Renderer/Eye/ModuleEye.php',
    'BaconQrCode\\Renderer\\Eye\\PointyEye' => $vendorDir . '/bacon/bacon-qr-code/src/Renderer/Eye/PointyEye.php',
    'BaconQrCode\\Renderer\\Eye\\SimpleCircleEye' => $vendorDir . '/bacon/bacon-qr-code/src/Renderer/Eye/SimpleCircleEye.php',
    'BaconQrCode\\Renderer\\Eye\\SquareEye' => $vendorDir . '/bacon/bacon-qr-code/src/Renderer/Eye/SquareEye.php',
    'BaconQrCode\\Renderer\\GDLibRenderer' => $vendorDir . '/bacon/bacon-qr-code/src/Renderer/GDLibRenderer.php',
    'BaconQrCode\\Renderer\\ImageRenderer' => $vendorDir . '/bacon/bacon-qr-code/src/Renderer/ImageRenderer.php',
    'BaconQrCode\\Renderer\\Image\\EpsImageBackEnd' => $vendorDir . '/bacon/bacon-qr-code/src/Renderer/Image/EpsImageBackEnd.php',
    'BaconQrCode\\Renderer\\Image\\ImageBackEndInterface' => $vendorDir . '/bacon/bacon-qr-code/src/Renderer/Image/ImageBackEndInterface.php',
    'BaconQrCode\\Renderer\\Image\\ImagickImageBackEnd' => $vendorDir . '/bacon/bacon-qr-code/src/Renderer/Image/ImagickImageBackEnd.php',
    'BaconQrCode\\Renderer\\Image\\SvgImageBackEnd' => $vendorDir . '/bacon/bacon-qr-code/src/Renderer/Image/SvgImageBackEnd.php',
    'BaconQrCode\\Renderer\\Image\\TransformationMatrix' => $vendorDir . '/bacon/bacon-qr-code/src/Renderer/Image/TransformationMatrix.php',
    'BaconQrCode\\Renderer\\Module\\DotsModule' => $vendorDir . '/bacon/bacon-qr-code/src/Renderer/Module/DotsModule.php',
    'BaconQrCode\\Renderer\\Module\\EdgeIterator\\Edge' => $vendorDir . '/bacon/bacon-qr-code/src/Renderer/Module/EdgeIterator/Edge.php',
    'BaconQrCode\\Renderer\\Module\\EdgeIterator\\EdgeIterator' => $vendorDir . '/bacon/bacon-qr-code/src/Renderer/Module/EdgeIterator/EdgeIterator.php',
    'BaconQrCode\\Renderer\\Module\\ModuleInterface' => $vendorDir . '/bacon/bacon-qr-code/src/Renderer/Module/ModuleInterface.php',
    'BaconQrCode\\Renderer\\Module\\RoundnessModule' => $vendorDir . '/bacon/bacon-qr-code/src/Renderer/Module/RoundnessModule.php',
    'BaconQrCode\\Renderer\\Module\\SquareModule' => $vendorDir . '/bacon/bacon-qr-code/src/Renderer/Module/SquareModule.php',
    'BaconQrCode\\Renderer\\Path\\Close' => $vendorDir . '/bacon/bacon-qr-code/src/Renderer/Path/Close.php',
    'BaconQrCode\\Renderer\\Path\\Curve' => $vendorDir . '/bacon/bacon-qr-code/src/Renderer/Path/Curve.php',
    'BaconQrCode\\Renderer\\Path\\EllipticArc' => $vendorDir . '/bacon/bacon-qr-code/src/Renderer/Path/EllipticArc.php',
    'BaconQrCode\\Renderer\\Path\\Line' => $vendorDir . '/bacon/bacon-qr-code/src/Renderer/Path/Line.php',
    'BaconQrCode\\Renderer\\Path\\Move' => $vendorDir . '/bacon/bacon-qr-code/src/Renderer/Path/Move.php',
    'BaconQrCode\\Renderer\\Path\\OperationInterface' => $vendorDir . '/bacon/bacon-qr-code/src/Renderer/Path/OperationInterface.php',
    'BaconQrCode\\Renderer\\Path\\Path' => $vendorDir . '/bacon/bacon-qr-code/src/Renderer/Path/Path.php',
    'BaconQrCode\\Renderer\\PlainTextRenderer' => $vendorDir . '/bacon/bacon-qr-code/src/Renderer/PlainTextRenderer.php',
    'BaconQrCode\\Renderer\\RendererInterface' => $vendorDir . '/bacon/bacon-qr-code/src/Renderer/RendererInterface.php',
    'BaconQrCode\\Renderer\\RendererStyle\\EyeFill' => $vendorDir . '/bacon/bacon-qr-code/src/Renderer/RendererStyle/EyeFill.php',
    'BaconQrCode\\Renderer\\RendererStyle\\Fill' => $vendorDir . '/bacon/bacon-qr-code/src/Renderer/RendererStyle/Fill.php',
    'BaconQrCode\\Renderer\\RendererStyle\\Gradient' => $vendorDir . '/bacon/bacon-qr-code/src/Renderer/RendererStyle/Gradient.php',
    'BaconQrCode\\Renderer\\RendererStyle\\GradientType' => $vendorDir . '/bacon/bacon-qr-code/src/Renderer/RendererStyle/GradientType.php',
    'BaconQrCode\\Renderer\\RendererStyle\\RendererStyle' => $vendorDir . '/bacon/bacon-qr-code/src/Renderer/RendererStyle/RendererStyle.php',
    'BaconQrCode\\Writer' => $vendorDir . '/bacon/bacon-qr-code/src/Writer.php',
    'Brick\\Math\\BigDecimal' => $vendorDir . '/brick/math/src/BigDecimal.php',
    'Brick\\Math\\BigInteger' => $vendorDir . '/brick/math/src/BigInteger.php',
    'Brick\\Math\\BigNumber' => $vendorDir . '/brick/math/src/BigNumber.php',
    'Brick\\Math\\BigRational' => $vendorDir . '/brick/math/src/BigRational.php',
    'Brick\\Math\\Exception\\DivisionByZeroException' => $vendorDir . '/brick/math/src/Exception/DivisionByZeroException.php',
    'Brick\\Math\\Exception\\IntegerOverflowException' => $vendorDir . '/brick/math/src/Exception/IntegerOverflowException.php',
    'Brick\\Math\\Exception\\InvalidArgumentException' => $vendorDir . '/brick/math/src/Exception/InvalidArgumentException.php',
    'Brick\\Math\\Exception\\MathException' => $vendorDir . '/brick/math/src/Exception/MathException.php',
    'Brick\\Math\\Exception\\NegativeNumberException' => $vendorDir . '/brick/math/src/Exception/NegativeNumberException.php',
    'Brick\\Math\\Exception\\NoInverseException' => $vendorDir . '/brick/math/src/Exception/NoInverseException.php',
    'Brick\\Math\\Exception\\NumberFormatException' => $vendorDir . '/brick/math/src/Exception/NumberFormatException.php',
    'Brick\\Math\\Exception\\PlatformException' => $vendorDir . '/brick/math/src/Exception/PlatformException.php',
    'Brick\\Math\\Exception\\RandomSourceException' => $vendorDir . '/brick/math/src/Exception/RandomSourceException.php',
    'Brick\\Math\\Exception\\RoundingNecessaryException' => $vendorDir . '/brick/math/src/Exception/RoundingNecessaryException.php',
    'Brick\\Math\\Internal\\Calculator' => $vendorDir . '/brick/math/src/Internal/Calculator.php',
    'Brick\\Math\\Internal\\CalculatorRegistry' => $vendorDir . '/brick/math/src/Internal/CalculatorRegistry.php',
    'Brick\\Math\\Internal\\Calculator\\BcMathCalculator' => $vendorDir . '/brick/math/src/Internal/Calculator/BcMathCalculator.php',
    'Brick\\Math\\Internal\\Calculator\\GmpCalculator' => $vendorDir . '/brick/math/src/Internal/Calculator/GmpCalculator.php',
    'Brick\\Math\\Internal\\Calculator\\NativeCalculator' => $vendorDir . '/brick/math/src/Internal/Calculator/NativeCalculator.php',
    'Brick\\Math\\Internal\\DecimalHelper' => $vendorDir . '/brick/math/src/Internal/DecimalHelper.php',
    'Brick\\Math\\Internal\\Safe' => $vendorDir . '/brick/math/src/Internal/Safe.php',
    'Brick\\Math\\NumberSyntax' => $vendorDir . '/brick/math/src/NumberSyntax.php',
    'Brick\\Math\\RoundingMode' => $vendorDir . '/brick/math/src/RoundingMode.php',
    'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
    'DASPRiD\\Enum\\AbstractEnum' => $vendorDir . '/dasprid/enum/src/AbstractEnum.php',
    'DASPRiD\\Enum\\EnumMap' => $vendorDir . '/dasprid/enum/src/EnumMap.php',
    'DASPRiD\\Enum\\Exception\\CloneNotSupportedException' => $vendorDir . '/dasprid/enum/src/Exception/CloneNotSupportedException.php',
    'DASPRiD\\Enum\\Exception\\ExceptionInterface' => $vendorDir . '/dasprid/enum/src/Exception/ExceptionInterface.php',
    'DASPRiD\\Enum\\Exception\\ExpectationException' => $vendorDir . '/dasprid/enum/src/Exception/ExpectationException.php',
    'DASPRiD\\Enum\\Exception\\IllegalArgumentException' => $vendorDir . '/dasprid/enum/src/Exception/IllegalArgumentException.php',
    'DASPRiD\\Enum\\Exception\\MismatchException' => $vendorDir . '/dasprid/enum/src/Exception/MismatchException.php',
    'DASPRiD\\Enum\\Exception\\SerializeNotSupportedException' => $vendorDir . '/dasprid/enum/src/Exception/SerializeNotSupportedException.php',
    'DASPRiD\\Enum\\Exception\\UnserializeNotSupportedException' => $vendorDir . '/dasprid/enum/src/Exception/UnserializeNotSupportedException.php',
    'DASPRiD\\Enum\\NullValue' => $vendorDir . '/dasprid/enum/src/NullValue.php',
    'DateError' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateError.php',
    'DateException' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateException.php',
    'DateInvalidOperationException' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateInvalidOperationException.php',
    'DateInvalidTimeZoneException' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateInvalidTimeZoneException.php',
    'DateMalformedIntervalStringException' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateMalformedIntervalStringException.php',
    'DateMalformedPeriodStringException' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateMalformedPeriodStringException.php',
    'DateMalformedStringException' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateMalformedStringException.php',
    'DateObjectError' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateObjectError.php',
    'DateRangeError' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateRangeError.php',
    'DelayedTargetValidation' => $vendorDir . '/symfony/polyfill-php85/Resources/stubs/DelayedTargetValidation.php',
    'Deprecated' => $vendorDir . '/symfony/polyfill-php84/Resources/stubs/Deprecated.php',
    'Filter\\FilterException' => $vendorDir . '/symfony/polyfill-php85/Resources/stubs/Filter/FilterException.php',
    'Filter\\FilterFailedException' => $vendorDir . '/symfony/polyfill-php85/Resources/stubs/Filter/FilterFailedException.php',
    'Kelunik\\Certificate\\Certificate' => $vendorDir . '/kelunik/certificate/src/Certificate.php',
    'Kelunik\\Certificate\\FieldNotSupportedException' => $vendorDir . '/kelunik/certificate/src/FieldNotSupportedException.php',
    'Kelunik\\Certificate\\InvalidCertificateException' => $vendorDir . '/kelunik/certificate/src/InvalidCertificateException.php',
    'Kelunik\\Certificate\\Profile' => $vendorDir . '/kelunik/certificate/src/Profile.php',
    'League\\Uri\\BaseUri' => $vendorDir . '/league/uri/BaseUri.php',
    'League\\Uri\\Builder' => $vendorDir . '/league/uri/Builder.php',
    'League\\Uri\\Components\\Authority' => $vendorDir . '/league/uri-components/Components/Authority.php',
    'League\\Uri\\Components\\Component' => $vendorDir . '/league/uri-components/Components/Component.php',
    'League\\Uri\\Components\\DataPath' => $vendorDir . '/league/uri-components/Components/DataPath.php',
    'League\\Uri\\Components\\Domain' => $vendorDir . '/league/uri-components/Components/Domain.php',
    'League\\Uri\\Components\\Fragment' => $vendorDir . '/league/uri-components/Components/Fragment.php',
    'League\\Uri\\Components\\FragmentDirectives' => $vendorDir . '/league/uri-components/Components/FragmentDirectives.php',
    'League\\Uri\\Components\\FragmentDirectives\\DirectiveString' => $vendorDir . '/league/uri-components/Components/FragmentDirectives/DirectiveString.php',
    'League\\Uri\\Components\\FragmentDirectives\\Encoder' => $vendorDir . '/league/uri-components/Components/FragmentDirectives/Encoder.php',
    'League\\Uri\\Components\\FragmentDirectives\\GenericDirective' => $vendorDir . '/league/uri-components/Components/FragmentDirectives/GenericDirective.php',
    'League\\Uri\\Components\\FragmentDirectives\\TextDirective' => $vendorDir . '/league/uri-components/Components/FragmentDirectives/TextDirective.php',
    'League\\Uri\\Components\\HierarchicalPath' => $vendorDir . '/league/uri-components/Components/HierarchicalPath.php',
    'League\\Uri\\Components\\Host' => $vendorDir . '/league/uri-components/Components/Host.php',
    'League\\Uri\\Components\\Path' => $vendorDir . '/league/uri-components/Components/Path.php',
    'League\\Uri\\Components\\Port' => $vendorDir . '/league/uri-components/Components/Port.php',
    'League\\Uri\\Components\\Query' => $vendorDir . '/league/uri-components/Components/Query.php',
    'League\\Uri\\Components\\Scheme' => $vendorDir . '/league/uri-components/Components/Scheme.php',
    'League\\Uri\\Components\\URLSearchParams' => $vendorDir . '/league/uri-components/Components/URLSearchParams.php',
    'League\\Uri\\Components\\UserInfo' => $vendorDir . '/league/uri-components/Components/UserInfo.php',
    'League\\Uri\\Contracts\\AuthorityInterface' => $vendorDir . '/league/uri-interfaces/Contracts/AuthorityInterface.php',
    'League\\Uri\\Contracts\\Conditionable' => $vendorDir . '/league/uri-interfaces/Contracts/Conditionable.php',
    'League\\Uri\\Contracts\\DataPathInterface' => $vendorDir . '/league/uri-interfaces/Contracts/DataPathInterface.php',
    'League\\Uri\\Contracts\\DomainHostInterface' => $vendorDir . '/league/uri-interfaces/Contracts/DomainHostInterface.php',
    'League\\Uri\\Contracts\\FragmentDirective' => $vendorDir . '/league/uri-interfaces/Contracts/FragmentDirective.php',
    'League\\Uri\\Contracts\\FragmentInterface' => $vendorDir . '/league/uri-interfaces/Contracts/FragmentInterface.php',
    'League\\Uri\\Contracts\\HostInterface' => $vendorDir . '/league/uri-interfaces/Contracts/HostInterface.php',
    'League\\Uri\\Contracts\\IpHostInterface' => $vendorDir . '/league/uri-interfaces/Contracts/IpHostInterface.php',
    'League\\Uri\\Contracts\\PathInterface' => $vendorDir . '/league/uri-interfaces/Contracts/PathInterface.php',
    'League\\Uri\\Contracts\\PortInterface' => $vendorDir . '/league/uri-interfaces/Contracts/PortInterface.php',
    'League\\Uri\\Contracts\\QueryInterface' => $vendorDir . '/league/uri-interfaces/Contracts/QueryInterface.php',
    'League\\Uri\\Contracts\\SegmentedPathInterface' => $vendorDir . '/league/uri-interfaces/Contracts/SegmentedPathInterface.php',
    'League\\Uri\\Contracts\\Transformable' => $vendorDir . '/league/uri-interfaces/Contracts/Transformable.php',
    'League\\Uri\\Contracts\\UriAccess' => $vendorDir . '/league/uri-interfaces/Contracts/UriAccess.php',
    'League\\Uri\\Contracts\\UriComponentInterface' => $vendorDir . '/league/uri-interfaces/Contracts/UriComponentInterface.php',
    'League\\Uri\\Contracts\\UriException' => $vendorDir . '/league/uri-interfaces/Contracts/UriException.php',
    'League\\Uri\\Contracts\\UriInterface' => $vendorDir . '/league/uri-interfaces/Contracts/UriInterface.php',
    'League\\Uri\\Contracts\\UserInfoInterface' => $vendorDir . '/league/uri-interfaces/Contracts/UserInfoInterface.php',
    'League\\Uri\\Encoder' => $vendorDir . '/league/uri-interfaces/Encoder.php',
    'League\\Uri\\Exceptions\\ConversionFailed' => $vendorDir . '/league/uri-interfaces/Exceptions/ConversionFailed.php',
    'League\\Uri\\Exceptions\\MissingFeature' => $vendorDir . '/league/uri-interfaces/Exceptions/MissingFeature.php',
    'League\\Uri\\Exceptions\\OffsetOutOfBounds' => $vendorDir . '/league/uri-interfaces/Exceptions/OffsetOutOfBounds.php',
    'League\\Uri\\Exceptions\\SyntaxError' => $vendorDir . '/league/uri-interfaces/Exceptions/SyntaxError.php',
    'League\\Uri\\FeatureDetection' => $vendorDir . '/league/uri-interfaces/FeatureDetection.php',
    'League\\Uri\\HostFormat' => $vendorDir . '/league/uri-interfaces/HostFormat.php',
    'League\\Uri\\HostRecord' => $vendorDir . '/league/uri-interfaces/HostRecord.php',
    'League\\Uri\\HostType' => $vendorDir . '/league/uri-interfaces/HostType.php',
    'League\\Uri\\Http' => $vendorDir . '/league/uri/Http.php',
    'League\\Uri\\HttpFactory' => $vendorDir . '/league/uri/HttpFactory.php',
    'League\\Uri\\IPv4Normalizer' => $vendorDir . '/league/uri-components/IPv4Normalizer.php',
    'League\\Uri\\IPv4\\BCMathCalculator' => $vendorDir . '/league/uri-interfaces/IPv4/BCMathCalculator.php',
    'League\\Uri\\IPv4\\Calculator' => $vendorDir . '/league/uri-interfaces/IPv4/Calculator.php',
    'League\\Uri\\IPv4\\Converter' => $vendorDir . '/league/uri-interfaces/IPv4/Converter.php',
    'League\\Uri\\IPv4\\GMPCalculator' => $vendorDir . '/league/uri-interfaces/IPv4/GMPCalculator.php',
    'League\\Uri\\IPv4\\NativeCalculator' => $vendorDir . '/league/uri-interfaces/IPv4/NativeCalculator.php',
    'League\\Uri\\IPv6\\Converter' => $vendorDir . '/league/uri-interfaces/IPv6/Converter.php',
    'League\\Uri\\Idna\\Converter' => $vendorDir . '/league/uri-interfaces/Idna/Converter.php',
    'League\\Uri\\Idna\\Error' => $vendorDir . '/league/uri-interfaces/Idna/Error.php',
    'League\\Uri\\Idna\\Option' => $vendorDir . '/league/uri-interfaces/Idna/Option.php',
    'League\\Uri\\Idna\\Result' => $vendorDir . '/league/uri-interfaces/Idna/Result.php',
    'League\\Uri\\KeyValuePair\\Converter' => $vendorDir . '/league/uri-interfaces/KeyValuePair/Converter.php',
    'League\\Uri\\Modifier' => $vendorDir . '/league/uri-components/Modifier.php',
    'League\\Uri\\Occurrence' => $vendorDir . '/league/uri-interfaces/Occurrence.php',
    'League\\Uri\\QueryComposeMode' => $vendorDir . '/league/uri-interfaces/QueryComposeMode.php',
    'League\\Uri\\QueryExtractMode' => $vendorDir . '/league/uri-interfaces/QueryExtractMode.php',
    'League\\Uri\\QueryString' => $vendorDir . '/league/uri-interfaces/QueryString.php',
    'League\\Uri\\SchemeType' => $vendorDir . '/league/uri/SchemeType.php',
    'League\\Uri\\StringCoercionMode' => $vendorDir . '/league/uri-interfaces/StringCoercionMode.php',
    'League\\Uri\\TypeConverter' => $vendorDir . '/league/uri/TypeConverter.php',
    'League\\Uri\\Uri' => $vendorDir . '/league/uri/Uri.php',
    'League\\Uri\\UriComparisonMode' => $vendorDir . '/league/uri-interfaces/UriComparisonMode.php',
    'League\\Uri\\UriInfo' => $vendorDir . '/league/uri/UriInfo.php',
    'League\\Uri\\UriModifier' => $vendorDir . '/league/uri-components/UriModifier.php',
    'League\\Uri\\UriResolver' => $vendorDir . '/league/uri/UriResolver.php',
    'League\\Uri\\UriScheme' => $vendorDir . '/league/uri/UriScheme.php',
    'League\\Uri\\UriString' => $vendorDir . '/league/uri-interfaces/UriString.php',
    'League\\Uri\\UriTemplate' => $vendorDir . '/league/uri/UriTemplate.php',
    'League\\Uri\\UriTemplate\\Expression' => $vendorDir . '/league/uri/UriTemplate/Expression.php',
    'League\\Uri\\UriTemplate\\ExtractedValue' => $vendorDir . '/league/uri/UriTemplate/ExtractedValue.php',
    'League\\Uri\\UriTemplate\\ExtractionErrorReason' => $vendorDir . '/league/uri/UriTemplate/ExtractionErrorReason.php',
    'League\\Uri\\UriTemplate\\ExtractionResult' => $vendorDir . '/league/uri/UriTemplate/ExtractionResult.php',
    'League\\Uri\\UriTemplate\\Literal' => $vendorDir . '/league/uri/UriTemplate/Literal.php',
    'League\\Uri\\UriTemplate\\Operator' => $vendorDir . '/league/uri/UriTemplate/Operator.php',
    'League\\Uri\\UriTemplate\\Template' => $vendorDir . '/league/uri/UriTemplate/Template.php',
    'League\\Uri\\UriTemplate\\TemplateCanNotBeExpanded' => $vendorDir . '/league/uri/UriTemplate/TemplateCanNotBeExpanded.php',
    'League\\Uri\\UriTemplate\\VarSpecifier' => $vendorDir . '/league/uri/UriTemplate/VarSpecifier.php',
    'League\\Uri\\UriTemplate\\VariableBag' => $vendorDir . '/league/uri/UriTemplate/VariableBag.php',
    'League\\Uri\\UriTemplate\\VariableCanNotBeExtracted' => $vendorDir . '/league/uri/UriTemplate/VariableCanNotBeExtracted.php',
    'League\\Uri\\UrlPattern' => $vendorDir . '/league/uri/UrlPattern.php',
    'League\\Uri\\UrlPatternBuilder' => $vendorDir . '/league/uri/UrlPatternBuilder.php',
    'League\\Uri\\UrlPattern\\Component' => $vendorDir . '/league/uri/UrlPattern/Component.php',
    'League\\Uri\\UrlPattern\\ComponentName' => $vendorDir . '/league/uri/UrlPattern/ComponentName.php',
    'League\\Uri\\UrlPattern\\ComponentResult' => $vendorDir . '/league/uri/UrlPattern/ComponentResult.php',
    'League\\Uri\\UrlPattern\\MatchMode' => $vendorDir . '/league/uri/UrlPattern/MatchMode.php',
    'League\\Uri\\UrlPattern\\Modifier' => $vendorDir . '/league/uri/UrlPattern/Modifier.php',
    'League\\Uri\\UrlPattern\\Parser' => $vendorDir . '/league/uri/UrlPattern/Parser.php',
    'League\\Uri\\UrlPattern\\Part' => $vendorDir . '/league/uri/UrlPattern/Part.php',
    'League\\Uri\\UrlPattern\\PartType' => $vendorDir . '/league/uri/UrlPattern/PartType.php',
    'League\\Uri\\UrlPattern\\PathToRegexp' => $vendorDir . '/league/uri/UrlPattern/PathToRegexp.php',
    'League\\Uri\\UrlPattern\\PatternType' => $vendorDir . '/league/uri/UrlPattern/PatternType.php',
    'League\\Uri\\UrlPattern\\Result' => $vendorDir . '/league/uri/UrlPattern/Result.php',
    'League\\Uri\\UrlPattern\\State' => $vendorDir . '/league/uri/UrlPattern/State.php',
    'League\\Uri\\UrlPattern\\Token' => $vendorDir . '/league/uri/UrlPattern/Token.php',
    'League\\Uri\\UrlPattern\\TokenType' => $vendorDir . '/league/uri/UrlPattern/TokenType.php',
    'League\\Uri\\Urn' => $vendorDir . '/league/uri/Urn.php',
    'League\\Uri\\UrnComparisonMode' => $vendorDir . '/league/uri-interfaces/UrnComparisonMode.php',
    'LibDNS\\Decoder\\Decoder' => $vendorDir . '/daverandom/libdns/src/Decoder/Decoder.php',
    'LibDNS\\Decoder\\DecoderFactory' => $vendorDir . '/daverandom/libdns/src/Decoder/DecoderFactory.php',
    'LibDNS\\Decoder\\DecodingContext' => $vendorDir . '/daverandom/libdns/src/Decoder/DecodingContext.php',
    'LibDNS\\Decoder\\DecodingContextFactory' => $vendorDir . '/daverandom/libdns/src/Decoder/DecodingContextFactory.php',
    'LibDNS\\Encoder\\Encoder' => $vendorDir . '/daverandom/libdns/src/Encoder/Encoder.php',
    'LibDNS\\Encoder\\EncoderFactory' => $vendorDir . '/daverandom/libdns/src/Encoder/EncoderFactory.php',
    'LibDNS\\Encoder\\EncodingContext' => $vendorDir . '/daverandom/libdns/src/Encoder/EncodingContext.php',
    'LibDNS\\Encoder\\EncodingContextFactory' => $vendorDir . '/daverandom/libdns/src/Encoder/EncodingContextFactory.php',
    'LibDNS\\Enumeration' => $vendorDir . '/daverandom/libdns/src/Enumeration.php',
    'LibDNS\\Messages\\Message' => $vendorDir . '/daverandom/libdns/src/Messages/Message.php',
    'LibDNS\\Messages\\MessageFactory' => $vendorDir . '/daverandom/libdns/src/Messages/MessageFactory.php',
    'LibDNS\\Messages\\MessageOpCodes' => $vendorDir . '/daverandom/libdns/src/Messages/MessageOpCodes.php',
    'LibDNS\\Messages\\MessageResponseCodes' => $vendorDir . '/daverandom/libdns/src/Messages/MessageResponseCodes.php',
    'LibDNS\\Messages\\MessageTypes' => $vendorDir . '/daverandom/libdns/src/Messages/MessageTypes.php',
    'LibDNS\\Packets\\LabelRegistry' => $vendorDir . '/daverandom/libdns/src/Packets/LabelRegistry.php',
    'LibDNS\\Packets\\Packet' => $vendorDir . '/daverandom/libdns/src/Packets/Packet.php',
    'LibDNS\\Packets\\PacketFactory' => $vendorDir . '/daverandom/libdns/src/Packets/PacketFactory.php',
    'LibDNS\\Records\\Question' => $vendorDir . '/daverandom/libdns/src/Records/Question.php',
    'LibDNS\\Records\\QuestionFactory' => $vendorDir . '/daverandom/libdns/src/Records/QuestionFactory.php',
    'LibDNS\\Records\\RData' => $vendorDir . '/daverandom/libdns/src/Records/RData.php',
    'LibDNS\\Records\\RDataBuilder' => $vendorDir . '/daverandom/libdns/src/Records/RDataBuilder.php',
    'LibDNS\\Records\\RDataFactory' => $vendorDir . '/daverandom/libdns/src/Records/RDataFactory.php',
    'LibDNS\\Records\\Record' => $vendorDir . '/daverandom/libdns/src/Records/Record.php',
    'LibDNS\\Records\\RecordCollection' => $vendorDir . '/daverandom/libdns/src/Records/RecordCollection.php',
    'LibDNS\\Records\\RecordCollectionFactory' => $vendorDir . '/daverandom/libdns/src/Records/RecordCollectionFactory.php',
    'LibDNS\\Records\\RecordTypes' => $vendorDir . '/daverandom/libdns/src/Records/RecordTypes.php',
    'LibDNS\\Records\\Resource' => $vendorDir . '/daverandom/libdns/src/Records/Resource.php',
    'LibDNS\\Records\\ResourceBuilder' => $vendorDir . '/daverandom/libdns/src/Records/ResourceBuilder.php',
    'LibDNS\\Records\\ResourceBuilderFactory' => $vendorDir . '/daverandom/libdns/src/Records/ResourceBuilderFactory.php',
    'LibDNS\\Records\\ResourceClasses' => $vendorDir . '/daverandom/libdns/src/Records/ResourceClasses.php',
    'LibDNS\\Records\\ResourceFactory' => $vendorDir . '/daverandom/libdns/src/Records/ResourceFactory.php',
    'LibDNS\\Records\\ResourceQClasses' => $vendorDir . '/daverandom/libdns/src/Records/ResourceQClasses.php',
    'LibDNS\\Records\\ResourceQTypes' => $vendorDir . '/daverandom/libdns/src/Records/ResourceQTypes.php',
    'LibDNS\\Records\\ResourceTypes' => $vendorDir . '/daverandom/libdns/src/Records/ResourceTypes.php',
    'LibDNS\\Records\\TypeDefinitions\\FieldDefinition' => $vendorDir . '/daverandom/libdns/src/Records/TypeDefinitions/FieldDefinition.php',
    'LibDNS\\Records\\TypeDefinitions\\FieldDefinitionFactory' => $vendorDir . '/daverandom/libdns/src/Records/TypeDefinitions/FieldDefinitionFactory.php',
    'LibDNS\\Records\\TypeDefinitions\\TypeDefinition' => $vendorDir . '/daverandom/libdns/src/Records/TypeDefinitions/TypeDefinition.php',
    'LibDNS\\Records\\TypeDefinitions\\TypeDefinitionFactory' => $vendorDir . '/daverandom/libdns/src/Records/TypeDefinitions/TypeDefinitionFactory.php',
    'LibDNS\\Records\\TypeDefinitions\\TypeDefinitionManager' => $vendorDir . '/daverandom/libdns/src/Records/TypeDefinitions/TypeDefinitionManager.php',
    'LibDNS\\Records\\TypeDefinitions\\TypeDefinitionManagerFactory' => $vendorDir . '/daverandom/libdns/src/Records/TypeDefinitions/TypeDefinitionManagerFactory.php',
    'LibDNS\\Records\\Types\\Anything' => $vendorDir . '/daverandom/libdns/src/Records/Types/Anything.php',
    'LibDNS\\Records\\Types\\BitMap' => $vendorDir . '/daverandom/libdns/src/Records/Types/BitMap.php',
    'LibDNS\\Records\\Types\\Char' => $vendorDir . '/daverandom/libdns/src/Records/Types/Char.php',
    'LibDNS\\Records\\Types\\CharacterString' => $vendorDir . '/daverandom/libdns/src/Records/Types/CharacterString.php',
    'LibDNS\\Records\\Types\\DomainName' => $vendorDir . '/daverandom/libdns/src/Records/Types/DomainName.php',
    'LibDNS\\Records\\Types\\IPv4Address' => $vendorDir . '/daverandom/libdns/src/Records/Types/IPv4Address.php',
    'LibDNS\\Records\\Types\\IPv6Address' => $vendorDir . '/daverandom/libdns/src/Records/Types/IPv6Address.php',
    'LibDNS\\Records\\Types\\Long' => $vendorDir . '/daverandom/libdns/src/Records/Types/Long.php',
    'LibDNS\\Records\\Types\\Short' => $vendorDir . '/daverandom/libdns/src/Records/Types/Short.php',
    'LibDNS\\Records\\Types\\Type' => $vendorDir . '/daverandom/libdns/src/Records/Types/Type.php',
    'LibDNS\\Records\\Types\\TypeBuilder' => $vendorDir . '/daverandom/libdns/src/Records/Types/TypeBuilder.php',
    'LibDNS\\Records\\Types\\TypeFactory' => $vendorDir . '/daverandom/libdns/src/Records/Types/TypeFactory.php',
    'LibDNS\\Records\\Types\\Types' => $vendorDir . '/daverandom/libdns/src/Records/Types/Types.php',
    'Monolog\\Attribute\\AsMonologProcessor' => $vendorDir . '/monolog/monolog/src/Monolog/Attribute/AsMonologProcessor.php',
    'Monolog\\Attribute\\WithMonologChannel' => $vendorDir . '/monolog/monolog/src/Monolog/Attribute/WithMonologChannel.php',
    'Monolog\\DateTimeImmutable' => $vendorDir . '/monolog/monolog/src/Monolog/DateTimeImmutable.php',
    'Monolog\\ErrorHandler' => $vendorDir . '/monolog/monolog/src/Monolog/ErrorHandler.php',
    'Monolog\\Formatter\\ChromePHPFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/ChromePHPFormatter.php',
    'Monolog\\Formatter\\ElasticaFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/ElasticaFormatter.php',
    'Monolog\\Formatter\\ElasticsearchFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/ElasticsearchFormatter.php',
    'Monolog\\Formatter\\FlowdockFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/FlowdockFormatter.php',
    'Monolog\\Formatter\\FluentdFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/FluentdFormatter.php',
    'Monolog\\Formatter\\FormatterInterface' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/FormatterInterface.php',
    'Monolog\\Formatter\\GelfMessageFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/GelfMessageFormatter.php',
    'Monolog\\Formatter\\GoogleCloudLoggingFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/GoogleCloudLoggingFormatter.php',
    'Monolog\\Formatter\\HtmlFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/HtmlFormatter.php',
    'Monolog\\Formatter\\JsonFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/JsonFormatter.php',
    'Monolog\\Formatter\\LineFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/LineFormatter.php',
    'Monolog\\Formatter\\LogglyFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/LogglyFormatter.php',
    'Monolog\\Formatter\\LogmaticFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/LogmaticFormatter.php',
    'Monolog\\Formatter\\LogstashFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/LogstashFormatter.php',
    'Monolog\\Formatter\\MongoDBFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/MongoDBFormatter.php',
    'Monolog\\Formatter\\NormalizerFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/NormalizerFormatter.php',
    'Monolog\\Formatter\\RedactingFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/RedactingFormatter.php',
    'Monolog\\Formatter\\ScalarFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/ScalarFormatter.php',
    'Monolog\\Formatter\\SyslogFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/SyslogFormatter.php',
    'Monolog\\Formatter\\WildfireFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/WildfireFormatter.php',
    'Monolog\\Formatter\\WrappingFormatterInterface' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/WrappingFormatterInterface.php',
    'Monolog\\Handler\\AbstractHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/AbstractHandler.php',
    'Monolog\\Handler\\AbstractProcessingHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/AbstractProcessingHandler.php',
    'Monolog\\Handler\\AbstractSyslogHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/AbstractSyslogHandler.php',
    'Monolog\\Handler\\AmqpHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/AmqpHandler.php',
    'Monolog\\Handler\\BrowserConsoleHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/BrowserConsoleHandler.php',
    'Monolog\\Handler\\BufferHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/BufferHandler.php',
    'Monolog\\Handler\\ChromePHPHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/ChromePHPHandler.php',
    'Monolog\\Handler\\CouchDBHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/CouchDBHandler.php',
    'Monolog\\Handler\\CubeHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/CubeHandler.php',
    'Monolog\\Handler\\Curl\\Util' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/Curl/Util.php',
    'Monolog\\Handler\\DeduplicationHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/DeduplicationHandler.php',
    'Monolog\\Handler\\DoctrineCouchDBHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/DoctrineCouchDBHandler.php',
    'Monolog\\Handler\\DynamoDbHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/DynamoDbHandler.php',
    'Monolog\\Handler\\ElasticaHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/ElasticaHandler.php',
    'Monolog\\Handler\\ElasticsearchHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/ElasticsearchHandler.php',
    'Monolog\\Handler\\ErrorLogHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/ErrorLogHandler.php',
    'Monolog\\Handler\\FallbackGroupHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/FallbackGroupHandler.php',
    'Monolog\\Handler\\FilterHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/FilterHandler.php',
    'Monolog\\Handler\\FingersCrossedHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/FingersCrossedHandler.php',
    'Monolog\\Handler\\FingersCrossed\\ActivationStrategyInterface' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php',
    'Monolog\\Handler\\FingersCrossed\\ChannelLevelActivationStrategy' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/FingersCrossed/ChannelLevelActivationStrategy.php',
    'Monolog\\Handler\\FingersCrossed\\ErrorLevelActivationStrategy' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/FingersCrossed/ErrorLevelActivationStrategy.php',
    'Monolog\\Handler\\FirePHPHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/FirePHPHandler.php',
    'Monolog\\Handler\\FleepHookHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/FleepHookHandler.php',
    'Monolog\\Handler\\FlowdockHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/FlowdockHandler.php',
    'Monolog\\Handler\\FormattableHandlerInterface' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/FormattableHandlerInterface.php',
    'Monolog\\Handler\\FormattableHandlerTrait' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/FormattableHandlerTrait.php',
    'Monolog\\Handler\\FrankenPhpHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/FrankenPhpHandler.php',
    'Monolog\\Handler\\GelfHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/GelfHandler.php',
    'Monolog\\Handler\\GroupHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/GroupHandler.php',
    'Monolog\\Handler\\Handler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/Handler.php',
    'Monolog\\Handler\\HandlerInterface' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/HandlerInterface.php',
    'Monolog\\Handler\\HandlerWrapper' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/HandlerWrapper.php',
    'Monolog\\Handler\\IFTTTHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/IFTTTHandler.php',
    'Monolog\\Handler\\InsightOpsHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/InsightOpsHandler.php',
    'Monolog\\Handler\\LogEntriesHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/LogEntriesHandler.php',
    'Monolog\\Handler\\LogMonsterHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/LogMonsterHandler.php',
    'Monolog\\Handler\\LogglyHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/LogglyHandler.php',
    'Monolog\\Handler\\LogmaticHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/LogmaticHandler.php',
    'Monolog\\Handler\\MailHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/MailHandler.php',
    'Monolog\\Handler\\MandrillHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/MandrillHandler.php',
    'Monolog\\Handler\\MissingExtensionException' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/MissingExtensionException.php',
    'Monolog\\Handler\\MongoDBHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/MongoDBHandler.php',
    'Monolog\\Handler\\NativeMailerHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/NativeMailerHandler.php',
    'Monolog\\Handler\\NewRelicHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/NewRelicHandler.php',
    'Monolog\\Handler\\NoopHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/NoopHandler.php',
    'Monolog\\Handler\\NullHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/NullHandler.php',
    'Monolog\\Handler\\OverflowHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/OverflowHandler.php',
    'Monolog\\Handler\\PHPConsoleHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/PHPConsoleHandler.php',
    'Monolog\\Handler\\ProcessHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/ProcessHandler.php',
    'Monolog\\Handler\\ProcessableHandlerInterface' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/ProcessableHandlerInterface.php',
    'Monolog\\Handler\\ProcessableHandlerTrait' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/ProcessableHandlerTrait.php',
    'Monolog\\Handler\\PsrHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/PsrHandler.php',
    'Monolog\\Handler\\PushoverHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/PushoverHandler.php',
    'Monolog\\Handler\\RedisHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/RedisHandler.php',
    'Monolog\\Handler\\RedisPubSubHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/RedisPubSubHandler.php',
    'Monolog\\Handler\\RollbarHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/RollbarHandler.php',
    'Monolog\\Handler\\RotatingFileHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php',
    'Monolog\\Handler\\SamplingHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/SamplingHandler.php',
    'Monolog\\Handler\\SendGridHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/SendGridHandler.php',
    'Monolog\\Handler\\SlackHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/SlackHandler.php',
    'Monolog\\Handler\\SlackWebhookHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/SlackWebhookHandler.php',
    'Monolog\\Handler\\Slack\\SlackRecord' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/Slack/SlackRecord.php',
    'Monolog\\Handler\\SocketHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/SocketHandler.php',
    'Monolog\\Handler\\SqsHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/SqsHandler.php',
    'Monolog\\Handler\\StreamHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/StreamHandler.php',
    'Monolog\\Handler\\SymfonyMailerHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/SymfonyMailerHandler.php',
    'Monolog\\Handler\\SyslogHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/SyslogHandler.php',
    'Monolog\\Handler\\SyslogUdpHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/SyslogUdpHandler.php',
    'Monolog\\Handler\\SyslogUdp\\UdpSocket' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/SyslogUdp/UdpSocket.php',
    'Monolog\\Handler\\TelegramBotHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/TelegramBotHandler.php',
    'Monolog\\Handler\\TestHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/TestHandler.php',
    'Monolog\\Handler\\WebRequestRecognizerTrait' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/WebRequestRecognizerTrait.php',
    'Monolog\\Handler\\WhatFailureGroupHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/WhatFailureGroupHandler.php',
    'Monolog\\Handler\\ZendMonitorHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/ZendMonitorHandler.php',
    'Monolog\\JsonSerializableDateTimeImmutable' => $vendorDir . '/monolog/monolog/src/Monolog/JsonSerializableDateTimeImmutable.php',
    'Monolog\\Level' => $vendorDir . '/monolog/monolog/src/Monolog/Level.php',
    'Monolog\\LogRecord' => $vendorDir . '/monolog/monolog/src/Monolog/LogRecord.php',
    'Monolog\\Logger' => $vendorDir . '/monolog/monolog/src/Monolog/Logger.php',
    'Monolog\\Processor\\ClosureContextProcessor' => $vendorDir . '/monolog/monolog/src/Monolog/Processor/ClosureContextProcessor.php',
    'Monolog\\Processor\\GitProcessor' => $vendorDir . '/monolog/monolog/src/Monolog/Processor/GitProcessor.php',
    'Monolog\\Processor\\HostnameProcessor' => $vendorDir . '/monolog/monolog/src/Monolog/Processor/HostnameProcessor.php',
    'Monolog\\Processor\\IntrospectionProcessor' => $vendorDir . '/monolog/monolog/src/Monolog/Processor/IntrospectionProcessor.php',
    'Monolog\\Processor\\LoadAverageProcessor' => $vendorDir . '/monolog/monolog/src/Monolog/Processor/LoadAverageProcessor.php',
    'Monolog\\Processor\\MemoryPeakUsageProcessor' => $vendorDir . '/monolog/monolog/src/Monolog/Processor/MemoryPeakUsageProcessor.php',
    'Monolog\\Processor\\MemoryProcessor' => $vendorDir . '/monolog/monolog/src/Monolog/Processor/MemoryProcessor.php',
    'Monolog\\Processor\\MemoryUsageProcessor' => $vendorDir . '/monolog/monolog/src/Monolog/Processor/MemoryUsageProcessor.php',
    'Monolog\\Processor\\MercurialProcessor' => $vendorDir . '/monolog/monolog/src/Monolog/Processor/MercurialProcessor.php',
    'Monolog\\Processor\\ProcessIdProcessor' => $vendorDir . '/monolog/monolog/src/Monolog/Processor/ProcessIdProcessor.php',
    'Monolog\\Processor\\ProcessorInterface' => $vendorDir . '/monolog/monolog/src/Monolog/Processor/ProcessorInterface.php',
    'Monolog\\Processor\\PsrLogMessageProcessor' => $vendorDir . '/monolog/monolog/src/Monolog/Processor/PsrLogMessageProcessor.php',
    'Monolog\\Processor\\TagProcessor' => $vendorDir . '/monolog/monolog/src/Monolog/Processor/TagProcessor.php',
    'Monolog\\Processor\\UidProcessor' => $vendorDir . '/monolog/monolog/src/Monolog/Processor/UidProcessor.php',
    'Monolog\\Processor\\WebProcessor' => $vendorDir . '/monolog/monolog/src/Monolog/Processor/WebProcessor.php',
    'Monolog\\Registry' => $vendorDir . '/monolog/monolog/src/Monolog/Registry.php',
    'Monolog\\ResettableInterface' => $vendorDir . '/monolog/monolog/src/Monolog/ResettableInterface.php',
    'Monolog\\SignalHandler' => $vendorDir . '/monolog/monolog/src/Monolog/SignalHandler.php',
    'Monolog\\Test\\MonologTestCase' => $vendorDir . '/monolog/monolog/src/Monolog/Test/MonologTestCase.php',
    'Monolog\\Test\\TestCase' => $vendorDir . '/monolog/monolog/src/Monolog/Test/TestCase.php',
    'Monolog\\Utils' => $vendorDir . '/monolog/monolog/src/Monolog/Utils.php',
    'NoDiscard' => $vendorDir . '/symfony/polyfill-php85/Resources/stubs/NoDiscard.php',
    'Override' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/Override.php',
    'ParagonIE\\ConstantTime\\Base32' => $vendorDir . '/paragonie/constant_time_encoding/src/Base32.php',
    'ParagonIE\\ConstantTime\\Base32Hex' => $vendorDir . '/paragonie/constant_time_encoding/src/Base32Hex.php',
    'ParagonIE\\ConstantTime\\Base64' => $vendorDir . '/paragonie/constant_time_encoding/src/Base64.php',
    'ParagonIE\\ConstantTime\\Base64DotSlash' => $vendorDir . '/paragonie/constant_time_encoding/src/Base64DotSlash.php',
    'ParagonIE\\ConstantTime\\Base64DotSlashOrdered' => $vendorDir . '/paragonie/constant_time_encoding/src/Base64DotSlashOrdered.php',
    'ParagonIE\\ConstantTime\\Base64UrlSafe' => $vendorDir . '/paragonie/constant_time_encoding/src/Base64UrlSafe.php',
    'ParagonIE\\ConstantTime\\Binary' => $vendorDir . '/paragonie/constant_time_encoding/src/Binary.php',
    'ParagonIE\\ConstantTime\\EncoderInterface' => $vendorDir . '/paragonie/constant_time_encoding/src/EncoderInterface.php',
    'ParagonIE\\ConstantTime\\Encoding' => $vendorDir . '/paragonie/constant_time_encoding/src/Encoding.php',
    'ParagonIE\\ConstantTime\\Hex' => $vendorDir . '/paragonie/constant_time_encoding/src/Hex.php',
    'ParagonIE\\ConstantTime\\RFC4648' => $vendorDir . '/paragonie/constant_time_encoding/src/RFC4648.php',
    'Pdo\\Dblib' => $vendorDir . '/symfony/polyfill-php84/Resources/stubs/Pdo/Dblib.php',
    'Pdo\\Firebird' => $vendorDir . '/symfony/polyfill-php84/Resources/stubs/Pdo/Firebird.php',
    'Pdo\\Mysql' => $vendorDir . '/symfony/polyfill-php84/Resources/stubs/Pdo/Mysql.php',
    'Pdo\\Odbc' => $vendorDir . '/symfony/polyfill-php84/Resources/stubs/Pdo/Odbc.php',
    'Pdo\\Pgsql' => $vendorDir . '/symfony/polyfill-php84/Resources/stubs/Pdo/Pgsql.php',
    'Pdo\\Sqlite' => $vendorDir . '/symfony/polyfill-php84/Resources/stubs/Pdo/Sqlite.php',
    'PhpParser\\Builder' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Builder.php',
    'PhpParser\\BuilderFactory' => $vendorDir . '/nikic/php-parser/lib/PhpParser/BuilderFactory.php',
    'PhpParser\\BuilderHelpers' => $vendorDir . '/nikic/php-parser/lib/PhpParser/BuilderHelpers.php',
    'PhpParser\\Builder\\ClassConst' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Builder/ClassConst.php',
    'PhpParser\\Builder\\Class_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Builder/Class_.php',
    'PhpParser\\Builder\\Declaration' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Builder/Declaration.php',
    'PhpParser\\Builder\\EnumCase' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Builder/EnumCase.php',
    'PhpParser\\Builder\\Enum_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Builder/Enum_.php',
    'PhpParser\\Builder\\FunctionLike' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Builder/FunctionLike.php',
    'PhpParser\\Builder\\Function_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Builder/Function_.php',
    'PhpParser\\Builder\\Interface_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Builder/Interface_.php',
    'PhpParser\\Builder\\Method' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Builder/Method.php',
    'PhpParser\\Builder\\Namespace_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Builder/Namespace_.php',
    'PhpParser\\Builder\\Param' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Builder/Param.php',
    'PhpParser\\Builder\\Property' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Builder/Property.php',
    'PhpParser\\Builder\\TraitUse' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Builder/TraitUse.php',
    'PhpParser\\Builder\\TraitUseAdaptation' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Builder/TraitUseAdaptation.php',
    'PhpParser\\Builder\\Trait_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Builder/Trait_.php',
    'PhpParser\\Builder\\Use_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Builder/Use_.php',
    'PhpParser\\Comment' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Comment.php',
    'PhpParser\\Comment\\Doc' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Comment/Doc.php',
    'PhpParser\\ConstExprEvaluationException' => $vendorDir . '/nikic/php-parser/lib/PhpParser/ConstExprEvaluationException.php',
    'PhpParser\\ConstExprEvaluator' => $vendorDir . '/nikic/php-parser/lib/PhpParser/ConstExprEvaluator.php',
    'PhpParser\\Error' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Error.php',
    'PhpParser\\ErrorHandler' => $vendorDir . '/nikic/php-parser/lib/PhpParser/ErrorHandler.php',
    'PhpParser\\ErrorHandler\\Collecting' => $vendorDir . '/nikic/php-parser/lib/PhpParser/ErrorHandler/Collecting.php',
    'PhpParser\\ErrorHandler\\Throwing' => $vendorDir . '/nikic/php-parser/lib/PhpParser/ErrorHandler/Throwing.php',
    'PhpParser\\Internal\\DiffElem' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Internal/DiffElem.php',
    'PhpParser\\Internal\\Differ' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Internal/Differ.php',
    'PhpParser\\Internal\\PrintableNewAnonClassNode' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Internal/PrintableNewAnonClassNode.php',
    'PhpParser\\Internal\\TokenPolyfill' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Internal/TokenPolyfill.php',
    'PhpParser\\Internal\\TokenStream' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Internal/TokenStream.php',
    'PhpParser\\JsonDecoder' => $vendorDir . '/nikic/php-parser/lib/PhpParser/JsonDecoder.php',
    'PhpParser\\Lexer' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Lexer.php',
    'PhpParser\\Lexer\\Emulative' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Lexer/Emulative.php',
    'PhpParser\\Lexer\\TokenEmulator\\AsymmetricVisibilityTokenEmulator' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/AsymmetricVisibilityTokenEmulator.php',
    'PhpParser\\Lexer\\TokenEmulator\\AttributeEmulator' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/AttributeEmulator.php',
    'PhpParser\\Lexer\\TokenEmulator\\EnumTokenEmulator' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/EnumTokenEmulator.php',
    'PhpParser\\Lexer\\TokenEmulator\\ExplicitOctalEmulator' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ExplicitOctalEmulator.php',
    'PhpParser\\Lexer\\TokenEmulator\\FnTokenEmulator' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/FnTokenEmulator.php',
    'PhpParser\\Lexer\\TokenEmulator\\KeywordEmulator' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/KeywordEmulator.php',
    'PhpParser\\Lexer\\TokenEmulator\\MatchTokenEmulator' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/MatchTokenEmulator.php',
    'PhpParser\\Lexer\\TokenEmulator\\NullsafeTokenEmulator' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/NullsafeTokenEmulator.php',
    'PhpParser\\Lexer\\TokenEmulator\\PipeOperatorEmulator' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/PipeOperatorEmulator.php',
    'PhpParser\\Lexer\\TokenEmulator\\PropertyTokenEmulator' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/PropertyTokenEmulator.php',
    'PhpParser\\Lexer\\TokenEmulator\\ReadonlyFunctionTokenEmulator' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ReadonlyFunctionTokenEmulator.php',
    'PhpParser\\Lexer\\TokenEmulator\\ReadonlyTokenEmulator' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ReadonlyTokenEmulator.php',
    'PhpParser\\Lexer\\TokenEmulator\\ReverseEmulator' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ReverseEmulator.php',
    'PhpParser\\Lexer\\TokenEmulator\\TokenEmulator' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/TokenEmulator.php',
    'PhpParser\\Lexer\\TokenEmulator\\VoidCastEmulator' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/VoidCastEmulator.php',
    'PhpParser\\Modifiers' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Modifiers.php',
    'PhpParser\\NameContext' => $vendorDir . '/nikic/php-parser/lib/PhpParser/NameContext.php',
    'PhpParser\\Node' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node.php',
    'PhpParser\\NodeAbstract' => $vendorDir . '/nikic/php-parser/lib/PhpParser/NodeAbstract.php',
    'PhpParser\\NodeDumper' => $vendorDir . '/nikic/php-parser/lib/PhpParser/NodeDumper.php',
    'PhpParser\\NodeFinder' => $vendorDir . '/nikic/php-parser/lib/PhpParser/NodeFinder.php',
    'PhpParser\\NodeTraverser' => $vendorDir . '/nikic/php-parser/lib/PhpParser/NodeTraverser.php',
    'PhpParser\\NodeTraverserInterface' => $vendorDir . '/nikic/php-parser/lib/PhpParser/NodeTraverserInterface.php',
    'PhpParser\\NodeVisitor' => $vendorDir . '/nikic/php-parser/lib/PhpParser/NodeVisitor.php',
    'PhpParser\\NodeVisitorAbstract' => $vendorDir . '/nikic/php-parser/lib/PhpParser/NodeVisitorAbstract.php',
    'PhpParser\\NodeVisitor\\CloningVisitor' => $vendorDir . '/nikic/php-parser/lib/PhpParser/NodeVisitor/CloningVisitor.php',
    'PhpParser\\NodeVisitor\\CommentAnnotatingVisitor' => $vendorDir . '/nikic/php-parser/lib/PhpParser/NodeVisitor/CommentAnnotatingVisitor.php',
    'PhpParser\\NodeVisitor\\FindingVisitor' => $vendorDir . '/nikic/php-parser/lib/PhpParser/NodeVisitor/FindingVisitor.php',
    'PhpParser\\NodeVisitor\\FirstFindingVisitor' => $vendorDir . '/nikic/php-parser/lib/PhpParser/NodeVisitor/FirstFindingVisitor.php',
    'PhpParser\\NodeVisitor\\NameResolver' => $vendorDir . '/nikic/php-parser/lib/PhpParser/NodeVisitor/NameResolver.php',
    'PhpParser\\NodeVisitor\\NodeConnectingVisitor' => $vendorDir . '/nikic/php-parser/lib/PhpParser/NodeVisitor/NodeConnectingVisitor.php',
    'PhpParser\\NodeVisitor\\ParentConnectingVisitor' => $vendorDir . '/nikic/php-parser/lib/PhpParser/NodeVisitor/ParentConnectingVisitor.php',
    'PhpParser\\Node\\Arg' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Arg.php',
    'PhpParser\\Node\\ArgPlaceholder' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/ArgPlaceholder.php',
    'PhpParser\\Node\\ArrayItem' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/ArrayItem.php',
    'PhpParser\\Node\\Attribute' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Attribute.php',
    'PhpParser\\Node\\AttributeGroup' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/AttributeGroup.php',
    'PhpParser\\Node\\ClosureUse' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/ClosureUse.php',
    'PhpParser\\Node\\ComplexType' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/ComplexType.php',
    'PhpParser\\Node\\Const_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Const_.php',
    'PhpParser\\Node\\DeclareItem' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/DeclareItem.php',
    'PhpParser\\Node\\Expr' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr.php',
    'PhpParser\\Node\\Expr\\ArrayDimFetch' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/ArrayDimFetch.php',
    'PhpParser\\Node\\Expr\\ArrayItem' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/ArrayItem.php',
    'PhpParser\\Node\\Expr\\Array_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Array_.php',
    'PhpParser\\Node\\Expr\\ArrowFunction' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/ArrowFunction.php',
    'PhpParser\\Node\\Expr\\Assign' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Assign.php',
    'PhpParser\\Node\\Expr\\AssignOp' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp.php',
    'PhpParser\\Node\\Expr\\AssignOp\\BitwiseAnd' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/BitwiseAnd.php',
    'PhpParser\\Node\\Expr\\AssignOp\\BitwiseOr' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/BitwiseOr.php',
    'PhpParser\\Node\\Expr\\AssignOp\\BitwiseXor' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/BitwiseXor.php',
    'PhpParser\\Node\\Expr\\AssignOp\\Coalesce' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Coalesce.php',
    'PhpParser\\Node\\Expr\\AssignOp\\Concat' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Concat.php',
    'PhpParser\\Node\\Expr\\AssignOp\\Div' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Div.php',
    'PhpParser\\Node\\Expr\\AssignOp\\Minus' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Minus.php',
    'PhpParser\\Node\\Expr\\AssignOp\\Mod' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Mod.php',
    'PhpParser\\Node\\Expr\\AssignOp\\Mul' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Mul.php',
    'PhpParser\\Node\\Expr\\AssignOp\\Plus' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Plus.php',
    'PhpParser\\Node\\Expr\\AssignOp\\Pow' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Pow.php',
    'PhpParser\\Node\\Expr\\AssignOp\\ShiftLeft' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/ShiftLeft.php',
    'PhpParser\\Node\\Expr\\AssignOp\\ShiftRight' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/ShiftRight.php',
    'PhpParser\\Node\\Expr\\AssignRef' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignRef.php',
    'PhpParser\\Node\\Expr\\BinaryOp' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp.php',
    'PhpParser\\Node\\Expr\\BinaryOp\\BitwiseAnd' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BitwiseAnd.php',
    'PhpParser\\Node\\Expr\\BinaryOp\\BitwiseOr' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BitwiseOr.php',
    'PhpParser\\Node\\Expr\\BinaryOp\\BitwiseXor' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BitwiseXor.php',
    'PhpParser\\Node\\Expr\\BinaryOp\\BooleanAnd' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BooleanAnd.php',
    'PhpParser\\Node\\Expr\\BinaryOp\\BooleanOr' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BooleanOr.php',
    'PhpParser\\Node\\Expr\\BinaryOp\\Coalesce' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Coalesce.php',
    'PhpParser\\Node\\Expr\\BinaryOp\\Concat' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Concat.php',
    'PhpParser\\Node\\Expr\\BinaryOp\\Div' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Div.php',
    'PhpParser\\Node\\Expr\\BinaryOp\\Equal' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Equal.php',
    'PhpParser\\Node\\Expr\\BinaryOp\\Greater' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Greater.php',
    'PhpParser\\Node\\Expr\\BinaryOp\\GreaterOrEqual' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/GreaterOrEqual.php',
    'PhpParser\\Node\\Expr\\BinaryOp\\Identical' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Identical.php',
    'PhpParser\\Node\\Expr\\BinaryOp\\LogicalAnd' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/LogicalAnd.php',
    'PhpParser\\Node\\Expr\\BinaryOp\\LogicalOr' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/LogicalOr.php',
    'PhpParser\\Node\\Expr\\BinaryOp\\LogicalXor' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/LogicalXor.php',
    'PhpParser\\Node\\Expr\\BinaryOp\\Minus' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Minus.php',
    'PhpParser\\Node\\Expr\\BinaryOp\\Mod' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Mod.php',
    'PhpParser\\Node\\Expr\\BinaryOp\\Mul' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Mul.php',
    'PhpParser\\Node\\Expr\\BinaryOp\\NotEqual' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/NotEqual.php',
    'PhpParser\\Node\\Expr\\BinaryOp\\NotIdentical' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/NotIdentical.php',
    'PhpParser\\Node\\Expr\\BinaryOp\\Pipe' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Pipe.php',
    'PhpParser\\Node\\Expr\\BinaryOp\\Plus' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Plus.php',
    'PhpParser\\Node\\Expr\\BinaryOp\\Pow' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Pow.php',
    'PhpParser\\Node\\Expr\\BinaryOp\\ShiftLeft' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/ShiftLeft.php',
    'PhpParser\\Node\\Expr\\BinaryOp\\ShiftRight' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/ShiftRight.php',
    'PhpParser\\Node\\Expr\\BinaryOp\\Smaller' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Smaller.php',
    'PhpParser\\Node\\Expr\\BinaryOp\\SmallerOrEqual' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/SmallerOrEqual.php',
    'PhpParser\\Node\\Expr\\BinaryOp\\Spaceship' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Spaceship.php',
    'PhpParser\\Node\\Expr\\BitwiseNot' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BitwiseNot.php',
    'PhpParser\\Node\\Expr\\BooleanNot' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BooleanNot.php',
    'PhpParser\\Node\\Expr\\CallLike' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/CallLike.php',
    'PhpParser\\Node\\Expr\\Cast' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Cast.php',
    'PhpParser\\Node\\Expr\\Cast\\Array_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Array_.php',
    'PhpParser\\Node\\Expr\\Cast\\Bool_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Bool_.php',
    'PhpParser\\Node\\Expr\\Cast\\Double' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Double.php',
    'PhpParser\\Node\\Expr\\Cast\\Int_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Int_.php',
    'PhpParser\\Node\\Expr\\Cast\\Object_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Object_.php',
    'PhpParser\\Node\\Expr\\Cast\\String_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/String_.php',
    'PhpParser\\Node\\Expr\\Cast\\Unset_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Unset_.php',
    'PhpParser\\Node\\Expr\\Cast\\Void_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Void_.php',
    'PhpParser\\Node\\Expr\\ClassConstFetch' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/ClassConstFetch.php',
    'PhpParser\\Node\\Expr\\Clone_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Clone_.php',
    'PhpParser\\Node\\Expr\\Closure' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Closure.php',
    'PhpParser\\Node\\Expr\\ClosureUse' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/ClosureUse.php',
    'PhpParser\\Node\\Expr\\ConstFetch' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/ConstFetch.php',
    'PhpParser\\Node\\Expr\\Empty_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Empty_.php',
    'PhpParser\\Node\\Expr\\Error' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Error.php',
    'PhpParser\\Node\\Expr\\ErrorSuppress' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/ErrorSuppress.php',
    'PhpParser\\Node\\Expr\\Eval_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Eval_.php',
    'PhpParser\\Node\\Expr\\Exit_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Exit_.php',
    'PhpParser\\Node\\Expr\\FuncCall' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/FuncCall.php',
    'PhpParser\\Node\\Expr\\Include_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Include_.php',
    'PhpParser\\Node\\Expr\\Instanceof_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Instanceof_.php',
    'PhpParser\\Node\\Expr\\Isset_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Isset_.php',
    'PhpParser\\Node\\Expr\\List_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/List_.php',
    'PhpParser\\Node\\Expr\\Match_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Match_.php',
    'PhpParser\\Node\\Expr\\MethodCall' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/MethodCall.php',
    'PhpParser\\Node\\Expr\\New_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/New_.php',
    'PhpParser\\Node\\Expr\\NullsafeMethodCall' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/NullsafeMethodCall.php',
    'PhpParser\\Node\\Expr\\NullsafePropertyFetch' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/NullsafePropertyFetch.php',
    'PhpParser\\Node\\Expr\\PostDec' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/PostDec.php',
    'PhpParser\\Node\\Expr\\PostInc' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/PostInc.php',
    'PhpParser\\Node\\Expr\\PreDec' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/PreDec.php',
    'PhpParser\\Node\\Expr\\PreInc' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/PreInc.php',
    'PhpParser\\Node\\Expr\\Print_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Print_.php',
    'PhpParser\\Node\\Expr\\PropertyFetch' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/PropertyFetch.php',
    'PhpParser\\Node\\Expr\\ShellExec' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/ShellExec.php',
    'PhpParser\\Node\\Expr\\StaticCall' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/StaticCall.php',
    'PhpParser\\Node\\Expr\\StaticPropertyFetch' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/StaticPropertyFetch.php',
    'PhpParser\\Node\\Expr\\Ternary' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Ternary.php',
    'PhpParser\\Node\\Expr\\Throw_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Throw_.php',
    'PhpParser\\Node\\Expr\\UnaryMinus' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/UnaryMinus.php',
    'PhpParser\\Node\\Expr\\UnaryPlus' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/UnaryPlus.php',
    'PhpParser\\Node\\Expr\\Variable' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Variable.php',
    'PhpParser\\Node\\Expr\\YieldFrom' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/YieldFrom.php',
    'PhpParser\\Node\\Expr\\Yield_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Yield_.php',
    'PhpParser\\Node\\FunctionLike' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/FunctionLike.php',
    'PhpParser\\Node\\Identifier' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Identifier.php',
    'PhpParser\\Node\\InterpolatedStringPart' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/InterpolatedStringPart.php',
    'PhpParser\\Node\\IntersectionType' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/IntersectionType.php',
    'PhpParser\\Node\\MatchArm' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/MatchArm.php',
    'PhpParser\\Node\\Name' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Name.php',
    'PhpParser\\Node\\Name\\FullyQualified' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Name/FullyQualified.php',
    'PhpParser\\Node\\Name\\Relative' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Name/Relative.php',
    'PhpParser\\Node\\NullableType' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/NullableType.php',
    'PhpParser\\Node\\Param' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Param.php',
    'PhpParser\\Node\\PropertyHook' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/PropertyHook.php',
    'PhpParser\\Node\\PropertyItem' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/PropertyItem.php',
    'PhpParser\\Node\\Scalar' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Scalar.php',
    'PhpParser\\Node\\Scalar\\DNumber' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Scalar/DNumber.php',
    'PhpParser\\Node\\Scalar\\Encapsed' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Scalar/Encapsed.php',
    'PhpParser\\Node\\Scalar\\EncapsedStringPart' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Scalar/EncapsedStringPart.php',
    'PhpParser\\Node\\Scalar\\Float_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Scalar/Float_.php',
    'PhpParser\\Node\\Scalar\\Int_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Scalar/Int_.php',
    'PhpParser\\Node\\Scalar\\InterpolatedString' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Scalar/InterpolatedString.php',
    'PhpParser\\Node\\Scalar\\LNumber' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Scalar/LNumber.php',
    'PhpParser\\Node\\Scalar\\MagicConst' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst.php',
    'PhpParser\\Node\\Scalar\\MagicConst\\Class_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Class_.php',
    'PhpParser\\Node\\Scalar\\MagicConst\\Dir' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Dir.php',
    'PhpParser\\Node\\Scalar\\MagicConst\\File' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/File.php',
    'PhpParser\\Node\\Scalar\\MagicConst\\Function_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Function_.php',
    'PhpParser\\Node\\Scalar\\MagicConst\\Line' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Line.php',
    'PhpParser\\Node\\Scalar\\MagicConst\\Method' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Method.php',
    'PhpParser\\Node\\Scalar\\MagicConst\\Namespace_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Namespace_.php',
    'PhpParser\\Node\\Scalar\\MagicConst\\Property' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Property.php',
    'PhpParser\\Node\\Scalar\\MagicConst\\Trait_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Trait_.php',
    'PhpParser\\Node\\Scalar\\String_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Scalar/String_.php',
    'PhpParser\\Node\\StaticVar' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/StaticVar.php',
    'PhpParser\\Node\\Stmt' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt.php',
    'PhpParser\\Node\\Stmt\\Block' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Block.php',
    'PhpParser\\Node\\Stmt\\Break_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Break_.php',
    'PhpParser\\Node\\Stmt\\Case_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Case_.php',
    'PhpParser\\Node\\Stmt\\Catch_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Catch_.php',
    'PhpParser\\Node\\Stmt\\ClassConst' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/ClassConst.php',
    'PhpParser\\Node\\Stmt\\ClassLike' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/ClassLike.php',
    'PhpParser\\Node\\Stmt\\ClassMethod' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/ClassMethod.php',
    'PhpParser\\Node\\Stmt\\Class_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Class_.php',
    'PhpParser\\Node\\Stmt\\Const_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Const_.php',
    'PhpParser\\Node\\Stmt\\Continue_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Continue_.php',
    'PhpParser\\Node\\Stmt\\DeclareDeclare' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/DeclareDeclare.php',
    'PhpParser\\Node\\Stmt\\Declare_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Declare_.php',
    'PhpParser\\Node\\Stmt\\Do_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Do_.php',
    'PhpParser\\Node\\Stmt\\Echo_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Echo_.php',
    'PhpParser\\Node\\Stmt\\ElseIf_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/ElseIf_.php',
    'PhpParser\\Node\\Stmt\\Else_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Else_.php',
    'PhpParser\\Node\\Stmt\\EnumCase' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/EnumCase.php',
    'PhpParser\\Node\\Stmt\\Enum_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Enum_.php',
    'PhpParser\\Node\\Stmt\\Expression' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Expression.php',
    'PhpParser\\Node\\Stmt\\Finally_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Finally_.php',
    'PhpParser\\Node\\Stmt\\For_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/For_.php',
    'PhpParser\\Node\\Stmt\\Foreach_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Foreach_.php',
    'PhpParser\\Node\\Stmt\\Function_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Function_.php',
    'PhpParser\\Node\\Stmt\\Global_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Global_.php',
    'PhpParser\\Node\\Stmt\\Goto_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Goto_.php',
    'PhpParser\\Node\\Stmt\\GroupUse' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/GroupUse.php',
    'PhpParser\\Node\\Stmt\\HaltCompiler' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/HaltCompiler.php',
    'PhpParser\\Node\\Stmt\\If_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/If_.php',
    'PhpParser\\Node\\Stmt\\InlineHTML' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/InlineHTML.php',
    'PhpParser\\Node\\Stmt\\Interface_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Interface_.php',
    'PhpParser\\Node\\Stmt\\Label' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Label.php',
    'PhpParser\\Node\\Stmt\\Namespace_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Namespace_.php',
    'PhpParser\\Node\\Stmt\\Nop' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Nop.php',
    'PhpParser\\Node\\Stmt\\Property' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Property.php',
    'PhpParser\\Node\\Stmt\\PropertyProperty' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/PropertyProperty.php',
    'PhpParser\\Node\\Stmt\\Return_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Return_.php',
    'PhpParser\\Node\\Stmt\\StaticVar' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/StaticVar.php',
    'PhpParser\\Node\\Stmt\\Static_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Static_.php',
    'PhpParser\\Node\\Stmt\\Switch_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Switch_.php',
    'PhpParser\\Node\\Stmt\\TraitUse' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/TraitUse.php',
    'PhpParser\\Node\\Stmt\\TraitUseAdaptation' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/TraitUseAdaptation.php',
    'PhpParser\\Node\\Stmt\\TraitUseAdaptation\\Alias' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/TraitUseAdaptation/Alias.php',
    'PhpParser\\Node\\Stmt\\TraitUseAdaptation\\Precedence' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/TraitUseAdaptation/Precedence.php',
    'PhpParser\\Node\\Stmt\\Trait_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Trait_.php',
    'PhpParser\\Node\\Stmt\\TryCatch' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/TryCatch.php',
    'PhpParser\\Node\\Stmt\\Unset_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Unset_.php',
    'PhpParser\\Node\\Stmt\\UseUse' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/UseUse.php',
    'PhpParser\\Node\\Stmt\\Use_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Use_.php',
    'PhpParser\\Node\\Stmt\\While_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/While_.php',
    'PhpParser\\Node\\UnionType' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/UnionType.php',
    'PhpParser\\Node\\UseItem' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/UseItem.php',
    'PhpParser\\Node\\VarLikeIdentifier' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/VarLikeIdentifier.php',
    'PhpParser\\Node\\VariadicPlaceholder' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/VariadicPlaceholder.php',
    'PhpParser\\Parser' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Parser.php',
    'PhpParser\\ParserAbstract' => $vendorDir . '/nikic/php-parser/lib/PhpParser/ParserAbstract.php',
    'PhpParser\\ParserFactory' => $vendorDir . '/nikic/php-parser/lib/PhpParser/ParserFactory.php',
    'PhpParser\\Parser\\Php7' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Parser/Php7.php',
    'PhpParser\\Parser\\Php8' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Parser/Php8.php',
    'PhpParser\\PhpVersion' => $vendorDir . '/nikic/php-parser/lib/PhpParser/PhpVersion.php',
    'PhpParser\\PrettyPrinter' => $vendorDir . '/nikic/php-parser/lib/PhpParser/PrettyPrinter.php',
    'PhpParser\\PrettyPrinterAbstract' => $vendorDir . '/nikic/php-parser/lib/PhpParser/PrettyPrinterAbstract.php',
    'PhpParser\\PrettyPrinter\\Standard' => $vendorDir . '/nikic/php-parser/lib/PhpParser/PrettyPrinter/Standard.php',
    'PhpParser\\Token' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Token.php',
    'Prometheus\\Collector' => $vendorDir . '/promphp/prometheus_client_php/src/Prometheus/Collector.php',
    'Prometheus\\CollectorRegistry' => $vendorDir . '/promphp/prometheus_client_php/src/Prometheus/CollectorRegistry.php',
    'Prometheus\\Counter' => $vendorDir . '/promphp/prometheus_client_php/src/Prometheus/Counter.php',
    'Prometheus\\Exception\\MetricJsonException' => $vendorDir . '/promphp/prometheus_client_php/src/Prometheus/Exception/MetricJsonException.php',
    'Prometheus\\Exception\\MetricNotFoundException' => $vendorDir . '/promphp/prometheus_client_php/src/Prometheus/Exception/MetricNotFoundException.php',
    'Prometheus\\Exception\\MetricsRegistrationException' => $vendorDir . '/promphp/prometheus_client_php/src/Prometheus/Exception/MetricsRegistrationException.php',
    'Prometheus\\Exception\\StorageException' => $vendorDir . '/promphp/prometheus_client_php/src/Prometheus/Exception/StorageException.php',
    'Prometheus\\Gauge' => $vendorDir . '/promphp/prometheus_client_php/src/Prometheus/Gauge.php',
    'Prometheus\\Histogram' => $vendorDir . '/promphp/prometheus_client_php/src/Prometheus/Histogram.php',
    'Prometheus\\Math' => $vendorDir . '/promphp/prometheus_client_php/src/Prometheus/Math.php',
    'Prometheus\\MetricFamilySamples' => $vendorDir . '/promphp/prometheus_client_php/src/Prometheus/MetricFamilySamples.php',
    'Prometheus\\RegistryInterface' => $vendorDir . '/promphp/prometheus_client_php/src/Prometheus/RegistryInterface.php',
    'Prometheus\\RenderTextFormat' => $vendorDir . '/promphp/prometheus_client_php/src/Prometheus/RenderTextFormat.php',
    'Prometheus\\RendererInterface' => $vendorDir . '/promphp/prometheus_client_php/src/Prometheus/RendererInterface.php',
    'Prometheus\\Sample' => $vendorDir . '/promphp/prometheus_client_php/src/Prometheus/Sample.php',
    'Prometheus\\Storage\\APC' => $vendorDir . '/promphp/prometheus_client_php/src/Prometheus/Storage/APC.php',
    'Prometheus\\Storage\\APCng' => $vendorDir . '/promphp/prometheus_client_php/src/Prometheus/Storage/APCng.php',
    'Prometheus\\Storage\\AbstractRedis' => $vendorDir . '/promphp/prometheus_client_php/src/Prometheus/Storage/AbstractRedis.php',
    'Prometheus\\Storage\\Adapter' => $vendorDir . '/promphp/prometheus_client_php/src/Prometheus/Storage/Adapter.php',
    'Prometheus\\Storage\\InMemory' => $vendorDir . '/promphp/prometheus_client_php/src/Prometheus/Storage/InMemory.php',
    'Prometheus\\Storage\\PDO' => $vendorDir . '/promphp/prometheus_client_php/src/Prometheus/Storage/PDO.php',
    'Prometheus\\Storage\\Predis' => $vendorDir . '/promphp/prometheus_client_php/src/Prometheus/Storage/Predis.php',
    'Prometheus\\Storage\\Redis' => $vendorDir . '/promphp/prometheus_client_php/src/Prometheus/Storage/Redis.php',
    'Prometheus\\Storage\\RedisClients\\PHPRedis' => $vendorDir . '/promphp/prometheus_client_php/src/Prometheus/Storage/RedisClients/PHPRedis.php',
    'Prometheus\\Storage\\RedisClients\\Predis' => $vendorDir . '/promphp/prometheus_client_php/src/Prometheus/Storage/RedisClients/Predis.php',
    'Prometheus\\Storage\\RedisClients\\RedisClient' => $vendorDir . '/promphp/prometheus_client_php/src/Prometheus/Storage/RedisClients/RedisClient.php',
    'Prometheus\\Storage\\RedisClients\\RedisClientException' => $vendorDir . '/promphp/prometheus_client_php/src/Prometheus/Storage/RedisClients/RedisClientException.php',
    'Prometheus\\Storage\\RedisNg' => $vendorDir . '/promphp/prometheus_client_php/src/Prometheus/Storage/RedisNg.php',
    'Prometheus\\Summary' => $vendorDir . '/promphp/prometheus_client_php/src/Prometheus/Summary.php',
    'Psr\\Http\\Message\\MessageInterface' => $vendorDir . '/psr/http-message/src/MessageInterface.php',
    'Psr\\Http\\Message\\RequestFactoryInterface' => $vendorDir . '/psr/http-factory/src/RequestFactoryInterface.php',
    'Psr\\Http\\Message\\RequestInterface' => $vendorDir . '/psr/http-message/src/RequestInterface.php',
    'Psr\\Http\\Message\\ResponseFactoryInterface' => $vendorDir . '/psr/http-factory/src/ResponseFactoryInterface.php',
    'Psr\\Http\\Message\\ResponseInterface' => $vendorDir . '/psr/http-message/src/ResponseInterface.php',
    'Psr\\Http\\Message\\ServerRequestFactoryInterface' => $vendorDir . '/psr/http-factory/src/ServerRequestFactoryInterface.php',
    'Psr\\Http\\Message\\ServerRequestInterface' => $vendorDir . '/psr/http-message/src/ServerRequestInterface.php',
    'Psr\\Http\\Message\\StreamFactoryInterface' => $vendorDir . '/psr/http-factory/src/StreamFactoryInterface.php',
    'Psr\\Http\\Message\\StreamInterface' => $vendorDir . '/psr/http-message/src/StreamInterface.php',
    'Psr\\Http\\Message\\UploadedFileFactoryInterface' => $vendorDir . '/psr/http-factory/src/UploadedFileFactoryInterface.php',
    'Psr\\Http\\Message\\UploadedFileInterface' => $vendorDir . '/psr/http-message/src/UploadedFileInterface.php',
    'Psr\\Http\\Message\\UriFactoryInterface' => $vendorDir . '/psr/http-factory/src/UriFactoryInterface.php',
    'Psr\\Http\\Message\\UriInterface' => $vendorDir . '/psr/http-message/src/UriInterface.php',
    'Psr\\Log\\AbstractLogger' => $vendorDir . '/psr/log/src/AbstractLogger.php',
    'Psr\\Log\\InvalidArgumentException' => $vendorDir . '/psr/log/src/InvalidArgumentException.php',
    'Psr\\Log\\LogLevel' => $vendorDir . '/psr/log/src/LogLevel.php',
    'Psr\\Log\\LoggerAwareInterface' => $vendorDir . '/psr/log/src/LoggerAwareInterface.php',
    'Psr\\Log\\LoggerAwareTrait' => $vendorDir . '/psr/log/src/LoggerAwareTrait.php',
    'Psr\\Log\\LoggerInterface' => $vendorDir . '/psr/log/src/LoggerInterface.php',
    'Psr\\Log\\LoggerTrait' => $vendorDir . '/psr/log/src/LoggerTrait.php',
    'Psr\\Log\\NullLogger' => $vendorDir . '/psr/log/src/NullLogger.php',
    'Ramsey\\Collection\\AbstractArray' => $vendorDir . '/ramsey/collection/src/AbstractArray.php',
    'Ramsey\\Collection\\AbstractCollection' => $vendorDir . '/ramsey/collection/src/AbstractCollection.php',
    'Ramsey\\Collection\\AbstractSet' => $vendorDir . '/ramsey/collection/src/AbstractSet.php',
    'Ramsey\\Collection\\ArrayInterface' => $vendorDir . '/ramsey/collection/src/ArrayInterface.php',
    'Ramsey\\Collection\\Collection' => $vendorDir . '/ramsey/collection/src/Collection.php',
    'Ramsey\\Collection\\CollectionInterface' => $vendorDir . '/ramsey/collection/src/CollectionInterface.php',
    'Ramsey\\Collection\\DoubleEndedQueue' => $vendorDir . '/ramsey/collection/src/DoubleEndedQueue.php',
    'Ramsey\\Collection\\DoubleEndedQueueInterface' => $vendorDir . '/ramsey/collection/src/DoubleEndedQueueInterface.php',
    'Ramsey\\Collection\\Exception\\CollectionException' => $vendorDir . '/ramsey/collection/src/Exception/CollectionException.php',
    'Ramsey\\Collection\\Exception\\CollectionMismatchException' => $vendorDir . '/ramsey/collection/src/Exception/CollectionMismatchException.php',
    'Ramsey\\Collection\\Exception\\InvalidArgumentException' => $vendorDir . '/ramsey/collection/src/Exception/InvalidArgumentException.php',
    'Ramsey\\Collection\\Exception\\InvalidPropertyOrMethod' => $vendorDir . '/ramsey/collection/src/Exception/InvalidPropertyOrMethod.php',
    'Ramsey\\Collection\\Exception\\NoSuchElementException' => $vendorDir . '/ramsey/collection/src/Exception/NoSuchElementException.php',
    'Ramsey\\Collection\\Exception\\OutOfBoundsException' => $vendorDir . '/ramsey/collection/src/Exception/OutOfBoundsException.php',
    'Ramsey\\Collection\\Exception\\UnsupportedOperationException' => $vendorDir . '/ramsey/collection/src/Exception/UnsupportedOperationException.php',
    'Ramsey\\Collection\\GenericArray' => $vendorDir . '/ramsey/collection/src/GenericArray.php',
    'Ramsey\\Collection\\Map\\AbstractMap' => $vendorDir . '/ramsey/collection/src/Map/AbstractMap.php',
    'Ramsey\\Collection\\Map\\AbstractTypedMap' => $vendorDir . '/ramsey/collection/src/Map/AbstractTypedMap.php',
    'Ramsey\\Collection\\Map\\AssociativeArrayMap' => $vendorDir . '/ramsey/collection/src/Map/AssociativeArrayMap.php',
    'Ramsey\\Collection\\Map\\MapInterface' => $vendorDir . '/ramsey/collection/src/Map/MapInterface.php',
    'Ramsey\\Collection\\Map\\NamedParameterMap' => $vendorDir . '/ramsey/collection/src/Map/NamedParameterMap.php',
    'Ramsey\\Collection\\Map\\TypedMap' => $vendorDir . '/ramsey/collection/src/Map/TypedMap.php',
    'Ramsey\\Collection\\Map\\TypedMapInterface' => $vendorDir . '/ramsey/collection/src/Map/TypedMapInterface.php',
    'Ramsey\\Collection\\Queue' => $vendorDir . '/ramsey/collection/src/Queue.php',
    'Ramsey\\Collection\\QueueInterface' => $vendorDir . '/ramsey/collection/src/QueueInterface.php',
    'Ramsey\\Collection\\Set' => $vendorDir . '/ramsey/collection/src/Set.php',
    'Ramsey\\Collection\\Sort' => $vendorDir . '/ramsey/collection/src/Sort.php',
    'Ramsey\\Collection\\Tool\\TypeTrait' => $vendorDir . '/ramsey/collection/src/Tool/TypeTrait.php',
    'Ramsey\\Collection\\Tool\\ValueExtractorTrait' => $vendorDir . '/ramsey/collection/src/Tool/ValueExtractorTrait.php',
    'Ramsey\\Collection\\Tool\\ValueToStringTrait' => $vendorDir . '/ramsey/collection/src/Tool/ValueToStringTrait.php',
    'Ramsey\\Uuid\\BinaryUtils' => $vendorDir . '/ramsey/uuid/src/BinaryUtils.php',
    'Ramsey\\Uuid\\Builder\\BuilderCollection' => $vendorDir . '/ramsey/uuid/src/Builder/BuilderCollection.php',
    'Ramsey\\Uuid\\Builder\\DefaultUuidBuilder' => $vendorDir . '/ramsey/uuid/src/Builder/DefaultUuidBuilder.php',
    'Ramsey\\Uuid\\Builder\\DegradedUuidBuilder' => $vendorDir . '/ramsey/uuid/src/Builder/DegradedUuidBuilder.php',
    'Ramsey\\Uuid\\Builder\\FallbackBuilder' => $vendorDir . '/ramsey/uuid/src/Builder/FallbackBuilder.php',
    'Ramsey\\Uuid\\Builder\\UuidBuilderInterface' => $vendorDir . '/ramsey/uuid/src/Builder/UuidBuilderInterface.php',
    'Ramsey\\Uuid\\Codec\\CodecInterface' => $vendorDir . '/ramsey/uuid/src/Codec/CodecInterface.php',
    'Ramsey\\Uuid\\Codec\\GuidStringCodec' => $vendorDir . '/ramsey/uuid/src/Codec/GuidStringCodec.php',
    'Ramsey\\Uuid\\Codec\\OrderedTimeCodec' => $vendorDir . '/ramsey/uuid/src/Codec/OrderedTimeCodec.php',
    'Ramsey\\Uuid\\Codec\\StringCodec' => $vendorDir . '/ramsey/uuid/src/Codec/StringCodec.php',
    'Ramsey\\Uuid\\Codec\\TimestampFirstCombCodec' => $vendorDir . '/ramsey/uuid/src/Codec/TimestampFirstCombCodec.php',
    'Ramsey\\Uuid\\Codec\\TimestampLastCombCodec' => $vendorDir . '/ramsey/uuid/src/Codec/TimestampLastCombCodec.php',
    'Ramsey\\Uuid\\Converter\\NumberConverterInterface' => $vendorDir . '/ramsey/uuid/src/Converter/NumberConverterInterface.php',
    'Ramsey\\Uuid\\Converter\\Number\\BigNumberConverter' => $vendorDir . '/ramsey/uuid/src/Converter/Number/BigNumberConverter.php',
    'Ramsey\\Uuid\\Converter\\Number\\DegradedNumberConverter' => $vendorDir . '/ramsey/uuid/src/Converter/Number/DegradedNumberConverter.php',
    'Ramsey\\Uuid\\Converter\\Number\\GenericNumberConverter' => $vendorDir . '/ramsey/uuid/src/Converter/Number/GenericNumberConverter.php',
    'Ramsey\\Uuid\\Converter\\TimeConverterInterface' => $vendorDir . '/ramsey/uuid/src/Converter/TimeConverterInterface.php',
    'Ramsey\\Uuid\\Converter\\Time\\BigNumberTimeConverter' => $vendorDir . '/ramsey/uuid/src/Converter/Time/BigNumberTimeConverter.php',
    'Ramsey\\Uuid\\Converter\\Time\\DegradedTimeConverter' => $vendorDir . '/ramsey/uuid/src/Converter/Time/DegradedTimeConverter.php',
    'Ramsey\\Uuid\\Converter\\Time\\GenericTimeConverter' => $vendorDir . '/ramsey/uuid/src/Converter/Time/GenericTimeConverter.php',
    'Ramsey\\Uuid\\Converter\\Time\\PhpTimeConverter' => $vendorDir . '/ramsey/uuid/src/Converter/Time/PhpTimeConverter.php',
    'Ramsey\\Uuid\\Converter\\Time\\UnixTimeConverter' => $vendorDir . '/ramsey/uuid/src/Converter/Time/UnixTimeConverter.php',
    'Ramsey\\Uuid\\DegradedUuid' => $vendorDir . '/ramsey/uuid/src/DegradedUuid.php',
    'Ramsey\\Uuid\\DeprecatedUuidInterface' => $vendorDir . '/ramsey/uuid/src/DeprecatedUuidInterface.php',
    'Ramsey\\Uuid\\DeprecatedUuidMethodsTrait' => $vendorDir . '/ramsey/uuid/src/DeprecatedUuidMethodsTrait.php',
    'Ramsey\\Uuid\\Exception\\BuilderNotFoundException' => $vendorDir . '/ramsey/uuid/src/Exception/BuilderNotFoundException.php',
    'Ramsey\\Uuid\\Exception\\DateTimeException' => $vendorDir . '/ramsey/uuid/src/Exception/DateTimeException.php',
    'Ramsey\\Uuid\\Exception\\DceSecurityException' => $vendorDir . '/ramsey/uuid/src/Exception/DceSecurityException.php',
    'Ramsey\\Uuid\\Exception\\InvalidArgumentException' => $vendorDir . '/ramsey/uuid/src/Exception/InvalidArgumentException.php',
    'Ramsey\\Uuid\\Exception\\InvalidBytesException' => $vendorDir . '/ramsey/uuid/src/Exception/InvalidBytesException.php',
    'Ramsey\\Uuid\\Exception\\InvalidUuidStringException' => $vendorDir . '/ramsey/uuid/src/Exception/InvalidUuidStringException.php',
    'Ramsey\\Uuid\\Exception\\NameException' => $vendorDir . '/ramsey/uuid/src/Exception/NameException.php',
    'Ramsey\\Uuid\\Exception\\NodeException' => $vendorDir . '/ramsey/uuid/src/Exception/NodeException.php',
    'Ramsey\\Uuid\\Exception\\RandomSourceException' => $vendorDir . '/ramsey/uuid/src/Exception/RandomSourceException.php',
    'Ramsey\\Uuid\\Exception\\TimeSourceException' => $vendorDir . '/ramsey/uuid/src/Exception/TimeSourceException.php',
    'Ramsey\\Uuid\\Exception\\UnableToBuildUuidException' => $vendorDir . '/ramsey/uuid/src/Exception/UnableToBuildUuidException.php',
    'Ramsey\\Uuid\\Exception\\UnsupportedOperationException' => $vendorDir . '/ramsey/uuid/src/Exception/UnsupportedOperationException.php',
    'Ramsey\\Uuid\\Exception\\UuidExceptionInterface' => $vendorDir . '/ramsey/uuid/src/Exception/UuidExceptionInterface.php',
    'Ramsey\\Uuid\\FeatureSet' => $vendorDir . '/ramsey/uuid/src/FeatureSet.php',
    'Ramsey\\Uuid\\Fields\\FieldsInterface' => $vendorDir . '/ramsey/uuid/src/Fields/FieldsInterface.php',
    'Ramsey\\Uuid\\Fields\\SerializableFieldsTrait' => $vendorDir . '/ramsey/uuid/src/Fields/SerializableFieldsTrait.php',
    'Ramsey\\Uuid\\Generator\\CombGenerator' => $vendorDir . '/ramsey/uuid/src/Generator/CombGenerator.php',
    'Ramsey\\Uuid\\Generator\\DceSecurityGenerator' => $vendorDir . '/ramsey/uuid/src/Generator/DceSecurityGenerator.php',
    'Ramsey\\Uuid\\Generator\\DceSecurityGeneratorInterface' => $vendorDir . '/ramsey/uuid/src/Generator/DceSecurityGeneratorInterface.php',
    'Ramsey\\Uuid\\Generator\\DefaultNameGenerator' => $vendorDir . '/ramsey/uuid/src/Generator/DefaultNameGenerator.php',
    'Ramsey\\Uuid\\Generator\\DefaultTimeGenerator' => $vendorDir . '/ramsey/uuid/src/Generator/DefaultTimeGenerator.php',
    'Ramsey\\Uuid\\Generator\\NameGeneratorFactory' => $vendorDir . '/ramsey/uuid/src/Generator/NameGeneratorFactory.php',
    'Ramsey\\Uuid\\Generator\\NameGeneratorInterface' => $vendorDir . '/ramsey/uuid/src/Generator/NameGeneratorInterface.php',
    'Ramsey\\Uuid\\Generator\\PeclUuidNameGenerator' => $vendorDir . '/ramsey/uuid/src/Generator/PeclUuidNameGenerator.php',
    'Ramsey\\Uuid\\Generator\\PeclUuidRandomGenerator' => $vendorDir . '/ramsey/uuid/src/Generator/PeclUuidRandomGenerator.php',
    'Ramsey\\Uuid\\Generator\\PeclUuidTimeGenerator' => $vendorDir . '/ramsey/uuid/src/Generator/PeclUuidTimeGenerator.php',
    'Ramsey\\Uuid\\Generator\\RandomBytesGenerator' => $vendorDir . '/ramsey/uuid/src/Generator/RandomBytesGenerator.php',
    'Ramsey\\Uuid\\Generator\\RandomGeneratorFactory' => $vendorDir . '/ramsey/uuid/src/Generator/RandomGeneratorFactory.php',
    'Ramsey\\Uuid\\Generator\\RandomGeneratorInterface' => $vendorDir . '/ramsey/uuid/src/Generator/RandomGeneratorInterface.php',
    'Ramsey\\Uuid\\Generator\\RandomLibAdapter' => $vendorDir . '/ramsey/uuid/src/Generator/RandomLibAdapter.php',
    'Ramsey\\Uuid\\Generator\\TimeGeneratorFactory' => $vendorDir . '/ramsey/uuid/src/Generator/TimeGeneratorFactory.php',
    'Ramsey\\Uuid\\Generator\\TimeGeneratorInterface' => $vendorDir . '/ramsey/uuid/src/Generator/TimeGeneratorInterface.php',
    'Ramsey\\Uuid\\Generator\\UnixTimeGenerator' => $vendorDir . '/ramsey/uuid/src/Generator/UnixTimeGenerator.php',
    'Ramsey\\Uuid\\Guid\\Fields' => $vendorDir . '/ramsey/uuid/src/Guid/Fields.php',
    'Ramsey\\Uuid\\Guid\\Guid' => $vendorDir . '/ramsey/uuid/src/Guid/Guid.php',
    'Ramsey\\Uuid\\Guid\\GuidBuilder' => $vendorDir . '/ramsey/uuid/src/Guid/GuidBuilder.php',
    'Ramsey\\Uuid\\Lazy\\LazyUuidFromString' => $vendorDir . '/ramsey/uuid/src/Lazy/LazyUuidFromString.php',
    'Ramsey\\Uuid\\Math\\BrickMathCalculator' => $vendorDir . '/ramsey/uuid/src/Math/BrickMathCalculator.php',
    'Ramsey\\Uuid\\Math\\BrickMathRoundingMode' => $vendorDir . '/ramsey/uuid/src/Math/BrickMathRoundingMode.php',
    'Ramsey\\Uuid\\Math\\CalculatorInterface' => $vendorDir . '/ramsey/uuid/src/Math/CalculatorInterface.php',
    'Ramsey\\Uuid\\Math\\RoundingMode' => $vendorDir . '/ramsey/uuid/src/Math/RoundingMode.php',
    'Ramsey\\Uuid\\Nonstandard\\Fields' => $vendorDir . '/ramsey/uuid/src/Nonstandard/Fields.php',
    'Ramsey\\Uuid\\Nonstandard\\Uuid' => $vendorDir . '/ramsey/uuid/src/Nonstandard/Uuid.php',
    'Ramsey\\Uuid\\Nonstandard\\UuidBuilder' => $vendorDir . '/ramsey/uuid/src/Nonstandard/UuidBuilder.php',
    'Ramsey\\Uuid\\Nonstandard\\UuidV6' => $vendorDir . '/ramsey/uuid/src/Nonstandard/UuidV6.php',
    'Ramsey\\Uuid\\Provider\\DceSecurityProviderInterface' => $vendorDir . '/ramsey/uuid/src/Provider/DceSecurityProviderInterface.php',
    'Ramsey\\Uuid\\Provider\\Dce\\SystemDceSecurityProvider' => $vendorDir . '/ramsey/uuid/src/Provider/Dce/SystemDceSecurityProvider.php',
    'Ramsey\\Uuid\\Provider\\NodeProviderInterface' => $vendorDir . '/ramsey/uuid/src/Provider/NodeProviderInterface.php',
    'Ramsey\\Uuid\\Provider\\Node\\FallbackNodeProvider' => $vendorDir . '/ramsey/uuid/src/Provider/Node/FallbackNodeProvider.php',
    'Ramsey\\Uuid\\Provider\\Node\\NodeProviderCollection' => $vendorDir . '/ramsey/uuid/src/Provider/Node/NodeProviderCollection.php',
    'Ramsey\\Uuid\\Provider\\Node\\RandomNodeProvider' => $vendorDir . '/ramsey/uuid/src/Provider/Node/RandomNodeProvider.php',
    'Ramsey\\Uuid\\Provider\\Node\\StaticNodeProvider' => $vendorDir . '/ramsey/uuid/src/Provider/Node/StaticNodeProvider.php',
    'Ramsey\\Uuid\\Provider\\Node\\SystemNodeProvider' => $vendorDir . '/ramsey/uuid/src/Provider/Node/SystemNodeProvider.php',
    'Ramsey\\Uuid\\Provider\\TimeProviderInterface' => $vendorDir . '/ramsey/uuid/src/Provider/TimeProviderInterface.php',
    'Ramsey\\Uuid\\Provider\\Time\\FixedTimeProvider' => $vendorDir . '/ramsey/uuid/src/Provider/Time/FixedTimeProvider.php',
    'Ramsey\\Uuid\\Provider\\Time\\SystemTimeProvider' => $vendorDir . '/ramsey/uuid/src/Provider/Time/SystemTimeProvider.php',
    'Ramsey\\Uuid\\Rfc4122\\Fields' => $vendorDir . '/ramsey/uuid/src/Rfc4122/Fields.php',
    'Ramsey\\Uuid\\Rfc4122\\FieldsInterface' => $vendorDir . '/ramsey/uuid/src/Rfc4122/FieldsInterface.php',
    'Ramsey\\Uuid\\Rfc4122\\MaxTrait' => $vendorDir . '/ramsey/uuid/src/Rfc4122/MaxTrait.php',
    'Ramsey\\Uuid\\Rfc4122\\MaxUuid' => $vendorDir . '/ramsey/uuid/src/Rfc4122/MaxUuid.php',
    'Ramsey\\Uuid\\Rfc4122\\NilTrait' => $vendorDir . '/ramsey/uuid/src/Rfc4122/NilTrait.php',
    'Ramsey\\Uuid\\Rfc4122\\NilUuid' => $vendorDir . '/ramsey/uuid/src/Rfc4122/NilUuid.php',
    'Ramsey\\Uuid\\Rfc4122\\TimeTrait' => $vendorDir . '/ramsey/uuid/src/Rfc4122/TimeTrait.php',
    'Ramsey\\Uuid\\Rfc4122\\UuidBuilder' => $vendorDir . '/ramsey/uuid/src/Rfc4122/UuidBuilder.php',
    'Ramsey\\Uuid\\Rfc4122\\UuidInterface' => $vendorDir . '/ramsey/uuid/src/Rfc4122/UuidInterface.php',
    'Ramsey\\Uuid\\Rfc4122\\UuidV1' => $vendorDir . '/ramsey/uuid/src/Rfc4122/UuidV1.php',
    'Ramsey\\Uuid\\Rfc4122\\UuidV2' => $vendorDir . '/ramsey/uuid/src/Rfc4122/UuidV2.php',
    'Ramsey\\Uuid\\Rfc4122\\UuidV3' => $vendorDir . '/ramsey/uuid/src/Rfc4122/UuidV3.php',
    'Ramsey\\Uuid\\Rfc4122\\UuidV4' => $vendorDir . '/ramsey/uuid/src/Rfc4122/UuidV4.php',
    'Ramsey\\Uuid\\Rfc4122\\UuidV5' => $vendorDir . '/ramsey/uuid/src/Rfc4122/UuidV5.php',
    'Ramsey\\Uuid\\Rfc4122\\UuidV6' => $vendorDir . '/ramsey/uuid/src/Rfc4122/UuidV6.php',
    'Ramsey\\Uuid\\Rfc4122\\UuidV7' => $vendorDir . '/ramsey/uuid/src/Rfc4122/UuidV7.php',
    'Ramsey\\Uuid\\Rfc4122\\UuidV8' => $vendorDir . '/ramsey/uuid/src/Rfc4122/UuidV8.php',
    'Ramsey\\Uuid\\Rfc4122\\Validator' => $vendorDir . '/ramsey/uuid/src/Rfc4122/Validator.php',
    'Ramsey\\Uuid\\Rfc4122\\VariantTrait' => $vendorDir . '/ramsey/uuid/src/Rfc4122/VariantTrait.php',
    'Ramsey\\Uuid\\Rfc4122\\VersionTrait' => $vendorDir . '/ramsey/uuid/src/Rfc4122/VersionTrait.php',
    'Ramsey\\Uuid\\Type\\Decimal' => $vendorDir . '/ramsey/uuid/src/Type/Decimal.php',
    'Ramsey\\Uuid\\Type\\Hexadecimal' => $vendorDir . '/ramsey/uuid/src/Type/Hexadecimal.php',
    'Ramsey\\Uuid\\Type\\Integer' => $vendorDir . '/ramsey/uuid/src/Type/Integer.php',
    'Ramsey\\Uuid\\Type\\NumberInterface' => $vendorDir . '/ramsey/uuid/src/Type/NumberInterface.php',
    'Ramsey\\Uuid\\Type\\Time' => $vendorDir . '/ramsey/uuid/src/Type/Time.php',
    'Ramsey\\Uuid\\Type\\TypeInterface' => $vendorDir . '/ramsey/uuid/src/Type/TypeInterface.php',
    'Ramsey\\Uuid\\Uuid' => $vendorDir . '/ramsey/uuid/src/Uuid.php',
    'Ramsey\\Uuid\\UuidFactory' => $vendorDir . '/ramsey/uuid/src/UuidFactory.php',
    'Ramsey\\Uuid\\UuidFactoryInterface' => $vendorDir . '/ramsey/uuid/src/UuidFactoryInterface.php',
    'Ramsey\\Uuid\\UuidInterface' => $vendorDir . '/ramsey/uuid/src/UuidInterface.php',
    'Ramsey\\Uuid\\Validator\\GenericValidator' => $vendorDir . '/ramsey/uuid/src/Validator/GenericValidator.php',
    'Ramsey\\Uuid\\Validator\\ValidatorInterface' => $vendorDir . '/ramsey/uuid/src/Validator/ValidatorInterface.php',
    'Random\\BrokenRandomEngineError' => $vendorDir . '/symfony/polyfill-php82/Resources/stubs/Random/BrokenRandomEngineError.php',
    'Random\\CryptoSafeEngine' => $vendorDir . '/symfony/polyfill-php82/Resources/stubs/Random/CryptoSafeEngine.php',
    'Random\\Engine' => $vendorDir . '/symfony/polyfill-php82/Resources/stubs/Random/Engine.php',
    'Random\\Engine\\Secure' => $vendorDir . '/symfony/polyfill-php82/Resources/stubs/Random/Engine/Secure.php',
    'Random\\RandomError' => $vendorDir . '/symfony/polyfill-php82/Resources/stubs/Random/RandomError.php',
    'Random\\RandomException' => $vendorDir . '/symfony/polyfill-php82/Resources/stubs/Random/RandomException.php',
    'ReflectionConstant' => $vendorDir . '/symfony/polyfill-php84/Resources/stubs/ReflectionConstant.php',
    'Revolt\\EventLoop' => $vendorDir . '/revolt/event-loop/src/EventLoop.php',
    'Revolt\\EventLoop\\CallbackType' => $vendorDir . '/revolt/event-loop/src/EventLoop/CallbackType.php',
    'Revolt\\EventLoop\\Driver' => $vendorDir . '/revolt/event-loop/src/EventLoop/Driver.php',
    'Revolt\\EventLoop\\DriverFactory' => $vendorDir . '/revolt/event-loop/src/EventLoop/DriverFactory.php',
    'Revolt\\EventLoop\\Driver\\EvDriver' => $vendorDir . '/revolt/event-loop/src/EventLoop/Driver/EvDriver.php',
    'Revolt\\EventLoop\\Driver\\EventDriver' => $vendorDir . '/revolt/event-loop/src/EventLoop/Driver/EventDriver.php',
    'Revolt\\EventLoop\\Driver\\StreamSelectDriver' => $vendorDir . '/revolt/event-loop/src/EventLoop/Driver/StreamSelectDriver.php',
    'Revolt\\EventLoop\\Driver\\TracingDriver' => $vendorDir . '/revolt/event-loop/src/EventLoop/Driver/TracingDriver.php',
    'Revolt\\EventLoop\\Driver\\UvDriver' => $vendorDir . '/revolt/event-loop/src/EventLoop/Driver/UvDriver.php',
    'Revolt\\EventLoop\\FiberLocal' => $vendorDir . '/revolt/event-loop/src/EventLoop/FiberLocal.php',
    'Revolt\\EventLoop\\Internal\\AbstractDriver' => $vendorDir . '/revolt/event-loop/src/EventLoop/Internal/AbstractDriver.php',
    'Revolt\\EventLoop\\Internal\\ClosureHelper' => $vendorDir . '/revolt/event-loop/src/EventLoop/Internal/ClosureHelper.php',
    'Revolt\\EventLoop\\Internal\\DeferCallback' => $vendorDir . '/revolt/event-loop/src/EventLoop/Internal/DeferCallback.php',
    'Revolt\\EventLoop\\Internal\\DriverCallback' => $vendorDir . '/revolt/event-loop/src/EventLoop/Internal/DriverCallback.php',
    'Revolt\\EventLoop\\Internal\\DriverSuspension' => $vendorDir . '/revolt/event-loop/src/EventLoop/Internal/DriverSuspension.php',
    'Revolt\\EventLoop\\Internal\\SignalCallback' => $vendorDir . '/revolt/event-loop/src/EventLoop/Internal/SignalCallback.php',
    'Revolt\\EventLoop\\Internal\\StreamCallback' => $vendorDir . '/revolt/event-loop/src/EventLoop/Internal/StreamCallback.php',
    'Revolt\\EventLoop\\Internal\\StreamReadableCallback' => $vendorDir . '/revolt/event-loop/src/EventLoop/Internal/StreamReadableCallback.php',
    'Revolt\\EventLoop\\Internal\\StreamWritableCallback' => $vendorDir . '/revolt/event-loop/src/EventLoop/Internal/StreamWritableCallback.php',
    'Revolt\\EventLoop\\Internal\\TimerCallback' => $vendorDir . '/revolt/event-loop/src/EventLoop/Internal/TimerCallback.php',
    'Revolt\\EventLoop\\Internal\\TimerQueue' => $vendorDir . '/revolt/event-loop/src/EventLoop/Internal/TimerQueue.php',
    'Revolt\\EventLoop\\InvalidCallbackError' => $vendorDir . '/revolt/event-loop/src/EventLoop/InvalidCallbackError.php',
    'Revolt\\EventLoop\\Suspension' => $vendorDir . '/revolt/event-loop/src/EventLoop/Suspension.php',
    'Revolt\\EventLoop\\UncaughtThrowable' => $vendorDir . '/revolt/event-loop/src/EventLoop/UncaughtThrowable.php',
    'Revolt\\EventLoop\\UnsupportedFeatureException' => $vendorDir . '/revolt/event-loop/src/EventLoop/UnsupportedFeatureException.php',
    'RoundingMode' => $vendorDir . '/symfony/polyfill-php84/Resources/stubs/RoundingMode.php',
    'SQLite3Exception' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/SQLite3Exception.php',
    'SensitiveParameter' => $vendorDir . '/symfony/polyfill-php82/Resources/stubs/SensitiveParameter.php',
    'SensitiveParameterValue' => $vendorDir . '/symfony/polyfill-php82/Resources/stubs/SensitiveParameterValue.php',
    'SortDirection' => $vendorDir . '/symfony/polyfill-php86/Resources/stubs/SortDirection.php',
    'Symfony\\Polyfill\\Iconv\\Iconv' => $vendorDir . '/symfony/polyfill-iconv/Iconv.php',
    'Symfony\\Polyfill\\Mbstring\\Mbstring' => $vendorDir . '/symfony/polyfill-mbstring/Mbstring.php',
    'Symfony\\Polyfill\\Php82\\NoDynamicProperties' => $vendorDir . '/symfony/polyfill-php82/NoDynamicProperties.php',
    'Symfony\\Polyfill\\Php82\\Php82' => $vendorDir . '/symfony/polyfill-php82/Php82.php',
    'Symfony\\Polyfill\\Php82\\Random\\Engine\\Secure' => $vendorDir . '/symfony/polyfill-php82/Random/Engine/Secure.php',
    'Symfony\\Polyfill\\Php82\\SensitiveParameterValue' => $vendorDir . '/symfony/polyfill-php82/SensitiveParameterValue.php',
    'Symfony\\Polyfill\\Php83\\Php83' => $vendorDir . '/symfony/polyfill-php83/Php83.php',
    'Symfony\\Polyfill\\Php84\\Php84' => $vendorDir . '/symfony/polyfill-php84/Php84.php',
    'Symfony\\Polyfill\\Php85\\Php85' => $vendorDir . '/symfony/polyfill-php85/Php85.php',
    'Symfony\\Polyfill\\Php86\\Php86' => $vendorDir . '/symfony/polyfill-php86/Php86.php',
    'Webmozart\\Assert\\Assert' => $vendorDir . '/webmozart/assert/src/Assert.php',
    'Webmozart\\Assert\\HasAssert' => $vendorDir . '/webmozart/assert/src/HasAssert.php',
    'Webmozart\\Assert\\InvalidArgumentException' => $vendorDir . '/webmozart/assert/src/InvalidArgumentException.php',
    'Webmozart\\Assert\\Mixin' => $vendorDir . '/webmozart/assert/src/Mixin.php',
    'Webmozart\\Assert\\PsalmPlugin' => $vendorDir . '/webmozart/assert/src/PsalmPlugin.php',
    'Webrtc\\AVCodec\\AVCodec' => $vendorDir . '/danog/php-rtc-av/src/AVCodec.php',
    'Webrtc\\AVCodec\\AVFilter' => $vendorDir . '/danog/php-rtc-av/src/AVFilter.php',
    'Webrtc\\AVCodec\\AVFormat' => $vendorDir . '/danog/php-rtc-av/src/AVFormat.php',
    'Webrtc\\AVCodec\\Audio\\AudioChannel' => $vendorDir . '/danog/php-rtc-av/src/Audio/AudioChannel.php',
    'Webrtc\\AVCodec\\Audio\\AudioLayout' => $vendorDir . '/danog/php-rtc-av/src/Audio/AudioLayout.php',
    'Webrtc\\AVCodec\\Audio\\AudioResampler' => $vendorDir . '/danog/php-rtc-av/src/Audio/AudioResampler.php',
    'Webrtc\\AVCodec\\Codec' => $vendorDir . '/danog/php-rtc-av/src/Codec.php',
    'Webrtc\\AVCodec\\Context\\AudioContext' => $vendorDir . '/danog/php-rtc-av/src/Context/AudioContext.php',
    'Webrtc\\AVCodec\\Context\\Context' => $vendorDir . '/danog/php-rtc-av/src/Context/Context.php',
    'Webrtc\\AVCodec\\Context\\ContextInterface' => $vendorDir . '/danog/php-rtc-av/src/Context/ContextInterface.php',
    'Webrtc\\AVCodec\\Context\\Dictionary' => $vendorDir . '/danog/php-rtc-av/src/Context/Dictionary.php',
    'Webrtc\\AVCodec\\Context\\VideoContext' => $vendorDir . '/danog/php-rtc-av/src/Context/VideoContext.php',
    'Webrtc\\AVCodec\\Data\\AudioPlane' => $vendorDir . '/danog/php-rtc-av/src/Data/AudioPlane.php',
    'Webrtc\\AVCodec\\Data\\Buffer' => $vendorDir . '/danog/php-rtc-av/src/Data/Buffer.php',
    'Webrtc\\AVCodec\\Data\\BufferInterface' => $vendorDir . '/danog/php-rtc-av/src/Data/BufferInterface.php',
    'Webrtc\\AVCodec\\Data\\Packet' => $vendorDir . '/danog/php-rtc-av/src/Data/Packet.php',
    'Webrtc\\AVCodec\\Data\\VideoPlane' => $vendorDir . '/danog/php-rtc-av/src/Data/VideoPlane.php',
    'Webrtc\\AVCodec\\Enum\\ColorRange' => $vendorDir . '/danog/php-rtc-av/src/Enum/ColorRange.php',
    'Webrtc\\AVCodec\\Enum\\Colorspace' => $vendorDir . '/danog/php-rtc-av/src/Enum/Colorspace.php',
    'Webrtc\\AVCodec\\Enum\\Interpolation' => $vendorDir . '/danog/php-rtc-av/src/Enum/Interpolation.php',
    'Webrtc\\AVCodec\\Enum\\PictureType' => $vendorDir . '/danog/php-rtc-av/src/Enum/PictureType.php',
    'Webrtc\\AVCodec\\Exception\\AvCodecException' => $vendorDir . '/danog/php-rtc-av/src/Exception/AvCodecException.php',
    'Webrtc\\AVCodec\\Filter\\Filter' => $vendorDir . '/danog/php-rtc-av/src/Filter/Filter.php',
    'Webrtc\\AVCodec\\Filter\\FilterContext' => $vendorDir . '/danog/php-rtc-av/src/Filter/FilterContext.php',
    'Webrtc\\AVCodec\\Filter\\Graph' => $vendorDir . '/danog/php-rtc-av/src/Filter/Graph.php',
    'Webrtc\\AVCodec\\Format\\AudioFormat' => $vendorDir . '/danog/php-rtc-av/src/Format/AudioFormat.php',
    'Webrtc\\AVCodec\\Format\\VideoFormat' => $vendorDir . '/danog/php-rtc-av/src/Format/VideoFormat.php',
    'Webrtc\\AVCodec\\Format\\VideoFormatComponent' => $vendorDir . '/danog/php-rtc-av/src/Format/VideoFormatComponent.php',
    'Webrtc\\AVCodec\\Frame\\AudioFrame' => $vendorDir . '/danog/php-rtc-av/src/Frame/AudioFrame.php',
    'Webrtc\\AVCodec\\Frame\\Frame' => $vendorDir . '/danog/php-rtc-av/src/Frame/Frame.php',
    'Webrtc\\AVCodec\\Frame\\FrameInterface' => $vendorDir . '/danog/php-rtc-av/src/Frame/FrameInterface.php',
    'Webrtc\\AVCodec\\Frame\\OpaqueContainer' => $vendorDir . '/danog/php-rtc-av/src/Frame/OpaqueContainer.php',
    'Webrtc\\AVCodec\\Frame\\VideoFrame' => $vendorDir . '/danog/php-rtc-av/src/Frame/VideoFrame.php',
    'Webrtc\\AVCodec\\Frame\\VideoFrameReformater' => $vendorDir . '/danog/php-rtc-av/src/Frame/VideoFrameReformater.php',
    'Webrtc\\AVCodec\\LibraryVersion' => $vendorDir . '/danog/php-rtc-av/src/LibraryVersion.php',
    'Webrtc\\AVCodec\\SWScale' => $vendorDir . '/danog/php-rtc-av/src/SWScale.php',
    'Webrtc\\AVCodec\\TransCoder' => $vendorDir . '/danog/php-rtc-av/src/TransCoder.php',
    'Webrtc\\AVCodec\\TransCoderInterface' => $vendorDir . '/danog/php-rtc-av/src/TransCoderInterface.php',
    'Webrtc\\AVCodec\\Util\\Util' => $vendorDir . '/danog/php-rtc-av/src/Util/Util.php',
    'Webrtc\\Codecs\\Audio\\Opus\\OpusDecoder' => $vendorDir . '/danog/php-rtc-codecs/src/Audio/Opus/OpusDecoder.php',
    'Webrtc\\Codecs\\Audio\\Opus\\OpusEncoder' => $vendorDir . '/danog/php-rtc-codecs/src/Audio/Opus/OpusEncoder.php',
    'Webrtc\\Codecs\\Audio\\PCM\\PCMDecoder' => $vendorDir . '/danog/php-rtc-codecs/src/Audio/PCM/PCMDecoder.php',
    'Webrtc\\Codecs\\Audio\\PCM\\PCMEncoder' => $vendorDir . '/danog/php-rtc-codecs/src/Audio/PCM/PCMEncoder.php',
    'Webrtc\\Codecs\\Audio\\PCM\\PCMaDecoder' => $vendorDir . '/danog/php-rtc-codecs/src/Audio/PCM/PCMaDecoder.php',
    'Webrtc\\Codecs\\Audio\\PCM\\PCMaEncoder' => $vendorDir . '/danog/php-rtc-codecs/src/Audio/PCM/PCMaEncoder.php',
    'Webrtc\\Codecs\\Audio\\PCM\\PCMuDecoder' => $vendorDir . '/danog/php-rtc-codecs/src/Audio/PCM/PCMuDecoder.php',
    'Webrtc\\Codecs\\Audio\\PCM\\PCMuEncoder' => $vendorDir . '/danog/php-rtc-codecs/src/Audio/PCM/PCMuEncoder.php',
    'Webrtc\\Codecs\\Codec' => $vendorDir . '/danog/php-rtc-codecs/src/Codec.php',
    'Webrtc\\Codecs\\CodecUtility' => $vendorDir . '/danog/php-rtc-codecs/src/CodecUtility.php',
    'Webrtc\\Codecs\\DecoderInterface' => $vendorDir . '/danog/php-rtc-codecs/src/DecoderInterface.php',
    'Webrtc\\Codecs\\EncodedPacket' => $vendorDir . '/danog/php-rtc-codecs/src/EncodedPacket.php',
    'Webrtc\\Codecs\\Encoder' => $vendorDir . '/danog/php-rtc-codecs/src/Encoder.php',
    'Webrtc\\Codecs\\EncoderInterface' => $vendorDir . '/danog/php-rtc-codecs/src/EncoderInterface.php',
    'Webrtc\\Codecs\\JitterFrameInterface' => $vendorDir . '/danog/php-rtc-codecs/src/JitterFrameInterface.php',
    'Webrtc\\Codecs\\PayloadDescriptorInterface' => $vendorDir . '/danog/php-rtc-codecs/src/PayloadDescriptorInterface.php',
    'Webrtc\\Codecs\\Video\\Av1\\Av1Encoder' => $vendorDir . '/danog/php-rtc-codecs/src/Video/Av1/Av1Encoder.php',
    'Webrtc\\Codecs\\Video\\Av1\\Av1PayloadDescriptor' => $vendorDir . '/danog/php-rtc-codecs/src/Video/Av1/Av1PayloadDescriptor.php',
    'Webrtc\\Codecs\\Video\\H265\\H265Encoder' => $vendorDir . '/danog/php-rtc-codecs/src/Video/H265/H265Encoder.php',
    'Webrtc\\Codecs\\Video\\H265\\H265PayloadDescriptor' => $vendorDir . '/danog/php-rtc-codecs/src/Video/H265/H265PayloadDescriptor.php',
    'Webrtc\\Codecs\\Video\\Vp8\\Vp8Decoder' => $vendorDir . '/danog/php-rtc-codecs/src/Video/Vp8/Vp8Decoder.php',
    'Webrtc\\Codecs\\Video\\Vp8\\Vp8Encoder' => $vendorDir . '/danog/php-rtc-codecs/src/Video/Vp8/Vp8Encoder.php',
    'Webrtc\\Codecs\\Video\\Vp8\\Vp8PayloadDescriptor' => $vendorDir . '/danog/php-rtc-codecs/src/Video/Vp8/Vp8PayloadDescriptor.php',
    'Webrtc\\Codecs\\Video\\Vp9\\Vp9Decoder' => $vendorDir . '/danog/php-rtc-codecs/src/Video/Vp9/Vp9Decoder.php',
    'Webrtc\\Codecs\\Video\\Vp9\\Vp9Encoder' => $vendorDir . '/danog/php-rtc-codecs/src/Video/Vp9/Vp9Encoder.php',
    'Webrtc\\Codecs\\Video\\Vp9\\Vp9PayloadDescriptor' => $vendorDir . '/danog/php-rtc-codecs/src/Video/Vp9/Vp9PayloadDescriptor.php',
    'Webrtc\\Codecs\\Video\\X264\\H264Decoder' => $vendorDir . '/danog/php-rtc-codecs/src/Video/X264/H264Decoder.php',
    'Webrtc\\Codecs\\Video\\X264\\H264Encoder' => $vendorDir . '/danog/php-rtc-codecs/src/Video/X264/H264Encoder.php',
    'Webrtc\\Codecs\\Video\\X264\\H264PayloadDescriptor' => $vendorDir . '/danog/php-rtc-codecs/src/Video/X264/H264PayloadDescriptor.php',
    'Webrtc\\DTLS\\DTLS\\Crypto' => $vendorDir . '/danog/php-rtc-dtls/src/DTLS/Crypto.php',
    'Webrtc\\DTLS\\DTLS\\EcPrivateKey' => $vendorDir . '/danog/php-rtc-dtls/src/DTLS/EcPrivateKey.php',
    'Webrtc\\DTLS\\DTLS\\Engine' => $vendorDir . '/danog/php-rtc-dtls/src/DTLS/Engine.php',
    'Webrtc\\DTLS\\DTLS\\Enum\\SSLHandshakeState' => $vendorDir . '/danog/php-rtc-dtls/src/DTLS/Enum/SSLHandshakeState.php',
    'Webrtc\\DTLS\\DTLS\\Exception\\DTLSException' => $vendorDir . '/danog/php-rtc-dtls/src/DTLS/Exception/DTLSException.php',
    'Webrtc\\DTLS\\DTLS\\Exception\\DTLSExceptionInterface' => $vendorDir . '/danog/php-rtc-dtls/src/DTLS/Exception/DTLSExceptionInterface.php',
    'Webrtc\\DTLS\\DTLS\\Exception\\HandshakeException' => $vendorDir . '/danog/php-rtc-dtls/src/DTLS/Exception/HandshakeException.php',
    'Webrtc\\DTLS\\DTLS\\Exception\\RTCCertificateException' => $vendorDir . '/danog/php-rtc-dtls/src/DTLS/Exception/RTCCertificateException.php',
    'Webrtc\\DTLS\\DTLS\\Exception\\TLSException' => $vendorDir . '/danog/php-rtc-dtls/src/DTLS/Exception/TLSException.php',
    'Webrtc\\DTLS\\DTLS\\Handshake' => $vendorDir . '/danog/php-rtc-dtls/src/DTLS/Handshake.php',
    'Webrtc\\DTLS\\DTLS\\Prf' => $vendorDir . '/danog/php-rtc-dtls/src/DTLS/Prf.php',
    'Webrtc\\DTLS\\DTLS\\RTCCertificate' => $vendorDir . '/danog/php-rtc-dtls/src/DTLS/RTCCertificate.php',
    'Webrtc\\DTLS\\DTLS\\RTCDtlsTransport' => $vendorDir . '/danog/php-rtc-dtls/src/DTLS/RTCDtlsTransport.php',
    'Webrtc\\DTLS\\DTLS\\Reader' => $vendorDir . '/danog/php-rtc-dtls/src/DTLS/Reader.php',
    'Webrtc\\DTLS\\DTLS\\RecordLayer' => $vendorDir . '/danog/php-rtc-dtls/src/DTLS/RecordLayer.php',
    'Webrtc\\DTLS\\DTLS\\Srtp' => $vendorDir . '/danog/php-rtc-dtls/src/DTLS/Srtp.php',
    'Webrtc\\DTLS\\DTLS\\TLS\\Handshake' => $vendorDir . '/danog/php-rtc-dtls/src/DTLS/TLS/Handshake.php',
    'Webrtc\\DTLS\\DTLS\\TLS\\TLS' => $vendorDir . '/danog/php-rtc-dtls/src/DTLS/TLS/TLS.php',
    'Webrtc\\DTLS\\Enum\\BioMethod' => $vendorDir . '/danog/php-rtc-dtls/src/Enum/BioMethod.php',
    'Webrtc\\DTLS\\Enum\\ContextMethod' => $vendorDir . '/danog/php-rtc-dtls/src/Enum/ContextMethod.php',
    'Webrtc\\DTLS\\Enum\\ECCurveName' => $vendorDir . '/danog/php-rtc-dtls/src/Enum/ECCurveName.php',
    'Webrtc\\DTLS\\Enum\\PrivateKeyType' => $vendorDir . '/danog/php-rtc-dtls/src/Enum/PrivateKeyType.php',
    'Webrtc\\DTLS\\Enum\\Verify' => $vendorDir . '/danog/php-rtc-dtls/src/Enum/Verify.php',
    'Webrtc\\DTLS\\Exception\\OpenSSLException' => $vendorDir . '/danog/php-rtc-dtls/src/Exception/OpenSSLException.php',
    'Webrtc\\DTLS\\Exception\\SSLException' => $vendorDir . '/danog/php-rtc-dtls/src/Exception/SSLException.php',
    'Webrtc\\DTLS\\Exception\\SysCallException' => $vendorDir . '/danog/php-rtc-dtls/src/Exception/SysCallException.php',
    'Webrtc\\DTLS\\Exception\\ZeroReturnException' => $vendorDir . '/danog/php-rtc-dtls/src/Exception/ZeroReturnException.php',
    'Webrtc\\DTLS\\Listener\\DtlsTransportStateChangeListener' => $vendorDir . '/danog/php-rtc-dtls/src/Listener/DtlsTransportStateChangeListener.php',
    'Webrtc\\DTLS\\SSL\\BIO' => $vendorDir . '/danog/php-rtc-dtls/src/SSL/BIO.php',
    'Webrtc\\DTLS\\SSL\\BIOInterface' => $vendorDir . '/danog/php-rtc-dtls/src/SSL/BIOInterface.php',
    'Webrtc\\DTLS\\SSL\\Context' => $vendorDir . '/danog/php-rtc-dtls/src/SSL/Context.php',
    'Webrtc\\DTLS\\SSL\\SSL' => $vendorDir . '/danog/php-rtc-dtls/src/SSL/SSL.php',
    'Webrtc\\DTLS\\SSL\\SSLInterface' => $vendorDir . '/danog/php-rtc-dtls/src/SSL/SSLInterface.php',
    'Webrtc\\DataChannel\\Enum\\State' => $vendorDir . '/danog/php-rtc-datachannel/src/Enum/State.php',
    'Webrtc\\DataChannel\\Listener\\DataChannelBufferedAmountLowListener' => $vendorDir . '/danog/php-rtc-datachannel/src/Listener/DataChannelBufferedAmountLowListener.php',
    'Webrtc\\DataChannel\\Listener\\DataChannelCloseListener' => $vendorDir . '/danog/php-rtc-datachannel/src/Listener/DataChannelCloseListener.php',
    'Webrtc\\DataChannel\\Listener\\DataChannelMessageListener' => $vendorDir . '/danog/php-rtc-datachannel/src/Listener/DataChannelMessageListener.php',
    'Webrtc\\DataChannel\\Listener\\DataChannelOpenListener' => $vendorDir . '/danog/php-rtc-datachannel/src/Listener/DataChannelOpenListener.php',
    'Webrtc\\DataChannel\\RTCDataChannel' => $vendorDir . '/danog/php-rtc-datachannel/src/RTCDataChannel.php',
    'Webrtc\\DataChannel\\RTCDataChannelInterface' => $vendorDir . '/danog/php-rtc-datachannel/src/RTCDataChannelInterface.php',
    'Webrtc\\DataChannel\\RTCDataChannelParameters' => $vendorDir . '/danog/php-rtc-datachannel/src/RTCDataChannelParameters.php',
    'Webrtc\\DataChannel\\RTCSctpTransportInterface' => $vendorDir . '/danog/php-rtc-datachannel/src/RTCSctpTransportInterface.php',
    'Webrtc\\Exception\\Exception' => $vendorDir . '/danog/php-rtc-exception/src/Exception.php',
    'Webrtc\\Exception\\InvalidArgumentException' => $vendorDir . '/danog/php-rtc-exception/src/InvalidArgumentException.php',
    'Webrtc\\Exception\\NotImplementedException' => $vendorDir . '/danog/php-rtc-exception/src/NotImplementedException.php',
    'Webrtc\\Exception\\RuntimeException' => $vendorDir . '/danog/php-rtc-exception/src/RuntimeException.php',
    'Webrtc\\Exception\\WebrtcExceptionInterface' => $vendorDir . '/danog/php-rtc-exception/src/WebrtcExceptionInterface.php',
    'Webrtc\\ICE\\Enum\\CandidateType' => $vendorDir . '/danog/php-rtc-ice/src/Enum/CandidateType.php',
    'Webrtc\\ICE\\Enum\\CheckListState' => $vendorDir . '/danog/php-rtc-ice/src/Enum/CheckListState.php',
    'Webrtc\\ICE\\Enum\\IceGatheringState' => $vendorDir . '/danog/php-rtc-ice/src/Enum/IceGatheringState.php',
    'Webrtc\\ICE\\Enum\\IceRole' => $vendorDir . '/danog/php-rtc-ice/src/Enum/IceRole.php',
    'Webrtc\\ICE\\Enum\\IceTransportState' => $vendorDir . '/danog/php-rtc-ice/src/Enum/IceTransportState.php',
    'Webrtc\\ICE\\Enum\\RTCIceCandidatePairStats' => $vendorDir . '/danog/php-rtc-ice/src/Enum/RTCIceCandidatePairStats.php',
    'Webrtc\\ICE\\Enum\\TransportPolicyType' => $vendorDir . '/danog/php-rtc-ice/src/Enum/TransportPolicyType.php',
    'Webrtc\\ICE\\Enum\\TransportType' => $vendorDir . '/danog/php-rtc-ice/src/Enum/TransportType.php',
    'Webrtc\\ICE\\IceProtocolParser' => $vendorDir . '/danog/php-rtc-ice/src/IceProtocolParser.php',
    'Webrtc\\ICE\\Listener\\IceConnectionClosedListener' => $vendorDir . '/danog/php-rtc-ice/src/Listener/IceConnectionClosedListener.php',
    'Webrtc\\ICE\\Listener\\IceConnectionDataListener' => $vendorDir . '/danog/php-rtc-ice/src/Listener/IceConnectionDataListener.php',
    'Webrtc\\ICE\\Listener\\IceGathererStateChangeListener' => $vendorDir . '/danog/php-rtc-ice/src/Listener/IceGathererStateChangeListener.php',
    'Webrtc\\ICE\\Listener\\IceTransportDataListener' => $vendorDir . '/danog/php-rtc-ice/src/Listener/IceTransportDataListener.php',
    'Webrtc\\ICE\\Listener\\IceTransportDisconnectListener' => $vendorDir . '/danog/php-rtc-ice/src/Listener/IceTransportDisconnectListener.php',
    'Webrtc\\ICE\\Listener\\IceTransportStateChangeListener' => $vendorDir . '/danog/php-rtc-ice/src/Listener/IceTransportStateChangeListener.php',
    'Webrtc\\ICE\\RTCICESetting' => $vendorDir . '/danog/php-rtc-ice/src/RTCICESetting.php',
    'Webrtc\\ICE\\RTCIceCandidate' => $vendorDir . '/danog/php-rtc-ice/src/RTCIceCandidate.php',
    'Webrtc\\ICE\\RTCIceCandidatePair' => $vendorDir . '/danog/php-rtc-ice/src/RTCIceCandidatePair.php',
    'Webrtc\\ICE\\RTCIceConnection' => $vendorDir . '/danog/php-rtc-ice/src/RTCIceConnection.php',
    'Webrtc\\ICE\\RTCIceConnectionInterface' => $vendorDir . '/danog/php-rtc-ice/src/RTCIceConnectionInterface.php',
    'Webrtc\\ICE\\RTCIceGatherer' => $vendorDir . '/danog/php-rtc-ice/src/RTCIceGatherer.php',
    'Webrtc\\ICE\\RTCIceGathererInterface' => $vendorDir . '/danog/php-rtc-ice/src/RTCIceGathererInterface.php',
    'Webrtc\\ICE\\RTCIceParameters' => $vendorDir . '/danog/php-rtc-ice/src/RTCIceParameters.php',
    'Webrtc\\ICE\\RTCIceProtocolConfiguration' => $vendorDir . '/danog/php-rtc-ice/src/RTCIceProtocolConfiguration.php',
    'Webrtc\\ICE\\RTCIceProtocolConfigurationInterface' => $vendorDir . '/danog/php-rtc-ice/src/RTCIceProtocolConfigurationInterface.php',
    'Webrtc\\ICE\\RTCIceServer' => $vendorDir . '/danog/php-rtc-ice/src/RTCIceServer.php',
    'Webrtc\\ICE\\RTCIceServerInterface' => $vendorDir . '/danog/php-rtc-ice/src/RTCIceServerInterface.php',
    'Webrtc\\ICE\\RTCIceTransport' => $vendorDir . '/danog/php-rtc-ice/src/RTCIceTransport.php',
    'Webrtc\\ICE\\RTCIceTransportInterface' => $vendorDir . '/danog/php-rtc-ice/src/RTCIceTransportInterface.php',
    'Webrtc\\ICE\\Trait\\DNS' => $vendorDir . '/danog/php-rtc-ice/src/Trait/DNS.php',
    'Webrtc\\ICE\\Trait\\Mdns' => $vendorDir . '/danog/php-rtc-ice/src/Trait/Mdns.php',
    'Webrtc\\ICE\\Trait\\NetworkAdapter' => $vendorDir . '/danog/php-rtc-ice/src/Trait/NetworkAdapter.php',
    'Webrtc\\ICE\\Utils' => $vendorDir . '/danog/php-rtc-ice/src/Utils.php',
    'Webrtc\\MDNS\\Factory' => $vendorDir . '/danog/php-rtc-mdns/src/Factory.php',
    'Webrtc\\MDNS\\MulticastExecutor' => $vendorDir . '/danog/php-rtc-mdns/src/MulticastExecutor.php',
    'Webrtc\\Mixin\\DataClass' => $vendorDir . '/danog/php-rtc-mixin/src/DataClass.php',
    'Webrtc\\Mixin\\SerializableState' => $vendorDir . '/danog/php-rtc-mixin/src/SerializableState.php',
    'Webrtc\\Mixin\\SharedLibraryInterface' => $vendorDir . '/danog/php-rtc-mixin/src/SharedLibraryInterface.php',
    'Webrtc\\NTP\\NetworkTimeProtocol' => $vendorDir . '/danog/php-rtc-ntp/src/NetworkTimeProtocol.php',
    'Webrtc\\RTCP\\Exception\\RtcpExceptionInterface' => $vendorDir . '/danog/php-rtc-rtcp/src/Exception/RtcpExceptionInterface.php',
    'Webrtc\\RTCP\\Exception\\RtcpPacketException' => $vendorDir . '/danog/php-rtc-rtcp/src/Exception/RtcpPacketException.php',
    'Webrtc\\RTCP\\RtcpByePacket' => $vendorDir . '/danog/php-rtc-rtcp/src/RtcpByePacket.php',
    'Webrtc\\RTCP\\RtcpConstants' => $vendorDir . '/danog/php-rtc-rtcp/src/RtcpConstants.php',
    'Webrtc\\RTCP\\RtcpPacket' => $vendorDir . '/danog/php-rtc-rtcp/src/RtcpPacket.php',
    'Webrtc\\RTCP\\RtcpPacketInterface' => $vendorDir . '/danog/php-rtc-rtcp/src/RtcpPacketInterface.php',
    'Webrtc\\RTCP\\RtcpPsfbPacket' => $vendorDir . '/danog/php-rtc-rtcp/src/RtcpPsfbPacket.php',
    'Webrtc\\RTCP\\RtcpReceiverInfo' => $vendorDir . '/danog/php-rtc-rtcp/src/RtcpReceiverInfo.php',
    'Webrtc\\RTCP\\RtcpRrPacket' => $vendorDir . '/danog/php-rtc-rtcp/src/RtcpRrPacket.php',
    'Webrtc\\RTCP\\RtcpRtpfbPacket' => $vendorDir . '/danog/php-rtc-rtcp/src/RtcpRtpfbPacket.php',
    'Webrtc\\RTCP\\RtcpSdesPacket' => $vendorDir . '/danog/php-rtc-rtcp/src/RtcpSdesPacket.php',
    'Webrtc\\RTCP\\RtcpSenderInfo' => $vendorDir . '/danog/php-rtc-rtcp/src/RtcpSenderInfo.php',
    'Webrtc\\RTCP\\RtcpSourceInfo' => $vendorDir . '/danog/php-rtc-rtcp/src/RtcpSourceInfo.php',
    'Webrtc\\RTCP\\RtcpSrPacket' => $vendorDir . '/danog/php-rtc-rtcp/src/RtcpSrPacket.php',
    'Webrtc\\RTCP\\RtcpUtility' => $vendorDir . '/danog/php-rtc-rtcp/src/RtcpUtility.php',
    'Webrtc\\RTPParameter\\RTCRtcpFeedback' => $vendorDir . '/danog/php-rtc-rtp-parameter/src/RTCRtcpFeedback.php',
    'Webrtc\\RTPParameter\\RTCRtcpParameters' => $vendorDir . '/danog/php-rtc-rtp-parameter/src/RTCRtcpParameters.php',
    'Webrtc\\RTPParameter\\RTCRtpCapabilities' => $vendorDir . '/danog/php-rtc-rtp-parameter/src/RTCRtpCapabilities.php',
    'Webrtc\\RTPParameter\\RTCRtpCodecCapability' => $vendorDir . '/danog/php-rtc-rtp-parameter/src/RTCRtpCodecCapability.php',
    'Webrtc\\RTPParameter\\RTCRtpCodecParameters' => $vendorDir . '/danog/php-rtc-rtp-parameter/src/RTCRtpCodecParameters.php',
    'Webrtc\\RTPParameter\\RTCRtpCodingParameters' => $vendorDir . '/danog/php-rtc-rtp-parameter/src/RTCRtpCodingParameters.php',
    'Webrtc\\RTPParameter\\RTCRtpContributingSource' => $vendorDir . '/danog/php-rtc-rtp-parameter/src/RTCRtpContributingSource.php',
    'Webrtc\\RTPParameter\\RTCRtpDecodingParameters' => $vendorDir . '/danog/php-rtc-rtp-parameter/src/RTCRtpDecodingParameters.php',
    'Webrtc\\RTPParameter\\RTCRtpEncodingParameters' => $vendorDir . '/danog/php-rtc-rtp-parameter/src/RTCRtpEncodingParameters.php',
    'Webrtc\\RTPParameter\\RTCRtpHeaderExtensionCapability' => $vendorDir . '/danog/php-rtc-rtp-parameter/src/RTCRtpHeaderExtensionCapability.php',
    'Webrtc\\RTPParameter\\RTCRtpHeaderExtensionParameters' => $vendorDir . '/danog/php-rtc-rtp-parameter/src/RTCRtpHeaderExtensionParameters.php',
    'Webrtc\\RTPParameter\\RTCRtpParameters' => $vendorDir . '/danog/php-rtc-rtp-parameter/src/RTCRtpParameters.php',
    'Webrtc\\RTPParameter\\RTCRtpReceiveParameters' => $vendorDir . '/danog/php-rtc-rtp-parameter/src/RTCRtpReceiveParameters.php',
    'Webrtc\\RTPParameter\\RTCRtpRtxParameters' => $vendorDir . '/danog/php-rtc-rtp-parameter/src/RTCRtpRtxParameters.php',
    'Webrtc\\RTPParameter\\RTCRtpSendParameters' => $vendorDir . '/danog/php-rtc-rtp-parameter/src/RTCRtpSendParameters.php',
    'Webrtc\\RTPParameter\\RTCRtpSynchronizationSource' => $vendorDir . '/danog/php-rtc-rtp-parameter/src/RTCRtpSynchronizationSource.php',
    'Webrtc\\RTP\\Crypto\\FrameCryptorInterface' => $vendorDir . '/danog/php-rtc-rtp/src/Crypto/FrameCryptorInterface.php',
    'Webrtc\\RTP\\Enum\\BandwidthUsage' => $vendorDir . '/danog/php-rtc-rtp/src/Enum/BandwidthUsage.php',
    'Webrtc\\RTP\\Enum\\MediaKind' => $vendorDir . '/danog/php-rtc-rtp/src/Enum/MediaKind.php',
    'Webrtc\\RTP\\Enum\\RateControlState' => $vendorDir . '/danog/php-rtc-rtp/src/Enum/RateControlState.php',
    'Webrtc\\RTP\\Exception\\MediaStreamTrackException' => $vendorDir . '/danog/php-rtc-rtp/src/Exception/MediaStreamTrackException.php',
    'Webrtc\\RTP\\Exception\\RtpExceptionInterface' => $vendorDir . '/danog/php-rtc-rtp/src/Exception/RtpExceptionInterface.php',
    'Webrtc\\RTP\\Exception\\RtpPacketException' => $vendorDir . '/danog/php-rtc-rtp/src/Exception/RtpPacketException.php',
    'Webrtc\\RTP\\Exception\\RtpSenderException' => $vendorDir . '/danog/php-rtc-rtp/src/Exception/RtpSenderException.php',
    'Webrtc\\RTP\\HeaderExtension\\HeaderExtensions' => $vendorDir . '/danog/php-rtc-rtp/src/HeaderExtension/HeaderExtensions.php',
    'Webrtc\\RTP\\HeaderExtension\\HeaderExtensionsMap' => $vendorDir . '/danog/php-rtc-rtp/src/HeaderExtension/HeaderExtensionsMap.php',
    'Webrtc\\RTP\\Jitter\\JitterBuffer' => $vendorDir . '/danog/php-rtc-rtp/src/Jitter/JitterBuffer.php',
    'Webrtc\\RTP\\Jitter\\JitterFrame' => $vendorDir . '/danog/php-rtc-rtp/src/Jitter/JitterFrame.php',
    'Webrtc\\RTP\\Listener\\MediaStreamTrackEndedListener' => $vendorDir . '/danog/php-rtc-rtp/src/Listener/MediaStreamTrackEndedListener.php',
    'Webrtc\\RTP\\MediaStreamTrack\\MediaStreamTrack' => $vendorDir . '/danog/php-rtc-rtp/src/MediaStreamTrack/MediaStreamTrack.php',
    'Webrtc\\RTP\\MediaStreamTrack\\MediaStreamTrackSink' => $vendorDir . '/danog/php-rtc-rtp/src/MediaStreamTrack/MediaStreamTrackSink.php',
    'Webrtc\\RTP\\MediaStreamTrack\\RTCTrackEvent' => $vendorDir . '/danog/php-rtc-rtp/src/MediaStreamTrack/RTCTrackEvent.php',
    'Webrtc\\RTP\\MediaStreamTrack\\RemoteStreamTrack' => $vendorDir . '/danog/php-rtc-rtp/src/MediaStreamTrack/RemoteStreamTrack.php',
    'Webrtc\\RTP\\RTCRTPDtlsTransportInterface' => $vendorDir . '/danog/php-rtc-rtp/src/RTCRTPDtlsTransportInterface.php',
    'Webrtc\\RTP\\RTCRtpTransceiver' => $vendorDir . '/danog/php-rtc-rtp/src/RTCRtpTransceiver.php',
    'Webrtc\\RTP\\Receiver\\DecoderQueue' => $vendorDir . '/danog/php-rtc-rtp/src/Receiver/DecoderQueue.php',
    'Webrtc\\RTP\\Receiver\\NackGenerator' => $vendorDir . '/danog/php-rtc-rtp/src/Receiver/NackGenerator.php',
    'Webrtc\\RTP\\Receiver\\RTCRtpReceiver' => $vendorDir . '/danog/php-rtc-rtp/src/Receiver/RTCRtpReceiver.php',
    'Webrtc\\RTP\\Receiver\\Rate\\AimdRateControl' => $vendorDir . '/danog/php-rtc-rtp/src/Receiver/Rate/AimdRateControl.php',
    'Webrtc\\RTP\\Receiver\\Rate\\InterArrival' => $vendorDir . '/danog/php-rtc-rtp/src/Receiver/Rate/InterArrival.php',
    'Webrtc\\RTP\\Receiver\\Rate\\InterArrivalDelta' => $vendorDir . '/danog/php-rtc-rtp/src/Receiver/Rate/InterArrivalDelta.php',
    'Webrtc\\RTP\\Receiver\\Rate\\OveruseDetector' => $vendorDir . '/danog/php-rtc-rtp/src/Receiver/Rate/OveruseDetector.php',
    'Webrtc\\RTP\\Receiver\\Rate\\OveruseEstimator' => $vendorDir . '/danog/php-rtc-rtp/src/Receiver/Rate/OveruseEstimator.php',
    'Webrtc\\RTP\\Receiver\\Rate\\RateBucket' => $vendorDir . '/danog/php-rtc-rtp/src/Receiver/Rate/RateBucket.php',
    'Webrtc\\RTP\\Receiver\\Rate\\RateCounter' => $vendorDir . '/danog/php-rtc-rtp/src/Receiver/Rate/RateCounter.php',
    'Webrtc\\RTP\\Receiver\\Rate\\RemoteBitrateEstimator' => $vendorDir . '/danog/php-rtc-rtp/src/Receiver/Rate/RemoteBitrateEstimator.php',
    'Webrtc\\RTP\\Receiver\\Rate\\TimestampGroup' => $vendorDir . '/danog/php-rtc-rtp/src/Receiver/Rate/TimestampGroup.php',
    'Webrtc\\RTP\\Receiver\\RtpReceiverInterface' => $vendorDir . '/danog/php-rtc-rtp/src/Receiver/RtpReceiverInterface.php',
    'Webrtc\\RTP\\Receiver\\StreamStatistics' => $vendorDir . '/danog/php-rtc-rtp/src/Receiver/StreamStatistics.php',
    'Webrtc\\RTP\\Receiver\\TimestampMapper' => $vendorDir . '/danog/php-rtc-rtp/src/Receiver/TimestampMapper.php',
    'Webrtc\\RTP\\RtpComponentLogger' => $vendorDir . '/danog/php-rtc-rtp/src/RtpComponentLogger.php',
    'Webrtc\\RTP\\RtpConstants' => $vendorDir . '/danog/php-rtc-rtp/src/RtpConstants.php',
    'Webrtc\\RTP\\RtpPacket' => $vendorDir . '/danog/php-rtc-rtp/src/RtpPacket.php',
    'Webrtc\\RTP\\RtpRouter' => $vendorDir . '/danog/php-rtc-rtp/src/RtpRouter.php',
    'Webrtc\\RTP\\RtpUtility' => $vendorDir . '/danog/php-rtc-rtp/src/RtpUtility.php',
    'Webrtc\\RTP\\Sender\\RTCEncodedFrame' => $vendorDir . '/danog/php-rtc-rtp/src/Sender/RTCEncodedFrame.php',
    'Webrtc\\RTP\\Sender\\RTCRtpSender' => $vendorDir . '/danog/php-rtc-rtp/src/Sender/RTCRtpSender.php',
    'Webrtc\\RTP\\Sender\\RtpSenderInterface' => $vendorDir . '/danog/php-rtc-rtp/src/Sender/RtpSenderInterface.php',
    'Webrtc\\RTP\\TransportSequenceNumbers' => $vendorDir . '/danog/php-rtc-rtp/src/TransportSequenceNumbers.php',
    'Webrtc\\SCTP\\Chunk\\AbortChunk' => $vendorDir . '/danog/php-rtc-sctp/src/Chunk/AbortChunk.php',
    'Webrtc\\SCTP\\Chunk\\AttributeChunk' => $vendorDir . '/danog/php-rtc-sctp/src/Chunk/AttributeChunk.php',
    'Webrtc\\SCTP\\Chunk\\BaseInitChunk' => $vendorDir . '/danog/php-rtc-sctp/src/Chunk/BaseInitChunk.php',
    'Webrtc\\SCTP\\Chunk\\BaseParamsChunk' => $vendorDir . '/danog/php-rtc-sctp/src/Chunk/BaseParamsChunk.php',
    'Webrtc\\SCTP\\Chunk\\Chunk' => $vendorDir . '/danog/php-rtc-sctp/src/Chunk/Chunk.php',
    'Webrtc\\SCTP\\Chunk\\ChunkInterface' => $vendorDir . '/danog/php-rtc-sctp/src/Chunk/ChunkInterface.php',
    'Webrtc\\SCTP\\Chunk\\CookieAckChunk' => $vendorDir . '/danog/php-rtc-sctp/src/Chunk/CookieAckChunk.php',
    'Webrtc\\SCTP\\Chunk\\CookieEchoChunk' => $vendorDir . '/danog/php-rtc-sctp/src/Chunk/CookieEchoChunk.php',
    'Webrtc\\SCTP\\Chunk\\DataChunk' => $vendorDir . '/danog/php-rtc-sctp/src/Chunk/DataChunk.php',
    'Webrtc\\SCTP\\Chunk\\ErrorChunk' => $vendorDir . '/danog/php-rtc-sctp/src/Chunk/ErrorChunk.php',
    'Webrtc\\SCTP\\Chunk\\ForwardTsnChunk' => $vendorDir . '/danog/php-rtc-sctp/src/Chunk/ForwardTsnChunk.php',
    'Webrtc\\SCTP\\Chunk\\HeartbeatAckChunk' => $vendorDir . '/danog/php-rtc-sctp/src/Chunk/HeartbeatAckChunk.php',
    'Webrtc\\SCTP\\Chunk\\HeartbeatChunk' => $vendorDir . '/danog/php-rtc-sctp/src/Chunk/HeartbeatChunk.php',
    'Webrtc\\SCTP\\Chunk\\InitAckChunk' => $vendorDir . '/danog/php-rtc-sctp/src/Chunk/InitAckChunk.php',
    'Webrtc\\SCTP\\Chunk\\InitChunk' => $vendorDir . '/danog/php-rtc-sctp/src/Chunk/InitChunk.php',
    'Webrtc\\SCTP\\Chunk\\ReconfigChunk' => $vendorDir . '/danog/php-rtc-sctp/src/Chunk/ReconfigChunk.php',
    'Webrtc\\SCTP\\Chunk\\SackChunk' => $vendorDir . '/danog/php-rtc-sctp/src/Chunk/SackChunk.php',
    'Webrtc\\SCTP\\Chunk\\ShutdownAckChunk' => $vendorDir . '/danog/php-rtc-sctp/src/Chunk/ShutdownAckChunk.php',
    'Webrtc\\SCTP\\Chunk\\ShutdownChunk' => $vendorDir . '/danog/php-rtc-sctp/src/Chunk/ShutdownChunk.php',
    'Webrtc\\SCTP\\Chunk\\ShutdownCompleteChunk' => $vendorDir . '/danog/php-rtc-sctp/src/Chunk/ShutdownCompleteChunk.php',
    'Webrtc\\SCTP\\Enum\\State' => $vendorDir . '/danog/php-rtc-sctp/src/Enum/State.php',
    'Webrtc\\SCTP\\Exception\\SctpException' => $vendorDir . '/danog/php-rtc-sctp/src/Exception/SctpException.php',
    'Webrtc\\SCTP\\Exception\\SctpExceptionInterface' => $vendorDir . '/danog/php-rtc-sctp/src/Exception/SctpExceptionInterface.php',
    'Webrtc\\SCTP\\InboundStream' => $vendorDir . '/danog/php-rtc-sctp/src/InboundStream.php',
    'Webrtc\\SCTP\\Listener\\DataChannelListener' => $vendorDir . '/danog/php-rtc-sctp/src/Listener/DataChannelListener.php',
    'Webrtc\\SCTP\\Param\\StreamAddOutgoingParam' => $vendorDir . '/danog/php-rtc-sctp/src/Param/StreamAddOutgoingParam.php',
    'Webrtc\\SCTP\\Param\\StreamParamInterface' => $vendorDir . '/danog/php-rtc-sctp/src/Param/StreamParamInterface.php',
    'Webrtc\\SCTP\\Param\\StreamResetOutgoingParam' => $vendorDir . '/danog/php-rtc-sctp/src/Param/StreamResetOutgoingParam.php',
    'Webrtc\\SCTP\\Param\\StreamResetResponseParam' => $vendorDir . '/danog/php-rtc-sctp/src/Param/StreamResetResponseParam.php',
    'Webrtc\\SCTP\\RTCSctpDtlsTransportInterface' => $vendorDir . '/danog/php-rtc-sctp/src/RTCSctpDtlsTransportInterface.php',
    'Webrtc\\SCTP\\RTCSctpTransport' => $vendorDir . '/danog/php-rtc-sctp/src/RTCSctpTransport.php',
    'Webrtc\\SCTP\\SctpConstant' => $vendorDir . '/danog/php-rtc-sctp/src/SctpConstant.php',
    'Webrtc\\SCTP\\SctpPacket' => $vendorDir . '/danog/php-rtc-sctp/src/SctpPacket.php',
    'Webrtc\\SCTP\\SctpTimer' => $vendorDir . '/danog/php-rtc-sctp/src/SctpTimer.php',
    'Webrtc\\SCTP\\SctpUtility' => $vendorDir . '/danog/php-rtc-sctp/src/SctpUtility.php',
    'Webrtc\\SCTP\\SignalingSinkInterface' => $vendorDir . '/danog/php-rtc-sctp/src/SignalingSinkInterface.php',
    'Webrtc\\SCTP\\Trait\\DataChannel' => $vendorDir . '/danog/php-rtc-sctp/src/Trait/DataChannel.php',
    'Webrtc\\SDP\\BitPattern' => $vendorDir . '/danog/php-rtc-sdp/src/BitPattern.php',
    'Webrtc\\SDP\\DtlsParameter\\RTCDtlsFingerprint' => $vendorDir . '/danog/php-rtc-sdp/src/DtlsParameter/RTCDtlsFingerprint.php',
    'Webrtc\\SDP\\DtlsParameter\\RTCDtlsParameters' => $vendorDir . '/danog/php-rtc-sdp/src/DtlsParameter/RTCDtlsParameters.php',
    'Webrtc\\SDP\\Enum\\DtlsRole' => $vendorDir . '/danog/php-rtc-sdp/src/Enum/DtlsRole.php',
    'Webrtc\\SDP\\Enum\\H264Level' => $vendorDir . '/danog/php-rtc-sdp/src/Enum/H264Level.php',
    'Webrtc\\SDP\\Enum\\H264Profile' => $vendorDir . '/danog/php-rtc-sdp/src/Enum/H264Profile.php',
    'Webrtc\\SDP\\Enum\\SDPDirections' => $vendorDir . '/danog/php-rtc-sdp/src/Enum/SDPDirections.php',
    'Webrtc\\SDP\\Enum\\SDPType' => $vendorDir . '/danog/php-rtc-sdp/src/Enum/SDPType.php',
    'Webrtc\\SDP\\GroupDescription' => $vendorDir . '/danog/php-rtc-sdp/src/GroupDescription.php',
    'Webrtc\\SDP\\H264Sdp' => $vendorDir . '/danog/php-rtc-sdp/src/H264Sdp.php',
    'Webrtc\\SDP\\MediaDescription' => $vendorDir . '/danog/php-rtc-sdp/src/MediaDescription.php',
    'Webrtc\\SDP\\RTCSessionDescription' => $vendorDir . '/danog/php-rtc-sdp/src/RTCSessionDescription.php',
    'Webrtc\\SDP\\SDPUtility' => $vendorDir . '/danog/php-rtc-sdp/src/SDPUtility.php',
    'Webrtc\\SDP\\SctpParameter\\RTCSctpCapabilities' => $vendorDir . '/danog/php-rtc-sdp/src/SctpParameter/RTCSctpCapabilities.php',
    'Webrtc\\SDP\\SessionDescription' => $vendorDir . '/danog/php-rtc-sdp/src/SessionDescription.php',
    'Webrtc\\SDP\\SsrcDescription' => $vendorDir . '/danog/php-rtc-sdp/src/SsrcDescription.php',
    'Webrtc\\STUN\\BaseProtocol' => $vendorDir . '/danog/php-rtc-stun/src/BaseProtocol.php',
    'Webrtc\\STUN\\BaseProtocolInterface' => $vendorDir . '/danog/php-rtc-stun/src/BaseProtocolInterface.php',
    'Webrtc\\STUN\\Datagram' => $vendorDir . '/danog/php-rtc-stun/src/Datagram.php',
    'Webrtc\\STUN\\Enum\\MessageAttribute' => $vendorDir . '/danog/php-rtc-stun/src/Enum/MessageAttribute.php',
    'Webrtc\\STUN\\Enum\\MessageClass' => $vendorDir . '/danog/php-rtc-stun/src/Enum/MessageClass.php',
    'Webrtc\\STUN\\Enum\\MessageMethod' => $vendorDir . '/danog/php-rtc-stun/src/Enum/MessageMethod.php',
    'Webrtc\\STUN\\Exception\\TransactionException' => $vendorDir . '/danog/php-rtc-stun/src/Exception/TransactionException.php',
    'Webrtc\\STUN\\Exception\\TransactionExceptionInterface' => $vendorDir . '/danog/php-rtc-stun/src/Exception/TransactionExceptionInterface.php',
    'Webrtc\\STUN\\Exception\\TransactionFailedException' => $vendorDir . '/danog/php-rtc-stun/src/Exception/TransactionFailedException.php',
    'Webrtc\\STUN\\Exception\\TransactionTimeoutException' => $vendorDir . '/danog/php-rtc-stun/src/Exception/TransactionTimeoutException.php',
    'Webrtc\\STUN\\IceCandidateInterface' => $vendorDir . '/danog/php-rtc-stun/src/IceCandidateInterface.php',
    'Webrtc\\STUN\\IceConnectionProtocolInterface' => $vendorDir . '/danog/php-rtc-stun/src/IceConnectionProtocolInterface.php',
    'Webrtc\\STUN\\Message\\Message' => $vendorDir . '/danog/php-rtc-stun/src/Message/Message.php',
    'Webrtc\\STUN\\Message\\MessageAttributeCollection' => $vendorDir . '/danog/php-rtc-stun/src/Message/MessageAttributeCollection.php',
    'Webrtc\\STUN\\Message\\MessageAttributeEncoder' => $vendorDir . '/danog/php-rtc-stun/src/Message/MessageAttributeEncoder.php',
    'Webrtc\\STUN\\Message\\MessageIntegrity' => $vendorDir . '/danog/php-rtc-stun/src/Message/MessageIntegrity.php',
    'Webrtc\\STUN\\Message\\MessageInterface' => $vendorDir . '/danog/php-rtc-stun/src/Message/MessageInterface.php',
    'Webrtc\\STUN\\ReceiverInterface' => $vendorDir . '/danog/php-rtc-stun/src/ReceiverInterface.php',
    'Webrtc\\STUN\\RequestInterface' => $vendorDir . '/danog/php-rtc-stun/src/RequestInterface.php',
    'Webrtc\\STUN\\Stun' => $vendorDir . '/danog/php-rtc-stun/src/Stun.php',
    'Webrtc\\STUN\\StunInterface' => $vendorDir . '/danog/php-rtc-stun/src/StunInterface.php',
    'Webrtc\\STUN\\Trait\\CandidateSetterGetter' => $vendorDir . '/danog/php-rtc-stun/src/Trait/CandidateSetterGetter.php',
    'Webrtc\\STUN\\Trait\\Request' => $vendorDir . '/danog/php-rtc-stun/src/Trait/Request.php',
    'Webrtc\\STUN\\Transaction' => $vendorDir . '/danog/php-rtc-stun/src/Transaction.php',
    'Webrtc\\STUN\\TransactionInterface' => $vendorDir . '/danog/php-rtc-stun/src/TransactionInterface.php',
    'Webrtc\\STUN\\Utils' => $vendorDir . '/danog/php-rtc-stun/src/Utils.php',
    'Webrtc\\Srtp\\Aes' => $vendorDir . '/danog/php-rtc-srtp/src/Aes.php',
    'Webrtc\\Srtp\\Enum\\SrtpProfile' => $vendorDir . '/danog/php-rtc-srtp/src/Enum/SrtpProfile.php',
    'Webrtc\\Srtp\\Enum\\SsrcType' => $vendorDir . '/danog/php-rtc-srtp/src/Enum/SsrcType.php',
    'Webrtc\\Srtp\\Exception\\SrtpException' => $vendorDir . '/danog/php-rtc-srtp/src/Exception/SrtpException.php',
    'Webrtc\\Srtp\\Exception\\SrtpExceptionInterface' => $vendorDir . '/danog/php-rtc-srtp/src/Exception/SrtpExceptionInterface.php',
    'Webrtc\\Srtp\\Exception\\SrtpValidateException' => $vendorDir . '/danog/php-rtc-srtp/src/Exception/SrtpValidateException.php',
    'Webrtc\\Srtp\\Policy' => $vendorDir . '/danog/php-rtc-srtp/src/Policy.php',
    'Webrtc\\Srtp\\Session' => $vendorDir . '/danog/php-rtc-srtp/src/Session.php',
    'Webrtc\\Stats\\RTCInboundRtpStreamStats' => $vendorDir . '/danog/php-rtc-stats/src/RTCInboundRtpStreamStats.php',
    'Webrtc\\Stats\\RTCOutboundRtpStreamStats' => $vendorDir . '/danog/php-rtc-stats/src/RTCOutboundRtpStreamStats.php',
    'Webrtc\\Stats\\RTCReceivedRtpStreamStats' => $vendorDir . '/danog/php-rtc-stats/src/RTCReceivedRtpStreamStats.php',
    'Webrtc\\Stats\\RTCRemoteInboundRtpStreamStats' => $vendorDir . '/danog/php-rtc-stats/src/RTCRemoteInboundRtpStreamStats.php',
    'Webrtc\\Stats\\RTCRemoteOutboundRtpStreamStats' => $vendorDir . '/danog/php-rtc-stats/src/RTCRemoteOutboundRtpStreamStats.php',
    'Webrtc\\Stats\\RTCRtpStreamStats' => $vendorDir . '/danog/php-rtc-stats/src/RTCRtpStreamStats.php',
    'Webrtc\\Stats\\RTCSentRtpStreamStats' => $vendorDir . '/danog/php-rtc-stats/src/RTCSentRtpStreamStats.php',
    'Webrtc\\Stats\\RTCStats' => $vendorDir . '/danog/php-rtc-stats/src/RTCStats.php',
    'Webrtc\\Stats\\RTCStatsReport' => $vendorDir . '/danog/php-rtc-stats/src/RTCStatsReport.php',
    'Webrtc\\Stats\\RTCTransportStats' => $vendorDir . '/danog/php-rtc-stats/src/RTCTransportStats.php',
    'Webrtc\\Stats\\enum\\StatType' => $vendorDir . '/danog/php-rtc-stats/src/enum/StatType.php',
    'Webrtc\\Stats\\enum\\TLSState' => $vendorDir . '/danog/php-rtc-stats/src/enum/TLSState.php',
    'Webrtc\\TURN\\TCPConnection' => $vendorDir . '/danog/php-rtc-turn/src/TCPConnection.php',
    'Webrtc\\TURN\\Trait\\TurnConnection' => $vendorDir . '/danog/php-rtc-turn/src/Trait/TurnConnection.php',
    'Webrtc\\TURN\\Turn' => $vendorDir . '/danog/php-rtc-turn/src/Turn.php',
    'Webrtc\\TURN\\TurnConfigurationInterface' => $vendorDir . '/danog/php-rtc-turn/src/TurnConfigurationInterface.php',
    'Webrtc\\TURN\\TurnConnectionInterface' => $vendorDir . '/danog/php-rtc-turn/src/TurnConnectionInterface.php',
    'Webrtc\\TURN\\TurnInterface' => $vendorDir . '/danog/php-rtc-turn/src/TurnInterface.php',
    'Webrtc\\TURN\\TurnTcpConnection' => $vendorDir . '/danog/php-rtc-turn/src/TurnTcpConnection.php',
    'Webrtc\\TURN\\TurnUdpConnection' => $vendorDir . '/danog/php-rtc-turn/src/TurnUdpConnection.php',
    'Webrtc\\Webrtc\\Enum\\ConnectionState' => $vendorDir . '/danog/php-rtc-webrtc/src/Enum/ConnectionState.php',
    'Webrtc\\Webrtc\\Enum\\IceConnectionState' => $vendorDir . '/danog/php-rtc-webrtc/src/Enum/IceConnectionState.php',
    'Webrtc\\Webrtc\\Enum\\SignalingState' => $vendorDir . '/danog/php-rtc-webrtc/src/Enum/SignalingState.php',
    'Webrtc\\Webrtc\\Listener\\PeerConnectionConnectionStateChangeListener' => $vendorDir . '/danog/php-rtc-webrtc/src/Listener/PeerConnectionConnectionStateChangeListener.php',
    'Webrtc\\Webrtc\\Listener\\PeerConnectionDataChannelListener' => $vendorDir . '/danog/php-rtc-webrtc/src/Listener/PeerConnectionDataChannelListener.php',
    'Webrtc\\Webrtc\\Listener\\PeerConnectionIceConnectionStateChangeListener' => $vendorDir . '/danog/php-rtc-webrtc/src/Listener/PeerConnectionIceConnectionStateChangeListener.php',
    'Webrtc\\Webrtc\\Listener\\PeerConnectionIceGatheringStateChangeListener' => $vendorDir . '/danog/php-rtc-webrtc/src/Listener/PeerConnectionIceGatheringStateChangeListener.php',
    'Webrtc\\Webrtc\\Listener\\PeerConnectionSignalingStateChangeListener' => $vendorDir . '/danog/php-rtc-webrtc/src/Listener/PeerConnectionSignalingStateChangeListener.php',
    'Webrtc\\Webrtc\\Listener\\PeerConnectionTrackListener' => $vendorDir . '/danog/php-rtc-webrtc/src/Listener/PeerConnectionTrackListener.php',
    'Webrtc\\Webrtc\\RTCConfiguration' => $vendorDir . '/danog/php-rtc-webrtc/src/RTCConfiguration.php',
    'Webrtc\\Webrtc\\RTCConfigurationInterface' => $vendorDir . '/danog/php-rtc-webrtc/src/RTCConfigurationInterface.php',
    'Webrtc\\Webrtc\\RTCPeerConnection' => $vendorDir . '/danog/php-rtc-webrtc/src/RTCPeerConnection.php',
    'Webrtc\\Webrtc\\RTCPeerConnectionInterface' => $vendorDir . '/danog/php-rtc-webrtc/src/RTCPeerConnectionInterface.php',
    'danog\\AsyncOrm\\Annotations\\OrmMappedArray' => $vendorDir . '/danog/async-orm/src/Annotations/OrmMappedArray.php',
    'danog\\AsyncOrm\\DbArray' => $vendorDir . '/danog/async-orm/src/DbArray.php',
    'danog\\AsyncOrm\\DbArrayBuilder' => $vendorDir . '/danog/async-orm/src/DbArrayBuilder.php',
    'danog\\AsyncOrm\\DbAutoProperties' => $vendorDir . '/danog/async-orm/src/DbAutoProperties.php',
    'danog\\AsyncOrm\\DbObject' => $vendorDir . '/danog/async-orm/src/DbObject.php',
    'danog\\AsyncOrm\\Driver\\DriverArray' => $vendorDir . '/danog/async-orm/src/Driver/DriverArray.php',
    'danog\\AsyncOrm\\Driver\\MemoryArray' => $vendorDir . '/danog/async-orm/src/Driver/MemoryArray.php',
    'danog\\AsyncOrm\\Driver\\SqlArray' => $vendorDir . '/danog/async-orm/src/Driver/SqlArray.php',
    'danog\\AsyncOrm\\Internal\\Containers\\CacheContainer' => $vendorDir . '/danog/async-orm/src/Internal/Containers/CacheContainer.php',
    'danog\\AsyncOrm\\Internal\\Containers\\ObjectContainer' => $vendorDir . '/danog/async-orm/src/Internal/Containers/ObjectContainer.php',
    'danog\\AsyncOrm\\Internal\\Containers\\ObjectReference' => $vendorDir . '/danog/async-orm/src/Internal/Containers/ObjectReference.php',
    'danog\\AsyncOrm\\Internal\\Driver\\CachedArray' => $vendorDir . '/danog/async-orm/src/Internal/Driver/CachedArray.php',
    'danog\\AsyncOrm\\Internal\\Driver\\MysqlArray' => $vendorDir . '/danog/async-orm/src/Internal/Driver/MysqlArray.php',
    'danog\\AsyncOrm\\Internal\\Driver\\ObjectArray' => $vendorDir . '/danog/async-orm/src/Internal/Driver/ObjectArray.php',
    'danog\\AsyncOrm\\Internal\\Driver\\PostgresArray' => $vendorDir . '/danog/async-orm/src/Internal/Driver/PostgresArray.php',
    'danog\\AsyncOrm\\Internal\\Driver\\RedisArray' => $vendorDir . '/danog/async-orm/src/Internal/Driver/RedisArray.php',
    'danog\\AsyncOrm\\Internal\\Serializer\\BoolInt' => $vendorDir . '/danog/async-orm/src/Internal/Serializer/BoolInt.php',
    'danog\\AsyncOrm\\Internal\\Serializer\\BoolString' => $vendorDir . '/danog/async-orm/src/Internal/Serializer/BoolString.php',
    'danog\\AsyncOrm\\Internal\\Serializer\\ByteaSerializer' => $vendorDir . '/danog/async-orm/src/Internal/Serializer/ByteaSerializer.php',
    'danog\\AsyncOrm\\Internal\\Serializer\\FloatString' => $vendorDir . '/danog/async-orm/src/Internal/Serializer/FloatString.php',
    'danog\\AsyncOrm\\Internal\\Serializer\\IntString' => $vendorDir . '/danog/async-orm/src/Internal/Serializer/IntString.php',
    'danog\\AsyncOrm\\Internal\\Serializer\\Passthrough' => $vendorDir . '/danog/async-orm/src/Internal/Serializer/Passthrough.php',
    'danog\\AsyncOrm\\KeyType' => $vendorDir . '/danog/async-orm/src/KeyType.php',
    'danog\\AsyncOrm\\Serializer' => $vendorDir . '/danog/async-orm/src/Serializer.php',
    'danog\\AsyncOrm\\Serializer\\Igbinary' => $vendorDir . '/danog/async-orm/src/Serializer/Igbinary.php',
    'danog\\AsyncOrm\\Serializer\\Json' => $vendorDir . '/danog/async-orm/src/Serializer/Json.php',
    'danog\\AsyncOrm\\Serializer\\Native' => $vendorDir . '/danog/async-orm/src/Serializer/Native.php',
    'danog\\AsyncOrm\\Settings' => $vendorDir . '/danog/async-orm/src/Settings.php',
    'danog\\AsyncOrm\\Settings\\DriverSettings' => $vendorDir . '/danog/async-orm/src/Settings/DriverSettings.php',
    'danog\\AsyncOrm\\Settings\\MemorySettings' => $vendorDir . '/danog/async-orm/src/Settings/MemorySettings.php',
    'danog\\AsyncOrm\\Settings\\MysqlSettings' => $vendorDir . '/danog/async-orm/src/Settings/MysqlSettings.php',
    'danog\\AsyncOrm\\Settings\\PostgresSettings' => $vendorDir . '/danog/async-orm/src/Settings/PostgresSettings.php',
    'danog\\AsyncOrm\\Settings\\RedisSettings' => $vendorDir . '/danog/async-orm/src/Settings/RedisSettings.php',
    'danog\\AsyncOrm\\Settings\\SqlSettings' => $vendorDir . '/danog/async-orm/src/Settings/SqlSettings.php',
    'danog\\AsyncOrm\\ValueType' => $vendorDir . '/danog/async-orm/src/ValueType.php',
    'danog\\BetterPrometheus\\BetterCollector' => $vendorDir . '/danog/better-prometheus/lib/BetterCollector.php',
    'danog\\BetterPrometheus\\BetterCollectorRegistry' => $vendorDir . '/danog/better-prometheus/lib/BetterCollectorRegistry.php',
    'danog\\BetterPrometheus\\BetterCounter' => $vendorDir . '/danog/better-prometheus/lib/BetterCounter.php',
    'danog\\BetterPrometheus\\BetterGauge' => $vendorDir . '/danog/better-prometheus/lib/BetterGauge.php',
    'danog\\BetterPrometheus\\BetterHistogram' => $vendorDir . '/danog/better-prometheus/lib/BetterHistogram.php',
    'danog\\BetterPrometheus\\BetterSummary' => $vendorDir . '/danog/better-prometheus/lib/BetterSummary.php',
    'danog\\Decoder\\FileId' => $vendorDir . '/danog/tg-file-decoder/src/FileId.php',
    'danog\\Decoder\\FileIdType' => $vendorDir . '/danog/tg-file-decoder/src/FileIdType.php',
    'danog\\Decoder\\PhotoSizeSource' => $vendorDir . '/danog/tg-file-decoder/src/PhotoSizeSource.php',
    'danog\\Decoder\\PhotoSizeSourceType' => $vendorDir . '/danog/tg-file-decoder/src/PhotoSizeSourceType.php',
    'danog\\Decoder\\PhotoSizeSource\\PhotoSizeSourceDialogPhoto' => $vendorDir . '/danog/tg-file-decoder/src/PhotoSizeSource/PhotoSizeSourceDialogPhoto.php',
    'danog\\Decoder\\PhotoSizeSource\\PhotoSizeSourceDialogPhotoBig' => $vendorDir . '/danog/tg-file-decoder/src/PhotoSizeSource/PhotoSizeSourceDialogPhotoBig.php',
    'danog\\Decoder\\PhotoSizeSource\\PhotoSizeSourceDialogPhotoSmall' => $vendorDir . '/danog/tg-file-decoder/src/PhotoSizeSource/PhotoSizeSourceDialogPhotoSmall.php',
    'danog\\Decoder\\PhotoSizeSource\\PhotoSizeSourceLegacy' => $vendorDir . '/danog/tg-file-decoder/src/PhotoSizeSource/PhotoSizeSourceLegacy.php',
    'danog\\Decoder\\PhotoSizeSource\\PhotoSizeSourceStickersetThumbnail' => $vendorDir . '/danog/tg-file-decoder/src/PhotoSizeSource/PhotoSizeSourceStickersetThumbnail.php',
    'danog\\Decoder\\PhotoSizeSource\\PhotoSizeSourceStickersetThumbnailVersion' => $vendorDir . '/danog/tg-file-decoder/src/PhotoSizeSource/PhotoSizeSourceStickersetThumbnailVersion.php',
    'danog\\Decoder\\PhotoSizeSource\\PhotoSizeSourceThumbnail' => $vendorDir . '/danog/tg-file-decoder/src/PhotoSizeSource/PhotoSizeSourceThumbnail.php',
    'danog\\Decoder\\Tools' => $vendorDir . '/danog/tg-file-decoder/src/Tools.php',
    'danog\\Decoder\\UniqueFileId' => $vendorDir . '/danog/tg-file-decoder/src/UniqueFileId.php',
    'danog\\Decoder\\UniqueFileIdType' => $vendorDir . '/danog/tg-file-decoder/src/UniqueFileIdType.php',
    'danog\\DialogId\\DialogId' => $vendorDir . '/danog/tg-dialog-id/src/DialogId.php',
    'danog\\LibDNSJson\\JsonDecoder' => $vendorDir . '/danog/libdns-json/lib/JsonDecoder.php',
    'danog\\LibDNSJson\\JsonDecoderFactory' => $vendorDir . '/danog/libdns-json/lib/JsonDecoderFactory.php',
    'danog\\LibDNSJson\\QueryEncoder' => $vendorDir . '/danog/libdns-json/lib/QueryEncoder.php',
    'danog\\LibDNSJson\\QueryEncoderFactory' => $vendorDir . '/danog/libdns-json/lib/QueryEncoderFactory.php',
    'danog\\Loop\\GenericLoop' => $vendorDir . '/danog/loop/lib/GenericLoop.php',
    'danog\\Loop\\Loop' => $vendorDir . '/danog/loop/lib/Loop.php',
    'danog\\Loop\\PeriodicLoop' => $vendorDir . '/danog/loop/lib/PeriodicLoop.php',
    'danog\\MadelineProto\\API' => $vendorDir . '/danog/madelineproto/src/API.php',
    'danog\\MadelineProto\\APIWrapper' => $vendorDir . '/danog/madelineproto/src/APIWrapper.php',
    'danog\\MadelineProto\\AbstractAPI' => $vendorDir . '/danog/madelineproto/src/AbstractAPI.php',
    'danog\\MadelineProto\\ApiWrappers\\Start' => $vendorDir . '/danog/madelineproto/src/ApiWrappers/Start.php',
    'danog\\MadelineProto\\AsyncTools' => $vendorDir . '/danog/madelineproto/src/AsyncTools.php',
    'danog\\MadelineProto\\BotApiFileId' => $vendorDir . '/danog/madelineproto/src/BotApiFileId.php',
    'danog\\MadelineProto\\Broadcast\\Action' => $vendorDir . '/danog/madelineproto/src/Broadcast/Action.php',
    'danog\\MadelineProto\\Broadcast\\Action\\ActionForward' => $vendorDir . '/danog/madelineproto/src/Broadcast/Action/ActionForward.php',
    'danog\\MadelineProto\\Broadcast\\Action\\ActionSend' => $vendorDir . '/danog/madelineproto/src/Broadcast/Action/ActionSend.php',
    'danog\\MadelineProto\\Broadcast\\Broadcast' => $vendorDir . '/danog/madelineproto/src/Broadcast/Broadcast.php',
    'danog\\MadelineProto\\Broadcast\\BroadcastCancelledException' => $vendorDir . '/danog/madelineproto/src/Broadcast/BroadcastCancelledException.php',
    'danog\\MadelineProto\\Broadcast\\Filter' => $vendorDir . '/danog/madelineproto/src/Broadcast/Filter.php',
    'danog\\MadelineProto\\Broadcast\\InternalState' => $vendorDir . '/danog/madelineproto/src/Broadcast/InternalState.php',
    'danog\\MadelineProto\\Broadcast\\Progress' => $vendorDir . '/danog/madelineproto/src/Broadcast/Progress.php',
    'danog\\MadelineProto\\Broadcast\\Status' => $vendorDir . '/danog/madelineproto/src/Broadcast/Status.php',
    'danog\\MadelineProto\\Broadcast\\StatusInternal' => $vendorDir . '/danog/madelineproto/src/Broadcast/StatusInternal.php',
    'danog\\MadelineProto\\CallNotAllowedException' => $vendorDir . '/danog/madelineproto/src/CallNotAllowedException.php',
    'danog\\MadelineProto\\CallStream' => $vendorDir . '/danog/madelineproto/src/CallStream.php',
    'danog\\MadelineProto\\Connection' => $vendorDir . '/danog/madelineproto/src/Connection.php',
    'danog\\MadelineProto\\ContextConnector' => $vendorDir . '/danog/madelineproto/src/ContextConnector.php',
    'danog\\MadelineProto\\Conversion' => $vendorDir . '/danog/madelineproto/src/Conversion.php',
    'danog\\MadelineProto\\DataCenter' => $vendorDir . '/danog/madelineproto/src/DataCenter.php',
    'danog\\MadelineProto\\DataCenterConnection' => $vendorDir . '/danog/madelineproto/src/DataCenterConnection.php',
    'danog\\MadelineProto\\Db\\CachedArray' => $vendorDir . '/danog/madelineproto/src/Db/CachedArray.php',
    'danog\\MadelineProto\\Db\\MemoryArray' => $vendorDir . '/danog/madelineproto/src/Db/MemoryArray.php',
    'danog\\MadelineProto\\DoHConnector' => $vendorDir . '/danog/madelineproto/src/DoHConnector.php',
    'danog\\MadelineProto\\DoHWrapper' => $vendorDir . '/danog/madelineproto/src/DoHWrapper.php',
    'danog\\MadelineProto\\EventHandler' => $vendorDir . '/danog/madelineproto/src/EventHandler.php',
    'danog\\MadelineProto\\EventHandlerIssue' => $vendorDir . '/danog/madelineproto/src/EventHandlerIssue.php',
    'danog\\MadelineProto\\EventHandler\\AbstractLocation' => $vendorDir . '/danog/madelineproto/src/EventHandler/AbstractLocation.php',
    'danog\\MadelineProto\\EventHandler\\AbstractMessage' => $vendorDir . '/danog/madelineproto/src/EventHandler/AbstractMessage.php',
    'danog\\MadelineProto\\EventHandler\\AbstractPoll' => $vendorDir . '/danog/madelineproto/src/EventHandler/AbstractPoll.php',
    'danog\\MadelineProto\\EventHandler\\AbstractPrivateMessage' => $vendorDir . '/danog/madelineproto/src/EventHandler/AbstractPrivateMessage.php',
    'danog\\MadelineProto\\EventHandler\\AbstractStory' => $vendorDir . '/danog/madelineproto/src/EventHandler/AbstractStory.php',
    'danog\\MadelineProto\\EventHandler\\Action' => $vendorDir . '/danog/madelineproto/src/EventHandler/Action.php',
    'danog\\MadelineProto\\EventHandler\\Action\\Cancel' => $vendorDir . '/danog/madelineproto/src/EventHandler/Action/Cancel.php',
    'danog\\MadelineProto\\EventHandler\\Action\\ChooseContact' => $vendorDir . '/danog/madelineproto/src/EventHandler/Action/ChooseContact.php',
    'danog\\MadelineProto\\EventHandler\\Action\\ChooseSticker' => $vendorDir . '/danog/madelineproto/src/EventHandler/Action/ChooseSticker.php',
    'danog\\MadelineProto\\EventHandler\\Action\\EmojiSeen' => $vendorDir . '/danog/madelineproto/src/EventHandler/Action/EmojiSeen.php',
    'danog\\MadelineProto\\EventHandler\\Action\\EmojiTap' => $vendorDir . '/danog/madelineproto/src/EventHandler/Action/EmojiTap.php',
    'danog\\MadelineProto\\EventHandler\\Action\\GamePlay' => $vendorDir . '/danog/madelineproto/src/EventHandler/Action/GamePlay.php',
    'danog\\MadelineProto\\EventHandler\\Action\\GeoLocation' => $vendorDir . '/danog/madelineproto/src/EventHandler/Action/GeoLocation.php',
    'danog\\MadelineProto\\EventHandler\\Action\\GroupCallSpeaking' => $vendorDir . '/danog/madelineproto/src/EventHandler/Action/GroupCallSpeaking.php',
    'danog\\MadelineProto\\EventHandler\\Action\\HistoryImport' => $vendorDir . '/danog/madelineproto/src/EventHandler/Action/HistoryImport.php',
    'danog\\MadelineProto\\EventHandler\\Action\\RecordAudio' => $vendorDir . '/danog/madelineproto/src/EventHandler/Action/RecordAudio.php',
    'danog\\MadelineProto\\EventHandler\\Action\\RecordRound' => $vendorDir . '/danog/madelineproto/src/EventHandler/Action/RecordRound.php',
    'danog\\MadelineProto\\EventHandler\\Action\\RecordVideo' => $vendorDir . '/danog/madelineproto/src/EventHandler/Action/RecordVideo.php',
    'danog\\MadelineProto\\EventHandler\\Action\\Typing' => $vendorDir . '/danog/madelineproto/src/EventHandler/Action/Typing.php',
    'danog\\MadelineProto\\EventHandler\\Action\\UploadAudio' => $vendorDir . '/danog/madelineproto/src/EventHandler/Action/UploadAudio.php',
    'danog\\MadelineProto\\EventHandler\\Action\\UploadDocument' => $vendorDir . '/danog/madelineproto/src/EventHandler/Action/UploadDocument.php',
    'danog\\MadelineProto\\EventHandler\\Action\\UploadPhoto' => $vendorDir . '/danog/madelineproto/src/EventHandler/Action/UploadPhoto.php',
    'danog\\MadelineProto\\EventHandler\\Action\\UploadRound' => $vendorDir . '/danog/madelineproto/src/EventHandler/Action/UploadRound.php',
    'danog\\MadelineProto\\EventHandler\\Action\\UploadVideo' => $vendorDir . '/danog/madelineproto/src/EventHandler/Action/UploadVideo.php',
    'danog\\MadelineProto\\EventHandler\\Attributes\\Cron' => $vendorDir . '/danog/madelineproto/src/EventHandler/Attributes/Cron.php',
    'danog\\MadelineProto\\EventHandler\\Attributes\\Handler' => $vendorDir . '/danog/madelineproto/src/EventHandler/Attributes/Handler.php',
    'danog\\MadelineProto\\EventHandler\\BotApp' => $vendorDir . '/danog/madelineproto/src/EventHandler/BotApp.php',
    'danog\\MadelineProto\\EventHandler\\BotCommands' => $vendorDir . '/danog/madelineproto/src/EventHandler/BotCommands.php',
    'danog\\MadelineProto\\EventHandler\\Call' => $vendorDir . '/danog/madelineproto/src/EventHandler/Call.php',
    'danog\\MadelineProto\\EventHandler\\CallbackQuery' => $vendorDir . '/danog/madelineproto/src/EventHandler/CallbackQuery.php',
    'danog\\MadelineProto\\EventHandler\\Calls\\AbstractGroupCall' => $vendorDir . '/danog/madelineproto/src/EventHandler/Calls/AbstractGroupCall.php',
    'danog\\MadelineProto\\EventHandler\\Calls\\AbstractGroupCallMessage' => $vendorDir . '/danog/madelineproto/src/EventHandler/Calls/AbstractGroupCallMessage.php',
    'danog\\MadelineProto\\EventHandler\\Calls\\AbstractGroupCallParticipant' => $vendorDir . '/danog/madelineproto/src/EventHandler/Calls/AbstractGroupCallParticipant.php',
    'danog\\MadelineProto\\EventHandler\\Calls\\CallDenialReason' => $vendorDir . '/danog/madelineproto/src/EventHandler/Calls/CallDenialReason.php',
    'danog\\MadelineProto\\EventHandler\\Calls\\CallStreams' => $vendorDir . '/danog/madelineproto/src/EventHandler/Calls/CallStreams.php',
    'danog\\MadelineProto\\EventHandler\\Calls\\ConferenceCall' => $vendorDir . '/danog/madelineproto/src/EventHandler/Calls/ConferenceCall.php',
    'danog\\MadelineProto\\EventHandler\\Calls\\ConferenceCallMessage' => $vendorDir . '/danog/madelineproto/src/EventHandler/Calls/ConferenceCallMessage.php',
    'danog\\MadelineProto\\EventHandler\\Calls\\ConferenceCallParticipant' => $vendorDir . '/danog/madelineproto/src/EventHandler/Calls/ConferenceCallParticipant.php',
    'danog\\MadelineProto\\EventHandler\\Calls\\GroupCall' => $vendorDir . '/danog/madelineproto/src/EventHandler/Calls/GroupCall.php',
    'danog\\MadelineProto\\EventHandler\\Calls\\GroupCallDonor' => $vendorDir . '/danog/madelineproto/src/EventHandler/Calls/GroupCallDonor.php',
    'danog\\MadelineProto\\EventHandler\\Calls\\GroupCallMessage' => $vendorDir . '/danog/madelineproto/src/EventHandler/Calls/GroupCallMessage.php',
    'danog\\MadelineProto\\EventHandler\\Calls\\GroupCallMessagesDeleted' => $vendorDir . '/danog/madelineproto/src/EventHandler/Calls/GroupCallMessagesDeleted.php',
    'danog\\MadelineProto\\EventHandler\\Calls\\GroupCallParticipant' => $vendorDir . '/danog/madelineproto/src/EventHandler/Calls/GroupCallParticipant.php',
    'danog\\MadelineProto\\EventHandler\\Calls\\GroupCallParticipants' => $vendorDir . '/danog/madelineproto/src/EventHandler/Calls/GroupCallParticipants.php',
    'danog\\MadelineProto\\EventHandler\\Calls\\GroupCallStars' => $vendorDir . '/danog/madelineproto/src/EventHandler/Calls/GroupCallStars.php',
    'danog\\MadelineProto\\EventHandler\\Calls\\GroupCallState' => $vendorDir . '/danog/madelineproto/src/EventHandler/Calls/GroupCallState.php',
    'danog\\MadelineProto\\EventHandler\\Calls\\LiveStory' => $vendorDir . '/danog/madelineproto/src/EventHandler/Calls/LiveStory.php',
    'danog\\MadelineProto\\EventHandler\\Calls\\LiveStoryMessage' => $vendorDir . '/danog/madelineproto/src/EventHandler/Calls/LiveStoryMessage.php',
    'danog\\MadelineProto\\EventHandler\\Calls\\LiveStoryParticipant' => $vendorDir . '/danog/madelineproto/src/EventHandler/Calls/LiveStoryParticipant.php',
    'danog\\MadelineProto\\EventHandler\\Calls\\MultiCallMessage' => $vendorDir . '/danog/madelineproto/src/EventHandler/Calls/MultiCallMessage.php',
    'danog\\MadelineProto\\EventHandler\\Calls\\MultiCallParticipant' => $vendorDir . '/danog/madelineproto/src/EventHandler/Calls/MultiCallParticipant.php',
    'danog\\MadelineProto\\EventHandler\\Calls\\PrivateCall' => $vendorDir . '/danog/madelineproto/src/EventHandler/Calls/PrivateCall.php',
    'danog\\MadelineProto\\EventHandler\\Channel\\ChannelParticipant' => $vendorDir . '/danog/madelineproto/src/EventHandler/Channel/ChannelParticipant.php',
    'danog\\MadelineProto\\EventHandler\\Channel\\MessageForwards' => $vendorDir . '/danog/madelineproto/src/EventHandler/Channel/MessageForwards.php',
    'danog\\MadelineProto\\EventHandler\\Channel\\MessageViewsChanged' => $vendorDir . '/danog/madelineproto/src/EventHandler/Channel/MessageViewsChanged.php',
    'danog\\MadelineProto\\EventHandler\\Channel\\UpdateChannel' => $vendorDir . '/danog/madelineproto/src/EventHandler/Channel/UpdateChannel.php',
    'danog\\MadelineProto\\EventHandler\\ChatInvite' => $vendorDir . '/danog/madelineproto/src/EventHandler/ChatInvite.php',
    'danog\\MadelineProto\\EventHandler\\ChatInviteRequester' => $vendorDir . '/danog/madelineproto/src/EventHandler/ChatInviteRequester.php',
    'danog\\MadelineProto\\EventHandler\\ChatInviteRequester\\BotChatInviteRequest' => $vendorDir . '/danog/madelineproto/src/EventHandler/ChatInviteRequester/BotChatInviteRequest.php',
    'danog\\MadelineProto\\EventHandler\\ChatInviteRequester\\PendingJoinRequests' => $vendorDir . '/danog/madelineproto/src/EventHandler/ChatInviteRequester/PendingJoinRequests.php',
    'danog\\MadelineProto\\EventHandler\\ChatInvite\\ChatInviteExported' => $vendorDir . '/danog/madelineproto/src/EventHandler/ChatInvite/ChatInviteExported.php',
    'danog\\MadelineProto\\EventHandler\\ChatInvite\\ChatInvitePublicJoin' => $vendorDir . '/danog/madelineproto/src/EventHandler/ChatInvite/ChatInvitePublicJoin.php',
    'danog\\MadelineProto\\EventHandler\\Command' => $vendorDir . '/danog/madelineproto/src/EventHandler/Command.php',
    'danog\\MadelineProto\\EventHandler\\CommandType' => $vendorDir . '/danog/madelineproto/src/EventHandler/CommandType.php',
    'danog\\MadelineProto\\EventHandler\\Delete' => $vendorDir . '/danog/madelineproto/src/EventHandler/Delete.php',
    'danog\\MadelineProto\\EventHandler\\Delete\\DeleteChannelMessages' => $vendorDir . '/danog/madelineproto/src/EventHandler/Delete/DeleteChannelMessages.php',
    'danog\\MadelineProto\\EventHandler\\Delete\\DeleteMessages' => $vendorDir . '/danog/madelineproto/src/EventHandler/Delete/DeleteMessages.php',
    'danog\\MadelineProto\\EventHandler\\Delete\\DeleteScheduledMessages' => $vendorDir . '/danog/madelineproto/src/EventHandler/Delete/DeleteScheduledMessages.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\AbstractFilterFromSender' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/AbstractFilterFromSender.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\AbstractFilterFromSenders' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/AbstractFilterFromSenders.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\Combinator\\FilterNot' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/Combinator/FilterNot.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\Combinator\\FiltersAnd' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/Combinator/FiltersAnd.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\Combinator\\FiltersOr' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/Combinator/FiltersOr.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\Filter' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/Filter.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\FilterAllowAll' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/FilterAllowAll.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\FilterBotCommand' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/FilterBotCommand.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\FilterButtonQueryData' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/FilterButtonQueryData.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\FilterChannel' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/FilterChannel.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\FilterCommand' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/FilterCommand.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\FilterCommandCaseInsensitive' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/FilterCommandCaseInsensitive.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\FilterCommentReply' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/FilterCommentReply.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\FilterEdited' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/FilterEdited.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\FilterEnded' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/FilterEnded.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\FilterForwarded' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/FilterForwarded.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\FilterForwardedFrom' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/FilterForwardedFrom.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\FilterFromAdmin' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/FilterFromAdmin.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\FilterFromBot' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/FilterFromBot.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\FilterFromSender' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/FilterFromSender.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\FilterFromSenders' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/FilterFromSenders.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\FilterGroup' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/FilterGroup.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\FilterIncoming' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/FilterIncoming.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\FilterLocation' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/FilterLocation.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\FilterMedia' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/FilterMedia.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\FilterMessage' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/FilterMessage.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\FilterNoMedia' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/FilterNoMedia.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\FilterNotEdited' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/FilterNotEdited.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\FilterOutgoing' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/FilterOutgoing.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\FilterPeer' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/FilterPeer.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\FilterPoll' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/FilterPoll.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\FilterPrivate' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/FilterPrivate.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\FilterRegex' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/FilterRegex.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\FilterRegexMatchAll' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/FilterRegexMatchAll.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\FilterReply' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/FilterReply.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\FilterReplyToSelf' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/FilterReplyToSelf.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\FilterRunning' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/FilterRunning.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\FilterSecret' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/FilterSecret.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\FilterSender' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/FilterSender.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\FilterSenders' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/FilterSenders.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\FilterService' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/FilterService.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\FilterText' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/FilterText.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\FilterTextCaseInsensitive' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/FilterTextCaseInsensitive.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\FilterTextContains' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/FilterTextContains.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\FilterTextContainsCaseInsensitive' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/FilterTextContainsCaseInsensitive.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\FilterTextEnds' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/FilterTextEnds.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\FilterTextEndsCaseInsensitive' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/FilterTextEndsCaseInsensitive.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\FilterTextStarts' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/FilterTextStarts.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\FilterTextStartsCaseInsensitive' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/FilterTextStartsCaseInsensitive.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\FilterTopic' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/FilterTopic.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\FilterTopicId' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/FilterTopicId.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\Location\\FilterLiveLocation' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/Location/FilterLiveLocation.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\Location\\FilterVenue' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/Location/FilterVenue.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\Media\\FilterAudio' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/Media/FilterAudio.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\Media\\FilterDocument' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/Media/FilterDocument.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\Media\\FilterDocumentPhoto' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/Media/FilterDocumentPhoto.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\Media\\FilterGif' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/Media/FilterGif.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\Media\\FilterPhoto' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/Media/FilterPhoto.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\Media\\FilterRoundVideo' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/Media/FilterRoundVideo.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\Media\\FilterSticker' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/Media/FilterSticker.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\Media\\FilterVideo' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/Media/FilterVideo.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\Media\\FilterVoice' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/Media/FilterVoice.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\Poll\\FilterMultiplePoll' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/Poll/FilterMultiplePoll.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\Poll\\FilterQuizPoll' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/Poll/FilterQuizPoll.php',
    'danog\\MadelineProto\\EventHandler\\Filter\\Poll\\FilterSinglePoll' => $vendorDir . '/danog/madelineproto/src/EventHandler/Filter/Poll/FilterSinglePoll.php',
    'danog\\MadelineProto\\EventHandler\\ForwardedInfo' => $vendorDir . '/danog/madelineproto/src/EventHandler/ForwardedInfo.php',
    'danog\\MadelineProto\\EventHandler\\GroupOrChannelMessage' => $vendorDir . '/danog/madelineproto/src/EventHandler/GroupOrChannelMessage.php',
    'danog\\MadelineProto\\EventHandler\\InlineQuery' => $vendorDir . '/danog/madelineproto/src/EventHandler/InlineQuery.php',
    'danog\\MadelineProto\\EventHandler\\InlineQueryPeerType' => $vendorDir . '/danog/madelineproto/src/EventHandler/InlineQueryPeerType.php',
    'danog\\MadelineProto\\EventHandler\\Keyboard' => $vendorDir . '/danog/madelineproto/src/EventHandler/Keyboard.php',
    'danog\\MadelineProto\\EventHandler\\Keyboard\\InlineKeyboard' => $vendorDir . '/danog/madelineproto/src/EventHandler/Keyboard/InlineKeyboard.php',
    'danog\\MadelineProto\\EventHandler\\Keyboard\\ReplyKeyboard' => $vendorDir . '/danog/madelineproto/src/EventHandler/Keyboard/ReplyKeyboard.php',
    'danog\\MadelineProto\\EventHandler\\Location\\LiveLocation' => $vendorDir . '/danog/madelineproto/src/EventHandler/Location/LiveLocation.php',
    'danog\\MadelineProto\\EventHandler\\Location\\StaticLocation' => $vendorDir . '/danog/madelineproto/src/EventHandler/Location/StaticLocation.php',
    'danog\\MadelineProto\\EventHandler\\Location\\Venue' => $vendorDir . '/danog/madelineproto/src/EventHandler/Location/Venue.php',
    'danog\\MadelineProto\\EventHandler\\Media' => $vendorDir . '/danog/madelineproto/src/EventHandler/Media.php',
    'danog\\MadelineProto\\EventHandler\\Media\\AbstractAudio' => $vendorDir . '/danog/madelineproto/src/EventHandler/Media/AbstractAudio.php',
    'danog\\MadelineProto\\EventHandler\\Media\\AbstractSticker' => $vendorDir . '/danog/madelineproto/src/EventHandler/Media/AbstractSticker.php',
    'danog\\MadelineProto\\EventHandler\\Media\\AbstractThumbnail' => $vendorDir . '/danog/madelineproto/src/EventHandler/Media/AbstractThumbnail.php',
    'danog\\MadelineProto\\EventHandler\\Media\\AbstractVideo' => $vendorDir . '/danog/madelineproto/src/EventHandler/Media/AbstractVideo.php',
    'danog\\MadelineProto\\EventHandler\\Media\\AnimatedSticker' => $vendorDir . '/danog/madelineproto/src/EventHandler/Media/AnimatedSticker.php',
    'danog\\MadelineProto\\EventHandler\\Media\\Audio' => $vendorDir . '/danog/madelineproto/src/EventHandler/Media/Audio.php',
    'danog\\MadelineProto\\EventHandler\\Media\\CustomEmoji' => $vendorDir . '/danog/madelineproto/src/EventHandler/Media/CustomEmoji.php',
    'danog\\MadelineProto\\EventHandler\\Media\\Document' => $vendorDir . '/danog/madelineproto/src/EventHandler/Media/Document.php',
    'danog\\MadelineProto\\EventHandler\\Media\\DocumentPhoto' => $vendorDir . '/danog/madelineproto/src/EventHandler/Media/DocumentPhoto.php',
    'danog\\MadelineProto\\EventHandler\\Media\\GeoPoint' => $vendorDir . '/danog/madelineproto/src/EventHandler/Media/GeoPoint.php',
    'danog\\MadelineProto\\EventHandler\\Media\\Gif' => $vendorDir . '/danog/madelineproto/src/EventHandler/Media/Gif.php',
    'danog\\MadelineProto\\EventHandler\\Media\\InlineThumbnail' => $vendorDir . '/danog/madelineproto/src/EventHandler/Media/InlineThumbnail.php',
    'danog\\MadelineProto\\EventHandler\\Media\\MaskPosition' => $vendorDir . '/danog/madelineproto/src/EventHandler/Media/MaskPosition.php',
    'danog\\MadelineProto\\EventHandler\\Media\\MaskSticker' => $vendorDir . '/danog/madelineproto/src/EventHandler/Media/MaskSticker.php',
    'danog\\MadelineProto\\EventHandler\\Media\\MediaStory' => $vendorDir . '/danog/madelineproto/src/EventHandler/Media/MediaStory.php',
    'danog\\MadelineProto\\EventHandler\\Media\\Photo' => $vendorDir . '/danog/madelineproto/src/EventHandler/Media/Photo.php',
    'danog\\MadelineProto\\EventHandler\\Media\\RoundVideo' => $vendorDir . '/danog/madelineproto/src/EventHandler/Media/RoundVideo.php',
    'danog\\MadelineProto\\EventHandler\\Media\\StaticSticker' => $vendorDir . '/danog/madelineproto/src/EventHandler/Media/StaticSticker.php',
    'danog\\MadelineProto\\EventHandler\\Media\\Sticker' => $vendorDir . '/danog/madelineproto/src/EventHandler/Media/Sticker.php',
    'danog\\MadelineProto\\EventHandler\\Media\\Thumbnail' => $vendorDir . '/danog/madelineproto/src/EventHandler/Media/Thumbnail.php',
    'danog\\MadelineProto\\EventHandler\\Media\\Video' => $vendorDir . '/danog/madelineproto/src/EventHandler/Media/Video.php',
    'danog\\MadelineProto\\EventHandler\\Media\\VideoSticker' => $vendorDir . '/danog/madelineproto/src/EventHandler/Media/VideoSticker.php',
    'danog\\MadelineProto\\EventHandler\\Media\\VideoThumbnail' => $vendorDir . '/danog/madelineproto/src/EventHandler/Media/VideoThumbnail.php',
    'danog\\MadelineProto\\EventHandler\\Media\\Voice' => $vendorDir . '/danog/madelineproto/src/EventHandler/Media/Voice.php',
    'danog\\MadelineProto\\EventHandler\\Message' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message.php',
    'danog\\MadelineProto\\EventHandler\\Message\\ChannelMessage' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/ChannelMessage.php',
    'danog\\MadelineProto\\EventHandler\\Message\\CommentReply' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/CommentReply.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Entities\\BankCard' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Entities/BankCard.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Entities\\Blockquote' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Entities/Blockquote.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Entities\\Bold' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Entities/Bold.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Entities\\BotCommand' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Entities/BotCommand.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Entities\\Cashtag' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Entities/Cashtag.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Entities\\Code' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Entities/Code.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Entities\\CustomEmoji' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Entities/CustomEmoji.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Entities\\Email' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Entities/Email.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Entities\\FormattedDate' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Entities/FormattedDate.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Entities\\Hashtag' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Entities/Hashtag.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Entities\\InputMentionName' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Entities/InputMentionName.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Entities\\Italic' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Entities/Italic.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Entities\\Mention' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Entities/Mention.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Entities\\MentionName' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Entities/MentionName.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Entities\\MessageEntity' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Entities/MessageEntity.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Entities\\Phone' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Entities/Phone.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Entities\\Pre' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Entities/Pre.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Entities\\Spoiler' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Entities/Spoiler.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Entities\\Strike' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Entities/Strike.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Entities\\TextUrl' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Entities/TextUrl.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Entities\\TextWithEntities' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Entities/TextWithEntities.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Entities\\Underline' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Entities/Underline.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Entities\\Unknown' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Entities/Unknown.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Entities\\Url' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Entities/Url.php',
    'danog\\MadelineProto\\EventHandler\\Message\\GroupMessage' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/GroupMessage.php',
    'danog\\MadelineProto\\EventHandler\\Message\\PrivateMessage' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/PrivateMessage.php',
    'danog\\MadelineProto\\EventHandler\\Message\\ReportReason' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/ReportReason.php',
    'danog\\MadelineProto\\EventHandler\\Message\\SecretMessage' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/SecretMessage.php',
    'danog\\MadelineProto\\EventHandler\\Message\\ServiceMessage' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/ServiceMessage.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogBotAllowed' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Service/DialogBotAllowed.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogChannelCreated' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Service/DialogChannelCreated.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogChannelMigrateFrom' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Service/DialogChannelMigrateFrom.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogChatJoinedByLink' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Service/DialogChatJoinedByLink.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogChatMigrateTo' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Service/DialogChatMigrateTo.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogConferenceCall' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Service/DialogConferenceCall.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogContactSignUp' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Service/DialogContactSignUp.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogCreated' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Service/DialogCreated.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogDeleteMessages' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Service/DialogDeleteMessages.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogGameScore' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Service/DialogGameScore.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogGeoProximityReached' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Service/DialogGeoProximityReached.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogGiftPremium' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Service/DialogGiftPremium.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogGiftStars' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Service/DialogGiftStars.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogGroupCall' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Service/DialogGroupCall.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogGroupCall\\GroupCall' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Service/DialogGroupCall/GroupCall.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogGroupCall\\GroupCallInvited' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Service/DialogGroupCall/GroupCallInvited.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogGroupCall\\GroupCallScheduled' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Service/DialogGroupCall/GroupCallScheduled.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogHistoryCleared' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Service/DialogHistoryCleared.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogMemberJoinedByRequest' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Service/DialogMemberJoinedByRequest.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogMemberLeft' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Service/DialogMemberLeft.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogMembersJoined' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Service/DialogMembersJoined.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogMessagePinned' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Service/DialogMessagePinned.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogNoForwardRequest' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Service/DialogNoForwardRequest.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogNoForwardToggle' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Service/DialogNoForwardToggle.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogPaymentSent' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Service/DialogPaymentSent.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogPaymentSentMe' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Service/DialogPaymentSentMe.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogPeerRequested' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Service/DialogPeerRequested.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogPhoneCall' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Service/DialogPhoneCall.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogPhotoChanged' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Service/DialogPhotoChanged.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogReadMessages' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Service/DialogReadMessages.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogScreenshotTaken' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Service/DialogScreenshotTaken.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogSetChatTheme' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Service/DialogSetChatTheme.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogSetChatWallPaper' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Service/DialogSetChatWallPaper.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogSetTTL' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Service/DialogSetTTL.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogStarGift' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Service/DialogStarGift.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogSuggestProfilePhoto' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Service/DialogSuggestProfilePhoto.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogTitleChanged' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Service/DialogTitleChanged.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogTopicCreated' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Service/DialogTopicCreated.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogTopicEdited' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Service/DialogTopicEdited.php',
    'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogWebView' => $vendorDir . '/danog/madelineproto/src/EventHandler/Message/Service/DialogWebView.php',
    'danog\\MadelineProto\\EventHandler\\MultiCall' => $vendorDir . '/danog/madelineproto/src/EventHandler/MultiCall.php',
    'danog\\MadelineProto\\EventHandler\\Participant' => $vendorDir . '/danog/madelineproto/src/EventHandler/Participant.php',
    'danog\\MadelineProto\\EventHandler\\Participant\\Admin' => $vendorDir . '/danog/madelineproto/src/EventHandler/Participant/Admin.php',
    'danog\\MadelineProto\\EventHandler\\Participant\\Banned' => $vendorDir . '/danog/madelineproto/src/EventHandler/Participant/Banned.php',
    'danog\\MadelineProto\\EventHandler\\Participant\\Creator' => $vendorDir . '/danog/madelineproto/src/EventHandler/Participant/Creator.php',
    'danog\\MadelineProto\\EventHandler\\Participant\\Left' => $vendorDir . '/danog/madelineproto/src/EventHandler/Participant/Left.php',
    'danog\\MadelineProto\\EventHandler\\Participant\\Member' => $vendorDir . '/danog/madelineproto/src/EventHandler/Participant/Member.php',
    'danog\\MadelineProto\\EventHandler\\Participant\\MySelf' => $vendorDir . '/danog/madelineproto/src/EventHandler/Participant/MySelf.php',
    'danog\\MadelineProto\\EventHandler\\Participant\\Rights' => $vendorDir . '/danog/madelineproto/src/EventHandler/Participant/Rights.php',
    'danog\\MadelineProto\\EventHandler\\Participant\\Rights\\Admin' => $vendorDir . '/danog/madelineproto/src/EventHandler/Participant/Rights/Admin.php',
    'danog\\MadelineProto\\EventHandler\\Participant\\Rights\\Banned' => $vendorDir . '/danog/madelineproto/src/EventHandler/Participant/Rights/Banned.php',
    'danog\\MadelineProto\\EventHandler\\Payments\\Payment' => $vendorDir . '/danog/madelineproto/src/EventHandler/Payments/Payment.php',
    'danog\\MadelineProto\\EventHandler\\Payments\\PaymentCharge' => $vendorDir . '/danog/madelineproto/src/EventHandler/Payments/PaymentCharge.php',
    'danog\\MadelineProto\\EventHandler\\Payments\\PaymentRequestedInfo' => $vendorDir . '/danog/madelineproto/src/EventHandler/Payments/PaymentRequestedInfo.php',
    'danog\\MadelineProto\\EventHandler\\Payments\\PostAddress' => $vendorDir . '/danog/madelineproto/src/EventHandler/Payments/PostAddress.php',
    'danog\\MadelineProto\\EventHandler\\Payments\\StarGift' => $vendorDir . '/danog/madelineproto/src/EventHandler/Payments/StarGift.php',
    'danog\\MadelineProto\\EventHandler\\Pinned' => $vendorDir . '/danog/madelineproto/src/EventHandler/Pinned.php',
    'danog\\MadelineProto\\EventHandler\\Pinned\\PinnedChannelMessages' => $vendorDir . '/danog/madelineproto/src/EventHandler/Pinned/PinnedChannelMessages.php',
    'danog\\MadelineProto\\EventHandler\\Pinned\\PinnedGroupMessages' => $vendorDir . '/danog/madelineproto/src/EventHandler/Pinned/PinnedGroupMessages.php',
    'danog\\MadelineProto\\EventHandler\\Pinned\\PinnedPrivateMessages' => $vendorDir . '/danog/madelineproto/src/EventHandler/Pinned/PinnedPrivateMessages.php',
    'danog\\MadelineProto\\EventHandler\\Plugin\\RestartPlugin' => $vendorDir . '/danog/madelineproto/src/EventHandler/Plugin/RestartPlugin.php',
    'danog\\MadelineProto\\EventHandler\\Poll\\MultiplePoll' => $vendorDir . '/danog/madelineproto/src/EventHandler/Poll/MultiplePoll.php',
    'danog\\MadelineProto\\EventHandler\\Poll\\PollAnswer' => $vendorDir . '/danog/madelineproto/src/EventHandler/Poll/PollAnswer.php',
    'danog\\MadelineProto\\EventHandler\\Poll\\QuizPoll' => $vendorDir . '/danog/madelineproto/src/EventHandler/Poll/QuizPoll.php',
    'danog\\MadelineProto\\EventHandler\\Poll\\SinglePoll' => $vendorDir . '/danog/madelineproto/src/EventHandler/Poll/SinglePoll.php',
    'danog\\MadelineProto\\EventHandler\\Privacy' => $vendorDir . '/danog/madelineproto/src/EventHandler/Privacy.php',
    'danog\\MadelineProto\\EventHandler\\Privacy\\Rule' => $vendorDir . '/danog/madelineproto/src/EventHandler/Privacy/Rule.php',
    'danog\\MadelineProto\\EventHandler\\Privacy\\RuleDestination' => $vendorDir . '/danog/madelineproto/src/EventHandler/Privacy/RuleDestination.php',
    'danog\\MadelineProto\\EventHandler\\Privacy\\RuleDestination\\AllowAll' => $vendorDir . '/danog/madelineproto/src/EventHandler/Privacy/RuleDestination/AllowAll.php',
    'danog\\MadelineProto\\EventHandler\\Privacy\\RuleDestination\\AllowChatParticipants' => $vendorDir . '/danog/madelineproto/src/EventHandler/Privacy/RuleDestination/AllowChatParticipants.php',
    'danog\\MadelineProto\\EventHandler\\Privacy\\RuleDestination\\AllowCloseFriends' => $vendorDir . '/danog/madelineproto/src/EventHandler/Privacy/RuleDestination/AllowCloseFriends.php',
    'danog\\MadelineProto\\EventHandler\\Privacy\\RuleDestination\\AllowContacts' => $vendorDir . '/danog/madelineproto/src/EventHandler/Privacy/RuleDestination/AllowContacts.php',
    'danog\\MadelineProto\\EventHandler\\Privacy\\RuleDestination\\AllowUsers' => $vendorDir . '/danog/madelineproto/src/EventHandler/Privacy/RuleDestination/AllowUsers.php',
    'danog\\MadelineProto\\EventHandler\\Privacy\\RuleDestination\\DisallowAll' => $vendorDir . '/danog/madelineproto/src/EventHandler/Privacy/RuleDestination/DisallowAll.php',
    'danog\\MadelineProto\\EventHandler\\Privacy\\RuleDestination\\DisallowChatParticipants' => $vendorDir . '/danog/madelineproto/src/EventHandler/Privacy/RuleDestination/DisallowChatParticipants.php',
    'danog\\MadelineProto\\EventHandler\\Privacy\\RuleDestination\\DisallowContacts' => $vendorDir . '/danog/madelineproto/src/EventHandler/Privacy/RuleDestination/DisallowContacts.php',
    'danog\\MadelineProto\\EventHandler\\Privacy\\RuleDestination\\DisallowUsers' => $vendorDir . '/danog/madelineproto/src/EventHandler/Privacy/RuleDestination/DisallowUsers.php',
    'danog\\MadelineProto\\EventHandler\\Query\\ButtonQuery' => $vendorDir . '/danog/madelineproto/src/EventHandler/Query/ButtonQuery.php',
    'danog\\MadelineProto\\EventHandler\\Query\\ChatButtonQuery' => $vendorDir . '/danog/madelineproto/src/EventHandler/Query/ChatButtonQuery.php',
    'danog\\MadelineProto\\EventHandler\\Query\\ChatGameQuery' => $vendorDir . '/danog/madelineproto/src/EventHandler/Query/ChatGameQuery.php',
    'danog\\MadelineProto\\EventHandler\\Query\\ChatTrait' => $vendorDir . '/danog/madelineproto/src/EventHandler/Query/ChatTrait.php',
    'danog\\MadelineProto\\EventHandler\\Query\\GameQuery' => $vendorDir . '/danog/madelineproto/src/EventHandler/Query/GameQuery.php',
    'danog\\MadelineProto\\EventHandler\\Query\\InlineButtonQuery' => $vendorDir . '/danog/madelineproto/src/EventHandler/Query/InlineButtonQuery.php',
    'danog\\MadelineProto\\EventHandler\\Query\\InlineGameQuery' => $vendorDir . '/danog/madelineproto/src/EventHandler/Query/InlineGameQuery.php',
    'danog\\MadelineProto\\EventHandler\\Query\\InlineTrait' => $vendorDir . '/danog/madelineproto/src/EventHandler/Query/InlineTrait.php',
    'danog\\MadelineProto\\EventHandler\\SimpleFilter\\Ended' => $vendorDir . '/danog/madelineproto/src/EventHandler/SimpleFilter/Ended.php',
    'danog\\MadelineProto\\EventHandler\\SimpleFilter\\FromAdmin' => $vendorDir . '/danog/madelineproto/src/EventHandler/SimpleFilter/FromAdmin.php',
    'danog\\MadelineProto\\EventHandler\\SimpleFilter\\FromAdminOrOutgoing' => $vendorDir . '/danog/madelineproto/src/EventHandler/SimpleFilter/FromAdminOrOutgoing.php',
    'danog\\MadelineProto\\EventHandler\\SimpleFilter\\HasAudio' => $vendorDir . '/danog/madelineproto/src/EventHandler/SimpleFilter/HasAudio.php',
    'danog\\MadelineProto\\EventHandler\\SimpleFilter\\HasDocument' => $vendorDir . '/danog/madelineproto/src/EventHandler/SimpleFilter/HasDocument.php',
    'danog\\MadelineProto\\EventHandler\\SimpleFilter\\HasDocumentPhoto' => $vendorDir . '/danog/madelineproto/src/EventHandler/SimpleFilter/HasDocumentPhoto.php',
    'danog\\MadelineProto\\EventHandler\\SimpleFilter\\HasGif' => $vendorDir . '/danog/madelineproto/src/EventHandler/SimpleFilter/HasGif.php',
    'danog\\MadelineProto\\EventHandler\\SimpleFilter\\HasLiveLocation' => $vendorDir . '/danog/madelineproto/src/EventHandler/SimpleFilter/HasLiveLocation.php',
    'danog\\MadelineProto\\EventHandler\\SimpleFilter\\HasLocation' => $vendorDir . '/danog/madelineproto/src/EventHandler/SimpleFilter/HasLocation.php',
    'danog\\MadelineProto\\EventHandler\\SimpleFilter\\HasMedia' => $vendorDir . '/danog/madelineproto/src/EventHandler/SimpleFilter/HasMedia.php',
    'danog\\MadelineProto\\EventHandler\\SimpleFilter\\HasMultiplePoll' => $vendorDir . '/danog/madelineproto/src/EventHandler/SimpleFilter/HasMultiplePoll.php',
    'danog\\MadelineProto\\EventHandler\\SimpleFilter\\HasNoMedia' => $vendorDir . '/danog/madelineproto/src/EventHandler/SimpleFilter/HasNoMedia.php',
    'danog\\MadelineProto\\EventHandler\\SimpleFilter\\HasPhoto' => $vendorDir . '/danog/madelineproto/src/EventHandler/SimpleFilter/HasPhoto.php',
    'danog\\MadelineProto\\EventHandler\\SimpleFilter\\HasPoll' => $vendorDir . '/danog/madelineproto/src/EventHandler/SimpleFilter/HasPoll.php',
    'danog\\MadelineProto\\EventHandler\\SimpleFilter\\HasQuizPoll' => $vendorDir . '/danog/madelineproto/src/EventHandler/SimpleFilter/HasQuizPoll.php',
    'danog\\MadelineProto\\EventHandler\\SimpleFilter\\HasRoundVideo' => $vendorDir . '/danog/madelineproto/src/EventHandler/SimpleFilter/HasRoundVideo.php',
    'danog\\MadelineProto\\EventHandler\\SimpleFilter\\HasSinglePoll' => $vendorDir . '/danog/madelineproto/src/EventHandler/SimpleFilter/HasSinglePoll.php',
    'danog\\MadelineProto\\EventHandler\\SimpleFilter\\HasSticker' => $vendorDir . '/danog/madelineproto/src/EventHandler/SimpleFilter/HasSticker.php',
    'danog\\MadelineProto\\EventHandler\\SimpleFilter\\HasTopic' => $vendorDir . '/danog/madelineproto/src/EventHandler/SimpleFilter/HasTopic.php',
    'danog\\MadelineProto\\EventHandler\\SimpleFilter\\HasVenue' => $vendorDir . '/danog/madelineproto/src/EventHandler/SimpleFilter/HasVenue.php',
    'danog\\MadelineProto\\EventHandler\\SimpleFilter\\HasVideo' => $vendorDir . '/danog/madelineproto/src/EventHandler/SimpleFilter/HasVideo.php',
    'danog\\MadelineProto\\EventHandler\\SimpleFilter\\HasVoice' => $vendorDir . '/danog/madelineproto/src/EventHandler/SimpleFilter/HasVoice.php',
    'danog\\MadelineProto\\EventHandler\\SimpleFilter\\Incoming' => $vendorDir . '/danog/madelineproto/src/EventHandler/SimpleFilter/Incoming.php',
    'danog\\MadelineProto\\EventHandler\\SimpleFilter\\IsEdited' => $vendorDir . '/danog/madelineproto/src/EventHandler/SimpleFilter/IsEdited.php',
    'danog\\MadelineProto\\EventHandler\\SimpleFilter\\IsForwarded' => $vendorDir . '/danog/madelineproto/src/EventHandler/SimpleFilter/IsForwarded.php',
    'danog\\MadelineProto\\EventHandler\\SimpleFilter\\IsNotEdited' => $vendorDir . '/danog/madelineproto/src/EventHandler/SimpleFilter/IsNotEdited.php',
    'danog\\MadelineProto\\EventHandler\\SimpleFilter\\IsReply' => $vendorDir . '/danog/madelineproto/src/EventHandler/SimpleFilter/IsReply.php',
    'danog\\MadelineProto\\EventHandler\\SimpleFilter\\IsReplyToSelf' => $vendorDir . '/danog/madelineproto/src/EventHandler/SimpleFilter/IsReplyToSelf.php',
    'danog\\MadelineProto\\EventHandler\\SimpleFilter\\Outgoing' => $vendorDir . '/danog/madelineproto/src/EventHandler/SimpleFilter/Outgoing.php',
    'danog\\MadelineProto\\EventHandler\\SimpleFilter\\Running' => $vendorDir . '/danog/madelineproto/src/EventHandler/SimpleFilter/Running.php',
    'danog\\MadelineProto\\EventHandler\\SimpleFilters' => $vendorDir . '/danog/madelineproto/src/EventHandler/SimpleFilters.php',
    'danog\\MadelineProto\\EventHandler\\Story\\Story' => $vendorDir . '/danog/madelineproto/src/EventHandler/Story/Story.php',
    'danog\\MadelineProto\\EventHandler\\Story\\StoryDeleted' => $vendorDir . '/danog/madelineproto/src/EventHandler/Story/StoryDeleted.php',
    'danog\\MadelineProto\\EventHandler\\Story\\StoryReaction' => $vendorDir . '/danog/madelineproto/src/EventHandler/Story/StoryReaction.php',
    'danog\\MadelineProto\\EventHandler\\Topic\\IconColor' => $vendorDir . '/danog/madelineproto/src/EventHandler/Topic/IconColor.php',
    'danog\\MadelineProto\\EventHandler\\Typing' => $vendorDir . '/danog/madelineproto/src/EventHandler/Typing.php',
    'danog\\MadelineProto\\EventHandler\\Typing\\ChatUserTyping' => $vendorDir . '/danog/madelineproto/src/EventHandler/Typing/ChatUserTyping.php',
    'danog\\MadelineProto\\EventHandler\\Typing\\SecretUserTyping' => $vendorDir . '/danog/madelineproto/src/EventHandler/Typing/SecretUserTyping.php',
    'danog\\MadelineProto\\EventHandler\\Typing\\SupergroupUserTyping' => $vendorDir . '/danog/madelineproto/src/EventHandler/Typing/SupergroupUserTyping.php',
    'danog\\MadelineProto\\EventHandler\\Typing\\UserTyping' => $vendorDir . '/danog/madelineproto/src/EventHandler/Typing/UserTyping.php',
    'danog\\MadelineProto\\EventHandler\\Update' => $vendorDir . '/danog/madelineproto/src/EventHandler/Update.php',
    'danog\\MadelineProto\\EventHandler\\User\\Blocked' => $vendorDir . '/danog/madelineproto/src/EventHandler/User/Blocked.php',
    'danog\\MadelineProto\\EventHandler\\User\\BotStopped' => $vendorDir . '/danog/madelineproto/src/EventHandler/User/BotStopped.php',
    'danog\\MadelineProto\\EventHandler\\User\\Phone' => $vendorDir . '/danog/madelineproto/src/EventHandler/User/Phone.php',
    'danog\\MadelineProto\\EventHandler\\User\\Status' => $vendorDir . '/danog/madelineproto/src/EventHandler/User/Status.php',
    'danog\\MadelineProto\\EventHandler\\User\\Status\\Emoji' => $vendorDir . '/danog/madelineproto/src/EventHandler/User/Status/Emoji.php',
    'danog\\MadelineProto\\EventHandler\\User\\Status\\EmptyStatus' => $vendorDir . '/danog/madelineproto/src/EventHandler/User/Status/EmptyStatus.php',
    'danog\\MadelineProto\\EventHandler\\User\\Status\\LastMonth' => $vendorDir . '/danog/madelineproto/src/EventHandler/User/Status/LastMonth.php',
    'danog\\MadelineProto\\EventHandler\\User\\Status\\LastWeek' => $vendorDir . '/danog/madelineproto/src/EventHandler/User/Status/LastWeek.php',
    'danog\\MadelineProto\\EventHandler\\User\\Status\\Offline' => $vendorDir . '/danog/madelineproto/src/EventHandler/User/Status/Offline.php',
    'danog\\MadelineProto\\EventHandler\\User\\Status\\Online' => $vendorDir . '/danog/madelineproto/src/EventHandler/User/Status/Online.php',
    'danog\\MadelineProto\\EventHandler\\User\\Status\\Recently' => $vendorDir . '/danog/madelineproto/src/EventHandler/User/Status/Recently.php',
    'danog\\MadelineProto\\EventHandler\\User\\Username' => $vendorDir . '/danog/madelineproto/src/EventHandler/User/Username.php',
    'danog\\MadelineProto\\EventHandler\\User\\UsernameInfo' => $vendorDir . '/danog/madelineproto/src/EventHandler/User/UsernameInfo.php',
    'danog\\MadelineProto\\EventHandler\\Wallpaper' => $vendorDir . '/danog/madelineproto/src/EventHandler/Wallpaper.php',
    'danog\\MadelineProto\\EventHandler\\Wallpaper\\WallpaperSettings' => $vendorDir . '/danog/madelineproto/src/EventHandler/Wallpaper/WallpaperSettings.php',
    'danog\\MadelineProto\\Exception' => $vendorDir . '/danog/madelineproto/src/Exception.php',
    'danog\\MadelineProto\\FileCallback' => $vendorDir . '/danog/madelineproto/src/FileCallback.php',
    'danog\\MadelineProto\\FileCallbackInterface' => $vendorDir . '/danog/madelineproto/src/FileCallbackInterface.php',
    'danog\\MadelineProto\\FileRedirect' => $vendorDir . '/danog/madelineproto/src/FileRedirect.php',
    'danog\\MadelineProto\\GarbageCollector' => $vendorDir . '/danog/madelineproto/src/GarbageCollector.php',
    'danog\\MadelineProto\\GroupCall\\GroupCallController' => $vendorDir . '/danog/madelineproto/src/GroupCall/GroupCallController.php',
    'danog\\MadelineProto\\GroupCall\\Handler' => $vendorDir . '/danog/madelineproto/src/GroupCall/Handler.php',
    'danog\\MadelineProto\\GroupCall\\OggDemuxer' => $vendorDir . '/danog/madelineproto/src/GroupCall/OggDemuxer.php',
    'danog\\MadelineProto\\GroupCall\\Rtmp\\Amf0' => $vendorDir . '/danog/madelineproto/src/GroupCall/Rtmp/Amf0.php',
    'danog\\MadelineProto\\GroupCall\\Rtmp\\Amf0EcmaArray' => $vendorDir . '/danog/madelineproto/src/GroupCall/Rtmp/Amf0EcmaArray.php',
    'danog\\MadelineProto\\GroupCall\\Rtmp\\FlvTags' => $vendorDir . '/danog/madelineproto/src/GroupCall/Rtmp/FlvTags.php',
    'danog\\MadelineProto\\GroupCall\\Rtmp\\RtmpClient' => $vendorDir . '/danog/madelineproto/src/GroupCall/Rtmp/RtmpClient.php',
    'danog\\MadelineProto\\GroupCall\\Rtmp\\RtmpPublisher' => $vendorDir . '/danog/madelineproto/src/GroupCall/Rtmp/RtmpPublisher.php',
    'danog\\MadelineProto\\GroupCall\\Rtmp\\RtmpRefusedException' => $vendorDir . '/danog/madelineproto/src/GroupCall/Rtmp/RtmpRefusedException.php',
    'danog\\MadelineProto\\GroupCall\\StreamReceiver' => $vendorDir . '/danog/madelineproto/src/GroupCall/StreamReceiver.php',
    'danog\\MadelineProto\\InternalDoc' => $vendorDir . '/danog/madelineproto/src/InternalDoc.php',
    'danog\\MadelineProto\\Ipc\\AbstractServer' => $vendorDir . '/danog/madelineproto/src/Ipc/AbstractServer.php',
    'danog\\MadelineProto\\Ipc\\AlreadyServedException' => $vendorDir . '/danog/madelineproto/src/Ipc/AlreadyServedException.php',
    'danog\\MadelineProto\\Ipc\\Client' => $vendorDir . '/danog/madelineproto/src/Ipc/Client.php',
    'danog\\MadelineProto\\Ipc\\ClientAbstract' => $vendorDir . '/danog/madelineproto/src/Ipc/ClientAbstract.php',
    'danog\\MadelineProto\\Ipc\\EventHandlerProxy' => $vendorDir . '/danog/madelineproto/src/Ipc/EventHandlerProxy.php',
    'danog\\MadelineProto\\Ipc\\ExitFailure' => $vendorDir . '/danog/madelineproto/src/Ipc/ExitFailure.php',
    'danog\\MadelineProto\\Ipc\\IpcCapable' => $vendorDir . '/danog/madelineproto/src/Ipc/IpcCapable.php',
    'danog\\MadelineProto\\Ipc\\IpcState' => $vendorDir . '/danog/madelineproto/src/Ipc/IpcState.php',
    'danog\\MadelineProto\\Ipc\\Runner\\ProcessRunner' => $vendorDir . '/danog/madelineproto/src/Ipc/Runner/ProcessRunner.php',
    'danog\\MadelineProto\\Ipc\\Runner\\RunnerAbstract' => $vendorDir . '/danog/madelineproto/src/Ipc/Runner/RunnerAbstract.php',
    'danog\\MadelineProto\\Ipc\\Runner\\WebRunner' => $vendorDir . '/danog/madelineproto/src/Ipc/Runner/WebRunner.php',
    'danog\\MadelineProto\\Ipc\\Server' => $vendorDir . '/danog/madelineproto/src/Ipc/Server.php',
    'danog\\MadelineProto\\Ipc\\ServerCallback' => $vendorDir . '/danog/madelineproto/src/Ipc/ServerCallback.php',
    'danog\\MadelineProto\\Ipc\\Wrapper' => $vendorDir . '/danog/madelineproto/src/Ipc/Wrapper.php',
    'danog\\MadelineProto\\Ipc\\Wrapper\\Cancellation' => $vendorDir . '/danog/madelineproto/src/Ipc/Wrapper/Cancellation.php',
    'danog\\MadelineProto\\Ipc\\Wrapper\\CancellationInner' => $vendorDir . '/danog/madelineproto/src/Ipc/Wrapper/CancellationInner.php',
    'danog\\MadelineProto\\Ipc\\Wrapper\\CancellationReference' => $vendorDir . '/danog/madelineproto/src/Ipc/Wrapper/CancellationReference.php',
    'danog\\MadelineProto\\Ipc\\Wrapper\\ClosableTrait' => $vendorDir . '/danog/madelineproto/src/Ipc/Wrapper/ClosableTrait.php',
    'danog\\MadelineProto\\Ipc\\Wrapper\\FileCallback' => $vendorDir . '/danog/madelineproto/src/Ipc/Wrapper/FileCallback.php',
    'danog\\MadelineProto\\Ipc\\Wrapper\\Obj' => $vendorDir . '/danog/madelineproto/src/Ipc/Wrapper/Obj.php',
    'danog\\MadelineProto\\Ipc\\Wrapper\\ReadableStream' => $vendorDir . '/danog/madelineproto/src/Ipc/Wrapper/ReadableStream.php',
    'danog\\MadelineProto\\Ipc\\Wrapper\\SeekableReadableStream' => $vendorDir . '/danog/madelineproto/src/Ipc/Wrapper/SeekableReadableStream.php',
    'danog\\MadelineProto\\Ipc\\Wrapper\\SeekableTrait' => $vendorDir . '/danog/madelineproto/src/Ipc/Wrapper/SeekableTrait.php',
    'danog\\MadelineProto\\Ipc\\Wrapper\\SeekableWritableStream' => $vendorDir . '/danog/madelineproto/src/Ipc/Wrapper/SeekableWritableStream.php',
    'danog\\MadelineProto\\Ipc\\Wrapper\\WrapMethodTrait' => $vendorDir . '/danog/madelineproto/src/Ipc/Wrapper/WrapMethodTrait.php',
    'danog\\MadelineProto\\Ipc\\Wrapper\\WrappedCancellation' => $vendorDir . '/danog/madelineproto/src/Ipc/Wrapper/WrappedCancellation.php',
    'danog\\MadelineProto\\Ipc\\Wrapper\\WritableStream' => $vendorDir . '/danog/madelineproto/src/Ipc/Wrapper/WritableStream.php',
    'danog\\MadelineProto\\Lang' => $vendorDir . '/danog/madelineproto/src/Lang.php',
    'danog\\MadelineProto\\LegacyMigrator' => $vendorDir . '/danog/madelineproto/src/LegacyMigrator.php',
    'danog\\MadelineProto\\LightState' => $vendorDir . '/danog/madelineproto/src/LightState.php',
    'danog\\MadelineProto\\LocalDirectory' => $vendorDir . '/danog/madelineproto/src/LocalDirectory.php',
    'danog\\MadelineProto\\LocalFile' => $vendorDir . '/danog/madelineproto/src/LocalFile.php',
    'danog\\MadelineProto\\Logger' => $vendorDir . '/danog/madelineproto/src/Logger.php',
    'danog\\MadelineProto\\LoggerGetter' => $vendorDir . '/danog/madelineproto/src/LoggerGetter.php',
    'danog\\MadelineProto\\Loop\\APILoop' => $vendorDir . '/danog/madelineproto/src/Loop/APILoop.php',
    'danog\\MadelineProto\\Loop\\Connection\\CleanupLoop' => $vendorDir . '/danog/madelineproto/src/Loop/Connection/CleanupLoop.php',
    'danog\\MadelineProto\\Loop\\Connection\\Common' => $vendorDir . '/danog/madelineproto/src/Loop/Connection/Common.php',
    'danog\\MadelineProto\\Loop\\Connection\\PingLoop' => $vendorDir . '/danog/madelineproto/src/Loop/Connection/PingLoop.php',
    'danog\\MadelineProto\\Loop\\Connection\\ReadLoop' => $vendorDir . '/danog/madelineproto/src/Loop/Connection/ReadLoop.php',
    'danog\\MadelineProto\\Loop\\Connection\\WriteLoop' => $vendorDir . '/danog/madelineproto/src/Loop/Connection/WriteLoop.php',
    'danog\\MadelineProto\\Loop\\Generic\\PeriodicLoopInternal' => $vendorDir . '/danog/madelineproto/src/Loop/Generic/PeriodicLoopInternal.php',
    'danog\\MadelineProto\\Loop\\InternalLoop' => $vendorDir . '/danog/madelineproto/src/Loop/InternalLoop.php',
    'danog\\MadelineProto\\Loop\\LoggerLoop' => $vendorDir . '/danog/madelineproto/src/Loop/LoggerLoop.php',
    'danog\\MadelineProto\\Loop\\Secret\\SecretFeedLoop' => $vendorDir . '/danog/madelineproto/src/Loop/Secret/SecretFeedLoop.php',
    'danog\\MadelineProto\\Loop\\Update\\FeedLoop' => $vendorDir . '/danog/madelineproto/src/Loop/Update/FeedLoop.php',
    'danog\\MadelineProto\\Loop\\Update\\SeqLoop' => $vendorDir . '/danog/madelineproto/src/Loop/Update/SeqLoop.php',
    'danog\\MadelineProto\\Loop\\Update\\UpdateLoop' => $vendorDir . '/danog/madelineproto/src/Loop/Update/UpdateLoop.php',
    'danog\\MadelineProto\\Loop\\VoIPLoop' => $vendorDir . '/danog/madelineproto/src/Loop/VoIPLoop.php',
    'danog\\MadelineProto\\Loop\\VoIP\\DjLoop' => $vendorDir . '/danog/madelineproto/src/Loop/VoIP/DjLoop.php',
    'danog\\MadelineProto\\Loop\\VoIP\\StreamSnapshot' => $vendorDir . '/danog/madelineproto/src/Loop/VoIP/StreamSnapshot.php',
    'danog\\MadelineProto\\MTProto' => $vendorDir . '/danog/madelineproto/src/MTProto.php',
    'danog\\MadelineProto\\MTProtoSession\\AuthKeyHandler' => $vendorDir . '/danog/madelineproto/src/MTProtoSession/AuthKeyHandler.php',
    'danog\\MadelineProto\\MTProtoSession\\CallHandler' => $vendorDir . '/danog/madelineproto/src/MTProtoSession/CallHandler.php',
    'danog\\MadelineProto\\MTProtoSession\\MsgIdHandler' => $vendorDir . '/danog/madelineproto/src/MTProtoSession/MsgIdHandler.php',
    'danog\\MadelineProto\\MTProtoSession\\Reliable' => $vendorDir . '/danog/madelineproto/src/MTProtoSession/Reliable.php',
    'danog\\MadelineProto\\MTProtoSession\\ResponseHandler' => $vendorDir . '/danog/madelineproto/src/MTProtoSession/ResponseHandler.php',
    'danog\\MadelineProto\\MTProtoSession\\SeqNoHandler' => $vendorDir . '/danog/madelineproto/src/MTProtoSession/SeqNoHandler.php',
    'danog\\MadelineProto\\MTProtoSession\\Session' => $vendorDir . '/danog/madelineproto/src/MTProtoSession/Session.php',
    'danog\\MadelineProto\\MTProtoTools\\AuthKeyHandler' => $vendorDir . '/danog/madelineproto/src/MTProtoTools/AuthKeyHandler.php',
    'danog\\MadelineProto\\MTProtoTools\\CallHandler' => $vendorDir . '/danog/madelineproto/src/MTProtoTools/CallHandler.php',
    'danog\\MadelineProto\\MTProtoTools\\CombinedUpdatesState' => $vendorDir . '/danog/madelineproto/src/MTProtoTools/CombinedUpdatesState.php',
    'danog\\MadelineProto\\MTProtoTools\\Crypt' => $vendorDir . '/danog/madelineproto/src/MTProtoTools/Crypt.php',
    'danog\\MadelineProto\\MTProtoTools\\Crypt\\AesCtr' => $vendorDir . '/danog/madelineproto/src/MTProtoTools/Crypt/AesCtr.php',
    'danog\\MadelineProto\\MTProtoTools\\Crypt\\IGE' => $vendorDir . '/danog/madelineproto/src/MTProtoTools/Crypt/IGE.php',
    'danog\\MadelineProto\\MTProtoTools\\Crypt\\IGEOpenssl' => $vendorDir . '/danog/madelineproto/src/MTProtoTools/Crypt/IGEOpenssl.php',
    'danog\\MadelineProto\\MTProtoTools\\Crypt\\IGEPhpseclib' => $vendorDir . '/danog/madelineproto/src/MTProtoTools/Crypt/IGEPhpseclib.php',
    'danog\\MadelineProto\\MTProtoTools\\FileRef\\FileRefEnvironment' => $vendorDir . '/danog/madelineproto/src/MTProtoTools/FileRef/FileRefEnvironment.php',
    'danog\\MadelineProto\\MTProtoTools\\FileRef\\FileRefMap' => $vendorDir . '/danog/madelineproto/src/MTProtoTools/FileRef/FileRefMap.php',
    'danog\\MadelineProto\\MTProtoTools\\FileRef\\FileRefMarker' => $vendorDir . '/danog/madelineproto/src/MTProtoTools/FileRef/FileRefMarker.php',
    'danog\\MadelineProto\\MTProtoTools\\FileRef\\FileRefMethodCall' => $vendorDir . '/danog/madelineproto/src/MTProtoTools/FileRef/FileRefMethodCall.php',
    'danog\\MadelineProto\\MTProtoTools\\FileRef\\FileRefTraversal' => $vendorDir . '/danog/madelineproto/src/MTProtoTools/FileRef/FileRefTraversal.php',
    'danog\\MadelineProto\\MTProtoTools\\FileServer' => $vendorDir . '/danog/madelineproto/src/MTProtoTools/FileServer.php',
    'danog\\MadelineProto\\MTProtoTools\\Files' => $vendorDir . '/danog/madelineproto/src/MTProtoTools/Files.php',
    'danog\\MadelineProto\\MTProtoTools\\FilesAbstraction' => $vendorDir . '/danog/madelineproto/src/MTProtoTools/FilesAbstraction.php',
    'danog\\MadelineProto\\MTProtoTools\\FilesLogic' => $vendorDir . '/danog/madelineproto/src/MTProtoTools/FilesLogic.php',
    'danog\\MadelineProto\\MTProtoTools\\MinDatabase' => $vendorDir . '/danog/madelineproto/src/MTProtoTools/MinDatabase.php',
    'danog\\MadelineProto\\MTProtoTools\\PasswordCalculator' => $vendorDir . '/danog/madelineproto/src/MTProtoTools/PasswordCalculator.php',
    'danog\\MadelineProto\\MTProtoTools\\PeerDatabase' => $vendorDir . '/danog/madelineproto/src/MTProtoTools/PeerDatabase.php',
    'danog\\MadelineProto\\MTProtoTools\\PeerHandler' => $vendorDir . '/danog/madelineproto/src/MTProtoTools/PeerHandler.php',
    'danog\\MadelineProto\\MTProtoTools\\ReferenceDatabase' => $vendorDir . '/danog/madelineproto/src/MTProtoTools/ReferenceDatabase.php',
    'danog\\MadelineProto\\MTProtoTools\\ResponseInfo' => $vendorDir . '/danog/madelineproto/src/MTProtoTools/ResponseInfo.php',
    'danog\\MadelineProto\\MTProtoTools\\ResumableCalls' => $vendorDir . '/danog/madelineproto/src/MTProtoTools/ResumableCalls.php',
    'danog\\MadelineProto\\MTProtoTools\\TransferProgress' => $vendorDir . '/danog/madelineproto/src/MTProtoTools/TransferProgress.php',
    'danog\\MadelineProto\\MTProtoTools\\UpdateHandler' => $vendorDir . '/danog/madelineproto/src/MTProtoTools/UpdateHandler.php',
    'danog\\MadelineProto\\MTProtoTools\\UpdatesState' => $vendorDir . '/danog/madelineproto/src/MTProtoTools/UpdatesState.php',
    'danog\\MadelineProto\\MTProto\\AuthKey' => $vendorDir . '/danog/madelineproto/src/MTProto/AuthKey.php',
    'danog\\MadelineProto\\MTProto\\ConnectionState' => $vendorDir . '/danog/madelineproto/src/MTProto/ConnectionState.php',
    'danog\\MadelineProto\\MTProto\\Container' => $vendorDir . '/danog/madelineproto/src/MTProto/Container.php',
    'danog\\MadelineProto\\MTProto\\LinkedList' => $vendorDir . '/danog/madelineproto/src/MTProto/LinkedList.php',
    'danog\\MadelineProto\\MTProto\\LoginState' => $vendorDir . '/danog/madelineproto/src/MTProto/LoginState.php',
    'danog\\MadelineProto\\MTProto\\MTProtoIncomingMessage' => $vendorDir . '/danog/madelineproto/src/MTProto/MTProtoIncomingMessage.php',
    'danog\\MadelineProto\\MTProto\\MTProtoMessage' => $vendorDir . '/danog/madelineproto/src/MTProto/MTProtoMessage.php',
    'danog\\MadelineProto\\MTProto\\MTProtoOutgoingMessage' => $vendorDir . '/danog/madelineproto/src/MTProto/MTProtoOutgoingMessage.php',
    'danog\\MadelineProto\\MTProto\\NewAuthKey' => $vendorDir . '/danog/madelineproto/src/MTProto/NewAuthKey.php',
    'danog\\MadelineProto\\MTProto\\PermAuthKey' => $vendorDir . '/danog/madelineproto/src/MTProto/PermAuthKey.php',
    'danog\\MadelineProto\\MTProto\\SpecialMethodType' => $vendorDir . '/danog/madelineproto/src/MTProto/SpecialMethodType.php',
    'danog\\MadelineProto\\MTProto\\TempAuthKey' => $vendorDir . '/danog/madelineproto/src/MTProto/TempAuthKey.php',
    'danog\\MadelineProto\\Magic' => $vendorDir . '/danog/madelineproto/src/Magic.php',
    'danog\\MadelineProto\\Matroska' => $vendorDir . '/danog/madelineproto/src/Matroska.php',
    'danog\\MadelineProto\\MatroskaWriter' => $vendorDir . '/danog/madelineproto/src/MatroskaWriter.php',
    'danog\\MadelineProto\\MediaDestination' => $vendorDir . '/danog/madelineproto/src/MediaDestination.php',
    'danog\\MadelineProto\\Mp4' => $vendorDir . '/danog/madelineproto/src/Mp4.php',
    'danog\\MadelineProto\\MyTelegramOrgWrapper' => $vendorDir . '/danog/madelineproto/src/MyTelegramOrgWrapper.php',
    'danog\\MadelineProto\\Namespace\\AbstractAPI' => $vendorDir . '/danog/madelineproto/src/Namespace/AbstractAPI.php',
    'danog\\MadelineProto\\Namespace\\Account' => $vendorDir . '/danog/madelineproto/src/Namespace/Account.php',
    'danog\\MadelineProto\\Namespace\\Aicompose' => $vendorDir . '/danog/madelineproto/src/Namespace/Aicompose.php',
    'danog\\MadelineProto\\Namespace\\Auth' => $vendorDir . '/danog/madelineproto/src/Namespace/Auth.php',
    'danog\\MadelineProto\\Namespace\\Blacklist' => $vendorDir . '/danog/madelineproto/src/Namespace/Blacklist.php',
    'danog\\MadelineProto\\Namespace\\Bots' => $vendorDir . '/danog/madelineproto/src/Namespace/Bots.php',
    'danog\\MadelineProto\\Namespace\\Channels' => $vendorDir . '/danog/madelineproto/src/Namespace/Channels.php',
    'danog\\MadelineProto\\Namespace\\Chatlists' => $vendorDir . '/danog/madelineproto/src/Namespace/Chatlists.php',
    'danog\\MadelineProto\\Namespace\\Communities' => $vendorDir . '/danog/madelineproto/src/Namespace/Communities.php',
    'danog\\MadelineProto\\Namespace\\Contacts' => $vendorDir . '/danog/madelineproto/src/Namespace/Contacts.php',
    'danog\\MadelineProto\\Namespace\\Ephemeral' => $vendorDir . '/danog/madelineproto/src/Namespace/Ephemeral.php',
    'danog\\MadelineProto\\Namespace\\Folders' => $vendorDir . '/danog/madelineproto/src/Namespace/Folders.php',
    'danog\\MadelineProto\\Namespace\\Fragment' => $vendorDir . '/danog/madelineproto/src/Namespace/Fragment.php',
    'danog\\MadelineProto\\Namespace\\Help' => $vendorDir . '/danog/madelineproto/src/Namespace/Help.php',
    'danog\\MadelineProto\\Namespace\\Langpack' => $vendorDir . '/danog/madelineproto/src/Namespace/Langpack.php',
    'danog\\MadelineProto\\Namespace\\Messages' => $vendorDir . '/danog/madelineproto/src/Namespace/Messages.php',
    'danog\\MadelineProto\\Namespace\\Payments' => $vendorDir . '/danog/madelineproto/src/Namespace/Payments.php',
    'danog\\MadelineProto\\Namespace\\Phone' => $vendorDir . '/danog/madelineproto/src/Namespace/Phone.php',
    'danog\\MadelineProto\\Namespace\\Photos' => $vendorDir . '/danog/madelineproto/src/Namespace/Photos.php',
    'danog\\MadelineProto\\Namespace\\Premium' => $vendorDir . '/danog/madelineproto/src/Namespace/Premium.php',
    'danog\\MadelineProto\\Namespace\\Smsjobs' => $vendorDir . '/danog/madelineproto/src/Namespace/Smsjobs.php',
    'danog\\MadelineProto\\Namespace\\Stats' => $vendorDir . '/danog/madelineproto/src/Namespace/Stats.php',
    'danog\\MadelineProto\\Namespace\\Stickers' => $vendorDir . '/danog/madelineproto/src/Namespace/Stickers.php',
    'danog\\MadelineProto\\Namespace\\Stories' => $vendorDir . '/danog/madelineproto/src/Namespace/Stories.php',
    'danog\\MadelineProto\\Namespace\\Toncenter' => $vendorDir . '/danog/madelineproto/src/Namespace/Toncenter.php',
    'danog\\MadelineProto\\Namespace\\Updates' => $vendorDir . '/danog/madelineproto/src/Namespace/Updates.php',
    'danog\\MadelineProto\\Namespace\\Upload' => $vendorDir . '/danog/madelineproto/src/Namespace/Upload.php',
    'danog\\MadelineProto\\Namespace\\Users' => $vendorDir . '/danog/madelineproto/src/Namespace/Users.php',
    'danog\\MadelineProto\\Namespace\\Wallet' => $vendorDir . '/danog/madelineproto/src/Namespace/Wallet.php',
    'danog\\MadelineProto\\NothingInTheSocketException' => $vendorDir . '/danog/madelineproto/src/NothingInTheSocketException.php',
    'danog\\MadelineProto\\Ogg' => $vendorDir . '/danog/madelineproto/src/Ogg.php',
    'danog\\MadelineProto\\OggWriter' => $vendorDir . '/danog/madelineproto/src/OggWriter.php',
    'danog\\MadelineProto\\PTSException' => $vendorDir . '/danog/madelineproto/src/PTSException.php',
    'danog\\MadelineProto\\ParseMode' => $vendorDir . '/danog/madelineproto/src/ParseMode.php',
    'danog\\MadelineProto\\PeerNotInDbException' => $vendorDir . '/danog/madelineproto/src/PeerNotInDbException.php',
    'danog\\MadelineProto\\PluginEventHandler' => $vendorDir . '/danog/madelineproto/src/PluginEventHandler.php',
    'danog\\MadelineProto\\PsrLogger' => $vendorDir . '/danog/madelineproto/src/PsrLogger.php',
    'danog\\MadelineProto\\RPCErrorException' => $vendorDir . '/danog/madelineproto/src/RPCErrorException.php',
    'danog\\MadelineProto\\RPCError\\AllowPaymentRequiredError' => $vendorDir . '/danog/madelineproto/src/RPCError/AllowPaymentRequiredError.php',
    'danog\\MadelineProto\\RPCError\\BalanceTooLowError' => $vendorDir . '/danog/madelineproto/src/RPCError/BalanceTooLowError.php',
    'danog\\MadelineProto\\RPCError\\BotGamesDisabledError' => $vendorDir . '/danog/madelineproto/src/RPCError/BotGamesDisabledError.php',
    'danog\\MadelineProto\\RPCError\\BotPaymentsDisabledError' => $vendorDir . '/danog/madelineproto/src/RPCError/BotPaymentsDisabledError.php',
    'danog\\MadelineProto\\RPCError\\BroadcastPublicVotersForbiddenError' => $vendorDir . '/danog/madelineproto/src/RPCError/BroadcastPublicVotersForbiddenError.php',
    'danog\\MadelineProto\\RPCError\\BusinessConnectionNotAllowedError' => $vendorDir . '/danog/madelineproto/src/RPCError/BusinessConnectionNotAllowedError.php',
    'danog\\MadelineProto\\RPCError\\BusinessPeerUsageMissingError' => $vendorDir . '/danog/madelineproto/src/RPCError/BusinessPeerUsageMissingError.php',
    'danog\\MadelineProto\\RPCError\\ButtonUserPrivacyRestrictedError' => $vendorDir . '/danog/madelineproto/src/RPCError/ButtonUserPrivacyRestrictedError.php',
    'danog\\MadelineProto\\RPCError\\CallAlreadyAcceptedError' => $vendorDir . '/danog/madelineproto/src/RPCError/CallAlreadyAcceptedError.php',
    'danog\\MadelineProto\\RPCError\\CallAlreadyDeclinedError' => $vendorDir . '/danog/madelineproto/src/RPCError/CallAlreadyDeclinedError.php',
    'danog\\MadelineProto\\RPCError\\ChannelInvalidError' => $vendorDir . '/danog/madelineproto/src/RPCError/ChannelInvalidError.php',
    'danog\\MadelineProto\\RPCError\\ChannelMonoforumUnsupportedError' => $vendorDir . '/danog/madelineproto/src/RPCError/ChannelMonoforumUnsupportedError.php',
    'danog\\MadelineProto\\RPCError\\ChannelPrivateError' => $vendorDir . '/danog/madelineproto/src/RPCError/ChannelPrivateError.php',
    'danog\\MadelineProto\\RPCError\\ChatAdminRequiredError' => $vendorDir . '/danog/madelineproto/src/RPCError/ChatAdminRequiredError.php',
    'danog\\MadelineProto\\RPCError\\ChatForbiddenError' => $vendorDir . '/danog/madelineproto/src/RPCError/ChatForbiddenError.php',
    'danog\\MadelineProto\\RPCError\\ChatForwardsRestrictedError' => $vendorDir . '/danog/madelineproto/src/RPCError/ChatForwardsRestrictedError.php',
    'danog\\MadelineProto\\RPCError\\ChatGuestSendForbiddenError' => $vendorDir . '/danog/madelineproto/src/RPCError/ChatGuestSendForbiddenError.php',
    'danog\\MadelineProto\\RPCError\\ChatRestrictedError' => $vendorDir . '/danog/madelineproto/src/RPCError/ChatRestrictedError.php',
    'danog\\MadelineProto\\RPCError\\ChatSendAudiosForbiddenError' => $vendorDir . '/danog/madelineproto/src/RPCError/ChatSendAudiosForbiddenError.php',
    'danog\\MadelineProto\\RPCError\\ChatSendDocsForbiddenError' => $vendorDir . '/danog/madelineproto/src/RPCError/ChatSendDocsForbiddenError.php',
    'danog\\MadelineProto\\RPCError\\ChatSendGifsForbiddenError' => $vendorDir . '/danog/madelineproto/src/RPCError/ChatSendGifsForbiddenError.php',
    'danog\\MadelineProto\\RPCError\\ChatSendMediaForbiddenError' => $vendorDir . '/danog/madelineproto/src/RPCError/ChatSendMediaForbiddenError.php',
    'danog\\MadelineProto\\RPCError\\ChatSendPhotosForbiddenError' => $vendorDir . '/danog/madelineproto/src/RPCError/ChatSendPhotosForbiddenError.php',
    'danog\\MadelineProto\\RPCError\\ChatSendPlainForbiddenError' => $vendorDir . '/danog/madelineproto/src/RPCError/ChatSendPlainForbiddenError.php',
    'danog\\MadelineProto\\RPCError\\ChatSendPollForbiddenError' => $vendorDir . '/danog/madelineproto/src/RPCError/ChatSendPollForbiddenError.php',
    'danog\\MadelineProto\\RPCError\\ChatSendRoundvideosForbiddenError' => $vendorDir . '/danog/madelineproto/src/RPCError/ChatSendRoundvideosForbiddenError.php',
    'danog\\MadelineProto\\RPCError\\ChatSendStickersForbiddenError' => $vendorDir . '/danog/madelineproto/src/RPCError/ChatSendStickersForbiddenError.php',
    'danog\\MadelineProto\\RPCError\\ChatSendVideosForbiddenError' => $vendorDir . '/danog/madelineproto/src/RPCError/ChatSendVideosForbiddenError.php',
    'danog\\MadelineProto\\RPCError\\ChatSendVoicesForbiddenError' => $vendorDir . '/danog/madelineproto/src/RPCError/ChatSendVoicesForbiddenError.php',
    'danog\\MadelineProto\\RPCError\\ChatWriteForbiddenError' => $vendorDir . '/danog/madelineproto/src/RPCError/ChatWriteForbiddenError.php',
    'danog\\MadelineProto\\RPCError\\DcIdInvalidError' => $vendorDir . '/danog/madelineproto/src/RPCError/DcIdInvalidError.php',
    'danog\\MadelineProto\\RPCError\\EncryptionAlreadyAcceptedError' => $vendorDir . '/danog/madelineproto/src/RPCError/EncryptionAlreadyAcceptedError.php',
    'danog\\MadelineProto\\RPCError\\EncryptionAlreadyDeclinedError' => $vendorDir . '/danog/madelineproto/src/RPCError/EncryptionAlreadyDeclinedError.php',
    'danog\\MadelineProto\\RPCError\\EncryptionDeclinedError' => $vendorDir . '/danog/madelineproto/src/RPCError/EncryptionDeclinedError.php',
    'danog\\MadelineProto\\RPCError\\FileReferenceExpiredError' => $vendorDir . '/danog/madelineproto/src/RPCError/FileReferenceExpiredError.php',
    'danog\\MadelineProto\\RPCError\\FileTokenInvalidError' => $vendorDir . '/danog/madelineproto/src/RPCError/FileTokenInvalidError.php',
    'danog\\MadelineProto\\RPCError\\FloodPremiumWaitError' => $vendorDir . '/danog/madelineproto/src/RPCError/FloodPremiumWaitError.php',
    'danog\\MadelineProto\\RPCError\\FloodWaitError' => $vendorDir . '/danog/madelineproto/src/RPCError/FloodWaitError.php',
    'danog\\MadelineProto\\RPCError\\FromMessageBotDisabledError' => $vendorDir . '/danog/madelineproto/src/RPCError/FromMessageBotDisabledError.php',
    'danog\\MadelineProto\\RPCError\\ImageProcessFailedError' => $vendorDir . '/danog/madelineproto/src/RPCError/ImageProcessFailedError.php',
    'danog\\MadelineProto\\RPCError\\InputUserDeactivatedError' => $vendorDir . '/danog/madelineproto/src/RPCError/InputUserDeactivatedError.php',
    'danog\\MadelineProto\\RPCError\\MsgIdInvalidError' => $vendorDir . '/danog/madelineproto/src/RPCError/MsgIdInvalidError.php',
    'danog\\MadelineProto\\RPCError\\PasswordHashInvalidError' => $vendorDir . '/danog/madelineproto/src/RPCError/PasswordHashInvalidError.php',
    'danog\\MadelineProto\\RPCError\\PaymentUnsupportedError' => $vendorDir . '/danog/madelineproto/src/RPCError/PaymentUnsupportedError.php',
    'danog\\MadelineProto\\RPCError\\PeerIdInvalidError' => $vendorDir . '/danog/madelineproto/src/RPCError/PeerIdInvalidError.php',
    'danog\\MadelineProto\\RPCError\\PinnedDialogsTooMuchError' => $vendorDir . '/danog/madelineproto/src/RPCError/PinnedDialogsTooMuchError.php',
    'danog\\MadelineProto\\RPCError\\PollOptionDuplicateError' => $vendorDir . '/danog/madelineproto/src/RPCError/PollOptionDuplicateError.php',
    'danog\\MadelineProto\\RPCError\\PremiumAccountRequiredError' => $vendorDir . '/danog/madelineproto/src/RPCError/PremiumAccountRequiredError.php',
    'danog\\MadelineProto\\RPCError\\PrivacyPremiumRequiredError' => $vendorDir . '/danog/madelineproto/src/RPCError/PrivacyPremiumRequiredError.php',
    'danog\\MadelineProto\\RPCError\\QuickRepliesBotNotAllowedError' => $vendorDir . '/danog/madelineproto/src/RPCError/QuickRepliesBotNotAllowedError.php',
    'danog\\MadelineProto\\RPCError\\QuickRepliesTooMuchError' => $vendorDir . '/danog/madelineproto/src/RPCError/QuickRepliesTooMuchError.php',
    'danog\\MadelineProto\\RPCError\\QuizCorrectAnswersTooMuchError' => $vendorDir . '/danog/madelineproto/src/RPCError/QuizCorrectAnswersTooMuchError.php',
    'danog\\MadelineProto\\RPCError\\RateLimitError' => $vendorDir . '/danog/madelineproto/src/RPCError/RateLimitError.php',
    'danog\\MadelineProto\\RPCError\\ReplyMessagesTooMuchError' => $vendorDir . '/danog/madelineproto/src/RPCError/ReplyMessagesTooMuchError.php',
    'danog\\MadelineProto\\RPCError\\RequestTokenInvalidError' => $vendorDir . '/danog/madelineproto/src/RPCError/RequestTokenInvalidError.php',
    'danog\\MadelineProto\\RPCError\\ScheduleBotNotAllowedError' => $vendorDir . '/danog/madelineproto/src/RPCError/ScheduleBotNotAllowedError.php',
    'danog\\MadelineProto\\RPCError\\ScheduleDateTooLateError' => $vendorDir . '/danog/madelineproto/src/RPCError/ScheduleDateTooLateError.php',
    'danog\\MadelineProto\\RPCError\\ScheduleStatusPrivateError' => $vendorDir . '/danog/madelineproto/src/RPCError/ScheduleStatusPrivateError.php',
    'danog\\MadelineProto\\RPCError\\ScheduleTooMuchError' => $vendorDir . '/danog/madelineproto/src/RPCError/ScheduleTooMuchError.php',
    'danog\\MadelineProto\\RPCError\\SessionPasswordNeededError' => $vendorDir . '/danog/madelineproto/src/RPCError/SessionPasswordNeededError.php',
    'danog\\MadelineProto\\RPCError\\StoriesNeverCreatedError' => $vendorDir . '/danog/madelineproto/src/RPCError/StoriesNeverCreatedError.php',
    'danog\\MadelineProto\\RPCError\\SubscriptionExportMissingError' => $vendorDir . '/danog/madelineproto/src/RPCError/SubscriptionExportMissingError.php',
    'danog\\MadelineProto\\RPCError\\TimeoutError' => $vendorDir . '/danog/madelineproto/src/RPCError/TimeoutError.php',
    'danog\\MadelineProto\\RPCError\\TodoItemDuplicateError' => $vendorDir . '/danog/madelineproto/src/RPCError/TodoItemDuplicateError.php',
    'danog\\MadelineProto\\RPCError\\TodoItemsTooMuchError' => $vendorDir . '/danog/madelineproto/src/RPCError/TodoItemsTooMuchError.php',
    'danog\\MadelineProto\\RPCError\\TopicClosedError' => $vendorDir . '/danog/madelineproto/src/RPCError/TopicClosedError.php',
    'danog\\MadelineProto\\RPCError\\TopicDeletedError' => $vendorDir . '/danog/madelineproto/src/RPCError/TopicDeletedError.php',
    'danog\\MadelineProto\\RPCError\\UserBannedInChannelError' => $vendorDir . '/danog/madelineproto/src/RPCError/UserBannedInChannelError.php',
    'danog\\MadelineProto\\RPCError\\UserBotToBotDisabledError' => $vendorDir . '/danog/madelineproto/src/RPCError/UserBotToBotDisabledError.php',
    'danog\\MadelineProto\\RPCError\\UserIsBlockedError' => $vendorDir . '/danog/madelineproto/src/RPCError/UserIsBlockedError.php',
    'danog\\MadelineProto\\RPCError\\UserIsBotError' => $vendorDir . '/danog/madelineproto/src/RPCError/UserIsBotError.php',
    'danog\\MadelineProto\\RPCError\\UsernameInvalidError' => $vendorDir . '/danog/madelineproto/src/RPCError/UsernameInvalidError.php',
    'danog\\MadelineProto\\RPCError\\UsernameNotOccupiedError' => $vendorDir . '/danog/madelineproto/src/RPCError/UsernameNotOccupiedError.php',
    'danog\\MadelineProto\\RPCError\\VoiceMessagesForbiddenError' => $vendorDir . '/danog/madelineproto/src/RPCError/VoiceMessagesForbiddenError.php',
    'danog\\MadelineProto\\RPCError\\WebpageCurlFailedError' => $vendorDir . '/danog/madelineproto/src/RPCError/WebpageCurlFailedError.php',
    'danog\\MadelineProto\\RPCError\\WebpageNotFoundError' => $vendorDir . '/danog/madelineproto/src/RPCError/WebpageNotFoundError.php',
    'danog\\MadelineProto\\RPCError\\YouBlockedUserError' => $vendorDir . '/danog/madelineproto/src/RPCError/YouBlockedUserError.php',
    'danog\\MadelineProto\\RSA' => $vendorDir . '/danog/madelineproto/src/RSA.php',
    'danog\\MadelineProto\\Reactive\\Actor' => $vendorDir . '/danog/madelineproto/src/Reactive/Actor.php',
    'danog\\MadelineProto\\Reactive\\AsyncWaiter' => $vendorDir . '/danog/madelineproto/src/Reactive/AsyncWaiter.php',
    'danog\\MadelineProto\\Reactive\\BaseSubscriber' => $vendorDir . '/danog/madelineproto/src/Reactive/BaseSubscriber.php',
    'danog\\MadelineProto\\Reactive\\EphemeralSubscriber' => $vendorDir . '/danog/madelineproto/src/Reactive/EphemeralSubscriber.php',
    'danog\\MadelineProto\\Reactive\\Publisher' => $vendorDir . '/danog/madelineproto/src/Reactive/Publisher.php',
    'danog\\MadelineProto\\Reactive\\SimpleSubscriber' => $vendorDir . '/danog/madelineproto/src/Reactive/SimpleSubscriber.php',
    'danog\\MadelineProto\\Reactive\\SimpleSubscriberAdaptor' => $vendorDir . '/danog/madelineproto/src/Reactive/SimpleSubscriberAdaptor.php',
    'danog\\MadelineProto\\Reactive\\Subscriber' => $vendorDir . '/danog/madelineproto/src/Reactive/Subscriber.php',
    'danog\\MadelineProto\\RecordingEvent' => $vendorDir . '/danog/madelineproto/src/RecordingEvent.php',
    'danog\\MadelineProto\\RecordingFormat' => $vendorDir . '/danog/madelineproto/src/RecordingFormat.php',
    'danog\\MadelineProto\\ReferencedTimeoutCancellation' => $vendorDir . '/danog/madelineproto/src/ReferencedTimeoutCancellation.php',
    'danog\\MadelineProto\\RemoteUrl' => $vendorDir . '/danog/madelineproto/src/RemoteUrl.php',
    'danog\\MadelineProto\\ResponseException' => $vendorDir . '/danog/madelineproto/src/ResponseException.php',
    'danog\\MadelineProto\\RestoresDeclaredProperties' => $vendorDir . '/danog/madelineproto/src/RestoresDeclaredProperties.php',
    'danog\\MadelineProto\\ResumableDownloadStream' => $vendorDir . '/danog/madelineproto/src/ResumableDownloadStream.php',
    'danog\\MadelineProto\\ResumableStream' => $vendorDir . '/danog/madelineproto/src/ResumableStream.php',
    'danog\\MadelineProto\\SecretChats\\AuthKeyHandler' => $vendorDir . '/danog/madelineproto/src/SecretChats/AuthKeyHandler.php',
    'danog\\MadelineProto\\SecretChats\\RekeyState' => $vendorDir . '/danog/madelineproto/src/SecretChats/RekeyState.php',
    'danog\\MadelineProto\\SecretChats\\SecretChat' => $vendorDir . '/danog/madelineproto/src/SecretChats/SecretChat.php',
    'danog\\MadelineProto\\SecretChats\\SecretChatController' => $vendorDir . '/danog/madelineproto/src/SecretChats/SecretChatController.php',
    'danog\\MadelineProto\\SecretPeerNotInDbException' => $vendorDir . '/danog/madelineproto/src/SecretPeerNotInDbException.php',
    'danog\\MadelineProto\\SecurityException' => $vendorDir . '/danog/madelineproto/src/SecurityException.php',
    'danog\\MadelineProto\\Serialization' => $vendorDir . '/danog/madelineproto/src/Serialization.php',
    'danog\\MadelineProto\\SessionPaths' => $vendorDir . '/danog/madelineproto/src/SessionPaths.php',
    'danog\\MadelineProto\\Settings' => $vendorDir . '/danog/madelineproto/src/Settings.php',
    'danog\\MadelineProto\\SettingsAbstract' => $vendorDir . '/danog/madelineproto/src/SettingsAbstract.php',
    'danog\\MadelineProto\\SettingsEmpty' => $vendorDir . '/danog/madelineproto/src/SettingsEmpty.php',
    'danog\\MadelineProto\\SettingsGetter' => $vendorDir . '/danog/madelineproto/src/SettingsGetter.php',
    'danog\\MadelineProto\\Settings\\AppInfo' => $vendorDir . '/danog/madelineproto/src/Settings/AppInfo.php',
    'danog\\MadelineProto\\Settings\\Auth' => $vendorDir . '/danog/madelineproto/src/Settings/Auth.php',
    'danog\\MadelineProto\\Settings\\Connection' => $vendorDir . '/danog/madelineproto/src/Settings/Connection.php',
    'danog\\MadelineProto\\Settings\\DatabaseAbstract' => $vendorDir . '/danog/madelineproto/src/Settings/DatabaseAbstract.php',
    'danog\\MadelineProto\\Settings\\Database\\DriverDatabaseAbstract' => $vendorDir . '/danog/madelineproto/src/Settings/Database/DriverDatabaseAbstract.php',
    'danog\\MadelineProto\\Settings\\Database\\Memory' => $vendorDir . '/danog/madelineproto/src/Settings/Database/Memory.php',
    'danog\\MadelineProto\\Settings\\Database\\Mysql' => $vendorDir . '/danog/madelineproto/src/Settings/Database/Mysql.php',
    'danog\\MadelineProto\\Settings\\Database\\Postgres' => $vendorDir . '/danog/madelineproto/src/Settings/Database/Postgres.php',
    'danog\\MadelineProto\\Settings\\Database\\Redis' => $vendorDir . '/danog/madelineproto/src/Settings/Database/Redis.php',
    'danog\\MadelineProto\\Settings\\Database\\SerializerType' => $vendorDir . '/danog/madelineproto/src/Settings/Database/SerializerType.php',
    'danog\\MadelineProto\\Settings\\Database\\SqlAbstract' => $vendorDir . '/danog/madelineproto/src/Settings/Database/SqlAbstract.php',
    'danog\\MadelineProto\\Settings\\Files' => $vendorDir . '/danog/madelineproto/src/Settings/Files.php',
    'danog\\MadelineProto\\Settings\\Ipc' => $vendorDir . '/danog/madelineproto/src/Settings/Ipc.php',
    'danog\\MadelineProto\\Settings\\Logger' => $vendorDir . '/danog/madelineproto/src/Settings/Logger.php',
    'danog\\MadelineProto\\Settings\\Metrics' => $vendorDir . '/danog/madelineproto/src/Settings/Metrics.php',
    'danog\\MadelineProto\\Settings\\Peer' => $vendorDir . '/danog/madelineproto/src/Settings/Peer.php',
    'danog\\MadelineProto\\Settings\\Pwr' => $vendorDir . '/danog/madelineproto/src/Settings/Pwr.php',
    'danog\\MadelineProto\\Settings\\RPC' => $vendorDir . '/danog/madelineproto/src/Settings/RPC.php',
    'danog\\MadelineProto\\Settings\\SecretChats' => $vendorDir . '/danog/madelineproto/src/Settings/SecretChats.php',
    'danog\\MadelineProto\\Settings\\Serialization' => $vendorDir . '/danog/madelineproto/src/Settings/Serialization.php',
    'danog\\MadelineProto\\Settings\\TLSchema' => $vendorDir . '/danog/madelineproto/src/Settings/TLSchema.php',
    'danog\\MadelineProto\\Settings\\Templates' => $vendorDir . '/danog/madelineproto/src/Settings/Templates.php',
    'danog\\MadelineProto\\Settings\\VoIP' => $vendorDir . '/danog/madelineproto/src/Settings/VoIP.php',
    'danog\\MadelineProto\\Shutdown' => $vendorDir . '/danog/madelineproto/src/Shutdown.php',
    'danog\\MadelineProto\\SimpleEventHandler' => $vendorDir . '/danog/madelineproto/src/SimpleEventHandler.php',
    'danog\\MadelineProto\\Snitch' => $vendorDir . '/danog/madelineproto/src/Snitch.php',
    'danog\\MadelineProto\\StrTools' => $vendorDir . '/danog/madelineproto/src/StrTools.php',
    'danog\\MadelineProto\\StreamDuplicator' => $vendorDir . '/danog/madelineproto/src/StreamDuplicator.php',
    'danog\\MadelineProto\\StreamEof' => $vendorDir . '/danog/madelineproto/src/StreamEof.php',
    'danog\\MadelineProto\\Stream\\ADNLTransport\\ADNLStream' => $vendorDir . '/danog/madelineproto/src/Stream/ADNLTransport/ADNLStream.php',
    'danog\\MadelineProto\\Stream\\BufferInterface' => $vendorDir . '/danog/madelineproto/src/Stream/BufferInterface.php',
    'danog\\MadelineProto\\Stream\\BufferedProxyStreamInterface' => $vendorDir . '/danog/madelineproto/src/Stream/BufferedProxyStreamInterface.php',
    'danog\\MadelineProto\\Stream\\BufferedStreamInterface' => $vendorDir . '/danog/madelineproto/src/Stream/BufferedStreamInterface.php',
    'danog\\MadelineProto\\Stream\\Common\\BufferedRawStream' => $vendorDir . '/danog/madelineproto/src/Stream/Common/BufferedRawStream.php',
    'danog\\MadelineProto\\Stream\\Common\\CtrStream' => $vendorDir . '/danog/madelineproto/src/Stream/Common/CtrStream.php',
    'danog\\MadelineProto\\Stream\\Common\\FileBufferedStream' => $vendorDir . '/danog/madelineproto/src/Stream/Common/FileBufferedStream.php',
    'danog\\MadelineProto\\Stream\\Common\\HashedBufferedStream' => $vendorDir . '/danog/madelineproto/src/Stream/Common/HashedBufferedStream.php',
    'danog\\MadelineProto\\Stream\\Common\\SimpleBufferedRawStream' => $vendorDir . '/danog/madelineproto/src/Stream/Common/SimpleBufferedRawStream.php',
    'danog\\MadelineProto\\Stream\\Common\\UdpBufferedStream' => $vendorDir . '/danog/madelineproto/src/Stream/Common/UdpBufferedStream.php',
    'danog\\MadelineProto\\Stream\\ConnectionContext' => $vendorDir . '/danog/madelineproto/src/Stream/ConnectionContext.php',
    'danog\\MadelineProto\\Stream\\ContextIterator' => $vendorDir . '/danog/madelineproto/src/Stream/ContextIterator.php',
    'danog\\MadelineProto\\Stream\\MTProtoBufferInterface' => $vendorDir . '/danog/madelineproto/src/Stream/MTProtoBufferInterface.php',
    'danog\\MadelineProto\\Stream\\MTProtoTransport\\AbridgedStream' => $vendorDir . '/danog/madelineproto/src/Stream/MTProtoTransport/AbridgedStream.php',
    'danog\\MadelineProto\\Stream\\MTProtoTransport\\FakeTlsStream' => $vendorDir . '/danog/madelineproto/src/Stream/MTProtoTransport/FakeTlsStream.php',
    'danog\\MadelineProto\\Stream\\MTProtoTransport\\FullStream' => $vendorDir . '/danog/madelineproto/src/Stream/MTProtoTransport/FullStream.php',
    'danog\\MadelineProto\\Stream\\MTProtoTransport\\HttpStream' => $vendorDir . '/danog/madelineproto/src/Stream/MTProtoTransport/HttpStream.php',
    'danog\\MadelineProto\\Stream\\MTProtoTransport\\HttpsStream' => $vendorDir . '/danog/madelineproto/src/Stream/MTProtoTransport/HttpsStream.php',
    'danog\\MadelineProto\\Stream\\MTProtoTransport\\IntermediatePaddedStream' => $vendorDir . '/danog/madelineproto/src/Stream/MTProtoTransport/IntermediatePaddedStream.php',
    'danog\\MadelineProto\\Stream\\MTProtoTransport\\IntermediateStream' => $vendorDir . '/danog/madelineproto/src/Stream/MTProtoTransport/IntermediateStream.php',
    'danog\\MadelineProto\\Stream\\MTProtoTransport\\ObfuscatedStream' => $vendorDir . '/danog/madelineproto/src/Stream/MTProtoTransport/ObfuscatedStream.php',
    'danog\\MadelineProto\\Stream\\ProxyStreamInterface' => $vendorDir . '/danog/madelineproto/src/Stream/ProxyStreamInterface.php',
    'danog\\MadelineProto\\Stream\\Proxy\\HttpProxy' => $vendorDir . '/danog/madelineproto/src/Stream/Proxy/HttpProxy.php',
    'danog\\MadelineProto\\Stream\\Proxy\\SocksProxy' => $vendorDir . '/danog/madelineproto/src/Stream/Proxy/SocksProxy.php',
    'danog\\MadelineProto\\Stream\\RawProxyStreamInterface' => $vendorDir . '/danog/madelineproto/src/Stream/RawProxyStreamInterface.php',
    'danog\\MadelineProto\\Stream\\RawStreamInterface' => $vendorDir . '/danog/madelineproto/src/Stream/RawStreamInterface.php',
    'danog\\MadelineProto\\Stream\\ReadBufferInterface' => $vendorDir . '/danog/madelineproto/src/Stream/ReadBufferInterface.php',
    'danog\\MadelineProto\\Stream\\StreamInterface' => $vendorDir . '/danog/madelineproto/src/Stream/StreamInterface.php',
    'danog\\MadelineProto\\Stream\\Transport\\DefaultStream' => $vendorDir . '/danog/madelineproto/src/Stream/Transport/DefaultStream.php',
    'danog\\MadelineProto\\Stream\\Transport\\PremadeStream' => $vendorDir . '/danog/madelineproto/src/Stream/Transport/PremadeStream.php',
    'danog\\MadelineProto\\Stream\\Transport\\WsStream' => $vendorDir . '/danog/madelineproto/src/Stream/Transport/WsStream.php',
    'danog\\MadelineProto\\Stream\\Transport\\WssStream' => $vendorDir . '/danog/madelineproto/src/Stream/Transport/WssStream.php',
    'danog\\MadelineProto\\Stream\\WriteBufferInterface' => $vendorDir . '/danog/madelineproto/src/Stream/WriteBufferInterface.php',
    'danog\\MadelineProto\\TL\\Conversion\\BotAPI' => $vendorDir . '/danog/madelineproto/src/TL/Conversion/BotAPI.php',
    'danog\\MadelineProto\\TL\\Conversion\\BotAPIFiles' => $vendorDir . '/danog/madelineproto/src/TL/Conversion/BotAPIFiles.php',
    'danog\\MadelineProto\\TL\\Conversion\\Exception' => $vendorDir . '/danog/madelineproto/src/TL/Conversion/Exception.php',
    'danog\\MadelineProto\\TL\\Conversion\\Extension' => $vendorDir . '/danog/madelineproto/src/TL/Conversion/Extension.php',
    'danog\\MadelineProto\\TL\\Conversion\\TD' => $vendorDir . '/danog/madelineproto/src/TL/Conversion/TD.php',
    'danog\\MadelineProto\\TL\\Exception' => $vendorDir . '/danog/madelineproto/src/TL/Exception.php',
    'danog\\MadelineProto\\TL\\PrettyException' => $vendorDir . '/danog/madelineproto/src/TL/PrettyException.php',
    'danog\\MadelineProto\\TL\\SecretTLParser' => $vendorDir . '/danog/madelineproto/src/TL/SecretTLParser.php',
    'danog\\MadelineProto\\TL\\TL' => $vendorDir . '/danog/madelineproto/src/TL/TL.php',
    'danog\\MadelineProto\\TL\\TLCallback' => $vendorDir . '/danog/madelineproto/src/TL/TLCallback.php',
    'danog\\MadelineProto\\TL\\TLConstructors' => $vendorDir . '/danog/madelineproto/src/TL/TLConstructors.php',
    'danog\\MadelineProto\\TL\\TLInterface' => $vendorDir . '/danog/madelineproto/src/TL/TLInterface.php',
    'danog\\MadelineProto\\TL\\TLMethods' => $vendorDir . '/danog/madelineproto/src/TL/TLMethods.php',
    'danog\\MadelineProto\\TL\\TLParams' => $vendorDir . '/danog/madelineproto/src/TL/TLParams.php',
    'danog\\MadelineProto\\TL\\TLParser' => $vendorDir . '/danog/madelineproto/src/TL/TLParser.php',
    'danog\\MadelineProto\\TL\\Types\\Button' => $vendorDir . '/danog/madelineproto/src/TL/Types/Button.php',
    'danog\\MadelineProto\\TL\\Types\\Bytes' => $vendorDir . '/danog/madelineproto/src/TL/Types/Bytes.php',
    'danog\\MadelineProto\\TL\\Types\\LoginQrCode' => $vendorDir . '/danog/madelineproto/src/TL/Types/LoginQrCode.php',
    'danog\\MadelineProto\\TextEntities' => $vendorDir . '/danog/madelineproto/src/TextEntities.php',
    'danog\\MadelineProto\\Tgcalls\\Av1Bitstream' => $vendorDir . '/danog/madelineproto/src/Tgcalls/Av1Bitstream.php',
    'danog\\MadelineProto\\Tgcalls\\BitReader' => $vendorDir . '/danog/madelineproto/src/Tgcalls/BitReader.php',
    'danog\\MadelineProto\\Tgcalls\\CallControllerInterface' => $vendorDir . '/danog/madelineproto/src/Tgcalls/CallControllerInterface.php',
    'danog\\MadelineProto\\Tgcalls\\CallRecorder' => $vendorDir . '/danog/madelineproto/src/Tgcalls/CallRecorder.php',
    'danog\\MadelineProto\\Tgcalls\\CallStreamsDispatcher' => $vendorDir . '/danog/madelineproto/src/Tgcalls/CallStreamsDispatcher.php',
    'danog\\MadelineProto\\Tgcalls\\Controller' => $vendorDir . '/danog/madelineproto/src/Tgcalls/Controller.php',
    'danog\\MadelineProto\\Tgcalls\\E2E\\BlockCodec' => $vendorDir . '/danog/madelineproto/src/Tgcalls/E2E/BlockCodec.php',
    'danog\\MadelineProto\\Tgcalls\\E2E\\CallPacket' => $vendorDir . '/danog/madelineproto/src/Tgcalls/E2E/CallPacket.php',
    'danog\\MadelineProto\\Tgcalls\\E2E\\ConferenceCall' => $vendorDir . '/danog/madelineproto/src/Tgcalls/E2E/ConferenceCall.php',
    'danog\\MadelineProto\\Tgcalls\\E2E\\ConferenceChain' => $vendorDir . '/danog/madelineproto/src/Tgcalls/E2E/ConferenceChain.php',
    'danog\\MadelineProto\\Tgcalls\\E2E\\Crypto' => $vendorDir . '/danog/madelineproto/src/Tgcalls/E2E/Crypto.php',
    'danog\\MadelineProto\\Tgcalls\\E2E\\E2EKeyProvider' => $vendorDir . '/danog/madelineproto/src/Tgcalls/E2E/E2EKeyProvider.php',
    'danog\\MadelineProto\\Tgcalls\\E2E\\FrameCryptor' => $vendorDir . '/danog/madelineproto/src/Tgcalls/E2E/FrameCryptor.php',
    'danog\\MadelineProto\\Tgcalls\\E2E\\FrameCryptorState' => $vendorDir . '/danog/madelineproto/src/Tgcalls/E2E/FrameCryptorState.php',
    'danog\\MadelineProto\\Tgcalls\\E2E\\Verification' => $vendorDir . '/danog/madelineproto/src/Tgcalls/E2E/Verification.php',
    'danog\\MadelineProto\\Tgcalls\\EncryptedConnection' => $vendorDir . '/danog/madelineproto/src/Tgcalls/EncryptedConnection.php',
    'danog\\MadelineProto\\Tgcalls\\GroupConnection' => $vendorDir . '/danog/madelineproto/src/Tgcalls/GroupConnection.php',
    'danog\\MadelineProto\\Tgcalls\\GroupConnectionOwner' => $vendorDir . '/danog/madelineproto/src/Tgcalls/GroupConnectionOwner.php',
    'danog\\MadelineProto\\Tgcalls\\GroupMediaTrait' => $vendorDir . '/danog/madelineproto/src/Tgcalls/GroupMediaTrait.php',
    'danog\\MadelineProto\\Tgcalls\\GroupSdp' => $vendorDir . '/danog/madelineproto/src/Tgcalls/GroupSdp.php',
    'danog\\MadelineProto\\Tgcalls\\H264Framing' => $vendorDir . '/danog/madelineproto/src/Tgcalls/H264Framing.php',
    'danog\\MadelineProto\\Tgcalls\\HevcBitstream' => $vendorDir . '/danog/madelineproto/src/Tgcalls/HevcBitstream.php',
    'danog\\MadelineProto\\Tgcalls\\IncomingMedia' => $vendorDir . '/danog/madelineproto/src/Tgcalls/IncomingMedia.php',
    'danog\\MadelineProto\\Tgcalls\\IncomingMediaObserver' => $vendorDir . '/danog/madelineproto/src/Tgcalls/IncomingMediaObserver.php',
    'danog\\MadelineProto\\Tgcalls\\LegacyController' => $vendorDir . '/danog/madelineproto/src/Tgcalls/LegacyController.php',
    'danog\\MadelineProto\\Tgcalls\\OpusPlaybackTrack' => $vendorDir . '/danog/madelineproto/src/Tgcalls/OpusPlaybackTrack.php',
    'danog\\MadelineProto\\Tgcalls\\OpusRecorder' => $vendorDir . '/danog/madelineproto/src/Tgcalls/OpusRecorder.php',
    'danog\\MadelineProto\\Tgcalls\\PrivateCallController' => $vendorDir . '/danog/madelineproto/src/Tgcalls/PrivateCallController.php',
    'danog\\MadelineProto\\Tgcalls\\RecordingObserver' => $vendorDir . '/danog/madelineproto/src/Tgcalls/RecordingObserver.php',
    'danog\\MadelineProto\\Tgcalls\\RtcDebugLogger' => $vendorDir . '/danog/madelineproto/src/Tgcalls/RtcDebugLogger.php',
    'danog\\MadelineProto\\Tgcalls\\ScreencastCodecObserver' => $vendorDir . '/danog/madelineproto/src/Tgcalls/ScreencastCodecObserver.php',
    'danog\\MadelineProto\\Tgcalls\\SctpSignalingObserver' => $vendorDir . '/danog/madelineproto/src/Tgcalls/SctpSignalingObserver.php',
    'danog\\MadelineProto\\Tgcalls\\SignalingIceRole' => $vendorDir . '/danog/madelineproto/src/Tgcalls/SignalingIceRole.php',
    'danog\\MadelineProto\\Tgcalls\\SignalingSctpTransport' => $vendorDir . '/danog/madelineproto/src/Tgcalls/SignalingSctpTransport.php',
    'danog\\MadelineProto\\Tgcalls\\SignalingServiceObserver' => $vendorDir . '/danog/madelineproto/src/Tgcalls/SignalingServiceObserver.php',
    'danog\\MadelineProto\\Tgcalls\\TgcallsTools' => $vendorDir . '/danog/madelineproto/src/Tgcalls/TgcallsTools.php',
    'danog\\MadelineProto\\Tgcalls\\V2Sdp' => $vendorDir . '/danog/madelineproto/src/Tgcalls/V2Sdp.php',
    'danog\\MadelineProto\\Tgcalls\\VideoCodecObserver' => $vendorDir . '/danog/madelineproto/src/Tgcalls/VideoCodecObserver.php',
    'danog\\MadelineProto\\Tgcalls\\VideoPlaybackTrack' => $vendorDir . '/danog/madelineproto/src/Tgcalls/VideoPlaybackTrack.php',
    'danog\\MadelineProto\\Tools' => $vendorDir . '/danog/madelineproto/src/Tools.php',
    'danog\\MadelineProto\\TransportError' => $vendorDir . '/danog/madelineproto/src/TransportError.php',
    'danog\\MadelineProto\\UpdateHandlerType' => $vendorDir . '/danog/madelineproto/src/UpdateHandlerType.php',
    'danog\\MadelineProto\\UploadResumeException' => $vendorDir . '/danog/madelineproto/src/UploadResumeException.php',
    'danog\\MadelineProto\\VoIPServerConfig' => $vendorDir . '/danog/madelineproto/src/VoIPServerConfig.php',
    'danog\\MadelineProto\\VoIP\\AuthKeyHandler' => $vendorDir . '/danog/madelineproto/src/VoIP/AuthKeyHandler.php',
    'danog\\MadelineProto\\VoIP\\CallState' => $vendorDir . '/danog/madelineproto/src/VoIP/CallState.php',
    'danog\\MadelineProto\\VoIP\\DiscardReason' => $vendorDir . '/danog/madelineproto/src/VoIP/DiscardReason.php',
    'danog\\MadelineProto\\VoIP\\Endpoint' => $vendorDir . '/danog/madelineproto/src/VoIP/Endpoint.php',
    'danog\\MadelineProto\\VoIP\\MediaState' => $vendorDir . '/danog/madelineproto/src/VoIP/MediaState.php',
    'danog\\MadelineProto\\VoIP\\MessageHandler' => $vendorDir . '/danog/madelineproto/src/VoIP/MessageHandler.php',
    'danog\\MadelineProto\\VoIP\\SignalingProtocolVersion' => $vendorDir . '/danog/madelineproto/src/VoIP/SignalingProtocolVersion.php',
    'danog\\MadelineProto\\VoIP\\VoIPState' => $vendorDir . '/danog/madelineproto/src/VoIP/VoIPState.php',
    'danog\\MadelineProto\\WebUI\\Action' => $vendorDir . '/danog/madelineproto/src/WebUI/Action.php',
    'danog\\MadelineProto\\WebUI\\Alert' => $vendorDir . '/danog/madelineproto/src/WebUI/Alert.php',
    'danog\\MadelineProto\\WebUI\\AlertLevel' => $vendorDir . '/danog/madelineproto/src/WebUI/AlertLevel.php',
    'danog\\MadelineProto\\WebUI\\Field' => $vendorDir . '/danog/madelineproto/src/WebUI/Field.php',
    'danog\\MadelineProto\\WebUI\\FieldType' => $vendorDir . '/danog/madelineproto/src/WebUI/FieldType.php',
    'danog\\MadelineProto\\WebUI\\Page' => $vendorDir . '/danog/madelineproto/src/WebUI/Page.php',
    'danog\\MadelineProto\\WebUI\\SafeHtml' => $vendorDir . '/danog/madelineproto/src/WebUI/SafeHtml.php',
    'danog\\MadelineProto\\WebUI\\Step' => $vendorDir . '/danog/madelineproto/src/WebUI/Step.php',
    'danog\\MadelineProto\\WebUI\\Template' => $vendorDir . '/danog/madelineproto/src/WebUI/Template.php',
    'danog\\MadelineProto\\WebUI\\TemplateEngine' => $vendorDir . '/danog/madelineproto/src/WebUI/TemplateEngine.php',
    'danog\\MadelineProto\\WebUI\\WebUI' => $vendorDir . '/danog/madelineproto/src/WebUI/WebUI.php',
    'danog\\MadelineProto\\WrappedFuture' => $vendorDir . '/danog/madelineproto/src/WrappedFuture.php',
    'danog\\MadelineProto\\Wrappers\\Ads' => $vendorDir . '/danog/madelineproto/src/Wrappers/Ads.php',
    'danog\\MadelineProto\\Wrappers\\Button' => $vendorDir . '/danog/madelineproto/src/Wrappers/Button.php',
    'danog\\MadelineProto\\Wrappers\\DialogHandler' => $vendorDir . '/danog/madelineproto/src/Wrappers/DialogHandler.php',
    'danog\\MadelineProto\\Wrappers\\Events' => $vendorDir . '/danog/madelineproto/src/Wrappers/Events.php',
    'danog\\MadelineProto\\Wrappers\\Login' => $vendorDir . '/danog/madelineproto/src/Wrappers/Login.php',
    'danog\\MadelineProto\\Wrappers\\Loop' => $vendorDir . '/danog/madelineproto/src/Wrappers/Loop.php',
    'danog\\MadelineProto\\Wrappers\\Start' => $vendorDir . '/danog/madelineproto/src/Wrappers/Start.php',
    'danog\\PrimeModule' => $vendorDir . '/danog/primemodule/lib/danog/PrimeModule.php',
    'danog\\TelegramEntities\\Entities' => $vendorDir . '/danog/telegram-entities/src/Entities.php',
    'danog\\TelegramEntities\\EntityTools' => $vendorDir . '/danog/telegram-entities/src/EntityTools.php',
    'phpseclib4\\Common\\ConstantUtilityTrait' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Common/ConstantUtilityTrait.php',
    'phpseclib4\\Common\\Functions\\Arrays' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Common/Functions/Arrays.php',
    'phpseclib4\\Common\\Functions\\Files' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Common/Functions/Files.php',
    'phpseclib4\\Common\\Functions\\Strings' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Common/Functions/Strings.php',
    'phpseclib4\\Crypt\\AES' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/AES.php',
    'phpseclib4\\Crypt\\Blowfish' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Blowfish.php',
    'phpseclib4\\Crypt\\ChaCha20' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/ChaCha20.php',
    'phpseclib4\\Crypt\\Common\\AsymmetricKey' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Common/AsymmetricKey.php',
    'phpseclib4\\Crypt\\Common\\BlockCipher' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Common/BlockCipher.php',
    'phpseclib4\\Crypt\\Common\\Formats\\Keys\\JWK' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/JWK.php',
    'phpseclib4\\Crypt\\Common\\Formats\\Keys\\OpenSSH' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/OpenSSH.php',
    'phpseclib4\\Crypt\\Common\\Formats\\Keys\\PKCS' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PKCS.php',
    'phpseclib4\\Crypt\\Common\\Formats\\Keys\\PKCS1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PKCS1.php',
    'phpseclib4\\Crypt\\Common\\Formats\\Keys\\PKCS8' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PKCS8.php',
    'phpseclib4\\Crypt\\Common\\Formats\\Keys\\PuTTY' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PuTTY.php',
    'phpseclib4\\Crypt\\Common\\Formats\\Signature\\Raw' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Signature/Raw.php',
    'phpseclib4\\Crypt\\Common\\PrivateKey' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Common/PrivateKey.php',
    'phpseclib4\\Crypt\\Common\\PublicKey' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Common/PublicKey.php',
    'phpseclib4\\Crypt\\Common\\StreamCipher' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Common/StreamCipher.php',
    'phpseclib4\\Crypt\\Common\\SymmetricKey' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Common/SymmetricKey.php',
    'phpseclib4\\Crypt\\Common\\Traits\\ASN1AlgorithmIdentifier' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Common/Traits/ASN1AlgorithmIdentifier.php',
    'phpseclib4\\Crypt\\Common\\Traits\\Fingerprint' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Common/Traits/Fingerprint.php',
    'phpseclib4\\Crypt\\Common\\Traits\\PKCS12Helper' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Common/Traits/PKCS12Helper.php',
    'phpseclib4\\Crypt\\Common\\Traits\\PasswordProtected' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Common/Traits/PasswordProtected.php',
    'phpseclib4\\Crypt\\DES' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/DES.php',
    'phpseclib4\\Crypt\\DH' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/DH.php',
    'phpseclib4\\Crypt\\DH\\Formats\\Keys\\PKCS1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/DH/Formats/Keys/PKCS1.php',
    'phpseclib4\\Crypt\\DH\\Formats\\Keys\\PKCS8' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/DH/Formats/Keys/PKCS8.php',
    'phpseclib4\\Crypt\\DH\\Parameters' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/DH/Parameters.php',
    'phpseclib4\\Crypt\\DH\\PrivateKey' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/DH/PrivateKey.php',
    'phpseclib4\\Crypt\\DH\\PublicKey' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/DH/PublicKey.php',
    'phpseclib4\\Crypt\\DSA' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/DSA.php',
    'phpseclib4\\Crypt\\DSA\\Formats\\Keys\\OpenSSH' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/OpenSSH.php',
    'phpseclib4\\Crypt\\DSA\\Formats\\Keys\\PKCS1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/PKCS1.php',
    'phpseclib4\\Crypt\\DSA\\Formats\\Keys\\PKCS8' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/PKCS8.php',
    'phpseclib4\\Crypt\\DSA\\Formats\\Keys\\PuTTY' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/PuTTY.php',
    'phpseclib4\\Crypt\\DSA\\Formats\\Keys\\Raw' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/Raw.php',
    'phpseclib4\\Crypt\\DSA\\Formats\\Keys\\XML' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/XML.php',
    'phpseclib4\\Crypt\\DSA\\Formats\\Signature\\ASN1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Signature/ASN1.php',
    'phpseclib4\\Crypt\\DSA\\Formats\\Signature\\Raw' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Signature/Raw.php',
    'phpseclib4\\Crypt\\DSA\\Formats\\Signature\\SSH2' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Signature/SSH2.php',
    'phpseclib4\\Crypt\\DSA\\Parameters' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/DSA/Parameters.php',
    'phpseclib4\\Crypt\\DSA\\PrivateKey' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/DSA/PrivateKey.php',
    'phpseclib4\\Crypt\\DSA\\PublicKey' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/DSA/PublicKey.php',
    'phpseclib4\\Crypt\\EC' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC.php',
    'phpseclib4\\Crypt\\EC\\BaseCurves\\Base' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Base.php',
    'phpseclib4\\Crypt\\EC\\BaseCurves\\Binary' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Binary.php',
    'phpseclib4\\Crypt\\EC\\BaseCurves\\KoblitzPrime' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/KoblitzPrime.php',
    'phpseclib4\\Crypt\\EC\\BaseCurves\\Montgomery' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Montgomery.php',
    'phpseclib4\\Crypt\\EC\\BaseCurves\\Prime' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Prime.php',
    'phpseclib4\\Crypt\\EC\\BaseCurves\\TwistedEdwards' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/TwistedEdwards.php',
    'phpseclib4\\Crypt\\EC\\Curves\\Curve25519' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Curve25519.php',
    'phpseclib4\\Crypt\\EC\\Curves\\Curve448' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Curve448.php',
    'phpseclib4\\Crypt\\EC\\Curves\\Ed25519' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Ed25519.php',
    'phpseclib4\\Crypt\\EC\\Curves\\Ed448' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Ed448.php',
    'phpseclib4\\Crypt\\EC\\Curves\\brainpoolP160r1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP160r1.php',
    'phpseclib4\\Crypt\\EC\\Curves\\brainpoolP160t1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP160t1.php',
    'phpseclib4\\Crypt\\EC\\Curves\\brainpoolP192r1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP192r1.php',
    'phpseclib4\\Crypt\\EC\\Curves\\brainpoolP192t1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP192t1.php',
    'phpseclib4\\Crypt\\EC\\Curves\\brainpoolP224r1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP224r1.php',
    'phpseclib4\\Crypt\\EC\\Curves\\brainpoolP224t1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP224t1.php',
    'phpseclib4\\Crypt\\EC\\Curves\\brainpoolP256r1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP256r1.php',
    'phpseclib4\\Crypt\\EC\\Curves\\brainpoolP256t1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP256t1.php',
    'phpseclib4\\Crypt\\EC\\Curves\\brainpoolP320r1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP320r1.php',
    'phpseclib4\\Crypt\\EC\\Curves\\brainpoolP320t1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP320t1.php',
    'phpseclib4\\Crypt\\EC\\Curves\\brainpoolP384r1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP384r1.php',
    'phpseclib4\\Crypt\\EC\\Curves\\brainpoolP384t1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP384t1.php',
    'phpseclib4\\Crypt\\EC\\Curves\\brainpoolP512r1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP512r1.php',
    'phpseclib4\\Crypt\\EC\\Curves\\brainpoolP512t1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP512t1.php',
    'phpseclib4\\Crypt\\EC\\Curves\\nistb233' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistb233.php',
    'phpseclib4\\Crypt\\EC\\Curves\\nistb409' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistb409.php',
    'phpseclib4\\Crypt\\EC\\Curves\\nistk163' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk163.php',
    'phpseclib4\\Crypt\\EC\\Curves\\nistk233' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk233.php',
    'phpseclib4\\Crypt\\EC\\Curves\\nistk283' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk283.php',
    'phpseclib4\\Crypt\\EC\\Curves\\nistk409' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk409.php',
    'phpseclib4\\Crypt\\EC\\Curves\\nistp192' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp192.php',
    'phpseclib4\\Crypt\\EC\\Curves\\nistp224' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp224.php',
    'phpseclib4\\Crypt\\EC\\Curves\\nistp256' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp256.php',
    'phpseclib4\\Crypt\\EC\\Curves\\nistp384' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp384.php',
    'phpseclib4\\Crypt\\EC\\Curves\\nistp521' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp521.php',
    'phpseclib4\\Crypt\\EC\\Curves\\nistt571' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistt571.php',
    'phpseclib4\\Crypt\\EC\\Curves\\prime192v1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime192v1.php',
    'phpseclib4\\Crypt\\EC\\Curves\\prime192v2' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime192v2.php',
    'phpseclib4\\Crypt\\EC\\Curves\\prime192v3' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime192v3.php',
    'phpseclib4\\Crypt\\EC\\Curves\\prime239v1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime239v1.php',
    'phpseclib4\\Crypt\\EC\\Curves\\prime239v2' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime239v2.php',
    'phpseclib4\\Crypt\\EC\\Curves\\prime239v3' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime239v3.php',
    'phpseclib4\\Crypt\\EC\\Curves\\prime256v1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime256v1.php',
    'phpseclib4\\Crypt\\EC\\Curves\\secp112r1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp112r1.php',
    'phpseclib4\\Crypt\\EC\\Curves\\secp112r2' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp112r2.php',
    'phpseclib4\\Crypt\\EC\\Curves\\secp128r1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp128r1.php',
    'phpseclib4\\Crypt\\EC\\Curves\\secp128r2' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp128r2.php',
    'phpseclib4\\Crypt\\EC\\Curves\\secp160k1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp160k1.php',
    'phpseclib4\\Crypt\\EC\\Curves\\secp160r1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp160r1.php',
    'phpseclib4\\Crypt\\EC\\Curves\\secp160r2' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp160r2.php',
    'phpseclib4\\Crypt\\EC\\Curves\\secp192k1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp192k1.php',
    'phpseclib4\\Crypt\\EC\\Curves\\secp192r1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp192r1.php',
    'phpseclib4\\Crypt\\EC\\Curves\\secp224k1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp224k1.php',
    'phpseclib4\\Crypt\\EC\\Curves\\secp224r1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp224r1.php',
    'phpseclib4\\Crypt\\EC\\Curves\\secp256k1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp256k1.php',
    'phpseclib4\\Crypt\\EC\\Curves\\secp256r1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp256r1.php',
    'phpseclib4\\Crypt\\EC\\Curves\\secp384r1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp384r1.php',
    'phpseclib4\\Crypt\\EC\\Curves\\secp521r1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp521r1.php',
    'phpseclib4\\Crypt\\EC\\Curves\\sect113r1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect113r1.php',
    'phpseclib4\\Crypt\\EC\\Curves\\sect113r2' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect113r2.php',
    'phpseclib4\\Crypt\\EC\\Curves\\sect131r1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect131r1.php',
    'phpseclib4\\Crypt\\EC\\Curves\\sect131r2' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect131r2.php',
    'phpseclib4\\Crypt\\EC\\Curves\\sect163k1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect163k1.php',
    'phpseclib4\\Crypt\\EC\\Curves\\sect163r1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect163r1.php',
    'phpseclib4\\Crypt\\EC\\Curves\\sect163r2' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect163r2.php',
    'phpseclib4\\Crypt\\EC\\Curves\\sect193r1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect193r1.php',
    'phpseclib4\\Crypt\\EC\\Curves\\sect193r2' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect193r2.php',
    'phpseclib4\\Crypt\\EC\\Curves\\sect233k1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect233k1.php',
    'phpseclib4\\Crypt\\EC\\Curves\\sect233r1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect233r1.php',
    'phpseclib4\\Crypt\\EC\\Curves\\sect239k1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect239k1.php',
    'phpseclib4\\Crypt\\EC\\Curves\\sect283k1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect283k1.php',
    'phpseclib4\\Crypt\\EC\\Curves\\sect283r1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect283r1.php',
    'phpseclib4\\Crypt\\EC\\Curves\\sect409k1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect409k1.php',
    'phpseclib4\\Crypt\\EC\\Curves\\sect409r1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect409r1.php',
    'phpseclib4\\Crypt\\EC\\Curves\\sect571k1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect571k1.php',
    'phpseclib4\\Crypt\\EC\\Curves\\sect571r1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect571r1.php',
    'phpseclib4\\Crypt\\EC\\Formats\\Keys\\Common' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/Common.php',
    'phpseclib4\\Crypt\\EC\\Formats\\Keys\\JWK' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/JWK.php',
    'phpseclib4\\Crypt\\EC\\Formats\\Keys\\MontgomeryPrivate' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/MontgomeryPrivate.php',
    'phpseclib4\\Crypt\\EC\\Formats\\Keys\\MontgomeryPublic' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/MontgomeryPublic.php',
    'phpseclib4\\Crypt\\EC\\Formats\\Keys\\OpenSSH' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/OpenSSH.php',
    'phpseclib4\\Crypt\\EC\\Formats\\Keys\\PKCS1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/PKCS1.php',
    'phpseclib4\\Crypt\\EC\\Formats\\Keys\\PKCS8' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/PKCS8.php',
    'phpseclib4\\Crypt\\EC\\Formats\\Keys\\PuTTY' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/PuTTY.php',
    'phpseclib4\\Crypt\\EC\\Formats\\Keys\\XML' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/XML.php',
    'phpseclib4\\Crypt\\EC\\Formats\\Keys\\libsodium' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/libsodium.php',
    'phpseclib4\\Crypt\\EC\\Formats\\Signature\\ASN1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/ASN1.php',
    'phpseclib4\\Crypt\\EC\\Formats\\Signature\\IEEE' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/IEEE.php',
    'phpseclib4\\Crypt\\EC\\Formats\\Signature\\Raw' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/Raw.php',
    'phpseclib4\\Crypt\\EC\\Formats\\Signature\\SSH2' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/SSH2.php',
    'phpseclib4\\Crypt\\EC\\Parameters' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Parameters.php',
    'phpseclib4\\Crypt\\EC\\PrivateKey' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/PrivateKey.php',
    'phpseclib4\\Crypt\\EC\\PublicKey' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/PublicKey.php',
    'phpseclib4\\Crypt\\Hash' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Hash.php',
    'phpseclib4\\Crypt\\PublicKeyLoader' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/PublicKeyLoader.php',
    'phpseclib4\\Crypt\\RC2' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RC2.php',
    'phpseclib4\\Crypt\\RC4' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RC4.php',
    'phpseclib4\\Crypt\\RSA' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RSA.php',
    'phpseclib4\\Crypt\\RSA\\Formats\\Keys\\JWK' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/JWK.php',
    'phpseclib4\\Crypt\\RSA\\Formats\\Keys\\MSBLOB' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/MSBLOB.php',
    'phpseclib4\\Crypt\\RSA\\Formats\\Keys\\OpenSSH' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/OpenSSH.php',
    'phpseclib4\\Crypt\\RSA\\Formats\\Keys\\PKCS1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PKCS1.php',
    'phpseclib4\\Crypt\\RSA\\Formats\\Keys\\PKCS8' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PKCS8.php',
    'phpseclib4\\Crypt\\RSA\\Formats\\Keys\\PSS' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PSS.php',
    'phpseclib4\\Crypt\\RSA\\Formats\\Keys\\PuTTY' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PuTTY.php',
    'phpseclib4\\Crypt\\RSA\\Formats\\Keys\\Raw' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/Raw.php',
    'phpseclib4\\Crypt\\RSA\\Formats\\Keys\\XML' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/XML.php',
    'phpseclib4\\Crypt\\RSA\\PrivateKey' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/PrivateKey.php',
    'phpseclib4\\Crypt\\RSA\\PublicKey' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/PublicKey.php',
    'phpseclib4\\Crypt\\Rijndael' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.php',
    'phpseclib4\\Crypt\\Salsa20' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Salsa20.php',
    'phpseclib4\\Crypt\\TripleDES' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/TripleDES.php',
    'phpseclib4\\Crypt\\Twofish' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Twofish.php',
    'phpseclib4\\Exception\\BadConfigurationException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/BadConfigurationException.php',
    'phpseclib4\\Exception\\BadDecryptionException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/BadDecryptionException.php',
    'phpseclib4\\Exception\\BadMethodCallException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/BadMethodCallException.php',
    'phpseclib4\\Exception\\BaseException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/BaseException.php',
    'phpseclib4\\Exception\\CharacterConversionException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/CharacterConversionException.php',
    'phpseclib4\\Exception\\ConnectionClosedException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/ConnectionClosedException.php',
    'phpseclib4\\Exception\\EOCException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/EOCException.php',
    'phpseclib4\\Exception\\EncodedDataUnavailableException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/EncodedDataUnavailableException.php',
    'phpseclib4\\Exception\\ExcessivelyDeepDataException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/ExcessivelyDeepDataException.php',
    'phpseclib4\\Exception\\FileSystemException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/FileSystemException.php',
    'phpseclib4\\Exception\\InvalidArgumentException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/InvalidArgumentException.php',
    'phpseclib4\\Exception\\InvalidModeException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/InvalidModeException.php',
    'phpseclib4\\Exception\\InvalidPacketLengthException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/InvalidPacketLengthException.php',
    'phpseclib4\\Exception\\InvalidStateException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/InvalidStateException.php',
    'phpseclib4\\Exception\\KeyConstraintException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/KeyConstraintException.php',
    'phpseclib4\\Exception\\LengthException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/LengthException.php',
    'phpseclib4\\Exception\\NoKeyLoadedException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/NoKeyLoadedException.php',
    'phpseclib4\\Exception\\NoSupportedAlgorithmsException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/NoSupportedAlgorithmsException.php',
    'phpseclib4\\Exception\\PasswordNeededException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/PasswordNeededException.php',
    'phpseclib4\\Exception\\ResourceLimitException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/ResourceLimitException.php',
    'phpseclib4\\Exception\\SSHChannelExitSignalException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/SSHChannelExitSignalException.php',
    'phpseclib4\\Exception\\ServiceUnavailableException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/ServiceUnavailableException.php',
    'phpseclib4\\Exception\\TimeoutException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/TimeoutException.php',
    'phpseclib4\\Exception\\UnexpectedSFTPPacketException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/UnexpectedSFTPPacketException.php',
    'phpseclib4\\Exception\\UnexpectedSSHMessageException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/UnexpectedSSHMessageException.php',
    'phpseclib4\\Exception\\UnexpectedValueException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/UnexpectedValueException.php',
    'phpseclib4\\Exception\\UnsupportedAlgorithmException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/UnsupportedAlgorithmException.php',
    'phpseclib4\\Exception\\UnsupportedCurveException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/UnsupportedCurveException.php',
    'phpseclib4\\Exception\\UnsupportedValueException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/UnsupportedValueException.php',
    'phpseclib4\\File\\ANSI' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ANSI.php',
    'phpseclib4\\File\\ASN1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1.php',
    'phpseclib4\\File\\ASN1\\Constructed' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Constructed.php',
    'phpseclib4\\File\\ASN1\\Element' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Element.php',
    'phpseclib4\\File\\ASN1\\ExcessivelyDeepData' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/ExcessivelyDeepData.php',
    'phpseclib4\\File\\ASN1\\MalformedData' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/MalformedData.php',
    'phpseclib4\\File\\ASN1\\Maps\\AccessDescription' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AccessDescription.php',
    'phpseclib4\\File\\ASN1\\Maps\\Accuracy' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Accuracy.php',
    'phpseclib4\\File\\ASN1\\Maps\\AdministrationDomainName' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AdministrationDomainName.php',
    'phpseclib4\\File\\ASN1\\Maps\\AlgorithmIdentifier' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AlgorithmIdentifier.php',
    'phpseclib4\\File\\ASN1\\Maps\\AnotherName' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AnotherName.php',
    'phpseclib4\\File\\ASN1\\Maps\\AttCertIssuer' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttCertIssuer.php',
    'phpseclib4\\File\\ASN1\\Maps\\AttCertValidityPeriod' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttCertValidityPeriod.php',
    'phpseclib4\\File\\ASN1\\Maps\\AttCertVersion' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttCertVersion.php',
    'phpseclib4\\File\\ASN1\\Maps\\AttCertVersionV1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttCertVersionV1.php',
    'phpseclib4\\File\\ASN1\\Maps\\Attribute' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Attribute.php',
    'phpseclib4\\File\\ASN1\\Maps\\AttributeCertificateInfo' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeCertificateInfo.php',
    'phpseclib4\\File\\ASN1\\Maps\\AttributeCertificateInfoV1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeCertificateInfoV1.php',
    'phpseclib4\\File\\ASN1\\Maps\\AttributeCertificateV1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeCertificateV1.php',
    'phpseclib4\\File\\ASN1\\Maps\\AttributeCertificateV2' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeCertificateV2.php',
    'phpseclib4\\File\\ASN1\\Maps\\AttributeType' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeType.php',
    'phpseclib4\\File\\ASN1\\Maps\\AttributeTypeAndValue' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeTypeAndValue.php',
    'phpseclib4\\File\\ASN1\\Maps\\AttributeValue' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeValue.php',
    'phpseclib4\\File\\ASN1\\Maps\\Attributes' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Attributes.php',
    'phpseclib4\\File\\ASN1\\Maps\\AuthAttributes' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AuthAttributes.php',
    'phpseclib4\\File\\ASN1\\Maps\\AuthEnvelopedData' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AuthEnvelopedData.php',
    'phpseclib4\\File\\ASN1\\Maps\\AuthenticatedSafe' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AuthenticatedSafe.php',
    'phpseclib4\\File\\ASN1\\Maps\\AuthorityInfoAccessSyntax' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AuthorityInfoAccessSyntax.php',
    'phpseclib4\\File\\ASN1\\Maps\\AuthorityKeyIdentifier' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AuthorityKeyIdentifier.php',
    'phpseclib4\\File\\ASN1\\Maps\\BaseDistance' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BaseDistance.php',
    'phpseclib4\\File\\ASN1\\Maps\\BasicConstraints' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BasicConstraints.php',
    'phpseclib4\\File\\ASN1\\Maps\\BuiltInDomainDefinedAttribute' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BuiltInDomainDefinedAttribute.php',
    'phpseclib4\\File\\ASN1\\Maps\\BuiltInDomainDefinedAttributes' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BuiltInDomainDefinedAttributes.php',
    'phpseclib4\\File\\ASN1\\Maps\\BuiltInStandardAttributes' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BuiltInStandardAttributes.php',
    'phpseclib4\\File\\ASN1\\Maps\\CMSAlgorithmProtection' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CMSAlgorithmProtection.php',
    'phpseclib4\\File\\ASN1\\Maps\\CMSEncryptedData' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CMSEncryptedData.php',
    'phpseclib4\\File\\ASN1\\Maps\\CMSVersion' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CMSVersion.php',
    'phpseclib4\\File\\ASN1\\Maps\\CPSuri' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CPSuri.php',
    'phpseclib4\\File\\ASN1\\Maps\\CRLDistributionPoints' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CRLDistributionPoints.php',
    'phpseclib4\\File\\ASN1\\Maps\\CRLNumber' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CRLNumber.php',
    'phpseclib4\\File\\ASN1\\Maps\\CRLReason' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CRLReason.php',
    'phpseclib4\\File\\ASN1\\Maps\\CertBag' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertBag.php',
    'phpseclib4\\File\\ASN1\\Maps\\CertPolicyId' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertPolicyId.php',
    'phpseclib4\\File\\ASN1\\Maps\\Certificate' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Certificate.php',
    'phpseclib4\\File\\ASN1\\Maps\\CertificateChoices' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificateChoices.php',
    'phpseclib4\\File\\ASN1\\Maps\\CertificateIssuer' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificateIssuer.php',
    'phpseclib4\\File\\ASN1\\Maps\\CertificateList' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificateList.php',
    'phpseclib4\\File\\ASN1\\Maps\\CertificatePolicies' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificatePolicies.php',
    'phpseclib4\\File\\ASN1\\Maps\\CertificateSerialNumber' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificateSerialNumber.php',
    'phpseclib4\\File\\ASN1\\Maps\\CertificateSet' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificateSet.php',
    'phpseclib4\\File\\ASN1\\Maps\\CertificationRequest' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificationRequest.php',
    'phpseclib4\\File\\ASN1\\Maps\\CertificationRequestInfo' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificationRequestInfo.php',
    'phpseclib4\\File\\ASN1\\Maps\\Characteristic_two' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Characteristic_two.php',
    'phpseclib4\\File\\ASN1\\Maps\\CompressedData' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CompressedData.php',
    'phpseclib4\\File\\ASN1\\Maps\\CompressionAlgorithmIdentifier' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CompressionAlgorithmIdentifier.php',
    'phpseclib4\\File\\ASN1\\Maps\\CompressionAlgorithmIdentifiers' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CompressionAlgorithmIdentifiers.php',
    'phpseclib4\\File\\ASN1\\Maps\\ContentEncryptionAlgorithmIdentifier' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ContentEncryptionAlgorithmIdentifier.php',
    'phpseclib4\\File\\ASN1\\Maps\\ContentInfo' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ContentInfo.php',
    'phpseclib4\\File\\ASN1\\Maps\\ContentType' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ContentType.php',
    'phpseclib4\\File\\ASN1\\Maps\\CountryName' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CountryName.php',
    'phpseclib4\\File\\ASN1\\Maps\\Curve' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Curve.php',
    'phpseclib4\\File\\ASN1\\Maps\\DHParameter' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DHParameter.php',
    'phpseclib4\\File\\ASN1\\Maps\\DSAParams' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DSAParams.php',
    'phpseclib4\\File\\ASN1\\Maps\\DSAPrivateKey' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DSAPrivateKey.php',
    'phpseclib4\\File\\ASN1\\Maps\\DSAPublicKey' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DSAPublicKey.php',
    'phpseclib4\\File\\ASN1\\Maps\\Digest' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Digest.php',
    'phpseclib4\\File\\ASN1\\Maps\\DigestAlgorithmIdentifier' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DigestAlgorithmIdentifier.php',
    'phpseclib4\\File\\ASN1\\Maps\\DigestAlgorithmIdentifiers' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DigestAlgorithmIdentifiers.php',
    'phpseclib4\\File\\ASN1\\Maps\\DigestInfo' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DigestInfo.php',
    'phpseclib4\\File\\ASN1\\Maps\\DigestedData' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DigestedData.php',
    'phpseclib4\\File\\ASN1\\Maps\\DirectoryString' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DirectoryString.php',
    'phpseclib4\\File\\ASN1\\Maps\\DisplayText' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DisplayText.php',
    'phpseclib4\\File\\ASN1\\Maps\\DistributionPoint' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DistributionPoint.php',
    'phpseclib4\\File\\ASN1\\Maps\\DistributionPointName' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DistributionPointName.php',
    'phpseclib4\\File\\ASN1\\Maps\\DssSigValue' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DssSigValue.php',
    'phpseclib4\\File\\ASN1\\Maps\\ECCCMSSharedInfo' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ECCCMSSharedInfo.php',
    'phpseclib4\\File\\ASN1\\Maps\\ECParameters' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ECParameters.php',
    'phpseclib4\\File\\ASN1\\Maps\\ECPoint' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ECPoint.php',
    'phpseclib4\\File\\ASN1\\Maps\\ECPrivateKey' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ECPrivateKey.php',
    'phpseclib4\\File\\ASN1\\Maps\\EDIPartyName' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EDIPartyName.php',
    'phpseclib4\\File\\ASN1\\Maps\\ESSCertID' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ESSCertID.php',
    'phpseclib4\\File\\ASN1\\Maps\\ESSCertIDv2' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ESSCertIDv2.php',
    'phpseclib4\\File\\ASN1\\Maps\\EcdsaSigValue' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EcdsaSigValue.php',
    'phpseclib4\\File\\ASN1\\Maps\\EncapsulatedContentInfo' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EncapsulatedContentInfo.php',
    'phpseclib4\\File\\ASN1\\Maps\\EncryptedContent' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EncryptedContent.php',
    'phpseclib4\\File\\ASN1\\Maps\\EncryptedContentInfo' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EncryptedContentInfo.php',
    'phpseclib4\\File\\ASN1\\Maps\\EncryptedData' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EncryptedData.php',
    'phpseclib4\\File\\ASN1\\Maps\\EncryptedDataCMS' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EncryptedDataCMS.php',
    'phpseclib4\\File\\ASN1\\Maps\\EncryptedKey' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EncryptedKey.php',
    'phpseclib4\\File\\ASN1\\Maps\\EncryptedPrivateKeyInfo' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EncryptedPrivateKeyInfo.php',
    'phpseclib4\\File\\ASN1\\Maps\\EnvelopedData' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EnvelopedData.php',
    'phpseclib4\\File\\ASN1\\Maps\\ExtKeyUsageSyntax' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ExtKeyUsageSyntax.php',
    'phpseclib4\\File\\ASN1\\Maps\\ExtendedCertificate' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ExtendedCertificate.php',
    'phpseclib4\\File\\ASN1\\Maps\\ExtendedCertificateInfo' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ExtendedCertificateInfo.php',
    'phpseclib4\\File\\ASN1\\Maps\\Extension' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Extension.php',
    'phpseclib4\\File\\ASN1\\Maps\\ExtensionAttribute' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ExtensionAttribute.php',
    'phpseclib4\\File\\ASN1\\Maps\\ExtensionAttributes' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ExtensionAttributes.php',
    'phpseclib4\\File\\ASN1\\Maps\\Extensions' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Extensions.php',
    'phpseclib4\\File\\ASN1\\Maps\\FieldElement' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/FieldElement.php',
    'phpseclib4\\File\\ASN1\\Maps\\FieldID' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/FieldID.php',
    'phpseclib4\\File\\ASN1\\Maps\\GeneralName' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralName.php',
    'phpseclib4\\File\\ASN1\\Maps\\GeneralNames' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralNames.php',
    'phpseclib4\\File\\ASN1\\Maps\\GeneralSubtree' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralSubtree.php',
    'phpseclib4\\File\\ASN1\\Maps\\GeneralSubtrees' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralSubtrees.php',
    'phpseclib4\\File\\ASN1\\Maps\\Hash' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Hash.php',
    'phpseclib4\\File\\ASN1\\Maps\\HashAlgAndValue' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/HashAlgAndValue.php',
    'phpseclib4\\File\\ASN1\\Maps\\HashAlgorithm' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/HashAlgorithm.php',
    'phpseclib4\\File\\ASN1\\Maps\\HoldInstructionCode' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/HoldInstructionCode.php',
    'phpseclib4\\File\\ASN1\\Maps\\Holder' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Holder.php',
    'phpseclib4\\File\\ASN1\\Maps\\InvalidityDate' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/InvalidityDate.php',
    'phpseclib4\\File\\ASN1\\Maps\\Iso4217CurrencyCode' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Iso4217CurrencyCode.php',
    'phpseclib4\\File\\ASN1\\Maps\\IssuerAltName' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/IssuerAltName.php',
    'phpseclib4\\File\\ASN1\\Maps\\IssuerAndSerialNumber' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/IssuerAndSerialNumber.php',
    'phpseclib4\\File\\ASN1\\Maps\\IssuerSerial' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/IssuerSerial.php',
    'phpseclib4\\File\\ASN1\\Maps\\IssuingDistributionPoint' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/IssuingDistributionPoint.php',
    'phpseclib4\\File\\ASN1\\Maps\\KEKIdentifier' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KEKIdentifier.php',
    'phpseclib4\\File\\ASN1\\Maps\\KEKRecipientInfo' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KEKRecipientInfo.php',
    'phpseclib4\\File\\ASN1\\Maps\\KeyAgreeRecipientIdentifier' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyAgreeRecipientIdentifier.php',
    'phpseclib4\\File\\ASN1\\Maps\\KeyAgreeRecipientInfo' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyAgreeRecipientInfo.php',
    'phpseclib4\\File\\ASN1\\Maps\\KeyDerivationAlgorithmIdentifier' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyDerivationAlgorithmIdentifier.php',
    'phpseclib4\\File\\ASN1\\Maps\\KeyEncryptionAlgorithmIdentifier' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyEncryptionAlgorithmIdentifier.php',
    'phpseclib4\\File\\ASN1\\Maps\\KeyIdentifier' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyIdentifier.php',
    'phpseclib4\\File\\ASN1\\Maps\\KeyPurposeId' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyPurposeId.php',
    'phpseclib4\\File\\ASN1\\Maps\\KeyTransRecipientInfo' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyTransRecipientInfo.php',
    'phpseclib4\\File\\ASN1\\Maps\\KeyUsage' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyUsage.php',
    'phpseclib4\\File\\ASN1\\Maps\\LogotypeAudio' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/LogotypeAudio.php',
    'phpseclib4\\File\\ASN1\\Maps\\LogotypeAudioInfo' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/LogotypeAudioInfo.php',
    'phpseclib4\\File\\ASN1\\Maps\\LogotypeData' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/LogotypeData.php',
    'phpseclib4\\File\\ASN1\\Maps\\LogotypeDetails' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/LogotypeDetails.php',
    'phpseclib4\\File\\ASN1\\Maps\\LogotypeExtn' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/LogotypeExtn.php',
    'phpseclib4\\File\\ASN1\\Maps\\LogotypeImage' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/LogotypeImage.php',
    'phpseclib4\\File\\ASN1\\Maps\\LogotypeImageInfo' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/LogotypeImageInfo.php',
    'phpseclib4\\File\\ASN1\\Maps\\LogotypeImageResolution' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/LogotypeImageResolution.php',
    'phpseclib4\\File\\ASN1\\Maps\\LogotypeImageType' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/LogotypeImageType.php',
    'phpseclib4\\File\\ASN1\\Maps\\LogotypeInfo' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/LogotypeInfo.php',
    'phpseclib4\\File\\ASN1\\Maps\\LogotypeReference' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/LogotypeReference.php',
    'phpseclib4\\File\\ASN1\\Maps\\MQVuserKeyingMaterial' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/MQVuserKeyingMaterial.php',
    'phpseclib4\\File\\ASN1\\Maps\\MacData' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/MacData.php',
    'phpseclib4\\File\\ASN1\\Maps\\MaskGenAlgorithm' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/MaskGenAlgorithm.php',
    'phpseclib4\\File\\ASN1\\Maps\\MessageAuthenticationCode' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/MessageAuthenticationCode.php',
    'phpseclib4\\File\\ASN1\\Maps\\MessageAuthenticationCodeAlgorithm' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/MessageAuthenticationCodeAlgorithm.php',
    'phpseclib4\\File\\ASN1\\Maps\\MessageImprint' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/MessageImprint.php',
    'phpseclib4\\File\\ASN1\\Maps\\MonetaryValue' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/MonetaryValue.php',
    'phpseclib4\\File\\ASN1\\Maps\\Name' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Name.php',
    'phpseclib4\\File\\ASN1\\Maps\\NameConstraints' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NameConstraints.php',
    'phpseclib4\\File\\ASN1\\Maps\\NetworkAddress' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NetworkAddress.php',
    'phpseclib4\\File\\ASN1\\Maps\\NoticeReference' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NoticeReference.php',
    'phpseclib4\\File\\ASN1\\Maps\\NumericUserIdentifier' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NumericUserIdentifier.php',
    'phpseclib4\\File\\ASN1\\Maps\\ORAddress' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ORAddress.php',
    'phpseclib4\\File\\ASN1\\Maps\\ObjectDigestInfo' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ObjectDigestInfo.php',
    'phpseclib4\\File\\ASN1\\Maps\\OneAsymmetricKey' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OneAsymmetricKey.php',
    'phpseclib4\\File\\ASN1\\Maps\\OrganizationName' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OrganizationName.php',
    'phpseclib4\\File\\ASN1\\Maps\\OrganizationalUnitNames' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OrganizationalUnitNames.php',
    'phpseclib4\\File\\ASN1\\Maps\\OriginatorIdentifierOrKey' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OriginatorIdentifierOrKey.php',
    'phpseclib4\\File\\ASN1\\Maps\\OriginatorInfo' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OriginatorInfo.php',
    'phpseclib4\\File\\ASN1\\Maps\\OriginatorPublicKey' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OriginatorPublicKey.php',
    'phpseclib4\\File\\ASN1\\Maps\\OtherCertificateFormat' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OtherCertificateFormat.php',
    'phpseclib4\\File\\ASN1\\Maps\\OtherKeyAttribute' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OtherKeyAttribute.php',
    'phpseclib4\\File\\ASN1\\Maps\\OtherLogotypeInfo' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OtherLogotypeInfo.php',
    'phpseclib4\\File\\ASN1\\Maps\\OtherPrimeInfo' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OtherPrimeInfo.php',
    'phpseclib4\\File\\ASN1\\Maps\\OtherPrimeInfos' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OtherPrimeInfos.php',
    'phpseclib4\\File\\ASN1\\Maps\\OtherRecipientInfo' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OtherRecipientInfo.php',
    'phpseclib4\\File\\ASN1\\Maps\\OtherRevocationInfoFormat' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OtherRevocationInfoFormat.php',
    'phpseclib4\\File\\ASN1\\Maps\\PBEParameter' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBEParameter.php',
    'phpseclib4\\File\\ASN1\\Maps\\PBES2params' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBES2params.php',
    'phpseclib4\\File\\ASN1\\Maps\\PBKDF2params' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBKDF2params.php',
    'phpseclib4\\File\\ASN1\\Maps\\PBMAC1params' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBMAC1params.php',
    'phpseclib4\\File\\ASN1\\Maps\\PFX' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PFX.php',
    'phpseclib4\\File\\ASN1\\Maps\\PKCS8ShroudedKeyBag' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PKCS8ShroudedKeyBag.php',
    'phpseclib4\\File\\ASN1\\Maps\\PKCS9String' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PKCS9String.php',
    'phpseclib4\\File\\ASN1\\Maps\\PSourceAlgorithm' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PSourceAlgorithm.php',
    'phpseclib4\\File\\ASN1\\Maps\\PasswordRecipientInfo' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PasswordRecipientInfo.php',
    'phpseclib4\\File\\ASN1\\Maps\\Pentanomial' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Pentanomial.php',
    'phpseclib4\\File\\ASN1\\Maps\\PersonalName' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PersonalName.php',
    'phpseclib4\\File\\ASN1\\Maps\\PolicyInformation' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyInformation.php',
    'phpseclib4\\File\\ASN1\\Maps\\PolicyMappings' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyMappings.php',
    'phpseclib4\\File\\ASN1\\Maps\\PolicyQualifierId' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyQualifierId.php',
    'phpseclib4\\File\\ASN1\\Maps\\PolicyQualifierInfo' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyQualifierInfo.php',
    'phpseclib4\\File\\ASN1\\Maps\\PostalAddress' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PostalAddress.php',
    'phpseclib4\\File\\ASN1\\Maps\\Prime_p' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Prime_p.php',
    'phpseclib4\\File\\ASN1\\Maps\\PrivateDomainName' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateDomainName.php',
    'phpseclib4\\File\\ASN1\\Maps\\PrivateKey' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateKey.php',
    'phpseclib4\\File\\ASN1\\Maps\\PrivateKeyInfo' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateKeyInfo.php',
    'phpseclib4\\File\\ASN1\\Maps\\PrivateKeyUsagePeriod' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateKeyUsagePeriod.php',
    'phpseclib4\\File\\ASN1\\Maps\\PublicKey' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PublicKey.php',
    'phpseclib4\\File\\ASN1\\Maps\\PublicKeyAndChallenge' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PublicKeyAndChallenge.php',
    'phpseclib4\\File\\ASN1\\Maps\\PublicKeyInfo' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PublicKeyInfo.php',
    'phpseclib4\\File\\ASN1\\Maps\\QCStatement' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/QCStatement.php',
    'phpseclib4\\File\\ASN1\\Maps\\QCStatements' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/QCStatements.php',
    'phpseclib4\\File\\ASN1\\Maps\\QcEuLimitValue' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/QcEuLimitValue.php',
    'phpseclib4\\File\\ASN1\\Maps\\RC2CBCParameter' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RC2CBCParameter.php',
    'phpseclib4\\File\\ASN1\\Maps\\RDNSequence' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RDNSequence.php',
    'phpseclib4\\File\\ASN1\\Maps\\RSAES_OAEP_params' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RSAES_OAEP_params.php',
    'phpseclib4\\File\\ASN1\\Maps\\RSAPrivateKey' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RSAPrivateKey.php',
    'phpseclib4\\File\\ASN1\\Maps\\RSAPublicKey' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RSAPublicKey.php',
    'phpseclib4\\File\\ASN1\\Maps\\RSASSA_PSS_params' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RSASSA_PSS_params.php',
    'phpseclib4\\File\\ASN1\\Maps\\ReasonFlags' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ReasonFlags.php',
    'phpseclib4\\File\\ASN1\\Maps\\RecipientEncryptedKey' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RecipientEncryptedKey.php',
    'phpseclib4\\File\\ASN1\\Maps\\RecipientEncryptedKeys' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RecipientEncryptedKeys.php',
    'phpseclib4\\File\\ASN1\\Maps\\RecipientIdentifier' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RecipientIdentifier.php',
    'phpseclib4\\File\\ASN1\\Maps\\RecipientInfo' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RecipientInfo.php',
    'phpseclib4\\File\\ASN1\\Maps\\RecipientInfos' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RecipientInfos.php',
    'phpseclib4\\File\\ASN1\\Maps\\RecipientKeyIdentifier' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RecipientKeyIdentifier.php',
    'phpseclib4\\File\\ASN1\\Maps\\RelativeDistinguishedName' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RelativeDistinguishedName.php',
    'phpseclib4\\File\\ASN1\\Maps\\RevocationInfoChoice' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RevocationInfoChoice.php',
    'phpseclib4\\File\\ASN1\\Maps\\RevocationInfoChoices' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RevocationInfoChoices.php',
    'phpseclib4\\File\\ASN1\\Maps\\RevokedCertificate' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RevokedCertificate.php',
    'phpseclib4\\File\\ASN1\\Maps\\SMIMECapabilities' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SMIMECapabilities.php',
    'phpseclib4\\File\\ASN1\\Maps\\SMIMECapability' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SMIMECapability.php',
    'phpseclib4\\File\\ASN1\\Maps\\SafeBag' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SafeBag.php',
    'phpseclib4\\File\\ASN1\\Maps\\SafeContents' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SafeContents.php',
    'phpseclib4\\File\\ASN1\\Maps\\SecretBag' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SecretBag.php',
    'phpseclib4\\File\\ASN1\\Maps\\ShakeOutputLen' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ShakeOutputLen.php',
    'phpseclib4\\File\\ASN1\\Maps\\SignatureAlgorithmIdentifier' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SignatureAlgorithmIdentifier.php',
    'phpseclib4\\File\\ASN1\\Maps\\SignatureValue' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SignatureValue.php',
    'phpseclib4\\File\\ASN1\\Maps\\SignedAttributes' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SignedAttributes.php',
    'phpseclib4\\File\\ASN1\\Maps\\SignedData' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SignedData.php',
    'phpseclib4\\File\\ASN1\\Maps\\SignedPublicKeyAndChallenge' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SignedPublicKeyAndChallenge.php',
    'phpseclib4\\File\\ASN1\\Maps\\SignerIdentifier' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SignerIdentifier.php',
    'phpseclib4\\File\\ASN1\\Maps\\SignerInfo' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SignerInfo.php',
    'phpseclib4\\File\\ASN1\\Maps\\SignerInfos' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SignerInfos.php',
    'phpseclib4\\File\\ASN1\\Maps\\SigningCertificate' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SigningCertificate.php',
    'phpseclib4\\File\\ASN1\\Maps\\SigningCertificateV2' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SigningCertificateV2.php',
    'phpseclib4\\File\\ASN1\\Maps\\SpecifiedECDomain' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SpecifiedECDomain.php',
    'phpseclib4\\File\\ASN1\\Maps\\SubjectAltName' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectAltName.php',
    'phpseclib4\\File\\ASN1\\Maps\\SubjectDirectoryAttributes' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectDirectoryAttributes.php',
    'phpseclib4\\File\\ASN1\\Maps\\SubjectInfoAccessSyntax' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectInfoAccessSyntax.php',
    'phpseclib4\\File\\ASN1\\Maps\\SubjectKeyIdentifier' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectKeyIdentifier.php',
    'phpseclib4\\File\\ASN1\\Maps\\SubjectPublicKeyInfo' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectPublicKeyInfo.php',
    'phpseclib4\\File\\ASN1\\Maps\\TBSCertList' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/TBSCertList.php',
    'phpseclib4\\File\\ASN1\\Maps\\TBSCertificate' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/TBSCertificate.php',
    'phpseclib4\\File\\ASN1\\Maps\\TSAPolicyId' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/TSAPolicyId.php',
    'phpseclib4\\File\\ASN1\\Maps\\TSTInfo' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/TSTInfo.php',
    'phpseclib4\\File\\ASN1\\Maps\\TerminalIdentifier' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/TerminalIdentifier.php',
    'phpseclib4\\File\\ASN1\\Maps\\Time' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Time.php',
    'phpseclib4\\File\\ASN1\\Maps\\TimeStampToken' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/TimeStampToken.php',
    'phpseclib4\\File\\ASN1\\Maps\\Trinomial' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Trinomial.php',
    'phpseclib4\\File\\ASN1\\Maps\\UnauthAttributes' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/UnauthAttributes.php',
    'phpseclib4\\File\\ASN1\\Maps\\UniqueIdentifier' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/UniqueIdentifier.php',
    'phpseclib4\\File\\ASN1\\Maps\\UnprotectedAttributes' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/UnprotectedAttributes.php',
    'phpseclib4\\File\\ASN1\\Maps\\UnsignedAttributes' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/UnsignedAttributes.php',
    'phpseclib4\\File\\ASN1\\Maps\\UserKeyingMaterial' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/UserKeyingMaterial.php',
    'phpseclib4\\File\\ASN1\\Maps\\UserNotice' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/UserNotice.php',
    'phpseclib4\\File\\ASN1\\Maps\\V2Form' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/V2Form.php',
    'phpseclib4\\File\\ASN1\\Maps\\Validity' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Validity.php',
    'phpseclib4\\File\\ASN1\\Maps\\netscape_ca_policy_url' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/netscape_ca_policy_url.php',
    'phpseclib4\\File\\ASN1\\Maps\\netscape_cert_type' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/netscape_cert_type.php',
    'phpseclib4\\File\\ASN1\\Maps\\netscape_comment' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/netscape_comment.php',
    'phpseclib4\\File\\ASN1\\OIDs\\CMS' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/OIDs/CMS.php',
    'phpseclib4\\File\\ASN1\\OIDs\\Curves' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/OIDs/Curves.php',
    'phpseclib4\\File\\ASN1\\OIDs\\EC' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/OIDs/EC.php',
    'phpseclib4\\File\\ASN1\\OIDs\\Hashes' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/OIDs/Hashes.php',
    'phpseclib4\\File\\ASN1\\OIDs\\PFX' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/OIDs/PFX.php',
    'phpseclib4\\File\\ASN1\\OIDs\\PKCS8' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/OIDs/PKCS8.php',
    'phpseclib4\\File\\ASN1\\OIDs\\X509' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/OIDs/X509.php',
    'phpseclib4\\File\\ASN1\\Types\\BMPString' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Types/BMPString.php',
    'phpseclib4\\File\\ASN1\\Types\\BaseString' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Types/BaseString.php',
    'phpseclib4\\File\\ASN1\\Types\\BaseType' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Types/BaseType.php',
    'phpseclib4\\File\\ASN1\\Types\\BitString' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Types/BitString.php',
    'phpseclib4\\File\\ASN1\\Types\\Boolean' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Types/Boolean.php',
    'phpseclib4\\File\\ASN1\\Types\\Choice' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Types/Choice.php',
    'phpseclib4\\File\\ASN1\\Types\\Common' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Types/Common.php',
    'phpseclib4\\File\\ASN1\\Types\\ExplicitNull' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Types/ExplicitNull.php',
    'phpseclib4\\File\\ASN1\\Types\\GeneralString' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Types/GeneralString.php',
    'phpseclib4\\File\\ASN1\\Types\\GeneralizedTime' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Types/GeneralizedTime.php',
    'phpseclib4\\File\\ASN1\\Types\\GraphicString' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Types/GraphicString.php',
    'phpseclib4\\File\\ASN1\\Types\\IA5String' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Types/IA5String.php',
    'phpseclib4\\File\\ASN1\\Types\\Integer' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Types/Integer.php',
    'phpseclib4\\File\\ASN1\\Types\\NumericString' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Types/NumericString.php',
    'phpseclib4\\File\\ASN1\\Types\\OID' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Types/OID.php',
    'phpseclib4\\File\\ASN1\\Types\\OctetString' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Types/OctetString.php',
    'phpseclib4\\File\\ASN1\\Types\\PrintableString' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Types/PrintableString.php',
    'phpseclib4\\File\\ASN1\\Types\\TeletexString' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Types/TeletexString.php',
    'phpseclib4\\File\\ASN1\\Types\\UTCTime' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Types/UTCTime.php',
    'phpseclib4\\File\\ASN1\\Types\\UTF8String' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Types/UTF8String.php',
    'phpseclib4\\File\\ASN1\\Types\\UniversalString' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Types/UniversalString.php',
    'phpseclib4\\File\\ASN1\\Types\\VideotexString' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Types/VideotexString.php',
    'phpseclib4\\File\\ASN1\\Types\\VisibleString' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Types/VisibleString.php',
    'phpseclib4\\File\\CMS' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/CMS.php',
    'phpseclib4\\File\\CMS\\CompressedData' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/CMS/CompressedData.php',
    'phpseclib4\\File\\CMS\\DigestedData' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/CMS/DigestedData.php',
    'phpseclib4\\File\\CMS\\EncryptedData' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/CMS/EncryptedData.php',
    'phpseclib4\\File\\CMS\\EnvelopedData\\DerivableKey' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/CMS/EnvelopedData/DerivableKey.php',
    'phpseclib4\\File\\CMS\\EnvelopedData\\KEKRecipient' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/CMS/EnvelopedData/KEKRecipient.php',
    'phpseclib4\\File\\CMS\\EnvelopedData\\KeyAgreeRecipient' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/CMS/EnvelopedData/KeyAgreeRecipient.php',
    'phpseclib4\\File\\CMS\\EnvelopedData\\KeyAgreeRecipient\\EncryptedKey' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/CMS/EnvelopedData/KeyAgreeRecipient/EncryptedKey.php',
    'phpseclib4\\File\\CMS\\EnvelopedData\\KeyTransRecipient' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/CMS/EnvelopedData/KeyTransRecipient.php',
    'phpseclib4\\File\\CMS\\EnvelopedData\\PasswordRecipient' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/CMS/EnvelopedData/PasswordRecipient.php',
    'phpseclib4\\File\\CMS\\EnvelopedData\\Recipient' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/CMS/EnvelopedData/Recipient.php',
    'phpseclib4\\File\\CMS\\EnvelopedData\\SearchableKey' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/CMS/EnvelopedData/SearchableKey.php',
    'phpseclib4\\File\\CMS\\SignedData' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/CMS/SignedData.php',
    'phpseclib4\\File\\CMS\\SignedData\\Signer' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/CMS/SignedData/Signer.php',
    'phpseclib4\\File\\CRL' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/CRL.php',
    'phpseclib4\\File\\CSR' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/CSR.php',
    'phpseclib4\\File\\Common\\Signable' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/Common/Signable.php',
    'phpseclib4\\File\\Common\\Traits\\ASN1Signature' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/Common/Traits/ASN1Signature.php',
    'phpseclib4\\File\\Common\\Traits\\DN' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/Common/Traits/DN.php',
    'phpseclib4\\File\\Common\\Traits\\Extension' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/Common/Traits/Extension.php',
    'phpseclib4\\File\\Common\\Traits\\KeyDerivation' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/Common/Traits/KeyDerivation.php',
    'phpseclib4\\File\\PFX' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/PFX.php',
    'phpseclib4\\File\\SPKAC' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/SPKAC.php',
    'phpseclib4\\File\\X509' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/X509.php',
    'phpseclib4\\Math\\BigInteger' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger.php',
    'phpseclib4\\Math\\BigInteger\\Engines\\BCMath' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath.php',
    'phpseclib4\\Math\\BigInteger\\Engines\\BCMath64' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath64.php',
    'phpseclib4\\Math\\BigInteger\\Engines\\BCMath\\Base' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/Base.php',
    'phpseclib4\\Math\\BigInteger\\Engines\\BCMath\\BuiltIn' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/BuiltIn.php',
    'phpseclib4\\Math\\BigInteger\\Engines\\BCMath\\DefaultEngine' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/DefaultEngine.php',
    'phpseclib4\\Math\\BigInteger\\Engines\\BCMath\\OpenSSL' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/OpenSSL.php',
    'phpseclib4\\Math\\BigInteger\\Engines\\BCMath\\Reductions\\Barrett' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/Reductions/Barrett.php',
    'phpseclib4\\Math\\BigInteger\\Engines\\BCMath\\Reductions\\EvalBarrett' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/Reductions/EvalBarrett.php',
    'phpseclib4\\Math\\BigInteger\\Engines\\Engine' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/Engine.php',
    'phpseclib4\\Math\\BigInteger\\Engines\\GMP' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/GMP.php',
    'phpseclib4\\Math\\BigInteger\\Engines\\GMP\\DefaultEngine' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/GMP/DefaultEngine.php',
    'phpseclib4\\Math\\BigInteger\\Engines\\OpenSSL' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/OpenSSL.php',
    'phpseclib4\\Math\\BigInteger\\Engines\\PHP' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP.php',
    'phpseclib4\\Math\\BigInteger\\Engines\\PHP32' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP32.php',
    'phpseclib4\\Math\\BigInteger\\Engines\\PHP64' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP64.php',
    'phpseclib4\\Math\\BigInteger\\Engines\\PHP\\Base' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Base.php',
    'phpseclib4\\Math\\BigInteger\\Engines\\PHP\\DefaultEngine' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/DefaultEngine.php',
    'phpseclib4\\Math\\BigInteger\\Engines\\PHP\\Montgomery' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Montgomery.php',
    'phpseclib4\\Math\\BigInteger\\Engines\\PHP\\OpenSSL' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/OpenSSL.php',
    'phpseclib4\\Math\\BigInteger\\Engines\\PHP\\Reductions\\Barrett' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Barrett.php',
    'phpseclib4\\Math\\BigInteger\\Engines\\PHP\\Reductions\\Classic' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Classic.php',
    'phpseclib4\\Math\\BigInteger\\Engines\\PHP\\Reductions\\EvalBarrett' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/EvalBarrett.php',
    'phpseclib4\\Math\\BigInteger\\Engines\\PHP\\Reductions\\Montgomery' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Montgomery.php',
    'phpseclib4\\Math\\BigInteger\\Engines\\PHP\\Reductions\\MontgomeryMult' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/MontgomeryMult.php',
    'phpseclib4\\Math\\BigInteger\\Engines\\PHP\\Reductions\\PowerOfTwo' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/PowerOfTwo.php',
    'phpseclib4\\Math\\BinaryField' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BinaryField.php',
    'phpseclib4\\Math\\BinaryField\\Integer' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BinaryField/Integer.php',
    'phpseclib4\\Math\\Common\\FiniteField' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/Common/FiniteField.php',
    'phpseclib4\\Math\\Common\\FiniteField\\Integer' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/Common/FiniteField/Integer.php',
    'phpseclib4\\Math\\PrimeField' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/PrimeField.php',
    'phpseclib4\\Math\\PrimeField\\Integer' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/PrimeField/Integer.php',
    'phpseclib4\\Net\\SCP' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Net/SCP.php',
    'phpseclib4\\Net\\SFTP' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Net/SFTP.php',
    'phpseclib4\\Net\\SFTP\\Attribute' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Net/SFTP/Attribute.php',
    'phpseclib4\\Net\\SFTP\\FileType' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Net/SFTP/FileType.php',
    'phpseclib4\\Net\\SFTP\\OpenFlag' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Net/SFTP/OpenFlag.php',
    'phpseclib4\\Net\\SFTP\\OpenFlag5' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Net/SFTP/OpenFlag5.php',
    'phpseclib4\\Net\\SFTP\\PacketType' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Net/SFTP/PacketType.php',
    'phpseclib4\\Net\\SFTP\\StatusCode' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Net/SFTP/StatusCode.php',
    'phpseclib4\\Net\\SFTP\\Stream' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Net/SFTP/Stream.php',
    'phpseclib4\\Net\\SSH2' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Net/SSH2.php',
    'phpseclib4\\Net\\SSH2\\ChannelConnectionFailureReason' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Net/SSH2/ChannelConnectionFailureReason.php',
    'phpseclib4\\Net\\SSH2\\DisconnectReason' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Net/SSH2/DisconnectReason.php',
    'phpseclib4\\Net\\SSH2\\MessageType' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Net/SSH2/MessageType.php',
    'phpseclib4\\Net\\SSH2\\MessageTypeExtra' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Net/SSH2/MessageTypeExtra.php',
    'phpseclib4\\Net\\SSH2\\TerminalMode' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Net/SSH2/TerminalMode.php',
    'phpseclib4\\System\\SSH\\Agent' => $vendorDir . '/phpseclib/phpseclib/phpseclib/System/SSH/Agent.php',
    'phpseclib4\\System\\SSH\\Agent\\Identity' => $vendorDir . '/phpseclib/phpseclib/phpseclib/System/SSH/Agent/Identity.php',
    'phpseclib4\\System\\SSH\\Common\\Traits\\ReadBytes' => $vendorDir . '/phpseclib/phpseclib/phpseclib/System/SSH/Common/Traits/ReadBytes.php',
);
<?php

// platform_check.php @generated by Composer

$issues = array();

if (!(PHP_VERSION_ID >= 80204)) {
    $issues[] = 'Your Composer dependencies require a PHP version ">= 8.2.4". You are running ' . PHP_VERSION . '.';
}

if (PHP_INT_SIZE !== 8) {
    $issues[] = 'Your Composer dependencies require a 64-bit build of PHP.';
}

if ($issues) {
    if (!headers_sent()) {
        header('HTTP/1.1 500 Internal Server Error');
    }
    if (!ini_get('display_errors')) {
        if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
            fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
        } elseif (!headers_sent()) {
            echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
        }
    }
    throw new \RuntimeException(
        'Composer detected issues in your platform: ' . implode(' ', $issues)
    );
}
<?php

// autoload_static.php @generated by Composer

namespace Composer\Autoload;

class ComposerStaticInitfaa67bc9fee81c220dc566b8c299dea4
{
    public static $files = array (
        '88254829cb0eed057c30eaabb6d8edc4' => __DIR__ . '/..' . '/amphp/amp/src/functions.php',
        '429ae5f14a13a9076791c19422e10996' => __DIR__ . '/..' . '/amphp/amp/src/Future/functions.php',
        'c8601a4144b50a7b548da082c89c4dc1' => __DIR__ . '/..' . '/amphp/amp/src/Internal/functions.php',
        '3da389f428d8ee50333e4391c3f45046' => __DIR__ . '/..' . '/amphp/serialization/src/functions.php',
        'bcb7d4fc55f4b1a7e10f5806723e9892' => __DIR__ . '/..' . '/amphp/sync/src/functions.php',
        '6c2681bc13923b80242cc286856cab22' => __DIR__ . '/..' . '/amphp/byte-stream/src/functions.php',
        '4ee21bf8253a2272056aefb41f7f0116' => __DIR__ . '/..' . '/amphp/byte-stream/src/Internal/functions.php',
        '445532134d762b3cbc25500cac266092' => __DIR__ . '/..' . '/daverandom/libdns/src/functions.php',
        '107d0b55c0ad2d07d8643394552ac029' => __DIR__ . '/..' . '/amphp/process/src/functions.php',
        'f4c761ca07639428acc28ba36643243e' => __DIR__ . '/..' . '/amphp/dns/src/functions.php',
        'd4e415514e4352172d58f02433fa50e4' => __DIR__ . '/..' . '/amphp/socket/src/functions.php',
        '1c2dcb9d6851a7abaae89f9586ddd460' => __DIR__ . '/..' . '/amphp/socket/src/Internal/functions.php',
        '7fec264e4908bd2976476a6241c3f4d0' => __DIR__ . '/..' . '/amphp/socket/src/SocketAddress/functions.php',
        'e39a8b23c42d4e1452234d762b03835a' => __DIR__ . '/..' . '/ramsey/uuid/src/functions.php',
        '9d2b9fc6db0f153a0a149fefb182415e' => __DIR__ . '/..' . '/symfony/polyfill-php84/bootstrap.php',
        '662a729f963d39afe703c9d9b7ab4a8c' => __DIR__ . '/..' . '/symfony/polyfill-php83/bootstrap.php',
        '3d8ee50db78074a9235f0c2008c26b42' => __DIR__ . '/..' . '/amphp/http/src/functions.php',
        '40522f9f0eb7d4f191114e944ee6d064' => __DIR__ . '/..' . '/amphp/http/src/Internal/constants.php',
        '5897ea0ac4cccf14d323035e65887801' => __DIR__ . '/..' . '/symfony/polyfill-php82/bootstrap.php',
        'decc78cc4436b1292c6c0d151b19445c' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/bootstrap.php',
        '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
        'db2f6d3e039789c7acd0e5d1d5a42077' => __DIR__ . '/..' . '/amphp/http-client/src/functions.php',
        '77e5a577434e31d19d8dd6aeceac1ff4' => __DIR__ . '/..' . '/amphp/http-client/src/Internal/functions.php',
        '5d92623b88cbe7c988a6aca6fa79ebce' => __DIR__ . '/..' . '/amphp/parallel/src/Context/functions.php',
        'c20d640a0b06382af5bbe7e0c7475adf' => __DIR__ . '/..' . '/amphp/parallel/src/Context/Internal/functions.php',
        '42a785d14bae29c606457755ffd1653b' => __DIR__ . '/..' . '/amphp/parallel/src/Ipc/functions.php',
        '805558626a43dc52f2afc82368f8d62e' => __DIR__ . '/..' . '/amphp/parallel/src/Worker/functions.php',
        '73cfe662a9f753fb79cdfcb7b4206d43' => __DIR__ . '/..' . '/amphp/mysql/src/functions.php',
        '4da7a33b8388a4c58699a4f48894fced' => __DIR__ . '/..' . '/amphp/postgres/src/functions.php',
        '33e77b43ad8185a87488d9c8e2900fb0' => __DIR__ . '/..' . '/amphp/postgres/src/Internal/functions.php',
        '792db3860ad68f8c7b522ed67947a5eb' => __DIR__ . '/..' . '/amphp/redis/src/functions.php',
        '6a333119f7ba58386c4385af1d708a0a' => __DIR__ . '/..' . '/amphp/redis/src/Internal/functions.php',
        '3d05d4f147c95ba663000bd908d45656' => __DIR__ . '/..' . '/amphp/websocket/src/functions.php',
        'd10f490189cfd2d00bda2b165dfbacae' => __DIR__ . '/..' . '/amphp/file/src/functions.php',
        '9dc653cbaa44da77069f245bac46455f' => __DIR__ . '/..' . '/amphp/http-server/src/Driver/functions.php',
        '3ff3609f8c8173833eaa97521a843531' => __DIR__ . '/..' . '/amphp/http-server/src/Middleware/functions.php',
        'a56027adf84a32da155734de2d858e65' => __DIR__ . '/..' . '/amphp/http-server/src/functions.php',
        '5ac5dbc97af12bd847e1db9fe93e192f' => __DIR__ . '/..' . '/amphp/log/src/functions.php',
        '4be4fbd9f5a89207b1fd1c85ae339dd7' => __DIR__ . '/..' . '/amphp/websocket-client/src/functions.php',
        '2b4b72fd9056e8b7ab3f418bbf68fc53' => __DIR__ . '/..' . '/danog/ipc/lib/functions.php',
        'def43f6c87e4f8dfd0c9e1b1bab14fe8' => __DIR__ . '/..' . '/symfony/polyfill-iconv/bootstrap.php',
        '606a39d89246991a373564698c2d8383' => __DIR__ . '/..' . '/symfony/polyfill-php85/bootstrap.php',
        '2c2415ec15363ede1bff13a287462ba1' => __DIR__ . '/..' . '/symfony/polyfill-php86/bootstrap.php',
        'cad60ee4f0892badc28f3feef7cce08d' => __DIR__ . '/..' . '/danog/madelineproto/src/polyfill.php',
    );

    public static $prefixLengthsPsr4 = array (
        'p' =>
        array (
            'phpseclib4\\' => 11,
        ),
        'd' =>
        array (
            'danog\\TelegramEntities\\' => 23,
            'danog\\MadelineProto\\' => 20,
            'danog\\Loop\\' => 11,
            'danog\\LibDNSJson\\' => 17,
            'danog\\DialogId\\' => 15,
            'danog\\Decoder\\' => 14,
            'danog\\BetterPrometheus\\' => 23,
            'danog\\AsyncOrm\\' => 15,
        ),
        'W' =>
        array (
            'Webrtc\\Webrtc\\' => 14,
            'Webrtc\\TURN\\' => 12,
            'Webrtc\\Stats\\' => 13,
            'Webrtc\\Srtp\\' => 12,
            'Webrtc\\STUN\\' => 12,
            'Webrtc\\SDP\\' => 11,
            'Webrtc\\SCTP\\' => 12,
            'Webrtc\\RTP\\' => 11,
            'Webrtc\\RTPParameter\\' => 20,
            'Webrtc\\RTCP\\' => 12,
            'Webrtc\\NTP\\' => 11,
            'Webrtc\\Mixin\\' => 13,
            'Webrtc\\MDNS\\' => 12,
            'Webrtc\\ICE\\' => 11,
            'Webrtc\\Exception\\' => 17,
            'Webrtc\\DataChannel\\' => 19,
            'Webrtc\\DTLS\\' => 12,
            'Webrtc\\Codecs\\' => 14,
            'Webrtc\\AVCodec\\' => 15,
            'Webmozart\\Assert\\' => 17,
        ),
        'S' =>
        array (
            'Symfony\\Polyfill\\Php86\\' => 23,
            'Symfony\\Polyfill\\Php85\\' => 23,
            'Symfony\\Polyfill\\Php84\\' => 23,
            'Symfony\\Polyfill\\Php83\\' => 23,
            'Symfony\\Polyfill\\Php82\\' => 23,
            'Symfony\\Polyfill\\Mbstring\\' => 26,
            'Symfony\\Polyfill\\Iconv\\' => 23,
        ),
        'R' =>
        array (
            'Revolt\\' => 7,
            'Ramsey\\Uuid\\' => 12,
            'Ramsey\\Collection\\' => 18,
        ),
        'P' =>
        array (
            'Psr\\Log\\' => 8,
            'Psr\\Http\\Message\\' => 17,
            'Prometheus\\' => 11,
            'PhpParser\\' => 10,
            'ParagonIE\\ConstantTime\\' => 23,
        ),
        'M' =>
        array (
            'Monolog\\' => 8,
        ),
        'L' =>
        array (
            'LibDNS\\' => 7,
            'League\\Uri\\' => 11,
        ),
        'K' =>
        array (
            'Kelunik\\Certificate\\' => 20,
        ),
        'D' =>
        array (
            'DASPRiD\\Enum\\' => 13,
        ),
        'B' =>
        array (
            'Brick\\Math\\' => 11,
            'BaconQrCode\\' => 12,
        ),
        'A' =>
        array (
            'Amp\\Websocket\\Client\\' => 21,
            'Amp\\Websocket\\' => 14,
            'Amp\\Sync\\' => 9,
            'Amp\\Sql\\Common\\' => 15,
            'Amp\\Sql\\' => 8,
            'Amp\\Socket\\' => 11,
            'Amp\\Serialization\\' => 18,
            'Amp\\Redis\\' => 10,
            'Amp\\Process\\' => 12,
            'Amp\\Postgres\\' => 13,
            'Amp\\Pipeline\\' => 13,
            'Amp\\Parser\\' => 11,
            'Amp\\Parallel\\' => 13,
            'Amp\\Mysql\\' => 10,
            'Amp\\Log\\' => 8,
            'Amp\\Ipc\\' => 8,
            'Amp\\Http\\Server\\' => 16,
            'Amp\\Http\\Client\\Cookie\\' => 23,
            'Amp\\Http\\Client\\' => 16,
            'Amp\\Http\\' => 9,
            'Amp\\File\\' => 9,
            'Amp\\DoH\\' => 8,
            'Amp\\Dns\\' => 8,
            'Amp\\Cache\\' => 10,
            'Amp\\ByteStream\\' => 15,
            'Amp\\' => 4,
        ),
    );

    public static $prefixDirsPsr4 = array (
        'phpseclib4\\' =>
        array (
            0 => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib',
        ),
        'danog\\TelegramEntities\\' =>
        array (
            0 => __DIR__ . '/..' . '/danog/telegram-entities/src',
        ),
        'danog\\MadelineProto\\' =>
        array (
            0 => __DIR__ . '/..' . '/danog/madelineproto/src',
        ),
        'danog\\Loop\\' =>
        array (
            0 => __DIR__ . '/..' . '/danog/loop/lib',
        ),
        'danog\\LibDNSJson\\' =>
        array (
            0 => __DIR__ . '/..' . '/danog/libdns-json/lib',
        ),
        'danog\\DialogId\\' =>
        array (
            0 => __DIR__ . '/..' . '/danog/tg-dialog-id/src',
        ),
        'danog\\Decoder\\' =>
        array (
            0 => __DIR__ . '/..' . '/danog/tg-file-decoder/src',
        ),
        'danog\\BetterPrometheus\\' =>
        array (
            0 => __DIR__ . '/..' . '/danog/better-prometheus/lib',
        ),
        'danog\\AsyncOrm\\' =>
        array (
            0 => __DIR__ . '/..' . '/danog/async-orm/src',
        ),
        'Webrtc\\Webrtc\\' =>
        array (
            0 => __DIR__ . '/..' . '/danog/php-rtc-webrtc/src',
        ),
        'Webrtc\\TURN\\' =>
        array (
            0 => __DIR__ . '/..' . '/danog/php-rtc-turn/src',
        ),
        'Webrtc\\Stats\\' =>
        array (
            0 => __DIR__ . '/..' . '/danog/php-rtc-stats/src',
        ),
        'Webrtc\\Srtp\\' =>
        array (
            0 => __DIR__ . '/..' . '/danog/php-rtc-srtp/src',
        ),
        'Webrtc\\STUN\\' =>
        array (
            0 => __DIR__ . '/..' . '/danog/php-rtc-stun/src',
        ),
        'Webrtc\\SDP\\' =>
        array (
            0 => __DIR__ . '/..' . '/danog/php-rtc-sdp/src',
        ),
        'Webrtc\\SCTP\\' =>
        array (
            0 => __DIR__ . '/..' . '/danog/php-rtc-sctp/src',
        ),
        'Webrtc\\RTP\\' =>
        array (
            0 => __DIR__ . '/..' . '/danog/php-rtc-rtp/src',
        ),
        'Webrtc\\RTPParameter\\' =>
        array (
            0 => __DIR__ . '/..' . '/danog/php-rtc-rtp-parameter/src',
        ),
        'Webrtc\\RTCP\\' =>
        array (
            0 => __DIR__ . '/..' . '/danog/php-rtc-rtcp/src',
        ),
        'Webrtc\\NTP\\' =>
        array (
            0 => __DIR__ . '/..' . '/danog/php-rtc-ntp/src',
        ),
        'Webrtc\\Mixin\\' =>
        array (
            0 => __DIR__ . '/..' . '/danog/php-rtc-mixin/src',
        ),
        'Webrtc\\MDNS\\' =>
        array (
            0 => __DIR__ . '/..' . '/danog/php-rtc-mdns/src',
        ),
        'Webrtc\\ICE\\' =>
        array (
            0 => __DIR__ . '/..' . '/danog/php-rtc-ice/src',
        ),
        'Webrtc\\Exception\\' =>
        array (
            0 => __DIR__ . '/..' . '/danog/php-rtc-exception/src',
        ),
        'Webrtc\\DataChannel\\' =>
        array (
            0 => __DIR__ . '/..' . '/danog/php-rtc-datachannel/src',
        ),
        'Webrtc\\DTLS\\' =>
        array (
            0 => __DIR__ . '/..' . '/danog/php-rtc-dtls/src',
        ),
        'Webrtc\\Codecs\\' =>
        array (
            0 => __DIR__ . '/..' . '/danog/php-rtc-codecs/src',
        ),
        'Webrtc\\AVCodec\\' =>
        array (
            0 => __DIR__ . '/..' . '/danog/php-rtc-av/src',
        ),
        'Webmozart\\Assert\\' =>
        array (
            0 => __DIR__ . '/..' . '/webmozart/assert/src',
        ),
        'Symfony\\Polyfill\\Php86\\' =>
        array (
            0 => __DIR__ . '/..' . '/symfony/polyfill-php86',
        ),
        'Symfony\\Polyfill\\Php85\\' =>
        array (
            0 => __DIR__ . '/..' . '/symfony/polyfill-php85',
        ),
        'Symfony\\Polyfill\\Php84\\' =>
        array (
            0 => __DIR__ . '/..' . '/symfony/polyfill-php84',
        ),
        'Symfony\\Polyfill\\Php83\\' =>
        array (
            0 => __DIR__ . '/..' . '/symfony/polyfill-php83',
        ),
        'Symfony\\Polyfill\\Php82\\' =>
        array (
            0 => __DIR__ . '/..' . '/symfony/polyfill-php82',
        ),
        'Symfony\\Polyfill\\Mbstring\\' =>
        array (
            0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring',
        ),
        'Symfony\\Polyfill\\Iconv\\' =>
        array (
            0 => __DIR__ . '/..' . '/symfony/polyfill-iconv',
        ),
        'Revolt\\' =>
        array (
            0 => __DIR__ . '/..' . '/revolt/event-loop/src',
        ),
        'Ramsey\\Uuid\\' =>
        array (
            0 => __DIR__ . '/..' . '/ramsey/uuid/src',
        ),
        'Ramsey\\Collection\\' =>
        array (
            0 => __DIR__ . '/..' . '/ramsey/collection/src',
        ),
        'Psr\\Log\\' =>
        array (
            0 => __DIR__ . '/..' . '/psr/log/src',
        ),
        'Psr\\Http\\Message\\' =>
        array (
            0 => __DIR__ . '/..' . '/psr/http-factory/src',
            1 => __DIR__ . '/..' . '/psr/http-message/src',
        ),
        'Prometheus\\' =>
        array (
            0 => __DIR__ . '/..' . '/promphp/prometheus_client_php/src/Prometheus',
        ),
        'PhpParser\\' =>
        array (
            0 => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser',
        ),
        'ParagonIE\\ConstantTime\\' =>
        array (
            0 => __DIR__ . '/..' . '/paragonie/constant_time_encoding/src',
        ),
        'Monolog\\' =>
        array (
            0 => __DIR__ . '/..' . '/monolog/monolog/src/Monolog',
        ),
        'LibDNS\\' =>
        array (
            0 => __DIR__ . '/..' . '/daverandom/libdns/src',
        ),
        'League\\Uri\\' =>
        array (
            0 => __DIR__ . '/..' . '/league/uri-components',
            1 => __DIR__ . '/..' . '/league/uri',
            2 => __DIR__ . '/..' . '/league/uri-interfaces',
        ),
        'Kelunik\\Certificate\\' =>
        array (
            0 => __DIR__ . '/..' . '/kelunik/certificate/src',
        ),
        'DASPRiD\\Enum\\' =>
        array (
            0 => __DIR__ . '/..' . '/dasprid/enum/src',
        ),
        'Brick\\Math\\' =>
        array (
            0 => __DIR__ . '/..' . '/brick/math/src',
        ),
        'BaconQrCode\\' =>
        array (
            0 => __DIR__ . '/..' . '/bacon/bacon-qr-code/src',
        ),
        'Amp\\Websocket\\Client\\' =>
        array (
            0 => __DIR__ . '/..' . '/amphp/websocket-client/src',
        ),
        'Amp\\Websocket\\' =>
        array (
            0 => __DIR__ . '/..' . '/amphp/websocket/src',
        ),
        'Amp\\Sync\\' =>
        array (
            0 => __DIR__ . '/..' . '/amphp/sync/src',
        ),
        'Amp\\Sql\\Common\\' =>
        array (
            0 => __DIR__ . '/..' . '/amphp/sql-common/src',
        ),
        'Amp\\Sql\\' =>
        array (
            0 => __DIR__ . '/..' . '/amphp/sql/src',
        ),
        'Amp\\Socket\\' =>
        array (
            0 => __DIR__ . '/..' . '/amphp/socket/src',
        ),
        'Amp\\Serialization\\' =>
        array (
            0 => __DIR__ . '/..' . '/amphp/serialization/src',
        ),
        'Amp\\Redis\\' =>
        array (
            0 => __DIR__ . '/..' . '/amphp/redis/src',
        ),
        'Amp\\Process\\' =>
        array (
            0 => __DIR__ . '/..' . '/amphp/process/src',
        ),
        'Amp\\Postgres\\' =>
        array (
            0 => __DIR__ . '/..' . '/amphp/postgres/src',
        ),
        'Amp\\Pipeline\\' =>
        array (
            0 => __DIR__ . '/..' . '/amphp/pipeline/src',
        ),
        'Amp\\Parser\\' =>
        array (
            0 => __DIR__ . '/..' . '/amphp/parser/src',
        ),
        'Amp\\Parallel\\' =>
        array (
            0 => __DIR__ . '/..' . '/amphp/parallel/src',
        ),
        'Amp\\Mysql\\' =>
        array (
            0 => __DIR__ . '/..' . '/amphp/mysql/src',
        ),
        'Amp\\Log\\' =>
        array (
            0 => __DIR__ . '/..' . '/amphp/log/src',
        ),
        'Amp\\Ipc\\' =>
        array (
            0 => __DIR__ . '/..' . '/danog/ipc/lib',
        ),
        'Amp\\Http\\Server\\' =>
        array (
            0 => __DIR__ . '/..' . '/amphp/http-server/src',
        ),
        'Amp\\Http\\Client\\Cookie\\' =>
        array (
            0 => __DIR__ . '/..' . '/amphp/http-client-cookies/src',
        ),
        'Amp\\Http\\Client\\' =>
        array (
            0 => __DIR__ . '/..' . '/amphp/http-client/src',
        ),
        'Amp\\Http\\' =>
        array (
            0 => __DIR__ . '/..' . '/amphp/http/src',
            1 => __DIR__ . '/..' . '/amphp/hpack/src',
        ),
        'Amp\\File\\' =>
        array (
            0 => __DIR__ . '/..' . '/amphp/file/src',
        ),
        'Amp\\DoH\\' =>
        array (
            0 => __DIR__ . '/..' . '/danog/dns-over-https/lib',
        ),
        'Amp\\Dns\\' =>
        array (
            0 => __DIR__ . '/..' . '/amphp/dns/src',
        ),
        'Amp\\Cache\\' =>
        array (
            0 => __DIR__ . '/..' . '/amphp/cache/src',
        ),
        'Amp\\ByteStream\\' =>
        array (
            0 => __DIR__ . '/..' . '/amphp/byte-stream/src',
        ),
        'Amp\\' =>
        array (
            0 => __DIR__ . '/..' . '/amphp/amp/src',
        ),
    );

    public static $prefixesPsr0 = array (
        'd' =>
        array (
            'danog\\' =>
            array (
                0 => __DIR__ . '/..' . '/danog/primemodule/lib',
            ),
        ),
    );

    public static $classMap = array (
        'AllowDynamicProperties' => __DIR__ . '/..' . '/symfony/polyfill-php82/Resources/stubs/AllowDynamicProperties.php',
        'Amp\\ByteStream\\AsyncWriter' => __DIR__ . '/..' . '/amphp/byte-stream/src/AsyncWriter.php',
        'Amp\\ByteStream\\Base64\\Base64DecodingReadableStream' => __DIR__ . '/..' . '/amphp/byte-stream/src/Base64/Base64DecodingReadableStream.php',
        'Amp\\ByteStream\\Base64\\Base64DecodingWritableStream' => __DIR__ . '/..' . '/amphp/byte-stream/src/Base64/Base64DecodingWritableStream.php',
        'Amp\\ByteStream\\Base64\\Base64EncodingReadableStream' => __DIR__ . '/..' . '/amphp/byte-stream/src/Base64/Base64EncodingReadableStream.php',
        'Amp\\ByteStream\\Base64\\Base64EncodingWritableStream' => __DIR__ . '/..' . '/amphp/byte-stream/src/Base64/Base64EncodingWritableStream.php',
        'Amp\\ByteStream\\BufferException' => __DIR__ . '/..' . '/amphp/byte-stream/src/BufferException.php',
        'Amp\\ByteStream\\BufferedReader' => __DIR__ . '/..' . '/amphp/byte-stream/src/BufferedReader.php',
        'Amp\\ByteStream\\ClosedException' => __DIR__ . '/..' . '/amphp/byte-stream/src/ClosedException.php',
        'Amp\\ByteStream\\Compression\\CompressingReadableStream' => __DIR__ . '/..' . '/amphp/byte-stream/src/Compression/CompressingReadableStream.php',
        'Amp\\ByteStream\\Compression\\CompressingWritableStream' => __DIR__ . '/..' . '/amphp/byte-stream/src/Compression/CompressingWritableStream.php',
        'Amp\\ByteStream\\Compression\\DecompressingReadableStream' => __DIR__ . '/..' . '/amphp/byte-stream/src/Compression/DecompressingReadableStream.php',
        'Amp\\ByteStream\\Compression\\DecompressingWritableStream' => __DIR__ . '/..' . '/amphp/byte-stream/src/Compression/DecompressingWritableStream.php',
        'Amp\\ByteStream\\Internal\\ChannelParser' => __DIR__ . '/..' . '/amphp/byte-stream/src/Internal/ChannelParser.php',
        'Amp\\ByteStream\\Payload' => __DIR__ . '/..' . '/amphp/byte-stream/src/Payload.php',
        'Amp\\ByteStream\\PendingReadError' => __DIR__ . '/..' . '/amphp/byte-stream/src/PendingReadError.php',
        'Amp\\ByteStream\\Pipe' => __DIR__ . '/..' . '/amphp/byte-stream/src/Pipe.php',
        'Amp\\ByteStream\\ReadableBuffer' => __DIR__ . '/..' . '/amphp/byte-stream/src/ReadableBuffer.php',
        'Amp\\ByteStream\\ReadableIterableStream' => __DIR__ . '/..' . '/amphp/byte-stream/src/ReadableIterableStream.php',
        'Amp\\ByteStream\\ReadableResourceStream' => __DIR__ . '/..' . '/amphp/byte-stream/src/ReadableResourceStream.php',
        'Amp\\ByteStream\\ReadableStream' => __DIR__ . '/..' . '/amphp/byte-stream/src/ReadableStream.php',
        'Amp\\ByteStream\\ReadableStreamChain' => __DIR__ . '/..' . '/amphp/byte-stream/src/ReadableStreamChain.php',
        'Amp\\ByteStream\\ReadableStreamIteratorAggregate' => __DIR__ . '/..' . '/amphp/byte-stream/src/ReadableStreamIteratorAggregate.php',
        'Amp\\ByteStream\\ResourceStream' => __DIR__ . '/..' . '/amphp/byte-stream/src/ResourceStream.php',
        'Amp\\ByteStream\\StreamChannel' => __DIR__ . '/..' . '/amphp/byte-stream/src/StreamChannel.php',
        'Amp\\ByteStream\\StreamException' => __DIR__ . '/..' . '/amphp/byte-stream/src/StreamException.php',
        'Amp\\ByteStream\\WritableBuffer' => __DIR__ . '/..' . '/amphp/byte-stream/src/WritableBuffer.php',
        'Amp\\ByteStream\\WritableIterableStream' => __DIR__ . '/..' . '/amphp/byte-stream/src/WritableIterableStream.php',
        'Amp\\ByteStream\\WritableResourceStream' => __DIR__ . '/..' . '/amphp/byte-stream/src/WritableResourceStream.php',
        'Amp\\ByteStream\\WritableStream' => __DIR__ . '/..' . '/amphp/byte-stream/src/WritableStream.php',
        'Amp\\Cache\\AtomicCache' => __DIR__ . '/..' . '/amphp/cache/src/AtomicCache.php',
        'Amp\\Cache\\Cache' => __DIR__ . '/..' . '/amphp/cache/src/Cache.php',
        'Amp\\Cache\\CacheException' => __DIR__ . '/..' . '/amphp/cache/src/CacheException.php',
        'Amp\\Cache\\LocalCache' => __DIR__ . '/..' . '/amphp/cache/src/LocalCache.php',
        'Amp\\Cache\\NullCache' => __DIR__ . '/..' . '/amphp/cache/src/NullCache.php',
        'Amp\\Cache\\PrefixCache' => __DIR__ . '/..' . '/amphp/cache/src/PrefixCache.php',
        'Amp\\Cache\\SerializedCache' => __DIR__ . '/..' . '/amphp/cache/src/SerializedCache.php',
        'Amp\\Cache\\StringCache' => __DIR__ . '/..' . '/amphp/cache/src/StringCache.php',
        'Amp\\Cache\\StringCacheAdapter' => __DIR__ . '/..' . '/amphp/cache/src/StringCacheAdapter.php',
        'Amp\\Cancellation' => __DIR__ . '/..' . '/amphp/amp/src/Cancellation.php',
        'Amp\\CancelledException' => __DIR__ . '/..' . '/amphp/amp/src/CancelledException.php',
        'Amp\\Closable' => __DIR__ . '/..' . '/amphp/amp/src/Closable.php',
        'Amp\\CompositeCancellation' => __DIR__ . '/..' . '/amphp/amp/src/CompositeCancellation.php',
        'Amp\\CompositeException' => __DIR__ . '/..' . '/amphp/amp/src/CompositeException.php',
        'Amp\\CompositeLengthException' => __DIR__ . '/..' . '/amphp/amp/src/CompositeLengthException.php',
        'Amp\\DeferredCancellation' => __DIR__ . '/..' . '/amphp/amp/src/DeferredCancellation.php',
        'Amp\\DeferredFuture' => __DIR__ . '/..' . '/amphp/amp/src/DeferredFuture.php',
        'Amp\\Dns\\BlockingFallbackDnsResolver' => __DIR__ . '/..' . '/amphp/dns/src/BlockingFallbackDnsResolver.php',
        'Amp\\Dns\\DnsConfig' => __DIR__ . '/..' . '/amphp/dns/src/DnsConfig.php',
        'Amp\\Dns\\DnsConfigException' => __DIR__ . '/..' . '/amphp/dns/src/DnsConfigException.php',
        'Amp\\Dns\\DnsConfigLoader' => __DIR__ . '/..' . '/amphp/dns/src/DnsConfigLoader.php',
        'Amp\\Dns\\DnsException' => __DIR__ . '/..' . '/amphp/dns/src/DnsException.php',
        'Amp\\Dns\\DnsRecord' => __DIR__ . '/..' . '/amphp/dns/src/DnsRecord.php',
        'Amp\\Dns\\DnsResolver' => __DIR__ . '/..' . '/amphp/dns/src/DnsResolver.php',
        'Amp\\Dns\\DnsTimeoutException' => __DIR__ . '/..' . '/amphp/dns/src/DnsTimeoutException.php',
        'Amp\\Dns\\HostLoader' => __DIR__ . '/..' . '/amphp/dns/src/HostLoader.php',
        'Amp\\Dns\\Internal\\Socket' => __DIR__ . '/..' . '/amphp/dns/src/Internal/Socket.php',
        'Amp\\Dns\\Internal\\TcpSocket' => __DIR__ . '/..' . '/amphp/dns/src/Internal/TcpSocket.php',
        'Amp\\Dns\\Internal\\UdpSocket' => __DIR__ . '/..' . '/amphp/dns/src/Internal/UdpSocket.php',
        'Amp\\Dns\\InvalidNameException' => __DIR__ . '/..' . '/amphp/dns/src/InvalidNameException.php',
        'Amp\\Dns\\MissingDnsRecordException' => __DIR__ . '/..' . '/amphp/dns/src/MissingDnsRecordException.php',
        'Amp\\Dns\\Rfc1035StubDnsResolver' => __DIR__ . '/..' . '/amphp/dns/src/Rfc1035StubDnsResolver.php',
        'Amp\\Dns\\StaticDnsConfigLoader' => __DIR__ . '/..' . '/amphp/dns/src/StaticDnsConfigLoader.php',
        'Amp\\Dns\\UnixDnsConfigLoader' => __DIR__ . '/..' . '/amphp/dns/src/UnixDnsConfigLoader.php',
        'Amp\\Dns\\WindowsDnsConfigLoader' => __DIR__ . '/..' . '/amphp/dns/src/WindowsDnsConfigLoader.php',
        'Amp\\DoH\\DoHConfig' => __DIR__ . '/..' . '/danog/dns-over-https/lib/DoHConfig.php',
        'Amp\\DoH\\DoHException' => __DIR__ . '/..' . '/danog/dns-over-https/lib/DoHException.php',
        'Amp\\DoH\\DoHNameserver' => __DIR__ . '/..' . '/danog/dns-over-https/lib/DoHNameserver.php',
        'Amp\\DoH\\DoHNameserverType' => __DIR__ . '/..' . '/danog/dns-over-https/lib/DoHNameserverType.php',
        'Amp\\DoH\\Rfc8484StubDoHResolver' => __DIR__ . '/..' . '/danog/dns-over-https/lib/Rfc8484StubDoHResolver.php',
        'Amp\\File\\Driver\\BlockingFile' => __DIR__ . '/..' . '/amphp/file/src/Driver/BlockingFile.php',
        'Amp\\File\\Driver\\BlockingFilesystemDriver' => __DIR__ . '/..' . '/amphp/file/src/Driver/BlockingFilesystemDriver.php',
        'Amp\\File\\Driver\\EioFile' => __DIR__ . '/..' . '/amphp/file/src/Driver/EioFile.php',
        'Amp\\File\\Driver\\EioFilesystemDriver' => __DIR__ . '/..' . '/amphp/file/src/Driver/EioFilesystemDriver.php',
        'Amp\\File\\Driver\\ParallelFile' => __DIR__ . '/..' . '/amphp/file/src/Driver/ParallelFile.php',
        'Amp\\File\\Driver\\ParallelFilesystemDriver' => __DIR__ . '/..' . '/amphp/file/src/Driver/ParallelFilesystemDriver.php',
        'Amp\\File\\Driver\\StatusCachingFile' => __DIR__ . '/..' . '/amphp/file/src/Driver/StatusCachingFile.php',
        'Amp\\File\\Driver\\StatusCachingFilesystemDriver' => __DIR__ . '/..' . '/amphp/file/src/Driver/StatusCachingFilesystemDriver.php',
        'Amp\\File\\Driver\\UvFile' => __DIR__ . '/..' . '/amphp/file/src/Driver/UvFile.php',
        'Amp\\File\\Driver\\UvFilesystemDriver' => __DIR__ . '/..' . '/amphp/file/src/Driver/UvFilesystemDriver.php',
        'Amp\\File\\File' => __DIR__ . '/..' . '/amphp/file/src/File.php',
        'Amp\\File\\FileCache' => __DIR__ . '/..' . '/amphp/file/src/FileCache.php',
        'Amp\\File\\FileMutex' => __DIR__ . '/..' . '/amphp/file/src/FileMutex.php',
        'Amp\\File\\Filesystem' => __DIR__ . '/..' . '/amphp/file/src/Filesystem.php',
        'Amp\\File\\FilesystemDriver' => __DIR__ . '/..' . '/amphp/file/src/FilesystemDriver.php',
        'Amp\\File\\FilesystemException' => __DIR__ . '/..' . '/amphp/file/src/FilesystemException.php',
        'Amp\\File\\Internal\\Cache' => __DIR__ . '/..' . '/amphp/file/src/Internal/Cache.php',
        'Amp\\File\\Internal\\EioPoll' => __DIR__ . '/..' . '/amphp/file/src/Internal/EioPoll.php',
        'Amp\\File\\Internal\\FileTask' => __DIR__ . '/..' . '/amphp/file/src/Internal/FileTask.php',
        'Amp\\File\\Internal\\FileWorker' => __DIR__ . '/..' . '/amphp/file/src/Internal/FileWorker.php',
        'Amp\\File\\Internal\\QueuedWritesFile' => __DIR__ . '/..' . '/amphp/file/src/Internal/QueuedWritesFile.php',
        'Amp\\File\\Internal\\UvPoll' => __DIR__ . '/..' . '/amphp/file/src/Internal/UvPoll.php',
        'Amp\\File\\KeyedFileMutex' => __DIR__ . '/..' . '/amphp/file/src/KeyedFileMutex.php',
        'Amp\\File\\PendingOperationError' => __DIR__ . '/..' . '/amphp/file/src/PendingOperationError.php',
        'Amp\\File\\Whence' => __DIR__ . '/..' . '/amphp/file/src/Whence.php',
        'Amp\\ForbidCloning' => __DIR__ . '/..' . '/amphp/amp/src/ForbidCloning.php',
        'Amp\\ForbidSerialization' => __DIR__ . '/..' . '/amphp/amp/src/ForbidSerialization.php',
        'Amp\\Future' => __DIR__ . '/..' . '/amphp/amp/src/Future.php',
        'Amp\\Future\\UnhandledFutureError' => __DIR__ . '/..' . '/amphp/amp/src/Future/UnhandledFutureError.php',
        'Amp\\Http\\Client\\ApplicationInterceptor' => __DIR__ . '/..' . '/amphp/http-client/src/ApplicationInterceptor.php',
        'Amp\\Http\\Client\\BufferedContent' => __DIR__ . '/..' . '/amphp/http-client/src/BufferedContent.php',
        'Amp\\Http\\Client\\Connection\\Connection' => __DIR__ . '/..' . '/amphp/http-client/src/Connection/Connection.php',
        'Amp\\Http\\Client\\Connection\\ConnectionFactory' => __DIR__ . '/..' . '/amphp/http-client/src/Connection/ConnectionFactory.php',
        'Amp\\Http\\Client\\Connection\\ConnectionLimitingPool' => __DIR__ . '/..' . '/amphp/http-client/src/Connection/ConnectionLimitingPool.php',
        'Amp\\Http\\Client\\Connection\\ConnectionPool' => __DIR__ . '/..' . '/amphp/http-client/src/Connection/ConnectionPool.php',
        'Amp\\Http\\Client\\Connection\\DefaultConnectionFactory' => __DIR__ . '/..' . '/amphp/http-client/src/Connection/DefaultConnectionFactory.php',
        'Amp\\Http\\Client\\Connection\\Http1Connection' => __DIR__ . '/..' . '/amphp/http-client/src/Connection/Http1Connection.php',
        'Amp\\Http\\Client\\Connection\\Http2Connection' => __DIR__ . '/..' . '/amphp/http-client/src/Connection/Http2Connection.php',
        'Amp\\Http\\Client\\Connection\\HttpStream' => __DIR__ . '/..' . '/amphp/http-client/src/Connection/HttpStream.php',
        'Amp\\Http\\Client\\Connection\\InterceptedStream' => __DIR__ . '/..' . '/amphp/http-client/src/Connection/InterceptedStream.php',
        'Amp\\Http\\Client\\Connection\\Internal\\Http1Parser' => __DIR__ . '/..' . '/amphp/http-client/src/Connection/Internal/Http1Parser.php',
        'Amp\\Http\\Client\\Connection\\Internal\\Http2ConnectionProcessor' => __DIR__ . '/..' . '/amphp/http-client/src/Connection/Internal/Http2ConnectionProcessor.php',
        'Amp\\Http\\Client\\Connection\\Internal\\Http2Stream' => __DIR__ . '/..' . '/amphp/http-client/src/Connection/Internal/Http2Stream.php',
        'Amp\\Http\\Client\\Connection\\Internal\\RequestNormalizer' => __DIR__ . '/..' . '/amphp/http-client/src/Connection/Internal/RequestNormalizer.php',
        'Amp\\Http\\Client\\Connection\\Stream' => __DIR__ . '/..' . '/amphp/http-client/src/Connection/Stream.php',
        'Amp\\Http\\Client\\Connection\\StreamLimitingPool' => __DIR__ . '/..' . '/amphp/http-client/src/Connection/StreamLimitingPool.php',
        'Amp\\Http\\Client\\Connection\\UnlimitedConnectionPool' => __DIR__ . '/..' . '/amphp/http-client/src/Connection/UnlimitedConnectionPool.php',
        'Amp\\Http\\Client\\Connection\\UpgradedSocket' => __DIR__ . '/..' . '/amphp/http-client/src/Connection/UpgradedSocket.php',
        'Amp\\Http\\Client\\Cookie\\CookieInterceptor' => __DIR__ . '/..' . '/amphp/http-client-cookies/src/CookieInterceptor.php',
        'Amp\\Http\\Client\\Cookie\\CookieJar' => __DIR__ . '/..' . '/amphp/http-client-cookies/src/CookieJar.php',
        'Amp\\Http\\Client\\Cookie\\FileCookieJar' => __DIR__ . '/..' . '/amphp/http-client-cookies/src/FileCookieJar.php',
        'Amp\\Http\\Client\\Cookie\\Internal\\PublicSuffixList' => __DIR__ . '/..' . '/amphp/http-client-cookies/src/Internal/PublicSuffixList.php',
        'Amp\\Http\\Client\\Cookie\\LocalCookieJar' => __DIR__ . '/..' . '/amphp/http-client-cookies/src/LocalCookieJar.php',
        'Amp\\Http\\Client\\Cookie\\NullCookieJar' => __DIR__ . '/..' . '/amphp/http-client-cookies/src/NullCookieJar.php',
        'Amp\\Http\\Client\\DelegateHttpClient' => __DIR__ . '/..' . '/amphp/http-client/src/DelegateHttpClient.php',
        'Amp\\Http\\Client\\EventListener' => __DIR__ . '/..' . '/amphp/http-client/src/EventListener.php',
        'Amp\\Http\\Client\\EventListener\\LogHttpArchive' => __DIR__ . '/..' . '/amphp/http-client/src/EventListener/LogHttpArchive.php',
        'Amp\\Http\\Client\\Form' => __DIR__ . '/..' . '/amphp/http-client/src/Form.php',
        'Amp\\Http\\Client\\HttpClient' => __DIR__ . '/..' . '/amphp/http-client/src/HttpClient.php',
        'Amp\\Http\\Client\\HttpClientBuilder' => __DIR__ . '/..' . '/amphp/http-client/src/HttpClientBuilder.php',
        'Amp\\Http\\Client\\HttpContent' => __DIR__ . '/..' . '/amphp/http-client/src/HttpContent.php',
        'Amp\\Http\\Client\\HttpException' => __DIR__ . '/..' . '/amphp/http-client/src/HttpException.php',
        'Amp\\Http\\Client\\InterceptedHttpClient' => __DIR__ . '/..' . '/amphp/http-client/src/InterceptedHttpClient.php',
        'Amp\\Http\\Client\\Interceptor\\AddRequestHeader' => __DIR__ . '/..' . '/amphp/http-client/src/Interceptor/AddRequestHeader.php',
        'Amp\\Http\\Client\\Interceptor\\AddResponseHeader' => __DIR__ . '/..' . '/amphp/http-client/src/Interceptor/AddResponseHeader.php',
        'Amp\\Http\\Client\\Interceptor\\DecompressResponse' => __DIR__ . '/..' . '/amphp/http-client/src/Interceptor/DecompressResponse.php',
        'Amp\\Http\\Client\\Interceptor\\FollowRedirects' => __DIR__ . '/..' . '/amphp/http-client/src/Interceptor/FollowRedirects.php',
        'Amp\\Http\\Client\\Interceptor\\ForbidUriUserInfo' => __DIR__ . '/..' . '/amphp/http-client/src/Interceptor/ForbidUriUserInfo.php',
        'Amp\\Http\\Client\\Interceptor\\MatchOrigin' => __DIR__ . '/..' . '/amphp/http-client/src/Interceptor/MatchOrigin.php',
        'Amp\\Http\\Client\\Interceptor\\ModifyRequest' => __DIR__ . '/..' . '/amphp/http-client/src/Interceptor/ModifyRequest.php',
        'Amp\\Http\\Client\\Interceptor\\ModifyResponse' => __DIR__ . '/..' . '/amphp/http-client/src/Interceptor/ModifyResponse.php',
        'Amp\\Http\\Client\\Interceptor\\RemoveRequestHeader' => __DIR__ . '/..' . '/amphp/http-client/src/Interceptor/RemoveRequestHeader.php',
        'Amp\\Http\\Client\\Interceptor\\RemoveResponseHeader' => __DIR__ . '/..' . '/amphp/http-client/src/Interceptor/RemoveResponseHeader.php',
        'Amp\\Http\\Client\\Interceptor\\ResolveBaseUri' => __DIR__ . '/..' . '/amphp/http-client/src/Interceptor/ResolveBaseUri.php',
        'Amp\\Http\\Client\\Interceptor\\RetryRequests' => __DIR__ . '/..' . '/amphp/http-client/src/Interceptor/RetryRequests.php',
        'Amp\\Http\\Client\\Interceptor\\SetRequestHeader' => __DIR__ . '/..' . '/amphp/http-client/src/Interceptor/SetRequestHeader.php',
        'Amp\\Http\\Client\\Interceptor\\SetRequestHeaderIfUnset' => __DIR__ . '/..' . '/amphp/http-client/src/Interceptor/SetRequestHeaderIfUnset.php',
        'Amp\\Http\\Client\\Interceptor\\SetRequestTimeout' => __DIR__ . '/..' . '/amphp/http-client/src/Interceptor/SetRequestTimeout.php',
        'Amp\\Http\\Client\\Interceptor\\SetResponseHeader' => __DIR__ . '/..' . '/amphp/http-client/src/Interceptor/SetResponseHeader.php',
        'Amp\\Http\\Client\\Interceptor\\SetResponseHeaderIfUnset' => __DIR__ . '/..' . '/amphp/http-client/src/Interceptor/SetResponseHeaderIfUnset.php',
        'Amp\\Http\\Client\\Interceptor\\TooManyRedirectsException' => __DIR__ . '/..' . '/amphp/http-client/src/Interceptor/TooManyRedirectsException.php',
        'Amp\\Http\\Client\\Internal\\EventInvoker' => __DIR__ . '/..' . '/amphp/http-client/src/Internal/EventInvoker.php',
        'Amp\\Http\\Client\\Internal\\FormField' => __DIR__ . '/..' . '/amphp/http-client/src/Internal/FormField.php',
        'Amp\\Http\\Client\\Internal\\HarAttributes' => __DIR__ . '/..' . '/amphp/http-client/src/Internal/HarAttributes.php',
        'Amp\\Http\\Client\\Internal\\Phase' => __DIR__ . '/..' . '/amphp/http-client/src/Internal/Phase.php',
        'Amp\\Http\\Client\\Internal\\ResponseBodyStream' => __DIR__ . '/..' . '/amphp/http-client/src/Internal/ResponseBodyStream.php',
        'Amp\\Http\\Client\\Internal\\SizeLimitingReadableStream' => __DIR__ . '/..' . '/amphp/http-client/src/Internal/SizeLimitingReadableStream.php',
        'Amp\\Http\\Client\\InvalidRequestException' => __DIR__ . '/..' . '/amphp/http-client/src/InvalidRequestException.php',
        'Amp\\Http\\Client\\MissingAttributeError' => __DIR__ . '/..' . '/amphp/http-client/src/MissingAttributeError.php',
        'Amp\\Http\\Client\\NetworkInterceptor' => __DIR__ . '/..' . '/amphp/http-client/src/NetworkInterceptor.php',
        'Amp\\Http\\Client\\ParseException' => __DIR__ . '/..' . '/amphp/http-client/src/ParseException.php',
        'Amp\\Http\\Client\\PooledHttpClient' => __DIR__ . '/..' . '/amphp/http-client/src/PooledHttpClient.php',
        'Amp\\Http\\Client\\Request' => __DIR__ . '/..' . '/amphp/http-client/src/Request.php',
        'Amp\\Http\\Client\\Response' => __DIR__ . '/..' . '/amphp/http-client/src/Response.php',
        'Amp\\Http\\Client\\SocketException' => __DIR__ . '/..' . '/amphp/http-client/src/SocketException.php',
        'Amp\\Http\\Client\\StreamedContent' => __DIR__ . '/..' . '/amphp/http-client/src/StreamedContent.php',
        'Amp\\Http\\Client\\TimeoutException' => __DIR__ . '/..' . '/amphp/http-client/src/TimeoutException.php',
        'Amp\\Http\\Client\\TlsException' => __DIR__ . '/..' . '/amphp/http-client/src/TlsException.php',
        'Amp\\Http\\Client\\Trailers' => __DIR__ . '/..' . '/amphp/http-client/src/Trailers.php',
        'Amp\\Http\\Cookie\\CookieAttributes' => __DIR__ . '/..' . '/amphp/http/src/Cookie/CookieAttributes.php',
        'Amp\\Http\\Cookie\\InvalidCookieException' => __DIR__ . '/..' . '/amphp/http/src/Cookie/InvalidCookieException.php',
        'Amp\\Http\\Cookie\\RequestCookie' => __DIR__ . '/..' . '/amphp/http/src/Cookie/RequestCookie.php',
        'Amp\\Http\\Cookie\\ResponseCookie' => __DIR__ . '/..' . '/amphp/http/src/Cookie/ResponseCookie.php',
        'Amp\\Http\\HPack' => __DIR__ . '/..' . '/amphp/hpack/src/HPack.php',
        'Amp\\Http\\HPackException' => __DIR__ . '/..' . '/amphp/hpack/src/HPackException.php',
        'Amp\\Http\\Http1\\Rfc7230' => __DIR__ . '/..' . '/amphp/http/src/Http1/Rfc7230.php',
        'Amp\\Http\\Http2\\Http2ConnectionException' => __DIR__ . '/..' . '/amphp/http/src/Http2/Http2ConnectionException.php',
        'Amp\\Http\\Http2\\Http2Parser' => __DIR__ . '/..' . '/amphp/http/src/Http2/Http2Parser.php',
        'Amp\\Http\\Http2\\Http2Processor' => __DIR__ . '/..' . '/amphp/http/src/Http2/Http2Processor.php',
        'Amp\\Http\\Http2\\Http2StreamException' => __DIR__ . '/..' . '/amphp/http/src/Http2/Http2StreamException.php',
        'Amp\\Http\\HttpMessage' => __DIR__ . '/..' . '/amphp/http/src/HttpMessage.php',
        'Amp\\Http\\HttpRequest' => __DIR__ . '/..' . '/amphp/http/src/HttpRequest.php',
        'Amp\\Http\\HttpResponse' => __DIR__ . '/..' . '/amphp/http/src/HttpResponse.php',
        'Amp\\Http\\HttpStatus' => __DIR__ . '/..' . '/amphp/http/src/HttpStatus.php',
        'Amp\\Http\\Internal\\HPackNative' => __DIR__ . '/..' . '/amphp/hpack/src/Internal/HPackNative.php',
        'Amp\\Http\\Internal\\HPackNghttp2' => __DIR__ . '/..' . '/amphp/hpack/src/Internal/HPackNghttp2.php',
        'Amp\\Http\\InvalidHeaderException' => __DIR__ . '/..' . '/amphp/http/src/InvalidHeaderException.php',
        'Amp\\Http\\Server\\ClientException' => __DIR__ . '/..' . '/amphp/http-server/src/ClientException.php',
        'Amp\\Http\\Server\\DefaultErrorHandler' => __DIR__ . '/..' . '/amphp/http-server/src/DefaultErrorHandler.php',
        'Amp\\Http\\Server\\DefaultExceptionHandler' => __DIR__ . '/..' . '/amphp/http-server/src/DefaultExceptionHandler.php',
        'Amp\\Http\\Server\\Driver\\Client' => __DIR__ . '/..' . '/amphp/http-server/src/Driver/Client.php',
        'Amp\\Http\\Server\\Driver\\ClientFactory' => __DIR__ . '/..' . '/amphp/http-server/src/Driver/ClientFactory.php',
        'Amp\\Http\\Server\\Driver\\ConnectionLimitingClientFactory' => __DIR__ . '/..' . '/amphp/http-server/src/Driver/ConnectionLimitingClientFactory.php',
        'Amp\\Http\\Server\\Driver\\ConnectionLimitingServerSocket' => __DIR__ . '/..' . '/amphp/http-server/src/Driver/ConnectionLimitingServerSocket.php',
        'Amp\\Http\\Server\\Driver\\ConnectionLimitingServerSocketFactory' => __DIR__ . '/..' . '/amphp/http-server/src/Driver/ConnectionLimitingServerSocketFactory.php',
        'Amp\\Http\\Server\\Driver\\DefaultHttpDriverFactory' => __DIR__ . '/..' . '/amphp/http-server/src/Driver/DefaultHttpDriverFactory.php',
        'Amp\\Http\\Server\\Driver\\Http1Driver' => __DIR__ . '/..' . '/amphp/http-server/src/Driver/Http1Driver.php',
        'Amp\\Http\\Server\\Driver\\Http2Driver' => __DIR__ . '/..' . '/amphp/http-server/src/Driver/Http2Driver.php',
        'Amp\\Http\\Server\\Driver\\HttpDriver' => __DIR__ . '/..' . '/amphp/http-server/src/Driver/HttpDriver.php',
        'Amp\\Http\\Server\\Driver\\HttpDriverFactory' => __DIR__ . '/..' . '/amphp/http-server/src/Driver/HttpDriverFactory.php',
        'Amp\\Http\\Server\\Driver\\Internal\\AbstractHttpDriver' => __DIR__ . '/..' . '/amphp/http-server/src/Driver/Internal/AbstractHttpDriver.php',
        'Amp\\Http\\Server\\Driver\\Internal\\Http2Stream' => __DIR__ . '/..' . '/amphp/http-server/src/Driver/Internal/Http2Stream.php',
        'Amp\\Http\\Server\\Driver\\Internal\\HttpDriverErrorHandler' => __DIR__ . '/..' . '/amphp/http-server/src/Driver/Internal/HttpDriverErrorHandler.php',
        'Amp\\Http\\Server\\Driver\\Internal\\StreamTimeoutTracker' => __DIR__ . '/..' . '/amphp/http-server/src/Driver/Internal/StreamTimeoutTracker.php',
        'Amp\\Http\\Server\\Driver\\Internal\\TimeoutQueue' => __DIR__ . '/..' . '/amphp/http-server/src/Driver/Internal/TimeoutQueue.php',
        'Amp\\Http\\Server\\Driver\\LoggingSocketClientFactory' => __DIR__ . '/..' . '/amphp/http-server/src/Driver/LoggingSocketClientFactory.php',
        'Amp\\Http\\Server\\Driver\\SocketClient' => __DIR__ . '/..' . '/amphp/http-server/src/Driver/SocketClient.php',
        'Amp\\Http\\Server\\Driver\\SocketClientFactory' => __DIR__ . '/..' . '/amphp/http-server/src/Driver/SocketClientFactory.php',
        'Amp\\Http\\Server\\Driver\\UpgradedSocket' => __DIR__ . '/..' . '/amphp/http-server/src/Driver/UpgradedSocket.php',
        'Amp\\Http\\Server\\ErrorHandler' => __DIR__ . '/..' . '/amphp/http-server/src/ErrorHandler.php',
        'Amp\\Http\\Server\\ExceptionHandler' => __DIR__ . '/..' . '/amphp/http-server/src/ExceptionHandler.php',
        'Amp\\Http\\Server\\HttpErrorException' => __DIR__ . '/..' . '/amphp/http-server/src/HttpErrorException.php',
        'Amp\\Http\\Server\\HttpServer' => __DIR__ . '/..' . '/amphp/http-server/src/HttpServer.php',
        'Amp\\Http\\Server\\HttpServerStatus' => __DIR__ . '/..' . '/amphp/http-server/src/HttpServerStatus.php',
        'Amp\\Http\\Server\\Middleware' => __DIR__ . '/..' . '/amphp/http-server/src/Middleware.php',
        'Amp\\Http\\Server\\Middleware\\AccessLoggerMiddleware' => __DIR__ . '/..' . '/amphp/http-server/src/Middleware/AccessLoggerMiddleware.php',
        'Amp\\Http\\Server\\Middleware\\AllowedMethodsMiddleware' => __DIR__ . '/..' . '/amphp/http-server/src/Middleware/AllowedMethodsMiddleware.php',
        'Amp\\Http\\Server\\Middleware\\ClosureMiddleware' => __DIR__ . '/..' . '/amphp/http-server/src/Middleware/ClosureMiddleware.php',
        'Amp\\Http\\Server\\Middleware\\CompressionMiddleware' => __DIR__ . '/..' . '/amphp/http-server/src/Middleware/CompressionMiddleware.php',
        'Amp\\Http\\Server\\Middleware\\ConcurrencyLimitingMiddleware' => __DIR__ . '/..' . '/amphp/http-server/src/Middleware/ConcurrencyLimitingMiddleware.php',
        'Amp\\Http\\Server\\Middleware\\ExceptionHandlerMiddleware' => __DIR__ . '/..' . '/amphp/http-server/src/Middleware/ExceptionHandlerMiddleware.php',
        'Amp\\Http\\Server\\Middleware\\Forwarded' => __DIR__ . '/..' . '/amphp/http-server/src/Middleware/Forwarded.php',
        'Amp\\Http\\Server\\Middleware\\ForwardedHeaderType' => __DIR__ . '/..' . '/amphp/http-server/src/Middleware/ForwardedHeaderType.php',
        'Amp\\Http\\Server\\Middleware\\ForwardedMiddleware' => __DIR__ . '/..' . '/amphp/http-server/src/Middleware/ForwardedMiddleware.php',
        'Amp\\Http\\Server\\Middleware\\Internal\\MiddlewareRequestHandler' => __DIR__ . '/..' . '/amphp/http-server/src/Middleware/Internal/MiddlewareRequestHandler.php',
        'Amp\\Http\\Server\\MissingAttributeError' => __DIR__ . '/..' . '/amphp/http-server/src/MissingAttributeError.php',
        'Amp\\Http\\Server\\Push' => __DIR__ . '/..' . '/amphp/http-server/src/Push.php',
        'Amp\\Http\\Server\\Request' => __DIR__ . '/..' . '/amphp/http-server/src/Request.php',
        'Amp\\Http\\Server\\RequestBody' => __DIR__ . '/..' . '/amphp/http-server/src/RequestBody.php',
        'Amp\\Http\\Server\\RequestHandler' => __DIR__ . '/..' . '/amphp/http-server/src/RequestHandler.php',
        'Amp\\Http\\Server\\RequestHandler\\ClosureRequestHandler' => __DIR__ . '/..' . '/amphp/http-server/src/RequestHandler/ClosureRequestHandler.php',
        'Amp\\Http\\Server\\Response' => __DIR__ . '/..' . '/amphp/http-server/src/Response.php',
        'Amp\\Http\\Server\\SocketHttpServer' => __DIR__ . '/..' . '/amphp/http-server/src/SocketHttpServer.php',
        'Amp\\Http\\Server\\Trailers' => __DIR__ . '/..' . '/amphp/http-server/src/Trailers.php',
        'Amp\\Internal\\Cancellable' => __DIR__ . '/..' . '/amphp/amp/src/Internal/Cancellable.php',
        'Amp\\Internal\\FutureIterator' => __DIR__ . '/..' . '/amphp/amp/src/Internal/FutureIterator.php',
        'Amp\\Internal\\FutureIteratorQueue' => __DIR__ . '/..' . '/amphp/amp/src/Internal/FutureIteratorQueue.php',
        'Amp\\Internal\\FutureState' => __DIR__ . '/..' . '/amphp/amp/src/Internal/FutureState.php',
        'Amp\\Internal\\WrappedCancellation' => __DIR__ . '/..' . '/amphp/amp/src/Internal/WrappedCancellation.php',
        'Amp\\Interval' => __DIR__ . '/..' . '/amphp/amp/src/Interval.php',
        'Amp\\Ipc\\IpcServer' => __DIR__ . '/..' . '/danog/ipc/lib/IpcServer.php',
        'Amp\\Ipc\\IpcServerException' => __DIR__ . '/..' . '/danog/ipc/lib/IpcServerException.php',
        'Amp\\Ipc\\PendingAcceptError' => __DIR__ . '/..' . '/danog/ipc/lib/PendingAcceptError.php',
        'Amp\\Ipc\\Sync\\Channel' => __DIR__ . '/..' . '/danog/ipc/lib/Sync/Channel.php',
        'Amp\\Ipc\\Sync\\ChannelCloseReq' => __DIR__ . '/..' . '/danog/ipc/lib/Sync/ChannelCloseReq.php',
        'Amp\\Ipc\\Sync\\ChannelException' => __DIR__ . '/..' . '/danog/ipc/lib/Sync/ChannelException.php',
        'Amp\\Ipc\\Sync\\ChannelInitAck' => __DIR__ . '/..' . '/danog/ipc/lib/Sync/ChannelInitAck.php',
        'Amp\\Ipc\\Sync\\ChannelParser' => __DIR__ . '/..' . '/danog/ipc/lib/Sync/ChannelParser.php',
        'Amp\\Ipc\\Sync\\ChannelledSocket' => __DIR__ . '/..' . '/danog/ipc/lib/Sync/ChannelledSocket.php',
        'Amp\\Ipc\\Sync\\PanicError' => __DIR__ . '/..' . '/danog/ipc/lib/Sync/PanicError.php',
        'Amp\\Ipc\\Sync\\SerializationException' => __DIR__ . '/..' . '/danog/ipc/lib/Sync/SerializationException.php',
        'Amp\\Ipc\\Sync\\SynchronizationError' => __DIR__ . '/..' . '/danog/ipc/lib/Sync/SynchronizationError.php',
        'Amp\\Log\\ConsoleFormatter' => __DIR__ . '/..' . '/amphp/log/src/ConsoleFormatter.php',
        'Amp\\Log\\StreamHandler' => __DIR__ . '/..' . '/amphp/log/src/StreamHandler.php',
        'Amp\\Mysql\\Internal\\ConnectionProcessor' => __DIR__ . '/..' . '/amphp/mysql/src/Internal/ConnectionProcessor.php',
        'Amp\\Mysql\\Internal\\ConnectionState' => __DIR__ . '/..' . '/amphp/mysql/src/Internal/ConnectionState.php',
        'Amp\\Mysql\\Internal\\MysqlCommandResult' => __DIR__ . '/..' . '/amphp/mysql/src/Internal/MysqlCommandResult.php',
        'Amp\\Mysql\\Internal\\MysqlConnectionMetadata' => __DIR__ . '/..' . '/amphp/mysql/src/Internal/MysqlConnectionMetadata.php',
        'Amp\\Mysql\\Internal\\MysqlConnectionResult' => __DIR__ . '/..' . '/amphp/mysql/src/Internal/MysqlConnectionResult.php',
        'Amp\\Mysql\\Internal\\MysqlConnectionStatement' => __DIR__ . '/..' . '/amphp/mysql/src/Internal/MysqlConnectionStatement.php',
        'Amp\\Mysql\\Internal\\MysqlConnectionTransaction' => __DIR__ . '/..' . '/amphp/mysql/src/Internal/MysqlConnectionTransaction.php',
        'Amp\\Mysql\\Internal\\MysqlEncodedValue' => __DIR__ . '/..' . '/amphp/mysql/src/Internal/MysqlEncodedValue.php',
        'Amp\\Mysql\\Internal\\MysqlNestableExecutor' => __DIR__ . '/..' . '/amphp/mysql/src/Internal/MysqlNestableExecutor.php',
        'Amp\\Mysql\\Internal\\MysqlNestedTransaction' => __DIR__ . '/..' . '/amphp/mysql/src/Internal/MysqlNestedTransaction.php',
        'Amp\\Mysql\\Internal\\MysqlPooledResult' => __DIR__ . '/..' . '/amphp/mysql/src/Internal/MysqlPooledResult.php',
        'Amp\\Mysql\\Internal\\MysqlPooledStatement' => __DIR__ . '/..' . '/amphp/mysql/src/Internal/MysqlPooledStatement.php',
        'Amp\\Mysql\\Internal\\MysqlPooledTransaction' => __DIR__ . '/..' . '/amphp/mysql/src/Internal/MysqlPooledTransaction.php',
        'Amp\\Mysql\\Internal\\MysqlResultProxy' => __DIR__ . '/..' . '/amphp/mysql/src/Internal/MysqlResultProxy.php',
        'Amp\\Mysql\\Internal\\MysqlResultProxyState' => __DIR__ . '/..' . '/amphp/mysql/src/Internal/MysqlResultProxyState.php',
        'Amp\\Mysql\\Internal\\MysqlStatementCreationDelegate' => __DIR__ . '/..' . '/amphp/mysql/src/Internal/MysqlStatementCreationDelegate.php',
        'Amp\\Mysql\\Internal\\MysqlStatementPool' => __DIR__ . '/..' . '/amphp/mysql/src/Internal/MysqlStatementPool.php',
        'Amp\\Mysql\\Internal\\MysqlTransactionDelegate' => __DIR__ . '/..' . '/amphp/mysql/src/Internal/MysqlTransactionDelegate.php',
        'Amp\\Mysql\\Internal\\PublicKeyCache' => __DIR__ . '/..' . '/amphp/mysql/src/Internal/PublicKeyCache.php',
        'Amp\\Mysql\\Internal\\SessionStateType' => __DIR__ . '/..' . '/amphp/mysql/src/Internal/SessionStateType.php',
        'Amp\\Mysql\\MysqlColumnDefinition' => __DIR__ . '/..' . '/amphp/mysql/src/MysqlColumnDefinition.php',
        'Amp\\Mysql\\MysqlConfig' => __DIR__ . '/..' . '/amphp/mysql/src/MysqlConfig.php',
        'Amp\\Mysql\\MysqlConnection' => __DIR__ . '/..' . '/amphp/mysql/src/MysqlConnection.php',
        'Amp\\Mysql\\MysqlConnectionPool' => __DIR__ . '/..' . '/amphp/mysql/src/MysqlConnectionPool.php',
        'Amp\\Mysql\\MysqlDataType' => __DIR__ . '/..' . '/amphp/mysql/src/MysqlDataType.php',
        'Amp\\Mysql\\MysqlExecutor' => __DIR__ . '/..' . '/amphp/mysql/src/MysqlExecutor.php',
        'Amp\\Mysql\\MysqlLink' => __DIR__ . '/..' . '/amphp/mysql/src/MysqlLink.php',
        'Amp\\Mysql\\MysqlResult' => __DIR__ . '/..' . '/amphp/mysql/src/MysqlResult.php',
        'Amp\\Mysql\\MysqlStatement' => __DIR__ . '/..' . '/amphp/mysql/src/MysqlStatement.php',
        'Amp\\Mysql\\MysqlTransaction' => __DIR__ . '/..' . '/amphp/mysql/src/MysqlTransaction.php',
        'Amp\\Mysql\\SocketMysqlConnection' => __DIR__ . '/..' . '/amphp/mysql/src/SocketMysqlConnection.php',
        'Amp\\Mysql\\SocketMysqlConnector' => __DIR__ . '/..' . '/amphp/mysql/src/SocketMysqlConnector.php',
        'Amp\\NullCancellation' => __DIR__ . '/..' . '/amphp/amp/src/NullCancellation.php',
        'Amp\\Parallel\\Context\\Context' => __DIR__ . '/..' . '/amphp/parallel/src/Context/Context.php',
        'Amp\\Parallel\\Context\\ContextException' => __DIR__ . '/..' . '/amphp/parallel/src/Context/ContextException.php',
        'Amp\\Parallel\\Context\\ContextFactory' => __DIR__ . '/..' . '/amphp/parallel/src/Context/ContextFactory.php',
        'Amp\\Parallel\\Context\\ContextPanicError' => __DIR__ . '/..' . '/amphp/parallel/src/Context/ContextPanicError.php',
        'Amp\\Parallel\\Context\\DefaultContextFactory' => __DIR__ . '/..' . '/amphp/parallel/src/Context/DefaultContextFactory.php',
        'Amp\\Parallel\\Context\\ForkContext' => __DIR__ . '/..' . '/amphp/parallel/src/Context/ForkContext.php',
        'Amp\\Parallel\\Context\\ForkContextFactory' => __DIR__ . '/..' . '/amphp/parallel/src/Context/ForkContextFactory.php',
        'Amp\\Parallel\\Context\\Internal\\AbstractContext' => __DIR__ . '/..' . '/amphp/parallel/src/Context/Internal/AbstractContext.php',
        'Amp\\Parallel\\Context\\Internal\\ContextChannel' => __DIR__ . '/..' . '/amphp/parallel/src/Context/Internal/ContextChannel.php',
        'Amp\\Parallel\\Context\\Internal\\ContextException' => __DIR__ . '/..' . '/amphp/parallel/src/Context/Internal/ContextException.php',
        'Amp\\Parallel\\Context\\Internal\\ContextMessage' => __DIR__ . '/..' . '/amphp/parallel/src/Context/Internal/ContextMessage.php',
        'Amp\\Parallel\\Context\\Internal\\ExitFailure' => __DIR__ . '/..' . '/amphp/parallel/src/Context/Internal/ExitFailure.php',
        'Amp\\Parallel\\Context\\Internal\\ExitResult' => __DIR__ . '/..' . '/amphp/parallel/src/Context/Internal/ExitResult.php',
        'Amp\\Parallel\\Context\\Internal\\ExitSuccess' => __DIR__ . '/..' . '/amphp/parallel/src/Context/Internal/ExitSuccess.php',
        'Amp\\Parallel\\Context\\Internal\\ParallelHub' => __DIR__ . '/..' . '/amphp/parallel/src/Context/Internal/ParallelHub.php',
        'Amp\\Parallel\\Context\\ProcessContext' => __DIR__ . '/..' . '/amphp/parallel/src/Context/ProcessContext.php',
        'Amp\\Parallel\\Context\\ProcessContextFactory' => __DIR__ . '/..' . '/amphp/parallel/src/Context/ProcessContextFactory.php',
        'Amp\\Parallel\\Context\\StatusError' => __DIR__ . '/..' . '/amphp/parallel/src/Context/StatusError.php',
        'Amp\\Parallel\\Context\\ThreadContext' => __DIR__ . '/..' . '/amphp/parallel/src/Context/ThreadContext.php',
        'Amp\\Parallel\\Context\\ThreadContextFactory' => __DIR__ . '/..' . '/amphp/parallel/src/Context/ThreadContextFactory.php',
        'Amp\\Parallel\\Ipc\\IpcHub' => __DIR__ . '/..' . '/amphp/parallel/src/Ipc/IpcHub.php',
        'Amp\\Parallel\\Ipc\\LocalIpcHub' => __DIR__ . '/..' . '/amphp/parallel/src/Ipc/LocalIpcHub.php',
        'Amp\\Parallel\\Ipc\\SocketIpcHub' => __DIR__ . '/..' . '/amphp/parallel/src/Ipc/SocketIpcHub.php',
        'Amp\\Parallel\\Worker\\ContextWorkerFactory' => __DIR__ . '/..' . '/amphp/parallel/src/Worker/ContextWorkerFactory.php',
        'Amp\\Parallel\\Worker\\ContextWorkerPool' => __DIR__ . '/..' . '/amphp/parallel/src/Worker/ContextWorkerPool.php',
        'Amp\\Parallel\\Worker\\DelegatingWorkerPool' => __DIR__ . '/..' . '/amphp/parallel/src/Worker/DelegatingWorkerPool.php',
        'Amp\\Parallel\\Worker\\Execution' => __DIR__ . '/..' . '/amphp/parallel/src/Worker/Execution.php',
        'Amp\\Parallel\\Worker\\Internal\\ContextWorker' => __DIR__ . '/..' . '/amphp/parallel/src/Worker/Internal/ContextWorker.php',
        'Amp\\Parallel\\Worker\\Internal\\JobCancellation' => __DIR__ . '/..' . '/amphp/parallel/src/Worker/Internal/JobCancellation.php',
        'Amp\\Parallel\\Worker\\Internal\\JobChannel' => __DIR__ . '/..' . '/amphp/parallel/src/Worker/Internal/JobChannel.php',
        'Amp\\Parallel\\Worker\\Internal\\JobMessage' => __DIR__ . '/..' . '/amphp/parallel/src/Worker/Internal/JobMessage.php',
        'Amp\\Parallel\\Worker\\Internal\\JobPacket' => __DIR__ . '/..' . '/amphp/parallel/src/Worker/Internal/JobPacket.php',
        'Amp\\Parallel\\Worker\\Internal\\PooledWorker' => __DIR__ . '/..' . '/amphp/parallel/src/Worker/Internal/PooledWorker.php',
        'Amp\\Parallel\\Worker\\Internal\\TaskCancelled' => __DIR__ . '/..' . '/amphp/parallel/src/Worker/Internal/TaskCancelled.php',
        'Amp\\Parallel\\Worker\\Internal\\TaskExceptionType' => __DIR__ . '/..' . '/amphp/parallel/src/Worker/Internal/TaskExceptionType.php',
        'Amp\\Parallel\\Worker\\Internal\\TaskFailure' => __DIR__ . '/..' . '/amphp/parallel/src/Worker/Internal/TaskFailure.php',
        'Amp\\Parallel\\Worker\\Internal\\TaskResult' => __DIR__ . '/..' . '/amphp/parallel/src/Worker/Internal/TaskResult.php',
        'Amp\\Parallel\\Worker\\Internal\\TaskSubmission' => __DIR__ . '/..' . '/amphp/parallel/src/Worker/Internal/TaskSubmission.php',
        'Amp\\Parallel\\Worker\\Internal\\TaskSuccess' => __DIR__ . '/..' . '/amphp/parallel/src/Worker/Internal/TaskSuccess.php',
        'Amp\\Parallel\\Worker\\LimitedWorkerPool' => __DIR__ . '/..' . '/amphp/parallel/src/Worker/LimitedWorkerPool.php',
        'Amp\\Parallel\\Worker\\Task' => __DIR__ . '/..' . '/amphp/parallel/src/Worker/Task.php',
        'Amp\\Parallel\\Worker\\TaskCancelledException' => __DIR__ . '/..' . '/amphp/parallel/src/Worker/TaskCancelledException.php',
        'Amp\\Parallel\\Worker\\TaskFailureError' => __DIR__ . '/..' . '/amphp/parallel/src/Worker/TaskFailureError.php',
        'Amp\\Parallel\\Worker\\TaskFailureException' => __DIR__ . '/..' . '/amphp/parallel/src/Worker/TaskFailureException.php',
        'Amp\\Parallel\\Worker\\TaskFailureThrowable' => __DIR__ . '/..' . '/amphp/parallel/src/Worker/TaskFailureThrowable.php',
        'Amp\\Parallel\\Worker\\Worker' => __DIR__ . '/..' . '/amphp/parallel/src/Worker/Worker.php',
        'Amp\\Parallel\\Worker\\WorkerException' => __DIR__ . '/..' . '/amphp/parallel/src/Worker/WorkerException.php',
        'Amp\\Parallel\\Worker\\WorkerFactory' => __DIR__ . '/..' . '/amphp/parallel/src/Worker/WorkerFactory.php',
        'Amp\\Parallel\\Worker\\WorkerPool' => __DIR__ . '/..' . '/amphp/parallel/src/Worker/WorkerPool.php',
        'Amp\\Parser\\InvalidDelimiterError' => __DIR__ . '/..' . '/amphp/parser/src/InvalidDelimiterError.php',
        'Amp\\Parser\\Parser' => __DIR__ . '/..' . '/amphp/parser/src/Parser.php',
        'Amp\\Pipeline\\ConcurrentIterator' => __DIR__ . '/..' . '/amphp/pipeline/src/ConcurrentIterator.php',
        'Amp\\Pipeline\\DisposedException' => __DIR__ . '/..' . '/amphp/pipeline/src/DisposedException.php',
        'Amp\\Pipeline\\Internal\\ConcurrentArrayIterator' => __DIR__ . '/..' . '/amphp/pipeline/src/Internal/ConcurrentArrayIterator.php',
        'Amp\\Pipeline\\Internal\\ConcurrentChainedIterator' => __DIR__ . '/..' . '/amphp/pipeline/src/Internal/ConcurrentChainedIterator.php',
        'Amp\\Pipeline\\Internal\\ConcurrentClosureIterator' => __DIR__ . '/..' . '/amphp/pipeline/src/Internal/ConcurrentClosureIterator.php',
        'Amp\\Pipeline\\Internal\\ConcurrentFlatMapIterator' => __DIR__ . '/..' . '/amphp/pipeline/src/Internal/ConcurrentFlatMapIterator.php',
        'Amp\\Pipeline\\Internal\\ConcurrentIterableIterator' => __DIR__ . '/..' . '/amphp/pipeline/src/Internal/ConcurrentIterableIterator.php',
        'Amp\\Pipeline\\Internal\\ConcurrentMergedIterator' => __DIR__ . '/..' . '/amphp/pipeline/src/Internal/ConcurrentMergedIterator.php',
        'Amp\\Pipeline\\Internal\\ConcurrentQueueIterator' => __DIR__ . '/..' . '/amphp/pipeline/src/Internal/ConcurrentQueueIterator.php',
        'Amp\\Pipeline\\Internal\\FlatMapOperation' => __DIR__ . '/..' . '/amphp/pipeline/src/Internal/FlatMapOperation.php',
        'Amp\\Pipeline\\Internal\\IntermediateOperation' => __DIR__ . '/..' . '/amphp/pipeline/src/Internal/IntermediateOperation.php',
        'Amp\\Pipeline\\Internal\\QueueState' => __DIR__ . '/..' . '/amphp/pipeline/src/Internal/QueueState.php',
        'Amp\\Pipeline\\Internal\\Sequence' => __DIR__ . '/..' . '/amphp/pipeline/src/Internal/Sequence.php',
        'Amp\\Pipeline\\Internal\\SortOperation' => __DIR__ . '/..' . '/amphp/pipeline/src/Internal/SortOperation.php',
        'Amp\\Pipeline\\Pipeline' => __DIR__ . '/..' . '/amphp/pipeline/src/Pipeline.php',
        'Amp\\Pipeline\\Queue' => __DIR__ . '/..' . '/amphp/pipeline/src/Queue.php',
        'Amp\\Postgres\\DefaultPostgresConnector' => __DIR__ . '/..' . '/amphp/postgres/src/DefaultPostgresConnector.php',
        'Amp\\Postgres\\Internal\\AbstractHandle' => __DIR__ . '/..' . '/amphp/postgres/src/Internal/AbstractHandle.php',
        'Amp\\Postgres\\Internal\\ArrayParser' => __DIR__ . '/..' . '/amphp/postgres/src/Internal/ArrayParser.php',
        'Amp\\Postgres\\Internal\\PgSqlHandle' => __DIR__ . '/..' . '/amphp/postgres/src/Internal/PgSqlHandle.php',
        'Amp\\Postgres\\Internal\\PgSqlResultIterator' => __DIR__ . '/..' . '/amphp/postgres/src/Internal/PgSqlResultIterator.php',
        'Amp\\Postgres\\Internal\\PgSqlResultSet' => __DIR__ . '/..' . '/amphp/postgres/src/Internal/PgSqlResultSet.php',
        'Amp\\Postgres\\Internal\\PgSqlType' => __DIR__ . '/..' . '/amphp/postgres/src/Internal/PgSqlType.php',
        'Amp\\Postgres\\Internal\\PostgresCommandResult' => __DIR__ . '/..' . '/amphp/postgres/src/Internal/PostgresCommandResult.php',
        'Amp\\Postgres\\Internal\\PostgresConnectionListener' => __DIR__ . '/..' . '/amphp/postgres/src/Internal/PostgresConnectionListener.php',
        'Amp\\Postgres\\Internal\\PostgresConnectionStatement' => __DIR__ . '/..' . '/amphp/postgres/src/Internal/PostgresConnectionStatement.php',
        'Amp\\Postgres\\Internal\\PostgresConnectionTransaction' => __DIR__ . '/..' . '/amphp/postgres/src/Internal/PostgresConnectionTransaction.php',
        'Amp\\Postgres\\Internal\\PostgresHandle' => __DIR__ . '/..' . '/amphp/postgres/src/Internal/PostgresHandle.php',
        'Amp\\Postgres\\Internal\\PostgresHandleConnection' => __DIR__ . '/..' . '/amphp/postgres/src/Internal/PostgresHandleConnection.php',
        'Amp\\Postgres\\Internal\\PostgresNestedTransaction' => __DIR__ . '/..' . '/amphp/postgres/src/Internal/PostgresNestedTransaction.php',
        'Amp\\Postgres\\Internal\\PostgresPooledListener' => __DIR__ . '/..' . '/amphp/postgres/src/Internal/PostgresPooledListener.php',
        'Amp\\Postgres\\Internal\\PostgresPooledResult' => __DIR__ . '/..' . '/amphp/postgres/src/Internal/PostgresPooledResult.php',
        'Amp\\Postgres\\Internal\\PostgresPooledStatement' => __DIR__ . '/..' . '/amphp/postgres/src/Internal/PostgresPooledStatement.php',
        'Amp\\Postgres\\Internal\\PostgresPooledTransaction' => __DIR__ . '/..' . '/amphp/postgres/src/Internal/PostgresPooledTransaction.php',
        'Amp\\Postgres\\Internal\\PostgresStatementPool' => __DIR__ . '/..' . '/amphp/postgres/src/Internal/PostgresStatementPool.php',
        'Amp\\Postgres\\Internal\\PostgresTransactionDelegate' => __DIR__ . '/..' . '/amphp/postgres/src/Internal/PostgresTransactionDelegate.php',
        'Amp\\Postgres\\Internal\\PqBufferedResultSet' => __DIR__ . '/..' . '/amphp/postgres/src/Internal/PqBufferedResultSet.php',
        'Amp\\Postgres\\Internal\\PqHandle' => __DIR__ . '/..' . '/amphp/postgres/src/Internal/PqHandle.php',
        'Amp\\Postgres\\Internal\\PqUnbufferedResultSet' => __DIR__ . '/..' . '/amphp/postgres/src/Internal/PqUnbufferedResultSet.php',
        'Amp\\Postgres\\Internal\\StatementStorage' => __DIR__ . '/..' . '/amphp/postgres/src/Internal/StatementStorage.php',
        'Amp\\Postgres\\PgSqlConnection' => __DIR__ . '/..' . '/amphp/postgres/src/PgSqlConnection.php',
        'Amp\\Postgres\\PostgresArray' => __DIR__ . '/..' . '/amphp/postgres/src/PostgresArray.php',
        'Amp\\Postgres\\PostgresByteA' => __DIR__ . '/..' . '/amphp/postgres/src/PostgresByteA.php',
        'Amp\\Postgres\\PostgresConfig' => __DIR__ . '/..' . '/amphp/postgres/src/PostgresConfig.php',
        'Amp\\Postgres\\PostgresConnection' => __DIR__ . '/..' . '/amphp/postgres/src/PostgresConnection.php',
        'Amp\\Postgres\\PostgresConnectionPool' => __DIR__ . '/..' . '/amphp/postgres/src/PostgresConnectionPool.php',
        'Amp\\Postgres\\PostgresExecutor' => __DIR__ . '/..' . '/amphp/postgres/src/PostgresExecutor.php',
        'Amp\\Postgres\\PostgresLink' => __DIR__ . '/..' . '/amphp/postgres/src/PostgresLink.php',
        'Amp\\Postgres\\PostgresListener' => __DIR__ . '/..' . '/amphp/postgres/src/PostgresListener.php',
        'Amp\\Postgres\\PostgresNotification' => __DIR__ . '/..' . '/amphp/postgres/src/PostgresNotification.php',
        'Amp\\Postgres\\PostgresParseException' => __DIR__ . '/..' . '/amphp/postgres/src/PostgresParseException.php',
        'Amp\\Postgres\\PostgresQueryError' => __DIR__ . '/..' . '/amphp/postgres/src/PostgresQueryError.php',
        'Amp\\Postgres\\PostgresResult' => __DIR__ . '/..' . '/amphp/postgres/src/PostgresResult.php',
        'Amp\\Postgres\\PostgresStatement' => __DIR__ . '/..' . '/amphp/postgres/src/PostgresStatement.php',
        'Amp\\Postgres\\PostgresTransaction' => __DIR__ . '/..' . '/amphp/postgres/src/PostgresTransaction.php',
        'Amp\\Postgres\\PqConnection' => __DIR__ . '/..' . '/amphp/postgres/src/PqConnection.php',
        'Amp\\Process\\Internal\\Posix\\PosixHandle' => __DIR__ . '/..' . '/amphp/process/src/Internal/Posix/PosixHandle.php',
        'Amp\\Process\\Internal\\Posix\\PosixRunner' => __DIR__ . '/..' . '/amphp/process/src/Internal/Posix/PosixRunner.php',
        'Amp\\Process\\Internal\\ProcHolder' => __DIR__ . '/..' . '/amphp/process/src/Internal/ProcHolder.php',
        'Amp\\Process\\Internal\\ProcessContext' => __DIR__ . '/..' . '/amphp/process/src/Internal/ProcessContext.php',
        'Amp\\Process\\Internal\\ProcessHandle' => __DIR__ . '/..' . '/amphp/process/src/Internal/ProcessHandle.php',
        'Amp\\Process\\Internal\\ProcessRunner' => __DIR__ . '/..' . '/amphp/process/src/Internal/ProcessRunner.php',
        'Amp\\Process\\Internal\\ProcessStatus' => __DIR__ . '/..' . '/amphp/process/src/Internal/ProcessStatus.php',
        'Amp\\Process\\Internal\\ProcessStreams' => __DIR__ . '/..' . '/amphp/process/src/Internal/ProcessStreams.php',
        'Amp\\Process\\Internal\\Windows\\HandshakeException' => __DIR__ . '/..' . '/amphp/process/src/Internal/Windows/HandshakeException.php',
        'Amp\\Process\\Internal\\Windows\\HandshakeStatus' => __DIR__ . '/..' . '/amphp/process/src/Internal/Windows/HandshakeStatus.php',
        'Amp\\Process\\Internal\\Windows\\SignalCode' => __DIR__ . '/..' . '/amphp/process/src/Internal/Windows/SignalCode.php',
        'Amp\\Process\\Internal\\Windows\\SocketConnector' => __DIR__ . '/..' . '/amphp/process/src/Internal/Windows/SocketConnector.php',
        'Amp\\Process\\Internal\\Windows\\WindowsHandle' => __DIR__ . '/..' . '/amphp/process/src/Internal/Windows/WindowsHandle.php',
        'Amp\\Process\\Internal\\Windows\\WindowsRunner' => __DIR__ . '/..' . '/amphp/process/src/Internal/Windows/WindowsRunner.php',
        'Amp\\Process\\Process' => __DIR__ . '/..' . '/amphp/process/src/Process.php',
        'Amp\\Process\\ProcessException' => __DIR__ . '/..' . '/amphp/process/src/ProcessException.php',
        'Amp\\Redis\\Command\\Boundary\\LexBoundary' => __DIR__ . '/..' . '/amphp/redis/src/Command/Boundary/LexBoundary.php',
        'Amp\\Redis\\Command\\Boundary\\ScoreBoundary' => __DIR__ . '/..' . '/amphp/redis/src/Command/Boundary/ScoreBoundary.php',
        'Amp\\Redis\\Command\\Option\\RangeOptions' => __DIR__ . '/..' . '/amphp/redis/src/Command/Option/RangeOptions.php',
        'Amp\\Redis\\Command\\Option\\SetOptions' => __DIR__ . '/..' . '/amphp/redis/src/Command/Option/SetOptions.php',
        'Amp\\Redis\\Command\\Option\\SortOptions' => __DIR__ . '/..' . '/amphp/redis/src/Command/Option/SortOptions.php',
        'Amp\\Redis\\Command\\RedisHyperLogLog' => __DIR__ . '/..' . '/amphp/redis/src/Command/RedisHyperLogLog.php',
        'Amp\\Redis\\Command\\RedisList' => __DIR__ . '/..' . '/amphp/redis/src/Command/RedisList.php',
        'Amp\\Redis\\Command\\RedisMap' => __DIR__ . '/..' . '/amphp/redis/src/Command/RedisMap.php',
        'Amp\\Redis\\Command\\RedisSet' => __DIR__ . '/..' . '/amphp/redis/src/Command/RedisSet.php',
        'Amp\\Redis\\Command\\RedisSortedSet' => __DIR__ . '/..' . '/amphp/redis/src/Command/RedisSortedSet.php',
        'Amp\\Redis\\Connection\\Authenticator' => __DIR__ . '/..' . '/amphp/redis/src/Connection/Authenticator.php',
        'Amp\\Redis\\Connection\\DatabaseSelector' => __DIR__ . '/..' . '/amphp/redis/src/Connection/DatabaseSelector.php',
        'Amp\\Redis\\Connection\\ReconnectingRedisLink' => __DIR__ . '/..' . '/amphp/redis/src/Connection/ReconnectingRedisLink.php',
        'Amp\\Redis\\Connection\\RedisConnection' => __DIR__ . '/..' . '/amphp/redis/src/Connection/RedisConnection.php',
        'Amp\\Redis\\Connection\\RedisConnectionException' => __DIR__ . '/..' . '/amphp/redis/src/Connection/RedisConnectionException.php',
        'Amp\\Redis\\Connection\\RedisConnector' => __DIR__ . '/..' . '/amphp/redis/src/Connection/RedisConnector.php',
        'Amp\\Redis\\Connection\\RedisLink' => __DIR__ . '/..' . '/amphp/redis/src/Connection/RedisLink.php',
        'Amp\\Redis\\Connection\\SocketRedisConnection' => __DIR__ . '/..' . '/amphp/redis/src/Connection/SocketRedisConnection.php',
        'Amp\\Redis\\Connection\\SocketRedisConnector' => __DIR__ . '/..' . '/amphp/redis/src/Connection/SocketRedisConnector.php',
        'Amp\\Redis\\Protocol\\ProtocolException' => __DIR__ . '/..' . '/amphp/redis/src/Protocol/ProtocolException.php',
        'Amp\\Redis\\Protocol\\QueryException' => __DIR__ . '/..' . '/amphp/redis/src/Protocol/QueryException.php',
        'Amp\\Redis\\Protocol\\RedisError' => __DIR__ . '/..' . '/amphp/redis/src/Protocol/RedisError.php',
        'Amp\\Redis\\Protocol\\RedisResponse' => __DIR__ . '/..' . '/amphp/redis/src/Protocol/RedisResponse.php',
        'Amp\\Redis\\Protocol\\RedisValue' => __DIR__ . '/..' . '/amphp/redis/src/Protocol/RedisValue.php',
        'Amp\\Redis\\Protocol\\RespParser' => __DIR__ . '/..' . '/amphp/redis/src/Protocol/RespParser.php',
        'Amp\\Redis\\RedisCache' => __DIR__ . '/..' . '/amphp/redis/src/RedisCache.php',
        'Amp\\Redis\\RedisClient' => __DIR__ . '/..' . '/amphp/redis/src/RedisClient.php',
        'Amp\\Redis\\RedisConfig' => __DIR__ . '/..' . '/amphp/redis/src/RedisConfig.php',
        'Amp\\Redis\\RedisException' => __DIR__ . '/..' . '/amphp/redis/src/RedisException.php',
        'Amp\\Redis\\RedisSubscriber' => __DIR__ . '/..' . '/amphp/redis/src/RedisSubscriber.php',
        'Amp\\Redis\\RedisSubscription' => __DIR__ . '/..' . '/amphp/redis/src/RedisSubscription.php',
        'Amp\\Redis\\Sync\\RedisMutex' => __DIR__ . '/..' . '/amphp/redis/src/Sync/RedisMutex.php',
        'Amp\\Redis\\Sync\\RedisMutexException' => __DIR__ . '/..' . '/amphp/redis/src/Sync/RedisMutexException.php',
        'Amp\\Redis\\Sync\\RedisMutexOptions' => __DIR__ . '/..' . '/amphp/redis/src/Sync/RedisMutexOptions.php',
        'Amp\\Redis\\Sync\\RedisParcel' => __DIR__ . '/..' . '/amphp/redis/src/Sync/RedisParcel.php',
        'Amp\\Serialization\\CompressingSerializer' => __DIR__ . '/..' . '/amphp/serialization/src/CompressingSerializer.php',
        'Amp\\Serialization\\JsonSerializer' => __DIR__ . '/..' . '/amphp/serialization/src/JsonSerializer.php',
        'Amp\\Serialization\\NativeSerializer' => __DIR__ . '/..' . '/amphp/serialization/src/NativeSerializer.php',
        'Amp\\Serialization\\PassthroughSerializer' => __DIR__ . '/..' . '/amphp/serialization/src/PassthroughSerializer.php',
        'Amp\\Serialization\\SerializationException' => __DIR__ . '/..' . '/amphp/serialization/src/SerializationException.php',
        'Amp\\Serialization\\Serializer' => __DIR__ . '/..' . '/amphp/serialization/src/Serializer.php',
        'Amp\\SignalCancellation' => __DIR__ . '/..' . '/amphp/amp/src/SignalCancellation.php',
        'Amp\\SignalException' => __DIR__ . '/..' . '/amphp/amp/src/SignalException.php',
        'Amp\\Socket\\BindContext' => __DIR__ . '/..' . '/amphp/socket/src/BindContext.php',
        'Amp\\Socket\\Certificate' => __DIR__ . '/..' . '/amphp/socket/src/Certificate.php',
        'Amp\\Socket\\CidrMatcher' => __DIR__ . '/..' . '/amphp/socket/src/CidrMatcher.php',
        'Amp\\Socket\\ClientTlsContext' => __DIR__ . '/..' . '/amphp/socket/src/ClientTlsContext.php',
        'Amp\\Socket\\ConnectContext' => __DIR__ . '/..' . '/amphp/socket/src/ConnectContext.php',
        'Amp\\Socket\\ConnectException' => __DIR__ . '/..' . '/amphp/socket/src/ConnectException.php',
        'Amp\\Socket\\DnsSocketConnector' => __DIR__ . '/..' . '/amphp/socket/src/DnsSocketConnector.php',
        'Amp\\Socket\\InternetAddress' => __DIR__ . '/..' . '/amphp/socket/src/InternetAddress.php',
        'Amp\\Socket\\InternetAddressVersion' => __DIR__ . '/..' . '/amphp/socket/src/InternetAddressVersion.php',
        'Amp\\Socket\\PendingAcceptError' => __DIR__ . '/..' . '/amphp/socket/src/PendingAcceptError.php',
        'Amp\\Socket\\PendingReceiveError' => __DIR__ . '/..' . '/amphp/socket/src/PendingReceiveError.php',
        'Amp\\Socket\\ResourceServerSocket' => __DIR__ . '/..' . '/amphp/socket/src/ResourceServerSocket.php',
        'Amp\\Socket\\ResourceServerSocketFactory' => __DIR__ . '/..' . '/amphp/socket/src/ResourceServerSocketFactory.php',
        'Amp\\Socket\\ResourceSocket' => __DIR__ . '/..' . '/amphp/socket/src/ResourceSocket.php',
        'Amp\\Socket\\ResourceUdpSocket' => __DIR__ . '/..' . '/amphp/socket/src/ResourceUdpSocket.php',
        'Amp\\Socket\\RetrySocketConnector' => __DIR__ . '/..' . '/amphp/socket/src/RetrySocketConnector.php',
        'Amp\\Socket\\ServerSocket' => __DIR__ . '/..' . '/amphp/socket/src/ServerSocket.php',
        'Amp\\Socket\\ServerSocketFactory' => __DIR__ . '/..' . '/amphp/socket/src/ServerSocketFactory.php',
        'Amp\\Socket\\ServerTlsContext' => __DIR__ . '/..' . '/amphp/socket/src/ServerTlsContext.php',
        'Amp\\Socket\\Socket' => __DIR__ . '/..' . '/amphp/socket/src/Socket.php',
        'Amp\\Socket\\SocketAddress' => __DIR__ . '/..' . '/amphp/socket/src/SocketAddress.php',
        'Amp\\Socket\\SocketAddressType' => __DIR__ . '/..' . '/amphp/socket/src/SocketAddressType.php',
        'Amp\\Socket\\SocketConnector' => __DIR__ . '/..' . '/amphp/socket/src/SocketConnector.php',
        'Amp\\Socket\\SocketException' => __DIR__ . '/..' . '/amphp/socket/src/SocketException.php',
        'Amp\\Socket\\SocketPool' => __DIR__ . '/..' . '/amphp/socket/src/SocketPool.php',
        'Amp\\Socket\\Socks5SocketConnector' => __DIR__ . '/..' . '/amphp/socket/src/Socks5SocketConnector.php',
        'Amp\\Socket\\StaticSocketConnector' => __DIR__ . '/..' . '/amphp/socket/src/StaticSocketConnector.php',
        'Amp\\Socket\\TlsException' => __DIR__ . '/..' . '/amphp/socket/src/TlsException.php',
        'Amp\\Socket\\TlsInfo' => __DIR__ . '/..' . '/amphp/socket/src/TlsInfo.php',
        'Amp\\Socket\\TlsState' => __DIR__ . '/..' . '/amphp/socket/src/TlsState.php',
        'Amp\\Socket\\UdpSocket' => __DIR__ . '/..' . '/amphp/socket/src/UdpSocket.php',
        'Amp\\Socket\\UnixAddress' => __DIR__ . '/..' . '/amphp/socket/src/UnixAddress.php',
        'Amp\\Socket\\UnlimitedSocketPool' => __DIR__ . '/..' . '/amphp/socket/src/UnlimitedSocketPool.php',
        'Amp\\Sql\\Common\\RetrySqlConnector' => __DIR__ . '/..' . '/amphp/sql-common/src/RetrySqlConnector.php',
        'Amp\\Sql\\Common\\SqlCommandResult' => __DIR__ . '/..' . '/amphp/sql-common/src/SqlCommandResult.php',
        'Amp\\Sql\\Common\\SqlCommonConnectionPool' => __DIR__ . '/..' . '/amphp/sql-common/src/SqlCommonConnectionPool.php',
        'Amp\\Sql\\Common\\SqlConnectionTransaction' => __DIR__ . '/..' . '/amphp/sql-common/src/SqlConnectionTransaction.php',
        'Amp\\Sql\\Common\\SqlNestableTransactionExecutor' => __DIR__ . '/..' . '/amphp/sql-common/src/SqlNestableTransactionExecutor.php',
        'Amp\\Sql\\Common\\SqlNestedTransaction' => __DIR__ . '/..' . '/amphp/sql-common/src/SqlNestedTransaction.php',
        'Amp\\Sql\\Common\\SqlPooledResult' => __DIR__ . '/..' . '/amphp/sql-common/src/SqlPooledResult.php',
        'Amp\\Sql\\Common\\SqlPooledStatement' => __DIR__ . '/..' . '/amphp/sql-common/src/SqlPooledStatement.php',
        'Amp\\Sql\\Common\\SqlPooledTransaction' => __DIR__ . '/..' . '/amphp/sql-common/src/SqlPooledTransaction.php',
        'Amp\\Sql\\Common\\SqlStatementPool' => __DIR__ . '/..' . '/amphp/sql-common/src/SqlStatementPool.php',
        'Amp\\Sql\\SqlConfig' => __DIR__ . '/..' . '/amphp/sql/src/SqlConfig.php',
        'Amp\\Sql\\SqlConnection' => __DIR__ . '/..' . '/amphp/sql/src/SqlConnection.php',
        'Amp\\Sql\\SqlConnectionException' => __DIR__ . '/..' . '/amphp/sql/src/SqlConnectionException.php',
        'Amp\\Sql\\SqlConnectionPool' => __DIR__ . '/..' . '/amphp/sql/src/SqlConnectionPool.php',
        'Amp\\Sql\\SqlConnector' => __DIR__ . '/..' . '/amphp/sql/src/SqlConnector.php',
        'Amp\\Sql\\SqlException' => __DIR__ . '/..' . '/amphp/sql/src/SqlException.php',
        'Amp\\Sql\\SqlExecutor' => __DIR__ . '/..' . '/amphp/sql/src/SqlExecutor.php',
        'Amp\\Sql\\SqlLink' => __DIR__ . '/..' . '/amphp/sql/src/SqlLink.php',
        'Amp\\Sql\\SqlQueryError' => __DIR__ . '/..' . '/amphp/sql/src/SqlQueryError.php',
        'Amp\\Sql\\SqlResult' => __DIR__ . '/..' . '/amphp/sql/src/SqlResult.php',
        'Amp\\Sql\\SqlStatement' => __DIR__ . '/..' . '/amphp/sql/src/SqlStatement.php',
        'Amp\\Sql\\SqlTransaction' => __DIR__ . '/..' . '/amphp/sql/src/SqlTransaction.php',
        'Amp\\Sql\\SqlTransactionError' => __DIR__ . '/..' . '/amphp/sql/src/SqlTransactionError.php',
        'Amp\\Sql\\SqlTransactionIsolation' => __DIR__ . '/..' . '/amphp/sql/src/SqlTransactionIsolation.php',
        'Amp\\Sql\\SqlTransactionIsolationLevel' => __DIR__ . '/..' . '/amphp/sql/src/SqlTransactionIsolationLevel.php',
        'Amp\\Sql\\SqlTransientResource' => __DIR__ . '/..' . '/amphp/sql/src/SqlTransientResource.php',
        'Amp\\Sync\\Barrier' => __DIR__ . '/..' . '/amphp/sync/src/Barrier.php',
        'Amp\\Sync\\Channel' => __DIR__ . '/..' . '/amphp/sync/src/Channel.php',
        'Amp\\Sync\\ChannelException' => __DIR__ . '/..' . '/amphp/sync/src/ChannelException.php',
        'Amp\\Sync\\Internal\\ConcurrentIteratorChannel' => __DIR__ . '/..' . '/amphp/sync/src/Internal/ConcurrentIteratorChannel.php',
        'Amp\\Sync\\KeyedMutex' => __DIR__ . '/..' . '/amphp/sync/src/KeyedMutex.php',
        'Amp\\Sync\\KeyedSemaphore' => __DIR__ . '/..' . '/amphp/sync/src/KeyedSemaphore.php',
        'Amp\\Sync\\LocalKeyedMutex' => __DIR__ . '/..' . '/amphp/sync/src/LocalKeyedMutex.php',
        'Amp\\Sync\\LocalKeyedSemaphore' => __DIR__ . '/..' . '/amphp/sync/src/LocalKeyedSemaphore.php',
        'Amp\\Sync\\LocalMutex' => __DIR__ . '/..' . '/amphp/sync/src/LocalMutex.php',
        'Amp\\Sync\\LocalParcel' => __DIR__ . '/..' . '/amphp/sync/src/LocalParcel.php',
        'Amp\\Sync\\LocalSemaphore' => __DIR__ . '/..' . '/amphp/sync/src/LocalSemaphore.php',
        'Amp\\Sync\\Lock' => __DIR__ . '/..' . '/amphp/sync/src/Lock.php',
        'Amp\\Sync\\Mutex' => __DIR__ . '/..' . '/amphp/sync/src/Mutex.php',
        'Amp\\Sync\\Parcel' => __DIR__ . '/..' . '/amphp/sync/src/Parcel.php',
        'Amp\\Sync\\ParcelException' => __DIR__ . '/..' . '/amphp/sync/src/ParcelException.php',
        'Amp\\Sync\\PosixSemaphore' => __DIR__ . '/..' . '/amphp/sync/src/PosixSemaphore.php',
        'Amp\\Sync\\PrefixedKeyedMutex' => __DIR__ . '/..' . '/amphp/sync/src/PrefixedKeyedMutex.php',
        'Amp\\Sync\\PrefixedKeyedSemaphore' => __DIR__ . '/..' . '/amphp/sync/src/PrefixedKeyedSemaphore.php',
        'Amp\\Sync\\PriorityQueue' => __DIR__ . '/..' . '/amphp/sync/src/PriorityQueue.php',
        'Amp\\Sync\\RateLimitingSemaphore' => __DIR__ . '/..' . '/amphp/sync/src/RateLimitingSemaphore.php',
        'Amp\\Sync\\Semaphore' => __DIR__ . '/..' . '/amphp/sync/src/Semaphore.php',
        'Amp\\Sync\\SemaphoreMutex' => __DIR__ . '/..' . '/amphp/sync/src/SemaphoreMutex.php',
        'Amp\\Sync\\SharedMemoryParcel' => __DIR__ . '/..' . '/amphp/sync/src/SharedMemoryParcel.php',
        'Amp\\Sync\\StaticKeyMutex' => __DIR__ . '/..' . '/amphp/sync/src/StaticKeyMutex.php',
        'Amp\\Sync\\StaticKeySemaphore' => __DIR__ . '/..' . '/amphp/sync/src/StaticKeySemaphore.php',
        'Amp\\Sync\\SyncException' => __DIR__ . '/..' . '/amphp/sync/src/SyncException.php',
        'Amp\\TimeoutCancellation' => __DIR__ . '/..' . '/amphp/amp/src/TimeoutCancellation.php',
        'Amp\\TimeoutException' => __DIR__ . '/..' . '/amphp/amp/src/TimeoutException.php',
        'Amp\\Websocket\\Client\\Rfc6455Connection' => __DIR__ . '/..' . '/amphp/websocket-client/src/Rfc6455Connection.php',
        'Amp\\Websocket\\Client\\Rfc6455ConnectionFactory' => __DIR__ . '/..' . '/amphp/websocket-client/src/Rfc6455ConnectionFactory.php',
        'Amp\\Websocket\\Client\\Rfc6455Connector' => __DIR__ . '/..' . '/amphp/websocket-client/src/Rfc6455Connector.php',
        'Amp\\Websocket\\Client\\WebsocketConnectException' => __DIR__ . '/..' . '/amphp/websocket-client/src/WebsocketConnectException.php',
        'Amp\\Websocket\\Client\\WebsocketConnection' => __DIR__ . '/..' . '/amphp/websocket-client/src/WebsocketConnection.php',
        'Amp\\Websocket\\Client\\WebsocketConnectionFactory' => __DIR__ . '/..' . '/amphp/websocket-client/src/WebsocketConnectionFactory.php',
        'Amp\\Websocket\\Client\\WebsocketConnector' => __DIR__ . '/..' . '/amphp/websocket-client/src/WebsocketConnector.php',
        'Amp\\Websocket\\Client\\WebsocketHandshake' => __DIR__ . '/..' . '/amphp/websocket-client/src/WebsocketHandshake.php',
        'Amp\\Websocket\\Compression\\Rfc7692Compression' => __DIR__ . '/..' . '/amphp/websocket/src/Compression/Rfc7692Compression.php',
        'Amp\\Websocket\\Compression\\Rfc7692CompressionFactory' => __DIR__ . '/..' . '/amphp/websocket/src/Compression/Rfc7692CompressionFactory.php',
        'Amp\\Websocket\\Compression\\WebsocketCompressionContext' => __DIR__ . '/..' . '/amphp/websocket/src/Compression/WebsocketCompressionContext.php',
        'Amp\\Websocket\\Compression\\WebsocketCompressionContextFactory' => __DIR__ . '/..' . '/amphp/websocket/src/Compression/WebsocketCompressionContextFactory.php',
        'Amp\\Websocket\\ConstantRateLimit' => __DIR__ . '/..' . '/amphp/websocket/src/ConstantRateLimit.php',
        'Amp\\Websocket\\Internal\\Rfc6455FrameHandler' => __DIR__ . '/..' . '/amphp/websocket/src/Internal/Rfc6455FrameHandler.php',
        'Amp\\Websocket\\Internal\\WebsocketClientMetadata' => __DIR__ . '/..' . '/amphp/websocket/src/Internal/WebsocketClientMetadata.php',
        'Amp\\Websocket\\Parser\\Rfc6455FrameCompiler' => __DIR__ . '/..' . '/amphp/websocket/src/Parser/Rfc6455FrameCompiler.php',
        'Amp\\Websocket\\Parser\\Rfc6455FrameCompilerFactory' => __DIR__ . '/..' . '/amphp/websocket/src/Parser/Rfc6455FrameCompilerFactory.php',
        'Amp\\Websocket\\Parser\\Rfc6455Parser' => __DIR__ . '/..' . '/amphp/websocket/src/Parser/Rfc6455Parser.php',
        'Amp\\Websocket\\Parser\\Rfc6455ParserFactory' => __DIR__ . '/..' . '/amphp/websocket/src/Parser/Rfc6455ParserFactory.php',
        'Amp\\Websocket\\Parser\\WebsocketFrameCompiler' => __DIR__ . '/..' . '/amphp/websocket/src/Parser/WebsocketFrameCompiler.php',
        'Amp\\Websocket\\Parser\\WebsocketFrameCompilerFactory' => __DIR__ . '/..' . '/amphp/websocket/src/Parser/WebsocketFrameCompilerFactory.php',
        'Amp\\Websocket\\Parser\\WebsocketFrameHandler' => __DIR__ . '/..' . '/amphp/websocket/src/Parser/WebsocketFrameHandler.php',
        'Amp\\Websocket\\Parser\\WebsocketFrameType' => __DIR__ . '/..' . '/amphp/websocket/src/Parser/WebsocketFrameType.php',
        'Amp\\Websocket\\Parser\\WebsocketParser' => __DIR__ . '/..' . '/amphp/websocket/src/Parser/WebsocketParser.php',
        'Amp\\Websocket\\Parser\\WebsocketParserException' => __DIR__ . '/..' . '/amphp/websocket/src/Parser/WebsocketParserException.php',
        'Amp\\Websocket\\Parser\\WebsocketParserFactory' => __DIR__ . '/..' . '/amphp/websocket/src/Parser/WebsocketParserFactory.php',
        'Amp\\Websocket\\PeriodicHeartbeatQueue' => __DIR__ . '/..' . '/amphp/websocket/src/PeriodicHeartbeatQueue.php',
        'Amp\\Websocket\\Rfc6455Client' => __DIR__ . '/..' . '/amphp/websocket/src/Rfc6455Client.php',
        'Amp\\Websocket\\WebsocketClient' => __DIR__ . '/..' . '/amphp/websocket/src/WebsocketClient.php',
        'Amp\\Websocket\\WebsocketCloseCode' => __DIR__ . '/..' . '/amphp/websocket/src/WebsocketCloseCode.php',
        'Amp\\Websocket\\WebsocketCloseInfo' => __DIR__ . '/..' . '/amphp/websocket/src/WebsocketCloseInfo.php',
        'Amp\\Websocket\\WebsocketClosedException' => __DIR__ . '/..' . '/amphp/websocket/src/WebsocketClosedException.php',
        'Amp\\Websocket\\WebsocketCount' => __DIR__ . '/..' . '/amphp/websocket/src/WebsocketCount.php',
        'Amp\\Websocket\\WebsocketException' => __DIR__ . '/..' . '/amphp/websocket/src/WebsocketException.php',
        'Amp\\Websocket\\WebsocketHeartbeatQueue' => __DIR__ . '/..' . '/amphp/websocket/src/WebsocketHeartbeatQueue.php',
        'Amp\\Websocket\\WebsocketMessage' => __DIR__ . '/..' . '/amphp/websocket/src/WebsocketMessage.php',
        'Amp\\Websocket\\WebsocketRateLimit' => __DIR__ . '/..' . '/amphp/websocket/src/WebsocketRateLimit.php',
        'Amp\\Websocket\\WebsocketTimestamp' => __DIR__ . '/..' . '/amphp/websocket/src/WebsocketTimestamp.php',
        'BaconQrCode\\Common\\BitArray' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Common/BitArray.php',
        'BaconQrCode\\Common\\BitMatrix' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Common/BitMatrix.php',
        'BaconQrCode\\Common\\BitUtils' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Common/BitUtils.php',
        'BaconQrCode\\Common\\CharacterSetEci' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Common/CharacterSetEci.php',
        'BaconQrCode\\Common\\EcBlock' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Common/EcBlock.php',
        'BaconQrCode\\Common\\EcBlocks' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Common/EcBlocks.php',
        'BaconQrCode\\Common\\ErrorCorrectionLevel' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Common/ErrorCorrectionLevel.php',
        'BaconQrCode\\Common\\FormatInformation' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Common/FormatInformation.php',
        'BaconQrCode\\Common\\Mode' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Common/Mode.php',
        'BaconQrCode\\Common\\ReedSolomonCodec' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Common/ReedSolomonCodec.php',
        'BaconQrCode\\Common\\Version' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Common/Version.php',
        'BaconQrCode\\Encoder\\BlockPair' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Encoder/BlockPair.php',
        'BaconQrCode\\Encoder\\ByteMatrix' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Encoder/ByteMatrix.php',
        'BaconQrCode\\Encoder\\Encoder' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Encoder/Encoder.php',
        'BaconQrCode\\Encoder\\MaskUtil' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Encoder/MaskUtil.php',
        'BaconQrCode\\Encoder\\MatrixUtil' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Encoder/MatrixUtil.php',
        'BaconQrCode\\Encoder\\QrCode' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Encoder/QrCode.php',
        'BaconQrCode\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Exception/ExceptionInterface.php',
        'BaconQrCode\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Exception/InvalidArgumentException.php',
        'BaconQrCode\\Exception\\OutOfBoundsException' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Exception/OutOfBoundsException.php',
        'BaconQrCode\\Exception\\RuntimeException' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Exception/RuntimeException.php',
        'BaconQrCode\\Exception\\UnexpectedValueException' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Exception/UnexpectedValueException.php',
        'BaconQrCode\\Exception\\WriterException' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Exception/WriterException.php',
        'BaconQrCode\\Renderer\\Color\\Alpha' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Renderer/Color/Alpha.php',
        'BaconQrCode\\Renderer\\Color\\Cmyk' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Renderer/Color/Cmyk.php',
        'BaconQrCode\\Renderer\\Color\\ColorInterface' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Renderer/Color/ColorInterface.php',
        'BaconQrCode\\Renderer\\Color\\Gray' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Renderer/Color/Gray.php',
        'BaconQrCode\\Renderer\\Color\\Rgb' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Renderer/Color/Rgb.php',
        'BaconQrCode\\Renderer\\Eye\\CompositeEye' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Renderer/Eye/CompositeEye.php',
        'BaconQrCode\\Renderer\\Eye\\EyeInterface' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Renderer/Eye/EyeInterface.php',
        'BaconQrCode\\Renderer\\Eye\\ModuleEye' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Renderer/Eye/ModuleEye.php',
        'BaconQrCode\\Renderer\\Eye\\PointyEye' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Renderer/Eye/PointyEye.php',
        'BaconQrCode\\Renderer\\Eye\\SimpleCircleEye' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Renderer/Eye/SimpleCircleEye.php',
        'BaconQrCode\\Renderer\\Eye\\SquareEye' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Renderer/Eye/SquareEye.php',
        'BaconQrCode\\Renderer\\GDLibRenderer' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Renderer/GDLibRenderer.php',
        'BaconQrCode\\Renderer\\ImageRenderer' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Renderer/ImageRenderer.php',
        'BaconQrCode\\Renderer\\Image\\EpsImageBackEnd' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Renderer/Image/EpsImageBackEnd.php',
        'BaconQrCode\\Renderer\\Image\\ImageBackEndInterface' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Renderer/Image/ImageBackEndInterface.php',
        'BaconQrCode\\Renderer\\Image\\ImagickImageBackEnd' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Renderer/Image/ImagickImageBackEnd.php',
        'BaconQrCode\\Renderer\\Image\\SvgImageBackEnd' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Renderer/Image/SvgImageBackEnd.php',
        'BaconQrCode\\Renderer\\Image\\TransformationMatrix' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Renderer/Image/TransformationMatrix.php',
        'BaconQrCode\\Renderer\\Module\\DotsModule' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Renderer/Module/DotsModule.php',
        'BaconQrCode\\Renderer\\Module\\EdgeIterator\\Edge' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Renderer/Module/EdgeIterator/Edge.php',
        'BaconQrCode\\Renderer\\Module\\EdgeIterator\\EdgeIterator' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Renderer/Module/EdgeIterator/EdgeIterator.php',
        'BaconQrCode\\Renderer\\Module\\ModuleInterface' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Renderer/Module/ModuleInterface.php',
        'BaconQrCode\\Renderer\\Module\\RoundnessModule' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Renderer/Module/RoundnessModule.php',
        'BaconQrCode\\Renderer\\Module\\SquareModule' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Renderer/Module/SquareModule.php',
        'BaconQrCode\\Renderer\\Path\\Close' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Renderer/Path/Close.php',
        'BaconQrCode\\Renderer\\Path\\Curve' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Renderer/Path/Curve.php',
        'BaconQrCode\\Renderer\\Path\\EllipticArc' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Renderer/Path/EllipticArc.php',
        'BaconQrCode\\Renderer\\Path\\Line' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Renderer/Path/Line.php',
        'BaconQrCode\\Renderer\\Path\\Move' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Renderer/Path/Move.php',
        'BaconQrCode\\Renderer\\Path\\OperationInterface' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Renderer/Path/OperationInterface.php',
        'BaconQrCode\\Renderer\\Path\\Path' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Renderer/Path/Path.php',
        'BaconQrCode\\Renderer\\PlainTextRenderer' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Renderer/PlainTextRenderer.php',
        'BaconQrCode\\Renderer\\RendererInterface' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Renderer/RendererInterface.php',
        'BaconQrCode\\Renderer\\RendererStyle\\EyeFill' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Renderer/RendererStyle/EyeFill.php',
        'BaconQrCode\\Renderer\\RendererStyle\\Fill' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Renderer/RendererStyle/Fill.php',
        'BaconQrCode\\Renderer\\RendererStyle\\Gradient' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Renderer/RendererStyle/Gradient.php',
        'BaconQrCode\\Renderer\\RendererStyle\\GradientType' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Renderer/RendererStyle/GradientType.php',
        'BaconQrCode\\Renderer\\RendererStyle\\RendererStyle' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Renderer/RendererStyle/RendererStyle.php',
        'BaconQrCode\\Writer' => __DIR__ . '/..' . '/bacon/bacon-qr-code/src/Writer.php',
        'Brick\\Math\\BigDecimal' => __DIR__ . '/..' . '/brick/math/src/BigDecimal.php',
        'Brick\\Math\\BigInteger' => __DIR__ . '/..' . '/brick/math/src/BigInteger.php',
        'Brick\\Math\\BigNumber' => __DIR__ . '/..' . '/brick/math/src/BigNumber.php',
        'Brick\\Math\\BigRational' => __DIR__ . '/..' . '/brick/math/src/BigRational.php',
        'Brick\\Math\\Exception\\DivisionByZeroException' => __DIR__ . '/..' . '/brick/math/src/Exception/DivisionByZeroException.php',
        'Brick\\Math\\Exception\\IntegerOverflowException' => __DIR__ . '/..' . '/brick/math/src/Exception/IntegerOverflowException.php',
        'Brick\\Math\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/brick/math/src/Exception/InvalidArgumentException.php',
        'Brick\\Math\\Exception\\MathException' => __DIR__ . '/..' . '/brick/math/src/Exception/MathException.php',
        'Brick\\Math\\Exception\\NegativeNumberException' => __DIR__ . '/..' . '/brick/math/src/Exception/NegativeNumberException.php',
        'Brick\\Math\\Exception\\NoInverseException' => __DIR__ . '/..' . '/brick/math/src/Exception/NoInverseException.php',
        'Brick\\Math\\Exception\\NumberFormatException' => __DIR__ . '/..' . '/brick/math/src/Exception/NumberFormatException.php',
        'Brick\\Math\\Exception\\PlatformException' => __DIR__ . '/..' . '/brick/math/src/Exception/PlatformException.php',
        'Brick\\Math\\Exception\\RandomSourceException' => __DIR__ . '/..' . '/brick/math/src/Exception/RandomSourceException.php',
        'Brick\\Math\\Exception\\RoundingNecessaryException' => __DIR__ . '/..' . '/brick/math/src/Exception/RoundingNecessaryException.php',
        'Brick\\Math\\Internal\\Calculator' => __DIR__ . '/..' . '/brick/math/src/Internal/Calculator.php',
        'Brick\\Math\\Internal\\CalculatorRegistry' => __DIR__ . '/..' . '/brick/math/src/Internal/CalculatorRegistry.php',
        'Brick\\Math\\Internal\\Calculator\\BcMathCalculator' => __DIR__ . '/..' . '/brick/math/src/Internal/Calculator/BcMathCalculator.php',
        'Brick\\Math\\Internal\\Calculator\\GmpCalculator' => __DIR__ . '/..' . '/brick/math/src/Internal/Calculator/GmpCalculator.php',
        'Brick\\Math\\Internal\\Calculator\\NativeCalculator' => __DIR__ . '/..' . '/brick/math/src/Internal/Calculator/NativeCalculator.php',
        'Brick\\Math\\Internal\\DecimalHelper' => __DIR__ . '/..' . '/brick/math/src/Internal/DecimalHelper.php',
        'Brick\\Math\\Internal\\Safe' => __DIR__ . '/..' . '/brick/math/src/Internal/Safe.php',
        'Brick\\Math\\NumberSyntax' => __DIR__ . '/..' . '/brick/math/src/NumberSyntax.php',
        'Brick\\Math\\RoundingMode' => __DIR__ . '/..' . '/brick/math/src/RoundingMode.php',
        'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
        'DASPRiD\\Enum\\AbstractEnum' => __DIR__ . '/..' . '/dasprid/enum/src/AbstractEnum.php',
        'DASPRiD\\Enum\\EnumMap' => __DIR__ . '/..' . '/dasprid/enum/src/EnumMap.php',
        'DASPRiD\\Enum\\Exception\\CloneNotSupportedException' => __DIR__ . '/..' . '/dasprid/enum/src/Exception/CloneNotSupportedException.php',
        'DASPRiD\\Enum\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/dasprid/enum/src/Exception/ExceptionInterface.php',
        'DASPRiD\\Enum\\Exception\\ExpectationException' => __DIR__ . '/..' . '/dasprid/enum/src/Exception/ExpectationException.php',
        'DASPRiD\\Enum\\Exception\\IllegalArgumentException' => __DIR__ . '/..' . '/dasprid/enum/src/Exception/IllegalArgumentException.php',
        'DASPRiD\\Enum\\Exception\\MismatchException' => __DIR__ . '/..' . '/dasprid/enum/src/Exception/MismatchException.php',
        'DASPRiD\\Enum\\Exception\\SerializeNotSupportedException' => __DIR__ . '/..' . '/dasprid/enum/src/Exception/SerializeNotSupportedException.php',
        'DASPRiD\\Enum\\Exception\\UnserializeNotSupportedException' => __DIR__ . '/..' . '/dasprid/enum/src/Exception/UnserializeNotSupportedException.php',
        'DASPRiD\\Enum\\NullValue' => __DIR__ . '/..' . '/dasprid/enum/src/NullValue.php',
        'DateError' => __DIR__ . '/..' . '/symfony/polyfill-php83/Resources/stubs/DateError.php',
        'DateException' => __DIR__ . '/..' . '/symfony/polyfill-php83/Resources/stubs/DateException.php',
        'DateInvalidOperationException' => __DIR__ . '/..' . '/symfony/polyfill-php83/Resources/stubs/DateInvalidOperationException.php',
        'DateInvalidTimeZoneException' => __DIR__ . '/..' . '/symfony/polyfill-php83/Resources/stubs/DateInvalidTimeZoneException.php',
        'DateMalformedIntervalStringException' => __DIR__ . '/..' . '/symfony/polyfill-php83/Resources/stubs/DateMalformedIntervalStringException.php',
        'DateMalformedPeriodStringException' => __DIR__ . '/..' . '/symfony/polyfill-php83/Resources/stubs/DateMalformedPeriodStringException.php',
        'DateMalformedStringException' => __DIR__ . '/..' . '/symfony/polyfill-php83/Resources/stubs/DateMalformedStringException.php',
        'DateObjectError' => __DIR__ . '/..' . '/symfony/polyfill-php83/Resources/stubs/DateObjectError.php',
        'DateRangeError' => __DIR__ . '/..' . '/symfony/polyfill-php83/Resources/stubs/DateRangeError.php',
        'DelayedTargetValidation' => __DIR__ . '/..' . '/symfony/polyfill-php85/Resources/stubs/DelayedTargetValidation.php',
        'Deprecated' => __DIR__ . '/..' . '/symfony/polyfill-php84/Resources/stubs/Deprecated.php',
        'Filter\\FilterException' => __DIR__ . '/..' . '/symfony/polyfill-php85/Resources/stubs/Filter/FilterException.php',
        'Filter\\FilterFailedException' => __DIR__ . '/..' . '/symfony/polyfill-php85/Resources/stubs/Filter/FilterFailedException.php',
        'Kelunik\\Certificate\\Certificate' => __DIR__ . '/..' . '/kelunik/certificate/src/Certificate.php',
        'Kelunik\\Certificate\\FieldNotSupportedException' => __DIR__ . '/..' . '/kelunik/certificate/src/FieldNotSupportedException.php',
        'Kelunik\\Certificate\\InvalidCertificateException' => __DIR__ . '/..' . '/kelunik/certificate/src/InvalidCertificateException.php',
        'Kelunik\\Certificate\\Profile' => __DIR__ . '/..' . '/kelunik/certificate/src/Profile.php',
        'League\\Uri\\BaseUri' => __DIR__ . '/..' . '/league/uri/BaseUri.php',
        'League\\Uri\\Builder' => __DIR__ . '/..' . '/league/uri/Builder.php',
        'League\\Uri\\Components\\Authority' => __DIR__ . '/..' . '/league/uri-components/Components/Authority.php',
        'League\\Uri\\Components\\Component' => __DIR__ . '/..' . '/league/uri-components/Components/Component.php',
        'League\\Uri\\Components\\DataPath' => __DIR__ . '/..' . '/league/uri-components/Components/DataPath.php',
        'League\\Uri\\Components\\Domain' => __DIR__ . '/..' . '/league/uri-components/Components/Domain.php',
        'League\\Uri\\Components\\Fragment' => __DIR__ . '/..' . '/league/uri-components/Components/Fragment.php',
        'League\\Uri\\Components\\FragmentDirectives' => __DIR__ . '/..' . '/league/uri-components/Components/FragmentDirectives.php',
        'League\\Uri\\Components\\FragmentDirectives\\DirectiveString' => __DIR__ . '/..' . '/league/uri-components/Components/FragmentDirectives/DirectiveString.php',
        'League\\Uri\\Components\\FragmentDirectives\\Encoder' => __DIR__ . '/..' . '/league/uri-components/Components/FragmentDirectives/Encoder.php',
        'League\\Uri\\Components\\FragmentDirectives\\GenericDirective' => __DIR__ . '/..' . '/league/uri-components/Components/FragmentDirectives/GenericDirective.php',
        'League\\Uri\\Components\\FragmentDirectives\\TextDirective' => __DIR__ . '/..' . '/league/uri-components/Components/FragmentDirectives/TextDirective.php',
        'League\\Uri\\Components\\HierarchicalPath' => __DIR__ . '/..' . '/league/uri-components/Components/HierarchicalPath.php',
        'League\\Uri\\Components\\Host' => __DIR__ . '/..' . '/league/uri-components/Components/Host.php',
        'League\\Uri\\Components\\Path' => __DIR__ . '/..' . '/league/uri-components/Components/Path.php',
        'League\\Uri\\Components\\Port' => __DIR__ . '/..' . '/league/uri-components/Components/Port.php',
        'League\\Uri\\Components\\Query' => __DIR__ . '/..' . '/league/uri-components/Components/Query.php',
        'League\\Uri\\Components\\Scheme' => __DIR__ . '/..' . '/league/uri-components/Components/Scheme.php',
        'League\\Uri\\Components\\URLSearchParams' => __DIR__ . '/..' . '/league/uri-components/Components/URLSearchParams.php',
        'League\\Uri\\Components\\UserInfo' => __DIR__ . '/..' . '/league/uri-components/Components/UserInfo.php',
        'League\\Uri\\Contracts\\AuthorityInterface' => __DIR__ . '/..' . '/league/uri-interfaces/Contracts/AuthorityInterface.php',
        'League\\Uri\\Contracts\\Conditionable' => __DIR__ . '/..' . '/league/uri-interfaces/Contracts/Conditionable.php',
        'League\\Uri\\Contracts\\DataPathInterface' => __DIR__ . '/..' . '/league/uri-interfaces/Contracts/DataPathInterface.php',
        'League\\Uri\\Contracts\\DomainHostInterface' => __DIR__ . '/..' . '/league/uri-interfaces/Contracts/DomainHostInterface.php',
        'League\\Uri\\Contracts\\FragmentDirective' => __DIR__ . '/..' . '/league/uri-interfaces/Contracts/FragmentDirective.php',
        'League\\Uri\\Contracts\\FragmentInterface' => __DIR__ . '/..' . '/league/uri-interfaces/Contracts/FragmentInterface.php',
        'League\\Uri\\Contracts\\HostInterface' => __DIR__ . '/..' . '/league/uri-interfaces/Contracts/HostInterface.php',
        'League\\Uri\\Contracts\\IpHostInterface' => __DIR__ . '/..' . '/league/uri-interfaces/Contracts/IpHostInterface.php',
        'League\\Uri\\Contracts\\PathInterface' => __DIR__ . '/..' . '/league/uri-interfaces/Contracts/PathInterface.php',
        'League\\Uri\\Contracts\\PortInterface' => __DIR__ . '/..' . '/league/uri-interfaces/Contracts/PortInterface.php',
        'League\\Uri\\Contracts\\QueryInterface' => __DIR__ . '/..' . '/league/uri-interfaces/Contracts/QueryInterface.php',
        'League\\Uri\\Contracts\\SegmentedPathInterface' => __DIR__ . '/..' . '/league/uri-interfaces/Contracts/SegmentedPathInterface.php',
        'League\\Uri\\Contracts\\Transformable' => __DIR__ . '/..' . '/league/uri-interfaces/Contracts/Transformable.php',
        'League\\Uri\\Contracts\\UriAccess' => __DIR__ . '/..' . '/league/uri-interfaces/Contracts/UriAccess.php',
        'League\\Uri\\Contracts\\UriComponentInterface' => __DIR__ . '/..' . '/league/uri-interfaces/Contracts/UriComponentInterface.php',
        'League\\Uri\\Contracts\\UriException' => __DIR__ . '/..' . '/league/uri-interfaces/Contracts/UriException.php',
        'League\\Uri\\Contracts\\UriInterface' => __DIR__ . '/..' . '/league/uri-interfaces/Contracts/UriInterface.php',
        'League\\Uri\\Contracts\\UserInfoInterface' => __DIR__ . '/..' . '/league/uri-interfaces/Contracts/UserInfoInterface.php',
        'League\\Uri\\Encoder' => __DIR__ . '/..' . '/league/uri-interfaces/Encoder.php',
        'League\\Uri\\Exceptions\\ConversionFailed' => __DIR__ . '/..' . '/league/uri-interfaces/Exceptions/ConversionFailed.php',
        'League\\Uri\\Exceptions\\MissingFeature' => __DIR__ . '/..' . '/league/uri-interfaces/Exceptions/MissingFeature.php',
        'League\\Uri\\Exceptions\\OffsetOutOfBounds' => __DIR__ . '/..' . '/league/uri-interfaces/Exceptions/OffsetOutOfBounds.php',
        'League\\Uri\\Exceptions\\SyntaxError' => __DIR__ . '/..' . '/league/uri-interfaces/Exceptions/SyntaxError.php',
        'League\\Uri\\FeatureDetection' => __DIR__ . '/..' . '/league/uri-interfaces/FeatureDetection.php',
        'League\\Uri\\HostFormat' => __DIR__ . '/..' . '/league/uri-interfaces/HostFormat.php',
        'League\\Uri\\HostRecord' => __DIR__ . '/..' . '/league/uri-interfaces/HostRecord.php',
        'League\\Uri\\HostType' => __DIR__ . '/..' . '/league/uri-interfaces/HostType.php',
        'League\\Uri\\Http' => __DIR__ . '/..' . '/league/uri/Http.php',
        'League\\Uri\\HttpFactory' => __DIR__ . '/..' . '/league/uri/HttpFactory.php',
        'League\\Uri\\IPv4Normalizer' => __DIR__ . '/..' . '/league/uri-components/IPv4Normalizer.php',
        'League\\Uri\\IPv4\\BCMathCalculator' => __DIR__ . '/..' . '/league/uri-interfaces/IPv4/BCMathCalculator.php',
        'League\\Uri\\IPv4\\Calculator' => __DIR__ . '/..' . '/league/uri-interfaces/IPv4/Calculator.php',
        'League\\Uri\\IPv4\\Converter' => __DIR__ . '/..' . '/league/uri-interfaces/IPv4/Converter.php',
        'League\\Uri\\IPv4\\GMPCalculator' => __DIR__ . '/..' . '/league/uri-interfaces/IPv4/GMPCalculator.php',
        'League\\Uri\\IPv4\\NativeCalculator' => __DIR__ . '/..' . '/league/uri-interfaces/IPv4/NativeCalculator.php',
        'League\\Uri\\IPv6\\Converter' => __DIR__ . '/..' . '/league/uri-interfaces/IPv6/Converter.php',
        'League\\Uri\\Idna\\Converter' => __DIR__ . '/..' . '/league/uri-interfaces/Idna/Converter.php',
        'League\\Uri\\Idna\\Error' => __DIR__ . '/..' . '/league/uri-interfaces/Idna/Error.php',
        'League\\Uri\\Idna\\Option' => __DIR__ . '/..' . '/league/uri-interfaces/Idna/Option.php',
        'League\\Uri\\Idna\\Result' => __DIR__ . '/..' . '/league/uri-interfaces/Idna/Result.php',
        'League\\Uri\\KeyValuePair\\Converter' => __DIR__ . '/..' . '/league/uri-interfaces/KeyValuePair/Converter.php',
        'League\\Uri\\Modifier' => __DIR__ . '/..' . '/league/uri-components/Modifier.php',
        'League\\Uri\\Occurrence' => __DIR__ . '/..' . '/league/uri-interfaces/Occurrence.php',
        'League\\Uri\\QueryComposeMode' => __DIR__ . '/..' . '/league/uri-interfaces/QueryComposeMode.php',
        'League\\Uri\\QueryExtractMode' => __DIR__ . '/..' . '/league/uri-interfaces/QueryExtractMode.php',
        'League\\Uri\\QueryString' => __DIR__ . '/..' . '/league/uri-interfaces/QueryString.php',
        'League\\Uri\\SchemeType' => __DIR__ . '/..' . '/league/uri/SchemeType.php',
        'League\\Uri\\StringCoercionMode' => __DIR__ . '/..' . '/league/uri-interfaces/StringCoercionMode.php',
        'League\\Uri\\TypeConverter' => __DIR__ . '/..' . '/league/uri/TypeConverter.php',
        'League\\Uri\\Uri' => __DIR__ . '/..' . '/league/uri/Uri.php',
        'League\\Uri\\UriComparisonMode' => __DIR__ . '/..' . '/league/uri-interfaces/UriComparisonMode.php',
        'League\\Uri\\UriInfo' => __DIR__ . '/..' . '/league/uri/UriInfo.php',
        'League\\Uri\\UriModifier' => __DIR__ . '/..' . '/league/uri-components/UriModifier.php',
        'League\\Uri\\UriResolver' => __DIR__ . '/..' . '/league/uri/UriResolver.php',
        'League\\Uri\\UriScheme' => __DIR__ . '/..' . '/league/uri/UriScheme.php',
        'League\\Uri\\UriString' => __DIR__ . '/..' . '/league/uri-interfaces/UriString.php',
        'League\\Uri\\UriTemplate' => __DIR__ . '/..' . '/league/uri/UriTemplate.php',
        'League\\Uri\\UriTemplate\\Expression' => __DIR__ . '/..' . '/league/uri/UriTemplate/Expression.php',
        'League\\Uri\\UriTemplate\\ExtractedValue' => __DIR__ . '/..' . '/league/uri/UriTemplate/ExtractedValue.php',
        'League\\Uri\\UriTemplate\\ExtractionErrorReason' => __DIR__ . '/..' . '/league/uri/UriTemplate/ExtractionErrorReason.php',
        'League\\Uri\\UriTemplate\\ExtractionResult' => __DIR__ . '/..' . '/league/uri/UriTemplate/ExtractionResult.php',
        'League\\Uri\\UriTemplate\\Literal' => __DIR__ . '/..' . '/league/uri/UriTemplate/Literal.php',
        'League\\Uri\\UriTemplate\\Operator' => __DIR__ . '/..' . '/league/uri/UriTemplate/Operator.php',
        'League\\Uri\\UriTemplate\\Template' => __DIR__ . '/..' . '/league/uri/UriTemplate/Template.php',
        'League\\Uri\\UriTemplate\\TemplateCanNotBeExpanded' => __DIR__ . '/..' . '/league/uri/UriTemplate/TemplateCanNotBeExpanded.php',
        'League\\Uri\\UriTemplate\\VarSpecifier' => __DIR__ . '/..' . '/league/uri/UriTemplate/VarSpecifier.php',
        'League\\Uri\\UriTemplate\\VariableBag' => __DIR__ . '/..' . '/league/uri/UriTemplate/VariableBag.php',
        'League\\Uri\\UriTemplate\\VariableCanNotBeExtracted' => __DIR__ . '/..' . '/league/uri/UriTemplate/VariableCanNotBeExtracted.php',
        'League\\Uri\\UrlPattern' => __DIR__ . '/..' . '/league/uri/UrlPattern.php',
        'League\\Uri\\UrlPatternBuilder' => __DIR__ . '/..' . '/league/uri/UrlPatternBuilder.php',
        'League\\Uri\\UrlPattern\\Component' => __DIR__ . '/..' . '/league/uri/UrlPattern/Component.php',
        'League\\Uri\\UrlPattern\\ComponentName' => __DIR__ . '/..' . '/league/uri/UrlPattern/ComponentName.php',
        'League\\Uri\\UrlPattern\\ComponentResult' => __DIR__ . '/..' . '/league/uri/UrlPattern/ComponentResult.php',
        'League\\Uri\\UrlPattern\\MatchMode' => __DIR__ . '/..' . '/league/uri/UrlPattern/MatchMode.php',
        'League\\Uri\\UrlPattern\\Modifier' => __DIR__ . '/..' . '/league/uri/UrlPattern/Modifier.php',
        'League\\Uri\\UrlPattern\\Parser' => __DIR__ . '/..' . '/league/uri/UrlPattern/Parser.php',
        'League\\Uri\\UrlPattern\\Part' => __DIR__ . '/..' . '/league/uri/UrlPattern/Part.php',
        'League\\Uri\\UrlPattern\\PartType' => __DIR__ . '/..' . '/league/uri/UrlPattern/PartType.php',
        'League\\Uri\\UrlPattern\\PathToRegexp' => __DIR__ . '/..' . '/league/uri/UrlPattern/PathToRegexp.php',
        'League\\Uri\\UrlPattern\\PatternType' => __DIR__ . '/..' . '/league/uri/UrlPattern/PatternType.php',
        'League\\Uri\\UrlPattern\\Result' => __DIR__ . '/..' . '/league/uri/UrlPattern/Result.php',
        'League\\Uri\\UrlPattern\\State' => __DIR__ . '/..' . '/league/uri/UrlPattern/State.php',
        'League\\Uri\\UrlPattern\\Token' => __DIR__ . '/..' . '/league/uri/UrlPattern/Token.php',
        'League\\Uri\\UrlPattern\\TokenType' => __DIR__ . '/..' . '/league/uri/UrlPattern/TokenType.php',
        'League\\Uri\\Urn' => __DIR__ . '/..' . '/league/uri/Urn.php',
        'League\\Uri\\UrnComparisonMode' => __DIR__ . '/..' . '/league/uri-interfaces/UrnComparisonMode.php',
        'LibDNS\\Decoder\\Decoder' => __DIR__ . '/..' . '/daverandom/libdns/src/Decoder/Decoder.php',
        'LibDNS\\Decoder\\DecoderFactory' => __DIR__ . '/..' . '/daverandom/libdns/src/Decoder/DecoderFactory.php',
        'LibDNS\\Decoder\\DecodingContext' => __DIR__ . '/..' . '/daverandom/libdns/src/Decoder/DecodingContext.php',
        'LibDNS\\Decoder\\DecodingContextFactory' => __DIR__ . '/..' . '/daverandom/libdns/src/Decoder/DecodingContextFactory.php',
        'LibDNS\\Encoder\\Encoder' => __DIR__ . '/..' . '/daverandom/libdns/src/Encoder/Encoder.php',
        'LibDNS\\Encoder\\EncoderFactory' => __DIR__ . '/..' . '/daverandom/libdns/src/Encoder/EncoderFactory.php',
        'LibDNS\\Encoder\\EncodingContext' => __DIR__ . '/..' . '/daverandom/libdns/src/Encoder/EncodingContext.php',
        'LibDNS\\Encoder\\EncodingContextFactory' => __DIR__ . '/..' . '/daverandom/libdns/src/Encoder/EncodingContextFactory.php',
        'LibDNS\\Enumeration' => __DIR__ . '/..' . '/daverandom/libdns/src/Enumeration.php',
        'LibDNS\\Messages\\Message' => __DIR__ . '/..' . '/daverandom/libdns/src/Messages/Message.php',
        'LibDNS\\Messages\\MessageFactory' => __DIR__ . '/..' . '/daverandom/libdns/src/Messages/MessageFactory.php',
        'LibDNS\\Messages\\MessageOpCodes' => __DIR__ . '/..' . '/daverandom/libdns/src/Messages/MessageOpCodes.php',
        'LibDNS\\Messages\\MessageResponseCodes' => __DIR__ . '/..' . '/daverandom/libdns/src/Messages/MessageResponseCodes.php',
        'LibDNS\\Messages\\MessageTypes' => __DIR__ . '/..' . '/daverandom/libdns/src/Messages/MessageTypes.php',
        'LibDNS\\Packets\\LabelRegistry' => __DIR__ . '/..' . '/daverandom/libdns/src/Packets/LabelRegistry.php',
        'LibDNS\\Packets\\Packet' => __DIR__ . '/..' . '/daverandom/libdns/src/Packets/Packet.php',
        'LibDNS\\Packets\\PacketFactory' => __DIR__ . '/..' . '/daverandom/libdns/src/Packets/PacketFactory.php',
        'LibDNS\\Records\\Question' => __DIR__ . '/..' . '/daverandom/libdns/src/Records/Question.php',
        'LibDNS\\Records\\QuestionFactory' => __DIR__ . '/..' . '/daverandom/libdns/src/Records/QuestionFactory.php',
        'LibDNS\\Records\\RData' => __DIR__ . '/..' . '/daverandom/libdns/src/Records/RData.php',
        'LibDNS\\Records\\RDataBuilder' => __DIR__ . '/..' . '/daverandom/libdns/src/Records/RDataBuilder.php',
        'LibDNS\\Records\\RDataFactory' => __DIR__ . '/..' . '/daverandom/libdns/src/Records/RDataFactory.php',
        'LibDNS\\Records\\Record' => __DIR__ . '/..' . '/daverandom/libdns/src/Records/Record.php',
        'LibDNS\\Records\\RecordCollection' => __DIR__ . '/..' . '/daverandom/libdns/src/Records/RecordCollection.php',
        'LibDNS\\Records\\RecordCollectionFactory' => __DIR__ . '/..' . '/daverandom/libdns/src/Records/RecordCollectionFactory.php',
        'LibDNS\\Records\\RecordTypes' => __DIR__ . '/..' . '/daverandom/libdns/src/Records/RecordTypes.php',
        'LibDNS\\Records\\Resource' => __DIR__ . '/..' . '/daverandom/libdns/src/Records/Resource.php',
        'LibDNS\\Records\\ResourceBuilder' => __DIR__ . '/..' . '/daverandom/libdns/src/Records/ResourceBuilder.php',
        'LibDNS\\Records\\ResourceBuilderFactory' => __DIR__ . '/..' . '/daverandom/libdns/src/Records/ResourceBuilderFactory.php',
        'LibDNS\\Records\\ResourceClasses' => __DIR__ . '/..' . '/daverandom/libdns/src/Records/ResourceClasses.php',
        'LibDNS\\Records\\ResourceFactory' => __DIR__ . '/..' . '/daverandom/libdns/src/Records/ResourceFactory.php',
        'LibDNS\\Records\\ResourceQClasses' => __DIR__ . '/..' . '/daverandom/libdns/src/Records/ResourceQClasses.php',
        'LibDNS\\Records\\ResourceQTypes' => __DIR__ . '/..' . '/daverandom/libdns/src/Records/ResourceQTypes.php',
        'LibDNS\\Records\\ResourceTypes' => __DIR__ . '/..' . '/daverandom/libdns/src/Records/ResourceTypes.php',
        'LibDNS\\Records\\TypeDefinitions\\FieldDefinition' => __DIR__ . '/..' . '/daverandom/libdns/src/Records/TypeDefinitions/FieldDefinition.php',
        'LibDNS\\Records\\TypeDefinitions\\FieldDefinitionFactory' => __DIR__ . '/..' . '/daverandom/libdns/src/Records/TypeDefinitions/FieldDefinitionFactory.php',
        'LibDNS\\Records\\TypeDefinitions\\TypeDefinition' => __DIR__ . '/..' . '/daverandom/libdns/src/Records/TypeDefinitions/TypeDefinition.php',
        'LibDNS\\Records\\TypeDefinitions\\TypeDefinitionFactory' => __DIR__ . '/..' . '/daverandom/libdns/src/Records/TypeDefinitions/TypeDefinitionFactory.php',
        'LibDNS\\Records\\TypeDefinitions\\TypeDefinitionManager' => __DIR__ . '/..' . '/daverandom/libdns/src/Records/TypeDefinitions/TypeDefinitionManager.php',
        'LibDNS\\Records\\TypeDefinitions\\TypeDefinitionManagerFactory' => __DIR__ . '/..' . '/daverandom/libdns/src/Records/TypeDefinitions/TypeDefinitionManagerFactory.php',
        'LibDNS\\Records\\Types\\Anything' => __DIR__ . '/..' . '/daverandom/libdns/src/Records/Types/Anything.php',
        'LibDNS\\Records\\Types\\BitMap' => __DIR__ . '/..' . '/daverandom/libdns/src/Records/Types/BitMap.php',
        'LibDNS\\Records\\Types\\Char' => __DIR__ . '/..' . '/daverandom/libdns/src/Records/Types/Char.php',
        'LibDNS\\Records\\Types\\CharacterString' => __DIR__ . '/..' . '/daverandom/libdns/src/Records/Types/CharacterString.php',
        'LibDNS\\Records\\Types\\DomainName' => __DIR__ . '/..' . '/daverandom/libdns/src/Records/Types/DomainName.php',
        'LibDNS\\Records\\Types\\IPv4Address' => __DIR__ . '/..' . '/daverandom/libdns/src/Records/Types/IPv4Address.php',
        'LibDNS\\Records\\Types\\IPv6Address' => __DIR__ . '/..' . '/daverandom/libdns/src/Records/Types/IPv6Address.php',
        'LibDNS\\Records\\Types\\Long' => __DIR__ . '/..' . '/daverandom/libdns/src/Records/Types/Long.php',
        'LibDNS\\Records\\Types\\Short' => __DIR__ . '/..' . '/daverandom/libdns/src/Records/Types/Short.php',
        'LibDNS\\Records\\Types\\Type' => __DIR__ . '/..' . '/daverandom/libdns/src/Records/Types/Type.php',
        'LibDNS\\Records\\Types\\TypeBuilder' => __DIR__ . '/..' . '/daverandom/libdns/src/Records/Types/TypeBuilder.php',
        'LibDNS\\Records\\Types\\TypeFactory' => __DIR__ . '/..' . '/daverandom/libdns/src/Records/Types/TypeFactory.php',
        'LibDNS\\Records\\Types\\Types' => __DIR__ . '/..' . '/daverandom/libdns/src/Records/Types/Types.php',
        'Monolog\\Attribute\\AsMonologProcessor' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Attribute/AsMonologProcessor.php',
        'Monolog\\Attribute\\WithMonologChannel' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Attribute/WithMonologChannel.php',
        'Monolog\\DateTimeImmutable' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/DateTimeImmutable.php',
        'Monolog\\ErrorHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/ErrorHandler.php',
        'Monolog\\Formatter\\ChromePHPFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/ChromePHPFormatter.php',
        'Monolog\\Formatter\\ElasticaFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/ElasticaFormatter.php',
        'Monolog\\Formatter\\ElasticsearchFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/ElasticsearchFormatter.php',
        'Monolog\\Formatter\\FlowdockFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/FlowdockFormatter.php',
        'Monolog\\Formatter\\FluentdFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/FluentdFormatter.php',
        'Monolog\\Formatter\\FormatterInterface' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/FormatterInterface.php',
        'Monolog\\Formatter\\GelfMessageFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/GelfMessageFormatter.php',
        'Monolog\\Formatter\\GoogleCloudLoggingFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/GoogleCloudLoggingFormatter.php',
        'Monolog\\Formatter\\HtmlFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/HtmlFormatter.php',
        'Monolog\\Formatter\\JsonFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/JsonFormatter.php',
        'Monolog\\Formatter\\LineFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/LineFormatter.php',
        'Monolog\\Formatter\\LogglyFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/LogglyFormatter.php',
        'Monolog\\Formatter\\LogmaticFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/LogmaticFormatter.php',
        'Monolog\\Formatter\\LogstashFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/LogstashFormatter.php',
        'Monolog\\Formatter\\MongoDBFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/MongoDBFormatter.php',
        'Monolog\\Formatter\\NormalizerFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/NormalizerFormatter.php',
        'Monolog\\Formatter\\RedactingFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/RedactingFormatter.php',
        'Monolog\\Formatter\\ScalarFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/ScalarFormatter.php',
        'Monolog\\Formatter\\SyslogFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/SyslogFormatter.php',
        'Monolog\\Formatter\\WildfireFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/WildfireFormatter.php',
        'Monolog\\Formatter\\WrappingFormatterInterface' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/WrappingFormatterInterface.php',
        'Monolog\\Handler\\AbstractHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/AbstractHandler.php',
        'Monolog\\Handler\\AbstractProcessingHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/AbstractProcessingHandler.php',
        'Monolog\\Handler\\AbstractSyslogHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/AbstractSyslogHandler.php',
        'Monolog\\Handler\\AmqpHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/AmqpHandler.php',
        'Monolog\\Handler\\BrowserConsoleHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/BrowserConsoleHandler.php',
        'Monolog\\Handler\\BufferHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/BufferHandler.php',
        'Monolog\\Handler\\ChromePHPHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/ChromePHPHandler.php',
        'Monolog\\Handler\\CouchDBHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/CouchDBHandler.php',
        'Monolog\\Handler\\CubeHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/CubeHandler.php',
        'Monolog\\Handler\\Curl\\Util' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/Curl/Util.php',
        'Monolog\\Handler\\DeduplicationHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/DeduplicationHandler.php',
        'Monolog\\Handler\\DoctrineCouchDBHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/DoctrineCouchDBHandler.php',
        'Monolog\\Handler\\DynamoDbHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/DynamoDbHandler.php',
        'Monolog\\Handler\\ElasticaHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/ElasticaHandler.php',
        'Monolog\\Handler\\ElasticsearchHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/ElasticsearchHandler.php',
        'Monolog\\Handler\\ErrorLogHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/ErrorLogHandler.php',
        'Monolog\\Handler\\FallbackGroupHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/FallbackGroupHandler.php',
        'Monolog\\Handler\\FilterHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/FilterHandler.php',
        'Monolog\\Handler\\FingersCrossedHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/FingersCrossedHandler.php',
        'Monolog\\Handler\\FingersCrossed\\ActivationStrategyInterface' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php',
        'Monolog\\Handler\\FingersCrossed\\ChannelLevelActivationStrategy' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/FingersCrossed/ChannelLevelActivationStrategy.php',
        'Monolog\\Handler\\FingersCrossed\\ErrorLevelActivationStrategy' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/FingersCrossed/ErrorLevelActivationStrategy.php',
        'Monolog\\Handler\\FirePHPHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/FirePHPHandler.php',
        'Monolog\\Handler\\FleepHookHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/FleepHookHandler.php',
        'Monolog\\Handler\\FlowdockHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/FlowdockHandler.php',
        'Monolog\\Handler\\FormattableHandlerInterface' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/FormattableHandlerInterface.php',
        'Monolog\\Handler\\FormattableHandlerTrait' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/FormattableHandlerTrait.php',
        'Monolog\\Handler\\FrankenPhpHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/FrankenPhpHandler.php',
        'Monolog\\Handler\\GelfHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/GelfHandler.php',
        'Monolog\\Handler\\GroupHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/GroupHandler.php',
        'Monolog\\Handler\\Handler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/Handler.php',
        'Monolog\\Handler\\HandlerInterface' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/HandlerInterface.php',
        'Monolog\\Handler\\HandlerWrapper' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/HandlerWrapper.php',
        'Monolog\\Handler\\IFTTTHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/IFTTTHandler.php',
        'Monolog\\Handler\\InsightOpsHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/InsightOpsHandler.php',
        'Monolog\\Handler\\LogEntriesHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/LogEntriesHandler.php',
        'Monolog\\Handler\\LogMonsterHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/LogMonsterHandler.php',
        'Monolog\\Handler\\LogglyHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/LogglyHandler.php',
        'Monolog\\Handler\\LogmaticHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/LogmaticHandler.php',
        'Monolog\\Handler\\MailHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/MailHandler.php',
        'Monolog\\Handler\\MandrillHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/MandrillHandler.php',
        'Monolog\\Handler\\MissingExtensionException' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/MissingExtensionException.php',
        'Monolog\\Handler\\MongoDBHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/MongoDBHandler.php',
        'Monolog\\Handler\\NativeMailerHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/NativeMailerHandler.php',
        'Monolog\\Handler\\NewRelicHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/NewRelicHandler.php',
        'Monolog\\Handler\\NoopHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/NoopHandler.php',
        'Monolog\\Handler\\NullHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/NullHandler.php',
        'Monolog\\Handler\\OverflowHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/OverflowHandler.php',
        'Monolog\\Handler\\PHPConsoleHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/PHPConsoleHandler.php',
        'Monolog\\Handler\\ProcessHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/ProcessHandler.php',
        'Monolog\\Handler\\ProcessableHandlerInterface' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/ProcessableHandlerInterface.php',
        'Monolog\\Handler\\ProcessableHandlerTrait' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/ProcessableHandlerTrait.php',
        'Monolog\\Handler\\PsrHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/PsrHandler.php',
        'Monolog\\Handler\\PushoverHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/PushoverHandler.php',
        'Monolog\\Handler\\RedisHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/RedisHandler.php',
        'Monolog\\Handler\\RedisPubSubHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/RedisPubSubHandler.php',
        'Monolog\\Handler\\RollbarHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/RollbarHandler.php',
        'Monolog\\Handler\\RotatingFileHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php',
        'Monolog\\Handler\\SamplingHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/SamplingHandler.php',
        'Monolog\\Handler\\SendGridHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/SendGridHandler.php',
        'Monolog\\Handler\\SlackHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/SlackHandler.php',
        'Monolog\\Handler\\SlackWebhookHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/SlackWebhookHandler.php',
        'Monolog\\Handler\\Slack\\SlackRecord' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/Slack/SlackRecord.php',
        'Monolog\\Handler\\SocketHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/SocketHandler.php',
        'Monolog\\Handler\\SqsHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/SqsHandler.php',
        'Monolog\\Handler\\StreamHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/StreamHandler.php',
        'Monolog\\Handler\\SymfonyMailerHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/SymfonyMailerHandler.php',
        'Monolog\\Handler\\SyslogHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/SyslogHandler.php',
        'Monolog\\Handler\\SyslogUdpHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/SyslogUdpHandler.php',
        'Monolog\\Handler\\SyslogUdp\\UdpSocket' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/SyslogUdp/UdpSocket.php',
        'Monolog\\Handler\\TelegramBotHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/TelegramBotHandler.php',
        'Monolog\\Handler\\TestHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/TestHandler.php',
        'Monolog\\Handler\\WebRequestRecognizerTrait' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/WebRequestRecognizerTrait.php',
        'Monolog\\Handler\\WhatFailureGroupHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/WhatFailureGroupHandler.php',
        'Monolog\\Handler\\ZendMonitorHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/ZendMonitorHandler.php',
        'Monolog\\JsonSerializableDateTimeImmutable' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/JsonSerializableDateTimeImmutable.php',
        'Monolog\\Level' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Level.php',
        'Monolog\\LogRecord' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/LogRecord.php',
        'Monolog\\Logger' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Logger.php',
        'Monolog\\Processor\\ClosureContextProcessor' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Processor/ClosureContextProcessor.php',
        'Monolog\\Processor\\GitProcessor' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Processor/GitProcessor.php',
        'Monolog\\Processor\\HostnameProcessor' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Processor/HostnameProcessor.php',
        'Monolog\\Processor\\IntrospectionProcessor' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Processor/IntrospectionProcessor.php',
        'Monolog\\Processor\\LoadAverageProcessor' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Processor/LoadAverageProcessor.php',
        'Monolog\\Processor\\MemoryPeakUsageProcessor' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Processor/MemoryPeakUsageProcessor.php',
        'Monolog\\Processor\\MemoryProcessor' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Processor/MemoryProcessor.php',
        'Monolog\\Processor\\MemoryUsageProcessor' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Processor/MemoryUsageProcessor.php',
        'Monolog\\Processor\\MercurialProcessor' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Processor/MercurialProcessor.php',
        'Monolog\\Processor\\ProcessIdProcessor' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Processor/ProcessIdProcessor.php',
        'Monolog\\Processor\\ProcessorInterface' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Processor/ProcessorInterface.php',
        'Monolog\\Processor\\PsrLogMessageProcessor' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Processor/PsrLogMessageProcessor.php',
        'Monolog\\Processor\\TagProcessor' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Processor/TagProcessor.php',
        'Monolog\\Processor\\UidProcessor' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Processor/UidProcessor.php',
        'Monolog\\Processor\\WebProcessor' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Processor/WebProcessor.php',
        'Monolog\\Registry' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Registry.php',
        'Monolog\\ResettableInterface' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/ResettableInterface.php',
        'Monolog\\SignalHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/SignalHandler.php',
        'Monolog\\Test\\MonologTestCase' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Test/MonologTestCase.php',
        'Monolog\\Test\\TestCase' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Test/TestCase.php',
        'Monolog\\Utils' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Utils.php',
        'NoDiscard' => __DIR__ . '/..' . '/symfony/polyfill-php85/Resources/stubs/NoDiscard.php',
        'Override' => __DIR__ . '/..' . '/symfony/polyfill-php83/Resources/stubs/Override.php',
        'ParagonIE\\ConstantTime\\Base32' => __DIR__ . '/..' . '/paragonie/constant_time_encoding/src/Base32.php',
        'ParagonIE\\ConstantTime\\Base32Hex' => __DIR__ . '/..' . '/paragonie/constant_time_encoding/src/Base32Hex.php',
        'ParagonIE\\ConstantTime\\Base64' => __DIR__ . '/..' . '/paragonie/constant_time_encoding/src/Base64.php',
        'ParagonIE\\ConstantTime\\Base64DotSlash' => __DIR__ . '/..' . '/paragonie/constant_time_encoding/src/Base64DotSlash.php',
        'ParagonIE\\ConstantTime\\Base64DotSlashOrdered' => __DIR__ . '/..' . '/paragonie/constant_time_encoding/src/Base64DotSlashOrdered.php',
        'ParagonIE\\ConstantTime\\Base64UrlSafe' => __DIR__ . '/..' . '/paragonie/constant_time_encoding/src/Base64UrlSafe.php',
        'ParagonIE\\ConstantTime\\Binary' => __DIR__ . '/..' . '/paragonie/constant_time_encoding/src/Binary.php',
        'ParagonIE\\ConstantTime\\EncoderInterface' => __DIR__ . '/..' . '/paragonie/constant_time_encoding/src/EncoderInterface.php',
        'ParagonIE\\ConstantTime\\Encoding' => __DIR__ . '/..' . '/paragonie/constant_time_encoding/src/Encoding.php',
        'ParagonIE\\ConstantTime\\Hex' => __DIR__ . '/..' . '/paragonie/constant_time_encoding/src/Hex.php',
        'ParagonIE\\ConstantTime\\RFC4648' => __DIR__ . '/..' . '/paragonie/constant_time_encoding/src/RFC4648.php',
        'Pdo\\Dblib' => __DIR__ . '/..' . '/symfony/polyfill-php84/Resources/stubs/Pdo/Dblib.php',
        'Pdo\\Firebird' => __DIR__ . '/..' . '/symfony/polyfill-php84/Resources/stubs/Pdo/Firebird.php',
        'Pdo\\Mysql' => __DIR__ . '/..' . '/symfony/polyfill-php84/Resources/stubs/Pdo/Mysql.php',
        'Pdo\\Odbc' => __DIR__ . '/..' . '/symfony/polyfill-php84/Resources/stubs/Pdo/Odbc.php',
        'Pdo\\Pgsql' => __DIR__ . '/..' . '/symfony/polyfill-php84/Resources/stubs/Pdo/Pgsql.php',
        'Pdo\\Sqlite' => __DIR__ . '/..' . '/symfony/polyfill-php84/Resources/stubs/Pdo/Sqlite.php',
        'PhpParser\\Builder' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Builder.php',
        'PhpParser\\BuilderFactory' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/BuilderFactory.php',
        'PhpParser\\BuilderHelpers' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/BuilderHelpers.php',
        'PhpParser\\Builder\\ClassConst' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Builder/ClassConst.php',
        'PhpParser\\Builder\\Class_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Builder/Class_.php',
        'PhpParser\\Builder\\Declaration' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Builder/Declaration.php',
        'PhpParser\\Builder\\EnumCase' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Builder/EnumCase.php',
        'PhpParser\\Builder\\Enum_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Builder/Enum_.php',
        'PhpParser\\Builder\\FunctionLike' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Builder/FunctionLike.php',
        'PhpParser\\Builder\\Function_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Builder/Function_.php',
        'PhpParser\\Builder\\Interface_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Builder/Interface_.php',
        'PhpParser\\Builder\\Method' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Builder/Method.php',
        'PhpParser\\Builder\\Namespace_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Builder/Namespace_.php',
        'PhpParser\\Builder\\Param' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Builder/Param.php',
        'PhpParser\\Builder\\Property' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Builder/Property.php',
        'PhpParser\\Builder\\TraitUse' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Builder/TraitUse.php',
        'PhpParser\\Builder\\TraitUseAdaptation' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Builder/TraitUseAdaptation.php',
        'PhpParser\\Builder\\Trait_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Builder/Trait_.php',
        'PhpParser\\Builder\\Use_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Builder/Use_.php',
        'PhpParser\\Comment' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Comment.php',
        'PhpParser\\Comment\\Doc' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Comment/Doc.php',
        'PhpParser\\ConstExprEvaluationException' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/ConstExprEvaluationException.php',
        'PhpParser\\ConstExprEvaluator' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/ConstExprEvaluator.php',
        'PhpParser\\Error' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Error.php',
        'PhpParser\\ErrorHandler' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/ErrorHandler.php',
        'PhpParser\\ErrorHandler\\Collecting' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/ErrorHandler/Collecting.php',
        'PhpParser\\ErrorHandler\\Throwing' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/ErrorHandler/Throwing.php',
        'PhpParser\\Internal\\DiffElem' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Internal/DiffElem.php',
        'PhpParser\\Internal\\Differ' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Internal/Differ.php',
        'PhpParser\\Internal\\PrintableNewAnonClassNode' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Internal/PrintableNewAnonClassNode.php',
        'PhpParser\\Internal\\TokenPolyfill' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Internal/TokenPolyfill.php',
        'PhpParser\\Internal\\TokenStream' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Internal/TokenStream.php',
        'PhpParser\\JsonDecoder' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/JsonDecoder.php',
        'PhpParser\\Lexer' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Lexer.php',
        'PhpParser\\Lexer\\Emulative' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Lexer/Emulative.php',
        'PhpParser\\Lexer\\TokenEmulator\\AsymmetricVisibilityTokenEmulator' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/AsymmetricVisibilityTokenEmulator.php',
        'PhpParser\\Lexer\\TokenEmulator\\AttributeEmulator' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/AttributeEmulator.php',
        'PhpParser\\Lexer\\TokenEmulator\\EnumTokenEmulator' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/EnumTokenEmulator.php',
        'PhpParser\\Lexer\\TokenEmulator\\ExplicitOctalEmulator' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ExplicitOctalEmulator.php',
        'PhpParser\\Lexer\\TokenEmulator\\FnTokenEmulator' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/FnTokenEmulator.php',
        'PhpParser\\Lexer\\TokenEmulator\\KeywordEmulator' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/KeywordEmulator.php',
        'PhpParser\\Lexer\\TokenEmulator\\MatchTokenEmulator' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/MatchTokenEmulator.php',
        'PhpParser\\Lexer\\TokenEmulator\\NullsafeTokenEmulator' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/NullsafeTokenEmulator.php',
        'PhpParser\\Lexer\\TokenEmulator\\PipeOperatorEmulator' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/PipeOperatorEmulator.php',
        'PhpParser\\Lexer\\TokenEmulator\\PropertyTokenEmulator' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/PropertyTokenEmulator.php',
        'PhpParser\\Lexer\\TokenEmulator\\ReadonlyFunctionTokenEmulator' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ReadonlyFunctionTokenEmulator.php',
        'PhpParser\\Lexer\\TokenEmulator\\ReadonlyTokenEmulator' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ReadonlyTokenEmulator.php',
        'PhpParser\\Lexer\\TokenEmulator\\ReverseEmulator' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ReverseEmulator.php',
        'PhpParser\\Lexer\\TokenEmulator\\TokenEmulator' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/TokenEmulator.php',
        'PhpParser\\Lexer\\TokenEmulator\\VoidCastEmulator' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/VoidCastEmulator.php',
        'PhpParser\\Modifiers' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Modifiers.php',
        'PhpParser\\NameContext' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/NameContext.php',
        'PhpParser\\Node' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node.php',
        'PhpParser\\NodeAbstract' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/NodeAbstract.php',
        'PhpParser\\NodeDumper' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/NodeDumper.php',
        'PhpParser\\NodeFinder' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/NodeFinder.php',
        'PhpParser\\NodeTraverser' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/NodeTraverser.php',
        'PhpParser\\NodeTraverserInterface' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/NodeTraverserInterface.php',
        'PhpParser\\NodeVisitor' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/NodeVisitor.php',
        'PhpParser\\NodeVisitorAbstract' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/NodeVisitorAbstract.php',
        'PhpParser\\NodeVisitor\\CloningVisitor' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/NodeVisitor/CloningVisitor.php',
        'PhpParser\\NodeVisitor\\CommentAnnotatingVisitor' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/NodeVisitor/CommentAnnotatingVisitor.php',
        'PhpParser\\NodeVisitor\\FindingVisitor' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/NodeVisitor/FindingVisitor.php',
        'PhpParser\\NodeVisitor\\FirstFindingVisitor' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/NodeVisitor/FirstFindingVisitor.php',
        'PhpParser\\NodeVisitor\\NameResolver' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/NodeVisitor/NameResolver.php',
        'PhpParser\\NodeVisitor\\NodeConnectingVisitor' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/NodeVisitor/NodeConnectingVisitor.php',
        'PhpParser\\NodeVisitor\\ParentConnectingVisitor' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/NodeVisitor/ParentConnectingVisitor.php',
        'PhpParser\\Node\\Arg' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Arg.php',
        'PhpParser\\Node\\ArgPlaceholder' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/ArgPlaceholder.php',
        'PhpParser\\Node\\ArrayItem' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/ArrayItem.php',
        'PhpParser\\Node\\Attribute' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Attribute.php',
        'PhpParser\\Node\\AttributeGroup' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/AttributeGroup.php',
        'PhpParser\\Node\\ClosureUse' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/ClosureUse.php',
        'PhpParser\\Node\\ComplexType' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/ComplexType.php',
        'PhpParser\\Node\\Const_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Const_.php',
        'PhpParser\\Node\\DeclareItem' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/DeclareItem.php',
        'PhpParser\\Node\\Expr' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr.php',
        'PhpParser\\Node\\Expr\\ArrayDimFetch' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/ArrayDimFetch.php',
        'PhpParser\\Node\\Expr\\ArrayItem' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/ArrayItem.php',
        'PhpParser\\Node\\Expr\\Array_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Array_.php',
        'PhpParser\\Node\\Expr\\ArrowFunction' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/ArrowFunction.php',
        'PhpParser\\Node\\Expr\\Assign' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Assign.php',
        'PhpParser\\Node\\Expr\\AssignOp' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp.php',
        'PhpParser\\Node\\Expr\\AssignOp\\BitwiseAnd' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/BitwiseAnd.php',
        'PhpParser\\Node\\Expr\\AssignOp\\BitwiseOr' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/BitwiseOr.php',
        'PhpParser\\Node\\Expr\\AssignOp\\BitwiseXor' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/BitwiseXor.php',
        'PhpParser\\Node\\Expr\\AssignOp\\Coalesce' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Coalesce.php',
        'PhpParser\\Node\\Expr\\AssignOp\\Concat' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Concat.php',
        'PhpParser\\Node\\Expr\\AssignOp\\Div' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Div.php',
        'PhpParser\\Node\\Expr\\AssignOp\\Minus' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Minus.php',
        'PhpParser\\Node\\Expr\\AssignOp\\Mod' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Mod.php',
        'PhpParser\\Node\\Expr\\AssignOp\\Mul' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Mul.php',
        'PhpParser\\Node\\Expr\\AssignOp\\Plus' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Plus.php',
        'PhpParser\\Node\\Expr\\AssignOp\\Pow' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Pow.php',
        'PhpParser\\Node\\Expr\\AssignOp\\ShiftLeft' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/ShiftLeft.php',
        'PhpParser\\Node\\Expr\\AssignOp\\ShiftRight' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/ShiftRight.php',
        'PhpParser\\Node\\Expr\\AssignRef' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignRef.php',
        'PhpParser\\Node\\Expr\\BinaryOp' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp.php',
        'PhpParser\\Node\\Expr\\BinaryOp\\BitwiseAnd' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BitwiseAnd.php',
        'PhpParser\\Node\\Expr\\BinaryOp\\BitwiseOr' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BitwiseOr.php',
        'PhpParser\\Node\\Expr\\BinaryOp\\BitwiseXor' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BitwiseXor.php',
        'PhpParser\\Node\\Expr\\BinaryOp\\BooleanAnd' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BooleanAnd.php',
        'PhpParser\\Node\\Expr\\BinaryOp\\BooleanOr' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BooleanOr.php',
        'PhpParser\\Node\\Expr\\BinaryOp\\Coalesce' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Coalesce.php',
        'PhpParser\\Node\\Expr\\BinaryOp\\Concat' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Concat.php',
        'PhpParser\\Node\\Expr\\BinaryOp\\Div' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Div.php',
        'PhpParser\\Node\\Expr\\BinaryOp\\Equal' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Equal.php',
        'PhpParser\\Node\\Expr\\BinaryOp\\Greater' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Greater.php',
        'PhpParser\\Node\\Expr\\BinaryOp\\GreaterOrEqual' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/GreaterOrEqual.php',
        'PhpParser\\Node\\Expr\\BinaryOp\\Identical' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Identical.php',
        'PhpParser\\Node\\Expr\\BinaryOp\\LogicalAnd' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/LogicalAnd.php',
        'PhpParser\\Node\\Expr\\BinaryOp\\LogicalOr' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/LogicalOr.php',
        'PhpParser\\Node\\Expr\\BinaryOp\\LogicalXor' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/LogicalXor.php',
        'PhpParser\\Node\\Expr\\BinaryOp\\Minus' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Minus.php',
        'PhpParser\\Node\\Expr\\BinaryOp\\Mod' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Mod.php',
        'PhpParser\\Node\\Expr\\BinaryOp\\Mul' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Mul.php',
        'PhpParser\\Node\\Expr\\BinaryOp\\NotEqual' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/NotEqual.php',
        'PhpParser\\Node\\Expr\\BinaryOp\\NotIdentical' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/NotIdentical.php',
        'PhpParser\\Node\\Expr\\BinaryOp\\Pipe' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Pipe.php',
        'PhpParser\\Node\\Expr\\BinaryOp\\Plus' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Plus.php',
        'PhpParser\\Node\\Expr\\BinaryOp\\Pow' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Pow.php',
        'PhpParser\\Node\\Expr\\BinaryOp\\ShiftLeft' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/ShiftLeft.php',
        'PhpParser\\Node\\Expr\\BinaryOp\\ShiftRight' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/ShiftRight.php',
        'PhpParser\\Node\\Expr\\BinaryOp\\Smaller' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Smaller.php',
        'PhpParser\\Node\\Expr\\BinaryOp\\SmallerOrEqual' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/SmallerOrEqual.php',
        'PhpParser\\Node\\Expr\\BinaryOp\\Spaceship' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Spaceship.php',
        'PhpParser\\Node\\Expr\\BitwiseNot' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BitwiseNot.php',
        'PhpParser\\Node\\Expr\\BooleanNot' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BooleanNot.php',
        'PhpParser\\Node\\Expr\\CallLike' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/CallLike.php',
        'PhpParser\\Node\\Expr\\Cast' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Cast.php',
        'PhpParser\\Node\\Expr\\Cast\\Array_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Array_.php',
        'PhpParser\\Node\\Expr\\Cast\\Bool_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Bool_.php',
        'PhpParser\\Node\\Expr\\Cast\\Double' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Double.php',
        'PhpParser\\Node\\Expr\\Cast\\Int_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Int_.php',
        'PhpParser\\Node\\Expr\\Cast\\Object_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Object_.php',
        'PhpParser\\Node\\Expr\\Cast\\String_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/String_.php',
        'PhpParser\\Node\\Expr\\Cast\\Unset_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Unset_.php',
        'PhpParser\\Node\\Expr\\Cast\\Void_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Void_.php',
        'PhpParser\\Node\\Expr\\ClassConstFetch' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/ClassConstFetch.php',
        'PhpParser\\Node\\Expr\\Clone_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Clone_.php',
        'PhpParser\\Node\\Expr\\Closure' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Closure.php',
        'PhpParser\\Node\\Expr\\ClosureUse' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/ClosureUse.php',
        'PhpParser\\Node\\Expr\\ConstFetch' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/ConstFetch.php',
        'PhpParser\\Node\\Expr\\Empty_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Empty_.php',
        'PhpParser\\Node\\Expr\\Error' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Error.php',
        'PhpParser\\Node\\Expr\\ErrorSuppress' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/ErrorSuppress.php',
        'PhpParser\\Node\\Expr\\Eval_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Eval_.php',
        'PhpParser\\Node\\Expr\\Exit_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Exit_.php',
        'PhpParser\\Node\\Expr\\FuncCall' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/FuncCall.php',
        'PhpParser\\Node\\Expr\\Include_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Include_.php',
        'PhpParser\\Node\\Expr\\Instanceof_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Instanceof_.php',
        'PhpParser\\Node\\Expr\\Isset_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Isset_.php',
        'PhpParser\\Node\\Expr\\List_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/List_.php',
        'PhpParser\\Node\\Expr\\Match_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Match_.php',
        'PhpParser\\Node\\Expr\\MethodCall' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/MethodCall.php',
        'PhpParser\\Node\\Expr\\New_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/New_.php',
        'PhpParser\\Node\\Expr\\NullsafeMethodCall' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/NullsafeMethodCall.php',
        'PhpParser\\Node\\Expr\\NullsafePropertyFetch' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/NullsafePropertyFetch.php',
        'PhpParser\\Node\\Expr\\PostDec' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/PostDec.php',
        'PhpParser\\Node\\Expr\\PostInc' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/PostInc.php',
        'PhpParser\\Node\\Expr\\PreDec' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/PreDec.php',
        'PhpParser\\Node\\Expr\\PreInc' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/PreInc.php',
        'PhpParser\\Node\\Expr\\Print_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Print_.php',
        'PhpParser\\Node\\Expr\\PropertyFetch' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/PropertyFetch.php',
        'PhpParser\\Node\\Expr\\ShellExec' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/ShellExec.php',
        'PhpParser\\Node\\Expr\\StaticCall' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/StaticCall.php',
        'PhpParser\\Node\\Expr\\StaticPropertyFetch' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/StaticPropertyFetch.php',
        'PhpParser\\Node\\Expr\\Ternary' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Ternary.php',
        'PhpParser\\Node\\Expr\\Throw_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Throw_.php',
        'PhpParser\\Node\\Expr\\UnaryMinus' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/UnaryMinus.php',
        'PhpParser\\Node\\Expr\\UnaryPlus' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/UnaryPlus.php',
        'PhpParser\\Node\\Expr\\Variable' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Variable.php',
        'PhpParser\\Node\\Expr\\YieldFrom' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/YieldFrom.php',
        'PhpParser\\Node\\Expr\\Yield_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Yield_.php',
        'PhpParser\\Node\\FunctionLike' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/FunctionLike.php',
        'PhpParser\\Node\\Identifier' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Identifier.php',
        'PhpParser\\Node\\InterpolatedStringPart' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/InterpolatedStringPart.php',
        'PhpParser\\Node\\IntersectionType' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/IntersectionType.php',
        'PhpParser\\Node\\MatchArm' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/MatchArm.php',
        'PhpParser\\Node\\Name' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Name.php',
        'PhpParser\\Node\\Name\\FullyQualified' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Name/FullyQualified.php',
        'PhpParser\\Node\\Name\\Relative' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Name/Relative.php',
        'PhpParser\\Node\\NullableType' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/NullableType.php',
        'PhpParser\\Node\\Param' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Param.php',
        'PhpParser\\Node\\PropertyHook' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/PropertyHook.php',
        'PhpParser\\Node\\PropertyItem' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/PropertyItem.php',
        'PhpParser\\Node\\Scalar' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Scalar.php',
        'PhpParser\\Node\\Scalar\\DNumber' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Scalar/DNumber.php',
        'PhpParser\\Node\\Scalar\\Encapsed' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Scalar/Encapsed.php',
        'PhpParser\\Node\\Scalar\\EncapsedStringPart' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Scalar/EncapsedStringPart.php',
        'PhpParser\\Node\\Scalar\\Float_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Scalar/Float_.php',
        'PhpParser\\Node\\Scalar\\Int_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Scalar/Int_.php',
        'PhpParser\\Node\\Scalar\\InterpolatedString' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Scalar/InterpolatedString.php',
        'PhpParser\\Node\\Scalar\\LNumber' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Scalar/LNumber.php',
        'PhpParser\\Node\\Scalar\\MagicConst' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst.php',
        'PhpParser\\Node\\Scalar\\MagicConst\\Class_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Class_.php',
        'PhpParser\\Node\\Scalar\\MagicConst\\Dir' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Dir.php',
        'PhpParser\\Node\\Scalar\\MagicConst\\File' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/File.php',
        'PhpParser\\Node\\Scalar\\MagicConst\\Function_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Function_.php',
        'PhpParser\\Node\\Scalar\\MagicConst\\Line' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Line.php',
        'PhpParser\\Node\\Scalar\\MagicConst\\Method' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Method.php',
        'PhpParser\\Node\\Scalar\\MagicConst\\Namespace_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Namespace_.php',
        'PhpParser\\Node\\Scalar\\MagicConst\\Property' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Property.php',
        'PhpParser\\Node\\Scalar\\MagicConst\\Trait_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Trait_.php',
        'PhpParser\\Node\\Scalar\\String_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Scalar/String_.php',
        'PhpParser\\Node\\StaticVar' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/StaticVar.php',
        'PhpParser\\Node\\Stmt' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt.php',
        'PhpParser\\Node\\Stmt\\Block' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Block.php',
        'PhpParser\\Node\\Stmt\\Break_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Break_.php',
        'PhpParser\\Node\\Stmt\\Case_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Case_.php',
        'PhpParser\\Node\\Stmt\\Catch_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Catch_.php',
        'PhpParser\\Node\\Stmt\\ClassConst' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/ClassConst.php',
        'PhpParser\\Node\\Stmt\\ClassLike' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/ClassLike.php',
        'PhpParser\\Node\\Stmt\\ClassMethod' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/ClassMethod.php',
        'PhpParser\\Node\\Stmt\\Class_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Class_.php',
        'PhpParser\\Node\\Stmt\\Const_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Const_.php',
        'PhpParser\\Node\\Stmt\\Continue_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Continue_.php',
        'PhpParser\\Node\\Stmt\\DeclareDeclare' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/DeclareDeclare.php',
        'PhpParser\\Node\\Stmt\\Declare_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Declare_.php',
        'PhpParser\\Node\\Stmt\\Do_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Do_.php',
        'PhpParser\\Node\\Stmt\\Echo_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Echo_.php',
        'PhpParser\\Node\\Stmt\\ElseIf_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/ElseIf_.php',
        'PhpParser\\Node\\Stmt\\Else_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Else_.php',
        'PhpParser\\Node\\Stmt\\EnumCase' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/EnumCase.php',
        'PhpParser\\Node\\Stmt\\Enum_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Enum_.php',
        'PhpParser\\Node\\Stmt\\Expression' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Expression.php',
        'PhpParser\\Node\\Stmt\\Finally_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Finally_.php',
        'PhpParser\\Node\\Stmt\\For_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/For_.php',
        'PhpParser\\Node\\Stmt\\Foreach_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Foreach_.php',
        'PhpParser\\Node\\Stmt\\Function_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Function_.php',
        'PhpParser\\Node\\Stmt\\Global_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Global_.php',
        'PhpParser\\Node\\Stmt\\Goto_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Goto_.php',
        'PhpParser\\Node\\Stmt\\GroupUse' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/GroupUse.php',
        'PhpParser\\Node\\Stmt\\HaltCompiler' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/HaltCompiler.php',
        'PhpParser\\Node\\Stmt\\If_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/If_.php',
        'PhpParser\\Node\\Stmt\\InlineHTML' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/InlineHTML.php',
        'PhpParser\\Node\\Stmt\\Interface_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Interface_.php',
        'PhpParser\\Node\\Stmt\\Label' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Label.php',
        'PhpParser\\Node\\Stmt\\Namespace_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Namespace_.php',
        'PhpParser\\Node\\Stmt\\Nop' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Nop.php',
        'PhpParser\\Node\\Stmt\\Property' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Property.php',
        'PhpParser\\Node\\Stmt\\PropertyProperty' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/PropertyProperty.php',
        'PhpParser\\Node\\Stmt\\Return_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Return_.php',
        'PhpParser\\Node\\Stmt\\StaticVar' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/StaticVar.php',
        'PhpParser\\Node\\Stmt\\Static_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Static_.php',
        'PhpParser\\Node\\Stmt\\Switch_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Switch_.php',
        'PhpParser\\Node\\Stmt\\TraitUse' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/TraitUse.php',
        'PhpParser\\Node\\Stmt\\TraitUseAdaptation' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/TraitUseAdaptation.php',
        'PhpParser\\Node\\Stmt\\TraitUseAdaptation\\Alias' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/TraitUseAdaptation/Alias.php',
        'PhpParser\\Node\\Stmt\\TraitUseAdaptation\\Precedence' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/TraitUseAdaptation/Precedence.php',
        'PhpParser\\Node\\Stmt\\Trait_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Trait_.php',
        'PhpParser\\Node\\Stmt\\TryCatch' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/TryCatch.php',
        'PhpParser\\Node\\Stmt\\Unset_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Unset_.php',
        'PhpParser\\Node\\Stmt\\UseUse' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/UseUse.php',
        'PhpParser\\Node\\Stmt\\Use_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Use_.php',
        'PhpParser\\Node\\Stmt\\While_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/While_.php',
        'PhpParser\\Node\\UnionType' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/UnionType.php',
        'PhpParser\\Node\\UseItem' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/UseItem.php',
        'PhpParser\\Node\\VarLikeIdentifier' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/VarLikeIdentifier.php',
        'PhpParser\\Node\\VariadicPlaceholder' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/VariadicPlaceholder.php',
        'PhpParser\\Parser' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Parser.php',
        'PhpParser\\ParserAbstract' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/ParserAbstract.php',
        'PhpParser\\ParserFactory' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/ParserFactory.php',
        'PhpParser\\Parser\\Php7' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Parser/Php7.php',
        'PhpParser\\Parser\\Php8' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Parser/Php8.php',
        'PhpParser\\PhpVersion' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/PhpVersion.php',
        'PhpParser\\PrettyPrinter' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/PrettyPrinter.php',
        'PhpParser\\PrettyPrinterAbstract' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/PrettyPrinterAbstract.php',
        'PhpParser\\PrettyPrinter\\Standard' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/PrettyPrinter/Standard.php',
        'PhpParser\\Token' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Token.php',
        'Prometheus\\Collector' => __DIR__ . '/..' . '/promphp/prometheus_client_php/src/Prometheus/Collector.php',
        'Prometheus\\CollectorRegistry' => __DIR__ . '/..' . '/promphp/prometheus_client_php/src/Prometheus/CollectorRegistry.php',
        'Prometheus\\Counter' => __DIR__ . '/..' . '/promphp/prometheus_client_php/src/Prometheus/Counter.php',
        'Prometheus\\Exception\\MetricJsonException' => __DIR__ . '/..' . '/promphp/prometheus_client_php/src/Prometheus/Exception/MetricJsonException.php',
        'Prometheus\\Exception\\MetricNotFoundException' => __DIR__ . '/..' . '/promphp/prometheus_client_php/src/Prometheus/Exception/MetricNotFoundException.php',
        'Prometheus\\Exception\\MetricsRegistrationException' => __DIR__ . '/..' . '/promphp/prometheus_client_php/src/Prometheus/Exception/MetricsRegistrationException.php',
        'Prometheus\\Exception\\StorageException' => __DIR__ . '/..' . '/promphp/prometheus_client_php/src/Prometheus/Exception/StorageException.php',
        'Prometheus\\Gauge' => __DIR__ . '/..' . '/promphp/prometheus_client_php/src/Prometheus/Gauge.php',
        'Prometheus\\Histogram' => __DIR__ . '/..' . '/promphp/prometheus_client_php/src/Prometheus/Histogram.php',
        'Prometheus\\Math' => __DIR__ . '/..' . '/promphp/prometheus_client_php/src/Prometheus/Math.php',
        'Prometheus\\MetricFamilySamples' => __DIR__ . '/..' . '/promphp/prometheus_client_php/src/Prometheus/MetricFamilySamples.php',
        'Prometheus\\RegistryInterface' => __DIR__ . '/..' . '/promphp/prometheus_client_php/src/Prometheus/RegistryInterface.php',
        'Prometheus\\RenderTextFormat' => __DIR__ . '/..' . '/promphp/prometheus_client_php/src/Prometheus/RenderTextFormat.php',
        'Prometheus\\RendererInterface' => __DIR__ . '/..' . '/promphp/prometheus_client_php/src/Prometheus/RendererInterface.php',
        'Prometheus\\Sample' => __DIR__ . '/..' . '/promphp/prometheus_client_php/src/Prometheus/Sample.php',
        'Prometheus\\Storage\\APC' => __DIR__ . '/..' . '/promphp/prometheus_client_php/src/Prometheus/Storage/APC.php',
        'Prometheus\\Storage\\APCng' => __DIR__ . '/..' . '/promphp/prometheus_client_php/src/Prometheus/Storage/APCng.php',
        'Prometheus\\Storage\\AbstractRedis' => __DIR__ . '/..' . '/promphp/prometheus_client_php/src/Prometheus/Storage/AbstractRedis.php',
        'Prometheus\\Storage\\Adapter' => __DIR__ . '/..' . '/promphp/prometheus_client_php/src/Prometheus/Storage/Adapter.php',
        'Prometheus\\Storage\\InMemory' => __DIR__ . '/..' . '/promphp/prometheus_client_php/src/Prometheus/Storage/InMemory.php',
        'Prometheus\\Storage\\PDO' => __DIR__ . '/..' . '/promphp/prometheus_client_php/src/Prometheus/Storage/PDO.php',
        'Prometheus\\Storage\\Predis' => __DIR__ . '/..' . '/promphp/prometheus_client_php/src/Prometheus/Storage/Predis.php',
        'Prometheus\\Storage\\Redis' => __DIR__ . '/..' . '/promphp/prometheus_client_php/src/Prometheus/Storage/Redis.php',
        'Prometheus\\Storage\\RedisClients\\PHPRedis' => __DIR__ . '/..' . '/promphp/prometheus_client_php/src/Prometheus/Storage/RedisClients/PHPRedis.php',
        'Prometheus\\Storage\\RedisClients\\Predis' => __DIR__ . '/..' . '/promphp/prometheus_client_php/src/Prometheus/Storage/RedisClients/Predis.php',
        'Prometheus\\Storage\\RedisClients\\RedisClient' => __DIR__ . '/..' . '/promphp/prometheus_client_php/src/Prometheus/Storage/RedisClients/RedisClient.php',
        'Prometheus\\Storage\\RedisClients\\RedisClientException' => __DIR__ . '/..' . '/promphp/prometheus_client_php/src/Prometheus/Storage/RedisClients/RedisClientException.php',
        'Prometheus\\Storage\\RedisNg' => __DIR__ . '/..' . '/promphp/prometheus_client_php/src/Prometheus/Storage/RedisNg.php',
        'Prometheus\\Summary' => __DIR__ . '/..' . '/promphp/prometheus_client_php/src/Prometheus/Summary.php',
        'Psr\\Http\\Message\\MessageInterface' => __DIR__ . '/..' . '/psr/http-message/src/MessageInterface.php',
        'Psr\\Http\\Message\\RequestFactoryInterface' => __DIR__ . '/..' . '/psr/http-factory/src/RequestFactoryInterface.php',
        'Psr\\Http\\Message\\RequestInterface' => __DIR__ . '/..' . '/psr/http-message/src/RequestInterface.php',
        'Psr\\Http\\Message\\ResponseFactoryInterface' => __DIR__ . '/..' . '/psr/http-factory/src/ResponseFactoryInterface.php',
        'Psr\\Http\\Message\\ResponseInterface' => __DIR__ . '/..' . '/psr/http-message/src/ResponseInterface.php',
        'Psr\\Http\\Message\\ServerRequestFactoryInterface' => __DIR__ . '/..' . '/psr/http-factory/src/ServerRequestFactoryInterface.php',
        'Psr\\Http\\Message\\ServerRequestInterface' => __DIR__ . '/..' . '/psr/http-message/src/ServerRequestInterface.php',
        'Psr\\Http\\Message\\StreamFactoryInterface' => __DIR__ . '/..' . '/psr/http-factory/src/StreamFactoryInterface.php',
        'Psr\\Http\\Message\\StreamInterface' => __DIR__ . '/..' . '/psr/http-message/src/StreamInterface.php',
        'Psr\\Http\\Message\\UploadedFileFactoryInterface' => __DIR__ . '/..' . '/psr/http-factory/src/UploadedFileFactoryInterface.php',
        'Psr\\Http\\Message\\UploadedFileInterface' => __DIR__ . '/..' . '/psr/http-message/src/UploadedFileInterface.php',
        'Psr\\Http\\Message\\UriFactoryInterface' => __DIR__ . '/..' . '/psr/http-factory/src/UriFactoryInterface.php',
        'Psr\\Http\\Message\\UriInterface' => __DIR__ . '/..' . '/psr/http-message/src/UriInterface.php',
        'Psr\\Log\\AbstractLogger' => __DIR__ . '/..' . '/psr/log/src/AbstractLogger.php',
        'Psr\\Log\\InvalidArgumentException' => __DIR__ . '/..' . '/psr/log/src/InvalidArgumentException.php',
        'Psr\\Log\\LogLevel' => __DIR__ . '/..' . '/psr/log/src/LogLevel.php',
        'Psr\\Log\\LoggerAwareInterface' => __DIR__ . '/..' . '/psr/log/src/LoggerAwareInterface.php',
        'Psr\\Log\\LoggerAwareTrait' => __DIR__ . '/..' . '/psr/log/src/LoggerAwareTrait.php',
        'Psr\\Log\\LoggerInterface' => __DIR__ . '/..' . '/psr/log/src/LoggerInterface.php',
        'Psr\\Log\\LoggerTrait' => __DIR__ . '/..' . '/psr/log/src/LoggerTrait.php',
        'Psr\\Log\\NullLogger' => __DIR__ . '/..' . '/psr/log/src/NullLogger.php',
        'Ramsey\\Collection\\AbstractArray' => __DIR__ . '/..' . '/ramsey/collection/src/AbstractArray.php',
        'Ramsey\\Collection\\AbstractCollection' => __DIR__ . '/..' . '/ramsey/collection/src/AbstractCollection.php',
        'Ramsey\\Collection\\AbstractSet' => __DIR__ . '/..' . '/ramsey/collection/src/AbstractSet.php',
        'Ramsey\\Collection\\ArrayInterface' => __DIR__ . '/..' . '/ramsey/collection/src/ArrayInterface.php',
        'Ramsey\\Collection\\Collection' => __DIR__ . '/..' . '/ramsey/collection/src/Collection.php',
        'Ramsey\\Collection\\CollectionInterface' => __DIR__ . '/..' . '/ramsey/collection/src/CollectionInterface.php',
        'Ramsey\\Collection\\DoubleEndedQueue' => __DIR__ . '/..' . '/ramsey/collection/src/DoubleEndedQueue.php',
        'Ramsey\\Collection\\DoubleEndedQueueInterface' => __DIR__ . '/..' . '/ramsey/collection/src/DoubleEndedQueueInterface.php',
        'Ramsey\\Collection\\Exception\\CollectionException' => __DIR__ . '/..' . '/ramsey/collection/src/Exception/CollectionException.php',
        'Ramsey\\Collection\\Exception\\CollectionMismatchException' => __DIR__ . '/..' . '/ramsey/collection/src/Exception/CollectionMismatchException.php',
        'Ramsey\\Collection\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/ramsey/collection/src/Exception/InvalidArgumentException.php',
        'Ramsey\\Collection\\Exception\\InvalidPropertyOrMethod' => __DIR__ . '/..' . '/ramsey/collection/src/Exception/InvalidPropertyOrMethod.php',
        'Ramsey\\Collection\\Exception\\NoSuchElementException' => __DIR__ . '/..' . '/ramsey/collection/src/Exception/NoSuchElementException.php',
        'Ramsey\\Collection\\Exception\\OutOfBoundsException' => __DIR__ . '/..' . '/ramsey/collection/src/Exception/OutOfBoundsException.php',
        'Ramsey\\Collection\\Exception\\UnsupportedOperationException' => __DIR__ . '/..' . '/ramsey/collection/src/Exception/UnsupportedOperationException.php',
        'Ramsey\\Collection\\GenericArray' => __DIR__ . '/..' . '/ramsey/collection/src/GenericArray.php',
        'Ramsey\\Collection\\Map\\AbstractMap' => __DIR__ . '/..' . '/ramsey/collection/src/Map/AbstractMap.php',
        'Ramsey\\Collection\\Map\\AbstractTypedMap' => __DIR__ . '/..' . '/ramsey/collection/src/Map/AbstractTypedMap.php',
        'Ramsey\\Collection\\Map\\AssociativeArrayMap' => __DIR__ . '/..' . '/ramsey/collection/src/Map/AssociativeArrayMap.php',
        'Ramsey\\Collection\\Map\\MapInterface' => __DIR__ . '/..' . '/ramsey/collection/src/Map/MapInterface.php',
        'Ramsey\\Collection\\Map\\NamedParameterMap' => __DIR__ . '/..' . '/ramsey/collection/src/Map/NamedParameterMap.php',
        'Ramsey\\Collection\\Map\\TypedMap' => __DIR__ . '/..' . '/ramsey/collection/src/Map/TypedMap.php',
        'Ramsey\\Collection\\Map\\TypedMapInterface' => __DIR__ . '/..' . '/ramsey/collection/src/Map/TypedMapInterface.php',
        'Ramsey\\Collection\\Queue' => __DIR__ . '/..' . '/ramsey/collection/src/Queue.php',
        'Ramsey\\Collection\\QueueInterface' => __DIR__ . '/..' . '/ramsey/collection/src/QueueInterface.php',
        'Ramsey\\Collection\\Set' => __DIR__ . '/..' . '/ramsey/collection/src/Set.php',
        'Ramsey\\Collection\\Sort' => __DIR__ . '/..' . '/ramsey/collection/src/Sort.php',
        'Ramsey\\Collection\\Tool\\TypeTrait' => __DIR__ . '/..' . '/ramsey/collection/src/Tool/TypeTrait.php',
        'Ramsey\\Collection\\Tool\\ValueExtractorTrait' => __DIR__ . '/..' . '/ramsey/collection/src/Tool/ValueExtractorTrait.php',
        'Ramsey\\Collection\\Tool\\ValueToStringTrait' => __DIR__ . '/..' . '/ramsey/collection/src/Tool/ValueToStringTrait.php',
        'Ramsey\\Uuid\\BinaryUtils' => __DIR__ . '/..' . '/ramsey/uuid/src/BinaryUtils.php',
        'Ramsey\\Uuid\\Builder\\BuilderCollection' => __DIR__ . '/..' . '/ramsey/uuid/src/Builder/BuilderCollection.php',
        'Ramsey\\Uuid\\Builder\\DefaultUuidBuilder' => __DIR__ . '/..' . '/ramsey/uuid/src/Builder/DefaultUuidBuilder.php',
        'Ramsey\\Uuid\\Builder\\DegradedUuidBuilder' => __DIR__ . '/..' . '/ramsey/uuid/src/Builder/DegradedUuidBuilder.php',
        'Ramsey\\Uuid\\Builder\\FallbackBuilder' => __DIR__ . '/..' . '/ramsey/uuid/src/Builder/FallbackBuilder.php',
        'Ramsey\\Uuid\\Builder\\UuidBuilderInterface' => __DIR__ . '/..' . '/ramsey/uuid/src/Builder/UuidBuilderInterface.php',
        'Ramsey\\Uuid\\Codec\\CodecInterface' => __DIR__ . '/..' . '/ramsey/uuid/src/Codec/CodecInterface.php',
        'Ramsey\\Uuid\\Codec\\GuidStringCodec' => __DIR__ . '/..' . '/ramsey/uuid/src/Codec/GuidStringCodec.php',
        'Ramsey\\Uuid\\Codec\\OrderedTimeCodec' => __DIR__ . '/..' . '/ramsey/uuid/src/Codec/OrderedTimeCodec.php',
        'Ramsey\\Uuid\\Codec\\StringCodec' => __DIR__ . '/..' . '/ramsey/uuid/src/Codec/StringCodec.php',
        'Ramsey\\Uuid\\Codec\\TimestampFirstCombCodec' => __DIR__ . '/..' . '/ramsey/uuid/src/Codec/TimestampFirstCombCodec.php',
        'Ramsey\\Uuid\\Codec\\TimestampLastCombCodec' => __DIR__ . '/..' . '/ramsey/uuid/src/Codec/TimestampLastCombCodec.php',
        'Ramsey\\Uuid\\Converter\\NumberConverterInterface' => __DIR__ . '/..' . '/ramsey/uuid/src/Converter/NumberConverterInterface.php',
        'Ramsey\\Uuid\\Converter\\Number\\BigNumberConverter' => __DIR__ . '/..' . '/ramsey/uuid/src/Converter/Number/BigNumberConverter.php',
        'Ramsey\\Uuid\\Converter\\Number\\DegradedNumberConverter' => __DIR__ . '/..' . '/ramsey/uuid/src/Converter/Number/DegradedNumberConverter.php',
        'Ramsey\\Uuid\\Converter\\Number\\GenericNumberConverter' => __DIR__ . '/..' . '/ramsey/uuid/src/Converter/Number/GenericNumberConverter.php',
        'Ramsey\\Uuid\\Converter\\TimeConverterInterface' => __DIR__ . '/..' . '/ramsey/uuid/src/Converter/TimeConverterInterface.php',
        'Ramsey\\Uuid\\Converter\\Time\\BigNumberTimeConverter' => __DIR__ . '/..' . '/ramsey/uuid/src/Converter/Time/BigNumberTimeConverter.php',
        'Ramsey\\Uuid\\Converter\\Time\\DegradedTimeConverter' => __DIR__ . '/..' . '/ramsey/uuid/src/Converter/Time/DegradedTimeConverter.php',
        'Ramsey\\Uuid\\Converter\\Time\\GenericTimeConverter' => __DIR__ . '/..' . '/ramsey/uuid/src/Converter/Time/GenericTimeConverter.php',
        'Ramsey\\Uuid\\Converter\\Time\\PhpTimeConverter' => __DIR__ . '/..' . '/ramsey/uuid/src/Converter/Time/PhpTimeConverter.php',
        'Ramsey\\Uuid\\Converter\\Time\\UnixTimeConverter' => __DIR__ . '/..' . '/ramsey/uuid/src/Converter/Time/UnixTimeConverter.php',
        'Ramsey\\Uuid\\DegradedUuid' => __DIR__ . '/..' . '/ramsey/uuid/src/DegradedUuid.php',
        'Ramsey\\Uuid\\DeprecatedUuidInterface' => __DIR__ . '/..' . '/ramsey/uuid/src/DeprecatedUuidInterface.php',
        'Ramsey\\Uuid\\DeprecatedUuidMethodsTrait' => __DIR__ . '/..' . '/ramsey/uuid/src/DeprecatedUuidMethodsTrait.php',
        'Ramsey\\Uuid\\Exception\\BuilderNotFoundException' => __DIR__ . '/..' . '/ramsey/uuid/src/Exception/BuilderNotFoundException.php',
        'Ramsey\\Uuid\\Exception\\DateTimeException' => __DIR__ . '/..' . '/ramsey/uuid/src/Exception/DateTimeException.php',
        'Ramsey\\Uuid\\Exception\\DceSecurityException' => __DIR__ . '/..' . '/ramsey/uuid/src/Exception/DceSecurityException.php',
        'Ramsey\\Uuid\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/ramsey/uuid/src/Exception/InvalidArgumentException.php',
        'Ramsey\\Uuid\\Exception\\InvalidBytesException' => __DIR__ . '/..' . '/ramsey/uuid/src/Exception/InvalidBytesException.php',
        'Ramsey\\Uuid\\Exception\\InvalidUuidStringException' => __DIR__ . '/..' . '/ramsey/uuid/src/Exception/InvalidUuidStringException.php',
        'Ramsey\\Uuid\\Exception\\NameException' => __DIR__ . '/..' . '/ramsey/uuid/src/Exception/NameException.php',
        'Ramsey\\Uuid\\Exception\\NodeException' => __DIR__ . '/..' . '/ramsey/uuid/src/Exception/NodeException.php',
        'Ramsey\\Uuid\\Exception\\RandomSourceException' => __DIR__ . '/..' . '/ramsey/uuid/src/Exception/RandomSourceException.php',
        'Ramsey\\Uuid\\Exception\\TimeSourceException' => __DIR__ . '/..' . '/ramsey/uuid/src/Exception/TimeSourceException.php',
        'Ramsey\\Uuid\\Exception\\UnableToBuildUuidException' => __DIR__ . '/..' . '/ramsey/uuid/src/Exception/UnableToBuildUuidException.php',
        'Ramsey\\Uuid\\Exception\\UnsupportedOperationException' => __DIR__ . '/..' . '/ramsey/uuid/src/Exception/UnsupportedOperationException.php',
        'Ramsey\\Uuid\\Exception\\UuidExceptionInterface' => __DIR__ . '/..' . '/ramsey/uuid/src/Exception/UuidExceptionInterface.php',
        'Ramsey\\Uuid\\FeatureSet' => __DIR__ . '/..' . '/ramsey/uuid/src/FeatureSet.php',
        'Ramsey\\Uuid\\Fields\\FieldsInterface' => __DIR__ . '/..' . '/ramsey/uuid/src/Fields/FieldsInterface.php',
        'Ramsey\\Uuid\\Fields\\SerializableFieldsTrait' => __DIR__ . '/..' . '/ramsey/uuid/src/Fields/SerializableFieldsTrait.php',
        'Ramsey\\Uuid\\Generator\\CombGenerator' => __DIR__ . '/..' . '/ramsey/uuid/src/Generator/CombGenerator.php',
        'Ramsey\\Uuid\\Generator\\DceSecurityGenerator' => __DIR__ . '/..' . '/ramsey/uuid/src/Generator/DceSecurityGenerator.php',
        'Ramsey\\Uuid\\Generator\\DceSecurityGeneratorInterface' => __DIR__ . '/..' . '/ramsey/uuid/src/Generator/DceSecurityGeneratorInterface.php',
        'Ramsey\\Uuid\\Generator\\DefaultNameGenerator' => __DIR__ . '/..' . '/ramsey/uuid/src/Generator/DefaultNameGenerator.php',
        'Ramsey\\Uuid\\Generator\\DefaultTimeGenerator' => __DIR__ . '/..' . '/ramsey/uuid/src/Generator/DefaultTimeGenerator.php',
        'Ramsey\\Uuid\\Generator\\NameGeneratorFactory' => __DIR__ . '/..' . '/ramsey/uuid/src/Generator/NameGeneratorFactory.php',
        'Ramsey\\Uuid\\Generator\\NameGeneratorInterface' => __DIR__ . '/..' . '/ramsey/uuid/src/Generator/NameGeneratorInterface.php',
        'Ramsey\\Uuid\\Generator\\PeclUuidNameGenerator' => __DIR__ . '/..' . '/ramsey/uuid/src/Generator/PeclUuidNameGenerator.php',
        'Ramsey\\Uuid\\Generator\\PeclUuidRandomGenerator' => __DIR__ . '/..' . '/ramsey/uuid/src/Generator/PeclUuidRandomGenerator.php',
        'Ramsey\\Uuid\\Generator\\PeclUuidTimeGenerator' => __DIR__ . '/..' . '/ramsey/uuid/src/Generator/PeclUuidTimeGenerator.php',
        'Ramsey\\Uuid\\Generator\\RandomBytesGenerator' => __DIR__ . '/..' . '/ramsey/uuid/src/Generator/RandomBytesGenerator.php',
        'Ramsey\\Uuid\\Generator\\RandomGeneratorFactory' => __DIR__ . '/..' . '/ramsey/uuid/src/Generator/RandomGeneratorFactory.php',
        'Ramsey\\Uuid\\Generator\\RandomGeneratorInterface' => __DIR__ . '/..' . '/ramsey/uuid/src/Generator/RandomGeneratorInterface.php',
        'Ramsey\\Uuid\\Generator\\RandomLibAdapter' => __DIR__ . '/..' . '/ramsey/uuid/src/Generator/RandomLibAdapter.php',
        'Ramsey\\Uuid\\Generator\\TimeGeneratorFactory' => __DIR__ . '/..' . '/ramsey/uuid/src/Generator/TimeGeneratorFactory.php',
        'Ramsey\\Uuid\\Generator\\TimeGeneratorInterface' => __DIR__ . '/..' . '/ramsey/uuid/src/Generator/TimeGeneratorInterface.php',
        'Ramsey\\Uuid\\Generator\\UnixTimeGenerator' => __DIR__ . '/..' . '/ramsey/uuid/src/Generator/UnixTimeGenerator.php',
        'Ramsey\\Uuid\\Guid\\Fields' => __DIR__ . '/..' . '/ramsey/uuid/src/Guid/Fields.php',
        'Ramsey\\Uuid\\Guid\\Guid' => __DIR__ . '/..' . '/ramsey/uuid/src/Guid/Guid.php',
        'Ramsey\\Uuid\\Guid\\GuidBuilder' => __DIR__ . '/..' . '/ramsey/uuid/src/Guid/GuidBuilder.php',
        'Ramsey\\Uuid\\Lazy\\LazyUuidFromString' => __DIR__ . '/..' . '/ramsey/uuid/src/Lazy/LazyUuidFromString.php',
        'Ramsey\\Uuid\\Math\\BrickMathCalculator' => __DIR__ . '/..' . '/ramsey/uuid/src/Math/BrickMathCalculator.php',
        'Ramsey\\Uuid\\Math\\BrickMathRoundingMode' => __DIR__ . '/..' . '/ramsey/uuid/src/Math/BrickMathRoundingMode.php',
        'Ramsey\\Uuid\\Math\\CalculatorInterface' => __DIR__ . '/..' . '/ramsey/uuid/src/Math/CalculatorInterface.php',
        'Ramsey\\Uuid\\Math\\RoundingMode' => __DIR__ . '/..' . '/ramsey/uuid/src/Math/RoundingMode.php',
        'Ramsey\\Uuid\\Nonstandard\\Fields' => __DIR__ . '/..' . '/ramsey/uuid/src/Nonstandard/Fields.php',
        'Ramsey\\Uuid\\Nonstandard\\Uuid' => __DIR__ . '/..' . '/ramsey/uuid/src/Nonstandard/Uuid.php',
        'Ramsey\\Uuid\\Nonstandard\\UuidBuilder' => __DIR__ . '/..' . '/ramsey/uuid/src/Nonstandard/UuidBuilder.php',
        'Ramsey\\Uuid\\Nonstandard\\UuidV6' => __DIR__ . '/..' . '/ramsey/uuid/src/Nonstandard/UuidV6.php',
        'Ramsey\\Uuid\\Provider\\DceSecurityProviderInterface' => __DIR__ . '/..' . '/ramsey/uuid/src/Provider/DceSecurityProviderInterface.php',
        'Ramsey\\Uuid\\Provider\\Dce\\SystemDceSecurityProvider' => __DIR__ . '/..' . '/ramsey/uuid/src/Provider/Dce/SystemDceSecurityProvider.php',
        'Ramsey\\Uuid\\Provider\\NodeProviderInterface' => __DIR__ . '/..' . '/ramsey/uuid/src/Provider/NodeProviderInterface.php',
        'Ramsey\\Uuid\\Provider\\Node\\FallbackNodeProvider' => __DIR__ . '/..' . '/ramsey/uuid/src/Provider/Node/FallbackNodeProvider.php',
        'Ramsey\\Uuid\\Provider\\Node\\NodeProviderCollection' => __DIR__ . '/..' . '/ramsey/uuid/src/Provider/Node/NodeProviderCollection.php',
        'Ramsey\\Uuid\\Provider\\Node\\RandomNodeProvider' => __DIR__ . '/..' . '/ramsey/uuid/src/Provider/Node/RandomNodeProvider.php',
        'Ramsey\\Uuid\\Provider\\Node\\StaticNodeProvider' => __DIR__ . '/..' . '/ramsey/uuid/src/Provider/Node/StaticNodeProvider.php',
        'Ramsey\\Uuid\\Provider\\Node\\SystemNodeProvider' => __DIR__ . '/..' . '/ramsey/uuid/src/Provider/Node/SystemNodeProvider.php',
        'Ramsey\\Uuid\\Provider\\TimeProviderInterface' => __DIR__ . '/..' . '/ramsey/uuid/src/Provider/TimeProviderInterface.php',
        'Ramsey\\Uuid\\Provider\\Time\\FixedTimeProvider' => __DIR__ . '/..' . '/ramsey/uuid/src/Provider/Time/FixedTimeProvider.php',
        'Ramsey\\Uuid\\Provider\\Time\\SystemTimeProvider' => __DIR__ . '/..' . '/ramsey/uuid/src/Provider/Time/SystemTimeProvider.php',
        'Ramsey\\Uuid\\Rfc4122\\Fields' => __DIR__ . '/..' . '/ramsey/uuid/src/Rfc4122/Fields.php',
        'Ramsey\\Uuid\\Rfc4122\\FieldsInterface' => __DIR__ . '/..' . '/ramsey/uuid/src/Rfc4122/FieldsInterface.php',
        'Ramsey\\Uuid\\Rfc4122\\MaxTrait' => __DIR__ . '/..' . '/ramsey/uuid/src/Rfc4122/MaxTrait.php',
        'Ramsey\\Uuid\\Rfc4122\\MaxUuid' => __DIR__ . '/..' . '/ramsey/uuid/src/Rfc4122/MaxUuid.php',
        'Ramsey\\Uuid\\Rfc4122\\NilTrait' => __DIR__ . '/..' . '/ramsey/uuid/src/Rfc4122/NilTrait.php',
        'Ramsey\\Uuid\\Rfc4122\\NilUuid' => __DIR__ . '/..' . '/ramsey/uuid/src/Rfc4122/NilUuid.php',
        'Ramsey\\Uuid\\Rfc4122\\TimeTrait' => __DIR__ . '/..' . '/ramsey/uuid/src/Rfc4122/TimeTrait.php',
        'Ramsey\\Uuid\\Rfc4122\\UuidBuilder' => __DIR__ . '/..' . '/ramsey/uuid/src/Rfc4122/UuidBuilder.php',
        'Ramsey\\Uuid\\Rfc4122\\UuidInterface' => __DIR__ . '/..' . '/ramsey/uuid/src/Rfc4122/UuidInterface.php',
        'Ramsey\\Uuid\\Rfc4122\\UuidV1' => __DIR__ . '/..' . '/ramsey/uuid/src/Rfc4122/UuidV1.php',
        'Ramsey\\Uuid\\Rfc4122\\UuidV2' => __DIR__ . '/..' . '/ramsey/uuid/src/Rfc4122/UuidV2.php',
        'Ramsey\\Uuid\\Rfc4122\\UuidV3' => __DIR__ . '/..' . '/ramsey/uuid/src/Rfc4122/UuidV3.php',
        'Ramsey\\Uuid\\Rfc4122\\UuidV4' => __DIR__ . '/..' . '/ramsey/uuid/src/Rfc4122/UuidV4.php',
        'Ramsey\\Uuid\\Rfc4122\\UuidV5' => __DIR__ . '/..' . '/ramsey/uuid/src/Rfc4122/UuidV5.php',
        'Ramsey\\Uuid\\Rfc4122\\UuidV6' => __DIR__ . '/..' . '/ramsey/uuid/src/Rfc4122/UuidV6.php',
        'Ramsey\\Uuid\\Rfc4122\\UuidV7' => __DIR__ . '/..' . '/ramsey/uuid/src/Rfc4122/UuidV7.php',
        'Ramsey\\Uuid\\Rfc4122\\UuidV8' => __DIR__ . '/..' . '/ramsey/uuid/src/Rfc4122/UuidV8.php',
        'Ramsey\\Uuid\\Rfc4122\\Validator' => __DIR__ . '/..' . '/ramsey/uuid/src/Rfc4122/Validator.php',
        'Ramsey\\Uuid\\Rfc4122\\VariantTrait' => __DIR__ . '/..' . '/ramsey/uuid/src/Rfc4122/VariantTrait.php',
        'Ramsey\\Uuid\\Rfc4122\\VersionTrait' => __DIR__ . '/..' . '/ramsey/uuid/src/Rfc4122/VersionTrait.php',
        'Ramsey\\Uuid\\Type\\Decimal' => __DIR__ . '/..' . '/ramsey/uuid/src/Type/Decimal.php',
        'Ramsey\\Uuid\\Type\\Hexadecimal' => __DIR__ . '/..' . '/ramsey/uuid/src/Type/Hexadecimal.php',
        'Ramsey\\Uuid\\Type\\Integer' => __DIR__ . '/..' . '/ramsey/uuid/src/Type/Integer.php',
        'Ramsey\\Uuid\\Type\\NumberInterface' => __DIR__ . '/..' . '/ramsey/uuid/src/Type/NumberInterface.php',
        'Ramsey\\Uuid\\Type\\Time' => __DIR__ . '/..' . '/ramsey/uuid/src/Type/Time.php',
        'Ramsey\\Uuid\\Type\\TypeInterface' => __DIR__ . '/..' . '/ramsey/uuid/src/Type/TypeInterface.php',
        'Ramsey\\Uuid\\Uuid' => __DIR__ . '/..' . '/ramsey/uuid/src/Uuid.php',
        'Ramsey\\Uuid\\UuidFactory' => __DIR__ . '/..' . '/ramsey/uuid/src/UuidFactory.php',
        'Ramsey\\Uuid\\UuidFactoryInterface' => __DIR__ . '/..' . '/ramsey/uuid/src/UuidFactoryInterface.php',
        'Ramsey\\Uuid\\UuidInterface' => __DIR__ . '/..' . '/ramsey/uuid/src/UuidInterface.php',
        'Ramsey\\Uuid\\Validator\\GenericValidator' => __DIR__ . '/..' . '/ramsey/uuid/src/Validator/GenericValidator.php',
        'Ramsey\\Uuid\\Validator\\ValidatorInterface' => __DIR__ . '/..' . '/ramsey/uuid/src/Validator/ValidatorInterface.php',
        'Random\\BrokenRandomEngineError' => __DIR__ . '/..' . '/symfony/polyfill-php82/Resources/stubs/Random/BrokenRandomEngineError.php',
        'Random\\CryptoSafeEngine' => __DIR__ . '/..' . '/symfony/polyfill-php82/Resources/stubs/Random/CryptoSafeEngine.php',
        'Random\\Engine' => __DIR__ . '/..' . '/symfony/polyfill-php82/Resources/stubs/Random/Engine.php',
        'Random\\Engine\\Secure' => __DIR__ . '/..' . '/symfony/polyfill-php82/Resources/stubs/Random/Engine/Secure.php',
        'Random\\RandomError' => __DIR__ . '/..' . '/symfony/polyfill-php82/Resources/stubs/Random/RandomError.php',
        'Random\\RandomException' => __DIR__ . '/..' . '/symfony/polyfill-php82/Resources/stubs/Random/RandomException.php',
        'ReflectionConstant' => __DIR__ . '/..' . '/symfony/polyfill-php84/Resources/stubs/ReflectionConstant.php',
        'Revolt\\EventLoop' => __DIR__ . '/..' . '/revolt/event-loop/src/EventLoop.php',
        'Revolt\\EventLoop\\CallbackType' => __DIR__ . '/..' . '/revolt/event-loop/src/EventLoop/CallbackType.php',
        'Revolt\\EventLoop\\Driver' => __DIR__ . '/..' . '/revolt/event-loop/src/EventLoop/Driver.php',
        'Revolt\\EventLoop\\DriverFactory' => __DIR__ . '/..' . '/revolt/event-loop/src/EventLoop/DriverFactory.php',
        'Revolt\\EventLoop\\Driver\\EvDriver' => __DIR__ . '/..' . '/revolt/event-loop/src/EventLoop/Driver/EvDriver.php',
        'Revolt\\EventLoop\\Driver\\EventDriver' => __DIR__ . '/..' . '/revolt/event-loop/src/EventLoop/Driver/EventDriver.php',
        'Revolt\\EventLoop\\Driver\\StreamSelectDriver' => __DIR__ . '/..' . '/revolt/event-loop/src/EventLoop/Driver/StreamSelectDriver.php',
        'Revolt\\EventLoop\\Driver\\TracingDriver' => __DIR__ . '/..' . '/revolt/event-loop/src/EventLoop/Driver/TracingDriver.php',
        'Revolt\\EventLoop\\Driver\\UvDriver' => __DIR__ . '/..' . '/revolt/event-loop/src/EventLoop/Driver/UvDriver.php',
        'Revolt\\EventLoop\\FiberLocal' => __DIR__ . '/..' . '/revolt/event-loop/src/EventLoop/FiberLocal.php',
        'Revolt\\EventLoop\\Internal\\AbstractDriver' => __DIR__ . '/..' . '/revolt/event-loop/src/EventLoop/Internal/AbstractDriver.php',
        'Revolt\\EventLoop\\Internal\\ClosureHelper' => __DIR__ . '/..' . '/revolt/event-loop/src/EventLoop/Internal/ClosureHelper.php',
        'Revolt\\EventLoop\\Internal\\DeferCallback' => __DIR__ . '/..' . '/revolt/event-loop/src/EventLoop/Internal/DeferCallback.php',
        'Revolt\\EventLoop\\Internal\\DriverCallback' => __DIR__ . '/..' . '/revolt/event-loop/src/EventLoop/Internal/DriverCallback.php',
        'Revolt\\EventLoop\\Internal\\DriverSuspension' => __DIR__ . '/..' . '/revolt/event-loop/src/EventLoop/Internal/DriverSuspension.php',
        'Revolt\\EventLoop\\Internal\\SignalCallback' => __DIR__ . '/..' . '/revolt/event-loop/src/EventLoop/Internal/SignalCallback.php',
        'Revolt\\EventLoop\\Internal\\StreamCallback' => __DIR__ . '/..' . '/revolt/event-loop/src/EventLoop/Internal/StreamCallback.php',
        'Revolt\\EventLoop\\Internal\\StreamReadableCallback' => __DIR__ . '/..' . '/revolt/event-loop/src/EventLoop/Internal/StreamReadableCallback.php',
        'Revolt\\EventLoop\\Internal\\StreamWritableCallback' => __DIR__ . '/..' . '/revolt/event-loop/src/EventLoop/Internal/StreamWritableCallback.php',
        'Revolt\\EventLoop\\Internal\\TimerCallback' => __DIR__ . '/..' . '/revolt/event-loop/src/EventLoop/Internal/TimerCallback.php',
        'Revolt\\EventLoop\\Internal\\TimerQueue' => __DIR__ . '/..' . '/revolt/event-loop/src/EventLoop/Internal/TimerQueue.php',
        'Revolt\\EventLoop\\InvalidCallbackError' => __DIR__ . '/..' . '/revolt/event-loop/src/EventLoop/InvalidCallbackError.php',
        'Revolt\\EventLoop\\Suspension' => __DIR__ . '/..' . '/revolt/event-loop/src/EventLoop/Suspension.php',
        'Revolt\\EventLoop\\UncaughtThrowable' => __DIR__ . '/..' . '/revolt/event-loop/src/EventLoop/UncaughtThrowable.php',
        'Revolt\\EventLoop\\UnsupportedFeatureException' => __DIR__ . '/..' . '/revolt/event-loop/src/EventLoop/UnsupportedFeatureException.php',
        'RoundingMode' => __DIR__ . '/..' . '/symfony/polyfill-php84/Resources/stubs/RoundingMode.php',
        'SQLite3Exception' => __DIR__ . '/..' . '/symfony/polyfill-php83/Resources/stubs/SQLite3Exception.php',
        'SensitiveParameter' => __DIR__ . '/..' . '/symfony/polyfill-php82/Resources/stubs/SensitiveParameter.php',
        'SensitiveParameterValue' => __DIR__ . '/..' . '/symfony/polyfill-php82/Resources/stubs/SensitiveParameterValue.php',
        'SortDirection' => __DIR__ . '/..' . '/symfony/polyfill-php86/Resources/stubs/SortDirection.php',
        'Symfony\\Polyfill\\Iconv\\Iconv' => __DIR__ . '/..' . '/symfony/polyfill-iconv/Iconv.php',
        'Symfony\\Polyfill\\Mbstring\\Mbstring' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/Mbstring.php',
        'Symfony\\Polyfill\\Php82\\NoDynamicProperties' => __DIR__ . '/..' . '/symfony/polyfill-php82/NoDynamicProperties.php',
        'Symfony\\Polyfill\\Php82\\Php82' => __DIR__ . '/..' . '/symfony/polyfill-php82/Php82.php',
        'Symfony\\Polyfill\\Php82\\Random\\Engine\\Secure' => __DIR__ . '/..' . '/symfony/polyfill-php82/Random/Engine/Secure.php',
        'Symfony\\Polyfill\\Php82\\SensitiveParameterValue' => __DIR__ . '/..' . '/symfony/polyfill-php82/SensitiveParameterValue.php',
        'Symfony\\Polyfill\\Php83\\Php83' => __DIR__ . '/..' . '/symfony/polyfill-php83/Php83.php',
        'Symfony\\Polyfill\\Php84\\Php84' => __DIR__ . '/..' . '/symfony/polyfill-php84/Php84.php',
        'Symfony\\Polyfill\\Php85\\Php85' => __DIR__ . '/..' . '/symfony/polyfill-php85/Php85.php',
        'Symfony\\Polyfill\\Php86\\Php86' => __DIR__ . '/..' . '/symfony/polyfill-php86/Php86.php',
        'Webmozart\\Assert\\Assert' => __DIR__ . '/..' . '/webmozart/assert/src/Assert.php',
        'Webmozart\\Assert\\HasAssert' => __DIR__ . '/..' . '/webmozart/assert/src/HasAssert.php',
        'Webmozart\\Assert\\InvalidArgumentException' => __DIR__ . '/..' . '/webmozart/assert/src/InvalidArgumentException.php',
        'Webmozart\\Assert\\Mixin' => __DIR__ . '/..' . '/webmozart/assert/src/Mixin.php',
        'Webmozart\\Assert\\PsalmPlugin' => __DIR__ . '/..' . '/webmozart/assert/src/PsalmPlugin.php',
        'Webrtc\\AVCodec\\AVCodec' => __DIR__ . '/..' . '/danog/php-rtc-av/src/AVCodec.php',
        'Webrtc\\AVCodec\\AVFilter' => __DIR__ . '/..' . '/danog/php-rtc-av/src/AVFilter.php',
        'Webrtc\\AVCodec\\AVFormat' => __DIR__ . '/..' . '/danog/php-rtc-av/src/AVFormat.php',
        'Webrtc\\AVCodec\\Audio\\AudioChannel' => __DIR__ . '/..' . '/danog/php-rtc-av/src/Audio/AudioChannel.php',
        'Webrtc\\AVCodec\\Audio\\AudioLayout' => __DIR__ . '/..' . '/danog/php-rtc-av/src/Audio/AudioLayout.php',
        'Webrtc\\AVCodec\\Audio\\AudioResampler' => __DIR__ . '/..' . '/danog/php-rtc-av/src/Audio/AudioResampler.php',
        'Webrtc\\AVCodec\\Codec' => __DIR__ . '/..' . '/danog/php-rtc-av/src/Codec.php',
        'Webrtc\\AVCodec\\Context\\AudioContext' => __DIR__ . '/..' . '/danog/php-rtc-av/src/Context/AudioContext.php',
        'Webrtc\\AVCodec\\Context\\Context' => __DIR__ . '/..' . '/danog/php-rtc-av/src/Context/Context.php',
        'Webrtc\\AVCodec\\Context\\ContextInterface' => __DIR__ . '/..' . '/danog/php-rtc-av/src/Context/ContextInterface.php',
        'Webrtc\\AVCodec\\Context\\Dictionary' => __DIR__ . '/..' . '/danog/php-rtc-av/src/Context/Dictionary.php',
        'Webrtc\\AVCodec\\Context\\VideoContext' => __DIR__ . '/..' . '/danog/php-rtc-av/src/Context/VideoContext.php',
        'Webrtc\\AVCodec\\Data\\AudioPlane' => __DIR__ . '/..' . '/danog/php-rtc-av/src/Data/AudioPlane.php',
        'Webrtc\\AVCodec\\Data\\Buffer' => __DIR__ . '/..' . '/danog/php-rtc-av/src/Data/Buffer.php',
        'Webrtc\\AVCodec\\Data\\BufferInterface' => __DIR__ . '/..' . '/danog/php-rtc-av/src/Data/BufferInterface.php',
        'Webrtc\\AVCodec\\Data\\Packet' => __DIR__ . '/..' . '/danog/php-rtc-av/src/Data/Packet.php',
        'Webrtc\\AVCodec\\Data\\VideoPlane' => __DIR__ . '/..' . '/danog/php-rtc-av/src/Data/VideoPlane.php',
        'Webrtc\\AVCodec\\Enum\\ColorRange' => __DIR__ . '/..' . '/danog/php-rtc-av/src/Enum/ColorRange.php',
        'Webrtc\\AVCodec\\Enum\\Colorspace' => __DIR__ . '/..' . '/danog/php-rtc-av/src/Enum/Colorspace.php',
        'Webrtc\\AVCodec\\Enum\\Interpolation' => __DIR__ . '/..' . '/danog/php-rtc-av/src/Enum/Interpolation.php',
        'Webrtc\\AVCodec\\Enum\\PictureType' => __DIR__ . '/..' . '/danog/php-rtc-av/src/Enum/PictureType.php',
        'Webrtc\\AVCodec\\Exception\\AvCodecException' => __DIR__ . '/..' . '/danog/php-rtc-av/src/Exception/AvCodecException.php',
        'Webrtc\\AVCodec\\Filter\\Filter' => __DIR__ . '/..' . '/danog/php-rtc-av/src/Filter/Filter.php',
        'Webrtc\\AVCodec\\Filter\\FilterContext' => __DIR__ . '/..' . '/danog/php-rtc-av/src/Filter/FilterContext.php',
        'Webrtc\\AVCodec\\Filter\\Graph' => __DIR__ . '/..' . '/danog/php-rtc-av/src/Filter/Graph.php',
        'Webrtc\\AVCodec\\Format\\AudioFormat' => __DIR__ . '/..' . '/danog/php-rtc-av/src/Format/AudioFormat.php',
        'Webrtc\\AVCodec\\Format\\VideoFormat' => __DIR__ . '/..' . '/danog/php-rtc-av/src/Format/VideoFormat.php',
        'Webrtc\\AVCodec\\Format\\VideoFormatComponent' => __DIR__ . '/..' . '/danog/php-rtc-av/src/Format/VideoFormatComponent.php',
        'Webrtc\\AVCodec\\Frame\\AudioFrame' => __DIR__ . '/..' . '/danog/php-rtc-av/src/Frame/AudioFrame.php',
        'Webrtc\\AVCodec\\Frame\\Frame' => __DIR__ . '/..' . '/danog/php-rtc-av/src/Frame/Frame.php',
        'Webrtc\\AVCodec\\Frame\\FrameInterface' => __DIR__ . '/..' . '/danog/php-rtc-av/src/Frame/FrameInterface.php',
        'Webrtc\\AVCodec\\Frame\\OpaqueContainer' => __DIR__ . '/..' . '/danog/php-rtc-av/src/Frame/OpaqueContainer.php',
        'Webrtc\\AVCodec\\Frame\\VideoFrame' => __DIR__ . '/..' . '/danog/php-rtc-av/src/Frame/VideoFrame.php',
        'Webrtc\\AVCodec\\Frame\\VideoFrameReformater' => __DIR__ . '/..' . '/danog/php-rtc-av/src/Frame/VideoFrameReformater.php',
        'Webrtc\\AVCodec\\LibraryVersion' => __DIR__ . '/..' . '/danog/php-rtc-av/src/LibraryVersion.php',
        'Webrtc\\AVCodec\\SWScale' => __DIR__ . '/..' . '/danog/php-rtc-av/src/SWScale.php',
        'Webrtc\\AVCodec\\TransCoder' => __DIR__ . '/..' . '/danog/php-rtc-av/src/TransCoder.php',
        'Webrtc\\AVCodec\\TransCoderInterface' => __DIR__ . '/..' . '/danog/php-rtc-av/src/TransCoderInterface.php',
        'Webrtc\\AVCodec\\Util\\Util' => __DIR__ . '/..' . '/danog/php-rtc-av/src/Util/Util.php',
        'Webrtc\\Codecs\\Audio\\Opus\\OpusDecoder' => __DIR__ . '/..' . '/danog/php-rtc-codecs/src/Audio/Opus/OpusDecoder.php',
        'Webrtc\\Codecs\\Audio\\Opus\\OpusEncoder' => __DIR__ . '/..' . '/danog/php-rtc-codecs/src/Audio/Opus/OpusEncoder.php',
        'Webrtc\\Codecs\\Audio\\PCM\\PCMDecoder' => __DIR__ . '/..' . '/danog/php-rtc-codecs/src/Audio/PCM/PCMDecoder.php',
        'Webrtc\\Codecs\\Audio\\PCM\\PCMEncoder' => __DIR__ . '/..' . '/danog/php-rtc-codecs/src/Audio/PCM/PCMEncoder.php',
        'Webrtc\\Codecs\\Audio\\PCM\\PCMaDecoder' => __DIR__ . '/..' . '/danog/php-rtc-codecs/src/Audio/PCM/PCMaDecoder.php',
        'Webrtc\\Codecs\\Audio\\PCM\\PCMaEncoder' => __DIR__ . '/..' . '/danog/php-rtc-codecs/src/Audio/PCM/PCMaEncoder.php',
        'Webrtc\\Codecs\\Audio\\PCM\\PCMuDecoder' => __DIR__ . '/..' . '/danog/php-rtc-codecs/src/Audio/PCM/PCMuDecoder.php',
        'Webrtc\\Codecs\\Audio\\PCM\\PCMuEncoder' => __DIR__ . '/..' . '/danog/php-rtc-codecs/src/Audio/PCM/PCMuEncoder.php',
        'Webrtc\\Codecs\\Codec' => __DIR__ . '/..' . '/danog/php-rtc-codecs/src/Codec.php',
        'Webrtc\\Codecs\\CodecUtility' => __DIR__ . '/..' . '/danog/php-rtc-codecs/src/CodecUtility.php',
        'Webrtc\\Codecs\\DecoderInterface' => __DIR__ . '/..' . '/danog/php-rtc-codecs/src/DecoderInterface.php',
        'Webrtc\\Codecs\\EncodedPacket' => __DIR__ . '/..' . '/danog/php-rtc-codecs/src/EncodedPacket.php',
        'Webrtc\\Codecs\\Encoder' => __DIR__ . '/..' . '/danog/php-rtc-codecs/src/Encoder.php',
        'Webrtc\\Codecs\\EncoderInterface' => __DIR__ . '/..' . '/danog/php-rtc-codecs/src/EncoderInterface.php',
        'Webrtc\\Codecs\\JitterFrameInterface' => __DIR__ . '/..' . '/danog/php-rtc-codecs/src/JitterFrameInterface.php',
        'Webrtc\\Codecs\\PayloadDescriptorInterface' => __DIR__ . '/..' . '/danog/php-rtc-codecs/src/PayloadDescriptorInterface.php',
        'Webrtc\\Codecs\\Video\\Av1\\Av1Encoder' => __DIR__ . '/..' . '/danog/php-rtc-codecs/src/Video/Av1/Av1Encoder.php',
        'Webrtc\\Codecs\\Video\\Av1\\Av1PayloadDescriptor' => __DIR__ . '/..' . '/danog/php-rtc-codecs/src/Video/Av1/Av1PayloadDescriptor.php',
        'Webrtc\\Codecs\\Video\\H265\\H265Encoder' => __DIR__ . '/..' . '/danog/php-rtc-codecs/src/Video/H265/H265Encoder.php',
        'Webrtc\\Codecs\\Video\\H265\\H265PayloadDescriptor' => __DIR__ . '/..' . '/danog/php-rtc-codecs/src/Video/H265/H265PayloadDescriptor.php',
        'Webrtc\\Codecs\\Video\\Vp8\\Vp8Decoder' => __DIR__ . '/..' . '/danog/php-rtc-codecs/src/Video/Vp8/Vp8Decoder.php',
        'Webrtc\\Codecs\\Video\\Vp8\\Vp8Encoder' => __DIR__ . '/..' . '/danog/php-rtc-codecs/src/Video/Vp8/Vp8Encoder.php',
        'Webrtc\\Codecs\\Video\\Vp8\\Vp8PayloadDescriptor' => __DIR__ . '/..' . '/danog/php-rtc-codecs/src/Video/Vp8/Vp8PayloadDescriptor.php',
        'Webrtc\\Codecs\\Video\\Vp9\\Vp9Decoder' => __DIR__ . '/..' . '/danog/php-rtc-codecs/src/Video/Vp9/Vp9Decoder.php',
        'Webrtc\\Codecs\\Video\\Vp9\\Vp9Encoder' => __DIR__ . '/..' . '/danog/php-rtc-codecs/src/Video/Vp9/Vp9Encoder.php',
        'Webrtc\\Codecs\\Video\\Vp9\\Vp9PayloadDescriptor' => __DIR__ . '/..' . '/danog/php-rtc-codecs/src/Video/Vp9/Vp9PayloadDescriptor.php',
        'Webrtc\\Codecs\\Video\\X264\\H264Decoder' => __DIR__ . '/..' . '/danog/php-rtc-codecs/src/Video/X264/H264Decoder.php',
        'Webrtc\\Codecs\\Video\\X264\\H264Encoder' => __DIR__ . '/..' . '/danog/php-rtc-codecs/src/Video/X264/H264Encoder.php',
        'Webrtc\\Codecs\\Video\\X264\\H264PayloadDescriptor' => __DIR__ . '/..' . '/danog/php-rtc-codecs/src/Video/X264/H264PayloadDescriptor.php',
        'Webrtc\\DTLS\\DTLS\\Crypto' => __DIR__ . '/..' . '/danog/php-rtc-dtls/src/DTLS/Crypto.php',
        'Webrtc\\DTLS\\DTLS\\EcPrivateKey' => __DIR__ . '/..' . '/danog/php-rtc-dtls/src/DTLS/EcPrivateKey.php',
        'Webrtc\\DTLS\\DTLS\\Engine' => __DIR__ . '/..' . '/danog/php-rtc-dtls/src/DTLS/Engine.php',
        'Webrtc\\DTLS\\DTLS\\Enum\\SSLHandshakeState' => __DIR__ . '/..' . '/danog/php-rtc-dtls/src/DTLS/Enum/SSLHandshakeState.php',
        'Webrtc\\DTLS\\DTLS\\Exception\\DTLSException' => __DIR__ . '/..' . '/danog/php-rtc-dtls/src/DTLS/Exception/DTLSException.php',
        'Webrtc\\DTLS\\DTLS\\Exception\\DTLSExceptionInterface' => __DIR__ . '/..' . '/danog/php-rtc-dtls/src/DTLS/Exception/DTLSExceptionInterface.php',
        'Webrtc\\DTLS\\DTLS\\Exception\\HandshakeException' => __DIR__ . '/..' . '/danog/php-rtc-dtls/src/DTLS/Exception/HandshakeException.php',
        'Webrtc\\DTLS\\DTLS\\Exception\\RTCCertificateException' => __DIR__ . '/..' . '/danog/php-rtc-dtls/src/DTLS/Exception/RTCCertificateException.php',
        'Webrtc\\DTLS\\DTLS\\Exception\\TLSException' => __DIR__ . '/..' . '/danog/php-rtc-dtls/src/DTLS/Exception/TLSException.php',
        'Webrtc\\DTLS\\DTLS\\Handshake' => __DIR__ . '/..' . '/danog/php-rtc-dtls/src/DTLS/Handshake.php',
        'Webrtc\\DTLS\\DTLS\\Prf' => __DIR__ . '/..' . '/danog/php-rtc-dtls/src/DTLS/Prf.php',
        'Webrtc\\DTLS\\DTLS\\RTCCertificate' => __DIR__ . '/..' . '/danog/php-rtc-dtls/src/DTLS/RTCCertificate.php',
        'Webrtc\\DTLS\\DTLS\\RTCDtlsTransport' => __DIR__ . '/..' . '/danog/php-rtc-dtls/src/DTLS/RTCDtlsTransport.php',
        'Webrtc\\DTLS\\DTLS\\Reader' => __DIR__ . '/..' . '/danog/php-rtc-dtls/src/DTLS/Reader.php',
        'Webrtc\\DTLS\\DTLS\\RecordLayer' => __DIR__ . '/..' . '/danog/php-rtc-dtls/src/DTLS/RecordLayer.php',
        'Webrtc\\DTLS\\DTLS\\Srtp' => __DIR__ . '/..' . '/danog/php-rtc-dtls/src/DTLS/Srtp.php',
        'Webrtc\\DTLS\\DTLS\\TLS\\Handshake' => __DIR__ . '/..' . '/danog/php-rtc-dtls/src/DTLS/TLS/Handshake.php',
        'Webrtc\\DTLS\\DTLS\\TLS\\TLS' => __DIR__ . '/..' . '/danog/php-rtc-dtls/src/DTLS/TLS/TLS.php',
        'Webrtc\\DTLS\\Enum\\BioMethod' => __DIR__ . '/..' . '/danog/php-rtc-dtls/src/Enum/BioMethod.php',
        'Webrtc\\DTLS\\Enum\\ContextMethod' => __DIR__ . '/..' . '/danog/php-rtc-dtls/src/Enum/ContextMethod.php',
        'Webrtc\\DTLS\\Enum\\ECCurveName' => __DIR__ . '/..' . '/danog/php-rtc-dtls/src/Enum/ECCurveName.php',
        'Webrtc\\DTLS\\Enum\\PrivateKeyType' => __DIR__ . '/..' . '/danog/php-rtc-dtls/src/Enum/PrivateKeyType.php',
        'Webrtc\\DTLS\\Enum\\Verify' => __DIR__ . '/..' . '/danog/php-rtc-dtls/src/Enum/Verify.php',
        'Webrtc\\DTLS\\Exception\\OpenSSLException' => __DIR__ . '/..' . '/danog/php-rtc-dtls/src/Exception/OpenSSLException.php',
        'Webrtc\\DTLS\\Exception\\SSLException' => __DIR__ . '/..' . '/danog/php-rtc-dtls/src/Exception/SSLException.php',
        'Webrtc\\DTLS\\Exception\\SysCallException' => __DIR__ . '/..' . '/danog/php-rtc-dtls/src/Exception/SysCallException.php',
        'Webrtc\\DTLS\\Exception\\ZeroReturnException' => __DIR__ . '/..' . '/danog/php-rtc-dtls/src/Exception/ZeroReturnException.php',
        'Webrtc\\DTLS\\Listener\\DtlsTransportStateChangeListener' => __DIR__ . '/..' . '/danog/php-rtc-dtls/src/Listener/DtlsTransportStateChangeListener.php',
        'Webrtc\\DTLS\\SSL\\BIO' => __DIR__ . '/..' . '/danog/php-rtc-dtls/src/SSL/BIO.php',
        'Webrtc\\DTLS\\SSL\\BIOInterface' => __DIR__ . '/..' . '/danog/php-rtc-dtls/src/SSL/BIOInterface.php',
        'Webrtc\\DTLS\\SSL\\Context' => __DIR__ . '/..' . '/danog/php-rtc-dtls/src/SSL/Context.php',
        'Webrtc\\DTLS\\SSL\\SSL' => __DIR__ . '/..' . '/danog/php-rtc-dtls/src/SSL/SSL.php',
        'Webrtc\\DTLS\\SSL\\SSLInterface' => __DIR__ . '/..' . '/danog/php-rtc-dtls/src/SSL/SSLInterface.php',
        'Webrtc\\DataChannel\\Enum\\State' => __DIR__ . '/..' . '/danog/php-rtc-datachannel/src/Enum/State.php',
        'Webrtc\\DataChannel\\Listener\\DataChannelBufferedAmountLowListener' => __DIR__ . '/..' . '/danog/php-rtc-datachannel/src/Listener/DataChannelBufferedAmountLowListener.php',
        'Webrtc\\DataChannel\\Listener\\DataChannelCloseListener' => __DIR__ . '/..' . '/danog/php-rtc-datachannel/src/Listener/DataChannelCloseListener.php',
        'Webrtc\\DataChannel\\Listener\\DataChannelMessageListener' => __DIR__ . '/..' . '/danog/php-rtc-datachannel/src/Listener/DataChannelMessageListener.php',
        'Webrtc\\DataChannel\\Listener\\DataChannelOpenListener' => __DIR__ . '/..' . '/danog/php-rtc-datachannel/src/Listener/DataChannelOpenListener.php',
        'Webrtc\\DataChannel\\RTCDataChannel' => __DIR__ . '/..' . '/danog/php-rtc-datachannel/src/RTCDataChannel.php',
        'Webrtc\\DataChannel\\RTCDataChannelInterface' => __DIR__ . '/..' . '/danog/php-rtc-datachannel/src/RTCDataChannelInterface.php',
        'Webrtc\\DataChannel\\RTCDataChannelParameters' => __DIR__ . '/..' . '/danog/php-rtc-datachannel/src/RTCDataChannelParameters.php',
        'Webrtc\\DataChannel\\RTCSctpTransportInterface' => __DIR__ . '/..' . '/danog/php-rtc-datachannel/src/RTCSctpTransportInterface.php',
        'Webrtc\\Exception\\Exception' => __DIR__ . '/..' . '/danog/php-rtc-exception/src/Exception.php',
        'Webrtc\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/danog/php-rtc-exception/src/InvalidArgumentException.php',
        'Webrtc\\Exception\\NotImplementedException' => __DIR__ . '/..' . '/danog/php-rtc-exception/src/NotImplementedException.php',
        'Webrtc\\Exception\\RuntimeException' => __DIR__ . '/..' . '/danog/php-rtc-exception/src/RuntimeException.php',
        'Webrtc\\Exception\\WebrtcExceptionInterface' => __DIR__ . '/..' . '/danog/php-rtc-exception/src/WebrtcExceptionInterface.php',
        'Webrtc\\ICE\\Enum\\CandidateType' => __DIR__ . '/..' . '/danog/php-rtc-ice/src/Enum/CandidateType.php',
        'Webrtc\\ICE\\Enum\\CheckListState' => __DIR__ . '/..' . '/danog/php-rtc-ice/src/Enum/CheckListState.php',
        'Webrtc\\ICE\\Enum\\IceGatheringState' => __DIR__ . '/..' . '/danog/php-rtc-ice/src/Enum/IceGatheringState.php',
        'Webrtc\\ICE\\Enum\\IceRole' => __DIR__ . '/..' . '/danog/php-rtc-ice/src/Enum/IceRole.php',
        'Webrtc\\ICE\\Enum\\IceTransportState' => __DIR__ . '/..' . '/danog/php-rtc-ice/src/Enum/IceTransportState.php',
        'Webrtc\\ICE\\Enum\\RTCIceCandidatePairStats' => __DIR__ . '/..' . '/danog/php-rtc-ice/src/Enum/RTCIceCandidatePairStats.php',
        'Webrtc\\ICE\\Enum\\TransportPolicyType' => __DIR__ . '/..' . '/danog/php-rtc-ice/src/Enum/TransportPolicyType.php',
        'Webrtc\\ICE\\Enum\\TransportType' => __DIR__ . '/..' . '/danog/php-rtc-ice/src/Enum/TransportType.php',
        'Webrtc\\ICE\\IceProtocolParser' => __DIR__ . '/..' . '/danog/php-rtc-ice/src/IceProtocolParser.php',
        'Webrtc\\ICE\\Listener\\IceConnectionClosedListener' => __DIR__ . '/..' . '/danog/php-rtc-ice/src/Listener/IceConnectionClosedListener.php',
        'Webrtc\\ICE\\Listener\\IceConnectionDataListener' => __DIR__ . '/..' . '/danog/php-rtc-ice/src/Listener/IceConnectionDataListener.php',
        'Webrtc\\ICE\\Listener\\IceGathererStateChangeListener' => __DIR__ . '/..' . '/danog/php-rtc-ice/src/Listener/IceGathererStateChangeListener.php',
        'Webrtc\\ICE\\Listener\\IceTransportDataListener' => __DIR__ . '/..' . '/danog/php-rtc-ice/src/Listener/IceTransportDataListener.php',
        'Webrtc\\ICE\\Listener\\IceTransportDisconnectListener' => __DIR__ . '/..' . '/danog/php-rtc-ice/src/Listener/IceTransportDisconnectListener.php',
        'Webrtc\\ICE\\Listener\\IceTransportStateChangeListener' => __DIR__ . '/..' . '/danog/php-rtc-ice/src/Listener/IceTransportStateChangeListener.php',
        'Webrtc\\ICE\\RTCICESetting' => __DIR__ . '/..' . '/danog/php-rtc-ice/src/RTCICESetting.php',
        'Webrtc\\ICE\\RTCIceCandidate' => __DIR__ . '/..' . '/danog/php-rtc-ice/src/RTCIceCandidate.php',
        'Webrtc\\ICE\\RTCIceCandidatePair' => __DIR__ . '/..' . '/danog/php-rtc-ice/src/RTCIceCandidatePair.php',
        'Webrtc\\ICE\\RTCIceConnection' => __DIR__ . '/..' . '/danog/php-rtc-ice/src/RTCIceConnection.php',
        'Webrtc\\ICE\\RTCIceConnectionInterface' => __DIR__ . '/..' . '/danog/php-rtc-ice/src/RTCIceConnectionInterface.php',
        'Webrtc\\ICE\\RTCIceGatherer' => __DIR__ . '/..' . '/danog/php-rtc-ice/src/RTCIceGatherer.php',
        'Webrtc\\ICE\\RTCIceGathererInterface' => __DIR__ . '/..' . '/danog/php-rtc-ice/src/RTCIceGathererInterface.php',
        'Webrtc\\ICE\\RTCIceParameters' => __DIR__ . '/..' . '/danog/php-rtc-ice/src/RTCIceParameters.php',
        'Webrtc\\ICE\\RTCIceProtocolConfiguration' => __DIR__ . '/..' . '/danog/php-rtc-ice/src/RTCIceProtocolConfiguration.php',
        'Webrtc\\ICE\\RTCIceProtocolConfigurationInterface' => __DIR__ . '/..' . '/danog/php-rtc-ice/src/RTCIceProtocolConfigurationInterface.php',
        'Webrtc\\ICE\\RTCIceServer' => __DIR__ . '/..' . '/danog/php-rtc-ice/src/RTCIceServer.php',
        'Webrtc\\ICE\\RTCIceServerInterface' => __DIR__ . '/..' . '/danog/php-rtc-ice/src/RTCIceServerInterface.php',
        'Webrtc\\ICE\\RTCIceTransport' => __DIR__ . '/..' . '/danog/php-rtc-ice/src/RTCIceTransport.php',
        'Webrtc\\ICE\\RTCIceTransportInterface' => __DIR__ . '/..' . '/danog/php-rtc-ice/src/RTCIceTransportInterface.php',
        'Webrtc\\ICE\\Trait\\DNS' => __DIR__ . '/..' . '/danog/php-rtc-ice/src/Trait/DNS.php',
        'Webrtc\\ICE\\Trait\\Mdns' => __DIR__ . '/..' . '/danog/php-rtc-ice/src/Trait/Mdns.php',
        'Webrtc\\ICE\\Trait\\NetworkAdapter' => __DIR__ . '/..' . '/danog/php-rtc-ice/src/Trait/NetworkAdapter.php',
        'Webrtc\\ICE\\Utils' => __DIR__ . '/..' . '/danog/php-rtc-ice/src/Utils.php',
        'Webrtc\\MDNS\\Factory' => __DIR__ . '/..' . '/danog/php-rtc-mdns/src/Factory.php',
        'Webrtc\\MDNS\\MulticastExecutor' => __DIR__ . '/..' . '/danog/php-rtc-mdns/src/MulticastExecutor.php',
        'Webrtc\\Mixin\\DataClass' => __DIR__ . '/..' . '/danog/php-rtc-mixin/src/DataClass.php',
        'Webrtc\\Mixin\\SerializableState' => __DIR__ . '/..' . '/danog/php-rtc-mixin/src/SerializableState.php',
        'Webrtc\\Mixin\\SharedLibraryInterface' => __DIR__ . '/..' . '/danog/php-rtc-mixin/src/SharedLibraryInterface.php',
        'Webrtc\\NTP\\NetworkTimeProtocol' => __DIR__ . '/..' . '/danog/php-rtc-ntp/src/NetworkTimeProtocol.php',
        'Webrtc\\RTCP\\Exception\\RtcpExceptionInterface' => __DIR__ . '/..' . '/danog/php-rtc-rtcp/src/Exception/RtcpExceptionInterface.php',
        'Webrtc\\RTCP\\Exception\\RtcpPacketException' => __DIR__ . '/..' . '/danog/php-rtc-rtcp/src/Exception/RtcpPacketException.php',
        'Webrtc\\RTCP\\RtcpByePacket' => __DIR__ . '/..' . '/danog/php-rtc-rtcp/src/RtcpByePacket.php',
        'Webrtc\\RTCP\\RtcpConstants' => __DIR__ . '/..' . '/danog/php-rtc-rtcp/src/RtcpConstants.php',
        'Webrtc\\RTCP\\RtcpPacket' => __DIR__ . '/..' . '/danog/php-rtc-rtcp/src/RtcpPacket.php',
        'Webrtc\\RTCP\\RtcpPacketInterface' => __DIR__ . '/..' . '/danog/php-rtc-rtcp/src/RtcpPacketInterface.php',
        'Webrtc\\RTCP\\RtcpPsfbPacket' => __DIR__ . '/..' . '/danog/php-rtc-rtcp/src/RtcpPsfbPacket.php',
        'Webrtc\\RTCP\\RtcpReceiverInfo' => __DIR__ . '/..' . '/danog/php-rtc-rtcp/src/RtcpReceiverInfo.php',
        'Webrtc\\RTCP\\RtcpRrPacket' => __DIR__ . '/..' . '/danog/php-rtc-rtcp/src/RtcpRrPacket.php',
        'Webrtc\\RTCP\\RtcpRtpfbPacket' => __DIR__ . '/..' . '/danog/php-rtc-rtcp/src/RtcpRtpfbPacket.php',
        'Webrtc\\RTCP\\RtcpSdesPacket' => __DIR__ . '/..' . '/danog/php-rtc-rtcp/src/RtcpSdesPacket.php',
        'Webrtc\\RTCP\\RtcpSenderInfo' => __DIR__ . '/..' . '/danog/php-rtc-rtcp/src/RtcpSenderInfo.php',
        'Webrtc\\RTCP\\RtcpSourceInfo' => __DIR__ . '/..' . '/danog/php-rtc-rtcp/src/RtcpSourceInfo.php',
        'Webrtc\\RTCP\\RtcpSrPacket' => __DIR__ . '/..' . '/danog/php-rtc-rtcp/src/RtcpSrPacket.php',
        'Webrtc\\RTCP\\RtcpUtility' => __DIR__ . '/..' . '/danog/php-rtc-rtcp/src/RtcpUtility.php',
        'Webrtc\\RTPParameter\\RTCRtcpFeedback' => __DIR__ . '/..' . '/danog/php-rtc-rtp-parameter/src/RTCRtcpFeedback.php',
        'Webrtc\\RTPParameter\\RTCRtcpParameters' => __DIR__ . '/..' . '/danog/php-rtc-rtp-parameter/src/RTCRtcpParameters.php',
        'Webrtc\\RTPParameter\\RTCRtpCapabilities' => __DIR__ . '/..' . '/danog/php-rtc-rtp-parameter/src/RTCRtpCapabilities.php',
        'Webrtc\\RTPParameter\\RTCRtpCodecCapability' => __DIR__ . '/..' . '/danog/php-rtc-rtp-parameter/src/RTCRtpCodecCapability.php',
        'Webrtc\\RTPParameter\\RTCRtpCodecParameters' => __DIR__ . '/..' . '/danog/php-rtc-rtp-parameter/src/RTCRtpCodecParameters.php',
        'Webrtc\\RTPParameter\\RTCRtpCodingParameters' => __DIR__ . '/..' . '/danog/php-rtc-rtp-parameter/src/RTCRtpCodingParameters.php',
        'Webrtc\\RTPParameter\\RTCRtpContributingSource' => __DIR__ . '/..' . '/danog/php-rtc-rtp-parameter/src/RTCRtpContributingSource.php',
        'Webrtc\\RTPParameter\\RTCRtpDecodingParameters' => __DIR__ . '/..' . '/danog/php-rtc-rtp-parameter/src/RTCRtpDecodingParameters.php',
        'Webrtc\\RTPParameter\\RTCRtpEncodingParameters' => __DIR__ . '/..' . '/danog/php-rtc-rtp-parameter/src/RTCRtpEncodingParameters.php',
        'Webrtc\\RTPParameter\\RTCRtpHeaderExtensionCapability' => __DIR__ . '/..' . '/danog/php-rtc-rtp-parameter/src/RTCRtpHeaderExtensionCapability.php',
        'Webrtc\\RTPParameter\\RTCRtpHeaderExtensionParameters' => __DIR__ . '/..' . '/danog/php-rtc-rtp-parameter/src/RTCRtpHeaderExtensionParameters.php',
        'Webrtc\\RTPParameter\\RTCRtpParameters' => __DIR__ . '/..' . '/danog/php-rtc-rtp-parameter/src/RTCRtpParameters.php',
        'Webrtc\\RTPParameter\\RTCRtpReceiveParameters' => __DIR__ . '/..' . '/danog/php-rtc-rtp-parameter/src/RTCRtpReceiveParameters.php',
        'Webrtc\\RTPParameter\\RTCRtpRtxParameters' => __DIR__ . '/..' . '/danog/php-rtc-rtp-parameter/src/RTCRtpRtxParameters.php',
        'Webrtc\\RTPParameter\\RTCRtpSendParameters' => __DIR__ . '/..' . '/danog/php-rtc-rtp-parameter/src/RTCRtpSendParameters.php',
        'Webrtc\\RTPParameter\\RTCRtpSynchronizationSource' => __DIR__ . '/..' . '/danog/php-rtc-rtp-parameter/src/RTCRtpSynchronizationSource.php',
        'Webrtc\\RTP\\Crypto\\FrameCryptorInterface' => __DIR__ . '/..' . '/danog/php-rtc-rtp/src/Crypto/FrameCryptorInterface.php',
        'Webrtc\\RTP\\Enum\\BandwidthUsage' => __DIR__ . '/..' . '/danog/php-rtc-rtp/src/Enum/BandwidthUsage.php',
        'Webrtc\\RTP\\Enum\\MediaKind' => __DIR__ . '/..' . '/danog/php-rtc-rtp/src/Enum/MediaKind.php',
        'Webrtc\\RTP\\Enum\\RateControlState' => __DIR__ . '/..' . '/danog/php-rtc-rtp/src/Enum/RateControlState.php',
        'Webrtc\\RTP\\Exception\\MediaStreamTrackException' => __DIR__ . '/..' . '/danog/php-rtc-rtp/src/Exception/MediaStreamTrackException.php',
        'Webrtc\\RTP\\Exception\\RtpExceptionInterface' => __DIR__ . '/..' . '/danog/php-rtc-rtp/src/Exception/RtpExceptionInterface.php',
        'Webrtc\\RTP\\Exception\\RtpPacketException' => __DIR__ . '/..' . '/danog/php-rtc-rtp/src/Exception/RtpPacketException.php',
        'Webrtc\\RTP\\Exception\\RtpSenderException' => __DIR__ . '/..' . '/danog/php-rtc-rtp/src/Exception/RtpSenderException.php',
        'Webrtc\\RTP\\HeaderExtension\\HeaderExtensions' => __DIR__ . '/..' . '/danog/php-rtc-rtp/src/HeaderExtension/HeaderExtensions.php',
        'Webrtc\\RTP\\HeaderExtension\\HeaderExtensionsMap' => __DIR__ . '/..' . '/danog/php-rtc-rtp/src/HeaderExtension/HeaderExtensionsMap.php',
        'Webrtc\\RTP\\Jitter\\JitterBuffer' => __DIR__ . '/..' . '/danog/php-rtc-rtp/src/Jitter/JitterBuffer.php',
        'Webrtc\\RTP\\Jitter\\JitterFrame' => __DIR__ . '/..' . '/danog/php-rtc-rtp/src/Jitter/JitterFrame.php',
        'Webrtc\\RTP\\Listener\\MediaStreamTrackEndedListener' => __DIR__ . '/..' . '/danog/php-rtc-rtp/src/Listener/MediaStreamTrackEndedListener.php',
        'Webrtc\\RTP\\MediaStreamTrack\\MediaStreamTrack' => __DIR__ . '/..' . '/danog/php-rtc-rtp/src/MediaStreamTrack/MediaStreamTrack.php',
        'Webrtc\\RTP\\MediaStreamTrack\\MediaStreamTrackSink' => __DIR__ . '/..' . '/danog/php-rtc-rtp/src/MediaStreamTrack/MediaStreamTrackSink.php',
        'Webrtc\\RTP\\MediaStreamTrack\\RTCTrackEvent' => __DIR__ . '/..' . '/danog/php-rtc-rtp/src/MediaStreamTrack/RTCTrackEvent.php',
        'Webrtc\\RTP\\MediaStreamTrack\\RemoteStreamTrack' => __DIR__ . '/..' . '/danog/php-rtc-rtp/src/MediaStreamTrack/RemoteStreamTrack.php',
        'Webrtc\\RTP\\RTCRTPDtlsTransportInterface' => __DIR__ . '/..' . '/danog/php-rtc-rtp/src/RTCRTPDtlsTransportInterface.php',
        'Webrtc\\RTP\\RTCRtpTransceiver' => __DIR__ . '/..' . '/danog/php-rtc-rtp/src/RTCRtpTransceiver.php',
        'Webrtc\\RTP\\Receiver\\DecoderQueue' => __DIR__ . '/..' . '/danog/php-rtc-rtp/src/Receiver/DecoderQueue.php',
        'Webrtc\\RTP\\Receiver\\NackGenerator' => __DIR__ . '/..' . '/danog/php-rtc-rtp/src/Receiver/NackGenerator.php',
        'Webrtc\\RTP\\Receiver\\RTCRtpReceiver' => __DIR__ . '/..' . '/danog/php-rtc-rtp/src/Receiver/RTCRtpReceiver.php',
        'Webrtc\\RTP\\Receiver\\Rate\\AimdRateControl' => __DIR__ . '/..' . '/danog/php-rtc-rtp/src/Receiver/Rate/AimdRateControl.php',
        'Webrtc\\RTP\\Receiver\\Rate\\InterArrival' => __DIR__ . '/..' . '/danog/php-rtc-rtp/src/Receiver/Rate/InterArrival.php',
        'Webrtc\\RTP\\Receiver\\Rate\\InterArrivalDelta' => __DIR__ . '/..' . '/danog/php-rtc-rtp/src/Receiver/Rate/InterArrivalDelta.php',
        'Webrtc\\RTP\\Receiver\\Rate\\OveruseDetector' => __DIR__ . '/..' . '/danog/php-rtc-rtp/src/Receiver/Rate/OveruseDetector.php',
        'Webrtc\\RTP\\Receiver\\Rate\\OveruseEstimator' => __DIR__ . '/..' . '/danog/php-rtc-rtp/src/Receiver/Rate/OveruseEstimator.php',
        'Webrtc\\RTP\\Receiver\\Rate\\RateBucket' => __DIR__ . '/..' . '/danog/php-rtc-rtp/src/Receiver/Rate/RateBucket.php',
        'Webrtc\\RTP\\Receiver\\Rate\\RateCounter' => __DIR__ . '/..' . '/danog/php-rtc-rtp/src/Receiver/Rate/RateCounter.php',
        'Webrtc\\RTP\\Receiver\\Rate\\RemoteBitrateEstimator' => __DIR__ . '/..' . '/danog/php-rtc-rtp/src/Receiver/Rate/RemoteBitrateEstimator.php',
        'Webrtc\\RTP\\Receiver\\Rate\\TimestampGroup' => __DIR__ . '/..' . '/danog/php-rtc-rtp/src/Receiver/Rate/TimestampGroup.php',
        'Webrtc\\RTP\\Receiver\\RtpReceiverInterface' => __DIR__ . '/..' . '/danog/php-rtc-rtp/src/Receiver/RtpReceiverInterface.php',
        'Webrtc\\RTP\\Receiver\\StreamStatistics' => __DIR__ . '/..' . '/danog/php-rtc-rtp/src/Receiver/StreamStatistics.php',
        'Webrtc\\RTP\\Receiver\\TimestampMapper' => __DIR__ . '/..' . '/danog/php-rtc-rtp/src/Receiver/TimestampMapper.php',
        'Webrtc\\RTP\\RtpComponentLogger' => __DIR__ . '/..' . '/danog/php-rtc-rtp/src/RtpComponentLogger.php',
        'Webrtc\\RTP\\RtpConstants' => __DIR__ . '/..' . '/danog/php-rtc-rtp/src/RtpConstants.php',
        'Webrtc\\RTP\\RtpPacket' => __DIR__ . '/..' . '/danog/php-rtc-rtp/src/RtpPacket.php',
        'Webrtc\\RTP\\RtpRouter' => __DIR__ . '/..' . '/danog/php-rtc-rtp/src/RtpRouter.php',
        'Webrtc\\RTP\\RtpUtility' => __DIR__ . '/..' . '/danog/php-rtc-rtp/src/RtpUtility.php',
        'Webrtc\\RTP\\Sender\\RTCEncodedFrame' => __DIR__ . '/..' . '/danog/php-rtc-rtp/src/Sender/RTCEncodedFrame.php',
        'Webrtc\\RTP\\Sender\\RTCRtpSender' => __DIR__ . '/..' . '/danog/php-rtc-rtp/src/Sender/RTCRtpSender.php',
        'Webrtc\\RTP\\Sender\\RtpSenderInterface' => __DIR__ . '/..' . '/danog/php-rtc-rtp/src/Sender/RtpSenderInterface.php',
        'Webrtc\\RTP\\TransportSequenceNumbers' => __DIR__ . '/..' . '/danog/php-rtc-rtp/src/TransportSequenceNumbers.php',
        'Webrtc\\SCTP\\Chunk\\AbortChunk' => __DIR__ . '/..' . '/danog/php-rtc-sctp/src/Chunk/AbortChunk.php',
        'Webrtc\\SCTP\\Chunk\\AttributeChunk' => __DIR__ . '/..' . '/danog/php-rtc-sctp/src/Chunk/AttributeChunk.php',
        'Webrtc\\SCTP\\Chunk\\BaseInitChunk' => __DIR__ . '/..' . '/danog/php-rtc-sctp/src/Chunk/BaseInitChunk.php',
        'Webrtc\\SCTP\\Chunk\\BaseParamsChunk' => __DIR__ . '/..' . '/danog/php-rtc-sctp/src/Chunk/BaseParamsChunk.php',
        'Webrtc\\SCTP\\Chunk\\Chunk' => __DIR__ . '/..' . '/danog/php-rtc-sctp/src/Chunk/Chunk.php',
        'Webrtc\\SCTP\\Chunk\\ChunkInterface' => __DIR__ . '/..' . '/danog/php-rtc-sctp/src/Chunk/ChunkInterface.php',
        'Webrtc\\SCTP\\Chunk\\CookieAckChunk' => __DIR__ . '/..' . '/danog/php-rtc-sctp/src/Chunk/CookieAckChunk.php',
        'Webrtc\\SCTP\\Chunk\\CookieEchoChunk' => __DIR__ . '/..' . '/danog/php-rtc-sctp/src/Chunk/CookieEchoChunk.php',
        'Webrtc\\SCTP\\Chunk\\DataChunk' => __DIR__ . '/..' . '/danog/php-rtc-sctp/src/Chunk/DataChunk.php',
        'Webrtc\\SCTP\\Chunk\\ErrorChunk' => __DIR__ . '/..' . '/danog/php-rtc-sctp/src/Chunk/ErrorChunk.php',
        'Webrtc\\SCTP\\Chunk\\ForwardTsnChunk' => __DIR__ . '/..' . '/danog/php-rtc-sctp/src/Chunk/ForwardTsnChunk.php',
        'Webrtc\\SCTP\\Chunk\\HeartbeatAckChunk' => __DIR__ . '/..' . '/danog/php-rtc-sctp/src/Chunk/HeartbeatAckChunk.php',
        'Webrtc\\SCTP\\Chunk\\HeartbeatChunk' => __DIR__ . '/..' . '/danog/php-rtc-sctp/src/Chunk/HeartbeatChunk.php',
        'Webrtc\\SCTP\\Chunk\\InitAckChunk' => __DIR__ . '/..' . '/danog/php-rtc-sctp/src/Chunk/InitAckChunk.php',
        'Webrtc\\SCTP\\Chunk\\InitChunk' => __DIR__ . '/..' . '/danog/php-rtc-sctp/src/Chunk/InitChunk.php',
        'Webrtc\\SCTP\\Chunk\\ReconfigChunk' => __DIR__ . '/..' . '/danog/php-rtc-sctp/src/Chunk/ReconfigChunk.php',
        'Webrtc\\SCTP\\Chunk\\SackChunk' => __DIR__ . '/..' . '/danog/php-rtc-sctp/src/Chunk/SackChunk.php',
        'Webrtc\\SCTP\\Chunk\\ShutdownAckChunk' => __DIR__ . '/..' . '/danog/php-rtc-sctp/src/Chunk/ShutdownAckChunk.php',
        'Webrtc\\SCTP\\Chunk\\ShutdownChunk' => __DIR__ . '/..' . '/danog/php-rtc-sctp/src/Chunk/ShutdownChunk.php',
        'Webrtc\\SCTP\\Chunk\\ShutdownCompleteChunk' => __DIR__ . '/..' . '/danog/php-rtc-sctp/src/Chunk/ShutdownCompleteChunk.php',
        'Webrtc\\SCTP\\Enum\\State' => __DIR__ . '/..' . '/danog/php-rtc-sctp/src/Enum/State.php',
        'Webrtc\\SCTP\\Exception\\SctpException' => __DIR__ . '/..' . '/danog/php-rtc-sctp/src/Exception/SctpException.php',
        'Webrtc\\SCTP\\Exception\\SctpExceptionInterface' => __DIR__ . '/..' . '/danog/php-rtc-sctp/src/Exception/SctpExceptionInterface.php',
        'Webrtc\\SCTP\\InboundStream' => __DIR__ . '/..' . '/danog/php-rtc-sctp/src/InboundStream.php',
        'Webrtc\\SCTP\\Listener\\DataChannelListener' => __DIR__ . '/..' . '/danog/php-rtc-sctp/src/Listener/DataChannelListener.php',
        'Webrtc\\SCTP\\Param\\StreamAddOutgoingParam' => __DIR__ . '/..' . '/danog/php-rtc-sctp/src/Param/StreamAddOutgoingParam.php',
        'Webrtc\\SCTP\\Param\\StreamParamInterface' => __DIR__ . '/..' . '/danog/php-rtc-sctp/src/Param/StreamParamInterface.php',
        'Webrtc\\SCTP\\Param\\StreamResetOutgoingParam' => __DIR__ . '/..' . '/danog/php-rtc-sctp/src/Param/StreamResetOutgoingParam.php',
        'Webrtc\\SCTP\\Param\\StreamResetResponseParam' => __DIR__ . '/..' . '/danog/php-rtc-sctp/src/Param/StreamResetResponseParam.php',
        'Webrtc\\SCTP\\RTCSctpDtlsTransportInterface' => __DIR__ . '/..' . '/danog/php-rtc-sctp/src/RTCSctpDtlsTransportInterface.php',
        'Webrtc\\SCTP\\RTCSctpTransport' => __DIR__ . '/..' . '/danog/php-rtc-sctp/src/RTCSctpTransport.php',
        'Webrtc\\SCTP\\SctpConstant' => __DIR__ . '/..' . '/danog/php-rtc-sctp/src/SctpConstant.php',
        'Webrtc\\SCTP\\SctpPacket' => __DIR__ . '/..' . '/danog/php-rtc-sctp/src/SctpPacket.php',
        'Webrtc\\SCTP\\SctpTimer' => __DIR__ . '/..' . '/danog/php-rtc-sctp/src/SctpTimer.php',
        'Webrtc\\SCTP\\SctpUtility' => __DIR__ . '/..' . '/danog/php-rtc-sctp/src/SctpUtility.php',
        'Webrtc\\SCTP\\SignalingSinkInterface' => __DIR__ . '/..' . '/danog/php-rtc-sctp/src/SignalingSinkInterface.php',
        'Webrtc\\SCTP\\Trait\\DataChannel' => __DIR__ . '/..' . '/danog/php-rtc-sctp/src/Trait/DataChannel.php',
        'Webrtc\\SDP\\BitPattern' => __DIR__ . '/..' . '/danog/php-rtc-sdp/src/BitPattern.php',
        'Webrtc\\SDP\\DtlsParameter\\RTCDtlsFingerprint' => __DIR__ . '/..' . '/danog/php-rtc-sdp/src/DtlsParameter/RTCDtlsFingerprint.php',
        'Webrtc\\SDP\\DtlsParameter\\RTCDtlsParameters' => __DIR__ . '/..' . '/danog/php-rtc-sdp/src/DtlsParameter/RTCDtlsParameters.php',
        'Webrtc\\SDP\\Enum\\DtlsRole' => __DIR__ . '/..' . '/danog/php-rtc-sdp/src/Enum/DtlsRole.php',
        'Webrtc\\SDP\\Enum\\H264Level' => __DIR__ . '/..' . '/danog/php-rtc-sdp/src/Enum/H264Level.php',
        'Webrtc\\SDP\\Enum\\H264Profile' => __DIR__ . '/..' . '/danog/php-rtc-sdp/src/Enum/H264Profile.php',
        'Webrtc\\SDP\\Enum\\SDPDirections' => __DIR__ . '/..' . '/danog/php-rtc-sdp/src/Enum/SDPDirections.php',
        'Webrtc\\SDP\\Enum\\SDPType' => __DIR__ . '/..' . '/danog/php-rtc-sdp/src/Enum/SDPType.php',
        'Webrtc\\SDP\\GroupDescription' => __DIR__ . '/..' . '/danog/php-rtc-sdp/src/GroupDescription.php',
        'Webrtc\\SDP\\H264Sdp' => __DIR__ . '/..' . '/danog/php-rtc-sdp/src/H264Sdp.php',
        'Webrtc\\SDP\\MediaDescription' => __DIR__ . '/..' . '/danog/php-rtc-sdp/src/MediaDescription.php',
        'Webrtc\\SDP\\RTCSessionDescription' => __DIR__ . '/..' . '/danog/php-rtc-sdp/src/RTCSessionDescription.php',
        'Webrtc\\SDP\\SDPUtility' => __DIR__ . '/..' . '/danog/php-rtc-sdp/src/SDPUtility.php',
        'Webrtc\\SDP\\SctpParameter\\RTCSctpCapabilities' => __DIR__ . '/..' . '/danog/php-rtc-sdp/src/SctpParameter/RTCSctpCapabilities.php',
        'Webrtc\\SDP\\SessionDescription' => __DIR__ . '/..' . '/danog/php-rtc-sdp/src/SessionDescription.php',
        'Webrtc\\SDP\\SsrcDescription' => __DIR__ . '/..' . '/danog/php-rtc-sdp/src/SsrcDescription.php',
        'Webrtc\\STUN\\BaseProtocol' => __DIR__ . '/..' . '/danog/php-rtc-stun/src/BaseProtocol.php',
        'Webrtc\\STUN\\BaseProtocolInterface' => __DIR__ . '/..' . '/danog/php-rtc-stun/src/BaseProtocolInterface.php',
        'Webrtc\\STUN\\Datagram' => __DIR__ . '/..' . '/danog/php-rtc-stun/src/Datagram.php',
        'Webrtc\\STUN\\Enum\\MessageAttribute' => __DIR__ . '/..' . '/danog/php-rtc-stun/src/Enum/MessageAttribute.php',
        'Webrtc\\STUN\\Enum\\MessageClass' => __DIR__ . '/..' . '/danog/php-rtc-stun/src/Enum/MessageClass.php',
        'Webrtc\\STUN\\Enum\\MessageMethod' => __DIR__ . '/..' . '/danog/php-rtc-stun/src/Enum/MessageMethod.php',
        'Webrtc\\STUN\\Exception\\TransactionException' => __DIR__ . '/..' . '/danog/php-rtc-stun/src/Exception/TransactionException.php',
        'Webrtc\\STUN\\Exception\\TransactionExceptionInterface' => __DIR__ . '/..' . '/danog/php-rtc-stun/src/Exception/TransactionExceptionInterface.php',
        'Webrtc\\STUN\\Exception\\TransactionFailedException' => __DIR__ . '/..' . '/danog/php-rtc-stun/src/Exception/TransactionFailedException.php',
        'Webrtc\\STUN\\Exception\\TransactionTimeoutException' => __DIR__ . '/..' . '/danog/php-rtc-stun/src/Exception/TransactionTimeoutException.php',
        'Webrtc\\STUN\\IceCandidateInterface' => __DIR__ . '/..' . '/danog/php-rtc-stun/src/IceCandidateInterface.php',
        'Webrtc\\STUN\\IceConnectionProtocolInterface' => __DIR__ . '/..' . '/danog/php-rtc-stun/src/IceConnectionProtocolInterface.php',
        'Webrtc\\STUN\\Message\\Message' => __DIR__ . '/..' . '/danog/php-rtc-stun/src/Message/Message.php',
        'Webrtc\\STUN\\Message\\MessageAttributeCollection' => __DIR__ . '/..' . '/danog/php-rtc-stun/src/Message/MessageAttributeCollection.php',
        'Webrtc\\STUN\\Message\\MessageAttributeEncoder' => __DIR__ . '/..' . '/danog/php-rtc-stun/src/Message/MessageAttributeEncoder.php',
        'Webrtc\\STUN\\Message\\MessageIntegrity' => __DIR__ . '/..' . '/danog/php-rtc-stun/src/Message/MessageIntegrity.php',
        'Webrtc\\STUN\\Message\\MessageInterface' => __DIR__ . '/..' . '/danog/php-rtc-stun/src/Message/MessageInterface.php',
        'Webrtc\\STUN\\ReceiverInterface' => __DIR__ . '/..' . '/danog/php-rtc-stun/src/ReceiverInterface.php',
        'Webrtc\\STUN\\RequestInterface' => __DIR__ . '/..' . '/danog/php-rtc-stun/src/RequestInterface.php',
        'Webrtc\\STUN\\Stun' => __DIR__ . '/..' . '/danog/php-rtc-stun/src/Stun.php',
        'Webrtc\\STUN\\StunInterface' => __DIR__ . '/..' . '/danog/php-rtc-stun/src/StunInterface.php',
        'Webrtc\\STUN\\Trait\\CandidateSetterGetter' => __DIR__ . '/..' . '/danog/php-rtc-stun/src/Trait/CandidateSetterGetter.php',
        'Webrtc\\STUN\\Trait\\Request' => __DIR__ . '/..' . '/danog/php-rtc-stun/src/Trait/Request.php',
        'Webrtc\\STUN\\Transaction' => __DIR__ . '/..' . '/danog/php-rtc-stun/src/Transaction.php',
        'Webrtc\\STUN\\TransactionInterface' => __DIR__ . '/..' . '/danog/php-rtc-stun/src/TransactionInterface.php',
        'Webrtc\\STUN\\Utils' => __DIR__ . '/..' . '/danog/php-rtc-stun/src/Utils.php',
        'Webrtc\\Srtp\\Aes' => __DIR__ . '/..' . '/danog/php-rtc-srtp/src/Aes.php',
        'Webrtc\\Srtp\\Enum\\SrtpProfile' => __DIR__ . '/..' . '/danog/php-rtc-srtp/src/Enum/SrtpProfile.php',
        'Webrtc\\Srtp\\Enum\\SsrcType' => __DIR__ . '/..' . '/danog/php-rtc-srtp/src/Enum/SsrcType.php',
        'Webrtc\\Srtp\\Exception\\SrtpException' => __DIR__ . '/..' . '/danog/php-rtc-srtp/src/Exception/SrtpException.php',
        'Webrtc\\Srtp\\Exception\\SrtpExceptionInterface' => __DIR__ . '/..' . '/danog/php-rtc-srtp/src/Exception/SrtpExceptionInterface.php',
        'Webrtc\\Srtp\\Exception\\SrtpValidateException' => __DIR__ . '/..' . '/danog/php-rtc-srtp/src/Exception/SrtpValidateException.php',
        'Webrtc\\Srtp\\Policy' => __DIR__ . '/..' . '/danog/php-rtc-srtp/src/Policy.php',
        'Webrtc\\Srtp\\Session' => __DIR__ . '/..' . '/danog/php-rtc-srtp/src/Session.php',
        'Webrtc\\Stats\\RTCInboundRtpStreamStats' => __DIR__ . '/..' . '/danog/php-rtc-stats/src/RTCInboundRtpStreamStats.php',
        'Webrtc\\Stats\\RTCOutboundRtpStreamStats' => __DIR__ . '/..' . '/danog/php-rtc-stats/src/RTCOutboundRtpStreamStats.php',
        'Webrtc\\Stats\\RTCReceivedRtpStreamStats' => __DIR__ . '/..' . '/danog/php-rtc-stats/src/RTCReceivedRtpStreamStats.php',
        'Webrtc\\Stats\\RTCRemoteInboundRtpStreamStats' => __DIR__ . '/..' . '/danog/php-rtc-stats/src/RTCRemoteInboundRtpStreamStats.php',
        'Webrtc\\Stats\\RTCRemoteOutboundRtpStreamStats' => __DIR__ . '/..' . '/danog/php-rtc-stats/src/RTCRemoteOutboundRtpStreamStats.php',
        'Webrtc\\Stats\\RTCRtpStreamStats' => __DIR__ . '/..' . '/danog/php-rtc-stats/src/RTCRtpStreamStats.php',
        'Webrtc\\Stats\\RTCSentRtpStreamStats' => __DIR__ . '/..' . '/danog/php-rtc-stats/src/RTCSentRtpStreamStats.php',
        'Webrtc\\Stats\\RTCStats' => __DIR__ . '/..' . '/danog/php-rtc-stats/src/RTCStats.php',
        'Webrtc\\Stats\\RTCStatsReport' => __DIR__ . '/..' . '/danog/php-rtc-stats/src/RTCStatsReport.php',
        'Webrtc\\Stats\\RTCTransportStats' => __DIR__ . '/..' . '/danog/php-rtc-stats/src/RTCTransportStats.php',
        'Webrtc\\Stats\\enum\\StatType' => __DIR__ . '/..' . '/danog/php-rtc-stats/src/enum/StatType.php',
        'Webrtc\\Stats\\enum\\TLSState' => __DIR__ . '/..' . '/danog/php-rtc-stats/src/enum/TLSState.php',
        'Webrtc\\TURN\\TCPConnection' => __DIR__ . '/..' . '/danog/php-rtc-turn/src/TCPConnection.php',
        'Webrtc\\TURN\\Trait\\TurnConnection' => __DIR__ . '/..' . '/danog/php-rtc-turn/src/Trait/TurnConnection.php',
        'Webrtc\\TURN\\Turn' => __DIR__ . '/..' . '/danog/php-rtc-turn/src/Turn.php',
        'Webrtc\\TURN\\TurnConfigurationInterface' => __DIR__ . '/..' . '/danog/php-rtc-turn/src/TurnConfigurationInterface.php',
        'Webrtc\\TURN\\TurnConnectionInterface' => __DIR__ . '/..' . '/danog/php-rtc-turn/src/TurnConnectionInterface.php',
        'Webrtc\\TURN\\TurnInterface' => __DIR__ . '/..' . '/danog/php-rtc-turn/src/TurnInterface.php',
        'Webrtc\\TURN\\TurnTcpConnection' => __DIR__ . '/..' . '/danog/php-rtc-turn/src/TurnTcpConnection.php',
        'Webrtc\\TURN\\TurnUdpConnection' => __DIR__ . '/..' . '/danog/php-rtc-turn/src/TurnUdpConnection.php',
        'Webrtc\\Webrtc\\Enum\\ConnectionState' => __DIR__ . '/..' . '/danog/php-rtc-webrtc/src/Enum/ConnectionState.php',
        'Webrtc\\Webrtc\\Enum\\IceConnectionState' => __DIR__ . '/..' . '/danog/php-rtc-webrtc/src/Enum/IceConnectionState.php',
        'Webrtc\\Webrtc\\Enum\\SignalingState' => __DIR__ . '/..' . '/danog/php-rtc-webrtc/src/Enum/SignalingState.php',
        'Webrtc\\Webrtc\\Listener\\PeerConnectionConnectionStateChangeListener' => __DIR__ . '/..' . '/danog/php-rtc-webrtc/src/Listener/PeerConnectionConnectionStateChangeListener.php',
        'Webrtc\\Webrtc\\Listener\\PeerConnectionDataChannelListener' => __DIR__ . '/..' . '/danog/php-rtc-webrtc/src/Listener/PeerConnectionDataChannelListener.php',
        'Webrtc\\Webrtc\\Listener\\PeerConnectionIceConnectionStateChangeListener' => __DIR__ . '/..' . '/danog/php-rtc-webrtc/src/Listener/PeerConnectionIceConnectionStateChangeListener.php',
        'Webrtc\\Webrtc\\Listener\\PeerConnectionIceGatheringStateChangeListener' => __DIR__ . '/..' . '/danog/php-rtc-webrtc/src/Listener/PeerConnectionIceGatheringStateChangeListener.php',
        'Webrtc\\Webrtc\\Listener\\PeerConnectionSignalingStateChangeListener' => __DIR__ . '/..' . '/danog/php-rtc-webrtc/src/Listener/PeerConnectionSignalingStateChangeListener.php',
        'Webrtc\\Webrtc\\Listener\\PeerConnectionTrackListener' => __DIR__ . '/..' . '/danog/php-rtc-webrtc/src/Listener/PeerConnectionTrackListener.php',
        'Webrtc\\Webrtc\\RTCConfiguration' => __DIR__ . '/..' . '/danog/php-rtc-webrtc/src/RTCConfiguration.php',
        'Webrtc\\Webrtc\\RTCConfigurationInterface' => __DIR__ . '/..' . '/danog/php-rtc-webrtc/src/RTCConfigurationInterface.php',
        'Webrtc\\Webrtc\\RTCPeerConnection' => __DIR__ . '/..' . '/danog/php-rtc-webrtc/src/RTCPeerConnection.php',
        'Webrtc\\Webrtc\\RTCPeerConnectionInterface' => __DIR__ . '/..' . '/danog/php-rtc-webrtc/src/RTCPeerConnectionInterface.php',
        'danog\\AsyncOrm\\Annotations\\OrmMappedArray' => __DIR__ . '/..' . '/danog/async-orm/src/Annotations/OrmMappedArray.php',
        'danog\\AsyncOrm\\DbArray' => __DIR__ . '/..' . '/danog/async-orm/src/DbArray.php',
        'danog\\AsyncOrm\\DbArrayBuilder' => __DIR__ . '/..' . '/danog/async-orm/src/DbArrayBuilder.php',
        'danog\\AsyncOrm\\DbAutoProperties' => __DIR__ . '/..' . '/danog/async-orm/src/DbAutoProperties.php',
        'danog\\AsyncOrm\\DbObject' => __DIR__ . '/..' . '/danog/async-orm/src/DbObject.php',
        'danog\\AsyncOrm\\Driver\\DriverArray' => __DIR__ . '/..' . '/danog/async-orm/src/Driver/DriverArray.php',
        'danog\\AsyncOrm\\Driver\\MemoryArray' => __DIR__ . '/..' . '/danog/async-orm/src/Driver/MemoryArray.php',
        'danog\\AsyncOrm\\Driver\\SqlArray' => __DIR__ . '/..' . '/danog/async-orm/src/Driver/SqlArray.php',
        'danog\\AsyncOrm\\Internal\\Containers\\CacheContainer' => __DIR__ . '/..' . '/danog/async-orm/src/Internal/Containers/CacheContainer.php',
        'danog\\AsyncOrm\\Internal\\Containers\\ObjectContainer' => __DIR__ . '/..' . '/danog/async-orm/src/Internal/Containers/ObjectContainer.php',
        'danog\\AsyncOrm\\Internal\\Containers\\ObjectReference' => __DIR__ . '/..' . '/danog/async-orm/src/Internal/Containers/ObjectReference.php',
        'danog\\AsyncOrm\\Internal\\Driver\\CachedArray' => __DIR__ . '/..' . '/danog/async-orm/src/Internal/Driver/CachedArray.php',
        'danog\\AsyncOrm\\Internal\\Driver\\MysqlArray' => __DIR__ . '/..' . '/danog/async-orm/src/Internal/Driver/MysqlArray.php',
        'danog\\AsyncOrm\\Internal\\Driver\\ObjectArray' => __DIR__ . '/..' . '/danog/async-orm/src/Internal/Driver/ObjectArray.php',
        'danog\\AsyncOrm\\Internal\\Driver\\PostgresArray' => __DIR__ . '/..' . '/danog/async-orm/src/Internal/Driver/PostgresArray.php',
        'danog\\AsyncOrm\\Internal\\Driver\\RedisArray' => __DIR__ . '/..' . '/danog/async-orm/src/Internal/Driver/RedisArray.php',
        'danog\\AsyncOrm\\Internal\\Serializer\\BoolInt' => __DIR__ . '/..' . '/danog/async-orm/src/Internal/Serializer/BoolInt.php',
        'danog\\AsyncOrm\\Internal\\Serializer\\BoolString' => __DIR__ . '/..' . '/danog/async-orm/src/Internal/Serializer/BoolString.php',
        'danog\\AsyncOrm\\Internal\\Serializer\\ByteaSerializer' => __DIR__ . '/..' . '/danog/async-orm/src/Internal/Serializer/ByteaSerializer.php',
        'danog\\AsyncOrm\\Internal\\Serializer\\FloatString' => __DIR__ . '/..' . '/danog/async-orm/src/Internal/Serializer/FloatString.php',
        'danog\\AsyncOrm\\Internal\\Serializer\\IntString' => __DIR__ . '/..' . '/danog/async-orm/src/Internal/Serializer/IntString.php',
        'danog\\AsyncOrm\\Internal\\Serializer\\Passthrough' => __DIR__ . '/..' . '/danog/async-orm/src/Internal/Serializer/Passthrough.php',
        'danog\\AsyncOrm\\KeyType' => __DIR__ . '/..' . '/danog/async-orm/src/KeyType.php',
        'danog\\AsyncOrm\\Serializer' => __DIR__ . '/..' . '/danog/async-orm/src/Serializer.php',
        'danog\\AsyncOrm\\Serializer\\Igbinary' => __DIR__ . '/..' . '/danog/async-orm/src/Serializer/Igbinary.php',
        'danog\\AsyncOrm\\Serializer\\Json' => __DIR__ . '/..' . '/danog/async-orm/src/Serializer/Json.php',
        'danog\\AsyncOrm\\Serializer\\Native' => __DIR__ . '/..' . '/danog/async-orm/src/Serializer/Native.php',
        'danog\\AsyncOrm\\Settings' => __DIR__ . '/..' . '/danog/async-orm/src/Settings.php',
        'danog\\AsyncOrm\\Settings\\DriverSettings' => __DIR__ . '/..' . '/danog/async-orm/src/Settings/DriverSettings.php',
        'danog\\AsyncOrm\\Settings\\MemorySettings' => __DIR__ . '/..' . '/danog/async-orm/src/Settings/MemorySettings.php',
        'danog\\AsyncOrm\\Settings\\MysqlSettings' => __DIR__ . '/..' . '/danog/async-orm/src/Settings/MysqlSettings.php',
        'danog\\AsyncOrm\\Settings\\PostgresSettings' => __DIR__ . '/..' . '/danog/async-orm/src/Settings/PostgresSettings.php',
        'danog\\AsyncOrm\\Settings\\RedisSettings' => __DIR__ . '/..' . '/danog/async-orm/src/Settings/RedisSettings.php',
        'danog\\AsyncOrm\\Settings\\SqlSettings' => __DIR__ . '/..' . '/danog/async-orm/src/Settings/SqlSettings.php',
        'danog\\AsyncOrm\\ValueType' => __DIR__ . '/..' . '/danog/async-orm/src/ValueType.php',
        'danog\\BetterPrometheus\\BetterCollector' => __DIR__ . '/..' . '/danog/better-prometheus/lib/BetterCollector.php',
        'danog\\BetterPrometheus\\BetterCollectorRegistry' => __DIR__ . '/..' . '/danog/better-prometheus/lib/BetterCollectorRegistry.php',
        'danog\\BetterPrometheus\\BetterCounter' => __DIR__ . '/..' . '/danog/better-prometheus/lib/BetterCounter.php',
        'danog\\BetterPrometheus\\BetterGauge' => __DIR__ . '/..' . '/danog/better-prometheus/lib/BetterGauge.php',
        'danog\\BetterPrometheus\\BetterHistogram' => __DIR__ . '/..' . '/danog/better-prometheus/lib/BetterHistogram.php',
        'danog\\BetterPrometheus\\BetterSummary' => __DIR__ . '/..' . '/danog/better-prometheus/lib/BetterSummary.php',
        'danog\\Decoder\\FileId' => __DIR__ . '/..' . '/danog/tg-file-decoder/src/FileId.php',
        'danog\\Decoder\\FileIdType' => __DIR__ . '/..' . '/danog/tg-file-decoder/src/FileIdType.php',
        'danog\\Decoder\\PhotoSizeSource' => __DIR__ . '/..' . '/danog/tg-file-decoder/src/PhotoSizeSource.php',
        'danog\\Decoder\\PhotoSizeSourceType' => __DIR__ . '/..' . '/danog/tg-file-decoder/src/PhotoSizeSourceType.php',
        'danog\\Decoder\\PhotoSizeSource\\PhotoSizeSourceDialogPhoto' => __DIR__ . '/..' . '/danog/tg-file-decoder/src/PhotoSizeSource/PhotoSizeSourceDialogPhoto.php',
        'danog\\Decoder\\PhotoSizeSource\\PhotoSizeSourceDialogPhotoBig' => __DIR__ . '/..' . '/danog/tg-file-decoder/src/PhotoSizeSource/PhotoSizeSourceDialogPhotoBig.php',
        'danog\\Decoder\\PhotoSizeSource\\PhotoSizeSourceDialogPhotoSmall' => __DIR__ . '/..' . '/danog/tg-file-decoder/src/PhotoSizeSource/PhotoSizeSourceDialogPhotoSmall.php',
        'danog\\Decoder\\PhotoSizeSource\\PhotoSizeSourceLegacy' => __DIR__ . '/..' . '/danog/tg-file-decoder/src/PhotoSizeSource/PhotoSizeSourceLegacy.php',
        'danog\\Decoder\\PhotoSizeSource\\PhotoSizeSourceStickersetThumbnail' => __DIR__ . '/..' . '/danog/tg-file-decoder/src/PhotoSizeSource/PhotoSizeSourceStickersetThumbnail.php',
        'danog\\Decoder\\PhotoSizeSource\\PhotoSizeSourceStickersetThumbnailVersion' => __DIR__ . '/..' . '/danog/tg-file-decoder/src/PhotoSizeSource/PhotoSizeSourceStickersetThumbnailVersion.php',
        'danog\\Decoder\\PhotoSizeSource\\PhotoSizeSourceThumbnail' => __DIR__ . '/..' . '/danog/tg-file-decoder/src/PhotoSizeSource/PhotoSizeSourceThumbnail.php',
        'danog\\Decoder\\Tools' => __DIR__ . '/..' . '/danog/tg-file-decoder/src/Tools.php',
        'danog\\Decoder\\UniqueFileId' => __DIR__ . '/..' . '/danog/tg-file-decoder/src/UniqueFileId.php',
        'danog\\Decoder\\UniqueFileIdType' => __DIR__ . '/..' . '/danog/tg-file-decoder/src/UniqueFileIdType.php',
        'danog\\DialogId\\DialogId' => __DIR__ . '/..' . '/danog/tg-dialog-id/src/DialogId.php',
        'danog\\LibDNSJson\\JsonDecoder' => __DIR__ . '/..' . '/danog/libdns-json/lib/JsonDecoder.php',
        'danog\\LibDNSJson\\JsonDecoderFactory' => __DIR__ . '/..' . '/danog/libdns-json/lib/JsonDecoderFactory.php',
        'danog\\LibDNSJson\\QueryEncoder' => __DIR__ . '/..' . '/danog/libdns-json/lib/QueryEncoder.php',
        'danog\\LibDNSJson\\QueryEncoderFactory' => __DIR__ . '/..' . '/danog/libdns-json/lib/QueryEncoderFactory.php',
        'danog\\Loop\\GenericLoop' => __DIR__ . '/..' . '/danog/loop/lib/GenericLoop.php',
        'danog\\Loop\\Loop' => __DIR__ . '/..' . '/danog/loop/lib/Loop.php',
        'danog\\Loop\\PeriodicLoop' => __DIR__ . '/..' . '/danog/loop/lib/PeriodicLoop.php',
        'danog\\MadelineProto\\API' => __DIR__ . '/..' . '/danog/madelineproto/src/API.php',
        'danog\\MadelineProto\\APIWrapper' => __DIR__ . '/..' . '/danog/madelineproto/src/APIWrapper.php',
        'danog\\MadelineProto\\AbstractAPI' => __DIR__ . '/..' . '/danog/madelineproto/src/AbstractAPI.php',
        'danog\\MadelineProto\\ApiWrappers\\Start' => __DIR__ . '/..' . '/danog/madelineproto/src/ApiWrappers/Start.php',
        'danog\\MadelineProto\\AsyncTools' => __DIR__ . '/..' . '/danog/madelineproto/src/AsyncTools.php',
        'danog\\MadelineProto\\BotApiFileId' => __DIR__ . '/..' . '/danog/madelineproto/src/BotApiFileId.php',
        'danog\\MadelineProto\\Broadcast\\Action' => __DIR__ . '/..' . '/danog/madelineproto/src/Broadcast/Action.php',
        'danog\\MadelineProto\\Broadcast\\Action\\ActionForward' => __DIR__ . '/..' . '/danog/madelineproto/src/Broadcast/Action/ActionForward.php',
        'danog\\MadelineProto\\Broadcast\\Action\\ActionSend' => __DIR__ . '/..' . '/danog/madelineproto/src/Broadcast/Action/ActionSend.php',
        'danog\\MadelineProto\\Broadcast\\Broadcast' => __DIR__ . '/..' . '/danog/madelineproto/src/Broadcast/Broadcast.php',
        'danog\\MadelineProto\\Broadcast\\BroadcastCancelledException' => __DIR__ . '/..' . '/danog/madelineproto/src/Broadcast/BroadcastCancelledException.php',
        'danog\\MadelineProto\\Broadcast\\Filter' => __DIR__ . '/..' . '/danog/madelineproto/src/Broadcast/Filter.php',
        'danog\\MadelineProto\\Broadcast\\InternalState' => __DIR__ . '/..' . '/danog/madelineproto/src/Broadcast/InternalState.php',
        'danog\\MadelineProto\\Broadcast\\Progress' => __DIR__ . '/..' . '/danog/madelineproto/src/Broadcast/Progress.php',
        'danog\\MadelineProto\\Broadcast\\Status' => __DIR__ . '/..' . '/danog/madelineproto/src/Broadcast/Status.php',
        'danog\\MadelineProto\\Broadcast\\StatusInternal' => __DIR__ . '/..' . '/danog/madelineproto/src/Broadcast/StatusInternal.php',
        'danog\\MadelineProto\\CallNotAllowedException' => __DIR__ . '/..' . '/danog/madelineproto/src/CallNotAllowedException.php',
        'danog\\MadelineProto\\CallStream' => __DIR__ . '/..' . '/danog/madelineproto/src/CallStream.php',
        'danog\\MadelineProto\\Connection' => __DIR__ . '/..' . '/danog/madelineproto/src/Connection.php',
        'danog\\MadelineProto\\ContextConnector' => __DIR__ . '/..' . '/danog/madelineproto/src/ContextConnector.php',
        'danog\\MadelineProto\\Conversion' => __DIR__ . '/..' . '/danog/madelineproto/src/Conversion.php',
        'danog\\MadelineProto\\DataCenter' => __DIR__ . '/..' . '/danog/madelineproto/src/DataCenter.php',
        'danog\\MadelineProto\\DataCenterConnection' => __DIR__ . '/..' . '/danog/madelineproto/src/DataCenterConnection.php',
        'danog\\MadelineProto\\Db\\CachedArray' => __DIR__ . '/..' . '/danog/madelineproto/src/Db/CachedArray.php',
        'danog\\MadelineProto\\Db\\MemoryArray' => __DIR__ . '/..' . '/danog/madelineproto/src/Db/MemoryArray.php',
        'danog\\MadelineProto\\DoHConnector' => __DIR__ . '/..' . '/danog/madelineproto/src/DoHConnector.php',
        'danog\\MadelineProto\\DoHWrapper' => __DIR__ . '/..' . '/danog/madelineproto/src/DoHWrapper.php',
        'danog\\MadelineProto\\EventHandler' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler.php',
        'danog\\MadelineProto\\EventHandlerIssue' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandlerIssue.php',
        'danog\\MadelineProto\\EventHandler\\AbstractLocation' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/AbstractLocation.php',
        'danog\\MadelineProto\\EventHandler\\AbstractMessage' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/AbstractMessage.php',
        'danog\\MadelineProto\\EventHandler\\AbstractPoll' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/AbstractPoll.php',
        'danog\\MadelineProto\\EventHandler\\AbstractPrivateMessage' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/AbstractPrivateMessage.php',
        'danog\\MadelineProto\\EventHandler\\AbstractStory' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/AbstractStory.php',
        'danog\\MadelineProto\\EventHandler\\Action' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Action.php',
        'danog\\MadelineProto\\EventHandler\\Action\\Cancel' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Action/Cancel.php',
        'danog\\MadelineProto\\EventHandler\\Action\\ChooseContact' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Action/ChooseContact.php',
        'danog\\MadelineProto\\EventHandler\\Action\\ChooseSticker' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Action/ChooseSticker.php',
        'danog\\MadelineProto\\EventHandler\\Action\\EmojiSeen' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Action/EmojiSeen.php',
        'danog\\MadelineProto\\EventHandler\\Action\\EmojiTap' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Action/EmojiTap.php',
        'danog\\MadelineProto\\EventHandler\\Action\\GamePlay' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Action/GamePlay.php',
        'danog\\MadelineProto\\EventHandler\\Action\\GeoLocation' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Action/GeoLocation.php',
        'danog\\MadelineProto\\EventHandler\\Action\\GroupCallSpeaking' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Action/GroupCallSpeaking.php',
        'danog\\MadelineProto\\EventHandler\\Action\\HistoryImport' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Action/HistoryImport.php',
        'danog\\MadelineProto\\EventHandler\\Action\\RecordAudio' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Action/RecordAudio.php',
        'danog\\MadelineProto\\EventHandler\\Action\\RecordRound' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Action/RecordRound.php',
        'danog\\MadelineProto\\EventHandler\\Action\\RecordVideo' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Action/RecordVideo.php',
        'danog\\MadelineProto\\EventHandler\\Action\\Typing' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Action/Typing.php',
        'danog\\MadelineProto\\EventHandler\\Action\\UploadAudio' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Action/UploadAudio.php',
        'danog\\MadelineProto\\EventHandler\\Action\\UploadDocument' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Action/UploadDocument.php',
        'danog\\MadelineProto\\EventHandler\\Action\\UploadPhoto' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Action/UploadPhoto.php',
        'danog\\MadelineProto\\EventHandler\\Action\\UploadRound' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Action/UploadRound.php',
        'danog\\MadelineProto\\EventHandler\\Action\\UploadVideo' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Action/UploadVideo.php',
        'danog\\MadelineProto\\EventHandler\\Attributes\\Cron' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Attributes/Cron.php',
        'danog\\MadelineProto\\EventHandler\\Attributes\\Handler' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Attributes/Handler.php',
        'danog\\MadelineProto\\EventHandler\\BotApp' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/BotApp.php',
        'danog\\MadelineProto\\EventHandler\\BotCommands' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/BotCommands.php',
        'danog\\MadelineProto\\EventHandler\\Call' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Call.php',
        'danog\\MadelineProto\\EventHandler\\CallbackQuery' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/CallbackQuery.php',
        'danog\\MadelineProto\\EventHandler\\Calls\\AbstractGroupCall' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Calls/AbstractGroupCall.php',
        'danog\\MadelineProto\\EventHandler\\Calls\\AbstractGroupCallMessage' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Calls/AbstractGroupCallMessage.php',
        'danog\\MadelineProto\\EventHandler\\Calls\\AbstractGroupCallParticipant' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Calls/AbstractGroupCallParticipant.php',
        'danog\\MadelineProto\\EventHandler\\Calls\\CallDenialReason' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Calls/CallDenialReason.php',
        'danog\\MadelineProto\\EventHandler\\Calls\\CallStreams' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Calls/CallStreams.php',
        'danog\\MadelineProto\\EventHandler\\Calls\\ConferenceCall' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Calls/ConferenceCall.php',
        'danog\\MadelineProto\\EventHandler\\Calls\\ConferenceCallMessage' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Calls/ConferenceCallMessage.php',
        'danog\\MadelineProto\\EventHandler\\Calls\\ConferenceCallParticipant' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Calls/ConferenceCallParticipant.php',
        'danog\\MadelineProto\\EventHandler\\Calls\\GroupCall' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Calls/GroupCall.php',
        'danog\\MadelineProto\\EventHandler\\Calls\\GroupCallDonor' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Calls/GroupCallDonor.php',
        'danog\\MadelineProto\\EventHandler\\Calls\\GroupCallMessage' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Calls/GroupCallMessage.php',
        'danog\\MadelineProto\\EventHandler\\Calls\\GroupCallMessagesDeleted' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Calls/GroupCallMessagesDeleted.php',
        'danog\\MadelineProto\\EventHandler\\Calls\\GroupCallParticipant' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Calls/GroupCallParticipant.php',
        'danog\\MadelineProto\\EventHandler\\Calls\\GroupCallParticipants' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Calls/GroupCallParticipants.php',
        'danog\\MadelineProto\\EventHandler\\Calls\\GroupCallStars' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Calls/GroupCallStars.php',
        'danog\\MadelineProto\\EventHandler\\Calls\\GroupCallState' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Calls/GroupCallState.php',
        'danog\\MadelineProto\\EventHandler\\Calls\\LiveStory' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Calls/LiveStory.php',
        'danog\\MadelineProto\\EventHandler\\Calls\\LiveStoryMessage' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Calls/LiveStoryMessage.php',
        'danog\\MadelineProto\\EventHandler\\Calls\\LiveStoryParticipant' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Calls/LiveStoryParticipant.php',
        'danog\\MadelineProto\\EventHandler\\Calls\\MultiCallMessage' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Calls/MultiCallMessage.php',
        'danog\\MadelineProto\\EventHandler\\Calls\\MultiCallParticipant' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Calls/MultiCallParticipant.php',
        'danog\\MadelineProto\\EventHandler\\Calls\\PrivateCall' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Calls/PrivateCall.php',
        'danog\\MadelineProto\\EventHandler\\Channel\\ChannelParticipant' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Channel/ChannelParticipant.php',
        'danog\\MadelineProto\\EventHandler\\Channel\\MessageForwards' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Channel/MessageForwards.php',
        'danog\\MadelineProto\\EventHandler\\Channel\\MessageViewsChanged' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Channel/MessageViewsChanged.php',
        'danog\\MadelineProto\\EventHandler\\Channel\\UpdateChannel' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Channel/UpdateChannel.php',
        'danog\\MadelineProto\\EventHandler\\ChatInvite' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/ChatInvite.php',
        'danog\\MadelineProto\\EventHandler\\ChatInviteRequester' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/ChatInviteRequester.php',
        'danog\\MadelineProto\\EventHandler\\ChatInviteRequester\\BotChatInviteRequest' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/ChatInviteRequester/BotChatInviteRequest.php',
        'danog\\MadelineProto\\EventHandler\\ChatInviteRequester\\PendingJoinRequests' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/ChatInviteRequester/PendingJoinRequests.php',
        'danog\\MadelineProto\\EventHandler\\ChatInvite\\ChatInviteExported' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/ChatInvite/ChatInviteExported.php',
        'danog\\MadelineProto\\EventHandler\\ChatInvite\\ChatInvitePublicJoin' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/ChatInvite/ChatInvitePublicJoin.php',
        'danog\\MadelineProto\\EventHandler\\Command' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Command.php',
        'danog\\MadelineProto\\EventHandler\\CommandType' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/CommandType.php',
        'danog\\MadelineProto\\EventHandler\\Delete' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Delete.php',
        'danog\\MadelineProto\\EventHandler\\Delete\\DeleteChannelMessages' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Delete/DeleteChannelMessages.php',
        'danog\\MadelineProto\\EventHandler\\Delete\\DeleteMessages' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Delete/DeleteMessages.php',
        'danog\\MadelineProto\\EventHandler\\Delete\\DeleteScheduledMessages' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Delete/DeleteScheduledMessages.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\AbstractFilterFromSender' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/AbstractFilterFromSender.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\AbstractFilterFromSenders' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/AbstractFilterFromSenders.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\Combinator\\FilterNot' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/Combinator/FilterNot.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\Combinator\\FiltersAnd' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/Combinator/FiltersAnd.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\Combinator\\FiltersOr' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/Combinator/FiltersOr.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\Filter' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/Filter.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\FilterAllowAll' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/FilterAllowAll.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\FilterBotCommand' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/FilterBotCommand.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\FilterButtonQueryData' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/FilterButtonQueryData.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\FilterChannel' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/FilterChannel.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\FilterCommand' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/FilterCommand.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\FilterCommandCaseInsensitive' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/FilterCommandCaseInsensitive.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\FilterCommentReply' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/FilterCommentReply.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\FilterEdited' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/FilterEdited.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\FilterEnded' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/FilterEnded.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\FilterForwarded' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/FilterForwarded.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\FilterForwardedFrom' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/FilterForwardedFrom.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\FilterFromAdmin' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/FilterFromAdmin.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\FilterFromBot' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/FilterFromBot.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\FilterFromSender' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/FilterFromSender.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\FilterFromSenders' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/FilterFromSenders.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\FilterGroup' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/FilterGroup.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\FilterIncoming' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/FilterIncoming.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\FilterLocation' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/FilterLocation.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\FilterMedia' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/FilterMedia.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\FilterMessage' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/FilterMessage.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\FilterNoMedia' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/FilterNoMedia.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\FilterNotEdited' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/FilterNotEdited.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\FilterOutgoing' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/FilterOutgoing.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\FilterPeer' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/FilterPeer.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\FilterPoll' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/FilterPoll.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\FilterPrivate' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/FilterPrivate.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\FilterRegex' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/FilterRegex.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\FilterRegexMatchAll' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/FilterRegexMatchAll.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\FilterReply' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/FilterReply.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\FilterReplyToSelf' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/FilterReplyToSelf.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\FilterRunning' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/FilterRunning.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\FilterSecret' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/FilterSecret.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\FilterSender' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/FilterSender.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\FilterSenders' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/FilterSenders.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\FilterService' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/FilterService.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\FilterText' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/FilterText.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\FilterTextCaseInsensitive' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/FilterTextCaseInsensitive.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\FilterTextContains' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/FilterTextContains.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\FilterTextContainsCaseInsensitive' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/FilterTextContainsCaseInsensitive.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\FilterTextEnds' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/FilterTextEnds.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\FilterTextEndsCaseInsensitive' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/FilterTextEndsCaseInsensitive.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\FilterTextStarts' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/FilterTextStarts.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\FilterTextStartsCaseInsensitive' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/FilterTextStartsCaseInsensitive.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\FilterTopic' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/FilterTopic.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\FilterTopicId' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/FilterTopicId.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\Location\\FilterLiveLocation' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/Location/FilterLiveLocation.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\Location\\FilterVenue' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/Location/FilterVenue.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\Media\\FilterAudio' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/Media/FilterAudio.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\Media\\FilterDocument' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/Media/FilterDocument.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\Media\\FilterDocumentPhoto' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/Media/FilterDocumentPhoto.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\Media\\FilterGif' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/Media/FilterGif.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\Media\\FilterPhoto' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/Media/FilterPhoto.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\Media\\FilterRoundVideo' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/Media/FilterRoundVideo.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\Media\\FilterSticker' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/Media/FilterSticker.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\Media\\FilterVideo' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/Media/FilterVideo.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\Media\\FilterVoice' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/Media/FilterVoice.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\Poll\\FilterMultiplePoll' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/Poll/FilterMultiplePoll.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\Poll\\FilterQuizPoll' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/Poll/FilterQuizPoll.php',
        'danog\\MadelineProto\\EventHandler\\Filter\\Poll\\FilterSinglePoll' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Filter/Poll/FilterSinglePoll.php',
        'danog\\MadelineProto\\EventHandler\\ForwardedInfo' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/ForwardedInfo.php',
        'danog\\MadelineProto\\EventHandler\\GroupOrChannelMessage' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/GroupOrChannelMessage.php',
        'danog\\MadelineProto\\EventHandler\\InlineQuery' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/InlineQuery.php',
        'danog\\MadelineProto\\EventHandler\\InlineQueryPeerType' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/InlineQueryPeerType.php',
        'danog\\MadelineProto\\EventHandler\\Keyboard' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Keyboard.php',
        'danog\\MadelineProto\\EventHandler\\Keyboard\\InlineKeyboard' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Keyboard/InlineKeyboard.php',
        'danog\\MadelineProto\\EventHandler\\Keyboard\\ReplyKeyboard' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Keyboard/ReplyKeyboard.php',
        'danog\\MadelineProto\\EventHandler\\Location\\LiveLocation' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Location/LiveLocation.php',
        'danog\\MadelineProto\\EventHandler\\Location\\StaticLocation' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Location/StaticLocation.php',
        'danog\\MadelineProto\\EventHandler\\Location\\Venue' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Location/Venue.php',
        'danog\\MadelineProto\\EventHandler\\Media' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Media.php',
        'danog\\MadelineProto\\EventHandler\\Media\\AbstractAudio' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Media/AbstractAudio.php',
        'danog\\MadelineProto\\EventHandler\\Media\\AbstractSticker' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Media/AbstractSticker.php',
        'danog\\MadelineProto\\EventHandler\\Media\\AbstractThumbnail' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Media/AbstractThumbnail.php',
        'danog\\MadelineProto\\EventHandler\\Media\\AbstractVideo' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Media/AbstractVideo.php',
        'danog\\MadelineProto\\EventHandler\\Media\\AnimatedSticker' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Media/AnimatedSticker.php',
        'danog\\MadelineProto\\EventHandler\\Media\\Audio' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Media/Audio.php',
        'danog\\MadelineProto\\EventHandler\\Media\\CustomEmoji' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Media/CustomEmoji.php',
        'danog\\MadelineProto\\EventHandler\\Media\\Document' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Media/Document.php',
        'danog\\MadelineProto\\EventHandler\\Media\\DocumentPhoto' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Media/DocumentPhoto.php',
        'danog\\MadelineProto\\EventHandler\\Media\\GeoPoint' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Media/GeoPoint.php',
        'danog\\MadelineProto\\EventHandler\\Media\\Gif' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Media/Gif.php',
        'danog\\MadelineProto\\EventHandler\\Media\\InlineThumbnail' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Media/InlineThumbnail.php',
        'danog\\MadelineProto\\EventHandler\\Media\\MaskPosition' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Media/MaskPosition.php',
        'danog\\MadelineProto\\EventHandler\\Media\\MaskSticker' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Media/MaskSticker.php',
        'danog\\MadelineProto\\EventHandler\\Media\\MediaStory' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Media/MediaStory.php',
        'danog\\MadelineProto\\EventHandler\\Media\\Photo' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Media/Photo.php',
        'danog\\MadelineProto\\EventHandler\\Media\\RoundVideo' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Media/RoundVideo.php',
        'danog\\MadelineProto\\EventHandler\\Media\\StaticSticker' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Media/StaticSticker.php',
        'danog\\MadelineProto\\EventHandler\\Media\\Sticker' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Media/Sticker.php',
        'danog\\MadelineProto\\EventHandler\\Media\\Thumbnail' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Media/Thumbnail.php',
        'danog\\MadelineProto\\EventHandler\\Media\\Video' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Media/Video.php',
        'danog\\MadelineProto\\EventHandler\\Media\\VideoSticker' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Media/VideoSticker.php',
        'danog\\MadelineProto\\EventHandler\\Media\\VideoThumbnail' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Media/VideoThumbnail.php',
        'danog\\MadelineProto\\EventHandler\\Media\\Voice' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Media/Voice.php',
        'danog\\MadelineProto\\EventHandler\\Message' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message.php',
        'danog\\MadelineProto\\EventHandler\\Message\\ChannelMessage' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/ChannelMessage.php',
        'danog\\MadelineProto\\EventHandler\\Message\\CommentReply' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/CommentReply.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Entities\\BankCard' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Entities/BankCard.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Entities\\Blockquote' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Entities/Blockquote.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Entities\\Bold' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Entities/Bold.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Entities\\BotCommand' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Entities/BotCommand.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Entities\\Cashtag' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Entities/Cashtag.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Entities\\Code' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Entities/Code.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Entities\\CustomEmoji' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Entities/CustomEmoji.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Entities\\Email' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Entities/Email.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Entities\\FormattedDate' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Entities/FormattedDate.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Entities\\Hashtag' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Entities/Hashtag.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Entities\\InputMentionName' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Entities/InputMentionName.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Entities\\Italic' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Entities/Italic.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Entities\\Mention' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Entities/Mention.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Entities\\MentionName' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Entities/MentionName.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Entities\\MessageEntity' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Entities/MessageEntity.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Entities\\Phone' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Entities/Phone.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Entities\\Pre' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Entities/Pre.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Entities\\Spoiler' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Entities/Spoiler.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Entities\\Strike' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Entities/Strike.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Entities\\TextUrl' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Entities/TextUrl.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Entities\\TextWithEntities' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Entities/TextWithEntities.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Entities\\Underline' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Entities/Underline.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Entities\\Unknown' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Entities/Unknown.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Entities\\Url' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Entities/Url.php',
        'danog\\MadelineProto\\EventHandler\\Message\\GroupMessage' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/GroupMessage.php',
        'danog\\MadelineProto\\EventHandler\\Message\\PrivateMessage' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/PrivateMessage.php',
        'danog\\MadelineProto\\EventHandler\\Message\\ReportReason' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/ReportReason.php',
        'danog\\MadelineProto\\EventHandler\\Message\\SecretMessage' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/SecretMessage.php',
        'danog\\MadelineProto\\EventHandler\\Message\\ServiceMessage' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/ServiceMessage.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogBotAllowed' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Service/DialogBotAllowed.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogChannelCreated' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Service/DialogChannelCreated.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogChannelMigrateFrom' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Service/DialogChannelMigrateFrom.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogChatJoinedByLink' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Service/DialogChatJoinedByLink.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogChatMigrateTo' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Service/DialogChatMigrateTo.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogConferenceCall' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Service/DialogConferenceCall.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogContactSignUp' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Service/DialogContactSignUp.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogCreated' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Service/DialogCreated.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogDeleteMessages' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Service/DialogDeleteMessages.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogGameScore' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Service/DialogGameScore.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogGeoProximityReached' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Service/DialogGeoProximityReached.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogGiftPremium' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Service/DialogGiftPremium.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogGiftStars' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Service/DialogGiftStars.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogGroupCall' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Service/DialogGroupCall.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogGroupCall\\GroupCall' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Service/DialogGroupCall/GroupCall.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogGroupCall\\GroupCallInvited' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Service/DialogGroupCall/GroupCallInvited.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogGroupCall\\GroupCallScheduled' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Service/DialogGroupCall/GroupCallScheduled.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogHistoryCleared' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Service/DialogHistoryCleared.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogMemberJoinedByRequest' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Service/DialogMemberJoinedByRequest.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogMemberLeft' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Service/DialogMemberLeft.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogMembersJoined' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Service/DialogMembersJoined.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogMessagePinned' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Service/DialogMessagePinned.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogNoForwardRequest' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Service/DialogNoForwardRequest.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogNoForwardToggle' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Service/DialogNoForwardToggle.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogPaymentSent' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Service/DialogPaymentSent.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogPaymentSentMe' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Service/DialogPaymentSentMe.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogPeerRequested' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Service/DialogPeerRequested.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogPhoneCall' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Service/DialogPhoneCall.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogPhotoChanged' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Service/DialogPhotoChanged.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogReadMessages' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Service/DialogReadMessages.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogScreenshotTaken' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Service/DialogScreenshotTaken.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogSetChatTheme' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Service/DialogSetChatTheme.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogSetChatWallPaper' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Service/DialogSetChatWallPaper.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogSetTTL' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Service/DialogSetTTL.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogStarGift' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Service/DialogStarGift.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogSuggestProfilePhoto' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Service/DialogSuggestProfilePhoto.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogTitleChanged' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Service/DialogTitleChanged.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogTopicCreated' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Service/DialogTopicCreated.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogTopicEdited' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Service/DialogTopicEdited.php',
        'danog\\MadelineProto\\EventHandler\\Message\\Service\\DialogWebView' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Message/Service/DialogWebView.php',
        'danog\\MadelineProto\\EventHandler\\MultiCall' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/MultiCall.php',
        'danog\\MadelineProto\\EventHandler\\Participant' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Participant.php',
        'danog\\MadelineProto\\EventHandler\\Participant\\Admin' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Participant/Admin.php',
        'danog\\MadelineProto\\EventHandler\\Participant\\Banned' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Participant/Banned.php',
        'danog\\MadelineProto\\EventHandler\\Participant\\Creator' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Participant/Creator.php',
        'danog\\MadelineProto\\EventHandler\\Participant\\Left' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Participant/Left.php',
        'danog\\MadelineProto\\EventHandler\\Participant\\Member' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Participant/Member.php',
        'danog\\MadelineProto\\EventHandler\\Participant\\MySelf' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Participant/MySelf.php',
        'danog\\MadelineProto\\EventHandler\\Participant\\Rights' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Participant/Rights.php',
        'danog\\MadelineProto\\EventHandler\\Participant\\Rights\\Admin' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Participant/Rights/Admin.php',
        'danog\\MadelineProto\\EventHandler\\Participant\\Rights\\Banned' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Participant/Rights/Banned.php',
        'danog\\MadelineProto\\EventHandler\\Payments\\Payment' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Payments/Payment.php',
        'danog\\MadelineProto\\EventHandler\\Payments\\PaymentCharge' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Payments/PaymentCharge.php',
        'danog\\MadelineProto\\EventHandler\\Payments\\PaymentRequestedInfo' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Payments/PaymentRequestedInfo.php',
        'danog\\MadelineProto\\EventHandler\\Payments\\PostAddress' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Payments/PostAddress.php',
        'danog\\MadelineProto\\EventHandler\\Payments\\StarGift' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Payments/StarGift.php',
        'danog\\MadelineProto\\EventHandler\\Pinned' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Pinned.php',
        'danog\\MadelineProto\\EventHandler\\Pinned\\PinnedChannelMessages' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Pinned/PinnedChannelMessages.php',
        'danog\\MadelineProto\\EventHandler\\Pinned\\PinnedGroupMessages' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Pinned/PinnedGroupMessages.php',
        'danog\\MadelineProto\\EventHandler\\Pinned\\PinnedPrivateMessages' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Pinned/PinnedPrivateMessages.php',
        'danog\\MadelineProto\\EventHandler\\Plugin\\RestartPlugin' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Plugin/RestartPlugin.php',
        'danog\\MadelineProto\\EventHandler\\Poll\\MultiplePoll' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Poll/MultiplePoll.php',
        'danog\\MadelineProto\\EventHandler\\Poll\\PollAnswer' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Poll/PollAnswer.php',
        'danog\\MadelineProto\\EventHandler\\Poll\\QuizPoll' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Poll/QuizPoll.php',
        'danog\\MadelineProto\\EventHandler\\Poll\\SinglePoll' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Poll/SinglePoll.php',
        'danog\\MadelineProto\\EventHandler\\Privacy' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Privacy.php',
        'danog\\MadelineProto\\EventHandler\\Privacy\\Rule' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Privacy/Rule.php',
        'danog\\MadelineProto\\EventHandler\\Privacy\\RuleDestination' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Privacy/RuleDestination.php',
        'danog\\MadelineProto\\EventHandler\\Privacy\\RuleDestination\\AllowAll' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Privacy/RuleDestination/AllowAll.php',
        'danog\\MadelineProto\\EventHandler\\Privacy\\RuleDestination\\AllowChatParticipants' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Privacy/RuleDestination/AllowChatParticipants.php',
        'danog\\MadelineProto\\EventHandler\\Privacy\\RuleDestination\\AllowCloseFriends' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Privacy/RuleDestination/AllowCloseFriends.php',
        'danog\\MadelineProto\\EventHandler\\Privacy\\RuleDestination\\AllowContacts' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Privacy/RuleDestination/AllowContacts.php',
        'danog\\MadelineProto\\EventHandler\\Privacy\\RuleDestination\\AllowUsers' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Privacy/RuleDestination/AllowUsers.php',
        'danog\\MadelineProto\\EventHandler\\Privacy\\RuleDestination\\DisallowAll' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Privacy/RuleDestination/DisallowAll.php',
        'danog\\MadelineProto\\EventHandler\\Privacy\\RuleDestination\\DisallowChatParticipants' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Privacy/RuleDestination/DisallowChatParticipants.php',
        'danog\\MadelineProto\\EventHandler\\Privacy\\RuleDestination\\DisallowContacts' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Privacy/RuleDestination/DisallowContacts.php',
        'danog\\MadelineProto\\EventHandler\\Privacy\\RuleDestination\\DisallowUsers' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Privacy/RuleDestination/DisallowUsers.php',
        'danog\\MadelineProto\\EventHandler\\Query\\ButtonQuery' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Query/ButtonQuery.php',
        'danog\\MadelineProto\\EventHandler\\Query\\ChatButtonQuery' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Query/ChatButtonQuery.php',
        'danog\\MadelineProto\\EventHandler\\Query\\ChatGameQuery' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Query/ChatGameQuery.php',
        'danog\\MadelineProto\\EventHandler\\Query\\ChatTrait' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Query/ChatTrait.php',
        'danog\\MadelineProto\\EventHandler\\Query\\GameQuery' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Query/GameQuery.php',
        'danog\\MadelineProto\\EventHandler\\Query\\InlineButtonQuery' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Query/InlineButtonQuery.php',
        'danog\\MadelineProto\\EventHandler\\Query\\InlineGameQuery' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Query/InlineGameQuery.php',
        'danog\\MadelineProto\\EventHandler\\Query\\InlineTrait' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Query/InlineTrait.php',
        'danog\\MadelineProto\\EventHandler\\SimpleFilter\\Ended' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/SimpleFilter/Ended.php',
        'danog\\MadelineProto\\EventHandler\\SimpleFilter\\FromAdmin' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/SimpleFilter/FromAdmin.php',
        'danog\\MadelineProto\\EventHandler\\SimpleFilter\\FromAdminOrOutgoing' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/SimpleFilter/FromAdminOrOutgoing.php',
        'danog\\MadelineProto\\EventHandler\\SimpleFilter\\HasAudio' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/SimpleFilter/HasAudio.php',
        'danog\\MadelineProto\\EventHandler\\SimpleFilter\\HasDocument' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/SimpleFilter/HasDocument.php',
        'danog\\MadelineProto\\EventHandler\\SimpleFilter\\HasDocumentPhoto' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/SimpleFilter/HasDocumentPhoto.php',
        'danog\\MadelineProto\\EventHandler\\SimpleFilter\\HasGif' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/SimpleFilter/HasGif.php',
        'danog\\MadelineProto\\EventHandler\\SimpleFilter\\HasLiveLocation' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/SimpleFilter/HasLiveLocation.php',
        'danog\\MadelineProto\\EventHandler\\SimpleFilter\\HasLocation' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/SimpleFilter/HasLocation.php',
        'danog\\MadelineProto\\EventHandler\\SimpleFilter\\HasMedia' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/SimpleFilter/HasMedia.php',
        'danog\\MadelineProto\\EventHandler\\SimpleFilter\\HasMultiplePoll' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/SimpleFilter/HasMultiplePoll.php',
        'danog\\MadelineProto\\EventHandler\\SimpleFilter\\HasNoMedia' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/SimpleFilter/HasNoMedia.php',
        'danog\\MadelineProto\\EventHandler\\SimpleFilter\\HasPhoto' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/SimpleFilter/HasPhoto.php',
        'danog\\MadelineProto\\EventHandler\\SimpleFilter\\HasPoll' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/SimpleFilter/HasPoll.php',
        'danog\\MadelineProto\\EventHandler\\SimpleFilter\\HasQuizPoll' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/SimpleFilter/HasQuizPoll.php',
        'danog\\MadelineProto\\EventHandler\\SimpleFilter\\HasRoundVideo' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/SimpleFilter/HasRoundVideo.php',
        'danog\\MadelineProto\\EventHandler\\SimpleFilter\\HasSinglePoll' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/SimpleFilter/HasSinglePoll.php',
        'danog\\MadelineProto\\EventHandler\\SimpleFilter\\HasSticker' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/SimpleFilter/HasSticker.php',
        'danog\\MadelineProto\\EventHandler\\SimpleFilter\\HasTopic' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/SimpleFilter/HasTopic.php',
        'danog\\MadelineProto\\EventHandler\\SimpleFilter\\HasVenue' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/SimpleFilter/HasVenue.php',
        'danog\\MadelineProto\\EventHandler\\SimpleFilter\\HasVideo' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/SimpleFilter/HasVideo.php',
        'danog\\MadelineProto\\EventHandler\\SimpleFilter\\HasVoice' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/SimpleFilter/HasVoice.php',
        'danog\\MadelineProto\\EventHandler\\SimpleFilter\\Incoming' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/SimpleFilter/Incoming.php',
        'danog\\MadelineProto\\EventHandler\\SimpleFilter\\IsEdited' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/SimpleFilter/IsEdited.php',
        'danog\\MadelineProto\\EventHandler\\SimpleFilter\\IsForwarded' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/SimpleFilter/IsForwarded.php',
        'danog\\MadelineProto\\EventHandler\\SimpleFilter\\IsNotEdited' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/SimpleFilter/IsNotEdited.php',
        'danog\\MadelineProto\\EventHandler\\SimpleFilter\\IsReply' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/SimpleFilter/IsReply.php',
        'danog\\MadelineProto\\EventHandler\\SimpleFilter\\IsReplyToSelf' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/SimpleFilter/IsReplyToSelf.php',
        'danog\\MadelineProto\\EventHandler\\SimpleFilter\\Outgoing' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/SimpleFilter/Outgoing.php',
        'danog\\MadelineProto\\EventHandler\\SimpleFilter\\Running' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/SimpleFilter/Running.php',
        'danog\\MadelineProto\\EventHandler\\SimpleFilters' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/SimpleFilters.php',
        'danog\\MadelineProto\\EventHandler\\Story\\Story' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Story/Story.php',
        'danog\\MadelineProto\\EventHandler\\Story\\StoryDeleted' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Story/StoryDeleted.php',
        'danog\\MadelineProto\\EventHandler\\Story\\StoryReaction' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Story/StoryReaction.php',
        'danog\\MadelineProto\\EventHandler\\Topic\\IconColor' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Topic/IconColor.php',
        'danog\\MadelineProto\\EventHandler\\Typing' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Typing.php',
        'danog\\MadelineProto\\EventHandler\\Typing\\ChatUserTyping' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Typing/ChatUserTyping.php',
        'danog\\MadelineProto\\EventHandler\\Typing\\SecretUserTyping' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Typing/SecretUserTyping.php',
        'danog\\MadelineProto\\EventHandler\\Typing\\SupergroupUserTyping' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Typing/SupergroupUserTyping.php',
        'danog\\MadelineProto\\EventHandler\\Typing\\UserTyping' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Typing/UserTyping.php',
        'danog\\MadelineProto\\EventHandler\\Update' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Update.php',
        'danog\\MadelineProto\\EventHandler\\User\\Blocked' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/User/Blocked.php',
        'danog\\MadelineProto\\EventHandler\\User\\BotStopped' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/User/BotStopped.php',
        'danog\\MadelineProto\\EventHandler\\User\\Phone' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/User/Phone.php',
        'danog\\MadelineProto\\EventHandler\\User\\Status' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/User/Status.php',
        'danog\\MadelineProto\\EventHandler\\User\\Status\\Emoji' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/User/Status/Emoji.php',
        'danog\\MadelineProto\\EventHandler\\User\\Status\\EmptyStatus' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/User/Status/EmptyStatus.php',
        'danog\\MadelineProto\\EventHandler\\User\\Status\\LastMonth' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/User/Status/LastMonth.php',
        'danog\\MadelineProto\\EventHandler\\User\\Status\\LastWeek' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/User/Status/LastWeek.php',
        'danog\\MadelineProto\\EventHandler\\User\\Status\\Offline' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/User/Status/Offline.php',
        'danog\\MadelineProto\\EventHandler\\User\\Status\\Online' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/User/Status/Online.php',
        'danog\\MadelineProto\\EventHandler\\User\\Status\\Recently' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/User/Status/Recently.php',
        'danog\\MadelineProto\\EventHandler\\User\\Username' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/User/Username.php',
        'danog\\MadelineProto\\EventHandler\\User\\UsernameInfo' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/User/UsernameInfo.php',
        'danog\\MadelineProto\\EventHandler\\Wallpaper' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Wallpaper.php',
        'danog\\MadelineProto\\EventHandler\\Wallpaper\\WallpaperSettings' => __DIR__ . '/..' . '/danog/madelineproto/src/EventHandler/Wallpaper/WallpaperSettings.php',
        'danog\\MadelineProto\\Exception' => __DIR__ . '/..' . '/danog/madelineproto/src/Exception.php',
        'danog\\MadelineProto\\FileCallback' => __DIR__ . '/..' . '/danog/madelineproto/src/FileCallback.php',
        'danog\\MadelineProto\\FileCallbackInterface' => __DIR__ . '/..' . '/danog/madelineproto/src/FileCallbackInterface.php',
        'danog\\MadelineProto\\FileRedirect' => __DIR__ . '/..' . '/danog/madelineproto/src/FileRedirect.php',
        'danog\\MadelineProto\\GarbageCollector' => __DIR__ . '/..' . '/danog/madelineproto/src/GarbageCollector.php',
        'danog\\MadelineProto\\GroupCall\\GroupCallController' => __DIR__ . '/..' . '/danog/madelineproto/src/GroupCall/GroupCallController.php',
        'danog\\MadelineProto\\GroupCall\\Handler' => __DIR__ . '/..' . '/danog/madelineproto/src/GroupCall/Handler.php',
        'danog\\MadelineProto\\GroupCall\\OggDemuxer' => __DIR__ . '/..' . '/danog/madelineproto/src/GroupCall/OggDemuxer.php',
        'danog\\MadelineProto\\GroupCall\\Rtmp\\Amf0' => __DIR__ . '/..' . '/danog/madelineproto/src/GroupCall/Rtmp/Amf0.php',
        'danog\\MadelineProto\\GroupCall\\Rtmp\\Amf0EcmaArray' => __DIR__ . '/..' . '/danog/madelineproto/src/GroupCall/Rtmp/Amf0EcmaArray.php',
        'danog\\MadelineProto\\GroupCall\\Rtmp\\FlvTags' => __DIR__ . '/..' . '/danog/madelineproto/src/GroupCall/Rtmp/FlvTags.php',
        'danog\\MadelineProto\\GroupCall\\Rtmp\\RtmpClient' => __DIR__ . '/..' . '/danog/madelineproto/src/GroupCall/Rtmp/RtmpClient.php',
        'danog\\MadelineProto\\GroupCall\\Rtmp\\RtmpPublisher' => __DIR__ . '/..' . '/danog/madelineproto/src/GroupCall/Rtmp/RtmpPublisher.php',
        'danog\\MadelineProto\\GroupCall\\Rtmp\\RtmpRefusedException' => __DIR__ . '/..' . '/danog/madelineproto/src/GroupCall/Rtmp/RtmpRefusedException.php',
        'danog\\MadelineProto\\GroupCall\\StreamReceiver' => __DIR__ . '/..' . '/danog/madelineproto/src/GroupCall/StreamReceiver.php',
        'danog\\MadelineProto\\InternalDoc' => __DIR__ . '/..' . '/danog/madelineproto/src/InternalDoc.php',
        'danog\\MadelineProto\\Ipc\\AbstractServer' => __DIR__ . '/..' . '/danog/madelineproto/src/Ipc/AbstractServer.php',
        'danog\\MadelineProto\\Ipc\\AlreadyServedException' => __DIR__ . '/..' . '/danog/madelineproto/src/Ipc/AlreadyServedException.php',
        'danog\\MadelineProto\\Ipc\\Client' => __DIR__ . '/..' . '/danog/madelineproto/src/Ipc/Client.php',
        'danog\\MadelineProto\\Ipc\\ClientAbstract' => __DIR__ . '/..' . '/danog/madelineproto/src/Ipc/ClientAbstract.php',
        'danog\\MadelineProto\\Ipc\\EventHandlerProxy' => __DIR__ . '/..' . '/danog/madelineproto/src/Ipc/EventHandlerProxy.php',
        'danog\\MadelineProto\\Ipc\\ExitFailure' => __DIR__ . '/..' . '/danog/madelineproto/src/Ipc/ExitFailure.php',
        'danog\\MadelineProto\\Ipc\\IpcCapable' => __DIR__ . '/..' . '/danog/madelineproto/src/Ipc/IpcCapable.php',
        'danog\\MadelineProto\\Ipc\\IpcState' => __DIR__ . '/..' . '/danog/madelineproto/src/Ipc/IpcState.php',
        'danog\\MadelineProto\\Ipc\\Runner\\ProcessRunner' => __DIR__ . '/..' . '/danog/madelineproto/src/Ipc/Runner/ProcessRunner.php',
        'danog\\MadelineProto\\Ipc\\Runner\\RunnerAbstract' => __DIR__ . '/..' . '/danog/madelineproto/src/Ipc/Runner/RunnerAbstract.php',
        'danog\\MadelineProto\\Ipc\\Runner\\WebRunner' => __DIR__ . '/..' . '/danog/madelineproto/src/Ipc/Runner/WebRunner.php',
        'danog\\MadelineProto\\Ipc\\Server' => __DIR__ . '/..' . '/danog/madelineproto/src/Ipc/Server.php',
        'danog\\MadelineProto\\Ipc\\ServerCallback' => __DIR__ . '/..' . '/danog/madelineproto/src/Ipc/ServerCallback.php',
        'danog\\MadelineProto\\Ipc\\Wrapper' => __DIR__ . '/..' . '/danog/madelineproto/src/Ipc/Wrapper.php',
        'danog\\MadelineProto\\Ipc\\Wrapper\\Cancellation' => __DIR__ . '/..' . '/danog/madelineproto/src/Ipc/Wrapper/Cancellation.php',
        'danog\\MadelineProto\\Ipc\\Wrapper\\CancellationInner' => __DIR__ . '/..' . '/danog/madelineproto/src/Ipc/Wrapper/CancellationInner.php',
        'danog\\MadelineProto\\Ipc\\Wrapper\\CancellationReference' => __DIR__ . '/..' . '/danog/madelineproto/src/Ipc/Wrapper/CancellationReference.php',
        'danog\\MadelineProto\\Ipc\\Wrapper\\ClosableTrait' => __DIR__ . '/..' . '/danog/madelineproto/src/Ipc/Wrapper/ClosableTrait.php',
        'danog\\MadelineProto\\Ipc\\Wrapper\\FileCallback' => __DIR__ . '/..' . '/danog/madelineproto/src/Ipc/Wrapper/FileCallback.php',
        'danog\\MadelineProto\\Ipc\\Wrapper\\Obj' => __DIR__ . '/..' . '/danog/madelineproto/src/Ipc/Wrapper/Obj.php',
        'danog\\MadelineProto\\Ipc\\Wrapper\\ReadableStream' => __DIR__ . '/..' . '/danog/madelineproto/src/Ipc/Wrapper/ReadableStream.php',
        'danog\\MadelineProto\\Ipc\\Wrapper\\SeekableReadableStream' => __DIR__ . '/..' . '/danog/madelineproto/src/Ipc/Wrapper/SeekableReadableStream.php',
        'danog\\MadelineProto\\Ipc\\Wrapper\\SeekableTrait' => __DIR__ . '/..' . '/danog/madelineproto/src/Ipc/Wrapper/SeekableTrait.php',
        'danog\\MadelineProto\\Ipc\\Wrapper\\SeekableWritableStream' => __DIR__ . '/..' . '/danog/madelineproto/src/Ipc/Wrapper/SeekableWritableStream.php',
        'danog\\MadelineProto\\Ipc\\Wrapper\\WrapMethodTrait' => __DIR__ . '/..' . '/danog/madelineproto/src/Ipc/Wrapper/WrapMethodTrait.php',
        'danog\\MadelineProto\\Ipc\\Wrapper\\WrappedCancellation' => __DIR__ . '/..' . '/danog/madelineproto/src/Ipc/Wrapper/WrappedCancellation.php',
        'danog\\MadelineProto\\Ipc\\Wrapper\\WritableStream' => __DIR__ . '/..' . '/danog/madelineproto/src/Ipc/Wrapper/WritableStream.php',
        'danog\\MadelineProto\\Lang' => __DIR__ . '/..' . '/danog/madelineproto/src/Lang.php',
        'danog\\MadelineProto\\LegacyMigrator' => __DIR__ . '/..' . '/danog/madelineproto/src/LegacyMigrator.php',
        'danog\\MadelineProto\\LightState' => __DIR__ . '/..' . '/danog/madelineproto/src/LightState.php',
        'danog\\MadelineProto\\LocalDirectory' => __DIR__ . '/..' . '/danog/madelineproto/src/LocalDirectory.php',
        'danog\\MadelineProto\\LocalFile' => __DIR__ . '/..' . '/danog/madelineproto/src/LocalFile.php',
        'danog\\MadelineProto\\Logger' => __DIR__ . '/..' . '/danog/madelineproto/src/Logger.php',
        'danog\\MadelineProto\\LoggerGetter' => __DIR__ . '/..' . '/danog/madelineproto/src/LoggerGetter.php',
        'danog\\MadelineProto\\Loop\\APILoop' => __DIR__ . '/..' . '/danog/madelineproto/src/Loop/APILoop.php',
        'danog\\MadelineProto\\Loop\\Connection\\CleanupLoop' => __DIR__ . '/..' . '/danog/madelineproto/src/Loop/Connection/CleanupLoop.php',
        'danog\\MadelineProto\\Loop\\Connection\\Common' => __DIR__ . '/..' . '/danog/madelineproto/src/Loop/Connection/Common.php',
        'danog\\MadelineProto\\Loop\\Connection\\PingLoop' => __DIR__ . '/..' . '/danog/madelineproto/src/Loop/Connection/PingLoop.php',
        'danog\\MadelineProto\\Loop\\Connection\\ReadLoop' => __DIR__ . '/..' . '/danog/madelineproto/src/Loop/Connection/ReadLoop.php',
        'danog\\MadelineProto\\Loop\\Connection\\WriteLoop' => __DIR__ . '/..' . '/danog/madelineproto/src/Loop/Connection/WriteLoop.php',
        'danog\\MadelineProto\\Loop\\Generic\\PeriodicLoopInternal' => __DIR__ . '/..' . '/danog/madelineproto/src/Loop/Generic/PeriodicLoopInternal.php',
        'danog\\MadelineProto\\Loop\\InternalLoop' => __DIR__ . '/..' . '/danog/madelineproto/src/Loop/InternalLoop.php',
        'danog\\MadelineProto\\Loop\\LoggerLoop' => __DIR__ . '/..' . '/danog/madelineproto/src/Loop/LoggerLoop.php',
        'danog\\MadelineProto\\Loop\\Secret\\SecretFeedLoop' => __DIR__ . '/..' . '/danog/madelineproto/src/Loop/Secret/SecretFeedLoop.php',
        'danog\\MadelineProto\\Loop\\Update\\FeedLoop' => __DIR__ . '/..' . '/danog/madelineproto/src/Loop/Update/FeedLoop.php',
        'danog\\MadelineProto\\Loop\\Update\\SeqLoop' => __DIR__ . '/..' . '/danog/madelineproto/src/Loop/Update/SeqLoop.php',
        'danog\\MadelineProto\\Loop\\Update\\UpdateLoop' => __DIR__ . '/..' . '/danog/madelineproto/src/Loop/Update/UpdateLoop.php',
        'danog\\MadelineProto\\Loop\\VoIPLoop' => __DIR__ . '/..' . '/danog/madelineproto/src/Loop/VoIPLoop.php',
        'danog\\MadelineProto\\Loop\\VoIP\\DjLoop' => __DIR__ . '/..' . '/danog/madelineproto/src/Loop/VoIP/DjLoop.php',
        'danog\\MadelineProto\\Loop\\VoIP\\StreamSnapshot' => __DIR__ . '/..' . '/danog/madelineproto/src/Loop/VoIP/StreamSnapshot.php',
        'danog\\MadelineProto\\MTProto' => __DIR__ . '/..' . '/danog/madelineproto/src/MTProto.php',
        'danog\\MadelineProto\\MTProtoSession\\AuthKeyHandler' => __DIR__ . '/..' . '/danog/madelineproto/src/MTProtoSession/AuthKeyHandler.php',
        'danog\\MadelineProto\\MTProtoSession\\CallHandler' => __DIR__ . '/..' . '/danog/madelineproto/src/MTProtoSession/CallHandler.php',
        'danog\\MadelineProto\\MTProtoSession\\MsgIdHandler' => __DIR__ . '/..' . '/danog/madelineproto/src/MTProtoSession/MsgIdHandler.php',
        'danog\\MadelineProto\\MTProtoSession\\Reliable' => __DIR__ . '/..' . '/danog/madelineproto/src/MTProtoSession/Reliable.php',
        'danog\\MadelineProto\\MTProtoSession\\ResponseHandler' => __DIR__ . '/..' . '/danog/madelineproto/src/MTProtoSession/ResponseHandler.php',
        'danog\\MadelineProto\\MTProtoSession\\SeqNoHandler' => __DIR__ . '/..' . '/danog/madelineproto/src/MTProtoSession/SeqNoHandler.php',
        'danog\\MadelineProto\\MTProtoSession\\Session' => __DIR__ . '/..' . '/danog/madelineproto/src/MTProtoSession/Session.php',
        'danog\\MadelineProto\\MTProtoTools\\AuthKeyHandler' => __DIR__ . '/..' . '/danog/madelineproto/src/MTProtoTools/AuthKeyHandler.php',
        'danog\\MadelineProto\\MTProtoTools\\CallHandler' => __DIR__ . '/..' . '/danog/madelineproto/src/MTProtoTools/CallHandler.php',
        'danog\\MadelineProto\\MTProtoTools\\CombinedUpdatesState' => __DIR__ . '/..' . '/danog/madelineproto/src/MTProtoTools/CombinedUpdatesState.php',
        'danog\\MadelineProto\\MTProtoTools\\Crypt' => __DIR__ . '/..' . '/danog/madelineproto/src/MTProtoTools/Crypt.php',
        'danog\\MadelineProto\\MTProtoTools\\Crypt\\AesCtr' => __DIR__ . '/..' . '/danog/madelineproto/src/MTProtoTools/Crypt/AesCtr.php',
        'danog\\MadelineProto\\MTProtoTools\\Crypt\\IGE' => __DIR__ . '/..' . '/danog/madelineproto/src/MTProtoTools/Crypt/IGE.php',
        'danog\\MadelineProto\\MTProtoTools\\Crypt\\IGEOpenssl' => __DIR__ . '/..' . '/danog/madelineproto/src/MTProtoTools/Crypt/IGEOpenssl.php',
        'danog\\MadelineProto\\MTProtoTools\\Crypt\\IGEPhpseclib' => __DIR__ . '/..' . '/danog/madelineproto/src/MTProtoTools/Crypt/IGEPhpseclib.php',
        'danog\\MadelineProto\\MTProtoTools\\FileRef\\FileRefEnvironment' => __DIR__ . '/..' . '/danog/madelineproto/src/MTProtoTools/FileRef/FileRefEnvironment.php',
        'danog\\MadelineProto\\MTProtoTools\\FileRef\\FileRefMap' => __DIR__ . '/..' . '/danog/madelineproto/src/MTProtoTools/FileRef/FileRefMap.php',
        'danog\\MadelineProto\\MTProtoTools\\FileRef\\FileRefMarker' => __DIR__ . '/..' . '/danog/madelineproto/src/MTProtoTools/FileRef/FileRefMarker.php',
        'danog\\MadelineProto\\MTProtoTools\\FileRef\\FileRefMethodCall' => __DIR__ . '/..' . '/danog/madelineproto/src/MTProtoTools/FileRef/FileRefMethodCall.php',
        'danog\\MadelineProto\\MTProtoTools\\FileRef\\FileRefTraversal' => __DIR__ . '/..' . '/danog/madelineproto/src/MTProtoTools/FileRef/FileRefTraversal.php',
        'danog\\MadelineProto\\MTProtoTools\\FileServer' => __DIR__ . '/..' . '/danog/madelineproto/src/MTProtoTools/FileServer.php',
        'danog\\MadelineProto\\MTProtoTools\\Files' => __DIR__ . '/..' . '/danog/madelineproto/src/MTProtoTools/Files.php',
        'danog\\MadelineProto\\MTProtoTools\\FilesAbstraction' => __DIR__ . '/..' . '/danog/madelineproto/src/MTProtoTools/FilesAbstraction.php',
        'danog\\MadelineProto\\MTProtoTools\\FilesLogic' => __DIR__ . '/..' . '/danog/madelineproto/src/MTProtoTools/FilesLogic.php',
        'danog\\MadelineProto\\MTProtoTools\\MinDatabase' => __DIR__ . '/..' . '/danog/madelineproto/src/MTProtoTools/MinDatabase.php',
        'danog\\MadelineProto\\MTProtoTools\\PasswordCalculator' => __DIR__ . '/..' . '/danog/madelineproto/src/MTProtoTools/PasswordCalculator.php',
        'danog\\MadelineProto\\MTProtoTools\\PeerDatabase' => __DIR__ . '/..' . '/danog/madelineproto/src/MTProtoTools/PeerDatabase.php',
        'danog\\MadelineProto\\MTProtoTools\\PeerHandler' => __DIR__ . '/..' . '/danog/madelineproto/src/MTProtoTools/PeerHandler.php',
        'danog\\MadelineProto\\MTProtoTools\\ReferenceDatabase' => __DIR__ . '/..' . '/danog/madelineproto/src/MTProtoTools/ReferenceDatabase.php',
        'danog\\MadelineProto\\MTProtoTools\\ResponseInfo' => __DIR__ . '/..' . '/danog/madelineproto/src/MTProtoTools/ResponseInfo.php',
        'danog\\MadelineProto\\MTProtoTools\\ResumableCalls' => __DIR__ . '/..' . '/danog/madelineproto/src/MTProtoTools/ResumableCalls.php',
        'danog\\MadelineProto\\MTProtoTools\\TransferProgress' => __DIR__ . '/..' . '/danog/madelineproto/src/MTProtoTools/TransferProgress.php',
        'danog\\MadelineProto\\MTProtoTools\\UpdateHandler' => __DIR__ . '/..' . '/danog/madelineproto/src/MTProtoTools/UpdateHandler.php',
        'danog\\MadelineProto\\MTProtoTools\\UpdatesState' => __DIR__ . '/..' . '/danog/madelineproto/src/MTProtoTools/UpdatesState.php',
        'danog\\MadelineProto\\MTProto\\AuthKey' => __DIR__ . '/..' . '/danog/madelineproto/src/MTProto/AuthKey.php',
        'danog\\MadelineProto\\MTProto\\ConnectionState' => __DIR__ . '/..' . '/danog/madelineproto/src/MTProto/ConnectionState.php',
        'danog\\MadelineProto\\MTProto\\Container' => __DIR__ . '/..' . '/danog/madelineproto/src/MTProto/Container.php',
        'danog\\MadelineProto\\MTProto\\LinkedList' => __DIR__ . '/..' . '/danog/madelineproto/src/MTProto/LinkedList.php',
        'danog\\MadelineProto\\MTProto\\LoginState' => __DIR__ . '/..' . '/danog/madelineproto/src/MTProto/LoginState.php',
        'danog\\MadelineProto\\MTProto\\MTProtoIncomingMessage' => __DIR__ . '/..' . '/danog/madelineproto/src/MTProto/MTProtoIncomingMessage.php',
        'danog\\MadelineProto\\MTProto\\MTProtoMessage' => __DIR__ . '/..' . '/danog/madelineproto/src/MTProto/MTProtoMessage.php',
        'danog\\MadelineProto\\MTProto\\MTProtoOutgoingMessage' => __DIR__ . '/..' . '/danog/madelineproto/src/MTProto/MTProtoOutgoingMessage.php',
        'danog\\MadelineProto\\MTProto\\NewAuthKey' => __DIR__ . '/..' . '/danog/madelineproto/src/MTProto/NewAuthKey.php',
        'danog\\MadelineProto\\MTProto\\PermAuthKey' => __DIR__ . '/..' . '/danog/madelineproto/src/MTProto/PermAuthKey.php',
        'danog\\MadelineProto\\MTProto\\SpecialMethodType' => __DIR__ . '/..' . '/danog/madelineproto/src/MTProto/SpecialMethodType.php',
        'danog\\MadelineProto\\MTProto\\TempAuthKey' => __DIR__ . '/..' . '/danog/madelineproto/src/MTProto/TempAuthKey.php',
        'danog\\MadelineProto\\Magic' => __DIR__ . '/..' . '/danog/madelineproto/src/Magic.php',
        'danog\\MadelineProto\\Matroska' => __DIR__ . '/..' . '/danog/madelineproto/src/Matroska.php',
        'danog\\MadelineProto\\MatroskaWriter' => __DIR__ . '/..' . '/danog/madelineproto/src/MatroskaWriter.php',
        'danog\\MadelineProto\\MediaDestination' => __DIR__ . '/..' . '/danog/madelineproto/src/MediaDestination.php',
        'danog\\MadelineProto\\Mp4' => __DIR__ . '/..' . '/danog/madelineproto/src/Mp4.php',
        'danog\\MadelineProto\\MyTelegramOrgWrapper' => __DIR__ . '/..' . '/danog/madelineproto/src/MyTelegramOrgWrapper.php',
        'danog\\MadelineProto\\Namespace\\AbstractAPI' => __DIR__ . '/..' . '/danog/madelineproto/src/Namespace/AbstractAPI.php',
        'danog\\MadelineProto\\Namespace\\Account' => __DIR__ . '/..' . '/danog/madelineproto/src/Namespace/Account.php',
        'danog\\MadelineProto\\Namespace\\Aicompose' => __DIR__ . '/..' . '/danog/madelineproto/src/Namespace/Aicompose.php',
        'danog\\MadelineProto\\Namespace\\Auth' => __DIR__ . '/..' . '/danog/madelineproto/src/Namespace/Auth.php',
        'danog\\MadelineProto\\Namespace\\Blacklist' => __DIR__ . '/..' . '/danog/madelineproto/src/Namespace/Blacklist.php',
        'danog\\MadelineProto\\Namespace\\Bots' => __DIR__ . '/..' . '/danog/madelineproto/src/Namespace/Bots.php',
        'danog\\MadelineProto\\Namespace\\Channels' => __DIR__ . '/..' . '/danog/madelineproto/src/Namespace/Channels.php',
        'danog\\MadelineProto\\Namespace\\Chatlists' => __DIR__ . '/..' . '/danog/madelineproto/src/Namespace/Chatlists.php',
        'danog\\MadelineProto\\Namespace\\Communities' => __DIR__ . '/..' . '/danog/madelineproto/src/Namespace/Communities.php',
        'danog\\MadelineProto\\Namespace\\Contacts' => __DIR__ . '/..' . '/danog/madelineproto/src/Namespace/Contacts.php',
        'danog\\MadelineProto\\Namespace\\Ephemeral' => __DIR__ . '/..' . '/danog/madelineproto/src/Namespace/Ephemeral.php',
        'danog\\MadelineProto\\Namespace\\Folders' => __DIR__ . '/..' . '/danog/madelineproto/src/Namespace/Folders.php',
        'danog\\MadelineProto\\Namespace\\Fragment' => __DIR__ . '/..' . '/danog/madelineproto/src/Namespace/Fragment.php',
        'danog\\MadelineProto\\Namespace\\Help' => __DIR__ . '/..' . '/danog/madelineproto/src/Namespace/Help.php',
        'danog\\MadelineProto\\Namespace\\Langpack' => __DIR__ . '/..' . '/danog/madelineproto/src/Namespace/Langpack.php',
        'danog\\MadelineProto\\Namespace\\Messages' => __DIR__ . '/..' . '/danog/madelineproto/src/Namespace/Messages.php',
        'danog\\MadelineProto\\Namespace\\Payments' => __DIR__ . '/..' . '/danog/madelineproto/src/Namespace/Payments.php',
        'danog\\MadelineProto\\Namespace\\Phone' => __DIR__ . '/..' . '/danog/madelineproto/src/Namespace/Phone.php',
        'danog\\MadelineProto\\Namespace\\Photos' => __DIR__ . '/..' . '/danog/madelineproto/src/Namespace/Photos.php',
        'danog\\MadelineProto\\Namespace\\Premium' => __DIR__ . '/..' . '/danog/madelineproto/src/Namespace/Premium.php',
        'danog\\MadelineProto\\Namespace\\Smsjobs' => __DIR__ . '/..' . '/danog/madelineproto/src/Namespace/Smsjobs.php',
        'danog\\MadelineProto\\Namespace\\Stats' => __DIR__ . '/..' . '/danog/madelineproto/src/Namespace/Stats.php',
        'danog\\MadelineProto\\Namespace\\Stickers' => __DIR__ . '/..' . '/danog/madelineproto/src/Namespace/Stickers.php',
        'danog\\MadelineProto\\Namespace\\Stories' => __DIR__ . '/..' . '/danog/madelineproto/src/Namespace/Stories.php',
        'danog\\MadelineProto\\Namespace\\Toncenter' => __DIR__ . '/..' . '/danog/madelineproto/src/Namespace/Toncenter.php',
        'danog\\MadelineProto\\Namespace\\Updates' => __DIR__ . '/..' . '/danog/madelineproto/src/Namespace/Updates.php',
        'danog\\MadelineProto\\Namespace\\Upload' => __DIR__ . '/..' . '/danog/madelineproto/src/Namespace/Upload.php',
        'danog\\MadelineProto\\Namespace\\Users' => __DIR__ . '/..' . '/danog/madelineproto/src/Namespace/Users.php',
        'danog\\MadelineProto\\Namespace\\Wallet' => __DIR__ . '/..' . '/danog/madelineproto/src/Namespace/Wallet.php',
        'danog\\MadelineProto\\NothingInTheSocketException' => __DIR__ . '/..' . '/danog/madelineproto/src/NothingInTheSocketException.php',
        'danog\\MadelineProto\\Ogg' => __DIR__ . '/..' . '/danog/madelineproto/src/Ogg.php',
        'danog\\MadelineProto\\OggWriter' => __DIR__ . '/..' . '/danog/madelineproto/src/OggWriter.php',
        'danog\\MadelineProto\\PTSException' => __DIR__ . '/..' . '/danog/madelineproto/src/PTSException.php',
        'danog\\MadelineProto\\ParseMode' => __DIR__ . '/..' . '/danog/madelineproto/src/ParseMode.php',
        'danog\\MadelineProto\\PeerNotInDbException' => __DIR__ . '/..' . '/danog/madelineproto/src/PeerNotInDbException.php',
        'danog\\MadelineProto\\PluginEventHandler' => __DIR__ . '/..' . '/danog/madelineproto/src/PluginEventHandler.php',
        'danog\\MadelineProto\\PsrLogger' => __DIR__ . '/..' . '/danog/madelineproto/src/PsrLogger.php',
        'danog\\MadelineProto\\RPCErrorException' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCErrorException.php',
        'danog\\MadelineProto\\RPCError\\AllowPaymentRequiredError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/AllowPaymentRequiredError.php',
        'danog\\MadelineProto\\RPCError\\BalanceTooLowError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/BalanceTooLowError.php',
        'danog\\MadelineProto\\RPCError\\BotGamesDisabledError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/BotGamesDisabledError.php',
        'danog\\MadelineProto\\RPCError\\BotPaymentsDisabledError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/BotPaymentsDisabledError.php',
        'danog\\MadelineProto\\RPCError\\BroadcastPublicVotersForbiddenError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/BroadcastPublicVotersForbiddenError.php',
        'danog\\MadelineProto\\RPCError\\BusinessConnectionNotAllowedError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/BusinessConnectionNotAllowedError.php',
        'danog\\MadelineProto\\RPCError\\BusinessPeerUsageMissingError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/BusinessPeerUsageMissingError.php',
        'danog\\MadelineProto\\RPCError\\ButtonUserPrivacyRestrictedError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/ButtonUserPrivacyRestrictedError.php',
        'danog\\MadelineProto\\RPCError\\CallAlreadyAcceptedError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/CallAlreadyAcceptedError.php',
        'danog\\MadelineProto\\RPCError\\CallAlreadyDeclinedError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/CallAlreadyDeclinedError.php',
        'danog\\MadelineProto\\RPCError\\ChannelInvalidError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/ChannelInvalidError.php',
        'danog\\MadelineProto\\RPCError\\ChannelMonoforumUnsupportedError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/ChannelMonoforumUnsupportedError.php',
        'danog\\MadelineProto\\RPCError\\ChannelPrivateError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/ChannelPrivateError.php',
        'danog\\MadelineProto\\RPCError\\ChatAdminRequiredError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/ChatAdminRequiredError.php',
        'danog\\MadelineProto\\RPCError\\ChatForbiddenError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/ChatForbiddenError.php',
        'danog\\MadelineProto\\RPCError\\ChatForwardsRestrictedError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/ChatForwardsRestrictedError.php',
        'danog\\MadelineProto\\RPCError\\ChatGuestSendForbiddenError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/ChatGuestSendForbiddenError.php',
        'danog\\MadelineProto\\RPCError\\ChatRestrictedError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/ChatRestrictedError.php',
        'danog\\MadelineProto\\RPCError\\ChatSendAudiosForbiddenError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/ChatSendAudiosForbiddenError.php',
        'danog\\MadelineProto\\RPCError\\ChatSendDocsForbiddenError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/ChatSendDocsForbiddenError.php',
        'danog\\MadelineProto\\RPCError\\ChatSendGifsForbiddenError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/ChatSendGifsForbiddenError.php',
        'danog\\MadelineProto\\RPCError\\ChatSendMediaForbiddenError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/ChatSendMediaForbiddenError.php',
        'danog\\MadelineProto\\RPCError\\ChatSendPhotosForbiddenError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/ChatSendPhotosForbiddenError.php',
        'danog\\MadelineProto\\RPCError\\ChatSendPlainForbiddenError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/ChatSendPlainForbiddenError.php',
        'danog\\MadelineProto\\RPCError\\ChatSendPollForbiddenError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/ChatSendPollForbiddenError.php',
        'danog\\MadelineProto\\RPCError\\ChatSendRoundvideosForbiddenError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/ChatSendRoundvideosForbiddenError.php',
        'danog\\MadelineProto\\RPCError\\ChatSendStickersForbiddenError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/ChatSendStickersForbiddenError.php',
        'danog\\MadelineProto\\RPCError\\ChatSendVideosForbiddenError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/ChatSendVideosForbiddenError.php',
        'danog\\MadelineProto\\RPCError\\ChatSendVoicesForbiddenError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/ChatSendVoicesForbiddenError.php',
        'danog\\MadelineProto\\RPCError\\ChatWriteForbiddenError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/ChatWriteForbiddenError.php',
        'danog\\MadelineProto\\RPCError\\DcIdInvalidError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/DcIdInvalidError.php',
        'danog\\MadelineProto\\RPCError\\EncryptionAlreadyAcceptedError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/EncryptionAlreadyAcceptedError.php',
        'danog\\MadelineProto\\RPCError\\EncryptionAlreadyDeclinedError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/EncryptionAlreadyDeclinedError.php',
        'danog\\MadelineProto\\RPCError\\EncryptionDeclinedError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/EncryptionDeclinedError.php',
        'danog\\MadelineProto\\RPCError\\FileReferenceExpiredError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/FileReferenceExpiredError.php',
        'danog\\MadelineProto\\RPCError\\FileTokenInvalidError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/FileTokenInvalidError.php',
        'danog\\MadelineProto\\RPCError\\FloodPremiumWaitError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/FloodPremiumWaitError.php',
        'danog\\MadelineProto\\RPCError\\FloodWaitError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/FloodWaitError.php',
        'danog\\MadelineProto\\RPCError\\FromMessageBotDisabledError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/FromMessageBotDisabledError.php',
        'danog\\MadelineProto\\RPCError\\ImageProcessFailedError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/ImageProcessFailedError.php',
        'danog\\MadelineProto\\RPCError\\InputUserDeactivatedError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/InputUserDeactivatedError.php',
        'danog\\MadelineProto\\RPCError\\MsgIdInvalidError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/MsgIdInvalidError.php',
        'danog\\MadelineProto\\RPCError\\PasswordHashInvalidError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/PasswordHashInvalidError.php',
        'danog\\MadelineProto\\RPCError\\PaymentUnsupportedError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/PaymentUnsupportedError.php',
        'danog\\MadelineProto\\RPCError\\PeerIdInvalidError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/PeerIdInvalidError.php',
        'danog\\MadelineProto\\RPCError\\PinnedDialogsTooMuchError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/PinnedDialogsTooMuchError.php',
        'danog\\MadelineProto\\RPCError\\PollOptionDuplicateError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/PollOptionDuplicateError.php',
        'danog\\MadelineProto\\RPCError\\PremiumAccountRequiredError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/PremiumAccountRequiredError.php',
        'danog\\MadelineProto\\RPCError\\PrivacyPremiumRequiredError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/PrivacyPremiumRequiredError.php',
        'danog\\MadelineProto\\RPCError\\QuickRepliesBotNotAllowedError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/QuickRepliesBotNotAllowedError.php',
        'danog\\MadelineProto\\RPCError\\QuickRepliesTooMuchError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/QuickRepliesTooMuchError.php',
        'danog\\MadelineProto\\RPCError\\QuizCorrectAnswersTooMuchError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/QuizCorrectAnswersTooMuchError.php',
        'danog\\MadelineProto\\RPCError\\RateLimitError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/RateLimitError.php',
        'danog\\MadelineProto\\RPCError\\ReplyMessagesTooMuchError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/ReplyMessagesTooMuchError.php',
        'danog\\MadelineProto\\RPCError\\RequestTokenInvalidError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/RequestTokenInvalidError.php',
        'danog\\MadelineProto\\RPCError\\ScheduleBotNotAllowedError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/ScheduleBotNotAllowedError.php',
        'danog\\MadelineProto\\RPCError\\ScheduleDateTooLateError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/ScheduleDateTooLateError.php',
        'danog\\MadelineProto\\RPCError\\ScheduleStatusPrivateError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/ScheduleStatusPrivateError.php',
        'danog\\MadelineProto\\RPCError\\ScheduleTooMuchError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/ScheduleTooMuchError.php',
        'danog\\MadelineProto\\RPCError\\SessionPasswordNeededError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/SessionPasswordNeededError.php',
        'danog\\MadelineProto\\RPCError\\StoriesNeverCreatedError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/StoriesNeverCreatedError.php',
        'danog\\MadelineProto\\RPCError\\SubscriptionExportMissingError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/SubscriptionExportMissingError.php',
        'danog\\MadelineProto\\RPCError\\TimeoutError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/TimeoutError.php',
        'danog\\MadelineProto\\RPCError\\TodoItemDuplicateError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/TodoItemDuplicateError.php',
        'danog\\MadelineProto\\RPCError\\TodoItemsTooMuchError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/TodoItemsTooMuchError.php',
        'danog\\MadelineProto\\RPCError\\TopicClosedError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/TopicClosedError.php',
        'danog\\MadelineProto\\RPCError\\TopicDeletedError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/TopicDeletedError.php',
        'danog\\MadelineProto\\RPCError\\UserBannedInChannelError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/UserBannedInChannelError.php',
        'danog\\MadelineProto\\RPCError\\UserBotToBotDisabledError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/UserBotToBotDisabledError.php',
        'danog\\MadelineProto\\RPCError\\UserIsBlockedError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/UserIsBlockedError.php',
        'danog\\MadelineProto\\RPCError\\UserIsBotError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/UserIsBotError.php',
        'danog\\MadelineProto\\RPCError\\UsernameInvalidError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/UsernameInvalidError.php',
        'danog\\MadelineProto\\RPCError\\UsernameNotOccupiedError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/UsernameNotOccupiedError.php',
        'danog\\MadelineProto\\RPCError\\VoiceMessagesForbiddenError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/VoiceMessagesForbiddenError.php',
        'danog\\MadelineProto\\RPCError\\WebpageCurlFailedError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/WebpageCurlFailedError.php',
        'danog\\MadelineProto\\RPCError\\WebpageNotFoundError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/WebpageNotFoundError.php',
        'danog\\MadelineProto\\RPCError\\YouBlockedUserError' => __DIR__ . '/..' . '/danog/madelineproto/src/RPCError/YouBlockedUserError.php',
        'danog\\MadelineProto\\RSA' => __DIR__ . '/..' . '/danog/madelineproto/src/RSA.php',
        'danog\\MadelineProto\\Reactive\\Actor' => __DIR__ . '/..' . '/danog/madelineproto/src/Reactive/Actor.php',
        'danog\\MadelineProto\\Reactive\\AsyncWaiter' => __DIR__ . '/..' . '/danog/madelineproto/src/Reactive/AsyncWaiter.php',
        'danog\\MadelineProto\\Reactive\\BaseSubscriber' => __DIR__ . '/..' . '/danog/madelineproto/src/Reactive/BaseSubscriber.php',
        'danog\\MadelineProto\\Reactive\\EphemeralSubscriber' => __DIR__ . '/..' . '/danog/madelineproto/src/Reactive/EphemeralSubscriber.php',
        'danog\\MadelineProto\\Reactive\\Publisher' => __DIR__ . '/..' . '/danog/madelineproto/src/Reactive/Publisher.php',
        'danog\\MadelineProto\\Reactive\\SimpleSubscriber' => __DIR__ . '/..' . '/danog/madelineproto/src/Reactive/SimpleSubscriber.php',
        'danog\\MadelineProto\\Reactive\\SimpleSubscriberAdaptor' => __DIR__ . '/..' . '/danog/madelineproto/src/Reactive/SimpleSubscriberAdaptor.php',
        'danog\\MadelineProto\\Reactive\\Subscriber' => __DIR__ . '/..' . '/danog/madelineproto/src/Reactive/Subscriber.php',
        'danog\\MadelineProto\\RecordingEvent' => __DIR__ . '/..' . '/danog/madelineproto/src/RecordingEvent.php',
        'danog\\MadelineProto\\RecordingFormat' => __DIR__ . '/..' . '/danog/madelineproto/src/RecordingFormat.php',
        'danog\\MadelineProto\\ReferencedTimeoutCancellation' => __DIR__ . '/..' . '/danog/madelineproto/src/ReferencedTimeoutCancellation.php',
        'danog\\MadelineProto\\RemoteUrl' => __DIR__ . '/..' . '/danog/madelineproto/src/RemoteUrl.php',
        'danog\\MadelineProto\\ResponseException' => __DIR__ . '/..' . '/danog/madelineproto/src/ResponseException.php',
        'danog\\MadelineProto\\RestoresDeclaredProperties' => __DIR__ . '/..' . '/danog/madelineproto/src/RestoresDeclaredProperties.php',
        'danog\\MadelineProto\\ResumableDownloadStream' => __DIR__ . '/..' . '/danog/madelineproto/src/ResumableDownloadStream.php',
        'danog\\MadelineProto\\ResumableStream' => __DIR__ . '/..' . '/danog/madelineproto/src/ResumableStream.php',
        'danog\\MadelineProto\\SecretChats\\AuthKeyHandler' => __DIR__ . '/..' . '/danog/madelineproto/src/SecretChats/AuthKeyHandler.php',
        'danog\\MadelineProto\\SecretChats\\RekeyState' => __DIR__ . '/..' . '/danog/madelineproto/src/SecretChats/RekeyState.php',
        'danog\\MadelineProto\\SecretChats\\SecretChat' => __DIR__ . '/..' . '/danog/madelineproto/src/SecretChats/SecretChat.php',
        'danog\\MadelineProto\\SecretChats\\SecretChatController' => __DIR__ . '/..' . '/danog/madelineproto/src/SecretChats/SecretChatController.php',
        'danog\\MadelineProto\\SecretPeerNotInDbException' => __DIR__ . '/..' . '/danog/madelineproto/src/SecretPeerNotInDbException.php',
        'danog\\MadelineProto\\SecurityException' => __DIR__ . '/..' . '/danog/madelineproto/src/SecurityException.php',
        'danog\\MadelineProto\\Serialization' => __DIR__ . '/..' . '/danog/madelineproto/src/Serialization.php',
        'danog\\MadelineProto\\SessionPaths' => __DIR__ . '/..' . '/danog/madelineproto/src/SessionPaths.php',
        'danog\\MadelineProto\\Settings' => __DIR__ . '/..' . '/danog/madelineproto/src/Settings.php',
        'danog\\MadelineProto\\SettingsAbstract' => __DIR__ . '/..' . '/danog/madelineproto/src/SettingsAbstract.php',
        'danog\\MadelineProto\\SettingsEmpty' => __DIR__ . '/..' . '/danog/madelineproto/src/SettingsEmpty.php',
        'danog\\MadelineProto\\SettingsGetter' => __DIR__ . '/..' . '/danog/madelineproto/src/SettingsGetter.php',
        'danog\\MadelineProto\\Settings\\AppInfo' => __DIR__ . '/..' . '/danog/madelineproto/src/Settings/AppInfo.php',
        'danog\\MadelineProto\\Settings\\Auth' => __DIR__ . '/..' . '/danog/madelineproto/src/Settings/Auth.php',
        'danog\\MadelineProto\\Settings\\Connection' => __DIR__ . '/..' . '/danog/madelineproto/src/Settings/Connection.php',
        'danog\\MadelineProto\\Settings\\DatabaseAbstract' => __DIR__ . '/..' . '/danog/madelineproto/src/Settings/DatabaseAbstract.php',
        'danog\\MadelineProto\\Settings\\Database\\DriverDatabaseAbstract' => __DIR__ . '/..' . '/danog/madelineproto/src/Settings/Database/DriverDatabaseAbstract.php',
        'danog\\MadelineProto\\Settings\\Database\\Memory' => __DIR__ . '/..' . '/danog/madelineproto/src/Settings/Database/Memory.php',
        'danog\\MadelineProto\\Settings\\Database\\Mysql' => __DIR__ . '/..' . '/danog/madelineproto/src/Settings/Database/Mysql.php',
        'danog\\MadelineProto\\Settings\\Database\\Postgres' => __DIR__ . '/..' . '/danog/madelineproto/src/Settings/Database/Postgres.php',
        'danog\\MadelineProto\\Settings\\Database\\Redis' => __DIR__ . '/..' . '/danog/madelineproto/src/Settings/Database/Redis.php',
        'danog\\MadelineProto\\Settings\\Database\\SerializerType' => __DIR__ . '/..' . '/danog/madelineproto/src/Settings/Database/SerializerType.php',
        'danog\\MadelineProto\\Settings\\Database\\SqlAbstract' => __DIR__ . '/..' . '/danog/madelineproto/src/Settings/Database/SqlAbstract.php',
        'danog\\MadelineProto\\Settings\\Files' => __DIR__ . '/..' . '/danog/madelineproto/src/Settings/Files.php',
        'danog\\MadelineProto\\Settings\\Ipc' => __DIR__ . '/..' . '/danog/madelineproto/src/Settings/Ipc.php',
        'danog\\MadelineProto\\Settings\\Logger' => __DIR__ . '/..' . '/danog/madelineproto/src/Settings/Logger.php',
        'danog\\MadelineProto\\Settings\\Metrics' => __DIR__ . '/..' . '/danog/madelineproto/src/Settings/Metrics.php',
        'danog\\MadelineProto\\Settings\\Peer' => __DIR__ . '/..' . '/danog/madelineproto/src/Settings/Peer.php',
        'danog\\MadelineProto\\Settings\\Pwr' => __DIR__ . '/..' . '/danog/madelineproto/src/Settings/Pwr.php',
        'danog\\MadelineProto\\Settings\\RPC' => __DIR__ . '/..' . '/danog/madelineproto/src/Settings/RPC.php',
        'danog\\MadelineProto\\Settings\\SecretChats' => __DIR__ . '/..' . '/danog/madelineproto/src/Settings/SecretChats.php',
        'danog\\MadelineProto\\Settings\\Serialization' => __DIR__ . '/..' . '/danog/madelineproto/src/Settings/Serialization.php',
        'danog\\MadelineProto\\Settings\\TLSchema' => __DIR__ . '/..' . '/danog/madelineproto/src/Settings/TLSchema.php',
        'danog\\MadelineProto\\Settings\\Templates' => __DIR__ . '/..' . '/danog/madelineproto/src/Settings/Templates.php',
        'danog\\MadelineProto\\Settings\\VoIP' => __DIR__ . '/..' . '/danog/madelineproto/src/Settings/VoIP.php',
        'danog\\MadelineProto\\Shutdown' => __DIR__ . '/..' . '/danog/madelineproto/src/Shutdown.php',
        'danog\\MadelineProto\\SimpleEventHandler' => __DIR__ . '/..' . '/danog/madelineproto/src/SimpleEventHandler.php',
        'danog\\MadelineProto\\Snitch' => __DIR__ . '/..' . '/danog/madelineproto/src/Snitch.php',
        'danog\\MadelineProto\\StrTools' => __DIR__ . '/..' . '/danog/madelineproto/src/StrTools.php',
        'danog\\MadelineProto\\StreamDuplicator' => __DIR__ . '/..' . '/danog/madelineproto/src/StreamDuplicator.php',
        'danog\\MadelineProto\\StreamEof' => __DIR__ . '/..' . '/danog/madelineproto/src/StreamEof.php',
        'danog\\MadelineProto\\Stream\\ADNLTransport\\ADNLStream' => __DIR__ . '/..' . '/danog/madelineproto/src/Stream/ADNLTransport/ADNLStream.php',
        'danog\\MadelineProto\\Stream\\BufferInterface' => __DIR__ . '/..' . '/danog/madelineproto/src/Stream/BufferInterface.php',
        'danog\\MadelineProto\\Stream\\BufferedProxyStreamInterface' => __DIR__ . '/..' . '/danog/madelineproto/src/Stream/BufferedProxyStreamInterface.php',
        'danog\\MadelineProto\\Stream\\BufferedStreamInterface' => __DIR__ . '/..' . '/danog/madelineproto/src/Stream/BufferedStreamInterface.php',
        'danog\\MadelineProto\\Stream\\Common\\BufferedRawStream' => __DIR__ . '/..' . '/danog/madelineproto/src/Stream/Common/BufferedRawStream.php',
        'danog\\MadelineProto\\Stream\\Common\\CtrStream' => __DIR__ . '/..' . '/danog/madelineproto/src/Stream/Common/CtrStream.php',
        'danog\\MadelineProto\\Stream\\Common\\FileBufferedStream' => __DIR__ . '/..' . '/danog/madelineproto/src/Stream/Common/FileBufferedStream.php',
        'danog\\MadelineProto\\Stream\\Common\\HashedBufferedStream' => __DIR__ . '/..' . '/danog/madelineproto/src/Stream/Common/HashedBufferedStream.php',
        'danog\\MadelineProto\\Stream\\Common\\SimpleBufferedRawStream' => __DIR__ . '/..' . '/danog/madelineproto/src/Stream/Common/SimpleBufferedRawStream.php',
        'danog\\MadelineProto\\Stream\\Common\\UdpBufferedStream' => __DIR__ . '/..' . '/danog/madelineproto/src/Stream/Common/UdpBufferedStream.php',
        'danog\\MadelineProto\\Stream\\ConnectionContext' => __DIR__ . '/..' . '/danog/madelineproto/src/Stream/ConnectionContext.php',
        'danog\\MadelineProto\\Stream\\ContextIterator' => __DIR__ . '/..' . '/danog/madelineproto/src/Stream/ContextIterator.php',
        'danog\\MadelineProto\\Stream\\MTProtoBufferInterface' => __DIR__ . '/..' . '/danog/madelineproto/src/Stream/MTProtoBufferInterface.php',
        'danog\\MadelineProto\\Stream\\MTProtoTransport\\AbridgedStream' => __DIR__ . '/..' . '/danog/madelineproto/src/Stream/MTProtoTransport/AbridgedStream.php',
        'danog\\MadelineProto\\Stream\\MTProtoTransport\\FakeTlsStream' => __DIR__ . '/..' . '/danog/madelineproto/src/Stream/MTProtoTransport/FakeTlsStream.php',
        'danog\\MadelineProto\\Stream\\MTProtoTransport\\FullStream' => __DIR__ . '/..' . '/danog/madelineproto/src/Stream/MTProtoTransport/FullStream.php',
        'danog\\MadelineProto\\Stream\\MTProtoTransport\\HttpStream' => __DIR__ . '/..' . '/danog/madelineproto/src/Stream/MTProtoTransport/HttpStream.php',
        'danog\\MadelineProto\\Stream\\MTProtoTransport\\HttpsStream' => __DIR__ . '/..' . '/danog/madelineproto/src/Stream/MTProtoTransport/HttpsStream.php',
        'danog\\MadelineProto\\Stream\\MTProtoTransport\\IntermediatePaddedStream' => __DIR__ . '/..' . '/danog/madelineproto/src/Stream/MTProtoTransport/IntermediatePaddedStream.php',
        'danog\\MadelineProto\\Stream\\MTProtoTransport\\IntermediateStream' => __DIR__ . '/..' . '/danog/madelineproto/src/Stream/MTProtoTransport/IntermediateStream.php',
        'danog\\MadelineProto\\Stream\\MTProtoTransport\\ObfuscatedStream' => __DIR__ . '/..' . '/danog/madelineproto/src/Stream/MTProtoTransport/ObfuscatedStream.php',
        'danog\\MadelineProto\\Stream\\ProxyStreamInterface' => __DIR__ . '/..' . '/danog/madelineproto/src/Stream/ProxyStreamInterface.php',
        'danog\\MadelineProto\\Stream\\Proxy\\HttpProxy' => __DIR__ . '/..' . '/danog/madelineproto/src/Stream/Proxy/HttpProxy.php',
        'danog\\MadelineProto\\Stream\\Proxy\\SocksProxy' => __DIR__ . '/..' . '/danog/madelineproto/src/Stream/Proxy/SocksProxy.php',
        'danog\\MadelineProto\\Stream\\RawProxyStreamInterface' => __DIR__ . '/..' . '/danog/madelineproto/src/Stream/RawProxyStreamInterface.php',
        'danog\\MadelineProto\\Stream\\RawStreamInterface' => __DIR__ . '/..' . '/danog/madelineproto/src/Stream/RawStreamInterface.php',
        'danog\\MadelineProto\\Stream\\ReadBufferInterface' => __DIR__ . '/..' . '/danog/madelineproto/src/Stream/ReadBufferInterface.php',
        'danog\\MadelineProto\\Stream\\StreamInterface' => __DIR__ . '/..' . '/danog/madelineproto/src/Stream/StreamInterface.php',
        'danog\\MadelineProto\\Stream\\Transport\\DefaultStream' => __DIR__ . '/..' . '/danog/madelineproto/src/Stream/Transport/DefaultStream.php',
        'danog\\MadelineProto\\Stream\\Transport\\PremadeStream' => __DIR__ . '/..' . '/danog/madelineproto/src/Stream/Transport/PremadeStream.php',
        'danog\\MadelineProto\\Stream\\Transport\\WsStream' => __DIR__ . '/..' . '/danog/madelineproto/src/Stream/Transport/WsStream.php',
        'danog\\MadelineProto\\Stream\\Transport\\WssStream' => __DIR__ . '/..' . '/danog/madelineproto/src/Stream/Transport/WssStream.php',
        'danog\\MadelineProto\\Stream\\WriteBufferInterface' => __DIR__ . '/..' . '/danog/madelineproto/src/Stream/WriteBufferInterface.php',
        'danog\\MadelineProto\\TL\\Conversion\\BotAPI' => __DIR__ . '/..' . '/danog/madelineproto/src/TL/Conversion/BotAPI.php',
        'danog\\MadelineProto\\TL\\Conversion\\BotAPIFiles' => __DIR__ . '/..' . '/danog/madelineproto/src/TL/Conversion/BotAPIFiles.php',
        'danog\\MadelineProto\\TL\\Conversion\\Exception' => __DIR__ . '/..' . '/danog/madelineproto/src/TL/Conversion/Exception.php',
        'danog\\MadelineProto\\TL\\Conversion\\Extension' => __DIR__ . '/..' . '/danog/madelineproto/src/TL/Conversion/Extension.php',
        'danog\\MadelineProto\\TL\\Conversion\\TD' => __DIR__ . '/..' . '/danog/madelineproto/src/TL/Conversion/TD.php',
        'danog\\MadelineProto\\TL\\Exception' => __DIR__ . '/..' . '/danog/madelineproto/src/TL/Exception.php',
        'danog\\MadelineProto\\TL\\PrettyException' => __DIR__ . '/..' . '/danog/madelineproto/src/TL/PrettyException.php',
        'danog\\MadelineProto\\TL\\SecretTLParser' => __DIR__ . '/..' . '/danog/madelineproto/src/TL/SecretTLParser.php',
        'danog\\MadelineProto\\TL\\TL' => __DIR__ . '/..' . '/danog/madelineproto/src/TL/TL.php',
        'danog\\MadelineProto\\TL\\TLCallback' => __DIR__ . '/..' . '/danog/madelineproto/src/TL/TLCallback.php',
        'danog\\MadelineProto\\TL\\TLConstructors' => __DIR__ . '/..' . '/danog/madelineproto/src/TL/TLConstructors.php',
        'danog\\MadelineProto\\TL\\TLInterface' => __DIR__ . '/..' . '/danog/madelineproto/src/TL/TLInterface.php',
        'danog\\MadelineProto\\TL\\TLMethods' => __DIR__ . '/..' . '/danog/madelineproto/src/TL/TLMethods.php',
        'danog\\MadelineProto\\TL\\TLParams' => __DIR__ . '/..' . '/danog/madelineproto/src/TL/TLParams.php',
        'danog\\MadelineProto\\TL\\TLParser' => __DIR__ . '/..' . '/danog/madelineproto/src/TL/TLParser.php',
        'danog\\MadelineProto\\TL\\Types\\Button' => __DIR__ . '/..' . '/danog/madelineproto/src/TL/Types/Button.php',
        'danog\\MadelineProto\\TL\\Types\\Bytes' => __DIR__ . '/..' . '/danog/madelineproto/src/TL/Types/Bytes.php',
        'danog\\MadelineProto\\TL\\Types\\LoginQrCode' => __DIR__ . '/..' . '/danog/madelineproto/src/TL/Types/LoginQrCode.php',
        'danog\\MadelineProto\\TextEntities' => __DIR__ . '/..' . '/danog/madelineproto/src/TextEntities.php',
        'danog\\MadelineProto\\Tgcalls\\Av1Bitstream' => __DIR__ . '/..' . '/danog/madelineproto/src/Tgcalls/Av1Bitstream.php',
        'danog\\MadelineProto\\Tgcalls\\BitReader' => __DIR__ . '/..' . '/danog/madelineproto/src/Tgcalls/BitReader.php',
        'danog\\MadelineProto\\Tgcalls\\CallControllerInterface' => __DIR__ . '/..' . '/danog/madelineproto/src/Tgcalls/CallControllerInterface.php',
        'danog\\MadelineProto\\Tgcalls\\CallRecorder' => __DIR__ . '/..' . '/danog/madelineproto/src/Tgcalls/CallRecorder.php',
        'danog\\MadelineProto\\Tgcalls\\CallStreamsDispatcher' => __DIR__ . '/..' . '/danog/madelineproto/src/Tgcalls/CallStreamsDispatcher.php',
        'danog\\MadelineProto\\Tgcalls\\Controller' => __DIR__ . '/..' . '/danog/madelineproto/src/Tgcalls/Controller.php',
        'danog\\MadelineProto\\Tgcalls\\E2E\\BlockCodec' => __DIR__ . '/..' . '/danog/madelineproto/src/Tgcalls/E2E/BlockCodec.php',
        'danog\\MadelineProto\\Tgcalls\\E2E\\CallPacket' => __DIR__ . '/..' . '/danog/madelineproto/src/Tgcalls/E2E/CallPacket.php',
        'danog\\MadelineProto\\Tgcalls\\E2E\\ConferenceCall' => __DIR__ . '/..' . '/danog/madelineproto/src/Tgcalls/E2E/ConferenceCall.php',
        'danog\\MadelineProto\\Tgcalls\\E2E\\ConferenceChain' => __DIR__ . '/..' . '/danog/madelineproto/src/Tgcalls/E2E/ConferenceChain.php',
        'danog\\MadelineProto\\Tgcalls\\E2E\\Crypto' => __DIR__ . '/..' . '/danog/madelineproto/src/Tgcalls/E2E/Crypto.php',
        'danog\\MadelineProto\\Tgcalls\\E2E\\E2EKeyProvider' => __DIR__ . '/..' . '/danog/madelineproto/src/Tgcalls/E2E/E2EKeyProvider.php',
        'danog\\MadelineProto\\Tgcalls\\E2E\\FrameCryptor' => __DIR__ . '/..' . '/danog/madelineproto/src/Tgcalls/E2E/FrameCryptor.php',
        'danog\\MadelineProto\\Tgcalls\\E2E\\FrameCryptorState' => __DIR__ . '/..' . '/danog/madelineproto/src/Tgcalls/E2E/FrameCryptorState.php',
        'danog\\MadelineProto\\Tgcalls\\E2E\\Verification' => __DIR__ . '/..' . '/danog/madelineproto/src/Tgcalls/E2E/Verification.php',
        'danog\\MadelineProto\\Tgcalls\\EncryptedConnection' => __DIR__ . '/..' . '/danog/madelineproto/src/Tgcalls/EncryptedConnection.php',
        'danog\\MadelineProto\\Tgcalls\\GroupConnection' => __DIR__ . '/..' . '/danog/madelineproto/src/Tgcalls/GroupConnection.php',
        'danog\\MadelineProto\\Tgcalls\\GroupConnectionOwner' => __DIR__ . '/..' . '/danog/madelineproto/src/Tgcalls/GroupConnectionOwner.php',
        'danog\\MadelineProto\\Tgcalls\\GroupMediaTrait' => __DIR__ . '/..' . '/danog/madelineproto/src/Tgcalls/GroupMediaTrait.php',
        'danog\\MadelineProto\\Tgcalls\\GroupSdp' => __DIR__ . '/..' . '/danog/madelineproto/src/Tgcalls/GroupSdp.php',
        'danog\\MadelineProto\\Tgcalls\\H264Framing' => __DIR__ . '/..' . '/danog/madelineproto/src/Tgcalls/H264Framing.php',
        'danog\\MadelineProto\\Tgcalls\\HevcBitstream' => __DIR__ . '/..' . '/danog/madelineproto/src/Tgcalls/HevcBitstream.php',
        'danog\\MadelineProto\\Tgcalls\\IncomingMedia' => __DIR__ . '/..' . '/danog/madelineproto/src/Tgcalls/IncomingMedia.php',
        'danog\\MadelineProto\\Tgcalls\\IncomingMediaObserver' => __DIR__ . '/..' . '/danog/madelineproto/src/Tgcalls/IncomingMediaObserver.php',
        'danog\\MadelineProto\\Tgcalls\\LegacyController' => __DIR__ . '/..' . '/danog/madelineproto/src/Tgcalls/LegacyController.php',
        'danog\\MadelineProto\\Tgcalls\\OpusPlaybackTrack' => __DIR__ . '/..' . '/danog/madelineproto/src/Tgcalls/OpusPlaybackTrack.php',
        'danog\\MadelineProto\\Tgcalls\\OpusRecorder' => __DIR__ . '/..' . '/danog/madelineproto/src/Tgcalls/OpusRecorder.php',
        'danog\\MadelineProto\\Tgcalls\\PrivateCallController' => __DIR__ . '/..' . '/danog/madelineproto/src/Tgcalls/PrivateCallController.php',
        'danog\\MadelineProto\\Tgcalls\\RecordingObserver' => __DIR__ . '/..' . '/danog/madelineproto/src/Tgcalls/RecordingObserver.php',
        'danog\\MadelineProto\\Tgcalls\\RtcDebugLogger' => __DIR__ . '/..' . '/danog/madelineproto/src/Tgcalls/RtcDebugLogger.php',
        'danog\\MadelineProto\\Tgcalls\\ScreencastCodecObserver' => __DIR__ . '/..' . '/danog/madelineproto/src/Tgcalls/ScreencastCodecObserver.php',
        'danog\\MadelineProto\\Tgcalls\\SctpSignalingObserver' => __DIR__ . '/..' . '/danog/madelineproto/src/Tgcalls/SctpSignalingObserver.php',
        'danog\\MadelineProto\\Tgcalls\\SignalingIceRole' => __DIR__ . '/..' . '/danog/madelineproto/src/Tgcalls/SignalingIceRole.php',
        'danog\\MadelineProto\\Tgcalls\\SignalingSctpTransport' => __DIR__ . '/..' . '/danog/madelineproto/src/Tgcalls/SignalingSctpTransport.php',
        'danog\\MadelineProto\\Tgcalls\\SignalingServiceObserver' => __DIR__ . '/..' . '/danog/madelineproto/src/Tgcalls/SignalingServiceObserver.php',
        'danog\\MadelineProto\\Tgcalls\\TgcallsTools' => __DIR__ . '/..' . '/danog/madelineproto/src/Tgcalls/TgcallsTools.php',
        'danog\\MadelineProto\\Tgcalls\\V2Sdp' => __DIR__ . '/..' . '/danog/madelineproto/src/Tgcalls/V2Sdp.php',
        'danog\\MadelineProto\\Tgcalls\\VideoCodecObserver' => __DIR__ . '/..' . '/danog/madelineproto/src/Tgcalls/VideoCodecObserver.php',
        'danog\\MadelineProto\\Tgcalls\\VideoPlaybackTrack' => __DIR__ . '/..' . '/danog/madelineproto/src/Tgcalls/VideoPlaybackTrack.php',
        'danog\\MadelineProto\\Tools' => __DIR__ . '/..' . '/danog/madelineproto/src/Tools.php',
        'danog\\MadelineProto\\TransportError' => __DIR__ . '/..' . '/danog/madelineproto/src/TransportError.php',
        'danog\\MadelineProto\\UpdateHandlerType' => __DIR__ . '/..' . '/danog/madelineproto/src/UpdateHandlerType.php',
        'danog\\MadelineProto\\UploadResumeException' => __DIR__ . '/..' . '/danog/madelineproto/src/UploadResumeException.php',
        'danog\\MadelineProto\\VoIPServerConfig' => __DIR__ . '/..' . '/danog/madelineproto/src/VoIPServerConfig.php',
        'danog\\MadelineProto\\VoIP\\AuthKeyHandler' => __DIR__ . '/..' . '/danog/madelineproto/src/VoIP/AuthKeyHandler.php',
        'danog\\MadelineProto\\VoIP\\CallState' => __DIR__ . '/..' . '/danog/madelineproto/src/VoIP/CallState.php',
        'danog\\MadelineProto\\VoIP\\DiscardReason' => __DIR__ . '/..' . '/danog/madelineproto/src/VoIP/DiscardReason.php',
        'danog\\MadelineProto\\VoIP\\Endpoint' => __DIR__ . '/..' . '/danog/madelineproto/src/VoIP/Endpoint.php',
        'danog\\MadelineProto\\VoIP\\MediaState' => __DIR__ . '/..' . '/danog/madelineproto/src/VoIP/MediaState.php',
        'danog\\MadelineProto\\VoIP\\MessageHandler' => __DIR__ . '/..' . '/danog/madelineproto/src/VoIP/MessageHandler.php',
        'danog\\MadelineProto\\VoIP\\SignalingProtocolVersion' => __DIR__ . '/..' . '/danog/madelineproto/src/VoIP/SignalingProtocolVersion.php',
        'danog\\MadelineProto\\VoIP\\VoIPState' => __DIR__ . '/..' . '/danog/madelineproto/src/VoIP/VoIPState.php',
        'danog\\MadelineProto\\WebUI\\Action' => __DIR__ . '/..' . '/danog/madelineproto/src/WebUI/Action.php',
        'danog\\MadelineProto\\WebUI\\Alert' => __DIR__ . '/..' . '/danog/madelineproto/src/WebUI/Alert.php',
        'danog\\MadelineProto\\WebUI\\AlertLevel' => __DIR__ . '/..' . '/danog/madelineproto/src/WebUI/AlertLevel.php',
        'danog\\MadelineProto\\WebUI\\Field' => __DIR__ . '/..' . '/danog/madelineproto/src/WebUI/Field.php',
        'danog\\MadelineProto\\WebUI\\FieldType' => __DIR__ . '/..' . '/danog/madelineproto/src/WebUI/FieldType.php',
        'danog\\MadelineProto\\WebUI\\Page' => __DIR__ . '/..' . '/danog/madelineproto/src/WebUI/Page.php',
        'danog\\MadelineProto\\WebUI\\SafeHtml' => __DIR__ . '/..' . '/danog/madelineproto/src/WebUI/SafeHtml.php',
        'danog\\MadelineProto\\WebUI\\Step' => __DIR__ . '/..' . '/danog/madelineproto/src/WebUI/Step.php',
        'danog\\MadelineProto\\WebUI\\Template' => __DIR__ . '/..' . '/danog/madelineproto/src/WebUI/Template.php',
        'danog\\MadelineProto\\WebUI\\TemplateEngine' => __DIR__ . '/..' . '/danog/madelineproto/src/WebUI/TemplateEngine.php',
        'danog\\MadelineProto\\WebUI\\WebUI' => __DIR__ . '/..' . '/danog/madelineproto/src/WebUI/WebUI.php',
        'danog\\MadelineProto\\WrappedFuture' => __DIR__ . '/..' . '/danog/madelineproto/src/WrappedFuture.php',
        'danog\\MadelineProto\\Wrappers\\Ads' => __DIR__ . '/..' . '/danog/madelineproto/src/Wrappers/Ads.php',
        'danog\\MadelineProto\\Wrappers\\Button' => __DIR__ . '/..' . '/danog/madelineproto/src/Wrappers/Button.php',
        'danog\\MadelineProto\\Wrappers\\DialogHandler' => __DIR__ . '/..' . '/danog/madelineproto/src/Wrappers/DialogHandler.php',
        'danog\\MadelineProto\\Wrappers\\Events' => __DIR__ . '/..' . '/danog/madelineproto/src/Wrappers/Events.php',
        'danog\\MadelineProto\\Wrappers\\Login' => __DIR__ . '/..' . '/danog/madelineproto/src/Wrappers/Login.php',
        'danog\\MadelineProto\\Wrappers\\Loop' => __DIR__ . '/..' . '/danog/madelineproto/src/Wrappers/Loop.php',
        'danog\\MadelineProto\\Wrappers\\Start' => __DIR__ . '/..' . '/danog/madelineproto/src/Wrappers/Start.php',
        'danog\\PrimeModule' => __DIR__ . '/..' . '/danog/primemodule/lib/danog/PrimeModule.php',
        'danog\\TelegramEntities\\Entities' => __DIR__ . '/..' . '/danog/telegram-entities/src/Entities.php',
        'danog\\TelegramEntities\\EntityTools' => __DIR__ . '/..' . '/danog/telegram-entities/src/EntityTools.php',
        'phpseclib4\\Common\\ConstantUtilityTrait' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Common/ConstantUtilityTrait.php',
        'phpseclib4\\Common\\Functions\\Arrays' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Common/Functions/Arrays.php',
        'phpseclib4\\Common\\Functions\\Files' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Common/Functions/Files.php',
        'phpseclib4\\Common\\Functions\\Strings' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Common/Functions/Strings.php',
        'phpseclib4\\Crypt\\AES' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/AES.php',
        'phpseclib4\\Crypt\\Blowfish' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Blowfish.php',
        'phpseclib4\\Crypt\\ChaCha20' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/ChaCha20.php',
        'phpseclib4\\Crypt\\Common\\AsymmetricKey' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Common/AsymmetricKey.php',
        'phpseclib4\\Crypt\\Common\\BlockCipher' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Common/BlockCipher.php',
        'phpseclib4\\Crypt\\Common\\Formats\\Keys\\JWK' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/JWK.php',
        'phpseclib4\\Crypt\\Common\\Formats\\Keys\\OpenSSH' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/OpenSSH.php',
        'phpseclib4\\Crypt\\Common\\Formats\\Keys\\PKCS' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PKCS.php',
        'phpseclib4\\Crypt\\Common\\Formats\\Keys\\PKCS1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PKCS1.php',
        'phpseclib4\\Crypt\\Common\\Formats\\Keys\\PKCS8' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PKCS8.php',
        'phpseclib4\\Crypt\\Common\\Formats\\Keys\\PuTTY' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PuTTY.php',
        'phpseclib4\\Crypt\\Common\\Formats\\Signature\\Raw' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Signature/Raw.php',
        'phpseclib4\\Crypt\\Common\\PrivateKey' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Common/PrivateKey.php',
        'phpseclib4\\Crypt\\Common\\PublicKey' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Common/PublicKey.php',
        'phpseclib4\\Crypt\\Common\\StreamCipher' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Common/StreamCipher.php',
        'phpseclib4\\Crypt\\Common\\SymmetricKey' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Common/SymmetricKey.php',
        'phpseclib4\\Crypt\\Common\\Traits\\ASN1AlgorithmIdentifier' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Common/Traits/ASN1AlgorithmIdentifier.php',
        'phpseclib4\\Crypt\\Common\\Traits\\Fingerprint' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Common/Traits/Fingerprint.php',
        'phpseclib4\\Crypt\\Common\\Traits\\PKCS12Helper' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Common/Traits/PKCS12Helper.php',
        'phpseclib4\\Crypt\\Common\\Traits\\PasswordProtected' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Common/Traits/PasswordProtected.php',
        'phpseclib4\\Crypt\\DES' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/DES.php',
        'phpseclib4\\Crypt\\DH' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/DH.php',
        'phpseclib4\\Crypt\\DH\\Formats\\Keys\\PKCS1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/DH/Formats/Keys/PKCS1.php',
        'phpseclib4\\Crypt\\DH\\Formats\\Keys\\PKCS8' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/DH/Formats/Keys/PKCS8.php',
        'phpseclib4\\Crypt\\DH\\Parameters' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/DH/Parameters.php',
        'phpseclib4\\Crypt\\DH\\PrivateKey' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/DH/PrivateKey.php',
        'phpseclib4\\Crypt\\DH\\PublicKey' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/DH/PublicKey.php',
        'phpseclib4\\Crypt\\DSA' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/DSA.php',
        'phpseclib4\\Crypt\\DSA\\Formats\\Keys\\OpenSSH' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/OpenSSH.php',
        'phpseclib4\\Crypt\\DSA\\Formats\\Keys\\PKCS1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/PKCS1.php',
        'phpseclib4\\Crypt\\DSA\\Formats\\Keys\\PKCS8' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/PKCS8.php',
        'phpseclib4\\Crypt\\DSA\\Formats\\Keys\\PuTTY' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/PuTTY.php',
        'phpseclib4\\Crypt\\DSA\\Formats\\Keys\\Raw' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/Raw.php',
        'phpseclib4\\Crypt\\DSA\\Formats\\Keys\\XML' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/XML.php',
        'phpseclib4\\Crypt\\DSA\\Formats\\Signature\\ASN1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Signature/ASN1.php',
        'phpseclib4\\Crypt\\DSA\\Formats\\Signature\\Raw' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Signature/Raw.php',
        'phpseclib4\\Crypt\\DSA\\Formats\\Signature\\SSH2' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Signature/SSH2.php',
        'phpseclib4\\Crypt\\DSA\\Parameters' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/DSA/Parameters.php',
        'phpseclib4\\Crypt\\DSA\\PrivateKey' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/DSA/PrivateKey.php',
        'phpseclib4\\Crypt\\DSA\\PublicKey' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/DSA/PublicKey.php',
        'phpseclib4\\Crypt\\EC' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC.php',
        'phpseclib4\\Crypt\\EC\\BaseCurves\\Base' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Base.php',
        'phpseclib4\\Crypt\\EC\\BaseCurves\\Binary' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Binary.php',
        'phpseclib4\\Crypt\\EC\\BaseCurves\\KoblitzPrime' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/KoblitzPrime.php',
        'phpseclib4\\Crypt\\EC\\BaseCurves\\Montgomery' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Montgomery.php',
        'phpseclib4\\Crypt\\EC\\BaseCurves\\Prime' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Prime.php',
        'phpseclib4\\Crypt\\EC\\BaseCurves\\TwistedEdwards' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/TwistedEdwards.php',
        'phpseclib4\\Crypt\\EC\\Curves\\Curve25519' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Curve25519.php',
        'phpseclib4\\Crypt\\EC\\Curves\\Curve448' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Curve448.php',
        'phpseclib4\\Crypt\\EC\\Curves\\Ed25519' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Ed25519.php',
        'phpseclib4\\Crypt\\EC\\Curves\\Ed448' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Ed448.php',
        'phpseclib4\\Crypt\\EC\\Curves\\brainpoolP160r1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP160r1.php',
        'phpseclib4\\Crypt\\EC\\Curves\\brainpoolP160t1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP160t1.php',
        'phpseclib4\\Crypt\\EC\\Curves\\brainpoolP192r1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP192r1.php',
        'phpseclib4\\Crypt\\EC\\Curves\\brainpoolP192t1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP192t1.php',
        'phpseclib4\\Crypt\\EC\\Curves\\brainpoolP224r1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP224r1.php',
        'phpseclib4\\Crypt\\EC\\Curves\\brainpoolP224t1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP224t1.php',
        'phpseclib4\\Crypt\\EC\\Curves\\brainpoolP256r1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP256r1.php',
        'phpseclib4\\Crypt\\EC\\Curves\\brainpoolP256t1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP256t1.php',
        'phpseclib4\\Crypt\\EC\\Curves\\brainpoolP320r1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP320r1.php',
        'phpseclib4\\Crypt\\EC\\Curves\\brainpoolP320t1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP320t1.php',
        'phpseclib4\\Crypt\\EC\\Curves\\brainpoolP384r1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP384r1.php',
        'phpseclib4\\Crypt\\EC\\Curves\\brainpoolP384t1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP384t1.php',
        'phpseclib4\\Crypt\\EC\\Curves\\brainpoolP512r1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP512r1.php',
        'phpseclib4\\Crypt\\EC\\Curves\\brainpoolP512t1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP512t1.php',
        'phpseclib4\\Crypt\\EC\\Curves\\nistb233' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistb233.php',
        'phpseclib4\\Crypt\\EC\\Curves\\nistb409' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistb409.php',
        'phpseclib4\\Crypt\\EC\\Curves\\nistk163' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk163.php',
        'phpseclib4\\Crypt\\EC\\Curves\\nistk233' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk233.php',
        'phpseclib4\\Crypt\\EC\\Curves\\nistk283' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk283.php',
        'phpseclib4\\Crypt\\EC\\Curves\\nistk409' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk409.php',
        'phpseclib4\\Crypt\\EC\\Curves\\nistp192' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp192.php',
        'phpseclib4\\Crypt\\EC\\Curves\\nistp224' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp224.php',
        'phpseclib4\\Crypt\\EC\\Curves\\nistp256' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp256.php',
        'phpseclib4\\Crypt\\EC\\Curves\\nistp384' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp384.php',
        'phpseclib4\\Crypt\\EC\\Curves\\nistp521' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp521.php',
        'phpseclib4\\Crypt\\EC\\Curves\\nistt571' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistt571.php',
        'phpseclib4\\Crypt\\EC\\Curves\\prime192v1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime192v1.php',
        'phpseclib4\\Crypt\\EC\\Curves\\prime192v2' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime192v2.php',
        'phpseclib4\\Crypt\\EC\\Curves\\prime192v3' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime192v3.php',
        'phpseclib4\\Crypt\\EC\\Curves\\prime239v1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime239v1.php',
        'phpseclib4\\Crypt\\EC\\Curves\\prime239v2' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime239v2.php',
        'phpseclib4\\Crypt\\EC\\Curves\\prime239v3' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime239v3.php',
        'phpseclib4\\Crypt\\EC\\Curves\\prime256v1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime256v1.php',
        'phpseclib4\\Crypt\\EC\\Curves\\secp112r1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp112r1.php',
        'phpseclib4\\Crypt\\EC\\Curves\\secp112r2' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp112r2.php',
        'phpseclib4\\Crypt\\EC\\Curves\\secp128r1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp128r1.php',
        'phpseclib4\\Crypt\\EC\\Curves\\secp128r2' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp128r2.php',
        'phpseclib4\\Crypt\\EC\\Curves\\secp160k1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp160k1.php',
        'phpseclib4\\Crypt\\EC\\Curves\\secp160r1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp160r1.php',
        'phpseclib4\\Crypt\\EC\\Curves\\secp160r2' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp160r2.php',
        'phpseclib4\\Crypt\\EC\\Curves\\secp192k1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp192k1.php',
        'phpseclib4\\Crypt\\EC\\Curves\\secp192r1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp192r1.php',
        'phpseclib4\\Crypt\\EC\\Curves\\secp224k1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp224k1.php',
        'phpseclib4\\Crypt\\EC\\Curves\\secp224r1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp224r1.php',
        'phpseclib4\\Crypt\\EC\\Curves\\secp256k1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp256k1.php',
        'phpseclib4\\Crypt\\EC\\Curves\\secp256r1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp256r1.php',
        'phpseclib4\\Crypt\\EC\\Curves\\secp384r1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp384r1.php',
        'phpseclib4\\Crypt\\EC\\Curves\\secp521r1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp521r1.php',
        'phpseclib4\\Crypt\\EC\\Curves\\sect113r1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect113r1.php',
        'phpseclib4\\Crypt\\EC\\Curves\\sect113r2' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect113r2.php',
        'phpseclib4\\Crypt\\EC\\Curves\\sect131r1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect131r1.php',
        'phpseclib4\\Crypt\\EC\\Curves\\sect131r2' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect131r2.php',
        'phpseclib4\\Crypt\\EC\\Curves\\sect163k1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect163k1.php',
        'phpseclib4\\Crypt\\EC\\Curves\\sect163r1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect163r1.php',
        'phpseclib4\\Crypt\\EC\\Curves\\sect163r2' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect163r2.php',
        'phpseclib4\\Crypt\\EC\\Curves\\sect193r1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect193r1.php',
        'phpseclib4\\Crypt\\EC\\Curves\\sect193r2' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect193r2.php',
        'phpseclib4\\Crypt\\EC\\Curves\\sect233k1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect233k1.php',
        'phpseclib4\\Crypt\\EC\\Curves\\sect233r1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect233r1.php',
        'phpseclib4\\Crypt\\EC\\Curves\\sect239k1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect239k1.php',
        'phpseclib4\\Crypt\\EC\\Curves\\sect283k1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect283k1.php',
        'phpseclib4\\Crypt\\EC\\Curves\\sect283r1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect283r1.php',
        'phpseclib4\\Crypt\\EC\\Curves\\sect409k1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect409k1.php',
        'phpseclib4\\Crypt\\EC\\Curves\\sect409r1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect409r1.php',
        'phpseclib4\\Crypt\\EC\\Curves\\sect571k1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect571k1.php',
        'phpseclib4\\Crypt\\EC\\Curves\\sect571r1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect571r1.php',
        'phpseclib4\\Crypt\\EC\\Formats\\Keys\\Common' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/Common.php',
        'phpseclib4\\Crypt\\EC\\Formats\\Keys\\JWK' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/JWK.php',
        'phpseclib4\\Crypt\\EC\\Formats\\Keys\\MontgomeryPrivate' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/MontgomeryPrivate.php',
        'phpseclib4\\Crypt\\EC\\Formats\\Keys\\MontgomeryPublic' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/MontgomeryPublic.php',
        'phpseclib4\\Crypt\\EC\\Formats\\Keys\\OpenSSH' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/OpenSSH.php',
        'phpseclib4\\Crypt\\EC\\Formats\\Keys\\PKCS1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/PKCS1.php',
        'phpseclib4\\Crypt\\EC\\Formats\\Keys\\PKCS8' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/PKCS8.php',
        'phpseclib4\\Crypt\\EC\\Formats\\Keys\\PuTTY' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/PuTTY.php',
        'phpseclib4\\Crypt\\EC\\Formats\\Keys\\XML' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/XML.php',
        'phpseclib4\\Crypt\\EC\\Formats\\Keys\\libsodium' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/libsodium.php',
        'phpseclib4\\Crypt\\EC\\Formats\\Signature\\ASN1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/ASN1.php',
        'phpseclib4\\Crypt\\EC\\Formats\\Signature\\IEEE' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/IEEE.php',
        'phpseclib4\\Crypt\\EC\\Formats\\Signature\\Raw' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/Raw.php',
        'phpseclib4\\Crypt\\EC\\Formats\\Signature\\SSH2' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/SSH2.php',
        'phpseclib4\\Crypt\\EC\\Parameters' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Parameters.php',
        'phpseclib4\\Crypt\\EC\\PrivateKey' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/PrivateKey.php',
        'phpseclib4\\Crypt\\EC\\PublicKey' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/PublicKey.php',
        'phpseclib4\\Crypt\\Hash' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Hash.php',
        'phpseclib4\\Crypt\\PublicKeyLoader' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/PublicKeyLoader.php',
        'phpseclib4\\Crypt\\RC2' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RC2.php',
        'phpseclib4\\Crypt\\RC4' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RC4.php',
        'phpseclib4\\Crypt\\RSA' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RSA.php',
        'phpseclib4\\Crypt\\RSA\\Formats\\Keys\\JWK' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/JWK.php',
        'phpseclib4\\Crypt\\RSA\\Formats\\Keys\\MSBLOB' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/MSBLOB.php',
        'phpseclib4\\Crypt\\RSA\\Formats\\Keys\\OpenSSH' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/OpenSSH.php',
        'phpseclib4\\Crypt\\RSA\\Formats\\Keys\\PKCS1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PKCS1.php',
        'phpseclib4\\Crypt\\RSA\\Formats\\Keys\\PKCS8' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PKCS8.php',
        'phpseclib4\\Crypt\\RSA\\Formats\\Keys\\PSS' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PSS.php',
        'phpseclib4\\Crypt\\RSA\\Formats\\Keys\\PuTTY' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PuTTY.php',
        'phpseclib4\\Crypt\\RSA\\Formats\\Keys\\Raw' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/Raw.php',
        'phpseclib4\\Crypt\\RSA\\Formats\\Keys\\XML' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/XML.php',
        'phpseclib4\\Crypt\\RSA\\PrivateKey' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/PrivateKey.php',
        'phpseclib4\\Crypt\\RSA\\PublicKey' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/PublicKey.php',
        'phpseclib4\\Crypt\\Rijndael' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.php',
        'phpseclib4\\Crypt\\Salsa20' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Salsa20.php',
        'phpseclib4\\Crypt\\TripleDES' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/TripleDES.php',
        'phpseclib4\\Crypt\\Twofish' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Twofish.php',
        'phpseclib4\\Exception\\BadConfigurationException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/BadConfigurationException.php',
        'phpseclib4\\Exception\\BadDecryptionException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/BadDecryptionException.php',
        'phpseclib4\\Exception\\BadMethodCallException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/BadMethodCallException.php',
        'phpseclib4\\Exception\\BaseException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/BaseException.php',
        'phpseclib4\\Exception\\CharacterConversionException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/CharacterConversionException.php',
        'phpseclib4\\Exception\\ConnectionClosedException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/ConnectionClosedException.php',
        'phpseclib4\\Exception\\EOCException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/EOCException.php',
        'phpseclib4\\Exception\\EncodedDataUnavailableException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/EncodedDataUnavailableException.php',
        'phpseclib4\\Exception\\ExcessivelyDeepDataException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/ExcessivelyDeepDataException.php',
        'phpseclib4\\Exception\\FileSystemException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/FileSystemException.php',
        'phpseclib4\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/InvalidArgumentException.php',
        'phpseclib4\\Exception\\InvalidModeException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/InvalidModeException.php',
        'phpseclib4\\Exception\\InvalidPacketLengthException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/InvalidPacketLengthException.php',
        'phpseclib4\\Exception\\InvalidStateException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/InvalidStateException.php',
        'phpseclib4\\Exception\\KeyConstraintException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/KeyConstraintException.php',
        'phpseclib4\\Exception\\LengthException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/LengthException.php',
        'phpseclib4\\Exception\\NoKeyLoadedException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/NoKeyLoadedException.php',
        'phpseclib4\\Exception\\NoSupportedAlgorithmsException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/NoSupportedAlgorithmsException.php',
        'phpseclib4\\Exception\\PasswordNeededException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/PasswordNeededException.php',
        'phpseclib4\\Exception\\ResourceLimitException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/ResourceLimitException.php',
        'phpseclib4\\Exception\\SSHChannelExitSignalException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/SSHChannelExitSignalException.php',
        'phpseclib4\\Exception\\ServiceUnavailableException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/ServiceUnavailableException.php',
        'phpseclib4\\Exception\\TimeoutException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/TimeoutException.php',
        'phpseclib4\\Exception\\UnexpectedSFTPPacketException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/UnexpectedSFTPPacketException.php',
        'phpseclib4\\Exception\\UnexpectedSSHMessageException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/UnexpectedSSHMessageException.php',
        'phpseclib4\\Exception\\UnexpectedValueException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/UnexpectedValueException.php',
        'phpseclib4\\Exception\\UnsupportedAlgorithmException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/UnsupportedAlgorithmException.php',
        'phpseclib4\\Exception\\UnsupportedCurveException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/UnsupportedCurveException.php',
        'phpseclib4\\Exception\\UnsupportedValueException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/UnsupportedValueException.php',
        'phpseclib4\\File\\ANSI' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ANSI.php',
        'phpseclib4\\File\\ASN1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1.php',
        'phpseclib4\\File\\ASN1\\Constructed' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Constructed.php',
        'phpseclib4\\File\\ASN1\\Element' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Element.php',
        'phpseclib4\\File\\ASN1\\ExcessivelyDeepData' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/ExcessivelyDeepData.php',
        'phpseclib4\\File\\ASN1\\MalformedData' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/MalformedData.php',
        'phpseclib4\\File\\ASN1\\Maps\\AccessDescription' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AccessDescription.php',
        'phpseclib4\\File\\ASN1\\Maps\\Accuracy' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Accuracy.php',
        'phpseclib4\\File\\ASN1\\Maps\\AdministrationDomainName' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AdministrationDomainName.php',
        'phpseclib4\\File\\ASN1\\Maps\\AlgorithmIdentifier' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AlgorithmIdentifier.php',
        'phpseclib4\\File\\ASN1\\Maps\\AnotherName' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AnotherName.php',
        'phpseclib4\\File\\ASN1\\Maps\\AttCertIssuer' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttCertIssuer.php',
        'phpseclib4\\File\\ASN1\\Maps\\AttCertValidityPeriod' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttCertValidityPeriod.php',
        'phpseclib4\\File\\ASN1\\Maps\\AttCertVersion' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttCertVersion.php',
        'phpseclib4\\File\\ASN1\\Maps\\AttCertVersionV1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttCertVersionV1.php',
        'phpseclib4\\File\\ASN1\\Maps\\Attribute' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Attribute.php',
        'phpseclib4\\File\\ASN1\\Maps\\AttributeCertificateInfo' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeCertificateInfo.php',
        'phpseclib4\\File\\ASN1\\Maps\\AttributeCertificateInfoV1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeCertificateInfoV1.php',
        'phpseclib4\\File\\ASN1\\Maps\\AttributeCertificateV1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeCertificateV1.php',
        'phpseclib4\\File\\ASN1\\Maps\\AttributeCertificateV2' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeCertificateV2.php',
        'phpseclib4\\File\\ASN1\\Maps\\AttributeType' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeType.php',
        'phpseclib4\\File\\ASN1\\Maps\\AttributeTypeAndValue' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeTypeAndValue.php',
        'phpseclib4\\File\\ASN1\\Maps\\AttributeValue' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeValue.php',
        'phpseclib4\\File\\ASN1\\Maps\\Attributes' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Attributes.php',
        'phpseclib4\\File\\ASN1\\Maps\\AuthAttributes' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AuthAttributes.php',
        'phpseclib4\\File\\ASN1\\Maps\\AuthEnvelopedData' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AuthEnvelopedData.php',
        'phpseclib4\\File\\ASN1\\Maps\\AuthenticatedSafe' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AuthenticatedSafe.php',
        'phpseclib4\\File\\ASN1\\Maps\\AuthorityInfoAccessSyntax' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AuthorityInfoAccessSyntax.php',
        'phpseclib4\\File\\ASN1\\Maps\\AuthorityKeyIdentifier' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AuthorityKeyIdentifier.php',
        'phpseclib4\\File\\ASN1\\Maps\\BaseDistance' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BaseDistance.php',
        'phpseclib4\\File\\ASN1\\Maps\\BasicConstraints' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BasicConstraints.php',
        'phpseclib4\\File\\ASN1\\Maps\\BuiltInDomainDefinedAttribute' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BuiltInDomainDefinedAttribute.php',
        'phpseclib4\\File\\ASN1\\Maps\\BuiltInDomainDefinedAttributes' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BuiltInDomainDefinedAttributes.php',
        'phpseclib4\\File\\ASN1\\Maps\\BuiltInStandardAttributes' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BuiltInStandardAttributes.php',
        'phpseclib4\\File\\ASN1\\Maps\\CMSAlgorithmProtection' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CMSAlgorithmProtection.php',
        'phpseclib4\\File\\ASN1\\Maps\\CMSEncryptedData' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CMSEncryptedData.php',
        'phpseclib4\\File\\ASN1\\Maps\\CMSVersion' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CMSVersion.php',
        'phpseclib4\\File\\ASN1\\Maps\\CPSuri' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CPSuri.php',
        'phpseclib4\\File\\ASN1\\Maps\\CRLDistributionPoints' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CRLDistributionPoints.php',
        'phpseclib4\\File\\ASN1\\Maps\\CRLNumber' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CRLNumber.php',
        'phpseclib4\\File\\ASN1\\Maps\\CRLReason' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CRLReason.php',
        'phpseclib4\\File\\ASN1\\Maps\\CertBag' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertBag.php',
        'phpseclib4\\File\\ASN1\\Maps\\CertPolicyId' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertPolicyId.php',
        'phpseclib4\\File\\ASN1\\Maps\\Certificate' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Certificate.php',
        'phpseclib4\\File\\ASN1\\Maps\\CertificateChoices' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificateChoices.php',
        'phpseclib4\\File\\ASN1\\Maps\\CertificateIssuer' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificateIssuer.php',
        'phpseclib4\\File\\ASN1\\Maps\\CertificateList' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificateList.php',
        'phpseclib4\\File\\ASN1\\Maps\\CertificatePolicies' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificatePolicies.php',
        'phpseclib4\\File\\ASN1\\Maps\\CertificateSerialNumber' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificateSerialNumber.php',
        'phpseclib4\\File\\ASN1\\Maps\\CertificateSet' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificateSet.php',
        'phpseclib4\\File\\ASN1\\Maps\\CertificationRequest' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificationRequest.php',
        'phpseclib4\\File\\ASN1\\Maps\\CertificationRequestInfo' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificationRequestInfo.php',
        'phpseclib4\\File\\ASN1\\Maps\\Characteristic_two' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Characteristic_two.php',
        'phpseclib4\\File\\ASN1\\Maps\\CompressedData' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CompressedData.php',
        'phpseclib4\\File\\ASN1\\Maps\\CompressionAlgorithmIdentifier' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CompressionAlgorithmIdentifier.php',
        'phpseclib4\\File\\ASN1\\Maps\\CompressionAlgorithmIdentifiers' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CompressionAlgorithmIdentifiers.php',
        'phpseclib4\\File\\ASN1\\Maps\\ContentEncryptionAlgorithmIdentifier' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ContentEncryptionAlgorithmIdentifier.php',
        'phpseclib4\\File\\ASN1\\Maps\\ContentInfo' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ContentInfo.php',
        'phpseclib4\\File\\ASN1\\Maps\\ContentType' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ContentType.php',
        'phpseclib4\\File\\ASN1\\Maps\\CountryName' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CountryName.php',
        'phpseclib4\\File\\ASN1\\Maps\\Curve' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Curve.php',
        'phpseclib4\\File\\ASN1\\Maps\\DHParameter' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DHParameter.php',
        'phpseclib4\\File\\ASN1\\Maps\\DSAParams' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DSAParams.php',
        'phpseclib4\\File\\ASN1\\Maps\\DSAPrivateKey' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DSAPrivateKey.php',
        'phpseclib4\\File\\ASN1\\Maps\\DSAPublicKey' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DSAPublicKey.php',
        'phpseclib4\\File\\ASN1\\Maps\\Digest' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Digest.php',
        'phpseclib4\\File\\ASN1\\Maps\\DigestAlgorithmIdentifier' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DigestAlgorithmIdentifier.php',
        'phpseclib4\\File\\ASN1\\Maps\\DigestAlgorithmIdentifiers' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DigestAlgorithmIdentifiers.php',
        'phpseclib4\\File\\ASN1\\Maps\\DigestInfo' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DigestInfo.php',
        'phpseclib4\\File\\ASN1\\Maps\\DigestedData' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DigestedData.php',
        'phpseclib4\\File\\ASN1\\Maps\\DirectoryString' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DirectoryString.php',
        'phpseclib4\\File\\ASN1\\Maps\\DisplayText' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DisplayText.php',
        'phpseclib4\\File\\ASN1\\Maps\\DistributionPoint' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DistributionPoint.php',
        'phpseclib4\\File\\ASN1\\Maps\\DistributionPointName' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DistributionPointName.php',
        'phpseclib4\\File\\ASN1\\Maps\\DssSigValue' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DssSigValue.php',
        'phpseclib4\\File\\ASN1\\Maps\\ECCCMSSharedInfo' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ECCCMSSharedInfo.php',
        'phpseclib4\\File\\ASN1\\Maps\\ECParameters' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ECParameters.php',
        'phpseclib4\\File\\ASN1\\Maps\\ECPoint' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ECPoint.php',
        'phpseclib4\\File\\ASN1\\Maps\\ECPrivateKey' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ECPrivateKey.php',
        'phpseclib4\\File\\ASN1\\Maps\\EDIPartyName' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EDIPartyName.php',
        'phpseclib4\\File\\ASN1\\Maps\\ESSCertID' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ESSCertID.php',
        'phpseclib4\\File\\ASN1\\Maps\\ESSCertIDv2' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ESSCertIDv2.php',
        'phpseclib4\\File\\ASN1\\Maps\\EcdsaSigValue' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EcdsaSigValue.php',
        'phpseclib4\\File\\ASN1\\Maps\\EncapsulatedContentInfo' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EncapsulatedContentInfo.php',
        'phpseclib4\\File\\ASN1\\Maps\\EncryptedContent' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EncryptedContent.php',
        'phpseclib4\\File\\ASN1\\Maps\\EncryptedContentInfo' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EncryptedContentInfo.php',
        'phpseclib4\\File\\ASN1\\Maps\\EncryptedData' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EncryptedData.php',
        'phpseclib4\\File\\ASN1\\Maps\\EncryptedDataCMS' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EncryptedDataCMS.php',
        'phpseclib4\\File\\ASN1\\Maps\\EncryptedKey' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EncryptedKey.php',
        'phpseclib4\\File\\ASN1\\Maps\\EncryptedPrivateKeyInfo' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EncryptedPrivateKeyInfo.php',
        'phpseclib4\\File\\ASN1\\Maps\\EnvelopedData' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EnvelopedData.php',
        'phpseclib4\\File\\ASN1\\Maps\\ExtKeyUsageSyntax' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ExtKeyUsageSyntax.php',
        'phpseclib4\\File\\ASN1\\Maps\\ExtendedCertificate' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ExtendedCertificate.php',
        'phpseclib4\\File\\ASN1\\Maps\\ExtendedCertificateInfo' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ExtendedCertificateInfo.php',
        'phpseclib4\\File\\ASN1\\Maps\\Extension' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Extension.php',
        'phpseclib4\\File\\ASN1\\Maps\\ExtensionAttribute' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ExtensionAttribute.php',
        'phpseclib4\\File\\ASN1\\Maps\\ExtensionAttributes' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ExtensionAttributes.php',
        'phpseclib4\\File\\ASN1\\Maps\\Extensions' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Extensions.php',
        'phpseclib4\\File\\ASN1\\Maps\\FieldElement' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/FieldElement.php',
        'phpseclib4\\File\\ASN1\\Maps\\FieldID' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/FieldID.php',
        'phpseclib4\\File\\ASN1\\Maps\\GeneralName' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralName.php',
        'phpseclib4\\File\\ASN1\\Maps\\GeneralNames' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralNames.php',
        'phpseclib4\\File\\ASN1\\Maps\\GeneralSubtree' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralSubtree.php',
        'phpseclib4\\File\\ASN1\\Maps\\GeneralSubtrees' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralSubtrees.php',
        'phpseclib4\\File\\ASN1\\Maps\\Hash' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Hash.php',
        'phpseclib4\\File\\ASN1\\Maps\\HashAlgAndValue' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/HashAlgAndValue.php',
        'phpseclib4\\File\\ASN1\\Maps\\HashAlgorithm' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/HashAlgorithm.php',
        'phpseclib4\\File\\ASN1\\Maps\\HoldInstructionCode' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/HoldInstructionCode.php',
        'phpseclib4\\File\\ASN1\\Maps\\Holder' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Holder.php',
        'phpseclib4\\File\\ASN1\\Maps\\InvalidityDate' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/InvalidityDate.php',
        'phpseclib4\\File\\ASN1\\Maps\\Iso4217CurrencyCode' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Iso4217CurrencyCode.php',
        'phpseclib4\\File\\ASN1\\Maps\\IssuerAltName' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/IssuerAltName.php',
        'phpseclib4\\File\\ASN1\\Maps\\IssuerAndSerialNumber' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/IssuerAndSerialNumber.php',
        'phpseclib4\\File\\ASN1\\Maps\\IssuerSerial' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/IssuerSerial.php',
        'phpseclib4\\File\\ASN1\\Maps\\IssuingDistributionPoint' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/IssuingDistributionPoint.php',
        'phpseclib4\\File\\ASN1\\Maps\\KEKIdentifier' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KEKIdentifier.php',
        'phpseclib4\\File\\ASN1\\Maps\\KEKRecipientInfo' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KEKRecipientInfo.php',
        'phpseclib4\\File\\ASN1\\Maps\\KeyAgreeRecipientIdentifier' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyAgreeRecipientIdentifier.php',
        'phpseclib4\\File\\ASN1\\Maps\\KeyAgreeRecipientInfo' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyAgreeRecipientInfo.php',
        'phpseclib4\\File\\ASN1\\Maps\\KeyDerivationAlgorithmIdentifier' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyDerivationAlgorithmIdentifier.php',
        'phpseclib4\\File\\ASN1\\Maps\\KeyEncryptionAlgorithmIdentifier' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyEncryptionAlgorithmIdentifier.php',
        'phpseclib4\\File\\ASN1\\Maps\\KeyIdentifier' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyIdentifier.php',
        'phpseclib4\\File\\ASN1\\Maps\\KeyPurposeId' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyPurposeId.php',
        'phpseclib4\\File\\ASN1\\Maps\\KeyTransRecipientInfo' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyTransRecipientInfo.php',
        'phpseclib4\\File\\ASN1\\Maps\\KeyUsage' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyUsage.php',
        'phpseclib4\\File\\ASN1\\Maps\\LogotypeAudio' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/LogotypeAudio.php',
        'phpseclib4\\File\\ASN1\\Maps\\LogotypeAudioInfo' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/LogotypeAudioInfo.php',
        'phpseclib4\\File\\ASN1\\Maps\\LogotypeData' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/LogotypeData.php',
        'phpseclib4\\File\\ASN1\\Maps\\LogotypeDetails' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/LogotypeDetails.php',
        'phpseclib4\\File\\ASN1\\Maps\\LogotypeExtn' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/LogotypeExtn.php',
        'phpseclib4\\File\\ASN1\\Maps\\LogotypeImage' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/LogotypeImage.php',
        'phpseclib4\\File\\ASN1\\Maps\\LogotypeImageInfo' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/LogotypeImageInfo.php',
        'phpseclib4\\File\\ASN1\\Maps\\LogotypeImageResolution' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/LogotypeImageResolution.php',
        'phpseclib4\\File\\ASN1\\Maps\\LogotypeImageType' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/LogotypeImageType.php',
        'phpseclib4\\File\\ASN1\\Maps\\LogotypeInfo' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/LogotypeInfo.php',
        'phpseclib4\\File\\ASN1\\Maps\\LogotypeReference' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/LogotypeReference.php',
        'phpseclib4\\File\\ASN1\\Maps\\MQVuserKeyingMaterial' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/MQVuserKeyingMaterial.php',
        'phpseclib4\\File\\ASN1\\Maps\\MacData' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/MacData.php',
        'phpseclib4\\File\\ASN1\\Maps\\MaskGenAlgorithm' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/MaskGenAlgorithm.php',
        'phpseclib4\\File\\ASN1\\Maps\\MessageAuthenticationCode' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/MessageAuthenticationCode.php',
        'phpseclib4\\File\\ASN1\\Maps\\MessageAuthenticationCodeAlgorithm' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/MessageAuthenticationCodeAlgorithm.php',
        'phpseclib4\\File\\ASN1\\Maps\\MessageImprint' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/MessageImprint.php',
        'phpseclib4\\File\\ASN1\\Maps\\MonetaryValue' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/MonetaryValue.php',
        'phpseclib4\\File\\ASN1\\Maps\\Name' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Name.php',
        'phpseclib4\\File\\ASN1\\Maps\\NameConstraints' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NameConstraints.php',
        'phpseclib4\\File\\ASN1\\Maps\\NetworkAddress' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NetworkAddress.php',
        'phpseclib4\\File\\ASN1\\Maps\\NoticeReference' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NoticeReference.php',
        'phpseclib4\\File\\ASN1\\Maps\\NumericUserIdentifier' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NumericUserIdentifier.php',
        'phpseclib4\\File\\ASN1\\Maps\\ORAddress' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ORAddress.php',
        'phpseclib4\\File\\ASN1\\Maps\\ObjectDigestInfo' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ObjectDigestInfo.php',
        'phpseclib4\\File\\ASN1\\Maps\\OneAsymmetricKey' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OneAsymmetricKey.php',
        'phpseclib4\\File\\ASN1\\Maps\\OrganizationName' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OrganizationName.php',
        'phpseclib4\\File\\ASN1\\Maps\\OrganizationalUnitNames' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OrganizationalUnitNames.php',
        'phpseclib4\\File\\ASN1\\Maps\\OriginatorIdentifierOrKey' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OriginatorIdentifierOrKey.php',
        'phpseclib4\\File\\ASN1\\Maps\\OriginatorInfo' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OriginatorInfo.php',
        'phpseclib4\\File\\ASN1\\Maps\\OriginatorPublicKey' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OriginatorPublicKey.php',
        'phpseclib4\\File\\ASN1\\Maps\\OtherCertificateFormat' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OtherCertificateFormat.php',
        'phpseclib4\\File\\ASN1\\Maps\\OtherKeyAttribute' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OtherKeyAttribute.php',
        'phpseclib4\\File\\ASN1\\Maps\\OtherLogotypeInfo' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OtherLogotypeInfo.php',
        'phpseclib4\\File\\ASN1\\Maps\\OtherPrimeInfo' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OtherPrimeInfo.php',
        'phpseclib4\\File\\ASN1\\Maps\\OtherPrimeInfos' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OtherPrimeInfos.php',
        'phpseclib4\\File\\ASN1\\Maps\\OtherRecipientInfo' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OtherRecipientInfo.php',
        'phpseclib4\\File\\ASN1\\Maps\\OtherRevocationInfoFormat' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OtherRevocationInfoFormat.php',
        'phpseclib4\\File\\ASN1\\Maps\\PBEParameter' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBEParameter.php',
        'phpseclib4\\File\\ASN1\\Maps\\PBES2params' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBES2params.php',
        'phpseclib4\\File\\ASN1\\Maps\\PBKDF2params' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBKDF2params.php',
        'phpseclib4\\File\\ASN1\\Maps\\PBMAC1params' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBMAC1params.php',
        'phpseclib4\\File\\ASN1\\Maps\\PFX' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PFX.php',
        'phpseclib4\\File\\ASN1\\Maps\\PKCS8ShroudedKeyBag' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PKCS8ShroudedKeyBag.php',
        'phpseclib4\\File\\ASN1\\Maps\\PKCS9String' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PKCS9String.php',
        'phpseclib4\\File\\ASN1\\Maps\\PSourceAlgorithm' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PSourceAlgorithm.php',
        'phpseclib4\\File\\ASN1\\Maps\\PasswordRecipientInfo' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PasswordRecipientInfo.php',
        'phpseclib4\\File\\ASN1\\Maps\\Pentanomial' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Pentanomial.php',
        'phpseclib4\\File\\ASN1\\Maps\\PersonalName' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PersonalName.php',
        'phpseclib4\\File\\ASN1\\Maps\\PolicyInformation' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyInformation.php',
        'phpseclib4\\File\\ASN1\\Maps\\PolicyMappings' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyMappings.php',
        'phpseclib4\\File\\ASN1\\Maps\\PolicyQualifierId' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyQualifierId.php',
        'phpseclib4\\File\\ASN1\\Maps\\PolicyQualifierInfo' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyQualifierInfo.php',
        'phpseclib4\\File\\ASN1\\Maps\\PostalAddress' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PostalAddress.php',
        'phpseclib4\\File\\ASN1\\Maps\\Prime_p' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Prime_p.php',
        'phpseclib4\\File\\ASN1\\Maps\\PrivateDomainName' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateDomainName.php',
        'phpseclib4\\File\\ASN1\\Maps\\PrivateKey' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateKey.php',
        'phpseclib4\\File\\ASN1\\Maps\\PrivateKeyInfo' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateKeyInfo.php',
        'phpseclib4\\File\\ASN1\\Maps\\PrivateKeyUsagePeriod' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateKeyUsagePeriod.php',
        'phpseclib4\\File\\ASN1\\Maps\\PublicKey' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PublicKey.php',
        'phpseclib4\\File\\ASN1\\Maps\\PublicKeyAndChallenge' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PublicKeyAndChallenge.php',
        'phpseclib4\\File\\ASN1\\Maps\\PublicKeyInfo' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PublicKeyInfo.php',
        'phpseclib4\\File\\ASN1\\Maps\\QCStatement' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/QCStatement.php',
        'phpseclib4\\File\\ASN1\\Maps\\QCStatements' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/QCStatements.php',
        'phpseclib4\\File\\ASN1\\Maps\\QcEuLimitValue' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/QcEuLimitValue.php',
        'phpseclib4\\File\\ASN1\\Maps\\RC2CBCParameter' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RC2CBCParameter.php',
        'phpseclib4\\File\\ASN1\\Maps\\RDNSequence' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RDNSequence.php',
        'phpseclib4\\File\\ASN1\\Maps\\RSAES_OAEP_params' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RSAES_OAEP_params.php',
        'phpseclib4\\File\\ASN1\\Maps\\RSAPrivateKey' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RSAPrivateKey.php',
        'phpseclib4\\File\\ASN1\\Maps\\RSAPublicKey' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RSAPublicKey.php',
        'phpseclib4\\File\\ASN1\\Maps\\RSASSA_PSS_params' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RSASSA_PSS_params.php',
        'phpseclib4\\File\\ASN1\\Maps\\ReasonFlags' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ReasonFlags.php',
        'phpseclib4\\File\\ASN1\\Maps\\RecipientEncryptedKey' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RecipientEncryptedKey.php',
        'phpseclib4\\File\\ASN1\\Maps\\RecipientEncryptedKeys' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RecipientEncryptedKeys.php',
        'phpseclib4\\File\\ASN1\\Maps\\RecipientIdentifier' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RecipientIdentifier.php',
        'phpseclib4\\File\\ASN1\\Maps\\RecipientInfo' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RecipientInfo.php',
        'phpseclib4\\File\\ASN1\\Maps\\RecipientInfos' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RecipientInfos.php',
        'phpseclib4\\File\\ASN1\\Maps\\RecipientKeyIdentifier' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RecipientKeyIdentifier.php',
        'phpseclib4\\File\\ASN1\\Maps\\RelativeDistinguishedName' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RelativeDistinguishedName.php',
        'phpseclib4\\File\\ASN1\\Maps\\RevocationInfoChoice' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RevocationInfoChoice.php',
        'phpseclib4\\File\\ASN1\\Maps\\RevocationInfoChoices' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RevocationInfoChoices.php',
        'phpseclib4\\File\\ASN1\\Maps\\RevokedCertificate' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RevokedCertificate.php',
        'phpseclib4\\File\\ASN1\\Maps\\SMIMECapabilities' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SMIMECapabilities.php',
        'phpseclib4\\File\\ASN1\\Maps\\SMIMECapability' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SMIMECapability.php',
        'phpseclib4\\File\\ASN1\\Maps\\SafeBag' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SafeBag.php',
        'phpseclib4\\File\\ASN1\\Maps\\SafeContents' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SafeContents.php',
        'phpseclib4\\File\\ASN1\\Maps\\SecretBag' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SecretBag.php',
        'phpseclib4\\File\\ASN1\\Maps\\ShakeOutputLen' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ShakeOutputLen.php',
        'phpseclib4\\File\\ASN1\\Maps\\SignatureAlgorithmIdentifier' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SignatureAlgorithmIdentifier.php',
        'phpseclib4\\File\\ASN1\\Maps\\SignatureValue' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SignatureValue.php',
        'phpseclib4\\File\\ASN1\\Maps\\SignedAttributes' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SignedAttributes.php',
        'phpseclib4\\File\\ASN1\\Maps\\SignedData' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SignedData.php',
        'phpseclib4\\File\\ASN1\\Maps\\SignedPublicKeyAndChallenge' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SignedPublicKeyAndChallenge.php',
        'phpseclib4\\File\\ASN1\\Maps\\SignerIdentifier' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SignerIdentifier.php',
        'phpseclib4\\File\\ASN1\\Maps\\SignerInfo' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SignerInfo.php',
        'phpseclib4\\File\\ASN1\\Maps\\SignerInfos' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SignerInfos.php',
        'phpseclib4\\File\\ASN1\\Maps\\SigningCertificate' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SigningCertificate.php',
        'phpseclib4\\File\\ASN1\\Maps\\SigningCertificateV2' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SigningCertificateV2.php',
        'phpseclib4\\File\\ASN1\\Maps\\SpecifiedECDomain' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SpecifiedECDomain.php',
        'phpseclib4\\File\\ASN1\\Maps\\SubjectAltName' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectAltName.php',
        'phpseclib4\\File\\ASN1\\Maps\\SubjectDirectoryAttributes' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectDirectoryAttributes.php',
        'phpseclib4\\File\\ASN1\\Maps\\SubjectInfoAccessSyntax' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectInfoAccessSyntax.php',
        'phpseclib4\\File\\ASN1\\Maps\\SubjectKeyIdentifier' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectKeyIdentifier.php',
        'phpseclib4\\File\\ASN1\\Maps\\SubjectPublicKeyInfo' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectPublicKeyInfo.php',
        'phpseclib4\\File\\ASN1\\Maps\\TBSCertList' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/TBSCertList.php',
        'phpseclib4\\File\\ASN1\\Maps\\TBSCertificate' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/TBSCertificate.php',
        'phpseclib4\\File\\ASN1\\Maps\\TSAPolicyId' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/TSAPolicyId.php',
        'phpseclib4\\File\\ASN1\\Maps\\TSTInfo' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/TSTInfo.php',
        'phpseclib4\\File\\ASN1\\Maps\\TerminalIdentifier' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/TerminalIdentifier.php',
        'phpseclib4\\File\\ASN1\\Maps\\Time' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Time.php',
        'phpseclib4\\File\\ASN1\\Maps\\TimeStampToken' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/TimeStampToken.php',
        'phpseclib4\\File\\ASN1\\Maps\\Trinomial' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Trinomial.php',
        'phpseclib4\\File\\ASN1\\Maps\\UnauthAttributes' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/UnauthAttributes.php',
        'phpseclib4\\File\\ASN1\\Maps\\UniqueIdentifier' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/UniqueIdentifier.php',
        'phpseclib4\\File\\ASN1\\Maps\\UnprotectedAttributes' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/UnprotectedAttributes.php',
        'phpseclib4\\File\\ASN1\\Maps\\UnsignedAttributes' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/UnsignedAttributes.php',
        'phpseclib4\\File\\ASN1\\Maps\\UserKeyingMaterial' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/UserKeyingMaterial.php',
        'phpseclib4\\File\\ASN1\\Maps\\UserNotice' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/UserNotice.php',
        'phpseclib4\\File\\ASN1\\Maps\\V2Form' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/V2Form.php',
        'phpseclib4\\File\\ASN1\\Maps\\Validity' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Validity.php',
        'phpseclib4\\File\\ASN1\\Maps\\netscape_ca_policy_url' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/netscape_ca_policy_url.php',
        'phpseclib4\\File\\ASN1\\Maps\\netscape_cert_type' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/netscape_cert_type.php',
        'phpseclib4\\File\\ASN1\\Maps\\netscape_comment' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/netscape_comment.php',
        'phpseclib4\\File\\ASN1\\OIDs\\CMS' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/OIDs/CMS.php',
        'phpseclib4\\File\\ASN1\\OIDs\\Curves' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/OIDs/Curves.php',
        'phpseclib4\\File\\ASN1\\OIDs\\EC' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/OIDs/EC.php',
        'phpseclib4\\File\\ASN1\\OIDs\\Hashes' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/OIDs/Hashes.php',
        'phpseclib4\\File\\ASN1\\OIDs\\PFX' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/OIDs/PFX.php',
        'phpseclib4\\File\\ASN1\\OIDs\\PKCS8' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/OIDs/PKCS8.php',
        'phpseclib4\\File\\ASN1\\OIDs\\X509' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/OIDs/X509.php',
        'phpseclib4\\File\\ASN1\\Types\\BMPString' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Types/BMPString.php',
        'phpseclib4\\File\\ASN1\\Types\\BaseString' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Types/BaseString.php',
        'phpseclib4\\File\\ASN1\\Types\\BaseType' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Types/BaseType.php',
        'phpseclib4\\File\\ASN1\\Types\\BitString' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Types/BitString.php',
        'phpseclib4\\File\\ASN1\\Types\\Boolean' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Types/Boolean.php',
        'phpseclib4\\File\\ASN1\\Types\\Choice' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Types/Choice.php',
        'phpseclib4\\File\\ASN1\\Types\\Common' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Types/Common.php',
        'phpseclib4\\File\\ASN1\\Types\\ExplicitNull' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Types/ExplicitNull.php',
        'phpseclib4\\File\\ASN1\\Types\\GeneralString' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Types/GeneralString.php',
        'phpseclib4\\File\\ASN1\\Types\\GeneralizedTime' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Types/GeneralizedTime.php',
        'phpseclib4\\File\\ASN1\\Types\\GraphicString' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Types/GraphicString.php',
        'phpseclib4\\File\\ASN1\\Types\\IA5String' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Types/IA5String.php',
        'phpseclib4\\File\\ASN1\\Types\\Integer' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Types/Integer.php',
        'phpseclib4\\File\\ASN1\\Types\\NumericString' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Types/NumericString.php',
        'phpseclib4\\File\\ASN1\\Types\\OID' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Types/OID.php',
        'phpseclib4\\File\\ASN1\\Types\\OctetString' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Types/OctetString.php',
        'phpseclib4\\File\\ASN1\\Types\\PrintableString' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Types/PrintableString.php',
        'phpseclib4\\File\\ASN1\\Types\\TeletexString' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Types/TeletexString.php',
        'phpseclib4\\File\\ASN1\\Types\\UTCTime' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Types/UTCTime.php',
        'phpseclib4\\File\\ASN1\\Types\\UTF8String' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Types/UTF8String.php',
        'phpseclib4\\File\\ASN1\\Types\\UniversalString' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Types/UniversalString.php',
        'phpseclib4\\File\\ASN1\\Types\\VideotexString' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Types/VideotexString.php',
        'phpseclib4\\File\\ASN1\\Types\\VisibleString' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Types/VisibleString.php',
        'phpseclib4\\File\\CMS' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/CMS.php',
        'phpseclib4\\File\\CMS\\CompressedData' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/CMS/CompressedData.php',
        'phpseclib4\\File\\CMS\\DigestedData' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/CMS/DigestedData.php',
        'phpseclib4\\File\\CMS\\EncryptedData' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/CMS/EncryptedData.php',
        'phpseclib4\\File\\CMS\\EnvelopedData\\DerivableKey' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/CMS/EnvelopedData/DerivableKey.php',
        'phpseclib4\\File\\CMS\\EnvelopedData\\KEKRecipient' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/CMS/EnvelopedData/KEKRecipient.php',
        'phpseclib4\\File\\CMS\\EnvelopedData\\KeyAgreeRecipient' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/CMS/EnvelopedData/KeyAgreeRecipient.php',
        'phpseclib4\\File\\CMS\\EnvelopedData\\KeyAgreeRecipient\\EncryptedKey' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/CMS/EnvelopedData/KeyAgreeRecipient/EncryptedKey.php',
        'phpseclib4\\File\\CMS\\EnvelopedData\\KeyTransRecipient' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/CMS/EnvelopedData/KeyTransRecipient.php',
        'phpseclib4\\File\\CMS\\EnvelopedData\\PasswordRecipient' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/CMS/EnvelopedData/PasswordRecipient.php',
        'phpseclib4\\File\\CMS\\EnvelopedData\\Recipient' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/CMS/EnvelopedData/Recipient.php',
        'phpseclib4\\File\\CMS\\EnvelopedData\\SearchableKey' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/CMS/EnvelopedData/SearchableKey.php',
        'phpseclib4\\File\\CMS\\SignedData' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/CMS/SignedData.php',
        'phpseclib4\\File\\CMS\\SignedData\\Signer' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/CMS/SignedData/Signer.php',
        'phpseclib4\\File\\CRL' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/CRL.php',
        'phpseclib4\\File\\CSR' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/CSR.php',
        'phpseclib4\\File\\Common\\Signable' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/Common/Signable.php',
        'phpseclib4\\File\\Common\\Traits\\ASN1Signature' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/Common/Traits/ASN1Signature.php',
        'phpseclib4\\File\\Common\\Traits\\DN' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/Common/Traits/DN.php',
        'phpseclib4\\File\\Common\\Traits\\Extension' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/Common/Traits/Extension.php',
        'phpseclib4\\File\\Common\\Traits\\KeyDerivation' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/Common/Traits/KeyDerivation.php',
        'phpseclib4\\File\\PFX' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/PFX.php',
        'phpseclib4\\File\\SPKAC' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/SPKAC.php',
        'phpseclib4\\File\\X509' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/X509.php',
        'phpseclib4\\Math\\BigInteger' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger.php',
        'phpseclib4\\Math\\BigInteger\\Engines\\BCMath' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath.php',
        'phpseclib4\\Math\\BigInteger\\Engines\\BCMath64' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath64.php',
        'phpseclib4\\Math\\BigInteger\\Engines\\BCMath\\Base' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/Base.php',
        'phpseclib4\\Math\\BigInteger\\Engines\\BCMath\\BuiltIn' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/BuiltIn.php',
        'phpseclib4\\Math\\BigInteger\\Engines\\BCMath\\DefaultEngine' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/DefaultEngine.php',
        'phpseclib4\\Math\\BigInteger\\Engines\\BCMath\\OpenSSL' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/OpenSSL.php',
        'phpseclib4\\Math\\BigInteger\\Engines\\BCMath\\Reductions\\Barrett' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/Reductions/Barrett.php',
        'phpseclib4\\Math\\BigInteger\\Engines\\BCMath\\Reductions\\EvalBarrett' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/Reductions/EvalBarrett.php',
        'phpseclib4\\Math\\BigInteger\\Engines\\Engine' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/Engine.php',
        'phpseclib4\\Math\\BigInteger\\Engines\\GMP' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/GMP.php',
        'phpseclib4\\Math\\BigInteger\\Engines\\GMP\\DefaultEngine' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/GMP/DefaultEngine.php',
        'phpseclib4\\Math\\BigInteger\\Engines\\OpenSSL' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/OpenSSL.php',
        'phpseclib4\\Math\\BigInteger\\Engines\\PHP' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP.php',
        'phpseclib4\\Math\\BigInteger\\Engines\\PHP32' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP32.php',
        'phpseclib4\\Math\\BigInteger\\Engines\\PHP64' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP64.php',
        'phpseclib4\\Math\\BigInteger\\Engines\\PHP\\Base' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Base.php',
        'phpseclib4\\Math\\BigInteger\\Engines\\PHP\\DefaultEngine' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/DefaultEngine.php',
        'phpseclib4\\Math\\BigInteger\\Engines\\PHP\\Montgomery' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Montgomery.php',
        'phpseclib4\\Math\\BigInteger\\Engines\\PHP\\OpenSSL' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/OpenSSL.php',
        'phpseclib4\\Math\\BigInteger\\Engines\\PHP\\Reductions\\Barrett' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Barrett.php',
        'phpseclib4\\Math\\BigInteger\\Engines\\PHP\\Reductions\\Classic' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Classic.php',
        'phpseclib4\\Math\\BigInteger\\Engines\\PHP\\Reductions\\EvalBarrett' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/EvalBarrett.php',
        'phpseclib4\\Math\\BigInteger\\Engines\\PHP\\Reductions\\Montgomery' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Montgomery.php',
        'phpseclib4\\Math\\BigInteger\\Engines\\PHP\\Reductions\\MontgomeryMult' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/MontgomeryMult.php',
        'phpseclib4\\Math\\BigInteger\\Engines\\PHP\\Reductions\\PowerOfTwo' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/PowerOfTwo.php',
        'phpseclib4\\Math\\BinaryField' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BinaryField.php',
        'phpseclib4\\Math\\BinaryField\\Integer' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BinaryField/Integer.php',
        'phpseclib4\\Math\\Common\\FiniteField' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/Common/FiniteField.php',
        'phpseclib4\\Math\\Common\\FiniteField\\Integer' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/Common/FiniteField/Integer.php',
        'phpseclib4\\Math\\PrimeField' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/PrimeField.php',
        'phpseclib4\\Math\\PrimeField\\Integer' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/PrimeField/Integer.php',
        'phpseclib4\\Net\\SCP' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Net/SCP.php',
        'phpseclib4\\Net\\SFTP' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Net/SFTP.php',
        'phpseclib4\\Net\\SFTP\\Attribute' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Net/SFTP/Attribute.php',
        'phpseclib4\\Net\\SFTP\\FileType' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Net/SFTP/FileType.php',
        'phpseclib4\\Net\\SFTP\\OpenFlag' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Net/SFTP/OpenFlag.php',
        'phpseclib4\\Net\\SFTP\\OpenFlag5' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Net/SFTP/OpenFlag5.php',
        'phpseclib4\\Net\\SFTP\\PacketType' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Net/SFTP/PacketType.php',
        'phpseclib4\\Net\\SFTP\\StatusCode' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Net/SFTP/StatusCode.php',
        'phpseclib4\\Net\\SFTP\\Stream' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Net/SFTP/Stream.php',
        'phpseclib4\\Net\\SSH2' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Net/SSH2.php',
        'phpseclib4\\Net\\SSH2\\ChannelConnectionFailureReason' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Net/SSH2/ChannelConnectionFailureReason.php',
        'phpseclib4\\Net\\SSH2\\DisconnectReason' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Net/SSH2/DisconnectReason.php',
        'phpseclib4\\Net\\SSH2\\MessageType' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Net/SSH2/MessageType.php',
        'phpseclib4\\Net\\SSH2\\MessageTypeExtra' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Net/SSH2/MessageTypeExtra.php',
        'phpseclib4\\Net\\SSH2\\TerminalMode' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Net/SSH2/TerminalMode.php',
        'phpseclib4\\System\\SSH\\Agent' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/System/SSH/Agent.php',
        'phpseclib4\\System\\SSH\\Agent\\Identity' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/System/SSH/Agent/Identity.php',
        'phpseclib4\\System\\SSH\\Common\\Traits\\ReadBytes' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/System/SSH/Common/Traits/ReadBytes.php',
    );

    public static function getInitializer(ClassLoader $loader)
    {
        return \Closure::bind(function () use ($loader) {
            $loader->prefixLengthsPsr4 = ComposerStaticInitfaa67bc9fee81c220dc566b8c299dea4::$prefixLengthsPsr4;
            $loader->prefixDirsPsr4 = ComposerStaticInitfaa67bc9fee81c220dc566b8c299dea4::$prefixDirsPsr4;
            $loader->prefixesPsr0 = ComposerStaticInitfaa67bc9fee81c220dc566b8c299dea4::$prefixesPsr0;
            $loader->classMap = ComposerStaticInitfaa67bc9fee81c220dc566b8c299dea4::$classMap;

        }, null, ClassLoader::class);
    }
}
<?php

/*
 * This file is part of Composer.
 *
 * (c) Nils Adermann <naderman@naderman.de>
 *     Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Composer\Autoload;

/**
 * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
 *
 *     $loader = new \Composer\Autoload\ClassLoader();
 *
 *     // register classes with namespaces
 *     $loader->add('Symfony\Component', __DIR__.'/component');
 *     $loader->add('Symfony',           __DIR__.'/framework');
 *
 *     // activate the autoloader
 *     $loader->register();
 *
 *     // to enable searching the include path (eg. for PEAR packages)
 *     $loader->setUseIncludePath(true);
 *
 * In this example, if you try to use a class in the Symfony\Component
 * namespace or one of its children (Symfony\Component\Console for instance),
 * the autoloader will first look for the class under the component/
 * directory, and it will then fallback to the framework/ directory if not
 * found before giving up.
 *
 * This class is loosely based on the Symfony UniversalClassLoader.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 * @author Jordi Boggiano <j.boggiano@seld.be>
 * @see    https://www.php-fig.org/psr/psr-0/
 * @see    https://www.php-fig.org/psr/psr-4/
 */
class ClassLoader
{
    /** @var \Closure(string):void */
    private static $includeFile;

    /** @var string|null */
    private $vendorDir;

    // PSR-4
    /**
     * @var array<string, array<string, int>>
     */
    private $prefixLengthsPsr4 = array();
    /**
     * @var array<string, list<string>>
     */
    private $prefixDirsPsr4 = array();
    /**
     * @var list<string>
     */
    private $fallbackDirsPsr4 = array();

    // PSR-0
    /**
     * List of PSR-0 prefixes
     *
     * Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2')))
     *
     * @var array<string, array<string, list<string>>>
     */
    private $prefixesPsr0 = array();
    /**
     * @var list<string>
     */
    private $fallbackDirsPsr0 = array();

    /** @var bool */
    private $useIncludePath = false;

    /**
     * @var array<string, string>
     */
    private $classMap = array();

    /** @var bool */
    private $classMapAuthoritative = false;

    /**
     * @var array<string, bool>
     */
    private $missingClasses = array();

    /** @var string|null */
    private $apcuPrefix;

    /**
     * @var array<string, self>
     */
    private static $registeredLoaders = array();

    /**
     * @param string|null $vendorDir
     */
    public function __construct($vendorDir = null)
    {
        $this->vendorDir = $vendorDir;
        self::initializeIncludeClosure();
    }

    /**
     * @return array<string, list<string>>
     */
    public function getPrefixes()
    {
        if (!empty($this->prefixesPsr0)) {
            return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
        }

        return array();
    }

    /**
     * @return array<string, list<string>>
     */
    public function getPrefixesPsr4()
    {
        return $this->prefixDirsPsr4;
    }

    /**
     * @return list<string>
     */
    public function getFallbackDirs()
    {
        return $this->fallbackDirsPsr0;
    }

    /**
     * @return list<string>
     */
    public function getFallbackDirsPsr4()
    {
        return $this->fallbackDirsPsr4;
    }

    /**
     * @return array<string, string> Array of classname => path
     */
    public function getClassMap()
    {
        return $this->classMap;
    }

    /**
     * @param array<string, string> $classMap Class to filename map
     *
     * @return void
     */
    public function addClassMap(array $classMap)
    {
        if ($this->classMap) {
            $this->classMap = array_merge($this->classMap, $classMap);
        } else {
            $this->classMap = $classMap;
        }
    }

    /**
     * Registers a set of PSR-0 directories for a given prefix, either
     * appending or prepending to the ones previously set for this prefix.
     *
     * @param string              $prefix  The prefix
     * @param list<string>|string $paths   The PSR-0 root directories
     * @param bool                $prepend Whether to prepend the directories
     *
     * @return void
     */
    public function add($prefix, $paths, $prepend = false)
    {
        $paths = (array) $paths;
        if (!$prefix) {
            if ($prepend) {
                $this->fallbackDirsPsr0 = array_merge(
                    $paths,
                    $this->fallbackDirsPsr0
                );
            } else {
                $this->fallbackDirsPsr0 = array_merge(
                    $this->fallbackDirsPsr0,
                    $paths
                );
            }

            return;
        }

        $first = $prefix[0];
        if (!isset($this->prefixesPsr0[$first][$prefix])) {
            $this->prefixesPsr0[$first][$prefix] = $paths;

            return;
        }
        if ($prepend) {
            $this->prefixesPsr0[$first][$prefix] = array_merge(
                $paths,
                $this->prefixesPsr0[$first][$prefix]
            );
        } else {
            $this->prefixesPsr0[$first][$prefix] = array_merge(
                $this->prefixesPsr0[$first][$prefix],
                $paths
            );
        }
    }

    /**
     * Registers a set of PSR-4 directories for a given namespace, either
     * appending or prepending to the ones previously set for this namespace.
     *
     * @param string              $prefix  The prefix/namespace, with trailing '\\'
     * @param list<string>|string $paths   The PSR-4 base directories
     * @param bool                $prepend Whether to prepend the directories
     *
     * @throws \InvalidArgumentException
     *
     * @return void
     */
    public function addPsr4($prefix, $paths, $prepend = false)
    {
        $paths = (array) $paths;
        if (!$prefix) {
            // Register directories for the root namespace.
            if ($prepend) {
                $this->fallbackDirsPsr4 = array_merge(
                    $paths,
                    $this->fallbackDirsPsr4
                );
            } else {
                $this->fallbackDirsPsr4 = array_merge(
                    $this->fallbackDirsPsr4,
                    $paths
                );
            }
        } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
            // Register directories for a new namespace.
            $length = strlen($prefix);
            if ('\\' !== $prefix[$length - 1]) {
                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
            }
            $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
            $this->prefixDirsPsr4[$prefix] = $paths;
        } elseif ($prepend) {
            // Prepend directories for an already registered namespace.
            $this->prefixDirsPsr4[$prefix] = array_merge(
                $paths,
                $this->prefixDirsPsr4[$prefix]
            );
        } else {
            // Append directories for an already registered namespace.
            $this->prefixDirsPsr4[$prefix] = array_merge(
                $this->prefixDirsPsr4[$prefix],
                $paths
            );
        }
    }

    /**
     * Registers a set of PSR-0 directories for a given prefix,
     * replacing any others previously set for this prefix.
     *
     * @param string              $prefix The prefix
     * @param list<string>|string $paths  The PSR-0 base directories
     *
     * @return void
     */
    public function set($prefix, $paths)
    {
        if (!$prefix) {
            $this->fallbackDirsPsr0 = (array) $paths;
        } else {
            $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
        }
    }

    /**
     * Registers a set of PSR-4 directories for a given namespace,
     * replacing any others previously set for this namespace.
     *
     * @param string              $prefix The prefix/namespace, with trailing '\\'
     * @param list<string>|string $paths  The PSR-4 base directories
     *
     * @throws \InvalidArgumentException
     *
     * @return void
     */
    public function setPsr4($prefix, $paths)
    {
        if (!$prefix) {
            $this->fallbackDirsPsr4 = (array) $paths;
        } else {
            $length = strlen($prefix);
            if ('\\' !== $prefix[$length - 1]) {
                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
            }
            $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
            $this->prefixDirsPsr4[$prefix] = (array) $paths;
        }
    }

    /**
     * Turns on searching the include path for class files.
     *
     * @param bool $useIncludePath
     *
     * @return void
     */
    public function setUseIncludePath($useIncludePath)
    {
        $this->useIncludePath = $useIncludePath;
    }

    /**
     * Can be used to check if the autoloader uses the include path to check
     * for classes.
     *
     * @return bool
     */
    public function getUseIncludePath()
    {
        return $this->useIncludePath;
    }

    /**
     * Turns off searching the prefix and fallback directories for classes
     * that have not been registered with the class map.
     *
     * @param bool $classMapAuthoritative
     *
     * @return void
     */
    public function setClassMapAuthoritative($classMapAuthoritative)
    {
        $this->classMapAuthoritative = $classMapAuthoritative;
    }

    /**
     * Should class lookup fail if not found in the current class map?
     *
     * @return bool
     */
    public function isClassMapAuthoritative()
    {
        return $this->classMapAuthoritative;
    }

    /**
     * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
     *
     * @param string|null $apcuPrefix
     *
     * @return void
     */
    public function setApcuPrefix($apcuPrefix)
    {
        $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
    }

    /**
     * The APCu prefix in use, or null if APCu caching is not enabled.
     *
     * @return string|null
     */
    public function getApcuPrefix()
    {
        return $this->apcuPrefix;
    }

    /**
     * Registers this instance as an autoloader.
     *
     * @param bool $prepend Whether to prepend the autoloader or not
     *
     * @return void
     */
    public function register($prepend = false)
    {
        spl_autoload_register(array($this, 'loadClass'), true, $prepend);

        if (null === $this->vendorDir) {
            return;
        }

        if ($prepend) {
            self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
        } else {
            unset(self::$registeredLoaders[$this->vendorDir]);
            self::$registeredLoaders[$this->vendorDir] = $this;
        }
    }

    /**
     * Unregisters this instance as an autoloader.
     *
     * @return void
     */
    public function unregister()
    {
        spl_autoload_unregister(array($this, 'loadClass'));

        if (null !== $this->vendorDir) {
            unset(self::$registeredLoaders[$this->vendorDir]);
        }
    }

    /**
     * Loads the given class or interface.
     *
     * @param  string    $class The name of the class
     * @return true|null True if loaded, null otherwise
     */
    public function loadClass($class)
    {
        if ($file = $this->findFile($class)) {
            $includeFile = self::$includeFile;
            $includeFile($file);

            return true;
        }

        return null;
    }

    /**
     * Finds the path to the file where the class is defined.
     *
     * @param string $class The name of the class
     *
     * @return string|false The path if found, false otherwise
     */
    public function findFile($class)
    {
        // class map lookup
        if (isset($this->classMap[$class])) {
            return $this->classMap[$class];
        }
        if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
            return false;
        }
        if (null !== $this->apcuPrefix) {
            $file = apcu_fetch($this->apcuPrefix.$class, $hit);
            if ($hit) {
                return $file;
            }
        }

        $file = $this->findFileWithExtension($class, '.php');

        // Search for Hack files if we are running on HHVM
        if (false === $file && defined('HHVM_VERSION')) {
            $file = $this->findFileWithExtension($class, '.hh');
        }

        if (null !== $this->apcuPrefix) {
            apcu_add($this->apcuPrefix.$class, $file);
        }

        if (false === $file) {
            // Remember that this class does not exist.
            $this->missingClasses[$class] = true;
        }

        return $file;
    }

    /**
     * Returns the currently registered loaders keyed by their corresponding vendor directories.
     *
     * @return array<string, self>
     */
    public static function getRegisteredLoaders()
    {
        return self::$registeredLoaders;
    }

    /**
     * @param  string       $class
     * @param  string       $ext
     * @return string|false
     */
    private function findFileWithExtension($class, $ext)
    {
        // PSR-4 lookup
        $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;

        $first = $class[0];
        if (isset($this->prefixLengthsPsr4[$first])) {
            $subPath = $class;
            while (false !== $lastPos = strrpos($subPath, '\\')) {
                $subPath = substr($subPath, 0, $lastPos);
                $search = $subPath . '\\';
                if (isset($this->prefixDirsPsr4[$search])) {
                    $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
                    foreach ($this->prefixDirsPsr4[$search] as $dir) {
                        if (file_exists($file = $dir . $pathEnd)) {
                            return $file;
                        }
                    }
                }
            }
        }

        // PSR-4 fallback dirs
        foreach ($this->fallbackDirsPsr4 as $dir) {
            if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
                return $file;
            }
        }

        // PSR-0 lookup
        if (false !== $pos = strrpos($class, '\\')) {
            // namespaced class name
            $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
                . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
        } else {
            // PEAR-like class name
            $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
        }

        if (isset($this->prefixesPsr0[$first])) {
            foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
                if (0 === strpos($class, $prefix)) {
                    foreach ($dirs as $dir) {
                        if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
                            return $file;
                        }
                    }
                }
            }
        }

        // PSR-0 fallback dirs
        foreach ($this->fallbackDirsPsr0 as $dir) {
            if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
                return $file;
            }
        }

        // PSR-0 include paths.
        if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
            return $file;
        }

        return false;
    }

    /**
     * @return void
     */
    private static function initializeIncludeClosure()
    {
        if (self::$includeFile !== null) {
            return;
        }

        /**
         * Scope isolated include.
         *
         * Prevents access to $this/self from included files.
         *
         * @param  string $file
         * @return void
         */
        self::$includeFile = \Closure::bind(static function($file) {
            include $file;
        }, null, null);
    }
}
<?php

/*
 * This file is part of Composer.
 *
 * (c) Nils Adermann <naderman@naderman.de>
 *     Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Composer;

use Composer\Autoload\ClassLoader;
use Composer\Semver\VersionParser;

/**
 * This class is copied in every Composer installed project and available to all
 *
 * See also https://getcomposer.org/doc/07-runtime.md#installed-versions
 *
 * To require its presence, you can require `composer-runtime-api ^2.0`
 *
 * @final
 */
class InstalledVersions
{
    /**
     * @var string|null if set (by reflection by Composer), this should be set to the path where this class is being copied to
     * @internal
     */
    private static $selfDir = null;

    /**
     * @var mixed[]|null
     * @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
     */
    private static $installed;

    /**
     * @var bool
     */
    private static $installedIsLocalDir;

    /**
     * @var bool|null
     */
    private static $canGetVendors;

    /**
     * @var array[]
     * @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
     */
    private static $installedByVendor = array();

    /**
     * Returns a list of all package names which are present, either by being installed, replaced or provided
     *
     * @return string[]
     * @psalm-return list<string>
     */
    public static function getInstalledPackages()
    {
        $packages = array();
        foreach (self::getInstalled() as $installed) {
            $packages[] = array_keys($installed['versions']);
        }

        if (1 === \count($packages)) {
            return $packages[0];
        }

        return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
    }

    /**
     * Returns a list of all package names with a specific type e.g. 'library'
     *
     * @param  string   $type
     * @return string[]
     * @psalm-return list<string>
     */
    public static function getInstalledPackagesByType($type)
    {
        $packagesByType = array();

        foreach (self::getInstalled() as $installed) {
            foreach ($installed['versions'] as $name => $package) {
                if (isset($package['type']) && $package['type'] === $type) {
                    $packagesByType[] = $name;
                }
            }
        }

        return $packagesByType;
    }

    /**
     * Checks whether the given package is installed
     *
     * This also returns true if the package name is provided or replaced by another package
     *
     * @param  string $packageName
     * @param  bool   $includeDevRequirements
     * @return bool
     */
    public static function isInstalled($packageName, $includeDevRequirements = true)
    {
        foreach (self::getInstalled() as $installed) {
            if (isset($installed['versions'][$packageName])) {
                return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false;
            }
        }

        return false;
    }

    /**
     * Checks whether the given package satisfies a version constraint
     *
     * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
     *
     *   Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
     *
     * @param  VersionParser $parser      Install composer/semver to have access to this class and functionality
     * @param  string        $packageName
     * @param  string|null   $constraint  A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
     * @return bool
     */
    public static function satisfies(VersionParser $parser, $packageName, $constraint)
    {
        $constraint = $parser->parseConstraints((string) $constraint);
        $provided = $parser->parseConstraints(self::getVersionRanges($packageName));

        return $provided->matches($constraint);
    }

    /**
     * Returns a version constraint representing all the range(s) which are installed for a given package
     *
     * It is easier to use this via isInstalled() with the $constraint argument if you need to check
     * whether a given version of a package is installed, and not just whether it exists
     *
     * @param  string $packageName
     * @return string Version constraint usable with composer/semver
     */
    public static function getVersionRanges($packageName)
    {
        foreach (self::getInstalled() as $installed) {
            if (!isset($installed['versions'][$packageName])) {
                continue;
            }

            $ranges = array();
            if (isset($installed['versions'][$packageName]['pretty_version'])) {
                $ranges[] = $installed['versions'][$packageName]['pretty_version'];
            }
            if (array_key_exists('aliases', $installed['versions'][$packageName])) {
                $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
            }
            if (array_key_exists('replaced', $installed['versions'][$packageName])) {
                $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
            }
            if (array_key_exists('provided', $installed['versions'][$packageName])) {
                $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
            }

            return implode(' || ', $ranges);
        }

        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
    }

    /**
     * @param  string      $packageName
     * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
     */
    public static function getVersion($packageName)
    {
        foreach (self::getInstalled() as $installed) {
            if (!isset($installed['versions'][$packageName])) {
                continue;
            }

            if (!isset($installed['versions'][$packageName]['version'])) {
                return null;
            }

            return $installed['versions'][$packageName]['version'];
        }

        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
    }

    /**
     * @param  string      $packageName
     * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
     */
    public static function getPrettyVersion($packageName)
    {
        foreach (self::getInstalled() as $installed) {
            if (!isset($installed['versions'][$packageName])) {
                continue;
            }

            if (!isset($installed['versions'][$packageName]['pretty_version'])) {
                return null;
            }

            return $installed['versions'][$packageName]['pretty_version'];
        }

        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
    }

    /**
     * @param  string      $packageName
     * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
     */
    public static function getReference($packageName)
    {
        foreach (self::getInstalled() as $installed) {
            if (!isset($installed['versions'][$packageName])) {
                continue;
            }

            if (!isset($installed['versions'][$packageName]['reference'])) {
                return null;
            }

            return $installed['versions'][$packageName]['reference'];
        }

        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
    }

    /**
     * @param  string      $packageName
     * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
     */
    public static function getInstallPath($packageName)
    {
        foreach (self::getInstalled() as $installed) {
            if (!isset($installed['versions'][$packageName])) {
                continue;
            }

            return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
        }

        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
    }

    /**
     * @return array
     * @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
     */
    public static function getRootPackage()
    {
        $installed = self::getInstalled();

        return $installed[0]['root'];
    }

    /**
     * Returns the raw installed.php data for custom implementations
     *
     * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
     * @return array[]
     * @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}
     */
    public static function getRawData()
    {
        @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);

        if (null === self::$installed) {
            // only require the installed.php file if this file is loaded from its dumped location,
            // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
            if (substr(__DIR__, -8, 1) !== 'C') {
                self::$installed = include __DIR__ . '/installed.php';
            } else {
                self::$installed = array();
            }
        }

        return self::$installed;
    }

    /**
     * Returns the raw data of all installed.php which are currently loaded for custom implementations
     *
     * @return array[]
     * @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
     */
    public static function getAllRawData()
    {
        return self::getInstalled();
    }

    /**
     * Lets you reload the static array from another file
     *
     * This is only useful for complex integrations in which a project needs to use
     * this class but then also needs to execute another project's autoloader in process,
     * and wants to ensure both projects have access to their version of installed.php.
     *
     * A typical case would be PHPUnit, where it would need to make sure it reads all
     * the data it needs from this class, then call reload() with
     * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
     * the project in which it runs can then also use this class safely, without
     * interference between PHPUnit's dependencies and the project's dependencies.
     *
     * @param  array[] $data A vendor/composer/installed.php data set
     * @return void
     *
     * @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data
     */
    public static function reload($data)
    {
        self::$installed = $data;
        self::$installedByVendor = array();

        // when using reload, we disable the duplicate protection to ensure that self::$installed data is
        // always returned, but we cannot know whether it comes from the installed.php in __DIR__ or not,
        // so we have to assume it does not, and that may result in duplicate data being returned when listing
        // all installed packages for example
        self::$installedIsLocalDir = false;
    }

    /**
     * @return string
     */
    private static function getSelfDir()
    {
        if (self::$selfDir === null) {
            self::$selfDir = strtr(__DIR__, '\\', '/');
        }

        return self::$selfDir;
    }

    /**
     * @return array[]
     * @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
     */
    private static function getInstalled()
    {
        if (null === self::$canGetVendors) {
            self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
        }

        $installed = array();
        $copiedLocalDir = false;

        if (self::$canGetVendors) {
            $selfDir = self::getSelfDir();
            foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
                $vendorDir = strtr($vendorDir, '\\', '/');
                if (isset(self::$installedByVendor[$vendorDir])) {
                    $installed[] = self::$installedByVendor[$vendorDir];
                } elseif (is_file($vendorDir.'/composer/installed.php')) {
                    /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
                    $required = require $vendorDir.'/composer/installed.php';
                    self::$installedByVendor[$vendorDir] = $required;
                    $installed[] = $required;
                    if (self::$installed === null && $vendorDir.'/composer' === $selfDir) {
                        self::$installed = $required;
                        self::$installedIsLocalDir = true;
                    }
                }
                if (self::$installedIsLocalDir && $vendorDir.'/composer' === $selfDir) {
                    $copiedLocalDir = true;
                }
            }
        }

        if (null === self::$installed) {
            // only require the installed.php file if this file is loaded from its dumped location,
            // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
            if (substr(__DIR__, -8, 1) !== 'C') {
                /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
                $required = require __DIR__ . '/installed.php';
                self::$installed = $required;
            } else {
                self::$installed = array();
            }
        }

        if (self::$installed !== array() && !$copiedLocalDir) {
            $installed[] = self::$installed;
        }

        return $installed;
    }
}
<?php

// autoload_psr4.php @generated by Composer

$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);

return array(
    'phpseclib4\\' => array($vendorDir . '/phpseclib/phpseclib/phpseclib'),
    'danog\\TelegramEntities\\' => array($vendorDir . '/danog/telegram-entities/src'),
    'danog\\MadelineProto\\' => array($vendorDir . '/danog/madelineproto/src'),
    'danog\\Loop\\' => array($vendorDir . '/danog/loop/lib'),
    'danog\\LibDNSJson\\' => array($vendorDir . '/danog/libdns-json/lib'),
    'danog\\DialogId\\' => array($vendorDir . '/danog/tg-dialog-id/src'),
    'danog\\Decoder\\' => array($vendorDir . '/danog/tg-file-decoder/src'),
    'danog\\BetterPrometheus\\' => array($vendorDir . '/danog/better-prometheus/lib'),
    'danog\\AsyncOrm\\' => array($vendorDir . '/danog/async-orm/src'),
    'Webrtc\\Webrtc\\' => array($vendorDir . '/danog/php-rtc-webrtc/src'),
    'Webrtc\\TURN\\' => array($vendorDir . '/danog/php-rtc-turn/src'),
    'Webrtc\\Stats\\' => array($vendorDir . '/danog/php-rtc-stats/src'),
    'Webrtc\\Srtp\\' => array($vendorDir . '/danog/php-rtc-srtp/src'),
    'Webrtc\\STUN\\' => array($vendorDir . '/danog/php-rtc-stun/src'),
    'Webrtc\\SDP\\' => array($vendorDir . '/danog/php-rtc-sdp/src'),
    'Webrtc\\SCTP\\' => array($vendorDir . '/danog/php-rtc-sctp/src'),
    'Webrtc\\RTP\\' => array($vendorDir . '/danog/php-rtc-rtp/src'),
    'Webrtc\\RTPParameter\\' => array($vendorDir . '/danog/php-rtc-rtp-parameter/src'),
    'Webrtc\\RTCP\\' => array($vendorDir . '/danog/php-rtc-rtcp/src'),
    'Webrtc\\NTP\\' => array($vendorDir . '/danog/php-rtc-ntp/src'),
    'Webrtc\\Mixin\\' => array($vendorDir . '/danog/php-rtc-mixin/src'),
    'Webrtc\\MDNS\\' => array($vendorDir . '/danog/php-rtc-mdns/src'),
    'Webrtc\\ICE\\' => array($vendorDir . '/danog/php-rtc-ice/src'),
    'Webrtc\\Exception\\' => array($vendorDir . '/danog/php-rtc-exception/src'),
    'Webrtc\\DataChannel\\' => array($vendorDir . '/danog/php-rtc-datachannel/src'),
    'Webrtc\\DTLS\\' => array($vendorDir . '/danog/php-rtc-dtls/src'),
    'Webrtc\\Codecs\\' => array($vendorDir . '/danog/php-rtc-codecs/src'),
    'Webrtc\\AVCodec\\' => array($vendorDir . '/danog/php-rtc-av/src'),
    'Webmozart\\Assert\\' => array($vendorDir . '/webmozart/assert/src'),
    'Symfony\\Polyfill\\Php86\\' => array($vendorDir . '/symfony/polyfill-php86'),
    'Symfony\\Polyfill\\Php85\\' => array($vendorDir . '/symfony/polyfill-php85'),
    'Symfony\\Polyfill\\Php84\\' => array($vendorDir . '/symfony/polyfill-php84'),
    'Symfony\\Polyfill\\Php83\\' => array($vendorDir . '/symfony/polyfill-php83'),
    'Symfony\\Polyfill\\Php82\\' => array($vendorDir . '/symfony/polyfill-php82'),
    'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'),
    'Symfony\\Polyfill\\Iconv\\' => array($vendorDir . '/symfony/polyfill-iconv'),
    'Revolt\\' => array($vendorDir . '/revolt/event-loop/src'),
    'Ramsey\\Uuid\\' => array($vendorDir . '/ramsey/uuid/src'),
    'Ramsey\\Collection\\' => array($vendorDir . '/ramsey/collection/src'),
    'Psr\\Log\\' => array($vendorDir . '/psr/log/src'),
    'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-factory/src', $vendorDir . '/psr/http-message/src'),
    'Prometheus\\' => array($vendorDir . '/promphp/prometheus_client_php/src/Prometheus'),
    'PhpParser\\' => array($vendorDir . '/nikic/php-parser/lib/PhpParser'),
    'ParagonIE\\ConstantTime\\' => array($vendorDir . '/paragonie/constant_time_encoding/src'),
    'Monolog\\' => array($vendorDir . '/monolog/monolog/src/Monolog'),
    'LibDNS\\' => array($vendorDir . '/daverandom/libdns/src'),
    'League\\Uri\\' => array($vendorDir . '/league/uri-components', $vendorDir . '/league/uri', $vendorDir . '/league/uri-interfaces'),
    'Kelunik\\Certificate\\' => array($vendorDir . '/kelunik/certificate/src'),
    'DASPRiD\\Enum\\' => array($vendorDir . '/dasprid/enum/src'),
    'Brick\\Math\\' => array($vendorDir . '/brick/math/src'),
    'BaconQrCode\\' => array($vendorDir . '/bacon/bacon-qr-code/src'),
    'Amp\\Websocket\\Client\\' => array($vendorDir . '/amphp/websocket-client/src'),
    'Amp\\Websocket\\' => array($vendorDir . '/amphp/websocket/src'),
    'Amp\\Sync\\' => array($vendorDir . '/amphp/sync/src'),
    'Amp\\Sql\\Common\\' => array($vendorDir . '/amphp/sql-common/src'),
    'Amp\\Sql\\' => array($vendorDir . '/amphp/sql/src'),
    'Amp\\Socket\\' => array($vendorDir . '/amphp/socket/src'),
    'Amp\\Serialization\\' => array($vendorDir . '/amphp/serialization/src'),
    'Amp\\Redis\\' => array($vendorDir . '/amphp/redis/src'),
    'Amp\\Process\\' => array($vendorDir . '/amphp/process/src'),
    'Amp\\Postgres\\' => array($vendorDir . '/amphp/postgres/src'),
    'Amp\\Pipeline\\' => array($vendorDir . '/amphp/pipeline/src'),
    'Amp\\Parser\\' => array($vendorDir . '/amphp/parser/src'),
    'Amp\\Parallel\\' => array($vendorDir . '/amphp/parallel/src'),
    'Amp\\Mysql\\' => array($vendorDir . '/amphp/mysql/src'),
    'Amp\\Log\\' => array($vendorDir . '/amphp/log/src'),
    'Amp\\Ipc\\' => array($vendorDir . '/danog/ipc/lib'),
    'Amp\\Http\\Server\\' => array($vendorDir . '/amphp/http-server/src'),
    'Amp\\Http\\Client\\Cookie\\' => array($vendorDir . '/amphp/http-client-cookies/src'),
    'Amp\\Http\\Client\\' => array($vendorDir . '/amphp/http-client/src'),
    'Amp\\Http\\' => array($vendorDir . '/amphp/http/src', $vendorDir . '/amphp/hpack/src'),
    'Amp\\File\\' => array($vendorDir . '/amphp/file/src'),
    'Amp\\DoH\\' => array($vendorDir . '/danog/dns-over-https/lib'),
    'Amp\\Dns\\' => array($vendorDir . '/amphp/dns/src'),
    'Amp\\Cache\\' => array($vendorDir . '/amphp/cache/src'),
    'Amp\\ByteStream\\' => array($vendorDir . '/amphp/byte-stream/src'),
    'Amp\\' => array($vendorDir . '/amphp/amp/src'),
);
<?php

// autoload_files.php @generated by Composer

$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);

return array(
    '88254829cb0eed057c30eaabb6d8edc4' => $vendorDir . '/amphp/amp/src/functions.php',
    '429ae5f14a13a9076791c19422e10996' => $vendorDir . '/amphp/amp/src/Future/functions.php',
    'c8601a4144b50a7b548da082c89c4dc1' => $vendorDir . '/amphp/amp/src/Internal/functions.php',
    '3da389f428d8ee50333e4391c3f45046' => $vendorDir . '/amphp/serialization/src/functions.php',
    'bcb7d4fc55f4b1a7e10f5806723e9892' => $vendorDir . '/amphp/sync/src/functions.php',
    '6c2681bc13923b80242cc286856cab22' => $vendorDir . '/amphp/byte-stream/src/functions.php',
    '4ee21bf8253a2272056aefb41f7f0116' => $vendorDir . '/amphp/byte-stream/src/Internal/functions.php',
    '445532134d762b3cbc25500cac266092' => $vendorDir . '/daverandom/libdns/src/functions.php',
    '107d0b55c0ad2d07d8643394552ac029' => $vendorDir . '/amphp/process/src/functions.php',
    'f4c761ca07639428acc28ba36643243e' => $vendorDir . '/amphp/dns/src/functions.php',
    'd4e415514e4352172d58f02433fa50e4' => $vendorDir . '/amphp/socket/src/functions.php',
    '1c2dcb9d6851a7abaae89f9586ddd460' => $vendorDir . '/amphp/socket/src/Internal/functions.php',
    '7fec264e4908bd2976476a6241c3f4d0' => $vendorDir . '/amphp/socket/src/SocketAddress/functions.php',
    'e39a8b23c42d4e1452234d762b03835a' => $vendorDir . '/ramsey/uuid/src/functions.php',
    '9d2b9fc6db0f153a0a149fefb182415e' => $vendorDir . '/symfony/polyfill-php84/bootstrap.php',
    '662a729f963d39afe703c9d9b7ab4a8c' => $vendorDir . '/symfony/polyfill-php83/bootstrap.php',
    '3d8ee50db78074a9235f0c2008c26b42' => $vendorDir . '/amphp/http/src/functions.php',
    '40522f9f0eb7d4f191114e944ee6d064' => $vendorDir . '/amphp/http/src/Internal/constants.php',
    '5897ea0ac4cccf14d323035e65887801' => $vendorDir . '/symfony/polyfill-php82/bootstrap.php',
    'decc78cc4436b1292c6c0d151b19445c' => $vendorDir . '/phpseclib/phpseclib/phpseclib/bootstrap.php',
    '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
    'db2f6d3e039789c7acd0e5d1d5a42077' => $vendorDir . '/amphp/http-client/src/functions.php',
    '77e5a577434e31d19d8dd6aeceac1ff4' => $vendorDir . '/amphp/http-client/src/Internal/functions.php',
    '5d92623b88cbe7c988a6aca6fa79ebce' => $vendorDir . '/amphp/parallel/src/Context/functions.php',
    'c20d640a0b06382af5bbe7e0c7475adf' => $vendorDir . '/amphp/parallel/src/Context/Internal/functions.php',
    '42a785d14bae29c606457755ffd1653b' => $vendorDir . '/amphp/parallel/src/Ipc/functions.php',
    '805558626a43dc52f2afc82368f8d62e' => $vendorDir . '/amphp/parallel/src/Worker/functions.php',
    '73cfe662a9f753fb79cdfcb7b4206d43' => $vendorDir . '/amphp/mysql/src/functions.php',
    '4da7a33b8388a4c58699a4f48894fced' => $vendorDir . '/amphp/postgres/src/functions.php',
    '33e77b43ad8185a87488d9c8e2900fb0' => $vendorDir . '/amphp/postgres/src/Internal/functions.php',
    '792db3860ad68f8c7b522ed67947a5eb' => $vendorDir . '/amphp/redis/src/functions.php',
    '6a333119f7ba58386c4385af1d708a0a' => $vendorDir . '/amphp/redis/src/Internal/functions.php',
    '3d05d4f147c95ba663000bd908d45656' => $vendorDir . '/amphp/websocket/src/functions.php',
    'd10f490189cfd2d00bda2b165dfbacae' => $vendorDir . '/amphp/file/src/functions.php',
    '9dc653cbaa44da77069f245bac46455f' => $vendorDir . '/amphp/http-server/src/Driver/functions.php',
    '3ff3609f8c8173833eaa97521a843531' => $vendorDir . '/amphp/http-server/src/Middleware/functions.php',
    'a56027adf84a32da155734de2d858e65' => $vendorDir . '/amphp/http-server/src/functions.php',
    '5ac5dbc97af12bd847e1db9fe93e192f' => $vendorDir . '/amphp/log/src/functions.php',
    '4be4fbd9f5a89207b1fd1c85ae339dd7' => $vendorDir . '/amphp/websocket-client/src/functions.php',
    '2b4b72fd9056e8b7ab3f418bbf68fc53' => $vendorDir . '/danog/ipc/lib/functions.php',
    'def43f6c87e4f8dfd0c9e1b1bab14fe8' => $vendorDir . '/symfony/polyfill-iconv/bootstrap.php',
    '606a39d89246991a373564698c2d8383' => $vendorDir . '/symfony/polyfill-php85/bootstrap.php',
    '2c2415ec15363ede1bff13a287462ba1' => $vendorDir . '/symfony/polyfill-php86/bootstrap.php',
    'cad60ee4f0892badc28f3feef7cce08d' => $vendorDir . '/danog/madelineproto/src/polyfill.php',
);
<?php return array(
    'root' => array(
        'name' => 'danog/madelineprotophar',
        'pretty_version' => '1.0.0+no-version-set',
        'version' => '1.0.0.0',
        'reference' => null,
        'type' => 'library',
        'install_path' => __DIR__ . '/../../',
        'aliases' => array(),
        'dev' => true,
    ),
    'versions' => array(
        'amphp/amp' => array(
            'pretty_version' => 'v3.1.3',
            'version' => '3.1.3.0',
            'reference' => '73c38b323ff8d790abf0f76c56fcc892bda4111a',
            'type' => 'library',
            'install_path' => __DIR__ . '/../amphp/amp',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'amphp/byte-stream' => array(
            'pretty_version' => 'v2.1.2',
            'version' => '2.1.2.0',
            'reference' => '55a6bd071aec26fa2a3e002618c20c35e3df1b46',
            'type' => 'library',
            'install_path' => __DIR__ . '/../amphp/byte-stream',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'amphp/cache' => array(
            'pretty_version' => 'v2.0.1',
            'version' => '2.0.1.0',
            'reference' => '46912e387e6aa94933b61ea1ead9cf7540b7797c',
            'type' => 'library',
            'install_path' => __DIR__ . '/../amphp/cache',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'amphp/dns' => array(
            'pretty_version' => 'v2.4.1',
            'version' => '2.4.1.0',
            'reference' => 'ec5948bfafac808f410406e18bc7b52a2d4629b7',
            'type' => 'library',
            'install_path' => __DIR__ . '/../amphp/dns',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'amphp/file' => array(
            'pretty_version' => 'v3.2.0',
            'version' => '3.2.0.0',
            'reference' => '28b38a805d2c235bb581d24415e78a42cd03aedc',
            'type' => 'library',
            'install_path' => __DIR__ . '/../amphp/file',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'amphp/hpack' => array(
            'pretty_version' => 'v3.2.2',
            'version' => '3.2.2.0',
            'reference' => '291da27078e7e149a9bad4d08ff05bf7d81c89f4',
            'type' => 'library',
            'install_path' => __DIR__ . '/../amphp/hpack',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'amphp/http' => array(
            'pretty_version' => 'v2.1.2',
            'version' => '2.1.2.0',
            'reference' => '3680d80bd38b5d6f3c2cef2214ca6dd6cef26588',
            'type' => 'library',
            'install_path' => __DIR__ . '/../amphp/http',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'amphp/http-client' => array(
            'pretty_version' => 'v5.3.6',
            'version' => '5.3.6.0',
            'reference' => 'ca155026acafa74a612d776a97202d53077fee86',
            'type' => 'library',
            'install_path' => __DIR__ . '/../amphp/http-client',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'amphp/http-client-cookies' => array(
            'pretty_version' => 'v2.0.0',
            'version' => '2.0.0.0',
            'reference' => '4124b37ffa4d15034e70d5d30e6fb9605ed6c1a7',
            'type' => 'library',
            'install_path' => __DIR__ . '/../amphp/http-client-cookies',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'amphp/http-server' => array(
            'pretty_version' => 'v3.4.7',
            'version' => '3.4.7.0',
            'reference' => 'aa96ca38c5f822a827236543d5417d0c2a782ef0',
            'type' => 'library',
            'install_path' => __DIR__ . '/../amphp/http-server',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'amphp/log' => array(
            'pretty_version' => 'v2.0.0',
            'version' => '2.0.0.0',
            'reference' => 'bf1562b8a18a3f30efa069ed740f412ac70a8a6c',
            'type' => 'library',
            'install_path' => __DIR__ . '/../amphp/log',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'amphp/mysql' => array(
            'pretty_version' => 'v3.1.1',
            'version' => '3.1.1.0',
            'reference' => 'ea3a5e07dc3dddc267669337ded2a98f02d86223',
            'type' => 'library',
            'install_path' => __DIR__ . '/../amphp/mysql',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'amphp/parallel' => array(
            'pretty_version' => 'v2.4.0',
            'version' => '2.4.0.0',
            'reference' => '37f5b2754fadc229c00f9416bd68fb8d04529a81',
            'type' => 'library',
            'install_path' => __DIR__ . '/../amphp/parallel',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'amphp/parser' => array(
            'pretty_version' => 'v1.1.1',
            'version' => '1.1.1.0',
            'reference' => '3cf1f8b32a0171d4b1bed93d25617637a77cded7',
            'type' => 'library',
            'install_path' => __DIR__ . '/../amphp/parser',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'amphp/pipeline' => array(
            'pretty_version' => 'v1.2.7',
            'version' => '1.2.7.0',
            'reference' => 'cf2d67696c2015ea7c7fd8f6ec5f8ed7c2d17c17',
            'type' => 'library',
            'install_path' => __DIR__ . '/../amphp/pipeline',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'amphp/postgres' => array(
            'pretty_version' => 'v2.2.1',
            'version' => '2.2.1.0',
            'reference' => 'b68c4d5929d0ec1701781dbbb0bb81dd8d0a42d7',
            'type' => 'library',
            'install_path' => __DIR__ . '/../amphp/postgres',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'amphp/process' => array(
            'pretty_version' => 'v2.1.0',
            'version' => '2.1.0.0',
            'reference' => '583959df17d00304ad7b0b32285373f985935643',
            'type' => 'library',
            'install_path' => __DIR__ . '/../amphp/process',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'amphp/redis' => array(
            'pretty_version' => 'v2.0.4',
            'version' => '2.0.4.0',
            'reference' => '964bcf6c2574645058371925a3668240a622bdab',
            'type' => 'library',
            'install_path' => __DIR__ . '/../amphp/redis',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'amphp/serialization' => array(
            'pretty_version' => 'v1.1.0',
            'version' => '1.1.0.0',
            'reference' => 'fdf2834d78cebb0205fb2672676c1b1eb84371f0',
            'type' => 'library',
            'install_path' => __DIR__ . '/../amphp/serialization',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'amphp/socket' => array(
            'pretty_version' => 'v2.4.1',
            'version' => '2.4.1.0',
            'reference' => 'b347be5aff6b2cc025208bb4d896607eb470c018',
            'type' => 'library',
            'install_path' => __DIR__ . '/../amphp/socket',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'amphp/sql' => array(
            'pretty_version' => 'v2.1.1',
            'version' => '2.1.1.0',
            'reference' => '258bafe5ecf8a0491d86681f2a2af1dee2933a69',
            'type' => 'library',
            'install_path' => __DIR__ . '/../amphp/sql',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'amphp/sql-common' => array(
            'pretty_version' => 'v2.0.4',
            'version' => '2.0.4.0',
            'reference' => '735da17ef0a66e7139c9f7584af5c3f9827f83c0',
            'type' => 'library',
            'install_path' => __DIR__ . '/../amphp/sql-common',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'amphp/sync' => array(
            'pretty_version' => 'v2.3.0',
            'version' => '2.3.0.0',
            'reference' => '217097b785130d77cfcc58ff583cf26cd1770bf1',
            'type' => 'library',
            'install_path' => __DIR__ . '/../amphp/sync',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'amphp/websocket' => array(
            'pretty_version' => 'v2.0.4',
            'version' => '2.0.4.0',
            'reference' => '963904b6a883c4b62d9222d1d9749814fac96a3b',
            'type' => 'library',
            'install_path' => __DIR__ . '/../amphp/websocket',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'amphp/websocket-client' => array(
            'pretty_version' => 'v2.0.2',
            'version' => '2.0.2.0',
            'reference' => 'dc033fdce0af56295a23f63ac4f579b34d470d6c',
            'type' => 'library',
            'install_path' => __DIR__ . '/../amphp/websocket-client',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'bacon/bacon-qr-code' => array(
            'pretty_version' => 'v3.1.1',
            'version' => '3.1.1.0',
            'reference' => '4da2233e72eeecd9be3b62e0dc2cc9ed8e2e31c2',
            'type' => 'library',
            'install_path' => __DIR__ . '/../bacon/bacon-qr-code',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'brick/math' => array(
            'pretty_version' => '1.0.0',
            'version' => '1.0.0.0',
            'reference' => '2effe05d2177c451b86c6a073196a4034c02f211',
            'type' => 'library',
            'install_path' => __DIR__ . '/../brick/math',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'danog/async-orm' => array(
            'pretty_version' => '1.1.7',
            'version' => '1.1.7.0',
            'reference' => '5f721426349a970cbb43b0daf7947d58a3807d16',
            'type' => 'library',
            'install_path' => __DIR__ . '/../danog/async-orm',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'danog/better-prometheus' => array(
            'pretty_version' => '0.1.1',
            'version' => '0.1.1.0',
            'reference' => '74e19343be41905572290878ca9fd0ef08ba0c5e',
            'type' => 'library',
            'install_path' => __DIR__ . '/../danog/better-prometheus',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'danog/dns-over-https' => array(
            'pretty_version' => '1.0.1',
            'version' => '1.0.1.0',
            'reference' => '142b8d439e7a582d66b13d9f1119820dd3110884',
            'type' => 'library',
            'install_path' => __DIR__ . '/../danog/dns-over-https',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'danog/ipc' => array(
            'pretty_version' => '1.1.0',
            'version' => '1.1.0.0',
            'reference' => 'eea6de6a57273f32a4f90a652ae1409786be18a0',
            'type' => 'library',
            'install_path' => __DIR__ . '/../danog/ipc',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'danog/libdns-json' => array(
            'pretty_version' => '0.2.1',
            'version' => '0.2.1.0',
            'reference' => 'cd2981a386a32ff62d447a9553e203384651ba90',
            'type' => 'library',
            'install_path' => __DIR__ . '/../danog/libdns-json',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'danog/loop' => array(
            'pretty_version' => '1.1.1',
            'version' => '1.1.1.0',
            'reference' => '83c42b26c1d8d070c56bc9c03e2e8a147b45c60f',
            'type' => 'library',
            'install_path' => __DIR__ . '/../danog/loop',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'danog/madelineproto' => array(
            'pretty_version' => '8.12.0',
            'version' => '8.12.0.0',
            'reference' => 'f751f650859e231ce1921721bbf78287df5c30f8',
            'type' => 'library',
            'install_path' => __DIR__ . '/../danog/madelineproto',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'danog/madelineprotophar' => array(
            'pretty_version' => '1.0.0+no-version-set',
            'version' => '1.0.0.0',
            'reference' => null,
            'type' => 'library',
            'install_path' => __DIR__ . '/../../',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'danog/php-rtc-av' => array(
            'pretty_version' => '1.1.11',
            'version' => '1.1.11.0',
            'reference' => 'f328e6b5682d7e885dcfab425839921312162008',
            'type' => 'library',
            'install_path' => __DIR__ . '/../danog/php-rtc-av',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'danog/php-rtc-codecs' => array(
            'pretty_version' => '1.1.19',
            'version' => '1.1.19.0',
            'reference' => '00836382276949099077e19659ff6648b823284f',
            'type' => 'library',
            'install_path' => __DIR__ . '/../danog/php-rtc-codecs',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'danog/php-rtc-datachannel' => array(
            'pretty_version' => '1.1.10',
            'version' => '1.1.10.0',
            'reference' => '3a5ad6f76326514f41a9e1847b41d503e157c459',
            'type' => 'library',
            'install_path' => __DIR__ . '/../danog/php-rtc-datachannel',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'danog/php-rtc-dtls' => array(
            'pretty_version' => '2.0.20',
            'version' => '2.0.20.0',
            'reference' => '794180b0c3cf54842df44b7bcb44f8fb50f199c8',
            'type' => 'library',
            'install_path' => __DIR__ . '/../danog/php-rtc-dtls',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'danog/php-rtc-exception' => array(
            'pretty_version' => '1.1.10',
            'version' => '1.1.10.0',
            'reference' => 'f9347a5376bb6b7b2fb008e06a8fd9fb6d9febd7',
            'type' => 'library',
            'install_path' => __DIR__ . '/../danog/php-rtc-exception',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'danog/php-rtc-ice' => array(
            'pretty_version' => '2.0.18',
            'version' => '2.0.18.0',
            'reference' => '91c53e982f334cf3bfe64a9e298d874bba51a3f1',
            'type' => 'library',
            'install_path' => __DIR__ . '/../danog/php-rtc-ice',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'danog/php-rtc-mdns' => array(
            'pretty_version' => '1.1.12',
            'version' => '1.1.12.0',
            'reference' => 'ff3f4315339ddcdd4d71bd4e02156903261ef57a',
            'type' => 'library',
            'install_path' => __DIR__ . '/../danog/php-rtc-mdns',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'danog/php-rtc-mixin' => array(
            'pretty_version' => '2.0.9',
            'version' => '2.0.9.0',
            'reference' => 'e7a086a7fe01875f52e1f08301195db05e7ff9a5',
            'type' => 'library',
            'install_path' => __DIR__ . '/../danog/php-rtc-mixin',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'danog/php-rtc-ntp' => array(
            'pretty_version' => '1.1.15',
            'version' => '1.1.15.0',
            'reference' => 'd70dc01410dba146cb808b9c2bf3c28ed1199d20',
            'type' => 'library',
            'install_path' => __DIR__ . '/../danog/php-rtc-ntp',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'danog/php-rtc-rtcp' => array(
            'pretty_version' => '1.1.11',
            'version' => '1.1.11.0',
            'reference' => 'f2e7c76d040fb0c4b2e4b62a611d13c6352af145',
            'type' => 'library',
            'install_path' => __DIR__ . '/../danog/php-rtc-rtcp',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'danog/php-rtc-rtp' => array(
            'pretty_version' => '2.0.23',
            'version' => '2.0.23.0',
            'reference' => 'e9bee7a5aae91f6abec1fe9a599eefbce219d967',
            'type' => 'library',
            'install_path' => __DIR__ . '/../danog/php-rtc-rtp',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'danog/php-rtc-rtp-parameter' => array(
            'pretty_version' => '1.1.8',
            'version' => '1.1.8.0',
            'reference' => 'cd1e34df768ddae43fc50c1306c2332cf99f07eb',
            'type' => 'library',
            'install_path' => __DIR__ . '/../danog/php-rtc-rtp-parameter',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'danog/php-rtc-sctp' => array(
            'pretty_version' => '2.0.14',
            'version' => '2.0.14.0',
            'reference' => '741697e355abd7eb8e8134b115fab775bb5ccd06',
            'type' => 'library',
            'install_path' => __DIR__ . '/../danog/php-rtc-sctp',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'danog/php-rtc-sdp' => array(
            'pretty_version' => '1.1.12',
            'version' => '1.1.12.0',
            'reference' => '315b9cbaff71625351864c292de0b6ac3b3a39fc',
            'type' => 'library',
            'install_path' => __DIR__ . '/../danog/php-rtc-sdp',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'danog/php-rtc-srtp' => array(
            'pretty_version' => '1.1.16',
            'version' => '1.1.16.0',
            'reference' => 'b19959292663d8933a0a48cd1fda05788fb5c542',
            'type' => 'library',
            'install_path' => __DIR__ . '/../danog/php-rtc-srtp',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'danog/php-rtc-stats' => array(
            'pretty_version' => '1.1.7',
            'version' => '1.1.7.0',
            'reference' => '137934d7d57e97184d5b6b2fb58e571e0d3f9fbd',
            'type' => 'library',
            'install_path' => __DIR__ . '/../danog/php-rtc-stats',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'danog/php-rtc-stun' => array(
            'pretty_version' => '2.0.18',
            'version' => '2.0.18.0',
            'reference' => '7f1b48a736e6d7a9ad174e7dc58b794168befed4',
            'type' => 'library',
            'install_path' => __DIR__ . '/../danog/php-rtc-stun',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'danog/php-rtc-turn' => array(
            'pretty_version' => '2.0.9',
            'version' => '2.0.9.0',
            'reference' => '5789c0d4b23c6097c72812fa8f9e9930ac288bc6',
            'type' => 'library',
            'install_path' => __DIR__ . '/../danog/php-rtc-turn',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'danog/php-rtc-webrtc' => array(
            'pretty_version' => '2.0.20',
            'version' => '2.0.20.0',
            'reference' => '58f8a24180cd1e137127214f9bfba3b0a7eff97b',
            'type' => 'library',
            'install_path' => __DIR__ . '/../danog/php-rtc-webrtc',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'danog/primemodule' => array(
            'pretty_version' => '1.0.13',
            'version' => '1.0.13.0',
            'reference' => 'e7ebfc1ea64c24186e49064cd4b76bc76c28600e',
            'type' => 'library',
            'install_path' => __DIR__ . '/../danog/primemodule',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'danog/telegram-entities' => array(
            'pretty_version' => '1.1.0',
            'version' => '1.1.0.0',
            'reference' => '92ca7621594b7a6c4a820c3b557e66ee1d439273',
            'type' => 'library',
            'install_path' => __DIR__ . '/../danog/telegram-entities',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'danog/tg-dialog-id' => array(
            'pretty_version' => '2.0.0',
            'version' => '2.0.0.0',
            'reference' => 'd03c653b2dad43d739675c83a6e8fa14b3b43636',
            'type' => 'library',
            'install_path' => __DIR__ . '/../danog/tg-dialog-id',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'danog/tg-file-decoder' => array(
            'pretty_version' => '1.0.2',
            'version' => '1.0.2.0',
            'reference' => 'f4da78c28d47b658c4596249f1d6595956f3b796',
            'type' => 'project',
            'install_path' => __DIR__ . '/../danog/tg-file-decoder',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'dasprid/enum' => array(
            'pretty_version' => '1.0.7',
            'version' => '1.0.7.0',
            'reference' => 'b5874fa9ed0043116c72162ec7f4fb50e02e7cce',
            'type' => 'library',
            'install_path' => __DIR__ . '/../dasprid/enum',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'daverandom/libdns' => array(
            'pretty_version' => 'v2.1.0',
            'version' => '2.1.0.0',
            'reference' => 'b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a',
            'type' => 'library',
            'install_path' => __DIR__ . '/../daverandom/libdns',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'endclothing/prometheus_client_php' => array(
            'dev_requirement' => false,
            'replaced' => array(
                0 => '*',
            ),
        ),
        'jimdo/prometheus_client_php' => array(
            'dev_requirement' => false,
            'replaced' => array(
                0 => '*',
            ),
        ),
        'kelunik/certificate' => array(
            'pretty_version' => 'v1.1.3',
            'version' => '1.1.3.0',
            'reference' => '7e00d498c264d5eb4f78c69f41c8bd6719c0199e',
            'type' => 'library',
            'install_path' => __DIR__ . '/../kelunik/certificate',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'league/uri' => array(
            'pretty_version' => '7.9.0',
            'version' => '7.9.0.0',
            'reference' => 'fcc872a3d872aa13f935e0cd1887b9abb06da07f',
            'type' => 'library',
            'install_path' => __DIR__ . '/../league/uri',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'league/uri-components' => array(
            'pretty_version' => '7.9.0',
            'version' => '7.9.0.0',
            'reference' => '4e37c1663f7d0daebbab6b3d947631b0835b3943',
            'type' => 'library',
            'install_path' => __DIR__ . '/../league/uri-components',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'league/uri-interfaces' => array(
            'pretty_version' => '7.9.0',
            'version' => '7.9.0.0',
            'reference' => 'eb5f6d24e6a4fe0a8e9c78ded4eb1dfff1404f58',
            'type' => 'library',
            'install_path' => __DIR__ . '/../league/uri-interfaces',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'lkaemmerling/prometheus_client_php' => array(
            'dev_requirement' => false,
            'replaced' => array(
                0 => '*',
            ),
        ),
        'monolog/monolog' => array(
            'pretty_version' => '3.12.1',
            'version' => '3.12.1.0',
            'reference' => 'd7059e4c691d05ea1222b5124bed0f961be46e15',
            'type' => 'library',
            'install_path' => __DIR__ . '/../monolog/monolog',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'nikic/php-parser' => array(
            'pretty_version' => 'v5.9.0',
            'version' => '5.9.0.0',
            'reference' => '9e33da9553fe7786f0962b35f4e4ecf01be89def',
            'type' => 'library',
            'install_path' => __DIR__ . '/../nikic/php-parser',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'paragonie/constant_time_encoding' => array(
            'pretty_version' => 'v3.1.3',
            'version' => '3.1.3.0',
            'reference' => 'd5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77',
            'type' => 'library',
            'install_path' => __DIR__ . '/../paragonie/constant_time_encoding',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'phpseclib/phpseclib' => array(
            'pretty_version' => '4.0.2',
            'version' => '4.0.2.0',
            'reference' => '1fe20b9acb08ce810cec24d8a9d02e7c13426e4c',
            'type' => 'library',
            'install_path' => __DIR__ . '/../phpseclib/phpseclib',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'promphp/prometheus_client_php' => array(
            'pretty_version' => 'v2.15.1',
            'version' => '2.15.1.0',
            'reference' => 'e153786bda5ca1d07335a2b5f477742c37195c40',
            'type' => 'library',
            'install_path' => __DIR__ . '/../promphp/prometheus_client_php',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'psr/http-factory' => array(
            'pretty_version' => '1.1.0',
            'version' => '1.1.0.0',
            'reference' => '2b4765fddfe3b508ac62f829e852b1501d3f6e8a',
            'type' => 'library',
            'install_path' => __DIR__ . '/../psr/http-factory',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'psr/http-message' => array(
            'pretty_version' => '2.0',
            'version' => '2.0.0.0',
            'reference' => '402d35bcb92c70c026d1a6a9883f06b2ead23d71',
            'type' => 'library',
            'install_path' => __DIR__ . '/../psr/http-message',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'psr/log' => array(
            'pretty_version' => '3.0.2',
            'version' => '3.0.2.0',
            'reference' => 'f16e1d5863e37f8d8c2a01719f5b34baa2b714d3',
            'type' => 'library',
            'install_path' => __DIR__ . '/../psr/log',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'psr/log-implementation' => array(
            'dev_requirement' => false,
            'provided' => array(
                0 => '3.0.0',
            ),
        ),
        'ramsey/collection' => array(
            'pretty_version' => '2.1.1',
            'version' => '2.1.1.0',
            'reference' => '344572933ad0181accbf4ba763e85a0306a8c5e2',
            'type' => 'library',
            'install_path' => __DIR__ . '/../ramsey/collection',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'ramsey/uuid' => array(
            'pretty_version' => '4.9.4',
            'version' => '4.9.4.0',
            'reference' => '75d73f48d02797c2c285a7e9f348fadc0102ffe2',
            'type' => 'library',
            'install_path' => __DIR__ . '/../ramsey/uuid',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'revolt/event-loop' => array(
            'pretty_version' => 'v1.0.9',
            'version' => '1.0.9.0',
            'reference' => '44061cf513e53c6200372fc935ac42271566295d',
            'type' => 'library',
            'install_path' => __DIR__ . '/../revolt/event-loop',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'rhumsaa/uuid' => array(
            'dev_requirement' => false,
            'replaced' => array(
                0 => '4.9.4',
            ),
        ),
        'symfony/polyfill-iconv' => array(
            'pretty_version' => 'v1.43.0',
            'version' => '1.43.0.0',
            'reference' => '254724d95fe01b679cd97d88786e9623ee00bfb7',
            'type' => 'library',
            'install_path' => __DIR__ . '/../symfony/polyfill-iconv',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'symfony/polyfill-mbstring' => array(
            'pretty_version' => 'v1.43.0',
            'version' => '1.43.0.0',
            'reference' => '4d5d331bf731e0aedbc6b2f5e61135b7b7996a72',
            'type' => 'library',
            'install_path' => __DIR__ . '/../symfony/polyfill-mbstring',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'symfony/polyfill-php82' => array(
            'pretty_version' => 'v1.43.0',
            'version' => '1.43.0.0',
            'reference' => '843bbe8b2a7934307b749751eede0f111f5d1cfc',
            'type' => 'library',
            'install_path' => __DIR__ . '/../symfony/polyfill-php82',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'symfony/polyfill-php83' => array(
            'pretty_version' => 'v1.43.0',
            'version' => '1.43.0.0',
            'reference' => '80ccff923a8d61f73ebe9da0aa94e25232f004ce',
            'type' => 'library',
            'install_path' => __DIR__ . '/../symfony/polyfill-php83',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'symfony/polyfill-php84' => array(
            'pretty_version' => 'v1.43.0',
            'version' => '1.43.0.0',
            'reference' => '22c6548499cbe5c5bb072fc0c42312dff062a015',
            'type' => 'library',
            'install_path' => __DIR__ . '/../symfony/polyfill-php84',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'symfony/polyfill-php85' => array(
            'pretty_version' => 'v1.43.0',
            'version' => '1.43.0.0',
            'reference' => '86a42cbcc07adc331481a5ab310a4024bfc227f6',
            'type' => 'library',
            'install_path' => __DIR__ . '/../symfony/polyfill-php85',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'symfony/polyfill-php86' => array(
            'pretty_version' => 'v1.43.0',
            'version' => '1.43.0.0',
            'reference' => '8840df45b24f33a49dc419fa18dc0057231ae1d8',
            'type' => 'library',
            'install_path' => __DIR__ . '/../symfony/polyfill-php86',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
        'webmozart/assert' => array(
            'pretty_version' => '2.5.0',
            'version' => '2.5.0.0',
            'reference' => '8a163706bbcac613733dba03f51e13b5eb1bb819',
            'type' => 'library',
            'install_path' => __DIR__ . '/../webmozart/assert',
            'aliases' => array(),
            'dev_requirement' => false,
        ),
    ),
);
<?php

// autoload_namespaces.php @generated by Composer

$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);

return array(
    'danog\\' => array($vendorDir . '/danog/primemodule/lib'),
);
{
    "packages": [
        {
            "name": "amphp/amp",
            "version": "v3.1.3",
            "version_normalized": "3.1.3.0",
            "source": {
                "type": "git",
                "url": "https://github.com/amphp/amp.git",
                "reference": "73c38b323ff8d790abf0f76c56fcc892bda4111a"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/amphp/amp/zipball/73c38b323ff8d790abf0f76c56fcc892bda4111a",
                "reference": "73c38b323ff8d790abf0f76c56fcc892bda4111a",
                "shasum": ""
            },
            "require": {
                "php": ">=8.1",
                "revolt/event-loop": "^1 || ^0.2"
            },
            "require-dev": {
                "amphp/php-cs-fixer-config": "^2",
                "phpunit/phpunit": "^9",
                "psalm/phar": "6.16.1"
            },
            "time": "2026-07-19T17:59:20+00:00",
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "files": [
                    "src/functions.php",
                    "src/Future/functions.php",
                    "src/Internal/functions.php"
                ],
                "psr-4": {
                    "Amp\\": "src"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Aaron Piotrowski",
                    "email": "aaron@trowski.com"
                },
                {
                    "name": "Bob Weinand",
                    "email": "bobwei9@hotmail.com"
                },
                {
                    "name": "Niklas Keller",
                    "email": "me@kelunik.com"
                },
                {
                    "name": "Daniel Lowrey",
                    "email": "rdlowrey@php.net"
                }
            ],
            "description": "A non-blocking concurrency framework for PHP applications.",
            "homepage": "https://amphp.org/amp",
            "keywords": [
                "async",
                "asynchronous",
                "awaitable",
                "concurrency",
                "event",
                "event-loop",
                "future",
                "non-blocking",
                "promise"
            ],
            "support": {
                "issues": "https://github.com/amphp/amp/issues",
                "source": "https://github.com/amphp/amp/tree/v3.1.3"
            },
            "funding": [
                {
                    "url": "https://github.com/amphp",
                    "type": "github"
                }
            ],
            "install-path": "../amphp/amp"
        },
        {
            "name": "amphp/byte-stream",
            "version": "v2.1.2",
            "version_normalized": "2.1.2.0",
            "source": {
                "type": "git",
                "url": "https://github.com/amphp/byte-stream.git",
                "reference": "55a6bd071aec26fa2a3e002618c20c35e3df1b46"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/amphp/byte-stream/zipball/55a6bd071aec26fa2a3e002618c20c35e3df1b46",
                "reference": "55a6bd071aec26fa2a3e002618c20c35e3df1b46",
                "shasum": ""
            },
            "require": {
                "amphp/amp": "^3",
                "amphp/parser": "^1.1",
                "amphp/pipeline": "^1",
                "amphp/serialization": "^1",
                "amphp/sync": "^2",
                "php": ">=8.1",
                "revolt/event-loop": "^1 || ^0.2.3"
            },
            "require-dev": {
                "amphp/php-cs-fixer-config": "^2",
                "amphp/phpunit-util": "^3",
                "phpunit/phpunit": "^9",
                "psalm/phar": "5.22.1"
            },
            "time": "2025-03-16T17:10:27+00:00",
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "files": [
                    "src/functions.php",
                    "src/Internal/functions.php"
                ],
                "psr-4": {
                    "Amp\\ByteStream\\": "src"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Aaron Piotrowski",
                    "email": "aaron@trowski.com"
                },
                {
                    "name": "Niklas Keller",
                    "email": "me@kelunik.com"
                }
            ],
            "description": "A stream abstraction to make working with non-blocking I/O simple.",
            "homepage": "https://amphp.org/byte-stream",
            "keywords": [
                "amp",
                "amphp",
                "async",
                "io",
                "non-blocking",
                "stream"
            ],
            "support": {
                "issues": "https://github.com/amphp/byte-stream/issues",
                "source": "https://github.com/amphp/byte-stream/tree/v2.1.2"
            },
            "funding": [
                {
                    "url": "https://github.com/amphp",
                    "type": "github"
                }
            ],
            "install-path": "../amphp/byte-stream"
        },
        {
            "name": "amphp/cache",
            "version": "v2.0.1",
            "version_normalized": "2.0.1.0",
            "source": {
                "type": "git",
                "url": "https://github.com/amphp/cache.git",
                "reference": "46912e387e6aa94933b61ea1ead9cf7540b7797c"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/amphp/cache/zipball/46912e387e6aa94933b61ea1ead9cf7540b7797c",
                "reference": "46912e387e6aa94933b61ea1ead9cf7540b7797c",
                "shasum": ""
            },
            "require": {
                "amphp/amp": "^3",
                "amphp/serialization": "^1",
                "amphp/sync": "^2",
                "php": ">=8.1",
                "revolt/event-loop": "^1 || ^0.2"
            },
            "require-dev": {
                "amphp/php-cs-fixer-config": "^2",
                "amphp/phpunit-util": "^3",
                "phpunit/phpunit": "^9",
                "psalm/phar": "^5.4"
            },
            "time": "2024-04-19T03:38:06+00:00",
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "Amp\\Cache\\": "src"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Niklas Keller",
                    "email": "me@kelunik.com"
                },
                {
                    "name": "Aaron Piotrowski",
                    "email": "aaron@trowski.com"
                },
                {
                    "name": "Daniel Lowrey",
                    "email": "rdlowrey@php.net"
                }
            ],
            "description": "A fiber-aware cache API based on Amp and Revolt.",
            "homepage": "https://amphp.org/cache",
            "support": {
                "issues": "https://github.com/amphp/cache/issues",
                "source": "https://github.com/amphp/cache/tree/v2.0.1"
            },
            "funding": [
                {
                    "url": "https://github.com/amphp",
                    "type": "github"
                }
            ],
            "install-path": "../amphp/cache"
        },
        {
            "name": "amphp/dns",
            "version": "v2.4.1",
            "version_normalized": "2.4.1.0",
            "source": {
                "type": "git",
                "url": "https://github.com/amphp/dns.git",
                "reference": "ec5948bfafac808f410406e18bc7b52a2d4629b7"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/amphp/dns/zipball/ec5948bfafac808f410406e18bc7b52a2d4629b7",
                "reference": "ec5948bfafac808f410406e18bc7b52a2d4629b7",
                "shasum": ""
            },
            "require": {
                "amphp/amp": "^3",
                "amphp/byte-stream": "^2",
                "amphp/cache": "^2",
                "amphp/parser": "^1",
                "amphp/process": "^2",
                "daverandom/libdns": "^2.0.2",
                "ext-filter": "*",
                "ext-json": "*",
                "php": ">=8.1",
                "revolt/event-loop": "^1 || ^0.2"
            },
            "require-dev": {
                "amphp/php-cs-fixer-config": "^2",
                "amphp/phpunit-util": "^3",
                "phpunit/phpunit": "^9",
                "psalm/phar": "6.16.1"
            },
            "time": "2026-07-26T17:53:54+00:00",
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "files": [
                    "src/functions.php"
                ],
                "psr-4": {
                    "Amp\\Dns\\": "src"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Chris Wright",
                    "email": "addr@daverandom.com"
                },
                {
                    "name": "Daniel Lowrey",
                    "email": "rdlowrey@php.net"
                },
                {
                    "name": "Bob Weinand",
                    "email": "bobwei9@hotmail.com"
                },
                {
                    "name": "Niklas Keller",
                    "email": "me@kelunik.com"
                },
                {
                    "name": "Aaron Piotrowski",
                    "email": "aaron@trowski.com"
                }
            ],
            "description": "Async DNS resolution for Amp.",
            "homepage": "https://github.com/amphp/dns",
            "keywords": [
                "amp",
                "amphp",
                "async",
                "client",
                "dns",
                "resolve"
            ],
            "support": {
                "issues": "https://github.com/amphp/dns/issues",
                "source": "https://github.com/amphp/dns/tree/v2.4.1"
            },
            "funding": [
                {
                    "url": "https://github.com/amphp",
                    "type": "github"
                }
            ],
            "install-path": "../amphp/dns"
        },
        {
            "name": "amphp/file",
            "version": "v3.2.0",
            "version_normalized": "3.2.0.0",
            "source": {
                "type": "git",
                "url": "https://github.com/amphp/file.git",
                "reference": "28b38a805d2c235bb581d24415e78a42cd03aedc"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/amphp/file/zipball/28b38a805d2c235bb581d24415e78a42cd03aedc",
                "reference": "28b38a805d2c235bb581d24415e78a42cd03aedc",
                "shasum": ""
            },
            "require": {
                "amphp/amp": "^3",
                "amphp/byte-stream": "^2",
                "amphp/cache": "^2",
                "amphp/parallel": "^2.3",
                "amphp/sync": "^2",
                "php": ">=8.1",
                "revolt/event-loop": "^1"
            },
            "require-dev": {
                "amphp/php-cs-fixer-config": "^2",
                "amphp/phpunit-util": "^3",
                "phpunit/phpunit": "^9",
                "psalm/phar": "5.22.2"
            },
            "suggest": {
                "ext-eio": "^2 || ^3",
                "ext-uv": "^0.3 || ^0.2"
            },
            "time": "2024-12-06T23:39:02+00:00",
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "files": [
                    "src/functions.php"
                ],
                "psr-4": {
                    "Amp\\File\\": "src"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Daniel Lowrey",
                    "email": "rdlowrey@php.net"
                },
                {
                    "name": "Aaron Piotrowski",
                    "email": "aaron@trowski.com"
                },
                {
                    "name": "Niklas Keller",
                    "email": "me@kelunik.com"
                }
            ],
            "description": "Non-blocking access to the filesystem based on Amp and Revolt.",
            "homepage": "https://github.com/amphp/file",
            "keywords": [
                "amp",
                "amphp",
                "async",
                "disk",
                "file",
                "filesystem",
                "io",
                "non-blocking",
                "static"
            ],
            "support": {
                "issues": "https://github.com/amphp/file/issues",
                "source": "https://github.com/amphp/file/tree/v3.2.0"
            },
            "funding": [
                {
                    "url": "https://github.com/amphp",
                    "type": "github"
                }
            ],
            "install-path": "../amphp/file"
        },
        {
            "name": "amphp/hpack",
            "version": "v3.2.2",
            "version_normalized": "3.2.2.0",
            "source": {
                "type": "git",
                "url": "https://github.com/amphp/hpack.git",
                "reference": "291da27078e7e149a9bad4d08ff05bf7d81c89f4"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/amphp/hpack/zipball/291da27078e7e149a9bad4d08ff05bf7d81c89f4",
                "reference": "291da27078e7e149a9bad4d08ff05bf7d81c89f4",
                "shasum": ""
            },
            "require": {
                "php": ">=7.1"
            },
            "require-dev": {
                "amphp/php-cs-fixer-config": "^2",
                "http2jp/hpack-test-case": "^1",
                "nikic/php-fuzzer": "^0.0.11",
                "phpunit/phpunit": "^7 | ^8 | ^9"
            },
            "time": "2026-05-03T19:28:59+00:00",
            "type": "library",
            "extra": {
                "branch-alias": {
                    "dev-master": "3.x-dev"
                }
            },
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "Amp\\Http\\": "src"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Daniel Lowrey",
                    "email": "rdlowrey@php.net"
                },
                {
                    "name": "Bob Weinand"
                },
                {
                    "name": "Niklas Keller",
                    "email": "me@kelunik.com"
                },
                {
                    "name": "Aaron Piotrowski",
                    "email": "aaron@trowski.com"
                }
            ],
            "description": "HTTP/2 HPack implementation.",
            "homepage": "https://github.com/amphp/hpack",
            "keywords": [
                "headers",
                "hpack",
                "http-2"
            ],
            "support": {
                "issues": "https://github.com/amphp/hpack/issues",
                "source": "https://github.com/amphp/hpack/tree/v3.2.2"
            },
            "funding": [
                {
                    "url": "https://github.com/amphp",
                    "type": "github"
                }
            ],
            "install-path": "../amphp/hpack"
        },
        {
            "name": "amphp/http",
            "version": "v2.1.2",
            "version_normalized": "2.1.2.0",
            "source": {
                "type": "git",
                "url": "https://github.com/amphp/http.git",
                "reference": "3680d80bd38b5d6f3c2cef2214ca6dd6cef26588"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/amphp/http/zipball/3680d80bd38b5d6f3c2cef2214ca6dd6cef26588",
                "reference": "3680d80bd38b5d6f3c2cef2214ca6dd6cef26588",
                "shasum": ""
            },
            "require": {
                "amphp/hpack": "^3",
                "amphp/parser": "^1.1",
                "league/uri-components": "^2.4.2 | ^7.1",
                "php": ">=8.1",
                "psr/http-message": "^1 | ^2"
            },
            "require-dev": {
                "amphp/php-cs-fixer-config": "^2",
                "league/uri": "^6.8 | ^7.1",
                "phpunit/phpunit": "^9",
                "psalm/phar": "^5.26.1"
            },
            "time": "2024-11-23T14:57:26+00:00",
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "files": [
                    "src/functions.php",
                    "src/Internal/constants.php"
                ],
                "psr-4": {
                    "Amp\\Http\\": "src"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Niklas Keller",
                    "email": "me@kelunik.com"
                },
                {
                    "name": "Aaron Piotrowski",
                    "email": "aaron@trowski.com"
                }
            ],
            "description": "Basic HTTP primitives which can be shared by servers and clients.",
            "support": {
                "issues": "https://github.com/amphp/http/issues",
                "source": "https://github.com/amphp/http/tree/v2.1.2"
            },
            "funding": [
                {
                    "url": "https://github.com/amphp",
                    "type": "github"
                }
            ],
            "install-path": "../amphp/http"
        },
        {
            "name": "amphp/http-client",
            "version": "v5.3.6",
            "version_normalized": "5.3.6.0",
            "source": {
                "type": "git",
                "url": "https://github.com/amphp/http-client.git",
                "reference": "ca155026acafa74a612d776a97202d53077fee86"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/amphp/http-client/zipball/ca155026acafa74a612d776a97202d53077fee86",
                "reference": "ca155026acafa74a612d776a97202d53077fee86",
                "shasum": ""
            },
            "require": {
                "amphp/amp": "^3",
                "amphp/byte-stream": "^2",
                "amphp/hpack": "^3",
                "amphp/http": "^2",
                "amphp/pipeline": "^1",
                "amphp/socket": "^2",
                "amphp/sync": "^2",
                "league/uri": "^7",
                "league/uri-components": "^7",
                "league/uri-interfaces": "^7.1",
                "php": ">=8.1",
                "psr/http-message": "^1 | ^2",
                "revolt/event-loop": "^1"
            },
            "conflict": {
                "amphp/file": "<3 | >=5"
            },
            "require-dev": {
                "amphp/file": "^3 | ^4",
                "amphp/http-server": "^3",
                "amphp/php-cs-fixer-config": "^2",
                "amphp/phpunit-util": "^3",
                "ext-json": "*",
                "kelunik/link-header-rfc5988": "^1",
                "phpunit/phpunit": "^9",
                "psalm/phar": "6.16.1"
            },
            "suggest": {
                "amphp/file": "Required for file request bodies and HTTP archive logging",
                "ext-json": "Required for logging HTTP archives",
                "ext-zlib": "Allows using compression for response bodies."
            },
            "time": "2026-05-15T23:29:38+00:00",
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "files": [
                    "src/functions.php",
                    "src/Internal/functions.php"
                ],
                "psr-4": {
                    "Amp\\Http\\Client\\": "src"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Daniel Lowrey",
                    "email": "rdlowrey@gmail.com"
                },
                {
                    "name": "Niklas Keller",
                    "email": "me@kelunik.com"
                },
                {
                    "name": "Aaron Piotrowski",
                    "email": "aaron@trowski.com"
                }
            ],
            "description": "An advanced async HTTP client library for PHP, enabling efficient, non-blocking, and concurrent requests and responses.",
            "homepage": "https://amphp.org/http-client",
            "keywords": [
                "async",
                "client",
                "concurrent",
                "http",
                "non-blocking",
                "rest"
            ],
            "support": {
                "issues": "https://github.com/amphp/http-client/issues",
                "source": "https://github.com/amphp/http-client/tree/v5.3.6"
            },
            "funding": [
                {
                    "url": "https://github.com/amphp",
                    "type": "github"
                }
            ],
            "install-path": "../amphp/http-client"
        },
        {
            "name": "amphp/http-client-cookies",
            "version": "v2.0.0",
            "version_normalized": "2.0.0.0",
            "source": {
                "type": "git",
                "url": "https://github.com/amphp/http-client-cookies.git",
                "reference": "4124b37ffa4d15034e70d5d30e6fb9605ed6c1a7"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/amphp/http-client-cookies/zipball/4124b37ffa4d15034e70d5d30e6fb9605ed6c1a7",
                "reference": "4124b37ffa4d15034e70d5d30e6fb9605ed6c1a7",
                "shasum": ""
            },
            "require": {
                "amphp/amp": "^3",
                "amphp/dns": "^2",
                "amphp/http": "^2",
                "amphp/http-client": "^5",
                "amphp/sync": "^2",
                "ext-filter": "*",
                "php": ">=8.1",
                "psr/http-message": "^1|^2"
            },
            "conflict": {
                "amphp/file": "<3 || >=4"
            },
            "require-dev": {
                "amphp/file": "^3",
                "amphp/http-server": "^3",
                "amphp/php-cs-fixer-config": "^2",
                "amphp/phpunit-util": "^3",
                "amphp/socket": "^2",
                "phpunit/phpunit": "^9",
                "psalm/phar": "^5.6"
            },
            "time": "2023-10-08T17:29:36+00:00",
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "Amp\\Http\\Client\\Cookie\\": "src"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Daniel Lowrey",
                    "email": "rdlowrey@gmail.com"
                },
                {
                    "name": "Niklas Keller",
                    "email": "me@kelunik.com"
                }
            ],
            "description": "Automatic cookie handling for Amp's HTTP client.",
            "homepage": "https://github.com/amphp/http-client-cookies",
            "keywords": [
                "async",
                "client",
                "cookie",
                "cookies",
                "http"
            ],
            "support": {
                "issues": "https://github.com/amphp/http-client-cookies/issues",
                "source": "https://github.com/amphp/http-client-cookies/tree/v2.0.0"
            },
            "funding": [
                {
                    "url": "https://github.com/amphp",
                    "type": "github"
                }
            ],
            "install-path": "../amphp/http-client-cookies"
        },
        {
            "name": "amphp/http-server",
            "version": "v3.4.7",
            "version_normalized": "3.4.7.0",
            "source": {
                "type": "git",
                "url": "https://github.com/amphp/http-server.git",
                "reference": "aa96ca38c5f822a827236543d5417d0c2a782ef0"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/amphp/http-server/zipball/aa96ca38c5f822a827236543d5417d0c2a782ef0",
                "reference": "aa96ca38c5f822a827236543d5417d0c2a782ef0",
                "shasum": ""
            },
            "require": {
                "amphp/amp": "^3",
                "amphp/byte-stream": "^2",
                "amphp/cache": "^2",
                "amphp/hpack": "^3",
                "amphp/http": "^2",
                "amphp/pipeline": "^1",
                "amphp/socket": "^2.1",
                "amphp/sync": "^2.2",
                "league/uri": "^7.1",
                "league/uri-interfaces": "^7.1",
                "php": ">=8.1",
                "psr/http-message": "^1 | ^2",
                "psr/log": "^1 | ^2 | ^3",
                "revolt/event-loop": "^1"
            },
            "require-dev": {
                "amphp/http-client": "^5",
                "amphp/log": "^2",
                "amphp/php-cs-fixer-config": "^2",
                "amphp/phpunit-util": "^3",
                "league/uri-components": "^7.1",
                "monolog/monolog": "^3",
                "phpunit/phpunit": "^9",
                "psalm/phar": "6.16.1"
            },
            "suggest": {
                "ext-zlib": "Allows GZip compression of response bodies"
            },
            "time": "2026-10-04T15:14:17+00:00",
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "files": [
                    "src/Driver/functions.php",
                    "src/Middleware/functions.php",
                    "src/functions.php"
                ],
                "psr-4": {
                    "Amp\\Http\\Server\\": "src"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Daniel Lowrey",
                    "email": "rdlowrey@php.net"
                },
                {
                    "name": "Bob Weinand"
                },
                {
                    "name": "Niklas Keller",
                    "email": "me@kelunik.com"
                },
                {
                    "name": "Aaron Piotrowski",
                    "email": "aaron@trowski.com"
                }
            ],
            "description": "A non-blocking HTTP application server for PHP based on Amp.",
            "homepage": "https://github.com/amphp/http-server",
            "keywords": [
                "amp",
                "amphp",
                "async",
                "http",
                "non-blocking",
                "server"
            ],
            "support": {
                "issues": "https://github.com/amphp/http-server/issues",
                "source": "https://github.com/amphp/http-server/tree/v3.4.7"
            },
            "funding": [
                {
                    "url": "https://github.com/amphp",
                    "type": "github"
                }
            ],
            "install-path": "../amphp/http-server"
        },
        {
            "name": "amphp/log",
            "version": "v2.0.0",
            "version_normalized": "2.0.0.0",
            "source": {
                "type": "git",
                "url": "https://github.com/amphp/log.git",
                "reference": "bf1562b8a18a3f30efa069ed740f412ac70a8a6c"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/amphp/log/zipball/bf1562b8a18a3f30efa069ed740f412ac70a8a6c",
                "reference": "bf1562b8a18a3f30efa069ed740f412ac70a8a6c",
                "shasum": ""
            },
            "require": {
                "amphp/amp": "^3",
                "amphp/byte-stream": "^2",
                "monolog/monolog": "^3|^2|^1.23",
                "php": ">=8.1",
                "psr/log": "^3|^2|^1"
            },
            "require-dev": {
                "amphp/file": "^3",
                "amphp/php-cs-fixer-config": "^2",
                "amphp/phpunit-util": "^3",
                "phpunit/phpunit": "^9",
                "psalm/phar": "^5.6"
            },
            "time": "2023-08-05T18:59:54+00:00",
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "files": [
                    "src/functions.php"
                ],
                "psr-4": {
                    "Amp\\Log\\": "src"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Aaron Piotrowski",
                    "email": "aaron@trowski.com"
                },
                {
                    "name": "Niklas Keller",
                    "email": "me@kelunik.com"
                }
            ],
            "description": "Non-blocking logging for PHP based on Amp, Revolt, and Monolog.",
            "homepage": "https://github.com/amphp/log",
            "keywords": [
                "amp",
                "amphp",
                "async",
                "log",
                "logger",
                "logging",
                "non-blocking"
            ],
            "support": {
                "issues": "https://github.com/amphp/log/issues",
                "source": "https://github.com/amphp/log/tree/v2.0.0"
            },
            "funding": [
                {
                    "url": "https://github.com/amphp",
                    "type": "github"
                }
            ],
            "install-path": "../amphp/log"
        },
        {
            "name": "amphp/mysql",
            "version": "v3.1.1",
            "version_normalized": "3.1.1.0",
            "source": {
                "type": "git",
                "url": "https://github.com/amphp/mysql.git",
                "reference": "ea3a5e07dc3dddc267669337ded2a98f02d86223"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/amphp/mysql/zipball/ea3a5e07dc3dddc267669337ded2a98f02d86223",
                "reference": "ea3a5e07dc3dddc267669337ded2a98f02d86223",
                "shasum": ""
            },
            "require": {
                "amphp/amp": "^3",
                "amphp/parser": "^1.1",
                "amphp/pipeline": "^1",
                "amphp/socket": "^2.2",
                "amphp/sql": "^2",
                "amphp/sql-common": "^2",
                "php": ">=8.1"
            },
            "require-dev": {
                "amphp/file": "^3 | ^4",
                "amphp/php-cs-fixer-config": "^2",
                "amphp/phpunit-util": "^3",
                "amphp/process": "^2",
                "ext-mysqli": "*",
                "ext-openssl": "*",
                "phpbench/phpbench": "^1.2.6",
                "phpunit/phpunit": "^9",
                "psalm/phar": "6.16.1"
            },
            "time": "2026-05-16T19:09:29+00:00",
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "files": [
                    "src/functions.php"
                ],
                "psr-4": {
                    "Amp\\Mysql\\": "src"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Bob Weinand",
                    "email": "bobwei9@hotmail.com"
                },
                {
                    "name": "Aaron Piotrowski",
                    "email": "aaron@trowski.com"
                }
            ],
            "description": "Asynchronous MySQL client for PHP based on Amp.",
            "support": {
                "issues": "https://github.com/amphp/mysql/issues",
                "source": "https://github.com/amphp/mysql/tree/v3.1.1"
            },
            "funding": [
                {
                    "url": "https://github.com/amphp",
                    "type": "github"
                }
            ],
            "install-path": "../amphp/mysql"
        },
        {
            "name": "amphp/parallel",
            "version": "v2.4.0",
            "version_normalized": "2.4.0.0",
            "source": {
                "type": "git",
                "url": "https://github.com/amphp/parallel.git",
                "reference": "37f5b2754fadc229c00f9416bd68fb8d04529a81"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/amphp/parallel/zipball/37f5b2754fadc229c00f9416bd68fb8d04529a81",
                "reference": "37f5b2754fadc229c00f9416bd68fb8d04529a81",
                "shasum": ""
            },
            "require": {
                "amphp/amp": "^3",
                "amphp/byte-stream": "^2",
                "amphp/cache": "^2",
                "amphp/parser": "^1",
                "amphp/pipeline": "^1",
                "amphp/process": "^2",
                "amphp/serialization": "^1",
                "amphp/socket": "^2",
                "amphp/sync": "^2",
                "php": ">=8.1",
                "revolt/event-loop": "^1"
            },
            "require-dev": {
                "amphp/php-cs-fixer-config": "^2",
                "amphp/phpunit-util": "^3",
                "phpunit/phpunit": "^9",
                "psalm/phar": "6.16.1"
            },
            "time": "2026-05-16T16:54:01+00:00",
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "files": [
                    "src/Context/functions.php",
                    "src/Context/Internal/functions.php",
                    "src/Ipc/functions.php",
                    "src/Worker/functions.php"
                ],
                "psr-4": {
                    "Amp\\Parallel\\": "src"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Aaron Piotrowski",
                    "email": "aaron@trowski.com"
                },
                {
                    "name": "Niklas Keller",
                    "email": "me@kelunik.com"
                },
                {
                    "name": "Stephen Coakley",
                    "email": "me@stephencoakley.com"
                }
            ],
            "description": "Parallel processing component for Amp.",
            "homepage": "https://github.com/amphp/parallel",
            "keywords": [
                "async",
                "asynchronous",
                "concurrent",
                "multi-processing",
                "multi-threading"
            ],
            "support": {
                "issues": "https://github.com/amphp/parallel/issues",
                "source": "https://github.com/amphp/parallel/tree/v2.4.0"
            },
            "funding": [
                {
                    "url": "https://github.com/amphp",
                    "type": "github"
                }
            ],
            "install-path": "../amphp/parallel"
        },
        {
            "name": "amphp/parser",
            "version": "v1.1.1",
            "version_normalized": "1.1.1.0",
            "source": {
                "type": "git",
                "url": "https://github.com/amphp/parser.git",
                "reference": "3cf1f8b32a0171d4b1bed93d25617637a77cded7"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/amphp/parser/zipball/3cf1f8b32a0171d4b1bed93d25617637a77cded7",
                "reference": "3cf1f8b32a0171d4b1bed93d25617637a77cded7",
                "shasum": ""
            },
            "require": {
                "php": ">=7.4"
            },
            "require-dev": {
                "amphp/php-cs-fixer-config": "^2",
                "phpunit/phpunit": "^9",
                "psalm/phar": "^5.4"
            },
            "time": "2024-03-21T19:16:53+00:00",
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "Amp\\Parser\\": "src"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Aaron Piotrowski",
                    "email": "aaron@trowski.com"
                },
                {
                    "name": "Niklas Keller",
                    "email": "me@kelunik.com"
                }
            ],
            "description": "A generator parser to make streaming parsers simple.",
            "homepage": "https://github.com/amphp/parser",
            "keywords": [
                "async",
                "non-blocking",
                "parser",
                "stream"
            ],
            "support": {
                "issues": "https://github.com/amphp/parser/issues",
                "source": "https://github.com/amphp/parser/tree/v1.1.1"
            },
            "funding": [
                {
                    "url": "https://github.com/amphp",
                    "type": "github"
                }
            ],
            "install-path": "../amphp/parser"
        },
        {
            "name": "amphp/pipeline",
            "version": "v1.2.7",
            "version_normalized": "1.2.7.0",
            "source": {
                "type": "git",
                "url": "https://github.com/amphp/pipeline.git",
                "reference": "cf2d67696c2015ea7c7fd8f6ec5f8ed7c2d17c17"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/amphp/pipeline/zipball/cf2d67696c2015ea7c7fd8f6ec5f8ed7c2d17c17",
                "reference": "cf2d67696c2015ea7c7fd8f6ec5f8ed7c2d17c17",
                "shasum": ""
            },
            "require": {
                "amphp/amp": "^3",
                "php": ">=8.1",
                "revolt/event-loop": "^1"
            },
            "require-dev": {
                "amphp/php-cs-fixer-config": "^2",
                "amphp/phpunit-util": "^3",
                "phpunit/phpunit": "^9",
                "psalm/phar": "6.16.1"
            },
            "time": "2026-07-26T14:50:43+00:00",
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "Amp\\Pipeline\\": "src"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Aaron Piotrowski",
                    "email": "aaron@trowski.com"
                },
                {
                    "name": "Niklas Keller",
                    "email": "me@kelunik.com"
                }
            ],
            "description": "Asynchronous iterators and operators.",
            "homepage": "https://amphp.org/pipeline",
            "keywords": [
                "amp",
                "amphp",
                "async",
                "io",
                "iterator",
                "non-blocking"
            ],
            "support": {
                "issues": "https://github.com/amphp/pipeline/issues",
                "source": "https://github.com/amphp/pipeline/tree/v1.2.7"
            },
            "funding": [
                {
                    "url": "https://github.com/amphp",
                    "type": "github"
                }
            ],
            "install-path": "../amphp/pipeline"
        },
        {
            "name": "amphp/postgres",
            "version": "v2.2.1",
            "version_normalized": "2.2.1.0",
            "source": {
                "type": "git",
                "url": "https://github.com/amphp/postgres.git",
                "reference": "b68c4d5929d0ec1701781dbbb0bb81dd8d0a42d7"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/amphp/postgres/zipball/b68c4d5929d0ec1701781dbbb0bb81dd8d0a42d7",
                "reference": "b68c4d5929d0ec1701781dbbb0bb81dd8d0a42d7",
                "shasum": ""
            },
            "require": {
                "amphp/amp": "^3",
                "amphp/pipeline": "^1",
                "amphp/sql": "^2",
                "amphp/sql-common": "^2",
                "php": ">=8.1"
            },
            "require-dev": {
                "amphp/php-cs-fixer-config": "^2",
                "amphp/phpunit-util": "^3",
                "ext-pgsql": "*",
                "ext-pq": "*",
                "phpunit/phpunit": "^9",
                "psalm/phar": "6.15.1"
            },
            "time": "2026-02-25T04:47:18+00:00",
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "files": [
                    "src/functions.php",
                    "src/Internal/functions.php"
                ],
                "psr-4": {
                    "Amp\\Postgres\\": "src"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Aaron Piotrowski",
                    "email": "aaron@trowski.com"
                }
            ],
            "description": "Asynchronous PostgreSQL client for Amp.",
            "homepage": "https://amphp.org",
            "keywords": [
                "async",
                "asynchronous",
                "database",
                "db",
                "pgsql",
                "postgre",
                "postgresql"
            ],
            "support": {
                "issues": "https://github.com/amphp/postgres/issues",
                "source": "https://github.com/amphp/postgres/tree/v2.2.1"
            },
            "funding": [
                {
                    "url": "https://github.com/amphp",
                    "type": "github"
                }
            ],
            "install-path": "../amphp/postgres"
        },
        {
            "name": "amphp/process",
            "version": "v2.1.0",
            "version_normalized": "2.1.0.0",
            "source": {
                "type": "git",
                "url": "https://github.com/amphp/process.git",
                "reference": "583959df17d00304ad7b0b32285373f985935643"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/amphp/process/zipball/583959df17d00304ad7b0b32285373f985935643",
                "reference": "583959df17d00304ad7b0b32285373f985935643",
                "shasum": ""
            },
            "require": {
                "amphp/amp": "^3",
                "amphp/byte-stream": "^2",
                "amphp/sync": "^2",
                "php": ">=8.1",
                "revolt/event-loop": "^1 || ^0.2"
            },
            "require-dev": {
                "amphp/php-cs-fixer-config": "^2",
                "amphp/phpunit-util": "^3",
                "phpunit/phpunit": "^9",
                "psalm/phar": "6.16.1"
            },
            "time": "2026-05-31T15:11:55+00:00",
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "files": [
                    "src/functions.php"
                ],
                "psr-4": {
                    "Amp\\Process\\": "src"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Bob Weinand",
                    "email": "bobwei9@hotmail.com"
                },
                {
                    "name": "Aaron Piotrowski",
                    "email": "aaron@trowski.com"
                },
                {
                    "name": "Niklas Keller",
                    "email": "me@kelunik.com"
                }
            ],
            "description": "A fiber-aware process manager based on Amp and Revolt.",
            "homepage": "https://amphp.org/process",
            "support": {
                "issues": "https://github.com/amphp/process/issues",
                "source": "https://github.com/amphp/process/tree/v2.1.0"
            },
            "funding": [
                {
                    "url": "https://github.com/amphp",
                    "type": "github"
                }
            ],
            "install-path": "../amphp/process"
        },
        {
            "name": "amphp/redis",
            "version": "v2.0.4",
            "version_normalized": "2.0.4.0",
            "source": {
                "type": "git",
                "url": "https://github.com/amphp/redis.git",
                "reference": "964bcf6c2574645058371925a3668240a622bdab"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/amphp/redis/zipball/964bcf6c2574645058371925a3668240a622bdab",
                "reference": "964bcf6c2574645058371925a3668240a622bdab",
                "shasum": ""
            },
            "require": {
                "amphp/amp": "^3",
                "amphp/byte-stream": "^2",
                "amphp/cache": "^2",
                "amphp/parser": "^1",
                "amphp/pipeline": "^1",
                "amphp/serialization": "^1",
                "amphp/socket": "^2",
                "amphp/sync": "^2",
                "league/uri": "^7",
                "php": ">=8.1",
                "psr/log": "^1|^2|^3",
                "revolt/event-loop": "^1"
            },
            "require-dev": {
                "amphp/php-cs-fixer-config": "^2",
                "amphp/phpunit-util": "^3",
                "amphp/process": "^2",
                "phpunit/phpunit": "^9",
                "psalm/phar": "5.22"
            },
            "time": "2026-03-03T20:52:26+00:00",
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "files": [
                    "src/functions.php",
                    "src/Internal/functions.php"
                ],
                "psr-4": {
                    "Amp\\Redis\\": "src"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Niklas Keller",
                    "email": "me@kelunik.com"
                },
                {
                    "name": "Aaron Piotrowski",
                    "email": "aaron@trowski.com"
                }
            ],
            "description": "Efficient asynchronous communication with Redis servers, enabling scalable and responsive data storage and retrieval.",
            "homepage": "https://amphp.org/redis",
            "keywords": [
                "amp",
                "amphp",
                "async",
                "client",
                "redis",
                "revolt"
            ],
            "support": {
                "issues": "https://github.com/amphp/redis/issues",
                "source": "https://github.com/amphp/redis/tree/v2.0.4"
            },
            "funding": [
                {
                    "url": "https://github.com/amphp",
                    "type": "github"
                }
            ],
            "install-path": "../amphp/redis"
        },
        {
            "name": "amphp/serialization",
            "version": "v1.1.0",
            "version_normalized": "1.1.0.0",
            "source": {
                "type": "git",
                "url": "https://github.com/amphp/serialization.git",
                "reference": "fdf2834d78cebb0205fb2672676c1b1eb84371f0"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/amphp/serialization/zipball/fdf2834d78cebb0205fb2672676c1b1eb84371f0",
                "reference": "fdf2834d78cebb0205fb2672676c1b1eb84371f0",
                "shasum": ""
            },
            "require": {
                "php": ">=7.4"
            },
            "require-dev": {
                "amphp/php-cs-fixer-config": "^2",
                "ext-json": "*",
                "ext-zlib": "*",
                "phpunit/phpunit": "^9",
                "psalm/phar": "6.16.1"
            },
            "time": "2026-04-05T15:59:53+00:00",
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "files": [
                    "src/functions.php"
                ],
                "psr-4": {
                    "Amp\\Serialization\\": "src"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Aaron Piotrowski",
                    "email": "aaron@trowski.com"
                },
                {
                    "name": "Niklas Keller",
                    "email": "me@kelunik.com"
                }
            ],
            "description": "Serialization tools for IPC and data storage in PHP.",
            "homepage": "https://github.com/amphp/serialization",
            "keywords": [
                "async",
                "asynchronous",
                "serialization",
                "serialize"
            ],
            "support": {
                "issues": "https://github.com/amphp/serialization/issues",
                "source": "https://github.com/amphp/serialization/tree/v1.1.0"
            },
            "funding": [
                {
                    "url": "https://github.com/amphp",
                    "type": "github"
                }
            ],
            "install-path": "../amphp/serialization"
        },
        {
            "name": "amphp/socket",
            "version": "v2.4.1",
            "version_normalized": "2.4.1.0",
            "source": {
                "type": "git",
                "url": "https://github.com/amphp/socket.git",
                "reference": "b347be5aff6b2cc025208bb4d896607eb470c018"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/amphp/socket/zipball/b347be5aff6b2cc025208bb4d896607eb470c018",
                "reference": "b347be5aff6b2cc025208bb4d896607eb470c018",
                "shasum": ""
            },
            "require": {
                "amphp/amp": "^3",
                "amphp/byte-stream": "^2",
                "amphp/dns": "^2",
                "ext-openssl": "*",
                "kelunik/certificate": "^1.1",
                "league/uri": "^7",
                "league/uri-interfaces": "^7",
                "php": ">=8.1",
                "revolt/event-loop": "^1"
            },
            "require-dev": {
                "amphp/php-cs-fixer-config": "^2",
                "amphp/phpunit-util": "^3",
                "amphp/process": "^2",
                "phpunit/phpunit": "^9",
                "psalm/phar": "6.16.1"
            },
            "time": "2026-08-22T18:13:20+00:00",
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "files": [
                    "src/functions.php",
                    "src/Internal/functions.php",
                    "src/SocketAddress/functions.php"
                ],
                "psr-4": {
                    "Amp\\Socket\\": "src"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Daniel Lowrey",
                    "email": "rdlowrey@gmail.com"
                },
                {
                    "name": "Aaron Piotrowski",
                    "email": "aaron@trowski.com"
                },
                {
                    "name": "Niklas Keller",
                    "email": "me@kelunik.com"
                }
            ],
            "description": "Non-blocking socket connection / server implementations based on Amp and Revolt.",
            "homepage": "https://github.com/amphp/socket",
            "keywords": [
                "amp",
                "async",
                "encryption",
                "non-blocking",
                "sockets",
                "tcp",
                "tls"
            ],
            "support": {
                "issues": "https://github.com/amphp/socket/issues",
                "source": "https://github.com/amphp/socket/tree/v2.4.1"
            },
            "funding": [
                {
                    "url": "https://github.com/amphp",
                    "type": "github"
                }
            ],
            "install-path": "../amphp/socket"
        },
        {
            "name": "amphp/sql",
            "version": "v2.1.1",
            "version_normalized": "2.1.1.0",
            "source": {
                "type": "git",
                "url": "https://github.com/amphp/sql.git",
                "reference": "258bafe5ecf8a0491d86681f2a2af1dee2933a69"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/amphp/sql/zipball/258bafe5ecf8a0491d86681f2a2af1dee2933a69",
                "reference": "258bafe5ecf8a0491d86681f2a2af1dee2933a69",
                "shasum": ""
            },
            "require": {
                "amphp/amp": "^3",
                "php": ">=8.1"
            },
            "require-dev": {
                "amphp/php-cs-fixer-config": "^2",
                "phpunit/phpunit": "^9",
                "psalm/phar": "6.15.1"
            },
            "time": "2026-02-25T04:44:15+00:00",
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "Amp\\Sql\\": "src"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "description": "Asynchronous SQL client for Amp.",
            "homepage": "https://amphp.org",
            "keywords": [
                "async",
                "asynchronous",
                "database",
                "db",
                "sql"
            ],
            "support": {
                "issues": "https://github.com/amphp/sql/issues",
                "source": "https://github.com/amphp/sql/tree/v2.1.1"
            },
            "funding": [
                {
                    "url": "https://github.com/amphp",
                    "type": "github"
                }
            ],
            "install-path": "../amphp/sql"
        },
        {
            "name": "amphp/sql-common",
            "version": "v2.0.4",
            "version_normalized": "2.0.4.0",
            "source": {
                "type": "git",
                "url": "https://github.com/amphp/sql-common.git",
                "reference": "735da17ef0a66e7139c9f7584af5c3f9827f83c0"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/amphp/sql-common/zipball/735da17ef0a66e7139c9f7584af5c3f9827f83c0",
                "reference": "735da17ef0a66e7139c9f7584af5c3f9827f83c0",
                "shasum": ""
            },
            "require": {
                "amphp/amp": "^3",
                "amphp/sql": "^2",
                "php": ">=8.1"
            },
            "require-dev": {
                "amphp/php-cs-fixer-config": "^2",
                "amphp/phpunit-util": "^3",
                "phpunit/phpunit": "^9",
                "psalm/phar": "5.23"
            },
            "time": "2025-12-11T20:05:29+00:00",
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "Amp\\Sql\\Common\\": "src"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "description": "Common classes for non-blocking SQL implementations.",
            "homepage": "https://amphp.org",
            "keywords": [
                "async",
                "asynchronous",
                "database",
                "db",
                "sql"
            ],
            "support": {
                "issues": "https://github.com/amphp/sql-common/issues",
                "source": "https://github.com/amphp/sql-common/tree/v2.0.4"
            },
            "funding": [
                {
                    "url": "https://github.com/amphp",
                    "type": "github"
                }
            ],
            "install-path": "../amphp/sql-common"
        },
        {
            "name": "amphp/sync",
            "version": "v2.3.0",
            "version_normalized": "2.3.0.0",
            "source": {
                "type": "git",
                "url": "https://github.com/amphp/sync.git",
                "reference": "217097b785130d77cfcc58ff583cf26cd1770bf1"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/amphp/sync/zipball/217097b785130d77cfcc58ff583cf26cd1770bf1",
                "reference": "217097b785130d77cfcc58ff583cf26cd1770bf1",
                "shasum": ""
            },
            "require": {
                "amphp/amp": "^3",
                "amphp/pipeline": "^1",
                "amphp/serialization": "^1",
                "php": ">=8.1",
                "revolt/event-loop": "^1 || ^0.2"
            },
            "require-dev": {
                "amphp/php-cs-fixer-config": "^2",
                "amphp/phpunit-util": "^3",
                "phpunit/phpunit": "^9",
                "psalm/phar": "5.23"
            },
            "time": "2024-08-03T19:31:26+00:00",
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "files": [
                    "src/functions.php"
                ],
                "psr-4": {
                    "Amp\\Sync\\": "src"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Aaron Piotrowski",
                    "email": "aaron@trowski.com"
                },
                {
                    "name": "Niklas Keller",
                    "email": "me@kelunik.com"
                },
                {
                    "name": "Stephen Coakley",
                    "email": "me@stephencoakley.com"
                }
            ],
            "description": "Non-blocking synchronization primitives for PHP based on Amp and Revolt.",
            "homepage": "https://github.com/amphp/sync",
            "keywords": [
                "async",
                "asynchronous",
                "mutex",
                "semaphore",
                "synchronization"
            ],
            "support": {
                "issues": "https://github.com/amphp/sync/issues",
                "source": "https://github.com/amphp/sync/tree/v2.3.0"
            },
            "funding": [
                {
                    "url": "https://github.com/amphp",
                    "type": "github"
                }
            ],
            "install-path": "../amphp/sync"
        },
        {
            "name": "amphp/websocket",
            "version": "v2.0.4",
            "version_normalized": "2.0.4.0",
            "source": {
                "type": "git",
                "url": "https://github.com/amphp/websocket.git",
                "reference": "963904b6a883c4b62d9222d1d9749814fac96a3b"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/amphp/websocket/zipball/963904b6a883c4b62d9222d1d9749814fac96a3b",
                "reference": "963904b6a883c4b62d9222d1d9749814fac96a3b",
                "shasum": ""
            },
            "require": {
                "amphp/amp": "^3",
                "amphp/byte-stream": "^2",
                "amphp/parser": "^1",
                "amphp/pipeline": "^1",
                "amphp/socket": "^2",
                "php": ">=8.1",
                "revolt/event-loop": "^1"
            },
            "require-dev": {
                "amphp/php-cs-fixer-config": "^2",
                "amphp/phpunit-util": "^3",
                "phpunit/phpunit": "^9",
                "psalm/phar": "^5.18"
            },
            "suggest": {
                "ext-zlib": "Required for compression"
            },
            "time": "2024-10-28T21:28:45+00:00",
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "files": [
                    "src/functions.php"
                ],
                "psr-4": {
                    "Amp\\Websocket\\": "src"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Aaron Piotrowski",
                    "email": "aaron@trowski.com"
                },
                {
                    "name": "Niklas Keller",
                    "email": "me@kelunik.com"
                },
                {
                    "name": "Bob Weinand",
                    "email": "bobwei9@hotmail.com"
                }
            ],
            "description": "Shared code for websocket servers and clients.",
            "homepage": "https://github.com/amphp/websocket",
            "keywords": [
                "amp",
                "amphp",
                "async",
                "http",
                "non-blocking",
                "websocket"
            ],
            "support": {
                "issues": "https://github.com/amphp/websocket/issues",
                "source": "https://github.com/amphp/websocket/tree/v2.0.4"
            },
            "funding": [
                {
                    "url": "https://github.com/amphp",
                    "type": "github"
                }
            ],
            "install-path": "../amphp/websocket"
        },
        {
            "name": "amphp/websocket-client",
            "version": "v2.0.2",
            "version_normalized": "2.0.2.0",
            "source": {
                "type": "git",
                "url": "https://github.com/amphp/websocket-client.git",
                "reference": "dc033fdce0af56295a23f63ac4f579b34d470d6c"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/amphp/websocket-client/zipball/dc033fdce0af56295a23f63ac4f579b34d470d6c",
                "reference": "dc033fdce0af56295a23f63ac4f579b34d470d6c",
                "shasum": ""
            },
            "require": {
                "amphp/amp": "^3",
                "amphp/byte-stream": "^2.1",
                "amphp/http": "^2.1",
                "amphp/http-client": "^5",
                "amphp/socket": "^2.2",
                "amphp/websocket": "^2",
                "league/uri": "^7.1",
                "php": ">=8.1",
                "psr/http-message": "^1|^2",
                "revolt/event-loop": "^1"
            },
            "require-dev": {
                "amphp/http-server": "^3",
                "amphp/php-cs-fixer-config": "^2",
                "amphp/phpunit-util": "^3",
                "amphp/websocket-server": "^3|^4",
                "phpunit/phpunit": "^9",
                "psalm/phar": "~5.26.1",
                "psr/log": "^1"
            },
            "time": "2025-08-24T17:25:34+00:00",
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "files": [
                    "src/functions.php"
                ],
                "psr-4": {
                    "Amp\\Websocket\\Client\\": "src"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Bob Weinand",
                    "email": "bobwei9@hotmail.com"
                },
                {
                    "name": "Aaron Piotrowski",
                    "email": "aaron@trowski.com"
                },
                {
                    "name": "Niklas Keller",
                    "email": "me@kelunik.com"
                }
            ],
            "description": "Async WebSocket client for PHP based on Amp.",
            "keywords": [
                "amp",
                "amphp",
                "async",
                "client",
                "http",
                "non-blocking",
                "websocket"
            ],
            "support": {
                "issues": "https://github.com/amphp/websocket-client/issues",
                "source": "https://github.com/amphp/websocket-client/tree/v2.0.2"
            },
            "funding": [
                {
                    "url": "https://github.com/amphp",
                    "type": "github"
                }
            ],
            "install-path": "../amphp/websocket-client"
        },
        {
            "name": "bacon/bacon-qr-code",
            "version": "v3.1.1",
            "version_normalized": "3.1.1.0",
            "source": {
                "type": "git",
                "url": "https://github.com/Bacon/BaconQrCode.git",
                "reference": "4da2233e72eeecd9be3b62e0dc2cc9ed8e2e31c2"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/4da2233e72eeecd9be3b62e0dc2cc9ed8e2e31c2",
                "reference": "4da2233e72eeecd9be3b62e0dc2cc9ed8e2e31c2",
                "shasum": ""
            },
            "require": {
                "dasprid/enum": "^1.0.3",
                "ext-iconv": "*",
                "php": "^8.1"
            },
            "require-dev": {
                "phly/keep-a-changelog": "^2.12",
                "phpunit/phpunit": "^10.5.11 || ^11.0.4",
                "spatie/phpunit-snapshot-assertions": "^5.1.5",
                "spatie/pixelmatch-php": "^1.2.0",
                "squizlabs/php_codesniffer": "^3.9"
            },
            "suggest": {
                "ext-imagick": "to generate QR code images"
            },
            "time": "2026-04-05T21:06:35+00:00",
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "BaconQrCode\\": "src/"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "BSD-2-Clause"
            ],
            "authors": [
                {
                    "name": "Ben Scholzen 'DASPRiD'",
                    "email": "mail@dasprids.de",
                    "homepage": "https://dasprids.de/",
                    "role": "Developer"
                }
            ],
            "description": "BaconQrCode is a QR code generator for PHP.",
            "homepage": "https://github.com/Bacon/BaconQrCode",
            "support": {
                "issues": "https://github.com/Bacon/BaconQrCode/issues",
                "source": "https://github.com/Bacon/BaconQrCode/tree/v3.1.1"
            },
            "install-path": "../bacon/bacon-qr-code"
        },
        {
            "name": "brick/math",
            "version": "1.0.0",
            "version_normalized": "1.0.0.0",
            "source": {
                "type": "git",
                "url": "https://github.com/brick/math.git",
                "reference": "2effe05d2177c451b86c6a073196a4034c02f211"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/brick/math/zipball/2effe05d2177c451b86c6a073196a4034c02f211",
                "reference": "2effe05d2177c451b86c6a073196a4034c02f211",
                "shasum": ""
            },
            "require": {
                "php": "^8.2"
            },
            "require-dev": {
                "phpstan/phpstan": "2.2.13",
                "phpstan/phpstan-phpunit": "2.0.18",
                "phpunit/phpunit": "^11.5"
            },
            "time": "2026-09-12T10:28:18+00:00",
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "Brick\\Math\\": "src/"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "description": "Arbitrary-precision arithmetic library",
            "keywords": [
                "Arbitrary-precision",
                "BigInteger",
                "BigRational",
                "arithmetic",
                "bigdecimal",
                "bignum",
                "bignumber",
                "brick",
                "decimal",
                "integer",
                "math",
                "mathematics",
                "rational"
            ],
            "support": {
                "issues": "https://github.com/brick/math/issues",
                "source": "https://github.com/brick/math/tree/1.0.0"
            },
            "funding": [
                {
                    "url": "https://github.com/BenMorel",
                    "type": "github"
                }
            ],
            "install-path": "../brick/math"
        },
        {
            "name": "danog/async-orm",
            "version": "1.1.7",
            "version_normalized": "1.1.7.0",
            "source": {
                "type": "git",
                "url": "https://github.com/danog/AsyncOrm.git",
                "reference": "5f721426349a970cbb43b0daf7947d58a3807d16"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/danog/AsyncOrm/zipball/5f721426349a970cbb43b0daf7947d58a3807d16",
                "reference": "5f721426349a970cbb43b0daf7947d58a3807d16",
                "shasum": ""
            },
            "require": {
                "amphp/mysql": "^3.0",
                "amphp/postgres": "^2.0",
                "amphp/redis": "^2.0",
                "amphp/sync": "^2.2",
                "php": ">=8.2.4",
                "revolt/event-loop": "^1.0.6",
                "symfony/polyfill-php83": "^1.32"
            },
            "require-dev": {
                "amphp/php-cs-fixer-config": "^2.0.1",
                "amphp/process": "^2.0.2",
                "danog/phpdoc": "^0.1.22",
                "friendsofphp/php-cs-fixer": "^3.52.1",
                "infection/infection": "^0.28.1",
                "phpunit/phpunit": "^11.0.9",
                "vimeo/psalm": "dev-master"
            },
            "time": "2026-10-24T12:19:22+00:00",
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "danog\\AsyncOrm\\": "src/"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "Apache-2.0"
            ],
            "authors": [
                {
                    "name": "Daniil Gentili",
                    "email": "daniil@daniil.it"
                },
                {
                    "name": "Alexander Pankratov",
                    "email": "alexander@i-c-a.su"
                }
            ],
            "description": "Async ORM based on AMPHP v3 and fibers.",
            "support": {
                "issues": "https://github.com/danog/AsyncOrm/issues",
                "source": "https://github.com/danog/AsyncOrm/tree/1.1.7"
            },
            "funding": [
                {
                    "url": "https://github.com/danog",
                    "type": "github"
                }
            ],
            "install-path": "../danog/async-orm"
        },
        {
            "name": "danog/better-prometheus",
            "version": "0.1.1",
            "version_normalized": "0.1.1.0",
            "source": {
                "type": "git",
                "url": "https://github.com/danog/better-prometheus.git",
                "reference": "74e19343be41905572290878ca9fd0ef08ba0c5e"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/danog/better-prometheus/zipball/74e19343be41905572290878ca9fd0ef08ba0c5e",
                "reference": "74e19343be41905572290878ca9fd0ef08ba0c5e",
                "shasum": ""
            },
            "require": {
                "php": ">=8.1",
                "promphp/prometheus_client_php": "^2.10"
            },
            "require-dev": {
                "amphp/php-cs-fixer-config": "^2",
                "danog/phpdoc": "^0.1.24",
                "psalm/phar": "dev-master"
            },
            "time": "2024-05-09T19:10:54+00:00",
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "danog\\BetterPrometheus\\": "lib/"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "Apache-2.0"
            ],
            "authors": [
                {
                    "name": "Daniil Gentili",
                    "email": "daniil@daniil.it"
                }
            ],
            "description": "A better Prometheus library for PHP applications",
            "support": {
                "issues": "https://github.com/danog/better-prometheus/issues",
                "source": "https://github.com/danog/better-prometheus/tree/0.1.1"
            },
            "funding": [
                {
                    "url": "https://github.com/danog",
                    "type": "github"
                }
            ],
            "install-path": "../danog/better-prometheus"
        },
        {
            "name": "danog/dns-over-https",
            "version": "1.0.1",
            "version_normalized": "1.0.1.0",
            "source": {
                "type": "git",
                "url": "https://github.com/danog/dns-over-https.git",
                "reference": "142b8d439e7a582d66b13d9f1119820dd3110884"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/danog/dns-over-https/zipball/142b8d439e7a582d66b13d9f1119820dd3110884",
                "reference": "142b8d439e7a582d66b13d9f1119820dd3110884",
                "shasum": ""
            },
            "require": {
                "amphp/amp": "^3",
                "amphp/cache": "^2",
                "amphp/dns": "^2",
                "amphp/http-client": "^5",
                "amphp/parser": "^1",
                "amphp/socket": "^2",
                "danog/libdns-json": "^0.2",
                "daverandom/libdns": "^2.0.1",
                "ext-filter": "*",
                "ext-json": "*",
                "php": ">=8.1"
            },
            "require-dev": {
                "amphp/php-cs-fixer-config": "^2",
                "amphp/phpunit-util": "^3",
                "phpunit/phpunit": "^9",
                "vimeo/psalm": "dev-master"
            },
            "time": "2024-12-02T16:42:09+00:00",
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "Amp\\DoH\\": "lib"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Daniil Gentili",
                    "email": "daniil@daniil.it"
                },
                {
                    "name": "Chris Wright",
                    "email": "addr@daverandom.com"
                },
                {
                    "name": "Daniel Lowrey",
                    "email": "rdlowrey@php.net"
                },
                {
                    "name": "Bob Weinand",
                    "email": "bobwei9@hotmail.com"
                },
                {
                    "name": "Niklas Keller",
                    "email": "me@kelunik.com"
                },
                {
                    "name": "Aaron Piotrowski",
                    "email": "aaron@trowski.com"
                }
            ],
            "description": "Async DNS-over-HTTPS resolution for Amp.",
            "homepage": "https://github.com/danog/dns-over-https",
            "keywords": [
                "amp",
                "amphp",
                "async",
                "client",
                "dns",
                "dns-over-https",
                "doh",
                "https",
                "resolve"
            ],
            "support": {
                "issues": "https://github.com/danog/dns-over-https/issues",
                "source": "https://github.com/danog/dns-over-https/tree/1.0.1"
            },
            "install-path": "../danog/dns-over-https"
        },
        {
            "name": "danog/ipc",
            "version": "1.1.0",
            "version_normalized": "1.1.0.0",
            "source": {
                "type": "git",
                "url": "https://github.com/danog/ipc.git",
                "reference": "eea6de6a57273f32a4f90a652ae1409786be18a0"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/danog/ipc/zipball/eea6de6a57273f32a4f90a652ae1409786be18a0",
                "reference": "eea6de6a57273f32a4f90a652ae1409786be18a0",
                "shasum": ""
            },
            "require": {
                "amphp/byte-stream": "^2.1.2",
                "amphp/parallel": "^2.3.1",
                "amphp/parser": "^1.1.1",
                "php": ">=8.1"
            },
            "require-dev": {
                "amphp/amp": "v3.x-dev",
                "amphp/php-cs-fixer-config": "v2.x-dev",
                "amphp/phpunit-util": "v3.x-dev",
                "phpunit/phpunit": "^9.6.22",
                "psalm/phar": "^5.26.1"
            },
            "suggest": {
                "ext-sockets": "Required for UNIX socket support on Windows (TCP is used otherwise)"
            },
            "time": "2026-10-18T18:06:10+00:00",
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "files": [
                    "lib/functions.php"
                ],
                "psr-4": {
                    "Amp\\Ipc\\": "lib"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Daniil Gentili",
                    "email": "daniil@daniil.it"
                },
                {
                    "name": "Aaron Piotrowski",
                    "email": "aaron@trowski.com"
                },
                {
                    "name": "Stephen Coakley",
                    "email": "me@stephencoakley.com"
                }
            ],
            "description": "IPC component for Amp.",
            "homepage": "https://github.com/danog/ipc",
            "keywords": [
                "async",
                "asynchronous",
                "concurrent",
                "multi-processing",
                "multi-threading"
            ],
            "support": {
                "issues": "https://github.com/danog/ipc/issues",
                "source": "https://github.com/danog/ipc/tree/1.1.0"
            },
            "install-path": "../danog/ipc"
        },
        {
            "name": "danog/libdns-json",
            "version": "0.2.1",
            "version_normalized": "0.2.1.0",
            "source": {
                "type": "git",
                "url": "https://github.com/danog/LibDNSJson.git",
                "reference": "cd2981a386a32ff62d447a9553e203384651ba90"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/danog/LibDNSJson/zipball/cd2981a386a32ff62d447a9553e203384651ba90",
                "reference": "cd2981a386a32ff62d447a9553e203384651ba90",
                "shasum": ""
            },
            "require": {
                "daverandom/libdns": "^2.0.1",
                "ext-json": "*",
                "php": ">=8.1"
            },
            "require-dev": {
                "amphp/php-cs-fixer-config": "v2.x-dev",
                "phpunit/phpunit": "^9",
                "psalm/phar": "^5.1"
            },
            "time": "2024-12-02T16:40:37+00:00",
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "danog\\LibDNSJson\\": "lib"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Daniil Gentili",
                    "email": "daniil@daniil.it"
                },
                {
                    "name": "Chris Wright",
                    "email": "addr@daverandom.com"
                }
            ],
            "description": "Encoder/decoder for google's JSON DNS message format based on libdns",
            "homepage": "https://github.com/danog/libdns-json",
            "keywords": [
                "dns",
                "dns-over-https",
                "doh",
                "https",
                "json",
                "libdns",
                "message"
            ],
            "support": {
                "issues": "https://github.com/danog/LibDNSJson/issues",
                "source": "https://github.com/danog/LibDNSJson/tree/0.2.1"
            },
            "install-path": "../danog/libdns-json"
        },
        {
            "name": "danog/loop",
            "version": "1.1.1",
            "version_normalized": "1.1.1.0",
            "source": {
                "type": "git",
                "url": "https://github.com/danog/loop.git",
                "reference": "83c42b26c1d8d070c56bc9c03e2e8a147b45c60f"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/danog/loop/zipball/83c42b26c1d8d070c56bc9c03e2e8a147b45c60f",
                "reference": "83c42b26c1d8d070c56bc9c03e2e8a147b45c60f",
                "shasum": ""
            },
            "require": {
                "amphp/amp": "^3",
                "php": ">=8.1"
            },
            "require-dev": {
                "amphp/php-cs-fixer-config": "^2",
                "amphp/phpunit-util": "^3",
                "phpunit/phpunit": "^9",
                "psalm/phar": "dev-master"
            },
            "time": "2023-09-30T12:45:07+00:00",
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "danog\\Loop\\": "lib"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Daniil Gentili",
                    "email": "daniil@daniil.it"
                }
            ],
            "description": "Loop abstraction for AMPHP.",
            "homepage": "https://github.com/danog/loop",
            "keywords": [
                "async",
                "asynchronous",
                "concurrent",
                "multi-processing",
                "multi-threading"
            ],
            "support": {
                "issues": "https://github.com/danog/loop/issues",
                "source": "https://github.com/danog/loop/tree/1.1.1"
            },
            "funding": [
                {
                    "url": "https://github.com/danog",
                    "type": "github"
                }
            ],
            "install-path": "../danog/loop"
        },
        {
            "name": "danog/madelineproto",
            "version": "8.12.0",
            "version_normalized": "8.12.0.0",
            "dist": {
                "type": "path",
                "url": "/__w/MadelineProto/MadelineProto",
                "reference": "f751f650859e231ce1921721bbf78287df5c30f8"
            },
            "require": {
                "amphp/amp": "^3.1.3",
                "amphp/byte-stream": "^2.1.2",
                "amphp/dns": "^2.4.1",
                "amphp/file": "^3.2.0",
                "amphp/http": "^2.1.2",
                "amphp/http-client": "^5.3.6",
                "amphp/http-client-cookies": "^2",
                "amphp/http-server": "^3.4.6",
                "amphp/log": "^2",
                "amphp/mysql": "^3.1.1",
                "amphp/postgres": "^2.2.1",
                "amphp/process": "^2.1",
                "amphp/redis": "^2.0.4",
                "amphp/socket": "^2.4.1",
                "amphp/websocket-client": "^2.0.2",
                "bacon/bacon-qr-code": "^3.1.1",
                "danog/async-orm": "^1.1.7",
                "danog/better-prometheus": "^0.1.1",
                "danog/dns-over-https": "^1.0.1",
                "danog/ipc": "^1.1.0",
                "danog/loop": "^1.1.1",
                "danog/php-rtc-av": "^1.1.11",
                "danog/php-rtc-codecs": "^1.1.19",
                "danog/php-rtc-datachannel": "^1.1.10",
                "danog/php-rtc-dtls": "^2.0.19",
                "danog/php-rtc-exception": "^1.1.10",
                "danog/php-rtc-ice": "^2.0.18",
                "danog/php-rtc-mdns": "^1.1.12",
                "danog/php-rtc-mixin": "^2.0.9",
                "danog/php-rtc-ntp": "^1.1.14",
                "danog/php-rtc-rtcp": "^1.1.11",
                "danog/php-rtc-rtp": "^2.0.23",
                "danog/php-rtc-rtp-parameter": "^1.1.8",
                "danog/php-rtc-sctp": "^2.0.14",
                "danog/php-rtc-sdp": "^1.1.12",
                "danog/php-rtc-srtp": "^1.1.16",
                "danog/php-rtc-stats": "^1.1.7",
                "danog/php-rtc-stun": "^2.0.18",
                "danog/php-rtc-turn": "^2.0.9",
                "danog/php-rtc-webrtc": "^2.0.19",
                "danog/primemodule": "^1.0.13",
                "danog/telegram-entities": "^1.1.0",
                "danog/tg-dialog-id": "^2",
                "danog/tg-file-decoder": "^1.0.2",
                "ext-dom": "*",
                "ext-fileinfo": "*",
                "ext-filter": "*",
                "ext-hash": "*",
                "ext-json": "*",
                "ext-xml": "*",
                "ext-zlib": "*",
                "league/uri": "^7.8.1",
                "nikic/php-parser": "^5.9.0",
                "php-64bit": ">=8.2",
                "phpseclib/phpseclib": "^4.0.1",
                "psr/http-factory": "^1.1.0",
                "psr/log": "^3.0.2",
                "revolt/event-loop": "^1.0.9",
                "symfony/polyfill-iconv": "^1.37",
                "symfony/polyfill-mbstring": ">=1.38.2",
                "symfony/polyfill-php83": "^1.41",
                "symfony/polyfill-php84": "^1.38",
                "symfony/polyfill-php85": "^1.41",
                "symfony/polyfill-php86": "^1.41",
                "webmozart/assert": "^2.4.1"
            },
            "conflict": {
                "ext-pthreads": "*",
                "krakjoe/pthreads-polyfill": "*"
            },
            "require-dev": {
                "amphp/phpunit-util": "^3",
                "bamarni/composer-bin-plugin": "1.9.1",
                "brianium/paratest": "^6.11.1",
                "danog/phpdoc": "^0.1.24",
                "dg/bypass-finals": "dev-master",
                "ext-ctype": "*",
                "phpunit/phpunit": "^9.6.36",
                "sebastian/diff": "^4.0.6",
                "symfony/yaml": "^6.4.45",
                "vimeo/psalm": "dev-master"
            },
            "suggest": {
                "ext-bcmath": "Install the bcmath extension to speed up authorization",
                "ext-ffi": "Install the FFI extension (with libopus, libvpx and ffmpeg) to transcode audio and video in realtime; not required to play and record video and audio inside calls",
                "ext-gmp": "Install the gmp extension to speed up authorization",
                "ext-openssl": "Install the openssl extension for faster crypto",
                "ext-pdo": "Install the pdo extension to store session data on MySQL",
                "ext-primemodule": "Install the primemodule and FFI extensions to speed up MadelineProto (https://prime.madelineproto.xyz)",
                "ext-sockets": "Install the sockets extension on Windows to use UNIX sockets for IPC (otherwise TCP on 127.0.0.1 is used, which any local user can connect to)",
                "ext-sodium": "Install the sodium extension for faster crypto",
                "ext-uv": "Install the uv extension to greatly speed up MadelineProto!"
            },
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "danog\\MadelineProto\\": "src"
                },
                "files": [
                    "src/polyfill.php"
                ]
            },
            "autoload-dev": {
                "psr-4": {
                    "danog\\MadelineProto\\Test\\": "tests/danog/MadelineProto",
                    "danog\\MadelineProto\\": "tools/"
                },
                "files": [
                    "tools/build_docs/schemas.php",
                    "tools/build_docs/merge.php",
                    "tools/build_docs/layerUpgrade.php"
                ]
            },
            "scripts": {
                "build": [
                    "@docs",
                    "@docs-fix",
                    "@cs-fix",
                    "@fuzz",
                    "@file_ref_map",
                    "@test-light"
                ],
                "test": [
                    "@paratest",
                    "@test-session"
                ],
                "test-light": [
                    "@paratest-light"
                ],
                "cs-fix": [
                    "PHP_CS_FIXER_IGNORE_ENV=1 php -d pcre.jit=0 vendor/bin/php-cs-fixer fix -v"
                ],
                "psalm": [
                    "psalm --no-cache --threads=10"
                ],
                "docs": [
                    "php tools/build_docs.php"
                ],
                "fuzz": [
                    "php tools/fuzzer.php"
                ],
                "file_ref_map": [
                    "php tools/gen_filerefmap.php"
                ],
                "file_ref_map_docs": [
                    "php tools/gen_filerefmap_docs.php"
                ],
                "docs-fix": [
                    "tools/fix_docs.sh"
                ],
                "paratest": [
                    "@php -dzend.assertions=1 ./vendor/bin/paratest -v --exclude-group session"
                ],
                "test-session": [
                    "tests/session_tests.sh"
                ],
                "paratest-light": [
                    "@php -dzend.assertions=1 ./vendor/bin/paratest -vc phpunit-light.xml --exclude-group session"
                ],
                "bin": [
                    "echo 'bin not installed'"
                ],
                "post-install-cmd": [
                    "@composer bin all install --ansi"
                ],
                "post-update-cmd": [
                    "@composer bin all update --ansi"
                ]
            },
            "license": [
                "AGPL-3.0-only"
            ],
            "authors": [
                {
                    "name": "Daniil Gentili",
                    "email": "daniil@daniil.it"
                }
            ],
            "description": "Async PHP client API for the telegram MTProto protocol.",
            "homepage": "https://docs.madelineproto.xyz",
            "keywords": [
                "GB",
                "PHP",
                "audio",
                "bytes",
                "client",
                "files",
                "messenger",
                "mtproto",
                "protocol",
                "stickers",
                "telegram",
                "video"
            ],
            "transport-options": {
                "symlink": false,
                "relative": false
            },
            "install-path": "../danog/madelineproto"
        },
        {
            "name": "danog/php-rtc-av",
            "version": "1.1.11",
            "version_normalized": "1.1.11.0",
            "source": {
                "type": "git",
                "url": "https://github.com/danog/php-rtc-av.git",
                "reference": "f328e6b5682d7e885dcfab425839921312162008"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/danog/php-rtc-av/zipball/f328e6b5682d7e885dcfab425839921312162008",
                "reference": "f328e6b5682d7e885dcfab425839921312162008",
                "shasum": ""
            },
            "require": {
                "danog/php-rtc-exception": "^1.1.5",
                "danog/php-rtc-mixin": "^1.1.6 || ^2.0",
                "php-64bit": "^8.2"
            },
            "require-dev": {
                "bamarni/composer-bin-plugin": "^1.9.1",
                "phpunit/phpunit": "^11.5 || ^12.0 || ^13.3.2"
            },
            "suggest": {
                "ext-ffi": "Install the FFI extension and libavcodec/libavformat/libavfilter to transcode media; not required to mux or packetize already-encoded media"
            },
            "time": "2026-10-06T15:43:53+00:00",
            "type": "library",
            "extra": {
                "bamarni-bin": {
                    "bin-links": true,
                    "forward-command": true
                }
            },
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "Webrtc\\AVCodec\\": "src"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Amin Yazdanpanah",
                    "email": "github@aminyazdanpanah.com"
                },
                {
                    "name": "Sana Moniri",
                    "email": "s.mo@quasarstream.com"
                },
                {
                    "name": "Quasar Stream Community"
                },
                {
                    "name": "Daniil Gentili",
                    "email": "daniil@daniil.it"
                }
            ],
            "description": "FFmpeg/libav FFI bindings for PHP media processing. MadelineProto's PHP 8.2+ fork uses lazy, optional FFI, strict ABI checks, and the modular danog/php-rtc stack.",
            "homepage": "https://github.com/danog/php-rtc-av",
            "keywords": [
                "WebRTC",
                "avcodec",
                "avfilter",
                "avformat",
                "codec",
                "ffmpeg",
                "libavcodec",
                "libavfilter",
                "libavformat",
                "libswscale",
                "swscale"
            ],
            "support": {
                "issues": "https://github.com/danog/php-rtc-av/issues",
                "source": "https://github.com/danog/php-rtc-av/tree/1.1.11"
            },
            "install-path": "../danog/php-rtc-av"
        },
        {
            "name": "danog/php-rtc-codecs",
            "version": "1.1.19",
            "version_normalized": "1.1.19.0",
            "source": {
                "type": "git",
                "url": "https://github.com/danog/php-rtc-codecs.git",
                "reference": "00836382276949099077e19659ff6648b823284f"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/danog/php-rtc-codecs/zipball/00836382276949099077e19659ff6648b823284f",
                "reference": "00836382276949099077e19659ff6648b823284f",
                "shasum": ""
            },
            "require": {
                "danog/php-rtc-av": "^1.1.3",
                "danog/php-rtc-exception": "^1.1.5",
                "danog/php-rtc-rtp-parameter": "^1.1.4",
                "php": "^8.2"
            },
            "require-dev": {
                "bamarni/composer-bin-plugin": "^1.9.1",
                "phpunit/phpunit": "^11.5 || ^12.0 || ^13.3.2"
            },
            "suggest": {
                "ext-ffi": "Install the FFI extension and libopus/libvpx/libavcodec to transcode media; not required to packetize already-encoded media"
            },
            "time": "2026-10-16T23:52:08+00:00",
            "type": "library",
            "extra": {
                "bamarni-bin": {
                    "bin-links": true,
                    "forward-command": true
                }
            },
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "Webrtc\\Codecs\\": "src"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "BSD-3-Clause"
            ],
            "authors": [
                {
                    "name": "Amin Yazdanpanah",
                    "email": "github@aminyazdanpanah.com"
                },
                {
                    "name": "Sana Moniri",
                    "email": "s.mo@quasarstream.com"
                },
                {
                    "name": "Quasar Stream Community"
                },
                {
                    "name": "Daniil Gentili",
                    "email": "daniil@daniil.it"
                }
            ],
            "description": "WebRTC audio/video codec and RTP packetization utilities. MadelineProto's PHP 8.2+ fork adds optional transcoding FFI, VP9 packetization, and the modular danog/php-rtc stack.",
            "homepage": "https://github.com/danog/php-rtc-codecs",
            "keywords": [
                "WebRTC",
                "codecs",
                "h264",
                "opus",
                "pcma",
                "pcmu",
                "vp8"
            ],
            "support": {
                "issues": "https://github.com/danog/php-rtc-codecs/issues",
                "source": "https://github.com/danog/php-rtc-codecs/tree/1.1.19"
            },
            "install-path": "../danog/php-rtc-codecs"
        },
        {
            "name": "danog/php-rtc-datachannel",
            "version": "1.1.10",
            "version_normalized": "1.1.10.0",
            "source": {
                "type": "git",
                "url": "https://github.com/danog/php-rtc-datachannel.git",
                "reference": "3a5ad6f76326514f41a9e1847b41d503e157c459"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/danog/php-rtc-datachannel/zipball/3a5ad6f76326514f41a9e1847b41d503e157c459",
                "reference": "3a5ad6f76326514f41a9e1847b41d503e157c459",
                "shasum": ""
            },
            "require": {
                "danog/php-rtc-exception": "^1.1.5",
                "danog/php-rtc-mixin": "^2.0",
                "php": "^8.2",
                "psr/log": "^3.0.2"
            },
            "require-dev": {
                "bamarni/composer-bin-plugin": "^1.9.1",
                "phpunit/phpunit": "^11.5 || ^12.0 || ^13.3.2"
            },
            "time": "2026-09-16T11:03:48+00:00",
            "type": "library",
            "extra": {
                "bamarni-bin": {
                    "bin-links": true,
                    "forward-command": true
                }
            },
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "Webrtc\\DataChannel\\": "src"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "BSD-3-Clause"
            ],
            "authors": [
                {
                    "name": "Amin Yazdanpanah",
                    "email": "github@aminyazdanpanah.com"
                },
                {
                    "name": "Sana Moniri",
                    "email": "s.mo@quasarstream.com"
                },
                {
                    "name": "Quasar Stream Community"
                },
                {
                    "name": "Daniil Gentili",
                    "email": "daniil@daniil.it"
                }
            ],
            "description": "A PHP implementation of WebRTC's RTCDataChannel interface. MadelineProto's PHP 8.2+ fork is a Composer-compatible component of the modular danog/php-rtc stack.",
            "homepage": "https://github.com/danog/php-rtc-datachannel",
            "keywords": [
                "WebRTC",
                "datachannel"
            ],
            "support": {
                "issues": "https://github.com/danog/php-rtc-datachannel/issues",
                "source": "https://github.com/danog/php-rtc-datachannel/tree/1.1.10"
            },
            "install-path": "../danog/php-rtc-datachannel"
        },
        {
            "name": "danog/php-rtc-dtls",
            "version": "2.0.20",
            "version_normalized": "2.0.20.0",
            "source": {
                "type": "git",
                "url": "https://github.com/danog/php-rtc-dtls.git",
                "reference": "794180b0c3cf54842df44b7bcb44f8fb50f199c8"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/danog/php-rtc-dtls/zipball/794180b0c3cf54842df44b7bcb44f8fb50f199c8",
                "reference": "794180b0c3cf54842df44b7bcb44f8fb50f199c8",
                "shasum": ""
            },
            "require": {
                "amphp/amp": "^3.1.3",
                "danog/php-rtc-exception": "^1.1.5",
                "danog/php-rtc-ice": "^1.1.18 || ^2.0",
                "danog/php-rtc-mixin": "^2.0",
                "danog/php-rtc-rtp": "^2.0.23",
                "danog/php-rtc-sctp": "^1.1.5 || ^2.0",
                "danog/php-rtc-sdp": "^1.1.6",
                "danog/php-rtc-srtp": "^1.1.16",
                "danog/php-rtc-stats": "^1.1.3",
                "php-64bit": "^8.2",
                "phpseclib/phpseclib": "^4.0",
                "symfony/polyfill-php84": "*"
            },
            "require-dev": {
                "bamarni/composer-bin-plugin": "^1.9.1",
                "dg/bypass-finals": "dev-master",
                "phpunit/phpunit": "^11.5 || ^12.0 || ^13.3.2"
            },
            "time": "2026-10-17T20:02:59+00:00",
            "type": "library",
            "extra": {
                "bamarni-bin": {
                    "bin-links": true,
                    "forward-command": true
                }
            },
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "Webrtc\\DTLS\\": "src"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "BSD-3-Clause"
            ],
            "authors": [
                {
                    "name": "Amin Yazdanpanah",
                    "email": "github@aminyazdanpanah.com"
                },
                {
                    "name": "Sana Moniri",
                    "email": "s.mo@quasarstream.com"
                },
                {
                    "name": "Quasar Stream Community"
                },
                {
                    "name": "Daniil Gentili",
                    "email": "daniil@daniil.it"
                }
            ],
            "description": "Pure-PHP DTLS 1.2 for WebRTC, powered by phpseclib and Amp v3.",
            "homepage": "https://github.com/danog/php-rtc-dtls",
            "keywords": [
                "certificate",
                "crypto",
                "dtls",
                "openssl",
                "privateKey",
                "rtp",
                "ssl",
                "x509"
            ],
            "support": {
                "issues": "https://github.com/danog/php-rtc-dtls/issues",
                "source": "https://github.com/danog/php-rtc-dtls/tree/2.0.20"
            },
            "install-path": "../danog/php-rtc-dtls"
        },
        {
            "name": "danog/php-rtc-exception",
            "version": "1.1.10",
            "version_normalized": "1.1.10.0",
            "source": {
                "type": "git",
                "url": "https://github.com/danog/php-rtc-exception.git",
                "reference": "f9347a5376bb6b7b2fb008e06a8fd9fb6d9febd7"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/danog/php-rtc-exception/zipball/f9347a5376bb6b7b2fb008e06a8fd9fb6d9febd7",
                "reference": "f9347a5376bb6b7b2fb008e06a8fd9fb6d9febd7",
                "shasum": ""
            },
            "require": {
                "php": "^8.2"
            },
            "require-dev": {
                "bamarni/composer-bin-plugin": "^1.9.1"
            },
            "time": "2026-09-16T11:03:57+00:00",
            "type": "library",
            "extra": {
                "bamarni-bin": {
                    "bin-links": true,
                    "forward-command": true
                }
            },
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "Webrtc\\Exception\\": "src"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "BSD-3-Clause"
            ],
            "authors": [
                {
                    "name": "Amin Yazdanpanah",
                    "email": "github@aminyazdanpanah.com"
                },
                {
                    "name": "Sana Moniri",
                    "email": "s.mo@quasarstream.com"
                },
                {
                    "name": "Quasar Stream Community"
                },
                {
                    "name": "Daniil Gentili",
                    "email": "daniil@daniil.it"
                }
            ],
            "description": "Lightweight exceptions shared by the WebRTC components. MadelineProto's PHP 8.2+ fork is a Composer-compatible foundation of the modular danog/php-rtc stack.",
            "homepage": "https://github.com/danog/php-rtc-exception",
            "keywords": [
                "WebRTC"
            ],
            "support": {
                "issues": "https://github.com/danog/php-rtc-exception/issues",
                "source": "https://github.com/danog/php-rtc-exception/tree/1.1.10"
            },
            "install-path": "../danog/php-rtc-exception"
        },
        {
            "name": "danog/php-rtc-ice",
            "version": "2.0.18",
            "version_normalized": "2.0.18.0",
            "source": {
                "type": "git",
                "url": "https://github.com/danog/php-rtc-ice.git",
                "reference": "91c53e982f334cf3bfe64a9e298d874bba51a3f1"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/danog/php-rtc-ice/zipball/91c53e982f334cf3bfe64a9e298d874bba51a3f1",
                "reference": "91c53e982f334cf3bfe64a9e298d874bba51a3f1",
                "shasum": ""
            },
            "require": {
                "amphp/amp": "^3.1.3",
                "amphp/dns": "^2.4.1",
                "amphp/socket": "^2.4.1",
                "danog/php-rtc-exception": "^1.1.5",
                "danog/php-rtc-mdns": "^1.1.7",
                "danog/php-rtc-mixin": "^2.0.9",
                "danog/php-rtc-stun": "^2.0.17",
                "danog/php-rtc-turn": "^2.0.9",
                "php-64bit": "^8.2",
                "psr/log": "^3.0.2",
                "symfony/polyfill-php84": "*"
            },
            "require-dev": {
                "bamarni/composer-bin-plugin": "^1.9.1",
                "mockery/mockery": "^1.6.15",
                "phpunit/phpunit": "^11.5 || ^12.0 || ^13.3.2"
            },
            "time": "2026-10-16T21:18:22+00:00",
            "type": "library",
            "extra": {
                "bamarni-bin": {
                    "bin-links": true,
                    "forward-command": true
                }
            },
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "Webrtc\\ICE\\": "src"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "BSD-3-Clause"
            ],
            "authors": [
                {
                    "name": "Amin Yazdanpanah",
                    "email": "github@aminyazdanpanah.com"
                },
                {
                    "name": "Sana Moniri",
                    "email": "s.mo@quasarstream.com"
                },
                {
                    "name": "Quasar Stream Community"
                },
                {
                    "name": "Daniil Gentili",
                    "email": "daniil@daniil.it"
                }
            ],
            "description": "WebRTC ICE connectivity in PHP, ported to Amp v3 fibers and sockets. MadelineProto's PHP 8.2+ fork integrates the modular danog/php-rtc STUN, TURN, and mDNS stack.",
            "homepage": "https://github.com/danog/php-rtc-ice",
            "keywords": [
                "WebRTC",
                "ice",
                "stun",
                "turn"
            ],
            "support": {
                "issues": "https://github.com/danog/php-rtc-ice/issues",
                "source": "https://github.com/danog/php-rtc-ice/tree/2.0.18"
            },
            "install-path": "../danog/php-rtc-ice"
        },
        {
            "name": "danog/php-rtc-mdns",
            "version": "1.1.12",
            "version_normalized": "1.1.12.0",
            "source": {
                "type": "git",
                "url": "https://github.com/danog/php-rtc-mdns.git",
                "reference": "ff3f4315339ddcdd4d71bd4e02156903261ef57a"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/danog/php-rtc-mdns/zipball/ff3f4315339ddcdd4d71bd4e02156903261ef57a",
                "reference": "ff3f4315339ddcdd4d71bd4e02156903261ef57a",
                "shasum": ""
            },
            "require": {
                "amphp/amp": "^3.1.3",
                "amphp/socket": "^2.4.1",
                "danog/php-rtc-exception": "^1.1.5",
                "daverandom/libdns": "^2.1.0",
                "php": "^8.2"
            },
            "require-dev": {
                "bamarni/composer-bin-plugin": "^1.9.1",
                "phpunit/phpunit": "^11.5 || ^12.0 || ^13.3.2"
            },
            "time": "2026-09-16T11:04:05+00:00",
            "type": "library",
            "extra": {
                "bamarni-bin": {
                    "bin-links": true,
                    "forward-command": true
                }
            },
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "Webrtc\\MDNS\\": "src"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "BSD-3-Clause"
            ],
            "authors": [
                {
                    "name": "Amin Yazdanpanah",
                    "email": "github@aminyazdanpanah.com"
                },
                {
                    "name": "Sana Moniri",
                    "email": "s.mo@quasarstream.com"
                },
                {
                    "name": "Quasar Stream Community"
                },
                {
                    "name": "Daniil Gentili",
                    "email": "daniil@daniil.it"
                }
            ],
            "description": "An Amp v3 Multicast DNS resolver for WebRTC. MadelineProto's PHP 8.2+ fork is a Composer-compatible component of the modular danog/php-rtc stack.",
            "homepage": "https://github.com/danog/php-rtc-mdns",
            "keywords": [
                "mDNS",
                "multicastdns"
            ],
            "support": {
                "issues": "https://github.com/danog/php-rtc-mdns/issues",
                "source": "https://github.com/danog/php-rtc-mdns/tree/1.1.12"
            },
            "install-path": "../danog/php-rtc-mdns"
        },
        {
            "name": "danog/php-rtc-mixin",
            "version": "2.0.9",
            "version_normalized": "2.0.9.0",
            "source": {
                "type": "git",
                "url": "https://github.com/danog/php-rtc-mixin.git",
                "reference": "e7a086a7fe01875f52e1f08301195db05e7ff9a5"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/danog/php-rtc-mixin/zipball/e7a086a7fe01875f52e1f08301195db05e7ff9a5",
                "reference": "e7a086a7fe01875f52e1f08301195db05e7ff9a5",
                "shasum": ""
            },
            "require": {
                "php": "^8.2"
            },
            "require-dev": {
                "bamarni/composer-bin-plugin": ">=1.9.1"
            },
            "time": "2026-10-06T12:36:35+00:00",
            "type": "library",
            "extra": {
                "bamarni-bin": {
                    "bin-links": true,
                    "forward-command": true
                }
            },
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "Webrtc\\Mixin\\": "src"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "BSD-3-Clause"
            ],
            "authors": [
                {
                    "name": "Amin Yazdanpanah",
                    "email": "github@aminyazdanpanah.com"
                },
                {
                    "name": "Sana Moniri",
                    "email": "s.mo@quasarstream.com"
                },
                {
                    "name": "Quasar Stream Community"
                },
                {
                    "name": "Daniil Gentili",
                    "email": "daniil@daniil.it"
                }
            ],
            "description": "Common PHP classes and traits for WebRTC components. MadelineProto's PHP 8.2+ fork is a Composer-compatible foundation of the modular danog/php-rtc stack.",
            "homepage": "https://github.com/danog/php-rtc-mixin",
            "keywords": [
                "WebRTC"
            ],
            "support": {
                "issues": "https://github.com/danog/php-rtc-mixin/issues",
                "source": "https://github.com/danog/php-rtc-mixin/tree/2.0.9"
            },
            "install-path": "../danog/php-rtc-mixin"
        },
        {
            "name": "danog/php-rtc-ntp",
            "version": "1.1.15",
            "version_normalized": "1.1.15.0",
            "source": {
                "type": "git",
                "url": "https://github.com/danog/php-rtc-ntp.git",
                "reference": "d70dc01410dba146cb808b9c2bf3c28ed1199d20"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/danog/php-rtc-ntp/zipball/d70dc01410dba146cb808b9c2bf3c28ed1199d20",
                "reference": "d70dc01410dba146cb808b9c2bf3c28ed1199d20",
                "shasum": ""
            },
            "require": {
                "php-64bit": "^8.2",
                "symfony/polyfill-php83": "*"
            },
            "require-dev": {
                "bamarni/composer-bin-plugin": "^1.9.1",
                "phpunit/phpunit": "^11.5 || ^12.0 || ^13.3.2",
                "symfony/phpunit-bridge": "7.3.x-dev"
            },
            "time": "2026-10-16T23:17:01+00:00",
            "type": "library",
            "extra": {
                "bamarni-bin": {
                    "bin-links": true,
                    "forward-command": true
                }
            },
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "Webrtc\\NTP\\": "src"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "BSD-3-Clause"
            ],
            "authors": [
                {
                    "name": "Amin Yazdanpanah",
                    "email": "github@aminyazdanpanah.com"
                },
                {
                    "name": "Sana Moniri",
                    "email": "s.mo@quasarstream.com"
                },
                {
                    "name": "Quasar Stream Community"
                },
                {
                    "name": "Daniil Gentili",
                    "email": "daniil@daniil.it"
                }
            ],
            "description": "Pure-PHP NTP timestamp conversion for WebRTC. MadelineProto's PHP 8.2+ fork fixes unsigned 64-bit handling and integrates with the modular danog/php-rtc stack.",
            "homepage": "https://github.com/danog/php-rtc-ntp",
            "keywords": [
                "multicastdns",
                "ntp"
            ],
            "support": {
                "issues": "https://github.com/danog/php-rtc-ntp/issues",
                "source": "https://github.com/danog/php-rtc-ntp/tree/1.1.15"
            },
            "install-path": "../danog/php-rtc-ntp"
        },
        {
            "name": "danog/php-rtc-rtcp",
            "version": "1.1.11",
            "version_normalized": "1.1.11.0",
            "source": {
                "type": "git",
                "url": "https://github.com/danog/php-rtc-rtcp.git",
                "reference": "f2e7c76d040fb0c4b2e4b62a611d13c6352af145"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/danog/php-rtc-rtcp/zipball/f2e7c76d040fb0c4b2e4b62a611d13c6352af145",
                "reference": "f2e7c76d040fb0c4b2e4b62a611d13c6352af145",
                "shasum": ""
            },
            "require": {
                "danog/php-rtc-exception": "^1.1.5",
                "php-64bit": "^8.2"
            },
            "require-dev": {
                "bamarni/composer-bin-plugin": "^1.9.1",
                "phpunit/phpunit": "^11.5 || ^12.0 || ^13.3.2"
            },
            "time": "2026-10-16T23:52:08+00:00",
            "type": "library",
            "extra": {
                "bamarni-bin": {
                    "bin-links": true,
                    "forward-command": true
                }
            },
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "Webrtc\\RTCP\\": "src"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "BSD-3-Clause"
            ],
            "authors": [
                {
                    "name": "Amin Yazdanpanah",
                    "email": "github@aminyazdanpanah.com"
                },
                {
                    "name": "Sana Moniri",
                    "email": "s.mo@quasarstream.com"
                },
                {
                    "name": "Quasar Stream Community"
                },
                {
                    "name": "Daniil Gentili",
                    "email": "daniil@daniil.it"
                }
            ],
            "description": "Pure-PHP RTCP packet parsing and generation. MadelineProto's PHP 8.2+ fork fixes unsigned NTP handling and integrates with the modular danog/php-rtc stack.",
            "homepage": "https://github.com/danog/php-rtc-rtcp",
            "keywords": [
                "rtcp",
                "rtp"
            ],
            "support": {
                "issues": "https://github.com/danog/php-rtc-rtcp/issues",
                "source": "https://github.com/danog/php-rtc-rtcp/tree/1.1.11"
            },
            "install-path": "../danog/php-rtc-rtcp"
        },
        {
            "name": "danog/php-rtc-rtp",
            "version": "2.0.23",
            "version_normalized": "2.0.23.0",
            "source": {
                "type": "git",
                "url": "https://github.com/danog/php-rtc-rtp.git",
                "reference": "e9bee7a5aae91f6abec1fe9a599eefbce219d967"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/danog/php-rtc-rtp/zipball/e9bee7a5aae91f6abec1fe9a599eefbce219d967",
                "reference": "e9bee7a5aae91f6abec1fe9a599eefbce219d967",
                "shasum": ""
            },
            "require": {
                "amphp/amp": "^3.1.3",
                "amphp/pipeline": "^1.2.7",
                "danog/php-rtc-codecs": "^1.1.18",
                "danog/php-rtc-exception": "^1.1.5",
                "danog/php-rtc-mixin": "^2.0",
                "danog/php-rtc-ntp": "^1.1.14",
                "danog/php-rtc-rtcp": "^1.1.4",
                "danog/php-rtc-rtp-parameter": "^1.1.4",
                "danog/php-rtc-sdp": "^1.1.6",
                "danog/php-rtc-stats": "^1.1.3",
                "php-64bit": "^8.2",
                "ramsey/uuid": "^4.9.3",
                "symfony/polyfill-php83": "*"
            },
            "require-dev": {
                "bamarni/composer-bin-plugin": "^1.9.1",
                "dg/bypass-finals": "dev-master",
                "phpunit/phpunit": "^11.5 || ^12.0 || ^13.3.2",
                "symfony/phpunit-bridge": "7.3.x-dev"
            },
            "time": "2026-10-16T23:52:08+00:00",
            "type": "library",
            "extra": {
                "bamarni-bin": {
                    "bin-links": true,
                    "forward-command": true
                }
            },
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "Webrtc\\RTP\\": "src"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "BSD-3-Clause"
            ],
            "authors": [
                {
                    "name": "Amin Yazdanpanah",
                    "email": "github@aminyazdanpanah.com"
                },
                {
                    "name": "Sana Moniri",
                    "email": "s.mo@quasarstream.com"
                },
                {
                    "name": "Quasar Stream Community"
                },
                {
                    "name": "Daniil Gentili",
                    "email": "daniil@daniil.it"
                }
            ],
            "description": "RTP packet and media transport handling for PHP, ported to Amp v3. MadelineProto's PHP 8.2+ fork supports optional media FFI in the modular danog/php-rtc stack.",
            "homepage": "https://github.com/danog/php-rtc-rtp",
            "keywords": [
                "WebRTC",
                "real-time-transport-protocol",
                "rtp"
            ],
            "support": {
                "issues": "https://github.com/danog/php-rtc-rtp/issues",
                "source": "https://github.com/danog/php-rtc-rtp/tree/2.0.23"
            },
            "install-path": "../danog/php-rtc-rtp"
        },
        {
            "name": "danog/php-rtc-rtp-parameter",
            "version": "1.1.8",
            "version_normalized": "1.1.8.0",
            "source": {
                "type": "git",
                "url": "https://github.com/danog/php-rtc-rtp-parameter.git",
                "reference": "cd1e34df768ddae43fc50c1306c2332cf99f07eb"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/danog/php-rtc-rtp-parameter/zipball/cd1e34df768ddae43fc50c1306c2332cf99f07eb",
                "reference": "cd1e34df768ddae43fc50c1306c2332cf99f07eb",
                "shasum": ""
            },
            "require": {
                "danog/php-rtc-mixin": "^1.1.6 || ^2.0",
                "php": "^8.2"
            },
            "require-dev": {
                "bamarni/composer-bin-plugin": ">=1.9.1"
            },
            "time": "2026-09-16T11:04:26+00:00",
            "type": "library",
            "extra": {
                "bamarni-bin": {
                    "bin-links": true,
                    "forward-command": true
                }
            },
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "Webrtc\\RTPParameter\\": "src"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "BSD-3-Clause"
            ],
            "authors": [
                {
                    "name": "Amin Yazdanpanah",
                    "email": "github@aminyazdanpanah.com"
                },
                {
                    "name": "Sana Moniri",
                    "email": "s.mo@quasarstream.com"
                },
                {
                    "name": "Quasar Stream Community"
                },
                {
                    "name": "Daniil Gentili",
                    "email": "daniil@daniil.it"
                }
            ],
            "description": "RTP codec, payload, SSRC, header-extension, and RTCP parameter objects. MadelineProto's PHP 8.2+ fork supports the modular danog/php-rtc stack.",
            "homepage": "https://github.com/danog/php-rtc-rtp-parameter",
            "keywords": [
                "WebRTC",
                "rtp",
                "rtp-parameter"
            ],
            "support": {
                "issues": "https://github.com/danog/php-rtc-rtp-parameter/issues",
                "source": "https://github.com/danog/php-rtc-rtp-parameter/tree/1.1.8"
            },
            "install-path": "../danog/php-rtc-rtp-parameter"
        },
        {
            "name": "danog/php-rtc-sctp",
            "version": "2.0.14",
            "version_normalized": "2.0.14.0",
            "source": {
                "type": "git",
                "url": "https://github.com/danog/php-rtc-sctp.git",
                "reference": "741697e355abd7eb8e8134b115fab775bb5ccd06"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/danog/php-rtc-sctp/zipball/741697e355abd7eb8e8134b115fab775bb5ccd06",
                "reference": "741697e355abd7eb8e8134b115fab775bb5ccd06",
                "shasum": ""
            },
            "require": {
                "amphp/amp": "^3.1.3",
                "danog/php-rtc-datachannel": "^1.1.4",
                "danog/php-rtc-exception": "^1.1.5",
                "danog/php-rtc-ice": "^1.1.18 || ^2.0",
                "danog/php-rtc-mixin": "^2.0",
                "danog/php-rtc-sdp": "^1.1.6",
                "danog/php-rtc-stats": "^1.1.3",
                "php-64bit": "^8.2",
                "symfony/polyfill-mbstring": "*"
            },
            "require-dev": {
                "bamarni/composer-bin-plugin": "^1.9.1",
                "dg/bypass-finals": "dev-master",
                "mockery/mockery": "^1.6.15",
                "phpunit/phpunit": "^11.5 || ^12.0 || ^13.3.2",
                "symfony/phpunit-bridge": "7.3.x-dev"
            },
            "time": "2026-10-06T15:18:53+00:00",
            "type": "library",
            "extra": {
                "bamarni-bin": {
                    "bin-links": true,
                    "forward-command": true
                }
            },
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "Webrtc\\SCTP\\": "src"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "BSD-3-Clause"
            ],
            "authors": [
                {
                    "name": "Amin Yazdanpanah",
                    "email": "github@aminyazdanpanah.com"
                },
                {
                    "name": "Sana Moniri",
                    "email": "s.mo@quasarstream.com"
                },
                {
                    "name": "Quasar Stream Community"
                },
                {
                    "name": "Daniil Gentili",
                    "email": "daniil@daniil.it"
                }
            ],
            "description": "SCTP for PHP WebRTC data channels, ported to Amp v3 fibers and timers. MadelineProto's PHP 8.2+ fork is part of the modular danog/php-rtc stack.",
            "homepage": "https://github.com/danog/php-rtc-sctp",
            "keywords": [
                "WebRTC",
                "datachannel",
                "sctp"
            ],
            "support": {
                "issues": "https://github.com/danog/php-rtc-sctp/issues",
                "source": "https://github.com/danog/php-rtc-sctp/tree/2.0.14"
            },
            "install-path": "../danog/php-rtc-sctp"
        },
        {
            "name": "danog/php-rtc-sdp",
            "version": "1.1.12",
            "version_normalized": "1.1.12.0",
            "source": {
                "type": "git",
                "url": "https://github.com/danog/php-rtc-sdp.git",
                "reference": "315b9cbaff71625351864c292de0b6ac3b3a39fc"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/danog/php-rtc-sdp/zipball/315b9cbaff71625351864c292de0b6ac3b3a39fc",
                "reference": "315b9cbaff71625351864c292de0b6ac3b3a39fc",
                "shasum": ""
            },
            "require": {
                "danog/php-rtc-exception": "^1.1.5",
                "danog/php-rtc-ice": "^1.1.18 || ^2.0",
                "danog/php-rtc-rtp-parameter": "^1.1.4",
                "php": "^8.2",
                "symfony/polyfill-php84": "*"
            },
            "require-dev": {
                "bamarni/composer-bin-plugin": "^1.9.1",
                "phpunit/phpunit": "^11.5 || ^12.0 || ^13.3.2"
            },
            "time": "2026-09-16T11:04:35+00:00",
            "type": "library",
            "extra": {
                "bamarni-bin": {
                    "bin-links": true,
                    "forward-command": true
                }
            },
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "Webrtc\\SDP\\": "src"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "BSD-3-Clause"
            ],
            "authors": [
                {
                    "name": "Amin Yazdanpanah",
                    "email": "github@aminyazdanpanah.com"
                },
                {
                    "name": "Sana Moniri",
                    "email": "s.mo@quasarstream.com"
                },
                {
                    "name": "Quasar Stream Community"
                },
                {
                    "name": "Daniil Gentili",
                    "email": "daniil@daniil.it"
                }
            ],
            "description": "SDP parsing and generation for PHP WebRTC. MadelineProto's PHP 8.2+ fork is a Composer-compatible component of the modular danog/php-rtc stack.",
            "homepage": "https://github.com/danog/php-rtc-sdp",
            "keywords": [
                "SDP",
                "rtp"
            ],
            "support": {
                "issues": "https://github.com/danog/php-rtc-sdp/issues",
                "source": "https://github.com/danog/php-rtc-sdp/tree/1.1.12"
            },
            "install-path": "../danog/php-rtc-sdp"
        },
        {
            "name": "danog/php-rtc-srtp",
            "version": "1.1.16",
            "version_normalized": "1.1.16.0",
            "source": {
                "type": "git",
                "url": "https://github.com/danog/php-rtc-srtp.git",
                "reference": "b19959292663d8933a0a48cd1fda05788fb5c542"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/danog/php-rtc-srtp/zipball/b19959292663d8933a0a48cd1fda05788fb5c542",
                "reference": "b19959292663d8933a0a48cd1fda05788fb5c542",
                "shasum": ""
            },
            "require": {
                "danog/php-rtc-exception": "^1.1.5",
                "danog/php-rtc-mixin": "^1.1.6 || ^2.0",
                "php-64bit": "^8.2",
                "phpseclib/phpseclib": "^4.0"
            },
            "require-dev": {
                "bamarni/composer-bin-plugin": "^1.9.1",
                "phpunit/phpunit": "^11.5 || ^12.0 || ^13.3.2"
            },
            "time": "2026-10-16T22:00:35+00:00",
            "type": "library",
            "extra": {
                "bamarni-bin": {
                    "bin-links": true,
                    "forward-command": true
                }
            },
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "Webrtc\\Srtp\\": "src"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "BSD-3-Clause"
            ],
            "authors": [
                {
                    "name": "Amin Yazdanpanah",
                    "email": "github@aminyazdanpanah.com"
                },
                {
                    "name": "Sana Moniri",
                    "email": "s.mo@quasarstream.com"
                },
                {
                    "name": "Quasar Stream Community"
                },
                {
                    "name": "Daniil Gentili",
                    "email": "daniil@daniil.it"
                }
            ],
            "description": "Pure-PHP SRTP and SRTCP encryption using phpseclib, with AES-CM and AES-GCM profiles. MadelineProto's PHP 8.2+ fork removes the libsrtp/FFI dependency.",
            "homepage": "https://github.com/danog/php-rtc-srtp",
            "keywords": [
                "srtp"
            ],
            "support": {
                "issues": "https://github.com/danog/php-rtc-srtp/issues",
                "source": "https://github.com/danog/php-rtc-srtp/tree/1.1.16"
            },
            "install-path": "../danog/php-rtc-srtp"
        },
        {
            "name": "danog/php-rtc-stats",
            "version": "1.1.7",
            "version_normalized": "1.1.7.0",
            "source": {
                "type": "git",
                "url": "https://github.com/danog/php-rtc-stats.git",
                "reference": "137934d7d57e97184d5b6b2fb58e571e0d3f9fbd"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/danog/php-rtc-stats/zipball/137934d7d57e97184d5b6b2fb58e571e0d3f9fbd",
                "reference": "137934d7d57e97184d5b6b2fb58e571e0d3f9fbd",
                "shasum": ""
            },
            "require": {
                "danog/php-rtc-ice": "^1.1.18 || ^2.0",
                "danog/php-rtc-mixin": "^1.1.6 || ^2.0",
                "php": "^8.2"
            },
            "require-dev": {
                "bamarni/composer-bin-plugin": ">=1.9.1"
            },
            "time": "2026-09-16T11:04:43+00:00",
            "type": "library",
            "extra": {
                "bamarni-bin": {
                    "bin-links": true,
                    "forward-command": true
                }
            },
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "Webrtc\\Stats\\": "src"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "BSD-3-Clause"
            ],
            "authors": [
                {
                    "name": "Amin Yazdanpanah",
                    "email": "github@aminyazdanpanah.com"
                },
                {
                    "name": "Sana Moniri",
                    "email": "s.mo@quasarstream.com"
                },
                {
                    "name": "Quasar Stream Community"
                },
                {
                    "name": "Daniil Gentili",
                    "email": "daniil@daniil.it"
                }
            ],
            "description": "WebRTC statistics objects for RTP, DTLS, ICE, and media metrics. MadelineProto's PHP 8.2+ fork supports the modular danog/php-rtc stack.",
            "homepage": "https://github.com/danog/php-rtc-stats",
            "keywords": [
                "WebRTC",
                "stats"
            ],
            "support": {
                "issues": "https://github.com/danog/php-rtc-stats/issues",
                "source": "https://github.com/danog/php-rtc-stats/tree/1.1.7"
            },
            "install-path": "../danog/php-rtc-stats"
        },
        {
            "name": "danog/php-rtc-stun",
            "version": "2.0.18",
            "version_normalized": "2.0.18.0",
            "source": {
                "type": "git",
                "url": "https://github.com/danog/php-rtc-stun.git",
                "reference": "7f1b48a736e6d7a9ad174e7dc58b794168befed4"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/danog/php-rtc-stun/zipball/7f1b48a736e6d7a9ad174e7dc58b794168befed4",
                "reference": "7f1b48a736e6d7a9ad174e7dc58b794168befed4",
                "shasum": ""
            },
            "require": {
                "amphp/amp": "^3.1.3",
                "amphp/socket": "^2.4.1",
                "danog/php-rtc-exception": "^1.1.5",
                "danog/php-rtc-mixin": "^2.0",
                "php-64bit": "^8.2",
                "psr/log": "^3.0.2",
                "ramsey/uuid": "^4.9.3"
            },
            "require-dev": {
                "bamarni/composer-bin-plugin": "^1.9.1",
                "dg/bypass-finals": "dev-master",
                "mockery/mockery": "^1.6.15",
                "phpunit/phpunit": "^11.5 || ^12.0 || ^13.3.2"
            },
            "time": "2026-10-06T15:41:42+00:00",
            "type": "library",
            "extra": {
                "bamarni-bin": {
                    "bin-links": true,
                    "forward-command": true
                }
            },
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "Webrtc\\STUN\\": "src"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "BSD-3-Clause"
            ],
            "authors": [
                {
                    "name": "Amin Yazdanpanah",
                    "email": "github@aminyazdanpanah.com"
                },
                {
                    "name": "Sana Moniri",
                    "email": "s.mo@quasarstream.com"
                },
                {
                    "name": "Quasar Stream Community"
                },
                {
                    "name": "Daniil Gentili",
                    "email": "daniil@daniil.it"
                }
            ],
            "description": "STUN transactions and NAT discovery for PHP, ported to Amp v3 sockets and fibers. MadelineProto's PHP 8.2+ fork supports the modular danog/php-rtc stack.",
            "homepage": "https://github.com/danog/php-rtc-stun",
            "keywords": [
                "SessionTraversalUtilitiesForNAT",
                "WebRTC",
                "stun"
            ],
            "support": {
                "issues": "https://github.com/danog/php-rtc-stun/issues",
                "source": "https://github.com/danog/php-rtc-stun/tree/2.0.18"
            },
            "install-path": "../danog/php-rtc-stun"
        },
        {
            "name": "danog/php-rtc-turn",
            "version": "2.0.9",
            "version_normalized": "2.0.9.0",
            "source": {
                "type": "git",
                "url": "https://github.com/danog/php-rtc-turn.git",
                "reference": "5789c0d4b23c6097c72812fa8f9e9930ac288bc6"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/danog/php-rtc-turn/zipball/5789c0d4b23c6097c72812fa8f9e9930ac288bc6",
                "reference": "5789c0d4b23c6097c72812fa8f9e9930ac288bc6",
                "shasum": ""
            },
            "require": {
                "amphp/amp": "^3.1.3",
                "amphp/socket": "^2.4.1",
                "danog/php-rtc-exception": "^1.1.5",
                "danog/php-rtc-mixin": "^2.0",
                "danog/php-rtc-stun": "^2.0.17",
                "php": "^8.2",
                "psr/log": "^3.0.2"
            },
            "require-dev": {
                "bamarni/composer-bin-plugin": "^1.9.1",
                "mockery/mockery": "^1.6.15",
                "phpunit/phpunit": "^11.5 || ^12.0 || ^13.3.2"
            },
            "time": "2026-10-06T13:18:16+00:00",
            "type": "library",
            "extra": {
                "bamarni-bin": {
                    "bin-links": true,
                    "forward-command": true
                }
            },
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "Webrtc\\TURN\\": "src"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "BSD-3-Clause"
            ],
            "authors": [
                {
                    "name": "Amin Yazdanpanah",
                    "email": "github@aminyazdanpanah.com"
                },
                {
                    "name": "Sana Moniri",
                    "email": "s.mo@quasarstream.com"
                },
                {
                    "name": "Quasar Stream Community"
                },
                {
                    "name": "Daniil Gentili",
                    "email": "daniil@daniil.it"
                }
            ],
            "description": "TURN relay transport for PHP WebRTC, ported to Amp v3 TCP, UDP, and fibers. MadelineProto's PHP 8.2+ fork supports the modular danog/php-rtc stack.",
            "homepage": "https://github.com/danog/php-rtc-turn",
            "keywords": [
                "WebRTC",
                "traversal-using-relays-around-nat",
                "turn"
            ],
            "support": {
                "issues": "https://github.com/danog/php-rtc-turn/issues",
                "source": "https://github.com/danog/php-rtc-turn/tree/2.0.9"
            },
            "install-path": "../danog/php-rtc-turn"
        },
        {
            "name": "danog/php-rtc-webrtc",
            "version": "2.0.20",
            "version_normalized": "2.0.20.0",
            "source": {
                "type": "git",
                "url": "https://github.com/danog/php-rtc-webrtc.git",
                "reference": "58f8a24180cd1e137127214f9bfba3b0a7eff97b"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/danog/php-rtc-webrtc/zipball/58f8a24180cd1e137127214f9bfba3b0a7eff97b",
                "reference": "58f8a24180cd1e137127214f9bfba3b0a7eff97b",
                "shasum": ""
            },
            "require": {
                "amphp/amp": "^3.1.3",
                "danog/php-rtc-dtls": "^1.1.9 || ^2.0",
                "danog/php-rtc-exception": "^1.1.5",
                "danog/php-rtc-ice": "^2.0.17",
                "danog/php-rtc-mixin": "^2.0",
                "danog/php-rtc-ntp": "^1.1.6",
                "danog/php-rtc-rtp": "^2.0.23",
                "danog/php-rtc-sctp": "^2.0.14",
                "danog/php-rtc-sdp": "^1.1.6",
                "danog/php-rtc-stats": "^1.1.3",
                "php": "^8.2",
                "symfony/polyfill-php84": "*"
            },
            "require-dev": {
                "bamarni/composer-bin-plugin": "^1.9.1",
                "phpunit/phpunit": "^11.5 || ^12.0 || ^13.3.2"
            },
            "time": "2026-10-16T23:52:08+00:00",
            "type": "library",
            "extra": {
                "bamarni-bin": {
                    "bin-links": true,
                    "forward-command": true
                }
            },
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "Webrtc\\Webrtc\\": "src"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "BSD-3-Clause"
            ],
            "authors": [
                {
                    "name": "Amin Yazdanpanah",
                    "email": "github@aminyazdanpanah.com"
                },
                {
                    "name": "Sana Moniri",
                    "email": "s.mo@quasarstream.com"
                },
                {
                    "name": "Quasar Stream Community"
                },
                {
                    "name": "Daniil Gentili",
                    "email": "daniil@daniil.it"
                }
            ],
            "description": "A modular PHP WebRTC stack for PHP 8.2+, ported to Amp v3 with pure-PHP DTLS/SRTP and optional media FFI. Maintained by danog for MadelineProto.",
            "homepage": "https://github.com/danog/php-rtc-webrtc",
            "keywords": [
                "WebRTC",
                "rtp"
            ],
            "support": {
                "issues": "https://github.com/danog/php-rtc-webrtc/issues",
                "source": "https://github.com/danog/php-rtc-webrtc/tree/2.0.20"
            },
            "install-path": "../danog/php-rtc-webrtc"
        },
        {
            "name": "danog/primemodule",
            "version": "1.0.13",
            "version_normalized": "1.0.13.0",
            "source": {
                "type": "git",
                "url": "https://github.com/danog/PrimeModule.git",
                "reference": "e7ebfc1ea64c24186e49064cd4b76bc76c28600e"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/danog/PrimeModule/zipball/e7ebfc1ea64c24186e49064cd4b76bc76c28600e",
                "reference": "e7ebfc1ea64c24186e49064cd4b76bc76c28600e",
                "shasum": ""
            },
            "require": {
                "php": "^8.0"
            },
            "require-dev": {
                "phabel/phabel": "^1"
            },
            "suggest": {
                "ext-primemodule": "Install the native C++ extension for extremely fast factorization (https://prime.madelineproto.xyz)"
            },
            "time": "2023-05-27T14:41:40+00:00",
            "type": "library",
            "extra": {
                "phabel": {
                    "revision": 0
                }
            },
            "installation-source": "dist",
            "autoload": {
                "psr-0": {
                    "danog\\": "lib/"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "AGPL-3.0-only"
            ],
            "authors": [
                {
                    "name": "Daniil Gentili",
                    "email": "daniil@daniil.it"
                }
            ],
            "description": "Prime module capable of doing prime factorization of huge numbers very quickly.\"",
            "support": {
                "issues": "https://github.com/danog/PrimeModule/issues",
                "source": "https://github.com/danog/PrimeModule/tree/1.0.13"
            },
            "install-path": "../danog/primemodule"
        },
        {
            "name": "danog/telegram-entities",
            "version": "1.1.0",
            "version_normalized": "1.1.0.0",
            "source": {
                "type": "git",
                "url": "https://github.com/danog/telegram-entities.git",
                "reference": "92ca7621594b7a6c4a820c3b557e66ee1d439273"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/danog/telegram-entities/zipball/92ca7621594b7a6c4a820c3b557e66ee1d439273",
                "reference": "92ca7621594b7a6c4a820c3b557e66ee1d439273",
                "shasum": ""
            },
            "require": {
                "php-64bit": ">=8.2.4",
                "symfony/polyfill-mbstring": "*",
                "webmozart/assert": "^2"
            },
            "require-dev": {
                "amphp/http-client": "^5.0",
                "amphp/php-cs-fixer-config": "^2.0.1",
                "danog/phpdoc": "^0.1.22",
                "friendsofphp/php-cs-fixer": "^3.52.1",
                "phpunit/phpunit": "^11.0.9",
                "vimeo/psalm": "dev-master"
            },
            "time": "2026-10-18T15:46:15+00:00",
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "danog\\TelegramEntities\\": "src/"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "Apache-2.0"
            ],
            "authors": [
                {
                    "name": "Daniil Gentili",
                    "email": "daniil@daniil.it"
                }
            ],
            "description": "A library to work with Telegram UTF-16 styled text entities.",
            "support": {
                "issues": "https://github.com/danog/telegram-entities/issues",
                "source": "https://github.com/danog/telegram-entities/tree/1.1.0"
            },
            "funding": [
                {
                    "url": "https://github.com/danog",
                    "type": "github"
                }
            ],
            "install-path": "../danog/telegram-entities"
        },
        {
            "name": "danog/tg-dialog-id",
            "version": "2.0.0",
            "version_normalized": "2.0.0.0",
            "source": {
                "type": "git",
                "url": "https://github.com/danog/tg-dialog-id.git",
                "reference": "d03c653b2dad43d739675c83a6e8fa14b3b43636"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/danog/tg-dialog-id/zipball/d03c653b2dad43d739675c83a6e8fa14b3b43636",
                "reference": "d03c653b2dad43d739675c83a6e8fa14b3b43636",
                "shasum": ""
            },
            "require": {
                "php-64bit": ">=8.1"
            },
            "require-dev": {
                "amphp/file": "^3.1",
                "amphp/php-cs-fixer-config": "^2.0.1",
                "danog/phpdoc": "^0.1.22",
                "infection/infection": "^0.28.1",
                "phpunit/phpunit": "^11.0.9|^10",
                "vimeo/psalm": "dev-master"
            },
            "time": "2025-06-28T15:36:24+00:00",
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "danog\\DialogId\\": "src/"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "Apache-2.0"
            ],
            "authors": [
                {
                    "name": "Daniil Gentili",
                    "email": "daniil@daniil.it"
                }
            ],
            "description": "A library to work with Telegram bot API dialog IDs",
            "support": {
                "issues": "https://github.com/danog/tg-dialog-id/issues",
                "source": "https://github.com/danog/tg-dialog-id/tree/2.0.0"
            },
            "funding": [
                {
                    "url": "https://github.com/danog",
                    "type": "github"
                }
            ],
            "install-path": "../danog/tg-dialog-id"
        },
        {
            "name": "danog/tg-file-decoder",
            "version": "1.0.2",
            "version_normalized": "1.0.2.0",
            "source": {
                "type": "git",
                "url": "https://github.com/danog/tg-file-decoder.git",
                "reference": "f4da78c28d47b658c4596249f1d6595956f3b796"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/danog/tg-file-decoder/zipball/f4da78c28d47b658c4596249f1d6595956f3b796",
                "reference": "f4da78c28d47b658c4596249f1d6595956f3b796",
                "shasum": ""
            },
            "require": {
                "php-64bit": ">=8.2"
            },
            "require-dev": {
                "amphp/php-cs-fixer-config": "^2",
                "danog/phpdoc": "^0.1.22",
                "phpunit/phpunit": "^11",
                "vimeo/psalm": "dev-master"
            },
            "time": "2025-05-31T15:14:35+00:00",
            "type": "project",
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "danog\\Decoder\\": "src/"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "AGPL-3.0-only"
            ],
            "authors": [
                {
                    "name": "Daniil Gentili",
                    "email": "daniil@daniil.it"
                }
            ],
            "description": "Decode Telegram bot API file IDs",
            "keywords": [
                "audio",
                "bot api",
                "file ID",
                "files",
                "mtproto",
                "stickers",
                "telegram",
                "video"
            ],
            "support": {
                "issues": "https://github.com/danog/tg-file-decoder/issues",
                "source": "https://github.com/danog/tg-file-decoder/tree/1.0.2"
            },
            "funding": [
                {
                    "url": "https://github.com/danog",
                    "type": "github"
                }
            ],
            "install-path": "../danog/tg-file-decoder"
        },
        {
            "name": "dasprid/enum",
            "version": "1.0.7",
            "version_normalized": "1.0.7.0",
            "source": {
                "type": "git",
                "url": "https://github.com/DASPRiD/Enum.git",
                "reference": "b5874fa9ed0043116c72162ec7f4fb50e02e7cce"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/DASPRiD/Enum/zipball/b5874fa9ed0043116c72162ec7f4fb50e02e7cce",
                "reference": "b5874fa9ed0043116c72162ec7f4fb50e02e7cce",
                "shasum": ""
            },
            "require": {
                "php": ">=7.1 <9.0"
            },
            "require-dev": {
                "phpunit/phpunit": "^7 || ^8 || ^9 || ^10 || ^11",
                "squizlabs/php_codesniffer": "*"
            },
            "time": "2025-09-16T12:23:56+00:00",
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "DASPRiD\\Enum\\": "src/"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "BSD-2-Clause"
            ],
            "authors": [
                {
                    "name": "Ben Scholzen 'DASPRiD'",
                    "email": "mail@dasprids.de",
                    "homepage": "https://dasprids.de/",
                    "role": "Developer"
                }
            ],
            "description": "PHP 7.1 enum implementation",
            "keywords": [
                "enum",
                "map"
            ],
            "support": {
                "issues": "https://github.com/DASPRiD/Enum/issues",
                "source": "https://github.com/DASPRiD/Enum/tree/1.0.7"
            },
            "install-path": "../dasprid/enum"
        },
        {
            "name": "daverandom/libdns",
            "version": "v2.1.0",
            "version_normalized": "2.1.0.0",
            "source": {
                "type": "git",
                "url": "https://github.com/DaveRandom/LibDNS.git",
                "reference": "b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/DaveRandom/LibDNS/zipball/b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a",
                "reference": "b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a",
                "shasum": ""
            },
            "require": {
                "ext-ctype": "*",
                "php": ">=7.1"
            },
            "suggest": {
                "ext-intl": "Required for IDN support"
            },
            "time": "2024-04-12T12:12:48+00:00",
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "files": [
                    "src/functions.php"
                ],
                "psr-4": {
                    "LibDNS\\": "src/"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "description": "DNS protocol implementation written in pure PHP",
            "keywords": [
                "dns"
            ],
            "support": {
                "issues": "https://github.com/DaveRandom/LibDNS/issues",
                "source": "https://github.com/DaveRandom/LibDNS/tree/v2.1.0"
            },
            "install-path": "../daverandom/libdns"
        },
        {
            "name": "kelunik/certificate",
            "version": "v1.1.3",
            "version_normalized": "1.1.3.0",
            "source": {
                "type": "git",
                "url": "https://github.com/kelunik/certificate.git",
                "reference": "7e00d498c264d5eb4f78c69f41c8bd6719c0199e"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/kelunik/certificate/zipball/7e00d498c264d5eb4f78c69f41c8bd6719c0199e",
                "reference": "7e00d498c264d5eb4f78c69f41c8bd6719c0199e",
                "shasum": ""
            },
            "require": {
                "ext-openssl": "*",
                "php": ">=7.0"
            },
            "require-dev": {
                "amphp/php-cs-fixer-config": "^2",
                "phpunit/phpunit": "^6 | 7 | ^8 | ^9"
            },
            "time": "2023-02-03T21:26:53+00:00",
            "type": "library",
            "extra": {
                "branch-alias": {
                    "dev-master": "1.x-dev"
                }
            },
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "Kelunik\\Certificate\\": "src"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Niklas Keller",
                    "email": "me@kelunik.com"
                }
            ],
            "description": "Access certificate details and transform between different formats.",
            "keywords": [
                "DER",
                "certificate",
                "certificates",
                "openssl",
                "pem",
                "x509"
            ],
            "support": {
                "issues": "https://github.com/kelunik/certificate/issues",
                "source": "https://github.com/kelunik/certificate/tree/v1.1.3"
            },
            "install-path": "../kelunik/certificate"
        },
        {
            "name": "league/uri",
            "version": "7.9.0",
            "version_normalized": "7.9.0.0",
            "source": {
                "type": "git",
                "url": "https://github.com/thephpleague/uri.git",
                "reference": "fcc872a3d872aa13f935e0cd1887b9abb06da07f"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/thephpleague/uri/zipball/fcc872a3d872aa13f935e0cd1887b9abb06da07f",
                "reference": "fcc872a3d872aa13f935e0cd1887b9abb06da07f",
                "shasum": ""
            },
            "require": {
                "ext-mbstring": "*",
                "league/uri-interfaces": "^7.9.0",
                "php": "^8.1",
                "psr/http-factory": "^1"
            },
            "conflict": {
                "league/uri-schemes": "^1.0"
            },
            "suggest": {
                "ext-bcmath": "to improve IPV4 host parsing",
                "ext-dom": "to convert the URI into an HTML anchor tag",
                "ext-fileinfo": "to create Data URI from file contennts",
                "ext-gmp": "to improve IPV4 host parsing",
                "ext-intl": "to handle IDN host with the best performance",
                "ext-uri": "to use the PHP native URI class",
                "jeremykendall/php-domain-parser": "to further parse the URI host and resolve its Public Suffix and Top Level Domain",
                "league/uri-components": "to provide additional tools to manipulate URI objects components",
                "league/uri-polyfill": "to backport the PHP URI extension for older versions of PHP",
                "php-64bit": "to improve IPV4 host parsing",
                "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification",
                "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present"
            },
            "time": "2026-10-16T15:19:33+00:00",
            "type": "library",
            "extra": {
                "branch-alias": {
                    "dev-master": "7.x-dev"
                }
            },
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "League\\Uri\\": ""
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Ignace Nyamagana Butera",
                    "email": "nyamsprod@gmail.com",
                    "homepage": "https://nyamsprod.com"
                }
            ],
            "description": "URI manipulation library",
            "homepage": "https://uri.thephpleague.com",
            "keywords": [
                "URN",
                "data-uri",
                "file-uri",
                "ftp",
                "hostname",
                "http",
                "https",
                "middleware",
                "parse_str",
                "parse_url",
                "psr-7",
                "query-string",
                "querystring",
                "rfc2141",
                "rfc3986",
                "rfc3987",
                "rfc6570",
                "rfc8141",
                "uri",
                "uri-template",
                "url",
                "ws"
            ],
            "support": {
                "docs": "https://uri.thephpleague.com",
                "forum": "https://thephpleague.slack.com",
                "issues": "https://github.com/thephpleague/uri-src/issues",
                "source": "https://github.com/thephpleague/uri/tree/7.9.0"
            },
            "funding": [
                {
                    "url": "https://github.com/sponsors/nyamsprod",
                    "type": "github"
                }
            ],
            "install-path": "../league/uri"
        },
        {
            "name": "league/uri-components",
            "version": "7.9.0",
            "version_normalized": "7.9.0.0",
            "source": {
                "type": "git",
                "url": "https://github.com/thephpleague/uri-components.git",
                "reference": "4e37c1663f7d0daebbab6b3d947631b0835b3943"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/thephpleague/uri-components/zipball/4e37c1663f7d0daebbab6b3d947631b0835b3943",
                "reference": "4e37c1663f7d0daebbab6b3d947631b0835b3943",
                "shasum": ""
            },
            "require": {
                "league/uri": "^7.9.0",
                "php": "^8.1"
            },
            "suggest": {
                "ext-bcmath": "to improve IPV4 host parsing",
                "ext-fileinfo": "to create Data URI from file contennts",
                "ext-gmp": "to improve IPV4 host parsing",
                "ext-intl": "to handle IDN host with the best performance",
                "ext-mbstring": "to use the sorting algorithm of URLSearchParams",
                "jeremykendall/php-domain-parser": "to further parse the URI host and resolve its Public Suffix and Top Level Domain",
                "league/uri-polyfill": "to backport the PHP URI extension for older versions of PHP",
                "php-64bit": "to improve IPV4 host parsing",
                "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification",
                "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present"
            },
            "time": "2026-10-16T15:19:33+00:00",
            "type": "library",
            "extra": {
                "branch-alias": {
                    "dev-master": "7.x-dev"
                }
            },
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "League\\Uri\\": ""
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Ignace Nyamagana Butera",
                    "email": "nyamsprod@gmail.com",
                    "homepage": "https://nyamsprod.com"
                }
            ],
            "description": "URI components manipulation library",
            "homepage": "http://uri.thephpleague.com",
            "keywords": [
                "authority",
                "components",
                "fragment",
                "host",
                "middleware",
                "modifier",
                "path",
                "port",
                "query",
                "rfc3986",
                "scheme",
                "uri",
                "url",
                "userinfo"
            ],
            "support": {
                "docs": "https://uri.thephpleague.com",
                "forum": "https://thephpleague.slack.com",
                "issues": "https://github.com/thephpleague/uri-src/issues",
                "source": "https://github.com/thephpleague/uri-components/tree/7.9.0"
            },
            "funding": [
                {
                    "url": "https://github.com/nyamsprod",
                    "type": "github"
                }
            ],
            "install-path": "../league/uri-components"
        },
        {
            "name": "league/uri-interfaces",
            "version": "7.9.0",
            "version_normalized": "7.9.0.0",
            "source": {
                "type": "git",
                "url": "https://github.com/thephpleague/uri-interfaces.git",
                "reference": "eb5f6d24e6a4fe0a8e9c78ded4eb1dfff1404f58"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/eb5f6d24e6a4fe0a8e9c78ded4eb1dfff1404f58",
                "reference": "eb5f6d24e6a4fe0a8e9c78ded4eb1dfff1404f58",
                "shasum": ""
            },
            "require": {
                "ext-filter": "*",
                "php": "^8.1",
                "psr/http-message": "^1.1 || ^2.0"
            },
            "suggest": {
                "ext-bcmath": "to improve IPV4 host parsing",
                "ext-gmp": "to improve IPV4 host parsing",
                "ext-intl": "to handle IDN host with the best performance",
                "php-64bit": "to improve IPV4 host parsing",
                "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification",
                "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present"
            },
            "time": "2026-10-16T15:19:33+00:00",
            "type": "library",
            "extra": {
                "branch-alias": {
                    "dev-master": "7.x-dev"
                }
            },
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "League\\Uri\\": ""
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Ignace Nyamagana Butera",
                    "email": "nyamsprod@gmail.com",
                    "homepage": "https://nyamsprod.com"
                }
            ],
            "description": "Common tools for parsing and resolving RFC3987/RFC3986 URI",
            "homepage": "https://uri.thephpleague.com",
            "keywords": [
                "data-uri",
                "file-uri",
                "ftp",
                "hostname",
                "http",
                "https",
                "parse_str",
                "parse_url",
                "psr-7",
                "query-string",
                "querystring",
                "rfc3986",
                "rfc3987",
                "rfc6570",
                "uri",
                "url",
                "ws"
            ],
            "support": {
                "docs": "https://uri.thephpleague.com",
                "forum": "https://thephpleague.slack.com",
                "issues": "https://github.com/thephpleague/uri-src/issues",
                "source": "https://github.com/thephpleague/uri-interfaces/tree/7.9.0"
            },
            "funding": [
                {
                    "url": "https://github.com/sponsors/nyamsprod",
                    "type": "github"
                }
            ],
            "install-path": "../league/uri-interfaces"
        },
        {
            "name": "monolog/monolog",
            "version": "3.12.1",
            "version_normalized": "3.12.1.0",
            "source": {
                "type": "git",
                "url": "https://github.com/Seldaek/monolog.git",
                "reference": "d7059e4c691d05ea1222b5124bed0f961be46e15"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/Seldaek/monolog/zipball/d7059e4c691d05ea1222b5124bed0f961be46e15",
                "reference": "d7059e4c691d05ea1222b5124bed0f961be46e15",
                "shasum": ""
            },
            "require": {
                "php": ">=8.1",
                "psr/log": "^2.0 || ^3.0"
            },
            "provide": {
                "psr/log-implementation": "3.0.0"
            },
            "require-dev": {
                "aws/aws-sdk-php": "^3.0",
                "doctrine/couchdb": "~1.0@dev",
                "elasticsearch/elasticsearch": "^7 || ^8",
                "ext-json": "*",
                "graylog2/gelf-php": "^1.4.2 || ^2.0",
                "guzzlehttp/guzzle": "^7.4.5",
                "guzzlehttp/psr7": "^2.2",
                "mongodb/mongodb": "^1.8 || ^2.0",
                "php-amqplib/php-amqplib": "~2.4 || ^3",
                "php-console/php-console": "^3.1.8",
                "phpstan/phpstan": "^2",
                "phpstan/phpstan-deprecation-rules": "^2",
                "phpstan/phpstan-strict-rules": "^2",
                "phpunit/phpunit": "^10.5.17 || ^11.0.7",
                "predis/predis": "^1.1 || ^2",
                "psr/clock": "^1.0",
                "rollbar/rollbar": "^4.0",
                "ruflin/elastica": "^7 || ^8",
                "symfony/mailer": "^5.4 || ^6",
                "symfony/mime": "^5.4 || ^6"
            },
            "suggest": {
                "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB",
                "doctrine/couchdb": "Allow sending log messages to a CouchDB server",
                "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client",
                "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)",
                "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler",
                "ext-mbstring": "Allow to work properly with unicode symbols",
                "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)",
                "ext-openssl": "Required to send log messages using SSL",
                "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)",
                "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server",
                "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)",
                "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib",
                "psr/clock": "Required to pass a clock to the Logger and control the timestamp of log records",
                "rollbar/rollbar": "Allow sending log messages to Rollbar",
                "ruflin/elastica": "Allow sending log messages to an Elastic Search server"
            },
            "time": "2026-09-29T19:21:12+00:00",
            "type": "library",
            "extra": {
                "branch-alias": {
                    "dev-main": "3.x-dev"
                }
            },
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "Monolog\\": "src/Monolog"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Jordi Boggiano",
                    "email": "j.boggiano@seld.be",
                    "homepage": "https://seld.be"
                }
            ],
            "description": "Sends your logs to files, sockets, inboxes, databases and various web services",
            "homepage": "https://github.com/Seldaek/monolog",
            "keywords": [
                "log",
                "logging",
                "psr-3"
            ],
            "support": {
                "issues": "https://github.com/Seldaek/monolog/issues",
                "source": "https://github.com/Seldaek/monolog/tree/3.12.1"
            },
            "funding": [
                {
                    "url": "https://github.com/Seldaek",
                    "type": "github"
                },
                {
                    "url": "https://tidelift.com/funding/github/packagist/monolog/monolog",
                    "type": "tidelift"
                }
            ],
            "install-path": "../monolog/monolog"
        },
        {
            "name": "nikic/php-parser",
            "version": "v5.9.0",
            "version_normalized": "5.9.0.0",
            "source": {
                "type": "git",
                "url": "https://github.com/nikic/PHP-Parser.git",
                "reference": "9e33da9553fe7786f0962b35f4e4ecf01be89def"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/9e33da9553fe7786f0962b35f4e4ecf01be89def",
                "reference": "9e33da9553fe7786f0962b35f4e4ecf01be89def",
                "shasum": ""
            },
            "require": {
                "ext-json": "*",
                "ext-tokenizer": "*",
                "php": ">=7.4"
            },
            "require-dev": {
                "ircmaxell/php-yacc": "^0.0.7",
                "phpunit/phpunit": "^9.0"
            },
            "time": "2026-09-13T18:51:52+00:00",
            "bin": [
                "bin/php-parse"
            ],
            "type": "library",
            "extra": {
                "branch-alias": {
                    "dev-master": "5.x-dev"
                }
            },
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "PhpParser\\": "lib/PhpParser"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "BSD-3-Clause"
            ],
            "authors": [
                {
                    "name": "Nikita Popov"
                }
            ],
            "description": "A PHP parser written in PHP",
            "keywords": [
                "parser",
                "php"
            ],
            "support": {
                "issues": "https://github.com/nikic/PHP-Parser/issues",
                "source": "https://github.com/nikic/PHP-Parser/tree/v5.9.0"
            },
            "install-path": "../nikic/php-parser"
        },
        {
            "name": "paragonie/constant_time_encoding",
            "version": "v3.1.3",
            "version_normalized": "3.1.3.0",
            "source": {
                "type": "git",
                "url": "https://github.com/paragonie/constant_time_encoding.git",
                "reference": "d5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/d5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77",
                "reference": "d5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77",
                "shasum": ""
            },
            "require": {
                "php": "^8"
            },
            "require-dev": {
                "infection/infection": "^0",
                "nikic/php-fuzzer": "^0",
                "phpunit/phpunit": "^9|^10|^11",
                "vimeo/psalm": "^4|^5|^6"
            },
            "time": "2025-09-24T15:06:41+00:00",
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "ParagonIE\\ConstantTime\\": "src/"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Paragon Initiative Enterprises",
                    "email": "security@paragonie.com",
                    "homepage": "https://paragonie.com",
                    "role": "Maintainer"
                },
                {
                    "name": "Steve 'Sc00bz' Thomas",
                    "email": "steve@tobtu.com",
                    "homepage": "https://www.tobtu.com",
                    "role": "Original Developer"
                }
            ],
            "description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)",
            "keywords": [
                "base16",
                "base32",
                "base32_decode",
                "base32_encode",
                "base64",
                "base64_decode",
                "base64_encode",
                "bin2hex",
                "encoding",
                "hex",
                "hex2bin",
                "rfc4648"
            ],
            "support": {
                "email": "info@paragonie.com",
                "issues": "https://github.com/paragonie/constant_time_encoding/issues",
                "source": "https://github.com/paragonie/constant_time_encoding"
            },
            "install-path": "../paragonie/constant_time_encoding"
        },
        {
            "name": "phpseclib/phpseclib",
            "version": "4.0.2",
            "version_normalized": "4.0.2.0",
            "source": {
                "type": "git",
                "url": "https://github.com/phpseclib/phpseclib.git",
                "reference": "1fe20b9acb08ce810cec24d8a9d02e7c13426e4c"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/1fe20b9acb08ce810cec24d8a9d02e7c13426e4c",
                "reference": "1fe20b9acb08ce810cec24d8a9d02e7c13426e4c",
                "shasum": ""
            },
            "require": {
                "paragonie/constant_time_encoding": "^2|^3",
                "php": ">=8.1",
                "symfony/polyfill-php82": "^1.26"
            },
            "require-dev": {
                "brianium/paratest": "^7.22",
                "ext-xml": "*",
                "php-parallel-lint/php-parallel-lint": "^1.3",
                "phpunit/phpunit": "^13",
                "squizlabs/php_codesniffer": "^3.7",
                "vimeo/psalm": "*"
            },
            "suggest": {
                "ext-dom": "Install the DOM extension to load XML formatted public keys.",
                "ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.",
                "ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.",
                "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations."
            },
            "time": "2026-10-07T19:55:38+00:00",
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "files": [
                    "phpseclib/bootstrap.php"
                ],
                "psr-4": {
                    "phpseclib4\\": "phpseclib/"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Jim Wigginton",
                    "email": "terrafrost@php.net",
                    "role": "Lead Developer"
                },
                {
                    "name": "Patrick Monnerat",
                    "email": "pm@datasphere.ch",
                    "role": "Developer"
                },
                {
                    "name": "Andreas Fischer",
                    "email": "bantu@phpbb.com",
                    "role": "Developer"
                },
                {
                    "name": "Hans-JÃ¼rgen Petrich",
                    "email": "petrich@tronic-media.com",
                    "role": "Developer"
                },
                {
                    "name": "Graham Campbell",
                    "email": "graham@alt-three.com",
                    "role": "Developer"
                },
                {
                    "name": "Jack Worman",
                    "email": "jack.worman@gmail.com",
                    "homepage": "https://jackworman.com",
                    "role": "Developer"
                }
            ],
            "description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.",
            "homepage": "https://phpseclib.com/",
            "keywords": [
                "BigInteger",
                "aes",
                "asn.1",
                "asn1",
                "blowfish",
                "crypto",
                "cryptography",
                "encryption",
                "rsa",
                "security",
                "sftp",
                "signature",
                "signing",
                "ssh",
                "twofish",
                "x.509",
                "x509"
            ],
            "support": {
                "issues": "https://github.com/phpseclib/phpseclib/issues",
                "source": "https://github.com/phpseclib/phpseclib/tree/4.0.2"
            },
            "funding": [
                {
                    "url": "https://github.com/terrafrost",
                    "type": "github"
                },
                {
                    "url": "https://www.patreon.com/phpseclib",
                    "type": "patreon"
                },
                {
                    "url": "https://tidelift.com/funding/github/packagist/phpseclib/phpseclib",
                    "type": "tidelift"
                }
            ],
            "install-path": "../phpseclib/phpseclib"
        },
        {
            "name": "promphp/prometheus_client_php",
            "version": "v2.15.1",
            "version_normalized": "2.15.1.0",
            "source": {
                "type": "git",
                "url": "https://github.com/PromPHP/prometheus_client_php.git",
                "reference": "e153786bda5ca1d07335a2b5f477742c37195c40"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/PromPHP/prometheus_client_php/zipball/e153786bda5ca1d07335a2b5f477742c37195c40",
                "reference": "e153786bda5ca1d07335a2b5f477742c37195c40",
                "shasum": ""
            },
            "require": {
                "ext-json": "*",
                "php": "^8.2"
            },
            "replace": {
                "endclothing/prometheus_client_php": "*",
                "jimdo/prometheus_client_php": "*",
                "lkaemmerling/prometheus_client_php": "*"
            },
            "require-dev": {
                "guzzlehttp/guzzle": "^6.3|^7.0",
                "phpstan/extension-installer": "^1.0",
                "phpstan/phpstan": "^1.5.4",
                "phpstan/phpstan-phpunit": "^1.1.0",
                "phpstan/phpstan-strict-rules": "^1.1.0",
                "phpunit/phpunit": "^9.4",
                "predis/predis": "^2.3",
                "squizlabs/php_codesniffer": "^3.6 || ^4.0",
                "symfony/polyfill-apcu": "^1.6"
            },
            "suggest": {
                "ext-apc": "Required if using APCu.",
                "ext-pdo": "Required if using PDO.",
                "ext-redis": "Required if using Redis.",
                "predis/predis": "Required if using Predis.",
                "promphp/prometheus_push_gateway_php": "An easy client for using Prometheus PushGateway.",
                "symfony/polyfill-apcu": "Required if you use APCu."
            },
            "time": "2026-05-28T07:51:04+00:00",
            "type": "library",
            "extra": {
                "branch-alias": {
                    "dev-master": "1.0-dev"
                }
            },
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "Prometheus\\": "src/Prometheus/"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "Apache-2.0"
            ],
            "authors": [
                {
                    "name": "Lukas KÃ¤mmerling",
                    "email": "kontakt@lukas-kaemmerling.de"
                }
            ],
            "description": "Prometheus instrumentation library for PHP applications.",
            "support": {
                "issues": "https://github.com/PromPHP/prometheus_client_php/issues",
                "source": "https://github.com/PromPHP/prometheus_client_php/tree/v2.15.1"
            },
            "install-path": "../promphp/prometheus_client_php"
        },
        {
            "name": "psr/http-factory",
            "version": "1.1.0",
            "version_normalized": "1.1.0.0",
            "source": {
                "type": "git",
                "url": "https://github.com/php-fig/http-factory.git",
                "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
                "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
                "shasum": ""
            },
            "require": {
                "php": ">=7.1",
                "psr/http-message": "^1.0 || ^2.0"
            },
            "time": "2024-04-15T12:06:14+00:00",
            "type": "library",
            "extra": {
                "branch-alias": {
                    "dev-master": "1.0.x-dev"
                }
            },
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "Psr\\Http\\Message\\": "src/"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "PHP-FIG",
                    "homepage": "https://www.php-fig.org/"
                }
            ],
            "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories",
            "keywords": [
                "factory",
                "http",
                "message",
                "psr",
                "psr-17",
                "psr-7",
                "request",
                "response"
            ],
            "support": {
                "source": "https://github.com/php-fig/http-factory"
            },
            "install-path": "../psr/http-factory"
        },
        {
            "name": "psr/http-message",
            "version": "2.0",
            "version_normalized": "2.0.0.0",
            "source": {
                "type": "git",
                "url": "https://github.com/php-fig/http-message.git",
                "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71",
                "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71",
                "shasum": ""
            },
            "require": {
                "php": "^7.2 || ^8.0"
            },
            "time": "2023-04-04T09:54:51+00:00",
            "type": "library",
            "extra": {
                "branch-alias": {
                    "dev-master": "2.0.x-dev"
                }
            },
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "Psr\\Http\\Message\\": "src/"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "PHP-FIG",
                    "homepage": "https://www.php-fig.org/"
                }
            ],
            "description": "Common interface for HTTP messages",
            "homepage": "https://github.com/php-fig/http-message",
            "keywords": [
                "http",
                "http-message",
                "psr",
                "psr-7",
                "request",
                "response"
            ],
            "support": {
                "source": "https://github.com/php-fig/http-message/tree/2.0"
            },
            "install-path": "../psr/http-message"
        },
        {
            "name": "psr/log",
            "version": "3.0.2",
            "version_normalized": "3.0.2.0",
            "source": {
                "type": "git",
                "url": "https://github.com/php-fig/log.git",
                "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3",
                "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3",
                "shasum": ""
            },
            "require": {
                "php": ">=8.0.0"
            },
            "time": "2024-09-11T13:17:53+00:00",
            "type": "library",
            "extra": {
                "branch-alias": {
                    "dev-master": "3.x-dev"
                }
            },
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "Psr\\Log\\": "src"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "PHP-FIG",
                    "homepage": "https://www.php-fig.org/"
                }
            ],
            "description": "Common interface for logging libraries",
            "homepage": "https://github.com/php-fig/log",
            "keywords": [
                "log",
                "psr",
                "psr-3"
            ],
            "support": {
                "source": "https://github.com/php-fig/log/tree/3.0.2"
            },
            "install-path": "../psr/log"
        },
        {
            "name": "ramsey/collection",
            "version": "2.1.1",
            "version_normalized": "2.1.1.0",
            "source": {
                "type": "git",
                "url": "https://github.com/ramsey/collection.git",
                "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/ramsey/collection/zipball/344572933ad0181accbf4ba763e85a0306a8c5e2",
                "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2",
                "shasum": ""
            },
            "require": {
                "php": "^8.1"
            },
            "require-dev": {
                "captainhook/plugin-composer": "^5.3",
                "ergebnis/composer-normalize": "^2.45",
                "fakerphp/faker": "^1.24",
                "hamcrest/hamcrest-php": "^2.0",
                "jangregor/phpstan-prophecy": "^2.1",
                "mockery/mockery": "^1.6",
                "php-parallel-lint/php-console-highlighter": "^1.0",
                "php-parallel-lint/php-parallel-lint": "^1.4",
                "phpspec/prophecy-phpunit": "^2.3",
                "phpstan/extension-installer": "^1.4",
                "phpstan/phpstan": "^2.1",
                "phpstan/phpstan-mockery": "^2.0",
                "phpstan/phpstan-phpunit": "^2.0",
                "phpunit/phpunit": "^10.5",
                "ramsey/coding-standard": "^2.3",
                "ramsey/conventional-commits": "^1.6",
                "roave/security-advisories": "dev-latest"
            },
            "time": "2025-03-22T05:38:12+00:00",
            "type": "library",
            "extra": {
                "captainhook": {
                    "force-install": true
                },
                "ramsey/conventional-commits": {
                    "configFile": "conventional-commits.json"
                }
            },
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "Ramsey\\Collection\\": "src/"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Ben Ramsey",
                    "email": "ben@benramsey.com",
                    "homepage": "https://benramsey.com"
                }
            ],
            "description": "A PHP library for representing and manipulating collections.",
            "keywords": [
                "array",
                "collection",
                "hash",
                "map",
                "queue",
                "set"
            ],
            "support": {
                "issues": "https://github.com/ramsey/collection/issues",
                "source": "https://github.com/ramsey/collection/tree/2.1.1"
            },
            "install-path": "../ramsey/collection"
        },
        {
            "name": "ramsey/uuid",
            "version": "4.9.4",
            "version_normalized": "4.9.4.0",
            "source": {
                "type": "git",
                "url": "https://github.com/ramsey/uuid.git",
                "reference": "75d73f48d02797c2c285a7e9f348fadc0102ffe2"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/ramsey/uuid/zipball/75d73f48d02797c2c285a7e9f348fadc0102ffe2",
                "reference": "75d73f48d02797c2c285a7e9f348fadc0102ffe2",
                "shasum": ""
            },
            "require": {
                "brick/math": "^0.8.16 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13 || ^0.14 || ^0.15 || ^0.16 || ^0.17 || ^0.18 || ^0.19 || ^0.20 || ^1.0",
                "php": "^8.0",
                "ramsey/collection": "^1.2 || ^2.0"
            },
            "replace": {
                "rhumsaa/uuid": "self.version"
            },
            "require-dev": {
                "captainhook/captainhook": "^5.25",
                "captainhook/plugin-composer": "^5.3",
                "dealerdirect/phpcodesniffer-composer-installer": "^1.0",
                "ergebnis/composer-normalize": "^2.47",
                "mockery/mockery": "^1.6",
                "paragonie/random-lib": "^2",
                "php-mock/php-mock": "^2.6",
                "php-mock/php-mock-mockery": "^1.5",
                "php-parallel-lint/php-parallel-lint": "^1.4.0",
                "phpbench/phpbench": "^1.2.14",
                "phpstan/extension-installer": "^1.4",
                "phpstan/phpstan": "^2.1",
                "phpstan/phpstan-mockery": "^2.0",
                "phpstan/phpstan-phpunit": "^2.0",
                "phpunit/phpunit": "^9.6",
                "slevomat/coding-standard": "^8.18",
                "squizlabs/php_codesniffer": "^3.13"
            },
            "suggest": {
                "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.",
                "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.",
                "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.",
                "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter",
                "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type."
            },
            "time": "2026-09-16T11:39:30+00:00",
            "type": "library",
            "extra": {
                "captainhook": {
                    "force-install": true
                }
            },
            "installation-source": "dist",
            "autoload": {
                "files": [
                    "src/functions.php"
                ],
                "psr-4": {
                    "Ramsey\\Uuid\\": "src/"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).",
            "keywords": [
                "guid",
                "identifier",
                "uuid"
            ],
            "support": {
                "issues": "https://github.com/ramsey/uuid/issues",
                "source": "https://github.com/ramsey/uuid/tree/4.9.4"
            },
            "install-path": "../ramsey/uuid"
        },
        {
            "name": "revolt/event-loop",
            "version": "v1.0.9",
            "version_normalized": "1.0.9.0",
            "source": {
                "type": "git",
                "url": "https://github.com/revoltphp/event-loop.git",
                "reference": "44061cf513e53c6200372fc935ac42271566295d"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/revoltphp/event-loop/zipball/44061cf513e53c6200372fc935ac42271566295d",
                "reference": "44061cf513e53c6200372fc935ac42271566295d",
                "shasum": ""
            },
            "require": {
                "php": ">=8.1"
            },
            "require-dev": {
                "ext-json": "*",
                "jetbrains/phpstorm-stubs": "^2019.3",
                "phpunit/phpunit": "^9",
                "psalm/phar": "6.16.*"
            },
            "time": "2026-05-16T17:55:38+00:00",
            "type": "library",
            "extra": {
                "branch-alias": {
                    "dev-main": "1.x-dev"
                }
            },
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "Revolt\\": "src"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Aaron Piotrowski",
                    "email": "aaron@trowski.com"
                },
                {
                    "name": "Cees-Jan Kiewiet",
                    "email": "ceesjank@gmail.com"
                },
                {
                    "name": "Christian LÃ¼ck",
                    "email": "christian@clue.engineering"
                },
                {
                    "name": "Niklas Keller",
                    "email": "me@kelunik.com"
                }
            ],
            "description": "Rock-solid event loop for concurrent PHP applications.",
            "keywords": [
                "async",
                "asynchronous",
                "concurrency",
                "event",
                "event-loop",
                "non-blocking",
                "scheduler"
            ],
            "support": {
                "issues": "https://github.com/revoltphp/event-loop/issues",
                "source": "https://github.com/revoltphp/event-loop/tree/v1.0.9"
            },
            "install-path": "../revolt/event-loop"
        },
        {
            "name": "symfony/polyfill-iconv",
            "version": "v1.43.0",
            "version_normalized": "1.43.0.0",
            "source": {
                "type": "git",
                "url": "https://github.com/symfony/polyfill-iconv.git",
                "reference": "254724d95fe01b679cd97d88786e9623ee00bfb7"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/254724d95fe01b679cd97d88786e9623ee00bfb7",
                "reference": "254724d95fe01b679cd97d88786e9623ee00bfb7",
                "shasum": ""
            },
            "require": {
                "php": ">=7.2"
            },
            "provide": {
                "ext-iconv": "*"
            },
            "suggest": {
                "ext-iconv": "For best performance"
            },
            "time": "2026-09-29T06:25:14+00:00",
            "type": "library",
            "extra": {
                "thanks": {
                    "url": "https://github.com/symfony/polyfill",
                    "name": "symfony/polyfill"
                }
            },
            "installation-source": "dist",
            "autoload": {
                "files": [
                    "bootstrap.php"
                ],
                "psr-4": {
                    "Symfony\\Polyfill\\Iconv\\": ""
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Nicolas Grekas",
                    "email": "p@tchwork.com"
                },
                {
                    "name": "Symfony Community",
                    "homepage": "https://symfony.com/contributors"
                }
            ],
            "description": "Symfony polyfill for the Iconv extension",
            "homepage": "https://symfony.com",
            "keywords": [
                "compatibility",
                "iconv",
                "polyfill",
                "portable",
                "shim"
            ],
            "support": {
                "source": "https://github.com/symfony/polyfill-iconv/tree/v1.43.0"
            },
            "funding": [
                {
                    "url": "https://symfony.com/sponsor",
                    "type": "custom"
                },
                {
                    "url": "https://github.com/fabpot",
                    "type": "github"
                },
                {
                    "url": "https://github.com/nicolas-grekas",
                    "type": "github"
                },
                {
                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
                    "type": "tidelift"
                }
            ],
            "install-path": "../symfony/polyfill-iconv"
        },
        {
            "name": "symfony/polyfill-mbstring",
            "version": "v1.43.0",
            "version_normalized": "1.43.0.0",
            "source": {
                "type": "git",
                "url": "https://github.com/symfony/polyfill-mbstring.git",
                "reference": "4d5d331bf731e0aedbc6b2f5e61135b7b7996a72"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/4d5d331bf731e0aedbc6b2f5e61135b7b7996a72",
                "reference": "4d5d331bf731e0aedbc6b2f5e61135b7b7996a72",
                "shasum": ""
            },
            "require": {
                "ext-iconv": "*",
                "php": ">=7.2"
            },
            "provide": {
                "ext-mbstring": "*"
            },
            "suggest": {
                "ext-mbstring": "For best performance"
            },
            "time": "2026-09-24T15:19:04+00:00",
            "type": "library",
            "extra": {
                "thanks": {
                    "url": "https://github.com/symfony/polyfill",
                    "name": "symfony/polyfill"
                }
            },
            "installation-source": "dist",
            "autoload": {
                "files": [
                    "bootstrap.php"
                ],
                "psr-4": {
                    "Symfony\\Polyfill\\Mbstring\\": ""
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Nicolas Grekas",
                    "email": "p@tchwork.com"
                },
                {
                    "name": "Symfony Community",
                    "homepage": "https://symfony.com/contributors"
                }
            ],
            "description": "Symfony polyfill for the Mbstring extension",
            "homepage": "https://symfony.com",
            "keywords": [
                "compatibility",
                "mbstring",
                "polyfill",
                "portable",
                "shim"
            ],
            "support": {
                "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.43.0"
            },
            "funding": [
                {
                    "url": "https://symfony.com/sponsor",
                    "type": "custom"
                },
                {
                    "url": "https://github.com/fabpot",
                    "type": "github"
                },
                {
                    "url": "https://github.com/nicolas-grekas",
                    "type": "github"
                },
                {
                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
                    "type": "tidelift"
                }
            ],
            "install-path": "../symfony/polyfill-mbstring"
        },
        {
            "name": "symfony/polyfill-php82",
            "version": "v1.43.0",
            "version_normalized": "1.43.0.0",
            "source": {
                "type": "git",
                "url": "https://github.com/symfony/polyfill-php82.git",
                "reference": "843bbe8b2a7934307b749751eede0f111f5d1cfc"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/symfony/polyfill-php82/zipball/843bbe8b2a7934307b749751eede0f111f5d1cfc",
                "reference": "843bbe8b2a7934307b749751eede0f111f5d1cfc",
                "shasum": ""
            },
            "require": {
                "php": ">=7.2"
            },
            "time": "2026-09-25T15:50:05+00:00",
            "type": "library",
            "extra": {
                "thanks": {
                    "url": "https://github.com/symfony/polyfill",
                    "name": "symfony/polyfill"
                }
            },
            "installation-source": "dist",
            "autoload": {
                "files": [
                    "bootstrap.php"
                ],
                "psr-4": {
                    "Symfony\\Polyfill\\Php82\\": ""
                },
                "classmap": [
                    "Resources/stubs"
                ]
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Nicolas Grekas",
                    "email": "p@tchwork.com"
                },
                {
                    "name": "Symfony Community",
                    "homepage": "https://symfony.com/contributors"
                }
            ],
            "description": "Symfony polyfill backporting some PHP 8.2+ features to lower PHP versions",
            "homepage": "https://symfony.com",
            "keywords": [
                "compatibility",
                "polyfill",
                "portable",
                "shim"
            ],
            "support": {
                "source": "https://github.com/symfony/polyfill-php82/tree/v1.43.0"
            },
            "funding": [
                {
                    "url": "https://symfony.com/sponsor",
                    "type": "custom"
                },
                {
                    "url": "https://github.com/fabpot",
                    "type": "github"
                },
                {
                    "url": "https://github.com/nicolas-grekas",
                    "type": "github"
                },
                {
                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
                    "type": "tidelift"
                }
            ],
            "install-path": "../symfony/polyfill-php82"
        },
        {
            "name": "symfony/polyfill-php83",
            "version": "v1.43.0",
            "version_normalized": "1.43.0.0",
            "source": {
                "type": "git",
                "url": "https://github.com/symfony/polyfill-php83.git",
                "reference": "80ccff923a8d61f73ebe9da0aa94e25232f004ce"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/80ccff923a8d61f73ebe9da0aa94e25232f004ce",
                "reference": "80ccff923a8d61f73ebe9da0aa94e25232f004ce",
                "shasum": ""
            },
            "require": {
                "php": ">=7.2"
            },
            "time": "2026-09-25T15:50:05+00:00",
            "type": "library",
            "extra": {
                "thanks": {
                    "url": "https://github.com/symfony/polyfill",
                    "name": "symfony/polyfill"
                }
            },
            "installation-source": "dist",
            "autoload": {
                "files": [
                    "bootstrap.php"
                ],
                "psr-4": {
                    "Symfony\\Polyfill\\Php83\\": ""
                },
                "classmap": [
                    "Resources/stubs"
                ]
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Nicolas Grekas",
                    "email": "p@tchwork.com"
                },
                {
                    "name": "Symfony Community",
                    "homepage": "https://symfony.com/contributors"
                }
            ],
            "description": "Symfony polyfill backporting some PHP 8.3+ features to lower PHP versions",
            "homepage": "https://symfony.com",
            "keywords": [
                "compatibility",
                "polyfill",
                "portable",
                "shim"
            ],
            "support": {
                "source": "https://github.com/symfony/polyfill-php83/tree/v1.43.0"
            },
            "funding": [
                {
                    "url": "https://symfony.com/sponsor",
                    "type": "custom"
                },
                {
                    "url": "https://github.com/fabpot",
                    "type": "github"
                },
                {
                    "url": "https://github.com/nicolas-grekas",
                    "type": "github"
                },
                {
                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
                    "type": "tidelift"
                }
            ],
            "install-path": "../symfony/polyfill-php83"
        },
        {
            "name": "symfony/polyfill-php84",
            "version": "v1.43.0",
            "version_normalized": "1.43.0.0",
            "source": {
                "type": "git",
                "url": "https://github.com/symfony/polyfill-php84.git",
                "reference": "22c6548499cbe5c5bb072fc0c42312dff062a015"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/22c6548499cbe5c5bb072fc0c42312dff062a015",
                "reference": "22c6548499cbe5c5bb072fc0c42312dff062a015",
                "shasum": ""
            },
            "require": {
                "php": ">=7.2"
            },
            "time": "2026-09-23T22:01:09+00:00",
            "type": "library",
            "extra": {
                "thanks": {
                    "url": "https://github.com/symfony/polyfill",
                    "name": "symfony/polyfill"
                }
            },
            "installation-source": "dist",
            "autoload": {
                "files": [
                    "bootstrap.php"
                ],
                "psr-4": {
                    "Symfony\\Polyfill\\Php84\\": ""
                },
                "classmap": [
                    "Resources/stubs"
                ]
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Nicolas Grekas",
                    "email": "p@tchwork.com"
                },
                {
                    "name": "Symfony Community",
                    "homepage": "https://symfony.com/contributors"
                }
            ],
            "description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions",
            "homepage": "https://symfony.com",
            "keywords": [
                "compatibility",
                "polyfill",
                "portable",
                "shim"
            ],
            "support": {
                "source": "https://github.com/symfony/polyfill-php84/tree/v1.43.0"
            },
            "funding": [
                {
                    "url": "https://symfony.com/sponsor",
                    "type": "custom"
                },
                {
                    "url": "https://github.com/fabpot",
                    "type": "github"
                },
                {
                    "url": "https://github.com/nicolas-grekas",
                    "type": "github"
                },
                {
                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
                    "type": "tidelift"
                }
            ],
            "install-path": "../symfony/polyfill-php84"
        },
        {
            "name": "symfony/polyfill-php85",
            "version": "v1.43.0",
            "version_normalized": "1.43.0.0",
            "source": {
                "type": "git",
                "url": "https://github.com/symfony/polyfill-php85.git",
                "reference": "86a42cbcc07adc331481a5ab310a4024bfc227f6"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/86a42cbcc07adc331481a5ab310a4024bfc227f6",
                "reference": "86a42cbcc07adc331481a5ab310a4024bfc227f6",
                "shasum": ""
            },
            "require": {
                "php": ">=7.2"
            },
            "time": "2026-09-25T15:46:47+00:00",
            "type": "library",
            "extra": {
                "thanks": {
                    "url": "https://github.com/symfony/polyfill",
                    "name": "symfony/polyfill"
                }
            },
            "installation-source": "dist",
            "autoload": {
                "files": [
                    "bootstrap.php"
                ],
                "psr-4": {
                    "Symfony\\Polyfill\\Php85\\": ""
                },
                "classmap": [
                    "Resources/stubs"
                ]
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Nicolas Grekas",
                    "email": "p@tchwork.com"
                },
                {
                    "name": "Symfony Community",
                    "homepage": "https://symfony.com/contributors"
                }
            ],
            "description": "Symfony polyfill backporting some PHP 8.5+ features to lower PHP versions",
            "homepage": "https://symfony.com",
            "keywords": [
                "compatibility",
                "polyfill",
                "portable",
                "shim"
            ],
            "support": {
                "source": "https://github.com/symfony/polyfill-php85/tree/v1.43.0"
            },
            "funding": [
                {
                    "url": "https://symfony.com/sponsor",
                    "type": "custom"
                },
                {
                    "url": "https://github.com/fabpot",
                    "type": "github"
                },
                {
                    "url": "https://github.com/nicolas-grekas",
                    "type": "github"
                },
                {
                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
                    "type": "tidelift"
                }
            ],
            "install-path": "../symfony/polyfill-php85"
        },
        {
            "name": "symfony/polyfill-php86",
            "version": "v1.43.0",
            "version_normalized": "1.43.0.0",
            "source": {
                "type": "git",
                "url": "https://github.com/symfony/polyfill-php86.git",
                "reference": "8840df45b24f33a49dc419fa18dc0057231ae1d8"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/symfony/polyfill-php86/zipball/8840df45b24f33a49dc419fa18dc0057231ae1d8",
                "reference": "8840df45b24f33a49dc419fa18dc0057231ae1d8",
                "shasum": ""
            },
            "require": {
                "php": ">=7.2"
            },
            "time": "2026-09-25T15:46:47+00:00",
            "type": "library",
            "extra": {
                "thanks": {
                    "url": "https://github.com/symfony/polyfill",
                    "name": "symfony/polyfill"
                }
            },
            "installation-source": "dist",
            "autoload": {
                "files": [
                    "bootstrap.php"
                ],
                "psr-4": {
                    "Symfony\\Polyfill\\Php86\\": ""
                },
                "classmap": [
                    "Resources/stubs"
                ]
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Nicolas Grekas",
                    "email": "p@tchwork.com"
                },
                {
                    "name": "Symfony Community",
                    "homepage": "https://symfony.com/contributors"
                }
            ],
            "description": "Symfony polyfill backporting some PHP 8.6+ features to lower PHP versions",
            "homepage": "https://symfony.com",
            "keywords": [
                "compatibility",
                "polyfill",
                "portable",
                "shim"
            ],
            "support": {
                "source": "https://github.com/symfony/polyfill-php86/tree/v1.43.0"
            },
            "funding": [
                {
                    "url": "https://symfony.com/sponsor",
                    "type": "custom"
                },
                {
                    "url": "https://github.com/fabpot",
                    "type": "github"
                },
                {
                    "url": "https://github.com/nicolas-grekas",
                    "type": "github"
                },
                {
                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
                    "type": "tidelift"
                }
            ],
            "install-path": "../symfony/polyfill-php86"
        },
        {
            "name": "webmozart/assert",
            "version": "2.5.0",
            "version_normalized": "2.5.0.0",
            "source": {
                "type": "git",
                "url": "https://github.com/webmozarts/assert.git",
                "reference": "8a163706bbcac613733dba03f51e13b5eb1bb819"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/webmozarts/assert/zipball/8a163706bbcac613733dba03f51e13b5eb1bb819",
                "reference": "8a163706bbcac613733dba03f51e13b5eb1bb819",
                "shasum": ""
            },
            "require": {
                "ext-ctype": "*",
                "ext-date": "*",
                "ext-filter": "*",
                "php": "^8.2"
            },
            "suggest": {
                "ext-intl": "",
                "ext-simplexml": "",
                "ext-spl": ""
            },
            "time": "2026-10-05T23:14:19+00:00",
            "type": "library",
            "extra": {
                "psalm": {
                    "pluginClass": "Webmozart\\Assert\\PsalmPlugin"
                },
                "branch-alias": {
                    "dev-master": "2.0-dev",
                    "dev-feature/2-0": "2.0-dev"
                }
            },
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "Webmozart\\Assert\\": "src/"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Bernhard Schussek",
                    "email": "bschussek@gmail.com"
                },
                {
                    "name": "Woody Gilk",
                    "email": "woody.gilk@gmail.com"
                }
            ],
            "description": "Assertions to validate method input/output with nice error messages.",
            "keywords": [
                "assert",
                "check",
                "validate"
            ],
            "support": {
                "issues": "https://github.com/webmozarts/assert/issues",
                "source": "https://github.com/webmozarts/assert/tree/2.5.0"
            },
            "install-path": "../webmozart/assert"
        }
    ],
    "dev": true,
    "dev-package-names": []
}
<?php

declare(strict_types=1);

namespace Brick\Math;

use Brick\Math\Exception\DivisionByZeroException;
use Brick\Math\Exception\IntegerOverflowException;
use Brick\Math\Exception\InvalidArgumentException;
use Brick\Math\Exception\MathException;
use Brick\Math\Exception\NegativeNumberException;
use Brick\Math\Exception\NoInverseException;
use Brick\Math\Exception\NumberFormatException;
use Brick\Math\Exception\PlatformException;
use Brick\Math\Exception\RandomSourceException;
use Brick\Math\Exception\RoundingNecessaryException;
use Brick\Math\Internal\Calculator;
use Brick\Math\Internal\CalculatorRegistry;
use Brick\Math\Internal\Safe;
use LogicException;
use Override;
use Throwable;

use function array_map;
use function assert;
use function bin2hex;
use function chr;
use function count_chars;
use function filter_var;
use function hex2bin;
use function in_array;
use function intdiv;
use function is_string;
use function ltrim;
use function ord;
use function preg_match;
use function preg_quote;
use function random_bytes;
use function str_repeat;
use function strlen;
use function substr;

use const FILTER_VALIDATE_INT;

/**
 * An arbitrarily large integer number.
 *
 * This class is immutable.
 */
final readonly class BigInteger extends BigNumber
{
    /**
     * The value, as a string of digits with optional leading minus sign.
     *
     * No leading zeros must be present.
     * No leading minus sign must be present if the number is zero.
     */
    private string $value;

    /**
     * Protected constructor. Use a factory method to obtain an instance.
     *
     * @param string $value A string of digits, with optional leading minus sign.
     *
     * @pure
     */
    protected function __construct(string $value)
    {
        $this->value = $value;
    }

    /**
     * Creates a number from a string in a given base.
     *
     * The string can optionally be prefixed with the `+` or `-` sign.
     *
     * Bases greater than 36 are not supported by this method, as there is no clear consensus on which of the lowercase
     * or uppercase characters should come first. Instead, this method accepts any base up to 36, and does not
     * differentiate lowercase and uppercase characters, which are considered equal.
     *
     * For bases greater than 36, and/or custom alphabets, use the fromArbitraryBase() method.
     *
     * @param non-empty-string $number The number to convert, in the given base.
     * @param int<2, 36>       $base   The base of the number, between 2 and 36.
     *
     * @throws NumberFormatException    If the number is empty, or contains invalid chars for the given base.
     * @throws InvalidArgumentException If the base is out of range.
     *
     * @pure
     */
    public static function fromBase(string $number, int $base): BigInteger
    {
        if ($base < 2 || $base > 36) { // @phpstan-ignore smaller.alwaysFalse, greater.alwaysFalse, booleanOr.alwaysFalse
            throw InvalidArgumentException::baseOutOfRange($base);
        }

        if ($number === '') { // @phpstan-ignore identical.alwaysFalse
            throw NumberFormatException::emptyNumber();
        }

        $originalNumber = $number;

        if ($number[0] === '-') {
            $sign = '-';
            $number = substr($number, 1);
        } elseif ($number[0] === '+') {
            $sign = '';
            $number = substr($number, 1);
        } else {
            $sign = '';
        }

        if ($number === '') {
            throw NumberFormatException::invalidFormat($originalNumber);
        }

        $number = ltrim($number, '0');

        if ($number === '') {
            // The result will be the same in any base, avoid further calculation.
            return BigInteger::zero();
        }

        if ($number === '1') {
            // The result will be the same in any base, avoid further calculation.
            return new BigInteger($sign . '1');
        }

        $pattern = '/[^' . substr(Calculator::ALPHABET, 0, $base) . ']/i';

        $result = preg_match($pattern, $number, $matches);

        if ($result === false) {
            throw PlatformException::pcreFailure();
        }

        if ($result === 1) {
            throw NumberFormatException::charNotValidInBase($matches[0], $base);
        }

        if ($base === 10) {
            // The number is usable as is, avoid further calculation.
            return new BigInteger($sign . $number);
        }

        $result = CalculatorRegistry::get()->fromBase($number, $base);

        return new BigInteger($sign . $result);
    }

    /**
     * Parses a string containing an integer in an arbitrary base, using a custom alphabet.
     *
     * This method is byte-oriented: the alphabet is interpreted as a sequence of single-byte characters.
     * Multibyte UTF-8 characters are not supported.
     *
     * Because this method accepts any single-byte character, including dash, it does not handle negative numbers.
     *
     * @param non-empty-string $number   The number to parse.
     * @param non-empty-string $alphabet The alphabet, for example '01' for base 2, or '01234567' for base 8.
     *
     * @throws NumberFormatException    If the given number is empty or contains invalid chars for the given alphabet.
     * @throws InvalidArgumentException If the alphabet does not contain at least 2 chars, or contains duplicates.
     *
     * @pure
     */
    public static function fromArbitraryBase(string $number, string $alphabet): BigInteger
    {
        $base = strlen($alphabet);

        if ($base < 2) {
            throw InvalidArgumentException::alphabetTooShort();
        }

        if (strlen(count_chars($alphabet, 3)) !== $base) {
            throw InvalidArgumentException::duplicateCharsInAlphabet();
        }

        if ($number === '') { // @phpstan-ignore identical.alwaysFalse
            throw NumberFormatException::emptyNumber();
        }

        $pattern = '/[^' . preg_quote($alphabet, '/') . ']/';

        $result = preg_match($pattern, $number, $matches);

        if ($result === false) {
            throw PlatformException::pcreFailure();
        }

        if ($result === 1) {
            throw NumberFormatException::charNotInAlphabet($matches[0]);
        }

        $number = CalculatorRegistry::get()->fromArbitraryBase($number, $alphabet, $base);

        return new BigInteger($number);
    }

    /**
     * Translates a string of bytes containing the binary representation of a BigInteger into a BigInteger.
     *
     * The input string is assumed to be in big-endian byte-order: the most significant byte is in the zeroth element.
     *
     * If `$signed` is true, the input is assumed to be in two's-complement representation, and the leading bit is
     * interpreted as a sign bit. If `$signed` is false, the input is interpreted as an unsigned number, and the
     * resulting BigInteger will always be positive or zero.
     *
     * This method can be used to retrieve a number exported by `toBytes()`, as long as the `$signed` flags match.
     *
     * @param non-empty-string $bytes  The byte string.
     * @param bool             $signed Whether to interpret as a signed number in two's-complement representation with a leading
     *                                 sign bit.
     *
     * @throws NumberFormatException If the string is empty.
     *
     * @pure
     */
    public static function fromBytes(string $bytes, bool $signed = true): BigInteger
    {
        if ($bytes === '') { // @phpstan-ignore identical.alwaysFalse
            throw NumberFormatException::emptyByteString();
        }

        $twosComplement = false;

        if ($signed) {
            $x = ord($bytes[0]);

            if (($twosComplement = ($x >= 0x80))) {
                $bytes = ~$bytes;
            }
        }

        $number = self::fromBase(bin2hex($bytes), 16);

        if ($twosComplement) {
            return $number->plus(1)->negated();
        }

        return $number;
    }

    /**
     * Generates a pseudo-random number in the range 0 to 2^bitCount - 1.
     *
     * Using the default random bytes generator, this method is suitable for cryptographic use.
     *
     * @param non-negative-int             $bitCount             The number of bits.
     * @param (callable(int): string)|null $randomBytesGenerator A function that accepts a number of bytes, and returns
     *                                                           a string of random bytes of the given length. Defaults
     *                                                           to the `random_bytes()` function.
     *
     * @throws InvalidArgumentException If $bitCount is negative.
     * @throws RandomSourceException    If random byte generation fails.
     */
    public static function randomBits(int $bitCount, ?callable $randomBytesGenerator = null): BigInteger
    {
        if ($bitCount < 0) { // @phpstan-ignore smaller.alwaysFalse
            throw InvalidArgumentException::negativeBitCount();
        }

        if ($bitCount === 0) {
            return BigInteger::zero();
        }

        /** @var int<1, max> $byteLength */
        $byteLength = intdiv($bitCount - 1, 8) + 1;

        $extraBits = ($byteLength * 8 - $bitCount);
        $bitmask = chr(0xFF >> $extraBits); // @phpstan-ignore argument.type

        $randomBytes = self::randomBytes($byteLength, $randomBytesGenerator);
        $randomBytes[0] = $randomBytes[0] & $bitmask;

        return self::fromBytes($randomBytes, false);
    }

    /**
     * Generates a pseudo-random number between `$min` and `$max`, inclusive.
     *
     * Using the default random bytes generator, this method is suitable for cryptographic use.
     *
     * @param BigNumber|int|string         $min                  The lower bound. Must be convertible to a BigInteger.
     * @param BigNumber|int|string         $max                  The upper bound. Must be convertible to a BigInteger.
     * @param (callable(int): string)|null $randomBytesGenerator A function that accepts a number of bytes, and returns
     *                                                           a string of random bytes of the given length. Defaults
     *                                                           to the `random_bytes()` function.
     *
     * @throws MathException            If one of the parameters cannot be converted to a BigInteger.
     * @throws InvalidArgumentException If `$min` is greater than `$max`.
     * @throws RandomSourceException    If random byte generation fails.
     */
    public static function randomRange(
        BigNumber|int|string $min,
        BigNumber|int|string $max,
        ?callable $randomBytesGenerator = null,
    ): BigInteger {
        $min = BigInteger::of($min);
        $max = BigInteger::of($max);

        if ($min->isGreaterThan($max)) {
            throw InvalidArgumentException::minGreaterThanMax();
        }

        if ($min->isEqualTo($max)) {
            return $min;
        }

        $diff = $max->minus($min);
        $bitLength = $diff->getBitLength();

        // try until the number is in range (50% to 100% chance of success)
        do {
            $randomNumber = self::randomBits($bitLength, $randomBytesGenerator);
        } while ($randomNumber->isGreaterThan($diff));

        return $randomNumber->plus($min);
    }

    /**
     * Returns a BigInteger representing zero.
     *
     * @pure
     */
    public static function zero(): BigInteger
    {
        /** @var BigInteger|null $zero */
        static $zero;

        if ($zero === null) {
            $zero = new BigInteger('0');
        }

        return $zero;
    }

    /**
     * Returns a BigInteger representing one.
     *
     * @pure
     */
    public static function one(): BigInteger
    {
        /** @var BigInteger|null $one */
        static $one;

        if ($one === null) {
            $one = new BigInteger('1');
        }

        return $one;
    }

    /**
     * Returns a BigInteger representing ten.
     *
     * @pure
     */
    public static function ten(): BigInteger
    {
        /** @var BigInteger|null $ten */
        static $ten;

        if ($ten === null) {
            $ten = new BigInteger('10');
        }

        return $ten;
    }

    /**
     * Returns the greatest common divisor of the given numbers.
     *
     * The GCD is always positive, unless all numbers are zero, in which case it is zero.
     *
     * @param BigNumber|int|string $a    The first number. Must be convertible to a BigInteger.
     * @param BigNumber|int|string ...$n The additional numbers. Each number must be convertible to a BigInteger.
     *
     * @throws MathException If one of the parameters cannot be converted to a BigInteger.
     *
     * @pure
     */
    public static function gcdAll(BigNumber|int|string $a, BigNumber|int|string ...$n): BigInteger
    {
        $result = BigInteger::of($a)->abs();

        $n = array_map(BigInteger::of(...), $n);

        foreach ($n as $next) {
            $result = $result->gcd($next);

            if ($result->isEqualTo(1)) {
                return $result;
            }
        }

        return $result;
    }

    /**
     * Returns the least common multiple of the given numbers.
     *
     * The LCM is always positive, unless one of the numbers is zero, in which case it is zero.
     *
     * @param BigNumber|int|string $a    The first number. Must be convertible to a BigInteger.
     * @param BigNumber|int|string ...$n The additional numbers. Each number must be convertible to a BigInteger.
     *
     * @throws MathException If one of the parameters cannot be converted to a BigInteger.
     *
     * @pure
     */
    public static function lcmAll(BigNumber|int|string $a, BigNumber|int|string ...$n): BigInteger
    {
        $result = BigInteger::of($a)->abs();

        $n = array_map(BigInteger::of(...), $n);

        foreach ($n as $next) {
            $result = $result->lcm($next);

            if ($result->isZero()) {
                return $result;
            }
        }

        return $result;
    }

    /**
     * Returns the sum of this number and the given one.
     *
     * @param BigNumber|int|string $that The number to add. Must be convertible to a BigInteger.
     *
     * @throws MathException If the number is not valid, or is not convertible to a BigInteger.
     *
     * @pure
     */
    public function plus(BigNumber|int|string $that): BigInteger
    {
        $that = BigInteger::of($that);

        if ($that->isZero()) {
            return $this;
        }

        if ($this->isZero()) {
            return $that;
        }

        $value = CalculatorRegistry::get()->add($this->value, $that->value);

        return new BigInteger($value);
    }

    /**
     * Returns the difference of this number and the given one.
     *
     * @param BigNumber|int|string $that The number to subtract. Must be convertible to a BigInteger.
     *
     * @throws MathException If the number is not valid, or is not convertible to a BigInteger.
     *
     * @pure
     */
    public function minus(BigNumber|int|string $that): BigInteger
    {
        $that = BigInteger::of($that);

        if ($that->isZero()) {
            return $this;
        }

        if ($this->isZero()) {
            return $that->negated();
        }

        $value = CalculatorRegistry::get()->sub($this->value, $that->value);

        return new BigInteger($value);
    }

    /**
     * Returns the product of this number and the given one.
     *
     * @param BigNumber|int|string $that The multiplier. Must be convertible to a BigInteger.
     *
     * @throws MathException If the multiplier is not valid, or is not convertible to a BigInteger.
     *
     * @pure
     */
    public function multipliedBy(BigNumber|int|string $that): BigInteger
    {
        $that = BigInteger::of($that);

        if ($that->isOne()) {
            return $this;
        }

        if ($this->isOne()) {
            return $that;
        }

        $value = CalculatorRegistry::get()->mul($this->value, $that->value);

        return new BigInteger($value);
    }

    /**
     * Returns the result of the division of this number by the given one.
     *
     * @param BigNumber|int|string $that         The divisor. Must be convertible to a BigInteger.
     * @param RoundingMode         $roundingMode An optional rounding mode, defaults to Unnecessary.
     *
     * @throws MathException              If the divisor is not valid, or is not convertible to a BigInteger.
     * @throws DivisionByZeroException    If the divisor is zero.
     * @throws RoundingNecessaryException If RoundingMode::Unnecessary is used and the remainder is not zero.
     *
     * @pure
     */
    public function dividedBy(BigNumber|int|string $that, RoundingMode $roundingMode = RoundingMode::Unnecessary): BigInteger
    {
        $that = BigInteger::of($that);

        if ($that->isZero()) {
            throw DivisionByZeroException::divisionByZero();
        }

        if ($that->isOne()) {
            return $this;
        }

        if ($that->isMinusOne()) {
            return $this->negated();
        }

        $result = CalculatorRegistry::get()->divRound($this->value, $that->value, $roundingMode);

        if ($result === null) {
            throw RoundingNecessaryException::integerDivisionNotExact();
        }

        return new BigInteger($result);
    }

    /**
     * Returns this number exponentiated to the given value.
     *
     * @param non-negative-int $exponent
     *
     * @throws InvalidArgumentException If the exponent is negative.
     *
     * @pure
     */
    public function power(int $exponent): BigInteger
    {
        if ($exponent === 0) {
            return BigInteger::one();
        }

        if ($exponent === 1) {
            return $this;
        }

        if ($exponent < 0) { // @phpstan-ignore smaller.alwaysFalse
            throw InvalidArgumentException::negativeExponent();
        }

        return new BigInteger(CalculatorRegistry::get()->pow($this->value, $exponent));
    }

    /**
     * Returns the quotient of the division of this number by the given one.
     *
     * Examples:
     *
     * - `7` quotient `3` returns `2`
     * - `7` quotient `-3` returns `-2`
     * - `-7` quotient `3` returns `-2`
     * - `-7` quotient `-3` returns `2`
     *
     * @param BigNumber|int|string $that The divisor. Must be convertible to a BigInteger.
     *
     * @throws MathException           If the divisor is not valid, or is not convertible to a BigInteger.
     * @throws DivisionByZeroException If the divisor is zero.
     *
     * @pure
     */
    public function quotient(BigNumber|int|string $that): BigInteger
    {
        $that = BigInteger::of($that);

        if ($that->isZero()) {
            throw DivisionByZeroException::divisionByZero();
        }

        if ($that->isOne()) {
            return $this;
        }

        if ($that->isMinusOne()) {
            return $this->negated();
        }

        $quotient = CalculatorRegistry::get()->divQ($this->value, $that->value);

        return new BigInteger($quotient);
    }

    /**
     * Returns the remainder of the division of this number by the given one.
     *
     * The remainder, when non-zero, has the same sign as the dividend.
     *
     * Examples:
     *
     * - `7` remainder `3` returns `1`
     * - `7` remainder `-3` returns `1`
     * - `-7` remainder `3` returns `-1`
     * - `-7` remainder `-3` returns `-1`
     *
     * @param BigNumber|int|string $that The divisor. Must be convertible to a BigInteger.
     *
     * @throws MathException           If the divisor is not valid, or is not convertible to a BigInteger.
     * @throws DivisionByZeroException If the divisor is zero.
     *
     * @pure
     */
    public function remainder(BigNumber|int|string $that): BigInteger
    {
        $that = BigInteger::of($that);

        if ($that->isZero()) {
            throw DivisionByZeroException::divisionByZero();
        }

        if ($that->isOne() || $that->isMinusOne()) {
            return BigInteger::zero();
        }

        $remainder = CalculatorRegistry::get()->divR($this->value, $that->value);

        return new BigInteger($remainder);
    }

    /**
     * Returns the quotient and remainder of the division of this number by the given one.
     *
     * Examples:
     *
     * - `7` quotientAndRemainder `3` returns [`2`, `1`]
     * - `7` quotientAndRemainder `-3` returns [`-2`, `1`]
     * - `-7` quotientAndRemainder `3` returns [`-2`, `-1`]
     * - `-7` quotientAndRemainder `-3` returns [`2`, `-1`]
     *
     * @param BigNumber|int|string $that The divisor. Must be convertible to a BigInteger.
     *
     * @return array{BigInteger, BigInteger} An array containing the quotient and the remainder.
     *
     * @throws MathException           If the divisor is not valid, or is not convertible to a BigInteger.
     * @throws DivisionByZeroException If the divisor is zero.
     *
     * @pure
     */
    public function quotientAndRemainder(BigNumber|int|string $that): array
    {
        $that = BigInteger::of($that);

        if ($that->isZero()) {
            throw DivisionByZeroException::divisionByZero();
        }

        if ($that->isOne()) {
            return [$this, BigInteger::zero()];
        }

        if ($that->isMinusOne()) {
            return [$this->negated(), BigInteger::zero()];
        }

        [$quotient, $remainder] = CalculatorRegistry::get()->divQR($this->value, $that->value);

        return [
            new BigInteger($quotient),
            new BigInteger($remainder),
        ];
    }

    /**
     * Returns this number modulo the given one.
     *
     * The result is always non-negative, and is the unique value `r` such that `0 <= r < m`
     * and `this - r` is a multiple of `m`.
     *
     * This is also known as Euclidean modulo. Unlike `remainder()`, which can return negative values
     * when the dividend is negative, `mod()` always returns a non-negative result.
     *
     * Examples:
     *
     * - `7` mod `3` returns `1`
     * - `-7` mod `3` returns `2`
     *
     * @param BigNumber|int|string $modulus The modulus. Must be convertible to a BigInteger.
     *
     * @throws MathException            If the modulus is not valid, or is not convertible to a BigInteger.
     * @throws InvalidArgumentException If the modulus is negative.
     * @throws DivisionByZeroException  If the modulus is zero.
     *
     * @pure
     */
    public function mod(BigNumber|int|string $modulus): BigInteger
    {
        $modulus = BigInteger::of($modulus);

        if ($modulus->isZero()) {
            throw DivisionByZeroException::zeroModulus();
        }

        if ($modulus->isNegative()) {
            throw InvalidArgumentException::negativeModulus();
        }

        $value = CalculatorRegistry::get()->mod($this->value, $modulus->value);

        return new BigInteger($value);
    }

    /**
     * Returns the modular multiplicative inverse of this BigInteger modulo $modulus.
     *
     * @param BigNumber|int|string $modulus The modulus. Must be convertible to a BigInteger.
     *
     * @throws MathException            If the modulus is not valid, or is not convertible to a BigInteger.
     * @throws InvalidArgumentException If the modulus is negative.
     * @throws DivisionByZeroException  If the modulus is zero.
     * @throws NoInverseException       If this BigInteger has no multiplicative inverse mod m (that is, this BigInteger
     *                                  is not relatively prime to m).
     *
     * @pure
     */
    public function modInverse(BigNumber|int|string $modulus): BigInteger
    {
        $modulus = BigInteger::of($modulus);

        if ($modulus->isZero()) {
            throw DivisionByZeroException::zeroModulus();
        }

        if ($modulus->isNegative()) {
            throw InvalidArgumentException::negativeModulus();
        }

        if ($modulus->isOne()) {
            return BigInteger::zero();
        }

        $value = CalculatorRegistry::get()->modInverse($this->value, $modulus->value);

        if ($value === null) {
            throw NoInverseException::noModularInverse();
        }

        return new BigInteger($value);
    }

    /**
     * Returns this number raised into power with modulo.
     *
     * This operation requires a non-negative exponent and a strictly positive modulus.
     *
     * @param BigNumber|int|string $exponent The exponent. Must be convertible to a BigInteger.
     * @param BigNumber|int|string $modulus  The modulus. Must be convertible to a BigInteger.
     *
     * @throws MathException            If the exponent or modulus is not valid, or is not convertible to a BigInteger.
     * @throws InvalidArgumentException If the exponent or modulus is negative.
     * @throws DivisionByZeroException  If the modulus is zero.
     *
     * @pure
     */
    public function modPow(BigNumber|int|string $exponent, BigNumber|int|string $modulus): BigInteger
    {
        $exponent = BigInteger::of($exponent);
        $modulus = BigInteger::of($modulus);

        if ($modulus->isZero()) {
            throw DivisionByZeroException::zeroModulus();
        }

        if ($modulus->isNegative()) {
            throw InvalidArgumentException::negativeModulus();
        }

        if ($exponent->isNegative()) {
            throw InvalidArgumentException::negativeExponent();
        }

        $result = CalculatorRegistry::get()->modPow($this->value, $exponent->value, $modulus->value);

        return new BigInteger($result);
    }

    /**
     * Returns the greatest common divisor of this number and the given one.
     *
     * The GCD is always positive, unless both operands are zero, in which case it is zero.
     *
     * @param BigNumber|int|string $that The operand. Must be convertible to a BigInteger.
     *
     * @throws MathException If the operand is not valid, or is not convertible to a BigInteger.
     *
     * @pure
     */
    public function gcd(BigNumber|int|string $that): BigInteger
    {
        $that = BigInteger::of($that);

        if ($that->isZero()) {
            return $this->abs();
        }

        if ($this->isZero()) {
            return $that->abs();
        }

        $value = CalculatorRegistry::get()->gcd($this->value, $that->value);

        return new BigInteger($value);
    }

    /**
     * Returns the least common multiple of this number and the given one.
     *
     * The LCM is always positive, unless at least one operand is zero, in which case it is zero.
     *
     * @param BigNumber|int|string $that The operand. Must be convertible to a BigInteger.
     *
     * @throws MathException If the operand is not valid, or is not convertible to a BigInteger.
     *
     * @pure
     */
    public function lcm(BigNumber|int|string $that): BigInteger
    {
        $that = BigInteger::of($that);

        if ($this->isZero() || $that->isZero()) {
            return BigInteger::zero();
        }

        $value = CalculatorRegistry::get()->lcm($this->value, $that->value);

        return new BigInteger($value);
    }

    /**
     * Returns the integer square root of this number, rounded according to the given rounding mode.
     *
     * @param RoundingMode $roundingMode An optional rounding mode, defaults to Unnecessary.
     *
     * @throws NegativeNumberException    If this number is negative.
     * @throws RoundingNecessaryException If RoundingMode::Unnecessary is used, and the number is not a perfect square.
     *
     * @pure
     */
    public function sqrt(RoundingMode $roundingMode = RoundingMode::Unnecessary): BigInteger
    {
        if ($this->isNegative()) {
            throw NegativeNumberException::squareRootOfNegativeNumber();
        }

        $calculator = CalculatorRegistry::get();

        $sqrt = $calculator->sqrt($this->value);

        // For Down and Floor (equivalent for non-negative numbers), return floor sqrt
        if ($roundingMode === RoundingMode::Down || $roundingMode === RoundingMode::Floor) {
            return new BigInteger($sqrt);
        }

        // Check if the sqrt is exact
        $s2 = $calculator->mul($sqrt, $sqrt);
        $remainder = $calculator->sub($this->value, $s2);

        if ($remainder === '0') {
            // sqrt is exact
            return new BigInteger($sqrt);
        }

        // sqrt is not exact
        if ($roundingMode === RoundingMode::Unnecessary) {
            throw RoundingNecessaryException::integerSquareRootNotExact();
        }

        // For Up and Ceiling (equivalent for non-negative numbers), round up
        if ($roundingMode === RoundingMode::Up || $roundingMode === RoundingMode::Ceiling) {
            return new BigInteger($calculator->add($sqrt, '1'));
        }

        // For Half* modes, compare our number to the midpoint of the interval [sÂ², (s+1)Â²[.
        // The midpoint is sÂ² + s + 0.5. Comparing n >= sÂ² + s + 0.5 with remainder = n âˆ’ sÂ²
        // is equivalent to comparing 2*remainder >= 2*s + 1.
        $twoRemainder = $calculator->mul($remainder, '2');
        $threshold = $calculator->add($calculator->mul($sqrt, '2'), '1');
        $cmp = $calculator->cmp($twoRemainder, $threshold);

        // We're supposed to increment (round up) when:
        //   - HalfUp, HalfCeiling => $cmp >= 0
        //   - HalfDown, HalfFloor => $cmp > 0
        //   - HalfEven => $cmp > 0 || ($cmp === 0 && $sqrt % 2 === 1)
        //   - HalfOdd => $cmp > 0 || ($cmp === 0 && $sqrt % 2 === 0)
        // But 2*remainder is always even and 2*s + 1 is always odd, so $cmp is never zero.
        // Therefore, all Half* modes simplify to:
        if ($cmp > 0) {
            $sqrt = $calculator->add($sqrt, '1');
        }

        return new BigInteger($sqrt);
    }

    /**
     * Returns the integer nth root of this number, rounded according to the given rounding mode.
     *
     * For odd $n, the operation is defined for negative inputs: the sign is preserved and the
     * magnitude of the root is |$this|^(1/$n).
     *
     * @param positive-int $n            The root degree. Must be a strictly positive integer.
     * @param RoundingMode $roundingMode An optional rounding mode, defaults to Unnecessary.
     *
     * @throws InvalidArgumentException   If $n is less than 1.
     * @throws NegativeNumberException    If this number is negative and $n is even.
     * @throws RoundingNecessaryException If RoundingMode::Unnecessary is used, and this number is not a perfect nth power.
     *
     * @pure
     */
    public function nthRoot(int $n, RoundingMode $roundingMode = RoundingMode::Unnecessary): BigInteger
    {
        if ($n < 1) { // @phpstan-ignore smaller.alwaysFalse
            throw InvalidArgumentException::nonPositiveNthRootDegree();
        }

        if ($n === 1) {
            return $this;
        }

        $isNegative = $this->isNegative();

        if ($isNegative && $n % 2 === 0) {
            throw NegativeNumberException::nthRootOfNegativeNumber();
        }

        $calculator = CalculatorRegistry::get();

        // Truncation toward zero: for positive $this this is the floor root, for negative $this
        // with odd $n this is the ceiling of the true root (i.e., the root of smaller magnitude).
        $truncatedRoot = $calculator->nthRoot($this->value, $n);
        $rootPow = $calculator->pow($truncatedRoot, $n);

        if ($rootPow === $this->value) {
            return new BigInteger($truncatedRoot);
        }

        if ($roundingMode === RoundingMode::Unnecessary) {
            throw RoundingNecessaryException::integerNthRootNotExact();
        }

        $isPositive = ! $isNegative;

        // The next-step root is one unit further from zero than the truncated root.
        $nextStep = $isPositive
            ? $calculator->add($truncatedRoot, '1')
            : $calculator->sub($truncatedRoot, '1');

        if ($roundingMode === RoundingMode::Up) {
            $increment = true;
        } elseif ($roundingMode === RoundingMode::Down) {
            $increment = false;
        } elseif ($roundingMode === RoundingMode::Ceiling) {
            $increment = $isPositive;
        } elseif ($roundingMode === RoundingMode::Floor) {
            $increment = ! $isPositive;
        } else {
            // Half* modes: increment iff |$this| > (|truncated| + 0.5)^n, equivalently
            // 2^n * |$this| > (2*|truncated| + 1)^n. The rhs is odd while the lhs is even
            // (n â‰¥ 2 here, so 2^n is even), so a midpoint tie is impossible and all six
            // Half* modes collapse to the same comparison.
            $absValue = $calculator->abs($this->value);
            $absTruncated = $calculator->abs($truncatedRoot);
            $twoAbsRootPlus1 = $calculator->add($calculator->mul($absTruncated, '2'), '1');

            $lhs = $calculator->mul($calculator->pow('2', $n), $absValue);
            $rhs = $calculator->pow($twoAbsRootPlus1, $n);

            $increment = $calculator->cmp($lhs, $rhs) > 0;
        }

        return new BigInteger($increment ? $nextStep : $truncatedRoot);
    }

    #[Override]
    public function negated(): static
    {
        return new BigInteger(CalculatorRegistry::get()->neg($this->value));
    }

    /**
     * Returns the integer bitwise-and combined with another integer.
     *
     * This method returns a negative BigInteger if and only if both operands are negative.
     *
     * @param BigNumber|int|string $that The operand. Must be convertible to a BigInteger.
     *
     * @throws MathException If the operand is not valid, or is not convertible to a BigInteger.
     *
     * @pure
     */
    public function and(BigNumber|int|string $that): BigInteger
    {
        $that = BigInteger::of($that);

        return new BigInteger(CalculatorRegistry::get()->and($this->value, $that->value));
    }

    /**
     * Returns the integer bitwise-or combined with another integer.
     *
     * This method returns a negative BigInteger if and only if either of the operands is negative.
     *
     * @param BigNumber|int|string $that The operand. Must be convertible to a BigInteger.
     *
     * @throws MathException If the operand is not valid, or is not convertible to a BigInteger.
     *
     * @pure
     */
    public function or(BigNumber|int|string $that): BigInteger
    {
        $that = BigInteger::of($that);

        return new BigInteger(CalculatorRegistry::get()->or($this->value, $that->value));
    }

    /**
     * Returns the integer bitwise-xor combined with another integer.
     *
     * This method returns a negative BigInteger if and only if exactly one of the operands is negative.
     *
     * @param BigNumber|int|string $that The operand. Must be convertible to a BigInteger.
     *
     * @throws MathException If the operand is not valid, or is not convertible to a BigInteger.
     *
     * @pure
     */
    public function xor(BigNumber|int|string $that): BigInteger
    {
        $that = BigInteger::of($that);

        return new BigInteger(CalculatorRegistry::get()->xor($this->value, $that->value));
    }

    /**
     * Returns the bitwise-not of this BigInteger.
     *
     * @pure
     */
    public function not(): BigInteger
    {
        return $this->negated()->minus(1);
    }

    /**
     * Returns the integer left shifted by a given number of bits.
     *
     * If $bits is negative, the integer is shifted right by the absolute value instead.
     *
     * @pure
     */
    public function shiftedLeft(int $bits): BigInteger
    {
        if ($bits === 0) {
            return $this;
        }

        if ($bits < 0) {
            return $this->shiftedRight(Safe::neg($bits));
        }

        return $this->multipliedBy(BigInteger::of(2)->power($bits));
    }

    /**
     * Returns the integer right shifted by a given number of bits.
     *
     * If $bits is negative, the integer is shifted left by the absolute value instead.
     *
     * @pure
     */
    public function shiftedRight(int $bits): BigInteger
    {
        if ($bits === 0) {
            return $this;
        }

        if ($bits < 0) {
            return $this->shiftedLeft(Safe::neg($bits));
        }

        $operand = BigInteger::of(2)->power($bits);

        if ($this->isPositiveOrZero()) {
            return $this->quotient($operand);
        }

        return $this->dividedBy($operand, RoundingMode::Up);
    }

    /**
     * Returns the number of bits in the minimal two's-complement representation of this BigInteger, excluding a sign bit.
     *
     * For positive BigIntegers, this is equivalent to the number of bits in the ordinary binary representation.
     * Computes (ceil(log2(this < 0 ? -this : this+1))).
     *
     * @return non-negative-int
     *
     * @pure
     */
    public function getBitLength(): int
    {
        if ($this->isZero()) {
            return 0;
        }

        if ($this->isNegative()) {
            return $this->abs()->minus(1)->getBitLength();
        }

        return strlen($this->toBase(2));
    }

    /**
     * Returns the index of the rightmost (lowest-order) one bit in this BigInteger.
     *
     * Returns null if this BigInteger is zero.
     *
     * @return non-negative-int|null
     *
     * @pure
     */
    public function getLowestSetBit(): ?int
    {
        $n = $this;
        $bitLength = $this->getBitLength();

        for ($i = 0; $i <= $bitLength; $i++) {
            if ($n->isOdd()) {
                return $i;
            }

            $n = $n->shiftedRight(1);
        }

        return null;
    }

    /**
     * Returns true if and only if the designated bit is set.
     *
     * Computes ((this & (1<<bitIndex)) != 0).
     *
     * @param non-negative-int $bitIndex The bit to test, 0-based.
     *
     * @throws InvalidArgumentException If the bit to test is negative.
     *
     * @pure
     */
    public function isBitSet(int $bitIndex): bool
    {
        if ($bitIndex < 0) { // @phpstan-ignore smaller.alwaysFalse
            throw InvalidArgumentException::negativeBitIndex();
        }

        return $this->shiftedRight($bitIndex)->isOdd();
    }

    /**
     * Returns whether this number is even.
     *
     * @pure
     */
    public function isEven(): bool
    {
        return in_array($this->value[-1], ['0', '2', '4', '6', '8'], true);
    }

    /**
     * Returns whether this number is odd.
     *
     * @pure
     */
    public function isOdd(): bool
    {
        return in_array($this->value[-1], ['1', '3', '5', '7', '9'], true);
    }

    #[Override]
    public function compareTo(BigNumber|int|string $that): int
    {
        $that = BigNumber::of($that);

        if ($that instanceof BigInteger) {
            return CalculatorRegistry::get()->cmp($this->value, $that->value);
        }

        return -$that->compareTo($this);
    }

    #[Override]
    public function getSign(): int
    {
        return ($this->value === '0') ? 0 : (($this->value[0] === '-') ? -1 : 1);
    }

    #[Override]
    public function toBigInteger(): BigInteger
    {
        return $this;
    }

    #[Override]
    public function toBigDecimal(): BigDecimal
    {
        return self::newBigDecimal($this->value);
    }

    #[Override]
    public function toBigRational(): BigRational
    {
        return self::newBigRational($this, BigInteger::one(), false, false);
    }

    #[Override]
    public function toScale(int $scale, RoundingMode $roundingMode = RoundingMode::Unnecessary): BigDecimal
    {
        return $this->toBigDecimal()->toScale($scale, $roundingMode);
    }

    #[Override]
    public function toInt(): int
    {
        $intValue = filter_var($this->value, FILTER_VALIDATE_INT);

        if ($intValue === false) {
            throw IntegerOverflowException::integerOutOfRange($this);
        }

        return $intValue;
    }

    #[Override]
    public function toFloat(): float
    {
        return (float) $this->value;
    }

    /**
     * Returns a string representation of this number in the given base.
     *
     * The output will always be lowercase for bases greater than 10.
     *
     * @param int<2, 36> $base
     *
     * @return non-empty-string
     *
     * @throws InvalidArgumentException If the base is out of range.
     *
     * @pure
     */
    public function toBase(int $base): string
    {
        if ($base === 10) {
            /** @var non-empty-string */
            return $this->value;
        }

        if ($base < 2 || $base > 36) { // @phpstan-ignore smaller.alwaysFalse, greater.alwaysFalse, booleanOr.alwaysFalse
            throw InvalidArgumentException::baseOutOfRange($base);
        }

        /** @var non-empty-string */
        return CalculatorRegistry::get()->toBase($this->value, $base);
    }

    /**
     * Returns a string representation of this number in an arbitrary base with a custom alphabet.
     *
     * This method is byte-oriented: the alphabet is interpreted as a sequence of single-byte characters.
     * Multibyte UTF-8 characters are not supported.
     *
     * Because this method accepts any single-byte character, including dash, it does not handle negative numbers;
     * a NegativeNumberException will be thrown when attempting to call this method on a negative number.
     *
     * @param non-empty-string $alphabet The alphabet, for example '01' for base 2, or '01234567' for base 8.
     *
     * @return non-empty-string
     *
     * @throws InvalidArgumentException If the alphabet does not contain at least 2 chars, or contains duplicates.
     * @throws NegativeNumberException  If this number is negative.
     *
     * @pure
     */
    public function toArbitraryBase(string $alphabet): string
    {
        $base = strlen($alphabet);

        if ($base < 2) {
            throw InvalidArgumentException::alphabetTooShort();
        }

        if (strlen(count_chars($alphabet, 3)) !== $base) {
            throw InvalidArgumentException::duplicateCharsInAlphabet();
        }

        if ($this->isNegative()) {
            throw NegativeNumberException::toArbitraryBaseOfNegativeNumber();
        }

        /** @var non-empty-string */
        return CalculatorRegistry::get()->toArbitraryBase($this->value, $alphabet, $base);
    }

    /**
     * Returns a string of bytes containing the binary representation of this BigInteger.
     *
     * The string is in big-endian byte-order: the most significant byte is in the zeroth element.
     *
     * If `$signed` is true, the output will be in two's-complement representation, and a sign bit will be prepended to
     * the output. If `$signed` is false, no sign bit will be prepended, and this method will throw an exception if the
     * number is negative.
     *
     * The string will contain the minimum number of bytes required to represent this BigInteger, including a sign bit
     * if `$signed` is true.
     *
     * Note that in signed mode, a positive number whose most significant byte is 0x80 or greater gains an extra leading
     * 0x00 byte, so that its first bit is not interpreted as a sign bit: for example, 128 converts to "\x00\x80" when
     * signed, and to "\x80" when unsigned. Callers that require fixed-width unsigned output, such as cryptographic
     * code, should use `$signed = false`.
     *
     * This representation is compatible with the `fromBytes()` factory method, as long as the `$signed` flags match.
     *
     * @param bool $signed Whether to output a signed number in two's-complement representation with a leading sign bit.
     *
     * @return non-empty-string
     *
     * @throws NegativeNumberException If $signed is false, and the number is negative.
     *
     * @pure
     */
    public function toBytes(bool $signed = true): string
    {
        if (! $signed && $this->isNegative()) {
            throw NegativeNumberException::unsignedBytesOfNegativeNumber();
        }

        $hex = $this->abs()->toBase(16);

        if (strlen($hex) % 2 !== 0) {
            $hex = '0' . $hex;
        }

        $baseHexLength = strlen($hex);

        if ($signed) {
            if ($this->isNegative()) {
                $bin = hex2bin($hex);
                assert($bin !== false);

                /** @var non-empty-string $hex */
                $hex = bin2hex(~$bin);
                $hex = self::fromBase($hex, 16)->plus(1)->toBase(16);

                $hexLength = strlen($hex);

                if ($hexLength < $baseHexLength) {
                    $hex = str_repeat('0', $baseHexLength - $hexLength) . $hex;
                }

                if ($hex[0] < '8') {
                    $hex = 'FF' . $hex;
                }
            } else {
                if ($hex[0] >= '8') {
                    $hex = '00' . $hex;
                }
            }
        }

        $result = hex2bin($hex);
        assert($result !== false);

        /** @var non-empty-string */
        return $result;
    }

    /**
     * @return numeric-string
     */
    #[Override]
    public function toString(): string
    {
        /** @var numeric-string */
        return $this->value;
    }

    /**
     * This method is required for serializing the object and SHOULD NOT be accessed directly.
     *
     * @internal
     *
     * @return array{value: string}
     */
    public function __serialize(): array
    {
        return ['value' => $this->value];
    }

    /**
     * This method is only here to allow unserializing the object and cannot be accessed directly.
     *
     * @internal
     *
     * @param array{value: string} $data
     *
     * @throws LogicException
     */
    public function __unserialize(array $data): void
    {
        if (isset($this->value)) {
            throw new LogicException('__unserialize() is an internal function, it must not be called directly.');
        }

        $this->value = $data['value'];
    }

    #[Override]
    protected static function from(BigNumber $number): static
    {
        return $number->toBigInteger();
    }

    #[Override]
    protected function digitCount(): int
    {
        return strlen($this->value) - (int) ($this->value[0] === '-');
    }

    /**
     * Returns random bytes from the provided generator or from random_bytes().
     *
     * @param int                          $byteLength           The number of requested bytes.
     * @param (callable(int): string)|null $randomBytesGenerator The random bytes generator, or null to use random_bytes().
     *
     * @throws RandomSourceException If random byte generation fails.
     */
    private static function randomBytes(int $byteLength, ?callable $randomBytesGenerator): string
    {
        $randomBytesGenerator ??= random_bytes(...);

        try {
            $randomBytes = $randomBytesGenerator($byteLength);
        } catch (Throwable $e) {
            throw RandomSourceException::randomSourceFailure($e);
        }

        /** @phpstan-ignore function.alreadyNarrowedType (Defensive runtime check for user-provided callbacks) */
        if (! is_string($randomBytes)) {
            throw RandomSourceException::invalidRandomBytesType($randomBytes);
        }

        if (strlen($randomBytes) !== $byteLength) {
            throw RandomSourceException::invalidRandomBytesLength($byteLength, strlen($randomBytes));
        }

        return $randomBytes;
    }

    /**
     * @pure
     */
    private function isOne(): bool
    {
        return $this->value === '1';
    }

    /**
     * @pure
     */
    private function isMinusOne(): bool
    {
        return $this->value === '-1';
    }
}
<?php

declare(strict_types=1);

namespace Brick\Math;

use Brick\Math\Exception\DivisionByZeroException;
use Brick\Math\Exception\InvalidArgumentException;
use Brick\Math\Exception\MathException;
use Brick\Math\Exception\NegativeNumberException;
use Brick\Math\Exception\PlatformException;
use Brick\Math\Exception\RoundingNecessaryException;
use Brick\Math\Internal\CalculatorRegistry;
use Brick\Math\Internal\DecimalHelper;
use Brick\Math\Internal\Safe;
use LogicException;
use Override;

use function assert;
use function chr;
use function in_array;
use function ini_set;
use function intdiv;
use function is_infinite;
use function is_nan;
use function json_encode;
use function max;
use function pack;
use function rtrim;
use function str_repeat;
use function strlen;
use function substr;
use function unpack;

use const PHP_INT_SIZE;

/**
 * An arbitrarily large decimal number.
 *
 * This class is immutable.
 *
 * The scale of the number is the number of digits after the decimal point. It is always positive or zero.
 */
final readonly class BigDecimal extends BigNumber
{
    /**
     * The unscaled value of this decimal number.
     *
     * This is a string of digits with an optional leading minus sign.
     * No leading zero must be present.
     * No leading minus sign must be present if the value is 0.
     */
    private string $value;

    /**
     * The scale (number of digits after the decimal point) of this decimal number.
     *
     * This must be zero or more.
     *
     * @var non-negative-int
     */
    private int $scale;

    /**
     * Protected constructor. Use a factory method to obtain an instance.
     *
     * @param string           $value The unscaled value, validated.
     * @param non-negative-int $scale The scale, validated.
     *
     * @pure
     */
    protected function __construct(string $value, int $scale = 0)
    {
        $this->value = $value;
        $this->scale = $scale;
    }

    /**
     * Creates a BigDecimal from an unscaled value and a scale.
     *
     * Example: `(12345, 3)` will result in the BigDecimal `12.345`.
     *
     * A negative scale is normalized to zero by appending zeros to the unscaled value.
     *
     * Example: `(12345, -3)` will result in the BigDecimal `12345000`.
     *
     * @param BigNumber|int|string $value The unscaled value. Must be convertible to a BigInteger.
     * @param int                  $scale The scale of the number. If negative, the scale will be set to zero
     *                                    and the unscaled value will be adjusted accordingly.
     *
     * @throws MathException If the value is not valid, or is not convertible to a BigInteger.
     *
     * @pure
     */
    public static function ofUnscaledValue(BigNumber|int|string $value, int $scale = 0): BigDecimal
    {
        $value = BigInteger::of($value)->toString();

        if ($scale < 0) {
            if ($value !== '0') {
                $value .= str_repeat('0', Safe::neg($scale));
            }
            $scale = 0;
        }

        return new BigDecimal($value, $scale);
    }

    /**
     * Returns a BigDecimal representing zero, with a scale of zero.
     *
     * @pure
     */
    public static function zero(): BigDecimal
    {
        /** @var BigDecimal|null $zero */
        static $zero;

        if ($zero === null) {
            $zero = new BigDecimal('0');
        }

        return $zero;
    }

    /**
     * Returns a BigDecimal representing one, with a scale of zero.
     *
     * @pure
     */
    public static function one(): BigDecimal
    {
        /** @var BigDecimal|null $one */
        static $one;

        if ($one === null) {
            $one = new BigDecimal('1');
        }

        return $one;
    }

    /**
     * Returns a BigDecimal representing ten, with a scale of zero.
     *
     * @pure
     */
    public static function ten(): BigDecimal
    {
        /** @var BigDecimal|null $ten */
        static $ten;

        if ($ten === null) {
            $ten = new BigDecimal('10');
        }

        return $ten;
    }

    /**
     * Creates a BigDecimal from the exact IEEE-754 value of a float.
     *
     * Examples:
     *   - `fromFloatExact(0.1)` returns a BigDecimal with value '0.1000000000000000055511151231257827021181583404541015625'
     *   - `fromFloatExact(0.3)` returns a BigDecimal with value '0.299999999999999988897769753748434595763683319091796875'
     *   - `fromFloatExact(0.5)` returns a BigDecimal with value '0.5'
     *   - `fromFloatExact(1.0)` returns a BigDecimal with value '1'
     *
     * Note that BigDecimal has no concept of negative zero, so `-0.0` and `0.0` both convert to zero.
     *
     * @throws InvalidArgumentException If the value is NaN or infinite.
     * @throws PlatformException        If the platform uses a non-IEEE-754 double format.
     *
     * @pure
     */
    public static function fromFloatExact(float $value): BigDecimal
    {
        if (is_nan($value)) {
            throw InvalidArgumentException::cannotConvertFloat('NaN');
        }
        if (is_infinite($value)) {
            throw InvalidArgumentException::cannotConvertFloat($value > 0 ? 'INF' : '-INF');
        }

        // @codeCoverageIgnoreStart
        if (pack('E', 1.0) !== "\x3f\xf0\x00\x00\x00\x00\x00\x00") {
            throw PlatformException::unsupportedFloatFormat();
        }
        // @codeCoverageIgnoreEnd

        if (PHP_INT_SIZE >= 8) {
            // 64-bit: extract the IEEE-754 bit pattern as a 64-bit integer.
            /** @var array{1: int} $unpacked */
            $unpacked = unpack('J', pack('E', $value));
            $bits = $unpacked[1];

            // Bits: [sign(1)|exp(11)|mantissa(52)]
            $signBit = ($bits >> 63) & 1;
            $expBits = ($bits >> 52) & 0x7FF;
            $mantissa = $bits & 0xFFFFFFFFFFFFF;

            // Zero (covers both 0.0 and -0.0).
            if ($expBits === 0 && $mantissa === 0) {
                return BigDecimal::zero();
            }

            if ($expBits === 0) {
                $significand = BigInteger::of($mantissa);
            } else {
                $significand = BigInteger::of(0x10000000000000 | $mantissa);
            }
        } else {
            // 32-bit: extract the IEEE-754 bit pattern as 8 bytes.
            $packed = pack('E', $value);

            // Get the first 16 bits as an integer.
            /** @var array{1: int} $unpacked */
            $unpacked = unpack('n', $packed);
            $high16 = $unpacked[1];

            // Bits: [sign(1)|exp(11)|mantissa(4)] in header (bytes 0-1) + 48 bits of mantissa in bytes 2-7
            $signBit = ($high16 >> 15) & 1;
            $expBits = ($high16 >> 4) & 0x7FF;
            $mantissaBytes = chr($high16 & 0x0F) . substr($packed, 2);

            // Zero (covers both 0.0 and -0.0).
            if ($expBits === 0 && $mantissaBytes === "\x00\x00\x00\x00\x00\x00\x00") {
                return BigDecimal::zero();
            }

            $mantissa = BigInteger::fromBytes($mantissaBytes, false);

            if ($expBits === 0) {
                $significand = $mantissa;
            } else {
                $significand = $mantissa->plus(BigInteger::of(1)->shiftedLeft(52));
            }
        }

        if ($expBits === 0) {
            // Subnormal: no implicit leading 1-bit; effective exponent = -1074.
            $baseExp = -1074;
        } else {
            // Normal: biased exp - 1023 (bias) - 52 (mantissa shift)
            $baseExp = $expBits - 1075;
        }

        if ($baseExp >= 0) {
            // Result is an integer: significand Ã— 2^baseExp.
            $unscaled = $significand->multipliedBy(BigInteger::of(2)->power($baseExp));
            $scale = 0;
        } else {
            // Fraction: significand Ã— 5^|baseExp| / 10^|baseExp|.
            // Multiplying by 5^n eliminates the 2-based denominator while keeping scale = n.
            $absExp = -$baseExp;
            $unscaled = $significand->multipliedBy(BigInteger::of(5)->power($absExp));
            $scale = $absExp;
        }

        if ($signBit === 1) {
            $unscaled = $unscaled->negated();
        }

        return BigDecimal::ofUnscaledValue($unscaled, $scale)->strippedOfTrailingZeros();
    }

    /**
     * Creates a BigDecimal from the shortest decimal representation of a float that round-trips back to the same value.
     *
     * The result is the shortest BigDecimal that passes `BigDecimal::fromFloatShortest($f)->toFloat() === $f`.
     *
     * Examples:
     *   - `fromFloatShortest(0.3)` returns a BigDecimal with value '0.3'
     *   - `fromFloatShortest(0.1 * 3.0)` returns a BigDecimal with value '0.30000000000000004' (`0.1 * 3.0 !== 0.3`)
     *   - `fromFloatShortest(1.0 / 3.0)` returns a BigDecimal with value '0.3333333333333333'
     *
     * Note that BigDecimal has no concept of negative zero, so `-0.0` and `0.0` both convert to zero.
     *
     * @throws InvalidArgumentException If the value is NaN or infinite.
     */
    public static function fromFloatShortest(float $value): BigDecimal
    {
        if (is_nan($value)) {
            throw InvalidArgumentException::cannotConvertFloat('NaN');
        }
        if (is_infinite($value)) {
            throw InvalidArgumentException::cannotConvertFloat($value > 0 ? 'INF' : '-INF');
        }

        // json_encode() uses serialize_precision; precision -1 uses the shortest round-trip algorithm
        $previousPrecision = ini_set('serialize_precision', '-1');

        try {
            $str = json_encode($value);
        } finally {
            if ($previousPrecision !== false) {
                ini_set('serialize_precision', $previousPrecision);
            }
        }

        assert($str !== false);

        return BigDecimal::of($str)->strippedOfTrailingZeros();
    }

    /**
     * Returns the sum of this number and the given one.
     *
     * The result has a scale of `max($this->scale, $that->scale)`.
     *
     * @param BigNumber|int|string $that The number to add. Must be convertible to a BigDecimal.
     *
     * @throws MathException If the number is not valid, or is not convertible to a BigDecimal.
     *
     * @pure
     */
    public function plus(BigNumber|int|string $that): BigDecimal
    {
        $that = BigDecimal::of($that);

        if ($that->isZero() && $that->scale <= $this->scale) {
            return $this;
        }

        if ($this->isZero() && $this->scale <= $that->scale) {
            return $that;
        }

        [$a, $b] = $this->scaleValues($this, $that);

        $value = CalculatorRegistry::get()->add($a, $b);
        $scale = max($this->scale, $that->scale);

        return new BigDecimal($value, $scale);
    }

    /**
     * Returns the difference of this number and the given one.
     *
     * The result has a scale of `max($this->scale, $that->scale)`.
     *
     * @param BigNumber|int|string $that The number to subtract. Must be convertible to a BigDecimal.
     *
     * @throws MathException If the number is not valid, or is not convertible to a BigDecimal.
     *
     * @pure
     */
    public function minus(BigNumber|int|string $that): BigDecimal
    {
        $that = BigDecimal::of($that);

        if ($that->isZero() && $that->scale <= $this->scale) {
            return $this;
        }

        if ($this->isZero() && $this->scale <= $that->scale) {
            return $that->negated();
        }

        [$a, $b] = $this->scaleValues($this, $that);

        $value = CalculatorRegistry::get()->sub($a, $b);
        $scale = max($this->scale, $that->scale);

        return new BigDecimal($value, $scale);
    }

    /**
     * Returns the product of this number and the given one.
     *
     * The result has a scale of `$this->scale + $that->scale`.
     *
     * @param BigNumber|int|string $that The multiplier. Must be convertible to a BigDecimal.
     *
     * @throws MathException If the multiplier is not valid, or is not convertible to a BigDecimal.
     *
     * @pure
     */
    public function multipliedBy(BigNumber|int|string $that): BigDecimal
    {
        $that = BigDecimal::of($that);

        if ($that->isOneScaleZero()) {
            return $this;
        }

        if ($this->isOneScaleZero()) {
            return $that;
        }

        /** @var non-negative-int $scale */
        $scale = Safe::add($this->scale, $that->scale);

        if ($this->isZero() || $that->isZero()) {
            return new BigDecimal('0', $scale);
        }

        $value = CalculatorRegistry::get()->mul($this->value, $that->value);

        return new BigDecimal($value, $scale);
    }

    /**
     * Returns the result of the division of this number by the given one, at the given scale.
     *
     * @param BigNumber|int|string $that         The divisor. Must be convertible to a BigDecimal.
     * @param non-negative-int     $scale        The desired scale. Must be non-negative.
     * @param RoundingMode         $roundingMode An optional rounding mode, defaults to Unnecessary.
     *
     * @throws MathException              If the divisor is not valid, or is not convertible to a BigDecimal.
     * @throws InvalidArgumentException   If the scale is negative.
     * @throws DivisionByZeroException    If the divisor is zero.
     * @throws RoundingNecessaryException If RoundingMode::Unnecessary is used and the result cannot be represented
     *                                    exactly at the given scale.
     *
     * @pure
     */
    public function dividedBy(BigNumber|int|string $that, int $scale, RoundingMode $roundingMode = RoundingMode::Unnecessary): BigDecimal
    {
        if ($scale < 0) { // @phpstan-ignore smaller.alwaysFalse
            throw InvalidArgumentException::negativeScale();
        }

        $that = BigDecimal::of($that);

        if ($that->isZero()) {
            throw DivisionByZeroException::divisionByZero();
        }

        if ($that->isOneScaleZero() && $scale === $this->scale) {
            return $this;
        }

        $p = $this->valueWithMinScale(Safe::add($that->scale, $scale));
        $q = $that->valueWithMinScale(Safe::sub($this->scale, $scale));

        $calculator = CalculatorRegistry::get();
        $result = $calculator->divRound($p, $q, $roundingMode);

        if ($result === null) {
            [$a, $b] = $this->scaleValues($this->abs(), $that->abs());

            $denominator = $calculator->divQ($b, $calculator->gcd($a, $b));
            $requiredScale = DecimalHelper::computeScaleFromReducedFractionDenominator($denominator);

            if ($requiredScale === null) {
                throw RoundingNecessaryException::decimalDivisionNotExact();
            }

            throw RoundingNecessaryException::decimalDivisionScaleTooSmall();
        }

        return new BigDecimal($result, $scale);
    }

    /**
     * Returns the exact result of the division of this number by the given one.
     *
     * The scale of the result is automatically calculated to fit all the fraction digits.
     *
     * @param BigNumber|int|string $that The divisor. Must be convertible to a BigDecimal.
     *
     * @throws MathException              If the divisor is not valid, or is not convertible to a BigDecimal.
     * @throws DivisionByZeroException    If the divisor is zero.
     * @throws RoundingNecessaryException If the result yields an infinite number of digits.
     *
     * @pure
     */
    public function dividedByExact(BigNumber|int|string $that): BigDecimal
    {
        $that = BigDecimal::of($that);

        if ($that->isZero()) {
            throw DivisionByZeroException::divisionByZero();
        }

        [$a, $b] = $this->scaleValues($this->abs(), $that->abs());

        $calculator = CalculatorRegistry::get();

        $denominator = $calculator->divQ($b, $calculator->gcd($a, $b));
        $scale = DecimalHelper::computeScaleFromReducedFractionDenominator($denominator);

        if ($scale === null) {
            throw RoundingNecessaryException::decimalDivisionNotExact();
        }

        return $this->dividedBy($that, $scale)->strippedOfTrailingZeros();
    }

    /**
     * Returns this number exponentiated to the given value.
     *
     * The result has a scale of `$this->scale * $exponent`.
     *
     * @param non-negative-int $exponent
     *
     * @throws InvalidArgumentException If the exponent is negative.
     *
     * @pure
     */
    public function power(int $exponent): BigDecimal
    {
        if ($exponent === 0) {
            return BigDecimal::one();
        }

        if ($exponent === 1) {
            return $this;
        }

        if ($exponent < 0) { // @phpstan-ignore smaller.alwaysFalse
            throw InvalidArgumentException::negativeExponent();
        }

        /** @var non-negative-int $scale */
        $scale = Safe::mul($this->scale, $exponent);

        return new BigDecimal(CalculatorRegistry::get()->pow($this->value, $exponent), $scale);
    }

    /**
     * Returns the quotient of the division of this number by the given one.
     *
     * The quotient has a scale of `0`.
     *
     * Examples:
     *
     * - `7.5` quotient `3` returns `2`
     * - `7.5` quotient `-3` returns `-2`
     * - `-7.5` quotient `3` returns `-2`
     * - `-7.5` quotient `-3` returns `2`
     *
     * @param BigNumber|int|string $that The divisor. Must be convertible to a BigDecimal.
     *
     * @throws MathException           If the divisor is not valid, or is not convertible to a BigDecimal.
     * @throws DivisionByZeroException If the divisor is zero.
     *
     * @pure
     */
    public function quotient(BigNumber|int|string $that): BigDecimal
    {
        $that = BigDecimal::of($that);

        if ($that->isZero()) {
            throw DivisionByZeroException::divisionByZero();
        }

        $p = $this->valueWithMinScale($that->scale);
        $q = $that->valueWithMinScale($this->scale);

        $quotient = CalculatorRegistry::get()->divQ($p, $q);

        return new BigDecimal($quotient, 0);
    }

    /**
     * Returns the remainder of the division of this number by the given one.
     *
     * The remainder has a scale of `max($this->scale, $that->scale)`.
     * The remainder, when non-zero, has the same sign as the dividend.
     *
     * Examples:
     *
     * - `7.5` remainder `3` returns `1.5`
     * - `7.5` remainder `-3` returns `1.5`
     * - `-7.5` remainder `3` returns `-1.5`
     * - `-7.5` remainder `-3` returns `-1.5`
     *
     * @param BigNumber|int|string $that The divisor. Must be convertible to a BigDecimal.
     *
     * @throws MathException           If the divisor is not valid, or is not convertible to a BigDecimal.
     * @throws DivisionByZeroException If the divisor is zero.
     *
     * @pure
     */
    public function remainder(BigNumber|int|string $that): BigDecimal
    {
        $that = BigDecimal::of($that);

        if ($that->isZero()) {
            throw DivisionByZeroException::divisionByZero();
        }

        $p = $this->valueWithMinScale($that->scale);
        $q = $that->valueWithMinScale($this->scale);

        $remainder = CalculatorRegistry::get()->divR($p, $q);

        $scale = max($this->scale, $that->scale);

        return new BigDecimal($remainder, $scale);
    }

    /**
     * Returns the quotient and remainder of the division of this number by the given one.
     *
     * The quotient has a scale of `0`, and the remainder has a scale of `max($this->scale, $that->scale)`.
     *
     * Examples:
     *
     * - `7.5` quotientAndRemainder `3` returns [`2`, `1.5`]
     * - `7.5` quotientAndRemainder `-3` returns [`-2`, `1.5`]
     * - `-7.5` quotientAndRemainder `3` returns [`-2`, `-1.5`]
     * - `-7.5` quotientAndRemainder `-3` returns [`2`, `-1.5`]
     *
     * @param BigNumber|int|string $that The divisor. Must be convertible to a BigDecimal.
     *
     * @return array{BigDecimal, BigDecimal} An array containing the quotient and the remainder.
     *
     * @throws MathException           If the divisor is not valid, or is not convertible to a BigDecimal.
     * @throws DivisionByZeroException If the divisor is zero.
     *
     * @pure
     */
    public function quotientAndRemainder(BigNumber|int|string $that): array
    {
        $that = BigDecimal::of($that);

        if ($that->isZero()) {
            throw DivisionByZeroException::divisionByZero();
        }

        $p = $this->valueWithMinScale($that->scale);
        $q = $that->valueWithMinScale($this->scale);

        [$quotient, $remainder] = CalculatorRegistry::get()->divQR($p, $q);

        $scale = max($this->scale, $that->scale);

        $quotient = new BigDecimal($quotient, 0);
        $remainder = new BigDecimal($remainder, $scale);

        return [$quotient, $remainder];
    }

    /**
     * Returns the square root of this number, rounded to the given scale according to the given rounding mode.
     *
     * @param non-negative-int $scale        The target scale. Must be non-negative.
     * @param RoundingMode     $roundingMode An optional rounding mode, defaults to Unnecessary.
     *
     * @throws InvalidArgumentException   If the scale is negative.
     * @throws NegativeNumberException    If this number is negative.
     * @throws RoundingNecessaryException If RoundingMode::Unnecessary is used and the result cannot be represented
     *                                    exactly at the given scale.
     *
     * @pure
     */
    public function sqrt(int $scale, RoundingMode $roundingMode = RoundingMode::Unnecessary): BigDecimal
    {
        if ($scale < 0) { // @phpstan-ignore smaller.alwaysFalse
            throw InvalidArgumentException::negativeScale();
        }

        if ($this->isZero()) {
            return new BigDecimal('0', $scale);
        }

        if ($this->isNegative()) {
            throw NegativeNumberException::squareRootOfNegativeNumber();
        }

        $value = $this->value;
        $inputScale = $this->scale;

        if ($inputScale % 2 !== 0) {
            $value .= '0';
            $inputScale = Safe::add($inputScale, 1);
        }

        $calculator = CalculatorRegistry::get();

        // Keep one extra digit for rounding.
        $intermediateScale = Safe::add(max($scale, intdiv($inputScale, 2)), 1);
        $value .= str_repeat('0', Safe::sub(Safe::mul(2, $intermediateScale), $inputScale));

        $sqrt = $calculator->sqrt($value);
        $isExact = $calculator->mul($sqrt, $sqrt) === $value;

        if (! $isExact) {
            if ($roundingMode === RoundingMode::Unnecessary) {
                throw RoundingNecessaryException::decimalSquareRootNotExact();
            }

            // Non-perfect-square sqrt is irrational, so the true value is strictly above this sqrt floor.
            // Add one at the intermediate scale to guarantee Up/Ceiling round up at the target scale.
            if (in_array($roundingMode, [RoundingMode::Up, RoundingMode::Ceiling], true)) {
                $sqrt = $calculator->add($sqrt, '1');
            }

            // Irrational sqrt cannot land exactly on a midpoint; the intermediate approximation can,
            // so rewrite every tie-sensitive mode that could round down on such a phantom tie to HalfUp.
            elseif (in_array($roundingMode, [
                RoundingMode::HalfDown,
                RoundingMode::HalfFloor,
                RoundingMode::HalfEven,
                RoundingMode::HalfOdd,
            ], true)) {
                $roundingMode = RoundingMode::HalfUp;
            }
        }

        $scaled = DecimalHelper::scale($sqrt, $intermediateScale, $scale, $roundingMode);

        if ($scaled === null) {
            throw RoundingNecessaryException::decimalSquareRootScaleTooSmall();
        }

        return new BigDecimal($scaled, $scale);
    }

    /**
     * Returns the nth root of this number, rounded to the given scale according to the given rounding mode.
     *
     * For odd $n, the operation is defined for negative inputs: the sign is preserved and the
     * magnitude of the root is |$this|^(1/$n).
     *
     * @param positive-int     $n            The root degree. Must be a strictly positive integer.
     * @param non-negative-int $scale        The target scale. Must be non-negative.
     * @param RoundingMode     $roundingMode An optional rounding mode, defaults to Unnecessary.
     *
     * @throws InvalidArgumentException   If $n is less than 1 or $scale is negative.
     * @throws NegativeNumberException    If this number is negative and $n is even.
     * @throws RoundingNecessaryException If RoundingMode::Unnecessary is used and the result cannot be represented
     *                                    exactly at the given scale.
     *
     * @pure
     */
    public function nthRoot(int $n, int $scale, RoundingMode $roundingMode = RoundingMode::Unnecessary): BigDecimal
    {
        if ($n < 1) { // @phpstan-ignore smaller.alwaysFalse
            throw InvalidArgumentException::nonPositiveNthRootDegree();
        }

        if ($scale < 0) { // @phpstan-ignore smaller.alwaysFalse
            throw InvalidArgumentException::negativeScale();
        }

        $isNegative = $this->isNegative();

        if ($isNegative && $n % 2 === 0) {
            throw NegativeNumberException::nthRootOfNegativeNumber();
        }

        if ($n === 1) {
            $scaled = DecimalHelper::scale($this->value, $this->scale, $scale, $roundingMode);

            if ($scaled === null) {
                throw RoundingNecessaryException::decimalNthRootScaleTooSmall();
            }

            return new BigDecimal($scaled, $scale);
        }

        if ($this->isZero()) {
            return new BigDecimal('0', $scale);
        }

        $value = $this->value;
        $inputScale = $this->scale;

        // Pad inputScale up to a multiple of $n so the shift by n*intermediateScale lands cleanly.
        $remainder = $inputScale % $n;

        if ($remainder !== 0) {
            $padding = $n - $remainder;
            $value .= str_repeat('0', $padding);
            $inputScale = Safe::add($inputScale, $padding);
        }

        $calculator = CalculatorRegistry::get();

        // Keep one extra digit beyond the target scale for rounding.
        $intermediateScale = Safe::add(max($scale, intdiv($inputScale, $n)), 1);
        $value .= str_repeat('0', Safe::sub(Safe::mul($n, $intermediateScale), $inputScale));

        $root = $calculator->nthRoot($value, $n);
        $isExact = $calculator->pow($root, $n) === $value;

        if (! $isExact) {
            if ($roundingMode === RoundingMode::Unnecessary) {
                throw RoundingNecessaryException::decimalNthRootNotExact();
            }

            $isPositive = ! $isNegative;

            // Non-perfect-nth-power root is irrational, so the true value has strictly greater
            // magnitude than this truncated root. For "round away from zero" modes, bump the
            // integer root one step further from zero so the subsequent rescale rounds up.
            if (
                $roundingMode === RoundingMode::Up
                || ($roundingMode === RoundingMode::Ceiling && $isPositive)
                || ($roundingMode === RoundingMode::Floor && ! $isPositive)
            ) {
                $root = $isPositive
                    ? $calculator->add($root, '1')
                    : $calculator->sub($root, '1');
            }

            // Irrational nth root cannot land on a midpoint. For any Half* mode, the "tie" case
            // never occurs, so rewrite them all to HalfUp (round half away from zero), which is
            // the mode whose away-from-zero direction matches the sign of the (strictly larger
            // in magnitude) true value for both positive and negative inputs.
            elseif (in_array($roundingMode, [
                RoundingMode::HalfDown,
                RoundingMode::HalfCeiling,
                RoundingMode::HalfFloor,
                RoundingMode::HalfEven,
                RoundingMode::HalfOdd,
            ], true)) {
                $roundingMode = RoundingMode::HalfUp;
            }
        }

        $scaled = DecimalHelper::scale($root, $intermediateScale, $scale, $roundingMode);

        if ($scaled === null) {
            throw RoundingNecessaryException::decimalNthRootScaleTooSmall();
        }

        return new BigDecimal($scaled, $scale);
    }

    /**
     * Returns a copy of this BigDecimal with the decimal point moved to the left by the given number of places.
     *
     * If $places is negative, the decimal point is moved to the right by the absolute value instead.
     *
     * @pure
     */
    public function withPointMovedLeft(int $places): BigDecimal
    {
        if ($places === 0) {
            return $this;
        }

        if ($places < 0) {
            return $this->withPointMovedRight(Safe::neg($places));
        }

        /** @var non-negative-int $scale */
        $scale = Safe::add($this->scale, $places);

        return new BigDecimal($this->value, $scale);
    }

    /**
     * Returns a copy of this BigDecimal with the decimal point moved to the right by the given number of places.
     *
     * If $places is negative, the decimal point is moved to the left by the absolute value instead.
     *
     * @pure
     */
    public function withPointMovedRight(int $places): BigDecimal
    {
        if ($places === 0) {
            return $this;
        }

        if ($places < 0) {
            return $this->withPointMovedLeft(Safe::neg($places));
        }

        $value = $this->value;
        $scale = Safe::sub($this->scale, $places);

        if ($scale < 0) {
            if ($value !== '0') {
                $value .= str_repeat('0', Safe::neg($scale));
            }
            $scale = 0;
        }

        return new BigDecimal($value, $scale);
    }

    /**
     * Returns a copy of this BigDecimal with any trailing zeros removed from the fractional part.
     *
     * Examples:
     *
     * - `1.200` returns `1.2`
     * - `1.000` returns `1`
     * - `100` returns `100`
     *
     * @pure
     */
    public function strippedOfTrailingZeros(): BigDecimal
    {
        if ($this->scale === 0) {
            return $this;
        }

        $trimmedValue = rtrim($this->value, '0');

        if ($trimmedValue === '') {
            return BigDecimal::zero();
        }

        $trimmableZeros = strlen($this->value) - strlen($trimmedValue);

        if ($trimmableZeros === 0) {
            return $this;
        }

        if ($trimmableZeros > $this->scale) {
            $trimmableZeros = $this->scale;
        }

        $value = substr($this->value, 0, -$trimmableZeros);

        /** @var non-negative-int $scale */
        $scale = $this->scale - $trimmableZeros;

        return new BigDecimal($value, $scale);
    }

    #[Override]
    public function negated(): static
    {
        return new BigDecimal(CalculatorRegistry::get()->neg($this->value), $this->scale);
    }

    #[Override]
    public function compareTo(BigNumber|int|string $that): int
    {
        $that = BigNumber::of($that);

        if ($that instanceof BigInteger) {
            $that = $that->toBigDecimal();
        }

        if ($that instanceof BigDecimal) {
            [$a, $b] = $this->scaleValues($this, $that);

            return CalculatorRegistry::get()->cmp($a, $b);
        }

        return -$that->compareTo($this);
    }

    #[Override]
    public function getSign(): int
    {
        return ($this->value === '0') ? 0 : (($this->value[0] === '-') ? -1 : 1);
    }

    /**
     * Returns the unscaled value of this decimal number.
     *
     * For example, the unscaled value of `123.456` is `123456`.
     *
     * @pure
     */
    public function getUnscaledValue(): BigInteger
    {
        return self::newBigInteger($this->value);
    }

    /**
     * Returns the scale of this decimal number.
     *
     * The scale is the number of digits after the decimal point. For example, the scale of `123.456` is `3`.
     *
     * @return non-negative-int
     *
     * @pure
     */
    public function getScale(): int
    {
        return $this->scale;
    }

    /**
     * Returns the number of significant digits in the number.
     *
     * This is the number of digits in the unscaled value of the number.
     * The sign has no impact on the result.
     *
     * Examples:
     *   0 => 1
     *   0.0 => 1
     *   123 => 3
     *   123.456 => 6
     *   0.00123 => 3
     *   0.0012300 => 5
     *
     * @return positive-int
     *
     * @pure
     */
    public function getPrecision(): int
    {
        $length = strlen($this->value);

        /** @var positive-int */
        return ($this->value[0] === '-') ? $length - 1 : $length;
    }

    /**
     * Returns the integral part of this decimal number.
     *
     * Examples:
     *
     * - `123.456` returns `123`
     * - `-123.456` returns `-123`
     * - `0.123` returns `0`
     * - `-0.123` returns `0`
     *
     * The following identity holds: `$d->isEqualTo($d->getFractionalPart()->plus($d->getIntegralPart()))`. Note that in
     * this identity, the operand order is significant: the reversed form throws when the fractional part is non-zero.
     *
     * @pure
     */
    public function getIntegralPart(): BigInteger
    {
        if ($this->scale === 0) {
            return self::newBigInteger($this->value);
        }

        $value = DecimalHelper::padUnscaledValue($this->value, $this->scale);
        $integerPart = substr($value, 0, -$this->scale);

        if ($integerPart === '-0') {
            $integerPart = '0';
        }

        return self::newBigInteger($integerPart);
    }

    /**
     * Returns the fractional part of this decimal number.
     *
     * Examples:
     *
     * - `123.456` returns `0.456`
     * - `-123.456` returns `-0.456`
     * - `123` returns `0`
     * - `-123` returns `0`
     * - `123.000` returns `0.000`
     *
     * The result always has the same scale as `$this`.
     *
     * The following identity holds: `$d->isEqualTo($d->getFractionalPart()->plus($d->getIntegralPart()))`. Note that in
     * this identity, the operand order is significant: the reversed form throws when the fractional part is non-zero.
     *
     * @pure
     */
    public function getFractionalPart(): BigDecimal
    {
        if ($this->scale === 0) {
            return BigDecimal::zero();
        }

        return $this->minus($this->getIntegralPart());
    }

    #[Override]
    public function toBigInteger(): BigInteger
    {
        $value = DecimalHelper::tryScaleExactly($this->value, $this->scale, 0);

        if ($value !== null) {
            return self::newBigInteger($value);
        }

        throw RoundingNecessaryException::decimalNotConvertibleToInteger();
    }

    #[Override]
    public function toBigDecimal(): BigDecimal
    {
        return $this;
    }

    #[Override]
    public function toBigRational(): BigRational
    {
        $numerator = self::newBigInteger($this->value);
        $denominator = self::newBigInteger('1' . str_repeat('0', $this->scale));

        return self::newBigRational($numerator, $denominator, false, true);
    }

    #[Override]
    public function toScale(int $scale, RoundingMode $roundingMode = RoundingMode::Unnecessary): BigDecimal
    {
        if ($scale < 0) { // @phpstan-ignore smaller.alwaysFalse
            throw InvalidArgumentException::negativeScale();
        }

        if ($scale === $this->scale) {
            return $this;
        }

        $value = DecimalHelper::scale($this->value, $this->scale, $scale, $roundingMode);

        if ($value === null) {
            throw RoundingNecessaryException::decimalScaleTooSmall();
        }

        return new BigDecimal($value, $scale);
    }

    #[Override]
    public function toInt(): int
    {
        return $this->toBigInteger()->toInt();
    }

    #[Override]
    public function toFloat(): float
    {
        return (float) $this->toString();
    }

    /**
     * @return numeric-string
     */
    #[Override]
    public function toString(): string
    {
        if ($this->scale === 0) {
            /** @var numeric-string */
            return $this->value;
        }

        $value = DecimalHelper::padUnscaledValue($this->value, $this->scale);

        /** @phpstan-ignore return.type */
        return substr($value, 0, -$this->scale) . '.' . substr($value, -$this->scale);
    }

    /**
     * This method is required for serializing the object and SHOULD NOT be accessed directly.
     *
     * @internal
     *
     * @return array{value: string, scale: non-negative-int}
     */
    public function __serialize(): array
    {
        return ['value' => $this->value, 'scale' => $this->scale];
    }

    /**
     * This method is only here to allow unserializing the object and cannot be accessed directly.
     *
     * @internal
     *
     * @param array{value: string, scale: non-negative-int} $data
     *
     * @throws LogicException
     */
    public function __unserialize(array $data): void
    {
        if (isset($this->value)) {
            throw new LogicException('__unserialize() is an internal function, it must not be called directly.');
        }

        $this->value = $data['value'];
        $this->scale = $data['scale'];
    }

    #[Override]
    protected static function from(BigNumber $number): static
    {
        return $number->toBigDecimal();
    }

    #[Override]
    protected function digitCount(): int
    {
        // At least one integral digit is written: 0.001 counts 4 digits.
        return max($this->getPrecision(), $this->scale + 1);
    }

    /**
     * Puts the internal values of the given decimal numbers on the same scale.
     *
     * @return array{string, string} The scaled integer values of $x and $y.
     *
     * @pure
     */
    private function scaleValues(BigDecimal $x, BigDecimal $y): array
    {
        $a = $x->value;
        $b = $y->value;

        if ($b !== '0' && $x->scale > $y->scale) {
            $b .= str_repeat('0', $x->scale - $y->scale);
        } elseif ($a !== '0' && $x->scale < $y->scale) {
            $a .= str_repeat('0', $y->scale - $x->scale);
        }

        return [$a, $b];
    }

    /**
     * @pure
     */
    private function valueWithMinScale(int $scale): string
    {
        $value = $this->value;

        if ($this->value !== '0' && $scale > $this->scale) {
            $value .= str_repeat('0', $scale - $this->scale);
        }

        return $value;
    }

    /**
     * @pure
     */
    private function isOneScaleZero(): bool
    {
        return $this->value === '1' && $this->scale === 0;
    }
}
<?php

declare(strict_types=1);

namespace Brick\Math\Exception;

use function sprintf;

/**
 * Exception thrown when an invalid argument is provided.
 */
final class InvalidArgumentException extends \InvalidArgumentException implements MathException
{
    /**
     * @internal
     *
     * @pure
     */
    public function __construct(string $message)
    {
        parent::__construct($message);
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function baseOutOfRange(int $base): self
    {
        return new self(sprintf('Base %d is out of range [2, 36].', $base));
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function negativeScale(): self
    {
        return new self('The scale must not be negative.');
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function negativeBitIndex(): self
    {
        return new self('The bit index must not be negative.');
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function negativeBitCount(): self
    {
        return new self('The bit count must not be negative.');
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function alphabetTooShort(): self
    {
        return new self('The alphabet must contain at least 2 characters.');
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function duplicateCharsInAlphabet(): self
    {
        return new self('The alphabet must not contain duplicate characters.');
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function minGreaterThanMax(): self
    {
        return new self('The minimum value must be less than or equal to the maximum value.');
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function cannotConvertFloat(string $type): self
    {
        return new self(sprintf('Cannot convert %s to a BigDecimal.', $type));
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function negativeExponent(): self
    {
        return new self('The exponent must not be negative.');
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function negativeModulus(): self
    {
        return new self('The modulus must not be negative.');
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function nonPositiveNthRootDegree(): self
    {
        return new self('The degree of an nth root must be a positive integer.');
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function nonPositiveMaxDigits(): self
    {
        return new self('The maximum number of digits must be a positive integer.');
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function invalidAllowedSyntax(): self
    {
        return new self('The allowed syntax must be a list of NumberSyntax cases.');
    }
}
<?php

declare(strict_types=1);

namespace Brick\Math\Exception;

use RuntimeException;

/**
 * Exception thrown when attempting to perform an unsupported operation, such as a square root, on a negative number.
 */
final class NegativeNumberException extends RuntimeException implements MathException
{
    /**
     * @internal
     *
     * @pure
     */
    public function __construct(string $message)
    {
        parent::__construct($message);
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function squareRootOfNegativeNumber(): self
    {
        return new self('Cannot calculate the square root of a negative number.');
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function nthRootOfNegativeNumber(): self
    {
        return new self('Cannot take an even nth root of a negative number.');
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function toArbitraryBaseOfNegativeNumber(): self
    {
        return new self('Cannot convert a negative number to an arbitrary base.');
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function unsignedBytesOfNegativeNumber(): self
    {
        return new self('Cannot convert a negative number to a byte string in unsigned mode.');
    }
}
<?php

declare(strict_types=1);

namespace Brick\Math\Exception;

use RuntimeException;

/**
 * Exception thrown when a number cannot be represented at the requested scale without rounding.
 */
final class RoundingNecessaryException extends RuntimeException implements MathException
{
    /**
     * @internal
     *
     * @pure
     */
    public function __construct(string $message)
    {
        parent::__construct($message);
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function decimalScaleTooSmall(): self
    {
        return new self('This decimal number cannot be represented at the requested scale without rounding.');
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function rationalScaleTooSmall(): self
    {
        return new self('This rational number cannot be represented at the requested scale without rounding.');
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function integerDivisionNotExact(): self
    {
        return new self('The division has a non-zero remainder and cannot be represented as an integer without rounding.');
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function decimalDivisionNotExact(): self
    {
        return new self('The division yields a non-terminating decimal expansion and cannot be represented as a decimal without rounding.');
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function decimalDivisionScaleTooSmall(): self
    {
        return new self('The division result is exact but cannot be represented at the requested scale without rounding.');
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function integerSquareRootNotExact(): self
    {
        return new self('The square root is not exact and cannot be represented as an integer without rounding.');
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function decimalSquareRootNotExact(): self
    {
        return new self('The square root is not exact and cannot be represented as a decimal without rounding.');
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function decimalSquareRootScaleTooSmall(): self
    {
        return new self('The square root is exact but cannot be represented at the requested scale without rounding.');
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function integerNthRootNotExact(): self
    {
        return new self('The nth root is not exact and cannot be represented as an integer without rounding.');
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function decimalNthRootNotExact(): self
    {
        return new self('The nth root is not exact and cannot be represented as a decimal without rounding.');
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function decimalNthRootScaleTooSmall(): self
    {
        return new self('The nth root is exact but cannot be represented at the requested scale without rounding.');
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function decimalNotConvertibleToInteger(): self
    {
        return new self('This decimal number cannot be represented as an integer without rounding.');
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function rationalNotConvertibleToInteger(): self
    {
        return new self('This rational number cannot be represented as an integer without rounding.');
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function rationalNotConvertibleToDecimal(): self
    {
        return new self('This rational number has a non-terminating decimal expansion and cannot be represented as a decimal without rounding.');
    }
}
<?php

declare(strict_types=1);

namespace Brick\Math\Exception;

use Brick\Math\NumberSyntax;
use RuntimeException;

use function ord;
use function sprintf;
use function strlen;
use function substr;

/**
 * Exception thrown when attempting to create a number from a string with an invalid format.
 */
final class NumberFormatException extends RuntimeException implements MathException
{
    /**
     * @internal
     *
     * @pure
     */
    public function __construct(string $message)
    {
        parent::__construct($message);
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function invalidFormat(string $value): self
    {
        return new self(sprintf(
            'Value "%s" does not represent a valid number.',
            self::truncateAndEscape($value),
        ));
    }

    /**
     * @internal
     *
     * @param string $char The failing character.
     *
     * @pure
     */
    public static function charNotInAlphabet(string $char): self
    {
        return new self(sprintf(
            'Character "%s" is not valid in the given alphabet.',
            self::escapeChar($char),
        ));
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function charNotValidInBase(string $char, int $base): self
    {
        return new self(sprintf(
            'Character "%s" is not valid in base %d.',
            self::escapeChar($char),
            $base,
        ));
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function emptyNumber(): self
    {
        return new self('The number must not be empty.');
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function emptyByteString(): self
    {
        return new self('The byte string must not be empty.');
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function exponentTooLarge(): self
    {
        return new self('The exponent is too large to be represented as an integer.');
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function tooManyDigits(int $maxDigits): self
    {
        return new self(sprintf(
            'The number exceeds the maximum number of %d digits.',
            $maxDigits,
        ));
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function syntaxNotAllowed(NumberSyntax $syntax): self
    {
        return new self(sprintf('The %s syntax is not allowed.', match ($syntax) {
            NumberSyntax::DecimalPoint => 'decimal point',
            NumberSyntax::Exponent => 'exponent',
            NumberSyntax::Fraction => 'fraction',
        }));
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function zeroDenominator(): self
    {
        return new self('The denominator of a rational number must not be zero.');
    }

    /**
     * @pure
     */
    private static function truncateAndEscape(string $value): string
    {
        if (strlen($value) > 40) {
            $value = substr($value, 0, 40) . '...';
        }

        $escaped = '';
        $length = strlen($value);

        for ($i = 0; $i < $length; $i++) {
            $escaped .= self::escapeChar($value[$i]);
        }

        return $escaped;
    }

    /**
     * @pure
     */
    private static function escapeChar(string $char): string
    {
        $ord = ord($char);

        return match (true) {
            $char === "\t" => '\t',
            $char === "\n" => '\n',
            $char === "\r" => '\r',
            $char === '\\' => '\\\\',
            $char === '"' => '\"',
            $ord < 32 || $ord > 126 => sprintf('\x%02X', $ord),
            default => $char,
        };
    }
}
<?php

declare(strict_types=1);

namespace Brick\Math\Exception;

use RuntimeException;
use Throwable;

use function get_debug_type;
use function sprintf;

/**
 * Exception thrown when random byte generation fails.
 */
final class RandomSourceException extends RuntimeException implements MathException
{
    /**
     * @internal
     *
     * @pure
     */
    public function __construct(string $message, ?Throwable $previous = null)
    {
        parent::__construct($message, 0, $previous);
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function randomSourceFailure(Throwable $previous): self
    {
        return new self('Random byte generation failed.', $previous);
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function invalidRandomBytesType(mixed $value): self
    {
        return new self(sprintf(
            'The random bytes generator must return a string, got %s.',
            get_debug_type($value),
        ));
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function invalidRandomBytesLength(int $expectedLength, int $actualLength): self
    {
        return new self(sprintf(
            'The random bytes generator returned %d byte(s), expected %d.',
            $actualLength,
            $expectedLength,
        ));
    }
}
<?php

declare(strict_types=1);

namespace Brick\Math\Exception;

use Throwable;

/**
 * Base interface for all math exceptions.
 */
interface MathException extends Throwable
{
}
<?php

declare(strict_types=1);

namespace Brick\Math\Exception;

use Brick\Math\BigInteger;
use RuntimeException;

use function sprintf;

use const PHP_INT_MAX;
use const PHP_INT_MIN;

/**
 * Exception thrown when a native integer overflow occurs.
 */
final class IntegerOverflowException extends RuntimeException implements MathException
{
    /**
     * @internal
     *
     * @pure
     */
    public function __construct(string $message)
    {
        parent::__construct($message);
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function integerOutOfRange(BigInteger $value): self
    {
        $message = '%s is out of range [%d, %d] and cannot be represented as an integer.';

        return new self(sprintf($message, $value->toString(), PHP_INT_MIN, PHP_INT_MAX));
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function nativeIntegerOverflow(string $expression): self
    {
        return new self(sprintf(
            'Cannot compute %s because the result is outside the native integer range [%d, %d].',
            $expression,
            PHP_INT_MIN,
            PHP_INT_MAX,
        ));
    }
}
<?php

declare(strict_types=1);

namespace Brick\Math\Exception;

use RuntimeException;

/**
 * Exception thrown when a division by zero occurs.
 */
final class DivisionByZeroException extends RuntimeException implements MathException
{
    /**
     * @internal
     *
     * @pure
     */
    public function __construct(string $message)
    {
        parent::__construct($message);
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function divisionByZero(): self
    {
        return new self('Division by zero.');
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function zeroModulus(): self
    {
        return new self('The modulus must not be zero.');
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function zeroDenominator(): self
    {
        return new self('The denominator of a rational number must not be zero.');
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function reciprocalOfZero(): self
    {
        return new self('The reciprocal of zero is undefined.');
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function zeroToNegativePower(): self
    {
        return new self('Cannot raise zero to a negative power.');
    }
}
<?php

declare(strict_types=1);

namespace Brick\Math\Exception;

use RuntimeException;

/**
 * Exception thrown when attempting to compute a modular inverse that does not exist.
 */
final class NoInverseException extends RuntimeException implements MathException
{
    /**
     * @internal
     *
     * @pure
     */
    public function __construct(string $message)
    {
        parent::__construct($message);
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function noModularInverse(): self
    {
        return new self('This number has no multiplicative inverse modulo the given modulus (they are not coprime).');
    }
}
<?php

declare(strict_types=1);

namespace Brick\Math\Exception;

use RuntimeException;

use function preg_last_error_msg;
use function sprintf;

use const PHP_INT_SIZE;

/**
 * Exception thrown when the current PHP platform does not support a required feature.
 */
final class PlatformException extends RuntimeException implements MathException
{
    /**
     * @internal
     *
     * @pure
     */
    public function __construct(string $message)
    {
        parent::__construct($message);
    }

    /**
     * @internal
     *
     * @pure
     *
     * @codeCoverageIgnore
     */
    public static function unsupportedFloatFormat(): self
    {
        return new self('Unsupported float format: expected IEEE-754 double.');
    }

    /**
     * @internal
     *
     * @pure
     *
     * @codeCoverageIgnore
     */
    public static function unsupportedIntSize(): self
    {
        return new self(sprintf('Unsupported integer size: %d bytes, expected 4 or 8.', PHP_INT_SIZE));
    }

    /**
     * @internal
     *
     * @pure
     */
    public static function pcreFailure(): self
    {
        return new self(sprintf(
            'PCRE regular expression matching failed: %s. Check the pcre.* ini settings.',
            preg_last_error_msg(),
        ));
    }
}
<?php

declare(strict_types=1);

namespace Brick\Math;

use Brick\Math\Exception\DivisionByZeroException;
use Brick\Math\Exception\InvalidArgumentException;
use Brick\Math\Exception\MathException;
use Brick\Math\Exception\RoundingNecessaryException;
use Brick\Math\Internal\DecimalHelper;
use Brick\Math\Internal\Safe;
use LogicException;
use Override;

use function max;
use function min;
use function strlen;
use function substr;

/**
 * An arbitrarily large rational number.
 *
 * This class is immutable.
 *
 * Fractions are automatically simplified to lowest terms. For example, `2/4` becomes `1/2`.
 * The denominator is always strictly positive; the sign is carried by the numerator.
 */
final readonly class BigRational extends BigNumber
{
    /**
     * The numerator.
     */
    private BigInteger $numerator;

    /**
     * The denominator. Always strictly positive.
     */
    private BigInteger $denominator;

    /**
     * Protected constructor. Use a factory method to obtain an instance.
     *
     * @param BigInteger $numerator        The numerator.
     * @param BigInteger $denominator      The denominator.
     * @param bool       $checkDenominator Whether to check the denominator for negative and zero.
     * @param bool       $simplify         Whether to simplify the fraction to lowest terms.
     *
     * @throws DivisionByZeroException If the denominator is zero.
     *
     * @pure
     */
    protected function __construct(BigInteger $numerator, BigInteger $denominator, bool $checkDenominator, bool $simplify)
    {
        if ($checkDenominator) {
            if ($denominator->isZero()) {
                throw DivisionByZeroException::zeroDenominator();
            }

            if ($denominator->isNegative()) {
                $numerator = $numerator->negated();
                $denominator = $denominator->negated();
            }
        }

        if ($simplify) {
            $gcd = $numerator->gcd($denominator);

            $numerator = $numerator->quotient($gcd);
            $denominator = $denominator->quotient($gcd);
        }

        $this->numerator = $numerator;
        $this->denominator = $denominator;
    }

    /**
     * Creates a BigRational out of a numerator and a denominator.
     *
     * If the denominator is negative, the signs of both the numerator and the denominator
     * will be inverted to ensure that the denominator is always positive.
     *
     * @param BigNumber|int|string $numerator   The numerator. Must be convertible to a BigInteger.
     * @param BigNumber|int|string $denominator The denominator. Must be convertible to a BigInteger.
     *
     * @throws MathException           If an argument is not valid, or is not convertible to a BigInteger.
     * @throws DivisionByZeroException If the denominator is zero.
     *
     * @pure
     */
    public static function ofFraction(
        BigNumber|int|string $numerator,
        BigNumber|int|string $denominator,
    ): BigRational {
        $numerator = BigInteger::of($numerator);
        $denominator = BigInteger::of($denominator);

        return new BigRational($numerator, $denominator, true, true);
    }

    /**
     * Returns a BigRational representing zero.
     *
     * @pure
     */
    public static function zero(): BigRational
    {
        /** @var BigRational|null $zero */
        static $zero;

        if ($zero === null) {
            $zero = new BigRational(BigInteger::zero(), BigInteger::one(), false, false);
        }

        return $zero;
    }

    /**
     * Returns a BigRational representing one.
     *
     * @pure
     */
    public static function one(): BigRational
    {
        /** @var BigRational|null $one */
        static $one;

        if ($one === null) {
            $one = new BigRational(BigInteger::one(), BigInteger::one(), false, false);
        }

        return $one;
    }

    /**
     * Returns a BigRational representing ten.
     *
     * @pure
     */
    public static function ten(): BigRational
    {
        /** @var BigRational|null $ten */
        static $ten;

        if ($ten === null) {
            $ten = new BigRational(BigInteger::ten(), BigInteger::one(), false, false);
        }

        return $ten;
    }

    /**
     * Returns the numerator of this rational number.
     *
     * @pure
     */
    public function getNumerator(): BigInteger
    {
        return $this->numerator;
    }

    /**
     * Returns the denominator of this rational number.
     *
     * The denominator is always strictly positive.
     *
     * @pure
     */
    public function getDenominator(): BigInteger
    {
        return $this->denominator;
    }

    /**
     * Returns the integral part of this rational number.
     *
     * Examples:
     *
     * - `7/3` returns `2` (since 7/3 = 2 + 1/3)
     * - `-7/3` returns `-2` (since -7/3 = -2 + (-1/3))
     *
     * The following identity holds: `$r->isEqualTo($r->getFractionalPart()->plus($r->getIntegralPart()))`. Note that in
     * this identity, the operand order is significant: the reversed form throws when the fractional part is non-zero.
     *
     * @pure
     */
    public function getIntegralPart(): BigInteger
    {
        return $this->numerator->quotient($this->denominator);
    }

    /**
     * Returns the fractional part of this rational number.
     *
     * Examples:
     *
     * - `7/3` returns `1/3` (since 7/3 = 2 + 1/3)
     * - `-7/3` returns `-1/3` (since -7/3 = -2 + (-1/3))
     *
     * The following identity holds: `$r->isEqualTo($r->getFractionalPart()->plus($r->getIntegralPart()))`. Note that in
     * this identity, the operand order is significant: the reversed form throws when the fractional part is non-zero.
     *
     * @pure
     */
    public function getFractionalPart(): BigRational
    {
        return new BigRational($this->numerator->remainder($this->denominator), $this->denominator, false, false);
    }

    /**
     * Returns the sum of this number and the given one.
     *
     * @param BigNumber|int|string $that The number to add.
     *
     * @throws MathException If the number is not valid.
     *
     * @pure
     */
    public function plus(BigNumber|int|string $that): BigRational
    {
        $that = BigRational::of($that);

        if ($that->isZero()) {
            return $this;
        }

        if ($this->isZero()) {
            return $that;
        }

        $numerator = $this->numerator->multipliedBy($that->denominator);
        $numerator = $numerator->plus($that->numerator->multipliedBy($this->denominator));
        $denominator = $this->denominator->multipliedBy($that->denominator);

        return new BigRational($numerator, $denominator, false, true);
    }

    /**
     * Returns the difference of this number and the given one.
     *
     * @param BigNumber|int|string $that The number to subtract.
     *
     * @throws MathException If the number is not valid.
     *
     * @pure
     */
    public function minus(BigNumber|int|string $that): BigRational
    {
        $that = BigRational::of($that);

        if ($that->isZero()) {
            return $this;
        }

        if ($this->isZero()) {
            return $that->negated();
        }

        $numerator = $this->numerator->multipliedBy($that->denominator);
        $numerator = $numerator->minus($that->numerator->multipliedBy($this->denominator));
        $denominator = $this->denominator->multipliedBy($that->denominator);

        return new BigRational($numerator, $denominator, false, true);
    }

    /**
     * Returns the product of this number and the given one.
     *
     * @param BigNumber|int|string $that The multiplier.
     *
     * @throws MathException If the multiplier is not valid.
     *
     * @pure
     */
    public function multipliedBy(BigNumber|int|string $that): BigRational
    {
        $that = BigRational::of($that);

        if ($that->isZero() || $this->isZero()) {
            return BigRational::zero();
        }

        $numerator = $this->numerator->multipliedBy($that->numerator);
        $denominator = $this->denominator->multipliedBy($that->denominator);

        return new BigRational($numerator, $denominator, false, true);
    }

    /**
     * Returns the result of the division of this number by the given one.
     *
     * @param BigNumber|int|string $that The divisor.
     *
     * @throws MathException           If the divisor is not valid.
     * @throws DivisionByZeroException If the divisor is zero.
     *
     * @pure
     */
    public function dividedBy(BigNumber|int|string $that): BigRational
    {
        $that = BigRational::of($that);

        if ($that->isZero()) {
            throw DivisionByZeroException::divisionByZero();
        }

        $numerator = $this->numerator->multipliedBy($that->denominator);
        $denominator = $this->denominator->multipliedBy($that->numerator);

        return new BigRational($numerator, $denominator, true, true);
    }

    /**
     * Returns this number exponentiated to the given value.
     *
     * Unlike BigInteger and BigDecimal, BigRational supports negative exponents:
     * the result is the reciprocal raised to the absolute value of the exponent.
     *
     * @throws DivisionByZeroException If the exponent is negative and this number is zero.
     *
     * @pure
     */
    public function power(int $exponent): BigRational
    {
        if ($exponent === 0) {
            return BigRational::one();
        }

        if ($exponent === 1) {
            return $this;
        }

        if ($exponent < 0) {
            if ($this->isZero()) {
                throw DivisionByZeroException::zeroToNegativePower();
            }

            return $this->reciprocal()->power(Safe::neg($exponent));
        }

        return new BigRational(
            $this->numerator->power($exponent),
            $this->denominator->power($exponent),
            false,
            false,
        );
    }

    /**
     * Returns the reciprocal of this BigRational.
     *
     * The reciprocal has the numerator and denominator swapped.
     *
     * @throws DivisionByZeroException If this number is zero.
     *
     * @pure
     */
    public function reciprocal(): BigRational
    {
        if ($this->isZero()) {
            throw DivisionByZeroException::reciprocalOfZero();
        }

        return new BigRational($this->denominator, $this->numerator, true, false);
    }

    #[Override]
    public function negated(): static
    {
        return new BigRational($this->numerator->negated(), $this->denominator, false, false);
    }

    #[Override]
    public function compareTo(BigNumber|int|string $that): int
    {
        $that = BigRational::of($that);

        if ($this->denominator->isEqualTo($that->denominator)) {
            return $this->numerator->compareTo($that->numerator);
        }

        return $this->numerator
            ->multipliedBy($that->denominator)
            ->compareTo($that->numerator->multipliedBy($this->denominator));
    }

    #[Override]
    public function getSign(): int
    {
        return $this->numerator->getSign();
    }

    #[Override]
    public function toBigInteger(): BigInteger
    {
        if ($this->denominator->isEqualTo(1)) {
            return $this->numerator;
        }

        throw RoundingNecessaryException::rationalNotConvertibleToInteger();
    }

    #[Override]
    public function toBigDecimal(): BigDecimal
    {
        $scale = DecimalHelper::computeScaleFromReducedFractionDenominator($this->denominator->toString());

        if ($scale === null) {
            throw RoundingNecessaryException::rationalNotConvertibleToDecimal();
        }

        return $this->numerator->toBigDecimal()->dividedBy($this->denominator, $scale)->strippedOfTrailingZeros();
    }

    #[Override]
    public function toBigRational(): BigRational
    {
        return $this;
    }

    #[Override]
    public function toScale(int $scale, RoundingMode $roundingMode = RoundingMode::Unnecessary): BigDecimal
    {
        if ($scale < 0) { // @phpstan-ignore smaller.alwaysFalse
            throw InvalidArgumentException::negativeScale();
        }

        if ($roundingMode === RoundingMode::Unnecessary) {
            $requiredScale = DecimalHelper::computeScaleFromReducedFractionDenominator($this->denominator->toString());

            if ($requiredScale === null) {
                throw RoundingNecessaryException::rationalNotConvertibleToDecimal();
            }

            if ($requiredScale > $scale) {
                throw RoundingNecessaryException::rationalScaleTooSmall();
            }
        }

        return $this->numerator->toBigDecimal()->dividedBy($this->denominator, $scale, $roundingMode);
    }

    #[Override]
    public function toInt(): int
    {
        return $this->toBigInteger()->toInt();
    }

    #[Override]
    public function toFloat(): float
    {
        if ($this->denominator->isEqualTo(1)) {
            return $this->numerator->toFloat();
        }

        // Avoid $this->numerator->toFloat() / $this->denominator->toFloat(): converting both operands to float first
        // adds an extra rounding step before the division and can change the final float. Instead, divide in decimal
        // first and convert the resulting decimal approximation to float once.

        // We need ~17 significant digits for double precision (we use 20 for some margin). Since $scale controls
        // decimal places (not significant digits), we subtract the estimated order of magnitude so that large results
        // use fewer decimal places and small results use more (to look past leading zeros). Clamped to [0, 350] as
        // doubles range from e-324 to e308 (350 â‰ˆ 324 + 20 significant digits + margin).
        $magnitude = strlen($this->numerator->abs()->toString()) - strlen($this->denominator->toString());
        $scale = min(350, max(0, 20 - $magnitude));

        $result = $this->numerator
            ->toBigDecimal()
            ->dividedBy($this->denominator, $scale, RoundingMode::HalfEven)
            ->toFloat();

        // Preserve the sign when the decimal approximation underflows to zero.
        if ($result === 0.0 && $this->numerator->isNegative()) {
            return -0.0;
        }

        return $result;
    }

    #[Override]
    public function toString(): string
    {
        $numerator = $this->numerator->toString();
        $denominator = $this->denominator->toString();

        if ($denominator === '1') {
            return $numerator;
        }

        return $numerator . '/' . $denominator;
    }

    /**
     * Returns the decimal representation of this rational number, with repeating decimals in parentheses.
     *
     * WARNING: This method is unbounded.
     *          The length of the repeating decimal period can be as large as `denominator - 1`.
     *          For fractions with large denominators, this method can use excessive memory and CPU time.
     *          For example, `1/100019` has a repeating period of 100,018 digits.
     *
     * Examples:
     *
     * - `10/3` returns `3.(3)`
     * - `171/70` returns `2.4(428571)`
     * - `1/2` returns `0.5`
     *
     * @return non-empty-string
     *
     * @pure
     */
    public function toRepeatingDecimalString(): string
    {
        if ($this->isZero()) {
            return '0';
        }

        $sign = $this->numerator->isNegative() ? '-' : '';
        $numerator = $this->numerator->abs();
        $denominator = $this->denominator;

        $integral = $numerator->quotient($denominator);
        $remainder = $numerator->remainder($denominator);

        $integralString = $integral->toString();

        if ($remainder->isZero()) {
            return $sign . $integralString;
        }

        $digits = '';
        $remainderPositions = [];
        $index = 0;

        while (! $remainder->isZero()) {
            $remainderString = $remainder->toString();

            if (isset($remainderPositions[$remainderString])) {
                $repeatIndex = $remainderPositions[$remainderString];
                $nonRepeating = substr($digits, 0, $repeatIndex);
                $repeating = substr($digits, $repeatIndex);

                return $sign . $integralString . '.' . $nonRepeating . '(' . $repeating . ')';
            }

            $remainderPositions[$remainderString] = $index;
            $remainder = $remainder->multipliedBy(10);

            $digits .= $remainder->quotient($denominator)->toString();
            $remainder = $remainder->remainder($denominator);
            $index++;
        }

        return $sign . $integralString . '.' . $digits;
    }

    /**
     * This method is required for serializing the object and SHOULD NOT be accessed directly.
     *
     * @internal
     *
     * @return array{numerator: BigInteger, denominator: BigInteger}
     */
    public function __serialize(): array
    {
        return ['numerator' => $this->numerator, 'denominator' => $this->denominator];
    }

    /**
     * This method is only here to allow unserializing the object and cannot be accessed directly.
     *
     * @internal
     *
     * @param array{numerator: BigInteger, denominator: BigInteger} $data
     *
     * @throws LogicException
     */
    public function __unserialize(array $data): void
    {
        if (isset($this->numerator)) {
            throw new LogicException('__unserialize() is an internal function, it must not be called directly.');
        }

        $this->numerator = $data['numerator'];
        $this->denominator = $data['denominator'];
    }

    #[Override]
    protected static function from(BigNumber $number): static
    {
        return $number->toBigRational();
    }

    #[Override]
    protected function digitCount(): int
    {
        $count = $this->numerator->digitCount();

        // Mirrors toString(), which does not write a denominator of 1.
        if ($this->denominator->toString() === '1') {
            return $count;
        }

        return $count + $this->denominator->digitCount();
    }
}
<?php

declare(strict_types=1);

namespace Brick\Math;

use Brick\Math\Exception\IntegerOverflowException;
use Brick\Math\Exception\InvalidArgumentException;
use Brick\Math\Exception\MathException;
use Brick\Math\Exception\NumberFormatException;
use Brick\Math\Exception\PlatformException;
use Brick\Math\Exception\RoundingNecessaryException;
use Brick\Math\Internal\Safe;
use JsonSerializable;
use Override;
use Stringable;

use function array_is_list;
use function assert;
use function filter_var;
use function in_array;
use function is_int;
use function ltrim;
use function max;
use function preg_match;
use function str_contains;
use function str_repeat;
use function strlen;

use const FILTER_VALIDATE_INT;
use const PHP_INT_MAX;
use const PREG_UNMATCHED_AS_NULL;

/**
 * Base class for arbitrary-precision numbers.
 *
 * This class is sealed: it is part of the public API but should not be subclassed in userland.
 * Protected methods may change in any version.
 *
 * @phpstan-sealed BigInteger|BigDecimal|BigRational
 */
abstract readonly class BigNumber implements JsonSerializable, Stringable
{
    /**
     * The regular expression used to parse integer or decimal numbers.
     *
     * The end anchor must be \z, not $: the latter would also match before a trailing newline.
     * The digit quantifiers must be possessive (++): backtracking on malformed input could exhaust
     * pcre.backtrack_limit, surfacing as PlatformException instead of NumberFormatException.
     */
    private const PARSE_REGEXP_NUMERICAL =
        '/^' .
        '(?<sign>[\-\+])?' .
        '(?<integral>[0-9]++)?' .
        '(?<point>\.)?' .
        '(?<fractional>[0-9]++)?' .
        '(?:[eE](?<exponent>[\-\+]?[0-9]++))?' .
        '\z/';

    /**
     * The regular expression used to parse rational numbers.
     *
     * The end anchor must be \z, not $: the latter would also match before a trailing newline.
     * The digit quantifiers must be possessive (++): backtracking on malformed input could exhaust
     * pcre.backtrack_limit, surfacing as PlatformException instead of NumberFormatException.
     */
    private const PARSE_REGEXP_RATIONAL =
        '/^' .
        '(?<sign>[\-\+])?' .
        '(?<numerator>[0-9]++)' .
        '\/' .
        '(?<denominator>[0-9]++)' .
        '\z/';

    /**
     * Creates a BigNumber of the given value.
     *
     * When of() is called on BigNumber, the concrete return type is dependent on the given value, with the following
     * rules:
     *
     * - BigNumber instances are returned as is
     * - integer numbers are returned as BigInteger
     * - strings containing a `/` character are returned as BigRational
     * - strings containing a `.` character or using an exponential notation are returned as BigDecimal
     * - strings containing only digits with an optional leading `+` or `-` sign are returned as BigInteger
     *
     * When of() is called on BigInteger, BigDecimal, or BigRational, the resulting number is converted to an instance
     * of the subclass when possible; otherwise a RoundingNecessaryException is thrown.
     *
     * When parsing untrusted input, use {@see parse()} instead.
     *
     * @throws NumberFormatException      If the input is a string, and the format of the number is not valid.
     * @throws RoundingNecessaryException If the method is called on a subclass of BigNumber, and the value cannot be
     *                                    converted to an instance of the subclass without rounding.
     *
     * @pure
     */
    final public static function of(BigNumber|int|string $value): static
    {
        $value = self::_of($value);

        if ($value instanceof static) {
            // No conversion needed.
            return $value;
        }

        return static::from($value);
    }

    /**
     * Creates a BigNumber of the given value, or returns null if the input is null.
     *
     * Behaves like {@see of()} for non-null values.
     *
     * When parsing untrusted input, use {@see parseNullable()} instead.
     *
     * @throws NumberFormatException      If the input is a string, and the format of the number is not valid.
     * @throws RoundingNecessaryException If the method is called on a subclass of BigNumber, and the value cannot be
     *                                    converted to an instance of the subclass without rounding.
     *
     * @pure
     */
    final public static function ofNullable(BigNumber|int|string|null $value): ?static
    {
        if ($value === null) {
            return null;
        }

        return static::of($value);
    }

    /**
     * Creates a BigNumber of the given string, limiting the allowed syntax and the number of digits.
     *
     * This method is designed to safely parse untrusted input: huge strings, and exponential notation that allows a
     * short string such as `1e1000000000` to expand to gigabytes of memory.
     *
     * The $allowedSyntax parameter restricts the accepted notations: plain integers such as `123` are always accepted,
     * then each NumberSyntax case allows one additional feature: DecimalPoint, Exponent, Fraction. A value is accepted
     * only if every feature it uses is allowed. The NumberSyntax enum also provides constants for the most common
     * combinations, from NumberSyntax::INTEGER to NumberSyntax::ALL.
     *
     * The $maxDigits parameter limits the number of digits, counted in each of these forms:
     *
     * - as written, where every digit of the input counts, including leading zeros and exponent digits: `005` counts
     *   3 digits, `01e-3` counts 3, and `010/012` counts 6;
     * - in its expanded form, with exponents materialized and leading zeros trimmed: `01e-3` counts 4 (`0.001`);
     * - in its converted form, when called on a subclass, as written by toString(): `BigDecimal::parse('1/8', ...)`
     *   counts 4 digits (`0.125`), although `1/8` counts only 2.
     *
     * When parse() is called on BigNumber, the concrete return type is determined by the format of the string,
     * following the same rules as {@see of()}. When called on a subclass, the value is converted to an instance of
     * that subclass when possible.
     *
     * @param string             $value         The untrusted value to parse.
     * @param list<NumberSyntax> $allowedSyntax The allowed syntax features; plain integers are always accepted.
     * @param positive-int       $maxDigits     The maximum number of digits, as written and in the resulting number.
     *
     * @throws NumberFormatException      If the format of $value is invalid, if it uses a syntax that is not allowed
     *                                    by $allowedSyntax, or if it has more than $maxDigits digits.
     * @throws RoundingNecessaryException If the method is called on a subclass of BigNumber, and the value cannot be
     *                                    converted to an instance of the subclass without rounding.
     * @throws InvalidArgumentException   If $allowedSyntax is not a list of NumberSyntax cases, or if $maxDigits is
     *                                    less than 1.
     *
     * @pure
     */
    final public static function parse(
        string $value,
        array $allowedSyntax,
        int $maxDigits,
    ): static {
        if (! array_is_list($allowedSyntax)) { // @phpstan-ignore function.alreadyNarrowedType
            throw InvalidArgumentException::invalidAllowedSyntax();
        }

        foreach ($allowedSyntax as $syntax) {
            if (! $syntax instanceof NumberSyntax) { // @phpstan-ignore instanceof.alwaysTrue
                throw InvalidArgumentException::invalidAllowedSyntax();
            }
        }

        if ($maxDigits < 1) { // @phpstan-ignore smaller.alwaysFalse
            throw InvalidArgumentException::nonPositiveMaxDigits();
        }

        $value = self::_parse($value, $allowedSyntax, $maxDigits);

        if ($value instanceof static) {
            // No conversion needed.
            return $value;
        }

        $result = static::from($value);

        // The conversion may expand the number (1/8 -> 0.125): the limit applies to the result as well.
        if ($result->digitCount() > $maxDigits) {
            throw NumberFormatException::tooManyDigits($maxDigits);
        }

        return $result;
    }

    /**
     * Creates a BigNumber of the given string, limiting the allowed syntax and the number of digits, or returns null
     * if the input is null.
     *
     * Behaves like {@see parse()} for non-null values.
     *
     * @param string|null        $value         The untrusted value to parse, or null.
     * @param list<NumberSyntax> $allowedSyntax The allowed syntax features; plain integers are always accepted.
     * @param positive-int       $maxDigits     The maximum number of digits, as written and in the resulting number.
     *
     * @throws NumberFormatException      If the format of $value is invalid, if it uses a syntax that is not allowed
     *                                    by $allowedSyntax, or if it has more than $maxDigits digits.
     * @throws RoundingNecessaryException If the method is called on a subclass of BigNumber, and the value cannot be
     *                                    converted to an instance of the subclass without rounding.
     * @throws InvalidArgumentException   If $allowedSyntax is not a list of NumberSyntax cases, or if $maxDigits is
     *                                    less than 1.
     *
     * @pure
     */
    final public static function parseNullable(
        ?string $value,
        array $allowedSyntax,
        int $maxDigits,
    ): ?static {
        if ($value === null) {
            if (! array_is_list($allowedSyntax)) { // @phpstan-ignore function.alreadyNarrowedType
                throw InvalidArgumentException::invalidAllowedSyntax();
            }

            foreach ($allowedSyntax as $syntax) {
                if (! $syntax instanceof NumberSyntax) { // @phpstan-ignore instanceof.alwaysTrue
                    throw InvalidArgumentException::invalidAllowedSyntax();
                }
            }

            if ($maxDigits < 1) { // @phpstan-ignore smaller.alwaysFalse
                throw InvalidArgumentException::nonPositiveMaxDigits();
            }

            return null;
        }

        return static::parse($value, $allowedSyntax, $maxDigits);
    }

    /**
     * Returns the minimum of the given values.
     *
     * If several values are equal and minimal, the first one is returned.
     * This can affect the concrete return type when calling this method on BigNumber.
     *
     * @param BigNumber|int|string $a    The first number. Must be convertible to an instance of the class this method
     *                                   is called on.
     * @param BigNumber|int|string ...$n The additional numbers. Each number must be convertible to an instance of the
     *                                   class this method is called on.
     *
     * @throws MathException If a number is not valid, or is not convertible to an instance of the class this method is
     *                       called on.
     *
     * @pure
     */
    final public static function min(BigNumber|int|string $a, BigNumber|int|string ...$n): static
    {
        $min = static::of($a);

        foreach ($n as $value) {
            $value = static::of($value);

            if ($value->isLessThan($min)) {
                $min = $value;
            }
        }

        return $min;
    }

    /**
     * Returns the maximum of the given values.
     *
     * If several values are equal and maximal, the first one is returned.
     * This can affect the concrete return type when calling this method on BigNumber.
     *
     * @param BigNumber|int|string $a    The first number. Must be convertible to an instance of the class this method
     *                                   is called on.
     * @param BigNumber|int|string ...$n The additional numbers. Each number must be convertible to an instance of the
     *                                   class this method is called on.
     *
     * @throws MathException If a number is not valid, or is not convertible to an instance of the class this method is
     *                       called on.
     *
     * @pure
     */
    final public static function max(BigNumber|int|string $a, BigNumber|int|string ...$n): static
    {
        $max = static::of($a);

        foreach ($n as $value) {
            $value = static::of($value);

            if ($value->isGreaterThan($max)) {
                $max = $value;
            }
        }

        return $max;
    }

    /**
     * Returns the sum of the given values.
     *
     * When called on BigNumber, sum() accepts any supported type and returns a result whose type is the widest among
     * the given values (BigInteger < BigDecimal < BigRational).
     *
     * When called on BigInteger, BigDecimal, or BigRational, sum() requires that all values can be converted to that
     * specific subclass, and returns a result of the same type.
     *
     * @param BigNumber|int|string $a    The first number. Must be convertible to an instance of the class this method
     *                                   is called on.
     * @param BigNumber|int|string ...$n The additional numbers. Each number must be convertible to an instance of the
     *                                   class this method is called on.
     *
     * @throws MathException If a number is not valid, or is not convertible to an instance of the class this method is
     *                       called on.
     *
     * @pure
     */
    final public static function sum(BigNumber|int|string $a, BigNumber|int|string ...$n): static
    {
        $sum = static::of($a);

        foreach ($n as $value) {
            $sum = self::add($sum, static::of($value));
        }

        assert($sum instanceof static);

        return $sum;
    }

    /**
     * Checks if this number is equal to the given one.
     *
     * @throws MathException If the given number is not valid.
     *
     * @pure
     */
    final public function isEqualTo(BigNumber|int|string $that): bool
    {
        return $this->compareTo($that) === 0;
    }

    /**
     * Checks if this number is strictly less than the given one.
     *
     * @throws MathException If the given number is not valid.
     *
     * @pure
     */
    final public function isLessThan(BigNumber|int|string $that): bool
    {
        return $this->compareTo($that) < 0;
    }

    /**
     * Checks if this number is less than or equal to the given one.
     *
     * @throws MathException If the given number is not valid.
     *
     * @pure
     */
    final public function isLessThanOrEqualTo(BigNumber|int|string $that): bool
    {
        return $this->compareTo($that) <= 0;
    }

    /**
     * Checks if this number is strictly greater than the given one.
     *
     * @throws MathException If the given number is not valid.
     *
     * @pure
     */
    final public function isGreaterThan(BigNumber|int|string $that): bool
    {
        return $this->compareTo($that) > 0;
    }

    /**
     * Checks if this number is greater than or equal to the given one.
     *
     * @throws MathException If the given number is not valid.
     *
     * @pure
     */
    final public function isGreaterThanOrEqualTo(BigNumber|int|string $that): bool
    {
        return $this->compareTo($that) >= 0;
    }

    /**
     * Checks if this number equals zero.
     *
     * @pure
     */
    final public function isZero(): bool
    {
        return $this->getSign() === 0;
    }

    /**
     * Checks if this number is strictly negative.
     *
     * @pure
     */
    final public function isNegative(): bool
    {
        return $this->getSign() < 0;
    }

    /**
     * Checks if this number is negative or zero.
     *
     * @pure
     */
    final public function isNegativeOrZero(): bool
    {
        return $this->getSign() <= 0;
    }

    /**
     * Checks if this number is strictly positive.
     *
     * @pure
     */
    final public function isPositive(): bool
    {
        return $this->getSign() > 0;
    }

    /**
     * Checks if this number is positive or zero.
     *
     * @pure
     */
    final public function isPositiveOrZero(): bool
    {
        return $this->getSign() >= 0;
    }

    /**
     * Returns the absolute value of this number.
     *
     * @pure
     */
    final public function abs(): static
    {
        return $this->isNegative() ? $this->negated() : $this;
    }

    /**
     * Returns the negated value of this number.
     *
     * @pure
     */
    abstract public function negated(): static;

    /**
     * Returns the sign of this number.
     *
     * Returns -1 if the number is negative, 0 if zero, 1 if positive.
     *
     * @return -1|0|1
     *
     * @pure
     */
    abstract public function getSign(): int;

    /**
     * Compares this number to the given one.
     *
     * Returns -1 if `$this` is lower than, 0 if equal to, 1 if greater than `$that`.
     *
     * @return -1|0|1
     *
     * @throws MathException If the number is not valid.
     *
     * @pure
     */
    abstract public function compareTo(BigNumber|int|string $that): int;

    /**
     * Limits (clamps) this number between the given minimum and maximum values.
     *
     * If the number is lower than $min, returns $min.
     * If the number is greater than $max, returns $max.
     * Otherwise, returns this number unchanged.
     *
     * @param BigNumber|int|string $min The minimum. Must be convertible to an instance of the class this method is called on.
     * @param BigNumber|int|string $max The maximum. Must be convertible to an instance of the class this method is called on.
     *
     * @throws MathException            If min/max are not convertible to an instance of the class this method is called on.
     * @throws InvalidArgumentException If min is greater than max.
     *
     * @pure
     */
    final public function clamp(BigNumber|int|string $min, BigNumber|int|string $max): static
    {
        $min = static::of($min);
        $max = static::of($max);

        if ($min->isGreaterThan($max)) {
            throw InvalidArgumentException::minGreaterThanMax();
        }

        if ($this->isLessThan($min)) {
            return $min;
        }

        if ($this->isGreaterThan($max)) {
            return $max;
        }

        return $this;
    }

    /**
     * Converts this number to a BigInteger.
     *
     * @throws RoundingNecessaryException If this number cannot be converted to a BigInteger without rounding.
     *
     * @pure
     */
    abstract public function toBigInteger(): BigInteger;

    /**
     * Converts this number to a BigDecimal.
     *
     * @throws RoundingNecessaryException If this number cannot be converted to a BigDecimal without rounding.
     *
     * @pure
     */
    abstract public function toBigDecimal(): BigDecimal;

    /**
     * Converts this number to a BigRational.
     *
     * @pure
     */
    abstract public function toBigRational(): BigRational;

    /**
     * Converts this number to a BigDecimal with the given scale, using rounding if necessary.
     *
     * @param non-negative-int $scale        The scale of the resulting `BigDecimal`. Must be non-negative.
     * @param RoundingMode     $roundingMode An optional rounding mode, defaults to Unnecessary.
     *
     * @throws InvalidArgumentException   If the scale is negative.
     * @throws RoundingNecessaryException If RoundingMode::Unnecessary is used, and this number cannot be converted to
     *                                    the given scale without rounding.
     *
     * @pure
     */
    abstract public function toScale(int $scale, RoundingMode $roundingMode = RoundingMode::Unnecessary): BigDecimal;

    /**
     * Returns the exact value of this number as a native integer.
     *
     * If this number cannot be converted to a native integer without losing precision, an exception is thrown.
     * Note that the acceptable range for an integer depends on the platform and differs for 32-bit and 64-bit.
     *
     * @throws RoundingNecessaryException If this number cannot be converted to an integer without rounding.
     * @throws IntegerOverflowException   If this number is too large to fit in a native integer.
     *
     * @pure
     */
    abstract public function toInt(): int;

    /**
     * Returns an approximation of this number as a floating-point value.
     *
     * Note that this method can discard information as the precision of a floating-point value
     * is inherently limited.
     *
     * If the number is greater than the largest representable floating point number, positive infinity is returned.
     * If the number is less than the smallest representable floating point number, negative infinity is returned.
     * This method never returns NaN.
     *
     * @pure
     */
    abstract public function toFloat(): float;

    /**
     * Returns a string representation of this number.
     *
     * The output of this method can be parsed by the `of()` factory method; this will yield an object equal to this
     * one, but possibly of a different type if instantiated through `BigNumber::of()`.
     *
     * @return non-empty-string
     *
     * @pure
     */
    abstract public function toString(): string;

    /**
     * @return non-empty-string
     */
    #[Override]
    final public function jsonSerialize(): string
    {
        return $this->toString();
    }

    /**
     * @return non-empty-string
     *
     * @pure
     */
    #[Override]
    final public function __toString(): string
    {
        return $this->toString();
    }

    /**
     * Overridden by subclasses to convert a BigNumber to an instance of the subclass.
     *
     * @throws RoundingNecessaryException If the value cannot be converted.
     *
     * @pure
     */
    abstract protected static function from(BigNumber $number): static;

    /**
     * Returns the number of digits in this number, as written by toString().
     *
     * @pure
     */
    abstract protected function digitCount(): int;

    /**
     * Proxy method to access BigInteger's protected constructor from sibling classes.
     *
     * @internal
     *
     * @pure
     */
    final protected function newBigInteger(string $value): BigInteger
    {
        return new BigInteger($value);
    }

    /**
     * Proxy method to access BigDecimal's protected constructor from sibling classes.
     *
     * @internal
     *
     * @param non-negative-int $scale
     *
     * @pure
     */
    final protected function newBigDecimal(string $value, int $scale = 0): BigDecimal
    {
        return new BigDecimal($value, $scale);
    }

    /**
     * Proxy method to access BigRational's protected constructor from sibling classes.
     *
     * @internal
     *
     * @pure
     */
    final protected function newBigRational(BigInteger $numerator, BigInteger $denominator, bool $checkDenominator, bool $simplify): BigRational
    {
        return new BigRational($numerator, $denominator, $checkDenominator, $simplify);
    }

    /**
     * @throws NumberFormatException If the format of the number is not valid.
     *
     * @pure
     */
    private static function _of(BigNumber|int|string $value): BigNumber
    {
        if ($value instanceof BigNumber) {
            return $value;
        }

        if (is_int($value)) {
            return new BigInteger((string) $value);
        }

        return self::_parse($value, null, null);
    }

    /**
     * Note: $allowedSyntax and $maxDigits are nullable as a performance optimization for of(): null behaves like
     * `NumberSyntax::ALL` and `PHP_INT_MAX`, but skips the checks, which could never fail against those values.
     *
     * @param list<NumberSyntax>|null $allowedSyntax The allowed syntax features; plain integers are always accepted. Null for all.
     * @param positive-int|null       $maxDigits     The maximum number of digits, as written and in the resulting number. Null for no limit.
     *
     * @throws NumberFormatException If the format of $value is invalid, if it uses a syntax that is not allowed
     *                               by $allowedSyntax, or if it has more than $maxDigits digits.
     *
     * @pure
     */
    private static function _parse(string $value, ?array $allowedSyntax, ?int $maxDigits): BigNumber
    {
        if ($value === '') {
            throw NumberFormatException::emptyNumber();
        }

        if (str_contains($value, '/')) {
            // Rational number
            $result = preg_match(self::PARSE_REGEXP_RATIONAL, $value, $matches, PREG_UNMATCHED_AS_NULL);

            if ($result === false) {
                throw PlatformException::pcreFailure();
            }

            if ($result === 0) {
                throw NumberFormatException::invalidFormat($value);
            }

            if ($allowedSyntax !== null && ! in_array(NumberSyntax::Fraction, $allowedSyntax, true)) {
                throw NumberFormatException::syntaxNotAllowed(NumberSyntax::Fraction);
            }

            $sign = $matches['sign'];
            $numerator = self::cleanUp($sign, $matches['numerator']);
            $denominator = self::cleanUp(null, $matches['denominator']);

            if ($denominator === '0') {
                throw NumberFormatException::zeroDenominator();
            }

            // The digit count is taken as written, before trimming zeros and before simplification:
            // the final count will always be less or equal.
            if ($maxDigits !== null && strlen($matches['numerator']) + strlen($matches['denominator']) > $maxDigits) {
                throw NumberFormatException::tooManyDigits($maxDigits);
            }

            return new BigRational(
                new BigInteger($numerator),
                new BigInteger($denominator),
                false,
                true,
            );
        }

        // Integer or decimal number
        $result = preg_match(self::PARSE_REGEXP_NUMERICAL, $value, $matches, PREG_UNMATCHED_AS_NULL);

        if ($result === false) {
            throw PlatformException::pcreFailure();
        }

        if ($result === 0) {
            throw NumberFormatException::invalidFormat($value);
        }

        $sign = $matches['sign'];
        $point = $matches['point'];
        $integral = $matches['integral'];
        $fractional = $matches['fractional'];
        $exponent = $matches['exponent'];

        if ($integral === null && $fractional === null) {
            throw NumberFormatException::invalidFormat($value);
        }

        if ($integral === null) {
            $integral = '0';
        }

        if ($point === null && $exponent === null) {
            // Integer number.
            if ($maxDigits !== null && strlen($integral) > $maxDigits) {
                throw NumberFormatException::tooManyDigits($maxDigits);
            }

            $integral = self::cleanUp($sign, $integral);

            return new BigInteger($integral);
        }

        // Decimal number.
        if ($allowedSyntax !== null) {
            if ($point !== null && ! in_array(NumberSyntax::DecimalPoint, $allowedSyntax, true)) {
                throw NumberFormatException::syntaxNotAllowed(NumberSyntax::DecimalPoint);
            }

            if ($exponent !== null && ! in_array(NumberSyntax::Exponent, $allowedSyntax, true)) {
                throw NumberFormatException::syntaxNotAllowed(NumberSyntax::Exponent);
            }
        }

        if ($exponent === null) {
            $exponent = 0;
        } else {
            $exponentSign = $exponent[0] === '-' ? '-' : '';
            $exponent = ltrim(ltrim($exponent, '+-'), '0');

            if ($exponent === '') {
                $exponent = 0;
            } else {
                $exponent = filter_var($exponentSign . $exponent, FILTER_VALIDATE_INT);

                if ($exponent === false) {
                    throw NumberFormatException::exponentTooLarge();
                }
            }
        }

        $fractional ??= '';

        $unscaledValue = self::cleanUp($sign, $integral . $fractional);
        $scale = strlen($fractional) - $exponent;

        // @phpstan-ignore function.alreadyNarrowedType (may overflow to float)
        if (! is_int($scale)) {
            throw NumberFormatException::exponentTooLarge();
        }

        $unscaledDigits = strlen($unscaledValue) - (int) ($unscaledValue[0] === '-');

        if ($scale < 0 && $unscaledValue !== '0') {
            // The unscaled value is padded with -$scale zeros below.
            $expandedDigits = $unscaledDigits - $scale;
        } else {
            // The fractional digits, plus at least a zero integer part.
            $expandedDigits = max($unscaledDigits, $scale + 1);
        }

        // @phpstan-ignore function.alreadyNarrowedType (may overflow to float)
        if (! is_int($expandedDigits)) {
            throw NumberFormatException::tooManyDigits($maxDigits ?? PHP_INT_MAX);
        }

        if ($maxDigits !== null) {
            // Digits as written: every digit of the input counts, including leading zeros and exponent digits.
            $writtenDigits = strlen($matches['integral'] ?? '') + strlen($matches['fractional'] ?? '');

            if ($matches['exponent'] !== null) {
                $writtenDigits += strlen($matches['exponent']) - (int) ($matches['exponent'][0] === '-' || $matches['exponent'][0] === '+');
            }

            if ($expandedDigits > $maxDigits || $writtenDigits > $maxDigits) {
                throw NumberFormatException::tooManyDigits($maxDigits);
            }
        }

        if ($scale < 0) {
            if ($unscaledValue !== '0') {
                $unscaledValue .= str_repeat('0', Safe::neg($scale));
            }
            $scale = 0;
        }

        return new BigDecimal($unscaledValue, $scale);
    }

    /**
     * Removes optional leading zeros and applies sign.
     *
     * @param '+'|'-'|null     $sign   The sign, optional. Null is allowed for convenience and treated as '+'.
     * @param non-empty-string $number The number, validated as a string of digits.
     *
     * @pure
     */
    private static function cleanUp(?string $sign, string $number): string
    {
        $number = ltrim($number, '0');

        if ($number === '') {
            return '0';
        }

        return $sign === '-' ? '-' . $number : $number;
    }

    /**
     * Adds two BigNumber instances in the correct order to avoid a RoundingNecessaryException.
     *
     * @pure
     */
    private static function add(BigNumber $a, BigNumber $b): BigNumber
    {
        if ($a instanceof BigRational) {
            return $a->plus($b);
        }

        if ($b instanceof BigRational) {
            return $b->plus($a);
        }

        if ($a instanceof BigDecimal) {
            return $a->plus($b);
        }

        if ($b instanceof BigDecimal) {
            return $b->plus($a);
        }

        return $a->plus($b);
    }
}
<?php

declare(strict_types=1);

namespace Brick\Math;

/**
 * A syntax feature that {@see BigNumber::parse()} can accept.
 *
 * Plain signed integers such as `123` and `-7` are the base language: they are always accepted.
 * Each case allows one additional feature:
 *
 * - DecimalPoint: `.`
 * - Exponent: `e` or `E`
 * - Fraction: `/`
 *
 * In addition to its cases, this enum provides list constants for the most common combinations, from INTEGER to ALL.
 */
enum NumberSyntax
{
    /**
     * Allows the decimal point: `1.5`, `.5`, `1.`.
     */
    case DecimalPoint;

    /**
     * Allows the exponent: `5e3`, `15E-2`.
     */
    case Exponent;

    /**
     * Allows the fraction form: `2/4`. The numerator and denominator are unsigned integers; an optional sign
     * precedes the whole fraction.
     */
    case Fraction;

    /**
     * Integers only: `123`.
     * The base language, with no additional notation.
     */
    public const INTEGER = [];

    /**
     * Integers and decimal numbers: `123`, `123.45`.
     * Typical for monetary input.
     */
    public const DECIMAL = [
        self::DecimalPoint,
    ];

    /**
     * Integers and decimal numbers, with exponents: `123`, `123.45`, `1.5e-3`.
     * Accepts every JSON number.
     */
    public const SCIENTIFIC = [
        self::DecimalPoint,
        self::Exponent,
    ];

    /**
     * Integers and fractions: `123`, `22/7`.
     */
    public const RATIONAL = [
        self::Fraction,
    ];

    /**
     * The full syntax accepted by {@see BigNumber::of()}: `123`, `123.45`, `1.5e-3`, `22/7`.
     */
    public const ALL = [
        self::DecimalPoint,
        self::Exponent,
        self::Fraction,
    ];
}
<?php

declare(strict_types=1);

namespace Brick\Math;

/**
 * Specifies rounding behavior by defining how discarded digits affect the returned result when an exact value cannot
 * be represented at the requested scale.
 */
enum RoundingMode
{
    /**
     * Asserts that the requested operation has an exact result, hence no rounding is necessary.
     *
     * If this rounding mode is specified on an operation that yields a result that
     * cannot be represented at the requested scale, a RoundingNecessaryException is thrown.
     *
     * Equivalent native PHP rounding mode: none.
     */
    case Unnecessary;

    /**
     * Rounds away from zero.
     *
     * Always increments the digit prior to a nonzero discarded fraction.
     * Note that this rounding mode never decreases the magnitude of the calculated value.
     *
     * Equivalent native PHP rounding mode: {@see \RoundingMode::AwayFromZero}.
     */
    case Up;

    /**
     * Rounds towards zero.
     *
     * Never increments the digit prior to a discarded fraction (i.e., truncates).
     * Note that this rounding mode never increases the magnitude of the calculated value.
     *
     * Equivalent native PHP rounding mode: {@see \RoundingMode::TowardsZero}.
     */
    case Down;

    /**
     * Rounds towards positive infinity.
     *
     * If the result is positive, behaves as for Up; if negative, behaves as for Down.
     * Note that this rounding mode never decreases the calculated value.
     *
     * Equivalent native PHP rounding mode: {@see \RoundingMode::PositiveInfinity}.
     */
    case Ceiling;

    /**
     * Rounds towards negative infinity.
     *
     * If the result is positive, behaves as for Down; if negative, behaves as for Up.
     * Note that this rounding mode never increases the calculated value.
     *
     * Equivalent native PHP rounding mode: {@see \RoundingMode::NegativeInfinity}.
     */
    case Floor;

    /**
     * Rounds towards "nearest neighbor" unless both neighbors are equidistant, in which case round up.
     *
     * Behaves as for Up if the discarded fraction is >= 0.5; otherwise, behaves as for Down.
     * Note that this is the rounding mode commonly taught at school.
     *
     * Equivalent native PHP rounding mode: {@see \RoundingMode::HalfAwayFromZero}.
     */
    case HalfUp;

    /**
     * Rounds towards "nearest neighbor" unless both neighbors are equidistant, in which case round down.
     *
     * Behaves as for Up if the discarded fraction is > 0.5; otherwise, behaves as for Down.
     *
     * Equivalent native PHP rounding mode: {@see \RoundingMode::HalfTowardsZero}.
     */
    case HalfDown;

    /**
     * Rounds towards "nearest neighbor" unless both neighbors are equidistant, in which case round towards positive infinity.
     *
     * If the result is positive, behaves as for HalfUp; if negative, behaves as for HalfDown.
     *
     * Equivalent native PHP rounding mode: none.
     */
    case HalfCeiling;

    /**
     * Rounds towards "nearest neighbor" unless both neighbors are equidistant, in which case round towards negative infinity.
     *
     * If the result is positive, behaves as for HalfDown; if negative, behaves as for HalfUp.
     *
     * Equivalent native PHP rounding mode: none.
     */
    case HalfFloor;

    /**
     * Rounds towards the "nearest neighbor" unless both neighbors are equidistant, in which case rounds towards the even neighbor.
     *
     * Behaves as for HalfUp if the digit to the left of the discarded fraction is odd;
     * behaves as for HalfDown if it's even.
     *
     * Note that this is the rounding mode that statistically minimizes
     * cumulative error when applied repeatedly over a sequence of calculations.
     * It is sometimes known as "Banker's rounding", and is the default rounding mode in IEEE 754.
     *
     * Equivalent native PHP rounding mode: {@see \RoundingMode::HalfEven}.
     */
    case HalfEven;

    /**
     * Rounds towards the "nearest neighbor" unless both neighbors are equidistant, in which case rounds towards the odd neighbor.
     *
     * Behaves as for HalfUp if the digit to the left of the discarded fraction is even;
     * behaves as for HalfDown if it's odd.
     *
     * Like HalfEven, this rounding mode is free of bias towards or away from zero on ties;
     * unlike HalfEven, ties never round to an even digit â€” in particular, never to zero or to a multiple of ten.
     *
     * Equivalent native PHP rounding mode: {@see \RoundingMode::HalfOdd}.
     */
    case HalfOdd;

    /**
     * Returns the equivalent of the given native PHP rounding mode.
     *
     * Note that PHP's RoundingMode enum is only available on PHP 8.4 and later.
     *
     * @pure
     */
    public static function fromNativeRoundingMode(\RoundingMode $roundingMode): self
    {
        return match ($roundingMode) {
            \RoundingMode::AwayFromZero => self::Up,
            \RoundingMode::TowardsZero => self::Down,
            \RoundingMode::PositiveInfinity => self::Ceiling,
            \RoundingMode::NegativeInfinity => self::Floor,
            \RoundingMode::HalfAwayFromZero => self::HalfUp,
            \RoundingMode::HalfTowardsZero => self::HalfDown,
            \RoundingMode::HalfEven => self::HalfEven,
            \RoundingMode::HalfOdd => self::HalfOdd,
        };
    }
}
<?php

declare(strict_types=1);

namespace Brick\Math\Internal\Calculator;

use Brick\Math\Exception\PlatformException;
use Brick\Math\Internal\Calculator;
use Override;

use function assert;
use function in_array;
use function intdiv;
use function is_int;
use function ltrim;
use function str_pad;
use function str_repeat;
use function strcmp;
use function strlen;
use function substr;

use const PHP_INT_SIZE;
use const STR_PAD_LEFT;

/**
 * Calculator implementation using only native PHP code.
 *
 * @internal
 */
final readonly class NativeCalculator extends Calculator
{
    /**
     * The max number of digits the platform can natively add, subtract, multiply or divide without overflow.
     * For multiplication, this represents the max sum of the lengths of both operands.
     *
     * In addition, it is assumed that an extra digit can hold a carry (1) without overflowing.
     * Example: 32-bit: max number 1,999,999,999 (9 digits + carry)
     *          64-bit: max number 1,999,999,999,999,999,999 (18 digits + carry)
     */
    private int $maxDigits;

    /**
     * @pure
     *
     * @codeCoverageIgnore
     */
    public function __construct()
    {
        $this->maxDigits = match (PHP_INT_SIZE) {
            4 => 9,
            8 => 18, // @phpstan-ignore match.alwaysTrue
            default => throw PlatformException::unsupportedIntSize(),
        };
    }

    #[Override]
    public function add(string $a, string $b): string
    {
        /**
         * @var numeric-string $a
         * @var numeric-string $b
         */
        $result = $a + $b;

        if (is_int($result)) {
            return (string) $result;
        }

        if ($a === '0') {
            return $b;
        }

        if ($b === '0') {
            return $a;
        }

        [$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b);

        $result = $aNeg === $bNeg ? $this->doAdd($aDig, $bDig) : $this->doSub($aDig, $bDig);

        if ($aNeg) {
            $result = $this->neg($result);
        }

        return $result;
    }

    #[Override]
    public function sub(string $a, string $b): string
    {
        return $this->add($a, $this->neg($b));
    }

    #[Override]
    public function mul(string $a, string $b): string
    {
        /**
         * @var numeric-string $a
         * @var numeric-string $b
         */
        $result = $a * $b;

        if (is_int($result)) {
            return (string) $result;
        }

        if ($a === '0' || $b === '0') {
            return '0';
        }

        if ($a === '1') {
            return $b;
        }

        if ($b === '1') {
            return $a;
        }

        if ($a === '-1') {
            return $this->neg($b);
        }

        if ($b === '-1') {
            return $this->neg($a);
        }

        [$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b);

        $result = $this->doMul($aDig, $bDig);

        if ($aNeg !== $bNeg) {
            $result = $this->neg($result);
        }

        return $result;
    }

    #[Override]
    public function divQ(string $a, string $b): string
    {
        return $this->divQR($a, $b)[0];
    }

    #[Override]
    public function divR(string $a, string $b): string
    {
        return $this->divQR($a, $b)[1];
    }

    #[Override]
    public function divQR(string $a, string $b): array
    {
        if ($a === '0') {
            return ['0', '0'];
        }

        if ($a === $b) {
            return ['1', '0'];
        }

        if ($b === '1') {
            return [$a, '0'];
        }

        if ($b === '-1') {
            return [$this->neg($a), '0'];
        }

        /** @var numeric-string $a */
        $na = $a * 1; // cast to number

        if (is_int($na)) {
            /** @var numeric-string $b */
            $nb = $b * 1;

            if (is_int($nb)) {
                // the only division that may overflow is PHP_INT_MIN / -1,
                // which cannot happen here as we've already handled a divisor of -1 above.
                $q = intdiv($na, $nb);
                $r = $na % $nb;

                return [
                    (string) $q,
                    (string) $r,
                ];
            }
        }

        [$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b);

        [$q, $r] = $this->doDiv($aDig, $bDig);

        if ($aNeg !== $bNeg) {
            $q = $this->neg($q);
        }

        if ($aNeg) {
            $r = $this->neg($r);
        }

        return [$q, $r];
    }

    #[Override]
    public function pow(string $a, int $e): string
    {
        if ($e === 0) {
            return '1';
        }

        if ($e === 1) {
            return $a;
        }

        $odd = $e % 2;
        $e -= $odd;

        $aa = $this->mul($a, $a);

        $result = $this->pow($aa, $e / 2);

        if ($odd === 1) {
            $result = $this->mul($result, $a);
        }

        return $result;
    }

    /**
     * Algorithm from: https://www.geeksforgeeks.org/modular-exponentiation-power-in-modular-arithmetic/.
     */
    #[Override]
    public function modPow(string $base, string $exp, string $mod): string
    {
        // normalize to Euclidean representative so modPow() stays consistent with mod()
        $base = $this->mod($base, $mod);

        // special case: the algorithm below fails with power 0 mod 1 (returns 1 instead of 0)
        if ($exp === '0' && $mod === '1') {
            return '0';
        }

        $x = $base;

        $res = '1';

        // numbers are positive, so we can use remainder instead of modulo
        $x = $this->divR($x, $mod);

        while ($exp !== '0') {
            if (in_array($exp[-1], ['1', '3', '5', '7', '9'])) { // odd
                $res = $this->divR($this->mul($res, $x), $mod);
            }

            $exp = $this->divQ($exp, '2');
            $x = $this->divR($this->mul($x, $x), $mod);
        }

        return $res;
    }

    /**
     * Adapted from https://cp-algorithms.com/num_methods/roots_newton.html.
     */
    #[Override]
    public function sqrt(string $n): string
    {
        if ($n === '0') {
            return '0';
        }

        // initial approximation
        $x = str_repeat('9', intdiv(strlen($n), 2) ?: 1);

        $decreased = false;

        for (; ;) {
            $nx = $this->divQ($this->add($x, $this->divQ($n, $x)), '2');

            if ($x === $nx || $this->cmp($nx, $x) > 0 && $decreased) {
                break;
            }

            $decreased = $this->cmp($nx, $x) < 0;
            $x = $nx;
        }

        return $x;
    }

    /**
     * Performs the addition of two non-signed large integers.
     *
     * @pure
     */
    private function doAdd(string $a, string $b): string
    {
        [$a, $b, $length] = $this->pad($a, $b);

        $carry = 0;
        $result = '';

        for ($i = $length - $this->maxDigits; ; $i -= $this->maxDigits) {
            $blockLength = $this->maxDigits;

            if ($i < 0) {
                $blockLength += $i;
                $i = 0;
            }

            /** @var numeric-string $blockA */
            $blockA = substr($a, $i, $blockLength);

            /** @var numeric-string $blockB */
            $blockB = substr($b, $i, $blockLength);

            $sum = (string) ($blockA + $blockB + $carry);
            $sumLength = strlen($sum);

            if ($sumLength > $blockLength) {
                $sum = substr($sum, 1);
                $carry = 1;
            } else {
                if ($sumLength < $blockLength) {
                    $sum = str_repeat('0', $blockLength - $sumLength) . $sum;
                }
                $carry = 0;
            }

            $result = $sum . $result;

            if ($i === 0) {
                break;
            }
        }

        if ($carry === 1) {
            $result = '1' . $result;
        }

        return $result;
    }

    /**
     * Performs the subtraction of two non-signed large integers.
     *
     * @pure
     */
    private function doSub(string $a, string $b): string
    {
        if ($a === $b) {
            return '0';
        }

        // Ensure that we always subtract to a positive result: biggest minus smallest.
        $cmp = $this->doCmp($a, $b);

        $invert = ($cmp === -1);

        if ($invert) {
            $c = $a;
            $a = $b;
            $b = $c;
        }

        [$a, $b, $length] = $this->pad($a, $b);

        $carry = 0;
        $result = '';

        $complement = 10 ** $this->maxDigits;

        for ($i = $length - $this->maxDigits; ; $i -= $this->maxDigits) {
            $blockLength = $this->maxDigits;

            if ($i < 0) {
                $blockLength += $i;
                $i = 0;
            }

            /** @var numeric-string $blockA */
            $blockA = substr($a, $i, $blockLength);

            /** @var numeric-string $blockB */
            $blockB = substr($b, $i, $blockLength);

            $sum = $blockA - $blockB - $carry;

            if ($sum < 0) {
                $sum += $complement;
                $carry = 1;
            } else {
                $carry = 0;
            }

            $sum = (string) $sum;
            $sumLength = strlen($sum);

            if ($sumLength < $blockLength) {
                $sum = str_repeat('0', $blockLength - $sumLength) . $sum;
            }

            $result = $sum . $result;

            if ($i === 0) {
                break;
            }
        }

        // Carry cannot be 1 when the loop ends, as a > b
        assert($carry === 0);

        $result = ltrim($result, '0');

        if ($invert) {
            $result = $this->neg($result);
        }

        return $result;
    }

    /**
     * Performs the multiplication of two non-signed large integers.
     *
     * @pure
     */
    private function doMul(string $a, string $b): string
    {
        $x = strlen($a);
        $y = strlen($b);

        $maxDigits = intdiv($this->maxDigits, 2);
        $complement = 10 ** $maxDigits;

        $result = '0';

        for ($i = $x - $maxDigits; ; $i -= $maxDigits) {
            $blockALength = $maxDigits;

            if ($i < 0) {
                $blockALength += $i;
                $i = 0;
            }

            $blockA = (int) substr($a, $i, $blockALength);

            $line = '';
            $carry = 0;

            for ($j = $y - $maxDigits; ; $j -= $maxDigits) {
                $blockBLength = $maxDigits;

                if ($j < 0) {
                    $blockBLength += $j;
                    $j = 0;
                }

                $blockB = (int) substr($b, $j, $blockBLength);

                $mul = $blockA * $blockB + $carry;
                $value = $mul % $complement;
                $carry = ($mul - $value) / $complement;

                $value = (string) $value;
                $value = str_pad($value, $maxDigits, '0', STR_PAD_LEFT);

                $line = $value . $line;

                if ($j === 0) {
                    break;
                }
            }

            if ($carry !== 0) {
                $line = $carry . $line;
            }

            $line = ltrim($line, '0');

            if ($line !== '') {
                $line .= str_repeat('0', $x - $blockALength - $i);
                $result = $this->add($result, $line);
            }

            if ($i === 0) {
                break;
            }
        }

        return $result;
    }

    /**
     * Performs the division of two non-signed large integers.
     *
     * @return string[] The quotient and remainder.
     *
     * @pure
     */
    private function doDiv(string $a, string $b): array
    {
        $cmp = $this->doCmp($a, $b);

        if ($cmp === -1) {
            return ['0', $a];
        }

        $x = strlen($a);
        $y = strlen($b);

        // we now know that a >= b && x >= y

        $q = '0'; // quotient
        $r = $a; // remainder
        $z = $y; // focus length, always $y or $y+1

        /** @var numeric-string $b */
        $nb = $b * 1; // cast to number
        // performance optimization in cases where the remainder will never cause int overflow
        if (is_int(($nb - 1) * 10 + 9)) {
            $r = (int) substr($a, 0, $z - 1);

            for ($i = $z - 1; $i < $x; $i++) {
                $n = $r * 10 + (int) $a[$i];
                /** @var int $nb */
                $q .= intdiv($n, $nb);
                $r = $n % $nb;
            }

            return [ltrim($q, '0') ?: '0', (string) $r];
        }

        for (; ;) {
            $focus = substr($a, 0, $z);

            $cmp = $this->doCmp($focus, $b);

            if ($cmp === -1) {
                if ($z === $x) { // remainder < dividend
                    break;
                }

                $z++;
            }

            $zeros = str_repeat('0', $x - $z);

            $q = $this->add($q, '1' . $zeros);
            $a = $this->sub($a, $b . $zeros);

            $r = $a;

            if ($r === '0') { // remainder == 0
                break;
            }

            $x = strlen($a);

            if ($x < $y) { // remainder < dividend
                break;
            }

            $z = $y;
        }

        return [$q, $r];
    }

    /**
     * Compares two non-signed large numbers.
     *
     * @return -1|0|1
     *
     * @pure
     */
    private function doCmp(string $a, string $b): int
    {
        $x = strlen($a);
        $y = strlen($b);

        $cmp = $x <=> $y;

        if ($cmp !== 0) {
            return $cmp;
        }

        return strcmp($a, $b) <=> 0; // enforce -1|0|1
    }

    /**
     * Pads the left of one of the given numbers with zeros if necessary to make both numbers the same length.
     *
     * The numbers must only consist of digits, without leading minus sign.
     *
     * @return array{string, string, int}
     *
     * @pure
     */
    private function pad(string $a, string $b): array
    {
        $x = strlen($a);
        $y = strlen($b);

        if ($x > $y) {
            $b = str_repeat('0', $x - $y) . $b;

            return [$a, $b, $x];
        }

        if ($x < $y) {
            $a = str_repeat('0', $y - $x) . $a;

            return [$a, $b, $y];
        }

        return [$a, $b, $x];
    }
}
<?php

declare(strict_types=1);

namespace Brick\Math\Internal\Calculator;

use Brick\Math\Internal\Calculator;
use GMP;
use Override;

use function gmp_add;
use function gmp_and;
use function gmp_div_q;
use function gmp_div_qr;
use function gmp_div_r;
use function gmp_gcd;
use function gmp_init;
use function gmp_invert;
use function gmp_lcm;
use function gmp_mul;
use function gmp_or;
use function gmp_pow;
use function gmp_powm;
use function gmp_root;
use function gmp_sqrt;
use function gmp_strval;
use function gmp_sub;
use function gmp_xor;
use function substr;

/**
 * Calculator implementation built around the GMP library.
 *
 * @internal
 */
final readonly class GmpCalculator extends Calculator
{
    #[Override]
    public function add(string $a, string $b): string
    {
        return gmp_strval(gmp_add($a, $b));
    }

    #[Override]
    public function sub(string $a, string $b): string
    {
        return gmp_strval(gmp_sub($a, $b));
    }

    #[Override]
    public function mul(string $a, string $b): string
    {
        return gmp_strval(gmp_mul($a, $b));
    }

    #[Override]
    public function divQ(string $a, string $b): string
    {
        return gmp_strval(gmp_div_q($a, $b));
    }

    #[Override]
    public function divR(string $a, string $b): string
    {
        return gmp_strval(gmp_div_r($a, $b));
    }

    #[Override]
    public function divQR(string $a, string $b): array
    {
        [$q, $r] = gmp_div_qr($a, $b);

        /**
         * @var GMP $q
         * @var GMP $r
         */
        return [
            gmp_strval($q),
            gmp_strval($r),
        ];
    }

    #[Override]
    public function pow(string $a, int $e): string
    {
        return gmp_strval(gmp_pow($a, $e));
    }

    #[Override]
    public function modInverse(string $x, string $m): ?string
    {
        $result = gmp_invert($x, $m);

        if ($result === false) {
            return null;
        }

        return gmp_strval($result);
    }

    #[Override]
    public function modPow(string $base, string $exp, string $mod): string
    {
        return gmp_strval(gmp_powm($base, $exp, $mod));
    }

    #[Override]
    public function gcd(string $a, string $b): string
    {
        return gmp_strval(gmp_gcd($a, $b));
    }

    #[Override]
    public function lcm(string $a, string $b): string
    {
        return gmp_strval(gmp_lcm($a, $b));
    }

    #[Override]
    public function fromBase(string $number, int $base): string
    {
        return gmp_strval(gmp_init($number, $base));
    }

    #[Override]
    public function toBase(string $number, int $base): string
    {
        return gmp_strval($number, $base);
    }

    #[Override]
    public function and(string $a, string $b): string
    {
        return gmp_strval(gmp_and($a, $b));
    }

    #[Override]
    public function or(string $a, string $b): string
    {
        return gmp_strval(gmp_or($a, $b));
    }

    #[Override]
    public function xor(string $a, string $b): string
    {
        return gmp_strval(gmp_xor($a, $b));
    }

    #[Override]
    public function sqrt(string $n): string
    {
        return gmp_strval(gmp_sqrt($n));
    }

    #[Override]
    public function nthRoot(string $n, int $k): string
    {
        // Delegate on the absolute value and re-apply the sign ourselves so the
        // truncation-toward-zero convention matches the shared Newton-Raphson fallback
        // bit-for-bit, regardless of any PHP/GMP behaviour changes for negative inputs.
        if ($n[0] === '-') {
            return '-' . gmp_strval(gmp_root(substr($n, 1), $k));
        }

        return gmp_strval(gmp_root($n, $k));
    }
}
<?php

declare(strict_types=1);

namespace Brick\Math\Internal\Calculator;

use Brick\Math\Internal\Calculator;
use Override;

use function bcadd;
use function bcdiv;
use function bcmod;
use function bcmul;
use function bcpow;
use function bcpowmod;
use function bcsqrt;
use function bcsub;

/**
 * Calculator implementation built around the bcmath library.
 *
 * @internal
 */
final readonly class BcMathCalculator extends Calculator
{
    #[Override]
    public function add(string $a, string $b): string
    {
        return bcadd($a, $b, 0);
    }

    #[Override]
    public function sub(string $a, string $b): string
    {
        return bcsub($a, $b, 0);
    }

    #[Override]
    public function mul(string $a, string $b): string
    {
        return bcmul($a, $b, 0);
    }

    #[Override]
    public function divQ(string $a, string $b): string
    {
        return bcdiv($a, $b, 0);
    }

    #[Override]
    public function divR(string $a, string $b): string
    {
        return bcmod($a, $b, 0);
    }

    #[Override]
    public function divQR(string $a, string $b): array
    {
        $q = bcdiv($a, $b, 0);
        $r = bcmod($a, $b, 0);

        return [$q, $r];
    }

    #[Override]
    public function pow(string $a, int $e): string
    {
        if ($e === 0) {
            return '1';
        }

        // bcpow() allocates memory proportional to the exponent even when the base is trivial,
        // exhausting memory on 32-bit builds at large exponents.
        if ($a === '0' || $a === '1') {
            return $a;
        }

        if ($a === '-1') {
            return $e % 2 === 0 ? '1' : '-1';
        }

        return bcpow($a, (string) $e, 0);
    }

    #[Override]
    public function modPow(string $base, string $exp, string $mod): string
    {
        // normalize to Euclidean representative so modPow() stays consistent with mod()
        $base = $this->mod($base, $mod);

        return bcpowmod($base, $exp, $mod, 0);
    }

    #[Override]
    public function sqrt(string $n): string
    {
        return bcsqrt($n, 0);
    }
}
<?php

declare(strict_types=1);

namespace Brick\Math\Internal;

use Brick\Math\RoundingMode;

use function chr;
use function intdiv;
use function ltrim;
use function ord;
use function str_repeat;
use function strlen;
use function strpos;
use function strrev;
use function strtolower;
use function substr;

/**
 * Performs basic operations on arbitrary size integers.
 *
 * Unless otherwise specified, all parameters must be validated as non-empty strings of digits,
 * without leading zero, and with an optional leading minus sign if the number is not zero.
 *
 * Any other parameter format will lead to undefined behaviour.
 * All methods must return strings respecting this format, unless specified otherwise.
 *
 * @internal
 */
abstract readonly class Calculator
{
    /**
     * The alphabet for converting from and to base 2 to 36, lowercase.
     */
    public const ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyz';

    /**
     * Returns the absolute value of a number.
     *
     * @pure
     */
    final public function abs(string $n): string
    {
        return ($n[0] === '-') ? substr($n, 1) : $n;
    }

    /**
     * Negates a number.
     *
     * @pure
     */
    final public function neg(string $n): string
    {
        if ($n === '0') {
            return '0';
        }

        if ($n[0] === '-') {
            return substr($n, 1);
        }

        return '-' . $n;
    }

    /**
     * Compares two numbers.
     *
     * Returns -1 if the first number is less than, 0 if equal to, 1 if greater than the second number.
     *
     * @return -1|0|1
     *
     * @pure
     */
    final public function cmp(string $a, string $b): int
    {
        [$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b);

        if ($aNeg && ! $bNeg) {
            return -1;
        }

        if ($bNeg && ! $aNeg) {
            return 1;
        }

        $aLen = strlen($aDig);
        $bLen = strlen($bDig);

        if ($aLen < $bLen) {
            $result = -1;
        } elseif ($aLen > $bLen) {
            $result = 1;
        } else {
            $result = $aDig <=> $bDig;
        }

        return $aNeg ? -$result : $result;
    }

    /**
     * Adds two numbers.
     *
     * @pure
     */
    abstract public function add(string $a, string $b): string;

    /**
     * Subtracts two numbers.
     *
     * @pure
     */
    abstract public function sub(string $a, string $b): string;

    /**
     * Multiplies two numbers.
     *
     * @pure
     */
    abstract public function mul(string $a, string $b): string;

    /**
     * Returns the quotient of the division of two numbers.
     *
     * @param string $a The dividend.
     * @param string $b The divisor, must not be zero.
     *
     * @return string The quotient.
     *
     * @pure
     */
    abstract public function divQ(string $a, string $b): string;

    /**
     * Returns the remainder of the division of two numbers.
     *
     * @param string $a The dividend.
     * @param string $b The divisor, must not be zero.
     *
     * @return string The remainder.
     *
     * @pure
     */
    abstract public function divR(string $a, string $b): string;

    /**
     * Returns the quotient and remainder of the division of two numbers.
     *
     * @param string $a The dividend.
     * @param string $b The divisor, must not be zero.
     *
     * @return array{string, string} An array containing the quotient and remainder.
     *
     * @pure
     */
    abstract public function divQR(string $a, string $b): array;

    /**
     * Exponentiates a number.
     *
     * @param string $a The base number.
     * @param int    $e The exponent, validated as a non-negative integer.
     *
     * @return string The power.
     *
     * @pure
     */
    abstract public function pow(string $a, int $e): string;

    /**
     * @param string $b The modulus; must not be zero.
     *
     * @pure
     */
    public function mod(string $a, string $b): string
    {
        return $this->divR($this->add($this->divR($a, $b), $b), $b);
    }

    /**
     * Returns the modular multiplicative inverse of $x modulo $m.
     *
     * If $x has no multiplicative inverse mod m, this method must return null.
     *
     * This method can be overridden by the concrete implementation if the underlying library has built-in support.
     *
     * @param string $m The modulus; must not be negative or zero.
     *
     * @pure
     */
    public function modInverse(string $x, string $m): ?string
    {
        if ($m === '1') {
            return '0';
        }

        $modVal = $x;

        if ($x[0] === '-' || ($this->cmp($this->abs($x), $m) >= 0)) {
            $modVal = $this->mod($x, $m);
        }

        [$g, $x] = $this->gcdExtended($modVal, $m);

        if ($g !== '1') {
            return null;
        }

        return $this->mod($this->add($this->mod($x, $m), $m), $m);
    }

    /**
     * Raises a number into power with modulo.
     *
     * @param string $base The base number.
     * @param string $exp  The exponent; must be positive or zero.
     * @param string $mod  The modulus; must be strictly positive.
     *
     * @pure
     */
    abstract public function modPow(string $base, string $exp, string $mod): string;

    /**
     * Returns the greatest common divisor of the two numbers.
     *
     * This method can be overridden by the concrete implementation if the underlying library
     * has built-in support for GCD calculations.
     *
     * @return string The GCD, always positive, or zero if both arguments are zero.
     *
     * @pure
     */
    public function gcd(string $a, string $b): string
    {
        while ($b !== '0') {
            [$a, $b] = [$b, $this->divR($a, $b)];
        }

        return $this->abs($a);
    }

    /**
     * Returns the least common multiple of the two numbers.
     *
     * This method can be overridden by the concrete implementation if the underlying library
     * has built-in support for LCM calculations.
     *
     * @return string The LCM, always positive, or zero if at least one argument is zero.
     *
     * @pure
     */
    public function lcm(string $a, string $b): string
    {
        if ($a === '0' || $b === '0') {
            return '0';
        }

        return $this->divQ($this->abs($this->mul($a, $b)), $this->gcd($a, $b));
    }

    /**
     * Returns the square root of the given number, rounded down.
     *
     * The result is the largest x such that xÂ² â‰¤ n.
     * The input MUST NOT be negative.
     *
     * @pure
     */
    abstract public function sqrt(string $n): string;

    /**
     * Returns the integer nth root of the given number, truncated toward zero.
     *
     * If $n is non-negative, the result is the largest x such that x^$k â‰¤ $n (floor).
     * If $n is negative, $k MUST be odd, and the result is the negation of the floor root of |$n|
     * (i.e., truncation toward zero: the smallest x such that x^$k â‰¥ $n).
     *
     * The caller MUST guarantee that $k â‰¥ 1 and that $n is non-negative when $k is even.
     *
     * This method can be overridden by the concrete implementation if the underlying library
     * has built-in support for nth root calculations.
     *
     * @param string $n The number. May be negative only when $k is odd.
     * @param int    $k The root degree. Must be strictly positive.
     *
     * @pure
     */
    public function nthRoot(string $n, int $k): string
    {
        if ($n === '0') {
            return '0';
        }

        $negative = ($n[0] === '-');
        $m = $negative ? substr($n, 1) : $n;

        if ($m === '1') {
            return $negative ? '-1' : '1';
        }

        // Initial overshoot: 10^ceil(strlen(m)/k) is strictly greater than the true root.
        // Newton-Raphson requires starting above the true root to converge monotonically down.
        $x = '1' . str_repeat('0', intdiv(strlen($m) - 1, $k) + 1);

        $kStr = (string) $k;
        $kMinusOneStr = (string) ($k - 1);

        // Newton-Raphson recurrence for integer nth root:
        //   x_{i+1} = floor(((k-1) * x_i + floor(m / x_i^{k-1})) / k)
        for (; ;) {
            $nx = $this->divQ(
                $this->add(
                    $this->mul($kMinusOneStr, $x),
                    $this->divQ($m, $this->pow($x, $k - 1)),
                ),
                $kStr,
            );

            if ($this->cmp($nx, $x) >= 0) {
                break;
            }

            $x = $nx;
        }

        return $negative ? $this->neg($x) : $x;
    }

    /**
     * Converts a number from an arbitrary base.
     *
     * This method can be overridden by the concrete implementation if the underlying library
     * has built-in support for base conversion.
     *
     * @param string $number The number, positive or zero, non-empty, case-insensitively validated for the given base.
     * @param int    $base   The base of the number, validated from 2 to 36.
     *
     * @return string The converted number, following the Calculator conventions.
     *
     * @pure
     */
    public function fromBase(string $number, int $base): string
    {
        return $this->fromArbitraryBase(strtolower($number), self::ALPHABET, $base);
    }

    /**
     * Converts a number to an arbitrary base.
     *
     * This method can be overridden by the concrete implementation if the underlying library
     * has built-in support for base conversion.
     *
     * @param string $number The number to convert, following the Calculator conventions.
     * @param int    $base   The base to convert to, validated from 2 to 36.
     *
     * @return string The converted number, lowercase.
     *
     * @pure
     */
    public function toBase(string $number, int $base): string
    {
        $negative = ($number[0] === '-');

        if ($negative) {
            $number = substr($number, 1);
        }

        $number = $this->toArbitraryBase($number, self::ALPHABET, $base);

        if ($negative) {
            return '-' . $number;
        }

        return $number;
    }

    /**
     * Converts a non-negative number in an arbitrary base using a custom alphabet, to base 10.
     *
     * @param string $number   The number to convert, validated as a non-empty string,
     *                         containing only chars in the given alphabet/base.
     * @param string $alphabet The alphabet that contains every digit, validated as 2 chars minimum.
     * @param int    $base     The base of the number, validated from 2 to alphabet length.
     *
     * @return string The number in base 10, following the Calculator conventions.
     *
     * @pure
     */
    final public function fromArbitraryBase(string $number, string $alphabet, int $base): string
    {
        // remove leading "zeros"
        $number = ltrim($number, $alphabet[0]);

        if ($number === '') {
            return '0';
        }

        // optimize for "one"
        if ($number === $alphabet[1]) {
            return '1';
        }

        $result = '0';
        $power = '1';

        $base = (string) $base;

        for ($i = strlen($number) - 1; $i >= 0; $i--) {
            $index = strpos($alphabet, $number[$i]);

            if ($index !== 0) {
                $result = $this->add(
                    $result,
                    ($index === 1) ? $power : $this->mul($power, (string) $index),
                );
            }

            if ($i !== 0) {
                $power = $this->mul($power, $base);
            }
        }

        return $result;
    }

    /**
     * Converts a non-negative number to an arbitrary base using a custom alphabet.
     *
     * @param string $number   The number to convert, positive or zero, following the Calculator conventions.
     * @param string $alphabet The alphabet that contains every digit, validated as 2 chars minimum.
     * @param int    $base     The base to convert to, validated from 2 to alphabet length.
     *
     * @return string The converted number in the given alphabet.
     *
     * @pure
     */
    final public function toArbitraryBase(string $number, string $alphabet, int $base): string
    {
        if ($number === '0') {
            return $alphabet[0];
        }

        $base = (string) $base;
        $result = '';

        while ($number !== '0') {
            [$number, $remainder] = $this->divQR($number, $base);
            $remainder = (int) $remainder;

            $result .= $alphabet[$remainder];
        }

        return strrev($result);
    }

    /**
     * Performs a rounded division.
     *
     * When the remainder of the division is not zero, rounding is performed according to the rounding mode provided,
     * unless RoundingMode::Unnecessary is used, in which case the method returns null.
     *
     * @param string       $a            The dividend.
     * @param string       $b            The divisor, must not be zero.
     * @param RoundingMode $roundingMode The rounding mode.
     *
     * @pure
     */
    final public function divRound(string $a, string $b, RoundingMode $roundingMode): ?string
    {
        [$quotient, $remainder] = $this->divQR($a, $b);

        $hasDiscardedFraction = ($remainder !== '0');
        $isPositiveOrZero = ($a[0] === '-') === ($b[0] === '-');

        $discardedFractionSign = function () use ($remainder, $b): int {
            $r = $this->abs($this->mul($remainder, '2'));
            $b = $this->abs($b);

            return $this->cmp($r, $b);
        };

        $lastDigitIsEven = (int) $quotient[-1] % 2 === 0;

        $increment = match ($roundingMode) {
            RoundingMode::Unnecessary => $hasDiscardedFraction ? null : false,
            RoundingMode::Up => $hasDiscardedFraction,
            RoundingMode::Down => false,
            RoundingMode::Ceiling => $hasDiscardedFraction && $isPositiveOrZero,
            RoundingMode::Floor => $hasDiscardedFraction && ! $isPositiveOrZero,
            RoundingMode::HalfUp => $discardedFractionSign() >= 0,
            RoundingMode::HalfDown => $discardedFractionSign() > 0,
            RoundingMode::HalfCeiling => $isPositiveOrZero ? $discardedFractionSign() >= 0 : $discardedFractionSign() > 0,
            RoundingMode::HalfFloor => $isPositiveOrZero ? $discardedFractionSign() > 0 : $discardedFractionSign() >= 0,
            RoundingMode::HalfEven => $lastDigitIsEven ? $discardedFractionSign() > 0 : $discardedFractionSign() >= 0,
            RoundingMode::HalfOdd => $lastDigitIsEven ? $discardedFractionSign() >= 0 : $discardedFractionSign() > 0,
        };

        if ($increment === null) {
            return null;
        }

        if ($increment) {
            return $isPositiveOrZero
                ? $this->add($quotient, '1')
                : $this->sub($quotient, '1');
        }

        return $quotient;
    }

    /**
     * Calculates bitwise AND of two numbers.
     *
     * This method can be overridden by the concrete implementation if the underlying library
     * has built-in support for bitwise operations.
     *
     * @pure
     */
    public function and(string $a, string $b): string
    {
        return $this->bitwise('and', $a, $b);
    }

    /**
     * Calculates bitwise OR of two numbers.
     *
     * This method can be overridden by the concrete implementation if the underlying library
     * has built-in support for bitwise operations.
     *
     * @pure
     */
    public function or(string $a, string $b): string
    {
        return $this->bitwise('or', $a, $b);
    }

    /**
     * Calculates bitwise XOR of two numbers.
     *
     * This method can be overridden by the concrete implementation if the underlying library
     * has built-in support for bitwise operations.
     *
     * @pure
     */
    public function xor(string $a, string $b): string
    {
        return $this->bitwise('xor', $a, $b);
    }

    /**
     * Extracts the sign & digits of the operands.
     *
     * @return array{bool, bool, string, string} Whether $a and $b are negative, followed by their digits.
     *
     * @pure
     */
    final protected function init(string $a, string $b): array
    {
        return [
            $aNeg = ($a[0] === '-'),
            $bNeg = ($b[0] === '-'),

            $aNeg ? substr($a, 1) : $a,
            $bNeg ? substr($b, 1) : $b,
        ];
    }

    /**
     * @param string $a Must be non-negative.
     * @param string $b Must be non-negative.
     *
     * @return array{string, string} GCD, X
     *
     * @pure
     */
    private function gcdExtended(string $a, string $b): array
    {
        // Iterative extended Euclidean algorithm; recursion would exhaust memory on large inputs.
        [$r0, $r1] = [$a, $b];
        [$x0, $x1] = ['1', '0'];

        while ($r1 !== '0') {
            [$q, $r] = $this->divQR($r0, $r1);

            [$r0, $r1] = [$r1, $r];
            [$x0, $x1] = [$x1, $this->sub($x0, $this->mul($q, $x1))];
        }

        return [$r0, $x0];
    }

    /**
     * Performs a bitwise operation on a decimal number.
     *
     * @param 'and'|'or'|'xor' $operator The operator to use.
     * @param string           $a        The left operand.
     * @param string           $b        The right operand.
     *
     * @pure
     */
    private function bitwise(string $operator, string $a, string $b): string
    {
        [$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b);

        $aBin = $this->toBinary($aDig);
        $bBin = $this->toBinary($bDig);

        $aLen = strlen($aBin);
        $bLen = strlen($bBin);

        if ($aLen > $bLen) {
            $bBin = str_repeat("\x00", $aLen - $bLen) . $bBin;
        } elseif ($bLen > $aLen) {
            $aBin = str_repeat("\x00", $bLen - $aLen) . $aBin;
        }

        if ($aNeg) {
            $aBin = $this->twosComplement($aBin);
        }
        if ($bNeg) {
            $bBin = $this->twosComplement($bBin);
        }

        $value = match ($operator) {
            'and' => $aBin & $bBin,
            'or' => $aBin | $bBin,
            'xor' => $aBin ^ $bBin,
        };

        $negative = match ($operator) {
            'and' => $aNeg and $bNeg,
            'or' => $aNeg or $bNeg,
            'xor' => $aNeg xor $bNeg,
        };

        if ($negative) {
            $value = $this->twosComplement($value);
        }

        $result = $this->toDecimal($value);

        return $negative ? $this->neg($result) : $result;
    }

    /**
     * @param string $number A positive, binary number.
     *
     * @pure
     */
    private function twosComplement(string $number): string
    {
        $xor = str_repeat("\xff", strlen($number));

        $number ^= $xor;

        for ($i = strlen($number) - 1; $i >= 0; $i--) {
            $byte = ord($number[$i]);

            if (++$byte !== 256) {
                $number[$i] = chr($byte); // @phpstan-ignore argument.type

                break;
            }

            $number[$i] = "\x00";

            if ($i === 0) {
                $number = "\x01" . $number;
            }
        }

        return $number;
    }

    /**
     * Converts a decimal number to a binary string.
     *
     * @param string $number The number to convert, positive or zero, only digits.
     *
     * @pure
     */
    private function toBinary(string $number): string
    {
        $result = '';

        while ($number !== '0') {
            [$number, $remainder] = $this->divQR($number, '256');
            $result .= chr((int) $remainder); // @phpstan-ignore argument.type
        }

        return strrev($result);
    }

    /**
     * Returns the positive decimal representation of a binary number.
     *
     * @param string $bytes The bytes representing the number.
     *
     * @pure
     */
    private function toDecimal(string $bytes): string
    {
        $result = '0';
        $power = '1';

        for ($i = strlen($bytes) - 1; $i >= 0; $i--) {
            $index = ord($bytes[$i]);

            if ($index !== 0) {
                $result = $this->add(
                    $result,
                    ($index === 1) ? $power : $this->mul($power, (string) $index),
                );
            }

            if ($i !== 0) {
                $power = $this->mul($power, '256');
            }
        }

        return $result;
    }
}
<?php

declare(strict_types=1);

namespace Brick\Math\Internal;

use Brick\Math\Exception\IntegerOverflowException;

use function is_int;
use function sprintf;

use const PHP_INT_MIN;

/**
 * Helpers for arithmetic operations that throw on native integer overflow.
 *
 * @internal
 */
final class Safe
{
    /**
     * @codeCoverageIgnore
     */
    private function __construct()
    {
    }

    /**
     * @pure
     */
    public static function add(int $a, int $b): int
    {
        $result = $a + $b;

        if (is_int($result)) {
            return $result;
        }

        // @phpstan-ignore deadCode.unreachable
        throw IntegerOverflowException::nativeIntegerOverflow(sprintf('%d + %d', $a, $b));
    }

    /**
     * @pure
     */
    public static function sub(int $a, int $b): int
    {
        $result = $a - $b;

        if (is_int($result)) {
            return $result;
        }

        // @phpstan-ignore deadCode.unreachable
        throw IntegerOverflowException::nativeIntegerOverflow(sprintf('%d - %d', $a, $b));
    }

    /**
     * @pure
     */
    public static function mul(int $a, int $b): int
    {
        $result = $a * $b;

        if (is_int($result)) {
            return $result;
        }

        // @phpstan-ignore deadCode.unreachable
        throw IntegerOverflowException::nativeIntegerOverflow(sprintf('%d * %d', $a, $b));
    }

    /**
     * @pure
     */
    public static function neg(int $value): int
    {
        if ($value === PHP_INT_MIN) {
            throw IntegerOverflowException::nativeIntegerOverflow(sprintf('-(%d)', $value));
        }

        return -$value;
    }
}
<?php

declare(strict_types=1);

namespace Brick\Math\Internal;

use function extension_loaded;

/**
 * Stores the current Calculator instance used by BigNumber classes.
 *
 * @internal
 */
final class CalculatorRegistry
{
    /**
     * The Calculator instance in use.
     */
    private static ?Calculator $instance = null;

    /**
     * Sets the Calculator instance to use.
     *
     * An instance is typically set only in unit tests: autodetect is usually the best option.
     *
     * @param Calculator|null $calculator The calculator instance, or null to revert to autodetect.
     */
    final public static function set(?Calculator $calculator): void
    {
        self::$instance = $calculator;
    }

    /**
     * Returns the Calculator instance to use.
     *
     * If none has been explicitly set, the fastest available implementation will be returned.
     *
     * Note: even though this method is not technically pure, it is considered pure when used in a normal context, when
     * only relying on autodetect.
     *
     * @pure
     */
    final public static function get(): Calculator
    {
        /** @phpstan-ignore impure.staticPropertyAccess */
        if (self::$instance === null) {
            /** @phpstan-ignore impure.propertyAssign */
            self::$instance = self::detect();
        }

        /** @phpstan-ignore impure.staticPropertyAccess */
        return self::$instance;
    }

    /**
     * Returns the fastest available Calculator implementation.
     *
     * @pure
     *
     * @codeCoverageIgnore
     */
    private static function detect(): Calculator
    {
        if (extension_loaded('gmp')) {
            return new Calculator\GmpCalculator();
        }

        if (extension_loaded('bcmath')) {
            return new Calculator\BcMathCalculator();
        }

        return new Calculator\NativeCalculator();
    }
}
<?php

declare(strict_types=1);

namespace Brick\Math\Internal;

use Brick\Math\RoundingMode;

use function ltrim;
use function rtrim;
use function str_pad;
use function str_repeat;
use function strlen;
use function substr;

use const STR_PAD_LEFT;

/**
 * Shared helper for decimal operations.
 *
 * @internal
 */
final class DecimalHelper
{
    /**
     * @codeCoverageIgnore
     */
    private function __construct()
    {
    }

    /**
     * Computes the scale needed to represent the exact decimal result of a reduced fraction.
     *
     * Returns null if the denominator has prime factors other than 2 or 5.
     *
     * @param string $denominator The denominator of the reduced fraction. Must be strictly positive.
     *
     * @return non-negative-int|null
     *
     * @pure
     */
    public static function computeScaleFromReducedFractionDenominator(string $denominator): ?int
    {
        $calculator = CalculatorRegistry::get();

        $d = rtrim($denominator, '0');

        /** @var non-negative-int $scale rtrim can only shorten a string */
        $scale = strlen($denominator) - strlen($d);

        foreach ([5, 2] as $prime) {
            for (; ;) {
                $lastDigit = (int) $d[-1];

                if ($lastDigit % $prime !== 0) {
                    break;
                }

                $d = $calculator->divQ($d, (string) $prime);
                $scale++;
            }
        }

        return $d === '1' ? $scale : null;
    }

    /**
     * Scales an unscaled decimal value to the requested scale.
     *
     * Returns null when rounding is necessary and the rounding mode is Unnecessary.
     *
     * @param string       $value        The unscaled value.
     * @param int          $currentScale The current scale.
     * @param int          $targetScale  The target scale.
     * @param RoundingMode $roundingMode The rounding mode.
     *
     * @return string|null The unscaled value at the target scale, or null if RoundingMode::Unnecessary is used and rounding is necessary.
     *
     * @pure
     */
    public static function scale(string $value, int $currentScale, int $targetScale, RoundingMode $roundingMode): ?string
    {
        $scaled = self::tryScaleExactly($value, $currentScale, $targetScale);

        if ($scaled !== null) {
            return $scaled;
        }

        if ($roundingMode === RoundingMode::Unnecessary) {
            return null;
        }

        $divisor = '1' . str_repeat('0', $currentScale - $targetScale);

        return CalculatorRegistry::get()->divRound($value, $divisor, $roundingMode);
    }

    /**
     * Adds leading zeros if necessary to represent the full decimal number.
     *
     * @param string $value The unscaled value.
     * @param int    $scale The current scale.
     *
     * @pure
     */
    public static function padUnscaledValue(string $value, int $scale): string
    {
        $targetLength = $scale + 1;
        $negative = ($value[0] === '-');
        $length = strlen($value);

        if ($negative) {
            $length--;
        }

        if ($length >= $targetLength) {
            return $value;
        }

        if ($negative) {
            $value = substr($value, 1);
        }

        $value = str_pad($value, $targetLength, '0', STR_PAD_LEFT);

        if ($negative) {
            $value = '-' . $value;
        }

        return $value;
    }

    /**
     * Tries to scale exactly without rounding, returning null when rounding would be required.
     *
     * @param string $value        The unscaled value.
     * @param int    $currentScale The current scale.
     * @param int    $targetScale  The target scale.
     *
     * @return string|null The unscaled value at the target scale, or null if rounding would be required.
     *
     * @pure
     */
    public static function tryScaleExactly(string $value, int $currentScale, int $targetScale): ?string
    {
        if ($value === '0' || $targetScale === $currentScale) {
            return $value;
        }

        if ($targetScale > $currentScale) {
            return $value . str_repeat('0', $targetScale - $currentScale);
        }

        $negative = ($value[0] === '-');
        if ($negative) {
            $value = substr($value, 1);
        }

        $value = self::padUnscaledValue($value, $currentScale);
        $discardedDigits = $currentScale - $targetScale;

        if (substr($value, -$discardedDigits) !== str_repeat('0', $discardedDigits)) {
            return null;
        }

        $value = substr($value, 0, -$discardedDigits);
        $value = ltrim($value, '0');

        if ($value === '') {
            return '0';
        }

        if ($negative) {
            $value = '-' . $value;
        }

        return $value;
    }
}
{
    "name": "brick/math",
    "description": "Arbitrary-precision arithmetic library",
    "type": "library",
    "keywords": [
        "Brick",
        "Math",
        "Mathematics",
        "Arbitrary-precision",
        "Arithmetic",
        "BigInteger",
        "BigDecimal",
        "BigRational",
        "BigNumber",
        "Bignum",
        "Decimal",
        "Rational",
        "Integer"
    ],
    "license": "MIT",
    "require": {
        "php": "^8.2"
    },
    "require-dev": {
        "phpunit/phpunit": "^11.5",
        "phpstan/phpstan": "2.2.13",
        "phpstan/phpstan-phpunit": "2.0.18"
    },
    "autoload": {
        "psr-4": {
            "Brick\\Math\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Brick\\Math\\Tests\\": "tests/"
        }
    }
}
{
    "name": "danog/madelineprotophar",
    "require": {
        "danog/madelineproto": "8.12.0"
    },
    "authors": [
        {
            "name": "Daniil Gentili",
            "email": "daniil@daniil.it"
        }
    ],
    "config": {
        "allow-plugins": {
            "symfony/thanks": true
        }
    },
    "repositories": [
        {
            "type": "path",
            "url": "/__w/MadelineProto/MadelineProto",
            "options": {"symlink": false}
        }
    ]
}
<?php
/**
 * A page of the setup flow, or a status page.
 *
 * @var \danog\MadelineProto\WebUI\Template $this
 * @var \danog\MadelineProto\WebUI\Page $page
 * @var string $state Initial state of the web app (JSON)
 *
 * The web app renders the page from the state in #madeline-app; the markup is for browsers without JavaScript.
 */
$this->layout('layout', ['title' => $page->title]);
?>
<?php $this->start('app') ?>
<?php if (isset($state)): ?>
<script type="application/json" id="madeline-state"><?= $state ?></script>
<?php endif ?>
<div id="madeline-app"></div>
<?php $this->stop() ?>
<div class="shell">
    <aside class="rail">
        <?= $this->insert('partials/brand') ?>
        <?php if ($page->step !== null): ?>
            <?= $this->insert('partials/steps', ['current' => $page->step]) ?>
        <?php endif ?>
        <p class="rail-foot">
            <a href="https://docs.madelineproto.xyz" target="_blank" rel="noopener noreferrer"><?= $this->t('webDocs') ?></a>
        </p>
    </aside>
    <main class="pane">
        <div class="pane-body">
            <div class="pane-main">
                <header class="pane-head">
                    <h1><?= $this->e($page->title) ?></h1>
                    <?php if ($page->lead !== ''): ?>
                        <p class="lead"><?= $this->e($page->lead) ?></p>
                    <?php endif ?>
                </header>

                <?= $this->insert('partials/alerts', ['alerts' => $page->alerts]) ?>

                <?php if ($page->instructions): ?>
                    <ol class="instructions">
                        <?php foreach ($page->instructions as $instruction): ?>
                            <li><?= $this->e($instruction) ?></li>
                        <?php endforeach ?>
                    </ol>
                <?php endif ?>

                <?php if ($page->submit !== null): ?>
                    <form method="post" class="form" data-madeline-form>
                        <?= $this->insert('partials/fields', ['page' => $page]) ?>
                        <?= $this->insert('partials/actions', ['page' => $page]) ?>
                    </form>
                <?php endif ?>
            </div>
        </div>
    </main>
</div>
<?php
/**
 * Base HTML document.
 *
 * @var \danog\MadelineProto\WebUI\Template $this
 * @var string $title    Page title
 * @var string $lang     Language code
 * @var string $dir      Text direction
 * @var string $assetUrl Prefix of the URLs of the assets (see WebUI::serveAsset())
 *
 * Sections: content (the markup for browsers without JavaScript), app (the root of the web app, before it),
 * head (extra tags in <head>), scripts (extra tags at the end of <body>).
 *
 * With JavaScript, the web app (assets/tweb/app.js) renders the page: its stylesheet is loaded as media="print",
 * so that it doesn't apply to the markup for browsers without JavaScript, and the app enables it.
 */
$hasApp = isset($assetUrl) && $this->getEngine()->exists('assets/tweb/app.js');
?><!DOCTYPE html>
<html<?= $this->attrs(['lang' => $lang ?? 'en', 'dir' => $dir ?? 'ltr']) ?>>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex, nofollow">
<meta name="color-scheme" content="light dark">
<title><?= $this->e($title) ?> â€“ MadelineProto</title>
<link rel="icon" href="data:image/svg+xml,<?= $this->e(rawurlencode($this->getEngine()->asset('assets/mark.svg'))) ?>">
<?php if ($hasApp): ?>
<link id="madeline-app-css" rel="stylesheet" media="print" href="<?= $this->e($assetUrl.'assets/tweb/app.css') ?>">
<noscript><style><?= $this->asset('assets/madeline.css') ?></style></noscript>
<?php else: ?>
<style><?= $this->asset('assets/madeline.css') ?></style>
<?php endif ?>
<?= $this->section('head') ?>
</head>
<body>
<?php if ($hasApp): ?>
<?= $this->section('app') ?>
<noscript><?= $this->section('content') ?></noscript>
<script defer src="<?= $this->e($assetUrl.'assets/tweb/app.js') ?>"></script>
<?php else: ?>
<?= $this->section('content') ?>
<?php endif ?>
<?= $this->section('scripts') ?>
</body>
</html>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><rect width="32" height="32" rx="9" fill="#5546D6"/><path d="M9 22.5V10l7 7.5 7-7.5v12.5" fill="none" stroke="#fff" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></svg>
/* MadelineProto web UI. Self-contained: these pages are served by your own server, so no external fonts or assets. */
:root {
    --ink: #1c2147;
    --ink-soft: #4d5378;
    --ink-faint: #7c82a3;
    --mist: #edf0f7;
    --surface: #ffffff;
    --line: #d5dae8;
    --line-strong: #b4bbd3;
    --signal: #5546d6;
    --signal-hover: #4637c4;
    --signal-ink: #ffffff;
    --signal-soft: #e5e2fb;
    --ok: #17855f;
    --ok-soft: #dcf1e8;
    --warn: #8f5a04;
    --warn-soft: #fbefd6;
    --err: #b4322a;
    --err-soft: #fbe2df;
    --rail: #1c2147;
    --rail-ink: #e4e6f5;
    --rail-faint: #9aa0c6;
    --rail-line: #353b6b;
    --focus: 0 0 0 3px color-mix(in srgb, var(--signal) 35%, transparent);
    --qr-light: #ffffff;
    --qr-dark: #1c2147;

    --font-display: ui-rounded, "SF Pro Rounded", "Nunito", "Varela Round", system-ui, sans-serif;
    --font-body: system-ui, -apple-system, "Segoe UI", Roboto, "Noto Sans", "Helvetica Neue", Arial, sans-serif;
    --font-mono: ui-monospace, "SF Mono", "Cascadia Mono", "JetBrains Mono", Menlo, Consolas, monospace;

    --radius-control: 10px;
    --radius-panel: 18px;
    color-scheme: light;
}
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        --ink: #e7e9f6;
        --ink-soft: #aab0cf;
        --ink-faint: #7a80a6;
        --mist: #121530;
        --surface: #1a1e40;
        --line: #2d3363;
        --line-strong: #444b82;
        --signal: #8f84ff;
        --signal-hover: #a59cff;
        --signal-ink: #120f3a;
        --signal-soft: #2a2768;
        --ok: #4cc79a;
        --ok-soft: #12372c;
        --warn: #e8b04b;
        --warn-soft: #3a2e12;
        --err: #ff8a7f;
        --err-soft: #45201e;
        --rail: #0b0d26;
        --rail-ink: #e4e6f5;
        --rail-faint: #8a90b8;
        --rail-line: #262b57;
        --qr-light: #d3d6ea;
        --qr-dark: #14172e;
        color-scheme: dark;
    }
}
:root[data-theme="dark"] {
    --ink: #e7e9f6;
    --ink-soft: #aab0cf;
    --ink-faint: #7a80a6;
    --mist: #121530;
    --surface: #1a1e40;
    --line: #2d3363;
    --line-strong: #444b82;
    --signal: #8f84ff;
    --signal-hover: #a59cff;
    --signal-ink: #120f3a;
    --signal-soft: #2a2768;
    --ok: #4cc79a;
    --ok-soft: #12372c;
    --warn: #e8b04b;
    --warn-soft: #3a2e12;
    --err: #ff8a7f;
    --err-soft: #45201e;
    --rail: #0b0d26;
    --rail-ink: #e4e6f5;
    --rail-faint: #8a90b8;
    --rail-line: #262b57;
    --qr-light: #d3d6ea;
    --qr-dark: #14172e;
    color-scheme: dark;
}

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
body {
    margin: 0;
    min-height: 100vh;
    background: var(--mist);
    color: var(--ink);
    font: 1rem/1.55 var(--font-body);
    -webkit-font-smoothing: antialiased;
}
a { color: var(--signal); text-underline-offset: 0.18em; }
a:hover { color: var(--signal-hover); }
:focus-visible { outline: none; box-shadow: var(--focus); }
[hidden] { display: none !important; }

/* Shell: progress rail + working pane */
.shell {
    display: grid;
    grid-template-columns: minmax(15rem, 19rem) 1fr;
    min-height: 100vh;
}
.rail {
    background: var(--rail);
    color: var(--rail-ink);
    padding: 2.25rem 2rem;
    display: flex;
    flex-direction: column;
    gap: 3rem;
}
.brand { display: flex; align-items: center; gap: 0.7rem; }
.brand-mark svg { display: block; width: 2.25rem; height: 2.25rem; }
.brand-name {
    font: 650 1.2rem/1 var(--font-display);
    letter-spacing: -0.01em;
}
.rail-foot { margin: auto 0 0; font-size: 0.875rem; }
.rail-foot a { color: var(--rail-faint); }
.rail-foot a:hover { color: var(--rail-ink); }

/* Steps: a real sequence, so they are numbered and joined by a line */
.steps { list-style: none; margin: 0; padding: 0; }
.step {
    position: relative;
    display: flex;
    align-items: center;
    gap: 0.9rem;
    padding: 0.55rem 0;
    color: var(--rail-faint);
}
.step + .step::before {
    content: "";
    position: absolute;
    inset-inline-start: calc(0.875rem - 1px);
    bottom: calc(50% + 0.875rem);
    height: calc(100% - 1.75rem);
    width: 2px;
    background: var(--rail-line);
}
.step.is-done + .step::before, .step.is-current::before { background: var(--signal); }
.step-dot {
    flex: none;
    display: grid;
    place-items: center;
    width: 1.75rem;
    height: 1.75rem;
    border-radius: 50%;
    border: 2px solid var(--rail-line);
    font: 650 0.8125rem/1 var(--font-display);
    font-variant-numeric: tabular-nums;
}
.step-dot svg { width: 0.95rem; height: 0.95rem; }
.step.is-done .step-dot { border-color: var(--signal); color: var(--signal); }
.step.is-current { color: var(--rail-ink); font-weight: 600; }
.step.is-current .step-dot { background: var(--signal); border-color: var(--signal); color: var(--signal-ink); }

/* Pane */
.pane {
    display: flex;
    align-items: center;
    padding: 3.5rem clamp(1.5rem, 5vw, 5rem);
}
.pane-body { width: 100%; max-width: 31rem; }
.pane-body.has-qr {
    max-width: 58rem;
    display: grid;
    grid-template-columns: minmax(0, 1fr) 19rem;
    gap: clamp(2rem, 5vw, 4.5rem);
    align-items: start;
}
.pane-head h1 {
    margin: 0 0 0.6rem;
    font: 680 clamp(1.75rem, 1.3rem + 1.6vw, 2.35rem)/1.12 var(--font-display);
    letter-spacing: -0.02em;
    text-wrap: balance;
}
.lead {
    margin: 0 0 1.75rem;
    color: var(--ink-soft);
    font-size: 1.0625rem;
    max-width: 34em;
    text-wrap: pretty;
}

/* Alerts */
.alert {
    margin: 0 0 1.25rem;
    padding: 0.85rem 1rem;
    border-radius: var(--radius-control);
    border-inline-start: 4px solid var(--alert);
    background: var(--alert-soft);
    font-size: 0.9375rem;
    overflow-wrap: anywhere;
}
.alert p { margin: 0; }
.alert p + p { margin-top: 0.25rem; }
.alert-title { font-weight: 650; color: var(--alert); }
.alert pre {
    margin: 0.6rem 0 0;
    padding: 0.6rem 0.75rem;
    border-radius: 8px;
    background: var(--surface);
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    font: 0.8125rem/1.5 var(--font-mono);
}
.alert.is-info { --alert: var(--signal); --alert-soft: var(--signal-soft); }
.alert.is-success { --alert: var(--ok); --alert-soft: var(--ok-soft); }
.alert.is-warning { --alert: var(--warn); --alert-soft: var(--warn-soft); }
.alert.is-error { --alert: var(--err); --alert-soft: var(--err-soft); }

/* Instructions */
.instructions {
    margin: 0 0 1.75rem;
    padding: 0;
    list-style: none;
    counter-reset: instruction;
    color: var(--ink-soft);
}
.instructions > li {
    counter-increment: instruction;
    position: relative;
    padding-inline-start: 2.25rem;
    margin-bottom: 0.65rem;
}
.instructions > li::before {
    content: counter(instruction);
    position: absolute;
    inset-inline-start: 0;
    top: 0.05rem;
    display: grid;
    place-items: center;
    width: 1.5rem;
    height: 1.5rem;
    border-radius: 50%;
    background: var(--signal-soft);
    color: var(--signal);
    font: 650 0.75rem/1 var(--font-display);
}
.instructions ul { margin: 0.4rem 0 0; padding-inline-start: 1.1rem; }
.instructions ul li + li { margin-top: 0.2rem; }

/* Form */
.form { display: flex; flex-direction: column; gap: 1.15rem; }
.field { margin: 0; padding: 0; border: 0; min-width: 0; }
.field > label, .field > legend {
    display: block;
    margin-bottom: 0.4rem;
    padding: 0;
    font-weight: 600;
    font-size: 0.9375rem;
}
.optional { font-weight: 400; color: var(--ink-faint); margin-inline-start: 0.35rem; }
.hint { margin: 0.4rem 0 0; font-size: 0.875rem; color: var(--ink-faint); }
.field-error { margin: 0.4rem 0 0; font-size: 0.875rem; font-weight: 600; color: var(--err); }
.field.is-invalid input:not([type="radio"]), .field.is-invalid textarea { border-color: var(--err); }
.field.is-invalid input:not([type="radio"]):focus, .field.is-invalid textarea:focus {
    border-color: var(--err);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--err) 30%, transparent);
}
input:not([type="radio"]), textarea {
    width: 100%;
    min-height: 3rem;
    padding: 0.7rem 0.9rem;
    border: 1px solid var(--line-strong);
    border-radius: var(--radius-control);
    background: var(--surface);
    color: var(--ink);
    font: inherit;
    transition: border-color 0.15s, box-shadow 0.15s;
}
textarea { resize: vertical; min-height: 5.5rem; }
input::placeholder, textarea::placeholder { color: var(--ink-faint); opacity: 1; }
input:not([type="radio"]):focus, textarea:focus { outline: none; border-color: var(--signal); box-shadow: var(--focus); }
.field.is-code input {
    font: 600 1.6rem/1 var(--font-mono);
    letter-spacing: 0.35em;
    padding-inline-start: 1rem;
    font-variant-numeric: tabular-nums;
}
.field.is-code input::placeholder { letter-spacing: 0.35em; }

.reveal { position: relative; }
.reveal input { padding-inline-end: 5rem; }
.reveal-toggle {
    position: absolute;
    inset-inline-end: 0.4rem;
    top: 50%;
    transform: translateY(-50%);
    padding: 0.35rem 0.6rem;
    border: 0;
    border-radius: 7px;
    background: transparent;
    color: var(--signal);
    font: 600 0.875rem/1 var(--font-body);
    cursor: pointer;
}
.reveal-toggle:hover { background: var(--signal-soft); }

/* Segmented choice */
.segmented {
    display: inline-flex;
    padding: 4px;
    gap: 4px;
    border-radius: 12px;
    background: color-mix(in srgb, var(--line) 60%, transparent);
}
.segment { position: relative; }
.segment input { position: absolute; inset: 0; opacity: 0; margin: 0; cursor: pointer; }
.segment span {
    display: block;
    padding: 0.5rem 1.15rem;
    border-radius: 8px;
    font-weight: 600;
    font-size: 0.9375rem;
    color: var(--ink-soft);
    transition: background-color 0.15s, color 0.15s;
}
.segment input:checked + span {
    background: var(--surface);
    color: var(--ink);
    box-shadow: 0 1px 2px color-mix(in srgb, var(--ink) 18%, transparent);
}
.segment input:focus-visible + span { box-shadow: var(--focus); }

/* Buttons */
.form-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 0.75rem 1.25rem; margin-top: 0.4rem; }
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.55rem;
    min-height: 3rem;
    padding: 0 1.5rem;
    border: 0;
    border-radius: var(--radius-control);
    font: 650 1rem/1 var(--font-body);
    cursor: pointer;
    transition: background-color 0.15s, color 0.15s;
}
.btn-primary { background: var(--signal); color: var(--signal-ink); }
.btn-primary:hover { background: var(--signal-hover); }
.btn-quiet {
    padding: 0 0.25rem;
    background: transparent;
    color: var(--ink-soft);
    font-weight: 600;
    text-decoration: underline;
    text-decoration-color: var(--line-strong);
    text-underline-offset: 0.25em;
}
.btn-quiet:hover { color: var(--ink); text-decoration-color: currentColor; }
.btn[aria-busy="true"] { cursor: progress; opacity: 0.75; }
.btn[aria-busy="true"]::before {
    content: "";
    width: 1em;
    height: 1em;
    border: 2px solid currentColor;
    border-inline-end-color: transparent;
    border-radius: 50%;
    animation: spin 0.8s linear infinite;
}

/* QR code login: always dark on light, phones often can't scan inverted codes; dimmed in dark mode */
.qr {
    padding: 1.5rem;
    border: 1px solid var(--line);
    border-radius: var(--radius-panel);
    background: var(--surface);
}
.qr h2 { margin: 0 0 1.1rem; font: 650 1.15rem/1.25 var(--font-display); }
.qr-frame {
    position: relative;
    padding: 0.85rem;
    border-radius: 14px;
    background: var(--qr-light);
    aspect-ratio: 1;
    --corner: var(--signal);
    background-image:
        linear-gradient(var(--corner), var(--corner)), linear-gradient(var(--corner), var(--corner)),
        linear-gradient(var(--corner), var(--corner)), linear-gradient(var(--corner), var(--corner)),
        linear-gradient(var(--corner), var(--corner)), linear-gradient(var(--corner), var(--corner)),
        linear-gradient(var(--corner), var(--corner)), linear-gradient(var(--corner), var(--corner));
    background-repeat: no-repeat;
    background-size: 1.6rem 3px, 3px 1.6rem;
    background-position: 0 0, 0 0, 100% 0, 100% 0, 0 100%, 0 100%, 100% 100%, 100% 100%;
}
.qr-code { width: 100%; height: 100%; display: grid; place-items: center; }
.qr-code svg { display: block; width: 100%; height: 100%; }
/* The SVG of the QR code has a white background rect and black modules */
.qr-code svg > rect { fill: var(--qr-light); }
.qr-code svg path, .qr-code svg g { fill: var(--qr-dark); }
.qr-code:empty {
    border-radius: 6px;
    background: repeating-linear-gradient(45deg, color-mix(in srgb, var(--qr-dark) 8%, var(--qr-light)) 0 10px, var(--qr-light) 10px 20px);
}
.qr-status {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    margin: 1rem 0 1.1rem;
    font-size: 0.9375rem;
    font-weight: 600;
    color: var(--ink-soft);
}
.qr-pulse {
    position: relative;
    flex: none;
    width: 0.6rem;
    height: 0.6rem;
    border-radius: 50%;
    background: var(--signal);
}
.qr-pulse::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: 50%;
    background: var(--signal);
    animation: pulse 1.8s ease-out infinite;
}
.qr.is-done { --corner: var(--ok); }
.qr.is-done .qr-status { color: var(--ok); }
.qr.is-done .qr-pulse, .qr.is-done .qr-pulse::after { background: var(--ok); animation: none; }
.qr.is-done .qr-code svg { opacity: 0.25; transition: opacity 0.3s; }
.qr-steps { margin: 0; padding-inline-start: 1.15rem; font-size: 0.875rem; color: var(--ink-soft); }
.qr-steps li + li { margin-top: 0.3rem; }

@keyframes pulse { from { transform: scale(1); opacity: 0.6; } to { transform: scale(3.2); opacity: 0; } }
@keyframes spin { to { transform: rotate(360deg); } }

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after { animation: none !important; transition: none !important; }
}

/* Narrow screens: the rail becomes a top bar, the steps a compact progress line */
@media (max-width: 60rem) {
    .pane-body.has-qr { grid-template-columns: 1fr; max-width: 31rem; }
    .qr { max-width: 21rem; }
}
@media (max-width: 48rem) {
    .shell { grid-template-columns: 1fr; grid-template-rows: auto 1fr; }
    .rail {
        padding: 1rem;
        gap: 1rem;
        flex-direction: row;
        flex-wrap: wrap;
        align-items: center;
        justify-content: space-between;
    }
    .brand-mark svg { width: 1.9rem; height: 1.9rem; }
    .brand-name { font-size: 1.05rem; }
    .rail-foot { display: none; }
    .steps { display: flex; gap: 0.35rem; align-items: center; }
    .step { padding: 0; gap: 0.45rem; }
    .step + .step::before { display: none; }
    .step-label { display: none; }
    .step.is-current .step-label { display: inline; font-size: 0.875rem; }
    .step-dot { width: 1.5rem; height: 1.5rem; font-size: 0.75rem; }
    .pane { align-items: flex-start; padding: 2rem 1rem 3rem; }
    .form-actions .btn-primary { width: 100%; }
    .qr { max-width: none; }
}
var MadelineProtoWebUI=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var t=Object.create,n=Object.defineProperty,r=Object.getOwnPropertyDescriptor,i=Object.getOwnPropertyNames,a=Object.getPrototypeOf,o=Object.prototype.hasOwnProperty,s=(e,t)=>()=>(t||(e((t={exports:{}}).exports,t),e=null),t.exports),c=(e,t,a,s)=>{if(t&&typeof t==`object`||typeof t==`function`)for(var c=i(t),l=0,u=c.length,d;l<u;l++)d=c[l],!o.call(e,d)&&d!==a&&n(e,d,{get:(e=>t[e]).bind(null,d),enumerable:!(s=r(t,d))||s.enumerable});return e},l=(e,r,i)=>(i=e==null?{}:t(a(e)),c(r||!e||!e.__esModule||!o.call(e,`default`)?n(i,`default`,{value:e,enumerable:!0}):i,e)),u={context:void 0,registry:void 0,effects:void 0,done:!1,getContextId(){return d(this.context.count)},getNextContextId(){return d(this.context.count++)}};function d(e){let t=String(e),n=t.length-1;return u.context.id+(n?String.fromCharCode(96+n):``)+t}function f(e){u.context=e}var p=(e,t)=>e===t,m=Symbol(`solid-proxy`),h=typeof Proxy==`function`,g=Symbol(`solid-track`),_={equals:p},v=null,y=ge,b=1,x=2,S={owned:null,cleanups:null,context:null,owner:null},C=null,w=null,T=null,E=null,D=null,O=0;function k(e,t){let n=T,r=C,i=e.length===0,a=t===void 0?r:t,o=i?S:{owned:null,cleanups:null,context:a?a.context:null,owner:a},s=i?e:()=>e(()=>P(()=>be(o)));C=o,T=null;try{return R(s,!0)}finally{T=n,C=r}}function A(e,t){t=t?Object.assign({},_,t):_;let n={value:e,observers:null,observerSlots:null,comparator:t.equals||void 0};return[de.bind(n),e=>(typeof e==`function`&&(e=w&&w.running&&w.sources.has(n)?e(n.tValue):e(n.value)),fe(n,e))]}function ee(e,t,n){pe(me(e,t,!0,b))}function j(e,t,n){pe(me(e,t,!1,b))}function M(e,t,n){y=_e;let r=me(e,t,!1,b),i=ue&&ce(ue);i&&(r.suspense=i),(!n||!n.render)&&(r.user=!0),D?D.push(r):pe(r)}function N(e,t,n){n=n?Object.assign({},_,n):_;let r=me(e,t,!0,0);return r.observers=null,r.observerSlots=null,r.comparator=n.equals||void 0,pe(r),de.bind(r)}function te(e){return R(e,!1)}function P(e){if(T===null)return e();let t=T;T=null;try{return e()}finally{T=t}}function F(e){M(()=>P(e))}function ne(e){return C===null||(C.cleanups===null?C.cleanups=[e]:C.cleanups.push(e)),e}function re(e){if(w&&w.running)return e(),w.done;let t=T,n=C;return Promise.resolve().then(()=>{T=t,C=n;let r;return ue&&(r=w||(w={sources:new Set,effects:[],promises:new Set,disposed:new Set,queue:new Set,running:!0}),r.done||(r.done=new Promise(e=>r.resolve=e)),r.running=!0),R(e,!1),T=C=null,r?r.done:void 0})}var[ie,ae]=A(!1);function oe(){return[ie,re]}function se(e,t){let n=Symbol(`context`);return{id:n,Provider:we(n),defaultValue:e}}function ce(e){let t;return C&&C.context&&(t=C.context[e.id])!==void 0?t:e.defaultValue}function le(e){let t=N(e),n=N(()=>Ce(t()));return n.toArray=()=>{let e=n();return Array.isArray(e)?e:e==null?[]:[e]},n}var ue;function de(){let e=w&&w.running;if(this.sources&&(e?this.tState:this.state)){if((e?this.tState:this.state)===b)pe(this);else{let e=E;E=null,R(()=>ve(this),!1),E=e}}if(T){let e=this.observers;if(!e||e[e.length-1]!==T){let t=e?e.length:0;T.sources?(T.sources.push(this),T.sourceSlots.push(t)):(T.sources=[this],T.sourceSlots=[t]),e?(e.push(T),this.observerSlots.push(T.sources.length-1)):(this.observers=[T],this.observerSlots=[T.sources.length-1])}}return e&&w.sources.has(this)?this.tValue:this.value}function fe(e,t,n){let r=w&&w.running&&w.sources.has(e)?e.tValue:e.value;if(!e.comparator||!e.comparator(r,t)){if(w){let r=w.running;(r||!n&&w.sources.has(e))&&(w.sources.add(e),e.tValue=t),r||(e.value=t)}else e.value=t;e.observers&&e.observers.length&&R(()=>{for(let t=0;t<e.observers.length;t+=1){let n=e.observers[t],r=w&&w.running;r&&w.disposed.has(n)||((r?!n.tState:!n.state)&&(n.pure?E.push(n):D.push(n),n.observers&&ye(n)),r?n.tState=b:n.state=b)}if(E.length>1e6)throw E=[],Error()},!1)}return t}function pe(e){if(!e.fn)return;be(e);let t=O;I(e,w&&w.running&&w.sources.has(e)?e.tValue:e.value,t),w&&!w.running&&w.sources.has(e)&&queueMicrotask(()=>{R(()=>{w&&(w.running=!0),T=C=e,I(e,e.tValue,t),T=C=null},!1)})}function I(e,t,n){let r,i=C,a=T;T=C=e;try{r=e.fn(t)}catch(t){return e.pure&&(w&&w.running?(e.tState=b,e.tOwned&&e.tOwned.forEach(be),e.tOwned=void 0):(e.state=b,e.owned&&e.owned.forEach(be),e.owned=null)),e.updatedAt=n+1,B(t)}finally{T=a,C=i}(!e.updatedAt||e.updatedAt<=n)&&(e.updatedAt!=null&&`observers`in e?fe(e,r,!0):w&&w.running&&e.pure?(w.sources.has(e)||(e.value=r),w.sources.add(e),e.tValue=r):e.value=r,e.updatedAt=n)}function me(e,t,n,r=b,i){let a={fn:e,state:r,updatedAt:null,owned:null,sources:null,sourceSlots:null,cleanups:null,value:t,owner:C,context:C?C.context:null,pure:n};return w&&w.running&&(a.state=0,a.tState=r),C===null||C!==S&&(w&&w.running&&C.pure?C.tOwned?C.tOwned.push(a):C.tOwned=[a]:C.owned?C.owned.push(a):C.owned=[a]),a}function L(e){let t=w&&w.running;if((t?e.tState:e.state)===0)return;if((t?e.tState:e.state)===x)return ve(e);if(e.suspense&&P(e.suspense.inFallback))return e.suspense.effects.push(e);let n=[e];for(;(e=e.owner)&&(!e.updatedAt||e.updatedAt<O);){if(t&&w.disposed.has(e))return;(t?e.tState:e.state)&&n.push(e)}for(let r=n.length-1;r>=0;r--){if(e=n[r],t){let t=e,i=n[r+1];for(;(t=t.owner)&&t!==i;)if(w.disposed.has(t))return}if((t?e.tState:e.state)===b)pe(e);else if((t?e.tState:e.state)===x){let t=E;E=null,R(()=>ve(e,n[0]),!1),E=t}}}function R(e,t){if(E)return e();let n=!1;t||(E=[]),D?n=!0:D=[],O++;try{let t=e();return he(n),t}catch(e){n||(D=null),E=null,B(e)}}function he(e){if(E&&(ge(E),E=null),e)return;let t;if(w){if(!w.promises.size&&!w.queue.size){let e=w.sources,n=w.disposed;D.push.apply(D,w.effects),t=w.resolve;for(let e of D)`tState`in e&&(e.state=e.tState),delete e.tState;w=null,R(()=>{for(let e of n)be(e);for(let t of e){if(t.value=t.tValue,t.owned)for(let e=0,n=t.owned.length;e<n;e++)be(t.owned[e]);t.tOwned&&(t.owned=t.tOwned),delete t.tValue,delete t.tOwned,t.tState=0}ae(!1)},!1)}else if(w.running){w.running=!1,w.effects.push.apply(w.effects,D),D=null,ae(!0);return}}let n=D;D=null,n.length&&R(()=>y(n),!1),t&&t()}function ge(e){for(let t=0;t<e.length;t++)L(e[t])}function _e(e){let t,n=0;for(t=0;t<e.length;t++){let r=e[t];r.user?e[n++]=r:L(r)}if(u.context){if(u.count){u.effects||(u.effects=[]),u.effects.push(...e.slice(0,n));return}f()}for(u.effects&&(u.done||!u.count)&&(e=[...u.effects,...e],n+=u.effects.length,delete u.effects),t=0;t<n;t++)L(e[t])}function ve(e,t){let n=w&&w.running;n?e.tState=0:e.state=0;for(let r=0;r<e.sources.length;r+=1){let i=e.sources[r];if(i.sources){let e=n?i.tState:i.state;e===b?i!==t&&(!i.updatedAt||i.updatedAt<O)&&L(i):e===x&&ve(i,t)}}}function ye(e){let t=w&&w.running;for(let n=0;n<e.observers.length;n+=1){let r=e.observers[n];(t?!r.tState:!r.state)&&(t?r.tState=x:r.state=x,r.pure?E.push(r):D.push(r),r.observers&&ye(r))}}function be(e){let t;if(e.sources)for(;e.sources.length;){let t=e.sources.pop(),n=e.sourceSlots.pop(),r=t.observers;if(r&&r.length){let e=r.pop(),i=t.observerSlots.pop();n<r.length&&(e.sourceSlots[i]=n,r[n]=e,t.observerSlots[n]=i)}}if(e.tOwned){for(t=e.tOwned.length-1;t>=0;t--)be(e.tOwned[t]);delete e.tOwned}if(w&&w.running&&e.pure)xe(e,!0);else if(e.owned){for(t=e.owned.length-1;t>=0;t--)be(e.owned[t]);e.owned=null}if(e.cleanups){for(t=e.cleanups.length-1;t>=0;t--)e.cleanups[t]();e.cleanups=null}w&&w.running?e.tState=0:e.state=0}function xe(e,t){if(t||(e.tState=0,w.disposed.add(e)),e.owned)for(let t=0;t<e.owned.length;t++)xe(e.owned[t])}function Se(e){return e instanceof Error?e:Error(typeof e==`string`?e:`Unknown error`,{cause:e})}function z(e,t,n){try{for(let n of t)n(e)}catch(e){B(e,n&&n.owner||null)}}function B(e,t=C){let n=v&&t&&t.context&&t.context[v],r=Se(e);if(!n)throw r;D?D.push({fn(){z(r,n,t)},state:b}):z(r,n,t)}function Ce(e){if(typeof e==`function`&&!e.length)return Ce(e());if(Array.isArray(e)){let t=[];for(let n=0;n<e.length;n++){let r=Ce(e[n]);if(Array.isArray(r)){if(r.length<32768)t.push.apply(t,r);else for(let e=0;e<r.length;e++)t.push(r[e])}else t.push(r)}return t}return e}function we(e,t){return function(t){let n;return j(()=>n=P(()=>(C.context={...C.context,[e]:t.value},le(()=>t.children))),void 0),n}}var Te=Symbol(`fallback`);function Ee(e){for(let t=0;t<e.length;t++)e[t]()}function De(e,t,n={}){let r=[],i=[],a=[],o=0,s=t.length>1?[]:null;return ne(()=>Ee(a)),()=>{let c=e()||[],l=c.length,u,d;return c[g],P(()=>{let e,t,p,m,h,g,_,v,y;if(l===0)o!==0&&(Ee(a),a=[],r=[],i=[],o=0,s&&(s=[])),n.fallback&&(r=[Te],i[0]=k(e=>(a[0]=e,n.fallback())),o=1);else if(o===0){for(i=Array(l),d=0;d<l;d++)r[d]=c[d],i[d]=k(f);o=l}else{for(p=Array(l),m=Array(l),s&&(h=Array(l)),g=0,_=Math.min(o,l);g<_&&r[g]===c[g];g++);for(_=o-1,v=l-1;_>=g&&v>=g&&r[_]===c[v];_--,v--)p[v]=i[_],m[v]=a[_],s&&(h[v]=s[_]);for(e=new Map,t=Array(v+1),d=v;d>=g;d--)y=c[d],u=e.get(y),t[d]=u===void 0?-1:u,e.set(y,d);for(u=g;u<=_;u++)y=r[u],d=e.get(y),d!==void 0&&d!==-1?(p[d]=i[u],m[d]=a[u],s&&(h[d]=s[u]),d=t[d],e.set(y,d)):a[u]();for(d=g;d<l;d++)d in p?(i[d]=p[d],a[d]=m[d],s&&(s[d]=h[d],s[d](d))):i[d]=k(f);i=i.slice(0,o=l),r=c.slice(0)}return i});function f(e){if(a[d]=e,s){let[e,n]=A(d);return s[d]=n,t(c[d],e)}return t(c[d])}}}function Oe(e,t,n={}){let r=[],i=[],a=[],o=[],s=0,c;return ne(()=>Ee(a)),()=>{let l=e()||[],u=l.length;return l[g],P(()=>{if(u===0)return s!==0&&(Ee(a),a=[],r=[],i=[],s=0,o=[]),n.fallback&&(r=[Te],i[0]=k(e=>(a[0]=e,n.fallback())),s=1),i;for(r[0]===Te&&(a[0](),a=[],r=[],i=[],s=0),c=0;c<u;c++)c<r.length&&r[c]!==l[c]?o[c](()=>l[c]):c>=r.length&&(i[c]=k(d));for(;c<r.length;c++)a[c]();return s=o.length=a.length=u,r=l.slice(0),i=i.slice(0,s)});function d(e){a[c]=e;let[n,r]=A(l[c]);return o[c]=r,t(n,c)}}}function V(e,t){return P(()=>e(t||{}))}function ke(){return!0}var Ae={get(e,t,n){return t===m?n:e.get(t)},has(e,t){return t===m||e.has(t)},set:ke,deleteProperty:ke,getOwnPropertyDescriptor(e,t){return{configurable:!0,enumerable:!0,get(){return e.get(t)},set:ke,deleteProperty:ke}},ownKeys(e){return e.keys()}};function je(e){return(e=typeof e==`function`?e():e)?e:{}}function Me(){for(let e=0,t=this.length;e<t;++e){let t=this[e]();if(t!==void 0)return t}}function Ne(...e){let t=!1;for(let n=0;n<e.length;n++){let r=e[n];t=t||!!r&&m in r,e[n]=typeof r==`function`?(t=!0,N(r)):r}if(h&&t)return new Proxy({get(t){for(let n=e.length-1;n>=0;n--){let r=je(e[n])[t];if(r!==void 0)return r}},has(t){for(let n=e.length-1;n>=0;n--)if(t in je(e[n]))return!0;return!1},keys(){let t=[];for(let n=0;n<e.length;n++)t.push(...Object.keys(je(e[n])));return[...new Set(t)]}},Ae);let n={},r=Object.create(null);for(let t=e.length-1;t>=0;t--){let i=e[t];if(!i)continue;let a=Object.getOwnPropertyNames(i);for(let e=a.length-1;e>=0;e--){let t=a[e];if(t===`__proto__`||t===`constructor`)continue;let o=Object.getOwnPropertyDescriptor(i,t);if(!r[t])r[t]=o.get?{enumerable:!0,configurable:!0,get:Me.bind(n[t]=[o.get.bind(i)])}:o.value===void 0?void 0:o;else{let e=n[t];e&&(o.get?e.push(o.get.bind(i)):o.value!==void 0&&e.push(()=>o.value))}}}let i={},a=Object.keys(r);for(let e=a.length-1;e>=0;e--){let t=a[e],n=r[t];n&&n.get?Object.defineProperty(i,t,n):i[t]=n?n.value:void 0}return i}function Pe(e,...t){let n=t.length;if(h&&m in e){let r=n>1?t.flat():t[0],i=new Set,a=t.map(t=>{let n=t.filter(e=>!i.has(e)&&(i.add(e),!0));return new Proxy({get(t){return n.includes(t)?e[t]:void 0},has(t){return n.includes(t)&&t in e},keys(){return n.filter(t=>t in e)}},Ae)});return a.push(new Proxy({get(t){return r.includes(t)?void 0:e[t]},has(t){return!r.includes(t)&&t in e},keys(){return Object.keys(e).filter(e=>!r.includes(e))}},Ae)),a}let r=[];for(let e=0;e<=n;e++)r[e]={};for(let i of Object.getOwnPropertyNames(e)){let a=n;for(let e=0;e<t.length;e++)if(t[e].includes(i)){a=e;break}let o=Object.getOwnPropertyDescriptor(e,i);!o.get&&!o.set&&o.enumerable&&o.writable&&o.configurable?r[a][i]=o.value:Object.defineProperty(r[a],i,o)}return r}var Fe=e=>`Stale read from <${e}>.`;function Ie(e){let t=`fallback`in e&&{fallback:()=>e.fallback};return N(De(()=>e.each,e.children,t||void 0))}function Le(e){let t=`fallback`in e&&{fallback:()=>e.fallback};return N(Oe(()=>e.each,e.children,t||void 0))}function Re(e){let t=e.keyed,n=N(()=>e.when,void 0,void 0),r=t?n:N(n,void 0,{equals:(e,t)=>!e==!t});return N(()=>{let i=r();if(i){let a=e.children;return typeof a==`function`&&a.length>0?P(()=>a(t?i:()=>{if(!P(r))throw Fe(`Show`);return n()})):a}return e.fallback},void 0,void 0)}function ze(e){let t=le(()=>e.children),n=N(()=>{let e=t(),n=Array.isArray(e)?e:[e],r=()=>void 0;for(let e=0;e<n.length;e++){let t=e,i=n[e],a=r,o=N(()=>a()?void 0:i.when,void 0,void 0),s=i.keyed?o:N(o,void 0,{equals:(e,t)=>!e==!t});r=()=>a()||(s()?[t,o,i]:void 0)}return r});return N(()=>{let t=n()();if(!t)return e.fallback;let[r,i,a]=t,o=a.children;return typeof o==`function`&&o.length>0?P(()=>o(a.keyed?i():()=>{if(P(n)()?.[0]!==r)throw Fe(`Match`);return i()})):o},void 0,void 0)}function Be(e){return e}var Ve=new Set([`className`,`value`,`readOnly`,`noValidate`,`formNoValidate`,`isMap`,`noModule`,`playsInline`,`adAuctionHeaders`,`allowFullscreen`,`browsingTopics`,`defaultChecked`,`defaultMuted`,`defaultSelected`,`disablePictureInPicture`,`disableRemotePlayback`,`preservesPitch`,`shadowRootClonable`,`shadowRootCustomElementRegistry`,`shadowRootDelegatesFocus`,`shadowRootSerializable`,`sharedStorageWritable`,...`allowfullscreen.async.alpha.autofocus.autoplay.checked.controls.default.disabled.formnovalidate.hidden.indeterminate.inert.ismap.loop.multiple.muted.nomodule.novalidate.open.playsinline.readonly.required.reversed.seamless.selected.adauctionheaders.browsingtopics.credentialless.defaultchecked.defaultmuted.defaultselected.defer.disablepictureinpicture.disableremoteplayback.preservespitch.shadowrootclonable.shadowrootcustomelementregistry.shadowrootdelegatesfocus.shadowrootserializable.sharedstoragewritable`.split(`.`)]),He=new Set([`innerHTML`,`textContent`,`innerText`,`children`]),H=Object.assign(Object.create(null),{className:`class`,htmlFor:`for`}),Ue=Object.assign(Object.create(null),{class:`className`,novalidate:{$:`noValidate`,FORM:1},formnovalidate:{$:`formNoValidate`,BUTTON:1,INPUT:1},ismap:{$:`isMap`,IMG:1},nomodule:{$:`noModule`,SCRIPT:1},playsinline:{$:`playsInline`,VIDEO:1},readonly:{$:`readOnly`,INPUT:1,TEXTAREA:1},adauctionheaders:{$:`adAuctionHeaders`,IFRAME:1},allowfullscreen:{$:`allowFullscreen`,IFRAME:1},browsingtopics:{$:`browsingTopics`,IMG:1},defaultchecked:{$:`defaultChecked`,INPUT:1},defaultmuted:{$:`defaultMuted`,AUDIO:1,VIDEO:1},defaultselected:{$:`defaultSelected`,OPTION:1},disablepictureinpicture:{$:`disablePictureInPicture`,VIDEO:1},disableremoteplayback:{$:`disableRemotePlayback`,AUDIO:1,VIDEO:1},preservespitch:{$:`preservesPitch`,AUDIO:1,VIDEO:1},shadowrootclonable:{$:`shadowRootClonable`,TEMPLATE:1},shadowrootdelegatesfocus:{$:`shadowRootDelegatesFocus`,TEMPLATE:1},shadowrootserializable:{$:`shadowRootSerializable`,TEMPLATE:1},sharedstoragewritable:{$:`sharedStorageWritable`,IFRAME:1,IMG:1}});function We(e,t){let n=Ue[e];return typeof n==`object`?n[t]?n.$:void 0:n}var Ge=new Set([`beforeinput`,`click`,`dblclick`,`contextmenu`,`focusin`,`focusout`,`input`,`keydown`,`keyup`,`mousedown`,`mousemove`,`mouseout`,`mouseover`,`mouseup`,`pointerdown`,`pointermove`,`pointerout`,`pointerover`,`pointerup`,`touchend`,`touchmove`,`touchstart`]),Ke=new Set(`altGlyph.altGlyphDef.altGlyphItem.animate.animateColor.animateMotion.animateTransform.circle.clipPath.color-profile.cursor.defs.desc.ellipse.feBlend.feColorMatrix.feComponentTransfer.feComposite.feConvolveMatrix.feDiffuseLighting.feDisplacementMap.feDistantLight.feDropShadow.feFlood.feFuncA.feFuncB.feFuncG.feFuncR.feGaussianBlur.feImage.feMerge.feMergeNode.feMorphology.feOffset.fePointLight.feSpecularLighting.feSpotLight.feTile.feTurbulence.filter.font.font-face.font-face-format.font-face-name.font-face-src.font-face-uri.foreignObject.g.glyph.glyphRef.hkern.image.line.linearGradient.marker.mask.metadata.missing-glyph.mpath.path.pattern.polygon.polyline.radialGradient.rect.set.stop.svg.switch.symbol.text.textPath.tref.tspan.use.view.vkern`.split(`.`)),qe={xlink:`http://www.w3.org/1999/xlink`,xml:`http://www.w3.org/XML/1998/namespace`},U=e=>N(()=>e());function Je(e,t,n){let r=n.length,i=t.length,a=r,o=0,s=0,c=t[i-1].nextSibling,l=null;for(;o<i||s<a;){if(t[o]===n[s]){o++,s++;continue}for(;t[i-1]===n[a-1];)i--,a--;if(i===o){let t=a<r?s?n[s-1].nextSibling:n[a-s]:c;for(;s<a;)e.insertBefore(n[s++],t)}else if(a===s)for(;o<i;)(!l||!l.has(t[o]))&&t[o].remove(),o++;else if(t[o]===n[a-1]&&n[s]===t[i-1]){let r=t[--i].nextSibling;e.insertBefore(n[s++],t[o++].nextSibling),e.insertBefore(n[--a],r),t[i]=n[a]}else{if(!l){l=new Map;let e=s;for(;e<a;)l.set(n[e],e++)}let r=l.get(t[o]);if(r!=null){if(s<r&&r<a){let c=o,u=1,d;for(;++c<i&&c<a&&(d=l.get(t[c]))!=null&&d===r+u;)u++;if(u>r-s){let i=t[o];for(;s<r;)e.insertBefore(n[s++],i)}else e.replaceChild(n[s++],t[o++])}else o++}else t[o++].remove()}}}var Ye=`_$DX_DELEGATE`;function Xe(e,t,n,r={}){let i;return k(r=>{i=r,t===document?e():K(t,e(),!n&&t.firstChild?null:void 0,n)},r.owner),()=>{i(),t.textContent=``}}function W(e,t,n,r){let i,a=()=>{let t=r?document.createElementNS(`http://www.w3.org/1998/Math/MathML`,`template`):document.createElement(`template`);return t.innerHTML=e,n?t.content.firstChild.firstChild:r?t.firstChild:t.content.firstChild},o=t?()=>P(()=>document.importNode(i||(i=a()),!0)):()=>(i||(i=a())).cloneNode(!0);return o.cloneNode=o,o}function Ze(e,t=window.document){let n=t[Ye]||(t[Ye]=new Set);for(let r=0,i=e.length;r<i;r++){let i=e[r];n.has(i)||(n.add(i),t.addEventListener(i,pt))}}function Qe(e,t,n){lt(e)||(n==null?e.removeAttribute(t):e.setAttribute(t,n))}function $e(e,t,n,r){lt(e)||(r==null?e.removeAttributeNS(t,n):e.setAttributeNS(t,n,r))}function et(e,t,n){lt(e)||(n?e.setAttribute(t,``):e.removeAttribute(t))}function G(e,t){lt(e)||(t==null?e.removeAttribute(`class`):e.className=t)}function tt(e,t,n,r){if(r)Array.isArray(n)?(e[`$$${t}`]=n[0],e[`$$${t}Data`]=n[1]):e[`$$${t}`]=n;else if(Array.isArray(n)){let r=n[0];e.addEventListener(t,n[0]=t=>r.call(e,n[1],t))}else e.addEventListener(t,n,typeof n!=`function`&&n)}function nt(e,t,n={}){let r=Object.keys(t||{}),i=Object.keys(n),a,o;for(a=0,o=i.length;a<o;a++){let r=i[a];r&&r!==`undefined`&&!t[r]&&(dt(e,r,!1),delete n[r])}for(a=0,o=r.length;a<o;a++){let i=r[a],o=!!t[i];i&&i!==`undefined`&&n[i]!==o&&o&&(dt(e,i,!0),n[i]=o)}return n}function rt(e,t,n){if(!t)return n?Qe(e,`style`):t;let r=e.style;if(typeof t==`string`)return r.cssText=t;typeof n==`string`&&(r.cssText=n=void 0),n||(n={}),t||(t={});let i,a;for(a in n)t[a]??r.removeProperty(a),delete n[a];for(a in t)i=t[a],i!==n[a]&&(r.setProperty(a,i),n[a]=i);return n}function it(e,t,n){n==null?e.style.removeProperty(t):e.style.setProperty(t,n)}function at(e,t={},n,r){let i={};return r||j(()=>i.children=mt(e,t.children,i.children)),j(()=>typeof t.ref==`function`&&ot(t.ref,e)),j(()=>st(e,t,n,!0,i,!0)),i}function ot(e,t,n){return P(()=>e(t,n))}function K(e,t,n,r){if(n!==void 0&&!r&&(r=[]),typeof t!=`function`)return mt(e,t,r,n);j(r=>mt(e,t(),r,n),r)}function st(e,t,n,r,i={},a=!1){t||(t={});for(let r in i)if(!(r in t)){if(r===`children`)continue;i[r]=ft(e,r,null,i[r],n,a,t)}for(let o in t){if(o===`children`){r||mt(e,t.children);continue}let s=t[o];i[o]=ft(e,o,s,i[o],n,a,t)}}function ct(e){let t,n;return!lt()||!(t=u.registry.get(n=vt()))?e():(u.completed&&u.completed.add(t),u.registry.delete(n),t)}function lt(e){return!!u.context&&!u.done&&(!e||e.isConnected)}function ut(e){return e.toLowerCase().replace(/-([a-z])/g,(e,t)=>t.toUpperCase())}function dt(e,t,n){let r=t.trim().split(/\s+/);for(let t=0,i=r.length;t<i;t++)e.classList.toggle(r[t],n)}function ft(e,t,n,r,i,a,o){let s,c,l,u,d;if(t===`style`)return rt(e,n,r);if(t===`classList`)return nt(e,n,r);if(n===r)return r;if(t===`ref`)a||n(e);else if(t.slice(0,3)===`on:`){let i=t.slice(3);r&&e.removeEventListener(i,r,typeof r!=`function`&&r),n&&e.addEventListener(i,n,typeof n!=`function`&&n)}else if(t.slice(0,10)===`oncapture:`){let i=t.slice(10);r&&e.removeEventListener(i,r,!0),n&&e.addEventListener(i,n,!0)}else if(t.slice(0,2)===`on`){let i=t.slice(2).toLowerCase(),a=Ge.has(i);if(!a&&r){let t=Array.isArray(r)?r[0]:r;e.removeEventListener(i,t)}(a||n)&&(tt(e,i,n,a),a&&Ze([i]))}else if(t.slice(0,5)===`attr:`)Qe(e,t.slice(5),n);else if(t.slice(0,5)===`bool:`)et(e,t.slice(5),n);else if((d=t.slice(0,5)===`prop:`)||(l=He.has(t))||!i&&((u=We(t,e.tagName))||(c=Ve.has(t)))||(s=e.nodeName.includes(`-`)||`is`in o)){if(d)t=t.slice(5),c=!0;else if(lt(e))return n;t===`class`||t===`className`?G(e,n):s&&!c&&!l?e[ut(t)]=n:(t===`value`||t===`defaultValue`)&&(e.nodeName===`INPUT`||e.nodeName===`TEXTAREA`)?e[u||t]=n??``:e[u||t]=n}else{let r=i&&t.indexOf(`:`)>-1&&qe[t.split(`:`)[0]];r?$e(e,r,t,n):Qe(e,H[t]||t,n)}return n}function pt(e){if(u.registry&&u.events&&u.events.find(([t,n])=>n===e))return;let t=e.target,n=`$$${e.type}`,r=e.target,i=e.currentTarget,a=t=>Object.defineProperty(e,"target",{configurable:!0,value:t}),o=()=>{let r=t[n];if(r&&!t.disabled){let i=t[`${n}Data`];if(i===void 0?r.call(t,e):r.call(t,i,e),e.cancelBubble)return}return t.host&&typeof t.host!=`string`&&!t.host._$host&&t.contains(e.target)&&a(t.host),!0},s=()=>{for(;o()&&(t=t._$host||t.parentNode||t.host););};Object.defineProperty(e,"currentTarget",{configurable:!0,get(){return t||document}}),u.registry&&!u.done&&(u.done=_$HY.done=!0);try{if(e.composedPath){let n=e.composedPath();a(n[0]);for(let e=0;e<n.length-2&&(t=n[e],o());e++){if(t._$host){t=t._$host,s();break}if(t.parentNode===i)break}}else s();a(r)}finally{e=t=null}}function mt(e,t,n,r,i){let a=lt(e);if(a){!n&&(n=[...e.childNodes]);let t=[];for(let e=0;e<n.length;e++){let r=n[e];r.nodeType===8&&r.data.slice(0,2)===`!$`?r.remove():t.push(r)}n=t}for(;typeof n==`function`;)n=n();if(t===n)return n;let o=typeof t,s=r!==void 0;if(e=s&&n[0]&&n[0].parentNode||e,o===`string`||o===`number`){if(a||o===`number`&&(t=t.toString(),t===n))return n;if(s){let i=n[0];i&&i.nodeType===3?i.data!==t&&(i.data=t):i=document.createTextNode(t),n=_t(e,n,r,i)}else n=n!==``&&typeof n==`string`?e.firstChild.data=t:e.textContent=t}else if(t==null||o===`boolean`){if(a)return n;n=_t(e,n,r)}else if(o===`function`)return j(()=>{let i=t();for(;typeof i==`function`;)i=i();n=mt(e,i,n,r)}),()=>n;else if(Array.isArray(t)){let o=[],c=n&&Array.isArray(n);if(ht(o,t,n,i))return j(()=>n=mt(e,o,n,r,!0)),()=>n;if(a){if(!o.length)return n;if(r===void 0)return n=[...e.childNodes];let t=o[0];if(t.parentNode!==e)return n;let i=[t];for(;(t=t.nextSibling)!==r;)i.push(t);return n=i}if(o.length===0){if(n=_t(e,n,r),s)return n}else c?n.length===0?gt(e,o,r):Je(e,n,o):(n&&_t(e),gt(e,o));n=o}else if(t.nodeType){if(a&&t.parentNode)return n=s?[t]:t;if(Array.isArray(n)){if(s)return n=_t(e,n,r,t);_t(e,n,null,t)}else n==null||n===``||!e.firstChild?e.appendChild(t):e.replaceChild(t,e.firstChild);n=t}return n}function ht(e,t,n,r){let i=!1;for(let a=0,o=t.length;a<o;a++){let o=t[a],s=n&&n[e.length],c;if(o!=null&&o!==!0&&o!==!1){if((c=typeof o)==`object`&&o.nodeType)e.push(o);else if(Array.isArray(o))i=ht(e,o,s)||i;else if(c===`function`){if(r){for(;typeof o==`function`;)o=o();i=ht(e,Array.isArray(o)?o:[o],Array.isArray(s)?s:[s])||i}else e.push(o),i=!0}else{let t=String(o);s&&s.nodeType===3&&s.data===t?e.push(s):e.push(document.createTextNode(t))}}}return i}function gt(e,t,n=null){for(let r=0,i=t.length;r<i;r++)e.insertBefore(t[r],n)}function _t(e,t,n,r){if(n===void 0)return e.textContent=``;let i=r||document.createTextNode(``);if(t.length){let r=!1;for(let a=t.length-1;a>=0;a--){let o=t[a];if(i!==o){let t=o.parentNode===e;!r&&!a?t?e.replaceChild(i,o):e.insertBefore(i,n):t&&o.remove()}else r=!0}}else e.insertBefore(i,n);return[i]}function vt(){return u.getNextContextId()}var yt=`http://www.w3.org/2000/svg`;function bt(e,t=!1,n=void 0){return t?document.createElementNS(yt,e):document.createElement(e,{is:n})}function xt(e,t){let n=N(e);return N(()=>{let e=n();switch(typeof e){case`function`:return P(()=>e(t));case`string`:let n=Ke.has(e),r=u.context?ct():bt(e,n,P(()=>t.is));return at(r,t,n),r}})}function St(e){let[,t]=Pe(e,[`component`]);return xt(()=>e.component,t)}var Ct=`ontouchstart`in window||window.DocumentTouch&&document instanceof DocumentTouch,wt=`madeline-webui-night`;function Tt(){try{let e=window.localStorage.getItem(wt);return e===null?void 0:e===`1`}catch{return}}function Et(){return!!window.matchMedia?.(`(prefers-color-scheme: dark)`).matches}function Dt(){return document.documentElement.classList.contains(`night`)}function Ot(e){document.documentElement.classList.toggle(`night`,e),window.dispatchEvent(new CustomEvent(`madeline-theme-change`))}function kt(){let e=!Dt();Ot(e);try{window.localStorage.setItem(wt,e?`1`:`0`)}catch{}}function At(){Ot(Tt()??Et()),window.matchMedia?.(`(prefers-color-scheme: dark)`).addEventListener?.(`change`,e=>{Tt()===void 0&&Ot(e.matches)})}var jt=l(s(((e,t)=>{typeof document<`u`&&typeof navigator<`u`&&(function(n,r){typeof e==`object`&&t!==void 0?t.exports=r():typeof define==`function`&&define.amd?define(r):(n=typeof globalThis<`u`?globalThis:n||self,n.lottie=r())})(e,(function(){"use strict";var n=``,r=!1,i=-999999,a=function(e){r=!!e},o=function(){return r},s=function(e){n=e},c=function(){return n};function l(e){return document.createElement(e)}function u(e,t){var n,r=e.length,i;for(n=0;n<r;n+=1)for(var a in i=e[n].prototype,i)Object.prototype.hasOwnProperty.call(i,a)&&(t.prototype[a]=i[a])}function d(e){function t(){}return t.prototype=e,t}var f=function(){function e(e){this.audios=[],this.audioFactory=e,this._volume=1,this._isMuted=!1}return e.prototype={addAudio:function(e){this.audios.push(e)},pause:function(){var e,t=this.audios.length;for(e=0;e<t;e+=1)this.audios[e].pause()},resume:function(){var e,t=this.audios.length;for(e=0;e<t;e+=1)this.audios[e].resume()},setRate:function(e){var t,n=this.audios.length;for(t=0;t<n;t+=1)this.audios[t].setRate(e)},createAudio:function(e){return this.audioFactory?this.audioFactory(e):window.Howl?new window.Howl({src:[e]}):{isPlaying:!1,play:function(){this.isPlaying=!0},seek:function(){this.isPlaying=!1},playing:function(){},rate:function(){},setVolume:function(){}}},setAudioFactory:function(e){this.audioFactory=e},setVolume:function(e){this._volume=e,this._updateVolume()},mute:function(){this._isMuted=!0,this._updateVolume()},unmute:function(){this._isMuted=!1,this._updateVolume()},getVolume:function(){return this._volume},_updateVolume:function(){var e,t=this.audios.length;for(e=0;e<t;e+=1)this.audios[e].volume(this._volume*+!this._isMuted)}},function(){return new e}}(),p=function(){function e(e,t){var n=0,r=[],i;switch(e){case`int16`:case`uint8c`:i=1;break;default:i=1.1}for(n=0;n<t;n+=1)r.push(i);return r}function t(t,n){return t===`float32`?new Float32Array(n):t===`int16`?new Int16Array(n):t===`uint8c`?new Uint8ClampedArray(n):e(t,n)}return typeof Uint8ClampedArray==`function`&&typeof Float32Array==`function`?t:e}();function m(e){return Array.apply(null,{length:e})}function h(e){"@babel/helpers - typeof";return h=typeof Symbol==`function`&&typeof Symbol.iterator==`symbol`?function(e){return typeof e}:function(e){return e&&typeof Symbol==`function`&&e.constructor===Symbol&&e!==Symbol.prototype?`symbol`:typeof e},h(e)}var g=!0,_=null,v=null,y=``,b=/^((?!chrome|android).)*safari/i.test(navigator.userAgent),x=Math.pow,S=Math.sqrt,C=Math.floor,w=Math.min,T={};(function(){var e=`abs.acos.acosh.asin.asinh.atan.atanh.atan2.ceil.cbrt.expm1.clz32.cos.cosh.exp.floor.fround.hypot.imul.log.log1p.log2.log10.max.min.pow.random.round.sign.sin.sinh.sqrt.tan.tanh.trunc.E.LN10.LN2.LOG10E.LOG2E.PI.SQRT1_2.SQRT2`.split(`.`),t,n=e.length;for(t=0;t<n;t+=1)T[e[t]]=Math[e[t]]})(),T.random=Math.random,T.abs=function(e){if(h(e)===`object`&&e.length){var t=m(e.length),n,r=e.length;for(n=0;n<r;n+=1)t[n]=Math.abs(e[n]);return t}return Math.abs(e)};var E=150,D=Math.PI/180,O=.5519;function k(e,t,n,r){this.type=e,this.currentTime=t,this.totalTime=n,this.direction=r<0?-1:1}function A(e,t){this.type=e,this.direction=t<0?-1:1}function ee(e,t,n,r){this.type=e,this.currentLoop=n,this.totalLoops=t,this.direction=r<0?-1:1}function j(e,t,n){this.type=e,this.firstFrame=t,this.totalFrames=n}function M(e,t){this.type=e,this.target=t}function N(e,t){this.type=`renderFrameError`,this.nativeError=e,this.currentTime=t}function te(e){this.type=`configError`,this.nativeError=e}var P=function(){var e=0;return function(){return e+=1,y+`__lottie_element_`+e}}();function F(e,t,n){var r,i,a,o=Math.floor(e*6),s=e*6-o,c=n*(1-t),l=n*(1-s*t),u=n*(1-(1-s)*t);switch(o%6){case 0:r=n,i=u,a=c;break;case 1:r=l,i=n,a=c;break;case 2:r=c,i=n,a=u;break;case 3:r=c,i=l,a=n;break;case 4:r=u,i=c,a=n;break;case 5:r=n,i=c,a=l}return[r,i,a]}function ne(e,t,n){var r=Math.max(e,t,n),i=Math.min(e,t,n),a=r-i,o,s=r===0?0:a/r,c=r/255;switch(r){case i:o=0;break;case e:o=t-n+a*(t<n?6:0),o/=6*a;break;case t:o=n-e+a*2,o/=6*a;break;case n:o=e-t+a*4,o/=6*a}return[o,s,c]}function re(e,t){var n=ne(e[0]*255,e[1]*255,e[2]*255);return n[1]+=t,n[1]>1?n[1]=1:n[1]<=0&&(n[1]=0),F(n[0],n[1],n[2])}function ie(e,t){var n=ne(e[0]*255,e[1]*255,e[2]*255);return n[2]+=t,n[2]>1?n[2]=1:n[2]<0&&(n[2]=0),F(n[0],n[1],n[2])}function ae(e,t){var n=ne(e[0]*255,e[1]*255,e[2]*255);return n[0]+=t/360,n[0]>1?--n[0]:n[0]<0&&(n[0]+=1),F(n[0],n[1],n[2])}(function(){for(var e=[],t=0,n;t<256;t+=1)n=t.toString(16),e[t]=n.length===1?`0`+n:n;return function(t,n,r){return t<0&&(t=0),n<0&&(n=0),r<0&&(r=0),`#`+e[t]+e[n]+e[r]}})();var oe=function(e){g=!!e},se=function(){return g},ce=function(e){_=e},le=function(){return _},ue=function(){return v},de=function(e){E=e},fe=function(){return E},pe=function(e){y=e};function I(e){return document.createElementNS(`http://www.w3.org/2000/svg`,e)}function me(e){"@babel/helpers - typeof";return me=typeof Symbol==`function`&&typeof Symbol.iterator==`symbol`?function(e){return typeof e}:function(e){return e&&typeof Symbol==`function`&&e.constructor===Symbol&&e!==Symbol.prototype?`symbol`:typeof e},me(e)}var L=function(){var e=1,t=[],n,r,i={onmessage:function(){},postMessage:function(e){n({data:e})}},a={postMessage:function(e){i.onmessage({data:e})}};function s(e){if(window.Worker&&window.Blob&&o()){var t=new Blob([`var _workerSelf = self; self.onmessage = `,e.toString()],{type:`text/javascript`}),r=URL.createObjectURL(t);return new Worker(r)}return n=e,i}function c(){r||(r=s(function(e){function t(){function e(t,n){var o,s,c=t.length,l,u,d,f;for(s=0;s<c;s+=1)if(o=t[s],`ks`in o&&!o.completed){if(o.completed=!0,o.hasMask){var m=o.masksProperties;for(u=m.length,l=0;l<u;l+=1)if(m[l].pt.k.i)a(m[l].pt.k);else for(f=m[l].pt.k.length,d=0;d<f;d+=1)m[l].pt.k[d].s&&a(m[l].pt.k[d].s[0]),m[l].pt.k[d].e&&a(m[l].pt.k[d].e[0])}o.ty===0?(o.layers=r(o.refId,n),e(o.layers,n)):o.ty===4?i(o.shapes):o.ty===5&&p(o)}}function t(t,n){if(t){var i=0,a=t.length;for(i=0;i<a;i+=1)t[i].t===1&&(t[i].data.layers=r(t[i].data.refId,n),e(t[i].data.layers,n))}}function n(e,t){for(var n=0,r=t.length;n<r;){if(t[n].id===e)return t[n];n+=1}return null}function r(e,t){var r=n(e,t);return r?r.layers.__used?JSON.parse(JSON.stringify(r.layers)):(r.layers.__used=!0,r.layers):null}function i(e){var t,n=e.length,r,o;for(t=n-1;t>=0;--t)if(e[t].ty===`sh`){if(e[t].ks.k.i)a(e[t].ks.k);else for(o=e[t].ks.k.length,r=0;r<o;r+=1)e[t].ks.k[r].s&&a(e[t].ks.k[r].s[0]),e[t].ks.k[r].e&&a(e[t].ks.k[r].e[0])}else e[t].ty===`gr`&&i(e[t].it)}function a(e){var t,n=e.i.length;for(t=0;t<n;t+=1)e.i[t][0]+=e.v[t][0],e.i[t][1]+=e.v[t][1],e.o[t][0]+=e.v[t][0],e.o[t][1]+=e.v[t][1]}function o(e,t){var n=t?t.split(`.`):[100,100,100];return e[0]>n[0]?!0:n[0]>e[0]?!1:e[1]>n[1]?!0:n[1]>e[1]?!1:e[2]>n[2]?!0:n[2]>e[2]?!1:null}var s=function(){var e=[4,4,14];function t(e){var t=e.t.d;e.t.d={k:[{s:t,t:0}]}}function n(e){var n,r=e.length;for(n=0;n<r;n+=1)e[n].ty===5&&t(e[n])}return function(t){if(o(e,t.v)&&(n(t.layers),t.assets)){var r,i=t.assets.length;for(r=0;r<i;r+=1)t.assets[r].layers&&n(t.assets[r].layers)}}}(),c=function(){var e=[4,7,99];return function(t){if(t.chars&&!o(e,t.v)){var n,r=t.chars.length;for(n=0;n<r;n+=1){var a=t.chars[n];a.data&&a.data.shapes&&(i(a.data.shapes),a.data.ip=0,a.data.op=99999,a.data.st=0,a.data.sr=1,a.data.ks={p:{k:[0,0],a:0},s:{k:[100,100],a:0},a:{k:[0,0],a:0},r:{k:0,a:0},o:{k:100,a:0}},t.chars[n].t||(a.data.shapes.push({ty:`no`}),a.data.shapes[0].it.push({p:{k:[0,0],a:0},s:{k:[100,100],a:0},a:{k:[0,0],a:0},r:{k:0,a:0},o:{k:100,a:0},sk:{k:0,a:0},sa:{k:0,a:0},ty:`tr`})))}}}}(),l=function(){var e=[5,7,15];function t(e){var t=e.t.p;typeof t.a==`number`&&(t.a={a:0,k:t.a}),typeof t.p==`number`&&(t.p={a:0,k:t.p}),typeof t.r==`number`&&(t.r={a:0,k:t.r})}function n(e){var n,r=e.length;for(n=0;n<r;n+=1)e[n].ty===5&&t(e[n])}return function(t){if(o(e,t.v)&&(n(t.layers),t.assets)){var r,i=t.assets.length;for(r=0;r<i;r+=1)t.assets[r].layers&&n(t.assets[r].layers)}}}(),u=function(){var e=[4,1,9];function t(e){var n,r=e.length,i,a;for(n=0;n<r;n+=1)if(e[n].ty===`gr`)t(e[n].it);else if(e[n].ty===`fl`||e[n].ty===`st`){if(e[n].c.k&&e[n].c.k[0].i)for(a=e[n].c.k.length,i=0;i<a;i+=1)e[n].c.k[i].s&&(e[n].c.k[i].s[0]/=255,e[n].c.k[i].s[1]/=255,e[n].c.k[i].s[2]/=255,e[n].c.k[i].s[3]/=255),e[n].c.k[i].e&&(e[n].c.k[i].e[0]/=255,e[n].c.k[i].e[1]/=255,e[n].c.k[i].e[2]/=255,e[n].c.k[i].e[3]/=255);else e[n].c.k[0]/=255,e[n].c.k[1]/=255,e[n].c.k[2]/=255,e[n].c.k[3]/=255}}function n(e){var n,r=e.length;for(n=0;n<r;n+=1)e[n].ty===4&&t(e[n].shapes)}return function(t){if(o(e,t.v)&&(n(t.layers),t.assets)){var r,i=t.assets.length;for(r=0;r<i;r+=1)t.assets[r].layers&&n(t.assets[r].layers)}}}(),d=function(){var e=[4,4,18];function t(e){var n,r=e.length,i,a;for(n=r-1;n>=0;--n)if(e[n].ty===`sh`){if(e[n].ks.k.i)e[n].ks.k.c=e[n].closed;else for(a=e[n].ks.k.length,i=0;i<a;i+=1)e[n].ks.k[i].s&&(e[n].ks.k[i].s[0].c=e[n].closed),e[n].ks.k[i].e&&(e[n].ks.k[i].e[0].c=e[n].closed)}else e[n].ty===`gr`&&t(e[n].it)}function n(e){var n,r,i=e.length,a,o,s,c;for(r=0;r<i;r+=1){if(n=e[r],n.hasMask){var l=n.masksProperties;for(o=l.length,a=0;a<o;a+=1)if(l[a].pt.k.i)l[a].pt.k.c=l[a].cl;else for(c=l[a].pt.k.length,s=0;s<c;s+=1)l[a].pt.k[s].s&&(l[a].pt.k[s].s[0].c=l[a].cl),l[a].pt.k[s].e&&(l[a].pt.k[s].e[0].c=l[a].cl)}n.ty===4&&t(n.shapes)}}return function(t){if(o(e,t.v)&&(n(t.layers),t.assets)){var r,i=t.assets.length;for(r=0;r<i;r+=1)t.assets[r].layers&&n(t.assets[r].layers)}}}();function f(n){n.__complete||(u(n),s(n),c(n),l(n),d(n),e(n.layers,n.assets),t(n.chars,n.assets),n.__complete=!0)}function p(e){e.t.a.length===0&&`m`in e.t.p}var m={};return m.completeData=f,m.checkColors=u,m.checkChars=c,m.checkPathProperties=l,m.checkShapes=d,m.completeLayers=e,m}if(a.dataManager||(a.dataManager=t()),a.assetLoader||(a.assetLoader=function(){function e(e){var t=e.getResponseHeader(`content-type`);return t&&e.responseType===`json`&&t.indexOf(`json`)!==-1||e.response&&me(e.response)===`object`?e.response:e.response&&typeof e.response==`string`?JSON.parse(e.response):e.responseText?JSON.parse(e.responseText):null}function t(t,n,r,i){var a,o=new XMLHttpRequest;try{o.responseType=`json`}catch{}o.onreadystatechange=function(){if(o.readyState===4){if(o.status===200)a=e(o),r(a);else try{a=e(o),r(a)}catch(e){i&&i(e)}}};try{o.open([`G`,`E`,`T`].join(``),t,!0)}catch{o.open([`G`,`E`,`T`].join(``),n+`/`+t,!0)}o.send()}return{load:t}}()),e.data.type===`loadAnimation`)a.assetLoader.load(e.data.path,e.data.fullPath,function(t){a.dataManager.completeData(t),a.postMessage({id:e.data.id,payload:t,status:`success`})},function(){a.postMessage({id:e.data.id,status:`error`})});else if(e.data.type===`complete`){var n=e.data.animation;a.dataManager.completeData(n),a.postMessage({id:e.data.id,payload:n,status:`success`})}else e.data.type===`loadData`&&a.assetLoader.load(e.data.path,e.data.fullPath,function(t){a.postMessage({id:e.data.id,payload:t,status:`success`})},function(){a.postMessage({id:e.data.id,status:`error`})})}),r.onmessage=function(e){var n=e.data,r=n.id,i=t[r];t[r]=null,n.status===`success`?i.onComplete(n.payload):i.onError&&i.onError()})}function l(n,r){e+=1;var i=`processId_`+e;return t[i]={onComplete:n,onError:r},i}function u(e,t,n){c();var i=l(t,n);r.postMessage({type:`loadAnimation`,path:e,fullPath:window.location.origin+window.location.pathname,id:i})}function d(e,t,n){c();var i=l(t,n);r.postMessage({type:`loadData`,path:e,fullPath:window.location.origin+window.location.pathname,id:i})}function f(e,t,n){c();var i=l(t,n);r.postMessage({type:`complete`,animation:e,id:i})}return{loadAnimation:u,loadData:d,completeAnimation:f}}(),R=function(){var e=function(){var e=l(`canvas`);e.width=1,e.height=1;var t=e.getContext(`2d`);return t.fillStyle=`rgba(0,0,0,0)`,t.fillRect(0,0,1,1),e}();function t(){this.loadedAssets+=1,this.loadedAssets===this.totalImages&&this.loadedFootagesCount===this.totalFootages&&this.imagesLoadedCb&&this.imagesLoadedCb(null)}function n(){this.loadedFootagesCount+=1,this.loadedAssets===this.totalImages&&this.loadedFootagesCount===this.totalFootages&&this.imagesLoadedCb&&this.imagesLoadedCb(null)}function r(e,t,n){var r=``;if(e.e)r=e.p;else if(t){var i=e.p;i.indexOf(`images/`)!==-1&&(i=i.split(`/`)[1]),r=t+i}else r=n,r+=e.u?e.u:``,r+=e.p;return r}function i(e){var t=0,n=setInterval(function(){(e.getBBox().width||t>500)&&(this._imageLoaded(),clearInterval(n)),t+=1}.bind(this),50)}function a(t){var n=r(t,this.assetsPath,this.path),i=I(`image`);b?this.testImageLoaded(i):i.addEventListener(`load`,this._imageLoaded,!1),i.addEventListener(`error`,function(){a.img=e,this._imageLoaded()}.bind(this),!1),i.setAttributeNS(`http://www.w3.org/1999/xlink`,`href`,n),this._elementHelper.append?this._elementHelper.append(i):this._elementHelper.appendChild(i);var a={img:i,assetData:t};return a}function o(t){var n=r(t,this.assetsPath,this.path),i=l(`img`);i.crossOrigin=`anonymous`,i.addEventListener(`load`,this._imageLoaded,!1),i.addEventListener(`error`,function(){a.img=e,this._imageLoaded()}.bind(this),!1),i.src=n;var a={img:i,assetData:t};return a}function s(e){var t={assetData:e},n=r(e,this.assetsPath,this.path);return L.loadData(n,function(e){t.img=e,this._footageLoaded()}.bind(this),function(){t.img={},this._footageLoaded()}.bind(this)),t}function c(e,t){this.imagesLoadedCb=t;var n,r=e.length;for(n=0;n<r;n+=1)e[n].layers||(!e[n].t||e[n].t===`seq`?(this.totalImages+=1,this.images.push(this._createImageData(e[n]))):e[n].t===3&&(this.totalFootages+=1,this.images.push(this.createFootageData(e[n]))))}function u(e){this.path=e||``}function d(e){this.assetsPath=e||``}function f(e){for(var t=0,n=this.images.length;t<n;){if(this.images[t].assetData===e)return this.images[t].img;t+=1}return null}function p(){this.imagesLoadedCb=null,this.images.length=0}function m(){return this.totalImages===this.loadedAssets}function h(){return this.totalFootages===this.loadedFootagesCount}function g(e,t){e===`svg`?(this._elementHelper=t,this._createImageData=this.createImageData.bind(this)):this._createImageData=this.createImgData.bind(this)}function _(){this._imageLoaded=t.bind(this),this._footageLoaded=n.bind(this),this.testImageLoaded=i.bind(this),this.createFootageData=s.bind(this),this.assetsPath=``,this.path=``,this.totalImages=0,this.totalFootages=0,this.loadedAssets=0,this.loadedFootagesCount=0,this.imagesLoadedCb=null,this.images=[]}return _.prototype={loadAssets:c,setAssetsPath:d,setPath:u,loadedImages:m,loadedFootages:h,destroy:p,getAsset:f,createImgData:o,createImageData:a,imageLoaded:t,footageLoaded:n,setCacheType:g},_}();function he(){}he.prototype={triggerEvent:function(e,t){if(this._cbs[e])for(var n=this._cbs[e],r=0;r<n.length;r+=1)n[r](t)},addEventListener:function(e,t){return this._cbs[e]||(this._cbs[e]=[]),this._cbs[e].push(t),function(){this.removeEventListener(e,t)}.bind(this)},removeEventListener:function(e,t){if(!t)this._cbs[e]=null;else if(this._cbs[e]){for(var n=0,r=this._cbs[e].length;n<r;)this._cbs[e][n]===t&&(this._cbs[e].splice(n,1),--n,--r),n+=1;this._cbs[e].length||(this._cbs[e]=null)}}};var ge=function(){function e(e){for(var t=e.split(`\r
`),n={},r,i=0,a=0;a<t.length;a+=1)r=t[a].split(`:`),r.length===2&&(n[r[0]]=r[1].trim(),i+=1);if(i===0)throw Error();return n}return function(t){for(var n=[],r=0;r<t.length;r+=1){var i=t[r],a={time:i.tm,duration:i.dr};try{a.payload=JSON.parse(t[r].cm)}catch{try{a.payload=e(t[r].cm)}catch{a.payload={name:t[r].cm}}}n.push(a)}return n}}(),_e=function(){function e(e){this.compositions.push(e)}return function(){function t(e){for(var t=0,n=this.compositions.length;t<n;){if(this.compositions[t].data&&this.compositions[t].data.nm===e)return this.compositions[t].prepareFrame&&this.compositions[t].data.xt&&this.compositions[t].prepareFrame(this.currentFrame),this.compositions[t].compInterface;t+=1}return null}return t.compositions=[],t.currentFrame=0,t.registerComposition=e,t}}(),ve={},ye=function(e,t){ve[e]=t};function be(e){return ve[e]}function xe(){if(ve.canvas)return`canvas`;for(var e in ve)if(ve[e])return e;return``}function Se(e){"@babel/helpers - typeof";return Se=typeof Symbol==`function`&&typeof Symbol.iterator==`symbol`?function(e){return typeof e}:function(e){return e&&typeof Symbol==`function`&&e.constructor===Symbol&&e!==Symbol.prototype?`symbol`:typeof e},Se(e)}var z=function(){this._cbs=[],this.name=``,this.path=``,this.isLoaded=!1,this.currentFrame=0,this.currentRawFrame=0,this.firstFrame=0,this.totalFrames=0,this.frameRate=0,this.frameMult=0,this.playSpeed=1,this.playDirection=1,this.playCount=0,this.animationData={},this.assets=[],this.isPaused=!0,this.autoplay=!1,this.loop=!0,this.renderer=null,this.animationID=P(),this.assetsPath=``,this.timeCompleted=0,this.segmentPos=0,this.isSubframeEnabled=se(),this.segments=[],this._idle=!0,this._completedLoop=!1,this.projectInterface=_e(),this.imagePreloader=new R,this.audioController=f(),this.markers=[],this.configAnimation=this.configAnimation.bind(this),this.onSetupError=this.onSetupError.bind(this),this.onSegmentComplete=this.onSegmentComplete.bind(this),this.drawnFrameEvent=new k(`drawnFrame`,0,0,0),this.expressionsPlugin=le()};u([he],z),z.prototype.setParams=function(e){(e.wrapper||e.container)&&(this.wrapper=e.wrapper||e.container);var t=`svg`;e.animType?t=e.animType:e.renderer&&(t=e.renderer);var n=be(t);this.renderer=new n(this,e.rendererSettings),this.imagePreloader.setCacheType(t,this.renderer.globalData.defs),this.renderer.setProjectInterface(this.projectInterface),this.animType=t,this.loop=e.loop===``||e.loop===null||e.loop===void 0||e.loop===!0||e.loop!==!1&&parseInt(e.loop,10),this.autoplay=`autoplay`in e?e.autoplay:!0,this.name=e.name?e.name:``,this.autoloadSegments=!Object.prototype.hasOwnProperty.call(e,`autoloadSegments`)||e.autoloadSegments,this.assetsPath=e.assetsPath,this.initialSegment=e.initialSegment,e.audioFactory&&this.audioController.setAudioFactory(e.audioFactory),e.animationData?this.setupAnimation(e.animationData):e.path&&(this.path=e.path.lastIndexOf(`\\`)===-1?e.path.substr(0,e.path.lastIndexOf(`/`)+1):e.path.substr(0,e.path.lastIndexOf(`\\`)+1),this.fileName=e.path.substr(e.path.lastIndexOf(`/`)+1),this.fileName=this.fileName.substr(0,this.fileName.lastIndexOf(`.json`)),L.loadAnimation(e.path,this.configAnimation,this.onSetupError))},z.prototype.onSetupError=function(){this.trigger(`data_failed`)},z.prototype.setupAnimation=function(e){L.completeAnimation(e,this.configAnimation)},z.prototype.setData=function(e,t){t&&Se(t)!==`object`&&(t=JSON.parse(t));var n={wrapper:e,animationData:t},r=e.attributes;n.path=r.getNamedItem(`data-animation-path`)?r.getNamedItem(`data-animation-path`).value:r.getNamedItem(`data-bm-path`)?r.getNamedItem(`data-bm-path`).value:r.getNamedItem(`bm-path`)?r.getNamedItem(`bm-path`).value:``,n.animType=r.getNamedItem(`data-anim-type`)?r.getNamedItem(`data-anim-type`).value:r.getNamedItem(`data-bm-type`)?r.getNamedItem(`data-bm-type`).value:r.getNamedItem(`bm-type`)?r.getNamedItem(`bm-type`).value:r.getNamedItem(`data-bm-renderer`)?r.getNamedItem(`data-bm-renderer`).value:r.getNamedItem(`bm-renderer`)?r.getNamedItem(`bm-renderer`).value:xe()||`canvas`;var i=r.getNamedItem(`data-anim-loop`)?r.getNamedItem(`data-anim-loop`).value:r.getNamedItem(`data-bm-loop`)?r.getNamedItem(`data-bm-loop`).value:r.getNamedItem(`bm-loop`)?r.getNamedItem(`bm-loop`).value:``;i===`false`?n.loop=!1:i===`true`?n.loop=!0:i!==``&&(n.loop=parseInt(i,10)),n.autoplay=(r.getNamedItem(`data-anim-autoplay`)?r.getNamedItem(`data-anim-autoplay`).value:r.getNamedItem(`data-bm-autoplay`)?r.getNamedItem(`data-bm-autoplay`).value:!r.getNamedItem(`bm-autoplay`)||r.getNamedItem(`bm-autoplay`).value)!==`false`,n.name=r.getNamedItem(`data-name`)?r.getNamedItem(`data-name`).value:r.getNamedItem(`data-bm-name`)?r.getNamedItem(`data-bm-name`).value:r.getNamedItem(`bm-name`)?r.getNamedItem(`bm-name`).value:``,(r.getNamedItem(`data-anim-prerender`)?r.getNamedItem(`data-anim-prerender`).value:r.getNamedItem(`data-bm-prerender`)?r.getNamedItem(`data-bm-prerender`).value:r.getNamedItem(`bm-prerender`)?r.getNamedItem(`bm-prerender`).value:``)===`false`&&(n.prerender=!1),n.path?this.setParams(n):this.trigger(`destroy`)},z.prototype.includeLayers=function(e){e.op>this.animationData.op&&(this.animationData.op=e.op,this.totalFrames=Math.floor(e.op-this.animationData.ip));var t=this.animationData.layers,n,r=t.length,i=e.layers,a,o=i.length;for(a=0;a<o;a+=1)for(n=0;n<r;){if(t[n].id===i[a].id){t[n]=i[a];break}n+=1}if((e.chars||e.fonts)&&(this.renderer.globalData.fontManager.addChars(e.chars),this.renderer.globalData.fontManager.addFonts(e.fonts,this.renderer.globalData.defs)),e.assets)for(r=e.assets.length,n=0;n<r;n+=1)this.animationData.assets.push(e.assets[n]);this.animationData.__complete=!1,L.completeAnimation(this.animationData,this.onSegmentComplete)},z.prototype.onSegmentComplete=function(e){this.animationData=e;var t=le();t&&t.initExpressions(this),this.loadNextSegment()},z.prototype.loadNextSegment=function(){var e=this.animationData.segments;if(!e||e.length===0||!this.autoloadSegments){this.trigger(`data_ready`),this.timeCompleted=this.totalFrames;return}var t=e.shift();this.timeCompleted=t.time*this.frameRate;var n=this.path+this.fileName+`_`+this.segmentPos+`.json`;this.segmentPos+=1,L.loadData(n,this.includeLayers.bind(this),function(){this.trigger(`data_failed`)}.bind(this))},z.prototype.loadSegments=function(){this.animationData.segments||(this.timeCompleted=this.totalFrames),this.loadNextSegment()},z.prototype.imagesLoaded=function(){this.trigger(`loaded_images`),this.checkLoaded()},z.prototype.preloadImages=function(){this.imagePreloader.setAssetsPath(this.assetsPath),this.imagePreloader.setPath(this.path),this.imagePreloader.loadAssets(this.animationData.assets,this.imagesLoaded.bind(this))},z.prototype.configAnimation=function(e){if(this.renderer)try{this.animationData=e,this.initialSegment?(this.totalFrames=Math.floor(this.initialSegment[1]-this.initialSegment[0]),this.firstFrame=Math.round(this.initialSegment[0])):(this.totalFrames=Math.floor(this.animationData.op-this.animationData.ip),this.firstFrame=Math.round(this.animationData.ip)),this.renderer.configAnimation(e),e.assets||(e.assets=[]),this.assets=this.animationData.assets,this.frameRate=this.animationData.fr,this.frameMult=this.animationData.fr/1e3,this.renderer.searchExtraCompositions(e.assets),this.markers=ge(e.markers||[]),this.trigger(`config_ready`),this.preloadImages(),this.loadSegments(),this.updaFrameModifier(),this.waitForFontsLoaded(),this.isPaused&&this.audioController.pause()}catch(e){this.triggerConfigError(e)}},z.prototype.waitForFontsLoaded=function(){this.renderer&&(this.renderer.globalData.fontManager.isLoaded?this.checkLoaded():setTimeout(this.waitForFontsLoaded.bind(this),20))},z.prototype.checkLoaded=function(){if(!this.isLoaded&&this.renderer.globalData.fontManager.isLoaded&&(this.imagePreloader.loadedImages()||this.renderer.rendererType!==`canvas`)&&this.imagePreloader.loadedFootages()){this.isLoaded=!0;var e=le();e&&e.initExpressions(this),this.renderer.initItems(),setTimeout(function(){this.trigger(`DOMLoaded`)}.bind(this),0),this.gotoFrame(),this.autoplay&&this.play()}},z.prototype.resize=function(e,t){var n=typeof e==`number`?e:void 0,r=typeof t==`number`?t:void 0;this.renderer.updateContainerSize(n,r)},z.prototype.setSubframe=function(e){this.isSubframeEnabled=!!e},z.prototype.gotoFrame=function(){this.currentFrame=this.isSubframeEnabled?this.currentRawFrame:~~this.currentRawFrame,this.timeCompleted!==this.totalFrames&&this.currentFrame>this.timeCompleted&&(this.currentFrame=this.timeCompleted),this.trigger(`enterFrame`),this.renderFrame(),this.trigger(`drawnFrame`)},z.prototype.renderFrame=function(){if(this.isLoaded!==!1&&this.renderer)try{this.expressionsPlugin&&this.expressionsPlugin.resetFrame(),this.renderer.renderFrame(this.currentFrame+this.firstFrame)}catch(e){this.triggerRenderFrameError(e)}},z.prototype.play=function(e){e&&this.name!==e||this.isPaused===!0&&(this.isPaused=!1,this.trigger(`_play`),this.audioController.resume(),this._idle&&(this._idle=!1,this.trigger(`_active`)))},z.prototype.pause=function(e){e&&this.name!==e||this.isPaused===!1&&(this.isPaused=!0,this.trigger(`_pause`),this._idle=!0,this.trigger(`_idle`),this.audioController.pause())},z.prototype.togglePause=function(e){e&&this.name!==e||(this.isPaused===!0?this.play():this.pause())},z.prototype.stop=function(e){e&&this.name!==e||(this.pause(),this.playCount=0,this._completedLoop=!1,this.setCurrentRawFrameValue(0))},z.prototype.getMarkerData=function(e){for(var t,n=0;n<this.markers.length;n+=1)if(t=this.markers[n],t.payload&&t.payload.name===e)return t;return null},z.prototype.goToAndStop=function(e,t,n){if(!(n&&this.name!==n)){var r=Number(e);if(isNaN(r)){var i=this.getMarkerData(e);i&&this.goToAndStop(i.time,!0)}else t?this.setCurrentRawFrameValue(e):this.setCurrentRawFrameValue(e*this.frameModifier);this.pause()}},z.prototype.goToAndPlay=function(e,t,n){if(!(n&&this.name!==n)){var r=Number(e);if(isNaN(r)){var i=this.getMarkerData(e);i&&(i.duration?this.playSegments([i.time,i.time+i.duration],!0):this.goToAndStop(i.time,!0))}else this.goToAndStop(r,t,n);this.play()}},z.prototype.advanceTime=function(e){if(this.isPaused!==!0&&this.isLoaded!==!1){var t=this.currentRawFrame+e*this.frameModifier,n=!1;t>=this.totalFrames-1&&this.frameModifier>0?!this.loop||this.playCount===this.loop?this.checkSegments(t>this.totalFrames?t%this.totalFrames:0)||(n=!0,t=this.totalFrames-1):t>=this.totalFrames?(this.playCount+=1,this.checkSegments(t%this.totalFrames)||(this.setCurrentRawFrameValue(t%this.totalFrames),this._completedLoop=!0,this.trigger(`loopComplete`))):this.setCurrentRawFrameValue(t):t<0?this.checkSegments(t%this.totalFrames)||(this.loop&&!(this.playCount--<=0&&this.loop!==!0)?(this.setCurrentRawFrameValue(this.totalFrames+t%this.totalFrames),this._completedLoop?this.trigger(`loopComplete`):this._completedLoop=!0):(n=!0,t=0)):this.setCurrentRawFrameValue(t),n&&(this.setCurrentRawFrameValue(t),this.pause(),this.trigger(`complete`))}},z.prototype.adjustSegment=function(e,t){this.playCount=0,e[1]<e[0]?(this.frameModifier>0&&(this.playSpeed<0?this.setSpeed(-this.playSpeed):this.setDirection(-1)),this.totalFrames=e[0]-e[1],this.timeCompleted=this.totalFrames,this.firstFrame=e[1],this.setCurrentRawFrameValue(this.totalFrames-.001-t)):e[1]>e[0]&&(this.frameModifier<0&&(this.playSpeed<0?this.setSpeed(-this.playSpeed):this.setDirection(1)),this.totalFrames=e[1]-e[0],this.timeCompleted=this.totalFrames,this.firstFrame=e[0],this.setCurrentRawFrameValue(.001+t)),this.trigger(`segmentStart`)},z.prototype.setSegment=function(e,t){var n=-1;this.isPaused&&(this.currentRawFrame+this.firstFrame<e?n=e:this.currentRawFrame+this.firstFrame>t&&(n=t-e)),this.firstFrame=e,this.totalFrames=t-e,this.timeCompleted=this.totalFrames,n!==-1&&this.goToAndStop(n,!0)},z.prototype.playSegments=function(e,t){if(t&&(this.segments.length=0),Se(e[0])===`object`){var n,r=e.length;for(n=0;n<r;n+=1)this.segments.push(e[n])}else this.segments.push(e);this.segments.length&&t&&this.adjustSegment(this.segments.shift(),0),this.isPaused&&this.play()},z.prototype.resetSegments=function(e){this.segments.length=0,this.segments.push([this.animationData.ip,this.animationData.op]),e&&this.checkSegments(0)},z.prototype.checkSegments=function(e){return this.segments.length?(this.adjustSegment(this.segments.shift(),e),!0):!1},z.prototype.destroy=function(e){e&&this.name!==e||!this.renderer||(this.renderer.destroy(),this.imagePreloader.destroy(),this.trigger(`destroy`),this._cbs=null,this.onEnterFrame=null,this.onLoopComplete=null,this.onComplete=null,this.onSegmentStart=null,this.onDestroy=null,this.renderer=null,this.expressionsPlugin=null,this.imagePreloader=null,this.projectInterface=null)},z.prototype.setCurrentRawFrameValue=function(e){this.currentRawFrame=e,this.gotoFrame()},z.prototype.setSpeed=function(e){this.playSpeed=e,this.updaFrameModifier()},z.prototype.setDirection=function(e){this.playDirection=e<0?-1:1,this.updaFrameModifier()},z.prototype.setLoop=function(e){this.loop=e},z.prototype.setVolume=function(e,t){t&&this.name!==t||this.audioController.setVolume(e)},z.prototype.getVolume=function(){return this.audioController.getVolume()},z.prototype.mute=function(e){e&&this.name!==e||this.audioController.mute()},z.prototype.unmute=function(e){e&&this.name!==e||this.audioController.unmute()},z.prototype.updaFrameModifier=function(){this.frameModifier=this.frameMult*this.playSpeed*this.playDirection,this.audioController.setRate(this.playSpeed*this.playDirection)},z.prototype.getPath=function(){return this.path},z.prototype.getAssetsPath=function(e){var t=``;if(e.e)t=e.p;else if(this.assetsPath){var n=e.p;n.indexOf(`images/`)!==-1&&(n=n.split(`/`)[1]),t=this.assetsPath+n}else t=this.path,t+=e.u?e.u:``,t+=e.p;return t},z.prototype.getAssetData=function(e){for(var t=0,n=this.assets.length;t<n;){if(e===this.assets[t].id)return this.assets[t];t+=1}return null},z.prototype.hide=function(){this.renderer.hide()},z.prototype.show=function(){this.renderer.show()},z.prototype.getDuration=function(e){return e?this.totalFrames:this.totalFrames/this.frameRate},z.prototype.updateDocumentData=function(e,t,n){try{this.renderer.getElementByPath(e).updateDocumentData(t,n)}catch{}},z.prototype.trigger=function(e){if(this._cbs&&this._cbs[e])switch(e){case`enterFrame`:this.triggerEvent(e,new k(e,this.currentFrame,this.totalFrames,this.frameModifier));break;case`drawnFrame`:this.drawnFrameEvent.currentTime=this.currentFrame,this.drawnFrameEvent.totalTime=this.totalFrames,this.drawnFrameEvent.direction=this.frameModifier,this.triggerEvent(e,this.drawnFrameEvent);break;case`loopComplete`:this.triggerEvent(e,new ee(e,this.loop,this.playCount,this.frameMult));break;case`complete`:this.triggerEvent(e,new A(e,this.frameMult));break;case`segmentStart`:this.triggerEvent(e,new j(e,this.firstFrame,this.totalFrames));break;case`destroy`:this.triggerEvent(e,new M(e,this));break;default:this.triggerEvent(e)}e===`enterFrame`&&this.onEnterFrame&&this.onEnterFrame.call(this,new k(e,this.currentFrame,this.totalFrames,this.frameMult)),e===`loopComplete`&&this.onLoopComplete&&this.onLoopComplete.call(this,new ee(e,this.loop,this.playCount,this.frameMult)),e===`complete`&&this.onComplete&&this.onComplete.call(this,new A(e,this.frameMult)),e===`segmentStart`&&this.onSegmentStart&&this.onSegmentStart.call(this,new j(e,this.firstFrame,this.totalFrames)),e===`destroy`&&this.onDestroy&&this.onDestroy.call(this,new M(e,this))},z.prototype.triggerRenderFrameError=function(e){var t=new N(e,this.currentFrame);this.triggerEvent(`error`,t),this.onError&&this.onError.call(this,t)},z.prototype.triggerConfigError=function(e){var t=new te(e,this.currentFrame);this.triggerEvent(`error`,t),this.onError&&this.onError.call(this,t)};var B=function(){var e={},t=[],n=0,r=0,i=0,a=!0,o=!1;function s(e){for(var n=0,i=e.target;n<r;)t[n].animation===i&&(t.splice(n,1),--n,--r,i.isPaused||f()),n+=1}function c(e,n){if(!e)return null;for(var i=0;i<r;){if(t[i].elem===e&&t[i].elem!==null)return t[i].animation;i+=1}var a=new z;return p(a,e),a.setData(e,n),a}function u(){var e,n=t.length,r=[];for(e=0;e<n;e+=1)r.push(t[e].animation);return r}function d(){i+=1,D()}function f(){--i}function p(e,n){e.addEventListener(`destroy`,s),e.addEventListener(`_active`,d),e.addEventListener(`_idle`,f),t.push({elem:n,animation:e}),r+=1}function m(e){var t=new z;return p(t,null),t.setParams(e),t}function h(e,n){for(var i=0;i<r;i+=1)t[i].animation.setSpeed(e,n)}function g(e,n){for(var i=0;i<r;i+=1)t[i].animation.setDirection(e,n)}function _(e){for(var n=0;n<r;n+=1)t[n].animation.play(e)}function v(e){for(var s=e-n,c=0;c<r;c+=1)t[c].animation.advanceTime(s);n=e,i&&!o?window.requestAnimationFrame(v):a=!0}function y(e){n=e,window.requestAnimationFrame(v)}function b(e){for(var n=0;n<r;n+=1)t[n].animation.pause(e)}function x(e,n,i){for(var a=0;a<r;a+=1)t[a].animation.goToAndStop(e,n,i)}function S(e){for(var n=0;n<r;n+=1)t[n].animation.stop(e)}function C(e){for(var n=0;n<r;n+=1)t[n].animation.togglePause(e)}function w(e){for(var n=r-1;n>=0;--n)t[n].animation.destroy(e)}function T(e,t,n){var r=[].concat([].slice.call(document.getElementsByClassName(`lottie`)),[].slice.call(document.getElementsByClassName(`bodymovin`))),i,a=r.length;for(i=0;i<a;i+=1)n&&r[i].setAttribute(`data-bm-type`,n),c(r[i],e);if(t&&a===0){n||(n=`svg`);var o=document.getElementsByTagName(`body`)[0];o.innerText=``;var s=l(`div`);s.style.width=`100%`,s.style.height=`100%`,s.setAttribute(`data-bm-type`,n),o.appendChild(s),c(s,e)}}function E(){for(var e=0;e<r;e+=1)t[e].animation.resize()}function D(){!o&&i&&a&&(window.requestAnimationFrame(y),a=!1)}function O(){o=!0}function k(){o=!1,D()}function A(e,n){for(var i=0;i<r;i+=1)t[i].animation.setVolume(e,n)}function ee(e){for(var n=0;n<r;n+=1)t[n].animation.mute(e)}function j(e){for(var n=0;n<r;n+=1)t[n].animation.unmute(e)}return e.registerAnimation=c,e.loadAnimation=m,e.setSpeed=h,e.setDirection=g,e.play=_,e.pause=b,e.stop=S,e.togglePause=C,e.searchAnimations=T,e.resize=E,e.goToAndStop=x,e.destroy=w,e.freeze=O,e.unfreeze=k,e.setVolume=A,e.mute=ee,e.unmute=j,e.getRegisteredAnimations=u,e}(),Ce=function(){var e={};e.getBezierEasing=n;var t={};function n(e,n,r,i,a){var o=a||(`bez_`+e+`_`+n+`_`+r+`_`+i).replace(/\./g,`p`);if(t[o])return t[o];var s=new _([e,n,r,i]);return t[o]=s,s}var r=4,i=.001,a=1e-7,o=10,s=11,c=1/(s-1),l=typeof Float32Array==`function`;function u(e,t){return 1-3*t+3*e}function d(e,t){return 3*t-6*e}function f(e){return 3*e}function p(e,t,n){return((u(t,n)*e+d(t,n))*e+f(t))*e}function m(e,t,n){return 3*u(t,n)*e*e+2*d(t,n)*e+f(t)}function h(e,t,n,r,i){var s,c,l=0;do c=t+(n-t)/2,s=p(c,r,i)-e,s>0?n=c:t=c;while(Math.abs(s)>a&&++l<o);return c}function g(e,t,n,i){for(var a=0;a<r;++a){var o=m(t,n,i);if(o===0)return t;var s=p(t,n,i)-e;t-=s/o}return t}function _(e){this._p=e,this._mSampleValues=l?new Float32Array(s):Array(s),this._precomputed=!1,this.get=this.get.bind(this)}return _.prototype={get:function(e){var t=this._p[0],n=this._p[1],r=this._p[2],i=this._p[3];return this._precomputed||this._precompute(),t===n&&r===i?e:e===0?0:e===1?1:p(this._getTForX(e),n,i)},_precompute:function(){var e=this._p[0],t=this._p[1],n=this._p[2],r=this._p[3];this._precomputed=!0,(e!==t||n!==r)&&this._calcSampleValues()},_calcSampleValues:function(){for(var e=this._p[0],t=this._p[2],n=0;n<s;++n)this._mSampleValues[n]=p(n*c,e,t)},_getTForX:function(e){for(var t=this._p[0],n=this._p[2],r=this._mSampleValues,a=0,o=1,l=s-1;o!==l&&r[o]<=e;++o)a+=c;--o;var u=(e-r[o])/(r[o+1]-r[o]),d=a+u*c,f=m(d,t,n);return f>=i?g(e,d,t,n):f===0?d:h(e,a,a+c,t,n)}},e}(),we=function(){function e(e){return e.concat(m(e.length))}return{double:e}}(),Te=function(){return function(e,t,n){var r=0,i=e,a=m(i),o={newElement:s,release:c};function s(){var e;return r?(--r,e=a[r]):e=t(),e}function c(e){r===i&&(a=we.double(a),i*=2),n&&n(e),a[r]=e,r+=1}return o}}(),Ee=function(){function e(){return{addedLength:0,percents:p(`float32`,fe()),lengths:p(`float32`,fe())}}return Te(8,e)}(),De=function(){function e(){return{lengths:[],totalLength:0}}function t(e){var t,n=e.lengths.length;for(t=0;t<n;t+=1)Ee.release(e.lengths[t]);e.lengths.length=0}return Te(8,e,t)}();function Oe(){var e=Math;function t(e,t,n,r,i,a){var o=e*r+t*i+n*a-i*r-a*e-n*t;return o>-.001&&o<.001}function n(n,r,i,a,o,s,c,l,u){if(i===0&&s===0&&u===0)return t(n,r,a,o,c,l);var d=e.sqrt(e.pow(a-n,2)+e.pow(o-r,2)+e.pow(s-i,2)),f=e.sqrt(e.pow(c-n,2)+e.pow(l-r,2)+e.pow(u-i,2)),p=e.sqrt(e.pow(c-a,2)+e.pow(l-o,2)+e.pow(u-s,2)),m=d>f?d>p?d-f-p:p-f-d:p>f?p-f-d:f-d-p;return m>-1e-4&&m<1e-4}var r=function(){return function(e,t,n,r){var i=fe(),a,o,s,c,l,u=0,d,f=[],p=[],m=Ee.newElement();for(s=n.length,a=0;a<i;a+=1){for(l=a/(i-1),d=0,o=0;o<s;o+=1)c=x(1-l,3)*e[o]+3*x(1-l,2)*l*n[o]+3*(1-l)*x(l,2)*r[o]+x(l,3)*t[o],f[o]=c,p[o]!==null&&(d+=x(f[o]-p[o],2)),p[o]=f[o];d&&(d=S(d),u+=d),m.percents[a]=l,m.lengths[a]=u}return m.addedLength=u,m}}();function i(e){var t=De.newElement(),n=e.c,i=e.v,a=e.o,o=e.i,s,c=e._length,l=t.lengths,u=0;for(s=0;s<c-1;s+=1)l[s]=r(i[s],i[s+1],a[s],o[s+1]),u+=l[s].addedLength;return n&&c&&(l[s]=r(i[s],i[0],a[s],o[0]),u+=l[s].addedLength),t.totalLength=u,t}function a(e){this.segmentLength=0,this.points=Array(e)}function o(e,t){this.partialLength=e,this.point=t}var s=function(){var e={};return function(n,r,i,s){var c=(n[0]+`_`+n[1]+`_`+r[0]+`_`+r[1]+`_`+i[0]+`_`+i[1]+`_`+s[0]+`_`+s[1]).replace(/\./g,`p`);if(!e[c]){var l=fe(),u,d,f,p,h,g=0,_,v,y=null;n.length===2&&(n[0]!==r[0]||n[1]!==r[1])&&t(n[0],n[1],r[0],r[1],n[0]+i[0],n[1]+i[1])&&t(n[0],n[1],r[0],r[1],r[0]+s[0],r[1]+s[1])&&(l=2);var b=new a(l);for(f=i.length,u=0;u<l;u+=1){for(v=m(f),h=u/(l-1),_=0,d=0;d<f;d+=1)p=x(1-h,3)*n[d]+3*x(1-h,2)*h*(n[d]+i[d])+3*(1-h)*x(h,2)*(r[d]+s[d])+x(h,3)*r[d],v[d]=p,y!==null&&(_+=x(v[d]-y[d],2));_=S(_),g+=_,b.points[u]=new o(_,v),y=v}b.segmentLength=g,e[c]=b}return e[c]}}();function c(e,t){var n=t.percents,r=t.lengths,i=n.length,a=C((i-1)*e),o=e*t.addedLength,s=0;if(a===i-1||a===0||o===r[a])return n[a];for(var c=r[a]>o?-1:1,l=!0;l;)if(r[a]<=o&&r[a+1]>o?(s=(o-r[a])/(r[a+1]-r[a]),l=!1):a+=c,a<0||a>=i-1){if(a===i-1)return n[a];l=!1}return n[a]+(n[a+1]-n[a])*s}function l(t,n,r,i,a,o){var s=c(a,o),l=1-s;return[e.round((l*l*l*t[0]+(s*l*l+l*s*l+l*l*s)*r[0]+(s*s*l+l*s*s+s*l*s)*i[0]+s*s*s*n[0])*1e3)/1e3,e.round((l*l*l*t[1]+(s*l*l+l*s*l+l*l*s)*r[1]+(s*s*l+l*s*s+s*l*s)*i[1]+s*s*s*n[1])*1e3)/1e3]}var u=p(`float32`,8);function d(t,n,r,i,a,o,s){a<0?a=0:a>1&&(a=1);var l=c(a,s);o=o>1?1:o;var d=c(o,s),f,p=t.length,m=1-l,h=1-d,g=m*m*m,_=l*m*m*3,v=l*l*m*3,y=l*l*l,b=m*m*h,x=l*m*h+m*l*h+m*m*d,S=l*l*h+m*l*d+l*m*d,C=l*l*d,w=m*h*h,T=l*h*h+m*d*h+m*h*d,E=l*d*h+m*d*d+l*h*d,D=l*d*d,O=h*h*h,k=d*h*h+h*d*h+h*h*d,A=d*d*h+h*d*d+d*h*d,ee=d*d*d;for(f=0;f<p;f+=1)u[f*4]=e.round((g*t[f]+_*r[f]+v*i[f]+y*n[f])*1e3)/1e3,u[f*4+1]=e.round((b*t[f]+x*r[f]+S*i[f]+C*n[f])*1e3)/1e3,u[f*4+2]=e.round((w*t[f]+T*r[f]+E*i[f]+D*n[f])*1e3)/1e3,u[f*4+3]=e.round((O*t[f]+k*r[f]+A*i[f]+ee*n[f])*1e3)/1e3;return u}return{getSegmentsLength:i,getNewSegment:d,getPointInSegment:l,buildBezierData:s,pointOnLine2D:t,pointOnLine3D:n}}var V=Oe(),ke=i,Ae=Math.abs;function je(e,t){var n=this.offsetTime,r;this.propType===`multidimensional`&&(r=p(`float32`,this.pv.length));for(var i=t.lastIndex,a=i,o=this.keyframes.length-1,s=!0,c,l,u;s;){if(c=this.keyframes[a],l=this.keyframes[a+1],a===o-1&&e>=l.t-n){c.h&&(c=l),i=0;break}if(l.t-n>e){i=a;break}a<o-1?a+=1:(i=0,s=!1)}u=this.keyframesMetadata[a]||{};var d,f,m,h,g,_,v=l.t-n,y=c.t-n,b;if(c.to){u.bezierData||(u.bezierData=V.buildBezierData(c.s,l.s||c.e,c.to,c.ti));var x=u.bezierData;if(e>=v||e<y){var S=e>=v?x.points.length-1:0;for(f=x.points[S].point.length,d=0;d<f;d+=1)r[d]=x.points[S].point[d]}else{u.__fnct?_=u.__fnct:(_=Ce.getBezierEasing(c.o.x,c.o.y,c.i.x,c.i.y,c.n).get,u.__fnct=_),m=_((e-y)/(v-y));var C=x.segmentLength*m,w,T=t.lastFrame<e&&t._lastKeyframeIndex===a?t._lastAddedLength:0;for(g=t.lastFrame<e&&t._lastKeyframeIndex===a?t._lastPoint:0,s=!0,h=x.points.length;s;){if(T+=x.points[g].partialLength,C===0||m===0||g===x.points.length-1){for(f=x.points[g].point.length,d=0;d<f;d+=1)r[d]=x.points[g].point[d];break}if(C>=T&&C<T+x.points[g+1].partialLength){for(w=(C-T)/x.points[g+1].partialLength,f=x.points[g].point.length,d=0;d<f;d+=1)r[d]=x.points[g].point[d]+(x.points[g+1].point[d]-x.points[g].point[d])*w;break}g<h-1?g+=1:s=!1}t._lastPoint=g,t._lastAddedLength=T-x.points[g].partialLength,t._lastKeyframeIndex=a}}else{var E,D,O,k,A;if(o=c.s.length,b=l.s||c.e,this.sh&&c.h!==1){if(e>=v)r[0]=b[0],r[1]=b[1],r[2]=b[2];else if(e<=y)r[0]=c.s[0],r[1]=c.s[1],r[2]=c.s[2];else{var ee=Pe(c.s),j=Pe(b),M=(e-y)/(v-y);Ne(r,Me(ee,j,M))}}else for(a=0;a<o;a+=1)c.h!==1&&(e>=v?m=1:e<y?m=0:(c.o.x.constructor===Array?(u.__fnct||(u.__fnct=[]),u.__fnct[a]?_=u.__fnct[a]:(E=c.o.x[a]===void 0?c.o.x[0]:c.o.x[a],D=c.o.y[a]===void 0?c.o.y[0]:c.o.y[a],O=c.i.x[a]===void 0?c.i.x[0]:c.i.x[a],k=c.i.y[a]===void 0?c.i.y[0]:c.i.y[a],_=Ce.getBezierEasing(E,D,O,k).get,u.__fnct[a]=_)):u.__fnct?_=u.__fnct:(E=c.o.x,D=c.o.y,O=c.i.x,k=c.i.y,_=Ce.getBezierEasing(E,D,O,k).get,c.keyframeMetadata=_),m=_((e-y)/(v-y)))),b=l.s||c.e,A=c.h===1?c.s[a]:c.s[a]+(b[a]-c.s[a])*m,this.propType===`multidimensional`?r[a]=A:r=A}return t.lastIndex=i,r}function Me(e,t,n){var r=[],i=e[0],a=e[1],o=e[2],s=e[3],c=t[0],l=t[1],u=t[2],d=t[3],f,p=i*c+a*l+o*u+s*d,m,h,g;return p<0&&(p=-p,c=-c,l=-l,u=-u,d=-d),1-p>1e-6?(f=Math.acos(p),m=Math.sin(f),h=Math.sin((1-n)*f)/m,g=Math.sin(n*f)/m):(h=1-n,g=n),r[0]=h*i+g*c,r[1]=h*a+g*l,r[2]=h*o+g*u,r[3]=h*s+g*d,r}function Ne(e,t){var n=t[0],r=t[1],i=t[2],a=t[3],o=Math.atan2(2*r*a-2*n*i,1-2*r*r-2*i*i),s=Math.asin(2*n*r+2*i*a),c=Math.atan2(2*n*a-2*r*i,1-2*n*n-2*i*i);e[0]=o/D,e[1]=s/D,e[2]=c/D}function Pe(e){var t=e[0]*D,n=e[1]*D,r=e[2]*D,i=Math.cos(t/2),a=Math.cos(n/2),o=Math.cos(r/2),s=Math.sin(t/2),c=Math.sin(n/2),l=Math.sin(r/2),u=i*a*o-s*c*l;return[s*c*o+i*a*l,s*a*o+i*c*l,i*c*o-s*a*l,u]}function Fe(){var e=this.comp.renderedFrame-this.offsetTime,t=this.keyframes[0].t-this.offsetTime,n=this.keyframes[this.keyframes.length-1].t-this.offsetTime;if(!(e===this._caching.lastFrame||this._caching.lastFrame!==ke&&(this._caching.lastFrame>=n&&e>=n||this._caching.lastFrame<t&&e<t))){this._caching.lastFrame>=e&&(this._caching._lastKeyframeIndex=-1,this._caching.lastIndex=0);var r=this.interpolateValue(e,this._caching);this.pv=r}return this._caching.lastFrame=e,this.pv}function Ie(e){var t;if(this.propType===`unidimensional`)t=e*this.mult,Ae(this.v-t)>1e-5&&(this.v=t,this._mdf=!0);else for(var n=0,r=this.v.length;n<r;)t=e[n]*this.mult,Ae(this.v[n]-t)>1e-5&&(this.v[n]=t,this._mdf=!0),n+=1}function Le(){if(this.elem.globalData.frameId!==this.frameId&&this.effectsSequence.length){if(this.lock){this.setVValue(this.pv);return}this.lock=!0,this._mdf=this._isFirstFrame;var e,t=this.effectsSequence.length,n=this.kf?this.pv:this.data.k;for(e=0;e<t;e+=1)n=this.effectsSequence[e](n);this.setVValue(n),this._isFirstFrame=!1,this.lock=!1,this.frameId=this.elem.globalData.frameId}}function Re(e){this.effectsSequence.push(e),this.container.addDynamicProperty(this)}function ze(e,t,n,r){this.propType=`unidimensional`,this.mult=n||1,this.data=t,this.v=n?t.k*n:t.k,this.pv=t.k,this._mdf=!1,this.elem=e,this.container=r,this.comp=e.comp,this.k=!1,this.kf=!1,this.vel=0,this.effectsSequence=[],this._isFirstFrame=!0,this.getValue=Le,this.setVValue=Ie,this.addEffect=Re}function Be(e,t,n,r){this.propType=`multidimensional`,this.mult=n||1,this.data=t,this._mdf=!1,this.elem=e,this.container=r,this.comp=e.comp,this.k=!1,this.kf=!1,this.frameId=-1;var i,a=t.k.length;for(this.v=p(`float32`,a),this.pv=p(`float32`,a),this.vel=p(`float32`,a),i=0;i<a;i+=1)this.v[i]=t.k[i]*this.mult,this.pv[i]=t.k[i];this._isFirstFrame=!0,this.effectsSequence=[],this.getValue=Le,this.setVValue=Ie,this.addEffect=Re}function Ve(e,t,n,r){this.propType=`unidimensional`,this.keyframes=t.k,this.keyframesMetadata=[],this.offsetTime=e.data.st,this.frameId=-1,this._caching={lastFrame:ke,lastIndex:0,value:0,_lastKeyframeIndex:-1},this.k=!0,this.kf=!0,this.data=t,this.mult=n||1,this.elem=e,this.container=r,this.comp=e.comp,this.v=ke,this.pv=ke,this._isFirstFrame=!0,this.getValue=Le,this.setVValue=Ie,this.interpolateValue=je,this.effectsSequence=[Fe.bind(this)],this.addEffect=Re}function He(e,t,n,r){this.propType=`multidimensional`;var i,a=t.k.length,o,s,c,l;for(i=0;i<a-1;i+=1)t.k[i].to&&t.k[i].s&&t.k[i+1]&&t.k[i+1].s&&(o=t.k[i].s,s=t.k[i+1].s,c=t.k[i].to,l=t.k[i].ti,(o.length===2&&(o[0]!==s[0]||o[1]!==s[1])&&V.pointOnLine2D(o[0],o[1],s[0],s[1],o[0]+c[0],o[1]+c[1])&&V.pointOnLine2D(o[0],o[1],s[0],s[1],s[0]+l[0],s[1]+l[1])||o.length===3&&(o[0]!==s[0]||o[1]!==s[1]||o[2]!==s[2])&&V.pointOnLine3D(o[0],o[1],o[2],s[0],s[1],s[2],o[0]+c[0],o[1]+c[1],o[2]+c[2])&&V.pointOnLine3D(o[0],o[1],o[2],s[0],s[1],s[2],s[0]+l[0],s[1]+l[1],s[2]+l[2]))&&(t.k[i].to=null,t.k[i].ti=null),o[0]===s[0]&&o[1]===s[1]&&c[0]===0&&c[1]===0&&l[0]===0&&l[1]===0&&(o.length===2||o[2]===s[2]&&c[2]===0&&l[2]===0)&&(t.k[i].to=null,t.k[i].ti=null));this.effectsSequence=[Fe.bind(this)],this.data=t,this.keyframes=t.k,this.keyframesMetadata=[],this.offsetTime=e.data.st,this.k=!0,this.kf=!0,this._isFirstFrame=!0,this.mult=n||1,this.elem=e,this.container=r,this.comp=e.comp,this.getValue=Le,this.setVValue=Ie,this.interpolateValue=je,this.frameId=-1;var u=t.k[0].s.length;for(this.v=p(`float32`,u),this.pv=p(`float32`,u),i=0;i<u;i+=1)this.v[i]=ke,this.pv[i]=ke;this._caching={lastFrame:ke,lastIndex:0,value:p(`float32`,u)},this.addEffect=Re}var H=function(){function e(e,t,n,r,i){t.sid&&(t=e.globalData.slotManager.getProp(t));var a;if(!t.k.length)a=new ze(e,t,r,i);else if(typeof t.k[0]==`number`)a=new Be(e,t,r,i);else switch(n){case 0:a=new Ve(e,t,r,i);break;case 1:a=new He(e,t,r,i)}return a.effectsSequence.length&&i.addDynamicProperty(a),a}return{getProp:e}}();function Ue(){}Ue.prototype={addDynamicProperty:function(e){this.dynamicProperties.indexOf(e)===-1&&(this.dynamicProperties.push(e),this.container.addDynamicProperty(this),this._isAnimated=!0)},iterateDynamicProperties:function(){this._mdf=!1;var e,t=this.dynamicProperties.length;for(e=0;e<t;e+=1)this.dynamicProperties[e].getValue(),this.dynamicProperties[e]._mdf&&(this._mdf=!0)},initDynamicPropertyContainer:function(e){this.container=e,this.dynamicProperties=[],this._mdf=!1,this._isAnimated=!1}};var We=function(){function e(){return p(`float32`,2)}return Te(8,e)}();function Ge(){this.c=!1,this._length=0,this._maxLength=8,this.v=m(this._maxLength),this.o=m(this._maxLength),this.i=m(this._maxLength)}Ge.prototype.setPathData=function(e,t){this.c=e,this.setLength(t);for(var n=0;n<t;)this.v[n]=We.newElement(),this.o[n]=We.newElement(),this.i[n]=We.newElement(),n+=1},Ge.prototype.setLength=function(e){for(;this._maxLength<e;)this.doubleArrayLength();this._length=e},Ge.prototype.doubleArrayLength=function(){this.v=this.v.concat(m(this._maxLength)),this.i=this.i.concat(m(this._maxLength)),this.o=this.o.concat(m(this._maxLength)),this._maxLength*=2},Ge.prototype.setXYAt=function(e,t,n,r,i){var a;switch(this._length=Math.max(this._length,r+1),this._length>=this._maxLength&&this.doubleArrayLength(),n){case`v`:a=this.v;break;case`i`:a=this.i;break;case`o`:a=this.o;break;default:a=[]}(!a[r]||a[r]&&!i)&&(a[r]=We.newElement()),a[r][0]=e,a[r][1]=t},Ge.prototype.setTripleAt=function(e,t,n,r,i,a,o,s){this.setXYAt(e,t,`v`,o,s),this.setXYAt(n,r,`o`,o,s),this.setXYAt(i,a,`i`,o,s)},Ge.prototype.reverse=function(){var e=new Ge;e.setPathData(this.c,this._length);var t=this.v,n=this.o,r=this.i,i=0;this.c&&(e.setTripleAt(t[0][0],t[0][1],r[0][0],r[0][1],n[0][0],n[0][1],0,!1),i=1);for(var a=this._length-1,o=this._length,s=i;s<o;s+=1)e.setTripleAt(t[a][0],t[a][1],r[a][0],r[a][1],n[a][0],n[a][1],s,!1),--a;return e},Ge.prototype.length=function(){return this._length};var Ke=function(){function e(){return new Ge}function t(e){for(var t=e._length,n=0;n<t;n+=1)We.release(e.v[n]),We.release(e.i[n]),We.release(e.o[n]),e.v[n]=null,e.i[n]=null,e.o[n]=null;e._length=0,e.c=!1}function n(e){var t=r.newElement(),n,i=e._length===void 0?e.v.length:e._length;for(t.setLength(i),t.c=e.c,n=0;n<i;n+=1)t.setTripleAt(e.v[n][0],e.v[n][1],e.o[n][0],e.o[n][1],e.i[n][0],e.i[n][1],n);return t}var r=Te(4,e,t);return r.clone=n,r}();function qe(){this._length=0,this._maxLength=4,this.shapes=m(this._maxLength)}qe.prototype.addShape=function(e){this._length===this._maxLength&&(this.shapes=this.shapes.concat(m(this._maxLength)),this._maxLength*=2),this.shapes[this._length]=e,this._length+=1},qe.prototype.releaseShapes=function(){for(var e=0;e<this._length;e+=1)Ke.release(this.shapes[e]);this._length=0};var U=function(){var e={newShapeCollection:i,release:a},t=0,n=4,r=m(n);function i(){var e;return t?(--t,e=r[t]):e=new qe,e}function a(e){var i,a=e._length;for(i=0;i<a;i+=1)Ke.release(e.shapes[i]);e._length=0,t===n&&(r=we.double(r),n*=2),r[t]=e,t+=1}return e}(),Je=function(){var e=-999999;function t(e,t,n){var r=n.lastIndex,i,a,o,s,c,l,u,d,f,p=this.keyframes;if(e<p[0].t-this.offsetTime)i=p[0].s[0],o=!0,r=0;else if(e>=p[p.length-1].t-this.offsetTime)i=p[p.length-1].s?p[p.length-1].s[0]:p[p.length-2].e[0],o=!0;else{for(var m=r,h=p.length-1,g=!0,_,v,y;g&&(_=p[m],v=p[m+1],!(v.t-this.offsetTime>e));)m<h-1?m+=1:g=!1;if(y=this.keyframesMetadata[m]||{},o=_.h===1,r=m,!o){if(e>=v.t-this.offsetTime)d=1;else if(e<_.t-this.offsetTime)d=0;else{var b;y.__fnct?b=y.__fnct:(b=Ce.getBezierEasing(_.o.x,_.o.y,_.i.x,_.i.y).get,y.__fnct=b),d=b((e-(_.t-this.offsetTime))/(v.t-this.offsetTime-(_.t-this.offsetTime)))}a=v.s?v.s[0]:_.e[0]}i=_.s[0]}for(l=t._length,u=i.i[0].length,n.lastIndex=r,s=0;s<l;s+=1)for(c=0;c<u;c+=1)f=o?i.i[s][c]:i.i[s][c]+(a.i[s][c]-i.i[s][c])*d,t.i[s][c]=f,f=o?i.o[s][c]:i.o[s][c]+(a.o[s][c]-i.o[s][c])*d,t.o[s][c]=f,f=o?i.v[s][c]:i.v[s][c]+(a.v[s][c]-i.v[s][c])*d,t.v[s][c]=f}function n(){var t=this.comp.renderedFrame-this.offsetTime,n=this.keyframes[0].t-this.offsetTime,r=this.keyframes[this.keyframes.length-1].t-this.offsetTime,i=this._caching.lastFrame;return i!==e&&(i<n&&t<n||i>r&&t>r)||(this._caching.lastIndex=i<t?this._caching.lastIndex:0,this.interpolateShape(t,this.pv,this._caching)),this._caching.lastFrame=t,this.pv}function r(){this.paths=this.localShapeCollection}function i(e,t){if(e._length!==t._length||e.c!==t.c)return!1;var n,r=e._length;for(n=0;n<r;n+=1)if(e.v[n][0]!==t.v[n][0]||e.v[n][1]!==t.v[n][1]||e.o[n][0]!==t.o[n][0]||e.o[n][1]!==t.o[n][1]||e.i[n][0]!==t.i[n][0]||e.i[n][1]!==t.i[n][1])return!1;return!0}function a(e){i(this.v,e)||(this.v=Ke.clone(e),this.localShapeCollection.releaseShapes(),this.localShapeCollection.addShape(this.v),this._mdf=!0,this.paths=this.localShapeCollection)}function o(){if(this.elem.globalData.frameId!==this.frameId){if(!this.effectsSequence.length){this._mdf=!1;return}if(this.lock){this.setVValue(this.pv);return}this.lock=!0,this._mdf=!1;var e=this.kf?this.pv:this.data.ks?this.data.ks.k:this.data.pt.k,t,n=this.effectsSequence.length;for(t=0;t<n;t+=1)e=this.effectsSequence[t](e);this.setVValue(e),this.lock=!1,this.frameId=this.elem.globalData.frameId}}function s(e,t,n){this.propType=`shape`,this.comp=e.comp,this.container=e,this.elem=e,this.data=t,this.k=!1,this.kf=!1,this._mdf=!1;var i=n===3?t.pt.k:t.ks.k;this.v=Ke.clone(i),this.pv=Ke.clone(this.v),this.localShapeCollection=U.newShapeCollection(),this.paths=this.localShapeCollection,this.paths.addShape(this.v),this.reset=r,this.effectsSequence=[]}function c(e){this.effectsSequence.push(e),this.container.addDynamicProperty(this)}s.prototype.interpolateShape=t,s.prototype.getValue=o,s.prototype.setVValue=a,s.prototype.addEffect=c;function l(t,i,a){this.propType=`shape`,this.comp=t.comp,this.elem=t,this.container=t,this.offsetTime=t.data.st,this.keyframes=a===3?i.pt.k:i.ks.k,this.keyframesMetadata=[],this.k=!0,this.kf=!0;var o=this.keyframes[0].s[0].i.length;this.v=Ke.newElement(),this.v.setPathData(this.keyframes[0].s[0].c,o),this.pv=Ke.clone(this.v),this.localShapeCollection=U.newShapeCollection(),this.paths=this.localShapeCollection,this.paths.addShape(this.v),this.lastFrame=e,this.reset=r,this._caching={lastFrame:e,lastIndex:0},this.effectsSequence=[n.bind(this)]}l.prototype.getValue=o,l.prototype.interpolateShape=t,l.prototype.setVValue=a,l.prototype.addEffect=c;var d=function(){var e=O;function t(e,t){this.v=Ke.newElement(),this.v.setPathData(!0,4),this.localShapeCollection=U.newShapeCollection(),this.paths=this.localShapeCollection,this.localShapeCollection.addShape(this.v),this.d=t.d,this.elem=e,this.comp=e.comp,this.frameId=-1,this.initDynamicPropertyContainer(e),this.p=H.getProp(e,t.p,1,0,this),this.s=H.getProp(e,t.s,1,0,this),this.dynamicProperties.length?this.k=!0:(this.k=!1,this.convertEllToPath())}return t.prototype={reset:r,getValue:function(){this.elem.globalData.frameId!==this.frameId&&(this.frameId=this.elem.globalData.frameId,this.iterateDynamicProperties(),this._mdf&&this.convertEllToPath())},convertEllToPath:function(){var t=this.p.v[0],n=this.p.v[1],r=this.s.v[0]/2,i=this.s.v[1]/2,a=this.d!==3,o=this.v;o.v[0][0]=t,o.v[0][1]=n-i,o.v[1][0]=a?t+r:t-r,o.v[1][1]=n,o.v[2][0]=t,o.v[2][1]=n+i,o.v[3][0]=a?t-r:t+r,o.v[3][1]=n,o.i[0][0]=a?t-r*e:t+r*e,o.i[0][1]=n-i,o.i[1][0]=a?t+r:t-r,o.i[1][1]=n-i*e,o.i[2][0]=a?t+r*e:t-r*e,o.i[2][1]=n+i,o.i[3][0]=a?t-r:t+r,o.i[3][1]=n+i*e,o.o[0][0]=a?t+r*e:t-r*e,o.o[0][1]=n-i,o.o[1][0]=a?t+r:t-r,o.o[1][1]=n+i*e,o.o[2][0]=a?t-r*e:t+r*e,o.o[2][1]=n+i,o.o[3][0]=a?t-r:t+r,o.o[3][1]=n-i*e}},u([Ue],t),t}(),f=function(){function e(e,t){this.v=Ke.newElement(),this.v.setPathData(!0,0),this.elem=e,this.comp=e.comp,this.data=t,this.frameId=-1,this.d=t.d,this.initDynamicPropertyContainer(e),t.sy===1?(this.ir=H.getProp(e,t.ir,0,0,this),this.is=H.getProp(e,t.is,0,.01,this),this.convertToPath=this.convertStarToPath):this.convertToPath=this.convertPolygonToPath,this.pt=H.getProp(e,t.pt,0,0,this),this.p=H.getProp(e,t.p,1,0,this),this.r=H.getProp(e,t.r,0,D,this),this.or=H.getProp(e,t.or,0,0,this),this.os=H.getProp(e,t.os,0,.01,this),this.localShapeCollection=U.newShapeCollection(),this.localShapeCollection.addShape(this.v),this.paths=this.localShapeCollection,this.dynamicProperties.length?this.k=!0:(this.k=!1,this.convertToPath())}return e.prototype={reset:r,getValue:function(){this.elem.globalData.frameId!==this.frameId&&(this.frameId=this.elem.globalData.frameId,this.iterateDynamicProperties(),this._mdf&&this.convertToPath())},convertStarToPath:function(){var e=Math.floor(this.pt.v)*2,t=Math.PI*2/e,n=!0,r=this.or.v,i=this.ir.v,a=this.os.v,o=this.is.v,s=2*Math.PI*r/(e*2),c=2*Math.PI*i/(e*2),l,u,d,f,p=-Math.PI/2;p+=this.r.v;var m=this.data.d===3?-1:1;for(this.v._length=0,l=0;l<e;l+=1){u=n?r:i,d=n?a:o,f=n?s:c;var h=u*Math.cos(p),g=u*Math.sin(p),_=h===0&&g===0?0:g/Math.sqrt(h*h+g*g),v=h===0&&g===0?0:-h/Math.sqrt(h*h+g*g);h+=+this.p.v[0],g+=+this.p.v[1],this.v.setTripleAt(h,g,h-_*f*d*m,g-v*f*d*m,h+_*f*d*m,g+v*f*d*m,l,!0),n=!n,p+=t*m}},convertPolygonToPath:function(){var e=Math.floor(this.pt.v),t=Math.PI*2/e,n=this.or.v,r=this.os.v,i=2*Math.PI*n/(e*4),a,o=-Math.PI*.5,s=this.data.d===3?-1:1;for(o+=this.r.v,this.v._length=0,a=0;a<e;a+=1){var c=n*Math.cos(o),l=n*Math.sin(o),u=c===0&&l===0?0:l/Math.sqrt(c*c+l*l),d=c===0&&l===0?0:-c/Math.sqrt(c*c+l*l);c+=+this.p.v[0],l+=+this.p.v[1],this.v.setTripleAt(c,l,c-u*i*r*s,l-d*i*r*s,c+u*i*r*s,l+d*i*r*s,a,!0),o+=t*s}this.paths.length=0,this.paths[0]=this.v}},u([Ue],e),e}(),p=function(){function e(e,t){this.v=Ke.newElement(),this.v.c=!0,this.localShapeCollection=U.newShapeCollection(),this.localShapeCollection.addShape(this.v),this.paths=this.localShapeCollection,this.elem=e,this.comp=e.comp,this.frameId=-1,this.d=t.d,this.initDynamicPropertyContainer(e),this.p=H.getProp(e,t.p,1,0,this),this.s=H.getProp(e,t.s,1,0,this),this.r=H.getProp(e,t.r,0,0,this),this.dynamicProperties.length?this.k=!0:(this.k=!1,this.convertRectToPath())}return e.prototype={convertRectToPath:function(){var e=this.p.v[0],t=this.p.v[1],n=this.s.v[0]/2,r=this.s.v[1]/2,i=w(n,r,this.r.v),a=i*(1-O);this.v._length=0,this.d===2||this.d===1?(this.v.setTripleAt(e+n,t-r+i,e+n,t-r+i,e+n,t-r+a,0,!0),this.v.setTripleAt(e+n,t+r-i,e+n,t+r-a,e+n,t+r-i,1,!0),i===0?(this.v.setTripleAt(e-n,t+r,e-n+a,t+r,e-n,t+r,2),this.v.setTripleAt(e-n,t-r,e-n,t-r+a,e-n,t-r,3)):(this.v.setTripleAt(e+n-i,t+r,e+n-i,t+r,e+n-a,t+r,2,!0),this.v.setTripleAt(e-n+i,t+r,e-n+a,t+r,e-n+i,t+r,3,!0),this.v.setTripleAt(e-n,t+r-i,e-n,t+r-i,e-n,t+r-a,4,!0),this.v.setTripleAt(e-n,t-r+i,e-n,t-r+a,e-n,t-r+i,5,!0),this.v.setTripleAt(e-n+i,t-r,e-n+i,t-r,e-n+a,t-r,6,!0),this.v.setTripleAt(e+n-i,t-r,e+n-a,t-r,e+n-i,t-r,7,!0))):(this.v.setTripleAt(e+n,t-r+i,e+n,t-r+a,e+n,t-r+i,0,!0),i===0?(this.v.setTripleAt(e-n,t-r,e-n+a,t-r,e-n,t-r,1,!0),this.v.setTripleAt(e-n,t+r,e-n,t+r-a,e-n,t+r,2,!0),this.v.setTripleAt(e+n,t+r,e+n-a,t+r,e+n,t+r,3,!0)):(this.v.setTripleAt(e+n-i,t-r,e+n-i,t-r,e+n-a,t-r,1,!0),this.v.setTripleAt(e-n+i,t-r,e-n+a,t-r,e-n+i,t-r,2,!0),this.v.setTripleAt(e-n,t-r+i,e-n,t-r+i,e-n,t-r+a,3,!0),this.v.setTripleAt(e-n,t+r-i,e-n,t+r-a,e-n,t+r-i,4,!0),this.v.setTripleAt(e-n+i,t+r,e-n+i,t+r,e-n+a,t+r,5,!0),this.v.setTripleAt(e+n-i,t+r,e+n-a,t+r,e+n-i,t+r,6,!0),this.v.setTripleAt(e+n,t+r-i,e+n,t+r-i,e+n,t+r-a,7,!0)))},getValue:function(){this.elem.globalData.frameId!==this.frameId&&(this.frameId=this.elem.globalData.frameId,this.iterateDynamicProperties(),this._mdf&&this.convertRectToPath())},reset:r},u([Ue],e),e}();function m(e,t,n){var r;return n===3||n===4?r=(n===3?t.pt:t.ks).k.length?new l(e,t,n):new s(e,t,n):n===5?r=new p(e,t):n===6?r=new d(e,t):n===7&&(r=new f(e,t)),r.k&&e.addDynamicProperty(r),r}function h(){return s}function g(){return l}var _={};return _.getShapeProp=m,_.getConstructorFunction=h,_.getKeyframedConstructorFunction=g,_}(),Ye=function(){var e=Math.cos,t=Math.sin,n=Math.tan,r=Math.round;function i(){return this.props[0]=1,this.props[1]=0,this.props[2]=0,this.props[3]=0,this.props[4]=0,this.props[5]=1,this.props[6]=0,this.props[7]=0,this.props[8]=0,this.props[9]=0,this.props[10]=1,this.props[11]=0,this.props[12]=0,this.props[13]=0,this.props[14]=0,this.props[15]=1,this}function a(n){if(n===0)return this;var r=e(n),i=t(n);return this._t(r,-i,0,0,i,r,0,0,0,0,1,0,0,0,0,1)}function o(n){if(n===0)return this;var r=e(n),i=t(n);return this._t(1,0,0,0,0,r,-i,0,0,i,r,0,0,0,0,1)}function s(n){if(n===0)return this;var r=e(n),i=t(n);return this._t(r,0,i,0,0,1,0,0,-i,0,r,0,0,0,0,1)}function c(n){if(n===0)return this;var r=e(n),i=t(n);return this._t(r,-i,0,0,i,r,0,0,0,0,1,0,0,0,0,1)}function l(e,t){return this._t(1,t,e,1,0,0)}function u(e,t){return this.shear(n(e),n(t))}function d(r,i){var a=e(i),o=t(i);return this._t(a,o,0,0,-o,a,0,0,0,0,1,0,0,0,0,1)._t(1,0,0,0,n(r),1,0,0,0,0,1,0,0,0,0,1)._t(a,-o,0,0,o,a,0,0,0,0,1,0,0,0,0,1)}function f(e,t,n){return!n&&n!==0&&(n=1),e===1&&t===1&&n===1?this:this._t(e,0,0,0,0,t,0,0,0,0,n,0,0,0,0,1)}function m(e,t,n,r,i,a,o,s,c,l,u,d,f,p,m,h){return this.props[0]=e,this.props[1]=t,this.props[2]=n,this.props[3]=r,this.props[4]=i,this.props[5]=a,this.props[6]=o,this.props[7]=s,this.props[8]=c,this.props[9]=l,this.props[10]=u,this.props[11]=d,this.props[12]=f,this.props[13]=p,this.props[14]=m,this.props[15]=h,this}function h(e,t,n){return n=n||0,e!==0||t!==0||n!==0?this._t(1,0,0,0,0,1,0,0,0,0,1,0,e,t,n,1):this}function g(e,t,n,r,i,a,o,s,c,l,u,d,f,p,m,h){var g=this.props;if(e===1&&t===0&&n===0&&r===0&&i===0&&a===1&&o===0&&s===0&&c===0&&l===0&&u===1&&d===0)return g[12]=g[12]*e+g[15]*f,g[13]=g[13]*a+g[15]*p,g[14]=g[14]*u+g[15]*m,g[15]*=h,this._identityCalculated=!1,this;var _=g[0],v=g[1],y=g[2],b=g[3],x=g[4],S=g[5],C=g[6],w=g[7],T=g[8],E=g[9],D=g[10],O=g[11],k=g[12],A=g[13],ee=g[14],j=g[15];return g[0]=_*e+v*i+y*c+b*f,g[1]=_*t+v*a+y*l+b*p,g[2]=_*n+v*o+y*u+b*m,g[3]=_*r+v*s+y*d+b*h,g[4]=x*e+S*i+C*c+w*f,g[5]=x*t+S*a+C*l+w*p,g[6]=x*n+S*o+C*u+w*m,g[7]=x*r+S*s+C*d+w*h,g[8]=T*e+E*i+D*c+O*f,g[9]=T*t+E*a+D*l+O*p,g[10]=T*n+E*o+D*u+O*m,g[11]=T*r+E*s+D*d+O*h,g[12]=k*e+A*i+ee*c+j*f,g[13]=k*t+A*a+ee*l+j*p,g[14]=k*n+A*o+ee*u+j*m,g[15]=k*r+A*s+ee*d+j*h,this._identityCalculated=!1,this}function _(e){var t=e.props;return this.transform(t[0],t[1],t[2],t[3],t[4],t[5],t[6],t[7],t[8],t[9],t[10],t[11],t[12],t[13],t[14],t[15])}function v(){return this._identityCalculated||(this._identity=this.props[0]===1&&this.props[1]===0&&this.props[2]===0&&this.props[3]===0&&this.props[4]===0&&this.props[5]===1&&this.props[6]===0&&this.props[7]===0&&this.props[8]===0&&this.props[9]===0&&this.props[10]===1&&this.props[11]===0&&this.props[12]===0&&this.props[13]===0&&this.props[14]===0&&this.props[15]===1,this._identityCalculated=!0),this._identity}function y(e){for(var t=0;t<16;){if(e.props[t]!==this.props[t])return!1;t+=1}return!0}function b(e){for(var t=0;t<16;t+=1)e.props[t]=this.props[t];return e}function x(e){for(var t=0;t<16;t+=1)this.props[t]=e[t]}function S(e,t,n){return{x:e*this.props[0]+t*this.props[4]+n*this.props[8]+this.props[12],y:e*this.props[1]+t*this.props[5]+n*this.props[9]+this.props[13],z:e*this.props[2]+t*this.props[6]+n*this.props[10]+this.props[14]}}function C(e,t,n){return e*this.props[0]+t*this.props[4]+n*this.props[8]+this.props[12]}function w(e,t,n){return e*this.props[1]+t*this.props[5]+n*this.props[9]+this.props[13]}function T(e,t,n){return e*this.props[2]+t*this.props[6]+n*this.props[10]+this.props[14]}function E(){var e=this.props[0]*this.props[5]-this.props[1]*this.props[4],t=this.props[5]/e,n=-this.props[1]/e,r=-this.props[4]/e,i=this.props[0]/e,a=(this.props[4]*this.props[13]-this.props[5]*this.props[12])/e,o=-(this.props[0]*this.props[13]-this.props[1]*this.props[12])/e,s=new Ye;return s.props[0]=t,s.props[1]=n,s.props[4]=r,s.props[5]=i,s.props[12]=a,s.props[13]=o,s}function D(e){return this.getInverseMatrix().applyToPointArray(e[0],e[1],e[2]||0)}function O(e){var t,n=e.length,r=[];for(t=0;t<n;t+=1)r[t]=D(e[t]);return r}function k(e,t,n){var r=p(`float32`,6);if(this.isIdentity())r[0]=e[0],r[1]=e[1],r[2]=t[0],r[3]=t[1],r[4]=n[0],r[5]=n[1];else{var i=this.props[0],a=this.props[1],o=this.props[4],s=this.props[5],c=this.props[12],l=this.props[13];r[0]=e[0]*i+e[1]*o+c,r[1]=e[0]*a+e[1]*s+l,r[2]=t[0]*i+t[1]*o+c,r[3]=t[0]*a+t[1]*s+l,r[4]=n[0]*i+n[1]*o+c,r[5]=n[0]*a+n[1]*s+l}return r}function A(e,t,n){return this.isIdentity()?[e,t,n]:[e*this.props[0]+t*this.props[4]+n*this.props[8]+this.props[12],e*this.props[1]+t*this.props[5]+n*this.props[9]+this.props[13],e*this.props[2]+t*this.props[6]+n*this.props[10]+this.props[14]]}function ee(e,t){if(this.isIdentity())return e+`,`+t;var n=this.props;return Math.round((e*n[0]+t*n[4]+n[12])*100)/100+`,`+Math.round((e*n[1]+t*n[5]+n[13])*100)/100}function j(){for(var e=0,t=this.props,n=`matrix3d(`,i=1e4;e<16;)n+=r(t[e]*i)/i,n+=e===15?`)`:`,`,e+=1;return n}function M(e){var t=1e4;return e<1e-6&&e>0||e>-1e-6&&e<0?r(e*t)/t:e}function N(){var e=this.props,t=M(e[0]),n=M(e[1]),r=M(e[4]),i=M(e[5]),a=M(e[12]),o=M(e[13]);return`matrix(`+t+`,`+n+`,`+r+`,`+i+`,`+a+`,`+o+`)`}return function(){this.reset=i,this.rotate=a,this.rotateX=o,this.rotateY=s,this.rotateZ=c,this.skew=u,this.skewFromAxis=d,this.shear=l,this.scale=f,this.setTransform=m,this.translate=h,this.transform=g,this.multiply=_,this.applyToPoint=S,this.applyToX=C,this.applyToY=w,this.applyToZ=T,this.applyToPointArray=A,this.applyToTriplePoints=k,this.applyToPointStringified=ee,this.toCSS=j,this.to2dCSS=N,this.clone=b,this.cloneFromProps=x,this.equals=y,this.inversePoints=O,this.inversePoint=D,this.getInverseMatrix=E,this._t=this.transform,this.isIdentity=v,this._identity=!0,this._identityCalculated=!1,this.props=p(`float32`,16),this.reset()}}();function Xe(e){"@babel/helpers - typeof";return Xe=typeof Symbol==`function`&&typeof Symbol.iterator==`symbol`?function(e){return typeof e}:function(e){return e&&typeof Symbol==`function`&&e.constructor===Symbol&&e!==Symbol.prototype?`symbol`:typeof e},Xe(e)}var W={},Ze=`__[STANDALONE]__`,Qe=`__[ANIMATIONDATA]__`,$e=``;function et(e){s(e)}function G(){Ze===!0?B.searchAnimations(Qe,Ze,$e):B.searchAnimations()}function tt(e){oe(e)}function nt(e){pe(e)}function rt(e){return Ze===!0&&(e.animationData=JSON.parse(Qe)),B.loadAnimation(e)}function it(e){if(typeof e==`string`)switch(e){case`high`:de(200);break;default:case`medium`:de(50);break;case`low`:de(10)}else!isNaN(e)&&e>1&&de(e)}function at(){return typeof navigator<`u`}function ot(e,t){e===`expressions`&&ce(t)}function K(e){switch(e){case`propertyFactory`:return H;case`shapePropertyFactory`:return Je;case`matrix`:return Ye;default:return null}}W.play=B.play,W.pause=B.pause,W.setLocationHref=et,W.togglePause=B.togglePause,W.setSpeed=B.setSpeed,W.setDirection=B.setDirection,W.stop=B.stop,W.searchAnimations=G,W.registerAnimation=B.registerAnimation,W.loadAnimation=rt,W.setSubframeRendering=tt,W.resize=B.resize,W.goToAndStop=B.goToAndStop,W.destroy=B.destroy,W.setQuality=it,W.inBrowser=at,W.installPlugin=ot,W.freeze=B.freeze,W.unfreeze=B.unfreeze,W.setVolume=B.setVolume,W.mute=B.mute,W.unmute=B.unmute,W.getRegisteredAnimations=B.getRegisteredAnimations,W.useWebWorker=a,W.setIDPrefix=nt,W.__getFactory=K,W.version=`5.13.0`;function st(){document.readyState===`complete`&&(clearInterval(ft),G())}function ct(e){for(var t=lt.split(`&`),n=0;n<t.length;n+=1){var r=t[n].split(`=`);if(decodeURIComponent(r[0])==e)return decodeURIComponent(r[1])}return null}var lt=``;if(Ze){var ut=document.getElementsByTagName(`script`),dt=ut[ut.length-1]||{src:``};lt=dt.src?dt.src.replace(/^[^\?]+\??/,``):``,$e=ct(`renderer`)}var ft=setInterval(st,100);try{((e===void 0?`undefined`:Xe(e))!==`object`||t===void 0)&&!(typeof define==`function`&&define.amd)&&(window.bodymovin=W)}catch{}var pt=function(){var e={},t={};e.registerModifier=n,e.getModifier=r;function n(e,n){t[e]||(t[e]=n)}function r(e,n,r){return new t[e](n,r)}return e}();function mt(){}mt.prototype.initModifierProperties=function(){},mt.prototype.addShapeToModifier=function(){},mt.prototype.addShape=function(e){if(!this.closed){e.sh.container.addDynamicProperty(e.sh);var t={shape:e.sh,data:e,localShapeCollection:U.newShapeCollection()};this.shapes.push(t),this.addShapeToModifier(t),this._isAnimated&&e.setAsAnimated()}},mt.prototype.init=function(e,t){this.shapes=[],this.elem=e,this.initDynamicPropertyContainer(e),this.initModifierProperties(e,t),this.frameId=i,this.closed=!1,this.k=!1,this.dynamicProperties.length?this.k=!0:this.getValue(!0)},mt.prototype.processKeys=function(){this.elem.globalData.frameId!==this.frameId&&(this.frameId=this.elem.globalData.frameId,this.iterateDynamicProperties())},u([Ue],mt);function ht(){}u([mt],ht),ht.prototype.initModifierProperties=function(e,t){this.s=H.getProp(e,t.s,0,.01,this),this.e=H.getProp(e,t.e,0,.01,this),this.o=H.getProp(e,t.o,0,0,this),this.sValue=0,this.eValue=0,this.getValue=this.processKeys,this.m=t.m,this._isAnimated=!!this.s.effectsSequence.length||!!this.e.effectsSequence.length||!!this.o.effectsSequence.length},ht.prototype.addShapeToModifier=function(e){e.pathsData=[]},ht.prototype.calculateShapeEdges=function(e,t,n,r,i){var a=[];t<=1?a.push({s:e,e:t}):e>=1?a.push({s:e-1,e:t-1}):(a.push({s:e,e:1}),a.push({s:0,e:t-1}));var o=[],s,c=a.length,l;for(s=0;s<c;s+=1)if(l=a[s],!(l.e*i<r||l.s*i>r+n)){var u=l.s*i<=r?0:(l.s*i-r)/n,d=l.e*i>=r+n?1:(l.e*i-r)/n;o.push([u,d])}return o.length||o.push([0,0]),o},ht.prototype.releasePathsData=function(e){var t,n=e.length;for(t=0;t<n;t+=1)De.release(e[t]);return e.length=0,e},ht.prototype.processShapes=function(e){var t,n;if(this._mdf||e){var r=this.o.v%360/360;if(r<0&&(r+=1),t=this.s.v>1?1+r:this.s.v<0?0+r:this.s.v+r,n=this.e.v>1?1+r:this.e.v<0?0+r:this.e.v+r,t>n){var i=t;t=n,n=i}t=Math.round(t*1e4)*1e-4,n=Math.round(n*1e4)*1e-4,this.sValue=t,this.eValue=n}else t=this.sValue,n=this.eValue;var a,o,s=this.shapes.length,c,l,u,d,f,p=0;if(n===t)for(o=0;o<s;o+=1)this.shapes[o].localShapeCollection.releaseShapes(),this.shapes[o].shape._mdf=!0,this.shapes[o].shape.paths=this.shapes[o].localShapeCollection,this._mdf&&(this.shapes[o].pathsData.length=0);else if(!(n===1&&t===0||n===0&&t===1)){var m=[],h,g;for(o=0;o<s;o+=1)if(h=this.shapes[o],!h.shape._mdf&&!this._mdf&&!e&&this.m!==2)h.shape.paths=h.localShapeCollection;else{if(a=h.shape.paths,l=a._length,f=0,!h.shape._mdf&&h.pathsData.length)f=h.totalShapeLength;else{for(u=this.releasePathsData(h.pathsData),c=0;c<l;c+=1)d=V.getSegmentsLength(a.shapes[c]),u.push(d),f+=d.totalLength;h.totalShapeLength=f,h.pathsData=u}p+=f,h.shape._mdf=!0}var _=t,v=n,y=0,b;for(o=s-1;o>=0;--o)if(h=this.shapes[o],h.shape._mdf){for(g=h.localShapeCollection,g.releaseShapes(),this.m===2&&s>1?(b=this.calculateShapeEdges(t,n,h.totalShapeLength,y,p),y+=h.totalShapeLength):b=[[_,v]],l=b.length,c=0;c<l;c+=1){_=b[c][0],v=b[c][1],m.length=0,v<=1?m.push({s:h.totalShapeLength*_,e:h.totalShapeLength*v}):_>=1?m.push({s:h.totalShapeLength*(_-1),e:h.totalShapeLength*(v-1)}):(m.push({s:h.totalShapeLength*_,e:h.totalShapeLength}),m.push({s:0,e:h.totalShapeLength*(v-1)}));var x=this.addShapes(h,m[0]);if(m[0].s!==m[0].e){if(m.length>1){if(h.shape.paths.shapes[h.shape.paths._length-1].c){var S=x.pop();this.addPaths(x,g),x=this.addShapes(h,m[1],S)}else this.addPaths(x,g),x=this.addShapes(h,m[1])}this.addPaths(x,g)}}h.shape.paths=g}}else if(this._mdf)for(o=0;o<s;o+=1)this.shapes[o].pathsData.length=0,this.shapes[o].shape._mdf=!0},ht.prototype.addPaths=function(e,t){var n,r=e.length;for(n=0;n<r;n+=1)t.addShape(e[n])},ht.prototype.addSegment=function(e,t,n,r,i,a,o){i.setXYAt(t[0],t[1],`o`,a),i.setXYAt(n[0],n[1],`i`,a+1),o&&i.setXYAt(e[0],e[1],`v`,a),i.setXYAt(r[0],r[1],`v`,a+1)},ht.prototype.addSegmentFromArray=function(e,t,n,r){t.setXYAt(e[1],e[5],`o`,n),t.setXYAt(e[2],e[6],`i`,n+1),r&&t.setXYAt(e[0],e[4],`v`,n),t.setXYAt(e[3],e[7],`v`,n+1)},ht.prototype.addShapes=function(e,t,n){var r=e.pathsData,i=e.shape.paths.shapes,a,o=e.shape.paths._length,s,c,l=0,u,d,f,p,m=[],h,g=!0;for(n?(d=n._length,h=n._length):(n=Ke.newElement(),d=0,h=0),m.push(n),a=0;a<o;a+=1){for(f=r[a].lengths,n.c=i[a].c,c=i[a].c?f.length:f.length+1,s=1;s<c;s+=1)if(u=f[s-1],l+u.addedLength<t.s)l+=u.addedLength,n.c=!1;else if(l>t.e){n.c=!1;break}else t.s<=l&&t.e>=l+u.addedLength?(this.addSegment(i[a].v[s-1],i[a].o[s-1],i[a].i[s],i[a].v[s],n,d,g),g=!1):(p=V.getNewSegment(i[a].v[s-1],i[a].v[s],i[a].o[s-1],i[a].i[s],(t.s-l)/u.addedLength,(t.e-l)/u.addedLength,f[s-1]),this.addSegmentFromArray(p,n,d,g),g=!1,n.c=!1),l+=u.addedLength,d+=1;if(i[a].c&&f.length){if(u=f[s-1],l<=t.e){var _=f[s-1].addedLength;t.s<=l&&t.e>=l+_?(this.addSegment(i[a].v[s-1],i[a].o[s-1],i[a].i[0],i[a].v[0],n,d,g),g=!1):(p=V.getNewSegment(i[a].v[s-1],i[a].v[0],i[a].o[s-1],i[a].i[0],(t.s-l)/_,(t.e-l)/_,f[s-1]),this.addSegmentFromArray(p,n,d,g),g=!1,n.c=!1)}else n.c=!1;l+=u.addedLength,d+=1}if(n._length&&(n.setXYAt(n.v[h][0],n.v[h][1],`i`,h),n.setXYAt(n.v[n._length-1][0],n.v[n._length-1][1],`o`,n._length-1)),l>t.e)break;a<o-1&&(n=Ke.newElement(),g=!0,m.push(n),d=0)}return m};function gt(){}u([mt],gt),gt.prototype.initModifierProperties=function(e,t){this.getValue=this.processKeys,this.amount=H.getProp(e,t.a,0,null,this),this._isAnimated=!!this.amount.effectsSequence.length},gt.prototype.processPath=function(e,t){var n=t/100,r=[0,0],i=e._length,a=0;for(a=0;a<i;a+=1)r[0]+=e.v[a][0],r[1]+=e.v[a][1];r[0]/=i,r[1]/=i;var o=Ke.newElement();o.c=e.c;var s,c,l,u,d,f;for(a=0;a<i;a+=1)s=e.v[a][0]+(r[0]-e.v[a][0])*n,c=e.v[a][1]+(r[1]-e.v[a][1])*n,l=e.o[a][0]+(r[0]-e.o[a][0])*-n,u=e.o[a][1]+(r[1]-e.o[a][1])*-n,d=e.i[a][0]+(r[0]-e.i[a][0])*-n,f=e.i[a][1]+(r[1]-e.i[a][1])*-n,o.setTripleAt(s,c,l,u,d,f,a);return o},gt.prototype.processShapes=function(e){var t,n,r=this.shapes.length,i,a,o=this.amount.v;if(o!==0){var s,c;for(n=0;n<r;n+=1){if(s=this.shapes[n],c=s.localShapeCollection,s.shape._mdf||this._mdf||e)for(c.releaseShapes(),s.shape._mdf=!0,t=s.shape.paths.shapes,a=s.shape.paths._length,i=0;i<a;i+=1)c.addShape(this.processPath(t[i],o));s.shape.paths=s.localShapeCollection}}this.dynamicProperties.length||(this._mdf=!1)};var _t=function(){var e=[0,0];function t(e){var t=this._mdf;this.iterateDynamicProperties(),this._mdf=this._mdf||t,this.a&&e.translate(-this.a.v[0],-this.a.v[1],this.a.v[2]),this.s&&e.scale(this.s.v[0],this.s.v[1],this.s.v[2]),this.sk&&e.skewFromAxis(-this.sk.v,this.sa.v),this.r?e.rotate(-this.r.v):e.rotateZ(-this.rz.v).rotateY(this.ry.v).rotateX(this.rx.v).rotateZ(-this.or.v[2]).rotateY(this.or.v[1]).rotateX(this.or.v[0]),this.data.p.s?this.data.p.z?e.translate(this.px.v,this.py.v,-this.pz.v):e.translate(this.px.v,this.py.v,0):e.translate(this.p.v[0],this.p.v[1],-this.p.v[2])}function n(t){if(this.elem.globalData.frameId!==this.frameId){if(this._isDirty&&(this.precalculateMatrix(),this._isDirty=!1),this.iterateDynamicProperties(),this._mdf||t){var n;if(this.v.cloneFromProps(this.pre.props),this.appliedTransformations<1&&this.v.translate(-this.a.v[0],-this.a.v[1],this.a.v[2]),this.appliedTransformations<2&&this.v.scale(this.s.v[0],this.s.v[1],this.s.v[2]),this.sk&&this.appliedTransformations<3&&this.v.skewFromAxis(-this.sk.v,this.sa.v),this.r&&this.appliedTransformations<4?this.v.rotate(-this.r.v):!this.r&&this.appliedTransformations<4&&this.v.rotateZ(-this.rz.v).rotateY(this.ry.v).rotateX(this.rx.v).rotateZ(-this.or.v[2]).rotateY(this.or.v[1]).rotateX(this.or.v[0]),this.autoOriented){var r,i;if(n=this.elem.globalData.frameRate,this.p&&this.p.keyframes&&this.p.getValueAtTime)this.p._caching.lastFrame+this.p.offsetTime<=this.p.keyframes[0].t?(r=this.p.getValueAtTime((this.p.keyframes[0].t+.01)/n,0),i=this.p.getValueAtTime(this.p.keyframes[0].t/n,0)):this.p._caching.lastFrame+this.p.offsetTime>=this.p.keyframes[this.p.keyframes.length-1].t?(r=this.p.getValueAtTime(this.p.keyframes[this.p.keyframes.length-1].t/n,0),i=this.p.getValueAtTime((this.p.keyframes[this.p.keyframes.length-1].t-.05)/n,0)):(r=this.p.pv,i=this.p.getValueAtTime((this.p._caching.lastFrame+this.p.offsetTime-.01)/n,this.p.offsetTime));else if(this.px&&this.px.keyframes&&this.py.keyframes&&this.px.getValueAtTime&&this.py.getValueAtTime){r=[],i=[];var a=this.px,o=this.py;a._caching.lastFrame+a.offsetTime<=a.keyframes[0].t?(r[0]=a.getValueAtTime((a.keyframes[0].t+.01)/n,0),r[1]=o.getValueAtTime((o.keyframes[0].t+.01)/n,0),i[0]=a.getValueAtTime(a.keyframes[0].t/n,0),i[1]=o.getValueAtTime(o.keyframes[0].t/n,0)):a._caching.lastFrame+a.offsetTime>=a.keyframes[a.keyframes.length-1].t?(r[0]=a.getValueAtTime(a.keyframes[a.keyframes.length-1].t/n,0),r[1]=o.getValueAtTime(o.keyframes[o.keyframes.length-1].t/n,0),i[0]=a.getValueAtTime((a.keyframes[a.keyframes.length-1].t-.01)/n,0),i[1]=o.getValueAtTime((o.keyframes[o.keyframes.length-1].t-.01)/n,0)):(r=[a.pv,o.pv],i[0]=a.getValueAtTime((a._caching.lastFrame+a.offsetTime-.01)/n,a.offsetTime),i[1]=o.getValueAtTime((o._caching.lastFrame+o.offsetTime-.01)/n,o.offsetTime))}else i=e,r=i;this.v.rotate(-Math.atan2(r[1]-i[1],r[0]-i[0]))}this.data.p&&this.data.p.s?this.data.p.z?this.v.translate(this.px.v,this.py.v,-this.pz.v):this.v.translate(this.px.v,this.py.v,0):this.v.translate(this.p.v[0],this.p.v[1],-this.p.v[2])}this.frameId=this.elem.globalData.frameId}}function r(){if(this.appliedTransformations=0,this.pre.reset(),!this.a.effectsSequence.length)this.pre.translate(-this.a.v[0],-this.a.v[1],this.a.v[2]),this.appliedTransformations=1;else return;if(!this.s.effectsSequence.length)this.pre.scale(this.s.v[0],this.s.v[1],this.s.v[2]),this.appliedTransformations=2;else return;if(this.sk){if(!this.sk.effectsSequence.length&&!this.sa.effectsSequence.length)this.pre.skewFromAxis(-this.sk.v,this.sa.v),this.appliedTransformations=3;else return}this.r?this.r.effectsSequence.length||(this.pre.rotate(-this.r.v),this.appliedTransformations=4):!this.rz.effectsSequence.length&&!this.ry.effectsSequence.length&&!this.rx.effectsSequence.length&&!this.or.effectsSequence.length&&(this.pre.rotateZ(-this.rz.v).rotateY(this.ry.v).rotateX(this.rx.v).rotateZ(-this.or.v[2]).rotateY(this.or.v[1]).rotateX(this.or.v[0]),this.appliedTransformations=4)}function i(){}function a(e){this._addDynamicProperty(e),this.elem.addDynamicProperty(e),this._isDirty=!0}function o(e,t,n){if(this.elem=e,this.frameId=-1,this.propType=`transform`,this.data=t,this.v=new Ye,this.pre=new Ye,this.appliedTransformations=0,this.initDynamicPropertyContainer(n||e),t.p&&t.p.s?(this.px=H.getProp(e,t.p.x,0,0,this),this.py=H.getProp(e,t.p.y,0,0,this),t.p.z&&(this.pz=H.getProp(e,t.p.z,0,0,this))):this.p=H.getProp(e,t.p||{k:[0,0,0]},1,0,this),t.rx){if(this.rx=H.getProp(e,t.rx,0,D,this),this.ry=H.getProp(e,t.ry,0,D,this),this.rz=H.getProp(e,t.rz,0,D,this),t.or.k[0].ti){var r,i=t.or.k.length;for(r=0;r<i;r+=1)t.or.k[r].to=null,t.or.k[r].ti=null}this.or=H.getProp(e,t.or,1,D,this),this.or.sh=!0}else this.r=H.getProp(e,t.r||{k:0},0,D,this);t.sk&&(this.sk=H.getProp(e,t.sk,0,D,this),this.sa=H.getProp(e,t.sa,0,D,this)),this.a=H.getProp(e,t.a||{k:[0,0,0]},1,0,this),this.s=H.getProp(e,t.s||{k:[100,100,100]},1,.01,this),this.o=t.o?H.getProp(e,t.o,0,.01,e):{_mdf:!1,v:1},this._isDirty=!0,this.dynamicProperties.length||this.getValue(!0)}o.prototype={applyToMatrix:t,getValue:n,precalculateMatrix:r,autoOrient:i},u([Ue],o),o.prototype.addDynamicProperty=a,o.prototype._addDynamicProperty=Ue.prototype.addDynamicProperty;function s(e,t,n){return new o(e,t,n)}return{getTransformProperty:s}}();function vt(){}u([mt],vt),vt.prototype.initModifierProperties=function(e,t){this.getValue=this.processKeys,this.c=H.getProp(e,t.c,0,null,this),this.o=H.getProp(e,t.o,0,null,this),this.tr=_t.getTransformProperty(e,t.tr,this),this.so=H.getProp(e,t.tr.so,0,.01,this),this.eo=H.getProp(e,t.tr.eo,0,.01,this),this.data=t,this.dynamicProperties.length||this.getValue(!0),this._isAnimated=!!this.dynamicProperties.length,this.pMatrix=new Ye,this.rMatrix=new Ye,this.sMatrix=new Ye,this.tMatrix=new Ye,this.matrix=new Ye},vt.prototype.applyTransforms=function(e,t,n,r,i,a){var o=a?-1:1,s=r.s.v[0]+(1-r.s.v[0])*(1-i),c=r.s.v[1]+(1-r.s.v[1])*(1-i);e.translate(r.p.v[0]*o*i,r.p.v[1]*o*i,r.p.v[2]),t.translate(-r.a.v[0],-r.a.v[1],r.a.v[2]),t.rotate(-r.r.v*o*i),t.translate(r.a.v[0],r.a.v[1],r.a.v[2]),n.translate(-r.a.v[0],-r.a.v[1],r.a.v[2]),n.scale(a?1/s:s,a?1/c:c),n.translate(r.a.v[0],r.a.v[1],r.a.v[2])},vt.prototype.init=function(e,t,n,r){for(this.elem=e,this.arr=t,this.pos=n,this.elemsData=r,this._currentCopies=0,this._elements=[],this._groups=[],this.frameId=-1,this.initDynamicPropertyContainer(e),this.initModifierProperties(e,t[n]);n>0;)--n,this._elements.unshift(t[n]);this.dynamicProperties.length?this.k=!0:this.getValue(!0)},vt.prototype.resetElements=function(e){var t,n=e.length;for(t=0;t<n;t+=1)e[t]._processed=!1,e[t].ty===`gr`&&this.resetElements(e[t].it)},vt.prototype.cloneElements=function(e){var t=JSON.parse(JSON.stringify(e));return this.resetElements(t),t},vt.prototype.changeGroupRender=function(e,t){var n,r=e.length;for(n=0;n<r;n+=1)e[n]._render=t,e[n].ty===`gr`&&this.changeGroupRender(e[n].it,t)},vt.prototype.processShapes=function(e){var t,n,r,i,a,o=!1;if(this._mdf||e){var s=Math.ceil(this.c.v);if(this._groups.length<s){for(;this._groups.length<s;){var c={it:this.cloneElements(this._elements),ty:`gr`};c.it.push({a:{a:0,ix:1,k:[0,0]},nm:`Transform`,o:{a:0,ix:7,k:100},p:{a:0,ix:2,k:[0,0]},r:{a:1,ix:6,k:[{s:0,e:0,t:0},{s:0,e:0,t:1}]},s:{a:0,ix:3,k:[100,100]},sa:{a:0,ix:5,k:0},sk:{a:0,ix:4,k:0},ty:`tr`}),this.arr.splice(0,0,c),this._groups.splice(0,0,c),this._currentCopies+=1}this.elem.reloadShapes(),o=!0}a=0;var l;for(r=0;r<=this._groups.length-1;r+=1){if(l=a<s,this._groups[r]._render=l,this.changeGroupRender(this._groups[r].it,l),!l){var u=this.elemsData[r].it,d=u[u.length-1];d.transform.op.v===0?d.transform.op._mdf=!1:(d.transform.op._mdf=!0,d.transform.op.v=0)}a+=1}this._currentCopies=s;var f=this.o.v,p=f%1,m=f>0?Math.floor(f):Math.ceil(f),h=this.pMatrix.props,g=this.rMatrix.props,_=this.sMatrix.props;this.pMatrix.reset(),this.rMatrix.reset(),this.sMatrix.reset(),this.tMatrix.reset(),this.matrix.reset();var v=0;if(f>0){for(;v<m;)this.applyTransforms(this.pMatrix,this.rMatrix,this.sMatrix,this.tr,1,!1),v+=1;p&&(this.applyTransforms(this.pMatrix,this.rMatrix,this.sMatrix,this.tr,p,!1),v+=p)}else if(f<0){for(;v>m;)this.applyTransforms(this.pMatrix,this.rMatrix,this.sMatrix,this.tr,1,!0),--v;p&&(this.applyTransforms(this.pMatrix,this.rMatrix,this.sMatrix,this.tr,-p,!0),v-=p)}r=this.data.m===1?0:this._currentCopies-1,i=this.data.m===1?1:-1,a=this._currentCopies;for(var y,b;a;){if(t=this.elemsData[r].it,n=t[t.length-1].transform.mProps.v.props,b=n.length,t[t.length-1].transform.mProps._mdf=!0,t[t.length-1].transform.op._mdf=!0,t[t.length-1].transform.op.v=this._currentCopies===1?this.so.v:this.so.v+(this.eo.v-this.so.v)*(r/(this._currentCopies-1)),v!==0){for((r!==0&&i===1||r!==this._currentCopies-1&&i===-1)&&this.applyTransforms(this.pMatrix,this.rMatrix,this.sMatrix,this.tr,1,!1),this.matrix.transform(g[0],g[1],g[2],g[3],g[4],g[5],g[6],g[7],g[8],g[9],g[10],g[11],g[12],g[13],g[14],g[15]),this.matrix.transform(_[0],_[1],_[2],_[3],_[4],_[5],_[6],_[7],_[8],_[9],_[10],_[11],_[12],_[13],_[14],_[15]),this.matrix.transform(h[0],h[1],h[2],h[3],h[4],h[5],h[6],h[7],h[8],h[9],h[10],h[11],h[12],h[13],h[14],h[15]),y=0;y<b;y+=1)n[y]=this.matrix.props[y];this.matrix.reset()}else for(this.matrix.reset(),y=0;y<b;y+=1)n[y]=this.matrix.props[y];v+=1,--a,r+=i}}else for(a=this._currentCopies,r=0,i=1;a;)t=this.elemsData[r].it,n=t[t.length-1].transform.mProps.v.props,t[t.length-1].transform.mProps._mdf=!1,t[t.length-1].transform.op._mdf=!1,--a,r+=i;return o},vt.prototype.addShape=function(){};function yt(){}u([mt],yt),yt.prototype.initModifierProperties=function(e,t){this.getValue=this.processKeys,this.rd=H.getProp(e,t.r,0,null,this),this._isAnimated=!!this.rd.effectsSequence.length},yt.prototype.processPath=function(e,t){var n=Ke.newElement();n.c=e.c;var r,i=e._length,a,o,s,c,l,u,d=0,f,p,m,h,g,_;for(r=0;r<i;r+=1)a=e.v[r],s=e.o[r],o=e.i[r],a[0]===s[0]&&a[1]===s[1]&&a[0]===o[0]&&a[1]===o[1]?(r===0||r===i-1)&&!e.c?(n.setTripleAt(a[0],a[1],s[0],s[1],o[0],o[1],d),d+=1):(c=r===0?e.v[i-1]:e.v[r-1],l=Math.sqrt((a[0]-c[0])**2+(a[1]-c[1])**2),u=l?Math.min(l/2,t)/l:0,g=a[0]+(c[0]-a[0])*u,f=g,_=a[1]-(a[1]-c[1])*u,p=_,m=f-(f-a[0])*O,h=p-(p-a[1])*O,n.setTripleAt(f,p,m,h,g,_,d),d+=1,c=r===i-1?e.v[0]:e.v[r+1],l=Math.sqrt((a[0]-c[0])**2+(a[1]-c[1])**2),u=l?Math.min(l/2,t)/l:0,m=a[0]+(c[0]-a[0])*u,f=m,h=a[1]+(c[1]-a[1])*u,p=h,g=f-(f-a[0])*O,_=p-(p-a[1])*O,n.setTripleAt(f,p,m,h,g,_,d),d+=1):(n.setTripleAt(e.v[r][0],e.v[r][1],e.o[r][0],e.o[r][1],e.i[r][0],e.i[r][1],d),d+=1);return n},yt.prototype.processShapes=function(e){var t,n,r=this.shapes.length,i,a,o=this.rd.v;if(o!==0){var s,c;for(n=0;n<r;n+=1){if(s=this.shapes[n],c=s.localShapeCollection,s.shape._mdf||this._mdf||e)for(c.releaseShapes(),s.shape._mdf=!0,t=s.shape.paths.shapes,a=s.shape.paths._length,i=0;i<a;i+=1)c.addShape(this.processPath(t[i],o));s.shape.paths=s.localShapeCollection}}this.dynamicProperties.length||(this._mdf=!1)};function bt(e,t){return Math.abs(e-t)*1e5<=Math.min(Math.abs(e),Math.abs(t))}function xt(e){return Math.abs(e)<=1e-5}function St(e,t,n){return e*(1-n)+t*n}function Ct(e,t,n){return[St(e[0],t[0],n),St(e[1],t[1],n)]}function wt(e,t,n){if(e===0)return[];var r=t*t-4*e*n;if(r<0)return[];var i=-t/(2*e);if(r===0)return[i];var a=Math.sqrt(r)/(2*e);return[i-a,i+a]}function Tt(e,t,n,r){return[-e+3*t-3*n+r,3*e-6*t+3*n,-3*e+3*t,e]}function Et(e){return new Dt(e,e,e,e,!1)}function Dt(e,t,n,r,i){i&&Lt(e,t)&&(t=Ct(e,r,1/3)),i&&Lt(n,r)&&(n=Ct(e,r,2/3));var a=Tt(e[0],t[0],n[0],r[0]),o=Tt(e[1],t[1],n[1],r[1]);this.a=[a[0],o[0]],this.b=[a[1],o[1]],this.c=[a[2],o[2]],this.d=[a[3],o[3]],this.points=[e,t,n,r]}Dt.prototype.point=function(e){return[((this.a[0]*e+this.b[0])*e+this.c[0])*e+this.d[0],((this.a[1]*e+this.b[1])*e+this.c[1])*e+this.d[1]]},Dt.prototype.derivative=function(e){return[(3*e*this.a[0]+2*this.b[0])*e+this.c[0],(3*e*this.a[1]+2*this.b[1])*e+this.c[1]]},Dt.prototype.tangentAngle=function(e){var t=this.derivative(e);return Math.atan2(t[1],t[0])},Dt.prototype.normalAngle=function(e){var t=this.derivative(e);return Math.atan2(t[0],t[1])},Dt.prototype.inflectionPoints=function(){var e=this.a[1]*this.b[0]-this.a[0]*this.b[1];if(xt(e))return[];var t=-.5*(this.a[1]*this.c[0]-this.a[0]*this.c[1])/e,n=t*t-1/3*(this.b[1]*this.c[0]-this.b[0]*this.c[1])/e;if(n<0)return[];var r=Math.sqrt(n);return xt(r)?r>0&&r<1?[t]:[]:[t-r,t+r].filter(function(e){return e>0&&e<1})},Dt.prototype.split=function(e){if(e<=0)return[Et(this.points[0]),this];if(e>=1)return[this,Et(this.points[this.points.length-1])];var t=Ct(this.points[0],this.points[1],e),n=Ct(this.points[1],this.points[2],e),r=Ct(this.points[2],this.points[3],e),i=Ct(t,n,e),a=Ct(n,r,e),o=Ct(i,a,e);return[new Dt(this.points[0],t,i,o,!0),new Dt(o,a,r,this.points[3],!0)]};function Ot(e,t){var n=e.points[0][t],r=e.points[e.points.length-1][t];if(n>r){var i=r;r=n,n=i}for(var a=wt(3*e.a[t],2*e.b[t],e.c[t]),o=0;o<a.length;o+=1)if(a[o]>0&&a[o]<1){var s=e.point(a[o])[t];s<n?n=s:s>r&&(r=s)}return{min:n,max:r}}Dt.prototype.bounds=function(){return{x:Ot(this,0),y:Ot(this,1)}},Dt.prototype.boundingBox=function(){var e=this.bounds();return{left:e.x.min,right:e.x.max,top:e.y.min,bottom:e.y.max,width:e.x.max-e.x.min,height:e.y.max-e.y.min,cx:(e.x.max+e.x.min)/2,cy:(e.y.max+e.y.min)/2}};function kt(e,t,n){var r=e.boundingBox();return{cx:r.cx,cy:r.cy,width:r.width,height:r.height,bez:e,t:(t+n)/2,t1:t,t2:n}}function At(e){var t=e.bez.split(.5);return[kt(t[0],e.t1,e.t),kt(t[1],e.t,e.t2)]}function jt(e,t){return Math.abs(e.cx-t.cx)*2<e.width+t.width&&Math.abs(e.cy-t.cy)*2<e.height+t.height}function Mt(e,t,n,r,i,a){if(jt(e,t)){if(n>=a||e.width<=r&&e.height<=r&&t.width<=r&&t.height<=r){i.push([e.t,t.t]);return}var o=At(e),s=At(t);Mt(o[0],s[0],n+1,r,i,a),Mt(o[0],s[1],n+1,r,i,a),Mt(o[1],s[0],n+1,r,i,a),Mt(o[1],s[1],n+1,r,i,a)}}Dt.prototype.intersections=function(e,t,n){t===void 0&&(t=2),n===void 0&&(n=7);var r=[];return Mt(kt(this,0,1),kt(e,0,1),0,t,r,n),r},Dt.shapeSegment=function(e,t){var n=(t+1)%e.length();return new Dt(e.v[t],e.o[t],e.i[n],e.v[n],!0)},Dt.shapeSegmentInverted=function(e,t){var n=(t+1)%e.length();return new Dt(e.v[n],e.i[n],e.o[t],e.v[t],!0)};function Nt(e,t){return[e[1]*t[2]-e[2]*t[1],e[2]*t[0]-e[0]*t[2],e[0]*t[1]-e[1]*t[0]]}function Pt(e,t,n,r){var i=[e[0],e[1],1],a=[t[0],t[1],1],o=[n[0],n[1],1],s=[r[0],r[1],1],c=Nt(Nt(i,a),Nt(o,s));return xt(c[2])?null:[c[0]/c[2],c[1]/c[2]]}function Ft(e,t,n){return[e[0]+Math.cos(t)*n,e[1]-Math.sin(t)*n]}function It(e,t){return Math.hypot(e[0]-t[0],e[1]-t[1])}function Lt(e,t){return bt(e[0],t[0])&&bt(e[1],t[1])}function Rt(){}u([mt],Rt),Rt.prototype.initModifierProperties=function(e,t){this.getValue=this.processKeys,this.amplitude=H.getProp(e,t.s,0,null,this),this.frequency=H.getProp(e,t.r,0,null,this),this.pointsType=H.getProp(e,t.pt,0,null,this),this._isAnimated=this.amplitude.effectsSequence.length!==0||this.frequency.effectsSequence.length!==0||this.pointsType.effectsSequence.length!==0};function zt(e,t,n,r,i,a,o){var s=n-Math.PI/2,c=n+Math.PI/2,l=t[0]+Math.cos(n)*r*i,u=t[1]-Math.sin(n)*r*i;e.setTripleAt(l,u,l+Math.cos(s)*a,u-Math.sin(s)*a,l+Math.cos(c)*o,u-Math.sin(c)*o,e.length())}function Bt(e,t){var n=[t[0]-e[0],t[1]-e[1]],r=-Math.PI*.5;return[Math.cos(r)*n[0]-Math.sin(r)*n[1],Math.sin(r)*n[0]+Math.cos(r)*n[1]]}function Vt(e,t){var n=t===0?e.length()-1:t-1,r=(t+1)%e.length(),i=e.v[n],a=e.v[r],o=Bt(i,a);return Math.atan2(0,1)-Math.atan2(o[1],o[0])}function Ht(e,t,n,r,i,a,o){var s=Vt(t,n),c=t.v[n%t._length],l=t.v[n===0?t._length-1:n-1],u=t.v[(n+1)%t._length],d=a===2?Math.sqrt((c[0]-l[0])**2+(c[1]-l[1])**2):0,f=a===2?Math.sqrt((c[0]-u[0])**2+(c[1]-u[1])**2):0;zt(e,t.v[n%t._length],s,o,r,f/((i+1)*2),d/((i+1)*2),a)}function Ut(e,t,n,r,i,a){for(var o=0;o<r;o+=1){var s=(o+1)/(r+1),c=i===2?Math.sqrt((t.points[3][0]-t.points[0][0])**2+(t.points[3][1]-t.points[0][1])**2):0,l=t.normalAngle(s);zt(e,t.point(s),l,a,n,c/((r+1)*2),c/((r+1)*2),i),a=-a}return a}Rt.prototype.processPath=function(e,t,n,r){var i=e._length,a=Ke.newElement();if(a.c=e.c,e.c||--i,i===0)return a;var o=-1,s=Dt.shapeSegment(e,0);Ht(a,e,0,t,n,r,o);for(var c=0;c<i;c+=1)o=Ut(a,s,t,n,r,-o),s=c===i-1&&!e.c?null:Dt.shapeSegment(e,(c+1)%i),Ht(a,e,c+1,t,n,r,o);return a},Rt.prototype.processShapes=function(e){var t,n,r=this.shapes.length,i,a,o=this.amplitude.v,s=Math.max(0,Math.round(this.frequency.v)),c=this.pointsType.v;if(o!==0){var l,u;for(n=0;n<r;n+=1){if(l=this.shapes[n],u=l.localShapeCollection,l.shape._mdf||this._mdf||e)for(u.releaseShapes(),l.shape._mdf=!0,t=l.shape.paths.shapes,a=l.shape.paths._length,i=0;i<a;i+=1)u.addShape(this.processPath(t[i],o,s,c));l.shape.paths=l.localShapeCollection}}this.dynamicProperties.length||(this._mdf=!1)};function q(e,t,n){var r=Math.atan2(t[0]-e[0],t[1]-e[1]);return[Ft(e,r,n),Ft(t,r,n)]}function Wt(e,t){var n,r,i,a,o,s,c=q(e.points[0],e.points[1],t);n=c[0],r=c[1],c=q(e.points[1],e.points[2],t),i=c[0],a=c[1],c=q(e.points[2],e.points[3],t),o=c[0],s=c[1];var l=Pt(n,r,i,a);l===null&&(l=r);var u=Pt(o,s,i,a);return u===null&&(u=o),new Dt(n,l,u,s)}function Gt(e,t,n,r,i){var a=t.points[3],o=n.points[0];if(r===3||Lt(a,o))return a;if(r===2){var s=-t.tangentAngle(1),c=-n.tangentAngle(0)+Math.PI,l=Pt(a,Ft(a,s+Math.PI/2,100),o,Ft(o,s+Math.PI/2,100)),u=l?It(l,a):It(a,o)/2,d=Ft(a,s,2*u*O);return e.setXYAt(d[0],d[1],`o`,e.length()-1),d=Ft(o,c,2*u*O),e.setTripleAt(o[0],o[1],o[0],o[1],d[0],d[1],e.length()),o}var f=Pt(Lt(a,t.points[2])?t.points[0]:t.points[2],a,o,Lt(o,n.points[1])?n.points[3]:n.points[1]);return f&&It(f,a)<i?(e.setTripleAt(f[0],f[1],f[0],f[1],f[0],f[1],e.length()),f):a}function Kt(e,t){var n=e.intersections(t);return n.length&&bt(n[0][0],1)&&n.shift(),n.length?n[0]:null}function qt(e,t){var n=e.slice(),r=t.slice(),i=Kt(e[e.length-1],t[0]);return i&&(n[e.length-1]=e[e.length-1].split(i[0])[0],r[0]=t[0].split(i[1])[1]),e.length>1&&t.length>1&&(i=Kt(e[0],t[t.length-1]),i)?[[e[0].split(i[0])[0]],[t[t.length-1].split(i[1])[1]]]:[n,r]}function Jt(e){for(var t,n=1;n<e.length;n+=1)t=qt(e[n-1],e[n]),e[n-1]=t[0],e[n]=t[1];return e.length>1&&(t=qt(e[e.length-1],e[0]),e[e.length-1]=t[0],e[0]=t[1]),e}function Yt(e,t){var n=e.inflectionPoints(),r,i,a,o;if(n.length===0)return[Wt(e,t)];if(n.length===1||bt(n[1],1))return a=e.split(n[0]),r=a[0],i=a[1],[Wt(r,t),Wt(i,t)];a=e.split(n[0]),r=a[0];var s=(n[1]-n[0])/(1-n[0]);return a=a[1].split(s),o=a[0],i=a[1],[Wt(r,t),Wt(o,t),Wt(i,t)]}function Xt(){}u([mt],Xt),Xt.prototype.initModifierProperties=function(e,t){this.getValue=this.processKeys,this.amount=H.getProp(e,t.a,0,null,this),this.miterLimit=H.getProp(e,t.ml,0,null,this),this.lineJoin=t.lj,this._isAnimated=this.amount.effectsSequence.length!==0},Xt.prototype.processPath=function(e,t,n,r){var i=Ke.newElement();i.c=e.c;var a=e.length();e.c||--a;var o,s,c,l=[];for(o=0;o<a;o+=1)c=Dt.shapeSegment(e,o),l.push(Yt(c,t));if(!e.c)for(o=a-1;o>=0;--o)c=Dt.shapeSegmentInverted(e,o),l.push(Yt(c,t));l=Jt(l);var u=null,d=null;for(o=0;o<l.length;o+=1){var f=l[o];for(d&&(u=Gt(i,d,f[0],n,r)),d=f[f.length-1],s=0;s<f.length;s+=1)c=f[s],u&&Lt(c.points[0],u)?i.setXYAt(c.points[1][0],c.points[1][1],`o`,i.length()-1):i.setTripleAt(c.points[0][0],c.points[0][1],c.points[1][0],c.points[1][1],c.points[0][0],c.points[0][1],i.length()),i.setTripleAt(c.points[3][0],c.points[3][1],c.points[3][0],c.points[3][1],c.points[2][0],c.points[2][1],i.length()),u=c.points[3]}return l.length&&Gt(i,d,l[0][0],n,r),i},Xt.prototype.processShapes=function(e){var t,n,r=this.shapes.length,i,a,o=this.amount.v,s=this.miterLimit.v,c=this.lineJoin;if(o!==0){var l,u;for(n=0;n<r;n+=1){if(l=this.shapes[n],u=l.localShapeCollection,l.shape._mdf||this._mdf||e)for(u.releaseShapes(),l.shape._mdf=!0,t=l.shape.paths.shapes,a=l.shape.paths._length,i=0;i<a;i+=1)u.addShape(this.processPath(t[i],o,c,s));l.shape.paths=l.localShapeCollection}}this.dynamicProperties.length||(this._mdf=!1)};function Zt(e){for(var t=e.fStyle?e.fStyle.split(` `):[],n=`normal`,r=`normal`,i=t.length,a,o=0;o<i;o+=1)switch(a=t[o].toLowerCase(),a){case`italic`:r=`italic`;break;case`bold`:n=`700`;break;case`black`:n=`900`;break;case`medium`:n=`500`;break;case`regular`:case`normal`:n=`400`;break;case`light`:case`thin`:n=`200`}return{style:r,weight:e.fWeight||n}}var Qt=function(){var e={w:0,size:0,shapes:[],data:{shapes:[]}},t=[];t=t.concat([2304,2305,2306,2307,2362,2363,2364,2364,2366,2367,2368,2369,2370,2371,2372,2373,2374,2375,2376,2377,2378,2379,2380,2381,2382,2383,2387,2388,2389,2390,2391,2402,2403]);var n=127988,r=917631,i=917601,a=917626,o=65039,s=8205,c=127462,u=127487,d=[`d83cdffb`,`d83cdffc`,`d83cdffd`,`d83cdffe`,`d83cdfff`];function f(e){var t=e.split(`,`),n,r=t.length,i=[];for(n=0;n<r;n+=1)t[n]!==`sans-serif`&&t[n]!==`monospace`&&i.push(t[n]);return i.join(`,`)}function p(e,t){var n=l(`span`);n.setAttribute(`aria-hidden`,!0),n.style.fontFamily=t;var r=l(`span`);r.innerText=`giItT1WQy@!-/#`,n.style.position=`absolute`,n.style.left=`-10000px`,n.style.top=`-10000px`,n.style.fontSize=`300px`,n.style.fontVariant=`normal`,n.style.fontStyle=`normal`,n.style.fontWeight=`normal`,n.style.letterSpacing=`0`,n.appendChild(r),document.body.appendChild(n);var i=r.offsetWidth;return r.style.fontFamily=f(e)+`, `+t,{node:r,w:i,parent:n}}function m(){var e,t=this.fonts.length,n,r,i=t;for(e=0;e<t;e+=1)this.fonts[e].loaded?--i:this.fonts[e].fOrigin===`n`||this.fonts[e].origin===0?this.fonts[e].loaded=!0:(n=this.fonts[e].monoCase.node,r=this.fonts[e].monoCase.w,n.offsetWidth===r?(n=this.fonts[e].sansCase.node,r=this.fonts[e].sansCase.w,n.offsetWidth!==r&&(--i,this.fonts[e].loaded=!0)):(--i,this.fonts[e].loaded=!0),this.fonts[e].loaded&&(this.fonts[e].sansCase.parent.parentNode.removeChild(this.fonts[e].sansCase.parent),this.fonts[e].monoCase.parent.parentNode.removeChild(this.fonts[e].monoCase.parent)));i!==0&&Date.now()-this.initTime<5e3?setTimeout(this.checkLoadedFontsBinded,20):setTimeout(this.setIsLoadedBinded,10)}function h(e,t){var n=document.body&&t?`svg`:`canvas`,r,i=Zt(e);if(n===`svg`){var a=I(`text`);a.style.fontSize=`100px`,a.setAttribute(`font-family`,e.fFamily),a.setAttribute(`font-style`,i.style),a.setAttribute(`font-weight`,i.weight),a.textContent=`1`,e.fClass?(a.style.fontFamily=`inherit`,a.setAttribute(`class`,e.fClass)):a.style.fontFamily=e.fFamily,t.appendChild(a),r=a}else{var o=new OffscreenCanvas(500,500).getContext(`2d`);o.font=i.style+` `+i.weight+` 100px `+e.fFamily,r=o}function s(e){return n===`svg`?(r.textContent=e,r.getComputedTextLength()):r.measureText(e).width}return{measureText:s}}function g(e,t){if(!e){this.isLoaded=!0;return}if(this.chars){this.isLoaded=!0,this.fonts=e.list;return}if(!document.body){this.isLoaded=!0,e.list.forEach(function(e){e.helper=h(e),e.cache={}}),this.fonts=e.list;return}var n=e.list,r,i=n.length,a=i;for(r=0;r<i;r+=1){var o=!0,s,c;if(n[r].loaded=!1,n[r].monoCase=p(n[r].fFamily,`monospace`),n[r].sansCase=p(n[r].fFamily,`sans-serif`),!n[r].fPath)n[r].loaded=!0,--a;else if(n[r].fOrigin===`p`||n[r].origin===3){if(s=document.querySelectorAll(`style[f-forigin="p"][f-family="`+n[r].fFamily+`"], style[f-origin="3"][f-family="`+n[r].fFamily+`"]`),s.length>0&&(o=!1),o){var u=l(`style`);u.setAttribute(`f-forigin`,n[r].fOrigin),u.setAttribute(`f-origin`,n[r].origin),u.setAttribute(`f-family`,n[r].fFamily),u.type=`text/css`,u.innerText=`@font-face {font-family: `+n[r].fFamily+`; font-style: normal; src: url('`+n[r].fPath+`');}`,t.appendChild(u)}}else if(n[r].fOrigin===`g`||n[r].origin===1){for(s=document.querySelectorAll(`link[f-forigin="g"], link[f-origin="1"]`),c=0;c<s.length;c+=1)s[c].href.indexOf(n[r].fPath)!==-1&&(o=!1);if(o){var d=l(`link`);d.setAttribute(`f-forigin`,n[r].fOrigin),d.setAttribute(`f-origin`,n[r].origin),d.type=`text/css`,d.rel=`stylesheet`,d.href=n[r].fPath,document.body.appendChild(d)}}else if(n[r].fOrigin===`t`||n[r].origin===2){for(s=document.querySelectorAll(`script[f-forigin="t"], script[f-origin="2"]`),c=0;c<s.length;c+=1)n[r].fPath===s[c].src&&(o=!1);if(o){var f=l(`link`);f.setAttribute(`f-forigin`,n[r].fOrigin),f.setAttribute(`f-origin`,n[r].origin),f.setAttribute(`rel`,`stylesheet`),f.setAttribute(`href`,n[r].fPath),t.appendChild(f)}}n[r].helper=h(n[r],t),n[r].cache={},this.fonts.push(n[r])}a===0?this.isLoaded=!0:setTimeout(this.checkLoadedFonts.bind(this),100)}function _(e){if(e){this.chars||(this.chars=[]);var t,n=e.length,r,i=this.chars.length,a;for(t=0;t<n;t+=1){for(r=0,a=!1;r<i;)this.chars[r].style===e[t].style&&this.chars[r].fFamily===e[t].fFamily&&this.chars[r].ch===e[t].ch&&(a=!0),r+=1;a||(this.chars.push(e[t]),i+=1)}}}function v(t,n,r){for(var i=0,a=this.chars.length;i<a;){if(this.chars[i].ch===t&&this.chars[i].style===n&&this.chars[i].fFamily===r)return this.chars[i];i+=1}return(typeof t==`string`&&t.charCodeAt(0)!==13||!t)&&console&&console.warn&&!this._warned&&(this._warned=!0,console.warn(`Missing character from exported characters list: `,t,n,r)),e}function y(e,t,n){var r=this.getFontByName(t),i=e;if(!r.cache[i]){var a=r.helper;if(e===` `){var o=a.measureText(`|`+e+`|`),s=a.measureText(`||`);r.cache[i]=(o-s)/100}else r.cache[i]=a.measureText(e)/100}return r.cache[i]*n}function b(e){for(var t=0,n=this.fonts.length;t<n;){if(this.fonts[t].fName===e)return this.fonts[t];t+=1}return this.fonts[0]}function x(e){var t=0,n=e.charCodeAt(0);if(n>=55296&&n<=56319){var r=e.charCodeAt(1);r>=56320&&r<=57343&&(t=(n-55296)*1024+r-56320+65536)}return t}function S(e,t){var n=e.toString(16)+t.toString(16);return d.indexOf(n)!==-1}function C(e){return e===s}function w(e){return e===o}function T(e){var t=x(e);return t>=c&&t<=u}function E(e){return T(e.substr(0,2))&&T(e.substr(2,2))}function D(e){return t.indexOf(e)!==-1}function O(e,t){var o=x(e.substr(t,2));if(o!==n)return!1;var s=0;for(t+=2;s<5;){if(o=x(e.substr(t,2)),o<i||o>a)return!1;s+=1,t+=2}return x(e.substr(t,2))===r}function k(){this.isLoaded=!0}var A=function(){this.fonts=[],this.chars=null,this.typekitLoaded=0,this.isLoaded=!1,this._warned=!1,this.initTime=Date.now(),this.setIsLoadedBinded=this.setIsLoaded.bind(this),this.checkLoadedFontsBinded=this.checkLoadedFonts.bind(this)};return A.isModifier=S,A.isZeroWidthJoiner=C,A.isFlagEmoji=E,A.isRegionalCode=T,A.isCombinedCharacter=D,A.isRegionalFlag=O,A.isVariationSelector=w,A.BLACK_FLAG_CODE_POINT=n,A.prototype={addChars:_,addFonts:g,getCharData:v,getFontByName:b,measureText:y,checkLoadedFonts:m,setIsLoaded:k},A}();function $t(e){this.animationData=e}$t.prototype.getProp=function(e){return this.animationData.slots&&this.animationData.slots[e.sid]?Object.assign(e,this.animationData.slots[e.sid].p):e};function en(e){return new $t(e)}function tn(){}tn.prototype={initRenderable:function(){this.isInRange=!1,this.hidden=!1,this.isTransparent=!1,this.renderableComponents=[]},addRenderableComponent:function(e){this.renderableComponents.indexOf(e)===-1&&this.renderableComponents.push(e)},removeRenderableComponent:function(e){this.renderableComponents.indexOf(e)!==-1&&this.renderableComponents.splice(this.renderableComponents.indexOf(e),1)},prepareRenderableFrame:function(e){this.checkLayerLimits(e)},checkTransparency:function(){this.finalTransform.mProp.o.v<=0?!this.isTransparent&&this.globalData.renderConfig.hideOnTransparent&&(this.isTransparent=!0,this.hide()):this.isTransparent&&(this.isTransparent=!1,this.show())},checkLayerLimits:function(e){this.data.ip-this.data.st<=e&&this.data.op-this.data.st>e?this.isInRange!==!0&&(this.globalData._mdf=!0,this._mdf=!0,this.isInRange=!0,this.show()):this.isInRange!==!1&&(this.globalData._mdf=!0,this.isInRange=!1,this.hide())},renderRenderable:function(){var e,t=this.renderableComponents.length;for(e=0;e<t;e+=1)this.renderableComponents[e].renderFrame(this._isFirstFrame)},sourceRectAtTime:function(){return{top:0,left:0,width:100,height:100}},getLayerSize:function(){return this.data.ty===5?{w:this.data.textData.width,h:this.data.textData.height}:{w:this.data.width,h:this.data.height}}};var nn=function(){var e={0:`source-over`,1:`multiply`,2:`screen`,3:`overlay`,4:`darken`,5:`lighten`,6:`color-dodge`,7:`color-burn`,8:`hard-light`,9:`soft-light`,10:`difference`,11:`exclusion`,12:`hue`,13:`saturation`,14:`color`,15:`luminosity`};return function(t){return e[t]||``}}();function rn(e,t,n){this.p=H.getProp(t,e.v,0,0,n)}function an(e,t,n){this.p=H.getProp(t,e.v,0,0,n)}function on(e,t,n){this.p=H.getProp(t,e.v,1,0,n)}function sn(e,t,n){this.p=H.getProp(t,e.v,1,0,n)}function cn(e,t,n){this.p=H.getProp(t,e.v,0,0,n)}function ln(e,t,n){this.p=H.getProp(t,e.v,0,0,n)}function un(e,t,n){this.p=H.getProp(t,e.v,0,0,n)}function dn(){this.p={}}function fn(e,t){var n=e.ef||[];this.effectElements=[];var r,i=n.length,a;for(r=0;r<i;r+=1)a=new pn(n[r],t),this.effectElements.push(a)}function pn(e,t){this.init(e,t)}u([Ue],pn),pn.prototype.getValue=pn.prototype.iterateDynamicProperties,pn.prototype.init=function(e,t){this.data=e,this.effectElements=[],this.initDynamicPropertyContainer(t);var n,r=this.data.ef.length,i,a=this.data.ef;for(n=0;n<r;n+=1){switch(i=null,a[n].ty){case 0:i=new rn(a[n],t,this);break;case 1:i=new an(a[n],t,this);break;case 2:i=new on(a[n],t,this);break;case 3:i=new sn(a[n],t,this);break;case 4:case 7:i=new un(a[n],t,this);break;case 10:i=new cn(a[n],t,this);break;case 11:i=new ln(a[n],t,this);break;case 5:i=new fn(a[n],t,this);break;default:i=new dn(a[n],t,this)}i&&this.effectElements.push(i)}};function mn(){}mn.prototype={checkMasks:function(){if(!this.data.hasMask)return!1;for(var e=0,t=this.data.masksProperties.length;e<t;){if(this.data.masksProperties[e].mode!==`n`&&this.data.masksProperties[e].cl!==!1)return!0;e+=1}return!1},initExpressions:function(){var e=ue();if(e){var t=e(`layer`),n=e(`effects`),r=e(`shape`),i=e(`text`),a=e(`comp`);this.layerInterface=t(this),this.data.hasMask&&this.maskManager&&this.layerInterface.registerMaskInterface(this.maskManager);var o=n.createEffectsInterface(this,this.layerInterface);this.layerInterface.registerEffectsInterface(o),this.data.ty===0||this.data.xt?this.compInterface=a(this):this.data.ty===4?(this.layerInterface.shapeInterface=r(this.shapesData,this.itemsData,this.layerInterface),this.layerInterface.content=this.layerInterface.shapeInterface):this.data.ty===5&&(this.layerInterface.textInterface=i(this),this.layerInterface.text=this.layerInterface.textInterface)}},setBlendMode:function(){var e=nn(this.data.bm),t=this.baseElement||this.layerElement;t.style[`mix-blend-mode`]=e},initBaseData:function(e,t,n){this.globalData=t,this.comp=n,this.data=e,this.layerId=P(),this.data.sr||(this.data.sr=1),this.effectsManager=new fn(this.data,this,this.dynamicProperties)},getType:function(){return this.type},sourceRectAtTime:function(){}};function hn(){}hn.prototype={initFrame:function(){this._isFirstFrame=!1,this.dynamicProperties=[],this._mdf=!1},prepareProperties:function(e,t){var n,r=this.dynamicProperties.length;for(n=0;n<r;n+=1)(t||this._isParent&&this.dynamicProperties[n].propType===`transform`)&&(this.dynamicProperties[n].getValue(),this.dynamicProperties[n]._mdf&&(this.globalData._mdf=!0,this._mdf=!0))},addDynamicProperty:function(e){this.dynamicProperties.indexOf(e)===-1&&this.dynamicProperties.push(e)}};function gn(e,t,n){this.initFrame(),this.initRenderable(),this.assetData=t.getAssetData(e.refId),this.footageData=t.imageLoader.getAsset(this.assetData),this.initBaseData(e,t,n)}gn.prototype.prepareFrame=function(){},u([tn,mn,hn],gn),gn.prototype.getBaseElement=function(){return null},gn.prototype.renderFrame=function(){},gn.prototype.destroy=function(){},gn.prototype.initExpressions=function(){var e=ue();if(e){var t=e(`footage`);this.layerInterface=t(this)}},gn.prototype.getFootageData=function(){return this.footageData};function _n(e,t,n){this.initFrame(),this.initRenderable(),this.assetData=t.getAssetData(e.refId),this.initBaseData(e,t,n),this._isPlaying=!1,this._canPlay=!1;var r=this.globalData.getAssetsPath(this.assetData);this.audio=this.globalData.audioController.createAudio(r),this._currentTime=0,this.globalData.audioController.addAudio(this),this._volumeMultiplier=1,this._volume=1,this._previousVolume=null,this.tm=e.tm?H.getProp(this,e.tm,0,t.frameRate,this):{_placeholder:!0},this.lv=H.getProp(this,e.au&&e.au.lv?e.au.lv:{k:[100]},1,.01,this)}_n.prototype.prepareFrame=function(e){if(this.prepareRenderableFrame(e,!0),this.prepareProperties(e,!0),this.tm._placeholder)this._currentTime=e/this.data.sr;else{var t=this.tm.v;this._currentTime=t}this._volume=this.lv.v[0];var n=this._volume*this._volumeMultiplier;this._previousVolume!==n&&(this._previousVolume=n,this.audio.volume(n))},u([tn,mn,hn],_n),_n.prototype.renderFrame=function(){this.isInRange&&this._canPlay&&(this._isPlaying?(!this.audio.playing()||Math.abs(this._currentTime/this.globalData.frameRate-this.audio.seek())>.1)&&this.audio.seek(this._currentTime/this.globalData.frameRate):(this.audio.play(),this.audio.seek(this._currentTime/this.globalData.frameRate),this._isPlaying=!0))},_n.prototype.show=function(){},_n.prototype.hide=function(){this.audio.pause(),this._isPlaying=!1},_n.prototype.pause=function(){this.audio.pause(),this._isPlaying=!1,this._canPlay=!1},_n.prototype.resume=function(){this._canPlay=!0},_n.prototype.setRate=function(e){this.audio.rate(e)},_n.prototype.volume=function(e){this._volumeMultiplier=e,this._previousVolume=e*this._volume,this.audio.volume(this._previousVolume)},_n.prototype.getBaseElement=function(){return null},_n.prototype.destroy=function(){},_n.prototype.sourceRectAtTime=function(){},_n.prototype.initExpressions=function(){};function vn(){}vn.prototype.checkLayers=function(e){var t,n=this.layers.length,r;for(this.completeLayers=!0,t=n-1;t>=0;--t)this.elements[t]||(r=this.layers[t],r.ip-r.st<=e-this.layers[t].st&&r.op-r.st>e-this.layers[t].st&&this.buildItem(t)),this.completeLayers=this.elements[t]?this.completeLayers:!1;this.checkPendingElements()},vn.prototype.createItem=function(e){switch(e.ty){case 2:return this.createImage(e);case 0:return this.createComp(e);case 1:return this.createSolid(e);case 3:return this.createNull(e);case 4:return this.createShape(e);case 5:return this.createText(e);case 6:return this.createAudio(e);case 13:return this.createCamera(e);case 15:return this.createFootage(e);default:return this.createNull(e)}},vn.prototype.createCamera=function(){throw Error(`You're using a 3d camera. Try the html renderer.`)},vn.prototype.createAudio=function(e){return new _n(e,this.globalData,this)},vn.prototype.createFootage=function(e){return new gn(e,this.globalData,this)},vn.prototype.buildAllItems=function(){var e,t=this.layers.length;for(e=0;e<t;e+=1)this.buildItem(e);this.checkPendingElements()},vn.prototype.includeLayers=function(e){this.completeLayers=!1;var t,n=e.length,r,i=this.layers.length;for(t=0;t<n;t+=1)for(r=0;r<i;){if(this.layers[r].id===e[t].id){this.layers[r]=e[t];break}r+=1}},vn.prototype.setProjectInterface=function(e){this.globalData.projectInterface=e},vn.prototype.initItems=function(){this.globalData.progressiveLoad||this.buildAllItems()},vn.prototype.buildElementParenting=function(e,t,n){for(var r=this.elements,i=this.layers,a=0,o=i.length;a<o;)i[a].ind==t&&(!r[a]||r[a]===!0?(this.buildItem(a),this.addPendingElement(e)):(n.push(r[a]),r[a].setAsParent(),i[a].parent===void 0?e.setHierarchy(n):this.buildElementParenting(e,i[a].parent,n))),a+=1},vn.prototype.addPendingElement=function(e){this.pendingElements.push(e)},vn.prototype.searchExtraCompositions=function(e){var t,n=e.length;for(t=0;t<n;t+=1)if(e[t].xt){var r=this.createComp(e[t]);r.initExpressions(),this.globalData.projectInterface.registerComposition(r)}},vn.prototype.getElementById=function(e){var t,n=this.elements.length;for(t=0;t<n;t+=1)if(this.elements[t].data.ind===e)return this.elements[t];return null},vn.prototype.getElementByPath=function(e){var t=e.shift(),n;if(typeof t==`number`)n=this.elements[t];else{var r,i=this.elements.length;for(r=0;r<i;r+=1)if(this.elements[r].data.nm===t){n=this.elements[r];break}}return e.length===0?n:n.getElementByPath(e)},vn.prototype.setupGlobalData=function(e,t){this.globalData.fontManager=new Qt,this.globalData.slotManager=en(e),this.globalData.fontManager.addChars(e.chars),this.globalData.fontManager.addFonts(e.fonts,t),this.globalData.getAssetData=this.animationItem.getAssetData.bind(this.animationItem),this.globalData.getAssetsPath=this.animationItem.getAssetsPath.bind(this.animationItem),this.globalData.imageLoader=this.animationItem.imagePreloader,this.globalData.audioController=this.animationItem.audioController,this.globalData.frameId=0,this.globalData.frameRate=e.fr,this.globalData.nm=e.nm,this.globalData.compSize={w:e.w,h:e.h}};var yn={TRANSFORM_EFFECT:`transformEFfect`};function bn(){}bn.prototype={initTransform:function(){var e=new Ye;this.finalTransform={mProp:this.data.ks?_t.getTransformProperty(this,this.data.ks,this):{o:0},_matMdf:!1,_localMatMdf:!1,_opMdf:!1,mat:e,localMat:e,localOpacity:1},this.data.ao&&(this.finalTransform.mProp.autoOriented=!0),this.data.ty},renderTransform:function(){if(this.finalTransform._opMdf=this.finalTransform.mProp.o._mdf||this._isFirstFrame,this.finalTransform._matMdf=this.finalTransform.mProp._mdf||this._isFirstFrame,this.hierarchy){var e,t=this.finalTransform.mat,n=0,r=this.hierarchy.length;if(!this.finalTransform._matMdf)for(;n<r;){if(this.hierarchy[n].finalTransform.mProp._mdf){this.finalTransform._matMdf=!0;break}n+=1}if(this.finalTransform._matMdf)for(e=this.finalTransform.mProp.v.props,t.cloneFromProps(e),n=0;n<r;n+=1)t.multiply(this.hierarchy[n].finalTransform.mProp.v)}(!this.localTransforms||this.finalTransform._matMdf)&&(this.finalTransform._localMatMdf=this.finalTransform._matMdf),this.finalTransform._opMdf&&(this.finalTransform.localOpacity=this.finalTransform.mProp.o.v)},renderLocalTransform:function(){if(this.localTransforms){var e=0,t=this.localTransforms.length;if(this.finalTransform._localMatMdf=this.finalTransform._matMdf,!this.finalTransform._localMatMdf||!this.finalTransform._opMdf)for(;e<t;)this.localTransforms[e]._mdf&&(this.finalTransform._localMatMdf=!0),this.localTransforms[e]._opMdf&&!this.finalTransform._opMdf&&(this.finalTransform.localOpacity=this.finalTransform.mProp.o.v,this.finalTransform._opMdf=!0),e+=1;if(this.finalTransform._localMatMdf){var n=this.finalTransform.localMat;for(this.localTransforms[0].matrix.clone(n),e=1;e<t;e+=1){var r=this.localTransforms[e].matrix;n.multiply(r)}n.multiply(this.finalTransform.mat)}if(this.finalTransform._opMdf){var i=this.finalTransform.localOpacity;for(e=0;e<t;e+=1)i*=this.localTransforms[e].opacity*.01;this.finalTransform.localOpacity=i}}},searchEffectTransforms:function(){if(this.renderableEffectsManager){var e=this.renderableEffectsManager.getEffects(yn.TRANSFORM_EFFECT);if(e.length){this.localTransforms=[],this.finalTransform.localMat=new Ye;var t=0,n=e.length;for(t=0;t<n;t+=1)this.localTransforms.push(e[t])}}},globalToLocal:function(e){var t=[];t.push(this.finalTransform);for(var n=!0,r=this.comp;n;)r.finalTransform?(r.data.hasMask&&t.splice(0,0,r.finalTransform),r=r.comp):n=!1;var i,a=t.length,o;for(i=0;i<a;i+=1)o=t[i].mat.applyToPointArray(0,0,0),e=[e[0]-o[0],e[1]-o[1],0];return e},mHelper:new Ye};function xn(e,t,n){this.data=e,this.element=t,this.globalData=n,this.storedData=[],this.masksProperties=this.data.masksProperties||[],this.maskElement=null;var r=this.globalData.defs,i,a=this.masksProperties?this.masksProperties.length:0;this.viewData=m(a),this.solidPath=``;var o,s=this.masksProperties,l=0,u=[],d,f,p=P(),h,g,_,v,y=`clipPath`,b=`clip-path`;for(i=0;i<a;i+=1)if((s[i].mode!==`a`&&s[i].mode!==`n`||s[i].inv||s[i].o.k!==100||s[i].o.x)&&(y=`mask`,b=`mask`),(s[i].mode===`s`||s[i].mode===`i`)&&l===0?(h=I(`rect`),h.setAttribute(`fill`,`#ffffff`),h.setAttribute(`width`,this.element.comp.data.w||0),h.setAttribute(`height`,this.element.comp.data.h||0),u.push(h)):h=null,o=I(`path`),s[i].mode===`n`)this.viewData[i]={op:H.getProp(this.element,s[i].o,0,.01,this.element),prop:Je.getShapeProp(this.element,s[i],3),elem:o,lastPath:``},r.appendChild(o);else{l+=1,o.setAttribute(`fill`,s[i].mode===`s`?`#000000`:`#ffffff`),o.setAttribute(`clip-rule`,`nonzero`);var x;if(s[i].x.k===0?(_=null,v=null):(y=`mask`,b=`mask`,v=H.getProp(this.element,s[i].x,0,null,this.element),x=P(),g=I(`filter`),g.setAttribute(`id`,x),_=I(`feMorphology`),_.setAttribute(`operator`,`erode`),_.setAttribute(`in`,`SourceGraphic`),_.setAttribute(`radius`,`0`),g.appendChild(_),r.appendChild(g),o.setAttribute(`stroke`,s[i].mode===`s`?`#000000`:`#ffffff`)),this.storedData[i]={elem:o,x:v,expan:_,lastPath:``,lastOperator:``,filterId:x,lastRadius:0},s[i].mode===`i`){f=u.length;var S=I(`g`);for(d=0;d<f;d+=1)S.appendChild(u[d]);var C=I(`mask`);C.setAttribute(`mask-type`,`alpha`),C.setAttribute(`id`,p+`_`+l),C.appendChild(o),r.appendChild(C),S.setAttribute(`mask`,`url(`+c()+`#`+p+`_`+l+`)`),u.length=0,u.push(S)}else u.push(o);s[i].inv&&!this.solidPath&&(this.solidPath=this.createLayerSolidPath()),this.viewData[i]={elem:o,lastPath:``,op:H.getProp(this.element,s[i].o,0,.01,this.element),prop:Je.getShapeProp(this.element,s[i],3),invRect:h},this.viewData[i].prop.k||this.drawPath(s[i],this.viewData[i].prop.v,this.viewData[i])}for(this.maskElement=I(y),a=u.length,i=0;i<a;i+=1)this.maskElement.appendChild(u[i]);l>0&&(this.maskElement.setAttribute(`id`,p),this.element.maskedElement.setAttribute(b,`url(`+c()+`#`+p+`)`),r.appendChild(this.maskElement)),this.viewData.length&&this.element.addRenderableComponent(this)}xn.prototype.getMaskProperty=function(e){return this.viewData[e].prop},xn.prototype.renderFrame=function(e){var t=this.element.finalTransform.mat,n,r=this.masksProperties.length;for(n=0;n<r;n+=1)if((this.viewData[n].prop._mdf||e)&&this.drawPath(this.masksProperties[n],this.viewData[n].prop.v,this.viewData[n]),(this.viewData[n].op._mdf||e)&&this.viewData[n].elem.setAttribute(`fill-opacity`,this.viewData[n].op.v),this.masksProperties[n].mode!==`n`&&(this.viewData[n].invRect&&(this.element.finalTransform.mProp._mdf||e)&&this.viewData[n].invRect.setAttribute(`transform`,t.getInverseMatrix().to2dCSS()),this.storedData[n].x&&(this.storedData[n].x._mdf||e))){var i=this.storedData[n].expan;this.storedData[n].x.v<0?(this.storedData[n].lastOperator!==`erode`&&(this.storedData[n].lastOperator=`erode`,this.storedData[n].elem.setAttribute(`filter`,`url(`+c()+`#`+this.storedData[n].filterId+`)`)),i.setAttribute(`radius`,-this.storedData[n].x.v)):(this.storedData[n].lastOperator!==`dilate`&&(this.storedData[n].lastOperator=`dilate`,this.storedData[n].elem.setAttribute(`filter`,null)),this.storedData[n].elem.setAttribute(`stroke-width`,this.storedData[n].x.v*2))}},xn.prototype.getMaskelement=function(){return this.maskElement},xn.prototype.createLayerSolidPath=function(){var e=`M0,0 `;return e+=` h`+this.globalData.compSize.w,e+=` v`+this.globalData.compSize.h,e+=` h-`+this.globalData.compSize.w,e+=` v-`+this.globalData.compSize.h+` `,e},xn.prototype.drawPath=function(e,t,n){var r=` M`+t.v[0][0]+`,`+t.v[0][1],i,a=t._length;for(i=1;i<a;i+=1)r+=` C`+t.o[i-1][0]+`,`+t.o[i-1][1]+` `+t.i[i][0]+`,`+t.i[i][1]+` `+t.v[i][0]+`,`+t.v[i][1];if(t.c&&a>1&&(r+=` C`+t.o[i-1][0]+`,`+t.o[i-1][1]+` `+t.i[0][0]+`,`+t.i[0][1]+` `+t.v[0][0]+`,`+t.v[0][1]),n.lastPath!==r){var o=``;n.elem&&(t.c&&(o=e.inv?this.solidPath+r:r),n.elem.setAttribute(`d`,o)),n.lastPath=r}},xn.prototype.destroy=function(){this.element=null,this.globalData=null,this.maskElement=null,this.data=null,this.masksProperties=null};var Sn=function(){var e={};e.createFilter=t,e.createAlphaToLuminanceFilter=n;function t(e,t){var n=I(`filter`);return n.setAttribute(`id`,e),t!==!0&&(n.setAttribute(`filterUnits`,`objectBoundingBox`),n.setAttribute(`x`,`0%`),n.setAttribute(`y`,`0%`),n.setAttribute(`width`,`100%`),n.setAttribute(`height`,`100%`)),n}function n(){var e=I(`feColorMatrix`);return e.setAttribute(`type`,`matrix`),e.setAttribute(`color-interpolation-filters`,`sRGB`),e.setAttribute(`values`,`0 0 0 1 0  0 0 0 1 0  0 0 0 1 0  0 0 0 1 1`),e}return e}(),Cn=function(){var e={maskType:!0,svgLumaHidden:!0,offscreenCanvas:typeof OffscreenCanvas<`u`};return(/MSIE 10/i.test(navigator.userAgent)||/MSIE 9/i.test(navigator.userAgent)||/rv:11.0/i.test(navigator.userAgent)||/Edge\/\d./i.test(navigator.userAgent))&&(e.maskType=!1),/firefox/i.test(navigator.userAgent)&&(e.svgLumaHidden=!1),e}(),wn={},Tn=`filter_result_`;function En(e){var t,n=`SourceGraphic`,r=e.data.ef?e.data.ef.length:0,i=P(),a=Sn.createFilter(i,!0),o=0;this.filters=[];var s;for(t=0;t<r;t+=1){s=null;var l=e.data.ef[t].ty;if(wn[l]){var u=wn[l].effect;s=new u(a,e.effectsManager.effectElements[t],e,Tn+o,n),n=Tn+o,wn[l].countsAsEffect&&(o+=1)}s&&this.filters.push(s)}o&&(e.globalData.defs.appendChild(a),e.layerElement.setAttribute(`filter`,`url(`+c()+`#`+i+`)`)),this.filters.length&&e.addRenderableComponent(this)}En.prototype.renderFrame=function(e){var t,n=this.filters.length;for(t=0;t<n;t+=1)this.filters[t].renderFrame(e)},En.prototype.getEffects=function(e){var t,n=this.filters.length,r=[];for(t=0;t<n;t+=1)this.filters[t].type===e&&r.push(this.filters[t]);return r};function Dn(){}Dn.prototype={initRendererElement:function(){this.layerElement=I(`g`)},createContainerElements:function(){this.matteElement=I(`g`),this.transformedElement=this.layerElement,this.maskedElement=this.layerElement,this._sizeChanged=!1;var e=null;if(this.data.td){this.matteMasks={};var t=I(`g`);t.setAttribute(`id`,this.layerId),t.appendChild(this.layerElement),e=t,this.globalData.defs.appendChild(t)}else this.data.tt?(this.matteElement.appendChild(this.layerElement),e=this.matteElement,this.baseElement=this.matteElement):this.baseElement=this.layerElement;if(this.data.ln&&this.layerElement.setAttribute(`id`,this.data.ln),this.data.cl&&this.layerElement.setAttribute(`class`,this.data.cl),this.data.ty===0&&!this.data.hd){var n=I(`clipPath`),r=I(`path`);r.setAttribute(`d`,`M0,0 L`+this.data.w+`,0 L`+this.data.w+`,`+this.data.h+` L0,`+this.data.h+`z`);var i=P();if(n.setAttribute(`id`,i),n.appendChild(r),this.globalData.defs.appendChild(n),this.checkMasks()){var a=I(`g`);a.setAttribute(`clip-path`,`url(`+c()+`#`+i+`)`),a.appendChild(this.layerElement),this.transformedElement=a,e?e.appendChild(this.transformedElement):this.baseElement=this.transformedElement}else this.layerElement.setAttribute(`clip-path`,`url(`+c()+`#`+i+`)`)}this.data.bm!==0&&this.setBlendMode()},renderElement:function(){this.finalTransform._localMatMdf&&this.transformedElement.setAttribute(`transform`,this.finalTransform.localMat.to2dCSS()),this.finalTransform._opMdf&&this.transformedElement.setAttribute(`opacity`,this.finalTransform.localOpacity)},destroyBaseElement:function(){this.layerElement=null,this.matteElement=null,this.maskManager.destroy()},getBaseElement:function(){return this.data.hd?null:this.baseElement},createRenderableComponents:function(){this.maskManager=new xn(this.data,this,this.globalData),this.renderableEffectsManager=new En(this),this.searchEffectTransforms()},getMatte:function(e){if(this.matteMasks||(this.matteMasks={}),!this.matteMasks[e]){var t=this.layerId+`_`+e,n,r,i,a;if(e===1||e===3){var o=I(`mask`);o.setAttribute(`id`,t),o.setAttribute(`mask-type`,e===3?`luminance`:`alpha`),i=I(`use`),i.setAttributeNS(`http://www.w3.org/1999/xlink`,`href`,`#`+this.layerId),o.appendChild(i),this.globalData.defs.appendChild(o),!Cn.maskType&&e===1&&(o.setAttribute(`mask-type`,`luminance`),n=P(),r=Sn.createFilter(n),this.globalData.defs.appendChild(r),r.appendChild(Sn.createAlphaToLuminanceFilter()),a=I(`g`),a.appendChild(i),o.appendChild(a),a.setAttribute(`filter`,`url(`+c()+`#`+n+`)`))}else if(e===2){var s=I(`mask`);s.setAttribute(`id`,t),s.setAttribute(`mask-type`,`alpha`);var l=I(`g`);s.appendChild(l),n=P(),r=Sn.createFilter(n);var u=I(`feComponentTransfer`);u.setAttribute(`in`,`SourceGraphic`),r.appendChild(u);var d=I(`feFuncA`);d.setAttribute(`type`,`table`),d.setAttribute(`tableValues`,`1.0 0.0`),u.appendChild(d),this.globalData.defs.appendChild(r);var f=I(`rect`);f.setAttribute(`width`,this.comp.data.w),f.setAttribute(`height`,this.comp.data.h),f.setAttribute(`x`,`0`),f.setAttribute(`y`,`0`),f.setAttribute(`fill`,`#ffffff`),f.setAttribute(`opacity`,`0`),l.setAttribute(`filter`,`url(`+c()+`#`+n+`)`),l.appendChild(f),i=I(`use`),i.setAttributeNS(`http://www.w3.org/1999/xlink`,`href`,`#`+this.layerId),l.appendChild(i),Cn.maskType||(s.setAttribute(`mask-type`,`luminance`),r.appendChild(Sn.createAlphaToLuminanceFilter()),a=I(`g`),l.appendChild(f),a.appendChild(this.layerElement),l.appendChild(a)),this.globalData.defs.appendChild(s)}this.matteMasks[e]=t}return this.matteMasks[e]},setMatte:function(e){this.matteElement&&this.matteElement.setAttribute(`mask`,`url(`+c()+`#`+e+`)`)}};function On(){}On.prototype={initHierarchy:function(){this.hierarchy=[],this._isParent=!1,this.checkParenting()},setHierarchy:function(e){this.hierarchy=e},setAsParent:function(){this._isParent=!0},checkParenting:function(){this.data.parent!==void 0&&this.comp.buildElementParenting(this,this.data.parent,[])}};function kn(){}(function(){u([tn,d({initElement:function(e,t,n){this.initFrame(),this.initBaseData(e,t,n),this.initTransform(e,t,n),this.initHierarchy(),this.initRenderable(),this.initRendererElement(),this.createContainerElements(),this.createRenderableComponents(),this.createContent(),this.hide()},hide:function(){if(!this.hidden&&(!this.isInRange||this.isTransparent)){var e=this.baseElement||this.layerElement;e.style.display=`none`,this.hidden=!0}},show:function(){if(this.isInRange&&!this.isTransparent){if(!this.data.hd){var e=this.baseElement||this.layerElement;e.style.display=`block`}this.hidden=!1,this._isFirstFrame=!0}},renderFrame:function(){this.data.hd||this.hidden||(this.renderTransform(),this.renderRenderable(),this.renderLocalTransform(),this.renderElement(),this.renderInnerContent(),this._isFirstFrame&&(this._isFirstFrame=!1))},renderInnerContent:function(){},prepareFrame:function(e){this._mdf=!1,this.prepareRenderableFrame(e),this.prepareProperties(e,this.isInRange),this.checkTransparency()},destroy:function(){this.innerElem=null,this.destroyBaseElement()}})],kn)})();function An(e,t,n){this.assetData=t.getAssetData(e.refId),this.assetData&&this.assetData.sid&&(this.assetData=t.slotManager.getProp(this.assetData)),this.initElement(e,t,n),this.sourceRect={top:0,left:0,width:this.assetData.w,height:this.assetData.h}}u([mn,bn,Dn,On,hn,kn],An),An.prototype.createContent=function(){var e=this.globalData.getAssetsPath(this.assetData);this.innerElem=I(`image`),this.innerElem.setAttribute(`width`,this.assetData.w+`px`),this.innerElem.setAttribute(`height`,this.assetData.h+`px`),this.innerElem.setAttribute(`preserveAspectRatio`,this.assetData.pr||this.globalData.renderConfig.imagePreserveAspectRatio),this.innerElem.setAttributeNS(`http://www.w3.org/1999/xlink`,`href`,e),this.layerElement.appendChild(this.innerElem)},An.prototype.sourceRectAtTime=function(){return this.sourceRect};function jn(e,t){this.elem=e,this.pos=t}function Mn(){}Mn.prototype={addShapeToModifiers:function(e){var t,n=this.shapeModifiers.length;for(t=0;t<n;t+=1)this.shapeModifiers[t].addShape(e)},isShapeInAnimatedModifiers:function(e){for(var t=0,n=this.shapeModifiers.length;t<n;)if(this.shapeModifiers[t].isAnimatedWithShape(e))return!0;return!1},renderModifiers:function(){if(this.shapeModifiers.length){var e,t=this.shapes.length;for(e=0;e<t;e+=1)this.shapes[e].sh.reset();t=this.shapeModifiers.length;var n;for(e=t-1;e>=0&&(n=this.shapeModifiers[e].processShapes(this._isFirstFrame),!n);--e);}},searchProcessedElement:function(e){for(var t=this.processedElements,n=0,r=t.length;n<r;){if(t[n].elem===e)return t[n].pos;n+=1}return 0},addProcessedElement:function(e,t){for(var n=this.processedElements,r=n.length;r;)if(--r,n[r].elem===e){n[r].pos=t;return}n.push(new jn(e,t))},prepareFrame:function(e){this.prepareRenderableFrame(e),this.prepareProperties(e,this.isInRange)}};var J={1:`butt`,2:`round`,3:`square`},Nn={1:`miter`,2:`round`,3:`bevel`};function Pn(e,t,n){this.caches=[],this.styles=[],this.transformers=e,this.lStr=``,this.sh=n,this.lvl=t,this._isAnimated=!!n.k;for(var r=0,i=e.length;r<i;){if(e[r].mProps.dynamicProperties.length){this._isAnimated=!0;break}r+=1}}Pn.prototype.setAsAnimated=function(){this._isAnimated=!0};function Fn(e,t){this.data=e,this.type=e.ty,this.d=``,this.lvl=t,this._mdf=!1,this.closed=e.hd===!0,this.pElem=I(`path`),this.msElem=null}Fn.prototype.reset=function(){this.d=``,this._mdf=!1};function In(e,t,n,r){this.elem=e,this.frameId=-1,this.dataProps=m(t.length),this.renderer=n,this.k=!1,this.dashStr=``,this.dashArray=p(`float32`,t.length?t.length-1:0),this.dashoffset=p(`float32`,1),this.initDynamicPropertyContainer(r);var i,a=t.length||0,o;for(i=0;i<a;i+=1)o=H.getProp(e,t[i].v,0,0,this),this.k=o.k||this.k,this.dataProps[i]={n:t[i].n,p:o};this.k||this.getValue(!0),this._isAnimated=this.k}In.prototype.getValue=function(e){if((this.elem.globalData.frameId!==this.frameId||e)&&(this.frameId=this.elem.globalData.frameId,this.iterateDynamicProperties(),this._mdf=this._mdf||e,this._mdf)){var t=0,n=this.dataProps.length;for(this.renderer===`svg`&&(this.dashStr=``),t=0;t<n;t+=1)this.dataProps[t].n===`o`?this.dashoffset[0]=this.dataProps[t].p.v:this.renderer===`svg`?this.dashStr+=` `+this.dataProps[t].p.v:this.dashArray[t]=this.dataProps[t].p.v}},u([Ue],In);function Ln(e,t,n){this.initDynamicPropertyContainer(e),this.getValue=this.iterateDynamicProperties,this.o=H.getProp(e,t.o,0,.01,this),this.w=H.getProp(e,t.w,0,null,this),this.d=new In(e,t.d||{},`svg`,this),this.c=H.getProp(e,t.c,1,255,this),this.style=n,this._isAnimated=!!this._isAnimated}u([Ue],Ln);function Rn(e,t,n){this.initDynamicPropertyContainer(e),this.getValue=this.iterateDynamicProperties,this.o=H.getProp(e,t.o,0,.01,this),this.c=H.getProp(e,t.c,1,255,this),this.style=n}u([Ue],Rn);function zn(e,t,n){this.initDynamicPropertyContainer(e),this.getValue=this.iterateDynamicProperties,this.style=n}u([Ue],zn);function Bn(e,t,n){this.data=t,this.c=p(`uint8c`,t.p*4);var r=t.k.k[0].s?t.k.k[0].s.length-t.p*4:t.k.k.length-t.p*4;this.o=p(`float32`,r),this._cmdf=!1,this._omdf=!1,this._collapsable=this.checkCollapsable(),this._hasOpacity=r,this.initDynamicPropertyContainer(n),this.prop=H.getProp(e,t.k,1,null,this),this.k=this.prop.k,this.getValue(!0)}Bn.prototype.comparePoints=function(e,t){for(var n=0,r=this.o.length/2,i;n<r;){if(i=Math.abs(e[n*4]-e[t*4+n*2]),i>.01)return!1;n+=1}return!0},Bn.prototype.checkCollapsable=function(){if(this.o.length/2!=this.c.length/4)return!1;if(this.data.k.k[0].s)for(var e=0,t=this.data.k.k.length;e<t;){if(!this.comparePoints(this.data.k.k[e].s,this.data.p))return!1;e+=1}else if(!this.comparePoints(this.data.k.k,this.data.p))return!1;return!0},Bn.prototype.getValue=function(e){if(this.prop.getValue(),this._mdf=!1,this._cmdf=!1,this._omdf=!1,this.prop._mdf||e){var t,n=this.data.p*4,r,i;for(t=0;t<n;t+=1)r=t%4==0?100:255,i=Math.round(this.prop.v[t]*r),this.c[t]!==i&&(this.c[t]=i,this._cmdf=!e);if(this.o.length)for(n=this.prop.v.length,t=this.data.p*4;t<n;t+=1)r=t%2==0?100:1,i=t%2==0?Math.round(this.prop.v[t]*100):this.prop.v[t],this.o[t-this.data.p*4]!==i&&(this.o[t-this.data.p*4]=i,this._omdf=!e);this._mdf=!e}},u([Ue],Bn);function Vn(e,t,n){this.initDynamicPropertyContainer(e),this.getValue=this.iterateDynamicProperties,this.initGradientData(e,t,n)}Vn.prototype.initGradientData=function(e,t,n){this.o=H.getProp(e,t.o,0,.01,this),this.s=H.getProp(e,t.s,1,null,this),this.e=H.getProp(e,t.e,1,null,this),this.h=H.getProp(e,t.h||{k:0},0,.01,this),this.a=H.getProp(e,t.a||{k:0},0,D,this),this.g=new Bn(e,t.g,this),this.style=n,this.stops=[],this.setGradientData(n.pElem,t),this.setGradientOpacity(t,n),this._isAnimated=!!this._isAnimated},Vn.prototype.setGradientData=function(e,t){var n=P(),r=I(t.t===1?`linearGradient`:`radialGradient`);r.setAttribute(`id`,n),r.setAttribute(`spreadMethod`,`pad`),r.setAttribute(`gradientUnits`,`userSpaceOnUse`);var i=[],a,o,s=t.g.p*4;for(o=0;o<s;o+=4)a=I(`stop`),r.appendChild(a),i.push(a);e.setAttribute(t.ty===`gf`?`fill`:`stroke`,`url(`+c()+`#`+n+`)`),this.gf=r,this.cst=i},Vn.prototype.setGradientOpacity=function(e,t){if(this.g._hasOpacity&&!this.g._collapsable){var n,r,i,a=I(`mask`),o=I(`path`);a.appendChild(o);var s=P(),l=P();a.setAttribute(`id`,l);var u=I(e.t===1?`linearGradient`:`radialGradient`);u.setAttribute(`id`,s),u.setAttribute(`spreadMethod`,`pad`),u.setAttribute(`gradientUnits`,`userSpaceOnUse`),i=e.g.k.k[0].s?e.g.k.k[0].s.length:e.g.k.k.length;var d=this.stops;for(r=e.g.p*4;r<i;r+=2)n=I(`stop`),n.setAttribute(`stop-color`,`rgb(255,255,255)`),u.appendChild(n),d.push(n);o.setAttribute(e.ty===`gf`?`fill`:`stroke`,`url(`+c()+`#`+s+`)`),e.ty===`gs`&&(o.setAttribute(`stroke-linecap`,J[e.lc||2]),o.setAttribute(`stroke-linejoin`,Nn[e.lj||2]),e.lj===1&&o.setAttribute(`stroke-miterlimit`,e.ml)),this.of=u,this.ms=a,this.ost=d,this.maskId=l,t.msElem=o}},u([Ue],Vn);function Hn(e,t,n){this.initDynamicPropertyContainer(e),this.getValue=this.iterateDynamicProperties,this.w=H.getProp(e,t.w,0,null,this),this.d=new In(e,t.d||{},`svg`,this),this.initGradientData(e,t,n),this._isAnimated=!!this._isAnimated}u([Vn,Ue],Hn);function Un(){this.it=[],this.prevViewData=[],this.gr=I(`g`)}function Wn(e,t,n){this.transform={mProps:e,op:t,container:n},this.elements=[],this._isAnimated=this.transform.mProps.dynamicProperties.length||this.transform.op.effectsSequence.length}var Gn=function(e,t,n,r){if(t===0)return``;var i=e.o,a=e.i,o=e.v,s,c=` M`+r.applyToPointStringified(o[0][0],o[0][1]);for(s=1;s<t;s+=1)c+=` C`+r.applyToPointStringified(i[s-1][0],i[s-1][1])+` `+r.applyToPointStringified(a[s][0],a[s][1])+` `+r.applyToPointStringified(o[s][0],o[s][1]);return n&&t&&(c+=` C`+r.applyToPointStringified(i[s-1][0],i[s-1][1])+` `+r.applyToPointStringified(a[0][0],a[0][1])+` `+r.applyToPointStringified(o[0][0],o[0][1]),c+=`z`),c},Kn=function(){var e=new Ye,t=new Ye,n={createRenderFunction:r};function r(e){switch(e.ty){case`fl`:return s;case`gf`:return l;case`gs`:return c;case`st`:return u;case`sh`:case`el`:case`rc`:case`sr`:return o;case`tr`:return i;case`no`:return a;default:return null}}function i(e,t,n){(n||t.transform.op._mdf)&&t.transform.container.setAttribute(`opacity`,t.transform.op.v),(n||t.transform.mProps._mdf)&&t.transform.container.setAttribute(`transform`,t.transform.mProps.v.to2dCSS())}function a(){}function o(n,r,i){var a,o,s,c,l,u,d=r.styles.length,f=r.lvl,p,m,h,g;for(u=0;u<d;u+=1){if(c=r.sh._mdf||i,r.styles[u].lvl<f){for(m=t.reset(),h=f-r.styles[u].lvl,g=r.transformers.length-1;!c&&h>0;)c=r.transformers[g].mProps._mdf||c,--h,--g;if(c)for(h=f-r.styles[u].lvl,g=r.transformers.length-1;h>0;)m.multiply(r.transformers[g].mProps.v),--h,--g}else m=e;if(p=r.sh.paths,o=p._length,c){for(s=``,a=0;a<o;a+=1)l=p.shapes[a],l&&l._length&&(s+=Gn(l,l._length,l.c,m));r.caches[u]=s}else s=r.caches[u];r.styles[u].d+=n.hd===!0?``:s,r.styles[u]._mdf=c||r.styles[u]._mdf}}function s(e,t,n){var r=t.style;(t.c._mdf||n)&&r.pElem.setAttribute(`fill`,`rgb(`+C(t.c.v[0])+`,`+C(t.c.v[1])+`,`+C(t.c.v[2])+`)`),(t.o._mdf||n)&&r.pElem.setAttribute(`fill-opacity`,t.o.v)}function c(e,t,n){l(e,t,n),u(e,t,n)}function l(e,t,n){var r=t.gf,i=t.g._hasOpacity,a=t.s.v,o=t.e.v;if(t.o._mdf||n){var s=e.ty===`gf`?`fill-opacity`:`stroke-opacity`;t.style.pElem.setAttribute(s,t.o.v)}if(t.s._mdf||n){var c=e.t===1?`x1`:`cx`,l=c===`x1`?`y1`:`cy`;r.setAttribute(c,a[0]),r.setAttribute(l,a[1]),i&&!t.g._collapsable&&(t.of.setAttribute(c,a[0]),t.of.setAttribute(l,a[1]))}var u,d,f,p;if(t.g._cmdf||n){u=t.cst;var m=t.g.c;for(f=u.length,d=0;d<f;d+=1)p=u[d],p.setAttribute(`offset`,m[d*4]+`%`),p.setAttribute(`stop-color`,`rgb(`+m[d*4+1]+`,`+m[d*4+2]+`,`+m[d*4+3]+`)`)}if(i&&(t.g._omdf||n)){var h=t.g.o;for(u=t.g._collapsable?t.cst:t.ost,f=u.length,d=0;d<f;d+=1)p=u[d],t.g._collapsable||p.setAttribute(`offset`,h[d*2]+`%`),p.setAttribute(`stop-opacity`,h[d*2+1])}if(e.t===1)(t.e._mdf||n)&&(r.setAttribute(`x2`,o[0]),r.setAttribute(`y2`,o[1]),i&&!t.g._collapsable&&(t.of.setAttribute(`x2`,o[0]),t.of.setAttribute(`y2`,o[1])));else{var g;if((t.s._mdf||t.e._mdf||n)&&(g=Math.sqrt((a[0]-o[0])**2+(a[1]-o[1])**2),r.setAttribute(`r`,g),i&&!t.g._collapsable&&t.of.setAttribute(`r`,g)),t.s._mdf||t.e._mdf||t.h._mdf||t.a._mdf||n){g||(g=Math.sqrt((a[0]-o[0])**2+(a[1]-o[1])**2));var _=Math.atan2(o[1]-a[1],o[0]-a[0]),v=t.h.v;v>=1?v=.99:v<=-1&&(v=-.99);var y=g*v,b=Math.cos(_+t.a.v)*y+a[0],x=Math.sin(_+t.a.v)*y+a[1];r.setAttribute(`fx`,b),r.setAttribute(`fy`,x),i&&!t.g._collapsable&&(t.of.setAttribute(`fx`,b),t.of.setAttribute(`fy`,x))}}}function u(e,t,n){var r=t.style,i=t.d;i&&(i._mdf||n)&&i.dashStr&&(r.pElem.setAttribute(`stroke-dasharray`,i.dashStr),r.pElem.setAttribute(`stroke-dashoffset`,i.dashoffset[0])),t.c&&(t.c._mdf||n)&&r.pElem.setAttribute(`stroke`,`rgb(`+C(t.c.v[0])+`,`+C(t.c.v[1])+`,`+C(t.c.v[2])+`)`),(t.o._mdf||n)&&r.pElem.setAttribute(`stroke-opacity`,t.o.v),(t.w._mdf||n)&&(r.pElem.setAttribute(`stroke-width`,t.w.v),r.msElem&&r.msElem.setAttribute(`stroke-width`,t.w.v))}return n}();function Y(e,t,n){this.shapes=[],this.shapesData=e.shapes,this.stylesList=[],this.shapeModifiers=[],this.itemsData=[],this.processedElements=[],this.animatedContents=[],this.initElement(e,t,n),this.prevViewData=[]}u([mn,bn,Dn,Mn,On,hn,kn],Y),Y.prototype.initSecondaryElement=function(){},Y.prototype.identityMatrix=new Ye,Y.prototype.buildExpressionInterface=function(){},Y.prototype.createContent=function(){this.searchShapes(this.shapesData,this.itemsData,this.prevViewData,this.layerElement,0,[],!0),this.filterUniqueShapes()},Y.prototype.filterUniqueShapes=function(){var e,t=this.shapes.length,n,r,i=this.stylesList.length,a,o=[],s=!1;for(r=0;r<i;r+=1){for(a=this.stylesList[r],s=!1,o.length=0,e=0;e<t;e+=1)n=this.shapes[e],n.styles.indexOf(a)!==-1&&(o.push(n),s=n._isAnimated||s);o.length>1&&s&&this.setShapesAsAnimated(o)}},Y.prototype.setShapesAsAnimated=function(e){var t,n=e.length;for(t=0;t<n;t+=1)e[t].setAsAnimated()},Y.prototype.createStyleElement=function(e,t){var n,r=new Fn(e,t),i=r.pElem;return e.ty===`st`?n=new Ln(this,e,r):e.ty===`fl`?n=new Rn(this,e,r):e.ty===`gf`||e.ty===`gs`?(n=new(e.ty===`gf`?Vn:Hn)(this,e,r),this.globalData.defs.appendChild(n.gf),n.maskId&&(this.globalData.defs.appendChild(n.ms),this.globalData.defs.appendChild(n.of),i.setAttribute(`mask`,`url(`+c()+`#`+n.maskId+`)`))):e.ty===`no`&&(n=new zn(this,e,r)),(e.ty===`st`||e.ty===`gs`)&&(i.setAttribute(`stroke-linecap`,J[e.lc||2]),i.setAttribute(`stroke-linejoin`,Nn[e.lj||2]),i.setAttribute(`fill-opacity`,`0`),e.lj===1&&i.setAttribute(`stroke-miterlimit`,e.ml)),e.r===2&&i.setAttribute(`fill-rule`,`evenodd`),e.ln&&i.setAttribute(`id`,e.ln),e.cl&&i.setAttribute(`class`,e.cl),e.bm&&(i.style[`mix-blend-mode`]=nn(e.bm)),this.stylesList.push(r),this.addToAnimatedContents(e,n),n},Y.prototype.createGroupElement=function(e){var t=new Un;return e.ln&&t.gr.setAttribute(`id`,e.ln),e.cl&&t.gr.setAttribute(`class`,e.cl),e.bm&&(t.gr.style[`mix-blend-mode`]=nn(e.bm)),t},Y.prototype.createTransformElement=function(e,t){var n=_t.getTransformProperty(this,e,this),r=new Wn(n,n.o,t);return this.addToAnimatedContents(e,r),r},Y.prototype.createShapeElement=function(e,t,n){var r=4;e.ty===`rc`?r=5:e.ty===`el`?r=6:e.ty===`sr`&&(r=7);var i=new Pn(t,n,Je.getShapeProp(this,e,r,this));return this.shapes.push(i),this.addShapeToModifiers(i),this.addToAnimatedContents(e,i),i},Y.prototype.addToAnimatedContents=function(e,t){for(var n=0,r=this.animatedContents.length;n<r;){if(this.animatedContents[n].element===t)return;n+=1}this.animatedContents.push({fn:Kn.createRenderFunction(e),element:t,data:e})},Y.prototype.setElementStyles=function(e){var t=e.styles,n,r=this.stylesList.length;for(n=0;n<r;n+=1)t.indexOf(this.stylesList[n])===-1&&!this.stylesList[n].closed&&t.push(this.stylesList[n])},Y.prototype.reloadShapes=function(){this._isFirstFrame=!0;var e,t=this.itemsData.length;for(e=0;e<t;e+=1)this.prevViewData[e]=this.itemsData[e];for(this.searchShapes(this.shapesData,this.itemsData,this.prevViewData,this.layerElement,0,[],!0),this.filterUniqueShapes(),t=this.dynamicProperties.length,e=0;e<t;e+=1)this.dynamicProperties[e].getValue();this.renderModifiers()},Y.prototype.searchShapes=function(e,t,n,r,i,a,o){var s=[].concat(a),c,l=e.length-1,u,d,f=[],p=[],m,h,g;for(c=l;c>=0;--c){if(g=this.searchProcessedElement(e[c]),g?t[c]=n[g-1]:e[c]._render=o,e[c].ty===`fl`||e[c].ty===`st`||e[c].ty===`gf`||e[c].ty===`gs`||e[c].ty===`no`)g?t[c].style.closed=e[c].hd:t[c]=this.createStyleElement(e[c],i),e[c]._render&&t[c].style.pElem.parentNode!==r&&r.appendChild(t[c].style.pElem),f.push(t[c].style);else if(e[c].ty===`gr`){if(!g)t[c]=this.createGroupElement(e[c]);else for(d=t[c].it.length,u=0;u<d;u+=1)t[c].prevViewData[u]=t[c].it[u];this.searchShapes(e[c].it,t[c].it,t[c].prevViewData,t[c].gr,i+1,s,o),e[c]._render&&t[c].gr.parentNode!==r&&r.appendChild(t[c].gr)}else e[c].ty===`tr`?(g||(t[c]=this.createTransformElement(e[c],r)),m=t[c].transform,s.push(m)):e[c].ty===`sh`||e[c].ty===`rc`||e[c].ty===`el`||e[c].ty===`sr`?(g||(t[c]=this.createShapeElement(e[c],s,i)),this.setElementStyles(t[c])):e[c].ty===`tm`||e[c].ty===`rd`||e[c].ty===`ms`||e[c].ty===`pb`||e[c].ty===`zz`||e[c].ty===`op`?(g?(h=t[c],h.closed=!1):(h=pt.getModifier(e[c].ty),h.init(this,e[c]),t[c]=h,this.shapeModifiers.push(h)),p.push(h)):e[c].ty===`rp`&&(g?(h=t[c],h.closed=!0):(h=pt.getModifier(e[c].ty),t[c]=h,h.init(this,e,c,t),this.shapeModifiers.push(h),o=!1),p.push(h));this.addProcessedElement(e[c],c+1)}for(l=f.length,c=0;c<l;c+=1)f[c].closed=!0;for(l=p.length,c=0;c<l;c+=1)p[c].closed=!0},Y.prototype.renderInnerContent=function(){this.renderModifiers();var e,t=this.stylesList.length;for(e=0;e<t;e+=1)this.stylesList[e].reset();for(this.renderShape(),e=0;e<t;e+=1)(this.stylesList[e]._mdf||this._isFirstFrame)&&(this.stylesList[e].msElem&&(this.stylesList[e].msElem.setAttribute(`d`,this.stylesList[e].d),this.stylesList[e].d=`M0 0`+this.stylesList[e].d),this.stylesList[e].pElem.setAttribute(`d`,this.stylesList[e].d||`M0 0`))},Y.prototype.renderShape=function(){var e,t=this.animatedContents.length,n;for(e=0;e<t;e+=1)n=this.animatedContents[e],(this._isFirstFrame||n.element._isAnimated)&&n.data!==!0&&n.fn(n.data,n.element,this._isFirstFrame)},Y.prototype.destroy=function(){this.destroyBaseElement(),this.shapesData=null,this.itemsData=null};function qn(e,t,n,r,i,a){this.o=e,this.sw=t,this.sc=n,this.fc=r,this.m=i,this.p=a,this._mdf={o:!0,sw:!!t,sc:!!n,fc:!!r,m:!0,p:!0}}qn.prototype.update=function(e,t,n,r,i,a){this._mdf.o=!1,this._mdf.sw=!1,this._mdf.sc=!1,this._mdf.fc=!1,this._mdf.m=!1,this._mdf.p=!1;var o=!1;return this.o!==e&&(this.o=e,this._mdf.o=!0,o=!0),this.sw!==t&&(this.sw=t,this._mdf.sw=!0,o=!0),this.sc!==n&&(this.sc=n,this._mdf.sc=!0,o=!0),this.fc!==r&&(this.fc=r,this._mdf.fc=!0,o=!0),this.m!==i&&(this.m=i,this._mdf.m=!0,o=!0),a.length&&(this.p[0]!==a[0]||this.p[1]!==a[1]||this.p[4]!==a[4]||this.p[5]!==a[5]||this.p[12]!==a[12]||this.p[13]!==a[13])&&(this.p=a,this._mdf.p=!0,o=!0),o};function Jn(e,t){this._frameId=i,this.pv=``,this.v=``,this.kf=!1,this._isFirstFrame=!0,this._mdf=!1,t.d&&t.d.sid&&(t.d=e.globalData.slotManager.getProp(t.d)),this.data=t,this.elem=e,this.comp=this.elem.comp,this.keysIndex=0,this.canResize=!1,this.minimumFontSize=1,this.effectsSequence=[],this.currentData={ascent:0,boxWidth:this.defaultBoxWidth,f:``,fStyle:``,fWeight:``,fc:``,j:``,justifyOffset:``,l:[],lh:0,lineWidths:[],ls:``,of:``,s:``,sc:``,sw:0,t:0,tr:0,sz:0,ps:null,fillColorAnim:!1,strokeColorAnim:!1,strokeWidthAnim:!1,yOffset:0,finalSize:0,finalText:[],finalLineHeight:0,__complete:!1},this.copyData(this.currentData,this.data.d.k[0].s),this.searchProperty()||this.completeTextData(this.currentData)}Jn.prototype.defaultBoxWidth=[0,0],Jn.prototype.copyData=function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n]);return e},Jn.prototype.setCurrentData=function(e){e.__complete||this.completeTextData(e),this.currentData=e,this.currentData.boxWidth=this.currentData.boxWidth||this.defaultBoxWidth,this._mdf=!0},Jn.prototype.searchProperty=function(){return this.searchKeyframes()},Jn.prototype.searchKeyframes=function(){return this.kf=this.data.d.k.length>1,this.kf&&this.addEffect(this.getKeyframeValue.bind(this)),this.kf},Jn.prototype.addEffect=function(e){this.effectsSequence.push(e),this.elem.addDynamicProperty(this)},Jn.prototype.getValue=function(e){if(this.elem.globalData.frameId!==this.frameId&&this.effectsSequence.length||e){this.currentData.t=this.data.d.k[this.keysIndex].s.t;var t=this.currentData,n=this.keysIndex;if(this.lock){this.setCurrentData(this.currentData);return}this.lock=!0,this._mdf=!1;var r,i=this.effectsSequence.length,a=e||this.data.d.k[this.keysIndex].s;for(r=0;r<i;r+=1)a=n===this.keysIndex?this.effectsSequence[r](this.currentData,a.t):this.effectsSequence[r](a,a.t);t!==a&&this.setCurrentData(a),this.v=this.currentData,this.pv=this.v,this.lock=!1,this.frameId=this.elem.globalData.frameId}},Jn.prototype.getKeyframeValue=function(){for(var e=this.data.d.k,t=this.elem.comp.renderedFrame,n=0,r=e.length;n<=r-1&&!(n===r-1||e[n+1].t>t);)n+=1;return this.keysIndex!==n&&(this.keysIndex=n),this.data.d.k[this.keysIndex].s},Jn.prototype.buildFinalText=function(e){for(var t=[],n=0,r=e.length,i,a,o=!1,s=!1,c=``;n<r;)o=s,s=!1,i=e.charCodeAt(n),c=e.charAt(n),Qt.isCombinedCharacter(i)?o=!0:i>=55296&&i<=56319?Qt.isRegionalFlag(e,n)?c=e.substr(n,14):(a=e.charCodeAt(n+1),a>=56320&&a<=57343&&(Qt.isModifier(i,a)?(c=e.substr(n,2),o=!0):c=Qt.isFlagEmoji(e.substr(n,4))?e.substr(n,4):e.substr(n,2))):i>56319?(a=e.charCodeAt(n+1),Qt.isVariationSelector(i)&&(o=!0)):Qt.isZeroWidthJoiner(i)&&(o=!0,s=!0),o?(t[t.length-1]+=c,o=!1):t.push(c),n+=c.length;return t},Jn.prototype.completeTextData=function(e){e.__complete=!0;var t=this.elem.globalData.fontManager,n=this.data,r=[],i,a,o,s=0,c,l=n.m.g,u=0,d=0,f=0,p=[],m=0,h=0,g,_,v=t.getFontByName(e.f),y,b=0,x=Zt(v);e.fWeight=x.weight,e.fStyle=x.style,e.finalSize=e.s,e.finalText=this.buildFinalText(e.t),a=e.finalText.length,e.finalLineHeight=e.lh;var S=e.tr/1e3*e.finalSize,C;if(e.sz)for(var w=!0,T=e.sz[0],E=e.sz[1],D,O;w;){O=this.buildFinalText(e.t),D=0,m=0,a=O.length,S=e.tr/1e3*e.finalSize;var k=-1;for(i=0;i<a;i+=1)C=O[i].charCodeAt(0),o=!1,O[i]===` `?k=i:(C===13||C===3)&&(m=0,o=!0,D+=e.finalLineHeight||e.finalSize*1.2),t.chars?(y=t.getCharData(O[i],v.fStyle,v.fFamily),b=o?0:y.w*e.finalSize/100):b=t.measureText(O[i],e.f,e.finalSize),m+b>T&&O[i]!==` `?(k===-1?a+=1:i=k,D+=e.finalLineHeight||e.finalSize*1.2,O.splice(i,+(k===i),`\r`),k=-1,m=0):(m+=b,m+=S);D+=v.ascent*e.finalSize/100,this.canResize&&e.finalSize>this.minimumFontSize&&E<D?(--e.finalSize,e.finalLineHeight=e.finalSize*e.lh/e.s):(e.finalText=O,a=e.finalText.length,w=!1)}m=-S,b=0;var A=0,ee;for(i=0;i<a;i+=1)if(o=!1,ee=e.finalText[i],C=ee.charCodeAt(0),C===13||C===3?(A=0,p.push(m),h=m>h?m:h,m=-2*S,c=``,o=!0,f+=1):c=ee,t.chars?(y=t.getCharData(ee,v.fStyle,t.getFontByName(e.f).fFamily),b=o?0:y.w*e.finalSize/100):b=t.measureText(c,e.f,e.finalSize),ee===` `?A+=b+S:(m+=b+S+A,A=0),r.push({l:b,an:b,add:u,n:o,anIndexes:[],val:c,line:f,animatorJustifyOffset:0}),l==2){if(u+=b,c===``||c===` `||i===a-1){for((c===``||c===` `)&&(u-=b);d<=i;)r[d].an=u,r[d].ind=s,r[d].extra=b,d+=1;s+=1,u=0}}else if(l==3){if(u+=b,c===``||i===a-1){for(c===``&&(u-=b);d<=i;)r[d].an=u,r[d].ind=s,r[d].extra=b,d+=1;u=0,s+=1}}else r[s].ind=s,r[s].extra=0,s+=1;if(e.l=r,h=m>h?m:h,p.push(m),e.sz)e.boxWidth=e.sz[0],e.justifyOffset=0;else switch(e.boxWidth=h,e.j){case 1:e.justifyOffset=-e.boxWidth;break;case 2:e.justifyOffset=-e.boxWidth/2;break;default:e.justifyOffset=0}e.lineWidths=p;var j=n.a,M,N;_=j.length;var te,P,F=[];for(g=0;g<_;g+=1){for(M=j[g],M.a.sc&&(e.strokeColorAnim=!0),M.a.sw&&(e.strokeWidthAnim=!0),(M.a.fc||M.a.fh||M.a.fs||M.a.fb)&&(e.fillColorAnim=!0),P=0,te=M.s.b,i=0;i<a;i+=1)N=r[i],N.anIndexes[g]=P,(te==1&&N.val!==``||te==2&&N.val!==``&&N.val!==` `||te==3&&(N.n||N.val==` `||i==a-1)||te==4&&(N.n||i==a-1))&&(M.s.rn===1&&F.push(P),P+=1);n.a[g].s.totalChars=P;var ne=-1,re;if(M.s.rn===1)for(i=0;i<a;i+=1)N=r[i],ne!=N.anIndexes[g]&&(ne=N.anIndexes[g],re=F.splice(Math.floor(Math.random()*F.length),1)[0]),N.anIndexes[g]=re}e.yOffset=e.finalLineHeight||e.finalSize*1.2,e.ls=e.ls||0,e.ascent=v.ascent*e.finalSize/100},Jn.prototype.updateDocumentData=function(e,t){t=t===void 0?this.keysIndex:t;var n=this.copyData({},this.data.d.k[t].s);n=this.copyData(n,e),this.data.d.k[t].s=n,this.recalculate(t),this.setCurrentData(n),this.elem.addDynamicProperty(this)},Jn.prototype.recalculate=function(e){var t=this.data.d.k[e].s;t.__complete=!1,this.keysIndex=0,this._isFirstFrame=!0,this.getValue(t)},Jn.prototype.canResizeFont=function(e){this.canResize=e,this.recalculate(this.keysIndex),this.elem.addDynamicProperty(this)},Jn.prototype.setMinimumFontSize=function(e){this.minimumFontSize=Math.floor(e)||1,this.recalculate(this.keysIndex),this.elem.addDynamicProperty(this)};var Yn=function(){var e=Math.max,t=Math.min,n=Math.floor;function r(e,t){this._currentTextLength=-1,this.k=!1,this.data=t,this.elem=e,this.comp=e.comp,this.finalS=0,this.finalE=0,this.initDynamicPropertyContainer(e),this.s=H.getProp(e,t.s||{k:0},0,0,this),this.e=`e`in t?H.getProp(e,t.e,0,0,this):{v:100},this.o=H.getProp(e,t.o||{k:0},0,0,this),this.xe=H.getProp(e,t.xe||{k:0},0,0,this),this.ne=H.getProp(e,t.ne||{k:0},0,0,this),this.sm=H.getProp(e,t.sm||{k:100},0,0,this),this.a=H.getProp(e,t.a,0,.01,this),this.dynamicProperties.length||this.getValue()}r.prototype={getMult:function(r){this._currentTextLength!==this.elem.textProperty.currentData.l.length&&this.getValue();var i=0,a=0,o=1,s=1;this.ne.v>0?i=this.ne.v/100:a=-this.ne.v/100,this.xe.v>0?o=1-this.xe.v/100:s=1+this.xe.v/100;var c=Ce.getBezierEasing(i,a,o,s).get,l=0,u=this.finalS,d=this.finalE,f=this.data.sh;if(f===2)l=d===u?+(r>=d):e(0,t(.5/(d-u)+(r-u)/(d-u),1)),l=c(l);else if(f===3)l=d===u?r>=d?0:1:1-e(0,t(.5/(d-u)+(r-u)/(d-u),1)),l=c(l);else if(f===4)d===u?l=0:(l=e(0,t(.5/(d-u)+(r-u)/(d-u),1)),l<.5?l*=2:l=1-2*(l-.5)),l=c(l);else if(f===5){if(d===u)l=0;else{var p=d-u;r=t(e(0,r+.5-u),d-u);var m=-p/2+r,h=p/2;l=Math.sqrt(1-m*m/(h*h))}l=c(l)}else f===6?(d===u?l=0:(r=t(e(0,r+.5-u),d-u),l=(1+Math.cos(Math.PI+Math.PI*2*r/(d-u)))/2),l=c(l)):(r>=n(u)&&(l=r-u<0?e(0,t(t(d,1)-(u-r),1)):e(0,t(d-r,1))),l=c(l));if(this.sm.v!==100){var g=this.sm.v*.01;g===0&&(g=1e-8);var _=.5-g*.5;l<_?l=0:(l=(l-_)/g,l>1&&(l=1))}return l*this.a.v},getValue:function(e){this.iterateDynamicProperties(),this._mdf=e||this._mdf,this._currentTextLength=this.elem.textProperty.currentData.l.length||0,e&&this.data.r===2&&(this.e.v=this._currentTextLength);var t=this.data.r===2?1:100/this.data.totalChars,n=this.o.v/t,r=this.s.v/t+n,i=this.e.v/t+n;if(r>i){var a=r;r=i,i=a}this.finalS=r,this.finalE=i}},u([Ue],r);function i(e,t,n){return new r(e,t,n)}return{getTextSelectorProp:i}}();function Xn(e,t,n){var r={propType:!1},i=H.getProp,a=t.a;this.a={r:a.r?i(e,a.r,0,D,n):r,rx:a.rx?i(e,a.rx,0,D,n):r,ry:a.ry?i(e,a.ry,0,D,n):r,sk:a.sk?i(e,a.sk,0,D,n):r,sa:a.sa?i(e,a.sa,0,D,n):r,s:a.s?i(e,a.s,1,.01,n):r,a:a.a?i(e,a.a,1,0,n):r,o:a.o?i(e,a.o,0,.01,n):r,p:a.p?i(e,a.p,1,0,n):r,sw:a.sw?i(e,a.sw,0,0,n):r,sc:a.sc?i(e,a.sc,1,0,n):r,fc:a.fc?i(e,a.fc,1,0,n):r,fh:a.fh?i(e,a.fh,0,0,n):r,fs:a.fs?i(e,a.fs,0,.01,n):r,fb:a.fb?i(e,a.fb,0,.01,n):r,t:a.t?i(e,a.t,0,0,n):r},this.s=Yn.getTextSelectorProp(e,t.s,n),this.s.t=t.s.t}function Zn(e,t,n){this._isFirstFrame=!0,this._hasMaskedPath=!1,this._frameId=-1,this._textData=e,this._renderType=t,this._elem=n,this._animatorsData=m(this._textData.a.length),this._pathData={},this._moreOptions={alignment:{}},this.renderedLetters=[],this.lettersChangedFlag=!1,this.initDynamicPropertyContainer(n)}Zn.prototype.searchProperties=function(){var e,t=this._textData.a.length,n,r=H.getProp;for(e=0;e<t;e+=1)n=this._textData.a[e],this._animatorsData[e]=new Xn(this._elem,n,this);this._textData.p&&`m`in this._textData.p?(this._pathData={a:r(this._elem,this._textData.p.a,0,0,this),f:r(this._elem,this._textData.p.f,0,0,this),l:r(this._elem,this._textData.p.l,0,0,this),r:r(this._elem,this._textData.p.r,0,0,this),p:r(this._elem,this._textData.p.p,0,0,this),m:this._elem.maskManager.getMaskProperty(this._textData.p.m)},this._hasMaskedPath=!0):this._hasMaskedPath=!1,this._moreOptions.alignment=r(this._elem,this._textData.m.a,1,0,this)},Zn.prototype.getMeasures=function(e,t){if(this.lettersChangedFlag=t,this._mdf||this._isFirstFrame||t||this._hasMaskedPath&&this._pathData.m._mdf){this._isFirstFrame=!1;var n=this._moreOptions.alignment.v,r=this._animatorsData,i=this._textData,a=this.mHelper,o=this._renderType,s=this.renderedLetters.length,c,l,u,d,f=e.l,p,m,h,g,_,v,y,b,x,S,C,w,T,E,D;if(this._hasMaskedPath){if(D=this._pathData.m,!this._pathData.n||this._pathData._mdf){var O=D.v;this._pathData.r.v&&(O=O.reverse()),p={tLength:0,segments:[]},d=O._length-1;var k;for(w=0,u=0;u<d;u+=1)k=V.buildBezierData(O.v[u],O.v[u+1],[O.o[u][0]-O.v[u][0],O.o[u][1]-O.v[u][1]],[O.i[u+1][0]-O.v[u+1][0],O.i[u+1][1]-O.v[u+1][1]]),p.tLength+=k.segmentLength,p.segments.push(k),w+=k.segmentLength;u=d,D.v.c&&(k=V.buildBezierData(O.v[u],O.v[0],[O.o[u][0]-O.v[u][0],O.o[u][1]-O.v[u][1]],[O.i[0][0]-O.v[0][0],O.i[0][1]-O.v[0][1]]),p.tLength+=k.segmentLength,p.segments.push(k),w+=k.segmentLength),this._pathData.pi=p}if(p=this._pathData.pi,m=this._pathData.f.v,y=0,v=1,g=0,_=!0,S=p.segments,m<0&&D.v.c)for(p.tLength<Math.abs(m)&&(m=-Math.abs(m)%p.tLength),y=S.length-1,x=S[y].points,v=x.length-1;m<0;)m+=x[v].partialLength,--v,v<0&&(--y,x=S[y].points,v=x.length-1);x=S[y].points,b=x[v-1],h=x[v],C=h.partialLength}d=f.length,c=0,l=0;var A=e.finalSize*1.2*.714,ee=!0,j,M,N,te=r.length,P,F,ne=-1,oe,se,ce,le=m,ue=y,de=v,fe=-1,pe,I,me,L,R,he,ge,_e,ve=``,ye=this.defaultPropsArray,be;if(e.j===2||e.j===1){var xe=0,Se=0,z=e.j===2?-.5:-1,B=0,Ce=!0;for(u=0;u<d;u+=1)if(f[u].n){for(xe&&(xe+=Se);B<u;)f[B].animatorJustifyOffset=xe,B+=1;xe=0,Ce=!0}else{for(N=0;N<te;N+=1)j=r[N].a,j.t.propType&&(Ce&&e.j===2&&(Se+=j.t.v*z),M=r[N].s,F=M.getMult(f[u].anIndexes[N],i.a[N].s.totalChars),F.length?xe+=j.t.v*F[0]*z:xe+=j.t.v*F*z);Ce=!1}for(xe&&(xe+=Se);B<u;)f[B].animatorJustifyOffset=xe,B+=1}for(u=0;u<d;u+=1){if(a.reset(),pe=1,f[u].n)c=0,l+=e.yOffset,l+=+!!ee,m=le,ee=!1,this._hasMaskedPath&&(y=ue,v=de,x=S[y].points,b=x[v-1],h=x[v],C=h.partialLength,g=0),ve=``,_e=``,he=``,be=``,ye=this.defaultPropsArray;else{if(this._hasMaskedPath){if(fe!==f[u].line){switch(e.j){case 1:m+=w-e.lineWidths[f[u].line];break;case 2:m+=(w-e.lineWidths[f[u].line])/2}fe=f[u].line}ne!==f[u].ind&&(f[ne]&&(m+=f[ne].extra),m+=f[u].an/2,ne=f[u].ind),m+=n[0]*f[u].an*.005;var we=0;for(N=0;N<te;N+=1)j=r[N].a,j.p.propType&&(M=r[N].s,F=M.getMult(f[u].anIndexes[N],i.a[N].s.totalChars),F.length?we+=j.p.v[0]*F[0]:we+=j.p.v[0]*F),j.a.propType&&(M=r[N].s,F=M.getMult(f[u].anIndexes[N],i.a[N].s.totalChars),F.length?we+=j.a.v[0]*F[0]:we+=j.a.v[0]*F);for(_=!0,this._pathData.a.v&&(m=f[0].an*.5+(w-this._pathData.f.v-f[0].an*.5-f[f.length-1].an*.5)*ne/(d-1),m+=this._pathData.f.v);_;)g+C>=m+we||!x?(T=(m+we-g)/h.partialLength,se=b.point[0]+(h.point[0]-b.point[0])*T,ce=b.point[1]+(h.point[1]-b.point[1])*T,a.translate(-n[0]*f[u].an*.005,-(n[1]*A)*.01),_=!1):x&&(g+=h.partialLength,v+=1,v>=x.length&&(v=0,y+=1,S[y]?x=S[y].points:D.v.c?(v=0,y=0,x=S[y].points):(g-=h.partialLength,x=null)),x&&(b=h,h=x[v],C=h.partialLength));oe=f[u].an/2-f[u].add,a.translate(-oe,0,0)}else oe=f[u].an/2-f[u].add,a.translate(-oe,0,0),a.translate(-n[0]*f[u].an*.005,-n[1]*A*.01,0);for(N=0;N<te;N+=1)j=r[N].a,j.t.propType&&(M=r[N].s,F=M.getMult(f[u].anIndexes[N],i.a[N].s.totalChars),(c!==0||e.j!==0)&&(this._hasMaskedPath?F.length?m+=j.t.v*F[0]:m+=j.t.v*F:F.length?c+=j.t.v*F[0]:c+=j.t.v*F));for(e.strokeWidthAnim&&(me=e.sw||0),e.strokeColorAnim&&(I=e.sc?[e.sc[0],e.sc[1],e.sc[2]]:[0,0,0]),e.fillColorAnim&&e.fc&&(L=[e.fc[0],e.fc[1],e.fc[2]]),N=0;N<te;N+=1)j=r[N].a,j.a.propType&&(M=r[N].s,F=M.getMult(f[u].anIndexes[N],i.a[N].s.totalChars),F.length?a.translate(-j.a.v[0]*F[0],-j.a.v[1]*F[1],j.a.v[2]*F[2]):a.translate(-j.a.v[0]*F,-j.a.v[1]*F,j.a.v[2]*F));for(N=0;N<te;N+=1)j=r[N].a,j.s.propType&&(M=r[N].s,F=M.getMult(f[u].anIndexes[N],i.a[N].s.totalChars),F.length?a.scale(1+(j.s.v[0]-1)*F[0],1+(j.s.v[1]-1)*F[1],1):a.scale(1+(j.s.v[0]-1)*F,1+(j.s.v[1]-1)*F,1));for(N=0;N<te;N+=1){if(j=r[N].a,M=r[N].s,F=M.getMult(f[u].anIndexes[N],i.a[N].s.totalChars),j.sk.propType&&(F.length?a.skewFromAxis(-j.sk.v*F[0],j.sa.v*F[1]):a.skewFromAxis(-j.sk.v*F,j.sa.v*F)),j.r.propType&&(F.length?a.rotateZ(-j.r.v*F[2]):a.rotateZ(-j.r.v*F)),j.ry.propType&&(F.length?a.rotateY(j.ry.v*F[1]):a.rotateY(j.ry.v*F)),j.rx.propType&&(F.length?a.rotateX(j.rx.v*F[0]):a.rotateX(j.rx.v*F)),j.o.propType&&(F.length?pe+=(j.o.v*F[0]-pe)*F[0]:pe+=(j.o.v*F-pe)*F),e.strokeWidthAnim&&j.sw.propType&&(F.length?me+=j.sw.v*F[0]:me+=j.sw.v*F),e.strokeColorAnim&&j.sc.propType)for(R=0;R<3;R+=1)F.length?I[R]+=(j.sc.v[R]-I[R])*F[0]:I[R]+=(j.sc.v[R]-I[R])*F;if(e.fillColorAnim&&e.fc){if(j.fc.propType)for(R=0;R<3;R+=1)F.length?L[R]+=(j.fc.v[R]-L[R])*F[0]:L[R]+=(j.fc.v[R]-L[R])*F;j.fh.propType&&(L=F.length?ae(L,j.fh.v*F[0]):ae(L,j.fh.v*F)),j.fs.propType&&(L=F.length?re(L,j.fs.v*F[0]):re(L,j.fs.v*F)),j.fb.propType&&(L=F.length?ie(L,j.fb.v*F[0]):ie(L,j.fb.v*F))}}for(N=0;N<te;N+=1)j=r[N].a,j.p.propType&&(M=r[N].s,F=M.getMult(f[u].anIndexes[N],i.a[N].s.totalChars),this._hasMaskedPath?F.length?a.translate(0,j.p.v[1]*F[0],-j.p.v[2]*F[1]):a.translate(0,j.p.v[1]*F,-j.p.v[2]*F):F.length?a.translate(j.p.v[0]*F[0],j.p.v[1]*F[1],-j.p.v[2]*F[2]):a.translate(j.p.v[0]*F,j.p.v[1]*F,-j.p.v[2]*F));if(e.strokeWidthAnim&&(he=me<0?0:me),e.strokeColorAnim&&(ge=`rgb(`+Math.round(I[0]*255)+`,`+Math.round(I[1]*255)+`,`+Math.round(I[2]*255)+`)`),e.fillColorAnim&&e.fc&&(_e=`rgb(`+Math.round(L[0]*255)+`,`+Math.round(L[1]*255)+`,`+Math.round(L[2]*255)+`)`),this._hasMaskedPath){if(a.translate(0,-e.ls),a.translate(0,n[1]*A*.01+l,0),this._pathData.p.v){E=(h.point[1]-b.point[1])/(h.point[0]-b.point[0]);var Te=Math.atan(E)*180/Math.PI;h.point[0]<b.point[0]&&(Te+=180),a.rotate(-Te*Math.PI/180)}a.translate(se,ce,0),m-=n[0]*f[u].an*.005,f[u+1]&&ne!==f[u+1].ind&&(m+=f[u].an/2,m+=e.tr*.001*e.finalSize)}else{switch(a.translate(c,l,0),e.ps&&a.translate(e.ps[0],e.ps[1]+e.ascent,0),e.j){case 1:a.translate(f[u].animatorJustifyOffset+e.justifyOffset+(e.boxWidth-e.lineWidths[f[u].line]),0,0);break;case 2:a.translate(f[u].animatorJustifyOffset+e.justifyOffset+(e.boxWidth-e.lineWidths[f[u].line])/2,0,0)}a.translate(0,-e.ls),a.translate(oe,0,0),a.translate(n[0]*f[u].an*.005,n[1]*A*.01,0),c+=f[u].l+e.tr*.001*e.finalSize}o===`html`?ve=a.toCSS():o===`svg`?ve=a.to2dCSS():ye=[a.props[0],a.props[1],a.props[2],a.props[3],a.props[4],a.props[5],a.props[6],a.props[7],a.props[8],a.props[9],a.props[10],a.props[11],a.props[12],a.props[13],a.props[14],a.props[15]],be=pe}s<=u?(P=new qn(be,he,ge,_e,ve,ye),this.renderedLetters.push(P),s+=1,this.lettersChangedFlag=!0):(P=this.renderedLetters[u],this.lettersChangedFlag=P.update(be,he,ge,_e,ve,ye)||this.lettersChangedFlag)}}},Zn.prototype.getValue=function(){this._elem.globalData.frameId!==this._frameId&&(this._frameId=this._elem.globalData.frameId,this.iterateDynamicProperties())},Zn.prototype.mHelper=new Ye,Zn.prototype.defaultPropsArray=[],u([Ue],Zn);function X(){}X.prototype.initElement=function(e,t,n){this.lettersChangedFlag=!0,this.initFrame(),this.initBaseData(e,t,n),this.textProperty=new Jn(this,e.t,this.dynamicProperties),this.textAnimator=new Zn(e.t,this.renderType,this),this.initTransform(e,t,n),this.initHierarchy(),this.initRenderable(),this.initRendererElement(),this.createContainerElements(),this.createRenderableComponents(),this.createContent(),this.hide(),this.textAnimator.searchProperties(this.dynamicProperties)},X.prototype.prepareFrame=function(e){this._mdf=!1,this.prepareRenderableFrame(e),this.prepareProperties(e,this.isInRange)},X.prototype.createPathShape=function(e,t){var n,r=t.length,i,a=``;for(n=0;n<r;n+=1)t[n].ty===`sh`&&(i=t[n].ks.k,a+=Gn(i,i.i.length,!0,e));return a},X.prototype.updateDocumentData=function(e,t){this.textProperty.updateDocumentData(e,t)},X.prototype.canResizeFont=function(e){this.textProperty.canResizeFont(e)},X.prototype.setMinimumFontSize=function(e){this.textProperty.setMinimumFontSize(e)},X.prototype.applyTextPropertiesToMatrix=function(e,t,n,r,i){switch(e.ps&&t.translate(e.ps[0],e.ps[1]+e.ascent,0),t.translate(0,-e.ls,0),e.j){case 1:t.translate(e.justifyOffset+(e.boxWidth-e.lineWidths[n]),0,0);break;case 2:t.translate(e.justifyOffset+(e.boxWidth-e.lineWidths[n])/2,0,0)}t.translate(r,i,0)},X.prototype.buildColor=function(e){return`rgb(`+Math.round(e[0]*255)+`,`+Math.round(e[1]*255)+`,`+Math.round(e[2]*255)+`)`},X.prototype.emptyProp=new qn,X.prototype.destroy=function(){},X.prototype.validateText=function(){(this.textProperty._mdf||this.textProperty._isFirstFrame)&&(this.buildNewText(),this.textProperty._isFirstFrame=!1,this.textProperty._mdf=!1)};var Qn={shapes:[]};function Z(e,t,n){this.textSpans=[],this.renderType=`svg`,this.initElement(e,t,n)}u([mn,bn,Dn,On,hn,kn,X],Z),Z.prototype.createContent=function(){this.data.singleShape&&!this.globalData.fontManager.chars&&(this.textContainer=I(`text`))},Z.prototype.buildTextContents=function(e){for(var t=0,n=e.length,r=[],i=``;t<n;)e[t]===`\r`||e[t]===``?(r.push(i),i=``):i+=e[t],t+=1;return r.push(i),r},Z.prototype.buildShapeData=function(e,t){if(e.shapes&&e.shapes.length){var n=e.shapes[0];if(n.it){var r=n.it[n.it.length-1];r.s&&(r.s.k[0]=t,r.s.k[1]=t)}}return e},Z.prototype.buildNewText=function(){this.addDynamicProperty(this);var e,t,n=this.textProperty.currentData;this.renderedLetters=m(n?n.l.length:0),n.fc?this.layerElement.setAttribute(`fill`,this.buildColor(n.fc)):this.layerElement.setAttribute(`fill`,`rgba(0,0,0,0)`),n.sc&&(this.layerElement.setAttribute(`stroke`,this.buildColor(n.sc)),this.layerElement.setAttribute(`stroke-width`,n.sw)),this.layerElement.setAttribute(`font-size`,n.finalSize);var r=this.globalData.fontManager.getFontByName(n.f);if(r.fClass)this.layerElement.setAttribute(`class`,r.fClass);else{this.layerElement.setAttribute(`font-family`,r.fFamily);var i=n.fWeight,a=n.fStyle;this.layerElement.setAttribute(`font-style`,a),this.layerElement.setAttribute(`font-weight`,i)}this.layerElement.setAttribute(`aria-label`,n.t);var o=n.l||[],s=!!this.globalData.fontManager.chars;t=o.length;var c,l=this.mHelper,u=``,d=this.data.singleShape,f=0,p=0,h=!0,g=n.tr*.001*n.finalSize;if(d&&!s&&!n.sz){var _=this.textContainer,v=`start`;switch(n.j){case 1:v=`end`;break;case 2:v=`middle`;break;default:v=`start`}_.setAttribute(`text-anchor`,v),_.setAttribute(`letter-spacing`,g);var y=this.buildTextContents(n.finalText);for(t=y.length,p=n.ps?n.ps[1]+n.ascent:0,e=0;e<t;e+=1)c=this.textSpans[e].span||I(`tspan`),c.textContent=y[e],c.setAttribute(`x`,0),c.setAttribute(`y`,p),c.style.display=`inherit`,_.appendChild(c),this.textSpans[e]||(this.textSpans[e]={span:null,glyph:null}),this.textSpans[e].span=c,p+=n.finalLineHeight;this.layerElement.appendChild(_)}else{var b=this.textSpans.length,x;for(e=0;e<t;e+=1){if(this.textSpans[e]||(this.textSpans[e]={span:null,childSpan:null,glyph:null}),!s||!d||e===0){if(c=b>e?this.textSpans[e].span:I(s?`g`:`text`),b<=e){if(c.setAttribute(`stroke-linecap`,`butt`),c.setAttribute(`stroke-linejoin`,`round`),c.setAttribute(`stroke-miterlimit`,`4`),this.textSpans[e].span=c,s){var S=I(`g`);c.appendChild(S),this.textSpans[e].childSpan=S}this.textSpans[e].span=c,this.layerElement.appendChild(c)}c.style.display=`inherit`}if(l.reset(),d&&(o[e].n&&(f=-g,p+=n.yOffset,p+=+!!h,h=!1),this.applyTextPropertiesToMatrix(n,l,o[e].line,f,p),f+=o[e].l||0,f+=g),s){x=this.globalData.fontManager.getCharData(n.finalText[e],r.fStyle,this.globalData.fontManager.getFontByName(n.f).fFamily);var C;if(x.t===1)C=new rr(x.data,this.globalData,this);else{var w=Qn;x.data&&x.data.shapes&&(w=this.buildShapeData(x.data,n.finalSize)),C=new Y(w,this.globalData,this)}if(this.textSpans[e].glyph){var T=this.textSpans[e].glyph;this.textSpans[e].childSpan.removeChild(T.layerElement),T.destroy()}this.textSpans[e].glyph=C,C._debug=!0,C.prepareFrame(0),C.renderFrame(),this.textSpans[e].childSpan.appendChild(C.layerElement),x.t===1&&this.textSpans[e].childSpan.setAttribute(`transform`,`scale(`+n.finalSize/100+`,`+n.finalSize/100+`)`)}else d&&c.setAttribute(`transform`,`translate(`+l.props[12]+`,`+l.props[13]+`)`),c.textContent=o[e].val,c.setAttributeNS(`http://www.w3.org/XML/1998/namespace`,`xml:space`,`preserve`)}d&&c&&c.setAttribute(`d`,u)}for(;e<this.textSpans.length;)this.textSpans[e].span.style.display=`none`,e+=1;this._sizeChanged=!0},Z.prototype.sourceRectAtTime=function(){if(this.prepareFrame(this.comp.renderedFrame-this.data.st),this.renderInnerContent(),this._sizeChanged){this._sizeChanged=!1;var e=this.layerElement.getBBox();this.bbox={top:e.y,left:e.x,width:e.width,height:e.height}}return this.bbox},Z.prototype.getValue=function(){var e,t=this.textSpans.length,n;for(this.renderedFrame=this.comp.renderedFrame,e=0;e<t;e+=1)n=this.textSpans[e].glyph,n&&(n.prepareFrame(this.comp.renderedFrame-this.data.st),n._mdf&&(this._mdf=!0))},Z.prototype.renderInnerContent=function(){if(this.validateText(),(!this.data.singleShape||this._mdf)&&(this.textAnimator.getMeasures(this.textProperty.currentData,this.lettersChangedFlag),this.lettersChangedFlag||this.textAnimator.lettersChangedFlag)){this._sizeChanged=!0;var e,t,n=this.textAnimator.renderedLetters,r=this.textProperty.currentData.l;t=r.length;var i,a,o;for(e=0;e<t;e+=1)r[e].n||(i=n[e],a=this.textSpans[e].span,o=this.textSpans[e].glyph,o&&o.renderFrame(),i._mdf.m&&a.setAttribute(`transform`,i.m),i._mdf.o&&a.setAttribute(`opacity`,i.o),i._mdf.sw&&a.setAttribute(`stroke-width`,i.sw),i._mdf.sc&&a.setAttribute(`stroke`,i.sc),i._mdf.fc&&a.setAttribute(`fill`,i.fc))}};function $n(e,t,n){this.initElement(e,t,n)}u([An],$n),$n.prototype.createContent=function(){var e=I(`rect`);e.setAttribute(`width`,this.data.sw),e.setAttribute(`height`,this.data.sh),e.setAttribute(`fill`,this.data.sc),this.layerElement.appendChild(e)};function er(e,t,n){this.initFrame(),this.initBaseData(e,t,n),this.initFrame(),this.initTransform(e,t,n),this.initHierarchy()}er.prototype.prepareFrame=function(e){this.prepareProperties(e,!0)},er.prototype.renderFrame=function(){},er.prototype.getBaseElement=function(){return null},er.prototype.destroy=function(){},er.prototype.sourceRectAtTime=function(){},er.prototype.hide=function(){},u([mn,bn,On,hn],er);function tr(){}u([vn],tr),tr.prototype.createNull=function(e){return new er(e,this.globalData,this)},tr.prototype.createShape=function(e){return new Y(e,this.globalData,this)},tr.prototype.createText=function(e){return new Z(e,this.globalData,this)},tr.prototype.createImage=function(e){return new An(e,this.globalData,this)},tr.prototype.createSolid=function(e){return new $n(e,this.globalData,this)},tr.prototype.configAnimation=function(e){this.svgElement.setAttribute(`xmlns`,`http://www.w3.org/2000/svg`),this.svgElement.setAttribute(`xmlns:xlink`,`http://www.w3.org/1999/xlink`),this.renderConfig.viewBoxSize?this.svgElement.setAttribute(`viewBox`,this.renderConfig.viewBoxSize):this.svgElement.setAttribute(`viewBox`,`0 0 `+e.w+` `+e.h),this.renderConfig.viewBoxOnly||(this.svgElement.setAttribute(`width`,e.w),this.svgElement.setAttribute(`height`,e.h),this.svgElement.style.width=`100%`,this.svgElement.style.height=`100%`,this.svgElement.style.transform=`translate3d(0,0,0)`,this.svgElement.style.contentVisibility=this.renderConfig.contentVisibility),this.renderConfig.width&&this.svgElement.setAttribute(`width`,this.renderConfig.width),this.renderConfig.height&&this.svgElement.setAttribute(`height`,this.renderConfig.height),this.renderConfig.className&&this.svgElement.setAttribute(`class`,this.renderConfig.className),this.renderConfig.id&&this.svgElement.setAttribute(`id`,this.renderConfig.id),this.renderConfig.focusable!==void 0&&this.svgElement.setAttribute(`focusable`,this.renderConfig.focusable),this.svgElement.setAttribute(`preserveAspectRatio`,this.renderConfig.preserveAspectRatio),this.animationItem.wrapper.appendChild(this.svgElement);var t=this.globalData.defs;this.setupGlobalData(e,t),this.globalData.progressiveLoad=this.renderConfig.progressiveLoad,this.data=e;var n=I(`clipPath`),r=I(`rect`);r.setAttribute(`width`,e.w),r.setAttribute(`height`,e.h),r.setAttribute(`x`,0),r.setAttribute(`y`,0);var i=P();n.setAttribute(`id`,i),n.appendChild(r),this.layerElement.setAttribute(`clip-path`,`url(`+c()+`#`+i+`)`),t.appendChild(n),this.layers=e.layers,this.elements=m(e.layers.length)},tr.prototype.destroy=function(){this.animationItem.wrapper&&(this.animationItem.wrapper.innerText=``),this.layerElement=null,this.globalData.defs=null;var e,t=this.layers?this.layers.length:0;for(e=0;e<t;e+=1)this.elements[e]&&this.elements[e].destroy&&this.elements[e].destroy();this.elements.length=0,this.destroyed=!0,this.animationItem=null},tr.prototype.updateContainerSize=function(){},tr.prototype.findIndexByInd=function(e){var t=0,n=this.layers.length;for(t=0;t<n;t+=1)if(this.layers[t].ind===e)return t;return-1},tr.prototype.buildItem=function(e){var t=this.elements;if(!(t[e]||this.layers[e].ty===99)){t[e]=!0;var n=this.createItem(this.layers[e]);if(t[e]=n,le()&&(this.layers[e].ty===0&&this.globalData.projectInterface.registerComposition(n),n.initExpressions()),this.appendElementInPos(n,e),this.layers[e].tt){var r=`tp`in this.layers[e]?this.findIndexByInd(this.layers[e].tp):e-1;if(r===-1)return;if(!this.elements[r]||this.elements[r]===!0)this.buildItem(r),this.addPendingElement(n);else{var i=t[r].getMatte(this.layers[e].tt);n.setMatte(i)}}}},tr.prototype.checkPendingElements=function(){for(;this.pendingElements.length;){var e=this.pendingElements.pop();if(e.checkParenting(),e.data.tt)for(var t=0,n=this.elements.length;t<n;){if(this.elements[t]===e){var r=`tp`in e.data?this.findIndexByInd(e.data.tp):t-1,i=this.elements[r].getMatte(this.layers[t].tt);e.setMatte(i);break}t+=1}}},tr.prototype.renderFrame=function(e){if(!(this.renderedFrame===e||this.destroyed)){e===null?e=this.renderedFrame:this.renderedFrame=e,this.globalData.frameNum=e,this.globalData.frameId+=1,this.globalData.projectInterface.currentFrame=e,this.globalData._mdf=!1;var t,n=this.layers.length;for(this.completeLayers||this.checkLayers(e),t=n-1;t>=0;--t)(this.completeLayers||this.elements[t])&&this.elements[t].prepareFrame(e-this.layers[t].st);if(this.globalData._mdf)for(t=0;t<n;t+=1)(this.completeLayers||this.elements[t])&&this.elements[t].renderFrame()}},tr.prototype.appendElementInPos=function(e,t){var n=e.getBaseElement();if(n){for(var r=0,i;r<t;)this.elements[r]&&this.elements[r]!==!0&&this.elements[r].getBaseElement()&&(i=this.elements[r].getBaseElement()),r+=1;i?this.layerElement.insertBefore(n,i):this.layerElement.appendChild(n)}},tr.prototype.hide=function(){this.layerElement.style.display=`none`},tr.prototype.show=function(){this.layerElement.style.display=`block`};function nr(){}u([mn,bn,On,hn,kn],nr),nr.prototype.initElement=function(e,t,n){this.initFrame(),this.initBaseData(e,t,n),this.initTransform(e,t,n),this.initRenderable(),this.initHierarchy(),this.initRendererElement(),this.createContainerElements(),this.createRenderableComponents(),(this.data.xt||!t.progressiveLoad)&&this.buildAllItems(),this.hide()},nr.prototype.prepareFrame=function(e){if(this._mdf=!1,this.prepareRenderableFrame(e),this.prepareProperties(e,this.isInRange),this.isInRange||this.data.xt){if(this.tm._placeholder)this.renderedFrame=e/this.data.sr;else{var t=this.tm.v;t===this.data.op&&(t=this.data.op-1),this.renderedFrame=t}var n,r=this.elements.length;for(this.completeLayers||this.checkLayers(this.renderedFrame),n=r-1;n>=0;--n)(this.completeLayers||this.elements[n])&&(this.elements[n].prepareFrame(this.renderedFrame-this.layers[n].st),this.elements[n]._mdf&&(this._mdf=!0))}},nr.prototype.renderInnerContent=function(){var e,t=this.layers.length;for(e=0;e<t;e+=1)(this.completeLayers||this.elements[e])&&this.elements[e].renderFrame()},nr.prototype.setElements=function(e){this.elements=e},nr.prototype.getElements=function(){return this.elements},nr.prototype.destroyElements=function(){var e,t=this.layers.length;for(e=0;e<t;e+=1)this.elements[e]&&this.elements[e].destroy()},nr.prototype.destroy=function(){this.destroyElements(),this.destroyBaseElement()};function rr(e,t,n){this.layers=e.layers,this.supports3d=!0,this.completeLayers=!1,this.pendingElements=[],this.elements=this.layers?m(this.layers.length):[],this.initElement(e,t,n),this.tm=e.tm?H.getProp(this,e.tm,0,t.frameRate,this):{_placeholder:!0}}u([tr,nr,Dn],rr),rr.prototype.createComp=function(e){return new rr(e,this.globalData,this)};function ir(e,t){this.animationItem=e,this.layers=null,this.renderedFrame=-1,this.svgElement=I(`svg`);var n=``;if(t&&t.title){var r=I(`title`),i=P();r.setAttribute(`id`,i),r.textContent=t.title,this.svgElement.appendChild(r),n+=i}if(t&&t.description){var a=I(`desc`),o=P();a.setAttribute(`id`,o),a.textContent=t.description,this.svgElement.appendChild(a),n+=` `+o}n&&this.svgElement.setAttribute(`aria-labelledby`,n);var s=I(`defs`);this.svgElement.appendChild(s);var c=I(`g`);this.svgElement.appendChild(c),this.layerElement=c,this.renderConfig={preserveAspectRatio:t&&t.preserveAspectRatio||`xMidYMid meet`,imagePreserveAspectRatio:t&&t.imagePreserveAspectRatio||`xMidYMid slice`,contentVisibility:t&&t.contentVisibility||`visible`,progressiveLoad:t&&t.progressiveLoad||!1,hideOnTransparent:!(t&&t.hideOnTransparent===!1),viewBoxOnly:t&&t.viewBoxOnly||!1,viewBoxSize:t&&t.viewBoxSize||!1,className:t&&t.className||``,id:t&&t.id||``,focusable:t&&t.focusable,filterSize:{width:t&&t.filterSize&&t.filterSize.width||`100%`,height:t&&t.filterSize&&t.filterSize.height||`100%`,x:t&&t.filterSize&&t.filterSize.x||`0%`,y:t&&t.filterSize&&t.filterSize.y||`0%`},width:t&&t.width,height:t&&t.height,runExpressions:!t||t.runExpressions===void 0||t.runExpressions},this.globalData={_mdf:!1,frameNum:-1,defs:s,renderConfig:this.renderConfig},this.elements=[],this.pendingElements=[],this.destroyed=!1,this.rendererType=`svg`}u([tr],ir),ir.prototype.createComp=function(e){return new rr(e,this.globalData,this)};function ar(){this.sequences={},this.sequenceList=[],this.transform_key_count=0}ar.prototype={addTransformSequence:function(e){var t,n=e.length,r=`_`;for(t=0;t<n;t+=1)r+=e[t].transform.key+`_`;var i=this.sequences[r];return i||(i={transforms:[].concat(e),finalTransform:new Ye,_mdf:!1},this.sequences[r]=i,this.sequenceList.push(i)),i},processSequence:function(e,t){for(var n=0,r=e.transforms.length,i=t;n<r&&!t;){if(e.transforms[n].transform.mProps._mdf){i=!0;break}n+=1}if(i)for(e.finalTransform.reset(),n=r-1;n>=0;--n)e.finalTransform.multiply(e.transforms[n].transform.mProps.v);e._mdf=i},processSequences:function(e){var t,n=this.sequenceList.length;for(t=0;t<n;t+=1)this.processSequence(this.sequenceList[t],e)},getNewKey:function(){return this.transform_key_count+=1,`_`+this.transform_key_count}};var or=function(){var e=`__lottie_element_luma_buffer`,t=null,n=null,r=null;function i(){var t=I(`svg`),n=I(`filter`),r=I(`feColorMatrix`);return n.setAttribute(`id`,e),r.setAttribute(`type`,`matrix`),r.setAttribute(`color-interpolation-filters`,`sRGB`),r.setAttribute(`values`,`0.3, 0.3, 0.3, 0, 0, 0.3, 0.3, 0.3, 0, 0, 0.3, 0.3, 0.3, 0, 0, 0.3, 0.3, 0.3, 0, 0`),n.appendChild(r),t.appendChild(n),t.setAttribute(`id`,e+`_svg`),Cn.svgLumaHidden&&(t.style.display=`none`),t}function a(){t||(r=i(),document.body.appendChild(r),t=l(`canvas`),n=t.getContext(`2d`),n.filter=`url(#`+e+`)`,n.fillStyle=`rgba(0,0,0,0)`,n.fillRect(0,0,1,1))}function o(r){return t||a(),t.width=r.width,t.height=r.height,n.filter=`url(#`+e+`)`,t}return{load:a,get:o}};function sr(e,t){if(Cn.offscreenCanvas)return new OffscreenCanvas(e,t);var n=l(`canvas`);return n.width=e,n.height=t,n}var cr=function(){return{loadLumaCanvas:or.load,getLumaCanvas:or.get,createCanvas:sr}}(),lr={};function ur(e){var t,n=e.data.ef?e.data.ef.length:0;this.filters=[];var r;for(t=0;t<n;t+=1){r=null;var i=e.data.ef[t].ty;if(lr[i]){var a=lr[i].effect;r=new a(e.effectsManager.effectElements[t],e)}r&&this.filters.push(r)}this.filters.length&&e.addRenderableComponent(this)}ur.prototype.renderFrame=function(e){var t,n=this.filters.length;for(t=0;t<n;t+=1)this.filters[t].renderFrame(e)},ur.prototype.getEffects=function(e){var t,n=this.filters.length,r=[];for(t=0;t<n;t+=1)this.filters[t].type===e&&r.push(this.filters[t]);return r};function dr(e,t){this.data=e,this.element=t,this.masksProperties=this.data.masksProperties||[],this.viewData=m(this.masksProperties.length);var n,r=this.masksProperties.length,i=!1;for(n=0;n<r;n+=1)this.masksProperties[n].mode!==`n`&&(i=!0),this.viewData[n]=Je.getShapeProp(this.element,this.masksProperties[n],3);this.hasMasks=i,i&&this.element.addRenderableComponent(this)}dr.prototype.renderFrame=function(){if(this.hasMasks){var e=this.element.finalTransform.mat,t=this.element.canvasContext,n,r=this.masksProperties.length,i,a,o;for(t.beginPath(),n=0;n<r;n+=1)if(this.masksProperties[n].mode!==`n`){this.masksProperties[n].inv&&(t.moveTo(0,0),t.lineTo(this.element.globalData.compSize.w,0),t.lineTo(this.element.globalData.compSize.w,this.element.globalData.compSize.h),t.lineTo(0,this.element.globalData.compSize.h),t.lineTo(0,0)),o=this.viewData[n].v,i=e.applyToPointArray(o.v[0][0],o.v[0][1],0),t.moveTo(i[0],i[1]);var s,c=o._length;for(s=1;s<c;s+=1)a=e.applyToTriplePoints(o.o[s-1],o.i[s],o.v[s]),t.bezierCurveTo(a[0],a[1],a[2],a[3],a[4],a[5]);a=e.applyToTriplePoints(o.o[s-1],o.i[0],o.v[0]),t.bezierCurveTo(a[0],a[1],a[2],a[3],a[4],a[5])}this.element.globalData.renderer.save(!0),t.clip()}},dr.prototype.getMaskProperty=xn.prototype.getMaskProperty,dr.prototype.destroy=function(){this.element=null};function fr(){}var pr={1:`source-in`,2:`source-out`,3:`source-in`,4:`source-out`};fr.prototype={createElements:function(){},initRendererElement:function(){},createContainerElements:function(){if(this.data.tt>=1){this.buffers=[];var e=this.globalData.canvasContext,t=cr.createCanvas(e.canvas.width,e.canvas.height);this.buffers.push(t);var n=cr.createCanvas(e.canvas.width,e.canvas.height);this.buffers.push(n),this.data.tt>=3&&!document._isProxy&&cr.loadLumaCanvas()}this.canvasContext=this.globalData.canvasContext,this.transformCanvas=this.globalData.transformCanvas,this.renderableEffectsManager=new ur(this),this.searchEffectTransforms()},createContent:function(){},setBlendMode:function(){var e=this.globalData;if(e.blendMode!==this.data.bm){e.blendMode=this.data.bm;var t=nn(this.data.bm);e.canvasContext.globalCompositeOperation=t}},createRenderableComponents:function(){this.maskManager=new dr(this.data,this),this.transformEffects=this.renderableEffectsManager.getEffects(yn.TRANSFORM_EFFECT)},hideElement:function(){!this.hidden&&(!this.isInRange||this.isTransparent)&&(this.hidden=!0)},showElement:function(){this.isInRange&&!this.isTransparent&&(this.hidden=!1,this._isFirstFrame=!0,this.maskManager._isFirstFrame=!0)},clearCanvas:function(e){e.clearRect(this.transformCanvas.tx,this.transformCanvas.ty,this.transformCanvas.w*this.transformCanvas.sx,this.transformCanvas.h*this.transformCanvas.sy)},prepareLayer:function(){if(this.data.tt>=1){var e=this.buffers[0].getContext(`2d`);this.clearCanvas(e),e.drawImage(this.canvasContext.canvas,0,0),this.currentTransform=this.canvasContext.getTransform(),this.canvasContext.setTransform(1,0,0,1,0,0),this.clearCanvas(this.canvasContext),this.canvasContext.setTransform(this.currentTransform)}},exitLayer:function(){if(this.data.tt>=1){var e=this.buffers[1],t=e.getContext(`2d`);if(this.clearCanvas(t),t.drawImage(this.canvasContext.canvas,0,0),this.canvasContext.setTransform(1,0,0,1,0,0),this.clearCanvas(this.canvasContext),this.canvasContext.setTransform(this.currentTransform),this.comp.getElementById(`tp`in this.data?this.data.tp:this.data.ind-1).renderFrame(!0),this.canvasContext.setTransform(1,0,0,1,0,0),this.data.tt>=3&&!document._isProxy){var n=cr.getLumaCanvas(this.canvasContext.canvas);n.getContext(`2d`).drawImage(this.canvasContext.canvas,0,0),this.clearCanvas(this.canvasContext),this.canvasContext.drawImage(n,0,0)}this.canvasContext.globalCompositeOperation=pr[this.data.tt],this.canvasContext.drawImage(e,0,0),this.canvasContext.globalCompositeOperation=`destination-over`,this.canvasContext.drawImage(this.buffers[0],0,0),this.canvasContext.setTransform(this.currentTransform),this.canvasContext.globalCompositeOperation=`source-over`}},renderFrame:function(e){if(!(this.hidden||this.data.hd)&&(this.data.td!==1||e)){this.renderTransform(),this.renderRenderable(),this.renderLocalTransform(),this.setBlendMode();var t=this.data.ty===0;this.prepareLayer(),this.globalData.renderer.save(t),this.globalData.renderer.ctxTransform(this.finalTransform.localMat.props),this.globalData.renderer.ctxOpacity(this.finalTransform.localOpacity),this.renderInnerContent(),this.globalData.renderer.restore(t),this.exitLayer(),this.maskManager.hasMasks&&this.globalData.renderer.restore(!0),this._isFirstFrame&&(this._isFirstFrame=!1)}},destroy:function(){this.canvasContext=null,this.data=null,this.globalData=null,this.maskManager.destroy()},mHelper:new Ye},fr.prototype.hide=fr.prototype.hideElement,fr.prototype.show=fr.prototype.showElement;function mr(e,t,n,r){this.styledShapes=[],this.tr=[0,0,0,0,0,0];var i=4;t.ty===`rc`?i=5:t.ty===`el`?i=6:t.ty===`sr`&&(i=7),this.sh=Je.getShapeProp(e,t,i,e);var a,o=n.length,s;for(a=0;a<o;a+=1)n[a].closed||(s={transforms:r.addTransformSequence(n[a].transforms),trNodes:[]},this.styledShapes.push(s),n[a].elements.push(s))}mr.prototype.setAsAnimated=Pn.prototype.setAsAnimated;function Q(e,t,n){this.shapes=[],this.shapesData=e.shapes,this.stylesList=[],this.itemsData=[],this.prevViewData=[],this.shapeModifiers=[],this.processedElements=[],this.transformsManager=new ar,this.initElement(e,t,n)}u([mn,bn,fr,Mn,On,hn,tn],Q),Q.prototype.initElement=kn.prototype.initElement,Q.prototype.transformHelper={opacity:1,_opMdf:!1},Q.prototype.dashResetter=[],Q.prototype.createContent=function(){this.searchShapes(this.shapesData,this.itemsData,this.prevViewData,!0,[])},Q.prototype.createStyleElement=function(e,t){var n={data:e,type:e.ty,preTransforms:this.transformsManager.addTransformSequence(t),transforms:[],elements:[],closed:e.hd===!0},r={};return e.ty===`fl`||e.ty===`st`?(r.c=H.getProp(this,e.c,1,255,this),r.c.k||(n.co=`rgb(`+C(r.c.v[0])+`,`+C(r.c.v[1])+`,`+C(r.c.v[2])+`)`)):(e.ty===`gf`||e.ty===`gs`)&&(r.s=H.getProp(this,e.s,1,null,this),r.e=H.getProp(this,e.e,1,null,this),r.h=H.getProp(this,e.h||{k:0},0,.01,this),r.a=H.getProp(this,e.a||{k:0},0,D,this),r.g=new Bn(this,e.g,this)),r.o=H.getProp(this,e.o,0,.01,this),e.ty===`st`||e.ty===`gs`?(n.lc=J[e.lc||2],n.lj=Nn[e.lj||2],e.lj==1&&(n.ml=e.ml),r.w=H.getProp(this,e.w,0,null,this),r.w.k||(n.wi=r.w.v),e.d&&(r.d=new In(this,e.d,`canvas`,this),r.d.k||(n.da=r.d.dashArray,n.do=r.d.dashoffset[0]))):n.r=e.r===2?`evenodd`:`nonzero`,this.stylesList.push(n),r.style=n,r},Q.prototype.createGroupElement=function(){return{it:[],prevViewData:[]}},Q.prototype.createTransformElement=function(e){return{transform:{opacity:1,_opMdf:!1,key:this.transformsManager.getNewKey(),op:H.getProp(this,e.o,0,.01,this),mProps:_t.getTransformProperty(this,e,this)}}},Q.prototype.createShapeElement=function(e){var t=new mr(this,e,this.stylesList,this.transformsManager);return this.shapes.push(t),this.addShapeToModifiers(t),t},Q.prototype.reloadShapes=function(){this._isFirstFrame=!0;var e,t=this.itemsData.length;for(e=0;e<t;e+=1)this.prevViewData[e]=this.itemsData[e];for(this.searchShapes(this.shapesData,this.itemsData,this.prevViewData,!0,[]),t=this.dynamicProperties.length,e=0;e<t;e+=1)this.dynamicProperties[e].getValue();this.renderModifiers(),this.transformsManager.processSequences(this._isFirstFrame)},Q.prototype.addTransformToStyleList=function(e){var t,n=this.stylesList.length;for(t=0;t<n;t+=1)this.stylesList[t].closed||this.stylesList[t].transforms.push(e)},Q.prototype.removeTransformFromStyleList=function(){var e,t=this.stylesList.length;for(e=0;e<t;e+=1)this.stylesList[e].closed||this.stylesList[e].transforms.pop()},Q.prototype.closeStyles=function(e){var t,n=e.length;for(t=0;t<n;t+=1)e[t].closed=!0},Q.prototype.searchShapes=function(e,t,n,r,i){var a,o=e.length-1,s,c,l=[],u=[],d,f,p,m=[].concat(i);for(a=o;a>=0;--a){if(d=this.searchProcessedElement(e[a]),d?t[a]=n[d-1]:e[a]._shouldRender=r,e[a].ty===`fl`||e[a].ty===`st`||e[a].ty===`gf`||e[a].ty===`gs`)d?t[a].style.closed=!1:t[a]=this.createStyleElement(e[a],m),l.push(t[a].style);else if(e[a].ty===`gr`){if(!d)t[a]=this.createGroupElement(e[a]);else for(c=t[a].it.length,s=0;s<c;s+=1)t[a].prevViewData[s]=t[a].it[s];this.searchShapes(e[a].it,t[a].it,t[a].prevViewData,r,m)}else e[a].ty===`tr`?(d||(p=this.createTransformElement(e[a]),t[a]=p),m.push(t[a]),this.addTransformToStyleList(t[a])):e[a].ty===`sh`||e[a].ty===`rc`||e[a].ty===`el`||e[a].ty===`sr`?d||(t[a]=this.createShapeElement(e[a])):e[a].ty===`tm`||e[a].ty===`rd`||e[a].ty===`pb`||e[a].ty===`zz`||e[a].ty===`op`?(d?(f=t[a],f.closed=!1):(f=pt.getModifier(e[a].ty),f.init(this,e[a]),t[a]=f,this.shapeModifiers.push(f)),u.push(f)):e[a].ty===`rp`&&(d?(f=t[a],f.closed=!0):(f=pt.getModifier(e[a].ty),t[a]=f,f.init(this,e,a,t),this.shapeModifiers.push(f),r=!1),u.push(f));this.addProcessedElement(e[a],a+1)}for(this.removeTransformFromStyleList(),this.closeStyles(l),o=u.length,a=0;a<o;a+=1)u[a].closed=!0},Q.prototype.renderInnerContent=function(){this.transformHelper.opacity=1,this.transformHelper._opMdf=!1,this.renderModifiers(),this.transformsManager.processSequences(this._isFirstFrame),this.renderShape(this.transformHelper,this.shapesData,this.itemsData,!0)},Q.prototype.renderShapeTransform=function(e,t){(e._opMdf||t.op._mdf||this._isFirstFrame)&&(t.opacity=e.opacity,t.opacity*=t.op.v,t._opMdf=!0)},Q.prototype.drawLayer=function(){var e,t=this.stylesList.length,n,r,i,a,o,s,c=this.globalData.renderer,l=this.globalData.canvasContext,u,d;for(e=0;e<t;e+=1)if(d=this.stylesList[e],u=d.type,(u!==`st`&&u!==`gs`||d.wi!==0)&&d.data._shouldRender&&d.coOp!==0&&this.globalData.currentGlobalAlpha!==0){for(c.save(),o=d.elements,u===`st`||u===`gs`?(c.ctxStrokeStyle(u===`st`?d.co:d.grd),c.ctxLineWidth(d.wi),c.ctxLineCap(d.lc),c.ctxLineJoin(d.lj),c.ctxMiterLimit(d.ml||0)):c.ctxFillStyle(u===`fl`?d.co:d.grd),c.ctxOpacity(d.coOp),u!==`st`&&u!==`gs`&&l.beginPath(),c.ctxTransform(d.preTransforms.finalTransform.props),r=o.length,n=0;n<r;n+=1){for((u===`st`||u===`gs`)&&(l.beginPath(),d.da&&(l.setLineDash(d.da),l.lineDashOffset=d.do)),s=o[n].trNodes,a=s.length,i=0;i<a;i+=1)s[i].t===`m`?l.moveTo(s[i].p[0],s[i].p[1]):s[i].t===`c`?l.bezierCurveTo(s[i].pts[0],s[i].pts[1],s[i].pts[2],s[i].pts[3],s[i].pts[4],s[i].pts[5]):l.closePath();(u===`st`||u===`gs`)&&(c.ctxStroke(),d.da&&l.setLineDash(this.dashResetter))}u!==`st`&&u!==`gs`&&this.globalData.renderer.ctxFill(d.r),c.restore()}},Q.prototype.renderShape=function(e,t,n,r){var i,a=t.length-1,o=e;for(i=a;i>=0;--i)t[i].ty===`tr`?(o=n[i].transform,this.renderShapeTransform(e,o)):t[i].ty===`sh`||t[i].ty===`el`||t[i].ty===`rc`||t[i].ty===`sr`?this.renderPath(t[i],n[i]):t[i].ty===`fl`?this.renderFill(t[i],n[i],o):t[i].ty===`st`?this.renderStroke(t[i],n[i],o):t[i].ty===`gf`||t[i].ty===`gs`?this.renderGradientFill(t[i],n[i],o):t[i].ty===`gr`?this.renderShape(o,t[i].it,n[i].it):t[i].ty;r&&this.drawLayer()},Q.prototype.renderStyledShape=function(e,t){if(this._isFirstFrame||t._mdf||e.transforms._mdf){var n=e.trNodes,r=t.paths,i,a,o,s=r._length;n.length=0;var c=e.transforms.finalTransform;for(o=0;o<s;o+=1){var l=r.shapes[o];if(l&&l.v){for(a=l._length,i=1;i<a;i+=1)i===1&&n.push({t:`m`,p:c.applyToPointArray(l.v[0][0],l.v[0][1],0)}),n.push({t:`c`,pts:c.applyToTriplePoints(l.o[i-1],l.i[i],l.v[i])});a===1&&n.push({t:`m`,p:c.applyToPointArray(l.v[0][0],l.v[0][1],0)}),l.c&&a&&(n.push({t:`c`,pts:c.applyToTriplePoints(l.o[i-1],l.i[0],l.v[0])}),n.push({t:`z`}))}}e.trNodes=n}},Q.prototype.renderPath=function(e,t){if(e.hd!==!0&&e._shouldRender){var n,r=t.styledShapes.length;for(n=0;n<r;n+=1)this.renderStyledShape(t.styledShapes[n],t.sh)}},Q.prototype.renderFill=function(e,t,n){var r=t.style;(t.c._mdf||this._isFirstFrame)&&(r.co=`rgb(`+C(t.c.v[0])+`,`+C(t.c.v[1])+`,`+C(t.c.v[2])+`)`),(t.o._mdf||n._opMdf||this._isFirstFrame)&&(r.coOp=t.o.v*n.opacity)},Q.prototype.renderGradientFill=function(e,t,n){var r=t.style,i;if(!r.grd||t.g._mdf||t.s._mdf||t.e._mdf||e.t!==1&&(t.h._mdf||t.a._mdf)){var a=this.globalData.canvasContext,o=t.s.v,s=t.e.v;if(e.t===1)i=a.createLinearGradient(o[0],o[1],s[0],s[1]);else{var c=Math.sqrt((o[0]-s[0])**2+(o[1]-s[1])**2),l=Math.atan2(s[1]-o[1],s[0]-o[0]),u=t.h.v;u>=1?u=.99:u<=-1&&(u=-.99);var d=c*u,f=Math.cos(l+t.a.v)*d+o[0],p=Math.sin(l+t.a.v)*d+o[1];i=a.createRadialGradient(f,p,0,o[0],o[1],c)}var m,h=e.g.p,g=t.g.c,_=1;for(m=0;m<h;m+=1)t.g._hasOpacity&&t.g._collapsable&&(_=t.g.o[m*2+1]),i.addColorStop(g[m*4]/100,`rgba(`+g[m*4+1]+`,`+g[m*4+2]+`,`+g[m*4+3]+`,`+_+`)`);r.grd=i}r.coOp=t.o.v*n.opacity},Q.prototype.renderStroke=function(e,t,n){var r=t.style,i=t.d;i&&(i._mdf||this._isFirstFrame)&&(r.da=i.dashArray,r.do=i.dashoffset[0]),(t.c._mdf||this._isFirstFrame)&&(r.co=`rgb(`+C(t.c.v[0])+`,`+C(t.c.v[1])+`,`+C(t.c.v[2])+`)`),(t.o._mdf||n._opMdf||this._isFirstFrame)&&(r.coOp=t.o.v*n.opacity),(t.w._mdf||this._isFirstFrame)&&(r.wi=t.w.v)},Q.prototype.destroy=function(){this.shapesData=null,this.globalData=null,this.canvasContext=null,this.stylesList.length=0,this.itemsData.length=0};function hr(e,t,n){this.textSpans=[],this.yOffset=0,this.fillColorAnim=!1,this.strokeColorAnim=!1,this.strokeWidthAnim=!1,this.stroke=!1,this.fill=!1,this.justifyOffset=0,this.currentRender=null,this.renderType=`canvas`,this.values={fill:`rgba(0,0,0,0)`,stroke:`rgba(0,0,0,0)`,sWidth:0,fValue:``},this.initElement(e,t,n)}u([mn,bn,fr,On,hn,tn,X],hr),hr.prototype.tHelper=l(`canvas`).getContext(`2d`),hr.prototype.buildNewText=function(){var e=this.textProperty.currentData;this.renderedLetters=m(e.l?e.l.length:0);var t=!1;e.fc?(t=!0,this.values.fill=this.buildColor(e.fc)):this.values.fill=`rgba(0,0,0,0)`,this.fill=t;var n=!1;e.sc&&(n=!0,this.values.stroke=this.buildColor(e.sc),this.values.sWidth=e.sw);var r=this.globalData.fontManager.getFontByName(e.f),i,a,o=e.l,s=this.mHelper;this.stroke=n,this.values.fValue=e.finalSize+`px `+this.globalData.fontManager.getFontByName(e.f).fFamily,a=e.finalText.length;var c,l,u,d,f,p,h,g,_,v,y=this.data.singleShape,b=e.tr*.001*e.finalSize,x=0,S=0,C=!0,w=0;for(i=0;i<a;i+=1){c=this.globalData.fontManager.getCharData(e.finalText[i],r.fStyle,this.globalData.fontManager.getFontByName(e.f).fFamily),l=c&&c.data||{},s.reset(),y&&o[i].n&&(x=-b,S+=e.yOffset,S+=+!!C,C=!1),f=l.shapes?l.shapes[0].it:[],h=f.length,s.scale(e.finalSize/100,e.finalSize/100),y&&this.applyTextPropertiesToMatrix(e,s,o[i].line,x,S),_=m(h-1);var T=0;for(p=0;p<h;p+=1)if(f[p].ty===`sh`){for(d=f[p].ks.k.i.length,g=f[p].ks.k,v=[],u=1;u<d;u+=1)u===1&&v.push(s.applyToX(g.v[0][0],g.v[0][1],0),s.applyToY(g.v[0][0],g.v[0][1],0)),v.push(s.applyToX(g.o[u-1][0],g.o[u-1][1],0),s.applyToY(g.o[u-1][0],g.o[u-1][1],0),s.applyToX(g.i[u][0],g.i[u][1],0),s.applyToY(g.i[u][0],g.i[u][1],0),s.applyToX(g.v[u][0],g.v[u][1],0),s.applyToY(g.v[u][0],g.v[u][1],0));v.push(s.applyToX(g.o[u-1][0],g.o[u-1][1],0),s.applyToY(g.o[u-1][0],g.o[u-1][1],0),s.applyToX(g.i[0][0],g.i[0][1],0),s.applyToY(g.i[0][0],g.i[0][1],0),s.applyToX(g.v[0][0],g.v[0][1],0),s.applyToY(g.v[0][0],g.v[0][1],0)),_[T]=v,T+=1}y&&(x+=o[i].l,x+=b),this.textSpans[w]?this.textSpans[w].elem=_:this.textSpans[w]={elem:_},w+=1}},hr.prototype.renderInnerContent=function(){this.validateText();var e=this.canvasContext;e.font=this.values.fValue,this.globalData.renderer.ctxLineCap(`butt`),this.globalData.renderer.ctxLineJoin(`miter`),this.globalData.renderer.ctxMiterLimit(4),this.data.singleShape||this.textAnimator.getMeasures(this.textProperty.currentData,this.lettersChangedFlag);var t,n,r,i,a,o,s=this.textAnimator.renderedLetters,c=this.textProperty.currentData.l;n=c.length;var l,u=null,d=null,f=null,p,m,h=this.globalData.renderer;for(t=0;t<n;t+=1)if(!c[t].n){if(l=s[t],l&&(h.save(),h.ctxTransform(l.p),h.ctxOpacity(l.o)),this.fill){for(l&&l.fc?u!==l.fc&&(h.ctxFillStyle(l.fc),u=l.fc):u!==this.values.fill&&(u=this.values.fill,h.ctxFillStyle(this.values.fill)),p=this.textSpans[t].elem,i=p.length,this.globalData.canvasContext.beginPath(),r=0;r<i;r+=1)for(m=p[r],o=m.length,this.globalData.canvasContext.moveTo(m[0],m[1]),a=2;a<o;a+=6)this.globalData.canvasContext.bezierCurveTo(m[a],m[a+1],m[a+2],m[a+3],m[a+4],m[a+5]);this.globalData.canvasContext.closePath(),h.ctxFill()}if(this.stroke){for(l&&l.sw?f!==l.sw&&(f=l.sw,h.ctxLineWidth(l.sw)):f!==this.values.sWidth&&(f=this.values.sWidth,h.ctxLineWidth(this.values.sWidth)),l&&l.sc?d!==l.sc&&(d=l.sc,h.ctxStrokeStyle(l.sc)):d!==this.values.stroke&&(d=this.values.stroke,h.ctxStrokeStyle(this.values.stroke)),p=this.textSpans[t].elem,i=p.length,this.globalData.canvasContext.beginPath(),r=0;r<i;r+=1)for(m=p[r],o=m.length,this.globalData.canvasContext.moveTo(m[0],m[1]),a=2;a<o;a+=6)this.globalData.canvasContext.bezierCurveTo(m[a],m[a+1],m[a+2],m[a+3],m[a+4],m[a+5]);this.globalData.canvasContext.closePath(),h.ctxStroke()}l&&this.globalData.renderer.restore()}};function gr(e,t,n){this.assetData=t.getAssetData(e.refId),this.img=t.imageLoader.getAsset(this.assetData),this.initElement(e,t,n)}u([mn,bn,fr,On,hn,tn],gr),gr.prototype.initElement=Y.prototype.initElement,gr.prototype.prepareFrame=An.prototype.prepareFrame,gr.prototype.createContent=function(){if(this.img.width&&(this.assetData.w!==this.img.width||this.assetData.h!==this.img.height)){var e=l(`canvas`);e.width=this.assetData.w,e.height=this.assetData.h;var t=e.getContext(`2d`),n=this.img.width,r=this.img.height,i=n/r,a=this.assetData.w/this.assetData.h,o,s,c=this.assetData.pr||this.globalData.renderConfig.imagePreserveAspectRatio;i>a&&c===`xMidYMid slice`||i<a&&c!==`xMidYMid slice`?(s=r,o=s*a):(o=n,s=o/a),t.drawImage(this.img,(n-o)/2,(r-s)/2,o,s,0,0,this.assetData.w,this.assetData.h),this.img=e}},gr.prototype.renderInnerContent=function(){this.canvasContext.drawImage(this.img,0,0)},gr.prototype.destroy=function(){this.img=null};function _r(e,t,n){this.initElement(e,t,n)}u([mn,bn,fr,On,hn,tn],_r),_r.prototype.initElement=Y.prototype.initElement,_r.prototype.prepareFrame=An.prototype.prepareFrame,_r.prototype.renderInnerContent=function(){this.globalData.renderer.ctxFillStyle(this.data.sc),this.globalData.renderer.ctxFillRect(0,0,this.data.sw,this.data.sh)};function $(){}u([vn],$),$.prototype.createShape=function(e){return new Q(e,this.globalData,this)},$.prototype.createText=function(e){return new hr(e,this.globalData,this)},$.prototype.createImage=function(e){return new gr(e,this.globalData,this)},$.prototype.createSolid=function(e){return new _r(e,this.globalData,this)},$.prototype.createNull=ir.prototype.createNull,$.prototype.ctxTransform=function(e){(e[0]!==1||e[1]!==0||e[4]!==0||e[5]!==1||e[12]!==0||e[13]!==0)&&this.canvasContext.transform(e[0],e[1],e[4],e[5],e[12],e[13])},$.prototype.ctxOpacity=function(e){this.canvasContext.globalAlpha*=e<0?0:e},$.prototype.ctxFillStyle=function(e){this.canvasContext.fillStyle=e},$.prototype.ctxStrokeStyle=function(e){this.canvasContext.strokeStyle=e},$.prototype.ctxLineWidth=function(e){this.canvasContext.lineWidth=e},$.prototype.ctxLineCap=function(e){this.canvasContext.lineCap=e},$.prototype.ctxLineJoin=function(e){this.canvasContext.lineJoin=e},$.prototype.ctxMiterLimit=function(e){this.canvasContext.miterLimit=e},$.prototype.ctxFill=function(e){this.canvasContext.fill(e)},$.prototype.ctxFillRect=function(e,t,n,r){this.canvasContext.fillRect(e,t,n,r)},$.prototype.ctxStroke=function(){this.canvasContext.stroke()},$.prototype.reset=function(){if(!this.renderConfig.clearCanvas){this.canvasContext.restore();return}this.contextData.reset()},$.prototype.save=function(){this.canvasContext.save()},$.prototype.restore=function(e){if(!this.renderConfig.clearCanvas){this.canvasContext.restore();return}e&&(this.globalData.blendMode=`source-over`),this.contextData.restore(e)},$.prototype.configAnimation=function(e){if(this.animationItem.wrapper){this.animationItem.container=l(`canvas`);var t=this.animationItem.container.style;t.width=`100%`,t.height=`100%`;var n=`0px 0px 0px`;t.transformOrigin=n,t.mozTransformOrigin=n,t.webkitTransformOrigin=n,t[`-webkit-transform`]=n,t.contentVisibility=this.renderConfig.contentVisibility,this.animationItem.wrapper.appendChild(this.animationItem.container),this.canvasContext=this.animationItem.container.getContext(`2d`),this.renderConfig.className&&this.animationItem.container.setAttribute(`class`,this.renderConfig.className),this.renderConfig.id&&this.animationItem.container.setAttribute(`id`,this.renderConfig.id)}else this.canvasContext=this.renderConfig.context;this.contextData.setContext(this.canvasContext),this.data=e,this.layers=e.layers,this.transformCanvas={w:e.w,h:e.h,sx:0,sy:0,tx:0,ty:0},this.setupGlobalData(e,document.body),this.globalData.canvasContext=this.canvasContext,this.globalData.renderer=this,this.globalData.isDashed=!1,this.globalData.progressiveLoad=this.renderConfig.progressiveLoad,this.globalData.transformCanvas=this.transformCanvas,this.elements=m(e.layers.length),this.updateContainerSize()},$.prototype.updateContainerSize=function(e,t){this.reset();var n,r;e?(n=e,r=t,this.canvasContext.canvas.width=n,this.canvasContext.canvas.height=r):(this.animationItem.wrapper&&this.animationItem.container?(n=this.animationItem.wrapper.offsetWidth,r=this.animationItem.wrapper.offsetHeight):(n=this.canvasContext.canvas.width,r=this.canvasContext.canvas.height),this.canvasContext.canvas.width=n*this.renderConfig.dpr,this.canvasContext.canvas.height=r*this.renderConfig.dpr);var i,a;if(this.renderConfig.preserveAspectRatio.indexOf(`meet`)!==-1||this.renderConfig.preserveAspectRatio.indexOf(`slice`)!==-1){var o=this.renderConfig.preserveAspectRatio.split(` `),s=o[1]||`meet`,c=o[0]||`xMidYMid`,l=c.substr(0,4),u=c.substr(4);i=n/r,a=this.transformCanvas.w/this.transformCanvas.h,a>i&&s===`meet`||a<i&&s===`slice`?(this.transformCanvas.sx=n/(this.transformCanvas.w/this.renderConfig.dpr),this.transformCanvas.sy=n/(this.transformCanvas.w/this.renderConfig.dpr)):(this.transformCanvas.sx=r/(this.transformCanvas.h/this.renderConfig.dpr),this.transformCanvas.sy=r/(this.transformCanvas.h/this.renderConfig.dpr)),l===`xMid`&&(a<i&&s===`meet`||a>i&&s===`slice`)?this.transformCanvas.tx=(n-this.transformCanvas.w*(r/this.transformCanvas.h))/2*this.renderConfig.dpr:l===`xMax`&&(a<i&&s===`meet`||a>i&&s===`slice`)?this.transformCanvas.tx=(n-this.transformCanvas.w*(r/this.transformCanvas.h))*this.renderConfig.dpr:this.transformCanvas.tx=0,u===`YMid`&&(a>i&&s===`meet`||a<i&&s===`slice`)?this.transformCanvas.ty=(r-this.transformCanvas.h*(n/this.transformCanvas.w))/2*this.renderConfig.dpr:u===`YMax`&&(a>i&&s===`meet`||a<i&&s===`slice`)?this.transformCanvas.ty=(r-this.transformCanvas.h*(n/this.transformCanvas.w))*this.renderConfig.dpr:this.transformCanvas.ty=0}else this.renderConfig.preserveAspectRatio===`none`?(this.transformCanvas.sx=n/(this.transformCanvas.w/this.renderConfig.dpr),this.transformCanvas.sy=r/(this.transformCanvas.h/this.renderConfig.dpr),this.transformCanvas.tx=0,this.transformCanvas.ty=0):(this.transformCanvas.sx=this.renderConfig.dpr,this.transformCanvas.sy=this.renderConfig.dpr,this.transformCanvas.tx=0,this.transformCanvas.ty=0);this.transformCanvas.props=[this.transformCanvas.sx,0,0,0,0,this.transformCanvas.sy,0,0,0,0,1,0,this.transformCanvas.tx,this.transformCanvas.ty,0,1],this.ctxTransform(this.transformCanvas.props),this.canvasContext.beginPath(),this.canvasContext.rect(0,0,this.transformCanvas.w,this.transformCanvas.h),this.canvasContext.closePath(),this.canvasContext.clip(),this.renderFrame(this.renderedFrame,!0)},$.prototype.destroy=function(){this.renderConfig.clearCanvas&&this.animationItem.wrapper&&(this.animationItem.wrapper.innerText=``);for(var e=(this.layers?this.layers.length:0)-1;e>=0;--e)this.elements[e]&&this.elements[e].destroy&&this.elements[e].destroy();this.elements.length=0,this.globalData.canvasContext=null,this.animationItem.container=null,this.destroyed=!0},$.prototype.renderFrame=function(e,t){if(!(this.renderedFrame===e&&this.renderConfig.clearCanvas===!0&&!t||this.destroyed||e===-1)){this.renderedFrame=e,this.globalData.frameNum=e-this.animationItem._isFirstFrame,this.globalData.frameId+=1,this.globalData._mdf=!this.renderConfig.clearCanvas||t,this.globalData.projectInterface.currentFrame=e;var n,r=this.layers.length;for(this.completeLayers||this.checkLayers(e),n=r-1;n>=0;--n)(this.completeLayers||this.elements[n])&&this.elements[n].prepareFrame(e-this.layers[n].st);if(this.globalData._mdf){for(this.renderConfig.clearCanvas===!0?this.canvasContext.clearRect(0,0,this.transformCanvas.w,this.transformCanvas.h):this.save(),n=r-1;n>=0;--n)(this.completeLayers||this.elements[n])&&this.elements[n].renderFrame();this.renderConfig.clearCanvas!==!0&&this.restore()}}},$.prototype.buildItem=function(e){var t=this.elements;if(!(t[e]||this.layers[e].ty===99)){var n=this.createItem(this.layers[e],this,this.globalData);t[e]=n,n.initExpressions()}},$.prototype.checkPendingElements=function(){for(;this.pendingElements.length;)this.pendingElements.pop().checkParenting()},$.prototype.hide=function(){this.animationItem.container.style.display=`none`},$.prototype.show=function(){this.animationItem.container.style.display=`block`};function vr(){this.opacity=-1,this.transform=p(`float32`,16),this.fillStyle=``,this.strokeStyle=``,this.lineWidth=``,this.lineCap=``,this.lineJoin=``,this.miterLimit=``,this.id=Math.random()}function yr(){this.stack=[],this.cArrPos=0,this.cTr=new Ye;var e,t=15;for(e=0;e<t;e+=1){var n=new vr;this.stack[e]=n}this._length=t,this.nativeContext=null,this.transformMat=new Ye,this.currentOpacity=1,this.currentFillStyle=``,this.appliedFillStyle=``,this.currentStrokeStyle=``,this.appliedStrokeStyle=``,this.currentLineWidth=``,this.appliedLineWidth=``,this.currentLineCap=``,this.appliedLineCap=``,this.currentLineJoin=``,this.appliedLineJoin=``,this.appliedMiterLimit=``,this.currentMiterLimit=``}yr.prototype.duplicate=function(){var e=this._length*2,t=0;for(t=this._length;t<e;t+=1)this.stack[t]=new vr;this._length=e},yr.prototype.reset=function(){this.cArrPos=0,this.cTr.reset(),this.stack[this.cArrPos].opacity=1},yr.prototype.restore=function(e){--this.cArrPos;var t=this.stack[this.cArrPos],n=t.transform,r,i=this.cTr.props;for(r=0;r<16;r+=1)i[r]=n[r];if(e){this.nativeContext.restore();var a=this.stack[this.cArrPos+1];this.appliedFillStyle=a.fillStyle,this.appliedStrokeStyle=a.strokeStyle,this.appliedLineWidth=a.lineWidth,this.appliedLineCap=a.lineCap,this.appliedLineJoin=a.lineJoin,this.appliedMiterLimit=a.miterLimit}this.nativeContext.setTransform(n[0],n[1],n[4],n[5],n[12],n[13]),(e||t.opacity!==-1&&this.currentOpacity!==t.opacity)&&(this.nativeContext.globalAlpha=t.opacity,this.currentOpacity=t.opacity),this.currentFillStyle=t.fillStyle,this.currentStrokeStyle=t.strokeStyle,this.currentLineWidth=t.lineWidth,this.currentLineCap=t.lineCap,this.currentLineJoin=t.lineJoin,this.currentMiterLimit=t.miterLimit},yr.prototype.save=function(e){e&&this.nativeContext.save();var t=this.cTr.props;this._length<=this.cArrPos&&this.duplicate();for(var n=this.stack[this.cArrPos],r=0;r<16;r+=1)n.transform[r]=t[r];this.cArrPos+=1;var i=this.stack[this.cArrPos];i.opacity=n.opacity,i.fillStyle=n.fillStyle,i.strokeStyle=n.strokeStyle,i.lineWidth=n.lineWidth,i.lineCap=n.lineCap,i.lineJoin=n.lineJoin,i.miterLimit=n.miterLimit},yr.prototype.setOpacity=function(e){this.stack[this.cArrPos].opacity=e},yr.prototype.setContext=function(e){this.nativeContext=e},yr.prototype.fillStyle=function(e){this.stack[this.cArrPos].fillStyle!==e&&(this.currentFillStyle=e,this.stack[this.cArrPos].fillStyle=e)},yr.prototype.strokeStyle=function(e){this.stack[this.cArrPos].strokeStyle!==e&&(this.currentStrokeStyle=e,this.stack[this.cArrPos].strokeStyle=e)},yr.prototype.lineWidth=function(e){this.stack[this.cArrPos].lineWidth!==e&&(this.currentLineWidth=e,this.stack[this.cArrPos].lineWidth=e)},yr.prototype.lineCap=function(e){this.stack[this.cArrPos].lineCap!==e&&(this.currentLineCap=e,this.stack[this.cArrPos].lineCap=e)},yr.prototype.lineJoin=function(e){this.stack[this.cArrPos].lineJoin!==e&&(this.currentLineJoin=e,this.stack[this.cArrPos].lineJoin=e)},yr.prototype.miterLimit=function(e){this.stack[this.cArrPos].miterLimit!==e&&(this.currentMiterLimit=e,this.stack[this.cArrPos].miterLimit=e)},yr.prototype.transform=function(e){this.transformMat.cloneFromProps(e);var t=this.cTr;this.transformMat.multiply(t),t.cloneFromProps(this.transformMat.props);var n=t.props;this.nativeContext.setTransform(n[0],n[1],n[4],n[5],n[12],n[13])},yr.prototype.opacity=function(e){var t=this.stack[this.cArrPos].opacity;t*=e<0?0:e,this.stack[this.cArrPos].opacity!==t&&(this.currentOpacity!==e&&(this.nativeContext.globalAlpha=e,this.currentOpacity=e),this.stack[this.cArrPos].opacity=t)},yr.prototype.fill=function(e){this.appliedFillStyle!==this.currentFillStyle&&(this.appliedFillStyle=this.currentFillStyle,this.nativeContext.fillStyle=this.appliedFillStyle),this.nativeContext.fill(e)},yr.prototype.fillRect=function(e,t,n,r){this.appliedFillStyle!==this.currentFillStyle&&(this.appliedFillStyle=this.currentFillStyle,this.nativeContext.fillStyle=this.appliedFillStyle),this.nativeContext.fillRect(e,t,n,r)},yr.prototype.stroke=function(){this.appliedStrokeStyle!==this.currentStrokeStyle&&(this.appliedStrokeStyle=this.currentStrokeStyle,this.nativeContext.strokeStyle=this.appliedStrokeStyle),this.appliedLineWidth!==this.currentLineWidth&&(this.appliedLineWidth=this.currentLineWidth,this.nativeContext.lineWidth=this.appliedLineWidth),this.appliedLineCap!==this.currentLineCap&&(this.appliedLineCap=this.currentLineCap,this.nativeContext.lineCap=this.appliedLineCap),this.appliedLineJoin!==this.currentLineJoin&&(this.appliedLineJoin=this.currentLineJoin,this.nativeContext.lineJoin=this.appliedLineJoin),this.appliedMiterLimit!==this.currentMiterLimit&&(this.appliedMiterLimit=this.currentMiterLimit,this.nativeContext.miterLimit=this.appliedMiterLimit),this.nativeContext.stroke()};function br(e,t,n){this.completeLayers=!1,this.layers=e.layers,this.pendingElements=[],this.elements=m(this.layers.length),this.initElement(e,t,n),this.tm=e.tm?H.getProp(this,e.tm,0,t.frameRate,this):{_placeholder:!0}}u([$,nr,fr],br),br.prototype.renderInnerContent=function(){var e=this.canvasContext;e.beginPath(),e.moveTo(0,0),e.lineTo(this.data.w,0),e.lineTo(this.data.w,this.data.h),e.lineTo(0,this.data.h),e.lineTo(0,0),e.clip();for(var t=this.layers.length-1;t>=0;--t)(this.completeLayers||this.elements[t])&&this.elements[t].renderFrame()},br.prototype.destroy=function(){for(var e=this.layers.length-1;e>=0;--e)this.elements[e]&&this.elements[e].destroy();this.layers=null,this.elements=null},br.prototype.createComp=function(e){return new br(e,this.globalData,this)};function xr(e,t){this.animationItem=e,this.renderConfig={clearCanvas:t&&t.clearCanvas!==void 0?t.clearCanvas:!0,context:t&&t.context||null,progressiveLoad:t&&t.progressiveLoad||!1,preserveAspectRatio:t&&t.preserveAspectRatio||`xMidYMid meet`,imagePreserveAspectRatio:t&&t.imagePreserveAspectRatio||`xMidYMid slice`,contentVisibility:t&&t.contentVisibility||`visible`,className:t&&t.className||``,id:t&&t.id||``,runExpressions:!t||t.runExpressions===void 0||t.runExpressions},this.renderConfig.dpr=t&&t.dpr||1,this.animationItem.wrapper&&(this.renderConfig.dpr=t&&t.dpr||window.devicePixelRatio||1),this.renderedFrame=-1,this.globalData={frameNum:-1,_mdf:!1,renderConfig:this.renderConfig,currentGlobalAlpha:-1},this.contextData=new yr,this.elements=[],this.pendingElements=[],this.transformMat=new Ye,this.completeLayers=!1,this.rendererType=`canvas`,this.renderConfig.clearCanvas&&(this.ctxTransform=this.contextData.transform.bind(this.contextData),this.ctxOpacity=this.contextData.opacity.bind(this.contextData),this.ctxFillStyle=this.contextData.fillStyle.bind(this.contextData),this.ctxStrokeStyle=this.contextData.strokeStyle.bind(this.contextData),this.ctxLineWidth=this.contextData.lineWidth.bind(this.contextData),this.ctxLineCap=this.contextData.lineCap.bind(this.contextData),this.ctxLineJoin=this.contextData.lineJoin.bind(this.contextData),this.ctxMiterLimit=this.contextData.miterLimit.bind(this.contextData),this.ctxFill=this.contextData.fill.bind(this.contextData),this.ctxFillRect=this.contextData.fillRect.bind(this.contextData),this.ctxStroke=this.contextData.stroke.bind(this.contextData),this.save=this.contextData.save.bind(this.contextData))}return u([$],xr),xr.prototype.createComp=function(e){return new br(e,this.globalData,this)},ye(`canvas`,xr),pt.registerModifier(`tm`,ht),pt.registerModifier(`pb`,gt),pt.registerModifier(`rp`,vt),pt.registerModifier(`rd`,yt),pt.registerModifier(`zz`,Rt),pt.registerModifier(`op`,Xt),W}))}))(),1),Mt=class{constructor(e,t){this.animation=e,this.direction=1,this.listeners=[],this.lastFrame=-1,this.canvas=[t],typeof ResizeObserver<`u`&&(this.resizeObserver=new ResizeObserver(()=>{t.offsetWidth&&e.resize()}),this.resizeObserver.observe(t)),e.addEventListener(`enterFrame`,()=>{let t=Math.round(e.currentFrame);t!==this.lastFrame&&(this.lastFrame=t,this.listeners.forEach(e=>e(t)))})}addEventListener(e,t){this.listeners.push(t)}play(){this.animation.play()}pause(){this.animation.pause()}stop(e){this.animation.stop(),this.lastFrame=0}setDirection(e){this.direction=e,this.animation.setDirection(e)}setSpeed(e){this.animation.setSpeed(e)}get curFrame(){return Math.round(this.animation.currentFrame)}set curFrame(e){this.animation.goToAndStop(e,!0),this.lastFrame=e}remove(){this.resizeObserver?.disconnect(),this.listeners=[],this.animation.destroy(),this.canvas[0].remove()}},Nt=``,Pt=new Map,Ft={setAssetUrl(e){Nt=e},loadAnimationFromURLManually(e){let t=Pt.get(e);return t||(t=fetch(Nt+`assets/tweb/tgs/`+e+`.json`).then(e=>{if(!e.ok)throw Error(`HTTP `+e.status);return e.json()}),t.catch(()=>Pt.delete(e)),Pt.set(e,t)),t},async loadAnimationAsAsset(e,t){let n=await Ft.loadAnimationFromURLManually(t),r=document.createElement(`div`);return r.classList.add(`rlottie`),r.style.width=e.width+`px`,r.style.height=e.height+`px`,e.container.append(r),new Mt(jt.default.loadAnimation({container:r,renderer:`canvas`,loop:e.loop??!1,autoplay:e.autoplay??!1,animationData:structuredClone(n),rendererSettings:{clearCanvas:!0,dpr:window.devicePixelRatio||1,preserveAspectRatio:`xMidYMid meet`}}),r)},waitForFirstFrame(e){return e.animation.isLoaded?Promise.resolve():new Promise(t=>{e.animation.addEventListener(`DOMLoaded`,()=>t())})}},It={_:`help.countriesList`,countries:[{iso2:`AD`,default_name:`Andorra`,country_codes:[{country_code:`376`,patterns:[`XX XX XX`]}]},{iso2:`AE`,default_name:`United Arab Emirates`,country_codes:[{country_code:`971`,patterns:[`XX XXX XXXX`]}]},{iso2:`AF`,default_name:`Afghanistan`,country_codes:[{country_code:`93`,patterns:[`XXX XXX XXX`]}]},{iso2:`AG`,default_name:`Antigua & Barbuda`,country_codes:[{country_code:`1268`,patterns:[`XXX XXXX`]}]},{iso2:`AI`,default_name:`Anguilla`,country_codes:[{country_code:`1264`,patterns:[`XXX XXXX`]}]},{iso2:`AL`,default_name:`Albania`,country_codes:[{country_code:`355`,patterns:[`XX XXX XXXX`]}]},{iso2:`AM`,default_name:`Armenia`,country_codes:[{country_code:`374`,patterns:[`XX XXX XXX`]}]},{iso2:`AO`,default_name:`Angola`,country_codes:[{country_code:`244`,patterns:[`XXX XXX XXX`]}]},{iso2:`AR`,default_name:`Argentina`,country_codes:[{_:`help.countryCode`,flags:0,country_code:`54`}]},{iso2:`AS`,default_name:`American Samoa`,country_codes:[{country_code:`1684`,patterns:[`XXX XXXX`]}]},{iso2:`AT`,default_name:`Austria`,country_codes:[{country_code:`43`,patterns:[`XXX XXXXXX`]}]},{iso2:`AU`,default_name:`Australia`,country_codes:[{country_code:`61`,patterns:[`X XXXX XXXX`]}]},{iso2:`AW`,default_name:`Aruba`,country_codes:[{country_code:`297`,patterns:[`XXX XXXX`]}]},{iso2:`AZ`,default_name:`Azerbaijan`,country_codes:[{country_code:`994`,patterns:[`XX XXX XXXX`]}]},{iso2:`BA`,default_name:`Bosnia & Herzegovina`,country_codes:[{country_code:`387`,patterns:[`XX XXX XXX`]}]},{iso2:`BB`,default_name:`Barbados`,country_codes:[{country_code:`1246`,patterns:[`XXX XXXX`]}]},{iso2:`BD`,default_name:`Bangladesh`,country_codes:[{country_code:`880`,patterns:[`XX XXX XXX`]}]},{iso2:`BE`,default_name:`Belgium`,country_codes:[{country_code:`32`,patterns:[`XXX XX XX XX`]}]},{iso2:`BF`,default_name:`Burkina Faso`,country_codes:[{country_code:`226`,patterns:[`XX XX XX XX`]}]},{iso2:`BG`,default_name:`Bulgaria`,country_codes:[{_:`help.countryCode`,flags:0,country_code:`359`}]},{iso2:`BH`,default_name:`Bahrain`,country_codes:[{country_code:`973`,patterns:[`XXXX XXXX`]}]},{iso2:`BI`,default_name:`Burundi`,country_codes:[{country_code:`257`,patterns:[`XX XX XXXX`]}]},{iso2:`BJ`,default_name:`Benin`,country_codes:[{country_code:`229`,patterns:[`XX XXX XXX`]}]},{iso2:`BM`,default_name:`Bermuda`,country_codes:[{country_code:`1441`,patterns:[`XXX XXXX`]}]},{iso2:`BN`,default_name:`Brunei Darussalam`,country_codes:[{country_code:`673`,patterns:[`XXX XXXX`]}]},{iso2:`BO`,default_name:`Bolivia`,country_codes:[{country_code:`591`,patterns:[`X XXX XXXX`]}]},{iso2:`BQ`,default_name:`Bonaire, Sint Eustatius & Saba`,country_codes:[{_:`help.countryCode`,flags:0,country_code:`599`}]},{iso2:`BR`,default_name:`Brazil`,country_codes:[{country_code:`55`,patterns:[`XX XXXXX XXXX`]}]},{iso2:`BS`,default_name:`Bahamas`,country_codes:[{country_code:`1242`,patterns:[`XXX XXXX`]}]},{iso2:`BT`,default_name:`Bhutan`,country_codes:[{country_code:`975`,patterns:[`XX XXX XXX`]}]},{iso2:`BW`,default_name:`Botswana`,country_codes:[{country_code:`267`,patterns:[`XX XXX XXX`]}]},{iso2:`BY`,default_name:`Belarus`,country_codes:[{country_code:`375`,patterns:[`XX XXX XXXX`]}]},{iso2:`BZ`,default_name:`Belize`,country_codes:[{_:`help.countryCode`,flags:0,country_code:`501`}]},{iso2:`CA`,default_name:`Canada`,country_codes:[{_:`help.countryCode`,flags:3,country_code:`1`,prefixes:`403.587.780.825.236.250.604.672.778.204.431.506.709.902.782.226.249.289.343.365.416.437.519.548.613.647.705.807.905.418.438.450.514.579.581.819.873.306.639.867`.split(`.`),patterns:[`XXX XXX XXXX`]}]},{iso2:`CD`,default_name:`Congo (Dem. Rep.)`,country_codes:[{country_code:`243`,patterns:[`XX XXX XXXX`]}]},{iso2:`CF`,default_name:`Central African Rep.`,country_codes:[{country_code:`236`,patterns:[`XX XX XX XX`]}]},{iso2:`CG`,default_name:`Congo (Rep.)`,country_codes:[{country_code:`242`,patterns:[`XX XXX XXXX`]}]},{iso2:`CH`,default_name:`Switzerland`,country_codes:[{country_code:`41`,patterns:[`XX XXX XXXX`]}]},{iso2:`CI`,default_name:`CÃ´te d"Ivoire`,country_codes:[{country_code:`225`,patterns:[`XX XX XX XXXX`]}]},{iso2:`CK`,default_name:`Cook Islands`,country_codes:[{_:`help.countryCode`,flags:0,country_code:`682`}]},{iso2:`CL`,default_name:`Chile`,country_codes:[{country_code:`56`,patterns:[`X XXXX XXXX`]}]},{iso2:`CM`,default_name:`Cameroon`,country_codes:[{country_code:`237`,patterns:[`XXXX XXXX`]}]},{iso2:`CN`,default_name:`China`,country_codes:[{country_code:`86`,patterns:[`XXX XXXX XXXX`]}]},{iso2:`CO`,default_name:`Colombia`,country_codes:[{country_code:`57`,patterns:[`XXX XXX XXXX`]}]},{iso2:`CR`,default_name:`Costa Rica`,country_codes:[{country_code:`506`,patterns:[`XXXX XXXX`]}]},{iso2:`CU`,default_name:`Cuba`,country_codes:[{country_code:`53`,patterns:[`X XXX XXXX`]}]},{iso2:`CV`,default_name:`Cape Verde`,country_codes:[{country_code:`238`,patterns:[`XXX XXXX`]}]},{iso2:`CW`,default_name:`CuraÃ§ao`,country_codes:[{_:`help.countryCode`,country_code:`599`,prefixes:[`9`]}]},{iso2:`CY`,default_name:`Cyprus`,country_codes:[{country_code:`357`,patterns:[`XXXX XXXX`]}]},{iso2:`CZ`,default_name:`Czech Republic`,country_codes:[{country_code:`420`,patterns:[`XXX XXX XXX`]}]},{iso2:`DE`,default_name:`Germany`,country_codes:[{_:`help.countryCode`,country_code:`49`,patterns:[`XXXX XXXXXXX`,`16X XXXXXXX`,`17X XXXXXXX`,`3601 4198XX`]}]},{iso2:`DJ`,default_name:`Djibouti`,country_codes:[{country_code:`253`,patterns:[`XX XX XX XX`]}]},{iso2:`DK`,default_name:`Denmark`,country_codes:[{country_code:`45`,patterns:[`XXXX XXXX`]}]},{iso2:`DM`,default_name:`Dominica`,country_codes:[{country_code:`1767`,patterns:[`XXX XXXX`]}]},{iso2:`DO`,default_name:`Dominican Rep.`,country_codes:[{country_code:`1809`,patterns:[`XXX XXXX`]},{country_code:`1829`,patterns:[`XXX XXXX`]},{country_code:`1849`,patterns:[`XXX XXXX`]}]},{iso2:`DZ`,default_name:`Algeria`,country_codes:[{country_code:`213`,patterns:[`XXX XX XX XX`]}]},{iso2:`EC`,default_name:`Ecuador`,country_codes:[{country_code:`593`,patterns:[`XX XXX XXXX`]}]},{iso2:`EE`,default_name:`Estonia`,country_codes:[{country_code:`372`,patterns:[`XXXX XXX`]}]},{iso2:`EG`,default_name:`Egypt`,country_codes:[{country_code:`20`,patterns:[`XX XXXX XXXX`]}]},{iso2:`ER`,default_name:`Eritrea`,country_codes:[{country_code:`291`,patterns:[`X XXX XXX`]}]},{iso2:`ES`,default_name:`Spain`,country_codes:[{country_code:`34`,patterns:[`XXX XXX XXX`]}]},{iso2:`ET`,default_name:`Ethiopia`,country_codes:[{country_code:`251`,patterns:[`XX XXX XXX`]}]},{iso2:`FI`,default_name:`Finland`,country_codes:[{_:`help.countryCode`,flags:0,country_code:`358`}]},{iso2:`FJ`,default_name:`Fiji`,country_codes:[{country_code:`679`,patterns:[`XXX XXXX`]}]},{iso2:`FK`,default_name:`Falkland Islands`,country_codes:[{_:`help.countryCode`,flags:0,country_code:`500`}]},{iso2:`FM`,default_name:`Micronesia`,country_codes:[{_:`help.countryCode`,flags:0,country_code:`691`}]},{iso2:`FO`,default_name:`Faroe Islands`,country_codes:[{country_code:`298`,patterns:[`XXX XXX`]}]},{iso2:`FR`,default_name:`France`,country_codes:[{country_code:`33`,patterns:[`X XX XX XX XX`]}]},{iso2:`FT`,default_name:`Anonymous Numbers`,country_codes:[{country_code:`888`,patterns:[`8 XXX`,`XXXX XXXX`]}]},{iso2:`GA`,default_name:`Gabon`,country_codes:[{country_code:`241`,patterns:[`X XX XX XX`]}]},{iso2:`GB`,default_name:`United Kingdom`,country_codes:[{country_code:`44`,patterns:[`XXXX XXXXXX`]}]},{iso2:`GD`,default_name:`Grenada`,country_codes:[{country_code:`1473`,patterns:[`XXX XXXX`]}]},{iso2:`GE`,default_name:`Georgia`,country_codes:[{country_code:`995`,patterns:[`XXX XXX XXX`]}]},{iso2:`GF`,default_name:`French Guiana`,country_codes:[{_:`help.countryCode`,flags:0,country_code:`594`}]},{iso2:`GH`,default_name:`Ghana`,country_codes:[{country_code:`233`,patterns:[`XX XXX XXXX`]}]},{iso2:`GI`,default_name:`Gibraltar`,country_codes:[{country_code:`350`,patterns:[`XXXX XXXX`]}]},{iso2:`GL`,default_name:`Greenland`,country_codes:[{country_code:`299`,patterns:[`XXX XXX`]}]},{iso2:`GM`,default_name:`Gambia`,country_codes:[{country_code:`220`,patterns:[`XXX XXXX`]}]},{iso2:`GN`,default_name:`Guinea`,country_codes:[{country_code:`224`,patterns:[`XXX XXX XXX`]}]},{iso2:`GP`,default_name:`Guadeloupe`,country_codes:[{country_code:`590`,patterns:[`XXX XX XX XX`]}]},{iso2:`GQ`,default_name:`Equatorial Guinea`,country_codes:[{country_code:`240`,patterns:[`XXX XXX XXX`]}]},{iso2:`GR`,default_name:`Greece`,country_codes:[{country_code:`30`,patterns:[`XXX XXX XXXX`]}]},{iso2:`GT`,default_name:`Guatemala`,country_codes:[{country_code:`502`,patterns:[`X XXX XXXX`]}]},{iso2:`GU`,default_name:`Guam`,country_codes:[{country_code:`1671`,patterns:[`XXX XXXX`]}]},{iso2:`GW`,default_name:`Guinea-Bissau`,country_codes:[{country_code:`245`,patterns:[`XXX XXXX`]}]},{iso2:`GY`,default_name:`Guyana`,country_codes:[{country_code:`592`,patterns:[`5926XXXXXX`]}]},{iso2:`HK`,default_name:`Hong Kong`,country_codes:[{country_code:`852`,patterns:[`X XXX XXXX`]}]},{iso2:`HN`,default_name:`Honduras`,country_codes:[{country_code:`504`,patterns:[`XXXX XXXX`]}]},{iso2:`HR`,default_name:`Croatia`,country_codes:[{country_code:`385`,patterns:[`XX XXX XXX`]}]},{iso2:`HT`,default_name:`Haiti`,country_codes:[{country_code:`509`,patterns:[`XXXX XXXX`]}]},{iso2:`HU`,default_name:`Hungary`,country_codes:[{country_code:`36`,patterns:[`XXX XXX XXX`]}]},{iso2:`ID`,default_name:`Indonesia`,country_codes:[{country_code:`62`,patterns:[`XXX XXXXXX`]}]},{iso2:`IE`,default_name:`Ireland`,country_codes:[{country_code:`353`,patterns:[`XX XXX XXXX`]}]},{iso2:`IL`,default_name:`Israel`,country_codes:[{country_code:`972`,patterns:[`XX XXX XXXX`]}]},{iso2:`IN`,default_name:`India`,country_codes:[{country_code:`91`,patterns:[`XXXXX XXXXX`]}]},{iso2:`IO`,default_name:`Diego Garcia`,country_codes:[{country_code:`246`,patterns:[`XXX XXXX`]}]},{iso2:`IQ`,default_name:`Iraq`,country_codes:[{country_code:`964`,patterns:[`XXX XXX XXXX`]}]},{iso2:`IR`,default_name:`Iran`,country_codes:[{country_code:`98`,patterns:[`XXX XXX XXXX`]}]},{iso2:`IS`,default_name:`Iceland`,country_codes:[{country_code:`354`,patterns:[`XXX XXXX`]}]},{iso2:`IT`,default_name:`Italy`,country_codes:[{country_code:`39`,patterns:[`XXX XXX XXXX`]}]},{iso2:`JM`,default_name:`Jamaica`,country_codes:[{country_code:`1876`,patterns:[`XXX XXXX`]}]},{iso2:`JO`,default_name:`Jordan`,country_codes:[{country_code:`962`,patterns:[`X XXXX XXXX`]}]},{iso2:`JP`,default_name:`Japan`,country_codes:[{country_code:`81`,patterns:[`XX XXXX XXXX`]}]},{iso2:`KE`,default_name:`Kenya`,country_codes:[{country_code:`254`,patterns:[`XXX XXX XXX`]}]},{iso2:`KG`,default_name:`Kyrgyzstan`,country_codes:[{country_code:`996`,patterns:[`XXX XXXXXX`]}]},{iso2:`KH`,default_name:`Cambodia`,country_codes:[{country_code:`855`,patterns:[`XX XXX XXX`]}]},{iso2:`KI`,default_name:`Kiribati`,country_codes:[{country_code:`686`,patterns:[`XXXX XXXX`]}]},{iso2:`KM`,default_name:`Comoros`,country_codes:[{country_code:`269`,patterns:[`XXX XXXX`]}]},{iso2:`KN`,default_name:`Saint Kitts & Nevis`,country_codes:[{country_code:`1869`,patterns:[`XXX XXXX`]}]},{iso2:`KP`,default_name:`North Korea`,country_codes:[{_:`help.countryCode`,flags:0,country_code:`850`}]},{iso2:`KR`,default_name:`South Korea`,country_codes:[{country_code:`82`,patterns:[`XX XXXX XXX`]}]},{iso2:`KW`,default_name:`Kuwait`,country_codes:[{country_code:`965`,patterns:[`XXXX XXXX`]}]},{iso2:`KY`,default_name:`Cayman Islands`,country_codes:[{country_code:`1345`,patterns:[`XXX XXXX`]}]},{iso2:`KZ`,default_name:`Kazakhstan`,country_codes:[{_:`help.countryCode`,flags:3,country_code:`7`,prefixes:[`6`,`7`],patterns:[`XXX XXX XX XX`]}]},{iso2:`LA`,default_name:`Laos`,country_codes:[{country_code:`856`,patterns:[`XX XX XXX XXX`]}]},{iso2:`LB`,default_name:`Lebanon`,country_codes:[{_:`help.countryCode`,flags:0,country_code:`961`}]},{iso2:`LC`,default_name:`Saint Lucia`,country_codes:[{country_code:`1758`,patterns:[`XXX XXXX`]}]},{iso2:`LI`,default_name:`Liechtenstein`,country_codes:[{country_code:`423`,patterns:[`XXX XXXX`]}]},{iso2:`LK`,default_name:`Sri Lanka`,country_codes:[{country_code:`94`,patterns:[`XX XXX XXXX`]}]},{iso2:`LR`,default_name:`Liberia`,country_codes:[{country_code:`231`,patterns:[`XX XXX XXXX`]}]},{iso2:`LS`,default_name:`Lesotho`,country_codes:[{country_code:`266`,patterns:[`XX XXX XXX`]}]},{iso2:`LT`,default_name:`Lithuania`,country_codes:[{country_code:`370`,patterns:[`XXX XXXXX`]}]},{iso2:`LU`,default_name:`Luxembourg`,country_codes:[{country_code:`352`,patterns:[`XXX XXX XXX`]}]},{iso2:`LV`,default_name:`Latvia`,country_codes:[{country_code:`371`,patterns:[`XXX XXXXX`]}]},{iso2:`LY`,default_name:`Libya`,country_codes:[{country_code:`218`,patterns:[`XX XXX XXXX`]}]},{iso2:`MA`,default_name:`Morocco`,country_codes:[{country_code:`212`,patterns:[`XX XXX XXXX`]}]},{iso2:`MC`,default_name:`Monaco`,country_codes:[{country_code:`377`,patterns:[`XXXX XXXX`]}]},{iso2:`MD`,default_name:`Moldova`,country_codes:[{country_code:`373`,patterns:[`XX XXX XXX`]}]},{iso2:`ME`,default_name:`Montenegro`,country_codes:[{_:`help.countryCode`,flags:0,country_code:`382`}]},{iso2:`MG`,default_name:`Madagascar`,country_codes:[{country_code:`261`,patterns:[`XX XX XXX XX`]}]},{iso2:`MH`,default_name:`Marshall Islands`,country_codes:[{_:`help.countryCode`,flags:0,country_code:`692`}]},{iso2:`MK`,default_name:`North Macedonia`,country_codes:[{country_code:`389`,patterns:[`XX XXXXXX`]}]},{iso2:`ML`,default_name:`Mali`,country_codes:[{country_code:`223`,patterns:[`XXXX XXXX`]}]},{iso2:`MM`,default_name:`Myanmar`,country_codes:[{_:`help.countryCode`,flags:0,country_code:`95`}]},{iso2:`MN`,default_name:`Mongolia`,country_codes:[{country_code:`976`,patterns:[`XX XX XXXX`]}]},{iso2:`MO`,default_name:`Macau`,country_codes:[{country_code:`853`,patterns:[`XXXX XXXX`]}]},{iso2:`MP`,default_name:`Northern Mariana Islands`,country_codes:[{country_code:`1670`,patterns:[`XXX XXXX`]}]},{iso2:`MQ`,default_name:`Martinique`,country_codes:[{_:`help.countryCode`,flags:0,country_code:`596`}]},{iso2:`MR`,default_name:`Mauritania`,country_codes:[{country_code:`222`,patterns:[`XXXX XXXX`]}]},{iso2:`MS`,default_name:`Montserrat`,country_codes:[{country_code:`1664`,patterns:[`XXX XXXX`]}]},{iso2:`MT`,default_name:`Malta`,country_codes:[{country_code:`356`,patterns:[`XX XX XX XX`]}]},{iso2:`MU`,default_name:`Mauritius`,country_codes:[{country_code:`230`,patterns:[`XXXX XXXX`]}]},{iso2:`MV`,default_name:`Maldives`,country_codes:[{country_code:`960`,patterns:[`XXX XXXX`]}]},{iso2:`MW`,default_name:`Malawi`,country_codes:[{country_code:`265`,patterns:[`XX XXX XXXX`]}]},{iso2:`MX`,default_name:`Mexico`,country_codes:[{_:`help.countryCode`,flags:0,country_code:`52`}]},{iso2:`MY`,default_name:`Malaysia`,country_codes:[{_:`help.countryCode`,flags:0,country_code:`60`}]},{iso2:`MZ`,default_name:`Mozambique`,country_codes:[{country_code:`258`,patterns:[`XX XXX XXXX`]}]},{iso2:`NA`,default_name:`Namibia`,country_codes:[{country_code:`264`,patterns:[`XX XXX XXXX`]}]},{iso2:`NC`,default_name:`New Caledonia`,country_codes:[{_:`help.countryCode`,flags:0,country_code:`687`}]},{iso2:`NE`,default_name:`Niger`,country_codes:[{country_code:`227`,patterns:[`XX XX XX XX`]}]},{iso2:`NF`,default_name:`Norfolk Island`,country_codes:[{_:`help.countryCode`,flags:0,country_code:`672`}]},{iso2:`NG`,default_name:`Nigeria`,country_codes:[{country_code:`234`,patterns:[`XX XXXX XXXX`]}]},{iso2:`NI`,default_name:`Nicaragua`,country_codes:[{country_code:`505`,patterns:[`XXXX XXXX`]}]},{iso2:`NL`,default_name:`Netherlands`,country_codes:[{country_code:`31`,patterns:[`X XX XX XX XX`]}]},{iso2:`NO`,default_name:`Norway`,country_codes:[{country_code:`47`,patterns:[`XXX XX XXX`]}]},{iso2:`NP`,default_name:`Nepal`,country_codes:[{country_code:`977`,patterns:[`XX XXXX XXXX`]}]},{iso2:`NR`,default_name:`Nauru`,country_codes:[{_:`help.countryCode`,flags:0,country_code:`674`}]},{iso2:`NU`,default_name:`Niue`,country_codes:[{_:`help.countryCode`,flags:0,country_code:`683`}]},{iso2:`NZ`,default_name:`New Zealand`,country_codes:[{country_code:`64`,patterns:[`XXXX XXXX`]}]},{iso2:`OM`,default_name:`Oman`,country_codes:[{country_code:`968`,patterns:[`XXXX XXXX`]}]},{iso2:`PA`,default_name:`Panama`,country_codes:[{country_code:`507`,patterns:[`XXXX XXXX`]}]},{iso2:`PE`,default_name:`Peru`,country_codes:[{country_code:`51`,patterns:[`XXX XXX XXX`]}]},{iso2:`PF`,default_name:`French Polynesia`,country_codes:[{_:`help.countryCode`,flags:0,country_code:`689`}]},{iso2:`PG`,default_name:`Papua New Guinea`,country_codes:[{_:`help.countryCode`,flags:0,country_code:`675`}]},{iso2:`PH`,default_name:`Philippines`,country_codes:[{country_code:`63`,patterns:[`XXX XXX XXXX`]}]},{iso2:`PK`,default_name:`Pakistan`,country_codes:[{country_code:`92`,patterns:[`XXX XXX XXXX`]}]},{iso2:`PL`,default_name:`Poland`,country_codes:[{country_code:`48`,patterns:[`XXX XXX XXX`]}]},{iso2:`PM`,default_name:`Saint Pierre & Miquelon`,country_codes:[{_:`help.countryCode`,flags:0,country_code:`508`}]},{iso2:`PR`,default_name:`Puerto Rico`,country_codes:[{country_code:`1787`,patterns:[`XXX XXXX`]},{country_code:`1939`,patterns:[`XXX XXXX`]}]},{iso2:`PS`,default_name:`Palestine`,country_codes:[{country_code:`970`,patterns:[`XXX XX XXXX`]}]},{iso2:`PT`,default_name:`Portugal`,country_codes:[{country_code:`351`,patterns:[`XXX XXX XXX`]}]},{iso2:`PW`,default_name:`Palau`,country_codes:[{_:`help.countryCode`,flags:0,country_code:`680`}]},{iso2:`PY`,default_name:`Paraguay`,country_codes:[{country_code:`595`,patterns:[`XXX XXX XXX`]}]},{iso2:`QA`,default_name:`Qatar`,country_codes:[{country_code:`974`,patterns:[`XX XXX XXX`]}]},{iso2:`RE`,default_name:`RÃ©union`,country_codes:[{country_code:`262`,patterns:[`XXX XXX XXX`]}]},{iso2:`RO`,default_name:`Romania`,country_codes:[{country_code:`40`,patterns:[`XXX XXX XXX`]}]},{iso2:`RS`,default_name:`Serbia`,country_codes:[{country_code:`381`,patterns:[`XX XXX XXX`]}]},{iso2:`RU`,default_name:`Russian Federation`,country_codes:[{country_code:`7`,patterns:[`XXX XXX XXXX`]}]},{iso2:`RW`,default_name:`Rwanda`,country_codes:[{country_code:`250`,patterns:[`XXX XXX XXX`]}]},{iso2:`SA`,default_name:`Saudi Arabia`,country_codes:[{country_code:`966`,patterns:[`XX XXX XXXX`]}]},{iso2:`SB`,default_name:`Solomon Islands`,country_codes:[{_:`help.countryCode`,flags:0,country_code:`677`}]},{iso2:`SC`,default_name:`Seychelles`,country_codes:[{country_code:`248`,patterns:[`X XX XX XX`]}]},{iso2:`SD`,default_name:`Sudan`,country_codes:[{country_code:`249`,patterns:[`XX XXX XXXX`]}]},{iso2:`SE`,default_name:`Sweden`,country_codes:[{country_code:`46`,patterns:[`XX XXX XXXX`]}]},{iso2:`SG`,default_name:`Singapore`,country_codes:[{country_code:`65`,patterns:[`XXXX XXXX`]}]},{iso2:`SH`,default_name:`Saint Helena`,country_codes:[{_:`help.countryCode`,flags:0,country_code:`247`},{country_code:`290`,patterns:[`XX XXX`]}]},{iso2:`SI`,default_name:`Slovenia`,country_codes:[{country_code:`386`,patterns:[`XX XXX XXX`]}]},{iso2:`SK`,default_name:`Slovakia`,country_codes:[{country_code:`421`,patterns:[`XXX XXX XXX`]}]},{iso2:`SL`,default_name:`Sierra Leone`,country_codes:[{country_code:`232`,patterns:[`XX XXX XXX`]}]},{iso2:`SM`,default_name:`San Marino`,country_codes:[{_:`help.countryCode`,flags:0,country_code:`378`}]},{iso2:`SN`,default_name:`Senegal`,country_codes:[{country_code:`221`,patterns:[`XX XXX XXXX`]}]},{iso2:`SO`,default_name:`Somalia`,country_codes:[{country_code:`252`,patterns:[`XX XXX XXX`]}]},{iso2:`SR`,default_name:`Suriname`,country_codes:[{country_code:`597`,patterns:[`XXX XXXX`]}]},{iso2:`SS`,default_name:`South Sudan`,country_codes:[{country_code:`211`,patterns:[`XX XXX XXXX`]}]},{iso2:`ST`,default_name:`SÃ£o TomÃ© & PrÃ­ncipe`,country_codes:[{country_code:`239`,patterns:[`XX XXXXX`]}]},{iso2:`SV`,default_name:`El Salvador`,country_codes:[{country_code:`503`,patterns:[`XXXX XXXX`]}]},{iso2:`SX`,default_name:`Sint Maarten`,country_codes:[{country_code:`1721`,patterns:[`XXX XXXX`]}]},{iso2:`SY`,default_name:`Syria`,country_codes:[{country_code:`963`,patterns:[`XXX XXX XXX`]}]},{iso2:`SZ`,default_name:`Eswatini`,country_codes:[{country_code:`268`,patterns:[`XXXX XXXX`]}]},{iso2:`TC`,default_name:`Turks & Caicos Islands`,country_codes:[{country_code:`1649`,patterns:[`XXX XXXX`]}]},{iso2:`TD`,default_name:`Chad`,country_codes:[{country_code:`235`,patterns:[`XX XX XX XX`]}]},{iso2:`TG`,default_name:`Togo`,country_codes:[{country_code:`228`,patterns:[`XX XXX XXX`]}]},{iso2:`TH`,default_name:`Thailand`,country_codes:[{country_code:`66`,patterns:[`X XXXX XXXX`]}]},{iso2:`TJ`,default_name:`Tajikistan`,country_codes:[{country_code:`992`,patterns:[`XX XXX XXXX`]}]},{iso2:`TK`,default_name:`Tokelau`,country_codes:[{_:`help.countryCode`,flags:0,country_code:`690`}]},{iso2:`TL`,default_name:`Timor-Leste`,country_codes:[{_:`help.countryCode`,flags:0,country_code:`670`}]},{iso2:`TM`,default_name:`Turkmenistan`,country_codes:[{country_code:`993`,patterns:[`XX XXXXXX`]}]},{iso2:`TN`,default_name:`Tunisia`,country_codes:[{country_code:`216`,patterns:[`XX XXX XXX`]}]},{iso2:`TO`,default_name:`Tonga`,country_codes:[{_:`help.countryCode`,flags:0,country_code:`676`}]},{iso2:`TR`,default_name:`Turkey`,country_codes:[{country_code:`90`,patterns:[`XXX XXX XXXX`]}]},{iso2:`TT`,default_name:`Trinidad & Tobago`,country_codes:[{country_code:`1868`,patterns:[`XXX XXXX`]}]},{iso2:`TV`,default_name:`Tuvalu`,country_codes:[{_:`help.countryCode`,flags:0,country_code:`688`}]},{iso2:`TW`,default_name:`Taiwan`,country_codes:[{country_code:`886`,patterns:[`XXX XXX XXX`]}]},{iso2:`TZ`,default_name:`Tanzania`,country_codes:[{country_code:`255`,patterns:[`XX XXX XXXX`]}]},{iso2:`UA`,default_name:`Ukraine`,country_codes:[{country_code:`380`,patterns:[`XX XXX XX XX`]}]},{iso2:`UG`,default_name:`Uganda`,country_codes:[{country_code:`256`,patterns:[`XX XXX XXXX`]}]},{iso2:`US`,default_name:`USA`,country_codes:[{country_code:`1`,patterns:[`XXX XXX XXXX`]}]},{iso2:`UY`,default_name:`Uruguay`,country_codes:[{country_code:`598`,patterns:[`X XXX XXXX`]}]},{iso2:`UZ`,default_name:`Uzbekistan`,country_codes:[{country_code:`998`,patterns:[`XX XXX XX XX`]}]},{iso2:`VC`,default_name:`Saint Vincent & the Grenadines`,country_codes:[{country_code:`1784`,patterns:[`XXX XXXX`]}]},{iso2:`VE`,default_name:`Venezuela`,country_codes:[{country_code:`58`,patterns:[`XXX XXX XXXX`]}]},{iso2:`VG`,default_name:`British Virgin Islands`,country_codes:[{country_code:`1284`,patterns:[`XXX XXXX`]}]},{iso2:`VI`,default_name:`US Virgin Islands`,country_codes:[{country_code:`1340`,patterns:[`XXX XXXX`]}]},{iso2:`VN`,default_name:`Vietnam`,country_codes:[{_:`help.countryCode`,flags:0,country_code:`84`}]},{iso2:`VU`,default_name:`Vanuatu`,country_codes:[{_:`help.countryCode`,flags:0,country_code:`678`}]},{iso2:`WF`,default_name:`Wallis & Futuna`,country_codes:[{_:`help.countryCode`,flags:0,country_code:`681`}]},{iso2:`WS`,default_name:`Samoa`,country_codes:[{_:`help.countryCode`,flags:0,country_code:`685`}]},{pFlags:{hidden:!0},iso2:`XG`,default_name:`Global Mobile Satellite System`,country_codes:[{_:`help.countryCode`,flags:0,country_code:`881`}]},{iso2:`XK`,default_name:`Kosovo`,country_codes:[{country_code:`383`,patterns:[`XXXX XXXX`]}]},{pFlags:{hidden:!0},iso2:`XV`,default_name:`International Networks`,country_codes:[{_:`help.countryCode`,flags:0,country_code:`882`},{_:`help.countryCode`,flags:0,country_code:`883`}]},{iso2:`YE`,default_name:`Yemen`,country_codes:[{country_code:`967`,patterns:[`XXX XXX XXX`]}]},{pFlags:{hidden:!0},iso2:`YL`,default_name:`Y-land`,country_codes:[{_:`help.countryCode`,country_code:`42`,prefixes:[`4`,`7`]}]},{iso2:`ZA`,default_name:`South Africa`,country_codes:[{country_code:`27`,patterns:[`XX XXX XXXX`]}]},{iso2:`ZM`,default_name:`Zambia`,country_codes:[{country_code:`260`,patterns:[`XX XXX XXXX`]}]},{iso2:`ZW`,default_name:`Zimbabwe`,country_codes:[{country_code:`263`,patterns:[`XX XXX XXXX`]}]}],hash:1451434695},Lt={"Login.Title":`Sign in to MadelineProto`,"Login.PhoneLabelInvalid":`Phone Number Invalid`,"Login.StartText":`Please confirm your country code
and enter your phone number.`,"Login.Code.SentSms":`We have sent you an SMS
with the code.`,"Login.Code.SentInApp":`We have sent you a message in Telegram
with the code.`,"Login.Code.SentCall":`We will call you and dictate
the code.`,"Login.Code.SentEmail":`Please check your email **%s** (don't forget the spam folder) and enter the code we just sent you.`,"Login.Code.SentUnknown":`Please check everything
for a code (type: %s)`,"Login.Code.SentMissedCall":`Within the next few seconds you should receive
a short call from **%s**. Don't answer it â€”
just enter the last digits of that number.`,"Login.Code.SentSmsWord":`We've sent an SMS with a secret word
to your phone **%s**.`,"Login.Code.SentSmsWordBeginning":`We've sent an SMS with a secret word that starts
with "**%2$s**" to your phone **%1$s**.`,"Login.Code.SentSmsPhrase":`We've sent an SMS with a secret phrase
to your phone **%s**.`,"Login.Code.SentSmsPhraseBeginning":`We've sent an SMS with a secret phrase that starts
with "**%2$s**" to your phone **%1$s**.`,"Login.Code.SecretWord":`Secret word`,"Login.Code.SecretPhrase":`Secret phrase`,"Login.Code.WordInvalid":`Incorrect, please try again`,"Login.Code.WordBeginningInvalid":`Incorrect word`,"Login.Password.Title":`Enter Your Password`,"Login.Password.Subtitle":`Your account is protected with
an additional password`,"Login.Register.Subtitle":`Enter your name
to create your account`,PleaseWait:`Please wait...`,Code:`Code`,LoginPassword:`Password`,YourName:`Your Name`,FirstName:`First name (required)`,LastName:`Last name (optional)`,StartMessaging:`Start Messaging`,Country:`Country`,"Contacts.PhoneNumber.Placeholder":`Phone Number`,"Login.Next":`Next`,"Login.QR.Title":`Log in by QR Code`,"Login.QR.Help1":`Open Telegram on your phone`,"Login.QR.Help2":`Go to **Settings** > **Devices** > **Add Device**`,"Login.QR.Help3":`Point your phone at this screen to confirm login`,"Login.QR.Cancel":`Log in by phone number >`,"Login.QR.Login":`Log in by QR Code >`,"Login.QR.Subtitle":`Scan with Telegram app on your phone`,"PhoneNumber.Code.Fragment.Info":`Get the code in the Numbers section on [Fragment]()`,PHONE_CODE_INVALID:`Invalid code`,PHONE_CODE_EXPIRED:`Code expired`,PASSWORD_HASH_INVALID:`Incorrect password`,DarkMode:`Dark Mode`,"Error.AnError":`An error occurred. Please try again later.`,"AccDescr.LoginCode":`Login code`,Close:`Close`,"Login.Bot":`Log in as a bot >`,"Login.Bot.Title":`Log in as a Bot`,"Login.Bot.Subtitle":`Enter the token @BotFather gave you
when you created the bot.`,"Login.Bot.Token":`Bot Token`,"Login.Bot.Cancel":`Log in as a user >`,Optional:`optional`,PHONE_NUMBER_INVALID:`Phone Number Invalid`,PHONE_NUMBER_BANNED:`This phone number is banned`,PHONE_NUMBER_FLOOD:`Too many attempts, try again later`,PHONE_CODE_EMPTY:`Invalid code`,PHONE_PASSWORD_FLOOD:`Too many attempts, try again later`,ACCESS_TOKEN_INVALID:`Invalid bot token`,ACCESS_TOKEN_EXPIRED:`This bot token was revoked`,FIRSTNAME_INVALID:`Invalid first name`,LASTNAME_INVALID:`Invalid last name`},Rt={next:`M409.041 835.709c-13.83 13.83-36.253 13.83-50.083 0s-13.83-36.254 0-50.083l273.626-273.625-273.626-273.626c-13.829-13.828-13.83-36.254 0-50.082s36.254-13.828 50.083 0l298.666 298.667c6.643 6.639 10.377 15.65 10.377 25.041s-3.738 18.402-10.377 25.041l-298.666 298.668z`,previous:`M614.959 835.709c13.828 13.83 36.254 13.83 50.082 0s13.828-36.254 0-50.083l-273.624-273.625 273.624-273.626c13.828-13.828 13.828-36.254 0-50.082s-36.254-13.828-50.082 0l-298.667 298.667c-6.641 6.639-10.374 15.65-10.375 25.041s3.735 18.402 10.375 25.041l298.667 298.668z`,edit:`M682.624 882.042c47.155 47.146 123.597 47.146 170.752 0l50.001-50c47.142-47.153 47.142-123.597 0-170.75l-478.169-478.166c-30.641-30.643-72.209-47.872-115.542-47.876h-75c-43.122 0-78.083 34.961-78.083 78.084v74.999c0.005 43.332 17.233 84.902 47.875 115.541l478.166 478.167zM254.542 353.792c-17.359-17.361-27.12-40.909-27.125-65.459v-74.999c0-4.006 3.244-7.249 7.25-7.249h75c24.549 0.004 48.099 9.766 65.458 27.123l385.46 385.459-120.585 120.583-385.458-385.458zM803.29 831.958c-19.49 19.486-51.089 19.486-70.579 0l-42.628-42.625 120.585-120.583 42.624 42.625c19.486 19.494 19.486 51.090 0 70.583l-50.001 50z`,eye1_filled:`M511.996 810.667c242.338 0 390.929-172.71 445.542-250.458 15.936-22.69 16.512-51.981 1.626-75.375-52.71-82.842-199.91-271.501-447.168-271.501-247.256 0.009-394.463 188.668-447.166 271.501-14.879 23.39-14.307 52.685 1.625 75.375 54.603 77.739 203.202 250.448 445.541 250.458zM511.996 725.333c-117.811-0.013-213.333-95.518-213.333-213.333s95.522-213.321 213.333-213.333c117.82 0 213.333 95.514 213.333 213.333s-95.514 213.333-213.333 213.333zM518.579 639.834c67.635-3.426 121.417-59.349 121.417-127.834l-0.166-6.583c-3.426-67.631-59.349-121.417-127.834-121.417l-6.583 0.166c-65.442 3.328-117.935 55.804-121.25 121.25l-0.166 6.583c0 3.763 0.183 7.488 0.499 11.166 12.442-7.091 26.825-11.166 42.167-11.166 47.13 0 85.333 38.204 85.333 85.333 0 15.347-4.075 29.726-11.166 42.167 3.682 0.32 7.403 0.499 11.166 0.499l6.583-0.166z`,eye2_filled:`M138.557 838.763c14.323 16.37 38.393 19.16 55.959 7.288l4.247-3.274 682.667-597.333c17.734-15.517 19.531-42.472 4.014-60.206-14.323-16.37-38.393-19.16-55.959-7.288l-4.247 3.274-113.642 99.426c-58.284-27.606-125.631-45.983-199.436-45.983-248.531 0-426.826 211.597-426.826 277.333 0 36.906 56.598 119.786 149.999 185.387l-92.761 81.17c-17.734 15.517-19.531 42.472-4.014 60.206zM341.333 512c0-94.257 76.41-170.667 170.667-170.667 35.709 0 68.857 10.967 96.258 29.716l-88.514 77.414c-2.539-0.306-5.123-0.464-7.744-0.464-35.101 0-63.602 28.257-63.996 63.265l-88.501 77.445c-11.623-23.062-18.169-49.121-18.169-76.71zM512.159 789.333c249.485 0 426.508-211.597 426.508-277.333 0-27.128-29.986-79.097-82.81-130.785l-174.596 152.787c-10.435 81.068-77.75 144.323-160.633 148.45l-9.311 0.214-106.655 93.298c34.013 8.496 69.975 13.369 107.496 13.369z`,darkmode_filled:`M868.983 370.812c12.595 31.932-25.852 56.905-58.018 44.911-33.387-12.442-69.517-19.247-107.238-19.247-169.583 0-307.055 137.476-307.055 307.055 0 37.722 6.801 73.855 19.247 107.24 11.989 32.166-12.979 70.616-44.911 58.017-142.313-56.151-243.008-194.897-243.008-357.163 0-211.977 171.84-383.817 383.817-383.817 162.266 0 301.013 100.693 357.167 243.004z`,back:`M486.959 835.708c13.828 13.83 36.254 13.83 50.082 0s13.828-36.253 0-50.083l-238.208-238.208h511.833c19.558 0 35.418-15.859 35.418-35.418s-15.859-35.418-35.418-35.418h-511.833l238.208-238.208c13.828-13.828 13.828-36.254 0-50.082s-36.254-13.828-50.082 0l-298.667 298.667c-6.641 6.639-10.375 15.65-10.375 25.041s3.734 18.402 10.375 25.041l298.667 298.667z`,down:`M835.708 614.959c13.833 13.83 13.833 36.253 0 50.083-13.828 13.829-36.254 13.83-50.082 0l-273.626-273.626-273.625 273.626c-13.83 13.829-36.254 13.83-50.083 0s-13.828-36.254 0-50.083l298.667-298.666c6.639-6.643 15.65-10.377 25.041-10.377s18.402 3.738 25.041 10.377l298.667 298.666z`},zt=new Set([`next`,`previous`,`back`]);function Bt(e){let t=document.createElementNS(`http://www.w3.org/2000/svg`,`svg`);t.setAttribute(`viewBox`,`0 0 1024 1024`);let n=document.createElementNS(`http://www.w3.org/2000/svg`,`path`);return n.setAttribute(`d`,Rt[e]),n.setAttribute(`transform`,`matrix(1 0 0 -1 0 1024)`),t.append(n),t}function Vt(e,...t){let n=document.createElement(`span`);return n.classList.add(`tgico`,`tgico-`+e,...t.filter(Boolean)),zt.has(e)&&n.classList.add(`tgico-mirror`),n.append(Bt(e)),n.setAttribute(`aria-hidden`,`true`),n}var Ht;(function(e){let t=e.strings=new Map(Object.entries(Lt)),n=e.countriesList=It.countries;function r(e,n){for(let[n,r]of Object.entries(e))typeof r==`string`&&t.set(n,r);n&&n!==`en`&&i(n)}e.applyStrings=r;function i(e){let t;try{t=new Intl.DisplayNames([e],{type:`region`,fallback:`none`})}catch{return}for(let e of n)try{let n=t.of(e.iso2);n&&(e.name=n)}catch{}}function a(e,t,n,r){let i=t[r===void 0?n.i++:r];Array.isArray(i)?e.push(...i):e.push(i)}let o={">":`next`,"<":`previous`},s=Object.keys(o);function c(e,t,n){if(!n){n={i:0};let t=e.match(/(%|un)\d+/g);t?.length&&(n.i=Math.max(...t.map(e=>+e.replace(/\D/g,``))))}let r=[],i=RegExp(`(\\*\\*|__)(.+?)\\1|(\\n)|(\\[.+?\\]\\(.*?\\))|(?:^|\\s)(${s.join(`|`)})(?:$|\\s)|un\\d|%\\d\\$.|%\\S`,`g`),l=0;return e.replace(i,(e,i,s,u,d,f,p,m)=>{if(p>l&&r.push(m.slice(l,p)),i){let e=document.createElement(i===`**`?`b`:`i`);e.append(...c(s,t,n)),r.push(e)}else if(u)r.push(document.createElement(`br`));else if(d){let e=d.lastIndexOf(`]`),i=d.slice(1,e),a=d.slice(e+2,d.length-1),o;a&&/^https?:\/\//.test(a)?(o=document.createElement(`a`),o.href=a,o.target=`_blank`,o.rel=`noopener noreferrer`):(o=t?.[n.i++],typeof o==`object`?o&&(o.textContent=``):o=void 0);let s=c(i,t,n);o?(o.append(...s),r.push(o)):r.push(...s)}else if(f){let t=`><`.includes(f);!t&&!e.startsWith(f)&&r.push(e[0]),r.push(Vt(o[f],`inline-icon`)),!t&&e.startsWith(f)&&r.push(e[e.length-1])}else if(t){let i=e.replace(/\D/g,``);a(r,t,n,!i||Number.isNaN(+i)?void 0:Math.min(t.length-1,i-1))}return l=p+e.length,``}),l!==e.length&&r.push(e.slice(l)),r}e.superFormatter=c;function l(e,n,r){let i=c(t.get(e)??e,r);return n?i.map(e=>e instanceof HTMLBRElement?`
`:e instanceof Node?e.textContent:e).join(``):i}e.format=l;function u(e,t){let n=document.createElement(`span`);return n.classList.add(`i18n`),n.append(...l(e,!1,t)),n}e.i18n=u;function d(e,t,n){return e.replaceChildren(...l(t,!1,n)),e}e._i18n=d;function f(e){return t.has(e)}e.has=f})(Ht||(Ht={}));var Ut=Ht,q=Ht.i18n;Ht._i18n;var Wt=`madeline-webui-notices-dismissed`;function Gt(){try{return window.sessionStorage.getItem(Wt)===`1`}catch{return!1}}var[Kt,qt]=k(()=>A(Gt()));function Jt(){qt(!0);try{window.sessionStorage.setItem(Wt,`1`)}catch{}}var Yt=!1;function Xt(){return Yt}function Zt(){return window.location.href.split(`#`)[0]}async function Qt(e){Yt=!0;try{let t=await fetch(Zt(),{method:`POST`,credentials:`same-origin`,cache:`no-store`,headers:{"X-MadelineProto-WebUI":`json`,Accept:`application/json`},body:new URLSearchParams(e)});if(!t.ok)throw Error(`HTTP `+t.status);if(!(t.headers.get(`Content-Type`)||``).includes(`application/json`))return{loggedIn:!0};let n=await t.json();if(n.loggedIn)return{loggedIn:!0};if(!n.state?.page)throw Error(`Unexpected answer`);return{state:n.state}}finally{Yt=!1}}function $t(){window.location.replace(Zt())}async function en(e,t,n){let r=new URL(e,window.location.href);if(r.origin!==window.location.origin)throw Error(`The QR code endpoint is on another origin`);r.search=r.search?r.search+`&`+t:`?`+t,r.hash=``;let i=await fetch(r.href,{credentials:`same-origin`,cache:`no-store`,redirect:`error`,signal:n});if(!i.ok)throw Error(`HTTP `+i.status);return i.json()}function tn(e){return{name:(()=>{switch(e.view){case`login.signIn`:return`signIn`;case`login.code`:return`authCode`;case`login.password`:return`password`;case`login.signUp`:return`signUp`;case`status`:return`status`;default:return`apiForm`}})(),page:e}}var[nn,rn]=k(()=>A(null)),[an,on]=k(()=>A(null));function sn(e){rn(e)}function cn(e){on(e),sn(tn(e.page))}function ln(e){let t=nn();return!t||t.name!==e?null:t}async function un(e){Jt();let t=await Qt(e);if(`loggedIn`in t){await dn();return}on(t.state);let n=tn(t.state.page),r=nn();if(r&&t.state.page.view===r.page.view)return t.state.page;sn(n)}async function dn(){document.getElementById(`auth-pages`)?.classList.add(`leaving`),$t()}var fn=se();function pn(){let e=ce(fn);if(!e)throw Error(`useAuthFlow() called outside of <AuthFlowProvider>`);return e}var mn=()=>{},hn=(e,t)=>t();function gn(e,t){let n=P(e),r=n?[n]:[],{onEnter:i=hn,onExit:a=hn}=t,[o,s]=A(t.appear?[]:r),[c]=oe(),l,u=!1;function d(e,t){if(!e)return t&&t();u=!0,a(e,()=>{te(()=>{u=!1,s(t=>t.filter(t=>t!==e)),t&&t()})})}function f(e){let t=l;if(!t)return e&&e();l=void 0,s(e=>[t,...e]),i(t,e??mn)}let p=t.mode===`out-in`?e=>u||d(e,f):t.mode===`in-out`?e=>f(()=>d(e)):e=>{d(e),f()};return ee(t=>{let n=e();return P(c)?(c(),t):(n!==t&&(l=n,te(()=>P(()=>p(t)))),n)},t.appear?void 0:n),o}var _n=e=>e instanceof Element;function vn(e,t){if(t(e))return e;if(typeof e==`function`&&!e.length)return vn(e(),t);if(Array.isArray(e))for(let n of e){let e=vn(n,t);if(e)return e}return null}function yn(e,t=_n,n=_n){let r=N(e);return N(()=>vn(r(),t))}function bn(e){return N(()=>{let t=e.name||`s`;return{enterActive:(e.enterActiveClass||t+`-enter-active`).split(` `),enter:(e.enterClass||t+`-enter`).split(` `),enterTo:(e.enterToClass||t+`-enter-to`).split(` `),exitActive:(e.exitActiveClass||t+`-exit-active`).split(` `),exit:(e.exitClass||t+`-exit`).split(` `),exitTo:(e.exitToClass||t+`-exit-to`).split(` `),move:(e.moveClass||t+`-move`).split(` `)}})}function xn(e){requestAnimationFrame(()=>requestAnimationFrame(e))}function Sn(e,t,n,r){let{onBeforeEnter:i,onEnter:a,onAfterEnter:o}=t;i?.(n),n.classList.add(...e.enter),n.classList.add(...e.enterActive),queueMicrotask(()=>{if(!n.parentNode)return r?.();a?.(n,()=>s())}),xn(()=>{n.classList.remove(...e.enter),n.classList.add(...e.enterTo),(!a||a.length<2)&&(n.addEventListener(`transitionend`,s),n.addEventListener(`animationend`,s))});function s(t){(!t||t.target===n)&&(r?.(),n.removeEventListener(`transitionend`,s),n.removeEventListener(`animationend`,s),n.classList.remove(...e.enterActive),n.classList.remove(...e.enterTo),o?.(n))}}function Cn(e,t,n,r){let{onBeforeExit:i,onExit:a,onAfterExit:o}=t;if(!n.parentNode)return r?.();i?.(n),n.classList.add(...e.exit),n.classList.add(...e.exitActive),a?.(n,()=>s()),xn(()=>{n.classList.remove(...e.exit),n.classList.add(...e.exitTo),(!a||a.length<2)&&(n.addEventListener(`transitionend`,s),n.addEventListener(`animationend`,s))});function s(t){(!t||t.target===n)&&(r?.(),n.removeEventListener(`transitionend`,s),n.removeEventListener(`animationend`,s),n.classList.remove(...e.exitActive),n.classList.remove(...e.exitTo),o?.(n))}}var wn={inout:`in-out`,outin:`out-in`},Tn=e=>{let t=bn(e);return gn(yn(()=>e.children),{mode:wn[e.mode],appear:e.appear,onEnter(n,r){Sn(t(),e,n,r)},onExit(n,r){Cn(t(),e,n,r)}})},En=window,Dn=navigator?navigator.userAgent:null;navigator.userAgent.search(/OS X|iPhone|iPad|iOS/i),navigator.userAgent.toLowerCase().indexOf(`android`),/Chrome/.test(navigator.userAgent)&&/Google Inc/.test(navigator.vendor),(()=>{try{return+navigator.userAgent.match(/Chrom(?:e|ium)\/(.+?)(?:\s|\.)/)[1]}catch{}})();var On=(/iPad|iPhone|iPod/.test(navigator.platform)||navigator.platform===`MacIntel`&&navigator.maxTouchPoints>1)&&!En.MSStream,kn=`safari`in En||!!(Dn&&(/\b(iPad|iPhone|iPod)\b/.test(Dn)||Dn.match(`Safari`)&&!Dn.match(`Chrome`)));navigator.userAgent.toLowerCase().indexOf(`firefox`);var An=kn&&On;(navigator.maxTouchPoints===void 0||navigator.maxTouchPoints>0)&&navigator.userAgent.search(/iOS|iPhone OS|Android|BlackBerry|BB10|Series ?[64]0|J2ME|MIDP|opera mini|opera mobi|mobi.+Gecko|Windows Phone/i);function jn(...e){return e.filter(Boolean).join(` `)}var Mn={isAvailable(e){return!document.body.classList.contains(`animation-level-0`)}},J={host:`authFlow_host`,closeButton:`authFlow_closeButton`,themeButton:`authFlow_themeButton`,leaving:`authFlow_leaving`,scrollable:`authFlow_scrollable`,placeholder:`authFlow_placeholder`,placeholderTop:`authFlow_placeholderTop`,cardsContainer:`authFlow_cardsContainer`,card:`authFlow_card`,logo:`authFlow_logo`,logoContainer:`authFlow_logoContainer`,errorLabel:`authFlow_errorLabel`,forgotLink:`authFlow_forgotLink`,phoneWrapper:`authFlow_phoneWrapper`,phone:`authFlow_phone`,phoneEdit:`authFlow_phoneEdit`,"no-touch":`authFlow_no-touch`,codeInputField:`authFlow_codeInputField`,codeInputHost:`authFlow_codeInputHost`,wordInputField:`authFlow_wordInputField`,wordSubmit:`authFlow_wordSubmit`,cardLink:`authFlow_cardLink`,qrDescription:`authFlow_qrDescription`,qrDescriptionMarker:`authFlow_qrDescriptionMarker`,qrDescriptionItem:`authFlow_qrDescriptionItem`,qrContainer:`authFlow_qrContainer`,qrCanvas:`authFlow_qrCanvas`,pageSignUp:`authFlow_pageSignUp`,pageAuthCode:`authFlow_pageAuthCode`,pagePassword:`authFlow_pagePassword`,pageSignQR:`authFlow_pageSignQR`,hostEnter:`authFlow_hostEnter`,hostEntering:`authFlow_hostEntering`,hostExit:`authFlow_hostExit`,hostExiting:`authFlow_hostExiting`,cardEnterActive:`authFlow_cardEnterActive`,cardExitActive:`authFlow_cardExitActive`,cardEnter:`authFlow_cardEnter`,cardEnterTo:`authFlow_cardEnterTo`,cardExit:`authFlow_cardExit`,cardExitTo:`authFlow_cardExitTo`,notices:`authFlow_notices`,notice:`authFlow_notice`,warning:`authFlow_warning`,error:`authFlow_error`,success:`authFlow_success`,noticeIcon:`authFlow_noticeIcon`,noticeBody:`authFlow_noticeBody`,noticeControls:`authFlow_noticeControls`,noticeClose:`authFlow_noticeClose`,noticePager:`authFlow_noticePager`,noticeTitle:`authFlow_noticeTitle`,noticeCode:`authFlow_noticeCode`,instructions:`authFlow_instructions`,instructionText:`authFlow_instructionText`,pageApi:`authFlow_pageApi`,fieldHint:`authFlow_fieldHint`,avatar:`authFlow_avatar`,statusIcon:`authFlow_statusIcon`},Nn=W(`<span aria-hidden=true>`),Pn=e=>{let[t,n]=Pe(e,[`icon`,`class`]);return(()=>{var e=Nn();return at(e,Ne({get class(){return jn(`tgico`,`tgico-`+t.icon,t.class)}},n),!1,!0),K(e,()=>Bt(t.icon)),e})()};function Fn(e,t){return e.closest(`.`+t)}function In(e,t){if(!e)return null;if(e.parentElement===t)return e;for(;e.parentElement;)if(e=e.parentElement,e.parentElement===t)return e;return null}function Ln(e){try{e()}catch(e){console.error(`fastRaf callback error:`,e)}}var Rn;function zn(e){Rn?Rn.push(e):(Rn=[e],requestAnimationFrame(()=>{let e=Rn;Rn=void 0,e.forEach(Ln)}))}function Bn(){return new Promise(e=>{zn(()=>{zn(e)})})}function Vn(e,t,n,{isStale:r,onEnd:i}={}){let a=Date.now(),o=document.createElement(`div`),s=(e.ownerDocument.defaultView||window).getComputedStyle(e).getPropertyValue(`--ripple-duration`).replace(`s`,``)*1e3;return zn(()=>{if(r?.())return;let i=e.getBoundingClientRect();o.classList.add(`c-ripple__circle`);let a=t-i.left,s=n-i.top,c=Math.sqrt((Math.abs(s-i.height/2)+i.height/2)**2+(Math.abs(a-i.width/2)+i.width/2)**2),l=a-c/2,u=s-c/2;o.style.width=o.style.height=c+`px`,o.style.left=l+`px`,o.style.top=u+`px`,o.style.opacity=`0`,e.append(o),o.offsetWidth,o.style.opacity=``}),()=>{let e=Date.now()-a,t=()=>{zn(()=>{o.remove()}),i?.()};if(e<s){let n=Math.max(s-e,s/2);setTimeout(()=>o.classList.add(`hiding`),Math.max(n-s/2,0)),setTimeout(t,n)}else o.classList.add(`hiding`),setTimeout(t,s/2)}}var Hn=0;function Un(e,t=!0,n=()=>Promise.resolve(),r=null,i=e){if(e.querySelector(`:scope > .c-ripple`))return;e.classList.add(`rp`);let a=document.createElement(`div`);a.classList.add(`c-ripple`),e.classList.contains(`rp-square`)&&a.classList.add(`is-square`),t!==`no`&&e[t?`prepend`:`append`](a);let o,s,c=(e,t)=>{let i=Hn++,c=o=s=()=>{if(l(),!Ct){let e=a.ownerDocument.defaultView||window;e.removeEventListener(`contextmenu`,o),e.removeEventListener(`mousemove`,o)}o=null,u=!1};n?.(i);let l=Vn(a,e,t,{isStale:()=>s!==c,onEnd:()=>r?.(i)})},l=t=>t.target!==e&&([`BUTTON`,`A`].includes(t.target.tagName)||Fn(t.target,`c-ripple`)!==a)&&(i===e||!In(t.target,i))&&!Fn(t.target,`checkbox-field`),u=!1;if(Ct){let e=()=>{o?.()},t=t=>{if(!Mn.isAvailable(`animations`)||t.touches.length>1||u||l(t))return;u=!0;let{clientX:n,clientY:r}=t.touches[0];c(n,r),i.addEventListener(`touchend`,e,{once:!0}),window.addEventListener(`touchmove`,t=>{t.cancelBubble=!0,t.stopPropagation(),e(),i.removeEventListener(`touchend`,e)},{once:!0})};return i.addEventListener(`touchstart`,t,{passive:!0}),{dispose:()=>{i.removeEventListener(`touchstart`,t),a.remove()},element:a}}{let e=e=>{if(![0,2].includes(e.button)||!Mn.isAvailable(`animations`)||i.dataset.ripple===`0`||l(e))return;if(u){u=!1;return}let{clientX:t,clientY:n}=e;c(t,n);let r=i.ownerDocument.defaultView||window;r.addEventListener(`mouseup`,o,{once:!0,passive:!0}),r.addEventListener(`contextmenu`,o,{once:!0,passive:!0})};return i.addEventListener(`mousedown`,e,{passive:!0}),{dispose:()=>{i.removeEventListener(`mousedown`,e),a.remove()},element:a}}}function Wn(e,t,n){if(t){j(()=>{let r=t();if(r===void 0||r){let t=Un(e,n);ne(()=>{t?.dispose()})}});return}return Un(e,n)}function Gn(e){let t=P(()=>e.element);K(t,le(()=>e.children));let n={};return M(()=>{let[r,i]=Pe(e,[`element`,`children`]);st(t,i,t instanceof SVGElement,!0,n)}),t}function Kn(e){let[t,n]=Pe(e,[`noRipple`,`rippleSquare`,`component`,`children`,`class`,`classList`,`ref`]),[r,i]=A(),a=document.createElement(t.component||`div`);return j(()=>{if(!t.noRipple){let e=Wn(a,void 0,`no`);i(e.element),ne(()=>{e.dispose(),i()})}}),t.ref?.(a),V(Gn,Ne({element:a},n,{get classList(){return{[t.class]:!!t.class,rp:!t.noRipple,"rp-square":!t.noRipple&&!!t.rippleSquare,...t.classList||{}}},get children(){return[U(()=>r()),U(()=>t.children)]}}))}var Y=(e={})=>{let t,n;return e.disabled===void 0?[t,n]=A(!1):t=N(()=>e.disabled),V(Kn,{ref(t){var n=e.ref;typeof n==`function`?n(t):e.ref=t},get component(){return e.as||`button`},get type(){return!e.as||e.as===`button`?`button`:void 0},get class(){return jn(e.class,e.primaryFilled&&`btn-primary btn-color-primary`,e.primary&&`btn btn-primary primary`,e.primaryTransparent&&`btn-primary primary btn-transparent`,e.large&&`btn-large`,e.onlyMobile&&`only-handhelds`)},get disabled(){return t()},get onClick(){return U(()=>!!(e.onClick&&n))()?t=>{try{let r=e.onClick(t);r instanceof Promise&&(n(!0),r.finally(()=>{n(!1)}))}catch(e){throw e}}:e.onClick},get noRipple(){return e.noRipple},get rippleSquare(){return e.rippleSquare},get tabIndex(){return e.tabIndex},get"aria-hidden"(){return e[`aria-hidden`]},get"aria-label"(){return e[`aria-label`]},get"aria-pressed"(){return e[`aria-pressed`]},get"aria-expanded"(){return e[`aria-expanded`]},get"aria-controls"(){return e[`aria-controls`]},get"on:click"(){return e[`on:click`]},get"on:keydown"(){return e[`on:keydown`]},get children(){return[U(()=>U(()=>!!e.icon)()&&V(Pn,{get icon(){return e.icon},get class(){return jn(`button-icon`,e.iconClass)}})),U(()=>U(()=>!!e.text)()?q(e.text,e.textArgs):e.children),U(()=>U(()=>!!e.iconAfter)()&&V(Pn,{get icon(){return e.iconAfter},get class(){return jn(`button-icon`,e.iconClass)}}))]}})};Y.Corner=e=>V(Y,Ne(e,{get class(){return jn(`btn-circle`,`btn-corner`,`z-depth-1`,e.class)},get tabIndex(){return e.tabIndex}})),Y.Icon=e=>V(Y,Ne(e,{get class(){return jn(`btn-icon`,e.icon,e.class)},get"aria-label"(){return e[`aria-label`]},get tabIndex(){return e.tabIndex}}));var qn=W(`<svg class=glass-defs aria-hidden=true><filter id=mp-glass-refraction x=0 y=0 width=100% height=100% color-interpolation-filters=sRGB><feTurbulence type=fractalNoise baseFrequency="0.006 0.009"numOctaves=2 seed=7 result=noise></feTurbulence><feGaussianBlur in=noise stdDeviation=2.5 result=lens></feGaussianBlur><feDisplacementMap in=SourceGraphic in2=lens scale=34 xChannelSelector=R yChannelSelector=G>`),Jn=W(`<div class=light-field aria-hidden=true><span class=light-field-blob></span><span class=light-field-blob></span><span class=light-field-blob></span><span class=light-field-blob></span><div class=light-field-texture>`);function Yn(){let e=navigator.userAgentData?.brands,t=e?e.some(({brand:e})=>e===`Chromium`):/\bChrom(e|ium)\/\d+/.test(navigator.userAgent),n=window.matchMedia?.(`(prefers-reduced-transparency: reduce), (prefers-contrast: more)`).matches;return t&&!n&&typeof CSS<`u`&&CSS.supports(`backdrop-filter`,`blur(1px)`)}function Xn(){if(window.matchMedia?.(`(prefers-reduced-motion: reduce), (hover: none)`).matches)return()=>{};let e=document.documentElement,t=0,n=0,r=0,i=i=>{n=i.clientX,r=i.clientY,t||(t=requestAnimationFrame(()=>{t=0,e.style.setProperty(`--glass-x`,n+`px`),e.style.setProperty(`--glass-y`,r+`px`)}))};return window.addEventListener(`pointermove`,i,{passive:!0}),()=>{window.removeEventListener(`pointermove`,i),cancelAnimationFrame(t)}}function Zn(){return F(()=>{document.documentElement.classList.toggle(`glass-refraction`,Yn()),ne(Xn())}),[(()=>{var e=qn();return e.firstChild,e})(),Jn()]}var X={container:`mediaHeader_container`,alignStart:`mediaHeader_alignStart`,title:`mediaHeader_title`,subtitle:`mediaHeader_subtitle`,onBackdrop:`mediaHeader_onBackdrop`,sticker:`mediaHeader_sticker`,backdrop:`mediaHeader_backdrop`,marginTop:`mediaHeader_marginTop`,marginBottom:`mediaHeader_marginBottom`,title20:`mediaHeader_title20`,secondary:`mediaHeader_secondary`,danger:`mediaHeader_danger`,lottie:`mediaHeader_lottie`},Qn=W(`<div>`);function Z(e){return(()=>{var t=Qn();return K(t,()=>e.children),j(()=>G(t,jn(X.container,e.align===`start`&&X.alignStart,e.onBackdrop&&X.onBackdrop,e.marginTop&&X.marginTop,e.marginBottom&&X.marginBottom,e.class))),t})()}Z.Backdrop=function(e){return(()=>{var t=Qn();return K(t,()=>e.children),j(()=>G(t,jn(X.backdrop,e.class))),t})()},Z.Sticker=function(e){let t=()=>e.size||130;return e.element&&e.onReady?.(),(()=>{var n=Qn(),r=e.ref;return typeof r==`function`?ot(r,n):e.ref=n,K(n,(()=>{var t=U(()=>typeof e.element==`function`);return()=>t()?e.element():e.element})()),j(r=>{var i=jn(X.sticker,e.class),a=t()+`px`;return i!==r.e&&G(n,r.e=i),a!==r.t&&it(n,`--sticker-size`,r.t=a),r},{e:void 0,t:void 0}),n})()},Z.Title=function(e){return V(St,{get component(){return e.tag||`div`},"data-popup-title":!0,get class(){return jn(X.title,e.size===20&&X.title20,e.class)},get children(){return e.children}})},Z.Subtitle=function(e){return(()=>{var t=Qn();return K(t,()=>e.children),j(()=>G(t,jn(X.subtitle,e.color&&X[e.color],e.class))),t})()};var $n=navigator.userAgent.search(/OS X|iPhone|iPad|iOS/i)!==-1;function er(e){if(e||(e=window.event),e){e=e.originalEvent||e;try{e.stopPropagation&&e.stopPropagation(),e.preventDefault&&e.preventDefault(),e.returnValue=!1,e.cancelBubble=!0}catch{}}return!1}function tr(e,t){return e.closest(t)}var nr=new Map([[`FT`,`ðŸ’Ž`]]);function rr(e){return nr.get(e)??String.fromCharCode(55356,56741+e.charCodeAt(0),55356,56741+e.charCodeAt(1))}var ir=function(e){return e[e.Neutral=0]=`Neutral`,e[e.Valid=1]=`Valid`,e[e.Error=2]=`Error`,e}({}),ar=0,or=()=>`input-field-`+ ++ar,sr=class{constructor(e={}){this.options=e,this.container=document.createElement(`div`),this.container.classList.add(`input-field`),this.required=e.required,this.allowStartingSpace=e.allowStartingSpace,e.maxLength!==void 0&&e.showLengthOn===void 0&&(e.showLengthOn=Math.min(40,Math.round(e.maxLength/3)));let{placeholder:t,maxLength:n,showLengthOn:r,name:i,autocomplete:a}=e,o=e.label||e.labelText,s=this.input=document.createElement(`input`);if(s.type=`text`,i&&(s.name=i),s.autocomplete=a??`off`,o&&(s.required=!0),s.classList.add(`input-field-input`),s.dir=`auto`,this.container.append(s),e.inputMode&&(s.inputMode=e.inputMode),t&&(this.placeholder=document.createElement(`span`),this.placeholder.id=or(),o||s.setAttribute(`aria-labelledby`,this.placeholder.id),this.placeholder.classList.add(`input-field-placeholder`),this.placeholder.append(q(t)),this.container.append(this.placeholder)),o||t){let e=document.createElement(`div`);e.classList.add(`input-field-border`),this.container.append(e)}(o!=null||n)&&(this.label=document.createElement(`label`),this.label.id=or(),s.id||(s.id=or()),this.label.htmlFor=s.id,this.setLabel(),this.container.append(this.label));let c=[()=>this.setEmpty()];if(n){let e=this.label,t=!1;c.push(()=>{let i=s.classList.contains(`error`),a=n-s.value.length,o=a<0;s.classList.toggle(`error`,o),e.classList.toggle(`error`,o),o||a<=r?(this.setLabel(),e.append(` (${n-s.value.length})`),e.style.visibility=`visible`,t=!0):(i&&!o||t)&&(this.setLabel(),t=!1)})}e.onRawInput&&c.push(()=>e.onRawInput(this.value)),s.addEventListener(`input`,()=>{c.forEach(e=>e())}),this.setEmpty()}select(){this.value&&this.input.select()}setLabel(){this.label&&(this.label.textContent=``,this.options.labelText?this.label.textContent=this.options.labelText:this.options.label&&this.label.append(q(this.options.label,this.options.labelOptions)),this.label.style.visibility=this.label.textContent?`visible`:`hidden`)}get value(){return this.input.value}set value(e){this.setValueSilently(e),this.simulateInputEvent()}simulateInputEvent(){this.input.dispatchEvent(new Event(`input`,{bubbles:!0,cancelable:!0}))}setValueSilently(e){this.input.value=typeof e==`string`?e:e?.textContent??``,this.setEmpty()}setEmpty(){let e=this.isEmpty();[this.input,this.placeholder].filter(Boolean).forEach(t=>{t.classList.toggle(`is-empty`,e)})}isEmpty(){return!(this.allowStartingSpace?this.input.value:this.input.value.trim())}isValid(){return!this.input.classList.contains(`error`)&&(!this.required||!this.isEmpty())}setState(e,t,n){let r=!!(e&2);t?(this.label.textContent=``,this.label.append(q(t,n??this.options.labelOptions)),this.label.style.visibility=`visible`):this.setLabel(),r?this.input.setAttribute(`aria-invalid`,`true`):this.input.removeAttribute(`aria-invalid`),this.input.classList.toggle(`error`,r),this.input.classList.toggle(`valid`,!!(e&1))}setError(e,t){this.setState(2,e,t)}},cr,lr=()=>{cr=Ut.countriesList.filter(e=>!e.pFlags?.hidden).sort((e,t)=>(e.name||e.default_name).localeCompare(t.name||t.default_name))},ur=()=>{ur=void 0,lr()},dr=new Set([`FT`]),fr=0;function pr(e,t){return ur?.(),e=e.toLowerCase(),cr.filter(n=>{if(t&&dr.has(n.iso2))return!1;let r=[n.name,n.default_name,n.iso2];return r.filter(Boolean).forEach(e=>{let t=e.split(` `).filter(e=>/\w/.test(e)).map(e=>e[0]).join(``);t.length>1&&r.push(t)}),!!r.filter(Boolean).find(t=>t.toLowerCase().indexOf(e)!==-1)})}var mr=class extends sr{constructor(e={}){super({label:`Country`,name:`country-`+Math.random().toString(36).slice(2),...e}),this.options=e,this.hidePicker=()=>{this.hideTimeout===void 0&&(this.selectWrapper.inert=!0,this.input.setAttribute(`aria-expanded`,`false`),this.selectWrapper.classList.remove(`active`),this.hideTimeout=window.setTimeout(()=>{this.selectWrapper.classList.add(`hide`),this.hideTimeout=void 0},200))},this.selectCountryByTarget=e=>{this.selectWrapper.querySelector(`[aria-selected="true"]`)?.setAttribute(`aria-selected`,`false`),e.setAttribute(`aria-selected`,`true`);let t=e.querySelector(`[data-default-name]`).dataset.defaultName,n=e.querySelector(`.phone-code`)?.textContent,r=n&&n.replace(/\D/g,``);this.lastCountrySelected=cr.find(e=>e.default_name===t),this.value=this.lastCountrySelected?.name||t,this.lastCountryCodeSelected=r&&this.lastCountrySelected.country_codes.find(e=>e.country_code===r),this.options.onCountryChange?.(this.lastCountrySelected,this.lastCountryCodeSelected),this.hidePicker()},ur?.(),this.liMap=new Map,this.container.classList.add(`input-select`);let t=this.selectWrapper=document.createElement(`div`);t.classList.add(`select-wrapper`,`z-depth-3`,`hide`);let n=document.createElement(`span`);n.classList.add(`arrow`,`arrow-down`),this.container.append(n);let r=document.createElement(`ul`);r.id=`country-options-`+ ++fr,r.setAttribute(`role`,`listbox`),this.input.setAttribute(`role`,`combobox`),this.input.removeAttribute(`aria-multiline`),this.input.setAttribute(`aria-autocomplete`,`list`),this.input.setAttribute(`aria-controls`,r.id),this.input.setAttribute(`aria-expanded`,`false`);let i=document.createElement(`div`);i.classList.add(`scrollable`,`scrollable-y`),t.append(i),i.append(r);let a=()=>{a=null,cr.forEach(t=>{if(e.noPhoneCodes&&dr.has(t.iso2))return;let n=rr(t.iso2),i=[];for(let a=0,o=Math.min(t.country_codes.length,e.noPhoneCodes?1:1/0);a<o;++a){let o=t.country_codes[a],s=document.createElement(`li`);s.setAttribute(`role`,`option`),s.setAttribute(`aria-selected`,`false`);let c=document.createElement(`span`);c.classList.add($n?`emoji-native`:`emoji`),c.textContent=n,s.append(c);let l=document.createElement(`span`);if(l.textContent=t.name||t.default_name,l.dataset.defaultName=t.default_name,s.append(l),!e.noPhoneCodes){let e=document.createElement(`span`);e.classList.add(`phone-code`);let t=document.createElement(`span`);t.dir=`ltr`,t.textContent=`+`+o.country_code,e.append(t),s.appendChild(e)}i.push(s),r.append(s)}this.liMap.set(t.iso2,i)}),r.addEventListener(`mousedown`,e=>{if(e.button!==0)return;let t=tr(e.target,`LI`);this.selectCountryByTarget(t)}),this.container.appendChild(t)};a(),this.input.addEventListener(`focus`,e=>{a?a():cr.forEach(e=>{let t=this.liMap.get(e.iso2);t&&t.forEach(e=>e.style.display=``)}),clearTimeout(this.hideTimeout),this.hideTimeout=void 0,t.classList.remove(`hide`),t.inert=!1,t.offsetWidth,t.classList.add(`active`),this.input.setAttribute(`aria-expanded`,`true`),this.select();let n=Fn(this.container,`scrollable-y`);if(n){let e=this.input.getBoundingClientRect().top-n.getBoundingClientRect().top+n.scrollTop-4;n.scrollTo({top:e,behavior:`smooth`})}setTimeout(()=>{o||(document.addEventListener(`mousedown`,s,{capture:!0}),o=!0)},0)});let o=!1,s=e=>{Fn(e.target,`input-select`)||e.target!==this.input&&(this.hidePicker(),document.removeEventListener(`mousedown`,s,{capture:!0}),o=!1)},c=e=>{let t=e.key;if(e.ctrlKey||t===`Control`)return!1;let n=new Set(pr(this.value).map(e=>e.iso2));this.liMap.forEach((e,t)=>{e.forEach(e=>e.style.display=n.has(t)?``:`none`)}),n.size?n.size===1&&t===`Enter`&&(er(e),this.selectCountryByTarget(this.liMap.get([...n][0])[0])):cr.forEach(e=>{let t=this.liMap.get(e.iso2);t&&t.forEach(e=>e.style.display=``)})};this.input.addEventListener(`keyup`,c),this.input.addEventListener(`keydown`,e=>{if(e.key===`Enter`){c(e);return}this.selectWrapper.classList.contains(`hide`)||(e.key===`Tab`||e.key===`Escape`)&&this.hidePicker()}),n.addEventListener(`mousedown`,e=>{this.input.matches(`:focus`)?(this.hidePicker(),this.input.blur()):(e.cancelBubble=!0,e.preventDefault(),this.input.focus())})}getSelected(){return{country:this.lastCountrySelected,code:this.lastCountryCodeSelected}}selectCountryByIso2(e){this.selectCountryByTarget(this.liMap.get(e)[0])}override(e,t,n){this.setValueSilently(e?e.name||e.default_name:n),this.lastCountrySelected=e,this.lastCountryCodeSelected=t,this.options.onCountryChange?.(this.lastCountrySelected,this.lastCountryCodeSelected)}},Q=new Map,hr=new Map,gr=0,_r=(e,t,n=``)=>{n=t.country_code+n,gr=Math.max(gr,n.length),Q.set(n,{country:e,code:t}),hr.has(t.country_code)||hr.set(t.country_code,{country:e,code:t})};function $(e,t,n){let r=e.patterns||[],i=n?t:t.slice(e.country_code.length),a=``,o=0,s=``;for(let e=r.length-1;e>=0;--e){a=r[e];let t=a.replace(/ /g,``),n=0;for(let e=0,r=Math.min(i.length,t.length);e<r;++e)if(i[e]===t[e])n+=1.01;else if(t[e]===`X`)++n;else{n=0;break}n>o&&(o=n,s=a)}a=s||a,a=a.replace(/\d/g,`X`),n||(a=e.country_code+` `+a),a.split(``).forEach((e,n)=>{e===` `&&t[n]!==` `&&t.length>n&&(t=t.slice(0,n)+` `+t.slice(n))});let c=a&&a.length>t.length?a.slice(t.length):``;return c&&(c=c.replace(/X/g,`â€’`)),{formatted:t,leftPattern:c}}function vr(e,t){e=e||``,Q.size||Ut.countriesList.forEach(e=>{e.country_codes.forEach(t=>{t.prefixes?t.prefixes.forEach(n=>{_r(e,t,n)}):_r(e,t)})});let n=e.replace(/\D/g,``),r=t?.defaultCountryCode;if(r&&!e.trimStart().startsWith(`+`)&&!n.startsWith(r)){let e=hr.get(r);if(e){let{formatted:t,leftPattern:r}=$(e.code,n,!0);return{formatted:t,country:e.country,code:void 0,leftPattern:r}}return{formatted:n,country:void 0,code:void 0,leftPattern:``}}let i=n.slice(0,gr),a;for(let e=i.length-1;e>=0&&(a=Q.get(i.slice(0,e+1)),!a);--e);if(!a)return{formatted:n,country:void 0,code:void 0,leftPattern:``};let o=a.country,{formatted:s,leftPattern:c}=$(a.code,n,!1);return{formatted:s,country:o,code:a.code,leftPattern:c}}var yr=36,br=class extends sr{constructor(e={}){super({label:`Contacts.PhoneNumber.Placeholder`,name:`phone`,...e}),this.pasted=!1,this.lastValue=``,this.container.classList.add(`input-field-phone`);let t=this.input;t.dir=`ltr`,t.type=`tel`,t.autocomplete=`rr55RandomRR55`,t.addEventListener(`input`,()=>{t.classList.remove(`error`),this.pastedValue!==void 0&&(this.setValueSilently(this.pastedValue),this.pastedValue=void 0);let n=this.value.replace(/\D/g,``);n.length>yr&&this.setValueSilently(`+`+n.slice(0,yr));let r=this.value;Math.abs(r.length-this.lastValue.length)>1&&!this.pasted&&On&&this.setValueSilently(this.lastValue+r),this.pasted=!1,this.setLabel();let i,a,o=``;this.value.replace(/\++/,`+`)===`+`?this.setValueSilently(`+`):(i=vr(this.value),a=i.formatted,i.country,o=i.leftPattern,i.code,this.setValueSilently(this.lastValue=a?`+`+a:``)),t.dataset.leftPattern=o,e.onInput&&e.onInput(i)}),t.addEventListener(`paste`,e=>{this.pasted=!0;let t=e.clipboardData?.getData(`text/plain`),n=t?.replace(/\D/g,``);if(n){if(t.trimStart().startsWith(`+`)||n.startsWith(`00`))this.pastedValue=`+`+(n.startsWith(`00`)?n.slice(2):n);else{let e=this.value.replace(/\D/g,``);this.pastedValue=`+`+e+(e?n.replace(/^0/,``):n)}}}),t.addEventListener(`keypress`,e=>{let t=e.key;if(/\D/.test(t)&&!(e.metaKey||e.ctrlKey)&&t!==`Backspace`&&!(t===`+`&&e.shiftKey))return e.preventDefault(),!1})}};function xr(e,t=!1,n=!0){let r=e.ownerDocument.activeElement,i=!!r&&(r.tagName===`INPUT`||r.isContentEditable);if(!Ct||t&&i){if(n&&e.focus(),e instanceof HTMLInputElement){let t=e.value.length;e.selectionStart=t,e.selectionEnd=t}else{let t=e.ownerDocument.defaultView;if(!t)return;let n=e.ownerDocument.createRange();n.selectNodeContents(e),n.collapse(!1);let r=t.getSelection();if(!r)return;r.removeAllRanges(),r.addRange(n)}}}function Sr(){for(let e of navigator.languages??[navigator.language])try{let t=new Intl.Locale(e).maximize().region;if(t)return t}catch{}}function Cr(e){let t,n,r=!1,i=new mr({onCountryChange:(e,i)=>{t=e,n=i,i&&!r&&(a.value=a.lastValue=`+`+i.country_code,setTimeout(()=>{a.input.focus(),xr(a.input,!0)},0))}}),a=new br({onInput:o=>{let{country:s,code:c}=o||{},l=s?s.name||s.default_name:``;l!==i.value&&(!t||!s||!c||t!==s&&n.country_code!==c.country_code)&&(r=!0,i.override(s,c,l),r=!1),e(!!(s||a.value.length-1>1))}});return{countryInputField:i,telInputField:a,init(e){if(e){a.value=e;return}let t=Sr();if(t)try{i.selectCountryByIso2(t)}catch{}}}}var wr=W(`<svg xmlns=http://www.w3.org/2000/svg viewBox="0 0 32 32"aria-hidden=true><rect width=32 height=32 rx=9 style=fill:var(--primary-color)></rect><path d="M9 22.5V10l7 7.5 7-7.5v12.5"fill=none stroke=#fff stroke-width=3 stroke-linecap=round stroke-linejoin=round>`),Tr=`M9 22.5V10l7 7.5 7-7.5v12.5`;function Er(e){return(()=>{var t=wr();return t.firstChild.nextSibling,j(()=>Qe(t,`class`,e.class)),t})()}function Dr(e){return`<svg xmlns="http://www.w3.org/2000/svg" viewBox="-6 -6 44 44"><rect width="32" height="32" rx="9" fill="${e}"/><path d="${Tr}" fill="none" stroke="#fff" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></svg>`}function Or(e){return new Promise(t=>{setTimeout(t,e)})}var kr=1e3;function Ar(e){let t=[];for(let n=e;n&&typeof n.getAnimations==`function`;n=n.parentElement)for(let e of n.getAnimations())e.effect?.getTiming().iterations!==1/0&&t.push(e);return t}function jr(e,t){let n=!1,r=0,i=()=>!n&&(!t||t()),a=async()=>{let t=Date.now()+kr;for(await Bn();i()&&Date.now()<t;){let n=Ar(e);if(!n.length)break;await Promise.race([Promise.all(n.map(e=>e.finished.catch(()=>{}))),Or(Math.max(0,t-Date.now()))])}i()&&e.focus()},o=()=>{if(i()){if(e.isConnected){a();return}r=requestAnimationFrame(o)}};return r=requestAnimationFrame(o),()=>{n=!0,cancelAnimationFrame(r)}}function Mr(e,t){if(typeof t==`string`){e.textContent=t;return}let n=e.firstChild;n?e.lastChild===n?n.replaceWith(t):(e.textContent=``,e.append(t)):e.append(t)}var Nr=W(`<div class=input-wrapper>`),Pr=W(`<div>`);function Fr(e){let t=()=>e.inputWrapper!==!1;return(()=>{var n=Pr();return K(n,()=>e.header,null),K(n,V(Re,{get when(){return t()},get fallback(){return e.children},get children(){var t=Nr();return K(t,()=>e.children),t}}),null),j(()=>G(n,jn(J.card,e.class))),n})()}var Ir=W(`<span>`),Lr=W(`<svg><path d="M6.5 10.4l2.3 2.3 4.7-5.2"fill=none stroke=var(--notice-glyph) stroke-width=1.8 stroke-linecap=round stroke-linejoin=round></svg>`,!1,!0,!1),Rr=W(`<svg><circle cx=10 cy=6.4 r=1.15 fill=var(--notice-glyph)></svg>`,!1,!0,!1),zr=W(`<svg><rect x=9 y=8.6 width=2 height=5.6 rx=1 fill=var(--notice-glyph)></svg>`,!1,!0,!1),Br=W(`<svg><rect x=9 y=5.2 width=2 height=6.4 rx=1 fill=var(--notice-glyph)></svg>`,!1,!0,!1),Vr=W(`<svg><circle cx=10 cy=14 r=1.15 fill=var(--notice-glyph)></svg>`,!1,!0,!1),Hr=W(`<svg viewBox="0 0 20 20"aria-hidden=true><circle cx=10 cy=10 r=10 fill=currentColor>`),Ur=W(`<div>`),Wr=W(`<pre>`),Gr=W(`<div><div>`),Kr=W(`<button type=button>/<svg viewBox="0 0 16 16"aria-hidden=true><path d="M6 3.5l4.5 4.5L6 12.5"fill=none stroke=currentColor stroke-width=1.6 stroke-linecap=round stroke-linejoin=round>`),qr=W(`<div><button type=button><svg viewBox="0 0 16 16"aria-hidden=true><path d="M4.5 4.5l7 7M11.5 4.5l-7 7"fill=none stroke=currentColor stroke-width=1.6 stroke-linecap=round>`);function Jr(e){return V(Re,{get when(){return typeof e.text!=`string`},get fallback(){return(()=>{var t=Ir();return K(t,()=>e.text),j(()=>G(t,e.class)),t})()},get children(){var t=Ir();return j(n=>{var r=e.class,i=e.text.html;return r!==n.e&&G(t,n.e=r),i!==n.t&&(t.innerHTML=n.t=i),n},{e:void 0,t:void 0}),t}})}function Yr(e){let t={...e.data};return delete t.rpcError,{...e,data:t,alerts:e.alerts.filter(e=>e.level!==`error`)}}function Xr(e){return e==null||(typeof e==`string`?e===``:e.html===``)}function Zr(e){return typeof e==`string`?e:new DOMParser().parseFromString(e.html,`text/html`).body.textContent??``}function Qr(e){if(!e)return;let t=e.data?.rpcError;if(t&&Ut.has(t))return q(t);let n=e.alerts.find(e=>e.level===`error`);if(n)return V(Jr,{get text(){return n.message}});if(t)return t}function $r(e){if(!e)return;let t=e.data?.rpcError;if(t&&Ut.has(t))return Ut.format(t,!0);let n=e.alerts.find(e=>e.level===`error`);return n?Zr(n.message):t}function ei(e){let t=()=>{switch(e.level){case`success`:return Lr();case`info`:return[Rr(),zr()];default:return[Br(),Vr()]}};return(()=>{var e=Hr();return e.firstChild,K(e,t,null),j(()=>Qe(e,`class`,J.noticeIcon)),e})()}function ti(e){return(()=>{var t=Gr(),n=t.firstChild;return K(t,V(ei,{get level(){return e.alert.level}}),n),K(n,V(Re,{get when(){return!Xr(e.alert.title)},get children(){var t=Ur();return K(t,V(Jr,{get text(){return e.alert.title}})),j(()=>G(t,J.noticeTitle)),t}}),null),K(n,V(Jr,{get text(){return e.alert.message}}),null),K(n,V(Re,{get when(){return!Xr(e.alert.code)},get children(){var t=Wr();return K(t,V(Jr,{get text(){return e.alert.code}})),j(()=>G(t,J.noticeCode)),t}}),null),K(t,()=>e.controls,null),j(r=>{var i=J.notice,a={[J[e.alert.level]]:!0},o=e.alert.level===`error`?`alert`:`status`,s=J.noticeBody;return i!==r.e&&G(t,r.e=i),r.t=nt(t,a,r.t),o!==r.a&&Qe(t,`role`,r.a=o),s!==r.o&&G(n,r.o=s),r},{e:void 0,t:void 0,a:void 0,o:void 0}),t})()}function ni(e){let t=()=>e.errors?e.alerts.filter(e=>e.level===`error`):[],n=()=>Kt()?[]:e.alerts.filter(e=>e.level!==`error`),[r,i]=A(0),a=N(()=>n()[r()%Math.max(n().length,1)]);return V(Re,{get when(){return t().length||n().length},get children(){var e=Ur();return K(e,V(Ie,{get each(){return t()},children:e=>V(ti,{alert:e})}),null),K(e,V(Re,{get when(){return a()},keyed:!0,children:e=>V(ti,{alert:e,get controls(){return(()=>{var e=qr(),t=e.firstChild;return K(e,V(Re,{get when(){return n().length>1},get children(){var e=Kr(),t=e.firstChild,a=t.nextSibling;return e.$$click=()=>i((r()+1)%n().length),K(e,()=>r()%n().length+1,t),K(e,()=>n().length,a),j(t=>{var n=J.noticePager,r=Ut.format(`Login.Next`,!0).replace(/\s*>$/,``);return n!==t.e&&G(e,t.e=n),r!==t.t&&Qe(e,`aria-label`,t.t=r),t},{e:void 0,t:void 0}),e}}),t),tt(t,`click`,Jt,!0),j(n=>{var r=J.noticeControls,i=J.noticeClose,a=Ut.format(`Close`,!0),o=Ut.format(`Close`,!0);return r!==n.e&&G(e,n.e=r),i!==n.t&&G(t,n.t=i),a!==n.a&&Qe(t,`aria-label`,n.a=a),o!==n.o&&Qe(t,`title`,n.o=o),n},{e:void 0,t:void 0,a:void 0,o:void 0}),e})()}})}),null),j(()=>G(e,J.notices)),e}})}Ze([`click`]);var ri={wrap:`codeInputField_wrap`,input:`codeInputField_input`,disabled:`codeInputField_disabled`,digit:`codeInputField_digit`,active:`codeInputField_active`,error:`codeInputField_error`,digitContent:`codeInputField_digitContent`,caret:`codeInputField_caret`,"caret-blink":`codeInputField_caret-blink`};function ii(e){return((t,n,r)=>{e.addEventListener(t,n,r),ne(()=>{e.removeEventListener(t,n,r)})})}var ai=W(`<div dir=ltr><input inputmode=numeric autocomplete=one-time-code required pattern=^\\d*$>`),oi=W(`<div>`),si=class{constructor(e){this.options=e,this.errorSignal=A(!1),this.disabledSignal=A(!1),this.valueSignal=A(``),this.container=k(t=>{let n=this;this.dispose=t;let r=V(ci,Ne(e,{ref(e){var t=n.input;typeof t==`function`?t(e):n.input=e},get error(){return n.errorSignal[0]()},get disabled(){return n.disabledSignal[0]()},get valueSignal(){return n.valueSignal}}));return le(()=>r)()})}set error(e){this.errorSignal[1](e)}set disabled(e){this.disabledSignal[1](e)}set value(e){this.valueSignal[1](e)}get value(){return this.valueSignal[0]()}cleanup(){this.dispose()}};function ci(e){let[t,n]=e.valueSignal??A(``),[r,i]=A(-1),[a,o]=A(-1),[s,c]=A(!1),l=!1,u=!1,d={inserting:!1,start:null,end:null},f=e=>{d.inserting=e.inserting,d.start=e.originalStart,d.end=e.originalEnd;let t=e.start,n=e.end;if(t===null||n===null){i(-1),o(-1);return}i(t),o(n)},p=t=>{if(!l||m.ownerDocument.activeElement!==m||m.selectionStart===null||m.selectionEnd===null){f({start:null,end:null,inserting:!1,originalStart:m.selectionStart,originalEnd:m.selectionEnd}),c(!1);return}let n=e.length,r=m.value.length<n&&m.selectionStart===m.value.length;if(c(r),r||m.selectionStart!==m.selectionEnd){f({start:m.selectionStart,end:r?m.selectionEnd+1:m.selectionEnd,inserting:r,originalStart:m.selectionStart,originalEnd:m.selectionEnd});return}let i=0,a=0,o;if(m.selectionStart===0)i=0,a=1,o=`forward`;else if(m.selectionStart===n)i=n-1,a=n,o=`backward`;else{let e=0,n=1;if(d.start!==null&&d.end!==null){let n=m.selectionStart<d.end&&Math.abs(d.start-d.end)===1;o=n?`backward`:`forward`,(n&&!d.inserting&&t!==`deleteContentForward`||!n&&u)&&(e+=-1)}u&&t===void 0&&(n+=1),i=m.selectionStart+e,a=m.selectionEnd+e+n}m.setSelectionRange(i,a,o),f({start:i,end:a,inserting:r,originalStart:m.selectionStart,originalEnd:m.selectionEnd})};ii(document)(`selectionchange`,()=>p());let m;return(()=>{var i=ai(),o=i.firstChild;return o.$$input=r=>{let i=m.value,a=i,o=t(),s=Math.abs((d.start??0)-(d.end??0));(d.inserting||s===o.length)&&(a=a.replace(/[^\d]/g,``)),a=a.slice(0,e.length);let c=!/^\d*$/.test(a);if(i.length!==0&&a.length===0||a===o||c){r.preventDefault(),r.currentTarget.value=o,c&&r.currentTarget.setSelectionRange(d.start??0,d.end??0);return}a.length<o.length&&p(r.inputType),n(a),e.onChange?.(a),a.length===e.length&&e.onFill?.(a)},o.$$keyup=e=>{e.key===`Shift`&&(u=!1)},o.$$keydown=e=>{e.key===`Shift`&&(u=!0)},o.addEventListener(`blur`,()=>{l=!1,p()}),o.addEventListener(`focus`,()=>{m.setSelectionRange(t().length,t().length),l=!0,p()}),ot(t=>{m=t,e.ref?.(t)},o),Qe(o,`spellcheck`,!1),K(i,V(Le,{get each(){return Array.from({length:e.length})},children:(e,n)=>(()=>{var e=oi();return K(e,V(Tn,{get children(){return V(Re,{get when(){return t()[n]},get children(){var e=oi();return K(e,()=>t()[n]),j(()=>G(e,ri.digitContent)),e}})}}),null),K(e,(()=>{var e=U(()=>!!(s()&&t().length===n));return()=>e()&&(()=>{var e=oi();return j(()=>G(e,ri.caret)),e})()})(),null),j(()=>G(e,jn(ri.digit,r()<=n&&n<a()&&ri.active))),e})()}),null),j(t=>{var n=jn(ri.wrap,e.error&&ri.error,e.disabled&&ri.disabled,e.class),r=ri.input,a=Ut.format(e.label||`AccDescr.LoginCode`,!0),s=e.disabled;return n!==t.e&&G(i,t.e=n),r!==t.t&&G(o,t.t=r),a!==t.a&&Qe(o,`aria-label`,t.a=a),s!==t.o&&(o.disabled=t.o=s),t},{e:void 0,t:void 0,a:void 0,o:void 0}),j(()=>o.value=t()),i})()}Ze([`keydown`,`keyup`,`input`]);var li=`TwoFactorSetupMonkeyIdle`,ui=`TwoFactorSetupMonkeyTracking`;function di(){return Promise.all([Ft.loadAnimationFromURLManually(li),Ft.loadAnimationFromURLManually(ui)])}var fi=class{constructor(e,t){this.inputField=e,this.size=t,this.max=45,this.needFrame=0,this.container=document.createElement(`div`),this.container.classList.add(`media-sticker-wrapper`);let n=e.input;n.addEventListener(`blur`,()=>{this.playAnimation(0)}),e instanceof si?(n.addEventListener(`focus`,()=>{this.playAnimation(1)}),n.addEventListener(`input`,t=>{zn(()=>{let t=(1+e.value.length)/e.options.length;this.playAnimation(t*this.max)})})):n.addEventListener(`input`,t=>{this.playAnimation(e.value.length)})}playAnimation(e){if(!this.animation)return;e=Math.min(e,30);let t;e?(t=Math.round(Math.min(this.max,e)*(165/this.max)+11.33),this.idleAnimation&&(this.idleAnimation.stop(!0),this.idleAnimation.canvas[0].style.display=`none`),this.animation.canvas[0].style.display=``):t=0;let n=this.needFrame>t?-1:1;this.animation.setDirection(n),this.needFrame!==0&&t===0&&this.animation.setSpeed(7),this.needFrame=t,this.animation.play()}load(){return this.loadPromise?this.loadPromise:this.loadPromise=Promise.all([Ft.loadAnimationAsAsset({container:this.container,loop:!0,autoplay:!0,width:this.size,height:this.size},li).then(e=>(this.idleAnimation=e,this.inputField.value.length||e.play(),Ft.waitForFirstFrame(e))),Ft.loadAnimationAsAsset({container:this.container,loop:!1,autoplay:!1,width:this.size,height:this.size},ui).then(e=>(this.animation=e,this.inputField.value.length||(this.animation.canvas[0].style.display=`none`),this.animation.addEventListener(`enterFrame`,e=>{(this.animation.direction===1&&e>=this.needFrame||this.animation.direction===-1&&e<=this.needFrame)&&(this.animation.setSpeed(1),this.animation.pause()),e===0&&this.needFrame===0&&this.idleAnimation&&(this.idleAnimation.canvas[0].style.display=``,this.idleAnimation.play(),this.animation.canvas[0].style.display=`none`)}),Ft.waitForFirstFrame(e)))])}remove(){this.animation&&this.animation.remove(),this.idleAnimation&&this.idleAnimation.remove()}},pi=W(`<svg xmlns=http://www.w3.org/2000/svg class=preloader-circular viewBox="25 25 50 50"><circle class=preloader-path cx=50 cy=50 r=20 fill=none stroke-miterlimit=10>`),mi=W(`<div class=input-wrapper>`);function hi(e){let{navigate:t,submit:n}=pn(),r=!1,[i,a]=A(!1),[o,s]=A(!1),[c,l]=A(q(`Login.Next`)),{countryInputField:u,telInputField:d,init:f}=Cr(s),p=d.input;p.addEventListener(`keypress`,e=>{if(o()&&!i()&&e.key===`Enter`)return h()});function m(e){if(e.data?.rpcError===`PHONE_NUMBER_INVALID`){d.setError(),Mr(d.label,q(`Login.PhoneLabelInvalid`)),p.classList.add(`error`),l(q(`Login.Next`));return}let t=$r(e);l(t||q(`Login.Next`))}function h(e){e&&er(e),a(!0),l([U(()=>q(`PleaseWait`)),pi()]),di().catch(()=>{}),n({type:`phone`,phone_number:d.value}).then(e=>{e&&!r&&(a(!1),m(e))}).catch(e=>{console.error(`sign in error:`,e),a(!1),l(q(`Error.AnError`))})}let g;return F(()=>{Ct||(g=jr(p,()=>!r)),f(e.spec.page.fields.find(e=>e.name===`phone_number`)?.value),(e.spec.page.data?.rpcError||e.spec.page.alerts.some(e=>e.level===`error`))&&m(e.spec.page)}),ne(()=>{r=!0,g?.()}),V(Fr,{get header(){return V(Z,{get children(){return[V(Z.Sticker,{get class(){return J.logoContainer},size:120,get element(){return V(Er,{get class(){return J.logo}})}}),V(Z.Title,{tag:`h1`,get children(){return q(`Login.Title`)}}),V(Z.Subtitle,{class:`secondary`,get children(){return q(`Login.StartText`)}}),V(ni,{get alerts(){return e.spec.page.alerts}})]}})},inputWrapper:!1,get children(){return[(()=>{var e=mi();return K(e,()=>u.container,null),K(e,()=>d.container,null),K(e,V(Y,{class:`btn-primary btn-color-primary`,get disabled(){return!o()||i()},onClick:h,get children(){return c()}}),null),e})(),V(Re,{get when(){return e.spec.page.qrEndpoint},get children(){return V(Y,{class:`btn-primary btn-secondary btn-primary-transparent primary`,get disabled(){return i()},onClick:()=>t({name:`signQR`,page:Yr(e.spec.page)}),text:`Login.QR.Login`})}}),V(Y,{class:`btn-primary btn-secondary btn-primary-transparent primary`,get disabled(){return i()},onClick:()=>t({name:`signBot`,page:Yr(e.spec.page)}),text:`Login.Bot`})]}})}var gi=class{constructor(e,t){this.container=e,this.input=t,this.passwordVisible=!1,this.onVisibilityClick=e=>{er(e),this.passwordVisible=!this.passwordVisible,this.toggleVisible.replaceChildren(Vt(this.passwordVisible?`eye2_filled`:`eye1_filled`)),this.input.type=this.passwordVisible?`text`:`password`,this.onVisibilityClickAdditional?.()},t.type=`password`,t.setAttribute(`required`,``),t.name=`notsearch_password`,t.autocomplete=`off`;let n=document.createElement(`input`);n.classList.add(`stealthy`),n.tabIndex=-1,n.setAttribute(`aria-hidden`,`true`),n.type=`password`,t.parentElement.prepend(n),t.parentElement.insertBefore(n.cloneNode(),t.nextSibling);let r=this.toggleVisible=document.createElement(`span`);r.classList.add(`toggle-visible`),r.append(Vt(`eye1_filled`)),e.classList.add(`input-field-password`),e.append(r),r.addEventListener(`click`,this.onVisibilityClick),r.addEventListener(`touchend`,this.onVisibilityClick)}},_i=class extends sr{constructor(e={}){super({plainText:!0,allowStartingSpace:!0,...e}),this.helpers=new gi(this.container,this.input)}},vi=W(`<div class=input-wrapper>`);function yi(e){let{navigate:t,submit:n}=pn(),r=!1,[i,a]=A(!1),[o,s]=A(q(`Login.Next`)),c=new _i({label:`Login.Bot.Token`,name:`token`}),l=c.input;function u(e){e&&er(e);let t=l.value.trim();if(!t.length){l.classList.add(`error`);return}a(!0),l.disabled=!0,s(q(`PleaseWait`)),n({type:`bot`,token:t}).then(e=>{e&&!r&&(a(!1),l.disabled=!1,c.setError($r(e)??`Error.AnError`),s(q(`Login.Next`)),l.select())}).catch(e=>{console.error(`bot login error:`,e),a(!1),l.disabled=!1,s(q(`Error.AnError`))})}l.addEventListener(`keypress`,e=>{if(l.classList.remove(`error`),c.setLabel(),s(q(`Login.Next`)),e.key===`Enter`)return u()});let d;return F(()=>{d=jr(l,()=>!r)}),ne(()=>{r=!0,d?.()}),V(Fr,{get header(){return V(Z,{get children(){return[V(Z.Sticker,{get class(){return J.logoContainer},size:120,get element(){return V(Er,{get class(){return J.logo}})}}),V(Z.Title,{tag:`h1`,get children(){return q(`Login.Bot.Title`)}}),V(Z.Subtitle,{class:`secondary`,get children(){return q(`Login.Bot.Subtitle`)}}),V(ni,{get alerts(){return e.spec.page.alerts}})]}})},inputWrapper:!1,get children(){return[(()=>{var e=vi();return K(e,()=>c.container,null),K(e,V(Y,{class:`btn-primary btn-color-primary`,get disabled(){return i()},onClick:u,get children(){return o()}}),null),e})(),V(Y,{class:`btn-primary btn-secondary btn-primary-transparent primary`,get disabled(){return i()},onClick:()=>t({name:`signIn`,page:Yr(e.spec.page)}),text:`Login.Bot.Cancel`})]}})}function bi(e,t,n=1e3){let r=e?.parentElement;if(!e?.isConnected||!r){t();return}let i=!1,a=()=>{i||(i=!0,o.disconnect(),clearTimeout(s),t())},o=new MutationObserver(()=>{e.isConnected||a()});o.observe(r,{childList:!0});let s=self.setTimeout(a,n)}var xi={get isMobile(){return!!window.matchMedia?.(`(max-width: 600px)`).matches}},Si={outer:`autoHeight_outer`,hasTransition:`autoHeight_hasTransition`,overflowHidden:`autoHeight_overflowHidden`},Ci=W(`<div><div>`),wi=e=>{let t=Ne({hasTransition:!0},e),n,[r,i]=A(!1),[a,o]=A(0),s=N(()=>r()&&t.hasTransition);return F(()=>{let e=new ResizeObserver(()=>{n.getClientRects().length&&te(()=>{i(!0),o(n.offsetHeight)})});e.observe(n),ne(()=>e.disconnect())}),(()=>{var e=Ci(),r=e.firstChild,i=t.ref;typeof i==`function`?ot(i,e):t.ref=e;var o=n;return typeof o==`function`?ot(o,r):n=r,K(r,()=>t.children),j(n=>{var r=jn(t.outerClass,Si.outer),i={[Si.overflowHidden]:t.overflowHidden,[Si.hasTransition]:s()},o=s()?`${a()}px`:void 0;return r!==n.e&&G(e,n.e=r),n.t=nt(e,i,n.t),o!==n.a&&it(e,`height`,n.a=o),n},{e:void 0,t:void 0,a:void 0}),e})()},Ti=W(`<div role=alert>`),Ei=0;function Di(e){let t=`auth-card-error-`+ ++Ei;return M(()=>{let n=e.describes;n&&(e.content?(n.setAttribute(`aria-invalid`,`true`),n.setAttribute(`aria-describedby`,t)):(n.removeAttribute(`aria-invalid`),n.removeAttribute(`aria-describedby`)))}),V(wi,{overflowHidden:!0,get children(){return V(Re,{get when(){return e.content},get children(){var n=Ti();return Qe(n,`id`,t),K(n,()=>e.content),j(()=>G(n,J.errorLabel)),n}})}})}function Oi(e,t){return{_:`auth.sentCode`,type:{_:e?.type||`auth.sentCodeTypeApp`,length:e?.length??void 0,beginning:e?.beginning??void 0,prefix:e?.prefix??void 0,url:e?.url??void 0,email_pattern:e?.emailPattern??void 0},next_type:e?.nextType?{_:e.nextType}:void 0,timeout:e?.timeout??void 0,phone_number:t}}var ki=5;function Ai(e){return e._===`auth.sentCodeTypeSmsWord`||e._===`auth.sentCodeTypeSmsPhrase`?`text`:`digits`}function ji(e){return e.length||ki}function Mi(e){return e._===`auth.sentCodeTypeSmsWord`||e._===`auth.sentCodeTypeSmsPhrase`?e.beginning:void 0}function Ni(e,t){if(!t)return!0;let n=e.trimStart().toLowerCase(),r=t.toLowerCase(),i=Math.min(n.length,r.length);return i<=0||n.slice(0,i)===r.slice(0,i)}var Pi=W(`<div>`);function Fi(e){let t=document.createElement(`span`);return t.dir=`ltr`,t.textContent=e,t}function Ii(e){let t,n,r=e.type;switch(r._){case`auth.sentCodeTypeSms`:case`auth.sentCodeTypeFirebaseSms`:t=`Login.Code.SentSms`;break;case`auth.sentCodeTypeApp`:t=`Login.Code.SentInApp`;break;case`auth.sentCodeTypeCall`:case`auth.sentCodeTypeFlashCall`:t=`Login.Code.SentCall`;break;case`auth.sentCodeTypeMissedCall`:t=`Login.Code.SentMissedCall`,n=[Fi(r.prefix??``)];break;case`auth.sentCodeTypeSmsWord`:case`auth.sentCodeTypeSmsPhrase`:{let i=r._===`auth.sentCodeTypeSmsWord`,a=Fi(e.phone_number??``);r.beginning?(t=i?`Login.Code.SentSmsWordBeginning`:`Login.Code.SentSmsPhraseBeginning`,n=[a,r.beginning]):(t=i?`Login.Code.SentSmsWord`:`Login.Code.SentSmsPhrase`,n=[a]);break}case`auth.sentCodeTypeFragmentSms`:{t=`PhoneNumber.Code.Fragment.Info`;let e=document.createElement(`a`);e.target=`_blank`,e.rel=`noopener noreferrer`,r.url&&/^https:\/\//.test(r.url)&&(e.href=r.url),n=[e];break}case`auth.sentCodeTypeEmailCode`:t=`Login.Code.SentEmail`,n=[r.email_pattern??``];break;default:t=`Login.Code.SentUnknown`,n=[r._]}return q(t,n)}function Li(e){let{submit:t}=pn(),n=Oi(e.spec.page.data?.sentCode,e.spec.page.data?.phoneNumber),r=Ai(n.type),[i,a]=A(!1),[o,s]=A(),c=document.createElement(`div`),l=xi.isMobile?100:130,u,d=!1,f=document.createElement(`h4`);f.classList.add(J.phone),f.dir=`ltr`,f.textContent=n.phone_number??``;let p=document.createElement(`div`);p.classList.add(J.codeInputHost);let m,h,g=()=>m?.input??h?.input;function _(){m&&(m.error=!1),h?.setState(ir.Neutral),s(void 0)}function v(e){m&&(m.error=!0),h?.setState(ir.Error),s(e)}r===`digits`?(m=new si({length:ji(n.type),onChange:_,onFill:e=>x(e),class:J.codeInputField}),p.append(m.container)):(h=new sr({plainText:!0,label:n.type._===`auth.sentCodeTypeSmsWord`?`Login.Code.SecretWord`:`Login.Code.SecretPhrase`,name:`sms-word`,onRawInput:_}),h.container.classList.add(J.wordInputField),h.input.addEventListener(`keydown`,e=>{e.key===`Enter`&&(er(e),y())}),p.append(h.container));function y(){let e=h?.value.trim();e&&!i()&&x(e)}function b(e){m&&(m.disabled=e),h?.input.toggleAttribute(`disabled`,e)}function x(e){if(r===`text`&&!Ni(e,Mi(n.type))){v(q(`Login.Code.WordBeginningInvalid`));return}a(!0),b(!0),t({phone_code:e}).then(e=>{if(!e||d)return;let t=e.data?.rpcError;v(r===`text`&&(t===`PHONE_CODE_INVALID`||t===`PHONE_CODE_EMPTY`)?q(`Login.Code.WordInvalid`):Qr(e)??q(`PHONE_CODE_INVALID`)),S()}).catch(e=>{console.error(`code error:`,e),!d&&(v(q(`Error.AnError`)),S())})}function S(){a(!1),b(!1),m&&(m.value=``),zn(()=>g()?.focus())}let C;return F(()=>{u=new fi(m??h,l),c.append(u.container),u.load().catch(()=>{}),C=jr(g(),()=>!d)}),ne(()=>{d=!0,C?.(),m?.cleanup();let e=u;u=void 0,bi(c.closest(`.`+J.card),()=>e?.remove())}),di().catch(()=>{}),V(Fr,{get class(){return J.pageAuthCode},get header(){return V(Z,{get children(){return[V(Z.Sticker,{element:c,size:l}),V(Z.Title,{get children(){var e=Pi();return K(e,f),j(()=>G(e,J.phoneWrapper)),e}}),V(Z.Subtitle,{class:`secondary`,get children(){return Ii(n)}}),V(ni,{get alerts(){return e.spec.page.alerts}})]}})},inputWrapper:!1,get children(){return[p,V(Di,{get content(){return o()},get describes(){return g()}}),V(Re,{when:r===`text`,get children(){return V(Y,{get class(){return jn(`btn-primary btn-color-primary`,J.wordSubmit)},get disabled(){return i()},onClick:y,text:`Login.Next`})}})]}})}var Ri=class{constructor(e,t){this.passwordInputField=e,this.size=t,this.needFrame=0,this.container=document.createElement(`div`),this.container.classList.add(`media-sticker-wrapper`)}load(){return this.loadPromise?this.loadPromise:this.loadPromise=Ft.loadAnimationAsAsset({container:this.container,loop:!1,autoplay:!1,width:this.size,height:this.size,noCache:!0},`TwoFactorSetupMonkeyPeek`).then(e=>(this.animation=e,this.animation.addEventListener(`enterFrame`,e=>{(this.animation.direction===1&&e>=this.needFrame||this.animation.direction===-1&&e<=this.needFrame)&&(this.animation.setSpeed(1),this.animation.pause())}),this.passwordInputField.helpers.onVisibilityClickAdditional=()=>{this.passwordInputField.helpers.passwordVisible?(this.animation.setDirection(1),this.animation.curFrame=0,this.needFrame=16,this.animation.play()):(this.animation.setDirection(-1),this.animation.curFrame=16,this.needFrame=0,this.animation.play())},Ft.waitForFirstFrame(e)))}remove(){this.animation&&this.animation.remove()}},zi=W(`<svg xmlns=http://www.w3.org/2000/svg class=preloader-circular viewBox="25 25 50 50"><circle class=preloader-path cx=50 cy=50 r=20 fill=none stroke-miterlimit=10>`);function Bi(e){let{submit:t}=pn(),n,r=document.createElement(`div`),i=xi.isMobile?100:130,a=!1,[o,s]=A(!1),[c,l]=A(q(`Login.Next`)),u=e.spec.page.data?.hint,d=new _i({label:`LoginPassword`,name:`password`}),f=d.input;function p(){if(u){let e=document.createElement(`span`);e.dir=`auto`,e.textContent=u,d.label.replaceChildren(e),d.label.style.visibility=`visible`}else d.setLabel()}p();function m(e){if(e&&er(e),!f.value.length){f.classList.add(`error`);return}s(!0),f.disabled=!0,l(q(`PleaseWait`));let n=f.value;d.setValueSilently(``+Math.random()),d.setValueSilently(n),t({password:n}).then(e=>{e&&!a&&(s(!1),f.disabled=!1,f.classList.add(`error`),l($r(e)??q(`PASSWORD_HASH_INVALID`)),f.select())}).catch(e=>{console.error(`password error:`,e),!a&&(s(!1),f.disabled=!1,l(q(`Error.AnError`)))})}f.addEventListener(`keypress`,function(e){if(this.classList.remove(`error`),l(q(`Login.Next`)),e.key===`Enter`)return m()});let h;return F(()=>{n=new Ri(d,i),r.append(n.container),n.load().catch(()=>{}),h=jr(f,()=>!a)}),ne(()=>{a=!0,h?.();let e=n;n=void 0,bi(r.closest(`.`+J.card),()=>e?.remove())}),V(Fr,{get class(){return J.pagePassword},get header(){return V(Z,{get children(){return[V(Z.Sticker,{element:r,size:i}),V(Z.Title,{tag:`h1`,get children(){return q(`Login.Password.Title`)}}),V(Z.Subtitle,{get children(){return q(`Login.Password.Subtitle`)}}),V(ni,{get alerts(){return e.spec.page.alerts}})]}})},get children(){return[U(()=>d.container),V(Y,{class:`btn-primary btn-color-primary`,get disabled(){return o()},onClick:m,get children(){return[U(()=>c()),U(()=>U(()=>!!o())()&&zi())]}})]}})}var Vi=W(`<div aria-hidden=true>`),Hi=W(`<svg xmlns=http://www.w3.org/2000/svg class=preloader-circular viewBox="25 25 50 50"><circle class=preloader-path cx=50 cy=50 r=20 fill=none stroke-miterlimit=10>`);function Ui(e,t){let n=e=>Array.from(e.trim())[0]??``;return(n(e)+n(t)).toUpperCase()}function Wi(e){let{submit:t}=pn(),[n,r]=A(!1),[i,a]=A(q(`StartMessaging`)),[o,s]=A(``),c=new sr({label:`FirstName`,maxLength:70,name:`first_name`,autocomplete:`given-name`}),l=new sr({label:`LastName`,maxLength:64,name:`last_name`,autocomplete:`family-name`}),[u,d]=A(q(`YourName`));function f(){let e=c.value||``,t=l.value||``,n=e||t?(e+` `+t).trim():``;d(n||q(`YourName`)),s(Ui(e,t)),a(q(`StartMessaging`))}c.input.addEventListener(`input`,f),l.input.addEventListener(`input`,f);function p(){if(!(c.input.classList.contains(`error`)||l.input.classList.contains(`error`))){if(!c.value.trim().length){c.input.classList.add(`error`);return}r(!0),a(q(`PleaseWait`)),t({first_name:c.value.trim(),last_name:l.value.trim()}).then(e=>{e&&(r(!1),a($r(e)??q(`Error.AnError`)))}).catch(e=>{console.error(`sign up error:`,e),r(!1),a(q(`Error.AnError`))})}}for(let e of[c,l])e.input.addEventListener(`keypress`,e=>{e.key===`Enter`&&p()});return F(()=>{let t=e.spec.page.fields.find(e=>e.name===`first_name`)?.value,n=e.spec.page.fields.find(e=>e.name===`last_name`)?.value;t&&(c.value=t),n&&(l.value=n),c.input.focus()}),V(Fr,{get class(){return J.pageSignUp},get header(){return V(Z,{get children(){return[V(Z.Sticker,{size:120,get element(){return(()=>{var e=Vi();return K(e,o),j(()=>G(e,J.avatar)),e})()}}),V(Z.Title,{tag:`h1`,get children(){return u()}}),V(Z.Subtitle,{get children(){return q(`Login.Register.Subtitle`)}}),V(ni,{get alerts(){return e.spec.page.alerts}})]}})},get children(){return[U(()=>c.container),U(()=>l.container),V(Y,{class:`btn-primary btn-color-primary`,get disabled(){return n()},onClick:p,get children(){return[U(()=>i()),U(()=>U(()=>!!n())()&&Hi())]}})]}})}var Gi=l(s(((e,t)=>{(function(n,r){typeof e==`object`&&typeof t==`object`?t.exports=r():typeof define==`function`&&define.amd?define([],r):typeof e==`object`?e.QRCodeStyling=r():n.QRCodeStyling=r()})(e,(()=>(()=>{var e={873:(e,t)=>{var n,r,i=function(){var e=function(e,t){var n=e,r=o[t],i=null,a=0,c=null,g=[],_={},y=function(e,t){i=function(e){for(var t=Array(e),n=0;n<e;n+=1){t[n]=Array(e);for(var r=0;r<e;r+=1)t[n][r]=null}return t}(a=4*n+17),b(0,0),b(a-7,0),b(0,a-7),S(),x(),w(e,t),n>=7&&C(e),c??(c=E(n,r,g)),T(c,t)},b=function(e,t){for(var n=-1;n<=7;n+=1)if(!(e+n<=-1||a<=e+n))for(var r=-1;r<=7;r+=1)t+r<=-1||a<=t+r||(i[e+n][t+r]=0<=n&&n<=6&&(r==0||r==6)||0<=r&&r<=6&&(n==0||n==6)||2<=n&&n<=4&&2<=r&&r<=4)},x=function(){for(var e=8;e<a-8;e+=1)i[e][6]??(i[e][6]=e%2==0);for(var t=8;t<a-8;t+=1)i[6][t]??(i[6][t]=t%2==0)},S=function(){for(var e=s.getPatternPosition(n),t=0;t<e.length;t+=1)for(var r=0;r<e.length;r+=1){var a=e[t],o=e[r];if(i[a][o]==null)for(var c=-2;c<=2;c+=1)for(var l=-2;l<=2;l+=1)i[a+c][o+l]=c==-2||c==2||l==-2||l==2||c==0&&l==0}},C=function(e){for(var t=s.getBCHTypeNumber(n),r=0;r<18;r+=1){var o=!e&&(t>>r&1)==1;i[Math.floor(r/3)][r%3+a-8-3]=o}for(r=0;r<18;r+=1)o=!e&&(t>>r&1)==1,i[r%3+a-8-3][Math.floor(r/3)]=o},w=function(e,t){for(var n=r<<3|t,o=s.getBCHTypeInfo(n),c=0;c<15;c+=1){var l=!e&&(o>>c&1)==1;c<6?i[c][8]=l:c<8?i[c+1][8]=l:i[a-15+c][8]=l}for(c=0;c<15;c+=1)l=!e&&(o>>c&1)==1,c<8?i[8][a-c-1]=l:c<9?i[8][15-c-1+1]=l:i[8][15-c-1]=l;i[a-8][8]=!e},T=function(e,t){for(var n=-1,r=a-1,o=7,c=0,l=s.getMaskFunction(t),u=a-1;u>0;u-=2)for(u==6&&--u;;){for(var d=0;d<2;d+=1)if(i[r][u-d]==null){var f=!1;c<e.length&&(f=(e[c]>>>o&1)==1),l(r,u-d)&&(f=!f),i[r][u-d]=f,--o==-1&&(c+=1,o=7)}if((r+=n)<0||a<=r){r-=n,n=-n;break}}},E=function(e,t,n){for(var r=u.getRSBlocks(e,t),i=d(),a=0;a<n.length;a+=1){var o=n[a];i.put(o.getMode(),4),i.put(o.getLength(),s.getLengthInBits(o.getMode(),e)),o.write(i)}var c=0;for(a=0;a<r.length;a+=1)c+=r[a].dataCount;if(i.getLengthInBits()>8*c)throw`code length overflow. (`+i.getLengthInBits()+`>`+8*c+`)`;for(i.getLengthInBits()+4<=8*c&&i.put(0,4);i.getLengthInBits()%8!=0;)i.putBit(!1);for(;!(i.getLengthInBits()>=8*c||(i.put(236,8),i.getLengthInBits()>=8*c));)i.put(17,8);return function(e,t){for(var n=0,r=0,i=0,a=Array(t.length),o=Array(t.length),c=0;c<t.length;c+=1){var u=t[c].dataCount,d=t[c].totalCount-u;r=Math.max(r,u),i=Math.max(i,d),a[c]=Array(u);for(var f=0;f<a[c].length;f+=1)a[c][f]=255&e.getBuffer()[f+n];n+=u;var p=s.getErrorCorrectPolynomial(d),m=l(a[c],p.getLength()-1).mod(p);for(o[c]=Array(p.getLength()-1),f=0;f<o[c].length;f+=1){var h=f+m.getLength()-o[c].length;o[c][f]=h>=0?m.getAt(h):0}}var g=0;for(f=0;f<t.length;f+=1)g+=t[f].totalCount;var _=Array(g),v=0;for(f=0;f<r;f+=1)for(c=0;c<t.length;c+=1)f<a[c].length&&(_[v]=a[c][f],v+=1);for(f=0;f<i;f+=1)for(c=0;c<t.length;c+=1)f<o[c].length&&(_[v]=o[c][f],v+=1);return _}(i,r)};_.addData=function(e,t){var n=null;switch(t=t||`Byte`){case`Numeric`:n=f(e);break;case`Alphanumeric`:n=p(e);break;case`Byte`:n=m(e);break;case`Kanji`:n=h(e);break;default:throw`mode:`+t}g.push(n),c=null},_.isDark=function(e,t){if(e<0||a<=e||t<0||a<=t)throw e+`,`+t;return i[e][t]},_.getModuleCount=function(){return a},_.make=function(){if(n<1){for(var e=1;e<40;e++){for(var t=u.getRSBlocks(e,r),i=d(),a=0;a<g.length;a++){var o=g[a];i.put(o.getMode(),4),i.put(o.getLength(),s.getLengthInBits(o.getMode(),e)),o.write(i)}var c=0;for(a=0;a<t.length;a++)c+=t[a].dataCount;if(i.getLengthInBits()<=8*c)break}n=e}y(!1,function(){for(var e=0,t=0,n=0;n<8;n+=1){y(!0,n);var r=s.getLostPoint(_);(n==0||e>r)&&(e=r,t=n)}return t}())},_.createTableTag=function(e,t){e=e||2;var n=``;n+=`<table style="`,n+=` border-width: 0px; border-style: none;`,n+=` border-collapse: collapse;`,n+=` padding: 0px; margin: `+(t=t===void 0?4*e:t)+`px;`,n+=`">`,n+=`<tbody>`;for(var r=0;r<_.getModuleCount();r+=1){n+=`<tr>`;for(var i=0;i<_.getModuleCount();i+=1)n+=`<td style="`,n+=` border-width: 0px; border-style: none;`,n+=` border-collapse: collapse;`,n+=` padding: 0px; margin: 0px;`,n+=` width: `+e+`px;`,n+=` height: `+e+`px;`,n+=` background-color: `,n+=_.isDark(r,i)?`#000000`:`#ffffff`,n+=`;`,n+=`"/>`;n+=`</tr>`}return(n+=`</tbody>`)+`</table>`},_.createSvgTag=function(e,t,n,r){var i={};typeof arguments[0]==`object`&&(e=(i=arguments[0]).cellSize,t=i.margin,n=i.alt,r=i.title),e=e||2,t=t===void 0?4*e:t,(n=typeof n==`string`?{text:n}:n||{}).text=n.text||null,n.id=n.text?n.id||`qrcode-description`:null,(r=typeof r==`string`?{text:r}:r||{}).text=r.text||null,r.id=r.text?r.id||`qrcode-title`:null;var a,o,s,c,l=_.getModuleCount()*e+2*t,u=``;for(c=`l`+e+`,0 0,`+e+` -`+e+`,0 0,-`+e+`z `,u+=`<svg version="1.1" xmlns="http://www.w3.org/2000/svg"`,u+=i.scalable?``:` width="`+l+`px" height="`+l+`px"`,u+=` viewBox="0 0 `+l+` `+l+`" `,u+=` preserveAspectRatio="xMinYMin meet"`,u+=r.text||n.text?` role="img" aria-labelledby="`+D([r.id,n.id].join(` `).trim())+`"`:``,u+=`>`,u+=r.text?`<title id="`+D(r.id)+`">`+D(r.text)+`</title>`:``,u+=n.text?`<description id="`+D(n.id)+`">`+D(n.text)+`</description>`:``,u+=`<rect width="100%" height="100%" fill="white" cx="0" cy="0"/>`,u+=`<path d="`,o=0;o<_.getModuleCount();o+=1)for(s=o*e+t,a=0;a<_.getModuleCount();a+=1)_.isDark(o,a)&&(u+=`M`+(a*e+t)+`,`+s+c);return(u+=`" stroke="transparent" fill="black"/>`)+`</svg>`},_.createDataURL=function(e,t){e=e||2,t=t===void 0?4*e:t;var n=_.getModuleCount()*e+2*t,r=t,i=n-t;return v(n,n,(function(t,n){if(r<=t&&t<i&&r<=n&&n<i){var a=Math.floor((t-r)/e),o=Math.floor((n-r)/e);return+!_.isDark(o,a)}return 1}))},_.createImgTag=function(e,t,n){e=e||2,t=t===void 0?4*e:t;var r=_.getModuleCount()*e+2*t,i=``;return i+=`<img`,i+=` src="`,i+=_.createDataURL(e,t),i+=`"`,i+=` width="`,i+=r,i+=`"`,i+=` height="`,i+=r,i+=`"`,n&&(i+=` alt="`,i+=D(n),i+=`"`),i+`/>`};var D=function(e){for(var t=``,n=0;n<e.length;n+=1){var r=e.charAt(n);switch(r){case`<`:t+=`&lt;`;break;case`>`:t+=`&gt;`;break;case`&`:t+=`&amp;`;break;case`"`:t+=`&quot;`;break;default:t+=r}}return t};return _.createASCII=function(e,t){if((e=e||1)<2)return function(e){e=e===void 0?2:e;var t,n,r,i,a,o=1*_.getModuleCount()+2*e,s=e,c=o-e,l={"â–ˆâ–ˆ":`â–ˆ`,"â–ˆ ":`â–€`," â–ˆ":`â–„`,"  ":` `},u={"â–ˆâ–ˆ":`â–€`,"â–ˆ ":`â–€`," â–ˆ":` `,"  ":` `},d=``;for(t=0;t<o;t+=2){for(r=Math.floor((t-s)/1),i=Math.floor((t+1-s)/1),n=0;n<o;n+=1)a=`â–ˆ`,s<=n&&n<c&&s<=t&&t<c&&_.isDark(r,Math.floor((n-s)/1))&&(a=` `),s<=n&&n<c&&s<=t+1&&t+1<c&&_.isDark(i,Math.floor((n-s)/1))?a+=` `:a+=`â–ˆ`,d+=e<1&&t+1>=c?u[a]:l[a];d+=`
`}return o%2&&e>0?d.substring(0,d.length-o-1)+Array(o+1).join(`â–€`):d.substring(0,d.length-1)}(t);--e,t=t===void 0?2*e:t;var n,r,i,a,o=_.getModuleCount()*e+2*t,s=t,c=o-t,l=Array(e+1).join(`â–ˆâ–ˆ`),u=Array(e+1).join(`  `),d=``,f=``;for(n=0;n<o;n+=1){for(i=Math.floor((n-s)/e),f=``,r=0;r<o;r+=1)a=1,s<=r&&r<c&&s<=n&&n<c&&_.isDark(i,Math.floor((r-s)/e))&&(a=0),f+=a?l:u;for(i=0;i<e;i+=1)d+=f+`
`}return d.substring(0,d.length-1)},_.renderTo2dContext=function(e,t){t=t||2;for(var n=_.getModuleCount(),r=0;r<n;r++)for(var i=0;i<n;i++)e.fillStyle=_.isDark(r,i)?`black`:`white`,e.fillRect(r*t,i*t,t,t)},_};e.stringToBytes=(e.stringToBytesFuncs={default:function(e){for(var t=[],n=0;n<e.length;n+=1){var r=e.charCodeAt(n);t.push(255&r)}return t}}).default,e.createStringToBytes=function(e,t){var n=function(){for(var n=_(e),r=function(){var e=n.read();if(e==-1)throw`eof`;return e},i=0,a={};;){var o=n.read();if(o==-1)break;var s=r(),c=r()<<8|r();a[String.fromCharCode(o<<8|s)]=c,i+=1}if(i!=t)throw i+` != `+t;return a}(),r=63;return function(e){for(var t=[],i=0;i<e.length;i+=1){var a=e.charCodeAt(i);if(a<128)t.push(a);else{var o=n[e.charAt(i)];typeof o==`number`?(255&o)==o?t.push(o):(t.push(o>>>8),t.push(255&o)):t.push(r)}}return t}};var t,n,r,i,a,o={L:1,M:0,Q:3,H:2},s=(t=[[],[6,18],[6,22],[6,26],[6,30],[6,34],[6,22,38],[6,24,42],[6,26,46],[6,28,50],[6,30,54],[6,32,58],[6,34,62],[6,26,46,66],[6,26,48,70],[6,26,50,74],[6,30,54,78],[6,30,56,82],[6,30,58,86],[6,34,62,90],[6,28,50,72,94],[6,26,50,74,98],[6,30,54,78,102],[6,28,54,80,106],[6,32,58,84,110],[6,30,58,86,114],[6,34,62,90,118],[6,26,50,74,98,122],[6,30,54,78,102,126],[6,26,52,78,104,130],[6,30,56,82,108,134],[6,34,60,86,112,138],[6,30,58,86,114,142],[6,34,62,90,118,146],[6,30,54,78,102,126,150],[6,24,50,76,102,128,154],[6,28,54,80,106,132,158],[6,32,58,84,110,136,162],[6,26,54,82,110,138,166],[6,30,58,86,114,142,170]],n=1335,r=7973,a=function(e){for(var t=0;e!=0;)t+=1,e>>>=1;return t},(i={}).getBCHTypeInfo=function(e){for(var t=e<<10;a(t)-a(n)>=0;)t^=n<<a(t)-a(n);return 21522^(e<<10|t)},i.getBCHTypeNumber=function(e){for(var t=e<<12;a(t)-a(r)>=0;)t^=r<<a(t)-a(r);return e<<12|t},i.getPatternPosition=function(e){return t[e-1]},i.getMaskFunction=function(e){switch(e){case 0:return function(e,t){return(e+t)%2==0};case 1:return function(e,t){return e%2==0};case 2:return function(e,t){return t%3==0};case 3:return function(e,t){return(e+t)%3==0};case 4:return function(e,t){return(Math.floor(e/2)+Math.floor(t/3))%2==0};case 5:return function(e,t){return e*t%2+e*t%3==0};case 6:return function(e,t){return(e*t%2+e*t%3)%2==0};case 7:return function(e,t){return(e*t%3+(e+t)%2)%2==0};default:throw`bad maskPattern:`+e}},i.getErrorCorrectPolynomial=function(e){for(var t=l([1],0),n=0;n<e;n+=1)t=t.multiply(l([1,c.gexp(n)],0));return t},i.getLengthInBits=function(e,t){if(1<=t&&t<10)switch(e){case 1:return 10;case 2:return 9;case 4:case 8:return 8;default:throw`mode:`+e}else if(t<27)switch(e){case 1:return 12;case 2:return 11;case 4:return 16;case 8:return 10;default:throw`mode:`+e}else{if(!(t<41))throw`type:`+t;switch(e){case 1:return 14;case 2:return 13;case 4:return 16;case 8:return 12;default:throw`mode:`+e}}},i.getLostPoint=function(e){for(var t=e.getModuleCount(),n=0,r=0;r<t;r+=1)for(var i=0;i<t;i+=1){for(var a=0,o=e.isDark(r,i),s=-1;s<=1;s+=1)if(!(r+s<0||t<=r+s))for(var c=-1;c<=1;c+=1)i+c<0||t<=i+c||s==0&&c==0||o==e.isDark(r+s,i+c)&&(a+=1);a>5&&(n+=3+a-5)}for(r=0;r<t-1;r+=1)for(i=0;i<t-1;i+=1){var l=0;e.isDark(r,i)&&(l+=1),e.isDark(r+1,i)&&(l+=1),e.isDark(r,i+1)&&(l+=1),e.isDark(r+1,i+1)&&(l+=1),l!=0&&l!=4||(n+=3)}for(r=0;r<t;r+=1)for(i=0;i<t-6;i+=1)e.isDark(r,i)&&!e.isDark(r,i+1)&&e.isDark(r,i+2)&&e.isDark(r,i+3)&&e.isDark(r,i+4)&&!e.isDark(r,i+5)&&e.isDark(r,i+6)&&(n+=40);for(i=0;i<t;i+=1)for(r=0;r<t-6;r+=1)e.isDark(r,i)&&!e.isDark(r+1,i)&&e.isDark(r+2,i)&&e.isDark(r+3,i)&&e.isDark(r+4,i)&&!e.isDark(r+5,i)&&e.isDark(r+6,i)&&(n+=40);var u=0;for(i=0;i<t;i+=1)for(r=0;r<t;r+=1)e.isDark(r,i)&&(u+=1);return n+Math.abs(100*u/t/t-50)/5*10},i),c=function(){for(var e=Array(256),t=Array(256),n=0;n<8;n+=1)e[n]=1<<n;for(n=8;n<256;n+=1)e[n]=e[n-4]^e[n-5]^e[n-6]^e[n-8];for(n=0;n<255;n+=1)t[e[n]]=n;return{glog:function(e){if(e<1)throw`glog(`+e+`)`;return t[e]},gexp:function(t){for(;t<0;)t+=255;for(;t>=256;)t-=255;return e[t]}}}();function l(e,t){if(e.length===void 0)throw e.length+`/`+t;var n=function(){for(var n=0;n<e.length&&e[n]==0;)n+=1;for(var r=Array(e.length-n+t),i=0;i<e.length-n;i+=1)r[i]=e[i+n];return r}(),r={getAt:function(e){return n[e]},getLength:function(){return n.length},multiply:function(e){for(var t=Array(r.getLength()+e.getLength()-1),n=0;n<r.getLength();n+=1)for(var i=0;i<e.getLength();i+=1)t[n+i]^=c.gexp(c.glog(r.getAt(n))+c.glog(e.getAt(i)));return l(t,0)},mod:function(e){if(r.getLength()-e.getLength()<0)return r;for(var t=c.glog(r.getAt(0))-c.glog(e.getAt(0)),n=Array(r.getLength()),i=0;i<r.getLength();i+=1)n[i]=r.getAt(i);for(i=0;i<e.getLength();i+=1)n[i]^=c.gexp(c.glog(e.getAt(i))+t);return l(n,0).mod(e)}};return r}var u=function(){var e=[[1,26,19],[1,26,16],[1,26,13],[1,26,9],[1,44,34],[1,44,28],[1,44,22],[1,44,16],[1,70,55],[1,70,44],[2,35,17],[2,35,13],[1,100,80],[2,50,32],[2,50,24],[4,25,9],[1,134,108],[2,67,43],[2,33,15,2,34,16],[2,33,11,2,34,12],[2,86,68],[4,43,27],[4,43,19],[4,43,15],[2,98,78],[4,49,31],[2,32,14,4,33,15],[4,39,13,1,40,14],[2,121,97],[2,60,38,2,61,39],[4,40,18,2,41,19],[4,40,14,2,41,15],[2,146,116],[3,58,36,2,59,37],[4,36,16,4,37,17],[4,36,12,4,37,13],[2,86,68,2,87,69],[4,69,43,1,70,44],[6,43,19,2,44,20],[6,43,15,2,44,16],[4,101,81],[1,80,50,4,81,51],[4,50,22,4,51,23],[3,36,12,8,37,13],[2,116,92,2,117,93],[6,58,36,2,59,37],[4,46,20,6,47,21],[7,42,14,4,43,15],[4,133,107],[8,59,37,1,60,38],[8,44,20,4,45,21],[12,33,11,4,34,12],[3,145,115,1,146,116],[4,64,40,5,65,41],[11,36,16,5,37,17],[11,36,12,5,37,13],[5,109,87,1,110,88],[5,65,41,5,66,42],[5,54,24,7,55,25],[11,36,12,7,37,13],[5,122,98,1,123,99],[7,73,45,3,74,46],[15,43,19,2,44,20],[3,45,15,13,46,16],[1,135,107,5,136,108],[10,74,46,1,75,47],[1,50,22,15,51,23],[2,42,14,17,43,15],[5,150,120,1,151,121],[9,69,43,4,70,44],[17,50,22,1,51,23],[2,42,14,19,43,15],[3,141,113,4,142,114],[3,70,44,11,71,45],[17,47,21,4,48,22],[9,39,13,16,40,14],[3,135,107,5,136,108],[3,67,41,13,68,42],[15,54,24,5,55,25],[15,43,15,10,44,16],[4,144,116,4,145,117],[17,68,42],[17,50,22,6,51,23],[19,46,16,6,47,17],[2,139,111,7,140,112],[17,74,46],[7,54,24,16,55,25],[34,37,13],[4,151,121,5,152,122],[4,75,47,14,76,48],[11,54,24,14,55,25],[16,45,15,14,46,16],[6,147,117,4,148,118],[6,73,45,14,74,46],[11,54,24,16,55,25],[30,46,16,2,47,17],[8,132,106,4,133,107],[8,75,47,13,76,48],[7,54,24,22,55,25],[22,45,15,13,46,16],[10,142,114,2,143,115],[19,74,46,4,75,47],[28,50,22,6,51,23],[33,46,16,4,47,17],[8,152,122,4,153,123],[22,73,45,3,74,46],[8,53,23,26,54,24],[12,45,15,28,46,16],[3,147,117,10,148,118],[3,73,45,23,74,46],[4,54,24,31,55,25],[11,45,15,31,46,16],[7,146,116,7,147,117],[21,73,45,7,74,46],[1,53,23,37,54,24],[19,45,15,26,46,16],[5,145,115,10,146,116],[19,75,47,10,76,48],[15,54,24,25,55,25],[23,45,15,25,46,16],[13,145,115,3,146,116],[2,74,46,29,75,47],[42,54,24,1,55,25],[23,45,15,28,46,16],[17,145,115],[10,74,46,23,75,47],[10,54,24,35,55,25],[19,45,15,35,46,16],[17,145,115,1,146,116],[14,74,46,21,75,47],[29,54,24,19,55,25],[11,45,15,46,46,16],[13,145,115,6,146,116],[14,74,46,23,75,47],[44,54,24,7,55,25],[59,46,16,1,47,17],[12,151,121,7,152,122],[12,75,47,26,76,48],[39,54,24,14,55,25],[22,45,15,41,46,16],[6,151,121,14,152,122],[6,75,47,34,76,48],[46,54,24,10,55,25],[2,45,15,64,46,16],[17,152,122,4,153,123],[29,74,46,14,75,47],[49,54,24,10,55,25],[24,45,15,46,46,16],[4,152,122,18,153,123],[13,74,46,32,75,47],[48,54,24,14,55,25],[42,45,15,32,46,16],[20,147,117,4,148,118],[40,75,47,7,76,48],[43,54,24,22,55,25],[10,45,15,67,46,16],[19,148,118,6,149,119],[18,75,47,31,76,48],[34,54,24,34,55,25],[20,45,15,61,46,16]],t=function(e,t){var n={};return n.totalCount=e,n.dataCount=t,n};return{getRSBlocks:function(n,r){var i=function(t,n){switch(n){case o.L:return e[4*(t-1)+0];case o.M:return e[4*(t-1)+1];case o.Q:return e[4*(t-1)+2];case o.H:return e[4*(t-1)+3];default:return}}(n,r);if(i===void 0)throw`bad rs block @ typeNumber:`+n+`/errorCorrectionLevel:`+r;for(var a=i.length/3,s=[],c=0;c<a;c+=1)for(var l=i[3*c+0],u=i[3*c+1],d=i[3*c+2],f=0;f<l;f+=1)s.push(t(u,d));return s}}}(),d=function(){var e=[],t=0,n={getBuffer:function(){return e},getAt:function(t){return(e[Math.floor(t/8)]>>>7-t%8&1)==1},put:function(e,t){for(var r=0;r<t;r+=1)n.putBit((e>>>t-r-1&1)==1)},getLengthInBits:function(){return t},putBit:function(n){var r=Math.floor(t/8);e.length<=r&&e.push(0),n&&(e[r]|=128>>>t%8),t+=1}};return n},f=function(e){var t=e,n={getMode:function(){return 1},getLength:function(e){return t.length},write:function(e){for(var n=t,i=0;i+2<n.length;)e.put(r(n.substring(i,i+3)),10),i+=3;i<n.length&&(n.length-i==1?e.put(r(n.substring(i,i+1)),4):n.length-i==2&&e.put(r(n.substring(i,i+2)),7))}},r=function(e){for(var t=0,n=0;n<e.length;n+=1)t=10*t+i(e.charAt(n));return t},i=function(e){if(`0`<=e&&e<=`9`)return e.charCodeAt(0)-48;throw`illegal char :`+e};return n},p=function(e){var t=e,n={getMode:function(){return 2},getLength:function(e){return t.length},write:function(e){for(var n=t,i=0;i+1<n.length;)e.put(45*r(n.charAt(i))+r(n.charAt(i+1)),11),i+=2;i<n.length&&e.put(r(n.charAt(i)),6)}},r=function(e){if(`0`<=e&&e<=`9`)return e.charCodeAt(0)-48;if(`A`<=e&&e<=`Z`)return e.charCodeAt(0)-65+10;switch(e){case` `:return 36;case`$`:return 37;case`%`:return 38;case`*`:return 39;case`+`:return 40;case`-`:return 41;case`.`:return 42;case`/`:return 43;case`:`:return 44;default:throw`illegal char :`+e}};return n},m=function(t){var n=e.stringToBytes(t);return{getMode:function(){return 4},getLength:function(e){return n.length},write:function(e){for(var t=0;t<n.length;t+=1)e.put(n[t],8)}}},h=function(t){var n=e.stringToBytesFuncs.SJIS;if(!n)throw`sjis not supported.`;(function(){var e=n(`å�‹`);if(e.length!=2||(e[0]<<8|e[1])!=38726)throw`sjis not supported.`})();var r=n(t);return{getMode:function(){return 8},getLength:function(e){return~~(r.length/2)},write:function(e){for(var t=r,n=0;n+1<t.length;){var i=(255&t[n])<<8|255&t[n+1];if(33088<=i&&i<=40956)i-=33088;else{if(!(57408<=i&&i<=60351))throw`illegal char at `+(n+1)+`/`+i;i-=49472}i=192*(i>>>8&255)+(255&i),e.put(i,13),n+=2}if(n<t.length)throw`illegal char at `+(n+1)}}},g=function(){var e=[],t={writeByte:function(t){e.push(255&t)},writeShort:function(e){t.writeByte(e),t.writeByte(e>>>8)},writeBytes:function(e,n,r){n=n||0,r=r||e.length;for(var i=0;i<r;i+=1)t.writeByte(e[i+n])},writeString:function(e){for(var n=0;n<e.length;n+=1)t.writeByte(e.charCodeAt(n))},toByteArray:function(){return e},toString:function(){var t=``;t+=`[`;for(var n=0;n<e.length;n+=1)n>0&&(t+=`,`),t+=e[n];return t+`]`}};return t},_=function(e){var t=e,n=0,r=0,i=0,a={read:function(){for(;i<8;){if(n>=t.length){if(i==0)return-1;throw`unexpected end of file./`+i}var e=t.charAt(n);if(n+=1,e==`=`)return i=0,-1;e.match(/^\s$/)||(r=r<<6|o(e.charCodeAt(0)),i+=6)}var a=r>>>i-8&255;return i-=8,a}},o=function(e){if(65<=e&&e<=90)return e-65;if(97<=e&&e<=122)return e-97+26;if(48<=e&&e<=57)return e-48+52;if(e==43)return 62;if(e==47)return 63;throw`c:`+e};return a},v=function(e,t,n){for(var r=function(e,t){var n=e,r=t,i=Array(e*t),a={setPixel:function(e,t,r){i[t*n+e]=r},write:function(e){e.writeString(`GIF87a`),e.writeShort(n),e.writeShort(r),e.writeByte(128),e.writeByte(0),e.writeByte(0),e.writeByte(0),e.writeByte(0),e.writeByte(0),e.writeByte(255),e.writeByte(255),e.writeByte(255),e.writeString(`,`),e.writeShort(0),e.writeShort(0),e.writeShort(n),e.writeShort(r),e.writeByte(0);var t=o(2);e.writeByte(2);for(var i=0;t.length-i>255;)e.writeByte(255),e.writeBytes(t,i,255),i+=255;e.writeByte(t.length-i),e.writeBytes(t,i,t.length-i),e.writeByte(0),e.writeString(`;`)}},o=function(e){for(var t=1<<e,n=1+(1<<e),r=e+1,a=s(),o=0;o<t;o+=1)a.add(String.fromCharCode(o));a.add(String.fromCharCode(t)),a.add(String.fromCharCode(n));var c,l,u,d=g(),f=(c=d,l=0,u=0,{write:function(e,t){if(e>>>t)throw`length over`;for(;l+t>=8;)c.writeByte(255&(e<<l|u)),t-=8-l,e>>>=8-l,u=0,l=0;u|=e<<l,l+=t},flush:function(){l>0&&c.writeByte(u)}});f.write(t,r);var p=0,m=String.fromCharCode(i[p]);for(p+=1;p<i.length;){var h=String.fromCharCode(i[p]);p+=1,a.contains(m+h)?m+=h:(f.write(a.indexOf(m),r),a.size()<4095&&(a.size()==1<<r&&(r+=1),a.add(m+h)),m=h)}return f.write(a.indexOf(m),r),f.write(n,r),f.flush(),d.toByteArray()},s=function(){var e={},t=0,n={add:function(r){if(n.contains(r))throw`dup key:`+r;e[r]=t,t+=1},size:function(){return t},indexOf:function(t){return e[t]},contains:function(t){return e[t]!==void 0}};return n};return a}(e,t),i=0;i<t;i+=1)for(var a=0;a<e;a+=1)r.setPixel(a,i,n(a,i));var o=g();r.write(o);for(var s=function(){var e=0,t=0,n=0,r=``,i={},a=function(e){r+=String.fromCharCode(o(63&e))},o=function(e){if(!(e<0)){if(e<26)return 65+e;if(e<52)return e-26+97;if(e<62)return e-52+48;if(e==62)return 43;if(e==63)return 47}throw`n:`+e};return i.writeByte=function(r){for(e=e<<8|255&r,t+=8,n+=1;t>=6;)a(e>>>t-6),t-=6},i.flush=function(){if(t>0&&(a(e<<6-t),e=0,t=0),n%3!=0)for(var i=3-n%3,o=0;o<i;o+=1)r+=`=`},i.toString=function(){return r},i}(),c=o.toByteArray(),l=0;l<c.length;l+=1)s.writeByte(c[l]);return s.flush(),`data:image/gif;base64,`+s};return e}();i.stringToBytesFuncs[`UTF-8`]=function(e){return function(e){for(var t=[],n=0;n<e.length;n++){var r=e.charCodeAt(n);r<128?t.push(r):r<2048?t.push(192|r>>6,128|63&r):r<55296||r>=57344?t.push(224|r>>12,128|r>>6&63,128|63&r):(n++,r=65536+((1023&r)<<10|1023&e.charCodeAt(n)),t.push(240|r>>18,128|r>>12&63,128|r>>6&63,128|63&r))}return t}(e)},(r=typeof(n=function(){return i})==`function`?n.apply(t,[]):n)===void 0||(e.exports=r)}},t={};function n(r){var i=t[r];if(i!==void 0)return i.exports;var a=t[r]={exports:{}};return e[r](a,a.exports,n),a.exports}n.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return n.d(t,{a:t}),t},n.d=(e,t)=>{for(var r in t)n.o(t,r)&&!n.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);var r={};return(()=>{"use strict";n.d(r,{default:()=>D});let e=e=>!!e&&typeof e==`object`&&!Array.isArray(e);function t(n,...r){if(!r.length)return n;let i=r.shift();return i!==void 0&&e(n)&&e(i)?(n=Object.assign({},n),Object.keys(i).forEach((r=>{let a=n[r],o=i[r];Array.isArray(a)&&Array.isArray(o)?n[r]=o:e(a)&&e(o)?n[r]=t(Object.assign({},a),o):n[r]=o})),t(n,...r)):n}function i(e,t){let n=document.createElement(`a`);n.download=t,n.href=e,document.body.appendChild(n),n.click(),document.body.removeChild(n)}let a={L:.07,M:.15,Q:.25,H:.3};class o{constructor({svg:e,type:t,window:n}){this._svg=e,this._type=t,this._window=n}draw(e,t,n,r){let i;switch(this._type){case`dots`:i=this._drawDot;break;case`classy`:i=this._drawClassy;break;case`classy-rounded`:i=this._drawClassyRounded;break;case`rounded`:i=this._drawRounded;break;case`extra-rounded`:i=this._drawExtraRounded;break;default:i=this._drawSquare}i.call(this,{x:e,y:t,size:n,getNeighbor:r})}_rotateFigure({x:e,y:t,size:n,rotation:r=0,draw:i}){var a;let o=e+n/2,s=t+n/2;i(),(a=this._element)==null||a.setAttribute(`transform`,`rotate(${180*r/Math.PI},${o},${s})`)}_basicDot(e){let{size:t,x:n,y:r}=e;this._rotateFigure(Object.assign(Object.assign({},e),{draw:()=>{this._element=this._window.document.createElementNS(`http://www.w3.org/2000/svg`,`circle`),this._element.setAttribute(`cx`,String(n+t/2)),this._element.setAttribute(`cy`,String(r+t/2)),this._element.setAttribute(`r`,String(t/2))}}))}_basicSquare(e){let{size:t,x:n,y:r}=e;this._rotateFigure(Object.assign(Object.assign({},e),{draw:()=>{this._element=this._window.document.createElementNS(`http://www.w3.org/2000/svg`,`rect`),this._element.setAttribute(`x`,String(n)),this._element.setAttribute(`y`,String(r)),this._element.setAttribute(`width`,String(t)),this._element.setAttribute(`height`,String(t))}}))}_basicSideRounded(e){let{size:t,x:n,y:r}=e;this._rotateFigure(Object.assign(Object.assign({},e),{draw:()=>{this._element=this._window.document.createElementNS(`http://www.w3.org/2000/svg`,`path`),this._element.setAttribute(`d`,`M ${n} ${r}v ${t}h `+t/2+`a ${t/2} ${t/2}, 0, 0, 0, 0 ${-t}`)}}))}_basicCornerRounded(e){let{size:t,x:n,y:r}=e;this._rotateFigure(Object.assign(Object.assign({},e),{draw:()=>{this._element=this._window.document.createElementNS(`http://www.w3.org/2000/svg`,`path`),this._element.setAttribute(`d`,`M ${n} ${r}v ${t}h ${t}v `+-t/2+`a ${t/2} ${t/2}, 0, 0, 0, ${-t/2} ${-t/2}`)}}))}_basicCornerExtraRounded(e){let{size:t,x:n,y:r}=e;this._rotateFigure(Object.assign(Object.assign({},e),{draw:()=>{this._element=this._window.document.createElementNS(`http://www.w3.org/2000/svg`,`path`),this._element.setAttribute(`d`,`M ${n} ${r}v ${t}h ${t}a ${t} ${t}, 0, 0, 0, ${-t} ${-t}`)}}))}_basicCornersRounded(e){let{size:t,x:n,y:r}=e;this._rotateFigure(Object.assign(Object.assign({},e),{draw:()=>{this._element=this._window.document.createElementNS(`http://www.w3.org/2000/svg`,`path`),this._element.setAttribute(`d`,`M ${n} ${r}v `+t/2+`a ${t/2} ${t/2}, 0, 0, 0, ${t/2} ${t/2}h `+t/2+`v `+-t/2+`a ${t/2} ${t/2}, 0, 0, 0, ${-t/2} ${-t/2}`)}}))}_drawDot({x:e,y:t,size:n}){this._basicDot({x:e,y:t,size:n,rotation:0})}_drawSquare({x:e,y:t,size:n}){this._basicSquare({x:e,y:t,size:n,rotation:0})}_drawRounded({x:e,y:t,size:n,getNeighbor:r}){let i=r?+r(-1,0):0,a=r?+r(1,0):0,o=r?+r(0,-1):0,s=r?+r(0,1):0,c=i+a+o+s;if(c!==0){if(c>2||i&&a||o&&s)this._basicSquare({x:e,y:t,size:n,rotation:0});else{if(c===2){let r=0;i&&o?r=Math.PI/2:o&&a?r=Math.PI:a&&s&&(r=-Math.PI/2),this._basicCornerRounded({x:e,y:t,size:n,rotation:r});return}if(c===1){let r=0;o?r=Math.PI/2:a?r=Math.PI:s&&(r=-Math.PI/2),this._basicSideRounded({x:e,y:t,size:n,rotation:r});return}}}else this._basicDot({x:e,y:t,size:n,rotation:0})}_drawExtraRounded({x:e,y:t,size:n,getNeighbor:r}){let i=r?+r(-1,0):0,a=r?+r(1,0):0,o=r?+r(0,-1):0,s=r?+r(0,1):0,c=i+a+o+s;if(c!==0){if(c>2||i&&a||o&&s)this._basicSquare({x:e,y:t,size:n,rotation:0});else{if(c===2){let r=0;i&&o?r=Math.PI/2:o&&a?r=Math.PI:a&&s&&(r=-Math.PI/2),this._basicCornerExtraRounded({x:e,y:t,size:n,rotation:r});return}if(c===1){let r=0;o?r=Math.PI/2:a?r=Math.PI:s&&(r=-Math.PI/2),this._basicSideRounded({x:e,y:t,size:n,rotation:r});return}}}else this._basicDot({x:e,y:t,size:n,rotation:0})}_drawClassy({x:e,y:t,size:n,getNeighbor:r}){let i=r?+r(-1,0):0,a=r?+r(1,0):0,o=r?+r(0,-1):0,s=r?+r(0,1):0;i+a+o+s===0?this._basicCornersRounded({x:e,y:t,size:n,rotation:Math.PI/2}):i||o?a||s?this._basicSquare({x:e,y:t,size:n,rotation:0}):this._basicCornerRounded({x:e,y:t,size:n,rotation:Math.PI/2}):this._basicCornerRounded({x:e,y:t,size:n,rotation:-Math.PI/2})}_drawClassyRounded({x:e,y:t,size:n,getNeighbor:r}){let i=r?+r(-1,0):0,a=r?+r(1,0):0,o=r?+r(0,-1):0,s=r?+r(0,1):0;i+a+o+s===0?this._basicCornersRounded({x:e,y:t,size:n,rotation:Math.PI/2}):i||o?a||s?this._basicSquare({x:e,y:t,size:n,rotation:0}):this._basicCornerExtraRounded({x:e,y:t,size:n,rotation:Math.PI/2}):this._basicCornerExtraRounded({x:e,y:t,size:n,rotation:-Math.PI/2})}}let s={dot:`dot`,square:`square`,extraRounded:`extra-rounded`},c=Object.values(s);class l{constructor({svg:e,type:t,window:n}){this._svg=e,this._type=t,this._window=n}draw(e,t,n,r){let i;switch(this._type){case s.square:i=this._drawSquare;break;case s.extraRounded:i=this._drawExtraRounded;break;default:i=this._drawDot}i.call(this,{x:e,y:t,size:n,rotation:r})}_rotateFigure({x:e,y:t,size:n,rotation:r=0,draw:i}){var a;let o=e+n/2,s=t+n/2;i(),(a=this._element)==null||a.setAttribute(`transform`,`rotate(${180*r/Math.PI},${o},${s})`)}_basicDot(e){let{size:t,x:n,y:r}=e,i=t/7;this._rotateFigure(Object.assign(Object.assign({},e),{draw:()=>{this._element=this._window.document.createElementNS(`http://www.w3.org/2000/svg`,`path`),this._element.setAttribute(`clip-rule`,`evenodd`),this._element.setAttribute(`d`,`M ${n+t/2} ${r}a ${t/2} ${t/2} 0 1 0 0.1 0zm 0 ${i}a ${t/2-i} ${t/2-i} 0 1 1 -0.1 0Z`)}}))}_basicSquare(e){let{size:t,x:n,y:r}=e,i=t/7;this._rotateFigure(Object.assign(Object.assign({},e),{draw:()=>{this._element=this._window.document.createElementNS(`http://www.w3.org/2000/svg`,`path`),this._element.setAttribute(`clip-rule`,`evenodd`),this._element.setAttribute(`d`,`M ${n} ${r}v ${t}h ${t}v `+-t+`zM ${n+i} ${r+i}h `+(t-2*i)+`v `+(t-2*i)+`h `+(2*i-t)+`z`)}}))}_basicExtraRounded(e){let{size:t,x:n,y:r}=e,i=t/7;this._rotateFigure(Object.assign(Object.assign({},e),{draw:()=>{this._element=this._window.document.createElementNS(`http://www.w3.org/2000/svg`,`path`),this._element.setAttribute(`clip-rule`,`evenodd`),this._element.setAttribute(`d`,`M ${n} ${r+2.5*i}v `+2*i+`a ${2.5*i} ${2.5*i}, 0, 0, 0, ${2.5*i} ${2.5*i}h `+2*i+`a ${2.5*i} ${2.5*i}, 0, 0, 0, ${2.5*i} ${2.5*-i}v `+-2*i+`a ${2.5*i} ${2.5*i}, 0, 0, 0, ${2.5*-i} ${2.5*-i}h `+-2*i+`a ${2.5*i} ${2.5*i}, 0, 0, 0, ${2.5*-i} ${2.5*i}M ${n+2.5*i} ${r+i}h `+2*i+`a ${1.5*i} ${1.5*i}, 0, 0, 1, ${1.5*i} ${1.5*i}v `+2*i+`a ${1.5*i} ${1.5*i}, 0, 0, 1, ${1.5*-i} ${1.5*i}h `+-2*i+`a ${1.5*i} ${1.5*i}, 0, 0, 1, ${1.5*-i} ${1.5*-i}v `+-2*i+`a ${1.5*i} ${1.5*i}, 0, 0, 1, ${1.5*i} ${1.5*-i}`)}}))}_drawDot({x:e,y:t,size:n,rotation:r}){this._basicDot({x:e,y:t,size:n,rotation:r})}_drawSquare({x:e,y:t,size:n,rotation:r}){this._basicSquare({x:e,y:t,size:n,rotation:r})}_drawExtraRounded({x:e,y:t,size:n,rotation:r}){this._basicExtraRounded({x:e,y:t,size:n,rotation:r})}}let u={dot:`dot`,square:`square`},d=Object.values(u);class f{constructor({svg:e,type:t,window:n}){this._svg=e,this._type=t,this._window=n}draw(e,t,n,r){let i;i=this._type===u.square?this._drawSquare:this._drawDot,i.call(this,{x:e,y:t,size:n,rotation:r})}_rotateFigure({x:e,y:t,size:n,rotation:r=0,draw:i}){var a;let o=e+n/2,s=t+n/2;i(),(a=this._element)==null||a.setAttribute(`transform`,`rotate(${180*r/Math.PI},${o},${s})`)}_basicDot(e){let{size:t,x:n,y:r}=e;this._rotateFigure(Object.assign(Object.assign({},e),{draw:()=>{this._element=this._window.document.createElementNS(`http://www.w3.org/2000/svg`,`circle`),this._element.setAttribute(`cx`,String(n+t/2)),this._element.setAttribute(`cy`,String(r+t/2)),this._element.setAttribute(`r`,String(t/2))}}))}_basicSquare(e){let{size:t,x:n,y:r}=e;this._rotateFigure(Object.assign(Object.assign({},e),{draw:()=>{this._element=this._window.document.createElementNS(`http://www.w3.org/2000/svg`,`rect`),this._element.setAttribute(`x`,String(n)),this._element.setAttribute(`y`,String(r)),this._element.setAttribute(`width`,String(t)),this._element.setAttribute(`height`,String(t))}}))}_drawDot({x:e,y:t,size:n,rotation:r}){this._basicDot({x:e,y:t,size:n,rotation:r})}_drawSquare({x:e,y:t,size:n,rotation:r}){this._basicSquare({x:e,y:t,size:n,rotation:r})}}let p=`circle`,m=[[1,1,1,1,1,1,1],[1,0,0,0,0,0,1],[1,0,0,0,0,0,1],[1,0,0,0,0,0,1],[1,0,0,0,0,0,1],[1,0,0,0,0,0,1],[1,1,1,1,1,1,1]],h=[[0,0,0,0,0,0,0],[0,0,0,0,0,0,0],[0,0,1,1,1,0,0],[0,0,1,1,1,0,0],[0,0,1,1,1,0,0],[0,0,0,0,0,0,0],[0,0,0,0,0,0,0]];class g{constructor(e,t){this._roundSize=e=>this._options.dotsOptions.roundSize?Math.floor(e):e,this._window=t,this._element=this._window.document.createElementNS(`http://www.w3.org/2000/svg`,`svg`),this._element.setAttribute(`width`,String(e.width)),this._element.setAttribute(`height`,String(e.height)),this._element.setAttribute(`xmlns:xlink`,`http://www.w3.org/1999/xlink`),e.dotsOptions.roundSize||this._element.setAttribute(`shape-rendering`,`crispEdges`),this._element.setAttribute(`viewBox`,`0 0 ${e.width} ${e.height}`),this._defs=this._window.document.createElementNS(`http://www.w3.org/2000/svg`,`defs`),this._element.appendChild(this._defs),this._imageUri=e.image,this._instanceId=g.instanceCount++,this._options=e}get width(){return this._options.width}get height(){return this._options.height}getElement(){return this._element}async drawQR(e){let t=e.getModuleCount(),n=Math.min(this._options.width,this._options.height)-2*this._options.margin,r=this._options.shape===p?n/Math.sqrt(2):n,i=this._roundSize(r/t),o={hideXDots:0,hideYDots:0,width:0,height:0};if(this._qr=e,this._options.image){if(await this.loadImage(),!this._image)return;let{imageOptions:e,qrOptions:n}=this._options,r=e.imageSize*a[n.errorCorrectionLevel],s=Math.floor(r*t*t);o=function({originalHeight:e,originalWidth:t,maxHiddenDots:n,maxHiddenAxisDots:r,dotSize:i}){let a={x:0,y:0},o={x:0,y:0};if(e<=0||t<=0||n<=0||i<=0)return{height:0,width:0,hideYDots:0,hideXDots:0};let s=e/t;return a.x=Math.floor(Math.sqrt(n/s)),a.x<=0&&(a.x=1),r&&r<a.x&&(a.x=r),a.x%2==0&&a.x--,o.x=a.x*i,a.y=1+2*Math.ceil((a.x*s-1)/2),o.y=Math.round(o.x*s),(a.y*a.x>n||r&&r<a.y)&&(r&&r<a.y?(a.y=r,a.y%2==0&&a.x--):a.y-=2,o.y=a.y*i,a.x=1+2*Math.ceil((a.y/s-1)/2),o.x=Math.round(o.y/s)),{height:o.y,width:o.x,hideYDots:a.y,hideXDots:a.x}}({originalWidth:this._image.width,originalHeight:this._image.height,maxHiddenDots:s,maxHiddenAxisDots:t-14,dotSize:i})}this.drawBackground(),this.drawDots(((e,n)=>!(this._options.imageOptions.hideBackgroundDots&&e>=(t-o.hideYDots)/2&&e<(t+o.hideYDots)/2&&n>=(t-o.hideXDots)/2&&n<(t+o.hideXDots)/2||m[e]?.[n]||m[e-t+7]?.[n]||m[e]?.[n-t+7]||h[e]?.[n]||h[e-t+7]?.[n]||h[e]?.[n-t+7]))),this.drawCorners(),this._options.image&&await this.drawImage({width:o.width,height:o.height,count:t,dotSize:i})}drawBackground(){var e;let t=this._element,n=this._options;if(t){let t=n.backgroundOptions?.gradient,r=n.backgroundOptions?.color,i=n.height,a=n.width;if(t||r){let o=this._window.document.createElementNS(`http://www.w3.org/2000/svg`,`rect`);this._backgroundClipPath=this._window.document.createElementNS(`http://www.w3.org/2000/svg`,`clipPath`),this._backgroundClipPath.setAttribute(`id`,`clip-path-background-color-${this._instanceId}`),this._defs.appendChild(this._backgroundClipPath),(e=n.backgroundOptions)!=null&&e.round&&(i=a=Math.min(n.width,n.height),o.setAttribute(`rx`,String(i/2*n.backgroundOptions.round))),o.setAttribute(`x`,String(this._roundSize((n.width-a)/2))),o.setAttribute(`y`,String(this._roundSize((n.height-i)/2))),o.setAttribute(`width`,String(a)),o.setAttribute(`height`,String(i)),this._backgroundClipPath.appendChild(o),this._createColor({options:t,color:r,additionalRotation:0,x:0,y:0,height:n.height,width:n.width,name:`background-color-${this._instanceId}`})}}}drawDots(e){var t;if(!this._qr)throw`QR code is not defined`;let n=this._options,r=this._qr.getModuleCount();if(r>n.width||r>n.height)throw`The canvas is too small.`;let i=Math.min(n.width,n.height)-2*n.margin,a=n.shape===p?i/Math.sqrt(2):i,s=this._roundSize(a/r),c=this._roundSize((n.width-r*s)/2),l=this._roundSize((n.height-r*s)/2),u=new o({svg:this._element,type:n.dotsOptions.type,window:this._window});this._dotsClipPath=this._window.document.createElementNS(`http://www.w3.org/2000/svg`,`clipPath`),this._dotsClipPath.setAttribute(`id`,`clip-path-dot-color-${this._instanceId}`),this._defs.appendChild(this._dotsClipPath),this._createColor({options:n.dotsOptions?.gradient,color:n.dotsOptions.color,additionalRotation:0,x:0,y:0,height:n.height,width:n.width,name:`dot-color-${this._instanceId}`});for(let n=0;n<r;n++)for(let i=0;i<r;i++)e&&!e(n,i)||(t=this._qr)!=null&&t.isDark(n,i)&&(u.draw(c+i*s,l+n*s,s,((t,a)=>!(i+t<0||n+a<0||i+t>=r||n+a>=r)&&!(e&&!e(n+a,i+t))&&!!this._qr&&this._qr.isDark(n+a,i+t))),u._element&&this._dotsClipPath&&this._dotsClipPath.appendChild(u._element));if(n.shape===p){let e=this._roundSize((i/s-r)/2),t=r+2*e,n=c-e*s,a=l-e*s,o=[],d=this._roundSize(t/2);for(let n=0;n<t;n++){o[n]=[];for(let i=0;i<t;i++)n>=e-1&&n<=t-e&&i>=e-1&&i<=t-e||Math.sqrt((n-d)*(n-d)+(i-d)*(i-d))>d?o[n][i]=0:o[n][i]=+!!this._qr.isDark(i-2*e<0?i:i>=r?i-2*e:i-e,n-2*e<0?n:n>=r?n-2*e:n-e)}for(let e=0;e<t;e++)for(let r=0;r<t;r++)o[e][r]&&(u.draw(n+r*s,a+e*s,s,((t,n)=>!!o[e+n]?.[r+t])),u._element&&this._dotsClipPath&&this._dotsClipPath.appendChild(u._element))}}drawCorners(){if(!this._qr)throw`QR code is not defined`;let e=this._element,t=this._options;if(!e)throw`Element code is not defined`;let n=this._qr.getModuleCount(),r=Math.min(t.width,t.height)-2*t.margin,i=t.shape===p?r/Math.sqrt(2):r,a=this._roundSize(i/n),s=7*a,u=3*a,g=this._roundSize((t.width-n*a)/2),_=this._roundSize((t.height-n*a)/2);[[0,0,0],[1,0,Math.PI/2],[0,1,-Math.PI/2]].forEach((([e,r,i])=>{var p,v,y,b,x,S;let C=g+e*a*(n-7),w=_+r*a*(n-7),T=this._dotsClipPath,E=this._dotsClipPath;if(((p=t.cornersSquareOptions)!=null&&p.gradient||(v=t.cornersSquareOptions)!=null&&v.color)&&(T=this._window.document.createElementNS(`http://www.w3.org/2000/svg`,`clipPath`),T.setAttribute(`id`,`clip-path-corners-square-color-${e}-${r}-${this._instanceId}`),this._defs.appendChild(T),this._cornersSquareClipPath=this._cornersDotClipPath=E=T,this._createColor({options:t.cornersSquareOptions?.gradient,color:t.cornersSquareOptions?.color,additionalRotation:i,x:C,y:w,height:s,width:s,name:`corners-square-color-${e}-${r}-${this._instanceId}`})),t.cornersSquareOptions?.type&&c.includes(t.cornersSquareOptions.type)){let e=new l({svg:this._element,type:t.cornersSquareOptions.type,window:this._window});e.draw(C,w,s,i),e._element&&T&&T.appendChild(e._element)}else{let e=new o({svg:this._element,type:t.cornersSquareOptions?.type||t.dotsOptions.type,window:this._window});for(let t=0;t<m.length;t++)for(let n=0;n<m[t].length;n++)(y=m[t])!=null&&y[n]&&(e.draw(C+n*a,w+t*a,a,((e,r)=>!!m[t+r]?.[n+e])),e._element&&T&&T.appendChild(e._element))}if(((b=t.cornersDotOptions)!=null&&b.gradient||(x=t.cornersDotOptions)!=null&&x.color)&&(E=this._window.document.createElementNS(`http://www.w3.org/2000/svg`,`clipPath`),E.setAttribute(`id`,`clip-path-corners-dot-color-${e}-${r}-${this._instanceId}`),this._defs.appendChild(E),this._cornersDotClipPath=E,this._createColor({options:t.cornersDotOptions?.gradient,color:t.cornersDotOptions?.color,additionalRotation:i,x:C+2*a,y:w+2*a,height:u,width:u,name:`corners-dot-color-${e}-${r}-${this._instanceId}`})),t.cornersDotOptions?.type&&d.includes(t.cornersDotOptions.type)){let e=new f({svg:this._element,type:t.cornersDotOptions.type,window:this._window});e.draw(C+2*a,w+2*a,u,i),e._element&&E&&E.appendChild(e._element)}else{let e=new o({svg:this._element,type:t.cornersDotOptions?.type||t.dotsOptions.type,window:this._window});for(let t=0;t<h.length;t++)for(let n=0;n<h[t].length;n++)(S=h[t])!=null&&S[n]&&(e.draw(C+n*a,w+t*a,a,((e,r)=>!!h[t+r]?.[n+e])),e._element&&E&&E.appendChild(e._element))}}))}loadImage(){return new Promise(((e,t)=>{let n=this._options;if(!n.image)return t(`Image is not defined`);if(n.nodeCanvas?.loadImage)n.nodeCanvas.loadImage(n.image).then((t=>{var r;if(this._image=t,this._options.imageOptions.saveAsBlob){let e=n.nodeCanvas?.createCanvas(this._image.width,this._image.height);(r=e?.getContext(`2d`))==null||r.drawImage(t,0,0),this._imageUri=e?.toDataURL()}e()})).catch(t);else{let t=new this._window.Image;typeof n.imageOptions.crossOrigin==`string`&&(t.crossOrigin=n.imageOptions.crossOrigin),this._image=t,t.onload=async()=>{this._options.imageOptions.saveAsBlob&&(this._imageUri=await async function(e,t){return new Promise((n=>{let r=new t.XMLHttpRequest;r.onload=function(){let e=new t.FileReader;e.onloadend=function(){n(e.result)},e.readAsDataURL(r.response)},r.open(`GET`,e),r.responseType=`blob`,r.send()}))}(n.image||``,this._window)),e()},t.src=n.image}}))}async drawImage({width:e,height:t,count:n,dotSize:r}){let i=this._options,a=this._roundSize((i.width-n*r)/2),o=this._roundSize((i.height-n*r)/2),s=a+this._roundSize(i.imageOptions.margin+(n*r-e)/2),c=o+this._roundSize(i.imageOptions.margin+(n*r-t)/2),l=e-2*i.imageOptions.margin,u=t-2*i.imageOptions.margin,d=this._window.document.createElementNS(`http://www.w3.org/2000/svg`,`image`);d.setAttribute(`href`,this._imageUri||``),d.setAttribute(`xlink:href`,this._imageUri||``),d.setAttribute(`x`,String(s)),d.setAttribute(`y`,String(c)),d.setAttribute(`width`,`${l}px`),d.setAttribute(`height`,`${u}px`),this._element.appendChild(d)}_createColor({options:e,color:t,additionalRotation:n,x:r,y:i,height:a,width:o,name:s}){let c=o>a?o:a,l=this._window.document.createElementNS(`http://www.w3.org/2000/svg`,`rect`);if(l.setAttribute(`x`,String(r)),l.setAttribute(`y`,String(i)),l.setAttribute(`height`,String(a)),l.setAttribute(`width`,String(o)),l.setAttribute(`clip-path`,`url('#clip-path-${s}')`),e){let t;if(e.type===`radial`)t=this._window.document.createElementNS(`http://www.w3.org/2000/svg`,`radialGradient`),t.setAttribute(`id`,s),t.setAttribute(`gradientUnits`,`userSpaceOnUse`),t.setAttribute(`fx`,String(r+o/2)),t.setAttribute(`fy`,String(i+a/2)),t.setAttribute(`cx`,String(r+o/2)),t.setAttribute(`cy`,String(i+a/2)),t.setAttribute(`r`,String(c/2));else{let c=((e.rotation||0)+n)%(2*Math.PI),l=(c+2*Math.PI)%(2*Math.PI),u=r+o/2,d=i+a/2,f=r+o/2,p=i+a/2;l>=0&&l<=.25*Math.PI||l>1.75*Math.PI&&l<=2*Math.PI?(u-=o/2,d-=a/2*Math.tan(c),f+=o/2,p+=a/2*Math.tan(c)):l>.25*Math.PI&&l<=.75*Math.PI?(d-=a/2,u-=o/2/Math.tan(c),p+=a/2,f+=o/2/Math.tan(c)):l>.75*Math.PI&&l<=1.25*Math.PI?(u+=o/2,d+=a/2*Math.tan(c),f-=o/2,p-=a/2*Math.tan(c)):l>1.25*Math.PI&&l<=1.75*Math.PI&&(d+=a/2,u+=o/2/Math.tan(c),p-=a/2,f-=o/2/Math.tan(c)),t=this._window.document.createElementNS(`http://www.w3.org/2000/svg`,`linearGradient`),t.setAttribute(`id`,s),t.setAttribute(`gradientUnits`,`userSpaceOnUse`),t.setAttribute(`x1`,String(Math.round(u))),t.setAttribute(`y1`,String(Math.round(d))),t.setAttribute(`x2`,String(Math.round(f))),t.setAttribute(`y2`,String(Math.round(p)))}e.colorStops.forEach((({offset:e,color:n})=>{let r=this._window.document.createElementNS(`http://www.w3.org/2000/svg`,`stop`);r.setAttribute(`offset`,100*e+`%`),r.setAttribute(`stop-color`,n),t.appendChild(r)})),l.setAttribute(`fill`,`url('#${s}')`),this._defs.appendChild(t)}else t&&l.setAttribute(`fill`,t);this._element.appendChild(l)}}g.instanceCount=0;let _=g,v=`canvas`,y={};for(let e=0;e<=40;e++)y[e]=e;let b={type:v,shape:`square`,width:300,height:300,data:``,margin:0,qrOptions:{typeNumber:y[0],mode:void 0,errorCorrectionLevel:`Q`},imageOptions:{saveAsBlob:!0,hideBackgroundDots:!0,imageSize:.4,crossOrigin:void 0,margin:0},dotsOptions:{type:`square`,color:`#000`,roundSize:!0},backgroundOptions:{round:0,color:`#fff`}};function x(e){let t=Object.assign({},e);if(!t.colorStops||!t.colorStops.length)throw`Field 'colorStops' is required in gradient`;return t.rotation=t.rotation?Number(t.rotation):0,t.colorStops=t.colorStops.map((e=>Object.assign(Object.assign({},e),{offset:Number(e.offset)}))),t}function S(e){let t=Object.assign({},e);return t.width=Number(t.width),t.height=Number(t.height),t.margin=Number(t.margin),t.imageOptions=Object.assign(Object.assign({},t.imageOptions),{hideBackgroundDots:!!t.imageOptions.hideBackgroundDots,imageSize:Number(t.imageOptions.imageSize),margin:Number(t.imageOptions.margin)}),t.margin>Math.min(t.width,t.height)&&(t.margin=Math.min(t.width,t.height)),t.dotsOptions=Object.assign({},t.dotsOptions),t.dotsOptions.gradient&&(t.dotsOptions.gradient=x(t.dotsOptions.gradient)),t.cornersSquareOptions&&(t.cornersSquareOptions=Object.assign({},t.cornersSquareOptions),t.cornersSquareOptions.gradient&&(t.cornersSquareOptions.gradient=x(t.cornersSquareOptions.gradient))),t.cornersDotOptions&&(t.cornersDotOptions=Object.assign({},t.cornersDotOptions),t.cornersDotOptions.gradient&&(t.cornersDotOptions.gradient=x(t.cornersDotOptions.gradient))),t.backgroundOptions&&(t.backgroundOptions=Object.assign({},t.backgroundOptions),t.backgroundOptions.gradient&&(t.backgroundOptions.gradient=x(t.backgroundOptions.gradient))),t}var C=n(873),w=n.n(C);function T(e){if(!e)throw Error(`Extension must be defined`);e[0]===`.`&&(e=e.substring(1));let t={bmp:`image/bmp`,gif:`image/gif`,ico:`image/vnd.microsoft.icon`,jpeg:`image/jpeg`,jpg:`image/jpeg`,png:`image/png`,svg:`image/svg+xml`,tif:`image/tiff`,tiff:`image/tiff`,webp:`image/webp`,pdf:`application/pdf`}[e.toLowerCase()];if(!t)throw Error(`Extension "${e}" is not supported`);return t}class E{constructor(e){this._window=e?.jsdom?new e.jsdom(``,{resources:`usable`}).window:window,this._options=e?S(t(b,e)):b,this.update()}static _clearContainer(e){e&&(e.innerHTML=``)}_setupSvg(){if(!this._qr)return;let e=new _(this._options,this._window);this._svg=e.getElement(),this._svgDrawingPromise=e.drawQR(this._qr).then((()=>{var t;this._svg&&((t=this._extension)==null||t.call(this,e.getElement(),this._options))}))}_setupCanvas(){this._qr&&(this._options.nodeCanvas?.createCanvas?(this._nodeCanvas=this._options.nodeCanvas.createCanvas(this._options.width,this._options.height),this._nodeCanvas.width=this._options.width,this._nodeCanvas.height=this._options.height):(this._domCanvas=document.createElement(`canvas`),this._domCanvas.width=this._options.width,this._domCanvas.height=this._options.height),this._setupSvg(),this._canvasDrawingPromise=this._svgDrawingPromise?.then((()=>{if(!this._svg)return;let e=this._svg,t=new this._window.XMLSerializer().serializeToString(e),n=btoa(t),r=`data:${T(`svg`)};base64,${n}`;if(this._options.nodeCanvas?.loadImage)return this._options.nodeCanvas.loadImage(r).then((e=>{var t;e.width=this._options.width,e.height=this._options.height,(t=this._nodeCanvas?.getContext(`2d`))==null||t.drawImage(e,0,0)}));{let e=new this._window.Image;return new Promise((t=>{e.onload=()=>{var n;(n=this._domCanvas?.getContext(`2d`))==null||n.drawImage(e,0,0),t()},e.src=r}))}})))}async _getElement(e=`png`){if(!this._qr)throw`QR code is empty`;return e.toLowerCase()===`svg`?(this._svg&&this._svgDrawingPromise||this._setupSvg(),await this._svgDrawingPromise,this._svg):((this._domCanvas||this._nodeCanvas)&&this._canvasDrawingPromise||this._setupCanvas(),await this._canvasDrawingPromise,this._domCanvas||this._nodeCanvas)}update(e){E._clearContainer(this._container),this._options=e?S(t(this._options,e)):this._options,this._options.data&&(this._qr=w()(this._options.qrOptions.typeNumber,this._options.qrOptions.errorCorrectionLevel),this._qr.addData(this._options.data,this._options.qrOptions.mode||function(e){switch(!0){case/^[0-9]*$/.test(e):return`Numeric`;case/^[0-9A-Z $%*+\-./:]*$/.test(e):return`Alphanumeric`;default:return`Byte`}}(this._options.data)),this._qr.make(),this._options.type===v?this._setupCanvas():this._setupSvg(),this.append(this._container))}append(e){if(e){if(typeof e.appendChild!=`function`)throw`Container should be a single DOM node`;this._options.type===v?this._domCanvas&&e.appendChild(this._domCanvas):this._svg&&e.appendChild(this._svg),this._container=e}}applyExtension(e){if(!e)throw`Extension function should be defined.`;this._extension=e,this.update()}deleteExtension(){this._extension=void 0,this.update()}async getRawData(e=`png`){if(!this._qr)throw`QR code is empty`;let t=await this._getElement(e),n=T(e);if(!t)return null;if(e.toLowerCase()===`svg`){let e=`<?xml version="1.0" standalone="no"?>\r\n${new this._window.XMLSerializer().serializeToString(t)}`;return typeof Blob>`u`||this._options.jsdom?Buffer.from(e):new Blob([e],{type:n})}return new Promise((e=>{let r=t;if(`toBuffer`in r){if(n===`image/png`)e(r.toBuffer(n));else if(n===`image/jpeg`)e(r.toBuffer(n));else{if(n!==`application/pdf`)throw Error(`Unsupported extension`);e(r.toBuffer(n))}}else`toBlob`in r&&r.toBlob(e,n,1)}))}async download(e){if(!this._qr)throw`QR code is empty`;if(typeof Blob>`u`)throw`Cannot download in Node.js, call getRawData instead.`;let t=`png`,n=`qr`;typeof e==`string`?(t=e,console.warn(`Extension is deprecated as argument for 'download' method, please pass object { name: '...', extension: '...' } as argument`)):typeof e==`object`&&e&&(e.name&&(n=e.name),e.extension&&(t=e.extension));let r=await this._getElement(t);if(r){if(t.toLowerCase()===`svg`){let e=new XMLSerializer().serializeToString(r);e=`<?xml version="1.0" standalone="no"?>\r
`+e,i(`data:${T(t)};charset=utf-8,${encodeURIComponent(e)}`,`${n}.svg`)}else i(r.toDataURL(T(t)),`${n}.${t}`)}}}let D=E})(),r.default})()))}))(),1);function Ki(e,t=!1){let n=`
  <svg xmlns="http://www.w3.org/2000/svg" class="preloader-circular" viewBox="25 25 50 50">
  <circle class="preloader-path" cx="50" cy="50" r="20" fill="none" stroke-miterlimit="10"/>
  </svg>`;if(t){let t=document.createElement(`div`);return t.classList.add(`preloader`),t.innerHTML=n,e&&e.appendChild(t),t}return e.insertAdjacentHTML(`beforeend`,n),e.lastElementChild}function qi(e){let t=new Blob([e],{type:`image/svg+xml;charset=utf-8`});return new Promise(e=>{let n=new FileReader;n.onload=t=>{e(t.target.result)},n.readAsDataURL(t)})}var Ji=new Map;function Yi(e){let t=Ji.get(e);return t||(t=Promise.resolve(qi(Dr(e))),Ji.set(e,t)),t}async function Xi(e){let{data:t,size:n,host:r,background:i,foreground:a,logoColor:o,pixelRatio:s=window.devicePixelRatio,QRCodeStylingCtor:c}=e,l=await Yi(o),u=new c({width:n*s,height:n*s,data:t,image:l,dotsOptions:{color:a,type:`rounded`},cornersSquareOptions:{type:`extra-rounded`,color:a},imageOptions:{imageSize:1,margin:0},backgroundOptions:{color:i},qrOptions:{errorCorrectionLevel:`L`}});u.append(r);let d=r.lastChild;e.canvasClass&&d.classList.add(e.canvasClass);let f,p=u._drawingPromise||u._canvasDrawingPromise;if(p)f=p;else{let e=u._canvas?._image;f=Promise.race([Or(1e3),new Promise(t=>{if(!e||e.complete){window.requestAnimationFrame(()=>t());return}e.addEventListener(`load`,()=>{window.requestAnimationFrame(()=>t())},{once:!0})})])}return await f,{canvas:d,qrCode:u}}var Zi=W(`<ol>`),Qi=W(`<li><span>`),$i=W(`<h1 style=margin-top:0>`),ea=240,ta=1e4;function na(e){let{navigate:t,toIm:n}=pn(),r,i,a=!1,o=new AbortController,s,c=[`Login.QR.Help1`,`Login.QR.Help2`,`Login.QR.Help3`],l=(()=>{var e=Zi();return K(e,()=>c.map((e,t)=>(()=>{var n=Qi(),r=n.firstChild;return K(r,t+1),K(n,()=>q(e),null),j(e=>{var t=J.qrDescriptionItem,i=J.qrDescriptionMarker;return t!==e.e&&G(n,e.e=t),i!==e.t&&G(r,e.t=i),e},{e:void 0,t:void 0}),n})())),j(()=>G(e,J.qrDescription)),e})();function u(e){i?(r.append(e),i.style.animation=`hide-icon .4s forwards`,e.style.display=`none`,e.style.animation=`grow-icon .4s forwards`,setTimeout(()=>{e.style.display=``},150),setTimeout(()=>{e.style.animation=``},500),i=void 0):r.replaceChildren(e)}async function d(e){let t=window.getComputedStyle(document.documentElement),n=t.getPropertyValue(`--qr-background`).trim()||t.getPropertyValue(`--light-filled-primary-color`).trim(),r=t.getPropertyValue(`--primary-text-color`).trim(),i=t.getPropertyValue(`--primary-color`).trim(),{canvas:o}=await Xi({data:e,size:ea,host:document.createElement(`div`),background:n,foreground:r,logoColor:i,canvasClass:J.qrCanvas,QRCodeStylingCtor:Gi.default});a||(u(o),s=e)}function f(e){let t=document.createElement(`img`);t.classList.add(J.qrCanvas),t.alt=``,t.addEventListener(`load`,()=>{a||u(t)},{once:!0}),t.src=`data:image/svg+xml;charset=utf-8,`+encodeURIComponent(e)}let p=()=>{!a&&s&&d(s)};window.addEventListener(`madeline-theme-change`,p);async function m(t){let r=0,i=`getQrCode`;for(;!a;)try{let e=await en(t,i,o.signal);if(a)return;if(r=0,i=`waitQrCodeOrLogin`,e.logged_in===!0){a=!0,Xt()||n();return}e.link?e.link!==s&&await d(e.link):e.svg&&f(e.svg)}catch(t){if(a)return;console.error(`SignQRCard:`,t),r++,r>1&&(i=`getQrCode`),await Or(Math.min((e.retryDelay??1e3)*r,ta))}}return F(()=>{i=Ki(r,!0),e.spec.page.qrEndpoint&&m(e.spec.page.qrEndpoint)}),ne(()=>{a=!0,o.abort(),window.removeEventListener(`madeline-theme-change`,p)}),V(Fr,{get class(){return J.pageSignQR},inputWrapper:!1,get header(){return V(Z,{get children(){return[V(Z.Sticker,{ref(e){var t=r;typeof t==`function`?t(e):r=e},get class(){return J.qrContainer},size:ea}),(()=>{var e=$i();return K(e,()=>q(`Login.QR.Title`)),j(()=>G(e,`${X.title} text-center text-overflow-wrap`)),e})(),V(Z.Subtitle,{class:`secondary`,get children(){return q(`Login.QR.Subtitle`)}}),V(ni,{get alerts(){return e.spec.page.alerts}})]}})},get children(){return[l,V(Y,{class:`btn-primary btn-secondary btn-primary-transparent primary`,onClick:()=>{a=!0,t({name:`signIn`,page:Yr(e.spec.page)})},text:`Login.QR.Cancel`})]}})}var ra=W(`<svg xmlns=http://www.w3.org/2000/svg class=preloader-circular viewBox="25 25 50 50"><circle class=preloader-path cx=50 cy=50 r=20 fill=none stroke-miterlimit=10>`),ia=W(`<div class=input-wrapper>`),aa=W(`<ol>`),oa=W(`<li><span>`),sa=W(`<div>`);function ca(e){return[U(()=>e.label.replace(/\s*>$/,``)),` `,V(Pn,{icon:`next`,class:`inline-icon`})]}function la(e,t,n){switch(e.type){case`hidden`:case`choice`:{let t=e.value||Object.keys(e.options)[0]||``;return{field:e,element:document.createDocumentFragment(),input:void 0,value:()=>t,setError(){}}}case`tel`:{let r=Cr(t),i=document.createDocumentFragment();return i.append(r.countryInputField.container,r.telInputField.container),n.push(()=>r.init(e.value)),{field:e,element:i,input:r.telInputField.input,value:()=>r.telInputField.value,setError:()=>r.telInputField.setError()}}case`password`:case`code`:{let t=new _i({labelText:e.label,name:e.name,autocomplete:e.autocomplete??`off`});return e.maxLength&&(t.input.maxLength=e.maxLength),t.setValueSilently(e.value),{field:e,element:t.container,input:t.input,value:()=>t.value,setError:()=>t.setError(),passwordField:t}}default:{let t=new sr({labelText:e.label+(e.required?``:` (`+Ut.format(`Optional`,!0)+`)`),name:e.name,autocomplete:e.autocomplete??`off`,maxLength:e.maxLength??void 0});return e.type===`url`&&(t.input.type=`url`),t.setValueSilently(e.value),{field:e,element:t.container,input:t.input,value:()=>t.value,setError:()=>t.setError()}}}}function ua(e){let{submit:t}=pn(),n=e.spec.page,r=!1,[i,a]=A(null),[o,s]=A(n.submit??``),[c,l]=A(!0),u=[],d=n.fields.map(e=>la(e,l,u)).filter(Boolean),f=()=>d.find(e=>e.input)?.input;for(let e of d)e.input?.addEventListener(`keypress`,t=>{if(e.input.classList.remove(`error`),s(n.submit??``),t.key===`Enter`&&!i())return _()});let p=xi.isMobile?100:130,m=n.view===`api.code`?d.find(e=>e.passwordField):void 0,h,g=document.createElement(`div`);function _(e){e&&er(e);let t=d.filter(e=>e.input&&e.field.required&&!e.value().trim());if(t.length){t.forEach(e=>e.setError()),t[0].input.focus();return}let n={};for(let e of d)n[e.field.name]=e.value().trim();v(n,`submit`)}function v(e,i){a(i),i===`submit`&&s([U(()=>q(`PleaseWait`)),ra()]),t(e).then(e=>{e&&!r&&(a(null),s(n.submit??``),b(e.alerts.filter(e=>e.level===`error`)),f()?.select())}).catch(e=>{console.error(`form error:`,e),!r&&(a(null),s(q(`Error.AnError`)))})}let[y,b]=A(n.alerts.filter(e=>e.level===`error`)),x=n.alerts.filter(e=>e.level!==`error`),S;return F(()=>{u.forEach(e=>e()),m&&(h=new Ri(m.passwordField,p),g.append(h.container),h.load().catch(()=>{}));let e=f();e&&!Ct&&(S=jr(e,()=>!r))}),ne(()=>{r=!0,S?.();let e=h;h=void 0,bi(g.closest(`.`+J.card),()=>e?.remove())}),V(Fr,{get class(){return J.pageApi},get header(){return V(Z,{get children(){return[V(Z.Sticker,{size:m?p:120,get element(){return m?g:V(Er,{get class(){return J.logo}})},get class(){return m?void 0:J.logoContainer}}),V(Z.Title,{tag:`h1`,get children(){return n.title}}),V(Re,{get when(){return!Xr(n.lead)},get children(){return V(Z.Subtitle,{class:`secondary`,get children(){return V(Jr,{get text(){return n.lead}})}})}}),V(ni,{get alerts(){return[...x,...y()]},errors:!0}),V(Re,{get when(){return n.instructions.length},get children(){var e=aa();return K(e,V(Ie,{get each(){return n.instructions},children:(e,t)=>(()=>{var n=oa(),r=n.firstChild;return K(r,()=>t()+1),K(n,V(Jr,{get class(){return J.instructionText},text:e}),null),j(e=>{var t=J.qrDescriptionItem,i=J.qrDescriptionMarker;return t!==e.e&&G(n,e.e=t),i!==e.t&&G(r,e.t=i),e},{e:void 0,t:void 0}),n})()})),j(()=>G(e,J.instructions)),e}})]}})},inputWrapper:!1,get children(){return[V(Re,{get when(){return n.submit!==null},get children(){var e=ia();return K(e,V(Ie,{each:d,children:e=>[U(()=>e.element),V(Re,{get when(){return!Xr(e.field.hint)},get children(){var t=sa();return K(t,V(Jr,{get text(){return e.field.hint}})),j(()=>G(t,J.fieldHint)),t}})]}),null),K(e,V(Y,{class:`btn-primary btn-color-primary`,get disabled(){return i()!==null||!c()},onClick:_,get children(){return o()}}),null),e}}),V(Ie,{get each(){return n.actions},children:e=>V(Y,{class:`btn-primary btn-secondary btn-primary-transparent primary`,get disabled(){return i()!==null},onClick:()=>v({[e.name]:e.value},e.name+`=`+e.value),get children(){return V(ca,{get label(){return e.label}})}})})]}})}function da(e){let t=e.spec.page;return V(Fr,{inputWrapper:!1,get header(){return V(Z,{get children(){return[V(Z.Sticker,{get class(){return J.logoContainer},size:120,get element(){return V(Er,{get class(){return J.logo}})}}),V(Z.Title,{tag:`h1`,get children(){return t.title}}),V(Re,{get when(){return!Xr(t.lead)},get children(){return V(Z.Subtitle,{class:`secondary`,get children(){return V(Jr,{get text(){return t.lead}})}})}}),V(ni,{get alerts(){return t.alerts},errors:!0})]}})}})}var fa=W(`<main id=auth-pages style=opacity:0><div><div></div><div></div><div>`);function pa(){let e,t={state:an,current:nn,navigate:sn,submit:un,toIm:dn};function n(){kt()}return F(()=>{nn()||console.warn(`[AuthCardsHost] mounted without an initial card â€” call navigateAuth({...}) before render`),(document.fonts?.ready??Promise.resolve()).then(()=>{e.style.opacity=``})}),V(fn.Provider,{value:t,get children(){return[V(Zn,{}),(()=>{var t=fa(),r=t.firstChild,i=r.firstChild,a=i.nextSibling,o=a.nextSibling,s=e;return typeof s==`function`?ot(s,t):e=t,K(t,V(Y.Icon,{icon:`darkmode_filled`,get class(){return J.themeButton},onClick:n,get"aria-label"(){return Ut.format(`DarkMode`,!0)}}),r),K(a,V(ma,{})),j(e=>{var n=jn(`whole`,J.host),s=jn(`scrollable`,`scrollable-y`,J.scrollable,(!Ct||An)&&`no-scrollbar`),c=jn(J.placeholder,J.placeholderTop),l=J.cardsContainer,u=J.placeholder;return n!==e.e&&G(t,e.e=n),s!==e.t&&G(r,e.t=s),c!==e.a&&G(i,e.a=c),l!==e.o&&G(a,e.o=l),u!==e.i&&G(o,e.i=u),e},{e:void 0,t:void 0,a:void 0,o:void 0,i:void 0}),t})()]}})}function ma(){let e=le(()=>V(ze,{get children(){return[V(Be,{get when(){return ln(`signIn`)},keyed:!0,children:e=>V(hi,{spec:e})}),V(Be,{get when(){return ln(`signBot`)},keyed:!0,children:e=>V(yi,{spec:e})}),V(Be,{get when(){return ln(`authCode`)},keyed:!0,children:e=>V(Li,{spec:e})}),V(Be,{get when(){return ln(`password`)},keyed:!0,children:e=>V(Bi,{spec:e})}),V(Be,{get when(){return ln(`signUp`)},keyed:!0,children:e=>V(Wi,{spec:e})}),V(Be,{get when(){return ln(`signQR`)},keyed:!0,children:e=>V(na,{spec:e})}),V(Be,{get when(){return ln(`apiForm`)},keyed:!0,children:e=>V(ua,{spec:e})}),V(Be,{get when(){return ln(`status`)},keyed:!0,children:e=>V(da,{spec:e})})]}})),t=N(t=>e()||t);return V(Re,{get when(){return t()},get children(){return V(Re,{get when(){return Mn.isAvailable(`animations`)},get fallback(){return t()},get children(){return V(Tn,{mode:`outin`,get enterActiveClass(){return J.cardEnterActive},get exitActiveClass(){return J.cardExitActive},get enterClass(){return J.cardEnter},get enterToClass(){return J.cardEnterTo},get exitClass(){return J.cardExit},get exitToClass(){return J.cardExitTo},appear:!1,get children(){return t()}})}})}})}function ha(){document.documentElement.classList.add(Ct?`is-touch`:`no-touch`);let e=window.matchMedia?.(`(prefers-reduced-motion: reduce)`).matches;document.body.classList.add(...e?[`animation-level-0`]:[`animation-level-1`,`animation-level-2`])}function ga(){let e=document.getElementById(`madeline-app-css`);return!e||(e.media=`all`,e.sheet)?Promise.resolve():new Promise(t=>{e.addEventListener(`load`,()=>t(),{once:!0}),e.addEventListener(`error`,()=>t(),{once:!0})})}function _a(e,t){return ha(),At(),Ft.setAssetUrl(t.assetUrl??``),Ut.applyStrings(t.strings??{},t.lang),t.lang&&(document.documentElement.lang=t.lang),t.dir&&(document.documentElement.dir=t.dir,document.documentElement.classList.toggle(`is-rtl`,t.dir===`rtl`)),cn(t),e.replaceChildren(),Xe(()=>V(pa,{}),e)}async function va(){let e=document.getElementById(`madeline-state`),t=document.getElementById(`madeline-app`);if(!e||!t)return;let n=JSON.parse(e.textContent||`{}`);await ga(),_a(t,n)}return window.__MADELINE_DEV__||va(),e.mount=_a,e.start=va,e})({});@font-face{font-family:Roboto;font-style:normal;font-weight:400;font-display:swap;src:local(Roboto),local(Roboto-Regular),url(data:font/woff2;base64,d09GMgABAAAAABpAAA4AAAAANGwAABnqAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGmobllYcNgZgAIcMEQwKvFCudguCEAABNgIkA4QcBCAFgnQHIBuNK7MDcVMl7SlI/usDU4aYOqHx/0iIDKdhs1tiOhRCdRDXpnvAjjLsifTEReM0nt970fkrMLQeXJDxT6HTn73D58c1uMQYIcksRPRu9TVJxyDIwsGKnanb33NhiLZ5gMyBGdvURdQvUxujekbNsZlBGAlGo/1voa6MaIzEXCRPvOvf3SQz2Vdh6murBBaE//yd5dTiHNJg2Qx+t18AfvNFLxCP3GVR1G0NobyRWnYB6P+3+TkD/d0Z+EWoqESoRKicCEPbWknplGcg6bDfyAgZ5YC/G7Xl+BzcrHQIB7yAirdGGr7qg91Y1Pb/f63S1p2/8Gvnm9qwmw5iHx+ZCIM2xlT9anr1uxqHqmuIanB7OrwEIFSAVUMIyYVdhIxcGxkhHLnk+AipoytUT3xsko6lkg/FKl1zf69p8Ew6b1GHygpPxwmQhdeNr77+a0Gh4QcAWtNwySwzRS75KAoqSFFUUYriSlKUVpuirroU9TWkaKwxRRsdKTrrQ9HPNAqFBriAiwIU4KGg8qTqu2ITJH10H/+CpK+Obz8g6ZfB9B8kEYBYFKD6fB7/AxtQgELQAA2P0ZViOgHKD1PbPNNHYLpG5hirjJZZHqYrln1D11SYmy3pheTPmSWu59Yn9vmav1JXXe/qv8YvrdN6yknZLpP1tUnbl/Qv2ntnoaP8SFhZr4yR32jaopY16kenZvenSFFeJllklU12OeSUS2555JVPAQUVUlgRRRVTXAkllVJaGWVVVE11NdRWRz31NdBEUy201FZ7HXTSWRdddddLb330QwwENzI2NbOwsnY0RUaKemjqoW+HuTW2prnrE6xbKLYIKHqBOBJIBqmaZUA5II8SKmhWfmkNqgfejklGM1x/0bxlbGvggCNdY6VbvWgU1cnG4mI1SzX5DFTXMtiOxBEwb7Kyvi3m1tla5M68UJsNr8tLTsgIV2hpwa0FgqY3RSkHKnyaOYQRM9kJWlkIEolR1kINfK/006F/EsKD3ZrEomiTql2aKCUi0+yZ24a36WUAWZ/VahgbxNo8x7WsFSNqcVyOW4LF1hxz8oxXAgnF8KRakG1RA88Llle8ZLpx3SIRW9Qkqk4himepmqMZAdYM/UwmntfckljLGvQCQgrhlX+L5YkZsDCDabAYiyDMNlamrmDGNJC9FRoCXf/WNolg7E7xulAbIjNFiTgSSFKqpowhR3mghAqaNAFmtOxjjQOuRGoW71i068iADBKn3posigvNoEm3qA6Mg2kr5pFWwOX6HLBclSoSwfAYmsxqkAcYGDBjaASWFSbiosxAUntKyZSlX8WeA8QqEYA8mJXGFmZ+V7fy0BjbnFbEZYQGxhYkiLWtAX/RTpJ0jJUIUiE0/ga9OsyBVagYocDs+8pXoHwDik2q1qPdlG4zSDAi7j5J1YINS4kbhb9apXsB/ls4IanMFeNVTkg1Tu14dbuNCCMBSUyuyZlum/ZsZSmRAR5YWqqeM3xRAm/4wh3eBpVOwgYtSWowLWwYsCq1PMC6waBUs6W+IrXdijuVAOdlinykwUG/Zms2FmDOCcHc0hyGUSuWMVc/LXNV09XILtDOYKyjt6X/uNtDWHMRDhu1Sb5hG+CKa9ah5onVS0y35MiVdn22pC2D7SUFN9NmyM012K35Mp/R+aZbknnTDSFcWtVa3yysMJJeZatynvIZp6VKSkVJsYrByykuoipB/EJRaa35AHkB8QdNLygZ1y+i+BUAddezJrMJrLBqbQ2SzbudS7t4QPD+mhuuN3PDYVkGjQi4+Hqv5mrWd5ONz5AYv4vR/XcrGLSxWuqCYgaPq+1yqHS3MzmioXXcQpZFx5fkrizNWyp+1MooCi5hPdBYS9XQ1F98Lx3mulm9l42xoJD0q0Wscwcm/djTm6c/+pX/e5q7u+xpkv9K0/KEIosuIiIAAHwAQLyDGzlPBaFNBjDCxIcMZkGX3fsQ9T4JhGglBw8e4mmEL5kcBqCAID8uPgwsQMckjk0Ai5CDsv1gkddPm0k3NpoEgewiiSFuscwBdOXaJ+j40Q1QbnAIAFAKtwQHN7SqVjYAV7NGbLyY1JARhYXrSHu4tFsQ3HVerC9XWFEsK8s6LetC6TyiTB+RDDBW9zKodPvu6o90n4B2azGp3h5qvK9kQMWQ6dnkf3S/Vk+QkdoAkNLGQRll7JluQIqr6k/GA8pF+BGEBOlokk0KSLU0SIcsSvGLaOw/BMkuBaV6GmZhGO7sv0+sNeCfv//0f0igZDylNZh8b5O0kUlbmbWb8ZGXc+qslXaRXVc55j7l9Mir+lhPneRdCW7f3s8n/3qIva4/vcHKKa+CISqqpLIqhqq6UqiaU3WnKU6NfPbGxhhrnPFrr1YzE0w1UXOukJgIRotCOHMmCVGcNkI0TiYhOqetEOFk1sU4rw20gzjhXiALxOV0FuLjTBbi53QREuBkFxLkdBUSx8mhSyg8DHSDZODkFBLh9NAlGp4A5IIkcnoKSeJ0EpLMyatLynkzA70gWcKzAvkg2TgDdcmuFQBCCnIG6VIovChQClKMM1iX4uElgXKQUpwhupQOLwdUhJTnDBVSgVPVEXjOSmAYpLLhVBNShTNCSFVOdSHVhhkJcc3ZANSANDScKUIacUYJaezgTjYxnNFCmhK1Ls3O2xKMgbTqaQPwybZnzgQh7ThThbTnTBTSgdNcl4XnXQRaQA6GHwLTkOn5N08rCg0bLXYEAJgBUH5gFqyx8I6D6g/VFIABAGjoqA5abBAzceeS4FfWwBV1jNJNhGYlvVtin8VTdwsqdnNJUrGtoBDdKNHxbbD1gO2kuq4E3UDI73VsV9I9UVscnyfoBr053KDjiPhMOzFjohN2oq7PCVjRXJ5QKNFx3LAb9DleR7xOxgS/z0nxBWzTDJjRHB5f0OfadrLX68bhdHE+0ojX72TJGO+EnaRDwo33LB2QfizF45vzeoYln196+XZ4KfOAg4wYFX3Wu2Ddk5iI0WhVoUtL9yK/Fobth9gyKR6m6Oo1ZRsP6J+YJqZOBYZeUg6sUATHCMDVQiY7sIhhhTb0Ynqr16B+PEs0VCgiA3FsgY+VUzEosvdKqnd6c3Kjt9Sd/5JARceNaP7eFxZ5kebaUNgfYqJHuWnJy1AVsZJLTyucX9DlOeWXYDrFPL4duKe9gCheZofCM0nW6Ck2mpHnEEklXSX0qnulBEfMEP5nPFvjwaPzIqJQTqzJx2YLfXfT1fWw96UI7tWfbVnpF0m0ao+g1iPAgbpXxtFGrtzKjQZnkkTFQZwEyA0ySuPh+2mSLa+88HgkRJvufgNbMmupG3P5VnQzNrPHPmOU1/inqqjnvaRpDWaXPrrNAo6J9wA3eJX3YyUHlT8fg7cTyzVjrIvg+DJFSa0mTnYeFz/NA1Eo/B8FS48TUD4KONi+Hhoiks8Gb/rTvcpSZJBDGyMjpRCH5bVtBTYk85e1iUKSKkhJZp9RB8+H+r6siswFGr+LdaR781FcCz6cyoYhJotsUvxnXcCGhRznFfFkO8eMtgumpeN8DH3dPBKCLL2755fE8LiihBJFkSsHNSeVLQj9XCTXQxiPG4HErVYQHjVL6evaC4ls2sYAF47q0ZQ4skkLT3eyh+nsSUwYbTAbXGlBnltqHtoeVtYfWrqXuEV9yl08LxeBZWKxUOFs7F8qNQMZtk4dRrT+VlKStZVO1piiRbp5rFfa858b3OeU32PaC7qqG4q1rTrk3oq8QX6NdJ3AqdYoGphZE7Y0Cwp/Y/ibyYxT481pM0Os9raHlUN+mKZrra3QgUFmYNCW1rbKrj3pVI6OVT31CUwYOLvwhQviNNhRonmXk2qikkduST6cMzLuxwisIq8yfenFrmnrDTZbO1rvEo9R+Kw2Z4eTHc3fozXVknO994G+02EsXHsdz6aROWSdS1ZeZcIXTs2j0FrIWvfg6H+CVDMzE2BMebvJgWF59r3aH8bxVpbUhX9WJOWqEyOVux2/WDeHEkpZlWKJ4tvQQvvtZCX1xLkuhbYnfqSgSiIfuGhotU2VemSqjM72o1yaTXqdOVkZ63y1mt+7+hZ/fw3ltmUjh3ySkBC+AcNPMRA3mXKDzdM08mpauM+I40O0gBIb7H1KqMWEANeylUU+4OgtHErIngKrMWLau0aPzyT2NT80MbtIx7kDGX5uZdUvioXFbvoY6BmJr72F2u6jWAilOT5+omJ6WGyyp91YrplYTs0HlzBqEo+K7EutEvVuJEeJqqjlZ3rFf5JPPjIO/s2TbUGx5sTU8P9cxUMaBrXzK9UxXQ3gdreXYaDn50OfGNmH+hmfSiTY37NBDsAyxtbWm5imTolGp97IBFb5fvw7uXgjm/uOf3ywB6fUvbhDPmRva7cQ64j9QPZdh+S81K919DWHa6GtkZdx9KJFMbQoq0G8IDOBXNXF9zts7ZNYSWpu5L5ScBed5soxnpQEsIyY5N4+V6DgCxrSJtKYXsp0nHBsAK5/vkxwoZzRwNn9a6V7eHP8K9M7MIToGk0C/kCSXly1Ou7Bu/w7F28Tj9HOzgNJEcaBj/Q75+dfq18XMDrASog50EcV1L8+/qMCCtE5AxF3ObMY+bjs6BuodoueqAf43PwifHbE/f46FKZTuLqiSxi9a/W7RwpGWnnRf4D5TejrL1aCzbvd3LeX9V01USOeFhlEpCWEo43BizeCfQArWM+/3N5kU3DiEVH77Exr98A4y+S62fXZwfG27mlwgD6Sgjo6CU78xzXy9StNbSsLyQHIUhnBwMnRwcjP6Bmi6Ej+qOZS3YtgaFuko2r8O6FqCRqHnk1EqXxRczIAr4XGhGS2DqV5/BkLf9e7JIKRHceklHwMNgyQv/TAXi2gS9CpxZ6SLhAtxcsZGhAPCDAEf5E6a2PtrelpbQWEy6DKG/vuXIz9fuXu/YfK1YJSZ2yvXoP/2LSA5rEUr78TQRHj/Bf/8SS7/ZoKiR7jv/gH3luWORwaljH2pSR7LDQoaxg8mBH4Pn224ZO9Fw4G2JGcva72Ic1NM0f3VGEqE3oDs5VjsUoxeX6JPeDCQelX9krit+jKck4KflOVRVWs2ljkta+NoYmD2rNHFo+E7+QRlNzUohYKYeIXLXu6PsyhHWztaOGjAr4VsoOHDEStDw38jL1IDEZzfosGwBovX9Ppz/uuTBvzjB16zE0W0jV/ddD4hI34kXMtYA9B66y1tVe0LQWn4n6DhD6DKa6jJEBAIASc9Rt0p0dzZ8S4UZ6+knvxVE72GXiEFawD1BqKGG+BAj84ygp2QOr9kuXBiyeC+C+J5z/sCGyUYGbWUKQESmQsKiydlE4OOpuJv5GUZEsg21D0ZwxpDr+Cz2T8EqZ4XmYSBW7nRS1BSRC68L9ycnOMqxbjaCy79TNqaxxzFmAFgcR93oPqh/MCI1D3khy09xhZXl7Cbx1ouO7Ye18vNC3pg29iQhAKiB2+2eHKNLScRoPp2nqwOIE5m9iRmMCH0HBj5r+3Q3hKmIczJSQIDV6aCPIzUo4X/docRo/1NnQASqAfPSfjZCMTYAWpyeaAq9cNsEIdtJdQq1mD2fzQD0UE68osZ9H/Zz9+Qi1NYM5J8/dTyz6sF5FM8fWkhoWigLaDSb2JiCkToF11LGyNefqkbc0AVqjuyec3a8J53L8sDcBLIjWRyqRmJFA2lv5fKZoluma4Md2Sia4zTYVSNBQb3Hx80jk7MM43P9Sc6exTOWsavMMz0mTimWF7HhD+fzw8txO1avq+9j3TqZwamoREuYHbHGXJpVFokjd4tMvg3Gt4ps8w+smGQN6EAbDAlgjic/n7BxINhbdlJ2Wu1F520tB3u6Qlr46laGIsLhm/P6ouzXFtEFz3k5KmAz4KGZ+RSeHhItNQhz8iH/dn8VPIXHJkLn5K1v4OpN+4ZmS+vWOYVS9phpjtrx9Ybvk6sNQE8LtyVuOLE/hf0OEkeO3H4HhvWuYH8kRtf24Ooie0uD6t7uPVCiAmwujQLlHvblWeoT0ISzBiNokYgd0MPf0LqASKz1BLSZONYX3+hFqeRp8CWPF4TbXDeuFqOSEFs6LudOLshsyNxr/qyMzmngPRy9hPBSi37yvNjUwt+MqDYt9JvrPWIYSQMxPECxpEWWLoRQY+M/xKc1Rz2GU0PXtd6pfkPLBJ32Jnz9yTEnuS83O399Vui8QudHvENdxe+x8t7brCmWvtilYStlJd/PPbnQDLuHj55wd6SjSQvCuQSI8Bx5z+tO3wTwrEvdkY+xA5gju0q0n4E/XtdIi4wbN7VG/7EM/9QVL0Y/Qq8bjonEJKYEpFOn9haktQcErrx7jUKpAEZQ7Nk6NfabipSRuIuvjpkEaSvp4feXrJ2ZEgl75+KobNcWGX4y+44pS2nYZABYBIvidnFcIrxl8E00L64bvbd2HQd37zhywexf8Q3C0eIA7AA8Rz/cvE8zq4KdH6t0yY6SNeP6WLJ55bBme3BV87qZsYEk2eVIWtlBbX9aZTXqi5KxyuuVdDmitgVm9VJFbJh9pbWL4zkNOylnOCE5trOMCVDxAMwSLSl0VgCBYFY20B7QiEtLhbNU/lcXZeXAH3r0AQkmYvEisgSmbTOF7SHmngZKvxf4U6wK8dMcyTBQgWEpkV8hVVPnIEhjYAgDs2OxAI6d/shwEKTttKQyATcAtDt6LDdGNyIDDay9d9tZLwpxPuf3JIt6r76hF2l0SlwWPpPDj7wX5CRZfE7mMmX1vIlOe9WT7mI6/A+psXSireVXtA0wKRdzzeDwPcRwihbdFg2juBwcwH50AtEugu7dva14mqfwS/4Nhk+bpLSyJAxBiihdJgWggNgVTuAwgIl32ao1G/7igBhyGKBf16I1GJ6q9OSx9tQlrSH/x0ogiZO1jqaNo6GqEA4eOIWSD4YSyE3O7pw2hu3MLw/+sy7yuMIr1VGTpvcpqMLXs5LnyTprr8GggMLmNPZjFMw4JNXeMquVLJ1FieKBIDEGw5hAf37mnp4zmAZ+GSPf9OBkZ24/j5hjGDMATDJ8oyhyXp45+Ls8ZDg7OGwW1nCEIyxP7Rv/IxzY7aJlvGjpfBYPyVvJW+OjhmhjdzNwbHrsBSaA/yJzU08UhuTjH1jiJv9BniMzNtGTdzB3vNAL2jshgYplPpRXCRFHhGNIpx5dRgcOi4ZjUZ2k6gjwOpFOdvg36xFRvDnAUMTvNwf3NiHAOXGEuNxaVQ88GdHShRqhzdUxYb01NdgR5MUxTNEBJOmKWjHtCUVXCvM7l7RHmrLzsImTibqyo66BkCQEjdjIOTDpViuhhkyhBS24LfxCMQkvN99Hhs3RVO7l2tcBuomuzsbni2ughKRB6cd0VnbetRIGVj2FgDAg+vimP21D1aP5CvCjC4cjjtexoC/bgNA48HiWbhZsG3E+DaGwhC00mqghOTYSQtncNQ11JVRdsC6JkYTBmCCp+2kfBjk7ajwra2MMAyWo8+dnCoBIfCisO3mG8IkfznHft0g8raCiAEfns61UHNF5waIy87+ba6N3zIIseFJ3vZaZA9LE3aPAxpdsVCfQtkjGPol7cNHxqpNGqa5wDJGdzJ9DMo4tB+k/XRyHac4xiQTnbaHQp4qyt6cRZh8S3Mw9VxzQAyxE3jmDPjafQtVnI7ODbq9ZjXr4/exHiK/vHO/0ZIY2/1Itv7awW2swtMyV4b6d3/lZYdC7BIdlU9TFi6kTIW9pg4oKY4+WPvhsc3aQ26Xg9vzCiYsHxBmtfqkaQEq4xapVvK8kvmQdaPQsxlNvOpjBXzM5MFWJCFVJhsSoXjbD5rLMriWZJKrNxwisZ8TClznHuxVZXwk2VwUxM1VPuvItXUikaZ2AqSNLPlA0q4I+L8k8zjqB/9poRbXGMqzcrv/3n6ZiWf///7AdgFqX5ZHIIUtCo7ATWiB08/tPZV5WJndbWV4TCM1IhdP82wClLvrKT1kiTFZGUmbKqoa2p1I3YltWE2rnXDLh1+VoszrlK7ygcXEyNMz5BWiMsKXSnc1/oVreXIaRexTWvRoIyr2ui5Epp7PyatcvSusw4GIhf027B9unvN6ixUo9/qQNWoPA+qZ6+eamhVR0P9XRitbOq2CsY3VE0dkohVRsymbNpwMPHBK0DLEiLja1M9Xtnf7EQbgKs/CpwDeKaU/v2d/PeZc8EeAhw0ABRYfW8XnFoC/1d7FygOZ5mxQnV1XfUPjJKkJJ52V68yInP/aZXZNc9yIpaKVdi1/9hVX8PrsiufFapQao5SKq31SznajZ/MG0unPpHaZJy5kaHD+kOudUt0tdRtTjZm/G3NsjQpTg613OH+l7tdjXksUuix1aFNVL5HHSwXmzcHha8qknNBwx51qDkK1KLezkuTvnKgHpZfAQ5eTAe3IMH9jcPFsqXGllzHOVFxWqWvTlls3dKyOn3y74xEEqRl1lsbJWuTKm9/sC5J1rrfhDUkZ+/pmnPWRkOPPdLanWxRvkvbxVkXdQayuu4ZVmysQHqexcxUg1ty7K89P+j8pX8oI5tXpEUPiM09wmFJZdoyHSmryVysX1oJS3llajENbGzEqEOyu2XSS8lcbo0iGsTll2Hj0vsXaCu7NSQs05d62Br5v061TtaQuFioOM3T6xAQjfwUMMMCUU0ZDsW+ZTqyStej2XSno8QB2mN4ksKl/UkafnqvqEMrijnJILuRJ5mkWF/qp6nTqvx/AUY9WC899DRURoUVVOi2IaMaZMgaxq+30+b8e2GhWIrfdN1TTRzemwm9sbl7ryyphzwbphLhfZh16UlCOzQ28aX+w8Iy4eQVZLGyl2IV5l4GtjzABVhv2YIsJeGkF4HeEg8Zt2Clr9x/88+RJlapKCchpawRlJlDrw3J4cqy9AoA)format("woff2");unicode-range:U400-45F, U490-491, U4B0-4B1, U2116}@font-face{font-family:Roboto;font-style:normal;font-weight:400;font-display:swap;src:local(Roboto),local(Roboto-Regular),url(data:font/woff2;base64,d09GMgABAAAAAB9YAA4AAAAAQtQAAB8CAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGkAbjgwcgTAGYACHSBEMCts8zA4Lg3oAATYCJAOHcAQgBYJ0ByAbOzhVRoWNA4Ab9FhRlAxGMPj/0wInMoRVH9rpfJ0o0MQ6Q/sePe/4HhTAomQRdA6OKFBFs6I66mkaH/Th+wd8tz1/C5c/XLsXr5JVU2oMDFEZVvFJtx2SVLIGz6+teX+DOHqBk17SJHJhLTpFeiVaDgGjET1BAZFIXSpFVnsxoFW0T8xGMTAKvB2ezv0LhK5bXvuJQ9QF80vi+b41Pu/rqer5etcvx8gFFQmkANBFsTJIEo+PMBEyEmk6ANTJBBalqeefBgHwCNmA3+Diw58Ct/1mVQUk9GxI0XtzmKstiOjeZ/9IZzkjWYYZwfnCWFThErD71e4BguUJyAlb90UTgh6oZBTYvico8/c203b/wvhrvDeTtYo9m79nNmugDfU0PRdVOhfV3lut6OsfrA4kOZpdm3Vn0smkM68MOl1Qdog7QFAYOoQKsaQqqVICTp+iSlOl6oHKFHUT6J2fmXlKK7sYcwgjjFmM0ZWU76gtNTIz2WIbru8u4oEA/spRxzcQCCjIAAAAExQY4LBGwGWLwJ4LglgVCKpNg4CAAiAFQAoEAAEgCRNL0B2v9wyE6gNZGalQfSQ5LgWqTwZlpUEVHQBuNiulPJSRBnEghNJjCQVJeCrK6AuYMLBod1sJmHCRKFmqNB8wcHt99tagKfQFffu4CBzXBxT1BB0Qf3w9dquwqyTPvShNm3dl9qXxp6YJT+6brxnURr/io6066am+TkU2TVWkT3u4avW+SPpYnpxX1W7cp1Phm/XlJMSsf9jmeetY9JWnJryc26bd8ab6PFdDdsN3NTphUn2zZdSzScVmPT98L5TKDTc4pMjJajQUGPYqjwEVW+u40KRXnDPed+XlAtDzkithk4c7eFabpL5Cudm79EYH/1vyHmFV4ilR78Fxj6vcujb/Fz4RgMSitLHOh5hyqa2Pufa572/L2V5QuGdvSVlF9T5+XUNTS2tbe0dn14Hugz2CQ0eOnRgcHj0/dunKtRs3b92+c/fe/QcPHz1/+Xri7eT7Dx8/TX3+8jW94BsIbB1HgDtGLsJDU2DudykL+cklABgQKwWilzQsASyJSgpSAbpOBwFNBeRKUi7FTpFALOUviKsykA7DWNhue6gMBWY1YnAjzx5FzWKjbw7NhJjtkgLZCfD3r5t2F+Q10aTTQIG+FoctAylUSWC3JcBokps6kg0DVKKWGobS5uAD9Dr0chk/iYKeoNdvTlg1aV43erJp3mC0jE35Fgevmv2TyUi5PEClPhOTCplHpjjZIEAUJtQRYAzGnLN72pmTDycFGdDxE7/wG9MgoCPNP7JtQ6PvzF4MgFHT5kC0yhm06+K/ncObbdaDOkASX1a43OjjAttA+zQT/wPxe80AG6kDgM5tATY22uKQOiP2+pMEyH50kg1wZosBJpEl8tHP7JiEE4/4JSoJ2ZKtKUpJGiLIp4gonsEZIWeieVXz333R/MVms5t16nJIrzNniUQAAZGLQgwyJ2axjmcCw0tinFKfnuDvMctI84pm9j5rfg02aR0gdPpyRckgSgRR/Iq7BvBW5K07+v8f72ksnwYA/pf988Oz3UM7+JCnPx7OPDDH409n87ijzjqyBQHguJ8C2EuIlQD17aeV9lp/y1Dcp4mvVp16DRo1abbQopv3M7nYEv0GDBsx6px4CRIleeyJp545q8+gIT8QSAoAqAUAADB8XVEWQDIQuf04iAUij8EJEAVOH2ODTAwwrGUyGyMuQOYYTQB7gJhxHIxKmVhjcAPExmgL4AOxM9oDFgJxMLoBWoC4Gz0Ai4F4Gv0AB4AEGqMAh4HwjCsA/UCijXGAYSDxXEIcpTERyBbA+cRWMJbKpAjDViYlGHYBUm9sANgD6UkIltlKPoHxLkB+G6cBiUBmjCIAj4GaZVQBnAVKlVPDGJQpDoazTIVguAQ0GQPgA2heMV4FMAmaLxlfD3gOmr8YfwUwAZq/cX9jfJLZ5zACZPabGD9k9lsYgQH2O9wHGEEy+0OM7zL7c4zQAPsL45cBYcD+mvsGI0LWPj0JohBQEM+IRMgJAWYA9JNr+MgSHJC+BGLHgV4FPzj2Lb/Uri/qtEhFyggl/DaUtvOP9AnyfV+7jTIOd0JioEAfENFEqly0AfaJCPj4JRPxqwIBoWUADOaI6Vy95V5qo4vQtQON/YCHJAkVGLQPnVxqJwY99dDHoesfeeSFSgJBaErycSQXlk4KeL6bNl4pQ+kXXlP7mZAe1YlKwHUCuncLGDNyBDir3vdrK9rM5QqWSm3KZdh6vMO+ML+2u0xj5NOdBA1ZWKoujwRLuOyLP0DCEe15Y5KeooMLNrYOz3JyPqQ8p3YHQLc6aWFYvOR0N7ztnVOqWeXkELHV0+KXAuiTOvhW4BN0Bxab22dUaXz+4rQpzT3XigPeEdtFnUOOSR6XAoe9/AqFq3FK7Rw5zkJm9pWl1G2fdyKaDpNhiVLNc8Yb1Bz9wTShZjX5eTWWuBPSusw73QX9fM8Jqtuh2XdL9Gj6SVO1CLTnJUVRrt39PMfWpJdlQHUTuRTi7PQTj52GIzIHPHEj4hvAS6ScnlMPti/oCpdfe2BlPjTC0TNpUFOrFO8v12qWyGnydB36UfqwDhG6C81x/TVdKIRuZXy4VdQqCk6y0IpDmEoKg3QAL7c+1XCaTsFALMXQJUq9bLx1uHeGDkdkN9PDMCrH2M/R1UkRu7oW+FlFryJMm+1UBZjnFpipeFKbUuLoqszcLCcFHx0skeP3fqgyCXtU46HWf8k/8d/tf6X6nNt+HD+0Eat1E3xL54dWWDbzPLNIffXCghO2rQot+MHMPVWmmsovWLI2nvz08wDyD2WXKJrVKl8YI04lt0kvfqaDqPC74VKU81k3sRio5Mx5BP5diA4h29IAz+FSjAJT4SpJD+EuGlVi8/sVdugujQ8lhTFNxt+SKD7WQdrQOP1DQ7swEJ4h78YI4jrq5f7URvqG/AEqrDgYgD8aNL0xmBZoRSa3H2EUPVQ22IhDyeHaGbV0I7ufER3oFCn9Ck7DjTVaEQLmfvPe4gjSdIgz6TNqwX9bIajVTMt353AcLv8KkWktvfBLbbNCVQBRxMV52y56yMANLExnoCpP47Xq9WQrsLkRoI+I9DxZWdTKe/DCIsk0TIhAsaPQOCwG/rX9WzwaSupx83lsRSQsDB9yKisrAEBCPaa1EprVFyYDsipETh253FM9qu8o+Oh5a/ZEdXRMX+atVLVloRFibUIn1RTIAoDx9JCfm8hCJQ7Ubpr9X+briz/7Ng7/ZypSqpSu91YTwJEYvhU0rFqgWOstUTLBdlTrM4gB7D2li0jhGD+juwekFeEwi2EYoCFkidODCdSBgjkL4V2tIskmFKwoV/aeTCHm5IkQOdQop37Nv0gSf6Mn+UvWeUBr5xAPUPgvmy1FJv4j+Sqtb1cyTV1YpuZ/XKVWvD8wt0wVFz2bLU/cMFaB6kf+sjCoCfnz2lz0Qg9cEGEgqLdoziU2IWO9w4veTTU8TauM7hTrCElYKoXT01TRvHayEHp7cYOfLoeEh3mAgRQHB2goFEl6iAc1uQYbWpXYRK/bwBV0Wbj+3G2JL9dKJNYhW/SLylSLHC55aEjNc55aLlElTgcD7/cR6KZrGSh152/2IVA+eMlWkJMrlIWszjgtIkWai8L4Jti7TF0g5ThgP4Y8us2WAQWfU6k4tFGy8xYQ5j1dIiftKCA8vGiQt2sUDxaoCU4pF7w5vNouSAkT46wBOQAdFbM8ViB0hAp1YaLgQnJ0aEWqyzP+4eS724yEqf8GVdzxusY6nh4ny/GmoXKYQtZkhjRbGBS03K4828VLwF1otAa95owNZ3UctkQET2pk8aQ3vDF5axH002/Sa6K1y/C13ujvNaopVugjXwjZ0l1KymnBx6j3rdyuVHfpx/J/M9U7vwfno+/t2RFwKvXnHtr7n/fPvmvj6Cs4asMD/zw19Yo4hkZ1RgTtwuOCkcMqH3+0w2vbieorzd8fKKtVVgUX95pLNPe8+JtSfjEwq7u1qXHuTHa68P3I34frB6t5jqADjI/2rYi01arhfMSVWkuDliEibamweuzRd5Yo/PFGYZxgCdWwlv6QzweMj0pbL99ci0WD22O2r4Hx0c7Wnc8x8Orvc0uiTBRMsLRWqJCxrW7KScovlN5ojvdbsnUGRmWA/W+P2X2pDQquLk7NjPDNBgMNjxosFfZnPMoYwNqfHiBMjgnWxa7/fnf97tTD8/2b88oKMkoawE6oNh+ae/O/Na/tvPpmUY5Bk+ErJO2faEnpFQ1AVUmgMdz8jf+6umKOfIzGi32lfDerFHgcbctvp+HN6Oh1oyfX8GtCwirwLRm4VrRErE2AULWXaJLLSTeK7SQ7D4xStxcZjp8ltKuKp46fKVLfUqZvaAp7D2WoGax4LrNz9AJ/9l388a28kv0c06Ak2ivFE7OL2uvg3PYqLDBAUN435ydXqg4wP3lQ8TYz+3D9wI62RnxlK/yPVyOUHHa0Ibor4RDLzdcVtIyC3a0DOwarmnAd2bfKs+C2mvkmGP19/ML8rzYe7i1j93AZjENZ7Y9qhDx9cvZebfdIRxcy3Hobz8bU+uYcUKD9bJQv/Tm7R3NIRvfFgLKSoId5WF85TTmNlaXfo8wQ9CP25sZ797RiigX6tcJ/Fndgvd+OL7rX9EWOfO2PQthkV9Zj64FG8706ov8Yc0S1m7PRk+1HR/MHQO/E0aAVKclJIegQlwWnjMat4DWLl3tpTZ/L9YPlvhYE7/PScXlUHHguODMILM6JKc0QC+khDV/dtxWTdpduAK3azG93cstmPtwV6+6hSNxTHFVIOOjXPVW9j0j9X++M+s7Gb1MVggmd4plpmJ3Tk+Y/TzJPVp7uFDjyP/pXIZew5/BkPWCxP+s0bJh5sGfb5QklmtdDWt3hrd3RuQ5mdukemDOFzLH0yk6mEk0ZsYJcAojS/zMxb2A0LZ9ybYfgjZ9TF6sfx3CXRcWn1yKYRmypX1ru2tm3psLPJ8rPcnucat8F1gxisOdQDDgBVWwHUzkXhZeQlZiQ4I0J0PeiCcEEIYKE2sJzmoYrU1He2bExQPEo94S12mLTsh8WS2ztXRaZNRekWVqpFPoA4dfMdbDxVTcEr3EMedg8NL76LoLcfRuwmrn0tWvKDp77dat5HQhYE0q2blw4lpWUjIrAQJIJV3pHJl8ft69hF3LBOspl5G7ct5fYvz0uQg3qNv15tKvorpzp/9K1634/yS+5I2c6IxQ/2nqzoLDnzrnD7XcKdvXdxNzMskal47Y1QMg9Sm0Kre5TPwMTbS2PdGfOhc0B3imO6AcneuaenClbsM2Oiw5L8rAwiTJhXdwV5J/pUfP8oLBgNyy9O1BIGr47NiMqxxXMWm1LPlY0VmdnaWsnuLHCkuW0CdVQ2rZdNMKEWXZKSs5599E9DPcLT307f9OJzyLkCuBY/9/bFgpZR7kVcpkWcjSLClYz96R0wDzZoXnleC8jOzR/VM3iaaz0Ax1HqQpWZKKNUOyclbP0jMZgU7G+0idPLEDFs1p1O5hhmFxqKjyIjmHpkfwUmL1Uri9qB6xRPPlLvxwaR6q4S1bUJRaRJ/u4qxgv+LVCXOtfrE4HmTI8i0u8ZxyG4ARukuWWd6WlksJAkPy685oPpsFVDhU48QiVVc/O9EUGTIQ7fK10U3x3Reu/vF4i/cW2MV9WFIg45IWl9U6HqzT1lT7OydEJWOVtsF4kXuT2TxQZuUcMgx5GFPYtItiWR90PHwelxEGRUWVxfrPMRVNHT6X0OJv6L/Hudk+6eI2JzDvMnu6VhvpMj3dLc/mZS9MrLPlWSu9fZJH8lQF2Ub5qNIvMskHG3apaO/6tBcBXbcPMk91F8RFSrqVw3NFgtW1t9p/HeXsjg4TdyuCYg0tlJ9UHeWnu0/ddd/fdjDs6457mMhppX9Z7Swj34eunrxOOyfDwnSITrxSvEuKWtByCGej56u3ZSflgoOFhwwAqnvCAzsO7ps2zjIeDDxHUq92Ls5dSLufXPRZWsP94SOr1ggkol3owAzZ/JzE9zE8pRkttnrRxAMpDcXzdjaO87W2qUt/4AJ71wkdJAKbd3jnfrOcnXe21OO+ay4KbyckibxZuW2IiYGVxpaozuw+PnDglY1LNrbNNE2/s/Qvsd238yE/bihp13CHby0X9QdDes9ypE6CoXLjB/MTDepxkaSxZ7pT2RYz6ovawZbfWBaM94XyPN0Yx6xxct/fZKh1rbRm4S5e5Z6pz78ZzGNC6LlJtbTMEPXnpkgcOduYRvqRdRUOZpPXgYodOgKJSSciiLtrghovwycHKl4GqrUwPV2JvFNtKdh34UFMTwAzPKeqzYy7idYFvZ2jUFaUza8fsvgMV0r8bPc3dZS62NCa7UeSKmeuuAfYRdt6TMZs+SeF4Yb9ycsfab+YLLAGfjasZkry8urYLwbWBv3Iw+oV2Q8mVth68K75f9x6trW0jcRKQJFPxjmw3IQ466i6pQDmQW9o5MI7TMc2Zs3iT1AGtrz4PpOf/0ivf+ON2wXpce/NnIusys61+N68XSrGEy9CMzy9GV3Sx5kmrbs5rACaxHySrywfqB/C032TToa4UVD7QTv1HftVveUk+DP+9vb65lralNLPS3MPeytzezgKIZ8GU/SvUWZ3tsTHVxxdKQ6D1c9B6azcFewclEwe7YGNTJTM7J7Blf2Kou6W9oo3UOieZ0JDwCHcvNB7iwPzlg9QNaN+5R3iLD6jPrTXdyExZV+anmxmlpa0139rBEPrjolPXqCPVaOSuUQU2TznHBFpbBAVYmAf4WtkG+bmbiix1FunqTenqszBn+C6q3OlOGt6h4h7MpHJ8eJCel48tNZxx7mvKLzx7h476joWOanx1em96GTD1u+VDOdTohT7EfnG+1zqlHBIlTjl2olpJchtRfLLj576+P3/24X+H+mK2ZCdE5eNRjOPhmz1IcgMQlIncr3pcxpf7ZnDJ69o3/W/uVt0t2x48vd+XVkTvs/lispBXeotVeqlswZHKjDXT8jSVukREPe+at57IQpQ7wfnxrmI5/ur0KLlgKQTjo/Vjk32sY0Xkvcwxt6dp/fm7y7G44opCoovgZt58G3x/IPZ+blW/5MD1qqm4pyeDnr4MzubEP52sutRKNVUVDN2q/tD7U0H3j6htt0rXGTiZPpCqO3Dk+wI7qD2QOB6jwmEoX4+9zrBUVh1bOmTG/PvA3wLMb+b2tK3RzkYIbbeGZsQDh4Z57Vll3IdCpyWr4lPB5p75CaKB5qEDBM023H6T0crRSPfS+Kpy6O12y38mK4e01QYrXyZ/y3KYUSmNLNevWBbihZC+cA9wa/XEohaFyaW+clkXDPlVTIe+Hbk/uVDquXwiNv3ahH3JimEOcnyW8ZlvYipGfD6wayKJJdeLrMVbehxTmEK/pB0GrX5cKGSN/M44DFp9+Zuvc4acl5VYDuMvq16x6RkGJ4R75BGfXqJ1STi2h10rSW6PCkcodWbqI+0LQkwr+LD6WFdrr+66yr96V+Wt/PuL48xXU/Db5eAtFrqvo6Y8t625EvBwUXHw+sS7FWppgqRIYT0c2r/977oKpE5s4HJ6Z3Q0Ibog8V70ctfwLxtn+xt7GCRQ1zBcn+hiEUu0JXzcNsx+ApgczocrYagpZ5y5fSJj2zXnmEBL02A/K4tAH3PLED9XUxFtPd1Pega4/ZQqHF+Y7K8dTaXlzRfYBSDgXE1/Zf/WLfuIOdUzf/any+NnxO2Ff/J0BmIvbkyrbW5SN85X0t0UYn9Du4nzmtPHuVV5y/G/2vjaK46v1/oT6J2onPBe77vs71Xe613zz7gxQ6AzLjCg7bzq/pidyQeSjKR5xWHkHGBmK/aS6bix7EPb8gEx7/ea9kmkspvGrFJCZNPxxHwm3/eM+cpkNXJRxAZ53aRJsj9q7yr3v7h+QwlC/Dbvo3TXwxxuzSqeveMJsnd2lOPKN/yeWFU7sCfKxG6OrhMwoAAQw9wKAkpYVaCT6IRGR1SbTSR679FMnMuSBUyYPEn5qLn7SjggpRoRGMQ0owmW1F1zkOrqWpRJ2WVTPdINuKpgC9y8zPJmxe0WwphWXxsHVAPC2242SxAjJkrintA8BH+nJBbtA8wW2hWW5pFVsYequxA+VDcdUSbxF4xJHE84L2p13A6H5u4LWQ62v2ymeqQFcM3gQ+fl3oElMZlvOZPsSqdGZWxTNGIn/EW816I4SzauDsqjmawlcQdcPUw095USByDrB+SY8Ppewie0ax3fonUJD+qx90GkKEMQtKfoZCqISkFcsymWovPAZb7uCNKEl7Ljl6SryB+vBBliyiCmkp47KxUenZfUozPDW9NEGD5eg4ZnWbN0j5z+f4R4xPr/40wutcq3WDEDuWGgKbxc6yQZxM1lS3OLYjhRJvt7kfgHLl4rCfLExERshdmuycOUjpHuUY5NSLOVIh+6T1JiTXy8AaYwKs0eXJzT4bgAIUac03/9EBX6qxPn8N8xVjAt7MrmVl0/8KiuKqNMyu5418xSB4AbsX6azsvqox4m2FTJSDcHYhOCf1ZUTCa+WZznGKnapptlL+hmPsIEbhrCQXgp792CUs5bf7hA8zlmx530d93EYJaoJndTau4+hgO4+YjpnvGIne7ptb90nzjGivh4AoJy0DUjbAFUD/Hv/kqQIccsu+4xaGyQzVoQzTXJyNUGUz2SBpSLM9JNazhWH8XtGhHMiem4EHtmKWcDMQspE8lmVirnHGrnqmPBIVWTb6rpOAUqV0U1zjegPHAaqqpfAC0wwZm4Ldd1l8lG0+oGpm9l3T9y48A3WwHYv3OddP86BQv8W33FeDS/OhFnUNadHJAlYY9o2Ha+foZqzbYiDcOFGFTUrVglFW9bsGJtP7YOtnOvbAp6GWZiHoVLUtQ8ZJjYbIcAMyLR0hW4um7NJ+1r8kSuBdSBobGXWVkfsr2dUfJ8Lnd33S6TumNtmVd39mDh2D2TE6JK9R70TdoB9NHdXtT0nj8pRjjPjyKGbF+6u25Xle5IdGlQtPcmsMGupR7swXjHsu0zonFd35FUuEH8/3e5h51ZGhtgiNcuANrj3W6dAGrDbrT4Qnj9dh2R1PlxTRD/7lqTxGFXXTduz9y+bdaxPOtasKYPhyT2MLeS7NjpOLEm+1cLFB4em3sPs7RRKme3XEedYXM8sz3ZDF++U0kxgQa5PPXdVClQFnsFFAm6Ogk+JEgQ3zGwD9nPuucHQJ3WA8cRrUD4kMAMAT4kqpa3kqLT5Y3TsWZNtgTzpbA1WRbdrToxOemOxFlUhHagX4jD1A0TfEh4LRBgs6nK8vahGHQ55mtGZOzWoja5nTJ8eOEexocTLm/5f5H8/3e/tpipmYNtAYnSo9zb1uTDy8QZ1WDAu9UyoskxOdcrYJTq3HXkzdge9CR18znr9kky7A5icd0MQ5am3XYphgiLgfl+tzIfGP9NQBagn8KWyofHJpg6py1DbEOfLAfaEUvy/Fm1B31feNFnEAve7XmvyblpEqxZTa2xZwtbG8pion9pmYSo2iPjWfnjl04Fpj9IPjDXLat+pLRGC6wsve1tebfj/98ItIBjtBU3TuplO+qiTlFPijou0w+AKADdOKt++MQKPqTjCCnnWISgL48/AUDpnpqt4tkbECz6XVzlBAAw/BOTEQCMx/+33zx/xiVMTjIBEqAAAAQQSz2kAAk3F/7fHWztzXgztAKxbgOZBoY19ba0tpfqqALmUm9DZ9rQR3UMQycqmWXT+5z3oDmj419TZ1tT77VXH98aZdomCnYVi+sKWfqYZdt+dGo9mwq1skyE13ZRtVhEbcarRAWZUpiJtVoyKMyU/TsN+ZWsoPYKG8cUFFvU4JOgJ8mbNMzfLlDefli+LRe3ddozcmzMtKx9w/rc2dbMAd6VJ6zOieoaGvltX2WqKR86Yy6dztdYeYozo75zRQzhetFbxFWJsN6roL7IGzTR0eKI3oZ1bIUKt2Tbxv3ZanRbP+Yx20ZtHDMiR6gt8CcS/ljTkzhXq7AZRGvu1XayRnu/ozXnK+h7tErHvMrCblcR7UNruyJx0BC+VSgax9Tmj7qOIEbfuZ7VKNPBX1/bmnqFWlnCymC26yRY5/RWEbXSI7VYhGDlsJ51EqwdVlc0DNeJ/F58QZfQYTTJ3RjNrEyt6Z4ds9KJ7Z7C7MBcIT+w6qLy1pAppTnq4IMHj1z7uctWLy7ZF3PvrNNrhnz4GuQQMmNLCqAx2TxybFFYcz8gCbgRyDTkb4MI8g9ZhUsA8hq+FCC3viUAmYWjgMIR4dI3EmYJlx5cJuEchD3+5ZINoiqVgj9szwACYAZFlAVigAAAZAAKlB4mCABxxKOB0CUAREJNI5BCpEZBBsnltNPldDQGDKzTmFBXWyiDQL0c/lGD1tdKkiBRFjYzJkyJsLn4xzQlVdzobtLEMIrZdVBqk38zzaym42RycYY14sQyMul/RANlgSs/wWqpVijRMpIhUxJN642MFpjjWDhIb05MJcMn6J6fJR2XMQlrc0ZWSOf8GIniGEkswyPjdJIYj9Jg1Ms35smNAyfeAjJqOMZ0EzRs9kyULBY=)format("woff2");unicode-range:U100-24F, U259, U1-1EFF, U2020, U20A0-20AB, U20AD-20CF, U2113, U2C60-2C7F, UA720-A7FF}@font-face{font-family:Roboto;font-style:normal;font-weight:400;font-display:swap;src:local(Roboto),local(Roboto-Regular),url(data:font/woff2;base64,d09GMgABAAAAACsIAA4AAAAAVHAAACqxAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGmQbmWQcg3wGYACHbBEMCvAE2SYLg3IAATYCJAOHYAQgBYJ0ByAbL0azoqzvUpziv07gxlB4DbUXLomlMJ2KzM681XQRJRADNl/GTXHdO/QLHl1szuTaToSWEZLMEjzfn7xzlXyzfbWxlrGnkXNAc7tfMaIGYhSgYBQtLSlRLdGjQnLQo0toGfSGSpSiEmXRYtBmHOm0ZmTJMKO1Hohqbu+qB1rtKnYuQE6iAxZMs0o6KWkfoWjv+++olO3AP9Pz7LzqbyIwEBpQr8EBzu2bVJtA58gmjph8C7KhpijuvXb/c1pwSYSuq6zR3aY/jEh4VHt+Ts0ogjsehf3/15W2uvNXtuRV4JtY5oS36MKdzxn7pExRpaikJ2kV6c/fwYDG5nFAHtPMrmPAnQlMACsgrgi5ShdXoZLLlARFly51lxahKJvoqGaE0YqmlGWpfk+OtbSOoukdc1hrEEZCYJRRPGi7j8/+6JKgsACwMRQGfGQhlFlDhAiBiBYNESsWgoQEkSQJIkUaRK0GKP2+QCBQwAhgDARIECEQYBgBsjBrrq1pCMf2CCF6wLF9iE7ucOxAO38vOAYDiP8QSAfsRfQCPJCABAMFZAz1JkQRDLBuulrFqlgSSTfLSJkt22UtaWYWFGfZzsfy2SyZNbNXjpoPOysFezG378XCVi+WixTZolkFyi0MDa9iJc8eU7bLSEaocg26bosPGDUBAgmxHyAQqjBwGDHhwOec8y646BoBwkSIEiNOkhQZsm5Qo06TDj36DBgzZc6CFRv5ChQp1m/AoCHDRowaM+6hRyZMmzFrzrwX1m14bdOWbTt27dn32RdfffMdQjyew5DESQqBDC4vcEFwBDcBbMGFEFz4wtjVwQ8Qx0k5VqjEDlWCGjRgAKMrNo63zGKbebxnJUFaEBcGlxKgzBzI2CCGMIwRjJovW/EKS1jGClbNtXasYwOvsYlt7GAXe9g3D9r2AR/xyfwc730pIyxi8PBAa6CYYhEd5bDEY4FkbJKKXTKx7AUCTARKsOdj+omdhoeJ54yji0WM8xz0Co/zokMLKdIlRSUMZpzksWfLG+xwAFjBhB3BYsOib+BcjX0ShAUs4hmer/UwHDaMLmIlgUq8pwqoQQNGZCBIM3E+NgWKIhQHa0GwDmzgNTbNr9h8w/e1MlKJXapQgwaMkIY1rGMDr7F5w2jqWJkSYoEdjgGCbWAHu9jD/lqx2CIOAYUoQRk5il0LWMAinuH5Whlp4qYD3Ss2iCgmkcECo20n2AF2sYf9O452JoaZxgGLWIXGHG/Jxxd2foyiHvthvA0RnSmzYIgN+iawxBSFgXoiNo7nTA1RV+kZd4XH02c+chVIMSgpBpTGHERBUCDUMUNYYIRqujCgPdfndMIRVfM4w+N1m7assbaNnXzSmxYOvbbb2qZorAbiryC+MMFaJzvodlVyhAwST78PRIOh9Me6/AAi2PgeU40bBQqxd9miA0C83b8mYO79x+Z9f1+p4au6T/G+/Zjt/efmvf8k52PWxxOjS+TWhP2+7ea8b1tvqzfk5Pv2ubwYyOP+Xvec79qLqupptwrsCDHWRh8wy8Vb2ONKEDxJVEKUtll2aV9ZW+R6N8u5bJHayDjHHViDPygfiQ0cvMuxZEDs9PbC3cMM4o3T4p2S2IHNtn5ERsUszr5/baRh93IsIZb6quN/joAxhxTxrPDyXUVgX7GC65LRWz64jxHi+x1vV0P053wexzmFNXH3gh20wT1a+Bqhl4tRF/SGpyQ2iZSg1OEnWerZW95rodVsk0dxEb9vRuw+vDc9apPva2KeoHi2zbr2++Yr0oV6GsQr1ThhLM444RoHwzmCjaMQHgganwswLhNBT5wsAmUqjlITfoJmy5P0GDrFmPMyb3mGJWtnJbc8J1+J88ru6lX9Nl2zZZ+Rz76zgCJmATvMDrPjOG6Bo/Dzo85xduc54gLHusoZXOOMBDgXYY4Q4ZzEOTMJzkCSHyPFj5DmbGQ4E1nOSpnj3OR0VMJDDjbDAxPrwzAARzMM6ZxtxgF6C5zAitOz5iySBV36Fx1WCHE8DYwOKwE/rowf198SMWAUytgdYzduAta0eYgFzzF4IS2ZvLQKZ63lIes24W21x9i2D50kcnpf04fJZOyMPeLEriEXpc04BS6LUC4pR7nKDxPmKKIc5XrIQmAzBkbPDjEOxsoYGRtRF6tBaKhASGNohonQjMARJpyJWYglMAkew0fMYsFRUjlKWntErjxclDJ2xsnYGWfE6QEEFzCcQJHJUIyeMYJuwiaERoa2MPzAJ1iGl+y/dLZciX9dLyY6WThcAd1q2ytzg/ka+oa85tr68n2lN9LR7Ke5sbwx31xHK9pSXyPjqDM/wAxqYsEkNOxB9TsBUOdMt+fB3p5w+b/lWQKEDnR1tgPpS6Zw09oHejt4gPHBAZ5EsAQSOgowlAMlas1t8MDuCSAzN4HNWaqISChm2yck+INATlOyYFqEXAbUBBb1iWmspRaKV5Lc6AnWwHXVCV3MvaqbYem9OOACtA4nQNyKQYO6oLIM7tClIsHIXZPG4K4WzVpRtGnXoTNvLXHs6e4Wyo+/Hr2oaPrcg4LgdBIolfQyYEG7rMIgzVqbUV52y2ut1dBjwDy0Deid52ydTXRUQA7E3xf+gb37RQFuyNWAunyj4MYNDV7EDAfd2rc3BpBugUmpwLrkYOGBkSJVaNai35gpbx34jwgmg3wvY/llXupQ3EO5h3NP4D7GfYqbl5ufW5hbkluJ25+7mYeXl/Dv////+w8wErcKlVrcNWDctHc+JMGkjzd8PPcR7hPjF+KWuGGMb206GUPYYxDE/4KfN2Se5JX6+/D/rv87PyKvJfUPO2bttkhhgg2v3WLl6ur31eNWspEmdyHgKVgeWBMHb2E6/64+ZbR+U7+pP/wYsRYseua5F+LEe+mVJctWrCJJ+K19pxMlOfDBR598luwLJkC2Dja6CJIceYGCDkehqtfgflyLO7CHeicewF14EIfgIfl4TT4BOBZPyhcb5UuAB/GyfEMCb+ItHIe38T5exwf4ECfgI3yOd+QXgJPwJX7ER+pPOBk/4w98jT/xF07B3/J/RTkAnKaG8puRFtOcrqfNlS00Zxj5PkIt5CdAc5aR39GcbeT3NOcYXdaca3RDNecZ3XDNBXo3XpmguVDvpijTNBfr3QxlFs1lRje75kqjm1tzld4triyh+V3vllOW11xtdGtqrjW6IzXXGd0dmuuN2WbX3GDMPk5zozEIzW1G/M27fXmdjYeK/ylt4q9C+okQbeRH2sQ19PKTOAW6NpFPWx+BhYY0ompAhWhDlvrNBBLdVAdU1GZFgPIv3XotlM2GAFXDHoGyUMXUVpJROQCk9ZKhGxGXvBzhgqfcgDS2WuCryWZVDdI6V4RLLd+hTNfH/FAAd90W33ALHNi68TiNIYig5UOxnzktRZZOAK345+0vG+9MTI6GsmHKOJTnnvxoE5oYS0Mj4CCp+9J5m691DHZx7MdlpaXxmI/IrWSF1RJ8Pm9zAfwX8zZzK8OsOcPUsHhgopLksZL6syVz8OejbR8QmTcuxdJO1lg0YeMVE0S4esMciydeWAFYUMhDsZ0aTVUmVjEKXJAol+3dfroY7WI6nFDJefXVfLj3+lMSGvPqefZa2GJCNQ57N056MFCWlqLrl4rMjLBiiwxTBHIrqQZw+O+scKCBcNVdY4fcsFTMDQI4oJowbskgPmbXUlHxYXvjgXDwdlkxEo8zoe2LtJDuSG/O+42EmEwLVDerTd561XCH0kU75+7Og7z0y5Wf2riVRO/tTKOepZMLJ0fazH2MlZ0YO9VjWbOrI07W45UFPUqMKAg96oSoOrxgHi5ykN0NrY9CKK4/JmHZE6pthPvOjaadnvPas/9abUSDFbzEd96GqU0loKo9xucbkXAk9oXvluOzwkHjL1K8o4aEgTzqDFE4V8FLy8sRDJOnFJ1fqCbD0w7XQXV4S4Ba0DILDYj74/pivdAGejOSt9JteLv06Q3Eyt4N88FwFEQ5iJaxHySKndtI/615sNetEPKc1xq2ONrV6QUoUeAY+kYWuooF5DiZb9OWgOY5zw26nSSBVm6F6mSmaOd+WzFebCm8gtMhrP+PdKv/esV349/H+apOxqf+Pzf1cd7SXLx3/Ovbax8mJFzQTbXEovw5jXZ9NWfsV2uKpisJpf2ng6GDlbYr7WOaEdUC4BSTdDzA+jgGJ//oss+NI1ZmZOhHcJvcDkMvciSVufbfqqEG90fVMaaTqpYEbbEPOyFGQoFCFftRwIotBe4TnmKGFDPtyVRkfL2g9a/YubBwpCJYzqk89/x0obLv2uwBixODju0V9TGYfWtMR27TZ+aPmhrcPzmrmAvKiV7Va62yeeg2CxNqV6B3gG6+hNl420/Vnsp0L947hcYOYUF0M3indNtOn6kH0+Mu06b2Lgm3RUqexFa4uKamnNHfXL6kvL1Du9uU7nK2v7Lcp6Dk9dvhDmTP4iBUMISi/DGmqCJEluqEUt6hglkdaItv/ywF7uSW+Siqohx7LkJgZulg5sRlofZ1aX6n3vy0Konw0oCHLe8oHBwEFwBXhsuVcxMDLqi/lC1Fudwpgwv/Kn8qiOHku0kbt9c16AhnxD1CnkoTNvUtnTMYqHuQlgMFYyMb4TdYsYTMy8Mdn4J7uQ7dJWFFAx5NlOr+XlVv4Y8agkGU4kJMAql91054Y4eYNjWelw15OllF11adHro0kOlsKj9YdqjUAzifeHHXFa4X4ZPu1reE7z0YhezKNCWCgtwy6HTNLKWm/4vaNRjlxhU3glWJx0C+rtwaWttjQMgdIW1qwL5Dh9zjPq1zS8gVvShFLh2zqtso0SZypUfFywbPNmxtyXPrTqAM8Xa4MiPEqsBmivNxu4oZuDCdwHHcfMhpe1jldVe6n+wUY3Al1JOZISR32SG0I9XTs083jMshSqNAg+QxylnlyoZY+SrYiVUfYBq7EOk7kH9ii4aQR6QUFxS4OzVpY1GqsrEjaXGoF0sL5dLhtFlDqAjbWbSzghA2qADpmnd5ocfdHgUsLkzXDJnGrMD1mQx4s7uhFgSZ6cVUqv1KvNZfmwRf2/q1S8fe4HCmwMSVXnT672F7ipX9R1F7/sr/P6yPIXJNzL5azNkEk0gZXfbWrsqLdAfhzRZ3MLcva8hZF/9Xwi+vetRTWwgN1dWsgL66PTZLhrfX2SRDjJILR7TF4XwQh9s5RZNp2RAna+TnAiIOt6W5Wgk3bf6m7GH6QGK+DRJ/itwMzRAu5pFEAdUNyVNUsbxPyKvHUEKjfhWvxqYUaFznu62/14l01p4L+qGWArcE0joajrWGqL8st42GyVp05xYiGJ6LSrU0nmzZzUm8sam3NtYMCiqFwvmR3hWeuZO7KBtelS6qlE9Xj4x4B5czHUOPZMFX5WRFAwooNLxjz9fzL05AuR4+5aw4SO0gN1Ah4ljZBHp+w3pD6sMhz6EsmsS2Yx3PwKUX+TibC9YMprOYP/Ezqsc12iLSV72b7tZpGfPcFLOj8/jxnovpr9vyhSpt/6LFj2mb6FO8zclttsw8MZ3KC2eTXw6GfpQ3eZDUOea8x+uCIl6o6sRQ/0GXAjKacMzzyOpWFSs3u4K+6/A4u1XU794rPExtFvGiYvlCdp9ToIUk1L/uEtdIqO9TuklbOGHZhA2abFkjsYT25obROmVT5nTUXNRC6JiysCQ08ijgBRR92tpzFgmV5/2VjTr5EEaes++6QpAk5odzUtBGuPdKy0WkXti9kwas4GnzTHhdlpMiL4vqXiqR7qv5+y/lY9HQAsQEvHgMStoX0fROqcmXurYNpzBMQ7M1ZsHp/jCZ/ffsGgMBYWhS3uDOc+OxglB3cnbjviDR+M8N1rZvxTZpes6qS2bblIYYZa638OBTvinakght6Zqj+r3qo/mqPieZZ721P6fjM9iwaZ5D3jQbgh1stExeS1jbvpxujPMjgKWMoJ0sAUvHHqMceMAY9LoSjVglIlZoMC+qg/lDQ0y5Blc8PMLNQz3cgN0o+KSTN+ylMieU88KfQrxOIfnq8g6S6JXsUCkn9crJouCVjB2epLxb2GE96feaqRmlvPj2KsWDaHSQZVREgdC83NB0Zc2DmVqOV7lLdVy0nae9afF3iEERoV634oIF7BsDdUvdGiaedhP6c/qpbLNvx6gpmbmk2NwIwOUUxNY2xMbXNCaqxNXWRsc01KYk4bRNHNVVjO2M1LSNnHKMnPTPdFyLra3H1dWlqJgQI6ljdX4AqRrZG6nrGDuoqZjaG/745CJ/2Z/oIg+OJFf/1hp/2wB5skrtcF993vDdeFPTHC89N23FGMXo8rr/Zd3UjxVbopr8mJLzArK+qJ4d4m1fT1cbGhNYkIY688Y2gBygr3hN0FhW0cFUU9/GC1gXMjm9o2ElF+K0N4LLDwyilPlCMr1pj/EnwDAm9bhhg78Nn0+cWnDLjgGIKa36F3wwi1Xl9ykMoH1ArnLPmriUnLTT4feJVd03CiwGzupnV8yu5F3xxpsU0lue97ySL2Q+yhJmRT3dcSTcHC68Zs5rzXtCe7L/D7qBBvE8LipWMejkuq5MJ/tHwHTqff57INsKkImtAldbiGlACS/nx10pA7kqT+Vlqd2tVRG00Hf55et7O8uXURK9TDuxi+T06AVk+fxu4lpKUtwc6jW8oM34J9azj50EYVr892vekYZlRsSkTL/kgpJU6iPU9xePd+bbnrGfj699BUcchFJikNx4IvzP0xN9qS1/Nocnt14M2YS2uV4K4otpvT3yvy+C5W6aX25SJCktJfZ2cUIWkgLidlsT+RP6NP18z/PDH0fPfR4fnTCBpy/UFrabtC6luskmjm/h1tY/oV4Ofl5gu9fKFk6ODIzOai3OKqPEhSiJ0IHI17ZS3mfo/98na8TY8QcbjPPrX5gnZ9Byf1WyjOv1MTNkm3LfU0ZKnZWvQiLSe3LzMnpBwUiVr0t1iC/3CGfBc5AdEfEkNthd8xWI6wu6/291evbP2r3AqPu+ArbXvBs8eneeDQ7sPqP3swktKvyVl/8jKy02Jjud3fGYJ4nk7x2XuBKXuJKUAsIbURJKFQa2kbQwp7XELK+tx2GJz38oHBZr0CHpY86WnNtEf33L/tOqxPeMgXp36euQ0GxaYW76ANDF+v4ZEeYJyxLmCR0BmeqrCpUKcgoVCiDYnpyDeiBqF5olahc2AI6FSKBgkqZBDMnA2tPVM9D1Vpi7BRWp3Bjrvj870dZa2eDhrqpbMCA9i3/yeFR+Y/z47HBXn0idqGGyvnp65jvCE5aaYNSDbFe92xd1ZIUvEN5m+6asYjsIcTfztR9dH+sIYvjK+J3swT8wY955qf0scfGJoOsv57X2WsZjjITl7rD9g58Oqy135NZ9mNN0802lvZzOWOOvfAuXCyjYA+mZcPvEsDHeUUo97wJltP3owfd6aAAsZWlvf3jgll+Jw8UA7sTeqJG/q+/F65lkSEXJoXSnbhTyRuZFeASneJBPgFb05LG3p37to8R8xVIfZwjT17S+P4zi2hw80lJbnNc7zfo7bW/0cHtFY8bxjh4up/jBmboyUXhoNamrEv+dYYRZlBAwsfKN47/Rj/eEBQRkUZVBmOXSUsBSssvm5oNAeijWIXYAsBRS2dgnB9Aa6FdzxbtQ2jlq84SRidtqQq5cm4fen1oYEOXmHRxjAQj6usrd71F5TVVFr4QNdKNgsHKlUk/7AeIKcRCCvniIINBFGaSb+bUz8+Lj8sOBiMSUmKCsXCDBsZq23t0bb3CaNPVeOoa/+spbcC1pL/ULFhmEY4coJ0dqrsKf2ZVfZ3c4uVmcfXK+gGB7vavT68wr8/b2gyov6pUvzs79oJvVsEiWpaujNNPXZ4kjT11nM+UZGptbGerJcgsPEPQUZ1/3NCfqq/vv2VNU2mMW1B9wkZDE/Ng4esDy+dE8fvO7cYlZUXVGUkx1cTrKEXQJDXPxAsFG6McxwUKfVdh19e9AYWgoSyz0A/KE60EXlH5Nn/YX0x9oGmvZ90Nm4xt+dTEx61GMoJEr02Tm1jlyfXZ4QgU5H/jBKPHbzbXJgmbC2lLetl/U45DBuLq8wvSycG+zvFA3pyeh9tXebRzz7/LQxNTx24NxQwXVBZVhi7n+oBZAmAcsoR+whLmyW6B+h7ICTwH7ipXn4DiCrK89eFnWcreyurml7BUk/zuGaZvlGaU08nRRZmBj4nj/+WYC+kcVe/aPc62nhlnObA5yHaK0Ekb4uLy4vDj8+Vq5sJQBOK8YY3ll4/HM4vKm03Xn66+fLT+ZWQfH+bCXL087ZJ4eOM1P6BKIl8iXns0Q3IxhL/ueBtxd7/sBl9hfEAJ0vSGAiqdQ+HFXKAB5UaF+FFowmPkTaxCO1fTdX2++uHj7wmITSETl5+TnwMmal+UI3yqc/0+vFxXfUfGkM6UBaFudRnZ+RF+HeAcVmh4xb8QksG+Ohq7+lMtnwvYl1aTYjFdlJdNw3MTPDhRQ7D9eXx546p2BD28EHFBlvJOT1y6gezH3M58m9eg0zBs2dMZnPwWZGIfsIDqzVoxlQN2Ig9cyhh9Ol/l9fZ6Q0/3hBV1LK51LOsk1rWhi4Exf4SOw5vu5c7X/gVe0CBVw0Ij380oYu7cr/KMX15s/RaLoln7UL2hOzJ8BTY4lDu5PvJWhf5bSo+e2DqG1ltHl7SPJ9gk6QvI+JgnTBL8Jn/wq9qxTLHSpCUXwrqmJ3OR9l3q/UW6NPve9qLmdiz3xHfldE9jw/d26ODjmlYIPauCe/fFxNmDVQdLYxiswqzkhLz/Zyuv2wINtQeqnntzphFa9sn3DosaE/BngNOgJzRs1QcU8pj4Bfxdvb/MEGz4tNBVwQAUjqiR1/NQVT48I8zAPd+DslOwRPy4jmPNdRFZOwaCPcOqyl6hY2d4CqF/8gsNVr7rSPlDXYb1meCHgBRjl3wjAUSOJCQpMmXit1yKq1WwT6IgNXqBO+LsSI5wTQhmcJ2mju++6FYq4qJLA0SmJTQgrfmjXf7CSYM5fHv53JYm8zCb4j7ks+NdaStYSm+AfKr6z9kVq2p2l8fb6pdSkuhcgv8H+ff3K4KhPOD0VsC8b8EE+KY+GN/hwveie4rnEeoMcrH52U3zJLFjz/d86PzrgFUXn0QjYl1SMdxBt5L3An146Wslc/B3jTIJJehWpdB4u+OXkCXbLFQFVshMVbp5RJ3YfBHhOa/joH5GqMdD2041f62nt3byfQ9OM8rR38jVRUXRV5JRpMtIPNsl8d5caM27hU2NLxVgmexBdIw3hSIAcboI8US4vKifPHZanyqEcHpGbXD5KHsWBUJS7e+RMS+dDGBmg9n17OK9cykGVpMCHX2zDN+4fUZJMOxIneR+UKv4rtBSVo1Myj81PhA0tksdRI9nLbHCRdfhiLny7e5D83I7Lg3fxZ87PnGcevHZci7DHwjp8aey4yLoj8xKvElMeh7WLBo/jec8jZ69OgEReSL5FIp/BUc1CkVgI8qQItm/eG/88nzn/aRrpvrvWLsRHnFY4NvG61rTW7M0kivBUHhSZcwH3IBcIvybkcPRL7zoNOvW2Hh+bVuAjUgTb1qB39mA+c/7zxL3O9buCoP2Zvoz2PEn1tkh9ttqZi9oGbtYVSEXP0xTNlButFT8HNa39bKChqR7mOYeYcjXD76OhFuOYHUxn2oqNGH1IPrpF3W7nGT++dGb+0QkK9wZ1NZerfw6bOxGVolfbgCfdqyIZ82UowZdqBQvGIDZ4uqU7pRV49PiXvYcPlufa+t0jw7xdowsj8OgbpaD0wacsdmaqLDo+IZqUnOpcPDHpXZWVF52QlVsaNDxSFpyTH5uYV+hZ9XjEqSydHJsgiQoLI84Nh2wMh/rMhtE8I8E+vsNBC/cDeDqOCucteE1hI5TuKIUrKQwqyIbL3pGNoJNV/DlkMR+mHX5I+0YDlczW+1RDJQGVBMd69Nimz8PryuLJPk+pSExHXkniHX9bszSXaZmua/1elkXetC4TeYGbCY61DOMr79HrT6kRdUUp6RVEe+XrOiS5Uzr1Kj62ElGnGs0Oqx/TIag7Kt8U1YmTP65H0eIwcFQ2TfYAnJHFjob8un3LRQ//T0H5k1Fhc0VZ6b3Zbi/Kg9niiqbq3CwHa0MLa/ekMN9g9yRrS0sDp3+xzeVnCXfk2O+Un21uLOUh1FZWcNSUnSE2TNDM1Mei1MZoZhOjHSbq02Oj6pNdJuA60GakYRQVFB3m7F0b1OwhkhCurC0qXMHo3BSdFF+YTpK2O6prEXrJ5Bwt3UcfTGWFrUFqPYiGj6+8AWbPsfPEMVuuqaKSo76Ep7mphJuj+BHU/0U/TY+zsD4mIuy2XIggFMoYY9ky56jcrDZEdOvtEhXERF5MTUvaMRmE3nBVkCsAf3bAJafYzSWryNUtK9fFNTf33yExtZsSkmrqYtfVtT3U1eB8trFHvkdZXVl0fnRpHSfnZ8UCRWgsMvG4dNTDSVnwj7TGTQ2JUmJtNr4w0svFzc1AuzWDDGw5twzVVaQFrmzdlFAXulBqXxUanlQc7HJd2Vz+SM55sltDeGLcnQj3sivaUoo3VaWEfYsRU+AYgzVGvlw4JCIZAlW3iv3HZfK5aZJ1eKIlqTFbV3Uhmt0VIOEN3+L/Li7Q/3vLDFgKZD3/ZbKqXrtLTIeTHQXHsuzKfcg4Nx1j4WuXf9rhT8fDflGp2TPxKHt/AtDVOJT1DRUV9w3UqpfTBkXuD96pwjra+hoaWBOdtB1sfQ30bDwRW9CoA9fzkTYBPQMM9pyefkRPx3AfCypSsfa4Z2ThYUdL+V0PH3mtrAHpQMpduVw8gQE+4MdiK/LEduM6ltJT0MGxqJ2veogwaMQe/PDUEpiqqumoaAeGqmqrsmUtz9KMuu5lpfTrapti9GyL0slv7/e7GOyV7vJidvitmrSyf4k0hE9GzuGLXeoZxUQnr9p07GoFdZm7r8fwGvia3wjZl/3c5MJS+A5nv3IJHIwconv7miBBAyyFeo/3UHp5XnBYeV4CgBLVZHpym9Y50l4ecXtCWVjduo9slKBZPtHWrdd5yeJiESvpkqi7j5W+iJrNvSzjJD2z5ElDa2c305d8oL3GiU3okDNat0qr62vOL91oWV41cvcMdXFN8dTrIZA/dvb5teoL6stqtxi7zr6DLb/nuNaWB7k5wRr7NxJKg2/45KmWqh3a2/RHUjwNtFwtRdEifjmNbMnHTsfFZ4Gw8qEfdIVWZf5/npFJCa+igldJ2d7vJ6Ojq4N9rw2lpr7IVrXvj4k17xYQL9sXvZt8L0rVARfLfJgpWdUys/JBXWrtcFREwXovKMyavu19ayMRHKUWYhqqHlz9mdAa+qUO/bOWTPayuqCaHe70ZKhQrizs72pihosRr1pOuMPEk5xesSFbL+OuVdXkuhdOnd3GXgatLgoptS+poEBcV88/XQVVzCd2SDxII5okDXwyQLeNk1j1NzeFBRpFm5PZ/uZVhTdw3PzZ8EuuNAizrCJ33NeJOd2DxaNlcBOZlJgb2gZb/orgvy8T05Me+ks7yDkNE32mtv7u+E/iGqpHSPeC3NUiEmO/JjzxlnWQsR/zIS3RXfvXi+8pf5aSVL442V3xPDmhckFhetEht+pQEsuxiMRcEHDcZyjMbchpKGT6yuoDvAFRJmH+FpahAc2hIb2hIepG1xQUBQUU5e0VlASF5JVB+M2SR2i85YUOW5nmExfKBMNw7sEpVmf8bCR4Tl8SV7wCbG85XFUsI1FZib3+t2zHgT6Hy0XFMhpFzuiJAQfbMUg95qJqHonKDKQ6Otk/gtTLLirmiagCKrXGw3526UbT40ircbedBnw1xsTL29LC18OKz8TT09rKy8sUTvIhqWn8sRz8cWnA4td1Y28WT0fSkHSo8nT+p4vi/AoWJZyMQg91zNzxd+LhKA86GfzjxFksY3t4e1J7RDs99sSpz26kUxyVHrxeNZQJbN/tiKikKJB5w5OSJJKSmGR+1IecTk73OWr+ZTz7ocXDHL0YkG1PUcxXhIkE885adk0+uu/TKH2qOFzQN8jFzd16mbzcpDa4iwtjHcI4NB/OuiedIbXLWyMQmvt72pwuSgP2OTVB3UuGXoVY4ephvUJeeckZymjKokp2raG/k3HqXg3yJH2pz1Nnj+/wlNyHj5IvrqvxW5DUo2rzcjPKk5HIx4TsSxK3RWwspczlrlpoPjKfaJxQ78D0IlwsO+6XpliePlg6P58uUarKb0lLKW6C8vTqopaUtPwmpKQEN5Acj+7oGEqJQ3cCfiLufw/pXHSPcW8obQjCOgVjK8O2VnkYwTtGMHMlzupQg9wJ9aj0xJDw8gxSSG0E1rGY3j5aXkVdSVZLU12tEXhjT8WyMyJcL1U0iYGkjCD/ssK4lNo7+cLTy6+fYCIyhTUvsfIa6hvekNHSDr+pATU0S1qtZdZojDskKwQGiiVLxrqVB7OVB0eQm4Ka7a/AX6BzXkBEJF1P4qiIloTsDXUJEVENicj6dZtycH1T4Oo7n1kevq6iJOcUmJoUGVJVHp1UXIPK/ktSOWxv58XahZZaX7gpbaCtIy2lYaAor6YF499A/sDtfycMPX/pn7+PSuzwY6fiDq19/AdgboQ/APoI8f34OWewOh2HbYQEL//nX1yelOKfxytGQlyR3EA+bolXu6DElekIfMgpedF2NbuOFBYt7dlcTjWnfJNShMCNtRLMxW668zZoPi9XfeYJcGtvOeM8fwiY+UkR8IIRUCpIN1idq2DthERl/SLFFOQ+UDi4fOAuLlEpapvarnaonayL44B9wtz4kOcqJYEk1pQckPqT0z0anWF0SR7SzU8Jk5x0kzw7Z4ak2fjOLsxV8jspQHJASk5Ol9PDzQi7kqwUdckBRJPTQ66HW/hIIWvZbynsJNdP4qpw43qWnG5nZIZ3S9pGPh2XkBJYUy4lsTw5INGS0430cPNFysizxJRyNt0CyDxIO8IxetIPLntk7I9xURvLFE2yrxilCtnshqrGR4GqJg2bHBNi53cPl6UjZW6P0pn/wQiY7tKc56/JA2EMKX0rE7tJA9NsSjVg1k0Gct8GUmR7enawAmmGw8ZVM2bcZCZ2zT1VliGtpNlxZqW5bWUxZwQMpxQMQIGrrVhBb/EO0P9U3RA4Kb4uz6rkwmAlGZm13pJfHEvHDRWU4NLFCLRz20JX8DS8L//71qc6p2za8MoUvY+eKJcqmPuOg3TlyqQDuCC+zqQZYrk4faXQzWkpba+LAt7CIqgDjOOmdpMMcQc6QXk6VsuZydW3C18+nZlR3U6ANrMv9tTEu23vN7x9MgUNlPC8jWgAde2+AlTS/V8D6Vh3FwrIm0GPLayNheLOfbr8LdOG0PEKjN2Hnk4cms72xf2VuhWm8RNb5H5jBfXeoniegLPdA1it7crR/IosWA6U/narNivB5/pgPXU9MQbSPu0/fATyMtD/v4gFce/lr9lGpILyYdtlgRMwIj2AVqm+bVSifpbqgw2Ta6DxR3HAdk3nyuNvfEol5DMwF61cSeX5T0CeY9zbivsFNqzSv+OPDgHAYz/7VQBeX/q//Tr+d2GowBA/YCgUQAL9AwdMAEPXy2j/1Km5c7Wev0oqR4X078sLmkdDRSZLrHuCaiSvtsS1/uSUnDjne+LzQ/yF6ERNop2ynMyi+PJxGUUT4anAD2iEFawhiiLEQQs+T7Rj+oCU2lyo7FSro4J6FamM0+hf49bEnai/NTRRQe2GhpoqgYc71E9o67yCfEVPdSPaMKGi3bf3R4Ot5SiBjan2xHrWPfW1n24dI9gEpLbDTvSPkmkYkKiTWOsE43xaVlF1Xp4Y+mJKrpTA5BAS1ZxnjZBaBVjWWDZVhlUeJx3ckG11kZo+Sktb0T8sWPSjkj4aUqrbUxMyWCjHqWxyl4L7jpair64SyxqeHhoule8ulEe4JDBNxnLps1bEHM1XrHNFKdWWa61l0TrxwIWxph3Z9T3zYsqpp8aiZMsug25VL7d76uENNUrPf8XUm+zyAQW6Y7I5RlhNVFS2KGYu2ek3f0ShpRoPZsCttsSoJ0q3J8G7PKLUD41Po8SMxBew0tTWISm9QdqVIrJmUqmJLGqJ3PRLcb+SS1+JXRnRkFuElDFhlROk66CAK2yrqurdkJEK2coNCaR8Z/M8l/yKdB50rqhdcjOXk2/xwMM1K9Wk8gGxGsmmIeBcs9RXL6drCune/NMygCFLsXQ0CAV0IQsxWMAYktCB2r7SlSCmE3OnNyt0jKt7QQx02OdnBJlYg4aix1Z6gXk1YRwBqcQ6YlbdjJhGLOuU9U2/GtPk6U2l+xhWE8ZxlUjsIqDZSrOYRmBeCPAXbzgXQ9hHyiiQoeaEgwBD3IKGVHoAO4NyJIKJ9UgUFm6N6NZGDIzE4hc8EueEsgfKwhCNIm8+QhC5cubCHzchAgQvJjcV3pZyD05rVePFwdVA3odHWl9V+DUFTiZ2QhTYI0dX/cibPcGfZCqdBfBgl4OxSeTHVXgZxtW5wq6TsmOv11H27sqBTFf68yHp2oUFBVfjwIcqHbhwcrUExI5rkSuHDi+y9Kav0aRGkTJtBpRdsaJNILE4vXDjqAIA)format("woff2");unicode-range:U0-0FF, U131, U152-153, U2BB-2BC, U2C6, U2DA, U2DC, U2000-206F, U2074, U20AC, U2122, U2191, U2193, U2212, U2215, UFEFF, UFFFD}@font-face{font-family:Roboto;font-style:normal;font-weight:500;font-display:swap;src:local(Roboto Medium),local(Roboto-Medium),url(data:font/woff2;base64,d09GMgABAAAAABrQAA4AAAAANqgAABp6AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGmobmnocNgZgAIcMEQwKvFyuQwuCEAABNgIkA4QcBCAFgwAHIBuKLQXcGOrdDgQJ1dnoKAQ2DqA8diCKisF09n+ZoB1h2u8CBzaohVCEaEVD6ZQMOzVN7qJbxURtyZnoHE8x9FL336xJ/tR8GsOUK2uSSqFeSjwenh2hsU9y4Xn+O6tzX72qSiVpEOlJQxhC6fUIaw2/lkZ+6k97AXCqA+I7E69YIVIxSfrPez/tEL/NP8qoGTjFhYvItxZ1ujBqgTZGLQzEnJEzKtDGnFlYtSjjidE860/083y659tpe2fmuU1WfI8zCBAYQQhmhZZY7C9GbpaAME9Qzm5379yb21e7GyqNQm868KaAKKqTBe0HyaX9uLQf3OD/pv1+WrJz20xCKc1zEBJhXu+Rr2avyR4cOQiDR0gQGv6323ie/l7TnBTxBINx+4VuQsFS8U2oVLd8XLEV0531/3+t0tbbP70DWAsBXiQhV28s6hjT//+ayt769Qf4dw3WhHprmWq7c053EG1YRUc1hKB7o2ZyPKEEVhEybuNGGBdQmwirYhMJ+Obr5MmhYmydqyxdapyHu37x3f591bu+Cm+uIEFCGsJBxK92H9ZZxRcPzEKTZhj034KgCAUAJlF4yZRJKOAj+PkJFSoIVWoIdfoIAwYIw4wgjDKKMNkMwiyLCUvtJQgK8AJeBBDAjV8T3fG90YfE3/bjB0j8a3V5D4lfetMnSKQBgkECyOI/4ydwAQIIGgUo3PYFhAANNC1LetbD+35/upNO/DScaf1pw221jOETcD7+tfDpxIxIIMfz/VgU5rhaM9Pqtnqy9jp6pnVqt/S4vq1cSVsS8mm3qo/ffV/u7VF2w/TNhhkjy1nl8yXzH8OkBIqOnTrj4OTi5uHl4xcQEhYRFROXkJSSlpGVk1dGIFFYHKSppafvk8++++mXP/46d+HKrTv3Hg0MjYxNzSysrGF7escNcTTE07pAS8JNSDSi0AoiBtSg85YMGjJs3SKmZStwTHzDDhyX6LQNDhRPq/zTz4HmhZuWqEmuISVTVlVTL0S9KGDCAtLS7z5v3qBEw1ZNYmaw9uZ9w5enukriwKMnlkCLts/NcvVbT8lTTqpq6oWoHYUwwEJa+t3nLRqUZBjLJGaGxmfWnjfx3sWXze64eg6bTcSQa0JJWVVNvRD9RUkYYCEt/QzaMpHMsBBeiIsVgifvjS/MEdOyWUxj1oxhjLF3jDHG2M9ojLF/jHHA5r5nGIqNO2nWKT0HFriZ+SVYHhfkmVRAy4IevQvcs3gyDHAs318qx601BK0l1g0iEXHuNODvEiftVRaLWU97M5Obia6jraPtp2ohw5SUV88zqtAhmkE3C/mGPTrnVC6kuwlaPHZVOi6Zw5y2IKZqbbgR1GidkUyZAqIitVC/GaFp2XXKadMcnnY3yTPBbxsGmlNpXg/nwKCGYSnggwt4JsEmTYAHcPKomiyMFkxWQrxa4DCDTz6ERa4V9nJZoVWlKWVRVVMPUQ4lmLCAtPQzgVmWbdbevEfcsFqHbLJoA240essnZiVPWVTV1AvREgpoTFgUrgEIaUHfxMzam/dpGbJVXeMyrcMcWcH+UyNfp0pfCY0V0psWI6ZGk5VPFlRWsP0tSmZQNBsICrQRCtqsjac17UDa1BTSLvcFXJUQuhG9MsaCObAC7SEhbVe0kPEWStwgLMBssWc7uXwr8INKiGPTC2HUOlRgh6SCcTZNQdeczLBlhEiJTEMrfieS6SuqTqXnKLgrlGCU7aCiFSg2ptFfbRYfl9HnJolb3yRqa6Eue4CWDNISmeraLPn6/w2R6q4mGglAjmY8/XacOsI+JuBz9wm5WeRZ3nr627kyRsLyqUhSEekItlLqsyFXKiFnKiFXsic3OdsxRDA2YI5SX4E59YAQqAxmkmigZYyV7CUCZbKnzuRpRm4Q7hRicF6+2ZkAgAtoKhgYYBiCm7bqbSHZeY0+6zxoIvfbbqB1QH35ZgbAMMbor3SP1kTv3iqZu7R/YkxmwxVtzT3a4F6OWI8zBAyiLIPWJvoq8GRrw2Oic/Dee98yOn0YamgKPEdmNbWIcVk7TV8ZpZhNC353V/LtdYX09JWBSuvOBeQEOqNGM7QQr5uoNJfbHjR4e6aM345Fy5lRrYrUbIPuWPWC3kdLD1zOSqi5+3jM3sATajpGXe9ReQNm6ffCkisgRi6IRr06m6pR19NJj4ysxSCzwtoH4gNQN3MV5nKZFQGXsSFqW1c1oJbBkv5A+thOng+i23IKyI+d7qyrbXLsnLFgrnbOX/KXLVinesOCnXroXkKW2WLuCXTIMRFH6JhtAoqksI0hi8GiY/diCSC2aAloYos5uHETRxF1Zvq9AAHCheIlBBMbMLCI4CIMm0islItPNjy6fIa5XChJEmJ2HFFERsMEGcp7DAxC5wUGIDr8ShCEknwWKx3ZTfkmodU+cuHBkiQdCTbeu063hREbVLXzpesaZSqwbJY9Ozvm4w318fUrE8yTVQ5JOz5f4yVXG1DvyUX03tH9q0VBS8h4WfwP8+bkJp1cDdB1dUq6dAZskQwtx4/HDeJF8yMQKZyB0jkZks4Mz/QcyOlvr+D/0Ojc+NOVEdmfU8/d/zqpnMA/T+yM/iUSfMsmgV1ld5oswxSZpmr09i9umFXfc7Zcc+S1PPLV+Rq3bYGZijtAL1kCn5I+cL/KvbNVGjVptlqLVm3ardHRIehUdCl2K0bqkaNstsVW23qu3Fjb7bHDOF6AtIOIKqDZrgo7oaUNJjdpVUdGkzbqmNKk9QpkQpsbuoCp0M5lHiCrSXvrmNWkQ1ZhF3ToBjAbOgw+7ckND58zESEhl7DIJR7vFhW93i/uIRBuJCT+HDjgP3js9viJwMmTw1OXQ1eujK7eyNy8Ob51p3D37gR6UH74cProUf3xk5mUFEEkLqSlSRmZpawipaS0V1Zeqah0VFXXampd9debN6Sepuablta7tjbW0fkYEvqJXuz4iIeBjFec+TCRoHChgkFhAAwCJBS4EPYWeAYgSyBjAJgAgMJAZlLuWcbYUza8fEXmGjllRKc8z5iz5maWnNBI8HZlkeAtZAvZbv2N8vJFjD5HeMYcKsl1IogcntO9GFCwoorecwUQApfx4YtHcAfucYJc3vuEt6pHAHyLqwQcgOHFgyTwmPNgUNbKfUCphGN8jJS34pctcnx5lxK8enYfbsNDxhg4oXx4mnOdNN/oyVlsNvKZbZKmFVZq+Uq2SKWPAjs58QJ/46mTPkCi9+hQISPqeIhp3dZ21GkoGymcckySxmBfLWI0uWwqrY0XVyxSQKWQCxJP6lDZfXNzGSiEpwWXqWL5WC5a1lalRb99g8PNja9u0q78HmP5IlZqYdSmkqrD4Cyg5j6CU7ur6bJm5YjA9rY2cnLKZyfcnInikdQ/9XjCMGmIvK8qdUKZhECZV65T5qkPS7DhXXTxfQzwd+FqSiTvKTOLY/e6nZIUO+VZBDoahcwL7FK2sXOYbtjfKwZh7S9svSoIMS8792xoDOqYFGfCIduV2dNoqMXBPf1Pj3Q4HHI3YtZTSM62F0oJxYvkC9ghA03q9DytB9WwtrLleFtu5qMf9rkPZnkouhWrjr230MoB+rtZZyqnm3dtNsWwVoRH0dvKbkZZfPtGXfYYigr4ZSPv9WLYb3tlYozCMgFvzEuHaL/MbtvMdh0CFxOaC5ocBflzKhhunczlqM9dRCFEP9qNU8ywAlwwrIlrH35E64tWZ5Zu0VcOt2jWMdt1pFsZeGVuMWCBJ6xnckoFBWrkpGXl2VWztJ5cu1r+1Sz9sNQi3KBS1Bafpux1IzcLY9TLv6jHuDT1sRK4/JZoEBCSq0mydCDXNqIBlcM6obRF2sCt05HFGPNae3Kqmig0yhNuY+1QP2VNHgdzCPwc4MtO/UPATdpt3vzZEZb+sUzDyWbhUjibAO9VJq4Gxzq+FEZoD81kPnWSvcZiwSfcTFtJY6QdPp1X2LjKvhLQDTEdt+Bbx8wCUOecfPTNFAVy+oCmJSV8aeLxQyd9weAe5UM0KpcIua0ys55LCuiTw22RghfmFt1cocX3WCVPjlQP/RaxysBf5yylxaqYpYjFCQKF4wR4lR0nC6kBr8jas/R1xOBcpUnXWdhHchs9r8fF020RsFD/Oz6UtPX2tw5l657c/zErE3jcof/7adpaT+ely/ktcAmztfqzNVTKGyB47Kpqk5mHN6qO8x5Sh7iMxSF8qlJyA9f0Iag6iAdsc3AkvOts/gGAIMPZbdapeu8eiW2Mph9o2MqUz2jWZCelyck9LVBeJD2F69Nc1GNW2J7l92Qzo2KzzHi03AtF0lplebRkychVHwyNj2ELRpPcelLOuKGyjvyU9CekdwnZgzDGxJPb4cH9X+O09xpwPGUxlKUiBEVTzoLsyVIQL5NsvhqFwgsofr4yS7tOzJKHayNpga6tLwvGrEuvLsLUnHItEsVFMstAOelkoyFLwy5q5IZaI/P5ebQfhZYO77+VM60AR2/FGGoCHKeQuwxlxWM3dS0GpeWL0sKA+1dVb9iqcB4zpE8ds6VzGRzOGiKBCICjI1uotlayebLxeVtJV7pTr/VzQzSkg2DnZWs69QX2YtU2xdNM38JBx30TKBT/EJ09j/2vkCsj+DK607cv9K5gTunaSZzYMlsoIexzUCZ9FTXrtpi9lR4Z6SUSDx5afdZovKerEuDoxT1lYRpA2RWwffx9dgGOvqvtr80GyWyAo7PBJS86e3cQuzEGb033sm2sbCyNqN524KZNRmF7zdPN88/P2b120PN4y/kL4FAbXUJaU+KG5tOWpcfF3SRg/XKJET1x1/G+KgPKk2fGwqWE80uL8flRj8CAyuhRmTBdsnBu5GMwwsayKlDZGZUANa+2Wo3Lyi77f1PIwUNj/O/o4vrh3cEx8aXdC11XfAJdqJTgAGfUbfAcC7fDO3Bzjwc4E552lf/El8Gm36fviN4VPdX8u3LwBDgSMdjFf0kUYPZ4Zayp2em9VX4FoHIl8qmaFllX1VTlMfLGNq7rlXP9RD06fzbsjIYQulDOReZfj4ePj5aGrKYKeC0yc/gY63Ca095EoP/vVQJav8XtlRBvRzPxdnd6rPttEQ8xmezgL+jUH8lp/9EJfEIgBCV+R1be86n8ndtP5Txl5YBYBhSXlVGYTBx48PUVe7RHOt75x6ZwKtl5b8o34qcggWeWK8VhB/aLmha+ssfiovnVbvo2VGUENNjb+dcCIlp4Z+pcY4mZDQoBuB/j3JZmbpXt46c41nBprnXUyMcO463OUTbu1eDHySdmxK3rKqfLX9ECFmoqmpcbYhFZY00NJbL8wztaDw49TNNQsH8VtlbW6tWvY03TW9TqdpoGugZyAPzvNRB4hlc/wff149hKAT/j5PF8YJP08z2MBPxHX3Dp5lrtkKamlITCMU2x1pscADeqcU2KSCESyUQiRYrY6V9WBkRuAVH8pRbgjkAIuFzOdCmKP/V4LZcKqWdynvJyUoBoA18DG+OIBKD8GLBhty8flj2Qx8JLAeEnrXbEVuhCC3ug3s+f6gkoQd7B3naSAbuwZ887oDDxlujMeex2EU9GyE3Q59hv+eXKkBPcYgtfi/jwwKDUIphzcKeLzfErF04Mj/QWSQBKw9dejQHzNi7Dg314Noy9BdcTrglw6h+r8Up63LVKg1Pb9MoY98CBzrwffGddQsJsrYOC7FGAYC3jas6+dD5hydKla2/vwU3kiIOrQ4SM327enqW+NKFeH96HuzsB+RPwiqG7RDAfx8I0g93l72QfF+B1oJQNU3pmAu6T9F2mG3MX4OjlJrsmDKRaP03WO5eyZOnI2NuD31pGXSRhc1L/d3LWNeiz9Tv/T44ooLlmtGrUYLTOedlWQ9X4sUDXSa8oZtXJDxI5YrEScUCRZZNuw7bJJVvfO6TKWZ59jaSTqMvWjXqtc7U465AINwRutR7XD7f+ZBlqRWLbeVTNGQX+w39vNbDLNL9sf9Y191NrEfNf3LalU50jWzveleqd5mzoGPTuc15nGh94gqOn+CT/6XX++QIBoDR66naxKfu8guSK49sEybLKi28NiseNcagO6wcW50nXHSRPnMADaPrU/mzhqBihvNwYAYFIGpCoR+rFc0Siovhz8qKFhGJyxdsRnpDs2sGqspq+fFEBREA8u3qw9It15mFXBqaLnJvVuQwUpr2Y9oPUM3Qc7zSY6WMPgM/OttGF6WdpgGBIZ9Yy2cwqpkDGiA5y7I8jlnNFJP9o61/5bwvKi+5zUYIf59QZ6N2/uYS6sOTvfWPzqAQPD4m6dl+YFP8RL9Db9TYhPhx3fjLG36ISo7zCE3KncGPoHjTnV/pUSkCEv1AiW2zpBCerdB8t7Dbocey/7A4vjb59His6Az5Y5c0jaZzRiQf4ayu0Jv2FBpfiOmNvCGQUlGTQf5r6RyTEJfdJm+3aCQ0Kz26gVgCOfrllztHP7/MCWAjw8QKn0/d6NgTn+eZ5jk7/FToNZLW3ojeu+R1Qenir1fCJs49C5rFoyaR0iZCQiLBmildSZoJHgYO9ZyE9wDMNBENf/szEhT7XpLyVNBV19CJF/0kc5Ozh56nx9qnnKOjsd3BtwQyDG8OUsXUsqBO4NpE/SHdZifyJK9QoLEuqQmoTYCjJNCGOPv6zgKhc6laKlLpul5wzuqbKcXY92yoHybHeyD6rgjG5eQncwwjLaVAVjlc9ro5co3c7dY/6GGPKkrJ9/faNiQpRkSSjW1hUlobylNKSV1GyVLjPCl6pqGL8TQfXsBALYg3RBm9nF4Cd55iPQAjd/FzRBW8c7fp98EQaghDKNRGq8L7cRfpzwMAr8sjlc8MivWDvHB6z7wqE9JztzjzXP4BA14AQUp5ZjkBIS2YLAoQRShYFgQLAA4y9jj1iT2q6E6jycw/cqnnx83nW0P+6eujGEWSY8EVV+lAuK4uhWDSA5pokfrhfYd2+6OKLduf6rxt6z9PMs80ucjMI50zeN8JFhhBqFhWhhougfz+5BMYRUy1Rkz9fjXS8QF5QTPQs9A328yFALAKiqhAvp0plQa8GgIAY/kcPtard74OaEoCA/MT3Xm0ko8x3tAVGC5ii3Gk9f0BraaLR6wyytjoAW9c4hEjbGNfbb1Uj5pg5RyL+6rYmitNzrAsZxla/3BfZhHjqzk+fUMeOeZZVttNTbSkaRs4xvL5q2ar8Hs5BwMO7R6KPe6v7dVdyiA+DH+5qRSCk8vRLz/K5gBZQS/1v/mAdgliBU8wA9l6ZwVmzeMPo2cY2VI2ONFlNcaVm/I3pG3DapoB3juJX8Rb7GV1S2ZIlGyJQb3HPsWHpg66mdthTNxxSkJSXlI1k84IHBW/sjHGKsDK0K2IY2MD2KTRhi/MM3Lg+gSmHOXRsbUjvnMK5A0jZb8VFwKMMyH67GQ3XJKfMNjajZigSeKowP22pECigGHhlHtlC7kmRXhb0aOm18auXcgZK6gAskzLjkQ67/s78jUBINPEGbw3fPJc1UCWBM2XlkmvejrUg1RCPKPfP9U9FUiWZtbJeC3puhBgpQIAoveTiToEL8FeuRBcBN0sp8+sQaOEuC9h/TNCL1PO7lFzXeKe+IUshoarpYW1TWiFOl2SurPjW3EBZh2ShpEiy0BeJ0OKTvMHDsw5wdF5JMj8/C0i9z/9QRr7KZP88g1tIpeTmFXBn0E7X21jH1Phx+xbGqcNT0zFL7336XNhWQa5eTr6mOkrhZqrk3h06WR8q9o+tJHHZhPX67Tql+8R6epqBOwUkO1PMy2mDbFvIMLSdck5iiL25iTWOqygMaVLsHpV/ygUFp+ku+eD+Newodg/bletACEcTFIo7iPdT9ga3AOpXCpLSl5uGVw3dYe0t7O3gWyRiaBGVywaYpZOcXY2BpdnJa1do4ZCtcXqv/jO5A4tJpPYwzbJVFZgvSPUWh2llJpW2VzbfL2I0rhTmZS4WotITC2UWC2PhLFJFidQ0n+/YRVgsi2OJKiFRVGiR9HVV9ey5yR5m8So66RR/tkL/qZhn3eLM7rgsSrSsnB+45kb40i+6a1x9NVF3d629tgiP5lh91ZLu/1/bgMPQA+NwOXT1NLoecnWdug1Qe8fBdxcPXHSXw/TXKYdhxk+V2ruQ33NcJMw2yqCvdynRfq8HuAhhuDRbixCP3efdxTpcjw7NZrT/KlWfBQQSnUIB6Ctr0i5nJ8OMroHXDw1AR9ZbWokepLe8d+JHce//P8/rgaTB+NIld8J8Fp8uG1q/6O8+XahsK2R/vXbzoFbWY7f8za29OMo/6Ln1s0RQTB6ap9FvlOq30P48/EIAlfX5aK2V6R8N/7kSHgAe/VHoEsBrovLv34ndUW7NZzVwUAAI2EveDM7iSt3ek0chrNYYPJeYowr5H+ZokeXlcNWal4C/+t0O7zbXZyVfeTg8stwwYF6lOJx3zh7CFezDSjPGpPQbvT1s3RI8n8sMz05bkqE/FmnnM2pjr6n7cit2on1ni7VZ6/I67fL6X2ZNtOC5gO/Z5/KerLRuc7Bc7G+L7LlObF7H1C0O16185aa2pxtefzlYF7psLnd8/nK8E8X3Xy4vm/01wuIZ45r8IT1bTc4++84ke/O7vF4nNbGG5rhAltmXkzLaQOqYQCxyfpNVr+jnlVjdPNP7MoH+1mvFSevl9mWBvVqDkb3HgayQl5X3J3Yd0y856E/54MK7LGstKnmute0psb/yXY5S+A3zKhZn0npuA1JBC6zCk4tecGTUt+DWdyMXqPyU2rjoXexSz0We8huOWwyCFr5FHWRtgsSbVx+eRN7VuK78k/uQTIeSToBB+8QaxkQACAWF8rMQwMU8BqIdYBquS4RE05YoQtmQa1C5Oi4xyRWAhcQde4YyxuPU/iVAG19tofkWWCNdGb/S+2Xpui3nYi8xd8Vey8xWgtYFLekcVaBW35m5VptZZZ1iDoVRjZiVaE2QbigRLbTWUh7mD2aJmSyNW7DKagvVMnUq2VGuWr2V/GqhjXwicU2vUGfIna1Pl5hphWJ2ZcO5Siy3ynws4TF7oZbZxGoUG9CrXadhRmflU570M1hdW3CROQAA)format("woff2");unicode-range:U400-45F, U490-491, U4B0-4B1, U2116}@font-face{font-family:Roboto;font-style:normal;font-weight:500;font-display:swap;src:local(Roboto Medium),local(Roboto-Medium),url(data:font/woff2;base64,d09GMgABAAAAAB70AA4AAAAAQoQAAB6dAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGkAbjgwcgTAGYACHSBEMCtpgyyoLg3oAATYCJAOHcAQgBYMAByAb4zcV45ilwMYBQHj/zqIoV6OFKKoGieD/6wRuDMHeULs4TRSwjuJRFI+iGCSKIyo0tu3vUnyz2BtKKHNB18+a42mLmuJ3MAA+mqr5bDgO5VorjviF9X1bH6OhkcTkoVp78vXswDHLCxEoYDCCVBSgA/ZIKhV55QPs7m//wK/z/St6nL6ZHVMLdvROn8nDqRmb6vVtalM5VmrWp2KciFwka5Erhls7LOBpkfcBBwnacoiwW6rJv9JVdkvcPSMdpM+RcwydOsOR5gBB2k4km3duQ2MSEcTO7FCrNUHlHtxmqoTC6E8i0ucCFw0AP35uk7bRKp49f0FSpCVKF00ku39vpvD3OstWP/9rTl4odN4ssHZGUYh7mv6o6BIuKulZYEn+S6TsaIFsH9lhX1BHOqQAs9ch8l6YO4aiS1FdVaZO23PRlumAed5t1b32B2vFXq8JFsFSzpzfN2cCPX23hLCGEpaylpJ76rfPtYSQtK0p4gX27DXU2v6erg2Cmlx+xASggB8AHAoFWNCiDQEyQwhQmEHAVy4EbtmAAAIoACsArAAEQABYBiT6MIfPtnIEws0R9JNAuJ3udwII93hdOA0EGAByGwIgTtxKPw2cAAJAAAZQAApwgxUERGEAdJCnW7JFAAdmAgU76bQVWHiTLF+B1hAL/UpF4HCuCIWaDrrm6X4TIAyv0s0LknPMCy35TXdVMn7yffEk85yf+T9rs0/5QSrB9gos6F0kWbvoyFVVcy4rjh8l5OKibOhc1LXwhWJtQsyiC1u5/Oo46CIzglyY24rzrbe9mzkq6p7fEUwv32FIED6HmJ2UudaOQpYy35ok9z2l4ky1wBoC2cBA4VdvKy7a6nRhafai6lwW33iLrmplcm4l9kc5e2aFx6bsAb9O6BKS+IzaZcwuBJlJZplxMKZk/Az+KTwu9wkTUmljnQ8x5dJ2t/vz9f58f/+4+ISMK1evZ2bn3rpz70F+YXFJaVl5RWXVw+qaxBdTrXqNmvUZMGTEmMeeeu6l1ya88dY7733w0SeT5ixY8sVX3y1b8cOqNetYfvoFAUNNEIAnGhfhugVw7fg7QHD/IgAacMwMqM5UuKXALYP5rWMRVcIAAugisGam8K/A7lmK1vPLgbOIH/h00PXQ3NpQDgpwpi4HR4CiAWoWc71atwMHx0cBsjvA2z9rnCRr1U2U0IACzLFw3KuAF0XcABNfCtg6gamjHToA8ylLNWBPc/AA7LWuM/ojEcti2TGKOu9ysr7EikjqYiMnItHLadaW9sikiJ4RlJP3+5wE/kKFJxskeBxIIoAxKG7ObofOTXjMZ8bAX//8twEBzG0Dbgz3jsbkxU9YgHa7qYHYradML/iXH0Ctdvoy2AEsgw3ynEtoZmAYIL0eB1vgtnG7ABHyAFDJjYmIuDQI6UIDoj993ABxFwaJBc5mCAs4ZAciGHKhEKTQCmrYhUcERHTExLXIjPxgxmpso9ZFq7jsCqlTpJKkWFL/iERiEbGCWEmsJbYRO6WFtrdBACIQ+JAPxVAN7bAKx/CMwDieETXB/0V8u6ROloqVWpf6GSwkloxtJXaMIu8SZKAg/fETpCfu7w/uerZqr+YKLuc2APPqzT9mm6f39qzPzJ8Z9rQawJ53pqlT5VMFU/lTVoAAOLN1OJ34FhbYuzw9c2/obxny+ybdc98DDPkKFCqiQ/fm/U7So69HrwGDhgzzFyBQkCnTZszq0q1Pvz8c8QJQDgBwEAgAsQPAXSAE2jpumRC8jhF4cp468ohjJxSuW0ERydNUaXmeNm3HO8TQC4xQnMCYhTdYojqJFTtvcuThfZ68fMybn8/5C/j0DoRoX58YiVMmrsWpE5lxWgdjzQdOB1FzmMt8sYrrV/Hfhjuwbf97wGSixLFKNNCFItTOQN+gtAKmg3IJmLUUFBJYaSn9Mgh8b6nYnQRz1rF88dO/Wn1EIgIOQywM/Bli0QVHKFHhiEqVfqtV65hWbY7r0FluT1oIzrPBg0eA4YO3U4AAgA2A/AUGl3DDl8nBxOQ9cLRb/syaBXmb2NAGPP0G7G28Iz2CPN699rB7OHMQEsOFVoN/u9orOvA2BHyXbUM73rcHgdq/ibHfKSvvFdKG4PKgB5YRA+xlGzLDlRSwscGuUvw+LUsmAojsF0QvwRniXCYA5jpZ2xz3S1/IcmWMnkiZJWcAqkH62AD0/f7/AKXbff3HlPV+MacF03dfW0pf5nbe/2RzwcOhXNFJXsneQXK4lB/gYSbZnBKu1Y1eka6hYfVriW38H7u2oJ7tYTLQmLJtWBbtyl8gFvxNg+QjPbZMknk5TEmBlq2WgEpzVwQ2vPGfdvcV9GvnmQlTtK8wFwplEjjIQ9Zavhuixukwss1evDWeULLd1Ve12nSTXqQWXZO/WtFg+ekRGiS7vXXKEpYe/KZH5S+FaSMU2KFOTgXhvMi8XTL7oG60Q3uppIz8b9nbbc3OjIHahBUy/+lazZUeUjaiEkoVzsrQNDT8YczROmHkxiIMSn5xuBa+PdRVQOFAb07DDEqGs1BP4bTwjp7+RHXe0GEz4/29U4sJO1N4jYQTh6cjBOs02+GuUq5eH9u/4DEa0b7axjkmQ65Jb31kF1BSuISPfQzCWyz0IBv/3VN0NPD3a+z5xrGcZLqptNfzQ10pMvFH64BCP+9cRCSsKeWdFs5AQ8dh+YShjiV+ddaWLvxu7zYLRc7WvqpnvJj4TsaoaZ1MZplC7ceRSuGsKV31vq8tHjep92HRmCqBZ+bXu0xgsdH67vlk9S1D8LpmCDEej85joKB5s+tDR3duSMi+CFZjyEFzpzrmhGSVK1pZogEvD2K7q7r6OcbkZx2sHWjMJeNthtMg8cJ17eY3GSTDXPQWbT5yHykBlWxlakHnFPwb5/OPBwz0CjSTlCHbuw+hSqSY8bpqpPQY7TtuVVWRcG2OdCgZA6aEYg5IF0JdrZdukhxeCOoyYEuNUduCiQ3T+UvNcXTbyMPvIXbcqr3vitNMCXRsHxHXjISMDFyhoIHBCsS00sKlspf7XOWYgaFjlzwV9vR66L19JRZMkMNUaNCG/COlW7tGCrItNhKUCiKOIWAgoInTVOvN15hq4fHdz8fp/iblxfL5CY9ab9SYZbKypJspMtTwvolWHIehZTkCeADKlwrkhsxYGqsGH2glq/1RKALY24VPwcR8A8Irq+OIqASU4ppmrSRdlLFUXAv6D1IOI8S8g8Olub522VLprDdQaQh3WSzDFu0gS0N3jByIT7pO1iF0l7QwDXgpk6UB0O+78RHQNGr5b/uz/7qQl3KQ6YvCW7TAc1T8YYO+dtscmTlnPNHwMCgnwNazIq6BI4Q/MRVRrFD77GlR9xuRmxnkb4BPCLn86LYYEbC70KBAEAaCYo92lOXXJwSiwLZWRubaB+HzlrmYj1AzXUeGAhLtTOEzPW53bkuHF17kOQYyMrhEK6oXHQs8b4nusaVjNM3K3OvJZMkgvQvyWyYVUVqHHGhSWgUHDwOKfUubu7JgoPv3GGgnu90HGeQnuldqW4F2OxpLNgxLovOgZPRlsLr3lJ2WpqMzQN1EuTmfwYLJLpgAUcbtPBD2XAYUdgSjxN/+FlkUa17Y4QLaWmEU6J0Mx0PGzkOn/WUAkzU0a60V7bfY3mgoinKikYFC3HNqNmQqy7ztu8TwS4pOmBBaodVjrOmOVfLiRqgMjkvI42CNDRyhqFwJvsJZUI5ar7nHaJxR4+OSmtBoLWwhwlVahNNZ5yXv2yy+0CbC03httpWYn/jHcfW/ct3Rg/t2iZrOfH5S8t8ReX7TBr0o/YsmeeoHCD99+NtLEMbTr09u/vdv/9u7uPyuvew7e0BXxUE9xJ5Dxo85JxPC5PSRKnl5HGxq34t83o/5V81ZdO0gaiT16Y1DAuX1a3JYsRW24N3My1eLmavI57jvZX8Kc3KShO5ANmCZ1b37Kgt1Hls8KzpEKh/k9p0nFA+//ot8tlt+8m8A9OmXnXrax1oAy6x93JDpBHp1QZZBm4Bl3nvMqLUEGnTa+IsF1TVLFOepo57QLXXoO1m7fkjO/N2L46hg/kR9P/t9CFPbZ3d014EeRbCLBXZKeoqZF2/TPd2dDQ/YgGWyQTmJyd58gfk1Mf9n4Qn7Av3CKf/w5DDQuFBUPSQ1isr0qvJNVcnEPf37D8D10ZyxzD9Hgl2DTMJhV4K6q5m+kafF4wjz3o2E117isdMWZgS7s7CpmM3F9fqGKjbw8W2A3WzdRBYFX1VfK1yVawDP7cdzTfBVvviKHEN4zcasNyFlRS2AfHVcbcOWlDVs/64gEVTu/9cN2IIzE5kZA1Hqpu58O/Q2MLHXYs+GJMYkIDyg/SLMMznAIyjAcChylCk1t5C3fCblaTSbfjU2KTI1yMM2K9jB90msR8nZJtGJH/c5L2Q+SduMLEy5lZgYDFTM/ND8xnzf4wSgjjOffFbk//Nfjnv5MftpZtat67LqQ2CbH8iPMd8QwTLXBR0VBBsUWCJY5ga/rKmgr+lrOL1pUysulZAnviUulZUnAXGj3GO6RQeFDb8/3s2AQ7HdOd05QMx+McanLALoLR6T8+FlhU9a0x5Cy48cM0c3X0+HIHtDlkvI7bGjUV0zXaiqz5mKToKoavNok/+PE1JS3JxMXe1B++HxsCC09QLaN6R61C9kjksOLfGHQ3G+d30a27SAdg0Ncw2NqYCHCszoKrCR/b+k3F8VFLaPBTj4yHPmZEJT11eV9bXi6JaQnMOJTylxOaExbeAstCQlsy5VELk1cyX906oEyrufO+nunbDSwOT4SEPPt8Hcxg98L6YLRD7Dc0SciwQ7Wfb3AwOtQbHcJz9IjHP+mgmb9SbT6DfOJBZFpMfHWAReqOlaVVr7VZjYFpplGvXx8MWs8MROEO0fCE1rOoa53M9qO+vp6p5pFodlAQ5YEMMisxBxbVOLRDMLbS0z80RTcxArIt8uKap+YPy8x5kUgUooFP5n/MOzE6e9zGQOpbrvs5W90dcRKgExf/z2B6LCZNzZc4lD72jFIcUT9dWDSM8wqyfAxSPQIdBZ2AwWWCOYrq5M3VABFhlE5sm4yLDr/fbY+EeszgBnb78ss0QUiww28g+ituZSsz8ISHB/5mRc3JhPy13Ak7bWOUvTOn6ndrcWZXRHhKV3wH411z9qpYdi5lnkz9jwoxl31XfBezmjYOM/avYKjUdLM761N/WtdN9imQa4Otn6Wuhru+nt0i9wsok4mrnWMJD0zON8qdd3zJEwVx9PH3MgjBtxdDd0u+rjDPaVd9iLWotr+NU11HEAqSc4OGqQ2fCkv5PV9rSyVi+ai0VeAGMVWuXXZfwF8sjS4tziCD6RvFw9+2m2GX4f//AJTZif9BZY1ljreOnqSiHYyLiKDWjgIKbXsO/0A9mJnQO024Add1KlGIcaG/saG4dSjEdIP75zQL2Yt+Z+DQhpgoiwcj/ooYpzCJlBlSF3cKkUU+V9SqYyNMzuyxByFuL5HX43O8ioazCnB5MFbsoOSgqN3rRTCYggpIC9TPe/mA1FCZbHcsqvL/f1BRiP5xgLVdcNXvV1ulqauX63B3R1eqjPTGN2+5CdHFPYYMQujpL956SyZK01gso54f1t4ym8mc4wtBp3SGYO19y9v1z5pdvsuLu/83G6M2UNn8luGZw6nRifPIy8XkZ9S2vmvHzzXnJa8KFe82ITrecGmrEt5BTy2IuBACTM3dTI1Ua1bsknOm1H9IDUuZMRtb+14KPSpldv+D3rzzNs/7Cbyy6zzEnap09y1jXuGnjt81w49D4KzpLsQ64Kzgy1S6ndQJUdqRnpN8rRhV/V+kbFxlXW1RottDf9ku3mq2MKYniH8mHZvJEFkSg2Kl0+J9p09/S2dIhnkVmwUnmmap28zjxCqzlwn7flgAYQhAK+mGbRtrhzxjJT4uSztp27CRKFEVnqmZtaYl1PUs58EpMssM1hAvZzc+dguSmTb+gS+VKLisJPERHOlzvfR8LCIDJA5s2hlw8wG8bhxUJDW2+JyXW+wUs6kcx34a5ngm5Y+hNZZEi/29TbxzRwFhZvua1a2JYHzUKyFA+LtOeYqckfj0tz1VRJg/w1Zu/JwRcsTvTn6xKqy17ldr4T7Hixu7W1CxSNk0cfMRu6i8xt+bu8jbnw4dVjxQ3F3k29fbV6x4TF21107kFVwCjYoUAyD3MxPOza7fxSG15qfHx7UfjzSzJOJehJtuDv5s0l0f+9hasMGq+IfH4key41eyYtm3IwiF6afLTOX1Y/Erm3i3FxYzEtZ05ICVmj7tjSjA7g+6/v40kut7j8xvm28QHO49z/ldsnZtVdLcrnVlnABtHvg5lqhjM0wlARKkioky9BbXNvqelZ/q6T5Miek0DE0OaF9v/effvi/3cZSTC+AsupFdwpVxlhTpflrdVt7pnovaBwCTGcwq7gzzdKnDkbAerq33j6c+5k3+nl4xe6Brvk/G/cfs4tKporLMb1FA42LkbX3JGv7zy6iWJpnmhhTgFOERcNY9PoUK2hbmwSfYsg4r/PLeZiRWSYp5e884roiwLPDhoZKh80NCAp61O84ecKZDhpWbipHDY7MCF8xXy/q8f5U8cbWSdAI+TL6egbngqxgTqVBHlz5WyO09E5HocYLuryzgdBPml7hZQGryl9+aTUCnHLFoHeNhZBnlTLIDcLm0APXzPpTRnpxzKyz6Rltr1+LuJumGMMd+4xz8VdLK8IcNa1tjbjMoG5VQ1T00Qz02Azk0RTEzCao4fTWfTzI+dDWaHhoCb9mrQrxcCxBUYlxuwD950+Llp8g4EOv3v9JoYx9u9L7tK7ll8egYEezv5xgViGwXUyJlwDjp4ffMt4exPSrmxqLjussFdeMV7dDC/XN7b616PfNsRZMtk3WXm5K+6519LjqAddJ/bvMwYfteRuVz0xA/kvA/a1DN9ZxWobRd3f+R+wzIzX7D6xuhCOdY5uS0GHzJ/wZwHLAF3Zfa/0kti0pFRGGTF/lPE1xOyfm1mkhxoONZtj9M8eXAEjziZpQz/pnW7STN8wXSK/xDr/FBTnf883f32F+HBSAwa2bZJtOkAaILc/7EbGqCzKKRfvIPtQFlkimzzBErINdvaEgOqf5dC+8vojjEvu2tzdwmgx5w8Kl9JDSNGJnWd0iiAdjBmNyH+JncK9ChRzY8rSqgn5HKLNGvippXIJ9LL/vVe15PcdU2m8KVL0oXpE4AWRbzfbFvI2z8a7ilAJqo1zpNtv+TQeVYMJfvvKeJGji6+oO5Eu5PbBaZiYNGZkUzrsN9MuI1ba5XV0xpYjtRt/rUE1IXhKEXUKLdTNaoGB2JVze3NZOWf3xq0P17LaUELoM5P7XrBAjbREWWIvPXn05AT++jGitLb7//PXgqsslO4JipGGUuq1qKizGVcuoXThvfDM968LnnjPdi8PsS+ortrx26mV9XfkH0441fpbXw2IOPnK2MrS6ZH/XoMaohOqFgbyYqzM6aiLvqM0cLVvBdWXKfcq7sHOoZzFn705cZ9sTvnYWJ3wtbYM9KbZBXt5W8qR5GRX5RTWZeVUAe+/P95e/yRyCZJeJbk4NkJdRntF+8UN+Aj66Qt9hY9es1dNr69PbfUCKAgPL6E3L89kuL+i/xrr2hLoZOGf4TfwrypeKfY9Tng8pPhshZ+z+G3FW741Hhd9WfGyeJVfnu42lY+YMiFM/Z6cciXbi25+sOjE7XnuUHERbPWyFYd3jAvYX/NxZIRw7CK1A8UCzsgMJSe7nzpmXB6Dl1vMg86qCn+N/jS7p7wYfJs4fs+4NPnIZhF49ks+zUuci9L92sZZyyrsGo5onAmyy7/9cLtd0OwRTueGJ8brL8+dAuyGBwbsrXRQTIwRvMM9JMnOIZ9zySOSdAV5WgfSuZgiWvodmkA+FmCsoBI9QWUl/XtZZK433OHbX0RqlIYjwBXAJWtbeNudY3EaxUlxQOixd0HwrAiGjTkEkoQwwlIu6UQMJEvtoVdk+QiI1DgdpmC6Crg4roUHORZXUK7D4S6y23eqjGtprcjyYRCpURSOIMq74ZE8Y27OEHxCQTTl3tOeHYYSTV4s3DCPYJOHRp895MrxXyRSqo6RREopNkXLDB49gOPEP8DovPUfuIi01fYL9B6CSjJMmQIvigKwtCNTeFZPeJsiMDMRU5akIpOU78D8vfkT3QNeaR7B/I6Kvf1UFi4hbngkzzg133U8CUCDVLgxlariSmVL1BhbvBao4buooQKJtb/tS+QHwh4QwH+c84g8F0YGXItA95+jg8Vd4C9Jz2xiQYFkPMhYwSVdhnHUWAYfoqmaQ9KAZ1xa+ARppjA+qBLnsGKRiocwKj4dUSSH26BrIa2/t4PFl4UKBeOrZO9aQSYF5q6FWGR+Akk17gFJNQ7nH9Ez0uZMMF3SnMq1LZQE51hcToWFD5tzyL4GI1G6SKqRDi2q+ghAmLw6/SXxpEVhEcFGfWih/bEONpchDm1BuIrsR+fnEck1z+5344FHsgrTCJaszFNfmxZkCdK8paKXBS4jBxqDm3nEcqUiJab7NpfgQfVUfEF2gskquzuBSNQIDgOJGijgci3c6e/tYHFEwWqCSmkPCtUQ/hyEqJfEdMSymi4ptw41LqFwTiNEMPAUqZRSoJ8LRQfjFcCvMY1xCT7nEB7HpoJFGjBJduSW61QOPsbjbUtgGxehAUMe4tinxr4OEVAx1Ezzkkeyq2OhK9pVFzhIEr5MlFq2xgMocR9ekVQoJSrhVp5y2px907ySfkGDjWsd8by1Rl1E6zDEDaaLAah2LgD2Zp5C4CcezF+y8/vXbUx40Cij1jG7fDd6ZF6fz2vV+UOyfZG/Tsi3QUXDxr86sFYsQvYuv036Np8N6suh39fjHYVqq/6NQ3l4sNX5Yw+7yO5kJ4l+Dw6BYDn66rBiPzuFF0QlH7vI7mUmuQUrrw004spjA038riPA9He4Z72gXB9q+YA1ke1HPinp37JVCGylOSX4jT356PHbtSlis/M8aEwwXz7X27h7IArHToX5/clvl0bN1A0ryaZSObSyscEn+uPZ8+GK++IHMZodjpdOfmi6DIr3uAEejZHf5MiMRmJKWq0MujQ/ANjOtw7HKRAbmgBusc01JjejW6qNsFoOsl2sLfSahEr1ef3Ra9WSPyTZRVbD3kbK5NAUCUoOYFw0ydAGUA3Vxsdr0Tb/C7KMXh6aqNI11sEmWiDcbMS1AW7W5qpN8+If8hY/vPoOq7RgV2Zj4vR3F3ozYt58uHTrNBdzQCHiHFboCaJ0QjrHAWEs+NN+5/d5Wu1jJekiSHZ+d0casmSv3LaBBPrC0CIN2uOOAAdAnYDEu3XBTnSgwBbVGUOT/cDU0wFEOL9NEq2XoSPbv8E4OijM8/tL7AgEZX5lRixahkWvbe3Pai4MgKDkkzYO1RyvHbZrdaPP568f9OA1BnqEsG4+VUr1sfncbsj93NbrgcZkVrdUchzbTpNscuBw7TfaXJ06CAWgqnTh5yOx2EXInl1jFXWY7cPD1x4BUPaseypbL+mxQ/d/ToklAPj8L0FlAH4O+9/+y26Oc7kuOQ8sBRQAAmCcnHkBlp5wcVtGxwGBait7pwy+sogt0Bzg2yH+jSy7JreMXxaNLrjVyC4UhfUcpcazzTg2DMRjVjOc3BeoW9HXLOsH3+PXEjW4gVSncWBesJ3uQqOyqVRHxyoJrSYyOyqHmsa+nhNReCCqINuNUdJ3zQMZ/bfIel5EvYpWnlt/BiWNWspxYujC2jrpaQEvBgf2UxK36YM75Y6zRcCpkZS/W6Kg3su8pqLVZbTGtLsQXKt5rlZfMoVGumW5XlNFF15Ib6nsyvOc72XO9T5aKxJV14UV+WyMLpdpUfbERJ8ihRQeiCt1wou1vdNbF6fdLMpJ8WBxMsBiB8aNYW3k0c+S7i3ylmTJ7oqke4bI9ot0VadYC0g3d7QWRXGaZRVrTKZW10ud3IKF7Oacf+q1fJ0PlEp5fq4DfHV0DLAymO2ssZosuxFCKz1RVA6AlcN6ZomQh9DqEilZb0I+IHqCWtEU82gzxAoPQOq9Xq+w8kJUdmCukB9YNWRI8dLLGh8J5stgliBbkANPym1jFmcO8uEpkEPIjEbdtrWzeeKkURma+7I4wbyATEP+5ggh/5BVuAQgr/qlALn11ADILGQXqEfo2jOyFyWftZceXCZ6DnSPtz5kAVr1g/Lrcg8QAHtdI8oG1ny6rAIogCLBAQJgCfhDA4LhAnAdKCICBO4iCvgh3I/e9mMgYkFesokDW5XCD46GjjX+twt63JXkolSqJJVGIJp5mbyhm7VwGJhGG8jU2Xfb2TSpjtLUlSgB9iEmekfFqaw347K1PSY3OgzsQ1chZWm7QZDyNDUtnbHm1QrdZQ1MLKF3sMFhUeAVWhO7tkvBmcTNkxLH1I28PrmxWowm29OkHqKPhK72KA8IAA==)format("woff2");unicode-range:U100-24F, U259, U1-1EFF, U2020, U20A0-20AB, U20AD-20CF, U2113, U2C60-2C7F, UA720-A7FF}@font-face{font-family:Roboto;font-style:normal;font-weight:500;font-display:swap;src:local(Roboto Medium),local(Roboto-Medium),url(data:font/woff2;base64,d09GMgABAAAAACswAA4AAAAAVHwAACrYAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGmQbmh4cg3wGYACHbBEMCu9I2GwLg3IAATYCJAOHYAQgBYMAByAbO0YzA8HGAQBQZyajkRFsHAAUbVAUZYOyJfg/JGgLhuC1B62JCWNiDIqaKUtrEQhjIJnCeHmtlh3y9eOs6eV8V611d7UeMfbVnwlxCKJ/aszxY8wj28fCDwDEjtHQSGISRHbBf3VPz+0TnYmOzJCIGIRCBCiDP3jzAz+33l9BS6kI2qBNxQhHjsiclLhROUa1REuFA0ERRaoM9IBDLEoseorniVIA/0TV1FpmLrOQP6wj/IhsLIYeC6Bn2ABQ3Vyt6V3efeJsp+PrOpULAVWjgHWnSrNxwAsHITjSac0InGRm174HrACpKHF3ZQqg6YEF02iTTkqK9rGouf4KSlk+ggSBkkpws/7DtXLzO9lkIJPNA7F7ZYgLqICnZXnCVZkqc0BALZ3NUi8zR+n6ZSTC4xRCpbRLOEdpwmRQyDhKtZJBV1mqURKhuG8/zWY62RfAadn1H/EX7kiXhTnjkg1uN1vgf8R7wKpAOQBwCEIyqcgDtiRsX4Wrsvd/+32r8xax9Anpk6wkztY0787TmXnPv81fUw1liV5p4pI0beKQ9lA9QogeCiGRIyTRUAixEGnA49Z/7z3xjdP+r4QQggQRsa281TCLXRWGF1YHo5axVcF1IBlz6Me/UkDhBOAAKKyEKUNocIAIFQoRKxYiXjwEDQ0iWTJEqnSIOo1Q+nyHQKCARYAlIEBAhEKAhYhRhtltDz0zEDg1NNAHBM4MdL8BAuc5k/1AAAYwIoA49/RAP2AGAggYKCCx1rUQxTDAXiFSARUQUqHZdgswKxRn7JecuUzxp0AKJT710i9j6mTmZGFZZUfjakfKoRzKrak3iyI1cpFaSgW0mgqogOJvu7WemxdjcoXFk2VVz+qANBLwh0NowcBhw46XsAsuuuQyUWIkSZEmQw6eAiXKrtOmQ48hYyZMWbBiw5Y9RwUKFSvRp9+AQUOGjRg1ZtyESdNeesX4/GPGa0uWrVj1xVffrNuwieG7H/b8hJDLMAw8PgrOU3LEa0cgeMNMDFfbxNMOC4dAJGLk4sMa3yP+RgvadJkyv9viGutf2fzM/krpDIraplKUF/0JBgwaMmykWPjeG2+9896HYvE3S5atWPXVN+s2bBZbv9q2Y7dgjM++HyFsD6jXPoVihVNs+xeWXD7AZ5VC1inlndckjOt52nBM/+40ZpgxzGIf2y/59hHRT2zU0NIiOVUwOPBRwR0+WuaMF4ew+9bnnGL4IxyRbJJvzZrzr/nKo3C4sDUzj/Mg/kwL2nSZ10ekdD0owKVQFCtJi15awrIVq8UPXPb8vBtRmEJsiNdpXbTpMpdu0ZJlK1bvx9DBx4oEW9xDB0xMLxt7/L7W9K1Yt2GzEosrVDyKlCqvL269illz/jVfyYu4kBZtugPTAczLsGV+ttikW2lRLFm2YrX4OsA36zZsvhztXO6yusX2B2gcY43K75wd+zWKzk1ff4WIDa3mxDqW9z2Hc7zYudCJaIvzrDTf9ca+20R81nX/SrQMSE6/svyzT9mvMAmySAcHn3vAqB4MwL458TmO3y0HeAw/WLmqqsAt5+CbZQfNZ428r78kac9e8c1O//4HYz+/NSlJsf8USS283vO9UbU7htqmpmH8tt9OBwd0O7WhGm/pm7UGgC919zvO3m/17fojgVPVlTu7Z5Q97gajWekT5aEK1v2y718X7bh2CRx8/EVwPj2eAzvdd+W8er/XSQeur8SNJ6OXb+MI6i+4c8wTXhENB/VXWyHW+bSkVtsAcOFK5+GKcP97Ds3nuNr2i8CNqW2t/OjkmszewLHOtq4tP/XJb8zcEnEOt7RJe78yZp/W95n+TOXbevplPBW4TgcHpmchGMxq8IdDqWd5BsDf83gPtLbxcPv+kP2KdvHeLuHnkvMvOP9+6zyo9h5INByMrO5am5/0uf+gpvzBG00HS7LFjbM/bDxNdR3cnInhcx18POGqz1rO7Ei/urpL7c2PLHGpPVZXt3J/f6BR+OAeBMBGCC5C4Xk6mrBLMK6SwkKOsrexBqJjtKHj9MwnGDNzkgWdZWM+x44DISnmCwqUuqj8fUX0WSXqi03mGH6yhSIzAx7FoygIgiEI5Twdc4G4XSTEJcISIVaixEaMjpAkhBTxkSMO8sQKTwIUiJ8icVEidsp0iAbhaFKGMCFWH17Rgxkw2gSGKYRmRkwsiJcNsbAjHvbEwoE4pUimV0frqCKk4CukdVQpJKicBPWZEf1GoIy+Hbcxk7CmzUDMmsfq9WVmt+ADnEXzYUtWMfuyOcZXm9AvmMTiR+3RfSI3clt8uIWO4CE+whKHhXIFQhGhoyQJRZpQZKs5d+jBiix4GHnxELIhF9+e1obQFYnsDdGIsdDMIYQlsbMmLJudmXeIzBaHeAgljVDSN0fku+3IVpEb+ZAb+Sw+T5BHQJxE7RNRyIJswDRnFbJJRIcYvmUXFpkV91tS1Ur9dVzOavVw3IVp52M0TrPZ1cRMBfcwUSlU3N9Qr0jvABULOsRQP3iYia60ScAKIXEmdoHW0Z+s+gigbs902gX+vnD1P+3pABLneXs6g6JC1fsbnOfv6gMWF1B8A8Gul+dDA0w0azYoHbNihGsW8EHviYsQLYFo6F5BgMAckUizPAeGJwkCqIFyaoCmQ18dFP3o1MexkYzUS6PUXXUGYUYeL5xAy3AeCA8NOlQDVRo8q4t4yruiKGLVplWLu+jadbink0QFgc99X5hbyB7o9lCPRx5DQfA5AbS6L77Dgv5AlwSxum7X3uORAFpku9a5PtgX1vgQ9jzQkAgEMMYZ/AVnTMUJpyGPAmqnTne6LkcDyFVWjOr/N1YQT8LEncBeCLCYgUVFqdSiVZ9RL6zZctCJh2k+ztHEaiHfroEyNbOsfb3VqW71W4/qDZ5nTqd5OzgYHwAA4HBapSqt2vQbM+2T7RDvThy+qG29VPxT96fj2LvqRAZxZwAcHBT49TSb+LnC42PXnTIXk+sQxy1GixIuxJDYE/nw88P/+1ykWRvERxDwnOpK0k/fd/LLY/V58T4+9HHizZrzr3mvUSVY8MZb77z3AU3iezqSDyRJtmXbjl0MKb5jBwCo3ZQVURAQmaOFdloUzHilXlVauI63eqJNH3/9eUTnUcQayy/sPI0+L/NcxL/mxXntnVnvfUC16KOFvAaaT3krlreRaMeeL376T5Jf+U9v/ouU8YPS94hsGSC1jBxSOjRAWkQ9V8/dRL1BgIyI+jxAZkR9GSArnp4EyI6npwFy4ulZgLzy6cXqJYB8cHqb9pYCL94UOzkpc3NT7sevH+4eKvR6VQaDOxtPAlTHM6cANfHsPEBt/DsCNMWDPw9CMwQKM9Q40ANbPQTk950EjpfD4Y8y+sf+rTHYOQDoo5r1I7DQkJsoDdVDj6pKx6mT0eY5jKPWikCdULeQvkul1Dlroh5qxghzjuRxawBpq1H0TWSpVnHQ5Euo/Q5i69vQO7laiocNtLf+N0gG7wOSJdDmDJD3s7CnKSnMofgykoWAQTVMbzPMj3GYJBqXABCm+EhinSPOCwktCLSpyVSUcVwzbsYSkiBFiAFggVuZyzwex6a5AKVHFOZLy1iEbox1lLU0CVP3T5MsLc6mCYBDRZxRnaFGdRyBSGByDUaYc0g6TW0tk4gp7m1znFcgGY+xZZiUBYlIPI6IhBoCrKYIkzCxBZswBQCTQWmNYWaUwyjGUWm1qPUymc7XF+PeHBfdCRU6814rlFKZ9Cnmh6f7jh6FI8JUl3OflwFdJ23xiMRb69roPUGulgrkjSqBUm+QeUwDhH1D/qVcu3gvnsKd/kqqDO3KRSyU4eSvjDslIPJgO07syHvSGr4Mwi/AHK4i/wGrXHO/3K356AnVTYoSk6f/2Ykt98Gdr5cS26S+Oc7WtOViWqbzxq1qiH+Le4qX+YKVvWR3yP19uzMww74TDSqgWlyq+cKt3Otw3EjaxfxYB3DgmN4tCctxoqoM8aQinQdOemCCr9MkPcJ7/ILvaN3rldtj0KfR9R70hJ9D4Ix5yFoe4MXzJGOggGo9Fc50vHm3fVvQ/pufoUw5EcdLVWTTDwWoEfMWWr+4bYh/d3pciYFSaz8xyUi6WExl3wCzLCbA4mzdcaWJloqcts9NAMfrHUIe+PCIJUj3u/gMChSQc7GA2u7T55hDhpPc+POvuqoSF8xm9aMJ+ZbXJKpeCK1IVvWxsOMmjPu5tFXV/Lxp8g1nwQf2f3G4rB1dqre2gsWFrLE9koz+fSbp9pVwrr7XWyy6XvhTzAMdngTtHTs2a6qWFx16foUbS23s5gMR0vECQJmuU7H+BJjbSzfI1Tm5LZjF1o1oGTZzgd7JjuMzuNgjKHfgULlsFLPU4yh7TqYQ7vjctvhGUhN0iM4m+ttc7avZP0suOqfwLqNiMypfqFiPzc832NbSjV/1bHHBGl+kJGUPF5/Re8BttkdS5lTulsaavX92ZPjaFk8VbSVmBur7tfB2s67l0n47+0bR6IfOap1Q3Ih1yi0f17gJ7/+smd6JzqjQIqzLx2LO9y0LPZ2p+VQt5jrdZimLyH/V+7eFYbQujlRGOYMSLZyT6FwhG0mfFjyj/FwsRkf6tn+ULWDeBFw/dWxQInzOwpfDYF/K3KImzFAPLGhPDELML1Sfm7xbsQ3x965vKSS3fT0qMOYvJNMvib0+h5/GZ3kImfMV0F0+z7eNFPmdNfaN18hxHSXR/P40CcBAHa7phNfTH7iETITiO8mqezaIyBu6rQ4Za0J+NhbTddBJHeJvZYmN4WM2ySKpkz6YqSKj1gQSEuBK3MrapJi7iUn9lYV4V9Np++CtR2DM1WBECaEUZWo1y2bMKXTG/D0VU4ZD43vf4AKmA+YREsnm9S4s4IqiX79c70O3PW06Oa7tzSjGDL8a8RpHep5NmJnburc1PtCmn45Kaumwv3iyTc/7quGrHU+SQtfUsfFTF0v15uYQfRudGIMEatPcEJYBlnjoIjR4AbcR0wOdp6CrCu3ARVe1yIAoEFu4zbl6R3VPzSfS4dyCD6sYqB7dvqFXtbAl9Mztkf87OkI+9X4WFGE1WFxGxiUtM9ggccSUGpjEtG5R6rL2VQlqaVNBpQwKsOnMThy/0oM1bgJ5thLA3E6QQ7jW+PO2lccviPi8pTvC2lcHUMktIPb+DSYoPEAUOUjAqYcuN6manz/ON85Wl4oxiR9DlJW2aIYD4U6QfBp7QE9L3a1fUKiu3HEYL9wrPw8oJLTosNRS70M/FKfl7IP+VZytViQ2B0HAIYx+fZt++Up18XmK9ZdMFX+SRosc8Clkpw7Pef/oCc31AUm01EykpxGrDMh6MWJGV6y/bFv18GD/gRvcado5SSk+xn2WqPef6lgJdDVLGzGmCWzI3eC5nKT5D4/iQFFGAzk3VH2FN/6gd36iQm+NJkfJ4cQeHJqjA1yALEJEmyPsEFcOshJlnC/8zFOmJksC7g/lNEQ2sJrOczRhF4bo2NqeWW8d+VcZkYUCCwHkGK0n2DHaDFdYZ4BE+gHFNfj78fZV4BGKrOJCB8rEBygwszb38W4udzG+/4Gv6hOYQTjPYmxlhsJdAC3VVtY5kgCUj0pc/E0rPvM0/WtU0cevTCOA1rSdE0ycFzLs5HBe8rnmjcmb2PY79/pSGD9AMoClFNpMdUe3AeQM4XMuTtu8LgMeNXlrf29v//EeybVALHxUDFzciuBoPrixqMmBBt/pP22ucIsBHSk97CEc0GiYOLGiUKmgPd0Ou5Gs+oPdkdh3AxoUaOnTMCDPFyOO6BkFVJNLnlf6Fx+gKWFUMIzmwMl2hTB6G8jMxzlh3mRazE5TkGaEkJkIP6JEH9LD8qzI/avCvDjP8K//QL/toRGwBDwuMAt1Kr0K57324980Hsc3XOe5D4Ao0560/1V+w0BAKKcTPmCcvBppUOa/fPQcZxA9mxwJVGLE6Wob9Su5168C2MbJlBYK8ydpgDlEg+cwrf+9VR/l78jfQflZf0+whdo+1JOw464+QF8j0B3H+NlpapsCkYGy49w3fUGGAUsfRrvbAZaJO04jaIsteKUKjdgn/Y+JhsiWlwgZDVwUJXltnRgSlZclalMPBtzSwWfjUtnbriYd6sLzokXdoga6r0/lWHk5RgW3fkU9YUvr5HTFpi/5rvx3VF49RXZR/PG5CJLiEeY3/H5baQh/jvhRNzHbwrtQ+baVv/f78G5UQJaz0w1PZ0uS18FmtqGrh3SCTePzl908QxWDI4dntkYZUVHJ5MAgX8BJFVIbG1LSGhqSteIb6lMS22rSGrEaembXlXXNtLU19MyjdCyIZ86J0VprE5NaatOJcXUN/E11yY0YdQMLZYKWia6upr6pkrKeqc4fRgTxKjkwggjmSe6+DVW3zEiq+ZrNL0bpFZM9Kba2hV66LkR+XcXg3H84Iptn9wWMnUrIdNTq0tlwkrFS0MdgG4p7bCSS22TmEeWn+3nOQt7cSJVo6ghcXfHHaBWYhxBt+Cm8fEszQUU0PY3Udp9JAMxFmxaKwkVpCSrbWmGlYHT9fWjpTxMaQZSWf6vtrcTRIwXj6cnfRBOjlbYUomlML4Bv6KmYpUzDNTKn5T1O+4uB0s3SlmMsWd73TrUfyXaHyytnIt5HtK+3R72Pyl+HcmKcvrzNZRpo9L6BNvD2mgFTZo5hDuA9UNfWw8tr66ir6GqTtfWgP6Lv3Ny1PlA9647/qry9vimDEtvHf5Xf2foqjhLb5X3m/yQs2L8T1afz9OZYGMW/GzUMB7vZbqHPZKdPAeGf+lUuL7JVqaEXOdo1KDYzkpEQMrNIyZ35PvPYN2wztcA1ogb4XYl5/hB308fLJzDAJTLAlhEQ9GDGg7rw38KQS0SLy5XgixF3w4bZP715xfahh+EUE+x7MzY48FZiOC0T8BJMXUNd6gx1Ci73gtr87wvoxkinU9D60uAd5yv1C7Zu8tHTayxL7w+aD+O4ZdaH3zi8wwPcAiIqUhKSi0NVbc+agMzhBxMnp1CM10fGyGH3B94f+vc9hmdmgVMFaxpNbnf4dt+Ian1SR+dO4pP5yCrm6FpQc78k5H7pQMhs7AztAEFI7IZvnYPoTamwB+R+9OL8a8xSHyX8ob+4vahP3Y2erddPn2295pA09omhzkTF+96IiH4bFn5y39E3rOcG2d3Nh9LjQwHxZefQmOr+FfY3Wbe+zMamv0WIvOoprmVOqBMT5+ZQH8ffcGpHm57VdqyKH3oV0UJNjqwH6SD9DwwpY8qAlDGZAcoMdodJh0CHCQf4Eu69vCWDkAdkEPIW2DcnqJQNPi6IIDrYudhYeDmZMhD6+rOhicWpB9UFZWEhKjpIJkNGQVvrOqGakeY3ks8rHNuvd6efdCkWSejSdHVybm9xjx+6E4UdzPAzi7lkoSIpwTOS4BG3gG3lClBOJAyLDLUHMq+zbafYnex6YXb/wo5w0thzMe+/N+ZspuxfsQqzcb/rjNnZ++Wx0F58/SWZPVE7zVjG0eGEydV1HlH/1GlQFDeVopgwnXWnr5yl0JldT9a9soU8wNIZP5GRYbcbFZ5XKKdi6BHPcG9+HM4aOxacQPFe+YS59vC/ICrJxSfMMW4PDNoXDn++gvnVxlyTKYoaT3qeLc/VeG9HGHt0c5+7NCclo5a+jXyOXW/4WZ2fn8BbouaBbBu43Fyt8FT7RY28WOMIq9uqQO3YzH/IZ+ONZ7+GQTkwxaL/0WQ3YOntTztzLECpw1vH+w9g6WVPK9t1wBB6DTyOenc8OFZbKIV6FqijECjIOLF1fOm/SzimJvp31HrA+iimfdDY8sTV/otgHAP7iamJYtr9cUh12IeKfRtWXZEEcdF/XmJ+zK3+/PhsnxxI9vUIoQWDNLmmbZTpAppTWxr/SBynnhu6chWyJvInc54eCE6M0AXU7QSk7Ij8DrzIHyuw6tIlMOPMH7MooTMYB8Cfi3ksLIdruNjJwfEbjJc75nJVeVrutfO13L4O0yaztzWcN+rG05RPgJl9zO59pKGmG5CvZts92LqGzoM9yDp+QsxLc+37yT8v5/g3/lydFEtMjwoJykyLRGTBOKfjRZwIxerXYHWu9PenmnT+YXsnXf0PZu/Bn4bqNb+Yr3ez1DcWNIkJiNOPGuxzL83lpA1HSmk6cBxS+o2JyYwJoFCj4xE2kHsZ7ETzdPT2JIxGTNBPrnws3PBPfB61H5gRkxCR5O1olEsydXsW41gXcP/I3FY5MznnWfKfiOrEIiqVBLqY1dHV36uDT+NBd5b+7PNFzp//C7NuPN1/npNblCUkNQp5ikeFbh05PUL/cbqWfhiMqrw4MVq/D2Ppu9xmF7g7LzBs/3L7zSmkye2mOQMXc++Icwg9eDnw7oLcYfnD5wffdb8UAr8/Bu38J+ML+f/yn8wtPAaxE6yTijWifIT1p6cq4czIdC3gLGt7C4HpVSEgGvTt87hr2wCjGcXA9Kq4tx5wlvUgaFbX/vT55bnmS3PPQD6mL78vH07nvZzkEDkM6L9sGrdCGqqfPUxuhe6tfKKZvZuTqbcJgWFNKZ60jHyy9ATV8jnnogU3qk0rSuP/p/GJifYWmnYmoHaWZ3/x0sgT7yQ28lvAAUPEK7xjdEmYeRdHD78bVH49+QkhrSIwvBFpBrlW22BvtP5HtBulbcKdssIijD72k+ni6sDuIvb+R7RdULBdUHQTtF6gR7WAgdD/n0SGWryDLzMAB2/Z/H3i7z/5Kr67UxvVTclXpz5Xjc0Piu4BK95PJ8/unrwT8XcpPfXd9jGUyxBrQmlJcL0XLS6C4DRPYlWrcAtL5Yp4wcMUejMCXtFqO2tdO8eGOglEbmxvsHTbPae3nW9pYCy0v351+KF3DKvPm2OzzD+Wgpdd8IaB2f7UmtDUuGhtL/LdJ9vXdn5UU3uCcjUj36qH5YZQe+HI0HBQ8n0bTMoQoyfAyc4hhxiLZQAOGBDNwDMQfjlNbSpRW06WqEXV1IKjNfjiupq2CrXpfiuxUFR8Nd+LlW9e3PBzFnPwIF2Hy0ZC2YOPg45B9JP/OsERPjwu4CZ19LVhLaV27l7bCNI/xuj3tHb0MvWy4iPCR8Y45smTHMUgLgYeDq/icRHBWUMm2LgpRq+nlYt7LpGKYuDB4HxF5N+VpLw3XMdYPzNXhv1eTb79kUfs7y5zffLjvaS+hzVpfaHBqY9BDcXze+VyfweJjDAAu7DI4kuK7R5dPI/bwd6J6Q3JJ4QtqkTeJsf1gIHw30+XRru8ozhI84BdYOx7+zcNfhHZ/VEX95icpRG2rkHJIFMfwhVJu5+S9fLRqwz8Z2yIZVqp1AlYEFYhqf2UNLnQZVmf9u3R/cHNviKGpqedhZGbtrKcvdIJ5TsWBqGWOTudwwkvHG/VO69jzIPtXJ1ctUBgVoWpr7PPThl3/XLjY5Mj+vzS7h2dHUwg1k8iRY7QO58N9TJ6nje3K0WxMPAf4Zckpynu91Ey/p+jNfh9UBM3bP66wUPGj39aW1kb56HiN9qW3y0/AJh95sqpw2cuxP7vzbmbnOZC2nzssGf75h1aYPW9C9eG9M7jV3Z2qgIGZ+2ODkvjIHqAMOhXITQnOGxYDE6eTi44x9mlgY6B5RnUvc53d8VFU1tksb/ml1psWmxXZrG/WmTBjKMecE/qQWDhrhz67/SnLvMu868v0H/vyommdkm0rz4cXxrsGFwaedSx2ikB+kM89N73GToRskVJqqfPq+l5mg00dy3k6VDxWfGqZy6omfqbQWP+S4SKQrgosnJEHRVVoo6cLFFHVYWoAwat1sGeWMNVdOLw02zBr+Nf75+ZOvn2+OzUqftnlsYXywS7/0WXrOIcKWTbtUOyQKRHtcC+Oe5u4nWzbpg4NmniddnPFtddNVbCtDP568vtT6+7fzh6eTlYe8R6Me/IjoHa9I3yqKnJssiY+LRMWpJryci4T01mTlpmelZxYP9gMTkrNz0r9zapfHLAuSQlLT1LLi48xPdZL/ltL8Xn2abP1GPy+16yzxSc5TXEOnGYTXwtFE0RLRC9tn9NqkAqRarwP4sJbkjkEPwoOP5x7yNoDYx1N1nRXB+2cox83Ts1/2IH/EqCk0LTHAz1E2x6ZB5J9PraFfl2N1gTxNUS3Zp4pta22RafMkhZ0SHhKQ4mZsEWcZ4DAdaFEr5GfLqCNy5EGLuY54l0SH0IFKEYJzoBdtZCQlUtSE3NTU0tSFVtXOzuPQMay2aFbVWExzOdN9uezJRXt9TklzrZmljZeSQGUYI8E2ytrY1caql1+ce5m6oruKpLTpkVn+KqI/A0Fpwy5qqf7LHWGY3RHtWNvOO9mTg+OKA19sgaHPu51PSMI4Liwrz96kNaPSSjySpaEmItHF5tMUnJRVlURedjRo6xF03PNSSTdMFcQdwKFM8EjWPCm9XAYp77uo25prT5LbBgY1xgcqyomRZJCVYmHJf0q8ap3eYqGajUU1HVL0vI1LBTktHUxNtBOkjVst8tvwv8lGFSUdnbgmIf3+Lit6VlpERpdU1pKXVdElHXS4sIl4biLccsqfNU9zH30Hleod/Gw8bQXuwWeRwT4KF22UwKr6Ii00NuKEzMLIm+dcPb3dkiNeAWcA37GlOdz5kpixHOn+txrQmPy7wT4h1Mk8i7mEGiR2YmNkXd7DmvLqV4ffSyewLYAa8MHOYTGQJ+dXwEkmYVS2rAh/Aw8B9w5rak3FA9mcFdwC4AldlojfnP3CzL37UzgKVD+gReW5+mo49X0NcmqOhqgxKqNl8gx7uZUoJLUtUUuXxN86wh5lQKIMUP8eOiOX5BvMBEdq58MijTN1CnVfFosKy8d6i6DeNi52tkYHMDnIyc2Q0YGtj5eAAlDwmXytIy8PC2diI5ONtZW3m7ToQvdfPF4POl8e7a4ju2dvpElkC+PRk5TR1qfWJ6B3WAEgBxnKZ7D0zPSknTF0doXAVCI8d5JwqMxT1DBRJB74aCioJKbj7+ar5CEwKeACZnZ1l3srMmXscmvzFrY3DQU202X423rWMkw80ioz5nt7QfFBX6dV9oRp9yxVuYJc66DvaEqe7KzZO5QvrS3nOkz1V+Cp1/5zv3xvgY9yZj9yoDsPTdhbW9yKzY96nxoQiE7RrPrW48fzTX06gs4KwecJhw4SrBtD3XKEm3eb571KjpvJXY4FSUkIyztbmauIZNa6JRopFV5Li+sa2TUTd6Dwyc+P6kVhL0Zk1p9LG7peUbzV/6iLYOHla2gVaqOzw5+90jH/yocbQxZGYD9S35AXNKQRktmSQ/oFWrITt9XSamG5+In3w57IkEO2iq2BlIdHxyjUo+FDV88qZPKEgJH/3DnG1SF4lbzU5LWYqMXErKD1h7FkHNCbARa4gMnaLp+zTfjDSuuqQzuCFOp90L1vfJWEMOClmDqJT6/Ki0ej//pLFWUAvU/D7zfa9dXVs3UFtHXU3FR5kupvo+HqZaKB8vr60tHG38PBTJ8oxmRrb7i6FCQg8ts7c+Qczx7Pf5s/xd6dLrzpedPso/l8Rclydvd8GqUjI8qQ/UAnl+LosMdtRnZ6pkXPIOoo8sC+N2MXVxD4PItcQx8AwInAbXOt7QeuacXHakEd+Sxc2a3xAeoHa/Eub+uEX/Iy2x466e7HI3OWyr2p7DI5mB9mmwOf8fp8jirwQ6wfS+vQ/MbaDWk5pYU7LKKNYZlzTFDco0lZ8SQn6mtia07q1wXEXtMDclPAwNTujur6P1hlASHypz9ZgHJ/GGdB3zDwgFKalvbEP5JXklAxycvJlw2OOyfXRYU0Swk7N67KaoMK4XoioEEVHCdTERZVUXnAtIU774RWU7XYjxUmgWOK8lksdkv8Z8R/lKa6nzVqLAXScQrmdDRWUWzqaAo8c0sH6HLWodh8ouP1dPr2louRSmbxaFys1dSAA79xdAV4rCMZJQpRMLPXBD5qaY608CveuW6ej8qYX7ru5vgTke7RgS4uYZHOIi7BAU5OkaEojYgQL/Xkb9qQTeUwn1cOhe4x381OxlQqPPfy3XIHPnkBu29U5zoZ/DacGp4OMxIHyKCWlJb4lRZ6+V/e8VgT8k6knBp+5n3AuqmpmrXdPdYtxAOYElnKwcfovsfzw5JoIakXDcf/NxyiPKo3THNFDe9QU92jvBvCEdiPLuGrBNMso5Y3BMaDxZNNDf1clZfbZzluYZ446S+dfdOXSGu22nnpQx8OE7w/25weEwNVHAfmTMUseTua8UHk8UvVXBoJFpzyUMzmGA41isDjC8IajQTBQYL05v7tALwvYO/rWs9nlbmk5M3e38rIoUJGqKJ/eKfLSUo52CDUHEVm/CZrJpUuceRgq4XH41rY5R3kGB6rvARqWKssasnPLG8sKystbsrIqW8h62Rkogqry7MdgfXQRsL/vBXwoO8o4FjtWPqj2YQ3z+6yUv9OzOgnOUZP76bTPe3AVifGpCWMidwrTYlnjE5SHGOJASKaeqpqpGhHPnzhZlJzmwO2uq36DQ0sOC6wtpuU11JeKvtj69z2+4pnJuTSg7WlpNQ4YMrc0mzcMmUZ23Q+A2gRwgE68YQ6oMzUirDrkRE68YIEP2LqbExhVTBE9J79oQREkHw93bTRbDFhdEnQ7BmrSKnE0ULTkypLGamlpVi9xW+PfN/Afgzc0MMXZpNjinoaVCkJIk6MvLKqhBR9qjpkdhfxgcQDn142D11Mxhsr+tSdr1Vu1tG7Aenv8CTHjG72bsAlnD2mIcrqUi7oqfFleECdNduSBVRtTG62sdr7NQdFUIYvaJniNh22k7ErOms0RyW5qAJxoMelMG1l1itNdU+y29twiMVTcFjzMIA7FgcUFYM92Fi+YUhq2Tpxnr44lnMC2kgdQGHeSD0Ab3MTprZx3sHuvELsLhDolb2Hr1OBcyUpYSg2UzDI3OMhKyJ5NsRozMsAoSXjBCY2L8xkvr1X0OJcpSIrFshqQcknt4raz6KChLIV02Y8jmkIaLkpgXN5XkmohnkviaSeICk8S3dklPYJV8GnIzeaQz+Z3biyDBqVVamoMaI9W1Al5q9UjO2Gn/HwVZB3YHkS7tNDItk1LEbaMS06YqwLQVwYyM2GnnH27SeZhaoH7DLy9lzeiWW84tHGlAymTGrMOV6Wi8z0zR3G6qjrB471RH5ZDEKuskvMus0cBuvdOB4iZdruzDeATtnaRdQsrNBnENhW7+5oHlEJhttQb0f8b1NK4fP6KsjInMu7skbbaU0hJaI3soMluDfkNvZmS82dt43vl//ZHy9K3CJi7ucvXpD+VTp3NrJoBQgHppBnD7+NEvBsC2WZJPOp1upmdT77ahkFoZijZAXz/sPWR8LvG41szMrC6DRUyWPCyf4QF9l0nmYrLsKkMNXh6PuWJ968PyQO03KV1MeWOWMQ7isbKyGuRVuMZU8vKYc/Q586ZBkwf68tNVHmO9n5xkYLJxgiiRzurtDyYbwmAC9bHN5mzt9wywI76+p54yeA4M54rtRxkwHevFNoM9Qn1PGJ0g7uy/ZII8G9d4YJzR/x/Nd7+4jwgF7V99jyGnU5oBdiQeK+xPBTvbz/6wI2EM9vm+bS5C2DZM2n6w6dPb9yRgbF2AnP79A8i1l9rsky52x8UV/858bAEAvPY792aAj8Lk5/+F/j/LYrfALWBBKIAA03lzdoAFzw055yqngbhN9K+Wxk1TzNu757oys5B7LXOq2Afti1z7CRZhwyQnxeQrF1sywaJQj0HHsxIxFSW2y1B9wlugAYzBEKQgFiigAV4f2j5PiYtPRIrb9QqTXZfFtFWK00W23RRPZ5fX5Xl0vLyiXcLdLg5E6nfvkqqq66Zrnc23mdVPSES0FUHrkye05VR/i56hU2/imcqK7NfwTKzEOpdLtYip3mOU4qjNCp9kwt7OJd1KofMu4mXGpmJFlBjDorZvr8s051yrZCZ1U2iyiUxkdNU1NJu02V6PcUtoZfB+W2p9TksmZ+hmI+a6RHD74UjfdaZwTrWP2C+zSd4llyhtv0awG6PkSbSK5FZdHHoZ+yJgi/9Jqv/YtAT2M3WWLVt6W2ZZvqLtp71R0lhFlw7H3KPYvp1slgioR3LaBo1UlZFcfH0d0+oFZ/yz9n2a9ewKo5klz7ZKdUoYe6VOv1N516S8+iRpNkCr6nlWNrVKYlgF4lJA5tTJbcKzbWvyskNImxdWfCCe8E/2m/UNlc3ExEY/13hMMp6TaHx8miBCYyaRU5J6nZQM5JPezFqpa7lHutI4dBa+Fa+8GvDXY9emc6ULYIFt2a0qYgBdkAVpMAR9JeMrTZ0+wk+dn+7ibjV+nlzalp9g5PwSwmkY4yWsxVSp7Tu9fiknKzJydmc+jA0psTPrQ2EKjE6nTNd4UNt8Yv1SLlAwPy8O/7zyzzH/G0T+OB3/JdMRBrDm/VgMJNQWcM+XLcADGlILAieAHyMRAhxGonAK2Rnt74xhJNZ5NIGDtWTKycwoNf4ChArkzZMXstMkiBFvik8j8g9DPtzH1ubHlYhQccCn1qRb3jKFu9buAgVV4dahTfhzaYE85zR9d7U3Ct8e4WlYPpz7wMIIdIu39JsERNZLkqUw166yqdNe47IxWQA80QaDtQhnAa1wDZndifAXyFP4FOHqSL+ZxS3upkebGg0GTJW8NkWN2GvGtcYfNzcAAA==)format("woff2");unicode-range:U0-0FF, U131, U152-153, U2BB-2BC, U2C6, U2DA, U2DC, U2000-206F, U2074, U20AC, U2122, U2191, U2193, U2212, U2215, UFEFF, UFFFD}@font-face{font-family:Roboto;font-style:normal;font-weight:600;font-display:swap;src:local(Roboto Medium),local(Roboto-Medium),url(data:font/woff2;base64,d09GMgABAAAAABrQAA4AAAAANqgAABp6AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGmobmnocNgZgAIcMEQwKvFyuQwuCEAABNgIkA4QcBCAFgwAHIBuKLQXcGOrdDgQJ1dnoKAQ2DqA8diCKisF09n+ZoB1h2u8CBzaohVCEaEVD6ZQMOzVN7qJbxURtyZnoHE8x9FL336xJ/tR8GsOUK2uSSqFeSjwenh2hsU9y4Xn+O6tzX72qSiVpEOlJQxhC6fUIaw2/lkZ+6k97AXCqA+I7E69YIVIxSfrPez/tEL/NP8qoGTjFhYvItxZ1ujBqgTZGLQzEnJEzKtDGnFlYtSjjidE860/083y659tpe2fmuU1WfI8zCBAYQQhmhZZY7C9GbpaAME9Qzm5379yb21e7GyqNQm868KaAKKqTBe0HyaX9uLQf3OD/pv1+WrJz20xCKc1zEBJhXu+Rr2avyR4cOQiDR0gQGv6323ie/l7TnBTxBINx+4VuQsFS8U2oVLd8XLEV0531/3+t0tbbP70DWAsBXiQhV28s6hjT//+ayt769Qf4dw3WhHprmWq7c053EG1YRUc1hKB7o2ZyPKEEVhEybuNGGBdQmwirYhMJ+Obr5MmhYmydqyxdapyHu37x3f591bu+Cm+uIEFCGsJBxK92H9ZZxRcPzEKTZhj034KgCAUAJlF4yZRJKOAj+PkJFSoIVWoIdfoIAwYIw4wgjDKKMNkMwiyLCUvtJQgK8AJeBBDAjV8T3fG90YfE3/bjB0j8a3V5D4lfetMnSKQBgkECyOI/4ydwAQIIGgUo3PYFhAANNC1LetbD+35/upNO/DScaf1pw221jOETcD7+tfDpxIxIIMfz/VgU5rhaM9Pqtnqy9jp6pnVqt/S4vq1cSVsS8mm3qo/ffV/u7VF2w/TNhhkjy1nl8yXzH8OkBIqOnTrj4OTi5uHl4xcQEhYRFROXkJSSlpGVk1dGIFFYHKSppafvk8++++mXP/46d+HKrTv3Hg0MjYxNzSysrGF7escNcTTE07pAS8JNSDSi0AoiBtSg85YMGjJs3SKmZStwTHzDDhyX6LQNDhRPq/zTz4HmhZuWqEmuISVTVlVTL0S9KGDCAtLS7z5v3qBEw1ZNYmaw9uZ9w5enukriwKMnlkCLts/NcvVbT8lTTqpq6oWoHYUwwEJa+t3nLRqUZBjLJGaGxmfWnjfx3sWXze64eg6bTcSQa0JJWVVNvRD9RUkYYCEt/QzaMpHMsBBeiIsVgifvjS/MEdOyWUxj1oxhjLF3jDHG2M9ojLF/jHHA5r5nGIqNO2nWKT0HFriZ+SVYHhfkmVRAy4IevQvcs3gyDHAs318qx601BK0l1g0iEXHuNODvEiftVRaLWU97M5Obia6jraPtp2ohw5SUV88zqtAhmkE3C/mGPTrnVC6kuwlaPHZVOi6Zw5y2IKZqbbgR1GidkUyZAqIitVC/GaFp2XXKadMcnnY3yTPBbxsGmlNpXg/nwKCGYSnggwt4JsEmTYAHcPKomiyMFkxWQrxa4DCDTz6ERa4V9nJZoVWlKWVRVVMPUQ4lmLCAtPQzgVmWbdbevEfcsFqHbLJoA240essnZiVPWVTV1AvREgpoTFgUrgEIaUHfxMzam/dpGbJVXeMyrcMcWcH+UyNfp0pfCY0V0psWI6ZGk5VPFlRWsP0tSmZQNBsICrQRCtqsjac17UDa1BTSLvcFXJUQuhG9MsaCObAC7SEhbVe0kPEWStwgLMBssWc7uXwr8INKiGPTC2HUOlRgh6SCcTZNQdeczLBlhEiJTEMrfieS6SuqTqXnKLgrlGCU7aCiFSg2ptFfbRYfl9HnJolb3yRqa6Eue4CWDNISmeraLPn6/w2R6q4mGglAjmY8/XacOsI+JuBz9wm5WeRZ3nr627kyRsLyqUhSEekItlLqsyFXKiFnKiFXsic3OdsxRDA2YI5SX4E59YAQqAxmkmigZYyV7CUCZbKnzuRpRm4Q7hRicF6+2ZkAgAtoKhgYYBiCm7bqbSHZeY0+6zxoIvfbbqB1QH35ZgbAMMbor3SP1kTv3iqZu7R/YkxmwxVtzT3a4F6OWI8zBAyiLIPWJvoq8GRrw2Oic/Dee98yOn0YamgKPEdmNbWIcVk7TV8ZpZhNC353V/LtdYX09JWBSuvOBeQEOqNGM7QQr5uoNJfbHjR4e6aM345Fy5lRrYrUbIPuWPWC3kdLD1zOSqi5+3jM3sATajpGXe9ReQNm6ffCkisgRi6IRr06m6pR19NJj4ysxSCzwtoH4gNQN3MV5nKZFQGXsSFqW1c1oJbBkv5A+thOng+i23IKyI+d7qyrbXLsnLFgrnbOX/KXLVinesOCnXroXkKW2WLuCXTIMRFH6JhtAoqksI0hi8GiY/diCSC2aAloYos5uHETRxF1Zvq9AAHCheIlBBMbMLCI4CIMm0islItPNjy6fIa5XChJEmJ2HFFERsMEGcp7DAxC5wUGIDr8ShCEknwWKx3ZTfkmodU+cuHBkiQdCTbeu063hREbVLXzpesaZSqwbJY9Ozvm4w318fUrE8yTVQ5JOz5f4yVXG1DvyUX03tH9q0VBS8h4WfwP8+bkJp1cDdB1dUq6dAZskQwtx4/HDeJF8yMQKZyB0jkZks4Mz/QcyOlvr+D/0Ojc+NOVEdmfU8/d/zqpnMA/T+yM/iUSfMsmgV1ld5oswxSZpmr09i9umFXfc7Zcc+S1PPLV+Rq3bYGZijtAL1kCn5I+cL/KvbNVGjVptlqLVm3ardHRIehUdCl2K0bqkaNstsVW23qu3Fjb7bHDOF6AtIOIKqDZrgo7oaUNJjdpVUdGkzbqmNKk9QpkQpsbuoCp0M5lHiCrSXvrmNWkQ1ZhF3ToBjAbOgw+7ckND58zESEhl7DIJR7vFhW93i/uIRBuJCT+HDjgP3js9viJwMmTw1OXQ1eujK7eyNy8Ob51p3D37gR6UH74cProUf3xk5mUFEEkLqSlSRmZpawipaS0V1Zeqah0VFXXampd9debN6Sepuablta7tjbW0fkYEvqJXuz4iIeBjFec+TCRoHChgkFhAAwCJBS4EPYWeAYgSyBjAJgAgMJAZlLuWcbYUza8fEXmGjllRKc8z5iz5maWnNBI8HZlkeAtZAvZbv2N8vJFjD5HeMYcKsl1IogcntO9GFCwoorecwUQApfx4YtHcAfucYJc3vuEt6pHAHyLqwQcgOHFgyTwmPNgUNbKfUCphGN8jJS34pctcnx5lxK8enYfbsNDxhg4oXx4mnOdNN/oyVlsNvKZbZKmFVZq+Uq2SKWPAjs58QJ/46mTPkCi9+hQISPqeIhp3dZ21GkoGymcckySxmBfLWI0uWwqrY0XVyxSQKWQCxJP6lDZfXNzGSiEpwWXqWL5WC5a1lalRb99g8PNja9u0q78HmP5IlZqYdSmkqrD4Cyg5j6CU7ur6bJm5YjA9rY2cnLKZyfcnInikdQ/9XjCMGmIvK8qdUKZhECZV65T5qkPS7DhXXTxfQzwd+FqSiTvKTOLY/e6nZIUO+VZBDoahcwL7FK2sXOYbtjfKwZh7S9svSoIMS8792xoDOqYFGfCIduV2dNoqMXBPf1Pj3Q4HHI3YtZTSM62F0oJxYvkC9ghA03q9DytB9WwtrLleFtu5qMf9rkPZnkouhWrjr230MoB+rtZZyqnm3dtNsWwVoRH0dvKbkZZfPtGXfYYigr4ZSPv9WLYb3tlYozCMgFvzEuHaL/MbtvMdh0CFxOaC5ocBflzKhhunczlqM9dRCFEP9qNU8ywAlwwrIlrH35E64tWZ5Zu0VcOt2jWMdt1pFsZeGVuMWCBJ6xnckoFBWrkpGXl2VWztJ5cu1r+1Sz9sNQi3KBS1Bafpux1IzcLY9TLv6jHuDT1sRK4/JZoEBCSq0mydCDXNqIBlcM6obRF2sCt05HFGPNae3Kqmig0yhNuY+1QP2VNHgdzCPwc4MtO/UPATdpt3vzZEZb+sUzDyWbhUjibAO9VJq4Gxzq+FEZoD81kPnWSvcZiwSfcTFtJY6QdPp1X2LjKvhLQDTEdt+Bbx8wCUOecfPTNFAVy+oCmJSV8aeLxQyd9weAe5UM0KpcIua0ys55LCuiTw22RghfmFt1cocX3WCVPjlQP/RaxysBf5yylxaqYpYjFCQKF4wR4lR0nC6kBr8jas/R1xOBcpUnXWdhHchs9r8fF020RsFD/Oz6UtPX2tw5l657c/zErE3jcof/7adpaT+ely/ktcAmztfqzNVTKGyB47Kpqk5mHN6qO8x5Sh7iMxSF8qlJyA9f0Iag6iAdsc3AkvOts/gGAIMPZbdapeu8eiW2Mph9o2MqUz2jWZCelyck9LVBeJD2F69Nc1GNW2J7l92Qzo2KzzHi03AtF0lplebRkychVHwyNj2ELRpPcelLOuKGyjvyU9CekdwnZgzDGxJPb4cH9X+O09xpwPGUxlKUiBEVTzoLsyVIQL5NsvhqFwgsofr4yS7tOzJKHayNpga6tLwvGrEuvLsLUnHItEsVFMstAOelkoyFLwy5q5IZaI/P5ebQfhZYO77+VM60AR2/FGGoCHKeQuwxlxWM3dS0GpeWL0sKA+1dVb9iqcB4zpE8ds6VzGRzOGiKBCICjI1uotlayebLxeVtJV7pTr/VzQzSkg2DnZWs69QX2YtU2xdNM38JBx30TKBT/EJ09j/2vkCsj+DK607cv9K5gTunaSZzYMlsoIexzUCZ9FTXrtpi9lR4Z6SUSDx5afdZovKerEuDoxT1lYRpA2RWwffx9dgGOvqvtr80GyWyAo7PBJS86e3cQuzEGb033sm2sbCyNqN524KZNRmF7zdPN88/P2b120PN4y/kL4FAbXUJaU+KG5tOWpcfF3SRg/XKJET1x1/G+KgPKk2fGwqWE80uL8flRj8CAyuhRmTBdsnBu5GMwwsayKlDZGZUANa+2Wo3Lyi77f1PIwUNj/O/o4vrh3cEx8aXdC11XfAJdqJTgAGfUbfAcC7fDO3Bzjwc4E552lf/El8Gm36fviN4VPdX8u3LwBDgSMdjFf0kUYPZ4Zayp2em9VX4FoHIl8qmaFllX1VTlMfLGNq7rlXP9RD06fzbsjIYQulDOReZfj4ePj5aGrKYKeC0yc/gY63Ca095EoP/vVQJav8XtlRBvRzPxdnd6rPttEQ8xmezgL+jUH8lp/9EJfEIgBCV+R1be86n8ndtP5Txl5YBYBhSXlVGYTBx48PUVe7RHOt75x6ZwKtl5b8o34qcggWeWK8VhB/aLmha+ssfiovnVbvo2VGUENNjb+dcCIlp4Z+pcY4mZDQoBuB/j3JZmbpXt46c41nBprnXUyMcO463OUTbu1eDHySdmxK3rKqfLX9ECFmoqmpcbYhFZY00NJbL8wztaDw49TNNQsH8VtlbW6tWvY03TW9TqdpoGugZyAPzvNRB4hlc/wff149hKAT/j5PF8YJP08z2MBPxHX3Dp5lrtkKamlITCMU2x1pscADeqcU2KSCESyUQiRYrY6V9WBkRuAVH8pRbgjkAIuFzOdCmKP/V4LZcKqWdynvJyUoBoA18DG+OIBKD8GLBhty8flj2Qx8JLAeEnrXbEVuhCC3ug3s+f6gkoQd7B3naSAbuwZ887oDDxlujMeex2EU9GyE3Q59hv+eXKkBPcYgtfi/jwwKDUIphzcKeLzfErF04Mj/QWSQBKw9dejQHzNi7Dg314Noy9BdcTrglw6h+r8Up63LVKg1Pb9MoY98CBzrwffGddQsJsrYOC7FGAYC3jas6+dD5hydKla2/vwU3kiIOrQ4SM327enqW+NKFeH96HuzsB+RPwiqG7RDAfx8I0g93l72QfF+B1oJQNU3pmAu6T9F2mG3MX4OjlJrsmDKRaP03WO5eyZOnI2NuD31pGXSRhc1L/d3LWNeiz9Tv/T44ooLlmtGrUYLTOedlWQ9X4sUDXSa8oZtXJDxI5YrEScUCRZZNuw7bJJVvfO6TKWZ59jaSTqMvWjXqtc7U465AINwRutR7XD7f+ZBlqRWLbeVTNGQX+w39vNbDLNL9sf9Y191NrEfNf3LalU50jWzveleqd5mzoGPTuc15nGh94gqOn+CT/6XX++QIBoDR66naxKfu8guSK49sEybLKi28NiseNcagO6wcW50nXHSRPnMADaPrU/mzhqBihvNwYAYFIGpCoR+rFc0Siovhz8qKFhGJyxdsRnpDs2sGqspq+fFEBREA8u3qw9It15mFXBqaLnJvVuQwUpr2Y9oPUM3Qc7zSY6WMPgM/OttGF6WdpgGBIZ9Yy2cwqpkDGiA5y7I8jlnNFJP9o61/5bwvKi+5zUYIf59QZ6N2/uYS6sOTvfWPzqAQPD4m6dl+YFP8RL9Db9TYhPhx3fjLG36ISo7zCE3KncGPoHjTnV/pUSkCEv1AiW2zpBCerdB8t7Dbocey/7A4vjb59His6Az5Y5c0jaZzRiQf4ayu0Jv2FBpfiOmNvCGQUlGTQf5r6RyTEJfdJm+3aCQ0Kz26gVgCOfrllztHP7/MCWAjw8QKn0/d6NgTn+eZ5jk7/FToNZLW3ojeu+R1Qenir1fCJs49C5rFoyaR0iZCQiLBmildSZoJHgYO9ZyE9wDMNBENf/szEhT7XpLyVNBV19CJF/0kc5Ozh56nx9qnnKOjsd3BtwQyDG8OUsXUsqBO4NpE/SHdZifyJK9QoLEuqQmoTYCjJNCGOPv6zgKhc6laKlLpul5wzuqbKcXY92yoHybHeyD6rgjG5eQncwwjLaVAVjlc9ro5co3c7dY/6GGPKkrJ9/faNiQpRkSSjW1hUlobylNKSV1GyVLjPCl6pqGL8TQfXsBALYg3RBm9nF4Cd55iPQAjd/FzRBW8c7fp98EQaghDKNRGq8L7cRfpzwMAr8sjlc8MivWDvHB6z7wqE9JztzjzXP4BA14AQUp5ZjkBIS2YLAoQRShYFgQLAA4y9jj1iT2q6E6jycw/cqnnx83nW0P+6eujGEWSY8EVV+lAuK4uhWDSA5pokfrhfYd2+6OKLduf6rxt6z9PMs80ucjMI50zeN8JFhhBqFhWhhougfz+5BMYRUy1Rkz9fjXS8QF5QTPQs9A328yFALAKiqhAvp0plQa8GgIAY/kcPtard74OaEoCA/MT3Xm0ko8x3tAVGC5ii3Gk9f0BraaLR6wyytjoAW9c4hEjbGNfbb1Uj5pg5RyL+6rYmitNzrAsZxla/3BfZhHjqzk+fUMeOeZZVttNTbSkaRs4xvL5q2ar8Hs5BwMO7R6KPe6v7dVdyiA+DH+5qRSCk8vRLz/K5gBZQS/1v/mAdgliBU8wA9l6ZwVmzeMPo2cY2VI2ONFlNcaVm/I3pG3DapoB3juJX8Rb7GV1S2ZIlGyJQb3HPsWHpg66mdthTNxxSkJSXlI1k84IHBW/sjHGKsDK0K2IY2MD2KTRhi/MM3Lg+gSmHOXRsbUjvnMK5A0jZb8VFwKMMyH67GQ3XJKfMNjajZigSeKowP22pECigGHhlHtlC7kmRXhb0aOm18auXcgZK6gAskzLjkQ67/s78jUBINPEGbw3fPJc1UCWBM2XlkmvejrUg1RCPKPfP9U9FUiWZtbJeC3puhBgpQIAoveTiToEL8FeuRBcBN0sp8+sQaOEuC9h/TNCL1PO7lFzXeKe+IUshoarpYW1TWiFOl2SurPjW3EBZh2ShpEiy0BeJ0OKTvMHDsw5wdF5JMj8/C0i9z/9QRr7KZP88g1tIpeTmFXBn0E7X21jH1Phx+xbGqcNT0zFL7336XNhWQa5eTr6mOkrhZqrk3h06WR8q9o+tJHHZhPX67Tql+8R6epqBOwUkO1PMy2mDbFvIMLSdck5iiL25iTWOqygMaVLsHpV/ygUFp+ku+eD+Newodg/bletACEcTFIo7iPdT9ga3AOpXCpLSl5uGVw3dYe0t7O3gWyRiaBGVywaYpZOcXY2BpdnJa1do4ZCtcXqv/jO5A4tJpPYwzbJVFZgvSPUWh2llJpW2VzbfL2I0rhTmZS4WotITC2UWC2PhLFJFidQ0n+/YRVgsi2OJKiFRVGiR9HVV9ey5yR5m8So66RR/tkL/qZhn3eLM7rgsSrSsnB+45kb40i+6a1x9NVF3d629tgiP5lh91ZLu/1/bgMPQA+NwOXT1NLoecnWdug1Qe8fBdxcPXHSXw/TXKYdhxk+V2ruQ33NcJMw2yqCvdynRfq8HuAhhuDRbixCP3efdxTpcjw7NZrT/KlWfBQQSnUIB6Ctr0i5nJ8OMroHXDw1AR9ZbWokepLe8d+JHce//P8/rgaTB+NIld8J8Fp8uG1q/6O8+XahsK2R/vXbzoFbWY7f8za29OMo/6Ln1s0RQTB6ap9FvlOq30P48/EIAlfX5aK2V6R8N/7kSHgAe/VHoEsBrovLv34ndUW7NZzVwUAAI2EveDM7iSt3ek0chrNYYPJeYowr5H+ZokeXlcNWal4C/+t0O7zbXZyVfeTg8stwwYF6lOJx3zh7CFezDSjPGpPQbvT1s3RI8n8sMz05bkqE/FmnnM2pjr6n7cit2on1ni7VZ6/I67fL6X2ZNtOC5gO/Z5/KerLRuc7Bc7G+L7LlObF7H1C0O16185aa2pxtefzlYF7psLnd8/nK8E8X3Xy4vm/01wuIZ45r8IT1bTc4++84ke/O7vF4nNbGG5rhAltmXkzLaQOqYQCxyfpNVr+jnlVjdPNP7MoH+1mvFSevl9mWBvVqDkb3HgayQl5X3J3Yd0y856E/54MK7LGstKnmute0psb/yXY5S+A3zKhZn0npuA1JBC6zCk4tecGTUt+DWdyMXqPyU2rjoXexSz0We8huOWwyCFr5FHWRtgsSbVx+eRN7VuK78k/uQTIeSToBB+8QaxkQACAWF8rMQwMU8BqIdYBquS4RE05YoQtmQa1C5Oi4xyRWAhcQde4YyxuPU/iVAG19tofkWWCNdGb/S+2Xpui3nYi8xd8Vey8xWgtYFLekcVaBW35m5VptZZZ1iDoVRjZiVaE2QbigRLbTWUh7mD2aJmSyNW7DKagvVMnUq2VGuWr2V/GqhjXwicU2vUGfIna1Pl5hphWJ2ZcO5Siy3ynws4TF7oZbZxGoUG9CrXadhRmflU570M1hdW3CROQAA)format("woff2");unicode-range:U400-45F, U490-491, U4B0-4B1, U2116}@font-face{font-family:Roboto;font-style:normal;font-weight:600;font-display:swap;src:local(Roboto Medium),local(Roboto-Medium),url(data:font/woff2;base64,d09GMgABAAAAAB70AA4AAAAAQoQAAB6dAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGkAbjgwcgTAGYACHSBEMCtpgyyoLg3oAATYCJAOHcAQgBYMAByAb4zcV45ilwMYBQHj/zqIoV6OFKKoGieD/6wRuDMHeULs4TRSwjuJRFI+iGCSKIyo0tu3vUnyz2BtKKHNB18+a42mLmuJ3MAA+mqr5bDgO5VorjviF9X1bH6OhkcTkoVp78vXswDHLCxEoYDCCVBSgA/ZIKhV55QPs7m//wK/z/St6nL6ZHVMLdvROn8nDqRmb6vVtalM5VmrWp2KciFwka5Erhls7LOBpkfcBBwnacoiwW6rJv9JVdkvcPSMdpM+RcwydOsOR5gBB2k4km3duQ2MSEcTO7FCrNUHlHtxmqoTC6E8i0ucCFw0AP35uk7bRKp49f0FSpCVKF00ku39vpvD3OstWP/9rTl4odN4ssHZGUYh7mv6o6BIuKulZYEn+S6TsaIFsH9lhX1BHOqQAs9ch8l6YO4aiS1FdVaZO23PRlumAed5t1b32B2vFXq8JFsFSzpzfN2cCPX23hLCGEpaylpJ76rfPtYSQtK0p4gX27DXU2v6erg2Cmlx+xASggB8AHAoFWNCiDQEyQwhQmEHAVy4EbtmAAAIoACsArAAEQABYBiT6MIfPtnIEws0R9JNAuJ3udwII93hdOA0EGAByGwIgTtxKPw2cAAJAAAZQAApwgxUERGEAdJCnW7JFAAdmAgU76bQVWHiTLF+B1hAL/UpF4HCuCIWaDrrm6X4TIAyv0s0LknPMCy35TXdVMn7yffEk85yf+T9rs0/5QSrB9gos6F0kWbvoyFVVcy4rjh8l5OKibOhc1LXwhWJtQsyiC1u5/Oo46CIzglyY24rzrbe9mzkq6p7fEUwv32FIED6HmJ2UudaOQpYy35ok9z2l4ky1wBoC2cBA4VdvKy7a6nRhafai6lwW33iLrmplcm4l9kc5e2aFx6bsAb9O6BKS+IzaZcwuBJlJZplxMKZk/Az+KTwu9wkTUmljnQ8x5dJ2t/vz9f58f/+4+ISMK1evZ2bn3rpz70F+YXFJaVl5RWXVw+qaxBdTrXqNmvUZMGTEmMeeeu6l1ya88dY7733w0SeT5ixY8sVX3y1b8cOqNetYfvoFAUNNEIAnGhfhugVw7fg7QHD/IgAacMwMqM5UuKXALYP5rWMRVcIAAugisGam8K/A7lmK1vPLgbOIH/h00PXQ3NpQDgpwpi4HR4CiAWoWc71atwMHx0cBsjvA2z9rnCRr1U2U0IACzLFw3KuAF0XcABNfCtg6gamjHToA8ylLNWBPc/AA7LWuM/ojEcti2TGKOu9ysr7EikjqYiMnItHLadaW9sikiJ4RlJP3+5wE/kKFJxskeBxIIoAxKG7ObofOTXjMZ8bAX//8twEBzG0Dbgz3jsbkxU9YgHa7qYHYradML/iXH0Ctdvoy2AEsgw3ynEtoZmAYIL0eB1vgtnG7ABHyAFDJjYmIuDQI6UIDoj993ABxFwaJBc5mCAs4ZAciGHKhEKTQCmrYhUcERHTExLXIjPxgxmpso9ZFq7jsCqlTpJKkWFL/iERiEbGCWEmsJbYRO6WFtrdBACIQ+JAPxVAN7bAKx/CMwDieETXB/0V8u6ROloqVWpf6GSwkloxtJXaMIu8SZKAg/fETpCfu7w/uerZqr+YKLuc2APPqzT9mm6f39qzPzJ8Z9rQawJ53pqlT5VMFU/lTVoAAOLN1OJ34FhbYuzw9c2/obxny+ybdc98DDPkKFCqiQ/fm/U7So69HrwGDhgzzFyBQkCnTZszq0q1Pvz8c8QJQDgBwEAgAsQPAXSAE2jpumRC8jhF4cp468ohjJxSuW0ERydNUaXmeNm3HO8TQC4xQnMCYhTdYojqJFTtvcuThfZ68fMybn8/5C/j0DoRoX58YiVMmrsWpE5lxWgdjzQdOB1FzmMt8sYrrV/Hfhjuwbf97wGSixLFKNNCFItTOQN+gtAKmg3IJmLUUFBJYaSn9Mgh8b6nYnQRz1rF88dO/Wn1EIgIOQywM/Bli0QVHKFHhiEqVfqtV65hWbY7r0FluT1oIzrPBg0eA4YO3U4AAgA2A/AUGl3DDl8nBxOQ9cLRb/syaBXmb2NAGPP0G7G28Iz2CPN699rB7OHMQEsOFVoN/u9orOvA2BHyXbUM73rcHgdq/ibHfKSvvFdKG4PKgB5YRA+xlGzLDlRSwscGuUvw+LUsmAojsF0QvwRniXCYA5jpZ2xz3S1/IcmWMnkiZJWcAqkH62AD0/f7/AKXbff3HlPV+MacF03dfW0pf5nbe/2RzwcOhXNFJXsneQXK4lB/gYSbZnBKu1Y1eka6hYfVriW38H7u2oJ7tYTLQmLJtWBbtyl8gFvxNg+QjPbZMknk5TEmBlq2WgEpzVwQ2vPGfdvcV9GvnmQlTtK8wFwplEjjIQ9Zavhuixukwss1evDWeULLd1Ve12nSTXqQWXZO/WtFg+ekRGiS7vXXKEpYe/KZH5S+FaSMU2KFOTgXhvMi8XTL7oG60Q3uppIz8b9nbbc3OjIHahBUy/+lazZUeUjaiEkoVzsrQNDT8YczROmHkxiIMSn5xuBa+PdRVQOFAb07DDEqGs1BP4bTwjp7+RHXe0GEz4/29U4sJO1N4jYQTh6cjBOs02+GuUq5eH9u/4DEa0b7axjkmQ65Jb31kF1BSuISPfQzCWyz0IBv/3VN0NPD3a+z5xrGcZLqptNfzQ10pMvFH64BCP+9cRCSsKeWdFs5AQ8dh+YShjiV+ddaWLvxu7zYLRc7WvqpnvJj4TsaoaZ1MZplC7ceRSuGsKV31vq8tHjep92HRmCqBZ+bXu0xgsdH67vlk9S1D8LpmCDEej85joKB5s+tDR3duSMi+CFZjyEFzpzrmhGSVK1pZogEvD2K7q7r6OcbkZx2sHWjMJeNthtMg8cJ17eY3GSTDXPQWbT5yHykBlWxlakHnFPwb5/OPBwz0CjSTlCHbuw+hSqSY8bpqpPQY7TtuVVWRcG2OdCgZA6aEYg5IF0JdrZdukhxeCOoyYEuNUduCiQ3T+UvNcXTbyMPvIXbcqr3vitNMCXRsHxHXjISMDFyhoIHBCsS00sKlspf7XOWYgaFjlzwV9vR66L19JRZMkMNUaNCG/COlW7tGCrItNhKUCiKOIWAgoInTVOvN15hq4fHdz8fp/iblxfL5CY9ab9SYZbKypJspMtTwvolWHIehZTkCeADKlwrkhsxYGqsGH2glq/1RKALY24VPwcR8A8Irq+OIqASU4ppmrSRdlLFUXAv6D1IOI8S8g8Olub522VLprDdQaQh3WSzDFu0gS0N3jByIT7pO1iF0l7QwDXgpk6UB0O+78RHQNGr5b/uz/7qQl3KQ6YvCW7TAc1T8YYO+dtscmTlnPNHwMCgnwNazIq6BI4Q/MRVRrFD77GlR9xuRmxnkb4BPCLn86LYYEbC70KBAEAaCYo92lOXXJwSiwLZWRubaB+HzlrmYj1AzXUeGAhLtTOEzPW53bkuHF17kOQYyMrhEK6oXHQs8b4nusaVjNM3K3OvJZMkgvQvyWyYVUVqHHGhSWgUHDwOKfUubu7JgoPv3GGgnu90HGeQnuldqW4F2OxpLNgxLovOgZPRlsLr3lJ2WpqMzQN1EuTmfwYLJLpgAUcbtPBD2XAYUdgSjxN/+FlkUa17Y4QLaWmEU6J0Mx0PGzkOn/WUAkzU0a60V7bfY3mgoinKikYFC3HNqNmQqy7ztu8TwS4pOmBBaodVjrOmOVfLiRqgMjkvI42CNDRyhqFwJvsJZUI5ar7nHaJxR4+OSmtBoLWwhwlVahNNZ5yXv2yy+0CbC03httpWYn/jHcfW/ct3Rg/t2iZrOfH5S8t8ReX7TBr0o/YsmeeoHCD99+NtLEMbTr09u/vdv/9u7uPyuvew7e0BXxUE9xJ5Dxo85JxPC5PSRKnl5HGxq34t83o/5V81ZdO0gaiT16Y1DAuX1a3JYsRW24N3My1eLmavI57jvZX8Kc3KShO5ANmCZ1b37Kgt1Hls8KzpEKh/k9p0nFA+//ot8tlt+8m8A9OmXnXrax1oAy6x93JDpBHp1QZZBm4Bl3nvMqLUEGnTa+IsF1TVLFOepo57QLXXoO1m7fkjO/N2L46hg/kR9P/t9CFPbZ3d014EeRbCLBXZKeoqZF2/TPd2dDQ/YgGWyQTmJyd58gfk1Mf9n4Qn7Av3CKf/w5DDQuFBUPSQ1isr0qvJNVcnEPf37D8D10ZyxzD9Hgl2DTMJhV4K6q5m+kafF4wjz3o2E117isdMWZgS7s7CpmM3F9fqGKjbw8W2A3WzdRBYFX1VfK1yVawDP7cdzTfBVvviKHEN4zcasNyFlRS2AfHVcbcOWlDVs/64gEVTu/9cN2IIzE5kZA1Hqpu58O/Q2MLHXYs+GJMYkIDyg/SLMMznAIyjAcChylCk1t5C3fCblaTSbfjU2KTI1yMM2K9jB90msR8nZJtGJH/c5L2Q+SduMLEy5lZgYDFTM/ND8xnzf4wSgjjOffFbk//Nfjnv5MftpZtat67LqQ2CbH8iPMd8QwTLXBR0VBBsUWCJY5ga/rKmgr+lrOL1pUysulZAnviUulZUnAXGj3GO6RQeFDb8/3s2AQ7HdOd05QMx+McanLALoLR6T8+FlhU9a0x5Cy48cM0c3X0+HIHtDlkvI7bGjUV0zXaiqz5mKToKoavNok/+PE1JS3JxMXe1B++HxsCC09QLaN6R61C9kjksOLfGHQ3G+d30a27SAdg0Ncw2NqYCHCszoKrCR/b+k3F8VFLaPBTj4yHPmZEJT11eV9bXi6JaQnMOJTylxOaExbeAstCQlsy5VELk1cyX906oEyrufO+nunbDSwOT4SEPPt8Hcxg98L6YLRD7Dc0SciwQ7Wfb3AwOtQbHcJz9IjHP+mgmb9SbT6DfOJBZFpMfHWAReqOlaVVr7VZjYFpplGvXx8MWs8MROEO0fCE1rOoa53M9qO+vp6p5pFodlAQ5YEMMisxBxbVOLRDMLbS0z80RTcxArIt8uKap+YPy8x5kUgUooFP5n/MOzE6e9zGQOpbrvs5W90dcRKgExf/z2B6LCZNzZc4lD72jFIcUT9dWDSM8wqyfAxSPQIdBZ2AwWWCOYrq5M3VABFhlE5sm4yLDr/fbY+EeszgBnb78ss0QUiww28g+ituZSsz8ISHB/5mRc3JhPy13Ak7bWOUvTOn6ndrcWZXRHhKV3wH411z9qpYdi5lnkz9jwoxl31XfBezmjYOM/avYKjUdLM761N/WtdN9imQa4Otn6Wuhru+nt0i9wsok4mrnWMJD0zON8qdd3zJEwVx9PH3MgjBtxdDd0u+rjDPaVd9iLWotr+NU11HEAqSc4OGqQ2fCkv5PV9rSyVi+ai0VeAGMVWuXXZfwF8sjS4tziCD6RvFw9+2m2GX4f//AJTZif9BZY1ljreOnqSiHYyLiKDWjgIKbXsO/0A9mJnQO024Add1KlGIcaG/saG4dSjEdIP75zQL2Yt+Z+DQhpgoiwcj/ooYpzCJlBlSF3cKkUU+V9SqYyNMzuyxByFuL5HX43O8ioazCnB5MFbsoOSgqN3rRTCYggpIC9TPe/mA1FCZbHcsqvL/f1BRiP5xgLVdcNXvV1ulqauX63B3R1eqjPTGN2+5CdHFPYYMQujpL956SyZK01gso54f1t4ym8mc4wtBp3SGYO19y9v1z5pdvsuLu/83G6M2UNn8luGZw6nRifPIy8XkZ9S2vmvHzzXnJa8KFe82ITrecGmrEt5BTy2IuBACTM3dTI1Ua1bsknOm1H9IDUuZMRtb+14KPSpldv+D3rzzNs/7Cbyy6zzEnap09y1jXuGnjt81w49D4KzpLsQ64Kzgy1S6ndQJUdqRnpN8rRhV/V+kbFxlXW1RottDf9ku3mq2MKYniH8mHZvJEFkSg2Kl0+J9p09/S2dIhnkVmwUnmmap28zjxCqzlwn7flgAYQhAK+mGbRtrhzxjJT4uSztp27CRKFEVnqmZtaYl1PUs58EpMssM1hAvZzc+dguSmTb+gS+VKLisJPERHOlzvfR8LCIDJA5s2hlw8wG8bhxUJDW2+JyXW+wUs6kcx34a5ngm5Y+hNZZEi/29TbxzRwFhZvua1a2JYHzUKyFA+LtOeYqckfj0tz1VRJg/w1Zu/JwRcsTvTn6xKqy17ldr4T7Hixu7W1CxSNk0cfMRu6i8xt+bu8jbnw4dVjxQ3F3k29fbV6x4TF21107kFVwCjYoUAyD3MxPOza7fxSG15qfHx7UfjzSzJOJehJtuDv5s0l0f+9hasMGq+IfH4key41eyYtm3IwiF6afLTOX1Y/Erm3i3FxYzEtZ05ICVmj7tjSjA7g+6/v40kut7j8xvm28QHO49z/ldsnZtVdLcrnVlnABtHvg5lqhjM0wlARKkioky9BbXNvqelZ/q6T5Miek0DE0OaF9v/effvi/3cZSTC+AsupFdwpVxlhTpflrdVt7pnovaBwCTGcwq7gzzdKnDkbAerq33j6c+5k3+nl4xe6Brvk/G/cfs4tKporLMb1FA42LkbX3JGv7zy6iWJpnmhhTgFOERcNY9PoUK2hbmwSfYsg4r/PLeZiRWSYp5e884roiwLPDhoZKh80NCAp61O84ecKZDhpWbipHDY7MCF8xXy/q8f5U8cbWSdAI+TL6egbngqxgTqVBHlz5WyO09E5HocYLuryzgdBPml7hZQGryl9+aTUCnHLFoHeNhZBnlTLIDcLm0APXzPpTRnpxzKyz6Rltr1+LuJumGMMd+4xz8VdLK8IcNa1tjbjMoG5VQ1T00Qz02Azk0RTEzCao4fTWfTzI+dDWaHhoCb9mrQrxcCxBUYlxuwD950+Llp8g4EOv3v9JoYx9u9L7tK7ll8egYEezv5xgViGwXUyJlwDjp4ffMt4exPSrmxqLjussFdeMV7dDC/XN7b616PfNsRZMtk3WXm5K+6519LjqAddJ/bvMwYfteRuVz0xA/kvA/a1DN9ZxWobRd3f+R+wzIzX7D6xuhCOdY5uS0GHzJ/wZwHLAF3Zfa/0kti0pFRGGTF/lPE1xOyfm1mkhxoONZtj9M8eXAEjziZpQz/pnW7STN8wXSK/xDr/FBTnf883f32F+HBSAwa2bZJtOkAaILc/7EbGqCzKKRfvIPtQFlkimzzBErINdvaEgOqf5dC+8vojjEvu2tzdwmgx5w8Kl9JDSNGJnWd0iiAdjBmNyH+JncK9ChRzY8rSqgn5HKLNGvippXIJ9LL/vVe15PcdU2m8KVL0oXpE4AWRbzfbFvI2z8a7ilAJqo1zpNtv+TQeVYMJfvvKeJGji6+oO5Eu5PbBaZiYNGZkUzrsN9MuI1ba5XV0xpYjtRt/rUE1IXhKEXUKLdTNaoGB2JVze3NZOWf3xq0P17LaUELoM5P7XrBAjbREWWIvPXn05AT++jGitLb7//PXgqsslO4JipGGUuq1qKizGVcuoXThvfDM968LnnjPdi8PsS+ortrx26mV9XfkH0441fpbXw2IOPnK2MrS6ZH/XoMaohOqFgbyYqzM6aiLvqM0cLVvBdWXKfcq7sHOoZzFn705cZ9sTvnYWJ3wtbYM9KbZBXt5W8qR5GRX5RTWZeVUAe+/P95e/yRyCZJeJbk4NkJdRntF+8UN+Aj66Qt9hY9es1dNr69PbfUCKAgPL6E3L89kuL+i/xrr2hLoZOGf4TfwrypeKfY9Tng8pPhshZ+z+G3FW741Hhd9WfGyeJVfnu42lY+YMiFM/Z6cciXbi25+sOjE7XnuUHERbPWyFYd3jAvYX/NxZIRw7CK1A8UCzsgMJSe7nzpmXB6Dl1vMg86qCn+N/jS7p7wYfJs4fs+4NPnIZhF49ks+zUuci9L92sZZyyrsGo5onAmyy7/9cLtd0OwRTueGJ8brL8+dAuyGBwbsrXRQTIwRvMM9JMnOIZ9zySOSdAV5WgfSuZgiWvodmkA+FmCsoBI9QWUl/XtZZK433OHbX0RqlIYjwBXAJWtbeNudY3EaxUlxQOixd0HwrAiGjTkEkoQwwlIu6UQMJEvtoVdk+QiI1DgdpmC6Crg4roUHORZXUK7D4S6y23eqjGtprcjyYRCpURSOIMq74ZE8Y27OEHxCQTTl3tOeHYYSTV4s3DCPYJOHRp895MrxXyRSqo6RREopNkXLDB49gOPEP8DovPUfuIi01fYL9B6CSjJMmQIvigKwtCNTeFZPeJsiMDMRU5akIpOU78D8vfkT3QNeaR7B/I6Kvf1UFi4hbngkzzg133U8CUCDVLgxlariSmVL1BhbvBao4buooQKJtb/tS+QHwh4QwH+c84g8F0YGXItA95+jg8Vd4C9Jz2xiQYFkPMhYwSVdhnHUWAYfoqmaQ9KAZ1xa+ARppjA+qBLnsGKRiocwKj4dUSSH26BrIa2/t4PFl4UKBeOrZO9aQSYF5q6FWGR+Akk17gFJNQ7nH9Ez0uZMMF3SnMq1LZQE51hcToWFD5tzyL4GI1G6SKqRDi2q+ghAmLw6/SXxpEVhEcFGfWih/bEONpchDm1BuIrsR+fnEck1z+5344FHsgrTCJaszFNfmxZkCdK8paKXBS4jBxqDm3nEcqUiJab7NpfgQfVUfEF2gskquzuBSNQIDgOJGijgci3c6e/tYHFEwWqCSmkPCtUQ/hyEqJfEdMSymi4ptw41LqFwTiNEMPAUqZRSoJ8LRQfjFcCvMY1xCT7nEB7HpoJFGjBJduSW61QOPsbjbUtgGxehAUMe4tinxr4OEVAx1Ezzkkeyq2OhK9pVFzhIEr5MlFq2xgMocR9ekVQoJSrhVp5y2px907ySfkGDjWsd8by1Rl1E6zDEDaaLAah2LgD2Zp5C4CcezF+y8/vXbUx40Cij1jG7fDd6ZF6fz2vV+UOyfZG/Tsi3QUXDxr86sFYsQvYuv036Np8N6suh39fjHYVqq/6NQ3l4sNX5Yw+7yO5kJ4l+Dw6BYDn66rBiPzuFF0QlH7vI7mUmuQUrrw004spjA038riPA9He4Z72gXB9q+YA1ke1HPinp37JVCGylOSX4jT356PHbtSlis/M8aEwwXz7X27h7IArHToX5/clvl0bN1A0ryaZSObSyscEn+uPZ8+GK++IHMZodjpdOfmi6DIr3uAEejZHf5MiMRmJKWq0MujQ/ANjOtw7HKRAbmgBusc01JjejW6qNsFoOsl2sLfSahEr1ef3Ra9WSPyTZRVbD3kbK5NAUCUoOYFw0ydAGUA3Vxsdr0Tb/C7KMXh6aqNI11sEmWiDcbMS1AW7W5qpN8+If8hY/vPoOq7RgV2Zj4vR3F3ozYt58uHTrNBdzQCHiHFboCaJ0QjrHAWEs+NN+5/d5Wu1jJekiSHZ+d0casmSv3LaBBPrC0CIN2uOOAAdAnYDEu3XBTnSgwBbVGUOT/cDU0wFEOL9NEq2XoSPbv8E4OijM8/tL7AgEZX5lRixahkWvbe3Pai4MgKDkkzYO1RyvHbZrdaPP568f9OA1BnqEsG4+VUr1sfncbsj93NbrgcZkVrdUchzbTpNscuBw7TfaXJ06CAWgqnTh5yOx2EXInl1jFXWY7cPD1x4BUPaseypbL+mxQ/d/ToklAPj8L0FlAH4O+9/+y26Oc7kuOQ8sBRQAAmCcnHkBlp5wcVtGxwGBait7pwy+sogt0Bzg2yH+jSy7JreMXxaNLrjVyC4UhfUcpcazzTg2DMRjVjOc3BeoW9HXLOsH3+PXEjW4gVSncWBesJ3uQqOyqVRHxyoJrSYyOyqHmsa+nhNReCCqINuNUdJ3zQMZ/bfIel5EvYpWnlt/BiWNWspxYujC2jrpaQEvBgf2UxK36YM75Y6zRcCpkZS/W6Kg3su8pqLVZbTGtLsQXKt5rlZfMoVGumW5XlNFF15Ib6nsyvOc72XO9T5aKxJV14UV+WyMLpdpUfbERJ8ihRQeiCt1wou1vdNbF6fdLMpJ8WBxMsBiB8aNYW3k0c+S7i3ylmTJ7oqke4bI9ot0VadYC0g3d7QWRXGaZRVrTKZW10ud3IKF7Oacf+q1fJ0PlEp5fq4DfHV0DLAymO2ssZosuxFCKz1RVA6AlcN6ZomQh9DqEilZb0I+IHqCWtEU82gzxAoPQOq9Xq+w8kJUdmCukB9YNWRI8dLLGh8J5stgliBbkANPym1jFmcO8uEpkEPIjEbdtrWzeeKkURma+7I4wbyATEP+5ggh/5BVuAQgr/qlALn11ADILGQXqEfo2jOyFyWftZceXCZ6DnSPtz5kAVr1g/Lrcg8QAHtdI8oG1ny6rAIogCLBAQJgCfhDA4LhAnAdKCICBO4iCvgh3I/e9mMgYkFesokDW5XCD46GjjX+twt63JXkolSqJJVGIJp5mbyhm7VwGJhGG8jU2Xfb2TSpjtLUlSgB9iEmekfFqaw347K1PSY3OgzsQ1chZWm7QZDyNDUtnbHm1QrdZQ1MLKF3sMFhUeAVWhO7tkvBmcTNkxLH1I28PrmxWowm29OkHqKPhK72KA8IAA==)format("woff2");unicode-range:U100-24F, U259, U1-1EFF, U2020, U20A0-20AB, U20AD-20CF, U2113, U2C60-2C7F, UA720-A7FF}@font-face{font-family:Roboto;font-style:normal;font-weight:600;font-display:swap;src:local(Roboto Medium),local(Roboto-Medium),url(data:font/woff2;base64,d09GMgABAAAAACswAA4AAAAAVHwAACrYAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGmQbmh4cg3wGYACHbBEMCu9I2GwLg3IAATYCJAOHYAQgBYMAByAbO0YzA8HGAQBQZyajkRFsHAAUbVAUZYOyJfg/JGgLhuC1B62JCWNiDIqaKUtrEQhjIJnCeHmtlh3y9eOs6eV8V611d7UeMfbVnwlxCKJ/aszxY8wj28fCDwDEjtHQSGISRHbBf3VPz+0TnYmOzJCIGIRCBCiDP3jzAz+33l9BS6kI2qBNxQhHjsiclLhROUa1REuFA0ERRaoM9IBDLEoseorniVIA/0TV1FpmLrOQP6wj/IhsLIYeC6Bn2ABQ3Vyt6V3efeJsp+PrOpULAVWjgHWnSrNxwAsHITjSac0InGRm174HrACpKHF3ZQqg6YEF02iTTkqK9rGouf4KSlk+ggSBkkpws/7DtXLzO9lkIJPNA7F7ZYgLqICnZXnCVZkqc0BALZ3NUi8zR+n6ZSTC4xRCpbRLOEdpwmRQyDhKtZJBV1mqURKhuG8/zWY62RfAadn1H/EX7kiXhTnjkg1uN1vgf8R7wKpAOQBwCEIyqcgDtiRsX4Wrsvd/+32r8xax9Anpk6wkztY0787TmXnPv81fUw1liV5p4pI0beKQ9lA9QogeCiGRIyTRUAixEGnA49Z/7z3xjdP+r4QQggQRsa281TCLXRWGF1YHo5axVcF1IBlz6Me/UkDhBOAAKKyEKUNocIAIFQoRKxYiXjwEDQ0iWTJEqnSIOo1Q+nyHQKCARYAlIEBAhEKAhYhRhtltDz0zEDg1NNAHBM4MdL8BAuc5k/1AAAYwIoA49/RAP2AGAggYKCCx1rUQxTDAXiFSARUQUqHZdgswKxRn7JecuUzxp0AKJT710i9j6mTmZGFZZUfjakfKoRzKrak3iyI1cpFaSgW0mgqogOJvu7WemxdjcoXFk2VVz+qANBLwh0NowcBhw46XsAsuuuQyUWIkSZEmQw6eAiXKrtOmQ48hYyZMWbBiw5Y9RwUKFSvRp9+AQUOGjRg1ZtyESdNeesX4/GPGa0uWrVj1xVffrNuwieG7H/b8hJDLMAw8PgrOU3LEa0cgeMNMDFfbxNMOC4dAJGLk4sMa3yP+RgvadJkyv9viGutf2fzM/krpDIraplKUF/0JBgwaMmykWPjeG2+9896HYvE3S5atWPXVN+s2bBZbv9q2Y7dgjM++HyFsD6jXPoVihVNs+xeWXD7AZ5VC1inlndckjOt52nBM/+40ZpgxzGIf2y/59hHRT2zU0NIiOVUwOPBRwR0+WuaMF4ew+9bnnGL4IxyRbJJvzZrzr/nKo3C4sDUzj/Mg/kwL2nSZ10ekdD0owKVQFCtJi15awrIVq8UPXPb8vBtRmEJsiNdpXbTpMpdu0ZJlK1bvx9DBx4oEW9xDB0xMLxt7/L7W9K1Yt2GzEosrVDyKlCqvL269illz/jVfyYu4kBZtugPTAczLsGV+ttikW2lRLFm2YrX4OsA36zZsvhztXO6yusX2B2gcY43K75wd+zWKzk1ff4WIDa3mxDqW9z2Hc7zYudCJaIvzrDTf9ca+20R81nX/SrQMSE6/svyzT9mvMAmySAcHn3vAqB4MwL458TmO3y0HeAw/WLmqqsAt5+CbZQfNZ428r78kac9e8c1O//4HYz+/NSlJsf8USS283vO9UbU7htqmpmH8tt9OBwd0O7WhGm/pm7UGgC919zvO3m/17fojgVPVlTu7Z5Q97gajWekT5aEK1v2y718X7bh2CRx8/EVwPj2eAzvdd+W8er/XSQeur8SNJ6OXb+MI6i+4c8wTXhENB/VXWyHW+bSkVtsAcOFK5+GKcP97Ds3nuNr2i8CNqW2t/OjkmszewLHOtq4tP/XJb8zcEnEOt7RJe78yZp/W95n+TOXbevplPBW4TgcHpmchGMxq8IdDqWd5BsDf83gPtLbxcPv+kP2KdvHeLuHnkvMvOP9+6zyo9h5INByMrO5am5/0uf+gpvzBG00HS7LFjbM/bDxNdR3cnInhcx18POGqz1rO7Ei/urpL7c2PLHGpPVZXt3J/f6BR+OAeBMBGCC5C4Xk6mrBLMK6SwkKOsrexBqJjtKHj9MwnGDNzkgWdZWM+x44DISnmCwqUuqj8fUX0WSXqi03mGH6yhSIzAx7FoygIgiEI5Twdc4G4XSTEJcISIVaixEaMjpAkhBTxkSMO8sQKTwIUiJ8icVEidsp0iAbhaFKGMCFWH17Rgxkw2gSGKYRmRkwsiJcNsbAjHvbEwoE4pUimV0frqCKk4CukdVQpJKicBPWZEf1GoIy+Hbcxk7CmzUDMmsfq9WVmt+ADnEXzYUtWMfuyOcZXm9AvmMTiR+3RfSI3clt8uIWO4CE+whKHhXIFQhGhoyQJRZpQZKs5d+jBiix4GHnxELIhF9+e1obQFYnsDdGIsdDMIYQlsbMmLJudmXeIzBaHeAgljVDSN0fku+3IVpEb+ZAb+Sw+T5BHQJxE7RNRyIJswDRnFbJJRIcYvmUXFpkV91tS1Ur9dVzOavVw3IVp52M0TrPZ1cRMBfcwUSlU3N9Qr0jvABULOsRQP3iYia60ScAKIXEmdoHW0Z+s+gigbs902gX+vnD1P+3pABLneXs6g6JC1fsbnOfv6gMWF1B8A8Gul+dDA0w0azYoHbNihGsW8EHviYsQLYFo6F5BgMAckUizPAeGJwkCqIFyaoCmQ18dFP3o1MexkYzUS6PUXXUGYUYeL5xAy3AeCA8NOlQDVRo8q4t4yruiKGLVplWLu+jadbink0QFgc99X5hbyB7o9lCPRx5DQfA5AbS6L77Dgv5AlwSxum7X3uORAFpku9a5PtgX1vgQ9jzQkAgEMMYZ/AVnTMUJpyGPAmqnTne6LkcDyFVWjOr/N1YQT8LEncBeCLCYgUVFqdSiVZ9RL6zZctCJh2k+ztHEaiHfroEyNbOsfb3VqW71W4/qDZ5nTqd5OzgYHwAA4HBapSqt2vQbM+2T7RDvThy+qG29VPxT96fj2LvqRAZxZwAcHBT49TSb+LnC42PXnTIXk+sQxy1GixIuxJDYE/nw88P/+1ykWRvERxDwnOpK0k/fd/LLY/V58T4+9HHizZrzr3mvUSVY8MZb77z3AU3iezqSDyRJtmXbjl0MKb5jBwCo3ZQVURAQmaOFdloUzHilXlVauI63eqJNH3/9eUTnUcQayy/sPI0+L/NcxL/mxXntnVnvfUC16KOFvAaaT3krlreRaMeeL376T5Jf+U9v/ouU8YPS94hsGSC1jBxSOjRAWkQ9V8/dRL1BgIyI+jxAZkR9GSArnp4EyI6npwFy4ulZgLzy6cXqJYB8cHqb9pYCL94UOzkpc3NT7sevH+4eKvR6VQaDOxtPAlTHM6cANfHsPEBt/DsCNMWDPw9CMwQKM9Q40ANbPQTk950EjpfD4Y8y+sf+rTHYOQDoo5r1I7DQkJsoDdVDj6pKx6mT0eY5jKPWikCdULeQvkul1Dlroh5qxghzjuRxawBpq1H0TWSpVnHQ5Euo/Q5i69vQO7laiocNtLf+N0gG7wOSJdDmDJD3s7CnKSnMofgykoWAQTVMbzPMj3GYJBqXABCm+EhinSPOCwktCLSpyVSUcVwzbsYSkiBFiAFggVuZyzwex6a5AKVHFOZLy1iEbox1lLU0CVP3T5MsLc6mCYBDRZxRnaFGdRyBSGByDUaYc0g6TW0tk4gp7m1znFcgGY+xZZiUBYlIPI6IhBoCrKYIkzCxBZswBQCTQWmNYWaUwyjGUWm1qPUymc7XF+PeHBfdCRU6814rlFKZ9Cnmh6f7jh6FI8JUl3OflwFdJ23xiMRb69roPUGulgrkjSqBUm+QeUwDhH1D/qVcu3gvnsKd/kqqDO3KRSyU4eSvjDslIPJgO07syHvSGr4Mwi/AHK4i/wGrXHO/3K356AnVTYoSk6f/2Ykt98Gdr5cS26S+Oc7WtOViWqbzxq1qiH+Le4qX+YKVvWR3yP19uzMww74TDSqgWlyq+cKt3Otw3EjaxfxYB3DgmN4tCctxoqoM8aQinQdOemCCr9MkPcJ7/ILvaN3rldtj0KfR9R70hJ9D4Ix5yFoe4MXzJGOggGo9Fc50vHm3fVvQ/pufoUw5EcdLVWTTDwWoEfMWWr+4bYh/d3pciYFSaz8xyUi6WExl3wCzLCbA4mzdcaWJloqcts9NAMfrHUIe+PCIJUj3u/gMChSQc7GA2u7T55hDhpPc+POvuqoSF8xm9aMJ+ZbXJKpeCK1IVvWxsOMmjPu5tFXV/Lxp8g1nwQf2f3G4rB1dqre2gsWFrLE9koz+fSbp9pVwrr7XWyy6XvhTzAMdngTtHTs2a6qWFx16foUbS23s5gMR0vECQJmuU7H+BJjbSzfI1Tm5LZjF1o1oGTZzgd7JjuMzuNgjKHfgULlsFLPU4yh7TqYQ7vjctvhGUhN0iM4m+ttc7avZP0suOqfwLqNiMypfqFiPzc832NbSjV/1bHHBGl+kJGUPF5/Re8BttkdS5lTulsaavX92ZPjaFk8VbSVmBur7tfB2s67l0n47+0bR6IfOap1Q3Ih1yi0f17gJ7/+smd6JzqjQIqzLx2LO9y0LPZ2p+VQt5jrdZimLyH/V+7eFYbQujlRGOYMSLZyT6FwhG0mfFjyj/FwsRkf6tn+ULWDeBFw/dWxQInzOwpfDYF/K3KImzFAPLGhPDELML1Sfm7xbsQ3x965vKSS3fT0qMOYvJNMvib0+h5/GZ3kImfMV0F0+z7eNFPmdNfaN18hxHSXR/P40CcBAHa7phNfTH7iETITiO8mqezaIyBu6rQ4Za0J+NhbTddBJHeJvZYmN4WM2ySKpkz6YqSKj1gQSEuBK3MrapJi7iUn9lYV4V9Np++CtR2DM1WBECaEUZWo1y2bMKXTG/D0VU4ZD43vf4AKmA+YREsnm9S4s4IqiX79c70O3PW06Oa7tzSjGDL8a8RpHep5NmJnburc1PtCmn45Kaumwv3iyTc/7quGrHU+SQtfUsfFTF0v15uYQfRudGIMEatPcEJYBlnjoIjR4AbcR0wOdp6CrCu3ARVe1yIAoEFu4zbl6R3VPzSfS4dyCD6sYqB7dvqFXtbAl9Mztkf87OkI+9X4WFGE1WFxGxiUtM9ggccSUGpjEtG5R6rL2VQlqaVNBpQwKsOnMThy/0oM1bgJ5thLA3E6QQ7jW+PO2lccviPi8pTvC2lcHUMktIPb+DSYoPEAUOUjAqYcuN6manz/ON85Wl4oxiR9DlJW2aIYD4U6QfBp7QE9L3a1fUKiu3HEYL9wrPw8oJLTosNRS70M/FKfl7IP+VZytViQ2B0HAIYx+fZt++Up18XmK9ZdMFX+SRosc8Clkpw7Pef/oCc31AUm01EykpxGrDMh6MWJGV6y/bFv18GD/gRvcado5SSk+xn2WqPef6lgJdDVLGzGmCWzI3eC5nKT5D4/iQFFGAzk3VH2FN/6gd36iQm+NJkfJ4cQeHJqjA1yALEJEmyPsEFcOshJlnC/8zFOmJksC7g/lNEQ2sJrOczRhF4bo2NqeWW8d+VcZkYUCCwHkGK0n2DHaDFdYZ4BE+gHFNfj78fZV4BGKrOJCB8rEBygwszb38W4udzG+/4Gv6hOYQTjPYmxlhsJdAC3VVtY5kgCUj0pc/E0rPvM0/WtU0cevTCOA1rSdE0ycFzLs5HBe8rnmjcmb2PY79/pSGD9AMoClFNpMdUe3AeQM4XMuTtu8LgMeNXlrf29v//EeybVALHxUDFzciuBoPrixqMmBBt/pP22ucIsBHSk97CEc0GiYOLGiUKmgPd0Ou5Gs+oPdkdh3AxoUaOnTMCDPFyOO6BkFVJNLnlf6Fx+gKWFUMIzmwMl2hTB6G8jMxzlh3mRazE5TkGaEkJkIP6JEH9LD8qzI/avCvDjP8K//QL/toRGwBDwuMAt1Kr0K57324980Hsc3XOe5D4Ao0560/1V+w0BAKKcTPmCcvBppUOa/fPQcZxA9mxwJVGLE6Wob9Su5168C2MbJlBYK8ydpgDlEg+cwrf+9VR/l78jfQflZf0+whdo+1JOw464+QF8j0B3H+NlpapsCkYGy49w3fUGGAUsfRrvbAZaJO04jaIsteKUKjdgn/Y+JhsiWlwgZDVwUJXltnRgSlZclalMPBtzSwWfjUtnbriYd6sLzokXdoga6r0/lWHk5RgW3fkU9YUvr5HTFpi/5rvx3VF49RXZR/PG5CJLiEeY3/H5baQh/jvhRNzHbwrtQ+baVv/f78G5UQJaz0w1PZ0uS18FmtqGrh3SCTePzl908QxWDI4dntkYZUVHJ5MAgX8BJFVIbG1LSGhqSteIb6lMS22rSGrEaembXlXXNtLU19MyjdCyIZ86J0VprE5NaatOJcXUN/E11yY0YdQMLZYKWia6upr6pkrKeqc4fRgTxKjkwggjmSe6+DVW3zEiq+ZrNL0bpFZM9Kba2hV66LkR+XcXg3H84Iptn9wWMnUrIdNTq0tlwkrFS0MdgG4p7bCSS22TmEeWn+3nOQt7cSJVo6ghcXfHHaBWYhxBt+Cm8fEszQUU0PY3Udp9JAMxFmxaKwkVpCSrbWmGlYHT9fWjpTxMaQZSWf6vtrcTRIwXj6cnfRBOjlbYUomlML4Bv6KmYpUzDNTKn5T1O+4uB0s3SlmMsWd73TrUfyXaHyytnIt5HtK+3R72Pyl+HcmKcvrzNZRpo9L6BNvD2mgFTZo5hDuA9UNfWw8tr66ir6GqTtfWgP6Lv3Ny1PlA9647/qry9vimDEtvHf5Xf2foqjhLb5X3m/yQs2L8T1afz9OZYGMW/GzUMB7vZbqHPZKdPAeGf+lUuL7JVqaEXOdo1KDYzkpEQMrNIyZ35PvPYN2wztcA1ogb4XYl5/hB308fLJzDAJTLAlhEQ9GDGg7rw38KQS0SLy5XgixF3w4bZP715xfahh+EUE+x7MzY48FZiOC0T8BJMXUNd6gx1Ci73gtr87wvoxkinU9D60uAd5yv1C7Zu8tHTayxL7w+aD+O4ZdaH3zi8wwPcAiIqUhKSi0NVbc+agMzhBxMnp1CM10fGyGH3B94f+vc9hmdmgVMFaxpNbnf4dt+Ian1SR+dO4pP5yCrm6FpQc78k5H7pQMhs7AztAEFI7IZvnYPoTamwB+R+9OL8a8xSHyX8ob+4vahP3Y2erddPn2295pA09omhzkTF+96IiH4bFn5y39E3rOcG2d3Nh9LjQwHxZefQmOr+FfY3Wbe+zMamv0WIvOoprmVOqBMT5+ZQH8ffcGpHm57VdqyKH3oV0UJNjqwH6SD9DwwpY8qAlDGZAcoMdodJh0CHCQf4Eu69vCWDkAdkEPIW2DcnqJQNPi6IIDrYudhYeDmZMhD6+rOhicWpB9UFZWEhKjpIJkNGQVvrOqGakeY3ks8rHNuvd6efdCkWSejSdHVybm9xjx+6E4UdzPAzi7lkoSIpwTOS4BG3gG3lClBOJAyLDLUHMq+zbafYnex6YXb/wo5w0thzMe+/N+ZspuxfsQqzcb/rjNnZ++Wx0F58/SWZPVE7zVjG0eGEydV1HlH/1GlQFDeVopgwnXWnr5yl0JldT9a9soU8wNIZP5GRYbcbFZ5XKKdi6BHPcG9+HM4aOxacQPFe+YS59vC/ICrJxSfMMW4PDNoXDn++gvnVxlyTKYoaT3qeLc/VeG9HGHt0c5+7NCclo5a+jXyOXW/4WZ2fn8BbouaBbBu43Fyt8FT7RY28WOMIq9uqQO3YzH/IZ+ONZ7+GQTkwxaL/0WQ3YOntTztzLECpw1vH+w9g6WVPK9t1wBB6DTyOenc8OFZbKIV6FqijECjIOLF1fOm/SzimJvp31HrA+iimfdDY8sTV/otgHAP7iamJYtr9cUh12IeKfRtWXZEEcdF/XmJ+zK3+/PhsnxxI9vUIoQWDNLmmbZTpAppTWxr/SBynnhu6chWyJvInc54eCE6M0AXU7QSk7Ij8DrzIHyuw6tIlMOPMH7MooTMYB8Cfi3ksLIdruNjJwfEbjJc75nJVeVrutfO13L4O0yaztzWcN+rG05RPgJl9zO59pKGmG5CvZts92LqGzoM9yDp+QsxLc+37yT8v5/g3/lydFEtMjwoJykyLRGTBOKfjRZwIxerXYHWu9PenmnT+YXsnXf0PZu/Bn4bqNb+Yr3ez1DcWNIkJiNOPGuxzL83lpA1HSmk6cBxS+o2JyYwJoFCj4xE2kHsZ7ETzdPT2JIxGTNBPrnws3PBPfB61H5gRkxCR5O1olEsydXsW41gXcP/I3FY5MznnWfKfiOrEIiqVBLqY1dHV36uDT+NBd5b+7PNFzp//C7NuPN1/npNblCUkNQp5ikeFbh05PUL/cbqWfhiMqrw4MVq/D2Ppu9xmF7g7LzBs/3L7zSmkye2mOQMXc++Icwg9eDnw7oLcYfnD5wffdb8UAr8/Bu38J+ML+f/yn8wtPAaxE6yTijWifIT1p6cq4czIdC3gLGt7C4HpVSEgGvTt87hr2wCjGcXA9Kq4tx5wlvUgaFbX/vT55bnmS3PPQD6mL78vH07nvZzkEDkM6L9sGrdCGqqfPUxuhe6tfKKZvZuTqbcJgWFNKZ60jHyy9ATV8jnnogU3qk0rSuP/p/GJifYWmnYmoHaWZ3/x0sgT7yQ28lvAAUPEK7xjdEmYeRdHD78bVH49+QkhrSIwvBFpBrlW22BvtP5HtBulbcKdssIijD72k+ni6sDuIvb+R7RdULBdUHQTtF6gR7WAgdD/n0SGWryDLzMAB2/Z/H3i7z/5Kr67UxvVTclXpz5Xjc0Piu4BK95PJ8/unrwT8XcpPfXd9jGUyxBrQmlJcL0XLS6C4DRPYlWrcAtL5Yp4wcMUejMCXtFqO2tdO8eGOglEbmxvsHTbPae3nW9pYCy0v351+KF3DKvPm2OzzD+Wgpdd8IaB2f7UmtDUuGhtL/LdJ9vXdn5UU3uCcjUj36qH5YZQe+HI0HBQ8n0bTMoQoyfAyc4hhxiLZQAOGBDNwDMQfjlNbSpRW06WqEXV1IKjNfjiupq2CrXpfiuxUFR8Nd+LlW9e3PBzFnPwIF2Hy0ZC2YOPg45B9JP/OsERPjwu4CZ19LVhLaV27l7bCNI/xuj3tHb0MvWy4iPCR8Y45smTHMUgLgYeDq/icRHBWUMm2LgpRq+nlYt7LpGKYuDB4HxF5N+VpLw3XMdYPzNXhv1eTb79kUfs7y5zffLjvaS+hzVpfaHBqY9BDcXze+VyfweJjDAAu7DI4kuK7R5dPI/bwd6J6Q3JJ4QtqkTeJsf1gIHw30+XRru8ozhI84BdYOx7+zcNfhHZ/VEX95icpRG2rkHJIFMfwhVJu5+S9fLRqwz8Z2yIZVqp1AlYEFYhqf2UNLnQZVmf9u3R/cHNviKGpqedhZGbtrKcvdIJ5TsWBqGWOTudwwkvHG/VO69jzIPtXJ1ctUBgVoWpr7PPThl3/XLjY5Mj+vzS7h2dHUwg1k8iRY7QO58N9TJ6nje3K0WxMPAf4Zckpynu91Ey/p+jNfh9UBM3bP66wUPGj39aW1kb56HiN9qW3y0/AJh95sqpw2cuxP7vzbmbnOZC2nzssGf75h1aYPW9C9eG9M7jV3Z2qgIGZ+2ODkvjIHqAMOhXITQnOGxYDE6eTi44x9mlgY6B5RnUvc53d8VFU1tksb/ml1psWmxXZrG/WmTBjKMecE/qQWDhrhz67/SnLvMu868v0H/vyommdkm0rz4cXxrsGFwaedSx2ikB+kM89N73GToRskVJqqfPq+l5mg00dy3k6VDxWfGqZy6omfqbQWP+S4SKQrgosnJEHRVVoo6cLFFHVYWoAwat1sGeWMNVdOLw02zBr+Nf75+ZOvn2+OzUqftnlsYXywS7/0WXrOIcKWTbtUOyQKRHtcC+Oe5u4nWzbpg4NmniddnPFtddNVbCtDP568vtT6+7fzh6eTlYe8R6Me/IjoHa9I3yqKnJssiY+LRMWpJryci4T01mTlpmelZxYP9gMTkrNz0r9zapfHLAuSQlLT1LLi48xPdZL/ltL8Xn2abP1GPy+16yzxSc5TXEOnGYTXwtFE0RLRC9tn9NqkAqRarwP4sJbkjkEPwoOP5x7yNoDYx1N1nRXB+2cox83Ts1/2IH/EqCk0LTHAz1E2x6ZB5J9PraFfl2N1gTxNUS3Zp4pta22RafMkhZ0SHhKQ4mZsEWcZ4DAdaFEr5GfLqCNy5EGLuY54l0SH0IFKEYJzoBdtZCQlUtSE3NTU0tSFVtXOzuPQMay2aFbVWExzOdN9uezJRXt9TklzrZmljZeSQGUYI8E2ytrY1caql1+ce5m6oruKpLTpkVn+KqI/A0Fpwy5qqf7LHWGY3RHtWNvOO9mTg+OKA19sgaHPu51PSMI4Liwrz96kNaPSSjySpaEmItHF5tMUnJRVlURedjRo6xF03PNSSTdMFcQdwKFM8EjWPCm9XAYp77uo25prT5LbBgY1xgcqyomRZJCVYmHJf0q8ap3eYqGajUU1HVL0vI1LBTktHUxNtBOkjVst8tvwv8lGFSUdnbgmIf3+Lit6VlpERpdU1pKXVdElHXS4sIl4biLccsqfNU9zH30Hleod/Gw8bQXuwWeRwT4KF22UwKr6Ii00NuKEzMLIm+dcPb3dkiNeAWcA37GlOdz5kpixHOn+txrQmPy7wT4h1Mk8i7mEGiR2YmNkXd7DmvLqV4ffSyewLYAa8MHOYTGQJ+dXwEkmYVS2rAh/Aw8B9w5rak3FA9mcFdwC4AldlojfnP3CzL37UzgKVD+gReW5+mo49X0NcmqOhqgxKqNl8gx7uZUoJLUtUUuXxN86wh5lQKIMUP8eOiOX5BvMBEdq58MijTN1CnVfFosKy8d6i6DeNi52tkYHMDnIyc2Q0YGtj5eAAlDwmXytIy8PC2diI5ONtZW3m7ToQvdfPF4POl8e7a4ju2dvpElkC+PRk5TR1qfWJ6B3WAEgBxnKZ7D0zPSknTF0doXAVCI8d5JwqMxT1DBRJB74aCioJKbj7+ar5CEwKeACZnZ1l3srMmXscmvzFrY3DQU202X423rWMkw80ioz5nt7QfFBX6dV9oRp9yxVuYJc66DvaEqe7KzZO5QvrS3nOkz1V+Cp1/5zv3xvgY9yZj9yoDsPTdhbW9yKzY96nxoQiE7RrPrW48fzTX06gs4KwecJhw4SrBtD3XKEm3eb571KjpvJXY4FSUkIyztbmauIZNa6JRopFV5Li+sa2TUTd6Dwyc+P6kVhL0Zk1p9LG7peUbzV/6iLYOHla2gVaqOzw5+90jH/yocbQxZGYD9S35AXNKQRktmSQ/oFWrITt9XSamG5+In3w57IkEO2iq2BlIdHxyjUo+FDV88qZPKEgJH/3DnG1SF4lbzU5LWYqMXErKD1h7FkHNCbARa4gMnaLp+zTfjDSuuqQzuCFOp90L1vfJWEMOClmDqJT6/Ki0ej//pLFWUAvU/D7zfa9dXVs3UFtHXU3FR5kupvo+HqZaKB8vr60tHG38PBTJ8oxmRrb7i6FCQg8ts7c+Qczx7Pf5s/xd6dLrzpedPso/l8Rclydvd8GqUjI8qQ/UAnl+LosMdtRnZ6pkXPIOoo8sC+N2MXVxD4PItcQx8AwInAbXOt7QeuacXHakEd+Sxc2a3xAeoHa/Eub+uEX/Iy2x466e7HI3OWyr2p7DI5mB9mmwOf8fp8jirwQ6wfS+vQ/MbaDWk5pYU7LKKNYZlzTFDco0lZ8SQn6mtia07q1wXEXtMDclPAwNTujur6P1hlASHypz9ZgHJ/GGdB3zDwgFKalvbEP5JXklAxycvJlw2OOyfXRYU0Swk7N67KaoMK4XoioEEVHCdTERZVUXnAtIU774RWU7XYjxUmgWOK8lksdkv8Z8R/lKa6nzVqLAXScQrmdDRWUWzqaAo8c0sH6HLWodh8ouP1dPr2louRSmbxaFys1dSAA79xdAV4rCMZJQpRMLPXBD5qaY608CveuW6ej8qYX7ru5vgTke7RgS4uYZHOIi7BAU5OkaEojYgQL/Xkb9qQTeUwn1cOhe4x381OxlQqPPfy3XIHPnkBu29U5zoZ/DacGp4OMxIHyKCWlJb4lRZ6+V/e8VgT8k6knBp+5n3AuqmpmrXdPdYtxAOYElnKwcfovsfzw5JoIakXDcf/NxyiPKo3THNFDe9QU92jvBvCEdiPLuGrBNMso5Y3BMaDxZNNDf1clZfbZzluYZ446S+dfdOXSGu22nnpQx8OE7w/25weEwNVHAfmTMUseTua8UHk8UvVXBoJFpzyUMzmGA41isDjC8IajQTBQYL05v7tALwvYO/rWs9nlbmk5M3e38rIoUJGqKJ/eKfLSUo52CDUHEVm/CZrJpUuceRgq4XH41rY5R3kGB6rvARqWKssasnPLG8sKystbsrIqW8h62Rkogqry7MdgfXQRsL/vBXwoO8o4FjtWPqj2YQ3z+6yUv9OzOgnOUZP76bTPe3AVifGpCWMidwrTYlnjE5SHGOJASKaeqpqpGhHPnzhZlJzmwO2uq36DQ0sOC6wtpuU11JeKvtj69z2+4pnJuTSg7WlpNQ4YMrc0mzcMmUZ23Q+A2gRwgE68YQ6oMzUirDrkRE68YIEP2LqbExhVTBE9J79oQREkHw93bTRbDFhdEnQ7BmrSKnE0ULTkypLGamlpVi9xW+PfN/Afgzc0MMXZpNjinoaVCkJIk6MvLKqhBR9qjpkdhfxgcQDn142D11Mxhsr+tSdr1Vu1tG7Aenv8CTHjG72bsAlnD2mIcrqUi7oqfFleECdNduSBVRtTG62sdr7NQdFUIYvaJniNh22k7ErOms0RyW5qAJxoMelMG1l1itNdU+y29twiMVTcFjzMIA7FgcUFYM92Fi+YUhq2Tpxnr44lnMC2kgdQGHeSD0Ab3MTprZx3sHuvELsLhDolb2Hr1OBcyUpYSg2UzDI3OMhKyJ5NsRozMsAoSXjBCY2L8xkvr1X0OJcpSIrFshqQcknt4raz6KChLIV02Y8jmkIaLkpgXN5XkmohnkviaSeICk8S3dklPYJV8GnIzeaQz+Z3biyDBqVVamoMaI9W1Al5q9UjO2Gn/HwVZB3YHkS7tNDItk1LEbaMS06YqwLQVwYyM2GnnH27SeZhaoH7DLy9lzeiWW84tHGlAymTGrMOV6Wi8z0zR3G6qjrB471RH5ZDEKuskvMus0cBuvdOB4iZdruzDeATtnaRdQsrNBnENhW7+5oHlEJhttQb0f8b1NK4fP6KsjInMu7skbbaU0hJaI3soMluDfkNvZmS82dt43vl//ZHy9K3CJi7ucvXpD+VTp3NrJoBQgHppBnD7+NEvBsC2WZJPOp1upmdT77ahkFoZijZAXz/sPWR8LvG41szMrC6DRUyWPCyf4QF9l0nmYrLsKkMNXh6PuWJ968PyQO03KV1MeWOWMQ7isbKyGuRVuMZU8vKYc/Q586ZBkwf68tNVHmO9n5xkYLJxgiiRzurtDyYbwmAC9bHN5mzt9wywI76+p54yeA4M54rtRxkwHevFNoM9Qn1PGJ0g7uy/ZII8G9d4YJzR/x/Nd7+4jwgF7V99jyGnU5oBdiQeK+xPBTvbz/6wI2EM9vm+bS5C2DZM2n6w6dPb9yRgbF2AnP79A8i1l9rsky52x8UV/858bAEAvPY792aAj8Lk5/+F/j/LYrfALWBBKIAA03lzdoAFzw055yqngbhN9K+Wxk1TzNu757oys5B7LXOq2Afti1z7CRZhwyQnxeQrF1sywaJQj0HHsxIxFSW2y1B9wlugAYzBEKQgFiigAV4f2j5PiYtPRIrb9QqTXZfFtFWK00W23RRPZ5fX5Xl0vLyiXcLdLg5E6nfvkqqq66Zrnc23mdVPSES0FUHrkye05VR/i56hU2/imcqK7NfwTKzEOpdLtYip3mOU4qjNCp9kwt7OJd1KofMu4mXGpmJFlBjDorZvr8s051yrZCZ1U2iyiUxkdNU1NJu02V6PcUtoZfB+W2p9TksmZ+hmI+a6RHD74UjfdaZwTrWP2C+zSd4llyhtv0awG6PkSbSK5FZdHHoZ+yJgi/9Jqv/YtAT2M3WWLVt6W2ZZvqLtp71R0lhFlw7H3KPYvp1slgioR3LaBo1UlZFcfH0d0+oFZ/yz9n2a9ewKo5klz7ZKdUoYe6VOv1N516S8+iRpNkCr6nlWNrVKYlgF4lJA5tTJbcKzbWvyskNImxdWfCCe8E/2m/UNlc3ExEY/13hMMp6TaHx8miBCYyaRU5J6nZQM5JPezFqpa7lHutI4dBa+Fa+8GvDXY9emc6ULYIFt2a0qYgBdkAVpMAR9JeMrTZ0+wk+dn+7ibjV+nlzalp9g5PwSwmkY4yWsxVSp7Tu9fiknKzJydmc+jA0psTPrQ2EKjE6nTNd4UNt8Yv1SLlAwPy8O/7zyzzH/G0T+OB3/JdMRBrDm/VgMJNQWcM+XLcADGlILAieAHyMRAhxGonAK2Rnt74xhJNZ5NIGDtWTKycwoNf4ChArkzZMXstMkiBFvik8j8g9DPtzH1ubHlYhQccCn1qRb3jKFu9buAgVV4dahTfhzaYE85zR9d7U3Ct8e4WlYPpz7wMIIdIu39JsERNZLkqUw166yqdNe47IxWQA80QaDtQhnAa1wDZndifAXyFP4FOHqSL+ZxS3upkebGg0GTJW8NkWN2GvGtcYfNzcAAA==)format("woff2");unicode-range:U0-0FF, U131, U152-153, U2BB-2BC, U2C6, U2DA, U2DC, U2000-206F, U2074, U20AC, U2122, U2191, U2193, U2212, U2215, UFEFF, UFFFD}:root{--primary-color:#5546d6;--primary-color-rgb:85, 70, 214;--light-primary-color:#5546d614;--light-filled-primary-color:#f1f0fc;--dark-primary-color:#3c2cc7;--primary-button-color:#5546d6;--surface-color:#fff;--surface-color-rgb:255, 255, 255;--danger-color:#df3f40;--danger-color-rgb:223, 63, 64;--light-danger-color:#df3f4014;--danger-button-color:#df3f40;--primary-text-color:#000;--primary-text-color-rgb:0, 0, 0;--secondary-text-color:#707579;--secondary-text-color-rgb:112, 117, 121;--light-secondary-text-color:#70757914;--light-filled-secondary-text-color:#f4f4f4;--secondary-button-color:#707579;--background-color:#f4f4f5;--background-color-rgb:244, 244, 245;--body-background-color:#f4f4f5;--body-background-color-rgb:244, 244, 245;--border-color:#dfe1e5;--secondary-color:#c4c9cc;--link-color:#5546d6;--message-highlighting-color:#5546d652;--message-highlighting-color-rgb:85, 70, 214;--message-highlighting-alpha:.32;--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light}.night{--primary-color:#8f84ff;--primary-color-rgb:143, 132, 255;--light-primary-color:#8f84ff14;--light-filled-primary-color:#2a2933;--dark-primary-color:#6a5bff;--primary-button-color:#8f84ff;--surface-color:#212121;--surface-color-rgb:33, 33, 33;--danger-color:#ff595a;--danger-color-rgb:255, 89, 90;--light-danger-color:#ff595a14;--danger-button-color:#ff595a;--primary-text-color:#fff;--primary-text-color-rgb:255, 255, 255;--secondary-text-color:#aaa;--secondary-text-color-rgb:170, 170, 170;--light-secondary-text-color:#aaaaaa14;--light-filled-secondary-text-color:#2c2c2c;--secondary-button-color:#aaa;--background-color:#181818;--background-color-rgb:24, 24, 24;--body-background-color:#181818;--body-background-color-rgb:24, 24, 24;--border-color:#0f0f0f;--secondary-color:#707579;--link-color:#8f84ff;--message-highlighting-color:#8f84ff52;--message-highlighting-color-rgb:143, 132, 255;--message-highlighting-alpha:.32;--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark}:root{--vh:1vh;--100vh:calc(var(--vh) * 100);--z-below:-1;--hover-alpha:.08;--reflect:1;--transition-standard-easing:cubic-bezier(.4, 0, .2, 1);--transition-standard-in-time:.3s;--transition-standard-out-time:.25s;--transform-origin-inline-start:left;--transform-origin-inline-end:right;--transform-origin-left-center:left center;--transform-origin-right-center:right center}:root.is-rtl{--reflect:-1;--transform-origin-inline-start:right;--transform-origin-inline-end:left;--transform-origin-left-center:right center;--transform-origin-right-center:left center}:root{--input-transition:.2s ease-out;--input-placeholder-transition:.15s ease-out;--line-height-24:30px;--line-height-20:26px;--line-height-18:24px;--line-height-16:21px;--line-height-15:20px;--line-height-14:18px;--line-height-13:17px;--line-height-12:16px;--line-height:1.3125;--font-size-24:24px;--font-size-20:20px;--font-size-18:18px;--font-size-16:16px;--font-size-15:15px;--font-size-14:14px;--font-size-13:13px;--font-size-12:12px;--font-size:var(--font-size-16);--disabled-opacity:.3;--menu-box-shadow:0px 0px 10px var(--menu-box-shadow-color);--font-regular:"Roboto", -apple-system, apple color emoji, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;--font-monospace:"Roboto Mono", ui-monospace, Menlo, Consolas, monospace;--font-weight-bold:500;--font-weight-normal:400;--font-weight:var(--font-weight-normal);--selection-background-color:rgba(var(--primary-color-rgb), .4);--selection-color:inherit;--white:#fff;--ripple-duration:.7s;--ripple-start-scale:0;--ripple-end-scale:2}@media only screen and (max-width:925px){:root{--ripple-duration:.4s;--ripple-start-scale:.27}}:root{--scrollbar-color:#0003;--menu-box-shadow-color:#00000026;--input-search-background-color:#fff;--input-search-border-color:#dfe1e5;--input-placeholder-color:#9ba0a5;--original-ripple-color:#00000014;--ripple-color:var(--original-ripple-color)}.night{--scrollbar-color:#fff3;--menu-box-shadow-color:#00000080;--input-search-background-color:#181818;--input-search-border-color:#2f2f2f;--original-ripple-color:#ffffff14;--ripple-color:var(--original-ripple-color)}html,body{width:100%;height:100%;margin:0;padding:0;overflow:hidden}[dir=ltr]{--reflect:1;--transform-origin-inline-start:left;--transform-origin-inline-end:right;--transform-origin-left-center:left center;--transform-origin-right-center:right center}[dir=rtl]{--reflect:-1;--transform-origin-inline-start:right;--transform-origin-inline-end:left;--transform-origin-left-center:right center;--transform-origin-right-center:left center}@supports (padding:max(0px)){html{padding:0 min(16px, env(safe-area-inset-right)) 0 min(16px, env(safe-area-inset-left))}}html{overscroll-behavior:auto;-webkit-font-smoothing:antialiased;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:#0000;font-size:16px}input,textarea{-webkit-user-select:text;user-select:text}body{background-color:var(--body-background-color);color:var(--primary-text-color);-webkit-user-select:none;user-select:none;transition:background-color .2s}body.animation-level-1:not(.animation-level-2){--transition-standard-in-time:.2s;--transition-standard-out-time:.15s;--ripple-duration:.4s}:root,body.animation-level-1:not(.animation-level-2){--transition-standard-in:var(--transition-standard-in-time) var(--transition-standard-easing);--transition-standard-out:var(--transition-standard-out-time) var(--transition-standard-easing)}a{color:var(--link-color)}button,input,optgroup,select,textarea,html{font-family:var(--font-regular)}input,textarea,button,select,a,div{-webkit-tap-highlight-color:#0000}pre,code,kbd,samp{font-family:var(--font-monospace)}::selection{background-color:var(--selection-background-color);color:var(--selection-color)}.whole{width:100%;height:100%;min-height:100%;margin:0 auto}h1,h2,h3,h4,h5,h6{font-weight:var(--font-weight-bold)}h4{margin:22px 0 14px;font-size:2rem;line-height:110%}@media only screen and (max-width:600px){h4{margin:2px 0 8px;font-size:20px}}input,[contenteditable=true]{caret-color:var(--primary-color);color:var(--primary-text-color);background-color:#0000}input,textarea{-webkit-appearance:none;font-size:inherit}input::-webkit-contacts-auto-fill-button{visibility:hidden;position:absolute;right:0}input::-webkit-credentials-auto-fill-button{visibility:hidden;position:absolute;right:0}input:-webkit-autofill:first-line{font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Noto Sans,Ubuntu,Cantarell,Helvetica Neue,Arial,sans-serif;font-size:16px}.secondary{color:var(--secondary-text-color)}.danger{color:var(--danger-color)!important}.danger .c-ripple__circle{background-color:var(--light-danger-color)}.blue,.primary{color:var(--primary-color)!important}.blue .c-ripple__circle,.primary .c-ripple__circle{background-color:var(--light-primary-color)}.hide{display:none!important}.z-depth-3{box-shadow:var(--menu-box-shadow)}.select-wrapper{background-color:var(--surface-color);z-index:3;transform-origin:top;opacity:0;border-radius:16px;flex-flow:column wrap;width:100%;max-height:23.5rem;transition:opacity .2s ease-out,transform .2s ease-out;display:flex;position:absolute;top:calc(100% + .5rem);left:0;overflow:hidden;transform:scale(.95)}.select-wrapper.active{opacity:1;transform:scale(1)}body:not(.animation-level-2) .select-wrapper{transform:none}body.animation-level-0 .select-wrapper{transition:none}.select-wrapper ul{margin:.5rem 0;padding:0}.select-wrapper li{cursor:pointer;text-align:start;grid-template-columns:calc(26px + 2rem) 1fr 80px;justify-content:space-between;align-items:center;height:3rem;padding:0 1rem;display:grid}html.no-touch .select-wrapper li:hover,html.no-touch .select-wrapper li:active{background-color:var(--light-secondary-text-color)}.select-wrapper .emoji{width:26px;height:26px;font-size:26px;line-height:1}.select-wrapper .scrollable{scrollbar-width:thin;scrollbar-color:var(--scrollbar-color) transparent;width:100%;max-height:23.5rem;position:relative;overflow-y:auto}.phone-code{color:var(--input-label-color,#9e9e9e);text-align:end}.stealthy{opacity:0;pointer-events:none;z-index:-1;outline:none;max-width:1px;max-height:1px;margin:0;position:absolute;top:0;left:0;overflow:hidden}.text-center{text-align:center;text-wrap:balance}.text-overflow-wrap{overflow-wrap:anywhere}.tgico{vertical-align:middle;width:1em;height:1em;line-height:1;display:inline-flex}.tgico svg{fill:currentColor;width:100%;height:100%}.no-scrollbar::-webkit-scrollbar{display:none}@keyframes grow-icon{0%{opacity:.8;transform:scale(.5)}50%{opacity:1;transform:scale(1.1)}to{transform:scale(1)}}@keyframes hide-icon{0%{opacity:.4;transform:scale(1)}to{opacity:0;transform:scale(.5)}}html.overlay-scroll .scrollable::-webkit-scrollbar{opacity:0;width:.375rem;height:0}html.overlay-scroll .scrollable::-webkit-scrollbar-thumb{width:0;height:0}html.overlay-scroll .scrollable::-webkit-scrollbar-thumb{opacity:0;transition:all .2s ease-in-out}html.overlay-scroll .scrollable::-webkit-scrollbar-button{width:0;height:0;display:none}html.overlay-scroll .scrollable::-webkit-scrollbar-corner{background-color:#0000}html.overlay-scroll .scrollable:hover{scrollbar-color:var(--scrollbar-color) #0000}html.overlay-scroll .scrollable:hover::-webkit-scrollbar{opacity:1}html.overlay-scroll .scrollable:hover::-webkit-scrollbar-thumb{background-color:var(--scrollbar-color);opacity:1;border-radius:16px;min-height:5rem;max-height:12.5rem}html.custom-scroll .scrollable::-webkit-scrollbar{width:0;height:0}html.custom-scroll .scrollable:hover .scrollable-thumb,html.custom-scroll .scrollable-thumb.is-focused{opacity:1}html.custom-scroll .scrollable-thumb--hidden{opacity:0!important;transition:none!important}.scrollable-x::-webkit-scrollbar{display:none}.no-scrollbar::-webkit-scrollbar{display:none}.no-scrollbar::-webkit-scrollbar{width:0;height:0}.no-scrollbar::-webkit-scrollbar-thumb{width:0;height:0}.no-scrollbar{scrollbar-width:none!important}.scrollable{-webkit-overflow-scrolling:touch;width:100%;height:100%;max-height:100%;position:absolute;inset:0;overflow:hidden}.scrollable.relative{position:relative}.scrollable.scrollable-x{scrollbar-width:none;-ms-overflow-style:none;overscroll-behavior-x:contain;overflow-x:auto}.scrollable.scrollable-y{overflow-y:auto;overflow-y:overlay;scrollbar-width:none;overscroll-behavior-y:contain}html.overlay-scroll .scrollable.scrollable-y{scrollbar-width:thin}.is-firefox .scrollable.scrollable-y{scrollbar-width:thin;scrollbar-color:#0000 #0000}body.animation-level-1 .scrollable.scrollable-y.scrollable-y-bordered{transition:border-top-color var(--transition-standard-in), border-bottom-color var(--transition-standard-in)}.scrollable.scrollable-y.scrollable-y-bordered-top{border-top:1px solid #0000}.scrollable.scrollable-y.scrollable-y-bordered-top:not(.scrolled-start){border-top-color:var(--border-color)}.scrollable.scrollable-y.scrollable-y-bordered-bottom{border-bottom:1px solid #0000}.scrollable.scrollable-y.scrollable-y-bordered-bottom:not(.scrolled-end){border-bottom-color:var(--border-color)}.scrollable-thumb{background-color:var(--scrollbar-color);cursor:default;opacity:0;border-radius:12px;width:5px;position:absolute;inset-inline-end:1px}body.animation-level-1 .scrollable-thumb{transition:opacity .1s ease-in-out}.scrollable-thumb-container{z-index:4;position:sticky;top:0}.rp{position:relative}.rp-overflow .c-ripple,.btn-menu-toggle.rp .c-ripple,.menu-horizontal-div-item.rp .c-ripple,.btn-corner.rp .c-ripple,.pinned-container-wrapper.rp .c-ripple,.btn-control-small.rp .c-ripple{border-radius:inherit;width:100%;height:100%;overflow:hidden}.rp-overflow .c-ripple__circle,.btn-menu-toggle.rp .c-ripple__circle,.menu-horizontal-div-item.rp .c-ripple__circle,.btn-corner.rp .c-ripple__circle,.pinned-container-wrapper.rp .c-ripple__circle,.btn-control-small.rp .c-ripple__circle{overflow:hidden}.c-ripple{position:absolute;inset:0}html.is-safari .c-ripple{border-radius:inherit;-webkit-mask-image:-webkit-radial-gradient(circle,#fff 100%,#000 100%)}.c-ripple__circle{opacity:var(--ripple-max-opacity,1);background-color:var(--ripple-color);animation-name:ripple-effect;animation-fill-mode:forwards;animation-duration:var(--ripple-duration);transition:calc(var(--ripple-duration) / 2) opacity, calc(var(--ripple-duration) / 2) background-color;pointer-events:none;border-radius:50%;display:block;position:absolute;transform:scale(0)}body:not(.animation-level-2) .c-ripple__circle{transform:scale(var(--ripple-end-scale));animation:none}body:not(.animation-level-2) .c-ripple__circle:not(.hiding){transition-duration:.1s}.c-ripple__circle.hiding,.c-ripple__square.hiding{opacity:0}@keyframes ripple-effect{0%{transform:scale(var(--ripple-start-scale))}to{transform:scale(var(--ripple-end-scale))}}.preloader-circular{transform-origin:50%;height:100%;margin:auto;animation:1s linear infinite rotate;position:absolute;inset:0}.preloader-path{stroke-dasharray:93.6375 124.85;stroke-dashoffset:0;stroke-linecap:round;stroke:#fff;stroke-width:3px}.preloader-container{cursor:pointer;opacity:0;width:54px;height:54px;margin:auto;display:flex;position:absolute;inset:0;overflow:hidden;transform:scale(0)}body.animation-level-1 .preloader-container{transition:opacity .2s ease-in-out}body.animation-level-2 .preloader-container{transition:opacity .2s ease-in-out,transform .2s ease-in-out}.preloader-container.is-visible.forwards{opacity:1;transform:scale(1)}.preloader-container-rtmp{opacity:0;pointer-events:none;-webkit-backdrop-filter:blur()brightness();backdrop-filter:blur()brightness();border-radius:inherit;position:absolute;inset:0;overflow:hidden}body:not(.animation-level-0) .preloader-container-rtmp{transition:opacity .2s ease-in-out,-webkit-backdrop-filter .2s ease-in-out,backdrop-filter .2s ease-in-out}.preloader-container-rtmp.is-visible.forwards{opacity:1}.preloader-template{border-radius:inherit;pointer-events:none;position:absolute;inset:0}.ckin__player.is-buffering.default-live .preloader-container-rtmp.is-visible.forwards{-webkit-backdrop-filter:blur(10px)brightness(.8);backdrop-filter:blur(10px)brightness(.8)}.preloader-container{--color:#fff}.preloader-container .you-spin-me-round{width:100%;height:100%}.preloader-container .preloader-circular{background-color:#0000004d;border-radius:50%;width:100%;height:100%;animation:none}.preloader-container .preloader-path-new{stroke-dasharray:5 149.82;stroke-dashoffset:0;stroke-linecap:round;stroke:var(--color);stroke-width:2px}body.animation-level-1 .preloader-container.is-visible:not(.manual) .you-spin-me-round,body.animation-level-1 .preloader-container.is-visible.animating .you-spin-me-round{animation:2s linear infinite rotate}body.animation-level-1 .preloader-container.is-visible:not(.manual) .preloader-path-new,body.animation-level-1 .preloader-container.is-visible.animating .preloader-path-new{transition:stroke-dasharray .2s ease-in-out,stroke-width .2s ease-in-out}.preloader-container.preloader-swing{cursor:default}.preloader-container.preloader-swing .you-spin-me-round{animation:1s linear infinite rotate!important}.preloader-container.preloader-swing .preloader-path-new{stroke-dasharray:112.36 149.82}.preloader-container.preloader-swing.preloader-streamable .preloader-path-new{stroke-dasharray:88.95 118.61}.preloader-container .preloader-close,.preloader-container .preloader-download{color:var(--color);opacity:1;width:56%;height:56%;margin:auto;position:absolute;inset:0}body.animation-level-1 .preloader-container .preloader-close,body.animation-level-1 .preloader-container .preloader-download{transition:opacity .2s ease-in-out}.preloader-container .preloader-close path,.preloader-container .preloader-download path{fill:var(--color)}html.no-touch .preloader-container .preloader-close:hover,html.no-touch .preloader-container .preloader-close:active,html.no-touch .preloader-container .preloader-download:hover,html.no-touch .preloader-container .preloader-download:active{background:0 0}.preloader-container:not(.manual) .preloader-download,.preloader-container.manual .preloader-close{opacity:0}.preloader-container.manual .preloader-path-new{stroke-width:0}.preloader-container .preloader-download{width:1.5rem;height:1.5rem}.preloader-container.preloader-streamable,.preloader-container.preloader-streamable svg{cursor:pointer!important}.preloader-container.preloader-streamable circle{stroke-width:2.5px!important}.preloader-container.preloader-streamable:after{content:"";background-color:var(--color);border-radius:.125rem;width:.8125rem;height:.8125rem;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}.preloader-container.preloader-transparent .preloader-circular{background-color:#0000}.preloader-container.preloader-bold .preloader-path-new{stroke-width:3.5px}@keyframes rotate{to{transform:rotate(360deg)}}@keyframes dash{0%{stroke-dasharray:1 200;stroke-dashoffset:0}50%{stroke-dasharray:89 200;stroke-dashoffset:-35px}to{stroke-dasharray:89 200;stroke-dashoffset:-124px}}@keyframes dashNew{0%{stroke-dasharray:1 149.82;stroke-dashoffset:0}50%{stroke-dasharray:112.36 149.82;stroke-dashoffset:-38px}to{stroke-dasharray:112.36 149.82;stroke-dashoffset:-149.82px}}@keyframes dashNewStreamable{0%{stroke-dasharray:1 200;stroke-dashoffset:0}50%{stroke-dasharray:89 200;stroke-dashoffset:-35px}to{stroke-dasharray:89 200;stroke-dashoffset:-237%}}.input-wrapper{grid-template-columns:100%;gap:1.5rem;width:100%;margin:0 auto;display:grid}.input-field{--height:3rem;--border-radius:16px;--padding:.8125rem;--padding-horizontal:1rem;--border-width:1px;align-items:center;display:flex;position:relative}.input-field .arrow-down{content:" ";top:50%;bottom:0;cursor:pointer;border:solid var(--secondary-text-color);vertical-align:middle;z-index:2;border-width:0 2px 2px 0;border-radius:1px;width:0;height:0;margin-top:-9px;padding:5px;transition:all .2s;display:inline-block;position:absolute;inset-inline-end:21px;transform:rotate(45deg)}.input-field label,.input-field-error-label{color:var(--input-label-color,#9e9e9e);top:0;inset-inline:var(--padding-horizontal) auto;z-index:2;background-color:var(--surface-color);height:1.5rem;transform-origin:var(--transform-origin-left-center);pointer-events:none;margin-top:calc((var(--height) - 1.5rem) / 2);white-space:nowrap;position:absolute;transform:translate(0)}body.animation-level-1 .input-field label,body.animation-level-1 .input-field-error-label{transition:opacity .1s,font-weight 0s .1s}body.animation-level-2 .input-field label,body.animation-level-2 .input-field-error-label{transition:transform .2s,padding .2s,opacity .1s,font-weight 0s .1s}.input-field-error-label{color:var(--danger-color)}.input-field-border{border:2px solid var(--primary-color);opacity:0;border-radius:var(--border-radius);pointer-events:none;z-index:1;position:absolute;inset:0}body.animation-level-1 .input-field-border{transition:opacity .2s}.input-field-icon{z-index:1;inset-inline-end:1rem;pointer-events:none;border-radius:.375rem;width:1.5rem;height:1.5rem;position:absolute;top:50%;transform:translateY(-50%)}.input-field-input{border:var(--border-width) solid var(--input-search-border-color);border-radius:var(--border-radius);background-color:var(--surface-color);padding:calc(var(--padding) - var(--border-width)) calc(var(--padding-horizontal) - var(--border-width));box-sizing:border-box;width:100%;min-height:var(--height);z-index:1;line-height:var(--line-height);transition:border-color;position:relative}.input-field-input:-webkit-autofill{-webkit-box-shadow:0 0 0px 1000px var(--surface-color) inset}.input-field-input:-webkit-autofill:hover{-webkit-box-shadow:0 0 0px 1000px var(--surface-color) inset}.input-field-input:-webkit-autofill:focus{-webkit-box-shadow:0 0 0px 1000px var(--surface-color) inset}.input-field-input:-webkit-autofill:active{-webkit-box-shadow:0 0 0px 1000px var(--surface-color) inset}.input-field-input:-webkit-autofill:first-line{font-family:var(--font-family);font-size:var(--font-size)!important;color:var(--primary-text-color)!important}.input-field-input:-webkit-autofill{font-family:var(--font-family);font-size:var(--font-size)!important;color:var(--primary-text-color)!important}.input-field-input:-webkit-autofill:hover{font-family:var(--font-family);font-size:var(--font-size)!important;color:var(--primary-text-color)!important}.input-field-input:-webkit-autofill:focus{font-family:var(--font-family);font-size:var(--font-size)!important;color:var(--primary-text-color)!important}.input-field-input:-webkit-autofill:active{font-family:var(--font-family);font-size:var(--font-size)!important;color:var(--primary-text-color)!important}body.animation-level-0 .input-field-input{transition:none!important}html.no-touch .input-field-input:hover:not(:focus):not(.error):not(.valid),html.no-touch .input-field-input:active:not(:focus):not(.error):not(.valid){border-color:var(--primary-color);transition:border-color .2s}html.no-touch .input-field-input:hover:not(:focus):not(.error):not(.valid)~label,html.no-touch .input-field-input:active:not(:focus):not(.error):not(.valid)~label{color:var(--primary-color)}.input-field-input:focus{border-color:var(--primary-color)}.input-field-input.error{border-color:var(--danger-color)}.input-field-input.error~label,.input-field-input.error~.input-field-error-label{color:var(--danger-color)!important}.input-field-input.error~.input-field-border{border-color:var(--danger-color)!important}.input-field-input.valid{border-color:#26962f}.input-field-input.valid~label{color:#26962f!important}.input-field-input.valid~.input-field-border{border-color:#26962f!important}.input-field-input:focus~.arrow-down{border-color:var(--primary-color);margin-top:-4px;transform:rotate(225deg)}.input-field-input:focus~label,.input-field-input:focus~.input-field-error-label,.input-field-input.force-focus~label,.input-field-input.force-focus~.input-field-error-label{color:var(--primary-color);font-weight:var(--font-weight-bold)}.input-field-input:focus~.input-field-border,.input-field-input.force-focus~.input-field-border{opacity:1}.input-field-input:focus~label,.input-field-input:focus~.input-field-error-label,.input-field-input:valid~label,.input-field-input:valid~.input-field-error-label,.input-field-input:not(.is-empty)~label,.input-field-input:not(.is-empty)~.input-field-error-label,.input-field-input:disabled~label,.input-field-input:disabled~.input-field-error-label,.input-field-input.force-focus~label,.input-field-input.force-focus~.input-field-error-label{transform:translate(calc((-.1875rem + 1rem - var(--padding-horizontal)) * var(--reflect)), calc(var(--height) / -2 + .0625rem)) scale(.75);opacity:1;padding:0 .3125rem}.input-field-input:not(:focus).is-empty~.input-field-placeholder~label,.input-field.has-error-label>label{opacity:0}.input-field .input-field-input[contenteditable=false]{opacity:var(--disabled-opacity);pointer-events:none}.input-field-placeholder{color:var(--input-placeholder-color);pointer-events:none;opacity:0;max-width:100%;z-index:1;white-space:nowrap;text-overflow:ellipsis;padding-inline-end:.5rem;display:block;position:absolute;inset-inline-start:var(--padding-horizontal);overflow:hidden}body.animation-level-1 .input-field-placeholder{transition:opacity var(--input-placeholder-transition) 10ms}body.animation-level-2 .input-field-placeholder{transition:opacity var(--input-placeholder-transition) 10ms, transform var(--input-placeholder-transition) 10ms;transform:translateX(calc(1rem * var(--reflect)))}.input-field-placeholder.is-empty{opacity:1}body.animation-level-2 .input-field-placeholder.is-empty{transform:translate(0)}.input-field-right-label{inset-inline-end:1rem;z-index:1;color:var(--secondary-text-color);position:absolute;top:50%;transform:translateY(-50%)}.input-field-height-wrapper{max-height:inherit;flex:auto;align-items:center;width:100%;min-width:0;min-height:0;display:flex;position:relative;overflow:hidden}body.animation-level-2 .input-field-height-wrapper{transition-property:height;transition-timing-function:var(--transition-standard-in)}.input-fields-row{gap:.5rem;display:flex}.input-fields-row .input-field{flex:auto;width:1%}::placeholder{color:var(--input-placeholder-color);opacity:1}::-moz-placeholder{color:var(--input-placeholder-color)}.input-clear{border:none;outline:none;padding:0}body:not(.animation-level-2) .input-clear.error{color:var(--danger-color)!important}body.animation-level-2 .input-clear.error{animation:.2s ease-in-out forwards input-shake}@keyframes input-shake{0%{transform:translate(0)}25%{transform:translate(-.5rem)}75%{transform:translate(.5rem)}to{transform:translate(0)}}.input-field-password .input-field-input{max-height:var(--height);padding-inline-end:2.5rem}.input-field-password .input-field-input[type=password]{padding-inline-start:calc(.875rem - var(--border-width));padding-top:0;padding-bottom:0;font-family:Arial,Helvetica,sans-serif;font-size:2.25rem;line-height:1}@media (-webkit-min-device-pixel-ratio:2){.input-field-password .input-field-input[type=password]{letter-spacing:.125rem;font-size:1.75rem}}html.is-ios .input-field-password .input-field-input[type=password]{font-size:1rem}.input-field-password .input-field-input[type=password]::-ms-reveal{display:none}.input-field-password .toggle-visible{z-index:2;color:var(--input-label-color,#9e9e9e);cursor:pointer;inset-inline-end:.375rem;justify-content:center;align-items:center;padding:.5rem;font-size:1.5rem;transition:color .2s;display:flex;position:absolute;top:50%;transform:translateY(-50%)}html.no-touch .input-field-password .toggle-visible:hover,html.no-touch .input-field-password .toggle-visible:active{color:var(--primary-text-color)}.input-field-phone .input-field-input{--letter-spacing:.24px}.input-field-phone .input-field-input:after{content:attr(data-left-pattern);color:var(--input-label-color,#9e9e9e);letter-spacing:var(--letter-spacing)}.btn,.btn-icon{cursor:pointer;background:0 0;border:none;outline:none}body.animation-level-0 .btn,body.animation-level-0 .btn-icon{transition:none}html.no-touch .btn:hover,html.no-touch .btn:active,html.no-touch .btn-icon:hover,html.no-touch .btn-icon:active{background-color:var(--light-secondary-text-color)}html.no-touch .btn.primary:hover,html.no-touch .btn.primary:active,html.no-touch .btn.blue:hover,html.no-touch .btn.blue:active,html.no-touch .btn.active:hover,html.no-touch .btn.active:active,html.no-touch .btn-icon.primary:hover,html.no-touch .btn-icon.primary:active,html.no-touch .btn-icon.blue:hover,html.no-touch .btn-icon.blue:active,html.no-touch .btn-icon.active:hover,html.no-touch .btn-icon.active:active{background-color:var(--light-primary-color)}html.no-touch .btn.danger:hover,html.no-touch .btn.danger:active,html.no-touch .btn-icon.danger:hover,html.no-touch .btn-icon.danger:active{background-color:var(--light-danger-color)}.btn:disabled,.btn-icon:disabled{opacity:var(--disabled-opacity);pointer-events:none!important}.btn-icon{text-align:center;color:var(--primary-text-color);background-color:#0000;border:none;justify-content:center;align-items:center;padding:.5rem;font-size:1.5rem;line-height:1;transition:color .15s ease-in-out,opacity .15s ease-in-out;display:flex;position:relative;border-radius:50%!important}.btn-icon.rp{overflow:hidden}.btn-icon.active{color:var(--primary-color)}.btn-primary{text-align:center;width:100%;height:3rem;font-weight:var(--font-weight-bold);cursor:pointer;transition:opacity var(--transition-standard-out), background-color var(--transition-standard-out), color var(--transition-standard-out);line-height:var(--line-height);text-transform:uppercase;border:none;border-radius:16px;padding:0;position:relative;overflow:hidden}html.no-touch .btn-primary:hover,html.no-touch .btn-primary:active{background:var(--dark-primary-color)}.btn-primary-transparent{--ripple-color:var(--light-primary-color);color:var(--primary-color);background-color:#0000}html.no-touch .btn-primary-transparent:hover,html.no-touch .btn-primary-transparent:active{background:var(--light-primary-color)}html.no-touch .btn-primary-transparent.danger:hover,html.no-touch .btn-primary-transparent.danger:active{background-color:var(--light-danger-color)}.btn-primary-transparent .preloader-circular .preloader-path{stroke:var(--primary-color)}body.animation-level-0 .btn-primary{transition:none}.btn-primary>svg,.btn-primary use{height:calc(100% - 20px);inset-inline:auto 15px}.btn-primary:disabled{opacity:var(--disabled-opacity);pointer-events:none!important}.btn-transparent{color:var(--primary-text-color);height:3rem;text-transform:unset;font-weight:400;line-height:var(--line-height);background-color:#0000;align-items:center;padding:0 1rem;display:flex}html.no-touch .btn-transparent:hover,html.no-touch .btn-transparent:active{background-color:var(--light-secondary-text-color)}html.no-touch .btn-transparent.danger:hover,html.no-touch .btn-transparent.danger:active{background-color:var(--light-danger-color)}html.no-touch .btn-transparent.primary:hover,html.no-touch .btn-transparent.primary:active{background-color:var(--light-primary-color)}.btn-transparent .button-icon{color:var(--secondary-text-color);margin-inline-end:2rem;font-size:1.5rem}@media only screen and (max-width:600px){.btn-transparent .button-icon{margin-inline-end:1.5rem}}.btn-transparent.btn-short .button-icon{margin-inline-end:1rem}.btn-disabled{pointer-events:none!important;cursor:default!important;color:var(--secondary-text-color)!important}.btn-disabled .button-icon{color:inherit!important}.btn-secondary{margin-top:-1rem}.btn-color-primary{background:var(--primary-color);color:#fff}:where(.a11y) .btn-color-primary{background:var(--primary-button-color,var(--primary-color))}:root{--glass-tint:#fff3;--glass-tint-strong:#ffffff42;--glass-tint-control:#ffffff47;--glass-tint-menu:#f8f5ffed;--glass-blur:16px;--glass-blur-strong:28px;--glass-blur-small:8px;--glass-saturate:170%;--glass-shadow:0 30px 60px -24px #301e8c61, 0 6px 18px -6px #301e8c24;--glass-shadow-small:0 10px 24px -10px #301e8c59;--glass-edge-top:#ffffffd9;--glass-edge-bottom:#ffffff38;--glass-rim-light:#fffffff2;--glass-rim-light-2:#ffffff8c;--glass-rim-fade:#ffffff2e;--glass-well:#ffffff29;--glass-well-border:#fff9;--glass-well-shadow:#28186e29;--glass-sheen:#ffffff4d;--glass-accent-alpha:.8;--qr-background:transparent;--qr-plate:#faf8ffe6;--glass-button-alpha:.7;--glass-button-edge:.75;--glass-button-caustic:.16;--glass-button-shadow:0 10px 26px -14px #301e8c8c;--glass-button-text-shadow:30, 18, 100;--field-base:linear-gradient(160deg, #d9cfff 0%, #eae1ff 45%, #d3e2ff 100%);--field-1:#7660fff2;--field-2:#5546d6bf;--field-3:#ff70c4b3;--field-4:#50beffa6;--field-texture:#ffffff42;--field-texture-2:#3c28aa12}.night{--glass-tint:#1e183e42;--glass-tint-strong:#14102c57;--glass-tint-control:#ffffff14;--glass-tint-menu:#1a1534f2;--glass-shadow:0 34px 70px -24px #000000b3, 0 8px 20px -8px #00000073;--glass-shadow-small:0 10px 24px -10px #0009;--glass-edge-top:#ffffff38;--glass-edge-bottom:#ffffff0d;--glass-rim-light:#ffffff73;--glass-rim-light-2:#c8beff47;--glass-rim-fade:#ffffff0f;--glass-well:#08061638;--glass-well-border:#ffffff29;--glass-well-shadow:#00000059;--glass-sheen:#beb4ff29;--glass-accent-alpha:.72;--glass-button-alpha:.46;--qr-plate:#14102adb;--glass-button-edge:.4;--glass-button-caustic:.16;--glass-button-shadow:0 12px 28px -14px #000000b3;--glass-button-text-shadow:0, 0, 0;--field-base:linear-gradient(160deg, #0b0920 0%, #110c2c 50%, #070d24 100%);--field-1:#624affd9;--field-2:#8f84ff80;--field-3:#c846dc8c;--field-4:#2878f080;--field-texture:#ffffff0e;--field-texture-2:#aaa0ff24}@media (prefers-reduced-transparency:reduce),(prefers-contrast:more){:root,.night{--glass-tint:var(--surface-color);--glass-tint-strong:var(--surface-color);--glass-tint-control:var(--surface-color);--glass-tint-menu:var(--surface-color);--glass-well:var(--surface-color);--glass-well-border:var(--border-color);--glass-rim-light:transparent;--glass-rim-light-2:transparent;--glass-rim-fade:transparent;--glass-sheen:transparent;--glass-accent-alpha:1;--glass-button-alpha:1;--qr-plate:var(--surface-color);--glass-button-caustic:0}}html{background:var(--field-base) fixed, var(--background-color)}body,#auth-pages{background:0 0!important}.light-field{z-index:-1;pointer-events:none;background:var(--field-base);position:fixed;inset:0;overflow:hidden}.light-field-blob{filter:blur(56px);will-change:transform;border-radius:50%;width:62vmax;height:62vmax;animation:46s ease-in-out infinite alternate light-field-drift;position:absolute}.light-field-blob:first-child{background:var(--field-1);top:-24vmax;left:-18vmax}.light-field-blob:nth-child(2){background:var(--field-2);animation-duration:58s;animation-delay:-12s;bottom:-30vmax;right:-16vmax}.light-field-blob:nth-child(3){background:var(--field-3);width:44vmax;height:44vmax;animation-duration:52s;animation-delay:-30s;top:30%;left:52%}.light-field-blob:nth-child(4){background:var(--field-4);width:40vmax;height:40vmax;animation-duration:64s;animation-delay:-20s;bottom:-12vmax;left:-10vmax}.light-field-texture{background:repeating-linear-gradient(118deg, var(--field-texture) 0 1px, transparent 1px 34px), radial-gradient(circle at 1px 1px, var(--field-texture-2) 1px, transparent 1.5px) 0 0/18px 18px;position:absolute;inset:0;-webkit-mask:radial-gradient(at 50% 45%,#000 0% 45%,#0000 85%);mask:radial-gradient(at 50% 45%,#000 0% 45%,#0000 85%)}@keyframes light-field-drift{0%{transform:translate(0,0)scale(1)}50%{transform:translate(6vmax,4vmax)scale(1.08)}to{transform:translate(-5vmax,7vmax)scale(.94)}}@media (prefers-reduced-motion:reduce){.light-field-blob{animation:none}}body.animation-level-0 .light-field-blob{animation:none}.glass-defs{width:0;height:0;position:absolute;overflow:hidden}.input-field label,.input-field-error-label{background-color:#0000}.input-field-input{background-color:var(--glass-well);-webkit-backdrop-filter:blur(var(--glass-blur-small)) saturate(var(--glass-saturate));backdrop-filter:blur(var(--glass-blur-small)) saturate(var(--glass-saturate));border-color:var(--glass-well-border);box-shadow:inset 0 1px 2px var(--glass-well-shadow), inset 0 -1px 0 var(--glass-edge-bottom);border-width:1px;transition:border-color .2s,box-shadow .2s,background-color .2s}.input-field-input:-webkit-autofill{-webkit-box-shadow:inset 0 1px 2px var(--glass-well-shadow);-webkit-text-fill-color:var(--primary-text-color);transition:background-color 600000s,color 600000s}.input-field-input:-webkit-autofill:hover{-webkit-box-shadow:inset 0 1px 2px var(--glass-well-shadow);-webkit-text-fill-color:var(--primary-text-color);transition:background-color 600000s,color 600000s}.input-field-input:-webkit-autofill:focus{-webkit-box-shadow:inset 0 1px 2px var(--glass-well-shadow);-webkit-text-fill-color:var(--primary-text-color);transition:background-color 600000s,color 600000s}.input-field-input:-webkit-autofill:active{-webkit-box-shadow:inset 0 1px 2px var(--glass-well-shadow);-webkit-text-fill-color:var(--primary-text-color);transition:background-color 600000s,color 600000s}@media (hover:hover){.input-field-input:hover:not(:focus):not(.error):not(.valid){border-color:rgba(var(--primary-color-rgb), .55)}}.input-field-input:focus{box-shadow:inset 0 1px 2px var(--glass-well-shadow), 0 0 0 4px rgba(var(--primary-color-rgb), .16), 0 0 18px -2px rgba(var(--primary-color-rgb), .45);background-color:var(--glass-tint-control);outline:none}.input-field-input:focus-visible{box-shadow:inset 0 1px 2px var(--glass-well-shadow), 0 0 0 4px rgba(var(--primary-color-rgb), .16), 0 0 18px -2px rgba(var(--primary-color-rgb), .45);background-color:var(--glass-tint-control);outline:none}.input-field-input.error{box-shadow:inset 0 1px 2px var(--glass-well-shadow), 0 0 0 4px rgba(var(--danger-color-rgb), .16), 0 0 18px -2px rgba(var(--danger-color-rgb), .45)}.input-field-input:focus~label,.input-field-input:focus~.input-field-error-label,.input-field-input:valid~label,.input-field-input:valid~.input-field-error-label,.input-field-input:not(.is-empty)~label,.input-field-input:not(.is-empty)~.input-field-error-label,.input-field-input:disabled~label,.input-field-input:disabled~.input-field-error-label,.input-field-input.force-focus~label,.input-field-input.force-focus~.input-field-error-label{transform:translate(calc((-.25rem + 1rem - var(--padding-horizontal)) * var(--reflect)), calc(var(--height) / -2 - .625rem)) scale(.8);padding:0}.input-field-border{display:none}.input-wrapper>*+.input-field{margin-top:1.875rem}.btn-primary.btn-color-primary,.btn-primary:not(.btn-transparent):not(.btn-secondary){background-color:rgba(var(--primary-color-rgb), var(--glass-button-alpha));-webkit-backdrop-filter:blur(var(--glass-blur-small)) saturate(var(--glass-saturate));backdrop-filter:blur(var(--glass-blur-small)) saturate(var(--glass-saturate));box-shadow:inset 0 1px 0 var(--glass-edge-top), inset 0 -1px 0 var(--glass-edge-bottom), var(--glass-button-shadow)}.btn-primary.btn-color-primary:after,.btn-primary:not(.btn-transparent):not(.btn-secondary):after{content:"";border-radius:inherit;pointer-events:none;background:linear-gradient(135deg, var(--glass-rim-light) 0%, var(--glass-rim-fade) 28%, transparent 50%, var(--glass-rim-fade) 72%, var(--glass-rim-light-2) 100%);-webkit-mask-composite:xor;z-index:3;padding:1px;position:absolute;inset:0;-webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);-webkit-mask-composite:xor,source-over;mask:linear-gradient(#000 0 0) content-box exclude,linear-gradient(#000 0 0)}html.glass-refraction .btn-primary.btn-color-primary,html.glass-refraction .btn-primary:not(.btn-transparent):not(.btn-secondary){-webkit-backdrop-filter:url(#mp-glass-refraction) blur(var(--glass-blur-small)) saturate(var(--glass-saturate));backdrop-filter:url(#mp-glass-refraction) blur(var(--glass-blur-small)) saturate(var(--glass-saturate))}.btn-primary.btn-color-primary,.btn-primary:not(.btn-transparent):not(.btn-secondary){background-image:radial-gradient(120% 70% at 50% 120%, rgba(255, 255, 255, var(--glass-button-caustic)), #fff0 60%), linear-gradient(180deg, rgba(var(--primary-color-rgb), .12), rgba(var(--primary-color-rgb), 0) 55%);box-shadow:inset 0 1px 0 rgba(255, 255, 255, var(--glass-button-edge)), inset 0 -1px 0 rgba(255, 255, 255, calc(var(--glass-button-edge) / 3)), inset 0 0 0 1px rgba(255, 255, 255, calc(var(--glass-button-edge) / 4)), var(--glass-button-shadow);color:#fff;text-shadow:0 1px 2px rgba(var(--glass-button-text-shadow), .35);transition:transform .2s cubic-bezier(.3,.7,.4,1.3),box-shadow .2s,background-color .2s;overflow:hidden}@media (hover:hover){.btn-primary.btn-color-primary:hover:not(:disabled),.btn-primary:not(.btn-transparent):not(.btn-secondary):hover:not(:disabled){background-color:rgba(var(--primary-color-rgb), calc(var(--glass-button-alpha) + .08))}}.btn-primary.btn-color-primary:active:not(:disabled),.btn-primary:not(.btn-transparent):not(.btn-secondary):active:not(:disabled){box-shadow:inset 0 2px 6px rgba(var(--glass-button-text-shadow), .25), inset 0 0 0 1px rgba(255, 255, 255, calc(var(--glass-button-edge) / 3)), var(--glass-shadow-small);transform:scale(.98)}.btn-primary.btn-secondary,.btn-primary.btn-transparent{border-radius:999px;transition:background-color .2s,box-shadow .2s}@media (hover:hover){.btn-primary.btn-secondary:hover,.btn-primary.btn-transparent:hover{background-color:var(--glass-tint-control);box-shadow:inset 0 1px 0 var(--glass-edge-top), inset 0 0 0 1px var(--glass-rim-fade)}}.select-wrapper{background-color:var(--glass-tint-menu);-webkit-backdrop-filter:blur(var(--glass-blur-strong)) saturate(var(--glass-saturate));backdrop-filter:blur(var(--glass-blur-strong)) saturate(var(--glass-saturate));box-shadow:inset 0 1px 0 var(--glass-edge-top), inset 0 -1px 0 var(--glass-edge-bottom), var(--glass-shadow)}html.glass-refraction .select-wrapper{-webkit-backdrop-filter:url(#mp-glass-refraction) blur(var(--glass-blur-strong)) saturate(var(--glass-saturate));backdrop-filter:url(#mp-glass-refraction) blur(var(--glass-blur-strong)) saturate(var(--glass-saturate))}.select-wrapper{border:1px solid var(--glass-rim-fade)}.select-wrapper li{border-radius:.75rem;margin-inline:.25rem}.btn-primary .c-ripple__circle{background-color:#ffffff47}#madeline-app{display:contents}.authFlow_host{max-width:100%;overflow:hidden}.authFlow_host .btn-primary{text-transform:uppercase}.authFlow_host .btn-primary .inline-icon{margin-inline-start:.125rem}.authFlow_host .input-wrapper{flex:none;margin-top:2rem}.authFlow_closeButton,.authFlow_themeButton{background-color:var(--message-highlighting-color);color:#fff}html.no-touch .authFlow_closeButton:hover,html.no-touch .authFlow_closeButton:active,html.no-touch .authFlow_themeButton:hover,html.no-touch .authFlow_themeButton:active{background-color:rgba(var(--message-highlighting-color-rgb), calc(var(--message-highlighting-alpha) + .24))!important}.authFlow_closeButton,.authFlow_themeButton{z-index:100;display:flex;position:fixed;top:24px}.authFlow_closeButton{inset-inline-start:24px}.authFlow_themeButton{inset-inline-end:24px}.authFlow_leaving .authFlow_closeButton,.authFlow_leaving .authFlow_themeButton{opacity:0;pointer-events:none}.authFlow_scrollable{box-sizing:border-box;opacity:1;transition:opacity var(--transition-standard-in);flex-direction:column;margin:0 auto;padding:0 1rem;display:flex;position:relative}.authFlow_placeholder{flex:1;width:100%;min-height:1rem}.authFlow_placeholderTop{min-height:calc(24px + 3.5rem)}.authFlow_cardsContainer{width:min(24.5rem,100%);margin:0 auto;position:relative}.authFlow_card{background-color:var(--surface-color);border-radius:2rem;padding:1rem}.authFlow_card .btn-secondary{font-weight:var(--font-weight-normal);margin-top:.5rem}.authFlow_card .btn-secondary+.btn-secondary{margin-top:0}.authFlow_logo{width:100%;height:100%;fill:var(--primary-color)}.authFlow_logoContainer{margin-block:1rem 1.5rem!important}.authFlow_errorLabel{text-align:center;color:var(--danger-color);font-size:14px}.authFlow_forgotLink{font-size:var(--font-size-14);line-height:var(--line-height-14);color:var(--secondary-text-color);overflow-wrap:anywhere;text-wrap:pretty}.authFlow_forgotLink a{color:var(--primary-color)}.authFlow_forgotLink{text-align:start;margin-top:-12px;margin-inline:.5rem}.authFlow_phoneWrapper{justify-content:center;align-items:center;display:flex}.authFlow_phoneWrapper .authFlow_phone{margin:0}.authFlow_phoneWrapper .authFlow_phone.error{border-color:var(--danger-color)!important}.authFlow_phoneWrapper .authFlow_phoneEdit{opacity:.5;cursor:pointer;width:1.5rem;height:1.5rem;margin-inline-start:.4rem;font-size:1.5rem;line-height:1;transition:opacity .2s}html.authFlow_no-touch .authFlow_phoneWrapper .authFlow_phoneEdit:hover,html.authFlow_no-touch .authFlow_phoneWrapper .authFlow_phoneEdit:active{opacity:1}.authFlow_codeInputField{margin-top:1.5rem}.authFlow_codeInputHost{justify-items:center;display:grid}.authFlow_wordInputField{width:100%;text-align:initial;margin-top:1.5rem}.authFlow_wordSubmit{width:100%;margin-top:1rem}.authFlow_cardLink{font-size:var(--font-size-14);line-height:var(--line-height-14);color:var(--secondary-text-color);overflow-wrap:anywhere;text-wrap:pretty}.authFlow_cardLink a{color:var(--primary-color)}.authFlow_cardLink{text-align:center;margin-top:1rem}.authFlow_qrDescription{--marker-size:1.375rem;max-width:480px;line-height:var(--line-height);text-align:start;margin:1.5rem auto 1rem;padding-inline-start:1rem;list-style:none}.authFlow_qrDescriptionMarker{width:var(--marker-size);height:var(--marker-size);background-color:var(--light-primary-color);color:var(--primary-color);font-weight:var(--font-weight-bold);font-size:var(--font-size-14);border-radius:50%;flex:none;justify-content:center;align-items:center;display:flex}.authFlow_qrDescriptionItem{line-height:var(--marker-size);align-items:flex-start;gap:.75rem;margin-top:.625rem;display:flex}.authFlow_qrContainer{background-color:var(--light-filled-primary-color);border-radius:16px;margin-block:1rem;padding:1rem}.authFlow_qrCanvas{width:100%;height:100%}.authFlow_pageSignUp .input-field{text-align:initial}.authFlow_pageAuthCode .input-wrapper{justify-items:center}@media only screen and (max-width:600px){.authFlow_pagePassword .input-wrapper{margin-top:31px}}.authFlow_pagePassword .input-wrapper .btn-primary{margin-top:-.5rem}.authFlow_pageSignQR{overflow:unset!important}.authFlow_pageSignQR .preloader{left:unset;top:unset;transform:none}.authFlow_hostEnter{opacity:0;transform:translateX(calc(100px * var(--reflect)));transition:all .4s}.authFlow_hostEntering{opacity:1;transform:translate(0)}.authFlow_hostExit{opacity:1;transition:all .2s;transform:scale(1)}.authFlow_hostExiting{opacity:0;transform:scale(1.025)}.authFlow_cardEnterActive,.authFlow_cardExitActive{transition:opacity .18s,transform .2s}.authFlow_cardEnter{opacity:0;transform:translateX(calc(24px * var(--reflect)))}.authFlow_cardEnterTo,.authFlow_cardExit{opacity:1;transform:translate(0)}.authFlow_cardExitTo{opacity:0;transform:translateX(calc(-24px * var(--reflect)))}.authFlow_notices{text-align:start;flex-direction:column;gap:.5rem;margin-top:1rem;display:flex}.authFlow_notice{--notice-color:var(--primary-color);--notice-glyph:var(--surface-color);background-color:color-mix(in srgb, var(--notice-color) 10%, transparent);font-size:var(--font-size-14);line-height:var(--line-height-14);color:var(--primary-text-color);overflow-wrap:anywhere;border-radius:12px;align-items:flex-start;gap:.75rem;padding:.75rem .875rem;display:flex}.authFlow_notice a{color:var(--primary-color)}.authFlow_notice.authFlow_warning{--notice-color:#e8a33d}.authFlow_notice.authFlow_error{--notice-color:var(--danger-color)}.authFlow_notice.authFlow_success{--notice-color:#4fae4e}.night .authFlow_notice{background-color:color-mix(in srgb, var(--notice-color) 14%, transparent)}.authFlow_noticeIcon{width:1.25rem;height:1.25rem;margin-top:calc((var(--line-height-14) - 1.25rem) / 2);color:var(--notice-color);flex:none}.authFlow_noticeBody{flex:1;min-width:0}.authFlow_noticeControls{flex:none;align-items:center;gap:.125rem;margin:-.25rem -.375rem -.25rem 0;display:flex}.authFlow_noticeClose{width:1.75rem;height:1.75rem;color:var(--secondary-text-color);cursor:pointer;background:0 0;border:0;border-radius:50%;justify-content:center;align-items:center;padding:0;display:inline-flex}.authFlow_noticeClose svg{width:1rem;height:1rem}@media (hover:hover){.authFlow_noticeClose:hover{background-color:color-mix(in srgb, var(--notice-color) 14%, transparent);color:var(--primary-text-color)}}.authFlow_noticePager{color:var(--secondary-text-color);font:inherit;font-size:var(--font-size-12,12px);font-variant-numeric:tabular-nums;cursor:pointer;white-space:nowrap;background:0 0;border:0;border-radius:1rem;align-items:center;gap:.125rem;padding:.25rem .25rem .25rem .5rem;display:inline-flex}.authFlow_noticePager svg{width:1rem;height:1rem;transform:scaleX(var(--reflect,1))}@media (hover:hover){.authFlow_noticePager:hover{background-color:color-mix(in srgb, var(--notice-color) 14%, transparent);color:var(--primary-text-color)}}.authFlow_noticeTitle{font-weight:var(--font-weight-bold);margin-bottom:.125rem}.authFlow_noticeCode{background-color:var(--surface-color);white-space:pre-wrap;overflow-wrap:anywhere;border-radius:.5rem;margin:.5rem 0 0;padding:.5rem .625rem;font-family:ui-monospace,Menlo,Consolas,monospace;font-size:13px}.authFlow_instructions{--marker-size:1.375rem;line-height:var(--line-height);text-align:start;margin:1rem 0 0;padding:0;list-style:none}.authFlow_instructions ul{color:var(--secondary-text-color);font-size:var(--font-size-14);line-height:var(--line-height-14);margin:.25rem 0 0;padding-inline-start:1.125rem}.authFlow_instructions a{color:var(--primary-color)}.authFlow_instructionText{overflow-wrap:anywhere;min-width:0}.authFlow_pageApi .input-field{text-align:initial}.authFlow_fieldHint{font-size:var(--font-size-14);line-height:var(--line-height-14);color:var(--secondary-text-color);text-align:start;margin:-1rem .75rem 0}.authFlow_avatar{background:linear-gradient(#fff3, transparent), var(--primary-color);color:#fff;width:100%;height:100%;font-size:2.75rem;font-weight:var(--font-weight-bold);-webkit-user-select:none;user-select:none;border-radius:50%;justify-content:center;align-items:center;display:flex}.authFlow_statusIcon{width:100%;height:100%}.authFlow_card{background-color:var(--glass-tint-strong);-webkit-backdrop-filter:blur(var(--glass-blur)) saturate(var(--glass-saturate));backdrop-filter:blur(var(--glass-blur)) saturate(var(--glass-saturate));box-shadow:inset 0 1px 0 var(--glass-edge-top), inset 0 -1px 0 var(--glass-edge-bottom), var(--glass-shadow)}.authFlow_card:before{content:"";border-radius:inherit;pointer-events:none;background:linear-gradient(135deg, var(--glass-rim-light) 0%, var(--glass-rim-fade) 28%, transparent 50%, var(--glass-rim-fade) 72%, var(--glass-rim-light-2) 100%);-webkit-mask-composite:xor;z-index:3;padding:1px;position:absolute;inset:0;-webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);-webkit-mask-composite:xor,source-over;mask:linear-gradient(#000 0 0) content-box exclude,linear-gradient(#000 0 0)}html.glass-refraction .authFlow_card{-webkit-backdrop-filter:url(#mp-glass-refraction) blur(var(--glass-blur)) saturate(var(--glass-saturate));backdrop-filter:url(#mp-glass-refraction) blur(var(--glass-blur)) saturate(var(--glass-saturate))}.authFlow_card{background-color:var(--glass-tint-strong);border-radius:2.25rem;padding:1.25rem 1.25rem 1rem;position:relative}.authFlow_card:after{content:"";border-radius:inherit;pointer-events:none;background:radial-gradient(36rem circle at var(--glass-x,30%) var(--glass-y,0%), var(--glass-sheen), transparent 42%) fixed;z-index:-1;position:absolute;inset:0}.authFlow_closeButton,.authFlow_themeButton{background-color:var(--glass-tint);-webkit-backdrop-filter:blur(var(--glass-blur-small)) saturate(var(--glass-saturate));backdrop-filter:blur(var(--glass-blur-small)) saturate(var(--glass-saturate));box-shadow:inset 0 1px 0 var(--glass-edge-top), inset 0 -1px 0 var(--glass-edge-bottom), var(--glass-shadow-small)}.authFlow_closeButton:before,.authFlow_themeButton:before{content:"";border-radius:inherit;pointer-events:none;background:linear-gradient(135deg, var(--glass-rim-light) 0%, var(--glass-rim-fade) 28%, transparent 50%, var(--glass-rim-fade) 72%, var(--glass-rim-light-2) 100%);-webkit-mask-composite:xor;z-index:3;padding:1px;position:absolute;inset:0;-webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);-webkit-mask-composite:xor,source-over;mask:linear-gradient(#000 0 0) content-box exclude,linear-gradient(#000 0 0)}html.glass-refraction .authFlow_closeButton,html.glass-refraction .authFlow_themeButton{-webkit-backdrop-filter:url(#mp-glass-refraction) blur(var(--glass-blur-small)) saturate(var(--glass-saturate));backdrop-filter:url(#mp-glass-refraction) blur(var(--glass-blur-small)) saturate(var(--glass-saturate))}.authFlow_closeButton,.authFlow_themeButton{color:var(--primary-color);justify-content:center;align-items:center;width:2.75rem;height:2.75rem}@media (hover:hover){.authFlow_closeButton:hover,.authFlow_themeButton:hover{background-color:var(--glass-tint-strong)!important}}.authFlow_notice{background-color:color-mix(in srgb, var(--notice-color) 12%, transparent);-webkit-backdrop-filter:blur(var(--glass-blur-small)) saturate(var(--glass-saturate));backdrop-filter:blur(var(--glass-blur-small)) saturate(var(--glass-saturate));box-shadow:inset 0 1px 0 var(--glass-edge-top), inset 0 -1px 0 var(--glass-edge-bottom), 0 0 0 transparent;border-inline-start:0;border:1px solid color-mix(in srgb, var(--notice-color) 32%, transparent);border-radius:1rem}.authFlow_notice.authFlow_warning{background-color:#e6a23c24}.authFlow_notice.authFlow_error{background-color:rgba(var(--danger-color-rgb), .12)}.authFlow_notice.authFlow_success{background-color:#4fae4e1f}.authFlow_noticeCode{background-color:var(--glass-well);border:1px solid var(--glass-well-border)}.authFlow_qrContainer{background-color:var(--qr-plate);-webkit-backdrop-filter:blur(var(--glass-blur)) saturate(var(--glass-saturate));backdrop-filter:blur(var(--glass-blur)) saturate(var(--glass-saturate));box-shadow:inset 0 1px 0 var(--glass-edge-top), inset 0 -1px 0 var(--glass-edge-bottom), 0 10px 30px -14px rgba(var(--primary-color-rgb), .45)}.authFlow_qrContainer:before{content:"";border-radius:inherit;pointer-events:none;background:linear-gradient(135deg, var(--glass-rim-light) 0%, var(--glass-rim-fade) 28%, transparent 50%, var(--glass-rim-fade) 72%, var(--glass-rim-light-2) 100%);-webkit-mask-composite:xor;z-index:3;padding:1px;position:absolute;inset:0;-webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);-webkit-mask-composite:xor,source-over;mask:linear-gradient(#000 0 0) content-box exclude,linear-gradient(#000 0 0)}.authFlow_qrContainer{border-radius:1.75rem;position:relative}.authFlow_avatar{background:radial-gradient(120% 90% at 30% 15%, #ffffff8c, #fff0 45%), rgba(var(--primary-color-rgb), var(--glass-accent-alpha));box-shadow:inset 0 1px 0 #fff9, inset 0 -6px 14px #0000001f, 0 14px 30px -12px rgba(var(--primary-color-rgb), .7)}.authFlow_logoContainer{box-shadow:0 18px 40px -14px rgba(var(--primary-color-rgb), .75), 0 4px 10px -4px rgba(var(--primary-color-rgb), .45);border-radius:28%;position:relative}.authFlow_logoContainer:after{content:"";border-radius:inherit;pointer-events:none;background:radial-gradient(120% 80% at 25% 0,#ffffff8c,#fff0 48%),linear-gradient(#fff0 70%,#ffffff2e);position:absolute;inset:0;box-shadow:inset 0 1px 1px #ffffffb3,inset 0 -2px 6px #0000001f}.mediaHeader_container{flex-direction:column;gap:.5rem;max-width:100%;display:flex}.mediaHeader_alignStart{align-items:flex-start}.mediaHeader_alignStart .mediaHeader_title,.mediaHeader_alignStart .mediaHeader_subtitle{text-align:start;text-wrap:initial}.mediaHeader_onBackdrop{color:#fff;position:relative}.mediaHeader_onBackdrop .mediaHeader_sticker,.mediaHeader_onBackdrop .mediaHeader_title,.mediaHeader_onBackdrop .mediaHeader_subtitle{position:relative}.mediaHeader_onBackdrop .mediaHeader_subtitle{color:#fffc}.mediaHeader_backdrop{z-index:0;display:grid;position:absolute;inset:0;overflow:hidden}.mediaHeader_backdrop>*{grid-area:1/1}.mediaHeader_marginTop{margin-top:1.5rem}.mediaHeader_marginBottom{margin-bottom:1rem}.mediaHeader_title,.mediaHeader_subtitle{text-align:center;text-wrap:balance;word-break:break-word;padding-inline:1rem}.mediaHeader_title{font-size:var(--font-size-24);line-height:var(--line-height-24);font-weight:var(--font-weight-bold);margin-block:0}.mediaHeader_title.mediaHeader_title20{font-size:var(--font-size-20);line-height:var(--line-height-20)}.mediaHeader_subtitle{font-size:var(--font-size-16);line-height:var(--line-height-16);white-space:pre-line}.mediaHeader_subtitle.mediaHeader_secondary{color:var(--secondary-text-color);font-size:var(--font-size-14);line-height:var(--line-height-14)}.mediaHeader_subtitle.mediaHeader_danger{color:var(--danger-color)}.mediaHeader_sticker{--sticker-size:130px;width:var(--sticker-size);height:var(--sticker-size);justify-content:center;align-items:center;margin-inline:auto;display:flex;position:relative}.mediaHeader_sticker>img{object-fit:contain;width:100%;height:100%}.mediaHeader_lottie{width:var(--size);height:var(--size);max-width:100%;max-height:100%;position:relative}.codeInputField_wrap{pointer-events:none;justify-content:center;gap:.625rem;width:100%;display:flex;position:relative;container-type:inline-size}.codeInputField_input{color:#0000;caret-color:#0000;height:100%;box-shadow:none;letter-spacing:-1em;font-variant-numeric:tabular-nums;pointer-events:all;background:0 0;border:0 solid #0000;outline:0 solid #0000;padding:0;font-family:monospace;line-height:1;display:flex;position:absolute;inset:0}.codeInputField_disabled .codeInputField_input{pointer-events:none}:where(.a11y) .codeInputField_wrap .codeInputField_input:focus-visible{outline:none}.codeInputField_digit{border:1px solid var(--input-search-border-color);min-width:0;max-width:3rem;height:3rem;transition-property:border-color,opacity;transition-duration:.1s,.3s;transition-timing-function:var(--transition-standard-easing);border-radius:.75rem;flex:3rem;justify-content:center;align-items:center;display:flex;position:relative;overflow:hidden}.codeInputField_digit.codeInputField_active{border-color:var(--primary-color);transition:none}.codeInputField_error .codeInputField_digit{border-color:var(--danger-color)}.codeInputField_disabled .codeInputField_digit{opacity:.75;pointer-events:none}.codeInputField_digitContent{justify-content:center;align-items:center;display:flex;position:absolute;inset:0}.codeInputField_digit .s-enter-active,.codeInputField_digit .s-exit-active{transition-property:opacity,transform;transition-duration:.2s;transition-timing-function:cubic-bezier(.175,.885,.32,1.275)}.codeInputField_digit .s-enter{transform:translateY(20px)}.codeInputField_digit .s-exit-to{opacity:0;transform:scale(.5)}@container (width<=320px){.codeInputField_digit{max-width:2.5rem;height:2.5rem}}.codeInputField_caret{background-color:var(--primary-text-color);width:1px;height:1rem;transition-duration:1s;animation:1.25s ease-out infinite codeInputField_caret-blink}@keyframes codeInputField_caret-blink{0%,70%,to{opacity:1}20%,50%{opacity:0}}.codeInputField_digit{background-color:var(--glass-well);-webkit-backdrop-filter:blur(var(--glass-blur-small)) saturate(var(--glass-saturate));backdrop-filter:blur(var(--glass-blur-small)) saturate(var(--glass-saturate));border-color:var(--glass-well-border);box-shadow:inset 0 1px 2px var(--glass-well-shadow), inset 0 -1px 0 var(--glass-edge-bottom);border-radius:.875rem;transition-property:border-color,box-shadow,opacity}.codeInputField_digit.codeInputField_active{box-shadow:inset 0 1px 2px var(--glass-well-shadow), 0 0 0 4px rgba(var(--primary-color-rgb), .16), 0 0 18px -2px rgba(var(--primary-color-rgb), .45);background-color:var(--glass-tint-control)}.codeInputField_error .codeInputField_digit{box-shadow:inset 0 1px 2px var(--glass-well-shadow), 0 0 0 4px rgba(var(--danger-color-rgb), .16), 0 0 18px -2px rgba(var(--danger-color-rgb), .45)}body.animation-level-1 .autoHeight_outer.autoHeight_hasTransition{transition:height .2s}.autoHeight_outer.autoHeight_overflowHidden{overflow:hidden}
/*$vite$:1*/<?php
/**
 * The fields of a form.
 *
 * @var \danog\MadelineProto\WebUI\Template $this
 * @var \danog\MadelineProto\WebUI\Page $page
 */
use danog\MadelineProto\WebUI\FieldType;

?>
<?php foreach ($page->fields as $field): ?>
    <?php
    $id = 'f-'.preg_replace('/[^a-z0-9_-]/i', '-', $field->name);
    $showWhen = $field->showWhen === null ? null : $field->showWhen[0].'='.$field->showWhen[1];
    $hintId = $field->hint !== '' ? "$id-hint" : null;
    ?>
    <?php if ($field->type === FieldType::HIDDEN): ?>
        <input type="hidden"<?= $this->attrs(['name' => $field->name, 'value' => $field->value]) ?>>
    <?php elseif ($field->type === FieldType::CHOICE): ?>
        <fieldset class="field choice"<?= $this->attrs(['data-show-when' => $showWhen]) ?>>
            <legend><?= $this->e($field->label) ?></legend>
            <div class="segmented">
                <?php foreach ($field->options as $value => $label): ?>
                    <label class="segment">
                        <input type="radio"<?= $this->attrs([
                            'name' => $field->name,
                            'value' => (string) $value,
                            'checked' => (string) $value === $field->value,
                            'required' => $field->required,
                        ]) ?>>
                        <span><?= $this->e($label) ?></span>
                    </label>
                <?php endforeach ?>
            </div>
            <?php if ($hintId !== null): ?>
                <p class="hint"><?= $this->e($field->hint) ?></p>
            <?php endif ?>
        </fieldset>
    <?php else: ?>
        <div class="field is-<?= $field->type->value ?>"<?= $this->attrs(['data-show-when' => $showWhen]) ?>>
            <label for="<?= $this->e($id) ?>">
                <?= $this->e($field->label) ?>
                <?php if (!$field->required): ?>
                    <span class="optional"><?= $this->t('webOptional') ?></span>
                <?php endif ?>
            </label>
            <?php $attrs = $field->getInputAttributes($id) + ['aria-describedby' => $hintId] ?>
            <?php if ($field->type === FieldType::TEXTAREA): ?>
                <textarea rows="3"<?= $this->attrs($attrs) ?>><?= $this->e($field->value) ?></textarea>
            <?php elseif ($field->type === FieldType::PASSWORD): ?>
                <div class="reveal">
                    <input<?= $this->attrs($attrs + ['value' => $field->value]) ?>>
                    <button type="button" class="reveal-toggle" data-reveal="<?= $this->e($id) ?>" data-show-label="<?= $this->t('webShowPassword') ?>" data-hide-label="<?= $this->t('webHidePassword') ?>" hidden><?= $this->t('webShowPassword') ?></button>
                </div>
            <?php else: ?>
                <input<?= $this->attrs($attrs + ['value' => $field->value]) ?>>
            <?php endif ?>
            <?php if ($hintId !== null): ?>
                <p class="hint" id="<?= $this->e($hintId) ?>"><?= $this->e($field->hint) ?></p>
            <?php endif ?>
        </div>
    <?php endif ?>
<?php endforeach ?>
<?php
/**
 * @var \danog\MadelineProto\WebUI\Template $this
 */
?>
<div class="brand">
    <span class="brand-mark" aria-hidden="true"><?= $this->asset('assets/mark.svg') ?></span>
    <span class="brand-name">MadelineProto</span>
</div>
<?php
/**
 * @var \danog\MadelineProto\WebUI\Template $this
 * @var list<\danog\MadelineProto\WebUI\Alert> $alerts
 */
use danog\MadelineProto\WebUI\AlertLevel;

?>
<?php foreach ($alerts as $alert): ?>
    <div class="alert is-<?= $alert->level->value ?>"<?= $this->attrs(['role' => $alert->level === AlertLevel::ERROR ? 'alert' : 'status']) ?>>
        <?php if ($alert->title !== null): ?>
            <p class="alert-title"><?= $this->e($alert->title) ?></p>
        <?php endif ?>
        <p><?= $this->e($alert->message) ?></p>
        <?php if ($alert->code !== null): ?>
            <pre><code><?= $this->e($alert->code) ?></code></pre>
        <?php endif ?>
    </div>
<?php endforeach ?>
<?php
/**
 * The buttons of a form.
 *
 * @var \danog\MadelineProto\WebUI\Template $this
 * @var \danog\MadelineProto\WebUI\Page $page
 */
?>
<div class="form-actions">
    <button type="submit" class="btn btn-primary"><?= $this->e($page->submit) ?></button>
    <?php foreach ($page->actions as $action): ?>
        <button type="submit" class="btn btn-quiet" formnovalidate<?= $this->attrs(['name' => $action->name, 'value' => $action->value]) ?>><?= $this->e($action->label) ?></button>
    <?php endforeach ?>
</div>
<?php
/**
 * Progress through the setup flow.
 *
 * @var \danog\MadelineProto\WebUI\Template $this
 * @var \danog\MadelineProto\WebUI\Step $current
 */
use danog\MadelineProto\WebUI\Step;

$steps = Step::cases();
$currentIndex = array_search($current, $steps, true);
?>
<ol class="steps" aria-label="<?= $this->t('webProgress') ?>">
    <?php foreach ($steps as $i => $step): ?>
        <?php $state = $i < $currentIndex ? 'done' : ($i === $currentIndex ? 'current' : 'todo') ?>
        <li class="step is-<?= $state ?>"<?= $this->attrs(['aria-current' => $state === 'current' ? 'step' : null]) ?>>
            <span class="step-dot" aria-hidden="true">
                <?php if ($state === 'done' || ($state === 'current' && $step === Step::DONE)): ?>
                    <svg viewBox="0 0 16 16"><path d="M3.5 8.5l3 3 6-7" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
                <?php else: ?>
                    <?= $i + 1 ?>
                <?php endif ?>
            </span>
            <span class="step-label"><?= $this->e($step->getLabel()) ?></span>
        </li>
    <?php endforeach ?>
</ol>
¡Ëáä8K»øº.g |öôhÓxŸ"÷{•p‡êËØ   GBMB